Vue进阶——组件化开发
- 一、什么是组件化
- 二、组件
- 1. 写法
- 2. 通信
- 3. 父子组件的访问方式
- 三、Slot 插槽(组件扩展性)
- 四、模板化概念
一、什么是组件化
类似微服务的软件架构,在前端开发中,一个页面的实现往往十分复杂,我们可以将一个页面划分为多个块,每个块负责相应的功能,块之间通过通信来交互。这样的前端开发方式正是组件化开发,一个页面是一个大的组件树,其下又划分有很多小的组件。这样一来,不仅降低了一次开发的难度,而且避免了重复造轮子,组件可以灵活的嵌入其他的Vue项目中进行使用。
二、组件
1. 写法
- 注册组件的步骤
创建组件的构造器:
Vue.extend()
注册组件:全局/局部 使用组件:Vue实例范围内 - 注册组件
全局:可以在多个Vue实例下使用,
Vue.conponent( , )
; 局部:在实例下注册,components: 属性
<script>
var cpnCreater = Vue.extend({
// 反单引号
template: `
<div>
<h2>Vue Component</h2>
</div>
`
});
var vm = new Vue({
el: '#app',
components: {
cpn: cpnCreater
}
});
</script>
- 父子组件
在父组件创建时,可以添加
components: 属性
,引入子组件,运行时会立刻编译完成。 注意:声明顺序会影响结果。 - 语法糖
V2.0之后的组件注册方式封装了
Vue.extend()
方法,使用对象代替,简化步骤。
<script>
// 全局注册
Vue.component('cpn1', {
template:`
<div>Component1 Test</div>
`
});
var vm = new Vue({
el: '#app',
// 局部组件
components: {
'cpn2': {
template: `
<div>Component2 Test</div>
`}
}
});
</script>
- 模板抽离 组件内部不能访问Vue实例的数据; 组件有属于自己的HTML模板,也有自己的数据,且规定data为函数形式(隔离各组件实例的数据域)。
// 使用<script>标签
<script id="cpnDiv" type="text/x-handlebars-template">
// 需要包含一个根
<div>
{{ title }}
Component1 Test
</div>
</script>
// 使用<template>标签
<template id="cpnTemp">
<div>
Component2 Test
</div>
</template>
<script>
// 全局注册
Vue.component('cpn1', {
template: '#cpnDiv',
data() {
title: 'Title'
},
methods: {}
});
</script>
2. 通信
- 父子组件通信 项目中的请求数据往往放在上层,需要在下层进行展示,这就涉及到组件之间的通信。
通过props向子组件传递数据
通过事件$emit() v-on向父组件发送消息
代码语言:javascript复制<div :cmovies="movies"></div>
props: ['cmovies', 'cmessage']
// String/Number/Boolean/Array/Object/Date/Function/Symbol
props: {
cmovies: Array,
cmessage: String,
cinfo: {
type: String,
// 类型是对象或数组时,默认值必须是函数,例如:default(){return [];}
default: 'abc',
// required: true
}
}
说明: Vue不支持驼峰命名,需要用-
小写来代替。
子组件不推荐修改父组件传来的值,应创建一个data进行双向绑定。
代码语言:javascript复制// 父传子,其中Vue实例当作父
// props属性
<body>
<div id="app">
<cpn :cmovies="movies"></cpn>
</div>
<template id="cpnTemp">
<div>
<h2>Component2 Test</h2>
<p>{{cmovies}}</p>
<p>{{cmessage}}</p>
</div>
</template>
<script>
// 子组件
const cpn = {
template: '#cpnTemp',
props: {
cmovies: Array,
cmessage: {
type: String,
default: "nothing..."
}
},
data() {
return {}
}
};
var vm = new Vue({
el: '#app',
data: {
message: 'Hi',
movies: ['1', '2', '3']
},
components: {
cpn: cpn
}
});
</script>
</body>
// 子传父
// v-on v-bind
<body>
<div id="app">
<cpn :cmovies="movies" @item-click="cpnClick"></cpn>
</div>
<template id="cpnTemp">
<div>
<h2>Component2 Test</h2>
<p>{{cmovies}}</p>
<p>{{cmessage}}</p>
<button v-for="item in info" @click="btnClick(item)">{{item.name}}</button>
</div>
</template>
<script>
// 子组件
const cpn = {
template: '#cpnTemp',
props: {
cmovies: Array,
cmessage: {
type: String,
default: "nothing..."
}
},
data() {
return {
info: [
{id: '001', name: 'aaa'},
{id: '002', name: 'bbb'},
{id: '003', name: 'ccc'},
{id: '004', name: 'ddd'},
]
}
},
methods: {
btnClick(item) {
// 自定义事件
this.$emit('item-click', item);
console.log(item);
}
}
};
var vm = new Vue({
el: '#app',
data: {
message: 'Hi',
movies: ['1', '2', '3']
},
components: {
cpn
},
methods: {
cpnClick(item) {
console.log('cpnClick', item);
}
}
});
</script>
</body>
- .vue文件
<template></template>
<script></script>
<style></style>
- 双向绑定 v-model = v-bind v-on v-model watch(components中)进行双向绑定
watch: {
_paraName(newValue) {
this._paraName = newValue...;
this.$emit();
}
}
3. 父子组件的访问方式
- 父访问子:children/refs this.children[index] / let t of this.children 标签上添加ref属性,即可通过this.
- 子访问父:parent/root this.
三、Slot 插槽(组件扩展性)
抽取共性,保留不同。
- 基本使用
// 多个值会一次性替换
<slot></slot>
// 默认为按钮标签
<slot><button>按钮</button></slot>
- 具名插槽
Vue 2.6.0之后使用
v-slot
代替了slot
和scope-slot
。
<cpn>
// 组件中使用插槽
<template v-slot="slot1">
<span>It's me!</span>
</template>
</cpn>
// 定义插槽
<template id="cpn">
<div>
<slot name="slot1"></slot>
</div>
</template>
- 编译作用域 作用域插槽 父组件替换插槽的标签,但是内容由子组件来提供
<cpn>
// 组件中访问子组件的message值
<template v-slot="slot1">
<span>{{ slot1.mydata }}</span>
</template>
</cpn>
// 定义插槽,绑定message数据
<template id="cpn">
<div>
<slot name="slot1" :mydata="message"></slot>
</div>
</template>
四、模板化概念
随着前端代码量的增多,通常会将代码组织在多个js中,进行维护,但这会造成类似全局变量同名、js文件的依赖等问题。
- 自定义模块化
将js封装在一个函数内,并定义一个变量,返回一个对象结果。每次调用时使用
变量名.对象.函数/变量
。 - CommonJS(还有AMD、CMD、ES6)
// CommonJS规范的导出
module.exports = { add, mul }
// CommonJS规范的导入
const { add, mul } = require('./xxx.js')
引用<script type="module">
时,js内部的数据都是局部的,无法被其他js文件访问。需要增加export和import关键字。
ES6 export关键字
代码语言:javascript复制export {
name, sum
};
export var name = "...";
export function sum(){...};
export class Class{...};
// 只能由一个default
export default age
ES6 import关键字
代码语言:javascript复制import {name, sum} from "./xxx.js";
import * as im from "./xxx.js"; // 使用:im.name
// 不需要{},且可以自己命名
import addr from "./xxx.js";