• 【源码】第10期|configstore 持久化配置存储


    1. 准备

    github.com/yeoman/conf…
    github1s.com/yeoman/conf…

    1.1 学习目的

    ● 了解configstore是的什么,其作用和使用场景

    1.2 准备工作

    git clone https://github.com/yeoman/configstore.git
    接着读index.js
    
    • 1
    • 2

    2.初识configstore

    Easily load and persist config without having to think about where and
    how 轻松加载和持久化配置,而无需考虑位置和方式

    2.1 下载

    npm install configstore
    
    • 1

    2.2 使用

    import Configstore from 'configstore';
    
    //读取package.json文件
    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
    
    
    
    2.3 看看官网给的APIConfigstore(packageName,default?,options?)
    
    packageName 包名 type:String
    default 默认配置 type:Object
    options type:Object
    
    ● 属性实例/方法构造函数
    
    .set(ket,value)  //设置一个项目
    .set(object) // 设置多个项目/对象
    .get(key) //获取
    .has(key) //检查是否存在
    .delete(key) //删除
    .clear() //清除所有
    .size // 获取项目数量
    .path //获取配置文件的路径,可用于向用户显示配置文件所在的位置,或者更好的打开它
    .all //将所有配置作为对象获取或将当前配置替换为对象:
    
    config.all = {
        hellol:'word
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48

    3.源码阅读

    import path from 'path'; // path 主要用处处理文件路径(菜鸟教程)[https://www.runoob.com/nodejs/nodejs-path-module.html]
    import os from 'os'; // os 模块提供了一些基本的系统操作函数
    import fs from 'graceful-fs'; // 文件系统模块(菜鸟教程)[https://www.runoob.com/nodejs/nodejs-fs.html]  graceful-fs 是fs的替代品,进行了各种改进
    import {xdgConfig} from 'xdg-basedir';// 获取路径,该软件包适用于Linux。 您不应在macOS或Windows上使用XDG。
    import writeFileAtomic from 'write-file-atomic';// fs.writeFile的扩展,使其操作原子化,并允许您设置所有权。
    import dotProp from 'dot-prop'; // 帮助快速访问 使用点路径从嵌套对象中获取,设置或者删除属性
    import uniqueString from 'unique-string';// 生成一个唯一的随机字符串
    
    
    ///os.tmpdir() 返回操作系统的默认临时文件夹
    // 如果是linux,获取xdgConfig;
    // 否则 os.tmpdir() 操作系统默认的临时文件的目录,uniqueString() 唯一字符串
    // 获取目录
    const configDirectory = xdgConfig || path.join(os.tmpdir(), uniqueString()); // 定义一下默认值 大概就是获取一下本地的操作路径吧
    const permissionError = 'You don\'t have access to this file.'; // 定义没有权限的提示语 没有访问文件的权限
    const mkdirOptions = {mode: 0o0700, recursive: true}; // 定义常量默认值
    const writeFileOptions = {mode: 0o0600}; // 定义常量默认值 写入文件
    
    export default class Configstore {
    	constructor(id, defaults, options = {}) {
    		// path.join 作用是连接路径
    		// 检查options 是否有 全局默认的路径
    		// 设置文件前缀路径:
    		// 如果有 就创建一个  以id 为目录的,config.json 文件 `${id}/config.json`
    		// 如果没有 就创建一个  以configstore 为目录的,`${id}.json`文件 `configstore/${id}.json`
    		const pathPrefix = options.globalConfigPath ?
    			path.join(id, 'config.json') :
    			path.join('configstore', `${id}.json`);
    
    		// 最终能够找到存储在本地文件的路径
    		// options.configPath 可以理解为默认配置文件中的路径
    		this._path = options.configPath || path.join(configDirectory, pathPrefix);
    		//defaults 表示默认配置文件的内容吧
    		if (defaults) {
    			// 解构赋值,合并传入的内容和原有的内容 会触发下面all属性的set方法
    			this.all = {
    				...defaults,
    				...this.all
    			};
    		}
    	}
    	/**
    	 * all 的 get方法
    	 */
    	get all() {
    		try {
    			//读取中path路径的本地文件
    			return JSON.parse(fs.readFileSync(this._path, 'utf8'));
    		} catch (error) {
    			// Create directory if it doesn't exist
    			//如果不存在,创建目录,返回空对象
    			if (error.code === 'ENOENT') {
    				return {};
    			}
    
    			// Improve the message of permission errors
    			//如果没权限 啥也不返回,赋值错误信息
    			if (error.code === 'EACCES') {
    				//permissionError 上方定义的常量 没有访问文件的权限
    				error.message = `${error.message}\n${permissionError}\n`;
    			}
    
    			// Empty the file if it encounters invalid JSON
    			//如果文件是无效的json,清空文件
    			if (error.name === 'SyntaxError') {
    				// writeFileOptions 上方定义的常量 写入文件
    				writeFileAtomic.sync(this._path, '', writeFileOptions);
    				return {};
    			}
    			// 抛出错误
    			throw error;
    		}
    	}
    	//all 的 set方法
    	set all(value) {
    		try {
    			// Make sure the folder exists as it could have been deleted in the meantime
    			//确保该文件夹存在,因为它可能同时被删除
    
    			/**
    			 * fs.mkdirSync 创建文件 fs.mkdirSync(path, [mode])
    			 * path  将创建的目录路径
    			 * mode  目录权限(读写权限),默认0o777 八进制表示的,一共4位:
    			 *  浏览器上 0o0700 == 0o777
    			 * */
    			/**
    			 * path.dirname 返回路径中代表文件夹的部分
    			 * JSON.stringify(value, undefined, '\t')
    			 * 使用空格缩进(菜鸟教程)[https://www.runoob.com/js/javascript-json-stringify.html]
    			 */
    			fs.mkdirSync(path.dirname(this._path), mkdirOptions);
    			// 写入文件
    			writeFileAtomic.sync(this._path, JSON.stringify(value, undefined, '\t'), writeFileOptions);
    		} catch (error) {
    			// Improve the message of permission errors
    			if (error.code === 'EACCES') {
    				error.message = `${error.message}\n${permissionError}\n`;
    			}
    
    			throw error;
    		}
    	}
    	//属性 size
    	// 为size属性定义了getter方法,通过 “实例.size”的方式读取size属性;
    	get size() {
    		// 获取配置文件对象,并且获取所有键的数组,然后返回长度
    		return Object.keys(this.all || {}).length;
    	}
    	// configstore的get方法,其实就是传入key返回对应的值
    	get(key) {
    		// 使用点路径从嵌套对象中获取
    		return dotProp.get(this.all, key);
    	}
    
    	set(key, value) {
    		// 读取配置对象给config
    		const config = this.all;
    		 // 如果参数长度是1的话,那么只传了一个参数key,表示一个对象
      		// 那么将这个对象所有的键和值,给配置对象设置上 循环这个对象赋值到config对象(也就是this.all)上
    		if (arguments.length === 1) {
    			for (const k of Object.keys(key)) {
    				// 使用点路径从嵌套对象中设置
    				dotProp.set(config, k, key[k]);
    			}
    		} else {
    			 // 普通的设置键和值 如果只传了一个参数(key),例如这个情况 config.set({foo: 'bar'})
    			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;
    	}
    	// 清空配置文件内容,通过给all属性设置值为空对象,将调用all的set方法,往文件里面写内容
    	clear() {
    		this.all = {};
    	}
    	// 属性path,获取文件路径path
    	// 为path属性定义了getter方法,通过 “实例.path”的方式读取path属性。
    	get path() {
    		return this._path;
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152

    4. 总结收获

    1. graceful-fsnode模块fs的增强版,
    2. xdg-basedir用于获取XDG Base Directory路径。
    3. write-file-atomic这是nodefs.writeFile的一个扩展,它使其操作成为原子性的,并允许您设置所有权(文件的uid/gid)
    4. dot-prop 帮助快速访问 使用点路径从嵌套对象中获取,设置或者删除属性

    5. Configstore类

    1. 构造函数主要做的是根据传入的id,设置文件的存储路径_path
    2. 如果有默认的存取内容defaults,就去创建对应文件,写入默认内容;不存在默认内容时,文件暂时不会创建
    3. 对应的实例方法set、get、delete、has内部都是调用dot-prop这个包的相关方法,只不过就是在set时可以传入单个对象参数,设置多个key/value
    4. Configstore类的核心主要是围绕实例属性all的处理,设置了all的存值函数和取值函数
    5. get all取值函数主要做的是读取对应_path的内容,如果路径不存在或者文件内容不符合json格式,抛出对应的错误
    6. set all存值函数主要是创建文件,写入内容,如果无写入权限,抛出对应的错误
  • 相关阅读:
    Ubuntu升级自带的Python3版本
    webpack构建01-vue项目之 手动webpack打包
    Intel汇编-在字符串中搜索一个字符
    Scala (一) --------- Scala 入门
    【剑指offer】Java中数组、字符串的长度获取区别 length、length()、size()
    Kong Learning
    大前端时代的乱流:带你了解最全面的 Flutter Web
    DAY3-深度学习100例-卷积神经网络(CNN)服装图像分类
    模仿dcloudio/uni-preset-vue 自定义基于uni-app的小程序框架
    C++ 使用c++类模板实现动态数组-可实现自定义数据类型存储
  • 原文地址:https://blog.csdn.net/rbgg_mp/article/details/127905490