Vue.js - 将JSON对象写入本地文件

29

有一段时间我开始学习Vue.js,不久之后开始了一个更大的项目,但没有考虑到Javascript与本地文件系统交互的受限性。

我通过vue-cli设置了该项目,所以我必须通过npm start启动网站。

该项目包括一个JSON文件的可视化编辑器。当我想要实现保存按钮时,我意识到将JSON文件写入/保存(可能在未来读取)到我的本机是一项相当困难的任务。

我已经尝试过使用node的fs库,认为它会起作用,因为它是由node启动的。 我还尝试了几个外部库,例如write-json-file npm库。

我已经到了无计可施的地步,愿意尝试任何必要的方法使其工作。


你打算使用服务器端还是客户端生成JSON并写入本地? - yue you
我认为如果使用浏览器环境,你只能创建一个Blob来下载或写入本地存储或IndexDB。如果有帮助的话,我可以分享一个我做过的例子给你。但是根据你的描述,你想要实现类似于VSCode或Atom的东西。对于你的用例,你可以尝试看看Electron。 - yue you
一个下载按钮似乎是可行的选择,如果不是太复杂的话 :) 我知道还有其他替代方案,但这更多是针对我的大学教育项目而非真实场景。如果能提供一个例子就更好了。 - Phero
以下是发布的内容。如果有任何问题,请随时告诉我。 - yue you
我没想到能这么快得到答案,明天我会试一下。无论如何非常感谢,看起来这可能是我们长期寻找的解决方案 :) - Phero
显示剩余4条评论
2个回答

59

有三种方法可以做到这一点。

  1. 写入本地存储

  2. 创建Blob并触发下载事件

  3. 将其包装成Electron应用程序,并使用node fs模块保存文件

我可以在这里为这三种情况展示一个示例

index.html

<!DOCTYPE html>
<html lang="en" xmlns:v-on="http://www.w3.org/1999/xhtml">
<head>
    <meta charset="UTF-8">
    <title>Vue test</title>
    <script src="https://cdn.jsdelivr.net/npm/vue@2.5.13/dist/vue.js"></script>
</head>
<body>
<div id="vue-app">
    <form>
        <input type="text" v-model="name"/>{{name}}<br/>
        <input type="text" v-model="last"/>{{last}}<br/>
        <input type="text" v-model="index"/>{{index}}<br/>
        <select v-model="grade">
            <option>5</option>
            <option>6</option>
            <option>7</option>
            <option>8</option>
            <option>9</option>
            <option>10</option>
        </select>
        {{grade}}
        <button type="button" v-on:click="add()">Add To Table</button>
        <button type="button" v-on:click="saveFile()">saveFile</button>
    </form>
    <table border="1">
        <thead><td>Name</td><td>Last Name</td><td>Index</td><td>Grade</td></thead>
        <tbody>
        <tr v-for="x in arr">
            <td>{{x.first}}</td>
            <td>{{x.lastn}}</td>
            <td>{{x.index}}</td>
            <td>{{x.grade}}</td>
        </tr>
        </tbody>
    </table>
</div>
<script src="test.js"></script>
</body>
</html>

test.js (写入本地存储)

new Vue ({
  el: '#vue-app',
  data: {
      name: '',
      last: '',
      index: 0,
      grade: 0,
      arr: []
  },

  methods: {
      add: function (e) {
          this.arr.push({first: this.name, lastn: this.last, index: this.index, grade: this.grade});
          console.log(1);
      },
      saveFile: function() {
        const data = JSON.stringify(this.arr)
        window.localStorage.setItem('arr', data);
        console.log(JSON.parse(window.localStorage.getItem('arr')))
  }
});

创建 Blob 实例并触发事件以进行下载

仅更改 saveFile 函数。

saveFile: function() {
    const data = JSON.stringify(this.arr)
    const blob = new Blob([data], {type: 'text/plain'})
    const e = document.createEvent('MouseEvents'),
    a = document.createElement('a');
    a.download = "test.json";
    a.href = window.URL.createObjectURL(blob);
    a.dataset.downloadurl = ['text/json', a.download, a.href].join(':');
    e.initEvent('click', true, false, window, 0, 0, 0, 0, 0, false, false, false, false, 0, null);
    a.dispatchEvent(e);
}

将其封装成Electron应用程序,并使用node fs模块保存文件。

更改saveFile函数。

saveFile: function() {
    const data = JSON.stringify(this.arr)
    const fs = require('fs');
    try { fs.writeFileSync('myfile.txt', data, 'utf-8'); }
    catch(e) { alert('Failed to save the file !'); }
}

然后使用Electron将其包装

electron ./index.html


很高兴能帮到你。如果可以的话,请接受我的答案。 - yue you
或许有点追求严谨,但为什么要费心去创建一个链接元素并在其上触发点击事件,当直接触发下载同样容易。解决方案似乎非常脆弱。 - Hybrid web dev
@Hybridwebdev,你也可以使用这个库来保存文件。https://github.com/eligrey/FileSaver.js/edit/master/src/FileSaver.js。它也是使用a.href和click事件。或者你可以使用下载API,但Edge不支持。https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/downloads/download - yue you
请问如何使用Electron进行封装?我对Electron没有什么了解。我想写一个CRUD代码生成器,类似于Yii2的Gii,但是我不知道如何在Vue和JS中创建目录或生成文件到本地项目。 - zia
2
我认为,仅仅为了让用户下载某些东西而制作一个电子应用程序似乎有些过度设计。 - Grumbunks
@Grumbunks 同意。有时候产品团队需要接受某些技术的限制,并为其设计一个更好的替代方案。 - yue you

1

这是我在Vue项目中编辑JSON文件的方法。在这种情况下,如果您运行该文件,它将创建一个新的data.json文件,并将Price添加到每个JSON对象中:

const fs = require("fs");

let cars = [
  {
    Name: "chevrolet chevelle malibu",
    Miles_per_Gallon: 18,
    Cylinders: 8,
    Displacement: 307,
    Horsepower: 130,
    Weight_in_lbs: 3504,
    Acceleration: 12,
    Year: "1970-01-01",
    Origin: "USA"
  },
  {
    Name: "buick skylark 320",
    Miles_per_Gallon: 15,
    Cylinders: 8,
    Displacement: 350,
    Horsepower: 165,
    Weight_in_lbs: 3693,
    Acceleration: 11.5,
    Year: "1970-01-01",
    Origin: "USA"
  }
];
cars.forEach(car => {
  car.price = 12000;
});

let data = JSON.stringify(cars);
fs.writeFileSync("data.json", data);

6
我收到了一个错误:“fs.writeFileSync不是一个函数”。 - ikreb

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接