其餘章節請看:javascript
vue 快速入門 系列html
Tip: vue 的基礎應用分上下兩篇,上篇是基礎,下篇是應用。vue
在初步認識 vue一文中,咱們已經寫了一個 vue 的 hello-world。對 vue 已經有了一個大概的印象。java
接下來咱們應該掌握 vue 的最基礎知識,學會 vue 的基本應用。vuex
比較好的方法就是花個幾天的時間將 vue 官網的基礎篇儘可能走一遍,寫一寫例子。如下是 vue 2.x 的基礎篇的目錄:app
- 教程 2.x - 基礎 - 安裝 - 介紹 - Vue 實例 - 模板語法 - 計算屬性和偵聽器 - Class 與 Style 綁定 - 條件渲染 - 列表渲染 - 事件處理 - 表單輸入綁定 - 組件基礎
注:若是你像筆者同樣,只有 jQuery 開發的經驗,在看基礎篇的過程種,確定會有不少的不懂之處,先看下去,把能看懂的先看完。框架
本文記錄的是筆者當初看 vue 基礎篇遇到的一些重要的、很差理解的知識點,算是基礎篇的一個補充或筆記。dom
經過 new Vue()
來建立一個實例,其中 el 選項提供一個在頁面上已存在的 DOM 元素做爲 Vue 實例的掛載目標。函數
vue 實例會經歷一系列的過程,好比數據偵測、模板編譯、渲染到視圖等,這就是 vue 實例的生命週期,vue 對外提供了生命週期的鉤子函數,這就容許咱們在 vue 的各個階段插入一些咱們的邏輯。工具
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> <script src='vue.js'></script> </head> <body> <div id='app'> <p> {{message}} </p> </div> <script> // 經過 new Vue() 建立一個實例 var app = new Vue({ // 提供一個在頁面上已存在的 DOM 元素做爲 Vue 實例的掛載目標。 // 能夠是 CSS 選擇器,也能夠是一個 HTMLElement 實例。 el: '#app', // Vue 實例的數據對象 data:{ message: 'hello', }, // 生命週期 created: function(){ console.log('實例被建立時觸發') }, mounted: function(){ console.log('被掛載時觸發') } }) </script> </body> </html>
Tip: 後續不在提供完整的代碼,省略 head、body 等。
前文說過 vue 是聲明式操做 dom 的框架。咱們只須要描述狀態與 dom 之間的映射關係便可,狀態到視圖的轉換,框架會幫咱們作,狀態改變,視圖也會自動更新。
而 vue 是經過模板來描述狀態與 dom 之間的映射關係。因此模板的知識點稍微會多一點。
Vue 會盡量高效地渲染元素,一般會複用已有元素而不是從頭開始渲染。
<div id='app'> <template v-if="loginType === 'username'"> <label>Username</label> <input placeholder="Enter your username"> </template> <template v-else> <label>Email</label> <input placeholder="Enter your email address"> </template> <button @click='toggleHandle'>切換</button> </div> <script> var app = new Vue({ el: '#app', data:{ loginType: 'username', }, methods: { toggleHandle: function(){ this.loginType = this.loginType === 'username' ? 'email' : 'username'; } } }) </script>
在上面的代碼中切換 loginType 將不會清除用戶已經輸入的內容。由於兩個模板使用了相同的元素,<input>
不會被替換掉——僅僅是替換了它的 placeholder。若是須要表達「這兩個元素是徹底獨立的,不要複用它們」,只需添加一個具備惟一值的 key attribute 便可。
// 給 input 添加 key 屬性 <input placeholder="Enter your username" key="username-input"> <input placeholder="Enter your email address" key="email-input">
v-if 是真正的條件渲染;v-show 只是簡單地切換元素的 CSS property display;
例以下面這種場景,點擊一個新建按鈕,顯示彈框組件(custom-dialog),彈框中有一些 input 輸入框,若是須要每次點擊新建,彈框組件整個都從新生成,則可使用 v-if;不然在彈框中輸入了文字,經過 v-show 隱藏彈框,下次在顯示時,可能以前輸入的文字、錯誤信息都還存在。
<custom-dialog v-if='isShow'></custom-dialog> <button @click='clickHandle'>新建</button>
v-model:在表單控件或者組件上建立雙向綁定。
應用在表單控件上,請看示例:
<div id='app'> <input v-model="message" placeholder="edit me"> <!-- {1} --> <!-- <input type="text" :value='message' @input='handleInput' placeholder="edit me"> // {2} --> <p>Message is: {{ message }}</p> </div> <script> var app = new Vue({ el: '#app', data:{ message: 'a' }, methods: { handleInput: function(e){ this.message = e.target.value } } }) </script>
v-model 本質上不過是語法糖。行{1} 的本質實際上是 行{2}。
v-model 在組件中的使用請看下文。
上面介紹的實際上是基礎中最簡單的部分。而組件是基礎篇中最應該掌握的部分。
能夠經過 Vue.component() 註冊或獲取全局組件。請看示例:
<div id='app'> <p>{{message}}</p> <button-counter></button-counter> <button-counter></button-counter> </div> <script> // 定義一個名爲 button-counter 的新組件 Vue.component('button-counter', { // 一個組件的 data 選項必須是一個函數 // 這樣,每一個組件的數據都是獨立的 data: function () { return { count: 0 } }, template: '<button v-on:click="count++">You clicked me {{ count }} times.</button>' }) var app = new Vue({ el: '#app', data: { message: 'hello' } }) </script>
組件是可複用的 Vue 實例,且帶有一個名字:在這個例子中是 <button-counter>
。
由於組件是可複用的 Vue 實例,因此它們與 new Vue() 接收相同的選項,例如 data、computed、watch、methods 以及生命週期鉤子等。僅有的例外是像 el 這樣根實例特有的選項。
一般一個應用會以一棵嵌套的組件樹的形式來組織,這樣就能完成一個功能很複雜的頁面。
props 用於接收來自父組件的數據。請看示例:
<div id='app'> <!-- <button-counter v-bind:msg='message'></button-counter> --> <!-- 縮寫 --> <button-counter :msg='message'></button-counter> </div> <script> Vue.component('button-counter', { props: ['msg'], template: `<div> 來自父組件的信息: {{msg}} </div>` }) var app = new Vue({ el: '#app', data: { message: 'hello' } }) </script>
v-bind:msg='message'
能夠縮寫成 :msg='message'
v-on 用在自定義元素組件上時,能夠監聽子組件觸發的自定義事件。請看示例:
<div id='app'> number: {{count}} <!-- 父組件給子組件註冊了事件 chang-count,事件的回調方法是 changCount --> <button-counter v-on:chang-count='changCount'></button-counter> </div> <script> Vue.component('button-counter', { data: function(){ return { num: 10 } }, template: `<div> <!-- v-on:click 縮寫 @click --> <button @click='triggerHandle'>觸發</button> </div>`, methods: { triggerHandle: function(){ // 觸發事件 this.$emit('chang-count', this.num++); } } }) var app = new Vue({ el: '#app', data: { count: 0 }, methods: { changCount: function(v){ this.count = v; } } }) </script>
v-on 的語法糖是 @。
v-model 在表單控件或者組件上建立雙向綁定。上文已經介紹瞭如何在表單上使用。而用在組件上,原理是相同的。請看示例:
<div id='app'> number: {{count}} <!-- 父組件給子組件註冊了事件 chang-count,事件的回調方法是 changCount --> <custom-input v-model="count"></custom-input> <!-- {20} --> <!-- <custom-input :value="count" @input='changCount'></custom-input> --> <!-- {21} --> </div> <script> Vue.component('custom-input', { props: ['value'], data: function(){ return { msg: this.value } }, template: `<div> <input :value='msg' @input='inputHandle'/> </div>`, methods: { inputHandle: function(e){ this.msg = e.target.value // 觸發事件 this.$emit('input', this.msg); } } }) var app = new Vue({ el: '#app', data: { count: 10 }, methods: { changCount: function(v){ this.count = v; } } }) </script>
行{20} 與 行{21} 等效。
props 用於父組件給子組件傳遞數據;
子組件給父組件傳遞數據能夠經過事件(例如 vm.$emit());
但非父子之間如何通訊?
對於 $dispatch 和 $broadcast 最簡單的升級方式就是:經過使用事件中心,容許組件自由交流,不管組件處於組件樹的哪一層。因爲 Vue 實例實現了一個事件分發接口,你能夠經過實例化一個空的 Vue 實例來實現這個目的。—— vue 官網
簡單來講,就是使用一個空的 Vue 實例做爲中央事件總線,能夠幫助咱們解決兄弟組件之間的通訊。請看示例:
<div id='app'> <component-a></component-a> <component-b></component-b> </div> <script> Vue.component('componentA', { data: function(){ return { msg: '我是 componentA' } }, template: `<div> message: {{msg}} <button @click='send'>給兄弟發送數據</button> </div>`, created: function(){ var that = this; // 給事件中心註冊事件 bus.$on('on-messageA', function(msg){ console.log('received: ' + msg) that.msg = msg }) }, methods: { send: function(e){ // 給事件中心觸發事件 bus.$emit('on-messageB', 'from componentA') } } }) Vue.component('componentB', { data: function(){ return { msg: '我是 componentB' } }, template: `<div> message: {{msg}} <button @click='send'>給兄弟發送數據</button> </div>`, created: function(){ var that = this; bus.$on('on-messageB', function(msg){ that.msg = msg }) }, methods: { send: function(e){ console.log('from componentB') bus.$emit('on-messageA', 'from componentB') } } }) // 事件中心 var bus = new Vue() var app = new Vue({ el: '#app' }) </script>
這個例子中定義了兩個組件,首先用 new Vue() 建立一箇中央事件總線(bus),相似一箇中介,接着組件(componentA、componentB)給 bus 註冊事件,當點擊按鈕時觸發 bus 的事件,並傳遞數據。
這種方式其實能夠實現任何組件之間的通訊,包括父子、兄弟、跨級等,由於在 bus 看來,我(bus)就是中介,其餘組件只是個人客戶。
Tip:項目比較大時,一般會使用 vuex,後續會介紹 vuex 這個狀態管理工具。
vm.$parent 能夠取得父組件;經過 vm.$children 取得當前實例的直接子組件,並經過 vm.$refs 取得指定子組件。請看示例:
<div id='app'> <component-a ref='comA'></component-a> <component-a ref='comB'></component-a> </div> <script> Vue.component('componentA', { data: function(){ return { msg: 'hello' } }, template: `<div> message: {{msg}} </div>`, watch: { msg: function(){ // 經過 vm.$parent 取得父組件 console.log(this.$parent.name) // 經過 ref 指定子組件索引名稱 // 經過 vm.$refs 取得子組件 console.log(this.$parent.$refs.comA.msg) } } }) var app = new Vue({ el: '#app', data: { name: '我是父' } }) // 經過 vm.$children 直接更改子組件的數據 console.log(app.$children[1].msg = 'hello2') </script>
插槽也能夠叫 slot 內容分發。請看示例:
<div id='app'> <component-a ref='comA'> <p>子元素的 slot 插槽將會被我替換</p> <!-- {30} --> </component-a> </div> <script> Vue.component('componentA', { template: `<div> hello <slot></slot> </div>`, }) var app = new Vue({ el: '#app', }) </script>
咱們在組件A中定義了一個插槽(<slot></slot>
),父組件中的 p(行{30})將會替換子組件的 slot。
Tip: props、事件以及 slot 內容分發構成了 Vue 組件的 3 個 API 來源。
做用域插槽,概念不太好理解,直接看示例:
<div id='app'> <component-a> <!-- slot-scope 用於將元素或組件表示爲做用域插槽 --> <template slot-scope='props'> <p>來自父組件</p> <p>{{props.msg}}</p> </template> </component-a> </div> <script> Vue.component('componentA', { data: function(){ return { msg: '來自子組件的內容' } }, template: `<div> hello <slot :msg='msg'></slot> </div>`, }) var app = new Vue({ el: '#app', }) </script>
做用域插槽,就是讓插槽的內容可以訪問子組件中才有的數據。
注:因爲筆者的 vue 是 2.5.x,因此用的是 slot-scope。而在 2.6.x 中,scope、slot和slot-scope 都推薦使用 v-scope。
Vue.js 容許你自定義過濾器,可被用於一些常見的文本格式化。請看示例:
<div id='app'> <p>{{msg | toUpperCase}}</p> </div> <script> var app = new Vue({ el: '#app', data: { msg: 'hello' }, // 過濾器 filters: { toUpperCase: function(v){ return v.toUpperCase() } } }) </script>
在有些狀況下,咱們可能須要對一個 prop 進行「雙向綁定」。不幸的是,真正的雙向綁定會帶來維護上的問題,由於子組件能夠變動父組件,且在父組件和子組件都沒有明顯的變動來源。這也是爲何咱們推薦以
update:myPropName
的模式觸發事件取而代之。—— vue 官網
簡單來講,父組件給子組件傳遞了一個屬性,子組件不要直接更改父組件的這個屬性,而應該通知父組件,讓父組件本身去更改這個屬性。請看示例:
<div id='app'> 父組件: name = {{name}} <!-- 當在父級組件監聽事件的時候,咱們能夠經過 $event 訪問到被拋出的這個值 --> <custom :name="name" v-on:update:name="updateName($event)"></custom> <!-- {40} --> <!-- 相等 --> <!-- <custom :name.sync="name"></custom> --> <!-- {41} --> </div> <script> let seed = 1; Vue.component('custom', { props: ['name'], template: ` <div> <p>子組件: name = {{name}}</p> <p><button @click='handleClick'>change name</button></p> </div> `, methods: { handleClick: function(){ // update:name 就是一個事件名 // 改成 update--name 也能夠 this.$emit('update:name', seed++) } } }) const app = new Vue({ el: '#app', data: { name: 'hello' }, methods: { updateName: function(v){ this.name = v } } }) </script>
爲了方便起見,咱們爲這種模式(行{40})提供一個縮寫,即 .sync 修飾符(行{41})
其餘章節請看: