第2章 vue基础语法
1. 简介
Vue (读音 /vjuː/,类似于 view) 是一套用于构建用户界面的渐进式框架。与其它大型框架不同的是,Vue 被设计为可以自底向上逐层应用。Vue 的核心库只关注视图层,不仅易于上手,还便于与第三方库或既有项目整合。
Vue.js 的核心是一个允许采用简洁的模板语法来声明式地将数据渲染进 DOM 的系统,区别于JQuery的DOM树操作,Vue更多的是将数据与DOM进行双向绑定。
Vue另一个重要概念是组件,它是一种抽象,允许我们使用小型、独立和通常可复用的组件构建大型应用。一个组件本质上是一个拥有预定义选项的一个 Vue 实例。
组件树
2. Vue.js安装与引入
Vue可以通过操作npm来进行进行脚手架方式安装,详细请见本系列第一章内容第1章 基于vscode的vue开发环境搭建,如果是初学者,也可以直接在HTML文件中通过常规的js方式引入Vue.js文件
对于制作原型或学习,可以这样使用最新版本
<script src="https://cdn.jsdelivr.net/npm/vue"></script>
对于生产环境,推荐链接到一个明确的版本号和构建文件,以避免新版本造成的不可预期的破坏
<script src="https://cdn.jsdelivr.net/npm/vue@2.6.10/dist/vue.js"></script>
如果你使用原生 ES Modules,也有一个兼容 ES Module 的构建文件
<script type="module">
import Vue from 'https://cdn.jsdelivr.net/npm/vue@2.6.10/dist/vue.esm.browser.js'
</script>
也可以在Vue官网上下载Vue的js文件至本地进行引入
3. Vue实例
每个 Vue 应用都是通过用 Vue 函数创建一个新的 Vue 实例开始的
var vm = new Vue({
// 选项
})
需要注意的是,Vue中的参数是一个json类型数据,请注意它的格式
当一个 Vue 实例被创建时,它将 data 对象中的所有的属性加入到 Vue 的响应式系统中。当这些属性的值发生改变时,视图将会产生“响应”,即匹配更新为新的值。
// 我们的数据对象
var data = { a: 1 }
// 该对象被加入到一个 Vue 实例中
var vm = new Vue({
data: data
})
// 获得这个实例上的属性
// 返回源数据中对应的字段
vm.a == data.a // => true
// 设置属性也会影响到原始数据
vm.a = 2
data.a // => 2
// ……反之亦然
data.a = 3
vm.a // => 3
当这些数据改变时,视图会进行重渲染。
双向绑定原理
这里唯一的例外是使用 Object.freeze(),这会阻止修改现有的属性,也意味着响应系统无法再追踪变化。
<script>
var obj = {
foo: 'bar'
}
Object.freeze(obj)
new Vue({
el: '#app',
data: obj
})
</script>
<div id="app">
<p>{{ foo }}</p>
<!-- 这里的 `foo` 不会更新! -->
<button v-on:click="foo = 'baz'">Change it</button>
</div>
下面是Vue实例的生命周期,随着深入学习,会理解各个部分的进行时机。
Vue生命周期
4. 模板语法
4.1 文本渲染
vue提供了其他几种常见的文本渲染方式:
- v-text:更新元素的innerText,相当于
{{message}}
形式
以文本方式绑定文本数据,相当于innerText
- v-html:更新元素的innerHTML
以HTML方式解析并绑定文本数据,相当于innerHTML
- v-once:静态插值
以文本方式绑定文本数据,但绑定后不可更改,在控制台上通过demo.message4='xxxx'进行测验,与v-text方式进行比较。
- v-pre:原格式输出
相当于<pre>标记的功能
<div id="demo">
<div>{{message0}}</div>
<div v-text="message1"></div>
<div v-html="message2"></div>
<div v-once>{{message3}}</div>
<div v-pre>{{message4}}</div>
</div>
<script>
var demo = new Vue({
el:"#demo",
data:{
message0: "普通文本渲染1",
message1: "<b>普通文本渲染2</b>",
message2: "<b>HTML文本渲染</b>",
message3: "<b>静态插值渲染</b>",
message4: "原文本渲染"
}
})
</script>
运行结果
运行结果
4.2 属性绑定
属性绑定使用v-bind:属性名
完成,也可以使用语法糖省略v-bind
,直接使用:属性名
完成。其主要绑定方式有以下几种
- 常规绑定方式
为属性title绑定数据message,鼠标悬停在文字上会有气泡文字显示
<div id="demo">
<div v-bind:title="message">{{message}}</div>
<div :title="message">{{message}}</div>
</div>
<script>
var demo = new Vue({
el:"#demo",
data:{
message: '这是一些测试文本'
}
})
</script>
- 对象绑定方式
通过设置json对象的方式,为某个属性(主要是class)设置一组数据。
下述示例演示的是为各个div设置不同的背景颜色和文字颜色
<div id="demo">
<div v-bind:class="{btext:sepText,aurora:!showGround}">{{message}}</div>
<div v-bind:class="{btext:sepText,aurora:showGround}">{{message}}</div>
<div v-bind:class="styleEffect">{{message}}</div>
</div>
<style type="text/css">
div{
width:200px;
height: 80px;
}
.btext{
color: darkorange;/*橙黄文字颜色*/
}
.aurora{
background-color: darkturquoise;/*青蓝背景颜色*/
}
</style>
<script>
var demo = new Vue({
el:"#demo",
data:{
message: '这是一些测试文本',
sepText: true,
showGround: false,
styleEffect:{
'btext':false,
'aurora': true
}
}
})
</script>
运行结果:
运行结果
解析运行的HTML:
解析运行的HTML
- 数组绑定方式
通过把一个数组传给v-bind:class,以应用一个class列表。
个人认为这种方式对于样式的应用控制不算方便。
<div id="demo">
<div :class="[sepText,showGround]">{{message}}</div>
</div>
<style type="text/css">
div{
width:200px;
height: 80px;
}
.btext{
color: darkorange;
}
.aurora{
background-color: darkturquoise;
}
</style>
<script>
var demo = new Vue({
el:"#demo",
data:{
message: '这是一些测试文本',
sepText: 'btext',
showGround: 'aurora',
}
})
</script>
运行结果:
运行结果
解析运行的HTML:
解析运行的HTML
- 三目运算绑定
可以通过三目运算符控制样式的使用
<div id="demo">
<div :class="[isBgA ? bgColorA : bgColorB]">{{message}}</div>
</div>
<style type="text/css">
div{
width:200px;
height: 80px;
}
.california{
background-color: darkorange;
}
.aurora{
background-color: darkturquoise;
}
</style>
<script>
var demo = new Vue({
el:"#demo",
data:{
message: '这是一些测试文本',
bgColorA: 'california',
bgColorB: 'aurora',
isBgA: true
}
})
</script>
运行结果:
解析运行的HTML:
解析运行的HTML
更改
isBgA
的值为false
,背景颜色会切换为样式aurora
- 内联样式绑定
内联CSS样式中,CSS属性名的变化与原生JS解析CSS的方式一致
css属性 | 原生JS | vue |
---|---|---|
color | color | color |
background-color | backgroundColor | backgroundColor |
<div id="demo">
<div :style="{color:tcolor, backgroundColor: bcolor}">{{message}}</div>
</div>
<style type="text/css">
div{
width:200px;
height: 80px;
}
</style>
<script>
var demo = new Vue({
el:"#demo",
data:{
message: '这是一些测试文本',
tcolor: 'darkorange',
bcolor: 'darkturquoise'
}
})
</script>
运行结果:
运行结果
解析运行的HTML:
解析运行的HTML
4.3 事件处理
- 事件处理语法
可以用v-on:事件名称
指令绑定一个事件监听器,通过它调用我们 Vue 实例中定义的方法。
v-on:事件名称
可以简写为@事件名称
<div id="demo">
<button type="button" v-on:click="mymethod1">{{counter1}}</button>
<button type="button" @click="mymethod2">{{counter2}}</button>
</div>
<script type="text/javascript">
var demo = new Vue({
el: "#demo",
data:{
counter1:0,
counter2:100
},
methods:{
mymethod1(){
this.counter1++;
},
mymethod2(){
this.counter2--;
}
}
});
</script>
运行结果:点击按钮时会增加和减少数字
运行结果
- 事件修饰符
事件修饰符的作用是对事件进行一些特殊控制,其语法规则为v-on:事件名称.事件修饰符
或@事件名称.事件修饰符
事件修饰符 | 功能 |
---|---|
stop | 阻止后续的事件捕获和事件冒泡 |
prevent | 阻止默认事件 |
once | 一次性事件 |
<div id="demo">
默认事件:<button type="button" v-on:click="mymethod1">{{counter1}}</button><br>
<div @click="changeTextColor">
阻止事件捕获和事件冒泡:
<span :style="{color: [isTC ? colorA : colorB]}">颜色</span>
<button v-on:click="mymethod2">non-stop:{{counter2}}</button>
<button v-on:click.stop="mymethod3">stop:{{counter3}}</button>
</div>
阻止默认事件:<a href="http://www.baidu.com" @click.prevent="mymethod4">{{counter4}}</a><br>
一次性事件:<button type="button" @click.once="mymethod5">{{counter5}}</button><br>
</div>
<script type="text/javascript">
var demo = new Vue({
el: "#demo",
data:{
counter1:0,
counter2:0,
counter3:0,
counter4:0,
counter5:0,
colorA: "blue",
colorB: "red",
isTC: true
},
methods:{
mymethod1(){
this.counter1++;
},
mymethod2(){
this.counter2++;
},
mymethod3(){
this.counter3++;
},
mymethod4(){
this.counter4++;
},
mymethod5(){
this.counter5++;
},
changeTextColor(){
this.isTC = !this.isTC;
}
}
});
</script>
运行结果:
运行结果
- 鼠标修饰符
可以通过使用@鼠标点击相关事件.鼠标修饰符
来控制鼠标各个按键的事件
鼠标相关事件:click,mouseup,mousedown
鼠标修饰符 | 功能 |
---|---|
left | 左键 |
middle | 滚轮 |
right | 右键 |
<div id="demo">
<button @click.right="rightMethod" @click.middle="middleMethod" @click="leftMethod">{{message}}</button>
</div>
<script type="text/javascript">
var demo = new Vue({
el: "#demo",
data:{
message: '请点击'
},
methods:{
rightMethod(){
this.message = '鼠标右键';
},
middleMethod(){
this.message = '鼠标滚轮';
},
leftMethod(){
this.message = '鼠标左键';
}
}
});
</script>
运行结果:分别使用鼠标左键,滚轮和右键点击按钮
运行结果
- 按键修饰符
vue中支持监控常用的按键,允许在按键事件中添加按键修饰符,其格式为@按键事件.按键修饰符
按键事件:keypress,keyup, keydown
按键修饰符 | 功能 |
---|---|
enter | 回车键 |
tab | Tab键 |
delete | “退格键”或“删除键” |
esc | Esc键 |
space | 空格键 |
up | ↑键 |
down | ↓键 |
left | ←键 |
right | →键 |
<div id="demo">
<button v-on:keyup.enter="enter">{{message1}}</button>
<button @keyup.tab="tab">{{message2}}</button>
<button @keyup="show($event)">{{message3}}</button>
</div>
<script>
var demo = new Vue({
el:'#demo',
data:{
message1: "hello",
message2: "hello",
message3: "hello"
},
methods:{
enter(){
this.message1 = 'enter';
},
tab(){
this.message2 = 'tab';
},
show(e){
this.message3 = e.keyCode;
}
}
})
</script>
运行结果:使各个按钮分别获得焦点后依次触发键盘按键测试结果
4.4 条件指令
- v-if
v-if的用法时当值为true时进行渲染,当值为false时,页面中不会进行渲染。
<div id="demo">
<div v-if="showInfo">{{message1}}</div>
<div v-if="hideInfo">{{message2}}</div>
</div>
<style type="text/css">
#demo{
width:200px;
height: 80px;
}
</style>
<script>
var demo = new Vue({
el:"#demo",
data:{
message1: '这是显示的文本',
message2: '这是隐藏的文本',
showInfo: true,
hideInfo: false
}
})
</script>
运行结果:显示“这是显示的文本”,更改showInfo和hideInfo的值可以看到不同结果
- v-else
与v-if形成互斥条件
<div id="demo">
<div v-if="showInfo">{{message1}}</div>
<div v-else>{{message2}}</div>
</div>
<style type="text/css">
#demo{
width:200px;
height: 80px;
}
</style>
<script>
var demo = new Vue({
el:"#demo",
data:{
message1: '这是显示的文本',
message2: '这是隐藏的文本',
showInfo: false
}
})
</script>
</body>
运行结果:显示“这是隐藏的文本”,更改showInfo的值可以看到不同结果
- v-else-if
多个条件互斥条件,相当于if...else if...else
<div id="demo">
<div v-if="vtype == 1">{{message1}}</div>
<div v-else-if="vtype == 2">{{message2}}</div>
<div v-else>{{message3}}</div>
</div>
<style type="text/css">
#demo{
width:200px;
height: 80px;
}
</style>
<script>
var demo = new Vue({
el:"#demo",
data:{
message1: '这是文本1',
message2: '这是文本2',
message3: '这是文本3',
vtype:1
}
})
</script>
运行结果:显示“这是文本1”,更爱vtype的值可以看到不同结果
- v-show
v-show也是根据条件展示元素的指令。带有 v-show 的元素始终会被渲染并保留在 DOM 中。v-show 是简单地切换元素的 CSS 属性display
来控制元素的显示和隐藏 。
<div id="demo">
<p v-show="message">message</p>
<p v-show="infor">infor</p>
</div>
<script>
var demo1 = new Vue({
el:'#demo',
data:{
message:true,
infor:false
}
})
</script>
页面运行源码
也就说,虽然第二个p元素没有显示出来,但是页面渲染时是存在的,只是多了一个隐藏属性。
通过上面的例子,我们可以发现两者的不同:
(1) v-if是真正的条件渲染,因为它会确保在切换过程中条件块内的事件监听器和子组件适当地被销毁和重建。
(2) v-if是惰性的,只有当条件为true时才会渲染,如果条件为false则什么都不做
(3) v-if有很高的切换开销,适用于条件不太容易改变的时候
v-show不管条件是true还是false都会进行渲染。并且只是简单地基于 CSS 进行切换
(4) v-show有很高的初始渲染开销,适用于非常频繁地切换
4.5 循环指令
- v-for
value 是遍历得到的属性值,key 是遍历得到的属性名,index 是遍历次序,这里的 key/index 都是可选参数,这个指令其实可以写成 v-for="value in xxx"
<div id="demo">
<ul>
<li v-for="value in object">
{{ value }}
</li>
</ul>
</div>
<script>
var demo = new Vue({
el: '#demo',
data: {
object: {
firstName: 'John',
lastName: 'Doe',
age: 30
}
}
})
</script>
运行结果:
运行结果
<div id="demo">
<div v-for="(value, key,index) in object">
{{ index }} : {{ key }} : {{ value }}
</div>
</div>
<script>
var demo = new Vue({
el: '#demo',
data: {
object: {
firstName: 'John',
lastName: 'Doe',
age: 30
}
}
})
</script>
运行结果
运行结果
4.6 方法,计算属性和侦听器
在vue中处理复杂的逻辑的时候,我们经常使用计算属性,方法和侦听器。
- methods
正如他的名字一样,它们是挂载在对象上的函数,通常是Vue实例本身或Vue组件,需要主动去触发。
<div id="demo" @click="getFullName">
{{fullName}}
</div>
<script>
var demo = new Vue({
el: '#demo',
data: {
firstName: 'Monkey',
middleName: 'D',
lastName: 'Luffy',
fullName: '请点击',
},
methods: {
getFullName: function(){
this.fullName = this.firstName + "."+this.middleName+"."+this.lastName;
}
}
})
</script>
运行结果:点击后文字变为“Monkey.D.Luffy”
- computed
computed被称为计算属性,通常用于多个数据的值影响某一个数据时。
与methods不同的是计算属性是根据依赖关系进行缓存的计算,并且只在需要的时候进行更新。
<div id="demo">
{{fullName}}
</div>
<script>
var demo = new Vue({
el: '#demo',
data: {
firstName: 'Monkey',
middleName: 'D',
lastName: 'Luffy',
},
computed: {
fullName: function(){
return this.firstName + "."+this.middleName+"."+this.lastName;
}
}
})
</script>
运行结果:页面显示“Monkey.D.Luffy”。
fullName属性是由firstName,middleName和lastName三者组成的
- watch
watch被称为观察者,通常用于某一个数据可能影响多个数据的值时。
<div id="demo">
company:{{company}}<br>
emp1:{{emp1}}<br>
emp2:{{emp2}}<br>
<input type="button" @click="changeCompany" value="点我" />
</div>
<script>
var demo = new Vue({
el: '#demo',
data: {
company: '乡村爱情',
emp1: '乡村爱情-赵四',
emp2: '乡村爱情-刘能'
},
methods:{
changeCompany: function (){
this.company = '东北F2';
}
},
watch: {
company: function(newCompany){
this.emp1 = newCompany+ '-赵四';
this.emp2 = newCompany+ '-刘能';
}
}
})
</script>
运行结果:点击按钮后切换两位emp的名字前缀。
此时两个数据emp1和emp2全部依赖于数据company
4.7 表单绑定
v-bind实现了数据的单向绑定,将vue实例中的数据同元素属性值进行单向绑定.vue也支持数据双向绑定,这时使用v-model在控件上实现数据的双向绑定。
- v-model
<div id="demo">
输入:<input type="text" v-model="message"><br>
<div>{{message}}</div>
</div>
<script>
var demo = new Vue({
el: '#demo',
data: {
message: ''
}
})
</script>
运行结果:文本框输入内容会自动显示在其下方的div
控制台更改demo.message的值,文本框中的内容也会改变
- v-model.lazy
将上个例子中的v-model
更改为v-model.lazy
可以看到当文本框失去焦点时才会触发message更改
指令 | 特点 |
---|---|
v-model | 类似oninput事件 |
v-model.lazy | 类似onblur事件 |
- v-model.number
对于type='number'的控件,v-model在绑定时会将其数据处理为string类型,可以通过v-model.number解决。
<div id="demo">
输入1:<input type="number" v-model.number="message1"><br>
输入2:<input type="number" v-model="message2"><br>
<div>内容1:{{message1}},类型:{{mess1type}}</div>
<div>内容2:{{message2}},类型:{{mess2type}}</div>
</div>
<script>
var demo = new Vue({
el: '#demo',
data: {
message1: 0,
message2: 0
},
computed:{
mess1type: function(){
return typeof(this.message1);
},
mess2type: function(){
return typeof(this.message2);
}
}
})
</script>
运行结果
运行结果
- v-model.trim
trim用于去除内容两侧多余的空白字符
<div id="demo">
<input v-model.trim="mess3">
<p>mess3 is :{{ mess3 }}</p>
</div>
<script>
var demo = new Vue({
el:'#demo',
data:{
mess3:''
},
})
</script>
运行结果
运行结果
4.8 过滤器
在vue2.0以前的版本中vue内置的过滤器,但是因为缺乏纯JavaScript的灵活性,在vue2.0版本中已经删除了内置过滤器,所以需要自己注册过滤器。
可以定义本地(在某一个template里面定义filter)过滤器,或者定义全局(global)过滤器。如果定义了一个全局过滤器,它必须在Vue实列之前声明。
- 全局过滤器
<div id="demo">
{{message1 | showTime}}<br>
{{message2 | showTime}}<br>
</div>
<script>
Vue.filter("showTime", function (value){
return value+"["+new Date()+"]";
});
var demo = new Vue({
el: '#demo',
data: {
message1: "你好",
message2: "测试"
}
})
</script>
运行结果:
运行结果
- 本地过滤器
本地过滤器存储在vue组件中,作为filters属性中的函数,我们可以注册多个过滤器存储在其中。
<div id="demo">
{{message}}<br>
{{message | reverse}}<br>
</div>
<script>
var demo = new Vue({
el: '#demo',
data: {
message: "这是一段测试文本"
},
filters:{
reverse:function (value){
value = value.toString();
return value.split('').reverse().join('');
}
}
})
</script>
运行结果
运行结果
- 串联过滤器
过滤器除了单独使用之外,我们还可以对过滤器进行串联使用,也可以在v-bind中使用过滤器,语法与{{format}}
中的语法一致。
<div id="demo">
{{message}}<br>
{{message | reverse}}<br>
{{message | length}}<br>
{{message | reverse | length}}<br>
</div>
<script>
var demo = new Vue({
el: '#demo',
data: {
message: "这是一段测试文本"
},
filters:{
reverse:function (value){
value = value.toString();
return value.split('').reverse().join('');
},
length: function(value){
value = value.toString();
return "文本\""+value+"\"的长度:"+value.length;
}
}
})
</script>
运行结果:
运行结果
- 过滤器传参
<div id="demo">
{{message}}<br>
{{message | test(1,2)}}<br>
{{message | test(2,3)}}<br>
</div>
<script>
var demo = new Vue({
el: '#demo',
data: {
message: 5
},
filters:{
test:function (value, x, y){
value = Number(value);
return value*x*y;
}
}
})
</script>
运行结果
运行结果
- 双向过滤器
下述内容当前vue版本报错,暂未解决
<div id="demo">
<input type="text" v-model="msg | currencyDisplay"><br>
<span>{{msg}}</span>
</div>
<script>
Vue.filter('currencyDisplay', {
// model -> view
// 在更新 `<input>` 元素之前格式化值
read: function(val) {
return '¥' + val.toFixed(2);
},
// view -> model
// 在写回数据之前格式化值
write: function(val, oldVal) {
var number = +val.replace(/[^\d.]/g, '');
return isNaN(number) ? 0 : parseFloat(number.toFixed(2));
}
})
var demo = new Vue({
el:"#demo",
data: {
msg:1024
}
})
</script>
4.9 自定义指令
除了核心功能默认内置的指令外,vue也允许用户注册自定义指令。虽然在vue2.0中,代码复用和抽象的主要形式是组件,但是有些情况下,仍需要对普通DOM元素进行底层操作,这个时候就需要用到自定义指令。
- 自定义指令
<div id="demo">
<div>{{message}}</div>
<div v-red>{{message}}</div>
</div>
<script>
Vue.directive('red', function (el){
el.style.color = 'red';
})
var demo = new Vue({
el:"#demo",
data: {
message:"测试文本"
}
})
</script>
运行结果
运行结果
- 传递参数
传参前需要保证传参的值在vue的data中有声明,调用时需要通过data.value的方式调出具体的值。
<div id="demo">
<div>{{message}}</div>
<div v-color='red'>{{message}}</div>
<div v-color='blue'>{{message}}</div>
</div>
<script>
Vue.directive('color',{
bind: function(el, c) {
el.style.color = c.value;
}
})
var demo = new Vue({
el: "#demo",
data: {
message: "测试文本",
red: 'red',
blue: 'blue'
}
})
</script>
- 事件处理
在声明自定义指令时,指令的触发时机也是不同的。
指令 | 特点 |
---|---|
bind | 每当指令绑定到元素上的时候,会立即执行且只执行一次 |
inserted | 元素插入到DOM中的时候,会执行inserted函数且只执行一次 |
updated | 当VNode更新的时候,会执行updated,可能会触发多次 |
<div id="demo">
输入1:<input type="text" v-model="message"><br>
输入2:<input type="text" v-model="message" v-focus><br>
</div>
<script>
Vue.directive('focus',{
inserted: function(el) {
el.focus();
}
});
var demo = new Vue({
el: "#demo",
data: {
message: "测试文本"
}
})
</script>
运行结果
输入2进入页面时获得焦点