组件化的思想
- 模块化是一种思想,一种构建方式,把一种很复杂的事物拆分成一个一个的小模块,然后通过某种特定的方式把这些小模块组织到一起相互协作完成这个复杂的应用功能。
- 在 Vue 中,组件就是用来封装视图(HTML)的。组件思想就是把一个很大的复杂的 Web 页面视图拆分成一块一块的组件视图,然后利用某种特定的方式把他们组织到一起完成完整的 Web 应用构建。
- HTML 结构
- CSS 样式
- JavaScript 行为
- 为什么要把视图组件化,优势?
- 开发效率
- 可维护性
- 可重用性
- 便于分工
通过 Element 体会组件的威力
Element 是基于 Vue 开发的一个知名的第三方组件库,它能帮助我们更加快速的构建应用。
Element 官网
使用:
- 安装:`npm i element-ui -S``
- 引入 element-ui 组件库的 CSS 样式
- 导入 Vue
- 引入 element-ui 组件库的 JavaScript 脚本
- 调用 element-ui 组件
组件是什么
组件在代码中的直观体现就是封装了一个自定义的 HTML 标签
使用组件
组件的定义方式分为两种,全局定义和局部定义:
- 全局组件定义在全局,在任意组件中都能直接使用
- 局部组件定义在局部,只能在当前组件使用
- 建议把通用组件定义在全局,把不通用组件定义在局部
全局注册
注册:
基本形式:Vue.component('组件名字', 组件模板对象)
方式 一:
Vue.component('my-component',
Vue.extend({
template: '<div>A custom component!</div>'
})
)
new Vue({
el: '#app'
})
方式二:
Vue.component('my-component', {
template: '<div>A custom component!</div>'
})
new Vue({
el: '#app'
})
方式三:
<template id="tmpl">
<div>A custom component!</div>
</template>
<script>
Vue.component('my-component', {
template: '#tmpl'
})
new Vue({
el: '#app'
})
</script>
方式四:
<script type="text/x-template" id="tmpl">
<div>A custom component!</div>
</script>
<script>
Vue.component('my-component', {
template: '#tmpl'
})
var vm = new Vue({
el: '#app'
})
</script>
在模板中使用组件:
<div id="app">
<my-component></my-component>
</div>
渲染结果:
<div id="app">
<div>A custom component!</div>
</div>
局部注册
不必把每个组件都注册到全局。可以通过某个 Vue 实例/组件的实例选项 components
注册,仅作用在其作用域中可用的组件:
注册:
new Vue({
el: '#app'
components: {
// <my-component> 将只能在父组件模板中可用
// 键名就是组件名称,值是一个对象,对象中配置组件的选项
'my-component': {
template: '<div>A custom component!</div>'
}
}
})
使用:
<div id="app">
<my-component></my-component>
</div>
- 组件一般分为两种
- 通用的组件
- 不通用的业务组件
所以设计具体业务尽量定义成局部。不要污染全局
组件的模板
-
组件的模板 template 只能有有一个根元素,否则警告报错:
Component template should contain exactly one root element.
-
组件定义 template 可以是字面量字符串或是一个定义了字面量字符串的变量,缺点是没有高亮,内置在 JavaScript 中,写起来麻烦。
-
X-Templates:template 可以写在 script 标签中,解决了高亮的问题,但是当组件数量多的时候,也麻烦。
-
以上方式都不好,我们最终的解决方案是使用 Vue 的
image.png.vue
单文件组件来写。但是要想使用这种方式必须结合 webpack 构建工具。
组件的复用
- 组件是可复用的 Vue 实例,可以有自己的 data、methods、computed、watch 等等选项
- 组件是独立的作用域,不能访问父组件的数据
- 组件的 data 必须是函数,函数中返回一个对象作为组件的 data。因此每个实例可以维护一份被返回对象的独立的拷贝。
data: function () {
return {
count: 0
}
}
组件的组织
-
采用组件化构建方式,一个应用被一个根组件管理起来,根组件中嵌套了子组件,子组件还可以嵌套自己的子子组件。
组件树
放到匿名自执行函数中,将某些代码包裹起来可以实现块级作用域的效果,减少全局变量的数量,减少命名冲突,在匿名自执行函数执行结束后变量就会被内存释放掉,从而也会节省了内存。
图解 Vue 组件化构建方式
从程序角度实现组件化应用构建的架构方式如下图,这种方式的缺点是:
- 组件的模板没有高亮
- 没有模块化之前,首页一堆 script 标签,比较麻烦
组件通信
在 Vue 中,父子组件的关系可以总结为 prop 向下传递,事件向上传递。父组件通过 prop 给子组件下发数据,子组件通过事件给父组件发送消息。
父子组件关系
父子组件通信:Props Down
- 在父组件中通过子组件标签声明属性的方式传递数据。注意:只有
v-bind
才可以传递动态数据。
<child message="hello!"></child>
- 在子组件中声明 props 接收父组件传递的数据,组件接收到 props 就可以像访问 data 中的数据一样,来访问 props 中的数据并使用。
Vue.component('child', {
// 声明 props
props: ['message'],
// 就像 data 一样,prop 也可以再模板中使用
// 同样也可以再 vm 实例中通过 this.message 来使用
template: '<span>{{ message }}</span>'
})
- Vue 组件通信原则:单向数据流
父组件数据的改变可以影响到子组件,但是子组件不要去修改父组件的数据。因为当你的组件嵌套过深的时候,在子组件中修改某个父组件的数据可能会让你的应用数据流变得非常复杂而难以理解。
注意:在 JavaScript 中对象和数组是通过引用传入的,所以对于一个数组或对象类型的 prop 来说,在子组件中改变这个对象或数组本身将会影响到父组件的状态。引用类型数据虽然可以修改,但是不建议使用,因为这样就违背了 Vue 组件的通信原则
思考:那子组件要改数据呢?子组件能不能把数据给父亲呢,让父组件自己去修改自己的数据呢?
父子组件通信:Event Up
父组件使用 prop 传递数据给子组件。但子组件怎么跟父组件通信呢?这个时候 Vue 的自定义事件系统就派的上用场了。
- 在子组件中调用
$emit()
方法发布一个事件
Vue.component('button-counter', {
template: `<button v-on:click="incrementCounter">{{ counter }}</button>`
data: function() {
return {
counter: 0
}
}
methods: {
incrementCounter: function () {
this.counter += 1
// 发布一个名字叫 increment 的事件
this.$emit('increment')
}
}
})
- 在父组件中提供一个子组件内部发布的事件处理函数
new Vue({
el: '#counter-event-example',
data: {
total: 0
},
methods: {
incrementTotal : function () {
this.total += 1
}
}
})
- 在子组件的模板的标签上订阅子组件内部发布的事件
<div id="counter-event-example">
<p>{{ total }}</p>
<!--订阅子组件内部发布的 increment 事件,当子组件内部 $emit('increment')
发布的时候,就会调用到父组件中的 incrementTotal 方法-->
<button-counter @increment="incrementTotal"></button-counter>
</div>
非父子组件通信:Event Bus
专业组件通信:Vuex
箭头函数绑定父级上下的 this
路由、和服务端交互、webpack
网友评论