hello everyone!
The text was born to improve development efficiency and experience practice.
Project background:
- Scaffold: vue-cli3, specifically "@ Vue / cli service": "^ 3.4.1"
- Library: vue2, specifically: "vue": "2.6.12"
- Note: no typescript, not ssr
Pain point: with the passage of time and the continuous iteration of business, there are more and more dependencies, functions and codes. The local start-up of the project is relatively slow, and the startup and update are relatively slow.
Improvement goal: retain the original webpack and support vite. And minimize changes and maintenance costs.
consider:
- The vite production environment uses rollup, which is more suitable for packaging libraries.
- vite packaging did not improve the efficiency much.
- Keep the original webpack mode to ensure the stability and security of production as much as possible.
practice
It mainly deals with three aspects:
- The configuration file shall be based on Vue config. JS add vite config. js
- Entry file index How HTML supports vite
- Configure vite startup command
May need
- Route lazy loading, vite needs special handling
- Solve the introduction and mixing of commonjs and esModule
Add vite config. js
Create a vite. Net in the root directory of the project config. js
Install required dependencies
npm i vite vite-plugin-vue2 -D
According to Vue config. JS in vite config. Add corresponding configuration in JS
// If you change the logic of the file, please change Vue config. js import path from 'path' import fs from 'fs' import { defineConfig } from 'vite' import config from './config' import { createVuePlugin } from 'vite-plugin-vue2' import { injectHtml } from 'vite-plugin-html' const resolve = dir => path.join(__dirname, dir) const alias = { vue: 'vue/dist/vue.esm.js', '@': resolve('src'), } const publicPath = '/' const mode = 'development' // https://vitejs.dev/config/ export default defineConfig({ base: publicPath, plugins: [ createVuePlugin(), ], // These variables are injected into the project. If there are variables starting with process, they need to be injected here. vite will not inject process related variables and values by default define: { 'process.env.NODE_ENV': JSON.stringify(mode), 'process.env.publicPath': JSON.stringify(publicPath), }, resolve: { // Configure alias alias, // The list of extensions you want to omit during import must be added vue, because vite does not support omission by default vue suffix extensions: ['.mjs', '.js', '.ts', '.jsx', '.tsx', '.json', '.vue'] }, server: { // Allow cross domain cors: true, proxy: { // Vue can be copied directly config. Corresponding proxy in JS } } })
sass correlation
Special description of configuration: if sass preprocessor is useful in the project, and variables are used.
Installation required sass@1.32.13 , you cannot install a version higher than 1.32. This problem exists with the higher version Sass reports an error: Using / for division is deprecated
npm i sass@1.32.13 -D
export default defineConfig({ ... css: { // Pass options to sass preprocessorOptions: { scss: { charset: false, // Only the latest version of sass is supported additionalData: `@import "src/sass/common/var.scss";`, } }, }, ... })
Note: if the production environment is packaged with vite, use sass@1.32.13 Will encounter this problem There is a warning in vite2 packaging, "@ charset" must be the first. How to eliminate it? , but sass@1.32.13 The configuration in the document is not supported. So this can be regarded as a reason why vite is not used in production environment.
You also need to globally replace / deep / with:: v-deep
Entry file index How HTML supports vite
Due to vite's project requirements, index The HTML file is in the root directory and has an entry file configuration.
So index Move HTML from the public directory to the root directory. And join
<% if (typeof isVite === 'boolean' && isVite) { %> <script type="module" src="/src/main.js"></script> <% } %>
This configuration is to enable both webpack and vite modes to support and share a file
And in order to The isVite variable is injected into the HTML file, which needs to be installed
npm i vite-plugin-html -D
Need to be in vite config. JS
... import { injectHtml } from 'vite-plugin-html' export default defineConfig({ ... plugins: [ ... injectHtml({ data: { title: 'vite-plugin-html-example', isVite: true }, }), ], define: { 'process.env.isVite': JSON.stringify(true) }, ... })
Configure vite startup command
Finally, in package Add script in JSON
"scripts": { "vite-start": "vite" }
Route lazy loading, vite needs special handling
vue implements lazy loading of routes in this way
const Home = import(/* webpackChunkName: "[home]" */ `@/page/home.vue`) const routes = [ { path: `/home`, name: 'home', component: Home }, ]
But vite doesn't support it. It can be solved
const modules = import.meta.glob('@/page/*.vue') const Home = modules['@/page/home.vue']
const modules = import.meta.glob('@/page/*.vue') // vite generates code const modules = { '@/page/home.vue': () => import('@/page/home.vue'), '@/page/page1.vue': () => import('@/page/page1.vue'), '@/page/page2.vue': () => import('@/page/page2.vue'), ... }
reference resources: Vite glob import
So you can encapsulate:
function loadPage (view) { if (process.env.isVite) { const modules = import.meta.glob('../pages/*.vue') return modules[`../pages/${view}.vue`] } return () => import(/* webpackChunkName: "[request]" */ `@/pages/${view}`) } // use: const routes = [ { path: `/home`, name: 'home', component: loadPage('home'), }, { path: `/404`, name: '404', component: loadPage('404'), }, ]
But webpack does not support import.meta , it needs to be handled by loader. Solution: create a local file webpack import meta loader js.
// Source: https://github.com/KmjKoishi/webpack-import-meta-loader-fixed // This is a fix to @ open WC / webpack import meta loader // Mainly repair when this The rootcontext does not exist. Does not exist when building the production environment /* eslint-disable */ // @ts-nocheck const path = require('path'); function toBrowserPath(filePath, _path = path) { return filePath.replace(new RegExp(_path.sep === '\\' ? '\\\\' : _path.sep, 'g'), '/'); }; const regex = /import\.meta/g; /** * Webpack loader to rewrite `import.meta` in modules with url data to the source code file location. * * @example * return import.meta; * // becomes: return ({ url: `${window.location.protocol}//${window.location.host}/relative/path/to/file.js` }); * * return import.meta.url; * // becomes: return ({ url: `${window.location.protocol}//${window.location.host}/relative/path/to/file.js` }).url; */ module.exports = function (source) { const path = require('path'); const relativePath = this.context.substring( this.context.indexOf(this.rootContext) + (this.rootContext && this.rootContext.length >= 0 ? (this.rootContext.length + 1) : 0), this.resource.lastIndexOf(path.sep) + 1, ); const browserPath = toBrowserPath(relativePath); const fileName = this.resource.substring(this.resource.lastIndexOf(path.sep) + 1); let found = false; let rewrittenSource = source.replace(regex, () => { found = true; return `({ url: getAbsoluteUrl('${browserPath}/${fileName}') })`; }); if (found) { return ` function getAbsoluteUrl(relativeUrl) { const publicPath = __webpack_public_path__; let url = ''; if (!publicPath || publicPath.indexOf('://') < 0) { url += window.location.protocol + '//' + window.location.host; } if (publicPath) { url += publicPath; } else { url += '/'; } return url + relativeUrl; } ${rewrittenSource}`; } else { return source; } };
vue.config.js modify configuration:
const resolve = dir => require('path').join(__dirname, dir) module.exports = { ... configureWebpack: { ... module: { rules: { ... { test: /index.js$/, use: [ resolve('webpack-import-meta-loader'), 'babel-loader' ], include: [resolve('src/router')] } } } } ... }
Solve the introduction and mixing of commonjs and esModule
Mixed use
In webpack mode, if there is a mixture of commonjs and esModule in the source code of your src project.
Scheme 1: do not change the source code, in vite config. JS to convert commonjs into esModule
Install npm i cjs2esmodule -D
In vite config. JS plus configuration
export default defineConfig({ plugins: [cjs2esmVitePlugin()] })
If this scheme can make your project run normally. Otherwise, Option II may be required.
Scheme 2: manually change the commonjs syntax in src code to esModule
introduce
If your project has a config JS, by Vue config.js. Then you may need to deal with it.
vue.config.js must be a common JS syntax file to be used, otherwise an error will be reported.
vite.config.js can use either esModule syntax or commonjs syntax. The default is esModule syntax.
If the above is mixed, you adopt scheme 2, and config. Is also used in the src code js. Then you can only put config JS to esModule. Vue config.js is not supported. The scheme adopted is based on config JS automatically generates a config CJS js.
The purpose is to reduce later maintenance costs.
// transformConfig2cjs.js // When running the project, it will be based on config JS auto generated file: config CJS JS to make Vue config JS can be used directly const {transformAsync} = require('@babel/core'); const plugin = require('@babel/plugin-transform-modules-commonjs') const fs = require('fs') const resolve = dir => require('path').join(__dirname, dir) async function transfrom () { const inputPath = resolve('config.js') const input = fs.readFileSync(inputPath, 'utf-8') const {code} = await transformAsync(input, { sourceType: 'module', plugins: [ plugin ] }); fs.writeFileSync(resolve('./config-cjs.js'), code); } transfrom()
Then in Vue config. config.js JS is changed to config CJS
Finally, in package Change the script in JSON and regenerate the latest configuration each time.
"scripts": { "transformConfig2cjs": "node transformConfig2cjs.js", "serve": "npm run transformConfig2cjs && vue-cli-service serve", "build": "npm run transformConfig2cjs && vue-cli-service build", }
summary
Encountered a lot of pits, are grammar related, and finally all 11 solved!
Some other schemes have been tried, but they can't be used. My project doesn't take effect:
After support, the development is really efficient!
I hope this article is helpful to those in need.