【源码共读】第10期 | configstore 存储

1. 前言

2. configstore是什么

configstore是一个简单的配置存储库,用于在Node.js应用程序中保存数据。它提供了一种简单的方式来读取、写入和管理配置数据,这些数据以JSON格式存储在磁盘上的文件中。

使用configstore有以下几个好处:

  1. 简化配置数据管理:使用configstore可以将配置数据以键值对的形式存储,以便更轻松地读取和修改配置。

  2. 方便的配置文件操作:configstore自动管理配置文件的存储路径,并提供了保存和读取配置文件的功能。

  3. 默认值和数据验证:configstore允许设置配置项的默认值,并在读取配置数据时使用该默认值。这是很有用的,当某个配置项的值尚未设置时,configstore可以提供一个合理的默认值,避免了因为配置数据缺失而导致的错误。

  4. 数据持久化:configstore将配置数据以JSON格式存储在磁盘上的文件中,从而确保数据的持久化。

  5. 轻量且易于使用:configstore是一个轻量级的模块,易于安装和使用。它具有简单的API,不需要额外的库或依赖项。

3. 源码解析

js 复制代码
import path from 'path'; // path 主要用处处理文件路径
import os from 'os'; // os 模块提供了一些基本的系统操作函数
import fs from 'graceful-fs'; // 文件系统模块 graceful-fs 是fs的替代品,进行了各种改进
import {xdgConfig} from 'xdg-basedir';// 跨平台的方法来获取常用的配置目录路径,如用户的配置目录和缓存目录。
import writeFileAtomic from 'write-file-atomic';// fs.writeFile的扩展,使其操作原子化,并允许您设置所有权。
import dotProp from 'dot-prop'; // dot-prop 用于获取对象的属性
import uniqueString from 'unique-string';// 生成一个唯一的随机字符串

const configDirectory = xdgConfig || path.join(os.tmpdir(), uniqueString()); // xdgConfig 获取配置文件夹路径,如果没有则使用临时文件夹路径 "C:\\Users\\admin\\.config"
const permissionError = 'You don\'t have access to this file.';
const mkdirOptions = {mode: 0o0700, recursive: true}; // 递归创建文件夹 0o0700 代表文件夹所有者可读可写可执行,其他人不可读不可写不可执行
const writeFileOptions = {mode: 0o0600}; // 创建文件  0o0600 代表文件所有者可读可写,其他人不可读不可写

/**
 * @param {string} id - The project ID.
 * @param {object} defaults - Default config.
 * @param {object} options - Options.
 * @param {boolean} options.globalConfigPath - Use a path within the user home directory.
 * @param {string} options.configPath - Use a custom path for the config file.
 */
