Aitter's Blog

Vue2.x 之 基础API

主要内容

  • 模板语法(文本插值、属性绑定、JS表达式、过滤器、指令)
  • Vue实例(viewModal(属性+函数)、生命周期)
  • 计算属性和监听器 (computed(get,set) 与 watch)
  • 样式绑定(对象绑定、数组绑定、内联绑定)
  • 条件绑定(v-if v-show)
  • 列表渲染 (v-for、:key、数组监测、过滤/排序)
  • 事件处理 (监听、修饰符、key修饰符)
  • 表单输入绑定(text、checkbox、radio、select)

模板语法

文本插值

- 使用 `{{ }} / <span v-text="msg"></span>` 绑定数据
- `{{ }}` 纯文本绑定,单向,随vm变化而变化
- `<span v-once>{{ msg }}</span>` 纯文本,单次,不跟随vm变化
- `<span v-html="msg"></span>` 不转义html标签,绑定html

属性绑定

- 使用 `v-bind` 指令绑定数据至标签属性
- `<a v-bind:id="msgId"></a> 简写 <a :id="msgId"></a>`

模板中的JS

  • 支持表达式执行,但不支持多个表达式、语句和控制流的执行
  • 属性绑定和绑定的数据都支持JS表达式
//加减乘除、三元运算、方法调用
{{ number + 1 }}
{{ ok ? 'YES' : 'NO' }}
{{ message.split('').reverse().join('') }}
<div v-bind:id="'list-' + id"></div>
//错误用法
<!-- 这是语句,不是表达式 -->
{{ var a = 1 }}
<!-- 流控制也不会生效,请使用三元表达式 -->
{{ if (ok) { return message } }}

过滤器

- 使用 `|` 对原始值进行处理
- 用于属性绑定与 `{{ }}`
- `{{ msg | capitalize }} <a :id="msgId | formatId"></a>`
- 可以串联 `{{ msg | filterA | filterB }}`
- 可以接收参数 `{{ msg | filterA(arg1, arg2) }}`

指令

- 带有 `v-` 前缀的特殊属性
- 当其表达式的值改变时相应地将某些行为应用到 DOM 上
- `v-bind/v-for/v-html/v-on/v-if/...`
- `v-bind` 缩写 `<a v-bind:href="url"></a><a :href="url"></a>`
- `v-on` 缩写 `<a v-on:click="doSomething"></a><a @click="doSomething"></a>`

注册过滤器

//全局注册
Vue.filters('capitalize',value=>{
if (!value) return ''
value = value.toString()
return value.charAt(0).toUpperCase() + value.slice(1)
})
//局部注册
filters: {
capitalize: function (value, arg1) {
if (!value) return ''
value = value.toString()
return value.charAt(0).toUpperCase() + value.slice(1)
}
}
//使用
<span>{{msg | capitalize(arg1) }}

注意

  • 注册过滤器时,如果需要传递参数,必须从第二个参数开始,第一个参数为当前绑定的数据
  • 过滤器一般用于简单的文本格式化,如果是对多个状态数据,或是复杂的数据处理应该使用计算属性

注册指令

//全局注册
// 注册一个全局自定义指令 v-focus
Vue.directive('focus', {
// 当绑定元素插入到 DOM 中。
inserted: function (el) {
// 聚焦元素
el.focus()
}
})
//局部注册
directives: {
focus: {
// 指令的定义---
}
}
//使用
<input v-focus />

Vue实例

Vue 实例,实则也就是 ViewModel(数据 + 函数),都是通过构造函数 Vue 创建

var vm = new Vue({
name:'root',
// 数据
data: { a: 1 } / Function, // data类型根实例为Object,组件中为Function
props:[]/{}, // 设置父组件传递给子组件的数据限制
computed:{}, // 计算属性
watch:{}, // 监控属性
methods:{}, // 事件操作
// 资源
directives:{}, // 内部指令
filters:{}, // 内部过滤器
components:{}, // 内部组件
// 生命周期:实例创建 => 编译挂载 => 组件更新 => 销毁
beforeCreate(){
console.log('beforeCreate ==> 实例创建')
},
created(){
// 可以操作data, 但未生成DOM(未挂载)发起异步请求,初始化组件状态数据 data
console.log('created ==> 实例创建完成,属性已绑定')
},
beforeMount(){
console.log('beforeMount ==> 模板编译/挂载之前')
},
mounted(){
// 已生成DOM到document中,可访问this.$el属性
console.log('mounted ==> 模板编译/挂载之后')
},
beforeUpdate(){
console.log('beforeUpdate ==> 组件更新之前')
},
updated(){
// 操作DOM $('#box1')
console.log('updated ==> 组件更新之后')
},
activated(){
// 操作DOM $('#box1')
console.log('activated ==> 组件被激活时(for keep-alive组件)')
},
deactivated(){
console.log('deactivated ==> 组件被移除时(for keep-alive组件)')
},
beforeDestroy(){
// 解除事件绑定,销毁非Vue组件实例等 如:this.$off('event1') select2.destory()
console.log('beforeDestroy ==> 组件销毁之前')
},
destroyed(){
console.log('destroyed ==> 组件销毁之后')
}
})

计算属性与监听器

computed

  • 任何复杂逻辑,都应当使用计算属性
  • 可以像绑定普通属性一样在模板中绑定计算属性
  • 声明式地创建依赖关系,计算属性的 getter 是干净无副作用的,因此也是易于测试和理解的。
<div id="example">
<p>Original message: "{{ message }}"</p>
<p>Computed reversed message: "{{ reversedMessage }}"</p>
</div>
var vm = new Vue({
el: '#example',
data: {
message: 'Hello'
},
computed: {
// a computed getter
reversedMessage: function () {
// `this` points to the vm instance
return this.message.split('').reverse().join('')
}
}
})

使用 methodsfilter 也能达到计算属性同样的效果,但计算属性使模板更加简单清晰(模板中放入太多的逻辑会让模板过重且难以维护)。

计算属性有 计算缓存 的特性,计算属性是基于它的依赖缓存,只有在它的相关依赖发生改变时才会重新取值,而 methods 每次执行都会重新取值。

什么需要缓存?

假设我们有一个重要的计算属性 A ,这个计算属性需要一个巨大的数组遍历和做大量的计算。然后我们可能有其他的计算属性依赖于 A 。如果没有缓存,我们将不可避免的多次执行 A 的 getter !如果你不希望有缓存,请用 method 替代。

getter与setter

计算属性默认为 getter

data: {
firstName: 'Foo',
lastName: 'Bar'
},
computed: {
fullName: function () {
return this.firstName + ' ' + this.lastName
}
}

也可以添加 setter

computed: {
fullName: {
// getter
get: function () {
return this.firstName + ' ' + this.lastName
},
// setter
set: function (newValue) {
var names = newValue.split(' ')
this.firstName = names[0]
this.lastName = names[names.length - 1]
}
}
}

watch

使用 watch 来监听data,实时响应数据的变化

例:监听用户输入,显示 正在输入...,输入完成时,显示 请稍等...,并发送异步请求,请求成功里,显示答案

var watchExampleVM = new Vue({
el: '#watch-example',
data: {
question: '',
answer: 'I cannot give you an answer until you ask a question!'
},
watch: {
// 如果 question 发生改变,这个函数就会运行
question: function (newQuestion) {
this.answer = '正在输入...'
this.getAnswer()
}
},
methods: {
// _.debounce 是一个通过 lodash 限制操作频率的函数。
// 在这个例子中,我们希望限制访问yesno.wtf/api的频率
// ajax请求直到用户输入完毕才会发出
getAnswer: _.debounce(
function () {
if (this.question.indexOf('?') === -1) {
this.answer = '需要一个问题标识\'?\''
return
}
this.answer = '请稍候...'
var vm = this
axios.get('https://yesno.wtf/api')
.then(function (response) {
vm.answer = _.capitalize(response.data.answer)
})
.catch(function (error) {
vm.answer = 'Error! Could not reach the API. ' + error
})
},
// 这是我们为用户停止输入等待的毫秒数
500
)
}
})

使用 watch 选项允许我们执行异步操作(访问一个 API),限制我们执行该操作的频率,并在我们得到最终结果前,设置中间状态。这是计算属性无法做到的。

样式绑定

使用 v-bind:classv-bind:style 来绑定样式

data:()=>{
return {
isActive: true,
hasError: false,
classObj:{
'active':true,
'align-left':true,
},
activeClass: 'active',
errorClass: 'text-danger',
styleObj:{
color: 'red',
fontSize: '13px'
},
activeColor: 'red',
fontSize: 30,
baseStyles:{color: 'red'},
overridingStyles: { fontSize: '20px'}
}
}

对象语法绑定

class 绑定的是对象的 key,如果对象的值为 true,则绑定 key
style 绑定的整个 对象

绑定 class

<div v-bind:class="classObj"></div>
//<div class="active align-left"></div>
<div class="static" v-bind:class="{ active: isActive, 'text-danger': hasError }"></div>
//<div class="static active"></div>

绑定style

<div v-bind:style="{ color: activeColor, fontSize: fontSize + 'px' }"></div>
<div v-bind:style="styleObj"></div>
// <div style=" color:'red'; fontSize:'13px'; "></div>

数组语法绑定

class 绑定的是数组的
style 绑定的是数组中的 对象

<div v-bind:class="[activeClass, errorClass]">
// <div class="active text-danger">
// 使用三元表达式
<div v-bind:class="[isActive ? activeClass : '', errorClass]">
// 数组语法中使用对象语法
<div v-bind:class="[{ active: isActive }, errorClass]">
//绑定style
<div v-bind:style="[baseStyles, overridingStyles]">

