1.process.env.NODE_ENV
为什么我们在写前端代码的时候,可以使用 process.env.NODE_ENV 这样的代码(process 不是 node 模块么?为啥可以在浏览器下使用)?
因为webpack有DefinePlugin的存在,在初始模板的时候,注入了全局process对象,所以浏览器可以用process对象。
2.Buffer、setImmediate
为什么我们在写前端代码的时候,可以使用诸如 Buffer、setImmediate 等 node 才有的全局对象(https://nodejs.org/api/globals.html#globals_clearimmediate_immediateobject)
Buffer其实都是基于typedArray实现的,v8是支持typedArray的。在浏览器中使用Buffer、setImmediate,只需要引入polyfill。
3.production build
如果代码中出现了 if (process.env.NODE_ENV === 'development') { // do something },在 production build 的代码里面,会有这段代码么,为什么?
打包的时候,这段代码会被优化掉。因为在编译的时候,这段代码就类似于 if(false) {},webpack在编译的时候会对 if(false === false){} 或者 if (true === true){} 类似的代码做标记,然后terser会删掉这段逻辑。同样,在代码压缩minify的时候,也会把类似if ('a'=== 'a'){} 这些逻辑优化掉。
4.axios 同时支持了 node 和 browser 环境
axios 同时支持了 node 和 browser 环境,是怎么支持的?打包的时候,两个环境的代码都会打包进去么?
axios源码里面,有xhrAdapter和httpAdater,通过判断当前是浏览器环境还是node环境,来决定使用哪个adapter。打包的时候,也只会把当前环境的adapter打包进去。
5.sockjs实现webpack热更新原理
sock-node 建立全双工低时延的websocket通信,来实现HTMLModuleReplacement,就是通知sock-client的dev-server代码发生了改动。
当代码发生更新后,本地的dev-server会重新编译run,然后生成热更新的.json文件推送给浏览器端展示。每次修改代码,紧接着触发重新编译,然后浏览器就会发出 2 次请求。请求的便是本次新生成的 2 个文件。
首先看json文件,返回的结果中,h代表本次新生成的Hash值,用于下次文件热更新请求的前缀。
c 表示当前要热更新的文件对应的是index模块。假设c是{ need-index-module:true }
新的Hash值:2536e910768ae4c9b393
新的json文件: 2536e910768ae4c9b393.hot-update.json
新的js文件:need-index-module.2536e910768ae4c9b393.hot-update.js
webpack负责监听文件是否变化,websocket只是实现双工通信,告诉浏览器啥时候请求。
https://juejin.im/post/5de0cfe46fb9a071665d3df0
https://segmentfault.com/a/1190000020310371
为什么代码的改动保存会自动编译,重新打包?这一系列的重新检测编译就归功于compiler.watch这个方法了。监听本地文件的变化主要是通过文件的生成时间是否有变化,比如fs.watch(sourceDir……),就可以监听到本地某个文件夹下文件的变化。
为什么在开发的过程中,你会发现dist目录没有打包后的代码,因为都在内存中。原因就在于访问内存中的代码比访问文件系统中的文件更快,而且也减少了代码写入文件的开销,这一切都归功于memory-fs。
6. vendor chunk
vendor: 小贩;摊贩;(某种产品的)销售公司;(房屋等的)卖主
webpack4之前,大家用CommonsChunkPlugin做code-split:详解CommonsChunkPlugin的配置和用法:https://segmentfault.com/a/1190000012828879
webpack-4之后,大家用splitChunkPlugin:https://juejin.im/post/5af15e895188256715479a9a
如何按需切割加载模块:https://webpack.docschina.org/api/module-methods/#import
7.优化打包模块的体积和命名方式(demo)
// vue.congif.js 示例 config.optimization.splitChunks({ automaticNameDelimiter: '-', // chunk命名用-隔开,默认好像是~ cacheGroups: { vendor: { minSize: 0, test(chunk) { return ( // vue的单独打包 /node_modules/.test(chunk.resource) && ['/vue/', '/vue-router/', '/vuex/'].some(key => chunk.resource.includes(key), ) ); }, name: 'vendor', chunks: 'all', enforce: true, }, common: { minChunks: 2, maxAsyncRequests: 2, maxInitialRequests: 2, name: 'common', chunks: 'all', enforce: true, }, }, });
优化前: 优化后:
config.optimization.splitChunks({ automaticNameDelimiter: '-', // chunk命名用-隔开,默认好像是~ cacheGroups: { vendor: { minSize: 0, test(chunk) { return ( // vue的单独打包 /node_modules/.test(chunk.resource) && ['/@vue/', '/vue-router/', '/vuex/'].some(key => chunk.resource.includes(key), ) ); }, name: 'vendor', chunks: 'all', enforce: true, }, uiLibs: { name: 'chunk-elementUI-libs', test: /node_modules\/element-ui/, priority: 10, chunks: 'initial', }, antV: { name: 'chunk-antv', test: /node_modules\/@antv/, priority: 10, chunks: 'initial', }, common: { minChunks: 2, maxAsyncRequests: 2, maxInitialRequests: 2, name: 'common', chunks: 'all', enforce: true, }, }, });
8.snowpack和ESM import
- 对于webpack,更改一个文件,需要把这个文件相关的文件(chunk)都重新编译并打包,当项目越庞大,涉及的chunk越多,打包时间越长
- 而基于
ESM
的snowpack,修改任何组件都只需做单文件编译,并且每个编译过的文件会被独立缓存,只要文件没改过就永远不会rebuild
,速度大大加快
9.找时间研究rollup
10.sourcemap
https://www.cnblogs.com/axl234/p/6500534.html
sourcemap是为了解决开发代码与实际运行代码不一致时帮助我们debug到原始开发代码的技术。eval和source-map都是webpack中devtool的配置选项, eval模式是使用eval将webpack中每个模块包裹,然后在模块末尾添加模块来源//# souceURL, 依靠souceURL找到原始代码的位置。包含eval关键字的配置项并不单独产生.map文件(eval模式有点特殊, 它和其他模式不一样的地方是它依靠sourceURL来定位原始代码, 而其他所有选项都使用.map文件的方式来定位)。包含source-map关键字的配置项都会产生一个.map文件,该文件保存有原始代码与运行代码的映射关系, 浏览器可以通过它找到原始代码的位置。
devtool: env === 'production' ? 'source-map' : 'module-eval-source-map'
所以在production环境是只能看到source-map,看不到eval-source-map。
9.webpack-bundle-analyzer
在vue-cli的vue.config.js中配置:webpack-bundle-analyzer
chainWebpack: config => { process.env.npm_report_event === 'analyzer' && config .plugin('webpack-bundle-analyzer') .use(require('webpack-bundle-analyzer').BundleAnalyzerPlugin); },
然后执行 yarn report ( "report": "npm_report_event=analyzer vue-cli-service build" ),就可以在控制台看到打包分析结果。
执行 yarn report --report ,就可以在浏览器看到打包分析结果。
10.webpack的loader和plugin的区别
loader,它是一个转换器,将A文件进行编译成B文件,比如:将A.less转换为A.css,单纯的文件转换过程。webpack自身只支持js和json这两种格式的文件,对于其他文件需要通过loader将其转换为commonJS规范的文件后,webpack才能解析到。例如:css-loader、style-loader、postcss-loader、sass-loader。
plugin是一个扩展器,它丰富了webpack本身,针对是loader结束后,webpack打包的整个过程,它并不直接操作文件,而是基于事件机制工作,会监听webpack打包过程中的某些节点,执行广泛的任务。例如:uglify-webpack-plugin、clean-webpack-plugin、babel-polyfill