It is easy to create a Vue
project through Vue CLI
, but it is not enough for actual projects, so generally we will add some common capabilities on the basis of the business situation, reduce some repetitive operations when creating new projects, in the spirit of learning For the purpose of sharing and sharing, this article will introduce the front-end architecture design of our Vue
project. Of course, some places may not be the best way. After all, everyone's business is different, and what suits you is the best.
In addition to introducing the basic architectural design, this article will also describe how to develop a Vue CLI
plugin and preset
preset.
ps.本文基于Vue2.x版本,node版本16.5.0
Create a basic project
First create a basic project using Vue CLI
:
vue create hello-world
Then select Vue2
option to create, the initial project structure is as follows:
Next, we will build on this foundation.
routing
Routing is essential, install vue-router
:
npm install vue-router
Modify App.vue
file:
<template>
<div id="app">
<router-view />
</div>
</template>
<script>
export default {
name: 'App',
}
</script>
<style>
* {
padding: 0;
margin: 0;
border: 0;
outline: none;
}
html,
body {
width: 100%;
height: 100%;
}
</style>
<style scoped>
#app {
width: 100%;
height: 100%;
display: flex;
}
</style>
Add routing export, simply set the page style.
Next, add pages
directory for placing pages, and move the original content of App.vue
to Hello.vue
:
For routing configuration, we choose to configure based on files, and create a new /src/router.config.js
in the src
directory:
export default [
{
path: '/',
redirect: '/hello',
},
{
name: 'hello',
path: '/hello/',
component: 'Hello',
}
]
The attribute supports all attributes of the vue-router
build option routes , and the component
attribute transmits the component path pages
pages
/src/router.js
import Vue from 'vue'
import Router from 'vue-router'
import routes from './router.config.js'
Vue.use(Router)
const createRoute = (routes) => {
if (!routes) {
return []
}
return routes.map((item) => {
return {
...item,
component: () => {
return import('./pages/' + item.component)
},
children: createRoute(item.children)
}
})
}
const router = new Router({
mode: 'history',
routes: createRoute(routes),
})
export default router
Using factory functions and import
methods to define dynamic components requires recursive processing of child routes. Finally, import routes in main.js
:
// main.js
// ...
import router from './router'// ++
// ...
new Vue({
router,// ++
render: h => h(App),
}).$mount('#app')
menu
Our business basically needs a menu, which is displayed on the left side of the page by default. We have an internal component library, but it is not open source, so this article uses Element
instead. The menu is also configured through files. Create a new /src/nav.config.js
file:
export default [{
title: 'hello',
router: '/hello',
icon: 'el-icon-menu'
}]
Then modify App.vue
file:
<template>
<div id="app">
<el-menu
style="width: 250px; height: 100%"
:router="true"
:default-active="defaultActive"
>
<el-menu-item
v-for="(item, index) in navList"
:key="index"
:index="item.router"
>
<i :class="item.icon"></i>
<span slot="title">{{ item.title }}</span>
</el-menu-item>
</el-menu>
<router-view />
</div>
</template>
<script>
import navList from './nav.config.js'
export default {
name: 'App',
data() {
return {
navList,
}
},
computed: {
defaultActive() {
let path = this.$route.path
// 检查是否有完全匹配的
let fullMatch = navList.find((item) => {
return item.router === path
})
// 没有则检查是否有部分匹配
if (!fullMatch) {
fullMatch = navList.find((item) => {
return new RegExp('^' + item.router + '/').test(path)
})
}
return fullMatch ? fullMatch.router : ''
},
},
}
</script>
The effect is as follows:
Of course, the above is just for the purpose, the actual situation is more complicated, after all, the situation of nested menus is not even considered here.
permission
The granularity of our permissions is relatively large, and we only control the routing level. The specific implementation is to add a code
field to each item in the menu configuration and routing configuration, and then obtain the code
that the current user has permission, and the menu without permission. It is not displayed by default, and access to routes without permission will be redirected to the 403
page.
Get permission data
The permission data is returned with the user information interface, and then stored in vuex
, so first configure vuex
and install:
npm install vuex --save
Added /src/store.js
:
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
export default new Vuex.Store({
state: {
userInfo: null,
},
actions: {
// 请求用户信息
async getUserInfo(ctx) {
let userInfo = {
// ...
code: ['001'] // 用户拥有的权限
}
ctx.commit('setUserInfo', userInfo)
}
},
mutations: {
setUserInfo(state, userInfo) {
state.userInfo = userInfo
}
},
})
First obtain user information in main.js
, and then initialize Vue
:
// ...
import store from './store'
// ...
const initApp = async () => {
await store.dispatch('getUserInfo')
new Vue({
router,
store,
render: h => h(App),
}).$mount('#app')
}
initApp()
menu
Modify nav.config.js
add code
field:
// nav.config.js
export default [{
title: 'hello',
router: '/hello',
icon: 'el-icon-menu'
code: '001',
}]
Then filter out menus without permission in App.vue
:
export default {
name: 'App',
data() {
return {
navList,// --
}
},
computed: {
navList() {// ++
const { userInfo } = this.$store.state
if (!userInfo || !userInfo.code || userInfo.code.length <= 0) return []
return navList.filter((item) => {
return userInfo.code.includes(item.code)
})
}
}
}
This way the menu without permission will not be displayed.
routing
Modify router.config.js
and add code
field:
export default [{
path: '/',
redirect: '/hello',
},
{
name: 'hello',
path: '/hello/',
component: 'Hello',
code: '001',
}
]
code
is a custom field and needs to be saved in the meta
field of the routing record, otherwise it will be lost in the end. Modify createRoute
method:
// router.js
// ...
const createRoute = (routes) => {
// ...
return routes.map((item) => {
return {
...item,
component: () => {
return import('./pages/' + item.component)
},
children: createRoute(item.children),
meta: {// ++
code: item.code
}
}
})
}
// ...
Then you need to intercept the route jump to determine whether you have permission. If you don't have permission, go to the 403
page:
// router.js
// ...
import store from './store'
// ...
router.beforeEach((to, from, next) => {
const userInfo = store.state.userInfo
const code = userInfo && userInfo.code && userInfo.code.length > 0 ? userInfo.code : []
// 去错误页面直接跳转即可,否则会引起死循环
if (/^\/error\//.test(to.path)) {
return next()
}
// 有权限直接跳转
if (code.includes(to.meta.code)) {
next()
} else if (to.meta.code) { // 路由存在,没有权限,跳转到403页面
next({
path: '/error/403'
})
} else { // 没有code则代表是非法路径,跳转到404页面
next({
path: '/error/404'
})
}
})
error
component is not available yet, add it:
// pages/Error.vue
<template>
<div class="container">{{ errorText }}</div>
</template>
<script>
const map = {
403: '无权限',
404: '页面不存在',
}
export default {
name: 'Error',
computed: {
errorText() {
return map[this.$route.params.type] || '未知错误'
},
},
}
</script>
<style scoped>
.container {
width: 100%;
height: 100%;
display: flex;
justify-content: center;
align-items: center;
font-size: 50px;
}
</style>
Next, modify router.config.js
, add a route for the error page, and add a route for testing without permission:
// router.config.js
export default [
// ...
{
name: 'Error',
path: '/error/:type',
component: 'Error',
},
{
name: 'hi',
path: '/hi/',
code: '无权限测试,请输入hi',
component: 'Hello',
}
]
Because this code
user does not exist, so now we open the /hi
route and directly jump to the 403
route:
Bread crumbs
Similar to the menu, breadcrumbs are also required for most pages. The breadcrumbs are divided into two parts, one part is the position in the current menu, and the other part is the path generated in the page operation. Because the path of the first part may change dynamically, it is generally obtained with the user information through the interface, and then stored in vuex
, and modified store.js
:
// ...
async getUserInfo(ctx) {
let userInfo = {
code: ['001'],
breadcrumb: {// 增加面包屑数据
'001': ['你好'],
},
}
ctx.commit('setUserInfo', userInfo)
}
// ...
The second part is configured in router.config.js
:
export default [
//...
{
name: 'hello',
path: '/hello/',
component: 'Hello',
code: '001',
breadcrumb: ['世界'],// ++
}
]
breadcrumb
field is the same as the code
field. It belongs to a custom field, but the data of this field is used by the component. The component needs to obtain the data of this field and then render the breadcrumb menu on the page, so it is possible to save it to the meta
field, but It is more troublesome to obtain in the component, so we can set it to the props
field of the routing record and directly inject it as props
of the component, so that it is much more convenient to use, modify router.js
:
// router.js
// ...
const createRoute = (routes) => {
// ...
return routes.map((item) => {
return {
...item,
component: () => {
return import('./pages/' + item.component)
},
children: createRoute(item.children),
meta: {
code: item.code
},
props: {// ++
breadcrumbObj: {
breadcrumb: item.breadcrumb,
code: item.code
}
}
}
})
}
// ...
In this way, by declaring a breadcrumbObj
attribute in the component, the breadcrumb data can be obtained. You can see that code
is also passed along. This is because the route code
code
Corresponding breadcrumb data, and then merge the two parts. In order to avoid doing this work for each component, we can write it in a global mixin
and modify main.js
:
// ...
Vue.mixin({
props: {
breadcrumbObj: {
type: Object,
default: () => null
}
},
computed: {
breadcrumb() {
if (!this.breadcrumbObj) {
return []
}
let {
code,
breadcrumb
} = this.breadcrumbObj
// 用户接口获取的面包屑数据
let breadcrumbData = this.$store.state.userInfo.breadcrumb
// 当前路由是否存在面包屑数据
let firstBreadcrumb = breadcrumbData && Array.isArray(breadcrumbData[code]) ? breadcrumbData[code] : []
// 合并两部分的面包屑数据
return firstBreadcrumb.concat(breadcrumb || [])
}
}
})
// ...
initApp()
Finally, we render the breadcrumbs in the Hello.vue
component:
<template>
<div class="container">
<el-breadcrumb separator="/">
<el-breadcrumb-item v-for="(item, index) in breadcrumb" :key="index">{{item}}</el-breadcrumb-item>
</el-breadcrumb>
// ...
</div>
</template>
Of course, our breadcrumbs do not need to support clicks. If necessary, you can modify the data structure of the breadcrumbs.
interface request
The interface request uses axios
, but will do some basic configuration, interception requests and responses, because there are still some scenarios that need to use the unconfigured axios
directly, so we create a new instance by default and install it first:
npm install axios
Then create a new /src/api/
directory, and add a httpInstance.js
file in it:
import axios from 'axios'
// 创建一个新实例
const http = axios.create({
timeout: 10000,// 超时时间设为10秒
withCredentials: true,// 跨域请求时是否需要使用凭证,设置为需要
headers: {
'X-Requested-With': 'XMLHttpRequest'// 表明是ajax请求
},
})
export default http
Then add a request interceptor:
// ...
// 请求拦截器
http.interceptors.request.use(function (config) {
// 在发送请求之前做些什么
return config;
}, function (error) {
// 对请求错误做些什么
return Promise.reject(error);
});
// ...
In fact, I didn't do anything, just write it out first, and keep different projects to modify as needed.
Finally add a response interceptor:
// ...
import { Message } from 'element-ui'
// ...
// 响应拦截器
http.interceptors.response.use(
function (response) {
// 对错误进行统一处理
if (response.data.code !== '0') {
// 弹出错误提示
if (!response.config.noMsg && response.data.msg) {
Message.error(response.data.msg)
}
return Promise.reject(response)
} else if (response.data.code === '0' && response.config.successNotify && response.data.msg) {
// 弹出成功提示
Message.success(response.data.msg)
}
return Promise.resolve({
code: response.data.code,
msg: response.data.msg,
data: response.data.data,
})
},
function (error) {
// 登录过期
if (error.status === 403) {
location.reload()
return
}
// 超时提示
if (error.message.indexOf('timeout') > -1) {
Message.error('请求超时,请重试!')
}
return Promise.reject(error)
},
)
// ...
We agree that a successful response (status code 200) has the following structure:
{
code: '0',
msg: 'xxx',
data: xxx
}
code
is not 0
even if the status code is 200
, it means that the request is wrong, then an error message prompt box will pop up. If you don’t want the prompt box to pop up automatically for a request, you can also disable it. Just add the configuration parameter noMsg: true
to the request, for example:
axios.get('/xxx', {
noMsg: true
})
If the request is successful, no prompt will be displayed by default. If necessary, you can set the configuration parameter successNotify: true
.
There are only two types of errors with a status code other than [200,300)
, login expiration and request timeout, and other situations can be modified according to the project.
multi-language
Multi-language is implemented using vue-i18n , first install:
npm install vue-i18n@8
The vue-i18n
version of 9.x
supports Vue3
, so we use the 8.x
version.
Then create a directory /src/i18n/
, and create a new index.js
file in the directory to create an instance of i18n
:
import Vue from 'vue'
import VueI18n from 'vue-i18n'
Vue.use(VueI18n)
const i18n = new VueI18n()
export default i18n
Don't do anything except create an instance, don't worry, let's go step by step.
Our general idea is that the multi-language source data is under /src/i18n/
, and then compiled into a json
file and placed in the /public/i18n/
directory of the project. The initial default language of the page is also returned together with the user information interface, and the page uses the ajax
request according to the default language type. The corresponding json
file in the public
directory is set dynamically by calling the method of VueI18n
.
The purpose of this is first to facilitate the modification of the default language of the page, and secondly, the multi-language files are not packaged with the project code, which reduces the packaging time, requests on demand, and reduces unnecessary resource requests.
Next, we create the Chinese and English data of the new page, and the directory structure is as follows:
For example, the content of the Chinese hello.json
file is as follows (ignoring the author's low-level translation~):
Import the index.js
file and the ElementUI
language file into the hello.json
file, and merge and export:
import hello from './hello.json'
import elementLocale from 'element-ui/lib/locale/lang/zh-CN'
export default {
hello,
...elementLocale
}
Why is it ...elementLocale
, because the multilingual data structure passed to Vue-i18n
is like this:
We take the entire export object of index.js
as the multilingual data of vue-i18n
, and the multilingual file of ElementUI
is like this:
So we need to combine the properties of this object and the properties of hello
into one object.
Next, we need to write the exported data to a json
file and output it to the public
directory. This can be done directly by writing a js
script file, but in order to separate it from the source code of the project, we write a npm
package.
Create an npm toolkit
We create a package directory under the project's level and initialize it with npm init
:
The reason for naming it -tool
is that there may be similar requirements for compiling multiple languages in the future, so a common name is adopted to facilitate adding other functions later.
The command line interactive tool uses Commander.js , install:
npm install commander
Then create a new entry file index.js
:
#!/usr/bin/env node
const {
program
} = require('commander');
// 编译多语言文件
const buildI18n = () => {
console.log('编译多语言文件');
}
program
.command('i18n') // 添加i18n命令
.action(buildI18n)
program.parse(process.argv);
Because our package is to be used as a command-line tool, the first line of the file needs to specify that the script's interpreter is node
, and then use commander
configure a i18n
command to compile multilingual files. If you want to add other new functions later Just add the command, and the executable file is there. We also need to add a bin
field to the package.json
file of the package to indicate that there is an executable file in our package, and let npm
create a symbolic link for us by the way when installing the package. , which maps commands to files.
// hello-tool/package.json
{
"bin": {
"hello": "./index.js"
}
}
Because our package has not been released to npm
, it is directly linked to the project for use, first execute it in the hello-tool
directory:
npm link
Then go to our hello world
directory and execute:
npm link hello-tool
Now try typing hello i18n
on the command line:
Compile multilingual files
Next, improve the logic of the buildI18n
function, which is mainly divided into three steps:
1. Empty the target directory, which is the /public/i18n
directory
2. Obtain data exported from various multilingual files under /src/i18n
3. Write to the json
file and output to the /public/i18n
directory
code show as below:
const path = require('path')
const fs = require('fs')
// 编译多语言文件
const buildI18n = () => {
// 多语言源目录
let srcDir = path.join(process.cwd(), 'src/i18n')
// 目标目录
let destDir = path.join(process.cwd(), 'public/i18n')
// 1.清空目标目录,clearDir是一个自定义方法,递归遍历目录进行删除
clearDir(destDir)
// 2.获取源多语言导出数据
let data = {}
let langDirs = fs.readdirSync(srcDir)
langDirs.forEach((dir) => {
let dirPath = path.join(srcDir, dir)
// 读取/src/i18n/xxx/index.js文件,获取导出的多语言对象,存储到data对象上
let indexPath = path.join(dirPath, 'index.js')
if (fs.statSync(dirPath).isDirectory() && fs.existsSync(indexPath)) {
// 使用require加载该文件模块,获取导出的数据
data[dir] = require(indexPath)
}
})
// 3.写入到目标目录
Object.keys(data).forEach((lang) => {
// 创建public/i18n目录
if (!fs.existsSync(destDir)) {
fs.mkdirSync(destDir)
}
let dirPath = path.join(destDir, lang)
let filePath = path.join(dirPath, 'index.json')
// 创建多语言目录
if (!fs.existsSync(dirPath)) {
fs.mkdirSync(dirPath)
}
// 创建json文件
fs.writeFileSync(filePath, JSON.stringify(data[lang], null, 4))
})
console.log('多语言编译完成');
}
The code is very simple, next we run the command:
An error is reported, indicating that import
cannot be used outside the module. In fact, the new version of nodejs
already supports the module syntax of ES6
. You can change the file suffix to .mjs
, or add the type=module
field to the package.json
file. Is there an easier way to do it? Change multilingual files to commonjs
module syntax? It is also possible, but it is not very elegant, but fortunately, babel
provides a @babel/register package, which can bind node
to the babel
require
module, which can then be compiled at runtime, that is, when require('/src/i18n/xxx/index.js')
will be 0622f4. Compile by babel
first. Of course, there is no import
statement after compilation. Install it first:
npm install @babel/core @babel/register @babel/preset-env
Then create a new babel
configuration file:
// hello-tool/babel.config.js
module.exports = {
'presets': ['@babel/preset-env']
}
Finally used in the hello-tool/index.js
file:
const path = require('path')
const {
program
} = require('commander');
const fs = require('fs')
require("@babel/register")({
configFile: path.resolve(__dirname, './babel.config.js'),
})
// ...
Next run the command again:
It can be seen that the compilation is completed, and the file is also output to the public
directory, but there is a default
attribute in the json
file. Obviously we do not need this layer, so when require('i18n/xxx/index.js')
, we store the exported default
object and modify hello-tool/index.js
:
const buildI18n = () => {
// ...
langDirs.forEach((dir) => {
let dirPath = path.join(srcDir, dir)
let indexPath = path.join(dirPath, 'index.js')
if (fs.statSync(dirPath).isDirectory() && fs.existsSync(indexPath)) {
data[dir] = require(indexPath).default// ++
}
})
// ...
}
The effect is as follows:
Use multilingual files
First, modify the return data of the user interface and add the default language field:
// /src/store.js
// ...
async getUserInfo(ctx) {
let userInfo = {
// ...
language: 'zh_CN'// 默认语言
}
ctx.commit('setUserInfo', userInfo)
}
// ...
Then request and set multi-language immediately after obtaining user information in main.js
:
// /src/main.js
import { setLanguage } from './utils'// ++
import i18n from './i18n'// ++
const initApp = async () => {
await store.dispatch('getUserInfo')
await setLanguage(store.state.userInfo.language)// ++
new Vue({
i18n,// ++
router,
store,
render: h => h(App),
}).$mount('#app')
}
setLanguage
method requests multilingual files and switches:
// /src/utils/index.js
import axios from 'axios'
import i18n from '../i18n'
// 请求并设置多语言数据
const languageCache = {}
export const setLanguage = async (language = 'zh_CN') => {
let languageData = null
// 有缓存,使用缓存数据
if (languageCache[language]) {
languageData = languageCache[language]
} else {
// 没有缓存,发起请求
const {
data
} = await axios.get(`/i18n/${language}/index.json`)
languageCache[language] = languageData = data
}
// 设置语言环境的 locale 信息
i18n.setLocaleMessage(language, languageData)
// 修改语言环境
i18n.locale = language
}
Then replace the information displayed in each component with the form of $t('xxx')
. Of course, the menu and routing need to be modified accordingly. The effect is as follows:
It can be found that the language of the ElementUI
component has not changed, of course, because we have not dealt with it yet, the modification is very simple, ElementUI
supports the processing method of custom i18n
:
// /src/main.js
// ...
Vue.use(ElementUI, {
i18n: (key, value) => i18n.t(key, value)
})
// ...
Generate initial polyglot files via CLI plugin
Finally, there is another question, what should I do if there are no multi-language files when the project is initialized? Is it necessary to manually run the command to compile the multi-language after the project is created? There are several workarounds:
1. In the end, a project scaffolding will generally be provided, so we can directly add the initial multilingual file to the default template;
2. Compile the multilingual file first when starting the service and packaging, like this:
"scripts": {
"serve": "hello i18n && vue-cli-service serve",
"build": "hello i18n && vue-cli-service build"
}
3. Develop a Vue CLI
plugin to help us automatically run a multi-language compilation command when the project is created;
Next, simply implement the third method, also create a plugin directory at the same level of the project, and create the corresponding file (note the naming convention of plugins):
According to the plug-in development specification, index.js
is the entry file of the Service
plug-in. The Service
plug-in can modify the webpack
configuration, create a new vue-cli service
command or modify an existing command. We don’t need it. Our logic is in generator.js
. The scene is called:
1. During project creation, when the CLI
plugin was installed as part of the project creation preset
2. Called when the plugin is installed separately through vue add
or vue invoke
when the project is created
What we need is to automatically run the multi-language compilation command for us when the project is created or when the plugin is installed. generator.js
needs to export a function, the content is as follows:
const {
exec
} = require('child_process');
module.exports = (api) => {
// 为了方便在项目里看到编译多语言的命令,我们把hello i18n添加到项目的package.json文件里,修改package.json文件可以使用提供的api.extendPackage方法
api.extendPackage({
scripts: {
buildI18n: 'hello i18n'
}
})
// 该钩子会在文件写入硬盘后调用
api.afterInvoke(() => {
// 获取项目的完整路径
let targetDir = api.generator.context
// 进入项目文件夹,然后运行命令
exec(`cd ${targetDir} && npm run buildI18n`, (error, stdout, stderr) => {
if (error) {
console.error(error);
return;
}
console.log(stdout);
console.error(stderr);
});
})
}
We run the compile command in the afterInvoke
hook, because if it runs too early, the dependencies may not be installed. In addition, we also get the full path of the project. This is because when the plugin is configured through preset
, the plugin may not be called in the actual project file. folder, for example, we create the b
project under the a
folder with this command:
vue create b
When the plugin is called, it is in the a
directory. Obviously, the hello-i18n
package is installed in the b
directory, so we must first enter the actual directory of the project and then run the compile command.
Next test, first install the plugin under the project:
npm install --save-dev file:完整路径\vue-cli-plugin-i18n
Then invoke the plugin's generator with the following command:
vue invoke vue-cli-plugin-i18n
The effect is as follows:
It can be seen that the compilation command has been injected into the package.json
file of the project, and the command is also automatically executed to generate a multilingual file.
Mock data
It is recommended to use Mock for Mock
data. It is very simple to use. Create a new mock
data file:
Then import it in /api/index.js
:
As simple as that, the request can be intercepted:
normalize
Regarding standardized configuration, such as code style check, git
submission specification, etc., the author has written an article on component library construction before, one of the subsections describes the configuration process in detail, you can move: [10,000-character long article] Configure from zero A vue component library - canonical configuration subsection .
other
request proxy
It is inevitable to encounter cross-domain problems when requesting the local development and testing interface. You can configure the proxy options of webpack-dev-server
and create a new vue.config.js
file:
module.exports = {
devServer: {
proxy: {
'^/api/': {
target: 'http://xxx:xxx',
changeOrigin: true
}
}
}
}
Compile dependencies in node_modules
By default, babel-loader
will ignore all files in node_modules
, but some dependencies may not be compiled. For example, some packages we wrote ourselves are not compiled in order to save trouble, then if the latest syntax is used, on low-version browsers It may not work, so you also need to compile them when packaging. To explicitly translate a dependency through Babel
, you can configure it in this transpileDependencies
option and modify vue.config.js
:
module.exports = {
// ...
transpileDependencies: ['your-package-name']
}
environment variable
If you need environment variables, you can create a new .env
file in the project root directory. It should be noted that if you want to render a template file starting with .
through a plug-in, use _
to replace the dot, that is, _env
, which will eventually be rendered as a file starting with .
.
scaffold
When we have designed a project structure, it must be used as a template to quickly create a project. Generally, a scaffolding tool will be created to generate it, but Vue CLI
provides the ability of preset
(preset). The so-called preset
refers to a project that contains The JSON
object of the pre-defined options and plugins required for the new project, so we can create a CLI
plugin to create a template, then create a preset
, and then configure this plugin into preset
, so that we use our custom when creating a project using the vue create
command preset
.
Create a CLI plugin that generates templates
The new plugin directory is as follows:
It can be seen that this time we created a generator
directory, because we need to render the template, and the template file will be placed in this directory, create a new template
directory, and then copy the project structure we configured earlier into it (excluding package .json):
Now let's complete the contents of the /generator/index.js
file:
1. Because package.json
is not included, we need to modify the default package.json
of the vue
project and add what we need, using the api.extendPackage
method mentioned earlier:
// generator/index.js
module.exports = (api) => {
// 扩展package.json
api.extendPackage({
"dependencies": {
"axios": "^0.25.0",
"element-ui": "^2.15.6",
"vue-i18n": "^8.27.0",
"vue-router": "^3.5.3",
"vuex": "^3.6.2"
},
"devDependencies": {
"mockjs": "^1.1.0",
"sass": "^1.49.7",
"sass-loader": "^8.0.2",
"hello-tool": "^1.0.0"// 注意这里,不要忘记把我们的工具包加上
}
})
}
Added some additional dependencies, including hello-tool
that we developed earlier.
2. Render the template
module.exports = (api) => {
// ...
api.render('./template')
}
render
method will render all files in the template
directory.
Create a custom preset
The plugins are all there. Finally, let's create a custom preset
, create a new preset.json
file, and configure the template
plugin and i18n
plugin we wrote earlier:
{
"plugins": {
"vue-cli-plugin-template": {
"version": "^1.0.0"
},
"vue-cli-plugin-i18n": {
"version": "^1.0.0"
}
}
}
At the same time, in order to test this preset
, we create an empty directory:
Then enter the test-preset
directory and specify our preset
path when running the vue create
command:
vue create --preset ../preset.json my-project
The effect is as follows:
Use preset remotely
preset
there is no problem with the local test, you can upload it to the warehouse, and then you can use it for others. For example, the author uploaded it to this warehouse: https://github.com/wanglin2/Vue_project_design , then you can use it like this:
vue create --preset wanglin2/Vue_project_design project-name
Summarize
If there is something wrong or better, see you in the comment section~
My blog will be synced to Tencent Cloud + Community, and I invite everyone to join: https://cloud.tencent.com/developer/support-plan?invite_code=cdvx38zb3864
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。