如何编写一个vue cli插件

4,123 阅读3分钟

背景

使用 vue 进行开发的同学对vue-cli一定不会陌生,最初 vue-cli@2x的时代,我们使用的时候通常会去修改webpack来满足我们团队的需求,又或者通过自己维护一个脚手架,满足日常开发。

在vue-cli 升级为3x之后提供了插件的选项。cli插件作为vue-cli的部分,为我们提供了便利的开发环境,他使得我们的开发环境插件化,让我们可以针对不同的情况定制不同的插件,例如 vue-cli-plugin-typescript vue-cli-plugin-vant

那么如何实现这么一个cli插件呢?

vue-cli-plugin-kg-request

为了在项目初始化的时候就能将我们自己封装的请求,内置在项目中,我们决定实现一个 vue-cli-plugin-kg-request。下面是一个基本的目录结构:

├── README.md
├── index.js  # service plugin    
└── package.json

service 插件 service plugin 主要修改webpack配置

module.exports = (api, opts) => {
 
}

通过generatorAPI生成模板

一个已经实现的目录结构

  .
  ├── README.md
  ├── generator 
  │   ├── index.js # generator
  │   └── template # plugins template file
  │       └── plugins
  │           └── request.js
  ├── index.js # sevirce plugin 
  └── package.json

通过 generator 可以往 package.json 注入依赖字段,或者添加文件到项目中。api.extendPackage 方法拓展 package.json, 默认情况下,该方法会merge已有依赖项,如果想这么做,可以设置参数 merge: false

// generator/index.js 
module.exports = (api, options, rootOptions) => {
  api.extendPackage({
    dependencies: {
      "axios": "^0.18.0" 
    }
  })
}

编写模板文件以备之后注入到我们创建的项目中。 这里封装了常用数据请求方法,设置了请求拦截,响应数据拦截

// /src/plugins/request.js

import axios from 'axios'
import querystring from 'query-string'

// 请求拦截回调
const requestInterceptors = function(config) {
  if (config.method.toLocaleUpperCase() === 'POST') {
    config.headers['Content-Type'] = 'application/x-www-form-urlencoded'
    config.data = querystring.stringify(config.data)
  }
  return config
}

// 响应拦截回调
const responseInterceptors = function(response) {
  let { data } = response
  // ..
  return Promise.resolve(data)
}

// 响应拦截错误回调
const responseErrorInterceptors = function (error) {
  try {
    let { status } = error.response

  } catch(err) {
    console.error(JSON.stringify(err))
  }

  return Promise.reject(error)
}

const instance = axios.create()

instance.defaults.withCredentials = true

instance.interceptors.request.use(requestInterceptors)

instance.interceptors.response.use(responseInterceptors, responseErrorInterceptors)

export default instance

修改main.js

模板导入之后,我们还要在入口文件进行导入,这样就避免了手动操作。具体做法就是使用fs模块,读取入门文件main.js,然后写入文本。

// generator/index.js 
 api.onCreateComplete(() => {

    // 添加字串
    const iviewLines = `\nimport request from '@/plugins/request'\n\nVue.use(request)`
    // 获取文件内容
    let contentMain = fs.readFileSync(api.entryFile, { encoding: 'utf-8' })
    // 反转内容
    const lines = contentMain.split(/\r?\n/g).reverse()
    // 找到import的下标
    const importIndex = lines.findIndex(line => line.match(/^import/))
    // 在反转第一个import的下面添加引入语句
    lines[importIndex] += iviewLines
    // 回归main内容
    contentMain = lines.reverse().join('\n')
    // 写入入口文件
    fs.writeFileSync(api.entryFile, contentMain, { encoding: 'utf-8' })
  })

api.render 调用时,生成器将使用EJS渲染./template中的文件。

// generator/index.js 
// 渲染模板
api.render('./template')

调试

调试, 编写好之后我们本地调试下我们的代码,测试是否满足。

# 创建一个新项目
vue create plugin-test

# 进入项目目录
cd plugin-test

# 本地安装vue-cli插件
npm install file://localPath/vue-cli-plugin-kg-request

# cli 调用
vue invoke vue-cli-plugin-kg-request

发布

vue-cli插件写完可以发布到npm让更多人使用,这里只是实现了一个简单的例子,各位可以根据自己的需求,自己拓展实现,将自己的开发环境插件化,组装起来,需要注意的是插件的命名必须是这样的vue-cli-plugin-<name>,以备vue add 的时候能够拉取到。官方文档

# 登录你的 npm 账号 
npm login

# 发布
npm publish

关注我们