vue?Proxy數(shù)據(jù)代理進(jìn)行校驗(yàn)部分源碼實(shí)例解析
initProxy
數(shù)據(jù)攔截的思想除了為構(gòu)建響應(yīng)式系統(tǒng)準(zhǔn)備,它也可以為數(shù)據(jù)進(jìn)行篩選過(guò)濾,我們接著往下看初始化的代碼,在合并選項(xiàng)后,vue接下來(lái)會(huì)為vm實(shí)例設(shè)置一層代理,這層代理可以為vue在模板渲染時(shí)進(jìn)行一層數(shù)據(jù)篩選
Vue.prototype._init = function(options) {
// 選項(xiàng)合并
...
{
// 對(duì)vm實(shí)例進(jìn)行一層代理
initProxy(vm);
}
...
}initProxy
// 代理函數(shù)
var initProxy = function initProxy (vm) {
//是否支持Proxy
if (hasProxy) {
var options = vm.$options;
//當(dāng)使用類似webpack這樣的打包工具時(shí),通常會(huì)使用vue-loader插件進(jìn)行模板的編譯,這個(gè)時(shí)候options.render是存在的,并且_withStripped的屬性也會(huì)設(shè)置為true(關(guān)于編譯版本和運(yùn)行時(shí)版本的區(qū)別可以參考后面章節(jié)),所以此時(shí)代理的選項(xiàng)是hasHandler
//在其他場(chǎng)景下,代理的選項(xiàng)是getHandler
var handlers = options.render && options.render._withStripped
? getHandler
: hasHandler;
// 代理vm實(shí)例到vm屬性_renderProxy
vm._renderProxy = new Proxy(vm, handlers);
} else {
vm._renderProxy = vm;
}
};hasProxy
var hasProxy =
typeof Proxy !== 'undefined' && isNative(Proxy);hasHandler
var hasHandler = {
// key in obj或者with作用域時(shí),會(huì)觸發(fā)has的鉤子
has: function has (target, key) {
···
}
};觸發(fā)代理
源碼中vm._renderProxy的使用出現(xiàn)在Vue實(shí)例的_render方法中,Vue.prototype._render是將渲染函數(shù)轉(zhuǎn)換成Virtual DOM的方法,這部分是關(guān)于實(shí)例的掛載和模板引擎的解析
當(dāng)我們調(diào)用render函數(shù)時(shí),代理的vm._renderProxy對(duì)象便會(huì)訪問(wèn)到
而這個(gè)render函數(shù)就是包裝成with的執(zhí)行語(yǔ)句,在執(zhí)行with語(yǔ)句的過(guò)程中,該作用域下變量的訪問(wèn)都會(huì)觸發(fā)has鉤子,這也是模板渲染時(shí)之所有會(huì)觸發(fā)代理攔截的原因
之所以會(huì)觸發(fā)數(shù)據(jù)代理攔截是因?yàn)槟0逯惺褂昧俗兞?,例?lt;div>{{message}}}
Vue.prototype._render = function () {
···
// 調(diào)用vm._renderProxy
vnode = render.call(vm._renderProxy, vm.$createElement);
}
=========================================
var vm = new Vue({
el: '#app'
})
console.log(vm.$options.render)
???????//輸出, 模板渲染使用with語(yǔ)句
? anonymous() {
with(this){return _c('div',{attrs:{"id":"app"}},[_v(_s(message)+_s(_test))])}
}數(shù)據(jù)過(guò)濾
通過(guò)data選項(xiàng)去設(shè)置實(shí)例數(shù)據(jù),那么這些數(shù)據(jù)可以隨著個(gè)人的習(xí)慣任意命名嗎?顯然不是的,如果你使用js的關(guān)鍵字(像Object,Array,NaN)去命名,這是不被允許的。另一方面,Vue源碼內(nèi)部使用了以$,_作為開頭的內(nèi)部變量,所以以$,_開頭的變量名也是不被允許的,這就構(gòu)成了數(shù)據(jù)過(guò)濾監(jiān)測(cè)的前提。
var hasHandler = {
has: function has (target, key) {
var has = key in target;
// isAllowed用來(lái)判斷模板上出現(xiàn)的變量是否合法。
var isAllowed = allowedGlobals(key) ||
(typeof key === 'string' && key.charAt(0) === '_' && !(key in target.$data));
// _和$開頭的變量不允許出現(xiàn)在定義的數(shù)據(jù)中,因?yàn)樗莢ue內(nèi)部保留屬性的開頭。
// 1. warnReservedPrefix: 警告不能以$ _開頭的變量
// 2. warnNonPresent: 警告模板出現(xiàn)的變量在vue實(shí)例中未定義
//has判斷是否是target對(duì)象中的變量
if (!has && !isAllowed) {
if (key in target.$data) { warnReservedPrefix(target, key); }
else { warnNonPresent(target, key); }
}
return has || !isAllowed
}
};
// 模板中允許出現(xiàn)的非vue實(shí)例定義的變量
var 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,' +
'require' // for Webpack/Browserify
);在瀏覽器不支持proxy的情況下的數(shù)據(jù)過(guò)濾
在沒(méi)有經(jīng)過(guò)代理的情況下,使用_開頭的變量依舊會(huì) 報(bào)錯(cuò),但是它變成了js語(yǔ)言層面的錯(cuò)誤。但是這個(gè)報(bào)錯(cuò)無(wú)法在Vue這一層知道錯(cuò)誤的詳細(xì)信息,而這就是能使用Proxy的好處。
在初始化數(shù)據(jù)階段,Vue已經(jīng)為數(shù)據(jù)進(jìn)行了一層篩選的代理。具體看initData對(duì)數(shù)據(jù)的代理
有了isReserved的篩選,即使this._data._test存在,我們依舊無(wú)法在訪問(wèn)this._test時(shí)拿到_test變量
function initData(vm) {
vm._data = typeof data === 'function' ? getData(data, vm) : data || {}
if (!isReserved(key)) {
// 數(shù)據(jù)代理,用戶可直接通過(guò)vm實(shí)例獲取返回data數(shù)據(jù)
proxy(vm, "_data", key);
}
}
function isReserved (str) {
var c = (str + '').charCodeAt(0);
// 首字符是$, _的字符串
return c === 0x24 || c === 0x5F
}
proxy
function proxy (target, sourceKey, key) {
sharedPropertyDefinition.get = function proxyGetter () {
// 當(dāng)訪問(wèn)this[key]時(shí),會(huì)代理訪問(wèn)this._data[key]的值
return this[sourceKey][key]
};
sharedPropertyDefinition.set = function proxySetter (val) {
this[sourceKey][key] = val;
};
Object.defineProperty(target, key, sharedPropertyDefinition);
}


