欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

利用Dectorator分模塊存儲Vuex狀態(tài)的實現

 更新時間:2019年02月05日 09:24:07   作者:coffee-ai  
這篇文章主要介紹了利用Dectorator分模塊存儲Vuex狀態(tài)的實現,小編覺得挺不錯的,現在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧

1、引言

在H5的Vue項目中,最為常見的當為單頁應用(SPA),利用Vue-Router控制組件的掛載與復用,這時使用Vuex可以方便的維護數據狀態(tài)而不必關心組件間的數據通信。但在Weex中,不同的頁面之間使用不同的執(zhí)行環(huán)境,無法共享數據,此時多為通過BroadcastChannel或storage模塊來實現數據通信,本文主要使用修飾器(Decorator)來擴展Vuex的功能,實現分模塊存儲數據,并降低與業(yè)務代碼的耦合度。

2、Decorator

設計模式中有一種裝飾器模式,可以在運行時擴展對象的功能,而無需創(chuàng)建多個繼承對象。類似的,Decorator可以在編譯時擴展一個對象的功能,降低代碼耦合度的同時實現多繼承一樣的效果。

2.1、Decorator安裝

目前Decorator還只是一個提案,在生產環(huán)境中無法直接使用,可以用babel-plugin-transform-decorators-legacy來實現。使用npm管理依賴包的可以執(zhí)行以下命令:

npm install babel-plugin-transform-decorators-legacy -D

然后在 .babelrc 中配置

{
  "plugins": [
    "transform-decorators-legacy"
  ]
}

或者在webpack.config.js中配置

{
  test: /\.js$/,
  loader: "babel-loader",
  options: [
    plugins: [
      require("babel-plugin-transform-decorators-legacy").default
    ]
  ]
}

這時可以在代碼里編寫Decorator函數了。

2.2、Decorator的編寫

在本文中,Decorator主要是對方法進行修飾,主要代碼如下:
decorator.js

const actionDecorator = (target, name, descriptor) => {
  const fn = descriptor.value;
  descriptor.value = function(...args) {
    console.log('調用了修飾器的方法');
    return fn.apply(this, args);
  };
  return descriptor;
};

store.js

const module = {
  state: () => ({}),
  actions: {
    @actionDecorator
    someAction() {/** 業(yè)務代碼 **/ },
  },
};

可以看到,actionDecorator修飾器的三個入參和Object.defineProperty一樣,通過對module.actions.someAction函數的修飾,實現在編譯時重寫someAction方法,在調用方法時,會先執(zhí)行console.log('調用了修飾器的方法');,而后再調用方法里的業(yè)務代碼。對于多個功能的實現,比如存儲數據,發(fā)送廣播,打印日志和數據埋點,增加多個Decorator即可。

3、Vuex

Vuex本身可以用subscribe和subscribeAction訂閱相應的mutation和action,但只支持同步執(zhí)行,而Weex的storage存儲是異步操作,因此需要對Vuex的現有方法進行擴展,以滿足相應的需求。

3.1、修飾action

在Vuex里,可以通過commit mutation或者dispatch action來更改state,而action本質是調用commit mutation。因為storage包含異步操作,在不破壞Vuex代碼規(guī)范的前提下,我們選擇修飾action來擴展功能。

storage使用回調函數來讀寫item,首先我們將其封裝成Promise結構:

storage.js

const storage = weex.requireModule('storage');
const handler = {
 get: function(target, prop) {
  const fn = target[prop];
  // 這里只需要用到這兩個方法
  if ([
   'getItem',
   'setItem'
  ].some(method => method === prop)) {
   return function(...args) {
    // 去掉回調函數,返回promise
    const [callback] = args.slice(-1);
    const innerArgs = typeof callback === 'function' ? args.slice(0, -1) : args;
    return new Promise((resolve, reject) => {
     fn.call(target, ...innerArgs, ({result, data}) => {
      if (result === 'success') {
       return resolve(data);
      }
      // 防止module無保存state而出現報錯
      return resolve(result);
     })
    })
   }
  }
  return fn;
 },
};
export default new Proxy(storage, handler);

通過Proxy,將setItem和getItem封裝為promise對象,后續(xù)使用時可以避免過多的回調結構。

現在我們把storage的setItem方法寫入到修飾器:

decorator.js

