Vue 原理分析
1. vue 生命周期
![](/assets/vue-lifecycle-95ec5090.png)
2. vuex 原理
3. 数据代理
3.1 通过定义单个属性实现数据代理
let data = { x: 100 };
let dataProxyer = {};
// dataProxyer对象的x属性值代理了data对象的x属性值
// 1.获取data中的某个key值可以通过dataProxyer的key获取
// 2.修改data中的某个key值可以通过修改dataProxyer的key实现
Object.defineProperty(dataProxyer, "x", {
get() {
return data.x;
},
set(value) {
data.x = value;
},
});
console.log("data.x的初始值为:", data.x);
// data.x的初始值为:100
// 通过数据代理dataProxyer修改了data的x属性的值
dataProxyer.x = 1000;
console.log("通过数据代理修改,data.x的最终值为:", data.x);
// 通过数据代理修改,data.x的最终值为:1000
4. 双向绑定
双向绑定的原理图
实现代码 demo
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div id="app">
<span>标题:{{name}}</span>
<input type="text" v-model="name">
<span>更多:{{more.like}}</span>
<input type="text" v-model="more.like">
</div>
<script src="./minVue.js"></script>
<script>
const vm = new Vue({
el:"#app",
data:{
name:"老师1",
more:{
like:"喜欢"
}
}
})
console.log(vm);
</script>
</body>
</html>
class Vue {
constructor(obj_instance) {
/*******************************************************************************************************
* data={
name:"老师",
more:{
like:"喜欢"
}
*/
this.$data = obj_instance.data;
Observer(this.$data);
// el:"#app",this指vue的实例vm;
Compile(obj_instance.el, this);
}
}
/*******数据劫持- 监听实例里的数据************************************************************************************************
* data_instance = {
name:"老师",
more:{
like:"喜欢"
}
*/
function Observer(data_instance) {
if (!data_instance || typeof data_instance !== "object") {
return;
}
const dependency = new Dependency();
// Object.keys()只遍历自身属性(键)
Object.keys(data_instance).forEach(key => {
let value = data_instance[key];
// 递归-子属性数据劫持
Observer(value);
Object.defineProperty(data_instance, key, {
enumerable: true,
configurable: true,
get() {
console.log(`访问了属性:${key}=> 值:${value}`)
// 获取属性时添加依赖-收集和通知订阅者
Dependency.temp && dependency.addSub(Dependency.temp);
return value;
},
set(newValue) {
console.log(`属性:${key}的值${value}修改为->${newValue}`)
value = newValue;
Observer(newValue);
// 修改属性时添加依赖收集者开始通知
dependency.notify()
}
})
})
}
/*******************************************************************************************************
* HTML模板解析函数-替换DOM内
*/
function Compile(element, vm) {
// 找到el的dom元素
vm.$el = document.querySelector(element);
// 创建文档碎片
const fragment = document.createDocumentFragment();
let child;
// 遍历vm.$el的元素,每次append第一个元素会被添加到fragment(child节点自身会从vm.$el的dom节点移除)
// 添加后第一个元素就被移动到fragment中,所以直到最后$el变成无子节点,while判断为假,跳出循环
while (child = vm.$el.firstChild) {
fragment.append(child);
}
fragment_compile(fragment);
function fragment_compile(node) {
const pattern = /\{\{\s*(\S+)\s*\}\}/;
/*******************************************************************************************************
* 正则匹配:(\S+)表示排除换行空白的字符
* 如果是{{more.like}},结果为
* result_regex[0] = "{{more.like}}"
* result_regex[1] = "more.like"
* 如果是"{{name}}",结果为:
* result_regex[0] = "{{name}}"
* result_regex[0] = "name" *
*/
// 如果是文本
if (node.nodeType === 3) {
const xxx = node.nodeValue;
const result_regex = pattern.exec(node.nodeValue);
if (result_regex) {
const arr = result_regex[1].split('.');
/*******************************************************************************************************
* 如果是'more.like',split分割成数组为['more','like']
* 使用reduce:初始值total为vm.$data
* 第一次遍历(total=data, current='more')得到结果data['more']
* 第二次遍历(total=data['more'], current='like')得到结果data['more']['like'];
* 最终拿到递归的元素值data['more']['like'];
*
* 如果是'name',split分割成数组为['name']
* 第一次遍历(total=data, current='name')得到结果data['name']
* 最终拿到递归的元素值data['name'];
*/
const value = arr.reduce((total, current) => total[current], vm.$data)
node.nodeValue = xxx.replace(pattern, value);
// 创建订阅者,回调函数为将节点中插值表达式的key为"more.like"的值更改为最新值,即更新插值表达式
new Watcher(vm, result_regex[1], newValue => {
node.nodeValue = xxx.replace(pattern, newValue);
})
}
return;
}
if (node.nodeType === 1 && node.nodeName === 'INPUT') {
const attr = Array.from(node.attributes);
attr.forEach(i => {
if (i.nodeName === 'v-model') {
// v-model='more.like'或者v-model='name'
// i.nodeValue为"more.like"或者'name',
const value = i.nodeValue.split('.').reduce((total, current) => total[current], vm.$data);
node.value = value;
new Watcher(vm, i.nodeValue, newValue => {
node.value = newValue;
})
node.addEventListener('input', e => {
// ["more","like"]
const arr1 = i.nodeValue.split('.');
// ['more']
const arr2 = arr1.slice(0, arr1.length - 1);
// vm.$data.more
const final = arr2.reduce(
(total, current) => total[current], vm.$data
)
// vm.$data.more['like']=e.target.value
final[arr1[arr1.length - 1]] = e.target.value;
})
}
})
}
// 有子节点遍历子节点
node.childNodes.forEach(child => fragment_compile(child));
}
vm.$el.appendChild(fragment);
}
/****依赖-收集和通知订阅者***************************************************************************************************
*
*/
class Dependency {
constructor() {
this.subscribers = [];
}
addSub(sub) {
this.subscribers.push(sub);
}
notify() {
this.subscribers.forEach(sub => sub.update());
}
}
/****订阅者***************************************************************************************************
*
* 先把当前watcher存到dependency的临时标量,然后触发getter,在getter中奖watcher添加到dep中的订阅者数组
*/
class Watcher {
constructor(vm, key, callback) {
this.vm = vm;
this.key = key;
this.callback = callback
// 临时属性-触发getter
Dependency.temp = this;
key.split('.').reduce((total, current) => total[current], vm.$data);
Dependency.temp = null;
}
update() {
// 拿到this.vm.$data中的key的实际的值
const value = this.key.split('.').reduce((total, current) => total[current], this.vm.$data);
// 这里的callback更新插值表达式的值为:this.vm.$data中的key值
this.callback(value);
}
}