vue和react源码区别
- react做运行时优化,所以react源码复杂
- vue没有太多运行时优化但也很流畅,因为vue做编译时优化
- 所以vue模板必须按它的规则去写,而react就很灵活
compiler编译步骤:
parser:
compiler 的一个步骤,对模板进行 AST 分析(先分词,再做词法分析)。
observer 是 core 的核心。
observer
将一个数据修改为可观察数据,主要是为了解决数组和对象新增下标或属性时,新元素不是可观察数据的问题。
它会创建一个属性名为 __ob__ 的不可配置、不可枚举的对象,用于表示当前对象已经是响应式对象了。
对于嵌套对象,递归调用。
keep-alive,保存的是 vnode 节点,而不是数据。
由于 vnode 节点比描述状态的数据大一些,所以 keep-alive 能够保存的数据大小有限,所以它存在取舍问题,一般舍弃最老的组件。
对于任意组件,无论是否被添加到 keep-alive 缓存列表中,重新访问时,都会把它设置为列表的结尾。
用于为vue设置插件,它维护一个插件队列,判断是否已存在,如果未存在,执行插件,并且添加到插件队列中。
编译模板,无论是在线编译,还是离线编译,均会为每一个组件生成一个对应的 render 函数。并且,会根据节点的状态,为它们优化成静态节点 render 函数。
defineReactive 函数利用 Object.defineProperty方法, 使 vue data 重写成可控对象。
在此方法内,创建一个用于双向数据绑定的、观察者模式中的发布者 dep。
在 get 方法中,判断是否为第一次访问数据,第一次访问数据时需要通知 dep 发布。
在 set 方法中,判断 newValue === oldValue,需要更新则通知 dep 发布。
dep 作为 vue data 实现双向数据绑定功能中的发布者。每一个 dep 对应一个数据,维护所有与该数据有关的 watcher。
这些 render 函数在某个vue钩子的生命周期时被执行,此时会创建 watcher。
watcher 作为 vue data 实现双向数据绑定功能中的观察者,每一个 watcher 对应一个组件。
watcher 最主要的工作是维护 数据 与 dom 之间的渲染关系。
新创建的 watcher 会调用 get 方法,将触发 Object.defineProperty 的 get 方法,将 watcher 添加到对应的 dep中。
watcher 的 get 方法还会尝试调用渲染函数,所以当创建 watcher 后,组件中的数据就已经被渲染了。如果是在线编译,第一次渲染会删除原生 dom ,此后均为 vnode。
首次渲染完成。。。
vue data 在后续更新时不是立刻更新,而是将数据更新的过程添加到一个调度器中,实现批量更新而不是每次数据变化时更新 dom 。由 nextTick 执行回调批量更新。
编译时优化:
运行时优化:
todo。。。key
vue源码分析
index.js
// 是一个构造函数,返回vue实例
function Vue({ data, el }){
// 数据
this.data = data;
// 双向数据
defineReactive(this, this.data);
// 编译
compiler(this, document.getElementById(el));
}
compiler.js
// 编译模板
// ⚠️源码中用到了大量的正则,这里仅仅做案例,ast分析也忽略了
function compiler(vm, el){
const div = el.querySelector('#div');
const input = el.querySelector('#input');
// 新创建dom,是可以绑定很多与vue有关的值,这里模拟的是 createElement
const _div = document.createElement('div');
const _input = document.createElement('input');
const parentElement = div.parentElement;
parentElement.removeChild(div);
parentElement.removeChild(input);
// 替换成新创建的dom
parentElement.appendChild(_div);
parentElement.appendChild(_input);
// 'msg'
const msg = div.getAttribute('v-text');
// div 的 render,正常的render函数是 width(this){xxx},xxx是ast分析后的结构,并且通过不同的渲染函数渲染
const divRender = function (innerHTML){
_div.innerHTML = innerHTML;
};
const inputRender = function (value){
_input.value = value;
};
// watcher
new Watcher(vm, msg, divRender);
const inputWatcher = new Watcher(vm, msg, inputRender);
_input.oninput = function (){
// input中输入的值
const value = this.value;
// 触发 Object.defineProperty 的 set
inputWatcher.vm[inputWatcher.key] = value;
};
}
dep.js
let depId = 0;
// 观察者模式的发布者
function Dep(){
// 区分是哪个发布者
this.depId = depId++;
// 维护关心某个数据的所有watcher,这里用set可以去重
this.watcherList = new Set();
}
// 当且仅当 创建任意 watcher 时,会指向正在被创建的 watcher
Dep.target = null;
// 添加 watcher
Dep.prototype.add = function (watcher){
if (this.watcherList.has(watcher)) {
return;
}
watcher.depId = this.depId;
this.watcherList.add(watcher);
};
// 发布
Dep.prototype.notify = function (){
for (let o of this.watcherList) {
// 通知到每一个订阅了该数据的 watcher
o.update(this.depId);
}
};
observer.js
function defineReactive(vm, data){
Reflect.ownKeys(data).forEach(key => {
let value = data[key];
// 为每一个key创建新的发布者
const dep = new Dep();
Object.defineProperty(vm, key, {
get(){
// 第一次执行get,只有在创建 watcher 时 === true
if (!!Dep.target) {
// 添加一个 watcher 到 发布者 dep
dep.add(Dep.target);
}
return value;
},
set(v){
// 相同则什么都不做
if (v === value) {
return;
}
// 先设置值
value = v;
// 再更新
dep.notify();
},
});
});
}
schedule.js
// 调度器是为了批量更新和同步调度,这里只写了nextTick和批量更新
const taskSet = new Map();
// 添加任务,然后调用nextTick
function addTask(depId, watcher){
if (!taskSet.has(depId)) {
taskSet.set(depId, new Set());
}
const set = taskSet.get(depId);
set.add(watcher);
nextTick(depId);
}
function nextTick(depId){
// nextTick优先尝试使用微任务,然后才是宏任务,这里做了简化
setTimeout(() => {
const set = taskSet.get(depId);
for (let watcher of set) {
// 执行每一个 watcher 的渲染函数
watcher.render();
}
// 执行完后清空任务
set.clear();
});
}
watcher.js
function Watcher(vm, key, render){
// 表示当前正在被创建的 watcher,源码中是一个 watcher 的栈
Dep.target = this;
this.vm = vm;
this.key = key;
// 渲染函数,由compiler生成
this.render = function (){
render(this.value);
};
this.value = this.get();
Dep.target = null;
// 更新这个 watcher 所在的 dep
this.update(this.depId);
}
// ⚠️源码中,get含义不同,get直接更新了视图
Watcher.prototype.get = function (){
// 会触发 defineReactive 中的 get,从而添加这个 watcher 到 dep
return this.vm[this.key];
};
Watcher.prototype.update = function (depId){
// 先得到新的值
this.value = this.get();
// 再添加到批量更新任务列表
addTask(depId, this);
};