webpack 是一个现代 JavaScript 应用程序的静态模块打包器(module bundler)。当 webpack 处理应用程序时,它会递归地构建一个依赖关系图(dependency graph),其中包含应用程序需要的每个模块,然后将所有这些模块打包成一个或多个 bundle

四个概念

  • entry:入口起点,可以配置多页面。
  • output:出口,项目编译完成后之后文件输出路径。
  • loader:webpack 自身只理解 JavaScript,loader 能够去处理非 JavaScript 文件并转化 JavaScript,处理源文件,一次处理一个。
  • plugins:用来扩展 webpack 功能,插件能够执行很多任务。如:打包优化、压缩等。

核心对象

  • Tapable:控制钩子的发布与订阅。
  • Compiler:webpack 的编译器,继承 Tapable 对象,可以广播和监听 webpack 事件,webpack 生命周期值存在一个 Compiler 对象。Compiler 在 webpack 启动时创建实例,compiler 实例中包含 webpack 的完整配置,包括 loaders, plugins 信息。
  • Compilation:编译器编译之后的资源信息对象,继承 Tapable 对象,可以广播和监听 webpack 事件,Compilation 实例仅代表一次 webpack 构建和生成编译资源的的过程。

webpack 开发模式开启 watch 选项,每次检测到入口文件模块变化时,会创建一次新的编译: 生成一次新的编译资源和新的 compilation 对象,这个 compilation 对象包含了当前编译的模块资源 module,编译生成的资源,变化的文件,依赖的的状态。

module、chunk、bundle

  • module:js 模块,就是被 require 或 export 的模块,如:ES 模块、commonjs 模块、 AMD 模块。
  • chunk:代码块,是进行依赖分析的时候,代码分割出来的代码块,包括一个或多个 module,是被分组了的 module 集合,code spliting 之后的就是chunk。
  • bundle:文件,最终打包出来的文件,通常一个 bundle 对应一个 chunk。

打包原理

根据文件间的依赖关系对其进行静态分析,然后将这些模块按指定规则生成静态资源。当 webpack 处理应用程序时,它会递归地构建一个依赖关系图,其中包含应用程序需要的每个模块,然后将所有这些模块打包成一个或多个 bundle。webpack 有两种组织模块的依赖方式,同步、异步。异步依赖将作为分割点,形成一个新的块;在优化了依赖树之后,每一个异步区块都将作为一个文件被打包。

构建流程

  • 生成 options(将 webpack.config.js 和 shell 中的参数合并到 options 对象)。
  • 实例化 compiler 对象 (webpack 全局对象,包含 entry、output、loader、plugins等所有配置对象)。
  • 实例化 compilation 对象(compiler.run 方法执行,开始编译过程,生成 compilation 对象)。
  • 分析入口 js 文件,调用 AST 引擎处理入口文件,生成抽象语法树 AST,根据 AST 构建模块的所有依赖。
  • 通过 loader 处理入口文件的所有依赖,转换为 js 模块,生成 AST,然后继续递归遍历,直至所有依赖被分析完毕。
  • 对生成的所有 module 进行处理,调用 plugins,合并,拆分,生成 chunk。
  • 将 chunk 生成为对应 bundle 文件,输出到目录。

HMR(热更新)

模块热替换功能会在应用程序应用过程中进行替换、添加或者删除模块,无需加载整个页面。主要通过以下几种方式:

  • 保留在完全重新加载页面时丢失的应用程序状态。
  • 只更逊变更内容,以节省宝贵的开发时间。
  • 调整样式更加快速,几乎相当于在浏览器调试器中更改样式。

webpack 的热更新,只是提供一套接口和基础的模块替换的实现。开发者需要在代码中通过热更新的接口(module.hot.xxx)向 webpack 声明依赖模块和当前模块是否能够更新,以及更新的前后进行的处理。如果接受更新,那么需要开发者自己来在模块替换前清理或者保留必要的数据、状态,并在模块被替换后恢复之前的数据、状态。在 vue-cli 等脚手架中,热更新的开发者这边的工作是由 vue-loader、css-loader 等加载器已经完成了,再配合 webpack 的 module.hot 等 API 来完成了热更新。

大致热更新流程:webpack 进行文件变化监听,服务端和客户端使用 websocket 进行通信,服务端传递模块变化信息给客户端,客户端根据文件变化消息获取变更模块代码,进行模块的替换。

  • 运行时开启热更新配置,在打包的 bundle 里面包含 HMR runtime 及 websocket 功能。
  • 服务端与客户端建立 websocket 长连接。
  • webpack 监听文件变化,文件保存时触发 webpack 重新编译,编译后代码保存在内存中。
  • 编译时会生成 hot-update.json (已改动模块的 json)、hot-update.js (已改动模块的 js)。
  • 通过 websocket 向客户端发送 hash 值。
  • 客户端对比 hash 值,一致走缓存,不一致再通过 Ajax 获取 hot-update.json,json 包含模块 hash 值,再通过 jsonp 请求获取 hot-update.js。
  • 热更新 js 模块,若失败,则 live reload 刷新浏览器代替热更新(若模块未配置热更新,则同样 live reload)。

