Используйте vue для инкапсуляции плагина контекстного меню.

Vue.js
Используйте vue для инкапсуляции плагина контекстного меню.

предисловие

На прошлой неделе я поделился с вами тем, как использовать пользовательскую команду Vue для реализации пользовательского контекстного меню браузера. Всем показалось, что это очень интересно. На этот раз я превратил его в плагин и загрузил в репозиторий npm.

В процессе создания этого плагина я наступил на множество ям. В этой статье я поделюсь с вами своими идеями реализации и процессом. Заинтересованные разработчики могут прочитать эту статью.

Строительство окружающей среды

Сначала я использовал команду tsc машинописного текста непосредственно для упаковки, но я использовал vue и scss в своем плагине и обнаружил, что для упаковки этих файлов мне нужно настроить веб-пакет самостоятельно.

Я помню давным-давно, когда я использовал Vue CLI 2.x для создания проекта, я мог выбрать, будет ли создаваемый проект плагином или веб-проектом.Теперь я использую Vue CLI 4.x, и я не не вижу эту опцию, когда я создал проект.

Итак, по счастливой случайности я зашел на официальный сайт Vue CLI, чтобы найти волну, и действительно нашел ее.buildЕсть командаtargetвариант, вы можете упаковать его в плагин, его конкретное использование:vue-cli-service build.

Поскольку Vue CLI предоставляет готовое решение, используйте то, что оно предоставляет.

Создать проект

  • Перейдите в каталог вашего проекта в терминале и используйтеcreateкоманда для созданияvue-right-click-menu-nextпроект
vue create vue-right-click-menu-next
  • На следующем шаге выберите пользовательскую конфигурацию, выберите vue3, node-sass, eslint+prettier, напечатайте эти параметры.

Настроить зависимости

После создания проекта мы удаляем вещи, созданные при инициализации CLI, а затем модифицируем содержимое package.json.

В package.json CLI по умолчанию помещаетvueа такжеcore-jsпомещатьdependenciesКроме того, на плагины, которые мы разрабатываем, должны ссылаться другие разработчики.Если пакет Vue включен в наш упакованный продукт, это может вызвать различные проблемы.Например, пользователи могут создать два пакета во время выполнения после представления нашего пакета. экземпляр не используется, поэтому его нельзя помещать в зависимости в package.json плагина vue, а нужно помещать вpeerDependencies, указывающее, что соответствующий пакет будет импортирован из других пакетов реферера, а не напрямую импортирован в этот пакет.

  • Добавьте следующий код в package.json, чтобы удалить зависимости в исходных зависимостях.
 "peerDependencies": {
    "core-js": "^3.6.5",
    "vue": "^3.0.0"
  }
  • Добавьте зависимости, связанные со спецификацией git commit, в devDependencies.
{
    "@commitlint/cli": "^11.0.0",
    "@commitlint/config-angular": "^11.0.0",
    "commitizen": "^4.2.2",
    "cz-conventional-changelog": "^3.3.0",
    "husky": "^4.3.0",
}
  • Добавьте config и husky, чтобы настроить журнал изменений для генерации адресов и заставить редакторов отправлять код в соответствии с нашими определенными спецификациями.
{
  "config": {
    "commitizen": {
      "path": "./node_modules/cz-conventional-changelog"
    }
  },
  "husky": {
    "hooks": {
      "commit-msg": "commitlint -E HUSKY_GIT_PARAMS"
    }
  }
}
  • Наконец, добавьте команду отправки и команду для создания журнала изменений в скрипте.
{
    "changelog": "conventional-changelog -p angular -i CHANGELOG.md -s",
    "commit": "git-cz"
}

Настроить команды упаковки

Зависит отДокументацияВидно, что черезvue-cli-service build --target lib --name myLib [entry]команда для создания одной записи в виде библиотеки.

Затем мы можем добавить команду сборки в тег скрипта package.json для выполнения упаковки плагина.Код выглядит следующим образом.

  • Имя упакованного файла vueRightMenuPlugin
  • Входной файл плагина src/main.ts
{
  "build": "vue-cli-service build --target lib --name vueRightMenuPlugin src/main.ts",
}

Поскольку в нашем плагине включен машинописный текст, использование его упаковки по умолчанию не поможет нам сгенерировать файл объявления ts. Проект разработчика, использующий наш плагин, может включить машинописный текст, и при ссылке на плагин будет сообщено об ошибке. Файл объявления не существует, поэтому нам нужно сделать следующее дополнительно:

  • существуетtsconfig.jsonДобавьте следующий код в z, и файл конфигурации будет автоматически сгенерирован в указанном месте проекта при упаковке.
{
    "declaration": true,// 是否生成声明文件
    "declarationDir": "dist/lib",// 声明文件打包的位置
}
  • Создайтеvue.config.jsфайл, чтобы отключить некоторые связанные конфигурации для параллельной упаковки.
module.exports = {
  chainWebpack: config => {
    if (process.env.NODE_ENV === "production") {
      config.module.rule("ts").uses.delete("cache-loader");
      config.module
        .rule("ts")
        .use("ts-loader")
        .loader("ts-loader")
        .tap(opts => {
          opts.transpileOnly = false;
          opts.happyPackMode = false;
          return opts;
        });
    }
  },
  parallel: false
};

После выполнения вышеуказанных операций мы можем автоматически сгенерировать файл объявления при запуске команды упаковки.

заставить css встроенный

Когда я закончил разработку плагина, я обнаружил, что стиль компонента, на который я ссылался, был потерян во время теста.Я долго искал проблему и, наконец, нашел проблему в документации CLI.У него есть свойство css.extract , который используется для настройки включения. Стиль css извлекается в отдельный файл. По умолчанию: находится в производственной среде.true, в среде разработки естьfalse, когда мы упаковываем его, по умолчанию онtrue, пользователю необходимо импортировать этот файл стиля отдельно.

Мы можем сделать это, вручную установив его вfalse, пусть он использует встроенные стили при упаковке, чтобы можно было решить проблему инвалидации стиля, мы находимся вvue.config.jsДобавьте в него следующий код.

module.exports = {
  // 强制css内联
  css: { extract: false }
}

Добавить описание библиотеки

После выполнения вышеперечисленных операций мы завершили настройку, связанную с упаковкой, далее добавим описание библиотеки в package.json, чтобы npm смог корректно идентифицировать наш плагин.

  • имя имя плагина
  • версия номер версии
  • краткое описание плагина описания
  • личное есть личное
  • Расположение входного файла основной библиотеки (упакованный входной файл)
  • Расположение файла объявления для библиотеки типов
  • издатель библиотеки издатель
  • информация о репозитории
  • ключевые слова ключевые слова, ключевые слова, которые совпадают, когда npm находит пакеты
  • Автор библиотеки
  • соблюдать лицензионное соглашение библиотеки с открытым исходным кодом
  • ошибки адрес обратной связи об ошибках
  • Домашняя страница домашней библиотеки
{
  "name": "vue-right-click-menu-next",
  "version": "1.0.0",
  "description": "支持vue3的右键菜单插件",
  "private": false,
  "main": "dist/vueRightMenuPlugin.common.js",
  "types": "dist/lib/main.d.ts",
  "publisher": "magicalprogrammer@qq.com",
  "repository": {
    "type": "git",
    "url": "git+https://github.com/likaia/vue-right-click-menu-next.git"
  },
  "keywords": [
    "vuejs",
    "vue3",
    "vue",
    "rightMenu",
    "右键菜单",
    "vueRightMenu"
  ],
  "author": "likaia",
  "license": "MIT",
  "bugs": {
    "url": "https://github.com/likaia/vue-right-click-menu-next/issues"
  },
  "homepage": "https://github.com/likaia/vue-right-click-menu-next#readme",
}

