9. router配置中通过设置props向组件传参

442 阅读1分钟

在组件中使用 $route 会使之与其对应路由形成高度耦合,从而使组件只能在某些特定的 URL 上使用,限制了其灵活性。

1. 使用 props 将组件和路由解耦:

取代与 $route 的耦合

const User = {
  template: '<div>User {{ $route.params.id }}</div>'
}
const router = new VueRouter({
  routes: [{ path: '/user/:id', component: User }]
})

通过 props 解耦

const User = {
  props: ['id'],
  template: '<div>User {{ id }}</div>'
}
const router = new VueRouter({
  routes: [
    { path: '/user/:id', component: User, props: true },

    // 对于包含命名视图的路由,你必须分别为每个命名视图添加 `props` 选项:
    {
      path: '/user/:id',
      components: { default: User, sidebar: Sidebar },
      props: { default: true, sidebar: false }
    }
  ]
})

1.gif

<!DOCTYPE html>
<html>
  <head>
    <title>vue-router</title>
    <script src="https://cdn.jsdelivr.net/npm/vue@2.7.8/dist/vue.js"></script>
    <script src="https://unpkg.com/vue-router@3.1.3/dist/vue-router.js"></script>
  </head>
  <body>
    <div id="app">
      <router-link to="/user/lxl">default</router-link>
      <router-link to="/user/long">sidebar</router-link>

      <router-view></router-view>
      <router-view name="sidebar"></router-view>
    </div>
    <script>
      const Sidebar = {
        template: `<div class="Sidebar">
                <h2>Sidebar {{ id }}</h2>
              </div>`
      };
      const User = {
        props: ["id"],
        template: "<div>User {{ id }}</div>"
      };
      const router = new VueRouter({
        routes: [
          // { path: "/user/:id", component: User, props: true },
          // 对于包含命名视图的路由,你必须分别为每个命名视图添加 `props` 选项:
          {
            path: "/user/:id",
            components: { default: User, sidebar: Sidebar },
            props: { default: true, sidebar: false }
          }
        ]
      });
      const app = new Vue({
        router,
        el: "#app"
      }).$mount("#app");
    </script>
  </body>
</html>

这样就可以在任何地方使用该组件,使得该组件更易于重用和测试

2. props设置为布尔值

如果 props 被设置为 trueroute.params 将会被设置为组件的props属性

3. props设置为对象

如果 props 是一个对象,它会被按原样设置为组件的props属性。当 props 是静态的时候有用。

const Promotion = {
    props: ["newsletterPopup"],
    template: `<div class="Promotion">
            <h2>Promotion {{ newsletterPopup }}</h2>
          </div>`
};
const router = new VueRouter({
  routes: [
    {
      path: '/promotion/from-newsletter',
      component: Promotion,
      props: { newsletterPopup: false }
    }
  ]
})

1.gif

4. props设置为函数

可以创建一个函数返回 props。这样便可以将参数转换成另一种类型,将静态值与基于路由的值结合等等。

const router = new VueRouter({
  routes: [
    {
      path: '/search',
      component: SearchUser,
      props: route => ({ query: route.query.q })
    }
  ]
})

URL /search?q=vue 会将 {query: 'vue'} 作为组件的props属性传递给 SearchUser 组件。

1.gif

<!DOCTYPE html>
<html>
  <head>
    <title>vue-router</title>
    <script src="https://cdn.jsdelivr.net/npm/vue@2.7.8/dist/vue.js"></script>
    <script src="https://unpkg.com/vue-router@3.1.3/dist/vue-router.js"></script>
  </head>
  <body>
    <div id="app">
      <router-link to="/search?q=vue">search</router-link>
      <router-view></router-view>
    </div>
    <script>
      const SearchUser = {
        props: ["queryQ"],
        template: `<div class="SearchUser">
                <h2>SearchUser {{ queryQ }}</h2>
              </div>`
      };
      const router = new VueRouter({
        routes: [
          {
            path: "/search",
            component: SearchUser,
            props: route => ({ queryQ: route.query.q })
          }
        ]
      });
      const app = new Vue({
        router,
        el: "#app"
      }).$mount("#app");
    </script>
  </body>
</html>

请尽可能保持 props 函数为无状态的,因为它只会在路由发生变化时起作用。如果你需要状态来定义 props,请使用包装组件,这样 Vue 才可以对状态变化做出反应。

5. 例子

1.gif

<!DOCTYPE html>
<html>
  <head>
    <script src="https://cdn.jsdelivr.net/npm/vue@2.7.8/dist/vue.js"></script>
    <script src="https://unpkg.com/vue-router@3.1.3/dist/vue-router.js"></script>
  </head>
  <body>
    <div id="app"></div>
  </body>
  <script>
    const Hello = {
      props: ["name"],
      template: `<div class="Hello">
                <h2>Hello {{ name }}</h2>
              </div>`
    };
    function dynamicPropsFn(route) {
      const now = new Date();
      return {
        name: now.getFullYear() + parseInt(route.params.years) + "!"
      };
    }

    const router = new VueRouter({
      mode: "history",
      // base: __dirname,
      routes: [
        { path: "/", component: Hello }, // No props, no nothing
        { path: "/hello/:name", component: Hello, props: true }, // Pass route.params to props
        { path: "/static", component: Hello, props: { name: "world" } }, // static values
        { path: "/dynamic/:years", component: Hello, props: dynamicPropsFn }, // custom logic for mapping between route and props
        { path: "/attrs", component: Hello, props: { name: "attrs" } }
      ]
    });

    new Vue({
      router,
      template: `
    <div id="app">
      <h1>Route props</h1>
      <ul>
        <li><router-link to="/">/</router-link></li>
        <li><router-link to="/hello/you">/hello/you</router-link></li>
        <li><router-link to="/static">/static</router-link></li>
        <li><router-link to="/dynamic/1">/dynamic/1</router-link></li>
        <li><router-link to="/attrs">/attrs</router-link></li>
      </ul>
      <router-view class="view" foo="123"></router-view>
    </div>
  `
    }).$mount("#app");
  </script>
</html>