Vue 源碼解析 (二)initProxy 初始化代理

Vue 源碼解析 (二)initProxy 初始化代理
在 src/core/instance/proxy.js 找到源碼
makeMap,allowedGlobals
我們先來(lái)看看 makeMap 這個(gè)方法,做了什么處理:
/*makeMap函數(shù), str參數(shù)是接受的字符串, expectsLowerCase參數(shù)是否需要小寫(xiě)*/
function makeMap(str, expectsLowerCase ) {
/* 創(chuàng)建一個(gè)對(duì)象 */
var map = Object.create(null);
/*將字符串分割成數(shù)組*/
var list = str.split(',');
/*對(duì)數(shù)組進(jìn)行遍歷*/
for (var i = 0; i < list.length; i++) {
/*將每個(gè)key對(duì)應(yīng)的值設(shè)置為true*/
map[list[i]] = true;
}
/*最終返回, 根據(jù)參數(shù)設(shè)置是否是需要轉(zhuǎn)換大小寫(xiě)*/
return expectsLowerCase
? function (val) {
return map[val.toLowerCase()];
}
: function (val) {
return map[val];
}
}
然后給一些 js 內(nèi)置的全局方法做了相應(yīng)的處理:
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
);
makeMap 函數(shù)的只要作用把這些全局的API轉(zhuǎn)成以下的形式,
{
Infinity:true,
undefined:true
}
isNative
可以學(xué)習(xí)一下源碼是如何檢測(cè)是不是支持原生方法
export function isNative (Ctor: any): boolean {
return typeof Ctor === 'function' && /native code/.test(Ctor.toString())
}
warnNonPresent
這個(gè)方法的意思是不存在,未定義的屬性,方法被使用給出警告,我們來(lái)看看例子:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<script src="../vue.js"></script>
<div id="app">
<p>
{{msg}}
</p>
</div>
<script>
const vm = new Vue({
el: '#app',
})
console.log(vm)
</script>
</body>
</html>
上面例子直接在魔板中使用 msg 變量,但是他沒(méi)有在 data 中定義,此時(shí) warnNonPresent 會(huì)處理拋出警告如圖所示
warnReservedPrefix
源碼如下:
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://vuejs.org/v2/api/#data',
target
)
}
用于檢測(cè)屬性 key 的聲明方法,是否是 $ 或者 _ 開(kāi)頭的,如果是,會(huì)給出警告,拿個(gè)簡(jiǎn)單的例子來(lái)看下效果:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<script src="../vue.js"></script>
<div id="app">
<p>
{{$hhh}}
{{_dddd}}
</p>
</div>
<script>
const vm = new Vue({
el: '#app',
data() {
return {
$hhh: 'ddd',
_dddd: 'ffff'
}
},
})
console.log(vm)
</script>
</body>
</html>
hasHandler
var hasHandler = {
/*target要代理的對(duì)象, key在外部操作時(shí)訪問(wèn)的屬性*/
has: function has(target, key) {
/*key in target返回true或者false*/
var has = key in target;
/*在模板引擎里面,有一些屬性vm沒(méi)有進(jìn)行代理, 但是也能使用, 像Number,Object等*/
var isAllowed = allowedGlobals(key) ||
(typeof key === 'string' && key.charAt(0) === '_' && !(key in target.$data));
/*在上面的has和isAllowed為false的情況下*/
if (!has && !isAllowed) {
if (key in target.$data) {
warnReservedPrefix(target, key);
}
/*warnNonPresent函數(shù), 當(dāng)訪問(wèn)屬性,沒(méi)有存在vm實(shí)例上, 會(huì)報(bào)錯(cuò)提示*/
else {
warnNonPresent(target, key);
}
}
/*has或者isAllowed*/
return has || !isAllowed
}
};
hasHandler 只配置了 has 鉤子 ,當(dāng)進(jìn)行propKey in proxy in 操作符 或者 with() 操作時(shí), 會(huì)觸發(fā) has鉤子函數(shù)
hasHandler在查找key時(shí),從三個(gè)方向進(jìn)行查找
代理的 target 對(duì)象 通過(guò) in 操作符 全局對(duì)象API allowedGlobals 函數(shù) 查找是否是渲染函數(shù)的內(nèi)置方法 第一個(gè)字符以_開(kāi)始 typeof key === 'string' && key.charAt(0) === '_'
hasHandler, 首先去檢測(cè) vm 實(shí)例上是否有該屬性, 下面的代碼是vm實(shí)例上可以查看到test
new Vue({
el:"#app",
template:"<div>{{test}}</div>",
data:{
test
}
})
如果在 vm 實(shí)例上沒(méi)有找到, 然后再去判斷下是否是一些全局的對(duì)象, 例如 Number 等, Number是語(yǔ)言所提供的 在模板中也可以使用
new Vue({
el:"#app",
/*Number屬于語(yǔ)言提供的全局API*/
template:"<div> {{ Number(test) +1 }}</div>",
data:{
test
}
})
getHandler
const getHandler = {
get(target, key) {
if (typeof key === 'string' && !(key in target)) {
// 檢測(cè) data 是屬性 key 是不是 $,_ 開(kāi)頭
if (key in target.$data) warnReservedPrefix(target, key)
else warnNonPresent(target, key)
}
return target[key]
}
}
initProxy
initProxy = function initProxy(vm) {
/*hasProxy 判斷當(dāng)前環(huán)境是否支持es 提供的 Proxy*/
if (hasProxy) {
// determine which proxy handler to use
var options = vm.$options;
/*不同條件返回不同的handlers, getHandler或者h(yuǎn)asHandler */
var handlers = options.render && options.render._withStripped
? getHandler
: hasHandler;
/* 代理vm實(shí)例 */
vm._renderProxy = new Proxy(vm, handlers);
} else {
vm._renderProxy = vm;
}
};
評(píng)論
圖片
表情
