找回密码
 立即注册

小程序底层实现原理及一些思考

匿名  发表于 2022-1-4 09:43:50 阅读模式 打印 上一主题 下一主题
两月今后,看着电脑,我回忆起接到告诉说要开辟小法式引擎的阿谁下午。那时的我以为,这个小法式和其他小法式都纷歧样,由于它是个假的,实在是个网页。两月以后,我才发现,“噢~本来大师都是这么做的啊”。
比来一向在做小法式的底层实现,进程中磕磕绊绊也屡次停止架构偏向上的转型,趁着周末抽暇写一篇文章记录一下开辟进程中碰到的题目和一些思考与决议。
本篇文章更多的是在描写架构与技术偏向层面的思考和决议,不会过量先容具体某个题目是若何处理的,由于细节实在太多。
1. 单线程

那时的我将我们的小法式定位成一个SPA(单页利用),由于我们的小法式的宿主情况是阅读器。
它只是看起来像小法式(由于这个窗口没有地址栏什么的),但实在包括UI衬着和事务交互在内的绝大部分功用都是基于Web技术,虽然会供给Native和OS的一些才能与API,但本质上实在是个网页。又斟酌到今朝很多人利用第三方工具用Vue或React写小法式,我就在思考:“归副本质上就是一个网页,那为什么不原生内置Vue让用户间接用Vue的语法写小法式呢?”。
所以那时定了一个根基偏向:让开辟者利用Vue开辟我们的小法式,开辟体验完全与Web连结分歧。
虽然开辟体验与Web连结分歧,可是Web技术实在是太开放了,开辟者可以为所欲为。这类情况在小法式中是不答应的,不答应利用<iframe>、不答应<a>间接外跳到其他在线网页、不答应开辟者触碰DOM、不答应利用某些未知的危险API等。
所以碰到的第一个题目是若何制止用户在Vue的模板中利用iframe或a或其他不答应利用的工具。
若想做到这一点就不能差池Vue的衬着层停止一个托管与革新。
1.1 革新Vue

对Vue停止革新凡是有两种计划:

  • 利用类似polyfill的手法覆盖一些Vue原生供给的API
  • Fork一个Vue出来自己改
第一个计划才能有限,有一些Vue内部的逻辑没有法子经过polyfill的形式变动。第二种计划的缺点是假如我只想点窜Vue中的某一块逻辑,其他我不点窜的部分倘使有Bug,Vue官方更新了版本我没有法子同步。
这两种计划都有缺点和不敷,所以我没有益用这两个其中的任何一个,我利用了另一个计划,我感觉应当是今朝为止最好的一种计划。
我简单先容一下这类计划:

  • 把Vue.js装到node_modules里
  • 项目里利用webpack,并设备上别名
  • 把自己想革新的那部分代码copy到自己的项目目录中停止点窜
由于我需要对衬着层停止革新,所以我需要重设web这个体名,以下:
  1. const path = require('path')
  2. module.exports = {
  3.   'vue$': path.resolve(__dirname, '../src/web/entry-runtime-with-compiler'),
  4.   compiler: 'vue/src/compiler',
  5.   core: 'vue/src/core',
  6.   shared: 'vue/src/shared',
  7.   web: path.resolve(__dirname, '../src/web'),
  8.   weex: 'vue/src/platforms/weex',
  9.   server: 'vue/src/server',
  10.   sfc: 'vue/src/sfc'
  11. }
复制代码
大致道理是:假如import了一个不需要革新的,那实在是import了node_modules里的原始Vue的文件,假如是import了需要革新的,那实在import的是我的目录,文件也是我点窜后的文件。
对这方面技术有爱好可以留言具体味商,由于不是本文重点,不再展开。
1.2 为标签设备黑名单

Vue经过一系列计较以后终极产出的成果是一些指令,比如建立一个DOM元素,移除一个DOM元素,插入到某个位置等。
所以那时的做法就简单在建立DOM元素时,用tagName校验能否在黑名单中,假如在黑名单中就触发警告并怎样怎样样。
但实在这类做法只能是:防君子不防小人。
1.3 碰到的题目

