自定义Vue-CLI模板

1,470 阅读4分钟

自定义Vue-CLI模板

实现的原理是 Vue CLI preset ,即在创建新项目时使用预定义的配置和要用到的插件,而这些预定义的内容支持放在 Git 上(包括 GitHub、GitLab 等),使用远程仓库中的 Preset 创建 Vue 项目时需要加入特殊的选项:

# GitHub
$ vue create --preset <username>/<repo> <vue_project_name>

# GitLab 私有服务器
$ vue create --preset direct:https://gitlab.xxx.xx/vue-preset.git --clone <vue_project_name>

# 本地
$ vue craete ./<project_name> <vue_project_name>

初始化 Preset 目录结构

创建目录

.
├─ template/
│   └─ ...
├─ generator.js
├─ preset.json
├─ prompts.js

添加预定义文件

preset.json 是使用 $ vue create 命令时自动生成的预定义选项的 JSON 文件,Windows位于C:\Users\电脑名称\ .vuerc ,MacOS 中位于 ~/.vuerc。文件的内容会显示选择的内容:

{
  "useTaobaoRegistry": true,
  "packageManager": "yarn"
}

之后创建项目就会采用改预定选项,不再进行提示,以下是本地示例:

{
  "useConfigFiles": true,
  "cssPreprocessor": "less",
  "plugins": {
    "@vue/cli-plugin-babel": {},
    "@vue/cli-plugin-eslint": {
      "config": "airbnb",
      "lintOn": ["save", "commit"]
    },
    "@vue/cli-plugin-router": {},
    "@vue/cli-plugin-vuex": {}
  }
}

具体参考官网 Vue CLI preset

定义模板

本人项目模板

D:.
│  .editorconfig
│  .env
│  .env.dev
│  .env.dev213
│  .env.prd
│  .env.prd213
│  .eslintignore
│  .gitattributes
│  .gitignore
│  .npmrc
│  .prettierrc
│  babel.config.js
│  dev.js
│  Directory.txt
│  idea.config.js
│  LICENSE
│  package-lock.json
│  package.json
│  README.md
│  sentry.config.js
│  vue.config.js
│  webstorm.config.js
│  yarn
│  yarn.lock
│         
├─ModifyScript
│      beifen.js
│      config.js
│      
├─public
│  │  color.less
│  │  favicon.ico
│  │  goright.png
│  │  index.html
│  │  v2.js
│  │  
│  ├─dev
│  │      favicon.ico
│  │      
│  └─tinymce
│      │  emojis.js
│      │  
│      ├─langs
│      │      zh_CN.js
│      │      
│      └─skins
│          └─lightgray
│                  content.min.css
│                  skin.min.css
│                  
└─src
    │  App.vue
    │  defaultSettings.js
    │  main.js
    │  permission.js
    │  
    ├─api
    │  ├─dbcp
    │  │      offer.js
    │  │      
    │  └─teamwork
    │          index.js
    │          information.js
    │          workflow.js
    │             
    ├─components
    │  │  ChartCard.vue
    │  │  index.less
    │  │  README.md
    .......
                    

需要注意的地方

  1. 如果模板中没有定义 Vue CLI 本身就会生成的文件,则默认采用原来的,如 view/ 目录下的 Home.vue 和 About.vue 等

  2. . 开头的模板文件需要将 . 改为 _,以 _ 开头的模板文件要定义成 __,否则无法正确渲染

  3. 空文件夹不会被正确渲染

  4. 因为Vue-CLI 会使用EJS渲染,所以模板如果有EJS代码可能会报错,例如:

    <link rel="icon" href="<%= VUE_APP_BASE_URL %>favicon.ico">
    

    运行会报VUE_APP_BASE_URL 找不到,所以需要直接将<%= VUE_APP_BASE_URL %>当成字符串渲染出来,就要改成:

    <link rel="icon" href="<%%= VUE_APP_BASE_URL %%>favicon.ico">
    

扩展依赖包

