You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
constexpress=require('express')constfs=require('fs')constpath=require('path')const{ renderToString }=require('react-dom/server')consttemplate=fs.readFileSync(path.join(__dirname,'../dist/search.html'),'utf-8')constdata=require('./data.json')constSSR=require('../dist/search-server')if(typeofself==='undefined'){global.self={}}constrenderMarkUp=str=>{constdataStr=JSON.stringify(data)returntemplate.replace('<!--HTML_PLACEHOLDER-->',str).replace('<!--INSTALL_DATA_PLACEHOLDER-->',`<script>window.__install_data=${dataStr}</script>`)}constserver=(port)=>{constapp=express()app.use(express.static('dist'))app.get('/search',(req,res)=>{consthtml=renderToString(SSR)res.status(200).send(renderMarkUp(html))})app.listen(port,()=>{console.log('Server is running on port: ',`http://localhost:${port}`)})}server(process.env.port||3000)
constpath=require('path');constwebpack=require('webpack');constrimraf=require('rimraf');constMocha=require('mocha');constmocha=newMocha({timeout: 10000,});process.chdir(path.join(__dirname,'template'));rimraf('./dist',()=>{// eslint-disable-next-line global-requireconstprodConfig=require('../../lib/webpack.pord');webpack(prodConfig,(err,stats)=>{if(err){console.error(err);process.exit(2);}console.log(stats.toString({color: true,modules: false,children: false,chunks: false,chunkModules: false,}));});console.log('Webpack Build Success, begin run test.');// 不知道为什么 先执行了,延迟执行一下setTimeout(()=>{mocha.addFile(path.join(__dirname,'html-test.js'));mocha.addFile(path.join(__dirname,'css-js-test.js'));mocha.run();},3000);});
判断基本功能是否正常
编写mocha测试用例
是否有JS、CSS等静态资源文件
是否有HTML文件
constglob=require('glob-all');// eslint-disable-next-line no-undefdescribe('Checking generated html files',()=>{// eslint-disable-next-line no-undefit('should generate html files',(done)=>{constfiles=glob.sync(['./dist/index.html','./dist/search.html']);console.log('object',files);if(files.length>0){done();}else{thrownewError('no html files generated');}});});
const{
SyncHook,
SyncBailHook,
SyncWaterfallHook,
SyncLoopHook,
AsyncParallelHook,
AsyncParallelBailHook,
AsyncSeriesHook,
AsyncSeriesBailHook,
AsyncSeriesWaterfallHook
}=require("tapable");classCar{constructor(){this.hooks={accelerate: newSyncHook(["newSpeed"]),brake: newSyncHook(),calculateRoutes: newAsyncParallelHook(["source","target","routesList"])};}}constmyCar=newCar();// 绑定同步钩子// Use the tap method to add a consumentmyCar.hooks.brake.tap("WarningLampPlugin",()=>console.log('WarningLampPlugin'));// 绑定同步钩子myCar.hooks.accelerate.tap("LoggerPlugin",newSpeed=>console.log(`Accelerating to ${newSpeed}`));// 绑定一个异步 Promise 钩子myCar.hooks.calculateRoutes.tapPromise("calculateRoutes tapPromise",(source,target,routesList,callback)=>{returnnewPromise((resolve,reject)=>{setTimeout(()=>{console.log(`tapPromise too ${source}${target}${routesList}`);resolve()console.log('end');},1000)})});myCar.hooks.brake.call()myCar.hooks.accelerate.call(10)console.time('coost');// 执行异步钩子myCar.hooks.calculateRoutes.promise('Async','hook','demo').then(()=>{console.log('resolve')console.timeEnd('coost');},err=>{console.error(err);console.timeEnd('coost');})
WarningLampPlugin
Accelerating to 10
tapPromise too Async hook demo
end
resolve
coost: 1.005s
constCompiler=require('./compiler')classMyPlugin{constructor(){}apply(compiler){// 绑定同步钩子// Use the tap method to add a consumentcompiler.hooks.brake.tap('WarningLampPlugin',()=>console.log('WarningLampPlugin'))// 绑定同步钩子compiler.hooks.accelerate.tap('LoggerPlugin',(newSpeed)=>console.log(`Accelerating to ${newSpeed}`))// 绑定一个异步 Promise 钩子compiler.hooks.calculateRoutes.tapPromise('calculateRoutes tapPromise',(source,target,routesList,callback)=>{returnnewPromise((resolve,reject)=>{setTimeout(()=>{console.log(`tapPromise too ${source}${target}${routesList}`)resolve()console.log('end')},1000)})})}}
Babel 7 不需要 ts-loader。从 Babel 7 开始,ts-loader 是不必要的,因为 Babel 7 支持 TypeScript
https://github.com/Microsoft/TypeScript-Babel-Starter
https://stackoverflow.com/questions/38320220/how-to-setup-typescript-babel-webpack
https://babeljs.io/docs/en/babel-preset-typescript
https://devblogs.microsoft.com/typescript/typescript-and-babel-7/
webpack 系列一:最佳配置指北 #68
玩转 webpack5(上)
学习 Webpack5 之路(优化篇)- 近 7k 字
webpack5
面试官:webpack原理都不会?
从零实现一个迷你 Webpack
Notes
webpack4
version: 4.31.0
01丨课程介绍
02丨内容综述
03丨为什么需要构建工具
04丨前端构建演变之路
05丨为什么选择webpack
06丨初识webpack
只设定了 entry output
07丨环境搭建:安装webpack
08丨webpack初体验:一个最简单的例子
09丨通过npm script运行webpack
10丨webpack核心概念之entry用法
Entry 用来指定 webpack 的打包入口
11丨webpack核心概念之output
Output 用来告诉 webpack 如何将编译后的文件输出到磁盘
12丨webpack核心概念之loaders
13丨webpack核心概念之plugins
14丨webpack核心概念之mode
15丨解析ES6和React JSX
16丨解析CSS、Less和Sass
链式调用 从右到左
17丨解析图片和字体
file-loader 导入了,换了好几种字体都没成功...
18丨webpack中的文件监听
19丨webpack中的热更新及原理分析
20丨文件指纹策略:chunkhash、contenthash和hash
JS 的文件指纹设置
设置 output 的 filename,使用[chunnkhash]
[name][chunkhash:8].js
CSS 的文件指纹设置
设置 MiniCssExtractPlugin 的 filename, 使用 [contenthash]
[name][commtenthash:8].css
图片的文件指纹设置
[name][hash:8].[ext]
21丨HTML 、CSS和JS代码压缩
HTML CSS JS 压缩
JS
内置了
CSS
HTML
22丨自动清理构建目录产物
每次构建的时候不会清理目录,造成构建的输出目录 ouput 文件越来越多
23丨PostCSS插件autoprefixer自动补齐CSS3前缀
24丨移动端CSS px自动转换成rem
25丨静态资源内联
26丨多页面应用打包通用方案
多页面应用(MPA)概念
每一次页面跳转的时候,后台服务器都会给返回一个新的 html 文档,这种类型的网站也就是多页网站,也叫做多页应用。
多页面打包基本思路
每个页面对应一个 entry,一个 html- webpack- plugin
缺点:每次新增或删除页面需要改 webpack 配置
多页面打包通用方案
动态获取 entry 和设置 html- webpack- plugin 数量 利用 glob.sync
entry: glob.sync (path.join (_dirname, './src/*/index.js'),
27丨使用sourcemap
https://juejin.cn/post/6960941899616092167
https://webpack.js.org/configuration/devtool/#devtool
作用:通过 source map 定位到源代码
开发环境开启,线上环境关闭
Source map 关键字
eval:使用 eval 包裹模块代码
source map:产生 .Map 文件
cheap:不包含列信息
inline:将.Map 作为 DataURI 嵌入,不单独生成.Map 文件
module:包含 loader 的 sourcemap
default
devtool: 'source-map'
devtool: 'cheap-source-map',
28丨提取页面公共资源
基础库分离
思路:将 react、react-dom 基础包通过 cdn 引入,不打入 bundle 中
方法:使用 html- webpack- externals-plugin
利用 SplitChunksPlugin 进行公共脚本分离
Webpack4 内置的,替代 CommonsChunkPlugin:插件 chunks 参数说明:
async 异步引入的库进行分离(默认)
initial 同步引入的库进行分离
all 所有引入的库进行分离(推荐)
利用 SplitChunksPlugin 分离基础包
test:匹配出需要分离的包
利用 SplitChunksPlugin 分离页面公共文件
minChunks:设置最小引用次数为 2 次
minuSize:分离的包体积的大小 name: 'commons',
29丨treeshaking的使用和原理分析
Tree shaking(摇树优化)
概念:1 个模块可能有多个方法,只要其中的某个方法使用到了,则整个文件都会被打到 bundle 里面去,tree shaking 就是只把用到的方法打入 bundle,没用到的方法会在 uglify 阶段被擦除掉。
使用:webpack 默认支持,在。Babelrc 里设置 modules: false 即可
· production model 的情况下默认开启
要求:必须是 ES6 的语法,CJS 的方式不支持
DCE (Elimination)
代码不会被执行,不可到达代码执行的结果不会被用到代码只会影响死变量(只写不读)
Tree- shaking 原理
利用 ES6 模块的特点:
只能作为模块顶层的语句出现
import 的模块名只能是字符串常量
import binding 是 immutablel 的
代码擦除:uglify 阶段删除无用代码
30丨ScopeHoisting使用和原理分析
会导致什么问题?
大量函数闭包包裹代码,导致体积增大(模块越多越明显)
运行代码时创建的函数作用域变多,内存开销变大
结论:
进一步分析 webpack 的模块机制
分析:
打包出来的是一个 IFE(匿名闭包)
modules 是一个数组,每一项是一个模块初始化函数
_ webpack. Require 用来加载模块,返回 module. Exports
通过 WEBPACK_ REQUIRE_ METHOD (O)启动程序
Scope hoisting 原理
原理:将所有模块的代码按照引用顺序放在一个函数作用域里,然后适当的重命名一些变量以防止变量名冲突
对比:通过 scope hoisting 可以减少函数声明代码和内存开销
Scope hoisting 使用
webpack mode 为 production 默认开启必须是 ES6 语法,CJS 不支持
31丨代码分割和动态import
代码分割的意义
对于大的 Web 应用来讲,将所有的代码都放在一个文件中显然是不够有效的,特别是当你的某些代码块是在某些特殊的时候才会被使用到。webpack 有一个功能就是将你的代码库分割成 chunks(语块),当代码运行到需要它们的时候再进行加载。
适用的场景:
抽离相同代码到一个共享块
脚本懒加载,使得初始下载的代码更小
懒加载 JS 脚本的方式
CommonJS: require.ensure
ES6:动态import (目 前还没有原生支持,需要babel转换)
如何使用动态 import?
代码分割的效果
发送一个 Jsonp 请求,
window[webpackJsonp]
异步加载逻辑新版本 webpack5 是
(self["webpackChunkwebpack_learn"] = self["webpackChunkwebpack_learn"] || [])
根据我的 package name 来的,
"name": "webpack-learn"
32丨webpack和ESLint结合
ESL _int如何执行落地?
和CI/CD系统集成
和webpack集成
本地开发阶段增加 precommit 钩子
方案二: webpack 与ESLint集成
33丨webpack打包组件和基础库
webpack打包库和组件
webpack除了可以用来打包应用,也可以用来打包js库
实现一个大整数加法库的打包
如何将库暴露出去?
library:指定库的全局变量
libraryTarget:支持库引入的方式
34丨webpack实现SSR打包(上)
服务端渲染(SSR)是什么?
渲染:HTML+csS+JS+Data-->渲染后的HTML
服务端:
SSR的优势
减少白屏时间
对于SEO友好
35丨webpack实现SSR打包(下)
如何解决样式不显示的问题?
首屏数据如何处理?
36丨优化构建时命令行的显示日志
当前构建时的日志显示.
展示一大堆日志,很多并不需要开发者关注
Stats Presets
Webpack comes with certain presets available for the stats output:
'errors-only'
'errors-warnings'
'minimal'
'none'
false
'normal'
true
'verbose'
'detailed'
chunkModules
andchunkRootModules
'summary'
如何优化命令行的构建日志
使用friendly-errors-webpack-plugin
37丨构建异常和中断处理
如何判断构建是否成功?
在CI/CD的pipline 或者发布系统需要知道当前构建状态
每次构建完成后输入echo $?获取错误码
构建异常和中断处理
webpack4之前的版本构建失败不会抛出错误码(error code)
Node.js中的process.exit规范
如何主动捕获并处理构建错误?
compiler在每次构建结束后会触发 done 这个 hook
process.exit主动处理构建报错
38丨构建配置包设计
构建配置抽离成npm包的意义
通用性
可维护性
质量
构建配置管理的可选方案
通过多个配置文件管理不同环境的构建,webpack -- config参数进行控制
将构建配置设计成一个库,比如: hjs- -webpack、 Neutrino、 webpack- -blocks
抽成一个工具进行管理,比如: create-react- -app, kyt, nwb
将所有的配置放在一个文件,通过-- env参数控制分支选择
构建配置包设计
通过多个配置文件管理不同环境的webpack配置
抽离成一个npm 包统一管理
通过 webpack-merge 组合配置
https://www.npmjs.com/package/webpack-merge
39丨功能模块设计和目录结构
功能模块设计
目录结构
lib放置源代码
test放置测试代码
拆分不同环境的配置,通过 merge 合并
40丨使用ESLint规范构建脚本
使用ESL int规范构建脚本
使用eslint-config- -airbnb- base
eslint -- fix 可以自动处理空格
41丨冒烟测试介绍和实际运用
冒烟测试(smoke testing)
冒烟测试是指对提交测试的软件在进行详细深入的测试之前而进行的预测试,这种
预测试的主要目的是暴露导致软件需重新发布的基本功能失效等严重问题。
冒烟测试执行
构建是否成功
每次构建完成build目录是否有内容输出
判断构建是否成功
在示例项目里面运行构建,看看是否有报错
判断基本功能是否正常
编写mocha测试用例
42丨单元测试和测试覆盖率
43丨持续集成和TravisCI
接入Travis CI
44丨发布到npm
发布到npm
添加用户: npm adduser
升级版本
升级补丁版本号: npm version patch
升级小版本号: npm version minor
升级大版本号: npm version major
发布版本: npm publish
45丨Git丨Commi规范和changelog生成
46丨语义化版本(Semantic丨Versioning)规范格式
47丨初级分析:使用webpack内置的stats
stats:构建的统计信息
package.json 中使用 stats
48丨速度分析:使用speed-measure-webpack-plugin
速度分析插件作用
分析整个打包总耗时
每个插件和loader的耗时情况
49丨体积分析:使用webpack-bundle-analyzer
可以分析哪些问题?
依赖的第三方模块文件大小
业务里面的组件代码大小
50丨使用高版本的webpack和Node
51丨多进程多实例构建
52丨多进程多实例并行压缩
推荐
53丨进一步分包:预编译资源模块
进一步分包:预编译资源模块
思路:将react、react- dom、redux、 react- redux 基础包和业务基础包打包成一个文件
方法:使用DLLPlugin进行分包,DllReferencePlugin 对manifest.json弓|用
54丨充分利用缓存提升二次构建速度
缓存
目的:提升二次构建速度
缓存思路:
55丨缩小构建目标
缩小构建目标
目的:尽可能的少构建模块
比如babel-loader 不解析node_ modules
减少文件搜索范围
优化resolve.modules配置(减少模块搜索层级)
优化resolve.mainFields配置
优化resolve.extensions 配置
合理使用alias
56丨使用webpack进行图片压缩
图片压缩
要求:基于Node库的imagemin或者tinypng API
使用:配置image- -webpack-loader
57丨使用TreeShaking擦除无用的CSS
tree shaking(摇树优化)复习
概念: 1个模块可能有多个方法,只要其中的某个方法使用到了,则整个文件都会被打到
bundle里面去,tree shaking就是只把用到的方法打入bundle,没用到的方法会在
uglify阶段被擦除掉。
使用: webpack默认支持,在.babelrc里设置modules: false即可
要求:必须是ES6的语法,CJS的方式不支持
无用的CSS如何删除掉?
PurifyCSS:遍历代码,识别已经用到的CSS class
uncss: HTML需要通过jsdom加载,所有的样式通过PostCSS解析,通过
document.querySelector来识别在html文件里面不存在的选择器
58丨使用动态Polyfill服务
59丨webpack启动过程分析
查找webpack入口文件
在命令行运行以上命令后,npm会让命令行工具进入node_ modules.bin 目录
查找是否存在webpack.sh或者webpack.cmd文件,如果存在,就执行,不
存在,就抛出错误。
实际的入口文件是: node_ modules \webpack \bin\webpack.js
"version": "5.75.0",
启动后的结果
webpack最终找到webpack- -cli (webpack- -command) 这个npm包,并且执行CLI
60丨webpack-cli源码阅读
webpack- -cli做的事情
引入yargs,对命令行进行定制
分析命令行参数,对各个参数进行转换,组成编译配置项
引用webpack,根据配置项进行编译和构建
从NON_ COMPIL ATION_ _CMD分析出不需要编译的命令
webpack- cli处理不需要经过编译的命令
webpack-cli执行的结果
webpack--cli对配置文件和命令行参数进行转换最终生成配置选项参数options
最终会根据配置参数实例化webpack对象,然后执行构建流程
61丨Tapable插件架构与Hooks设计
Webpack的本质
Webpack可以将其理解是一种基于事件流的编程范例,一 系列的插件运行。
核心对象Compiler继承Tapable
核心対象Compilation継承Tapable
Tapable是什么?
Tapable是一个类似于Node.js的EventEmitter的库,主要是控制钩子函数的发布与订阅,控制着webpack的插件系统。
Tapable库暴露了很多Hook (钩子)类,为插件提供挂载的钩子
Tapable hooks 类型
Tapable的使用-new Hook新建钩子
Tapable暴露出来的都是类方法,new一个类方法获得我们需要的钩子
class接受数组参数options,非必传。类方法会根据传参,接受同样数量的参数。
const hook1 = new SyncHook(["arg1", "arg2", "arg3"]);
Tapable的使用-钩子的绑定与执行
Tabpack提供了同步&异步绑定钩子的方法,并且他们都有绑定事件和执行事件对应的方法。
Tapable的使用-hook基本用法示例
const hook1 = new SyncHook(["arg1", "arg2", "arg3"]);
//绑定事件到webapck事件流
hook1.tap('hook1', (arg1, arg2, arg3) => console.log(arg1, arg2, arg3)) //1,2,3
//执行绑定的事件
hook1.call(1,2,3)
Tapable的使用-实际例子演示
定义一个Car方法,在内部hooks.上新建钩子。分别是同步钩子accelerate、brake (accelerate接受一个参数)、 异步 钩子calculateRoutes
使用钩子对应的绑定和执行方法
calculateRoutes使用tapPromise可以返回一个promise对象。
62丨Tapable是如何和Webpack进行关联起来的?
模拟 Compiler.js
插件 my-plugin.js
模拟插件执行
63丨webpack流程篇:准备阶段
WebpackOptionsApply
将所有的配置options 参数转换成webpack内部插件
使用默认插件列表
举例:
64丨webpack流程篇:模块构建和chunk生成阶段
Compiler hooks
流程相关:
监听相关:
Compilation
Compiler 调用 Compilation 生命周期方法
NormalModule
Build
Chunk生成算法
65丨webpack流程篇:文件生成
66丨动手编写一个简易的webpack(上)
模块化:增强代码可读性和维护性
传统的网页开发转变成Web Apps开发
代码复杂度在逐步增高
分离的JS文件/模块,便于后续代码的维护性
部署时希望把代码优化成几个HTTP请求
常见的几种模块化方式
ES module
CJS
AMD
AST基础知识
在计算机科学中,抽象语法树(Abstract Syntax Tree,AST),或简称语法树(Syntax tree),是源代码语法结构的一种抽象表示。 它以树状的形式表现编程语言的语法结构,树上的每个节点都表示源代码中的一种结构。 之所以说语法是“抽象”的,是因为这里的语法并不会表示出真实语法中出现的每个细节。
DEMO: https://esprima.org/demo/parse.html
动手实现一个简易的webpack
可以将ES6语法转换成ES5的语法
可以分析模块之间的依赖关系
生成的JS文件可以在浏览器中运行
67丨动手编写一个简易的webpack(下)
The text was updated successfully, but these errors were encountered: