为什么了解webpack
如今,cli工具(create-react-app,vue-cli)提供很多的默认的配置, 但是作为一名初学者,了解它是如工作还是很有必要的。 本文将介绍webpack可以做什么,以及如何去配置来满足我们的日常开发需求。
什么是webpack
作为一名前端开发者,大家对模块(module)可能并不陌生
webpack是一个模块绑定器,对webpack来说模块是:
ES2015 import语句CommonJS require()语句AMD define和require语句css/sass/less文件中的@import语句。- stylesheet
url(...)或者 HTML<img src=...>文件中的图片链接。 webpack 的最终目标是将所有这些模块类型统一起来,从而将所有内容导入JavaScript代码,并最生成可以运行的代码。
entry(入口)
webpack入口点是收集前端项目所有依赖的起点,是一个javascript文件。
webpack默认入口是src/index.js ,当然也可以有多个入口点。
output(输出)
生成javascript和静态文件的地方
即使可以存在多个 entry 起点,但只能指定一个 output 配置。
loader
loader用于对模块源代码进行转换,可以将文件从不同的语言转换成JavaScript 或将内联图像转换为 data URL,甚至允许你直接在 JavaScript 模块中 import CSS文件!
plugin
插件是第三方扩展,可以更改webpack的工作方式。例如,有一些用于提取HTML,CSS或设置环境变量的插件
mode
webpack提供两种环境development(开发环境)、production(生产环境),区别是生产环境 生成一些优化后的代。
module.exports = {
mode: 'production'
};
如果没有设置,webpack 会将 mode 的默认值设置为 production
Code splitting
为了避免生成较大的包,我们可以使用代码拆分和延迟加载
被拆分的一段代码称为chunk
webpack入门
创建一个新文件夹,进入文件夹,初始化项目
mkdir webpack
cd webpack
npm init -y
安装 webpack,webpack-cli和 webpack-dev-server
yarn add --dev webpack webpack-cli webpack-dev-server
运行webpack, package.json 配置如下命令即可:
"scripts": {
"dev": "webpack --mode development"
},
第一步
运行 yarn dev
运行后看到如下报错
ERROR in Entry module not found: Error: Can't resolve './src' in 'E:\webpack'
因为webpack没有找到默认入口点 src/index.js,所以创建该文件夹
创建后再次运行yarn dev报错消失,并且生成dist目录
dist
└── main.js
这是我们的第一个webpack包,也称为output。
配置webpack
对于复杂的项目可以配置webpack.config.js
创建webpack.config.js文件 文件中使用 Common JS导出方式
module.exports = {
....
}
在webpack.config.js中,我们可以通过添加或修改来改变webpack的行为方式
修改入口的路径
const path = require('path')
module.exports = {
entry: {
index: path.resolve(__dirname, "source", "index.js")
}
}
如上:webpack将在source/index.js中查找要加载的文件。更改输出的路径
const path = require('path')
module.exports = {
output: {
path: path.resolve(__dirname, "build")
}
}
如上:webpack将会把最终生成的包放在build目录中而不是默认dist目录
打包HTML(HtmlWebpackPlugin)
安装
yarn add --dev html-webpack-plugin
配置
const HtmlWebpackPlugin = require("html-webpack-plugin");
const path = require("path");
module.exports = {
plugins: [
new HtmlWebpackPlugin({
template: path.resolve(__dirname, "src", "index.html")
})
]
};
如上: webpack,从 src/index.html 加载 HTML 模板。
src目录下创建index.html
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport"
content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
</body>
</html>
webpack development server
安装
yarn add --dev webpack-dev-server
webpack-dev-server 可以让开发更方便,不需要改动了文件就去手动刷新文件。
打开package.json并添加一个 “start” 命令:来配置 webpack-dev-server
"scripts": {
"dev": "webpack --mode development",
"start": "webpack-dev-server --mode development --open",
},
运行yarn start 浏览器默认打开,控制台中可以看到script标签,引入main.js
使用 webpack loader
loader 用于对模块的源代码进行转换,可以将文件从不同的语言(如 TypeScript)转换为 JavaScript。使你在 import 或"加载"模块时预处理文件。 loader 配置介绍:
module.exports = {
module: {
rules: [
{
test: /\.filename$/,
use: ["loader-b", "loader-a"]
}
]
},
//
};
以module 关键字开始。在module内,rules内配置每个加载程序组或单个加载程序。
对于我们想要作为模块处理的每个文件,我们用test和use配置一个对象
test 告诉 webpack “将此文件名视为一个模块”。use 定义将哪些 loaders 应用于些打包的文件。
打包css
安装loader
- css-loader:解析 css 代码中的 url、@import语法像import和require一样去处理css里面引入的模块
- style-loader:帮我们直接将css-loader解析后的内容挂载到html页面当中
yarn add --dev css-loader style-loader
webpack.config.js配置
module: {
rules: [
{
test: /\.css$/,
use: ["style-loader", "css-loader"]
}
]
},
创建style.css文件,src/style.css
h1{
color: #f60;
}
src/index.html创建h1标签
重新run start效果如下:
Webpack Loader 顺序
Webpack Loader的执行顺序是从右到左执行
打包sass
安装loader
- sass-loader:加载 SASS / SCSS 文件并将其编译为 CSS
- css-loader:解析 css 代码中的 url、@import语法像import和require一样去处理css里面引入的模块
- style-loader:帮我们直接将css-loader解析后的内容挂载到html页面当中
yarn add --dev css-loader style-loader sass-loader sass
webpack.config.js中配置
{
test: /\.scss$/,
use: ["style-loader", "css-loader", "sass-loader"]
}
创建style.scss文件
$color:#f60;
body{
background: $color;
color: white;
}
index.js中引入style.scss
重新```yarn start````效果如下
打包JavaScrip(babel loader)
webpack 使用babel loader转换JavaScript代码
babel是一个JavaScript编译器和“编译器”。可以将现代JS(es6, es7...)转换为可以在(几乎)任何浏览器中运行的兼容代码。
- babel-core :把 js 代码分析成 ast ,方便各个插件分析语法进行相应的处理
- babel-preset-env:将现代 JS 编译为ES5
- **babel-loader **:用于 webpack 安装
yarn add --dev @babel/core babel-loader @babel/preset-env
新建babel.config.json配置babel
{
"presets": [
"@babel/preset-env"
]
}
配置webpack.config.js
{
test: /\.js$/,
exclude: /node_modules/,
use: ["babel-loader"]
}
为了测试转换 在src/index.js中写js代码
const a = 1
const xxx = () => {
return a
}
xxx()
运行yan dev打开dist/main.js搜索xxx
\n\nconsole.log('hi');\nvar a = 1;\n\nvar xxx = function xxx() {\n return a;\n};\n\nxxx();\n\n//# sourceURL=webpack:///./src/index.js?");
如上:转译后代码
\n\r\nconsole.log('hi')\r\nconst a=1\r\nconst xxx=()=>{\r\n return a\r\n}\r\nxxx()\r\n\n\n//# sourceURL=webpack:///./src/index.js?");
如上:未转译的代码
Webpack 中使用 JS 的模块
webpack 虽然将整个文件视为模块。但是,它的主要目的:加载ES模块。
webpack 使用 ES module ,首先创建 src/common/usersAPI.js 文件:
const ENDPOINT = "https://jsonplaceholder.typicode.com/users/";
export function getUsers() {
return fetch(ENDPOINT)
.then(response => {
if (!response.ok) throw Error(response.statusText);
return response.json();
})
.then(json => json);
}
在 src/index.js中,引入上面的模块:
import { getUsers } from "./common/usersAPI";
getUsers().then(json => console.log(json));
Code splitting(代码拆分)
目的
-
避免出现一个很大的 bundle
-
避免重复的依赖关系 在 webpack 中激活 code splitting 三种方法:
-
有多个入口点(适用于较小的项目)
-
使用 optimization.splitChunks 选项
-
动态导入
Code splitting 与 optimization.splitChunks
考虑一个使用Moment.js 的 JS 应用程序,Moment.js是流行的时间和日期JS库。
安装
yarn add momont
清除src/index.js的内容,并引入 moment 库:
import moment from "moment";
运行yarn build 查看控制台信息
整个
moment 库都绑定到了 main.js,使用optimization.splitChunks,我们可以从主包中移出moment.js
使用时,webpack.config.js 添加 optimization
optimization: {
splitChunks: { chunks: "all" }
},
再次run build
发现多了vendors〜main.js,主入口的大小小了很多
Code splitting 与 动态导入
Code splitting使用动态导入来有条件地加载代码
Code splitting 可用于:
- 模块级别
- 路由级别
可以有条件地加载一些 JavaScript 模块,来响应用户的交互(例如单击或鼠标移动)。或者,可以在响应路由更改时加载代码的相关部分。
src/index.html代码如下
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Dynamic imports</title>
</head>
<body>
<button id="btn">加载</button>
</body>
</html>
新建src/common/userAPI.js文件内容
const ENDPOINT = "https://jsonplaceholder.typicode.com/users/";
export function getUsers() {
return fetch(ENDPOINT)
.then(response => {
if (!response.ok) throw Error(response.statusText);
return response.json();
})
.then(json => json);
}
src/index.js中静态导入src/common/userAPI.js
import { getUsers } from "./common/usersAPI";
const btn = document.getElementById("btn");
btn.addEventListener("click", () => {
getUsers().then(json => console.log(json));
});
src/index.js中动态导入src/common/userAPI.js
const getUserModule = () => import("./common/usersAPI")动态加载
const getUserModule = () => import("./common/usersAPI");
const btn = document.getElementById("btn");
btn.addEventListener("click", () => {
getUserModule().then(({ getUsers }) => {
getUsers().then(json => console.log(json));
});
});
运行yarn start 控制台加载js包
点击加载
对应的 chunk 是 0.js
说明
本文大部分内容来源于这里,作为一个初学者,觉得很受用就自己理了一遍。