通过 generator 方法你能够修改项目中的文件。最有用的场景是针对
main.js
或main.ts
文件的一些修改:新的导入,新的Vue.use()
调用等。
背景
当你使用vue-cli创建项目时,如果你选择插件,它会询问你是否需要集成vuex,router等插件,如果选择需要,那么vue-cli就会将其注入到package.json中,并修改main.js如下:
import Vue from 'vue'
import App from './App.vue'
import router from './router'
import store from './store'
Vue.config.productionTip = false
new Vue({
router,
store,
render: h => h(App)
}).$mount('#app')
main.js此时会变成以上内容,那这个“变”是如何实现的呢?
先大致瞄一眼示意图,可以看到设计到两个文件:
- packages/@vue/cli/lib/Create.js
- packages/@vue/cli/lib/Generator.js
我们假设你有一个自定义插件 vue-cli-plugin-my ,并且该插件存在generator.js文件,该文件内容如下:
// 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} my,`
fs.writeFileSync(api.entryFile, lines.join(EOL), { encoding: 'utf-8' })
})
}
该文件作用就是将你的自定义插件注入到new Vue() 依赖中。
afterInvoke 存在于Generator.js initPlugins 方法中,而 initPlugins方法则是由 Create.js 调用
Create.js
const plugins = await this.resolvePlugins(preset.plugins, pkg)
const generator = new Generator(context, {
pkg,
plugins,
afterInvokeCbs,
afterAnyInvokeCbs
})
await generator.generate({
extractConfigFiles: preset.useConfigFiles
})
Generator.js
// apply generators from plugins
for (const plugin of this.plugins) {
const { id, apply, options } = plugin
const api = new GeneratorAPI(id, this, options, rootOptions)
// QIU:const apply = loadModule(`${id}/generator`, this.context) || (() => {})
await apply(api, options, rootOptions, invoking)
// QIU: 设置插件的生命钩子函数
if (apply.hooks) {
// while we execute the entire `hooks` function,
// only the `afterInvoke` hook is respected
// because `afterAnyHooks` is already determined by the `allPluginIds` loop above
await apply.hooks(api, options, rootOptions, pluginIds)
}
}
钩子的收集和调用挂载在GeneratorAPI上。
结合源码阅读,再认真理解下面这个图:
当然,一定要结合完整的源码阅读才能掌握其中精髓