近期阅读文章的一点小笔记,主要内容有 JavaScript Error 深入学习,同构应用容易被忽略的地方,微前端介绍、webpack 构建速度优化的几个思考方向。
JavaScript Error
追溯栈格式,每一帧由以下三个部分组成
- 一个函数名
- 发生错误的脚本在网络中的地址
- 发生错误代码的行数和列数
为方便解析追溯栈,推荐这样做
- 为匿名函数取名 / 将函数赋值给一个变量
- displayName 属性:除了IE11,函数名的展现也可以通过给函数定义一个displayName 属性,displayName 会出现在浏览器的 devtools debugger 中
window.onerror 捕获错误
- 存在的问题
- No Error object provided:Safari 和 IE10还不支持在 window.onerror 的回调函数中使用第五个参数
- Cross domain sanitization:Firefox、Safari 或者 IE11 中,并不会引入跨域的 JS 错误,Chrome 是唯一一个能够通过 window.onerror 检测到其他源上面的文件错误的浏览器,要么将其过滤掉,要么为其设置合适的跨域头信息
- window.addEventListener(“error”):和 window.onerror API 相同
- 并不能够阻止错误显示在浏览器控制台中,这通常是正确的,也是开发需要的,因为开发者可以很容易从控制台中看到错误信息。如果你不希望这些错误在生产环境中显示给最终用户,那么在 window.addEventListener 中使用 e.preventDefault() 可以有效的避免错误显示在控制台上。
try/catch 问题
- 不能够捕获所有错误,比如异步错误
- try/catch 不利于性能优化,不能够被 V8 编译器优化。
- Protected Entry Points
- 通过这些入口代码抛出的 JS 错误能够被 window.onerror 捕获到,但是遗憾的是,在浏览器中这些代码入口抛出的错误并不是完整的 Error 对象
- 由于 try/catch 也不能够捕获到代码入口产生的 JS 错误
- 对这些入口代码进行包装,这样就是的在函数调用之前我们就可以引入 try/catch 语句,这样也就能够捕获入口代码抛出的错误了。
一个 JavaScript 的
代码入口
就是指任意开始执行你代码的浏览器 API。例如,setTimeout、setInterval、事件监听函数、XHR、web sockets、或者 promise。都可以是代码入口。
保护代码入口例子
function protectEntryPoint(fn) {
return function protectedFn() {
try {
return fn();
} catch (e) {
// Handle error.
}
}}
var _oldSetTimeout = window.setTimeout;
window.setTimeout = function protectedSetTimeout(fn, time) {
return _oldSetTimeout.call(window, protectEntryPoint(fn), time);};
Promises
- Promise 中的错误只会被 rejection 处理函数捕获到,window.onerror 是无法捕获到 promise 中的错误的
- 可以使用 Protected Entry Points 来包装一些 Promise 的方法,在其中添加一个 try/catch 语句来处理错误,使用这种方法可使得我们捕获更多错误信息。
Promise 封装例子
var _oldPromiseThen = Promise.prototype.then;
Promise.prototype.then = function protectedThen(callback, errorHandler) {
return _oldPromiseThen.call(this, protectEntryPoint(callback), protectEntryPoint(errorHandler));
};
Web Workers
- Dedicated workers
- Shared workers
- Service Workers
同构应用
同构应用能够实现的本质条件是虚拟 DOM,基于虚拟 DOM 我们可以生成真实的 DOM,并由浏览器渲染;也可以调用不同框架的不同 APIs,将虚拟 DOM 生成字符串,由服务端传输给客户端。
同构应用中往往被忽略的细节
- 打包环境区分:服务端运行的代码如果需要依赖 Node 核心模块或者第三方模块,就不再需要把这些模块代码打包到最终代码中了。因为环境已经安装这些依赖,可以直接引用。这样一来,就需要我们在 webpack 中配置:target:node,并借助 webpack-node-externals 插件,解决第三方依赖打包的问题。
- 注水和脱水:涉及到数据的预获取。也是服务端渲染的真正意义。
- 请求认证处理:透传 cookie 信息
- 样式问题处理:
isomorphic-style-loader
- meta tags 渲染:
React-helmet
- 404 处理
- 安全问题:
serialize-javascript
- 性能优化
- 使用缓存
- 采用 HSF 代替 HTTP
- 对于服务端压力过大的场景,动态切换为客户端渲染。
- NodeJS 升级。
- React 升级。
微前端
微前端架构旨在解决单体应用在一个相对长的时间跨度下,由于参与的人员、团队的增多、变迁,从一个普通应用演变成一个巨石应用(Frontend Monolith)后,随之而来的应用不可维护的问题。
微前端架构具备以下几个核心价值:
- 技术栈无关:主框架不限制接入应用的技术栈,子应用具备完全自主权
- 独立开发、独立部署:子应用仓库独立,前后端可独立开发,部署完成后主框架自动完成同步更新
- 独立运行时:每个子应用之间状态隔离,运行时状态不共享
MPA VS SPA
- MPA 方案的优点在于部署简单、各应用之间硬隔离,天生具备技术栈无关、独立开发、独立部署的特性。缺点则也很明显,应用之间切换会造成浏览器重刷,由于产品域名之间相互跳转,流程体验上会存在断点。
- SPA 则天生具备体验上的优势,应用直接无刷新切换,能极大的保证多产品之间流程操作串联时的流程性。缺点则在于各应用技术栈之间是强耦合的。
Stitching layer 作为主框架的核心成员,充当调度者的角色,由它来决定在不同的条件下激活不同的子应用。因此主框架的定位则仅仅是:导航路由 + 资源加载框架。而具体要实现这样一套架构,我们需要解决以下几个技术问题:
- 路由系统及 Future State
- App Entry
- 构建时组合 VS 运行时组合
- JS Entry vs HTML Entry
- 模块导入
- 应用隔离
- 样式隔离:Shadow DOM?CSS Module? BEM? Dynamic Stylesheet !
- JS 隔离:如何确保各个子应用之间的全局变量不会互相干扰,从而保证每个子应用之间的软隔离?
HTTP 协议
OSI 参考模型
- 物理层:光纤和网卡等,负责通信设备和网络媒体之间的互通
- 数据链路层:以太网,用来加强物理层功能
- 网络层:IP 协议和 ICMP 协议等,负责数据的路由的选择与数据转寄
- 传输层:TCP 协议和 UDP 协议等,承上启下,控制连接,控制流量
- 会话层:建立和维护会话关系
- 表达层:把数据转换为接受者系统可兼容的格式
- 应用层:HTTP、FTP、SMTP 和 SSH 等,粗犷的理解为程序员层
TCP/IP 参考模型
- 网络连接层:主机与网络相连的协议,如:以太网
- 网络互联层:IP 协议和 ICMP 协议等,负责数据的路由的选择与数据转寄
- 传输层:TCP 协议和 UDP 协议等,控制端对端的连接、流量和稳定性
- 应用层:HTTP、FTP、SMTP 和 SSH 等,粗犷的理解为程序员层
HTTP 协议的网络层基于 IP 协议,传输层基于 TCP 协议,因此就引出了我们开头说到的:HTTP 协议是基于 TCP/IP 协议的应用层协议。
这部分内容作者写的十分详细,更多请点击末尾链接
Webpack 构建优化
构建打点:speed-measure-webpack-plugin
,它能够测量出在你的构建过程中,每一个 Loader 和 Plugin 的执行时长
优化策略:缓存、多核、抽离以及拆分
- 缓存
- 大部分 Loader 都提供了 cache 配置项
- 如果没提供,则使用 cache-loader
- 多核:happypack
- 抽离
- webpack-dll-plugin
- Externals
- 拆分:微服务
提升体验工具
- progress-bar-webpack-plugin
- webpack-build-notifier
- webpack-dashboard