53

Webpack to Vite

background

Recently, a collection of pain points and optimizable items in the Among them, time-consuming construction and slow project compilation have appeared several times.

With the rapid development of business, the volume of many of our projects has rapidly expanded. Then comes problems such as slow packaging.

Improving research and development efficiency is the eternal pursuit of technical people.

Our project also has the problem of slow start-up, which has been mentioned by my colleagues several times. I happened to have done similar exploration and optimization before, so I took this opportunity to transform the project, solve the problem of time-consuming startup.

Yesterday afternoon (2021.4.7 23:00), Vite was successfully embedded, the project start time was about 190s => 20s , and the hot update time was shortened to 2s .

I stepped on some pits in the middle, but fortunately, I climbed out at the end. The relevant technical points will be presented below.

FBI Warning: The following text is just some superficial experience that I have combined with my own actual projects. If there are errors, please correct me :)

Today’s main content:

  • Why does Vite start so fast
  • How to install Vite in my project
  • I encountered problems during the renovation process
  • Some thoughts on Vite development, packaging and launching
  • related codes and conclusions

text

Why does Vite start so fast

In terms of the underlying implementation, Vite is based on esbuild's pre-built dependencies.

esbuild is written in go, and is 10-100 times faster than the packager pre-built dependencies written in js.

Because js is too slow compared to go, the general operation of js is in milliseconds, and go is in nanoseconds.

In addition, the startup methods of the two are also different.

webpack startup method

image.png

Vite startup method

image.png

Webpack will first, then start the development server, and directly give the packaging result when requesting the server.

In Vite, directly starts the development server, and which module is requested to real time.

Since modern browsers natively support ES Module, they will automatically send requests to dependent Modules.

Vite takes full advantage of this, and treats the module files in the development environment as the files to be executed by the browser, instead of the package and merge like Webpack.

does not need to be packaged when Vite is started, it means that does not need to analyze module dependencies, and does not need to be compiled.
Therefore, the startup speed is very fast. When the browser requests a module, it compiles the content of the module as needed.

This on-demand dynamic compilation method greatly reduces compilation time. The more complex the project and the more modules, the more obvious the advantages of vite.

In terms of HMR (hot update), when a module is changed, only the browser needs to request the module again. Unlike webpack, which requires all dependent modules of the module to be compiled once, it is more efficient.

From the actual development experience, in Vite mode, the development environment can be started instantly, but it will take a while to wait until the page comes out.

How to install Vite in my project

new project

Creating a new Vite project is relatively simple:

yarn create @vitejs/app

image.png

image.png

After it is generated, just start it directly:

image.png

Existing project

The migration of existing projects is slightly more cumbersome.

First, add the relevant configuration of Vite. Here I used a cli tool: wp2vite .

After installation, directly execute:

image.png

In this step, the Vite configuration file will be automatically generated and related dependencies will be introduced.

Install the dependency and start it.

If there are no accidents, you will a bunch of errors with 16076b6216b0a6.

Congratulations, enter the happy and joyful stepping on the pit.

The problems I encountered during the renovation

1. alias error

image.png

There are some aliases configured in the project code, which cannot be recognized by vite, so aliases need to be configured in vite as well:

  resolve: {
    alias: {
      '@': resolve(__dirname, 'src'),
    },
  },

2. Unable to recognize less global variables

image.png

Solution:

vite.config.js inject the custom global variables from the outside, and add them directly to the css option of 06076b6216b170:

  css: {
    preprocessorOptions: {
      less: {
        modifyVars: {
          hack: `true;@import '${resolve('./src/vars.less')}';`,
          ...themeVariables,
        },
        javascriptEnabled: true,
      },
    },
  },

3. Uncaught Error: Target container is not a DOM element.

image.png

The root element was not found.

The reason is: In the index.html generated by default:

<div id="root"></div>

id is root, and the logic is #app , here it can be changed directly to id=app .

4. The typings file cannot be found

image.png

typings file was not found.

This mistake, at first glance, was confusing.

Go in and take a look at the source code and compiled code:

Source code:

image.png

After compilation:

image.png

image.png

Isn't the typings file here? Why can't I find it?

Thought for a while: Vite doesn't know that the typings file does not need to be compiled, and needs to tell the compiler not to compile this file.

Finally found the answer in the official TS documentation:

https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-8.html

Type-Only Imports and Export

This feature is something most users may never have to think about; however, if you’ve hit issues under --isolatedModules, TypeScript’s transpileModule API, or Babel, this feature might be relevant.

TypeScript 3.8 adds a new syntax for type-only imports and exports.

import type { SomeThing } from "./some-module.js";
export type { SomeThing };

Need to introduce types separately, so change the code to:

image.png

At the same time, it should be noted that if a file has multiple exports, they must be imported separately:

image.png

The only painful thing is: the overall situation needs to be changed again, manual work.

So far, the typings problem is perfectly solved.

5. Unrecognized svg

When we use svg as an icon component, it is generally:

import Icon from '@ant-design/icons';
import ErrorSvg from '@/assets/ico_error.svg';

const ErrorIcon = (props: any) => <Icon component={ErrorSvg} />;

// ...
<ErrorIcon />

The browser reports an error:

image.png

error occurred in the </src/assets/ico_error.svg> component

Obviously, here is the file path as a component.

What we need to do now is: replace this file path with a recognizable component.

After searching, I found a plug-in: vite-plugin-react-svg

Join configuration:

const reactSvgPlugin = require('vite-plugin-react-svg');

plugins: [
  reactSvgPlugin(),
],
import MyIcon from './svgs/my-icon.svg?component';

function App() {
  return (
    <div>
      <MyIcon />
    </div>
  );
}

Note that: The imported svg file needs to be suffixed ?component

Looking at the source code, this suffix is used as an identifier,

image.png

If the suffix matches component , the file is parsed, cached, and the result is returned:

image.png

After knowing the principle, you need to put all .svg => .svg?component .

One-click replacement of vscode is fine, but be careful not to replace the node_module as well.

6. global is not defined

image.png

global is a variable in Node. Will it report an error on the client?

Looking at it layer by layer, it turns out that the imported third-party package uses global.

Look at the Client Types mentioned in the vite documentation:

image.png

Append to tsconfig :

 "compilerOptions": {
    "types": ["node", "jest", "vite/client"],
 }

Then, there is no misuse. . .

image.png

There is no way, but to window Dafa.

Add in the entry index.tsx:

(window as any).global = window;

Refresh, that's it.

image.png

7. [Unsolved] Replace HtmlWebpackPlugin

Also need to inject some external variables, modify the entry html, favicon, title and so on.

Found a plugin: vite-plugin-singlefile

But it is of no use.

Anyone who understands, please leave a message for advice.

At this point, the entire app can be run locally, and the build is no problem.

7. Memory overflow during online packaging and construction

It can run locally, and there is no problem with packing. Of course, I will put it on the line for a run.

Arrange now!

image.png

If the memory is insufficient, I will add something for you:

image.png

image.png

Get it done!

unnamed.gif

Some thoughts on Vite development, packaging and launching

From the actual use point of view, vite still cannot completely replace webpack in some functions.

After all, it is a rising star, and the related ecology needs to be continuously improved.

In my opinion, a relatively safe way at present is:

  • Keep webpack dev & build capabilities, vite is only used as a development aid

Wait for the related tools to be more perfect, and then consider the complete migration.

Related code and conclusion

A complete Vite demo

Warehouse address: https://github.com/beMySun/react-hooks-i18n-template/tree/test-wp2vite

image.png

Vite.config.js complete configuration of the business project

import { defineConfig } from 'vite';
import reactRefresh from '@vitejs/plugin-react-refresh';
import legacyPlugin from '@vitejs/plugin-legacy';
import { resolve } from 'path';

const fs = require('fs');
const lessToJS = require('less-vars-to-js');
const themeVariables = lessToJS(fs.readFileSync(resolve(__dirname, './src/antd-custom.less'), 'utf8'));
const reactSvgPlugin = require('vite-plugin-react-svg');

// https://cn.vitejs.dev/config/
export default defineConfig({
  base: './',
  root: './',
  resolve: {
    alias: {
      'react-native': 'react-native-web',
      '@': resolve(__dirname, 'src'),
    },
  },
  define: {
    'process.env.REACT_APP_IS_LOCAL': '\'true\'',
    'window.__CID__': JSON.stringify(process.env.cid || 'id'),
  },
  server: {
    port: 8080,
    proxy: {
      '/api': {
        target: 'https://stoku.test.shopee.co.id/',
        changeOrigin: true,
        cookieDomainRewrite: {
          'stoku.test.shopee.co.id': 'localhost',
        },
      },
    },
  },
  build: {
    target: 'es2015',
    minify: 'terser',
    manifest: false,
    sourcemap: false,
    outDir: 'build',
    rollupOptions: {},
  },
  esbuild: {},
  optimizeDeps: {},
  plugins: [
    // viteSingleFile({
    //   title: 'dynamic title', // doesn't work
    // }),
    reactSvgPlugin(),
    reactRefresh(),
    legacyPlugin({
      targets: [
        'Android > 39',
        'Chrome >= 60',
        'Safari >= 10.1',
        'iOS >= 10.3',
        'Firefox >= 54',
        'Edge >= 15',
      ],
    }),
    // vitePluginImp({
    //   libList: [
    //     {
    //       libName: 'antd',
    //       style: (name) => `antd/es/${name}/style`,
    //     },
    //   ],
    // }),
  ],
  css: {
    preprocessorOptions: {
      less: {
        modifyVars: {
          hack: `true;@import '${resolve('./src/vars.less')}';`,
          ...themeVariables,
        },
        javascriptEnabled: true,
      },
    },
  },
});

At last

Using Vite can greatly shorten project construction time and improve development efficiency.

However, it is necessary to make reasonable choices based on the actual situation of the project.

For this project of mine, it is quite useful to use Vite as a way to assist in development.

It is expected that Vite will continue to improve and improve the efficiency of research and development.

Okay, that's all there is to it, I hope it will be helpful to everyone.

Ability to learn, if there are any mistakes, please correct me.

Thank you.

Finally, if you find the content helpful, you can follow my official account, keep up with the latest developments, and learn together!

image.png


皮小蛋
8k 声望12.8k 粉丝

积跬步,至千里。