摘要:提供了函數,它把直接映射到我們的組件中,先給出的使用值為值為讓我們看看的源碼實現規范當前的命名空間。在中,都是同步事務。同步的意義在于這樣每一個執行完成后都可以對應到一個新的狀態和一樣,這樣就可以打個存下來,然后就可以隨便了。
Vue 組件中獲得 Vuex 狀態
按官網說法:“由于 Vuex 的狀態存儲是響應式的,從 store 實例中讀取狀態最簡單的方法就是在計算屬性中返回某個狀態”,本文結合下面的demo進行分析:
import Vue from "vue" import Vuex from "vuex" Vue.use(Vuex) const vueStore = new Vuex.Store({ state: { count: 0 }, mutations: { increment (state) { state.count++ } } }) let vm = new Vue({ el: "#app", store: vueStore, template: "{{count}}", computed: { count(){ return this.$store.state.count } } })
下面主要分析為什么可以通過this.$store直接訪問vueStore對象。先看看Vue.use方法
Vue.use = function (plugin) { //插件只能注冊一次 var installedPlugins = (this._installedPlugins || (this._installedPlugins = [])); if (installedPlugins.indexOf(plugin) > -1) { return this } //拼接參數,將Vue作為第一個參數 // additional parameters var args = toArray(arguments, 1); args.unshift(this); //調用plugin.install或plugin方法 if (typeof plugin.install === "function") { plugin.install.apply(plugin, args); } else if (typeof plugin === "function") { plugin.apply(null, args); } installedPlugins.push(plugin); return this };
再看Vuex源碼,Vuex其實是下面這個對象
{ Store: Store, install: install, mapState: mapState, mapMutations: mapMutations, mapGetters: mapGetters, mapActions: mapActions, createNamespacedHelpers: createNamespacedHelpers }
因此Vue.use(Vuex)其實想到于Vuex.install()
let Vue; // bind on install function install (_Vue) { Vue = _Vue; applyMixin(Vue); } var applyMixin = function (Vue) { var version = Number(Vue.version.split(".")[0]); //Vue2.0處理方法 if (version >= 2) { //將vuexInit方法注冊到beforeCreate鉤子上,當Vue的生命周期走到callHook(vm, "beforeCreate");時觸發vuexInit方法 Vue.mixin({ beforeCreate: vuexInit }); } // Vuex init hook, injected into each instances init hooks list. function vuexInit () { //this就是當前正在被new的Vue對象 var options = this.$options; //將options.store(本例demo中的vueStore)賦值給this.$store,因此可以通過this.$store訪問vueStore對象 // store injection if (options.store) { this.$store = typeof options.store === "function" ? options.store() : options.store; } else if (options.parent && options.parent.$store) { this.$store = options.parent.$store; } } }mapState
通過computed屬性可以獲取到狀態值,但是每一個屬性都要通過this.$store.state訪問不是很方便。vue 提供了 mapState 函數,它把state直接映射到我們的組件中,先給出mapState的使用demo
let mapState = Vuex.mapState let vm = new Vue({ el: "#app", store: vueStore, template: ``, components:{ "my-component":{ template: " {{count}}-{{num}}", computed: mapState({ // {{count}}值為this.$store.state.count count: state => state.count, // {{num}}值為this.$store.state.num, num: "num" }) } } })
讓我們看看mapState的源碼實現
//normalizeNamespace規范當前vuex的命名空間。默認情況下,vuex內部的 action、mutation 和 getter 是注冊在全局命名空間的,本例也是,因此namespace=‘’ var mapState = normalizeNamespace(function (namespace, states) { var res = {}; //規范states參數,將states轉換為map格式,因此mapState支持多種寫法 normalizeMap(states).forEach(function (ref) { var key = ref.key; var val = ref.val; res[key] = function mappedState () { var state = this.$store.state; var getters = this.$store.getters; if (namespace) { var module = getModuleByNamespace(this.$store, "mapState", namespace); if (!module) { return } state = module.context.state; getters = module.context.getters; } return typeof val === "function" ? val.call(this, state, getters) : state[val] }; // mark vuex getter for devtools res[key].vuex = true; }); //mapState其實就是提供簡潔的寫法將this.$store.state[val]賦值給coputed屬性 return res }); function normalizeMap (map) { return Array.isArray(map) ? map.map(function (key) { return ({ key: key, val: key }); }) : Object.keys(map).map(function (key) { return ({ key: key, val: map[key] }); }) } //規范當前vuex的命名空間 function normalizeNamespace (fn) { return function (namespace, map) { if (typeof namespace !== "string") { map = namespace; namespace = ""; } else if (namespace.charAt(namespace.length - 1) !== "/") { namespace += "/"; } return fn(namespace, map) } }store響應式原理
Vue源碼解析(二)中介紹過data的響應式原理:
1、對data進行observe,針對data屬性調用Object.defineProperty設置getter和setter,同時綁定一個dep對象
2、new Watcher(vm, updateComponent, noop)監聽整個dom的變化
3、watcher初始化時調用updateComponent,updateComponent調用render函數更新dom(此時還會將該watcher對象賦值給全局對象Dep.target,進行依賴收集)
4、在watcher對象依賴收集期間,render函數訪問data中的屬性(如本例的data.message),觸發data.message的getter方法,在getter方法中會將data.message綁定的dep對象和wathcer對象建立對應關系(互相加入到對方維護的隊列屬性上)
5、后續data屬性的值變化時dep對象會通知所有依賴此data屬性的watcher對象調用updateComponent方法更新視圖
store響應式的原理也是類似的,new Vuex.Store的過程也會對state進行observe
var Store = function Store (options) { var state = options.state //為了實現state的響應性new一個vue對象 // initialize the store vm, which is responsible for the reactivity resetStoreVM(this, state); } function resetStoreVM (store, state, hot) { //new一個vue對象對data(值為store.state)進行監聽 store._vm = new Vue({ data: { $$state: state }, computed: computed }); }
后續實現和上面的data響應式相同
mutationnew Vuex.Store的過程中會將mutation注冊到store._mutations上
function registerMutation (store, type, handler, local) { var entry = store._mutations[type] || (store._mutations[type] = []); //封裝mutation方法并push到store._mutations[type]上 entry.push(function wrappedMutationHandler (payload) { handler.call(store, local.state, payload); }); }
當執行commit方法時就會執行store._mutations上對應的方法
Store.prototype.commit = function commit (type, payload) { var entry = this._mutations[type]; entry.forEach(function commitIterator (handler) { handler(payload); }); }actions
const vueStore = new Vuex.Store({ state: { count: 1, }, mutations: { increment (state,payload) { state.count+=payload } }, actions: { increment (context,payload) { setTimeout(function () { context.commit("increment",payload) },1000) } } }) vueStore.dispatch("increment",10)
和mutation一樣,new Vuex.Store也會將action注冊到store._actions上,然后通過dispatch調用
function registerAction (store, type, handler, local) { var entry = store._actions[type] || (store._actions[type] = []); //包裝action方法,傳入store對象的commit方法和state等等 entry.push(function wrappedActionHandler (payload, cb) { var res = handler.call(store, { dispatch: local.dispatch, commit: local.commit, getters: local.getters, state: local.state, rootGetters: store.getters, rootState: store.state }, payload, cb); //action的返回不是promise會返回Promise.resolve(res) if (!isPromise(res)) { res = Promise.resolve(res); } return res }); } Store.prototype.dispatch = function dispatch (_type, _payload) { var entry = this._actions[type]; return entry.length > 1 ? Promise.all(entry.map(function (handler) { return handler(payload); })) : entry[0](payload) };
看到action和mutation的源碼實現,你不禁要問了,這不是基本一樣的嗎,那干嘛還要多此一舉?
vuex官網的解釋:在 mutation 中混合異步調用會導致你的程序很難調試。例如,當你能調用了兩個包含異步回調的 mutation 來改變狀態,你怎么知道什么時候回調和哪個先回調呢?這就是為什么我們要區分這兩個概念。在 Vuex 中,mutation 都是同步事務。
知乎上有個問題“vuex中為什么把把異步操作封裝在action,把同步操作放在mutations?“,vue的作者尤雨溪的解釋:事實上在 vuex 里面 actions 只是一個架構性的概念,并不是必須的,說到底只是一個函數,你在里面想干嘛都可以,只要最后觸發 mutation 就行。異步競態怎么處理那是用戶自己的事情。vuex 真正限制你的只有 mutation 必須是同步的這一點(在 redux 里面就好像 reducer 必須同步返回下一個狀態一樣)。
同步的意義在于這樣每一個 mutation 執行完成后都可以對應到一個新的狀態(和 reducer 一樣),這樣 devtools 就可以打個 snapshot 存下來,然后就可以隨便 time-travel 了。
我個人的理解這是vuex的使用規范問題,mutation中使用異步也不會有大問題,但是按規范開發能讓項目結構更清晰,調試更方便,下圖是用vue devtool調試的vuex官方例子(https://github.com/vuejs/vuex...),mutation的觸發時間線一目了然
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/89885.html
摘要:五六月份推薦集合查看最新的請點擊集前端最近很火的框架資源定時更新,歡迎一下。蘇幕遮燎沈香宋周邦彥燎沈香,消溽暑。鳥雀呼晴,侵曉窺檐語。葉上初陽乾宿雨,水面清圓,一一風荷舉。家住吳門,久作長安旅。五月漁郎相憶否。小楫輕舟,夢入芙蓉浦。 五、六月份推薦集合 查看github最新的Vue weekly;請::點擊::集web前端最近很火的vue2框架資源;定時更新,歡迎 Star 一下。 蘇...
摘要:五六月份推薦集合查看最新的請點擊集前端最近很火的框架資源定時更新,歡迎一下。蘇幕遮燎沈香宋周邦彥燎沈香,消溽暑。鳥雀呼晴,侵曉窺檐語。葉上初陽乾宿雨,水面清圓,一一風荷舉。家住吳門,久作長安旅。五月漁郎相憶否。小楫輕舟,夢入芙蓉浦。 五、六月份推薦集合 查看github最新的Vue weekly;請::點擊::集web前端最近很火的vue2框架資源;定時更新,歡迎 Star 一下。 蘇...
摘要:哪吒別人的看法都是狗屁,你是誰只有你自己說了才算,這是爹教我的道理。哪吒去他個鳥命我命由我,不由天是魔是仙,我自己決定哪吒白白搭上一條人命,你傻不傻敖丙不傻誰和你做朋友太乙真人人是否能夠改變命運,我不曉得。我只曉得,不認命是哪吒的命。 showImg(https://segmentfault.com/img/bVbwiGL?w=900&h=378); 出處 查看github最新的Vue...
摘要:中文官網英文官網組織發出一個問題之后,不要暫時的離開電腦,如果沒有把握先不要提問。珍惜每一次提問,感恩每一次反饋,每個人工作還是業余之外抽出的時間有限,充分準備好應有的資源之后再發問,有利于問題能夠高效質量地得到解決。 Vue.js資源分享 更多資源請Star:https://github.com/maidishike... 文章轉自:https://github.com/maid...
閱讀 2722·2021-11-11 17:21
閱讀 613·2021-09-23 11:22
閱讀 3578·2019-08-30 15:55
閱讀 1641·2019-08-29 17:15
閱讀 573·2019-08-29 16:38
閱讀 904·2019-08-26 11:54
閱讀 2504·2019-08-26 11:53
閱讀 2750·2019-08-26 10:31