下面是Vue的一些基本知識點相關學習跟應用,差缺補漏吧。Vue.js官網好好看一遍還是很香的。
Vue中的資料和DOM已經被關聯起來,所有的東西都是響應式的。注意我們不再和HTML直接互動。一個Vue應用會將其掛載到DOM元素上然後對齊進行完全的控制,那個HTML是我們的入口,但是其他的都會發生在新建立的Vue例項內部。詳情可見MVVM原理極其實現。
Vue例項
1、例項宣告週期鉤子函式
8個宣告周期函式
1、插值
①、 v-once 指令,你也能執行一次性地插值,當資料改變時,插值處的內容不會更新。
②、可以使用js表示式但是不能使用語句和流控制(if判斷語句)
1 {{ ok ? 'YES' : 'NO' }} 2 {{ message.split('').reverse().join('') }} 3 4 <!-- 這是語句,不是表示式 --> 5 {{ var a = 1 }} 6 <!-- 流控制也不會生效,請使用三元表示式 --> 7 {{ if (ok) { return message } }}
2、指令
①、動態引數 -2.60新增
可以使用動態引數為一個動態的事件名繫結處理函式:
約束:為某些字元,如空格和引號,放在 HTML attribute 名裡是無效的。會觸發警告。
1 <a v-on:[eventName]="doSomething"> ... </a>
當 eventName 的值為 "focus" 時,v-on:[eventName] 將等價於 v-on:focus
3、修飾符
在移動端最好的應用就是
①、 .stop 阻止事件冒泡
②、 .prevent (@touchmove.prevent 禁止底層頁面滑動)
計算屬性和偵聽器
模板中不應該放入過多的邏輯,會讓模板過重且難以維護,所以對於任何複雜邏輯,都應該使用計算屬性。
基礎例子
1 <div id="example"> 2 <p>Original message: "{{ message }}"</p> 3 <p>Computed reversed message: "{{ reversedMessage }}"</p> 4 </div> 5 var vm = new Vue({ 6 el: '#example', 7 data: { 8 message: 'Hello' 9 }, 10 computed: { 11 // 計算屬性的 getter 12 reversedMessage: function () { 13 // `this` 指向 vm 例項 14 return this.message.split('').reverse().join('') 15 } 16 } 17 }) 18 // Original message: "Hello" 19 20 // Computed reversed message: "olleH"
我們宣告瞭一個計算屬性 reversedMessage。Vue 知道
vm.reversedMessage
依賴於vm.message
,因此當 vm.message 發生改變時,,所有依賴vm.reversedMessage
的繫結也會更新。
計算屬性快取 vs 方法
我們通過表示式中呼叫方法可以同樣達到效果:
1 <p>Reversed message: "{{ reversedMessage() }}"</p> 2 // 在元件中 3 methods: { 4 reversedMessage: function () { 5 return this.message.split('').reverse().join('') 6 } 7 }
兩種方式的最終結果確實是完全相同的。然而,不同的是計算屬性是基於它們的響應式依賴進行快取。只在相關響應依賴發生改變時它們才會重新計算求值。這就意思只要message還沒有改變,多次訪問reversedMessage 計算屬性會立即返回之前的計算結果,而不必再次執行函式
注:這也同樣意味著下面的計算屬性將不再更新,因為 Date.now()
不是響應式依賴:
1 computed: { 2 now: function () { 3 return Date.now() 4 } 5 }
如你不希望有快取,請用方法來替代。
計算屬性 vs 偵聽屬性
Vue 提供了一種更通用的方式來觀察和響應 Vue 例項上的資料變動:偵聽屬性。
但是有些時候可以使用computed代替watch
計算屬性的 setter
計算屬性預設只有 getter,不過在需要時你也可以提供一個 setter:
1 computed: { 2 fullName: { 3 // getter 4 get: function () { 5 return this.firstName + ' ' + this.lastName 6 }, 7 // setter 8 set: function (newValue) { 9 var names = newValue.split(' ') 10 this.firstName = names[0] 11 this.lastName = names[names.length - 1] 12 } 13 } 14 }
現在再執行
vm.fullName = 'John Doe'
時,setter 會被呼叫,vm.firstName
和vm.lastName
也會相應地被更新。
偵聽器
當資料變化要執行非同步或者開銷較大的操作,watch 是最有用的選擇。
Class於Style繫結
繫結HTML Class
物件語法
①、我們可以傳給 v-bind:class
一個物件,以動態地切換 class:
1 <div v-bind:class="{ active: isActive }"></div>
是否渲染取決於isActive是 true或false
②、你可以在物件中傳入更多欄位來動態切換多個 class。此外,v-bind:class
指令也可以與普通的 class attribute 共存。當有如下模板:
1 <div 2 class="static" 3 v-bind:class="{ active: isActive, 'text-danger': hasError }" 4 ></div> 5 6 // data定義如下 7 data: { 8 isActive: true, 9 hasError: false 10 } 11 12 // 渲染結果 13 // <div class="static active"></div>
③、繫結的資料物件不必內聯定義在模板裡:
1 <div v-bind:class="classObject"></div> 2 data: { 3 classObject: { 4 active: true, 5 'text-danger': false 6 } 7 }
④、這是一個常用且強大的模式,我們也可以在這裡繫結一個返回物件的計算屬性:
<div v-bind:class="classObject"></div> data: { isActive: true, error: null }, computed: { classObject: function () { return { active: this.isActive && !this.error, 'text-danger': this.error && this.error.type === 'fatal' } } }
陣列語法
①、我們可以把一個陣列傳給 v-bind:class
,以應用一個 class 列表:
1 <div v-bind:class="[activeClass, errorClass]"></div> 2 data: { 3 activeClass: 'active', 4 errorClass: 'text-danger' 5 } 6 // 渲染為: 7 // <div class="active text-danger"></div>
②、 陣列語法中也可以使用物件語法:
1 <div v-bind:class="[{ active: isActive }, errorClass]"></div>
用在元件上
當在一個自定義元件上使用 class
時,這些 class 將被新增到該元件的根元素上面。這個元素上已經存在的 class 不會被覆蓋。
你宣告瞭這個元件:
1 Vue.component('my-component', { 2 template: '<p class="foo bar">Hi</p>' 3 }) 4 <!-- 在使用它的時候新增一些 class: --> 5 <my-component class="baz boo"></my-component> 6 7 <!-- HTML 將被渲染為: --> 8 <p class="foo bar baz boo">Hi</p>
繫結內聯樣式
物件語法
v-bind:style
的物件語法十分直觀——看著非常像 CSS,但其實是一個 JavaScript 物件。CSS property 名可以用駝峰式 (camelCase) 或短橫線分隔 (kebab-case,記得用引號括起來) 來命名:
1 <div v-bind:style="{ color: activeColor, fontSize: fontSize + 'px' }"></div> 2 3 <!-- 直接繫結到一個樣式物件通常更好,這會讓模板更清晰: --> 4 <div v-bind:style="styleObject"></div> 5 data: { 6 activeColor: 'red', 7 fontSize: 30 8 } 9 10 // 繫結物件 11 data: { 12 styleObject: { 13 color: 'red', 14 fontSize: '13px' 15 } 16 }
同樣的,物件語法常常結合返回物件的計算屬性使用。
多重值
從 2.3.0 起你可以為
style
繫結中的 property 提供一個包含多個值的陣列,常用於提供多個帶字首的值,例如:
1 <div :style="{ display: ['-webkit-box', '-ms-flexbox', 'flex'] }"></div>
條件渲染
在<template>元素上使用v-if條件渲染分組
因為
v-if
是一個指令,所以必須將它新增到一個元素上。但是如果想切換多個元素呢?此時可以把一個<template>
元素當做不可見的包裹元素,並在上面使用v-if
。最終的渲染結果將不包含<template>
元素。
1 <template v-if="ok"> 2 <h1>Title</h1> 3 <p>Paragraph 1</p> 4 <p>Paragraph 2</p> 5 </template>
v-else-if v-else
類似於 v-else
,v-else-if
也必須緊跟在帶 v-if
或者 v-else-if
的元素之後。
用key可以管理可複用的元素
Vue 會盡可能高效地渲染元素,通常會複用已有元素而不是從頭開始渲染。這麼做除了使 Vue 變得非常快之外,還有其它一些好處。例如,如果你允許使用者在不同的登入方式之間切換:
<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>
那麼在上面的程式碼中切換 loginType
將不會清除使用者已經輸入的內容。因為兩個模板使用了相同的元素,<input>
不會被替換掉——僅僅是替換了它的 placeholder
。
這樣也不總是符合實際需求,所以 Vue 為你提供了一種方式來表達“這兩個元素是完全獨立的,不要複用它們”。只需新增一個具有唯一值的 key
attribute 即可:
<template v-if="loginType === 'username'"> <label>Username</label> <input placeholder="Enter your username" key="username-input"> </template> <template v-else> <label>Email</label> <input placeholder="Enter your email address" key="email-input"> </template>
現在,每次切換時,輸入框都將被重新渲染。
v-for和v-if
永遠不要把 v-if
和 v-for
同時用在同一個元素上。
一般我們在兩種常見的情況下會傾向於這樣做:
-
為了過濾一個列表中的專案 (比如
v-for="user in users" v-if="user.isActive"
)。在這種情形下,請將users
替換為一個計算屬性 (比如activeUsers
),讓其返回過濾後的列表。 -
為了避免渲染本應該被隱藏的列表 (比如
v-for="user in users" v-if="shouldShowUsers"
)。這種情形下,請將v-if
移動至容器元素上 (比如ul
、ol
)。
1 <ul> 2 <li 3 v-for="user in users" 4 v-if="user.isActive" 5 :key="user.id" 6 > 7 {{ user.name }} 8 </li> 9 </ul>
當 Vue 處理指令時
v-for
比v-if
具有更高的優先順序,哪怕我們只渲染出一小部分使用者資料的元素,也得在每次重新渲染的時候遍歷整個列表,不論活躍使用者是否發生變化,應該使用計算屬性
列表渲染
v-for
在v-for中,既可以使用陣列也可以使用物件
狀態維護
當Vue正在更新使用v-for渲染列表時,它預設使用“就地更新策略”。如果資料項的順序被改變,Vue將不會移動DOM元素來匹配資料項的順序,而是就地更新每一個元素,並確保它們在每個位置索引位置正確渲染。
不要使用物件或陣列之類的非基本型別值作為v-for的key。請用字串或數值型的值。
key的特殊主要用在Vue的虛擬DOM演算法,在新舊nodes對比時辨識VNodes。如果不使用key,Vue會使用一種最大限度減少動態元素並且儘可能的嘗試就地修改/複用相同型別元素的演算法。而使用key時,它會基於key的變化重新排列元素順序,並且會移除key不存在的元素
事件處理
內聯處理器的方法
有時也需要在內聯語句處理器中訪問原始的DOM事件。可以使用特殊變數$event把它傳入方法:
<button v-on:click="warn('Form cannot be submitted yet.', $event)"> Submit </button> // ... methods: { warn: function (message, event) { // 現在我們可以訪問原生事件物件 if (event) { event.preventDefault() } alert(message) } }
事件修飾符
以上方法雖然可以實現,但是更好的方式是:方法只有純粹的資料邏輯,而不是去處理DOM事件細節。為了解決這個問題,Vue提供了事件修飾符。
.stop // 等同於js中的event.stopPropagation(),防止事件冒泡
.prevent // 取消預設事件,比如a標籤的連結跳轉
.caputer // 捕獲事件,點選子節點,由外到內父節點->子節點觸發點選事件(不加是由內到外)
.self // 只觸發自己範圍內的事件,不會包含子元素
.once // 只執行一次點選
.passive
【瀏覽器只有等核心執行緒執行到事件監聽器對應的JavaScript程式碼時,才能知道內部是否會呼叫preventDefault函式來阻止事件的預設行為,所以瀏覽器本身是沒有辦法對這種場景進行優化的。這種場景下,使用者的手勢事件無法快速產生,會導致頁面無法快速執行滑動邏輯,從而讓使用者感覺到頁面卡頓。】
通俗點說就是每次事件產生,瀏覽器都會去查詢一下是否有preventDefault阻止該次事件的預設動作。我們加上passive就是為了告訴瀏覽器,不用查詢了,我們沒用preventDefault阻止預設動作。
表單輸入繫結
修飾符
.lazy
在預設情況下,v-model
在每次 input
事件觸發後將輸入框的值與資料進行同步 。你可以新增 lazy
修飾符,從而轉為在 change
事件之後進行同步:
<!-- 在“change”時而非“input”時更新 -->
<input v-model.lazy="msg">
<!--注:ElementUI的input輸入框不支援 `v-model` 修飾符-->
number
可以自動將使用者輸入的值轉換成數值型別:
只有這個值無法被 parsenFloat()解析,則會返回原始的值
<input v-model.number="age" type="number">
.trim
過濾使用者輸入的收尾空白字元:
1 <input v-model.trim="msg">
元件基礎
通過Prop向子元件傳遞資料
傳入一個物件的所有property
如果想將一個物件的所有property都作為prop傳入,可以不用每個引數分別使用v-bind
1 post: { 2 id: 1, 3 title: 'My Journey with Vue' 4 }
下面的模板
1 <blog-post v-bind="post"></blog-post> 2 3 <!-- 等價於 --> 4 <blog-post 5 v-bind:id="post.id" 6 v-bind:title="post.title" 7 ></blog-post>
單向資料流
所有的prop傳值都讓父子prop之間形成一個單行下行繫結。每次父元件發生變更時,子元件中所有的prop都將會重新整理為最新的值,這說明你不應該在子元件內部改prop(強行做,Vue會有警告)
注:JavaScript中陣列和物件是通過引用傳入的,對於一個陣列或物件型別的prop來說,在子元件中改變變更這個物件的本身將會影響父元件的狀態
Prop驗證
1 Vue.component('my-component', { 2 props: { 3 // 基礎的型別檢查('null' 和 'undefined'會通過任何型別的驗證 4 propA: Number, 5 // 多個型別 6 propB: [Number, String] 7 // 必填字串 8 propC: [ 9 type: String, 10 required: true 11 ] 12 } 13 })
自定義事件
始終使用 kebab-case 的事件名。
插槽
編譯作用域
父級模板裡的所有內容都是在父級作用域中編譯的;子模板裡的所有內容都是在子作用域中編譯的。
具名插槽
作用域插槽
混入
基礎
混入(mixin)提供了一種非常靈活的方式,來分發Vue元件中的可複用功能。一個混入物件可以包含任意元件選項。當元件使用混入物件時,所有混入物件的選項將被混入該元件本身的選項
選項合併
當元件和混入物件有同名選項是, 資料物件在內部會進行遞迴合併,並且在發生衝突時以元件資料優先
1 var mixin = { 2 data() { 3 return { 4 message: 'hello', 5 foo: 'abc' 6 } 7 } 8 } 9 10 new Vue({ 11 mixin: [mixin], 12 data() { 13 return { 14 message: 'goodbye', 15 bar: 'def' 16 } 17 }, 18 created() { 19 console.log(this.$data) 20 // => {message: 'goodbye', foo:'adc', bar: 'def'} 21 } 22 })
同名鉤子函式將合併為一個陣列,因此都將被呼叫。只是混入物件的鉤子將在元件自身鉤子之前呼叫
值為物件的選項,例如methods、components和directives,將別合併成同一個物件。兩個物件鍵名衝突時,取元件物件的鍵值對。
自定義選項合併策略
自定義選項將使用預設策略,即簡單地覆蓋已有值。如果想要自定義選項以自定義邏輯合併,可以向 Vue.config.optionMergeStrategies
新增一個函式
自定義指令
鉤子函式
鉤子函式引數
動態指令引數
物件字面量
如果指令需要多個值,可以傳入一個JavaScript物件字面量。指令函式可以接受所有合法的JavaScript表示式
1 <div v-demo="{ color: 'white', text: 'hello!' }"></div> 2 Vue.directive('demo', function (el, binding) { 3 console.log(binding.value.color) // => "white" 4 console.log(binding.value.text) // => "hello!" 5 })
渲染函式 & JSX
看懂百分之30%,過段時間再看
路由
程式設計式導航和宣告式導航
1 // 宣告式 2 <router-link :to="...."> 3 4 // 程式設計式 5 router.push(...) 6 // ex: 7 // 字串 8 router.push('home') 9 // 物件 10 router.push({ path: 'home' }) 11 // 命名的路由 12 router.push({ name: 'user', params: { userId: '123' }}) 13 // 帶查詢引數,變成 /register?plan=private 14 router.push({ path: 'register', query: { plan: 'private' }})
導航守衛
全域性前置守衛
router.beforeEach
全域性解析守衛
router.beforeResolve
全域性後置鉤子
router.afterEach((to, from) => {
// ...
})
路由獨享守衛
beforeEnter: (to, from, next) => {
// ...
}
元件內的守衛
① beforeRouteEnter
② beforeRouteUpdate
③ beforeRouteLeave
1 beforeRouteEnter (to, from, next) { 2 // 在渲染該元件的對應路由被 confirm 前呼叫 3 // 不!能!獲取元件例項 `this` 4 // 因為當守衛執行前,元件例項還沒被建立。 5 next(vm => { 6 // 通過`vm`訪問元件例項 7 }) 8 }, 9 beforeRouteUpdate (to, from, next) { 10 // 在當前路由改變,但是該元件被複用時呼叫 11 // 舉例來說,對於一個帶有動態引數的路徑 /foo/:id,在 /foo/1 和 /foo/2 之間跳轉 的時候, 12 // 由於會渲染同樣的 Foo 元件,因此元件例項會被複用。而這個鉤子就會在這個情況下被調 用。 13 // 可以訪問元件例項 `this` 14 }, 15 beforeRouteLeave (to, from, next) { 16 // 導航離開該元件的對應路由時呼叫 17 // 可以訪問元件例項 `this` 18 }
完整的導航解析流程
1.導航被觸發。 2.在失活的元件裡呼叫 beforeRouterLeave
守衛。 3.呼叫全域性的 beforeEach
守衛。 4.在重用的元件裡呼叫 beforeRouterUpdate
守衛。 5.在路由配置裡呼叫 beforeEnter
。 6.解析非同步路由元件。 7.在被啟用的元件裡呼叫 beforeRouterEnter
。 8.呼叫全域性的 beforeResolve
守衛。 9.導航被確認。 10.呼叫全域性 afterEach
鉤子。 11.觸發DOM更新。 12.用建立好的例項呼叫 beforeRouteEnter
守衛中傳給 next
的回撥函式。
Vuex
State
state 提供唯一的公共資料來源。所有共享的資料要統一放到Store的State中進行儲存
元件訪問State中資料的第一種方式:
1 this.$store.state.全域性資料名稱
元件訪問State中資料的第二種方式:
1 // 1.從Vuex中按需匯入`mapState`函式 2 import { mapState } from 'vuex' 3 4 // 2.將全域性資料,對映為當前元件的計算屬性 5 computed: { 6 ...mapState(['count']) 7 }
Mutation
Mutation用於變更Store中的資料。
① 只能通過muntation變更Store資料,不可以直接操作Store中的資料。
② 通過這種方式雖然操作起來稍微繁瑣一些,但是可以集中監控所有資料的變化。
1 // 定義motation 2 mutations: { 3 add(state) { 4 // 變更狀態 5 state.count++ 6 }, 7 // 傳參 8 addN(state,n) { 9 state.count+=n 10 } 11 } 12 13 //元件中觸發mutation 14 methods: { 15 handle() { 16 // 觸發mutations 的第一種方式 17 this.$store.commit('add') 18 // 觸發mutations傳參 19 this.$store.commit('addN', 3) 20 } 21 }
this.$store.commit()是觸發mutations的第一種方式,觸發mutations的第二種方式:
1 // 1.從vuex中按需匯入`mapMutations`函式 2 import { mapMutations } from 'vuex' 3 4 // 2.將指定的 mutations函式,對映為當前元件的 methods函式 5 methods: { 6 ...mapMutations(['add','addN']) 7 }
Action
Action用於處理非同步任務。
如果通過非同步變更資料,必須通過Action,而不能使用Mutation,但是在Action中還是要通過觸發Mutation的方式間接變更資料。
// 定義Action actions: { // context 第一個形參可以理解為當前new 的例項 addAsync(context,n) { setTimeout(() => { context.commit('addN',n) },1000) } } // 觸發Action methods: { handle: { // 觸發actions 的第一種方式 // 攜帶引數 this.$store.dispath('addAsync', 5) } }
this.$store.dispatch()是觸發actions的第一種方式,觸發actions的第二種方式:
1 // 1.從vuex中按需匯入`mapActions`函式 2 import { mapActions } from 'vuex' 3 4 // 2.將指定的actions函式,對映為當前元件的methods函式 5 methods: { 6 ...mapActions(['addAsync', 'addNAsync']) 7 }
Getter
Getter用於對Store中的資料進行加工處理形成新的資料。
① Getter可以對Store中已有的資料加工處理之後形成新的資料,類似Vue的計算屬性
② Store中資料發生變化,Getter的資料也會跟著變化。
1 // 定義Getter 2 const strore = new Vuex.Store({ 3 state: { 4 count: 0 5 }, 6 getters: { 7 showNum: state => { 8 return '當前最新的數量是【'+ state.count +'】' 9 } 10 } 11 })
使用getters的第一種方式:
1 this.$store.getters.名稱
使用getters的第二種方式:
1 import { MapGetters } from 'vuex' 2 3 computed: { 4 ...mapGetters(['showNum']) 5 }
namespaced
這個屬性是用來解決不同模組命名衝突的問題:
1 // 不同頁面引入getter、actions、mutations時,要加上模組名 2 // ex 3 ...mapGetters('BadInfo', ['DialogDate']) 4 5 // 第二種寫法 6 this.$store.commit('XXX/SETXXX',sth); 7 this.$store.getters['XXX/getXXX'];