不知不觉,九月已经过去,由于这个月工作上,被 C++ 折磨得很难受,而且其他时间都在学习,所以没有时间写文章,好在技术提升很大。今天准备好好谈一谈重型应用的架构以及技术选型,为接下来我的正式架构设计做个铺垫。
为什么写重型应用的架构和技术选型
是人都想做出点什么事情,我想引起大家的共鸣去使用某些技术,或者朝着这个方向去发展,共同提升 社区的技术整体层次。
例如微信、QQ、Telegram 以及一些工具类的应用。
说到这些大家肯定觉得,为什么不说是游戏?当然游戏也算,可是我相信做出 1000 万人每天都在用的产品是大家的梦想,起码能吹一辈子吧。
工具类的东西其实是最难做的,比如 vsCode , Excel ,PhotoShop 这些。这也是为什么这么多年出现成功的工具类产品这么少。这里不得不提到 vsCode,它其实就是用 ELectron 开发,基于 TypeScript。当然肯定使用了不少 C++ 插件,说到这里,留下伤心的眼泪,最近也是被折磨得很难受。
成功开发一个重型应用的好处
更容易财务自由,生活自由,例如现在很多有过成功的重型应用开发者已经不单纯靠代码产出维持生活。他们做技术顾问,卖课程,出书,办培训都甚至比单纯写业务代码赚得多很多。
正式开始
库克说过,中国的移动端开发确实很强,美国人要做一个应用,首先考虑的是 PC 桌面端,而国内首先考虑的是移动端。
国内移动端开发人员,在我看来已经人目前已经够多了,如果说你现在不会 React-Native 和 Flutter,我也不建议你去深入学习,特别是 Flutter。
为什么要这么说?
React-Native 刚出来的时候,坑多吧。现在 Flutter 也是,可是当你从 RN 最初的版本踩坑踩到现在,之前踩的坑大都没有意义。(说这些话想过被喷,但是... 此处省略一万字,建议去了解下原理和基本使用,不要耗费太多时间)
一个技术你去使用,并不是它多流行,只要它足够流行。--- 来自某位国内大佬
技术的学习,应该多往底层钻研,如果你走错了路,钻错了方向,浪费了时间得不偿失,我之前有说过,前端最核心的几个基础知识点,应用层的东西从来不会很难。前提你的基础足够扎实。
既然说了移动端没有合适的重型跨平台应用开发框架,那么只有 PC 端了。还有多少小伙伴在 PC 端开发呢?
最新的 Node 版本、运行的 V8 环境以及最新的谷歌浏览器一起被打包,最新的技术和 API 都可以用,无需适配担心兼容性,真正放飞自我,可以随时随刻用 Node.js 实现功能,甚至调用大量 C++ 插件,著名的 VSCode 就是这样而来。
你甚至可以看成 Electron 给 web 网页套上一层壳,你可以在主进程写你的 Node.js 去实现功能,渲染进程你怎么写怎么写,还可以呼叫封装好的原生接口。遇到特别复杂的需求,用 C++ 插件去实现吧。
最终打包出来的安装包跟正常的桌面应用是一样的,正常安装卸载等,都已经封装好。
目前 GitHub 上已经有 77.2K 的 star。
应用层面的东西,大都不会太难,Electron 的文档已经非常全面,基于它出现了很多复杂,而且成功的工具类重型应用。我相信它。
WhatsApp 也是基于它,国外还有一些很 NB 的应用也是。这里不做过多阐述,可以确定它是一个成熟而且成功的框架。
可能很多人看到这里又要说标题党了,别急,下面来干货。
重型应用架构注重的核心问题
一个好的开发,它一定能懂一些产品,甚至测试,当然他也应该会炒河粉,35 岁以后好维持生活。
我们今天举一个例子,IM,即时通讯,Telegram,20 万人超级群端到端加密的核心卖点产品。
电报 Telegram:
答案:20 万人的超级群,端到端加密,隐私足够安全
核心竞争力,往往代表了这个应用产物的技术最难点,因为谁都能做,那么就不是核心竞争力了
所以我们其他的都忽略,关注第三点,开始进行技术选型,架构。
要想写好这个架构,我觉得你首先在自身的擅长领域不能有太多的黑盒过程。例如框架源码,库原理实现,浏览器和 Node.js 的事件 EventLopp 以及他们的缺陷,你要熟知在心。因为像这种应用,一个小方向可能就会掏空你的技术栈,耗尽你的精力,例如音视频、图片处理等。
单线程的 Node.js 以及 js 主解析引擎,让我们又爱又恨。
这款应用的核心竞争力,是 20 万人超级群,那么数据量很大,大批量渲染压力和频繁加解密计算耗时、频繁数据库写入压力都是不可避免的,那么我们的 Node.js 擅长异步非阻塞,以及前端渲染进程的异步就显得尤为重要。
前端架构整体的核心除了技术选型以及大体框架外,就是任务调度。
非渲染任务调度。
单个渲染任务调度
React 的 Fiber 架构思想, 把若干个任务分割成多个小任务执行,中间根据你的任务优先级安排去选择时机执行。
淘宝的分片渲染方案,跟上面第二条有一些类似。
我之前说过,应用层的东西都不难,只要你基础足够扎实,能手写出简单的框架,以及库。你绝对能非常轻松应对前端百分 80 以上的性能问题和需求,技术最终都是相似的。
上面只是说了别人的一些比较简单的优化方案,下面才是开始我们自己的渲染任务调度:
回到我们的 Telegram 架构设计方案。
考虑大批量数据到达渲染进程的用户应用体验,确定用户交互属于高优先级任务,其他的哪些是低优先级 - 但必须执行的任务,哪些是中优先级任务,这里说的任务,都是渲染任务。
今天在学习一篇小册,里面有一句话引起了我的共鸣,在计算机的世界,如果有解决不了的问题,那就加一个中间层,如果还不行,那就加两个。
——后面这句是我加的。
这个是我自己编写的 React, mini-react 源码地址: https://github.com/JinJieTan/mini-react
if (setStateQueue.length === 0) {
// 清空队列的办法是异步执行
defer(flush);
}
setStateQueue.push({
stateChange,
component
});
function defer(fn) {
//requestIdleCallback 的兼容性不好,对于用户交互频繁多次合并更新来说,requestAnimation 更有及时性高优先级,requestIdleCallback 则适合处理可以延迟渲染的任务~
// if (window.requestIdleCallback) {
// console.log('requestIdleCallback');
// return requestIdleCallback(fn);
// }
// 高优先级任务
return requestAnimationFrame(fn);
}
while ((component = renderQueue.shift())) {
renderComponent(component);
}
上面这段代码其实很重要,核心思想就是:
每当进入这个函数,如果发现队列队列里没有任务就去执行 defer 函数。
defer 函数执行是异步,此时 defer 下面的 setStateQueue 已经被 push 了进去数据,这样达到一帧完成一次渲染任务调度。
当然上面仅仅一个小的任务调度,这个必须要了解,才能往下看
requestAnimationFrame 和 requestIdleCallback 使用:
你需要深入了解 React 框架的 Fiber 架构,这块尤其重要,是性能优化,任务调度的基础,上面有提到,React 在每次 diff 对比阶段,将任务分割成若干个小任务,此时如果有 RAF 和 RID 的任务,就要考虑去执行了。
RAF 的任务会每次在下一次小任务前执行。
RID 的任务只有在下一次小人物前,有空余时间才会执行,所以它不一定会执行。(特别高频必须执行的任务)
Fiber 架构配合单个任务分割已经介绍完毕,下面出思维导图出整体的任务调度。
合理调度任务,分高、中、低三种优先级别任务。
释放主线程。
这里特别提示,为什么我一直强调不要使用定时器,一旦应用变得很复杂,如果任务调度不合理,定时器里的代码是要很久很久才能执行的。当然,只有重型应用会这样。
渲染任务调度这块,主要是微任务,RAF,RID 分片渲染以及同步代码,队列调度等手段。
异步调度任务,写入数据库异步,解密计算可以使用 nextTick 等方式去调度添加队列。
这里提到,任务调度的核心一点就是,频繁触发的任务必须加入队列,异步清空,否则像解密这种同步计算耗时,一旦被频繁触发就会引起阻塞。即使交给了其他进程,也要做队列。
重型应用架构远不止这些,所以标题是浅谈,等下个月技术作者再度飞速提升一波,再来谈这些。
来源 | 转载自微信公众号:前端巅峰