假如我们需要开发一个文章列表页面,该页面通过 v-for 指定动态渲染列表项,为了复用,决定将列表项封装成一个组件。但是,列表项中显示的数据需要动态传递,不然 v-for 将迭代渲染多个一样的组件,这不符合需求。Vue 为了解决向组件传输数据,提供了 props 属性。
Prop 是你可以在组件上注册的一些自定义属性。当一个值传递给一个 prop 属性的时候,它就变成了那个组件实例的一个属性。为了给列表项组件传递一个标题,我们可以用一个 props 选项将其包含在该组件可接受的 prop 列表中,例如:
// 创建一个组件 Vue.component('article-item', { // 给组件定义一些 prop,用于调用组件的时候传值 props: ['title'], // 组件模板 template: '<div>{{ title }}</div>' })
一个组件默认可以拥有任意数量的 prop,任何值都可以传递给任何 prop。在上述模板中,你会发现我们能够在组件实例中访问这个值(即访问 title),就像访问 data 中的值一样。
该示例将演示文章列表页,自定义文章项组件以及动态传值,如下:
<html> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Vue</title> <!-- 使用 CDN 引入 Vue 库 --> <script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script> </head> <body> <div id="app"> <!-- v-bind:title="article.title" 表示将当前迭代的文章标题绑定到组件的title属性,传递给组件 --> <article-item v-for="article in articles" v-bind:key="article.id" v-bind:title="article.title"></article-item> </div> <script type="text/javascript"> // 定义文章相组件 Vue.component('article-item', { // 自定义 prop props: [ "title" ], template: '<div>{{title}}</div>' }); var app = new Vue({ el: "#app", data: { articles: [ { id:1, title:"Vue 入门" }, { id:2, title:"Vue 生命周期" }, { id:3, title:"Vue 内部实现原理" } ] } }); </script> </body> </html>
运行效果如下图:
了解了怎样传递字符串到自定义组件,当构建一个 <article-item> 组件时,你的模板最终会包含的东西远不止一个标题:
<div>{{title}}</div>
最起码,你会包含这篇文章的标题、文章浏览数量、发布时间等信息。例如:
<div>{{title}}</div> <div> <label>发布日期:{{date}}</label> <label>浏览量:{{count}}</label> </div>
然而如果你在模板中尝试这样写,Vue 会显示一个错误,并解释道 every component must have a single root element (每个组件必须只有一个根元素)。你可以将模板的内容包裹在一个父元素内,来修复这个问题,例如:
<div class="article_item"> <div>{{title}}</div> <div> <label>发布日期:{{date}}</label> <label>浏览量:{{count}}</label> </div> </div>
看起来当组件变得越来越复杂的时候,我们的文章项组件不只需要标题、发布日期、浏览量,还需要点赞票、反对票等等。如果为每个相关的信息都定义一个 prop 属性会变得很麻烦,就像这样:
<article-item v-for="article in articles" v-bind:key="article.id" v-bind:title="article.title" v-bind:date="article.date" v-bind:count="article.count"></article-item>
所以是时候重构一下 <article-item> 组件了,让它变成接受一个单独的 article 对象 prop 属性,例如:
<html> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Vue</title> <!-- 使用 CDN 引入 Vue 库 --> <script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script> </head> <body> <div id="app"> <article-item v-for="article in articles" v-bind:key="article.id" v-bind:article="article"></article-item> </div> <script type="text/javascript"> // 定义文章相组件 Vue.component('article-item', { // 自定义 prop props: [ "article" ], template: ` <div style="background:#F0F0F0;margin-bottom:10px;padding:10px;"> <div>{{article.title}}</div> <div style="font-size:14px;color:#555;"> <label>发布日期:{{article.date}}</label> <label>浏览量:{{article.count}}</label> </div> </div> ` }); var app = new Vue({ el: "#app", data: { articles: [ { id:1, title:"Vue 入门", date:"昨日", count:189 }, { id:2, title:"Vue 生命周期", date:"上周", count:320 }, { id:3, title:"Vue 内部实现原理", date:"2023年6月9日", count:102 } ] } }); </script> </body> </html>
运行效果如下:
注意:
(1)现在不论何时为 <article-item> 组件的 article 对象添加一个新的属性,它都会自动地在 <article-item> 内可用。
(2)上面 <article-item> 组件的模板使用了模板字符串来支持多行文本。但是,在 IE 下并没有被支持,所以如果你需要在不 (经过 Babel 或 TypeScript 之类的工具) 编译的情况下支持 IE,请使用折行转义字符取而代之。