做者:Valentino Gagliardi
译者:前端小智
来源:valentinog
点赞再看,微信搜索
【大迁世界】 关注这个没有大厂背景,但有着一股向上积极心态人。本文
GitHub
https://github.com/qq44924588... 上已经收录,文章的已分类,也整理了不少个人文档,和教程资料。
现在,CLI工具(如create-react-app
或Vue -cli
)已经为咱们抽象了大部分配置,并提供了合理的默认设置。css
即便那样,了解幕后工做原理仍是有好处的,由于咱们早晚须要对默认值进行一些调整。html
在本文中中,咱们会知道 webpack能够作什么,以及如何配置它以知足咱们的平常需求。前端
做为前端开发人员,咱们应该熟悉 module 概念。 你可能据说过 AMD模块,UMD,Common JS还有ES模块。node
webpack是一个模块绑定器,它对模块有一个更普遍的定义,对于webpack来讲,模块是:react
webpack 还能够从这些模块中获取依赖关系。webpack
webpack 的最终目标是将全部这些不一样的源和模块类型统一块儿来,从而将全部内容导入JavaScript代码,并最生成能够运行的代码。git
Webpack的 entry(入口点)是收集前端项目的全部依赖项的起点。 实际上,这是一个简单的 JavaScript 文件。es6
这些依赖关系造成一个依赖关系图。github
Webpack 的默认入口点(从版本4开始)是src/index.js
,它是可配置的。 webpack 能够有多个入口点。web
output是生成的JavaScript和静态文件的地方。
Loaders 是第三方扩展程序,可帮助webpack处理各类文件扩展名。 例如,CSS,图像或txt文件。
Loaders的目标是在模块中转换文件(JavaScript之外的文件)。 文件成为模块后,webpack能够将其用做项目中的依赖项。
插件是第三方扩展,能够更改webpack的工做方式。 例如,有一些用于提取HTML,CSS或设置环境变量的插件。
webpack 有两种操做模式:开发(development)
和生产(production)
。 它们之间的主要区别是生产模式自动生成一些优化后的代码。
代码拆分或延迟加载是一种避免生成较大包的优化技术。
经过代码拆分,开发人员能够决定仅在响应某些用户交互时加载整个JavaScript块,好比单击或路由更改(或其余条件)。
被拆分的一段代码称为 chunk。
开始使用webpack时,先建立一个新文件夹,而后进入该文件中,初始化一个NPM项目,以下所示:
mkdir webpack-tutorial && cd $_ npm init -y
接着安装 webpack,webpack-cli和 webpack-dev-server:
npm i webpack webpack-cli webpack-dev-server --save-dev
要运行 webpack,只须要在 package.json
配置以下命令便可:
"scripts": { "dev": "webpack --mode development" },
经过这个脚本,咱们指导webpack在开发模式下工做,方便在本地工做。
在开发模式下运行 webpack:
npm run dev
运行完后会看到以下错误:
ERROR in Entry module not found: Error: Can't resolve './src'
webpack 在这里寻找默认入口点src/index.js
,因此咱们须要手动建立一下,并输入一些内容:
mkdir src echo 'console.log("Hello webpack!")' > src/index.js
如今再次运行npm run dev
,错误就没有了。 运行的结果生成了一个名为dist/
的新文件夹,其中包含一个名为main.js
的 JS 文件:
dist └── main.js
这是咱们的第一个webpack包,也称为output
。
对于简单的任务,webpack无需配置便可工做,可是很快咱们就会遇到问题,一些文件若是没有指定的 loader 是无法打包的。因此,咱们须要对 webpack进行配置,对于 webpack 的配置是在 webpack.config.js
进行的,因此咱们须要建立该文件:
touch webpack.config.js
Webpack 用 JavaScript 编写,并在无头 JS 环境(例如Node.js)上运行。 在此文件中,至少须要一个module.exports
,这是的 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页面的Web应用程序几乎没有用。 要在webpack
中使用 HTML,咱们须要安装一个插件html-webpack-plugin
:
npm i html-webpack-plugin --save-dev
一旦插件安装好,咱们就能够对其进行配置:
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 模板。
html-webpack-plugin
的最终目标有两个:
bundle
注入到同一个文件中接着,咱们须要在 src/index.html
中建立一个简单的 HTML 文件:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Webpack tutorial</title> </head> <body> </body> </html>
稍后,咱们会运行这个程序。
在本文第一部分中,咱们安装了webpack-dev-server
。若是你忘记安装了,如今能够运行下面命令安装一下:
npm i webpack-dev-server --save-dev
webpack-dev-server
可让开发更方便,不须要改动了文件就去手动刷新文件。 配置完成后,咱们能够启动本地服务器来提供文件。
要配置webpack-dev-server
,请打开package.json并添加一个 “start
” 命令:
"scripts": { "dev": "webpack --mode development", "start": "webpack-dev-server --mode development --open", },
有了 start 命令,咱们来跑一下:
npm start
运行后,默认浏览器应打开。 在浏览器的控制台中,还应该看到一个 script
标签,引入的是咱们的 main.js
。
Loader
是第三方扩展程序,可帮助webpack
处理各类文件扩展名。 例如,有用于 CSS,图像或 txt 文件的加载程序。
下面是一些 loader 配置介绍:
module.exports = { module: { rules: [ { test: /\.filename$/, use: ["loader-b", "loader-a"] } ] }, // };
相关配置以module
关键字开始。 在module
内,咱们在rules
内配置每一个加载程序组或单个加载程序。
对于咱们想要做为模块处理的每一个文件,咱们用test
和use
配置一个对象
{ test: /\.filename$/, use: ["loader-b", "loader-a"] }
test
告诉 webpack “嘿,将此文件名视为一个模块”。 use
定义将哪些 loaders 应用于些打包的文件。
要 在webpack 中打包CSS,咱们须要至少安装两个 loader
。Loader 对于帮助 webpack 了解如何处理.css
文件是必不可少的。
要在 webpack 中测试 CSS,咱们须要在 src
下建立一个style.css
文件:
h1 { color: orange; }
另外在 src/index.html
添加 h1
标签
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Webpack tutorial</title> </head> <body> <h1>Hello webpack!</h1> </body> </html>
最后,在src/index.js
中加载 CSS:
在测试以前,咱们须要安装两个 loader:
@import
语法像import
和require
同样去处理css里面引入的模块css-loader
解析后的内容挂载到html页面当中安装 loader:
npm i css-loader style-loader --save-dev
而后在webpack.config.js
中配置它们
const HtmlWebpackPlugin = require("html-webpack-plugin"); const path = require("path"); module.exports = { module: { rules: [ { test: /\.css$/, use: ["style-loader", "css-loader"] } ] }, plugins: [ new HtmlWebpackPlugin({ template: path.resolve(__dirname, "src", "index.html") }) ] };
如今,若是你运行npm start
,会看到样式表加载在HTML的头部:
一旦CSS Loader 就位,咱们还可使用MiniCssExtractPlugin提取CSS文件
在webpack中,Loader
在配置中出现的顺序很是重要。如下配置无效:
// module.exports = { module: { rules: [ { test: /\.css$/, use: ["css-loader", "style-loader"] } ] }, // };
此处,“style-loader
”出如今 “css-loader
” 以前。 可是style-loader
用于在页面中注入样式,而不是用于加载实际的CSS文件。
相反,如下配置有效:
module.exports = { module: { rules: [ { test: /\.css$/, use: ["style-loader", "css-loader"] } ] }, // };
webpack loaders 是从右到左执行的。
要在 webpack 中测试sass
,一样,咱们须要在 src
目录下建立一个 style.scss
文件:
@import url("https://fonts.googleapis.com/css?family=Karla:weight@400;700&display=swap"); $font: "Karla", sans-serif; $primary-color: #3e6f9e; body { font-family: $font; color: $primary-color; }
另外,在src/index.html
中添加一些 Dom 元素:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Webpack tutorial</title> </head> <body> <h1>Hello webpack!</h1> <p>Hello sass!</p> </body> </html>
最后,将 sass 文件加载到src/index.js
中:
import "./style.scss"; console.log("Hello webpack!");
在测试以前,咱们须要安装几个 loader:
@import
语法像import
和require
同样去处理css里面引入的模块css-loader
解析后的内容挂载到html页面当中安装 loader:
npm i css-loader style-loader sass-loader sass --save-dev
而后在webpack.config.js
中配置它们:
const HtmlWebpackPlugin = require("html-webpack-plugin"); const path = require("path"); module.exports = { module: { rules: [ { test: /\.scss$/, use: ["style-loader", "css-loader", "sass-loader"] } ] }, plugins: [ new HtmlWebpackPlugin({ template: path.resolve(__dirname, "src", "index.html") }) ] };
注意loader
的出现顺序:首先是sass-loader
,而后是css-loader
,最后是style-loader
。
如今,运行npm start
,你应该会在HTML的头部看到加载的样式表:
webpack 自己并不知道如何转换JavaScript代码。 该任务已外包给babel
的第三方 loader,特别是babel-loader。
babel是一个JavaScript编译器和“编译器”。 babel 能够将现代JS(es6, es7...)转换为能够在(几乎)任何浏览器中运行的兼容代码。
一样,要使用它,咱们须要安装一些 Loader:
引入依赖关系
npm i @babel/core babel-loader @babel/preset-env --save-dev
接着,建立一个新文件babel.config.json
配置babel
,内容以下:
{ "presets": [ "@babel/preset-env" ] }
最后在配置一下 webpack :
const HtmlWebpackPlugin = require("html-webpack-plugin"); const path = require("path"); module.exports = { module: { rules: [ { test: /\.scss$/, use: ["style-loader", "css-loader", "sass-loader"] }, { test: /\.js$/, exclude: /node_modules/, use: ["babel-loader"] } ] }, plugins: [ new HtmlWebpackPlugin({ template: path.resolve(__dirname, "src", "index.html") }) ] };
要测试转换,能够在 src/index.js
中编写一些现代语法:
import "./style.scss"; console.log("Hello webpack!"); const fancyFunc = () => { return [1, 2]; }; const [a, b] = fancyFunc();
如今运行npm run dev
来查看dist
中转换后的代码。 打开 dist/main.js
并搜索“fancyFunc
”:
\n\nvar fancyFunc = function fancyFunc() {\n return [1, 2];\n};\n\nvar _fancyFunc = fancyFunc(),\n _fancyFunc2 = _slicedToArray(_fancyFunc, 2),\n a = _fancyFunc2[0],\n b = _fancyFunc2[1];\n\n//# sourceURL=webpack:///./src/index.js?"
没有babel,代码将不会被转译:
\n\nconsole.log(\"Hello webpack!\");\n\nconst fancyFunc = () => {\n return [1, 2];\n};\n\nconst [a, b] = fancyFunc();\n\n\n//# sourceURL=webpack:///./src/index.js?");
注意:即便没有babel,webpack也能够正常工做。 仅在执行 ES5 代码时才须要进行代码转换过程。
webpack 将整个文件视为模块。 可是,请不要忘记它的主要目的:加载ES模块。
ECMAScript模块(简称ES模块)是一种JavaScript代码重用的机制,于2015年推出,一经推出就受到前端开发者的喜好。在2015之年,JavaScript 尚未一个代码重用的标准机制。多年来,人们对这方面的规范进行了不少尝试,致使如今有多种模块化的方式。
你可能据说过AMD模块,UMD,或CommonJS,这些没有孰优孰劣。最后,在ECMAScript 2015中,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"; import "./style.scss"; console.log("Hello webpack!"); getUsers().then(json => console.log(json));
如前所述,webpack有两种操做模式:开发(development )和(production)。 到目前为止,咱们仅在开发模式下工做。
在开发模式中,为了便于代码调试方便咱们快速定位错误,不会压缩混淆源代码。相反,在生产模式下,webpac k进行了许多优化:
TerserWebpackPlugin
进行缩小以减少 bundle 的大小ModuleConcatenationPlugin
提高做用域在生产模式下配 置webpack,请打开 package.json
并添加一个“ build” 命令:
如今运行 npm run build
,webpack 会生成一个压缩的包。
代码拆分(Code splitting)是指针对如下方面的优化技术:
webpack 社区考虑到应用程序的初始 bundle 的最大大小有一个限制:200KB
。
在 webpack 中有三种激活 code splitting 的主要方法:
optimization.splitChunks
选项第一种基于多个入口点的技术适用于较小的项目,可是从长远来看它是不可扩展的。这里咱们只关注第二和第三种方式。
考虑一个使用Moment.js 的 JS 应用程序,Moment.js
是流行的时间和日期JS库。
在项目文件夹中安装该库:
npm i moment
如今清除src/index.js
的内容,并引入 moment 库:
import moment from "moment";
运行 npm run build
并查看控制的输出内容:
main.js 350 KiB 0 [emitted] [big] main
整个 moment
库都绑定到了 main.js
中这样是很差的。借助optimization.splitChunks
,咱们能够从主包中移出moment.js
。
要使用它,须要在 webpack.config.js
添加 optimization
选项:
const HtmlWebpackPlugin = require("html-webpack-plugin"); const path = require("path"); module.exports = { module: { // ... }, optimization: { splitChunks: { chunks: "all" } }, // ... };
运行npm run build
并查看运行结果:
main.js 5.05 KiB 0 [emitted] main vendors~main.js 346 KiB 1 [emitted] [big] vendors~main
如今,咱们有了一个带有moment.js 的vendors〜main.js
,而主入口点的大小更合理。
注意:即便进行代码拆分,moment.js
仍然是一个体积较大的库。 有更好的选择,如使用luxon
或date-fns
。
Code splitting的一种更强大的技术使用动态导入来有条件地加载代码。 在ECMAScript 2020中提供此功能以前,webpack 提供了动态导入。
这种方法在 Vue 和 React 之类的现代前端库中获得了普遍使用(React有其本身的方式,可是概念是相同的)。
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">Load!</button> </body> </html>
在 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
中
const btn = document.getElementById("btn"); btn.addEventListener("click", () => { // });
若是运行npm run start
查看并单击界面中的按钮,什么也不会发生。
如今想象一下,咱们想在某人单击按钮后加载用户列表。 “原生”的方法可使用静态导入从src/common /usersAPI.js
加载函数:
import { getUsers } from "./common/usersAPI"; const btn = document.getElementById("btn"); btn.addEventListener("click", () => { getUsers().then(json => console.log(json)); });
问题在于ES模块是静态的,这意味着咱们没法在运行时更改导入的内容。
经过动态导入,咱们能够选择什么时候加载代码
const getUserModule = () => import("./common/usersAPI"); const btn = document.getElementById("btn"); btn.addEventListener("click", () => { getUserModule().then(({ getUsers }) => { getUsers().then(json => console.log(json)); }); });
这里咱们建立一个函数来动态加载模块
const getUserModule = () => import("./common/usersAPI");
如今,当你第一次使用npm run start
加载页面时,会看到控制台中已加载 js 包:
如今,仅在单击按钮时才加载/common/usersAPI
:
对应的 chunk 是 0.js
经过在导入路径前面加上魔法注释/ * webpackChunkName:“ name_here” * /
,能够更改块名称:
const getUserModule = () => import(/* webpackChunkName: "usersAPI" */ "./common/usersAPI"); const btn = document.getElementById("btn"); btn.addEventListener("click", () => { getUserModule().then(({ getUsers }) => { getUsers().then(json => console.log(json)); }); });
人才们的 【三连】 就是小智不断分享的最大动力,若是本篇博客有任何错误和建议,欢迎人才们留言,最后,谢谢你们的观看。
代码部署后可能存在的BUG无法实时知道,过后为了解决这些BUG,花了大量的时间进行log 调试,这边顺便给你们推荐一个好用的BUG监控工具 Fundebug。
原文:https://www.sitepoint.com/web...
文章每周持续更新,能够微信搜索 【大迁世界 】 第一时间阅读,回复 【福利】 有多份前端视频等着你,本文 GitHub https://github.com/qq449245884/xiaozhi 已经收录,欢迎Star。