【vue-cli3源码解析】03_vue cli插件开发

我美好的假期终于结束了,习惯了写BUG、运动,写BUG、看看综艺还是游戏直播的生活。

明天又要回去接受职场pua了,希望我今年找份好工作。

距离上一篇vue cli源码分析已经过去了一周了哈哈哈(忙着整理我的UI库)。

上一节中提到了vue create命令中调用了插件的generate方法去新增依赖啊创建模板等,加上下一个研究的源码命令是vue add,所以先来学习一下如何进行vue cli相关插件的开发。

这次先站在前人的肩膀上去学,后期我再搞一个这样的插件给我的UI库!。

实际案例

源码地址:vue-cli-plugin-element

在我们的vue项目中使用element时,有时候需要按需引入,此时我们还需要根据官网各种配置一波再自己验证一遍,然而使用vue add element进行安装插件时,这个插件很智能地询问你是要全局安装啊还是按需引入啊,我都能帮你配置好哦。

vue-cli-plugin-element这个插件就是帮你安装包的同时,还能根据你的需要初始化一些配置(比如按需引用时加入babel-plugin-component并帮你配置好其他文件等)。

在这里插入图片描述

(还会询问你使用哪种语言)旋转按需引入之后,项目中的文件发生了改变,尤其是babel.config.js配置好了。

在这里插入图片描述

CLI插件的作用

参考一下官网 插件开发指南

  • 修改webpack配置;
  • 添加新的 vue-cli-service 命令 ,比如@vue/cli-plugin-unit-jest添加了test:unit` 命令,允许开发者运行单元测试;
  • 扩展package.json,比如vue add element时旋转按需引用时还新增了babel-plugin-component`这个依赖包;
  • 在项目中新增、修改文件,比如vue add element 时修改了App.vue文件;
  • 提示用户选择一个特定的选项, 比如vue add element 询问引用方式。

开发注意事项

命名

必须遵循 vue-cli-plugin-<name> 或者 @scope/vue-cli-plugin-<name>

文件目录

├── README.md     # 说明
├── generator.js 或者 generator/index.js  # generator(可选)
├── index.js      # service 插件(入口文件)
├── package.json  # 插件包一定要有package.json(便于发布)
├── prompts.js    # prompt 文件(可选)
└── ui.js         # Vue UI 集成(可选)

vue-cli-plugin-element解读

index.js

这个文件超级简单:

module.exports = () => {}

prompts.js

还记得Inquirer吗,可以给用户提供好看的交互界面,这个文件主要是询问用户一些需求:

const localeList = require('./lang.js')

module.exports = [
  {
    type: 'list',
    name: 'import',
    message: 'How do you want to import Element?',
    choices: [
      { name: 'Fully import', value: 'full' },
      { name: 'Import on demand', value: 'partial' }
    ],
    default: 'full',
  },
  {
    when: answers => answers.import === 'full',
    type: 'confirm',
    name: 'customTheme',
    message: 'Do you wish to overwrite Element\'s SCSS variables?',
    default: false,
  },
  {
    type: 'list',
    name: 'lang',
    message: 'Choose the locale you want to load',
    choices: localeList.map(locale => ({
      name: locale,
      value: locale
    })),
    default: 'zh-CN'
  }
]

generator

这个文件有几个作用:

  • 创建新的模板;
  • 编辑已存在的模板;
  • 扩展依赖包extendPackage
  • 修改主文件injectImports

vue-cli-plugin-element中的generator的结构如下:

├── templates/sc  # 这是模板文件
├── indes.js      # 安装时会读取里面的方法
└── utils.js      # 封装的工具函数

分析index.js,里面使用了api这个对象方法:

  • extendPackage 新增packages.json中的依赖:

    module.exports = (api, opts, rootOptions) => {
        api.extendPackage({
            dependencies: {
              'element-ui': '^2.4.5'
            }
        })
        // ...
    }
    
  • injectImports修改主文件(main.js):

    api.injectImports(utils.getMain(), `import './plugins/element.js'`)
    

    判断是向main.js还是main.ts新增import './plugins/element.js

  • render使用 EJS 渲染 ./template 中的文件:

    api.render({
        './src/plugins/element.js': './templates/src/plugins/element.js',
        './src/App.vue': './templates/src/App.vue'
    })
    

    其中./templates/src/plugins/element.js中的文件会根据条件渲染,然后在你的项目中的./src/plugins下新增一个element.js文件。而./templates/src/App.vue会直接替换你现有的App.vue文件。

  • 根据条件渲染不同的模板和新增依赖:

    if (opts.import === 'partial') {
        api.extendPackage({
          devDependencies: {
            'babel-plugin-component': '^1.1.1'
          }
        })
      } else if (opts.customTheme) {
        api.render({
          './src/element-variables.scss': './templates/src/element-variables.scss'
        })
        api.extendPackage({
          devDependencies: {
            'sass-loader': '^7.0.3',
            'node-sass': '^4.9.2'
          }
        })
      }
    

    顺便提一下,这里的opts指的是prompts.js文件中弹窗的选择条件。

  • 在文件写入磁盘时调用onCreateComplete,根据需要创建并更新babel.config.js

    api.onCreateComplete(() => {
        if (opts.import === 'partial') {
          utils.updateBabelConfig(cfg => {
            const pluginComponent = ['component', {
              'libraryName': 'element-ui',
              'styleLibraryName': 'theme-chalk'
            }]
            cfg.plugins = cfg.plugins || []
            cfg.plugins.push(pluginComponent)
            return cfg
          })
        }
      })
    
  • 补充afterInvoke

    官网的栗子:

    module.exports.hooks = (api) => {
      api.afterInvoke(() => {
        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] += `\n router,`
      })
    }
    

    main.js已经存在的情况下,找到render这一行的行数,将该行写上router

    import Vue from 'vue'
    import App from './App.vue'
    import router from './router'
    
    new Vue({
      router,   // 新增
      render: h => h(App)
    }).$mount('#app')
    

更多有关于Generator API可以查看 GeneratorAPI

总结

vue cli插件开发需要遵循各种规范,其中generator的作用是为了方便新增依赖、新增修改文件等操作。后期等我的UI库开发完善了,我也来抄一个!

参考

插件开发指南


若有错误,欢迎指出,感谢~

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值