Vue-cli@3.0 插件系統(tǒng)簡(jiǎn)析
Vue-cli@3.0 是一個(gè)全新的 Vue 項(xiàng)目腳手架。不同于 1.x/2.x 基于模板的腳手架,Vue-cli@3.0 采用了一套基于插件的架構(gòu),它將部分核心功能收斂至 CLI 內(nèi)部,同時(shí)對(duì)開發(fā)者暴露可拓展的 API 以供開發(fā)者對(duì) CLI 的功能進(jìn)行靈活的拓展和配置。接下來我們就通過 Vue-cli@3.0 的源碼來看下這套插件架構(gòu)是如何設(shè)計(jì)的。
整個(gè)插件系統(tǒng)當(dāng)中包含2個(gè)重要的組成部分:
- @vue/cli,提供 cli 命令服務(wù),例如vue create創(chuàng)建一個(gè)新的項(xiàng)目;
- @vue/cli-service,提供了本地開發(fā)構(gòu)建服務(wù)。
@vue/cli-service
當(dāng)你使用 vue create <project-name> 創(chuàng)建一個(gè)新的 Vue 項(xiàng)目,你會(huì)發(fā)現(xiàn)生成的項(xiàng)目相較于 1.x/2.x 初始化一個(gè)項(xiàng)目時(shí)從遠(yuǎn)程拉取的模板發(fā)生了很大的變化,其中關(guān)于 webpack 相關(guān)的配置以及 npm script 都沒有在模板里面直接暴露出來,而是提供了新的 npm script:
// package.json
"scripts": {
"serve": "vue-cli-service serve",
"build": "vue-cli-service build",
"lint": "vue-cli-service lint"
}
前 2 個(gè)腳本命令是項(xiàng)目本地安裝的 @vue/cli-service 所提供的基于 webpack 及相關(guān)的插件進(jìn)行封裝的本地開發(fā)/構(gòu)建的服務(wù)。@vue/cli-service 將 webpack 及相關(guān)插件提供的功能都收斂到 @vue/cli-service 內(nèi)部來實(shí)現(xiàn)。
這 2 個(gè)命令對(duì)應(yīng)于 node_modules/@vue/cli-service/lib/commands 下的 serve.js 和 build/index.js。
在 serve.js 和 build/index.js 的內(nèi)部分別暴露了一個(gè)函數(shù)及一個(gè) defaultModes 屬性供外部來使用。 事實(shí)上這兩者都是作為 built-in(內(nèi)置)插件來供 vue-cli-service 來使用的 。
說到這里那么就來看看 @vue/cli-service 內(nèi)部是如何搭建整個(gè)插件系統(tǒng)的。就拿執(zhí)行 npm run serve 啟動(dòng)本地開發(fā)服務(wù)來說,大概流程是這樣的:

