Vue實例初始化為渲染函數(shù)設(shè)置檢查源碼剖析
引言
之前的文章提到,Vue實例化時_init
方法做了很多處理,其中就有這么一段:
if (__DEV__) { initProxy(vm) } else { vm._renderProxy = vm }
在生產(chǎn)模式下,_renderProxy
直接指向了Vue實例本身,而在開發(fā)環(huán)境下調(diào)用了initProxy
方法,那么它究竟是做什么的呢?
_renderProxy是干什么的
通過對_renderProxy進(jìn)行全局搜索,我們在src\core\instance\render.ts
文件中找到了它:
// 源碼文件 src\core\instance\render.ts vnode = render.call(vm._renderProxy, vm.$createElement)
也就是說,_renderProxy
是渲染函數(shù)render
的執(zhí)行上下文,在生產(chǎn)環(huán)境下,執(zhí)行上下文就是實例本身,而在開發(fā)環(huán)境下,執(zhí)行上下文則使用initProxy
進(jìn)行了處理,我們接下來看看它究竟做了什么。
initProxy方法
首先判斷了當(dāng)前環(huán)境下Proxy
對象是否存在進(jìn)行了判斷:
//源碼文件 src\core\instance\proxy.ts const hasProxy = typeof Proxy !== 'undefined' && isNative(Proxy) //... initProxy = function initProxy(vm) { if (hasProxy) { // determine which proxy handler to use const options = vm.$options const handlers = options.render && options.render._withStripped ? getHandler : hasHandler vm._renderProxy = new Proxy(vm, handlers) } else { vm._renderProxy = vm } }
如果Proxy
對象不存在,就放棄治療,上下文仍為原Vue實例。
而如果Proxy
對象存在,則進(jìn)一步去$options
里獲取_withStripped
屬性,如果_withStripped
存在,則使用getHandler
方法來代理Vue實例,如果不存在,就使用hasHandler
來代理實例。
關(guān)于Proxy
對象的用法,我在這篇文章里提過,簡單來說,它可以為一個對象設(shè)定代理,用以攔截對象的各種方法。
那么,我們進(jìn)一步看一下,hasHandler
和getHandler
都做了什么,首先是比較簡單的getHandler
:
// 源碼文件:src\core\instance\proxy.ts const getHandler = { get(target, key) { if (typeof key === 'string' && !(key in target)) { if (key in target.$data) warnReservedPrefix(target, key) else warnNonPresent(target, key) } return target[key] } }
這個方法攔截了Vue實例對象的getter
,也就是說,當(dāng)獲取實例的屬性時,就會觸發(fā)這個方法,在這個方法中,對屬性值是否在實例中以及是否在實例的$data
中進(jìn)行了檢查,如果不存在則發(fā)出相應(yīng)的警告:
// 源碼文件:src\core\instance\proxy.ts const warnReservedPrefix = (target, key) => { warn( `Property "${key}" must be accessed with "$data.${key}" because ` + 'properties starting with "$" or "_" are not proxied in the Vue instance to ' + 'prevent conflicts with Vue internals. ' + 'See: https://v2.vuejs.org/v2/api/#data', target ) } const warnNonPresent = (target, key) => { warn( `Property or method "${key}" is not defined on the instance but ` + 'referenced during render. Make sure that this property is reactive, ' + 'either in the data option, or for class-based components, by ' + 'initializing the property. ' + 'See: https://v2.vuejs.org/v2/guide/reactivity.html#Declaring-Reactive-Properties.', target ) }
而hasHandler
則是對實例對象的in
操作符進(jìn)行攔截,也就是攔截以下操作:
- 屬性查詢:
foo in proxy
- 繼承屬性查詢:
foo in Object.create(proxy)
with
檢查: with(proxy) { (foo); }
- Reflect.has()
那么做了什么呢:
// 源碼文件:src\core\instance\proxy.ts const hasHandler = { has(target, key) { const has = key in target const isAllowed = allowedGlobals(key) || (typeof key === 'string' && key.charAt(0) === '_' && !(key in target.$data)) if (!has && !isAllowed) { if (key in target.$data) warnReservedPrefix(target, key) else warnNonPresent(target, key) } return has || !isAllowed } }
類似的,依然是對屬性是否在實例中存在進(jìn)行了檢查,但是多了一步判斷,也就是allowedGlobals
,它實際上是一個全局方法列表,當(dāng)模板中出現(xiàn)了里面的方法名后,不會進(jìn)行下一個步驟的判斷,也就不會因為在Vue實例和$options
中找不到這個名字的屬性而彈出報錯,這些方法你在開發(fā)過程中肯定都用過:
// 源碼文件:src\core\instance\proxy.ts const allowedGlobals = makeMap( 'Infinity,undefined,NaN,isFinite,isNaN,' + 'parseFloat,parseInt,decodeURI,decodeURIComponent,encodeURI,encodeURIComponent,' + 'Math,Number,Date,Array,Object,Boolean,String,RegExp,Map,Set,JSON,Intl,BigInt,' + 'require' // for Webpack/Browserify )
總結(jié)
Vue實例化過程中,在開發(fā)環(huán)境下會調(diào)用initProxy
方法來包裝render
函數(shù)原本的執(zhí)行上下文(也就是Vue實例本身),在它的getter
和in
操作符中加入一部分屬性的檢查,當(dāng)模板中調(diào)用的屬性不存在于實例中或不存在于$options
中時,及時提出警告。
以上就是Vue實例初始化為渲染函數(shù)設(shè)置檢查源碼剖析的詳細(xì)內(nèi)容,更多關(guān)于Vue實例初始化渲染函數(shù)檢查的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
解決vue無法加載文件D:\Program Files\nodejs\node_global\vue.ps1,
這篇文章主要給大家介紹了關(guān)于解決vue無法加載文件D:\Program Files\nodejs\node_global\vue.ps1,因為在此系統(tǒng)上禁止運(yùn)行腳本的相關(guān)資料,這個報錯是由于在系統(tǒng)上禁止運(yùn)行腳本導(dǎo)致的,文中通過圖文介紹的非常詳細(xì),需要的朋友可以參考下2024-01-01jdk1.8+vue elementui實現(xiàn)多級菜單功能
這篇文章主要介紹了jdk1.8+vue elementui實現(xiàn)多級菜單功能,本文通過實例代碼給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2020-09-09Vue3中Provide?/?Inject的實現(xiàn)原理分享
provide和inject主要為高階插件/組件庫提供用例,這篇文章主要給大家介紹了關(guān)于Vue3中Provide?/?Inject的實現(xiàn)原理,文中通過實例代碼介紹的非常詳細(xì),需要的朋友可以參考下2022-02-02關(guān)于vue設(shè)置環(huán)境變量全流程
這篇文章主要介紹了關(guān)于vue設(shè)置環(huán)境變量全流程,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-03-03vue項目中接入websocket時需要ip端口動態(tài)部署問題
這篇文章主要介紹了vue項目中接入websocket時需要ip端口動態(tài)部署問題,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2023-09-09