Open UNDERCOVERj opened 5 years ago
[TOC]
过程:
chunk的名称和Entry的配置有关
内置变量列表:
处理模块的规则
配置模块的读取和解析规则
从入口触发找出所有依赖的模块时,配置如何寻找模块对应的文件
将webpack构建出的文件保存在内存中,在要访问输出的文件时,必须通过http服务访问
DevServer让webpack构建出的js代码里注入一个代理客户端用于控制网页,网页和DevServer间通过WebSocket协议通信。(刷新)
问题:从entry入口递归解析依赖的文件,只有entry本身和依赖的文件才会进入监听列表,所以html改变,不会触发上述机制,需要重新加载
inline:关闭,通过iframe的方式去运行要开发的网页在location后加/webpack-dev-server
构建出两份js代码,一份在浏览器端运行,一份在node环境运行并输出html
注意:
source map:一个信息文件,里面储存着位置信息,转换后的代码的每一个位置,所对应的转换前的位置。
控制是否生成,以及如何生成 source map。方便在浏览器中通过源码调试
//# sourceURL=webpack:///./src/a.js?
//# sourceMappingURL=demo.b2c7bdbf7638b61e9ce0.js.map
resolve: { modules: [path.resolve('../node_modules')], }
配置第三方模块使用哪个入口文件,大多数第三方模块都采用main字段去描述入口文件的位置
{ resolve: { mainFields: ['main'] } }
忽略对部分没采用模块化的文件的递归处理,提高构建性能
用DLLPlugin构建出动态链接库,DLLReferencePlugin告诉webpack使用了哪些动态链接库
原理:Loader对文件操作很耗时,happypack将任务分解到多个进程中并行处理,从而减少构建时间
mode: production
{ watch: true, watchOptions: { ignored: /node_modules/, // 忽略node_modules的监听 aggregateTimeout: 300, // 节流 poll: 1000 // 每秒轮询1000次 } }
原理:定时获取文件的最后编辑时间(fs.stats),当发生变化则存起来,aggregateTimeout时间后重新构建。
从entr开始递归解析依赖的文件,加入监听列表
监听文件发生改变,webpack-dev-server则负责刷新浏览器
刷新原理:
1. 借助浏览器扩展区通过浏览器提供的接口刷新,如:webstorm ide的liveedit功能 2. 向要开发的网页中注入代理客户端代码,通过代理客户端去刷新整个页面 3. 将要开发的网页装进一个iframe钟,通过刷新iframe去看到效果
不刷新整个网页,灵敏实时预览
new DefinePlugin({ 'process.env': { NODE_ENV: 'production' } }) process.env.NODE_ENV === 'production'
{ output: { publicPath: '//js.cdn.com/id/' } }
剔除用不上的死代码
使用splitChunks(webpack4)、commonChunkPlugin
optimization: { splitChunks: { chunks: 'all', cacheGroups: { vendor:{ // 抽离第三插件,本来打算搞成多页想想没必要,单页应该就不用抽了 test: /[\\/]node_modules[\\/]/, chunks: 'initial', name: 'vendor', priority: 10, enforce: true }, commons:{ test: /utils\/|components\//, chunks: 'initial', name: 'commons', priority: 10, enforce: true } } } }
示例
原则:
// webpack.config.js output: { chunkFilename: 'js/[name]/[chunkhash:8].chunk.js' } // app.js import (/* webpackChunkName: "xx" */ './xx.js') .then(xx => ....) // react router按需加载 <Route path='/about' component={getAsyncComponent(() => import(/* webpackChunkName */, './about'))} // getAsyncComponent function getAsyncComponent(load) { return class AsyncComp extends PureComponent { componentDidMount() { load().then(({default: copmonent}) => this.setState({compoennt})) } render() { let {component} = this.state; return component ? createElement(component): null; } } }
流程:
三个阶段:
import loaderUtils from 'loader-utils'; module.exports = function(source) { const options = loaderUtils.getOptions(this) || {}; ... return source;// 将原内容返回,等于没有处理 }
this.callback( err: Error | null, content: string | Buffer // 原内容转换后的内容 sourceMap?: SourceMap abstractSynctaxTree?: AST // 如果本次转换为原内容生成了AST语法书,则将这个AST返回,避免重复生成AST,方便之后使用 ) return;
module.exports = function(source) { var callback = this.async(); someAsyncOperation(source, function(err, result, sourceMaps, ast) [ callback(err, result, sourceMaps, ast); }) }
module.exports = function (source) { source instanceof Buffer === true; return source; } module.exports.raw = true; // 告诉webpack该loader是否需要二进制数据
this.cacheable(false) // 关闭该loader的缓存功能
npm link
npm link loader-name
Loader Api
this.context // 当前处理的文件所在目录,处理/src/main.js,则this.context等于/src this.resource // 当前处理的文件的完整请求路径,/src/main.js?name=1 this.resourcePath // 当前处理的文件的路径,/src/main.js this.resourceQuery // 当前处理的文件的queryString this.target // target配置 this.loadModule // 获取依赖的文件的处理结果 this.resolve // 文件完整路径 this.addDependency // 为当前处理的文件添加依赖文件 this.addContextDependency // 将整个目录加入当前处理的文件依赖 this.clearDependencies // 清除当前处理的文件所有依赖 this.emitFile(name, content[,...]) // 输出一个文件
webpack生命周期会广播许多事件,plugin监听这些事件。
class BasicPlugin { constructor(options) {} apply(compiler) { compiler.plugin('compilation', function(comilation) { }) } } module.exports = BasicPlugin;
new BasicPlugin(options)
compiler
basicPlugin.apply(compiler)
webpack
观察者模式
compiler和compilation都继承自Tapable
emit事件发生时,代表源文件的转换和组装已完成,可读取到最终将输出的资源、代码块、模块及其依赖,也可以修改输出资源的内容
当入口模块及依赖模块发生变化时会触发一次新的compilation
不会监听html,可手动将html添加到依赖列表
compilaer.plugin('after-compile', (compilation) => { compilation.fileDependencies.push(filePath); })
emit watch-run // 依赖文件发生变化 after-compile
监听emit资源
输出资源存放在compilation.assets中,键为文件名称,值为文件对应的内容
[TOC]
核心概念
过程:
Entry
chunk的名称
chunk的名称和Entry的配置有关
OutPut
内置变量列表:
Module
处理模块的规则
rules
配置模块的读取和解析规则
Resolve
从入口触发找出所有依赖的模块时,配置如何寻找模块对应的文件
DevServer
将webpack构建出的文件保存在内存中,在要访问输出的文件时,必须通过http服务访问
DevServer让webpack构建出的js代码里注入一个代理客户端用于控制网页,网页和DevServer间通过WebSocket协议通信。(刷新)
问题:从entry入口递归解析依赖的文件,只有entry本身和依赖的文件才会进入监听列表,所以html改变,不会触发上述机制,需要重新加载
inline:关闭,通过iframe的方式去运行要开发的网页在location后加/webpack-dev-server
同构
前端渲染的弊端:
虚拟DOM的优点
最终目的
构建出两份js代码,一份在浏览器端运行,一份在node环境运行并输出html
注意:
devtool
source map:一个信息文件,里面储存着位置信息,转换后的代码的每一个位置,所对应的转换前的位置。
控制是否生成,以及如何生成 source map。方便在浏览器中通过源码调试
//# sourceURL=webpack:///./src/a.js?
//# sourceMappingURL=demo.b2c7bdbf7638b61e9ce0.js.map
优化
缩小文件的搜索范围
配置第三方模块使用哪个入口文件,大多数第三方模块都采用main字段去描述入口文件的位置
忽略对部分没采用模块化的文件的递归处理,提高构建性能
使用DLLPlugin和DLLReferencePlugin
用DLLPlugin构建出动态链接库,DLLReferencePlugin告诉webpack使用了哪些动态链接库
使用happyPack
原理:Loader对文件操作很耗时,happypack将任务分解到多个进程中并行处理,从而减少构建时间
代码压缩
mode: production
自动刷新
原理:定时获取文件的最后编辑时间(fs.stats),当发生变化则存起来,aggregateTimeout时间后重新构建。
从entr开始递归解析依赖的文件,加入监听列表
监听文件发生改变,webpack-dev-server则负责刷新浏览器
刷新原理:
开启模块热替换
不刷新整个网页,灵敏实时预览
区分环境
接入cdn
Tree Shaking
剔除用不上的死代码
提取公共代码
使用splitChunks(webpack4)、commonChunkPlugin
示例
分隔代码以按需加载
原则:
webpack按需加载
原理
流程:
三个阶段:
Loader编写
npm link
npm link loader-name
,将2中的全局npm模块链接到node_modules下Loader Api
Plugin编写
webpack生命周期会广播许多事件,plugin监听这些事件。
new BasicPlugin(options)
,得到实例compiler
后,调用basicPlugin.apply(compiler)
compiler
去操作webpack
compiler(代表从启动到关闭的生命周期)
compilation(一次新的编译)
Tapable
观察者模式
compiler和compilation都继承自Tapable
API
emit事件发生时,代表源文件的转换和组装已完成,可读取到最终将输出的资源、代码块、模块及其依赖,也可以修改输出资源的内容
当入口模块及依赖模块发生变化时会触发一次新的compilation
不会监听html,可手动将html添加到依赖列表
监听emit资源
输出资源存放在compilation.assets中,键为文件名称,值为文件对应的内容