Чтобы получить полный файл конфигурации, перейдите по ссылке:package.json

Реализовать идеи

Предыдущая статьяНаша идея реализации состоит в том, чтобы vuex выполнял глобальное управление состоянием и контролировал отображение и скрытие контекстного меню. На этот раз мы хотим сделать его подключаемым модулем. Если мы снова будем использовать vuex, те, кто использует наш подключаемый модуль надо ввести vuex, тут немного не к месту.

Компоненты дисплея

Немного подумав, я пришел к следующим идеям:

  1. Сделайте контекстное меню компонентом, черезpropsПередать значения компонентам.
  2. использоватьcreateAppзагрузить компонент, передать значения компоненту, создать контейнер компонента
  3. Создайте элемент div и смонтируйте контейнер компонента прямо сейчас в этом элементе div.

уничтожить компонент

Проделав вышеперечисленные операции, мы поняли, что меню правого клика отображается до указанной позиции, но как его скрыть, после некоторых размышлений, я пришел к следующим идеям:

  1. Реализация вышеуказанного компонента загрузки инкапсулирована в функцию, а созданный элемент div используется в качестве возвращаемого значения.
  2. Глобально объявить переменную в плагинеmenuVM, по умолчанию объявлено как null
  3. Когда внутри инструкции запускается событие щелчка правой кнопкой мыши, вызывается инкапсулированная нами функция, используяmenuVMчтобы получить его возвращаемое значение
  4. На этом этапе мы создаем глобальный прослушиватель событий щелчка, еслиmenuVMне равен нулю, мы удаляем элемент
  5. Когда срабатывает событие правой кнопки, еслиmenuVMЕсли он не нулевой, значит, контекстное меню, которое он открыл в прошлый раз, не закрыто, поэтому будет проблема, поэтому нам также нужно удалить его из тела.

Процесс реализации

Проанализировав идею реализации, приступим к ее реализации.

Создайте компонент контекстного меню

Создайте папку компонентов в src проекта и создайте ее в папкеright-menu.vueМы не будем размещать здесь файл, стиль и содержимое компонента. Здесь мы будем размещать параметры, которые должен передать компонент. Пожалуйста, перейдите к полному коду:right-menu.vue)

<script lang="ts">
import { defineComponent } from "vue";
export default defineComponent({
  name: "right-menu",
  props: {
    rightMenuStatus: String,
    rightMenuTop: String,
    rightMenuLeft: String,
    rightMenuList: Array
  }
});
</script>

Функция компонента монтирования пакета

Мы можем использовать vue3createAppЧтобы загрузить компонент, передать ему значение, а затем смонтировать его на узле dom, код выглядит следующим образом:

/**
 * 将组件挂在到节点上
 * @param comp 需要挂载的组件
 * @param prop 向组件传的参数
 */
const creatComp = function(comp: Component, prop: rightMenuAttribute) {
  // 创建组件
  const app = createApp(comp, {
    ...prop
  });
  // 创建一个div元素
  const divEle = document.createElement("div");
  // 将创建的div元素挂载追加至body里
  document.body.appendChild(divEle);
  // 将组件挂载至刚才创建的div中
  app.mount(divEle);
  // 返回挂载的元素,便于操作
  return divEle;
};

Зарегистрируйте директивы в меню установки и отображения

Далее прописываем vue-инструкцию в методе install плагинаrightClick, перехватить его событие щелчка правой кнопкой мыши, получить параметры, переданные компонентом, смонтировать компонент и отобразить контекстное меню. код показывает, как показано ниже:

install(app: App): void {
    // 创建指令
    app.directive("rightClick", (el, binding): boolean | void => {
      // 指令绑定元素元素是否存在判断
      if (el == null) {
        throw "右键指令错误:元素未绑定";
      }
      el.oncontextmenu = function(e: MouseEvent) {
        if (menuVM != null) {
          // 销毁上次触发的右键菜单DOM
          document.body.removeChild(menuVM);
          menuVM = null;
        }
        const textArray = binding.value.text;
        const handlerObj = binding.value.handler;
        // 菜单选项与事件处理函数是否存在
        if (textArray == null || handlerObj == null) {
          throw "右键菜单内容与事件处理函数为必传项";
        }
        // 事件处理数组
        const handlerArray = [];
        // 处理好的右键菜单
        const menuList = [];
        // 将事件处理函数放入数组中
        for (const key in handlerObj) {
          handlerArray.push(handlerObj[key]);
        }
        if (textArray.length !== handlerArray.length) {
          // 文本数量与事件处理不对等
          throw "右键菜单的每个选项,都必须有它的事件处理函数";
        }
        // 追加右键菜单数据
        for (let i = 0; i < textArray.length; i++) {
          // 右键菜单对象, 添加名称
          const menuObj: rightMenuObjType = {
            text: textArray[i],
            handler: handlerArray[i],
            id: i + 1
          };
          menuList.push(menuObj);
        }
        // 鼠标点的坐标
        const oX = e.clientX;
        const oY = e.clientY;
        // 动态挂载组件,显示右键菜单
        menuVM = creatComp(rightMenu, {
          rightMenuStatus: "block",
          rightMenuTop: oY + "px",
          rightMenuLeft: oX + "px",
          rightMenuList: menuList
        });
        return false;
      };
    });
}

Создать компонент уничтожения слушателя

Когда пользователь щелкает контекстное меню, нам нужно уничтожить компонент и скрыть его, поэтому мы создаем прослушиватель кликов на теле при установке плагина, а затем удаляем смонтированный компонент.Код выглядит следующим образом:

  install(app: App): void {
    // 监听全局点击,销毁右键菜单dom
    document.body.addEventListener("click", () => {
      if (menuVM != null) {
        // 销毁右键菜单DOM
        document.body.removeChild(menuVM);
        menuVM = null;
      }
    });
  }

Пожалуйста, перейдите к полному коду:main.ts)

Опубликовать плагин

После выполнения вышеуказанных операций наш плагин разработан и может быть упакован и опубликован в репозиторий npm.

Терминал выполняет следующую команду:

npm publish --access public

Плагин успешно опубликован:vue-right-click-menu-next

Совместимость с Vue2.x

Плагин не совместим с Vue2.x, из-за нового синтаксиса Vue3 при создании приложения я изначально хотел использовать расширение Vue2.x для монтирования компонентов, но обнаружил, что Vue3 отказался от этого синтаксиса.

Это заставило меня написать два набора плагинов и поддерживать два плагина.

Разницы в логическом уровне плагина нет, отличается только способ написания смонтированного компонента.В Vue2.x требуется следующий способ написания:

/**
 * 将组件挂在到节点上
 * @param comp 需要挂载的组件
 * @param prop 向组件传的参数
 */
const creatComp = function(comp, prop) {
  // 创建组件
  const app = Vue.extend(comp);
  // 创建一个div元素
  const divEle = document.createElement("div");
  // 将创建的div元素挂载追加至body里
  document.body.appendChild(divEle);
  // 将组件挂载至刚才创建的div中, 使用propsData进行传参
  new app({
    propsData: {
      ...prop
    }
  }).$mount(divEle);
  // 返回挂载的元素,便于操作
  return divEle;
};

Адрес плагина:vue-right-click-menu

адрес проекта

Адрес кода плагина, разработанного в этой статье:vue-right-click-menu | vue-right-click-menu-next)

Адрес онлайн-опыта:chat-system

напиши в конце

  • Если в статье есть ошибки, исправьте их в комментариях, если статья вам поможет, ставьте лайк и подписывайтесь 😊

  • Эта статья была впервые опубликована на Наггетс, перепечатка без разрешения запрещена 💌