指南
基础概要
- 安装
- 介绍
- Vue 实例
- 模板语法
- computed 属性和 watcher
- class 和 style 绑定
- 根据条件进行渲染
- 列表渲染
- 事件处理
- 表单 input 绑定
- 组件基础
过渡 & 动画
- 进入、离开和列表的过渡
深入组件
- 组件注册
- props
- 自定义事件
- slot
- 动态组件和异步组件
- Handling Edge Cases
- 状态间的过渡
可重用 & 合成
- mixin
- 自定义指令
- render 函数 & jsx
- 插件
- 过滤器
工具
- 生产环境部署
- 单文件组件
- 单元测试
- TypeScript 支持
扩展升级
- 路由
- 状态管理
- 服务端渲染
内部原理
- 深入响应式原理
升级迁移
- 从 Vue 1.x 迁移
- 从 Vue Router 0.7.x 迁移
- 从 Vuex 0.6.x 迁移到 1.0
其他更多
- 对比其他框架
- 加入 Vue.js 社区
- 认识团队
组件基础
基本示例
这里是一个 Vue 组件示例:
// 定义一个新的组件,名称为 button-counter |
组件(component),是具有 name 名称的可复用 Vue 实例:当前示例中是 <button-counter>
。我们可以使用 new Vue
创建出一个 Vue 根实例,然后将这个组件作为其中的一个自定义元素(custom element):
<div id="components-demo"> |
new Vue({ el: '#components-demo' }) |
由于组件是可复用的 Vue 实例,它们接收的选项,和在 new Vue
时候的选项相同,例如 data
, computed
, watch
, methods
和生命周期钩子。唯一的例外是,类似 el
这样,根实例上特有(root-specific)的选项。
重复使用组件
可以根据需要,多次重复使用组件:
<div id="components-demo"> |
注意,当点击按钮时,每个按钮都维护彼此独立的 count
。这是因为每次使用组件时,都会创建出一个新的组件实例。
data
必须是一个函数
当我们定义 <button-counter>
组件时,你可能已经注意到,提供给组件定义对象的 data
,并不是如下所示的一个对象:
data: { |
相反,组件的 data
选项必须是一个函数,以便每个实例都可以维护「函数返回的数据对象」的彼此独立的数据副本:
data: function () { |
如果 Vue 没有遵循这个规定,点击其中一个按钮,会影响其他所有用到此 data 的组件实例,如下所示:
将组件组合在一起
通常都会将一个应用程序,组织为一个嵌套的组件树:
例如,你的组件可能有 header,sidebar 和 content 三个区域,每个组件都包含导航链接、博客文章等其他组件。
想要在模板中使用这些组件,就必须先进行注册,以便 Vue 能够找到它们。组件注册有两种方式:全局注册和局部注册。到目前为止,我们只通过 Vue.component
在全局注册组件:
Vue.component('my-component-name', { |
全局方式注册的组件,可以用于之后创建的所有(通过 new Vue
创建的)Vue 根实例,以及 Vue 实例组件树中所有子组件的内部。
现在,你仅需要知道组件注册的这些相关知识,等到你阅读完这个页面,并且能够很好适应这些内容,我们建议你稍后回到这里,继续深入阅读 组件注册 的完整指南。
使用 props 向子组件传递数据
前面我们提到过,创建一个博客文章组件。问题来了,如果无法向组件传递数据(例如向传入我们想要展示的文章的标题和内容),那这个组件也很难派上用场。props 就是为此而设计的。
props 是指注册在组件选项上的自定义属性。当一个值,被放置在 props 中,作为其中一个 prop,这个值就会成为组件实例上,一个可访问的属性。想要向我们的文章组件传递一个标题,我们需要在此组件接收 props 的 list 清单中将其置入,通过使用一个 props
来进行:
Vue.component('blog-post', { |
一个组件可以有很多 prop,并且默认情况下,任何类型的值都可以传递给 prop。在上面的 template 模板中,你可以看到我们可以在组件实例上访问到 title 这个值,就像我们访问 data
上的 title 一样。
在预先注册好一个 prop 属性之后,就可以将数据作为自定义属性传递给这个 prop 属性,如下所示:
<blog-post title="我的 Vue 旅程"></blog-post> |
然而,在一个应用程序中,你常常会将 data
中的 posts 设为一个数组:
new Vue({ |
然后,将每条数据渲染为一个组件:
<blog-post |
以上,你就会看到我们使用 v-bind
来动态地传递属性。对于预先不知道确切内容的渲染,这是很有用的功能,例如,当我们从一个 API 接口获取文章时。
现在,你仅需要知道 props 的这些相关知识,等到你阅读完整个页面,并且能够很好适应这些内容,我们建议你稍后回到这里,继续深入阅读 props 的完整指南。
单个根元素
在创建 <blog-post>
组件时,最终的模板中,不仅要包含标题:
<h3>{{ post.title }}</h3> |
至少,还需要包含文章的内容:
<h3>{{ post.title }}</h3> |
如果你试图在 template 模板中按照以上方式书写,Vue 将会显示一个错误,并解释为 every component must have a single root element(译注:每个组件都必须有一个根元素)。你可以通过为以上模板包裹一个父元素,来修复这个错误,例如:
<div class="blog-post"> |
使用 events 向父组件发送消息
在我们开发 <blog-post>
组件时,有些功能可能恰好与 props 相反,需要子组件反过来和父组件进行通信。例如,我们可能会决定添加能放大文章文本字号的辅助功能,而将页面的其余部分保留为默认大小:
在父组件中,我们可以通过在 data 中添加一个 postFontSize
属性来支持这种功能:
new Vue({ |
以便控制博客组件 template 模板中,所有博客文章的字号大小:
<div id="blog-posts-events-demo"> |
现在,我们在每篇文章的内容前面添加一个可以加大文本字号的按钮:
Vue.component('blog-post', { |
上面的示例,还有接下来的一些示例,都用到了 JavaScript 的 模板字面量(template literal),以便多行模板更加具备可读性。Internet Explorer (IE) 不支持此语法,因此如果你必须支持 IE,而又不想转译代码(例如,使用 Babel 或 TypeScript 进行转译),使用 新行转义(newline escapes) 替代模板字面量语法。
现在的问题是,这里的 button 无法实现这个功能:
<button> |
当我们点击 button 时,我们需要和父组件通信,告知它加大所有文章的文本字号。幸运的是,Vue 实例为我们提供了一个自定义事件(custom event)系统,来解决这个问题。想要向父组件发送事件,我们可以调用实例中内置的 $emit
方法,传递事件名称:
<button v-on:click="$emit('enlarge-text')"> |
然后在我们的博客文章组件上,我们可以通过 v-on
监听这个事件,就如同我们使用原生 DOM 事件一样:
<blog-post |
在 event 事件中发送一个值
有时,想在 event 事件中发送一个特定的值。例如,我们可能想要在 <blog-post>
组件自身内部,去控制放大文本字号的间隔。在这种情况下,我们可以使用 $emit
的第二个参数来提供字号间隔值:
<button v-on:click="$emit('enlarge-text', 0.1)"> |
然后,当我们在父实例中监听这个事件时,我们可以通过 $event
来访问这次发送事件的值:
<blog-post |
或者,如果事件处理函数是一个方法:
<blog-post |
然后,这个值会被传入到方法中,作为第一个参数:
methods: { |
在组件中使用 v-model
自定义事件(custom event),还可以用来创建出「实现 v-model
机制的自定义输入框(custom input)」。回顾前面章节中:
<input v-model="searchText"> |
等同于如下:
<input |
而用于一个组件时,v-model
则可以替换为如下:
<custom-input |
为了组件内部能够有效运行,组件内的 <input>
必须:
- 将
value
属性绑定到value
prop - 在
input
输入框中,在自定义的input
事件中,发送一个新的值
这里就是上面所描述的:
Vue.component('custom-input', { |
现在,我们的 custom-input 组件,应该可以实现 v-model
的完美运行:
<custom-input v-model="searchText"></custom-input> |
现在,你仅需要知道组件的自定义事件的这些相关知识,等到你阅读完整个页面,并且能够很好适应这些内容,我们建议你稍后回到这里,继续深入阅读 自定义事件(custom event) 的完整指南。
使用 slots 进行内容分发
如同在 HTML 元素中传入内容,我们也经常会向组件传入内容,就像这样:
<alert-box> |
会被渲染为如下结果:
幸运的是,通过 Vue 的 <slot>
自定义元素,可以非常简单的实现这个任务:
Vue.component('alert-box', { |
就像你看到的,我们只是将其插入到我们想要它在的位置 - 就像这样。我们已经实现了预期!
现在,你仅需要知道 slot(插槽) 的这些相关知识,等到你阅读完整个页面,并且能够很好适应这些内容,我们建议你稍后回到这里,继续深入阅读 slot(插槽) 的完整指南。
动态组件
有时,在组件之间进行动态切换非常有用,例如在标签式界面中:
通过向 Vue 的 <component>
元素传入 is
特性,可以实现以上效果:
<!-- Component changes when currentTabComponent changes --> |
在上面的示例中,currentTabComponent
也可以是以下之一:
- 已注册组件的注册名称(registered name)
- 或者是一个组件选项对象(options object)
在 fiddle 中查看 注册名称方式 的完整代码并体验效果,或者在 这个 fiddle 中查看 选项对象方式。
现在,你仅需要知道 动态组件 的这些相关知识,等到你阅读完整个页面,并且能够很好适应这些内容,我们建议你稍后回到这里,继续深入阅读 动态组件和异步组件 的完整指南。
DOM 模板解析注意事项
有些 HTML 元素,例如 <ul>
, <ol>
, <table>
和 <select>
这些元素,会对于出现在其内部的元素有所限制;而另一些 HTML 元素,例如 <li>
, <tr>
和 <option>
这些元素,只可以出现在前面那些元素的内部。
由于这种 HTML 运行机制,在这些受限制的元素内部使用组件,会导致一些问题。例如:
<table> |
自定义组件 <blog-post-row>
会被当作无效内容,提升到 table 元素之外,从而导致最终渲染输出后的错误。幸运的是,is
特性提供了一种解决方案:
<table> |
应当注意,在使用以下字符串模板之一的场景中,这些限制将不再适用:
- 字符串模板(例如
template: '...'
) - 单文件 (
.vue
) 组件 <script type="text/x-template">
现在,你仅需要知道 DOM 模板解析 的这些相关知识 - 实际上,Vue 基础指南部分已经结束。为此值得祝贺!虽然还有很多要学习的知识点,但是现在,我们推荐你在此休息一下,开始熟悉并享受使用 Vue 构建项目的乐趣。
如果你能够很好适应这些内容,并且已经完全消化这些知识点,我们建议你稍后回到这里,继续深入阅读 动态组件和异步组件 的完整指南,以及侧边栏中深入组件部分的其他页面。