首先来看下面两张图,一张是完整流程图一张是介绍vue四大阶段的八大方法图
下面会详细介绍其使用情况。
所谓生命周期,就是vue从创建到销毁的整个过程。上面八大方法我们简称为钩子函数,作用是在特定的时间点执行特定的操作,就比如在组件创建完毕后,我们可以在created生命周期函数中发起Ajax请求,从而初始化data数据。
vue从创建->编译->显示->更新->销毁的过程。在每个过程中就会执行不同的方法。并且在上图我把整个生命周期分成了四个阶段。
new Vue() -Vue实例化
Init Events & Lifecycle 初始化事件和生命周期函数
beforeCreate生命周期钩子函数被执行
Init injections & reactivity -vue内部添加data和methods
created生命周期钩子函数被执行,实例创建
接下来是模板编译阶段,Has el option?目的是检查要挂载到index.html中的哪个地方。如果没有,调用$mount()方法,如果有继续检查template选项。第一个阶段到此结束。
首先template选线检查,有的话编译template返回render渲染函数。无的话编译el选项对应标签作为template。
虚拟DOM挂载成真实DOM之前。
beforeMount被执行。
Cretae vm and replace就是把虚拟DOM和渲染的数据一并挂载到真实DOM上
真实DOM挂载完毕
mounted被执行,也就是在mounted才可以调用到真实DOM
当data里数据改变时,更新DOM之前
首先进行个循环,执行beforeUpdate
Virtual DOM re-render 然后虚拟DOM重新渲染,打补丁。
执行updated函数
当有data数据改变,重复这个循环
当destory被调用,比如组件DOM被移除
执行beforeDestory
拆卸数据监视器,子组件和事件监听器
实例销毁后,最后触发一个钩子函数
destoryed生命周期钩子函数被执行
axios是一个专门用于发送Ajax请求的库。特点是
在mounted生命周期中,获取原生DOM有两种方法,通过id或者ref。
<p>1. 获取原生DOM元素</p>
<h1 id="h" ref="myH">我是一个孤独可怜又能吃的h1</h1>
mounted(){
console.log(document.getElementById("h")); // h1
console.log(this.$refs.myH); // h1
}
如果我们定义组件的话,那么我们可以调用组件内的所有方法,通过this.$refs.组件名
<p>2. 获取组件对象 - 可调用组件内一切</p>
<Demo ref="de"></Demo>
components: {
Demo
},
mounted(){
let demoObj = this.$refs.de;
demoObj.fn()
}
之后我们可以调用组件里的方法和属性。
<p>3. vue更新DOM是异步的</p>
<p ref="myP">{{ count }}</p>
<button @click="btn">点击count+1, 马上提取p标签内容</button>
methods: {
btn(){
this.count++; // vue监测数据更新, 开启一个DOM更新队列(异步任务)
console.log(this.$refs.myP.innerHTML); // 0
}
}
因为vue更新DOM是异步的,更新语句会被放入到任务队列,最后再去执行。
this.$nextTick(() => {
console.log(this.$refs.myP.innerHTML); // 1
})
因此调用nextTick方法可以访问到更新后的DOM,当然我们也可以在updated生命周期钩子函数中。需要注意的是$nextTick返回的是一个promise对象。以下为一个简单的输入框聚焦案例
<template>
<div>
<input ref="myInp" type="text" placeholder="这是一个输入框" v-if="isShow">
<button v-else @click="btn">点击我进行搜索</button>
</div>
</template>
<script>
// 目标: 点按钮(消失) - 输入框出现并聚焦
// 1. 获取到输入框
// 2. 输入框调用事件方法focus()达到聚焦行为
export default {
data(){
return {
isShow: false
}
},
methods: {
async btn(){
this.isShow = true;
// this.$refs.myInp.focus()
// 原因: data变化更新DOM是异步的
// 输入框还没有挂载到真实DOM上
// 解决:
// this.$nextTick(() => {
// this.$refs.myInp.focus()
// })
// 扩展: await取代回调函数
// $nextTick()原地返回Promise对象
await this.$nextTick()
this.$refs.myInp.focus()
}
}
}
</script>
<style>
</style>
把上述案例分为四个模块,头部模块MyHeader底部模块MyFooter中间模块MyGoods数量模块MyCount,其中最复杂的部分在于全选框的设计与实现。
<template>
<div>
<MyHeader title="购物车案例"></MyHeader>
<div class="main">
<MyGoods v-for="obj in list" :key="obj.id"
:gObj="obj"
></MyGoods>
</div>
<MyFooter @changeAll="allFn" :arr="list"></MyFooter>
</div>
</template>
其中我们需要注意的是需要在created方法中调用axios函数,引入后台数据,并且把数据复制给list空数组。
<template>
<div class="my-header" :style="{backgroundColor: background, color}">{{ title }}</div>
</template>
props: {
background: String, // 外部插入此变量的值, 必须是字符串类型, 否则报错
color: {
type: String, // 约束color值的类型
default: "#fff" // color变量默认值(外部不给 我color传值, 使用默认值)
},
title: {
type: String,
required: true // 必须传入此变量的值
}
}
<template>
<div class="my-goods-item">
<div class="left">
<div class="custom-control custom-checkbox">
<!-- *重要:
每个对象和组件都是独立的
对象里的goods_state关联自己对应商品的复选框
-->
<!-- bug:
循环的所有label的for都是input, id也都是input - 默认只有第一个生效
解决: 每次对象里的id值(1, 2), 分别给id和for使用即可区分
-->
<input type="checkbox" class="custom-control-input" :id="gObj.id"
v-model="gObj.goods_state"
>
<label class="custom-control-label" :for="gObj.id">
<img :src="gObj.goods_img" alt="">
</label>
</div>
</div>
<div class="right">
<div class="top">{{ gObj.goods_name }}</div>
<div class="bottom">
<span class="price">¥ {{ gObj.goods_price }}</span>
<span>
<MyCount :obj="gObj"></MyCount>
</span>
</div>
</div>
</div>
</template>
> <template>
<div class="my-counter">
<button type="button" class="btn btn-light" :disabled="obj.goods_count === 1" @click="obj.goods_count > 1 && obj.goods_count--">-</button>
<input type="number" class="form-control inp" v-model.number="obj.goods_count">
<button type="button" class="btn btn-light" @click="obj.goods_count++">+</button>
</div>
</template>
<div class="custom-control custom-checkbox">
<input type="checkbox" class="custom-control-input" id="footerCheck" v-model="isAll">
<label class="custom-control-label" for="footerCheck">全选</label>
</div>
上面为全选框的逻辑代码,跟label进行关联,并且设置v-model为isAll这个变量
并且isAll是属于计算属性
computed: {
isAll: {
set(val){ // val就是关联表单的值(true/false)
this.$emit('changeAll', val)
},
get(){
// 查找小选框关联的属性有没有不符合勾选的条件
// 直接原地false
return this.arr.every(obj => obj.goods_state === true)
}
},
}
通过复选框本身的属性,通过set函数,传递给主组件
<MyFooter @changeAll="allFn" :arr="list"></MyFooter>
methods: {
allFn(bool){
this.list.forEach(obj => obj.goods_state = bool)
// 把MyFooter内的全选状态true/false同步给所有小选框的关联属性上
}
}
并且在methods函数中声明这个方法,逻辑代码为复选框的选中状态与其余所有小选框的选中状态一致。并且我们注意的是小选框的选中状态是用obj.goods_state来进行双向绑定的。以上便是整个小项目的业务逻辑流程。