Vue 全家桶,深入Vue 的世界

// button.jsimport ButtonComponent from './button.vue';const Button={ install:function (Vue) { Vue.component('Button',ButtonComponent) }}export default Button;// main.jsimport Button from './component/button.js';Vue.use(Button);

完成上面的步驟就可以在全局使用button組件了,其實最重要的 Vue.component('Button',ButtonComponent) , Vue.use(Button) 會執行install方法,也可以直接在 main.js 使用 Vue.component() 註冊全局組件。

props

子組件是不能直接修改props的。

Vue組件之間的通信問題可以看這裡…

Vue 組件 extend

使用 Vue.extend 就是構造了一個Vue構造函數的“子類”。它的參數是一個 包含組件選項的對象 ,其中 data 選項必須是函數。

import Vue from 'vue'// 一個包含組件選項的對象const compoent = { props: { active: Boolean, propOne: String }, template: ` 
see me if active
`, data () { return { text: 0 } }, mounted () { // 這個mounted先打印 console.log('comp mounted'); }}// 創建一個“子類”const CompVue = Vue.extend(compoent);// 實例化一個“子類”new CompVue({ el: '#root', propsData: { // 這裡如果用props,組件內是拿不到值的 propOne: 'xxx' }, data: { text: '123' }, mounted () { console.log('instance mounted'); }})const component2 = { extends: component, // 繼承於 component data(){ return { text: 1 } }, mounted () { this.$parent.text = '111111111'; // 可以改變父組件的值 console.log('comp2 mounted') }}new Vue({ name: 'Root', el: '#root', mounted () { console.log(this.$parent.$options.name) }, components: { Comp: componet2 }, data: { text: 23333 }, template: `
{{text}}
`})

Vue 組件高級屬性

Vue 組件插槽

通常我們會向一個組件中傳入一些自定義的內容,這個時候就可以用到插槽。插槽內可以包含任何模板代碼,包括HTML或者是一個組件。

// 定義一個帶插槽的組件const component = { name: 'comp', template: ` 
`}new CompVue({ el: '#root', components:{ Comp }, template: `

這裡的內容顯示在插槽內

`}

具名插槽

官網鏈接: https://cn.vuejs.org/v2/guide/components-slots.html

具名插槽的使用:

第一種:在一個父組件的

     

第二種:直接在普通元素上使用

 

Here might be a page title

A paragraph for the main content.

And another one.

Here's some contact info

插槽的默認內容

在插槽中可以設置一個默認內容,如果用戶沒有設置新的內容,則會顯示默認內容

作用域插槽

2.1.0+ 新增 在 2.5.0+, slot-scope 不再限制在

const component = { name: 'comp', template: ` 
`}new CompVue({ el: '#root', components:{ Comp }, template: `

{{props.value}} {{props.name}}