export default class Configstore {
	constructor(id, defaults, options = {}) {
		// pathPrefix是一个路径前缀,如果options.globalConfigPath为true,则为id/config.json,否则为configstore/id.json
		const pathPrefix = options.globalConfigPath ?
			path.join(id, 'config.json') :
			path.join('configstore', `${id}.json`);
		// this._path是配置文件的路径 如果未指定options.configPath,则为configDirectory/pathPrefix
		// 例如:"C:\\Users\\admin\\.config\\" + "configstore\\configstore-test.json"
		this._path = options.configPath || path.join(configDirectory, pathPrefix);
		console.log('打印***this._path',this._path)
		// 如果defaults存在,则将defaults和this.all合并
		if (defaults) {
			this.all = {
				...defaults,
				...this.all
			};
		}
	}
	// all是一个getter/setter,用于获取/设置配置文件的内容
	get all() {
		try {
			return JSON.parse(fs.readFileSync(this._path, 'utf8'));
		} catch (error) {
			console.log('打印***err',error)
			// 如果文件夹不存在,则创建文件夹
			if (error.code === 'ENOENT') {
				return {};
			}

			// 提高权限错误的消息
			if (error.code === 'EACCES') {
				error.message = `${error.message}\n${permissionError}\n`;
			}

			// 如果遇到无效的JSON,则清空文件
			if (error.name === 'SyntaxError') {
				writeFileAtomic.sync(this._path, '', writeFileOptions);
				return {};
			}

			throw error;
		}
	}
	// all是一个getter/setter,用于获取/设置配置文件的内容
	set all(value) {
		try {
			// 确保文件夹存在,因为它可能已经被删除了
			fs.mkdirSync(path.dirname(this._path), mkdirOptions);
			// 写入文件
			writeFileAtomic.sync(this._path, JSON.stringify(value, undefined, '\t'), writeFileOptions);
		} catch (error) {
			// 提高权限错误的消息
			if (error.code === 'EACCES') {
				error.message = `${error.message}\n${permissionError}\n`;
			}

			throw error;
		}
	}
	// size是一个getter,用于获取配置文件的大小
	get size() {
		return Object.keys(this.all || {}).length;
	}
	// get是一个getter,用于获取配置文件的内容
	get(key) {
		return dotProp.get(this.all, key);
	}
	// set是一个setter,用于设置配置文件的内容
	set(key, value) {
		// 获取配置文件的内容
		const config = this.all;
		// 如果key是一个对象,则将key的所有属性设置到config中
		if (arguments.length === 1) {
			for (const k of Object.keys(key)) {
				dotProp.set(config, k, key[k]);
			}
		} else {
			// 否则将key和value设置到config中
			dotProp.set(config, key, value);
		}
		// 将config设置到配置文件中
		this.all = config;
	}
	// has是一个getter,用于判断配置文件是否存在key
	has(key) {
		return dotProp.has(this.all, key);
	}
	// delete是一个setter,用于删除配置文件的key
	delete(key) {
		const config = this.all;
		dotProp.delete(config, key);
		this.all = config;
	}
	// clear是一个setter,用于清空配置文件
	clear() {
		this.all = {};
	}
	// path是一个getter,用于获取配置文件的路径
	get path() {
		return this._path;
	}
}

整体流程如下:

  1. 首先,创建一个configstore实例,可以通过指定配置文件名和默认配置来初始化。

  2. 在应用程序中,可以使用configstore的API来读取、写入和修改配置数据。API包括读取配置项、写入配置项、删除配置项等。

  3. 在读取配置数据时,configstore会首先查找指定的配置项。如果不存在,则返回默认值(如果设置了)。如果没有设置默认值,则返回undefined。

  4. 在写入和修改配置数据时,configstore会将新的配置数据写入内部的配置对象中,并自动将数据保存到配置文件中。

  5. 配置文件存储在磁盘上的文件中,以JSON格式保存。在保存配置数据时,configstore将配置对象转换为JSON字符串,并将其写入文件中。

  6. 配置文件的存储路径由configstore自动管理,通常位于用户的home目录下的.config文件夹中。

流程图如下:

4. 总结

最后总结一下:

  • 学习了一些包工具如xdg-basedirdot-propwrite-file-atomic ,参考常用npm包使用
  • 存储的路径的学习
  • 使用getter、setter进行属性读写

每天一小点,加油 O^O!

相关推荐
江华森1 分钟前
从 fork 到协程:一次完整的操作系统并发编程实操
前端·程序员
四千岁3 分钟前
稀疏向量BM25Retriever不支持中文怎么办?jieba来帮忙
前端·javascript·后端
这是个栗子6 分钟前
【JS代码分析】前端鉴权基础:Token 的本地存储与状态重置实践
开发语言·前端·javascript
颜进强7 分钟前
14 - OpenSpec 老页面改造骨架:定位 + 增量 + 回归三件套
前端·后端·ai编程
做前端的娜娜子8 分钟前
async/await 错误处理:try...catch vs .catch() 完全指南
前端·面试·掘金·金石计划
半个落月12 分钟前
React 性能优化入门:用 memo 避免无关的子组件重复渲染
前端·react.js
北城笑笑20 分钟前
Server 18 ,Nginx + Vite 前端部署排错实战:从 `/api/gpt/chat` 404 到 9012 后端服务的完整定位过程
linux·运维·前端·nginx·ubuntu·vue
江华森23 分钟前
从抓包到嗅探:用 C 语言把《计算机网络》第 9-13 章全部跑一遍
前端
IT_陈寒41 分钟前
Vite打包给我挖的这个坑,差点搞崩我的项目
前端·人工智能·后端
恋猫de小郭1 小时前
Flutter A2UI 的正确用法,怎么把 AI 和动态 UI 结合有效生产
android·前端·flutter