项目做到这里碰到一个题目是非论怎样,都没有法子避免开辟者做一些我们想禁用的功用。由因而一个网页,开辟者可以履行JS,可以操纵DOM,可以操纵BOM,可以做一切工作。
所以我们起头斟酌将用户的代码放到一个绝对平安的沙箱中去履行。
2. 双线程

在Web技术下,可以将用户的代码放到Web Worker中去履行,也可以放到一个隐藏的iframe中去履行,大概宿主情况供给一个情况。不管怎样,目标都是不异的,就是把用户的代码放到一个绝对平安的沙箱中履行。
由于开辟者是基于Vue.js开辟,用户的代码是没有法子零丁放到沙箱中去履行的,所以我把Vue也放到沙箱中去履行。
这个时辰技术架构和技术偏向被调剂成了Master-Slave的双线程形式。
沙箱中的代码我称为Master,它经过一系列计较,终极输出一些指令:建立元素、点窜元素、插入元素、路由跳转、事务绑定等一些根本指令。这些指令从沙箱中经过线程间的消息机制传递到网页中,这个网页有一个模块叫做Slave,它负责监听Master发过来的指令并按照指令做指定操纵。

小法式底层实现道理及一些思考-1.jpg
把Vue放到Web Worker中履行需要处理很是多的题目,比如:原本Vue间接对DOM的操纵需要转换成向另一个线程发送指令,还有事务绑定题目,事务的Event工具题目,事务修饰符(event.preventDefault)题目,路由控制(双向的)题目,表单元素的双向绑定题目、ref题目等。由于线程间的消息传递只能传递字符串,所以很多工具就会变得很是麻烦。
不外这些具体的技术题目都是比力轻易处理的,比力难的题目是两个:“性能”和“原生才能受限”。
2.1 性能

在这类架构下,当页面有大范围UI变化时(例如初次衬着),Logic线程需要往UI线程发送大量的指令,包括:建立DOM,插入DOM,绑定事务等,每条指令都是一个零丁的跨线程的消息通讯,当消息数目庞大时,性能题目就会表暴露来,而且很是明显。
假如去Chrome DevTools的Performance面板看,会发现UI线程实在很闲,可是衬着的就是很慢。由于消息传递的价格,而且每次encode与decode也都需要价格,我自己写DEMO时没发现题目,可是投放到生产情况下去衬着一个实在的组件时,就会发现性能题目很是明显。
这个架构下虽然有性能题目,但以我的才能想处理这个题目也并不是太困难。另一个题目(原生才能受限)是这个架构下永久都没法处理的一个题目。
2.2 原生才能受限

为了平安,需要把用户的代码放到沙箱中去履行,但由于想让用户利用Vue开辟,所以Vue也得放到沙箱中,这就致使了一个没法处理的题目是:我们官方供给的原生组件,也会受限。
事理很简单,假定用户在模板中利用了一个官方原生供给的组件,那末这个组件一定是需要提早注册到Vue中的,那就代表,我们官方供给的组件,也得放在沙箱中,所以把我们自己也给限制住了。
致使我们官方想供给视频组件,音频组件就很困难,由于我们官方组件也不能碰DOM和BOM,就更不用说供给其他Native才能的组件了。
固然这个题目在不改架构的情况下还是有法子强行处理的,处理计划是这样的:
官方组件先在沙箱中注册一个Vue的组件,然后这个组件不去实现具体的功用,只是接收用户传递的数据,然后把数据传递给UI线程,然后UI线程有一个这个组件对应的实在的组件去做具体的功用并终极衬着到UI上。

小法式底层实现道理及一些思考-2.jpg
每一个组件都需要这样做,这对开辟组件的同学很是不友爱,他们需要了解这个小法式底层的架构是若何运转的,而且还会增加很多工作量。
这不是我想要的,我希望的是不管我底层是单线程还是双线程,对上层开辟是无感知的。而且由于Vue.js是在沙箱中做各类操纵,也不肯定未来会不会有什么需求是没法满足的,技术风险太高了。
到了这一步,我已经渐渐的起头感遭到,Vue已经成为瓶颈,它限制了我。
3. 回归单线程

