一、实例生命周期钩子
每个 Vue 实例在被创建时都要经过一系列的初始化过程——例如,需要设置数据监听、编译模板、将实例挂载到 DOM 并在数据变化时更新 DOM 等。同时在这个过程中也会运行一些叫做生命周期钩子的函数,这给了用户在不同阶段添加自己的代码的机会。
不同的钩子函数在实例生命周期的不同阶段被调用,如有beforeCreate、created、beforeMount、mounted、beforeUpdate、updated 、beforeDestroy和 destroyed。生命周期钩子的 this 上下文指向调用它的 Vue 实例。
- beforeCreate:( 组件实例刚被创建,组件属性计算之前,$el data没有初始化)
- created:(组件实例创建完成,属性已绑定,data 数据初始化,但是 DOM 未生成,$el 属性不存在 )
- beforeMount:(模板编译/挂载之前,完成了 $el 和 data 初始化,但是这里的 el 还是 {{message}})
- mounted:(模板编译/挂载之后,el的位置变成message的值)这里有个this.el的位置变成message的值)这里有个this.nextTick方法,适用于想操控真实DOM(因为有时mounted 会出现渲染未完成就执行方法)
- beforeUpdate:(组件更新前)
- updated:(组件更新后)
- beforeDestroy:(组件销毁前)
- destroyed:(组件销毁后)
案例:
<!DOCTYPE html>
<html lang="en">
<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>life circle</title>
</head>
<body>
<div id="app"></div>
<script src="./lib/vue.js"></script>
<script>
var app = new Vue({
el: '#app',
created() {
console.group('created!!');
console.log(this.$el);
console.groupEnd();
},
beforeCreate() {
console.log('before create!!');
},
beforeMount() {
console.log('before mount!!');
},
mounted() {
console.group('mounted!!');
console.log(this.$el);
console.groupEnd();
},
beforeDestroy() {
console.log('beforeDestroy!!')
},
destroyed() {
console.log('destroyed!!')
}
})
</script>
</body>
</html>
生命周期图示:
vue生命周期官方文档
二、组件之间的传值
1、组件基础:
组件是可复用的 Vue 实例,且带有一个名字。我们可以在一个通过 new Vue 创建的 Vue 根实例中,把这个组件作为自定义元素来使用。
这里有一个 Vue 组件的示例:
<!DOCTYPE html>
<html lang="en">
<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>组件基础</title>
</head>
<body>
<div id="app">
<counter></counter>
</div>
<script src="./lib/vue.js"></script>
<script>
// 局部组件
const Counter = {
template: `<div>
<button @click="countPlusHandle">计数器-{{count}}</button>
</div>`,
data() {
return {
count: 1,
}
},
methods: {
countPlusHandle() {
this.count += 1;
}
},
created() {
console.log('init');
}
}
//全局组件
Vue.component('aaa', {
template: `<h5>这是一个组件</h5>`
})
var app = new Vue({
el: '#app',
components: {
Counter,
HelloWorld: {
template: `<h5>Hello World!</h5>`
}
}
})
</script>
</body>
</html>
- template:表示组件在页面中渲染的时候使用的html模板内容;每一个组件只能有一个根节点 。
- data:表示组件中使用的数据,组件中的数据必须是一个function,其返回值为data的值。
- 可以在组件内部继续使用vue的其他方法比如:lifeCircle,filter,computed 在一个vue实例中。
- 每一个组件都是独立存在的,它们都有自己一套固定的生命周期函数和data以及其他vue的可操作属性和方法。
- 在vue中使用组件时需要先引入,通过在实例中使用components节点进行设置(注册)在vue中如果使用驼峰命名,在上面写html代码时,需要使用‘-’把单词做分割。
2、组件分为局部组件和全局组件
局部组件:
在vue中使用组件需要先引入,通过在实例中使用components节点进行注册
全局组件:
全局组件定义好之后可以直接使用 不需要在components中做注册
参数一: 组件的名字
参数二 :一个对象 用来设置组件的属性
3、组件之间的传值
组件实例的作用域是孤立的。这意味着不能在子组件的模块中直接引用父组件的数据,必须使用特定的方法才能实现组件之间的数据传递。
1.父组件通过props属性向子组件传递数据
关键词:添加属性 props
案例:父组件的step的值传给子组件
<!DOCTYPE html>
<html lang="en">
<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>Document</title>
</head>
<body>
<div id="app">
<counter :step="2"></counter>
</div>
<script src="./lib/vue.js"></script>
<script>
Vue.component('counter',{
template:`<button @click="plusHandle">当前计数值: {{count}}</button>` ,
data(){
return {
count:0
}
},
methods: {
plusHandle(){
this.count+=this.step
}
},
props:['step']
})
var app=new Vue({
el:'#app',
})
</script>
</body>
</html>
2.子组件通过事件派发向父组件传递数据
关键词:$emit 定义方法
案例:子组件的count的值传给父组件
<html lang="en">
<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>子组件向父组件传值</title>
</head>
<body>
<div id="app">
<h5>当前计数器的值为:{{c}}</h5>
<counter @plusadd="plusAddHandle" :step="2"></counter>
</div>
<script src="./lib/vue.js"></script>
<script>
Vue.component('counter',{
template:`<button @click="plusHandle">当前计数值: {{count}}</button>` ,
data(){
return {
count:0,
}
},
methods: {
plusHandle(){
this.count+=this.step;
this.$emit('plusadd',this.count);
}
},
props:['step']
})
var app=new Vue({
el:'#app',
data:{
c:0,
},
methods: {
plusAddHandle(params){
this.c=params;
}
}
})
</script>
</body>
</html>
3.非父子组件之间的数据传递
关键词:$emit $on 空的vue对象
为了解决非父子组件之间的传值问题,引入事件总线
在vue中使用一个空白的Vue对象作为一个EventBus,用来做事件的监听和派发
<!DOCTYPE html>
<html lang="en">
<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>动态组件</title>
</head>
<body>
<div id="app">
<navbar @pagchange="changePageHandle"></navbar>
<component :is="currentPage"></component>
</div>
<script src="./lib/vue.js"></script>
<script>
const Nav = {
template: `
<div class="nav">
<ul>
<li><a :class="this.selIndex==0? 'cur': ''" @click="navClick(0, 'Home')">首页</a></li>
<li><a :class="this.selIndex==1? 'cur': ''" @click="navClick(1, 'List')">商品列表页</a></li>
<li><a :class="this.selIndex==2? 'cur': ''" @click="navClick(2, 'Cart')">购物车【{{cartCount}}】</a></li>
<li><a :class="this.selIndex==3? 'cur': ''" @click="navClick(3, 'Us')">关于我们</a></li>
</ul>
</div> `,
data() {
return {
selIndex: 0,
cartCount: 0
}
},
methods: {
navClick(index, page) {
this.$emit('pagchange', page); // 子组件传值到父组件
this.selIndex = index
}
},
created() {
this.$eventBus.$on('addToCartEvent', (id) => {
this.cartCount += 1;
})
}
}
const Home = {
template: `
<div>
<h1>我是首页</h1>
</div> `
}
const List = {
template: `
<div>
<div v-for="item in products" class="product">
<h3>{{item.name}}</h3>
<p>{{item.price}}<button @click="addToShopCart(item.id)">加入购物车</button></p>
</div>
</div> `,
data() {
return {
products: [{
id: 1,
name: 'iphone18',
price: 19999,
}, {
id: 2,
name: 'vivo 32',
price: 5200
}]
}
},
methods: {
addToShopCart(id) {
this.$eventBus.$emit('addToCartEvent', id); // 使用$eventBus对象派发一个事件
}
}
}
const Cart = {
template: `
<div>
<h1>我是购物车</h1>
</div> `
}
const Us = {
template: `
<div>
<h1>关于我们</h1>
</div> `
}
var $eventBus = new Vue(); // 使用一个空白的VUE实例作为中间媒介
Vue.prototype.$eventBus = $eventBus; // 此种定义的属性可以在实例中进行访问
var app = new Vue({
el: '#app',
components: {
navbar: Nav,
Home,
List,
Cart,
Us
},
data: {
currentPage: 'Home'
},
methods: {
changePageHandle(page) {
this.currentPage = page;
}
}
})
</script>
</body>
</html>
css:
<style>
html, body {
margin: 0;
padding: 0;
width: 100%;
height: 100%;
}
.nav {
background-color: deeppink;
padding: 1.5rem 1rem;
height: 60px;
}
.nav ul {
margin: 0;
padding: 0;
}
.nav ul li {
float: left;
margin: 1.5rem;
list-style: none;;
}
.nav a {
color: #fff;
cursor: pointer;
}
.nav a.cur {
color: greenyellow;
}
.product {
border-bottom: 1px solid #ccc;
padding: 0.2rem 0.5rem;
}
</style>