1. 前文回顾
在上一篇博文中介绍了 Vue.js 的计算属性和组件化开发基础,让我们做一个简单的回顾。
1.1 计算属性
通过上一篇博文,我们知道了当我们要对一些变量进行逻辑处理时,我们就可以使用计算属性。同时,我们还比较了计算属性、方法和侦听属性的相同点和不同点。
1.2 组件化开发基础
紧接着,我们了解了组件的概念,知道了组件在实际开发中所占的重要地位,了解了组件化开发的基础内容,接下来让我们更加深入地了解组件化开发吧!
2. 深入组件化开发
2.1 组件的注册
2.1.1 组件名
Vue.component('button-counter', { /* ... */ });
Vue.component
第一个参数就是组件的名字。我们给予的名字一般依赖于我们拿它做什么,我们需要的注意的是组件名的大小写,定义组件一般有这两种方式:
使用 kabab-case
Vue.component('button-counter', { /* ... */ });
当我们使用 kabab-case(短横线分割命名)定义一个组件时,我们使用它的时候也必须使用短横线引用,如:。
使用 PascalCase
Vue.component('ButtonCounter', { /* ... */ });
当我们使用 PascalCase(首字母大写命名)定义一个组件时,可以有两种方法,分别是: 和
。
2.1.2 全局注册
在上一篇博文中,我们尝试了用 Vue.component
的方式来创建组件,这是一种全局注册的方式:
Vue.component('button-counter', {
// ...
});
这种方式注册的组件可以使用在任何 Vue 实例中。
2.1.3 局部注册
还有一种注册组件的方法便是局部注册,在实际 Vue-Cli 工程项目中,代码看起来是这个样子的:
import ComponentA from './ComponentA.vue'
export default {
components: {
ComponentA
},
// ...
}
在 ES2015+ 语法中,在对象中放一个类似 ComponentA
的变量名其实是 ComponentA: ComponentA
的缩写,即这个变量名同时是:
-
用在模板中的自定义元素的名称
-
包含了这个组件选项的变量名
2.2 Prop
2.2.1 camelCase vs kabab-case
HTML 中的 attribute 名是大小写不敏感的,所以浏览器会把所有大写字符解释为小写字符。这意味着当你使用 DOM 中的模板时,camelCase (驼峰命名法) 的 prop 名需要使用其等价的 kebab-case (短横线分隔命名)命名:
Vue.component('blog-post', {
// 在 JavaScript 中是 camelCase 的
props: ['postTitle'],
template: '...'
})
<blog-post post-title="hello!"></blog-post>
2.2.2 Prop 的类型
在上面的例子中,我们学会了以字符串的形式列出向子组件传递的 Prop:
props: ['title', 'content', 'page', 'callback', 'flag' ]
但是,通常我们需要指定 prop 的类型,因此我们可以以对象的形式列出 prop,属性的名称和值分别是 prop 各自的名称和类型:
prop: {
title: String,
content: Object,
page: Number,
callback: Function,
flag: Boolean,
// Array、Promise...
}
当 prop 的类型不正确时,游览器的控制台会将遇到的错误报告给用户。
2.2.3 传递动态的 Prop
上面我们知道了如何传递一个静态的 prop,而传递动态的 prop 可以通过 v-bind
动态赋值,如:
<blog-post v-bind:title="post.title"></blog-post>
<blog-post
v-bind:title="post.title + ' by ' + post.author.name"
></blog-post>
注意:上面的例子都是传递字符串,那么我们如何传入一个数字、布尔值、对象呢?
<blog-post v-bind:page="3"></blog-post>
<blog-post v-bind:page="post.page"></blog-post>
同理,传入一个布尔值、数组也是需要用 v-bind
告诉 Vue 这是表达式,否则 Vue 会理解为字符串。
当我们传入一个对象:
<blog-post
v-bind:name="{
lastName: 'James',
firstName: 'Lebron'
}"
></blog-post>
<blog-post v-bind:author="post.author"></blog-post>
传入一个对象的所有属性:
<blog-post v-bind="post"></blog-post>
其中 post
中的所有属性都会被传递。
2.3 自定义事件
2.3.1 事件名
和 Prop 不一样,事件名不会不会进行大小写的转换。比如:
this.$emit('addUser');
则监听 addUser
事件的 kabab-case 名字事件是没用的:
<add-user-button v-on:add-user="do"></add-user-button>
因为 HTML 对大小写是不敏感的,所以 v-on:addUser
将会转化成 v-on:adduser
,这就导致了 addUser
没有被监听到。
2.3.2 .native
修饰符
当我们想在一个组件的根元素上直接监听一个原生事件,这时,我们救需要使用 v-on
的 .native
修饰符:
<base-input v-on:focus.native="onFocus"></base-input>
但是,如果这个 base-input 组件根元素不是 input 时,父组件的监听就失效了,并且不会产生任何报错,且
onFocus
处理函数不会被如期地被调用。
为了解决这个问题,Vue 提供了一个 $listeners
属性,它是一个对象,里面包含了作用在这个组件上的所有监听器。例如:
{
focus: function (event) { /* ... */ }
input: function (value) { /* ... */ },
}
2.3.3 .sync
修饰符
某些情况下,我们可能需要对一个 prop 进行“双向绑定”。但是,真正的双向绑定会带来维护上的问题,因为子组件可以修改父组件,且在父组件和子组件都没有明显的改动来源。
所以我们推荐以 update:myPropName
的模式触发事件取而代之。举个例子,在一个包含 title
prop 的假设的组件中,我们可以用以下方法表达对其赋新值的意图:
this.$emit('update:title', newTitle)
然后父组件可以监听 update
事件并根据需要更新一个本地的数据属性。例如:
v-bind:title="doc.title"
v-on:update:title="doc.title = $event"
为了方便起见,我们为这种模式提供一个缩写,即 .sync
修饰符:
v-bind:title.sync="doc.title"
注意带有 .sync
修饰符的 v-bind
不能和表达式一起使用 (例如 v-bind:title.sync=”doc.title + ‘!’”
是无效的)。取而代之的是,你只能提供你想要绑定的属性名,类似 v-model
。
当我们用一个对象同时设置多个 prop 的时候,也可以将这个 .sync
修饰符和 v-bind
配合使用:
v-bind.sync="doc"
这样会把 doc
对象中的每一个属性 (如 title
) 都作为一个独立的 prop 传进去,然后各自添加用于更新的 v-on
监听器。
将 v-bind.sync
用在一个字面量的对象上,例如 v-bind.sync=”{ title: doc.title }”
,是无法正常工作的,因为在解析一个像这样的复杂表达式的时候,有很多边缘情况需要考虑。
2.4 动态组件
什么是动态组件呢?比如我们页面中使用了一个多标签,并使用 is
来切换不同的组件:
v-bind:is="currentTabComponent"
当我们在这些组件中切换的时候,新的组件是会重新渲染的,然而假如我们想保留这些组件的状态,避免重复渲染导致的性能问题,那么我们就需要用到“动态组件”了,我们可以用一个 keep-alive 元素将其动态组件包裹起来。
这样,标签在被切换之后会被缓存起来了,当我们再切换回来就不会重新渲染了。
注意这个
要求被切换到的组件都有自己的名字,不论是通过组件的
name
选项还是通过局部/全局注册的。
3. 结尾
通过本篇内容我们更深入地了解了 Vue.js 的组件化开发,组件化开发带来的便利以及其的作用远不止这些例子所展示的,我们需要在实际的开发过程中深入体会组件化开发。
在刚刚结束的 2.4 动态组件 中,我们提到了利用动态组件实现标签中组件的缓存,那么我们将在下一篇内容中手把手教大家如何结合 Element UI 库与 Vue.js 动态组件实现标签的缓存,请期待。
参考资料:
评论