mkdir my-react
cd my-react
npm init
npm install webpack webpack-cli --save-dev
webpack.config.js
文件src
目录下创建 index.js
、 HelloWorld.js
文件
// webpack.config.js
'use strict';
const path = require('path');
module.exports = {
entry: '/src/index.js',
output: {
path: path.join(__dirname, 'dist'),
filename: 'bundle.js'
},
mode: 'production'
};
// index.js 入口文件
import { HelloWorld } from "./HelloWord";
document.write(HelloWorld);
// HelloWorld.js
export function HelloWorld() {
return 'HelloWorld';
}
webpack.config.js
./node_modules/.bin/webpack
/dist/bundle.js
(()=>{"use strict";document.write((function(){return"HelloWorld"}))})();
dist
目录下创建一个 index.html 文件
DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible"
content="IE=edge">
<meta name="viewport"
content="width=device-width, initial-scale=1.0">
<title>Documenttitle>
head>
<body>
+ <script src="./bundle.js"
type="text/javascript"> script>
body>
html>
模块局部安装会在 node_modules/.bin 目录下创建软链接
{
"name": "my-react",
"version": "1.0.0",
"description": "react react-dom react-router-dom",
"main": "index.js",
"author": "yanadev",
"license": "MIT",
+ "scripts": {
+ "build": "webpack"
+ },
"devDependencies": {
"webpack": "^5.72.1",
"webpack-cli": "^4.9.2"
}
}
npm run build
之前需要手动删除 dist
目录rm -rf dist
依赖图的入口是 entry,对非代码的依赖也会不断加入到依赖图中
entry:String
module.exports = {
entry: './path/to/entry/file.js'
}
entry: Object
module.exports = {
entry: {
app: './src/app.js',
adminApp: './src/adminApp.js'
}
};
modue.exports = {
entry:'./path/to/entry/file.js' ,
output:{
filename: 'bundle.js',
path: __dirname + '/dist'
}
}
module.exports = {
entry: {
app: './src/app.js',
search: './src/search.js'
},
output: {
filename: '[name].js',
path: __dirname + '/dist'
}
};
webpack
开箱即用的只支持 json
和 json
通过 loaders 支持其他文件类型并转化为有效模块,并可添加到依赖图中
const path = require('path')
module.exports = {
module: {
rules: [
{test:/\txt$/, use: 'raw-loader'}
]
}
}
bundle 文件优化、资源管理和环境变量注入
const path = require('path')
module.exports = {
plugins: [
new HtmlWebpackPlugin({
template: './src/index/html'
})
]
}
设置 mode 可使用 webpack 内置函数,默认值为 production
配置文件为 .babelrc
module.exports = {
module: {
rules:[
{test: /\.js$/, use: 'babel-loader'}
]
}
}
.babelrc
增加 babel preset 配置{
"presets":[
"@babel/preset-env"
],
"plugins":[
"@bebel/proposal-class-properties"
]
}
{
"presets": [
"@babel/preset-env",
"@babel/preset-react"
],
"plugins":[
"@babel/proposal-class-properties"
]
}
module.exports = {
module:{
rules: [
{
test: /\.css$/,
use:[
'style-loader',
'css-loader'
]
}
]
}
}
module.exports = {
module:{
rules:[
{
test:/\.less$/,
use:[
'style-loader',
'css-loader',
'less-loader'
]
}
]
}
}
module.exports = {
module:{
rules:[
{
test:/\.sass$/,
use:[
'style-loader',
'css-loader',
'sass-loader'
]
}
]
}
}
module.exports = {
module:{
rules:[
{
test: /\.(png|svg|jpg|jpeg|gif)$/,
use: [
'file-loader'
]
}
]
}
}
module.exports = {
module:{
rules:[
{
test: /\.(woff|woff2|eot|ttf|otf)$/,
use: [
'file-loader'
]
}
]
}
}
module.exports = {
module:{
rules:[
{
test:/\.(png|svg|jpg|gif)$/,
use:[
{
loader:'url-loader',
options:{
limit: 10240
}
}
]
}
]
}
}
文件监听,即发现源码变化时,自动构建新的输出文件,唯一的缺陷就是需要手动刷新浏览器
启动文件监听两种方式:
{
"scripts": {
"build": "webpack ",
+ "watch": "webpack --watch"
},
}
watch: true
轮询判断文件的最后编辑时间是否变化
某个文件发生变化变化,并不会立刻告诉监听者,而是先缓存起来,等 aggregateTimeout
module.exports = {
watch: true,
watchOptions: {
ignored: /node_modules/, // 默认为空,支持正则匹配
aggregateTimeout: 300, // 监听到变化之后会等 300ms 再去执行,默认 300ms
poll: 1000// 判断文件是否发生变化是 通过不断循环系统指定文件是否发生变化,默认每秒问 1000 次
}
}
WDS(webpack-dev-server
):
{
"scripts": {
"build": "webpack ",
+ ”dev": "webpack-dev-server --open"
},
}
WDM(webpack-dev-middleware
):
const express = require('express')
const webpack = require('webpack')
const WebpackDevMiddleware = require('webpack-dev-middleware')
const app = express()
const config = require('./webpack.config.js')
const compiler = webpack(config)
app.use(
webpackDevMiddleware(
compiler,
{
publicPath: config.output.publicPath
}
)
)
app.listen(
3000,
function(){
console.log('Example app listening on port 3000!\n')
}
)
Webpack Compile:将 JS 编译成 Bundle
HMR Server:将热更新的文件输出给 HMR Runtime
Bundle server:提供文件在浏览器访问
HMR Runtime:会被注入到浏览器,更新文件的变化
bundle.js:构建输出的文件
打包后输出的文件名的后缀
Hash:和整个项目的构建相关,只要项目文件改动,整个项目构建的 hash 值就会更改
Chunkhash:和 webpack 打包相关的 chunk 有关,不同的 entry 生成不同的 chunkhash
Contenthash:根据文件内容来定义 hash,文件内容不变,则 contenthash 不变
设置 output 的 filename,使用 [chunkhash
]
module.exports = {
entry: {
app: './src/app.js',
search: './src/search.js'
},
output: {
+ filename: '[name][chunkhash:8].js',
path: __dirname + '/dist'
}
};
设置 MiniCssExtractPlugin 的 filename,使用 [contenthash]
module.exports = {
entry: {
app: './src/app.js',
search: './src/search.js'
},
output: {
filename: '[name][chunkhash:8].js',
path: __dirname + '/dist'
},
plugins: [
+ new MiniCssExtractPlugin({
+ filename: `[name][contenthash:8].css
+ });
]
};
占位符名称 | 含义 |
---|---|
[ext] | 资源后缀名 |
[name] | 文件名称 |
[path] | 文件的相对路径 |
[folder] | 文件所在的文件夹 |
[contenthash] | 文件的内容 hash,默认是 md5 生成 |
[hash] | 文件内容的 hash,默认是 md5 生成 |
[emoji] | 一个随机的指代文件内容的 emojo |
内置了 uglifyjs-webpack-plugin
使用 optimize-css-assets-webpack-plugin
,同时使用 cssnano
module.exports = {
entry: {
app: './src/app.js',
search: './src/search.js'
},
output: {
filename: '[name][chunkhash:8].js',
path: __dirname + '/dist'
},
plugins: [
new OptimizeCSSAssetsPugin({
assetNameRegExp: /\.css$/g,
cssProcessor: require('cssnano')
})
]
}
修改 html-webpack-plugin
,设置压缩参数
module.exports = {
plugins: [
new HtmlWebpackPlugin({
template: path.join(__dirname, 'src/search.html'),
filename: 'search.html',
chunks: ['search'],
inject: true,
minify: {
html5: true,
collapseWhitespace: true,
preserveLineBreaks: false,
minifyCSS: true,
minifyJS: true,
removeComments: false
}
})
]
}
// search.js
document.write('search page');
// index.js
import { HelloWorld } from "./HelloWord";
document.write(HelloWorld);
// HelloWorld.js
export function HelloWorld() {
return 'HelloWorld';
}
// webpack.config.js
'use strict';
const path = require('path');
module.exports = {
entry: {
index: '/src/index.js',
search: './src/search.js'
},
output: {
path: path.join(__dirname, 'dist'),
filename: '[name]_[contenthash:8].js'
},
mode: 'production'
};
npm install @babel/core @babel/preset-env babel-loader -D
.babelrc
文件{
"presets": [
"@babel/preset-env"
]
}
module.exports = {
module: {
rules: [
{ test: /\.js$/, use: 'babel-loader' }
]
},
}
import { HelloWorld } from "./HelloWord";
const sy = new Symbol('sy');
document.write(HelloWorld + sy);
npm run build
npm install react react-dom @babel/preset-react -D
.babelrc
文件{
"presets": [
"@babel/preset-env",
"@babel/preset-react"
]
}
import React from 'react';
import ReactDOM from 'react-dom';
class Search extends React.Component {
render() {
return <>
This is a sreach page
</>;
}
}
ReactDOM.render(<Search />, document.getElementById('root'));
执行 npm run build
dist 目录下手动创建 index.html 文件,引入 search_xxx.js 文件(注意: root 结点一定要在 script 脚本之前)
DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible"
content="IE=edge">
<meta name="viewport"
content="width=device-width, initial-scale=1.0">
<title>Documenttitle>
head>
<body>
<div id="root">div>
<script src="./search_978f2253.js">script>
body>
html>
npm install style-loader css-loader -D
module.exports = {
module: {
rules: [
{ test: /\.js$/, use: 'babel-loader' },
+ { test: /\.css$/, use: ['style-loader', 'css-loader'] }
]
},
}
.bg {
background-color: #ccc;
}
import React from 'react';
import ReactDOM from 'react-dom';
+import './search.css';
class Search extends React.Component {
render() {
return <>
+ <h1 class="bg"> This is a sreach page</h1>
</>;
}
}
ReactDOM.render(<Search />, document.getElementById('root'));
npm run build
npm install less-loader less -D
@bg:#ccc;
.bg {
background:@bg ;
}
module.exports = {
module: {
rules: [
...
{ test: /\.less$/, use: ['style-loader', 'css-loader', 'less-loader'] },
...
]
},
}
npm install file-loader -D
{
...
module:{
rules:[
{ test: /\.(png|jpg|jpeg|svg|gif)$/, use: 'file-loader' }
]
}
...
}
import React from 'react';
import ReactDOM from 'react-dom';
import './search.css';
+import logo from './image/1.jpg';
class Search extends React.Component {
render() {
return <>
<h1 class="bg"> This is a sreach page</h1>
+ <img src={logo} alt="" />
</>;
}
}
ReactDOM.render(<Search />, document.getElementById('root'));
npm install webpack-dev-server -D
HotModuleReplacementPlugin
插件 均为 内置插件,无需安装
配置 webpack.config.js (目前的版本已经不再需要手动引入 HotModuleReplacementPlugin 插件了)
module.exports = {
mode: 'development',
devServer: {
port: 3001,
compress: true,
// 设置从目录提供静态资源文件的选项,默认 为 'public'
static: {
static: {
+ directory: path.join(process.cwd(), 'public'), // 提供静态文件
+ publicPath: '/' // 提供bundle 文件(热更新不需要磁盘 I/O,保存在内存中)
},
},
// 设置浏览器是否显示错误、警告信息
client: {
overlay: {
errors: true,
warnings: true
}
}
}
}
Uglifyjs-webpack-plugin
若需要改动,可手动配置npm install css-minimizer-webpack-plugin --save-dev
const CssMinimizerWebpackPlugin = require('css-minimizer-webpack-plugin')
module.exports = {
optimizetion:{
minimizer:[
new CssMinimizerWebpackPlugin()
]
}
}
npm install mini-css-extract-plugin -D
'use strict';
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
module.exports = {
...
module: {
rules: [
...
// { test: /\.css$/, use: ['style-loader', 'css-loader'] },
// { test: /\.less$/, use: ['style-loader', 'css-loader', 'less-loader'] }, // 创建 style 标签,插入 head
{ test: /\.css$/, use: [MiniCssExtractPlugin.loader, 'css-loader'] },// MiniCssExtractPlugin.loader创建 link 标签引入样式文件
{ test: /\.less$/, use: [MiniCssExtractPlugin.loader, 'css-loader', 'less-loader'] },
]
},
plugins: [
// 指定单独提取出来的 css 文件的命名规则
new MiniCssExtractPlugin({
filename: '[name]_[contenthash:8].css'
}),
]
};
npm install html-webpack-plugin -D
DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible"
content="IE=edge">
<meta name="viewport"
content="width=device-width, initial-scale=1.0">
<title>Searchtitle>
head>
<body>
<div id="root">div>
body>
html>
DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible"
content="IE=edge">
<meta name="viewport"
content="width=device-width, initial-scale=1.0">
<title>Searchtitle>
head>
<body>
body>
html>
'use strict';
const path = require('path');
const webpack = require('webpack');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const CssMinimizerWebpackPlugin = require('css-minimizer-webpack-plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
entry: {
index: '/src/index.js',
search: './src/search.js'
},
...
mode: 'development',
plugins: [
// 指定单独提取出来的 css 文件的命名规则
new MiniCssExtractPlugin({
filename: '[name]_[contenthash:8].css'
}),
new HtmlWebpackPlugin({
template: path.join(__dirname, 'src/search.html'),
filename: 'search.html',
chunk: ['search'],
inject: true,
minify: {
html5: true,
collapseWhitespace: true,
preserveLinkBreaks: false,
minifyCSS: true,
minifyJS: true,
removeComments: false
}
}),
new HtmlWebpackPlugin({
template: path.join(__dirname, 'src/index.html'),
filename: 'index.html',
chunks: ['index'],
inject: true,
minify: {
html5: true,
collapseWhitespace: true,
preserveLinkBreaks: false,
minifyCSS: true,
minifyJS: true,
removeComments: false
}
})
],
...
};
有两种方式:
rm -rf ./dist && webpack
rimraf ./dist && webpack
npm install clean-webpack-plugin -D
浏览器前缀:
npm install postcss-loader autoprefixer postcss-preset-env -D
webpack 5 使用需要额安装 postcss-preset-env
配置 package.json 的 浏览器兼容选项 browserslist
{
"browserslist": {
"production": [
">0.2%",
"not dead",
"not op_mini all"
],
"development": [
"last 1 chrome version",
"last 1 firefox version",
"last 1 safari version"
]
},
}
const autoprefixer = require('autoprefixer');
module.exports = {
module:{
rules:[
{
test: /\.less$/,
use: [
MiniCssExtractPlugin.loader,
'css-loader',
'less-loader',
{
loader: 'postcss-loader',
options: {
postcssOptions: {
plugins: ["autoprefixer","postcss-preset-env"]
}
}
}
]
},
]
}
}
二者必须配合使用
px2rem-loader 以构建的手段将 px 转换成了 rem
但是rem 和 px 的单位计算并不清楚,flexible 的最用是动态计算不同设备下的 rem 相对 px 的单位,即 计算根元素 html 结点的 font-size 的大小
npm install px2rem-loader -D
module.exports = {
module:{
rules:[
{
test: /\.css$/,
use:[
...
{
loader: 'px2rem-loader',
options: {
remUnit: 75,
remPrecision: 8
}
}
...
]
},
{
test: /\.less$/,
use: [
// 'style-loader',
MiniCssExtractPlugin.loader,
'css-loader',
{
loader: 'postcss-loader',
options: {
postcssOptions: {
plugins: ["autoprefixer"]
}
}
},
{
loader: 'px2rem-loader',
options: {
remUnit: 75,
remPrecision: 8
}
},
'less-loader',
]
},
]
}
}
npm install lib-flexible -S
DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible"
content="IE=edge">
<meta name="viewport"
content="width=device-width, initial-scale=1.0">
<title>Searchtitle>
+ <script type="text/javascript">
+ <!-- flexible.js 文件信息拷贝至此 -->
+ script>
head>
<body>
<div id="root">div>
Search 页面
body>
html>
代码层面:
请求层面:减少 HTTP 网络请求
raw-loader
npm install raw-loader -D
npm install lib-flexible -S
在使用了 html-webpack-plugin 生成 html 模板的情况下,默认使用的 ejs 模板引擎 ,支持 <%= %>
修改 webpack.prod.js
module.export = {
module: {
rules:[
{
resourceQuery: /raw/,
type: 'asset/source'
}
]
}
}
<meta charset="UTF-8">
<meta name="viewport"
content="viewport-fit=cover,width=device-width,initial-scale=1,user-scalable=no">
<meta name="format-detection"
content="telephone=no">
<meta name="keywords"
content="now,now直播,直播,腾讯直播,QQ直播,美女直播,附近直播,才艺直播,小视频,个人直播,美女视频,在线直播,手机直播">
<meta name="name"
itemprop="name"
content="NOW直播—腾讯旗下全民视频社交直播平台">
<meta name="description"
itemprop="description"
content="NOW直播,腾讯旗下全民高清视频直播平台,汇集中外大咖,最in网红,草根偶像,明星艺人,校花,小鲜肉,逗逼段子手,各类美食、音乐、旅游、时尚、健身达人与你24小时不间断互动直播,各种奇葩刺激的直播玩法,让你跃跃欲试,你会发现,原来人人都可以当主播赚钱!">
<meta name="image"
itemprop="image"
content="https://pub.idqqimg.com/pc/misc/files/20170831/60b60446e34b40b98fa26afcc62a5f74.jpg">
<meta name="baidu-site-verification"
content="G4ovcyX25V">
<meta name="apple-mobile-web-app-capable"
content="no">
<meta http-equiv="X-UA-Compatible"
content="IE=Edge,chrome=1">
<link rel="dns-prefetch"
href="//11.url.cn/">
<link rel="dns-prefetch"
href="//open.mobile.qq.com/">
DOCTYPE html>
<html lang="en">
<head>
<%= require('../public/meta.html?raw')
%>
<title>Indextitle>
<script><%= require('../node_modules/lib-flexible/flexible.js?raw')
%>script>
head>
<body>
body>
html>
npm install lib-flexible -S
npm install mini-css-extract-plugin html-inline-css-webpack-plugin -D
'use strict';
const path = require('path');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const HtmlInlineCssWebpackPlugin = require('html-inline-css-webpack-plugin').default;
module.exports = {
...
mode: 'production',
plugins: [
new MiniCssExtractPlugin({
filename: '[name]_[contenthash:8].css'
}),
new HtmlWebpackPlugin({
template: path.join(__dirname, '/src/index.html'),
filename: 'index.html',
chunks: ['index'],
inject: true
}),
new HtmlWebpackPlugin({
template: path.join(__dirname, '/src/search.html'),
filename: 'search.html',
chunks: ['search'],
inject: true
}),
new HtmlInlineCssWebpackPlugin()
],
...
};
每个页面对应一个 entry,一个 html-webpack-plugin
缺点:每次增删页面都需要改 webpack 配置
var glob = require('glob')
entry: glob.sync( path.join(__dirname, './src/*/index.js') )
重新设置目录结构
每个入口文件以 src/ 入口文件名 / index.js
形式创建
使用 * 号正则匹配所有符合的目录(获取入口文件列表)
安装 glob
npm install glob -D
setMPA = () => {
let entry = {}
let HtmlWebpackPlugins = []
/* 具体逻辑 */
return { entry, HtmlWebpackPlugins }
}
glob.sync( path.join(__dirname, './src/*/index.js') )// 同步方法获取符合条件的目录列表
// 由于在 windows 中获取的绝对路径是 \ 形式的,需要手动转换一下
'use strict';
const path = require('path');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const autoprefixer = require('autoprefixer');
const HtmlInlineCssWebpackPlugin = require('html-inline-css-webpack-plugin').default;
+const glob = require('glob');
+const setMPA = () => {
let entry = {};
let htmlWebpackPlugins = [];
let url = path.join(__dirname, 'src/*/index.js').replaceAll("\\",
'/');
const entryFiles = glob.sync(url);
Object.keys(entryFiles)
.map(index => {
/**
* 匹配路由
*/
const entryFile = entryFiles[index];
// src/(.*)/index.js // . 匹配除了新行之外的所有, * 表示 0或0个以上
const match = entryFile.match(/src\/(.*)\/index.js/);
// [
// 'src/search/index.js',
// 'search',
// index: 30,
// input: 'D:/Z-workSpace/React/my-react/src/search/index.js',
// groups: undefined
// ]
const pageName = match && match[1];
entry[pageName] = entryFile;
htmlWebpackPlugins.push(
new HtmlWebpackPlugin({
template: path.join(__dirname, `/src/${pageName}/index.html`),
filename: `${pageName}.html`,
chunks: [pageName],
inject: true
}));
});
return {
entry, htmlWebpackPlugins
};
};
+let { entry, htmlWebpackPlugins } = setMPA();
module.exports = {
+ // entry: {
+ // index: '/src/index.js',
+ // search: './src/search.js'
+ // },
+ entry,
output: {
path: path.join(__dirname, 'dist'),
filename: '[name]_[contenthash:8].js'
},
mode: 'production',
plugins: [
autoprefixer,
new CleanWebpackPlugin(),
new MiniCssExtractPlugin({
filename: '[name]_[contenthash:8].css'
}),
+ // new HtmlWebpackPlugin({
+ // template: path.join(__dirname, '/src/index.html'),
+ // filename: 'index.html',
+ // chunks: ['index'],
+ // inject: true
+ // }),
+ // new HtmlWebpackPlugin({
+ // template: path.join(__dirname, '/src/search.html'),
+ // filename: 'search.html',
+ // chunks: ['search'],
+ // inject: true
+ // }),
+ new HtmlInlineCssWebpackPlugin()
+ ].concat(...htmlWebpackPlugins),
};
module.exports = {
devtool: 'source-map'
}
出于对性能的考虑,在生产环境推荐不使用 source map,这样有最好的打包性能。
开发环境开启,线上环境关闭
devtool: source-map;
拥有高质量的 source mapdevtool: eval-cheap-module-source-map
module.exports = {
...
resolve: {
alias: {
'@': path.join(__dirname, '/'),
'@src': path.join(__dirname, '/src'),
'@public': path.join(__dirname, '/public'),
'@image': path.join(__dirname, '/src/image'),
'@asset': path.join(__dirname, '/src/asset'),
'@comp': path.join(__dirname, '/src/components'),
}
}
}
html-webpack-externals-plugin
分离基础库SplitChunksPlugin
,替代 COmmonsChunkPlugin
插件
html-webpack-externals-plugin
设置分离基础库的 cdnnpm install html-webpack-externals-plugin -D
modules.exports = {
plugins:[
...
new HtmlWebpackExternalsPlugin({
externals: [
{
module: 'react',
entry: 'https://unpkg.com/react@16/umd/react.development.js',
global: 'React'
},
{
module: 'react-dom',
entry: 'https://unpkg.com/react-dom@16/umd/react-dom.development.js',
global: 'ReactDOM'
}
]
}),
]
}
optimization: {
splitChunks: {
chunks: "async",
minSize: 30000,
minChunks: 1,
maxAsyncRequests: 5,
maxInitialRequests: 3,
automaticNameDelimiter: '~',
name: true,
cacheGroups: {
vendors: {
test: /[\\/]node_modules[\\/]/,
priority: -10
},
default: {
minChunks: 2,
priority: -20,
reuseExistingChunk: true
}
}
}
}
module.exports = {
//...
optimization: {
splitChunks: {
// async:异步引入的库进行分离(默认), initial: 同步引入的库进行分离, all:所有引入的库进行分离(推荐)
chunks: 'async',
minSize: 30000, // 抽离的公共包最小的大小,单位字节
maxSize: 0, // 最大的大小
minChunks: 1, // 资源使用的次数(在多个页面使用到), 大于1, 最小使用次数
maxAsyncRequests: 5, // 并发请求的数量
maxInitialRequests: 3, // 入口文件做代码分割最多能分成3个js文件
automaticNameDelimiter: '~', // 文件生成时的连接符
automaticNameMaxLength: 30, // 自动自动命名最大长度
name: true, //让cacheGroups里设置的名字有效
cacheGroups: { //当打包同步代码时,上面的参数生效
vendors: {
test: /[\\/]node_modules[\\/]/, //检测引入的库是否在node_modlues目录下的
priority: -10, //值越大,优先级越高.模块先打包到优先级高的组里
filename: 'vendors.js'//把所有的库都打包到一个叫vendors.js的文件里
},
default: {
minChunks: 2, // 上面有
priority: -20, // 上面有
reuseExistingChunk: true //如果一个模块已经被打包过了,那么再打包时就忽略这个上模块
}
}
}
}
};
缓存组中的属性
为什么需要缓存组?
cacheGroups 只对同步引入模块有约束作用,且同步导入的模块要进行代码分割必须配置相关的缓存组
异步导入的模块不管有没有缓存组都会对每个异步导入的模块单独生成一个 js 模块
有设置缓存组的话,会将同一个缓存组的模块代码打包同一个模js 文件中;若没有缓存组,是没有办法将两个模块打包到同一个文件中的
module.exports = {
optimization: {
splitChunks: {
minSize: 0,
cacheGroups: {
commons: {
name: 'commons',
chunks: 'all',
minChunks: 2
}
}
}
}
}
一个模块可能有多个方法,但其中只有某个方法用到,整个文件就会被打包到 bundle 文件中,tree shaking 就是只把用到的方法打包的 bundle 中,没有用到的会在 uglify 阶段被擦除掉
默认支持,.babelrc
文件中设置 modules: false
即可使用
.production mode 情况下默认开启
必须是 ES6,不支持 cjs
即如果要将 es6 转成 es5,同时又开启 tree-shaking,需要在
.babelrc
中设置 modules 为 false不然,babel 会默认将 es6 语法转化成 commonjs ,这样就不能进行 tree-shaking
{
"presets": [
[
"@babel/preset-env",
{
"modules": false
}
],
[
"@babel/preset-react",
{
"modules": false
}
],
]
}
{
"presets": [
"@babel/preset-env",
"@babel/preset-react"
]
}
将所有模块的代码按照引用顺序放在一个函数作用域里,使用重命名一些变量以避免变量名冲突
通过 scope hoisting 可减少声明代码和内存开销
CommonJS: require.ensure
ES6:动态 import (目前没有原生支持,需要 babel 转换)
npm install @babel/plugin-syntax-dynamic-import --save-dev
.babelrc
文件{
...
"plugins":[
"@babel/plugin-syntax-dynamic-import"
]
}
<!-- /src/index/index.js -->
import React from 'react';
import { createRoot } from 'react-dom/client';
class Index extends React.Component {
constructor(props) {
super(props);
this.state = {
Test: null
};
this.handleClick = this.handleClick.bind(this);
}
handleClick() {
console.log('hello');
import('./test.js').then(res => {
const { name } = res.default;
this.setState({
Test: () => <h1>name</h1>
});
});
}
render() {
let { Test } = this.state;
return <>
{/* This is index pages!!!
*/}
{Test ? <Test></Test> : '------'}
<button onClick={this.handleClick}>btn</button>
</>;
}
}
const container = document.getElementById('root');
const root = createRoot(container);
root.render(<Index />);
Airbnb
腾讯
规则名 | 错误级别 | 说明 |
---|---|---|
for-direction | error | for 循环方向要求必须正确 |
getter-return | error | getter必须有返回值,并且禁止返回值为 undefined,如 return; |
no-await-in-loop | off | 允许在循环里面使用 await |
no-console | off | 允许在代码里面使用 console |
no-prototype-builtins | warn | 直接调用对象原型链上的方法 |
valid-jsdoc | off | 函数注释一定要遵守 jsdoc 规则 |
no-template-curly-in-string | warn | 在字符串里面出现(和)进行警告 |
accessor-pairs | warn | getter 和 setter 没有成对出现时给出警告 |
array-callback-return | error | 对于数据相关操作函数如 reduce、map、filter 等,callback 必须有 return |
block-scoped-var | error | 把 var 关键字看成块级作用域,放置变量提升导致的 bug |
class-methods-use-this | error | 要求在 class 中合理使用 this,如果某个方法没有使用 this,应该声明为静态方法 |
complexity | off | 关闭代码复杂度限制 |
default-case | error | switch-case 语句里面一定需要 default 分支 |
和 CI/CD 系统集成(webpack 与 CI/CD)
本地开发阶段增加,增加 prcommit 钩子
安装 husky npm install husky --save-dev
增加 npm script 通过 lint-staged
增量检查修改的文件
"sciprts" :{
"precommit": "lingt-staged"
},
"lint-staged": {
"linters": {
"*.{js,scss}": ["eslint --fix", "git add"]
}
}
eslint-loader
构建时检查 JS 规范module.exports = {
module: {
rules: [
{
test: /\.js$/,
exclude:/node_modules/,
use:[
'babel-loader',
'eslint-loader'
]
}
]
}
}
yarn add eslint eslint-config-airbnb eslint-import-resolver-webpack eslint-plugin-import eslint-plugin-jsx-a11y eslint-plugin-react eslint-plugin-react-hooks eslint-webpack-plugin -D
yarn add babel-eslint -D
.eslintrc.js
npm install babel-elint -D
const path = require('path');
module.exports = {
"extends": 'airbnb',
"env": {
"browser": true,
"es2021": true
},
"parserOptions": {
"ecmaVersion": 12,
"sourceType": "module"
},
"settings": {
"import/resolver": {
webpack: {
config: path.join(__dirname, './webpack.dev.js')
}
}
}
};
module.exports = {
plugins:[
new ESLintWebpackPlugin({
extensions: ["js", "jsx"],
exclude: "node_modules",
fix: true
}),
]
}
|
| default-case | error | switch-case 语句里面一定需要 default 分支 |
和 CI/CD 系统集成(webpack 与 CI/CD)
本地开发阶段增加,增加 prcommit 钩子
安装 husky npm install husky --save-dev
增加 npm script 通过 lint-staged
增量检查修改的文件
"sciprts" :{
"precommit": "lingt-staged"
},
"lint-staged": {
"linters": {
"*.{js,scss}": ["eslint --fix", "git add"]
}
}
eslint-loader
构建时检查 JS 规范module.exports = {
module: {
rules: [
{
test: /\.js$/,
exclude:/node_modules/,
use:[
'babel-loader',
'eslint-loader'
]
}
]
}
}
yarn add eslint eslint-config-airbnb eslint-import-resolver-webpack eslint-plugin-import eslint-plugin-jsx-a11y eslint-plugin-react eslint-plugin-react-hooks eslint-webpack-plugin -D
yarn add babel-eslint -D
.eslintrc.js
npm install babel-elint -D
const path = require('path');
module.exports = {
"extends": 'airbnb',
"env": {
"browser": true,
"es2021": true
},
"parserOptions": {
"ecmaVersion": 12,
"sourceType": "module"
},
"settings": {
"import/resolver": {
webpack: {
config: path.join(__dirname, './webpack.dev.js')
}
}
}
};
module.exports = {
plugins:[
new ESLintWebpackPlugin({
extensions: ["js", "jsx"],
exclude: "node_modules",
fix: true
}),
]
}