vue源码解读(1)
前言
小白最近也被vue源码折腾的头疼不已,小白打算把自己学习vue源码的思路说出来,如有错误的地方,请务必留言指正,让小白能在前端的道路上更进一步。如果喜欢、批评、指正、或者你有更多的想法,不妨点个,或者评论留言谢谢?。
本系列基于vue2.0.0 探讨
接下来写多少章说实话小白自己也不是很清楚,小白也不是完全学明白了然后过来给大家描述,在这里只是记录了一些小白学习的过程。最近这些天一直想着看源码,结合网上的一些文章,发现根本看不懂啊。比如说Object.defineProperty()、Mixin模式、flow.js此处省略1万字。。。。看了半天还是云里雾里。我觉得想完完全全看懂源码,首先得知道一些扩充的知识,先把这些知识补充上吧。不过今天想说的并不是跟上面那些有关的,我觉得在看源码之前应该先了解一下vue具体运行过程,它到底是怎么样来执行的。
参考了掘金小册
响应式系统基本原理
在前面一章我们说过,响应式系统最核心的部分就是这个Object.defineProperty(obj, prop, descriptor) 定义:
Object.defineProperty() 方法会直接在一个对象上定义一个新属性,或者修改一个对象的现有属性, 并返回这个对象。
描述:
该方法允许精确添加或修改对象的属性。通过赋值操作添加的普通属性是可枚举的,能够在属性枚举期间呈现出来(for...in 或 Object.keys 方法), 这些属性的值可以被改变,也可以被删除。这个方法允许修改默认的额外选项(或配置)。默认情况下,使用 Object.defineProperty() 添加的属性值是不可修改的。
我们来解释一下这些参数分别是有什么用
Object.defineProperty(obj, prop, descriptor) obj 要在其上定义属性的对象。 prop 要定义或修改的属性的名称。 descriptor 将被定义或修改的属性描述符。返回值为: 被传递给函数的对象。复制代码
descriptor属性描述符
对象里目前存在的属性描述符有两种主要形式:数据描述符和存取描述符。数据描述符是一个具有值的属性,该值可能是可写的,也可能不是可写的。存取描述符是由getter-setter函数对描述的属性。描述符必须是这两种形式之一;不能同时是两者.
数据描述符和存取描述符均具有以下可选键值:
- configurable 当且仅当该属性的 configurable 为 true 时,该属性描述符才能够被改变,同时该属性也能从对应的对象上被删除。默认为 false。
- enumerable 当且仅当该属性的enumerable为true时,该属性才能够出现在对象的枚举属性中。默认为 false。
数据描述符同时具有以下可选键值:
- value 该属性对应的值。可以是任何有效的 JavaScript 值(数值,对象,函数等)。默认为 undefined。
- writable 当且仅当该属性的writable为true时,value才能被赋值运算符改变。默认为 false。
存取描述符同时具有以下可选键值:
- get 一个给属性提供 getter 的方法,如果没有 getter 则为 undefined。当访问该属性时,该方法会被执行,方法执行时没有参数传入,但是会传入this对象(由于继承关系,这里的this并不一定是定义该属性的对象)。 默认为 undefined。
- set 一个给属性提供 setter 的方法,如果没有 setter 则为 undefined。当属性值修改时,触发执行该方法。该方法将接受唯一参数,即该属性新的参数值。 默认为 undefined。
当然 这是MDN的原话啦 总结一下有以下几种:
- enumerable,属性是否可枚举,默认 false。
- configurable,属性是否可以被修改或者删除,默认 false。
- writable,value是否可被修改,默认false。
- value, 该属性对应的值。
- get,获取属性的方法。
- set,设置属性的方法。
封装一个myVue
var app = new myVue({ el:'#app', data: { number: 0 }, })复制代码
- 定义一个myVue
function myVue(options) { }复制代码
想想我们之前说的那个步骤,接下来该进行初始化,获取到data
function myVue(options) { this._init(options);}myVue.prototype._init = function (options) { this.$data = options.data; // this.$data = {number: 0} }复制代码
然后我们应该对data进行处理,重写data的set和get函数。我们定义一个defineReactive函数,利用Object.defineProperty 来实现对对象的响应式化,形参是一个 obj(需要绑定的对象)、key(obj的某一个属性),val(具体的值)。
function myVue(options) { this._init(options);}myVue.prototype._init = function (options) { this.$data = options.data; // this.$data = {number: 0} }function defineReactive(obj,key,val){ Object.defineProperty(obj, key, { enumerable: true, // 可枚举 configurable: true, // 可被删除 get: function reactiveGetter () { return val; }, set: function reactiveSetter (newVal) { console.log(`更新${newVal}`); if (newVal === val) { // 如果新的值与旧的值相等 return }; val = newVal; } });}复制代码
接下来我们还得写一个_observer函数,来对data中的每一个属性来进行defineReactive处理。
function myVue(options) { this._init(options);}myVue.prototype._init = function (options) { console.log('_init执行') this.$data = options.data; // this.$data = {number: 0} // 修改_init函数 this._observer(this.$data)}myVue.prototype._observer = function (obj) { console.log('_observer执行') // 循环遍历data for (value in obj) { // 使用 hasOwnProperty 方法判断属性是否存在 if (obj.hasOwnProperty(value)) { if (typeof obj[value] === 'object') { //如果值还是对象,则遍历处理 this._observer(obj[value]) } // 执行defineReactive对data属性进行处理 defineReactive(obj,value,obj[value]) } } } function defineReactive(obj, key, val) { console.log('对data进行重写') Object.defineProperty(obj, key, { enumerable: true, // 可枚举 configurable: true, // 可被删除 get: function reactiveGetter() { return val; }, set: function reactiveSetter(newVal) { console.log(`触发视图更新!+${newVal}`); if (newVal === val) { // 如果新的值与旧的值相等 return }; val = newVal; } }); }复制代码
到这里我们的简单的响应式系统就做好啦。代码在,复制粘贴到chrome中就可以看到输出啦。