本节开始,我们将从最核心基础的文件系统进行设计实现,构建文件系统Store
- 一个基础的响应式Store类
- 设计文件系统类接口
- 小结
一个基础的响应式Store类
从Vue3
开始,Vue响应式借助Proxy
重构后,整个响应式系统的应用变得非常的灵活,虽然目前业界依旧有 Pinia
等响应式管理库,但其本质依旧是在借助Vue的响应式API进行设计实现;所以很多时候其实并非需要第三方的响应式管理库,如果只是简单的响应式处理直接借助 Vue 的响应式API即可。
关于Vue3 的响应式实现细节,大家可以参考网上很多的Vue原理解析文章,或者大家有兴趣可以在下方留言,后面也可以出一期从0手写mini-vue3 源码的专栏
这里我们便借助 Vue 的响应式API封装一个具有响应式State管理的基类
import { reactive, UnwrapNestedRefs } from 'vue'export abstract class Store<T extends Record<string, unknown>> {state: UnwrapNestedRefs<T>;constructor(state: T) {this.state = reactive(state);}
}
设计文件系统类接口
web端的编辑器虽然可以借助 File System Access API
对本地文件进行操作,但是因其兼容性问题,目前Web编辑器的文件还是存在内存中的,将文件以树型结构在内存中进行存储维护,所以首先我们先明确文件系统的主要作用是: 管理内存中的文件树
; 这里的管理主要包括以下部分:
- 创建文件/文件夹
- 删除文件/文件夹
- 查找文件/文件夹
- 移动文件/文件夹
- 文件/文件夹重命名
- 文件写入
首先,我们需要先设计出文件在内存中进行管理的数据结构定义
// 定义文件的类型
export const enum FileType {File,Directory,
}// 文件的结构定义
export interface IFileSystemItem {filename: string; /* 文件名 */type: FileType.File; /* 文件类型 */code: string; /* 文件的代码 */ext: string; /* 文件的扩展名 */fullPath: string; /* 文件的完整路径 */status: number; /* 文件当前的状态: 用于记录当前对文件的操作 */cacheBuffer: string | null /** 文件内容缓冲区 */;language?: string; /* 代码语言 */readonly?: boolean; /* 文件是否只读 */visible?: boolean; /* 文件是否在文件树中显示 */
}// 目录的结构定义
export interface IDirectoryItem extends Record<string, unknown> {filename: string; /* 文件名 */type: FileType.Directory; /* 文件类型 */fullPath: string; /* 文件完整路径 */status: number; /* 文件夹当前状态 */readonly?: boolean; /* 文件夹是否只读 */visible?: boolean; /* 文件夹是否在文件树中可见 */children: (IFileSystemItem | IDirectoryItem)[]; /* 文件夹下的子文件 */
}
定义好文件系统在内存中的存储结构后,我们还需要定义文件系统的响应式 State
中的数据结构
export type FileSystemState = {/* 文件树 - 单根文件夹 */files: IDirectoryItem;/* 文件Map映射,方便快速的根据文件路径查找文件 */fileMap: Map<string, IFileSystemItem | IDirectoryItem>;
};
对于文件的操作,我们将通过文件的 status
字段进行记录,比如文件编辑、文件重命名等;那如何通过一个字段来记录多种状态呢? 这里我们采用二进制位Mask来处理,首先我们先定义几个基础的文件操作:
export const enum FileOperation {Editing = 0b00000001,Rename = 0b00000010,Delete = 0b00000100,Move = 0b00001000,Create = 0b00010000,
}
关于如何通过二进制位Mask来记录不同的状态,我们将在下篇文章中具体实现文件系统进行介绍
完成以上步骤之后,现在我们可以开始定义文件系统的接口描述了,文件系统Store
是一个继承自响应式基类的文件管理子类,处理响应式文件State
数据之外,还有相关的文件操作逻辑:
export interface FileSystemProvider {/* 响应式的文件数据 */state: FileSystemState;/*** 创建文件* @param path 文件路径 uri* @param content 文件内容* @param readonly 是否只读* @param visible 是否可见*/createFile(path: Uri,content: string,readonly?: boolean,visible?: boolean): IFileSystemItem;/*** 创建目录* @param path 目录 Uri* @param readonly 是否只读* @param visible 是否可见*/createDirectory(path: Uri,readonly?: boolean,visible?: boolean): IDirectoryItem;/*** 读取文件内容* @param path 文件路径 uri*/readFile(path: Uri | string): IFileSystemItem | IDirectoryItem | null;/*** 写入文件数据* @param path 文件路径 uri* @param content 文件内容* @returns 文件变更状态数据*/writeFile(path: Uri | string,content: string,isBuffer?: boolean): ChangeFileState | null;/*** 删除文件或目录* @param file 文件对象* @returns 文件变更状态数据*/delete(file: IFileSystemItem | IDirectoryItem): ChangeFileState | null;/*** 文件重命名* @param file 文件对象* @param newName 文件名* @returns 文件变更状态数据*/renameFile(file: IFileSystemItem, newName: string): ChangeFileState;/*** 文件夹重命名* @param folder 文件对象* @param newName 文件名* @returns 文件变更状态数据*/renameFolder(folder: IDirectoryItem, newName: string): ChangeFileState[];/*** 移动文件* @param sourcePath 源路径* @param targetPath 目标路径* @returns 文件变更状态数据数组*/moveFile: (sourcePath: Uri | string,targetPath: Uri | string) => ChangeFileState[] | null;/*** 为文件对象添加操作* @param file 文件对象* @param operator 操作*/addOperator: (file: IFileSystemItem | IDirectoryItem,operator: FileOperation) => void;/*** 为文件对象移除操作* @param file 文件对象* @param operator 操作*/removeOperator: (file: IFileSystemItem | IDirectoryItem,operator: FileOperation) => void;
}
小结
这一章我们正式开始组件库的开发,组件的开发我认为最重要的不是组件如何渲染,而是数据如何维护和管理;
从这章开始我们介绍的是整个编辑器组件的核心:文件系统
; 我们将按照面向接口编程的方式,逐步设计和实现文件系统的管理和操作。这一章节我们通过TS类型定义设计实现了文件系统相关的接口定义,大家也可以根据目前文件系统的接口定义,尝试实现文件系统管理类。
如果大家在开发过程中有任何的问题欢迎下方留言评论,我将尽快为大家解答;加油!