In the last section, we simply implemented the packaging function of WebPack, and some basic configuration of WebPack, packaging commands and different modes of packaging in a simple understanding, but also understand the meaning of the console output information. The next few sections will take a look at how WebPack is compiled and packaged for different types of resources and what needs to be done during the packaging process.

Js Compatibility processing

In the previous section, we wrote a simple JS code to use as a packaging example and get a feel for js packaging:

const add = (x, y) = > x + y
console.log(add(1.2))
Copy the code

We captured the packaged code:

eval("const add = (x, y) => x + y; \n\nconsole.log(add(1, 2)); \n\n\n//# sourceURL=webpack:///./src/js/index.js?");
Copy the code

As you can see, in the packaged code, add is defined as const instead of var, which is the most compatible. At present, in our daily development process is basically using ES6 syntax, but some versions of the lower browser is not fully compatible with ES6 syntax, at this time we have to do compatibility processing to JS code. To make js compatible, we need two tools: babel-loader and @babel/core. Let’s start by installing these two tools:

yarn add babel-loader @babel/core
Copy the code

How to use it after installation? Here we need to use loader, one of the five basic concepts of Webpack. Based on the previous configuration, we add the loader configuration. Loader-related configuration is written to the module of the configuration file:

// Introduce node's path module
const { resolve } = require('path')

module.exports = {
    // Import file
    entry: './src/index.js'.// File output configuration
    output: {
    	// The output file name
        // set [name] to the name of the entry file
    	filename: 'js/[name].js'.// The path to the output file
        // The output is in the build directory of the root directory. If you are not familiar with __dirname, you can check out nodejs
        path: resolve(__dirname, 'build')},// Used to configure loader
    module: {
      rules: [{// Match.js files
          test: /\.js$/.// Node_modules need to be ignored, otherwise the contents will also be checked, which will reduce the performance of packaging
          exclude: /node_modules/./ / use the Babel - loader
          loader: 'babel-loader'.// Loader configuration
          options: {}}]},// Used to configure plug-in information
    plugins: [].// Enable development mode
    mode: 'development'
}
Copy the code

After the configuration is complete, run yarn Build :dev and the result is the same as before. The ES6 code has not been converted. This is because we haven’t “told” Babel-Loader how to do compatibility processing. How to deal with js compatibility? Let’s move on.

There are three ways to handle JS compatibility

There are three main ways for JS compatibility processing, let’s take a look at it in turn.

Method 1: Basic compatibility processing

Basic compatibility handling is the ability to handle basic JS compatibility issues, such as conversion of const/let definitions in JS, conversion of arrow functions, etc. Here we need a tool: @babel/preset-env. This tool helps us deal with some basic JS compatibility issues.

yarn add -D @babel/preset-env
Copy the code

After the installation is complete, we add the following configuration to babel-loader options:

/ *... * /
module: {
  rules: [{// Match.js files
      test: /\.js$/.// Node_modules need to be ignored, otherwise the contents will also be checked, which will reduce the performance of packaging
      exclude: /node_modules/./ / use the Babel - loader
      loader: 'babel-loader'.// Loader configuration
      options: {
        presets: ['@babel/preset-env']}}]},/ *... * /
Copy the code

After the configuration is complete, I go back to the package command and intercept the code we have written:

eval("var add = function add(x, y) {\n return x + y; \n}; \n\nconsole.log(add(1, 2)); \n\n//# sourceURL=webpack:///./src/js/index.js?");
Copy the code

At this point, we can see that the const declaration in the code has been converted to var, and the original arrow function has been converted to normal function form. The Loader has taken effect. In the introduction of this compatibility approach, it was mentioned that this approach can only handle some basic JS problems. What happens if we add something slightly more complex to the file, like a Promise, and then perform the packaging?

const add = (x, y) = > x + y;
console.log(add(1.2));

const promiseHandle = new Promise((resolve, reject) = > {
  setTimeout(() = > {
    resolve('complete');
  }, 1000)})console.log(promiseHandle);