genarator.js 文件用来为项目添加其它依赖,比如 UI 框架、工具类库等等,渲染 template 模板的操作也需要在该文件内完成。该文件需要导出一个函数,包含三个参数:

  • api:generator 实例,函数中可以操作该实例,比如扩展依赖、检查插件、查看版本等
  • options:定制 Vue CLI 时与交互式命令行结合使用,用来接收答案参数
  • rootOptions:预定义的所有内容,也就是 preset.json 中的所有内容,并且包含项目名称、src/main.js 中配置的说明等:
{
  "useConfigFiles": true,
  "cssPreprocessor": "less",
  "plugins": {
    "@vue/cli-plugin-babel": {},
    "@vue/cli-plugin-eslint": {
      "config": "airbnb",
      "lintOn": ["save", "commit"]
    },
    "@vue/cli-plugin-router": {},
    "@vue/cli-plugin-vuex": {}
  }
}

添加依赖项

为项目添加脚本和依赖项需要使用 generator 实例的 extendPackage() 方法,内容和 package.json 无异,根据依赖的类型声明 NPM 包名和版本即可,创建项目时会自行安装声明的依赖项:

api.extendPackage({
        "dependencies": {
            "@antv/data-set": "^0.11.2",
            "@jeecg/antd-online-beta220": "^1.0.1",
            ...
        },
        "devDependencies": {
            "@babel/polyfill": "^7.2.5",
            "@vue/cli-plugin-babel": "^3.3.0",
			...
        },
        scripts: {
            "pre": "yarn --registry https://registry.npm.taobao.org || cnpm install || npm install --registry https://registry.npm.taobao.org ",
            "dev": "node dev.js && vue-cli-service serve --mode dev",
            "build:prd": "cross-env VUE_APP_RELEASE= npm run gitHash && npm run build -- --mode prd && git rev-parse --short HEAD > ./dist/version",
            "build:test": "cross-env VUE_APP_RELEASE= npm run gitHash && npm run build -- --mode test",
            "build:beta": "cross-env VUE_APP_RELEASE= npm run gitHash && npm run build -- --mode prdBeta && git rev-parse --short HEAD > ./dist/version",
            "build": "node --max_old_space_size=20000 node_modules/@vue/cli-service/bin/vue-cli-service.js build",
            "gitHash": "cross-env-shell \"git rev-parse --short HEAD\"",
            "lint": "vue-cli-service lint",
            "build213": "cross-env VUE_APP_RELEASE= npm run gitHash && npm run build -- --mode prd213 && git rev-parse --short HEAD > ./dist/version"

        }
    })

渲染模板

使用 render() 方法来渲染 template 中定义的模板,该方法实际使用 EJS 进行渲染,可以传入一个相对路径的字符串,会将原本的目录直接替换。也可以传入 Hash 对象,文件对应文件来渲染(不能是文件夹),写多个 rander() 的话会依次执行:

  api.render('./template');

  api.render({
    './.eslintrc.js': './template/_eslintrc.js',
    './.gitignore': './template/_gitignore'
  })

交互式命令行

是一个内建对话配置文件,进行安装时提示用户的操作流程

很多命令行操作都涉及对话的情境,比如 Git 操作、各种 CLI 操作,看起来比较 Geek,实现原理是 Node.js 的交互式命令行 Inquirer.js。要想自定义 Vue CLI 的对话内容需要用到 prompts.js 文件,该文件内应导出一个与 inquirer.prompt() 参数相同数据结构的数组,数组内每一个对象都作为一个命令行中的问题

module.exports = [
    {
        type: "confirm", // 问题类型
        name: "sentry", // 存储答案的 key
        message: "是否使用 sentry", // 问题的内容
        default: false, // 未选择时的默认值
        choices: [ // 可选项
            {
                name: '是', // 选项
                value: true // 选项对应的值
            },
            {
                name: '否', // 选项
                value: false // 选项对应的值
            }
        ]
    },
    {
        type: "confirm", // 问题类型
        name: "micro", // 存储答案的 key
        message: "是否参与微前端", // 问题的内容
        default: false, // 未选择时的默认值
        choices: [ // 可选项
            {
                name: '是', // 选项
                value: true // 选项对应的值
            },
            {
                name: '否', // 选项
                value: false // 选项对应的值
            }
        ]
    },
    {
        name: "app",
        type: "input",
        message: "请为你的app起个名字(xxxApp)",
        when: (answers) => answers.micro,
    },
]

完成对话后应该按照不同的答案执行不同的操作,上面 genarator.js 文件中的函数的 options 参数就起到作用了,可以在函数中打印 options,答案以 Key-value 的形式保存在一个对象内