条件渲染

v-if

切换元素的隐藏和显示 v-ifv-elsev-if-else

切换单个元素

<h1 v-if="ok">Yes</h1>
<h1 v-else>No</h1>

切换多个元素

<template v-if="ok">
<h1>Title</h1>
<p>Paragraph 1</p>
<p>Paragraph 2</p>
</template>

多条件判断

<div v-if="type === 'A'">
A
</div>
<div v-else-if="type === 'B'">
B
</div>
<div v-else-if="type === 'C'">
C
</div>
<div v-else>
Not A/B/C
</div>

条件渲染默认会复用相同的组件,如果不复用元素,可添加 key

<template v-if="loginType === 'username'">
<label>Username</label>
<input placeholder="Enter your username" key="username-input">
</template>
<template v-else>
<label>Email</label>
<input placeholder="Enter your email address" key="email-input">
</template>

v-show

  • 用于切换元素样式属性 displayblocknone
  • v-if 不同的是,元素隐藏时,并没有从DOM中删除,而 v-if 是删除了元素保存在缓存中。

注意 v-show 不支持 <template> 语法

v-if 是真实的条件渲染,因为它会确保条件块在切换当中适当地销毁与重建条件块内的事件监听器和子组件。

v-if 也是惰性的:如果在初始渲染时条件为假,则什么也不做——在条件第一次变为真时才开始局部编译(编译会被缓存起来)。

区分 v-ifv-show 的使用场景:

  • v-if 有更高的切换消耗而 v-show 有更高的初始渲染消耗
  • 如果需要频繁切换使用 v-show 较好
  • 如果在运行时条件不大可能改变则使用 v-if 较好

列表渲染

v-for='item of items / item in items' 用于迭代对象或数组中的元素

data: {
items: [
{message: 'Foo' },
{message: 'Bar' }
]
object: {
firstName: 'John',
lastName: 'Doe',
age: 30
}
}

基本用法

<ul id="example-1">
<li v-for="item in items">
{{ item.message }}
</li>
</ul>

添加第二个参数可以获取当前迭代的 key

数组迭代

<ul id="example-2">
<li v-for="(item, index) in items">
{{ parentMessage }} - {{ index }} - {{ item.message }}
</li>
</ul>

对象迭代

<div v-for="(value, key) in object">
{{ key }} : {{ value }}
</div>

循环组件,向组件中传递数据

<my-component
v-for="(item, index) in items"
v-bind:item="item"
v-bind:index="index">
</my-component>

组件有自己的作用域,向组件中传递数据需要使用属性传递

v-for 具有比 v-if 更高的优先级

判断每一个todo项是否可显示

<li v-for="todo in todos" v-if="!todo.isComplete">
{{ todo }}
</li>

判断是否需要迭代todos

<ul v-if="shouldRenderTodos">
<li v-for="todo in todos">
{{ todo }}
</li>
</ul>

添加 key 属性

提升重复渲染效率

<div v-for="item in items" :key="item.id">
<!-- 内容 -->
</div>

数组监测

Vue重写了数组的变异方法,用于监测数组的更新

push()pop()shift()unshift()
splice()sort()reverse()

  • 这些操作会改变原有数组,为变异方法
  • 非变异方法返回新的数组,可以用于替换旧的数组

直接修改数组长度、利用索引修改数组的值,不会被监听到

过滤/排序

使用计算属性对原有数组进行过滤和排序

data: {
numbers: [ 1, 2, 3, 4, 5 ]
},
computed: {
evenNumbers: function () {
return this.numbers.filter(function (number) {
return number % 2 === 0
})
}
}

事件监听

v-on

v-on 用于监听绑定的事件

<div id="example-3">
<button @click="say('hi')">Say hi</button>
<button @click="say('what')">Say what</button>
</div>
new Vue({
el: '#example-3',
methods: {
say: function (message) {
alert(message)
}
}
})

原生事件对象

使用 $event 传递原生事件对象

<button v-on:click="warn('hello', $event)">Submit</button>
methods: {
warn: function (message, event) {
// 现在我们可以访问原生事件对象
if (event) event.preventDefault()
alert(message)
}
}

事件修饰符

methods 应该只处理纯粹的数据逻辑,而不是去处理 DOM 事件细节

  • .stop 阻止事件冒泡
  • .prevent 阻止默认事件
  • .capture 使用捕获模式
  • .self 只有当事件作用于本身时才触发
  • .once 只绑定一次
