Build the project with vite and support the micro front end

Posted by nobertos on Fri, 14 Jan 2022 14:38:21 +0100

Benefit from Super high performance of esbuild , vite has attracted much attention since its birth, and has always maintained active development iterations. So far, vite has iterated to version 2.7.10, and all aspects basically meet the conditions for production and use. During this time, I tried to use vite for packaging construction in the project. This article is the process record of this construction.

Basic configuration

use first vite official scaffold Build project.

yarn create vite vite-demo --template react-ts

The above command creates a project called vite demo using the react TS template. Because my team mostly uses react and typescript for daily development, I chose react TS as the template. There are many templates officially supported by vite, which can be found in create-vite View in.

After the project is initialized, the directory structure is as follows:

.
|____index.html
|____.gitignore
|____package.json
|____tsconfig.json
|____vite.config.ts
|____src
| |____App.tsx
| |____main.tsx
| |____App.css
| |____index.css
| |____vite-env.d.ts
| |____logo.svg
| |____favicon.svg

Of which vite config. The contents are as follows:

import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'

// https://vitejs.dev/config/
export default defineConfig({
  plugins: [react()]
})

It can be seen that vite officials have made relatively perfect packaging, and the development experience has improved a lot compared with the previous version.

After installing the dependencies according to the instructions and starting the application, the speed is really fast. Now let's do some basic transformation.

I usually use less to write styles. vite has made good support. After installing the dependency, I just need to directly reference XXX in the code Less. For a tried and tested developer, the style still needs to be introduced into the scope, and css modules are usually used.

Install the less preprocessor,

yarn add --dev less

Then modify vite config. TS file, add css modules configuration:

export default defineConfig({
  ...
  css: {
    modules: {
      localsConvention: 'camelCaseOnly', // We use hump form
    },
  },
  ...
})

After adding the configuration, just add the original XXX Change less to XXX module. Less, which is the same as create react app.

Here is a recommended vscode plug-in clinyong.vscode-css-modules It can realize the intelligent prompt of style class name during coding, and click the style class name to jump to the place of style definition, which is very easy to use. If you write a style using the middle dash form of naming, such as XXX container, this vscode plug-in needs to be configured additionally, as follows:

{
  "cssModules.camelCase": true
}

In this way, the dash form can be used when writing styles, and the hump form can be used in the code.

Because I developed a middle and background project, using antd and lodash, as we all know, these two are large users loaded on demand, which we used before babel-plugin-import There are many similar schemes in vite ecology. I chose vite-plugin-imp This plug-in, modify vite config. The TS is as follows:

import vitePluginImp from 'vite-plugin-imp';

export default defineConfig({
  ...
  plugins: [
    ...
    vitePluginImp({
      libList: [
        {
          libName: 'lodash',
          libDirectory: '',
          camel2DashComponentName: false,
        },
        {
          libName: 'antd',
          style(name) {
              // use less
              return `antd/es/${name}/style/index.js`;
          },
        },
      ],
    }),
  ],
  css: {
    ...
    preprocessorOptions: {
      less: {
        javascriptEnabled: true,
      },
    },
  },
});

antd supports Tree Shaking by default. The above configuration will only handle the on-demand loading of styles. lodash does not support Tree Shaking. We can also use the ESM version lodash-es In this way, vite plugin imp can not be used. The configuration is as follows:

export default defineConfig({
  resolve: {
    alias: [{
      find: /^lodash$/,
      replacement: 'lodash-es',
    }],
  },
});

Generally, when developing front-end projects, we need some agents to call the back-end API interface. The vite configuration is as follows:

export default defineConfig({
    ...
    server: {
      proxy: {
        '/api_path/': {
          target: 'http://xxx.server.domain.com/',
          changeOrigin: true,
        },
      },
    },
});

The underlying agent is based on http-proxy Implementation, not too much description here.

Now you can develop code happily.

Support micro front-end construction

Because our middle and background applications are managed by qiankun, the above configuration cannot be recognized by qiankun after packaging. The main reasons can be seen here , we need to do some extra processing.

We know that to build a micro front end using webpack, you need to add the following three configuration items:

{
  output: {
    libraryTarget: 'umd',
    library: `${APP_NAME}-[name]`,
    jsonpFunction: `webpackJsonp_${APP_NAME}`,
  }
}

In vite, you can directly set build rollupOptions. Format is UMD to set the UMD specification, but the actual construction result can not be recognized by qiankun. I guess it may be related to vite's use of html entry.

Another way of thinking, we build the current entire application as a library, output it to the UMD specification, and then manually write an html file to load the output JS.

Modify the configuration as follows:

export default defineConfig({
  ...
  build: {
    lib: {
      name,
      entry: path.resolve(__dirname, 'src/index.tsx'),
      formats: ['umd'],
    },
  },
  ...
})

After the configuration is completed, execute yarn build to prompt the following error:

UMD and IIFE output formats are not supported for code-splitting builds.

Because there are routes in our application, on-demand loading is used. We will open the inlineDynamicImports configuration of rollup:

