Vue1.0.25源码分析,及Zue模拟实现(一)
前言
之前断断续续地在研究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
元素,_initState
将data(){ 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
文本值替换完毕后,再将其appendChild
到el
元素内,代码如下所示:
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模拟实现(一)相关推荐
- Pushlet 2.0.3 源码分析
转载地址:http://blog.csdn.net/yxw246/article/details/2418255 Pushlet 2.0.3 源码分析 ----服务器端 1 总体架构 Pushlet从 ...
- Spark2.4.0 SparkEnv 源码分析
Spark2.4.0 SparkEnv 源码分析 更多资源 github: https://github.com/opensourceteams/spark-scala-maven-2.4.0 时序图 ...
- 菜鸟读jQuery 2.0.3 源码分析系列(1)
原文链接在这里,作为一个菜鸟,我就一边读一边写 jQuery 2.0.3 源码分析系列 前面看着差不多了,看到下面一条(我是真菜鸟),推荐木有入门或者刚刚JS入门摸不着边的看看,大大们手下留情,想一起 ...
- Android 11.0 Settings源码分析 - 主界面加载
Android 11.0 Settings源码分析 - 主界面加载 本篇主要记录AndroidR Settings源码主界面加载流程,方便后续工作调试其流程. Settings代码路径: packag ...
- Android 8.0系统源码分析--Camera processCaptureResult结果回传源码分析
相机,从上到下概览一下,真是太大了,上面的APP->Framework->CameraServer->CameraHAL,HAL进程中Pipeline.接各种算法的Node.再往下的 ...
- photoshop-v.1.0.1源码分析第三篇–FilterInterface.p
photoshop-v.1.0.1源码分析第三篇–FilterInterface.p 总体预览 一.源码预览 二.语法解释 三.结构预览 四:语句分析 五:思维导图 六:疑留问题 一.源码预览 {Ph ...
- Spark2.0.2源码分析——RPC 通信机制(消息处理)
RPC 是一种远程过程的调用,即两台节点之间的数据传输. 每个组件都有它自己的执行环境,RPC 的执行环境就是 RPCENV,RPCENV 是 Spark 2.x.x 新增加的,用于替代之前版本的 a ...
- jQuery 2.0.3 源码分析 Deferred(最细的实现剖析,带图)
Deferred的概念请看第一篇 http://www.cnblogs.com/aaronjs/p/3348569.html ******************构建Deferred对象时候的流程图* ...
- 最细的实现剖析:jQuery 2.0.3源码分析Deferred
Deferred的概念请看第一篇 http://www.cnblogs.com/aaronjs/p/3348569.html **构建Deferred对象时候的流程图** **源码解析** 因为cal ...
最新文章
- struts2的配置
- SharePoint 2007 Web Content Management 性能优化系列 前言
- python爬取有道词典_利用Python3和Charles爬取有道词典,生成翻译exe单文件
- python处理数据库_python操作数据库
- 【译】Introduction to Smart Contract and DApp Security
- 传统公司部署OpenStack(t版)简易介绍(六)——neutron模块部署
- 网易严选Java开发三面面经:南京黑马java培训怎么样
- svn: Can't convert string from 'UTF-8' to native
- SQL工作笔记-达梦7存储过程中游标的使用(for循环 IF等)
- Linux管道函数使用
- java实现连拍_Camera2连拍相关问题
- iOS学习01C语言数据类型
- ArcView GIS 应用与开发技术(3)-地理要素的显示和标注
- 兄弟打印机内存已满清零方法_兄弟打印机清零方法
- 小米mix2安兔兔html5跑分,vivo X21跑分多少?高通骁龙660 AIE安兔兔跑分实测
- Java学习笔记-Lambda表达式
- Pytorch与强化学习 —— 1. 如何实现一个简单的Q Learning算法
- 计算机图形学--实时光线追踪
- 免费刷题的软件测试面试题库小程序,萌新必备
- Conflux v2.2.0 网络 Hardfork 升级公告
热门文章
- 北京智源大会 | AI + 医疗的下一个十年:从公共卫生预警到人类基因密码破解 道翰天琼认知智能api机器人接口。
- 基于华为云ECS的目标检测与识别的昇腾AI开发体验【华为云至简致远】
- 跨平台应用即将消亡!
- chrome 打印布局_Chrome打印网页中的宽度控制
- Go微服务架构实战 中篇:6. 微服务治理策略
- android libbinder – Binder的Native实现
- 为什么要求高频pcb板低ε(Dk)
- 哔哩哔哩2018校招前端笔试
- (万文)最全、最细前端面试问题总结(答题思路分析、答案解析)
- 系统管理Lesson 14. Performing Database Backups