New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Vue 进阶 ------- 深入响应式原理 #8

Open
Corbusier opened this Issue Jun 7, 2017 · 0 comments

Comments

Projects
None yet
1 participant
@Corbusier
Owner

Corbusier commented Jun 7, 2017

如何追踪变化

一个JS对象传给Vue实例的data选项,Vue将遍历此对象所有的属性,并使用Object.defineProperty将这些属性全部转为getter/setterObject.defineProperty属于ES5的特性,且无法优雅降级,所以Vue不支持IE8及以下的浏览器。

每个组件实例都有相应的watcher实例对象,在组件渲染的过程中把属性记录为依赖,之后当依赖项的setter调用时,通知watcher重新计算,从而使关联的组件更新。

data

变化检测问题

受限于JS及废弃的Object.observe,Vue不能检测到对象属性的添加或删除。由于Vue会在初始化实例时对属性执行getter/setter转化的过程,所以属性必须在data对象上保存才能被转换,如此,才可以让它是响应的。例如:

    new Vue({
        data:{
            a:1
        }
    })
    /* < !-- vm.a 是响应的 --> */
    
    vm.b = 2
    /* < !-- vm.b 是非响应的 --> */

关于ES5中的Object.defineProperty,通过它可以对obj.a进行数据监控,简单的示例如下:

    var obj = {};
    var a;
    Object.defineProperty(obj,"a",{
        get:function(){
            console.log('get val');
            return a;
        },
        set:function(newVal){
            console.log('set val:' + newVal);
            a = newVal;
        }
    })
    obj.a
    obj.a = "111"

Vue不允许在已创建的实例上动态添加新的根级响应式属性。但是可以使用Vue.set(object,key,value)方法将响应属性添加到嵌套的对象上:

    /*< !-- 一定要在实例化之前添加! -- > */
    Vue.set(vm.someObject, 'b', 2)

还可以使用vm.$set实例方法,这是全局Vue.set方法的别名:

    this.$set(this.someObject,'b',2)

声明响应式属性

Vue不允许动态添加根级响应式属性,所以必须在初始化实例前声明根级响应式属性,即便是一个空值:

    <div id="app">
           <span :message=""></span> 
    </div>
    
    var vm = new Vue({
        el:"#app",
        data:{
            message:""
        },
        template:'<div>{{ message }}</div>'
    })
    vm.message = "Hello!"   

如果没有在data选项中声明message,Vue报错会提升渲染函数时试图访问的属性不存在。

异步更新队列

Vue.js 默认异步更新 DOM。每当观察到数据变化时,Vue 就开始一个队列,将同一事件循环内所有的数据变化缓存起来。如果一个 watcher 被多次触发,只会推入一次到队列中。等到下一次事件循环,Vue 将清空队列,只进行必要的 DOM 更新。在内部异步队列优先使用MutationObserver,如果不支持则使用 setTimeout(fn, 0)。

    <div id="example">{{message}}</div>
    
    var vm = new Vue({
        el: '#example',
        data: {
            message: '123'
        }
    })
    vm.message = 'new message' // 更改数据
    console.log(vm.$el.textContent === 'new message')// false
    Vue.nextTick(function () {
        console.log(vm.$el.textContent === 'new message')// true
    })

@Corbusier Corbusier added the Vue label Jun 7, 2017

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment