by aoziao
Email:youxi0928@qq.com
一、邂逅Vuejs
1.遇见Vuejs
Vuejs的特点Vue不支持 IE8及以下版本,因为 Vue 使用了 IE8 无法模拟的 ECMAScript 5 特性。但它支持所有兼容 ECMAScript 5 的浏览器。
Vue (读音 /vjuː/,类似于 view) 是一套用于构建用户界面的渐进式框架。与其它大型框架不同的是,Vue 被设计为可以自底向上逐层应用。Vue 的核心库只关注视图层,不仅易于上手,还便于与第三方库或既有项目整合。另一方面,当与现代化的工具链以及各种支持类库结合使用时,Vue 也完全能够为复杂的单页应用提供驱动。
Vue有很多他点和web开发中的常见的高级功能
Exp:解耦视图和数据、可复用组件、前端路由技术、状态管理、虚拟DOM
2.安装Vuejs
2.1 安装
Vue有三种方式安装,使用开发环境版本,
- 第一:直接在CDN引入
- 第二:下载引入
- 第三:NPM安装
2.2 NPM安装
npm install vue
3.体验Vuejs
3.1 Hello Vuejs
<div class="div">{{message}}</div>
<script src="./js/vue.js"></script>
<script>
const app = new Vue({
el: '.div',
data: {
message: 'Hello Vue!'
}
});
</script>
3.2 Vue显示列表
<div id="app">
<h1>{{message}}</h1>
<ul><li v-for="item in movies">{{item}}</li></ul>
</div>
<script>
const obj = {
message: "nihao",
movies: ["星际穿越", "奥兹奥", "夏洛特烦恼", "速度与激情"]
};
var vm = new Vue({
el: "#app",
data: obj
});
</script>
3.3实现计时器
<div id="app">
<h2>当前计数:{{counter}}</h2>
<!-- <button v-on:click="counter++">+</button>
<button v-on:click="counter--">-</button> -->
<button @click="add">+</button>
<button @click="sub">-</button>
</div>
<script>
//创建Vue实例,得到 ViewModel
const app = new Vue({
el: "#app",
data: {
counter: 0
},
methods: {
add: function() {
console.log("add被执行");
this.counter++;
},
sub: function() {
console.log("sub被执行"); //console首字母不可以大写
this.counter--;
}
}
});
</script>
4.MVVM架构
4.1 Vue中的MVVM
Model层:数据层,数据可能是固定死的数据,更多的来自于服务器,从网络上请求下来的数据。
View层:视图层,也叫Dom层,主要给用户展示各种信息
VueModel层:视图模型层,视图模型层是view和model沟通的桥梁,一方面实现数据绑定,将model的改变实时反映到view中,另一方面实现了DOM Listener也就是DOM监听,当DOM发生一些事件(点击、touch等)时,可以监听到,并在需要的情况下改变对于的data
二、基础语法
1. 插值语法
1.1 Mustache语法
<div id="app">
<h2>{{message}}</h2>
<h2>{{message}},aoziao!</h2>
<h2>{{firstname}} {{lastname}}</h2>
<h2>{{firstname + " " + lastname}}</h2>
<h2>{{counter * 2}}</h2>
</div>
<script>
//创建Vue实例,得到 ViewModel
var app = new Vue({
el: "#app",
data: {
message: "你好啊",
firstname: "kobe",
lastname: "bryant",
counter: 100
}
});
</script>
1.2 v-once
在某些情况下,我们可能不希望界面中 Mustach 中的值随意的跟随改变,就可以使用一个 Vue 的指令:v-once ,该指令后面不需要跟任何和参数
该指令表示元素和组件只渲染一次,不会随着数据的改变而改变
<h2 v-once>{{message}}</h2> <!-- 只会渲染一次 -->
1.3 v-html
某些情况下,我们从服务器请求到的数据本身就是一个 HTML 代码,如果我们直接通过 {{}} 来输出,会将 HTML 代码也一起输出,但如果希望按照 HTML 格式进行解析,并且显示对应的内容,可以使用 v-html 指令
v-html 指令后面往往会跟上一个类型(String、url等),会将类型的 html 解析出来并且进行渲染
<div id="app">
<h2>{{message}}</h2>
<h2 v-once>{{message}}</h2>
<h2 v-html="url"></h2>
</div>
<script>
//创建Vue实例,得到 ViewModel
var vm = new Vue({
el: "#app",
data: {
message: "aoziao",
url: '<a href="http://www.baidu.com">百度一下</a>'
}
});
</script>
1.4 v-text
v-text作用和Mustache比较相似,都是用域将数据显示在界面中,通常情况下会接受一个String类型,
但是v-text会覆盖原有标签中渲染的内容
1.5 v-pre
v-pre 用于跳过这个元素和它子元素的编译过程,显示原本的 Mustache 语法,将代码原封不动的解析出来
1.6 v-block
将未解析出来的代码块进行隐藏,但基本不会用到
2.绑定属性
2.1 v-bind的基本使用
Mustache 指令主要作用是将值插入到我们模板的内容当中,但是,除了内容需要动态来决定外,某些属性我们也希望动态来绑定,比如:
动态绑定a元素的href属性或者动态绑定img元素的src属性
这时,可以使用 v-bind 指令来动态绑定属性,v-bind 用于绑定一个或多个属性值,或者向另一个组件传递 props值
<div id="app">
<img :src="imgURL" alt="" />
<a href="https://image.baidu.com/search/index?tn=baiduimage&ct=201326592&lm=-1&cl=2&ie=gb18030&word=%C3%C0%C5%AE&fr=ala&ala=1&alatpl=adress&pos=0&hs=2&xthttps=111111">美女</a>
</div>
<script>
//创建Vue实例,得到 ViewModel
var vm = new Vue({
el: "#app",
data: {
messagel: "nihyaoa",
imgURL:"https://ss1.bdstatic.com/70cFuXSh_Q1YnxGkpoWK1HF6hhy/it/u=1480623082,1028728801&fm=26&gp=0.jpg"
},
methods: {}
});
</script>
2.2 v-bind语法糖
v-bind 有一个对应的语法糖(简写方式),在开发中,通常会使用语法糖的形式,因为这样更加简洁
<img :src="imgURL" alt="" />
<img v-bind: src="imgURL" alt="" />
//二者意义相同
2.3 动态绑定class
动态绑定class有两种方法:对象语法、数组语法
对象语法:
对象语法的含义是class后面跟的是一个对象
对象语法有下面这些用法:
用法一:直接通过{}绑定一个类
<h2 :class="{'active': isActive}">Hello World</h2>
用法二:也可以通过判断,传入多个值
<h2 :class="{'active': isActive, 'line': isLine}">Hello World</h2>
用法三:和普通的类同时存在,并不冲突
注:如果isActive和isLine都为true,那么会有title/active/line三个类
<h2 class="title" :class="{'active': isActive, 'line': isLine}">Hello World</h2>
用法四:如果过于复杂,可以放在一个methods或者computed中
注:classes是一个计算属性
<h2 class="title" :class="classes">Hello World</h2>
点击按钮,修改文字颜色:
<head>
<style>
.active {color: red;}
.a {font-size: 50px;}
</style>
</head>
<body>
<div id="app">
<h2 class="a" v-bind:class="{active: isActive,line: isLine}">
{{message}}
</h2>
<button @click="btnClick">按钮</button>
</div>
<script src="../node_modules/vue/dist/vue.js"></script>
<script>
//创建Vue实例,得到 ViewModel
var vm = new Vue({
el: "#app",
data: {
message: "aoziao",
isActive: true,
isLine: true
},
methods: {
btnClick: function() {
this.isActive = !this.isActive;
},
}
});
</script>
通过点击按钮,触发btnClick事件,btnClick方法取反动态修改isActive的值,实现动态添加或删除class属性,此时class属性中为字体颜色样式,这样通过动态绑定class实现了对字体颜色的动态改变。
如果js<h2 class="a" v-bind:class="{active: isActive,line: isLine}">中的绑定class内容过多,可单独抽取出来放入methods成为一个方法。
数组语法:
数组语法的含义是class后面跟的是一个数组 数组语法有下面这些用法: 用法一:直接通过{}绑定一个类
<h2 :class="['active']">Hello World</h2>
用法二:也可以传入多个值
<h2 :class=“[‘active’, 'line']">Hello World</h2>
用法三:和普通的类同时存在,并不冲突 注:会有title/active/line三个类
<h2 class="title" :class=“[‘active’, 'line']">Hello World</h2>
用法四:如果过于复杂,可以放在一个methods或者computed中 注:classes是一个计算属性
<h2 class="title" :class="classes">Hello World</h2>
<h2 class="title" :class="classes">Hello World</h2>
点击指定项改变颜色:
<head>
<style>
.active {color: red;}
</style>
</head>
<body>
<div id="app">
<ul>
<li v-for="(item, index) in movies" v-on:click="changeColor(index)">
<div v-bind:class="{active: currentIndex == index}">
{{index}}-{{item}}
</div>
</li>
</ul>
</div>
<script src="../node_modules/vue/dist/vue.js"></script>
<script>
const app = new Vue({
el: "#app",
data: {
message: "奥兹奥",
movies: ["海王", "海尔兄弟", "火影忍者", "进击的巨人"],
currentIndex: Number.MIN_VALUE
},
methods: {
changeColor: function(index) {
this.currentIndex = index;
}
}
});
</script>
通过点击文字促发btnClick事件,同时传入文字的index,将传入的index赋值给currentIndex,通过判断传入的currentIndex是否就是点击currentIndex,如果是则为当前的div标签添加.active样式,通过.active的样式来改变点击文字的颜色。
2.4 动态绑定样式
我们可以利用 v-bind:style 来绑定一些 CSS 内联样式,同样的,动态绑定样式也分为动态绑定数组和动态绑定对象两种语法。
<body>
<div id="app">
<!-- 数组方法 -->
<h2 :style="{fontSize:finalSize + 'px',background:finalColor}">{{message}}</h2>
<!-- 对象方法 -->
<h2 :style="finalStyle()">{{message}}</h2>
</div>
<script>
var vm = new Vue({
el: "#app",
data: {
message: "奥兹奥",
finalColor: "red",
finalSize: 100
},
methods: {
finalStyle: function() {
return {
fontSize: this.finalSize + "px",
background: this.finalColor
};
}
}
});
</script>
对象语法:style后面跟的是一个对象类型,(key:value)对象的key值是css属性名称,value是具体的值 数组语法:style 后面跟的是一个数组类型,语法与上相同。
3.计算属性
3.1 computed的基本使用
什么时候使用计算属性:
在模板中可以直接通过插值语法显示一些 data 中的数据,但是在某些情况下,可能需要对数据进行一些转化后再显示,或者需要将多个数据结合起来进行显示,这时可以使用计算属性 computed。
<div id="app">
<h2>{{firstName}} {{lastName}}</h2>
<h2>{{firstName +" "+lastName}}</h2>
<h2>{{getFullName()}}</h2>
<h2>{{fullName}}</h2>
</div>
<script>
const app = new Vue({
el: "#app",
data: {
firstName: "aoziao",
lastName: "Vuejs"
},
computed: {
fullName: function() {
return this.firstName + " " + this.lastName;
}
},
methods: {
getFullName: function() {
return this.firstName + " " + this.lastName;
}
}
});
</script>
上述四种方法的结果一致
计算属性可以进行比较复杂的操作,如价格计算等
计算属性中也可以进行一些更加复杂的操作,比如下面计算图书价格的例子:
<div class="app">
<h2>图书总价为:{{totalPrice}}</h2>
</div>
<script src="../js/vue.js"></script>
<script>
const app = new Vue({
el: '.app',
data: {
books: [
{id: 1001,name: '计算机操作原理',price: 108},
{id: 1002,name: 'JavaScript高级程序设计',price: 99},
{id: 1003,name: '计算机网络',price: 28},
{id: 1004,name: '数据结构',price: 46},
{id: 1005,name: 'C语言',price: 48},
]
},
computed: {
totalPrice: function() {
let result = 0
for (let i in this.books) {
result += this.books[i].price
}
return result
}
},
})
</script>
通过遍历数组对象的price值,进行累加操作计算出五本书的总价格。
3.2 计算属性的setter和getter
每个计算属性都包含一个getter和一个setter,getter用来读取值,setter用来设置值(但setter不常用)
<body>
<div id="app">{{fullName}}</div>
<script>
const app = new Vue({
el: "#app",
data: {
firstName: "Kobe",
lastName: "Bryant"
},
computed: {
fullName: {
set: function(newValue) {
const names = newValue.split(" ");
this.firstName = names[0];
this.lastName = names[1];
},
get: function() {
return this.firstName + " " + this.lastName;
}
}
}
});
</script>
//通过浏览器调试模式中的console来修改值是调用computed的set函数
fullName: {
get: function() {
return this.firstName + " " + this.lastName;
}
}
直接简写为
fullName: function() {
return this.firstName + " " + this.lastName;
}
3.3 setter和getter的区别
问题:methods 和 computed 看起来都可以实现我们的功能,那么为什么还要多一个计算属性这个东西呢?
原因:计算属性会进行缓存,如果多次使用时,计算属性只会调用一次
computed 区别于 methods 的核心
在官方文档中,强调了computed 区别于 methods 最重要的两点
1、computed 是属性调用,而 methods 是函数调用computed 带有缓存功能,而 methods 没有。computed 定义的方法,我们是以属性访问的形式调用的,{{computedTest}},但是 methods 定义的方法,我们必须要加上()来调用,如{{methodTest()}},我们可以将同一函数定义为一个方法而不是一个计算属性,两种方式的最终结果确实是完全相同的,然而,不同的是计算属性是基于它们的响应式依赖进行缓存的,只在相关响应式依赖发生改变时它们才会重新求值,这就意味着只要 text 还没有发生改变,多次访问 getText 计算属性会立即返回之前的计算结果,而不必再次执行函数,而方法只要页面中的属性发生改变就会重新执行。对于任何复杂逻辑,都应当使用计算属性
2、computed 依赖于 data 中的数据,只有在它的相关依赖数据发生改变时才会重新求值
4.事件监听
4.1 v-on介绍
在前端开发中,我们需要经常和用户进行交互,这个时候,就必须监听用户发生的事件,比如点击、拖拽、键盘事件等,在 Vue 中监听事件使用 v-on 指令
4.2 v-on基础
<button v-on:click="counter--">-</button>
<button @click="decrement">-</button>
4.3 v-on参数
<body>
<div id="app">
<!-- 1.事件调用的方法没有参数 -->
<button @click="btn1Click()">按钮1</button>
<button @click="btn1Click">按钮2</button>
<!--2. 在事件定义时,写函数时省略了小括号,本身是需要一个参数的,这个时候,
vue会默认将浏览器产生的event事件对象作为参数传入方法 -->
<!-- 如果函数需要参数,但是没有传入,那么函数的形参为undefined -->
<button @click="btn2Click()">按钮3</button@click="btnClick()">
<button @click="btn2Click(123)">按钮4</button@click="btnClick()">
<!--去掉括号时,则监听的是时间:mouseEvent,不去括号为空,则函数寻找变量,报undefined -->
<button @click="btn3Click">按钮5</button@click="btnClick()">
<button @click="btn3Click()">按钮6</button@click="btnClick()">
<!-- 3.方法定义时,需要event对象,又需要其他参数 -->
<!-- 在调用方式,如何手动的获取到浏览器的event对象:$event -->
<button @click="btn4Click(123,$event)">按钮7</button@click="btnClick()">
</div>
<script>
//创建Vue实例,得到 ViewModel
const app = new Vue({
el: "#app",
data: {},
methods: {
btn1Click() {console.log("btn1Clock");},
btn2Click(abc){console.log("--------",abc);},
btn3Click(event){console.log("++++++++++",event); },
btn4Click(abc,event){console.log("***********",abc,event);}
}
});
</script>
4.4 v-on修饰符
在某些情况下,我们拿到 event 的目的可能是进行一些事件处理,vue 提供了修饰符来帮助我们方便的处理一些事件:
.stop :调用 event.stopPropagation()
.prevent :调用 event.preventDefault()
.{keyCode | keyAlias} :只当事件是从特定键触发时才触发回调
.native :监听组件根元素的原生事件(重中之重)
.once :只触发一次回调
<body>
<div id="app">
<!-- .stop修饰符 :阻止冒泡 -->
<div @click="divClick">
aaaaa
<button @click.stop="btnClick">按钮</button>
</div
<!-- .prevent修饰符:阻止默认行为 -->
<form action="baidu">
<input type="submit" value="提交" @click.prevent="submitClick" />
</form>
<!-- 键修饰符.键别名:监听某个键帽-->
<!--监听所有按键 -->
<input type="text" @keyup="keyUp" />
<!-- 监听enter键 -->
<input type="text" @keyup.13="keyEnterUp" />
<!-- .once :点击回调值调用一次 -->
<button @click.once="btn2Click">按钮2</button>
</div>
<script>
const app = new Vue({
el: "#app",
data: {},
methods: {
divClick() {console.log("divClick");},
btnClick() {console.log("btnClick");},
submitClick() {console.log("submitClick");},
keyUp() {console.log("keyUp");},
keyEnterUp() {console.log("keyEnterUp");},
btn2Click() {console.log("btn2Click");}
}
});
5.条件与循环
5.1 条件渲染
通过v-if、v-else-if、v-else 这三个指令添加条件指令可以实现对 DOM 中渲染或销毁元素或组件。
v-if后面的条件为 false 时,对应的元素以及其子元素不会渲染,也就是不会有对应的标签出现在DOM 中
<div id="app">
<h2 v-if="isShow">
<div>abc</div>
</h2>
<h2>{{messsage}}</h2>
</div>
<script>
const app = new Vue({
el: "#app",
data: {
messsage: "aoziao",
isShow: true
},
});
</script>
5.2 v-show指令
v-show 的用法和 v-if 非常相似,也用于决定一个元素是否渲染
5.3 v-if和v-show的对比
1、v-if 是真正的条件渲染,会确保在切换过程中,条件块内的事件和子组件被销毁和重建(组件被重建将会调用created)
2、v-show 不论如何,都会被渲染在 DOM 中,当条件为真值时,将会修改条件的 css 样式
3、v-if 有更高的切换开销,v-show有更高的初始渲染开销,v-if 适合运营条件不大可能改变,v-show适合频繁切换
4、v-if 是动态的向DOM树内添加或者删除DOM元素,v-show是通过设置DOM元素的display样式属性控制显隐
5、v-if 切换有一个局部编译/卸载的过程,切换过程中合适地销毁和重建内部的事件监听和子组件,v-show 只是简单的基于 css 切换
6、v-if 是惰性的,如果初始条件为假,则什么也不做,只有在条件第一次变为真时才开始局部编译,v-show 是在任何条件下(首次条件是否为真)都被编译,然后被缓存,而且 DOM 元素保留
v-if 和 v-show 都可以决定一个元素是否渲染,那么开发中我们如何选择呢?
v-if:当条件为False时,包含v-if指令的元素,根本就不会存在DOM中(从DOM中删除)
v-show:当条件为False时,v-show只是给元素增加一个行内样式:dispaly:none;
结论:当需要在显示与隐藏之间切换很频繁时,使用 v-show ,当只有一次切换时,通过使用 v-if
5.4 v-for指令
当我们有一组数据需要进行渲染时,我们就可以使用 v-for 来完成
v-for 遍历数组:
无索引值:vue <li v-for="item in names">{{item}}</li>
使用索引值:vue <li v-for="(item,index) in names">{{index+1}}-----{{item}}</li>
v-for 遍历对象:
vue <li v-for="(value,key,index) in info">{{key}}-----{{value}}-----{{index+1}}</li>
假设对象为 vue info:{name: “aoziao” ,age:18 } 在遍历获取对象中,获取key和value顺序依次为 value,key,index,
最后才是索引值。
遍历过程中获取key 值:
vue <li v-for="item in letter" :key="item">{{item}}</li>
具有响应式的数组方法
//push():在数组末尾追加元素(可添加多个,与shift()一样)
this.letter.push("aaaa");
//
pop():删除数组中的最后一个元素
this.letter.pop();
//shift():删除数组中的都一个元素
this.letter.shift();
//unshift():在数组最前面添加元素
this.letter.unshift("aaaa", "bbbb", "cccc");
//splice():删除元素/插入元素/替换元素
//删除元素:第一个元素传入从第几个元素开始,第二个元素传入要
//删除几个元素(如果没有传,则默认删除后面所有元素)
this.letter.splice(2, 2);
// 替换元素:第一个元素传入从第几个元素开始,第二个元素表示要替换几个元素,后面是用域替换前面的元素
this.letter.splice(2, 2, "c", "d");
//插入元素:第一个元素传入从第几个元素开始,第二个元素传入0,后面跟上要插入的元素
this.letter.splice(1, 0, "b", "c", "d", "e");
//sort():排序 注意:按照字符编码的顺序进行排序
this.letter1.sort();
//reverse():数组反转
this.letter.reverse();
//注:修改数组中的元素时:
//不采用:this.letter[0]="bbbbb";
//建议采用:this.letter.splice(0,1,"bbbb");
//set(要修改的对象,索引组,修改后的值)
set(this.letter,0,"bbbbb")
5.5 用户登录案例
<!-- 问题:从input元素切入另外一个input元素中去,输入的内容会随之切换带入另外一个input元素
解决方案:给input添加key且保证key值不同 -->
<body>
<div id="app">
<span v-if="isUser">
<label for="username">用户账户</label>
<input type="text" id="username" placeholder="********" key="1" />
</span>
<span v-else>
<label for="userEmail">用户邮箱</label>
<input type="text" id="userEmail" placeholder=<********@qq.com> key="2" />
</span>
<button @click="isUser = !isUser">切换登陆方式</button>
</div>
<script>
const app = new Vue({
el: "#app",
data: {
isUser: true
},
methods: {}
});
5.5 用户登录案例
Index.html:
<body>
<div id="app">
<div v-if="books.length">
<table>
<thead>
<th> </th>
<th>书籍名称</th>
<th>出版日期</th>
<th>价格</th>
<th>购买数量</th>
<th>操作</th>
</thead>
<tbody>
<tr v-for="(book, index) in books" :key="index">
<td>{{index}}</td>
<td>{{book.name}}</td>
<td>{{book.beginDate}}</td>
<td>{{book.price | showPrice}}</td>
<td>
<button @click="decrement(index)" :disabled="book.count<=1">-</button>
{{book.count}}
<button @click="increment(index)">+</button>
</td>
<td><button @click="remove">移除</button></td>
</tr>
</tbody>
</table>
<h3>总价:{{totalPrice | showPrice}}</h3>
</div>
<div v-else>
<h2>购物车为空</h2>
</div>
</div>
</body>
mian.js:
const app = new Vue({
el: "#app",
data: {
books: [
{ name: "《算法导论》",beginDate: "2006-9",price: 85.0,count: 1},
{ name: "《UNIX编程艺术》",beginDate: "2006-2",price: 59.0,count: 1},
{ name: "《编程大全》",beginDate: "2008-10",price: 39.0,count: 1},
{ name: "《代码大全》",beginDate: "2006-3",price: 128.0,count: 1}
]
},
computed: {
totalPrice() {
let total = 0;
//1.普通for循环
// for (let i = 0; i < this.books.length; i++) {
// total = total + this.books[i].price * this.books[i].count
// }
// 2.增强for循环
// for (let i in this.books) {
// total = total + this.books[i].price * this.books[i].count
// }
// 3.for of
// for (const book of this.books) {
// total = total + book.price * book.count
// }
// return total
methods: {
increment(index) { this.books[index].count++;},
decrement(index) { this.books[index].count--;},
remove(index) { this.books.splice(index, 1);}
},
filters: {
//过滤器
showPrice(price) {
return "¥" + price.toFixed(2);
}
}
});
1.filter过滤函数
const nums = [2, 3, 5, 1, 77, 55, 100, 200];
//要求获取nums中大于50的数
//回调函数会遍历nums中每一个数,传入回调函数,在回调函数中写判断逻辑,返回true则会被数组接收,false会被拒绝
let newNums = nums.filter(function(num) {
if (num > 50) {
return true;
}
return false;
});
//可以使用箭头函数简写
// let newNums = nums.filter(num => num >50)
console.log(newNums);
// 2.map高阶函数
// 要求将已经过滤的新数组每项乘以2
//map函数同样会遍历数组每一项,传入回调函数为参数,num是map遍历的每一项,回调函数function返回值会被添加到新数组中
let newNums2 = newNums.map(function(num) {
return num * 2;
});
//简写
// let newNums2 = newNums.map(num => num * 2)
console.log(newNums2);
// 3.reduce高阶函数
//要求将newNums2的数组所有数累加
//reduce函数同样会遍历数组每一项,传入回调函数和‘0’为参数,0表示回调函数中preValue初始值为0,回调函数中参数preValue是每一次回调函数function返回的值,currentValue是当前值
//例如数组为[154, 110, 200, 400],则回调函数第一次返回值为0+154=154,第二次preValue为154,返回值为154+110=264,以此类推直到遍历完成
let newNum = newNums2.reduce(function(preValue, currentValue) {
return preValue + currentValue;
}, 0);
//简写
// let newNum = newNums2.reduce((preValue,currentValue) => preValue + currentValue)
console.log(newNum);
//三个需求综合
let n = nums
.filter(num => num > 50)
.map(num => num * 2)
.reduce((preValue, currentValue) => preValue + currentValue);
console.log(n);
style.css
table {
border: 1px;
border-collapse: collapse;
border-spacing: 0;
}
th,
td {
padding: 8px 16px;
border: ipx solid #e9e9e9;
text-align: left;
}
th {
background-color: #f7f7f7;
color: #5c6b77;
font-weight: 600;
}
6.表单绑定
6.1 基本使用
表单控件在实际开发中是非常常见的,特别是对于用户信息的提交,需要大量的表单,vue 中使用 v-model 指令来实现表单元素和数据的双向绑定
<div id="app"><input type="text" v-model="message" />{{message}}
const app = new Vue({
el: "#app",
data: {
message: "你好啊"
}
});
当我们在输入框输入内容时,因为 input 中的 v-model 绑定了 message,所以会实时将输入的内容传递给 message,message 发生改变,当 message 发生改变时,因为使用了 Mustache 语法,所以将 message 的值插入到 DOM 中,所以 DOM 会发生响应的改变,所以通过 v-model 实现了双向绑定
6.2 v-model基本原理
v-model 其实是一个语法糖,它的背后本质上是包含两个操作:
v-bind 绑定一个 value 属性 + v-on 指令给当前元素绑定 input 事件
即:
<input type="text" v-model="message">
<input type="text" :value="message" @input="message = $event.target.value">
//二者相同
6.3 其他类型
v-model 结合radio 类型
<div id="app">
<label for="male">
<input type="radio" id="male" value="男" v-model="sex" />男
</label>
<label for="male">
<input type="radio" id="female" value="女" v-model="sex" />女
</label>
<h2>您选择的性别是:{{sex}}</h2>
</div>
<script>
const app = new Vue({
el: "#app",
data: {
message: "奥兹奥",
sex: "男"
},
methods: {}
});
</script>
默认为男,点击那个单选项则改为单选项的value
v-model 结合checkbox 类型
<body>
<div id="app">
<label v-for="item in originHobbies" :for="item">
<input type="checkbox" :value="item" v-model="hobbies" :id="item"/>
{{item}}
</label>
<h2>您选择的爱好是:{{hobbies}}</h2>
</div>
<script>
const app = new Vue({
el: "#app",
data: {
hobbies: [],
originHobbies: ["足球","篮球","乒乓球","羽毛球","台球","高尔夫球"]
},
});
</script>
默认为空,点击哪个选项则添加哪个选项
v-model 结合select 类型
<body>
<div id="app">
<select name="fruits" v-model="fruits" multiple>
<!-- multiple控制下拉框的隐藏-->
<option value="苹果">苹果</option>
<option value="香蕉">香蕉</option>
<option value="葡萄">葡萄</option>
<option value="桃子">桃子</option>
</select>
<h2>您选择的水果是:{{fruits}}</h2>
</div>
<script>
//创建Vue实例,得到 ViewModel
const app = new Vue({
el: "#app",
data: {
fruit: "香蕉",
fruits: []
},
});
</script>
Fruit默认有香蕉,点击哪个则添加哪个
6.4 修饰符
lazy 修饰符:默认情况下,v-model 是在 input 事件中同步输入框的数据,也就是说,一旦有数据发生改变,对应的 data 中的数据就会自动发生改变
lazy 修饰符可以让数据在失去焦点或者回车时才会更新
使用方法: js <input type="text" v-model.lazy="message" />
number 修饰符
number 修饰符可以让在输入框中输入的内容自动转成数字类型
使用方法: js <input type="number" v-model.number="age" />
trim 修饰符
trim 修饰符可以过滤内容左右两边的空格
使用方法:js<input type="text" v-model.trim="message" />
三、组件发开发
1.认识组件化
1.1 什么是组件化
组件化:如果我们将一个页面中所有的处理逻辑全部放在一起,处理起来就会变得非常复杂,而且不利于后续的管理以及扩展,但如果将一个页面拆分成一个个小的功能块,每个功能块完成属于自己这部分独立的功能,那么之后整个页面的管理和维护就变得非常容易了
1.2 vue组件化思想
组件化是 Vue.js 中的重要思想 ,它提供了一种抽象,让我们可以开发出一个个独立可复用的小组件来构造我们的应用
任何的应用都会被抽象成一颗组件树:
有了组件化的思想,我们在之后的开发中就要充分的利用它,尽可能的将页面拆分成一个个小的、可复用的组件,这样让我们的代码更加方便组织和管理,并且扩展性也更强
2.组件化基础
2.1注册组件
2.1.1 注册的基本步骤
组件的使用分成三个步骤:创建组件构造器 → 注册组件 → 使用组件
注意:以上代码方式创建的组件是全局组件,即可以在多个 vue 的实例下使用,要改为局部组件,需要将注册方法写到具体的某个实例中
2.1.2 全局组件和局部组件
当我们通过调用 Vue.component() 注册组件时,组件的注册是全局的 ,这意味着该组件可以在任意 Vue 实例下使用;如果我们注册的组件是挂载在某个实例中, 那么就是一个局部组件
2.1.3 父组件和子组件
组件和组件之间存在层级关系,而其中一种非常重要的关系就是父子组件的关系
2.1.4 组测组件语法糖
在上面注册组件的方式,可能会有些繁琐,Vue为了简化这个过程,提供了注册的语法糖,主要是省去了调用Vue.extend() 的步骤,直接使用一个对象来代替
语法糖注册全局组件和局部组件:
<div id="app">
<cpn1></cpn1>
<cpn2></cpn2>
</div>
<script>
Vue.component("cpn1", {
template: `
<div>
<h2>我是标题1</h2>
<p>我是内容,哈哈哈哈哈哈</p>
</div>
`
});
const app = new Vue({
el: "#app",
data: {},
methods: {},
components: {
cpn2: {
template: `
<div>
<h2>我是标题2</h2>
<p>我是内容,呵呵呵呵呵呵</p>
</div>
`
}
}
});
</script>
2.1.5 模块的分离写法
即使用 script 标签或 template 标签将模板内容从注册时的 template 中抽离出来
使用script 标签抽离:
使用template 标签抽离:(常用)
2.1.6 组件的数据存放问题
Vue 组件有自己保存数据的地方,组件对象也有一个 data 属性,只是这个 data 属性必须是一个函数,而且这个函数返回一个对象,对象内部保存着数据。
为什么 data 在组件中必须是一个函数呢?
1、首先,如果不是一个函数,Vue直接就会报错
2、其次,Vue 让每个组件对象都返回一个新的对象,因为如果是同一个对象的,组件在多次使用后会相互影响
2.2 数据传递
2.2.1 父级向子级传递 props
在组件中,使用选项 props 来声明需要从父级接收到的数据
props 的值有两种方式:
1. 字符串数组,数组中的字符串就是传递时的名称
2. 对象:可以设置传递时的类型,也可以设置默认值和类型等
2.2.2 子级向父级传递
自定义事件流程:子组件中,通 $ emit()来触发事件 → 父组件中,通过v-on来监听子组件事件
<body>
<!-- 父组件模板 -->
<div id="app">
<cpn @itemclick="cpnclick"></cpn>
</div>
<!-- 子组件模板 -->
<template id="cpn">
<div>
<button v-for="item in categories" @click="btnclick(item)">
{{item.name}}
</button>
</div>
</template>
<script>
//子组件
const cpn = {
template: "#cpn",
data() {
return {
categories: [
{ id: "aaa", name: "热门推荐" },{ id: "bbb", name: "手机数码" },
{ id: "ccc", name: "家用家电" },{ id: "ddd", name: "电脑办公" }
]
};
},
methods: {
btnclick(item) {
//发射一个事件:自定义事件
this.$emit("itemclick", item);
}
}
};
const app = new Vue({
el: "#app",
data: {},
methods: {
cpnclick(item) {
console.log("cpnclick", item);
}
},
components: {
cpn
}
});
</script>
</body>
点击界面的buttom按钮触发子组件的btnClick事件同时传回对应的item值,btnClick事件携带item通过this. $ emit(‘item-click’,item)发送item-click事件,并且同时携带item,父组件通过@item-click接受事件同时触发cpnClick事件,携带的item值传给cpnClick事件,cpnClick事件通过item值找到对应的数据并打印。
**父子组件通信案例**
<body>
<div id="app">
<cpn
:number1="num1"
:number2="num2"
@num1change="num1change"
@num2change="num2change"
/>
</div>
<template id="cpn">
<div>
<h2>props:{{number1}}</h2>
<h2>data:{{dnumber1}}</h2>
<!-- <input type="text" v-model="dnumber1" /> -->
<input type="text" :value="dnumber1" @input="num1Input" />
<h2>props:{{number2}}</h2>
<h2>data:{{dnumber2}}</h2>
<!-- <input type="text" v-model="dnumber2" /> -->
<input type="text" :value="dnumber2" @input="num2Input" />
</div>
</template>
<script>
//创建Vue实例,得到 ViewModel
const app = new Vue({
el: "#app",
data: {
num1: 1,
num2: 0
},
methods: {
num1change(value) {
this.num1 = parseFloat(value);
},
num2change(value) {
this.num2 = parseFloat(value);
}
},
components: {
cpn: {
template: "#cpn",
props: {
number1: Number,
number2: Number
},
data() {
return {
dnumber1: this.number1,
dnumber2: this.number2
};
},
methods: {
num1Input(event) {
//1.将input中的value赋值到dnumber中
this.dnumber1 = event.target.value;
//2.为了让父组件可以修改值,发出一个事件
this.$emit("num1change", this.dnumber1);
//3.同时修饰dnumber2的值
this.dnumber2 = this.dnumber1 * 100;
this.$emit("num2change", this.dnumber2);
},
num2Input(event) {
this.dnumber2 = event.target.value;
this.$emit("num2change", this.dnumber2);
this.dnumber1 = this.dnumber1 / 100;
this.$emit("num1change", this.dnumber1);
}
}
}
}
});
</script>
</body>
2.2.3 父子组件的访问
父访问子:
<div id="app">
<cpn ref="reference"></cpn>
<button @click="btnClick">按钮</button>
</div>
console.log(this.$refs.reference.name);==console.log(this.$children.name);
在父组件里 this.$refs.reference 通过对应子组件 ref="reference" 的进入子组件并获取相应数据
this.$refs比 this.$children要常用
子访问父:
通过This.$parent访问
2.2.4 非父子组件的通讯
注意:
尽管在 Vue 开发中,我们允许通过 $parent 来访问父组件,但是在真实开发中尽量不要这样做,因为这样耦合度太高了
子组件应该尽量避免直接访问父组件的数据,如果我们将子组件放在另外一个组件之内,很可能该父组件没有对应的属性,往往会引起问题
另外,通过 $parent 直接修改父组件的状态,那么父组件中的状态将变得飘忽不定,很不利于调试和维护
也不常用 $root 来访问根组件(即 vue 实例),因为根组件中一般只存放路由等重要数据,不存放其他信息
3.组件化高级
3.1 slot插槽
3.1.1 编译作用域
父组件模板的所有东西,都只会在父级作用域内编译
子组件模板的所有东西,都只会在子级作用域内编译
3.1.2 为什么使用slot
组件的插槽,也是为了让我们封装的组件更加具有扩展性,让使用者可以决定组件内部的一些内容到底展示什么
3.1.3 slot的基本使用
在子组件中,使用特殊的元素 就可以为子组件开启一个插槽,该插槽插入什么内容取决于父组件如何使用
3.1.4 slot的具名插槽
当子组件的功能复杂时,子组件的插槽可能并非是一个,比如我们封装一个导航栏的子组件,可能就需要三个插槽,分别代表左边、中间、右边
那么,外面在给插槽插入内容时,如何区分插入的是哪一个呢?这个时候,我们就需要给插槽起一个名字,这就是具名插槽
具名插槽的使用很简单,只要给 slot 元素一个 name 属性即可:,
使用时在对应标签内加入slot=”myslot”
3.1.5 slot作用域插槽
父组件替换插槽的标签,但是内容由子组件来提供
4.组件生命周期
beforeCreate
Vue对象用新方法实例化。它创建一个Vue类的对象来处理DOM元素。对象的这个生命阶段可以通过beforeCreated 挂钩来访问 。我们可以在这个钩子中插入我们的代码,在对象初始化之前执行。
created
在这个生命阶段,对象及其事件完全初始化。 created 是访问这个阶段并编写代码的钩子。
beforeMount
DOM未完成挂载,数据也初始化完成,但是数据的双向绑定还是显示{{}},这是因为Vue采用了Virtual DOM(虚拟Dom)技术。先占住了一个坑。这个钩子被调用 beforeMounted。在这个阶段,它检查是否有任何模板可用于要在DOM中呈现的对象。如果没有找到模板,那么它将所定义元素的外部HTML视为模板。
mounted
数据和DOM都完成挂载,在上一个周期占位的数据把值给渲染进去。可以在这边请求,不过created请求会更好一些。这个周期适合执行初始化需要操作DOM的方法。
beforeUpdate
只要是页面数据改变了都会触发,数据更新之前,页面数据还是原来的数据,当你请求赋值一个数据的时候会执行这个周期,如果没有数据改变不执行。
updated
只要是页面数据改变了都会触发,数据更新完毕,页面的数据是更新完成的。beforeUpdate和updated要谨慎使用,因为页面更新数据的时候都会触发,在这里操作数据很影响性能和容易死循环。
beforeDestroy
这个周期是在组件销毁之前执行, beforeDestroy无法阻止路由跳转,但是可以做一些路由离开的时候操作,因为这个周期里面还可以使用data和method。比如一个倒计时组件,如果在路由跳转的时候没有清除,这个定时器还是在的,这时候就可以在这个里面清除计时器。
Destroyed
销毁对象
四、Vue Cli详解
1.webPack
1.1 什么是webpack
At its core, webpack is a static module bundler for modern JavaScript applications.
从本质上来讲,webpack是一个现代的JavaScript应用的静态模块打包工具。
前端模块化
在ES6之前,我们要想进行模块化开发,就必须借助于其他的工具,让我们可以进行模块化开发,并且在通过模块化开发完成了项目后,还需要处理模块间的各种依赖,并且将其进行整合打包,而 webpack 其中一个核心就是让我们可能进行模块化开发,并且会帮助我们处理模块间的依赖关系,而且不仅仅是 JavaScript 文件,我们的 CSS、图片、json 文件等,在 webpack 中都可以被当做模块来使用,这就是 webpack 中模块化的概念
打包
webpack 可以帮助我们进行模块化和处理模块间的各种复杂关系,而打包则是将 webpack 中的各种资源模块进行打包合并成一个或多个包(Bundle),并且在打包的过程中,还可以对资源进行处理,比如压缩图片,将 scss 转成 css,将 ES6 语法转成 ES5 语法,将TypeScript 转成 JavaScript 等操作
1.3 webpack和gulp的对比
grunt/gulp 的核心是 Task:我们可以配置一系列的 task,并且定义 task 要处理的事务(例如 ES6、ts 转化,图片压缩,scss 转成 css),之后让 grunt/gulp 来依次执行这些 task,而且让整个流程自动化,所以 grunt/gulp 也被称为前端自动化任务管理工具
如果你的工程模块依赖非常简单,甚至是没有用到模块化的概念,只需要进行简单的合并、压缩,就使用 grunt/gulp 即可。但是如果整个项目使用了模块化管理,而且相互依赖非常强,就可以使用更加强大的 webpack
grunt/gulp 和 webpack 有什么不同?
grunt/gulp:更加强调前端流程的自动化,模块化不是它的核心
webpack:更加强调模块化开发管理,而文件压缩合并、预处理等功能,是他附带的功能
1.4 手动配置webpack
1.创建一个 webpack.config.js 文件
- 安装局部 webpack
npm install webpack@3.6.0 -save-dev
1.4.1 Webpack配置loader
1. css-loader 和style-loader 的使用
css-loader 解析 CSS 文件后,使用 import 加载,并且返回 CSS 代码。
style-loader 将模块的导出作为样式添加到 DOM 中
1.1安装命令
npm install style-loader css-loader --save-dev
- 2 在 webpack.config.js 中的 module关键字下添加如下进行配置
rules: [
{
test: /\.css$/,
use: ["style-loader", "css-loader"]
},
{
test: /\.less$/,
use: [
{
loader: "style-loader" // creates style nodes from JS strings
},
{
loader: "css-loader" // translates CSS into CommonJS
},
]
},
]
2. 安装配置 url-loader****
url-loader 像 file-loader 一样工作,但如果文件小于限制,可以返回 data URL
图片限制在8KB以下,也就是limit:8000;此时使用base64对图片进行编码
2.1 安装命令
npm install --save-dev url-loader
- 2 在 webpack.config.js 中的 module关键字下添加如下进行配置
{
test: /\.(png|jpg|gif|jpeg)$/,
use: [
{
loader: "url-loader",
options: {
//当加载的图片小于limit时,会将图片编译成base64字符串形式。
//当加载的图片大于limit时,需要使用file-loader模块进行加载
limit: 8196,
name: "img/[name].[hash:8].[ext]"
}
}
3. 安装配置 file-loader****
file-loader 将文件发送到输出文件夹,并返回(相对)URL,大于8KB的图片都需要使用file-loader进行处理。
3.1 安装命令
npm install --save-dev file-loader
3.2 在 webpack.config.js 中的 module关键字下添加如下进行配置
- img:文件要打包到的文件夹
- name:获取图片原来的名字,放在该位置
- hash:8:为了防止图片名称冲突,依然使用 hash,但是我们只保留8位
- ext:使用图片原来的扩展名
{
test: /\.(png|jpg|gif|jpeg)$/,
use: [
{
loader: "url-loader",
options: {
// 当加载的图片, 小于limit时, 会将图片编译成base64字符串形式.
// 当加载的图片, 大于limit时, 需要使用file-loader模块进行加载.
limit: 13000,
name: "img/[name].[hash:8].[ext]"
}
}
]
}
4. 安装配置babel-loader
将 ES6 的语法转成 ES5 ,避免一些还不支持ES6的浏览器没有办法很好的运行我们的代码
4.1 安装命令
npm install --save-dev babel-loader@7 babel-core babel-preset-es2015
- 2 在 webpack.config.js 中的 module关键字下添加如下进行配置
{
test: /\.js$/,
exclude: /(node_modules|bower_components)/,
use: {
loader: 'babel-loader',
options: {
presets: ['es2015']
}
}
}
1.4.2 Webpack配置Vue
1. 安装命令
npm install vue -s
2. 在 main.js 中引入 vue,创建 vue 实例
// 引用 vue
import Vue from 'vue'
// 创建 vue 实例
const vue = new Vue({
el:'#app',
data:{
message:”aoziao”
}
})
3.在 index.html 中挂载 vue 实例
<div id="app">
<h3>{{message}}</h3>
</div>
- 重新打包,运行程序, 发现打包过程没有任何错误,但是运行程序,没有出现想要的效果,这是因为目前我们使用的 Vue 版本为:runtime-only,这里需要修改为:runtime-compiler修改方法就是在 webpack.config.js 中添加如下代码:
resolve: {
// alias: 别名
extensions: [".js", ".css", ".vue"],
alias: {
vue$: "vue/dist/vue.esm.js"
}
}
- vue 文件封装处理
5.1 安装 vue-loader 和 vue-template-compiler
npm install vue-loader@14.2.2 vue-template-compiler --save-dev
5.2 修改 webpack.config.js 的配置文件
{
test: /\.vue$/,
use: ['vue-loader']
}
5.3 vue文件夹建立App.vue文件,在mian.js引用App.vue文件
1.4.3 Webpack配置plugin
1.什么是plugin:
plugin 是插件的意思,通常是用于对某个现有的架构进行扩展
webpack 中的插件,就是对 webpack 现有功能的各种扩展,比如打包优化,文件压缩等
2.loader 和 plugin 区别****
loader 主要用于转换某些类型的模块,它是一个转换器
plugin 是插件,它是对 webpack 本身的扩展,是一个扩展器
3.plugin 的使用过程
通过 npm 安装需要使用的 plugins (某些 webpack 已经内置的插件不需要安装)
在 webpack.config.js 中的 plugins 中配置插件
4.添加版权的 plugin:BannerPlugin
BannerPlugin 属于 webpack 自带的插件,可以为打包的文件添加版权声明,使用方式如下:
plugins: [ new webpack.BannerPlugin("最终版权归奥兹奥所有"), ],
- 打包 html 的 plugin:HtmlWebpackPlugin
自动生成一个 index.html 文件(可以指定模板来生成),将打包的 js 文件,自动通过 script 标签插入到 body 中
5.1 安装命令
npm install html-webpack-plugin --save-dev
5.2修改 webpack.config.js 文件中 plugins
const HtmlWebpackPlugin = require("html-webpack-plugin");
plugins: [
new HtmlWebpackPlugin({
template: "index.html"
}),
],
- js 压缩的 Plugin:uglifyjs-webpack-plugin
在项目发布之前,我们必然需要对 js 等文件进行压缩处理,对打包的js文件进行压缩,可以使用一个第三方的插件 uglifyjs-webpack-plugin,并且版本号指定 1.1.1,和 CLI2 保持一致。
6.1 安装命令
npm install uglifyjs-webpack-plugin@1.1.1 --save-dev
6.2 修改 webpack.config.js 文件中 plugins
const UglifyjsWebpackPlugin = require("uglifyjs-webpack-plugin");
plugins: [
new UglifyjsWebpackPlugin()
]
1.4.4 Webpack搭建本地服务器
webpack 提供了一个可选的本地开发服务器,这个本地服务器基于 node.js 搭建,内部使用 express 框架,可以实现让浏览器自动刷新显示我们修改后的结果,不过它是一个单独的模块,在webpack中使用之前需要先安装它。
1.安装 webpack-dev-server
npm install --save-dev webpack-dev-server@2.9.1
- webpack.config.js 文件配置修改:在moudle.exports下配置如下代码
devServer: {
contentBase: "./dist",
inline: true, //是否自动刷新
port:8080 //端口号
}
- package.json配置启动命令
"scripts": {
"dev": "webpack-dev-server --open"
},
4.执行命令 npm run dev
1.4.5webpack 配置分离
由于我们目前所有的生产环境代码和发布环境代码都写在 webpack.config.json 中,这样当我们在编译的时候,会将生产环境代码和发布环境代码一起编译,但有时候我们希望将二者分开编译,比如:
生产环境时,我们希望在生成服务器的代码执行,但是不希望执行uglifyjs-webpack-plugin命令对项目进行压缩打包,这时候,将生产环境代码和发布环境代码抽离出来显得很有必要。
配置分离操作步骤:
1.一个文件夹 build,将所有编译环境代码放到里面,在 build 中新建三个文件:
其中 base.config.js为公共部分、dev.config.js为开发部分、prod.config.js为生产部分
- 安装对配置文件合并的插件:webpack-merge
npm install webpack-merge --save-dev
3.修改配置文件
dev.config.js
prod.config.js
- 删除原来的 webpack.config.js 文件
此时执行 npm run build 命令会报如下错误:
5.修改 package.json 文件
此时可以打包成功了,但是发现打包的 bundle.js 的位置是在 built/dist 文件夹中,而不是在原来的 dist 中,这时因为在 base.config.js 中的 output 中的文件拼接路径是 'dist',表示的是从当前路径下拼接,需要将其修改为从上一级目录开始拼接:
2.Vue CLI
2.1 Vue CLI是什么
如果你只是简单写几个 Vue 的 Demo 程序, 那么你不需要 Vue CLI,但是如果你在开发大型项目, 那么你需要, 并且必然需要使用 Vue CLI
使用 Vue.js 开发大型应用时,我们需要考虑代码目录结构、项目结构和部署、热加载、代码单元测试等事情,如果每个项目都要手动完成这些工作,效率比较低,所以通常我们会使用一些脚手架工具来帮助完成这些事情。
CLI 是 Command-Line Interface , 翻译为命令行界面, 俗称脚手架
2.2 Vue CLI依赖环境
Node 与 webpack ,确保电脑安装了这两个东西
2.3 Vue CLI的安装
脚手架3安装命令: npm install -g @vue/cli
安装完后脚手架2拉取命令: npm install -g @vue/cli-init
3.Vue CLI2的使用
3.1 Vue CLI配置过程
3.1.1 Vue CLI2配置过程
3.1.1 Vue CLI3配置过程
3.2 Vue CLI目录结构
3.2.1 Vue CLI2目录结构
3.2.1 Vue CLI3目录结构
注意:Runtime-Compiler 和 Runtime-only 的区别
runtime-copmpiler : twmpalte -> ast -> render -> vdom ->UI
runtime-only(性能更高,代码量更少,且较上面小6KB) : render -> vdom ->UI
如果在之后的开发中,你依然使用 template ,就需要选择 Runtime-Compiler
如果你之后的开发中,使用的是 .vue 文件开发,那么可以选择 Runtime-only (使用!)
3.3 入口文件详解
3.3.1 Vue渲染过程
当我们把template传给vue的时候,vue会做一个保存,将template保存到vm.options中,让后对template进行一个parse(解析)操作,此时tempalte被解析为ast结构(抽象语法树 abstract syntax tree),让后vue会将ast编译(compile)成一个render函数,通过render函数创建一些virtual point(虚拟节点),再有virtual point组成虚拟dom树(virtual dom),之后再将虚拟dom树渲染成真实dom即显示在界面的dom,或者说UI上面的东西。
twmpalte -> ast -> render -> virtual dom ->UI
3.3.2 render函数的使用
New Vue({
el:’#app’
render:function(createElement => h){
return createElement(“h2”,”{class :”box”}”,[”Hello World” ]) }
createElement(“标签”,”{标签属性}”,[”内容”])
}
createElement中的h2标签会替换掉挂载的<div id= ” app ”></div>,使他变成""<h2></h2>,在内容里面除开 ” Hello World ”,还调入createElement(“button”,[‘按钮’]),此时内容会增加一个button。
createElement除开上述做法,还可以传入组件对象
New Vue({
el:’#app’
render:function(createElement => h){
return createElement(App);
此时没有template进行编译,直接给一个render函数,大大加快了编译速度
}
3.4 webpack配置解析
npm run build
npm run dev
4.Vue CLI3的使用
4.1 Vue CLI配置修改
4.1.1 Vue UI
在 Vue CLI 3 中,webpack 等相关配置文件被隐藏起来了,如果想查看或修改相关配置,所以引入原理Vue UI的概念
通过Vue UI启动本地服务器
4.1.2 配置存放路径
在UI界面里面找到项目配置,在输出目录项配置相关存放路径
4.13 添加新的别名
在 node_modules 中寻找
在项目中新建一个 vue.config.js 文件,将需要修改的配置代码写入其中
最终编译时会自动将我们添加的代码与隐藏的代码进行合并。
五、vue-router
1. 认识路由的概念
1.1 认识路由
路由(routing)就是通过互联的网络把信息从源地址传输到目的地址的活动 — 维基百科
路由器提供了两种机制, 路由和转发
路由是决定数据包从来源到目的地的路径,转发将输入端的数据转移到合适的输出端
1.2 后端路由时代
什么是后端路由?
早期的网站开发,整个 HTML 页面都是是由服务器来渲染的,服务器直接生产渲染好对应的 HTML 页面, 返回给客户端进行展示
服务器如何处理一个网站的诸多页面呢?
首先,一个页面会有自己对应的网址, 也就是 URL,客户端发生请求时,URL 会发送到服务器,服务器通过正则对该URL 进行匹配且最后交给 Controller 进行处理,Controller 进行各种处理后,最终生成 HTML 或者数据,返回给前端,这就完成了一个IO操作,这种操作, 就是后端路由
后端路由的优点
当页面中需要请求不同的路径内容时,交给服务器来进行处理, 服务器渲染好整个页面,并且将页面返回给客户端,这种情况下渲染好的页面,不需要单独加载任何的 js 和 css,可以直接交给浏览器展示,这样也有利于 SEO 的优化
后端路由的缺点
整个页面的模块都要由后端人员来编写和维护,工作量太大,前端开发人员如果要开发页面,需要通过 PHP 和 Java 等语言来编写页面代码,增加了额外的学习成本,HTML 代码和数据以及对应的逻辑混在一起,,不利于编写和维护
1.3 前后端分离时代
随着 Ajax 的出现,有了前后端分离的开发模式:后端只提供 API 来返回数据,前端通过 Ajax 获取数据,并且可以通过 JavaScript 将数据渲染到页面中
优点:
前后端责任变得很清晰,后端专注于数据上, 前端专注于交互和可视化上
当移动端(iOS/Android)出现后,后端不需要进行任何处理, 依然使用之前的一套API即可
1.5 前端路由时代
前端路由的核心:改变URL,但是页面不进行整体的刷新
单页面富应用,即单页Web应用(single page web application,SPA),就是只有一张 Web 页面的应用,是加载单个 HTML 页面并在用户与应用程序交互时动态更新该页面的 Web 应用程序。简单理解:就是在前后端分离的基础上加了一层前端路由
SPA 的特点:
速度:更好的用户体验,让用户在 web app 感受 native app 的速度和流畅
MVVM:经典 MVVM 开发模式,前后端各负其责
ajax:重前端,业务逻辑全部在本地操作,数据都需要通过AJAX同步、提交
路由:在 URL 中采用 # 号来作为当前视图的地址,改变 # 号后的参数,页面并不会重载
SPA 缺点
首屏渲染等待时长: 必须得加载完毕,才能渲染出首屏
seo不友好:爬虫只能拿到一个 div,认为页面是空的,不利于 seo
初次加载耗时多:为实现单页Web应用功能及显示效果,需要在加载页面的时候将 JavaScript、CSS 统一加载,部分页面可以在需要的时候加载,所以必须对 JavaScript 及 CSS 代码进行合并压缩处理,如果使用第三方库,建议使用一些大公司的 CDN,因此带宽的消耗是必然的
SPA 优点
良好的交互体验 :用户不需要重新刷新页面,获取数据也是通过 Ajax 异步获取,页面显示流畅
良好的前后端工作分离模式:单页 Web 应用可以和 RESTful 规约一起使用,通过 REST API 提供接口数据,并使用 Ajax 异步获取,这样有助于分离客户端和服务器端工作,更进一步,可以在客户端也可以分解为静态页面和页面交互两个部分
减轻服务器压力:服务器只用出数据就可以,不用管展示逻辑和页面合成,吞吐能力会提高几倍
共用一套后端程序代码 :不用修改后端程序代码就可以同时用于 Web 界面、手机、平板等多种客户端
2.前端路由的规则
2.1 URL的hash
URL 的 hash 也就是锚点(#),本质上是改变 window.location 的 href 属性,可以通过直接赋值 location.hash 来改变 href,但是页面不发生刷新
2.2 HTML5的history
history 接口是 HTML5 新增的,它有五种模式改变 URL 而不刷新页面
2.2.1 pushState
Console输入history.pushatate({ }, ” ”,’home’)会改变当前界面的URL:localhost:8080/home,
压入的URL形成一个栈结构:先进先出。
2.2.2 repalceState
Console输入history.repalceState({ }, ” ”,’home’),替换之前的URL,无法保留之前的URL记录,也就是说,无法返回上一个URL,你会发现回退箭头按钮为灰色,无法点击。
2.2.3 go
history.go( n ),表示从栈顶一次性弹出n个URL,正值为弹出,赋值为压入
history.go( -1 ) =history.back() history.go(1 )=history.forward()
3.router-view基础
3.1 认识和安装vue-router
目前前端流行的三大框架,都有自己的路由实现: Angular:ngRouter
React:ReactRouter
Vue:vue-router
vue-router 是 Vue.js 官方的路由插件,它和 vue.js 是深度集成的,适合用于构建单页面应用。
vue-router 是基于路由和组件的,路由用于设定访问路径, 将路径和组件映射起来,在 vue-router 的单页面应用中, 页面路径的改变就是组件的切换。
3.2 vue-router的基本使用
3.3.1 安装 vue-router
npm install vue-router --save
3.3.2 步骤一、创建路由组件
因为 vue-router 是一个插件,所以可以通过 Vue.use() 来安装路由功能
Import Vue from“vue”
Import VueRouter from “vue-router”
Vue.use(VueRouter);
const routes = [];
const router = new VueRouter({})
export default router;
之后在mian.js中挂载将路由组件挂载到Vue实例中
Import router from “./router”
New Vue({
render:h =>h(App),
router
}).$mount(‘#app’)
3.3.3 步骤二、配置组件和路由的映射关系
- 新建两个组件
- 为组件配置路由映射关系
3.3.4 步骤三、使用路由
通过和
<router-link>: 该标签是一个 vue-router 中已经内置的组件, 它默认会被渲染成一个 <a> 标签
<router-view>: 该标签会根据当前的路径,动态渲染出不同的组件
3.3 vu-router细节补充
3.3.1 路由的默认路径
默认情况下, 进入网站的首页,我们希望 渲染首页的内容,但是在上面的实现中,默认没有显示首页组件,必须让用户点击才可以
在 routes 中又配置了一个映射:
path:根路径 /
redirect:重定向,也就是将根路径重定向到 /home 的路径下
3.3.2 HTML5的History模式
默认情况下,Vue 路径的改变使用的是 URL 的 hash,这样显示出的页面的地址中有一个 # 号,不太美观,可以使用 HTML5 的 history 模式来进行改变,进行如下配置即可:
3.3.3 router-link补充
属性:
to:用于指定跳转的路径
tag:tag 可以指定 之后渲染成什么组件,默认是渲染为 标签
replace: replace 不会留下 history 记录,所以指定 replace 的情况下, 后退键返回不能返回到上一个页面中
active-class: 当对应的路由匹配成功时,会自动给当前元素设置一个 router-link-active 的class,该 class 具体的名称也可以通过 router 实例的属性进行修改,但是通常不会修改类的属性, 会直接使用默认的 router-link-active 即可
<router-link to="/home" tag="button" replace active-class="active">首页</router-link>
3.3.4 路由代码跳转
有时候,页面的跳转可能需要执行对应的 JavaScript 代码,这个时候,就可以使用第二种跳转方式了
比如,我们将代码修改如下:
注:
动态路由
在某些情况下,一个页面的 path 路径可能是不确定的,比如我们进入用户界面时,希望是如下的路径:/user/aaaa 或 /user/bbbb,除了有前面的 /user 之外,后面还跟上了用户的 ID
这种 path 和 Component 的匹配关系,称之为动态路由(也是路由传递数据的一种方式)
在 vue-router 的路由路径中使用“动态路径参数”(dynamic segment) :
{
path :”/user/:id”
component : User
}
在组件中手动绑定一个用户 ID:
更新 User 的模板,输出当前用户的 ID:
4.路由懒加载
4.1 什么是路由懒加载
路由懒加载的主要作用就是将路由对应的组件打包成一个个的 js 代码块,只有在这个路由被访问到的时候,才加载对应的组件
4.2 懒加载和非懒加载打包区别
当打包构建应用时,Javascript 包会变得非常大,影响页面加载,如果我们能把不同路由对应的组件分割成不同的代码块,然后当路由被访问的时候才加载对应组件,这样就更加高效了
为了实现这种效果,我们可以使用路由的懒加载
4.3 懒加载的方式
4.3.1 方式一:结合Vue的异步组件和webpack的代码分析
const Home = resolve => { require.ensure(['../components/Home.vue'], ()=>
{ resolve(require('../components/Home.vue')) })};
4.3.2 方式二:ADM写法
const About = resolve => require(['../components/About.vue'], resolve);
4.3.3 方式三:在ES6中,有更简单的写法来组织Vue异步组件和Webpack的代码分割
[const Home = () => import('../components/Home.vue')]()
5.路由嵌套的使用
5.1 认识嵌套路由
嵌套路由是一个很常见的功能 ,比如在 home 页面中,我们希望通过 /home/news 和 /home/message 访问一些内容,一个路径映射一个组件,访问这两个路径也会分别渲染两个组件。
路径和组件的关系如下:
实现嵌套路由有两个步骤:
`、创建对应的子组件,并且在路由映射中配置对应的子路由 -> 定义两个子组件
2、配置子组件的路由
5.3 嵌套默认路径
在父组件内部显示子组件
6.路由传递参数
6.1 准备工作
6.1.1 第一步:创建新的组件Profile.vue
6.1.2 第二步:配置路由映射
6.1.3第三步:添加跳转的
6.2 传递参数的方式:params方式
配置路由格式:/router/:id****
传递的方式:在path后面跟上对应的值
传递后形成的路径:/router/123,/router/abc
6.3传递参数的方式: query方式
配置路由格式:/router 也就是普通配置
传递的方式:对象中使用query的key作为传递方式
传递后形成的路径:/router?id=123,/router?id=abc
6.4 获取参数
获取参数的方式:$route对象获取****
在使用vu-route的应用中,路由对象会被注入每个组件中,赋值为this.$sroute,并且当路由切换时,路由对象会被更新
6.5 router的区别
router 为 VueRouter 实例,想要导航到不同 URL,则使用 router.push 方法
$route 为当前 router 跳转对象,里面可以获取 name、path、query、params 等
7.路由导航守卫
7.1什么是导航守卫
导航守卫主要用来监听路由的进入和离开,vue-router 提供了 beforeEach 和 afterEach 的钩子函数,它们会在路由即将改变前和改变后触发
7.2钩子函数
7.1.1 beforeEach
7.1.2 afterEach
7.3守卫应用
7.2.1 动态改变页面标题
见上beforeEarch使用方法
7.3参数解析
to:即将要进入的目标的路由对象
from:当导航将要离开的路由对象
next:调用该方法后才能进入下一个钩子
8.keep-alive
8.1 认识keep-alive
keep-alive 是 Vue 内置的一个组件,可以使被包含的组件保留状态,或避免重新渲染
router-view 也是一个组件,如果直接被包在 keep-alive 里面,所有路径匹配到的视图组件都会被缓存:
<keep-alive>
<router-view>
<!-- 所有路径匹配到的试图组件都会被缓存! -->
</router-view>
</keep-alive>
keep-alive 还有两个非常重要的属性:
include - 字符串或正则表达,只有匹配的组件会被缓存
exclude - 字符串或正则表达式,任何匹配的组件都不会被缓存
<keep-alive exclude="Profile,User"><router-view/></keep-alive>
六、Vuex详解
1.为什么使用Vuex
1.1 Vuex是做什么的
1.1.1 Vuex解释
Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。
Vuex 采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。
Vuex 也集成到 Vue 的官方调试工具 devtools extension,提供了诸如零配置的 time-travel 调试、状态快照导入导出等高级调试功能。
1.1.2 状态管理
把需要多个组件共享的变量全部存储在一个对象里面,然后将这个对象放在顶层的 Vue 实例中,让其他组件可以使用,共享这个对象中的所有变量属性,并且是响应式的。
1.1.3 有什么状态时需要我们在多个组件间共享?
用户的登录状态、用户名称、头像、地理位置信息等
商品的收藏、购物车中的物品等
1.2 单页面状态管理
在单个组件中进行状态管理是一件非常简单的事情,如图:
State:状态(姑且可以当做是 data 中的属性)
View:视图层,可以针对 State 的变化,显示不同的信息
Actions:用户的各种操作:点击、输入等,会导致状态的改变
在这个案例中:
counter 需要某种方式被记录下来,也就是 State,counter 目前的值需要被显示在界面中,也就是我们的 View,界面发生某些操作时(这里是用户的点击,也可以是用户的 input),需要去更新状态,也就是 Actions
1.3 多页面状态管理
多个视图都依赖同一个状态(一个状态改了,多个界面需要进行更新)
全局单例模式(大管家)
现在要做的就是将共享的状态抽取出来,交给大管家统一进行管理,之后每个试图按照规定进行访问和修改等操作,这就是 Vuex 的基本思想
1.4 Vuex状态管理图例
2.Vuex基本使用
2.1 Vuex安装
npm install vuex --save
2.2 Vuex的代码组织
1.创建一个文件夹 store,并且在其中创建一个 index.js 文件
2.在 index.js 文件中写入代码
import Vue from 'vue'
import Vuex from 'vuex'
vue.use(Vuex)
const store = new Vuex.Store({
state: {
count: 0
},
mutations: {
increment(state) {
state.count++
},
decrement(state) {
state.count--
}
}
})
export default store
3.挂载到 Vue 实例中
import Vue from 'vue'
import App from './App'
import router from './router'
import store from './store'
Vue.config.productionTip = false
new Vue({
el: '#app',
router,
store,
render: h => h(App)
})
- 此后,在其他 Vue 组件中,可以通过 this.$store 的方式,获取到 store 对象
2.3 count案例
<template>
<div class="test">
<h2>当前计数:{{count}}</h2>
<button @click="increment">+</button>
<button @click="decrement">-</button>
</div>
</template>
<script>
export default {
name: 'Test',
computed:{
count(){
return this.$store.state.count
}
},
methods:{
increment(){
this.$store.commit('increment')
},
decrement(){
this.$store.commit('decrement')
}
}
}
</script>
<style scoped>
</style>
首先提取出一个公共的 store 对象,用于保存在多个组件中共享的状态,将 store 对象放置在 new Vue 对象中,这样可以保证在所有的组件中都可以使用到,在其他组件中使用 store 对象中保存的状态即可。
通过 this.$store.state.属性 的方式来访问状态
通过 this.$store.commit(‘Mutations中方法’) 来修改状态
注意: 我们通过提交 Mutations 的方式,而非直接改变 store.state.count ,这是因为 Vuex 可以更明确的追踪状态的变化,所以不要直接改变 store.state.count 的值。
3.Vuex核心概念
3.1 state单一状态树
Vuex 提出使用单一状态树(Single Source of Truth),也可以翻译成单一数据源。
如果状态信息是保存到多个 Store 对象中的,那么之后的管理和维护等等都会变得特别困难,所以 Vuex 使用了单一状态树来管理应用层级的全部状态
单一状态树能够让我们以最直接的方式找到某个状态的片段,而且在之后的维护和调试过程中,也可以非常方便的管理和维护
Vuex中存在一个且只存在一个唯一的state对象
3.2 getter
3.2.1 Getters基本使用
从 store 中获取一些 state 变异后的状态,则可以使用 getters
Getters: {
powerCounter(state) {
return state.counter * state.counter;
},
在组件中使用 getters:
<template>
<div id="app">
<h3>{{this.$store.getters.getAgeLess20Count}}</h3>
</div>
</template>
3.2.2 Getters本身作为参数
getters: {
moreAgeStu(state) {
return age => {
return state.student.filter(s => s.age > age);
};
}
};
}
3.2.3 Getters传递参数
getters 默认是不能传递参数的,如果希望传递参数,那么只能让 getters 本身返回另一个函数
getters: {
// 根据id查找学生
getStuByID(state) {
return id => state.students.find(s => s.id === id)
}
}
在组件中使用:
<template>
<div id="app">
<h3>{{this.$store.getters.getStuByID(1002)}}</h3>
</div>
</template>
3.3 Mutation
3.3.1 mutation基本使用
Vuex 的 store 状态的更新唯一方式:提交 Mutations
Mutations 组成
字符串的事件类型(type)
一个回调函数(handler),该回调函数的第一个参数就是 state
Mutations 的定义方式
mutations: {
increment(state) {
state.count++
},
decrement(state) {
state.count--
}
}
通过 Mutations 更新
methods:{
increment(){
this.$store.commit('increment')
},
decrement(){
this.$store.commit('decrement')
}
}
3.3.2 mutation传递参数
在通过 Mutations 更新数据的时候,有时候希望携带一些额外的参数,这种参数被称为 Mutations 的载荷(Payload)
如果有很多参数需要传递,通常会以对象的形式传递,也就是 payload 是一个对象,再从对象中取出相关的信息
3.3.4 提交风格
上面的通过 commit 进行提交是一种普通的方式,Vue 还提供了另外一种风格, 它是一个包含 type 属性的对象。Mutations 中的处理方式是将整个 commit 的对象作为 payload 使用, 所以代码没有改变, 依然如下:
3.3.5 mutation响应规则
提前在store中初始化好所需的属性
当给 state 中的对象添加新属性时,使用下面的方式:
使用 Vue.set(obj, 'newProp', 123)
用新对象给旧对象重新赋值
例:
以上代码给 state 中的对象添加新属性时,由于不是响应式添加,所以界面不会更新,要想让界面更新,可以使用一下方式:
updateInfo(state, payload) {
// state.info['height'] = payload.height
// 方式一:Vue.set()
// Vue.set(state.info, 'height', payload.height)
// 方式二:给 info 赋值一个新的对象
state.info = {...state.info, 'height': payload.height }
}
3.3.6 mutation常量管理
在 Mutations 中, 我们定义了很多事件类型(也就是其中的方法名称),当我们的项目不断增大时,会出现:Vuex 管理的状态越来越多,需要更新状态的情况越来越多,Mutations 中的方法越来越多,使用者需要花费大量的经历去记住这些方法,甚至是多个文件间来回切换,查看方法名称,甚至出现写错的情况,如何避免上述的问题呢?
在各种 Flux 实现中,一种很常见的方案是: 使用常量替代 Mutations 事件的类型,将这些常量放在一个单独的文件中,方便管理以及让整个 app 所有的事件类型一目了然
具体操作: 创建一个文件: mutations-types.js,并且在其中定义我们的常量,定义常量时,我们可以使用 ES5 中的风格,使用一个常量来作为函数的名称,之后在相应位置使用常量。
3.4 Action
通常情况下,Vuex 要求 Mutations 中的方法必须是同步方法,因为当我们使用 devtools 时,devtools 可以帮助我们捕捉 Mutations 的快照,但如果是异步操作, 那么 devtools 将不能很好的追踪这个操作什么时候会被完成,此时可以引用Action
3.4.1 Action基本定义
Action 类似于 Mutations,是用来代替 Mutations 进行异步操作的,在 Action 中,可以将异步操作放在一个 Promise 中,并且在成功或者失败后,调用对应的 resolve 或reject:
3.4.2 Action返回的Promise
3.5 Moudle
3.5.1 认识Vuex的Moudle
Modules 是模块的意思,Vue 使用单一状态树,意味着很多状态都会交给 Vuex 来管理,当应用变得非常复杂时,store 对象就有可能变得相当臃肿,为了解决这个问题,Vuex 允许我们将 store 分割成模块(Module), 每个模块拥有自己的 state、mutations、actions、getters 等
3.5.2 Moudle的局部状态
Modules 中的 mutations
虽然 mutations 是定义在模块中的,但是在组件中提交时还是使用:this.$store.commit
Modules 中的 getters
Modules 中的 actions
3.5.3 Moudle的Action写法
const ModuleA = {
state:{},
mutations:{},
actions:{},
getters:{}
}
const ModuleB = {
state:{},
mutations:{},
actions:{},
getters:{}
}
const store = new Vuex.Store({
modules:{
a:ModuleA,
b:ModuleB
}
})
store.state.a // ModuleA 的状态
store.state.b // ModuleB 的状态
4.项目组织结构
基本项目结构
七、网络封装
1.网络模块选择
1.1 传统的Ajax基于XMLHttpRequest(XHR)
之所以不用它,是因为它配置和调用方式等非常混乱,编码起来看起来令人十分头疼
真实开发中很少直接使用传统的 Ajax , 而是使用 jQuery-Ajax
1.2 jQuery-Ajax
jQuery-Ajax 相对于传统的 Ajax 非常好用
之所以不用它,是因为 jQuery 是一个重量级的框架,代码有 1w+ 行,而 Vue 的代码才 1w+ 行,所以没必要为了网络请求,特意引用一个 jQuery
1.3 Vue-resource
官方在 Vue1.x 的时候, 推出了 Vue-resource,Vue-resource 的体积相对于 jQuery 小很多
之所以不用它,是因为在 Vue2.0 推出后,Vue 的作者就在 GitHub 的 Issues 中说明了去掉 vue-resource,并且以后也不会再更新,意味着以后 vue-reource 不再支持新的 Vue 版本,也不会再继续更新和维护,对以后的项目开发和维护都存在很大的隐患
1.4 axios
在说明不再继续更新和维护 vue-resource 的同时,作者还推荐了一个框架:axios
axios有非常多的优点, 并且用起来也非常方便
2.axios的使用
2.1 认识axios
2.1.1 为什么选择axios
- 在浏览器中发送 XMLHttpRequests 请求;
- 在 node.js 中发送 http请求;
- 支持 Promise API;
- 拦截请求和响应;
- 转换请求和响应数据;
2.1.2 axios请求方式
- axios(config)
- axios.request(config)
- axios.get(url[, config])
- axios.delete(url[, config])
- axios.head(url[, config])
- axios.post(url[, data[, config]])
- axios.put(url[, data[, config]])
- axios.patch(url[, data[, config]])
2.2 发送请求
2.2.1 发送get请求
2.2.2 发送并发请求
使用 axios.all,可以放入多个请求的数组 axios.all([]) 返回的结果是一个数组,使用 axios.spread 可将数组 [res1,res2] 展开为 res1, res2
axios.all([axios.get('http://123.207.32.32:8000/home/multidata'),
axios.get('http://123.207.32.32:8000/home/data', { params: { type: 'sell', page: 1 } })
])
.then(axios.spread((res1, res2) => {
console.log(res1);
console.log(res2);
}))
2.2.3 axios全局配置
在开发中,可能很多参数都是固定的,这个时候,我们可以进行一些抽取,也可以利用 axios 的全局配置
2.2.4 常见配置选项
baseURL:基本网址;
timeout:最长延迟时间;
2.3 axios实例
2.3.1 为什么创建axios实例
当我们从 axios 模块中导入对象时,使用的实例是默认的实例,当给该实例设置一些默认配置时,这些配置就被固定下来了,但是后续开发中,某些配置可能会不太一样,比如某些请求需要使用特定的 baseURL 或者 timeout 或者content-Type 等,这时我们就可以创建新的实例,并且传入属于该实例的配置信息
2.3.2 如何创建axios实例
const axiosInstance = axios.create({
baseURL: 'http://123.207.32.32:8000',
timeout: 5000,
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
}
})
// 发送网络请求
axiosInstance({
url: '/category',
method: 'get'
}).then(res => {
console.log(res);
}).catch(err => {
console.log(err);
})
2.3.3 axios的封装
2.3.3 axios的请求
2.4 axios的拦截器
2.4.1 请求拦截器
请求拦截器的时候,可以实现:
在发送网络请求是,在页面添加一个loading组件作为动画;
要求用户必须登录,判断用户是否有token,如果没有九跳转到login页面;
2.4.2 响应拦截器
响应的成功拦截中,主要是对数据进行过滤,响应的失败拦截中,可以根据 status 判断报错的错误码,跳转到不同的错误提示页面: