• vue03-vue工作原理


    vue运行机制:

    当我们new Vue的时候,实际上在构造函数里

    1.执行了一个init方法,init方法会初始化data,props,computed,watch,事件监听,派发生命周期钩子等等,

    2.得到Vue实例后进行挂载$mount(最终转换成render),查看$mount方法的源码,$mount函数再vue的原型上被定义(Vue.prototype.$mount),,函数的主要操作时,拿到el,将其转化为dom对象,接下来判断是否有render函数,没有的话判断是否有template,有templat就将其编译成render函数,如果没有template但是有el,则取el的outerHTML,然后编译陈render函数

    3.挂载最重要的作用就是进行虚拟DOM的计算过程,虚拟DOM的获取执行的是render function(渲染函数)

    4.第一条线是初始化的过程:render函数返回的是虚拟DOM,虚拟DOM通过patch()将虚拟DOM变为真实DOM;

    4.第二条线是更新过程:初始化完成后,有个更新过程,render函数里有好多值的获取,这些值已经做了响应化处理,响应化其实就是拦截的过程defineProperty,当访问数据时,watcher做一个依赖收集,把当前访问的属性和render函数间建立一个关系,当改数据时,watcher会执行render函数,做update,这块涉及一个diff算法,更新时会出现一个全新的虚拟DOM,与初始化的虚拟DOM进行对比

    说一下compile(编译):定义视图的时候会写template标签,webpack里会配一个vue-loader的加载器,执行一个编译过程,将template转换为render函数,平时写template其实是定义render函数,转换成的render函数会等到$mount的时候调用,中间complile的过程执不执行取决于运行环境,运行环境通常有两种,如果在浏览器中引入了一个带编译器的vue.js,在界面中写一些带字符串的模板,这些模板在浏览器中实时编译,这种称为待运行时的编译,还有一种是开发中的webpack的环境,webpack环境是预编译,(写的template在我打包的时候提前把template编译好了,程序运行时render函数已经存在了,不用经过实时编译,称为运行时编译

     写一个简版的vue.js来看一下双向数据绑定的原理

    1.首先创建一个Vue类,通过监听器observe方法来实现数据的劫持与监听,如果有变动就通知订阅者(遍历data,通过Object.defineProperty方法中的get方法,监听到对数据的访问,然后将其push到对应的deps数组中,其中的set方法,可以监听到对数据的修改,从而调用Dep中的notify方法,通知更新

    2.创建Dep类,用来与data中的数据一一对应:一个数据属性对应一个dep,所以在遍历data的时候,创建Dep的实例,与key做一一对应;再一个,属性在组件中访问或出现过一次,就会创建一个watcher,defineProperty的get方法可以监听到属性的访问,所以在get中,为dep添加一个watcher;set方法可以监听到数据的修改,数据修改就会调用Dep的notify方法,来通知deps中的所有数据进行更新

    3.创建(订阅者)watcher类,负责创建data中key和更新函数的映射关系,当收到属性变化的通知会执行相应的函数,实现视图的更新

    //watcher在做编译的时候初始化,实际在vue中是通过render函数在调用时触发的,今天我们在Vue中主动触发,来做验证
    //dep已经通过get里面的收集工作保存了相关watcher,它通知他的更新
     
    vue.js
    //new Vue({data:{....}})  创建vue实例的写法,
    class Vue {
        //1.数据响应化
        constructor (options) {
            this.$options = options;
            //处理传入data
            this.$data = options.data;
            //响应化
            this.observe(this.$data)
             //2.依赖收集
              new Watcher(this,'test');
              this.test    
        }
    
        observe(data){
            //遍历的必须是对象---我们只实现数据时对象的格式,数组的未实现
            if(!data || typeof data !== 'object'){
                return;
            }
            //遍历
            Object.keys(data).forEach(key => {
                //真正的响应化处理
                 this.defineReactive(data,key,data[key])
    
                 //代理data中的属性到vue实例上,在访问vue实例中的data属性时,省略了$data( vm.$data.test->vm.test)
                 this.proxyData(key)
            })
        }
    
        defineReactive(data,key,val){ //尝试创建一个闭包,一直保存key与value值,保存应用程序状态
            //递归
            this.observe(val);
            //创建Dep的实例和key一一对应
            const dep = new Dep();//一个dep对应一个属性
            Object.defineProperty(data,key,{  //不能深层修改,所以上面加了递归
                get(){
                    //一次访问对应一次watcher,每次访问就会创建一个watcher,此时将this绑定到里Dep.target上
                    Dep.target && dep.addDep(Dep.target);//一个dep对应多个watcher
                    return val
                },
                set(newVal){
                    if(newVal === val){
                        return
                    }
                    val = newVal;
                    dep.notify();//更新时dep通知所有watcher进行更新
                    console.log(`${key}更新了`)
                }
            })
        }
    
        proxyData(key){
            //需要给vue实例定义属性
            Object.defineProperty(this,key,{  //不能深层修改,所以上面加了递归
                get(){
                    return this.$data[key]
                },
                set(newVal){
                    this.$data[key] = newVal
                }
            })
        }
    }
    //Dep:和data中的每一个key对应起来,主要负责管理相关watcher
    class Dep {
        constructor(){
            this.deps = [];
        }
        addDep(dep){
            this.deps.push(dep)
        }
        notify(){
            this.deps.forEach(dep => {
                dep.update()
            })
        }
    }
    
    //Watcher:负责创建data中key和更新函数的映射关系
    class Watcher{
        constructor(vm,key,cb){
            this.vm = vm;
            this.key = key;
            this.cb = cb;
    
            Dep.target = this;//把当前watcher实例附加到Dep静态属性上
           
        }
    
        update(){
            console.log(`${this.key}属性更新了`)
        }
    }
    <head>
        <meta charset="utf-8">
        <title></title>
        <script src="./vue.js"></script>
        <script>
            var vm = new Vue({
                data:{
                    test:'i am a test'
                }
            })
            vm.test = 'change'
        </script>
    </head>
    <body>
    </body>
    </html>
    

     控制台会打印更新即为成功

    --------------------------------------------------------------------------------------------------------------------------

    总结:

    1.数据响应化处理,仅仅实现了对象响应化,数组怎么实现?

    2.自定义组件的实现?

    3.没有出现虚拟DOM(此例子中不需要)因为此例子中是Vue1.0的实现,在界面中每出现一次数据的绑定,指令等每出现一个动态的值,都会创建一个watcher与之相对应,这是vue1.0最大的问题;vue2.0最大的改变是引入虚拟DOM,把watcher的力度变小了,小到组件级别,每个组件只有一个watcher;

    vue1.0:

      一个数据属性对应一个dep  一个dep对应多个watcher(一个dep保存多个watcher),同一个属性,界面中用到了一次(访问一次),就会创建一个watcher,然后保存在对应的dep中
    vue2.0:
      一个组件只有一个watcher,watcher数量大大减少(一个watcher对应多个dep),这样当组件中的数据发生变化是,我们不知道是哪个数据发生了变化,所以引入虚拟DOM机制,将所有变化记录下来,然后把变化前和变化后做比对,
  • 相关阅读:
    webpack 爬坑
    npm install 安装依赖一直失败(解决)
    vue中使用keepAlive组件缓存,如何清缓存(有些时候页面不需要缓存)
    JS 解决安卓手机输入框被软键盘遮住的问题
    vue项目keep-alive返回记住滚动条位置
    vue中监听路由参数变化
    win10 优化
    提升工作能力的表达能力
    TFS变更地址
    iis php web.config处理404,500等,跳转友好页面,显示500错误信息
  • 原文地址:https://www.cnblogs.com/znLam/p/12879654.html
Copyright © 2020-2023  润新知