1. 源码简介
官网简介:Easily load and persist config without having to think about where and how
翻译:轻松加载和保持配置,而不必考虑在哪里以及如何加载和保持。
其实就是方便加载和持久化用户配置。它会在用户配置目录下生成对应的json文件。
2. 使用
import Configstore from 'configstore';const packageJson = JSON.parse(fs.readFileSync('./package.json', 'utf8'));// 实例化配置存储实例const config = new Configstore(packageJson.name, {foo: 'bar'});// get方法获取配置console.log(config.get('foo')); //=> 'bar'// set方法设置配置config.set('awesome', true);console.log(config.get('awesome')); //=> true// 设置嵌套属性config.set('bar.baz', true);console.log(config.get('bar')); //=> {baz: true}// 删除属性config.delete('awesome');console.log(config.get('awesome')); //=> undefined
3. 读源码
源码大概100来行,源码链接:
这里用到了一些没怎么见过的依赖包,奇奇怪怪可可爱爱:
// 是fs模块的增强版,旨在规范跨不同平台和环境的行为,并使文件系统访问对错误更友好import fs from 'graceful-fs';// 获取 XDG 基本目录路径,xdgConfig 是用户特定配置文件的目录(这个包适用于Linux)import { xdgConfig } from 'xdg-basedir';// fs.writeFile这个API的扩展包,使得它的操作是原子性的,且允许设置所有权import writeFileAtomic from 'write-file-atomic';// 从嵌套对象中获取、设置或删除属性import dotProp from 'dot-prop';// 生成唯一的随机字符串import uniqueString from 'unique-string';
引入包之后就是一些全局配置了:
// 如果是linux,获取xdgConfig;// 否则 os.tmpdir() 操作系统默认的临时文件的目录,uniqueString() 唯一字符串// 总之就是获取目录const configDirectory = xdgConfig || path.join(os.tmpdir(), uniqueString());// error 提示信息const permissionError = 'You don\'t have access to this file.';// 这里两个配置都是关于读写文件权限,这里是用八进制表示,options可以参考node读写文件配置const mkdirOptions = {mode: 0o0700, recursive: true};const writeFileOptions = {mode: 0o0600};

Configstore 构造函数源码
export default class Configstore {constructor(id, defaults, options = {}) {// 设置文件前缀路径const pathPrefix = options.globalConfigPath ? path.join(id, 'config.json') : path.join('configstore', `${id}.json`);// 最终存储在本地的文件路径this._path = options.configPath || path.join(configDirectory, pathPrefix);// defaults的意思是创建Store的时候就赋值if (defaults) {// 对all属性赋值,会触发下面all属性的set方法this.all = {...defaults,...this.all};}}// all属性的get方法get all() {try {// 直接读取本地文件return JSON.parse(fs.readFileSync(this._path, 'utf8'));} catch (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属性的set方法set all(value) {try {/// 确保目录存在fs.mkdirSync(path.dirname(this._path), mkdirOptions);// 写入文件// 注意这里的JSON.stringify(value, undefined, '\t')// MDN链接:https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringifywriteFileAtomic.sync(this._path, JSON.stringify(value, undefined, '\t'), writeFileOptions);} catch (error) {if (error.code === 'EACCES') {error.message = `${error.message}\n${permissionError}\n`;}throw error;}}// all对象键的lengthget size() {return Object.keys(this.all || {}).length;}// configstore的get方法,其实就是传入key返回对应的值get(key) {return dotProp.get(this.all, key);}set(key, value) {const config = this.all;// 这里做了一个重载// 如果只传了一个参数(key),例如这个情况 config.set({foo: 'bar'})if (arguments.length === 1) {// 表示这个key是一个对象,循环这个对象赋值到config对象(也就是this.all)上for (const k of Object.keys(key)) {dotProp.set(config, k, key[k]);}} else {dotProp.set(config, key, value);}this.all = config;}// 判断有没有这个属性值has(key) {return dotProp.has(this.all, key);}// 删除键值delete(key) {const config = this.all;// 删除对应的键值dotProp.delete(config, key);this.all = config;}// 清空clear() {this.all = {};}// 获取文件路径pathget path() {return this._path;}}
4. 总结和收获
读完源码之后,我们会发现这是一个方便持久化和获取值的包,这里持久化的位置是在用户目录,所以它的使用场景可能是在服务端或者客户端(浏览器端没有读取文件的权限)。
收获:
- 通过触发对象 all 的 get( )和 set() ,来做一些额外的操作
- dot-prop 这个包能快速读取/设置/删除嵌套属性的值
- fs模块的替代包:graceful-fs
- fs.writeFile的扩展包:write-file-atomic
- JSON.stringfy() 第二第三个参数
