首先創(chuàng)建一個簡單的vue應用
# 全局安裝 vue-cli
$ npm i -g vue-cli
# 創(chuàng)建一個簡單的vue倉庫
$ vue init webpack-simple learnvue
# 安裝依賴
$ cd learnvue
$ npm i
$ npm run dev
class綁定——動態(tài)地切換class
- 在:class上綁定一個對象
- 在:class上綁定一個對象名
- 在:class上綁定一個返回對象的計算屬性
- 在:class上綁定一個數(shù)組,數(shù)組語法中也可以使用對象語法
- 三元表達式:根據(jù)條件切換列表中的class
注意:普通class和綁定class可以共存
<template>
<div id="app">
{{msg}}
<!-- 1.在在:class上綁定一個對象 -->
<div :class="{active:isActive}" class="header">
我是header,我有一個普通class和一個可能存在的class對象
</div>
<div :class="{bigger:isBigger,'text-danger':hasError}" class="main">
我是main,我有一個普通class和一個可能存在的class對象
</div>
<!-- 在:class上綁定一個對象名 -->
<div :class="classObj1">
我是小字,我有背景色
</div>
<!-- 在:class上綁定一個返回對象的計算屬性 -->
<div :class="classObj2">
我是大字,沒有背景色
</div>
<!--在:class上綁定一個數(shù)組,可使用三元表達式,數(shù)組語法中也可以使用對象語法 -->
<div :class="['text-danger',classObj1]">我始終是紅色字,可能是大字也可能是小字,有或者沒有綠色背景</div>
<div :class="[classObj2,{'text-danger':hasError}]">我現(xiàn)在是大字且為紅色,沒有背景色</div>
<div :class="[isActive? '' :'text-danger']">我是紅字</div>
</div>
</template>
<script>
export default {
name: 'app',
data() {
return {
msg: 'Welcome to Your Vue.js App',
isActive: 0,
isBigger: '',
hasError: true,
classObj1: {
active: !this.isActive,
bigger: this.isBigger
}
}
},
computed: {
classObj2() {
return {
active: this.isActive && this.hasError,
bigger: !this.isBigger && this.hasError
}
}
}
}
</script>
<style>
.active {
background: green;
}
.header {
font-size: 2em;
}
.bigger {
font-size: 4em;
}
.text-danger {
color: red;
}
.main {
font-style: italic;
}
</style>

style綁定——綁定內聯(lián)樣式
- 看著很像css內聯(lián)樣式,屬性名可用駝峰式或用短橫線分隔,短橫線分隔要用單引號引起來
- 直接綁定到一個樣式對象
- 結合計算屬性使用
- 數(shù)組語法
當 v-bind:style 使用需要添加瀏覽器引擎前綴的 CSS 屬性時,如 transform,Vue.js 會自動偵測并添加相應的前綴。
<template>
<div id="app">
{{msg}}
<div :style="{color:activeColor,fontSize:fontSize+'px'}">
hello world
</div>
<div :style="styleObj1">
hello dot
</div>
<div :style="styleObj2">
hello dolby
</div>
<div :style="[styleObj1,styleObj2]">
hello dolby
</div>
</div>
</template>
<script>
export default {
name: 'app',
data() {
return {
msg: 'Welcome to Your Vue.js App',
activeColor: 'red',
fontSize: 12,
styleObj1: {
background: 'yellow',
color: 'pink'
}
}
},
computed: {
styleObj2() {
return {
fontStyle: 'italic',
fontWeight: 'bolder'
}
}
}
}
</script>

vue不得不說的生命周期
當一個vue實例被創(chuàng)建時,它向 Vue 的響應式系統(tǒng)中加入了其 data 對象中能找到的所有的屬性。當這些屬性的值發(fā)生改變時,視圖將會產生“響應”,即匹配更新為新的值。
看下圖:

