组件通信、插槽、动态组件、异步组件
修饰符
lazy
<!-- lazy修饰符是当点击回车的时候绑定的值才会改变 --> <input type="text" v-model.lazy='age'>
number
<!-- number修饰符指的是age的类型一致设置成number类型(没有的话是设置成了字符串型) --> <input type="text" v-model.number='age'>
trim
<!-- teim是过滤掉输入时两边的空格 --> <input type="text" v-model.trim='message'>
组件化开发
思想:提供了一种抽象,让我们可以开发出一个个独立可复用的小组件来构造我们的应用
使用方法
1.使用Vue.extend()创建出来组件构造器对象
2.调用Vue.component()方法注册组件
3.使用组件
<div id="app"> <!-- 3.使用 --> <mycpn> </mycpn> <mycpn> </mycpn></div><body> <script src="../../node_modules/vue/dist/vue.js"></script> <script> // 1. 创建一个组件构造器对象 const cpn = Vue.extend({ template: `<div> <h2>欢迎使用组件化开发</h2> <p>这里是内容</p> <p>哈哈哈</p> </div>` }) // 2.注册组件Vue.component('标签名', 组件构造器对象) Vue.component('mycpn', cpn) const app = new Vue({ el: '#app', data: { } }) </script></body>
综合写法,省略掉了Vue.extend(),但是component方法会自动调用extend函数
<div id="app"> <!-- 3.使用 --> <mycpn> </mycpn></div>
Vue.component('mycpn', { template: `<div> <h2>欢迎使用组件化开发</h2> <p>这里是内容</p> <p>哈哈哈</p> </div>` })
全局组件和局部组件
image.png
模板的两种写法
创建构造器&&注册组件
Vue.component('mycpn', { template: '#cpn' })
<script>标签
<script type="text/x-template" id="cpn"> <div> <h2>欢迎使用组件化开发</h2> <p>这里是内容</p> <p>哈哈哈</p> </div> </script>
<template>标签
<template id="cpn"> <div> <h2>欢迎使用组件化开发</h2> <p>这里是内容</p> <p>哈哈哈</p> </div> </template>
在模板里面添加数据
模板里面的数据要存放在data函数当中
<template id="cpn"> <div> <h2>{{title}}</h2> <p>这里是内容</p> <p>哈哈哈</p> </div> </template> Vue.component('mycpn', { template: '#cpn', data() { return { title: '我是标题' } } })
为什么data是一个函数,而不是一个对象? 因为Vue让每个组件对象都返回一个新的对象,因为如果是同一个对象的,组件在多次使用后会相互影响。
组件通信
父到子通信
image.png
props的三种写法
const cpn = { template: '#npm', // 1.数组类型写法 // props: ['cmessage'] props: { /* 2.类型限制 cmessage: String */ // 3.提供一些默认值 cmessage: { type: String, default: 'aaaa', //如果没有传值,默认为aaaa,且报错 required: true }, cmovies: { type: Array, //type:object默认值必须是函数返回,目的是防止不要智指向 一个对象 default () { return [111] } } } }
更多写法
image.png
关于props里面驼峰命名时传参的方式
image.png
在父组件中的自定义标签中,添加标签属性,那么在子标签中添加 v-bind="$attrs"
,这样子组件中就会添加父组件自定义标签里的属性
子到父的通信
子到父数据的传递需要使用自定义事件
<div id="app"> <mycpn @item-click='cClick'></mycpn> </div> <template id="npm"> <div> <button v-for='item of students' v-on:click='btnClick(item)'>{{item.name}}</button> </div> </template> <script src="../../node_modules/vue/dist/vue.js"></script> <script> //定义子组件 const cpn = { template: '#npm', data() { return { students: [{ id: 1, name: 'guo' }, { id: 2, name: 'zhang' }, { id: 3, name: 'liu' }, { id: 4, name: 'li' }] } }, methods: { btnClick(item) { this.$emit('item-click', item) } } } const app = new Vue({ el: '#app', data: { }, components: { mycpn: cpn }, methods: { cclick(item) { console.log('cClick', item); } } }) </script>
image.png
父子间双向通信
image.png
<body> <div id="app"> <cpn :pnum1='num1' :pnum2='num2' @cchange1='fchange1' @cchange2='fchange2' @changenum1='changenum1'></cpn> </div> <template id="tem"><div> <h1>props:{{pnum1}}</h1> <h1>data:{{cnum1}}</h1> <input type="text" v-bind:value='cnum1' @input='cinputchange1'> <h1>props:{{pnum2}}</h1> <h1>data:{{cnum2}}</h1> <input type="text" v-bind:value='cnum2' @input='cinputchange2'></div> </template> <script src="../../node_modules/vue/dist/vue.js"></script> <script> //子组件 const cpn = { template: '#tem', props: { //只接受父级传递过来的参数,不能手动对其进行修改 pnum1: Number, pnum2: Number }, data() { //因为父级也会改变props里面的值,所以我们初始化出来一个变量,来接受父级的改变的变量 return { cnum1: this.pnum1, cnum2: this.pnum2 } }, methods: { cinputchange1(event) { this.cnum1 = event.target.value this.$emit('cchange1', this.cnum1) this.cnum2 = this.cnum1 * 100 this.$emit('changenum1', this.cnum1 * 100) }, cinputchange2(event) { this.cnum2 = event.target.value this.$emit('cchange2', this.cnum2) this.cnum1 = this.cnum2 * 0.01 this.$emit('changenum2', this.cnum1) } } } //父组件 const app = new Vue({ el: '#app', data: { num1: 1, num2: 2 }, components: { cpn }, methods: { fchange1(pnum1) { this.num1 = pnum1 * 1 }, fchange2(pnum2) { this.num2 = pnum2 * 1 }, changenum1(num) { this.num2 = num }, changenum1(num) { this.num1 = num } } }) </script></body>
image.png
父子组件通信之$children(Vue3已经弃用了)
methods: { btnClick() { //以数组的形式输出所有的组件 console.log(this.$children); //调用子组件的函数 this.$children[0].cfn() //由于组件之间可能会插入其他的组件,所以但用数组的形式选择相应的子模块可能会出问题 //refs的用法 console.log(this.$refs); //输出一个对象,对象里包含组件中所有包含ref的组件, console.log(this.$refs.aaa.message) //输出子数据 } }
子访问父之 $parent
$root
$el
(可以拿到子组件整个模板)
子组件应该尽量避免直接访问父组件的数据,因为这样耦合度太高了
如果我们将子组件放在另外一个组件之内,很可能该父组件没有对应的属性,往往会引起问题
const app = new Vue({ el: '#app', data: { message: '我是父组件' }, components: { cpn: { template: '#cpn', methods: { btnClick() { //访问父组件 console.log(this.$parent); console.log(this.$parent.message); console.log(this.$root); } }, components: { ccpn: { template: '#ccpn', methods: { cbtnClick() { //访问根组件 console.log(this.$root.message); } } } } } } })
$refs
能够拿到所有绑定ref的DOM元素image.png
非父子组件之间的访问(信息由高到低)
父组件添加新属性:
provide:{name:"why"age:18}
provide里面的内容想要响应式处理需要用到computed,computed返回的是一个ref对象,需要取出其中的value来使用
当前的this指向的是根组件本身,即this能够访问到data里面的值
image.png
子组件也添加新属性
inject:["name","age"]
事件总线
image.png
在Vue2中
image.png
在Vue3中
如果想AB之间完成事件传递,那就要用到以下办法
下载
mitt
库npm install mitt
封装一个工具
image.png
导入工具并在B里面定义事件
btnClick() { console.log("about按钮的点击"); emitter.emit("why", {name: "why", age: 18}); // emitter.emit("kobe", {name: "kobe", age: 30}); }
在A里面监听函数
created() { emitter.on("why", (info) => { console.log("why:", info); }); emitter.on("kobe", (info) => { console.log("kobe:", info); });//所有的事件 type:事件名,info:传过来的信息 emitter.on("*", (type, info) => { console.log("* listener:", type, info); }) }
取消事件
image.png
插槽<slot></slot>
模板中slot标签里没有内容的时候,可以通过自定义组件标签里添加内容插入
模板中slot标签中有内容的时候,那就是默认插入的内容,若自定义组件标签里有内容,则会对其就行替换
image.png
具名slot
如果自定义模板标签里的内容没有写
name
属性,则会替换所有无名slot
标签内容image.png
第二种写法
v-slot:center
可以缩写成#center
插槽作用域改变
image.png
<div id="app"> <cpn> <template slot-scope='info'>{{info.data.join(' - ')}}</template> </cpn> </div> <template id="cpn"> <div> <slot :data='movies'></slot> </div> </template>
vue3中
image.png
动态组件
component 是内置组件,通过里面的is属性指定是哪个组件显示
image.png
keep-alive
include:只有匹配到name属性的组件才会被缓存
exclude:匹配到任何之间的名称都不会被缓存
max:最多可以缓存的数目,超过这个数目之后,会把最久未使用的组件删除缓存
image.png
在webpack打包的时候,将不同的函数分包
image.png
异步组件
通过定义异步组件的方式引入组件,并注册组件之后再使用组件,那webpack在进行打包的时候会进行分包处理
写法一:
import Home from './Home.vue'; const AsyncCategory = defineAsyncComponent(() => import("./AsyncCategory.vue"))
写法二:
const AsyncCategory = defineAsyncComponent({ loader: () => import("./AsyncCategory.vue"), loadingComponent: Loading, // errorComponent, // 在显示loadingComponent组件之前, 等待多长时间 delay: 2000, /** * err: 错误信息, * retry: 函数, 调用retry尝试重新加载 * attempts: 记录尝试的次数 */ onError: function(err, retry, attempts) { } })
异步组件和Suspense
Suspense:内置的全局组件,它里面有两个插槽,一个名叫:default(默认显示),另一个是:fallback(default能显示就不会显示该组件)
注册Suspense组件之后,使用组件,
作者:大佬教我写程序
链接:https://www.jianshu.com/p/c32f584efcab