preface

As an incremental framework, Vue has the flexibility to introduce a variety of tailor-made plug-ins. Every Vue developer has thought about implementing a Vue plugin of his or her own, publishing his or her creative imagination to the NPM community for other Vue developers to examine. Here IS a summary of my own experience with vuE-CLI scaffolding and typescript for developing Vue plug-ins. I hope it will be helpful for other students who want to develop plug-ins.

A clear purpose

Before making a Vue plug-in we need to consider what the plug-in does, how the user will use the plug-in, and how those functions will be implemented.

For example, the vueDraggable library provides DOM drag-and-drop functions for users to change elements in different lists by clicking and dragging with the mouse. It is used by combining the Template template component with JS. We can control component state and data through JS.

Alternatively, an alert plugin will bind the call alert function to the Vue instance. The user can call the call alert in any component by calling this.$alert({options}).

How to register components in plug-ins or provide new properties for Vue instances in plug-ins is explained below.

Once we know what the plug-in does and how other developers want to use it, we can start developing the plug-in.

Environment set up

Erection of scaffolding

First we need to install vuE-CLI3 scaffolding globally.

npm install -g @vue/cli
# OR
yarn global add @vue/cli
Copy the code

New project

And then create our new project

vue create hello-plugin
Copy the code

Since we want typeScript to regulate our code, select Manually Selecty Features in the configuration item to customize the configuration for our project

As you can see from the screenshot, we selected Babel, TypeScript, ESLint, and Unit test in Features. We chose Unit Test because our plug-in needs Unit tests to ensure stability. After all, it needs to be used by other developers. If we don’t write Test cases, it is easy to have all kinds of unexpected bugs when others use it.

Package. The json configuration

Next, modify our package.json.

