By: Cat driver

Most of the company’s projects are 2B type middle and background PC projects, and the business is very large. After years of maintenance, using WebPack Dev becomes very slow.

Because my recent work is mainly responsible for some front-end infrastructure construction, I have certain contact with products of various business lines. One project on my computer, NPM Run Dev, took 6 minutes. It was hard to be happy. In addition, new projects in the company have used Vite as a construction tool, so I chose an old project (VUe-CLI) I am familiar with for vite migration. And the process was recorded.

Project background

First, a brief description of the project:

  • Content: 2B PC side in the background management system, is a double entry application (login page one entrance, management system one entrance).

  • Build tool: vue-cli-service

  • Technology stack: VUE 2.6 + Axios + Vuex + VUE-Router + SF-VUE-Componet (company internal component library) + ElementUI

  • Maintenance time: 3 + years (actually much more than 5 years, this project is based on a project that has been maintained for a long time, and the earliest commit record is 3 years ago, but the base project has been developed for a long time)

  • Number of modules: 6000 (number of modules shown in NPM Run serve)

  • Business code volume: 19.3W lines

  • Compatibility requirements: Internet Explorer 11

The size of the project is only medium in the company, so I chose this project because I am familiar with this business. Keep your mouth shut and start fucking.

Thought analysis

The goal here is not to replace vue-CLI with vite directly, but to keep both vite and vue-CLI. Aside from the fact that the development experience is largely dev focused, there are two other reasons:

  • The product needs to be compatibleIE11It is not clearviteHow many holes are there in this regard. Step too far, maybe grab your crotch.
  • rollupPackaging is, after all, the samewebpackIt’s still different, and the business is more complex. It is also difficult to ensure that all businesses can be tested in place after the changes are made.

Both WebPack and Vite are essentially entry files + dependency analysis + module transformation. Vite is processed as it is used (vite also preprocesses third-party libraries), while WebPack is processed first. Real-time processing became possible when browsers naturally supported the ES Module. This is why Vite starts so fast.

So, in theory, migrating from vue-CLI to Vite is possible.

Starting the migration

Step1 analysis vue. Config. Js

First of all, we analyze vue.config.js. It is mainly to sort out some plug-ins, and some special configurations. Then go to the Vite website and community to see if the configuration or plugin matches. The mismatched parts of the solution, migration is estimated to be about the same.

After the analysis is completed, the main two points do not match

// vue.config.js
module.exports = {
    chainWebpack: config= > {
        config.plugins.delete('preload-loginPlatform');
        config.plugins.delete('prefetch-loginPlatform');
        config.plugins.delete('preload-platform');
        config.plugins.delete('prefetch-platform');
    },
    transpileDependencies: ['@sxf/cloudsec-components'.'@sxf/validations'.'@sxf/sf-vue-component']}Copy the code

The configuration in chainWebpack is designed to solve the problem of two entry resources loading each other, i.e. management system resources are preloaded when accessing the login page. Since this is the content that will be used in the production environment after packaging, and this migration does not require the packaged content, it is not dealt with for the time being.

TranspileDependencies allows files in node_modules to be treated with Babel. TranspileDependencies does not need to be treated with Babel because we can use the latest browsers during development.

Cli.vuejs.org/zh/config/#…

Step2 install dependencies and add scripts

In general, you will need the following NPM packages.

  • vite
  • Vite-plugin-vue2: a plug-in for VUe2 supported by Vite (by default, vite does not support vUE)
  • Viet-plugin-eslint (Optional) : A vite esLint plugin used to verify ESLint in real time during Run dev
  • Viet-plugin-legacy (optional) : Handles browser compatibility, not required here.
  • Viet-plugin-mock (optional) : mock data
Yarn add vite vite-plugin-vue2 -d # Only necessary vite and vite vue2 plug-ins are installedCopy the code

Then add package.json to add the scripts command.

// package.json{... ."scirpts": {... ."dev": "vite"}}Copy the code

Step3 compile the vite. Config. js file

