jsliang 求职系列 - 31 - Webpack

一 目录

不折腾的前端,和咸鱼有什么区别css

目录
一 目录
二 前言
三 Webpack 是什么
四 Webpack 核心概念
五 Webpack 构建流程
六 entry
七 output
八 loader
8.1 关于文件处理常见的 loader
8.2 关于语法检查常见 loader
8.3 关于 HTML 代码处理常见的 loader
8.4 关于 CSS 代码处理常见的 loader
8.5 关于 JS 代码处理常见的 loader
九 plugin
9.1 常见 plugin
9.2 提升效率的 plugin
十 loader 和 plugin 的区别
十一 resolve
十二 从 0 开始配置 Webpack
12.1 技术选型
12.2 Loader 配置 - 处理 CSS、Less
12.3 Loader 配置 - 处理图片
12.4 Loader 配置 - 处理字体
12.5 Loader 配置 - MPA 多页面打包通用方案
12.6 SourceMap
12.7 WebpackDevServer
12.8 babel 解析
12.9 React
12.10 性能优化
12.11 其余
十三 知识补充:懒加载
13.1 代码分割
13.2 实现原理
13.3 Vue 按需加载
十四 知识补充:热更新
14.1 开启热更新
14.2 热更新原理
十五 知识补充:3 种 hash
十六 知识补充:source map
十七 知识补充:Webpack 打包原理
十八 参考文献
18.1 Webpack 系列文章
18.2 Webpack 性能优化
18.3 Scope Hoisting
18.4 Tree Shaking
18.5 懒加载

二 前言

返回目录

都 2020 了,不会点 Webpack 好像有点说不过去。html

可是事实上若是不是分配到【架构组】之类的团体中,感受接触 Webpack 的几率会少点吧。前端

就比如 jsliang 在上家公司,就没机会接触 Webpack,都是用这别人已经配置好的方案,纯粹作一个业务仔~vue

三 Webpack 是什么

返回目录

Webpack 是一个现代 JavaScript 应用程序的静态模块打包器(module bundler)。node

当 Webpack 处理应用程序时,它会递归地构建一个依赖关系图(dependency graph),其中包含应用程序须要的每一个模块,而后将全部这些模块打包成一个或多个 bundlereact

因此,它的本质是一个模块打包器,其工做是将每一个模块打包成相应的 bundlewebpack

四 Webpack 核心概念

返回目录
  • mode:模式。对应有开发模式、生产模式等
  • entry:入口
  • output:出口
  • loader:模块转换器,用于把模块原内容按照需求转换成新内容。Webpack 对于 .jpg.txt 等内容没法处理,就须要 file-loaderurl-loader 等进行协助处理。
  • plugins:扩展插件,在 Webpack 构建流程中的特定时机注入拓展逻辑来改变构建结果或者作其余你想作的事情。

五 Webpack 构建流程

返回目录

Webpack 就像一条生产线,要通过一系列处理流程后才能将源文件转换成输出结果。nginx

这条生产线上的每一个处理流程的职责都是单一的,多个流程之间有存在依赖关系,只有完成当前处理后才能交给下一个流程去处理。 git

Webpack 的运行流程是一个串行的过程,从启动到结束会依次执行如下流程:github

  • 初始化参数:从配置文件和 Shell 语句中读取与合并参数,得出最终的参数
  • 开始编译:用上一步获得的参数初始化 Compiler 对象,加载全部配置的插件,执行对象的 run 方法开始执行编译
  • 肯定入口:根据配置中的 entry 找出全部的入口文件
  • 编译模块:从入口文件出发,调用全部配置的 Loader 对模块进行翻译,再找出该模块依赖的模块,再递归本步骤直到全部入口依赖的文件都通过了本步骤的处理
  • 完成模块编译:在通过第 4 步使用 Loader 翻译完全部模块后,获得了每一个模块被翻译后的最终内容以及它们之间的依赖关系
  • 输出资源:根据入口和模块之间的依赖关系,组装成一个个包含多个模块的 Chunk,再把每一个 Chunk 转换成一个单独的文件加入到输出列表,这步是能够修改输出内容的最后机会
  • 输出完成:在肯定好输出内容后,根据配置肯定输出的路径和文件名,把文件内容写入到文件系统

