首頁  >  文章  >  web前端  >  詳解vue中的資料初始化(initState)

詳解vue中的資料初始化(initState)

青灯夜游
青灯夜游轉載
2020-10-30 17:57:064581瀏覽

下面Vue.js教學欄位帶大家了解vue中的資料初始化(initState)。有一定的參考價值,有需要的朋友可以參考一下,希望對大家有幫助。

詳解vue中的資料初始化(initState)

資料初始化

#Vue 實例在建立的時候會執行一系列的初始化操作,而在這些初始化操作裡面,和資料綁定關聯最大的是initState。

首先,來看他的程式碼:

function initState(vm) {
    vm._watchers = [];
    var opts = vm.$options;
    if(opts.props) {
        initProps(vm, opts.props); //初始化props
    }
    if(opts.methods) {
        initMethods(vm, opts.methods); //初始化methods
    }
    if(opts.data) {
        initData(vm); //初始化data
    } else {
        observe(vm._data = {}, true /* asRootData */ );
    }
    if(opts.computed) {
        initComputed(vm, opts.computed); //初始化computed
    }
    if(opts.watch && opts.watch !== nativeWatch) {
        initWatch(vm, opts.watch); //初始化watch
    }
}

在這麼多的資料的初始化中,props、methods和data是比較簡單的(所以我就不詳細介紹了☺) ,而computed 和watch則相對較難,邏輯較複雜,所以我下面主要講下computed 和watch(以下程式碼部分為簡化後的)。

initState裡面主要是對vue實例中的 props, methods, data, computed 和 watch 資料進行初始化。

在初始化props的時候(initProps),會遍歷props中的每個屬性,然後進行型別驗證,資料監測等(提供為props屬性賦值就拋出警告的鉤子函數)。

在初始化methods的時候(initMethods),主要是監測methods中的方法名稱是否合法。

在初始化data的時候(initData),會執行 observe 函數深度遍歷資料中的每一個屬性,進行資料劫持。

在初始化computed的時候(initComputed),會監測資料是否已經存在data或props上,如果存在則拋出警告,否則調用defineComputed函數,監聽數據,為元件中的屬性綁定getter及setter。如果computed中屬性的值是一個函數,則預設為屬性的getter函數。另外屬性的值還可以是一個對象,他只有三個有效字段set、get和cache,分別表示屬性的setter、getter和是否啟用緩存,其中get是必須的,cache預設為true。

function initComputed(vm, computed) {
    var watchers = vm._computedWatchers = Object.create(null);

    for(var key in computed) {
        var userDef = computed[key];
        var getter = typeof userDef === 'function' ? userDef : userDef.get;

        //创建一个计算属性 watcher
        watchers[key] = new Watcher(
            vm,
            getter || noop,
            noop,
            computedWatcherOptions
        );

        if(!(key in vm)) {
            //如果定义的计算属性不在组件实例上,对属性进行数据劫持
            //defineComputed 很重要,下面我们再说
            defineComputed(vm, key, userDef);
        } else {
            //如果定义的计算属性在data和props有,抛出警告
        }
    }
}

在初始化watch的時候(initWatch),會呼叫vm.$watch函數為watch中的屬性綁定setter回呼(如果元件中沒有該屬性則不能成功監聽,屬性必須存在於props、 data或computed中)。如果watch中屬性的值是函數,則預設為屬性的setter回呼函數,如果屬性的值是數組,則遍歷數組中的內容,分別為屬性綁定回調,此外屬性的值還可以是一個對象,此時,物件中的handler欄位代表setter回呼函數,immediate代表是否立即先去執行裡面的handler方法,deep代表是否深度監聽。

vm.$watch函數會直接使用Watcher建構觀察者物件。 watch中屬性的值是作為watcher.cb存在,在觀察者update的時候,在watcher.run函數中執行。想了解這個過程可以看我上一篇的 vue響應式系統--observe、watcher、dep中關於Watcher的介紹。

function initWatch(vm, watch) {
    //遍历watch,为每一个属性创建侦听器
    for(var key in watch) {
        var handler = watch[key];
        //如果属性值是一个数组,则遍历数组,为属性创建多个侦听器
        //createWatcher函数中封装了vm.$watch,会在vm.$watch中创建侦听器
        if(Array.isArray(handler)) {
            for(var i = 0; i < handler.length; i++) {
                createWatcher(vm, key, handler[i]);
            }
        } else {
            //为属性创建侦听器
            createWatcher(vm, key, handler);
        }
    }
}

function createWatcher(vm, expOrFn, handler, options) {
    //如果属性值是一个对象,则取对象的handler属性作为回调
    if(isPlainObject(handler)) {
        options = handler;
        handler = handler.handler;
    }
    //如果属性值是一个字符串,则从组件实例上寻找
    if(typeof handler === &#39;string&#39;) {
        handler = vm[handler];
    }
    //为属性创建侦听器
    return vm.$watch(expOrFn, handler, options)
}

computed

computed本質是一個惰性求值的觀察者,具有快取性,只有當依賴變化後,第一次訪問computed 屬性,才會計算新的值

