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