简单来讲:

  • 初始化:启动构建,读取与合并配置参数,加载 Plugin,实例化 Compiler(钩子)
  • 编译:从 Entry 出发,针对每一个 Module(模块)串行调用对应的 Loader 去翻译文件的内容,再找到该 Module 依赖的 Module,递归地进行编译处理
  • 输出:将编译后的 Module 组合成 Chunk,将 Chunk 转换成文件,输出到文件系统中(Chunk 就是打包过程当中,入口模块引用其余模块,模块再引用模块,这个关系链链接的 Module 就造成了 Chunk

在这个过程当中,Webpack 会在特定的时间点广播出特定的事件,插件在监听到感兴趣的事件后会执行特定的逻辑,而且插件能够调用 Webpack 提供的 API 改变 Webpack 的运行结果。

六 entry

返回目录

指定打包⼊口文件,有三种不一样的形式:string | object | array

一对一:一个入口、一个打包文件

module.exports = {
  entry: './src/index.js'
}

多对一:多个入口、一个打包文件

module.exports = {
  entry: [
    './src/index1.js',
    './src/index2.js',
  ]
}

多对多:多个入口、多打包文件

module.exports = {
  entry: {
    'index1': "./src/index1.js",
    'index2': "./src/index2.js"
  }
}

七 output

返回目录

打包后的文件位置。

module.exports = {
  ...,
  output: {
    path: path.resolve(__dirname, "dist"),
    filename: "bundle.js",
    filename: "[name].js"
  }
}
  • 能够指定一个固定的文件名称,若是是多入口多出口(entry 为对象),则不能使用单文件出口,须要使用下面的方式
  • 经过 Webpack 内置的变量占位符:[name]

八 loader

返回目录

loader 的执行顺序是从右向左执行的,也就是后面的 loader 先执行。

假若有配置:

// webpack.config.js
module.exports = {
  //...
  module: {
    rules: [
      {
        test: /\.(le|c)ss$/,
        use: ['style-loader', 'css-loader', 'less-loader'],
        exclude: /node_modules/,
      },
    ],
  },
};

那就是先处理 less-loader,再处理 css-loader,最后处理 style-loader

8.1 关于文件处理常见的 loader

返回目录
  • file-loader:当引入的文件是 .png.txt 等时,能够经过 file-loader 解析项目中的 url 引入。根据配置将文件拷贝到相应的路径,并修改打包后文件的引入路径,让它指向正确的文件。
  • url-loaderurl-loader 封装了 file-loader 且能够不依赖于 file-loader 单独使用,而且能够配置 limit。对小于 limit 大小的图片转换成 Base64,大于 limit 的时候使用 file-loader 里的方法。

8.2 关于语法检查常见 loader

返回目录
  • tslint-loader:经过 TSLint 检查 TypeScript 代码
  • eslint-loader:经过 ESLint 检查 JavaScript 代码

8.3 关于 HTML 代码处理常见的 loader

返回目录
  • html-withimg-loader:处理 HTML 中的图片

8.4 关于 CSS 代码处理常见的 loader

返回目录
  • style-loader:动态建立 style 标签,将 CSS 代码插入到 head 中。
  • css-loader:负责处理 @importurl 等语句。例如 import css from 'file.css'url(image.png)
  • postcss-loader:负责进一步处理 CSS 文件,好比添加浏览器前缀,压缩 CSS 等。
  • less-loader:将 .less 文件内容转换成 CSS。
  • sass-loader:将 .sass 文件内容转换成 CSS。

8.5 关于 JS 代码处理常见的 loader

返回目录
  • babel-loader:将 JS 代码向低版本转换,咱们须要使用 babel-loader
  • ts-loader:将 TypeScript 转换成 JavaScript

九 plugin

返回目录

9.1 常见 plugin

返回目录
  • clean-webpack-plugin:打包前自动清理 dist 目录,防止文件残留。
  • copy-webpack-plugin:将单个文件或者整个目录复制到构建目录
  • mini-css-extract-plugin:将 CSS 抽离出来单独打包而且经过配置能够设置是否压缩。
  • html-webpack-plugin:这个插件能够配置生成一个 HTML5 文件,其中 script 标签包含全部 Webpack 包。若是你设置多个入口点,你能够据此实现多页面应用打包。

9.2 提升效率的 plugin

返回目录
  • webpack-dashboard:能够更友好的展现相关打包信息。
  • webpack-merge:提取公共配置,减小重复配置代码
  • speed-measure-webpack-plugin:简称 SMP,分析出 Webpack 打包过程当中 Loader 和 Plugin 的耗时,有助于找到构建过程当中的性能瓶颈。
  • size-plugin:监控资源体积变化,尽早发现问题
  • HotModuleReplacementPlugin:模块热替换

十 loader 和 plugin 的区别

返回目录
  • Loader

Loader 本质上就是一个函数,对接收到的内容进行转换,返回转换后的结果。

由于 Webpack 只认识 JavaScript,因此 Loader 就成了翻译官,对不一样类型的资源进行处理。

就比如 file-loader 或者 url-loader,配置以后就能够正确引用 png 等格式的图片、txt 等格式文件。

又比如 style-loader 以及 css-loader,引用后就能够对 CSS 内容进行预编译处理。

  • Plugin

Plugin 就是插件,就比如 jsliang 编写的 VS Code 插件同样,Plugin 拓展了 Webpack 的功能。

Plugin 就是在 Webpack 的生命周期中进行各类操做,从而达到使用者目的插件。

就比如 html-webpack-plugin,配合多入口形式使用以后,就能够实现多页面应用的功能。

又比如 clean-webpack-plugin 实现打包以前清空 dist 目录,copy-webpack-plugin 能够将单个文件或者整个目录复制到构建目录。

十一 resolve

返回目录

resolve 配置 Webpack 如何寻找模块所对应的文件。

Webpack 内置 JavaScript 模块化语法解析功能,默认会采用模块化标准里约定好的规则去寻找,但你能够根据本身的须要修改默认的规则。

// webpack.config.js
module.exports = {
  //....
  resolve: {
    modules: ['./src/components', 'node_modules'] // 从左到右依次查找
  }
}
  • resolve.modules:配置 Webpack 去哪些目录下寻找第三方模块,默认状况下,只会去 node_modules 下寻找,若是你在项目中某个文件夹下的模块常常被导入,不但愿写很长的路径,那么就能够经过配置 resolve.modules 来简化。
  • resolve.alias:配置项经过别名把原导入路径映射成一个新的导入路径。
  • resolve.extensions:适配多端的项目中,可能会出现 .web.js, .wx.js,例如在转 Web 的项目中,咱们但愿首先找 .web.js,若是没有,再找 .jsextensions: ['web.js', '.js']

十二 从 0 开始配置 Webpack

返回目录

如何从 0 开始配置一个属于本身的 Webpack 脚手架呢?那就涉及到选型问题。

12.1 技术选型

返回目录
  • 移动端 || PC
  • MPA || SPA
  • HTML || 模板引擎
  • CSS || 预处理器
  • JavaScript ES5 || ES6
  • 本地发布服务(数据模拟)
  • React || Vue
  • 多人项目 || 单人项目
  • 语法规范 Eslint
  • 单元测试
  • 提交规范

12.2 Loader 配置 - 处理 CSS、Less

返回目录
  • use: ['style-loader', 'css-loader', 'postcss-loader', 'less-loader']
  • less less-loader:解析 .less 文件
  • postcss-loader autoprefixer:对 flex 布局等进行前缀补充

12.3 Loader 配置 - 处理图片

返回目录
  • file-loader:解析 .txt.png.md 等格式文件
  • url-loaderlimit: 1024,判断大小是否处理成 base64 格式

12.4 Loader 配置 - 处理字体

返回目录
  • url-loader

12.5 Loader 配置 - MPA 多页面打包通用方案

返回目录
  • 安装 glob
  • entryhtmlwebpackplugin 动态生成

12.6 SourceMap

返回目录
  • 开发环境配置:devtool: "cheap-module-eval-source-map
  • 线上生成配置(不推荐):devtool: "cheap-module-source-map"

12.7 WebpackDevServer

返回目录
  • 安装
  • 配置:devServer
  • HMR(热模块替换,Hot Module Replacement)
  • 开启 JS 模块的 HMR,须要 Webpack 配合

12.8 babel 解析

返回目录
  • 安装:npm i babel-loader @babel/core @babel-preset-env -D

    • @babel/corebabel 核心
    • babel-loaderbabelWebpack 的链接桥梁
    • babel-preset-env:输出什么样的代码,用它来解决
  • babel-loader:解析 ES6+
  • @babel/polyfill:垫片。包含全部 ES6+ 新特性代码
  • .babelrc

12.9 React

返回目录
  • 安装:react react-dom
  • 使用:@babel/preset-react

12.10 性能优化

返回目录
  • 缩小 loader 的文件范围:loaderinclude 配置,能够指定 src 目录,减小检查范围。
  • 优化 resolve.modules 配置:配置 Webpack 去哪些目录下寻找第三方模块,默认 node_modules
  • 分离 CSS:MiniCssExtractPlugin
  • hashchunkhashcontenthash 区别

    • hash 做用域 JS、CSS,图片的 hash 有区别,每次打包构建都会变化一次。
    • chunkhashchunk 为单位,修改了那部分就改动哪部分的 hash。(同时依赖的模块也会改变 hash
    • contenthash 只有本身内容发生改变,才发生改变(区别于 chunkhash
    • 因此 JS 适用于 chunkhash;CSS 适用于 contenthash;Image 适用于 hash
  • 压缩 CSS:optimize-css-assets-webpack-plugincssnano
  • 压缩 HTML:html-webpack-plugin
  • 压缩图片:img-webpack-loader
  • 分离 Webpack 配置:分离 base.configdev.configmpa.configpro.config 4 个,经过 merge 进行 config 配置的合并

更多看这里:

Webpack 性能优化

12.11 其余

返回目录
  • 如何简单编写一个 Webpack 解析器
  • 如何编写一个 Webpack loader
  • 如何编写一个 Webpack plugin

十三 知识补充:懒加载

返回目录

懒加载或者按需加载,是一种很好的优化网页或应用的方式。

这种方式其实是先把你的代码在一些逻辑断点处分离开,而后在一些代码块中完成某些操做后,当即引用或即将引用另一些新的代码块。

这样加快了应用的初始加载速度,减轻了它的整体体积,由于某些代码块可能永远不会被加载。

13.1 代码分割

返回目录

代码分割(code splitting)是指:将脚本中无需当即调用的代码在代码构建时转变为异步加载的过程。

在 Webpack 构建时,会避免加载已声明要异步加载的代码,异步代码会被单独分离出一个文件,当代码实际调用时被加载至页面。

代码分割技术的核心是 异步加载资源

可喜的是,浏览器容许咱们这么作,W3C stage 3 规范: whatwg/loader 对其进行了定义:你能够经过 import() 关键字让浏览器在程序执行时异步加载相关资源。

在 Vue 中,能够直接使用 import() 关键字作到这一点,而在 React 中,你须要使用 react-loadable 去完成一样的事。

13.2 实现原理

返回目录
  1. 将须要进行懒加载的子模块单独打包成文件(children chunk
  2. 借助函数来实现延迟进行子模块的加载代码(import
print.js
console.log('输出 1');

export default () => {
  console.log('输出 2');
};
index.js
const btn = document.querySelector('.btn');
btn.onclick = import('./print.js').then((module) => {
  const print = module.default;
  print();
});

13.3 Vue 按需加载

返回目录

Vue 的特色就是 SPA - Single Page Application(单页应用程序)。

只有第一次加载页面,之后的每次页面切换,只须要进行组件替换。

它减小了请求次数,加快页面响应速度,下降对服务器压力等等。

可是,由于 Vue 是 SPA,因此首页第一次加载时会把全部组件以及组件相关资源所有加载,从而致使网站首页打开速度变慢,下降用户体验。

Vue 项目中,能够结合 Webpack,在 vue-router 经过 import 进行动态加载:

const routes = [{
  path: '/',
  name: 'Home',
  component: () => import('../views/Home.vue')
}];

十四 知识补充:热更新

返回目录

刷新咱们通常分为两种:

  • 一种是页面刷新,不保留页面状态,就是简单粗暴,直接 window.location.reload()
  • 另外一种是基于 WDSWebpack-dev-server)的模块热替换,只须要局部刷新页面上发生变化的模块,同时能够保留当前的页面状态,好比复选框的选中状态、输入框的输入等。

Webpack 的热更新又称热替换(Hot Module Replacement),缩写为 HMR

这个机制能够作到不用刷新浏览器而将新变动的模块替换掉旧的模块。

14.1 开启热更新

返回目录

在 Webpack 的 webpack.config.js 中:

  1. 配置 devServerhottrue
  2. plugins 中增长 new webpack.HotModuleReplacementPlugin()
// webpack.config.js
const webpack = require('webpack');
module.exports = {
  //....
  devServer: {
    hot: true
  },
  plugins: [
    new webpack.HotModuleReplacementPlugin() // 热更新插件
  ]
}

而且在入口文件配置:

if(module && module.hot) {
  module.hot.accept()
}

此时修改代码的时候,只有对应部分的内容才会相应更新。

14.2 热更新原理

返回目录

HMR 的核心就是客户端从服务端拉去更新后的文件,准确的说是 chunk diffchunk 须要更新的部分)。

实际上 webpack-dev-serverWDS)与浏览器之间维护了一个 Websocket,当本地资源发生变化时,WDS 会向浏览器推送更新,并带上构建时的 hash,让客户端与上一次资源进行对比。

客户端对比出差别后会向 WDS 发起 Ajax 请求来获取更改内容(文件列表、hash),这样客户端就能够再借助这些信息继续向 WDS 发起 jsonp 请求获取该 chunk 的增量更新。

后续的部分(拿到增量更新以后如何处理?哪些状态该保留?哪些又须要更新?)由 HotModulePlugin 来完成,提供了相关 API 以供开发者针对自身场景进行处理,像 react-hot-loadervue-loader 都是借助这些 API 实现 HMR

十五 知识补充:3 种 hash

返回目录

文件指纹是打包后输出的文件名的后缀,对应着 3 种 hash

  • hash 是跟整个项目的构建相关,只要项目里有文件更改,整个项目构建的 hash 值都会更改,而且所有文件都共用相同的 hash 值。(粒度整个项目)
  • chunkhash 是根据不一样的入口进行依赖文件解析,构建对应的 chunk(模块),生成对应的 hash 值。只有被修改的 chunk(模块)在从新构建以后才会生成新的 hash 值,不会影响其它的 chunk。(粒度 entry 的每一个入口文件)
  • contenthash 是跟每一个生成的文件有关,每一个文件都有一个惟一的 hash 值。当要构建的文件内容发生改变时,就会生成新的 hash 值,且该文件的改变并不会影响和它同一个模块下的其它文件。(粒度每一个文件的内容)

十六 知识补充:source map

返回目录

source map 是将编译、打包、压缩后的代码映射回源代码的过程。打包压缩后的代码不具有良好的可读性,想要调试源码就须要 soucre map

map 文件只要不打开开发者工具,浏览器是不会加载的。

线上环境通常有三种处理方案:

  • hidden-source-map:借助第三方错误监控平台 Sentry 使用
  • nosources-source-map:只会显示具体行数以及查看源代码的错误栈。安全性比 source map
  • source map:经过 nginx 设置将 .map 文件只对白名单开放(公司内网)

注意:避免在生产中使用 inline-eval-,由于它们会增长 bundle 体积大小,并下降总体性能。

十七 知识补充:Webpack 打包原理

返回目录

在 Webpack 简单实现中,简单的作了下如何将一份代码进行打包:

  1. 利用 babel 完成代码转换,并生成单个文件的依赖
  2. 生成依赖图谱
  3. 生成最后打包代码

十八 参考文献

返回目录

本系列参考文献有 51 篇文章。

18.1 Webpack 系列文章

返回目录

其余

2020 年文章

2019 年文章

2018 年文章

2017 文章

18.2 Webpack 性能优化

返回目录

2019 年文章

2018 年文章

2017 年文章

18.3 Scope Hoisting

返回目录

18.4 Tree Shaking

返回目录

18.5 懒加载

返回目录

jsliang 的文档库由 梁峻荣 采用 知识共享 署名-非商业性使用-相同方式共享 4.0 国际 许可协议 进行许可。<br/>基于 https://github.com/LiangJunrong/document-library 上的做品创做。<br/>本许可协议受权以外的使用权限能够从 https://creativecommons.org/licenses/by-nc-sa/2.5/cn/ 处得到。
相关文章
相关标签/搜索