组件通信、插槽、动态组件、异步组件
2021-05-08 本文已影响0人
大佬教我写程序
修饰符
- 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组件之后,使用组件, image.png