简单的vue响应式

102 阅读2分钟

跟随大师课简单学习vue响应式

首先定义一个简单的页面

<body>
    <div class="card">
        <p id="firstName"></p>
        <p id="lastName"></p>
        <p id="age"></p>
    </div>
    <script src="./index.js"></script>
</body>

js文件

var user={
    name:'尤雨溪',
    birth:'2002-05-14'
}
// 显示姓
function showFirstName(){
    document.querySelector('#firstName').
    textContent='姓:'+user.name[0];
}
// 显示名字
function showLastName(){
    document.querySelector('#lastName').
    textContent='名:'+user.name.slice(1);
}

showFirstName()
showLastName()

此时页面上显示为

屏幕截图(162).png

此时如果我们在js文件或控制台中直接修改user.name的话,虽然数据变化了,但是页面却没有发生变化,导致数据与界面不一致

屏幕截图(164).png

如果想让页面变化的话则需要运行他所依赖的函数,

所以需要让数据发生变化时自动的调用他所依赖的函数

使用Object.defineProperty来自动调用函数

var user={
    name:'尤雨溪',
    birth:'2002-05-14'
}
// observe(user) //观察数据
// 显示姓
function showFirstName(){
    document.querySelector('#firstName').
    textContent='姓:'+user.name[0];
}
// 显示名字
function showLastName(){
    document.querySelector('#lastName').
    textContent='名:'+user.name.slice(1);
}

showFirstName()
showLastName()

var internalName=user.name
Object.defineProperty(user,'name',{
    get:function(){
        console.log('有人读取了name属性');
        return internalName;
    },
    set:function(val){
        internalName=val;
        showFirstName()
        showLastName()
        console.log('有人再给name属性赋值,赋的值是'+val);
    }
})
user.name='奥特曼'

但是这样是一个写死的,实际情况肯定是不能这样写,所以新建一个通用的js文件,用来在数据变化时自动的运行依赖的函数

// 观察某个对象的所有属性
function observe(obj){
    for(const key in obj){
        let internalValue=obj[key]; //存储
        Object.defineProperty(obj,ley,{
            get:function(){
                return internalValue;
            },
            set:function(val){
                internalValue=val
                // 自动调用依赖该属性的函数???
            },
        })
    }
}

但是此时因为变成了一个公共的库,所以并不知道有哪些函数,所以可以声明一个空数组通过Object.defineProperty的get来存储用我的函数,

// 观察某个对象的所有属性
function observe(obj){
    for(const key in obj){
        let internalValue=obj[key]; //存储
        let foncs=[] //新建一个空数组用来存储用我的函数
        Object.defineProperty(obj,ley,{
            get:function(){
                // 记录是哪个函数在用我,vue里叫做依赖收集
                foncs.push(abc) //还是不知道函数叫什么
                return internalValue;
            },
            set:function(val){
                internalValue=val
                // 执行用我的函数,vue里叫做派发更新
                for(var i=0;i<foncs.length;i++){
                    foncs[i];
                }
            },
        })
    }
}

但是此时还有一个问题是如果一个函数改变了多次数据的话会push多次,所以通过判断或者net Set()来避免重复

// 观察某个对象的所有属性
function observe(obj){
    for(const key in obj){
        let internalValue=obj[key]; //存储
        let funcs=[] //新建一个空数组用来存储用我的函数
        Object.defineProperty(obj,ley,{
            get:function(){
                // 记录是哪个函数在用我,vue里叫做依赖收集
                if(!funcs.includes(abc)){
                    funcs.push(abc)//还是不知道函数叫什么
                }
                return internalValue;
            },
            set:function(val){
                internalValue=val
                // 执行用我的函数,vue里叫做派发更新
                for(var i=0;i<funcs.length;i++){
                   funcs[i]();
                }
            },
        })
    }
}

但是此时此刻我们还是不知道有哪些函数在用我,vue中用了一个非常巧妙的方法来解决这个问题,不直接运行函数,而是在他运行之前把他交给另外一个东西,比如给他声明一个全局变量,在他运行之前window.__func=showFirstName 将函数赋给变量,在函数运行完后在设为空,这样在Object.defineProperty的get中就可以获取到这个全局变量将他push到我们准备好的数组中

var user={
    name:'尤雨溪',
    birth:'2002-05-14'
}
observe(user) //观察数据
// 显示姓
function showFirstName(){
    document.querySelector('#firstName').
    textContent='姓:'+user.name[0];
}
// 显示名字
function showLastName(){
    document.querySelector('#lastName').
    textContent='名:'+user.name.slice(1);
}
window.__func=showFirstName
showFirstName()
window.__func=null

window.__func=showLastName
showLastName()
window.__func=null
// 观察某个对象的所有属性
function observe(obj) {
    for (const key in obj) {
        let internalValue = obj[key]; //存储
        let funcs = [] //新建一个空数组用来存储用我的函数
        Object.defineProperty(obj, key, {
            get: function () {
                // 记录是哪个函数在用我,vue里叫做依赖收集
                if (window.__func && !funcs.includes(window.__func)) {
                    funcs.push(window.__func)

                }
                return internalValue;
            },
            set: function (val) {
                internalValue = val
                // 执行用我的函数,vue里叫做派发更新
                for (var i = 0; i < funcs.length; i++) {
                    funcs[i]();
                }
            },
        })
    }
}

此时再修改user中的数据的话页面也会随之改变,但是这个一直重复赋值在给空比较麻烦,所以可以在公共的js中写一个函数autorun

function autorun(fn){
    window.__func=fn
    fn()
    window.__func=null
}
autorun(showFirstName)
autorun(showLastName)

可以设置一个input框试一下效果

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <div class="card">
        <p id="firstName"></p>
        <p id="lastName"></p>
        <p id="age"></p>
    </div>
    <input type="text" oninput="user.name=this.value"/>
    <script src="./eve.js"></script>
    <script src="./index.js"></script>
</body>
</html>
var user={
    name:'尤雨溪',
    birth:'2002-05-14'
}
observe(user) //观察数据
// 显示姓
function showFirstName(){
    document.querySelector('#firstName').
    textContent='姓:'+user.name[0];
}
// 显示名字
function showLastName(){
    document.querySelector('#lastName').
    textContent='名:'+user.name.slice(1);
}

autorun(showFirstName)
autorun(showLastName)

// 观察某个对象的所有属性
function observe(obj) {
    for (const key in obj) {
        let internalValue = obj[key]; //存储
        let funcs = [] //新建一个空数组用来存储用我的函数
        Object.defineProperty(obj, key, {
            get: function () {
                // 记录是哪个函数在用我,vue里叫做依赖收集
                if (window.__func && !funcs.includes(window.__func)) {
                    funcs.push(window.__func)

                }
                return internalValue;
            },
            set: function (val) {
                internalValue = val
                // 执行用我的函数,vue里叫做派发更新
                for (var i = 0; i < funcs.length; i++) {
                    funcs[i]();
                }
            },
        })
    }
}
function autorun(fn){
    window.__func=fn
    fn()
    window.__func=null
}

此时数据发生变化页面也会随之变化,代码已经写完,肯定会有很多bug,但算知道了一个大概的脉络

从这里可以得出,vue的数据响应式是 数据变化后,会重新运行依赖该数据的函数