當(dāng)面試被問(wèn)vuex的實(shí)現(xiàn)原理,你要怎么回答?下面本篇文章就來(lái)帶大家深入了解一下vuex的實(shí)現(xiàn)原理,希望對(duì)大家有所幫助!
關(guān)于vuex就不再贅述,簡(jiǎn)單回顧一下:當(dāng)應(yīng)用碰到多個(gè)組件共享狀態(tài)時(shí),簡(jiǎn)單的單向數(shù)據(jù)流很容易被破壞:第一,多個(gè)視圖依賴于同一狀態(tài);第二,來(lái)自不同視圖的行為需要變更同一狀態(tài)。
(資料圖片)
若解決前者使用傳參的方式,則不適用于多層嵌套的組件以及兄弟組件;若解決后者使用父子組件直接引用或事件變更和同步狀態(tài)的多份拷貝,則不利于代碼維護(hù)。
所以,最好的辦法是:把組件的共享狀態(tài)抽取出,以一個(gè)全局單例模式管理!這也正是vuex背后的基本思想。【相關(guān)推薦:vuejs視頻教程、web前端開(kāi)發(fā)】
所以,vuex的大致框架如下:
class Store { constructor() { // state // getters // mutations // actions } // commit // dipatch}接下來(lái),就寫寫看。
vue create vue2-vuex//創(chuàng)建vue2項(xiàng)目yarn add vuex@next --save//安裝vuexyarn serve//啟動(dòng)項(xiàng)目
1、State
(1)使用
//store.js// 倉(cāng)庫(kù)import Vue from "vue"import Vuex from "vuex"import extra from "./extra.js"Vue.use(Vuex) //引入vuex的方式,說(shuō)明Store需要install方法export default new Vuex.Store({ // 倉(cāng)庫(kù)數(shù)據(jù)源 state: { count: 1, dowhat: "addCount" },}//app.vue<template> <div class="testState"> <p>{{mycount}}</p> <p>{{dowhat}}:{{count}}</p> </div></template><script>export default { import { mapState } from "vuex" // 推薦方式 computed: mapState()({ mycount: state => state.count }), // 推薦方式的簡(jiǎn)寫方式 computed: { // 解構(gòu)的是getters ...mapState(["count", "dowhat"]) }, }</script>(2)注意
由于 Vuex 的狀態(tài)存儲(chǔ)是響應(yīng)式的,從 store 實(shí)例中讀取狀態(tài)最簡(jiǎn)單的方法就是在計(jì)算屬性中返回某個(gè)狀態(tài),這種模式導(dǎo)致組件依賴全局狀態(tài)單例。在模塊化的構(gòu)建系統(tǒng)中,在每個(gè)需要使用 state 的組件中需要頻繁地導(dǎo)入,并且在測(cè)試組件時(shí)需要模擬狀態(tài)
Vuex 通過(guò) store選項(xiàng),提供了一種機(jī)制將狀態(tài)從根組件“注入”到每一個(gè)子組件中(需調(diào)用 Vue.use(Vuex))
(3)實(shí)現(xiàn)
所以除了Store內(nèi)部的五大屬性以外,還需要考慮插件的一個(gè)install方法,所以大致框架如下:
class Store { constructor() { // state // getters // mutations // actions //modules } // commit // dipatch}let Vuex = { Store, Install}export default Vuex所以,接下來(lái)就可以具體實(shí)現(xiàn)了,
class Store { constructor(options) { // state this.state = options.state }}let install = function(_Vue) { _Vue.mixin({ beforeCreate() { //在組件創(chuàng)建之前自動(dòng)調(diào)用,每個(gè)組件都有這個(gè)鉤子 if (this.$options && this.$options.store) { //this.$options讀取根組件 this.$store = this.$options.store } else { this.$store = this.$parent && this.$parent.$store } } })}然而,上述的state的實(shí)現(xiàn)有一個(gè)缺點(diǎn):當(dāng)改變數(shù)據(jù)的時(shí)候,state的數(shù)據(jù)不能動(dòng)態(tài)的渲染。所以如何把state里的數(shù)據(jù)成為響應(yīng)式成為關(guān)鍵問(wèn)題?實(shí)際上,類似vue里的data,也可以通過(guò)這種方式讓其成為響應(yīng)式。那么就得從install方法中傳入Vue,所以改變后:
let Vue=nullclass Store { constructor(options) { // state this.vm = new _Vue({ data: { state: options.state//data中的數(shù)據(jù)才是響應(yīng)式 } }) } get state() { return this.vm.state }}let install = function(_Vue) {//用于Vue.use(plugin) Vue=_Vue _Vue.mixin({ onBeforeCreate() { //在組件創(chuàng)建之前自動(dòng)調(diào)用,每個(gè)組件都有這個(gè)鉤子 if (this.$options && this.$options.store) { //this.$options讀取根組件 this.$store = this.$options.store } else { this.$store = this.$parent && this.$parent.$store } } })}2、getters
(1)使用
//store.jsexport default new Vuex.Store({ // 計(jì)算屬性 getters: { // 這里的函數(shù)不需要調(diào)用,可以直接使用,官方默認(rèn)前面有g(shù)et getCount(state) {//接受 state 作為其第一個(gè)參數(shù) return state.count * 100; } },}(2)注意
有時(shí)候我們需要從 store 中的 state 中派生出一些狀態(tài)(比如增加,刪除,過(guò)濾等等),Vuex 允許我們?cè)?store 中定義“getter”(可以認(rèn)為是 store 的計(jì)算屬性)。就像計(jì)算屬性一樣,getter 的返回值會(huì)根據(jù)它的依賴被緩存起來(lái),且只有當(dāng)它的依賴值發(fā)生了改變才會(huì)被重新計(jì)算,Getter 接受 state 作為其第一個(gè)參數(shù),getter 在通過(guò)方法訪問(wèn)時(shí),每次都會(huì)去進(jìn)行調(diào)用,而不會(huì)緩存結(jié)果
(3)實(shí)現(xiàn)
// getters let getters = options.getters || {} this.getters = {} Object.keys(getters).forEach(getterName => { Object.defineProperty(this.getters, getterName, { get: () => { return getters[getterName](this.state) } }) })3、mutations
(1)使用
//store.jsexport default new Vuex.Store({ // 相當(dāng)于methods mutations: { // mutations內(nèi)部的函數(shù),天生具備一個(gè)形參 add(state, n) { state.count += n; }, decrease(state, n) { state.count -= n; } },}methods: { submit() { console.log("success"); }, // 解構(gòu)倉(cāng)庫(kù)mutations里面的方法,要啥解構(gòu)啥 ...mapMutations(["add", "decrease"]), // this.$store.commit("add"), ...mapActions(["addAction", "decreaseAction"]), // this.addAction()調(diào)用actions里面的方法 // this.$store.dispatch("add"),}(2)注意
更改 Vuex 的 store 中的狀態(tài)的唯一方法是提交 mutation。Vuex 中的 mutation 類似于事件:每個(gè) mutation 都有一個(gè)字符串的 事件類型 (type)和 一個(gè) 回調(diào)函數(shù) (handler)。這個(gè)回調(diào)函數(shù)就是進(jìn)行狀態(tài)更改的地方,并且它會(huì)接受 state 作為第一個(gè)參數(shù),不能直接調(diào)用一個(gè) mutation handler。這個(gè)選項(xiàng)更像是事件注冊(cè):“當(dāng)觸發(fā)一個(gè)類型為 increment的 mutation 時(shí),調(diào)用此函數(shù)?!币獑拘岩粋€(gè) mutation handler,你需要以相應(yīng)的 type 調(diào)用 store.commit方法
可以向 store.commit傳入額外的參數(shù),即 mutation 的 載荷(payload),在大多數(shù)情況下,載荷應(yīng)該是一個(gè)對(duì)象,這樣可以包含多個(gè)字段并且記錄的 mutation 會(huì)更易讀
(3)實(shí)現(xiàn)
// mutations let mutations = options.mutations || {} this.mutations = {} Object.keys(mutations).forEach(mutationName => { this.mutations[mutationName] = (arg) => {//保證多個(gè)(第二個(gè))參數(shù)的傳入 mutations[mutationName](this.state, arg) } }) commit = (method, arg) => {//使用箭頭函數(shù)改變被調(diào)用的this的指向 // console.log(this); this.mutations[method](arg) }4、actions
(1)使用
//store.jsexport default new Vuex.Store({ actions: { addAction(context) { // 在這里調(diào)用add方法 context.commit("add", 10); }, decreaseAction({ commit }) { commit("decreaseAction", 5) } },}(2)注意
Action 提交的是 mutation,而不是直接變更狀態(tài)。Action 可以包含任意異步操作Action 函數(shù)接受一個(gè)與 store 實(shí)例具有相同方法和屬性的 context 對(duì)象Action 通過(guò)store.dispatch方法觸發(fā)Action 通常是異步的,store.dispatch可以處理被觸發(fā)的 action 的處理函數(shù)返回的 Promise,并且 store.dispatch仍舊返回 Promise一個(gè) store.dispatch在不同模塊中可以觸發(fā)多個(gè) action 函數(shù)。在這種情況下,只有當(dāng)所有觸發(fā)函數(shù)完成后,返回的 Promise 才會(huì)執(zhí)行(3)實(shí)現(xiàn)
// actions let actions = options.actions || {} this.actions = {} Object.keys(actions).forEach(actionName => { this.actions[actionName] = (arg) => { actions[actionName](this, arg) } }) dispatch=(method, arg) =>{ this.actions[method](arg) }5、modules
(1)使用
// actions let actions = options.actions || {} this.actions = {} Object.keys(actions).forEach(actionName => { this.actions[actionName] = (arg) => { actions[actionName](this, arg) } }) dispatch=(method, arg) =>{ this.actions[method](arg) }//store.js modules: { extra: extra }(2)注意
由于使用單一狀態(tài)樹,應(yīng)用的所有狀態(tài)會(huì)集中到一個(gè)比較大的對(duì)象。當(dāng)應(yīng)用變得非常復(fù)雜時(shí),store 對(duì)象就有可能變得相當(dāng)臃腫,Vuex 允許我們將 store 分割成模塊(module)。每個(gè)模塊擁有自己的 state、mutation、action、getter、甚至是嵌套子模塊——從上至下進(jìn)行同樣方式的分割對(duì)于模塊內(nèi)部的 mutation 和 getter,接收的第一個(gè)參數(shù)是模塊的局部狀態(tài)對(duì)象對(duì)于模塊內(nèi)部的 action,局部狀態(tài)通過(guò)context.state暴露出來(lái),根節(jié)點(diǎn)狀態(tài)則為 context.rootState對(duì)于模塊內(nèi)部的 getter,根節(jié)點(diǎn)狀態(tài)(rootState)會(huì)作為第三個(gè)參數(shù)暴露出來(lái)
let Vue = null//全局的_Vueclass Store { constructor (options) { // state //this.state = options.state 寫法不完美,當(dāng)改變數(shù)據(jù)的時(shí)候,不能動(dòng)態(tài)的渲染,所以需要把data中的數(shù)據(jù)做成響應(yīng)式的 this.vm = new _Vue({ data: { state: options.state//data中的數(shù)據(jù)才是響應(yīng)式 } }) // getters let getters = options.getters || {} this.getters = {} Object.keys(getters).forEach(getterName => { Object.defineProperty(this.getters, getterName, { get: () => { return getters[getterName](this.state) } }) }) // mutations let mutations = options.mutations || {} this.mutations = {} Object.keys(mutations).forEach(mutationName => { this.mutations[mutationName] = (arg) => {//保證多個(gè)(第二個(gè))參數(shù)的傳入 mutations[mutationName](this.state, arg) } }) // actions let actions = options.actions || {} this.actions = {} Object.keys(actions).forEach(actionName => { this.actions[actionName] = (arg) => { actions[actionName](this, arg) } }) } dispatch=(method, arg) =>{ this.actions[method](arg) } commit = (method, arg) => { // console.log(this); this.mutations[method](arg) } get state() { return this.vm.state }}let install = function(_Vue) { Vue = _Vue Vue.mixin({ beforeCreate() {//在組件創(chuàng)建之前自動(dòng)調(diào)用,每個(gè)組件都有這個(gè)鉤子 if (this.$options && this.$options.store) { // this.$options讀取到根組件 this.$store = this.$options.store } else { // //如果不是根組件的話,也把$store掛到上面,因?yàn)槭菢錉罱M件 this.$store = this.$parent && this.$parent.$store } } })}let Vuex = { Store, install}export default Vuex(學(xué)習(xí)視頻分享:vuejs入門教程、編程基礎(chǔ)視頻)
以上就是深入了解vuex的實(shí)現(xiàn)原理的詳細(xì)內(nèi)容,更多請(qǐng)關(guān)注php中文網(wǎng)其它相關(guān)文章!
關(guān)鍵詞: