Vue3的多根节点组件与父子组件之间的事件继承

多根节点组件

        Vue.js 3.x 版本不再对组件的 template 选项进行“唯一根节点”的限制,而是允许 DOM 结构具备多个根节点,即下列结构的全局组件在 Vue.js 3.x 版本中是允许的。
app.component('my-box',{
        template:`
                <div class="first">第一个节点</div>
                <div class="second">第二个节点</div>
                <div class="third">第三个节点</button>
        `
})
本节对这种结构可能出现的问题以及 Vue 框架的语法要求进行讲解。

1、 Prop 属性

        在使用组件时,被子组件通过 props 选项接收的属性被称为“ Prop 属性”,没有被子组件的 props 选项接收的属性被称为“非 Prop 属性”。 Vue 框架规定: Prop 属性可以向子组件数据区那样进行调用,即使用 this 就可以访问到 Prop 属性;非 Prop 属性需要在子组件中使用 this.$attrs 来进行访问。
        【示例 4-15 Prop 属性的访问 。注册一个全局组件名为 my-phone ,其中设置一个 Prop 属性名为brand,再设置一个非 Prop 属性名为 count 。最终在组件的 template 选项中设置两个 div 容器访问这两个属性。
HTML 文档中对 my-phone 组件使用的代码如下所示。
<div id="app">
    <my-phone brand="HuaWei" :count="150"></my-phone>
</div>
Vue 代码如下所示。
01 let app=Vue.createApp({})
02     app.component('my-phone',{
03         template:`
04             <div class="brand">品牌:{{this.brand}}</div>
05             <div class="count">数量:{{this.$attrs.count}}</div>
06         `,
07     props:['brand']
08 })
09 app.mount('#app')
        上述代码中,<my-phone> HTML 代码中设置了两个参数: brand count 。在组件注册的代码中,第07 行将 brand 参数利用 props 选项进行接收,形成了 Prop 属性。而 count 参数没有用 props 选项进行接收,即是一个非 Prop 属性。
        在组件的 template 模板中,第 04 行访问了 Prop 属性 brand ,使用的是 this.brand 格式。第 05 行访问了非 Prop 属性 count ,使用的是 this.$attrs.count 格式。
        这里需要注意,非 Prop 属性也是单向数据流传递,即不能在组件注册代码中改变该属性的值,也不能将该变量通过 v-model 绑定在表单元素中。
        同理,在使用组件时绑定的自定义事件,也可以通过 this$attrs 获取。与非 Prop 属性不同的是,绑定事件的名称,在 this.$attrs 中需要添加 on_ 前缀。
        【示例 4-16 通过 this.$attrs 触发组件的事件 。注册一个全局组件名为 my-alert ,为该组件设置一个名为 message 的属性,在绑定一个名为 custom 的自定义事件。
HTML 文档中对 my-alert 组件使用的代码如下所示。
<div id="app">
    <my-alert message="非 Prop 属性的使用" @custom="custom"></my-alert>
</div>
Vue 代码如下所示。
01 let app=Vue.createApp({
02 methods:{
03 custom(value){
04 window.alert(value)
05 }
06 }
07 })
08 app.component('my-alert',{
09 template:`
10 <button @click="btnClick">单击</button>
11 `,
12 methods:{
13 btnClick(){
14 this.$attrs.onCustom(this.$attrs.message);
15 }
16 }
17 })
18 app.mount('#app')
        在上述代码中,HTML 文档使用 <my-alert> 组件时,设置了一个自定义属性 message ,同时绑定了一个自定义事件 custom 。由于在组件定义的 Vue 代码中,没有使用 props 来接收自定义属性 message ,因此message 是一个非 Prop 属性。对于绑定的自定义事件,可以在组件中使用 this.$emit() 进行调用,也可以使用 this.$attrs 进行访问。
        从第 14 行中可以看出,自定义事件 custom this.$attrs 对象中存储为 onCustom ,这是读者需要注意的地方,触发该事件时,需要使用 this.$attrs.onCustom() 来实现。

2、 组件事件的继承

        Vue.js 3.0 框架规定:父组件的事件可以直接继承给具备该事件的子组件及其后代组件。

 

        【示例 4-17 组件事件的继承 。书写一个用于选择专业的组件 select-specialty 。该组件中具备一个可供选择专业的下拉菜单。当用户选择某个专业的菜单项时,需要用弹窗在页面中显示选中的专业内容。

 

        从本示例的要求来看,应该为组件中的下拉菜单绑定 input change 事件,为了体现出组件事件的可传递性,我们将预计绑定到下拉菜单的 input 事件绑定在该组件的父组件中。测试该功能是否能够实现。
