【问题标题】:How to dynamically change content of component with JSON?如何使用 JSON 动态更改组件的内容?
【发布时间】:2020-04-06 07:26:40
【问题描述】:

我正在使用 Vue CLI 3 创建我的设计组合。我的网站架构非常简单。我有一个主页、关于页面、工作页面和几个单独的项目页面:

  • 首页
    • 关于
    • 工作
      • 项目
      • 项目
      • 项目

工作页面由几个链接组成,这些链接可以点击进入各个项目页面。 工作组件设置如下:

<template>
  <div>
    <projectLink v-for="data in projectLinkJson" />
  </div>
</template>

<script>
import projectLink from '@/components/projectLink.vue'
import json from '@/json/projectLink.json'

export default {
  name: 'work',
  data(){
    return{
      projectLinkJson: json
    }
  },
  components: {
    projectLink
  }
}
</script>

如您所见,我正在导入 JSON 以动态呈现内容。 接下来,可以在下面的代码块中看到 projectLink 组件。在这个组件中,我将一个名为 projectName 的参数传递给 &lt;router-link&gt;

<template>
  <router-link :to="{ name: 'projectDetails', params: { name: projectName }}">
      <h1>{{ title }}</h1>
  </router-link>
</template>

<script>
export default {
  name: 'projectLink',
  props: {
    title: String,
    projectName: String
  }
}
</script>

我的 routes.js 文件是这样设置的:

const routes = [
   { path: '/', component: home },
   { path: '/about', component: about },
   { path: '/work', component: work },
   {
     path: "/work/:name",
     name: "projectDetails",
     props: true,
     component: projectDetails
   },
];

我的 JSON 是这样的:

  {
    "0": {
      "title": "test",
      "projectName": "test"
    }
  }

最后,我的 projectDetails 组件是我遇到此问题的组件:

<template>
    <div>
      <div
        v-for="(data,index) in projectDetailsJson" v-if="index <= 1">
            <h1>{{ data.title }}</h1>
            <p>{{ data.description }}</p>
      </div>
    </div>
</template>

<script>
import json from '@/json/projectDetails.json'

export default {
  name: 'projectDetails',
  data(){
    return{
      projectDetailsJson: json
    }
  },
  props: {
    description: String,
    title: String
  }
}
</script>

我已成功路由到我想要的 URL,即 /project/'name'。我想使用 projectDetails 组件作为我每个单独项目页面的框架。但是我如何动态地做到这一点?我想从 JSON 文件中检索数据并根据传递给 URL 的名称从数组中显示正确的对象。我不想迭代并在页面上显示所有数组。我只想展示一个项目。

