前言

之前断断续续地在研究vue原理,但还未动手实战过。本文从最简单的开始,vue如何将{{}}data联系起来,并渲染出来,比如{{msg}}data(){ return {msg:333} },那么{{msg}}将渲染成333,现在让我们开始吧。

实战

为了实现该功能,我动手写了一个Zue,已将其放在github上,其链接地址为https://github.com/zwf193071/zue.git 。下面我将为大家详细地说明原理。

index.html的代码如下所示:

 <div id="app">{{msg}}</div><script type="module">import Zue from './src/zue.js'{new Zue({el: '#app',data() {return {msg: 666}}})}</script>

Zue首先执行init初始化操作,代码如下所示:

init(options) {const el = query(options.el);this.$options = options;this._initState();this._compile(el);}

query通过document.querySelector(el)获取到dom元素,_initStatedata(){ return {msg:666} }函数里返回的对象挂载在Zue对象上,_initState的源码如下所示:

 Object.defineProperty(Zue.prototype, '$data', {get () {return this._data;},set (newData) {if (newData !== this._data) {// this._setData(newData);}}});Zue.prototype._initState = function () {this._initData();}Zue.prototype._initData = function () {var dataFn = this.$options.data;var data = this._data = dataFn ? ( typeof dataFn == 'function' ? dataFn() : dataFn ) : {}// proxy data on instancevar keys = Object.keys(data);var i, key;i = keys.length;while (i--) {key = keys[i];this._proxy(key);}}Zue.prototype._proxy = function (key) {var self = this;Object.defineProperty(self, key, {configurable: true,enumerable: true,get: function proxyGetter() {return self._data[key];},set: function proxySetter(val) {self._data[key] = val;}});}

_compile只做一件事,那便是将我们的{{msg}}转换成666,其代码如下所示:

Zue.prototype._compile = function (el) {var vm = this;el.childNodes.forEach(node => {if (node.nodeType === 3 && /\{\{(.*)\}\}/.test(node.textContent)) {node.textContent = vm[RegExp.$1]}});
}

其中RegExp.$1是捕获正则表达式/\{\{(.*)\}\}/第一个匹配的结果msg,通过前面的_initData,我们已经将msg挂载在Zue上,Zue["msg"]值为666,将666赋值给node.textContent,即可实现值替换

若更改index.html的代码,需要对子元素内部的{{}}实现值替换

 <div id="app">{{msg}}<span>{{text}}</span></div><script type="module">import Zue from './src/zue.js'{new Zue({el: '#app',data() {return {msg: 666,text: "lucy"}}})}</script>

我们需要修改__compile里的源码

import { toArray,isElementNode,isTextNode
} from './utils.js'
export default function lifecycleMixin(Zue) {Zue.prototype._compile = function (el) {var vm = this;var childNodes = toArray(el.childNodes);childNodes.forEach(node => {var text = node.textContent;var reg = /\{\{(.*)\}\}/;if (isElementNode(node)) {} else if (isTextNode(node) && reg.test(text)) {vm.compileText(node, RegExp.$1.trim());}if (node.childNodes && node.childNodes.length) {vm._compile(node); // 迭代替换子元素内部的{{}}}});}Zue.prototype.compileText = function (node, exp) {node.textContent = this[exp]}
}

utils.js的代码如下所示:

function toArray (list, start) {start = start || 0var i = list.length - startvar ret = new Array(i)while (i--) {ret[i] = list[i + start]}return ret
}
function isElementNode (node) {return node.nodeType == 1;
}
function isTextNode (node) {return node.nodeType == 3;
}

且慢,有同学可能会问,若修改index.html如下所示,那么This is a test{{msg}}会被覆盖为msg的值吧

 <div id="app">This is a test{{msg}}<span>{{text}}</span></div><script type="module">import Zue from './src/zue.js'{new Zue({el: '#app',data() {return {msg: 666,text: "lucy"}}})}</script>

是的,所以我们需要修改_compile方法,如下所示:

function compileTextNode(node, vm) {const reg = /\{\{(.+?)\}\}/g;;let txt = node.wholeText;txt = txt.replace(reg, (_, gl) => {let key = gl.trim();let value = vm[key];return value;});node.textContent = txt;
}
export default function lifecycleMixin(Zue) {Zue.prototype._compile = function (el) {const vm = this;const childNodes = toArray(el.childNodes);childNodes.forEach(node => {const text = node.textContent;if (isElementNode(node)) {} else if (isTextNode(node)) {compileTextNode(node, vm);}if (node.childNodes && node.childNodes.length) {vm._compile(node);}});}
}

循环遍历,并替换{{}}里面的文本值,即可实现值替换

上面还有一个明显的问题,直接操作dom元素,对textContent进行文本替换,需创建Document Fragment,将el元素拷贝到Document Fragment中,对Document Fragment文本值替换完毕后,再将其appendChildel元素内,代码如下所示:

function compileTextNode(node, vm) {const reg = /\{\{(.+?)\}\}/g;;let txt = node.wholeText;txt = txt.replace(reg, (_, gl) => {let key = gl.trim();let value = vm[key];return value;});node.textContent = txt;
}
function compileNode(frag, vm) {const childNodes = toArray(frag.childNodes);childNodes.forEach(node => {if (isElementNode(node)) {} else if (isTextNode(node)) {compileTextNode(node, vm);}if (node.childNodes && node.childNodes.length) {vm._compile(node);}});
}
function node2Fragment(el) {let fragment = document.createDocumentFragment(), child;// 将原生节点拷贝到fragmentwhile (child = el.firstChild) {fragment.appendChild(child);}return fragment;
}
export default function lifecycleMixin(Zue) {Zue.prototype._compile = function (el) {const frag = node2Fragment(el);compileNode(frag, this);el.appendChild(frag);}
}

Vue1.0.25源码分析,及Zue模拟实现(一)相关推荐

  1. Pushlet 2.0.3 源码分析

    转载地址:http://blog.csdn.net/yxw246/article/details/2418255 Pushlet 2.0.3 源码分析 ----服务器端 1 总体架构 Pushlet从 ...

  2. Spark2.4.0 SparkEnv 源码分析

    Spark2.4.0 SparkEnv 源码分析 更多资源 github: https://github.com/opensourceteams/spark-scala-maven-2.4.0 时序图 ...

  3. 菜鸟读jQuery 2.0.3 源码分析系列(1)

    原文链接在这里,作为一个菜鸟,我就一边读一边写 jQuery 2.0.3 源码分析系列 前面看着差不多了,看到下面一条(我是真菜鸟),推荐木有入门或者刚刚JS入门摸不着边的看看,大大们手下留情,想一起 ...

  4. Android 11.0 Settings源码分析 - 主界面加载

    Android 11.0 Settings源码分析 - 主界面加载 本篇主要记录AndroidR Settings源码主界面加载流程,方便后续工作调试其流程. Settings代码路径: packag ...

  5. Android 8.0系统源码分析--Camera processCaptureResult结果回传源码分析

    相机,从上到下概览一下,真是太大了,上面的APP->Framework->CameraServer->CameraHAL,HAL进程中Pipeline.接各种算法的Node.再往下的 ...

  6. photoshop-v.1.0.1源码分析第三篇–FilterInterface.p

    photoshop-v.1.0.1源码分析第三篇–FilterInterface.p 总体预览 一.源码预览 二.语法解释 三.结构预览 四:语句分析 五:思维导图 六:疑留问题 一.源码预览 {Ph ...

  7. Spark2.0.2源码分析——RPC 通信机制(消息处理)

    RPC 是一种远程过程的调用,即两台节点之间的数据传输. 每个组件都有它自己的执行环境,RPC 的执行环境就是 RPCENV,RPCENV 是 Spark 2.x.x 新增加的,用于替代之前版本的 a ...

  8. jQuery 2.0.3 源码分析 Deferred(最细的实现剖析,带图)

    Deferred的概念请看第一篇 http://www.cnblogs.com/aaronjs/p/3348569.html ******************构建Deferred对象时候的流程图* ...

  9. 最细的实现剖析:jQuery 2.0.3源码分析Deferred

    Deferred的概念请看第一篇 http://www.cnblogs.com/aaronjs/p/3348569.html **构建Deferred对象时候的流程图** **源码解析** 因为cal ...

最新文章

  1. struts2的配置
  2. SharePoint 2007 Web Content Management 性能优化系列 前言
  3. python爬取有道词典_利用Python3和Charles爬取有道词典,生成翻译exe单文件
  4. python处理数据库_python操作数据库
  5. 【译】Introduction to Smart Contract and DApp Security
  6. 传统公司部署OpenStack(t版)简易介绍(六)——neutron模块部署
  7. 网易严选Java开发三面面经:南京黑马java培训怎么样
  8. svn: Can't convert string from 'UTF-8' to native
  9. SQL工作笔记-达梦7存储过程中游标的使用(for循环 IF等)
  10. Linux管道函数使用
  11. java实现连拍_Camera2连拍相关问题
  12. iOS学习01C语言数据类型
  13. ArcView GIS 应用与开发技术(3)-地理要素的显示和标注
  14. 兄弟打印机内存已满清零方法_兄弟打印机清零方法
  15. 小米mix2安兔兔html5跑分,vivo X21跑分多少?高通骁龙660 AIE安兔兔跑分实测
  16. Java学习笔记-Lambda表达式
  17. Pytorch与强化学习 —— 1. 如何实现一个简单的Q Learning算法
  18. 计算机图形学--实时光线追踪
  19. 免费刷题的软件测试面试题库小程序,萌新必备
  20. Conflux v2.2.0 网络 Hardfork 升级公告

热门文章

  1. 北京智源大会 | AI + 医疗的下一个十年:从公共卫生预警到人类基因密码破解 道翰天琼认知智能api机器人接口。
  2. 基于华为云ECS的目标检测与识别的昇腾AI开发体验【华为云至简致远】
  3. 跨平台应用即将消亡!
  4. chrome 打印布局_Chrome打印网页中的宽度控制
  5. Go微服务架构实战 中篇:6. 微服务治理策略
  6. android libbinder – Binder的Native实现
  7. 为什么要求高频pcb板低ε(Dk)
  8. 哔哩哔哩2018校招前端笔试
  9. (万文)最全、最细前端面试问题总结(答题思路分析、答案解析)
  10. 系统管理Lesson 14. Performing Database Backups