一個實例的生命周期分為creat、mount、update、destroy四個階段,它們的分工各有不同:
- create:創(chuàng)建
- mount:掛載DOM
- update:數(shù)據(jù)更新
- destroy:銷毀(一般為手動清理事件和定時器)
同時在這些過程中也會運行一些叫做生命周期鉤子的函數(shù),這給了用戶在不同階段添加自己的代碼的機會。對應生命周期來看,又有以下8個鉤子:
beforeCreate、created、beforeMount、mounted、beforeUpdate、updated、beforeDestroy、destroyed。
看一個例子:
<template>
<div id="app">
{{msg}}
</div>
</template>
<script>
export default {
name: 'app',
data() {
return {
msg: 'Welcome to Your Vue.js App',
}
},
beforeCreate() {
console.log('beforeCreate')
},
created() {
console.log('created')
setTimeout(() => {
this.msg = 'change msg'
}, 1000);
},
beforeMount() {
console.log('beforeMount')
},
mounted() {
console.log('mounted')
},
beforeUpdate() {
console.log('beforeUpdate')
},
updated() {
console.log('updated')
},
beforeDestroy() {
console.log('beforeDestroy')
},
destroyed() {
console.log('destroyed')
},
methods: {
clickBtn() {
alert('hello')
}
},
watch: {
msg() {
console.log('hello')
}
}
}
</script>
打開localhost:8080,頁面顯示初始msg內容,控制臺打印beforeCreate、created、beforeMount、mounted

大約1s之后,頁面內容發(fā)生變化,緊接著watch生效,控制臺一次打印出hello,beforeUpdate,updated,這里沒有打印出beforeDestroy和destroyed是因為頁面上沒有事件。

v-model
單行文本
v-model 指令用于在表單 <input> 及 <textarea> 元素上創(chuàng)建雙向數(shù)據(jù)綁定。它會根據(jù)控件類型自動選取正確的方法來更新元素,但 v-model 本質上不過是語法糖。它負責監(jiān)聽用戶的輸入事件以更新數(shù)據(jù),并對一些極端場景進行一些特殊處理。
<template>
<div id="app">
<input type="text" placeholder="edit me" v-value="hello">
</div>
</template>
以上代碼的效果是頁面上會出現(xiàn)一個input輸入框且有初始值value

當我們在input中使用v-model用于綁定數(shù)據(jù)時,value值不生效
<template>
<div id="app">
<input type="text" placeholder="edit me" v-model="message" value="hello">
</div>
</template>
<script>
export default {
name: 'app',
data() {
return {
message: '',
}
},
}
</script>

v-model 會忽略所有表單元素的 value、checked、selected 特性的初始值而總是將 Vue 實例的數(shù)據(jù)作為數(shù)據(jù)來源。如果有需要,我們應在組件的 data 選項中聲明初始值。
<template>
<div id="app">
<input type="text" placeholder="edit me" v-model="value">
</div>
</template>
<script>
export default {
name: 'app',
data() {
return {
message: '',
value: 'init',
}
},
}
</script>

現(xiàn)在結合前面學到的看一段代碼:
<template>
<div id="app">
<input type="text" placeholder="edit me" v-model="message">
<button @click="clickBtn">Click me</button>
<p :style="styleObj">Message is: {{message}}</p>
</div>
</template>
<script>
export default {
name: 'app',
data() {
return {
message: '',
isClick: ''
}
},
computed: {
styleObj() {
return {
color: this.isClick && this.message ? 'red' : '',
background: this.isClick && this.message ? 'yellow' : ''
}
}
},
methods: {
clickBtn() {
if (this.message) {
this.isClick = true
}
}
},
}
</script>
我們?yōu)閕nput定義了一個placeholder值為'edit me'并綁定了一個message屬性

首先看input里綁定的message和p中message之間的關系,前面說v-model雙向綁定不過是語法糖,實際上還是單向綁定。
當input中的內容發(fā)生變化,也就是input中綁定的message——來自于data中的message屬性值發(fā)生了變化,p中的message也會隨之改變,所以數(shù)據(jù)改變是單向的從data中的message屬性到頁面上的message屬性變換的過程。所以p中的文本會隨著input中的內容改變而改變。

我們再來看看data中的另一個屬性'isClick',我們給它賦值為一個空的字符串,即布爾值false
繼續(xù)看代碼,在button按鈕上定義了一個單擊事件'clickBtn',在p元素上綁定了一個內聯(lián)樣式'styleObj',我們把樣式對象放在computed中,這樣就會返回計算后的屬性對象。當用戶點擊按鈕時,如果input中有輸入,我們將'isClick'值設為true,反應在頁面上就是輸入不為空的情況下點擊按鈕之后p元素有了黃色背景,字色變?yōu)榧t色。

