【Vue学习笔记】三、vue组件与生命周期
三、vue组件与生命周期
1)vue组件
vue中规定:组件的后缀名是.vue
,App.vue
本质上就是一个组件
每个.vue
组件都由3部分组成:
- template -> 组件的模板结构
- script -> 组件的JavaScript行为
- style -> 组件的样式
注意<script>
中必须要写一个默认导出export default{}
,这是固定写法
.vue
中的data不能像之前一样指向对象,组件中的data必须是一个函数
<template>
<div>
<h3>这是用户自定义的Test.vue ---- {{ username }}</h3>
</div>
</template>
<script>
//默认导出,这是固定写法
export default{
//data数据源,不能指向对象
// data:{
// username: 'zs'
// }
//必须定义为一个函数
data(){
//这个return出去的{}中可以定义数据
return{
username:'admin'
}
}
}
</script>
<style>
.test-box{
background-color: pink;
}
</style>
methods、watch、computed
等写法与之前一致
<script>
export default{
data(){
return{
username:'admin'
}
},
methods:{
changeName(){
this.username = '哇哈哈'
}
}
}
</script>
2)组件之间的父子关系
3)使用组件的三个步骤
- 使用
Import
语法导入需要的组件 - 使用
components
节点注册组件 - 以标签的形式使用刚才注册的组件
通过components注册的是私有子组件
例如:
在组件A的components节点下,注册了组件F,则F只能在组件A中使用
4)注册全局组件
在vue项目的main.js
入口文件中,通过Vue.component()
方法,可以注册全局组件
//导入需要全局注册的组件
import Count from '@/components/Count.vue'
//参数1:字符串格式,表示组件的“注册名称”
//参数2:需要被全局注册的那个组件
Vue.component('MyCount',Count)
5)组件的props
1.创建props自定义属性
props是组件的自定义属性,在封装通用组件的时候,合理地使用props可以极大的提高组件的复用性
<script>
export default{
props:['init'],
}
</script>
在使用的注册好的组件标签时以标签属性的形式传参
<MyCount init="9"></MyCount>
2.结合v-bind使用自定义属性
加上:
之后就加上了v-bind
,此时""
里包裹的变成了js
语句,单个的9就变成了Number
类型
<MyCount :init="9"></MyCount>
3.props是只读的
vue规定:组件中封装的自定义属性是只读的,程序员不能直接修改props的值,否则会直接报错
4.转接props值
如果想要修改props中属性的值,应该让data中的属性来转接这个值
把init
赋值给count
,直接让count: this.init
即可
<script>
export default{
props:['init'],
data(){
return{
count: this.init
}
}
}
</script>
5.props的默认值
在声明自定义属性时,可以通过default来定义属性的默认值
<script>
export default{
props:{
init:{
//用default属性定义属性的默认值
default: 0
}
}
}
</script>
6.props的type属性
在声明自定义属性时,可以通过type来定义属性的值类型
<script>
export default{
props:{
init:{
default: 0,
//init值的类型必须是Number数字
type: Number
}
}
}
</script>
7.props的required必填项
required
属性为true表示该props属性为必填项,如果不传就会报错
<script>
export default{
props:{
init:{
default: 0,
type: Number,
//必填项校验
required: true,
}
}
}
</script>
6)组件之间的样式冲突问题
1.冲突的原因
默认情况下,写在.vue
中的样式会全局生效,因此很容易造成多个组件之间的样式冲突问题
样式冲突的根本原因:
- 单页面程序中,所有组件的DOM结构,都是基于唯一的index.html页面进行呈现的
- 每个组件中的样式,都会影响整个index.html页面中的DOM元素
2.冲突的解决方法
在<style>
标签中加上scoped
属性即可解决
<style lang="less" scoped>
.test-box{
background-color: pink;
}
</style>
原理是在组件中的每个标签里加上一个不同的自定义属性data-v-xxx
,通过自定义属性选择器[data-v-xxx]
选择到这个属性
3.使用deep
修改子组件中的样式
<style lang="less" scoped>
.test-box{
background-color: pink;
}
/deep/ h5{
color: pink;
}
</style>
这样操作会把h5[data-v-xxx]
变为[data-v-xxx] h5
,变成了一个后代选择器
当使用第三方组件库的时候,如果有修改组件默认样式的需求,需要用到/deep/
(因为不能直接修改组件库的源码)
7)vue组件的实例对象
创建点.vue
组件相当于创建了一个构造函数,在以标签的形式使用的时候就相当于new
了一个组件实例,每一个组件就相当于一个Vue
实例
8)生命周期
1.生命周期&生命周期函数
生命周期(Life Cycle)是指一个组件从创建 -> 运行 -> 销毁的整个阶段,强调的是一个时间段
生命周期函数:是由vue框架提供的内置函数,会伴随组件的生命周期,自动按次序执行
生命周期函数与props/data/methods
等同级,以函数的形式创建
<script>
export default{
data:{
},
beforeCreated(){ //创建了beforeCreated生命周期函数
}
}
</script>
2.组件生命周期函数的分类
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-DUz4AJIe-1647945088148)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20220321145908355.png)]
按照这个顺序依次执行函数其中create
代表创建,mount
代表渲染到页面上,update
代表组件更新,Destroy
代表销毁

