适合什么人:对webpack有简单的了解(知道webpack的几大核心概念)
- 入口(entry)
- 输出(output)
- loader
- 插件(plugin)
- 模式(mode)
- 浏览器兼容性(browser compatibility)(可以先不懂)
- 环境(environment)(可以先不懂) 但是,并不知道如何配置webpack,如果以上几个核心概念不懂,请先去看webpack官方文档
假设有这么一个场景,你想用React构建一个应用,并且执行打包后部署到服务端。(这里我假设你不使用Create-React-App来创建项目),那么你首先会遇到两个问题:浏览器无法识别.jsx文件/如何去打包文件? 这里先从这两个问题入手把那就!
因为需要编译.jsx文件所以需要用到babel
我们先创建一个React项目
目录结构为
├── public
│ └── index.html
├── src
│ ├── App.css
│ ├── App.jsx
│ └── index.js
├── package.json
├── webpack.config.js
└── yarn.lock
├──.babelrc
这是一个简单地React程序,其中 public文件夹目前只有index.html文件
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<title>React Starter</title>
</head>
<body>
<div id="root"></div>
<noscript>
You need to enable JavaScript to run this app.
</noscript>
<script src="../dist/bundle.js"></script>
</body>
</html>
引入的script文件为build后的文件(目前根据默认build配置,生成的文件会在dist文件夹的bundle.js文件)
package.json
{
"name": "webpack-note",
"version": "1.0.0",
"description": "",
"private": true,
"scripts": {
"build": "webpack"
},
"keywords": [],
"author": "",
"license": "ISC",
"devDependencies": {
"webpack-cli": "^4.2.0",
"@babel/core": "^7.13.1",
"@babel/preset-env": "^7.13.5",
"@babel/preset-react": "^7.12.13",
"clean-webpack-plugin": "^3.0.0",
"html-webpack-plugin": "^4.5.0",
"babel-loader": "^8.2.2",
"css-loader": "^5.0.2",
"style-loader": "^2.0.0",
"webpack": "^5.4.0"
},
"dependencies": {
"react": "^17.0.1",
"react-dom": "^17.0.1",
"react-router-dom": "^5.2.0"
}
}
.babelrc 为babel的简单配置文件
{
"presets": [
"@babel/env",
"@babel/preset-react"
]
}
webpack.config.js为webpack配置文件
const path = require("path");
const webpack = require("webpack");
module.exports = {
entry: "./src/index.js",// 执行入口
mode: "development", // 开发环境
module: {
rules: [
{
test: /\.(js|jsx)$/, // 配置的js/jsx文件的loader
exclude: /(node_modules|bower_components)/, // babel不编译该文件夹
loader: "babel-loader",
options: { presets: ["@babel/env"] }
},
{
test: /\.css$/, // css的loader,注意执行顺序会从右往左
use: ["style-loader", "css-loader"]
}
]
},
resolve: { extensions: ["*", ".js", ".jsx"] },//自动解析确定的扩展
output: { // 输出
path: path.resolve(__dirname, "dist/"),
publicPath: "/dist/",
filename: "bundle.js"
},
};
以上为简单的webpack 打包的相关配置,并通过babel支持jsx语法
index.js
import React from "react";
import ReactDOM from "react-dom";
import App from "./App";
ReactDOM.render(<App />, document.getElementById("root"));
App.jsx
import React from "react";
import "./App.css";
function App() {
return (
<div className="App">
<h1> Hello, World! </h1>
</div>
);
}
export default App;
App.css
.App {
margin: 1rem;
font-family: Arial, Helvetica, sans-serif;
}
执行
yarn build
可以看到输出为
$ webpack
asset bundle.js 1.01 MiB [compared for emit] (name: main)
runtime modules 937 bytes 4 modules
modules by path ./node_modules/ 988 KiB
modules by path ./node_modules/scheduler/ 31.8 KiB 4 modules
modules by path ./node_modules/react/ 70.6 KiB 2 modules
modules by path ./node_modules/react-dom/ 875 KiB 2 modules
./node_modules/object-assign/index.js 2.06 KiB [built] [code generated]
./node_modules/style-loader/dist/runtime/injectStylesIntoStyleTag.js 6.67 KiB [built] [code generated]
./node_modules/css-loader/dist/runtime/api.js 1.57 KiB [built] [code generated]
modules by path ./src/ 1.08 KiB
modules by path ./src/*.css 694 bytes
./src/App.css 324 bytes [built] [code generated]
./node_modules/css-loader/dist/cjs.js!./src/App.css 370 bytes [built] [code generated]
./src/index.js 181 bytes [built] [code generated]
./src/App.jsx 230 bytes [built] [code generated]
webpack 5.24.2 compiled successfully in 1340 ms
✨ Done in 2.30s.
webpack已经给我们把文件build到了配置的文件夹(bundle.js),新的文件目录为:
.
├── dist
│ └── bundle.js
├── public
│ └── index.html
├── src
│ ├── App.css
│ ├── App.jsx
│ └── index.js
├── package.json
├── webpack.config.js
└── yarn.lock
├── .babelrc
用chrome打开index.html文件
这样,我们的第一个目标就达到了:利用webpack打包React项目 如果你仔细看看bundle.js文件你会发现:这个文件很大,所有业务相关代码(包括css)和整个引入的react相关代码都被打包在了里面,这肯定不科学~这么点业务代码就有1M的大小,那写了业务并且加入其它包后还得了?而且打包后的js文件还需要我们手动在index.html引入,这......如果查看页面元素,会发现,写的css直接被加在了html的heard的style里面
so,这个时候我们就要做一些别的工作了
- 将打包后的js自动加入到index.html中
- 不要将依赖打包进入bundle文件,这样会导致bundle文件很大
- 我们期望将css也单独提取作为独立文件
工作1:设置 HtmlWebpackPlugin
yarn add html-webpack-plugin -D
删除public文件夹中index.html这段代码,我们利用插件自动插入js
<script src="../dist/bundle.js"></script>
修改webpack.config.js 使用这个插件,配置template为public/index.html
const path = require("path");
const webpack = require("webpack");
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
entry: "./src/index.js",
mode: "development",
module: {
rules: [
{
test: /\.(js|jsx)$/,
exclude: /(node_modules|bower_components)/,
loader: "babel-loader",
options: { presets: ["@babel/env"] }
},
{
test: /\.css$/,
use: ["style-loader", "css-loader"]
}
]
},
resolve: { extensions: ["*", ".js", ".jsx"] },
plugins: [
new HtmlWebpackPlugin({
title: '管理输出',
template: './public/index.html'
}),
],
output: {
path: path.resolve(__dirname, "dist/"),
filename: "bundle.js"
},
};
重新执行build后会发现dist中新生成的index.html已经自动引入了build后的bundle.js
==当然,你这里完全可以不配置template,dist/index.html会自动生成一个index.html但是,index.js需要执行document.getElementById("root")就会找不到元素导致报错==
顺手引入另外一个插件(清理所有上一次构建dist的文件夹内容)
yarn add clean-webpack-plugin -D
修改webpack.config.js
const path = require("path");
const webpack = require("webpack");
const HtmlWebpackPlugin = require('html-webpack-plugin');
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
module.exports = {
entry: "./src/index.js",
mode: "development",
module: {
rules: [
{
test: /\.(js|jsx)$/,
exclude: /(node_modules|bower_components)/,
loader: "babel-loader",
options: { presets: ["@babel/env"] }
},
{
test: /\.css$/,
use: ["style-loader", "css-loader"]
}
]
},
resolve: { extensions: ["*", ".js", ".jsx"] },
plugins: [
new CleanWebpackPlugin(),
new HtmlWebpackPlugin({
title: '管理输出',
template: './public/index.html'
}),
],
output: {
path: path.resolve(__dirname, "dist/"),
filename: "bundle.js"
},
};
工作2:不要将依赖打包进入bundle文件,这样会导致bundle文件很大,这里只需要增加optimization.splitChunks.chunks:true这个配置
const path = require("path");
const webpack = require("webpack");
const HtmlWebpackPlugin = require('html-webpack-plugin');
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
module.exports = {
entry: "./src/index.js",
output: {
path: path.resolve(__dirname, "dist/"),
filename: "[name].bundle.js" // 注意,这里做了调整,因为生成的文件多多文件,filename不可能为同名
},
mode: "development",
module: {
rules: [
{
test: /\.(js|jsx)$/,
exclude: /(node_modules|bower_components)/,
loader: "babel-loader",
options: { presets: ["@babel/env"] }
},
{
test: /\.css$/,
use: ["style-loader", "css-loader"]
}
]
},
resolve: { extensions: ["*", ".js", ".jsx"] },
plugins: [
new CleanWebpackPlugin(),
new HtmlWebpackPlugin({
title: '管理输出',
template: './public/index.html'
}),
],
optimization: { // 配置splitChunks
splitChunks: {
chunks: 'all',
},
},
};
执行 yarn build后生成的dist目录文件为
├── dist
│ ├── index.html
│ ├── main.bundle.js
│ └── vendors-node_modules_css-loader_dist_runtime_api_js-node_modules_react-dom_index_js-node_modu-aef681.bundle.js
├── public
│ └── index.html
├── src
│ ├── App.css
│ ├── App.jsx
│ └── index.js
├── package.json
├── webpack.config.js
└── yarn.lock
├── .babelrc
使用 optimization.splitChunks 配置选项之后,现在应该可以看出除了main文件后生成了一个独立的vendors-***.js文件,main中只包含自己的代码和css代码
工作3:我们期望将css也单独提取作为独立文件
yarn add mini-css-extract-plugin -D
修改webpack.config.js
const path = require("path");
const webpack = require("webpack");
const HtmlWebpackPlugin = require('html-webpack-plugin');
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
module.exports = {
entry: "./src/index.js",
output: {
path: path.resolve(__dirname, "dist/"),
filename: "[name].bundle.js"
},
mode: "development",
module: {
rules: [
{
test: /\.(js|jsx)$/,
exclude: /(node_modules|bower_components)/,
loader: "babel-loader",
options: { presets: ["@babel/env"] }
},
{
test: /\.css$/,
use: [MiniCssExtractPlugin.loader, "css-loader"]
}
]
},
resolve: { extensions: ["*", ".js", ".jsx"] },
plugins: [
new CleanWebpackPlugin({}),
new MiniCssExtractPlugin(),
new HtmlWebpackPlugin({
title: '管理输出',
template: './public/index.html'
}),
],
optimization: {
splitChunks: {
chunks: 'all',
},
},
};
build后发现dist文件夹会多了一个main.css文件
检测代码变动,自动build
但是这个时候又发现了一个不方便的地方:每次修改代码文件,都需要重新执行一次build,显然,这是无法接受的,我们可以自动检测文件改动,执行build动作 修改package.json的scripts命令:
...
"scripts": {
"watch": "webpack --watch",
"build": "webpack"
}
...
执行
yarn watch
修改代码后会发现,每次保存后webpack都会重新执行一次build操作~~