多行文本
<template>
<div id="app">
<span>Multiline message is:</span>
<p style="white-space: pre-line;">{{ message }}</p>
<br>
<textarea v-model="message" placeholder="add multiple lines"></textarea>
</div>
</template>
<script>
export default {
name: 'app',
data() {
return {
message: '',
}
},
}
</script>



在文本區(qū)域插值 (<textarea></textarea>) 并不會生效,應用 v-model 來代替。
復選框
- 單個復選框綁定到布爾值
<template>
<div id="app">
<input type="checkbox" v-model="checked">
<label for="checkbox">{{checked}}</label>
</div>
</template>
<script>
export default {
name: 'app',
data() {
return {
checked: false,
}
},
}
</script>


- 多個復選框綁定到同一數(shù)組
<template>
<div id="app">
<input type="checkbox" value="Jack" v-model="checkedArr">
<label for="jack">Jack</label>
<input type="checkbox" value="John" v-model="checkedArr">
<label for="john">John</label>
<input type="checkbox" value="Mike" v-model="checkedArr">
<label for="mike">Mike</label>
<br>
<span>Checked names: {{ checkedArr }}</span>
</div>
</template>
<script>
export default {
name: 'app',
data() {
return {
checkedArr: [],
}
},
}
</script>
綁定到checkedArr中的值是input中的value

單選按鈕
<template>
<div id="app">
<input type="radio" value="Jack" v-model="picked">
<label for="jack">Jack</label>
<input type="radio" value="John" v-model="picked">
<label for="john">John</label>
<input type="radio" value="Mike" v-model="picked">
<label for="mike">Mike</label>
<br>
<span>picked names: {{ picked }}</span>
</div>
</template>
<script>
export default {
name: 'app',
data() {
return {
picked: '',
}
},
}
</script>

選擇框
- 單選時
<template>
<div id="app">
<select v-model="selected">
<option disabled value="">請選擇</option>
<option>A</option>
<option>B</option>
<option>C</option>
</select>
<span>Selected: {{ selected }}</span>
</div>
</template>
<script>
export default {
name: 'app',
data() {
return {
selected: '',
}
},
}
</script>

如果 v-model 表達式的初始值未能匹配任何選項,<select> 元素將被渲染為“未選中”狀態(tài)。這種情況下iOS 不會觸發(fā) change 事件,這會使用戶無法選擇第一個選項。因此推薦像上面這樣提供一個值為空的禁用選項。
- 多選時
<template>
<div id="app">
<select v-model="selectedArr" multiple >
<option>A</option>
<option>B</option>
<option>C</option>
</select>
<span>SelectedArr: {{ selectedArr }}</span>
</div>
</template>
<script>
export default {
name: 'app',
data() {
return {
selectedArr: [],
}
},
}
</script>

用v-for渲染的動態(tài)項
<template>
<div id="app">
<select v-model="selected">
<option v-for="option in options" :value="option.value">{{option.text}}</option>
</select>
<span>Selected: {{ selected }}</span>
</div>
</template>
<script>
export default {
name: 'app',
data() {
return {
selected: 'A',
options: [
{ value: 'A', text: 'One' },
{ value: 'B', text: 'Two' },
{ value: 'C', text: 'Three' },
]
}
}
}

computed
接下來我們來說一個前面反復提到的計算屬性對象computed,計算屬性可以應用于各種復雜的邏輯,使代碼更加輕便容易維護。
語法:computed: {xxx: function () { return /* */}}
<template>
<div id="app">
<p>Original message: "{{ message }}"</p>
<p>Computed reversed message: "{{ reversedMessage }}"</p>
</div>
</template>
<script>
export default {
name: 'app',
data() {
return {
message: 'hello'
}
},
computed: {
reversedMessage() {
return this.message.split('').reverse().join('')
}
},
}