<!-- 阻止单击事件冒泡 -->
<a v-on:click.stop="doThis"></a>
<!-- 提交事件不再重载页面 -->
<form v-on:submit.prevent="onSubmit"></form>
<!-- 修饰符可以串联 -->
<a v-on:click.stop.prevent="doThat"></a>
<!-- 只有修饰符 -->
<form v-on:submit.prevent></form>
<!-- 添加事件侦听器时使用事件捕获模式 -->
<div v-on:click.capture="doThis">...</div>
<!-- 只当事件在该元素本身(而不是子元素)触发时触发回调 -->
<div v-on:click.self="doThat">...</div>
<!-- 点击事件将只会触发一次 2.1.4-->
<a v-on:click.once="doThis"></a>
<!-- 组件中的原生事件 -->
<my-component @click.native="onClick"></my-component>

按键修饰符

监听键盘按下的键值

监听keyCode

<!-- 只有在 keyCode 是 13 时调用 vm.submit() -->
<input v-on:keyup.13="submit">

常用按键别名

.enter
.tab
.delete (捕获 “删除” 和 “退格” 键)
.esc
.space
.up
.down
.left
.right
2.1.0
.ctrl
.alt
.shift
.meta
<!-- 同上 -->
<input v-on:keyup.enter="submit">

按键组合

<!-- Alt + C -->
<input @keyup.alt.67="clear">
  • 所有的 Vue.js 事件处理方法和表达式都严格绑定在当前视图的 ViewModel 上
  • 当一个 ViewModel 被销毁时,所有的事件处理器都会自动被删除。你无须担心如何自己清理它们。

表单输入绑定

使用 v-modal 给表单控件绑定相关数据(双向绑定)

v-modal 是一个语法糖

<input v-model="something">
// 等同于
<input v-bind:value="something" v-on:input="something = $event.target.value">

基本输入

// 文本框
<input v-model="message" placeholder="edit me">
// 文本域(支持换行)
<textarea v-model="message" placeholder="add multiple lines"></textarea>
// 复选框
// 单选(返回 true/false )
<input type="checkbox" id="checkbox" v-model="checked">
<label for="checkbox">{{ checked }}</label>
// 多选 (返回一个数组 ['jack', 'john'])
<input type="checkbox" id="jack" value="Jack" v-model="checkedNames">
<label for="jack">Jack</label>
<input type="checkbox" id="john" value="John" v-model="checkedNames">
<label for="john">John</label>
<input type="checkbox" id="mike" value="Mike" v-model="checkedNames">
<label for="mike">Mike</label>
<br>
<span>Checked names: {{ checkedNames }}</span>
//单选框 (返回选中的值)
<input type="radio" id="one" value="One" v-model="picked">
<label for="one">One</label>
<br>
<input type="radio" id="two" value="Two" v-model="picked">
<label for="two">Two</label>
// 下拉框
// 单选 (返回选中的值)
<select v-model="selected">
<option>A</option>
<option>B</option>
<option>C</option>
</select>
// 多选(返回一个数组 ['A','B'])
<select v-model="selected" multiple>
<option>A</option>
<option>B</option>
<option>C</option>
</select>

当有选项有 value 属性时,选中时,返回 value, 当属性没有 value 时,选中时返回字符串或 true/false

<select v-model="selected">
<option v-for="option in options" v-bind:value="option.value">
{{ option.text }}
</option>
</select>

绑定动态的value

v-model 绑定的 value 通常是静态字符串,对于 radiocheckboxselect,可以动态的使用v-bind设置value

<input
type="checkbox"
v-model="toggle"
v-bind:true-value="a"
v-bind:false-value="b"
>
// 当选中时
vm.toggle === vm.a
// 当没有选中时
vm.toggle === vm.b

修饰符

v-modal 的修饰符 .lazy.number.trim

<!-- 在 "change" 而不是 "input" 事件中更新 -->
<input v-model.lazy="msg" >
// 自动将用户的输入值转为 Number 类型(如果原值的转换结果为 NaN 则返回原值)
<input v-model.number="age" type="number">
// 过滤用户输入的首尾空格
<input v-model.trim="msg">

自定义输入组件

使用 v-modal 自定义输入组件

前提条件,组件必须有 value 属性, 在有新的value时,可通过 input 事件获取更新的值。

自定义的货币输入组件

<currency-input v-model="price"></currency-input>
Vue.component('currency-input', {
template: '
<span>
$
<input
ref="input"
v-bind:value="value"
v-on:input="updateValue($event.target.value)"
>
</span>
',
props: ['value'],
methods: {
// 不是直接更新值,而是使用此方法来对输入值进行格式化和位数限制
updateValue: function (value) {
var formattedValue = value
// 删除两侧的空格符
.trim()
// 保留 2 小数位
.slice(0, value.indexOf('.') + 3)
// 如果值不统一,手动覆盖以保持一致
if (formattedValue !== value) {
this.$refs.input.value = formattedValue
}
// 通过 input 事件发出数值
this.$emit('input', Number(formattedValue))
}
}
})