background

In 2021, the popularity of Vite in the front-end industry has not decreased. Recently, Vite2. X has been basically stable, so we decided to try it in the production environment.

After experiencing the silky Vue3 + Vite, the next target is React.

Most CSS modules of the React project in the front end team of the company adopt react-CSS-modules, while Vite does not support this method and only modularizes xx.module. CSS files.

From the Vite documentation: any CSS file with the suffix.module. CSS is considered a CSS modules file

If this problem is not addressed, accessing Vite will inevitably have an impact on the business code, and the larger the project, the more costly the changes.

We are not the only partners who have encountered the same problem. Someone mentioned this issue long ago, but it did not get much affirmation. Therefore, the issue questioner shared the plug-in he wrote, but found it was not quite the ideal effect after experiencing it.

So we decided to take matters into our own hands.

The solution

Option 1 – Modify node_modules

In Vite 2.7.4, for example, came to/node_modules/Vite/dist/node/chunks/dep – 4 b9dfa16. Js this file

The magic to change the source code

Find this section of code

const cssLangs = `\\.(css|less|sass|scss|styl|stylus|pcss|postcss)($|\\?) `;
const cssModuleRE = new RegExp(`\\.module${cssLangs}`);
Copy the code

As you can see, cssModuleRE is modular CSS file name matching rules, limits must be xx. The module. (CSS | less | sass | SCSS | styl | stylus | PCSS | postcss) form, so we need to start here, Make this matching rule configurable in vite.config.js.

We want something like WebPack Loader,

  • webpack.config.js

    module: {
      rules: [{test: /\.(css|less|sass|scss|styl|stylus)$/,
          loaders: ['style-loader/useable'.'css-loader'],},]; }Copy the code

    File name matching rules also use test field, reduce the mental burden of everyone

  • vite.config.js

    css: {
      modules: {
        test: /.(css|less|sass|scss|styl|stylus|pcss|postcss)/}},Copy the code

Then make the following changes to the source file:

const configFile = path__default.resolve(process.cwd(), 'vite.config.js');

const userCssModuleRe = configFile.css
  ? configFile.css.modules
    ? configFile.css.modules.test
    : null
  : null;

const cssModuleRE = userCssModuleRe || new RegExp(`\\.module${cssLangs}`);
Copy the code

That is, if CSS. Modules. test is configured, the user’s custom matching rules are used; otherwise, the original rules are used.

And then test it out,



patch

We know that node_modules changes are temporary and are not permanent and cannot be used by others.

So we need to use patch-package

Execute the following commands in sequence in the project root directory

yarn add patch-package

// Patch changes to 'node_modules'
npx patch-package vite
Copy the code

After executing the patches, you can see that a patch folder has been added to the root of the project, which contains a file called vite+2.7.4. Patch.

Patch information for Vite2.7.4.

inpackage.jsonAdd command topostinstallIn the future, vite2.7.4 will be automatically patched when installing dependencies

"scripts": {
  "postinstall":"patch-package"
},
Copy the code

Ps: Remember to change the map file, otherwise it is not convenient to locate the source file during error debugging

Option 2 – Vite plug-in

The above solution is feasible, but it is troublesome to do it in every project. In case of further changes, you have to synchronize each project again, which seems not the optimal solution.

So we decided to go with plan 2: Write a Vite plugin

Front knowledge

  1. The first thing to understand is that in Vite, everything is Javascript.

    The basic implementation principle of the Vite development environment is to start a server to intercept requests made by the ESM specification in the browser. Find the local corresponding file according to the request path, and return the ESM specification JavaScript code snippet after simple processing.

    For example, when we import ‘xx/ XXX /styles.css’, the server receives the request and returns js code instead of CSS resources.

    As shown in the figure above, export default __vite__css is exported to the import callers, while updateStyle at the top injects the style tag and the corresponding CSS snippet to the page, and import.meta.hot is used for hot updates.

  2. Second, you need to understand the difference between regular CSS files and modular CSS files in Vite

    For common CSS resources, export Default exports the CSS code, which is the same as updateStyle.

    For CSS Module resources, export Default exports the mapping of selectors after modularization.

    It looks something like this:

    {
      'wrapper': '_wrapper_uguz8_5'.'title': '_title_uguz8_5',}Copy the code

    The second parameter to updateStyle is the modularized CSS code

    ._wrapper_uguz8_5 {
      color: #0af;
    }
    
    ._title_uguz8_9 {
      font-size: 16px;
    }
    Copy the code

    This is the difference between modular and non-modular CSS.

  3. Plug-in API, the official website is very clear, we will not be a porter.

solution

Next we need to develop a solution to the problem.

First analysis source code Vite: CSS implementation ideas

// 1. If it is pure CSS and... Return directly without parsing
if ( lang === 'css'&&! postcssConfig && ! isModule && ! needInlineImport && ! hasUrl) {return { code }
}

// 2. Try to use CSS preprocessing language to generate pure CSS
const preprocessResult = awaitpreProcessor(code, ...) ;// 3. Postcss processing
const postcssResult = await (await import('postcss')).default(postcssPlugins).process(code, ...) ;Copy the code

To develop our plug-in along these lines:

  1. The path of the CSS Module that receives the user matches. If the path fails to match, the system does not process the path. The mechanics of the Vite plugin will eventually be handed over to Vite: CSS;

  2. Receive the postCSS configuration of the user, with postCSS-Modules plug-in to postCSS processing, here you can use postCSS-Modules plug-in to get CSS modularization before and after the selector relational mapping, is a JSON object;

  3. In the Enforce: ‘pre’ phase of the plugin, return directly to the code from the previous step;

  4. Manually define the contents of the file in the Enforce: ‘post’ phase of the plugin: inject CSS into the page, export the mapping of the modularized selector (json object from step 2), and complete the hot update.

The specific code has been packaged and published to the NPM repository and can be used by installing Viet-plugin-style-modules.

Installation starts with a bit of verbosity

The installation

npm install vite-plugin-style-modules
Copy the code

The use of plug-in

import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
import viteCssModule from 'vite-plugin-style-modules';
// Support ESM and CommonJS
// const viteCssModule = require('vite-plugin-style-modules')

export default defineConfig({
  plugins: [react(), viteCssModule()],
});
Copy the code

Start the project and test it

conclusion

At ordinary times, it is unavoidable for us to use third-party dependency in situations that are not applicable to our own scenarios. However, open source authors are usually too busy or have different ideas to provide support in a short period of time. At this time, we need to make ends meet by ourselves.

Both source forking and patch-package are good solutions. However, patching is safe and fast, but there will be an extra file and command, if you don’t like patching you can try using plug-ins.

When it comes to Vite, there is no doubt that Vite + Vue3 is a good user experience, but the React experience is still a little bit worse. However, this does not affect the highlights of Vite itself, and it is still worth studying in depth.

At present, Vite’s ecology is constantly improving, and our team has also started to try. More related articles will be published in the future, if you are interested, pay more attention to it

Related articles

Vite zero base speed entry

Contribution from: Jarvis