彻底掌握Vue全家桶

331 阅读4分钟

大纲

核心成员

vue:核心功能---插件,组件,数据管理,生命周期

vue-router:路由机制---拆分,拆开,将文件划分大小

vuex:vue官方提供的数据共享机制,全局性管理数据

编译打包

webpack:打包---js,css,图片,包的分隔,提供了很多开发功能

vue-loader:转换器

cli:脚手架,创建默认启动项目

开发支持

vue-devtools:按照组件查找

后端渲染

​ 前端渲染:性能更高,用户体验更好;所有的代码都需要用js写,所有的页面都需要被用户看到;

​ 后端渲染:安全性高,SEO

​ 结合:

​ vue-server-renderer:后端渲染的核心组件

​ nuxt.js:整合环境

UI组件

​ element-UI

​ vue-element-admin

Vue基础

历史介绍

angular 09年,复杂,学习曲线长,一开始被大家拒绝

react 13年,用户体验良好

vue 14年,用户体验良好,尤雨溪,无锡人

前端框架与库的区别

  • jquery库 -> DOM(操作DOM) +请求
  • art-template库 -> 模板引擎
  • 框架 = 全方位功能齐全
    • 简单的DOM体验 + 发请求 + 模板引擎 + 路由功能
  • KFC的世界里,库就是一个小小的套餐,框架就是全家桶
  • 代码上的不同
    • 一般使用库的代码,是调用某个函数,我们自己把控库的代码
    • 一般使用框架,其框架在帮助我们运行编写好的代码
      • 框架:初始化一些行为
        • 执行你所编写的代码
        • 释放一些资源

vue起步

  • 引包

  • 启动new Vue({el: 目的地,template:模板内容});

  • opations

    • 目的地 el
    • 数据源 data
    • 模板 template

插值表达式({{}})

<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8">
		<title>vue的基本使用</title>
		<!-- 1 引入vue包 -->
		<script src="./node_modules/vue/dist/vue.min.js"></script>
	</head>
	<body>
		<div id="app">
			<!-- 数据绑定 -->
			<!-- vue的模板语法 {{}} 双括号 -->
			<h2>{{ name }}</h2>	<!-- 绑定数据值 -->
			<h3>{{ 1+1 }}</h3>	<!-- 做运算 -->
			<h4>{{ isTure }}</h4>	<!-- 判断真假 -->
			<h5>{{ str.split('').reverse().join('') }}</h5>	<!-- 使用js中的方法 -->
			<h1>{{ 1 > 2 ? '真' : '假' }}</h1>	<!-- 使用三元表达式 -->
			<h6>{{ {"name" : "张三"} }}</h6>		<!-- 插入对象 -->
		</div>
		<script type="text/javascript">
			// 2 创建实例化对象
			var app = new Vue({
				el: '#app',	 // el 挂载点,vue操作的对象
				data: {	// 数据属性
					// 操作对象绑定的数据
					// 既可以是一个对象,也可以是一个函数
					name: '小陈',
					hobby: '电子竞技',	//data中数据发生改变,视图也会发生改变,简而言之,数据驱动视图
					isTure: 1===1,
					str: 'hello vue'
				},
				// 如果template中定义了内容 那么优先渲染 template 模板中的内容
				// 如果没有定义内容,则加载的是#app中的模板
				<!-- template: `<div>{{ hobby }}</div>` -->
				template: ``
			});
			// 除了数据属性 vue 实例还暴露了一些有用的实例属性和方法
			// 她们都有前缀$
			console.log(app);
			// 我们可以通过app.$数据名来获取vue实例中的数据
			console.log(app.$data);
		</script>
	</body>
</html>

在这里插入图片描述

指令

  • 在vue中提供了一些对于页面 + 数据的 跟为方便的输出,这些输出就成为指令,以v-xxx为例。
    • 比如html中的属性<div v-xxx></div>
  • 在angular中以ng-xxx开头
  • 在微信小程序中以vx-xxx开头
  • 在vue中以v-xxx开头的就叫做指令
  • 指令中封装了一些DOM行为,结合属性作为一个暗号,暗号有对应的值,根据不同的值,框架会进行相关的DOM操作的绑定

vue中常用的指令

  • v-text:元素的innerText属性,必须是双标签,跟{{}}效果是一样的,使用较少

  • v-html:元素的innerHtml

  • v-if :判断是否插入这个元素,相当于元素的销毁和创建

  • v-else-if

  • v-else

  • v-show:隐藏元素,如果元素确定要隐藏,会给元素的style加上display:none,是基于css样式的切换

  • v-bind:给标签进行属性绑定(类似于:hover--给元素添加属性)【简写 :bind】

  • v-on:绑定事件

    • v-on:原生事件名 = '函数名' 【简写 @】
  • v-for:遍历

    • v-for = "(item, index) in 数组名/对象名" ----> 使用{{ itme.字段名 }}
    v-text:只能在双标签中使用,其实就是给元素的innerText赋值
    v-html:其实就是给元素的innerHTML赋值
    v-if:如果值为false,不在页面上渲染,会留下<!---->作为标记,但是可视化界面上面没有显示,如果值为true就将值显示在可视化界面上面
    v-if,v-else-if都有相对应的值,v-else直接写
    v-show是控制DOM元素显示与否,true显示,false隐藏
    
<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8">
		<title>vue的基本使用</title>
		<style type="text/css">
			.obj {
				width: 200px;
				height: 200px;
				border: 1px solid;
			}
			.obj2 {
				width: 200px;
				height: 200px;
				border: 1px solid;
			}
			.active {
				background-color: aqua;
			}
			table{
				/* 去掉表格中的横向 内部边框线 */
				 border-collapse: collapse;	/* 表格单元格间距样式 */
			}
		</style>
		<!-- 1 引入vue包 -->
		<script src="./node_modules/vue/dist/vue.min.js"></script>
	</head>
	<body>
		<div id="app">
		
		</div>
		<script type="text/javascript">
			// 2 创建实例化对象
			new Vue({
				el: '#app',
				data: function() {
					return {
						msg: 'v-text指令',
						msg2: '<i>斜体i标签</i>',
						isShow: false,
						isBlue: false,
						isRed: false,
						text: 'chenhaha',
						listData: [
							{id: 1, name: '小陈', sex: '女'},
							{id: 2, name: '小王', sex: '女'},
							{id: 3, name: '小姜', sex: '女'},
							{id: 4, name: '小唐', sex: '女'},
							{id: 5, name: '小黎', sex: '女'},
							{id: 6, name: '小肖', sex: '女'}
						],
						person: {
							name: '筱辰',
							hobby: '街舞,rap,吉他',
							age: 20
						}
					}
				},
				template: `
					<div id="app">
						// 通过text做数据的增加
						<h2 v-text = "1 + 1"></h2>
						
						<!-- 给标签添加text值 -->
						<h3 v-text = "msg"></h3>
						
						<!-- 给标签嵌套html标签innerHTML -->
						<div v-html = "msg2"></div>
						
						<!-- 根据条件的真假输出对应项的值 -->
						<div v-if = 'isShow'>雨天,写作业</div>
						<div v-if = '!isShow'>晴天,出去耍,晒太阳</div>
						
						
						<div v-if = 'Math.random() > 0.5'>随机数大于0.5</div>
						<div v-else>随机数小于0.5</div>
						
						<!-- 根据属性值来让元素显示隐藏,实际上是改变display的值 none|block -->
						<div v-show = 'isShow'>夏天</div>
						<div v-show = '!isShow'>夏天</div>
						
						<!-- v-bind 绑定class  通过属性值来让元素增加隐藏 -->
						<div class = "obj" v-bind:class = "{active:!isBlue}" v-on:click = 'clickDiv'></div>
						<!-- v-bind还可以绑定自定义属性 -->
						<div v-bind:aaa = 'text'></div>
						
						<!-- 遍历数组 -->
						<table border="1">
							<tr>
								<th>编号</th>
								<th>姓名</th>
								<th>性别</th>
							</tr>
							<tr   v-for = '(item, index) in listData'>
								<td>{{ item.id }}</td>
								<td>{{ item.name }}</td>
								<td>{{ item.sex }}</td>
							</tr>
						</table>
						
						<!-- 遍历对象 -->
						<ul>
							<li v-for = "(value,key) in person">
								{{ key }} -------> {{ value }}
							</li>
						</ul>
					</div>
				`,
				methods: {
					clickDiv(e) {
						this.isBlue = !this.isBlue;
					}
				}
			});
		</script>
	</body>
</html>

v-if和v-show的区别

v-if 是 “真正”的条件渲染,因为他会确保在切换过程中条件块内的事件监听器和子组件适当的被销毁和重建

v-if也是惰性的:如果在初始渲染时条件为假,则什么也不做——知道条件第一次变为真时,才开始渲染条件块。

相比之下,v-show就简单得多——不管初始条件是什么,元素总是会被渲染,并且只是简单的基于css进行切换。(导航栏)

一般来说,v-if有更高的切换开销,而v-show有更高的初始渲染开销。因此,如果需要非常频繁地切换,则使用v-show较好,如果在运行时条件很少改变,则使用v-if较好。

vue的双向绑定(v-model)

<!DOCTYPE html>
<html lang="zh">
	<head>
		<meta charset="UTF-8">
		<meta name="viewport" content="width=device-width, initial-scale=1.0">
		<meta http-equiv="X-UA-Compatible" content="ie=edge">
		<title></title>
		<!-- 1 引入vue包 -->
		<script src="./node_modules/vue/dist/vue.min.js"></script>
	</head>
	<body>
		<div id="app">
			<!-- 单项数据绑定 -->
			<span>单项数据绑定:</span>
			<input type="text" :value="mag"/>
			<hr />
			<!-- 双向数据绑定   只会体现在UI控件上面  只能应用在value属性上 -->
			<span>双向数据绑定:</span>
			<input type="text" v-model="msg"/>
			<h1>{{ msg }}</h1>
			<hr />
			<!-- 实际上就是一个 语法糖,它是v-bind:value v-on:input的体现 -->
			<span>双向数据实现原理:</span>
			<input type="text" v-bind:value="mfg" v-on:input="valueChange"/>
			<h1>{{ mfg }}</h1>
		</div>
		<script type="text/javascript">
			new Vue({
				el: '#app',
				data() {
					return {
						mag: "小猪佩奇",
						msg: "好友记",
						mfg: "肖申克的救赎"
					}
				},
				methods: {
					valueChange(e){
						console.log(e.target.value);
						this.mfg = e.target.value;
					}
				}
			});
		</script>
	</body>
</html>

在这里插入图片描述

vue中的template(入口组件)

入口组件包含了:html,css,js

在这里插入图片描述

局部组件

打油诗:声子 挂子 用子

创建:(类似于自定义全局组件,使用时直接在父组件中,通过局部组件的声明名调用即可)

<!DOCTYPE html>
<html lang="zh">
	<head>
		<meta charset="UTF-8">
		<meta name="viewport" content="width=device-width, initial-scale=1.0">
		<meta http-equiv="X-UA-Compatible" content="ie=edge">
		<title></title>
		<!-- 1 引入vue包 -->
		<script src="./node_modules/vue/dist/vue.min.js"></script>
	</head>
	<body>
		<div id="app"></div>
		<script type="text/javascript">
			// 局部入口组件的的声明
			var App = {
				data() {
					return {
						
					}
				},
                // 局部入口组件的内容
				template: 
				`
					<h2>我是局部入口组件</h2>
				`
			};
			// 2 实例化对象
			new Vue({
				el: "#app",
				data() {
					return {
						
					}
				},
				<!-- 挂载子组件 -->
				components: {
					App	<!-- 类似于App:App -->
				},
				<!-- 父组件可以直接使用子组件 -->
				template: 
				`
					<app></app>	
				`
			});
		</script>
	</body>
</html>

全局组件

// 全局组件
// 第一个参数是组件的名字,第二个参数是组件的样式
Vue.component('V-button',{
	template:
		`
			<button>按钮</button>
		`
});

注意:在使用局部组件和全局组件时,不能单独使用,每个组件都是一个整体,在组件中使用全局组件和局部组件时,必须要写在组件的内部,它们是一个整体。

<!DOCTYPE html>
<html lang="zh">
	<head>
		<meta charset="UTF-8">
		<meta name="viewport" content="width=device-width, initial-scale=1.0">
		<meta http-equiv="X-UA-Compatible" content="ie=edge">
		<title></title>
		<!-- 1 引入vue包 -->
		<script src="./node_modules/vue/dist/vue.min.js"></script>
	</head>
	<body>
		<div id="app"></div>
		<script type="text/javascript">
			// 全局组件
			// 第一个参数是组件的名字,第二个参数是组件的样式
			Vue.component('V-button',{
				template:
				`
					<button>按钮</button>
				`
			});
			
			// 声子
			var Rightcentent = {
				data() {
					return {
						
					}
				},
				template: 
				`
					<div>
						<div>我是右边内容组件</div>
						<V-button></V-button>
					</div>
				`
			};
			<!-- 侧边栏组件 -->
			var Leftcentent = {
				data() {
					return {
						
					}
				},
				template: 
				`
					<div>我是左边菜单栏组件</div>
				`
			};
			<!-- 头部组件 -->
			var Header = {
				data() {
					return {
						
					}
				},
				template: 
				`
					<div>我是头部组件</div>
				`
			};
			// 局部入口组件的的声明
			var App = {
				data() {
					return {
						
					}
				},
				template: <!-- 用子 	注意:在使用创建的子组件是是一个整体,不能分散开来,不然只能显示第一个子组件的值-->
				`
					<div>
						<Header></Header>
						<div>
							<Leftcentent></Leftcentent>	
							<Rightcentent></Rightcentent>
						</div>
					</div>	
				`,
				<!-- 挂子 -->
				components: {
					Header,
					Leftcentent,
					Rightcentent
				}
			};
			// 2 实例化对象
			new Vue({
				el: "#app",
				data() {
					return {
						
					}
				},
				<!-- 挂载子组件 -->
				components: {
					App	<!-- 类似于App:App -->					
				},
				<!-- 父组件可以直接使用子组件 -->
				template: 
				`
					<app></app>						
				`
			});
		</script>
	</body>
</html>

父组件往子组件中传值

1 先给父组件绑定自定义属性 2 在子组件中使用prop接收父组件传递的数据 3 只要使用props接收了数据之后,就可以在子组件中任意使用

<!DOCTYPE html>
<html lang="zh">
	<head>
		<meta charset="UTF-8">
		<meta name="viewport" content="width=device-width, initial-scale=1.0">
		<meta http-equiv="X-UA-Compatible" content="ie=edge">
		<title></title>
		<!-- 1 引入vue包 -->
		<script src="./node_modules/vue/dist/vue.min.js"></script>
	</head>
	<body>
		<div id="app"></div>
		<script type="text/javascript">
			// 声明全局组件    子组件
			Vue.component('Child',{
				template: 
				`
					<div>
						<p>我是孩子</p>
						<input type="text" v-model="childData"/>
					</div>
				`,
				<!-- 接收自定义属性 -->
				props: ['childData']
			});
			<!--  声明全局组件    父组件 -->
			<!-- 1 先给父组件绑定自定义属性 -->
			<!-- 2 在子组件中使用prop接收父组件传递的数据 -->
			<!-- 3 只要使用props接收了数据之后,就可以在子组件中任意使用 -->
			Vue.component('Parent',{
				data() {
					return {
						msg: '我是父组件中的数据'
					}
				},
				template: 
				`
					<div>
						<p>我是父亲</p>
						<!-- 在子组件中挂载自定义属性 -->
						<Child :childData = 'msg'/>
					</div>
				`
			});
			// 声子
			var App = {
				template:
				`
					<div>
						<Parent />
					</div>
				`
			};
			// 2 实例化对象
			new Vue({
				el: "#app",
				data() {
					return {
						
					}
				},
				<!-- 挂载子组件 -->
				components: {
					App	<!-- 类似于App:App -->					
				},
				<!-- 父组件可以直接使用子组件 -->
				template: 
				`
					<app></app>						
				`
			});
		</script>
	</body>
</html>

子组件往父组件中传值

1.在父组件中绑定自定义事件 2.在子组件中触发原生的事件,在函数中使用$emit触发自定义函数

<!DOCTYPE html>
<html lang="zh">
	<head>
		<meta charset="UTF-8">
		<meta name="viewport" content="width=device-width, initial-scale=1.0">
		<meta http-equiv="X-UA-Compatible" content="ie=edge">
		<title></title>
		<!-- 1 引入vue包 -->
		<script src="./node_modules/vue/dist/vue.min.js"></script>
	</head>
	<body>
		<div id="app"></div>
		<script type="text/javascript">
			// 声明全局组件    子组件
			Vue.component('Child',{
				template: 
				`
					<div>
						<p>我是孩子</p>
						<input type="text" v-model="childData" @input="changeValue(childData)"/>
					</div>
				`,
				<!-- 接收自定义属性 -->
				props: ['childData'],
				methods: {
					changeValue(val){
						<!-- 自定义的时间一定通过thi.$emit()去触发 -->
						<!-- 在函数中使用$emit(自定义的事件名,传递的消息) -->
						this.$emit('childHeader',val);
					}
				}
			});
			<!-- 1.在父组件中绑定自定义事件 -->
			<!-- 2.在子组件中触发原生的事件,在函数中使用$emit触发自定义函数 -->
			Vue.component('Parent',{
				data() {
					return {
						msg: '我是父组件中的数据'
					}
				},
				template: 
				`
					<div>
						<p>我是父亲</p>
						<!-- 在子组件中挂载自定义属性 -->
						<Child :childData = 'msg' @childHeader = 'childHeader'/>
					</div>
				`,
				methods: {
					childHeader(val){
						console.log(val);
					}
				}
			});
			// 声子
			var App = {
				template:
				`
					<div>
						<Parent />
					</div>
				`
			};
			// 2 实例化对象
			new Vue({
				el: "#app",
				data() {
					return {
						
					}
				},
				<!-- 挂载子组件 -->
				components: {
					App	<!-- 类似于App:App -->					
				},
				<!-- 父组件可以直接使用子组件 -->
				template: 
				`
					<app></app>						
				`
			});
		</script>
	</body>
