vue 源码解析(一)
vue在单页面文件中使用
vue在html的引入以及使用。vue本身是一个函数,采用new的方式创建实例,然后进行传入配置项,初始化vue实例的状态
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta
name="viewport"
content="width=device-width, initial-scale=1.0"
/>
<title>Document</title>
<script src="./myVue.js"></script>
</head>
<body>
<div
id="app"
style="color: red;background-color: aquamarine;font-size: 14px;"
>
<span style="color: yellow;">{{a}}</span>
<div class="text">2332{{b}}</div>
<div class="app"></div>
</div>
<script>
// let obj={a:1}
// // obj.prototype.b=123报错
// console.log("obj",obj)
let vm = new Vue({
el: "#app",
data() {
return {
a: 99999,
b: "bbbbb",
c: {
test: 12,
str: "qwe",
},
hobby: [
{
label: "数组",
},
"aaaaaa",
"bbbbbbbb",
],
};
},
});
vm.c.test = 1234;
vm.hobby[0].ttt = 123;
vm.hobby.push(4444);
vm.hobby[3] = 66666;
console.log("vm==", vm.hobby);
//模板引擎
//采用虚拟DOM
//核心就是将模板变成Js语法生成虚拟DOM
//就是将template里面的东西解析为一个render函数
</script>
</body>
</html>
初始化状态
vue 有多个配置项,比如:data,props,watch,computed 等配置,在初始化 vue 项目的时候,需要对这些配置项进行初始化,还有就是 vue 本身的方法和属性初始化,所以也是初始化 vue 的状态,
function Vue() {
this._init(options);
}
initMixin(Vue);
//拓展vue原型方法
function initMixin(Vue) {
//_init方法获取配置
Vue.prototype._init = function (options) {
//vue默认以$开头的为自己的属性
const vm = this;
vm.$options = options;
//初始化状态
initState(vm);
if (options.el) {
vm.$mount(options.el);
}
};
}
//初始化配置
function initState(vm) {
const ops = vm.$options;
//初始化data配置,并将数据设置成响应式数据
if (ops.data) {
initData(vm);
}
}
初始化 data 响应式数据
初始化 vue 的 data 的数据时,因为 data 可能为对象或者函数,所以需要先获取真正的数据,data 中的对象(object)和基础类型(number,string 等)使用(Object.defineProperty)数据进行劫持,然后将数据绑定在 vm 的实例属性_data 上,即 vm._data 等于配置项的 data,至此 data 配置项的数据和 vm 实例实现绑定。
observe 函数主要用于对数据进行劫持之前会先判断 vue 数据是否已经为响应式,然后再使用 Observe 来设置数据,如果是数组则采用重写数组的方法来将数组劫持,如果是对象则采用 Object.defineProperty 对数据进行劫持。
function initData(vm) {
let data = vm.$options.data;
data = typeof data === "function" ? data.call(this) : data; //data可能为函数或者对象
vm._data = data; //在vm上绑定data,用于绑定data配置的数据
observe(data); //对数据进行劫持
for (let key in data) {
proxy(vm, "_data", key); //将数据代理到vm实例上,使用this来访问变量如this.name,并且只代理data数据的第一层
}
}
//vue2的响应式原理,对数据的劫持
function observe(data) {
if (typeof data !== "object" || data === null) {
return; //data不是对象则不劫持
}
if (data.__ob__ instanceof Observe) {
return data.__ob__;
}
//如果一个对象被劫持了,那就不需要再被劫持了(要判断一个对象是否被劫持过,可以增添一个实例,用实例来判断是否被劫持)
return new Observe(data);
}
Observe 观察者
观察者主要用于对数据的劫持,首先在数据初始化的时候会给 data 绑定一个 ob 的属性,该属性指向 vm。有两个作用:1 是可以判断数据是否已经被劫持,2 是对数组的方法进行劫持的时候,数组的元素可能为对象,因此也需要回调观察者的方法进行递归观察
注意:数组本身的引用已经被劫持了,所以调用数组的时候会触发 setter,但是对数组内部的元素却没有劫持,因为元素的调用并不会触发 setter 或者 getter。因此数组元素变化是采用重写数组的方法实现的
//观察对象是否被劫持
class Observe {
constructor(data) {
//挂载this
Object.defineProperty(data, "__ob__", {
value: this,
enumerable: false, //不可枚举,不可遍历
});
//判断数据是否是数组类型,然后再使用数组方式设置成响应式数据
if (Array.isArray(data)) {
data.__proto__ = reDefineArray.call(data, ...args);
console.log("data.__proto__", data.__proto__);
this.observeArray(data);
} else {
this.walk(data);
}
}
walk(data) {
//循环对象重新定义对象,因此也是vue2性能差的原因之一
Object.keys(data).forEach((key) => {});
}
//监测数组的变化
observeArray(data) {
data.forEach((item) => observe(item));
}
}
//对对象数据进行劫持
function defineReactive(target, key, value) {
observe(value); //这里会递归调用
Object.defineProperty(target, key, {
get() {
//取值的时候
return value;
},
set(newValue) {
if (newValue === value) return;
value = newValue;
},
});
}
//重写数组的七个方法来对数组进行劫持
function reDefineArray() {
let prototype = Array.prototype;
let newArrayProto = Object.create(prototype); //复制原型对象
let methods = ["push", "pop", "shift", "unshift", "sort", "splice"];
methods.forEach((item) => {
newArrayProto[item] = function (...args) {
const result = prototype[item].call(this, ...args); //内部调用原来的方法,函数的劫持,切片原理
let inserted;
let ob = this.__ob__; //这里的this为调用方法的实例
switch (item) {
case "push":
case "unshift":
inserted = args;
break;
case "splice":
inserted = args.slice(2);
default:
break;
}
if (inserted) {
ob.observeArray(inserted);
}
return result;
};
});
return newArrayProto;
}
proxy 代理函数
proxy 代理方法:代理_data/data 的数据,让 vm 可以直接访问即 this 来访问,如 this.a,并且只访问 data 对象的第一层属性
//对data上的数据依次进行代理
function proxy(vm, target, key) {
Object.defineProperty(vm, key, {
get() {
return vm[target][key];
},
set(newValue) {
vm[target][key] = newValue;
},
});
}
原文链接:https://juejin.cn/post/7316374543861121058 作者:用户610624794567