1. 组件基础
为什么要使用组件
组件(Component)是对数据和方法的简单封装。Vue组件可以扩展HTML元素,提高重用性的,让代码可复用。
组件的使用
- 组件命名 两种方式:
1)使用kebab-case(短横线分隔命名)<my-component>
2)使用PascalCase(首字母大写命名)<MyComponent>
- 组件复用 可以将组件进行任意次数的复用,每个组件都会独立维护它的实例数据
- 组件注册 两种方式:(demo中有)
1)全局注册
2)局部注册
demo<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>Document</title> <script src="https://cdn.bootcss.com/vue/2.6.10/vue.min.js"></script> </head> <body> <h4>全局注册</h4> <div id="app1"> <my-component1></my-component1> <my-component1></my-component1> <my-component1></my-component1> </div> <hr> <h4>局部注册</h4> <div id="app2"> <my-component2></my-component2> <my-component2></my-component2> <my-component2></my-component2> </div> </body> <script> // 全局注册 // 第一个参数是取得组件名,第二个参数是配置项 Vue.component('my-component1',{ // 写data的好处就是每一个组件都有自己的私有作用域 data:function () { return { title: '我是全局注册的组件' } }, template: '<div>{{title}}</div>' }) new Vue({ el: '#app1' }) // 局部注册 var ComponentA = { data:function () { return { title: '我是局部注册的组件' } }, template: '<div>{{title}}</div>' } new Vue({ el: '#app2', components: {// 采用键值对的形式 'my-component2': ComponentA } }) </script> </html>
使用prop向子组件传递数据
- prop书写规则 使用DOM中的模板时,驼峰命名的 prop 名需要使用其等价的短横线分隔命名
- prop基本用法 传递静态或动态prop
<body> <div id="app"> <my-component :message='fuji'></my-component> <my-component message="2"></my-component> <my-component message="3"></my-component> </div> </body> <script> Vue.component('my-component',{ // 数据message就是通过props从父级传递过来的,在组件的自定义标签上直接写该props的名称 // 大白话就是,用props可以取到 该组件标签上 需要传进来的变量,例如message // 第一个标签中的message对应的是父级上的 fuji 这个变量 // 如果要传递多个数据,就在props中添加就可以 props: ['message'], data:function () { return { title: '我是全局注册的组件' } }, template: '<div><div>{{title}}</div><p>message={{message}}</p></div>' }) new Vue({ el: '#app', data: { fuji: '父组件传的动态值1' } }) </script>
- 传递特性 所有的 prop 都使得其父子 prop 之间形成了一个单向下行绑定(单向数据流)
- 组件prop的数据验证
- 当组件需要提供给别人使用时,推荐都进行数据验证
- 验证的type类型可以是:String、Number、Boolean、Object、Array、Function、Symbol、Date。type也可以是一个自定义构造器,使用instanceof检测。
- 当prop验证失败时,在 开发版本 下会在控制台抛出一条警告
这里用的是开发版本,这样验证时如果传了空字符或者其他格式,控制台会显示报错
<script src="https://cdn.bootcss.com/vue/2.6.10/vue.js"></script>
<body> <div id="app"> <my-component :message='fuji' :message1='fuji1'></my-component> </div> </body> <script> Vue.component('my-component',{ // props 可以做数据验证 props: { message: String, message1: { type: Object, required: true } }, data:function () { return { title: '我是全局注册的组件', defaultMsg: this.message,// 这样子组件可以随意调用父组件中的值,非常灵活 defaultMsg1: this.message1 } }, template: '<div><div>{{title}}</div><p>{{defaultMsg}}</p><p>{{defaultMsg1.name}}</p></div>' }) new Vue({ el: '#app', data: { fuji: '父组件传的动态值1', fuji1: {name: 'xiaoming'} } }) </script>
自定义事件
- 使用场景 当子组件需要向父组件传递数据时,就要用到自定义事件
- 基本用法 子组件用
$emit()
来触发事件,父组件用$on()
来监听子组件的事件。 - 自定义组件的v-model 一个组件上的v-model默认会利用名为value的prop和名为input的事件
- 将原生事件绑定到组件 使用v-on的
.native
修饰符监听原生事件 .sync
修饰符 父组件监听自定义事件按需更新数据<div id="app"> <h4>demo1</h4> <!-- 在父组件直接用v-model绑定,就可以获取到子组件的值,不用像demo2再用@接受 --> <my-component v-model="total"></my-component> <button @click="handleReduce">减1按钮</button> <p>总共:{{total}}</p> <hr> <h4>demo2</h4> <!-- 通过子组件中的child-input接受来调用getVal函数--> <my-component1 @child-input='getVal'></my-component1> <div>子组件传值:{{msgVal}}</div> </div> <script> // demo1 Vue.component('my-component', { props: ['value'], template: '<input :value="value" @input="updateValue">', methods: { updateValue(e) { // 一个组件上的v-model默认会利用名为value的prop和名为input的事件 this.$emit('input', e.target.value) } } }) // demo2 Vue.component('my-component1', { template: '<button @click="updateValue">点击发送{{changeVal}}到父组件</button>', data () { return { changeVal: '' } }, methods: { updateValue() { if (!this.changeVal) { changeVal = 0 } this.changeVal ++ // 通过$emit向父组件传值 this.$emit('child-input', this.changeVal) } } }) // 父组件 new Vue({ el: "#app", data: { total: 0, msgVal: '' }, methods: { // demo1 handleReduce() { this.total --; }, // demo2 getVal(val) { this.msgVal = val } } }) </script>
2. Slot分发内容
什么是Slot
- ** 插槽(Slot) Vue提岀来的一个概念,插槽用于决定将所携带的內容,插入到指定的某个位置,从而使模板分块,具有模块化的特质和更大的重用性。
- 当需要让组件组合使用,混合父组件的内容与子组件的模板时,就会用到Slot。
单个slot
- 在子组件内使用特殊的
<slot>
元素,就可以为这个子组件开启一个slot(插槽)。 - 在父组件模板里,插入在子组件标签内的所有内容将替代子组件的
<slot>
标签及它的内容。
具名slot
- 给
<slot>
元素指定一个name后,可以分发多个内容。 - 具名Slot可以与单个Slot共存。
作用域slot
- 作用域插槽是一种特殊的slot,使用一个可以复用的模板替换己渲染元素
demo<div id="app"> <h4>单个slot</h4> <child-component> <!-- 如果没有内容,则默认显示子组件中的内容 --> <p>slot分发的内容</p> <p>slot分发的内容</p> </child-component> <h4>具名slot</h4> <child-component1> <p slot="header">slot分发的header内容</p> <p>slot分发的内容</p> <p>slot分发的内容</p> <div slot="footer">底部footer内容</div> </child-component1> <h4>作用域slot</h4> <scope-component> <template scope="props"> <p>来自父组件的内容</p> <h1>{{props.msg}}</h1> </template> </scope-component> </div> <script src="https://cdn.bootcss.com/vue/2.6.10/vue.js"></script> <script> // 单个slot Vue.component('child-component', { template: '<div><slot><p>如果父组件没有插入内容,该内容会默认显示</p></slot></div>' }) // 具名slot Vue.component('child-component1', { template: '<div>\ <slot name="header"><p>如果父组件没有插入内容,该内容会默认显示header</p></slot>\ <slot><p>如果父组件没有插入内容,该内容会默认显示</p></slot>\ <slot name="footer"><p>如果父组件没有插入内容,该内容会默认显示footer</p></slot>\ </div>' }) // 作用域slot Vue.component('scope-component', { template: '<div>\ <slot msg="我是子组件传递的内容"></slot>\ </div>' }) new Vue({ el: "#app" }) </script>
3. 单文件组件及自定义组件
单文件组件
- 一个后缀名为.vue的文件,使用.vue文件需要先安装vue-loader、vue- style-loader 等加载器并做 webpack配置。因为要使用ES6语法,还需要安装配置 babel 和 babel-loader等编译器。
- 每个.vue文件包含三种类型的顶级语言块——
<template>``<script>
和<style>
,还允许添加可选的自定义块。
自定义组件
- 类似 element-u这类组件库,都是为了完成一些自定义或者特定业务的,这一类组件都可以称之为自定义组件。
实例可参考博客:搭建webpack +babel + vue项目以及编写单文件和自定义组件
4. 动态组件及异步组件
动态组件
Vue.js提供了一个特殊的元素<component>
用来动态地挂载不同的组件,使用is特性来选择要挂载的组件,这样的组件叫动态组件。另外可以使用<keep-alive>
标签来使组件进行缓存。
demo
动态改变currentView的值,就可以动态挂载组件。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
<script src="https://cdn.bootcss.com/vue/2.6.10/vue.min.js"></script>
</head>
<body>
<div id="app">
<component :is="currentView"></component>
<button @click="changeView('A')">切换到A</button>
<button @click="changeView('B')">切换到B</button>
<button @click="changeView('C')">切换到C</button>
</div>
<script src="https://cdn.bootcss.com/vue/2.6.10/vue.min.js"></script>
<script>
new Vue({
el: '#app',
components: {
comA: { template: '<div>我是A</div>' },
comB: { template: '<div>我是B</div>' },
comC: { template: '<div>我是C</div>' },
},
data: {
currentView: 'comA'
},
methods: {
changeView(com) {
this.currentView = 'com' + com
}
},
})
</script>
</body>
</html>
异步组件
Vue.js允许将组件定义为一个工厂函数,动态地解析组件。Vue只在组件需要渲染时触发工厂函数,并且把结果缓存起来,用于后面的再次渲染。我们把这类组件称之为异步组件。
demo
工厂函数接收一个resolve回调,在收到从服务器下载的组件定义时调用。也可以调用reject(reason)指示加载失败。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div id="app">
<my-component></my-component>
</div>
<script src="https://cdn.bootcss.com/vue/2.6.10/vue.min.js"></script>
<script>
Vue.component('my-component', function(resolve, reject){
setTimeout( () => {
resolve({
template:"<div>我是异步渲染的</div>"
})
}, 2000)
})
new Vue({
el: '#app'
})
</script>
</body>
</html>