下面將圍繞這句話來做解釋。

上面程式碼中提到過,當計算屬性中的資料存在與data和props中時,會被警告,也就是這種做法是錯誤的。所以一般的,我們都會直接在計算屬性中聲明資料。還是那個程式碼片段中,如果定義的運算屬性不在元件實例上,會執行defineComputed函數對資料進行資料劫持。下面我們來看看defineComputed函數中做了什麼。

function defineComputed(target, key, userDef) {
//是不是服务端渲染
var shouldCache = !isServerRendering();
//如果我们把计算属性的值写成一个函数,这时函数默认为计算属性的get
if(typeof userDef === &#39;function&#39;) {
sharedPropertyDefinition.get = shouldCache ?
//如果不是服务端渲染,则默认使用缓存,设置get为createComputedGetter创建的缓存函数
createComputedGetter(key) :
//否则不使用缓存,直接设置get为userDef这个我们定义的函数
userDef;
//设置set为空函数
sharedPropertyDefinition.set = noop;
} else {
//如果我们把计算属性的值写成一个对象,对象中可能包含set、get和cache三个字段
sharedPropertyDefinition.get = userDef.get ?
shouldCache && userDef.cache !== false ?
//如果我们传入了get字段,且不是服务端渲染,且cache不为false,
//设置get为createComputedGetter创建的缓存函数
createComputedGetter(key) : 
//如果我们传入了get字段,但是是服务端渲染或者cache设为了false,设置get为userDef这个我们定义的函数
userDef.get :
//如果没有传入get字段,设置get为空函数
noop;
//设置set为我们传入的传入set字段或空函数
sharedPropertyDefinition.set = userDef.set ?
userDef.set :
noop;
}
//虽然这里可以get、set都可以设置为空函数
//但是在项目中,get为空函数对数据取值会报错,set为空函数对数据赋值会报错
//而computed主要作用就是计算取值的,所以get字段是必须的

//数据劫持
Object.defineProperty(target, key, sharedPropertyDefinition);
}

在上一篇的vue響應式系統--observe、watcher、dep 中,我有關於Watcher的介紹中提到,計算屬性watcher實例化的時候,會把options.lazy設定為true,這裡是計算屬性惰性求值,且可快取的關鍵,當然前提是cache不為false。

cache不為false,會呼叫createComputedGetter函數建立計算屬性的getter函數computedGetter,

先來看一段程式碼

function createComputedGetter(key) {
    return function computedGetter() {
        var watcher = this._computedWatchers && this._computedWatchers[key];
        if(watcher) {
            if(watcher.dirty) {
            //watcher.evaluate中更新watcher的值,并把watcher.dirty设置为false
            //这样等下次依赖更新的时候才会把watcher.dirty设置为true,
            //然后进行取值的时候才会再次运行这个函数
                watcher.evaluate();
            }
            //依赖追踪
            if(Dep.target) {
                watcher.depend();
            }
            //返回watcher的值
            return watcher.value
        }
    }
}

//对于计算属性,当取值计算属性时,发现计算属性的watcher的dirty是true
//说明数据不是最新的了,需要重新计算,这里就是重新计算计算属性的值。
Watcher.prototype.evaluate = function evaluate() {
    this.value = this.get();
    this.dirty = false;
};

//当一个依赖改变的时候,通知它update
Watcher.prototype.update = function update() {
    //三种watcher,只有计算属性 watcher的lazy设置了true,表示启用惰性求值
    if(this.lazy) {
        this.dirty = true;
    } else if(this.sync) {
        //标记为同步计算的直接运行run,三大类型暂无,所以基本会走下面的queueWatcher
        this.run();
    } else {
        //将watcher推入观察者队列中,下一个tick时调用。
        //也就是数据变化不是立即就去更新的,而是异步批量去更新的
        queueWatcher(this);
    }
};

當options.lazy設定為true之後(只計算屬性watcher的options.lazy設定為true),每次依賴更新,都不會主動觸發run函數,而是把watcher.dirty設為true。這樣,當計算屬性取值時,就會執行computedGetter函數,computedGetter函數中有一個關於watcher.dirty的判斷,當watcher.dirty為true時會執行watcher.evaluate進行值的更新,並把watcher. dirty設定為false,這樣就完成了惰性求值的過程。後面只要依賴不更新,就不會執行update,就不會把watcher.dirty為true,那麼再次取值的時候就不會執行watcher.evaluate進行值的更新,從而達到了快取的效果。

綜上,我們了解到cache不為false的時候,計算屬性都是惰性求值且具有快取性的,而cache預設是true,我們也大多使用這個預設值,所以我們說 computed本質是一個惰性求值的觀察者,具有快取性,只有當依賴變化後,第一次存取computed 屬性,才會計算新的值

相關推薦:

2020年前端vue面試題大匯總(附答案)

vue教程推薦:2020最新的5個vue.js影片教學精選

更多程式相關知識,請造訪:程式設計教學! !

以上是詳解vue中的資料初始化(initState)的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文轉載於:segmentfault.com。如有侵權,請聯絡admin@php.cn刪除