源码
【复习本篇笔记时一边看源码】
一、Vue前导
Vue的优点:
- 减少dom操作,性能更好
- 视图、数据分离
- 维护成本低
点击该pdf查看详情
二、数据绑定与视图渲染
使用“Mustache”语法 (双花括号)插值表达式渲染视图。
数据要先存在,才能实现数据绑定
在下图所示中,可以通过vm.name
的方式进行数据的获取和修改,数据修改的同时,视图也随之改变,如本例中的name
的值由jimmy变成了tony,视图立即随之改变。但是当vm.obj.xx=12
时,虽然vm.obj.xx
成功创建了,但是视图没有随之渲染。
因为在 Vue 中,数据要先存在,才能实现数据绑定,从而渲染视图
通过索引的方式修改数组,或通过修改数组长度的方式修改数组,都无法重新渲染视图
数组变异方法
发现使用数组方法时,就会触发视图重新渲染。
Vue的数据绑定是通过数据劫持来实现的,在数据劫持的时候通过索引修改数组或者通过修改数组长度来修改数组是无法被感知的,Vue在设计的时候通过修改数组的一些方法,在这些方法里面添加了一个更新视图的方法的执行,所以当我们调用这些数组的方法的时候可以触发更新视图方法,从而更新视图。这些数组的方法在vue中被称为数组变异方法。这些方法已经不是原生的 js 方法了,是被 Vue 改写了,让它能有一个重新渲染视图的功能。
Vue 有以下7中变异方法
push()
往数组最后面添加一个元素,成功返回当前数组的长度pop()
删除数组的最后一个元素,成功返回删除元素的值shift()
删除数组的第一个元素,成功返回删除元素的值unshift()
往数组最前面添加一个元素,成功返回当前数组的长度splice()
有三个参数,第一个是想要删除的元素的下标(必选),第二个是想要删除的个数(必选), 第三个是删除后想要在原位置替换的值(可选)sort()
使数组按照字符编码默认从小到大排序,成功返回排序后的数组reverse()
将数组倒序,成功返回倒序后的数组
$set方法
$set方法可以解决对象中未存在数据的数据绑定问题。
可以实现视图的实时更新。
$el
Vue实例vm中有一个 $el 属性,它能指向挂载 Vue 的Dom结构。
上图一共修改了 name 三次,修改了 age 两次,最后一行代码是打印 $el.innerText
,但是这五次的数据修改是异步的,结果就是是先打印了一开始的 $el.innerText
:
这样导致我们无法打印获取到数据更改后的$el.innerText
,如何解决,看下面的 $nextTick
$nextTick
它会在数据都修改完成并且重新渲染到页面上之后才执行里面的函数。这样就能获取到数据更改后的dom。
$mount
创建 Vue 实例的时候可以不写 el: '#app'
, 使用 $mount
也能实现同样的效果。
$refs
在元素标签上使用ref
来标记,然后通过$refs
来获取到标记的引用:
注意的是,不能重复标记相同名称,相同名称的 ref 只会覆盖为最后一个:
但是,如果是用 v-for 的方式来循环标记,则不会发生覆盖,会得到一个数组:
ref 除了可以标记 dom 元素,还可以标记组件。如果在普通的 DOM 元素上使用,引用指向的就是 DOM 元素;如果用在子组件上,引用就指向组件实例。下文讲组件传值时会讲到。
三、Vue 指令
v-pre、v-cloak和v-once指令
v-pre指令
在模板中跳过vue的编译,直接输出原始值。就是在标签中加入v-pre就不会输出vue中的data值了。
1 | <div v-pre>{{message}}</div> |
这时并不会输出我们的message值,而是直接在网页中显示
v-cloak指令
在vue渲染完指定的整个DOM后才进行显示。它必须和CSS样式一起使用,
1 | [v-cloak] { |
v-once指令
在第一次DOM时进行渲染,渲染完成后视为静态内容,跳出以后的渲染过程。
其他常用指令
还有 v-html、v-text、v-if、v-else-if、v-else、v-show等,具体见旧版笔记。
xss攻击
讲到 v-html 的时候,提到了XSS攻击,了解一下。
https://github.com/lynnic26/LynnNote/issues/1
v-bind和v-on
<img v-bind:src="imgUrl" alt="">
中的v-bind
可以简写为<img :src="imgUrl" alt="">
绑定class
使用 :class 来绑定类名:
1
2
3
4
5<style>
.red {
color: red
}
</style>1
2
3
4
5
6
7
8
9
10
11
12
13
14<div id="app">
<p :class='redClass'>peace!</p>
</div>
<script>
var vm = new Vue({
data: {
redClass: 'red',
}
})
vm.$mount('#app')
</script>这样 p 标签就添加上了一个名为 red 的类了。
结合数组方式绑定类名:
如果想要给一个标签绑定多个类名,
正确方式是使用【数组】:
1
2
3<div id="app">
<p :class="[redClass, bigClass]">peace!</p>
</div>
还可以在数组中进行判断运算:
结合对象绑定class:
也可以这样:
绑定style
将样式写进一个对象花括号里面,注意键值对中的值要用引号包起来。
1 | <p :style="{width:'100px',height:'200px',backgroundColor:'red'}"></p> |
也可以在data中写样式对象,然后用:style
绑定,这样比较清晰:
如果想要给一个标签绑定上多个样式对象,需要使用数组:
注意一点是在一个标签内部,style
的权重低于:style
的权重,先执行style的样式,然后再执行:style
的样式,遇到相同的backgroundColor:style
的样式会覆盖style
的。
p 的背景颜色为red
v-for
v-model
实现双向数据绑定
可以实现 v-model 的表单元素:
- input text
- input checkbox
- input radio
- textarea
- select
【这部分看一下源码】
四、自定义键盘修饰符
2.x中自定义键盘修饰符
- 通过
Vue.config.keyCodes.名称 = 按键值
来自定义案件修饰符的别名:
1 | Vue.config.keyCodes.f2 = 113; |
- 使用自定义的按键修饰符:
1 | <input type="text" v-model="name" @keyup.f2="add"> |
五、directive自定义指令
- 自定义全局和局部的 自定义指令:
1 | // 自定义全局指令 v-focus,为绑定的元素自动获取焦点: |
- 自定义指令的使用方式:
1 | <input type="text" v-model="searchName" v-focus v-color="'red'" v-font-weight="900"> |
【渡一案例见源码和视频】
F:\渡一前端课\新录制的vue课程资料\9. directive\vue.html
六、filter过滤器
概念:Vue.js 允许你自定义过滤器,可被用作一些常见的文本格式化。过滤器可以用在两个地方:mustache 插值和 v-bind 表达式。过滤器应该被添加在 JavaScript 表达式的尾部,由“管道”符指示;
私有过滤器
- HTML元素:
1 | <td>{{item.ctime | dataFormat('yyyy-mm-dd')}}</td> |
- 私有
filters
定义方式:
1 | filters: { // 私有局部过滤器,只能在 当前 VM 对象所控制的 View 区域进行使用 |
使用ES6中的字符串新方法 String.prototype.padStart(maxLength, fillString=’’) 或 String.prototype.padEnd(maxLength, fillString=’’)来填充字符串;
全局过滤器
1 | // 定义一个全局过滤器 |
注意:当有局部和全局两个名称相同的过滤器时候,会以就近原则进行调用,即:局部过滤器优先于全局过滤器被调用!
【渡一案例见源码和视频】
F:\渡一前端课\新录制的vue课程资料\10. filter\vue.html
七、el、 template、render
挂载过程:
【渡一案例见源码和视频】
F:\渡一前端课\新录制的vue课程资料\11. el、template、render\vue.html
八、vue实例的生命周期
- 什么是生命周期:从Vue实例创建、运行、到销毁期间,总是伴随着各种各样的事件,这些事件,统称为生命周期!
- 生命周期钩子:就是生命周期事件的别名而已;
- 生命周期钩子 = 生命周期函数 = 生命周期事件
主要的生命周期函数分类:
创建期间的生命周期函数:
beforeCreate:实例刚在内存中被创建出来,此时,还没有初始化好 data 和 methods 属性
- created:实例已经在内存中创建OK,此时 data 和 methods 已经创建OK,此时还没有开始 编译模板
- beforeMount:此时已经完成了模板的编译,但是还没有挂载到页面中
mounted:此时,已经将编译好的模板,挂载到了页面指定的容器中显示
运行期间的生命周期函数:
beforeUpdate:状态更新之前执行此函数, 此时 data 中的状态值是最新的,但是界面上显示的 数据还是旧的,因为此时还没有开始重新渲染DOM节点
updated:实例更新完毕之后调用此函数,此时 data 中的状态值 和 界面上显示的数据,都已经完成了更新,界面已经被重新渲染好了!
销毁期间的生命周期函数:
beforeDestroy:实例销毁之前调用。在这一步,实例仍然完全可用。
- destroyed:Vue 实例销毁后调用。调用后,Vue 实例指示的所有东西都会解绑定,所有的事件监听器会被移除,所有的子实例也会被销毁。
九、计算属性computed和监听器watch
模板内的表达式非常便利,但是设计它们的初衷是用于简单运算的。在模板中放入太多的逻辑会让模板过重且难以维护。
当我想要拼接name,looks,和age时,使用模板表达式很麻烦。直接写入describe属性中有显得代码亢余。
1 | <div id="dv"> |
1 | <script> |
效果:
以上三种方法都可以,但是并不灵活。
应该用methods中的方法来实现:
1 | <div id="dv"> |
1 | <script> |
函数返回了 name和age这两个属性,并且渲染到页面
如此相比直接用模板语法来拼接确实简洁很多。
【这里需要知道】
在控制台上修改了这两个属性的其中一个之后,函数会被再执行一次,比如我在控制台输入vm.name = "hh"
后, 页面上的dunteng 18
会立即变成hh 18
并再打印一次console语句。简而言之就是,当视图重新渲染了就会再执行一次该函数。另外当我们修改与describe方法无关的变量且该变量没有被渲染到试图上如height时,不会触发describe方法,但是一旦这个与describe方法无关的变量是有渲染到视图里的,则照样触发describe方法。
因此我们修改属性影响视图渲染的同时,会涉及到重新加载无关的函数的问题,如此对性能十分不友好。
解决方法一:watch
1 | <div id="dv"> |
1 | <script> |
这个方式虽然解决了问题,但是并不方便,代码冗余。
解决方法二: computed
这是最优的解决方法。
1 | <div id="dv"> |
1 | <script> |
computed里面除了可以像上面那样写成方法(实际上并不是方法)的样子,还可以写成对象:
1 | <div id="dv"> |
1 | <script> |
【注意】:上述例子中,当vm.name
和vm.age
修改了的时候,因为有computed监听,所以在修改的同时页面的内容也相应地修改了。但是当vm.describe
整个被修改的时候,页面时没有同步渲染更新的,所以还要如下操作:
1 | <div id="dv"> |
1 | <script> |
新版渡一代码:F:\渡一前端课\新录制的vue课程资料\13. 计算属性和侦听器\vue.html
思否文章:https://segmentfault.com/a/1190000012948175
十、组件
全局组件
这样注册了一个全局组件,名为lin-dunteng,使用时直接标签化。
也可以写成小驼峰式:linDunteng,标签化为<lin-Dunteng><lin-Dunteng>
或者<lin-dunteng></lin-dunteng>
,但是不能写成<linDunteng></linDunteng>
也可以写成大驼峰式:LinDunteng,标签化为<lin-Dunteng><lin-Dunteng>
或者<lin-dunteng></lin-dunteng>
或者<Lin-dunteng></Lin-dunteng>
或者<Lin-Dunteng></Lin-Dunteng>
, 但是不能写成<LinDunteng></LinDunteng>
【提醒】不过以上情况是针对.html文件而言的,之后在.vue文件中就没这么多禁忌,相对比较自由宽泛。还有一点就是在.html文件中最好使用双标签,使用单标签可能会有些bug,但是在.vue文件中就没事
【问题】:为什么组件中的data要通过函数返回对象?
答:当一个组件被定义, data 必须声明为返回一个初始数据对象的函数,因为组件可能被用来创建多个实例。如果 data 仍然是一个纯粹的对象,则所有的实例将共享引用同一个数据对象!通过提供 data 函数,每次创建一个新实例后,我们能够调用 data 函数,从而返回初始数据的一个全新副本数据对象。
其实也很好理解,大概意思就是:
类比与引用数据类型。如果不用function return 每个组件的data都是内存的同一个地址,那一个数据改变其他也改变了,这当然就不是我们想要的。用function return 其实就相当于申明了新的变量,相互独立,自然就不会有这样的问题;js在赋值object对象时,是直接一个相同的内存地址。所以为了每个组件的data独立,采用了这种方式。
如果不是组件的话,正常data的写法可以直接写一个对象,比如data:{ msg : ‘ 下载 ‘ },但由于组件是会在多个地方引用
的,JS中直接共享对象会造成引用传递,也就是说修改了msg后所有按钮的msg都会跟着修改,所以这里用function来每次返回一个对象实例。
局部组件
该局部组件只能在相应的el中使用
动态组件和插槽 v-slot
动态组件
关键为<component :is="comName"></component>
和<keep-alive></keep-alive>
插槽
【匿名插槽】
【具名插槽】
【作用域插槽】
详见F:\渡一前端课\0我的学习\VUE\上课学习代码\Vue全家桶一\09动态组件与插槽\作用域插槽01.html
和F:\渡一前端课\0我的学习\VUE\上课学习代码\Vue全家桶一\09动态组件与插槽\作用域插槽02.html
组件传值
利用属性的方式传值
组件传递普通的值
组件传递data中的值,父组件传值给子组件
以上的props
都是一个数组的形式,它还可以写成一个对象的形式,而且有多个属性,看下面的例子 :
数组和对象这两个引用类型,default需要写成一个函数的形式。
【一个小练习】:
F:\渡一前端课\新录制的vue课程资料\15. 组件数据传递&属性校验
多层父传子通信
【场景】: 父组件要传值给子组件和孙子组件(子组件的子组件)
####【方案一】两层的父传子通信。
【方案二】利用 $attrs 来传值
vm.$attrs 包含是组件中未被props注册的属性
$attrs–继承所有的父组件属性(除了prop注册传递的属性、class 和 style ),一般用在子组件的子元素上。
在本例中 <my-p :content='$attrs.content'></my-p>
得到应用。
【注意】如果$attrs继承了很多的属性,那么像上栗这样子一个一个在子组件标签中列出来很麻烦,可以直接使用
v-bind='$attrs'
来一次性全部拿到$attrs中的全部属性。
但是我们发现,在html结构中,被props注册传递的属性不会出现,而未被props注册传递的属性会出现,如下图所示:
如何去除呢?
使用inheritAttrs:false
!
inheritAttrs:默认值true,继承所有的父组件属性(除props的特定绑定)作为普通的HTML特性应用在子组件的根元素上,如果你不希望组件的根元素继承特性设置inheritAttrs: false,但是class属性会继承(简单的说,inheritAttrs:true 继承除props之外的所有属性;inheritAttrs:false 只继承class属性)
像这样子写之后,就大功告成了。
【方案三】利用 $parent
对于$children,是一个数组,因为可以有多个子组件。
但是这种方法官方不推荐使用,像孙子组件myP中这样多个$parent写起来很不友好。
【方案四】利用provide和inject
在组件中inject拿到的属性和数据是来自provide的,而非data中的。
虽然很方便却也是不太推荐使用的, 容易使得代码混乱,不知道属性是从何而来的。但是必须知道,面试的时候可以装逼。
子组件向父组件传值
【方案一】利用 $children
但是这种方法官方不推荐使用。
【方案二】 利用 $refs
通过 $refs 获取到了子组件的数据和方法。
####【方案三】$emit
这是比较常用的方法
效果如下:
兄弟组件之间的传值
利用事件总线EventBus
【具体实现方式】:
1 | var Eventbus=new Vue(); |
组件双向数据通信
暂未整理
十一、Vue-cli 脚手架
安装初试
npm install -g @vue/cli 安装脚手架,用于生成项目,使用指令vue --version
查看是否安装成功
npm install -g @vue/cli-service-global 快速原型开发,可以直接通过vue serve xxx.vue
的指令来编译.vue文件
如果之前已经安装过旧版本(非3.x)脚手架,需先卸载旧版本:
npm uninstall vue-cli -g
如果仍然需要使用旧版本的 vue init 功能,可以全局安装一个桥接工具:
npm install -g @vue/cli-init 拉取旧版本
插件名字:Vetur(由于vscode并不认识.vue文件,所以没有代码高亮,需要安装Vetur这个插件来弥补
vue create vue-app
创建一个vue项目文件夹,叫做vue-app,【注意这里的命令操作最好用cmd执行,git bash有些操作完成不了】。会提示选择一个预设的方案,有default和manually selectfeature,选择后者。然后会有一些配置需要你选择,按空格键进行选择和取消选择,这里只选择Babel。再然后选择In package.json
选项,选
- ★ 同时注意使用
redirect
来重定向到具体的一个子路由路径上,否则一打开“community”路由的时候没有默认的选中子路由,只会显示空白不会显示出具体的子路由页面。
在一级路由.vue文件中继续使用
<router-link>
和<router-view>
关于首页路径和
router-link-active
的优化在本例子中路由community的样式是加在
router-link-extra-active
上的,且匹配的是路径/community
,因此路由community的子路由被点击时community的样式没有显现,因为此时的路径变成了/community/xxx
没有完全匹配/community
。因此要把样式加到
router-link-active
上。但是问题又来了,router-link-active
匹配了两个路径/
和/community
这样就应该把原本首页匹配的路径由根路径
/
换成/home
。然后又有一个问题,就是我们访问http://localhost:8080
的时候无法自动跳转到首页。所以还需要进行一步重定向:或者还可以这样子使用redirect ( )重定向:
不存在的路径和优化
比如不存在该路径
http://localhost:8080/gjalhj
,对此要将这一切不存在路径同意跳转到某一指定页面这里的
path: *
表示当以上的所有路径都没有匹配的时候,判断路径是否为/
,是则重定向到/home
,否则重定向到统一的指定页面。
$router的push、replace和go
this.$router.push()
描述:跳转到不同的url,但这个方法回向history栈添加一个记录,点击后退会返回到上一个页面。
用法:
this.$router.replace()
描述:同样是跳转到指定的url,但是这个方法不会向history里面添加新的记录,点击返回,会跳转到上上一个页面。上一个记录是不存在的。
this.$router.go(n)
相对于当前页面向前或向后跳转多少个页面,类似 window.history.go(n)。n可为正数可为负数。正数返回上一个页面
-
导航守卫和动态路由
【重要的一节课】F:\渡一前端课\新录制的vue课程资料\28. 导航守卫&动态路由
动态路由
【方案一】使用params:
【方案二】使用query:
组件内的守卫
你可以在路由组件内直接定义以下路由导航守卫:
beforeRouteEnter
beforeRouteUpdate
(2.2 新增)beforeRouteLeave
1 | const Foo = { |
beforeRouteEnter
守卫 不能 访问 this
,因为守卫在导航确认前被调用,因此即将登场的新组件还没被创建。
不过,你可以通过传一个回调给 next
来访问组件实例。在导航被确认的时候执行回调,并且把组件实例作为回调方法的参数。
1 | beforeRouteEnter (to, from, next) { |
注意 beforeRouteEnter
是支持给 next
传递回调的唯一守卫。对于 beforeRouteUpdate
和 beforeRouteLeave
来说,this
已经可用了,所以不支持传递回调,因为没有必要了。
1 | beforeRouteUpdate (to, from, next) { |
这个离开守卫通常用来禁止用户在还未保存修改前突然离开。该导航可以通过 next(false)
来取消。
1 | beforeRouteLeave (to, from , next) { |
【案例】实现一个功能
这就要用到导航守卫之beforeRouterLeave
相应的还有beforeRouteEnter :
beforeRouteUpdate:
★ 这个看视频【F:\渡一前端课\新录制的vue课程资料\28. 导航守卫&动态路由】
路由独享守卫
全局前置守卫
你可以使用 router.beforeEach
注册一个全局前置守卫:
1 | const router = new VueRouter({ ... }) |
当一个导航触发时,全局前置守卫按照创建顺序调用。守卫是异步解析执行,此时导航在所有守卫 resolve 完之前一直处于 等待中。
每个守卫方法接收三个参数:
- to: Route: 即将要进入的目标 路由对象
- from: Route: 当前导航正要离开的路由
- next: Function: 一定要调用该方法来 resolve 这个钩子。执行效果依赖
next
方法的调用参数。- next(): 进行管道中的下一个钩子。如果全部钩子执行完了,则导航的状态就是 confirmed (确认的)。
- next(false): 中断当前的导航。如果浏览器的 URL 改变了 (可能是用户手动或者浏览器后退按钮),那么 URL 地址会重置到
from
路由对应的地址。 - next(‘/‘) 或者 next({ path: ‘/‘ }): 跳转到一个不同的地址。当前的导航被中断,然后进行一个新的导航。你可以向
next
传递任意位置对象,且允许设置诸如replace: true
、name: 'home'
之类的选项以及任何用在router-link
的to
prop 或router.push
中的选项。 - next(error): (2.4.0+) 如果传入
next
的参数是一个Error
实例,则导航会被终止且该错误会被传递给router.onError()
注册过的回调。
确保要调用 next 方法,否则钩子就不会被 resolved。
全局解析守卫
2.5.0 新增
在 2.5.0+ 你可以用 router.beforeResolve
注册一个全局守卫。这和 router.beforeEach
类似,区别是在导航被确认之前,同时在所有组件内守卫和异步路由组件被解析之后,解析守卫就被调用。
全局后置钩子
你也可以注册全局后置钩子,然而和守卫不同的是,这些钩子不会接受 next
函数也不会改变导航本身:
1 | router.afterEach((to, from) => { |
导航守卫小汇总
- 全局守卫
beforeEach
beforeResolve
afterEach
- 路由独享守卫
- beforeEnter
- 组件内守卫
beforeRouteLeave 当离开这个路径时执行
beforeRouteEnter
beforeRouteUpdate mounted
进入某一个路径时,执行顺序
- beforeEach -> beforeEnter -> beforeRouteEnter -> beforeResolve -> afterEach
完整的导航解析流程
- 导航被触发。
- 在失活的组件里调用离开守卫。
- 调用全局的
beforeEach
守卫。 - 在重用的组件里调用
beforeRouteUpdate
守卫 (2.2+)。 - 在路由配置里调用
beforeEnter
。 - 解析异步路由组件。
- 在被激活的组件里调用
beforeRouteEnter
。 - 调用全局的
beforeResolve
守卫 (2.5+)。 - 导航被确认。
- 调用全局的
afterEach
钩子。 - 触发 DOM 更新。
- 用创建好的实例调用
beforeRouteEnter
守卫中传给next
的回调函数。
路由元信息
定义路由的时候可以配置 meta
字段:
1 | const router = new VueRouter({ |
那么如何访问这个 meta
字段呢?
首先,我们称呼 routes
配置中的每个路由对象为 路由记录。路由记录可以是嵌套的,因此,当一个路由匹配成功后,他可能匹配多个路由记录
例如,根据上面的路由配置,/foo/bar
这个 URL 将会匹配父路由记录以及子路由记录。
一个路由匹配到的所有路由记录会暴露为 $route
对象 (还有在导航守卫中的路由对象) 的 $route.matched
数组。因此,我们需要遍历 $route.matched
来检查路由记录中的 meta
字段。
下面例子展示在全局导航守卫中检查元字段:
1 | router.beforeEach((to, from, next) => { |
【案例】实现用户登录后才有查看内容权限,仍然基于上面的程序,要求用户登录后才可以查看“学员展示”和”社区“
★★★ F:\渡一前端课\新录制的vue课程资料\30. 路由元信息
十三、Vuex
state、getters、mutations、actions
详见我的完整笔记:
vuex module
【F:\渡一前端课\新录制的vue课程资料\33. vuex mutation&action\src】
注意到在此,使用的 vuex state都是存放关于 “学员展示” 模块的,一旦其他板块如 “课程学习” “社区” 等也需要用到 vuex , 那么一大堆的数据都堆积到state中,这样就很庞大很杂乱,不好维护和管理,因此需要分模块用到 vuex module。
★★★★★ 看代码【F:\渡一前端课\新录制的vue课程资料\34. vuex module\src】
modules
根据功能让vuex分出模块
state会放入到每一个模块下,getters、mutations、actions会直接放入到全局
获取vuex中的数据(无namespaced)
获取state : this.$store.state.moduleName.xxx
获取getters: this.$store.getters.xxx
获取mutations: this.$store.commit(‘xxx’)
获取actions: this.$store.dispatch(‘xxx’)
可以通过mapXXX 方式拿到getters、mutations、action,但是不能拿到state,如果想通过这种方式获取state,需要加命名空间:namespaced:true
获取vuex中的数据(有namespaced)
获取state : this.$store.state.moduleName.xxx
获取getters: this.$store[‘moduleName/getters’].xxx
获取mutations: this.$store.commit(‘moduleName/xxx’)
获取actions: this.$store.dispatch(‘moduleName/xxx’)
可以通过mapXXX: mapXXX(‘moduleName’, [‘xxx’]) mapXXX(‘moduleName’, {})
十四、跨域
十五、网络请求axios
参考:
axios 是 Vue 作者尤雨溪推荐的官方 ajax 库。有一下主要功能特点:
- 在浏览器中发送 XMLHttpRequest 请求
- 在 node.js 中发送 http 请求
- 支持 Promise API
- 拦截请求和响应
- 转化请求和响应数据
先安装:
1 | npm install axios --save |
再引入:
1 | import axios from 'axios' |
发送请求:
1 | axios({ |
以上没有指定 GET 请求还是 POST 请求,只传了一个 url ,默认为 GET 请求。
Example
执行 GET
请求
1 | // 为给定 ID 的 user 创建请求 |
执行 POST
请求
1 | axios.post('/user', { |
执行多个并发请求
1 | function getUserAccount() { |
全局配置
看下面的例子
这里面的 baseURL 和 timeout 存在冗余,可以将其再全局配置中写好。事实上,在开发中可能很多参数都是固定的,这时候我们可以进行一些抽取,也可以利用axios的全局配置。
类似的还有
1 | axios.defaults.headers.post['Content-Type']='application/x-www-form-urlencoded' |
还有其他配置选项,我就不一一列出来了。
axios 的实例使用
像上面的几个例子都是直接使用 axios ,但是有时候这样子直接使用 axios 是不合适的。
比如,我们的项目并非只是在 baseURL=“http://aaaa:8000”,而是还有多个 baseURL,那么这时候如果直接用 axios 来配置 baseURL 和其他的相关配置,就实现不了了,所以可以使用 axios 的实例,使用实例就可以避免这样的问题。
1 | // 创建一个axios的实例 instance1 |
axios 封装代码
假设项目中有十处组件要用到 axios 来请求数据,那么我们可能需要引入 axios 十次,如果有五十次要用到就要引入五十次。万一某天 axios 挂掉了或者不再维护了,那么我们的项目就要大改了,每次用到 axios 的地方都要重新编写,五十次就够呛了,大型项目的话直接扑街。
所以在实际开发中,一定要有意识的封装 axios 代码,比如我们在 src/network/request.js
中对 axios 进行了封装,对外导出实例request
,在进行数据请求的时候就可以使用封装好的request
而不是直接使用 axios,这样一来,如果以后要更换技术或者修改,就不用每处地方都进行修改,只要该封装代码request
就可以了。
下面开始进行封装:
level 1
1 | // src/network/request.js |
调用:
1 | import { request } from './network/request' |
level 2
1 | import axios from 'axios' |
调用:
1 | import { request } from './network/request' |
level 3
Promise化:
1 | import axios from 'axios' |
调用:
1 | import { request } from './network/request' |
level 4
上面封装成 Promise 的版本还不是最好的,事实上instance(config)
所返回的就是一个 Promise,因此可以不必再去封装成 Promise ,直接将 instance(config)
返回出来即可:
1 | import axios from 'axios' |
调用:
1 | import { request } from './network/request' |
拦截器
在请求或响应被 then
或 catch
处理前拦截它们。
一般有以下业务场景:
- 当config中的一些信息不符合服务器要求,可以在发送请求之前拦截之并进行修改
- 每次发送网络请求时,在界面中触发加载动画,响应完成时关闭加载动画
- 某些网络请求(比如登录的token),必须携带一些特殊的信息
- 等等等
1 | // 添加请求拦截器 |
如果你想在稍后移除拦截器,可以这样:
1 | var myInterceptor = axios.interceptors.request.use(function () {/*...*/}); |
可以为自定义 axios 实例添加拦截器
1 | var instance = axios.create(); |
1 | import axios from 'axios' |