【万字总结】 Vue入门到Vux笔记

ES 6

ES6教程

VUE 的 Helloworld

普通文本的显示

使用与Debug的引用模式:

以上的模式只适用于学习不能用于开发

使用与线上部署的模式

let 用于申明局部变量 用法类似于var 但是let命令所在命令块才会有效,有暂时型的死区约束

<html lang="en">
	<head>
		<title>Opening Jupyter Notebook</title>
		<script src="https://cdn.jsdelivr.net/npm/vue@2.6.14/dist/vue.js"></script>

	</head>
	<body>
		
		<div id="Hello">
			{{message}},<h1>{{name}}</h1>
		</div>
		
		<!-- JS 需要在数据加载完毕后在渲染 -->
		<script type="text/javascript">
			const myvue = new Vue({
				el: "#Hello", // 用于挂载管理元素
				// delimiters: ['{[', ']}'] // 默认是{{}} 但是这与Jinja渲染冲突 所以我们可以修改他
				data: {
					message: "你好",
					name:"这里可以存放任意长度的数据"
				}
			}); 
		</script>
	</body>
</html>


我们之前使用js jq 进行开发,往往都是获取DOM对象在进行数据的设置或获取,那种设计模式叫做命令式设计模式(链表式编程范式)而Vue的设计模式是申明式编程(有点像是面向过程与面向对象之间的关系)

我们可以动态的修改data里面的数据

....
let myvue = new Vue({
				el: "#Hello", // 用于挂载管理元素
				// delimiters: ['{[', ']}'] // 默认是{{}} 但是这与Jinja渲染冲突 所以我们可以修改他
				data: {
					message: "你好",
					name:"这里可以存放任意长度的数据"
				}
			}); 
			myvue.message("Hello"); // 修改message的数据

列表的显示

列表的显示可以使用 for 循环

	<body>
		<div id="test">
			<h1>{{Title}}</h1>
			<ul>
				<li v-for='item in Students'>这里是{{item}}</li>
			</ul>
		</div>
	</body>
	<script type="text/javascript">
		const myVue = new Vue({
			el: "#test",
			data:{
				Title:" 学生列表",
				Students: ["李四","张三","刘二"]
			}
		})
	</script>

简单的创造 - 计数器示例

在没有导入 Vue 的监听之前:

<!DOCTYPE html>
<html lang="zh">
	<head>
		<script src="vue.js"></script>
		<title>计数器的实现</title>
	</head>
	<body>
		<div id="count">当前计数:{{myNumber}}</div>
		<button type="button" v-on:click="myNumber++">+</button>
		<button type="button" onclick="myVue.myNumber--">-</button>
        
	</body>
	<script type="text/javascript">
		let myVue = new Vue({
			el: "#count",
			data: {
				myNumber: 0,
			}
		})
	</script>
</html>

导入Vue的监听之后:

<!DOCTYPE html>
<html lang="zh">
	<head>
		<script src="vue.js"></script>
		<title>计数器的实现</title>
	</head>
	<body>
		<div id="count">
			当前计数:{{myNumber}}
			<button  v-on:click="add">+</button>
			<button  v-on:click="sub">-</button>
		</div>
	</body>
	
	<script type="text/javascript">
		let myVue = new Vue({
			el: "#count",
			data: {
				myNumber: 0,
			},
			methods:{
				// 加函数
				add: function(){
					this.myNumber ++
				},
				// 减函数
				sub: function(){
					this.myNumber --
				}
			}
		})
	</script>
</html>

v-on:click可以简写成@click

MVVM

在MVVM的框架下视图和模型是不能直接通信的。它们通过ViewModel来通信,ViewModel通常要实现一个observer观察者,当数据发生变化,ViewModel能够监听到数据的这种变化,然后通知到对应的视图做自动更新,而当用户操作视图,ViewModel也能监听到视图的变化,然后通知数据做改动,这实际上就实现了数据的双向绑定。并且MVVM中的View 和 ViewModel可以互相通信。

我们的计数器中就是一个严格的MVVM模型

  • View依然是我们的DOM层
  • Model就是我们抽离出来的obj
  • ViewModel 就是我们创建的Vue实例

创建Vue的时候我们可以创建以下几种对象:

  • el String | Object

    • 决定之后的Vue实例会管理哪一个DOM
  • data Object |Function

    • Vue实例对应的数据对象
  • methods key:Funtion

    • Vue的方法 方便调用

Vue 的基本语法

插值操作(Mustache语法)

不变指令

如果有一处的标签我们只希望他显示一次性的数据,之后变量如何进行改变都不会波及到他,可以使用v-once指令来规定

v-html 与 不常见的指令

默认的,我们给一个data对象的变量设置html时 浏览器不会解析,我们需要给对应的DOM加入一个指令 v-html,这个指令的值就是要解析的HTML,同时拥有该指令的DOM内部的文本不会被显示,同样的还有v-text指令显示完全的文本,v-pre表示原封不动的显示DOM的内部文本而不会被解析

胡子表达式

{{}}中不仅可以添加变量还可以添加有关变量的表达式

<!DOCTYPE html>
<html lang="zh">
	<head>
		<script src="vue.js"></script>
		<title>胡子语法</title>
	</head>
	<body>
		<div id="e">
			可以进行+拼接: {{welcome + ' ' + username}}
			<br>
			或者使用HTML拼接: {{welcome}} {{username}}
			
			<div v-once>
				不会改变得到welcome : {{welcome}}
			</div>
			<div v-html='bold'>
				我不会被显示,因为这个div是负责解析bold属性的
			</div>
		</div>
		
		
	</body>
	<script type="text/javascript">
		let myVue = new Vue({
			el: "#e",
			data:{
				welcome : "Hello",
				username : "李狗蛋",
				bold:"<b>加粗字体</b>"
			}
		});
	</script>
</html>

属性的动态绑定

v-bind

data的属性使用{{}}也就是Mustache语法可以绑定到文本,使用v-bind可以将属性绑定到属性

HTML:<img v-bind:src="picURL" />

JS: data:{picURL:"xxxxx.png"}

其中v-bind可以进行简写成 -> <img :src="picURL" />

动态绑定Class

<h2 :class="{类名A:,类名2:flase}"></h2>

如果类名后的boolean为true就将值给class,如果是false就不赋值。

我们可以将布尔值交给data,使用的时候直接写名称 不需要{{}} 这样我们就可以动态的设置类属性了

我们也可以利用数组动态绑定Class 但是用处较少 下来自行了解

动态绑定样式

使用对象可以动态绑定样式

:style = "{
	FontSize:"20px" //样式的值如果是具体的就需要引号
}"

属性的封装

getter
<h1> {{fullname}} </h1>
....
computed:{ // 表示计算属性
	fullname : function(){
		return this.fristName + this.lastName;
	}
}
访问器

可以同时设置getter与setter

computed: {
				fullname {
					get(){
						....
					},
					set(val){
						....
					}
				}
			}

练习

<!DOCTYPE html>
<html lang="zh">
	<head>
		<script src="vue.js"></script>
		<title>点击列表标红</title>
	</head>
	<style type="text/css">
		.Red{
			color: red;
		}
	</style>
	<body>
		<ul id ="myListGroup">
			<li v-for="(item,index) in studentGroup" @click="setColorRed(index)" :class=
			"{Red:selectedIndex == index}">{{item}}</li>
		</ul>
	</body>
	<script type="text/javascript">
		let myVue = new Vue({
			el: "#myListGroup",
			
			data:{
				studentGroup: ["李三","张思","王五"],
				selectedIndex:0
			},
			methods:{
				setColorRed(index){
					this.selectedIndex = index;
					
				}
			}
		});
	</script>
</html>

v-on 事件监听

基本使用

v-on:事件 可以进行事件的监听, 可以简写成 @+事件名.

