Historical issues
- ES Modules has an environment compatibility problem
- Too many module files and frequent network requests
- All front-end resources need to be modular
There is no doubt that modularity is necessary
Put forward the idea
The target
- New feature code compiled
- modular
javascript
packaging - Supports different types of resource modules
Packaging tools
The packaging tool addresses the overall modularity of the front end, not just thejavascript
modular
Webpack
Module Bundler-
According to the module Loader – code with environmental compatibility issues can be compiled and converted by the Loader during packaging
-
Code Splitting — It’s a way to package all the Code in an app in a way that we need to do, which means we don’t have to package all the Code together and have huge file problems
-
Asset Module – what problem type of resource files should be loaded in a modular manner
-
Webpack
1. Webpack is a quick start
Note the use of webpack ^4.40.2, webpack-cli ^3.3.9
yarn add webpack webpack-cli
package.json
add"build": "webpack"
2. Webpack configuration file
const path = require('path')
module.exports = {
entry: './src/index.js'.output: {
filename: 'bundle.js'.path: path.join(__dirname, 'output')}}Copy the code
3. Working mode
yarn webpack --mode none
- development
- production
- None – Packaging of the original state
- Webpack.js.org/configurati…
4. Operation principle of Webpack packing results
5. Load the resource module
Loader
是Webpack
By virtue of the core featuresLoader
You can load any type of resource
- configuration
Loader
// webpack.config.js
module: {
// Configure the loading rules for other resource modules
rules: [{test: /\.css$/,
use: [
'style-loader'.'css-loader'
] // When it is an array, the last one is executed first}}]Copy the code
6. Import the resource module
JavaScript
Drive the business of the entire front-end application
// In main.js
import './main.css'
Copy the code
Webpack
It is recommended that we import resources dynamically according to the needs of the code
7. File resource loader
file-loader
Configure the root directory of the website
The principle of
8. URL loader
Data URLs
Is a kind of presenturl
I can directly represent the contents of the fileurl
The text already contains the contents of the file, so we’re using thisurl
I will not send anyhttp
request
- loader
url-loader
- Small file usage
Data URLs
To reduce the number of requests - Large files are separately extracted and stored to improve loading speed
- Small file usage
{
test:/\.png$/,
use: {
loader: 'url-laoder'.options: {
limit: 10 * 1024 // only files smaller than 10KB will be converted}}}Copy the code
9. Common loader
- Compiler conversion class
- eg:
css-loader
- eg:
- File manipulation class
- eg:
file-loader
- eg:
- Code inspection class
- eg:
eslint-loader
- eg:
10. Webpack with ES 2015
- Install Babel correlation
yarn add babel-=loader @babel/core @babel/preset-env -D
// rules
{
test: /\.js$/,
use: {
loader: 'babel-loader'.options: [
presets: ["@babel/preset-env"]]}}Copy the code
Webpack simply packages the utility loader that can be used to compile the transformation code
11. How Webpack loads resources
- follow
ES Modules
The standardimport
The statement - follow
CommonJS
The standardrequire
function - follow
AMD
The standarddefine
Functions andrequire
function - In the style code
@import
Instructions andurl
function HTML
Code in the image tagsrc
attribute
Pay attention to
- The SRC attribute in HTML triggers packaging, and if you want to trigger other attribute reference files, you need to do the following
// rules
{
test: /\.html$/,
use: {
loader: 'html-loader'.options: {
attrs: ['img:src'.'a:href']}}}Copy the code
12. Core working principle of Webpack
Loader
Mechanism isWebpack
The core of the
13. Develop a Loader
// my-markdown-loader.js
const marked = require('marked')
module.exports = source= > {
const html = marked(source)
// Either of the following is ok
// A
return `module.exports = The ${JSON.stringify(html)}`
return `module.exports = The ${JSON.stringify(html)}`
// B An html-loader is required to continue processing the result if the following case is used
return html
}
Copy the code
// B
// rules
{
test: /\.md$/,
use: [
'html-loader'.'./my-markdown-loader']}Copy the code
14. Plugin mechanism
Enhance Webpack automation capabilities
Loader
Focus on resource module loading
Plugin
Solve other automation tasks
- Eg: remove
dist
directory - Eg: Copy static files to the output directory
- Eg: Compress the output code
14.2 Auto Clear Output Directory Plug-in
- use
clean-webpack-plugin
The plug-in
yarn add clean-webpack-plugin -D
Copy the code
Use:
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
// webpack.config.js
plugins: [
new CleanWebpackPlugin()
]
Copy the code
14.3 Automatically Generating HTML Plug-ins
html-webpack-plugin
const HtmlWebpackPlugin = require('html-webpack-plugin')
plugins: [
HtmlWebpackPlugin can be configured with parameters
// https://www.npmjs.com/package/html-webpack-plugin
new HtmlWebpackPlugin({
title: 'webpacl plugin sample'.meta: {
viewport: 'width-device-width'
},
template: './src/index.html'})]// Automatically generates HTML to dist
Copy the code
- The best way to do this is to configure templates in source code
<! -- HtmlWebpackPlugin to configure template path -->
<h1><%= htmlWebpackPlugin.options.title %></h1>
Copy the code
- Output multiple page files simultaneously
plugins: [
new HtmlWebpackPlugin({
title: 'webpacl plugin sample'.meta: {
viewport: 'width-device-width'
},
template: './src/index.html'
}),
// Used to generate about.html
new HtmlWebpackPlugin({
filename: 'about.html'})]Copy the code
14.4 Static Files are copied
copy-webpack-plugin
const CopyWebpackPlugin = require('copy-webpack-plugin')
plugins: [
new CopyWebpackPlugin([
'public']]Copy the code
15. Develop a plug-in
Plugin
Through the hook mechanismwww.webpackjs.com/api/compile…- A function or a contain
apply
Method object
class MyPlugin {
apply(compiler) {
// Find the emit hook (before generating resources to the output directory)
compiler.hooks.emit.tap('MyPlugin'.compilation= > {
// compilation => can be understood as the context of this compilation
for (let name in compilation.assets) {
const contents = compilation.assets[name].source()
const withoutComments = contents.replace(/\/\*\*+\*\//g.' ')
compilation.assets[name] = {
source: () = > withoutComments,
size: () = > withoutComments.length
}
}
})
}
}
Copy the code
Webpack
Handling development scenarios
Vision: Ideal development environment
- Idea 1. To
HTTP Server
run - Idea 2. Automatic compilation + automatic refresh
- Idea 3. Offers
Source Map
support
1. Automatic compilation
watch
Working mode, listening for file changes, automatic repackagingyarn webpack --watch
2. Automatically refresh the browser
- You want the browser to refresh automatically after compiling
BrowserSync
It’s difficult to operate, and it’s inefficient
3. Webpack Dev Server
- Provides for development
HTTP Server
- integration
Automatic compilation
和Automatically refresh the browser
The function such as
- integration
yarn add webpack-dev-server -D
The result of packing is not written to disk and stored in memory
yarn webpack-dev-server
Copy the code
3.2 Dev Server by default onlyserve
Package output file
- As long as it is
Webpack
Output files can be accessed directly if other static resources are needed as you sayserve
// webpack.config.js
{
devServer: {
// Specify an additional lookup resource directory for the development server
contentBase: './public'}}Copy the code
3.3 Dev Server agent API
- http://localhost:8080/index.html
- www.example.com/api/users
- This forms a cross-domain request
You do not need to enable same-origin deploymentCORS
, cross-domain is not allowed
Problem: Cross-domain interface issues during development
module.exports = {
devServer: {
proxy: {
'/api': {
// http://localhost: 8080/api/users -> https://api.github.com/api/users
target: 'https://api.github.com'.// http://localhost: 8080/api/users -> https://api.github.com/users
pathRewrite: {
'^/api': ' '
},
// Localhost: 8080 cannot be used as the host name for requesting Github
changeOrigin: true}}}}Copy the code
4. Source Map
Running code is completely different from source code. If you need to debug your application, error messages cannot be located. Debugging and error reporting are based on running code
- For example,
Jquery - 3.4.1 track. Min. Js
- If you want to use
Source Map
Need to be added at the end of the file
/ / # sourceMappingURL - jquery - 3.4.1 track. Min. The mapCopy the code
4.2 Configuring Source Map on Webpac
Webpack
对Source Map
There are many types of support, each with varying efficiency and effectiveness
// webpack.config.js
module.exports = {
devtool: 'source-map',}Copy the code
Support table (also refer to the official websiteWebpack.js.org/configurati… )
The eval mode
eval('console.log(123) //# sourceURL=./foo/bar.js')
// means that we can change the code we execute through eval via sourceURL
Copy the code
Devtool mode comparison
- Eval – Whether to use
eval
Execute module code - Cheap – Source Map contains row information
- Module – Whether it can be obtained
Loader
Process the previous source code - How to Choose (Suggestions)
- Development mode – cheap-module-eval-source-map
- Each line of code does not exceed 80 characters
- The code through
Loader
After the conversion, the difference is large - It doesn’t matter if you pack slowly the first time, but repack relatively quickly
- Production mode – None
Source Map
It exposes the source code- Debugging is a development phase thing
- If not confident in the code – suggestion
nosources-source-map
- Development mode – cheap-module-eval-source-map
Understand the differences between different modes and adapt to different environments and scenarios
const HtmlWebpackPlugin = require('html-webpack-plugin')
const allModes = [
'eval'.'cheap-eval-source-map'.'cheap-module-eval-source-map'.'eval-source-map'.'cheap-source-map'.'cheap-module-source-map'.'inline-cheap-source-map'.'inline-cheap-module-source-map'.'source-map'.'inline-source-map'.'hidden-source-map'.'nosources-source-map'
]
module.exports = allModes.map(item= > {
return {
devtool: item,
mode: 'none'.entry: './src/main.js'.output: {
filename: `js/${item}.js`
},
module: {
rules: [{test: /\.js$/,
use: {
loader: 'babel-loader'.options: {
presets: ['@babel/preset-env']}}}]},plugins: [
new HtmlWebpackPlugin({
filename: `${item}.html`})]}})Copy the code
5. Auto-refresh issues
The module can also be updated in time without refreshing the page
5.2 HMR – Hot Module Replacement
- Hot plug – To plug and remove devices on a running machine at any time
Webpack
Hot replacement refers to replacing a module in real time during application running without affecting application running statusHMR
是Webpack
One of the most powerful features – greatly improve the efficiency of developers
5.3 open the HMR
HMR
Integrated into thewebpack-dev-server
中- run
webpack-dev-server --hot
- It can also be enabled through a configuration file
const webpack = require('webpack') plugins: [ new webpack.HotModuleReplacementPlugin() ] // Run directly // yarn webpack-dev-server Copy the code
- run
Webpack
In theHMR
It does not work out of the box- The module hot replacement logic needs to be manually processed
Why do hot updates to style files work out of the box?
Because there are style filesLoader
Processing instyle-loader
Hot updates to style files are already handled
The style file is simple, and you just need to replace it with the browser
Projects created through scaffolding are internally integratedHMR
plan
Conclusion: We need to handle it manuallyJS
Hot replacement after module update
5.4 HMR APIs
Manually handle hot replacement after module update
- To deal with
JS
Module hot replacement and processingThe picture
Module hot replacement
// main.js
import createEditor from './editor'
import background from './better.png'
import './global.css'
const editor = createEditor()
document.body.appendChild(editor)
const img = new Image()
img.src = background
document.body.appendChild(img)
// ============ the following is used to handle HMR, independent of business code ============
// console.log(createEditor)
if (module.hot) {
let lastEditor = editor
module.hot.accept('./editor'.() = > {
// console.log(' Editor module updated, need to handle hot replacement logic manually here ')
// console.log(createEditor)
const value = lastEditor.innerHTML
document.body.removeChild(lastEditor)
const newEditor = createEditor()
newEditor.innerHTML = value
document.body.appendChild(newEditor)
lastEditor = newEditor
})
module.hot.accept('./better.png'.() = > {
img.src = background
console.log(background)
})
}
Copy the code
5.5 HMR Precautions
- To deal with
HMR
Code error causes an automatic refresh - Not enabled
HMR
In the case of,HMR API
An error-
In devServer: {hot: true, hotOnly: true} if these two are not enabled, module.hot does not exist
-
Webpack
Production environment optimization
- Development environments focus on development efficiency
- Production environments focus on operational efficiency
Create different configurations for different work environments
1. Configurations in different environments
- Configuration files export different configurations based on different environments
- Each environment corresponds to a configuration file
1.2 Webpack configuration files support exporting a function
module.exports = (env, argv) = > {
if (env == 'production') {
// ...}}Copy the code
2. Use configuration files in different environments
- webpack.common.js
- webpack.dev.js
- webpack.prod.js
This parameter must be configured withcommon
Inside the information to do merge, the community provides a better merge toolwebpack-merge
yarn webpack --config webpack.prod.js
Copy the code
3. Optimize the configuration
3.1 DefinePlugin
Inject global members into your code
- In the Production environment, one is started and injected by default
process.env.NODE_ENV
constant
const webpack = require('webpack')
module.exports = {
mode: 'none'.entry: './src/main.js'.output: {
filename: 'bundle.js'
},
plugins: [
new webpack.DefinePlugin({
// The value requires a snippet of code
// if it is 'https://api.example.com'
// Console. log(API_BASE_URL) ==> console.log(https://api.example.com)
API_BASE_URL: JSON.stringify('https://api.example.com')]}})Copy the code
3.2 Tree Snaking
“Shake off” unreferenced parts of code (unreferenced code-dead-code)
-
It is automatically enabled in production mode
-
Tree Shaking is not about configuration options, it’s about optimizing a set of functions together
-
Optimization configures optimization functionality within Webpack
- Webpack.docschina.org/configurati…
{ optimization: { // The module exports only used members usedExports: true.// Compress the output // minimize: true}}// Use usedExports: true and take care: true is similar to Tree Shaking // usedExports is responsible for marking "dead leaves" // Take care to shake them off Copy the code
- Merging module functions
Scope Hoisting
- As much as possible, combine all modules and output them into a single function – increasing operating efficiency and reducing the volume of code (this feature is also known as’ Scope promotion ‘).
optimization: { // The module exports only used members usedExports: true.// Merge each module into a function if possible concatenateModules: true,}Copy the code
Tree Shaking and Babel
Outlook: A lot of data shows that Shaking doesn’t work when you use Babel Tree. Here’s how it works.
Tree Shaking
Prerequisite for useES Modules
- by
Webpack
Packaged code must be usedESM
Webpack
The modules are assigned to different modules according to their configuration before packaging themLoader
Process, and finally package the results together- So in order to convert the code
ESMAScript
Features that we usebabel-loader
To deal with - while
babel-loader
When working with code, it is possible to changeES Modules
Converted toCommonJS
- So in order to convert the code
- by
- To explore the process
module.exports = {
mode: 'none'.entry: './src/index.js'.output: {
filename: 'bundle.js'
},
module: {
rules: [{test: /\.js$/,
use: {
loader: 'babel-loader'.options: {
presets: [
// If Babel has already transformed ESM when loading the module, Tree Shaking will fail
// ['@babel/preset-env', { modules: 'commonjs' }]
// ['@babel/preset-env', { modules: false }]
// You can also use the default configuration, i.e. Auto, so that babel-Loader automatically shuts down ESM conversion
['@babel/preset-env', { modules: 'auto'}]]}}}]},optimization: {
// The module exports only used members
usedExports: true.// Merge each module into a function if possible
// concatenateModules: true,
// Compress the output
// minimize: true}}Copy the code
-
babel-loader
There is clear supportESModules
-
- Find the ones we use
preset-env
- Find the ones we use
4. There are sideEffects
SideEffects has nothing to do with Tree Shaking
- Side effect: What the module does when it executes other than exporting members
sideEffects
Commonly used innpm
Package flags whether there are side effects
// webpack.config.js
module.exports = {
mode: 'none'.entry: './src/index.js'.output: {
filename: 'bundle.js'
},
optimization: {
sideEffects: true
// When enabled, Webpack will check whether the current code belongs to the package.json
// sideEffects to determine if this has sideEffects,
// If there are no side effects, the unused modules will not be packaged}}// package.json
{
"sideEffects": false // All the code in the project affected by our current package.json has no side effects - without side effects, it will be removed
}
Copy the code
4.2 sideEffects note
Make sure your code really has no side effects
- For example,
// Add an extension method to the prototype for Number
// extend.js
Number.prototype.pad = function(size) {
// Convert numbers to strings => '8'
let result = this + ' '
// Add a 0 before a number => '008'
while(result.length < size) {
result = '0' + result
}
return result
}
// This is the side effect code
// In main.js
import 'extend.js'
console.log((8).pad(3))
Copy the code
If used like this: set no side effects in the Side effects configuration, the code in exten.js will not be packaged
For example, the global CSS module is a side effect, if set to no side effect, packaging will ignore it
So, at configuration time, you can tell WebPack which side effects are available
// package.json
{
// "sideEffects": false
"sideEffects": [
"./src/extend.js"."./src/global.css"]}Copy the code
5. Code Splitting
Background: Because all the code ends up packaged together, the bundle is too bulky
- Not every module is necessary at startup
- Subcontract, load on demand
The current mainstream version of HTTP1.1 is inherently flawed – eg: same-domain parallel request restriction
Do not package problem, resource file too much:
- Each request has a certain amount of delay
- The request of
Header
Wasted bandwidth traffic
So module packaging is definitely necessary
Webpack implements subcontracting in two main ways
- Multiple entry packing
- Dynamic import
5.2 Multi-Entry Packaging
- Multi-page applications – one page for each package entry
// webpack.config.js
entry: {
index: "./src/index.js".album: "./src/album.js"
},
output: {
filename: "[name].bundle.js"
},
plugins: [
new HtmlWebpackPlugin({
title: 'Multi Entry'.template: './src/index.html'.filename: 'index.html'.// Configure the bundle referenced by the output HTML, using chunks
chunks: ['index']}),new HtmlWebpackPlugin({
title: 'Multi Entry'.template: './src/album.html'.filename: 'album.html'.chunks: ['album'"})"Copy the code
5.3 Extracting common Modules
{
optimization: {
splitChunks: {
chunks: 'all'}}}Copy the code
5.4 Dynamic Import
- Load on Demand – Load a module when it is needed
- Dynamically imported modules are automatically subcontracted
// import posts from './posts/posts'
// import album from './album/album'
const render = () = > {
const hash = window.location.hash || '#posts'
const mainElement = document.querySelector('.main')
mainElement.innerHTML = ' '
if (hash === '#posts') {
// mainElement.appendChild(posts())
import(/* webpackChunkName: 'posts' */'./posts/posts').then(({ default: posts }) = > {
mainElement.appendChild(posts())
})
} else if (hash === '#album') {
// mainElement.appendChild(album())
import(/* webpackChunkName: 'album' */'./album/album').then(({ default: album }) = > {
mainElement.appendChild(album())
})
}
}
render()
window.addEventListener('hashchange', render)
Copy the code
5.5 Magic Comments
/* webpackChunkName: '(name)' */
import(/* webpackChunkName: 'components' */'./posts/posts').then(({ default: posts }) = > {
mainElement.appendChild(posts())
})
Copy the code
6. MiniCssExtractPlugin
Extract CSS into a single file and load CSS modules on demand through this plug-in
- If the style size is not very large, taking a percentage of a single file can be counterproductive
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
module: {
rules: [{test: /\.css$/,
use: [
// 'style-loader', // inject the style through the style tag
MiniCssExtractPlugin.loader,
'css-loader']]}},plugins: [
new MiniCssExtractPlugin()
]
Copy the code
7. OptimizeCssAssetsWebpackPlugin
Compress the output CSS file
Webpack
The built-in compression is aimed atjs
The file is- for
CSS
useoptimize-css-assets-webpack-plugin
const OptimizeCssAssetsWebpackPlugin = require('optimize-css-assets-webpack-plugin')
plugins: [
new OptimizeCssAssetsWebpackPlugin()
]
Copy the code
Note:Webpack
It is recommended that plug-ins such as this compression class be configured in arrays for unified control by the Miner option
optimization: {
minimizer: [ // WebPack assumes that as long as this array is configured, it will assume that we need to customize the configuration of the compression plug-in
new OptimizeCssAssetsWebpackPlugin() // Configure this normally compressed js file is not compressed again]}// The built-in JS plug-in needs to be set back
// yarn add terser-webpack-plugin -D
const TerserWebpackPlugin = require('terser-webpack-plugin')
optimization: {
minimizer: [
new OptimizeCssAssetsWebpackPlugin(),
new TerserWebpackPlugin()
]
}
Copy the code
8. Output file name Hash (substitutions)
In production mode, the file name is Hash
- Usage:
filename: '[name].[hash].bundle.js'
hash
: One is generated for each buildhash
. It is related to the whole project and changes as soon as the project file changeshash
.chunkhash
And:webpack
package-generatedchunk
Related. eachentry
, there will be differenthash
.contenthash
: is related to the contents of a single file. Changes when the contents of the specified file are changedhash
.
Hash can specify length ‘[contenthash:8]’
For cache control, CONTenthash :8 is the best choice