在用vue-cli脚手架创建项目后通常用npm run serve来启动项目,但是这中间到底发生了什么呢?
溯源
通过vue create test
创建测试目录,可以观察基本的目录结构为
├── test
| ├── node_modules // 当前项目所有依赖,一般不可以移植给其他电脑环境
| ├── public
| | ├── favicon.ico // 标签图标
| | └── index.html // 当前项目唯一的页面
| ├── src
| | ├── assets // 静态资源img、css、js
| | ├── components // 小组件
| | ├── views // 页面组件
| | ├── App.vue // 根组件
| | ├── main.js // 全局脚本文件(项目的入口)
| | ├── router
| | | └── index.js// 路由脚本文件(配置路由 url链接 与 页面组件的映射关系)
| | └── store
| | | └── index.js// 仓库脚本文件(vuex插件的配置文件,数据仓库)
| ├── README.md
└ └── package.json等配置文件
查看根目录下的package.json
文件,存在如下代码
"scripts": {
"serve": "vue-cli-service serve",
"build": "vue-cli-service build",
"lint": "vue-cli-service lint"
},
这里的serve
就是npm run serve
的serve
,即通过serve
定位到这里后npm run serve
就相当于执行`vue-cli-service serve
命令。这里我们也可以将serve
改为dev
然后执行npm run dev
,效果是一样的。
可我执行在命令行下输入vue-cli-service
是无效的,那npm run serve
是如何启动的呢?原来运行npm run serve
命令时npm
会把node_modules
下的.bin
目录添加到Path
,执行完后再删掉。
node_modules\.bin\vue-cli-service
相当于一个快捷方式,它被添加到了环境变量,但它指向的是node_modules\@vue\cli-service\bin\vue-cli-service.js
。
综上所述,npm run serve
就是在运行vue-cli-service.js
这个js
文件
函数解析
#!/usr/bin/env node
const { semver, error } = require('@vue/cli-shared-utils')
const requiredVersion = require('../package.json').engines.node
// 检测node版本是否符合vue-cli运行的需求。不符合则打印错误并退出。
if (!semver.satisfies(process.version, requiredVersion, { includePrerelease: true })) {
error(
`You are using Node ${process.version}, but vue-cli-service ` +
`requires Node ${requiredVersion}.\nPlease upgrade your Node version.`
)
process.exit(1)
}
// cli-service的核心类
const Service = require('../lib/Service')
// 新建一个service的实例。并将项目路径传入
const service = new Service(process.env.VUE_CLI_CONTEXT || process.cwd())// 传入当前项目的所在路径
// 参数处理
const rawArgv = process.argv.slice(2)
// minimist 命令行参数解析器
const args = require('minimist')(rawArgv, {
boolean: [
// build
'modern',
'report',
'report-json',
'inline-vue',
'watch',
// serve
'open',
'copy',
'https',
// inspect
'verbose'
]
})
const command = args._[0]
// 将参数传入service这个实例并启动后续工作。如果我们运行的是npm run serve。则command = "serve"。
service.run(command, args, rawArgv).catch(err => {
error(err)
process.exit(1)
})
vue-cli-service.js
即创建了service
并对象调用了它的run
方法。Service
类定义位于\node_modules\@vue\cli-service\lib\Service.js
.
阅读Service.js
,代码比较长,只摘录其中run
方法以及init。
module.exports = class Service {
constructor (context, { plugins, pkg, inlineOptions, useBuiltIn } = {}) {
process.VUE_CLI_SERVICE = this
this.initialized = false
//一般为项目根目录
this.context = context
this.inlineOptions = inlineOptions
// webpack相关收集。不是本文重点。
this.webpackChainFns = []
this.webpackRawConfigFns = []
this.devServerConfigFns = []
//存储的命令
this.commands = {}
// Folder containing the target package.json for plugins
this.pkgContext = context
// package.json containing the plugins
// 键值对存储的pakcage.json对象
this.pkg = this.resolvePkg(pkg)
// If there are inline plugins, they will be used instead of those
// found in package.json.
// When useBuiltIn === false, built-in plugins are disabled. This is mostly
// for testing.
this.plugins = this.resolvePlugins(plugins, useBuiltIn)
// pluginsToSkip will be populated during run()
this.pluginsToSkip = new Set()
// resolve the default mode to use for each command
// this is provided by plugins as module.exports.defaultModes
// so we can get the information without actually applying the plugin.
//收集插件中的默认配置信息
this.modes = this.plugins.reduce((modes, { apply: { defaultModes }}) => {
return Object.assign(modes, defaultModes)
}, {})
}
init (mode = process.env.VUE_CLI_MODE) {
if (this.initialized) {
return
}
this.initialized = true
this.mode = mode
// load mode .env
if (mode) {
this.loadEnv(mode)
}
// load base .env
this.loadEnv()
// load user config
// 读取用户的配置信息.一般为vue.config.js
const userOptions = this.loadUserOptions()
this.projectOptions = defaultsDeep(userOptions, defaults())
debug('vue:project-config')(this.projectOptions)
// apply plugins.
this.plugins.forEach(({ id, apply }) => {
if (this.pluginsToSkip.has(id)) return
apply(new PluginAPI(id, this), this.projectOptions)
})
// apply webpack configs from project config file
if (this.projectOptions.chainWebpack) {
this.webpackChainFns.push(this.projectOptions.chainWebpack)
}
if (this.projectOptions.configureWebpack) {
this.webpackRawConfigFns.push(this.projectOptions.configureWebpack)
}
}
async run (name, args = {}, rawArgv = []) {
// resolve mode
// prioritize inline --mode
// fallback to resolved default modes from plugins or development if --watch is defined
const mode = args.mode || (name === 'build' && args.watch ? 'development' : this.modes[name])
// --skip-plugins arg may have plugins that should be skipped during init()
this.setPluginsToSkip(args)
// load env variables, load user config, apply plugins
this.init(mode)
args._ = args._ || []
let command = this.commands[name]
if (!command && name) {
error(`command "${name}" does not exist.`)
process.exit(1)
}
if (!command || args.help || args.h) {
command = this.commands.help
} else {
args._.shift() // remove command itself
rawArgv.shift()
}
// 执行命令。例如vue-cli-service serve 则,执行serve命令。
const { fn } = command
return fn(args, rawArgv)
}
resolveChainableWebpackConfig () {
const chainableConfig = new Config()
// apply chains
this.webpackChainFns.forEach(fn => fn(chainableConfig))
return chainableConfig
}
其实质就相当于是一个配置文件。通过vue-cli-service
中的new Service
,加载插件信息,缓存到Service
实例的plugins
变量中。
当得到命令行参数后,在通过new Service
的run
方法,异步执行命令。run
方法调用init
方法项目配置信息。init
方法又调用node_modules\@vue\cli-service\lib\PluginAPI.js
中的PluginAPI.js
类,关联Service
和插件,并存放关系到Service.commands
中,最后commands[cmdArgName]
调用该方法,完成插件调用。
总结
npm run serve
即创建存在当前插件的一个环境并运行当前vue项目的
参考链接
https://www.cnblogs.com/jhpy/p/11873270.html
https://blog.csdn.net/weixin_39555179/article/details/111137470