最近在网上搜到部很棒的vue视频,尤大亲自讲解( 之前也看到过但是这次是有中文字幕版的 )虽然之前也有看过别的vue源码解析对着老师的demo敲代码但是还是一知半解( 还是自己太菜了/(ㄒoㄒ)/~~ ),不过这次再看遍尤大的讲解理解又更深了一点,所以突发奇想要不写篇博客吧,就这样我的第一篇博客诞生了( 主要是觉得简历上贴个个人博客 github地址应该会加分吧O(∩_∩)O ),这次跟着视频实现了vue响应式的mini版,源码一下子接受不了我们就从简单的实现开始吧(●'◡'●)
首先vue的响应式主要是在Observer dep watch这几个类中实现得。这次先不管watch,先实现数据的响应式和依赖收集发布跟新。
大概思路: 先创建obersver函数内部使用 defineProperty 进行数据的拦截,再创建 Dep 类里面有两个方法 depend 和 notify,depend用来收集依赖,notify用来发布跟新
obersver中主要用到了defineProperty的get set两个方法,当访问对象属性的时候就会触发get函数,get函数中返回的数据就是你访问属性得到的数据。改变值得时候就会触发set函数,set函数接受一个参数即被赋予的新值。但是如果是后期手动在对象中添加属性就不会被 defineProperty 拦截所以vue中动态添加属性要用vue.$set()。下面代码中可以看出bar是手动添加的字面量属性当我访问它的时候并没有被监听到
let obj = {
foo: 'hahah',
name: 'zs',
age: 20,
};
Object.defineProperty( data, 'foo', {
get(){
console.log('我被访问了');
return 'hahah'
},
set( newVal ){
console.log('我被改变了', newVal);
}
} )
}
现在来实现Dep类,首先声明了autorun函数内部传入跟新的回调函数在回调中使用depend绑定依赖关系然后声明全局变量 activeUpdate 储存当前正在运行的函数,Dep中声明depend函数订阅当前正在运行的函数,notify中遍历 subscribes 触发所有跟新的方法,可以在autorun做点事情,当对象属性被改变的时候就会触发autorun中的函数
window.Dep = class Dep{
constructor(){
this.subscribes = new Set()
}
depend(){
if( activeUpdate ){
this.subscribes.add( activeUpdate )
}
}
notify(){
this.subscribes.forEach( v => v() );
}
}
let activeUpdate;
function autorun( update ){
function wrappUpdate(){
activeUpdate = wrappUpdate
update()
activeUpdate = null
}
wrappUpdate()
}
let dep = new Dep()
autorun( () => {
dep.depend()
console.log('update');
} )
dep.notify()
最后我们可以结合上面两部分实现mini版的响应式,先把state中的数据绑定到模板上。然后封装Obersver方法,既然 defineproperty 只能监听单个属性那就只能用遍历的方法对所有属性进行监听,然后在get函数中收集依赖,set函数中发布跟新。当点击按钮时name就会被改变p元素中的内容也会发生变化
<p></p>
<button>点击改变</button>
let state = {
name: 'zs',
age: 20
}
window.Dep = class Dep{
constructor(){
this.subscribe = new Set()
}
depend(){
if( actvieUpdate ){
this.subscribe.add( actvieUpdate )
}
}
notify(){
this.subscribe.forEach( v => v() )
}
}
let actvieUpdate;
function obersver( data ){
let dep = new Dep()
Object.keys( data ).forEach( v => {
let realVal = data[ v ]
Object.defineProperty( data, v, {
get(){
//收集依赖
console.log('属性被访问了');
dep.depend()
return realVal
},
set( newVal ){
console.log('属性被设置了', newVal);
realVal = newVal
//发布跟新
dep.notify()
}
} )
} )
}
function autorun( update ){
function wraapUpdate(){
actvieUpdate = wraapUpdate
update()
actvieUpdate = null
}
wraapUpdate()
}
obersver( state )
let p = document.querySelector('p')
render()
let button = document.querySelector('button')
button.onclick = () => {
state.name = 'ls'
}
function render(){
p.innerText = state.name
}
autorun( render )
视频地址: https://www.bilibili.com/video/BV1d4411v7UX?p=4&t=545
( 个人理解,如有不对,欢迎指正 😊 )