Since the original vue.config.js was not particularly complicated, and there are other migration articles on the web that explain how to migrate the WebPack configuration, I’ll just paste the results here.

const { createVuePlugin } = require('vite-plugin-vue2')
const { resolve } = require('path');
const config = require('./config');
module.exports = {
    server: {
        host: '0.0.0.0'.https: true.port: 4433.proxy: {
            '/platform': {
                target: `https://${config.host}`.secure: false.changeOrigin: true.rewrite: (path) = > path.replace(/^\/platform/.' ')},...// }},resolve: {
        alias: {
            The '@': resolve(__dirname, './src'),
            'src': resolve(__dirname, './src'),
            'home': resolve(__dirname, './src/home'),
            'components': resolve(__dirname, './src/components'),
            'assets': resolve(__dirname, './src/assets'),
            'utils': resolve(__dirname, './src/utils'),}},build: { // This is for build, so it can be omitted
      rollupOptions: {
        input: {
          login: resolve(__dirname, './public/login_platform.html'),
          platform: resolve(__dirname, './public/platform.html')}}},plugins: [
        createVuePlugin()
    ]
}
Copy the code

From the above results, there are only three sections to write:

  1. Serve: A port to run at development time, as well as rules for reverse proxy of data requests
  2. Alias: alias
  3. Plugins:vite-plugin-vue2Used to load vUE files.

About Multiple entry

In the introduction of the project, it is said that the project is a multi-entry file. In fact, in the Vite development environment, multiple portals do not need to be configured. After launching the file, access the corresponding HTML resource directly.

About wp2vite

Github.com/tnfe/wp2vit… (A front-end project conversion tool that enables WebPack projects to support Vite)

Wp2vite can generate vite configuration (support vue-CLI) based on your current WebPack configuration. If you don’t want to write viet.config.js by hand, you can use this tool to automatically convert it. I discovered this tool when I was compiling data for this article.

The wP2Vite conversion results are not completely correct, with the following problems.

  • Alias adds some strange aliases (possibly Feature :smile:)
  • The proxy configuration rewrite is incorrect

The above two points, the students in reference to the time to pay attention to the line.

Step 4 Adjust the entry file

Unlike vue-CLI, Vite only uses HTML as an entry file by default, so you need to adjust the HTML content.

Don’t forget to add type=”module” to the script tag

<! -- index.html entry file for main application -->
<! DOCTYPEhtml>
<html>
  <head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">.<link rel="icon" href="<%= BASE_URL %>favicon.ico">
    <title>.</title>
    <! Add this line directly to the previous entry file -->
    + <script type="module" src="/src/main.js"></script>
  </head>
  <body>
    <noscript>
      <strong>We're sorry but project doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
    </noscript>
    <div id="app"></div>
    <! -- built files will be auto injected -->
  </body>
</html>

<! -- login. HTML entry file for the login page -->
<! DOCTYPEhtml>
<html>
  <head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">.<link rel="icon" href="<%= BASE_URL %>favicon.ico">
    <title>.</title>
    <! Add this line directly to the previous entry file -->
    + <script type="module" src="/src/login.js"></script>
  </head>
  <body>
    <noscript>
      <strong>We're sorry but project doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
    </noscript>
    <div id="app"></div>
    <! -- built files will be auto injected -->
  </body>
</html>

Copy the code

step 5 npm run dev

Run NPM run dev directly and you can see that Vite immediately starts the service. Compared to the original time around 70s, this is more than half a point faster, it is like a rocket take off.

Of course, starting a service does not mean migration is complete. Vite handles content more when accessing the corresponding resource. Therefore, you need to visit the corresponding page for verification. The real migration is just beginning.

Endless pit: Stage 1

Problem 1: URIError: URI malformed

Vite after open, visit the login page, https://localhost:4433/static/login_platform.html, an error URIError: URI malformed

This is a decodeURIComponent function execution error.

The URI of the error is /static/%3C%=%20BASE_URL%20%% 3efavicon.ico

The corresponding code is in login.html, where the BASE_URL is the template variable in vue-CLI, which can be changed to /public/favicon.ico (based on your own directory structure).

<link rel="icon" href="<%= BASE_URL %>favicon.ico">= ><link rel="icon" href="/public/favicon.ico">
Copy the code

After the modification is complete, Vite will prompt you to remove the public prefix.

Error 2: Failed to resolve import “XXX”

The vUE file cannot be found. After looking up information this section is mainly for type prompts (ts items), Vite does not automatically look up files with the. Vue extension.

Here you just need to complete pagelogin.vue. Completing the. Vue extension here is not practical, as most of the projects omit the extension. Fortunately, the official provided a solution, direct configuration can be.

Cn. Vitejs. Dev/config / # res…

resolve.extensions

  • Type: string[]
  • The default: ['.mjs', '.js', '.ts', '.jsx', '.tsx', '.json']

List of extension names you want to omit when importing. Note that it is not recommended to ignore the extension of a custom import type (for example:.vue) because it affects the IDE and type support.

About Question 2

The resolve. Extensions configuration does not solve the problem completely. Keep in mind that you will continue to encounter problems due to the lack of a. Vue extension. The perfect solution is there.

Problem 3: Less import

After fixing problem 2, continue to the page. At this point a new problem arose.

After reviewing the data, Vite optimizes the processing of import from less and sass files, eliminating the need to add ~ symbols before aliases to identify them.

Vitejs.bootcss.com/guide/featu…

Vite has improved @import resolution for Sass and Less to ensure that vite aliases can also be used. In addition, Sass/Less files in a different directory than the root file referenced by the relative path in URL () are automatically rebased to ensure correctness.

Stylus is not supported by @import aliases and URL base changes due to Stylus API limitations.

In this case, you just need to search globally and then replace globally. If you find something that is not replaced globally (too many Spaces, etc.), just replace it manually.

Question 4: net::ERR_ABORTED 408 (Request Timeout)

Fixed problem 3, there are many resource 408 errors when accessing again.

Don’t panic when this happens, just refresh, this is Vite pre-building some dependencies. Since the prebuild is performed only when the resource is requested, vite directly returns 408 when it encounters a package that needs to be prebuilt.

Sometimes it needs to be refreshed multiple times, refreshed after each build, and returned again if new dependencies that need to be pre-built are found408

As to why 408 is returned here, rather than simply suspend the request and return after the pre-build is complete, I am not quite sure. Have the classmate that understands vite principle can answer below.

Vitejs.bootcss.com/guide/dep-p…

Vite transforms ESM dependencies that have many internal modules into a single module to improve subsequent page loading performance.

Some packages import their ES module builds to each other as many separate files. For example, Lodash-es has over 600 built-in modules! When we executed import {debounce} from ‘lodash-es’, the browser made over 600 HTTP requests at the same time! Although the server has no problem handling these requests, the large number of requests can cause network congestion on the browser side, causing pages to load very slowly.

By pre-building LoDash-es as a module, we only need one HTTP request!

Problem 5: Vue WARN: Runtime-only

After everything was pre-built, a new problem arose.

The problem is easier to understand here, as vue’s default reference does not include template compilation. Because vue-CLI was used before, after all, it’s a family thing and VUe-CLI has done something special for this situation.

This situation can be solved by adding an alias in viet.config. js.

Github.com/underfin/vi…

// vite.config.js
{
    resolve: {
        alias: {
            The '@': resolve(__dirname, './src'),... .'vue': require.resolve('vue/dist/vue.esm.js')}}}Copy the code

Problem 6: Require is not defined

The import issue continues here, but this is definitely not caused by the.vue extension. After analysis, the cause of the problem is the use of the REQUIRE statement in the business code, and Vite mainly relies on the ES Module mechanism. This causes the business code to be unrecognized, which in turn causes the module to fail to load.

As a joke, the vite console and browser console reported different errors, and the Vite console error message is completely untraceable.

Staring at the Vite console during the early phase of the migration resulted in a very simple problem that actually took a lot of time.

In this case, the business code needs to be handled manually.

{
    image: require('@uedc/login/dist/static/copyright/[email protected]')}/ / = >
import copyright from '@uedc/login/dist/static/copyright/[email protected]';
{
    image
}
Copy the code

Summary of pit mining stage I

After fixing the above problems, one of the login page entries in the multi-entry application of this project can finally be displayed properly.

  • viteConsole, Chrome console no error
  • The previously modified require is changed to the logo of import, which is displayed normally
  • Please load the verification code through the network, normal display (indicating reverse proxy these are normal)

Although there are still many problems, the solution is not very time-consuming, and the investigation is relatively smooth. So far the migration work can be said to have been carried out half. There are no other changes to the business code except for the HTML template file to modify favicon.ico and requrie to import images, and the intrusion to the business is minimal.

Without further ado, enter your account and password to open the second stage of mining.

Endless pit: Stage two

Uses lang HTML for template

This is a pretty dumb bug and should be a bug of the viet-plugin-vue2 plugin. The problem is that in the template, it says lang=” HTML”

<template lang="html">
	<div>.</div>
</template>
Copy the code

Although the template default is HTML, this declaration is unnecessary, but the error should not feel.

Fortunately, this problem is easier to solve by looking for string global substitution. Why should I declare lang=” HTML “? I don’t know, that’s the charm of the old project: Slightly_smiling_face.

Problem 8: NPM package error

This is also the most difficult issue in the process of migration. More than one NPM package failed, and the contents of the error were different. Some errors can be resolved with vite configuration, and some cannot.

Generally speaking, there are several ways to solve the problem of NPM package error.

Method 1: Modify the alias to point to the source code

Add an alias to viet.config.js

'@sxf/sp-qrcode': resolve(__dirname, './node_modules/@sxf/sp-qrcode/src/index.js'),
Copy the code

The source code for this method for the NPM package is written in the ES Module specification and is included in the uploaded NPM package.

Method 2: Modify the alias to point to the ESM package file

The modification method is the same as method 1, except that the packaged ESM file is executed.

This approach applies to NPM packages that have built esM module content, but do not provide the Module field in packages.json to point to the corresponding file.

This is not the case with open source, heavily used NPM packages, just because some of the problems are internal packages, and there are cases where esM build files are not specified in package.json.

Plan 3: Optimizidep.include

Cn. Vitejs. Dev/config / # opt…

By default, linked packages that are not in node_modules are not prebuilt. Use this option to force the prebuild of linked packages.

OptimiziDep. Include does not resolve the NPM package error. Because the content in node_modules will be pre-built anyway. But from a practical point of view

  • inVite 2.4.4,optimiziDep.includeCan solve some NPM loading problems.
  • inVite 2.5.3,optimiziDep.includeIt doesn’t work anymore.

Because Vite is constantly iterating at a high speed, this method still works in some cases.

Option 4: Refer to question 10

Question 10 is also a problem with NPM, but it is a typical problem, so it is singled out separately.

Solution None: Change or modify the NPM package

If all three solutions fail, you have to start with the NPM package itself.

For your own internal maintenance NPM package, you can repackage an ES Module file and distribute it. Rollup is recommended to easily build CommonJS, ES Module, UMD, and IIFE files

Rollupjs.org/guide/en/#o…

In the case of third-party NPM packages, this situation should not be maintained for a long time, so it should be replaced.

Problem 9: Failed to resolve entry (OPS, same Proplem)

The cause of the problem is the same as problem 2, also because there is no. Vue extension.

@SXF/Cloudsec-Component is a self-maintained business component library. It is based on the basic UI component library. It encapsulates some common services and publishes an NPM package separately. The main purpose is to facilitate the maintenance of multiple products by the same development team (this is related to the organizational structure of the company, not to explore). Because business components need to be debugable, NPM releases the source code for.vue.

Also because of the same coding specification, references to component libraries in the business, and cross-references to component libraries themselves during development, are omitted from the.vUE extension.

Neither solution can be resolved through resolve.extensions.

In vite 2.4.4, () => import(‘foo/bar/baz’) in vue-router cannot be recognized after the resolve. Extensions are configured

This issue was fixed in Vite 2.5.3, and you can upgrade the Vite version if you have such problems.

Solution 1: Human modification

This applies to projects that are just starting out. But for a project that has been under maintenance for years, this is unrealistic. About 20W lines of code, thousands of VUE files. You can’t do that as a programmer.

Solution 2: Vite plug-in

The first example of the Vite plugin introduces a virtual file, and the problem is similar. If there is no additional extension, you can find it through the plug-in.

Vitejs.bootcss.com/guide/api-p…

The pseudo-code is provided here, without actual verification.

Because this only solved the problem of vite loading, but did not solve the problem of type hint, which is not friendly for the subsequent TS related modification, so we did not verify this solution.

If you are interested, you can verify that plan three is adopted by individuals in actual business.

module.exports = function () {
    return {
        name: 'vite-plugin-fix-vue-ext'.resolveId(id) {
            // Determine whether the suffix needs to be added. If so, return the ID, indicating a match.
            if (isNeedFixExt(id)) return id;
        },
        load(id) {
            if (isNeedFixExt(id)) returnfs.readFileSync(fixExt(id)); }}}Copy the code

Solution 3: Codemod

Codemod means to code modify. In fact, it means to change the code itself, and to change the code through the code, not the human body.

In fact, ESLint’s auto fix is also a kind of Codemod.

Here’s the idea:

  1. Find all import statements and dynamic import statements
  2. Extract the value of import and filter out values that already have extensions.
  3. For values that do not have extensions, treat them as absolute paths (consider relative paths, vite aliases), temporarily calling this absolute pathap.
  4. Then theapA filling['.js','.ts','.vue','/index.js','/index.ts','/index.vue']Check for presence in turn. if.vueThe presence of a type, but the absence of any other type, indicates the need to complete the suffix.
  5. The solution can be to try to read the corresponding files in turn, or to pre-read all the files and build a Map for lookup.

That’s how it works. The key is how to get all the imports and how to modify the corresponding files.

  • Scheme 1: Use ES-Module-lexer and magic-String. Because Vite itself is using these two packages for naked module rewrite, about what is naked module wash, you can see the nuggets side of the article, here no further details. The advantage of this scheme is that the processing efficiency is very high, but the disadvantage is that the operation can only be carried out for import, and the scene is relatively simple.

    Juejin. Cn/post / 699220…

  • Jscodeshift is an operation AST. Jscodeshift is slower and much more complex. Option 2 is preferred because jscodeshift has a high versatility and other codemod requirements are feared during subsequent migration.

    Jscodeshift’s API is complex, and there are plenty of examples of Codemod on github. If we want to develop it, we can try it on astexplorer.net/.

First, click Transform and click jscodeshift.

Then you can see the results as you write the code.

The code for extracting dynamic imports and the code for normal imports is shown below

// Extract dynamic import
j(file.source)
    .find(j.ImportDeclaration)
    .forEach(path= > {
        const importNode = j(path);
    	const importString = importNode.get('source');
    	importString.value.value = 'zzzzzzzz';
    })
    .toSource();

// Extract the normal import
j(file.source)
    .find(j.ImportExpression) ImportDeclaration => ImportExpression
    .forEach(path= > {
        const importNode = j(path);
    	const importString = importNode.get('source');
    	importString.value.value = 'zzzzzzzz';
    })
    .toSource();
Copy the code

The code that tries to determine if the file exists is as follows

// STR is the string of import
// filename is the absolute path where the import statement appears
const fixExtname = (str, fileName) = > {
    let importAbsolutePath;
    // Handle relative paths, aliases, etc
    if (str.startsWith('/')) {
        importAbsolutePath = path.resolve(path.dirname(fileName), str);
    } else if (str.startsWith('.. / ')) {
        importAbsolutePath = path.resolve(path.dirname(fileName), str);
    } else  {
        importAbsolutePath = fixAlias(str);
    }
    const tries = {
        '.js': ' '.'.ts': ' '.'.vue': '.vue'.'/index.js': ' '.'/index.ts': ' '.'/index.vue': '/index.vue',}let ext = ' ';
    Object.keys(tries).forEach(key= > {
        const p = importAbsolutePath + key;
        if (fs.existsSync(p)) {
            ext = tries[key];
        } else{}})return ext;
}
Copy the code

About Programme 3

Another problem here is that JScodeshit does not support.vue files. Personal development is based on the company’s Codemod tool, which encapsulates the identification of vue files.

Vue files are also identified by the vUE official package resolution. If you have a strong need for this, consider encapsulating the code into a separate COdemod for JScodeshit.

Problem 10: Does not provide an export named ‘default’

The problem is that third-party NPM packages provide CommonJS specification files.

Vitejs.bootcss.com/guide/dep-p…

CommonJS and UMD compatibility: During development, vite’s development server treats all code as native ES modules. Therefore, Vite must first convert dependencies published as CommonJS or UMD to ESM.

Vite is compatible with CommonJS files, but it is not compatible with CommonJS files.

This file can be imported but does not provide default, so vite does support CommonJS.

In this case we need to introduce the @Originjs/vite-plugin-commonjs package

const { viteCommonJS } = require('@originjs/vite-plugin-CommonJS');
module.exports = {
    plugins: [
        viteCommonJS({
            include: ['procurios.resizesensor']
        }),
        createVuePlugin()
    ]
}
Copy the code

Summary of pit mining stage ii

A lot of code changed during the migration of the main entry page.

  • Business code293A file
  • Code for business components139A file

But modifying these files mostly does not modify the business, just completes the links. However, the import file of less is incompatible with Webpack.

In addition, there are some problematic NPM packages, and the migration process is not completely resolved, but the corresponding code is annotated.

Finally through the above measures, finally able to visit, but also see the main entrance page.

Final summary

We all know the advantages of Vite, I mainly talk about the disadvantages of the present stage.

In general, vite still has the following problems at this stage:

  • Manual tonpm run devAfter prebuild, cache is lost while modifiedvite.config.jsThe resulting automatic restart does not.
  • Prebuild requires access to the corresponding file to trigger, resulting in frequent408And very slowly. It takes a long time and requires you to manually refresh your browser. Here’s the weird partVite 2.4.4In this respectVite 2.5.3It’s so much faster, it’s basically one time408.
  • Can’t handle non wellES moduleNPM package, withwebpackThere are still some gaps.
  • The number of requests is huge, and judging from the Network panel, the number of requests is high468To removeXHRThere is more than the request400A request. So the first load time is a little bit slower and probably takes a little bit longer5 secondsThis should be optimized through vite configuration. After the adjustment of some internal NPM packages can carry out normal development, some development practices will be summarized and released.
  • Function is unstable. Early useVite 2.4.4Migration is slightly more problematic. And then update toVite 2.5.3Since then, some of the problems have been fixed, but some new ones have been added.

For older projects that need to migrate to Vite, consider the following:

  • Not support notES moduleBusiness code (such as require, module.exports), if there is any code that needs to be adjusted, you can try withQuestion 9Same way.
  • Less/sass@import: Vite no longer requires ~ to use aliases, which is incompatible with WebPack.
  • Because IE11 doesn’t support it naturallyES module, IE debugging is more troublesome, do not recommend the need to support IE11 projects willviteAs the only build tool.
  • At this stage of theviteNPM package has high requirements, before migration can first look at the project NPM package, the replacement of the replacement, the upgrade of the upgrade.

Overall, Vite is a good development experience, lightweight and fast. Even a bloated old project can respond quickly and without taking too many resources. When the project is large, webpack will take up a lot of computer resources when it starts, which leads to the lack of VSCode during this period, which is a very bad experience.

Overall, vite has a promising future.

Finally, I wish you a happy brick 😃