Webpack catalog

  1. The core configuration of webpack5
  2. Webpack5 modularity principle
  3. Webpackage 5 with Babel/ESlint/ browser compatibility
  4. Webpack5 performance optimization
  5. Webpack5 Loader and Plugin implementation
  6. Webpack5 core source code analysis

Babel usage

We rarely touch Babel directly in real development, but Babel is essential for front-end development. Babel is a toolchain that, like PostCSS, converts post-ECMAScript syntax code to ES5 code, including syntax conversions, source conversions, and Polyfill implementations.

Babel core installation

If we want to install @babel/cli on the command line, install NPM install @babel/core @babel/cli. NPM install @babel/preset-env -d if you want to use Babel, you need to install Bable. NPM install @babel/preset-env -d

Write a main.js file

Execute the command

npx babel src --out-dir dist --presets=@babel/preset-env 
Copy the code

Generate results

You can see that the output file has been converted to ES5 syntax for us.

babel-loader

NPM install babel-loader -d to configure Babel. Configuration is as follows

{
  test: /\.(j|t)sx? $/,
  exclude: /node_modules/,
  use: [
    {
      loader: 'babel-loader'.options: {
        presets: ['@babel/preset-env', {
          targets: 'last 2 version'  // Configure the targets attribute to override browserslist. Browserslist is recommended}]}}Copy the code

Babel configuration file

In addition to configuring the default Babel plug-in in the loader, the general situation is to extract a separate Babel configuration file

  • Json (or.js,.cjs,.mjs) files
  • Json (or. Babelrc,.js,.cjs,.mjs) files

Json /js is recommended. Let’s create a new babel.config.json

// babel.config.json
{
  "presets": [["@babel/preset-env"]]}Copy the code

Polyfill

Now let’s change main.js, use a Promise Api, and repackage it

// main.js
const a= '123'
const fn = Promise.resolve(3)
fn.then(res= > {
  console.log(res)
})
Copy the code

Now that you can see that the packaging doesn’t help us transform promises, it’s possible that promises won’t be supported on older browsers or on older versions. So we come up with a Polyfill thing that uses some syntactic new features (Promise, Generator, Symbol, etc., and instance methods array.prototype.includes, etc.) that will fill us in. The idea is to transform these apis and implement these new features using syntax apis that older browsers know. So how do we use it.

Previously we did this by installing the @babel/ Polyfill library, which we introduced in between at the top of the entry file. We don’t use it anymore. After Babel7.4.0, core-js and Regenerator-Runtime dependencies were introduced to provide polyfill. Install NPM install core-js regenerator-Runtime. Additional attributes need to be added after @babel/preset-env

  • useBuiltInsWhat is the way to use polyfill, which has three valuesFalse, Usage, entry
    • false: Do not use any polyfill related code
    • usage: Any polyfills you need in your code are automatically referenced to the relevant API(recommended)
    • entry: Which polyfills are required in the code, you need to add in the entryimport “core-js/stable”; import “regenerator-runtime/runtime”;The volume will go up
  • corejsSet:corejs3.x is the most commonly used version
    • In addition, Corejs can set whether features in the proposal phase are supported or not
    • Set the attributes of the proposals to true

The babel.config.json configuration is shown below

// babel.config.json
{
  "presets": [["@babel/preset-env", {
      "useBuiltIns": "usage"."corejs": 3}}]]Copy the code

Plugin-transform-runtime

Except that polyfill is global, so if we use Babel Polyfill when we’re writing our library, we might contaminate our global code. The plugin @babel/ plugin-transform-Runtime is recommended for polyfill. Install NPM install@babel /plugin-transform-runtime -d Configuration is as follows

// babel.config.json
{
  "presets": [["@babel/preset-env"]],"plugins": [["@babel/plugin-transform-runtime", {
      "corejs": 3}}]]Copy the code

NPM install — save@babel /runtime-corejs3

Jsx support

NPM install @babel/ preth-react-d when writing React, JSX syntax will be used, Babel official provides JSX preset @babel/ preth-react, NPM install @babel/ preth-react-d, as shown below

// babel.config.json
{
  "presets": [["@babel/preset-env", {
      "useBuiltIns": "usage"."corejs": 3
    }],
    ["@babel/preset-react"]]}Copy the code

Typescript support

We will also use TypeScript for development in our projects, and TypeScript code needs to be converted to JavaScript code, as we used to use TS-Loader to process TS files. But Babel also provides @babel/preset-typescript. So what’s the difference between these two? Let’s use them separately.

ts-loader

NPM install ts-loader -d

{
  test: /\.(j|t)sx? $/,
  exclude: /node_modules/,
  use: [
    'ts-loader']}Copy the code

We’ll start with TSC –init, because when I download ts-loader I also install typescrit by default. Now initialize the tsconfig.json file and change main.js to main.ts

// main.ts
const a: string = '123'
const fn = Promise.resolve(3)
fn.then(res => {
  console.log(res)
})
Copy the code

After repackaging, we found that the TS-Loader did not help us achieve polyfill

@babel/preset-typescript

Now let’s use @babel/preset-typescript. Install NPM install @babel/preset-typescript -d as follows

// webpack
{
  test: /\.(j|t)sx? $/,
  exclude: /node_modules/,
  use: [
    'babel-loader']}Copy the code
// babel.config.json
{
  "presets": [["@babel/preset-env", {
      "useBuiltIns": "usage"."corejs": 3
    }],
    ["@babel/preset-react"],
    ["@babel/preset-typescript"]]}Copy the code

Now try repacking

Now we can see that polyfill has been provided for us

The difference between TS-Loader and @babel/preset-typescript

So should we choose TS-Loader or Babel-Loader in development? Let’s distinguish between the two

  • ts-loader(TypeScript Compiler)
    • To compile TypeScript directly, you can only convert TS to JS
    • If we also want to add a corresponding polyfill in the process, then ts-Loader is powerless
    • We need Babel to complete the filling function of polyfill
  • babel-loader(Babel)
    • To compile TypeScript directly, you can convert TS to JS, and you can polyfill
    • However, babel-Loader does not detect type errors during compilation

So how do we do polyfill and provide type error detection? First of all, we definitely want to use Babel’s default. We can add a command to the script of package.json

"script" {
  "ts-check": "tsc --noEmit"
 }
Copy the code

When we perform at compile time once the command, but the individual is recommended to use a ForkTsCheckerWebpackPlugin plug-in, it can provide type error detection in developing and building environment, NPM install fork-ts-checker-webpack-plugin -d We can configure it as follows

// webpack
const ForkTsCheckerWebpackPlugin = require('fork-ts-checker-webpack-plugin')
/ /...
{
  plugins: [
    / /...
    new ForkTsCheckerWebpackPlugin({
      async: false,]}})Copy the code

So in our solution is to compile ts file @ Babel/preset – typescript and ForkTsCheckerWebpackPlugin the combination of the two plug-ins.

How Babel builds

We use a graph to see the entire process of compiling Babel

What the Babel compiler does is convert our source code into another piece of code that the browser can recognize directly. Workflow summary:

  1. Parsing
  2. Transformation phase
  3. Code Generation

ESlint configuration

ESLint is a static code analysis tool that helps us establish consistent team code specifications in projects, maintain correct and consistent code styles, and improve code readability and maintainability. And ESLint’s rules are configurable, so you can customize your own rules. Install ESlint, NPM install ESlint -d

Now let’s configure our ESLint rule, we’ll initialize ESLint first

npx eslint --init
Copy the code

Generate the.eslintrc.js file

This section describes the main properties of the configuration file

  • env: running environment, such as browser, and we will use es2021(corresponding ecmaVersion is 12) syntax
  • extends: Can extend the current configuration to inherit configuration information from other strings or arrays (multiple)
  • parserOptions: You can specify the version of ESMAScript and the type of sourceType
  • parserThe default is Espree (also a JS Parser for ESLint), and if we need to compile other things like TypeScript, we need to specify the interpreter
  • plugins: Specifies the plug-in we are using
  • rules: User-defined rules

The corresponding rules can be found on ESlint’s Official website

Now let’s test it with a command-line tool, modifying the main.js file

const a= '123'
const fn = () = > {
  return a
}
console.log(fn(a))
Copy the code

Run the eslint command

npx eslint ./src/main.js 
Copy the code

We can see that the command tells us that the string needs to use single quotes, and it tells us that we can fix it using –fix. In addition to checking the code specification through the command, we can also check it through the vscode plug-in

VSCode ESlint

We searched for ESlint in the plugin store, as shown in the image below

It checks against the.eslintrc.js configuration file of the current directory. If it doesn’t have that configuration file, it has a default configuration specification. Now looking at our main.js file, the editor has helped us wave out the irregularities

ESlint Loader

Eslint-loader: Eslint-loader: Eslint-loader: Eslint-loader: Eslint-loader: Eslint-loader: Eslint-loader: Eslint-loader Install NPM install eslint-loader -d

{
  test: /\.(j|t)sx? $/,
  exclude: /node_modules/,
  use: [
    'babel-loader'.'eslint-loader']}Copy the code

Auto repair save

ESLint will help you with errors (or warnings), but it won’t help you with automatic fixes; you’ll need to manually add the –fix parameter

eslint src --fix
Copy the code

Prettier in development, where we want to fix the problem when the file is saved, prettier could be used as an alternative tool. Prettier is searched for in the VSCode plugin store

However, it is not very popular to use this plug-in in real projects, and this habit may vary from person to person.

Problems encountered

TypeError: Cannot read property ‘getFormatter’ of undefined TypeError: Cannot read property ‘getFormatter’ of undefined TypeError: Cannot read property ‘getFormatter’ of undefined “^ 8.11.0 eslint -“, “loader” : “^ 4.0.2”,

After going through the debugger, I found that the esLint-Loader getOptions. Js references the ESLint CLIEngine

But the CLIEngine export is undefined, so I went to check eslint library reference by default. / lib/API. Js, found no CLIEngine derived.

Eslint /lib/cli-engine/index.js Eslint /lib/cli-engine/index.js in eslint-loader options

It says that we cannot export modules in ESLint through this path. I looked at esLint’s package.json and found that it imposes export restrictions

So we can configure it this way

// webpack
{
  test: /\.(j|t)sx? $/,
  exclude: /node_modules/,
  use: [
    'babel-loader', 
    {
      loader: 'eslint-loader'.options: {
        eslintPath: 'eslint/cli-engine'}}}]Copy the code

Exports of package.json in esLint add “./cli-engine”: “./lib/cli-engine/index.js”

Now let’s repack

As you can see, it’s working, but since we changed it to include esLint files in node_module, this was never a good idea, so we ended up deleting the original ESLint and lowering the ESLint version to 7.32.0. After installing it, repack it and it’s all ok now.

Browser compatibility

Browserslist

By compatibility, I mean features supported by different browsers: For example, the compatibility between CSS features and JS syntax, while we use Chrome, Safari, IE, Edge, Chrome for Android, UC Browser, QQ Browser and so on in the market. You can usually see this information in package.json in some scaffolding

> 1%
last 2 versions
not dead
Copy the code

> 1% means more browsers than the market share. The browser versions and market share can be found at caniuse.com/usage-table… View, as shown below

And we need to be compatible with browsers that are more than 1%, so we need to use Browserslist, which is a tool that shares the configuration of the target browser for the current condition, and when we use Browserslist to configure the conditions that we need to be compatible with, We will use the following compatibility tools to help us with CSS and JS compatibility.

  • Autoprefixer
  • Babel
  • postcss-preset-env
  • eslint-plugin-compat
  • stylelint-no-unsupported-browser-features
  • postcss-normalize
  • obsolete-webpack-plugin 

Browserlist is automatically installed when webPack is installed. You can use the browserslist command to view the list of target browsers.

npx browserslist ">1%, last 2 version, not dead"
Copy the code

Note: browserslist looks for the. Browserslistrc configuration file if it is not followed by a condition, otherwise the default condition is used.

These are the target browsers that meet our criteria and need to be compatible.

There are two ways to configure Browserslist:

  • Package. The json configuration
  • independent.browserslistrcConfiguration file

Package. The json configuration

{
  / /...
  "browserslist": [    
    "1%" >."last 2 version"."not dead"]}Copy the code

Browserlistrc file configuration, in the project directory to create a. Browserslistrc file

Note: Conditions are union when comma or space or OR is used between them. Use and as the intersection between conditions. Not is not included when not is used between conditions.

See Github Address for browserslist configuration details

Postcss

After confirming the target browser that needs to be compatible, we need to use tools to do compatibility processing. First, we need to introduce PostCSS.

PostCSS is a JavaScript style conversion tool, this tool can help us to do some CSS conversion and adaptation, such as automatically add browser prefix, CSS style reset, but to achieve these tools, we need to use PostCSS corresponding plug-in, These plug-ins are the compatibility tools described above.

You can use the command line to test the autoprefixer plug-in under the Postcss tool. You need to install postCSS, postCSS -cli, and run NPM intall postcss postCSS -cli -d to install postcss.

Using terminal commands requires the postCSS-CLI dependency package, and converting requires postCSS.

After that, we need to download the plug-in autoprefixer for compatibility processing. Postcss needs to specify a plug-in for processing, NPM Intall Autoprefixer -d. After installation, we can use the terminal command line to test.

npx postcss --use autoprefixer -o result.css ./src/css/index.css	
Copy the code

After executing the command

You can see that the output result.css file has automatically added compatibility code to our style properties. But in a real project it is impossible to use commands to do the transformation, so we need a loader to process the style files, and WebPack uses postCSS-loader to do that. Install NPM install postCSs-loader -d.

Add postCSs-loader to webpack.config.js.

{{test: /.css$/i,     
    use: [      
      'style-loader'.'css-loader',      
      {         
        loader: 'postcss-loader'.options: {           
          postcssOptions: {             
            plugins: [              
              'autoprefixer' // Require ('autoprefixer')}}}]}}Copy the code

But we use postCSS-loader not only in CSS files, but also in other files such as preprocessor less. This becomes tedious when we repeatedly write postCSs-loader. At this point we can extract the postcss.config.js file and put the configuration in that file, whereas the Webpack configuration becomes

{  
  rules: [{test: /.css$/i,       
      use: [        
        'style-loader'.'css-loader'.'postcss-loader'] {},test: /.less$/i,       
      use: [        
        'style-loader'.'css-loader'.'postcss-loader'.'less-loader']]}}Copy the code

Then separate out the postcss.config.js file

Postcss is almost ready now, and autoprefixer is no longer in use, we recommend postCSs-preset -env more. This plugin includes autoprefixer and can preset some modern CSS features, Convert to CSS that most browsers know, and add the required polyfills depending on the target browser or runtime environment. Install NPM install postCSs-env -d. Modify the postcss.config.js file

module.exports = {  
  plugins: [    
    'postcss-preset-env']}Copy the code

For example, the style attribute color: #28f2d334, when set to 8 bits, some browsers will recognize it and some won’t. Postcss-preset -env will help us with compatibility processing

After repackaging the build, you can see that the color attribute value has been converted to RGBA format, and the prefix compatibility processing for the previous attribute is done.

Postcss-loader and CSS-loader do not execute the postCSs-loader again when we import other CSS files from the @import CSS file. So we need to add configuration in CSS-Loader

{  
  rules: [{test: /.css$/i,       
      use: [        
        'style-loader',         
        {          
          loader: 'css-loader'.options: {            
            importLoaders: 1  // Importing CSS from CSS does not start with postCSs-loader but with CSS-loader conversion, so this configuration ensures that loader conversion starts one layer up}},'postcss-loader'] {},test: /.less$/i,       
      use: [        
        'style-loader',         
        {          
          loader: 'css-loader'.options: {            
            importLoaders: 2  // Importing CSS from CSS does not start with postCSs-loader but with CSS-loader conversion, so this configuration ensures that loader conversion starts one layer up}},'postcss-loader'.'less-loader']]}}Copy the code