</html>

vue内置全局组件(插槽)

插槽:slot 插槽,作为承载分发内容的出口

<!DOCTYPE html>
<html lang="zh">
	<head>
		<meta charset="UTF-8">
		<meta name="viewport" content="width=device-width, initial-scale=1.0">
		<meta http-equiv="X-UA-Compatible" content="ie=edge">
		<title></title>
		<style type="text/css">
			.default {
				display: inline-block;
				line-height: 1;
				white-space: nowrap;
				cursor: pointer;
				background: #fff;
				border: 1px solid #dcdfe6;
				color: #606266;
				-webkit-appearance: none;
				text-align: center;
				box-sizing: border-box;
				outline: none;
				margin: 0;
				transition: .1s;
				font-weight: 500;
				-moz-user-select: none;
				-webkit-user-select: none;
				-ms-user-select: none;
				padding: 12px 20px;
				font-size: 14px;
				border-radius: 4px;
			}

			.primary {
				color: #fff;
				background-color: #409eff;
				border-color: #409eff;
			}

			.success {
				color: #fff;
				background-color: green;
				border-color: #409eff;
			}
		</style>
		<!-- 1 引入vue包 -->
		<script src="./node_modules/vue/dist/vue.min.js"></script>
	</head>
	<body>
		<div id="app"></div>
		<script type="text/javascript">
			// 通过插槽的方式封装全局组件
			Vue.component('Vbtn', {
				template: `
					<button class = 'default' :class = 'type'>
						<slot>
							按钮
						</slot>
					</button>
				`,
				props: ['type']
			});
			<!-- 声子 -->
			var Vcontent = {
				template: `
					<div>
						我是内容组件
						<Vbtn type = 'primary'>主要按钮</Vbtn>
						<Vbtn type = 'success'>成功按钮</Vbtn>
					</div>
				`
			};
			// 2 实例化对象
			new Vue({
				el: "#app",
				data() {
					return {

					}
				},
				<!-- 挂子 -->
				components: {
					Vcontent <!-- 类似于App:App -->					
				},
				<!-- 用子 -->
				template: `
					<Vcontent></Vcontent>						
				`
			});
		</script>
	</body>
</html>

在这里插入图片描述

在这里插入图片描述

具名插槽(通过slot上的name)

<!DOCTYPE html>
<html lang="zh">
<head>
	<meta charset="UTF-8">
	<meta name="viewport" content="width=device-width, initial-scale=1.0">
	<meta http-equiv="X-UA-Compatible" content="ie=edge">
	<title></title>
	<script src="./node_modules/vue/dist/vue.min.js" type="text/javascript" charset="utf-8"></script>
</head>
<body>
	<div id="app"></div>
	
	<script type="text/javascript">
		Vue.component('myli',{
			template: 
			`
				<li>
					预留的第一个空白
					<slot name = 'two'></slot>
					
					预留的第二个空白
					<slot name = 'three'></slot>
				</li>
			`
		})
		var App = {
			template: 
			`
				<div>
					<ul>
						<myli>
							<h2 slot = 'two'>我是第一个空白</h2>
							<h2 slot = 'three'>我是第二个空白</h2>
						</myli>
					</ul>
				</div>
			`
		}
		<!-- 实例化vue -->
		new Vue({
			el: '#app',
			components: {
				App
			},
			template: `<App></App>`
		});
	</script>
</body>
</html>

在这里插入图片描述

过滤器

过滤器的功能:就是为页面中的数据进行添油加醋的功能

过滤器的分类:

  1. 局部过滤器
  2. 全局过滤器
<!DOCTYPE html>
<html lang="zh">
<head>
	<meta charset="UTF-8">
	<meta name="viewport" content="width=device-width, initial-scale=1.0">
	<meta http-equiv="X-UA-Compatible" content="ie=edge">
	<title></title>
	<script src="./node_modules/vue/dist/vue.min.js" type="text/javascript" charset="utf-8"></script>
</head>
<body>
	<div id="app"></div>
	<script type="text/javascript">
		// 过滤器的功能:就是为页面中的数据进行添油加醋的功能
		// 有两种:局部过滤器 全局过滤器
		
		// 声明全局过滤器
		Vue.filter('myReverse', function(value,arg) {
			return arg + value.split('').reverse().join('');
		});
		var App = {
			data() {
				return {
					price: 0,
					msg: 'filter'
				}
			},
			template: 
			`
				<div>
					<input type="number" v-model = 'price'/>
					<!-- 使用局部过滤器  {{ 数据 | 过滤器名 }} -->
					<h2>{{ price | myCurrentcy }}</h2>	
					
					<!-- 使用全局过滤器 -->
					<h4>{{ msg | myReverse('I am going to study well you ') }}</h4>
				</div>
			`,
			filters: {
				<!-- 1 声明局部过滤器 -->
				myCurrentcy: function(value) {
					return "¥" + value;
				}
			}
		}
		<!-- 实例化vue -->
		new Vue({
			el: '#app',
			components: {
				App
			},
			template: `<App></App>`
		});
	</script>
</body>
</html>

监视(watch)

watch监听的是单个属性。

基本的数据类型,简单监视。

复杂的数据类型深度监视。

<!DOCTYPE html>
<html lang="zh">
	<head>
		<meta charset="UTF-8">
		<meta name="viewport" content="width=device-width, initial-scale=1.0">
		<meta http-equiv="X-UA-Compatible" content="ie=edge">
		<title></title>
		<script src="./node_modules/vue/dist/vue.min.js" type="text/javascript" charset="utf-8"></script>
	</head>
	<body>
		<div id="app">
			<input type="text" v-model="msg">
			<h2>{{ msg }}</h2>
			<button type="button" @click="stus[0].name = 'rose'">获取用户名</button>
			<h4>{{ stus[0].name }}</h4>
		</div>
		<script type="text/javascript">
			new Vue({
				el: '#app',
				data() {
					return {
						msg: '',
						stus: [{name: 'jack'}]
					}
				},
				watch: {	
					//简单监视
					// 字符串
					msg: function(newV, oldV) {
						console.log(newV, oldV);
					},
					//深度监视
					stus:{
						deep: true,	
						handler:function(newV, oldV) {
							console.log(newV, oldV);
							console.log(newV[0].name);
						}
					}
				}
			})
		</script>
	</body>
</html>

计算属性

利用computed计算属性写网页版音乐播放器。

<!DOCTYPE html>
<html lang="zh">
	<head>
		<meta charset="UTF-8">
		<meta name="viewport" content="width=device-width, initial-scale=1.0">
		<meta http-equiv="X-UA-Compatible" content="ie=edge">
		<title></title>
		<style type="text/css">
			*{
				padding: 0;
				margin: 0;
			}
			ul{
				list-style: none;
			}
			ul li {
				margin: 20px 20px;
				padding: 20px 20px;
			}
			.active{
				background-color: aquamarine;
			}
		</style>
		<script src="./node_modules/vue/dist/vue.min.js" type="text/javascript" charset="utf-8"></script>
	</head>
	<body>
		<div id="app">
			<audio :src='getCurrentSongSrc' autoplay="autoplay" controls="controls"></audio>
			<ul>
				<li v-for="(item,index) in musicData" @click="clickHander(index)" :class="{active:currentIndex == index}">
					<h2>{{ item.id }} - 歌名:{{ item.name }}h2>
					<p>歌曲来源:{{ item.sonSrc }}</p>
				</li>
			</ul>
		</div>
		<script type="text/javascript">
			var musicData = [
				{
					id: 1,
					name: '世界美好与你环环相扣',
					author: '柏松',
					sonSrc: 'music/世界美好与你环环相扣.mp3'
				},
				{
					id: 2,
					name: '昨天的你现在的未来',
					author: '易烊千玺',
					sonSrc: 'music/昨天的你现在的未来.mp3'
				},
				{
					id: 3,
					name: '精彩才刚刚开始',
					author: '易烊千玺',
					sonSrc: 'music/精彩才刚刚开始.mp3'
				}
			];
			new Vue({
				el: '#app',
				data() {
					return {
						musicData:musicData,
						currentIndex:0
					}
				},
				computed:{
					// 计算属性默认只有getter
					// setter也可以
					
					// 使用getter
					// getCurrentSongSrc:function(){
					// 	return this.musicData[this.currentIndex].sonSrc
					// }
					
					// 使用setter
					getCurrentSongSrc:{
						set: function(newV){
							console.log(newV);
							this.currentIndex = newV;
						},
						get:function(){
							return this.musicData[this.currentIndex].sonSrc
						}
						
					}
				},
				methods:{
					clickHander(index){
						// 使用getter
						// 直接修改数据的属性
						// this.currentIndex = index;
						
						// 使用setter
						console.log(this.getCurrentSongSrc);
						this.getCurrentSongSrc = index;
					}
				}
			})
		</script>
	</body>
</html>

在这里插入图片描述