<body>
	<div id="app">
		<h2>{{val}}</h2>
		<button type="button" v-on:click="add">+</button>
		<button type="button" v-on:click="increment">-</button>
	</div>
	<script>
		let app = new Vue({
			el: '#app',
			data: {
				val: 0
			},
			methods:{
				add(){
					this.val ++;
				},
				increment() {
					this.val --;
				}
			}
		});
	</script>
</body>
  • 当方法不需要参数的时候在绑定的时候可以不加入()

    • 如果方法本身有一个参数 且在绑定的时候 **没有加入() ** 默认将原生事件参数传入数据
    • 如果方法本身有一个参数 且在绑定的时候 加入了() 那么参数的默认值就是undefined
  • 如果需要传入某个参数还需要传入event事件参数的时候可以通过 $event传入事件

v-on的修饰符

<body>
	<div id='app' @click="div_cli">
		<button href="" @click="a_cli">{{val}}</button>
	</div>
	<script>
		let app = new Vue({
			el: '#app',
			data: {
				val: 1
			},
			methods: {
				div_cli() {
					console.log('div');
				},
				a_cli() {
					console.log('a_cli');
				}
			}
		});
	</script>
</body>
.stop

因为 事件冒泡 的机制上述的代码 按钮点击后会执行两个函数的内容, 如果想要按钮只执行按钮的方法 可以使用事件冒泡的机制进行实现,在vue中可以直接使用 v-on的修饰符 即可.

我们只想让 button执行 @click="a_cli"的代码 可以使用 @click.stop='a_cli' 意味事件完.stop的时候进行事件停止

.prevent

阻止默认事件, 比如formsubmit的自动提交事件 可以使用 prevent进行事件的阻止,在进行一些处理之后再进行提交

.{keyCode | keyAlias}

监听键盘的输入 如 @keyup.enter 只监听回车按键事件

.once

只触发一次的事件

条件判断

v-if v-else v-else-if

v-if 的值为true时显示数据 v-elsev-iffalse时显示 但是条件过多的时候建议使用计算属性 getter setter

v-show

v-show 是一定会被渲染的 当 v-show 为 false 的时候只是 display: none

但是 v-if 为 false 的时候 页面甚至不会进行渲染

开发的选择

当切换频繁的时候 为了让页面进行少量渲染 建议使用 v-show

当只有一次切换的时候建议使用 v-if

v-show吃内存,v-if吃CPU

小项目 - 注册登录的切换

<!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">
		<script src="./vue.js" type="text/javascript" charset="utf-8"></script>
		<title>v-on的基本使用</title>
	</head>
	<body>

		<div id='app'>
			<h1>{{title}}</h1>
			<form v-if='showLogin' action="/login">
				<input type="text" name="" placeholder="登录">
				<input type="submit" @click.prevent @click="reg1" id="" name="" />
			</form>
			<form v-else>
				<input type="text" name="" placeholder="注册">
				<input type="submit" @click.prevent @click="login1"  id="" name="" />
                 
			</form>
		</div>
		<script>
			let app = new Vue({
				el: '#app',
				data: {
					title: "登录",
					login: "/login",
					reg:"/reg"
				},
				computed:{
					showLogin:function(){
						return this.title == "登录";
					}
				},
				methods: {
					reg1(){
						this.title = "注册"
					},
					
					login1(){
						this.title = "登录"
					}
				}
			});
		</script>
	</body>

</html>

Bug

如果我们把form的active删除掉 使得两个条件分支的form的属性只有一个v-if 或者是 v-else, 那么就会出现一个问题, 两个form表单的input输入数据在进行切换后 里面的数据不会被重置, 这是因为一个叫做 虚拟dom - vdom的机制影响的

v-dom

将需要显示的 dom 加载到 vdom的内存中,然后在渲染到页面, 考虑其性能, 虚拟dom在进行渲染的时候会使用已经存在的元素,而不是重新创建元素

解决方法

​ 给ele键入一个唯一的标志, 在我们的代码中 action就是一个标识,如果你是用的是其他的盒子,可以使用 :key 属性来进行标识

循环

v-for 遍历数组

<li v-for = "i in array">
 {{ i }} 普通数组的遍历
 {{ i }} 如果array是对象的话 i 就是对象的 value
</li>


<li v-for = "(val, index) in array">
 {{ i }} 普通数组的遍历 第{{index+1}}个值
</li>

<li v-for = "( alue, key, index) in array">
 对象的key 与 value 相对于其他的语言是反着的 index还是在最后
</li>

官方推荐: 推荐给 v-for 的元素或者组件加入 :key 属性

响应式的数组方法

我们知道 push方法是一个不错的响应式方法数组在push发生修改后 vue会实时的修改这个变量绑定的元素, 但是并不是所有的方法可以做到响应式

支持响应式的是: push pop shift shift unshift splice 但是 索引修改数组值是不支持响应式的, 我们可以使用vue.set 来进行替换这个方案

vue.set(this.array, 0, "123"); // 修改 array 第0个元素 为 123

this.array.splice(0, 1, 'bbbbb'); // 原生JS也可以使用splice的替换进行修改 

购物车项目

请完成一个项目 https://www.bilibili.com/video/BV15741177Eh?p=41&spm_id_from=pageDriver==[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5Iy4QlPu-1645785342403)(C:\Users\Administrator.KON-20200831MDP\AppData\Roaming\Typora\typora-user-images\image-20211203094934868.png)]==

Vue 的 进阶

JS 的高级函数

filter

filter() 方法创建一个新的数组,新数组中的元素是通过检查指定数组中符合条件的所有元素。

var ages = [32, 33, 16, 40];
function checkAdult(age) {
    return age >= 18;
}
function myFunction() {
    document.getElementById("demo").innerHTML = ages.filter(checkAdult);
}
map

map() 方法返回一个新数组,数组中的元素为原始数组元素调用函数处理后的值。

var numbers = [4, 9, 16, 25];

function myFunction() {
    x = document.getElementById("demo")
    x.innerHTML = numbers.map(Math.sqrt);
}
reduce

汇总

var numbers = [15.5, 2.3, 1.1, 4.7];
 
function getSum(total, num) {
    return total + Math.round(num);
}
function myFunction(item) {
    document.getElementById("demo").innerHTML = numbers.reduce(getSum, 0);
}

reduce() 方法接收一个函数作为累加器,数组中的每个值(从左到右)开始缩减,最终计算为一个值。

reduce() 可以作为一个高阶函数,用于函数的 compose。

注意: reduce() 对于空数组是不会执行回调函数的。

那 Python 举例:

import functools;functools.reduce(lambda x,y: x+y, map(lambda x:x[1]+20,filter(lambda x:x[1] > 40, bookName_Money.items())))

v-model

v-model作用 表单双向绑定

<input type="text" v-model="title"/>

这样我们 就让这个表单进行了双向的绑定 input的双向绑定 v-modle在对表单元素进行绑定的时候会直接把绑定的值给value 当value发生改变的时候 如用户进行的修改 变量值也会被改变 这就是 双向绑定

v-model 与 其他元素

<input type="radio" value="man" id='man' v-model="sex"><label for='man'></label><br>
<input type="radio" value='woman' id='woman' v-model="sex" ><label for="woman"></label><br>

这样不需要name就可以使得两个radio进行互斥

v-model 数组的绑定

checkbox 元素一般会同意选择多个数据 所以 复选框的v-model形式按数组存贮 所以我们绑定给 checkbox的变量必须是数组

<input type="checkbox" id='hobby1'value="羽毛球" v-model="hobby"> <label for='hobby1'>羽毛球</label><br />
<input type="checkbox" id='hobby2'value="网球" v-model="hobby"> <label for='hobby2'>网球</label><br />
<input type="checkbox" id='hobby3'value="足球" v-model="hobby"> <label for='hobby3'>足球</label><br />
<h1>
	当前hobby:{{ hobby }}
</h1>
....
data{
	hobby : [	] // JS 代码
}

修饰符

修饰符z作用
lazy用户输入完毕才会进行修改 而不是实时的修改
number限制变量为number
trim剥除两边的空格

