文章图片
一 概要 【显示器|前端实现多文件编译器】在前端工程中 , 有时我们需要在浏览器编译并执行一些代码 , 这种需求常见于低代码场景中 。 例如我们在搭建时需自定义一部分代码 , 这些代码需要在渲染时执行 。 为了方便起见 , 我们写的代码一定是 ES6 语法 , 如果要在浏览器执行 , 那么就必须经过编译 。 下面是前端编译 JS 代码的一些实践 。
二 需求描述
低码搭建时需要自定义一部分代码 希望代码是以多文件形式组织的 可以使用 ESModule 形式导入/导出 三 需求分析 1、在浏览器编译代码必然需要使用 babel 完成; 2、如果只有一个 JS 文件 , 那么可以直接使用 babel 的 transform 函数编译; 3、如果存在多文件 , 则文件内的变量必须相互隔离 , 且文件之间能够通过某种形式相互引用 , 并且需要考虑文件之间的依赖关系; 四 核心设计 流程 1 变量隔离 由于我们的需求是多文件编辑 , 各个文件内的变量应该相互隔离 。 最简单的办法是将每个文的内容转成一个闭包 , 再通过固定的接口将每个文件连接起来 。假设有 a.js , 内容如下: const a = 1;const b = 2;function sum () { return a + b'sum(); 可以将其转为如下形式: (function() { const a = 1; const b = 2; function sum () { return a + b'sum();)(); 转成这种形式之后 , 每个文件内的变量就只会存在于各自的闭包之内 , 互不影响 。五 文件引用 文件之间的相互引用可以通过定义一种接口规则实现: 所有文件的引用都将通过全局变量 module 进行; 每个文件都将对应到 module 上的一个对象 , key 根据文件名而定 。1 导出 原文件: // a.jsexport const a = 1; 编译后: (function() { __filename = 'a.js'; const a = 1; var mod = {; mod.a = a; module[__filename
= mod;)() 2 导入 源文件 // b.jsimport { hellofrom './a'hello(); 编译后 (function() { __filename = 'b.js'; var $$a = module['a.js'
; $$a.hello(); var mod = {; module[__filename
= mod;)() 六 依赖树解析 假设有一堆文件 , 我们通过解析(babel 或正则)后得到他们之间的关系如下: 他们之间存在循环依赖 根据这个依赖图可以梳理出几条依赖路线: A -B -D -C -F -循环依赖BA -B -E -F -循环依赖 BA -C -F -B -E -循环依赖 FA -C -G 从开始出现的第一个循环依赖截断依赖路线 , 分别统计统计每个节点的深度 , 按深度依次放入队列中 。如果两个节点深度相同 , 则分析两个节点的依赖关系 , 被依赖的先进队列 , 故最终形成的队列如下: F E B C D G A 为什么要得到一个编译顺序呢? 以上得出的编译顺序是为了尽可能解决如下的引用情况 , 但也不能解决所有: // a.jsexport const a = 2// b.jsimport { afrom 'a.js';console.log(a + 2); 这时候 , 假设执行 b 的时候 , a 还没被执行 , 那么 b 内部拿到的 a 实际上是 undefined , 显然不是我们所希望的 。 所以此时必须保证 a 先于 b 执行 。但这种使用方式在存在循环引用时无法解决 , 只能调整文件组织形式 。事实上 , 假设存在循环依赖时 , 下面的在函数内或在类内引用方式是没有问题的 , 有问题的只是直接使用: // a.jsexport const a = 2// b.jsimport { afrom 'a.js';export function test () { return a + 1; 这样 , 即使 b 有依赖 a , test 只要不是立即执行函数也不会产生影响 。七 编译 1 ESModule 转换 此过程可以通过自定义一个 Babel 插件完成 , 在语法编译时将文件编译成一个闭包 , 同时处理好 ESModule 语法 。该 Babel 插件很简单 , 在此就不展开去写了 。2 文件队列编译 对单个文件的编译可封装成一个方法 , 假设函数名为:compileFile 按照上面解析到的文件队列按照顺序逐个调用 compileFile 进行编译 , 并将结果直接拼接起来 , 形成一个巨大的字符串 , 该字符串的样子应该是如下的格式: (function() { __filename = 'b.js'; var $$a = module['a.js'
- 为什么人类无法实现永生?基因在从中搞鬼,战胜基因才有希望
- 显示器|三星 M8 智慧显示器发布,内置 TizenOS 可用 AirPlay2
- 显示器|结合三星49英寸带鱼屏,聊聊超大屏到底靠不靠谱儿,怎么选?
- 显示器|联想 G2722HP 27英寸IPS显示器到手1169
- |Web前端:使用Vue UI 组件库有哪些好处?
- 显示器|iPhone SE3降成“红米价”?果粉的春天来了。
- 液晶显示器|LCD党梦寐以求的神机!全球首款天玑8000+LCD屏手机曝光
- 显示器|Reno3元气版&K7 ColorOS12× Android12正式版开放升级!
- 本文转自:大众日报“这台机器人手臂前端安装了‘双目相机’|济宁机器人产业脉动5丨制造业又出新枝,培育出3家“小巨人”
- 显示器|折叠版iPad要来,苹果真敢想!