组件的生命周期

  • beforeCreate

    • 组件创建之前
  • created

    • 组件创建之后,就可以拿到自定义组件里面的内容
  • beforeMount

    • 挂载数据到DOM之前会调用
  • mounted

    • 挂载数据到DOM之后
  • beforeUpdate

    • 在更新DOM之前调用该钩子,应用:可以获取原始的DOM
  • updated

  • 在更新DOM之后调用该钩子,应用:可以获取原始的DOM

  • beforeDeatory

    • 销毁数据之前
  • destroyed

    • 销毁数据之后
  • activated

    • 激活组件
  • deactivated

    • 停用组件
    <!DOCTYPE html>
    <html lang="zh">
    	<head>
    		<meta charset="UTF-8">
    		<meta name="viewport" content="width=device-width, initial-scale=1.0">
    		<meta http-equiv="X-UA-Compatible" content="ie=edge">
    		<title></title>
    		<script src="./node_modules/vue/dist/vue.min.js" type="text/javascript" charset="utf-8"></script>
    	</head>
    	<body>
    		<div id="app">
    			<App></App>
    		</div>
    		<script type="text/javascript">
    			Vue.component('Test', {
    				data() {
    					return {
    						msg: 'lorry'
    					}
    				},
    				template: `
    				<div>
    					<h2>{{ msg }}</h2>
    					<button @click = 'changerHanlder'>改变</button>
    				</div>
    			`,
    				methods: {
    					changerHanlder() {
    						this.msg = this.msg + 'hello';
    					}
    				},
    				beforeCreate: function() {
    					<!-- 组件创建之前 -->
    					console.log(this.msg); <!-- 打印输出undefined -->
    				},
    				created: function() {
    					<!-- 组件创建之后 -->
    					console.log(this.msg); <!-- 打印输出lorry -->
    					<!-- 使用该组件,就会调用created方法 -->
    					<!-- 在created这个方法中可以操作后端的数据,数据驱动视图 -->
    					<!-- 应用:发起ajax请求 -->
    				},
    				beforeMount: function() {
    					<!-- 挂载数据之前 -->	<!-- 打印输出<div id="app"><app></app></div> -->
    					console.log(document.getElementById('app'));
    				},
    				mounted: function() {
    					<!-- 挂载数据到DOM之后 		Vue作用之后的DOM-->
    					<!-- 打印输出<div id="app"><div class="app"><div><h2>lorry</h2></div></div></div> -->
    					console.log(document.getElementById('app'));
    				},
    				beforeUpdate: function() {
    					<!-- 在更新Dom之前使用改方法 -->
    					<!-- 打印输出 <div class="app"><div><h2>lorry</h2> <button>改变</button></div></div> -->
    					console.log(document.getElementById('app').innerHTML);
    				},
    				updated: function() {
    					<!-- 在更新DOM之后使用该方法 -->
    					<!-- 打印输出 <div class="app"><div><h2>lorryhello</h2> <button>改变</button></div></div> -->
    					console.log(document.getElementById('app').innerHTML);
    				},
    				beforeDeatory: function() {
    					<!-- 销毁数据之前 -->
    					console.log('beforeDeatory'); <!-- Test组件中内容的显示隐藏的时候,打印输出beforeDeatory -->
    				},
    				destroyed: function() {
    					<!-- 销毁数据之后 -->
    					console.log('destroyed'); <!-- Test组件中内容的显示显示的时候,打印输出destroyed -->
    				},
    				activated: function() {
    					console.log('组件被激活了');
    				},
    				deactivated: function() {
    					console.log('组件被停用了');
    				},
    			});
    			var App = {
    				data() {
    					return {
    						isShow: true
    					}
    				},
    				template: `
    				<div class = "app">
    					<!-- <Test v-if = 'isShow'></Test> -->
    					
    					<!-- vue中的内置组件<keep-alive></keep-alive>,能在组件的切换过程中将所有的状态保存在内存中,重复渲染DOM -->
    					<keep-alive>
    						<Test v-if = 'isShow'></Test>
    					</keep-alive>
    					<!-- 根据按钮点击事件确定 Test组件中内容的显示隐藏-->
    					<!-- 点击两次后,再次点击切换,后台会重新创建Test,然后再次调用beforeDeatory和destroyed -->
    					<button @click = 'isShow = !isShow'>点击切换</button>
    				</div>
    			
    			`
    			};
    			new Vue({
    				el: '#app',
    				data() {
    					return {
    
    					}
    				},
    				components: {
    					App
    				}
    			})
    		</script>
    	</body>
    </html>
    

keep-alive标签

在组件的切换过程中将所有的状态保存在内存中,重复渲染DOM(缓存)

keep-alive在路由中的使用

keep-alive保存加载过的数据,使用时直接调用,不用再次加载,节省项目运行时间,提高用户体验。

<!DOCTYPE html>
<html lang="zh">
<head>
	<meta charset="UTF-8">
	<meta name="viewport" content="width=device-width, initial-scale=1.0">
	<meta http-equiv="X-UA-Compatible" content="ie=edge">
	<title></title>
		<script src="./node_modules/vue/dist/vue.js" type="text/javascript" charset="utf-8"></script>
		<script src="./node_modules/vue-router/dist/vue-router.js" type="text/javascript" charset="utf-8"></script>
</head>
<body>
	<div id="app"></div>
	<script type="text/javascript">
		var Timeline = {
			template:
			`
				<div id = 'timeline'>
					<h2>首页</h2>
				</div>
			`,
			created() {
				console.log('首页组件创建了');
			},
			mounted() {
				console.log('首页组件DOM加载了');
			},
			destroyed() {
				console.log('首页组件销毁了');
			}
		};
		var Pins = {
			template:
			`
				<div>
					<h2 @click = 'clickHandle'>沸点</h2>
				</div>
			`,
			methods: {
				clickHandle(e) {
					e.target.style.color = 'red';
				}
			},
			created() {
				console.log('沸点组件创建了');
			},
			mounted() {
				console.log('沸点组件DOM加载了');
			},
			destroyed() {
				console.log('沸点组件销毁了');
			}
		};
		<!-- // 创建路由对象 -->
		var router = new VueRouter({
			<!-- mode: 'history',	哈希模式,页面跳转时路由不会出现#  -->
			routes:[
				{
					path: '/timeline',
					component: Timeline
				},
				{
					path: '/pins',
					component: Pins
				}
			]
		});
		<!-- // 实例化app组件 -->
		var App = {
			template:
			`
				<div>
					<!-- 动态路由路径 -->
					<router-link to = '/timeline'>首页</router-link>
					<router-link to = '/pins'>沸点</router-link>
					<!-- 路由匹配组件的出口 -->
					<!-- <router-view></router-view> -->
					<!-- 使用keep-alive后,如果前面的数据缓存过后,就不会重新加载,直接调用 -->
					<keep-alive>
						<router-view></router-view>	
					</keep-alive>
				</div>
			`
		}
		<!-- // 实例化对象 -->
		new Vue({
			el: '#app',
			router,
			template: `<App></App>`,
			<!-- 挂载组件 -->
			components: {
				App
			}
		})
	</script>
</body>
</html>

vue组件的通信方式

vue组件之间通信可分为:

props和$emit(也就是常说的父子组件通信,常用)【父子组件中的传值】

父组件向子组件传递数据是通过props传递的,子组件传递数据给父组件是通过$emit触发事件来做到的。

解决父子组件层数较少的情况

<!DOCTYPE html>
<html lang="en">
<head>
	<meta charset="UTF-8">
	<title></title>
</head>
<body>
	<div id="app"></div>
	<script src="https://cdn.bootcss.com/vue/2.5.17-beta.0/vue.js"></script>
	<script type="text/javascript">
		/*
			在下面的例子中,有父组件App和子组件Child。 
			1).父组件传递了message数据给子组件,并且通过v-on绑定了一个getChildData事件来监听子组件的触发事件; 
			2).子组件通过props得到相关的message数据,最后通过this.$emit触发了getChildData事件。

		*/
		Vue.component('Child',{
			data(){
				return {
					aaa:this.message
				}
			},
			template:`
				<div>
					<input type="text"  v-model="aaa" @input="passData(aaa)"/>
				</div>
			`,
			props:['message'],
			methods:{
				passData(val){
                    // $emit(自定义事件名,传递的值)
					this.$emit('getChildData',val);
				}
			}
		});
		var App = {
			data(){
				return {
					msg:'我是父组件的内容'
				}
			},
			methods:{
				getChildData(val){
					console.log(`我是子组件传进来的${val}`);
				}
			},
			template:`<div>
				<p>这是一个父组件</p>
				<Child :message = 'msg' @getChildData = "getChildData"></Child>

			</div>`
		}

		new Vue({
			el:"#app",
			data(){
				return {

				}
			},
			components:{
				App
			},
			template:`<App />`
		});




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

attrs(绑定属性)attrs(绑定属性)和listeners(监听) 【父子组件中的传值】

attrsattrs是pops的集合

解决多层组件之间的嵌套和传值方法

绑定事件,传值,然后通过事件来接收传递的值

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <title></title>
</head>

