This is the 10th day of my participation in the August More Text Challenge.

Modular evolution process

Stage1. File partition mode

By convention

disadvantages

  • There is no private, independent space outside the module that can be accessed and modified at will, contaminating the global scope
  • Too many modules can cause naming conflicts
  • Unable to manage dependencies between modules

Stage2. Namespace mode

  • Naming conflicts can be reduced
  • But without private space, it is still possible to modify it externally
  • Dependencies between modules are not resolved

Stage3.iife (Execute function immediately)

Use immediate functions to provide private space for modules

Modular specification

Modular standard + module loader

Commonjs specification (nodeJS specification standard)

  • A file is a module
  • Each module has a separate scope
  • Exports members through module.exports
  • Load the module via the require function

CommonJS loads modules in synchronous mode. Modules are loaded at startup and do not need to be loaded during execution. This can lead to inefficiencies in the browser. The browser doesn’t use the commonJS specification directly because it loads a lot of content at startup

Asynchronous Moudle Definition (AMD) module Definition specification for Asynchronous

Require.js module loader

Most third-party libraries currently support the AMD specification

  • AMD is complicated to use
  • Module JS requests are frequent

Taobao launches Sea.js +CMD

Modularity standards specification best practices

  • ES Modules- The most popular module development specification
  • CommonJS

ES Module features

  • 1. The ESM automatically adopts strict mode and ignores’ use strict ‘
  • 2. Each ESM operates in a separate private scope
  • 3.ESM requests external JS modules through CORS
  • 4.ESM’s Script tag delays script execution equivalent to the defer attribute
<! Add type = module to script to execute js code as ES Module standard -->
    <script type="module">
        console.log("this is a module")
    </script>
    <! -- 1.ESM automatically adopts strict mode ignore 'use strict' -->
    <script type="module">
        /** * Strict mode cannot use this directly in the global scope ** non-strict mode this refers to global object */
        console.log(this)
    </script>
    <! -- 2. Each ESM works in a separate private scope
    <script type="module">
        var foo= '123'
        console.log(foo)
    </script>
    <script type="module">
        // console.log(foo)
    </script>
    <! -- 3. ESM requests external JS modules in the form of CORS, which requires us to request that our module is not the same origin address, requires that the response header of the requested JS module provide a valid CORS header. CORS does not support file access. So you have to use HTTP server to make the page work -->
     <script type="module" src="https://libs.baidu.com/jquery/2.0.0/jquery.min.js"></script>

     <! The ESM script tag will defer script execution equivalent to the defer attribute -->
     <script type="module" src="demo.js"></script>
     <p>This text will not be delayed by the introduction above</p>
Copy the code

ESM import and export

  • use

    npm install browser-sync -g
    // Monitor file updates to automatically update the interface display
    browser-sync . --files **/*.js
    Copy the code
    /** * Each module of the ESM has its own private scope ** all members of the module cannot be accessed outside the module */
    export var name = 'foo module'
    // You can modify functions
    export function hello() {}
    // You can modify class
    export class Person{}
    Copy the code
  • You can also export centrally what the current module is going to expose, which is more intuitive

    var name = 'foo module'
    function hello() {}class Person {}export {
        name, hello, Person
    }
    Copy the code
  • The as keyword is used to rename the exported content

    export {
    name as fooName, hello, Person
    }
    Copy the code
  • Rename the exported member to default, which is the default member of the current module

    export {
        name as default, hello, Person
    }
    Copy the code

    You have to rename it when you import it

    // Since default is a keyword, it cannot be used as a variable, so use as to rename it
    import { default as fooName } from './module.js'
    console.log(fooName)
    Copy the code
  • Export Default Name is the default export mode of the current module

    When you import it, you can take it as you want

    var name = 'foo module'
    export default name
    Copy the code
    import someName from './module.js'
    Copy the code