{
  "name": "hello-plugin"."version": "0.1.0 from"."private": true."scripts": {
    "serve": "vue-cli-service serve"."build": "vue-cli-service build"."test:unit": "vue-cli-service test:unit"."lint": "vue-cli-service lint"
  },
  "dependencies": {
    "core-js": "^ 3.6.4 radar echoes captured." "."vue": "^ 2.6.11." "."vue-class-component": "^ 7.2.2." "."vue-property-decorator": "^ 8.3.0"
  },
  "devDependencies": {
    "@types/chai": "^ 4.2.8"."@types/mocha": "^ 5.2.4." "."@typescript-eslint/eslint-plugin": "^ 2.18.0"."@typescript-eslint/parser": "^ 2.18.0"."@vue/cli-plugin-babel": "^ 4.2.0"."@vue/cli-plugin-eslint": "^ 4.2.0"."@vue/cli-plugin-typescript": "^ 4.2.0"."@vue/cli-plugin-unit-mocha": "^ 4.2.0"."@vue/cli-service": "^ 4.2.0"."@vue/eslint-config-typescript": "^ 5.0.1." "."@vue/test-utils": "31 1.0.0 - beta."."chai": "^ 4.1.2." "."eslint": "^ 6.7.2." "."eslint-plugin-vue": "^ 6.1.2." "."typescript": "~ 3.7.5." "."vue-template-compiler": "^ 2.6.11." "}}Copy the code

This is the default package.json after the project is created, and since the plug-in we develop will be referenced by other developers, it may cause problems if we package the product with the Vue package. The most common problem is that users may create two unused Vue instances at Runtime after importing our package. Therefore, in package.json of the Vue plug-in, we must not place Vue in dependencies, but in peerDependencies, indicating that Vue is introduced from other packages of the reference of this package, and not directly from this package.

That’s the difference

// dependencies
UserProject
|- node_modules
   |- hello-plugins
      |- node_modules
         |- vue
         

// peerDependencies
UserProject
|- node_modules
   |- hello-plugins
   |- vue
Copy the code

However, we cannot guarantee that the user will also use Typescript to develop the project, so we will leave the following two TS related packages in dependencies.

"vue-class-component": "^ 7.2.2." "."vue-property-decorator": "^ 8.3.0"
Copy the code

Also indicate the location of our final product and the instructions that need to be run when packaging.

  "main": "dist/helloPlugin.common.js"."types": "dist/lib/src/main.d.ts"."scripts": {
    "build": "vue-cli-service build --target lib --name helloPlugin src/main.ts",},Copy the code

–target lib –name helloPlugin SRC /main.ts — helloPlugin SRC /main.ts The package entry is SRC /main.ts. For details, see the official documentation of vue-CLI

Main attribute says that if the user introduced us this package will call which files as the entrance, we perform the build instructions generated after dist/helloPlugin.com mon. Js is the entrance of the packet.

The types attribute represents the entry to the type declaration file provided by the package if the user also uses typeScript. How this file is generated is described below.

The final modification of package.json is as follows:

{
  "name": "hello-plugin"."version": "0.1.0 from"."private": true."scripts": {
    "serve": "vue-cli-service serve"."build": "vue-cli-service build"."test:unit": "vue-cli-service test:unit"."lint": "vue-cli-service lint"
  },
  "dependencies": {
    "vue-class-component": "^ 7.2.2." "."vue-property-decorator": "^ 8.3.0"
  },
  "devDependencies": {
    "@types/chai": "^ 4.2.8"."@types/mocha": "^ 5.2.4." "."@typescript-eslint/eslint-plugin": "^ 2.18.0"."@typescript-eslint/parser": "^ 2.18.0"."@vue/cli-plugin-babel": "^ 4.2.0"."@vue/cli-plugin-eslint": "^ 4.2.0"."@vue/cli-plugin-typescript": "^ 4.2.0"."@vue/cli-plugin-unit-mocha": "^ 4.2.0"."@vue/cli-service": "^ 4.2.0"."@vue/eslint-config-typescript": "^ 5.0.1." "."@vue/test-utils": "31 1.0.0 - beta."."chai": "^ 4.1.2." "."eslint": "^ 6.7.2." "."eslint-plugin-vue": "^ 6.1.2." "."typescript": "~ 3.7.5." "."vue-template-compiler": "^ 2.6.11." "."core-js": "^ 3.6.4 radar echoes captured." "."vue": "^ 2.6.11." "
  },
  "peerDependencies": {
    "core-js": "^ 3.6.4 radar echoes captured." "."vue": "^ 2.6.11." "}}Copy the code

Ts correlation processing

Generate declaration file

To generate the.d.ts file, we added the following configuration to tsconfig.ts:

compierOptions: {
    "declaration": true."declarationDir": "dist/lib",}Copy the code

At compile time, all of our ts files will generate corresponding.d.ts files, which will be stored in dist/lib.

Amazingly, you will find that the.d.ts files you expected are not generated

This is fine if you are a standard TS project, but vue-CLI parallelizes webPack packaging and compilation, and we have to turn off some of the parallel packaging configuration to generate the desired type declaration file. So we need to modify the vuE-CLI configuration.

// vue.config.js
module.exports = {
  chainWebpack: (config) => {
    // These are some necessary steps changing the default webpack config of the Vue CLI
    // that need to be changed in order for Typescript based components to generate their
    // declaration (.d.ts) files.
    //
    // Discussed here https://github.com/vuejs/vue-cli/issues/1081
    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};Copy the code

Module is added

If we need to add a new attribute to the Vue object, the user will be reminded that the Vue object does not have this attribute when using it. So we need to put the following declaration in main.ts to add our own attributes to Vue’s type declaration

// main.ts
declare module 'vue/types/vue' {
  interface Vue {
    $alert: IAlert; }}Copy the code

Plug-in development

And finally, the fun coding part. The best guide to Vue plug-in development is, of course, the official documentation.

The user calls vue. use(helloPlugin) to register our plugin. Of course, we can also provide some initialization parameters vue. use(helloPlugin, {… options })

Vue.use calls the install function exposed by our plug-in and passes the parameters provided by the Vue — Vue object and options — user to our install function as parameters.

With Vue objects we can extend on them as much as we want. Here are four ways to use it, as provided in the official documentation.

MyPlugin.install = function (Vue, options) {
  // 1. add global method or property
  Vue.myGlobalMethod = function () {
    // some logic ...
  }

  // 2. add a global asset
  Vue.directive('my-directive', {
    bind(el, binding, vnode, oldVnode) { // some logic ... }... }) // 3. inject some component options Vue.mixin({ created:function() { // some logic ... }... }) // 4. add an instance method Vue.prototype.$myMethod = function(methodOptions) { // some logic ... }}Copy the code

Respectively is:

  • Register global methods or properties for Vue objects
  • Add such asv-onGlobal instruction of
  • Provide mixins for components that are incorporated into all user components.
  • Add methods or properties for Vue instances

Of course we can use all the features of Vue, such as providing our components to the user:

Vue.component('HelloComponent', HelloComponent);
Copy the code

Components registered in the plug-in can be used anywhere in the template, isn’t it convenient?

You can also think of other development models that provide a variety of happy development experiences for users of plug-ins.

Over

Come on, guys.