- 先研究v-bind的理解
- 其实就只想知道。:xx="1" 做了什么?
- 以及对于。:xx="res" 的区别在哪里?
- 以及源码中绑定后是什么样子,及为什么只能是表达式,及为什么有白名单说法?
// 采用compiler版本的vue
// 直接采用dist/vue.js版本,然后赋值到一个新项目,script导入即可
// 代码大概如下
<!DOCTYPE html>
<head>
<script src="./vue.js"></script>
</head>
<body>
<div id="app"></div>
</body>
<script>
const Vue =window.Vue
new Vue({
data(){
return {
x:1
}
},
template:`<div :title="x">hello word</div>`
}).$mount('#app')
</script>
</html>
- 我们知道模板经历了以下过程
- 字符串 -> ast树
- 其中解析出tag,命令等, 属性 也是在这阶段完成的
- 然后把ast转化为render
// ast
attrs: Array(1)
0: {name: "title", value: "x", dynamic: false, start: 5, end: 15}
length: 1
__proto__: Array(0)
attrsList: Array(1)
0: {name: ":title", value: "x", start: 5, end: 15}
length: 1
__proto__: Array(0)
attrsMap:
:title: "x"
__proto__: Object
children: [{…}]
end: 32
hasBindings: true
parent: undefined
plain: false
rawAttrsMap: {:title: {…}}
start: 0
static: false
staticRoot: false
tag: "div"
type: 1
// ast -> render
var code = generate(ast, options);
return {
ast: ast,
render: code.render,
staticRenderFns: code.staticRenderFns
}
- 重点看下面,
with
render: "with(this){return _c('div',{attrs:{"title":x}},[_v("hello word")])}"
// 然后
res.render = createFunction(compiled.render, fnGenErrors);
// createFunction 方法为
function createFunction (code, errors) {
try {
return new Function(code)
} catch (err) {
errors.push({ err: err, code: code });
return noop
}
}
- 其实关键就是with(this)
- 由此可知,如果x是变量'x',则访问this.x
- 如果x是常量1,则直接获取1
- 如果x是布尔true,则直接获取true
- 拓展:对属性,事件及{{}}中绑定值及执行的理解
template:`<div :title="x" @click="x = 2">{{ document.location.href }}</div>`
=> code:
render: "with(this){return _c('div',{attrs:{"title":x},on:{"click":function($event){x = 2}}},[_v(_s(document.location.href))])}"
// 总结:
// 1、所有属性在with(this) 作用域中获取,所以能取到this对应的值
// 2、对于事件,如果不是字符串,则外部会包裹一个函数function($event){}
// 3、对于Mustache 语法,首先_s():toString(),然后_v():createTextVNode()
// 从这边看,属性和内容,要写表达式很好理解,因为一个最终是属性,一个最终是实参
// 那事件的呢,好像是可以表达式的
// 其实,vue对于所有绑定值的地方,会校验是不是表达式
function checkExpression (exp, text, warn, range) {
try {
new Function(("return " + exp));
} catch (e) {// 略}
}
// 目的:为了统一和模板的干净
// 那白名单又是怎么回事呢
// 因为this是代理了。执行render的时候,with(this) 会触发this的
var hasHandler = {
has: function has (target, key) {
var has = key in target;
var 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
// 目前有:https://github.com/vuejs/vue/blob/v2.6.10/src/core/instance/proxy.js#L9
// 可以发现,白名单出现的就是一些原生js对象,不包括浏览器对象哦(保证代码可以在其它环境执行,所以环境变量写再模板里面不雅)
总结:
和真实值是怎么映射的,with(this)
属性,事件,内容。执行时是什么样子?
- :title="x" => ,{attrs:{"title":x} 即:在this中读取x
- on:{"click":function(event){x = 2}} 即外面会包裹一个带event的函数
- _v(_s(x))]) 即首先toString(),然后createTextVNode()
- vue绑定值其实会经过是否是表达式检验,new Function(("return " + exp));所以,只有支持return 后面的语句才能通过
- 白名单其实就是with触发proxy的has,进而约束一些对象是访问不到的。
其它
- computed是依赖收集,watch是依赖添加