前言
探讨了如何在 VUE 项目中基于源码实现可视化编程技术,并对未来的发展方向进行了展望。今日前端早读课文章由 @大前端分享,公号:哔哩哔哩技术授权。
正文从这开始~~
背景
在面对大型且高度组件化的项目时,传统的开发模式 —— 即边预览边手动修改代码,往往会因项目结构的复杂性而显得效率低下,尤其是对于新加入项目或对项目结构不够熟悉的开发者而言,从 UI 界面逆向定位到具体代码实现并作出修改的过程尤为耗时且挑战重重。为了解决这一问题,提升开发效率与团队协作的流畅度,我们构想了一种更为智能和直观的开发辅助方案,旨在通过 UI 直接映射到代码组件,并简化代码编辑过程,将源码 AST 与 UI 做绑定, 直接通过 UI 界面修改属性或内容,并自动同步到代码文件中,实现一种直观且高效的可视化编程体验,并通过这种方式来维护以及新建项目。
【第2597期】如何用JavaScript实现一门编程语言 - AST
传统低代码方案带来的问题
在深入探讨基于源码的可视化编程技术方案之前,我们先来分析一下传统低代码方案所带来的问题吧。
传统低代码方案通过自定义的私有协议与可视化视图做绑定,将代码逻辑抽象成私有描述,使用 JSON 配置来生成页面,后续只能通过维护 JSON schema 来维护项目。
功能和组件通常有一定的限制,无法完全满足所有复杂的、高度定制化的开发需求。因为是私有协议,所以定位问题也会比较困难。
公有协议
在摒弃私有协议,追求广泛兼容与协同工作的背景下,软件开发社区积极拥抱公有协议和标准。其中,抽象语法树(AST)作为解析 JavaScript(及其他编程语言)代码并转化为结构化数据的核心技术,已成为一个公认的公有协议。AST 还为开发者提供了一种统一、标准化的方式来理解和操作源代码。借助 AST 我们可以非常容易的对代码进行描述以及代码和协议之间的互转。
Vue AST Transform
AST 的生成通常涵盖代码的词法分析和语法分析阶段:
词法分析:将源代码分解成一个个有意义的单元(tokens),如关键字、标识符、运算符等。
语法分析:根据语言的语法规则,将 tokens 组织成 AST。在这个过程中,会构建出表示源代码结构的树状结构,其中每个节点对应源代码中的一个构造。
如下所示:
AST Transform 通常包括以下几个步骤:
解析(Parsing):将源代码转换为 AST
转换(Transformation):遍历 AST 并对其进行修改
生成(Generation):将修改后的 AST 转换回源代码文本
关键词说明
VariableDeclaration 变量声明
Expression:表达式,MemberExpression、BinaryExpression、UnaryExpression、AssignmentExpression、CallExpression、BindExpression、NewExpression 等...
Statement:语句,由 Expression 组成
...
可以通过访问 https://astexplorer.net 在线调试理解更多关键词类型。
解析
js 可以通过 babel 进行 ast 的解析,那 vue 文件该怎么解析成 ast 呢,你首先想到的肯定是 @vue/compiler-sfc
这个包,我们来看一下效果。
可以看到 descriptor 字段里分别用 template,script/scriptSetup
,styles 来描述 vue 文件的三个代码区块,所以可以将 descriptor 看成是 vue 的 ast,理论上来讲我们可以通过修改各个区块的 ast 节点,最后再通过将 descriptor 转回 vue 代码即可。很可惜的是官方设计 @vue/compiler-sfc
这个包主要是为了将 vue 代码编译为可以在浏览器运行的 render function,并没有提供 transform 的能力,即使修改了 ast,也无法将 ast 转成 vue-sfc 代码。
解决方案
为了实现对 vue-sfc 代码的 AST 修改并重新生成 vue-sfc 代码,我们可以采取一种分而治之的策略,针对代码中的不同区块(template, script/scriptSetup
, styles)使用不同的工具或库进行处理。通过上述 vue ast 的数据结构可以看到每个区块都有 content 字段表示当前区块的源代码,那是不是可以把 template 和 script 交给各自的解析器呢?(style 本文不做解析),script 区块的代码我们可以直接交给生态比较成熟的 babel 来处理,那 template 呢,我们可以想一下 template 默认的 lang 属性是什么?是不是 html,那是不是只要找到对应的 html 解析器就可以了。
流程图
template 区块的处理
通过对比多个 html 解析器,最终选择了 node-html-parser 来对 template 区块的代码来做 ast 的解析和处理。
对比其他解析器,这个包对 ast 的处理实现了 HTMLElement 接口,使用起来非常方便,就像处理 dom 节点一样简单。
注意:有一个坑,解析修饰符会有问题,仓库 issu 中有解决方法,需要自己改一下源码打补丁
更多使用方法可参考 WebAPI HTMLElement 接口,例如:
关联组件
组件在渲染的时候会被编译成 template 中的内容,那如果我需要在点击某一个组件的时候获取到该组件的 props 该怎么做呢?
可以使用 node-html-parser 给每个标签节点新增一个唯一标识,比如在每个标签的 class 中新增一个有固定前缀的随机唯一 id,点击的时候通过获取该 id,
然后通过 node-html-parser 提供的 querySelector(".${id}")
获取。
script 区块的处理
下文使用到的 babel 工具库:
@babel/parser
(对源代码进行 AST 解析)@babel-traverse
(遍历 ast 修改对应节点)@babel/generator
(ast 转成源代码)@babel/types
(用于处理 ast 节点的 lodash 式工具库)@babel/template
(通过字符串模板来创建 ast)
一些简单的案例
1、获取 ref
2、获取 computed
computed 的获取和 ref 其实大同小异
比如我们新增这样一段代码
3、获取生命周期
4、修改 ref 的值
5、修改 onMounted 回调里的执行语句
@babel/traverse
提供了 replaceWith,replaceWithSourceString 等 replace 方法来修改 AST
方式 1:
可以直接把表达式字符串传递给 replaceWithSourceString 来对 AST 进行修改
方式 2(推荐):
replaceWithSourceString 传递声明表达式语句会报错,推荐使用 @babel/template
创建 ast,减少容错,然后传递给 replaceWith
将
改成
style 区块的处理
那么同理 style 区块的代码也可以使用对应的解析器来处理,可以参考一些比较成熟的 loader,如 css-loader,postcss-loader 等
生成 vue-sfc 代码
实现思路
将通过 node-html-parser 修改过的 templateAST 重新生成 template 代码,使用 prettier 格式化
将通过 babel 修改的 scriptAST 重新生成代码,使用 prettier 格式化,
最后将格式化后的 template 代码,js 代码和没处理过的 style 区块的代码进行重组来达到生成 vue-sfc 代码的目的。
代码实现
效果展示
按住⌥option
可进行编辑模式,hover 选中组件,点击可以对组件进行可视化修改
修改 → 同步源码
重新渲染视图
流程图
未来展望
目前还在探索阶段,一些功能还处于开发阶段,未来将支持组件的拖拽,样式修改,全局状态,接口的管理,打通 AI,git 仓库,CICD,内部物料库组件等等
关于本文
作者:@大前端
原文:https://mp.weixin.qq.com/s/3PyLAoluIz3rjf30flrRDA
这期前端早读课
对你有帮助,帮” 赞 “一下,
期待下一期,帮” 在看” 一下 。