T1: Modular development

Evolution of modularity #2

Early modularity

There are no tools to help

File partition mode => Namespace mode => IIFE

This solution solves three problems: contamination of global scope, naming conflicts, and failure to manage module dependencies.

The emergence of modular specifications

Node.js has the CommonJS specification, namely:

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

But CommonJS is synchronous and not suitable for the browser side.

For browsers, AMD (Asynchronous Module Definition) + require.js provides define() to define modules and Require () to add dependencies.

There is also CMD (Common Module Definition) + sea-.js, which uses a similar specification to CommonJS for ease of use.

Current modularity specifications

CommonJS in Node.js

ES Module in Browsers

Use of ES Module

Feature # 5

Importing modules with

  1. The ESM automatically adopts strict mode. Use strict is ignored.

    In strict mode, global this does not refer to the global object window, but rather undefined

  2. Each module runs in a separate private scope

  3. ESM requests external JS modules through CORS.

    When a cross-domain request is made to an external module, the source address must be specified in the header and the server agrees.

  4. The ESM script tag delays script execution

    That is, differ by default, executes the script after the page is rendered

Export export # 6

Variables, functions and classes in a module can all be exported. There are two ways to use export:

  • Directly before the declaration of the exported object, as inexport var name = 'Tom'
  • 2. Often used (at the end of a module) to place items with{}Package export, such asexport { name, foo }

When exporting, you can set default items and rename them, for example, export {name as newName, foo as default}

When a renamed item is imported, it must be imported with a newName, that is, import newName.

Default items need to be renamed when they are imported, such as import default as newFoo, or simply import newFoo

【 note 】

  • When you export multiple items, you are not exporting an object, but a fixed syntax for exporting. Similarly, when importing, you don’t deconstruct objects.
  • The imported item gets the reference address, not a new copy of the item. In other words, changes of exported items can be detected by the import end in real time, but they are read-only and cannot be modified at the import end.

The import import # 8

  • When importing, from is followed by the specific module file URL

    • The extension of the module file cannot be omitted
    • Urls can be relative urls to. /It can also be an absolute URL that starts with/It can also start with the full URL, such as the module file on the CDN.
  • Another use of imports: Import {} form ‘./module.js’ or simply import ‘./module.js’, executes the module without extracting the export from it.

  • Import all exports of a module: import * as mod. A mod is an object that contains all the exported items

  • Dynamic import modules use the function import(), such as import(‘./module.js’).then(function (mod) {console.log(mod)}). Exports within a module are obtained through THEN.

  • Import both default and named members: import {name, age, default as newFoo} form ‘./module.js’ or import newFoo, {name, age} from ‘./module.js’

Import and export #9 simultaneously

export {name, age} from './module.js'

This notation is usually used when a single module is used to manage multiple module imports and exports. Import multiple module exports into index.js and export them at the same time so that you only need to import the index.js module on the home page.

ES Module compatibility handle #10

What about browsers that don’t support ES Module?

  1. Babel-browser-build and browser-es-module-loader are introduced and converted into general syntax.

    You can import it from unpkg.com using the script tag.

  2. Browsers that don’t support Promises also need to introduce promise-polyfill.

  3. Add the nomodule attribute for the above introduction to avoid executing the code twice in normal browsers.

The import method described above is too demanding to run online.

ES Module in Node.js

Node.js is also gradually adding support for ES Modules

The main difference from the browser: the extension of the module is.mjs (also note that the URL of the imported file is changed).

Interaction between ES module and CJS module #11~12

ESM import CJS module:

Under CJS, module export is in the form of objects, including various attributes, methods. The ES module can only be imported by default, which is the module.exports object. When using the library, you need to call the object and then call the property method.

e.g. import _ from 'lodash'; console.log(_.camelCase(Import From CJS Module))

Third-party packages that are not compatible with the ESM must be presented in this way. There are also third-party packages that are separated into a dedicated ES package, such as Lodash-ES

e.g. import camelCase from 'lodash-es'; console.log(camelCase(Import From ES Module))

The built-in module of Node.js is compatible with two kinds of export in the module, so the above two import methods can be used.

In Node.js, CJS modules cannot import ES modules.

Differences between ES modules and CJS modules #13

CommonJS global members are no longer available in ESM, including module, require(), exports, __filename, __dirname

Require and exports implement import and export, and in ESM there are import and export counterparts.

__filename and __dirname are replaced with the following methods:

import { fileURLToPath } from 'url'
import { dirname } from 'path'
const __filename = fileURLToPath(import.meta.url)
const __dirname = dirname(__filename)
Copy the code

New node.js support for ESM #14

Json with the ‘type’: ‘module’ field, js files run as ESM and CJS modules need to change the.cjs extension to run properly.

Older Versions of Node use ESM (via Babel) #15

Install @babel/node and set the transcoding rule to @babel/plugin-transform-modules-commonjs or @babel/preset-env including it