可以像綁定普通屬性一樣在模板中綁定計算屬性。Vue 知道 reversedMessage 依賴于message,因此當 message 發(fā)生改變時,所有依賴 reversedMessage 的綁定也會更新。而且最妙的是用計算屬性沒有副作用,更易于測試和理解。
methods
看了上面computed的例子你會發(fā)現(xiàn),我們在methods中使用方法可達到同樣的效果:
<template>
<div id="app">
<p>Original message: "{{ message }}"</p>
<p>Methods reversed message: "{{ reversedMessage() }}"</p>
</div>
</template>
<script>
export default {
name: 'app',
data() {
return {
message: 'hello'
}
},
methods: {
reversedMessage() {
return this.message.split('').reverse().join('')
}
},
}
我們可以將同一函數(shù)定義為一個方法而不是一個計算屬性。兩種方式的最終結果確實是完全相同的。不同的是計算屬性是基于它們的依賴進行緩存的,只有在它的相關依賴發(fā)生改變時才會重新求值。這就意味著只要 message 還沒有發(fā)生改變,多次訪問 reversedMessage 計算屬性會立即返回之前的計算結果,而不必再次執(zhí)行函數(shù)。
這也同樣意味著下面的計算屬性將不再更新,因為 Date.now() 不是響應式依賴:
<template>
<div id="app">
<p>{{ now }}</p>
<p>{{ now }}</p>
<p>{{ now }}</p>
<p>{{ now }}</p>
</div>
</template>
<script>
export default {
name: 'app',
computed: {
now() {
return Date.now()
}
},
}

相比之下,每當觸發(fā)重新渲染時,調用方法總會再次執(zhí)行函數(shù)。
<template>
<div id="app">
<p>{{ now() }}</p>
<p>{{ now() }}</p>
<p>{{ now() }}</p>
<p>{{ now() }}</p>
</div>
</template>
<script>
export default {
name: 'app',
methods: {
now() {
return Date.now()
}
},
}

我們?yōu)槭裁葱枰彺??假設我們有一個性能開銷比較大的的計算屬性 A,它需要遍歷一個巨大的數(shù)組并做大量的計算。然后我們可能有其他的計算屬性依賴于 A 。如果沒有緩存,我們將不可避免的多次執(zhí)行 A 的 getter函數(shù),如果你不希望有緩存,請用方法來替代。
前面說過了computed計算屬性與methods方法的比較,現(xiàn)在說說計算屬性和偵聽屬性watch的比較。
<template>
<div id="app">
{{ fullName }}
</div>
</template>
<script>
export default {
name: 'app',
data() {
return {
firstName: 'Foo',
lastName: 'Bar',
fullName: 'Foo Bar'
}
},
watch: {
firstName: function(val) {
this.fullName = val + ' ' + this.lastName
},
lastName: function(val) {
this.fullName = this.firstName + ' ' + val
}
},
}
以上做法是命令式的watch回調,而且代碼重復,也沒辦法做到響應式,沒有任何意義??碿omputed如何實現(xiàn)
<template>
<div id="app">
{{ fullName }}
</div>
</template>
<script>
export default {
name: 'app',
data() {
return {
firstName: 'Foo',
lastName: 'Bar',
}
},
computed: {
fullName() {
return this.firstName + ' ' + this.lastName
}
},
}
代碼簡潔直觀,且當firstName或lastName改變時,fullName也會隨之改變并反映到頁面上。
雖然計算屬性在大多數(shù)情況下更合適,但有時也需要一個自定義的偵聽器,于是就有了watch。
watch
當需要在數(shù)據(jù)變化時執(zhí)行異步或開銷較大的操作時,watch是最有用響應數(shù)據(jù)變化的方法,一般用于監(jiān)聽input等有輸入操作的場景。
<template>
<div id="app">
{{msg}}
<input v-model="question">
<div>{{answer}}</div>
</div>
</template>
<script>
export default {
name: 'app',
created() {
console.log('')
setTimeout(() => {
this.msg = '12121212'
}, 1000);
},
data() {
return {
msg: 'Welcome to Your Vue.js App',
question: '',
answer: 'I cannot give you an answei until you ask a question'
}
},
watch: {
question() {
this.answer = 'Waiting for you to stop typing...'
},
msg() {
alert('hello')
}
}
}
</script>
以上代碼在http://localhost:8080/中打開后,頁面正常顯示數(shù)據(jù),接著先彈出hello

點擊確定后msg值變?yōu)閟etTimeout中的12121212

在輸入框中輸入任意內容,answer值變?yōu)槲覀冊O定的字符串