首先來看下 @vue/cli-service 提供的 cli 啟動(dòng)入口服務(wù)(@vue/cli-service/bin/vue-cli-service.js):
#!/usr/bin/env node
const semver = require('semver')
const { error } = require('@vue/cli-shared-utils')
const Service = require('../lib/Service') // 引入 Service 基類
const service = new Service(process.env.VUE_CLI_CONTEXT || process.cwd()) // 實(shí)例化 service
const rawArgv = process.argv.slice(2)
const args = require('minimist')(rawArgv)
const command = args._[0]
service.run(command, args, rawArgv).catch(err => { // 開始執(zhí)行對(duì)應(yīng)的 service 服務(wù)
error(err)
process.exit(1)
})
看到這里你會(huì)發(fā)現(xiàn)在 bin 里面并未提供和本地開發(fā) serve 相關(guān)的服務(wù),事實(shí)上在項(xiàng)目當(dāng)中本地安裝的 @vue/cli-service 提供的不管是內(nèi)置的還是插件提供的服務(wù)都是動(dòng)態(tài)的去完成相關(guān) CLI 服務(wù)的注冊(cè)。
在 lib/Service.js 內(nèi)部定義了一個(gè)核心的類 Service,它作為 @vue/cli 的運(yùn)行時(shí)的服務(wù)而存在。在執(zhí)行 npm run serve 后,首先完成 Service 的實(shí)例化工作:
class Service {
constructor(context) {
...
this.webpackChainFns = [] // 數(shù)組內(nèi)部每項(xiàng)為一個(gè)fn
this.webpackRawConfigFns = [] // 數(shù)組內(nèi)部每項(xiàng)為一個(gè) fn 或 webpack 對(duì)象字面量配置項(xiàng)
this.devServerConfigFns = []
this.commands = {} // 緩存動(dòng)態(tài)注冊(cè) CLI 命令
...
this.plugins = this.resolvePlugins(plugins, useBuiltIn) // 完成插件的加載
this.modes = this.plugins.reduce((modes, { apply: { defaultModes }}) => { // 緩存不同 CLI 命令執(zhí)行時(shí)所對(duì)應(yīng)的mode值
return Object.assign(modes, defaultModes)
}, {})
}
}
在實(shí)例化 Service 的過程當(dāng)中完成了兩個(gè)比較重要的工作:
加載插件
將插件提供的不同命令服務(wù)所使用的 mode 進(jìn)行緩存
當(dāng) Service 實(shí)例化完成后,調(diào)用實(shí)例上的 run 方法來啟動(dòng)對(duì)應(yīng)的 CLI 命令所提供的服務(wù)。
async run (name, args = {}, rawArgv = []) {
const mode = args.mode || (name === 'build' && args.watch ? 'development' : this.modes[name])
// load env variables, load user config, apply plugins
// 執(zhí)行所有被加載進(jìn)來的插件
this.init(mode)
...
const { fn } = command
return fn(args, rawArgv) // 開始執(zhí)行對(duì)應(yīng)的 cli 命令服務(wù)
}
init (mode = process.env.VUE_CLI_MODE) {
...
// 執(zhí)行plugins
// apply plugins.
this.plugins.forEach(({ id, apply }) => {
// 傳入一個(gè)實(shí)例化的PluginAPI實(shí)例,插件名作為插件的id標(biāo)識(shí),在插件內(nèi)部完成注冊(cè) cli 命令服務(wù)和 webpack 配置的更新的工作
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)
}
}
接下來我們先看下 @vue/cli-service 當(dāng)中的 Service 實(shí)例化的過程:通過 resolvePlugins 方法去完成插件的加載工作:
resolvePlugins(inlinePlugins, useBuiltIn) {
const idToPlugin = id => ({
id: id.replace(/^.\//, 'built-in:'),
apply: require(id) // 加載對(duì)應(yīng)的插件
})
let plugins
// @vue/cli-service內(nèi)部提供的插件
const builtInPlugins = [
'./commands/serve',
'./commands/build',
'./commands/inspect',
'./commands/help',
// config plugins are order sensitive
'./config/base',
'./config/css',
'./config/dev',
'./config/prod',
'./config/app'
].map(idToPlugin)
if (inlinePlugins) {
plugins = useBuiltIn !== false
? builtInPlugins.concat(inlinePlugins)
: inlinePlugins
} else {
// 加載項(xiàng)目當(dāng)中使用的插件
const projectPlugins = Object.keys(this.pkg.devDependencies || {})
.concat(Object.keys(this.pkg.dependencies || {}))
.filter(isPlugin)
.map(idToPlugin)
plugins = builtInPlugins.concat(projectPlugins)
}
// Local plugins
if (this.pkg.vuePlugins && this.pkg.vuePlugins.service) {
const files = this.pkg.vuePlugins.service
if (!Array.isArray(files)) {
throw new Error(`Invalid type for option 'vuePlugins.service', expected 'array' but got ${typeof files}.`)
}
plugins = plugins.concat(files.map(file => ({
id: `local:${file}`,
apply: loadModule(file, this.pkgContext)
})))
}
return plugins
}
在這個(gè) resolvePlugins 方法當(dāng)中,主要完成了對(duì)于 @vue/cli-service 內(nèi)部提供的插件以及項(xiàng)目應(yīng)用(package.json)當(dāng)中需要使用的插件的加載,并將對(duì)應(yīng)的插件進(jìn)行緩存。在其提供的內(nèi)部插件當(dāng)中又分為兩類:
'./commands/serve'
'./commands/build'
'./commands/inspect'
'./commands/help'
這一類插件在內(nèi)部動(dòng)態(tài)注冊(cè)新的 CLI 命令,開發(fā)者即可通過 npm script 的形式去啟動(dòng)對(duì)應(yīng)的 CLI 命令服務(wù)。
'./config/base'
'./config/css'
'./config/dev'
'./config/prod'
'./config/app'
這一類插件主要是完成 webpack 本地編譯構(gòu)建時(shí)的各種相關(guān)的配置。@vue/cli-service 將 webpack 的開發(fā)構(gòu)建功能收斂到內(nèi)部來完成。
插件加載完成,開始調(diào)用 service.run 方法,在這個(gè)方法內(nèi)部開始執(zhí)行所有被加載的插件:
this.plugins.forEach(({ id, apply }) => {
apply(new PluginAPI(id, this), this.projectOptions)
})
在每個(gè)插件執(zhí)行的過程中,接收到的第一個(gè)參數(shù)都是 PluginAPI 的實(shí)例,PluginAPI 也是整個(gè) @vue/cli-service 服務(wù)當(dāng)中一個(gè)核心的基類:
class PluginAPI {
constructor (id, service) {
this.id = id // 對(duì)應(yīng)這個(gè)插件名
this.service = service // 對(duì)應(yīng) Service 類的實(shí)例(單例)
}
...
registerCommand (name, opts, fn) { // 注冊(cè)自定義 cli 命令
if (typeof opts === 'function') {
fn = opts
opts = null
}
this.service.commands[name] = { fn, opts: opts || {}}
}
chainWebpack (fn) { // 緩存變更的 webpack 配置
this.service.webpackChainFns.push(fn)
}
configureWebpack (fn) { // 緩存變更的 webpack 配置
this.service.webpackRawConfigFns.push(fn)
}
...
}
每個(gè)由 PluginAPI 實(shí)例化的 api 實(shí)例都提供了:
- 注冊(cè) cli 命令服務(wù)( api.registerCommand )
- 通過 api 形式去更新的 webpack 配置( api.chainWebpack )
- 通過 raw 配置形式去更新的 webpack 配置( api.configureWebpack ),與 api.chainWebpack 提供的鏈?zhǔn)?api 操作 webpack 配置的方式不同, api.configureWebpack 可接受raw式的配置形式,并通過 webpack-merge 對(duì) webpack 配置進(jìn)行合并。
- resolve wepack 配置( api.resolveWebpackConfig ),調(diào)用之前通過 chainWebpack 和 configureWebpack 上完成的對(duì)于 webpack 配置的改造,并生成最終的 webpack 配置
- ...
首先我們來看下 @vue/cli-service 提供的關(guān)于動(dòng)態(tài)注冊(cè) CLI 服務(wù)的插件,拿 serve 服務(wù)( ./commands/serve )來說:
// commands/serve
module.exports = (api, options) => {
api.registerCommand(
'serve',
{
description: 'start development server',
usage: 'vue-cli-service serve [options] [entry]',
options: {
'--open': `open browser on server start`,
'--copy': `copy url to clipboard on server start`,
'--mode': `specify env mode (default: development)`,
'--host': `specify host (default: ${defaults.host})`,
'--port': `specify port (default: ${defaults.port})`,
'--https': `use https (default: ${defaults.https})`,
'--public': `specify the public network URL for the HMR client`
}
},
async function serve(args) {
// do something
}
)
}
./commands/serve 對(duì)外暴露一個(gè)函數(shù),接收到的第一個(gè)參數(shù) PluginAPI 的實(shí)例 api,并通過 api 提供的 registerCommand 方法來完成 CLI 命令(即 serve 服務(wù))的注冊(cè)。
再來看下 @vue/cli-service 內(nèi)部提供的關(guān)于 webpack 配置的插件( ./config/base ):
module.exports = (api, options) => {
api.chainWebpack(webpackConfig => {
webpackConfig.module
.rule('vue')
.test(/\.vue$/)
.use('cache-loader')
.loader('cache-loader')
.options(vueLoaderCacheConfig)
.end()
.use('vue-loader')
.loader('vue-loader')
.options(
Object.assign(
{
compilerOptions: {
preserveWhitespace: false
}
},
vueLoaderCacheConfig
)
)
})
}
這個(gè)插件完成了 webpack 的基本配置內(nèi)容,例如 entry、output、加載不同文件類型的 loader 的配置。 不同于之前使用的配置式的 webpack 使用方式,@vue/cli-service 默認(rèn)使用 webpack-chain( 鏈接請(qǐng)戳我 ) 來完成 webpack 配置的修改 。這種方式也使得 webpack 的配置更加靈活,當(dāng)你的項(xiàng)目遷移至 @vue/cli@3.0,使用的 webpack 插件也必須要使用 API 式的配置,同時(shí)插件不僅僅要提供插件自身的功能,同時(shí)也需要幫助調(diào)用方完成插件的注冊(cè)等工作。
@vue/cli-service 將基于 webpack 的本地開發(fā)構(gòu)建配置收斂至內(nèi)部來實(shí)現(xiàn),當(dāng)你沒有特殊的開發(fā)構(gòu)建需求的時(shí)候,內(nèi)部配置可以開箱即用,不用開發(fā)者去關(guān)心一些細(xì)節(jié)。當(dāng)然在實(shí)際團(tuán)隊(duì)開發(fā)當(dāng)中,內(nèi)部配置肯定是無法滿足的,得益于 @vue-cli@3.0 的插件構(gòu)建設(shè)計(jì),開發(fā)者不需要將內(nèi)部的配置進(jìn)行 Eject,而是直接使用 @vue/cli-service 暴露出來的 API 去完成對(duì)于特殊的開發(fā)構(gòu)建需求。
以上介紹了 @vue/cli-service 插件系統(tǒng)當(dāng)中幾個(gè)核心的模塊,即:
Service.js 提供服務(wù)的基類,它提供了 @vue/cli 生態(tài)當(dāng)中本地開發(fā)構(gòu)建時(shí):插件加載(包括內(nèi)部插件和項(xiàng)目應(yīng)用插件)、插件的初始化,它的單例被所有的插件所共享,插件使用它的單例去完成 webpack 的更新。
PluginAPI.js 提供供插件使用的對(duì)象接口,它和插件是一一對(duì)應(yīng)的關(guān)系。所有供 @vue/cli-service 使用的本地開發(fā)構(gòu)建的插件接收的第一個(gè)參數(shù)都是 PluginAPI 的實(shí)例( api ),插件使用這個(gè)實(shí)例去完成 CLI 命令的注冊(cè)及對(duì)應(yīng)服務(wù)的執(zhí)行、webpack 配置的更新等。
以上就是 @vue/cli-service 插件系統(tǒng)簡(jiǎn)單的分析,感興趣的同學(xué)可以深入閱讀相關(guān)源碼( 鏈接請(qǐng)戳我 )進(jìn)行學(xué)習(xí)。
@vue/cli
不同于之前 1.x/2.x 的 vue-cli 工具都是基于遠(yuǎn)程模板去完成項(xiàng)目的初始化的工作,它屬于那種大而全的方式,當(dāng)你需要完成自定義的腳手架工具時(shí),你可能要對(duì) vue-cli 進(jìn)行源碼級(jí)別的改造,或者是在遠(yuǎn)程模板里面幫開發(fā)者將所有的配置文件初始化完成好。而 @vue/cli@3.0 主要是基于插件的 generator 去完成項(xiàng)目的初始化的工作,它將原來的大而全的模板拆解為現(xiàn)在基于插件系統(tǒng)的工作方式,每個(gè)插件完成自己所要對(duì)于項(xiàng)目應(yīng)用的模板拓展工作。
@vue/cli 提供了終端里面的 vue 命令,例如:
vue create <project> vue ui
當(dāng)你需要對(duì) vue-cli 進(jìn)行改造,自定義符合自己開發(fā)要求的腳手架的時(shí)候,那么你需要通過 開發(fā) vue-cli 插件來對(duì) vue-cli 提供的服務(wù)進(jìn)行拓展來滿足相關(guān)的要求 。vue-cli 插件始終包含一個(gè) Service 插件作為其主要導(dǎo)出,且可選的包含一個(gè) Generator 和一個(gè) Prompt 文件。這里不細(xì)講如何去開發(fā)一個(gè) vue-cli 插件了,大家感興趣的可以閱讀 vue-cli-plugin-eslint
這里主要是來看下 vue-cli 是如何設(shè)計(jì)整個(gè)插件系統(tǒng)以及整個(gè)插件系統(tǒng)是如何工作的。
@vue/cli@3.0 提供的插件安裝方式為一個(gè) cli 服務(wù): vue add <plugin> :
install a plugin and invoke its generator in an already created project
執(zhí)行這條命令后,@vue/cli 會(huì)幫你完成插件的下載,安裝以及執(zhí)行插件所提供的 generator。整個(gè)流程的執(zhí)行順序可通過如下的流程圖去概括:

我們來看下具體的代碼邏輯:
// @vue/cli/lib/add.js
async function add (pluginName, options = {}, context = process.cwd()) {
...
const packageManager = loadOptions().packageManager || (hasProjectYarn(context) ? 'yarn' : 'npm')
// 開始安裝這個(gè)插件
await installPackage(context, packageManager, null, packageName)
log(`${chalk.green('✔')} Successfully installed plugin: ${chalk.cyan(packageName)}`)
log()
// 判斷插件是否提供了 generator
const generatorPath = resolveModule(`${packageName}/generator`, context)
if (generatorPath) {
invoke(pluginName, options, context)
} else {
log(`Plugin ${packageName} does not have a generator to invoke`)
}
}
首先 cli 內(nèi)部會(huì)安裝這個(gè)插件,并判斷這個(gè)插件是否提供了 generator,若提供了那么去執(zhí)行對(duì)應(yīng)的 generator。
// @vue/cli/lib/invoke.js
async function invoke (pluginName, options = {}, context = process.cwd()) {
const pkg = getPkg(context)
...
// 從項(xiàng)目應(yīng)用package.json中獲取插件名
const id = findPlugin(pkg.devDependencies) || findPlugin(pkg.dependencies)
...
// 加載對(duì)應(yīng)插件提供的generator方法
const pluginGenerator = loadModule(`${id}/generator`, context)
...
const plugin = {
id,
apply: pluginGenerator,
options
}
// 開始執(zhí)行g(shù)enerator方法
await runGenerator(context, plugin, pkg)
}
async function runGenerator (context, plugin, pkg = getPkg(context)) {
...
// 實(shí)例化一個(gè)Generator實(shí)例
const generator = new Generator(context, {
pkg
plugins: [plugin], // 插件提供的generator方法
files: await readFiles(context), // 將項(xiàng)目當(dāng)中的文件讀取為字符串的形式保存到內(nèi)存當(dāng)中,被讀取的文件規(guī)則具體見readFiles方法
completeCbs: createCompleteCbs,
invoking: true
})
...
// resolveFiles 將內(nèi)存當(dāng)中的所有緩存的 files 輸出到文件當(dāng)中
await generator.generate({
extractConfigFiles: true,
checkExisting: true
})
}
和 @vue/cli-service 類似,在 @vue/cli 內(nèi)部也有一個(gè)核心的類 Generator ,每個(gè) @vue/cli 的插件對(duì)應(yīng)一個(gè) Generator 的實(shí)例。在實(shí)例化 Generator 方法的過程當(dāng)中,完成插件提供的 generator 的執(zhí)行。
// @vue/cli/lib/Generator.js
module.exports = class Generator {
constructor (context, {
pkg = {},
plugins = [],
completeCbs = [],
files = {},
invoking = false
} = {}) {
this.context = context
this.plugins = plugins
this.originalPkg = pkg
this.pkg = Object.assign({}, pkg)
this.imports = {}
this.rootOptions = {}
...
this.invoking = invoking
// for conflict resolution
this.depSources = {}
// virtual file tree
this.files = files
this.fileMiddlewares = []
this.postProcessFilesCbs = []
...
const cliService = plugins.find(p => p.id === '@vue/cli-service')
const rootOptions = cliService
? cliService.options
: inferRootOptions(pkg)
// apply generators from plugins
// 每個(gè)插件對(duì)應(yīng)生成一個(gè) GeneratorAPI 實(shí)例,并將實(shí)例 api 傳入插件暴露出來的 generator 函數(shù)
plugins.forEach(({ id, apply, options }) => {
const api = new GeneratorAPI(id, this, options, rootOptions)
apply(api, options, rootOptions, invoking)
})
}
}
和 @vue/cli-service 所使用的插件類似,@vue/cli 插件所提供的 generator 也是向外暴露一個(gè)函數(shù),接收的第一個(gè)參數(shù) api,然后通過該 api 提供的方法去完成應(yīng)用的拓展工作。
開發(fā)者利用這個(gè) api 實(shí)例去完成項(xiàng)目應(yīng)用的拓展工作,這個(gè) api 實(shí)例提供了:
拓展 package.json 配置方法( api.extendPackage )
利用 ejs 渲染模板文件的方法( api.render )
內(nèi)存中保存的文件字符串全部被寫入文件后的回調(diào)函數(shù)( api.onCreateComplete )
向文件當(dāng)中注入 import 語法的方法( api.injectImports )
...
例如 @vue/cli-plugin-eslint 插件的 generator 方法主要是完成了:vue-cli-service cli lint 服務(wù)命令的添加、相關(guān) lint 標(biāo)準(zhǔn)庫(kù)的依賴添加等工作:
module.exports = (api, { config, lintOn = [] }, _, invoking) => {
if (typeof lintOn === 'string') {
lintOn = lintOn.split(',')
}
const eslintConfig = require('./eslintOptions').config(api)
const pkg = {
scripts: {
lint: 'vue-cli-service lint'
},
eslintConfig,
devDependencies: {}
}
if (config === 'airbnb') {
eslintConfig.extends.push('@vue/airbnb')
Object.assign(pkg.devDependencies, {
'@vue/eslint-config-airbnb': '^3.0.0-rc.10'
})
} else if (config === 'standard') {
eslintConfig.extends.push('@vue/standard')
Object.assign(pkg.devDependencies, {
'@vue/eslint-config-standard': '^3.0.0-rc.10'
})
} else if (config === 'prettier') {
eslintConfig.extends.push('@vue/prettier')
Object.assign(pkg.devDependencies, {
'@vue/eslint-config-prettier': '^3.0.0-rc.10'
})
} else {
// default
eslintConfig.extends.push('eslint:recommended')
}
...
api.extendPackage(pkg)
...
// lint & fix after create to ensure files adhere to chosen config
if (config && config !== 'base') {
api.onCreateComplete(() => {
require('./lint')({ silent: true }, api)
})
}
}
以上介紹了 @vue/cli 和插件系統(tǒng)相關(guān)的幾個(gè)核心的模塊,即:
add.js 提供了插件下載的 cli 命令服務(wù)和安裝的功能;
invoke.js 完成插件所提供的 generator 方法的加載和執(zhí)行,同時(shí)將項(xiàng)目當(dāng)中的文件轉(zhuǎn)化為字符串緩存到內(nèi)存當(dāng)中;
Generator.js 和插件進(jìn)行橋接,@vue/cli 每次 add 一個(gè)插件時(shí),都會(huì)實(shí)例化一個(gè) Generator 實(shí)例與之對(duì)應(yīng);
GeneratorAPI.js 和插件一一對(duì)應(yīng),是 @vue/cli 暴露給插件的 api 對(duì)象,提供了很多項(xiàng)目應(yīng)用的拓展工作。
總結(jié)
以上是對(duì) Vue-cli@3.0 的插件系統(tǒng)當(dāng)中兩個(gè)主要部分:@vue/cli 和 @vue/cli-service 簡(jiǎn)析。
- @vue/cli 提供 vue cli 命令,負(fù)責(zé)偏好設(shè)置,生成模板、安裝插件依賴的工作,例如 vue create <projectName> 、 vue add <pluginName>
- @vue/cli-service 作為 @vue/cli 整個(gè)插件系統(tǒng)當(dāng)中的內(nèi)部核心插件,提供了 webpack 配置更新,本地開發(fā)構(gòu)建服務(wù)
前者主要完成了對(duì)于插件的依賴管理,項(xiàng)目模板的拓展等,后者主要是提供了在運(yùn)行時(shí)本地開發(fā)構(gòu)建的服務(wù),同時(shí)后者也作為 @vue/cli 整個(gè)插件系統(tǒng)當(dāng)中的內(nèi)部核心插件而存在。在插件系統(tǒng)內(nèi)部也對(duì)核心功能進(jìn)行了插件化的拆解,例如 @vue/cli-service 內(nèi)置的基礎(chǔ) webpack 配置,npm script 命令等。二者使用約定式的方式向開發(fā)者提供插件的拓展能力,具體到如何開發(fā) @vue/cli 的插件,請(qǐng)參考官方文檔。
以上所述是小編給大家介紹的Vue-cli@3.0 插件系統(tǒng)簡(jiǎn)析,希望對(duì)大家有所幫助,如果大家有任何疑問請(qǐng)給我留言,小編會(huì)及時(shí)回復(fù)大家的。在此也非常感謝大家對(duì)腳本之家網(wǎng)站的支持!
相關(guān)文章
使用Plotly.js在Vue中創(chuàng)建交互式散點(diǎn)圖的示例代碼
Plotly.js是一個(gè)功能強(qiáng)大的JavaScript庫(kù),用于創(chuàng)建交互式數(shù)據(jù)可視化,它支持各種圖表類型,包括散點(diǎn)圖、折線圖和直方圖,在Vue.js應(yīng)用程序中,Plotly.js可用于創(chuàng)建動(dòng)態(tài)且引人入勝的數(shù)據(jù)可視化,本文介紹了使用Plotly.js在Vue中創(chuàng)建交互式散點(diǎn)圖,需要的朋友可以參考下2024-07-07
詳解Vue前端生產(chǎn)環(huán)境發(fā)布配置實(shí)戰(zhàn)篇
這篇文章主要介紹了詳解Vue前端生產(chǎn)環(huán)境發(fā)布配置實(shí)戰(zhàn)篇,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2019-05-05
vue指令之表單控件綁定v-model v-model與v-bind結(jié)合使用
這篇文章主要介紹了vue指令之表單控件綁定v-model v-model與v-bind結(jié)合使用,需要的朋友可以參考下2019-04-04
vue項(xiàng)目build打包后部分樣式錯(cuò)亂的解決
這篇文章主要介紹了vue項(xiàng)目build打包后部分樣式錯(cuò)亂的解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-07-07
element?ui?From表單校驗(yàn)不生效問題解決
表單校驗(yàn)是注冊(cè)環(huán)節(jié)中必不可少的操作,表單校驗(yàn)可以提醒用戶填寫數(shù)據(jù)規(guī)則以確保用戶提交數(shù)據(jù)的有效性,本文主要介紹了element?ui?From表單校驗(yàn)不生效問題解決,具有一定的參考價(jià)值,感興趣的可以了解一下2024-01-01
vue3修改link標(biāo)簽?zāi)J(rèn)icon無效問題詳解
這篇文章主要介紹了vue3修改link標(biāo)簽?zāi)J(rèn)icon無效問題詳解,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2019-10-10
vue插槽slot的簡(jiǎn)單理解與用法實(shí)例分析
這篇文章主要介紹了vue插槽slot的簡(jiǎn)單理解與用法,結(jié)合實(shí)例形式分析了vue插槽slot的功能、原理、相關(guān)使用技巧與操作注意事項(xiàng),需要的朋友可以參考下2020-03-03

