试着写一个简单的
export function Vue(options = {}){
this.__init(options);
}
//initMixin
Vue.prototype.__init function(options){
this.$options = options;
//假设已经是一个el ,已经querySelector的
this.$el = options.el;
this.$data = options.data;
this.$methods = options.methods;
//beforeCreate -- initState -- initData;
proxy(this, this.$data);
//observer()
observer(this.$data);
new Compiler(this);
}
//this.$data.message -> this.message
function proxy(target, data){
Object.keys(data).forEach(key =>{
Object.defineProperty(target, key, {
enumerabke: true,
configurable: true,
get(){
return data[key],
},
set(newVal){
//考虑NaN的情况
if(!isSameVal(data[key], newVal)){
data[key] = newVal
}
}
})
})
}
function observer(target, data){
new Observer(data);
}
class Observer{
constructor(data){
this.walk(data);
}
walk(data){
if(data && typeof data === 'object'){
Object.keys(data).forEach(key=> this.defineReactive(data, key,data[key]));
}
}
//收集data里的数据
defineReactive(obj, key, value){
let that = this
this.walk(value);
let dep = new Dep();
Object.defineProperty(obj, key, {
configurable: true,
enumerable: true,
get(){
Dep.target && dep.add(Dep.target)
return value
},
set(newVal){
if(!isSameVal(value, newVal)){
//新赋值的没有响应式所以要walk一次给到响应式
value = newVal;
that.walk(newVal);
dep.notify();
}
}
})
}
}
//视图怎么更新
//数据改变,视图更新,需要观察
class Watcher{
constructor(vm, key, cb){
this.vm = vm;
this.key = key;
this.cb = cb;
Dep.target = this;
this._old = vm[key];
Dep.target = null;
}
//执行所有的cb
update(){
let newVal = this.vm[this.key];
if(!isSameVal(newVal, this._old)) this.cb(newVal);
}
}
//每一个数据都需要有一个Dep
class Dep{
constructor(){
this.deps = new Set();
}
add(watcher){
if(watcher && watcher.update) this.watchers.add(watcher);
}
//让所有的watcher 执行 update方法
notify(){
this.watchers.forEach(watc => watc.update())
}
}
class Compiler{
constructor(vm){
this.el = vm.$el;
this.vm = vm;
this.methdos = vm.$methods;
this.compile(vm.$el);
}
//递归编译#app下面的所有节点内容
compile(el){
let childNodes = el.childNodes;
//类数组
Array.from(childNodes).forEach(node =>{
if(node.nodeType === 3){
this.compileText(node);
}esle if(node.nodeType === 1){
this.compileElement(node);
}
if(node.childNode && node.childNodes.length) this.compile(node);
//
})
}
compileText(node){
//匹配{{ msg }}
let reg = /\{\{(.+?)\}\}/;
let value = node.textContent;
if(reg.text(value)){
let key = RegExp.$1.trim()
node.textContent = value.replace(reg, this.vm[key]);
new Watcher(this.vm, key, val=>{
node.textContent = val
})
}
}
compileElement(node){
//处理指令,这里只匹配 v-on 和 v-model
if(node.attributes.length){
Array.from(node.attributes).forEach(att =>{
let attrName = attr.name;
if(attrName.startsWith('v-')){
attrName = attrName.indexOf(':') > -1 ? attrName.substr(5) : attrName.substr(2)
let key attr.value;
this.update(node, key, attrName, this.vm[key]);
}
})
}
}
update(node, key, attrName, value){
if(attrName === 'model'){
node.value = value;
new Watcher(this.vm, key, val => node.value = val);
node.addEventListener('input', ()=>{
this.vm[key] = node.value;
})
}else if(attrName === 'click'){
node.addEventListener(attrName, this.methods[key].bind(this.vm))
}
}
}
function isSameVal(a, b){
return a === b || (Number.isNaN(a)&& Number.isNaN(b))
}
Vue2.x和Vue3.x对比
-
Vue 3.x 使用了proxy作为响应式,天生的代理,不用考虑属性重写,数组这些2.x中hack的情况;
-
diff,增加了最大递增子序列的算法,让我移动节点更高效;
-
架构采用monorepo 的方式,分层清晰,同时把编译部分也进行了一些拆解;
-
Vue3 对编译的内容进行了重写,template -- render 函数
- vue2基于正则,vue3基于状态机 --[ast 编译原理]
- patchFlag,标记哪些元素包含哪些元素
- 静态提升
-
vue3 使用了 blockTree,对比需要改变的,优化性能,如果要用jsx的写法就不用优化,不过可以自己去标记
-
ts 重构
-
compiler 拆成了4个包。方便重写
-
vue2 option API -- vue3 composition API
-
vue3是用来 rollup 打包 支持treeShaking
Vue3
function isObject(data){
return data && rtpeof data === 'object';
}
let targetMap = new WeakMap();
//在Vue2里有一个全局变量-Dep.target放watcher,所以还需要有一个全局变量来存放这么个东西。effect副作用
let activeEffect;
//依赖的收集
function track(target, key){
let depsMap = targetMap.get(target);
if(!depsMap) targetMap.set(target,(depsMap = new Map()));
//再判断 depsMap中有没有key
let dep = depsMap.get(key);
if(!dep) depsMap.set(key, (dep = new Set()));
trackEffect(dep)
}
function trackEffect(){
if(!dep.has(activeEffect)) dep.add(activeEffect); //Dep.target && dep.add(Dep.target)
}
//触发
function trigger(targrt, key){
const depsMap = targetMap.get(target);
if(!depsMap) return ;
//effect有一个run方法
depsMap.get(key).forEach(effect => effect && effect.run())
}
export function rective(data){
if(!isObject(data)) return;
return new Proxy(data, {
get(target, key, receiver){
const ret = Reflect.get(target, key, receiver);
track(target, key);
return isObject(ret) ? rective(ret): ret;
set(target, key, value, receiver){
Reflect.set(target, key, value, receiver);
trigger(target, key)
return true;
},
deleteProperty(target, key){
const ret = Reflect.deleteProperty(target, key);
trigger(target, key)
return ret;
},
has(target, key){
track(target, key);
const ret = Reflect.has(target, key);
},
ownKeys(target){
track(target, key);
return Reflect.ownKeys(target);
}
}
})
}
// const num = ref(0);
// num.value=xxx
export function ref(init){
class RefImpl{
constructor(init){
this.__value = init;
}
get value(){
track(this, 'value');
return this.__value;
}
set value(newVal){
this.__value = newVal;
trigger(this, 'value');
}
}
return new RefImpl(init)
}
//定义一个effect 的函数中第一个参数是个函数
//如果这个函数中有使用ref/reactive
function effect(fn, options = {}){
let __effect = new ReactiveEffect(fn);
if(!options.lazy){
__effect.run()
}
return __effect;
}
export function computed(fn){
//只考虑函数情况
let __computed;
const e = effect(fn,{lazy:true});
__computed = {
get value(){
return e.run();
}
}
}
export function mount(instance, el){
effect(function(){
instance.$data && update(linstance , el);
})
instance.$data = instance.setup();
update(instance, el);
function update(instance, el){
el.innerHTML = instance.render()
}
}
class ReactiveEffect {
constructor(fn){
this.fn = fn;
}
run(){
activeEffect = this;
return this.fn();
}
}