<body>
    <div id="app"></div>
    <script src="https://cdn.bootcss.com/vue/2.5.17-beta.0/vue.min.js"></script>
    <script type="text/javascript">
    /*
    $attrs和$listeners
    第一种方式处理父子组件之间的数据传输有一个问题: 如果父组件A下面有子组件B, 组件B下面有组件C, 这时如果组件A想传递数据给组件C怎么办呢?
    如果采用第一种方法, 我们必须让组件A通过prop传递消息给组件B, 组件B在通过prop传递消息给组件C; 要是组件A和组件C之间有更多的组件, 那采用这种方式就很复杂了。 Vue 2.4 开始提供了$attrs和$listeners来解决这个问题, 能够让组件A之间传递消息给组件C。
    */
      Vue.component('C', {
        data() {
            return {

            }
        },
        template: `
				<div>
					<div @click = 'cClickHandler'>{{$attrs.messagec}}</div>
				</div>
			`,
        methods: {
           cClickHandler(){
           	alert(1);
           	this.$emit('getCData','我是c的数据')
           }
        }
    });
      Vue.component('B', {
        data() {
            return {

            }
        },
        template: `
				<div>
					<C v-bind="$attrs" v-on = '$listeners'></C>
				</div>
			`,
        methods: {
           
        }
    });

    Vue.component('A', {
        data() {
            return {

            }
        },
        // props:['message'],
        template: `
				<div>
					<B v-bind="$attrs" v-on = '$listeners'></B>
					<!--<input type="text" v-model = '$attrs.messagec' />-->
				</div>
			`,
        methods: {
           
        }
    });
    var App = {
        data() {
            return {
                msg: '我是父组件的内容',
                messagec:'hello c'
            }
        },
        methods: {
          
        },
        template: `<div>
				<p>这是一个父组件</p>
				<A :messagec = 'messagec' v-on:getCData="getCData" ></A>

			</div>`,
	methods:{
		// 执行c组件的触发的函数
		getCData(val){
			console.log(val);
		}
	}
    };

    new Vue({
        el: "#app",
        data() {
            return {

            }
        },
        components: {
            App
        },
        template: `<App />`
    });
    </script>
</body>

</html>

在这里插入图片描述

中央事件总线(非父子组件间通信)(创建公共的类)

$on绑定自定义事件,$emit触发自定义事件

$on$emit绑定同一个实例化对象

解决兄弟组件之间的传值

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <title></title>
</head>

<body>
    <div id="app"></div>
    <script src="https://cdn.bootcss.com/vue/2.5.17-beta.0/vue.min.js"></script>
    <script type="text/javascript">
   /*
    上面两种方式处理的都是父子组件之间的数据传递,而如果两个组件不是父子关系呢?这种情况下可以使用中央事件总线的方式。新建一个Vue事件bus对象,然后通过bus.$emit触发事件,bus.$on监听触发的事件。
   */
      // 中央事件总线
      var bus = new Vue();
   
      Vue.component('brother2', {
        data() {
            return {
                msg:"hello brother1"
            }
        },
        template: `
				<div>
                                              <p>我是老大</p>
					<input type="text" v-model = 'msg' @input = 'passData(msg)' />
				</div>
			`,
        methods: {
           passData(val){
            // //触发全局事件globalEvent
            bus.$emit('globalEvent',val)
           }
        }
    });

    Vue.component('brother1', {
        data() {
            return {
                msg:"hello brother1",
                brother2Msg:''
            }
        },
        template: `
				<div>
					<p>我是老二</p>
					<p>老大传递过来的数据:{{brother2Msg}}</p>
				</div>
			`,
        
           mounted(){
                // 绑定全局事件globalEvent事件,
               bus.$on('globalEvent',(val)=>{
                    bus.brother2Msg = val;
               })
           }
    });
    var App = {
        data() {
            return {
                msg: '我是父组件的内容',
                messagec:'hello c'
            }
        },
        methods: {
          
        },
        template: `<div>
				<brother1></brother1>
                  <brother2></brother2>


			</div>`,
	methods:{
		// 执行c组件的触发的函数
		getCData(val){
			console.log(val);
		}
	}
    }

    new Vue({
        el: "#app",
        data() {
            return {

            }
        },
        components: {
            App
        },
        template: `<App />`
    });
    </script>
</body>

</html>

在这里插入图片描述

provide(传值)和inject(接收)【父组件传值,子组件接收】

父组件中通过provide来提供变量,然后在子组件中通过inject来注入变量。不论子组件有多深,只要调用了inject那么就可以注入provider中的数据。而不是局限于只能从当前父组件的prop属性来获取数据,只要在父组件的生命周期内,子组件都可以调用。

<!DOCTYPE html>
<html lang="en">
<head>
	<meta charset="UTF-8">
	<title></title>
</head>
<body>
	<div id="app"></div>
    <script src="https://cdn.bootcss.com/vue/2.5.17-beta.0/vue.min.js"></script>
    <script type="text/javascript">
    	
    	Vue.component('Child',{
    		data(){
    			return {
    				msg:''
    			}
    		},
    		template:`
			<div>我是孩子{{msg}}</div>
    		`,
    		 inject:['for'],
    		 created(){
                 // 拿到传递过来的值
    		 	this.msg = this.for;
    		 }
    	});

    	Vue.component('Parent',{
    		template:`
			<div>
				<p>我是父亲</p>
				<Child />
			</div>
			
    		`
    		
    	});
    	var App = {
    		data(){
    			return {

    			}
    		},
            
    		provide:{
    			for:'他爹'
    		},
    		template:`
			<div>
				<h2>我是入口组件</h2>
				<Parent />
			</div>
    		`
    	}

    	new Vue({
    		el:"#app",
    		template:`<App />`,
    		data(){
    			return {

    			}
    		},
    		components:{
    			App
    		}
    	});
    </script>
	
</body>
</html>

parentparent和children

挂载父组件,在子组件中通过props去接收

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <title></title>
</head>

<body>
    <div id="app"></div>
    <script src="https://cdn.bootcss.com/vue/2.5.17-beta.0/vue.min.js"></script>
    <script type="text/javascript">
    

    Vue.component('Child', {
                props: {
                    value: String, //v-model会自动传递一个字段为value的prop属性
                },
                data() {
                    return {
                      	// 将value 的值赋值给  mymessage
                        mymessage: this.value
                    }
                },
                methods: {
                    changeValue() {
                        console.log(this.mymessage);
                        this.$parent.message = this.mymessage; //通过如此调用可以改变父组件的值
                        console.log(this.$parent);
                    }
                },
                template: `
            <div>
                <input type="text" v-model="mymessage" @change="changeValue"> 
            </div>
            `
    })
    Vue.component('Parent',{
      // 点击按钮,将mymessage的值传递给子组件  
        template:` <div >
                    <p>我是父亲组件{{message}}</p>
                    <button @click = "changeChildValue" > test < /button > 
                    <Child ></Child> 
                    </div>
                `,
        methods:{
            changeChildValue(){
                this.$children[0].mymessage = 'hello';
            }
        },

        data(){
            return {
                message:'hello'
            }
        }
    })
    var App = {
            data(){
                return {

                }
            },
            template:`
            <div>
                <h2>我是入口组件</h2>
                <Parent />
            </div>
            `
        }

   var vm = new Vue({
        el:'#app',
        components:{
            App
        },
        template:`
            <App></App>
                `
    })
   console.log(vm);

    </script>
</body>

</html>

vuex流程图

在这里插入图片描述

Vuex原理

Vuex实现了一个单向数据流,在全局拥有一个State存放数据,当组件要更改State中的数据时,必须通过Mutation进行,Mutation同时提供了订阅者模式供外部插件调用获取State数据的更新。而当所有异步操作(常见于调用后端接口异步获取更新数据)或批量的同步操作需要走Action,但Action也是无法直接修改State的,还是需要通过Mutation来修改State的数据。最后,根据State的变化,渲染到视图上。

各模块之间的工作流程

  • Vue Components:Vue组件。HTML页面上,负责接收用户操作等交互行为,执行dispatch方法触发对应action进行回应。
  • dispatch:操作行为触发方法,是唯一能执行action的方法。
  • actions:操作行为处理模块,由组件中的$store.dispatch('action 名称', data1)来触发。然后由commit()来触发mutation的调用 , 间接更新 state。负责处理Vue Components接收到的所有交互行为。包含同步/异步操作,支持多个同名方法,按照注册的顺序依次触发。向后台API请求的操作就在这个模块中进行,包括触发其他action以及提交mutation的操作。该模块提供了Promise的封装,以支持action的链式触发。
  • commit:状态改变提交操作方法。对mutation进行提交,是唯一能执行mutation的方法。
  • mutations:状态改变操作方法,由actions中的commit('mutation 名称')来触发。是Vuex修改state的唯一推荐方法。该方法只能进行同步操作,且方法名只能全局唯一。操作之中会有一些hook暴露出来,以进行state的监控等。
  • state:页面状态管理容器对象。集中存储Vue components中data对象的零散数据,全局唯一,以进行统一的状态管理。页面显示所需的数据从该对象中进行读取,利用Vue的细粒度数据响应机制来进行高效的状态更新。
  • getters:state对象读取方法。图中没有单独列出该模块,应该被包含在了render中,Vue Components通过该方法读取全局state对象。

Vuex与localStorage

vuex 是 vue 的状态管理器,存储的数据是响应式的。但是并不会保存起来,刷新之后就回到了初始状态,   	**具体做法应该在vuex里数据改变的时候把数据拷贝一份保存到localStorage里面,刷新之后,如果		localStorage里有保存的数据,取出来再替换store里的state。**
let defaultCity = "上海"
try {   // 用户关闭了本地存储功能,此时在外层加个try...catch
  if (!defaultCity){
    defaultCity = JSON.parse(window.localStorage.getItem('defaultCity'))
  }
}catch(e){}
export default new Vuex.Store({
  state: {
    city: defaultCity
  },
  mutations: {
    changeCity(state, city) {
      state.city = city
      try {
      window.localStorage.setItem('defaultCity', JSON.stringify(state.city));
      // 数据改变的时候把数据拷贝一份保存到localStorage里面
      } catch (e) {}
    }
  }
})
复制代码

这里需要注意的是:由于vuex里,我们保存的状态,都是数组,而localStorage只支持字符串,所以需要用JSON转换:

JSON.stringify(state.subscribeList);   // array -> string
JSON.parse(window.localStorage.getItem("subscribeList"));    // string -> array 

在vue中获取DOM元素

<!DOCTYPE html>
<html lang="zh">
<head>
	<meta charset="UTF-8">
	<meta name="viewport" content="width=device-width, initial-scale=1.0">
	<meta http-equiv="X-UA-Compatible" content="ie=edge">
	<title></title>
	<script src="./node_modules/vue/dist/vue.min.js" type="text/javascript" charset="utf-8"></script>
</head>
<body>
	<div id="app"></div>
	<script type="text/javascript">
		// 组件的挂载
		Vue.component('SubCom', {
			template:
			`
				<div></div>
			`
		});
		var App = {
			template:
			`
				<div class = 'app'>
					<button ref = 'btn'>按钮1</button>
					<button ref = 'btn2'>按钮2</button>
					<!-- 给组件绑定ref -->
					<SubCom ref = 'abc'/>
				</div>b
			`,
			created() {
				<!-- 获取所有的加上有ref属性的集合 -->
				console.log(this.$refs.btn);
			},
			beforeMount:function() {
				<!-- 获取所有的加上有ref属性的集合 -->
				console.log(this.$refs.btn);
			},
			mounted() {
				<!-- 如果给标签蚌寺那个ref = 'xxx' 属性,使用this.$refs.xxx获取原生的js对象 -->
				<!-- ref属性值不能重名 -->
				console.log(this.$refs.btn);
				console.log(this.$refs.btn2);
				
				<!-- 如果是给自定义组件绑定ref属性,那么this.$refs.abc获取的是当前的组件对象 -->
				console.log(this.$refs.abc);
			}
		};
		new Vue({
			el: '#app',
			data() {
				return {
					
				}
			},
			template: `<App></App>`,
			components: {
				App
			}
		});
	</script>
</body>
</html>

给DOM元素添加事件的特殊情况

使用focus()方法,获取焦点事件

<!DOCTYPE html>
<html lang="zh">
<head>
	<meta charset="UTF-8">
	<meta name="viewport" content="width=device-width, initial-scale=1.0">
	<meta http-equiv="X-UA-Compatible" content="ie=edge">
	<title></title>
	<script src="./node_modules/vue/dist/vue.min.js" type="text/javascript" charset="utf-8"></script>
</head>
<body>
	<div id="app"></div>
	<script type="text/javascript">
		var App = {
			data() {
				return {
					isShow: false
				}
			},
			template:
			`
				<div class = 'app'>
					<input type = 'text' v-show = 'isShow' ref = 'input'/>
				</div>
			`,
			mounted() {
				this.isShow = true;
				<!-- 能获取到input框,但是不能使用focus()方法 -->
				console.log(this.$refs.input);
				<!-- 在Dom更新 -->
				<!-- $nextTick()方法,在DOM更新循环之后执行回调函数,再修改数据之后可以使用此方法,在回调函数中获取到更新之后的数据 -->
				<!-- this.$ref.input.focus(); -->
				this.$nextTick(function() {	
					console.log(this);
					<!-- 窗体加载的时候input框默认触发焦点事件 -->
					this.$refs.input.focus();
				});
				
			}
		};
		new Vue({
			el: '#app',
			data() {
				return {
					
				}
			},
			template: `<App></App>`,
			components: {
				App
			}
		});
	</script>
</body>
</html>

生命周期的图示

在这里插入图片描述

核心插件

路由

路由的实现

  • 传统开发方式url改变后,立刻发生请求响应整个页面,有可能资源过多,传统开发会让页面出现白屏

  • 前端路由的原理

    <!DOCTYPE html>
    <html lang="zh">
    <head>
    	<meta charset="UTF-8">
    	<meta name="viewport" content="width=device-width, initial-scale=1.0">
    	<meta http-equiv="X-UA-Compatible" content="ie=edge">
    	<title></title>
    </head>
    <body>
    	<a href="#/login">登录</a>
    	<a href="#/register">注册</a>
    	<div id="app"></div>
    	<script type="text/javascript">
    		var oDiv = document.getElementById('app');
    		
    		// onhashchange方法,哈希值改变后调用的方法
    		// a标签中href属性的值就是哈希值
    		window.onhashchange = function(){
    			// 访问到用户点击的哈希值
    			console.log(location.hash);
    			// 根据哈希值来做相对应的处理
    			switch (location.hash){
    				case '#/login':
    					oDiv.innerHTML = '<h2>我是登录页面</h2>'
    					break;
    				case '#/register':
    					oDiv.innerHTML = '<h2>我是注册页面</h2>'	
    					break;
    				default:
    					break;
    			}
    		}
    	</script>
    </body>
    </html>
    
  • SPA(Single Page Application) 单页面应用

    • 锚点值改变
      • 锚点值改变后,不会立刻发送请求,而是在某个合适的实际,发起的ajax请求页面的局部渲染
      • 优点:页面不会立刻跳转,用户体验好
      • Vue,Angular,React这些框架都是做单页面应用

Vue-Router的基本使用

下载packag.json配置文件

npm init --yes

下载vue包

npm install vue --save

下载vue-router包

npm install vue-router -S

使用:

  1. 引包(两个全局组件 router-link to属性 router-view(匹配路由组件的出口))
  2. 创建实例化VueRouter对象
  3. 匹配路由规则
  4. 挂载new Vue()实例化对象中
    1. 给vue实例化对象挂在了两个对象 this.$router(),它就是VueRouter
    2. this.route()获取的是配置路由信息的对象
  5. 命名路由
    1. 绑定自定义属性 :to = “{name:‘路由的名字’}”
  6. 路由的参数
    1. path:‘/user/id’
      1. to:“{name:‘user’,params:{id: 1}}”
    2. path:‘/user’
      1. to:“{name:‘user’,query:{userId: 1}}”
  7. 嵌套路由(应用于 子路由是不同的页面结构)
    1. /home/music ===> /home/movie
      1. 一个router-view中嵌套另外一个router-view
<!DOCTYPE html>
<html lang="zh">
	<head>
		<meta charset="UTF-8">
		<meta name="viewport" content="width=device-width, initial-scale=1.0">
		<meta http-equiv="X-UA-Compatible" content="ie=edge">
		<title></title>
		<!-- 1引入vue模块-->
		<script src="./node_modules/vue/dist/vue.js" type="text/javascript" charset="utf-8"></script>
		<!-- 2 引入vue-router模块 -->
		<!-- 引入vue-router模块后 会抛出两个全局组件 router-link 和 router-view -->
		<!-- router-link 相当于a标签 ,router-link中的to属性相当于a标签的href属性 -->
		<!-- router-view 路由匹配组件的出口 -->
		<script src="./node_modules/vue-router/dist/vue-router.js" type="text/javascript" charset="utf-8"></script>
	</head>
	<body>
		<div id="app"></div>
		<script type="text/javascript">
			// 创建使用vue-router
			// 3 让Vue使用VueRouter创建
			Vue.use(VueRouter);

			var Login = {
				template: `
				<div>我是登陆页面</div>
			`
			};
			var Register = {
				template: `
				<div>我是注册页面</div>
			`
			};
			<!-- // 4 创建router对象 -->
			var router = new VueRouter({
				<!-- //5 配置路由对象 -->
				routes: [
					<!-- // 路由匹配的规则 -->
					<!-- 当路径为login时,自动匹配Login组件,然后加载组件中的内容 -->
					{
						path: "/login",
						component: Login
					},
					{
						path: "/register",
						component: Register
					},
				]
			});


			<!-- // 声明组件 -->
			var App = {
				template: `
					<div>
						<!-- <a href="">登录页面</a> -->
						<router-link to="/login">登录页面</router-link>
						<router-link to="/register">注册页面</router-link>
						<!-- 路由匹配组件的出口 -->
						<router-view></router-view>
					</div>
			`
			}
			new Vue({
				el: '#app',
				data() {
					return {

					}
				},
				components: {
					App
				},
				<!-- 将 router 路由交给Vue实例化对象管理-->
				router,	
				template: `<App></App>`
			})
		</script>
	</body>
</html>

命名路由

绑定to属性,匹配路由对象(通过定义的name来使用)

