VUE系列之李南江写VUE源码

余生长醉 提交于 2020-03-01 13:40:57

一 VUE响应式原理

第一部分:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>01-Vue基本模板</title>
    <!--1.下载导入Vue.js-->
    <script src="js/vue.js"></script>
</head>
<body>
<div id="app">
    <input type="text" v-model="name">
    <p>{{ name }}</p>
</div>
<script>
    // 2.创建一个Vue的实例对象
    let vue = new Vue({
        // 3.告诉Vue的实例对象, 将来需要控制界面上的哪个区域
        el: '#app',
        // 4.告诉Vue的实例对象, 被控制区域的数据是什么
        data: {
            name: "李南江"
        }
    });
    /*
    1.Vue响应式的原理(数据改变界面就会改变)是什么?
      时时监听数据变化, 一旦数据发生变化就更新界面
    2.Vue是如何实现时时监听数据变化的?
      通过原生JS的defineProperty方法

    3.defineProperty方法的特点
      可以直接在一个对象上定义一个新属性,或者修改一个对象的现有属性, 并返回这个对象

    4.defineProperty用法
    obj: 需要操作的对象
    prop: 需要操作的属性
    descriptor: 属性描述符
    Object.defineProperty(obj, prop, descriptor)
    * */
    let obj = {name: '李南江'};
    // 需求: 给obj对象动态新增一个name属性, 并且name属性的取值必须是lnj
    Object.defineProperty(obj, 'name', {
        // 可以通过value来告诉defineProperty方法新增的属性的取值是什么
        value: 'lnj',
        // 默认情况下通过defineProperty新增的属性的取值是不能修改的
        // 如果想修改, 那么就必须显示的告诉defineProperty方法
        // writable: true
        writable: true,
        // 默认情况下通过defineProperty新增的属性是不能删除的
        // 如果想删除, 那么就必须显示的告诉defineProperty方法
        configurable: true,
        // 默认情况下通过defineProperty新增的属性是不能迭代的
        // 如果想迭代, 那么就必须显示的告诉defineProperty方法
        enumerable: true
    });
    console.log(obj);
    // 注意点: 默认情况下通过defineProperty新增的属性的取值是不能修改的
    // obj.name = 'it666';
    // console.log(obj);
    // 注意点: 默认情况下通过defineProperty新增的属性是不能删除的
    // delete obj.name
    // console.log(obj);
    // 注意点: 默认情况下通过defineProperty新增的属性是不能遍历(迭代的)
    // for(let key in obj){
    //     console.log(key, obj[key]);
    // }
</script>
</body>
</html>

第二部分:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>01-Vue基本模板</title>
    <!--1.下载导入Vue.js-->
    <script src="js/vue.js"></script>
</head>
<body>
<div id="app">
    <input type="text" v-model="name">
    <p>{{ name }}</p>
</div>
<script>
    // 2.创建一个Vue的实例对象
    let vue = new Vue({
        // 3.告诉Vue的实例对象, 将来需要控制界面上的哪个区域
        el: '#app',
        // 4.告诉Vue的实例对象, 被控制区域的数据是什么
        data: {
            name: "李南江"
        }
    });
    /*
    1.defineProperty方法
    defineProperty除了可以动态修改/新增对象的属性以外
    还可以在修改/新增的时候给该属性添加get/set方法
    2.defineProperty get/set方法特点
    只要通过defineProperty给某个属性添加了get/set方法
    那么以后只要获取这个属性的值就会自动调用get, 设置这个属性的值就会自动调用set
    3.注意点:
    如果设置了get/set方法, 那么就不能通过value直接赋值, 也不能编写writable:true
    * */
    let obj = {};
    let oldValue = '李南江';
    Object.defineProperty(obj, 'name', {
        // value: oldValue,
        // writable: true,
        configurable: true,
        enumerable: true,
        get(){
            console.log("get方法被执行了");
            return oldValue;
        },
        set(newValue){
            if(oldValue !== newValue){
                console.log("set方法被执行了");
                oldValue = newValue;
            }
        }
    });
    console.log(obj.name);
    obj.name = 'lnj';
</script>
</body>
</html>

第三部分:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>01-Vue基本模板</title>
    <!--1.下载导入Vue.js-->
    <script src="js/vue.js"></script>
