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

簡易vuex4核心原理及實現(xiàn)源碼分析

 更新時間:2023年01月12日 11:06:40   作者:騶虞  
這篇文章主要為大家介紹了簡易vuex4核心原理及實現(xiàn)源碼分析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪

前言

Vuex 是一個專為 Vue.js 應用程序開發(fā)的 狀態(tài)管理模式 。它借鑒了Flux、redux的基本思想,將共享的數(shù)據(jù)抽離到全局,同時利用Vue.js的 響應式 機制來進行高效的狀態(tài)管理與更新。想要掌握了解基礎知識可以查閱Vuex官網(wǎng),本篇主要是對 vuex4.x版本的源碼 進行研究分析。

Vuex 核心原理

使用方式

創(chuàng)建 store

import { createStore } from "@/vuex";
const store = createStore({
  state: {
      count: 0,
  },
  getters: {
      double: (state) => {
      return state.count * 2;
      },
  },
  mutations: {
      add(state, payload) {
      state.count += payload;
      },
  },
  actions: {
      asyncAdd({ commit }, payload) {
      return new Promise((resolve, reject) => {
          setTimeout(() => {
          commit("add", payload);
          resolve();
          }, 1000);
      });
      },
  },
});
export default store;

引入 store

import store from "./store";
// 傳入key值,標識 store
createApp(App).use(store, "my").mount("#app");

使用 store

<template>
    <div>
        count:{{ count }}
        <hr />
        getter:{{ double }}
        <hr />
        <button @click="$store.state.count++">直接修改state</button>
        <button @click="add">同步修改</button>
        <button @click="asyncAdd">異步修改</button>
    </div>
</template>
<script>
import { computed } from "vue";
import { useStore } from "@/vuex";
export default {
    name: "App",
    setup() {
        // 傳入 key 使用特定的 store
        const store = useStore("my");
        function add() {
            store.commit("add", 1);
        }
        function asyncAdd() {
            store.dispatch("asyncAdd", 1).then(() => {
                console.log("ok");
            });
        }
        return {
            count: computed(() => store.state.count),
            double: computed(() => store.getters.double),
            add,
            asyncAdd,
        };
    },
};
</script>

vuex 運行流程

Vuex 的運作流程如下圖所示:

核心原理

  • vuex4 是一個插件,所以創(chuàng)建的 store 實例需要實現(xiàn)一個 install 方法
  • vuex4 需要導出 createStore,用于創(chuàng)建 store ,接收一個 options 對象,
  • vuex4 需要導出 useStore ,用于在組件中使用 store
  • store 是一個全局狀態(tài)庫,并且是響應式的,可以在各個組件中使用 store 中的狀態(tài)
  • 可以創(chuàng)建多個 store 實例,通過 key 標識來區(qū)分不同的 store

實現(xiàn)一個簡易版的 vuex

首先不考慮 modules、插件、嚴格模式、動態(tài)模塊等功能,實現(xiàn)一個簡易版的vuex; 該版本包含的功能有:

  • store 的派發(fā)和注冊
  • state 的響應式
  • getters、mutationsactions、commit、dispatch
  • 通過 key 標識多個 store

實現(xiàn) store 的派發(fā)和注冊、響應式、injectKey

  • 通過 provide/inject 實現(xiàn) store 的派發(fā)和注冊
  • 通過 reactive 實現(xiàn) state 的響應式
  • 通過在 provide/inject 時傳入 injectKey ,來標識不同的 store
import { inject, reactive } from "vue";
const storeKey = "store";
class Store {
    constructor(options) {
        const store = this;
        // state 響應式
        // 做狀態(tài)持久化時需要整體替換state,為了保持state的響應式,用data進行包裹
        store._state = reactive({ data: options.state });
    }
    // 代理 store._state.data 到 store.state 上
    get state() {
        return this._state.data;
    }
    install(app, injectKey) {
        // 全局暴露一個變量,暴露的是store實例
        app.provide(injectKey || storeKey, this); // this 指向 store 實例
        // 設置全局變量 $store
        app.config.globalProperties.$store = this;
    }
}
export function createStore(options) {
  return new Store(options);
}
export function useStore(injectKey = storeKey) {
  return inject(injectKey);
}

