# 组件化的定义和使用
# 组件基础
组件是可复用的 Vue 实例,且带有一个名字,它们与 new Vue 接收相同的选项,例如 data、computed、watch、methods 以及生命周期钩子等。
# 基本示例
// 定义一个名为 button-counter 的新组件 Vue.component('button-counter', { data: function () { return { count: 0 } },
template: '
<button v-on:click="count++">You clicked me {{ count }} times.</button>
' })
# 组件的复用
组件可以进行任意次数的复用,每使用一次组件,就会有一个它的新实例被创建,每一个组件实例都有自己的作用域。
一个组件的 data 选项必须是一个函数,通过闭包返回数据,保证了数据不会被全局变量污染,因此每个实例可以维护一份被返回对象的独立的拷贝,注意如果是复杂数据类型,依然保持浅拷贝的特性。
# 组件的组织
通常一个应用会以一个嵌套的组件树的形式来组织,组件中又包含了子组件,为了能在模板中使用,这些组件必须先注册。
这里有两种组件的注册类型:全局注册和局部注册。全局组件是通过 Vue.component 注册的。
# 通过 Prop 向子组件传递数据
Prop 是你可以在组件上注册的一些自定义 attribute,当一个值传递给一个 prop attribute 的时候,它就变成了那个组件实例的一个 property
Vue.component('blog-post', { props: ['title'], template: '
<h3>{{ title }}</h3>
' })
# 单个根元素
每个组件必须有且只有一个根元素
,通常将模板的内容包裹在一个父元素内
# 监听子组件事件
- 使用事件抛出一个值
有的时候用一个事件来抛出一个特定的值是非常有用的。
例如我们可能想让<blog-post>
组件决定它的文办要放大多少。这时可以使用$emit
的第二个参数来提供这个值,父组件在监听这个事件的时候,通过$event
访问到被抛出的这个值,或者将这个事件处理函数放在方法里,通过参数来传递
<button v-on:click="$emit('enlarge-text', 0.1)">Enlarge text</button>
<blog-post ... v-on:enlarge-text="postFontSize += $event"></blog-post>
- 在组件上使用 v-model
自定义事件也可用于创建支持 v-model 的自定义输入组件
<input v-model="searchText" />
等价于:
<input v-bind:value="searchText" v-on:input="searchText = $event.target.value" />
写成组件时:
Vue.component('custom-input', { props: ['value'], template: `
<input v-bind:value="value" v-on:input="$emit('input', $event.target.value)" />
` })
这里有两点,1.将 value 属性绑定到 value 的 prop 上,2.在 input 事件被触发时,将新的值通过自定义的 input 事件抛出。
# 通过插槽分发内容
<alert-box>Something bad happened.</alert-box>
Vue.component("alert-box", {
template: `
<div class="demo-alert-box">
<strong>Error!</strong>
<slot></slot>
</div>
`
});
和 HTML 元素一样,我们经常需要向一个组件传递内容
如你所见,我们只要在需要的地方加入插槽就行了——就这么简单!
其中自定义组件<alert-box>
标签中的内容就填充在<slot>
标签中,能被正常渲染到了
# 动态组件
通过在 Vue 的 component 元素上加一个特殊的is
attribute 来实现
# 组件注册
# 组件名
在注册组件的时候,我们始终需要给他一个名字,这个组件名就是 Vue.component 的第一个参数。
组件名写法:一般使用 kebab-case
Vue.component("my-component-name", {
/* ... */
});
# 全局注册
Vue.component("my-component-name", {
// ... 选项 ...
});
# 局部注册
var ComponentA = {
/* ... */
};
var ComponentB = {
/* ... */
};
new Vue({
el: "#app",
components: {
ComponentA,
ComponentB
}
});
如果你通过 Babel 和 webpack 使用 ES2015 模块,那么代码看起来更像:
import ComponentA from "./ComponentA.vue";
export default {
components: {
ComponentA
}
// ...
};
# 模块系统
- 在模块系统中局部注册
推荐创建一个 component 目录,并把乜咯组件放置在其各自的文件中,然后在局部注册前导入每个想使用的组件
// ConponentB.Vue
import ComponentA from "./ComponentA";
import ComponentC from "./ComponentC";
export default {
components: {
ComponentA,
ComponentC
}
// ...
};
- 基础组件的自动化全局注册
如果使用了 webpack,那么就可使用request.context
只全局注册一些非常通用的基础组件。
//src/main.js
import Vue from "vue";
import upperFirst from "lodash/upperFirst";
import camelCase from "lodash/camelCase";
const requireComponent = require.context(
// 其组件目录的相对路径
"./components",
// 是否查询其子目录
false,
// 匹配基础组件文件名的正则表达式
/[A-Z]\w+\.(vue|js)$/
);
requireComponent.keys().forEach((fileName) => {
// 获取组件配置
const componentConfig = requireComponent(fileName);
// 获取组件的 PascalCase 命名
const componentName = upperFirst(
camelCase(
// 获取和目录深度无关的文件名
fileName
.split("/")
.pop()
.replace(/\.\w+$/, "")
)
);
// 全局注册组件
Vue.component(
componentName,
// 如果这个组件选项是通过 `export default` 导出的,
// 那么就会优先使用 `.default`,
// 否则回退到使用模块的根。
componentConfig.default || componentConfig
);
});
注意全局注册的行为必须在根 Vue 实例创建之前发生