首先創(chuàng)建一個(gè)簡單的vue應(yīng)用
# 全局安裝 vue-cli
$ npm i -g vue-cli
# 創(chuàng)建一個(gè)簡單的vue倉庫
$ vue init webpack-simple learnvue
# 安裝依賴
$ cd learnvue
$ npm i
$ npm run dev
class綁定——?jiǎng)討B(tài)地切換class
- 在:class上綁定一個(gè)對象
- 在:class上綁定一個(gè)對象名
- 在:class上綁定一個(gè)返回對象的計(jì)算屬性
- 在:class上綁定一個(gè)數(shù)組填帽,數(shù)組語法中也可以使用對象語法
- 三元表達(dá)式:根據(jù)條件切換列表中的class
注意:普通class和綁定class可以共存
<template>
<div id="app">
{{msg}}
<!-- 1.在在:class上綁定一個(gè)對象 -->
<div :class="{active:isActive}" class="header">
我是header溶耘,我有一個(gè)普通class和一個(gè)可能存在的class對象
</div>
<div :class="{bigger:isBigger,'text-danger':hasError}" class="main">
我是main念颈,我有一個(gè)普通class和一個(gè)可能存在的class對象
</div>
<!-- 在:class上綁定一個(gè)對象名 -->
<div :class="classObj1">
我是小字球散,我有背景色
</div>
<!-- 在:class上綁定一個(gè)返回對象的計(jì)算屬性 -->
<div :class="classObj2">
我是大字七芭,沒有背景色
</div>
<!--在:class上綁定一個(gè)數(shù)組拷沸,可使用三元表達(dá)式介衔,數(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綁定——綁定內(nèi)聯(lián)樣式
- 看著很像css內(nèi)聯(lián)樣式,屬性名可用駝峰式或用短橫線分隔央碟,短橫線分隔要用單引號(hào)引起來
- 直接綁定到一個(gè)樣式對象
- 結(jié)合計(jì)算屬性使用
- 數(shù)組語法
當(dāng) v-bind:style
使用需要添加瀏覽器引擎前綴的 CSS 屬性時(shí)税灌,如 transform
,Vue.js 會(huì)自動(dòng)偵測并添加相應(yīng)的前綴亿虽。
<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不得不說的生命周期
當(dāng)一個(gè)vue實(shí)例被創(chuàng)建時(shí)菱涤,它向 Vue 的響應(yīng)式系統(tǒng)中加入了其 data 對象中能找到的所有的屬性。當(dāng)這些屬性的值發(fā)生改變時(shí)洛勉,視圖將會(huì)產(chǎn)生“響應(yīng)”粘秆,即匹配更新為新的值。
看下圖:
一個(gè)實(shí)例的生命周期分為creat收毫、mount攻走、update殷勘、destroy四個(gè)階段,它們的分工各有不同:
- create:創(chuàng)建
- mount:掛載DOM
- update:數(shù)據(jù)更新
- destroy:銷毀(一般為手動(dòng)清理事件和定時(shí)器)
同時(shí)在這些過程中也會(huì)運(yùn)行一些叫做生命周期鉤子的函數(shù)昔搂,這給了用戶在不同階段添加自己的代碼的機(jī)會(huì)玲销。對應(yīng)生命周期來看,又有以下8個(gè)鉤子:
beforeCreate摘符、created痒玩、beforeMount、mounted议慰、beforeUpdate蠢古、updated、beforeDestroy别凹、destroyed草讶。
看一個(gè)例子:
<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內(nèi)容炉菲,控制臺(tái)打印beforeCreate堕战、created、beforeMount拍霜、mounted
大約1s之后文虏,頁面內(nèi)容發(fā)生變化,緊接著watch生效退敦,控制臺(tái)一次打印出hello,beforeUpdate,updated叽掘,這里沒有打印出beforeDestroy和destroyed是因?yàn)轫撁嫔蠜]有事件。
v-model
單行文本
v-model 指令用于在表單 <input>
及 <textarea>
元素上創(chuàng)建雙向數(shù)據(jù)綁定道偷。它會(huì)根據(jù)控件類型自動(dòng)選取正確的方法來更新元素缀旁,但 v-model 本質(zhì)上不過是語法糖。它負(fù)責(zé)監(jiān)聽用戶的輸入事件以更新數(shù)據(jù)勺鸦,并對一些極端場景進(jìn)行一些特殊處理并巍。
<template>
<div id="app">
<input type="text" placeholder="edit me" v-value="hello">
</div>
</template>
以上代碼的效果是頁面上會(huì)出現(xiàn)一個(gè)input輸入框且有初始值value
當(dāng)我們在input中使用v-model用于綁定數(shù)據(jù)時(shí),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 會(huì)忽略所有表單元素的 value换途、checked懊渡、selected 特性的初始值而總是將 Vue 實(shí)例的數(shù)據(jù)作為數(shù)據(jù)來源。如果有需要军拟,我們應(yīng)在組件的 data 選項(xiàng)中聲明初始值剃执。
<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)在結(jié)合前面學(xué)到的看一段代碼:
<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定義了一個(gè)placeholder值為'edit me'并綁定了一個(gè)message屬性
首先看input里綁定的message和p中message之間的關(guān)系,前面說v-model雙向綁定不過是語法糖吻谋,實(shí)際上還是單向綁定忠蝗。
當(dāng)input中的內(nèi)容發(fā)生變化,也就是input中綁定的message——來自于data中的message屬性值發(fā)生了變化漓拾,p中的message也會(huì)隨之改變阁最,所以數(shù)據(jù)改變是單向的從data中的message屬性到頁面上的message屬性變換的過程戒祠。所以p中的文本會(huì)隨著input中的內(nèi)容改變而改變。
我們再來看看data中的另一個(gè)屬性'isClick'速种,我們給它賦值為一個(gè)空的字符串姜盈,即布爾值false
繼續(xù)看代碼,在button按鈕上定義了一個(gè)單擊事件'clickBtn'配阵,在p元素上綁定了一個(gè)內(nèi)聯(lián)樣式'styleObj'馏颂,我們把樣式對象放在computed中,這樣就會(huì)返回計(jì)算后的屬性對象棋傍。當(dāng)用戶點(diǎn)擊按鈕時(shí)救拉,如果input中有輸入,我們將'isClick'值設(shè)為true瘫拣,反應(yīng)在頁面上就是輸入不為空的情況下點(diǎn)擊按鈕之后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>
) 并不會(huì)生效麸拄,應(yīng)用 v-model 來代替派昧。
復(fù)選框
- 單個(gè)復(fù)選框綁定到布爾值
<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>
- 多個(gè)復(fù)選框綁定到同一數(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>
選擇框
- 單選時(shí)
<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 表達(dá)式的初始值未能匹配任何選項(xiàng),<select>
元素將被渲染為“未選中”狀態(tài)拢切。這種情況下iOS 不會(huì)觸發(fā) change 事件蒂萎,這會(huì)使用戶無法選擇第一個(gè)選項(xiàng)。因此推薦像上面這樣提供一個(gè)值為空的禁用選項(xiàng)淮椰。
- 多選時(shí)
<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渲染的動(dòng)態(tài)項(xiàng)
<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
接下來我們來說一個(gè)前面反復(fù)提到的計(jì)算屬性對象computed五慈,計(jì)算屬性可以應(yīng)用于各種復(fù)雜的邏輯,使代碼更加輕便容易維護(hù)实苞。
語法: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('')
}
},
}
可以像綁定普通屬性一樣在模板中綁定計(jì)算屬性豺撑。Vue 知道 reversedMessage 依賴于message,因此當(dāng) message 發(fā)生改變時(shí)黔牵,所有依賴 reversedMessage 的綁定也會(huì)更新。而且最妙的是用計(jì)算屬性沒有副作用爷肝,更易于測試和理解猾浦。
methods
看了上面computed的例子你會(huì)發(fā)現(xiàn),我們在methods中使用方法可達(dá)到同樣的效果:
<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ù)定義為一個(gè)方法而不是一個(gè)計(jì)算屬性灯抛。兩種方式的最終結(jié)果確實(shí)是完全相同的金赦。不同的是計(jì)算屬性是基于它們的依賴進(jìn)行緩存的,只有在它的相關(guān)依賴發(fā)生改變時(shí)才會(huì)重新求值对嚼。這就意味著只要 message 還沒有發(fā)生改變夹抗,多次訪問 reversedMessage 計(jì)算屬性會(huì)立即返回之前的計(jì)算結(jié)果,而不必再次執(zhí)行函數(shù)纵竖。
這也同樣意味著下面的計(jì)算屬性將不再更新漠烧,因?yàn)?Date.now() 不是響應(yīng)式依賴:
<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()
}
},
}
相比之下杏愤,每當(dāng)觸發(fā)重新渲染時(shí),調(diào)用方法總會(huì)再次執(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)槭裁葱枰彺嫔郝ィ考僭O(shè)我們有一個(gè)性能開銷比較大的的計(jì)算屬性 A,它需要遍歷一個(gè)巨大的數(shù)組并做大量的計(jì)算度液。然后我們可能有其他的計(jì)算屬性依賴于 A 厕宗。如果沒有緩存,我們將不可避免的多次執(zhí)行 A 的 getter函數(shù)堕担,如果你不希望有緩存已慢,請用方法來替代。
前面說過了computed計(jì)算屬性與methods方法的比較霹购,現(xiàn)在說說計(jì)算屬性和偵聽屬性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回調(diào),而且代碼重復(fù)厕鹃,也沒辦法做到響應(yīng)式兢仰,沒有任何意義〖敛辏看computed如何實(shí)現(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
}
},
}
代碼簡潔直觀把将,且當(dāng)firstName或lastName改變時(shí),fullName也會(huì)隨之改變并反映到頁面上忆矛。
雖然計(jì)算屬性在大多數(shù)情況下更合適察蹲,但有時(shí)也需要一個(gè)自定義的偵聽器,于是就有了watch催训。
watch
當(dāng)需要在數(shù)據(jù)變化時(shí)執(zhí)行異步或開銷較大的操作時(shí)洽议,watch是最有用響應(yīng)數(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
點(diǎn)擊確定后msg值變?yōu)閟etTimeout中的12121212
在輸入框中輸入任意內(nèi)容,answer值變?yōu)槲覀冊O(shè)定的字符串