實現(xiàn) getters、mutations、actions、commit、dispatch

  • getters 的實現(xiàn):將 options.getters 代理到 store.getters,并傳入?yún)?shù) store.state;在vue3.2以上版本,可以使用 computed 實現(xiàn) getters 的緩存。
  • mutations 的實現(xiàn):將 options.mutations 代理到 store._mutations 上,將 mutation 內部的 this 指向 store,并傳入?yún)?shù) store.statepayload ;actions 的實現(xiàn)類似。
  • commitdispatch 的實現(xiàn):它們是一個函數(shù),通過傳入的 typepayload 匹配并執(zhí)行對應的 mutationaction
// 遍歷 obj,對每一項執(zhí)行 fn(obj[key], key)
export function forEachValue(obj, fn) {
  Object.keys(obj).forEach((key) => fn(obj[key], key));
}
class Store {
  constructor(options) {
    const store = this;
    store._state = reactive({ data: options.state });
    /**
     * 實現(xiàn)getters
     */
    const _getters = options.getters; // {getter1: fn1, getter2: fn2}
    store.getters = {};
    forEachValue(_getters, function (fn, key) {
      Object.defineProperty(store.getters, key, {
        get: computed(() => fn(store.state)), // 用 computed 對 getters 進行緩存
      });
    });
    /**
     * 實現(xiàn) mutation 和 actions
     */
    store._mutations = Object.create(null);
    store._actions = Object.create(null);
    const _mutations = options.mutations;
    const _actions = options.actions;
    forEachValue(_mutations, (mutation, key) => {
      store._mutations[key] = (payload) => {
        mutation.call(store, store.state, payload);
      };
    });
    forEachValue(_actions, (action, key) => {
      store._actions[key] = (payload) => {
        action.call(store, store, payload);
      };
    });
  }
  /**
   * 實現(xiàn) commit 和 dispatch
   * commit、dispatch必須寫成箭頭函數(shù),來保證commit、dispatch里面的this指向store實例
   */
  commit = (type, payload) => {
    this._mutations[type](payload);
  };
  dispatch = (type, payload) => {
    this._actions[type](payload);
  };
  get state() {
    return this._state.data;
  }
  install(app, injectKey) {
    app.provide(injectKey || storeKey, this);
    app.config.globalProperties.$store = this;
  }
}

源碼解析

當項目變得復雜,我們就不得不使用 modules 讓項目結構更清晰,更具可維護性;同時引入嚴格模式、插件系統(tǒng)、動態(tài)modules等功能。

ModuleCollection

modules 包含 rootModule 以及 options.modules 中的各個子模塊,我們 期望將用戶傳入的所有 module 轉化成以下樹狀結構,并存放到 store._modules 變量中 :

root = {
    _raw: rootModule,
    state: rootModule.state,
    _children: {
        aCount: {
            _raw: aModule,
            state: aModule.state,
            _children: {
                cCount: {
                    _raw:cModule,
                    state: cModule.state,
                    _children:{}
                }
            },
        },
        bCount: {
            _raw: bModule,
            state: bModule.state,
            _children: {},
        },
    },
};

實現(xiàn)方式:

// vuex/store.js
import { storeKey } from "./injectKey";
import ModuleCollection from "./module/module-collection";
export default class Store {
  constructor(options) {
    const store = this;
    // 1. modules 數(shù)據(jù)格式化
    store._modules = new ModuleCollection(options);
  }
  install(app, injectKey) {
    app.provide(injectKey || storeKey, this);
    app.config.globalProperties.$store = this;
  }
}
// module/module-collection.js
import Module from "./module";
import { forEachValue } from "../utils";
export default class ModuleCollection {
  constructor(rootModule) {
    this.root = null;
    this.register(rootModule, []);
  }
  register(rawModule, path) {
    const newModule = new Module(rawModule);
    // 1. 如果是根模塊
    if (path.length === 0) {
      this.root = newModule;
    } else {
      // 2. 如果不是根模塊,則設置父模塊的 _children 屬性
      const parent = path.slice(0, -1).reduce((module, current) => {
        return module.getChild(current);
      }, this.root);
      // key 為 path 的最后一位
      parent.addChild(path[path.length - 1], newModule);
    }
    // 遞歸處理 modules
    if (rawModule.modules) {
      forEachValue(rawModule.modules, (rawChildModule, key) => {
        this.register(rawChildModule, path.concat(key));
      });
    }
  }
}
// module/module.js
import { forEachValue } from "../utils";
export default class Module {
  constructor(rawModule) {
    this._raw = rawModule;
    this.state = rawModule.state;
    this._children = {};
  }
  addChild(key, module) {
    this._children[key] = module;
  }
  getChild(key) {
    return this._children[key];
  }
  forEachChild(fn) {
    forEachValue(this._children, fn);
  }
}

installModule

另外,當我們取子 module 中的 state 時,采用的方式是:store.state.moduleA.count,是直接從store.state 上鏈式獲取的。我們 期望在 store._state 上包含所有 modules 中的數(shù)據(jù),其結構如下 :

{
    count: 0,
    moduleA: {
        count: 0
        moduleC: {
            count: 0
        }
    },
    moduleB: {
        count: 0
    }
}

所以我們首先需要將 store._modules.root.state 插入各個模塊的 state 之后,改造成上述結構:

// vuex/store.js
function installModule(store, rootState, path, module) {
  let isRoot = !path.length;
  if (!isRoot) {
    let parentState = path
      .slice(0, -1)
      .reduce((state, key) => state[key], rootState);
    parentState[path[path.length - 1]] = module.state;
  }
  // 【遍歷】 module._children,【遞歸】執(zhí)行 installModule
  module.forEachChild((child, key) => {
    installModule(store, rootState, path.concat(key), child);
  });
}
export default class Store {
  constructor(options) {
    const store = this;
    store._modules = new ModuleCollection(options);
    // 2. 改造 store._modules.root.state
    const state = store._modules.root.state; // 根狀態(tài)
    installModule(store, state, [], store._modules.root);
  }
}

resetStoreState

創(chuàng)建 store._wrappedGetters、store._mutations、store._actions 用來存儲所有模塊的 getters、mutations、actions,期望的格式如下:

store: {
    // actions 和 mutations 都是數(shù)組格式
    _actions: {
        'moduleB/asyncAdd': [ ? ]
    },
    _mutations: {
        'moduleA/add': [ ? ]
        'moduleA/moduleC/add': [ ? ]
        'add': [ ? ]
        'moduleB/add': [ ? ]
    }
    _wrappedGetters: {
        'moduleB/plus': () =&gt; (...)
        'double': () =&gt; (...)
    }
}

具體實現(xiàn):

// vuex/store.js
// 根據(jù)路徑,獲取store上面的最新狀態(tài)(因為store.state是響應式的,通過store.state.xx.xx獲取的也是響應式的)
function getNestedState(state, path) {
  return path.reduce((state, key) => state[key], state);
}
function isPromise(val) {
  return val && typeof val.then === "function";
}
function installModule(store, rootState, path, module) {
  // 略...
  // getters  module._raw.getters
  module.forEachGetter((getter, key) => {
    store._wrappedGetters[key] = () => {
      return getter(getNestedState(store.state, path)); //  getter(module.state) 不可行,因為如果直接使用模塊自己的狀態(tài),此狀態(tài)不是響應式的
    };
  });
  // mutation:{add: [mutation1,mutation2], double: [mutation3]} 不同modules中的同名mutation放到同一個數(shù)組中
  module.forEachMutation((mutation, key) => {
    const entry = store._mutations[key] || (store._mutations[key] = []);
    entry.push((payload) => {
      // 也通過 getNestedState(store.state, path) 獲取module的最新狀態(tài)
      mutation.call(store, getNestedState(store.state, path), payload);
    });
  });
  // action:【action執(zhí)行完返回一個Promise】
  module.forEachAction((action, key) => {
    const entry = store._actions[key] || (store._actions[key] = []);
    entry.push((payload) => {
      let res = action.call(store, store, payload);
      if (!isPromise(res)) {
        return Promise.resolve(res);
      }
      return res;
    });
  });
  // 【遍歷】 module._children,【遞歸】執(zhí)行各個module 的 installModule
  module.forEachChild((child, key) => {
    installModule(store, rootState, path.concat(key), child);
  });
}
export default class Store {
  constructor(options) {
    const store = this;
    // 在store上定義變量,用來存儲getters、mutations、actions
    store._wrappedGetters = Object.create(null);
    store._mutations = Object.create(null);
    store._actions = Object.create(null);
  }
}
// module/module.js
import { forEachValue } from "../utils";
export default class Module {
    // ...略
  forEachGetter(fn) {
    if (this._raw.getters) {
      forEachValue(this._raw.getters, fn);
    }
  }
  forEachMutation(fn) {
    if (this._raw.mutations) {
      forEachValue(this._raw.mutations, fn);
    }
  }
  forEachAction(fn) {
    if (this._raw.actions) {
      forEachValue(this._raw.actions, fn);
    }
  }
}

然后執(zhí)行 resetStoreState ,實現(xiàn)數(shù)據(jù)響應式,并創(chuàng)建getters

// vuex/store.js
function resetStoreState(store, state) {
  // 由于state在狀態(tài)持久化的時候可能會整體替換,為了維持響應式,給state包一層data屬性
  store._state = reactive({ data: state });
  store.getters = {};
  forEachValue(store._wrappedGetters, (getter, key) => {
    Object.defineProperty(store.getters, key, {
      enumerable: true,
      get: () => getter(), // 在vue3.2版本后,可以用 computed 對 getter 值進行緩存
    });
  });
}
export default class Store {
  constructor(options) {
    const store = this;
    // 在store上定義變量,用來存儲getters、mutations、actions
    store._wrappedGetters = Object.create(null);
    store._mutations = Object.create(null);
    store._actions = Object.create(null);
    store._modules = new ModuleCollection(options);
    const state = store._modules.root.state;
    installModule(store, state, [], store._modules.root);
    // state數(shù)據(jù)響應式、創(chuàng)建store.getters
    resetStoreState(store, state);
  }
  get state() {
    return this._state.data;
  }
}

實現(xiàn) commitdispatch

export default class Store {
    // ...略
  commit = (type, payload) => {
    const entry = this._mutations[type] || [];
    entry.forEach((handler) => handler(payload));
  };
  dispatch = (type, payload) => {
    const entry = this._actions[type] || [];
    // action 返回的是一個 Promise
    return Promise.all(entry.map((handler) => handler(payload)));
  };
}

namespaced

在沒有設置命名空間的情況下,模塊內部的 action、 mutationgetters 是注冊在全局命名空間的,這樣可能會導致多個模塊對同一個 actionmutation 作出響應。啟用命名空間會讓模塊內部的狀態(tài)擁有私有局部空間,不受其他模塊影響。 首先修改 Module 類,增加一個 namespaced 屬性:

// vuex/module/module.js
export default class Module {
  constructor(rawModule) {
    this._raw = rawModule;
    this.state = rawModule.state;
    this._children = {};
    this.namespaced = rawModule.namespaced;
  }
}

然后創(chuàng)建 store._modules 實例的 getNamespaced 方法,用來獲取 namespaced 路徑,形如 moduleA/moduleC/

// vuex/module/module-collection.js
export default class ModuleCollection {
    // ...略
    // 獲取 namespaced 的路徑,形如 moduleA/moduleC/
    getNamespaced(path) {
        let module = this.root;
        return path.reduce((namespacedStr, key) => {
            module = module.getChild(key);
            return namespacedStr + (module.namespaced ? key + "/" : "");
        }, "");
    }
}

最后修改 store._mutations、store._actions、store.__wrappedGetters 中子模塊相關的路徑:

// vuex/store.js
function installModule(store, rootState, path, module) {
  // 略...
  const namespaced = store._modules.getNamespaced(path);
  // getters
  module.forEachGetter((getter, key) => {
    store._wrappedGetters[namespaced + key] = () => {
      return getter(getNestedState(store.state, path));
    };
  });
  // mutation
  module.forEachMutation((mutation, key) => {
    const entry = store._mutations[namespaced + key] || (store._mutations[namespaced + key] = []);
    entry.push((payload) => {
      mutation.call(store, getNestedState(store.state, path), payload);
    });
  });
  // action
  module.forEachAction((action, key) => {
    const entry = store._actions[namespaced + key] || (store._actions[namespaced + key] = []);
    entry.push((payload) => {
      let res = action.call(store, store, payload);
      if (!isPromise(res)) {
        return Promise.resolve(res);
      }
      return res;
    });
  });
  // ...略
}

嚴格模式

用戶在 options 中通過 strict: true 開啟嚴格模式;

  • 在嚴格模式中,mutation 只能執(zhí)行同步操作
  • 修改 store 的狀態(tài)只能在 mutation 中進行

實現(xiàn)嚴格模式的原理:

  • 設置一個初始狀態(tài) _commiting 為 false;當執(zhí)行fn回調時,將 _commiting 設為 true,最后將 _commiting 設為 false;如果 fn 是同步的,那么在 fn 中獲取到的 _commiting 就為 true,否則 在 fn 中獲取到的 _commitingfalse;
  • 如果沒有通過 mutation 修改數(shù)據(jù),那么 _commiting 依然為初始值 false;

具體實現(xiàn):

// vuex/store.js
import { watch } from "vue";
function resetStoreState(store, state) {
  // ...略
  if (store.strict) {
    enableStricMode(store);
  }
}
function enableStricMode(store) {
  // 監(jiān)控數(shù)據(jù)變化
  // 1. 如果是mutation同步修改數(shù)據(jù),則 store._commiting 為 true,不會報錯
  // 2. 如果是mutation異步修改數(shù)據(jù)、或通過其它方式修改數(shù)據(jù),則store._commiting 為 false,會報錯
  watch(
    () => store._state.data,
    () => {
      // 當?shù)谝粋€參數(shù)是false是,會打印出警告
      console.assert(
        store._commiting,
        "do not mutate vuex store state outside mutation handlers"
      );
    },
    { deep: true, flush: "sync" } // watch 默認是異步的,這里改成同步(狀態(tài)改變立刻執(zhí)行回調)監(jiān)聽
  );
}
export default class Store {
    // 先把 this._commiting 改為 true,執(zhí)行fn后,再將 this._commiting 改回去;如果fn是同步的,則在fn中this._commiting為true。
  _withCommit(fn) {
    const commiting = this._commiting;
    this._commiting = true;
    fn();
    this._commiting = commiting;
  }
  constructor(options) {
    // ...略
    this.strict = options.strict || false;
    this._commiting = false;
  }
  commit = (type, payload) => {
    const entry = this._mutations[type] || [];
    this._withCommit(() => {
      entry.forEach((handler) => handler(payload));
    });
  };
}

插件系統(tǒng)

手寫一個狀態(tài)持久化插件:

// vuex插件就是一個函數(shù)
// 實現(xiàn)一個數(shù)據(jù)持久化插件
function persistedStatePlugin(store) {
  // 從緩存中讀取數(shù)據(jù),并替換store中的state
  let local = localStorage.getItem("VUEX:STATE");
  if (local) {
    store.replaceState(JSON.parse(local));
  }
  // 每當狀態(tài)變化(執(zhí)行了mutation),就會執(zhí)行subscribe的回調
  store.subscribe((mutation, state) => {
    // 緩存狀態(tài)
    localStorage.setItem("VUEX:STATE", JSON.stringify(state));
  });
}
export default createStore({
    plugins: [persistedStatePlugin],
})

該插件有幾個重點:

  • vuex插件本質上是一個函數(shù),接收一個參數(shù) store
  • store.replaceState() 方法會替換掉 state
  • 每當通過 mutation 修改了狀態(tài),都會執(zhí)行 store.subscribe(fn) 里的回調函數(shù)(發(fā)布訂閱模式)

具體實現(xiàn):

// vuex/store.js
export default class Store {
    constructor(options) {
        // ...略
        // 執(zhí)行插件(本質是一個函數(shù))
        store._subscribers = [];
        options.plugins.forEach((plugin) => plugin(store));
    }
    subscribe(fn) {
        this._subscribers.push(fn);
    }
    replaceState(newState) {
        // 直接修改state會報錯,所以使用 _withCommit 包裹一下
        this._withCommit(() => {
            this._state.data = newState;
        });
    }
    commit = (type, payload) => {
        const entry = this._mutations[type] || [];
        this._withCommit(() => {
            entry.forEach((handler) => handler(payload));
        });
        // 每次 commit 的時候執(zhí)行所有的 subscribers
        this._subscribers.forEach((sub) => sub({ type, payload }, this.state));
    };
}

store.registerModule

vuex 可以使用store.registerModule 動態(tài)注冊modules,使用方式如下:

import { createStore } from "@/vuex";
const store = createStore({
    // ...略
})
// 在moduleA內部創(chuàng)建一個moduleC
store.registerModule(["moduleA", "moduleC"], {
  namespaced: true,
  state: { count: 0 },
  mutations: {
    add(state, payload) {
      state.count += payload;
    },
  },
});
export default store;

具體實現(xiàn):

  • 創(chuàng)建 store.registerModule 方法
export default class Store {
    registerModule(path, rawModule) {
        const store = this;
        if (typeof path === "string") {
            path = [path];
        }
        // 1. 在原有模塊基礎上新增加一個module
        const newModule = store._modules.register(rawModule, path);
        // 2. 再把模塊安裝上
        installModule(store, store.state, path, newModule);
        // 3. 重置容器
        resetStoreState(store, store.state);
    }
}

修改 ModuleCollectionregister 方法,返回新的 newModule

export default class ModuleCollection {
     // ...
     register(rawModule, path) {
         const newModule = new Module(rawModule);
         // ...略
         return newModule;
     }
     // ...
 }

installModule 中設置 parentStatestate 時,使用 store._withCommit() 進行包裹,否則會警告(嚴格模式下)

 function installModule(store, rootState, path, module) {
     if (!isRoot) {
     let parentState = path
         .slice(0, -1)
         .reduce((state, key) => state[key], rootState);
     store._withCommit(() => {
         parentState[path[path.length - 1]] = module.state;
     });
     module.forEachChild((child, key) => {
         installModule(store, rootState, path.concat(key), child);
     });
 }
 }

最后

本篇主要是對 vuex4.0 源碼的學習總結,源代碼倉庫可以查看 mini-vuex4。

以上就是簡易vuex4核心原理及實現(xiàn)源碼分析的詳細內容,更多關于vuex4核心原理的資料請關注腳本之家其它相關文章!

相關文章

  • 在Vue中使用icon 字體圖標的方法

    在Vue中使用icon 字體圖標的方法

    這篇文章主要介紹了在Vue中使用icon 字體圖標的方法,本文給大家介紹的非常詳細,具有一定的參考借鑒價值,需要的朋友可以參考下
    2019-06-06
  • Vue3中Element Plus Table(表格)點擊獲取對應id方式

    Vue3中Element Plus Table(表格)點擊獲取對應id方式

    這篇文章主要介紹了Vue3中Element Plus Table(表格)點擊獲取對應id方式,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2023-10-10
  • vue如何在自定義組件中使用v-model

    vue如何在自定義組件中使用v-model

    本篇文章主要介紹了vue如何在自定義組件中使用v-model,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2018-05-05
  • 解決修復報錯Error in render:TypeError:Cannot read properties of undefined(reading ‘ipconfig‘)問題

    解決修復報錯Error in render:TypeError:Cannot read&n

    這篇文章主要介紹了解決修復報錯Error in render:TypeError:Cannot read properties of undefined(reading ‘ipconfig‘)問題,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2024-03-03
  • vue組件通信的多種方法詳解

    vue組件通信的多種方法詳解

    這篇文章主要為大家介紹了vue組件通信的幾種方法,具有一定的參考價值,感興趣的小伙伴們可以參考一下,希望能夠給你帶來幫助
    2021-11-11
  • vue實現(xiàn)PC端錄音功能的實例代碼

    vue實現(xiàn)PC端錄音功能的實例代碼

    這篇文章主要介紹了vue實現(xiàn)PC端錄音功能的實例代碼,本文給大家介紹的非常詳細,具有一定的參考借鑒價值,需要的朋友可以參考下
    2019-06-06
  • 基于Axios 常用的請求方法別名(詳解)

    基于Axios 常用的請求方法別名(詳解)

    下面小編就為大家分享一篇Axios 常用的請求方法別名,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2018-03-03
  • vue實現(xiàn)選項卡功能

    vue實現(xiàn)選項卡功能

    這篇文章主要為大家詳細介紹了vue實現(xiàn)選項卡功能,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2022-03-03
  • 在vue中獲取wangeditor的html和text的操作

    在vue中獲取wangeditor的html和text的操作

    這篇文章主要介紹了在vue中獲取wangeditor的html和text的操作,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2020-10-10
  • 基于Vue的延遲加載插件vue-view-lazy

    基于Vue的延遲加載插件vue-view-lazy

    這篇文章主要介紹了基于Vue的延遲加載插件vue-view-lazy,可以使圖片或者其他資源進入可視區(qū)域后加載,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2018-05-05

最新評論