跳到主要内容

# 出码模块设计

本篇主要讲解了出码模块实现的基本思路与一些概念。如需接入出码和定制出码方案，可以参考《使用出码功能》一节。

## npm 包与仓库信息​

NPM 包 | 代码仓库 | 说明
--- | --- | ---
@alilc/lowcode-code-generator | alibaba/lowcode-engine（子目录：modules/code-generator） | 出码模块核心库，支持在 node 环境下运行，也提供了浏览器下运行的 standalone 模式
@alilc/lowcode-plugin-code-generator | alibaba/lowcode-code-generator-demo | 出码示例 -- 浏览器端出码插件

## 出码模块原理​

出码模块的输入和输出很简单：

这里有几个概念：

- schema: 搭建协议内容，指符合《阿里巴巴中后台前端搭建协议规范》的 schema
- solution：出码方案，指具体的项目框架（如 Rax，Ice.js)
- Source Codes：生成的源代码，以目录树的形式进行描述

可以看出，这是一个与用户基本没有交互，通过既定的流程完成整个功能链路的模块。其核心暴露的是一个将搭建协议 schema 按既定的 solution 转换为代码的函数。对于使用者来说就是一个输入输出都确定的黑盒系统。

### 出码流程概述​

出码模块和编译器很类似，都是将代码的一种表现形式转换成另一种表现形式，如：

#### 编译器流程​

#### 出码模块流程​

### 出码流程详解​

#### 协议解析​

协议解析主要是将输入的 schema 解析成更适合出码模块内部使用的数据结构的过程。这样在后面的代码生成过程中就可以直接用这些数据，不必重复解析了。

主要步骤如下：

- 解析三方组件依赖
- 分析 ref API 的使用情况
- 建立容器之间的依赖关系索引
- 分析容器内的组件依赖关系
- 分析路由配置
- 分析 utils 和 NPM 包依赖关系
- 其他兼容处理

#### 前置优化​

前置优化是计划基于策略对 schema 做一些优化。

主要逻辑分为分析、规则和优化三个部分，组合为一个支持通过配置进行一定程度定制化的策略包。每个策略包会先执行分析器，对输入进行特征提取，然后通过规则对特征进行判断，决定是否执行优化动作：

#### 代码生成​

代码生成的流程如下：

如果简单粗暴地拼字符串生成源代码将难以扩展和维护，因此出码模块在代码生成过程中将代码进行了一些抽象化。

日常开发中，我们常常是基于某一个特定的项目框架，将一些配置、UI 代码、逻辑代码放到他们应该在的地方，最终形成一套可以 run 起来的业务系统。那么其实对于出码这件事，我们也可以层层拆解，项目 -> 插槽 -> 模块 -> 文件 -> 代码块（代码片段）。这样就能将复杂的项目产出问题，拆分为一个个相对专注且单一的代码块产出问题，同时也支持组合复用。

注：中间表达结构即为对 Schema 解析后的结构化产物

##### 插槽​

首先来看下插槽，插槽描述了对应模块在项目中相对路径，并且可以对模块做固定的命名。每个插槽都有一系列插件来完成代码产出工作。生成的一个或多个文件，最终会依照插槽的描述放入项目中。

    // 项目模版
    export interface IProjectTemplate {
      slots: Record<string, IProjectSlot>;
    }

    // 插槽
    interface IProjectSlot {
      path: string[];
      fileName?: string;
    }

    // 插槽出码插件配置
    interface IProjectPlugins {
      [slotName: string]: BuilderComponentPlugin[];
    }

##### 代码块​

代码块是出码产物的最小单元，由出码模块插件产出，多个代码块最后会被组装为代码文件。每个代码块通过 name 描述自己，再通过 linkAfter 描述应该跟在哪些 name 的代码块后面。

    interface ICodeChunk {
      type: ChunkType;        // 处理类型 ast | string | json
      fileType: string;       // 文件类型 js | css | ts ...
      name: string;           // 代码块名称，与 linkAfter 相关
      subModule?: string;     // 模块内文件名，默认是 index
      content: ChunkContent;  // 代码块内容，数据格式与 type 相关
      linkAfter: string[];
    }

#### 后置优化​

后置优化分为文件级别和项目级别两种：

- 文件级别：在生成完一个文件后进行处理
- 项目级别：在所有文件都生成完了之后进行处理

文件级别的后置优化目前主要是有 prettier 这个代码格式化工具。