Vue进阶——组件化开发

一、什么是组件化

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

// 父传子,其中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. 父子组件的访问方式

  1. 父访问子:$children/$refs
    this.$children[index] / let t of this.$children
    标签上添加ref属性,即可通过this.$refs._refname来获取

  2. 子访问父:$parent/$root
    this.$root直接访问Vue实例

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

抽取共性,保留不同。

  1. 基本使用
// 多个值会一次性替换
<slot></slot>

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

// 定义插槽
<template id="cpn">
	<div>
		<slot name="slot1"></slot>
	</div>
</template>
  1. 编译作用域
    作用域插槽
    父组件替换插槽的标签,但是内容由子组件来提供
<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关键字

export {
	name, sum
};
export var name = "...";
export function sum(){...};
export class Class{...};

// 只能由一个default
export default age

ES6 import关键字

import {name, sum} from "./xxx.js";
import * as im from "./xxx.js";  // 使用:im.name

// 不需要{},且可以自己命名
import addr from "./xxx.js";
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值