第三章 项目初始化与架构设计

好,咱们正式开始动手了。

这一章,我会带你从零搭起一个低代码可视化编辑器的项目骨架。说白了,就是先把地基打好。地基稳不稳,直接决定后面盖楼会不会塌。我在好几个项目里都见过那种「先写着再说」的团队,结果写到一半发现架构撑不住了,重构成本高得吓人。嗯,咱们不干那种事。

3.1 使用 Vite 搭建项目

为什么选 Vite?

我个人习惯是:新项目首选 Vite。原因很简单——快。尤其是开发阶段的热更新,Vite 基于 ES Module,几乎秒开。你想想看,编辑器这种交互密集型的应用,开发过程中要反复调试组件,如果每次改完等两三秒才刷新,那体验太糟糕了。

初始化命令很简单:

npm create vite@latest lowcode-editor -- --template vue-ts

这里我选了 Vue 3 + TypeScript 的组合。为什么?

  • Vue 3 的 Composition API 非常适合编辑器这种复杂状态管理的场景
  • TypeScript 能帮我们在编译阶段就发现类型错误,避免运行时踩坑
小提示: 如果你团队更熟悉 React,换成 React + TypeScript 也完全没问题。架构思路是通用的。

安装完依赖后,跑一下 npm run dev,看到页面出来,就算第一步完成了。

3.2 目录结构规划

接下来是重头戏——目录怎么分?

我见过很多新手直接把所有代码扔到 src 下面,然后过两周就乱成一锅粥。咱们得从一开始就规划清楚。

这是我个人比较推荐的结构:

src/
├── components/          # 通用UI组件
│   ├── common/          # 按钮、输入框等基础组件
│   └── editor/          # 编辑器专用组件
├── core/                # 核心引擎
│   ├── engine.ts        # 渲染引擎
│   ├── event-bus.ts     # 事件总线
│   └── plugin-system.ts # 插件系统
├── stores/              # 状态管理
│   ├── editor.ts        # 编辑器状态
│   └── project.ts       # 项目状态
├── services/            # 服务层
│   ├── api.ts           # 接口请求
│   └── storage.ts       # 本地存储
├── utils/               # 工具函数
│   ├── dom.ts           # DOM操作
│   └── validator.ts     # 校验工具
├── types/               # 类型定义
│   └── editor.d.ts      # 编辑器相关类型
└── views/               # 页面视图
    ├── Designer.vue     # 设计器主页面
    └── Preview.vue      # 预览页面

你可能会问:为什么要把 core 单独拎出来?

因为核心引擎是整个编辑器的灵魂。它负责组件的渲染、拖拽、选中、缩放等基础能力。这部分应该和 UI 层解耦,方便后续单独测试或复用。

核心原则: 高内聚、低耦合。每个模块只做一件事,做好一件事。

3.3 模块划分

模块划分这件事,说白了就是「谁该管什么」。

我把编辑器拆成了四个核心模块:

模块 职责 典型文件
渲染引擎 将组件树渲染到画布上 core/engine.ts
状态管理 维护组件树、选中状态、历史记录 stores/editor.ts
事件系统 处理拖拽、点击、键盘等交互事件 core/event-bus.ts
插件系统 提供扩展点,支持第三方插件 core/plugin-system.ts

每个模块之间通过接口通信,不直接依赖。举个例子:渲染引擎只管「怎么画」,不管「画什么」。组件数据由状态管理提供,事件由事件系统分发。这样改一个模块,其他模块不受影响。

避坑指南: 我曾经在一个项目里把渲染逻辑和事件处理写在一起,结果后来要支持移动端适配,改得我头皮发麻。所以从一开始就分清楚,后面会省很多事。

3.4 设计模式选择:MVVM 还是 MVC?

这个问题我经常被问到。我的答案是:MVVM

为什么?

你想想看,编辑器本质上是一个「数据驱动视图」的应用。用户拖拽一个组件,本质上是修改了组件树的数据,然后视图自动更新。这不就是 MVVM 最擅长的场景吗?

MVC 的问题在于,Controller 层容易变得臃肿。尤其是编辑器这种交互复杂的场景,Controller 里既要处理数据,又要操作 DOM,最后代码会很难维护。

MVVM 的典型分层:

  • Model:组件树数据、项目配置
  • View:Vue 组件模板,负责渲染
  • ViewModel:Vue 的 Composition API 或 Pinia store,负责状态管理和业务逻辑

举个例子,一个简单的组件选中逻辑:

// stores/editor.ts
import { defineStore } from 'pinia'
import { ref } from 'vue'

export const useEditorStore = defineStore('editor', () => {
  const selectedId = ref(null)
  const componentTree = ref([])

  function selectComponent(id: string) {
    selectedId.value = id
    // 这里可以触发其他副作用,比如高亮、显示属性面板
  }

  return { selectedId, componentTree, selectComponent }
})

在 View 层,只需要绑定这个状态:

<template>
  <div 
    v-for="node in componentTree" 
    :class="{ active: node.id === selectedId }"
    @click="store.selectComponent(node.id)"
  >
    {{ node.name }}
  </div>
</template>

你看,View 层只负责「展示」和「触发事件」,不关心数据怎么变。ViewModel 层管理状态和逻辑。Model 层就是纯粹的数据结构。各司其职,清清楚楚。

个人经验: 刚开始写编辑器时,别急着上复杂的设计模式。先把 MVVM 用明白,等遇到性能瓶颈或扩展需求时,再考虑引入观察者模式、命令模式等。过度设计也是坑。

3.5 本章小结

这一章我们做了三件事:

  1. 用 Vite 搭好了项目骨架
  2. 规划了清晰的目录结构
  3. 选择了 MVVM 作为核心架构模式

嗯,地基打好了。下一章咱们开始写真正的编辑器核心——渲染引擎。到时候你会看到,今天做的这些架构决策,会让我们写代码时有多顺畅。

记住一句话:好的架构不是设计出来的,是演化出来的。但前提是,你得先有一个不差的起点。