寄語:自從厭倦於追尋,我已學會一覓其中。自從一股逆風襲來,我已能抵禦八面來風,駕舟而行。javascript
本文已收錄至github.com/likekk/stud…歡迎你們star,共同窗習,共同進步。若是文章有錯誤的地方,歡迎你們指出。後期將在將GitHub上規劃前端學習的路線和資源分享。html
每一篇文章都但願您有所收穫,每一篇文章都但願您能靜下心來瀏覽、閱讀。每一篇文章都是做者精心打磨的做品。前端
若是您以爲楊戩這個前端小白還有點東西的話,做者但願你能夠幫忙點亮那個點讚的按鈕,對於二郎神楊戩這個暖男來講,真的真的很是重要,這將是我持續寫做的動力。vue
上一篇文章的內容主要是關於vue-cli項目的搭建、vue-cli項目引入第三方插件的相關內容,插圖比較多,這也多是搭建類博客的特色,固然之後寫做盡可能少用插圖的,我儘可能將本身理解的意思表達出來。本篇博客的內容比較簡單。內容包括Vue模板、computed計算屬性、watch偵聽屬性、methods方法、事件處理等相關內容。java
Vue.js 使用了基於 HTML 的模板語法,容許開發者聲明式地將 DOM 綁定至底層 Vue 實例的數據。全部 Vue.js 的模板都是合法的 HTML,因此能被遵循規範的瀏覽器和 HTML 解析器解析。ios
在底層的實現上,Vue 將模板編譯成虛擬 DOM 渲染函數。結合響應系統,Vue 可以智能地計算出最少須要從新渲染多少組件,並把 DOM 操做次數減到最少。git
文本渲染的話主要是使用{{}}進行渲染,固然這也是數據的渲染方式程序員
<!DOCTYPE html>
<html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <div id="app"> <ul> <li v-for="(item,index) of fruit">{{item}}</li> </ul> </div> <script src="../js/vue.js"></script> <script> const vm=new Vue({ el:'#app', data:{ fruit:["蘋果","香蕉","雪梨"] } }) </script> </body> </html> 複製代碼
經過使用 v-once 指令,你也能執行一次性地插值,當數據改變時,插值處的內容不會更新。github
注意:使用v-once的時候,只會在數據渲染的時候第一次生效,改變它的值,那麼不會從新渲染web
有時候咱們可能有這樣或那樣的需求,輸出真正的html代碼,Vue也爲咱們提供了一個屬性v-html
<!DOCTYPE html>
<html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <div id="app"> <h2 v-html="joinHtml"></h2> </div> <script src="../js/vue.js"></script> <script> const vm=new Vue({ el:'#app', data:{ }, computed:{ joinHtml(){ return "<span>我是經過v-html進行渲染的</span>" } } }) </script> </body> </html> 複製代碼
注意:站點上動態渲染任意 HTML 可能會很是危險,由於它很容易致使XSS 攻擊。毫不要對用戶提供的內容使用插值。
屬性的話通常是以v-bind:[屬性]的格式進行綁定的,例如咱們須要綁定圖片的src屬性,v-bind:src,綁定其它標籤的title屬性,v-bind:title等等。
<!DOCTYPE html>
<html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <div id="app"> <a v-bind:href="href" v-bind:title="title" :data-id="id">百度一下</a> </div> <script src="../js/vue.js"></script> <script> const vm=new Vue({ el:'#app', data:{ href:"https://www.baidu.com", title:"點擊連接,跳轉百度", id:"10001" } }) </script> </body> </html> 複製代碼
若是一個標籤上綁定多個屬性,那麼咱們就須要寫一大堆的v-bind:, 這樣的話代碼的冗餘度就會很高,因此咱們也能夠直接使用:[屬性] 進行綁定。
文本插值中不只能夠直接使用數據,還能夠對數據進行處理
{{ number + 1 }}
{{ ok ? 'YES' : 'NO' }} {{ message.split('').reverse().join('') }} <div v-bind:id="'list-' + id"></div> 複製代碼
這些表達式會在所屬 Vue 實例的數據做用域下做爲 JavaScript 被解析。有個限制就是,每一個綁定都只能包含單個表達式,因此下面的例子都不會生效。
<!-- 這是語句,不是表達式 -->
{{ var a = 1 }} <!-- 流控制也不會生效,請使用三元表達式 --> {{ if (ok) { return message } }} 複製代碼
固然還可使用方法、計算屬性、過濾器等等。
指令是帶有 v-
前綴的特殊屬性。指令屬性的值預期是單個 JavaScript 表達式 ,指令的職責是,當表達式的值改變時,將其產生的連帶影響,響應式地做用於 DOM。
就拿v-if來講
<p v-if="seen">如今你看到我了</p>
複製代碼
這裏的p標籤顯示或者隱藏取決於seen的true或false。
一些指令可以接收一個「參數」,在指令名稱以後以冒號表示。例如,v-bind(:) 指令能夠用於響應式地更新 HTML 屬性
<a v-bind:href="url">...</a>
複製代碼
在這裏 href 是參數,告知 v-bind 指令將該元素的 href屬性與表達式 url 的值綁定。
另外一個例子是 v-on(@click)指令,它用於監聽 DOM 事件:
<a v-on:click="alertMsg">彈出消息</a>
複製代碼
alertMsg表示的是方法名(事件名)
能夠用方括號括起來的 JavaScript 表達式做爲一個指令的參數:
<a v-bind:[attributeName]="url"> ... </a>
複製代碼
這裏的 attributeName
會被做爲一個 JavaScript 表達式進行動態求值,求得的值將會做爲最終的參數來使用。例如,若是你的 Vue 實例有一個 data
property attributeName
,其值爲 "href"
,那麼這個綁定將等價於 v-bind:href
。
修飾符 (modifier) 是以半角句號 .
指明的特殊後綴,用於指出一個指令應該以特殊方式綁定。例如,.prevent
修飾符告訴 v-on
指令對於觸發的事件調用 event.preventDefault()
:
<form v-on:submit.prevent="onSubmit">...</form>
複製代碼
關於縮寫上文已經說起到,減小代碼的冗餘。
<!-- 完整語法 -->
<a v-bind:href="url">...</a> <!-- 縮寫 --> <a :href="url">...</a> <!-- 動態參數的縮寫 (2.6.0+) --> <a :[key]="url"> ... </a> 複製代碼
<!-- 完整語法 -->
<a v-on:click="doSomething">...</a> <!-- 縮寫 --> <a @click="doSomething">...</a> <!-- 動態參數的縮寫 (2.6.0+) --> <a @[event]="doSomething"> ... </a> 複製代碼
計算屬性也是咱們在Vue中常常會使用到的一個特性,和下文的methods、watch有着類似之處和非類似之處。
那咱們一塊兒來看看,
有這樣一個需求,假設須要在花括號中反轉字符串,那麼咱們的第一反應就是這樣寫
<span>{{message.spilt('').reverse.join('')}}</span> 複製代碼
那麼問題來了,這是一個標籤這樣寫,我以爲還很OK,可是若是有一百個,一千個呢?將這樣的代碼寫在界面上,那麼確定維護起來十分不方便。因此咱們須要將這些的邏輯處理進行拆分。
<!DOCTYPE html>
<html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <div id="app"> <p>原始數據{{message}}</p> <p>反轉後的數據{{reverseMessage}}</p> </div> <script src="../js/vue.js"></script> <script> const vm=new Vue({ el:'#app', data:{ message:"hello" }, computed:{ reverseMessage(){ return this.message.split('').reverse().join(''); } } }) </script> </body> </html> 複製代碼
computed計算屬性須要注意的事項:
方法也同時能夠實現上述的效果,看下方法是如何實現的
<!DOCTYPE html>
<html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <div id="app"> <p>原始數據{{message}}</p> <p>反轉後的數據{{reverseMessage}}</p> <p>調用方法反轉後的數據{{reverseMessageOfMethods()}}</p> </div> <script src="../js/vue.js"></script> <script> const vm=new Vue({ el:'#app', data:{ message:"hello" }, computed:{ reverseMessage(){ return this.message.split('').reverse().join(''); } }, methods:{ reverseMessageOfMethods(){ return this.message.split('').reverse().join(''); } } }) </script> </body> </html> 複製代碼
咱們能夠將同一函數定義爲一個方法而不是一個計算屬性。兩種方式的最終結果確實是徹底相同的。然而,不一樣的是計算屬性是基於它們的響應式依賴進行緩存的。
只在相關響應式依賴發生改變時它們纔會從新求值。這就意味着只要 message
尚未發生改變,屢次訪問 reversedMessage
計算屬性會當即返回以前的計算結果,而沒必要再次執行函數。
methods方法應該須要注意的事項
methods裏面定義的方法不該該使用箭頭函數,緣由是箭頭函數綁定了父級做用域上下文,因此this不是Vue實例
咱們爲何須要緩存?假設咱們有一個性能開銷比較大的計算屬性 A,它須要遍歷一個巨大的數組並作大量的計算。而後咱們可能有其餘的計算屬性依賴於 A。若是沒有緩存,咱們將不可避免的屢次執行 A 的 getter!若是你不但願有緩存,請用方法來替代。
Vue 提供了一種更通用的方式來觀察和響應 Vue 實例上的數據變更:偵聽屬性。當你有一些數據須要隨着其它數據變更而變更時,你很容易濫用 watch
——特別是若是你以前使用過 AngularJS。然而,一般更好的作法是使用計算屬性而不是命令式的 watch
回調。細想一下這個例子:
<!DOCTYPE html>
<html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <div id="demo"> <span>{{ fullName }}</span> </div> <script src="../js/vue.js"></script> <script> var vm = new Vue({ el: '#demo', data: { firstName: 'Foo', lastName: 'Bar', fullName: 'Foo Bar' }, watch: { firstName: function (val) { this.fullName = val + ' ' + this.lastName }, lastName: function (val) { this.fullName = this.firstName + ' ' + val } } }) </script> </body> </html> 複製代碼
上面代碼是命令式且重複的。將它與計算屬性的版本進行比較:
<!DOCTYPE html>
<html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <div id="demo"> <span>{{ fullName }}</span> </div> <script src="../js/vue.js"></script> <script> var vm = new Vue({ el: '#demo', data: { firstName: 'Foo', lastName: 'Bar' }, computed: { fullName: function () { return this.firstName + ' ' + this.lastName } } }) </script> </body> </html> 複製代碼
計算屬性默認只有 getter,不過在須要時你也能夠提供一個 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] } } } 複製代碼
雖然計算屬性在大多數狀況下更合適,但有時也須要一個自定義的偵聽器。這就是爲何 Vue 經過 watch
選項提供了一個更通用的方法,來響應數據的變化。當須要在數據變化時執行異步或開銷較大的操做時,這個方式是最有用的。
<div id="watch-example">
<p> Ask a yes/no question: <input v-model="question"> </p> <p>{{ answer }}</p> </div> 複製代碼
<!-- 由於 AJAX 庫和通用工具的生態已經至關豐富,Vue 核心代碼沒有重複 -->
<!-- 提供這些功能以保持精簡。這也可讓你自由選擇本身更熟悉的工具。 --> <script src="https://cdn.jsdelivr.net/npm/axios@0.12.0/dist/axios.min.js"></script> <script src="https://cdn.jsdelivr.net/npm/lodash@4.13.1/lodash.min.js"></script> <script> 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, oldQuestion) { this.answer = 'Waiting for you to stop typing...' this.debouncedGetAnswer() } }, created: function () { // `_.debounce` 是一個經過 Lodash 限制操做頻率的函數。 // 在這個例子中,咱們但願限制訪問 yesno.wtf/api 的頻率 // AJAX 請求直到用戶輸入完畢纔會發出。想要了解更多關於 // `_.debounce` 函數 (及其近親 `_.throttle`) 的知識, // 請參考:https://lodash.com/docs#debounce this.debouncedGetAnswer = _.debounce(this.getAnswer, 500) }, methods: { getAnswer: function () { if (this.question.indexOf('?') === -1) { this.answer = 'Questions usually contain a question mark. ;-)' return } this.answer = 'Thinking...' 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 }) } } }) </script> 複製代碼
三者的區別:
過濾器也是Vue中一個很是強大的功能,可被用於一些常見的文本格式化,過濾器用在兩個地方雙花括號插值和v-bind表達式,過濾器應該添加被添加在JavaScript表達式的尾部,由「管道」符號指示:
<!-- 在雙花括號中 -->
{{ message | capitalize }} <!-- 在 `v-bind` 中 --> <div v-bind:id="rawId | formatId"></div> 複製代碼
你能夠在一個組件的選項中定義本地的過濾器:
<!DOCTYPE html>
<html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <div id="app"> <span> {{message|capitalize}}</span> </div> <script src="../js/vue.js"></script> <script> const vm=new Vue({ el:'#app', data:{ message:"hello" }, methods:{ }, filters:{ capitalize(value){ if(!value){ return "" } value=value.toString(); return value.charAt(0).toUpperCase()+value.slice(1); } } }) </script> </body> </html> 複製代碼
或者在建立 Vue 實例以前全局定義過濾器:
<!DOCTYPE html>
<html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <div id="app"> <span> {{message|capitalize}}</span> </div> <script src="../js/vue.js"></script> <script> Vue.filter("capitalize",function (value) { if(!value){ return "" } value=value.toString(); return value.charAt(0).toUpperCase()+value.slice(1); }) const vm=new Vue({ el:'#app', data:{ message:"hello" }, methods:{ }, }) </script> </body> </html> 複製代碼
當全局過濾器和局部過濾器重名時,會採用局部過濾器
過濾器函數總接收表達式的值 (以前的操做鏈的結果) 做爲第一個參數。在上述例子中,capitalize
過濾器函數將會收到 message
的值做爲第一個參數。
過濾器能夠串聯:
{{ message | filterA | filterB }}
複製代碼
在這個例子中,filterA
被定義爲接收單個參數的過濾器函數,表達式 message
的值將做爲參數傳入到函數中。而後繼續調用一樣被定義爲接收單個參數的過濾器函數 filterB
,將 filterA
的結果傳遞到 filterB
中。
過濾器是 JavaScript 函數,所以能夠接收參數:
{{ message | filterA('arg1', arg2) }}
複製代碼
這裏,filterA
被定義爲接收三個參數的過濾器函數。其中 message
的值做爲第一個參數,普通字符串 'arg1'
做爲第二個參數,表達式 arg2
的值做爲第三個參數。
上文中咱們說起到了指令和事件,那麼如今就好好的聊聊。
v-text
v-html v-show v-if v-else v-eles-if v-for v-on v-bind v-model v-slot v-pre v-cloak v-once 複製代碼
一般咱們都是使用v-on:[事件名]來觸發事件的,例如單擊事件v-on:click等
<!DOCTYPE html>
<html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <div id="app"> <button @click="count+=1">+</button> <span>{{count}}</span> </div> <script src="../js/vue.js"></script> <script> const vm=new Vue({ el:'#app', data:{ count:1 } }) </script> </body> </html> 複製代碼
然而許多事件處理邏輯會更爲複雜,因此直接把 JavaScript 代碼寫在 v-on
指令中是不可行的(如上示例,點擊按鈕的時候實現數字加1)。所以 v-on
還能夠接收一個須要調用的方法名稱。將方法註冊在methods中
<!DOCTYPE html>
<html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <div id="app"> <button @click="greet">問候</button> <button @click="sum($event)">計算</button> </div> <script src="../js/vue.js"></script> <script> const vm=new Vue({ el:'#app', data:{ count:1 }, methods:{ greet(event){ console.log(this.count); if(event){ console.log(event.currentTarget); } }, sum(event){ console.log(event.currentTarget); } } }) </script> </body> </html> 複製代碼
這裏有一點須要注意就是關於事件添加括號和沒有括號的狀況
當事件沒有括號的時候默認傳入的第一個參數是event
當事件有括號的時候,若是想要獲取event這個對象,咱們須要手動傳入$event這個屬性
除了直接綁定到一個方法,也能夠在內聯 JavaScript 語句中調用方法:
<!DOCTYPE html>
<html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <div id="app"> <button @click="say('hi')">sayHi</button> <button @click="say('hello')">sayHello</button> </div> <script src="../js/vue.js"></script> <script> const vm=new Vue({ el:'#app', data:{ count:1 }, methods:{ say(param){ console.log(param) }, } }) </script> </body> </html> 複製代碼
這種是將方法進行復用,根據傳入的參數不一樣。
在事件處理程序中調用 event.preventDefault()
或 event.stopPropagation()
是很是常見的需求。
但更好的方式是:方法只有純粹的數據邏輯,而不是去處理 DOM 事件細節。
爲了解決這個問題,Vue.js 爲 v-on
提供了事件修飾符。以前提過,修飾符是由點開頭的指令後綴來表示的。
<!-- 阻止單擊事件繼續傳播 -->
<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> <!-- 只當在 event.target 是當前元素自身時觸發處理函數 --> <!-- 即事件不是從內部元素觸發的 --> <div v-on:click.self="doThat">...</div> <!-- 點擊事件將只會觸發一次 --> <a v-on:click.once="doThis"></a> 複製代碼
注意:
使用修飾符時,順序很重要;相應的代碼會以一樣的順序產生。所以,用
v-on:click.prevent.self
會阻止全部的點擊,而v-on:click.self.prevent
只會阻止對元素自身的點擊。
Vue 還對應 addEventListener中的 passive 選項提供了 .passive
修飾符。
<!-- 滾動事件的默認行爲 (即滾動行爲) 將會當即觸發 -->
<!-- 而不會等待 `onScroll` 完成 --> <!-- 這其中包含 `event.preventDefault()` 的狀況 --> <div v-on:scroll.passive="onScroll">...</div> 複製代碼
這個 .passive
修飾符尤爲可以提高移動端的性能。
注意:
不要把
.passive
和.prevent
一塊兒使用,由於.prevent
將會被忽略,同時瀏覽器可能會向你展現一個警告。請記住,.passive
會告訴瀏覽器你不想阻止事件的默認行爲。
在監聽鍵盤事件時,咱們常常須要檢查詳細的按鍵。Vue 容許爲 v-on
在監聽鍵盤事件時添加按鍵修飾符:
<!-- 只有在 `key` 是 `Enter` 時調用 `vm.submit()` -->
<input v-on:keyup.enter="submit"> 複製代碼
你能夠直接將 KeyboardEvent.key暴露的任意有效按鍵名轉換爲 kebab-case 來做爲修飾符。
<input v-on:keyup.page-down="onPageDown">
複製代碼
在上述示例中,處理函數只會在 $event.key
等於 PageDown
時被調用。
keyCode
的事件用法已經被廢棄了並可能不會被最新的瀏覽器支持。
使用 keyCode
attribute 也是容許的:
<input v-on:keyup.13="submit">
複製代碼
爲了在必要的狀況下支持舊瀏覽器,Vue 提供了絕大多數經常使用的按鍵碼的別名:
.enter
.tab
.delete
(捕獲「刪除」和「退格」鍵)
.esc
.space
.up
.down
.left
.right
有一些按鍵 (
.esc
以及全部的方向鍵) 在 IE9 中有不一樣的key
值, 若是你想支持 IE9,這些內置的別名應該是首選。
能夠用以下修飾符來實現僅在按下相應按鍵時才觸發鼠標或鍵盤事件的監聽器
.ctrl
.alt
.shift
.meta
<!-- Alt + C -->
<input v-on:keyup.alt.67="clear"> <!-- Ctrl + Click --> <div v-on:click.ctrl="doSomething">Do something</div> 複製代碼
請注意修飾鍵與常規按鍵不一樣,在和
keyup
事件一塊兒用時,事件觸發時修飾鍵必須處於按下狀態。換句話說,只有在按住ctrl
的狀況下釋放其它按鍵,才能觸發keyup.ctrl
。而單單釋放ctrl
也不會觸發事件。若是你想要這樣的行爲,請爲ctrl
換用keyCode
:keyup.17
。
一、.exact修飾符
.exact修飾符容許你控制由精確的系統修飾符組合觸發的事件
<!-- 即便 Alt 或 Shift 被一同按下時也會觸發 -->
<button v-on:click.ctrl="onClick">A</button> <!-- 有且只有 Ctrl 被按下的時候才觸發 --> <button v-on:click.ctrl.exact="onCtrlClick">A</button> <!-- 沒有任何系統修飾符被按下的時候才觸發 --> <button v-on:click.exact="onClick">A</button> 複製代碼
二、鼠標按鈕修飾符
這些修飾符會限制處理函數僅響應特定的鼠標按鈕
若是以爲本篇文章對您有用的話,能夠麻煩您給筆者點亮那個點贊按鈕。
對於楊戩這個暖男來講:真的真的很是有用,您的支持將是我繼續寫文章前進的動力,咱們下篇文章見。
【原創】|二郎神楊戩
二郎神楊戩,一個在互聯網前端苟且偷生的划水程序員,專一於前端開發,善於技術分享。 如需轉載,請聯繫做者或者保留原文連接,微信公衆號搜索二郎神楊戩或者掃描下方的二維碼更加方便。
一塊兒來見證二郎神楊戩的成長吧!更多好文、技術分享盡在下方這個公衆號。歡迎關注。