【问题讨论】:

    标签: json vue.js components vue-router routerlink


    【解决方案1】:

    为了大家,更进一步。您将如何仅显示与当前 URL 匹配的 projectLinks?因此,如果我有三种不同的 JSON 项目类型:设计、代码、动作。如果 URL 中包含动作,我如何过滤我的 projectLink 组件以仅显示那些具有匹配的设计、代码或动作的 JSON 值的组件。本质上我只是想过滤。

    【讨论】:

      【解决方案2】:

      快速解决方案:

      projectDetails.vue

      <template>
          <div>
              <div>
                  <h1>{{ projectDetails.title }}</h1>
                  <p>{{ projectDetails.description }}</p>
              </div>
          </div>
      </template>
      
      <script>
          import json from '@/json/projectDetails.json';
      
          export default {
              name: 'projectDetails',
              props: {
                  name: String,
              },
              data() {
                  return {
                      projectDetails: Object.values(json).find(project => project.title === this.name),
                  };
              },
          };
      </script>
      
      

      在我看来,更好的解决方案:

      我不明白您将项目数据保存在 2 个单独的 JSON 文件中。在编译期间,这两个文件都保存到生成的 JavaScript 文件中。将这些数据保存在 1 个文件中不是更好吗?您不必在一处使用所有数据。第二件事,如果您有项目列表,那么您可以使用可选段进行路由,并根据段是否具有值,显示特定项目的列表或数据。 Then you load project data only in one place, and when one project is selected, pass its data to the data rendering component of this project.您无需在其他任何地方加载此 JSON 文件。

      routes.js

      import home from '@/components/home.vue';
      import about from '@/components/about.vue';
      import work from '@/components/work.vue';
      
      const routes = [
          {path: '/', name: 'home', component: home},
          {path: '/about', name: 'about', component: about},
          {path: '/work/:name?', name: 'work', component: work, props: true},
      ];
      
      export default routes;
      

      work.vue

      <template>
          <div>
              <project-details v-if="currentProject" :project="currentProject"/>
              <projectLink v-else 
                           v-for="project in projects"
                           v-bind="project"
                           v-bind:key="project.projectName"
              />
          </div>
      </template>
      
      <script>
          import projectLink from './projectLink';
          import projectDetails from './projectDetails';
          import json from '@/json/projectLink.json';
      
          export default {
              name: 'work',
              props: {
                  name: String,
              },
              data() {
                  return {
                      projects: Object.values(json),
                  };
              },
              computed: {
                  currentProject() {
                      if (this.name) {
                          return this.projects.find(
                              project => project.projectName === this.name,
                          );
                      }
                  },
              },
              components: {
                  projectLink,
                  projectDetails,
              },
          };
      </script>
      

      projectDetails.vue

      <template>
          <div>
              <div>
                  <h1>{{ project.title }}</h1>
                  <p>{{ project.description }}</p>
              </div>
          </div>
      </template>
      
      <script>
      
          export default {
              name: 'projectDetails',
              props: {
                  project: Object,
              },
          };
      </script>
      

      projectLink.vue(只改了一行)

      <router-link v-if="projectName" :to="{ name: 'work', params: { name: projectName }}">
      

      一个完整的工作示例:

      Vue.component("navigation", {
        template: "#navigation"
      });
      
      const Projects = {
        template: "#projects",
        props: ["projects"]
      };
      const Project = {
        template: "#project",
        props: ["project"]
      };
      const HomePage = {
        template: "#home"
      };
      const AboutPage = {
        template: "#about"
      };
      const WorkPage = {
        data() {
          return {
            projects: [{
                slug: "foo",
                name: "Foo",
                desc: "Fus Ro Dah"
              },
              {
                slug: "bar",
                name: "Bar",
                desc: "Lorem Ipsum"
              }
            ]
          };
        },
        props: {
          slug: String
        },
        template: "#work",
        components: {
          Projects,
          Project
        },
        computed: {
          currentProject() {
            if (this.slug) {
              return this.projects.find(project => project.slug === this.slug);
            }
          }
        }
      };
      
      const router = new VueRouter({
        routes: [{
            path: "/",
            name: "home",
            component: HomePage
          },
          {
            path: "/about",
            name: "about",
            component: AboutPage
          },
          {
            path: "/work/:slug?",
            name: "work",
            component: WorkPage,
            props: true
          }
        ]
      });
      
      new Vue({
        router,
        template: "#base"
      }).$mount("#app");
      ul.nav {
        list-style-type: none;
        margin: 0;
        padding: 0;
        overflow: hidden;
        background-color: #333;
      }
      
      ul.nav>li {
        float: left;
      }
      
      ul.nav>li>a {
        display: block;
        color: white;
        text-align: center;
        padding: 14px 16px;
        text-decoration: none;
      }
      
      ul.nav>li>a:hover {
        background-color: #111;
      }
      <script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.6.10/vue.min.js"></script>
      <script src="https://cdnjs.cloudflare.com/ajax/libs/vue-router/3.1.3/vue-router.min.js"></script>
      
      <div id="app"></div>
      
      <script type="text/x-template" id="base">
        <div id="app">
          <div>
            <navigation></navigation>
            <router-view></router-view>
          </div>
        </div>
      </script>
      
      <script type="text/x-template" id="navigation">
        <ul class="nav" id="navigation">
          <li>
            <router-link :to="{name: 'home'}">Home</router-link>
          </li>
          <li>
            <router-link :to="{name: 'about'}">About</router-link>
          </li>
          <li>
            <router-link :to="{name: 'work'}">Work</router-link>
          </li>
        </ul>
      </script>
      
      <script type="text/x-template" id="home">
        <div id="home">This is Home Page</div>
      </script>
      
      <script type="text/x-template" id="about">
        <div id="about">This is About Page</div>
      </script>
      
      <script type="text/x-template" id="work">
        <div id="work">
          <project v-if="currentProject" :project="currentProject"></project>
          <projects v-else :projects="projects"></projects>
        </div>
      </script>
      
      <script type="text/x-template" id="projects">
        <div id="projects">
          <ul>
            <li v-for="project in projects" :key="project.slug">
              <router-link :to="{name: 'work', params:{ slug: project.slug}}">{{project.name}}</router-link>
            </li>
          </ul>
        </div>
      </script>
      
      <script type="text/x-template" id="project">
        <div id="project">
          <h2>{{project.name}}</h2>
          <p>{{project.desc}}</p>
        </div>
      </script>

      【讨论】:

      • 甘德,感谢您深思熟虑的答案。您将我的 JSON 合并到一个文件中非常重要。这两种解决方案都适用于我的情况,但我会选择将所有数据保存在一个文件中。非常感谢!
      • 甘德,更进一步。您将如何仅显示与当前 URL 匹配的 projectLinks?因此,如果我有三种不同的 JSON 项目类型:设计、代码、动作。如果 URL 中包含动作,我如何过滤我的 projectLink 组件以仅显示那些具有匹配的设计、代码或动作的 JSON 值的组件。本质上我只是想过滤。
      • @AustinBranham 我认为您应该提出一个新问题(新线程),并以您当前的代码为例。还有你试图实现新目标的代码。一年过去了,我的知识变了,我想把它作为一个新问题来解决,而不是更新旧问题,特别是因为它已经解决并接受了。
      【解决方案3】:

      如果我理解正确,您想保留一个父组件作为您所有页面的布局吗?

      如果我一直理解正确的话,你一定要用vuerouter的children属性

      https://router.vuejs.org/guide/essentials/nested-routes.html

      import layout from 'layout';
      
      const projectRoute = {
          path: '/project',
          component: Layout, // Load your layout
          redirect: '/project/list',
          name: 'Project',
          children: [
              {
                  path: "list", // here the path become /project/list
                  component: () => import('@/views/project/List'), // load your components
                  name: "List of project",
              },
              {
                  path: "detail/:id",
                  component: () => import('@/views/project/Detail'), 
                  name: "Detail of project",
              }
          ],
      };
      

      所以你可以创建你的布局并添加你想要的一切,这将在所有子组件上可用,你可以使用$emit$refs$props 等...

      +

      您可以创建一个文件 routes/index.js 并创建文件夹 routes/modules 。在其中,您可以添加您的routes/modules/project.js 并在routes/index.js 中加载模块

      import Vue from 'vue';
      import VueRouter from 'vue-router';
      
      Vue.use(VueRouter);
      
      import projectRoutes from "./modules/project";
      
      const routes = [
          projectRoutes,
          {
              // other routes.... 
          },
      ]
      
      
      export default new VueRouter({
          routes,
          mode: 'history',
          history: true,
      });    
      

      @查看相同的文档:https://router.vuejs.org/guide/essentials/nested-routes.html

      最后只需要在layout上做处理,用props分配detailproject list的值即可;并使用上面描述的过滤方法

      希望我已经理解了您的要求,如果不是这样,请告诉我,

      再见

      编辑:这是一个非常好的架构,包含 vue、vuex 和 vuerouter。也许会启发你 https://github.com/tuandm/laravue/tree/master/resources/js

      【讨论】:

        【解决方案4】:

        到目前为止做得很好,奥斯汀!你非常接近这个工作。有几种不同的方法可以将 JSON 文件中的正确数据解析到 projectDetails 组件中,但我将仅演示我喜欢的方法。

        首先,您将需要一些普通的 JS 来搜索您的 JSON 文件并只返回您想要的行。我会这样做作为一种方法,因为数据不会改变或要求组件重新渲染。所以,在你的道具之后,我会添加这样的东西:

        methods: {
          findProject(projectName) {
            return Object.values(json).find(project => project.title === projectName)
          }
        }
        

        请注意,这将返回与项目名称匹配的第一个项目。如果您有项目名称完全相同的项目,这将不起作用。

        接下来,您只需更新projectDetailsJson 的默认值即可调用此方法并传递路由的项目名称。使用以下内容更新数据:

        data() {
          return {
            projectDetailsJson: this.findProject(this.$route.params.name)
          }
        }
        

        如果这不起作用,我们可能需要在 created 生命周期挂钩中设置 projectDetailsJson,但请先尝试上述代码。

        【讨论】:

        • 科林,非常感谢您的帮助!我已经尝试了上面的代码,但它对我不起作用。我了解您在那里所做的事情,只是不足以尝试改变自己周围的事物以使其发挥作用。为了使该方法正常工作,我的 JSON 文件的结构应该如何?此外,我在控制台中只收到一个警告。这可能是有贡献的吗?当我在工作页面上并单击以访问项目页面时,控制台说:[vue-router] missing param for named route "projectDetails": Expected "name" to be defined。
        • 乐于助人。我整理了一个简单的 json find 演示,向您展示 JSON 的结构:repl.it/@ColinUlin/DelayedGregariousAutoresponder
        • 请注意,我在示例中使用“require”而不是“import”。这仅仅是由于 repl.it 的限制,所以请忽略它。它和你的 import json from ... 做同样的事情
        • 该错误似乎也确实相关。您能否将 console.log(this.$route.params) 放在您的 findProject 方法中的返回上方并告诉我您的控制台中显示了什么?
        • 所以,我放了一个 v-if 语句来纠正这个错误。该 console.log 返回项目的正确名称。数据的结构也正确,对象内的对象。当我使用 vue-devtools 时,我可以看到该组件正在根据名称返回正确的数据。但似乎它没有正确填充字段中的数据。也许我的道具有问题?我就是想不通。此外,当我在创建的生命周期挂钩中运行 console.log(projectName) 时(并且我传入 projectName 函数,它返回 undefined。
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2019-11-08
        • 2018-05-17
        • 2014-03-04
        • 1970-01-01
        • 1970-01-01
        • 2014-06-02
        • 2010-12-05
        相关资源
        最近更新 更多