1 生命周期
- 什么是生命周期:从Vue实例创建、运行、到销毁期间,总是伴随着各种各样的事件,这些事件,统称为生命周期!
- 生命周期钩子:就是生命周期事件的别名而已;
- 生命周期钩子 = 生命周期函数 = 生命周期事件
- 可以参考 vue 官方文档给出的“生命周期图示”,进一步理解组件生命周期执行的过程:
https://cn.vuejs.org/v2/guide/instance.html# 生命周期图示
创建期间的生命周期函数:- beforeCreate:实例刚在内存中被创建出来,此时,还没有初始化好 data 和 methods 属性
- created:实例已经在内存中创建OK,此时 data 和 methods 已经创建OK,此时还没有开始 编译模板。组件被创建时这个函数会被自动调用。
- beforeMount:此时已经完成了模板的编译,但是还没有挂载到页面中
- mounted:此时,已经将编译好的模板,挂载到了页面指定的容器中显示
运行期间的生命周期函数:
- beforeUpdate:状态更新之前执行此函数, 此时 data 中的状态值是最新的,但是界面上显示的 数据还是旧的,因为此时还没有开始重新渲染DOM节点
- updated:实例更新完毕之后调用此函数,此时 data 中的状态值 和 界面上显示的数据,都已经完成了更新,界面已经被重新渲染好了!
销毁期间的生命周期函数:
- beforeDestroy:实例销毁之前调用。在这一步,实例仍然完全可用。
- destroyed:Vue 实例销毁后调用。调用后,Vue 实例指示的所有东西都会解绑定,所有的事件监听器会被移除,所有的子实例也会被销毁。
<script>
// 创建 Vue 实例,得到 ViewModel
var vm = new Vue({
el: '#app',
data: {
msg: 'ok'
},
methods: {
show() {
console.log('执行了show方法')
}
},
beforeCreate() { // 这是我们遇到的第一个生命周期函数,表示实例完全被创建出来之前,会执行它
// console.log(this.msg)
// this.show()
// 注意: 在 beforeCreate 生命周期函数执行的时候,data 和 methods 中的 数据都还没有没初始化
},
created() { // 这是遇到的第二个生命周期函数
// console.log(this.msg)
// this.show()
// 在 created 中,data 和 methods 都已经被初始化好了!
// 如果要调用 methods 中的方法,或者操作 data 中的数据,最早,只能在 created 中操作
},
beforeMount() { // 这是遇到的第3个生命周期函数,表示 模板已经在内存中编辑完成了,但是尚未把 模板渲染到 页面中
// console.log(document.getElementById('h3').innerText)
// 在 beforeMount 执行的时候,页面中的元素,还没有被真正替换过来,只是之前写的一些模板字符串
},
mounted() { // 这是遇到的第4个生命周期函数,表示,内存中的模板,已经真实的挂载到了页面中,用户已经可以看到渲染好的页面了
// console.log(document.getElementById('h3').innerText)
// 注意: mounted 是 实例创建期间的最后一个生命周期函数,当执行完 mounted 就表示,实例已经被完全创建好了,此时,如果没有其它操作的话,这个实例,就静静的 躺在我们的内存中,一动不动
},
// 接下来的是运行中的两个事件
beforeUpdate() { // 这时候,表示 我们的界面还没有被更新【数据被更新了吗? 数据肯定被更新了】
/* console.log('界面上元素的内容:' + document.getElementById('h3').innerText)
console.log('data 中的 msg 数据是:' + this.msg) */
// 得出结论: 当执行 beforeUpdate 的时候,页面中的显示的数据,还是旧的,此时 data 数据是最新的,页面尚未和 最新的数据保持同步
},
updated() {
console.log('界面上元素的内容:' + document.getElementById('h3').innerText)
console.log('data 中的 msg 数据是:' + this.msg)
// updated 事件执行的时候,页面和 data 数据已经保持同步了,都是最新的
}
});
</script>
1.1 ref 引用
1.1.1 什么是 ref 引用
ref 用来辅助开发者在不依赖于 jQuery 的情况下,获取 DOM 元素或组件的引用。
每个 vue 的组件实例上,都包含一个 $refs 对象,里面存储着对应的 DOM 元素或组件的引用。默认情况下,组件的 $refs 指向一个空对象。
1.1.2 使用ref 引用DOM 元素
如果想要使用ref 引用页面上的DOM 元素,则可以按照如下的方式进行操作:
如果想要使用ref 引用页面上的组件实例,则可以按照如下的方式进行操作:
1.1.3 控制文本框和按钮的按需切换
通过布尔值inputVisible来控制组件中的文本框与按钮的按需切换。示例代码如下:
- 让文本框自动获得焦点:
当文本框展示出来之后,如果希望它立即获得焦点,则可以为其添加ref 引用,并调用原生DOM 对象的.focus() 方法即可。示例代码如下:
1.1.4 this.$nextTick(cb) 方法
组件的$nextTick(cb)
方法,会把cb 回调推迟到下一个DOM 更新周期之后执行。通俗的理解是:等组件的DOM 更新完成之后,再执行cb 回调函数。从而能保证cb 回调函数可以操作到最新的DOM 元素。
总结:
- ①能够知道vue 中常用的生命周期函数
创建阶段、运行阶段、销毁阶段
created、mounted - ②能够知道如何实现组件之间的数据共享
父-> 子(自定义属性)
子-> 父(自定义事件)
兄弟组件(EventBus) - ③能够知道如何使用ref 引用DOM 元素或组件
给元素或组件添加ref="xxx"的引用名称
通过this.KaTeX parse error: Unexpected character: '' at position 20: ….xxx 获取元素或组件的实例̲nextTick() 函数的执行时机
1.2 父子之间传值
1.2.1 父组件–>子组件传值
父传子,子直接props接收就可以,就像王位继承
- Vue中默认情况下子组件是不能访问父组件的数据的,因此为了让子组件可以访问父组件的数据,必须通过父组件传递
- 1 在父组件中通过v-bind传递数据 格式: v-bind:自定义接收名称= “要传递的数据”
<template>
<view>
<h3>父组件</h3>
<input type="text" v-model="value" />
<!-- 使用子组件 -->
<son :data="value"></son>
</view>
</template>
<script>
//引入子组件
import son from '@/pages/son/son.vue'
export default {
//注册子组件
components: { son },
data() {
return {
value:'传递的值',
}
},
methods: {}
}
</script>
- 2 在子组件中通过props接收数据 格式:props:[“自定义接收名称”]
<template>
<view>
<h3>子组件</h3>
<h4>{{data}}</h4>
</view>
</template>
<script>
export default {
//这个props类似java中的super。super的作用是即调用父类的成员变量和方法。
props:['data'],
data() {
return { }
},
methods: {},
}
</script>
例子:
<body>
<div id="app">
<!-- 父组件,可以在引用子组件的时候, 通过 属性绑定(v-bind:) 的形式, 把需要传递给子组件的数据,以属性绑定的形式,传递到子组件内部,供子组件使用 -->
<com1 v-bind:parentmsg="msg"></com1>
</div>
<script>
// 创建 Vue 实例,得到 ViewModel
var vm = new Vue({
el: '#app',
data: {
msg: '123 啊-父组件中的数据'
},
methods: {},
components: {
// 结论:经过演示,发现,子组件中,默认无法访问到 父组件中的 data 上的数据 和 methods 中的方法
com1: {
data() { // 注意: 子组件中的 data 数据,并不是通过 父组件传递过来的,而是子组件自身私有的,比如: 子组件通过 Ajax ,请求回来的数据,都可以放到 data 身上;
// data 上的数据,都是可读可写的;
return { }
},
props: ['parentmsg'], // 把父组件传递过来的 parentmsg 属性,先在 props 数组中,定义一下,这样,才能使用这个数据
template: '<h1 @click="change">这是子组件 --- {{ parentmsg }}</h1>',
// 注意: 组件中的 所有 props 中的数据,都是通过 父组件传递给子组件的,传递名称和接收名称保持一致
// props 中的数据,都是只读的,无法重新赋值
methods: {
change() {
this.parentmsg = '被修改了'
}
}
}
}
});
</script>
</body>
1.2.2 父组件向子组件方法传递
- Vue中默认情况下子组件是不能访问父组件的方法的,因此为了让子组件可以访问父组件的数据,必须通过父组件传递
- 如何传递?
1 父组件中通过v-on传递方法
格式: v-on:自定义接收名称=”要传递的方法”
2 在子组件中自定义一个方法
3 在自定义方法中通过this.$eimt(‘自定义接收名称’) 触发传递过来的方法
1.2.3 子组件–>父组件传值
子传父,需要自己主动发送,也就是事件发射。
- 如何将子组件数据传递给父组件
前面说到,子组件可以调用父组件传递过来的方法,那么我们可以在调用父组件方法的时候给方法传递参数,(子组件传递给父组件方法的参数,就是子组件传递给父组件的数据)
父组件:
<template>
<view>
<h3>父组件: {{ message }}</h3>
<son @send-value="getSonValue"></son> // 给子组件绑定需要给父组件传的数值。
// getSonValue这个方法的作用就是接收子组件传过来的值 赋值给了方法的value,最后渲染出页面。
</view>
</template>
<script>
import son from '@/pages/son/son.vue'
export default {
components: { son },
data() {
return {
message:''
}
},
methods: {
getSonValue(value){
this.message = value;
}
}
}
</script>
- 子组件自定义方法通过
this.$emit('father_say', '子组件数据')
调用父组件方法
第一个参数:自定义的调用父组件方法的接收方法名,
第二个参数:传递给父组件方法的数据值。
在子组件的input输入框输入的文字是可以上传到父组件的。
<template>
<view>
<h3>子组件</h3>
<input type="text" v-model="sonValue" />
<button @click="send">传递</button>
//点击触发函数,是把上面输入框输入的数值传达父组件里面
</view>
</template>
<script>
export default {
data() {
return {
sonValue:''
}
},
methods: {
send(){
this.$emit('send-value',this.sonValue);
}
}
}
</script>
组件命名的注意点 (在Vue标签中使用带短横线,在js代码中是驼峰命名)
-
注册组件的时候使用的 ‘驼峰命名’,那么在使用时需要转换成 ‘短横线分隔命名’
例如:注册时 -> fatherComponent 使用时 -> father-component -
在传递参数的时候如果想使用 ‘驼峰命名’,那么就必须写 ‘短横线分隔命名’
例如:传递时 -> :parent-name = ‘name’ 接收时 -> props: [‘parentName’] -
在传递方法的时候不能使用 ‘驼峰命名’,只能用 ‘短横线分隔命名’
@parent-say = ‘say’ -> this.$emit(‘parent-say’)
1.2.4 兄弟之间的传递
- 1 兄弟之间传递数据需要借助于事件中心,通过事件中心传递数据 ,提供事件中心 var hub = new Vue()
- 2 监听与销毁事件
hub.$on('add-todo', addTodo)
hub.$off('add-todo')
- 3 触发事件
hub.$emit('add-todo', id)
- 传递数据方,通过一个事件触发hub.$emit(方法名,传递的数据)
- 接收数据方,通过mounted(){} 钩子中 触发hub.$on()方法名
- 销毁事件 通过hub.$off()方法名销毁之后无法进行传递数据
<div id="app">
<div>父组件</div>
<div>
<button @click='handle'>销毁事件</button>
</div>
<test-tom></test-tom>
<test-jerry></test-jerry>
</div>
<script type="text/javascript" src="js/vue.js"></script>
<script type="text/javascript">
/*
兄弟组件之间数据传递
*/
//1、 提供事件中心
var hub = new Vue();
Vue.component('test-tom', {
data: function(){
return {
num: 0
}
},
template: `
<div>
<div>TOM:{{num}}</div>
<div>
<button @click='handle'>点击</button>
</div>
</div>
`,
methods: {
handle: function(){
//2、传递数据方,通过一个事件触发hub.$emit(方法名,传递的数据) 触发兄弟组件的事件
hub.$emit('jerry-event', 2);
}
},
mounted: function() {
// 3、接收数据方,通过mounted(){} 钩子中 触发hub.$on(方法名
hub.$on('tom-event', (val) => {
this.num += val;
});
}
});
Vue.component('test-jerry', {
data: function(){
return {
num: 0
}
},
template: `
<div>
<div>JERRY:{{num}}</div>
<div>
<button @click='handle'>点击</button>
</div>
</div>
`,
methods: {
handle: function(){
//2、传递数据方,通过一个事件触发hub.$emit(方法名,传递的数据) 触发兄弟组件的事件
hub.$emit('tom-event', 1);
}
},
mounted: function() {
// 3、接收数据方,通过mounted(){} 钩子中 触发hub.$on()方法名
hub.$on('jerry-event', (val) => {
this.num += val;
});
}
});
var vm = new Vue({
el: '#app',
data: {
},
methods: {
handle: function(){
//4、销毁事件 通过hub.$off()方法名销毁之后无法进行传递数据
hub.$off('tom-event');
hub.$off('jerry-event');
}
}
});
</script>
2 动态组件
动态组件指的是动态切换组件的显示与隐藏
2.1 实现动态组件渲染
2.2 keep-alive 保持状态
默认情况下,切换动态组件时无法保持组件的状态。此时可以使用vue 内置的组件保持动态组件的状态。示例代码如下:
2.2.1 keep-alive 对应的生命周期函数
当组件被缓存时,会自动触发组件的deactivated生命周期函数。当组件被激活时,会自动触发组件的activated生命周期函数。
2.2.2 keep-alive 的include属性
include 属性用来指定:只有名称匹配的组件会被缓存。多个组件名之间使用英文的逗号分隔:
3 插槽
- 插槽(Slot)是vue 为组件的封装者提供的能力。允许开发者在封装组件时,把不确定的、希望由用户指定的部分定义为插槽。组件的最大特性就是复用性,而用好插槽能大大提高组件的可复用能力
- 默认情况下是不能在使用子组件的时候给子组件动态添加内容的,如果想给子组件动态添加内容,那么就必须使用插槽
3.1 组件插槽
3.1.1 组件插槽的作用
父组件向子组件传递内容
3.1.2 组件插槽的基本用法
- 1 插槽位置,在封装组件时,可以通过元素定义插槽,从而为用户预留内容占位符。示例代码如下:
Vue.component('alert-box', {
template: `
<div class="demo-alert-box">
<strong>ERROR:</strong>
<slot></slot>
</div>
`
})
- 2 插槽内容
<alert-box>lalalala</alert-box>
3.2 匿名插槽
<div id="app">
<!-- 这里的所有组件标签中嵌套的内容会替换掉slot 如果不传值 则使用 slot 中的默认值 -->
<alert-box>有bug发生</alert-box>
<alert-box>有一个警告</alert-box>
<alert-box></alert-box>
</div>
<script type="text/javascript">
/*
组件插槽:父组件向子组件传递内容
*/
Vue.component('alert-box', {
template: `
<div>
<strong>ERROR:</strong>
# 当组件渲染的时候,这个 <slot> 元素将会被替换为“组件标签中嵌套的内容”。
# 插槽内可以包含任何模板代码,包括 HTML
<slot>默认内容</slot>
</div>
`
});
var vm = new Vue({
el: '#app',
data: {
}
});
</script>
</body>
</html>
3.3 具名插槽
- 通过插槽的name属性给插槽指定名称
在使用时可以通过slot=‘name’ 方式,指定当前内容用于替换哪一个插槽 - 注意点:
如果没有指定要替换哪个插槽中的内容,则不会被替换
slot 属性在 Vue2.6 之后被废弃,Vue2.6 之后使用 v-slot 指令替代 slot 属性
举例
1 指定插槽名称
2 指定填充内容到哪个插槽
3.4 作用域插槽
- 作用域插槽就是带数据的插槽,就是让父组件在填充子组件插槽内容时也能使用子组件的数据
- 在封装组件的过程中,可以为预留的 插槽绑定props 数据,这种带有props 数据的 叫做“作用域插槽”。示例代码如下:
如何使用作用域插槽 - 在 slot 中通过
v-bind:数据名称='数据名称'
的方式暴露数据 - 在父组件中通过
<template slot-scope='作用域名称'>
接收数据 - 在父组件的
<template></template>
中通过 作用域名称.数据名称 方式使用数据
Vue 2.6 v-slot 指令
- 对于作用域插槽,v-slot 指令替换了 slot-scope,匿名插槽时:
v-slot:default=”abc”
,简写:#default=”abc”
也就是说,我们除了可以通过v-slot指令告诉Vue内容要填充到哪一个具名插槽中。 - 还可以通过 v-slot 指令告诉 Vue 如何接收作用域插槽暴露的数据
- 使用方法:
v-slot:插槽名称='作用域名称'
作用域插槽的应用场景
子组件提供数据,父组件决定如何渲染
3.5 解构插槽Prop
作用域插槽对外提供的数据对象,可以使用解构赋值简化数据的接收过程。示例代码如下: