文章目录
1. vue的介绍
1.1 概念
vue官网称vue.js是渐进式的JavaScript框架,但什么是渐进式?什么是框架呢?
- 渐进式:vue的渐进式主要体现在它的全家桶(vue + vue-router + vuex)
在项目的开发中,起初是一个简单的demo,使用vue的基础知识就足够了。
但随着项目的开发,页面会越来越多,这时就需要使用vue-router(路由)来管理页面,从而实现组件化开发。
后期数据会逐渐增多,这时就可以使用vuex(状态管理工具)来管理数据。
- 框架与库的区别参考网址
库(Lib):一系列函数的集合,需要实现某个功能,由开发人员去调取。类似于自己购买配件,需要自己去组装成一台台式电脑之后才能使用。
框架(Framework):一套完整的解决方案,框架中制定了一套规则,使用框架的时候,只需要按照规则,把代码放到合适的地方,然后框架会在合适的时机,主动调用开发人员的代码。类似于购买了笔记本电脑,只需要按照它给定的方式去使用。
二者核心的区别在于:控制反转。库的控制权在开发人员,但框架的控制权在于框架。
1.2 设计模式MVVM
- MVC
Controller负责将Model的数据用View显示出来,换句话说就是在Controller里面把Model的数据赋值给View
M:model(数据层)专门用于操作数据
V:view(视图层)相对于前端来说就是页面
C:controller(控制层)是数据层与视图层沟通的桥梁,用于处理逻辑业务
- MVVM
MVVM 提出了 通过 数据双向绑定 让数据自动的双向同步的思想。因为在前端中频繁操作DOM比较消耗性能,因此为优化性能即减少操作DOM,vue就根据MVVM的思想,采用数据驱动视图的模式,以 数据为核心。以后如果想要操作 DOM, 立马想到的不是拿到元素设置,而是数据。
M:model(数据层)
V:view(视图层)
VM:view model(视图模型)
核心 : M <===> VM <===> V
1.3 数据双向绑定内在原理
vue的数据双向绑定(v-model) 是通过 数据劫持
Object.defineProperty() 来实现的,因此本质上vue是单向数据流
。
注意 :Object.defineProperty() 是 es5 提出来的一个 无法 shim(兼容) 的特性 , 无法兼容ie8以下。
<input type="text" id="txt" />
let obj = {}
let temp
var txt = document.querySelector("#txt")
//V→M
txt.oninput =>{
obj.name = this.value
}
// M→V
// 参数1 : 要给哪个对象设置属性
// 参数2 : 给对象设置什么属性
// 参数3 : 属性的修饰符
Object.defineProperty(obj,'name',{
set(newVal){
temp = newVal
txt.value = newVal
},
get(){
return temp
}
})
1.4.插值表达式{{}}
- 作用:绑定data中的数据,显示到模板上。数据发生变化,视图中展示的数据也会发生变化
- 注意:双括号中间只能放表达式(运算、方法、三元),不能放语句(例如:if、for、while)
2.指令
指令是HTML标签上特殊的属性,添加额外的功能。vue的指令统一是‘v-’开头
2.1 v-model
作用:实现数据的双向绑定。
使用场景:只能用于表单元素(文本框、单选框、多选框、文本域、下拉框等)
注意:使用在不同的表单元素,绑定的类型也不一样。例如:文本框是文本内容,单选框绑定的是状态(false/true)
2.2 v-text 与 v-html
作用:展示数据
用法类似于innerText与innerHtml,v-text不识别标签,v-html识别标签。与插值表达式实现的效果一样。
2.3 v-bind
作用:动态显示一个属性值
写法:
<!-- 全写 -->
<div v-bind:title="msg">嗯哼</div>
<!-- 简写 -->
<div :title="msg">嗯哼</div>
运用
<style>
.blue{color:blue}
.fz{font-size:24px}
</style>
<div id="app">
<div :class="{blue:isBlue,fz:isFz}">嗯哼</div>
</div>
<script>
const vm = new Vue({
el:"#app",
data:{
isBlue:true,
isFz:false
}
})
</script>
- 与v-model的区别
v-model | v-bind | |
---|---|---|
写法 | <input type="checkbox" v-model="isChecked1" /> | <input type="checkbox" v-bind:checked="isChecked2" /> |
使用场景 | 表单元素 | 任何元素的属性 |
方向 | 双向数据绑定 | 单向M→V |
2.4 v-on
作用:注册/绑定事件
写法:
<!-- 全写 -->
<button v-on:click="fn">嗯哼</button>
<!-- 简写 -->
<button @click="fn">嗯哼</button>
- 事件中的“this”指向vue实例
- 事件函数带参数传参,要获取事件对象时用$event
<button @click="fn($event,123)">带参数时需获取事件对象</button>
2.4.1事件修饰符
官方地址
格式:@事件.事件修饰符 = “事件函数”
范围:修饰任意事件
.prevent | .stop | .capture | .once | .self | .passive | |
---|---|---|---|---|---|---|
功能 | 阻止默认行为 | 阻止冒泡(添加在子元素) | 捕获(添加在父元素) | 只触发一次(添加在子元素) | 只有点击自己才触发(关闭冒泡和捕获) | 用于移动端提高性能,只需判断一次是捕获还是冒泡 |
2.4.2按键修饰符
官方地址
格式:@事件.按键修饰符 = “事件函数”
范围:修饰按键事件
.enter | .up | .down | .left | .right | .esc | delete | |
---|---|---|---|---|---|---|---|
功能 | 回车 | 上 | 下 | 左 | 右 | esc键 | 删除 |
2.5 v-for
作用:遍历数据,根据数据里元素的个数创建对应个数的标签
注意
:
- 官方推荐使用v-for时加上 key,目的是为了防止就地复用
- 默认key的取值是索引index,但使用时,不能改变数据的顺序
- key的取值应是数据中唯一固定的值,常取id值,即 :key=‘item.id’
<!-- 遍历数组 -->
<ul>
<li v-for="(item,index) in arr" :key="index">{{item}}</li>
</ul>
<!-- 遍历对象 -->
<ul>
<li v-for="(value,key,index) in obj" :key="index">{{key}}:{{value}}</li>
</ul>
<!-- 遍历数组对象 -->
<ul>
<li v-for="item in arrObj" :key="item.id">{{item.name}}</li>
</ul>
2.6 v-if 与v-show
作用:控制元素的显示与隐藏
区别:控制方式不同
v-show | v-if | |
---|---|---|
显示 | display:block | 新建节点 |
隐藏 | display:none | 删除节点 |
因为v-if操作的是节点,因此比较消耗性能 ,操作频率高的情况下,不推荐使用v-if
2.7 条件渲染指令
包含v-if、v-else-if、v-else,用法与if(){}else if (){}else(){}类似
<div id="app">
<p v-if="age >= 18">成年人</p>
<p v-else-if="age >= 12">青少年</p>
<p v-else="age < 12">儿童</p>
</div>
2.8其它指令
v-pre:不解析,可直接显示 Mustache 标签即“{{ }}”符号
v-once :解析一次
v-cloak :遮盖,可以隐藏未编译的 Mustache 标签直到实例准备完毕。
<!-- v-cloak使用步骤-->
<!-- 1. 添加指令:-->
<div v-cloak>{{ num}}</div>
<!-- 2.利用属性选择器添加样式 -->
<style>
[v-cloak]{
display:none
}
</style>
<!--3. vue会在数据解析完,自动删除v-cloak指令-->
3. computed(计算属性)
- 为什么需要用计算属性?
- vue中的data里的数据发生改变时,当前页面中所有的指令与表达式都会重新计算,因此操作频繁的情况下比较消耗性能。但有时只需要某一条数据改变而影响得到的结果,为避免性能的消耗,就需要用计算属性。
- 什么时候用计算属性?
- 官方: Vue.js 的内联表达式非常方便,但它最合适的使用场景是简单的布尔操作或字符串拼接。如果涉及更复杂的逻辑,你应该使用计算属性。
- 根据data中的已知值,想得到一个新值,且新值会根据data里的那个已知值变化而变化时。
- 特点
- 一定有一个返回值(结果)
- 计算结果会受相关数据的变化而变化,但是不会其它无关数据的影响
- 注意点
- 一定要有一个返回值
- 不能当方法使用
- 计算属性不能和data中的数据重名
<!-- 计算属性的案例 -->
<div id="app">
num1 : <input type="text" v-model="num1" /> +
num2 :<input type="text" v-model="num2" /> =
<span>{{ sum }}</span>
</div>
<script>
const vm = new Vue({
el: '#app',
data: {
num1: '',
num2: ''
},
computed: {
sum() {
return +this.num1 + +this.num2
}
}
})
</script>
4.$nextTick(解决DOM异步更新问题)
DOM异步更新,若需获取DOM更新后的数据就需要用setTimeOut(延时器)或者 $nextTick 。但延时器的时间不好把控,因此需要使用 $nextTick。
this. $nextTick()在DOM异步更新结束之后,自动执行回调函数,可在回调函数中获取自己所需的数据。
<div id="app">
<p>{{ num }}</p>
<button @click="fn">按钮</button>
</div>
<script>
const vm = new Vue({
el: '#app',
data: {
num: 100
},
methods: {
fn() {
console.log(document.querySelector('p').innerText)
this.num += 1
// 方式1 : 延时器
// setTimeout(() => {
// console.log(document.querySelector('h1').innerText)
// }, 1000)
// 方式2 : $nextTick,DOM更新完毕后,就可以获取DOM数据了
this.$nextTick(() => {
console.log(document.querySelector('p').innerText)
})
}
}
})
</script>
5. $set (解决数据响应式问题)
data中存在的数据,vue可监测到,在进行修改时,数据能响应式改变;但data中不存在的数据,数据就不会响应式变化。如果想让data中不存在的数据进行响应式更新,就需要借助于 $set
格式:this. $set(对象,属性,初始值)
this.$set(this.obj,'name','stitch')
6.watch(监听器)
作用:监听数据的变化,可应用于本地持久化
使用:
//简单类型
watch:{
//参数1:新值
//参数2:旧值(可省略)
被监听数据(newVal,oldVal){
console.log(newVal)
}
}
//复杂类型(复杂类型监听到的是地址,地址一直未改变,因此监听数据的变化需要深度监听)
//方式1:深度监听
watch:{
obj:{
//深度监听
deep:true,
//立即监听(非必要)
immediate:true,
//处理函数
handler(newVal,oldVal){
console.log(newVal.name)
}
}
}
//方式2:直接监听需要的属性
watch:{
'obj.name'(newVal){
console.log(newVal)
}
}
computed与watch的区别
computed是根据一个或多个值(data里的值),通过计算得到一个新值,当data的值发生变化得到的新值随着改变,是多对一或者一对一的关系。别人影响我。
watch监听的是data里的数据,当数据改变时,影响接下来的操作,是一对多的关系。自己改变影响别人。
7.filter(过滤器)
作用:vue.js允许你自定义过滤器,可被用于一些常见的文本格式化。过滤器可以用在两个地方:双花括号插值和 v-bind 表达式 (后者从 2.1.0+ 开始支持)。过滤器应该被添加在 JavaScript 表达式的尾部,由“管道”符号指示。
步骤:
- 先注册
参数1:过滤器名称
参数2:回调函数(参数1:需格式化的数据,参数2(可省略):过滤器传过来的参数)
注意:回调函数一定要有返回值
- 全局注册
Vue.filter('filterName', function (value) { return value的处理 }) new Vue({ el:'#app', data:{ date: new Date() } })
- 局部注册
new Vue({ el:'#app', data:{ date: new Date() } filters: { filterName(value) { return value的处理 } } })
- 再使用
- 双括号插值,格式:{{ 需处理的数据 | 过滤器 }}
<p> {{ date | filterName }} </p>
- v-bind表达式(从 2.1.0+ 开始支持)
<div v-bind:id="rawId | formatId"></div>
8.生命周期
概念: 生命周期函数又称为钩子函数
注意: 钩子函数到对应阶段时自动调用;命名是规定好的
钩子函数 | 阶段 | 使用场景 |
---|---|---|
beforeCreate | 挂载阶段 初始化内部使用的事件,开启生命周期 | 无法获取data中的数据;加loading事件 |
created★ | 挂载阶段 数据响应式之后 | 发送Ajax请求;操作data中的数据;获取本地存储数据 |
beforeMount | 挂载阶段 DOM渲染前,数据与模板初始化完成 | \ |
mounted ★ | 挂载阶段 DOM渲染完成后 | 操作DOM元素;发送Ajax请求 |
beforeUpdate | 更新阶段 更新前 | 获取更新前的数据 |
updated ☆ | 更新阶段 数据更新完成后 | 获取更新后的数据 |
beforeDestory | 卸载阶段 | \ |
destoryed | 卸载阶段 | 清理开启的定时器;移除手动创建的DOM对象等 |
9.组件
概念:可复用的UI模块;本质上是一个Vue实例。
注册:
<!-- 全局注册 -->
Vue.component('child',{
template:`<div> child </div>`,
data(){
return{
}
}
})
<!-- 局部注册 方式1-->
const vm = new Vue({
components : {
child : {
template : `<div>child</div>`
}
}
})
<!-- 局部注册 方式2 -->
const child = {
template : `<div></div>`
}
const vm = new Vue({
components : child
})
注意:
1. 注册组件需要在vue实例前
2. template只有一个根组件
3. 配置对象和vue实例中大部分一样
4. 组件中的data是一个函数,函数里面返回一个对象是组件里的数据,因为我们希望组件被复用,但不希望组件中的数据被复用
组件化开发
概念:将一个页面抽象成一个个独立的组件。
优点:可复用性强
与模块化开发的区别:模块化开发重业务,重逻辑,重功能,而组件化开发重界面/UI。
10. 通信机制
原因:组件是一个封闭的个体,组件之间无法直接获取对方的数据,因此需要组件之间的通信机制来访问数据。
10.1 父传子
实现思路:子组件通过配置项props,自定义一个属性,父组件通过这个属性将数据传递给子组件。
<div id="app">
<!-- 1.通过属性,父组件将数据传递给子组件 -->
<child :msg="pmsg"></child>
</div>
<script src="../node_modules/vue/dist/vue.js"></script>
<script>
Vue.component('child',{
template:`<div id="child">子组件:{{msg}}</div>`,
// 2.子组件通过配置项props,指定需接收来的数据
props:{
msg : String
}
})
const vm = new Vue({
el:'#app',
data:{
pmsg:'父组件中的数据'
}
})
</script>
10.2 子传父
实现思路:子组件通过触发自定义事件,利用函数传参的方式,将数据传递给父组件。
<div id="app">
<p>父组件:{{cmsg}}</p>
<!-- 2.在子组件上定义一个事件 -->
<child @event="pfn"></child>
</div>
<script>
Vue.component('child',{
template:`<div id="child"></div>`,
data(){
return{
cmsg:'子组件中的数据'
}
},
created(){
// 3.触发自定义事件
this.$emit('event',this.cmsg)
}
})
const vm = new Vue({
el:'#app',
data:{
cmsg:''
},
methods:{
// 1.在父组件中准备好一个方法
pfn(cmsg) {
this.cmsg = cmsg
}
}
})
</script>
vm.$emit( event, arg )
:触发当前实例上的事件,(参数1:事件名,参数2:传递的参数)
10.3 非父子
实现思路:通过事件总线(event bus)为媒介,发送数据的组件触发事件,接收数据的组件注册事件
<div id="app">
<child1></child1>
<child2></child2>
</div>
<script src="../node_modules/vue/dist/vue.js"></script>
<script>
// 1.创建事件总线
const bus = new Vue()
Vue.component('child1',{
template:`<div id="child">child1:<button @click='send'>发送数据</button></div>`,
data(){
return{
msg1:'child1发送的数据'
}
},
methods:{
send(){
// 2.发送数据的组件,触发事件
bus.$emit('transferData',this.msg1)
}
}
})
Vue.component('child2',{
template:`<div id="child">child2:{{msg2}}</div>`,
data(){
return{
msg2:''
}
},
created(){
// 3.接收数据的组件,注册事件
bus.$on('transferData',(res)=>{
this.msg2 = res
})
}
})
const vm = new Vue({
el:'#app'
})
</script>
注意: 要保证注册事件的代码先执行,触发事件的代码后执行
11. props
-
单向数据流
所有的 prop 都使得其父子 prop 之间形成了一个单向下行绑定:父级 prop 的更新会向下流动到子组件中,但是反过来则不行。这样会防止从子组件意外变更父级组件的状态,从而导致你的应用的数据流向难以理解。 额外的,每次父级组件发生变更时,子组件中所有的 prop 都将会刷新为最新的值。这意味着你不应该在一个子组件内部改变 prop。如果你这样做了,Vue 会在浏览器的控制台中发出警告。
-
大小写问题
HTML 中的 attribute 名是大小写不敏感的,所以浏览器会把所有大写字符解释为小写字符。这意味着当你使用 DOM 中的模板时,camelCase (驼峰命名法) 的 prop 名需要使用其等价的 kebab-case (短横线分隔命名) 命名:
<!-- 在 HTML 中是 kebab-case 的 --> <blog-post post-title="hello!"></blog-post> <script> Vue.component('blog-post', { // 在 JavaScript 中是 camelCase 的 props: ['postTitle'], template: '<h3>{{ postTitle }}</h3>' }) </script>
-
prop校验
当 prop 验证失败的时候,(开发环境构建版本的) Vue 将会产生一个控制台的警告。props: { // 基础的类型检查 (`null` 和 `undefined` 会通过任何类型验证) propA: Number, // 多个可能的类型 propB: [String, Number], // 必填的字符串 propC: { type: String, required: true }, // 带有默认值的数字 propD: { type: Number, default: 100 }, // 带有默认值的对象 propE: { type: Object, // 对象或数组默认值必须从一个工厂函数获取 default: function () { return { message: 'hello' } } }, // 自定义验证函数 propF: { validator: function (value) { // 这个值必须匹配下列字符串中的一个 return ['success', 'warning', 'danger'].indexOf(value) !== -1 } } }
12. refs
作用:获取标签/组件
先注册ref,凡是注册过ref的标签/数组,都会被refs收集起来,refs是一个键值对(键:ref值; 值:注册refs的组件或元素)。
<div id="app">
<!-- 1.注册 -->
<child ref='c'></child>
</div>
<script>
Vue.component('child',{
template:`<div id="child"></div>`,
data(){
return{
cmsg:'子组件中的数据'
}
}
})
const vm = new Vue({
el:'#app',
mounted(){
//2.使用
//父组件可获取到子组件
console.log(this.$refs.c)
//父组件可获取到子组件中的信息
console.log(this.$refs.c.cmsg)
}
})
</script>
注意: 若想一进来就要获取$refs的值,最早也要等DOM元素渲染之后,即mounted中。
13. 路由
13.1概念
学习目的:为了更好管理单页面应用程序(SPA)
路由是浏览器URL中的哈希值,与展示视图内容之间的对应规则。
vue中的路由是由hash和component的对应关系,一个哈希值对应一个组件。
13.2基本使用
-
准备工作 (3个)
-
安装 :
npm i vue-router
-
引入 :
注意: 引入路由一定要在引入vue之后,因为vue-router是基于vue工作的<script src="./vue.js"></script> <script src="./node_modules/vue-router/dist/vue-router.js"></script>
-
实例路由对象 + 挂载到vue上
-
实例路由对象 :
const router = new VueRouter()
-
挂载到vue上 :
new Vue({ router,data,methods })
-
验证路由是否挂载成功, 就看打开页面,最后面有没有个
#/
-
-
具体步骤
- 入口
1). 手动:在地址栏手动输入(测试用) 2). 声明式导航: <router-link to="/one"></router-link> 3). 编程式导航: 跳转:this.$router.push('/one') 有记录,可返回 返回:this.$router.back() 替换:this.$router.replace('/one') 没有记录,不可返回(返回到根目录,常用于支付)
- 规则
// path : 路由路径 // component : 将来要展示的路由组件 routes: [ { path: '/one', component: One }, { path: '/two', component: Two } ]
- 组件
- 出口
<router-view />
13.3 $route 路由对象
-
一个路由对象 (route object) 表示当前激活的路由的状态信息,包含了当前 URL 解析得到的信息
-
一个哈希值路径 对应 一个路由对象
-
$route.path★
- 类型:
string
- 字符串,对应当前路由的路径,总是解析为绝对路径,如
"/detail/4"
。 # 后面?前面的内容
- 类型:
-
$route.params★
- 类型:
Object
- 一个 key/value 对象,包含了动态片段和全匹配片段,如果没有路由参数,就是一个空对象。
- 类型:
-
$route.name★
- 类型:
string
- 路由匹配规则里面的name值
- 类型:
-
$route.meta 元信息★
- 类型:
Object
- 对应路由匹配规则中的meta。
- 类型:
-
$route.query
- 类型:
Object
- 参数对象
- 一个 key/value 对象,表示 URL 查询参数。例如,对于路径
/4?age=21
,则有$route.query.age == 21
,如果没有查询参数,则是个空对象。
- 类型:
-
$route.hash
-
类型:
string
-
当前路由的 hash 值 (带
#
) ,如果没有 hash 值,则为空字符串。
-
-
$route.fullPath
- 类型:
string
- 全路径
- 完成解析后的 URL,包含查询参数和 hash 的完整路径。
- 类型:
# 演示 :
<router-link to="/detail/4?age=21#one">detail</router-link>
{ path: '/detail/:id?', component: detail ,name='detail',meta:{title:'stitch'}}
在组件内 created打印 this.$route
> fullPath: "/detail/4?id=001#one"
> hash : "#one"
> params : {id:'4'}
> query : {age : 21}
> path : '/detail/4'
> name:'detail'
> meta:{title:'stitch'}
13.4 动态路由
作用:根据不同的条件动态的去配置路由规则。在vue-router的路由路径中,可以使用动态路径参数给路径动态的传值。
动态路由在来回切换时,由于它们都是指向同一组件,Vue不会销毁再重新创建这个组件,而是复用这个组件。
<div id="app">
<!-- 1.入口 -->
<router-link to="/detail/1">手机1</router-link>
<router-link to="/detail/2">手机2</router-link>
<router-link to="/detail/3">手机3</router-link>
<!-- 4.出口 -->
<router-view ></router-view>
</div>
<script src="../node_modules/vue/dist/vue.js"></script>
<script src="../node_modules/vue-router/dist/vue-router.js"></script>
<script >
//3.组件
//获取参数的三种方式
const detail = {
// 方式1 : 组件中直接读取
template:`<div>详情页信息:{{$route.params.id}}</div>`,
// 方式2 : js直接读取
created(){
// 打印只会打印一次,因为组件是复用的,每次进来钩子函数只会执行一次
console.log(this.$route.params.id)
},
// 方式3 : 监听路由的参数。为什么不需要深度监听,因为一个路径变化,就会对应一个对新的路由对象(地址变)
watch:{
$route(to,from){
console.log(to.params.id)
}
}
}
const router = new VueRouter({
//2.匹配规则
routes : [
{path:'/detail/:id?',component:detail}
]
})
const vm = new Vue({
el:'#app',
router,
components :{detail}
})
</script>
13.5 嵌套路由
作用:嵌套路由就是在路由里面嵌套它的子路由,简而言之,就是让一个组件显示在另一个组件中。
const Parent = {
//1.在父组件中留一个出口
template:`<div>Parent <router-view /></div>`
}
const Child = {
template:`<div>Child</div>`
}
const router = new VueRouter({
routes:[{
path:'/parent',
component:Parent,
//2.子组件的路由的路由规则需放到父组件的children中
/**
* path:'/child' =>入口:'/child'
* path:'child' =>入口:'/parent/child'
*/
// children:[{path:'/child',component:Child}]
children:[{path:'child',component:Child}]
}]
})
13.5 命名路由
作用:在创建Router实例的时候,在 routes中给某个路由设置名称name值作为标识,然后就可以通过这个名称来代替path路径去指向某个路由组件
<!-- 使用 -->
<router-link :to="{name:'one'}">One</router-link>
//命名name
routes:[
{path:'/one',name:'one',component:One}
]
13.6 命名视图
作用:想同时或同级在一个页面中展示多个视图,而不是嵌套展示,则可以在页面中定义多个单独命名的视图
- 需要展示几个视图就需要指定几个出口
- 配置规则中的component改为components
- 出口
<router-view />
没添加name时,显示默认视图
routes:[
{
//1.配置名字
path:'/',components:{
h:Header,
m:Main,
//指定默认显示视图
default:Footer
}
}
]
<!-- 2.给每个出口指定名字 -->
<router-view name='h'></router-view>
<router-view name='m'></router-view>
<!-- 未指定name时,使用default(默认)视图 -->
<router-view ></router-view>
13.7 重定向
const router = new VueRouter({
routes: [
//方式1:路径
{ path: '/', redirect: '/one' },
//方式2:name
{ path: '/', redirect: { name: 'one' }},
//方式3:函数
//to:目标路由对象
{ path: '/', redirect: to => {
//return '/two'
//可根据路由中的条件,来决定重定向到哪
if(to.params.id ==1){
return {name:'one'}
}else{
return {name:'two'}
}
}}
]
})
13.8 组件传参
- 原始:
- 入口:/one/1
- 规则路径:/one/:id?
- 使用:$route.params.id
- 布尔模式:
- 入口:/one/1
- 规则路径:/one/:id?
- 规则:props:true
- 指定:props:[‘id’]
- 使用:{{id}}
- 对象模式:
- 规则路径:/one
- 规则:props:{id:‘1’}
- 指定:props:[‘id’]
- 使用:{{id}}
- 函数模式:
- 规则路径:/one
- 规则:props:to=>{return {bb:‘bbb’}}
- 指定:props:[‘id’]
- 使用:{{id}}
- 优点:可根据to返回的信息,条件显示
const One = {
//使用
template:`<div>组件One:{{id}}</div>`,
//指定
props:['id','aa','bb']
}
const router = new VueRouter({
routes:[
// 方式1:原始
{path:'/one/:id?',component:One}
// 方式2:布尔模式 将路由参数id,作为组件one的属性存在
{path:'/one/:id?',component:One,props:true}
// 方式3:对象模式
{path:'/one',component:One,props:{aa:'aaa'}}
// 方式4:函数模式
{path:'/one',component:One,props:to=>{return {bb:'bbb'}}}
]
})
13.9 导航守卫
导航守卫主要用来通过跳转或取消的方式守卫导航。这里有很多方式植入路由导航中:全局的,单个路由独享的,或者组件级的。
to: 即将要进入的目标 路由对象
from: 当前导航正要离开的路由对象
next:1).允许访问:next() 2).不允许访问:next(false) 3).跳转到其它界面访问:next(‘login’)
const isLogin = false
router.beforeEach((to,from,next)=>{
if(to.name == 'login'){
next()
}else{
// isLogin ? next():next(false)
isLogin ? next():next('/login')
}
})
全局前置路由守卫:beforeEach((to,from,next)=>{ })
全局后置路由守卫:afterEach((to,from)=>{ })
独享路由守卫:beforeEnter((to,from,next)=>{ })
组件内路由守卫:beforeRouteEnter((to,from,next)=>{ }) / beforeRouteLeave((to,from,next)=>{ }) 通过路由规则触发才有
13.10 路由组件所独有的两个生命周期钩子
activated路由组件被激活时触发。
deactivated路由组件失活时触发。
13.11 keep-alive 标签
keep-alive 是 Vue 的内置组件,当它包裹动态组件时,会缓存不活动的组件实例,而不是销毁它们。和 transition 相似,keep-alive 是一个抽象组件:它自身不会渲染成一个 DOM 元素,也不会出现在父组件链中。
14. webpack
14.1 介绍
- 概念
- 前端模块化打包(构建)工具
- WebPack可以看做是模块打包机:它做的事情是,分析你的项目结构,找到JavaScript模块、其它的一些浏览器不能直接运行的拓展语言(Scss,less等)以及新语法,并将其转换和打包为合适的格式供浏览器使用。
- ** 使用WebPack的原因**
- 浏览器不识别 SASS、Less ==> 需要对Less/SASS 预编译CSS => 供浏览器使用
- 项目中的模块化以及互相之间引用依赖包造成文件分散 ==> 需要把各个分散的模块集中打包成大文件,减少HTTP的链接的请求次数
- 打包成了大文件,体积就变大了 ==> 代码压缩
- 部分ES6语法有兼容问题 => ES5 => 浏览器使用
- …
以上这些操作以前都是需要我们手动处理,这是非常繁琐的, 这个时候webpack就可以上场了,在webpack里,只要配置好,一下就可以都搞定了
- 基本能力
- 处理依赖:方便引用第三方模块,让模块更容易复用、避免全局注入导致的冲突、避免重复加载或者加载不必要的模块
- 合并代码:把各个分散的模块集中打包成大文件,减少HTTP的链接的请求次数,优化代码的体积(压缩代码)
- 各种插件:babel 把 ES6+ 转化为 ES5- 等
- 工作原理
- 简单的说就是分析代码,找到“require”、“import”、“define”等关键词,并替换成对应模块的引用。
- 在一个配置文件中,指明对某些文件进行编译、压缩、组合等任务。把你的项目当成一个整体,通过一个给定的主文件 (index.js),webpack将从这个文件开始找到你的项目的所有的依赖文件,使用loaders处理他们,最后打包为一个浏览器可 以识别的js文件。
- 四个核心概念
- 入口(entry) : 要打包哪个文件
- 出口(output) : 要打包到哪里
- 加载器(loader) : 加载除了js文件其他文件的功能
- 插件(plugins) : 处理加载器完成不了的功能, 使用插件
14.2 使用
14.2.1 webpack 命名初始化阶段
项目名不能有汉字,不能取名叫 webpack
- 创建项目名称并生成
package.json
, 命令 :npm init -y
- 安装 :
npm i -D webpack webpack-cli
webpack : webpack 工具的核心包
webpack-cli : 提供了一些在终端中使用的命令
-D(--save-dev) : 表示项目开发期间的依赖,也就是 : 线上代码中用不到这些包了
- 创建一个
main.js
文件
console.log('要被打包了');
- 在
package.json
的scripts
中,添加脚本
// webpack 是webpack-cli 中提供的命令, 用来实现打包的
// ./main.js 入口文件,要打包哪个文件
"scripts": {
"build": "webpack main.js"
},
- 运行 :
npm run build
- 设置开发状态 :
mode
"build" : "webpack ./main.js --mode development"
如果没有设置 mode 配置项, webpack 会默认提供 生产环境(production);生产环境下, 打包生产的js文件都是压缩后的, 开发环境下代码一般是不压缩的
- 开发环境 (development) : 开发过程就是开发环境
- 生产环境 (production) : 线上环境, 也就是 : 项目做好了,发布上线
14.2.2 webpack 配置文件
14.2.2.1 webpack 打包的两种方式
- 方式 1 : 命令行
"build" : 入口 --output 出口
"build": "webpack ./src/js/main.js --output ./dist/bundle.js --mode development",
- 方式 2 : 配置项
第一步 : 项目`根目录`下, 创建一个 `webpack.config.js`文件 (文件名固定死)
第二步 : 在 `webpack.config.js` 中,进行配置
* webpack 是基于 node的 , 所以配置文件符合 node 方式书写配置
* 注意 : 不要再这个文件中使用ES6的的模块化 import语法
* main.js里可以使用,是因为要通过webpack转化为es5的
* 而这个是webpack 的配置文件,它是要转化别人的,所以必须要通过
第三步 : 修改 `package.json` 中的 `scripts` , 脚本命令为: "build": "webpack"
第四步 : 执行命令 : `npm run build`
14.2.2.2 插件 html-webpack-plugin
作用:
- 能够根据指定的模板文件 (index.html),自动生成一个新的 index.html,并且注入到dist文件夹下
- 能够自动引入js文件
安装: npm i html-webpack-plugin
配置 :
//第一步: 引入模块
const htmlWebpackPlugin = require('html-webpack-plugin')
//第二步: 配置
plugins: [
// 使用插件 指定模板
new htmlWebpackPlugin({
template: path.join(__dirname, './src/index.html')
})
]
14.2.2.3 插件 webpack-dev-server
作用 : 为使用 webpack 打包提供一个服务器环境
- 自动为我们的项目创建一个服务器
- 自动打开浏览器
- 自动监视文件变化,自动刷新浏览器…
安装: npm i -D webpack-dev-server
配置:
方式 1 : 命令行配置
-
脚本 :
"dev" : "webpack-dev-server --open --port 3001 --hot"
-
运行:
npm run dev
-
自动打开: 添加
--open
-
指定端口号 : 添加
--port 3001
-
热更新:
--hot
( 局部更新 )
方式 2 : 配置文件配置
// hot 不要写在配置文件里面,,不然的话还要配其他插件麻烦
"dev" : "webpack-dev-server --hot",
devServer : {
open : true,
port : 3001
}
14.2.2.4 示例代码(webpack.config.js)
const path = require('path')
const htmlWebpackPlugin = require('html-webpack-plugin')
module.exports = {
// 入口
entry: path.join(__dirname, './src/js/main.js'),
// 出口
output: {
// 出口目录
path: path.join(__dirname, './dist'),
filename: 'bundle.js'
},
// 开发模式
mode: 'development'
// 插件
plugins: [
// 使用htmlWebpackPlugin插件 指定模板
new htmlWebpackPlugin({
template: path.join(__dirname, './src/index.html')
})
]
// 使用webpack-dev-server插件
devServer : {
open : true,
port : 3001
}
}
14.2.3 webpack 打包上线
- 开发模式 :
npm run dev
==> 不会打包的 ,只会把项目放到服务器里 - 打包:
npm run build
对项目进行打包,生成dist文件
1 执行 : `npm run build`
2 模拟本地服务器 : 安装 : `npm i -g http-server`
3 把dist文件里的内容放到服务器里即可, 直接运行`http-server`
14.2.4webpack 打包非js文件
webpack 只能处理 js 文件,非 js(css.less.图片.字体等)处理处理不了, 借助 loader 加载器
14.2.4.1处理 css 文件
- 安装 :
npm i -D style-loader css-loader
- 在
webpack.config.js
中,添加个新的配置项module
// loader
module: {
rules: [
//1.处理 css
// 注意点 use执行loader 顺序 从右往左
// css-loader : 读取css文件内容,将其转化为一个模块
// style-loader :拿到模块, 创建一个style标签,插入页面中
{ test: /\.css$/, use: ['style-loader', 'css-loader'] }
]
}
14.2.4.2 处理 less 文件
- 安装 :
npm i -D less-loader less style-loader css-loader
- 在 webpack.config.js 中, 配置
module->rules
{ test :/\.less$/, use : ['style-loader','css-loader','less-loader'] },
14.2.4.3 处理 图片 文件
- 安装 :
npm i -D url-loader file-loader
- 在 webpack.config.js 中, 配置
module->rules
// 处理图片
{ test : /\.(jpg|png)$/, use : ['url-loader'] },
**注意:**url-loader (推荐) 和 file-loader 二选一即可
url-loader 默认会将图片转化为 base64 编码格式, 目的:提高性能,减少请求次数。
file-loader 在处理图片时, 会对文件进行重命名 :
原始: background-image: url(…/images/1.jpg);
处理后: background-image: url(9c9690b56876ea9b4d092c0baef31bb3.jpg);
base64 编码格式的图片说明 :
- 精灵图 : 将一些小图片合并为一张图片,减少请求次数,提高性能
- 字体图标 :直接将一些小的图片,合并到字体文件中,并且不会失真
- base64 : 是一种编码格式,能够将图片、文字等常见的文件,转化为 base64 格式,这种字符串格式, 浏览器能够识别并且读取显示到页面中;
- base64 是一个字符串,也可以直接被内嵌到页面中, 或者 css 中
- 注意 : 大图片不适合用 base64 处理, 只有小的图标才适合 base64 处理
设置配置:
//方式1 :
{ test : /\.(jpg|png)$/, use : ['url-loader?limit=57417'] },
//方式2 :
{
test: /\.(jpg|png)$/, use: [
{
loader: 'url-loader',
options: {
// 比57417这个小 => 转化为base64
// 大于等于这个57417值 => 不会转base64 内部调用 file-loader 加载图片
limit: 57417
}
}
]
}
14.2.4.4 处理 字体 文件
- 安装
npm i -D url-loader
- 在 webpack.config.js 中, 配置 module->rules
// 4. 处理字体图标
{ test:/\.(svg|woff|woff2|ttf|eot)$/,use:'url-loader'}
14.2.4.5 处理 ES6 语法
- 现在的项目都是使用 ES6 开发的, 但是这些新的 ES6 语法, 并不是所有的浏览器都支持, 所以就需要有一个工具,帮我们转成 es5 语法, 这个就是: babel
- babel
- Babel is a JavaScript compiler. ==> babel 是一个 JavaScript 编译器
- webpack 只能处理 import / export 这个 es6 模块化语法
而其他的 js 新语法,应该使用 babel 来处理 - 比如 :
var o = { ...obj }
在谷歌上可以,edge 就不可以 - babel 的使用 :
- 6.1 安装 1 :
npm i -D babel-core babel-loader@7
- babel-core 是 babel 的核心包
- babel-loader 加载 js 文件, 并将 js 代码内容交给 babel-core 解析为 es5 低版本的 js
- 6.2 安装 2 :
npm i -D babel-preset-env babel-preset-stage-2
- babel-preset-* 表示能够解析什么版本的 js 语法
- babel-preset-env : 表示能够解析 es2015,es2016,es2017,es2018 这些标准的语法
- babel-preset-stage-2 : 用来解析经过会遇到,但是还没有被采纳为标准的语法
- 比如 : ‘abc’.padStart(10, ‘6’) : 准备 10 位字符,有 abc,前面不够用 6 补充,是 es2017 的语法,
babel-polyfill与babel-plugin-transform-runtime
也是做兼容处理的,以前都是用这个,兼容更早的
- 6.3 在 webpack.config.js 配置
{ test: /\.js$/, use: 'babel-loader', exclude: /node_modules/ }
- 6.4 在项目根目录中创建 babel 的配置文件,叫:
.babelrc
{
"presets": [
"env",
"stage-2"
],
-----------
// 暂时不用
// 如果未来某一天真的用到了polify
"plugins": [
["transform-runtime", {
"helpers": false,
"polyfill": true,
"regenerator": true,
"moduleName": "babel-runtime"
}]
15. 单文件组件
后缀为 .vue 的文件
注意 : 单文件组件,无法直接在浏览器中使用,必须经过 webpack 这种打包工具,处理后,才能在浏览器中使用
<template>
<!-- html代码 -->
</template>
<style scoped>
/*scoped:加上之后当前组件内的样式只在当前组件生效*/
/* css代码 */
</style>
<script>
export default {
// js代码
}
</script>
16. vue-cli vue脚手架
因为 webpack 配置繁琐, 阻止一批想用 vue 但是不会 webpack 的开发人员,所以作者直接将所有 vue 项目中用到的配置全部帮你写好了,这样,就不需要开发人员再去配置基础 webpack 配置项了
- 安装:
vue版本 | 安装命令 | 初始化项目命令 |
---|---|---|
2.0 | npm i vue-cli -g | vue init webpack 项目名 |
3.0及以上 | npm i @vue/cli -g | vue create 项目名 /vue ui |
-
查看版本:
vue -V
-
安装过程:
? Project name # 回车 (项目名称)
? Project description # 回车 (项目描述)
? Author # 回车 (作者)
? Vue build standalone # => 运行时+编译 => 详见下面的问题1
? Install vue-router? # Yes (是否添加路由)
? Use ESLint to lint your code? # Yes (是否使用Eslint)
? Pick an ESLint preset Standard # standard (使用eslint的什么标准)
? Set up unit tests # No
? Setup e2e tests with Nightwatch? # No
? Should we run `npm install` for you after the project has been created? # (recommended) npm
16.1 初始化项目时的问题
问题1 : 两种编译模式 和 @
参考 : vue.js => 安装 => 对不同构建本版本的解释
- 我们选择的是 Runtime + Compiler 模式 : ( 运行时 + 编辑器)
- 运行时模式 : 用来创建 Vue 实例、渲染并处理虚拟 DOM 等的代码。基本上就是除去编译器的其它一切。
- 编译器:用来将模板字符串编译成为 JavaScript 渲染函数的代码。
// 需要编译器
new Vue({
template: '<div>{{ hi }}</div>'
})
// 不需要编译器
new Vue({
render (h) {
return h('div', this.hi)
}
})
- 完整版版本选用 : ES Module (基于构建工具使用) : vue.esm.js
- build => webpack.base.config.js => 37 行 alias(别名) ‘vue$’: ‘vue/dist/vue.esm.js’,
- @ : 就是src的绝对路径
- build => webpack.base.config.js => 38 行 alias(别名) ‘@’: resolve(‘src’),
router/index.js =>
import HelloWorld from '@/components/HelloWorld'
import HelloWorld from 'C:/users/.../src/components/HelloWorld'
问题2 : ESLint
-
概念 : ESLint 是在 JavaScript 代码中识别和报告模式匹配的工具,它的目标是保证代码的一致性和避免错误。
-
在 vscode等编辑工具 中, 可以提示语法错误
-
在许多方面,它和 JSLint、JSHint 相似,除了少数的例外:
-
-
如何使用 eslint ?
- 1-安装vscode插件 ESlint
- 2-vscode 的 settings.json文件添加一些配置
"vetur.format.defaultFormatterOptions": { "prettier": { "semi": false, //不需要分号 "singleQuote": true, //使用单引号 "wrap_attributes": "force-aligned" } }, // 从这里往下大家都给加上 "editor.formatOnSave": true, //#每次保存的时候自动格式化 "eslint.autoFixOnSave": true, // #每次保存的时候将代码按eslint格式进行修复 "eslint.validate": [ { "language": "html", "autoFix": true }, { "language": "vue", "autoFix": true }, { "language": "javascript", "autoFix": true } ], "prettier.eslintIntegration": true, // #让prettier使用eslint的代码格式进行校验 "prettier.semi": false, //#去掉代码结尾的分号 "prettier.singleQuote": true, //#使用带引号替代双引号 "javascript.format.insertSpaceBeforeFunctionParenthesis": true, "editor.formatOnType": true, //#让函数(名)和后面的括号之间加个空格 "vetur.format.defaultFormatter.html": "js-beautify-html", "vetur.format.defaultFormatter.js": "vscode-typescript"
-
关闭 Eslint :
-
参考 : config/index.js ==> 26行 :
dev.useEslint
设置为false -
重启项目:
npm run dev
-
问题3 : vscode安装 格式化插件 Prettier
-
安装 vscode 插件
Prettier - Code formatter
-
功能1 : shift + alt + F => 格式化代码
-
功能2 : 配合 eslint : 见上一个问题的配置
问题4 : eslint 检测警告
`//eslint-disable-next-line` # 忽略下一行的警告 可以使用单行注释/多行注释,其他都是多行注释
`/*eslint-disable*/` # 忽略当前整个文件的警告 需放在文件最前面
`/* eslint-disable no-new */` # 忽略前面是new开头的警告
17. cnpm 和 yarn
- npm
npm 是 Node.js 标准的软件包管理器。 - cnpm
(1) cnpm跟npm用法完全一致,只是在执行命令时将npm改为cnpm。
(2) npm安装插件是从国外服务器下载,受网络影响大,可能出现异常。 cnpm 的镜像仓库在国内(淘宝机房),速度会相对快一点。不过现在 npm 加了缓存机制,速度也跟上来了。
(3) cnpm 安装时不会产生 package-lock.json,并且项目中即使有 package-lock.json,cnpm 也是不管不顾的,只读取 package.json。
配置:npm install -g cnpm --registry=https://registry.npm.taobao.org
- yarn
在npm5.0之前,npm的下载速度较慢,Facebook就写了yarn
配置:npm i yarn -g
使用 :安装命令和npm有些不一样,yarn安装用add,例 :yarn add vue
18. Promise
- 概念: es6提出的一种重要的语法;promise对象用来封装一个异步操作并可以获取其成功/失败的结果值。
- 优点:
(1) 支持链式回调,可以解决回调低于问题
(2) 指定回调函数的方式更加灵活
基本使用:
const p = new Promise((resolve,reject)=>{
setTimeout(()=>{
// 假设操作成功 resolve => then
// resolve('成功')
// 假设操作失败 reject => catch
reject('失败')
},0)
})
p.then(res=>{
console.log('then:',res)
}).catch(err=>{
console.log('catch:',err)
})
promise封装 异步读取多个文件
const fs = require('fs')
function readFiles (filePath) {
const p = new Promise((resolve, reject) => {
fs.readFile(filePath, 'utf-8', (err, data) => {
if (err) {
reject('读取文件失败')
}
resolve(data)
})
})
return p
}
readFiles('./a.txt')
.then(res1 => {
console.log(res1)
return readFiles('./b.txt')
})
.then(res2 => {
console.log(res2)
return readFiles('./c.txt')
})
.then(res3 => {
console.log(res3)
})
18.1 async 与 await
- 作用: 用编写同步代码的形式处理异步代码
- 使用:
功能 | 格式 | |
---|---|---|
async | 修饰一个内部有异步操作的函数 | async + 异步函数 |
await | 等待一个异步处理的结果值 | await+ 异步操作(promise类型) |
- 注意点:
- await必须写在async函数中,但async函数可以没有await
- 若await的promise失败了,就会抛出异常,需要通过try…catch捕获处理
- async 要加载await 最近的函数(就近原则)
- 基本使用:
const fs = require('fs')
function readFiles (filePath) {
const p = new Promise((resolve, reject) => {
fs.readFile(filePath, 'utf-8', (err, data) => {
if (err) {
reject("读取文件失败")
}
resolve(data)
})
})
return p
}
async function fn () {
try {
let res1 = await readFiles('./a1.txt')
console.log(res1)
} catch (err) {
console.log('读取失败了')
}
let res2 = await readFiles('./b.txt')
console.log(res2)
}
fn()
18.2 resolve与reject
Promise.resolve(value=>{}) :快速返回成功的数据
Promise.regect(reason=>{}):快速返回失败的数据
18.3 all与race
Promise.all(promises=>{ }) :返回一个新的promise,只有所有的promise都成功才成功,有一个失败的promise就失败
Promise.race(promises=>{ }) :第一个完成的promise的结果状态就是最终结果状态
18.4 promise的三个状态
pending: 未完成
resolved: 异步完成成功的结果
rejected: 异步完成失败的结果
promise是微任务,执行代码是先执行宏任务再执行微任务。script与timeout是宏任务,但timeout是下一次的宏任务。
19. Vuex
19.1 Vuex的介绍
19.1.1 概念
-
状态管理工具
-
状态即数据, 状态管理就是管理组件中的data数据
-
Vuex 中的状态管理工具,采用了 集中式 方式统一管理项目中组件之间需要通讯的数据
19.1.2 使用
- 最佳实践 : 只将组件之间共享的数据放在 vuex 中, 而不是将所有的数据都放在 vuex 中 , 也就是说:如果数据只是在组件内部使用的,这个数据应该放在组件中,而不要放在 vuex
- vuex 中的数据也是响应式的,也就是说:如果一个组件中修改了 vuex 中的数据,另外一个使用的 vuex 数据的组件,就会自动更新 ( vuex 和 localstorage的区别)
19.1.3 使用范围
- 说明: 项目体量很小,不需要使用 vuex, 如果项目中组件通讯不复杂,也不需要使用 vuex
- 只有写项目的时候,发现组件通讯多,组件之间的关系复杂,项目已经无法继续开发了,此时,就应该使用 vuex
19.2 Vuex的基本使用
19.2.1 vuex的基本使用
- 安装 :
npm i vuex
- 引入 : 引入
vuex
之前一定要先引入vue
<script src="./node_modules/vuex/dist/vuex.js"></script>
- 实例化 store
- store 仓库 , 获取数据和操作数据都要经过 store
const store = new Vuex.Store()
- 操作数据
- 获取数据 :
store.state.num
- 操作数据 :
store.state.num = 300
- 虽然
store.state.count = 300
可以修改值 , 但是vuex 也有严格模式, - 添加严格模式 :
strict : true,
- 获取数据 :
- 使用 mutations
- 注册 :
mutations : {}
increament(state) { state.count = 20; }
- 默认第一个参数永远是 state
- 触发事件 :
store.commit('increament')
- 注册 :
19.2.2 vuex的传参
- 触发事件 :
// 传参最好传一个对象,多个值查看方便
store.commit('increament', {
num: 400
})
- 事件
// payload 载荷
increament(state, payload) {
state.count = payload.num
}
19.2.3 vue和vuex的配合使用
需求 : 有个h1显示数字的标题, 点击按钮累加数字
- 先用vue做出来效果
- 再用vuex和vue配合使用
- 实例化vuex的store
- 实例化vue
- 把store挂载到vue上
- 操作数据
- h1展示数据 :
<h1>{{ $store.state.num }}</h1>
- 点击触发事件修改数据 :
this.$store.commit('addNum')
addNum(state) { state.num++ }
- h1展示数据 :
19.3 核心概念
- state: 状态,相当于data
- mutations: 相当于methods,只能写同步操作,严格模式 strict:true
执行方法:this.$store.commit('方法名');
写法:参数1:state - actions: 写异步操作,监听mutations
执行方法:this.$store.dispatch('方法名');
写法:参数1:context,相当于store - getters: 相当于computed(计算属性)
- module: 分模块存储数据
19.4 常用的几个辅助函数
19.4.1 mapGetters
- 引入 :
import { mapGetters } from "vuex";
computed:{
...mapGetters(['方法名'])
}
19.4.2 mapMutations
methods:{
...mapMutations(['方法名'])
}
19.4.3 mapActions
methods:{
...mapActions(['方法名'])
}
20. 反向代理
20.1 说明
- 解决跨域问题的方式 :
- JSONP == > 只能处理 get 方式
- CORS ==> 处理自己的服务器
- 反向代理 ==> 也很常用
- 说明
- 演示跨域问题
- 反向代理的原理
- 脚手架vue-cli 生成的项目中如何使用反向代理
20.2 演示跨域问题
-
代码 :
// 演示跨域问题 /* eslint-disable */ import axios from 'axios'; axios.get('https://api.douban.com/v2/movie/in_theaters').then(res => { console.log(res) })
-
报错 :
Access to XMLHttpRequest at 'https://api.douban.com/v2/movie/in_theaters' from origin 'http://localhost:8080' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.
-
报错原因
项目运行在 http://localhost:8080 // I Your application is running here: http://localhost:8080 发送ajax请求 : //域名是 https://api.douban.com/v2/movie/in_theaters 因此出现跨域问题
20.3 反向代理的原理
-
修改
config/index.js
配置文件proxyTable: { '/myapi': { // 代理的目标服务器地址 // https://api.douban.com/v2/movie/in_theaters // /myapi/movie/in_theaters target: 'https://douban.uieee.com/v2/', pathRewrite: { '^/myapi': '' }, // 替换的请求地址 // 设置https secure: false, changeOrigin: true // 允许跨域 (必须设置该项) } },
-
最终代码
// axios.get('https://api.douban.com/v2/movie/in_theaters').then(res => { axios.get("http://localhost:8080/api/movie/in_theaters").then(res => { console.log(res); });
-
最终配置 cli2.x :
proxyTable: { '/myapi': { // 代理的目标服务器地址 // https://api.douban.com/v2/movie/in_theaters // /myapi/movie/in_theaters target: 'https://douban.uieee.com/v2/', pathRewrite: { '^/myapi': '' }, // 设置https secure: false, // 必须设置该项 changeOrigin: true } },
-
最终配置 3.X
- 根目录下 新建一个
vue.config.js
- 拷贝如下代码
module.exports = { devServer: { proxy: { '/myapi': { // 代理的目标服务器地址 // https://api.douban.com/v2/movie/in_theaters // /myapi/movie/in_theaters target: 'https://douban.uieee.com/v2/', pathRewrite: { '^/myapi': '' }, // 设置https secure: false, // 必须设置该项 changeOrigin: true } } } } // 使用 axios.get('http://localhost:8080/myapi/movie/in_theaters').then(res => { console.log(res) }) axios.get('/myapi/movie/in_theaters').then(res => { console.log(res) })
- 根目录下 新建一个
-
重新启动 :
npm run dev
(配置vue.config.js 都需要重启项目)
21.keep-alive组件
21.1作用
keep-alive是vue内置的一个组件,而这个组件的作用就是能够缓存不活动的组件,我们能够知道,一般情况下,组件进行切换的时候,默认会进行销毁,如果有需求,某个组件切换后不进行销毁,而是保存之前的状态,那么就可以利用keep-alive来实现。
21.2 属性
- include 值为字符串或者正则表达式匹配的组件name会被缓存。
- exclude 值为字符串或正则表达式匹配的组件name不会被缓存。
被缓存的路由,第一次进入页面的时候会调用 created() 方法,第二次进入页面时就不会再调用 created() 方法了,因为页面被缓存起来了。那么我们如果页面改动了,怎么刷新页面呢?虽然 created() 方法不会被调用,但是activated、deactivated这两个生命周期钩子函数会被执行。也就是我们的接口可以写在 activated() 方法中。
// 将缓存 name 为 index 或者 home 的组件,结合动态组件使用
<keep-alive include="index,home">
<router-view :key="key" />
</keep-alive>
21.3 生命周期函数
被包含在 keep-alive 中创建的组件,会多出两个生命周期的钩子: activated 与 deactivated
1. activated
在 keep-alive 组件激活时调用
该钩子函数在服务器端渲染期间不被调用
2. deactivated
在 keep-alive 组件停用时调用
该钩子函数在服务器端渲染期间不被调用