HTML 文档中对 select-specialty 组件使用的代码如下所示。
<div id="app">
    <select-specialty @input="boxInput"></select-specialty>
</div>
Vue 代码如下所示。
01 let app=Vue.createApp({
02 methods:{
03 boxInput(){
04 window.alert(event.target.value);
05 }
06 }
07 })
08 app.component('select-specialty',{
09 template:`
10 <select v-model="specialty">
11 <option>前端工程师</option>
12 <option>PHP 工程师</option>
13 <option>Java 工程师</option>
14 <option>UI 设计师</option>
15 <option>项目架构师</option>
16 </select>
17 `,
18 data(){
19 return {
20 specialty:'UI 设计师'
21 }
22 }
23 })
24 app.mount('#app')
        在上述代码中,input 事件绑定在了 HTML 文档中使用 <select-specialty> 组件的代码中,即该组件的父组件中。由于父组件中的事件子组件成员(即<select> 标记对)也有,因此该事件会传递给子组件成员。

 

        若不希望父组件的事件传递给子组件,可以在子组件的定义代码中使用 inheritAttrs 选项,该选项取值为 false 时,this.$attrs 对象依然可以收集父组件传递过来的属性和绑定的事件,但是父组件绑定的事件不会在继承给子组件成员,即子组件成员的相应事件触发后,也不会执行任何父组件事件的代码。

若要让组件 select-specialty 进制父子组件之间的事件传递,代码如下所示。

01 app.component('select-specialty',{
02     inheritAttrs: false,
03     template:`
04         <select v-model="specialty">
05             <option>前端工程师</option>
06             <option>PHP 工程师</option>
07             <option>Java 工程师</option>
08             <option>UI 设计师</option>
09             <option>项目架构师</option>
10         </select>
11     `,
12     data(){
13         return {
14             specialty:'UI 设计师'
15         }
16     }
17 })
        在上述代码中,第 02 行为组件设置了 inheritAttrs 选项,并取值为 false ,这样父组件绑定的 input 事件在第 04 行的 <select> 子组件成员中就不会起作用了。

3、 多根节点组件的规定

        从 Vue.js 3.x 框架开始,组件的 template 选项可以接收多个根节点结构。在 Vue.js 2.x 框架中,对 template选项有必须是一个根节点的限制。
        若组件具备多个根节点,同时父组件绑定了事件,根据默认情况下父组件的事件会继承给子组件的这一特性,Vue 框架会报出一个警告。警告内容如下所示。
       
        [Vue warn]: Extraneous non-emits event listeners (click) were passed to component but could not be automatically inherited because component renders fragment or text root nodes. If the listener is intended to be a component custom event listener only, declare it using the "emits" option.
        上述警告的含义是:外部的(即父组件)的非触发的事件监听器(单击事件)被传递给子组件,但是不能自动继承,因为组件将渲染为片段或文本根节点。如果侦听器只是组件自定义事件的侦听器,则需要使用“emit ”选项来声明它。
        上述警告的解决方案有以下两个:
         禁止父组件向子组件继承事件:即在子组件中书写 inheritAttrs:false 选项。
         在子组件中需要继承父组件事件的根节点上显式绑定 $attrs ,这被称为“隐式贯穿”行为。

 

        【示例 4-18 多根节点组件的事件继承 。书写一个名为 root-test 的组件,其中包含三个根节点。在父组 件中绑定单击事件,显式指定第二个根节点继承父组件的事件。
        HTML 文档中对 select-specialty 组件使用的代码如下所示。
<div id="app">
    <root-test @click="divClick"></root-test>
</div>
Vue 代码如下所示。
01 let app=Vue.createApp({
02     methods:{
03         divClick(){
04             window.alert(event.target.textContent);
05         }
06     }
07 })
08 app.component('root-test',{
09 // inheritAttrs: false,
10     template:`
11         <div class="first">第一个根节点</div>
12         <div class="second" v-bind="$attrs">第二个根节点</div>
13         <div class="third">第三个根节点</div>
14     `
15 })
16 app.mount('#app')
        Vue 框架的官网指出:与单个根节点组件不同,具有多个根节点的组件不具有自动隐式贯穿行为( Auto fallthrough)。若未显式绑定 $attrs ,则会在运行时发出警告。
        在上述代码中,第 10 行至第 14 行定义了组件中存在的三个根节点,同时父组件上绑定了 click 事件。 要想放置警告的发生,可以将第 09 行注释变为语句,禁止父组件将事件传递给子组件成员,这也是我们讲解的第一种警告解决方案。也可以像第 12 行那样,使用 v-bind 绑定 $attrs ,这样就指定了只有第二个根节点可以继承父组件的事件,同时也不会在运行时发出警告了。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值