Precautions for export

  • 1. {} in ES Module is a fixed syntax, which is to directly extract Module exported members
  • 2. Importing members does not mean copying a copy, but directly importing the reference address of module members. That is to say, variables obtained by import and variables imported by export are in the same space in memory. Once a member is modified in the module, it will be modified here as well
  • 3. The imported module member variables are read-only

Import Precautions

  • 1. From must be the complete file path, do not omit the.js extension, which is different from commonJS

    import { name } from './module'
    import { name } from './module.js'
    console.log(name)
    Copy the code
  • 2. The INDEX. Js file in the ESM folder cannot be loaded by default

    import { lowercase } from './utils'
    import { lowercase } from './utils/index.js'
    console.log(lowercase('HHH'))
    Copy the code
  • 3. The./ of the relative path in web resources can be omitted, but in ESM./ cannot be omitted.

    The default is to load a third-party module, which is the same as commonJS

    You can also access it using an absolute path starting with /

    We can also access our modules using the full URL, which can refer directly to some files on the CDN

    import { name } from 'module.js'
    import { name } from './module.js'
    import { name } from '/04-import/module.js'
    import { name } from 'http://localhost:3000/04-import/module.js'
    console.log(name)
    Copy the code
  • 4. If you just perform a module without extracting a member, you can use the following two methods

    import {} from './module.js'
    import './module.js'
    Copy the code
  • 5. If you import too many members, you can use * to import all the members and put them into the mod

    Use mod. Directly to access

    import * as mod from './module.js'
    console.log(mod)
    Copy the code

Export import member

/** * these members are not accessible in the current scope */
export { foo, bar } from './module.js'
Copy the code

ESM in Browser Polyfill compatible solution

Only suitable for use in the development phase

<script nomodule src="https://unpkg.com/[email protected]/dist/polyfill.min.js"></script>
  <script nomodule src="https://unpkg.com/[email protected]/dist/babel-browser-build.js"></script>
  <script nomodule src="https://unpkg.com/[email protected]/dist/browser-es-module-loader.js"></script>
Copy the code

ESM in Nodejs support

  • Change the file extension from.js to.mjs;

  • An additional –experimental-modules parameter is required for startup;

  • The ESM can load built-in modules

    import fs from 'fs'
    fs.writeFileSync('./foo.txt'.'es modules works')
    Copy the code
  • Third-party NPM modules can also be loaded using the ESM

    import _ from 'lodash'
    console.log(_.camelCase('ES MOdule'))
    Copy the code
  • Extracting members from third-party modules is not supported because third-party modules export default members

import {camelCase} from 'lodash'
console.log(camelCase('ES MOdule'))
//The requested module 'lodash' does not provide an export named 'camelCase'
Copy the code
  • The built-in module is compatible with the ESM member extraction method

    import { writeFileSync } from 'fs'
    writeFileSync('bar.txt'.'To test extracting built-in members')
    Copy the code

Load the CommonJS module in ESM

  • CommonJS modules can be imported into ES Module
import mod from './commonjs.js'
console.log(mod)
Copy the code
  • The CommonJS module always exports only one default member, which means that the ESM can only export by importing default members
  • Members cannot be extracted directly, and note that import is not a structural export object
import { foo } from './commonjs.js'
console.log(foo)
//The requested module './commonjs.js' does not provide an export named 'foo'
Copy the code
  • You cannot load ES Module via require in CommonJS modules

ESM in NodeJs differs from CommonJS

  • ESM does not have module global members require, module, exports, __filename, __dirname, etc
  • Require, Module, exports naturally use import and export instead
  • “__filename” and “__dirname” are obtained from the meta attribute of the import object

Further support for the new version of ESM in NodeJs

  • Add the Type attribute to package.json

    {
      "type": "module"
    }
    // All js files in the current folder are automatically run in ESM mode, but commonJS files must use the.cjs extension, otherwise ESM mode is run by default
    Copy the code

Commonly used modular packaging tools

Existing problems

  • The ESM has environmental compatibility problems
  • Too many module files and frequent network requests
  • All front-end resources need to be modular

