目录
一、组件基础
1、vue是组件化开发,将整体拆分成很多的小模块
2、vue单文件组件(又名xx.vue 文件,缩写sfc)是一种特殊的文件格式
它允许将vue组件的模版(html vue叫template),逻辑(js)与样式(css)封装在单个文件中
3、要使用组件,就要知道组件注册的步骤:
- 第一步:引入组件
- 第二步:挂载组件(通过components进行挂载)
-
第三步:显示组件
文件结构:
代码演示:
在App.vue根组件中
<template>
<div id="app">
<!-- 第三步:使用组件 -->
<MyCompoent/>
</div>
</template>
<script>
// 第一步:引入组件
import MyCompoent from './components/MyCompoent.vue';
export default {
components: {
// 第二步:注册组件
MyCompoent
}
}
</script>
在MyCompoent.vue中
scoped: 如果在style中添加此属性,就代表着只在当前样式只在当前组件中生效
<template>
<div>
<h1>我是MyCompoent组件</h1>
</div>
</template>
<style lang="less" scoped>
h1 {
color: skyblue;
}
</style>
效果展示:
二、组件交互
组件和组件之间是存在数据交互的。组件和组件的交互有props和自定义事件两种方式。
1、props组件交互
props是将父组件的数据传入子组件
文件结构:
先在app.vue注册使用PropFather.vue组件
在父组件Father.vue中使用子组件Son.vue
父向子传值:可以传静态数据,也可以传动态数据
这样我们就向子组件传入了静态的msg和动态的msg1
<template>
<div>
<h1>我是父组件</h1>
<hr>
<Son msg="我是父组件的静态数据msg" :msg1="msg1"/>
</div>
</template>
<script>
import Son from './Son.vue'
export default {
components: {
Son
},
data() {
return {
msg1: "我是父组件的动态数据msg1"
}
}
}
</script>
这里只是传给了子组件并不能看到效果,得要子组件接收并展示。
对于props的接收形式有两种:一是数组形式,二是对象的形式。
子组件props接收到的值不用在data中声明,可以直接使用。
先来解释第一种接收形式:数组形式:
<template>
<div>
<h2>我是子组件</h2>
<p>{{ msg }}---{{ msg1 }}</p>
</div>
</template>
<script>
export default {
// 第一种:数组形式
props: ['msg','msg1']
}
</script>
<style lang="less" scoped>
</style>
内容展示:
props接收对象形式:
传入的对象要进行类型校验
<template>
<div>
<h2>我是子组件</h2>
<p>{{ msg }}---{{ msg1 }}</p>
</div>
</template>
<script>
export default {
props: {
msg: String,
msg1: String
}
}
</script>
上面是传入对象的最简单的写法。
下面是所有传入对象的其他写法:
props: {
// 基础的类型检查(null 和 undefined 会通过任何类型验证)
valueA: Number,
// 多个可能的类型
valueB: [String, Number],
// 必填的字符串(必传)
valueC: {
type: String,
required: true
},
// 带有默认值的数字
valueD: {
type: Number,
default: 100
},
// 自定义验证函数
valueF: {
validator: function (value) {
// 这个值必须匹配下列字符串中的一个
return ['success', 'warning', 'danger'].indexOf(value) !== -1
}
}
}
这里要注意一点:如果类型是数组和对象默认值必须使用函数(工厂模式)进行返回
props: {
arr: {
type: Array,
// 数组和对象默认值必须使用函数(工厂模式)进行返回
// 错误写法: default: []
default: function () {
// 返回为空
return []
}
}
}
2、自定义事件交互
上面讲了props可以将数据从父组件传递到子组件, 那么自定义事件可以在组件中反向传递数据。从子组件传递数据到父组件就可以使用自定义事件实现 $emit
在子组件中,要通过事件进行触发,通过this.$emit(参数1,参数2)来注册自定义事件。
参数1: 字符串,也就是自定义事件的名字,在父组件当作事件来使用
参数2: 传递的数据
通过代码来更清楚的了解:
<template>
<div>
<h2>我是子组件</h2>
<button @click="sendClickHandle">点击传递</button>
</div>
</template>
<script>
export default {
data() {
return {
message: "我是Son数据"
}
},
methods: {
sendClickHandle() {
this.$emit("onEvent", this.message)
// 将 onEvent 在父组件当作事件来处理
}
}
}
</script>
通过子组件注册的自定义事件名“onEvent”,接下来在父组件中进行使用:
<template>
<div>
<h1>我是父组件</h1>
<p>{{msg}}</p>
<!-- 这里的onEvent就是通过子组件注册的事件名 -->
<Son @onEvent="getDateHandle" />
</div>
</template>
<script>
import Son from './Son.vue'
export default {
components: {
Son
},
// data用来接收数据
data() {
return {
msg: ""
}
},
methods: {
getDateHandle(data) {
this.msg = data;
}
}
}
</script>
因为子组件的this.message数据给到了自定义事件的参数里,所以父组件自定义事件中的getDateHandle 可以拿到数据data。把传递来的参数赋值给msg就能显示出来。
通过点击子组件的button,父组件就获取到了数据了。
三、组件的生命周期
组件的生命周期: vue组件从创建到销毁的过程
钩子函数(生命周期函数): 在特定时间点上触发的函数,不需要我们自己调用。组件加载到某个阶段后,会自动触发。用它就是把一些特定的逻辑放在指定的钩子函数里。
vue2中有8个生命周期函数(vue2和vue3有些差别)
创建时: beforeCreate, created
渲染时: beforeMount, mounted
更新时: beforeUpdate, updated
卸载时: beforeDestroy, destroyed
下面根据代码可以更直观的了解:
<template>
<div>
<h2 ref="myname">小天</h2>
<button @click="btnDestroy">销毁</button>
</div>
</template>
<script>
export default {
data() {
return {
msg: '123'
}
},
methods: {
btnDestroy() {
this.$destroy(); // 对组件进行销毁
}
},
// 创建之前,特点:数据代理还未完成,访问不到数据和方法,但是可以this VUe实例
beforeCreate() {
console.group('beforeCreate: 数据代理还没有完成,访问不到数据和方法')
console.log(this); // Vue实例
console.log('$el:', this.$el); // $el是dom元素
console.log('msg:', this.msg);
},
created() {
console.group('created: 数据代理已经完成,能访问数据和方法。最早可以发送网络请求的地方')
console.log('$el:', this.$el);
console.log('msg:', this.msg);
},
beforeMount() {
// 和mounted的html有不同
console.group('beforeMount: 挂载之前')
console.log('$el:', this.$el);
console.log('msg:', this.msg);
},
mounted() {
console.group('mounted: 挂载完成, 页面进入稳定运行状态。获取dom元素, 开启定时器, 绑定事件监听')
console.log('$el:', this.$el);
console.log('msg:', this.msg);
},
// 下面的需要特定条件触发才显示
beforeUpdate() {
// 数据更新之后,界面更新之前触发的钩子函数
console.group('beforeUpdate: 数据已经更新,界面更新之前触发')
console.log(this.flag, this.$refs.myname.innerHTML);
},
updated() {
console.group('updated: 数据已经更新,界面也已经更新完成')
console.log(this.flag, this.$refs.myname.innerHTML);
},
beforeDestroy() {
// 销毁之前 例如销毁定时器
console.group('beforeDestroy: 销毁之前,做一些收尾工作,例如销毁定时器')
},
destroyed() {
console.group('销毁之后')
}
}
</script>
上面提到的this是vue实例,$el是dom元素。
四、动态组件
动态加载,可以用来写tab选项卡
用法:<component :is="组件名"></component> 只有写了组件才会展示出来
使用时,还是必须对组件进行引入和注册,但是挂载变成了动态的,通过<component :is="xxx"></component>动态挂载
<template>
<div id="app">
// 3、动态创建
<component :is="'one'"></component>
<component :is="'two'"></component>
</div>
</template>
<script>
// 1、对组件进行引入
import one from './components/one.vue';
import two from './components/two.vue';
export default {
name: 'App',
// 2、注册组件
components: {
one,
two
}
}
</script>
五、缓存组件
之前的动态组件,每次切换一次 mounted 执行一次,说明每次切换渲染一次。当组件比较多,内容较多的话,耗性能。
用法:keep-alive包住component
<keep-alive :exclude="['组件名1']">
<component :is="组件名"></component>
</keep-alive>
这里的 :exclude="['组件名1']"也可以没有
exclude:字符串或正则表达式。任何名称匹配的组件都不会被缓存。
include:字符串或正则表达式。只有名称匹配的组件会被缓存。
使用和动态组件是差不多的,只是外面包裹了一层。
这里重点注意的是:
- 缓存组件被缓存了, mounted只会执行一次, beforeDestroy不会被卸载有两个生命周期来代替 mounted 和 beforeDestroy。
- 当使用keep-alive可使用 activated 来代替 mounted 发送请求。
- 当使用keep-alive可使用 deactivated 来代替 destoyed 收尾工作, 比如清除缓存数据。
缓存后就不走挂载和卸载
根据下面的例子来深入了解:
在App.vue中
<template>
<div id="app">
<!-- 动态组件 -->
<div class="tabs">
<div class="tab-item" @click="changeTab('Home')">Home</div>
<div class="tab-item" @click="changeTab('Personal')">Personal</div>
</div>
<component :is="comName"></component>
<hr>
<!-- 缓存组件 -->
<div class="tabs">
<div class="tab-item" @click="keepChange('KeepHome')">Home</div>
<div class="tab-item" @click="keepChange('KeepPersonal')">Personal</div>
</div>
<keep-alive>
<component :is="keepComName"></component>
</keep-alive>
</div>
</template>
<script>
import Home from './components/01-dynamicComponent/Home.vue';
import Personal from './components/01-dynamicComponent/Personal.vue';
import KeepHome from './components/02-keepComponent/KeepHome.vue';
import KeepPersonal from './components/02-keepComponent/KeepPersonal.vue';
export default {
name: 'App',
components: {
Home,
Personal,
KeepHome,
KeepPersonal
},
data() {
return {
comName: 'Home',
keepComName: 'KeepHome'
}
},
methods: {
changeTab(name) {
this.comName = name;
},
keepChange(name) {
this.keepComName = name;
}
}
}
</script>
在动态组件Home.vue中写:
export default {
name: 'Home',
mounted() {
console.log('Home挂载完成');
},
beforeDestroy() {
console.log('Home组件即将卸载');
}
}
在缓存组件KeepHome.vue中写:
export default {
name: 'KeepHome',
// mounted只会使用一次,beforeDestroy不会被执行
mounted() {
console.log('keepHome 挂载完成');
},
beforeDestroy() {
console.log('keepHome 组件即将卸载');
},
// 代替上面两个:
activated() {
console.log('keepHome 处于激活状态');
},
deactivated() {
console.log('keepHome 处于失活状态');
}
}
缓存组件的mounted只有当页面第一次加载的时候执行一次,而beforeDestroy不会被执行。
组件的加载和卸载变成了激活和失活状态。
刚进页面时:
对动态组件的tab进行切换,会不断打印出挂载和卸载:
但是对缓存组件进行切换,就变成了失活和激活,不会走挂载和卸载:
六、异步组件
在大型应用中,我们可能需要将应用分割成小一些的代码,并且只在需要的时候才从服务器加载一个模块。vue只有在这个组件需要被渲染的时候才会触发该工厂函数,并且会把结果缓存起来供未来重渲染。
简而言之,异步组件就是:使用时再加载。
import MyComponent from './component/MyComponent.vue' 这种写法是同步写法
异步组件导入:用箭头函数加import
示例代码:
写法一:
<template>
<div id="app">
<!-- 使用组件 -->
<Async/>
</div>
</template>
<script>
// 1、异步引入
const Async = () => import('./components/Async.vue')
export default {
name: 'App',
// 2、注册组件
components: {
Async
}
}
</script>
写法二:将引入和注册合成一步了
<template>
<div id="app">
// 使用
<Async/>
</div>
</template>
<script>
export default {
name: 'App',
components: {
// 1、2 引入和注册
Async: () => import('./components/Async.vue')
}
}
</script>
关于组件的内容暂时就这么多了吧🌝