The ES module can be called directly from the command line babel-node esmodule.js.

T2: Webpack packaging tool

Basic usage

Package multiple JS modules into a single JS file, which does not require the type=”module” attribute in HTML

Configuration file #4

Create the webpack.config.js file in the root directory

  • The entrance
  • The output
const path = require('path')
module.exports = {
	entry: './src/app.js'.// entry, default is './ SRC /main.js'
	output: {
		filename: 'bundle.js'.// File name, default is 'main.js'
		path: path.join(__dirname, 'output')// Output the absolute path}}Copy the code
  • Working mode #5

    e.g. mode: 'none'

    Different working modes are equivalent to different configuration groups. You can also add the command line parameter –mode XXX. Default Production mode.

    • Production line with
    • Development optimize packaging speed and add development instructions
    • None has no additional

Loader for Webpack

Webpack default loader can package JS modules, other resource modules need to be added to handle.

Loader is the heart of Webpack #14.

Common Loader class #11

  • Compile conversion classes: convert modules to work in JS form

  • File operations: Copy files to a directory and export the access path

  • Code checking classes: checking and unifying code styles

Load CSS resource #7

Css-loader: converts CSS files into JS modules and packages them

Style-loader: Appends CSS modules to HTML rendering

Add the module field to the configuration file:

module: {
    rules: [
        {
            test: /.css$/,		// Match file paths with regular expressions
            use: 'css'			// The loader used is executed from the bottom up}}]Copy the code

And introduce the CSS file into the entry JS file

Load file resource #9

Images, fonts and other files cannot be converted into JavaScript code like CSS. They need to use file-loader. However, they also need to be imported into the entry file.

The default HTML file for Webpack is in output.path. If not, set output.publicPath to the path of the output directory relative to the HTML file, with a slash at the end. In this way, when HTML accesses static resources, the path configured by the publicPath splicing Loader is the correct path.

Load DataURL resource #10

url-loader

For short text, use DataURL to reduce HTTP requests.

Add the limit attribute to use.option under configuration file module.rules to limit the size of files processed using urL-loader. Files that exceed this limit are still loaded using file-loader, that is, files that rely on file-loader

e.g.

module: {
    rules: [{test: /.png$/,
            use: {
                loader: 'url-loader'.option: {
                    limit: 10 * 1024	// The file size does not exceed 10 kB}}}]}Copy the code

Added support for ES6 #12

Webpack is just a packaging tool and does not support ES6+ itself. You can use babel-loader instead of the default loader to load JS files for ES6 support.

module: {
    rules: [
        {
        test: /.js$/,
        use: {
            loader: 'babel-loader',
            options: {
                presets: ['@babel/preset-env']
            }
        }
    ]
}
Copy the code

Load other resources and files #13

Import of different standard modules
  • Follow the ES Module standard import statement
  • Follow the CommonJS standard require statement
  • Follow AMD standard define function and require function

Although Webpack supports multiple standards, it is best to use a single standard module for the same project.

@import and URL () in CSS

Other imported CSS files and files obtained from the URL () are loaded correctly

<img> SRC and <a> href in HTML

Install html-loader and add a set of rules:

{
    test: '/.html$/',
    use: {
        loader: 'html-loader',
        options: {
            attrs: ['img:src', 'a:href']
        }
    }
}
Copy the code

The img element’s SRC attribute can be handled by default, i.e. options.attrs defaults to ‘img: SRC ‘.

Loader working principle #15

Loader converts resource files from input to output

Loader is a concept of a pipeline. Multiple Loaders can be used to connect the same resource. The final output should be JS code.

Plugin plugin for Webpack

Plug-ins can do more automation than packaging

Clean up the directory with plugin #17

After installing the plug-in, you need to import the (require) plug-in in the configuration file and write a new instance of the plug-in, such as new CleanWebpackPlugin(), in the plugins array of the exported item.

Clean-webpack-plugin: Empty the output directory before packaging

Automatically generate HTML plugin #18~20

Html-webpack-plugin: Automatically generates HTML files in the output directory that reference the webpack bundles

The html-webpack-plugin can be configured in more detail

Default generated HTML

When you create a new instance of the HTML-webpack-plugin, you pass in an object argument with built-in properties that modify the generated HTML file. e.g.

{
    title: 'Custom Title'.meta: {
        viewport: 'width=device-width'}}Copy the code

Greater customization can be achieved using templates that require dynamic injection using LoDash template syntax. e.g.

{
    template: './src/template.html'
}
Copy the code
<body>
    <h1><%= htmlWepackPlugin.options.title %></h1>
</body>
Copy the code
Create multiple HTML files

Create multiple instances and pass in a parameter object {filename: ‘about.html’} to change the filename.

Move files with plugins

Copy-webpack-plugin: Copies files in the specified directory to the output directory, such as icon images.

When you create a new instance under plugins, you pass in the argument to the directory to copy

[Work] Usually do a static file merge before going live, which can be accessed during development.

How plugin works #22

The extension is implemented by mounting functions on hooks during the packaging process

Webpack Dev Server

Obstacle 1: Recompiling the package and refreshing the page are necessary to see the effect on the browser side after the source code is modified.

  • Automatic compilation: Webpack — Watch working mode, listening for file changes, automatic packaging

  • Automatic refresh browser: use browser-sync to go online and listen for files to automatically refresh

But this “two-step automation” creates repetitive disk reads and writes that drag down efficiency.

Webpack-dev-server is a development tool launched by Webpack, which can automatically compile and refresh the browser

Add access to static resources #27

Config.js configuration file => devServer object => contentBase array property

e.g. devServer: { contentBase: [ './public', './base'] }

Proxy API # 28

The development process page belongs to the cross-domain request to the server locally, and is the same origin after online.

If the server does not support CORS across domains, the local proxy needs to be developed on a same-origin development server.

Js configuration file => devServer object => Proxy object attribute

devServer: {
    proxy: {
        '/api': {			// Address starting with '/ API '
            target: 'https://api.github.com'.// 👆 https://localhost:8080 is replaced with https://api.github.com
            pathRewrite: {	/ / rewrite
                '^/api': ' '
                // 👆 https://localhost:8080/api/users 修正为 https://api.github.com/users
            },
            changeOrigin: true}}}Copy the code

Module hot update MHR

Many times, we need to debug code while the page is still on, and automatic page refresh is inefficient.

Hot Module Replacement enables CSS, JS, images and other resource files to be uploaded without refreshing.

Open MHR # 37

MHR is a built-in plug-in for Webpack Dev Server.

Add –hot to the webpack-dev-server command.

Or configure devServer: {hot: true} and introduce webPack and create a new plug-in instance.

At this point, the CSS is ready for hot replacement.

Further configure hot replacement logic #38

[Note] Projects generated through the framework may not need to be configured because the framework is fully configured with MHR.

Hot replacement is handled in the import module file (i.e., the entry file).

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

Module.hot is the core object of the MHR APIs. It provides Accept () with the first argument being the module URL and the second argument being the updated handler for the module.

Different modules require different handlers, written according to the module logic.

Js module # 40

Editor module

let lastEditor = editor					// Keep the original module result
module.hot.accept('./editor'.() = >{
    const value = lastEditor.innerHTML	// Keep the original state
    document.body.removeChild(lastEditor)// Result of removing the original module
    const newEditor = createEditor()	// Generate a new module result
    newEditor.innerHTML = value			// The new module results in the original state
    document.body.appendChild(newEditor)
    lasteditor = newEditor
})
Copy the code
Picture module #41
module.hot.accept('./pic.png'.() = >{
    img.src = background		// Reassign the image address once, namely realize the image refresh
})
Copy the code
MHR Considerations #42
  1. Hot: true: If an error occurs during module modification, the page automatically reloads the previous module.

    HotOnly: true can be used, and the page is not reloaded in case of errors, so that bugs can be observed easily

  2. Add if(module.hot) to MHR to avoid errors, and the relevant code will not be packaged when the production environment is online.

Source Map

Obstacle 2: After the project is packaged and compiled, variable names and file structures are modified, and the browser debugging cannot match the source code.

The source map establishes the mapping between packaged code and source code for easy and intuitive debugging.

Configure Source Map #30 for Webpack

Devtool: ‘source-map’

Source Map mode #32~33

Eval (Class Source Map)

devtool: 'eval'

Can locate the Source file, no Source Map, also no specific row, column information. The fastest, the effect is crude.

  • Code with EVAL indicates an execution module that uses EVAL

  • Cheap indicates simplicity and higher speed

  • With module indicates that the source code is available before loader processing

Mode selection:

Development: being – the module – the eval – source – the map

Production: None/nosource-source-map

Different mode configurations for Webpack

On the basis of the default configuration, add judgment modify configuration. This approach is suitable for small projects.

module.exports = (env, argv) = > {
    constThe config = {...}if (env === 'production') {
        config.mode = 'production'
        config.devtool = false
        config.plugins = [
            ...config.plugins,
            new CleanWebpackPlugin(),
            new CopyWebpackPlugin(['public'])]}return config
}
Copy the code

Add the command line argument –env Production to the package to use the production mode configuration.

It is more reliable for large projects to use multiple configuration files, usually three: webpack.common.js, webpack.dev.js, and webpack.prod.js

Common, as the base configuration file, was introduced by Dev and Prod as an object constant. const common = require(‘./webpack.common’)

Then write the modified value. Merge of configuration objects is handled here using the merge() of the Webpack-merge package. Module. exports = common, {···}. The merge function automatically appending values of array types, rather than overwriting them.

NPM scripts: webpack –config webpack.prod.js