webpack 是一个现代 JavaScript 应用程序的静态模块打包器(module bundler)。当 webpack 处理应用程序时,它会递归地构建一个依赖关系图(dependency graph),其中包含应用程序需要的每个模块,然后将所有这些模块打包成一个或多个 bundle。
webpack 开发模式开启 watch 选项,每次检测到入口文件模块变化时,会创建一次新的编译: 生成一次新的编译资源和新的 compilation 对象,这个 compilation 对象包含了当前编译的模块资源 module,编译生成的资源,变化的文件,依赖的的状态。
根据文件间的依赖关系对其进行静态分析,然后将这些模块按指定规则生成静态资源。当 webpack 处理应用程序时,它会递归地构建一个依赖关系图,其中包含应用程序需要的每个模块,然后将所有这些模块打包成一个或多个 bundle。webpack 有两种组织模块的依赖方式,同步、异步。异步依赖将作为分割点,形成一个新的块;在优化了依赖树之后,每一个异步区块都将作为一个文件被打包。
模块热替换功能会在应用程序应用过程中进行替换、添加或者删除模块,无需加载整个页面。主要通过以下几种方式:
webpack 的热更新,只是提供一套接口和基础的模块替换的实现。开发者需要在代码中通过热更新的接口(module.hot.xxx)向 webpack 声明依赖模块和当前模块是否能够更新,以及更新的前后进行的处理。如果接受更新,那么需要开发者自己来在模块替换前清理或者保留必要的数据、状态,并在模块被替换后恢复之前的数据、状态。在 vue-cli 等脚手架中,热更新的开发者这边的工作是由 vue-loader、css-loader 等加载器已经完成了,再配合 webpack 的 module.hot 等 API 来完成了热更新。
大致热更新流程:webpack 进行文件变化监听,服务端和客户端使用 websocket 进行通信,服务端传递模块变化信息给客户端,客户端根据文件变化消息获取变更模块代码,进行模块的替换。
当编译器开始运行、解析和映射应用程序时,manifest 数据集合会保留所有模块的详细要点。当完成打包并发送到浏览器时,会在运行通过 manifest 来解析和加载模块。无论选择哪种模块语法,import 或者 require 都已转换为 __webpack_require__ 方法,此方法指向模块标识符。通过使用 manifest 中的数据,runtime 能够查询模块标识符,检索出背后对应的模块。
css-loader 用来识别 css 文件,转为 js 对象。
style-loader 将 css-loader 编译之后的 js 对象转为 css 并插入 DOM 中。
移除 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" ] }
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‘ }) ] }
// 传统引入方式 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>
// no { test: /\.js$/, loader: "babel-loader" } // yes { test: /\.js$/, include: path.resolve(__dirname, "src"), loader: "babel-loader" }
DllPlugin
将更改不频繁的代码进行单独编译。这将改善引用程序的编译速度,即使它增加了构建过程的复杂性。thread-loader
可以将非常消耗资源的 loaders 转存到 worker pool 中。cache-loader
启用持久化缓存。使用 package.json
中的 "postinstall"
清除缓存目录。UglifyJsPlugin ExtractTextPlugin [hash]/[chunkhash] AggressiveSplittingPlugin AggressiveMergingPlugin ModuleConcatenationPlugin
未完待续......
原文:https://www.cnblogs.com/kdcg/p/13280767.html