8. 生命周期钩子
_1. 生命周期
每个 Vue 实例在被创建时都要经过一系列的初始化过程 :创建实例,装载模板,渲染模板等等。Vue为生命周期中的每个状态都设置了钩子函数(监听函数)。
每当Vue实例处于不同的生命周期时,对应的函数就会被触发调用。
生命周期图示:
常用的生命周期方法
-
created()
/mounted()
: 发送 ajax 请求, 启动定时器等异步任务 -
beforeDestory()
: 做收尾工作, 如: 清除定时器
示例
<template>
<div class="demo1">
<p><button type="button" @click="destroyVue">
销毁vue</button></p>
<p>现在时间:{{now}}</p>
</div>
</template>
<script>
export default {
name: "demo1",
data() {
return {
now: "",
intervalId: ""
};
},
methods: {
destroyVue(){
this.$destroy()
}
},
created() {
this.intervalId = setInterval(() => {
let d = new Date();
this.now = d.getHours() + ":" + d.getMinutes() + ":" + d.getSeconds();
console.log(this.now);
}, 1000);
},
beforeDestroy(){
// 如果不清除定时器,vue实例销毁后定时器仍然会继续运行
clearInterval(this.intervalId);
}
};
</script>
_2. 钩子函数
可以在Vue中定义每个时期的构造函数,比如在刚才的案例中定义一个created
函数,完成数据初始化
9. 指令
指令 (Directives) 是带有 v- 前缀的特殊特性。指令特性的预期值是: 单个 JavaScript 表达式 。指令的职责是,当表达式的值改变时,将其产生的连带影响,响应式地作用于 DOM。
_1. 差值表达式
{{表达式}}
说明:
-
该表达式支持JS语法,可以调用js内置函数(必须有返回值)
-
表达式必须有返回结果。例如
1 + 1
,没有结果的表达式不允许使用,如:
var a = 1 + 1;
-
可以直接获取Vue实例中定义的数据或函数
_2. v-model
v-text
和v-html
可以看做是单向绑定,数据影响了视图渲染,但是反过来就不行。
v-model
是双向绑定,视图(View)和模型(Model)之间会互相
影响。
v-model的可使用元素有:
-
input
-
select
-
textarea
-
checkbox
-
radio
-
components
(Vue中的自定义组件)
基本上除了最后一项,其它都是表单的输入项
1 {{表达式}}
_3. v-on
v-on
指令用于给页面元素绑定事件,可以用@简写
_3.1 基本语法
语法
v-on:事件名="js片段或函数名"
_3.2 事件修饰符
在事件处理程序中调用 event.preventDefault()
或event.stopPropagation()
是非常常见的需求。尽管我们可以在方法中轻松实现这点,但更好的方式是:方法只有纯粹的数据逻辑,而不是去处理 DOM 事件细节。
Vue.js
为 v-on
提供了 事件修饰符 。之前提过,修饰符是由点开头的指令后缀来表示的。
-
.stop
:阻止事件冒泡(注意一定要放在需要阻止冒泡的位置上)
-
.prevent
:阻止默认事件发生 -
.capture
:使用事件捕获模式 -
.self
:只有元素自身触发事件才执行。(冒泡或捕获的都不执行)
-
.once
:只执行一次
_3.3 按键修饰符
Vue 允许为v-on
在监听键盘事件时添加按键修饰符
<input @keyup.enter="incr" v-model="num">
全部的按键别名:
.enter
.tab
.delete
(捕获“删除”和“退格”键).esc
.space
.up
.down
.left
.right
还可以用如下修饰符来实现仅在按下相应按键时才触发鼠标或键盘
事件的监听器。
.ctrl
.ctrl
.ctrl
例:
<!-- ctrl + C -->
<input @keyup.ctrl.67="copy">
_4. v-for
v-for
用于循环遍历数据渲染页面
_4.1 遍历数组
<template>
<div class="demo1">
<ul>
<!--
语法:v-for="item in items"
- items:要遍历的数组
- item: 迭代得到的数组元素的别名
- index:迭代得到当前元素的索引号
-->
<li @click="getIndex(index)" v-for="(emp,index) in emps" :key="index">
{{index+1}}-{{emp.name}}-{{emp.age}}
</li>
</ul>
</div>
</template>
<script>
export default {
name: "Demo1",
data(){
return{
emps:[
{name:'张三', age: 25},
{name:'李四', age: 26},
{name:'王五', age: 27}
]
}
},
methods:{
getIndex(index){
console.log(index);
}
}
};
</script>
更新参考
add(index){
console.log(index);
this.emps.splice(index+1,0,{name:'张三',age:50});
},
update(index){
console.log(index);
this.emps.splice(index,1,{name:'李四',age:28});
},
del(index){
console.log(index);
this.emps.splices(index,1);
}
_4.2 遍历对象
语法和遍历数组类似
v-for="value in object"
v-for="(value,key) in object"
v-for="(value,key,index) in object"
<template>
<div class="demo1">
<ul>
<li v-for="(value,key,index) in emp">
{{index}}-{{key}}-{{value}}
</li>
</ul>
</div>
</template>
<script>
export default {
name: "Demo1",
data(){
return{
emp:{
name:'张三', sex:'男', age: 25, sal: 8000
}
}
}
};
</script>
_5. v-if
基本语法
v-if="布尔表达式"
v-if-else="布尔表达式" // v-else-if 也必须紧跟在带
v-if 或者 v-else-if 的元素之后
v-else // v-else 元素必须紧跟在带 v-
if 或者 v-else-if 的元素之后
示例
<template>
<div class="demo1">
<button type="button" @click="toggle">
<span v-if="isShow">隐藏</span>
<span v-else>显示</span>
</button>
<h1 v-if="isShow">hello vue</h1>
</div>
</template>
<script>
export default {
name: "Demo1",
data(){
return{
isShow: true
}
},
methods:{
toggle(){
this.isShow = !this.isShow;
}
}
};
</script>
_6. v-show
v-show
用于根据条件展示元素,v-show
只是简单地切换元素的 CSS 属性 display
<template>
<div class="demo1">
<button type="button" @click="toggle">
<span v-show="!isShow">显示</span>
<span v-show="isShow">隐藏</span>
</button>
<h1 v-show="isShow">hello vue</h1>
</div>
</template>
<script>
export default {
name: "Demo1",
data(){
return{
isShow: true
}
},
methods:{
toggle(){
this.isShow = !this.isShow;
}
}
};
</script>
_7. v-bind
因为插值表达式不能用在属性的值中, 可以使用v-bind
指令绑定class样式,v-bind指
令可以简写为冒号:
示例- 1
<template>
<div class="demo1">
<div :class="divClass"></div>
</div>
</template>
<script>
export default {
name: "Demo1",
data(){
return{
divClass: "red"
}
}
};
</script>
<style scoped>
.red{
width:100px;
height: 100px;
background-color: red;
}
</style>
示例- 2
注意这里的绑定值在{}
中,red
是样式名,isActive
是布尔值,决定该样式是否显示
<template>
<div class="demo1">
<div :class="{red:isActive}"></div>
</div>
</template>
<script>
export default {
name: "Demo1",
data(){
return{
isActive: true
}
}
};
</script>
<style scoped>
.red{
width:100px;
height: 100px;
background-color: red;
}
</style>
_8. 计算属性
使用计算属性可以替代复杂的表达式
示例- 1
使用计算属性前:
<template>
<div class="demo1">
今天是{{datetime.getFullYear()}}-
{{datetime.getMonth()+1}}-{{datetime.getDate()}}
</div>
</template>
<script>
export default {
name: "Demo1",
data(){
return{
datetime: new Date()
}
}
};
</script>
使用计算属性后:
<template>
<div class="demo1">
今天是{{getToday}}
</div>
</template>
<script>
export default {
name: "Demo1",
data(){
return{
datetime: new Date()
}
},
computed:{
getToday(){
let d = this.datetime;
return d.getFullYear()+'-'+(d.getMonth()+1)+'-'+d.getDate();
}
}
};
</script>
示例- 2
计算属性默认只有 getter
,不过在需要时你也可以提供一个 setter
:
<template>
<div class="demo1">
<p>FirstName:<input type="text" vmodel="firstName"></p>
<p>LastName:<input type="text" vmodel="lastName"></p>
<p>FullName:<input type="text" vmodel="fullName"></p>
</div>
</template>
<script>
export default {
name: "demo1",
data(){
return{
firstName:"",
lastName:""
}
},
computed:{
fullName:{
get(){
return this.firstName + " " + this.lastName;
},
set(val){
let names = val.split(" ");
this.firstName = names[0];
this.lastName = names[1];
}
}
}
};
</script>
_9. watch
watch用来监控一个值的变化,从而做出相应的反应
示例
<template>
<div class="demo1">
<input type="text" v-model="name">
</div>
</template>
<script>
export default {
name: "Demo1",
data(){
return{
name:'张三疯'
}
},
watch:{
name(newVal,oldVal){
console.log(oldVal + '改成了' + newVal)
}
}
};
</script>
_10. 表单输入绑定
<template>
<div class="demo1">
<form @submit.prevent="handleSubmit">
<span>用户名:</span>
<input type="text" v-model="user.username">
<br>
<span>密码:</span>
<input type="password" v-model="user.pwd">
<br>
<span>性别:</span>
<input type="radio" id="female" value="F" v-model="user.sex">
<label for="female">女</label>
<input type="radio" id="male" value="M" vmodel="user.sex">
<label for="male">男</label>
<br>
<span>爱好:</span>
<input type="checkbox" id="basket" value="basketball" v-model="user.hobby">
<label for="basket">篮球</label>
<input type="checkbox" id="foot" value="football" v-model="user.hobby">
<label for="foot">足球</label>
<input type="checkbox" id="pingpang" value="pingpang" v-model="user.hobby">
<label for="pingpang">乒乓</label>
<br>
<span>城市:</span>
<select v-model="user.cityId">
<option value>未选择</option>
<option v-for="city in allCitys" :key="city.id" :value="city.id">{{ city.name }}</option>
</select>
<br>
<span>介绍:</span>
<textarea v-model="user.desc" rows="10"></textarea>
<br>
<br>
<input type="submit" value="注册">
</form>
</div>
</template>
<script>
export default {
name: "demo1",
data() {
return {
user: {
username: "",
pwd: "",
sex: "F",
hobby: [],
cityId: "",
desc: ""
},
allCitys: [
{ id: 1, name: "长沙" },
{ id: 2, name: "武汉" },
{ id: 3, name: "深圳" }
]
};
},
methods: {
handleSubmit(event) {
alert(JSON.stringify(this.user));
}
}
};
</script>
10. 组件化
组件 (Component
) 是 Vue.js 最强大的功能之一。组件可以扩展HTML 元素,封装可重用的代码。
_1. 定义全局组件
示例
1. 创建组件相关目录及文件,参考
2. webpack首先会加载main.js,所以我们在main.js里面引入
import counter from './components/counter'
Vue.use(counter); // 注入组件
3. 在Counter.vue里面定义自己的组件模板
<template>
<div>
<button type="button" @click="count++">点击次数{{ count }}</button>
</div>
</template>
<script>
export default {
name: "Counter.vue",
data(){
return{
count:0
}
}
};
</script>
<style scoped></style>
4. 在counter/index.js文件里面添加install方法及导出组件
import myCounter from './Counter'
const Counter = {
install(Vue){
// 通过Vue的component方法来定义一个全局组件
Vue.component('Counter',myCounter);
}
}
// 导出组件
export default Counter;
5. 在其他组件中使用该组件
<template>
<div class="demo1">
<counter></counter>
<counter></counter>
<counter></counter>
</div>
</template>
<script>
</script>
<style scoped>
</style>
6. 测试
_2. 局部组件
全局注册,就意味着即便你不使用这个组件,它依然会随着Vue的加载而加载。那么,对于一些并不频繁使用的组件,可以注册为局部组件。
局部组件只需要的地方直接引入,比如上例中,需要删除main.js中的相关代码,然后在Demo1.vue中直接引入
<template>
<div class="demo1">
<counter></counter>
<counter></counter>
<counter></counter>
</div>
</template>
<script>
import counter from './counter/Counter.vue';
export default {
components:{counter}
}
</script>
<style scoped>
</style>
_3. 组件通信
通常一个单页应用会以一棵嵌套的组件树的形式来组织
【X图片】
-
页面首先分成了顶部导航、内容区、侧边栏三部分
-
内容区又分为上下两个组件
-
侧边栏中又包含了 3 个子组件
各个组件之间以嵌套的关系组合在一起,那么这个时候不可避免的会有组件间通信的需求。
_3.1 父向子传递props
父组件使用子组件,同时传递属性
示例
-
创建组件Component1.vue
<template> <div>{{ name }}是个好同志</div> </template> <script> export default { name: "Component1", props:['name'] }; </script>
-
父组件引用
<template> <div class="demo1"> <component1 name="张三疯"></component1> </div> </template> <script> import component1 from './Component1'; export default { components:{component1} } </script
_3.2 子向父通信
子组件接收到父组件属性后,默认是不允许修改的,所以对父组件属性的操作一定是放在父组件。
示例
-
创建组件Component2.vue
<template> <div> <h1>{{num}}</h1> <button type="button" @click="add">+ </button> <button type="button" @click="sub">- </button> </div> </template> <script> export default { name: "Component2", props: ["num"], methods: { add() { // this.$emit函数,用来调用父组件绑定的函数 this.$emit('add'); }, sub() { this.$emit('sub'); } } }; </script>
-
父组件
<template> <div class="demo1"> <component2 :num="count" @add="incr" @sub="decr"></component2> </div> </template> <script> import component2 from "./Component2"; export default { components: { component2 }, data(){ return{ count: 50 } }, methods:{ incr(){ this.count++; }, decr(){ this.count--; } } }; </script>