不但是由于技术缘由,也由于一些其他缘由,比如风险太高,时候紧,终极决议先将计划切换回单线程,这样最少说可以保证这个项目不用延期。然后另一边再去渐渐调研并研讨出一个技术计划可以处理之前碰到的一切题目。
似乎又回到了起点,由于单线程有单线程的题目,那就是Web技术太开放了,我们没法做到“平安”和对开辟者停止“管控”。
不外我们还是在单线程形式下找到了可以进步一定平安性的计划,计划是经过ShadowDOM的Close形式把Body锁住。这样开辟者自己的代码是没法操纵DOM的,由于被我锁住了,可是开辟者的JavaScript是自在的。

小法式底层实现道理及一些思考-3.jpg
在这个架构下,开辟者可以操纵DOM,可以操纵BOM,可以操纵Vue.js,什么都可以干,但它没法间接操纵被我锁住的ShadowDOM,想操纵这个ShadowDOM,必须经过正当的路子操纵,而这个ShadowDOM才是小法式用于展现的首要的窗口。
然后对于BOM上的某些危险API,会被提早禁用掉。
这个计划似乎处理了一切题目,但还是为未来留下了一点隐患,只要开辟者的JavaScript是自在的,你就永久没法晓得他会用他的JavaScript做什么。对于某些未知的缝隙,能够很是危险,这为往后留下了一个风险,工作会酿成一场官方和开辟者之间持久的攻防战。
不管怎样,这个计划处理了今朝碰到的一切题目,这也为我留下了很是多的时候去研讨实在的小法式应当怎样做。
4. 回归双线程

终极,我发现双线程才是正确的偏向,只要把用户的代码放到沙箱里履行才能实在的做到:“平安”和“管控”。
不外这一次我决议不再利用Vue.js,我需要开辟一个全新的框架来支持双线程这类形式。上一次的双线程之所以会失利,首要缘由是上一次是UI线程比力轻,而Logic线程比力重,用户的代码,Vue.js,官方的组件都跑在Logic线程下,而这个线程只是一个JS运转时,所以我们的原生才能会遭到限制。
而这一次我决议让UI重一点,Logic轻一点。只把用户的代码和框架的一部分下放到Logic线程,大部分操纵和原生组件都放在UI线程下履行。

小法式底层实现道理及一些思考-4.jpg
Worker线程只是做一些计较然后把数据传递到UI线程,然后大部合作作都在UI下面履行,而且官方的组件在UI这边履行。
这样可以处理之前碰到的两个题目:性能和原生才能受限。
由于线程之间的消息通道只传递数据,而数据不会像绘制UI指令数目那末多,可以说底子不在一个数目级,性能题目处理了,而且不但处理了性能题目,还顺带着提升了性能,由于不管用户代码写的履行效力再怎样低,都不会卡死UI线程。
原生才能受限的题目也处理了,由于官方供给的组件底子不在这个线程下运转,平安和管控的题目也处理了。
5. 一些思考

之前我一向以为其他小法式是Native衬着的,而我是基于Web技术实现的,但偶然有一次看到一些材料,才发现,本来大师都是基于Web技术实现的小法式,而实现方式也大致不异,碰到的题目也都一样。
能够唯一的区分是,我不需要多个webview,我只需要一个网页就够了,所以我可以把Logic线程的代码放到Web Worker里,而其他小法式是多个webview,所以他们不能用Web Worker,不外没有本质区分。
5.1 关于小法式跨平台的一些看法

前一段时候各类小法式多端框架满天飞,正确的说,这些框架都是“翻译”器,就我小我感觉,以翻译为根本在分歧小法式之间停止跨端,只能算是一种姑且性技术计划。
我感觉实在的终极技术计划,有两种:

  • 从衬着引擎层面抹平平台间的差别,例如:Flutter
  • 各个小法式开辟商配合制定一些标准和标准,例如:阅读器
