1.3 使用文件持久化数据

106 阅读2分钟

第一节说了,如果不做数据的持久化,服务重启后,我们之前做的所有操作就都失效了。

但怎么做持久化呢?最原始的方式当然是文件了。

新建.gitignore

根目录下新建一个.gitignore文件,把data文件夹(我们的数据文件user.json就放它下面)放进去,它不需要提交。

data

修改deno.jsonc文件

因为我们要写入和读取文件,所以要额外增加两个权限(--allow-write --allow-read)。

{
  "tasks": {
    "dev": "deno run --allow-net --allow-write --allow-read --watch src/main.ts"
  }
}

修改src/user.service.ts

class UserService {
  users: User[] = [
    { id: 1, author: "张三", age: 18 },
  ];
  
  constructor() {
    Deno.mkdir("data").catch(() => null);
  }

  async getAll(): Promise<User[]> {
    const usersStr = await Deno.readTextFile("data/user.json").catch(() =>
      null
    );
    if (usersStr) {
      return JSON.parse(usersStr);
    }
    return this.users;
  }
  async getUserById(id: number) {
    const users = await this.getAll();
    return users.find((user) => user.id === id);
  }

  private saveToFile(users: User[]) {
    return Deno.writeTextFile("data/user.json", JSON.stringify(users, null, 2));
  }

  async addUser(user: Omit<User, "id">) {
    const users = await this.getAll();
    const id = users.length + 1;
    const newUser = {
      ...user,
      id,
    };
    users.push(newUser);
    await this.saveToFile(users);
    return newUser;
  }

  async removeUser(id: number) {
    const users = await this.getAll();
    const newUsers = users.filter((user) => user.id !== id);
    await this.saveToFile(newUsers);
  }

  async updateUser(id: number, user: Omit<User, "id">) {
    const users = await this.getAll();
    const oldUser = users.find((u) => u.id === id);
    if (!oldUser) {
      throw new Error(`user not found`);
    }
    Object.assign(oldUser, user);
    await this.saveToFile(users);
  }
}

可以看到,重点是增加了一个方法:

 private saveToFile(users: User[]) {
    return Deno.writeTextFile("data/user.json", JSON.stringify(users, null, 2));
 }

getAll方法是读取这个文件内容,不存在时才降级为初始化的数据。

注意:增加、修改、删除都是在它的基础上,修改之后,再覆盖原文件。

因为读写文件我们用的异步的API,所以返回结果都是个Promise。

时刻记住一点,I/O是影响性能的一个关键点,尤其是同步I/O,更会阻塞进程。在使用JavaScript开发时,能用异步就用异步。

修改src/main.ts

正因为user.service.ts中每个方法都变成了异步的,main.ts上层调用时,也都需要把await加上。

router
  .get("/", (context) => {
    context.response.body = "hello world";
  })
  .get("/user", async (context) => {
    context.response.body = await userService.getAll();
  })
  .get("/user/:id", async (context) => {
    const id = Number(context.params.id);
    const user = await userService.getUserById(id);
    if (user) {
      context.response.body = user;
    } else {
      context.response.status = 404;
      context.response.body = "user not found";
    }
  })
  .post("/user", async (context) => {
    const result = context.request.body({
      type: "json",
    });
    const value = await result.value;
    const user = await userService.addUser(value);
    context.response.body = user;
  })
  .put("/user/:id", async (context) => {
    const id = Number(context.params.id);
    const result = context.request.body({
      type: "json",
    });
    const value = await result.value;
    try {
      await userService.updateUser(id, value);
      context.response.body = "update ok";
    } catch (e) {
      context.response.status = 400;
      context.response.body = e.message;
    }
  })
  .delete("/user/:id", (context) => {
    const id = Number(context.params.id);
    userService.removeUser(id);
    context.response.body = "delete ok";
  });

验证

当在F12控制台增加一条数据

fetch("/user", {
  method: "post",
  headers: { "content-type": "application/json" },
  body: JSON.stringify({ author: "李四", age: 10 }),
});

就能看到data文件夹下面生成了一个user.json文件,内容是:

[
  {
    "id": 1,
    "author": "张三",
    "age": 18
  },
  {
    "author": "李四",
    "age": 5,
    "id": 2
  }
]

同样的,修改或删除,都会引发这个文件的变化。

作业

思考下,如果使用localStorage来持久化我们的数据,该怎么修改呢?