提示:学习vue2的第三天,笔记记录:生命周期,组件(注册,传值)
目录
一、生命周期
在 Vue.js 2 中,每个组件实例都具有生命周期钩子函数,允许你在组件的不同阶段执行特定的操作。
-
beforeCreate:在实例初始化之后,数据观测(data observer) 和事件配置之前被调用。在这个阶段,实例的选项属性还未被初始化。
-
created:在实例创建完成后被立即调用。在这个阶段,实例已经完成了数据观测(data observer) 和属性的初始化,但是挂载还未开始,$el 属性还不存在。
-
beforeMount:在挂载开始之前被调用,相关的 render 函数首次被调用。
-
mounted:在挂载完成后被调用,el 被新创建的 vm.$el 替换,并挂载到实例上去之后调用该钩子函数。如果根实例挂载到了一个文档内的元素上,当 mounted 被调用时 vm.$el 也在文档内。
-
beforeUpdate:数据更新时调用,发生在虚拟 DOM 重新渲染和打补丁之前。
-
updated:由于数据更改导致的虚拟 DOM 重新渲染和打补丁后调用。
-
beforeDestroy:在实例销毁之前调用。在这个阶段,实例仍然完全可用。
-
destroyed:在实例销毁之后调用。调用后,所有的事件监听器被移除,所有的子实例也被销毁
<!DOCTYPE html>
<html lang="zh-cn">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Vue</title>
</head>
<body>
<div id="app">
<h2>数字: <span>{{num}}</span></h2>
<div ref="box">hello world</div>
</div>
<script src="./libs/vue@2.7.16/vue.js"></script>
<script>
//禁止控制台输出日志信息
Vue.config.productionTip = false;
const vm = new Vue({
//挂载容器
el: "#app",
// 设置数据
data: {
num: 0,
timer: null
},
// 生命周期: 程序从创建到销毁的过程。注意回调函数先后顺序....
// 钩子函数: 指的是回调函数,生命周期的钩子函数(在vue@2中有8个生命周期的钩子函数)
// 1) 创建阶段
// 创建之前执行的钩子函数
beforeCreate(){
console.log("1:",this.num);//undefine
},
// 创建后执行的钩子函数 (初始化数据)
created(){
console.log("2:",this.num);//0
console.log(this.$refs.box);// undefined
// 初始化
//创建一个定时器观察update(数据更新)钩子函数的执行
this.timer = setInterval(()=> {
if(this.num == 5) {
clearInterval(this.timer);
return ;
}
++ this.num;
},1000)
},
// 2)挂载阶段
// 挂载之前执行的钩子函数
beforeMount(){
console.log("3:",this.num);//0
console.log(this.$refs.box);// undefined
},
// 挂载后执行的钩子函数 (DOM操作)
mounted(){
console.log("4:",this.num);//0
console.log(this.$refs.box);// <div>hello world</div>
// 为了让dom操作更加稳定,可以在此处执行$nextTick回调函数
// 为了减少报错,所以调用$nextTick回调函数
仅在整个视图都被渲染之后才会运行的代码
this.$nextTick(()=>{
// 判断程序中是否存在这个标签
if(this.$refs.box){
// 进行dom操作
this.$refs.box.style.color = "red";
this.$refs.box.style.fontSize = "20px";
}
})
},
// 3)更新阶段
// 要数据发生改变才执行的回调函数
beforeUpdate(){
console.log("5:",this.num);//1 ~ 5
},
updated(){
console.log("6:",this.num);//1 ~ 5
},
// 4)销毁阶段
// 程序不存在了,或者以后卸载组件,(显示|隐藏 v-if="false")
beforeDestroy(){
console.log("7:",this.num);// 0
},
destroyed(){
console.log("8:",this.num);// 0
}
})
// 为了看得见销毁阶段的钩子函数被执行,可以调用销毁方法
// vm.$destroy();
</script>
</body>
</html>
二、组件
组件是可复用的 Vue 实例,它可以封装特定的功能和界面,并可以在应用程序中多次使用。组件使得代码更加模块化、可维护,并且提高了代码的复用性。
1.注册组件
组件只有被注册才能使用,在哪注册就只能在哪里使用
1)全局注册
通过Vue中的component方法注册组件,此方法注册的组件在任何地方都能使用
<!DOCTYPE html>
<html lang="zh-cn">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Vue</title>
<style></style>
</head>
<body>
<div id="app">
<el-button></el-button>
</div>
<hr>
<div id="app2">
<el-button></el-button>
</div>
<script src="./libs/vue@2.7.16/vue.js"></script>
<script>
// 一) 全局组件
// 1. 定义按钮组件 (template选项在vue2版本中必须有根节点)
const ElButton = {
// 设置数据
data() {
return {
message: "这是一个按钮123"
}
},
// 渲染模板
template: `
<div>
<div>{{message}}</div>
<button @click="foo" style="padding: 10px 20px; border: 0; background-color: skyblue; color: white;">
点击按钮
</button>
</div>
`,
// 设置方法
methods: {
// 提示
foo() {
alert("您好,触发事件。。。")
}
}
}
// 2. 注册组件(此处表示全局注册)
Vue.component("el-button", ElButton);
//禁止控制台输出日志信息
Vue.config.productionTip = false;
// 创建vue实例
new Vue({
//挂载容器
el: "#app",
})
new Vue({
//挂载容器
el: "#app2",
})
</script>
</body>
</html>
2)局部注册
通过Vue实例或某组件中的components对象注册组件,此方法注册的组件只能在注册的地方使用
<!DOCTYPE html>
<html lang="zh-cn">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Vue</title>
<style></style>
</head>
<body>
<div id="app">
<!-- <ElSearch></ElSearch> 错误的,驼峰注册,(-)使用 -->
<el-search></el-search>
</div>
<hr>
<div id="app2">
<!-- <el-search></el-search> 错误的,该组件没有在app2中注册 -->
</div>
<script src="./libs/vue@2.7.16/vue.js"></script>
<script>
//禁止控制台输出日志信息
Vue.config.productionTip = false;
// 创建vue实例
new Vue({
//挂载容器
el: "#app",
// 注册组件(此处表示局部注册)
components: {
// 此处注册组件名称采用驼峰命名“ElSearch”,在使用的时候需要改成 “el-search”
// "el-search": {},
"ElSearch": {
data() {
return {
}
},
template: `
<div>
<input type="text" placeholder="请输入关键字" style="width: 90%;height: 40px;">
</div>
`
},
}
})
new Vue({
//挂载容器
el: "#app2",
})
</script>
</body>
</html>
注册组件注意事项:
1) 采用驼峰命名法注册组件名称时“ElSearch”,使用组件时需要改用短横线“el-search”
2) 不能使用html文档内置的标签名称作为组件名称例如: div header footer nav
3) 组件中的data一个函数,并且返回一个对象
4) template选项需要有根节点(在vue@2版本)
5) 像属性计算、侦听,过滤,生命周期的钩子和vue实例的写法一样
2.组件传值
在vue中允许组件嵌套组件但不宜嵌套过多,其中就涉及到不同组件间的数据交互的问题。
<!DOCTYPE html>
<html lang="zh-cn">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Vue</title>
<style></style>
</head>
<body>
<div id="app">
<element-parent></element-parent>
<!-- <element-child></element-child> 错误的 -->
</div>
<script src="./libs/vue@2.7.16/vue.js"></script>
<script>
//禁止控制台输出日志信息
Vue.config.productionTip = false;
//定义孙组件
const ElementGrandson = {
template: `
<div>
<h4>孙组件</h4>
</div>
`,
}
// 定义子组件
const ElementChild = {
template: `
<div>
<h3>子组件</h3>
<element-grandson></element-grandson>
</div>
`,
components: {
ElementGrandson
}
}
// 定义父组件
const ElementParent = {
template: `
<div>
<h2>父组件</h2>
<element-child></element-child>
</div>
`,
// 在父组件中注册子组件
components: {
ElementChild
}
}
// vue实例对象
new Vue({
//挂载容器
el: "#app",
// 在vue实例中注册父组件
components: {
ElementParent
}
})
</script>
</body>
</html>
1)父传子
因为组件之间不可以直接通信,所以父组件传值给子组件,
需要在子组件中自定义属性,在props这个选项中自定义
在父组件的template模板中使用子组件时动态绑定属性
例如: <element-child v-bind:xxx="message"></element-child>
<!DOCTYPE html>
<html lang="zh-cn">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Vue</title>
<style></style>
</head>
<body>
<div id="app">
<element-parent></element-parent>
<!-- <element-child></element-child> 错误的 -->
</div>
<script src="./libs/vue@2.7.16/vue.js"></script>
<script>
//禁止控制台输出日志信息
Vue.config.productionTip = false;
// 定义子组件
const ElementChild = {
// 父组件传值给子组件,需要在子组件中自定义属性
// 通过在props选项中自定义属性接收父组件传递的数据
// props: ['xxx'],
props: {
xxx: {
default: "默认值",
type: [String,Number]
},
xxx2:{
default:'',
type: [String,Number]
}
},
template: `
<div>
<h3>子组件</h3>
<p>{{xxx}}</p>
<p>{{xxx2}}</p>
</div>
`
}
// 定义父组件
const ElementParent = {
// 设置数据
data(){
return {
message: "这是父组件的数据",
message2: "这是父组件的第二条数据"
}
},
template: `
<div>
<h2>父组件</h2>
<element-child v-bind:xxx="message"></element-child>
<element-child :xxx="100"></element-child>
<element-child :xxx="message" :xxx2="message2"></element-child>
<element-child></element-child>
</div>
`,
// 在父组件中注册子组件
components: {
ElementChild
}
}
// vue实例对象
new Vue({
//挂载容器
el: "#app",
// 在vue实例中注册父组件
components: {
ElementParent
}
})
</script>
</body>
</html>
2)子传父
传值过程中,需要在子组件中自定义事件(行为),在methods中写方法, this.$emit("child-num", {cnum: this.num})
(调用this.$emit('第一个参数是在父组件中@on监听的事件名','第二个参数(在父组件中接受子组件参数的参数名):第三个参数(需要向父组件传递的数据)
在父组件的模板中,定义一个方法用于接受子组件的数据并在子组件中绑定监听事件,例如: <element-child @child-num="jieshou"></element-child>就是也需要在父组件中写方法,接收子组件的数据。
<!DOCTYPE html>
<html lang="zh-cn">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Vue</title>
<style></style>
</head>
<body>
<div id="app">
<element-parent></element-parent>
</div>
<script src="./libs/vue@2.7.16/vue.js"></script>
<script>
// 子组件传值给父组件属于负向传值,需要使用事件行为方可传递数据给父组件
//禁止控制台输出日志信息
Vue.config.productionTip = false;
// 定义子组件
const ElementChild = {
data(){
return {
num: 100
}
},
template: `
<div>
<h3>子组件</h3>
<button @click="add">发送数据</button>
</div>
`,
methods: {
add(){
// 自定义行为
// 参数1:在父组件中@on监听的事件名, 第二个参数(在父组件中接受子组件参数的参数名):第三个参数(需要向父组件传递的数据)
this.num --;
this.$emit("child-num", {cnum: this.num})
}
}
}
// 定义父组件
const ElementParent = {
data(){
return {
num: 0
}
},
template: `
<div>
<h2>父组件</h2>
<p v-if="num!=0">{{num}}</p>
<element-child @child-num="jieshou"></element-child>
</div>
`,
// 在父组件中注册子组件
components: {
ElementChild
},
// 编写方法
methods: {
// 自定义方法接受子组件的数据
jieshou(data){
// console.log(data)
// 获取子组件传递的数据
this.num = data.cnum;
}
}
}
// vue实例对象
new Vue({
//挂载容器
el: "#app",
// 在vue实例中注册父组件
components: {
ElementParent
}
})
</script>
</body>
</html>
3)兄弟传值
兄弟传值需要定义一个新的vue实例或通过同一个父组件来共享事件。通过$emit和$on来传数据。
<!DOCTYPE html>
<html lang="zh-cn">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Vue</title>
<style>
#app > div {
padding: 20px;
border: 1px solid #000;
margin-top: 10px;
}
</style>
</head>
<body>
<div id="app">
<comp-a></comp-a>
<comp-b></comp-b>
</div>
<script src="../libs/vue@2.7.16/vue.js"></script>
<script>
// 创建vue实例作为通信的桥梁(这种有响应式)
const bridge = new Vue();
// 并列关系组件
const CompA = {
template: `
<div>
<h3>组件A</h3>
<button @click="senda">数字自增</button>
</div>
`,
data(){
return {
num: 100
}
},
methods: {
senda(){
this.num ++;
let value = this.num;
bridge.$emit('xxx', value)
},
},
created(){
// 发送数据
setTimeout(()=>{
this.senda();
},0)
}
}
const CompB = {
template: `
<div>
<h3>组件B</h3>
<p>{{num}}</p>
</div>
`,
data(){
return {
num: 0
}
},
created(){
// 监听组件A的事件且接收组件A的数据
bridge.$on("xxx",(value)=>{
this.num = value;
})
}
}
//禁止控制台输出日志信息
Vue.config.productionTip = false;
new Vue({
//挂载容器
el: "#app",
components: { CompA , CompB },
})
</script>
</body>
</html>