一:用法
vue的props属性用于父组件向子组件传递值,是一个高频使用的特性。
props的使用需要父子组件的配合,在父组件中使用v-bind绑定要传递的值,在子组件中的props属性中声明接受属性的类型和默认值。使用示例:
// 父组件 App.vue
<hello-world :testData="testData"></hello-world>
export default{
name:'App',
data(){
return{
testData:{}
}
}
}
// 子组件
export default {
name:'HelloWorld',
props:{
testData:{
type:Object,
default:()=>{}
}
}
}
二、父组件App.vue中的处理
从上面的使用示例可以看出,父组件的template的子组件标签上使用v-bind指令绑定了一个属性。我们知道template标签的内容会经过AST和Codegen,最终转换成render函数。
2.1:AST转换时的处理
AST转换时会对标签上的属性进行处理,对于v-bind的属性会在AST的节点上添加attrs数组属性
// vue源码 src\compiler\parser\index.js
function processAttrs (el){
const list = el.attrsList
let i, l, name, rawName, value, modifiers, syncGen, isDynamic
for (i = 0, l = list.length; i < l; i++) {
......
if (bindRE.test(name)) { // v-bind
// line 842
addAttr(el, name, value, list[i], isDynamic)
}
......
}
}
2.2:Codegen时的处理
Codegen对每个AST节点进行转换处理,最终生成render函数;处理节点时,针对attrs属性的处理如下:
// vue源码 src\compiler\codegen\index.js
export function genData (el: ASTElement, state: CodegenState): string {
let data = '{'
......
// attributes
if (el.attrs) {
data += `attrs:${genProps(el.attrs)},`
}
......
return data
}
2.3:render函数中的形式
我们看一下App.vue最终生成的render函数
// App.vue的render函数
var render = function() {
var _vm = this
var _h = _vm.$createElement
var _c = _vm._self._c || _h
return _c(
"div",
{ attrs: { id: "app" } },
[
_c("img", { attrs: { src: require("./assets/logo.png") } }),
_vm._v(" "),
_c("HelloWorld", { attrs: { testData: _vm.testData } })
],
1
)
}
我们需要关注的是_c("HelloWorld", { attrs: { testData: _vm.testData } })
可以看到,在template中绑定的属性,最终设置成createElement的第二个参数,在创建HelloWorld组件时发挥作用。
三、子组件HelloWorld.vue中的处理
子组件通过initProps方法将props属性绑定在vm._props上,并将其设置为响应式属性,最后通过proxy方法代理到vm上,达到直接通过this访问的目的。
// vue源码 src\core\instance\state.js
// 删除了部分代码
function initProps (vm: Component, propsOptions: Object) {
const propsData = vm.$options.propsData || {}
const props = vm._props = {}
for (const key in propsOptions) {
const value = validateProp(key, propsOptions, propsData, vm)
// 设置为响应式属性
defineReactive(props, key, value)
if (!(key in vm)) {
// 代理到vm上,可以直接通过this.访问
proxy(vm, `_props`, key)
}
}
}