Vue CLI 3 & Electron 构建应用动态读取 config.json
2025-03-19 15:53:58
Vue CLI 3 & Electron 构建后读取动态 config.json 文件
有没有遇到过这种情况:使用 Vue CLI 3 和 Electron 构建应用后,希望从一个 config.json
文件中读取配置,并且在应用运行时可以修改这个文件,让应用立即反映这些修改,而无需重新构建? 这篇文章,咱们就来聊聊怎么解决这个问题。
一、 问题在哪?
默认情况下,Webpack 在构建时会将 config.json
文件中的数据直接打包进 JavaScript 代码。这样一来,用户即便修改了 config.json
文件,应用读取的还是打包时的旧数据,没法动态更新。 你之前试过的把文件放在 assets
或 public
文件夹,然后用 import
或 require
,或者用 axios 读取,都会碰到这个问题。
二、 怎么解决?
核心思路是:让 config.json
文件不被 Webpack 打包,而是在运行时从文件系统直接读取。
2.1. 把 config.json
放在 Electron 的 resources
目录
-
目录结构调整:
不要把
config.json
放在 Vue 项目的src
或public
目录。 创建一个和src
平级的文件夹, 比如叫resources
(或者其他你喜欢的名字), 把config.json
文件放进去。my-project/ ├── ... ├── src/ ├── resources/ │ └── config.json ├── ...
这避免了被webpack处理.
-
修改
vue.config.js
(或创建):需要告诉 Electron-builder,在打包时把
resources
目录整个复制到应用的资源目录。 在vue.config.js
文件里加上类似下面的配置:// vue.config.js module.exports = { pluginOptions: { electronBuilder: { extraResources: [ { from: './resources', to: 'resources', filter: ['**/*'] } ] } } };
这个配置的意思是:把项目根目录下的
resources
文件夹(from: './resources'
)的内容,复制到 Electron 打包后的应用的resources
目录(to: 'resources'
)。filter
选项确保所有文件都被复制。
2.2. 使用 Node.js 的 fs
模块读取文件
-
在 Vue 组件中引入
fs
:因为 Electron 环境支持 Node.js 的 API,可以直接用
fs
模块来读取文件。// App.vue 或者其他需要读取配置的组件 <script lang="ts"> import { Component, Vue } from 'vue-property-decorator'; import * as fs from 'fs'; import * as path from 'path'; @Component export default class App extends Vue { brand: string = ''; printerIP: string = ''; getConfigPath(): string { //开发环境和生产环境获取路径方式不一样 const isDevelopment = process.env.NODE_ENV === 'development' if (isDevelopment) { return path.join(__dirname, '../../resources/config.json'); }else{ //path.join(__dirname,'../../../') 是错误的写法,不同系统上的结果可能不同. return path.join(process.resourcesPath, 'resources/config.json'); } } loadConfig() { try{ const configPath = this.getConfigPath(); const rawData:string = fs.readFileSync(configPath, 'utf-8'); const config = JSON.parse(rawData); this.brand = config.brand; this.printerIP = config.printerIP; fs.watchFile(configPath, (curr, prev) => { if (curr.mtime > prev.mtime) { // 文件有更新,重新加载 this.loadConfig(); } }); }catch(e){ //错误处理,可以记录日志 console.error("Failed to load config.json:",e) } } mounted() { this.loadConfig(); } } </script>
-
使用
fs.readFileSync
读取文件:
- 获取
config.json
的完整路径. 这里注意, 生产环境中,config.json
位于应用程序的resources
文件夹. 这里的关键是根据环境构建不同的文件读取路径.
- 使用fs.readFileSync
同步读取文件内容。注意,用同步方法简单, 但是大文件可能造成阻塞.
- 使用fs.watchFile
监听文件的修改。这样, 修改config.json
就能立即应用, 不用重启软件.
-
解析 JSON:
使用
JSON.parse()
把读取到的文件内容(字符串)转换为 JavaScript 对象。
2.3 进阶: 使用异步读取 和 封装配置读取
同步读取可能阻塞 UI 线程。 大的配置文件,可以改用异步读取。
//App.vue (<script lang="ts">)
//... 引入fs, path 和前面的例子相同
async loadConfigAsync() {
try {
const configPath = this.getConfigPath();
// 使用异步读取
const rawData = await fs.promises.readFile(configPath, 'utf-8');
const config = JSON.parse(rawData);
this.brand = config.brand;
this.printerIP = config.printerIP;
//使用 fs.watch 更好, 但为了演示promises,这里用setTimeout轮询
setTimeout(() => this.loadConfigAsync(),5000) //5秒检查一次.
} catch (error) {
// 处理错误
console.error("读取config出错", error)
}
}
getConfigPath(): string {
//开发环境和生产环境获取路径方式不一样
const isDevelopment = process.env.NODE_ENV === 'development'
if (isDevelopment) {
return path.join(__dirname, '../../resources/config.json');
}else{
return path.join(process.resourcesPath, 'resources/config.json');
}
}
可以将配置相关的逻辑提取到单独的模块, 实现更好的代码组织:
// configManager.ts
import * as fs from 'fs';
import * as path from 'path';
let configCache:any = null;
function getConfigPath(): string {
//开发环境和生产环境获取路径方式不一样
const isDevelopment = process.env.NODE_ENV === 'development'
if (isDevelopment) {
return path.join(__dirname, '../../resources/config.json');
}else{
return path.join(process.resourcesPath, 'resources/config.json');
}
}
export async function loadConfig():Promise<any> {
try {
const configPath:string = getConfigPath()
const data:string = await fs.promises.readFile(configPath, 'utf-8');
configCache = JSON.parse(data);
fs.watchFile(configPath, (curr, prev) => {
if (curr.mtime > prev.mtime) {
fs.promises.readFile(configPath, 'utf-8')
.then( newData=> configCache = JSON.parse(newData))
.catch(err => console.error("重载配置出错", err));
}
});
return configCache;
}catch(error){
console.error("读取配置失败:", error)
return null
}
}
export function getConfig(){
return configCache;
}
//App.vue
import {loadConfig, getConfig} from './configManager'
//...
async mounted() {
await loadConfig()
const config = getConfig();
if(config){
this.brand = config.brand;
this.printerIP = config.printerIP;
}
}
2.4 安全建议
对配置文件内容进行校验。 用户可能错误编辑导致应用异常。 加入类型判断,缺失值赋予默认值等措施,提高程序的健壮性。
三、 总结一下
通过把 config.json
文件放到 Electron 的 resources
目录,并使用 Node.js 的 fs
模块读取,就成功实现了在 Vue CLI 3 和 Electron 构建的应用中读取动态配置的需求。 而且文件变化会触发更新,很方便!