總結(jié)
到此這篇關(guān)于vue Proxy數(shù)據(jù)代理進(jìn)行校驗(yàn)部分源碼解析的文章就介紹到這了,更多相關(guān)vue Proxy數(shù)據(jù)代理校驗(yàn)內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
vue中使用iframe嵌入網(wǎng)頁(yè),頁(yè)面可自適應(yīng)問(wèn)題
這篇文章主要介紹了vue中使用iframe嵌入網(wǎng)頁(yè),頁(yè)面可自適應(yīng)問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-09-09
vue阻止重復(fù)請(qǐng)求實(shí)現(xiàn)示例詳解
這篇文章主要為大家介紹了vue阻止重復(fù)請(qǐng)求實(shí)現(xiàn)示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-01-01
vue實(shí)現(xiàn)滑動(dòng)和滾動(dòng)效果
這篇文章主要為大家詳細(xì)介紹了vue實(shí)現(xiàn)滑動(dòng)和滾動(dòng)效果,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-03-03
vue3中使用vuedraggable實(shí)現(xiàn)拖拽el-tree數(shù)據(jù)分組功能
這篇文章主要介紹了vue3中使用vuedraggable實(shí)現(xiàn)拖拽el-tree數(shù)據(jù)分組功能,可以實(shí)現(xiàn)單個(gè)拖拽、雙擊添加、按住ctrl鍵實(shí)現(xiàn)多個(gè)添加,或者按住shift鍵實(shí)現(xiàn)范圍添加,添加到框中的數(shù)據(jù),還能拖拽排序,需要的朋友可以參考下2024-02-02
Vue標(biāo)簽屬性動(dòng)態(tài)傳參并拼接字符串的操作方法
這篇文章主要介紹了Vue標(biāo)簽屬性動(dòng)態(tài)傳參并拼接字符串的操作方法,我們需要根據(jù)傳入值的類型,在placeholder屬性賦值"請(qǐng)輸入長(zhǎng)度",“請(qǐng)輸入寬度”,"請(qǐng)輸入厚度"等提示字符,本文通過(guò)實(shí)例代碼介紹的非常詳細(xì),需要的朋友參考下吧2023-11-11
vue中實(shí)現(xiàn)div可編輯并插入指定元素與樣式
這篇文章主要給大家介紹了關(guān)于vue中實(shí)現(xiàn)div可編輯并插入指定元素與樣式的相關(guān)資料,文中通過(guò)代碼以及圖文將實(shí)現(xiàn)的方法介紹的非常詳細(xì),對(duì)大家學(xué)習(xí)或者使用vue具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2023-09-09
vue自定義穿梭框支持遠(yuǎn)程滾動(dòng)加載的實(shí)現(xiàn)方法
這篇文章主要介紹了vue自定義穿梭框支持遠(yuǎn)程滾動(dòng)加載,iview是全局注入,基本使用原先的類名進(jìn)行二次創(chuàng)建公共組件,修改基礎(chǔ)js實(shí)現(xiàn)邏輯,本文結(jié)合實(shí)例代碼介紹的非常詳細(xì),需要的朋友可以參考下2023-08-08

