Vue项目使用外部JSON文件的问题求助
Hey there! Let's break down your issues, fix that image display problem, and share some tips to make this setup way friendlier for your non-technical buddy.
1. Quick Note on the Fixed Duplicate Rendering Issue
You mentioned the duplicate rendering stopped after adjusting your JSON structure—chances are your initial JSON had repeated entries in the hero array, or the structure wasn't an array at all, causing the v-for to loop unexpectedly.
One small best practice to tweak here: instead of using :key="data" (which passes the entire object as a key), use a unique string/number from your JSON, like data.name. This helps Vue track elements more efficiently:
<div v-for="data in myJson.hero" :key="data.name">
2. Why Images Aren't Showing & How to Fix It
The Core Problem
When you use @/assets/... directly in your Vue component's template or script, Webpack automatically parses that path and replaces it with the actual bundled file path (like /img/telescope.abc123.jpg). But JSON files are treated as static text—Webpack won't process the @/assets/... string inside them, so the browser tries to load a file at that exact unparsed path, which doesn't exist.
3 Practical Solutions
Option A: Dynamically Resolve Paths in Your Vue Component
Modify your JSON to store just the image filename (e.g., "image": "telescope.jpg"), then use require() in your component to let Webpack process the path:
<script> import json from "../json/controller.json"; export default { name: "Home", data() { return { myJson: json, }; }, created() { // Process each hero item's image path this.myJson.hero.forEach(item => { item.image = require(`@/assets/background/${item.image}`); }); }, components: {}, methods: {}, }; </script>
If you want to keep the full relative path in your JSON, you can extract the filename instead:
this.myJson.hero.forEach(item => { const fileName = item.image.split('/').pop(); // Grabs "telescope.jpg" from the path item.image = require(`@/assets/background/${fileName}`); });
Option B: Move Images to the public Folder
Move your images to the public/background/ folder in your project root, then update the JSON path to an absolute URL:
"image": "/background/telescope.jpg"
Files in the public folder are copied directly to your bundled output without Webpack processing, so the browser can access them via the absolute path.
Option C: Use a Full CDN URL (If Hosting Images Remotely)
If you're storing images on a CDN, just paste the full URL in your JSON:
"image": "https://your-cdn-domain.com/background/telescope.jpg"
3. Friendlier Alternatives for Your Non-Technical Friend
Editing JSON files can still be intimidating for someone who's not comfortable with code. Here are some better approaches:
- Use Markdown for Content: Store text in
.mdfiles instead of JSON. Tools likevue-markdown-loaderlet you render Markdown directly in Vue components, and your friend can use simple formatting (bold, lists) without dealing with JSON syntax. - Build a Tiny Admin Page: Use Vue + a UI library like Element UI or Naive UI to create a simple form. Your friend can edit headings, upload images, and save changes directly from the browser—no code editing required. You can save the data back to JSON or a lightweight database like Firebase.
- Centralize Content with Pinia/Vuex: If multiple pages use dynamic content, use a state management library to load and process your JSON once, then share the data across components. This avoids repeating path-resolving code everywhere.
- Automate Image Uploads: Add a simple upload feature to your admin page. When your friend uploads an image, it gets saved to
publicor a CDN, and the path is automatically updated in your content file.
Your Original Code for Reference
Vue Component
<template> <div class="home"> <div v-for="data in myJson.hero" :key="data"> <v-card tile flat> <v-img height="885" gradient="to top left, rgba(100,115,201,.13), rgba(25,32,72,.7)" :src="data.image" > <template v-slot:placeholder> <v-row class="fill-height ma-0" align="center" justify="center"> <v-progress-circular indeterminate color="grey lighten-5" ></v-progress-circular> </v-row> </template> <v-row> <v-col cols="12"> <div class="centered"> <h2 class="display-2 text-center white--text"> {{ data.firstHeading }} </h2> <h1 data-aos="zoom-in" class="display-4 font-weight-bold text-center white--text" > {{ data.secondHeading }} </h1> <h3 class="subtitle-4 text-center white--text"> {{ data.subHeading }} </h3> <v-btn large class="btnCentered" outlined fab dark> <v-icon>mdi-arrow-down</v-icon> </v-btn> </div> </v-col> </v-row> </v-img> </v-card> </div> </div> </template> <script> import json from "../json/controller.json"; export default { name: "Home", data() { return { myJson: json, }; }, components: {}, methods: {}, }; </script> <style scoped> .centered { position: absolute; top: 45%; left: 50%; transform: translate(-50%, -50%); } .btnCentered { position: absolute; top: 250%; left: 50%; transform: translate(-50%, -50%); } </style>
Corrected JSON File
{ "hero": [{ "name": "heroImage", "alt": "telescopeImage", "image": "@/assets/background/telescope.jpg", "firstHeading": "HeroText", "secondHeading": "HeroText", "subHeading": "telescopeImage" }] }
内容的提问来源于stack exchange,提问作者Faziki

