Vue进阶——组件化开发

2022-10-25 16:10:28 浏览数 (1)

Vue进阶——组件化开发

  • 一、什么是组件化
  • 二、组件
    • 1. 写法
    • 2. 通信
    • 3. 父子组件的访问方式
  • 三、Slot 插槽(组件扩展性)
  • 四、模板化概念

一、什么是组件化

类似微服务的软件架构,在前端开发中,一个页面的实现往往十分复杂,我们可以将一个页面划分为多个块,每个块负责相应的功能,块之间通过通信来交互。这样的前端开发方式正是组件化开发,一个页面是一个大的组件树,其下又划分有很多小的组件。这样一来,不仅降低了一次开发的难度,而且避免了重复造轮子,组件可以灵活的嵌入其他的Vue项目中进行使用。

二、组件

1. 写法

  • 注册组件的步骤 创建组件的构造器:Vue.extend() 注册组件:全局/局部 使用组件:Vue实例范围内
  • 注册组件 全局:可以在多个Vue实例下使用,Vue.conponent( , ); 局部:在实例下注册,components: 属性
代码语言:javascript复制
<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()方法,使用对象代替,简化步骤。
代码语言:javascript复制
<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为函数形式(隔离各组件实例的数据域)。
代码语言:javascript复制
// 使用<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. 通信

  • 父子组件通信 项目中的请求数据往往放在上层,需要在下层进行展示,这就涉及到组件之间的通信。
代码语言:javascript复制
通过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文件
代码语言:javascript复制
<template></template>
<script></script>
<style></style>
  • 双向绑定 v-model = v-bind v-on v-model watch(components中)进行双向绑定
代码语言:javascript复制
watch: {
	_paraName(newValue) {
		this._paraName = newValue...;
		this.$emit();
	}
}

3. 父子组件的访问方式

  1. 父访问子:children/refs this.children[index] / let t of this.children 标签上添加ref属性,即可通过this.
  2. 子访问父:parent/root this.

三、Slot 插槽(组件扩展性)

抽取共性,保留不同。

  1. 基本使用
代码语言:javascript复制
// 多个值会一次性替换
<slot></slot>

// 默认为按钮标签
<slot><button>按钮</button></slot>
  1. 具名插槽 Vue 2.6.0之后使用v-slot代替了slotscope-slot
代码语言:javascript复制
<cpn>
	// 组件中使用插槽
	<template v-slot="slot1">
		<span>It's me!</span>
	</template>
</cpn>

// 定义插槽
<template id="cpn">
	<div>
		<slot name="slot1"></slot>
	</div>
</template>
  1. 编译作用域 作用域插槽 父组件替换插槽的标签,但是内容由子组件来提供
代码语言:javascript复制
<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)
代码语言:javascript复制
// 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";

0 人点赞