vue-cli4定制自己的模板

670 阅读2分钟

通过vue-cli插件方式定制自己的模板,cli插件包括以下功能: 1.修改webpack配置 2.与用户进行交互,用户选择所需的一些功能模块 3.扩展package.json 4.创建、修改生成的项目文件 5.添加新的 vue-cli-service 命令

插件文件结构

通常的 CLI 插件目录结构看起来像下面这样:

.
├── README.md
├── generator.js 或者generator文件夹  # 修改或者生成项目文件(可选)
├── index.js      # service 插件
├── package.json
├── prompts.js    # 交互文件(可选)
└── ui.js         # Vue UI 集成(可选)

插件命名规范

为了让一个 CLI 插件在 Vue CLI 项目中被正常使用,它必须遵循 vue-cli-plugin-<name> 或者 @scope/vue-cli-plugin-<name> 这样的命名惯例。例如:vue-cli-plugin-template-preset,这样你的插件才能够:

  • @vue/cli-service 发现;
  • 被其他开发者通过搜索发现;
  • 通过 vue add <name> 或者 vue invoke <name> 安装

插件安装

  1. 本地安装

    // 1.如果没有项目就创建个新的
    vue create my-app
    
    // 2.安装本地插件
    npm install --save-dev file:/full/path/to/your/plugin
    vue invoke vue-cli-plugin-template-preset
    
  2. 使用preset自动在线安装插件

    vue create --preset vue-cli-plugin-template-preset my-app
    
  3. 手动在线安装

    vue create my-app
    cd my-app
    vue add vue-cli-plugin-template-preset
    

Generator修改文件

在 CLI 插件内部,generator 应该放在 generator.js 或者 generator/index.js 文件中。它将在以下两个场景被调用:

  • 项目初始创建期间,CLI 插件被作为项目创建 preset 的一部分被安装时。

  • 当插件在项目创建完成和通过 vue add 或者 vue invoke 单独调用被安装时。

  • 一个 generator 应该export一个接收三个参数的函数

    /**
    * generator/index.js
    * api: <https://cli.vuejs.org/dev-guide/generator-api.html>
    * options: prompt用户交互结果或者 `~/.vuerc`中的option值
    * rootOptions: `~/.vuerc`中的该preset整个值,相当于下面例子的 foo 对象值
    */
    module.exports = (api, options, rootOptions) => {
    	// 使用提供的api或者option进行一些定制
    }
    

    // ~/.vuerc文件格式 { "presets" : { "foo": { "plugins": { "@vue/cli-plugin-foo": { "option": "bar" } // generator拿到的option值 } } } }

一些常用的generator api

  1. api.render('./template') 渲染EJS模板

  2. api.extendPackage

  3. api.entryFile

  4. api.afterInvoke

  5. api.chainWebpack

  6. api.registerCommand

    // generator.js module.exports.hooks = (api) => { api.afterInvoke(() => { const { EOL } = require('os') const fs = require('fs') const contentMain = fs.readFileSync(api.resolve(api.entryFile), { encoding: 'utf-8' }) const lines = contentMain.split(/\r?\n/g)

    const renderIndex = lines.findIndex(line => line.match(/render/))
    lines[renderIndex] += `${EOL}  router,`
    
    fs.writeFileSync(api.entryFile, lines.join(EOL), { encoding: 'utf-8' })
    

    }) }

模板文件生成

  1. 新文件:

    // 放到template文件夹下,然后用api.render解析
    module.exports = api => {
      api.render('./template')
    }
    
  2. 编辑已存在的文件

    • 方式一:

      ---
      extend: '@vue/cli-service/generator/template/src/App.vue' // 目标文件
      replace: !!js/regexp /<script>[^]*?<\\/script>/  // 正则匹配内容
      ---
      
      <script>
      export default {
        // Replace default script
      }
      </script>
      
    • 方式二:多处替换

      ---
      extend: '@vue/cli-service/generator/template/src/App.vue'
      replace:
        - !!js/regexp /Welcome to Your Vue\\.js App/
        - !!js/regexp /<script>[^]*?<\\/script>
      ---
      
      <%# REPLACE %>
      替换欢迎信息
      <%# END_REPLACE %>
      
      <%# REPLACE %>
      <script>
      export default {
        // 替换默认脚本
      }
      </script>
      <%# END_REPLACE %>
      

3.扩展package.json

module.exports = api => {
  api.extendPackage({
    dependencies: {
      'vue-router-layout': '^0.1.2'
    }
  })
}

4.主文件注入

module.exports.hooks = (api) => {
  api.afterInvoke(() => {
    const { EOL } = require('os')
    const fs = require('fs')
		// 读取文件
    const contentMain = fs.readFileSync(api.resolve(api.entryFile), { encoding: 'utf-8' })
    // 文件内容分行
		const lines = contentMain.split(/\\r?\\n/g)

		// 查找到render那一行
    const renderIndex = lines.findIndex(line => line.match(/render/))
    lines[renderIndex] += `${EOL}  router,`

		// 写入
    fs.writeFileSync(api.entryFile, lines.join(EOL), { encoding: 'utf-8' })
  })
}

5.修改webpack配置

对话

所有的对话逻辑都存储在 prompts.js 文件中,用于用户选择所需的插件。所有对话的答案都会传给generator的option

rootOptions

  • 对话的配置项(所有配置项

    // prompts.js
    
    module.exports = [
      {
        name: `addExampleRoutes`, // option的key
        type: 'confirm', // 选项类型:checkbox、confirm
        message: 'Add example routes?', // 对话显示的提示信息
        default: false, // 默认值
    		validate: input => !!input, // 校验用户的答案,true则通过,否则返回错误提示字符串
      }
    ]
    

参考**:**

  1. 插件开发指南 | Vue CLI