Who is it? What do you call? From where? Where are you going?
Let’s start with the soul test. Our original project was a VUe2 project generated by using Vue CLI. There were no major problems in the project as a whole. However, with the increasing number of modules, the construction speed of Vue CLI based on Webpack became slower and slower, and the development experience was poor.
Without further ado, let’s take a look at the original project structure:
├─.browserslistrc ├─.EditorConfig ├─.env. The following files are the same as ├─.env.development.local ├─.env.Pre ├─.env.Production ├─.env.si ├─.env.Test ├─.Env .Eslintrc.js Flag School ──.NPMRC Flag School ──.nvMRC Flag School ──.PrettierIgnore Flag School ──.Prettierrc.js Flag School ──.Stylelintignore Flag School ── . Stylelintrc. Js ├ ─ ─ the README. Md ├ ─ ─ Babel. Config. Js ├ ─ ─ the build / ├ ─ ─ the config/some project configuration file, ├─ dist/ Build ├─ doc/ Development Document ├─ Jsconfig.json ├─ Mock / ├─ package.json ├─ Postcss.config.js ├─ Public / ├─ ├── ├─ ├─ ├─ ├─ ├── map.lockCopy the code
Good why migrate?
The main purpose of migration is the difference in build speed, but Vite also has some other advantages.
-
Build speed
The most significant advantage of Vite over Vue CLI is probably the build speed. Vite is based on esBuild pre-build dependencies, so it is much faster and the development experience is much better.
Vite’s development environment and production environment builds are currently different because the development environment doesn’t need packaging because it uses native ESM directly, while the production environment packaging uses Rollup
-
Hiding technical details
Well, there’s not much difference between vite and Vue CLI…
-
Play around, try new tools
New tools after all, give it a try, and the Vue community is currently pushing it.
A journey of a thousand miles begins with a single step.
All things are difficult before they are easy. Since the Flag of migration has been set, we can only go ahead with it. Let’s take a brief look at the Vite official document, the document continues the Vue official document concise and clear advantages, basically a simple look on the Vite more understanding, the details are not too deep. After reading the documentation, it is easy to find some differences with the Vue CLI conventions, and for this part of the code must be changed, let’s begin the migration
First, get rid of the historical baggage and remove Vue CLI light pack
First, we remove all Vue CLI-related dependencies and configurations
-
Search for vuE-CLI keywords in package.json dependencies and remove the dependencies.
-
Change the startup script in script to the startup script corresponding to Vite
Will be the original startup script
{ "scripts": { "dev": "vue-cli-service serve --open"."build": "vue-cli-service build --mode development",}}Copy the code
Instead of
{ "scripts": { "dev": "vite"."build": "vite build"."serve": "vite preview",}}Copy the code
Then pick your teeth and make “minor changes.”
We talked about the differences in some of the conventions between Vite and Vue CLI, requiring some minor code changes
The entrance is different
The default Vue CLI entry is SRC /main.js, while the default Vite entry is index.html
Direct quotes from official documents:
Vite treats index.html as part of the source code and module diagram.
So we need to modify the original entrance first
The public/index. HTML
<! DOCTYPEhtml>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<meta name="renderer" content="webkit">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
<meta name="description" content="">
<meta name="Keywords" content="">
<link rel="icon" href="<%= BASE_URL %>favicon.ico">
<title><%= webpackConfig.name %></title>
</head>
<body>
<noscript>
<strong>This page requires browser support (enabled) JavaScript!!</strong>
</noscript>
<div id="app"></div>
<! -- built files will be auto injected -->
</body>
</html>
Copy the code
Move to the root directory index. HTML and modify it
<! DOCTYPEhtml>
<html>
<head>
<meta charset="utf-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
<meta name="renderer" content="webkit" />
<meta
name="viewport"
content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no"
/>
<meta name="description" content="" />
<meta name="Keywords" content="" />
<link rel="icon" href="./favicon.ico" />
<title>XXX</title>
</head>
<body>
<noscript>
<strong>This page requires browser support (enabled) JavaScript!!</strong>
</noscript>
<div id="app"></div>
<! Vite will automatically parse the following js files -->
<script type="module" src="/src/main.js"></script>
</body>
</html>
Copy the code
Different environment variables
Vue CLI environment variable loading and Vite environment variable loading are implemented through Dotenv, so the file naming convention is the same. But there are two differences:
-
Exposed way
-
In the Vue CLI, only NODE_ENV, BASE_URL, and variables starting with VUE_APP_ are exposed.
-
In Vite conventions can access MODE (run MODE development | production), BASE_URL, PROD (whether running in a production environment), DEV (whether in the development environment), and begin with VITE_ environment variable.
-
-
access
-
Vue CLI is accessed through process.env
-
Vite is accessed through import.meta.env
-
Because of these two differences, we need to:
- Originally to
VUE_APP_
Environment variables that start with are replaced withVITE_
At the beginning. Or you can modify the configuration filevite.config.js
的 envPrefixTo directly set toVUE_APP_
You do not need to change the name of the original environment variable. (Configuration file creation is covered below) - will
process.env
Unified replacement byimport.meta.env
.
Custom import type extensions (e.g.vue
)
In the Vue CLI, we can import without writing the. Vue extension by default
import App from './App'
Copy the code
In Vite, however, it is not recommended (and actually configurable) to ignore custom extensions because of the impact on IDE and type support. Therefore, complete writing is required
import App from './App.vue'
Copy the code
Non-custom type extensions can be configured using the config item resolve.extensions, which default to [‘.mjs’, ‘.js’, ‘.ts’, ‘.jsx’, ‘.tsx’, ‘.json’]
Come on out, Vite
So far, with basic modifications as well as completion, we’re starting to introduce Vite.
Vite is relatively easy to install, the only thing to note is that different plug-ins are required for different Vue versions.
Install Vite
$ yarn add -D
Copy the code
Install the Vue plug-in of the corresponding version
Vite provides first priority support for Vue:
- Vue 3 single file component support: @vitejs/plugin-vue
- Vue 3 JSX support: @vitejs/plugin-vue-jsx
- Vue 2 supports underfin/ viet-plugin-vue2
Since we are using Vue2, install viet-plugin-vue2
$ yarn add -D vite-plugin-vue2
Copy the code
Create the configuration file vite.config.js in the root directory
import { defineConfig } from 'vite'
const { createVuePlugin } = require('vite-plugin-vue2')
// https://vitejs.dev/config/
export default defineConfig(() => {
return {
base: './',
clearScreen: false,
plugins: [createVuePlugin()],
build: {
target: 'es2015',
},
}
})
Copy the code
End scatter flower!!
If there are no other special requirements for your project, you should basically be running by now. However, our project is not so lucky, the journey has just begun…
Filling holes
Enable the JSX
Vite does not enable JSX by default. If you want to enable JSX support, you need to:
-
When instantiating the Vue2 plug-in, pass {JSX: true} to turn on JSX support.
export default defineConfig(() = > { return { plugins: [createVuePlugin({jsx:true})].// Introduce the Vue2 plug-in and enable JSX support}})Copy the code
-
Add JSX identifiers to the components used
<script lang="jsx"> export default { render(){ return <div>JSX Render</div> } } </script> Copy the code
-
If it is a JS file with JSX syntax, you need to change the suffix to.jsx.
alias
In the original project we defined the resolve. Alias feature using Webpack in vue.config.js:
{
"configureWebpack":{
resolve: {
alias: {
'@': resolve('src'),
'@api': resolve('src/api'),
'@components': resolve('src/components'),
'@containers': resolve('src/containers'),
'@services': resolve('src/services'),
'@styles': resolve('src/styles'),
'@utils': resolve('src/utils'),
'@@containers': resolve(
'node_modules/web-lib/packages/web-lib-containers/lib'
),
'@@components': resolve(
'node_modules/web-lib/packages/web-lib-components/lib'
),
'@@services': resolve(
'node_modules/web-lib/packages/web-lib-services/lib'
),
'@@utils': resolve('node_modules/web-lib/packages/web-lib-utils/lib'),
'@@styles': resolve('node_modules/web-lib/packages/web-lib-styles/lib'),
},
},
}
}
Copy the code
Looking at the configuration documentation for Vite, you can see that there is built-in support for Aliases, albeit via @rollup/plugin-alias, but fortunately the configuration is pretty much the same.
Therefore, add the resolve.alias to the configuration object returned in viet.config. js and copy the original configuration object.
resolve: {
alias: {
The '@': resolve('src'),
'@api': resolve('src/api'),
'@components': resolve('src/components'),
'@containers': resolve('src/containers'),
'@services': resolve('src/services'),
'@styles': resolve('src/styles'),
'@utils': resolve('src/utils'),
'@@containers': resolve(
'node_modules/web-lib/packages/web-lib-containers/lib'
),
'@@components': resolve(
'node_modules/web-lib/packages/web-lib-components/lib'
),
'@@services': resolve(
'node_modules/web-lib/packages/web-lib-services/lib'
),
'@@utils': resolve('node_modules/web-lib/packages/web-lib-utils/lib'),
'@@styles': resolve('node_modules/web-lib/packages/web-lib-styles/lib'),}}Copy the code
Node internal modules cannot be used in client code
Module compatibility is not handled in Vite, so some Node built-in modules will not be found in client code. If it is needed, it needs to be replaced with the corresponding browser-compatible implementation.
For example, the Path module can be replaced with the corresponding browser-compatible implementation of Path-Browserify.
The original code uses path.join to concatenate paths:
import path from 'path'
export function genPath(. paths) {
returnpath.join(... paths) }Copy the code
In Vite, you need to modify path-Browserify to be browser-compatible
First install Path-Browserify
$ yarn add path-browserify
Copy the code
Then replace the reference directly
import path from 'path-browserify'
export function genPath(. paths) {
returnpath.join(... paths) }Copy the code
Global CSS variables
In our original project, the configuration in vue.config.js was implemented by introducing sas-Resources-loader.
chainWebpack(config) {
// Import global SASS resources
const oneOfsMap = config.module.rule('scss').oneOfs.store
oneOfsMap.forEach(item= > {
item
.use('sass-resources-loader')
.loader('sass-resources-loader')
.options({
resources: [
'./node_modules/web-lib/packages/web-lib-styles/src/variables/index.scss'.'./src/styles/variables.scss',
],
})
.end()
})
}
Copy the code
In Vite we can do this with css.preprocessorOptions.
css: {
preprocessorOptions: {
scss: {
additionalData: `@import './node_modules/web-lib/packages/web-lib-styles/src/variables/index.scss'; `,,}}}Copy the code
If you are careful, you may notice that we have introduced two CSS files into the original code. After the migration, we only introduced one CSS file. Reason is that in the. / SRC/styles/variables. We use the following syntax in SCSS js variables was derived
// export {bg: $bg; text_color: $text_color; header_height: $header_height; sidebar_width: $sidebar_width; header_bg: $header_bg; logo_bg: $logo_bg; menu_bg: $menu_bg; menu_text_color: $menu_text_color; menu_active_text_color: $menu_active_text_color; menu_hover: $menu_hover; }Copy the code
AdditionalData will appear in Vite if additionalData is configured:
Error: This file is already being loaded.
╷
2 │ @import './src/styles/variables.scss'; │ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ╵ SRC/styles/variables. The SCSS root: in the stylesheetCopy the code
Refer to the following ISSUE:
- Github.com/vitejs/vite…
- Github.com/nuxt/vite/i…
At present, our solution is not to introduce, and then manually introduce where used to avoid this problem, there is no particularly good solution for the moment.
Dynamically import image resources such as PNG
In Vue CLI projects, we often need to dynamically identify static resources to import through runtime variables
const iconSrc = require(`./images/${iconName}.png`)
Copy the code
The above code uses webpack’s url-loader or url-loader and will be handled automatically
Vite handles dynamic import of image resources in the following ways:
-
Import with import.meta.globEager glob
This approach will fully introduce matching images
const images = import.meta.globEager('./images/*.png') // All matching images will be imported directly const iconSrc = iamges[`./images/${iconName}.png`].default if(iamges[`./images/${iconName}.png`]) {// TODO }else{ // TODO } Copy the code
-
Import using new URL(URL, import.meta. URL)
This way you can’t tell if the image doesn’t exist at the code level
const iconSrc = new URL(`./images/${iconName}.png`.import.meta.url) // A URL instance will be returned Copy the code
SVG Sprite figure
In the original project we used webPack’s SVG-Sprite-loader plug-in to implement SVG Sprite diagrams.
chainWebpack(config) {
// set svg-sprite-loader
config.module
.rule('svg')
.exclude.add(resolve('src/icons'))
.end()
config.module
.rule('icons')
.test(/\.svg$/)
.include.add(resolve('src/icons'))
.end()
.use('svg-sprite-loader')
.loader('svg-sprite-loader')
.options({
symbolId: 'icon-[name]',
})
.end()
}
Copy the code
Thanks to the community, in Vite we can use the plugin ** vite-plugin-SVG-icons ** to replace them to achieve the same functionality. The usage mode is basically the same. You can check the corresponding documents for specific usage.
Note: SVG-sprite-loader requires manual import of SVG files, whereas viet-plugin-SVG-icons are automatically imported.
Introduce plug-ins in the Plugins option of the Vite configuration
import viteSvgIcons from 'vite-plugin-svg-icons'
export default defineConfig(({ mode }) = > {
return {
viteSvgIcons({
iconDirs: [resolve('src/icons/svg')].symbolId: 'icon-[name]',})}}Copy the code
Introduced in main.js
import 'virtual:svg-icons-register'
Copy the code
Glob import
If we used webpack’s require.context syntax in old projects, we would get an error in Vite. Instead, Vite provides import.meta.glob and import.meta.globEager
For example, modules were introduced dynamically in SRC /store/index.js
// Get the module file
const getModuleFiles = () = > require.context('./modules'.true./\.js$/)
// Get the module object list
const getModules = () = > {
const storeFiles = getModuleFiles()
const modules = storeFiles.keys().reduce((modules, modulePath) = > {
// set './app.js' => 'app'
const moduleName = modulePath.replace(/^\.\/(.*)\.\w+$/.'$1')
const value = storeFiles(modulePath)
modules[moduleName] = value.default
return modules
}, {})
return modules
}
const modules = getModules()
Copy the code
Change to Vite
const files = import.meta.globEager('./modules/*.js')
const modules = Object.keys(files).reduce((pre, path) = > {
pre[path.replace(/(\.\/modules\/|\.js)/g.' ')] = files[path].default
return pre
}, {})
Copy the code
Hot update
In the original SRC /store/index.js we enabled store hot update in the following way
If (module.hot) {const modulePaths = getModuleFiles().keys() module.hot.accept(['./getters', ...[modulePaths]], () => {const getters = require('./getters'). Default const modules = getModules() getters, modules, }) }) }Copy the code
It can be removed directly from Vite without additional configuration.
Use environment variables in the Vite configuration file
If you want to use environment variables in a Vite configuration, you can’t get them using import.meta.env because Vite parses the configuration file first and then environment variables. Therefore, manual parsing access is available only through Dotenv.
export default defineConfig(({ mode }) = > {
// Access common environment variables
const { PORT } = require('dotenv').config({ path: `./.env` }).parsed || {}
// Access environment variables based on the running environment
const {
VITE_APP_URI_BUSINESS_SERVICE_BASE,
VITE_APP_URI_FILE_SERVICE_BASE,
VITE_APP_USER_SOCTET_BASE,
} = require('dotenv').config({ path: `./.env.${mode}` }).parsed || {}
return {
server: {port: PORT,
}
}
})
Copy the code
A little dessert, a little engineering optimization
Husky + Lint-staged implementations commit Lint
Husky allows us to define git-hooks that can be used to inject hooks into the Git lifecycle to define our workflow, such as code validation or email notifications. Lint-staged allows us to process only submitted code. Combining the two can trigger a series of actions on newly committed code within the desired Git life cycle. The most common is to validate and format your code before a Git pre-commit.
Installation and use
-
Install the husky
$ yarn add -D husky Copy the code
-
Open the Git hooks
yarn husky install Copy the code
-
Automatically enable Git hooks after adding installation project dependencies
package.json
{ "scripts": { "prepare": "husky install"}}Copy the code
-
Install the lint – staged
$ yarn add -D lint-staged Copy the code
-
Configuration lint – staged
package.json
{ "lint-staged": { "**/*.{vue,js}": [ "prettier --write"."eslint --fix"."git add"]."**/*.{html,vue,css,scss}": [ "prettier --write"."stylelint --allow-empty-input --fix"."git add"]."**/*.{md,json}": [ "prettier --write"."git add"]}}Copy the code
-
Configure hooks to trigger Lint-staged commits
$ npx husky add .husky/pre-commit "npx lint-staged" Copy the code
-
Add hooks to Git
$ git add .husky/pre-commit Copy the code
Git supports hooks
Hook type | The name of the hook | trigger | Hook parameters | May suspend |
---|---|---|---|---|
Submit workflow Hooks | pre-commit | Before you enter the commit information, that is, before you generate the commit object. This is a good time to review code and run tests. | \ | is |
Submit workflow Hooks | prepare-commit-msg | Run after the default information is created before starting the Submit information editor. You can modify the default commit information here. | filepath commitType SHA-1 | is |
Submit workflow Hooks | commit-msg | Occurs after submission information is entered and before the submission is executed. This is where you can check the submission information for specification and modify the submission information. | filepath | is |
Submit workflow Hooks | post-commit | When the entire submission is complete. Generally used for notifications, such as mail notification submissions, etc. (but it is recommended to do this in server-side post-receive hooks). | \ | no |
Email workflow hook | applypatch-msg | After the patch submission information is generated and before the patch application. Can be used to check whether patch submission information is normal. | mergeFilename | is |
Email workflow hook | pre-applypatch | Run after the patch is applied and before the commit object is generated. So, as with pre-COMMIT, it’s a good time to check your code and run your tests. | \ | is |
Email workflow hook | post-applypatch | When the entire submission is complete. As with post-commit, this is a good time to notify. | \ | no |
Other client-side hooks | pre-rebase | Running before the transition. | \ | is |
Other client-side hooks | post-rewrite | Triggered by the command that replaces the submitted record. Such asgit commit --amend |
commandName | no |
Other client-side hooks | post-checkout | After a successful git Checkout run. | commandName | no |
Other client-side hooks | post-merge | After Git Merge runs successfully. | commandName | no |
Other client-side hooks | pre-push | Updated remote reference but not pushed before local commit. | originBranchName HEAD | is |
Complete the map
Below is the migrated directory structure and the complete viet.config.js file
School exercises ──.env. School Exercises ──.env. School Exercises ──.env. School Exercises ──.env . Eslintrc. Js ├ ─ ─. Git / ├ ─ ─ the gitignore ├ ─ ─ the husky / ├ ─ ─ the NPMRC ├ ─ ─ the NVMRC ├ ─ ─ the prettierignore ├ ─ ─ the prettierrc. Js ├ ─ ─ . Stylelintignore ├ ─ ─. Stylelintrc. Js ├ ─ ─ the README. Md ├ ─ ─ _templates / ├ ─ ─ dist / ├ ─ ─ dist., tar, gz ├ ─ ─ doc / ├ ─ ─ index. The HTML ├ ─ ─ ├─ public/ ├─ SRC/Exercises / ├─ vite.config.js ├─ ├.jsconfig.json ├─ package.json ├─ public/ Exercises ├─ SRC/Exercises/Exercises ├─ vite.configCopy the code
import path from 'path'
import { defineConfig } from 'vite'
import viteSvgIcons from 'vite-plugin-svg-icons'
const { createVuePlugin } = require('vite-plugin-vue2')
function resolve(dir) {
return path.join(__dirname, dir)
}
// https://vitejs.dev/config/
export default defineConfig(({ mode }) = > {
const { PORT } = require('dotenv').config({ path: `./.env` }).parsed || {}
const {
VITE_URI_BUSINESS_SERVICE_BASE,
VITE_URI_FILE_SERVICE_BASE,
VITE_USER_SOCTET_BASE,
} = require('dotenv').config({ path: `./.env.${mode}` }).parsed || {}
return {
base: '/'.clearScreen: false.plugins: [
createVuePlugin({ jsx: true }),
viteSvgIcons({
iconDirs: [resolve('src/icons/svg')].symbolId: 'icon-[name]',})],resolve: {
extensions: ['.mjs'.'.js'.'.ts'.'.jsx'.'.tsx'.'.json'].alias: {
The '@': resolve('src'),
'@api': resolve('src/api'),
'@components': resolve('src/components'),
'@containers': resolve('src/containers'),
'@services': resolve('src/services'),
'@styles': resolve('src/styles'),
'@utils': resolve('src/utils'),
'@@containers': resolve(
'node_modules/web-lib/packages/web-lib-containers/lib'
),
'@@components': resolve(
'node_modules/web-lib/packages/web-lib-components/lib'
),
'@@services': resolve(
'node_modules/web-lib/packages/web-lib-services/lib'
),
'@@utils': resolve('node_modules/web-lib/packages/web-lib-utils/lib'),
'@@styles': resolve('node_modules/web-lib/packages/web-lib-styles/lib'),}},css: {
preprocessorOptions: {
scss: {
additionalData: `@import './node_modules/web-lib/packages/web-lib-styles/src/variables/index.scss'; `,}}},server: {
port: PORT,
open: true.proxy: {
'/user': {
target: VITE_URI_BUSINESS_SERVICE_BASE,
secure: false.changeOrigin: true,},'/file': {
target: VITE_URI_BUSINESS_SERVICE_BASE,
secure: false.changeOrigin: true,},'^/group1': {
target: VITE_URI_FILE_SERVICE_BASE,
secure: false.changeOrigin: true,},// User center long link
'^/wsUser': {
target: VITE_USER_SOCTET_BASE,
secure: false.changeOrigin: true.ws: true.// The actual address does not have the WSMSG prefix
pathRewrite: {
'^/wsUser': ' ',},// Resolve dev Server down if WS agent is disconnected
onProxyReqWs(proxyReq, req, socket) {
socket.on('error'.err= > {
console.error(err)
// console.error(options)},},},},build: {
target: 'es2015',}}})Copy the code