第一种,利用小法式供给的canvas的一些API实现一个衬着引擎,然后在衬着引擎上实现一些结构引擎,在此根本上供给的框架和其他才能都是同一的,分歧平台之间只需要实现分歧的衬着引擎即可。不外我不肯定小法式供给的canvas能不能做到这一点,不外Web阅读器供给的canvas可以做到,像SpriteJS就做到了。
第二种,各巨细法式厂商配合制定一套标准,依照标准实现各自的API,这类情况是比力好的,而且也不是完全没有能够。比来各巨细法式厂商已经在W3C起草了小法式白皮书。
我捡重要的列一下白皮书中的内容

  • 标准化小法式包(就是说一份小法式代码,可以在各巨细法式平台剖析,利用同一的.ma后缀的文件)
  • 标准化小法式页面的URI Scheme(就是说界说一份协议,然后同一个URI地址可以在分歧的小法式平台翻开不异的页面)
  • 标准化小法式Widgets
6. 总结

仔细看到这里的读者应当会对我开辟小法式的全部进程和一些决议有一个大致的领会。
大师对小法式的底层实现都是利用双线程模子,大师对外宣称城市说是为了:

  • 方便多个页面之间数据同享和交互
  • 为native开辟者供给更好的编码体验
  • 为了性能(避免用户的JS履行卡住UI线程)
  • 其他益处
但实在实在的缘由实在是:“平安”和“管控”,其他缘由都是附加上去的。
由于Web技术是很是开放的,JavaScript可以做任何事。但在小法式这个场景下,它不会给开辟者那末高的权限:

  • 不答应开辟者把页面跳转到其他在线网页
  • 不答应开辟者间接拜候DOM
  • 不答应开辟者随意利用window上的某些未知的能够有危险的API
固然,想处理这些题目纷歧定非要利用双线程模子,但双线程模子无疑是最合适的技术计划。
声明,本文仅限进修利用,终极会利用什么计划今朝还没法明白暗示。技术身分以及非技术身分城市影响终极技术计划的决议。
假如喜好,还可以在这里找到我~ https://github.com/berwin
回复

使用道具

大神点评

匿名  发表于 2022-1-4 09:44:04
不是微信小法式吧
回复

使用道具

匿名  发表于 2022-1-4 09:44:23
你不看看做者是哪家公司的吗。。那必定是360的小法式啊,具体在哪个App上利用不晓得,可是现在根基上是个大App都供给了小法式功用(固然大部分还是内部在用,这样便可以把一部分App开辟工作量转给前端了)。。
回复

使用道具

匿名  发表于 2022-1-4 09:45:17
微信小法式的题目主如果微信誉了很不现代的设想起了个坏头,致使大师不能不再开辟一个翻译器去顺应,而且就算现在起头定制标准,但已存在的大量小法式兼容又成磷砌大题目
回复

使用道具

匿名  发表于 2022-1-4 09:46:04
现在大部分小法式的api命名,逻辑都是根基类似的,少许的有不同,最严重的是快利用,根基上看不到同一的能够,每家动身点分歧。已存小法式兼容感受题目不大,保存新老两个库嘛,大概同一做翻译工具。
回复

使用道具

匿名  发表于 2022-1-4 09:46:39
贵司还是要赶紧完善用户文档,好多接口都没公布,还有api的利用方式也不全,还需要去问客服,客服再转给技术
回复

使用道具

匿名  发表于 2022-1-4 09:46:46
小法式原本就是个毛病的技术线路,把原本同一的web标准搞得支离破裂,正确的线路应当进修google的PWA,完全基于Web标准,定制webview,在webview上对利用做限制。
回复

使用道具

匿名  发表于 2022-1-4 09:47:00
不错的文章。
回复

使用道具

匿名  发表于 2022-1-4 09:47:39
你说的对.[赞][赞]
回复

使用道具

匿名  发表于 2022-1-4 09:47:53
话说做小法式的决议是谁下的?基于什么来由?
回复

使用道具

说点什么

您需要登录后才可以回帖 登录 | 立即注册
HOT • 推荐

神回复

站长姓名:王殿武 杭州共生网络科技 创始人 云裂变新零售系统 创始人 飞商人脉对接平台 创始人 同城交友聚会平台 创始人 生活经验分享社区 创始人 合作微信:15924191378(注明来意)