Summary of successful use of Vite in VUE2 environment

Vite has received a lot of praise from the community since it was released, and now vite has been updated to version 2.0. I have always been impressed by the extremely fast compilation features of Vite, especially since the project size has seriously slowed down compilation and hot update speed, and urgently needed to find a solution. However, there are only plug-ins supporting VUE3 officially now, and my project still uses vue2 version, so I have been trying to successfully apply Vite to VUe2 project. Here are some solutions to the problems summarized in practice during this period of time, for your reference.

Note: According to my ideal solution is: Webpack as a packaging tool, Vite as a development tool, so I will consider vite and WebPack environment as far as possible to maintain a common configuration.

How to import vUE single file correctly

In the @vue/ CLI project environment, resolution of.vue file extensions has been added to webPack configuration so that we can omit the. Vue suffix when importing, but it is not recommended to omit custom import type extensions in Vite documents (e.g. .vue), because it affects the IDE and type support, it is necessary to replace all previously introduced modules that do not have a. Vue suffix. Due to the sheer size of the project, it was definitely not practical to manually make changes in every file, so I wrote a script to execute in Node to automatically complete the.vue suffix

// rewriteImportPath.js

const path = require("path");
const fs = require("fs");
const chalk = require("chalk");

const overridePaths = [];
const r = / (? 
      ;

const baseDir = path.join(process.cwd(), "src");
const defaultExtensions = [".mjs".".js".".ts".".jsx".".tsx".".json"];

function rewritePath(baseDir, rootPath) {
  try {
    const resolveAlias = require("./resolveAlias");
    const directory = fs.readdirSync(baseDir);
    directory.forEach(file= > {
      const filePath = path.join(baseDir, file);
      const stat = fs.statSync(filePath);
      if (stat.isDirectory()) {
        rewritePath(filePath);
      } else if (/\.vue$|\.jsx? $/.test(file)) {
        const fileContent = fs.readFileSync(filePath, "utf-8");
        const newFileContent = fileContent.replace(r, ($$$1, $2, $3) = > {
          let importPath;
          constisDynamicImport = !! $1;
          const modulePath = isDynamicImport ? $2 : $3;
          if (modulePath.startsWith(".")) {
            importPath = path.join(baseDir, modulePath);
          } else {
            if (path.isAbsolute(modulePath)) {
              importPath = path.join(rootPath, modulePath);
            } else {
              const dirLevels = modulePath.split("/");
              const pathAlias = resolveAlias[dirLevels.shift()];
              if(pathAlias) { importPath = path.join(pathAlias, ... dirLevels); }}}if(! importPath) {return $$;
          }
          let suffix;
          const files = fs.readdirSync(path.dirname(importPath));
          const parsedImpPath = path.parse(importPath);
          files.some(file= > {
            const parsedFilePath = path.parse(file);
            if (
              defaultExtensions.includes(parsedFilePath.ext) &&
              parsedFilePath.name === parsedImpPath.name
            ) {
              // Fix suffix error in module import
              if (
                parsedImpPath.ext === ".js" &&
                parsedFilePath.ext === ".jsx"
              ) {
                suffix = parsedFilePath.ext;
              }
              return true;
            }
            if (
              !parsedImpPath.ext &&
              parsedFilePath.ext === ".vue" &&
              parsedFilePath.name === parsedImpPath.name
            ) {
              suffix = ".vue";
              return true; }});if (suffix) {
            const {dir,name} = path.parse(modulePath)
            const overridePath = $$.replace(
              modulePath,
              `${dir}/${name}${suffix}`
            );
            overridePaths.push("pathRewrite:"+ + $$"> > >" + overridePath);
            return overridePath;
          }
          return $$;
        });
        if(fileContent ! == newFileContent) { fs.writeFileSync(filePath, newFileContent,"utf-8"); }}}); }catch (e) {
    console.error(`error in ${baseDir}: \ n `, e);
  }
}
rewritePath(baseDir, process.cwd());
overridePaths.forEach(v= > console.log(v));
console.log(
  "\ n ✔ ️",
  chalk.green("Co-successful rewriting." + overridePaths.length + "One reference path"));Copy the code

How do I make vite parse VUe2 code

Vite and vue2 are used together. The main plug-in used is vite-plugin-vue2, which is also recommended for vue2 development in the Vite document

How do I enable JSX syntax

Using JSX in Vite is a bit of a hassle. Js files that use JSX syntax have to be named with JSX suffixed names, and the JSX identity has to be added to SFC components in Vue

  1. First you need to enable JSX in viet-plugin-vue2

    vite.config.js

    import { defineConfig } from "vite";
    import { createVuePlugin } from "vite-plugin-vue2";
    export default defineConfig({
        plugins: [createVuePlugin({jsx: true})]})Copy the code
  2. Add the JSX identifier to SFC components

    The script block needs to be marked with lang= JSX

    <script lang="jsx">
        export default {
            render(){
                return <div>JSX Render</div>
            }
        }
    </script>
    Copy the code

    In addition, as the development of vue2 project is basically in the IDE using the Vetur plug-in to enable vUE feature support, but when the script tag in the VUE file added lang=” JSX “, no longer use vetur for code formatting, see hereissue

  3. You need to change all js files that use the JSX syntax to.jsx

JSX translation is enabled when the vite-plugin-vue2 finds that the imported resource is of vue type and has lang= JSX, the core of which is still via Babel using @vue/babel-preset- JSX. There is a leak:

When using Babel translation, Babel searches the Babel configuration file in the current project directory by default, for examplebabelrcorbabel.config.jsIf the current project has a configuration file with Babel, it will compilejsxIf preset is enabled, check whether @vue/babel-preset- JSX is included in the configuration file. The same preset cannot be repeatedly added, otherwise an error will be generated during compilation

Since I plan to use Vite in the development environment and webpack in the production environment, the Babel configuration file cannot be deleted. The solution is as follows:

  1. If the babel.config.js file is used, env in the configuration determines that the configuration is enabled only in production mode

    module.exports = {
      env: {
        production: {
          presets: [['@vue/app',
                {
                    'useBuiltIns': 'entry'}]}}};Copy the code
  2. Replace babel.config.js with the.babelrc file, because the Babel in viet-plugin-vue2 specified babelrc:false, that is, ignore the.babelrc configuration to avoid using irrelevant Babel configuration.

    In vite plugin – vue2 jsxTransform. Js

    function transformVueJsx(code, id, jsxOptions) {
        const plugins = [];
        if (/\.tsx$/.test(id)) {
            plugins.push([
                require.resolve('@babel/plugin-transform-typescript'),
                { isTSX: true.allowExtensions: true},]); }const result = core_1.transform(code, {
            presets: [[require.resolve('@vue/babel-preset-jsx'), jsxOptions]],
            sourceFileName: id,
            sourceMaps: true,
            plugins,
            babelrc: false});return {
            code: result.code,
            map: result.map,
        };
    }
    Copy the code

I issued a PR to the authors of vite-plugin-vue2 to fix this problem by adding configFile:false to babel.transform, but it hasn’t been successfully adopted yet.

Problem: a memoir

Do not use index as key on transition-group elements

Solution: Do not use index as key in v-for loops, replace it with a unique value

SCSS cannot be used :export exports variables

Vite does not currently support the following syntax :export:

$primary: #1890ff
:export {
    primary: $primary
}
Copy the code

I have checked the corresponding issue, and what Judah said only supports the syntax officially supported by SASS, so I suggest using CSS-modules instead. Export is not a SASS syntax. In WebPack environment, export is provided by CSS-Loader

To enable CSS-modules, simply change the SCSS file name to module. SCSS

Error importing @ant-design/ ICONS -vue

In the project, several components from ant-design-vue were used. Using the on-demand import mode (via the vite-plugin-IMP plug-in), the internal icon component depended on @ant-Design/ICONS -vue, and the following error was reported:

The main reason is node_modules\ant-design-vue\es\icon\index.js

.import * as allIcons from '@ant-design/icons/lib/dist'; .Copy the code

The allIcons imported from @ant-design/ ICONS /lib/dist have an extra default export. This is caused by the conversion of CommonJS from Vite to ES module, so an error was reported when traversing allIcons to default

Solution:

Imoprt {XXX} from ‘antd-design-vue’; imoprt {XXX} from ‘antd-design-vue’ Point to import {XXX} from ‘ant-design-vue/dist/antd.min.js

vite.config.js

export defineConfig({
    resolve: {alias: [{find: /ant-design-vue$/,replacement: 'ant-design-vue/dist/antd.min'}]}})Copy the code

Note that when this is done, do not use the on-demand plug-in. Only importing compiled code can resolve this error. Only full import, which is acceptable in a development environment

Also, after full import, remember to manually import the ant-Design-vue style file

4. Redundant space characters appear in VUE template compilation

This problem can lead to misalignment of page content, as extra whitespace characters appear in the Dom structure, resulting in separations between text content and Spaces between inline tags. By default, vue-plugin-vue2 has no default configuration in vue-CLI, which makes the default configuration different from that in Vue-CLI of WebPack. See this issue.

Solution:

vite.config.js

import { createVuePlugin } from "vite-plugin-vue2";
export defineConfig({
      plugins: [
        createVuePlugin({
            jsx: true.vueTemplateOptions: {
                compilerOptions: {
                  whitespace: "condense"}}})]})Copy the code

Context cannot be used in vite context

Require. context is a webpack-specific syntax for creating a Require context, usually for bulk import modules. In Vite, however, require is not supported at all, and all module imports must use import, so vite also provides a glob import function for batch imports

import.meta.glob

It is used to import multiple modules dynamically (lazily loaded), and each module is split into separate chunks at build time. Ex. :

const modules = import.meta.glob('./dir/*.js')

// The above statement is translated like this:

// Vite generated code
const modules = {
  './dir/foo.js': () = > import('./dir/foo.js'),
  './dir/bar.js': () = > import('./dir/bar.js')}Copy the code

import.meta.globEager

For importing multiple modules statically, for example:

const modules = import.meta.glob('./dir/*.js')

// The above statement is translated like this:

// Vite generated code
// Vite generated code
import * as __glob__0_0 from './dir/foo.js'
import * as __glob__0_1 from './dir/bar.js'
const modules = {
  './dir/foo.js': __glob__0_0,
  './dir/bar.js': __glob__0_1
}
Copy the code

Both batch dynamic import and batch static import return an object, where the key is the path of the module, and the value is a function to load the module dynamically or is directly an imported module. Require. context returns a function, so when replacing require.context with a glob import, you also need to pay attention to the change in how you get the module that gets the import.

Because of the require.context difference, there is no common processing logic in webpack and vite environments. Recently I found Babel plugin babel-plugin-transform-viet-meta-glob. The glob import method can be used directly in the WebPack environment. This plug-in will translate it into the same processing method as in Vite, so that the vite environment and webPack environment batch import module code is consistent, for example:

const modules = import.meta.glob('./path/to/files/**/*')

const eagerModules = import.meta.globEager('./path/to/files/**/*')

// The above code will be translated as follows

const modules = {
  './path/to/files/file1.js': () = > import('./path/to/files/file1.js'),
  './path/to/files/file2.js': () = > import(('./path/to/files/file2.js'),
  './path/to/files/file3.js': () = > import(('./path/to/files/file3.js')}const eagerModules = {
  './path/to/files/file1.js': require('./path/to/files/file1.js'),
  './path/to/files/file2.js': require('./path/to/files/file2.js'),
  './path/to/files/file3.js': require('./path/to/files/file3.js')}Copy the code

How to use Svg Icon

SVG icon is generally processed by SVG-Sprite-loader in Webpack, which is convenient for direct use in code. There is also a similar plugin under Vite, vite-plugin-SVG-icons, which has a similar configuration to SVG-sprite-loader

import { defineConfig } from "vite";
import { createVuePlugin } from "vite-plugin-vue2";
import viteSvgIcons from "vite-plugin-svg-icons";

export default defineConfig({
  plugins: [
    createVuePlugin({
      jsx: true.vueTemplateOptions: {
        compilerOptions: {
          whitespace: "condense"
        }
      }
    }),
    viteSvgIcons({
      iconDirs: [resolve(__dirname, "src/icon/svg")].symbolId: "icon-[name]"})]})Copy the code

Then, in the js file of the project entry, add a module to introduce:

// main.js.import "vite-plugin-svg-icons/register"; .Copy the code

Now that you’re done, you can enjoy it as if you were using the SVG Icon in a WebPack environment.

7. Alias configuration

Vite also supports an alias system similar to WebPack

import {defineConfig} from 'vite';

export default defineConfig {
  resolve: {
    extensions: [".mjs".".js".".ts".".jsx".".tsx".".json"].alias: [
      ...Object.keys(aliasConfig).map(key= > ({
        find: key,
        replacement: aliasConfig[key]
      })),
      { find: "timers".replacement: "timers-browserify" },
      { find: /ant-design-vue$/, replacement: "ant-design-vue/dist/antd.min"}}}]Copy the code

Here I extract the alias configuration into a common configuration file in case WebPack and Vite use the same alias configuration:

// aliasConfig

const {resolve} = require('path');
module.exports = {
  "@": resolve(__dirname, "src"),
  static: resolve(__dirname, "public/static"),
  YZT: resolve(__dirname, "src/views/YZT"),
  FZSC: resolve(__dirname, "src/views/FZSC"),
  JCYJ: resolve(__dirname, "src/views/JCYJ"),
  GHSS: resolve(__dirname, "src/views/GHSS"),
  FZBZ: resolve(__dirname, "src/views/FZBZ"),
  ZBMX: resolve(__dirname, "src/views/ZBMX"),
  YWXT: resolve(__dirname, "src/views/YWXT"),
  MXXT: resolve(__dirname, "src/views/MXXT"),
  JDGL: resolve(__dirname, "src/views/JDGL"),
  JDTB: resolve(__dirname, "src/views/JDTB")
};
Copy the code

Remaining problems to be solved

  1. Added to script tags in vuelang="jsx"After identification, change code cannot achieve hot update, associationissue
  2. Script tag added in vuelang="jsx"After identification, cannot use vetur formatting, associationissue
  3. I searched many issues about the loss of hot update status, but none of them found any record of similar problems. In addition, I could not reproduce the problem when I used demo for testing, but this problem did occur in the actual application of the project, and I haven’t found the clue yet
  4. Some dependency packages are used internallyrequireThe imported syntax causes Vite to fail to convert it into an ES module, and an error will be reported at runtime.

conclusion

Now my project has been successfully started using Vite, but the actual experience shows that there is no qualitative change in the start speed of the project. Because the project is too large and the number of modules requested to be loaded through the network is very large at the initial start, the speed has not been significantly improved. But in terms of thermal renewal, it is, as Utah says, surprisingly fast.

Now based on the case of vite successfully used in a production environment is also less, encountered some problems that have to do your own research, but there is no denying the fact that with webpack vite provides a completely different way, and on the compilation and hot update speed has great advantages, look forward to as soon as possible to perfect the relevant system, vite can more convenient application in the project.

Check out this awesome vite project, which contains a collection of recommended plug-ins and sample projects using Vite for a variety of technology stacks.