# vue组件应用和数据通信

<!--父组件-->
<template>
    <div id="index">
         <personinfo 
            :isdisplay="isdisplay"
            @eswitch="doswitch" 
         >
          </personinfo>
    </div> 
</template>

<script>
import Personinfo from "./Personinfo.vue"
export default {
  data() {
	return {
       isdisplay:false,
	}
  },
  components:{
      Personinfo,
  },
  methods:{
      doswitch(value){
        this.isdisplay=value;
        
      },  
  },
}
</script>
<!--子组件-->
 <template>
    <div v-show="isdisplay">
    </div>
</template>
<script>
export default {
    props:['isdisplay'],
    methods:{
       closemes(){
           this.$emit('eswitch',false)
       },
    }
}
</script>

# vue中组件通信的方式

  • 父传子 props;子组件通知父组件$emit
  • vuex
  • 父组件通过获取子节点的引用this.refs.xx.childmethods,子组件可以this.$parent.fathermethods,注意时间,父组件如果调用子组件的方法,不要不要选择在created的时候,而是应该放在mouted时候,而子组件调用父组件的方法可以写在created时候,类似koa的洋葱模型。
  • let bus = new Vue()
  • 父组件调用children,但是不保证加载顺序,有问题,可以适用一个子组件,同时vue3移除该操作
  • provide/inject 隔代传参
  • 兄弟组件通信:依赖this.$parent.$on 和 this.$parent.$emit
  • $attrs 和 $listeners:当传给子组件的内容没有被props接受,可以使用$attrs进行处理,如果还要传给孙子组件的话,如果值比较多,可以使用v-bind='$attrs'进行类似解构传给孙子组件
  • slot插槽
  • vue1版本有$dispatch / $broadcast;虽然已经被移除,但是仍然可以自定义编写这个功能,很多第三方UI库如 Mint UI、Element UI 和 iView 等,可以解决父子组件、嵌套父子组件的通信。核心是向上寻找

# 父子组件

  • 属性props
  • 引用refs(注意如果要使用子元素中的data,要切记使用时间)
  • 子组件chidren