export default defineConfig({
  ...
  build: {
    rollupOptions: {
      output: {
        inlineDynamicImports: true,
      },
    },
  },
  ...
})

In this way, after the construction is completed, there are two files style. In the dist directory CSS and XXX umd. js.

Now we're going to generate index HTML.

Because vite directly uses ES Modules in the development state, it is not packaged, so the index in the development state is generated HTML and production index Html is different.

We modify the index. In the root directory of the project Html is:

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <link rel="icon" type="image/svg+xml" href="/src/favicon.svg" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Vite App</title>
    <!-- style placeholder -->
  </head>
  <body>
    <div id="root"></div>
    <!-- script placeholder -->
  </body>
</html>

Pay attention to the two lines of comments. We will do different processing in the development status and production build.

There is one in the vite plug-in API transformindexhtml The html content in the development status can be customized. Therefore, the configuration of our development status is as follows:

// https://vitejs.dev/config/
export default defineConfig({
  ...
  plugins: [
    ...
    {
      name: 'dev html',
      apply: 'serve',
      transformIndexHtml(indexHtml: string) {
        return indexHtml
          .replace('<!-- style placeholder -->', '')
          .replace('<!-- script placeholder -->', '<script type="module" src="/src/index.tsx"></script>');
      },
    },
    ...
  ],
});

Production build requires the help of @rollup/plugin-html This plug-in implements custom html content.

import html from '@rollup/plugin-html';
import fs from 'fs';

const entryHtml = fs.readFileSync('./index.html', { encoding: 'utf-8' });

export default defineConfig({
  ...
  plugins: [
    ...
    {
      name: 'build html',
      apply: 'build',
      ...html({
        template: () => {
          return entryHtml
            .replace(
              '<!-- style placeholder -->',
              '<link rel="stylesheet" type="text/css" href="style.css" />',
            )
            .replace(
              '<!-- script placeholder -->',
              `<script type="text/javascript" src="${name}.umd.js"></script>`,
            );
        },
      }),
    },
    ...
  ],
});

Through the above configuration and build again, qiankun can load this sub application.

Other instructions

1. Support for old browsers

Because my project this time is a middle and background project, the demand for support for old browsers is not strong, so I didn't deal with it in the project. In fact, vite officials also gave the solution, that is @vitejs/plugin-legacy This plug-in.

The principle is also very simple, that is to use < script nomodule > to implement the execution of relevant scripts by browsers that do not support ES Modules SystemJS To load the module.

2. Description of TypeScript

After the scaffold is initialized, it can be developed with TypeScript. It should be noted here that the compiler option isolatedModules:true needs to be turned on, because vite uses esbuild to process TS files and only converts ts to js without type checking (relying on the editor to process type checking, such as vscode). Therefore, errors will occur when some pure type imports and exports are encountered. You need to turn on isolatedModules:true to avoid this problem. If this option cannot be turned on for some reasons, you can use rollup-plugin-friendly-type-imports The README of this package also explains why there are such problems.

3. Docking CDN

Based on the results of the above configuration, the browser uses the root path (/) when loading resources. If CDN is used, there will be a problem of resource loading 404.

We can configure the base to set the basic path, which is similar to the public of webpack_ PATH.

export default defineConfig({
  base: '/some/public/path',
})

4. Build error

4.1 package not found

The error message is:

[plugin: vite:dep-scan] Failed to resolve entry for package "xxx"

Usually, the dependent package is not in package JSON correctly configures fields such as main and module, which makes vite unable to find the package entry.

You can map aliases to the correct files by setting them.

export default defineConfig({
  resolve: {
    alias: [{
      find: /^SOME_PACKAGE_NAME$/,
      replacement: 'SOME_PACKAGE_NAME/dist/xxx.es.js',
    }],
  },
});

4.2 request timeout

The error message is:

net::ERR_ABORTED 408 (Request Timeout)

After starting the development server, the browser encountered a request timeout error. Because vite detects a request for a dependent package and the dependency has not been processed by vite, the pre build will be triggered, resulting in request timeout and page overload.

We can refresh several more times and wait for vite to complete the pre build, or we can add the dependency to optimizedeps Include to handle in advance.

4.3 import module error

The error message is:

Internal server error: Failed to resolve import "./chunk-7L3SPMWF.js" from "node_modules/.vite/antd.js?v=7bec0e27". Does the file exist?

Maybe it's because some formats that depend on package output, vite, are not supported yet. Take a look at this issue.

This error only exists during the running and processing of the development server. It will not appear after the page is displayed normally. After ignoring this error, it has no impact at present.

Summary

In general, vite has basically met the conditions for production and use. For conventional application development, vite configuration is very simple, which can be said to be used out of the box. It is also very convenient to add additional configurations if necessary.

At present, the major problem is that the surrounding ecology is not particularly mature, and many mature packages have weak support for vite (ES Modules). At the same time, if there is a strong basic atmosphere in the team, the toolkit developed by yourself should also consider this issue.

Common interview knowledge points, technical solutions analysis, tutorials, you can sweep the code to pay attention to the official account "thousands of search" to get, or come here. https://everfind.github.io/posts/ .