Vue
(一)Vue简介
- Vue是一个JavaScript框架,有其独特的使用规则,按照其规则使用,可以事半功倍
- 它可以大大简化Dom操作
- 具有响应式数据驱动
- 官方文档 官方文档
一.Vue特点
- 采用组件化模式,提高代码复用率、且让代码更好维护(组件.vue==HTML+CSS+JS)
- 声明式编码,让编码人员无需直接操作DOM,提高开发效率
- 使用虚拟DOM+优秀的Diff算法,尽量复用DOM结点
(二)Vue核心基础
一、Vue基础
1.导入(安装)
<!-- 开发环境版本,包含了有帮助的命令行警告 -->
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
<!-- 生产环境版本,优化了尺寸和速度 -->
<script src="https://cdn.jsdelivr.net/npm/vue@2"></script>
2.el:挂载点
<div id="app">
{{ message }}
</div>
//第一种写法
var app = new Vue({
el: '#app', //el:挂载点
data: { // data:数据对象
message: 'Hello Vue!'
}
})
//第二种写法
var app = new Vue({
data: { // data:数据对象
message: 'Hello Vue!'
}
})
app.$mount('#app'); //挂载
挂载点 :选择HTML元素,Vue会管理该元素及其后代元素(不要挂载和标签)
注意:挂载只能一一对应,不能一对多,也不能多对一
3.data:数据对象
可以嵌套对象,数组等复杂数据,语法与JS语法一致
//data对象写法
data:{
massage:'hello!'
}
//data函数写法
//注意!组件中必须用这种写法
data(){
return{
massage:'hello!'
}
}
//注意!凡是由Vue管理的函数只能定义为普通函数,不能是箭头函数!!!
4.MVVM模型
- M:模型(Model):对应data中的数据
- V:视图(View):DOM
- VM:视图模型(ViewModel):Vue实例对象
- data中所有的属性最终都会出现在VM上(数据代理)
5.Object.defineProperty()回顾
let test={
name:'test'
}
let a=18;
//为对象添加属性,第三个参数为配置项对象
Object.defineProperty(test,'age',{
value:18,
enumerable:true, //控制属性是否可以枚举,默认为false
writable:true, //控制属性是否可以被修改,默认false
configurable:true, //控制属性是否可以被删除,默认false
//当有人读取test的age属性时,get函数(getter)会被调用,而且返回值就是age的值
get(){
console.log('有人读取了age属性');
return a;
},
//当有人修改test的age属性时,set函数(setter)会被调用,而且会收到修改的具体值
set(value){
console.log('有人修改了age属性,且值是'+value);
a=value;
}
})
6.数据代理(重点难点)
数据代理:通过一个对象代理另一个对象中属性的操作(读/写)
- 数据代理的好处:更加方便的操作data中的数据
- 基表原理:
- 通过Object.defineProperty()将data对象中的所有属性添加到VM上
- 为每一个添加到VM上的属性定义一个getter和setter
- 在getter和setter内部去操作(读/写)data中对应属性
二、Vue指令
1.v-text
v-text : 设置标签的文本值(textContent)
<div id="app">
<h1 v-text="massage+'!!!!'"></h1>
<h1 v-text="massage+inf"></h1>
<h1> {{massage}}啊!{{inf}}</h1>
</div>
var app=new Vue({
el:'#app',
data:{
massage:'你好',
inf:'世界'
}
})
2.v-html
v-html : 设置标签的innerHTML
<div id="app">
<h1 v-text="link"></h1>
<h1 v-html="link"></h1>
</div>
var app=new Vue({
el:'#app',
data:{
link:'<a href="https://www.baidu.com">百度</a>'
}
})
注意:v-html存在安全性问题!!!
- 在网站上动态渲染任何HTML是非常危险的,容易导致XSS攻击
- 一定要在可信的内容上使用v-html,一定不能用在用户提交的内容上!
3.v-on
3.1 v-on : 为元素绑定事件
//语法
<div id="app">
<input type="button" value="点我一下" v-on:事件="事件响应函数名">
<input type="button" value="点我一下" @事件="事件响应函数名"> <!--更简单的写法-->
</div>
//实例
<div id="app">
<input type="button" value="点我一下" @click="fun">
</div>
var app=new Vue({
el:'#app',
methods:{ //事件响应函数写这里
fun:function(){
alert('你好啊!');
}
}
})
3.2 v-on补充:传递自定义参数,事件修饰符
<div id="app">
<input type="button" value="点我一下" @click="fun(name)">
<!-- $event占位,用于传递事件对象 -->
<input type="text" @keyup.enter="fun($event,'李四')"> <!-- Vue提供了键盘按键的便捷绑定方式 @keyup.按键名="事件响应函数" -->
</div>
var app=new Vue({
el:'#app',
data:{
name:'张三'
},
methods:{
fun:function(event,name){
console.log(event);
alert('你好啊!'+name);
}
}
})
注 :更多详细用法查看文档!官方文档
3.3 Vue中的事件修饰符
- prevent:阻止默认事件(常用)
- stop:阻止事件冒泡(常用)
- once:事件只触发一次(常用)
- capture:使用事件的捕获模式
- self:只有event.target是当前操作元素时才触发事件
- passive:事件的默认行为立即执行,无需等待事件回调执行完毕
.test5,.test2{
width: 500px;
height: 100px;
padding: 20px;
background-color: skyblue;
}
.test41{
width: 500px;
height: 100px;
background-color: springgreen;
padding: 20px;
}
.test42{
width: 400px;
height: 50px;
background-color: skyblue;
}
.test6{
width: 300px;
height: 100px;
overflow: auto;
}
.test61{
width: 200px;
height: 1000px;
background-color: skyblue;
}
<div id="root">
<!-- prevent:阻止默认事件(常用) -->
<a href="https://www.baidu.com" @click.prevent='test1'>百度一下</a>
<!-- stop:阻止事件冒泡(常用) -->
<div class="test2" @click='test2'>
<button @click.stop='test2'>test2</button>
</div>
<!-- once:事件只触发一次(常用) -->
<button @click.once='test3'>test3</button>
<!-- capture:使用事件的捕获模式 -->
<div class="test41" @click.capture="test4('test41')">
test41
<div class="test42" @click="test4('test42')">
test42
</div>
</div>
<!-- self:只有event.target是当前操作元素时才触发事件 -->
<div class="test5" @click.self='test5'>
<button @click='test5'>test5</button>
</div>
<!-- passive:事件的默认行为立即执行,无需等待事件回调执行完毕 -->
<div class="test6" @wheel.passive="test6">
test6
<div class="test61"></div>
</div>
</div>
var vm=new Vue({
el:'#root',
data:{
},
methods:{
test1(){
alert('就不百度一下!');
},
test2(){
alert('不会冒泡哟!');
},
test3(){
alert('再点一次我就不会弹出来了!');
},
test4(i){
alert(i+'捕获到了点击事件!');
},
test5(e){
alert('你点的是'+e.target);
},
test6(){
for(var i=0;i<10000000;i++){
console.log('test6');
}
}
}
})
注:修饰符可以多个连着使用
3.4 键盘事件
-
Vue常用事件别名
- enter:回车
- delete:删除/退格
- esc:退出
- space:空格
- tab:换行(特殊,必须配合keydown使用,因为tab默认事件是切换焦点)
- up:上
- down:下
- left:左
- right:右
-
Vue未提供别名的按键,可以使用按键原始的Key值去绑定,但是注意如果Key值为两个以上单词,转化为-连接命名(例如:CapsLock:caps-lock)
//获取键值,键码 //为某一元素绑定按键事件(keydown,keyup都行) console.log(event.key,event.keyCode);
-
系统修饰键(用法特殊):ctrl,alt,shift,meta(win键)
- 配合keyup使用:按下修饰键同时,再按下其他键,随后释放其他键,事件才触发。
- 配合keydown使用:正常触发事件
-
可以使用kkeyCode去指定具体按键,但是不推荐!!!
- 不同键盘同一键键码可能不同
- 浏览器逐渐不支持
-
Vue.config.keyCodes.自定义键名=键码,可以自定义键名,不推荐!!!
注:键指定可以连着写实现Ctrl+y等事件绑定
4.v-show
v-show : 根据表达式真假,切换元素的显示和隐藏(操作样式:display)
<div id="app">
<img src="" v-show="true"> <!-- 直接布尔值 -->
<img src="" v-show="isShow"> <!-- 变量 -->
<img src="" v-show="age>=18"> <!-- 表达式 -->
</div>
var app=new Vue({
el:'#app',
data:{
isShow:true,
age:20
}
})
5.v-if/v-else-if/v-else
v-if : 根据表达式真假,切换元素显示和隐藏(操作Dom元素,直接将Dom元素移除或添加)
- 用起来和普通编程语言条件分支语句一样
<div id="app">
<img src="" v-if="true"> <!-- 直接布尔值 -->
<img src="" v-if="isShow"> <!-- 变量 -->
<img src="" v-if="age>=18"> <!-- 表达式 -->
<div v-if="n===1">a</div>
<div v-else-if="n===2">b</div>
<div v-else-if="n===3">c</div>
<div v-else>d</div>
<!-- 需求:条件相同,显示不同内容 -->
<div v-if="n===1">a</div>
<div v-if="n===1">b</div>
<div v-if="n===1">c</div>
<!-- 解决方案一 会破环原HTML结构,进而影响样式等生效,不推荐使用-->
<div v-if="n===1">
<div>a</div>
<div>b</div>
<div>c</div>
</div>
<!-- 解决方案二 -->
<template v-if="n===1">
<div>a</div>
<div>b</div>
<div>c</div>
</template>
<!-- template只能与v-if一起使用,不能和v-show一起使用 -->
</div>
var app=new Vue({
el:'#app',
data:{
isShow:true,
age:20,
n:2
}
})
6.v-bind
v-bind : 设置元素属性(如:src,title,class等)
<div id="app">
<img v-bind:src="imgsrc"> <!-- v-bind:属性名="属性值" -->
<img :alt="ingalt+'!!!'"> <!-- 简写为 :属性名="属性值" -->
<img v-bind:class="isActive?'active':''">
<img v-bind:class="{active:isActive}">
</div>
var app=new Vue({
el:'#app',
data:{
imgsrc:'./01.jpg',
imgalt:'图片1',
isActive:false
}
})
6.1 通过class绑定样式
<div id="root">
<!-- 字符串写法,适用于:样式的类名不确定,需要动态指定(只有一个样式(类)) -->
<div :class="test1" @click="change1">test1</div>
<!-- 数组写法,适用于:样式的个数不确定,名字不确定 -->
<div class="basic" :class="test2" @click="change2">test2</div>
<!-- 对象写法,适用于:样式个数确定,名字确定,但是用不用不确定 -->
<div class="basic" :class="test3" @click="change3">test3</div>
</div>
.class1{
width: 200px;
height: 200px;
background-color: skyblue;
}
.class2{
width: 200px;
height: 400px;
background-color: skyblue;
}
.class3{
width: 400px;
height: 200px;
background-color: skyblue;
}
.class4{
width: 200px;
height: 200px;
background-color: slateblue;
}
.basic{
width: 100px;
height: 100px;
background-color: skyblue;
}
.plus1{
border: black solid 3px;
}
.plus2{
border-radius: 10px;
}
.plus3{
color: blue;
}
var vm=new Vue({
el:'#root',
data:{
test1:'class1',
test2:[],
test3:{
plus1:false,
plus2:false,
plus3:false,
}
},
methods:{
change1(){
var classArr=['class1','class2','class3','class4'];
var index=Math.floor(Math.random()*4);
console.log(index);
this.test1=classArr[index];
},
change2(){
var classArr=['plus1','plus2','plus3'];
if(this.test2.length<3){
this.test2.push(classArr[this.test2.length]);
}else{
for(var i=0;i<3;++i){
this.test2.pop();
}
}
},
change3(){
var a,b,c;
a=Math.floor(Math.random()*2);
b=Math.floor(Math.random()*2);
c=Math.floor(Math.random()*2);
if(a)this.test3.plus1=!this.test3.plus1;
if(b)this.test3.plus2=!this.test3.plus2;
if(c)this.test3.plus3=!this.test3.plus3;
}
}
})
6.2 通过style绑定
<div id="root">
<!-- 对象写法 -->
<div class="basic" :style="styleObj">test1</div>
<!-- 数组方法 -->
<div class="basic" :style="styleArr">test2</div>
</div>
.basic{
width: 100px;
height: 100px;
background-color: skyblue;
border-radius: ;
}
var vm=new Vue({
el:'#root',
data:{
styleObj:{
fontSize:'30px',
color:'red',
},
styleArr:[
{
fontSize:'30px',
color:'red'
},
{marginTop:'10px'},
{
border:'black solid 2px',
borderRadius:'10%'
}
]
},
})
7.v-for
7.1 v-for : 根据数据生成列表结构
- 可以遍历:数组、对象、字符串(不常用)、指定次数(不常用)
- 用in或者of都行
<div id="root">
<!-- 遍历数组 -->
<h1>学生列表</h1>
<ul>
<li v-for="(item,index) in students" :key="index">
{{item.id}}:{{item.name}}-{{item.age}}
</li>
</ul>
<!-- 遍历对象 -->
<h1>学生信息</h1>
<ul>
<li v-for="(value,key) of student" :key="key">
{{key}}:{{value}}
</li>
</ul>
<!-- 遍历字符串 -->
<h1>hello</h1>
<ul>
<li v-for="(char,index) of str" :key="index">
{{index}}:{{char}}
</li>
</ul>
<!-- 遍历指定次数 -->
<h1>12345</h1>
<ul>
<li v-for="(v,index) in 5" :key="index">
{{index}}:{{v}}
</li>
</ul>
</div>
var vm=new Vue({
el:'#root',
data:{
students:[
{id:0,name:'张三',age:18},
{id:1,name:'李四',age:20},
{id:2,name:'王五',age:18},
{id:3,name:'赵六',age:19},
],
student:{
id:0,
name:'张三',
age:18
},
str:'hello'
},
})
7.2 v-for中的key属性(重点难点)
面试题:react、vue中key有什么作用?(key的原理是什么?)
要点:就是为了提高效率
- 虚拟DOM中key的作用:key是虚拟DOM对象的标识,当数据发生变化时,Vue会根据新数据生成新的虚拟DOM,随后Vue进行新的虚拟DOM与旧的虚拟DOM的差异比较(diff算法)
- 比对规则:
- 旧的虚拟DOM中找到了与新的虚拟DOM相同的key:
- 若虚拟DOM中内容没变,直接使用之前的真实DOM!(提高效率)
- 如果虚拟DOM中内容变了,则生成新的真实DOM,随后替换掉页面中之前的真实DOM。
- 旧虚拟DOM中未找到与新的虚拟DOM相同的key:
- 创建新的真实DOM,随后渲染到页面中
- 旧的虚拟DOM中找到了与新的虚拟DOM相同的key:
- v-for用索引值(index)作为key可能会引发的问题(不写key,Vue就会默认这样使用):
- 如果对数据进行逆序添加、逆序删除等破环顺序的操作:
- 会产生没有必要的真实DOM更新,效率低
- 如果结构中还有输入类DOM:
- 会产生错误DOM更新,效率低,界面还会出错(乱序)
- 如果对数据进行逆序添加、逆序删除等破环顺序的操作:
- 开发中如何选择key
- 最好使用每条数据的唯一标识作为key
- 如果不存在对数据进行逆序添加、逆序删除等破环顺序的操作,仅仅用于渲染展示,使用索引(index)也是没有问题的
8.v-model
8.1 v-model : 获取和设置表单元素的值(双向数据绑定)
<div id="app">
<input type="text" v-model:value="massage"> <!-- 简写如下 -->
<input type="text" v-model="massage"> <!-- 文本框内容与massage双向绑定(输入值改变,massage值会相应改变) -->
<h1>{{massage}}</h1>
</div>
var app=new Vue({
el:'#app',
data:{
massage:'你好!'
}
})
8.2 v-mdel补充
-
如果是文本框,则v-model绑定的是value值,用户输入的就是value
-
如果是单选框,责v-model绑定的是value值,必须要给标签配置value
-
如果是多选框,则:
- 没有配置value,那么绑定的就是checked(true or false)
- 配置了value
- 如果v-modle的初始值是非数组,那么绑定的就是checked(true or false)
- 如果v-modle的初始值是数组,那么绑定的就是value组成的数组
-
v-model修饰符
- number:输入字符串转化为有效数字
- lazy:失去焦点才收集更新数据
- trim:去掉字符串首尾的空格
-
实例
<div id="root"> <form @submit.prevent="output"> <!-- v-model.trim 会自动去掉字符串前后空格 --> 用户名: <input type="text" v-model.trim="userinf.username"> <br><br> 密码: <input type="password" v-model="userinf.password" autocomplete="on"> <br><br> <!-- type="number" 可以限制用户输入只能是数字 --> <!-- v-model.number 可以自动将输入的内容转化为数值类型 --> 年龄: <input type="number" v-model.number="userinf.age"> <br><br> 性别: 男<input type="radio" name="gender" value="男" v-model="userinf.gender"> 女<input type="radio" name="gender" value="女" v-model="userinf.gender"> <br><br> 班级: <select v-model="userinf.class"> <option value="">请选择班级</option> <option value="一班">一班</option> <option value="二班">二班</option> <option value="三班">三班</option> </select> <br><br> 爱好: 学习<input type="checkbox" value="学习" v-model="userinf.hobby"> 打游戏<input type="checkbox" value="打游戏" v-model="userinf.hobby"> 开车<input type="checkbox" value="开车" v-model="userinf.hobby"> <br><br> <!-- v-model.lazy 失去焦点才会收集更新信息 --> 其他信息:<textarea v-model.lazy="userinf.other"></textarea> <br><br> <input type="checkbox" v-model="userinf.agree">是否同意<a href="#">用户协议</a> <br><br> <button>提交</button> </form> </div>
var vm = new Vue({ el: '#root', data: { userinf:{ username:'', password:'', age:'', class:'', hobby:[], other:'', agree:'' } }, methods: { output(){ console.log(JSON.stringify(this.userinf)); } }, })
9.v-cloak
- 没有值
- 是一个特殊的属性,Vue实例创建完毕并接管容器后,会自动删除v-colak属性
- 使用css配合v-cloak可以解决网速慢时页面展示出未被Vue解析的模板页面的问题
<!DOCTYPE html>
<html lang="ch">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
[v-clock]{
display: none;
}
</style>
</head>
<body>
<!-- 整个页面核心结构 -->
<div id="root">
<h1 v-cloak>{{name}}</h1>
</div>
</body>
<!-- 如果网络卡顿,Vue.js无法在打开页面时立即加载 -->
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
<script>
var vm = new Vue({
el: '#root',
data: {
name:'Vue'
},
})
</script>
</html>
10.v-once
- v-once所在结点在初次动态渲染后,就会被视为静态内容
- 以后数据改变不会引起v-once所在结点的更新,可以优化性能
<div id="root">
<h1 v-once>初始n: {{n}}</h1>
<h1 >n: {{n}}</h1>
<button @click="n++">n+1</button>
</div>
var vm = new Vue({
el: '#root',
data: {
n:1
},
})
11.v-pre
- Vue会跳过其所在结点的解析过程
- 可以利用它跳过:没有用指令,插值等结点,提高性能
<div id="root">
<h1>欢迎访问该系统!!!!!</h1>
<h1 >n: {{n}}</h1>
<button @click="n++">n+1</button>
</div>
var vm = new Vue({
el: '#root',
data: {
n:1
},
})
三、计算属性(重点难点)
计算属性:根据原有的属性经过加工处理计算出来的属性叫做计算属性(computed)
- 原理:底层借助了Object.defineproperty方法提供的getter和setter
- get什么时候执行
- 初次读取计算属性
- 当计算属性依赖的数据发生改变时会被再次调用
- 优势:与methods实现相比,内部有缓存机制(可复用),效率更高
- 注意:
- 计算属性最终会被处理为vm的一个属性,直接读取使用即可
- 计算属性如果需要被修改,则必须使用setter,setter中对应修改计算属性的依赖数据即可
<div id="root">
性:<input type="text" v-model="firstname"> <br><br>
名:<input type="text" v-model="lastname"> <br><br>
性名: <span>{{name}}</span>
</div>
var vm=new Vue({
el:'#root',
data:{
firstname:'张',
lastname:'三'
},
computed:{
//这里得写成一个对象,但是Vue会把它解析处理为vm中的属性
name:{
//getter,setter中的this都已经被Vue预先处理为了vm,方便使用
get(){
return this.firstname+'-'+this.lastname;
},
set(value){
var values=value.split('-');
this.firstname=values[0];
this.lastname=values[1];
}
}
//当只需要读取,不需要修改时,可以简写为
// name(){
// return this.firstname+'-'+this.lastname;
// }
}
})
四、侦听(监视)属性
1.侦听(监视)属性
- 当侦听(监视)属性变化时,handler自动调用,进行相关操作
- 侦听(监视)属性必须存在,才能被侦听(监视)
<div id="root">
<h1>你真是个大{{description}}</h1>
<button @click='change'>点我</button>
</div>
var vm=new Vue({
el:'#root',
data:{
isCool:true
},
methods:{
change(){
this.isCool=!this.isCool;
}
},
computed:{
description(){
return this.isCool? '帅哥':'聪明';
}
},
watch:{
isCool:{
immediate:true, //初始化时让handler调用一次
// 当被侦听(监视)的值变化时,handler被调用
handler(newvalue,oldvalue){
console.log('isCool被改变了,从'+oldvalue+'改成了'+newvalue);
}
},
//当侦听(监视)时配置项只有handler时,可以简写为
// isColl(){
// console.log('isCool被改变了,从'+oldvalue+'改成了'+newvalue);
// }
}
})
// 另一种写法
// vm.$watch('isCool',{
// immediate:true, //初始化时让handler调用一次
// // 当被侦听(监视)的值变化时,handler被调用
// handler(newvalue,oldvalue){
// console.log('isCool被改变了,从'+oldvalue+'改成了'+newvalue);
// }
// })
//当侦听(监视)时配置项只有handler时,可以简写为
// vm.$watch('isCool',function(){
// console.log('isCool被改变了,从'+oldvalue+'改成了'+newvalue);
// })
2.深度侦听(监视)
- Vue中的watch默认不侦听(监视)对象内部值的改变(一层)
- 配置项
deep:true
可以侦听(监视)对象内部属性值的改变(多层) - Vue自身是可以侦听(监视)对象内部值改变,但是Vue提供的watch默认不可以
- 使用watch时根据实际使用需求,决定是否采用深度侦听(监视)
3.计算属性(computed)和侦听(监视)属性(watch)的区别
- computed能完成的功能,watch都可以完成。
- watch能完成的功能,computed不一定能成功,例如:watch可以进行异步操作
- 两个都行的用computed更简单
- 注意!!
- 所被Vue管理的函数,最好写成普通函数,这样this的指向才是vm或组件实例对象
- 所有不被Vue管理的函数(定时器回调函数,ajax回调函数,Promise的回调函数等),最好写成箭头函数,这样this的指向才是vm或组件实例对象
4.侦听(监视)原理(重点难点)
侦听(监视):包括Vue对于数据改变,动态更新页面中用到该数据的DOM,以及watch中侦听(监视)数据地秤实现(同样的逻辑)
-
Vue会侦听(监视)data中所有层级的数据
-
如何侦听(监视)对象中的数据?
-
通过setter实现侦听(监视),而且要是在创建Vue实例对象时就传入的数据才会被Vue侦听(监视)
-
对象中后追加的属性,Vue默认不侦听(监视),进而也不会做出响应式处理
-
如果想让后追加的属性也可以被Vue侦听(监视),并响应式处理,需要使用:
Vue.set() vm.$set()
-
-
如何侦听(监视)数组中的数据?
-
通过包裹数组更新元素方法实现,本质上就是两个处理
- 通过原生对应的方法对数组进行更新
- 重新解析模板,渲染页面
-
在Vue中修改某数组中某元素一定要用如下几种方法:
//这些方法用起来和原生JS中Array对象的这些方法一样,但是实际上底层逻辑不一样,Vue对这些方法做了如上述的包裹处理 push() pop() shift() unshift() splice() sort() reverse() //Vue的set方法 //注意!! 这两个方法用起来一样,但是都有局限, //不能为vm或者vm的根数据对象(即 data等)添加属性!!! Vue.set() vm.$set()
-
-
实例
<div id="root"> <button @click="student.age++">年龄加一岁</button> <br> <button @click.once="addgender">添加性别属性,默认男</button> <br> <button @click.once="addaf">在朋友们最前面加一个朋友</button> <br> <button @click.once="updateff">修改第一个朋友的名字</button> <br> <button @click.once="addh">添加一个爱好</button> <br> <button @click.once="updatafh">修改第一个爱好</button> <br> <h1>姓名 {{student.name}}</h1> <h2>绰号 {{student.nickname}}</h2> <h3>年龄 {{student.age}}</h3> <h3 v-if="student.gender">性别 {{student.gender}}</h3> <h3>朋友们</h3> <ul> <li v-for="(f,index) in student.friends" :key="index"> {{f.name}}--{{f.age}} </li> </ul> <h3>爱好</h3> <ul> <li v-for="(h,index) in student.hobby" :key="index"> {{h}} </li> </ul> </div>
var vm = new Vue({ el: '#root', data: { student: { name: '张三', nickname: '法外狂徒', age: 20, friends: [ { name: '罗老师', age: 18 }, { name: '李四', age: 22 }, ], hobby: ['抽烟', '赌博', '干坏事'] } }, methods: { addgender() { // 第一种 // Vue.set(this.student,'gender','男'); // 第二种 this.$set(this.student, 'gender', '男'); }, addaf() { this.student.friends.unshift({ name: '王五', age: 40 }); }, updateff() { this.student.friends[0].name = '王大炮' }, addh() { this.student.hobby.push('开车'); }, updatafh() { // 第一种 // this.student.hobby.splice(0,1,'学习'); // 第二种 // Vue.set(this.student.hobby, 0, '学习'); // 第三种 this.$set(this.student.hobby, 0, '学习'); } } })
五、过滤器
过滤器:对要显示的数据进行特定格式化后再显示(适用于一些简单逻辑处理)
- 语法:
- 注册过滤器:Vue.filter(name,callback)(全局过滤器) ,new Vue({filters:{}})(局部过滤器)
- 使用过滤器:{{xxx | 过滤器名}} 或 v-bind:属性 = “xxx | 过滤器名”
- 备注:
六、自定义Vue指令
-
定义语法:
-
局部指令
new Vue({ directives:{ 指令名:配置对象 //配置对象 bind() //指令与元素成功绑定的时候调用 inserted() //指令所在元素被插入页面时调用 update() //指令所在模板被重新解析时调用 } //对象形式 //简写 directives:{ 指令名(){} //写成一个函数 //这个函数相对于定义了上述配置项的bind()和update() } })
-
全局指令
Vue.directive(指令名,配置对象) Vue.directive(指令名,配置函数)
-
-
备注
- 指令定义时不加v-,但是使用的时候需要加v-
- 指令名如果是多个单词,使用kebab-case命名方式,不用驼峰命名方式。
七、生命周期(重点难点)
- 生命周期,又叫做生命周期回调函数、生命周期函数、生命周期钩子
- 生命周期是Vue在关键时刻帮我们调用了一些特殊名称的函数
- 生命周期函数名称不可更改,但是函数逻辑是由程序员根据需求编写的
- 生命周期函数中的this指向的是vm或组件实例对象
常用的生命周期钩子:
- mounted:发送ajax请求、启动定时器、绑定自定义事件、订阅消息等(初始化操作)
- beforeDestroy:清除定时器、解除自定义事件绑定、取消订阅消息等(收尾工作)
销毁Vue实例:
- 销毁后借助Vue开发者工具将看不见任何信息
- 销毁后自定义事件会失效,但是Vue的工作成果(最后一次更新后的真实DOM)会保留,原生DOM的事件依然有效
- 一般不会在beforeDestroy钩子中操作Vue实例相关数据,因为即使操了,Vue也不会再进行更新等流程
(三)Vue组件化编程
一、简介
-
模块:向外提供特定功能的JS文件
- JS文件在实际开发中很多很复杂
- 可以复用JS,简化JS的编写,提高效率
- 模块化:当应用的JS都以模块形式来编写,那么这个应用就是一个模块化的应用
-
组件:用来实现局部(特定)功能效果的代码及依赖数据集合(html/css/js/img/mp3…)
- 实际开发中一个界面的功能很复杂
- 复用编码,简化项目编码,便于理清项目结构,提高运行效率
- 组件化:当应用都是以组件的形式来搭建编写的,那么这个应用就是一个组件化的应用
- 非单文件组件:一个文件中包含了一个到多个组件
- 单文件组件:一个文件中只包含一个文件
二、基础
1.非单文件组件
1.1 基础使用
-
定义组件
- 使用Vue.extend(options)创建,其中options和创建Vue实例时传入的配置项基基本一样,有如下区别
- el配置项不能使用,因为最终组件都会被一个Vue实例管理,由Vue(大哥)决定他及他的组件(小弟)为哪个容器服务
- data必须用函数形式,因为组件为了被复用,用函数才能避免数据存在引用关系
- 注:组件中用template配置组件结构
- 使用Vue.extend(options)创建,其中options和创建Vue实例时传入的配置项基基本一样,有如下区别
-
注册组件
- 局部注册:创建Vue实例时的components配置项
- 全局注册:Vue.component(‘组件名’,组件);
- 组件名不要与HTML标签名冲突
-
使用组件
- 组件标签(类似于HTML标签)
-
实例
<div id="root"> <h1>root</h1> <heade></heade> <hr> <bodyer></bodyer> <foote></foote> </div> <div id="root1"> <h1>root1</h1> <foote></foote> </div>
// 定义组件header var heade=Vue.extend({ template:` <div> <h1>欢迎访问{{name}}系统!</h1> </div> `, data(){ return { name:'智慧张三' } } }) // 定义组件bodyer var bodyer=Vue.extend({ template:` <div> <h1>{{name}}列表</h1> <ul> <li v-for="(s,index) in students"> {{s.name}}---{{s.age}} </li> </ul> </div> `, data(){ return { name:'学生', students:[ {name:'张三',age:18}, {name:'罗老师',age:30}, ] } } }) var foote=Vue.extend({ template:` <div> <h2>版本号{{num}}!</h2> </div> `, data(){ return { num:'v1.0.1' } } }) // 注册组件(全局) Vue.component('foote',foote) var vm = new Vue({ el: '#root', data: { }, // 注册组件(局部) components:{ heade, bodyer } }) new Vue({ el: '#root1', })
1.2 组件命名等注意事项
-
组件命名
- 一个单词
- 首字母小写
- 首字母大写
- 多个单词
- kebab-case命名法
- 单词首字母全大写(需要Vue脚手架支持)
- 注意:
- 组件命名需要回避HTML标签
- 可以使用name属性指定组件在Vue开发者工具中的展现名字(组件定义时)
- 一个单词
-
组件标签
- 第一种写法:双标签,如:
<bodyer></bodyer>
- 第二种写法:自闭合标签,如:
<bodyer/>
(需要Vue脚手架支持,否则其后的组件不能被渲染)
- 第一种写法:双标签,如:
-
定义简写
var heade=Vue.extend(options) //简写 var heade=options;
1.3 关于VueComponent函数
- 组件本质上是一个名为VueComponent的构造函数,而且不需要由我们定义,是Vue.extend生成的
- 我们在使用组件时,Vue解析到会帮我们创建对应组件的实例对象,即执行
new VueComponent(options);
- 每次调用Vue.extend,返回的都是一个全新的VueComponent!!!
- 关于this指向
- 组件配置中:data函数、methods中的函数、watch中的函数、computed中的函数,它们的this都指向VueComponent实例对象
- new Vue()配置中:data函数、methods中的函数、watch中的函数、computed中的函数,它们的this都指向Vue实例对象
1.4 一个重要的内置关系
VueComponent.prototype.__proto__ === Vue.prototype
- 目的:让组件实例对象也可以访问到Vue原型上的属性、方法
2.单文件组件
(四)脚手架(CLI)的使用
一、初始化脚手架
- 脚手架是Vue官方提供的标准化开发工具(开发平台)
- 官方文档
- 安装步骤
npm install -g @vue/cli
全局安装@vue/cli (只需要安装一次)- 在你要创建项目的目录下,使用命令创建项目
vue create xxxx
- 启动项目
npm run serve
- 备注:
-
如果出现下载缓慢,请配置npm淘宝镜像:
npm config set registry https://registry.npm.taobao.org --global
npm config set disturl https: //npm.taobao.org/dist --global
-
Vue脚手架隐藏了所有的webpack相关的配置,如想要查看具体的wekpack配置,请执行:
vue inspect > output.js
-
二、关于不同版本Vue
- vue.js与vue.runtime.xxx.js的区别:
- vue.js是完整版的vue。包含了核心功能+模板解析器
- vue.runtime.xxx.js是运行版的vue,只包含了核心功能,没有模板解析器。
- 因为vue.runtime.xxx.js没有模板解析器,所以不能使用template配置项,需要使用render函数将收到的createELement函数去指定具体内容。
三、vue.config.js配置文件
- 使用output.js可以查看到脚手架默认配置。
- 使用vue.config.js可以对脚手架进行个性化定制,详见:脚手架配置
//单文件组件示例
<template>
<div>
<School></School>
<Student></Student>
</div>
</template>
<script>
import School from './components/Test1.vue';
import Student from './components/Test2.vue';
export default {
name:'App',
components:{
School,
Student
}
}
</script>
<style>
</style>
四、ref属性
-
用来给元素或者子组件注册引用信息(相当于id)
-
应用在html标签上获取真实的DOM元素,应用在组件标签上是组件的实例对象(vc)
-
使用方式
<template> <div> <h1 ref="h1">你好</h1> <School ref="sch"></School> <button ref="but" @click="console">输出refs</button> <Student></Student> </div> </template> <script> import School from './components/Test1.vue'; import Student from './components/Test2.vue'; export default { name:'App', components:{ School, Student }, methods: { console(){ console.log(this.$refs); } }, } </script>
五、配置项props
功能:让组件接收外部传过来的数据
备注:props是只读的,Vue底层会监视你对props的修改,如果进行了修改,就会发出警告
export default {
name:'Student',
data(){
return {
mas:'你好,我是:'
}
},
// 第一种方式:(只是简单接收)
// props:['name','gender','age'],
// 第二种方式:(限制数据类型)
// props:{
// name:String,
// gender:String,
// age:Number
// }
// 第三种方式:(详细配置)
props:{
name:{
type:String, //类型
required:true //必要性
},
gender:{
type:String,
default:'男' //默认值
},
age:{
type:Number,
required:true
}
}
}
六、混合(混入)
功能:把某些组件共用的配置提取出来,成为一个混合对象
使用方式
//定义并暴露混合
export const mixin={
data(){
return {
message:'你好啊!!'
}
},
methods: {
nihao(){
alert("Hello!");
}
},
mounted(){
console.log("挂载了组件");
},
}
//使用混合
//局部使用
import {mixin} from './mixin';
mixins:[mixin]
//全局使用(在main.js中引入注册混合)
import {mixin} from './mixin';
Vue.mixin(mixin);
七、插件
功能:增强Vue
本质:包含install方法的一个对象,install的第一个参数是Vue,第二个以后的参数是插件使用所需要的数据
//定义插件
export default {
install(Vue){
Vue.prototype.test=()=>{
alert('你好!');
}
}
}
//引入及使用(在main.js中)
import plugin from './plugins';
Vue.use(plugin);
八、scoped样式
作用:让样式只在局部(当前组件生效)其他地方不生效,这样来避免组件间样式冲突
<style scopes></style>
九、web Storage
- 存储内容大小一般支持5MB左右(不同的浏览器可能不一样)
- 浏览端通过Window.sessionStorage和Window.localStorage属性来实现本地存储机制。
- 相关API:
setItem('key','value');
该方法接收一个键和值作为参数,会把键值对添加到存储中,如果键名存在,则直接更新对应的值getItem('key');
该方法根据键获取存储中的值,如果没有,返回nullremoveItem('key');
该方法根据键删除存储中的相应的键值对clear();
清空存储的信息
- SessionStorage(会话存储)存储的内容会随着浏览器关闭而消失
- LocalStorage(本地存储)存储的内容,需要手动清除才会消失
十、组件自定义事件
-
一种组件间通信方式,子组件===>父组件
-
绑定与触发事件
//父组件中 <!-- 为子组件绑定自定义事件,实现子给父传递数据(v-on/@)--> <School @test="getName"></School> <!-- 普通事件绑定的修饰符仍然可用,如once等 --> <!-- 为子组件绑定自定义事件,实现子给父传递数据(ref 更加灵活) --> <School ref="school"></School>
//父组件中 methods: { getName(name){ console.log(name+'拿到了'); } }, mounted(){ setTimeout(()=>{ this.$refs.school.$on('test',this.getName); //使用$once方法,事件只触发一次 //注意:通过该方法绑定事件时,回调建议配置在methods中或者使用箭头函数,否则回调中(this.getName中)的this指向的是触发事件的子组件 },3000) } //子组件中触发事件 this.$emit('test',this.name);
-
解除事件绑定
//在子组件中 this.$off('test'); //解除事件test绑定 this.$off(['test1','test2']); //解除事件test1,test2的绑定 this.$off(); //解除所有事件绑定
-
组件绑定原生DOM事件,需要使用native修饰符
十一、全局事件总线(GlobalEventBus)(重点)
-
一种组件间通信的方式,任意组件间通信(基于组件自定义事件)
-
安装全局事件总线:(实际上就是造一个中转的组件实例对象,该VC可以被所有其他组件访问到)
//在main.js的root组件中 beforeCreate(){ Vue.prototype.$bus=this //$bus就是当前应用的vm,也就作为上述要造的vc }
-
使用事件总线
-
接收数据的组件:给$bus绑定自定义事件,事件的回调在自身
methods:{ test(data){} //回调函数 } mounted(){ this.$bus.$on('事件名',this.test); //绑定事件 }
-
发送数据的组件
this.$bus.$emit('事件名',data); //触发事件
-
-
最好在beforeDestroy钩子中,用$off去解绑当前组件所用到的事件。
十二、消息订阅与发布(pubsub)
-
一种组件间的通信方式,适用于任意组件间通信。
-
使用
-
安装第三方库:(这里用的是pubsub-js,也可以用其他的)
npm i pubsub-js
-
引入:
import pubsub from 'pubsub-js'
-
接收数据的组件中订阅消息,在自身编写回调函数
methods(){ test(data){} } mounted(){ this.pubid=pubsub.subscribe('消息名',this.test); //订阅消息 }
-
提供数据的组件发送消息
pubsub.publish('消息名',data);
-
最好在beforeDestroy钩子中,用pubsub.unsubscribe(this.pubid)去取消消息订阅
-
十三、$nextTick
this.$nextTick(回调函数)
- 作用:在下一次DOM更新结束后执行其回调函数。
- 什么时候用,当改变数据后,要基于更行后的DOM进行某些操作的时候,要在nextTick所指定的回调中执行。
十四、Vue封装的过度与动画
-
在插入、更新或者移除DOM元素时,会在合适的时候给元素添加样式类名
-
v-enter == v-enter-active ==> v-enter-to v-leave == v-leave-active ==> v-leave-to
-
使用方法
-
准备好样式:
- v-enter:进入的起点
- v-enter-active:进入的过程中
- v-enter-to:进入的终点
- v-leave:离开的起点
- v-leave-active:离开的过程中
- v-leave-to:离开的终点
-
使用
<transition>
包裹要过度的元素,并配置name属性:<transition name="test"> <!-- 指定了name属性,需要将类名对应为相应的(例如test-enter) --> <h1 v-show="isShow">你好啊!</h1> </transition>
-
备注:如果有多个元素需要动画效果,则需要使用:
transition-group
,且每个元素都要指定key
值
实例一:
<button @click="change">显示/隐藏</button> <transition name="test" appear> <div v-show="isShow" class="a"> <h1>{{mes}}</h1> </div> </transition>
<button @click="change">显示/隐藏</button> .test-enter-active{ animation: test 1s; } .test-leave-active{ animation: test 1s reverse; } @keyframes test{ from{ transform: translateX(-100%); } to{ transform: translateX(0); } }
实例二:
<transition name="test1" appear> <div v-show="isShow" class="a"> <h1>{{mes}}</h1> </div> </transition>
<button @click="change">显示/隐藏</button> .test1-enter, .test1-leave-to{ transform: translateX(-100%); } .test1-leave, .test1-enter-to{ transform: translateX(0); } .test1-leave-active, .test1-enter-active{ transition: 1s linear; }
实例三:
<button @click="change">显示/隐藏</button> <transition-group name="test2" appear> <div v-show="isShow" class="a" key="1"> <h1>{{mes}}</h1> </div> <div v-show="!isShow" class="a" key="2"> <h1>{{mes}}</h1> </div> </transition-group>
.test2-enter, .test2-leave-to{ transform: translateX(-100%); } .test2-leave, .test2-enter-to{ transform: translateX(0); } .test2-leave-active, .test2-enter-active{ transition: 1s linear; }
-
-
动画库推荐:animate.css
- 安装
npm i animate.css
- 引入
import 'animate.css';
- 官方文档 animate.css
<button @click="change">显示/隐藏</button> <transition appear name="animate__animated animate__bounce" enter-active-class="animate__rubberBand" leave-active-class="animate__backOutDown" > <div v-show="isShow" class="a"> <h1>{{mes}}</h1> </div> </transition>
- 安装
```javascript
import 'animate.css';
//记得配置这个methods
change(){
this.isShow=!this.isShow;
}
十五、插槽
-
作用:让父组件可以向子组件中指定位置插入HTML结构,也是一种组件间通信方式 父组件===>子组件
-
使用
-
默认插槽
//父组件 <template> <List title="汽车"> <img src="" alt=""> </List> <List title="食物"> <ul> <li v-for="(f,i) in foods" :key="i">{{f}}</li> </ul> </List> <List title="游戏"> <!-- <ul> <li v-for="(g,i) in games" :key="i">{{g}}</li> </ul> --> </List> </template> <script> import List from "./components/Test1.vue"; export default { name: "App", components: { List }, data() { return { foods: ["海底捞", "烧烤"], cars: ["劳斯莱斯", "兰博基尼"], games: ["推箱子", "连连看"], }; }, }; </script> <style> .lists, .a { display: flex; justify-content: space-around; } img { width: 100%; } </style> //子组件 <template> <div class="list"> <h1>{{title}} 列表</h1> <slot>没有内容了。。。</slot> </div> </template>
-
具名插槽
//父组件 <template> <div class="lists"> <List title="汽车"> <a slot="test" href="#">更多</a> </List> <List title="食物"> <div slot="test" class="a"> <a href="#">中餐</a> <a href="#">西餐</a> </div> </List> <List title="游戏"> <!-- 在用template标签时可以这样用,也可以slot="test" --> <template v-slot:test> <div class="a"> <a href="#">单机</a> <a href="#">联机</a> </div> <h2>快来游玩吧!</h2> </template> </List> </div> </template> //子组件 <template> <div class="list"> <h1>{{title}} 列表</h1> <slot name="test">没有内容了111。。。</slot> </div> </template>
-
作用域插槽
- 数据在子组件中,但是根据数据生成什么样的结构由父组件决定
//父组件 <template> <div class="lists"> <List title="手机"> <template slot="test2" scope="{phones}"> <ul> <li v-for="(p,i) in phones" :key="i">{{p}}</li> </ul> </template> </List> <List title="手机"> <!-- vue2.6后新的写法 --> <template v-slot:test2="{phones}"> <ol> <li v-for="(p,i) in phones" :key="i">{{p}}</li> </ol> </template> </List> </div> </template> //子组件 <template> <div class="list"> <h1>{{title}} 列表</h1> <slot name="test2" :phones="phones">没有内容222</slot> </div> </template> <script> export default { name:'List', data(){ return { phones:['小米','华为'] } }, props:['title'] } </script> <style scoped> .list{ background-color: skyblue; width: 200px; height: 300px; } h1{ text-align: center; } li{ background-color: orange; font-size: 18px; margin-bottom: 8px; } </style>
-
(五)Vue使用Ajax
一、axios:功能强大的网络请求库
安装 :npm npm上axios地址,网上找都行,到处都是
1.axios基本使用
<div id="app">
<button @click="get">get</button>
<br>
<button @click="post">post</button>
</div>
var app=new Vue({
el:'#app',
methods:{
get:function(){
axios.get("http://127.0.0.1:3000/get?num=4") //自己写的测试接口,下同
.then(
function(res){
console.log(res);
},function(err){
console.log(err);
}
)
},
post:function(){
axios.post("http://127.0.0.1:3000/post",{num:5})
.then(
function(res){
console.log(res);
},function(err){
console.log(err);
}
)
}
}
})
二、Vue脚手架配置服务器代理
-
方法一
// 开启代理服务器(方式一) devServer: { proxy: 'http://localhost:5000' },
students(){ axios.get("http://localhost:8081/students").then( (res)=>{ console.log(res.data); }, (err)=>{ console.log(err.massage); } ) }
- 优点:配置简单,请求资源时直接发送给本机服务器(8081)即可。
- 缺点:不能配置多个代理,不能灵活选择是否让代理转发请求
- 工作方式:当请求时,优先访问本机服务器,如果本机服务器没有资源,才会转发请求到对应服务器
-
方法二
// 开启代理服务器(方式二) devServer: { proxy: { '/s': { //当请求前缀为/s时代理服务器转发请求到target target: 'http://localhost:5000', pathRewrite:{'^/s':''}, //代理服务器转发请求时,将前缀/s去掉 ws: true, //用于支持websocket changeOrigin: true //用于控制请求头中的Host值(true: localhost:5000 false :localhost:8081) }, '/c': { //当请求前缀为/s时代理服务器转发请求到target target: 'http://localhost:5001', pathRewrite:{'^/c':''}, //代理服务器转发请求时,将前缀/s去掉 ws: true, //用于支持websocket changeOrigin: true //用于控制请求头中的Host值(true: localhost:5000 false :localhost:8081) } } }
students(){ axios.get("http://localhost:8081/s/students").then( (res)=>{ console.log(res.data); }, (err)=>{ console.log(err.massage); } ) }, cars(){ axios.get("http://localhost:8081/c/cars").then( (res)=>{ console.log(res.data); }, (err)=>{ console.log(err.massage); } ) }
- 优点:可以配置多个代理,且灵活控制
- 配置繁琐,请求时需要加前缀
三、vue-resource插件(了解)
- 安装
npm i vue-resource
- 使用
- 引入
import vueResource from 'vue-resource'
- 使用
Vue.use(vueResource);
- 使用
this.$http.get(...)
- 引入
(六)Vuex(重点)
一、Vuex简介
-
概念:专门在Vue中实现集中式状态(数据)管理的一个Vue插件,对vue应用中多个组件的共享状态进行集中式的管理(读写),也是组件间通信方式之一,适用于任意组件间通信
-
Github地址:vuex
-
原理:
二、Vuex搭建
-
安装:
npm i vuex
-
创建文件:src/store/index.js
// 引入Vue import Vue from 'vue' // 引入Vuex import Vuex from 'vuex' // 应用Vuex插件 Vue.use(Vuex); // 创建并暴露store export default new Vuex.Store({ actions:{}, mutations:{}, state:{} })
-
在main.js中配置store
// 引入store import store from './store' import App from './App.vue' Vue.config.productionTip = false Vue.use(vuex); new Vue({ render: h => h(App), store }).$mount('#app')
三、基本使用
-
初始化数据state、配置actions,mutations
actions:{ //响应组件中的动作 fun(context,value){ //context:上下文,相当于一个minstore,在里面有所有这里可能需要用到的数据和方法 //context.dispatch('action中的方法名',value) 可以对数据做多步中间处理 //context.state.test可以直接访问到state中的数据,这里不要直接去修改数据,可以读取数据做条件判断等, //如果直接修改数据,vue开发者工具无法捕捉到 //context.commit('mutations中的方法名',data) 提交操作 } //业务逻辑全部写在这里 }, mutations:{ //执行操作 //这里的方法名约定全大写,方便与actions中的方法进行区分 FUN(state,value){ //state 就是state,这里就对数据进行基本操作(不关心业务逻辑,只关心基本增删改查操作) } }, state:{ //数据 test:0 }
-
组件中读取数据,发起操作
//读取数据 $store.state.test //发起操作 $store.dispatch('action中的方法名',data) //如果不需要action做中间处理,则直接提交操作 $store.commit('mutations中的方法名',data)
1. getters配置项
-
概念:当state中的数据需要经过加工后再共享使用的时候,可以用getters加工
-
用法:
getters:{ bigTest(state){ return state.test*10; } } //用法形如组件中的computed计算属性,但是getters可以在组件间复用
2.四个map方法
引入import {mapState,mapGetters,mapActions,mapMutations} from 'vuex'
2.1 mapState
-
用于帮助我们映射state中的数据为computed计算属性
computed:{ //对象写法 ...mapState({a:'test1',b:'test2',c:'test3'}) //数组写法(不能重新命名,生成的计算属性名与state对象中的属性名一致,传递的参数也得与state对象中的属性名一致) ...mapState(['test1','test2','test3']) }
2.2 mapGetters
-
用于帮助我们映射getters中的数据为computed计算属性
computed:{ //对象写法 ...mapGetters({a:'test1',b:'test2',c:'test3'}) //数组写法(不能重新命名,生成的计算属性名与Getters对象中的属性名一致,传递的参数也得与Getters对象中的属性名一致) ...mapGetters(['test1','test2','test3']) }
2.3 mapActions
-
用于帮助我们生成与actions对话的方法
methods:{ //对象写法 ...mapActions({a:'test1',b:'test2',c:'test3'}) //生成的函数 //a(value){ // $store.dispatch('test1',value); //} //数组写法(不能重新命名,生成的函数名与actions对象中的方法名一致,传递的参数也得与actions对象中的方法名一致) ...mapActions(['test1','test2','test3']) }
2.4 mapMutations
-
用于帮助我们生成与mutations对话的方法
methods:{ //对象写法 ...mapMutations({a:'TEST1',b:'TEST2'}) //生成的函数 //a(value){ // $store.commit('TEST1',value); //} //数组写法(不能重新命名,生成的函数名与mutations对象中的方法名一致,传递的参数也得与mutations对象中的方法名一致) ...mapMutations(['TEST1','TEST2']) }
3.模块化+命名空间
-
目的:让代码更好的维护,让多种数据分类更加明确
-
使用方法
-
创建并暴露模块
//在model1.js中 export default model1={ //开启命名空间 namespaced:true, state:{}, actions:{}, mutations:{}, getters:{} }
//在model2.js中 export default model2={ //开启命名空间 namespaced:true, state:{}, actions:{}, mutations:{}, getters:{} }
-
在store模块中引入并使用其他模块
//在index.js中 // 引入Vue import Vue from 'vue' // 引入Vuex import Vuex from 'vuex' //引入其他模块 import model1 from './model1' import model2 from './model2' // 应用Vuex插件 Vue.use(Vuex); // 创建并暴露store export default new Vuex.Store({ modules:{ model1, model2 } })
-
读取state中数据的方法(在读取时要以某一种形式指明数据所在的模块)
//直接读取 this.$store.state.model1.test1 //借助mapState读取 ...mapState('model1',['test1','test2'])
-
读取getters中数据的方法
//直接读取 this.$store.getters['model2/test1'] //借助mapGetters ...mapGetters('model2',['test1','test2'])
-
调用dispatch与Actions对话的方法
//直接对话 this.$store.dispatch('model1/test1',data) //借助mapActions ...mapActions('model1',['test1','test2'])
-
调用commit与Mutations对话的方法
//直接对话 this.$store.commit('model2/test1',data) //借助mapMutations ...mapMutations('model2',['TEST1','TEST2'])
-
(七)路由 vue-router(重点)
一、vue-router简介
vue-router是vue的一个插件库,专门用来实现SPA(单页面)应用
1.SPA简介
- 单页面Web应用(Single page web application,SPA)
- 整个应用只有一个完整的页面
- 点击页面中的导航链接不会刷新页面,只会做页面的局部更新
- 数据通过Ajax请求获取
2.路由
- 什么是路由
- 一个路由就是一组映射关系(key-value)
- key是路径,value可能是function或component
- 路由分类
- 后端路由
- 理解:value是function,用于处理客户端提交的请求
- 工作过程:服务器收到一个请求时,根据请求路径找到匹配的函数来处理请求,返回响应数据
- 前端路由
- 理解:value是component,用于展示页面内容
- 工作过程:当浏览器的路径改变的时候,对应的组件就会显示
- 后端路由
二、基本使用
-
安装vue-router
npm i vue-router
-
引入
import VueRouter from 'vue-router'
-
应用插件
Vue.use(VueRouter);
-
创建并暴露一个router
//在src/router/index.js下 //引入vue-router import VueRouter from 'vue-router' //引入相关组件 import Component1 from '../component/Component1' import Component2 from '../component/Component2' //创建并暴露router export default router= new VueRouter({ routes:[ { path:'/component1', component:Component1 }, { path:'/component2', component:Component2 } ] })
-
实现切换
<router-link active-class="active" to="/component1">Component1</router-link> <!-- active-class指定被选中时的样式,to指定跳转路径 --> <!-- router-link 最终呈现出来是一个a标签 -->
-
指定显示位置
<router-view></router-view>
-
注意!
- 在开发中,路由组件通常放在src/pages下,一般的组件通常放在src/components下
- 切换路径进而切换显示的组件时,默认是在销毁、挂载组件
- 每个组件都有自己的$route属性,里面存储着自己的路由信息
- 整个应用只有一个router,通过每个路由组件的$router属性访问
三、多级路由
-
配置路由规则,在一级路由里使用children进行配置
export default router= new VueRouter({ routes:[ { path:'/component1', component:Component1, //配置子级路由,可以无限套娃 children:[ { path:'child1', //children中的路径不需要写/ component:Child1 }, { path:'child2', component:Child2 } ] } ] })
-
跳转(to中的路径需要写完整)
<router-link to="/component1/child1">Child1</router-link>
四、路由传参
1. query参数
-
传递参数
<!-- 字符串写法 --> <router-link :to="`/component1/child1/test1?a=${data.a}&b=${data.b}`">Test1</router-link> <!-- 对象写法 --> <router-link :to="{ path:'/component1/child1/test1', query:{ a:data.a, b:data.b } }" >Test1</router-link>
-
接收参数
this.$route.query.a
2.路由命名(name配置项)
-
使用
export default router= new VueRouter({ routes:[ ... { name:'c1', //配置名字 path:'xxxx', component:xxxxx }, ] })
-
简化跳转
<!-- 主要是在子级路由跳转时,即路径较长,或者需要传递参数,使用对象写法 --> <router-link :to="{ name:'c1', query:{ ... } }" >Test1</router-link>
3.params参数
-
在配置中声明params参数
name:'c1' path:'xxxx/:a/:b' //使用占位符声明params参数
-
传递参数
<!-- 字符串写法 --> <router-link :to="`/xxxx/${data.a}/${data.b}`">Test1</router-link> <!-- 对象写法 --> <router-link :to="{ name:'c1', //这里必须用name,不能用path!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! query:{ a:data.a, b:data.b } }" >Test1</router-link>
-
读取参数
this.$route.params.a
4.路由的props配置
作用,让路由组件更方便接收到参数
{
...
//第一种写法:props值为对象,该对象中所有的key-value的组合最终会通过props传给对应组件
//props:{a:100,b:'hello'}
//这种写法不能动态赋值
//第二种写法:props值为true,路由收到的所有**params**参数通过props传给对应组件
//props:true
//这种方法有局限性,只能处理params参数
//第三种写法:props值为函数,函数返回一个对象,函数的第一个参数是$route,对象中所有的key-value的组合最终会通过props传给对应组件
props($route){
return {
a:$router.query.xxx
b:$router.params.xxx
}
}
}
五、<router-link>
的replace属性
- 作用:控制路由跳转时操作浏览器历史记录的模式
- 浏览器的历史记录有两种写入模式:分别是
push
和replace
,push是追加记录(压栈),replace是替换,默认是push - 开启replace模式:
<router-link replace>.....</router-link>
六、编程式路由导航
-
作用:不需要再借助
<router-link>
,路由跳转更加灵活 -
具体方法
//$router的两个API this.$router.push({ //push模式跳转 name:'xxx', query:{ ... } }) //配置与to的对象写法一样 this.$router.replace({ //replace模式跳转 name:'xxx', params:{ ... } }) //历史记录操作方法,$router的三个API this.$router.back() //后退一步 this.$router.forward() //前进一步 this.$router.go() //传递参数,正数前进,负数后退具体的步数
七、缓存路由组件
-
作用:让不展示的路由组件保持挂载,不被销毁
-
使用
<keep-alive include="xxx"> <router-view></router-view> </keep-alive> <!-- keep-alive标签中的内容将会被缓存 include是一个配置项,里面值为**组件名**,其中包含的组件才会被缓存,如果不配置include,则keep-alive中的组件都会被缓存 include中需要包含多个组件时 :include="['xxx1','xxx2']" -->
八、activated与deactivated生命周期钩子
- 作用:路由组件独有,用于捕获路由组件的激活状态
activated
路由组件被激活时触发deactivated
路由组件失活时触发
九、路由守卫
1.全局守卫
//在暴露router之前,使用router相应API设置
//全局前置守卫:初始化时执行、每次路由切换前执行(控制访问权限等)
router.beforeEach((to,from,next)=>{
//to是即将跳转的路由的相关信息
//from是跳转前的路由相关信息
//next是一个函数,调用该函数即跳转路由
})
//全局后置守卫:初始化时执行、每次路由切换后执行(控制页签名称document.title变化等)
router.afterEach((to,from)=>{
//to是即将跳转的路由的相关信息
//from是跳转前的路由相关信息
})
//补充:路由配置中提供程序员存放其他信息(标识信息等)的配置项meta
2.独享守卫
- 某一个路由特有的
//配置在路由配置中
routes:[
...
{
...
beforeEnter(to,from,next){
...
//这里的三个参数与全局前置守卫一致
}
}
]
3.组件内守卫
-
组件独享的守卫
//在组件配置中 //进入守卫:通过路由规则,进入该组件时被调用 beforeRouteEnter(to,from,next){} //离开守卫:通过路由规则,离开该组件时被调用 beforeRouteLeave(to,from,next){} //这里的三个参数与全局前置守卫一致
十、路由器工作模式
- 对与一个路径来说,hash值就是#及其后面的内容
- hash值不会包含在HTTP请求中,即has值不会带给服务器
- hash模式
- 优点
- 兼容性较好
- 缺点
- 如果以后将地址通过第三方手机app分享,如果app校验严格,则地址会标记为不合法
- 地址中带着/#/,不美观
- 优点
- history模式
- 优点
- 地址干净美观
- 缺点
- 兼容性略差
- 应用部署上线时需要后端人员支持,解决刷新页面服务器404问题(本质上是因为刷新时浏览器将路径全部发送给了服务器,请求服务器导致)
- 优点
- 补充:Nodejs解决刷新页面服务器404问题方法(借助中间件)
- 比如:
connect-history-api-fallback
npm上的connect-history-api-fallback- 安装:
npm i connect-history-api-fallback
- 引入:
const history = require('connect-history-api-fallback');
- 使用:
app.use(history());
(注意要在配置静态资源前使用)
- 安装:
- 比如: