最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

vue2中組件互相調(diào)用實例methods中的方法實現(xiàn)詳解

 更新時間:2022年08月21日 16:45:27   作者:豪Sir  
vue在同一個組件內(nèi),方法之間經(jīng)常需要互相調(diào)用,下面這篇文章主要給大家介紹了關于vue2中組件互相調(diào)用實例methods中的方法實現(xiàn)的相關資料,文中通過示例代碼介紹的非常詳細,需要的朋友可以參考下

前言:

大家都知道在vue2中相互調(diào)用組件的方法是很麻煩的一件事。比如說要調(diào)用子孫組件的的方法,需要refs,refs一層一層往下面找;調(diào)用上層組件的可以在組件上掛載,一層可以用v-on 和 emit解決,多層可以用provide和inject,要是多個兄弟組件間呢? 唯一方便點的就是eventBus,bus每次on事件后,都要記得在beforeDestory里面on事件后,都要記得在beforeDestory里面on事件后,都要記得在beforeDestory里面off事件,不然會多次on事件,在on事件,在on事件,在emit觸發(fā)的時候就會執(zhí)行多次,導致bug,另外在項目里面bus使用的多了,$emit時具體調(diào)用的是在調(diào)用的哪一個,在出現(xiàn)重名事件時就會讓人非常頭疼了,于是我就試著自己實現(xiàn)解決這個問題。

開始前:

我打算用全局mixin來做這個功能。本來打算在每個組件里面定義name來綁定methods的,考慮到這樣做每個vue組件里面都要自己手動定義name,而且也容易存在重名的情況,于是我就打算用vue組件所在的路徑來做,我發(fā)現(xiàn)vue組件實例上$options的prototype下有個__file屬性記錄了當前文件的路徑,當時生產(chǎn)環(huán)境下就沒有了,于是我想到了寫個weboack插件來實現(xiàn),另外吐槽下webpack的鉤子真的多,示例不清晰。vue2項目大多數(shù)都是使用的js,代碼提示用jsconfig.json結(jié)合types, js代碼里面用注釋jsdoc語法添加代碼提示。

使用

直接在組件里面調(diào)用globalDispatch方法,有代碼提示的哦,考慮到一個組件可能同時調(diào)用了多次,所有可以多傳一個eKey 進行精確emit。在組件上可以進行eKey綁定(也可以寫e-key)。

第一步、定義全局mixin

import Vue from "vue";

const DEFAULT_E_KEY = "__default";
/**
 * 方法合集
 * @type {Record<string, {eKey: string; handler: function}[]>}
 */
const events = {};

/**
 * 全局調(diào)用event的mixin
 * @type {Vue & import("vue").ComponentOptions}
 */
const globalDispatch = {
  created() {
    const attrs = this.$attrs;
    const eKey = attrs.eKey ?? attrs["e-key"];
    const filePath = this.$options.__file ?? this.$options.__filePath;
    filePath && addEvents(filePath, this, eKey);
  },
  destroyed() {
    const filePath = this.$options.__file ?? this.$options.__filePath;
    filePath && removeEvents(filePath, this);
  }
};

/**
 * 監(jiān)聽方法
 * @param {string} filePath 獲取到的路徑
 * @param {Vue} vm vue組件實例
 * @param {string=} eKey event key
 */
function addEvents(filePath, vm, eKey = DEFAULT_E_KEY) {
  const methods = vm.$options.methods;
  if (methods) {
    Object.entries(methods).forEach(([key, handler]) => {
      handler = handler.bind(vm);
      handler.vm = vm;
      const eventKey = `${filePath}:${key}`;
      const event = { eKey, handler };

      if (events[eventKey] && events[eventKey].length) {
        events[eventKey].push(event);
      } else {
        events[eventKey] = [event];
      }
    });
  }
}

/**
 * 移除方法
 * @param {string} filePath 獲取到的路徑
 * @param {Vue} vm vue組件實例
 */
function removeEvents(filePath, vm) {
  Object.keys(events).forEach(key => {
    if (key.startsWith(filePath)) {
      events[key] = events[key].filter(v => v.handler.vm !== vm);
    }
  });
}

/**
 *
 * @param {import("../../types/event-keys").EventKeys | import("../../types/shims-vue").EventParams} params
 * @param  {...any} args
 * @returns
 */
Vue.prototype.globalDispatch = function dispatch(params, ...args) {
  let eventKey,
    eKey = DEFAULT_E_KEY;
  if (typeof params === "string") {
    eventKey = params;
  } else if (typeof params === "object") {
    eventKey = params.target;
    eKey = params.eKey ?? DEFAULT_E_KEY;
  }

  const eKeyMsg = eKey !== DEFAULT_E_KEY ? `eKey:${eKey},` : "";

  if (
    !eventKey ||
    typeof eventKey !== "string" ||
    !/^[^:]*:[^:](.*){1}$/.test(eventKey)
  ) {
    throw new Error(`${eKeyMsg}eventKey:${eventKey}, 參數(shù)不正確!`);
  }

  const handlers = events[eventKey]?.filter(v => v.eKey === eKey);
  if (handlers && handlers.length) {
    const results = handlers.map(v => v.handler(...args));
    if (results.length === 1) return results[0];
    return results.map(result => ({ eKey, result }));
  }

  const method = eventKey.split(":")[1];
  throw new Error(`${eKeyMsg}method:${method},該方法未找到!`);
};

export default globalDispatch;

這個文件主要添加所有的組件的methods到events里面,在Vue.prototype上掛載globalDispatch 方法,方便在vue組件上使用。

第二步添加代碼提示d.ts聲明文件

在項目下新建jsconfig.json

我用的時vue2.7版本寫的,主要時include把types文件夾的文件加進來

{
  "compilerOptions": {
    "moduleResolution": "node",
    "target": "esnext",
    "baseUrl": ".",
    "allowJs": true,
    "sourceMap": false,
    "strict": true,
    "jsx": "preserve",
    "module": "ESNext",
    "paths": {
      "@/*": ["./src/*"]
    },
    "lib": ["DOM", "ESNext"]
  },
  "vueCompilerOptions": {
    "target": 2.7
  },
  "exclude": ["node_modules", "dist"],
  "include": ["src/**/*.js", "src/**/*.vue", "types/**/*.ts", "types/**/*.d.ts"]
}

添加shims-vue.d.ts聲明文件

在types文件夾下新建shims-vue.d.ts, 因為globalDispatch需要支持兩種傳參形式,所以使用重載

import Vue from "vue";
import { EventKeys } from "./event-keys";

export type EventParams = { target: EventKeys; eKey: string };

function globalDispatch(eventKey: EventKeys, ...args: any[]): any;
function globalDispatch(eventParams: EventParams, ...args: any[]): any;

declare module "vue/types/vue" {
  interface Vue {
    /**
     * 全局互相調(diào)用event的dispatch
     */
    globalDispatch: typeof globalDispatch;
  }
}

添加event-keys.d.ts聲明文件

在types文件夾下新建event-keys.d.ts, 這個文件是用來給globalDispatch的第一個參數(shù)做代碼提示的,手動寫可以,寫個webpack插件自動讀取vue文件的路徑和方法自動生成更好,下面會貼出來。

export type EventKeys = "src/App.vue:onClick" | "src/views/IndexView.vue:test";

第三步編寫webpack插件

在項目根目錄下新建plugins文件夾

新建global-dispatch.js 自動生成event-keys.d.ts

開發(fā)者模式下才需要生成event-keys.d.ts,先遞歸找出所有的vue文件的路徑,然后讀取文件,用acorn庫解析,找出文件的methods里的所有方法名,用prettier格式化后寫入到event-keys.d.ts,在項目啟動和文件變化后都會執(zhí)行,在添加methos里新方法或刪除后,會執(zhí)行寫入。

const fs = require("fs");
const path = require("path");
const acorn = require("acorn");
const prettier = require("prettier");
const prettierConfig = require("../prettier.config");

/**
 * @typedef {import("webpack/lib/Compiler")} Compiler
 */

const PLUGIN_NAME = "global-dispatch";
const KEYS_PATH = path.resolve(__dirname, "../types/event-keys.d.ts");

class TransformFilePathPlugin {
  /**
   * @param {Compiler} compiler
   * @returns {void}
   */
  apply(compiler) {
    compiler.hooks.done.tap(PLUGIN_NAME, () => {
      process.env.NODE_ENV === "development" && writeEventKeys();
    });
  }
}

function writeEventKeys() {
  const vueFilePaths = getFilePath();
  writeVueKeyPaths(vueFilePaths);
}

/**
 * 緩存內(nèi)容,防止重復寫入
 */
let keysContentCache = fs.readFileSync(KEYS_PATH, "utf-8");

/**
 * 寫入__filePath到type Key文件
 * @param {string[]} paths 路徑集合
 */
function writeVueKeyPaths(paths) {
  let keysContent = "export type EventKeys =";
  const keys = [];

  paths.forEach(p => {
    let content = fs.readFileSync(getSrcPath(p), "utf-8");
    const scriptMatch = content.match(/<script/g);
    if (!scriptMatch) return;

    const startIndex = content.indexOf("export default");
    if (startIndex < 0) return;

    const endIndex = content.indexOf("</script>", startIndex);
    content = content.substring(startIndex, endIndex);

    const ast = acorn.parse(content, { sourceType: "module" });
    const defaultExportAst = ast.body.find(
      v => v.type === "ExportDefaultDeclaration"
    );

    let properties;
    if (defaultExportAst.declaration.type === "CallExpression") {
      properties = defaultExportAst.declaration.arguments[0].properties;
    }
    if (
      defaultExportAst.declaration.type === "ObjectExpression" &&
      Array.isArray(defaultExportAst.declaration.properties)
    ) {
      properties = defaultExportAst.declaration.properties;
    }

    const methods = properties.find(v => v.key.name === "methods");
    if (!methods) return;

    if (methods.value.properties.length) {
      const methodNames = methods.value.properties.map(
        v => `${p}:${v.key.name}`
      );
      keys.push(...methodNames);
    }
  });

  keysContent += keys.map(v => `'${v}'`).join("|") || "string";

  keysContent = prettier.format(keysContent, {
    ...prettierConfig,
    parser: "typescript"
  });

  if (keysContentCache !== keysContent) {
    keysContentCache = keysContent;
    fs.writeFileSync(KEYS_PATH, keysContent);
  }
}

/**
 *
 * @param {string=} p 路徑
 * @returns {string[]} 路徑集合
 */
function getFilePath(p = "src") {
  const paths = fs.readdirSync(getSrcPath(p), "utf-8");
  const vueFiles = getVueFiles(paths, p);
  const dirs = getDirs(paths, p);

  if (dirs.length) {
    dirs.forEach(dir => {
      vueFiles.push(...getFilePath(dir));
    });
  }
  return vueFiles;
}

function getDirs(paths, path) {
  return paths
    .map(v => `${path}/${v}`)
    .filter(v => fs.statSync(v).isDirectory());
}

function getVueFiles(paths, path) {
  return paths.filter(v => v.endsWith(".vue")).map(v => `${path}/${v}`);
}

function getSrcPath(p) {
  return path.resolve(__dirname, "../" + p);
}

module.exports = { TransformFilePathPlugin };

添加vue-path-loader.js webpack loader文件

這個文件是用來在vue實例上添加__filePath屬性的,本來是想寫在上面的插件一起的,無奈沒有在webpack文檔等地方找到在plugins里添加loader的方法,在vue-loader源碼里也沒有好的體現(xiàn)。 在開發(fā)者環(huán)境下vue的$options下有__file可以用,所以只需要生產(chǎn)環(huán)境啟用

module.exports = function(content) {
  if (process.env.NODE_ENV === "development") return content;

  const filePath = this.resourcePath
    .replace(/\\/g, "/")
    .replace(/(.*)?src/, "src");

  const reg = /export default.*?{/;
  content = content.replace(reg, $0 => `${$0} __filePath: "${filePath}",`);
  return content;
};

配置vue.config.js

添加configureWebpack里的即可

const path = require("path");
const { TransformFilePathPlugin } = require("./plugins/global-dispatch");
/**
 * @type {import('@vue/cli-service').ProjectOptions}
 */
module.exports = {
  lintOnSave: false,
  productionSourceMap: false,
  configureWebpack: {
    plugins: [new TransformFilePathPlugin()],
    module: {
      rules: [
        {
          test: /\.vue$/,
          use: [
            {
              loader: path.resolve(__dirname, "./plugins/vue-path-loader.js")
            }
          ]
        }
      ]
    }
  }
};

后記

  • 后續(xù)有時間可以弄成npm包,npm install 使用
  • 找下weboack loader 寫進webpack plugin 的方法
  • 目前還沒找不破壞vue源碼的情況下自定組件自定義props的類型,讓在組件上 有eKey的單詞提示,在vue/type/jsx.d.ts的ReservedProps下添加eKey?: string;才能實現(xiàn)功能。

倉庫地址

ywenhao/vue2-global-dispatch (github.com)

總結(jié)

到此這篇關于vue2中組件互相調(diào)用實例methods中的方法實現(xiàn)的文章就介紹到這了,更多相關vue2組件互相調(diào)用methods中方法內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!

相關文章

  • vuex提交state&&實時監(jiān)聽state數(shù)據(jù)的改變方法

    vuex提交state&&實時監(jiān)聽state數(shù)據(jù)的改變方法

    今天小編就為大家分享一篇vuex提交state&&實時監(jiān)聽state數(shù)據(jù)的改變方法,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2018-09-09
  • vue使用國密SM4進行加密、解密的過程

    vue使用國密SM4進行加密、解密的過程

    國密SM4算法是一種對稱加密算法,適用于對稱密鑰加密和解密的場景,這篇文章主要介紹了vue使用國密SM4進行加密、解密,需要的朋友可以參考下
    2023-07-07
  • vuepress打包部署踩坑及解決

    vuepress打包部署踩坑及解決

    這篇文章主要介紹了vuepress打包部署踩坑及解決方案,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2022-09-09
  • 淺談Vue下使用百度地圖的簡易方法

    淺談Vue下使用百度地圖的簡易方法

    本篇文章主要介紹了淺談Vue下使用百度地圖的簡易方法,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2018-03-03
  • vue中i18n的安裝與幾種使用方式詳解

    vue中i18n的安裝與幾種使用方式詳解

    在一些網(wǎng)站經(jīng)常可以看到切換語言包,將網(wǎng)站轉(zhuǎn)化成多種語言版本的情況,下面這篇文章主要給大家介紹了關于vue中i18n的安裝與幾種使用方式的相關資料,需要的朋友可以參考下
    2022-09-09
  • vue2組件進階與插槽詳解(推薦!)

    vue2組件進階與插槽詳解(推薦!)

    插槽(slot)作用是讓父組件可以往子組件指定位置插入?html?結(jié)構,也是組件的一種通信方式,下面這篇文章主要給大家介紹了關于vue2組件進階與插槽的相關資料,需要的朋友可以參考下
    2023-02-02
  • vue自定義組件實現(xiàn)雙向綁定

    vue自定義組件實現(xiàn)雙向綁定

    這篇文章主要為大家詳細介紹了vue自定義組件實現(xiàn)雙向綁定,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2021-01-01
  • vue日期時間工具類詳解

    vue日期時間工具類詳解

    這篇文章主要為大家詳細介紹了vue日期時間工具類,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2022-06-06
  • vue強制刷新組件的三種方法

    vue強制刷新組件的三種方法

    在Vue中,強制刷新組件通常涉及到以下幾種方法,本文給大家列出了常見的三種vue強制刷新組件的方法,感興趣的朋友跟隨小編一起看看吧
    2024-04-04
  • 前端Vue中常用rules校驗規(guī)則詳解

    前端Vue中常用rules校驗規(guī)則詳解

    這篇文章主要介紹了前端Vue中常用rules校驗規(guī)則詳解,文章圍繞主題展開詳細的內(nèi)容介紹,具有一定的參考價值,需要的小伙伴可以參考一下
    2022-07-07

最新評論

仙桃市| 郸城县| 阿瓦提县| 五指山市| 盐边县| 竹山县| 黄平县| 株洲市| 张掖市| 郓城县| 中宁县| 共和县| 静乐县| 河间市| 南昌市| 子洲县| 邹平县| 紫阳县| 荣昌县| 申扎县| 昌平区| 历史| 淮滨县| 新余市| 垫江县| 奉新县| 广州市| 江源县| 南投市| 莎车县| 石棉县| 新化县| 天祝| 淳化县| 休宁县| 紫云| 岳阳市| 高碑店市| 黎川县| 若羌县| 特克斯县|