Fetch data from local JSON file with Nuxt Pinia - vue.js

Is it possible to fetch a local .json. file using fetch()? I originally used the import method but the site's data doesn't get updated unless the page gets reloaded.
I tried doing this but it's not working:
stores/characters.ts
export const useCharactersStore = defineStore("characters", {
state: () => ({
characters: [],
}),
getters: {
getCharacters: (state) => {
return state.characters;
},
},
actions: {
fetchCharacters() {
fetch("../data.json")
.then((response) => response.json())
.then((data) => {
this.characters = data.characters;
});
},
},
});
app.vue
import { useCharactersStore } from "~/stores/characters";
const store = useCharactersStore();
onMounted(() => {
store.fetchCharacters();
});
Any help would be appreciated.

maybe a bit late but I have encountered the same problem migration from Nuxt 2 to Nuxt 3.
I'm certainly no expert on this, so if anyone finds a better way or if I'm totally wrong please let me know !
Whenever you import a json file in vue code they are imported as a module, that get's embedded within the code compilation on build (Vue Docs). Tu use json as a external file you need to place your json within the /public directory and use axios or fetch to load the file with a lifecyle hook.
This could be mounted() for options api or beforeMount()/onMounted() with composition api.
However some important annotations for this method.
If the json file you want to use in your app is not reactive, i.e. won't change, you should place this in the static folder of the nuxt app.
In your example you fetch '../data/...', this would imply the server knows the domain to look for. It can't call the route like this, you would have to give the full url if you put your json file in the static folder.
Set the baseUrl in the of your nuxt.config.ts, see docs for specifications.
Then you can access the static folder with your .env variables
--> $fe
Then in you data script you can access your json file
async getJson(some parameters){
const data = $fetch('your domain with the runtimeConfig composable').then((data)=>{ console.log(data)});
Sidenote you can also load the file from the server-side using fs.readFile
read more about this in this awesome post here

Related

Nuxt Generate dynamic routes from the store

I'm using nuxt.js and now need to generate my dynamic pages by running npm run generate. However my list items needed to make dynamic items are stored in the store, so I need to map over them somehow so the generate can make the routes for them
How can I access the store in my nuxt.config.js?
generate: {
dir: 'wwwroot', //override the default generation dir to create everything straight in wwwroot
routes() {
let vdc = this.$store.vdcServers.map(server => `/virtual-data-centres/${server.slug}`);
return Promise.all([vdc]).then(values => {
return values.join().split(',');
})
}
}
Output
ERROR Could not resolve routes
FATAL Cannot read properties of undefined (reading '$store')
To my knowledge, you cannot access the store from that place. Maybe with some hooks? I doubt.
Meanwhile, if you have your elements available in your store you should be able to find them back by making a quick axios call or like I think.
This kind of approach is totally fine
import axios from 'axios'
export default {
generate: {
routes(callback) {
axios
.get('https://my-api/users')
.then(res => {
const routes = res.data.map(user => {
return '/users/' + user.id
})
callback(null, routes)
})
.catch(callback)
}
}
}
It may be a bit of code duplication, meanwhile, it's still the simplest way to go.
Otherwise, you could try to persist it to some localStorage or any similar solution, to have it both in Vuex and during your generation.

Vue static assets are not accessible to a library

I am using a single file Vue component and import a face-api library. I want to use a function from that library, loadSsdMobilenetv1Model(url), which takes URL of folder, where the necessary files are located and loads them. The function however cannot fetch the files if I use #/assets/weights as url (# in Vue represents the src folder). I would like to be able to host the assets for. I'm able to read files from the assets folder folder with require('#/assets/file.json), but the library seems to need a static url.
What is the best solution in my situation? Maybe I'm missing some understanding.
Can I make it so that the assets folder is served and accessible?
Here's my component and the comments show some things I've tried:
<template>
<div>...stuff...</div>
</template>
<script>
import * as faceapi from 'face-api.js';
async function load() {
// example below: If I serve the files on a separate port with CORS allowed, the function loads files fine.
// const MODEL_URL = 'http://127.0.0.1:8081/weights/';
// example below: this does not work, but I would like this to work!
const MODEL_URL = '#/assets/weights';
// example below: also doesn't work, conscious of relative paths
// const MODEL_URL = '../assets/weights';
// example below: a file loads, but I can't just this unfortunately
// return require('#/assets/file.json')
return await faceapi.loadSsdMobilenetv1Model(MODEL_URL);
}
export default {
mounted() {
var promise = load();
promise.then((model) => {
this.model = model
}, (reject) => {
console.log(reject)
// alert(reject);
})
},
name: "Home",
data() {
return {
model: null
}
}
};
</script>
I'm not sure if it's relevant, but I set up the project with
vue create
and run the dev environment with
nmp run serve

How can I ensure this vue application doesn't exceed the recommended 244kb in production?

There is a vue file here that imports a json file that has about 9000 records in it.
How do I ensure that the json file is not compiled with the component?
A simple way would be to put the JSON file you want to access in the public folder (Webpack won't process anything in this folder). Then use AJAX to call the file at run time.
This will avoid expanding your app bundle, but Vue may still show that you're including a large resource. This approach would also allow you to split the data into smaller chunks and load them as needed in your app.
Here's an example, assuming the data file is located at /public/data/mydata.json. Also, I suggest using Axios to make the AJAX stuff easier.
import axios from 'axios'
export default {
name: 'Vue Component',
created() {
this.fetchData();
},
methods: {
fetchData() {
axios.get('/data/mydata.json').then(response => {
// do something with response.data
})
}
}
}
Use dynamic import. Like this:
import(
/* webpackChunkName: "my_json" */
'./src/my.json'
).then(({default: myJson}) => {
// do whatever you like here~
console.log(myJson);
});
doc:
https://webpack.js.org/guides/code-splitting/#dynamic-imports
If the json file is too big, you will still get the size exceeding warning.
But the json file would load async, so it would not cause any performance problem.
🔽 🔽 🔽 if you really don't want to see the warning, try this: 🔽 🔽 🔽
Use copy-webpack-plugin,it can copy your json file to dist folder, which means you can fire a XHR get request to load the json file, like this axios.get('/my.json').
By doing this, you can get the FULL control about when to load the file.

React-native: download and unzip large language file

A multilingual react-native app. Each language bundle is ~50MB. It doesn't make sense to include all of them in a bundle. So, what do I do about it?
I assume the right way to go here is to download the respective language files upon language selection.
What do I do with it next? Do I suppose to store it using AsyncStorage or what?
Briefly explaining, you will:
Store JSON as ZIP in Google Storage (save memory/bandwidth/time)
Unzip file to JSON (in RN)
Store JSON in AsyncStorage (in RN)
Retrieve from AsyncStorage (in RN)
[Dependencies Summary] You can do this, using these deps:
react-native
react-native-async-storage
rn-fetch-blob
react-native-zip-archive
Tip: Always store big language json in zip format (this can save up to 90% of size).
I made a quick test here: one 3.52MB json file, turned out a 26KB zipped file!
Let's consider that yours stored zip file, can be accessed by using a public url, eg: https://storage.googleapis.com/bucket/folder/lang-file.zip.
Install and link all above RN deps, it's required to get this working.
Import the deps
import RNFetchBlob from 'rn-fetch-blob';
import { unzip } from 'react-native-zip-archive';
import AsyncStorage from '#react-native-community/async-storage';
Download the file using rn-fetch-blob. This can be done using:
RNFetchBlob
.config({
// add this option that makes response data to be stored as a file,
// this is much more performant.
fileCache : true,
})
.fetch('GET', 'http://www.example.com/file/example.zip', {
//some headers ..
})
.then((res) => {
// the temp file path
console.log('The file saved to ', res.path())
// Unzip will be called here!
unzipDownloadFile(res.path(), (jsonFilePath) => {
// Let's store this json.
storeJSONtoAsyncStorage(jsonFilePath);
// Done!
// Now you can read the AsyncStorage everytime you need (using function bellow).
});
});
[function] Unzip the downloaded file, using react-native-zip-archive:
function unzipDownloadFile(target, cb) {
const targetPath = target;
const sourcePath = `${target}.json`;
const charset = 'UTF-8';
unzip(sourcePath, targetPath, charset)
.then((path) => {
console.log(`unzip completed at ${path}`)
return cb(path);
})
.catch((error) => {
console.error(error)
});
}
[function] Store JSON in AsyncStorage:
function storeJSONtoAsyncStorage (path) {
RNFetchBlob.fs.readFile(path, 'utf-8')
.then((data) => {
AsyncStorage.setItem('myJSON', data);
});
}
Retrieve JSON data from AsyncStorage (everytime you want):
AsyncStorage.getItem('myJSON', (err, json) => {
if (err) {
console.log(err);
} else {
const myJSON = JSON.parse(json);
// ... do what you need with you json lang file here...
}
})
That's enough to get dynamic json lang files working in React Native.
I'm using this approach to give a similar feature to my i18n'ed project.
Yes you are right to make the translation file downloadable.
You can store the downloaded file in the document directory of your app.
After that you can use a package to load the translations. For instance
https://github.com/fnando/i18n-js.
I would also suggest taking a look at the i18n library which is a standard tool for internationalisation in JavaScript.
Consider taking a look at this documentations page where you can find an option of loading a translation bundle or setting up a backend provider and hooking into it.
Also, to answer the storage question, if you do not plan on setting up a backend: AsyncStorage would be an appropriate place to store your key - translation text pairs.

Nuxt/Vuejs - How to create utils that have access to modules?

I am using asiox/vuejs to create a webpage. However I want to compartmentalize the code more. One example is I use axios to make requests to the backend, and the data in the response is commited into vuex.
this.$axios.get('events').then((response) => {
this.$store.commit('data/populate', response.data)
})
.catch((e) => {
console.error(e)
})
I want to write a util method for this, like this.$populate.events()
I have tried creating utils inside the plugins/ directory, but they dont have access to this.$axios or this.$store
Note that I have axios and vuex imported in nuxt.config.js
How can this be achieved?
If you need the function in the context, Vue instances and maybe even
in the Vuex store, you can use the inject function, which is the
second parameter of the plugins exported function.
Injecting content into Vue instances works similar to when doing this
in standard Vue apps. The $ will be prepended automatically to the
function.
Reference
export default ({ app, store }, inject) => {
inject("populate", () => {
app.$axios
.get("events")
.then(response => {
store.commit("data/populate", response.data);
})
.catch(e => {
console.error(e);
});
});
};
app variable is context property.
The root Vue instance options that includes all your plugins. For
example, when using axios, you can get access to $axios through
context.app.$axios.
Figured it out not 5 minutes after posting ...
Basically use this nuxt guide
And replace this with app in the method you'd like to move