Why build?
The front end is growing fast.
More and more ideas and frameworks are coming out.
Is to improve the efficiency of development.
For example, ES6 needs to be converted from Babel to ES5 to run on the browser.
For example, SCSS needs to be converted to CSS to run in the browser
.
These ideas and frameworks are built upon.
Why choose WebPack for the build?
Webpack treats everything like a module!
Webpack can be extended by plugin!
The Webpack community is very large and active, keeping up with The Times! Ecological chain integrity, maintenance is also very high!
To understand why we want to use WebPack, let’s take a look back at how we used JavaScript on the Web before packaging tools came along. There are two ways to run JavaScript in a browser. The first way is to reference some script for each function. This solution is difficult to scale because loading too many scripts can cause network bottlenecks. The second way is to use a large.js file that contains all the project code, but this can cause problems with scope, file size, readability, and maintainability.
Why did you choose webpack: webpack.docschina.org/concepts/wh…
Webpack is first packaged
Webpack is a module packaging tool, but it can only package JS, so if you want to package other modules such as CSS and pictures, you also need to use Loader. For problems that Loader cannot solve, you also need to use plug-in plugin to expand functions.
The installation
yarn add webpack webpack-cli
configuration
- create
webpack.config.js
file - configuration
webpack.config.js
const path = require('path') module.exports={ entry: { main:'./src/index.js' }, output: { path: path.resolve(__dirname, 'dist'), filename: "[name].js"}}Copy the code
- configuration
packge.json
"script": {"start":"webpack" } Copy the code
Explain the configuration
In fact, webpack already has a lot of very rich stuff configured by default, like output, and if we don’t configure output, by default we output a dist folder with a file called bundle.js.
We can also configure ourselves:
-
entry
To configure the entry file, if there is only one entry file, we can enter :’./ SRC /index.js’.
If you have multiple entry files, or you want to give your entry file a name: main is the name of the entry file.
The nice thing about the name of this entry file is that it can be used in many later configurations: instead of manually typing main, we can simply use ‘[name]’ to lock onto the previously configured main.
This can be realized, change a, can change all.
-
output
When you output path, it generates a folder containing a file named filename with the name ‘[name].js’. As mentioned above, this [name] corresponds to the entry file name!
loader
Webpack can only package and parse JS code, so we need to use loader to parse non-JS modules!
css
The installation
yarn add style-loader css-loader
configuration
module.exports={
module: {
rules: [
{
test:/\.css$/,
loaders:['style-loader'.'css-loader']},]}}Copy the code
Explain the configuration
Test :/\.css$/: When we encounter a.css file, we go to the following loader.
Cs-loader assembs CSS files together, and then style-loader converts the CSS code into a string and inserts it into our output file main.js.
scss
CSS is not enough for us, we need to use more powerful SCSS!
The installation
yarn add sass-loader style-loader css-loader node-sass
configuration
module.exports={
module: {
rules: [
{
test:/\.scss$/,
loaders:['style-loader'.'css-loader'.'sass-loader']},]}}Copy the code
Explain the configuration
Sass-loader first compiles SCSS code into CSS code, csS-loader sets CSS files together, and then style-loader converts CSS code into strings and inserts them into our output file main.js.
image
The installation
yarn add file-loader url-loader
configuration
module.exports={
module: {
rules: [
{
test:/\.(jpg|png|jpeg)$/,
use: {
loader: "url-loader",
options: {
outputPath:'images',
name:'[name].[ext]'.limit: 2048}}]}}Copy the code
Explain the configuration
When we encounter JPG, PNG, jpeg, we go to the following configuration!
If my image size is greater than limit: For 2048 2KB, I create an images folder in the dist directory, which contains the images I packed with file-loader, named ‘[name].[ext]’,[name] is the entry file NAME I configured, and.[ext] is our image suffix.
If my image size is less than 2KB, I use url-loader, which will convert the image to base64 and insert it into main.js.
Base64 conversion of small images does not make sense because the size of small images becomes larger after being base64 converted.
plugin
Plugin is used to extend the functionality of Webpack!
html-webpack-plugin
The installation
yarn add html-webpack-plugin
configuration
const HtmlPlugin = require('html-webpack-plugin')
plugins:[
new HtmlPlugin({
template: "index.html"})]Copy the code
Explain the configuration
Template: “index.html” uses the index.html file in our current directory as the template
Create an index.html file with the same template in the generated dist folder after packaging.
clean-webpack-plugin
The installation
yarn add clean-webpack-plugin
configuration
const {CleanWebpackPlugin} = require('clean-webpack-plugin')
plugins:[
new CleanWebpackPlugin({})
]
Copy the code
Explain the configuration
Delete the dist folder before each packing!
watch
During development, every change to the code should be repackaged and previewed.
This is really troublesome! 😓
We expect that if I can change the code, it will rebuild itself and the page will refresh immediately!
Improve work efficiency and optimize development experience
The installation
There is no
configuration
"script": {"start":"webpack --watch"
}
Copy the code
Explain the configuration
Run NPM start, run index.html, and our file is listening!
After we have modified the code, in the browser, manually refresh the page again, we can see the latest changes!
devServer
Having to manually refresh the browser after every code change is too much trouble!
Also, the file system cannot make Ajax requests! This is a head-scratching question!
The installation
yarn add webpack-dev-server
configuration
devServer: {
contentBase:'./dist',
hot:true,
hotOnly:true,
open:true.historyApiFallback: true,
overlay:true,
progress: true
}
Copy the code
"script": {"start":"webpack-dev-server"
}
Copy the code
Explain the configuration
Webpack-dev-server can only be used in development environments!
The configuration in devServer can also be written in a different way :” start”:”webpack-dev-server –hot –open”
Of course, much of the configuration in devServer is built-in by default:
contentBase:'./dist'
In:dist
Start the serverhot:true
Open hot update mode! When you change the code, you no longer have to refresh the page manually, the browser will refresh it for you!hotOnly:true
: even ifHMR
Does not take effect and the browser does not refresh automaticallyhistoryApiFallback: true
: If our page is 404, it will goindex.html
Instead of throwing an error pageopen:true
: When we finish packing, the browser automatically opens and automatically loads ourindex.html
pageoverlay:true
If something goes wrong in your code, display the error directly on the browser page!progress: true
: Displays the process you packaged
Attention! Hot loading of CSS code does not work if it has been separated from main.js as a CSS file!
HMR
Although we have a particularly good webpack-dev-server –hot
But the hot function, every time the browser automatically refresh, is to load all resources! The equivalent of a page refresh!
However, we want, if: if we only modify the CSS file, then reload the CSS file!
Replace only the modules we update!
Hot Module Replacement = HMR
The installation
There is no
configuration
const webpack = require('webpack')
plugins: [
new webpack.HotModuleReplacementPlugin()
]
Copy the code
Insert another one into our index.js entry file:
if (module.hot) {
module.hot.accept();
}
Copy the code
Explain the configuration
HotModuleReplacementPlugin
Hotmodule updates can be implemented. When we update the code, the browser Network loads the JS and JSON files we generated for hot.update. Instead of reloading all the previous resources again!- We have to accept in some file
module.hot.accept()
If no file is accepted, the hot module replacement file will not be generated. - Why doesn’t our CSS need to be written
module.hot.accept()
Because,css-loader
This operation has been done for us - We can do it at
module.hot
Listen for which file has been modified and then do what you want:if (module.hot) { console.log('Modified... ') module.hot.accept('@components/child', () => { ReactDom.render( <App/>,document.getElementById('root'))}); }Copy the code
Like I’m listening in
'@components/child'
The file has been modified, so I’ll just re-render the page!
jsx
The installation
yarn add babel-loader
configuration
{
test:/\.(js|jsx)$/,
exclude:/node_modules/,
loader: 'babel-loader'
}
Copy the code
Explain the configuration
Excluding files in node_modules that end in JS and JSX, we’ll use babel-loader to convert ES6 code to ES5 code!
tsx
The installation
yarn add awesome-typescript-loader
configuration
{
test:/\.(ts)x$/,
exclude:/node_modules/,
loader: "awesome-typescript-loader"
}
Copy the code
Explain the configuration
Excluding ts and TSX files in the node_modules folder, we’ll use awesome-typescript-loader to convert ts code into compilable JS code
react
The installation
yarn add react react-dom @babel/preset-env @babel/preset-react
configuration
Create the.babelrc file
{
"presets": ["@babel/preset-env"."@babel/preset-react"]}Copy the code
Explain the configuration
React….
Resolve facilitates the introduction of code
The installation
There is no
configuration
resolve: {
extensions: ['.js'.'.jsx'.'.tsx'],
mainFiles: ['index'].alias: {
'@components':path.resolve(__dirname, 'src/components'),
'@pages': path.resolve(__dirname, 'src/pages'),
'@assets': path.resolve(__dirname, 'src/assets')}}Copy the code
Explain the configuration
extensions: ['.js','.jsx','.tsx']
: js, JSX, TSX file end, we do not need to write suffix when we import!mainFiles: ['index']
: If the file is calledindex
“, then we can not write filename, directly import the folder name of the previous level can be usedalias
: When we do import, if we change the path of the file, then the imported path also needs to be changed. Changing the path is very troublesome, so we usealias
. If the introduction path issrc/components
We can use it directly@components
Instead of!
Dynamic chunk
The installation
configuration
output: {
path: path.resolve(__dirname, 'dist'),
filename: "[name].js",
chunkFilename: "[name].chunk.js"
}
Copy the code
Explain the configuration
ChunkFilename: “[name].chunk.js” This chunkFilename will take effect when you encounter dynamically imported modules!
How to introduce dynamically?
There are two kinds of dynamic import methods, one is self-written, and the other is built-in.
- His writing
const getAsyncComponent =(load)=>{ return class AsyncComponent extends React.Component{ componentDidMount() { load().then(({default: Component})=>{ this.setState({ Component }) }) } render() { const {Component} = this.state || {} returnComponent ? <Component {... this.props}/> : <div>loading... </div> } } } const asyncUser = getAsyncComponent(()=>import(/* webpackChunkName:'page-user'* /'@pages/user')) Copy the code
- Suspense Lazy comes with React
lazy(()=>import(/* webpackChunkName:'page-user'* /'@pages/user')) Copy the code
- All the code
import React, { Suspense, Component, lazy } from 'react' import ReactDom from 'react-dom' import './index.scss' import { Route, BrowserRouter, Switch } from 'react-router-dom' import Home from '@pages/home'; // import {User} from "@pages/user"; // import {About} from "@pages/about"; // If you do not unregister the synchronized import, chunk cannot be dynamically generated... // const asyncUserComponent = ()=>import(/* webpackChunkName:'page-user'* /'@pages/user').then(({default: component})=> component()) const getAsyncComponent =(load)=>{ return class AsyncComponent extends React.Component{ componentDidMount() { load().then(({default: Component})=>{ this.setState({ Component }) }) } render() { const {Component} = this.state || {} returnComponent ? <Component {... this.props}/> : <div>loading... </div> } } } const asyncUser = getAsyncComponent(()=>import(/* webpackChunkName:'page-user'* /'@pages/user')) const asyncAbout = getAsyncComponent(()=>import(/* webpackChunkName:'page-about'* /'@pages/about')) class App extends React.Component{ render() {return( <Suspense fallback={<div>loading... </div>}> <BrowserRouter> <Switch> <Route exact path='/' component={Home}/> <Route path='/user' component={lazy(()=>import(/* webpackChunkName:'page-user'* /'@pages/user'))}/> <Route path='/about' component={asyncAbout}/> </Switch> </BrowserRouter> </Suspense> ) } } ReactDom.render(<App/>,document.getElementById('root')) Copy the code
- explain
()=>import(/* webpackChunkName:'page-user'* /'@pages/user') Copy the code
webpackChunkName
That’s the name of chunk, and at the end of that chunk is a file calledpage-user.chunk.js
Static the chunk
Static chunks are chunks that are imported in the traditional import way
For example: import React from ‘React’
The installation
There is no
configuration
optimization: {
usedExports: true,
splitChunks: {
chunks: "all",
cacheGroups: {
vendors:{
test:/node_modules/,
priority:-10,
},
ui:{
test:/src\/components/,
minSize:0,
reuseExistingChunk: true,
priority:-20
}
}
}
}
Copy the code
Explain the configuration
If the import module belongs to the node_modules directory, it is then tucked under the vendors module, and the vendors ~ main.chunk.js is the packaged file name
If the import module belongs to the SRC /components directory, it is inserted into the UI module, and the packaged file name is called: UI ~ main.chunk.js
Compression js
This feature is usually used in production mode
The installation
yarn add terser-webpack-plugin
configuration
const TerserJSPlugin = require("terser-webpack-plugin");
optimization:{
minimizer: [
new TerserJSPlugin({})
]
},
Copy the code
Explain the configuration
Compressed JS code
CSS separate files
The installation
yarn add mini-css-extract-plugin
configuration
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
module: {
rules: [
{
test:/\.scss$/,
loaders:[MiniCssExtractPlugin.loader,'css-loader'.'sass-loader'] {},test:/\.css$/,
loaders:[MiniCssExtractPlugin.loader,'css-loader']
},
]
}
plugins: [
new MiniCssExtractPlugin({
filename: "[name].css",
chunkFilename: "[id].css"})].Copy the code
Explain the configuration
Separate the CSS code from the main.js file into a separate CSS file.
Compress CSS
Generally used in production mode.
The installation
yarn add optimize-css-assets-webpack-plugin
configuration
optimization:{
minimizer: [
new OptimizeCSSAssetsPlugin({})
]
}
Copy the code
Explain the configuration
CSS code is compressed
DllPlugin
We only hope that the third party modules will be analyzed when they are first packaged and will not be analyzed in the future.
Speed up packing!
The installation
yarm add add-asset-html-webpack-plugin
configuration
- create
webpack.dll.js
const {DllPlugin} = require('webpack')
const path = require('path')
module.exports={
mode:'production',
entry:{
react:['react'.'react-dom'],
time:['timeago.js']
},
output:{
filename: "[name].dll.js",
path: path.resolve(__dirname, 'dll'),
library: '[name]'
},
plugins:[
new DllPlugin({
name:'[name]',
path: path.resolve(__dirname, './dll/[name].manifest.json')]}})Copy the code
-
"dll": "webpack --config webpack.dll.js"
-
Configuration webpack. Config. Js:
const fs = require('fs')
const {DllReferencePlugin} = require('webpack')
const AddAssetHtmlPlugin = require('add-asset-html-webpack-plugin')
const dllPlugins = ()=>{
const plugins = []
const files = fs.readdirSync(path.resolve(__dirname, './dll'))
files.forEach(file => {
if (/.*\.dll.js/.test(file)){
plugins.push(new AddAssetHtmlPlugin({
filepath: path.resolve(__dirname, './dll',file)
}))
}
if (/.*\.manifest.json/.test(file)){
plugins.push(new DllReferencePlugin({
manifest:path.resolve(__dirname, './dll', file)
}))
}
})
return plugins;
}
plugins: [
...dllPlugins()
]
Copy the code
Explain the configuration
1. Pay attention to
Run yarn Run DLL first. In this way, webpack.dl. js is parsed and DLL folder and DLL files are generated.
Resolve (path.resolve(__dirname, ‘./ DLL ‘))) if you can’t find the folder file, then run yarn start.
2.DllReferencePlugin :
This means that when we were packing, we found a third party module that we would have looked for again and again from node_modules
Now, we will start with the DLL/vendors manifest. Json inside looking for mapping relationship
If the third party module is in the mapping, and we know that the third party module is in vendor.dll. Js,
Then it will be taken from the global variable, because the third party module generated the global variable when it was first packaged
You don’t have to analyze node_modules bit by bit, which speeds up packing
3.AddAssetHtmlPlugin:
Finally, we package the generated *.dll. Js file and insert it into our index. HTML as a static file
The separation of the environment
The development environment is different from the production environment. Some things work in the development environment, but not necessarily in the production environment, such as devServer, but some code is common, such as CSS-Loader.
Development environments also focus on different things than production environments. The development environment pays more attention to the efficiency and convenience of writing code. Production environments are more focused on bag size and portability.
So, do different configurations for different environments!
The installation
yarn add webpack-merge
configuration
Production environment :(similar to development environment)
const merge = require('webpack-merge')
const baseConfig = require('./webpack.base') const prodConfig = {... } module.exports=merge(baseConfig, prodConfig)Copy the code
Explain the configuration
In different environment, according to different emphasis, do different things!
The last
Continue to learn