// 456 finget
`}

provide/inject 跨級組件交互

2.2.0 新增

這對選項需要一起使用,以允許一個祖先組件向其所有子孫後代注入一個依賴,不論組件層次有多深,並在起上下游關係成立的時間裡始終生效。

// 父級組件提供 'foo'var Provider = { provide: { foo: 'bar' }, // ...}// 子組件注入 'foo'var Child = { inject: ['foo'], created () { console.log(this.foo) // => "bar" } // ...}

如果是注入一個父級組件內部的值,provide需要作為一個函數,類似於data

const component = { name: 'comp', inject: ["value"] template: ` 
子組件 {{value}}
`}new CompVue({ el: '#root', data() { return { value: '123' } } components:{ Comp }, provide() { // 這裡如果只是一個對象的話是無法拿到this.value的 return { value: this.value } }, template: `
`}

如果要監聽父級組件的屬性值的變化,從而自動更新子組件的值,需要手動實現監聽

const component = { name: 'comp', inject: ["data"] template: ` 
子組件 {{data.value}}
`}...provide() { const data = {} // 這是vue雙向綁定的基礎 Object.defineProperty(data,"value",{ get: () => this.value, enumerable: true }) return { data }},...

Vue 的render

Vue模板的解析: https://finget.github.io/2018/05/31/mvvm-vue/

Vue-router

router構建選項

重定向:

{ path: '/', redirect: '/app'}

History 模式:

const router = new VueRouter({ mode: 'history', routes: [...]})

vue-router 默認 hash 模式 —— 使用 URL 的 hash 來模擬一個完整的 URL,於是當 URL 改變時,頁面不會重新加載。

不過這種模式要玩好,還需要後臺配置支持。因為我們的應用是個單頁客戶端應用,如果後臺沒有正確的配置,當用戶在瀏覽器直接訪問 http://oursite.com/user/id 就會返回 404,這就不好看了。

給個警告頁:

const router = new VueRouter({ mode: 'history', routes: [ { path: '*', component: NotFoundComponent } ]})

base

const router = new VueRouter({ mode: 'history', base: '/base/', routes: [ { path: '/hello', component: hello } ]})

當訪問 localhost:8080/hello 會變成 localhost:8080/base/hello ,所有的路由路徑都會加上 /base ,當然手動刪除 /base 還是可以打開頁面

linkActiveClass 和 linkExactActiveClass

applogin 

router-link 在頁面中會渲染成 a 標籤,點擊之後會添加兩個類名: router-link-exact-active 和 router-link-active

const router = new VueRouter({ linkActiveClass: 'active-link', linkExactActiveClass: 'exact-active-link'})

這相當於是重新命名了兩個類名。

兩者的不同點:

loginlogin exact

上面這兩個路由有一部分 /login 是相同的,在點擊了 login exact 路由調轉到 /login/exact 後:

/login 上還保留了 router-link-active 類名

Vue 全家桶,深入Vue 的世界

scrollBehavior

使用前端路由,當切換到新路由時,想要頁面滾到頂部,或者是保持原先的滾動位置,就像重新加載頁面那樣。

注意: 這個功能只在支持 history.pushState 的瀏覽器中可用。

const router = new VueRouter({ scrollBehavior(to, form, savedPosition){ if (savedPosition) { return savedPosition } else { return { x: 0, y: 0 } } }, routes: [...]})

scrollBehavior 方法接收 to 和 from 路由對象。第三個參數 savedPosition 當且僅當 popstate 導航 (通過瀏覽器的 前進/後退 按鈕觸發) 時才可用。

parseQuery 和 stringifyQuery

提供自定義查詢字符串的解析/反解析函數。覆蓋默認行為。

const router = new VueRouter({ parseQuery (query) { console.log(query) }, stringifyQuery (obj) { console.log(obj) }})

fallback

當瀏覽器不支持 history.pushState 控制路由是否應該回退到 hash 模式。默認值為 true。

在 IE9 中,設置為 false 會使得每個 router-link 導航都觸發整頁刷新。它可用於工作在 IE9 下的服務端渲染應用,因為一個 hash 模式的 URL 並不支持服務端渲染。

const router = new VueRouter({ fallback: true})

路由元信息

官網例子:

const router = new VueRouter({ routes: [ { path: '/foo', component: Foo, children: [ { path: 'bar', component: Bar, // a meta field meta: { requiresAuth: true } } ] } ]})

那麼如何訪問這個 meta 字段呢?

首先,我們稱呼 routes 配置中的每個路由對象為 路由記錄。路由記錄可以是嵌套的,因此,當一個路由匹配成功後,他可能匹配多個路由記錄

例如,根據上面的路由配置, /foo/bar 這個 URL 將會匹配父路由記錄以及子路由記錄。

一個路由匹配到的所有路由記錄會暴露為 $route 對象 (還有在導航守衛中的路由對象) 的 $route.matched 數組。因此,我們需要遍歷 $route.matched 來檢查路由記錄中的 meta 字段。

下面例子展示在全局導航守衛中檢查元字段:

router.beforeEach((to, from, next) => { if (to.matched.some(record => record.meta.requiresAuth)) { // this route requires auth, check if logged in // if not, redirect to login page. if (!auth.loggedIn()) { next({ path: '/login', query: { redirect: to.fullPath } }) } else { next() } } else { next() // 確保一定要調用 next() }})

命名視圖

在一個路由下展示多個視圖組件,用的並不多

// 在這個頁面中要分別展示三個視圖 // 默認的 // 視圖a // 視圖bconst router = new VueRouter({ routes: [ { path: '/', components: { // 加s default: Foo, // 對應默認router-view a: Bar, // name = "a" b: Baz // name = "b" } } ]})

導航守衛

路由改變時,按順序觸發的鉤子函數

全局守衛

const router = new VueRouter({ ... })router.beforeEach((to, from, next) => { console.log('before each invoked'); next();})router.beforeResolve((to, from, next) => { console.log('before resolve invoked'); next();})

每個守衛方法接收三個參數:

  • to: Route : 即將要進入的目標 路由對象
  • from: Route : 當前導航正要離開的 路由對象
  • next: Function : 一定要調用該方法來 resolve 這個鉤子。執行效果依賴 next 方法的調用參數。
  • next() : 進行管道中的下一個鉤子。如果全部鉤子執行完了,則導航的狀態就是 confirmed (確認的)。
  • next(false) : 中斷當前的導航。如果瀏覽器的 URL 改變了 (可能是用戶手動或者瀏覽器後退按鈕),那麼 URL 地址會重置到 from 路由對應的地址。
  • next('/') 或者 next({ path: '/' }) : 跳轉到一個不同的地址。當前的導航被中斷,然後進行一個新的導航。你可以向 next 傳遞任意位置對象,且允許設置諸如 replace: true、name: 'home' 之類的選項以及任何用在 router-link 的 to prop 或 router.push 中的選項。
  • next(error) : (2.4.0+) 如果傳入 next 的參數是一個 Error 實例,則導航會被終止且該錯誤會被傳遞給 router.onError() 註冊過的回調。

確保要調用 next 方法,否則鉤子就不會被 resolved 。

路由對象

一個路由對象 (route object) 表示當前激活的路由的狀態信息,包含了當前 URL 解析得到的信息,還有 URL 匹配到的路由記錄 (route records)。

路由對象是不可變 (immutable) 的,每次成功的導航後都會產生一個新的對象。

路由對象屬性:

  • $route.path
  • 類型: string
  • 字符串,對應當前路由的路徑,總是解析為絕對路徑,如 “/foo/bar”。
  • $route.params
  • 類型: Object
  • 一個 key/value對象,包含了動態片段和全匹配片段,如果沒有路由參數,就是一個空對象。
  • $route.query
  • 類型: Object
  • 一個 key/value 對象,表示 URL 查詢參數。例如,對於路徑 /foo?user=1,則有 $route.query.user == 1,如果沒有查詢參數,則是個空對象。
  • $route.hash
  • 類型: string
  • 當前路由的 hash 值 (帶 #) ,如果沒有 hash 值,則為空字符串。
  • $route.fullPath
  • 類型: string
  • 完成解析後的 URL,包含查詢參數和 hash 的完整路徑。
  • $route.matched
  • 類型: Array 一個數組,包含當前路由的所有嵌套路徑片段的路由記錄 。路由記錄就是 routes 配置數組中的對象副本 (還有在 children 數組)。
const router = new VueRouter({ routes: [ // 下面的對象就是路由記錄 { path: '/foo', component: Foo, children: [ // 這也是個路由記錄 { path: 'bar', component: Bar } ] } ]}) 

當 URL 為 /foo/bar, $route.matched 將會是一個包含從上到下的所有對象 (副本)。

  • $route.name
  • 當前路由的名稱,如果有的話。(查看命名路由)
  • $route.redirectedFrom
  • 如果存在重定向,即為重定向來源的路由的名字

全局後置鉤子

router.afterEach((to, from) => { console.log('after each invoked');})

路由獨享的守衛

const router = new VueRouter({ routes: [ { path: '/foo', component: Foo, beforeEnter: (to, from, next) => { // ... } } ]})

組件內的守衛

const Foo = { template: `...`, beforeRouteEnter (to, from, next) { // 在渲染該組件的對應路由被 confirm 前調用 // 不!能!獲取組件實例 `this` // 因為當守衛執行前,組件實例還沒被創建 }, beforeRouteUpdate (to, from, next) { // 在當前路由改變,但是該組件被複用時調用 // 舉例來說,對於一個帶有動態參數的路徑 /foo/:id,在 /foo/1 和 /foo/2 之間跳轉的時候, // 由於會渲染同樣的 Foo 組件,因此組件實例會被複用。而這個鉤子就會在這個情況下被調用。 // 可以訪問組件實例 `this` }, beforeRouteLeave (to, from, next) { // 導航離開該組件的對應路由時調用 // 可以訪問組件實例 `this` }} 

beforeRouteEnter 守衛 不能 訪問 this,因為守衛在導航確認前被調用,因此即將登場的新組件還沒被創建。

不過,你可以通過傳一個回調給 next來訪問組件實例。在導航被確認的時候執行回調,並且把組件實例作為回調方法的參數。

beforeRouteEnter (to, from, next) { next(vm => { // 通過 `vm` 訪問組件實例 })}

完整的導航解析流程

  1. 導航被觸發。
  2. 在失活的組件裡調用離開守衛。
  3. 調用全局的 beforeEach 守衛。
  4. 在重用的組件裡調用 beforeRouteUpdate 守衛 (2.2+)。
  5. 在路由配置裡調用 beforeEnter 。
  6. 解析異步路由組件。
  7. 在被激活的組件裡調用 beforeRouteEnter 。
  8. 調用全局的 beforeResolve 守衛 (2.5+)。
  9. 導航被確認。
  10. 調用全局的 afterEach 鉤子。
  11. 觸發 DOM 更新。
  12. 用創建好的實例調用 beforeRouteEnter 守衛中傳給 next 的回調函數。

異步路由

在路由文件中,直接import所有組件勢必造成頁面首次渲染時間變長,異步路由,當進入對應的路由才加載對應的頁面。

const router = new VueRouter({ routes: [ { path: '/foo', component: () => import('../view/...'), } ]})

這種寫法需要安裝 syntax-dynamic-import ,並在 .babelrc 進行配置

// .babelrc{ "plugins": ["syntax-dynamic-import"]}

Vux

以下內容來自 官網:https://vuex.vuejs.org/zh/

簡單使用vuex

// store.jsimport Vuex from 'vuex'import Vue from 'vue'Vue.use(Vuex)const store = new Vuex.Store({ state: { count: 0 }, mutations: { updateCount(state, num) { state.count = num } }})export default store// main.jsimport Vue from 'vue'import App from './App'import store from './store/store.js'Vue.config.productionTip = false/* eslint-disable no-new */new Vue({ el: '#app', store, // 掛載 components: { App }, template: ''})// 任意組件mounted(){ console.log(this.$store) let i = 1 setInterval(() => { this.$store.commit('updateCount', i++) })},computed: { count() { return this.$store.state.count }}
Vue 全家桶,深入Vue 的世界

核心概念

State

Vuex 使用單一狀態樹——是的,用一個對象就包含了全部的應用層級狀態。至此它便作為一個“唯一數據源 (SSOT)”而存在。這也意味著,每個應用將僅僅包含一個 store 實例。單一狀態樹讓我們能夠直接地定位任一特定的狀態片段,在調試的過程中也能輕易地取得整個當前應用狀態的快照。

大白話: state就相當於是個全局對象,通過 Vue.use(Vuex) 全局註冊了vuex之後,在任意組件中可以用 this.$store.state 拿到該對象

Vuex的狀態存儲是響應式的,從store實例中讀取狀態最簡單的方法就是在計算屬性中返回某個狀態。

computed: { count() { return this.$store.state.count }}

當 state 中的 count 變化時,自動會更新 computed ,從而改變相關 DOM

mapState 輔助函數

當一個組件需要獲取多個狀態時候,將這些狀態都聲明為計算屬性會有些重複和冗餘。為了解決這個問題,我們可以使用 mapState 輔助函數幫助我們生成計算屬性,讓你少按幾次鍵:

// 在單獨構建的版本中輔助函數為 Vuex.mapStateimport { mapState } from 'vuex'export default { // ... computed: mapState({ // 箭頭函數可使代碼更簡練 count: state => state.count, // 傳字符串參數 'count' 等同於 `state => state.count` countAlias: 'count', // 為了能夠使用 `this` 獲取局部狀態,必須使用常規函數 不能用箭頭函數 countPlusLocalState (state) { return state.count + this.localCount } })}

當映射的計算屬性的名稱與 state 的子節點名稱相同時,我們也可以給 mapState 傳一個字符串數組。

computed: mapState([ // 映射 this.count 為 store.state.count 'count'])// 常用操作computed: { ...mapState(['count'])}// 換一個變量名computed: { ...mapState({ count1 : 'count', count2 : state => state.count })}

Getter

Getter就是vuex種state的computed,通過state派生出新的state,而且它會被緩存起來,只有依賴的state發生變化才會重新計算

export default { fullName(state) { // 默認接收state作為第一個參數 return `${state.firstName}${state.lastName}` }}

mapGetters 輔助函數

getter的使用和state類似,可以把它看成state來用。

import { mapGetters } from 'vuex'export default { // ... computed: { // 使用對象展開運算符將 getter 混入 computed 對象中 ...mapGetters([ 'doneTodosCount', 'anotherGetter', // ... ]) }}

如果想給getter換個名字,方法和state一樣,不重複

Mutation

Mutation必須是同步的

更改 Vuex 的 store 中的狀態的唯一方法是提交 mutation。Vuex 中的 mutation 非常類似於事件:每個 mutation 都有一個字符串的 事件類型 (type) 和 一個 回調函數 (handler)。這個回調函數就是我們實際進行狀態更改的地方,並且它會接受 state 作為第一個參數:

const store = new Vuex.Store({ state: { count: 1 }, mutations: { increment (state) { // 變更狀態 state.count++ } }})

你不能直接調用一個 mutation handler。這個選項更像是事件註冊:“當觸發一個類型為 increment 的 mutation 時,調用此函數。”要喚醒一個 mutation handler,你需要以相應的 type 調用 store.commit 方法:

store.commit('increment')

提交載荷(傳參)

你可以向 store.commit 傳入額外的參數,即 mutation 的 載荷(payload):

// ...mutations: { increment (state, n) { state.count += n }}store.commit('increment', 10)

在大多數情況下,載荷應該是一個 對象 ,這樣可以包含多個字段並且記錄的 mutation 會更易讀:

// ...mutations: { increment (state, payload) { state.count += payload.amount }}store.commit('increment', { amount: 10})

對象風格的提交方式

提交 mutation 的另一種方式是直接使用包含 type 屬性的對象:

store.commit({ type: 'increment', amount: 10})

當使用對象風格的提交方式,整個對象都作為載荷傳給 mutation 函數,因此 handler 保持不變:

mutations: { increment (state, payload) { state.count += payload.amount }}

使用常量替代 Mutation 事件類型

使用常量替代 mutation 事件類型在各種 Flux 實現中是很常見的模式。這樣可以使 linter之類的工具發揮作用,同時把這些常量放在單獨的文件中可以讓你的代碼合作者對整個 app 包含的 mutation 一目瞭然:

// mutation-types.jsexport const SOME_MUTATION = 'SOME_MUTATION'// store.jsimport Vuex from 'vuex'import { SOME_MUTATION } from './mutation-types'const store = new Vuex.Store({ state: { ... }, mutations: { // 我們可以使用 ES2015 風格的計算屬性命名功能來使用一個常量作為函數名 [SOME_MUTATION] (state) { // mutate state } }})

在組件中提交 Mutation

你可以在組件中使用 this.$store.commit('xxx') 提交 mutation ,或者使用 mapMutations 輔助函數將組件中的 methods 映射為 store.commit 調用(需要在根節點注入 store)。

import { mapMutations } from 'vuex'export default { // ... methods: { ...mapMutations([ 'increment', // 將 `this.increment()` 映射為 `this.$store.commit('increment')` // `mapMutations` 也支持載荷: 'incrementBy' // 將 `this.incrementBy(amount)` 映射為 `this.$store.commit('incrementBy', amount)` ]), ...mapMutations({ add: 'increment' // 將 `this.add()` 映射為 `this.$store.commit('increment')` }) }}

Action

Action 可以包含異步操作

Action跟Mutation類似,Action是調用 commit 方法,提交 mutation 的。

const store = new Vuex.Store({ state: { count: 0 }, mutations: { increment (state) { state.count++ } }, actions: { increment (context) { context.commit('increment') } }})

Action 函數接受一個與 store 實例具有相同方法和屬性的 context 對象,因此你可以調用 context.commit 提交一個 mutation ,或者通過 context.state 和 context.getters 來獲取 state 和 getters 。

實踐中,我們會經常用到 ES2015 的 參數解構 來簡化代碼(特別是我們需要調用 commit 很多次的時候):

actions: {// {commit} = context 解構出來 increment ({ commit }) { commit('increment') }}

實際代碼:

Vue 全家桶,深入Vue 的世界

在組件中分發 Action

你在組件中使用 this.$store.dispatch('xxx') 分發 action ,或者使用 mapActions 輔助函數將組件的 methods 映射為 store.dispatch 調用(需要先在根節點注入 store):

import { mapActions } from 'vuex'export default { // ... methods: { ...mapActions([ 'increment', // 將 `this.increment()` 映射為 `this.$store.dispatch('increment')` // `mapActions` 也支持載荷: 'incrementBy' // 將 `this.incrementBy(amount)` 映射為 `this.$store.dispatch('incrementBy', amount)` ]), ...mapActions({ add: 'increment' // 將 `this.add()` 映射為 `this.$store.dispatch('increment')` }) }}

嚴格模式

開啟嚴格模式,僅需在創建 store 的時候傳入 strict: true:

const store = new Vuex.Store({ // ... strict: true})

在嚴格模式下,無論何時發生了狀態變更且不是由 mutation 函數引起的,將會拋出錯誤。這能保證所有的狀態變更都能被調試工具跟蹤到。

開發環境與發佈環境

不要在發佈環境下啟用嚴格模式!嚴格模式會深度監測狀態樹來檢測不合規的狀態變更——請確保在發佈環境下關閉嚴格模式,以避免性能損失。

類似於插件,我們可以讓構建工具來處理這種情況:

const store = new Vuex.Store({ // ... strict: process.env.NODE_ENV !== 'production'})

原文鏈接:https://finget.github.io/2018/06/28/vue-family/?utm_source=tuicool&utm_medium=referral 如有侵權請聯繫刪除,謝謝

感覺文章不錯的同學可以通過原文鏈接查看作者其他JS系列文章

Vue 全家桶,深入Vue 的世界


分享到:


相關文章: