Vue第四天学习笔记之组件化高级

Posted 最小的帆也能远航

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Vue第四天学习笔记之组件化高级相关的知识,希望对你有一定的参考价值。

目录

 

1.父子组件通信

1.1组件通信-父子组件通信案例

1.2组件通信-父子组件通信案例(watch实现)

1.3组件访问-父访问子-children-refs

1.4组件访问-子访问父-parent-root

2.组件化高级

2.1slot-插槽的基本使用

2.2slot-具名插槽的使用

2.3什么是编译的作用域

2.4作用于插槽的案例

3.前端模块化

3.1CommonJS

3.2ES6的模块化实现


1.父子组件通信

1.1组件通信-父子组件通信案例

<!DOCTYPE html>
<html lang="en">
	<head>
		<meta charset="UTF-8">
		<title>Title</title>
	</head>
	<body>

		<div id="app">
			<cpn :number1="num1" :number2="num2" @num1change="num1change" @num2change="num2change" />
		</div>

		<template id="cpn">
			<div>
				<h2>props:{{number1}}</h2>
				<h2>data:{{dnumber1}}</h2>
				<!--<input type="text" v-model="dnumber1">-->
				<input type="text" :value="dnumber1" @input="num1Input">
				<h2>props:{{number2}}</h2>
				<h2>data:{{dnumber2}}</h2>
				<!--<input type="text" v-model="dnumber2">-->
				<input type="text" :value="dnumber2" @input="num2Input">
			</div>
		</template>

		<script src="../js/vue.js"></script>
		<script>
			const app = new Vue({
				el: '#app',
				data: {
					num1: 1,
					num2: 0
				},
				methods: {
					num1change(value) {
						this.num1 = parseFloat(value)
					},
					num2change(value) {
						this.num2 = parseFloat(value)
					}
				},
				components: {
					cpn: {
						template: '#cpn',
						props: {
							number1: Number,
							number2: Number
						},
						data() {
							return {
								dnumber1: this.number1,
								dnumber2: this.number2
							}
						},
						methods: {
							num1Input(event) {
								// 1.将input中的value赋值到dnumber中
								this.dnumber1 = event.target.value;

								// 2.为了让父组件可以修改值, 发出一个事件
								this.$emit('num1change', this.dnumber1)

								// 3.同时修饰dnumber2的值
								this.dnumber2 = this.dnumber1 * 100;
								this.$emit('num2change', this.dnumber2);
							},
							num2Input(event) {
								this.dnumber2 = event.target.value;
								this.$emit('num2change', this.dnumber2)

								// 同时修饰dnumber2的值
								this.dnumber1 = this.dnumber2 / 100;
								this.$emit('num1change', this.dnumber1);
							}
						}
					}
				}
			})
		</script>

	</body>
</html>

1.2组件通信-父子组件通信案例(watch实现)

<!DOCTYPE html>
<html lang="en">
	<head>
		<meta charset="UTF-8">
		<title>Title</title>
	</head>
	<body>

		<div id="app">
			<cpn :number1="num1" :number2="num2" @num1change="num1change" @num2change="num2change" />
		</div>

		<template id="cpn">
			<div>
				<h2>props:{{number1}}</h2>
				<h2>data:{{dnumber1}}</h2>
				<input type="text" v-model="dnumber1">
				<h2>props:{{number2}}</h2>
				<h2>data:{{dnumber2}}</h2>
				<input type="text" v-model="dnumber2">
			</div>
		</template>

		<script src="../js/vue.js"></script>
		<script>
			const app = new Vue({
				el: '#app',
				data: {
					num1: 1,
					num2: 0
				},
				methods: {
					num1change(value) {
						this.num1 = parseFloat(value)
					},
					num2change(value) {
						this.num2 = parseFloat(value)
					}
				},
				components: {
					cpn: {
						template: '#cpn',
						props: {
							number1: Number,
							number2: Number,
							name: ''
						},
						data() {
							return {
								dnumber1: this.number1,
								dnumber2: this.number2
							}
						},
						watch: {
							dnumber1(newValue) {
								this.dnumber2 = newValue * 100;
								this.$emit('num1change', newValue);
							},
							dnumber2(newValue) {
								this.number1 = newValue / 100;
								this.$emit('num2change', newValue);
							}
						}
					}
				}
			})
		</script>

	</body>
</html>

1.3组件访问-父访问子-children-refs

有时候我们需要父组件直接访问子组件,子组件直接访问父组件,或者是子组件访问跟组件。

  • 父组件访问子组件:使用$children或$refs reference(引用)
  • 子组件访问父组件:使用$parent
<!DOCTYPE html>
<html lang="en">
	<head>
		<meta charset="UTF-8">
		<title>Title</title>
	</head>
	<body>

		<div id="app">
			<cpn></cpn>
			<cpn></cpn>

			<my-cpn></my-cpn>
			<y-cpn></y-cpn>

			<cpn ref="aaa"></cpn>
			<button @click="btnClick">按钮</button>
		</div>

		<template id="cpn">
			<div>我是子组件</div>
		</template>
		<script src="../js/vue.js"></script>
		<script>
			const app = new Vue({
				el: '#app',
				data: {
					message: '你好啊'
				},
				methods: {
					btnClick() {
						// 1.$children
						// console.log(this.$children);
						// for (let c of this.$children) {
						//   console.log(c.name);
						//   c.showMessage();
						// }
						// console.log(this.$children[3].name);

						// 2.$refs => 对象类型, 默认是一个空的对象 ref='bbb'
						console.log(this.$refs.aaa.name);
					}
				},
				components: {
					cpn: {
						template: '#cpn',
						data() {
							return {
								name: '我是子组件的name'
							}
						},
						methods: {
							showMessage() {
								console.log('showMessage');
							}
						}
					},
				}
			})
		</script>

	</body>
</html>

$children的缺陷:

  • 通过$children访问子组件时,是一个数组类型,访问其中的子组件必须通过索引值。
  • 但是当子组件过多,我们需要拿到其中一个时,往往不能确定它的索引值,甚至还可能会发生变化。
  • 有时候,我们想明确获取其中一个特定的组件,这个时候就可以使用$refs

$refs的使用:

  • $refs和ref指令通常是一起使用的。
  • 首先,我们通过ref给某一个子组件绑定一个特定的ID。
  • 其次,通过this.$refs.ID就可以访问到该组件了。 

1.4组件访问-子访问父-parent-root

在子组件中直接访问父组件,可以通过$parent

注意事项:

  • 尽管在Vue开发中,我们允许通过$parent来访问父组件,但是在真实开发中尽量不要这样做。
  • 子组件应该尽量避免直接访问父组件的数据,因为这样耦合度太高了。
  • 如果我们将子组件放在另外一个组件之内,很可能该父组件没有对应的属性,往往会引起问题。
  • 另外,更不好做的是通过$parent直接修改父组件的状态,那么父组件中的状态将变得飘忽不定,很不利于调试和维护。
<!DOCTYPE html>
<html lang="en">
	<head>
		<meta charset="UTF-8">
		<title>Title</title>
	</head>
	<body>

		<div id="app">
			<cpn></cpn>
		</div>

		<template id="cpn">
			<div>
				<h2>我是cpn组件</h2>
				<ccpn></ccpn>
			</div>
		</template>

		<template id="ccpn">
			<div>
				<h2>我是子组件</h2>
				<button @click="btnClick">按钮</button>
			</div>
		</template>

		<script src="../js/vue.js"></script>
		<script>
			const app = new Vue({
				el: '#app',
				data: {
					message: '你好啊'
				},
				components: {
					cpn: {
						template: '#cpn',
						data() {
							return {
								name: '我是cpn组件的name'
							}
						},
						components: {
							ccpn: {
								template: '#ccpn',
								methods: {
									btnClick() {
										// 1.访问父组件$parent
										// console.log(this.$parent);
										// console.log(this.$parent.name);

										// 2.访问根组件$root
										console.log(this.$root);
										console.log(this.$root.message);
									}
								}
							}
						}
					}
				}
			})
		</script>

	</body>
</html>


2.组件化高级

2.1slot-插槽的基本使用

组件的插槽: 组件的插槽也是为了让我们封装的组件更加具有扩展性。 让使用者可以决定组件内部的一些内容到底展示什么。

如何去封装这类的组件呢?

  • 它们也很多区别,但是也有很多共性。
  • 如果,我们每一个单独去封装一个组件,显然不合适:比如每个页面都返回,这部分内容我们就要重复去封装。
  • 但是,如果我们封装成一个,好像也不合理:有些左侧是菜单,有些是返回,有些中间是搜索,有些是文字,等等。

如何封装合适呢?抽取共性,保留不同。

  • 最好的封装方式就是将共性抽取到组件中,将不同暴露为插槽。
  • 一旦我们预留了插槽,就可以让使用者根据自己的需求,决定插槽中插入什么内容。
  • 是搜索框,还是文字,还是菜单。由调用者自己来决定

在子组件中,使用特殊的元素<slot>就可以为子组件开启一个插槽。

该插槽插入什么内容取决于父组件如何使用。

<slot>中的内容表示,如果没有在该组件中插入任何其他内容,就默认显示该内容

<!DOCTYPE html>
<html lang="en">
	<head>
		<meta charset="UTF-8">
		<title>Title</title>
	</head>
	<body>

		<!--
1.插槽的基本使用 <slot></slot>
2.插槽的默认值 <slot>button</slot>
3.如果有多个值, 同时放入到组件进行替换时, 一起作为替换元素
-->

		<div id="app">
			<cpn></cpn>

			<cpn><span>哈哈哈</span></cpn>
			<cpn><i>呵呵呵</i></cpn>
			<cpn>
				<i>呵呵呵</i>
				<div>我是div元素</div>
				<p>我是p元素</p>
			</cpn>

			<cpn></cpn>
			<cpn></cpn>
			<cpn></cpn>
			<cpn></cpn>
		</div>


		<template id="cpn">
			<div>
				<h2>我是组件</h2>
				<p>我是组件, 哈哈哈</p>
				<slot><button>按钮</button></slot>
				<!--<button>按钮</button>-->
			</div>
		</template>

		<script src="../js/vue.js"></script>
		<script>
			const app = new Vue({
				el: '#app',
				data: {
					message: '你好啊'
				},
				components: {
					cpn: {
						template: '#cpn'
					}
				}
			})
		</script>

	</body>
</html>

 

2.2slot-具名插槽的使用

当子组件的功能复杂时,子组件的插槽可能并非是一个。

  • 比如我们封装一个导航栏的子组件,可能就需要三个插槽,分别代表左边、中间、右边。
  • 那么,外面在给插槽插入内容时,如何区分插入的是哪一个呢?
  • 这个时候,我们就需要给插槽起一个名字

如何使用具名插槽呢?

  • 非常简单,只要给slot元素一个name属性即可
  • <slot name='myslot'></slot>
<!DOCTYPE html>
<html lang="en">
	<head>
		<meta charset="UTF-8">
		<title>Title</title>
	</head>
	<body>

		<div id="app">
			<cpn><span slot="center">标题</span></cpn>
			<cpn><button slot="left">返回</button></cpn>
		</div>


		<template id="cpn">
			<div>
				<slot name="left"><span>左边</span></slot>
				<slot name="center"><span>中间</span></slot>
				<slot name="right"><span>右边</span></slot>
			</div>
		</template>

		<script src="../js/vue.js"></script>
		<script>
			const app = new Vue({
				el: '#app',
				data: {
					message: '你好啊'
				},
				components: {
					cpn: {
						template: '#cpn'
					}
				}
			})
		</script>

	</body>
</html>

2.3什么是编译的作用域

  • 官方给出了一条准则:父组件模板的所有东西都会在父级作用域内编译;子组件模板的所有东西都会在子级作用域内编译。
  • 而我们在使用<my-cpn v-show="isShow"></my-cpn>的时候,整个组件的使用过程是相当于在父组件中出现的。
  • 那么他的作用域就是父组件,使用的属性也是属于父组件的属性。
  • 因此,isShow使用的是Vue实例中的属性,而不是子组件的属性。
<!DOCTYPE html>
<html lang="en">
	<head>
		<meta charset="UTF-8">
		<title>Title</title>
	</head>
	<body>

		<div id="app">
			<cpn v-show="isShow"></cpn>
			<cpn v-for="item in names"></cpn>
		</div>

		<template id="cpn">
			<div>
				<h2>我是子组件</h2>
				<p>我是内容, 哈哈哈</p>
				<button v-show="isShow">按钮</button>
			</div>
		</template>

		<script src="../js/vue.js"></script>
		<script>
			const app = new Vue({
				el: '#app',
				data: {
					message: '你好啊',
					isShow: true
				},
				components: {
					cpn: {
						template: '#cpn',
						data() {
							return {
								isShow: false
							}
						}
					},
				}
			})
		</script>

	</body>
</html>

2.4作用于插槽的案例

父组件替换插槽的标签,但是内容由子组件来提供

<!DOCTYPE html>
<html lang="en">
	<head>
		<meta charset="UTF-8">
		<title>Title</title>
	</head>
	<body>

		<div id="app">
			<cpn></cpn>

			<cpn>
				<!--目的是获取子组件中的pLanguages-->
				<template slot-scope="slot">
					<!--<span v-for="item in slot.data"> - {{item}}</span>-->
					<span>{{slot.data.join(' - ')}}</span>
				</template>
			</cpn>

			<cpn>
				<!--目的是获取子组件中的pLanguages-->
				<template slot-scope="slot">
					<!--<span v-for="item in slot.data">{{item}} * </span>-->
					<span>{{slot.data.join(' * ')}}</span>
				</template>
			</cpn>
			<!--<cpn></cpn>-->
		</div>

		<template id="cpn">
			<div>
				<slot :data="pLanguages">
					<ul>
						<li v-for="item in pLanguages">{{item}}</li>
					</ul>
				</slot>
			</div>
		</template>
		<script src="../js/vue.js"></script>
		<script>
			const app = new Vue({
				el: '#app',
				data: {
					message: '你好啊'
				},
				components: {
					cpn: {
						template: '#cpn',
						data() {
							return {
								pLanguages: ['javascript', 'C++', 'Java', 'C#', 'Python', 'Go', 'Swift']
							}
						}
					}
				}
			})
		</script>


	</body>
</html>


3.前端模块化

JavaScript原始功能

在网页开发的早期,js制作作为一种脚本语言,做一些简单的表单验证或动画实现等,那个时候代码还是很少的。

  • 那个时候的代码是怎么写的呢?直接将代码写在<script>标签中即可

随着ajax异步请求的出现,慢慢形成了前后端的分离

  • 客户端需要完成的事情越来越多,代码量也是与日俱增。
  • 为了应对代码量的剧增,我们通常会将代码组织在多个js文件中,进行维护。
  • 但是这种维护方式,依然不能避免一些灾难性的问题。

常见的模块化规范: CommonJS、AMD、CMD,也有ES6的Modules

3.1CommonJS

index.html 

<!DOCTYPE html>
<html lang="en">
	<head>
		<meta charset="UTF-8">
		<title>Title</title>
	</head>
	<body>

		<!--
1.项目组长
2.小明
3.小红
-->

		<script src="main.js"></script>
		<script src="aaa.js"></script>
		<script src="mmm.js"></script>
		<script src="bbb.js"></script>
		<script src="nnn.js"></script>

	</body>
</html>

aaa.js

var moduleA = (function() {
	// 导出的对象
	var obj = {}

	// 小明
	var name = '小明'
	var age = 22

	function sum(num1, num2) {
		return num1 + num2
	}

	var flag = true

	if (flag) {
		console.log(sum(10, 20));
	}


	obj.flag = flag;
	obj.sum = sum;

	return obj
})()

// 小明
// var name = '小明'
// var age = 22
//
// function sum(num1, num2) {
//   return num1 + num2
// }
//
// var flag = true
//
// if (flag) {
//   console.log(sum(10, 20));
// }
//
// module.exports = {
//   flag: flag,
//   sum: sum
// }

bbb.js

;
var moduleB = (function() {
	var obj = {}

	var name = '小红'
	var flag = false

	console.log(name);

	obj.flag = flag

	return obj
})()

mmm.js

;
(function() {
	// 1.想使用flag
	if (moduleA.flag) {
		console.log('小明是天才, 哈哈哈');
	}

	// 2.使用sum函数
	console.log(moduleA.sum(40, 50));
})()

// var aaa = require('./aaa.js')
// var flag = aaa.flag;
// var sum = aaa.sum;
// var {flag, sum} = require('./aaa.js')

// sum(20, 30)

nnn.js

(function() {
	console.log(moduleB.flag);
})()

3.2ES6的模块化实现

index.html

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Title</title>
</head>
<body>

<script src="aaa.js" type="module"></script>
<script src="bbb.js" type="module"></script>
<script src="mmm.js" type="module"></script>
</body>
</html>

aaa.js

var name = '小明'
var age = 18
var flag = true

function sum(num1, num2) {
	return num1 + num2
}

if (flag) {
	console.log(sum(20, 30));
}

// 1.导出方式一:
export {
	flag,
	sum
}

// 2.导出方式二:
export var num1 = 1000;
export var height = 1.88


// 3.导出函数/类
export function mul(num1, num2) {
	return num1 * num2
}

export class Person {
	run() {
		console.log('在奔跑');
	}
}

// 5.export default
// const address = '北京市'
// export {
//   address
// }
// export const address = '北京市'
// const address = '北京市'
//
// export default address

export default function(argument) {
	console.log(argument);
}

bbb.js

import {
	sum
} from './aaa.js'

var name = '小红'
var flag = false

console.log(sum(100, 200));

mmm.js

// 1.导入的{}中定义的变量
import {
	flag,
	sum
} from "./aaa.js";

if (flag) {
	console.log('小明是天才, 哈哈哈');
	console.log(sum(20, 30));
}

// 2.直接导入export定义的变量
import {
	num1,
	height
} from "./aaa.js";

console.log(num1);
console.log(height);

// 3.导入 export的function/class
import {
	mul,
	Person
} from "./aaa.js";

console.log(mul(30, 50));

const p = new Person();
p.run()

// 4.导入 export default中的内容
import addr from "./aaa.js";

addr('你好啊');

// 5.统一全部导入
// import {flag, num, num1, height, Person, mul, sum} from "./aaa.js";

import * as aaa from './aaa.js'

console.log(aaa.flag);
console.log(aaa.height);

 

 

 

 

 

以上是关于Vue第四天学习笔记之组件化高级的主要内容,如果未能解决你的问题,请参考以下文章

学习Vue第四天

vue视频学习笔记05

vue学习笔记之$refs

第十四天学习笔记

《JavaScript高级程序设计(第四版)》学习笔记第4章

《JavaScript高级程序设计(第四版)》学习笔记第4章