文件系统设计 - 开发文件系统 Store (上篇)

server/2024/9/26 1:00:21/

本节开始,我们将从最核心基础的文件系统进行设计实现,构建文件系统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类型定义设计实现了文件系统相关的接口定义,大家也可以根据目前文件系统的接口定义,尝试实现文件系统管理类。

如果大家在开发过程中有任何的问题欢迎下方留言评论,我将尽快为大家解答;加油!


http://www.ppmy.cn/server/122095.html

相关文章

Vue.js props 子组件可以从父组件接收数据(通过 props)并可以向父组件发送事件(通过 $emit)

父子组件之间可以通过事件和 props 进行通信&#xff0c;但通常是单向的&#xff1a;父组件向子组件传递数据&#xff08;props&#xff09;&#xff0c;子组件向父组件发送事件&#xff08;$emit&#xff09;。 方式 父组件传递数据给子组件: 使用 props。 子组件通知父组件…

【多线程】面试高频考点!JUC常见类的详细总结,建议收藏!

&#x1f490;个人主页&#xff1a;初晴~ &#x1f4da;相关专栏&#xff1a;多线程 / javaEE初阶 JUC是“Java Util Concurrency”的缩写&#xff0c;指的是Java并发工具包&#xff0c;它位于java.util.concurrent包及其子包中。JUC包提供了大量用于构建并发应用程序的工具和…

【计算机网络 - 基础问题】每日 3 题(十一)

✍个人博客&#xff1a;Pandaconda-CSDN博客 &#x1f4e3;专栏地址&#xff1a;http://t.csdnimg.cn/fYaBd &#x1f4da;专栏简介&#xff1a;在这个专栏中&#xff0c;我将会分享 C 面试中常见的面试题给大家~ ❤️如果有收获的话&#xff0c;欢迎点赞&#x1f44d;收藏&…

floodfill算法(1)

一&#xff1a;图像渲染 题目&#xff1a; 有一幅以m*n的二维数组表示的图画image&#xff0c;其中image[i][j]表示该图像的像素值大小 给出三个整数sr,sc,和color&#xff0c;从像素image[i][j]开始对图像进行上色填充&#xff0c;为了完成上色工作&#xff0c;从初始像素开…

docker启动mysql未读取my.cnf配置文件问题

描述 在做mysql主从复制配置两台mysql时&#xff0c;从节点的my.cnf配置为&#xff1a; [mysqld] datadir /usr/local/mysql/slave1/data character-set-server utf8 lower-case-table-names 1 # 主从复制-从机配置# 从服务器唯一 ID server-id 2 # 启用中继日志 relay-l…

Flink 性能优化的高频面试题及答案

目录 高频面试题及答案1. 如何通过调整并行度来优化 Flink 性能?2. 如何优化 Flink 的状态管理?3. 如何通过优化事件时间处理提高 Flink 性能?4. 如何通过调整网络缓冲区大小优化性能?5. 如何通过优化资源配置提升 Flink 性能?6. 如何通过使用自定义序列化器提高性能?7. …

Winform中使用MySQL数据库

1、创建项目并添加引用MySql.Data&#xff1b; 2、在App.config文件添加connectionString <?xml version"1.0" encoding"utf-8" ?> <configuration><startup> <supportedRuntime version"v4.0" sku".NETFramework,…

STM32基础学习笔记-ADC面试基础题6

第六章、ADC 常见问题 1、基本概念&#xff1a;什么是ADC &#xff1f;作用 &#xff1f;逐次逼近型 2、传感器本质 &#xff1f;传感器、电压、ADC数值转化 &#xff1f; 3、ADC的特征 &#xff1f; 转化时间、分辨率、精度、量化误差 &#xff1f; 4、ADC框图组成部分 &…