component 组件 开发

组件使用的三步走

  • Vue.extend() 创建组件构造器
    • 传入template 表示我们自定义的模板
    • 模板就是使用组件的地方 所要显示的HTML
    • 很少使用extend这种方式创建组件
  • Vue.component() 注册组件
  • 在Vue范围内使用组件

组件化的基本使用

<!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">
		<script src="./vue.js" type="text/javascript" charset="utf-8"></script>
		<title>组件的基本使用</title>
	</head>
	<body>

		<div id="app">
			<my-cpn>
				<!-- 组件的使用需要在 Vue  管理的项目里 -->
			</my-cpn>
		</div>
		<script>
			const com = Vue.extend({
				template: `
				<div>
					<h2>这是一个组件模板</h2>
					<a href='http://www.baidu.com'>你好</a>
				</div>`
			}); // 创建一个组件
			
			const com2 = Vue.extend({
				template: `
				<div>
					<h2>这是第二个组件模板</h2>
					<a href='http://www.baidu.com'>再见</a>
				</div>`
			}); // 创建一个组件

			Vue.component("my-cpn", com); // 将组件注册成my-cpn 组件的标签最好不要大写
			
			let app = new Vue({
				el : "app"
			})
		</script>
	</body>

</html>

全局组件与局部组件

全局组件的注册使用component进行,局部的组件式找到对应局域的Vue构造函数

let app = new Vue({
    el: "#app"
    comments: {
       	cpn: com; // 注册局部的组件
	}
})
局部组件与全局组件的区别

全局组件可以在任意的Vue管理范围区域使用 而局部组件只能在对应的管理区域进行使用

父组件与子组件

<body>
	<div id="app">
		<mycpn2>
			<!-- 组件的使用需要在 Vue  管理的项目里 -->
		</mycpn2>
	</div>
	<script>
		const com = Vue.extend({
			template: `
			<div>
				<h2>这是一个组件模板</h2>
				<a href='http://www.baidu.com'>你好</a>
			</div>`
		}); 
		
		const com2 = Vue.extend({
			template: `
			<div>
				<h2>这是第二个组件模板</h2>
				<div style='border:1px solid'>
					<h3>我们可以在组件里面使用组件</h3>
					<cpn1></cpn1>
				<div>
			</div>`,
			components:{
				// 在extends构造器里面可以存在一个components
				cpn1 : com // 为com2组件注册一个com的组件  
			}
		}); // 创建第二个组件
		
		let app = new Vue({
			el : "#app",
			components:{
				mycpn2: com2
			}
		
		})
	</script>
</body>

如上代码 com2com的父组件 注意 局部的组件必须在全局被注册或者是在局部生效

语法糖

我们很少使用extend创建组件,一般使用语法糖更简单

Vue.component("mycpn2",{
	template: `
	<div>
		<h2>这是一个组件模板</h2>
		<a href='http://www.baidu.com'>你好</a>
	</div>`
});
			
let app = new Vue({
	el : "#app",
})

我们将传入extend的参数对象放在 component参数即可实现 构造器的使用与组件注册两个步骤,当然我们也可以通过这个形式创建局部语法糖组件

let app = new Vue({
	el : "#app",
	comments:{
		"app": {
			template: "局部组件"
		}
	}
})

抽离组件的写法

虽然使用了语法糖 但是代码看起来还是很乱的说 所以我们可以template的html单独抽离出来进行编写

  • 创建一个script标签
  • 他的type设置成 text/x-template
  • 在里面写模板的文件
  • 给这个标签设置id
  • 给传入 extend构造器 的对象的 template对象赋值对应的 id,记得加上#

当然 script 这个方式很少使用 我们可以将其设置成 template标签然后为其设置id即可

<div id="app">
	<mycpn2>
		<!-- 组件的使用需要在 Vue  管理的项目里 -->
	</mycpn2>
</div>
<template id='myhome'>
	<div>
		<h2>这是一个组件模板</h2>
		<a href='http://www.baidu.com'>你好</a>
	</div>
</template>
<script>
	
	Vue.component("mycpn2",{
		template: "#myhome"
	});
	
	let app = new Vue({
		el : "#app"
	})
</script>

组件使用变量

问题: 组件的内部 是不能访问 Vue的实例数据的

在上述的代码中 template的文本是写死的 我们给vue的date写入变量给template依旧无法显示,因为 组件的内部 是不能访问 Vue的实例数据的

解决方法

我们在注册, 也就是 compontent 方法里 ,给传入的对象加入一个新的方法: data ,其必须是一个 函数

<div id="app">
	<mycpn2>
		
	</mycpn2>
</div>
<template id='myhome'>
	<div>
		<h2>这是一个组件模板:{{title}}</h2>
		<a href='http://www.baidu.com'>你好</a>
	</div>
</template>
<script>
	
	Vue.component("mycpn2",{
		template: `#myhome`,
		data(){
			return {
				title: "abc"
			}
		}
	});
	
	let app = new Vue({
		el : "#app"
	})
</script>

同样的 组件可以拥有 methods 等所有该用有的 是一个封闭的小空间

为什么是函数

如果创建了多个组件 防止多个组件在共享同一个data属性所作的隔离

组件之间的通信

网页请求服务器的时候 一般是最外部的组件进行请求,从而获取响应值,如何将外部组件的数据交给内部组件进行数据的展示, 是本章的主要内容

vue交给我们两种父子组件的通讯方式

  • 通过props向子组件传递数据 props 即 properties的缩写

  • 利用事件 向父组件的数据传递子组件向父组件使用 事件 父组件向子组件式 props

父传子

<body>
	<template id='child'>
		<div>
			This is Child: {{ name }} <br>
			He live in {{ home }}
			<!-- 这里的home变量是父组件的数据 -->
		</div>
	</template>
	<div id="app">
		<child :home="type"></child>
        <!-- 这里v-bind可以简写 -->
	</div>
	<script>
		Vue.component("child", {
			template: "#child",
			props: ["home"] ,// 定义父传子的变量
			data() {
				return {
					name: "LeLe"
				}
			}
		})
		let app = new Vue({
			el: "#app",
			data:{
				type: "别墅"
                // 因为使用了 :home="type" 所以这里可以使用 type:"别墅" 从而进行父传子
			}
		})
	</script>
</body>

可以让 props 成为 对象 这样可以限制类型

props: {
    movies: {
		type: String, // 类型
		default: "xxxx", // 默认值
         required: true, // 这个变量是必须传入的值 v-bind
    }
}

在vue2.5之后 当type为Array的时候 default必须是一个函数

props: {
    movies: {
		type: Array, // 类型
		default(){
            return [];
        } , // 默认值
         required: true, // 这个变量是必须传入的值 v-bind
    }
}

除此之外我们可以自定义验证器

变量名称最好不要驼峰命名

子传父

常见的子传父使用的是事件, 子传父主要是用于子组件发生了一些事件需要父组件知道。

  • 给子组件定义一个func 在 methods 这个方法指定了向 父组件 发送的事件(记得事件全小写)
  • 父组件在使用的时候进行事件的绑定
  • 绑定的事件可以对应父组件自己的函数事件
  • 即可

在第二步的是时候 如果emit有向服务器发送对应的参数,那么第二步的时候会自动把参数交给函数执行,这样我们就不需要自己写实参了

children 与 refs

如果你想使用父组件拿到子组件的对象进行调用子组件对象的方法,或者反其道让子组件进行父组件的方法调用,这一章就对你大有裨益

  • 父组件访问子组件 使用 $children$refs
  • 子组件访问父组件 使用 $parent
<body>
   <template id='children-element'>
   	<div>
   		孩子
   	</div>
   </template>
   
   <div id="app">
   	<cpn></cpn>
   	<cpn ref="i-have-ref"></cpn>
   	<button @click="father_click">父亲按钮</button>
   </div>
</body>
<script type="text/javascript">
   let app = new Vue({
   	el: "#app",
   	components:{
   		cpn : {
   			template:"#children-element",
   			methods:{
   				sayHello(){
   					console.log("孩子在说话");
   				},
   				
   				sayHello2(){
   					console.log("特殊的孩子在说话");
   				}
   			}
   		}
   	},
   	data: {
   		mes: "你好"
   	},
   	methods:{
   		father_click(){
   			console.log("父亲按钮被点击");
   			
   			// 1、 $children 拿取
   			
   			console.log(this.$children);
   			// 返回了一个数组里面存放若干个子组件
   			
   			let child_ele = this.$children[0];
   			// 先拿一个子组件
   			
   			child_ele.sayHello();
   			// 调用子组件的方法
   			
   			// 2、refs 拿取 子组件中 带有 ref 属性的标签
   			let array_list = this.$refs;
   			let child_ele_2 = this.$refs['i-have-ref'];
   			child_ele_2.sayHello2();
   			
   			
   		}
   	}
   })
</script>

parent 与 root

与children refs类似 实际开发很少使用 故而不在赘述

插槽 slot

什么是插槽

插槽相当于组件的装饰,可以为组件内部的元素进行扩展

什么时候使用

基本使用
	<html>
	<head>
		<meta charset="utf-8">
		<title></title>
		<script src="./vue.js" type="text/javascript" charset="utf-8"></script>
	</head>
	<body>
		<div id="app">
			<cpn></cpn>
			<cpn><h1>你好</h1></cpn>
			
		</div>
		
		<template id='model'>
			<div style="border: 1px black solid;margin-top: 20px;">
				这是一个插槽,你需要放一个元素给她:<slot><h2>这是我的默认值</h2></slot>
			</div>
		</template>
	</body>
	<script type="text/javascript">
		let app = new Vue({
			el: "#app",
			components:{
				cpn:{
					template:"#model"
				}
			}
		})
	</script>
</html>

具名插槽

一个模板可以拥有多个插槽为了区别各个插槽 我们可以给插槽一个名字 拥有名称的插槽就是 具名插槽

<html>
	<head>
		<meta charset="utf-8">
		<title></title>
		<script src="./vue.js" type="text/javascript" charset="utf-8"></script>
	</head>
	<body>
		<div id="app">
			<cpn>标题</cpn>
			<!-- 不使用具名插槽会修改全部没有name的插槽元素 -->
			
			<cpn >
				<input slot="center" type="text">
				<!-- 使用具名插槽的修改 slot属性 -->
			</cpn>
		</div>
		
		<template id='model'>
			<div style="border: 1px black solid;margin-top: 20px;">
				<slot name='left'></slot>
				<slot name='center'></slot>
				<slot name='right'></slot>
				<slot>凑数的</slot>
				<slot>凑数的</slot>
				<slot>凑数的</slot>
				<slot>凑数的</slot>
			</div>
		</template>
	</body>
	<script type="text/javascript">
		let app = new Vue({
			el: "#app",
			components:{
				cpn:{
					template:"#model"
				}
			}
		})
	</script>
</html>

作用域插槽

编译作用域

如下代码中

<div id='app'>
    <cpn v-for="i in list"></cpn>
</div>

我们在子组件中使用了list, 那这个list是 父组件的还是子组件的? 答案: 父组件的

如下代码中

<template>
	<div>
        <li v-for="i in list"></li>
    </div>
</template>

我们在模板中使用了list, 那这个list是 父组件的还是子组件的? 答案: 子组件的

作用域插槽

作用:父组件替换插槽的标签 内容是子组件提供 也就是在确保数据依旧是子组件提供的情况下修改子组件插槽部分的数据···

<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8">
		<title>作用域插槽 </title>
		<script src="./vue.js" type="text/javascript" charset="utf-8"></script>
	</head>
	<body>

		<div id="app">
			<cpn>
				<template slot-scope='slot'>
					<span v-for='i in slot.val_list_data'> {{i}} </span>
					
				</template>
			</cpn>
		</div>

		<template id='child'>
			<div>
				hello

				<slot :val_list_data="list_data">
					<li v-for="i in list_data">{{i}}</li>
				</slot>
			</div>


		</template>
		<script type="text/javascript">
			let app = new Vue({
				el: "#app",
				components: {
					cpn: {
						template: "#child",
						data() {
							return {
								"list_data": ["第一步", "第二步", "第三步"]
							};
						}
					}
				}
			});
		</script>
	</body>
</html>

webpack

安装

需要先安装node,js, 并使用 npm管理 webpack.

node.js的安装请查看 AJAX

npm 安装 webpack 命令

npm install webpack@3.6.0 -g

-g 表示全局安装, 有两种安装方法:

  • 全局安装
  • 局部安装

使用 webpack -v 命令进行webpack是否安装完毕 的检验

使用

webpack的项目中一般存在两个目录:

当然还需要存在一个index.html的玩意~

  • 在 src 中创建一个 模块 我写的是math_util 是一个数据求值库
  • math_util 一些重要的函数或者变量导出(commandJS 方法 或者是 CMD AMD 方法都可以)
  • main,js 导入数据 并执行导入的方法

代码

这是项目结构:

-- math_util.js
function add(number1 , number2) {
	return number1 + number2;
}

module.exports = [add]
// 使用CommandJs进行导出

-- main.js:
const add = require('./math_util.js')[0]
console.log(add(1,2))

现在我们不能在 HTML 代码中写入 main.js的 引用 因为 commandJS不能是 JS 原生的支持 , 我们需要 webpack 进行 源码的打包

在项目根目录中执行cmd指令

webpack ./src/main.js ./dist/bundle.js
  • 我们只需打包main.js吗?
    • 在我们的代码中我们的 main.js 导入了一些依赖库(math_util.js) webpack 会自动打包这些依赖
  • 如果 webpack 版本在 4.0 以上 使用代码: npx webpack .\src\main.js ./dist/bundle.js
    • 这个只是听说不知道可以使用否

完成了这些操作我们只需要设置boundle.js的引用 —— 在html代码中就是了

webpack 的配置

webpack ./src/main.js ./dist/bundle.js这段命令是在是太长了 每次都要敲 麻烦! 我们可以给 webpack 进行设置,这样输入webpack就可以了 本章教你如何实现这样的功能

首先在项目根目录定义一个文件 这个文件的文件名称 固定为 webpack.config.js

因为我们要在webpack.config,js 中使用指定的第三方模块, 所以在正式开发之前我们需要执行命令(需要在项目根目录里)

npm init
npm install

完成之后就有了一个package.json文件出现

开始编写 webpack.config,js 的内容

const path = require('path'); // 导入 node 的 JS

module.exports = {
	entry: __dirname +  "/src/main.js", // 源文件 必须是绝对路径
	output: {
		path: __dirname + '/dist' 
		// 打包后的目录 必须是绝对路径
		, filename: 'bundle.js'  // 打包后的文件名
	}
}

完成这几步之后在使用命令webpack即可实现打包

但是在实际开发中 我们不常使用 webpack 进行打包,而是使用 npm run build 指令

只需要修改 package.json 文件即可进行修改

"scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
	"build": "webpack"
  },

开发时依赖环境安装

我们在使用webpack 可能会有多个项目同时开发 A项目是 3.5版本的WP B项目事是5.1版本的WP,为了让两个项目可以顺利开发,我们会给每个项目安装一个 开发时依赖 .

确保存在一个package.json 在项目根目录中使用:

npm install webpack@3.6.0 --save-dev

但是在 CMD 执行的任何webpack都是全局的,为了让这个依赖环境有效, 我们需要模仿上一章 使用 npm run build 指令进行打包

CSS | 资源文件的配置

webpack 不支持css等非JS的文件的转化,我们需要给webpack 做一个升级, 也就是使用 npm 安装loader

css

  • 创建 css 文件
  • main.js 中导入这个 css
require("./cee/normal.css")

完成准备工作后我们就去官网翻牌我们需要的loader: https://www.webpackjs.com/loaders/

安装我们的 loder

如果webpack的版本是3.6.0

安装css-loader的时候记得安装 2.0.2 版本的 否则会出错 style-loader@0.23.1

  • 使用的是开发时依赖:

    • npm install --save-dev css-loader
    • npm install --save-dev style-loader
  • 在导出配置 也就是webpack.config.js 中加入:

    module: {
    rules:{
    test: /.css$/,
    use: [‘style-loader’, css-loader’]
    }
    }

    style-loader 负责样式添加到css 中

    css-loader 是负责css的文件加载

    web的加载 方式是从右到左 所以我们要先加载css 在加载style 所以需要: [‘style-loader’, css-loader’]这么定义

  • 打包

安装成功 !

Less 文件的配置

1、 先写入一个less文件 less文件的写法是这样的:

@fontSize: 30px;
@ColorFont: red;
body{
	font-size: @fontSize;
	color: @ColorFont;
}

2、 然后还是老样子在 JS 导入依赖

3、 为了让我们的 webpack 有读取 less文件的能力 需要安装 less

npm install less

4、 安装完毕后,安装对应的less-loader文件即可

版本如上图

rules添加规则

test:/\.less$/,
use: [
    {
        loader: "style-loader"
    },
    {
        loader: "css-loader"
    },
    {
        loader: "less-loader"
    }
]
		

图片的配置

1、现在我们的css或者less中写入 图片文件的使用即可

2、 然后找到对应的加载器,这次的加载器不在是加载 样式而是文件: url-loader

npm install --save-dev url-loader@1.1.2

3、 设置配置

{
	test: /\.(png|jpg|gif|jpeg)/,
	use: {
		loader:'url-loader',
		options:{
			limit: 27000 // 限制大小
		}
	}
}

如果我们有的图片大于了options:limit的大小 在进行打包的时候就会报错 :-

为了加载比limit大的图片我们可以先下载一个file_loader@3.0.1, 但是 依旧无法显示,我们只需要在导出文件的output属性中加入 publicPath:'dist/' 即可 这表示 只要设置url这样的属性的时候会自动进行拼接publicPath

图片文件名称的修改

打包后的图片都会存放在dist的根目录下,他是图片名称转化成的hash值,非常难以看懂,所以我们需要将其转化为我们看得懂的名称(也就是原名称),为了文件名重复,我们可以在原名称后面加上hash值, 但是hash值太长 我们可以做一个截取 取8位就是了,然后在dist中我们需要图片在img文件夹下,all in all, 我们可以在 打包配置文件(webpack.conf.js) 的 options写入代码:

limit:111111,
name: 'img/[name].[hash:8].[ext]'

这就是操作的效果图

ES6 转 ES5

有些浏览器是不支持Es6的为了适用性扩大,我们一般使用es5 , 使用babel工具进行转换

npm install --save-dev babel-loader@7 babel-core babel-preset-es2015

由于IE下架本章不在阐述…

脚手架 4.x

vue-cli4.x开发 + Vue2.x开发

什么是脚手架?

如果你只是简单的写几个Vue的Demo程序 那么你不需要VueCli,但是如果你在开发大型香蜜, VueCLI是你的不二之选

再不使用cli的时候, Vue.js开发大型应用 我们需要考虑代码目录结构 项目结构部署热加载单元测试等事情

使用vue-cli可以快速搭建Vue的开发环境

安装Vue我们可以直接使用 全局安装 也就是 npm install -g @vue/cli

我们可以使用 命令vue --version可以获取脚手架的版本.

项目的简单创建

  • 先安装一个全局的插件: npm i -g @vue/cli-service-global

  • 在创建一个 app.vue

    并写入如下的代码:

    <template>
    		<div>
    			<h1>我是谁?</h1>
    			<h2>刘博源</h2>
    		</div>
    		
    </template>
    
    <script>
    </script>
    
    <style>
    </style>
    
    

我们也可以使用vue build命令进行打包的操作

默认启动的文件是App.vue 或者是 app.vue 如果需要启动非默认的文件可以进行指定操作

vue serve -c index.vue

项目的创建

我们可以使用 vue create 项目名称创建VueCli项目,当然你要选择Vue3版本 输入命令之后稍等片刻进行安装…

然后通过安装后的命令提示启动服务

cd xxx
npm run serve

接着使用 localhost:8080 进入网页

项目中存在一个src目录是我们的开发目录,默认存在的有:

  • main.js
  • App.vue
  • assets - 多媒体文件目录
  • components - 主要的组件

使用 npm run build 进行打包 (需要在项目中运行该命令).

图形化操作

使用 vue ui 打开GUI 操作模式, 这个也可以创建一个新的项目

组件化开发

组件化开发的模式其实就是.vue文件的使用, 我们知道 .vue文件是 html + js + css 三个主要标签实现的, 他们分离了主键的关联,提高了内聚, 开发完毕后我们可以打包好文件让浏览器可以正常识别.

main.js解析

import Vue from 'vue' 
// 引入 Vue.js 框架
import App from './App.vue'
// 引入 App.vue 组件


Vue.config.productionTip = false
// 去除警告

// 创建根实例 ( Vue 全局实例)
new Vue({
  render: h => h(App), // 渲染视图主键并且注册App.vue
}).$mount('#app') // 将APP组件的信息挂载到#app中

整个页面的html结构是public/index.html 中默认提供的模板 我们是用#app挂载的App.vue就会自动的修改,

创建一个LBY.Vuecomponents文件夹中.

<template>
	<div id="lby-show">
		我只是测试
	</div>
</template>

<script>
	export default {
		name: 'LBY',
		components: {

		}
	}
</script>

<style scoped>

</style>

然后对 App.vue进行修改

<template>
  <div id="app">
    <img alt="Vue logo" src="./assets/logo.png">
    <HelloWorld msg="Welcome to Your Vue.js App"/>
	<LBY></LBY>
  </div>
</template>

<script>
import HelloWorld from './components/HelloWorld.vue'
import LBY from "./components/LBY.vue"
export default {
  name: 'App',
  components: {
    HelloWorld,
	LBY
  }
}
</script>

<style>
#app {
  font-family: Avenir, Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-align: center;
  color: #2c3e50;
  margin-top: 60px;
}
</style>

Router

作用是创建单页面的项目

创建项目

  • 取消 Linter / Formatter
  • 勾选 Router
  • 回车选择2.x
  • 是否选择history模式? no,区别 http://localhost:8080/#/about 与 http://localhost:8080/about 的区别
  • In package.json

等待

如何启动 history 模式?

我们在创建的时候取消了history模式而是进入了Hash模式取消Hash模式的方法就是在router/index.js中修改:

const router = new VueRouter({
mode: ‘history’,
routes
})

main.js

import Vue from 'vue'
import App from './App.vue'
import router from './router' // 相对传统的创建多了一个router的引用

Vue.config.productionTip = false

new Vue({
  router, // router的注册 , 注册之后vue组件才可以使用router 相关的标签
  render: h => h(App)
}).$mount('#app')

App.vue

<template>
  <div id="app">
    <div id="nav">
      <router-link to="/">Home</router-link> <!-- router-link 是组件的导航会被渲染成 a 标签 to就是href的插槽-->
      |<router-link to="/about">About</router-link>
    </div>
      <!-- 两个切换按钮 -->
      
    <router-view/>
      <!-- 用于显示对应router的视图文件 -->
      
  </div>
</template>

<style>
#app {
  font-family: Avenir, Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-align: center;
  color: #2c3e50;
}

#nav {
  padding: 30px;
}

#nav a {
  font-weight: bold;
  color: #2c3e50;
}

#nav a.router-link-exact-active {
  color: #42b983;
}
</style>
 

router/index.js

import Vue from 'vue'
import VueRouter from 'vue-router'
import Home from '../views/Home.vue'

Vue.use(VueRouter)