webpack如何管理模块

当编译器开始运行、解析和映射应用程序时,manifest 数据集合会保留所有模块的详细要点。当完成打包并发送到浏览器时,会在运行通过 manifest 来解析和加载模块。无论选择哪种模块语法,import 或者 require 都已转换为 __webpack_require__ 方法,此方法指向模块标识符。通过使用 manifest 中的数据,runtime 能够查询模块标识符,检索出背后对应的模块。

style-loader 与 css-loader

css-loader 用来识别 css 文件,转为 js 对象。

style-loader 将 css-loader 编译之后的 js 对象转为 css 并插入 DOM 中。

tree shaking

移除 JavaScript 上下文中的未引用代码(dead-code)。

// 用法  package.json
{
sideEffects: false
}

这个有一定的副作用:如果一个文件执行一些特殊功能,而不是仅仅暴露一个或多个 export,比如 polyfill,影响全局作用域,通常又不提供 export,那么它将会被移除。

如果所有代码都不含副作用,则可以标记为 false,如果有,那么就提供一个数组:

{
"name": "your-project",
"sideEffects": [
"./src/some-side-effectful-file.js"
]
}

在项目中,任何导入的文件都受 tree shaking 影响,比如导入的 css 文件,我们则需要将它们添加进去:

{
"name": "your-project",
"sideEffects": [
"./src/some-side-effectful-file.js",
"*.css"
]
}

shimming全局变量

webpack 中如果需要用到的不规范的第三方库,有可能会引起全局依赖,如 jQuery 的 $。但是如果我们需要用到一个全局变量,可以使用 ProvidePlugin 后,能够在通过 webpack 编译的每个模块中,通过访问一个变量来获取到 package 包。如果 webpack 知道这个变量在某个模块中被使用了,那么 webpack 将在最终 bundle 中引入我们给定的 package。

const path = require('path');
const webpack = require('webpack'); module.exports = {
entry: './src/index.js',
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, 'dist')
}
},
plugins: [
new webpack.ProvidePlugin({
_: 'lodash'
})
]
}

polyfills 加载优化

// 传统引入方式
npm install --save babel-polyfill // src/main.js
import 'babel-polyfill'; ......

polyfills 虽然是一种模块引入方式,但是并不推荐在主 bundle 中引入 polyfills,因为这不利于具备这些模块功能的现代浏览器用户,会使他们下载体积很大、但却不需要的脚本文件。

让我们把 import 放入一个新文件,并加入 whatwg-fetch polyfill:

npm install --save whatwg-fetch

// 新建 src/polyfills.js
import 'babel-polyfill';
import 'whatwg-fetch'; // webpack.config.js
const path = require('path');
const webpack = require('webpack'); module.exports = {
entry: './src/index.js',
entry: {
polyfills: './src/polyfills.js',
index: './src/index.js'
},
output: {
filename: '[name].bundle.js',
path: path.resolve(__dirname, 'dist')
}
};
// index.html 然后根据条件引入
<!doctype html>
<html>
<head>
<title>Getting Started</title>
<script>
var modernBrowser = (
'fetch' in window &&
'assign' in Object
); if ( !modernBrowser ) {
var scriptElement = document.createElement('script'); scriptElement.async = false;
scriptElement.src = '/polyfills.bundle.js';
document.head.appendChild(scriptElement);
}
</script>
</head>
<body>
<script src="index.bundle.js"></script>
</body>
</html>

webpack 优化

  • 保持 node.js 最新版本:较新的版本能够建立更高效的模块树以及提高解析速度。
  • loaders 应用于最少数的必要模块:
    // no
    {
    test: /\.js$/,
    loader: "babel-loader"
    } // yes
    {
    test: /\.js$/,
    include: path.resolve(__dirname, "src"),
    loader: "babel-loader"
    }
  • 尽量少使用不同的 loader/plugin 工具:每个额外的 loader/plugin 都有启动时间。
  • 使用 Dlls:使用 DllPlugin 将更改不频繁的代码进行单独编译。这将改善引用程序的编译速度,即使它增加了构建过程的复杂性。
  • 尽量保持 chunks 小巧:减少编译的整体大小,以提高构建性能。
  • Worker Poll:thread-loader 可以将非常消耗资源的 loaders 转存到 worker pool 中。
  • 持久化缓存:使用 cache-loader 启用持久化缓存。使用 package.json 中的 "postinstall" 清除缓存目录。
  • 关闭 Source Maps:Source Maps很耗性能。
  • 开发环境中避免用到只有在生产环境中才能用到的工具:
    UglifyJsPlugin
    ExtractTextPlugin
    [hash]/[chunkhash]
    AggressiveSplittingPlugin
    AggressiveMergingPlugin
    ModuleConcatenationPlugin