routes中的path属于动态路由参数,以冒号开头

<!DOCTYPE html>
<html lang="zh">
	<head>
		<meta charset="UTF-8">
		<meta name="viewport" content="width=device-width, initial-scale=1.0">
		<meta http-equiv="X-UA-Compatible" content="ie=edge">
		<title></title>
		<!-- 1引入vue模块-->
		<script src="./node_modules/vue/dist/vue.js" type="text/javascript" charset="utf-8"></script>
		<!-- 2 引入vue-router模块 -->
		<!-- 引入vue-router模块后 会抛出两个全局组件 router-link 和 router-view -->
		<!-- router-link 相当于a标签 ,router-link中的to属性相当于a标签的href属性 -->
		<!-- router-view 路由匹配组件的出口 -->
		<script src="./node_modules/vue-router/dist/vue-router.js" type="text/javascript" charset="utf-8"></script>
	</head>
	<body>
		<div id="app"></div>
		<script type="text/javascript">
			// 创建使用vue-router
			// 3 让Vue使用VueRouter创建
			Vue.use(VueRouter);

			var Login = {
				template: `
				<div>我是登陆页面</div>
			`
			};
			var Register = {
				template: `
				<div>我是注册页面</div>
			`
			};
			<!-- // 4 创建router对象 -->
			var router = new VueRouter({
				<!-- //5 配置路由对象 -->
				routes: [
					<!-- // 路由匹配的规则 -->
					<!-- 当路径为login时,自动匹配Login组件,然后加载组件中的内容 -->
					{
						path: "/login",
						name: "login",	<!-- 给当前路由命名 -->
						component: Login
					},
					{
						path: "/register",
						name: "register",
						component: Register
					},
				]
			});


			<!-- // 声明组件 -->
			var App = {
				template: `
					<div>
						<!-- <a href="">登录页面</a> -->
						<!-- <router-link to="/login">登录页面</router-link> -->
						
						<!-- 通过路由的命名来使用 -->
						<router-link :to="{name: 'login'}">登录页面</router-link>
						
						
						<!-- <router-link to="/register">注册页面</router-link> -->
						<router-link :to="{name: 'register'}">登录页面</router-link>
						<!-- 路由匹配组件的出口 -->
						<router-view></router-view>
					</div>
			`
			}
			new Vue({
				el: '#app',
				data() {
					return {

					}
				},
				components: {
					App
				},
				<!-- 将 router 路由交给Vue实例化对象管理-->
				router,	
				template: `<App></App>`
			})
		</script>
	</body>
</html>

路由参数

路由范式(规则):

  • xxx.html#/user/1
    • 动态参数(params)路由
  • ooo.html#/user?userId = 1
    • ( ?)query查询
<!DOCTYPE html>
<html lang="zh">
	<head>
		<meta charset="UTF-8">
		<meta name="viewport" content="width=device-width, initial-scale=1.0">
		<meta http-equiv="X-UA-Compatible" content="ie=edge">
		<title></title>
		<!-- 1引入vue模块-->
		<script src="./node_modules/vue/dist/vue.js" type="text/javascript" charset="utf-8"></script>
		<!-- 2 引入vue-router模块 -->
		<!-- 引入vue-router模块后 会抛出两个全局组件 router-link 和 router-view -->
		<!-- router-link 相当于a标签 ,router-link中的to属性相当于a标签的href属性 -->
		<!-- router-view 路由匹配组件的出口 -->
		<script src="./node_modules/vue-router/dist/vue-router.js" type="text/javascript" charset="utf-8"></script>
	</head>
	<body>
		<div id="app"></div>
		<script type="text/javascript">
			// 创建使用vue-router
			// 3 让Vue使用VueRouter创建
			Vue.use(VueRouter);

			var userParams = {
				template: `
					<div>我是用户1</div>
				`,
				created() {
					<!--引入vue-router时就会抛出两个对象  -->
					<!-- 这两个对象就挂载到Vue实例化对象 -->
					console.log(this.$router);
					console.log(this.$route);
					<!-- 获取用户点击的id -->
					<!-- 注意通过  :id传递了id参数参能获取到id的值 -->
					console.log(this.$route.params.id);
				}
			};
			var userQuery = {
				template: `
				<div>我是用户2</div>
			`
			};
			<!-- // 4 创建router对象 -->
			var router = new VueRouter({
				<!-- //5 配置路由对象 -->
				routes: [
					<!-- // 路由匹配的规则 -->
					<!-- 当路径为login时,自动匹配Login组件,然后加载组件中的内容 -->
					{
						path: "/user/:id",
						<!-- params动态参数 -->
						name: "userP",	<!-- 给当前路由命名 -->
						component: userParams
					},
					{
						path: "/user",
						<!-- query 查询 -->
						name: "userQ",
						component: userQuery
					},
				]
			});


			<!-- // 声明组件 -->
			var App = {
				template: `
					<div>
						<!-- params:{key,value} -->
						<!-- 动态路由路径 -->
						<router-link :to="{name: 'userP', params:{id:1}}">用户1</router-link>
						<router-link :to="{name: 'userQ', params:{userId:2}}">用户2</router-link>
						<!-- 路由匹配组件的出口 -->
						<router-view></router-view>
					</div>
			`
			}
			new Vue({
				el: '#app',
				data() {
					return {

					}
				},
				components: {
					App
				},
				<!-- 将 router 路由交给Vue实例化对象管理-->
				router,	
				template: `<App></App>`
			})
		</script>
	</body>
</html>

嵌套路由

菜单栏的动态切换,标题栏一样,通过小组件来实现页面的动态切换

<!DOCTYPE html>
<html lang="zh">
	<head>
		<meta charset="UTF-8">
		<meta name="viewport" content="width=device-width, initial-scale=1.0">
		<meta http-equiv="X-UA-Compatible" content="ie=edge">
		<title></title>
		<!-- 1引入vue模块-->
		<script src="./node_modules/vue/dist/vue.js" type="text/javascript" charset="utf-8"></script>
		<!-- 2 引入vue-router模块 -->
		<!-- 引入vue-router模块后 会抛出两个全局组件 router-link 和 router-view -->
		<!-- router-link 相当于a标签 ,router-link中的to属性相当于a标签的href属性 -->
		<!-- router-view 路由匹配组件的出口 -->
		<script src="./node_modules/vue-router/dist/vue-router.js" type="text/javascript" charset="utf-8"></script>
	</head>
	<body>
		<div id="app"></div>
		<script type="text/javascript">
			// 创建使用vue-router
			// 3 让Vue使用VueRouter创建
			Vue.use(VueRouter);
			<!-- 内层小组件 -->
			var Song = {
				template: 
				`
					<div>歌曲列表</div>
				`
			};
			var Movie = {
				template: 
				`
					<div>电影列表</div>
				`
			};
			
			<!-- // 外层大组件 -->
			<!-- 注意:每一个组件模板都是一个大的整体,需要在一个盒子里面 -->
			var Home = {
				template: `
					<div>首页
						<br />
						<!-- 动态路由路径 -->
						<router-link to = '/home/song'>歌曲</router-link>
						<router-link to = '/home/movie'>电影</router-link>
						<!-- 路由匹配组件的出口 -->
						<router-view></router-view>
					</div>
				`
			};
			
			<!-- // 4 创建router对象 -->
			var router = new VueRouter({
				<!-- //5 配置路由对象 -->
				routes: [
					<!-- // 路由匹配的规则 -->
					<!-- 当路径为login时,自动匹配Login组件,然后加载组件中的内容 -->
					{
						path: "/home",
						<!-- params动态参数 -->
						name: "home",	<!-- 给当前路由命名 -->
						component: Home,
						<!-- 匹配多层目录下面的子目录 -->
						children: [
							{
								path: "song",
								<!-- params动态参数 -->
								component: Song
							},
							{
								path: "movie",
								<!-- params动态参数 -->
								component: Movie
							},
						]
					}
				]
			});


			<!-- // 声明组件 -->
			var App = {
				template: `
					<div>
						<!-- params:{key,value} -->
						<!-- 动态路由路径 -->
						<router-link :to="{name: 'home'}">首页</router-link>
						<!-- 路由匹配组件的出口 -->
						<router-view></router-view>
					</div>
			`
			}
			new Vue({
				el: '#app',
				data() {
					return {

					}
				},
				components: {
					App
				},
				<!-- 将 router 路由交给Vue实例化对象管理-->
				router,	
				template: `<App></App>`
			})
		</script>
	</body>
</html>

动态路由匹配

<!DOCTYPE html>
<html lang="zh">
<head>
	<meta charset="UTF-8">
	<meta name="viewport" content="width=device-width, initial-scale=1.0">
	<meta http-equiv="X-UA-Compatible" content="ie=edge">
	<title></title>
		<script src="./node_modules/vue/dist/vue.js" type="text/javascript" charset="utf-8"></script>
		<script src="./node_modules/vue-router/dist/vue-router.js" type="text/javascript" charset="utf-8"></script>
