CLI 2.x generated projects have build and config directories associated with webpack packaging, as shown below
If you want to upgrade webpack in the future, you have to upgrade Vue CLI by yourself. 3. By the official maintenance, more trouble. Webpack-chain provides a chain-call API to generate/modify a runtime dependency (@vue/cli-service) on the WebPack configuration. Dependencies: upgradable; Built on WebPack with reasonable default configuration; It can be configured through the configuration file in the project; Extensions can be made through plug-ins. // If you want to add new functions, such as using easy Tool to synchronize multiple languages within the group, you need to add the easy Tool configuration file to the project. And provides an interface to the plug-in, you can generate specified files in the project, add easy-tool configuration files to the project these things can be automated operations can be completed in the plug-in, group members can install the plug-in to the project, perform related operations to call the plug-in.Copy the code
Vue CLI 3 plugin see here
More than that, of courseCopy the code
Related toolkits
Commander Node.js inquirer Interactive Command Line, Webpack-chain provides a chain call API to generate/modify the webpack configuration FS-Extra system FS module extension, which provides more convenient APIS. And inherits the FS module API execa Process Execution for Humans, Improves Child_process EJS template engine, similar to Jade/Pug, Mustache DeepMerge, Merge the enumerable semver (Semantic Versioning) attribute of the two objects for the Semantic operation of the NPM version number Process. env recast Converts code to AST, modifies AST, etcCopy the code
vue create
The @vue/ CLI package provides vue [options] commands to quickly create a new project preset using vue create: the main file that contains preset configuration data
// Local preset vue create -p./preset. Json Dolphin_template // Load remote presetCopy the code
To build a project, follow this process. First official gives us an initial template (provided by the official plugin), then we write vue-CLI plugins based on the official template, and finally get an initial project that conforms to our requirements. Preset can be specified through plugins, and preset is based on the official default template. Go new/override some files, modify configuration files (vue.config.js, babel.config.js, etc.), inject script commands in package.json, etc. ##presetCopy the code
{
"plugins": {
"@vue/cli-plugin-babel": {
"version": "^ 3.5.1 track of"
},
"@vue/cli-plugin-eslint": {
"version": "^ 3.5.1 track of"."config": "standard"."lintOn": [
"save"]},"vue-cli-plugin-dolphin-base": {
"version": "2.6.5"."prompts": true
},
"vue-cli-plugin-dolphin-theme": {
"version": "2.6.5"
},
"vue-cli-plugin-changelog": {
"version": "2.6.5"
},
"vue-cli-plugin-easytool": {
"version": "2.6.5"
},
"vue-cli-plugin-easymock": {
"version": "2.6.5"."prompts": true
},
"vue-cli-plugin-lego": {
"version": "2.6.5"}}}Copy the code
No external plug-ins are used
What would the generated project look like with vue Create if we didn't add any plug-ins to preset. Json? Figure 4. The flow from the command line above can be summarized in the figure below. Changes to the project directory and package.json are also shown in the figure belowCopy the code
Where is the execution code for the command line tool
The code for vue create is in the @vue/cli package, Figure @ vue/cli/bin/vue. Js, cooperate with the package. The json bin field using https://docs.npmjs.com/files/package.json#bin # # the Execute vue create @vue/cli/bin/vue.jsCopy the code
const program = require('commander')
// ...
program
.command('create <app-name>')
.description('create a new project powered by vue-cli-service')
.option('-p, --preset <presetName>'.'Skip prompts and use saved or remote preset')
.option('-c, --clone'.'Use git clone when fetching remote preset') .action((name, cmd) => { const options = cleanArgs(cmd) // ... Some boundary conditions require('.. /lib/create')(name, options)
})
//...
program.parse(process.argv)
Copy the code
Using COMMANDER for command-line arguments, you can get the configuration parameter options input by the user to get the name of the project to be created, for example dolphin_template. After this, the formal process of creating a project begins:Copy the code
1. Verify and obtain the output directory
ValidateProjectName, for example, a user may use vue-cli to create a generic library for NPM. The library name should appear in the URL. Avoid non-url-safe characters. Does dolphin_tempalte exist in the target directory? If overwrite/merge/cancel (inquirer. Js) is displayed, cli information collected using commander includes parameter configuration information, project name, and targetDir @vue/cli/lib/create.js
const cwd = options.cwd || process.cwd()
const inCurrent = projectName === '. '
const name = inCurrent ? path.relative('.. / ', CWD) : the projectName / / project directory name const targetDir = path. Resolve (CWD, projectName | |'. ')
Copy the code
TargetDir is an important information for subsequent dependency installation, template generation, plug-in directory retrieval, dependency lookup, and so on.
2. Obtain Preset information
Only the logic related to vue create -p is discussed here, and since you specify the plug-in you want to install, some prompt related logic can be ignored. Resolve preset/ Inline preset to determine which preset is to be loaded, if preset is not used, ask prompt to determine. @vue/cli/lib/Creator
/ / use the presetif (cliOptions.preset) {
// vue create foo --preset bar
preset = await this.resolvePreset(cliOptions.preset, cliOptions.clone)
} else if (cliOptions.default) {
// vue create foo --default
preset = defaults.presets.default
} else if(cliOptions.inlinePreset) { // vue create foo --inlinePreset {... } try { preset = JSON.parse(cliOptions.inlinePreset) } catch (e) { error(`CLI inline preset is not valid JSON:${cliOptions.inlinePreset}`)
exit(1)}}else{/ / adopt the way of asking preset = await this. PromptAndResolvePreset ()}Copy the code
3. Determine the package manager to use
Priority: Command line arguments packageManager >.vuerc packageManager > YARN > PNPM > NPM Preset configurations saved during vue create are placed in a configuration file in your home directory (~ /. Vuerc). You can adjust, add, and delete saved configurations by editing the file directly. Cli.vuejs.org/zh/guide/pl…
const packageManager = (
cliOptions.packageManager ||
loadOptions().packageManager ||
(hasYarn() ? 'yarn' : null) ||
(hasPnpm3OrLater() ? 'pnpm' : 'npm')
)
const pm = new PackageManager({ context, forcePackageManager: packageManager }
Copy the code
4. Inject internal plug-ins
The list of plug-ins to be loaded by the user is obtained using preset, and the internal plugin @vue/ CLI-service is addedCopy the code
// Add an internal plugin and set its options(projectName, preset property) preset. Plugins ['@vue/cli-service'] = Object.assign({
projectName: name
}, preset)
Copy the code
5. Determine the plug-in version and generate package.json
For official plug-ins, the version defined in preset is preferred, and latest is used, otherwise the latest minor version is used. If @vue/cli monorepo is 3.2.4, Use the ^ 3.2.0. Because the minor versions maintained by Monorepo can be considered uniform, the sub-REPo Path versions differ
const pkg = {
name,
version: '0.1.0 from',
private: true,
devDependencies: {}
}
//...
const deps = Object.keys(preset.plugins)
pkg.devDependencies[dep] = (
preset.plugins[dep].version || ((/^@vue/.test(dep)) ? `^${latestMinor}` : `latest`)
)
Copy the code
In the project name dolphin_template folder, generate package.json await writeFileTree(context, {'package.json': JSON.stringify(pkg, null, 2)
})
async functionwriteFileTree (dir, files, previousFiles) { // ... Object.keys(files).forEach((name) => { const filePath = path.join(dir, Name) fs.ensureDirsync (path.dirName (filePath)) // Create project folder fs.writefilesync (filePath, files[name])})}Copy the code
6. git init
If your environment has Git, git init is executed by defaultCopy the code
7. Install dep
Child processes run YARN/NPM intall, etc., to install dependencies in package.json (including @vue/ CLI-service, preset).log(' ⚙ Installing CLI plugins. This might take a while... `) execa('npm'['install'] // Simplified versionCopy the code
8. resolve plugins && invoke plugins
Plugins are objects that save the information of the plugins, including the plugins defined in preset. Json, and the internal plug-in @vue/ CLI-service that is injected. Vue -cli uses object.keys () to sort the keys of objects. Object form plugins
{
"@vue/cli-service": / / {}"vue-cli-plugin-dolphin-base": {
// "version": "2.6.5", / /"prompts": true/ /}, / /... } the specification defines the output order of object.keys () var foo = {a: 1, b:2, c: 3} var bar = {} bar.c = foo.c delete foo.c Object.keys(foo).forEach(key => { bar[key] = foo[key] }) // Object.keys(foo) / / /'a', b'] // Object.keys(bar) // ['c', 'a', 'b']
Copy the code
Object foo, for non-integer like strings (a,b,c), in the order in which the attributes are created. Firstly, integer-like keys in ascending order Secondly, normal keys in insertion order Then, Symbols in insertion order Lastly, if mixed, order: interger-like, normal keys, Symbols https://tc39.es/ecma262/#sec-ordinaryownpropertykeys https://2ality.com/2015/10/property-traversal-order-es6.html https://zhuanlan.zhihu.com/p/40601459 https://juejin.cn/post/6844903796062191624Copy the code
@vue/cli/lib/Creator.js
async resolvePlugins (rawPlugins) {
// ensure cli-service is invoked first
rawPlugins = sortObject(rawPlugins, ['@vue/cli-service'].true)
const plugins = []
for (const id of Object.keys(rawPlugins)) {
const apply = loadModule(`${id}/generator`, this.context) || (() => {})
let options = rawPlugins[id] || {}
if (options.prompts) {
// ...
}
plugins.push({ id, apply, options })
}
returnAfter sorting the plugins in the form of plugins} objects, generate the corresponding array {"@vue/cli-service": {
//...
},
// "vue-cli-plugin-dolphin-base": {
// "version": "2.6.5", / /"prompts": true/ /}, / /... } sorted, array plugins [{id:'@vue/cli-service'
apply: require(require.resolve('@vue/cli-service/generator', context)), options: { //... }}] plugins provide templates that call vue-cli-* plugins' template generation logic in sequencefor(const plugin of this.plugins) { const { id, apply, options } = plugin const api = new GeneratorAPI(id, this, options, rootOptions) await apply(api, options, rootOptions, invoking) // ... } Each plug-in may provide a template to iterate over, using an object to record the path and content of the file to be generated. In case of overwriting, a typical CLI plug-in directory structure looks like this: ├ ─ ─ the README. Md ├ ─ ─ the generator, js# generator (optional)├ ─ ─ prompts. Js# prompt file (optional)├ ─ ─ index. Js# service plug-in└ ─ ─ package. JsonCopy the code