Copy the code

After executing the package command, let’s take a look at the code we wrote:

eval("var add = function add(x, y) {\n return x + y; \n}; \n\nconsole.log(add(1, 2)); \nvar promiseHandle = new Promise(function (resolvCopy the code

As you can see, definitions such as const and arrow functions are translated into ES5 syntax, but the contents of promises are not, and there are no other ways to handle promises in the code. So what do we do now? Don’t panic, let’s look at the second js compatibility handling method.

Method 2: @babel/polyfill

As mentioned, we need to introduce another tool: @babel/polyfill. This compatibility method is more comprehensive, it can do compatibility processing to all JS.

yarn add -D @babel/polyfill
Copy the code

This tool is relatively simple to use, after installation, just need to use in the JS file can be introduced. Let’s introduce this package based on the js file we just created.

import '@babel/polyfill'

const add = (x, y) = > x + y;
console.log(add(1.2));

const promiseHandle = new Promise((resolve, reject) = > {
  setTimeout(() = > {
    resolve('complete');
  }, 1000)})console.log(promiseHandle);
Copy the code

Execute the package command again, at this time we open the packed JS file, we will find that the JS file introduced a large number of polyfill to deal with JS compatibility, including the compatible processing for Promise (because the content is too long, I will not paste the code here, I recommend friends to try ~). This approach solves the problem in our first compatibility process. However, using this approach raises another problem. Let’s look at the output of the packaged file:

It wasn’t hard to see that the introduction of a large number of polyfills caused our file, which was only a few k, to swell to 511K, because the introduction of polyfills covered all the compatibility processing, which was obviously not what we wanted. And the fact is we don’t need this much compatibility processing. So how do we deal with this problem? Let’s look at the third compatibility solution.

Method 3: core-js

To put it bluntly, in order to solve the drawbacks of method 2, the core idea is to introduce as needed. At this point, we need to use code-js. @babel/polyfill can also be found in @babel/polyfill. If you are interested, you can find it in the generated file.

yarn add -D core-js
Copy the code

To use it, we need to modify the babel-Loader configuration a little more:

/ *... * /
module: {
  rules: [{// Match.js files
      test: /\.js$/.// Node_modules need to be ignored, otherwise the contents will also be checked, which will reduce the performance of packaging
      exclude: /node_modules/./ / use the Babel - loader
      loader: 'babel-loader'.// Loader configuration
      options: {
        "presets": [["@babel/preset-env",
            {
              // specify load on demand
              "useBuiltIns": "usage".// Specify the version of core-js
              "corejs": {
                "version": 3
              },
              // Specify compatible browser versions
              "targets": {
                "chrome": "60"."firefox": "60"."ie": "9"."safari": "10"."edge": "17"}}]]}}]},/ *... * /
Copy the code

At this point, we specify load on demand, specify version 3 of coreJs, and specify the specific browser version to be compatible with.

Note: when using this method, you need to comment out @babel/polyfill from the second method. They are not shared!!

Then we pack again:

After repackaging, we can see that the compatibility processing of Promise is introduced and the file size is reduced to 105K, which is obviously better than the second method. If you feel that this configuration is a bit redundant, it is not elegant enough. We can also move the presets configuration to the.babelrc file in the root directory:

{
  "presets": [["@babel/preset-env",
      {
        // specify load on demand
        "useBuiltIns": "usage".// Specify the version of core-js
        "corejs": {
          "version": 3
        },
        // Specify compatible browser versions
        "targets": {
          "chrome": "60"."firefox": "60"."ie": "9"."safari": "10"."edge": "17"}}]]}Copy the code

At this point, JS packaging and processing compatible several ways have been introduced. Of course, loader configuration is more than this. In the future development process, you can query the corresponding Loader document according to the project needs to customize the configuration suitable for your own project.

After dealing with the compatibility of JS, how should the development team deal with the unification of JS style in daily development? Some easy to make low-level mistakes and how to avoid it? In the next chapter, we will discuss how to deal with these problems