The idea of packaging tools

  • Code with the new feature compiles into code that even a browser can run
  • The development phase is modular and the production environment is packaged into a single file
  • Supports different resource types

Webpack module packer

Packaging tools address front-end modularity as a whole, not just JavaScript modularity

After WebPack 4, it supports the direct start of packaging with zero configuration. In the way of packaging, the index.js under SRC will be taken as the entry of packaging according to the convention, and the packaged files will be stored in main.js under dist

Webpack working mode

This property has three values: production, Development, and None.

  • In production mode, Webpack automatically optimizes packaging results;
  • In development mode, Webpack automatically optimizes the packaging speed and adds some assistance during debugging.
  • In None mode, Webpack runs raw packaging without any extra processing;

Fold vs Code command+k command+0

Webpack resource file loaded

Webpack packages JS files by default, so an error occurs when you package CSS files directly

  module: {// Configure loading rules for other resource modules
    rules: [// Two attributes need to be configured
      {
        // A regular expression used to match the file paths of packaged files
        test:/.css$/.// Specifies the loader to use for the matching path
        If use is configured with multiple Loaders, execute them from back to front */
        use:[
          'style-loader'.'css-loader']]}}Copy the code

Webpack imports the resource module

  • Dynamically import resource files as required by your code

Dataurl type -> Binaries: images and fonts