const routes = [
  {
    path: '/', // 路由
    name: 'Home', // 别名
    component: Home // 加载的组件 渲染到 router-view
  },
  {
    path: '/about',
    name: 'About',
    // route level code-splitting
    // this generates a separate chunk (about.[hash].js) for this route
    // which is lazy-loaded when the route is visited.
    component: () => import(/* webpackChunkName: "about" */ '../views/About.vue') // 懒加载
  }
]

const router = new VueRouter({
	// mode: 'history',
  routes
})

export default router

小例子 加入一个List页面

  • 创建Vue
<template>
  <div class="about">
    <h1>测试用例</h1>
  </div>
</template>
<script>
	
export default {
  name: 'List',
  components: {
    
  }
}
</script>
  • 在routers中注册vue
import Vue from 'vue'
import VueRouter from 'vue-router'
import Home from '../views/Home.vue'
import List from "../views/List.vue"

Vue.use(VueRouter)

const routes = [{
		path: '/',
		name: 'Home',
		component: Home
	},
	{
		path: '/about',
		name: 'About',
		// route level code-splitting
		// this generates a separate chunk (about.[hash].js) for this route
		// which is lazy-loaded when the route is visited.
		component: () => import( /* webpackChunkName: "about" */ '../views/About.vue')
	},
	{
		path: '/list',
		name: 'list',
		component: List
	}
]

const router = new VueRouter({
	// mode: 'history',
	routes
})

export default router
  • App.vue中创建
<template>
  <div id="app">
    <div id="nav">
      <router-link to="/">Home</router-link> |
      <router-link to="/about">About</router-link>|
	  <router-link to='/list'>List</router-link>
    </div>
    <router-view/>
  </div>
</template>

<style>
#app {
  font-family: Avenir, Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-align: center;
  color: #2c3e50;
}

#nav {
  padding: 30px;
}

#nav a {
  font-weight: bold;
  color: #2c3e50;
}

#nav a.router-link-exact-active {
  color: #42b983;
}
</style>

动态路由匹配

什么是动态路由匹配?

将不确定的参数进行路由映射到同一个组件上去 比如经典的user?id=5 ,其中5就是动态的数值 最终路径就是,他会成为 /user/5 其中 $route与params Cli会组合成一个对象 如{ id : 5 }

创建一个类似的组件

<template>
	<div class="main">
		这是我的组件
		该组件名称: {{$route.name}} <br>
		该组件的id参数: {{$route.query.id}} <br>
	</div>
</template>

<script>
	export default {
		name: 'Test'
	}
</script>

<style>
</style>

$route.name 是获取route的组件名称, route.query可以获取url的参数对象

如果是 /xxx/id 的传输方式的话就是动态的获取参数对象 使用 route.params 即可

<template>
	<div class="test">
		param值的获取: {{$route.params.id}}
	</div>
</template>

<script>
	export default{
		name: "p_test"
	}
</script>

<style>
</style>

注册的时候也需要注意

 {
	  path: "/p_test/:id",
	  name: "p_test",
	  component: p_test
  }

冒号加参数名称可以实现动态路由的匹配

路由捕获

{
	path: "*",
	name: '404',
	component: LostPage
}

注册这样的路由在 index.js 中可以获取 非法url的请求

path: "user-*" 以 user-开头的路由都向这里走.

嵌套路由

在某个实际业务中 我们要实现一个游戏卡池系统,这个卡池系统有两个子系统: 公开招募系统特殊招募系统。两个系统需要一个动态参数用来获取卡池的版本号信息, 当然在一个系统中包括多个子系统就可以使用我们的嵌套路由来实现.

  • 创建一个父组件
<template>
	<div>
		这是默认页显示的数据...
		<router-view />
		<!-- 显示子路由的组件可以使用router-view渲染 -->
	</div>
</template>

<script>
	export default {
		name: "default_recruit"
	}
</script>

<style>

</style>

  • 创建子路由的组件
<template> 
  <div class="main">
	这是一个版本为{{$route.params.version}}的特殊招募系统
  </div>
</template>

<script>
	export default{
		name: "private_system"
	}
</script>

<style>

</style>
  • public与上述的类似
  • 注册组件
{
	path: "/recruit/:version", // 大局系统
	name: "recruit",
	component: default_recruit,
	children:[
		{
			path: "public",
			component: public_system
		},
		{
			path: "private",
			component: private_system
		}
	]
}

···

导航式编程

  • 是组件标签可以直接进行导航,但缺乏编程性,无法进行各种逻辑判断
  • 插件是提供了编程式的导航 让我们自行定义我们的导航方法,
....
<button @click="golist" >点击我</button>
...
<script>
	export default{
		methods:{
			golist() {
				this.$router.push("/about"); 
			}
		}
	}
</script>

出现Uncaught (in promise) NavigationDuplicated: Avoided redundant navigation错误是同一页面不能出现两个相同的路由导航

this.$router.push 可以向路由后加入新文本从而进行访问

当然你可以使用键值对进行访问

golist() {
	this.$router.push(
	{
		path: '/test'
	});
}

这种方法一般可以适用在带参数 url 中

<script>
	export default {
		methods: {
			golist() {
				this.$router.push({
					name: 'p_test', // 此处使用的是组件的name属性
					params: {
						id: 4
					} // 如果是query传参就使用query
				});
			}
		}
	}
</script>

使用 $router.go(-1) 回到历史页

使用 $router.go(1) 前往下一页

命名路由与命名视图

命名路由

在上一章我们知道name可以 与params或者是 query进行配合从而进入路由的导航,组件也可以这么使用

<router-link :to="{name: 'p_test', params:{id:125}}">TestByParam(默认125)</router-link> |

这就是路由命名的简单的使用,同时我们可以命名视图

命名视图

我们知道, 是视图标签 组件通过这个标签来渲染组件的内容

  • 创建一个组件
  • index.js中进行注册
  • App.Vue中定义组件的插槽()

我们可以使用Home组件来查看作用

创建一个组件

<template>
  <div>
  这是一个固定的组件
  </div>
</template>
<script>
	export default{
		name: "Top"
	}
</script>

将组件创建在

{
	path: '/',
	name: 'Home',
	components: {
		default: Home, // 默认的组件显示
		top: Top // name为top的router-view渲染Top的组件
	}
}

路由别名

{
    path: "/list",
	name: "list",
    alias: "/mylist"
}

别名可以为path设置多路由

重定向

vue-cli也可以实现重定向的操作

{
    path: "/user",
    redirect: "/UserInfo"
}

这就是访问/user的时候跳转到UserInfo中

或者是对象传参

{
    path: "/user",
    redirect: {
        name: "UserInfo"
    }
}

使用箭头函数可以进行闭包的操作

{
    path: "/user",
    redirect: ()=>{
        if (this.validate){
            return "/UserInfo"
        }else{
            return "/index"
        }
    }
}

组件传参

如果我们希望User/5中获取的id时, 不希望{{$router.params.id}} 获取, 而是{{id}}这样直接获取

  • 在组件创建的地方建立一个props属性 当然你可以给她默认值或者是类型
<template>
	<div class="test">
		param值的获取: {{id}}
	</div>
</template>

<script>
	export default{
		name: "p_test",
		props: {
			"id": {
				type: Number
			} // props  使用
            // 或者直接 props: [ "id" ]
		}
	}
</script>

<style>
</style>

index.js 中创建组件注册的地方 将props的值设置为true.

Node.js 静态服务器的部署

Hash模式
  • 先将服务器设置为 hash 模式
  • npm run build # 打包
  • npm i serve -g # 安装serve
  • serve dist # 对 dist 目录下的 dist 进行挂载
History模式

在History模式中我们依旧可以向Hash模式一样进行挂载,但是,刷新等操作后会出现404的错误,为了防止这样的错误再次出现,我们可以使用命令: serve dist -s

导航守卫

全局前置

作用:

跳转的链接的前置与后置 可以进行跳转或者是取消跳转的操作

