Vue實例初始化為渲染函數設置檢查源碼剖析
引言
之前的文章提到,Vue實例化時_init
方法做瞭很多處理,其中就有這麼一段:
if (__DEV__) { initProxy(vm) } else { vm._renderProxy = vm }
在生產模式下,_renderProxy
直接指向瞭Vue實例本身,而在開發環境下調用瞭initProxy
方法,那麼它究竟是做什麼的呢?
_renderProxy是幹什麼的
通過對_renderProxy進行全局搜索,我們在src\core\instance\render.ts
文件中找到瞭它:
// 源碼文件 src\core\instance\render.ts vnode = render.call(vm._renderProxy, vm.$createElement)
也就是說,_renderProxy
是渲染函數render
的執行上下文,在生產環境下,執行上下文就是實例本身,而在開發環境下,執行上下文則使用initProxy
進行瞭處理,我們接下來看看它究竟做瞭什麼。
initProxy方法
首先判斷瞭當前環境下Proxy
對象是否存在進行瞭判斷:
//源碼文件 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
對象存在,則進一步去$options
裡獲取_withStripped
屬性,如果_withStripped
存在,則使用getHandler
方法來代理Vue實例,如果不存在,就使用hasHandler
來代理實例。
關於Proxy
對象的用法,我在這篇文章裡提過,簡單來說,它可以為一個對象設定代理,用以攔截對象的各種方法。
那麼,我們進一步看一下,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
,也就是說,當獲取實例的屬性時,就會觸發這個方法,在這個方法中,對屬性值是否在實例中以及是否在實例的$data
中進行瞭檢查,如果不存在則發出相應的警告:
// 源碼文件: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
操作符進行攔截,也就是攔截以下操作:
- 屬性查詢:
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 } }
類似的,依然是對屬性是否在實例中存在進行瞭檢查,但是多瞭一步判斷,也就是allowedGlobals
,它實際上是一個全局方法列表,當模板中出現瞭裡面的方法名後,不會進行下一個步驟的判斷,也就不會因為在Vue實例和$options
中找不到這個名字的屬性而彈出報錯,這些方法你在開發過程中肯定都用過:
// 源碼文件: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 )
總結
Vue實例化過程中,在開發環境下會調用initProxy
方法來包裝render
函數原本的執行上下文(也就是Vue實例本身),在它的getter
和in
操作符中加入一部分屬性的檢查,當模板中調用的屬性不存在於實例中或不存在於$options
中時,及時提出警告。
以上就是Vue實例初始化為渲染函數設置檢查源碼剖析的詳細內容,更多關於Vue實例初始化渲染函數檢查的資料請關註WalkonNet其它相關文章!
推薦閱讀:
- vue Proxy數據代理進行校驗部分源碼實例解析
- Vue中避免濫用this去讀取data中數據
- vue3源碼剖析之簡單實現方法
- Vue Computed底層原理深入探究
- Vue2 this 能夠直接獲取到 data 和 methods 的原理分析