利用vue脚手架创建一个项目
lua
vue create component_demo
可以在vscode中安装Vue VSCode Snippets插件或vue3 snippets,帮助生成一些代码片段
vbase-css
可以快速生成vue3模板
组件通信
父子组件之间通信
- 父组件传递给子组件:通过props属性
- 子组件传递给父组件:通过$emit触发事件
在父组件中引入组件传值
ruby
<show-message :title="title"></show-message>
子组件中在props中定义
javascript
props: {
title: {
type: String,
require: true, //表示这个属性是必传的
default: '123' //默认属性
},
//当属性是Object型时,默认值要为一个函数.因为多个组件会引用同一个对象
info: {
type: Object,
default() {
return {name: 'juju'}
}
},
//自定义验证函数
man: {
validator(value) {
//这个值必须匹配下列字符串中的一个
return ['success', 'warning', 'danger'].include(value)
}
}
}
props命名 建议父组件中要传递的值采用驼峰写法
ruby
<show-message :message-info="title"></show-message>
...
data() {
return {
messageInfo: 'damn'
}
}
非props的Attribute
当我们传递给一个组件某个属性,但是该属性并没有定义对应的props或者emits,就称之为非props的Aaaribute
常见的包括css,style,id属性等 例如当给子组件设置一个class属性
ruby
<show-message class="liin" :title="title"></show-message>
当组件有单个根节点时,会自动添加到根节点的attribute
xml
<template>
<div class="liin">
<h2>{{ title }}</h2>
<h2>{{ info.name }}</h2>
</div>
</template>
如果我们不希望根组件继承attribute,可以在子组件中设置inheritAttrs: false 我们可以通过$attrs来访问所有的非props的attribute
css
<h2 :class="$attrs.class">{{ title }}</h2>
通过v-bind绑定所有属性 父组件
ruby
<show-message id="xixi" class="liin" :title="title"></show-message>
子组件
bash
<h2 v-bind="$attrs">{{ title }}</h2>
子组件传递给父组件 子组件中与vue2不同的是多了个emit参数,里面存放着可能要触发的事件
javascript
export default {
//emits属性,里面存放着待会会触发的事件
emits: ["add", "sub"],
//emits的对象写法,目的是为了进行参数的验证
emits: {
sub: null,
add: (name, age) => {
if(name == 'juju') {
return true
}
return false
}
},
methods: {
increament() {
this.$emit("add","juju",18) //后面多个参数跟着的是要传的参数
},
decreament() {
this.$emit("sub")
},
}
}
父组件中写法与vue2相同
ini
<counter-operation @add="addOne" @sub="subOne"></counter-operation>
非父组件的通信
- provide/inject
- mitt全局事件总线
- vuex
provide和inject
无论层级结构有多深,父组件都可以作为其所有子组件的依赖提供者; 父组件有一个provide选项来提供数据,子组件有一个inject选项来使用这些数据
在父组件中添加一个provide
css
components: {
Home
},
provide: {
name: 'juju',
age: 18
},
在子孙组件中注入
arduino
export default {
inject: ["name", "age"]
}
当然,许多情况下数据都要从data中拿并从后台请求了,此时的provide要写成函数形式
javascript
provide() {
return {
name: juju,
age: 18,
title: this.title
}
},
如果后续title改变的话,inject里的值并不是响应式的,可以使用vue中的computed函数
javascript
import {computed} from 'vue'
provide() {
return {
name: juju,
age: 18,
title: computed(() => {
this.title
})
}
},
全局事件总线mitt库
安装mitt库
npm install mitt
创建一个eventbus.js文件
javascript
import mitt from 'mitt'
const emitter = mitt();
export default emitter;
// 也可以创建多个mitt对象
测试叔侄组件之间通信 在叔组件中传递
javascript
import emmiter from '../util/eventbus.js'
methods: {
btnClick() {
emmiter.emit("handle", "juju",18); //第一个参数方法名,后面跟着的是参数
}
}
在侄组件的生命周期中监听
javascript
import emitter from '../util/eventbus.js'
created() {
emitter.on("handle", (name,age)=> {
console.log(name)
})
}
mitt事件取消
csharp
emitter.all.clear(); //全部取消监听
function onFoo(){}
emitter.on('foo', onFoo) //监听
emitter.off('foo', onFoo) //取消监听
插槽slot
对于不同的区域可能存放不同的内容,因此要使用slot
- 插槽的使用过程其实是抽取共性,预留不同
- 我们会将共同的元素,内容依然在组件内进行封装
- 同时将不同的元素使用slot作为占位,让外部决定到底显示什么样的元素
简单插槽的使用
定义一个子组件
xml
<h2>组件开始</h2>
<slot></slot>
<h2>组件结束</h2>
在父组件中使用
xml
<my-slot-cpn>
<button>我是按钮</button>
</my-slot-cpn>
此时中间部分可以展示按钮或者图片或者组件,vue会将内容替换到slot的位置
具名插槽的使用
插槽的默认使用 如果子组件是这样的
xml
<div>
<h2>组件开始</h2>
<slot>
<i>我是默认内容</i>
</slot>
<h2>组件结束</h2>
</div>
父组件
perl
<my-slot-cpn>
</my-slot-cpn>
则显示默认内容
具名插槽
在子组件中编写有name属性的插槽
xml
<template>
<div class="nav-bar">
<div class="left">
<slot name="left"></slot>
</div>
<div class="center">
<slot name="center"></slot>
</div>
<div class="right">
<slot name="right"></slot>
</div>
</div>
</template>
在父组件中用template v-slot方式分配元素至对于的slot内
xml
<!-- 具名插槽的使用 -->
<nar-bar>
<template v-slot:left>
<button>左边按钮</button>
</template>
//等价于
<template #left>
<button>左边按钮</button>
</template>
<template v-slot:center>
<h2>我是标题</h2>
</template>
<template v-slot:right>
<i>右边的元素</i>
</template>
</nar-bar>
效果
如果名字是不定的话,template应该这么写
xml
<template v-slot:[name]>
<i>右边的元素</i>
</template>
作用域插槽
即子组件中的slot属性,可以传给父组件 子组件
ruby
<template>
<div>
<template v-for="(item,index) in names" :key="item">
<slot :item="item" :index="index"></slot>
</template>
</div>
</template>
父组件通过v-slot取得
xml
<show-names>
<template v-slot="slotProps">
<button>{{slotProps.item}}-{{ slotProps.index }}</button>
</template>
</show-names>
//是下面的简写,v-slot后跟插槽名字
<show-names>
<template v-slot:default="slotProps">
<button>{{slotProps.item}}-{{ slotProps.index }}</button>
</template>
</show-names>
当只有默认插槽时,可以使用独占默认插槽写法
xml
<show-names v-slot="slotProps">
<button>{{slotProps.item}}-{{ slotProps.index }}</button>
</show-names>
动态组件的实现
根据不同的场景,去渲染不同的组件。以前基本都使用v-if去判断从而渲染不同的组件。但是当组件很多的时候,组件会显得很臃肿。此时可以考虑采用动态组件去实现。
动态组件是使用component组件,通过特殊的attribute is来实现。 例如
ini
<component is="hello-world"></component>
此时就会渲染HelloWorld组件
动态组件传递参数
ini
<component is="hello-world" name="juju" :age="18" @helloClick="handleClick"></component>
此时在helloworld组件可以用props接收,同时动态组件可以接受子组件的点击事件
keep-alive
默认情况下,切换组件会被销毁,再次回来会重新创建组件。此时,在开发中某些情况我们希望继续保持组件的状态,而不是销毁掉,这时我们可以使用一个内置组件:keep-alive。
xml
<keep-alive>
<component is="hello-world" name="juju" :age="18" @helloClick="handleClick"></component>
<keep-alive>
keep-alive有一些属性,
- include.[string|regexp|array]只有名称匹配的组件会被缓存。
- exclude[string|regexp|array]任何匹配的组件都不会会被缓存。
- max[number|string]最多可以缓存多少组件实例,一旦达到这个数字,那么缓存组件中最近没有被访问的实例会被销毁
include是根据组件的name属性来进行适配,因此有时需要给组件加上name属性
arduino
export default {
name: 'about',
}
webpack分包
正常打包的话会将所有资源打包到一个app.js文件中,采用webpack分包方式会将利用分包的资源打包到一个chunk文件中,需要时再去加载,可以优化首屏渲染。
javascript
//通过import函数导如的模块,后续webpack对其进行打包的时候就会进行分包的操作
import('./utils/math').then(res => {console.log(sum(10,20))})
异步组件
同时vue3提供一个函数可以使打包后的组件成为一个单独的文件夹,加快首屏渲染速度 defineAsyncComponent接受两种类型的参数: 类型一:工厂函数,该工厂函数需要返回一个promise对象 类型二:接受一个对象类型,对异步函数进行配置
javascript
import {defineAsyncComponent} from 'vue'
const AsyncHello = defineAsyncComponent(() => import('./components/HelloWorld.vue'))
一般开发中使用路由懒加载比较多
获取元素和refs
在某些情况下,我们组件想要直接获取到元素对象或者子组件实例 在vue开发中我们不推荐dom操作的,这个时候,可以给元素或者组件绑定以恶搞ref的attribute属性
javascript
<h2 ref="title">哈哈哈哈哈哈</h2>
mounted() {
console.log(this.$refs.title)
},
打印结果
对于组件来说,调用this.refs.xx可以获取组件实例,取得data中的数据或调用一些方法。 同时,可以在子组件中采用this. <math xmlns="http://www.w3.org/1998/Math/MathML"> p a r e n t 取得父组件实例以及 parent取得父组件实例以及 </math>parent取得父组件实例以及root获取根组件
组件的生命周期
- beforecreated
- created
- beforemounted
- mounted
- beforeUpdate
- updated
- beforeUnmount
- unmounted
对于有缓存的组件来说,再次进入时,我们是不会执行created或者mounted等生命周期的,这时我们可以使用activated和deactiveated这两个生命周期钩子函数来监听。
组件的v-model
ruby
//组件绑定v-model与下列的等价
<hy-input v-model="title"></hy-input>
<!-- <hy-input :modelValue="title" @update:model-value="title = $event"></hy-input> -->
xml
<template>
<div>
<button @click="btnClick">input按钮</button>
{{ modelValue }}
</div>
</template>
<script>
export default {
props: {
modelValue: String
},
emits: ["update:modelValue"],
methods: {
btnClick() {
this.$emit("update:modelValue", '嘿嘿')
}
}
}
</script>
$event为update:model-value事件传出来的参数
如果子组件中有input可以实现v-model绑定计算属性的操作,因为不能绑定到props里
kotlin
<input v-model="value" />
...
computed: {
value: {
get(){
return this.modelValue
},
set(value) {
this.$emit("update:modelValue", value)
}
}
},
绑定多个v-model,后面加名字,此时why为子组件prop的值
ini
<hy-input v-model="title" v-model:why="why"></hy-input>