使用:

我们在 index.js 中加入一个路由守卫:

router.beforeEach(
	(to, from, next)=>{
		/**
		to: 目标的路由对象
		from: 正要离开的路由对象
		next: 钩子函数
        */
	}
)

注意不要将这段代码写在 export default router 之后

如果路由守卫什么都不做 那么网页无法实现正常的跳转 因为路由守卫有一定的拦截作用,为了取消这种拦截我们可以在回调函数里面使用next() 函数,next()支持指定的资源, next(false) 可以取消导航跳转

避免以下的问题

如果我们要实现登录功能(登陆失败不跳转登陆成功才跳转)

const flag_login = false;

router.beforeEach(
	(to, from, next)=>{
		if (flag_login){
			console.log(to)
			next();
		}else{
			next("/shopping");
		}
	}
)

export default router

按理说我们点击登陆后会登陆失败从而进入shopping页但是我们发现一旦登陆失败会出现无限递归的错误(RangeError)

为了避免这样的错误我们可以在指定的页面才进行跳转

const flag_login = false;

router.beforeEach(
	(to, from, next)=>{
        if(to.name == "Login"){
            if (flag_login){
                console.log(to)
                next("/user_index");
            }else{
                next("/");
            }
        }else next("/login")
	}
)

export default router
全局后置

afterEach 就是后置函数 只有两个参数to from 用于页面加载完成之后操作的一些动作

路由独享守卫

路由独享守卫 在某个路由内设置 仅仅供这个路由使用.

{
	path: '/about',
	name: 'About',
	component: () => import( '../views/About.vue'), // 这是懒加载
	beforeEnter: (to, from, next) => {
		next("/shopping")
	}
}

这样我们在/about页前往其他页面的时候就会直接跳转在/shopping

组件守卫

组件守卫有三种状态 beforeRouteUpdate beforeRouteLeave beforeRouteEnter 分别是复用态 离开态 与 激活态,与其同行的还有 this 对象

  • 复用态是指在 /:id 之类的地方 但是第一次是激活 后面才是复用 可以获取this对象
  • 激活就是加载后 由于组件被创建不久 this 对象是无法获取到的
  • 离开就是回收后 可以获取this对象

组件守卫不用卸载 index.js 而是具体的组件.vuescriptexport部分

虽然我们难以在 激活态 也就是beforeRouteEnter 中获取this但是我们还是可以使用next传入回调进行获取:

...
beforeRouteEnter: (to, from, next)=>{
    next(vm=>{
        console.log(vm);
    })
}
...

元信息

我们可以给路由设置一个mate 对象 在守卫中我们可以利用 to 获取Routemate对象, 由于给路由设置的所以我们是在 index.js 中进行设置

{
    path: '/index/home',
    name: "index.home",
    component: Route_home,
    mate: {
        title : 'mate的标题'
    }
}

使用代码这么获取

BeforeEach: (to, from, next){
    ...
    let title = to.mate.title;
    ...
}

过渡动效

作用就是专场效果.

路由数据获取

路由完成后获取
  • 我们先创建一个vue组件 并设置两个 带 v-if 的盒子, 将 v-if 所使用的 成员 加载到 Loadding , 默认值为 null 或者是 false
<template>
	<div class="main">
		<div v-if="loading">loading</div>
		<div v-if="post">post</div>
	</div>
</template>

<script>
	export default {
		name: "Loadding",
         data(){
             return {
                 loading: false,
                 post: null
             }
         }
	}
</script>

<style>

</style>
  • 注册组件在 index.js
  • 这样我们的组件已经创建完毕了,现在我们只需要执行获取数据即可
<template>
	<div class="main">
		<div v-if="loading">loading...</div>
		<div v-if="post"> <h1>title</h1>{{post.title}} <br> <h1>author</h1>{{post.who}}</div>
	</div>
</template>

<script>
	export default {
		name: "Loadding",
		data (){
			return {
				loading: false,
				post: null
			}
		},
		created() {
			// 该方法代表组件创建之后执行的代码
			
			this.getDatas();// 获取数据
		},
		methods:{
			getDatas(){
				
				this.loading = true; // 组件的加载盒子可以进行显示了
				
				let server = {title:"name", who:"liu"}; // 模拟网络发来的数据
				setTimeout(()=>{
					this.post = server; // 获取数据
					this.loading = false; // 我们要显示post盒子了 加载盒子可以不用显示了
				},1000); // 模拟传输需要1000ms
				
				
			}
		}
	}
</script>

<style>

</style>

路由完成前获取

组件守卫仿佛可以完成类似的事情 比如 beforeRouteEnter

自己完成哈, 就不写代码了

路由滚动

当显示的网页 height > 1vh 的时候 屏幕就需要使用滑块进行滑动 当我们的滑块位置不再 x:0 y:0的(也就是最初始的位置)前往其他的路由 浏览器依旧会保存滑块所在的位置(前提是 两个路由的vh或者是vw > 1), 我不想记录滑块的位置 如何完成

index.js

const router = new VueRouter({
	mode: 'history',
	base: process.env.BASE_URL,
	routes,
	scrollBehavior(to, from, savedPosition) {
		return {
			x: 0,
			y: 0
		}; // 每次切换的时候回到自己的位置
	}
})

return savedPosition 表示依旧保留切换前的位置

Vuex

什么是Vuex

  • Vuex 用于解决组件与组件之间共享的状态 (非父子组件的通讯)

  • 使用devtools工具查看是否安装了Vuex

我们先写一个计数器

<template>
<div class="test">
	<h1> {{number}} </h1>
	<button @click="add()"> 加一 </button>
</div>
</template>
	
<script>
	export default {
		name : "Test",
		data(){
			return {
				number : 1
			}
		},
		methods: {
		
			add(){
				this.number += 1
			}
		}
	}
</script>

<style>
</style>

我们在 /Test 中注册这个组件发现虽然我们可以使用但是切换到其他路由之后再回到 /Test 这个状态是无法被保存的

Store 模式

如果是大型的单页面应用使用VueX会繁琐冗余 也就是说你的应用在及其简单的情况下使用Vuex是大材小用了,使用 store 模式可以帮助你在极少地共享数据

  • 在项目中创建一个src/store目录 并在里面创建一个index.js
  • index.js 中加入如下代码
let store = {
	// 共享数据
	state: {
		public_number: 0
	},
	
	// 累计数据
	add(){
		this.state.public_number ++;
	}
}


export default store; // 导出 store
  • 然后修改之前的计数器组件
<template>
<div class="test">
	<h1> {{public_State.public_number}} </h1>
	<button @click="increment()"> 加一 </button>
</div>
</template>
	
<script>
	import store from "@/store"
	export default {
		name : "Test",
		data(){
			return {
				public_State: store.state, // 将共享状态给组件
			}
		},
		methods: {
			increment(){
				store.add();
			}
		}
	}
</script>

<style>
</style>

Vuex 使用方式

安装Vuex的时候选择Vuex插件即可安装Vuex

发现 src/store 中加入了这么一段代码

import Vue from 'vue'
import Vuex from 'vuex'

Vue.use(Vuex)

export default new Vuex.Store({
  state: {
  }, // 状态值
  mutations: {
  },  // 修改状态的方法
  actions: {
  }, // 接口异步请求 服务器的数据
  modules: {
  }
})

我们使用Vuex来实现一个全局共享的计数器.

  • 先再 store/index.js 中加入 状态
  state: {
	  count: 10
  }, // 状态值
  • 我们就直接使用 About.vue 进行计数器的编写
<template>
  <div class="about">
    <h1>This is an about page</h1>
	{{$store.state.count}}
	
	<button @click="add">点击我</button>
  </div>
</template>

<script>
	export default {
		methods:{
			add (){
				this.$store.commit('addvalue'); // 使用 commit 将组件发送到 addvalue 的 修改状态
			}
		}
		
	}
</script>

