节点、树以及虚拟 DOM 单向数据流 Prop 验证 关系链

    科技2024-12-03  13

    节点、树以及虚拟 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>
    Processed: 0.009, SQL: 8