</head>
<body>
	<div id="app"></div>
	<script type="text/javascript">
		// 当前使用路由参数,列入/timeline/fronted导航到/timeline/backed,原来的组件实例会被复用,
		// 因为两个路由都渲染同个组件,比起销毁在创建,复用则显得更高效,不过这也意味着组件的生命周期
		// 不会再被调用
		
		var ComDesc = {
			data() {
				return {
					msg: ''
				}
			},
			template:
			`
				<div>我是{{ msg }}</div>
			`,
			created() {
				<!-- created方法只会走一次 -->
				<!-- created可以发Ajax请求 -->
				this.msg = '前端';
			},
			<!-- watch在当前组件内部监听路由信息的变化 -->
			watch: {
				'$route'(to,from) {
					console.log(to);	<!-- to就是一个routes对象 -->
					console.log(from);
					<!-- 发送ajax请求,获取到对应的参数 -->
					this.msg = to.params.id;
				}
			}
		};
		var Timeline = {
			template:
			`
				<div id = 'timeline'>
					<!-- 动态路由路径 -->
					<router-link :to = "{name: 'comDesc',params:{id: 'frontend'}}">前端</router-link>
					<router-link :to = "{name: 'comDesc',params:{id: 'backed'}}">后端</router-link>
					<!-- 路由匹配组件的出口 -->
					<router-view></router-view>
				</div>
			`
		};
		var Pins = {
			template:
			`
				<div>
					<h2>沸点</h2>
				</div>
			`
		};
		<!-- // 创建路由对象 -->
		var router = new VueRouter({
			<!-- mode: 'history',	哈希模式,页面跳转时路由不会出现#  -->
			routes:[
				{
					path: '/timeline',
					component: Timeline,
					children: [
						{
							name: 'comDesc',
							<!-- 动态路由的参数  以:开头 -->
							path: '/timeline/:id',
							component: ComDesc
						}
					]
				},
				{
					path: '/pins',
					component: Pins
				}
			]
		});
		<!-- // 实例化app组件 -->
		var App = {
			template:
			`
				<div>
					<!-- 动态路由路径 -->
					<router-link to = '/timeline'>首页</router-link>
					<router-link to = '/pins'>沸点</router-link>
					<!-- 路由匹配组件的出口 -->
					<router-view></router-view>
				</div>
			`
		}
		<!-- // 实例化对象 -->
		new Vue({
			el: '#app',
			router,
			template: `<App></App>`,
			<!-- 挂载组件 -->
			components: {
				App
			}
		})
	</script>
</body>
</html>

路由元信息(meta的使用及权限控制)

<!DOCTYPE html>
<html lang="zh">
<head>
	<meta charset="UTF-8">
	<meta name="viewport" content="width=device-width, initial-scale=1.0">
	<meta http-equiv="X-UA-Compatible" content="ie=edge">
	<title></title>
		<script src="./node_modules/vue/dist/vue.js" type="text/javascript" charset="utf-8"></script>
		<script src="./node_modules/vue-router/dist/vue-router.js" type="text/javascript" charset="utf-8"></script>
</head>
<body>
	<div id="app">
		<router-link to = '/home'>首页</router-link>
		<router-link to = '/blog'>我的博客</router-link>
		<router-link to = '/login'>登录</router-link>
		<a href="javascript:void(0)">退出</a>
		<!-- 每个路由组件都渲染到router-view里面了 -->
		<router-view></router-view>
	</div>
	<script type="text/javascript">
		Vue.use(VueRouter);
		var Home = {
			template: 
			`
				<div>首页</div>
			`
		};
		var Blog = {
			template: 
			`
				<div>我的博客</div>
			`
		};
		var Login = {
			data() {
				return {
					name: '',
					pwd: ''
				}
			},
			template: 
			`
				<div>
					<input type="text" v-model="name"/>
					<input type="password" v-model="pwd"/>
					<input type="button" value="登录" @click = 'loginHandle'/>
				</div>
			`,
			methods: {
				loginHandle() {
					<!-- 登录 -->
					<!-- 将用户名 密码保存下来 -->
					localStorage.setItem('user', {name:this.name,pwd:this.pwd});
					<!-- 跳转到博客页面 -->
					<!-- 编程式导航 -->
					this.$router.push({
						name: 'blog'
					})
				}
			}
		};
		<!-- // 创建路由对象 -->
		var router = new VueRouter({
			<!-- mode: 'history',	哈希模式,页面跳转时路由不会出现#  -->
			routes:[
				{
					path: '/',
					redirect: '/home'
				},
				{
					path: '/home',
					component: Home
				},
				{
					path: '/blog',
					name: 'blog',
					component: Blog,
					<!-- meta给未来的路由做权限控制 -->
					meta: {
						<!-- 证明用户访问该组件的时候需要登录 -->
						auth: true
					}
				},
				{
					path: '/login',
					component: Login
				}
			]
		});

		<!-- 全局路由匹配 -->
		<!-- beforeEach会监测路由 -->
		router.beforeEach((to, from, next) => {
			console.log(to);
			console.log(from);
			if(to.meta.auth) {	<!-- true -->
				if (localStorage.getItem('user')) {
					<!-- 如果localStorage有值则证明用户登录完成了 -->
					next();
				}else{
					<!-- 用户需要登录 -->
					next({
						path: '/login'
					});
				}
				
			} else {
				<!-- false  直接放行 -->
				<!-- 如果不调用 next() 方法会卡主页面-->
				next();
			}
		});
		<!-- // 实例化对象 -->
		new Vue({
			el: '#app',
			router,		<!-- 挂载路由 -->
		})
	</script>
</body>
</html>

Vuex

Vue服务端渲染

Axios

Axios是一个基于promise的HTTP库,可以在浏览器和node.js中使用。

作用:

  • 从浏览器中创建XMLHttpRequests
  • 从node.js创建http请求
  • 支持PromiseAPI
  • 拦截请求和响应
  • 转换请求数据和响应数据
  • 取消请求
  • 自动转换JSON数据
  • 客户端支持防御XSRF攻击

参考文档链接:www.kancloud.cn/yunye/axios…

使用

安装

使用npm:

npm install axios

使用bower:

bower install axios

使用cdn:

<script src="https://unpkg.com/axios/dist/axios.min.js"></script>

axios的基本使用

<!DOCTYPE html>
<html lang="zh">
	<head>
		<meta charset="UTF-8">
		<meta name="viewport" content="width=device-width, initial-scale=1.0">
		<meta http-equiv="X-UA-Compatible" content="ie=edge">
		<title></title>
		<!-- 引包vue -->
		<script src="./node_modules/vue/dist/vue.js" type="text/javascript" charset="utf-8"></script>
		<!-- 引包axios -->
		<script src="./node_modules/axios/dist/axios.js" type="text/javascript" charset="utf-8"></script>
	</head>
	<body>
		<div id="app"></div>
		<script type="text/javascript">
			// console.log(axios);
			var App = {
				template:
				`
					<div>
						<button @click = 'sendAjax'>发请求</button>
					</div>
				`,
				methods: {
					sendAjax(){
						this.$axios.get('http://127.0.0.1:8888/')
						.then(res => {
							<!-- 拿到statusText: "OK" -->
							console.log(res.data.msg);	<!-- 打印输出ok -->
						})
						.catch(err => {
							console.log(err);
						}) 
					}
				}
			};
				Vue.prototype.$axios = axios;
			new Vue({
				el: '#app',
				data() {
					return {

					}
				},
				template: `<App></App>`,
				components: {
					App
				}
			});
		</script>
	</body>
</html>

处理并发请求

<!DOCTYPE html>
<html lang="zh">
	<head>
		<meta charset="UTF-8">
		<meta name="viewport" content="width=device-width, initial-scale=1.0">
		<meta http-equiv="X-UA-Compatible" content="ie=edge">
		<title></title>
		<!-- 引包vue -->
		<script src="./node_modules/vue/dist/vue.js" type="text/javascript" charset="utf-8"></script>
		<!-- 引包axios -->
		<script src="./node_modules/axios/dist/axios.js" type="text/javascript" charset="utf-8"></script>
	</head>
	<body>
		<div id="app"></div>
		<script type="text/javascript">
			// console.log(axios);
			var App = {
				data() {
					return {
						res1: '',
						res2: ''
					}
				},
				template: `
					<div>
						响应1: {{ res1 }},
						响应2: {{ res2 }},
						<button @click = 'sendAjax'>并发请求</button>
					</div>
				`,
				methods: {
					sendAjax() {
						<!-- 请求1 get: /-->
						<!-- 请求2 post :/add -->
						<!-- 配置 -->
						this.$axios.defaults.baseURL = `http://127.0.0.1:8888/`;
						<!-- 发请求 -->
						var r1 = this.$axios.get('');
						var r2 = this.$axios.post('add','a = 1');
						this.$axios.all([r1, r2])
						.then(this.$axios.spread((res1,res2) => {
							<!-- 请求全部成功 -->
							this.res1 = res1.data;
							this.res2 = res2.data;
						}))
						.catch(err => {
							<!-- 有一个失败就失败了 -->
							console.log(err);
						})
					}
				}
			};
			Vue.prototype.$axios = axios;
			new Vue({
				el: '#app',
				data() {
					return {

					}
				},
				template: `<App></App>`,
				components: {
					App
				}
			});
		</script>
	</body>
</html>

学习笔记完整版