我從未見過如此簡(jiǎn)潔易懂的Vue教程
這是一篇長(zhǎng)文,按照我自己的邏輯重新整理一下,包含所有Vue的基礎(chǔ)知識(shí)點(diǎn)。
但是我更建議你先簡(jiǎn)略的閱讀官方的文檔,因?yàn)楸疚木哂幸欢ǖ拈喿x門檻,同時(shí)我也竭盡所能把這門檻降到最低,同樣你也可以把本文作為快速回憶教程。
發(fā)揮100%的專注力,調(diào)動(dòng)體內(nèi)所有的熱情,你將做到很多令人驚嘆的事情
什么是MVVM ?
對(duì)比以前的mvc
或者mvp
,就是把C
或者p
替換成vm
。
vm
就是上圖,請(qǐng)仔細(xì)看一些細(xì)節(jié),vm
監(jiān)聽DOM
,當(dāng)數(shù)據(jù)改變的時(shí)候,vm
會(huì)去自動(dòng)更新視圖。
面向未來的組件系統(tǒng)
實(shí)現(xiàn)了一些未來的w3c
規(guī)范(暫不贅述)
- Web 組件規(guī)范
- Slot API
根 vue 實(shí)例
let viewModel = new Vue({
// 包含數(shù)據(jù)、模板、掛載元素、方法、生命周期鉤子等選項(xiàng)
})
Hello Wrold 例子
<!-- 這是我們的 View -->
<div id="app">
Hello {{ name }}!
</div>
// 這是我們的 Model
var model = {
name: 'Vue.js'
}
// 創(chuàng)建一個(gè) Vue 實(shí)例或 "viewModel"
// 它連接 View 與 Model
var viewModel = new Vue({
el: '#app',
data: model
})
組件 Component 構(gòu)造器
vue.extend()
返回的只是一個(gè)構(gòu)造器,我們需要通過vue.extend()
的返回值和new
關(guān)鍵字創(chuàng)建實(shí)例。
當(dāng)我們注冊(cè)為組件的時(shí)候,內(nèi)部就已經(jīng)幫我們創(chuàng)建好了實(shí)例。
Tip: 如果在實(shí)例創(chuàng)建之后添加新的屬性到實(shí)例上,它不會(huì)觸發(fā)視圖更新。
<div id="app">
<v-footer></v-footer> <!-- 加一個(gè)v前綴,跟原生的footer標(biāo)簽區(qū)別開來 -->
</div>
let Footer = vue.extend({
template: '<div>我是頁腳組件</div>'
})
// 注冊(cè)為全局組件
Vue.component('v-footer', MyComponent)
// 創(chuàng)建根實(shí)例
new Vue({
el: '#app'
})
生命周期
上圖的就是
ViewModel
的生命周期,仔細(xì)的看其實(shí)并不難。目前先了解一下。
在傳統(tǒng)軟件工程中,特別是QT類桌面端軟件,都有軟件的生命周期,還有比如Android
的生命周期,React
的生命周期。
目前的前端趨勢(shì)正在向此方面靠近。
流程大致像這樣
created()->beforeCompile()->compiled()->ready()
->attached()->detached()->beforeDestroy()->destroyed()
更詳細(xì)的介紹,請(qǐng)點(diǎn)這里查看API文檔
綁定
簡(jiǎn)單的理解就是模板字符串功能,放心的在任何你想用的地方去用,假如錯(cuò)了vue
會(huì)給你提示。
定界符都是可以修改的
// 模板定界符
Vue.config.delimiters = ['{{', '}}']
// html模板定界符
Vue.config.unsafeDelimiters = ["{{{", "}}}"]
數(shù)據(jù)的綁定
<span>消息: {{ msg }}</span> <!-- 同步更新js里面的數(shù)據(jù) -->
<span>他將永不會(huì)改變: {{* msg }}</span> <!-- 第一次插入之后就不更新了 -->
<div>{{{ raw_html }}}</div> <!-- 插入原生的 html -->
<div id="item-{{ id }}"></div> <!-- 放在id中 -->
表達(dá)的綁定
不可使用,var
/let
關(guān)鍵字聲明變量,也不能使用if
/for
流程控制。
{{ number + 1 }} // 做簡(jiǎn)單的運(yùn)算
{{ ok ? 'YES' : 'NO' }} // 三元表達(dá)式
{{ message.split('').reverse().join('') }} // 調(diào)用該對(duì)象上的方法
過濾器
對(duì)數(shù)據(jù)進(jìn)行相應(yīng)的處理,message
為第一個(gè)參數(shù)
、filter
為要執(zhí)行的函數(shù)
。
{{ message | filter }}
{{ message | filterA | filterB }} // filterB(filterA(message))
{{ message | filterA 'arg1' arg2 }}
// arg2是一個(gè)表達(dá)式(假設(shè)是1+2) filterA(message,arg1,3)
指令
當(dāng)其表達(dá)式的值改變時(shí)把某些特殊的行為應(yīng)用到 DOM 上。
<p v-if="ok">Hello!</p> <!-- 根據(jù)if里面的值,確定是否編譯 -->
<a v-bind:href="url"></a>
<!-- 等于href="{{url}}" 這里 href 是參數(shù),將元素的 href 屬性傳進(jìn)去。
告訴vue元素的 href 特性跟表達(dá)式 url 的值綁定 -->
<a v-on:click="doSomething">
<!-- v-on表示監(jiān)聽,傳入了click參數(shù),表示當(dāng)click事件發(fā)生的時(shí)候,執(zhí)行doSomething函數(shù) -->
<a v-bind:href.literal="/a/b/c"></a>
<!-- .literal 修飾符告訴指令將它的值解析為一個(gè)字面字符串而不是一個(gè)表達(dá)式 -->
v-bind 縮寫
<!-- 完整語法 -->
<a v-bind:href="url"></a>
<!-- 縮寫 -->
<a :href="url"></a>
<!-- 完整語法 -->
<a v-on:click="doSomething"></a>
<!-- 縮寫 -->
<a @click="doSomething"></a>
計(jì)算屬性
<div id="example">
a={{ a }}, b={{ b }}
</div>
var vm = new Vue({
el: '#example',
data: {
a: 1
},
computed: {
// 一個(gè)計(jì)算屬性的 getter
b: function () {
// `this` 指向 vm 實(shí)例
return this.a + 1
}
}
})
給計(jì)算屬性設(shè)置setter
computed: {
fullname: {
get: function() {
return this.firstName + ' ' + this.lastName
},
set: function() {
let names = newValue.split(' ')
this.firstName = names[0]
this.lastName = names[names.length - 1]
}
}
}
$watch
通常更優(yōu)的做法是使用computed
計(jì)算屬性
<div id="app">{{ fullname }}</div>
let vm = new Vue({
data: {
firstName: 'C',
lastName: 'O',
fullName: 'CO'
}
})
vm.$watch('firstname', funciton(val){
this.fullname = val + ' ' + this.lastName
})
vm.$watch('lastname', funciton(val){
this.fullname = this.firstName + ' ' + val
})
// 等價(jià)于
let vm = new Vue({
data: {
firstName: 'Foo',
lastName: 'Bar'
},
computed: {
fullName: function(){
return this.firstName + ' ' + this.lastName
}
}
})
Class 與 Style 綁定
vue特意增強(qiáng)了他們,支持對(duì)象和數(shù)組綁定
Class 對(duì)象綁定
<div class="static" :class="{ 'class-a': isA , 'class-b': isB}"></div>
data: {
isA: true,
isB: false
}
<div class="static" :class="classObjcet"></div>
data: {
classObject: {
'class-a': true
'class-b': false
}
}
//渲染之后
<div class="static class-a"></div>
Class 數(shù)組語法
<div :class="[classA,classB]"></div>
data: {
classA: 'class-a'
classB: 'class-b'
}
// 渲染為
<div class="class-a class-b"></div>
<div :class="[classA, isB? classB : '']"></div>
// 始終添加classA對(duì)應(yīng)的類名,根據(jù)isB的值確認(rèn)是否添加classB對(duì)應(yīng)的值。
// 在1.0.19+之后,為了更明了的寫法,支持?jǐn)?shù)組里面嵌套對(duì)象語法
<div :class="[classA, {classB: isB, classC: isC}]"></div>
Style 對(duì)象語法
CSS 屬性名可以用駝峰式(camelCase)或短橫分隔命名(kebab-case),自動(dòng)添加瀏覽器的前綴。
<div :style="{color: activeColor, fontSize: fontSize + 'px' }"></div>
data: {
activeColor: 'red',
fontSize: 30
}
<div :style="styleObject"></div>
data = {
styleObject: {
color: 'red',
fontSize: '13px'
}
}
Style 數(shù)組語法
<div :style="[styleObjectA,styleObjectB]"></div>
data = {
styleObjectA: {
fontSize: '15px'
}
}
條件渲染
** Tip: v-else 元素必須立即跟在 v-if 或 v-show 元素的后面——否則它不能被識(shí)別。**
v-if
<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>
v-show
不支持 template 元素包裹
<h1 v-show="ok">Hello!</h1>
// 在組件上不能使用 v-else
<custom-component v-show="condition"></custom-component>
<p v-show="!condition">這可能也是一個(gè)組件</p>
if 與 show 之間的戰(zhàn)爭(zhēng)
如果需要頻繁切換 v-show 較好,如果在運(yùn)行時(shí)條件不大可能改變 v-if 較好。
列表渲染
<ul id="list">
<li v-for="item in items">
{{ item.message }}
</li>
</ul>
var vm = new Vue({
el: '#list',
data: {
items: [
{ message: 'Foo' },
{ message: 'Bar'}
]
}
})
通過$index
可以訪問索引,且在v-for
塊內(nèi)可以訪問的到其他屬性。
<ul id="list">
<li v-for="item in items">
{{ parentMessage }} - {{ $index }} - {{ item.message }}
</li>
</ul>
var vm = new Vue({
el: '#list',
data: {
parentMessage: 'Parent',
items: [
{message: 'Foo'},
{message: 'Bar'}
]
}
})
為索引設(shè)置一個(gè)別名,且 1.0.17+ 之后可以使用 for of
<div v-for="(key,value) of items">
</div>
// 使用一層 template 包裹
<template v-for="item in items">
<span>{{ item.id }}</span>
<span>{{ item.content }}</span>
</template>
數(shù)組變動(dòng)檢測(cè)
以下是vue提供的一些數(shù)組上的方法,能觸發(fā)視圖更新。
- push()
- pop()
- shift()
- unshift()
- splice()
- sort()
- reverse()
替換數(shù)組
當(dāng)我們使用一些不改變數(shù)組本身的方法的時(shí)候(純函數(shù)),我們可以直接賦值給自身,雖然替換了原始的數(shù)組,但是vue不會(huì)重新渲染所有,他會(huì)去進(jìn)行對(duì)比。
track-by
通過此選項(xiàng)設(shè)置參考特征,用特征對(duì)比,一樣就進(jìn)行復(fù)用
{
items: [
{_uid: '339a99', ... }
{_uid: '2r9c92', ... }
]
}
<div v-for="item in items" track-by="_uid">
<!-- content -->
</div>
// _uid 就說明可以復(fù)用
track-by="$index"
這樣讓數(shù)據(jù)替換高效,此時(shí)DOM節(jié)點(diǎn)不再映射數(shù)組順序變化,不能同步臨時(shí)狀態(tài)。
v-for
包含 <input>
元素或者子組件,要小心使用
更新問題
vue不能檢測(cè)下面數(shù)組的變化
- 直接用索引設(shè)置元素,如 vm.items[0] = {};
- 修改數(shù)據(jù)的長(zhǎng)度,如 vm.items.length = 0。
(1) 解決方法
vm.items.$set(0, {})
(2) 解決方法
vm.items = []
對(duì)象v-for
<ul id="repeat-object" class="demo">
<li v-for="value in object">
{{ $key }} : {{ value }}
</li>
</ul>
<div>
<span v-for="n in 10">{{ n }} </span>
</div>
方法與事件處理
<div id="example">
<button @click="greet">Greet</button>
</div>
let vm = new Vue({
el: '#example',
data: {
name: 'Vue.js'
},
methods: {
greet: function(event) {
alert('hello'+this.name+'!')
console.log(event.target.tagName)
}
}
})
<div id="example">
<button :click="say('hi')">Say Hi</button>
<button :click="say('what')">Say What</button>
</div>
new Vue({
el: '#example',
methods: {
say: function(msg) {
alert(msg)
}
}
})
事件修飾符
在事件處理器中經(jīng)常需要調(diào)用event.preventDefault
或 event.stopPropagation
// 阻止單擊事件冒泡
<a @click.stop="do"></a>
// 提交事件不再重載頁面
<a @submit.prevent="submit"></a>
// 修飾符可以串聯(lián)
<a @click.stop.prevent="do"></a>
// 只有修飾符
<form @submit.prevent></form>
按鍵修飾符
- enter
- tab
- delete
- esc
- space
- up
- down
- left
- right
<!-- 只有在 keyCode 是 13 時(shí)調(diào)用 vm.submit() -->
<input v-on:keyup.13="submit">
<!-- 同上 -->
<input v-on:keyup.enter="submit">
<!-- 縮寫語法 -->
<input @keyup.enter="submit">
自定義按鍵別名
// 可以使用 @keyup.f1
Vue.directive('on').keyCodes.f1 = 112