为什么要学习 Webpack
- 了解前端“工程化”概念、工具、指标
- 进步集体外围竞争力
- 成为高阶前端工程师的必经之路
什么是 Webpack
-
前端我的项目由什么形成?—— 资源
-
前端工程化工具呈现之前靠手动治理资源文件,比方通过
<link >
标签引入款式文件,通过<script> </script>
标签引入 JS 文件等,但这种做法会呈现很多问题:- 操作过程繁琐
- 存在依赖关系时要严格依照程序书写
- 开发与生产环境统一导致难以接入 TS 或者 JS 的新个性
- 比拟难接入 Less、Sass 等工具
- JS、CSS、图片资源管理模型不统一等
- 为了解决这些问题呈现了许多前端工程化工具:Webpack、Vite、browserifyjs 等,某种程度上正是这些工具的呈现,才有了前端工程这一概念
-
-
Webpack 实质上是一种前端资源编译、打包工具
- 多份资源文件打包成一个 Bundle
- 反对 Babel、Eslint、TS、CoffeScript、Less、Sass
- 反对模块化解决 css、图片等资源
- 反对 HMR + 开发服务器
- 反对继续监听、继续构建
- 反对代码拆散
- 反对 Tree-shaking
- 反对 Sourcemap 等
Webpack 的用法
- 装置 Webpack:
npm i -D webpack webpack-cli
-
编辑配置文件 webpack.config.js
// webpack.config.js module.exports = { entry: './src/index.js', output: {filename: "[name].js", path: path.join(__dirname,"./dist"), }, module: { rules: [{ test: /\.less$/i, use: ['style-loader','css-loader','less-loader'] }], } }
- 执行编译命令:
npx webpack
会在根目录的 dist 文件夹下失去编译出的打包文件 main.js(默认为 main.js,能够批改) - 外围流程(极度简化版):入口解决、依赖解析、资源解析、资源合并打包
-
Webpack 实质上实现的事件:模块化 + 一致性
- 多个文件资源合并成一个,缩小 HTTP 申请数
- 反对模块化开发
- 反对 typescript、CoffeeScript 语言
- 对立图片、CSS、字体等其余资源的解决模型
应用 Webpack
-
对于 Webpack 的应用办法,根本都围绕“配置”开展,可划分为两大类:
- 流程类:作用于流程中的某个或若干个环节,间接影响打包成果的配置项
- 工具类:主流程之外,提供更多工程化能力的配置项
-
Webpack 中的属性按应用频率来看:
- entry / output
- module / plugins
- mode
- watch / devServer / devtool
- 流程类:作用于流程中的某个或若干个环节,间接影响打包成果的配置项
解决 CSS:例如在 JS 中引入 CSS 文件 import './index.css'
- 装置 Webpack:
npm i -D webpack webpack-cli
- 装置依赖:须要应用的 loader:
npm i -D style-loader css-loader
-
编辑配置文件 webpack.config.js 增加
module
解决 CSS 文件const path = require('path'); module.exports = { entry: './src/index.js', output: {filename: "[name].js", path: path.join(__dirname, "./dist"), }, module: { rules: [{ // test: /\.less$/i, test: /\.css$/, use: ['style-loader', 'css-loader'] }], }, mode: 'development', }
- 终端应用
npx webpack
进行编译
应用 Webpack 解决 JavaScript:接入 Babel 将高版本 JS 代码转译成低版本 JS 代码
- 装置依赖:
npm i -D @babel/core @babel/preset-env babel-loader
-
编辑配置文件 webpack.config.js
const path = require('path'); module.exports = { entry: './src/index.js', output: {filename: "[name].js", path: path.join(__dirname, "./dist"), }, module: { rules: [ { test: /\.css$/, use: ['style-loader', 'css-loader'] }, { test: /\.js$/, use: [{ loader: 'babel-loader', options: { presets: [['@babel/preset-env'] ], }, }], }, ], }, mode: 'development', }
- 终端应用
npx webpack
进行编译
应用 Webpack 解决 HTML:不应用 loader 而是应用 HTML 插件
- 装置依赖
npm i -D html-webpack-plugin
-
编辑配置文件 webpack.config.js
const path = require('path'); const HtmlWebpackPlugin = require('html-webpack-plugin'); module.exports = { entry: './src/index.js', output: {filename: "[name].js", path: path.join(__dirname, "./dist"), }, module: { rules: [ { // test: /\.less$/i, test: /\.css$/, use: ['style-loader', 'css-loader'] }, { test: /\.js$/, use: [{ loader: 'babel-loader', options: { presets: [['@babel/preset-env'] ], }, }], }, ], }, plugins: [new HtmlWebpackPlugin], mode: 'development', }
- 终端应用
npx webpack
进行编译
应用 Webpack 进行热模块更新(HMR,Hot Module Replacement)
- 写的代码可能立即更新到浏览器中,并且不必刷新浏览器
-
编辑配置文件 webpack.config.js 增加
devServer
属性module.exports = { //... devServer: {hot: true,}, }
- 启动 Webpack 应用
npx webpack serve
命令
应用 Webpack 进行 Tree-Shaking,用于删除 dead code
- dead code:代码没有被用到或者不可达到、代码的执行后果不会被用到、代码只读不写 …
-
开启 Tree-Shaking:
mode: "production"
-
optimization: {usedExports: true}
module.exports = { //... mode: "production", optimization: {usedExports: true,}, }
- 终端应用
npx webpack
进行编译
Loader 组件
- Loader 的作用是进行资源内容的转化,Webpack 只意识 JS,Loader 用于解决非标准 JS 资源,翻译为规范 JS
-
应用 Loader(以解决 less 文件为例):
- 装置 Loader:
npm i -D css-loader style-loader less-loader
-
增加
module
解决 css 文件,其中 less-loader 实现了 less -> css 的转换;css-loader 将 CSS 包装成相似 module.exports = “${css}” 的内容,包装后的内容合乎 JavaScript 语法;style-loader 将 CSS 模块包进 require 语句,并在运行时调用 injectStyle 等函数将内容注入到页面的 style 标签。并且这三个 loader 还是以链式调用形式加载的module.exports = { //... module: { rules: { test: /\.less$/i, use: ["css-loader","style-loader","less-loader"], }, }, }
- 装置 Loader:
-
Loader 的个性:
- 链式执行(前一个 loader 的输入可能是另一个 loader 的输出)
- 反对异步执行
- 分为 normal 和 pitch 两种模式
-
编写 Loader
-
一个没有任何性能的 loader:
module.exports = function(source, sourceMap?, data?){ //source 是 loader 的输出也可能是前一个 loader 的输入 return source; };
-
在 webpack 中调用这个 loader:
module.exports = { //... module: { rules: [ { test: /\.js$/, use: [path.join(__dirname, './loader')] }, ], }, }
-
- 罕用 Loader
Plugin 组件
- Webpack 很多性能是靠插件实现的
- 很多出名工具像 VS Code、Webstorm 等都设计了所谓的插件架构,Webpack 自身很多性能也是基于插件实现的
-
不应用插件的话会有很多毛病:
- 新人须要理解整个流程细节,上手老本高性能迭代老本高,牵一动员全身
- 性能僵化,作为开源我的项目而言不足成长性
- 也就是心智老本高、可维护性低、生命力弱
- 插件架构的精华:对扩大凋谢、对批改关闭
-
插件的应用:
- 例如
npm i -D webpack-dashboard
-
引入和应用插件:
// import the plugin const DashBoardPlugin = require("webpack-dashboard"); // add it to your webpack configuration plugins module.exports = { // ... plugins : [new DashBoardPlugin()], // ... };
- 例如
-
编写一个插件
-
插件围绕‘钩子’开展,钩子的作用在编译的某个环节触发钩子,某种程度上能够了解为事件
class SomePlugin {apply(compiler){compiler.hooks.thisCompilation.tap('SomePlugin', (compilation) => {}) } }
-
钩子的外围信息:
- 机会:编译过程的特定节点,Webpack 会以钩子模式告诉插件此刻正在产生什么事件
- 上下文:通过 tapable 提供的回调机制,以参数形式传递上下文信息
- 交互:在上下文参数对象中附带了很多存在 side effect 的交互接口,插件能够通过这些接口扭转
- 机会:compier.hooks.compilation
- 参数:compilation 等
- 交互:dependencyFactories.set
-
如何学习 Webpack