节点、树以及虚拟 DOM
<div>
<h1>My title</h1>
Some text content
<!-- TODO: Add tagline -->
</div>
当浏览器读到这些代码时,它会建立一个“DOM 节点”树来保持追踪所有内容,如同你会画一张家谱树来追踪家庭成员的发展一样。
上述 HTML 对应的 DOM 节点树如下图所示:
每个元素都是一个节点。每段文字也是一个节点。甚至注释也都是节点。一个节点就是页面的一个部分。就像家谱树一样,每个节点都可以有孩子节点 (也就是说每个部分可以包含其它的一些部分)。
高效地更新所有这些节点会是比较困难的,不过所幸你不必手动完成这个工作。你只需要告诉 Vue 你希望页面上的 HTML 是什么,这可以是在一个模板里:
<h1>{{ blogTitle }}</h1>
或者一个渲染函数里:
render: function (createElement) {
return createElement('h1', this.blogTitle)
}
在这两种情况下,Vue 都会自动保持页面的更新,即便 blogTitle 发生了改变。
虚拟 DOM
Vue 通过建立一个虚拟 DOM 来追踪自己要如何改变真实 DOM。请仔细看这行代码:
return createElement('h1', this.blogTitle)
createElement 到底会返回什么呢?其实不是一个实际的 DOM 元素。它更准确的名字可能是 createNodeDescription,因为它所包含的信息会告诉 Vue 页面上需要渲染什么样的节点,包括及其子节点的描述信息。我们把这样的节点描述为“虚拟节点 (virtual node)”,也常简写它为“VNode”。“虚拟 DOM”是我们对由 Vue 组件树建立起来的整个 VNode 树的称呼。
详细见官网:https://cn.vuejs.org/v2/guide/render-function.html#%E8%8A%82%E7%82%B9%E3%80%81%E6%A0%91%E4%BB%A5%E5%8F%8A%E8%99%9A%E6%8B%9F-DOM
单向数据流
//Vue/React单向数据流的体现:数据只能从父级组件流向子级组件
//实现父子间的数据共享,依靠的就是应用类型的地址传递
所有的 prop 都使得其父子 prop 之间形成了一个单向下行绑定:父级 prop 的更新会向下流动到子组件中,但是反过来则不行。
额外的,每次父级组件发生变更时,子组件中所有的 prop 都将会刷新为最新的值。这意味着你不应该在一个子组件内部改变 prop。如果你这样做了,Vue 会在浏览器的控制台中发出警告。
这里有两种常见的试图变更一个 prop 的情形:
这个 prop 用来传递一个初始值;这个子组件接下来希望将其作为一个本地的 prop 数据来使用。在这种情况下,最好定义一个本地的 data property 并将这个 prop 用作其初始值:
props: ['initialCounter'],
data: function () {
return {
counter: this.initialCounter
}
}
这个 prop 以一种原始的值传入且需要进行转换。在这种情况下,最好使用这个 prop 的值来定义一个计算属性:
props: ['size'],
computed: {
normalizedSize: function () {
return this.size.trim().toLowerCase()
}
}
注意:在 JavaScript 中对象和数组是通过引用传入的,所以对于一个数组或对象类型的 prop 来说,在子组件中改变变更这个对象或数组本身将会影响到父组件的状态。
详细参考:https://cn.vuejs.org/v2/guide/components-props.html#%E5%8D%95%E5%90%91%E6%95%B0%E6%8D%AE%E6%B5%81
Prop 验证
// props的值可以是对象,在里面可以对父组件传过来的数据做出验证,如果验证失败,会抛出警告
//如果子组件没有通过props来接收父组件传递的某个数据,该属性就会出现在子组件模板的最外层节点上面
<body>
<div id="app">
<father :msg="msg"></father>
</div>
<template id="father">
<div>
<p>这是father组件... {{msg}} </p>
</div>
</template>
</body>
<script src="./base/vue.js"></script>
<script>
new Vue({
el:"#app",
data:{
msg:10
},
components:{
father:{
template:"#father",
// props:["msg"]
// props:{
// msg:[Number,String] //验证传的值是什么类型
// }
props:{
msg:{
type:Number,
// required: true, //必传
default:1 //默认值
}
}
}
}
})
</script>
注意那些 prop 会在一个组件实例创建之前进行验证,所以实例的 property (如 data、computed 等) 在 default 或 validator 函数中是不可用的。
详细参考:https://cn.vuejs.org/v2/guide/components-props.html#%E5%8D%95%E5%90%91%E6%95%B0%E6%8D%AE%E6%B5%81
关系链
<body>
<div id="app">
<aaa></aaa>
</div>
<template id="aaa">
<div>
<p>这是A组件...</p>
<input type="text" v-model="msg">
<hr>
<bbb :msg="msg"></bbb>
</div>
</template>
<template id="bbb">
<div>
<input type="text" v-model="ownMsg">
</div>
</template>
</body>
<script src="./base/vue.js"></script>
<script>
// 组件、实例对象上有这样的属性:$parent,$children,$root,
//这样的话,就形成了viewmodel链(关系链),理论上来说,
//任何两个组件之间的数据都可以互相调用,获取
//缺点:太乱了 this.$root.$children[3].$children[4]....
Vue.component("aaa",{
template:"#aaa",
data(){
return {
msg:"hello"
}
}
})
Vue.component("bbb",{
template:"#bbb",
props:["msg"],
computed:{
// ownMsg(){
// return this.msg
// }
ownMsg:{
get(){
return this.msg;
},
set(val){
//需要aaa组件自己更改自身数据
this.$parent.msg = val
}
}
}
})
var vm = new Vue({
el:"#app"
})
</script>