未完待续......

web前端知识点(webpack篇)的更多相关文章

  1. 25、前端知识点--webpack篇之面试考点

    前端面试之webpack篇 https://blog.csdn.net/sinat_17775997/article/details/78122999 关于webpack的面试题 随着现代前端开发的复 ...

  2. web前端知识点(JavaScript篇)

    call,apply,bind call,apply,bind这三者的区别,及内部实现原理,点这里 promise promise函数的内部实现原理,点这里 闭包 闭包就是能够读取其他函数内部变量的函 ...

  3. web前端知识点

    一.CSS问题 1.flex布局 display:flex; 在父元素设置,子元素受弹性盒影响,默认排成一行,如果超出一行,按比例压缩 flex:1; 子元素设置,设置子元素如何分配父元素的空间,fl ...

  4. 27、前端知识点--webpack面试题(二)

    webpack面试题总结 本文主要是对webpack面试会常被问到的问题做一些总结,且文章会不断持续更新 1.webpack打包原理 把所有依赖打包成一个 bundle.js 文件,通过代码分割成单元 ...

  5. 23、前端知识点--webpack的使用详解

    Webpack 是当下最热门的前端资源模块化管理和打包工具. https://www.cnblogs.com/zhangruiqi/p/7656206.html

  6. Web前端知识点记录

    一.HTML的加载顺序 浏览器边下载HTML,边解析HTML代码,二者是从上往下同步进行的 先解析<head>中的代码,在<head>中遇到了<script>标签, ...

  7. web前端知识点1

    1. input属于窗体元素,层级显示比flash.其它元素都高.请判断这句话的正确与否. 错误 层级显示优先级: frameset > 表单元素 > 非表单元素 在html中,帧元素(f ...

  8. web前端知识点反思总结

    当别人问你之前的知识,我们便会勾起之前的回忆,然后进行一番痛苦的挣扎后,发现我依然记得你 什么是 DTD ? 文档类型定义 (DTD) 可定义合法的 xml 文档的构建模块 ,他是使用一系列合法的元素 ...

  9. Web 前端知识点

随机推荐

  1. 手写网页扫雷之js部分(vue)

    var vm = new Vue({ el:"#ui", data(){ return{ num:0, saoleiStyle:{ width: "0px", ...

  2. SpringMVC的url-pattern配置及原理剖析

    SpringMVC的url-pattern配置及原理剖析 xml里面配置标签: <!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc./ ...

  3. [问题解决]Windows下python中pydoc命令提示“'pydoc' 不是内部或外部命令,也不是可运行的程序 或批处理文件。”

    解决方法:python -m pydoc 例:python -m pydoc print

  4. cb08a_c++_顺序容器的操作1

    cb08a_c++_顺序容器的操作1容器定义的类型别名begin(闭区间)和end(开区间)成员{first,last)左闭右开,左包括,右不包括,end()指向最后一个的下一个. /*cb08a_c ...

  5. opencv视频教程分享

    opencv视频教程分享-在线与网盘 https://pan.baidu.com/s/1oAcctlS 密码:i5rd 链接:https://pan.baidu.com/s/1kVJ3iSJ  密码: ...

  6. 漏洞复现-Office远程代码执行漏洞 (CVE-2017-11882&CVE-2018-0802)

    漏洞原理 这两个漏洞本质都是由Office默认安装的公式编辑器(EQNEDT32.EXE)引发的栈溢出漏洞(不要问什么是栈溢出,咱也解释不了/(ㄒoㄒ)/~~) 影响版本 Office 365 Mic ...

  7. SSL/TSL 原理( 握手原理和传输原理)

    本文参考<计算机网络 自顶向下方法> 目录 背景 通信的4要素 SSL/TLS in Detail 通讯保证 The Handshake Protocol 1. Initial Clien ...

  8. elasticserach数据库深度分页查询的原理

    深度分页存在的问题 https://segmentfault.com/a/1190000019004316?utm_source=tag-newest 在实际应用中,分页是必不可少的,例如,前端页面展 ...

  9. 用Map+函数式接口来实现策略模式

    用Map+函数式接口来实现策略模式 目前在魔都,贝壳找房是我的雇主,平时关注一些 java 领域相关的技术,希望你们能在这篇文章中找到些有用的东西.个人水平有限,如果文章有错误还请指出,在留言区一起交 ...

  10. Github仓库如何选择开源许可证

    Github仓库如何选择开源许可证 目录 Github仓库如何选择开源许可证 为什么需要开源许可证? 不使用开源许可证对于开发者有何影响? 不使用开源许可证对于项目的使用者有何影响? Github的开 ...