那我们可不可以直接进行修改呢? 虽然是可以,但是是不能保存状态的,所以直接修改一般而言是不能的(有点像 PySide 的多线程信号)

State 状态

Vuex 是通过 单例模式 创建的 store实例 也就是说 一个Vue应用只能存在一个 store.

我们使用计算属性进行之前的简洁写法

// store/index.js
...
	state: {
		count: 10,
		age: 20,
		name: "刘博源",
		leavel: "A"
	}, // 状态值
...
<template>
  <div>
	<h1>{{name}}</h1>
	<h1>{{age}}</h1>
	<h1>{{leavel}}</h1>
  </div>
</template>

<script>
	export default{
		name: "Student",
		computed:{
			name(){
				return this.$store.state.name;
			},
			age(){
				return this.$store.state.age;
			},
			leavel(){
				return this.$store.state.leavel;
			}
		}
	}
</script>

<style>

</style>

但是这样虽然在h1处体现出了简洁 但是 在计算属性的地方依旧是冗余代码过多,为了避免这样的问题我们可以使用 vuex的 mapState 辅助函数

<template>
  <div>
	<h1>{{name}}</h1>
	<h1>{{age}}</h1>
	<h1>{{leavel}}</h1>
  </div>
</template>

<script>
	import {mapState} from "vuex"
	export default{
		name: "Student",
		computed: mapState(['name','age','leavel'])		
	}
</script>

但是这样的使用方式只能在 组件使用的变量名与在index.js中注册的状态变量名称一致的时候才可以使用 但是我们可以使用对象的模式进行显示

...
<script>
	import {
		mapState
	} from "vuex"
	export default {
		name: "Student",
		computed: mapState({
			a: "name",
			b: 'age',
			c: 'leavel'
		})

	}
</script>
...

如此使用 其他的计算属性的话需要使用对象展开运算符...

export default {
	name: "Student",
	computed: {
		...mapState({
			a: "name",
			b: 'age',
			c: 'leavel'
		}),
		frist_name(state){
			return state.name[0];
		}
	}
}

Getters 派生状态

有些时候我们需要对Store的 状态 值进行简单的处理 比如说给某个变量的违规词进行删除,使用后MapStore仿佛是个不错的选择但是很多组件都需要这个功能的时候,再去一个个的添加就很麻烦了, 这个时候我们就需要使用Getters来实现了,

// 该代码放在 store/index.js 的 store 构造器的参数里    
getters: {
	getName(state){
		return "[" + name + "]";
	}
}

使用的时候记得不是调用函数而是使用属性即可

{{$store.getters.getName}}

同样的 Getters 也有对应的映射简写模式 mapGetters

Getters 的参数

Getters 支持参数的编写 但是Getters本身使用的时候不是以函数调用 所以Getters 需要这么使用

getName(state){
	return (id)=>{
		return "[" + state.name + " " + id + "]";
	}
}

使用

<h1>{{c}}		{{$store.getters.getName(3)}}</h1>

Mutations 同步提交

我们可以在 index.js 中写入修改状态值的函数 ,然后在组件中使用mapMutaions进行绑定

import {
	mapState, mapMutations
} from "vuex"
export default {
	name: "Student",
	methods:{
		...mapMutations(['ageValueAdd'])
	},
	computed: {
		...mapState({
			a: "name",
			b: 'age',
			c: 'leavel'
		})
	}
}
// index.js
export default new Vuex.Store({
	state: {
		...
	}, // 状态值
	getters: {
		...
	},
	mutations: {
		addvalue(state){
			state.count ++;
		},
		ageValueAdd(state){
			state.age += 1;
		}
	}, // 修改状态
	actions: {
		
	}, // 接口异步请求
	modules: {
		
	}
})

Actions 异步提交

Mutations只能处理同步函数, 处理异步函数的时候需要使用Actions函数, 因此 API 的数据就可以通过Actions获取

<template>
  <div id='main'>
	<h1>name:{{$store.state.name}}</h1>
	<button @click="loading">点击加载</button>
  </div>
</template>

<script>
	import {mapMutations} from "vuex"
	export default{
		name: "Student",
		methods: {
			...mapMutations(["getInfo_mutations"]), // 同步获取 
			loading(){
				this.$store.dispatch("getInfo_Actions") // 异步获取
			}
		}
	}
</script>

// index.js
import Vue from 'vue'
import Vuex from 'vuex'

Vue.use(Vuex)

export default new Vuex.Store({
	state: {
		name: "需要获取信息",
		age: 12
	},
	mutations: {
		grow_up(state) {
			state.age += 1;
		},
		getInfo_mutations(state, name) {
			state.name = name
		}
	},
	actions: {
		getInfo_Actions(context) {
			setTimeout(
				() => {
					context.commit("getInfo_mutations", "离散")
				}, 1000
			)
		}
	},
	modules: {}
})

支持 mapActions 的使用

Module 的使用

作用

  • 由于 state 使用的是单一状态树,所有的状态都需要几种在一个对象中 状态一多往往就会很麻烦
  • 使用 Module 模块化设计可以将状态风格成一个个小模块.
状态的使用

使用

  • 创建一个 store/module 的目录

  • 在里面创建一个 Teacher.js 的文件 并 export default 导出 空对象

export default {
	state:{
		name: "pink老师"
		
	},
	
	getters: {
		getNameTeacher(state){
			return state.name + "老师";
		}
	}
}
  • store/index.js 中引用 Teacher.js文件,
import Vue from 'vue'
import Vuex from 'vuex'
import Teacher from "./Module/Teacher.js"

Vue.use(Vuex)

export default new Vuex.Store({
	state: {...},
	...
	modules: {
		Teacher
	}
})
  • 创建 Teacher 组件 并注册到 router/index.js
<template>
  <div>
  <h2>  老师: {{ $store.state.Teacher.name }} </h2> 
      <!-- 获取Module的组件的时候使用 $store.state.模块名.状态变量名 -->
  <h2>
	  学生: {{ $store.state.name }}
  </h2>
  </div>
</template>

<script>
	export default{
		name: "Teacher"
	}
</script>

状态修改函数的调用

但是我们发送状态修改到mutations就不用使用Teacher.commit了 而是直接使用 commit

methods: {
	getName(){
		this.$store.commit("getTeacher")
	}
}

也就是说我们可以直接使用 模块的修改状态函数

<template>
  <div>
  <h2>  老师: {{ $store.state.Teacher.name }} </h2>
  <h2>
	  学生: {{ $store.state.name }}
  </h2>
  <button @click="ChangeTeacher">修改老师</button>
  </div>
</template>

<script>
	import {mapMutations} from "vuex"
	export default{
		name: "Teacher",
		methods:{
			...mapMutations({'ChangeTeacher': "setName"})
		}
	}
</script>


但是当我们的模块里的修改状态函数与另一个模块的修改状态函数(包括 index.js里的修改状态函数)重名的时候, 会全部执行, 为了单独使用某个模块的修改状态函数 我们可以直接给模块设置命名空间来进行区别

注册

export default {
	
	namespaced: true, // 开启命名空间
	
	state:{
		...	
	},
	
	getters: {
		...
	},
	
	mutations: {
		...
	}
}

就这么使用即可:

this.$store.commit("Teacher/ChangeTeacher")
状态Getters的使用

return this.$store.getters["list/name"]

文件抽离

如果我们的store对象或者是模块对象的actives mutations state 等对象代码过大的时候 我们可以单独的去创建一个文件进行分离

分离之后在 js 文件中import分离出去的代码即可

比如我将某 index.js 的 state进行分离

import state from "../State/Teacher"
export default {
	
	namespaced: true, // 开启命名空间
	
	state, // ES6 语法 ES5为 state: state
	
	getters: {
		getNameTeacher(state){
			return state.name + "老师";
		}
	},
	
	mutations: {
		 ChangeTeacher(state){
			 state.name = "罗翔";
		 }
	}
}

这是我们分离出去的代码

export default {
	name: "pink老师"
}
  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值