import storage from './storage';
// 加個rootKey,防止rootState的namespace為''而導致報錯
// 可自行替換為其他字符串
import {rootKey} from './constant';
const setState = (target, name, descriptor) => {
  const fn = descriptor.value;
  descriptor.value = function(...args) {
    const [{state, commit}] = args;
    // action為異步操作,返回promise,
    // 且需在狀態(tài)修改為fulfilled時再將state存儲到storage
    return fn.apply(this, args).then(async data => {
      // 獲取store的moduleMap
      const rawModule = Object.entries(this._modulesNamespaceMap);
      // 根據當前的commit,查找此action所在的module
      const moduleMap = rawModule.find(([, module]) => {
        return module.context.commit === commit;
      });
      if (moduleMap) {
        const [key, {_children}] = moduleMap;
        const childrenKeys = Object.keys(_children);
        // 只獲取當前module的state,childModule的state交由其存儲,按module存儲數據,避免存儲數據過大
        // Object.fromEntries可使用object.fromentries來polyfill,或可用reduce替代
        const pureState = Object.fromEntries(Object.entries(state).filter(([stateKey]) => {
          return !childrenKeys.some(childKey => childKey === stateKey);
        }));
        await storage.setItem(rootKey + key, JSON.stringify(pureState));
      }
      // 將data沿著promise鏈向后傳遞
      return data;
    });
  };
  return descriptor;
};
export default setState;

完成了setState修飾器功能以后,就可以裝飾action方法了,這樣等action返回的promise狀態(tài)修改為fulfilled后調用storage的存儲功能,及時保存數據狀態(tài)以便在新開Weex頁面加載最新數據。

store.js

import setState from './decorator';
const module = {
  state: () => ({}),
  actions: {
    @setState
    someAction() {/** 業(yè)務代碼 **/ },
  },
};

3.2、讀取module數據

完成了存儲數據到storage以后,我們還需要在新開的Weex頁面實例能自動讀取數據并初始化Vuex的狀態(tài)。在這里,我們使用Vuex的plugins設置來完成這個功能。

首先我們先編寫Vuex的plugin:

plugin.js

import storage from './storage';
import {rootKey} from './constant';
const parseJSON = (str) => {
  try {
    return str ? JSON.parse(str) : undefined;
  } catch(e) {}
  return undefined;
};
const getState = (store) => {
  const getStateData = async function getModuleState(module, path = []) {
    const {_children} = module;
    // 根據path讀取當前module下存儲在storage里的數據
    const data = parseJSON(await storage.getItem(`${path.join('/')}/`)) || {};
    const children = Object.entries(_children);
    if (!children.length) {
      return data;
    }
    // 剔除childModule的數據,遞歸讀取
    const childModules = await Promise.all(
      children.map(async ([childKey, child]) => {
       return [childKey, await getModuleState(child, path.concat(childKey))];
      })
    );
    return {
      ...data,
      ...Object.fromEntries(childModules),
    }
  };
  // 讀取本地數據,merge到Vuex的state
  const init = getStateData(store._modules.root, [rootKey]).then(savedState => {
    store.replaceState(merge(store.state, savedState, {
      arrayMerge: function (store, saved) { return saved },
      clone: false,
    }));
  });
};
export default getState;

以上就完成了Vuex的數據按照module讀取,但Weex的IOS/Andriod中的storage存儲是異步的,為防止組件掛載以后發(fā)送請求返回的數據被本地數據覆蓋,需要在本地數據讀取并merge到state以后再調用new Vue,這里我們使用一個簡易的interceptor來攔截:

interceptor.js

const interceptors = {};
export const registerInterceptor = (type, fn) => {
  const interceptor = interceptors[type] || (interceptors[type] = []);
  interceptor.push(fn);
};
export const runInterceptor = async (type) => {
  const task = interceptors[type] || [];
  return Promise.all(task);
};

這樣plugin.js中的getState就修改為:

import {registerInterceptor} from './interceptor';
const getState = (store) => {
  /** other code **/
  const init = getStateData(store._modules.root, []).then(savedState => {
    store.replaceState(merge(store.state, savedState, {
      arrayMerge: function (store, saved) { return saved },
      clone: false,
    }));
  });
  // 將promise放入攔截器
  registerInterceptor('start', init);
};

store.js

