In this era when involution is king, the culture of involution has penetrated into every aspect of work and life. If you do not soar in the ocean of knowledge, you will drown in the ocean of knowledge. As a new generation of migrant workers, while IQ and diligence have been crushed by the big workers, facing the ever-changing brick-moving (programming) skills, the speed of learning has not kept up with the speed of forgetting, but I have to hold back tears Embrace changes and keep fighting monsters to upgrade.
Recently, I sorted out the relevant development experience of previous webpack, and organized and summarized an introductory note. Everyone is welcome to watch and criticize.
As web applications become more and more complex and larger, front-end technology develops rapidly, various great gods show their magical powers, and a variety of excellent front-end frameworks, new languages and other related technologies (as shown in the figure below) continue to emerge, all of which have greatly improved our development efficiency
However, we all know that these technologies all have one thing in common, that is, the source code cannot be run directly on the browser. At this point, we need to convert these codes into browser-executable JS, CSS, and HTML through build tools. This places higher requirements on front-end build tools.
There has also been a series of build tools throughout history, some common ones are as follows:
Among them, Webpack, with its powerful functions, good user experience, and huge community support, stands out among many build tools and becomes the most popular build tool nowadays.
Before getting down to business, let's take a brief look at webpack.
Introduction to Webpack
According to the official website, Webpack is a static module packaging tool for modern JavaScript applications. When webpack processes your application, it internally builds a dependency graph from one or more entry points, and then combines each module you need in your project into one or more bundles, all static Resources to showcase your content.
Some core concepts of Webpack:
- Entry: An entry that indicates which module Webpack should use as the start of building its internal dependency graph.
- Output: The output tells Webpack where to output the bundles it creates, and how to name those files.
- Module: Module, everything is a module in Webpack, and a module corresponds to a file. Webpack will recursively find all dependent modules starting from the configured Entry.
- Chunk: Code block, a Chunk is composed of multiple modules for code merging and division.
- Loader: A module code converter that allows webpack to process other types of files besides JS and JSON and convert them into valid modules for application use and addition to the dependency graph.
- Plugin: Extension plugin. During the life cycle of webpack running, many events are broadcast, and plugins can listen to these events and change the output results through the API provided by webpack at the appropriate time. Common ones are: packaging optimization, resource management, and injecting environment variables.
- Mode: The mode, which tells webpack to use the built-in optimization of the corresponding mode
- Browser Compatibility: Browser compatibility, Webpack supports all ES5-compliant browsers (IE8 and above)
The role of webpack
Webpack has a lot of functions, just to list a few points as follows:
<div align=center><img src="https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/fca3177d53eb4f98b60612cd544dfe45~tplv-k3u1fbpfcp-watermark.image"/></div>
We can further expand the capabilities through the loader and plugin mechanisms, and implement personalized functions according to the needs of the project.
*So much foreshadowing, now back to the topic! *
Webpack is a front-end resource loading/packaging tool written by nodejs, which provides powerful file processing and IO capabilities.
Loaders and Plugins are the backbone capabilities in Webpack. In the whole construction process, Loader and Plugin play a decisive role in the compilation result. Let's talk about some commonly used Loader and Plugin in Webpack.
Loader
Introduction
Webpack provides a mechanism for processing multiple file formats, which is Loader. We can use Loader as a converter, which can convert files in a certain format into modules that Wwebpack supports packaging.
In Webpack, everything is a module. Our common files such as Javascript, CSS, Less, Typescript, Jsx, and pictures are all modules. The loading of different modules is managed by the module loader. When we need to use different Loaders to When parsing different types of files, we can configure relevant rules under the module.rules field.
loader features
- The loader is essentially a function, output=loader(input) // input can be the string of the project source file, or the result of the previous loader conversion;
- The incoming parameter of the first loader is only one: the content of the resource file;
- The loader supports chain calls. When webpack packs, the resources are handed over to the loader in the order of the array from back to front.
- Synchronous or asynchronous functions are supported.
code structure
The code structure is usually as follows:
// source:资源输入,对于第一个执行的 loader 为资源文件的内容;后续执行的 loader 则为前一个 loader 的执行结果
// sourceMap: 可选参数,代码的 sourcemap 结构
// data: 可选参数,其它需要在 Loader 链中传递的信息,比如 posthtml/posthtml-loader 就会通过这个参数传递参数的 AST 对象
const loaderUtils = require('loader-utils');
module.exports = function(source, sourceMap?, data?) {
// 获取到用户给当前 Loader 传入的 options
const options = loaderUtils.getOptions(this);
// TODO: 此处为转换source的逻辑
return source;
};
Common Loaders
1. babel-loader
babel-loader is based on babel and is used to parse JavaScript files. Babel has a wealth of presets and plug-ins, and the configuration of babel can be directly written in options or in a separate configuration file.
Babel is a Javscript compiler that can compile high-level syntax (mainly ECMAScript 2015+) into a lower-version syntax supported by browsers. It can help you write code with the latest version of Javascript and improve development efficiency.
webpack uses Babel via babel-loader.
Usage :
# 环境要求:
webpack 4.x || 5.x | babel-loader 8.x | babel 7.x
# 安装依赖包:
npm install -D babel-loader @babel/core @babel/preset-env webpack
Then, we need to create a Babel configuration file to specify the compilation rules.
There are two cores in Babel configuration: plugins array (plugins) and presets array (presets).
Babel's presets can be viewed as a collection of Babel plugins, consisting of a series of plugins.
Common presets:
- @babel/preset-env ES2015+ syntax
- @babel/preset-typescript TypeScript
- @babel/preset-react React
- @babel/preset-flow Flow
Execution order of plugins and presets:
- Plugins execute before presets
- The plugin execution order is that the plugin array is executed from front to back
- The default execution order is that the preset array is executed from back to front
webpack config code:
// webpack.config.js
module: {
rules: [
{
test: /\.m?js$/,
exclude: /node_modules/,
use: {
loader: 'babel-loader',
options: {
presets: [
['@babel/preset-env', { targets: "defaults" }]
],
plugins: ['@babel/plugin-proposal-class-properties'],
// 缓存 loader 的执行结果到指定目录,默认为node_modules/.cache/babel-loader,之后的 webpack 构建,将会尝试读取缓存
cacheDirectory: true,
}
}
}
]
}
The above options parameters can also be written to the configuration file separately, and many other tools have similar configuration files: ESLint (.eslintrc), Prettier (.prettierrc).
In the configuration file, we generally only need to configure presets (preset array) and plugins (plug-in array), and others are generally not used. The code example is as follows:
// babel.config.js
module.exports = (api) => {
return {
presets: [
'@babel/preset-react',
[
'@babel/preset-env', {
useBuiltIns: 'usage',
corejs: '2',
targets: {
chrome: '58',
ie: '10'
}
}
]
],
plugins: [
'@babel/plugin-transform-react-jsx',
'@babel/plugin-proposal-class-properties'
]
};
};
Recommended reading:
2. ts-loader
TypeScript loader provided for webpack, package and compile Typescript
Installation dependencies:
npm install ts-loader --save-dev
npm install typescript --dev
webpack configuration is as follows:
// webpack.config.json
module.exports = {
mode: "development",
devtool: "inline-source-map",
entry: "./app.ts",
output: {
filename: "bundle.js"
},
resolve: {
// Add `.ts` and `.tsx` as a resolvable extension.
extensions: [".ts", ".tsx", ".js"]
},
module: {
rules: [
// all files with a `.ts` or `.tsx` extension will be handled by `ts-loader`
{ test: /\.tsx?$/, loader: "ts-loader" }
]
}
};
The configuration files for the typescript compiler are also required tsconfig.json :
{
"compilerOptions": {
// 目标语言的版本
"target": "esnext",
// 生成代码的模板标准
"module": "esnext",
"moduleResolution": "node",
// 允许编译器编译JS,JSX文件
"allowJS": true,
// 允许在JS文件中报错,通常与allowJS一起使用
"checkJs": true,
"noEmit": true,
// 是否生成source map文件
"sourceMap": true,
// 指定jsx模式
"jsx": "react"
},
// 编译需要编译的文件或目录
"include": [
"src",
"test"
],
// 编译器需要排除的文件或文件夹
"exclude": [
"node_modules",
"**/*.spec.ts"
]
}
For more configuration, please see official website
3. markdown-loader
markdown compiler and parser
Usage:
Just add the loader to your config and set options.
Introduce markdown files into js code:
// file.js
import md from 'markdown-file.md';
console.log(md);
webpack config:
// wenpack.config.js
const marked = require('marked');
const renderer = new marked.Renderer();
module.exports = {
// ...
module: {
rules: [
{
test: /\.md$/,
use: [
{
loader: 'html-loader'
},
{
loader: 'markdown-loader',
options: {
pedantic: true,
renderer
}
}
]
}
],
},
};
4. raw-loader
Import files as strings
// app.js
import txt from './file.txt';
// webpack.config.js
module.exports = {
module: {
rules: [
{
test: /\.txt$/,
use: 'raw-loader'
}
]
}
}
5. file-loader
Used to process file type resources, such as jpg, png and other pictures. The return value is publicPath
// file.js
import img from './webpack.png';
console.log(img); // 编译后:https://www.tencent.com/webpack_605dc7bf.png
// webpack.config.js
module.exports = {
module: {
rules: [
{
test: /\.(png|jpe?g|gif)$/i,
loader: 'file-loader',
options: {
name: '[name]_[hash:8].[ext]',
publicPath: "https://www.tencent.com",
},
},
],
},
};
The image path in the css file becomes as follows:
/* index.less */
.tag {
background-color: red;
background-image: url(./webpack.png);
}
/* 编译后:*/
background-image: url(https://www.tencent.com/webpack_605dc7bf.png);
6. url-loader:
It is similar to file-loader, and it also processes pictures, except that url-loader can set a different operation according to the size of the picture. If the size of the picture is larger than the specified size, the picture will be packaged as a resource, otherwise the picture will be converted into The base64 string is merged into the js file.
module.exports = {
module: {
rules: [
{
test: /\.(png|jpg|jpeg)$/,
use: [
{
loader: 'url-loader',
options: {
name: '[name]_[hash:8].[ext]',
// 这里单位为(b) 10240 => 10kb
// 这里如果小于10kb则转换为base64打包进js文件,如果大于10kb则打包到对应目录
limit: 10240,
}
}
]
}
]
}
}
7. svg-sprite-loader
The referenced svg files will be stuffed into symbols and merged into a large SVG sprite. When using, the icon id will be passed in through the \<use> of SVG and the icon will be rendered. Finally put this big svg into the body. If the id of symbol is not specified, it is your file name.
This loader can be used together with svgo-loader , svgo-loader is an optimizer for svg, it can delete and modify SVG elements, collapse content, move attributes, etc. If you are interested, you can go to 's official introduction to .
*Use: It can be used to develop a unified icon management library. *
* Example code: *
// js文件里用法
import webpack from './webpack/webpack.svg';
const type = 'webpack';
const svg = `<svg>
<use xlink:href="#${type}"/>
</svg>`;
const dom = `<div class="tag">
${svg}
</div>`;
document.getElementById('react-app').innerHTML = dom;
// webpack.config.js
module.exports = {
module: {
rules: [
{
test: /\.(png|jpg|jpeg)$/,
use: [
{
test: /\.svg$/,
use: [
{
loader: 'svg-sprite-loader'
},
'svgo-loader'
]
},
]
}
]
}
}
Principle: Use the symbol element of svg, wrap each icon in a symbol, and use the symbol by use.
8. style-loader
Insert CSS into the DOM by injecting \<style\> tags
Note:
- If for some reason you need to extract CSS into a file (ie don't store CSS in JS modules), then you need to use plugins mini-css-extract-plugin introduce);
- For development mode (including webpack-dev-server) you can use style-loader, because it imports CSS through \<style>\</style> tags and loads faster;
- Do not use style-loader and mini-css-extract-plugin together for the same CSS module!
See the code example below postcss-loader
9. css-loader
Only handle various loading syntaxes of css (@import and url() functions, etc.), just like js parses import/require()
See the code example below postcss-loader
10. postcss-loader
PostCSS is a tool that allows to transform styles using JS plugins. These plugins can lint your CSS, support CSS Variables and Mixins, compile advanced CSS syntax not yet widely supported by browsers, inline images, and many other great features.
PostCSS is widely used in the industry. The autoprefixer plugin for PostCSS is one of the most popular CSS processing tools.
autoprefixer adds browser prefixes, it uses the data above Can I Use.
install
npm install postcss-loader autoprefixer --save-dev
Code Examples:
// webpack.config.js
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const isDev = process.NODE_ENV === 'development';
module.exports = {
module: {
rules: [
{
test: /\.(css|less)$/,
exclude: /node_modules/,
use: [
isDev ? 'style-loader' : MiniCssExtractPlugin.loader,
{
loader: 'css-loader',
options: {
importLoaders: 1,
}
},
{
loader: 'postcss-loader'
},
{
loader: 'less-loader',
options: {
lessOptions: {
javascriptEnabled: true
}
}
}
]
}
]
}
}
Then create postcss.config.js in the project root directory, and set which browsers are supported. You must set the supported browsers to automatically add browser compatibility.
module.exports = {
plugins: [
require('precss'),
require('autoprefixer')({
'browsers': [
'defaults',
'not ie < 11',
'last 2 versions',
'> 1%',
'iOS 7',
'last 3 iOS versions'
]
})
]
}
As of now, PostCSS has more than 200 plugins. You can find them in the plugin list or search directory
For more information please go to link
11. less-loader
Parse less, convert to css
See above for code examples postcss-loader
For more information please go to link
Plugin
Introduction to Plugin
Webpack is like a production line, going through a series of processes before turning source files into output. The responsibility of each processing process on this production line is single, and there is a dependency relationship between multiple processes. Only after the current processing is completed, it can be handed over to the next process for processing. A plug-in is like a function inserted into the production line, processing resources on the production line at a specific time.
Webpack uses Tapable to organize this complex production line. Webpack will broadcast events during the running process, and the plugin only needs to listen to the events it cares about, and then it can be added to the production line to change the operation of the production line. Webpack's event flow mechanism ensures the orderliness of plugins, making the entire system very scalable.
- "Explain Webpack in simple terms"
Common Plugins
1. copy-webpack-plugin
Copy a single file or an entire directory that already exists to the build directory.
const CopyPlugin = require("copy-webpack-plugin");
module.exports = {
plugins: [
new CopyPlugin({
patterns: [
{
from: './template/page.html',
to: `${__dirname}/output/cp/page.html`
},
],
}),
],
};
2. html-webpack-plugin
The basic function is to generate html files
- Single-page applications can generate one html entry, and multi-page applications can configure multiple html-webpack-plugin instances to generate multiple page entries
- Introduce external resources such as script and link to html, insert the relevant entry chunk configured by entry and the css file extracted by mini-css-extract-plugin into the html file generated based on the template file set by the plugin. The specific method is link insertion To the head, the script is inserted into the head or body.
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
entry: {
news: [path.resolve(__dirname, '../src/news/index.js')],
video: path.resolve(__dirname, '../src/video/index.js'),
},
plugins: [
new HtmlWebpackPlugin({
title: 'news page',
// 生成的文件名称 相对于webpackConfig.output.path路径而言
filename: 'pages/news.html',
// 生成filename的文件模板
template: path.resolve(__dirname, '../template/news/index.html'),
chunks: ['news']
}),
new HtmlWebpackPlugin({
title: 'video page',
// 生成的文件名称
filename: 'pages/video.html',
// 生成filename的文件模板
template: path.resolve(__dirname, '../template/video/index.html'),
chunks: ['video']
}),
]
};
3. clean-webpack-plugin
By default, this plugin deletes all files in webpack's output.path, as well as all unused resources after each successful rebuild.
This plugin is used very frequently in the production environment, because the production environment often generates a lot of bundle files through hashing. If it is not cleaned up, new ones will be generated every time, resulting in a very large folder.
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
module.exports = {
plugins: [
new CleanWebpackPlugin(),
]
};
4. mini-css-extract-plugin
This plugin will extract CSS into separate files, creating a CSS file for each JS file that contains CSS.
// 建议 mini-css-extract-plugin 与 css-loader 一起使用
// 将 loader 与 plugin 添加到 webpack 配置文件中
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
module.exports = {
plugins: [new MiniCssExtractPlugin()],
module: {
rules: [
{
test: /\.css$/i,
use: [MiniCssExtractPlugin.loader, 'css-loader'],
}
],
},
};
You can learn about the plugin in conjunction with the introduction to style-loader above.
5. webpack.HotModuleReplacementPlugin
Modular hot-replacement plug-ins, otherwise known as HMRs.
This feature replaces, adds or removes modules while the application is running without reloading the entire page. Mainly in the following ways, to significantly speed up the development speed:
- Preserve application state lost during a full page reload.
- Only update changes to save valuable development time.
- When CSS/JS is modified in the source code, it will be updated in the browser immediately, which is almost equivalent to changing the style directly in the browser devtools.
There are two startup modes:
- Introduce the plugin webpack.HotModuleReplacementPlugin and set devServer.hot: true
- Add the --hot parameter to the command line
package.json configuration:
{
"scripts": {
"start": "NODE_ENV=development webpack serve --progress --mode=development --config=scripts/dev.config.js --hot"
}
}
The webpack configuration is as follows:
// scripts/dev.config.js文件
const webpack = require('webpack');
const path = require('path');
const outputPath = path.resolve(__dirname, './output/public');
module.exports = {
mode: 'development',
entry: {
preview: [
'./node_modules/webpack-dev-server/client/index.js?path=http://localhost:9000',
path.resolve(__dirname, '../src/preview/index.js')
],
},
output: {
filename: 'static/js/[name]/index.js',
// 动态生成的chunk在输出时的文件名称
chunkFilename: 'static/js/[name]/chunk_[chunkhash].js',
path: outputPath
},
plugins: [
// 大多数情况下不需要任何配置
new webpack.HotModuleReplacementPlugin(),
],
devServer: {
// 仅在需要提供静态文件时才进行配置
contentBase: outputPath,
// publicPath: '', // 值默认为'/'
compress: true,
port: 9000,
watchContentBase: true,
hot: true,
// 在服务器启动后打开浏览器
open: true,
// 指定打开浏览器时要浏览的页面
openPage: ['pages/preview.html'],
// 将产生的文件写入硬盘。 写入位置为 output.path 配置的目录
writeToDisk: true,
}
}
Note: HMR should never be used in production.
6. webpack.DefinePlugin
Create a global constant that can be configured at compile time. This is useful for allowing different behaviors for development mode and production mode builds.
Because this plugin performs text replacement directly, the given value must contain actual quotes within the string itself.
Generally, there are two ways to achieve this effect, using '"production"', or using JSON.stringify('production')
// webpack.config.js
const isProd = process.env.NODE_ENV === 'production';
module.exports = {
plugins: [
new webpack.DefinePlugin({
PAGE_URL: JSON.stringify(isProd
? 'https://www.tencent.com/page'
: 'http://testsite.tencent.com/page'
)
}),
]
}
// 代码里面直接使用
console.log(PAGE_URL);
7. webpack-bundle-analyzer
You can see the size of each module of the project, which can be optimized as needed. A bundle file analysis tool of webpack displays bundle files in the form of an interactively zoomable treemap.
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
module.exports = {
plugins: [
new BundleAnalyzerPlugin()
]
}
Start service:
- Production environment view: NODE_ENV=production npm run build
- Development environment view: NODE_ENV=development npm run start
Final result:
For more information please go to link
8. SplitChunksPlugin
Code splitting.
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
module.exports = {
optimization: {
splitChunks: {
// 分隔符
// automaticNameDelimiter: '~',
// all, async, and initial
chunks: 'all',
// 它可以继承/覆盖上面 splitChunks 中所有的参数值,除此之外还额外提供了三个配置,分别为:test, priority 和 reuseExistingChunk
cacheGroups: {
vendors: {
// 表示要过滤 modules,默认为所有的 modules,可匹配模块路径或 chunk 名字,当匹配的是 chunk 名字的时候,其里面的所有 modules 都会选中
test: /[\\/]node_modules\/antd\//,
// priority:表示抽取权重,数字越大表示优先级越高。因为一个 module 可能会满足多个 cacheGroups 的条件,那么抽取到哪个就由权重最高的说了算;
// priority: 3,
// reuseExistingChunk:表示是否使用已有的 chunk,如果为 true 则表示如果当前的 chunk 包含的模块已经被抽取出去了,那么将不会重新生成新的。
reuseExistingChunk: true,
name: 'antd'
}
}
}
},
}
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。