Vue底层实现原理总结
要实现MVVM 响应式原理,要实现如下几点
var data = {name: 'zhimeng'};
observe(data);
data.name = 'zhimeng520'; // 哈哈哈,监听到值变化了 zhimeng --> zhimeng520 function observe(data) {
if (!data || typeof data !== 'object') {
return;
}
// 取出所有属性遍历
Object.keys(data).forEach(function(key) {
defineReactive(data, key, data[key]);
});
}; function defineReactive(data, key, val) {
observe(val); // 监听子属性
Object.defineProperty(data, key, {
enumerable: true, // 可枚举
configurable: false, // 不能再define
get: function() {
return val;
},
set: function(newVal) {
console.log('哈哈哈,监听到值变化了 ', val, ' --> ', newVal);
val = newVal;
}
});
}
// ... 省略
function defineReactive(data, key, val) {
var dep = new Dep();
observe(val); // 监听子属性 Object.defineProperty(data, key, {
// ... 省略
set: function(newVal) {
if (val === newVal) return;
console.log('哈哈哈,监听到值变化了 ', val, ' --> ', newVal);
val = newVal;
dep.notify(); // 通知所有订阅者
}
});
} function Dep() {
this.subs = [];
}
Dep.prototype = {
addSub: function(sub) {
this.subs.push(sub);
},
notify: function() {
this.subs.forEach(function(sub) {
sub.update();
});
}
};
// Observer.js
// ...省略
Object.defineProperty(data, key, {
get: function() {
// 由于需要在闭包内添加watcher,所以通过Dep定义一个全局target属性,暂存watcher, 添加完移除
Dep.target && dep.addDep(Dep.target);
return val;
}
// ... 省略
}); // Watcher.js
Watcher.prototype = {
get: function(key) {
Dep.target = this;
this.value = data[key]; // 这里会触发属性的getter,从而添加订阅者
Dep.target = null;
}
}
function Compile(el) {
this.$el = this.isElementNode(el) ? el : document.querySelector(el);
if (this.$el) {
this.$fragment = this.node2Fragment(this.$el);
this.init();
this.$el.appendChild(this.$fragment);
}
}
Compile.prototype = {
init: function() { this.compileElement(this.$fragment); },
node2Fragment: function(el) {
var fragment = document.createDocumentFragment(), child;
// 将原生节点拷贝到fragment
while (child = el.firstChild) {
fragment.appendChild(child);
}
return fragment;
}
};
Compile.prototype = {
// ... 省略
compileElement: function(el) {
var childNodes = el.childNodes, me = this;
[].slice.call(childNodes).forEach(function(node) {
var text = node.textContent;
var reg = /\{\{(.*)\}\}/; // 表达式文本
// 按元素节点方式编译
if (me.isElementNode(node)) {
me.compile(node);
} else if (me.isTextNode(node) && reg.test(text)) {
me.compileText(node, RegExp.$1);
}
// 遍历编译子节点
if (node.childNodes && node.childNodes.length) {
me.compileElement(node);
}
});
},
compile: function(node) {
var nodeAttrs = node.attributes, me = this;
[].slice.call(nodeAttrs).forEach(function(attr) {
// 规定:指令以 v-xxx 命名
// 如 <span v-text="content"></span> 中指令为 v-text
var attrName = attr.name; // v-text
if (me.isDirective(attrName)) {
var exp = attr.value; // content
var dir = attrName.substring(2); // text
if (me.isEventDirective(dir)) {
// 事件指令, 如 v-on:click
compileUtil.eventHandler(node, me.$vm, exp, dir);
} else {
// 普通指令
compileUtil[dir] && compileUtil[dir](node, me.$vm, exp);
}
}
});
}
}; // 指令处理集合
var compileUtil = {
text: function(node, vm, exp) {
this.bind(node, vm, exp, 'text');
},
// ...省略
bind: function(node, vm, exp, dir) {
var updaterFn = updater[dir + 'Updater'];
// 第一次初始化视图
updaterFn && updaterFn(node, vm[exp]);
// 实例化订阅者,此操作会在对应的属性消息订阅器中添加了该订阅者watcher
new Watcher(vm, exp, function(value, oldValue) {
// 一旦属性值有变化,会收到通知执行此更新函数,更新视图
updaterFn && updaterFn(node, value, oldValue);
});
}
}; // 更新函数
var updater = {
textUpdater: function(node, value) {
node.textContent = typeof value == 'undefined' ? '' : value;
}
// ...省略
};
function Watcher(vm, exp, cb) {
this.cb = cb;
this.vm = vm;
this.exp = exp;
// 此处为了触发属性的getter,从而在dep添加自己,结合Observer更易理解
this.value = this.get();
}
Watcher.prototype = {
update: function() {
this.run(); // 属性值变化收到通知
},
run: function() {
var value = this.get(); // 取到最新值
var oldVal = this.value;
if (value !== oldVal) {
this.value = value;
this.cb.call(this.vm, value, oldVal); // 执行Compile中绑定的回调,更新视图
}
},
get: function() {
Dep.target = this; // 将当前订阅者指向自己
var value = this.vm[exp]; // 触发getter,添加自己到属性订阅器中
Dep.target = null; // 添加完毕,重置
return value;
}
};
// 这里再次列出Observer和Dep,方便理解
Object.defineProperty(data, key, {
get: function() {
// 由于需要在闭包内添加watcher,所以可以在Dep定义一个全局target属性,暂存watcher, 添加完移除
Dep.target && dep.addDep(Dep.target);
return val;
}
// ... 省略
});
Dep.prototype = {
notify: function() {
this.subs.forEach(function(sub) {
sub.update(); // 调用订阅者的update方法,通知变化
});
}
};
function MVVM(options) {
this.$options = options;
var data = this._data = this.$options.data;
observe(data, this);
this.$compile = new Compile(options.el || document.body, this)
}
var vm = new MVVM({data: {name: 'zhimeng'}}); vm.name = 'zhimeng520';
function MVVM(options) {
this.$options = options;
var data = this._data = this.$options.data, me = this;
// 属性代理,实现 vm.xxx -> vm._data.xxx
Object.keys(data).forEach(function(key) {
me._proxy(key);
});
observe(data, this);
this.$compile = new Compile(options.el || document.body, this)
} MVVM.prototype = {
_proxy: function(key) {
var me = this;
Object.defineProperty(me, key, {
configurable: false,
enumerable: true,
get: function proxyGetter() {
return me._data[key];
},
set: function proxySetter(newVal) {
me._data[key] = newVal;
}
});
}
};
Vue底层实现原理总结的更多相关文章
- vue 实现数据绑定原理
案例: Vue 底层原理 // 目的: 使用原生js来实现Vue深入响应式 var box = document.querySelector('.box') var button = ...
- 深入解析vue响应式原理
摘要:本文主要通过结合vue官方文档及源码,对vue响应式原理进行深入分析. 1.定义 作为vue最独特的特性,响应式可以说是vue的灵魂了,表面上看就是数据发生变化后,对应的界面会重新渲染,那么响应 ...
- PHP底层工作原理
最近搭建服务器,突然感觉lamp之间到底是怎么工作的,或者是怎么联系起来?平时只是写程序,重来没有思考过他们之间的工作原理: PHP底层工作原理 图1 php结构 从图上可以看出,php从下到上是一个 ...
- Java并发之底层实现原理学习笔记
本篇博文将介绍java并发底层的实现原理,我们知道java实现的并发操作最后肯定是由我们的CPU完成的,中间经历了将java源码编译成.class文件,然后进行加载,然后虚拟机执行引擎进行执行,解释为 ...
- spirng底层实现原理
什么是框架?框架解决的是什么问题? 编程有一个准则,Don't Repeat Yourself(不要重复你的代码),所以我们会将重复的代码抽取出来,封装到方法中:如果封装的方法过多,将将这些方法封装成 ...
- iOS weak底层实现原理
今年年底做了很多决定,离开工作三年的深圳,来到了上海,发现深圳和上海在苹果这方面还是差距有点大的,上海的市场8成使用swift编程,而深圳8成的使用OC,这点还是比较让准备来上海打拼的苹果工程师有点小 ...
- Vue双向绑定原理,教你一步一步实现双向绑定
当今前端天下以 Angular.React.vue 三足鼎立的局面,你不选择一个阵营基本上无法立足于前端,甚至是两个或者三个阵营都要选择,大势所趋. 所以我们要时刻保持好奇心,拥抱变化,只有在不断的变 ...
- 《Java并发编程的艺术》Java并发机制的底层实现原理(二)
Java并发机制的底层实现原理 1.volatile volatile相当于轻量级的synchronized,在并发编程中保证数据的可见性,使用 valotile 修饰的变量,其内存模型会增加一个 L ...
- Spring(二)IOC底层实现原理
IOC原理 将对象创建交给Spring去管理. 实现IOC的两种方式 IOC配置文件的方式 IOC注解的方式 IOC底层实现原理 底层实现使用的技术 1.1 xml配置文件 1.2 dom4j解析xm ...
随机推荐
- nyoj 17-单调递增最长子序列 && poj 2533(动态规划,演算法)
17-单调递增最长子序列 内存限制:64MB 时间限制:3000ms Special Judge: No accepted:21 submit:49 题目描述: 求一个字符串的最长递增子序列的长度 如 ...
- 力扣(LeetCode)键盘行 个人题解
给定一个单词列表,只返回可以使用在键盘同一行的字母打印出来的单词.键盘如下图所示. 示例: 输入: ["Hello", "Alaska", "Dad& ...
- PHP文件上传和下载
第 1 章 文件上传 1.1 客户端上传设置 在 B/S 程序中文件上传已经成为一个常用功能.其目的是客户可以通过浏览器 (Browser) 将文件上传到服务器(Server)上的指定目录. 网络上常 ...
- java版单例模式
单例模式可以说是最常用的设计模式之一,其主要作用就是保证一个类只有一个实例,并且提供一个访问它的全局访问点,严格的控制用户的访问方式. 单例模式又分为懒汉模式和饿汉模式,首先说一下饿汉模式: 饿汉模式 ...
- PHP创建对象的6种方式
创建对象实例: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 ...
- node后台初始配置(2)
一.node-app结构 创建成功node-app项目后,会自动生成一些文件一般初始的结构如下图 在bin文件夹里面只有一个文件www var port = normalizePort(pr ...
- HTTPS 原理分析——带着疑问层层深入
HTTPS 随着 HTTPS 建站的成本下降,现在大部分的网站都已经开始用上 HTTPS 协议.大家都知道 HTTPS 比 HTTP 安全,也听说过与 HTTPS 协议相关的概念有 SSL .非对称加 ...
- 新闻实时分析系统Hive与HBase集成进行数据分析 Cloudera HUE大数据可视化分析
1.Hue 概述及版本下载 1)概述 Hue是一个开源的Apache Hadoop UI系统,最早是由Cloudera Desktop演化而来,由Cloudera贡献给开源社区,它是基于Python ...
- Leetcode_01【两数之和】
文章目录: 题目 脚本一及注释 脚本逻辑 脚本二及注释 脚本逻辑 题目: 给定一个整数数组 nums 和一个目标值 target,请你在该数组中找出和为目标值的那 两个 整数,并返回他们的数组下标. ...
- 第二章 Unity Shader基础
[TOC] 1. Unity Shader 的基础: ShaderLab 学习和编写着色器的过程一直是一个学习曲线很陡峭的过程,通常情况下为了自定义渲染效果往往要和很多文件和设置打交道,这些设置很容易 ...