Vue组件化开发
- 组件化开发的思想
- 组件的注册
- 组件间的数据交互
- 组件插槽的用法
- Vue调试工具的用法
组件开发思想
- 标准
- 分治
- 重用
- 组合
组件化规范:Web Components(并非所有的浏览器都支持)
- 我们希望尽可能多的重用代码
- 自定义组件的方式不太容易(html、css和js)
- 多次使用组件可能导致冲突
Web Components通过创建封装好功能的定制元素解决上述问题(Vue部分实现了上述规范)
组件注册
Vue.component('组件名称',{
// 组件内容
data: '组件数据',
template: '组件模板内容'
})
<!-- 组件使用 -->
<div id="app">
<h1>{{name}}</h1>
<button-count></button-counter>
<!-- 组件是可以重用的 -->
<button-count></button-counter>
<button-count></button-counter>
</div>
<script>
// 定义一个名为 button-counter 的新组件
Vue.component('button-counter', {
data: function() {
return {
count: 0
}
},
template: '<button v-on:click="count++">点击{{count}}次</button>'
})
</script>
组件注册注意事项:
- data必须是一个函数 => 分析函数和普通对象的对比
- 组件模板内容必须是单个根元素 => 分析演示实例的效果
- 组件模板内容可以是模板字符串 => 模板字符串需要浏览器提供支持(ES6语法)
- 组建的命名方式
- 短横线方式
Vue.component('my-component',{ /*...*/ }
- 驼峰式
Vue.component('myComponent',{ /*...*/ }
- 如果使用驼峰式命名组件,那么在使用组件的时候,只能在字符串模板中使用驼峰的方式命名组件,但是在普通的标签模板中,必须使用短横线的方式命名组件
局部组件注册
var ComponentA = { /* ... */ }
var ComponentB = { /* ... */ }
var ComponentC = { /* ... */ }
new Vue({
el: '#app',
components: {
'component-a' :componentA,
'component-b' :componentB,
'component-c' :componentC
}
})
// 局部组件只能在其父组件中使用
Vue调试工具
- 克隆仓库
- 安装依赖包
- 构建
- 打开Chrome扩展页面
- 选中开发者模式
- 加载已解压的扩展,选择shells/chrome
组件间的数据交互
-
父组件向子组件传值
-
组件内部通过
props
接收传递过来的值Vue.component('menu-item', { props: ['title'], template: '<div>{{title}}</div>' })
-
父组件通过属性将值传递给子组件
<menu-item title="来自父组件传递的数据"></menu-item> <menu-item :title="title"></menu-item>
-
props
属性名规则- 在
props
中使用驼峰形式,模板中则需要使用短横线的形式 - 字符串形式的模板中则没有这个限制
<menu-item menu-title="hello"></menu-item> <script> Vue.component('menu-item', { // 在JavaScript中是驼峰式的 props: ['menuTitle'], template: `<div>{{menuTitle}}` }) </script>
<div id="app"> <h1>{{name}}</h1> <div>{{pmsg}}</div> <menu-item :menu-title="ptitle" content='hello'></menu-item> </div> <script> Vue.component('third-com', { props:['testTitle'], template:'<div>{{testTitle}}</div>' }); Vue.component('menu-item', { props:['menuTitle'], template:'<div>{{menuTitle}}<third-com testTitle="hello"></third-com></div>' }); let vm = new Vue({ el:'#app', data:{ name:"myVue", pmsg:'父组件的内容', ptitle:'动态绑定的属性' }, methods: { } }); </script>
- 在
-
props
属性值类型- 字符串 String
- 数值 Number
- 布尔值 Boolean
- 数组 Array
- 对象 Object
<div id="app"> <h1>{{name}}</h1> <div>{{pmsg}}</div> <!-- 不带引号为字符串类型 --> <menu-item :pstr="pstr" :pnum="13" :pboo="true" :parr="parr" :pobj="pobj"></menu-item> </div> <script> Vue.component('menu-item', { props: ['pstr','pnum','pboo','parr','pobj'], template:` <div> <div>{{pstr}}</div> <div>{{12 + pnum}}</div> <div>{{pboo}}</div> <ul> <li :key="index" v-for="(item,index) in parr">{{item}}</li> </ul> <div> <span>{{pobj.name}}</span> <span>{{pobj.age}}</span> <span>{{pobj.sex}}</span> </div> </div>` }); let vm = new Vue({ el: '#app', data: { name: "myVue", pmsg: '父组件的内容', pstr: 'hello', parr:['苹果','香蕉','哈密瓜'], pobj:{ name:'张三', age:19, sex:'男' } }, methods: { } }); </script>
-
-
子组件向父组件传值
子组件通过自定义事件向父组件传递消息
<button v-on:click='$emit("enlarge-text")'>扩大字体</button>
-
父组件监听子组件事件
<menu-item v-on:enlarge-text='fontSize += 0.1'></menu-item>
<div id="app"> <h1>{{name}}</h1> <div :style="{fontSize: fontSize + 'px'}">{{pmsg}}</div> <menu-item @enlarge-text="handle"></menu-item> </div> <script> /* 子组件向父组件传递数据-基本用法 props传递数据原则:单项数据绑定 */ Vue.component('menu-item', { template:` <div> <button @click="$emit('enlarge-text')">扩大字体</button> </div>` }); let vm = new Vue({ el: '#app', data: { name: "myVue", pmsg: '父组件的内容', fontSize: 10 }, methods: { handle:function() { // 扩大字体大小 this.fontSize+=10; } } }); </script>
子组件通过自定义事件向父组件传递信息
<button v-on:click='$emit("enlarge-text", 0.1)'>扩大字体</button>
-
父组件监听子组件事件
<menu-item v-on:enlarge-text='fontSize += $event'></menu-item>
<div id="app"> <h1>{{name}}</h1> <div :style="{fontSize: fontSize + 'px'}">{{pmsg}}</div> <!-- 不带引号为字符串类型 --> <menu-item @enlarge-text="handle($event)"></menu-item> </div> <script> /* 子组件向父组件传递数据-基本用法 props传递数据原则:单项数据绑定 */ Vue.component('menu-item', { template:` <div> <button @click="$emit('enlarge-text',5)">扩大字体</button> <button @click="$emit('enlarge-text',-5)">减小字体</button> </div>` }); let vm = new Vue({ el: '#app', data: { name: "myVue", pmsg: '父组件的内容', fontSize: 10 }, methods: { handle:function(val) { // 扩大字体大小 this.fontSize+=val; } } }); </script>
-
非父子组件间传值(兄弟间组件传值)
单独的事件中心管理组件间的通信
var eventHub = new Vue()
-
监听事件与销毁事件
eventHub.$on('add-todo', addTab) eventHub.off('add-todo')
触发事件
eventHub.$emit('add-todo', id)
<div id="app"> <h1>{{name}}</h1> <div> <button @click="handle">销毁事件</button> </div> <text-tom></text-tom> <text-jerry></text-jerry> </div> <script type="text/javascript"> // 兄弟组件间数据传递 // 提供事件中心 var hub = new Vue(); Vue.component('text-tom', { data: function () { return { num: 0 } }, template: ` <div> <div>Tom:{{num}}</div> <div> <button @click="handle">点击</button> </div> </div>`, methods: { handle: function () { hub.$emit('jerry-event', 2) } }, mounted: function () { // 监听事件 hub.$on('tom-event', (val) => { this.num += val; }) } }); Vue.component('text-jerry', { data: function () { return { num: 0 } }, template: ` <div> <div>Jerry:{{num}}</div> <div> <button @click="handle">点击</button> </div> </div>`, methods: { handle: function () { // 触发兄弟组件的事件 hub.$emit('tom-event', 1) } }, mounted: function () { // 监听事件 hub.$on('jerry-event', (val) => { this.num += val; }) } }); let vm = new Vue({ el: '#app', data: { name: "myVue" }, methods: { handle: function() { hub.$off('tom-event'); hub.$off('jerry-event'); } } }); </script>
组件插槽
- 父组件向子组件传递内容
插槽基本用法
-
插槽位置
<div id="app"> <h1>{{name}}</h1> <alert-box>有bug!</alert-box> <alert-box>有一个警告!</alert-box> <alert-box></alert-box> </div> <script> Vue.component('alert-box', { template: ` <div class="demo-alert-box"> <strong>Error!</strong> <slot></slot> </div>` }) let vm = new Vue({ el: '#app', data: { name: "myVue" }, methods: { } }); </script>
-
具名插槽用法
<!-- 插槽定义 --> <div class="container"> <header> <slot name="header"></slot> </header> <main> <slot></slot> </main> <footer> <slot name="footer"></slot> </footer> </div> <!-- 插槽内容 --> <base-layout> <h1 slot="header">标题内容</h1> <p>主要内容1</p> <p>主要内容2</p> <p slot="footer">底部内容</p> </base-layout>
<div id="app"> <h1>{{name}}</h1> <base-layout> <p slot="header">标题信息</p> <p>主要内容1</p> <p>主要内容2</p> <p slot="footer">底部信息</p> </base-layout> <base-layout> <template slot="header"> <p slot="header">标题信息</p> <p slot="header">标题信息</p> </template> <p>主要内容1</p> <p>主要内容2</p> <template slot="footer"> <p slot="header">尾部信息1</p> <p slot="header">尾部信息2</p> </template> </base-layout> </div> <script type="text/javascript"> Vue.component('base-layout', { template: ` <div> <header> <slot name="header"></slot> </header> <main> <slot></slot> </main> <footer> <slot name="footer"></slot> </footer> </div>` }); let vm = new Vue({ el: '#app', data: { name: "myVue" }, methods: { } }); </script>
作用域插槽
-
应用场景:父组件对子组件的内容进行加工处理
<!-- 插槽定义 --> <ul> <li v-for="item in list" v-bind:key="item.id"> <slot v-bind:item="item"> {{item.name}} </slot> </li> </ul> <!-- 插槽内容 --> <fruit-list v-bind:list="list"> <template slot-scope="slotProps"> <strong v-if="slotProps.item.current"> {{slotProps.item.text}} </strong> </template> </fruit-list>