插值与指令
<!-- {{ age }} 插值表达式 可写任意JS表达式 -->
<p>今年芳龄 {{ age }}</p>
<!--
v-on:click="onBtnClick" Vue指令
v-xxx指令名
冒号后是指令参数(可选)
"引号内可写任意JS表达式"
--------------------
!!!特别注意引号内是JS表达式而非普通字符串!!!
!!!特别注意引号内是JS表达式而非普通字符串!!!
!!!特别注意引号内是JS表达式而非普通字符串!!!
--------------------
-->
<button v-on:click="onBtnClick">SendMsg</button>
基础指令
v-if
renderHim数据成立时才渲染DOM结构- 否则不渲染,即DOM压根不存在
<span v-if="renderHim"> 我渲染与否由renderHim控制 </span>
v-show
showHim数据成立时才显示DOM结构- 否则
display:none,DOM还在但不可见
<span v-show="showHim"> 我显示与否由showHim控制 </span>
v-for
- 这里
arr数组有几个元素,就在ul中渲染出几个li元素 - index不需要时可以写作
<li v-for="item in arr">{{ item }}</li>
<ul>
<li v-for="(item, index) in arr">{{ index }}: {{ item }}</li>
</ul>
v-bind
- 此处input标签的value属性动态地由数据
count所控制 count变化,则input的value属性就随之变化,此之谓 数据驱动视图- 这一规则可以推广到任意标签的任意属性,包括自定义属性
v-bind:属性名="数据项"可以简写为:属性名="数据项"
<input type="text" v-bind:value="count">
<input type="text" :value="count">
v-on
- 此处button标签的click事件动态地由方法
subCount所处理 - 这一规则可以推广到任意标签的任意事件,包括自定义事件
v-on:事件名="事件处理函数"可以简写为@事件名="事件处理函数"
<button v-on:click="subCount">-</button>
<button @click="addCount">+</button>
export default {
data() {
return {
...
};
},
methods: {
subCount() {
this.count--;
},
addCount() {
this.count++;
}
},
components: { RouterLink }
}
v-model
- 此处一方面checkbox的
checked属性值由数据allSelected控制,当数据主动变化时,元素属性也随之变化; - 另一方面,当用户在表单元素身上交互时,会反向去修改数据,所有与该数据关联的状态都会随之发生改变;
- 这就是大名鼎鼎的 表单元素的双向数据绑定;
- 此处checkbox的
checked属性+change事件与数据allSelected的双向同步关系可以推广到所有的表单元素; - 更多案例请查阅 官方文档
<input type="checkbox" :checked="allSelected" @change="allSelected = !allSelected">
<input type="checkbox" v-model="allSelected">
组件化开发
组件拆分
- 页面中你认为有必要复用的部分都可以拆出去形成一个独立子组件;
- 组件拆分的目的是好复用、好维护;
- Vue项目开发本质就是在维护一棵组件树,做好它们的数据和互动关系;
简单的父组件实现
<template>
<div>
<Son></Son>
</div>
</template>
<script>
import Son from "../../components/Son.vue"
export default {
components: {
Son
},
}
</script>
简单的子组件实现
<template>
<div>
<h1>爷是儿子</h1>
</div>
</template>
父子通信
- 父组件向子组件传递自己的数据主要是通过
props来实现的; - 子组件需要声明接收这些props;
- 当父组件的数据发生变化时,子组件能够自动响应;
父组件向子组件传递数据
<template>
<div>
<Son :msg="msgFromDad"></Son>
</div>
</template>
<script>
import Son from "../../components/Son.vue"
export default {
components: {
Son
},
data() {
return {
msgFromDad:"好好学习,天天向上"
}
},
}
</script>
子组件对接收的数据进行声明
props: ["msg"],
使用来自父组件的数据
<template>
<div>
<h1>爷是儿子 {{ name }}</h1>
<p>爹的信息 {{ msg }}</p>
</div>
</template>
<script>
export default {
props: ["msg"],
}
</script>
子父通信
- 当子组件需要去修改来自父组件的数据时,需要发送自定义事件;
- 子组件需要声明这些自定义事件;
- 父组件在部署子组件时需要声明对这些自定义事件的处理方式;
- 这样当子组件发送事件时父组件就能响应并修改自身的数据状态了;
子组件声明需要发送的自定义事件
emits: ["sendMsg"],
在子组件中点击按钮向父组件发送自定义事件
<template>
<div>
<p>爹的信息 {{ msg }}</p>
<button @click="onBtnClick">SendMsg</button>
</div>
</template>
<script>
export default {
props: ["msg"],
emits: ["sendMsg"],
methods: {
onBtnClick() {
// 发送自定义事件并携带数据
this.$emit("sendMsg", "爹美国的同学们都坐地铁上学只有我是开跑车")
}
},
}
</script>
父组件处理来自子组件的自定义事件
<template>
<div>
<Son :msg="msgFromDad" @sendMsg="onMsg"></Son>
<hr>
<button @click="showSon = !showSon">狗带</button>
</div>
</template>
<script>
import Son from "../../components/Son.vue"
export default {
components: {
Son
},
data() {
return {
msgFromDad:"好好学习,天天向上"
}
},
methods:{
onMsg(msg){
console.log("onMsg",msg);
this.msgFromDad = "信息已收到,已经打过去20亿,快去给劳资买辆地铁,别在外面给我丢人!"
}
}
}
</script>
生命周期
概述
- 一个组件在内存中的形态本质上是一个JS对象;
- 所以一个组件从创建到消亡对应着一个JS对象在内存中从创建到消亡的过程;
- 一个完整的组件生命周期要经历创建、挂载(即渲染DOM)、更新(数据状态发生变化时会驱动视图更新)、销毁四个阶段;
- 在每个阶段执行前和执行完毕时分别会有一次插入自定义逻辑(钩子)的机会,因此形成的8个对应生命周期分别为:
- 实例创建阶段:
beforeCreate+created;- 实例挂载阶段:
beforeMount+mounted;- 实例更新阶段:
beforeUpdate+updated;- 实例销毁阶段:
beforeDestroy+destroyed;
图解
常用钩子
我们最常用的三个生命周期为:
- 挂载完毕(mounted):此时DOM已经渲染完毕,我们常常在此时去联网加载数据,数据初始化完毕视图即可驱动视图更新;
- 更新完毕(updated):此时数据更新完毕,我们可以按需做一些必要的校正或同步工作;
- 即将销毁(beforeDestroy):组件即将销毁,我们常常会在此阶段做一些数据保存和资源清理的工作;
更多内容请查阅 官方文档
计算属性
概述
- 计算属性
computed本质为依赖一手数据计算而来的二手数据; - 当一手数据发生变化时,我们需要一个计算过程换算出一些二手数据;
- 例如从人口普查的一手数据中我们可以换算得到男女比例、城乡比例、高等教育比例等等;
- 因此计算属性在形态上类似于一个方法:
/* 方法定义 */
methods: {
/* 由周岁换算虚岁 */
xusuiMethod() {
console.log("xusuiMethod");
return this.age + 1
}
},
/* 计算属性定义 */
computed: {
/* 由周岁换算虚岁 */
xusuiComputed() {
console.log("xusuiComputed");
return this.age + 1
}
},
在模板上使用二者能得到相同效果:
<p>虚岁from方法: {{ xusuiMethod() }}</p>
<p>虚岁计算属性: {{ xusuiComputed }}</p>
计算属性 VS 方法
计算属性与常规方法的不同之处在于:
- 方法在每次组件更新(DOM需要重新渲染)时都会重新执行;
- 而计算属性只要它所依赖的一手数据项没有发生变化,就不会重新执行,而是直接调用更新前的数据缓存;
- 从这个角度而言,在功能相同的前提下,相比于方法,计算属性能够有效地提升组件性能!
案例
- 本例我们在组件中每秒更新一次时间数据
date; - 该数据不影响计算虚岁所依赖的
一手数据项age,因此当组件更新时,计算属性不会重新执行; - 其对应的方法则会在每次组件更新时都执行,性能上不如计算属性高;
<template>
<div>
<p>当前时间: {{ date }}</p>
<p>实岁:{{ age }}</p>
<p>虚岁from方法: {{ xusuiMethod() }}</p>
<p>虚岁计算属性: {{ xusuiComputed }}</p>
</div>
</template>
<script>
export default {
data() {
return {
age: 10,
date: "",
timer: null
}
},
/* 方法定义 */
methods: {
/* 由周岁换算虚岁 */
xusuiMethod() {
console.log("xusuiMethod");
return this.age + 1
}
},
/* 计算属性定义 */
computed: {
/* 由周岁换算虚岁 */
xusuiComputed() {
console.log("xusuiComputed");
return this.age + 1
}
},
/* 组件挂载时每秒更新一下时间 */
mounted() {
console.log("mounted");
if (!this.timer) {
this.timer = setInterval(() => {
console.log("timer is running...");
this.date = new Date().toLocaleString()
}, 1000);
}
},
updated() {
console.log("updated");
},
/* 组件销毁前清除定时器 否则它会在后台继续执行 */
beforeDestroy() {
console.log("beforeDestroy");
if (this.timer) {
clearInterval(this.timer)
this.timer = null
console.log("timer cleared");
}
}
}
</script>
执行效果