url-loader

  • Use Data URLs for small files to reduce the number of requests

  • Large files are separately extracted and stored to improve loading speed

          {
            test: /.png$/,
            use: {
              loader: 'url-loader'.// To use url-loader, you still need to add file-loader. Files exceeding the size limit will automatically load file-loader
              options: {
                // Files larger than 10KB are stored separately. Files smaller than 10KC are converted to DataURLs embedded in the code
                limit: 10 * 1024 // The length is bytes so *1024 10 KB}}}Copy the code

Webpack often uses loader classifications

  • The compiler transforms the CSS -loader, which converts resource modules into javascript code to run CSS through JS
  • File action type loader, which copies the loaded resource to the output directory and exports the access path of the resource
  • Code check class loaders, used for the same code style, generally do not modify the code

Webpack processing ES2015

Import and export are handled because module packaging is required, and other ES6 features in the code cannot be converted

The corresponding Loader must be installed separately

yarn add babel-loader @babel-core @babel/preset-env --dev
Copy the code
  • Webpack is just a packaging tool
  • The loader can be used to compile the transformation code

Webpack module loading mode

  • Follow the ESM standard import statement

  • Follow the CommonJS standard require function

    // If you import an ESM using the require function, the default export of the ESM needs to be obtained using the default attribute of the import result
    const creatHeadding = require("./heading.js").default
    Copy the code
  • Follow AMD standard define function and require function

Non-javascript loaded by the loader also triggers resource loading

  • The @import directive and URL in the style code
  • The SRC attribute of the image tag in HTML code
      {
        test: /.html$/,
        use: {
          loader: 'html-loader'.options: {
            // By default, only img SRC triggers resource loading in Webpack, so arrts is used to configure the triggered properties
            attrs: ['img:src'.'a:href']}}}Copy the code

How the core of WebPack works

A typical project will have various code and resource files scattered around. Webpack will find the package entry of the project based on our configuration, and this file will usually be a JavaScript file. Then, according to the import or require appearing in this file, the dependency modules are resolved, and the corresponding dependencies of each resource module are resolved. Then, the dependency tree of the entire project is formed. With this dependency tree, WebPack recurses this dependency tree and finds the resource file corresponding to each node. According to the rules attribute in the configuration file, we will find the loader corresponding to the module, and then hand it to the loader to load the module. Finally, we will put the loaded result into the bundle.js packaging result, so as to realize the packaging of the whole project.

The Loader mechanism is at the heart of Webpack throughout the process

Webpack develops a loader

  • The working process of Webpack is similar to a pipeline, its loading process can use multiple loaders, the final result of this working pipeline must be a piece of JavaScript code
  • You can either have the Loader return JavaScript directly or use another loader to return JavaScript

Webpack plugin mechanism

  • Enhance the ability of Webpack in project automation, loader focuses on the loading of resource modules, Plugin solves other automation work
  • For example: clear dist directory, copy static files to output directory, compress output code

Automatically generate HTML plug-ins

yarn add html-webpack-plugin --dev
Copy the code
  • Automatically generate HTML files

    plugins:[
      new HtmlWebpackPugin()
    ]
    Copy the code
  • Generate multiple HTML files

/ / webpack. Config. Js file
/ / create index. HTML
new HtmlWebpackPlugin({
  title:'the index file'.meta: {viewport:'width = device-width'
  },
  // Configure the index.html template
  / * use lodash template syntax configuration template < h1 > < % = htmlWebpackPlugin. Options. The title % > < / h1 > * /
  template:'./src/index.html'
})
/ / create the about HTML
new HtmlWebpackPlugin({
  //filename specifies the output filename
  filename:'about.html'
})
Copy the code

Static file output

The copy-webpack-plugin can be used

const CopyWebpackPlugin = require('copy-webpack-plugin')

module.export={
  plugins: [new CopywebpackPlugin([
      // This can be a file directory or a wildcard
      //'public/**',
      'public'])]}Copy the code

The WebPack plug-in is a function implementation extension that hangs in a hook that declares the cycle

Webpack development experience

  • Run it using HTTP Serve
  • Auto-compile + auto-refresh
  • SourceMap support is provided

Webpoack auto-compile + auto-refresh

Watch mode

#Instead of packing out new files, the files are temporarily written to memory
#Webpack default package output files as all resource files, as long as it is webPack output files can be accessed
#If there are other static resources that need to be accessed, this needs to be configured separately

yarn webpack-dev-server --dev
Copy the code
const CopyWebpackPlugin = require("copy-webpack-plugin")
module.exports={
  // Specifies the output directory of static resources. It is generally used to reduce resource copying during development
  devServer: {contentBase: ['public']},plugins: [// This is usually used for pre-launch packaging, not during development, which incurs additional overhead
    //new CopyWebpackPlugin(["public"])]}Copy the code

Webpack Dev Server agent API

module.exports={
  devServer: {// Proxy mode
    proxy: {'/api': {/ * * / > https://api.github.com/api/users http://localhost:8080/api/users- is equivalent to the request
        target:'https://api.github.com'.// Override the proxy path
        pathRewrite: {/ * http://localhost:8080/api/users- is to request > https://api.github.com/users matches the corresponding address is empty in the form of regular * /
          '^/api':' '
        },
        // The request is made with the actual proxy host name when the value is true
        changeOrigin:true}}}}Copy the code

sourceMap

Source code maps, used to map the relationship between source code and transformation code, for debugging in a development environment.

Resolved source code and running code inconsistency issues

There are 12 modes

module.exports={
  devtool:'source-map'
}
Copy the code

Eval (‘consol.log(“123”)’)eval executes a piece of JS code. SourceMap files are not generated, only files can be located, not the row where the error occurred

  • Eval – Whether to use EVAL to execute module code
  • Cheap -source Map contains row information
  • Module – Whether the source code is available before loader processing
  • inline
  • no sources

How do I select the sourceMap schema

The development environment chose cheap-module-source-map reason

Select None for production environment

Understand the differences between different modes and adapt to different environments.

There are no one-size-fits-all rules in development

Hot Module Replacement Hot update of the HMR Module

Timely transformation during operation

Webpack is one of the most powerful functions, greatly improving the development efficiency

webpack-dev-server --hot// Enable features
Copy the code
// Two places to configure
const webpack = require('webpack')
module.exports={
  //1. Enable the configuration in the configuration file
  devServer: {hot:true
  }
  plugins: [//2. Add it in the plug-in
  new webpack.HotModuleReplacePlugin()
  ]
}
Copy the code

After HMR is enabled, you need to manually perform hot replacement after hot update of JS modules

module.hot.accept('./editor'.() = >{
  // Manually store the module status
})
const img = new Image()
// Image hot update, mainly store image path can be
module.hot.accept('./better.png'.() = >{
  img.src = background
})
Copy the code

HMR Precautions

  • An error in code handling HMR causes an automatic refresh

    const webpack = require('webpack')
    module.exports={
      devServer: {// Using hotOnly, the code does not refresh automatically when an error occurs
        hotOnly:true
      }
      plugins: [new webpack.HotModuleReplacePlugin()
      ]
    }
    Copy the code
    • If HMR is not enabled, an error is reported. Check whether HMR is enabled first

      if(module.hot){
         module.hot.accept('./editor'.() = >{
        // Manually store the module status})}Copy the code

Webpack production environment optimization

  • Production environments focus on operational efficiency
  • Development environments focus on development efficiency

Configurations in different environments

  • Configuration files export different configurations based on different environments, suitable for small – and medium-sized projects
      module.exports=(env,argv) = >{
        //env Environment parameters passed through the CLI. Argv is all the parameters in the running environment
        const config ={}// Contents of the development environment configuration
        if(env === 'production'){
           config.mode = 'production'
          config.devtool = false/ / close sourceMap
          config.plugins = [
            ...config.plugins,
            new CleanWebpackPlugin(),
            new CopyWebpackPlugin(['public'])]}return config
      }
Copy the code
  • Each environment corresponds to a configuration file
    / / so that need to have three files webpack.com mon. Js/webpack dev. Js/webpack. Prod. Js
    //webpack.prod.js
    const common = require('./webpack.common.js')
    const merge = require('webpack-merge')// Mainly used to handle configuration file merging for WebPack
    const { CleanWebpackPlugin } = require('clean-webpack-plugin')
    const CopyWebpackPlugin = require('copy-webpack-plugin')
    mpdule.export=merge(common,{
      mode:'production'.plugins: [new CleanWebpackPlugin(),
        new CopyWebpackPlugin(['public'])]})Copy the code
yarn webpack --config webpack.prod.js
Copy the code

DefinePlugin injects global members into the code

In production mode the plugin starts by default and injects constants of process.env.node_env into the environment

const webpack = require('webpack')
module.exports={
  plugins: [new webpack.DefinePlugins({
      // This constant is automatically injected into our code and can be used directly in main.js
      API_BASE_URL:'"https://api.example.com"'}})]Copy the code

Tree-Sharking

It is automatically turned on in production mode, which is the effect of a set of functions used together

module.exports={
  // Focus on optimizing functionality
  optimization: {// Export only functions for external use
    usedExports:true.// Be responsible for marking [dead leaves]
    concatenateModules:true// As far as possible, all modules are merged into a single function, which not only improves the running efficiency, but also reduces the size of the code. This function has been enhanced by the Scope Hosting Scope
    minimize:true// Shake them off}}Copy the code

The code packaged by Webpack must use ESM. If the Babel lower version is used in the configuration, ESM may be changed to CommonJS, so TreeShaking will not take effect. This conversion has been turned off in the new version of Babel, so TreeShaking can still be used

module.exports={
  module: {rules:[
      {
        test:/\.js$/,
        use:{
          loader:'babel-loader'.opitons: {presets:[
              ['@babel/preset-env', {modules:false }]// Ensures that the ESM conversion plugin will not be turned on in PRESET -env, ensuring that the Preset is used for TreeShaking}}}]}}Copy the code

SideEffects side effects

Side effects: what a module does while executing in addition to exporting members

NPM packages are generally used to flag if there are side effects

module.exports={
  optimization: {sideEffects:true// It is automatically enabled in Production mode. After it is enabled, it checks whether the package.json of the current code is marked with sideEffects}}Copy the code