// child
props: { msg: String }
// parent
<HelloWorld msg="Welcome to Your Vue.js App"/>
// parent
<HelloWorld ref="hw"/>
this.$refs.hw.xx = 'xxx'
// parent
created(){
	console.log(this.$refs.t)//undefined 还没绑定el
	console.log(this.$children[0])//undefined 子组件还未加载到父组件中
	// this.$refs.t.we=100000
},
mounted(){
	
	console.log(this.$children[0].we=30//可以修改,但是children如果多个加载顺序是不固定的
	// this.$refs.t.we=100000
},
 //子组件的data数据在父组件的created的时候获取不到,在mounted时候才可以获取,也类似koa2中的洋葱圈模型
 created(){
    this.init()
    console.log(this.$refs.newTenant,0) // undefined 0
    console.log(this.$refs.newTenant.visible,1)//Error in created hook: "TypeError: Cannot read property 'visible' of undefined"
  },
  mounted(){
    console.log(this.$refs.newTenant.visible,2)//可以成功获取
  },

父组件可以调用子组件方法,但是同时传递数据给子组件时,会获取不及时,使用setTimeout处理调用的子组件方法可以解决

在Vue3中已经移除了$children的属性,所以不可以使用了。

  • 子组件首先要引入父组件,并且要注册,然后展示
1. 引入子组件
2. 注册子组件 
3. 在template中使用子组件
4. 父组件给子组件传值 
5. 子组件接受并应用 
6. 通过事件等方式传递信息给父组件
7. 父组件接受并且处理

须知

父组件甚至还可以把自己的方法传给子组件。

 <Recivephone 
	v-if="isclosephone"
	:iscloseson='closephone'
 > 
 </Recivephone>
 ......
 closephone(){
	  this.isclosephone=false;
 },
 ---------------------------
  props:['iscloseson'],
  ......
  close(){
      this.iscloseson()
  }

# ref $parent和$root

在组件中想要直接获取到元素对象或者子组件实例,这个时候,我们可以给元素或者组件绑定一个ref的attribute属性;

组件实例有一个$refs属性:它一个对象Object,持有注册过 ref attribute 的所有 DOM 元素和组件实例

<template>
  <div>
    <!-- 绑定到一个元素上 -->
    <h2 ref="title">哈哈哈</h2>

    <!-- 绑定到一个组件实例上 -->
    <nav-bar ref="navBar"></nav-bar>
    <button @click="btnClick">获取元素</button>
  </div>
</template>
<script>
  import NavBar from './NavBar.vue';

  export default {
    components: {
      NavBar
    },
    methods: {
      btnClick() {
        console.log(this.$refs.title);
        console.log(this.$refs.navBar.message);
        this.$refs.navBar.sayHello();
        // $el
        console.log(this.$refs.navBar.$el);
      }
    }
  }
</script>
  • 在子组件中可以通过$parent来访问父元素/$root根元素。【耦合性问题】
console.log(this.$parent);
console.log(this.$root);

# emits vue3验证抛出的事件

与 prop 类型验证类似,如果使用对象语法而不是数组语法定义发出的事件,则可以对它进行验证。

要添加验证,请为事件分配一个函数,该函数接收传递给 $emit 调用的参数,并返回一个布尔值以指示事件是否有效。emit事件依然会触发,只不过控制台会有warning

app.component('custom-form', {
  emits: {
    // 没有验证
    click: null,

    // 验证 submit 事件
    submit: ({ email, password }) => {
      if (email && password) {
        return true
      } else {
        console.warn('Invalid submit event payload!')
        return false
      }
    }
  },
  methods: {
    submitForm(email, password) {
      this.$emit('submit', { email, password })
    }
  }
})

如果不需要验证的,也可以写成emits:['a','b']

# props

props除了最简单的数组写法,也可以换成对象写法,对象写法还可以升级成多种校验和默认值是否为true等!

由于vue-loader处理,在子组件上写:aaBc这种依然不会报错,不过不推荐

  • 如果是对象或数组,需要返回这个默认值而不是直接赋值(直接赋值的话多个子组件会出现对象的引用赋值问题,造成数据的错误)
 props: {
    userobj: {
      type: Object,
      default: function() {
        return {
        // 费用类型id
          itemId: '',
          // 小区id
          orgId: '',
          // 当前用户总表id
          id: ''
        }
      }
    }
  },
  • validator条件返回false时,会在控制台抛出warning
props: {
  content: {
	type: Number,
	validator: function(value) {
	  return value < 1000;
	},
	default: function() {
	  return 456;
	}
  }
},
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>lesson 16</title>
 <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
  <div id="root"></div>
</body>
<script>
 var a= Vue.component('counter', {
    props: ['count'],
    template: `<div @click="count += 1">{{count}}</div>`
  });
  const app = new Vue({
	el:"#root",
    data() {
      return { num: 1 }
    },
	components:{
		'counter':a
	},
    template: `
      <div>
        <counter :count="num" />
        <counter :count="num" />
        <counter :count="num" />
      </div>
    `
  });
</script>
</html>

相对于vue2的props单向数据流设计,vue3则是更加严格,虽然vue2会在控制台报错,但是如果真的修改了props也可以在页面上发生变化,而vue3则会禁止更改,同时在控制台抛出错误.代码如下。

[Vue warn]: Attempting to mutate prop "count". Props are readonly.

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>lesson 16</title>
  <script src="https://unpkg.com/vue@next"></script>
</head>
<body>
  <div id="root"></div>
</body>
<script>
  // v-bind="params"
  // :content="params.content" :a="params.a" :b="params.b" :c="params.c"
  // 属性传的时候,使用 content-abc 这种命名,接的时候,使用 contentAbc 命名
  const app = Vue.createApp({
    data() {
      return { num: 1 }
    },
    template: `
      <div>
        <counter :count="num" />
        <counter :count="num" />
        <counter :count="num" />
      </div>
    `
  });

  app.component('counter', {
    props: ['count'],
	mounted(){
		setInterval(()=>{
			this.count=this.count+4
		},500)
	},
    template: `<div @click="count += 1">{{count}}</div>`
  });

  const vm = app.mount('#root');
</script>
</html>

警告

注意在 JavaScript 中对象和数组是通过 引用传入 的,所以对于一个数组或对象类型的 prop 来说,在子组件中改变这个对象或数组本身将会影响到父组件的状态,且 Vue 无法为此向你发出警告。作为一个通用规则,应该 避免修改任何 prop,包括对象和数组 ,因为这种做法无视了单向数据绑定,且可能会导致意料之外的结果。

<template>
  <div class="home">
	 {{myarr}}
	 {{t}}
	  --------------
	  
	<T1 :arr='myarr' :t='t'/>
	
  </div>
</template>

<script>
import T1 from './com/Children1.vue'

export default {
  name: 'Home',
  components: {
	T1
  },
  data(){
	  return {
		  myarr:[1],
		  t:1
	  }
  }
}
</script>
 
<template>
	<div>
		{{arr}}
		-{{t}}
		<el-button @click='add'>add</el-button>
	</div>
	
</template>
<script>
	export default {
		props:{
			arr:Array,
			t:Number
		},
		methods:{
			add(){
				this.arr.push(11)
				this.t++
			}
		}
	}
</script>

# 传入一个对象的所有 property

如果想要将一个对象的所有 property 都作为 prop 传入,可以使用不带参数的 v-bind='xxx' (用 v-bind 代替 :prop-name)。例如,对于一个给定的对象 post:

post: {
  id: 1,
  title: 'My Journey with Vue'
}

下面的模板:

<blog-post v-bind="post"></blog-post>

等价于:

<blog-post v-bind:id="post.id" v-bind:title="post.title"></blog-post>
const app = Vue.createApp({
    data() {
      return { 
		  params1:{
			  a:1,
			  b:2,
			  c:3,
			  count:4
		  }
		 
		}
    },
    template: `
      <div>
        <counter v-bind="params1" />
      </div>
    `
  });

  app.component('counter', {
    props: ['count','a'],
    template: `<div @click="count += 1">{{count}}-{{a}}</div>`
  });

# Non-prop 属性

Attribute 继承 当组件返回单个根节点时,非 prop 的 attribute 将自动添加到根节点的 attribute 中。例如,在 date-picker 组件的实例中:

app.component('date-picker', {
  template: `
    <div class="date-picker">
      <input type="datetime-local" />
    </div>
  `
})

如果我们需要通过 data-status attribute 定义 <date-picker> 组件的状态,它将应用于根节点 (即 div.date-picker)。

<!-- 具有非 prop 的 attribute 的 date-picker 组件-->
<date-picker data-status="activated"></date-picker>

<!-- 渲染后的 date-picker 组件 -->
<div class="date-picker" data-status="activated">
  <input type="datetime-local" />
</div>

同样的规则也适用于事件监听器:

<date-picker @change="submitChange"></date-picker>
app.component('date-picker', {
  created() {
    console.log(this.$attrs) // { onChange: () => {}  }
  }
})

当一个具有 change 事件的 HTML 元素作为 date-picker 的根元素时,这可能会有帮助。

app.component('date-picker', {
  template: `
    <select>
      <option value="1">Yesterday</option>
      <option value="2">Today</option>
      <option value="3">Tomorrow</option>
    </select>
  `
})

在这种情况下,change 事件监听器将从父组件传递到子组件,它将在原生 <select> 的 change 事件上触发。我们不需要显式地从 date-picker 发出事件:

<div id="date-picker" class="demo">
  <date-picker @change="showChange"></date-picker>
</div>
const app = Vue.createApp({
  methods: {
    showChange(event) {
      console.log(event.target.value) // 将打印所选选项的值
    }
  }
})

禁用 Attribute 继承 如果你不希望组件的根元素继承 attribute,可以在组件的选项中设置 inheritAttrs: false。

禁用 attribute 继承的常见场景是需要将 attribute 应用于根节点之外的其他元素。

通过将 inheritAttrs 选项设置为 false,你可以使用组件的 $attrs property 将 attribute 应用到其它元素上,该 property 包括组件 props 和 emits property 中未包含的所有属性 (例如,class、style、v-on 监听器等)。

使用上一节中的 date-picker 组件示例,如果需要将所有非 prop 的 attribute 应用于 input 元素而不是根 div 元素,可以使用 v-bind 缩写来完成。

app.component('date-picker', {
  inheritAttrs: false,
  template: `
    <div class="date-picker">
      <input type="datetime-local" v-bind="$attrs" />
    </div>
  `
})

有了这个新配置,data-status attribute 将应用于 input 元素!

<!-- date-picker 组件使用非 prop 的 attribute -->
<date-picker data-status="activated"></date-picker>

<!-- 渲染后的 date-picker 组件 -->
<div class="date-picker">
  <input type="datetime-local" data-status="activated" />
</div>

当父组件没给子组件传值,而是在父组件把写在子组件上的信息当做属性attr传递到子组件。子组件可以使用v-bind="$attrs"全部接受,也可以自定义名:xx='$attrs.yy'接收。

子组件可别出现props和绑定在父组件里子组件上名字一样的内容,如props:['msg'],那么msg则就不属于Non-prop属性了

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>lesson 17</title>
  <script src="https://unpkg.com/vue@next"></script>
</head>
<body>
  <div id="root"></div>
</body>
<script>
  // Non-prop 属性
  const app = Vue.createApp({
    template: `
      <div>
        <counter msg="hello" msg1="hello1" />
      </div>
    `
  });

  app.component('counter', {
    // inheritAttrs: false,
    mounted() {
      console.log(this.$attrs.msg);
    },
    template: `
      <div :ttt="$attrs.msg">Counter</div>
      <div v-bind="$attrs">Counter</div>
      <div :msg1="$attrs.msg1">Counter</div>
    `
  });

  const vm = app.mount('#root');
</script>
</html>
<div>
	<div ttt="hello">Counter</div>
	<div msg="hello" msg1="hello1">Counter</div>
	<div msg1="hello1">Counter</div>
</div>
<T1 :arr='myarr' :t='t' class="home" />
.home{
	color:red
}

子元素

inheritAttrs: false//设置了不继承,就不会显示红色,不设置,默认会继承这个.home的信息

不管设不设置inheritAttrs,$attrs都能拿到对应的non-props属性

# Vue click.native

  • 如果在组件上直接使用click,需要加和是哪个@click.native来区分
  • 如果子组件设置inheritAttrs: false,点击子组件事件不生效
  • 但是子组件通过this.$attrs.onClick()依然可以触发这个事件
<template>
  <div >
	 {{myarr}}
	 {{t}}
	  --------------
	  
	<T1 :arr='myarr' :t='t' class="home" @click.native='xx()'/>
	
  </div>
</template>

<script>
import T1 from './com/Children1.vue'

export default {
  name: 'Home',
  components: {
	T1
  },
  data(){
	  return {
		  myarr:[1],
		  t:1
	  }
  },
  methods:{
	  xx(){
		  console.log('skt')
	  }
  }
}
</script>
<style type="text/css">
	.home{
		color:red
	}
</style>
 
<template>
	<div>
		{{arr}} - {{t}}
		<el-button @click='add'>add</el-button>
	</div>
	
</template>
<script>
	import { ElMessage } from 'element-plus'
	export default {
		// inheritAttrs: false, 
		props:{
			arr:Array,
			t:{
				validator(value){
					console.log(value)
					return value > 10
				},
				default:3
			}
		},
		methods:{
			add(){
				this.arr.push(11)
				console.log(this.$attrs)
				this.$attrs.onClick()
			}
		}
	}
</script>
<style>
	
</style>

# 兄弟组件交互

通过共同的祖辈组件搭桥,$parent或$root。

// brother1
this.$parent.$on('foo', handle)
// brother2
this.$parent.$emit('foo')
//组件1
<template>
	<div @click="$parent.$emit('tell')">
	</div>
</template>
//子组件2
<template>
	<div id="ti1">
		{{n}}
	</div>
</template>

<script>
	export default{
		data(){
			return {
				n:0
			}
		},
		mounted(){
			this.$parent.$on("tell",()=>{
				this.n++
			})
		}
	}
</script>

# provide和inject

这种方式可以避免在使用props传值时,必须将每一个属性都传递给子组件的写法,当使用的公共组件不确定会被传递什么值的时候,使用这种写法非常方便。

  • provide:是一个对象,或者是一个返回对象的函数。里面就包含要给后代的属性和属性值。

  • inject:一个字符串数组,或者是一个对象。属性值可以是一个对象,包含from和default默认值。

//父组件示例
<template>
  <div>
    <childOne></childOne>
  </div>
</template>
<script>
  import childOne from '../components/test/ChildOne'
  export default {
    name: "Parent",
    provide: {      //重要一步,在父组件中注入一个变量
      for: "demo"
    },
    components:{
      childOne
    }
  }
 </script>
//子组件示例,这个不管嵌套了多少层,都能请求到父组件中注册的变量
<template>
  <div>
      {{for}}
  </div>
</template>
<script>
  export default {
    name: "childOne",
    inject: ['for'],            //子孙组件中使用inject接住变量即可
  }
</script>
  • 如果provide中的值需要取data里的值,则需要改写成函数显示传递,且provide传给后代组件的值,如果是简单的值,后代只接受最初的值,变动了监听不到变化!如果是对象,则可以监听变化而随之改变!!!
 data() {
      return { count: 1}
    },
	// provide:{
	// 	count:this.count,
	// }
    provide() {
      return {
        count: this.count,
      }
    },
inject:['a']
---------------
inject:{
	'ss':'a'//别名ss,解决重名问题
}
------------
inject:{
	'ss':{
		from:'a',
		default:'xxx'//设置默认值
	}
}

# vue3 provide 和inject

利用ref和reactive,可以实现响应式的驱动上层组件传递过来的数据。

<template>
  <div >
	
	<div id="app">
		333
	  <T1></T1>
	  <el-button @click='add'>add</el-button>
	  {{todos}}
	</div>
  </div>
</template>

<script>
import T1 from './com/Children1.vue'
import { provide, reactive, ref } from 'vue'
export default {
  name: 'Home',
  components: {
	T1
  },
 setup() {
    const location = ref('North Pole')
	const geolocation = reactive({
	  longitude: 90,
	  latitude: 135
	})

	provide('location', location)
	provide('geolocation', geolocation)
	 function add(){
		location.value =10000
	 }
	 return {add}
	 
   }
}
</script>

<template>
	<div>
		{{userLocation}}-{{userGeolocation}}
	</div>
	
</template>
<script>
	import { inject } from 'vue'
	export default {

		setup() {
		    const userLocation = inject('location', 'The Universe')
		    const userGeolocation = inject('geolocation')
		
		    return {
		      userLocation,
		      userGeolocation
		    }
		  } 
		  
	}
</script>
<style>
	
</style>
  • 如果要使用到data中数据,需要改成函数模式,而且,为了保证length也是可以响应的,借助import {computed} from 'vue'将length包装成ref对象

# @hook 父组件监听子组件的生命周期

比如有父组件 Parent 和子组件 Child,如果父组件监听到子组件挂载 mounted 就做一些逻辑处理,可以通过以下写法实现:

// Parent.vue
<Child @mounted="doSomething"/>
    
// Child.vue
mounted() {
  this.$emit("mounted");
}

以上需要手动通过 $emit 触发父组件的事件,更简单的方式可以在父组件引用子组件时通过 @hook 来监听即可,如下所示:

//  Parent.vue
<Child @hook:mounted="doSomething" ></Child>

doSomething() {
   console.log('父组件监听到 mounted 钩子函数 ...');
},
   
//  Child.vue
mounted(){
   console.log('子组件触发 mounted 钩子函数 ...');
},    
    
// 以上输出顺序为:
// 子组件触发 mounted 钩子函数 ...
// 父组件监听到 mounted 钩子函数 ...     

当然 @hook 方法不仅仅是可以监听 mounted,其它的生命周期事件,例如:created,updated 等都可以监听。

# @hook应用场景

不干扰子组件的逻辑,增加的逻辑也和组件本身的功能好不关联。最好的办法就是使用 v-on="hook:xxx" 的方式:

<v-chart
@hook:mounted="loading = false"
@hook:beforeUpdated="loading = true"
@hook:updated="loading = false"
:data="data"
/>

# vue3 之provide和inject

  • 可以传递方法给后代去调用执行,同时如果不想后代自己通过获取到前辈的值后乱修改,可以加上readonly属性
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>lesson 41</title>
  <script src="https://unpkg.com/vue@next"></script>
</head>
<body>
  <div id="root"></div>
</body>
<script>
  // provide, inject
  // dom ref
  const app = Vue.createApp({
    setup() {
      const { provide, ref, readonly } = Vue;
      const name = ref('dell');
      provide('name', readonly(name));
      provide('changeName', (value) => {
        name.value = value;
      });
      return { }
    },
    template: `
      <div>
        <child />
      </div>
    `,
  });

  app.component('child', {
    setup() {
      const { inject } = Vue;
      const name = inject('name');
      const changeName = inject('changeName');
      const handleClick = () => {
        changeName('lee');
      }
      return { name, handleClick }
    },
    template: '<div @click="handleClick">{{name}}</div>'
  })

  const vm = app.mount('#root');
</script>
</html>

# $attr and $listeners

listeners

# $props

子组件可以将所有props传递给自己的子组件,也可以传递props中的部分值

<User v-bind="$props"/>
<template>
	<div>
		<p>子代组件</p>
		{{k1}} {{mes.g}}
		<Childson :p="$props"></Childson>
	</div>
</template>
<script>
	import Childson from "@/components/Childson"
	export default{
		props:{
			k1:Number,
			mes:{
				type:Object,
				default:{t:10000}
			}
		}	
	}
</script>
<template>
	<div>孙子组件:{{this.p.k1}}</div>
</template>
<script>
	export default{
		props:{
			p:Object
		},
		mounted() {
			console.log(this.p)
		}
	}
</script>

TIP

组件的本质 vue中的组件经历如下过程 组件配置 => VueComponent实例 => render() => Virtual DOM=> DOM 所以组件的本质是产生虚拟DOM

# vue3组件通信的修改

  • provide:

    • 在vue3中简单值也会响应式的变化(依赖ref/reactive或者利用computed[options API写法需要引入vue里的conputed包裹])
    • 而vue2中,如果传递的是简单值,只会取最初值,后面进行修改,是不会变化的,如果传递的是对象,才会变动。而且传递inject接受的值
    • 在vue2中,如果是简单类型,自己修改,不会改变原始数据,当时对象数据的话,则是可以影响到上级元素的值的变化。vue3简单和对象是都会变化,当然如果为了保护原始数据不被后代随意修改,可以设置为readonly传递。当然祖辈还是可以去修改这个值的,后代也可以同步变化。[ 2022-08-18认证 ]
  • emit通知组件变更,需要emits: ['abcd'],vue2则可以直接使用this.$emit()

  • 不再可以利用 new Vue()创建事件总线,如有需要,引入插件mitt或者 tiny-emitter

  • vue2的父子组件通信可以借助this.$parent.$emit/this.$parent.$on,但是$on,$off在vue3中已经被移除

  • vue3使用ref调用子元素方法也被修改

<template>
    <old1 ref='old1v'/>
</template>
<script setup lang="ts">

import old1 from './old1.vue'
import {ref} from 'vue'
let old1v = ref()
//old1v.value.init() //注意setup执行时间,子元素还没加载,不能调用这个方法
let say1 =() =>{
  old1v.value.init()
}
</script>
  • listeners 现在作为 $attrs 的一部分传递,可以将其删除;而vue2中 v-on="$listeners": 将父组件标签上的自定义事件向下传递,其子组件可以直接通过this.$emit(eventName)的方式调用。
  1. 祖辈
<template>
  <div>
      <children test="123"  :name="name" :age="age" v-on:start1="say1" @start2="say2" ></children>
  </div>
</template>
<script setup lang="ts">
import children from './children.vue'
import {ref} from 'vue'
let name =ref('传给父组件的值')
let age = ref(20)
let say1 =() =>{
			console.log('第一个。。。。。');
		}
let say2=() =>{
			console.log('第二个。。。。。');
		}
</script>
  1. 父辈
<template>
  <div>
    <h3>父组件</h3>
    <div>组件名上绑定的非props特性($attrs): {{ $attrs }}</div>
    <!--vue2$listeners可以将父级的方法传递给孙子组件,vue3合并到attrs中-->
    <app-child v-bind.$attrs="$attrs" v-bind="$props"></app-child>
  </div>
</template>
<script setup lang="ts">
import AppChild from './third.vue';
import { defineProps,onMounted } from 'vue';
import type{PropType} from 'vue'
let emit =defineEmits(['start1'])

const props = defineProps({
  name:String as PropType<string>,
  age:Number as PropType<number>
})
onMounted(()=>{
  emit('start1')
})
</script>
  1. 孙辈
<template>
 <div>
	  <h3>子组件</h3>
	  <div>父组件传递过来的名称: {{name}}</div>
	  <div>父组件传递过来的年龄: {{age}}</div>
	  {{$attrs}}
  </div>
</template>
<script setup lang="ts">
import { defineProps,toRefs,onMounted } from 'vue';
import type{PropType} from 'vue'
let emit =defineEmits(['start2'])
const props = defineProps({
  name:String as PropType<string>,
  age:Number as PropType<number>
})
let {name,age} =toRefs(props)
onMounted(()=>{
  setTimeout(() => {
    emit('start2')
  }, 500);
})
</script>

# mitt

cnpm i mitt -S
import mitt from 'mitt';
const emitter = mitt();
export default emitter;
import emitter from './utils/eventbus';
emitter.emit("why", {name: "why", age: 18});
 emitter.on("why", (info) => {
	console.log("why:", info);
  });

  emitter.on("kobe", (info) => {
	console.log("kobe:", info);
  });
  // 参数变成两个,第一个是类型
  emitter.on("*", (type, info) => {
	console.log("* listener:", type, info);
  })
  • 取消监听的两种方式
最后更新: 8/20/2022, 9:09:04 AM