3.beforeCreate和created的特点

在beforeCreate
阶段,组件的props/data/methods
都处于不可用的状态,这个生命周期不重要
在created
阶段,组件的props/data/methods
已创建好可以使用,但是模板结构尚未生成
注意:在
created
阶段,可以使用Ajax请求数据来初始化参数,非常常用!
4.beforeMount和mounted

在beforeMount
之前,先寻找要要生成结构的区域(’#app’),然后根据数据和模板在这个区域中生成html
结构,这个阶段同样可以发起Ajax请求,但是在created
阶段发起更好,所以这个阶段也不重要

如果要操作当前实例的DOM,最早可以在mounted
阶段操作
5.beforeUpdate和updated

在数据发生变化之后会执行beforeUpdate
阶段,然后再将更新的数据渲染到组件的DOM结构里,这个阶段会在页面刚打开的时候触发一次
更新数据渲染完毕后触发updated
当数据发生变化之后,为了能够操作到最新的DOM结构,代码必须写在created
阶段
6.beforeDestroy和destroyed
这两个阶段不常用
使用v-if
可以实现组件的销毁

9)组件之间的数据共享
1.组件之间的关系
在项目开发中,组件之间最常见的关系分为如下两种:
- 父子关系
- 兄弟关系
如果两个组件离得比较远,则当作兄弟关系处理(即使是后代)
2.父组件向子组件传递数据
使用自定义属性
//父组件
<Son :msg="message" :user="userinfo"></Son>
<script>
data(){
return{
message: 'hello vue.js',
userinfo: { name: 'zs', age: 20}
}
}
</script>
//父组件
<template>
<div>
<h5>Son组件</h5>
<p>父组件传递过来的msg指是:{{ msg }}</p>
<p>父组件传递过来的user指是:{{ user }}</p>
</div>
</template>
<script>
props: ['msg', 'user']
</script>
传递复杂对象是传递一个引用,使用user.name
可以直接修改到父组件中的user.name
,一般不要这么操作,而应该去设定一个新变量来接收
3.子组件向父组件传递数据
- 先定义一个变量来接收子组件传递过来的值
- 子组件向父组件共享数据使用自定义事件
$emit(事件,参数)
代表触发一个事件,父组件中按照触发事件同样的方法去接收这个
//子组件
<script>
export default {
data(){ return{count: 0}},
methods: {
add(){
this.count++;
//修改数据时,通过$emit()触发自定义事件
this.$emit('numchange',this.count);
},
},
}
</script>
//父组件
<Son @numchange="getNewCount"></Son>
<script>
export default {
data(){ return{countFromSon: 0}},
methods: {
getNewCount(val){
this.countFromSon = val
},
},
}
</script>
4.兄弟组件之间的数据共享
在vue2.x中,兄弟组件之间数据共享的方案是EventBus
- 创建
eventBus.js
模块,并向外共享一个Vue的实例对象 - 在数据发送方,调用
bus.$emit('事件名称', 要发送的数据)
来发送数据 - 在数据接收方,调用
bus.$on('事件名称', 事件处理函数)
来接收数据
接收方写在created
阶段中,是一个固定写法
10)ref引用
1.ref操作DOM
- 在需要操作的标签上命名一个属性
ref="xxx"
- 通过
this.$refs.xxx
来操作DOM
<div>
<h1 ref="myh1">一个标签</h1>
</div>
<script>
export default{
methods:{
showThis(){
this.$refs.myh1.style.color = "red"
}
}
}
</script>
2.ref操作组件
ref除了可以操作DOM,也可以用来操作组件
<template>
<div>
<Left ref="comLeft"></Left> //给子组件绑定了ref名称
</div>
</template>
<script>
methods:{
onReset(){
this.$refs.comLeft.resetCount() //可以用refs来调用Left组件里的resetCount()方法
this.$refs.comLeft.count = 0 //也可以直接操作值
}
}
</script>
3.this.$nextTick
如果想把代码的执行延后到页面重新渲染完毕之后,需要用到this.$nextTick(callback)
方法
<script>
methods:{
showInput(){
this.inputVisible = true
//此时把布尔值改变了,但是因为生命周期,页面还没有来得及渲染,下面的语句找不到这个文本框
console.log(this.$refs.iptRef)
//使用$nextTick方法执行就不会报错
this.$nextTick(() => {
console.log(this.$refs.iptRef)
})
}
}
</script>