本文是参与若川的源码共读活动第23期 以下是我阅读源码之后的笔记
1、解读构造Vue的源码
1.1 准备页面,写上vue构造函数
<script src="http://unpkg.com/vue@2.6.14/dist/vue.js"></script>
<script>
const vm = new Vue({
data: {
name: 'coco'
},
methods: {
sayName() {
console.log(this.name)
}
}
})
console.log(vm.name)
console.log(vm.sayName())
</script>
2、vscode安装Liver server插件,在浏览器中打开页面(npm i -g http-server也可以,但是我没有下载和启动成功)
a、在new Vue处进行断点,进入vue函数里,查看函数构造过程
function Vue (options) {
// 判断是不是用了new关键词调用构造函数,因为Vue一个项目一般只需要new一次,因此调用的时候需要new一次
// (jQuery在项目中需要经常调用,因此把new操作放在了源码里面)
if (!(this instanceof Vue) ) {
warn('Vue is a constructor and should be called with the `new` keyword');
}
// 初始化各种数据状态
this._init(options);
}
b、初始化methods---(1、判断是否为函数,不为函数则初始化一个函数,2、如果为函数,则用bind把函数的this指向绑定到vm上面)
c、初始化data
1.2、判断data是否为对象
1.3、遍历data,并和methods、props,进行比较,若有相同则报错
1.4、代理data数据为_data,并设置data里的属性为响应式数据
// 初始化数据initData()
// isReserved 判断属性是否为保留字段或者是以$/_开头的属性名
// 调用data函数,或者数据属性对象
function getData (data, vm) {
// #7573 disable dep collection when invoking data getters
pushTarget();
try {
return data.call(vm, vm)
} catch (e) {
handleError(e, vm, "data()");
return {}
} finally {
popTarget();
}
}
1.5、object.defineProperty(obj,prop,descriptor)
1.5.1、obj要定义属性的对象,prop要定义或修改的属性名称或者symbol、descriptor要定义或修改的属性描述符
1.5.2、descriptor包括
| configurable(是否可以删除) | enumerable(枚举) | value(值) | writable(值修改) | get(接收函数) | set(设置函数) | |
| 数据描述符 | 可以 | 可以 | 可以 | 可以 | 不可以 | 不可以 |
| 存取描述符 | 可以 | 可以 | 不可以 | 不可以 | 可以 | 可以 |
如果一个描述符不具有 value、writable、get 和 set 中的任意一个键,那么它将被认为是一个数据描述符。
如果一个描述符同时拥有 value 或 writable 和 get 或 set 键,则会产生一个异常。
2、简单的源码实现
// 初始化函数
function noop(a, b, c) {}
// 定义数据属性值
var sharedPropertyDefinition = {
enumerable: true,
configurable: true,
get: noop,
set: noop
};
// proxy 函数的原理是通过 Object.defineProperty 函数在实例对象 vm 上定义与 data 数据字段同名的访问器属性,并且这些属性代理的值是 vm._data 上对应属性的值
function proxy(target, sourceKey, key) {
sharedPropertyDefinition.get = function proxyGetter() {
return this[sourceKey][key]
};
sharedPropertyDefinition.set = function proxySetter(val) {
this[sourceKey][key] = val;
}
Object.defineProperty(target, key, sharedPropertyDefinition);
}
// 初始化值
function initData(vm) {
const data = vm._data = vm.$options.data;
const keys = Object.keys(data);
var i = keys.length;
while(i --) {
var key = keys[i];
proxy(vm, '_data', key);
}
}
// 初始化方法
function initMethods(vm, methods) {
for(var key in methods) {
vm[key] = typeof methods[key] !== 'function' ? noop : methods[key].bind(vm);
}
}
function Person(options) {
let vm = this;
vm.$options = options;
var opts = vm.$options;
if(opts.data) {
initData(vm);
}
if(opts.methods) {
initMethods(vm, opts.methods)
}
}
const p = new Person({
data: {
name: 'coco'
},
methods: {
sayName() {
console.log(this.name)
}
}
})
console.log(p.name,"name"); // coco name
console.log(p.sayName(),"sayName")
疑问 pushTarget、 popTarget这两个方法还不是特别懂,了解了一些
3、总结
3.1、 复习了代码调试方法和object。defineproperty的描述符属性
3.2、 知道了vue和jQuery使用new的不同
3.3、this可以直接访问到methods和data,methods的方法用bind把this指向绑定到了vm上面,data--数据在object.defineproperty代理前存储到了vm._data对象中
3.4、 本文笔记大多是借鉴若川的文章,小部分是自己思考并查阅资料整合
(阅读源码文章还是很有收获的,从以前的看不懂不耐心到现在的慢慢摸索, 只不过看完文章之后写笔记这个能力比较欠缺,有点不知道从那里下手,慢慢改进 大家一起进步加油ヾ(◍°∇°◍)ノ゙)