</head>
<body>
<div id="app">
    <input type="text" v-model="name">
    <p>{{ name }}</p>
</div>
<script>
    // 2.创建一个Vue的实例对象
    let vue = new Vue({
        // 3.告诉Vue的实例对象, 将来需要控制界面上的哪个区域
        el: '#app',
        // 4.告诉Vue的实例对象, 被控制区域的数据是什么
        data: {
            name: "李南江"
        }
    });
    /*
    需求: 快速监听对象中所有属性的变化
    * */
    let obj = {
        name: 'lnj',
        // name: {a: 'abc'},
        age: 33
    };

    class Observer{
        // 只要将需要监听的那个对象传递给Observer这个类
        // 这个类就可以快速的给传入的对象的所有属性都添加get/set方法
        constructor(data){
            this.observer(data);
        }
        observer(obj){
            if(obj && typeof obj === 'object'){
                // 遍历取出传入对象的所有属性, 给遍历到的属性都增加get/set方法
                for(let key in obj){
                    this.defineRecative(obj, key, obj[key])
                }
            }
        }
        // obj: 需要操作的对象
        // attr: 需要新增get/set方法的属性
        // value: 需要新增get/set方法属性的取值
        defineRecative(obj, attr, value){
            // 如果属性的取值又是一个对象, 那么也需要给这个对象的所有属性添加get/set方法
            this.observer(value);
            Object.defineProperty(obj, attr, {
                get(){
                    return value;
                },
                set:(newValue)=>{
                    if(value !== newValue){
                        // 如果给属性赋值的新值又是一个对象, 那么也需要给这个对象的所有属性添加get/set方法
                        this.observer(newValue);
                        value = newValue;
                        console.log('监听到数据的变化, 需要去更新UI');
                    }
                }
            })
        }
    }
    new Observer(obj);
    // obj.name = 'it666';
    // obj.age = 666;
    // obj.name.a = 'it666';
    obj.name = {a: 'abc'};
    obj.name.a = 'it666';
</script>
</body>
</html>

 二 构建VUE实例

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>01-Vue基本模板</title>
    <!--1.下载导入Vue.js-->
<!--    <script src="js/vue.js"></script>-->
    <script src="js/nue.js"></script>
</head>
<body>
<div id="app">
    <input type="text" v-model="name">
    <p>{{ name }}</p>
    <p>{{age}}</p>
    <ul>
        <li>6</li>
        <li>6</li>
        <li>6</li>
    </ul>
</div>
<script>
    /*
    1.要想使用Vue必须先创建Vue的实例, 创建Vue的实例通过new来创建, 所以说明Vue是一个类
      所以我们要想使用自己的Vue,就必须定义一个名称叫做Vue的类
    2.只要创建好了Vue的实例, Vue就会根据指定的区域和数据, 去编译渲染这个区域
      所以我们需要在自己编写的Vue实例中拿到数据和控制区域, 去编译渲染这个区域
      注意点: 创建Vue实例的时候指定的控制区域可以是一个ID名称, 也可以是一个Dom元素
      注意点: Vue实例会将传递的控制区域和数据都绑定到创建出来的实例对象上
              $el/$data
    * */
    // 2.创建一个Vue的实例对象
    // let vue = new Vue({
    let vue = new Nue({
        // 3.告诉Vue的实例对象, 将来需要控制界面上的哪个区域
        el: '#app',
        // el: document.querySelector('#app'),
        // 4.告诉Vue的实例对象, 被控制区域的数据是什么
        data: {
            name: "李南江",
            age: 33
        }
    });
    console.log(vue.$el);
    console.log(vue.$data);
</script>
</body>
</html>

NUE代码:

class Nue {
    constructor(options){
        // 1.保存创建时候传递过来的数据
        if(this.isElement(options.el)){
            this.$el = options.el;
        }else{
            this.$el = document.querySelector(options.el);
        }
        this.$data = options.data;
        // 2.根据指定的区域和数据去编译渲染界面
        if(this.$el){
            new Compiler(this)
        }
    }
    // 判断是否是一个元素
    isElement(node){
        return node.nodeType === 1;
    }
}
class Compiler {
    constructor(vm){
        this.vm = vm;
    }
}

 

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!