import getState from './plugin';
import setState from './decorator';
const rootModule = {
  state: {},
  actions: {
    @setState
    someAction() {/** 業(yè)務代碼 **/ },
  },
  plugins: [getState],
  modules: {
    /** children module**/
  }
};

app.js

import {runInterceptor} from './interceptor';
// 待攔截器內所有promise返回resolved后再實例化Vue根組件
// 也可以用Vue-Router的全局守衛(wèi)來完成
runInterceptor('start').then(() => {
  new Vue({/** other code **/});
});

這樣就實現了Weex頁面實例化后,先讀取storage數據到Vuex的state,再實例化各個Vue的組件,更新各自的module狀態(tài)。

4、TODO

通過Decorator實現了Vuex的數據分模塊存儲到storage,并在Store實例化時通過plugin分模塊讀取數據再merge到state,提高數據存儲效率的同時實現與業(yè)務邏輯代碼的解耦。但還存在一些可優(yōu)化的點:

1、觸發(fā)action會將所有module中的所有state全部,只需保存所需狀態(tài),避免存儲無用數據。

2、對于通過registerModule注冊的module,需支持自動讀取本地數據。

3、無法通過_modulesNamespaceMap獲取namespaced為false的module,需改為遍歷_children。

在此不再展開,將在后續(xù)版本中實現。

以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持腳本之家。

相關文章

  • vue組件中點擊按鈕后修改輸入框的狀態(tài)實例代碼

    vue組件中點擊按鈕后修改輸入框的狀態(tài)實例代碼

    要求點擊修改按鈕之后部分輸入框由禁用狀態(tài)變?yōu)榭捎脿顟B(tài)。下面我給大家分享一段實例代碼基于vue組件中點擊按鈕后修改輸入框的狀態(tài),需要的的朋友參考下
    2017-04-04
  • vue element-ui el-table組件自定義合計(summary-method)的坑

    vue element-ui el-table組件自定義合計(summary-method)的坑

    這篇文章主要介紹了vue element-ui el-table組件自定義合計(summary-method)的坑及解決,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2023-02-02
  • Vue基于el-breadcrumb實現面包屑功能(操作代碼)

    Vue基于el-breadcrumb實現面包屑功能(操作代碼)

    這篇文章主要介紹了Vue基于el-breadcrumb實現面包屑功能,本文通過實例代碼給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2023-09-09
  • vue-router中關于children的使用方法

    vue-router中關于children的使用方法

    這篇文章主要介紹了vue-router中關于children的使用方法,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2022-08-08
  • vue實現購物車全部功能的簡單方法

    vue實現購物車全部功能的簡單方法

    vue是前端輕量級MVVM框架,入門門檻相對較低,今天用Vue做一個購物車實例,所以下面這篇文章主要給大家介紹了關于vue實現購物車全部功能的簡單方法,需要的朋友可以參考下
    2021-07-07
  • vue中使用protobuf的過程記錄

    vue中使用protobuf的過程記錄

    由于目前公司采用了ProtoBuf做前后端數據交互,進公司以來一直用的是公司大神寫好的基礎庫,完全不了解底層是如何解析的。下面小編給大家分享vue中使用protobuf的過程記錄,需要的朋友參考下吧
    2018-10-10
  • 修改Vue打包后的默認文件名操作

    修改Vue打包后的默認文件名操作

    這篇文章主要介紹了修改Vue打包后的默認文件名操作,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2020-08-08
  • vue中@路徑無法跳轉到指定文件的解決

    vue中@路徑無法跳轉到指定文件的解決

    這篇文章主要介紹了vue中@路徑無法跳轉到指定文件的解決方案,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2023-10-10
  • 前端vue3打印功能實現(多頁打印、不使用插件)

    前端vue3打印功能實現(多頁打印、不使用插件)

    在Vue項目中實現打印功能是前端開發(fā)中常見需求之一,這篇文章主要介紹了前端vue3打印功能實現的全部過程,文中介紹的方法實現了多頁打印并且不使用插件,需要的朋友可以參考下
    2024-09-09
  • vue tab切換,解決echartst圖表寬度只有100px的問題

    vue tab切換,解決echartst圖表寬度只有100px的問題

    這篇文章主要介紹了vue tab切換,解決echartst圖表寬度只有100px的問題,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2020-07-07

最新評論