【源码共读】第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!

相关推荐
用户214118326360212 小时前
首发!即梦 4.0 接口开发全攻略:AI 辅助零代码实现,开源 + Docker 部署,小白也能上手
前端
gnip14 小时前
链式调用和延迟执行
前端·javascript
SoaringHeart14 小时前
Flutter组件封装:页面点击事件拦截
前端·flutter
杨天天.14 小时前
小程序原生实现音频播放器,下一首上一首切换,拖动进度条等功能
前端·javascript·小程序·音视频
Dragon Wu14 小时前
React state在setInterval里未获取最新值的问题
前端·javascript·react.js·前端框架
Jinuss14 小时前
Vue3源码reactivity响应式篇之watch实现
前端·vue3
YU大宗师14 小时前
React面试题
前端·javascript·react.js
木兮xg14 小时前
react基础篇
前端·react.js·前端框架
ssshooter15 小时前
你知道怎么用 pnpm 临时给某个库打补丁吗?
前端·面试·npm
IT利刃出鞘15 小时前
HTML--最简的二级菜单页面
前端·html