- 能够知道组件化开发思想
- 能够知道组件的注册方式
- 能够说出组件间的数据交互方式
- 能够说出组件插槽的用法
- 能够说出 Vue 调试工具的用法
- 能够基于组件的方式实现业务功能
Google曾试图推出一款组件化手机,通过不同的组件为手机添加不同的功能。
一般来说,组件化思想都有以下特点:
- 标准(有一套标准的规范)
- 分治(每个组件负责不同功能)
- 重用(组件可以多次被调用)
- 组合(多个简单的组件可以组合为复杂组件)
不同组件之间有父子、兄弟、爷孙等关系
-
我们希望尽可能多的重用代码
-
自定义组件的方式不太容易(html、css和js)
-
多次使用组件可能导致冲突
-
Web Components
通过创建封装好功能的定制元素解决上述问题 -
官网:https://developer.mozilla.org/zh-CN/docs/Web/Web_Components
-
Vue 部分实现了上述规范
Vue.component('组件名称', {
// data的值是一个函数
data: 组件数据,
template: 组件模板内容
})
举一个例子:
// 定义一个叫做 button-counter 的组件
Vue.component('button-counter', {
data: function(){
return {
count: 0
}
},
template: '<button @click="handle">点击了{{count}}次</button>',
methods: {
// 可以在methods中定义组件方法,供组件使用
handle: function(){
// 这里是this.count
this.count++;
}
}
});
全局注册的组件不能使用局部注册的组件
<div id="app">
<!-- 直接以组件名作为标签名 -->
<button-counter></button-counter>
<!-- 组件可以重用,组件之间的数据独立 -->
<button-counter></button-counter>
</div>
-
data
必须是个函数(使用函数可以实现闭包环境,一定程度上保证了每个组件实例之间的数据是独立的) -
组件模板
template
必须是确切的包含一个根元素(必须要有一个父元素,并且父元素没有兄弟元素) -
组件模板内容可以是模板字符串(模板字符串需要浏览器支持ES6语法)
-
组件命名可以有2种方式
-
命名法:Vue.component('vue-component', /* code... */)
- 驼峰命名法(第一个字母大写):
Vue.component('VueComponent', /* code... */)
- 驼峰命名法只能写在组件中的
template
直接写在页面中会报错 - 详细请看驼峰命名法的问题
- 驼峰命名法只能写在组件中的
const ComponentA = {};
const ComponentB = {};
const vm = new Vue({
el: '#app',
components: {
'component-a': ComponentA,
'component-b': ComponentB
}
});
局部组件只能在#app
中使用,详细请看代码示例
- 克隆仓库:https://github.com/vuejs/vue-devtools
- 安装依赖包:
npm install
- 构建:
npm run build
- 打开Chrome扩展页面(打开开发者模式)
- 加载已解压的扩展
- 或者直接通过Vue提供的构建好的包直接载入Chrome中(需翻墙)
打开 Vue 文件 --> F12开发者面板 --> Vue面板
Vue.component('menu-item', {
props: ['title'],
template: '<div>{{title}}</div>'
});
<menu-item title="来自父组件的数据"></menu-item>
<menu-item :title="title"></menu-item>
详细请看代码示例
如果是固定的值,加和不加:
有什么区别呢?
- 加上
:
,就会把一些特殊值解析为不同的类型,例如会把true
解析为boolean类型
- 不加
:
,所有的值都会被解析为字符串类型,就算是true
还是字符串
- 在
props
中使用驼峰形式,模板中需要使用短横线的形式 - 字符串形式的模板没有这个限制
Vue.component('menu-item', {
// 在 JS 中是驼峰形式
props: ['itemTitle'],
// 字符串形式的模板不受影响
template: '<div>{{itemTitle}}</div>'
});
<!-- 在HTML中需要加上短横线- -->
<menu-item item-title="hello"></menu-item>
- 字符串
String
- 数值
Number
- 布尔值
Boolean
- 数组
Array
- 对象
Object
详细请看代码示例
$emit
是固定写法
<button @click="$emit('自定义函数名称')">点击可操作父组件</button>
<list-item @上文自定义函数名称="操作"></list-item>
详细请看:代码示例
<button @click="$emit('自定义参数名', 参数)"></button>
$event
是固定写法
<menu-item @自定义函数名="$event接收参数"></menu-item>
// 重新创建一个 Vue 实例,这个实例就担任事件中心的工作
var eventHub = new Vue();
eventHub.$on('add-todo', addTodo);
eventHub.$off('add-todo');
// 可以携带参数
eventHub.$emit('add-todo', id);
详细请看代码示例
父组件向子组件传递内容
Vue.component('alert-box', {
template: `
<div>
<strong>Error!</strong>
<slot></slot>
</div>
`;
});
<slot>
是Vue的API,<slot>
中可以写默认的内容,如果有新的内容就会覆盖默认内容
<alert-box>Something bad happened.</alert-box>
组件中的内容就被放在了slot
标签中
详细请看代码示例
在组件template
中定义
<header>
<slot name="header"></slot>
</header>
<main>
<slot></slot>
</main>
<footer>
<slot name="footer"></slot>
</footer>
<组件名>
<h1 slot="header">标题内容</h1>
<!-- 也可以使用<template>这个标签不会渲染到页面上 -->
<template slot="header">
<p>标题内容1</p>
<p>标题内容2</p>
<p>标题内容3</p>
<p>标题内容4</p>
</template>
<p>主要内容1</p>
<p>主要内容2</p>
<p slot="footer">底部内容</p>
</组件名>
没有名称的会匹配没有名称的slot
详细请看代码示例
应用场景:父组件对子组件的内容进行加工处理
在template
中定义
<ul>
<li :key="item.id" v-for="item in fruitlist">
<slot :info="item">
{{item.name}}
</slot>
</li>
</ul>
<fruit-list :fruitlist="fruitlist">
<template slot-scope="slotProps">
<span class="current" v-if="slotProps.info.id === 3">
{{slotProps.info.name}}
</span>
<span v-else>{{slotProps.info.name}}</span>
</template>
</fruit-list>
使用slot-scope
来定义插槽的作用域
详细请看代码示例
根据业务功能进行组件化划分
- 标题组件(展示文本)
- 列表组件(列表展示、商品数量变更、商品删除)
- 结算组件(计算商品总额)
- 实现整体布局和样式效果
- 划分独立的功能组件
- 组合所有的子组件形成整体结构
- 逐个实现各个组件功能
- 标题组件
- 列表组件
- 结算组件