简介
-
概念
构建用户界面的前端框架
-
特性
数据驱动视图、双向数据绑定
-
核心原理
MVVM
指令
-
内容渲染
v-text
、插值表达式{{}}
、v-html
-
属性绑定
v-bind:attribute
简写为:attribute
-
事件绑定
v-bind:event
简写为@event
-
事件参数对象
-
若事件处理函数本身没有传参,则 event 事件本身会作为默认参数,传递给事件处理函数。
<template> <button @click="changeBgc">按钮变色</button> </template> <script> methods: { changeBgc(e) { e.target.style.backgroudColor = 'red' } } </script>
-
若事件处理函数需要传参,则会覆盖掉隐式的默认的 event 事件参数,需要显示的传入,传值方式为
$event
<template> <button @click="changeBgc('blue', $event)">按钮变色</button> </template> <script> methods: { changeBgc(color, e) { e.target.style.backgroudColor = color } } </script>
-
-
事件修饰符
在原生 js 中,我们会使用
event.preventDefault()
或event.stopPropagation()
来阻止默认事件的触发和传播。在 vue 中,提供了一些事件指令的修饰符,用于实现相同的功能。事件修饰符 说明 .prevent 阻止默认事件行为 .stop 阻止事件冒泡 .capture 以捕获方式触发事件 .once 绑定事件只触发一次 .self 仅当 event.target 是当前元素自身时触发 -
按键修饰符
用于判断所监听按键事件的具体按键。
<input @keyup.enter="submit"> <input @keyup.esc="clearInput">
-
-
双向绑定
v-model
用于表单数据双向绑定修饰符
修饰符 说明 .number 自动转换为 数值类型 .trim 去除首位空白字符 .lazy 懒更新,change 时而非 input 时更新 -
条件渲染
v-if
、v-else-if
、v-else
v-show
-
列表渲染
v-for
基于数组来渲染列表或其他子项-
普通
<ul> <li v-for="item in list"> {{ item.name }} </li> </ul>
-
可带第二个参数
index
<ul> <li v-for="(item, index) in items"></li> </ul>
-
使用
key
维护列表状态默认的 vue 虚拟 dom 渲染优化机制,它会尽可能复用已有元素以提高性能,但是会导致无法正确更新有状态的列表,所以,可以通过指定元素的唯一
key
属性,以便 vue 可以跟踪各节点状态。注意:key 必须唯一,只能为字符串或数字类型,不能是对象。
-
-
过滤器
过滤器本质就是一个函数,一般是用作文本格式化,提高复用性和代码的可阅读性。vue 3 已弃用。
-
私有过滤器
定义在各组件的 vue 文件的 filters 节点中
filters: { filterFunction(str, arg1, arg2, ...) { ... return newStr } }
使用时,添加管道符,可在 插值表达式 和 v-bind 指令下的参数里使用
<template> <div> {{ name | filterFunc(arg1, arg2)}} </div> </template>
-
全局过滤器
除了定义位置与私有过滤器不一样,其他都一样。它定义在 main.js 中的 Vue 类中,类似静态函数
<script> //main.js Vue.filter(filterFunc, (str, arg1, arg2, ..) => { ... return newStr }) </script> <div></div>
-
计算属性
-
声明
在
computed
的节点中,以函数的方式声明<script> export default = { name: 'MyApp', data(): { return { count: 1, price: 2.5, money: 50 } } computed: { summary() { return this.money - this.count * this.price } } } </script>
-
特性
- 计算属性本身就是一个函数,更新值的时候调用对应函数即可。
- 与普通函数的区别是它的调用时机,也就是更新调用时机:
- 初始化时
- 计算属性所依赖的 data 项发生更新时
- 除上述情况外,使用计算属性仅会使用缓存值,而不调用更新
侦听器
-
作用
监听某项数据变化,进而执行特定逻辑。常用场景:用户名合法性、唯一性校验。
-
声明
在 watch 节点下,声明与 data 中所监听项同名的监听函数,当监听值发生变化时触发函数。
export default { data(): { return { username: '', info: { age: 0, gender: true } } }, watch: { // 普通监听 username(newVal, oldVal) { if(newVal ....) { handleServie() } }, // 监听对象的某个属性 'info.age': { handler(newVal, oldVal) { ... } } } }
-
选项
-
immediate 选项
默认情况下,watch 不会在组件加载完毕后调用,添加
immediate
选项,可在组件加载后立即调用。 -
deep 选项
默认情况下,watch 在监听一个对象时,是不会监听其属性值的变化,这也是无意义的监听,只有当
deep
项为 true 时,才会监听对象属性值的变化export default { data(): { return { username: '' } }, watch: { username: { handler(newVal, oldVal) { ... }, // 组件首次加载后,立即调用 immediate: true, // 监听对象属性值 deep: true } } }
-
-
侦听器 vs 计算属性
计算属性,本质是一个属性作用,用来监听属性依赖项的更新而更新自身。
侦听器,本质是一个业务处理逻辑函数,用来对监听数据发生变化时而做出的具体业务逻辑处理。
单页面应用程序 SPA
-
特点
Single Page Application 整个 web 网站只有一个 html 页面,所有功能与交互都在其上进行。
-
优点
- 交互体验好,无白屏、无跳转
- 前后端分离更彻底,无需像传统 web 那样返回多个页面或模板
- 基于第二点,服务器只专注处理数据,无需渲染页面和组织合成逻辑,压力降低,吞吐能力增大
-
缺点
-
首屏加载慢
路由懒加载、cdn 加速、代码压缩、网络传输压缩
-
不利于 SEO
SSR 服务端渲染
-
vite & vue-cli
vue 官方提供的快速创建工程化的 SPA 项目的两种方式,对比如下
vite | vue-cli | |
---|---|---|
支持 vue 版本 | vue 3 | vue 2 和 vue 3 |
基于 webpack | 否 | 是 |
运行速度 | 快 | 较慢 |
功能完善度 | 小而巧 | 大而全 |
-
vite
-
创建项目
npm init vite-app my-app cd my-app npm install npm run dev
-
运行流程
vite 做的事就是:通过
main.js
将App.vue
渲染到index.html
的指定区域。// main.js import { createApp } from 'vue' import App from './App.vue' const app = createApp(App) app.mount('#app')
-
-
vue-cli
-
创建项目
// 安装 vue-cli npm install -g @vue/cli // 创建项目,参数初始化 vue create my-app // 命令行形式 vue ui // 可视化界面
-
vue 2.0 项目
-
main.js
基本结构
import Vue from 'vue' import App from '@/App.vue' Vue.config.productionTip = False // 类似于开发模式,在 console 面板中会有提示 new Vue({ render: h => h(App) }).$mount('#app')
-
vue-router 3.x 路由
-
创建路由
// router/index.js import Vue from 'vue' import VueRouter from 'vue-router' Vue.use(VueRouter) // 将 VueRouter 配置为 Vue 的插件 const router = VueRouter({ routes: [ { path: '/', redirect: '/movie'}, { path: '/movie', component: Movie } ] }) export default router
-
挂载路由
// main.js import router from '@/router/index.js' new Vue({ render: h => h(App), router: router }).$mount('#app')
-
-
-
vue 组件
组件构成
无需多赘述
export default = {
name: 'MyApp',
data(): {
return {
username: '',
passwd: ''
}
},
props: ['item1', 'at'],
methods: {
fuc1() {
....
}
},
}
组件注册
-
全局注册
在
main.js
中,给app
实例进行注册// main.js import { createApp } from 'vue' import App from './App.vue' import MyButton from './components/MyButton.vue' const app = createApp(App) // 注册名称可自定义,使用时必须与命名一致 app.component('my-button', MyButton) app.mount('#app')
-
局部注册
在需要使用的组件内部,
components
节点进行注册// Example.vue import MyButton from './components/MyButton.vue' export default = { components: { my-button: MyButton, // 或者命名为大驼峰命名法,该种方式在使用的时候,使用大驼峰或者短横线组件名,都是允许的 MyButton: MyButton, // 或者简写 MyButton } }
组件间样式冲突
-
问题
默认情况下,
style
节点的样式属性会对所有组件生效,因为整个 spa 程序只有单个的 html 页面。 -
添加
scoped
属性为了解决上述问题,vue 给 style 提供了
scope
d 属性,以防止组件间的样式冲突。实现原理是:自动地、给每个组件内的选择器及其所对应元素,添加一个全局唯一的属性名。 -
/deep/
样式穿透假如对 style 添加了
scoped
属性,又想让某些样式选择器对当前及其子组件生效,则需要使用到/deep/
样式穿透语法。<style lang="less" scoped> .title { color: 'blue' // 对应实际编译后的选择器为 title[data-v-asdf1] } /deep/ .title { color: 'blue' // 对应选择器为 [data-v-asdf1] title } // 在 vue3 中,/deep/ 语法改成 :deep() </style>
组件的 props
-
定义
组件本质就是一种抽象封装,如果类比函数的话,
props
就相当于函数的参数。如果要灵活复用组件,接收外界传参就是一个必不可少的设计。
vue 组件设计的传参方式是:通过在组件的
props
中声明参数变量,在使用时通过自定义属性进行传参。 -
声明
普通的
props
声明方式,就是在props
节点处指定一个 字符串变量列表。如果需要对props
属性的类型、是否允许缺省、值验证,则需要使用 对象声明 的形式。 -
props 验证
- 类型检查
- 必填项校验
- 默认值
- 自定义验证函数
export default = { props: { age: { type: Number, // 共八种类型:String Array Object Boolean Function Symbol Date required: true, default: 18, validator(value) { // bool 函数 if(value < 18) return false } } } }
组件动态样式 Class 与 Style 绑定
注意:
在 vue 中,我们不再建议通过获取元素对象,来直接操作 DOM 元素样式,包括使用 $this.refs.refName.style.transfrom = '...' 也不推荐,因为这样是直接绕过了 vue 的虚拟 DOM 渲染机制,甚至不会触发 transition 过渡动画。所以,当我们在 vue 中,有修改元素样式以触发动画的需求时,例如做一些动画时,建议通过动态的 class 属性,或者 style 属性,去修改和指定元素样式。
为实现组件的动态样式,可以使用动态的 class 属性,以及 style 属性。
-
动态绑定 class 的几种方式
v-bind
三目运算符
数组
对象
-
动态绑定 Style
只能以对象的形式,指定样式属性和值。属性的指定可以是驼峰命名,也可以是原生 css 属性名称;值可以是原生 css 值,也可以是表达式。
<template> <div :class='title'></div> <div :class='isItalic? "italic" : ""'></div> <div :class=[类名, 三目运算符, ...]></div> <div :class='clsObj'></div> <div :style={ color: 'red', fontSize: '42px', 'background-color': bgc}></div> </template> <script> export default = { data(): { return { isItalic: true, clsObj: { italic: true, bold: false }, bgc: 'gray' } } } </script>
组件的自定义事件
-
定义
在组件化开发中,组件都是高度封装抽象的,父组件一般不能也不会直接获取子组件的元素,进而去添加监听事件,这是不符合封装原则的。之前的
props
解决了组件传参的问题,那么 “自定义事件” 就是为了解决父组件对子组件内部操作信号的获取问题,因为,作为一个组件,除了能接收外部传递的数据进行展示外,还肯能发生一些交互,这些交互行为应该被父组件所获取,并进行相应的逻辑处理。所以,自定义事件也可以称作子组件向父组件释放信号。 -
声明-触发-监听
- 子组件 声明 事件
- 子组件在某种交互行为下 触发 事件(或称作释放信号)
- 父组件 监听 事件
子组件
<template> <button @click="onBtnClick">改变</button> </template> <script> export default = { name: Counter emit: ['change', 'add'] // ①声明 methods: { onBtnClick(){ this.$emit('change') // ②触发 this.$emit('add', 10) } } } </script>
父组件
<template> <counter @change='onChange' @add='onAdd'></counter> // ③监听 </template> <script> export default = { methods: { onChange(){ console.log('接收到子组件change事件') }, onAdd(count) { console.log('接收到子组件的add事件,以及参数:' + count) } } } </script>
-
传参
在子组件触发事件的位置,可以传参;同时,在父组件监听事件的处理函数可以接受参数。
-
组件的 v-model
-
说明
组件的 v-model 指令,是在 props 传参 的基础上添加 自定义事件,用于保证组件内外数据双向绑定的作用。
(个人理解,这其实是不符合封装思想的,在子组件内部修改父组件的数据,是非常不安全的。虽然实际上他不是在子组件内部修改的数据,但是这种行为机制不符合封装逻辑。)
-
使用
相较于普通的 props 传参 和 自定义事件传参,v-model 需要额外做两个地方
- 事件名称必须为
update:propsVariable
- 在父组件中使用子组件时,原来的传参形式
v-bind
改成v-model
- 事件名称必须为
-
原理
vue 3 只帮我们额外做了一件事,那就是自动监听了
update:
开头的自定义事件,并帮我们把该事件传递回来的参数,更新到了v-model
的传值对象。
子组件
<template> <P> {{ count }} </P> <button @click="onBtnClick">+1</button> </template> <script> export default = { name: Counter, props: ['count'] emit: ['update:count'] // ①声明: 特殊形式 update:props methods: { onBtnClick(){ this.$emit('update:count', this.count +1) // ②触发:正常传值 } } } </script>
父组件
<template> <counter v-model:count='totalClicks'></counter> </template> <script> data(): { return { totalClicks: 0 } } </script>
-
组件的生命周期
-
运行流程
导入 => 注册 => 内存中创建组件实例 => 将实例渲染到页面 => 切换时销毁
-
生命周期
生命周期函数 执行时机 所属阶段 执行次数 应用场景 created 创建完毕时 创建 1 发送 ajax 请求 mounted 首次渲染到页面完毕时 创建 1 操作 dom updated 每次被更新渲染完毕时 运行 0-n unmounted 组件销毁后 销毁 1
组件间数据共享
-
父子数据传递
父 => 子:属性绑定,props 接收
子 => 父:自定义事件,监听接收
父 <=> 子:v-model
-
兄弟数据传递
event bus
全局变量,依赖于 mitt,使用步骤如下:- 创建 event bus 对象
- 接收方:监听 bus 对象的自定义事件及定义数据处理函数
- 发送方:通过 bus 对象触发自定义事件及传参
// eventBus.js import mitt from 'mitt' const bus = mitt() export default bus // 数据接收方 import bus from './eventBus.js' bus.on('自定义事件名', (data) => {...}) // 数据发送方 import bus from './eventBus.js' bus.emit('自定义事件名, data')
-
祖先向子孙组件节点数据传递
provide
传递inject
接收// 祖先节点 export default = { provide: { color: this.color, height: computed(this.height) } } // 子孙节点 export default = { inject: ['color', 'height'] }
-
全局存储 vuex
ref 引用
-
是什么
在原生 js 或者 jQuery 中,我们要获取元素对象都是通过方法和选择器,而在 vue 中,无论是数据驱动视图的做法,还是组件封装的思想,我们都是有意去忽略对元素本身的属性、值获取和更新,进而更关注业务实现本身。
但有时候是无法避免的要获取元素本身,来获取更多原生的 api,且要获取到组件实例,所以 vue 使用了
ref
属性,用于选择获取元素和组件实例。 -
如何引用
-
给元素或组件添加 ref 属性
-
通过 this.$refs.refName 获取引用
<input type='text' ref='ipt'> <button @click='showInput'>展示输入框</button> methods: { showInput() { this.$refs.ipt.foucs() } }
-
-
this.$nextTick(callback) 方法
该方法,将传递进去的 callback 推迟到下一个 dom 更新周期之后执行,即 updated 之后,或者说是 updated 的最后。
动态组件
-
使用
vue 提供了一个内置组件
<component>
作为占位符,通过指定其is
属性动态的切换组件。为后续软路由的实现和逻辑做铺垫。<component is='组件名'></component> // is属性也可以通过 v-on 属性绑定,指定一个变量,进而实现动态变换 <component :is='comName'></component> data(): { return { comName: null } }
-
保持组件状态 keep-alive
默认情况下,随着动态组件的切换,旧组件的实例将会在内存中被销毁,再次切换回来也将重新创建新的组件实例并渲染至页面。
如果需要保持组件的存活状态,则需要在动态组件占位符
<component>
外层包裹<keep-alive>
组件。<keep-alive> <component :is='comName'></component> </keep-alive>
插槽
-
作用
组件作为一个高度封装的对象,使用者可以通过 props 传参的方式,更加自由的使用它。但是,仅自定义组件里的数据并不能满足多样化的需求,所以,插槽的设计,就是为了更加灵活的自定义组件的呈现形式,而无需修改组件本身。
有了它,我们能够:
- 在组件的插槽位置,插入复杂的元素、组件、数据。
- 通过擦作用于插槽传值的形式,自定义组件内数据的呈现形式。
-
基本使用 & 后备内容
-
定义
slot 标签之间的内容为后备内容,即插槽默认内容
// SubComp.vue 子组件 <template> <h1>标题</h1> <slot>正文预留插槽,此段文字也为后备内容</slot> <p>署名</p> </template>
-
使用
使用时需要在组件内, 使用 template 模板,并携带 v-slot 指令
<template> <sub-comp> <template v-slot>正文部分,将替代后备内容</template> </sub-comp> </template>
-
-
具名插槽
-
定义
在一个组件中,可能需要预留多个插槽,为区分不同插槽,需要通过指定
name
属性,来定义具名插槽 -
使用
在使用时,在组件内的插槽模板中,在
v-slot
指令后添加具名插槽名称即可<template> <sub-comp> <template v-slot:default>正文部分</template> <template v-slot:footer>页脚</template> <template #footer>v-slot 的简写形式</template> </sub-comp> </template>
-
-
作用域插槽
-
定义-传值
在定义插槽时,可能需要向外暴露数据,以求使用插槽时更好的自定义数据呈现形式,所以,可以通过在定义插槽时,添加
v-bind
属性,以实现类似 props 传参的形式供插槽使用者使用。这类具有 v-bind 属性的 slot,也称作作用域插槽。 -
使用-接收
在使用作用域插槽时,所有 v-bind 属性会被包装成一个对象,我们进需要在 v-slot 指令后,使用变量接收即可。
// SubComp.vue 子组件 <template> <table> <tr> <th>表头1</th> ... </tr> <tr v-for="item in items" :key="item.id"> <slot :lineData="item"></slot> </tr> </table> </template> // 父组件 <template> <sub-comp> <template v-slot:default="scope"> <td>{{ scope.lineData.id }}</td> <td>{{ scope.lineData.name }}</td> ... </template> </sub-comp> </template>
当然,我们也可以在接收作用域插槽的 props 时,使用解构语法。
<template> <sub-comp> <template #default="{ lineData }"> <td>{{ lineData.id }}</td> ...
-
自定义指令
-
声明
私有自定义指令的声明,是在
directives
节点下定义。全局自定义指令的声明,是在 main.js 下的 app 实例,使用
directives
方法。// 私有自定义指令,subCom.vue export default = { directives: { focus: { // mounted 仅在首次渲染到 dom 时触发 mounted(el) { el.focus() // el 为使用质量的元素 }, // updated 在每次 dom 更新时都触发 updated(el) { el.focus() } }, // 如果 mouted 和 updated 实现一样,则可以简写为如下,触发时机也是上述二者的并集 focus(el) { el.focus() } } } // 全局自定义指令,main.js ... const app = createApp(App) app.directive('focus', (el) => { mounted(el) { ... }, updated(el) { ... } })
-
生命周期函数
mounted:仅在元素首次渲染到 dom 时触发
updated:在 dom 每次更新时都触发
-
指令传参
传参:像内置指令一样,通过
=
接收参数。v-focus="'red'"
接收:在指令内部的 mounted 和 updated 函数中,默认隐式的传递了一个
el
参数,所以在第二个参数位置进行接收。<template> <input type="text" v-focus="'red'"> </template> <script> export default = { directives: { focus(el, color) { el.focus() el.style.color = color.value // 注意这里 color 不是值本身,类似一个 ref 对象,通过 .value 访问值 } } } </script>
路由
后端路由 vs 前端路由
-
后端路由
请求方式、请求地址、处理函数间的对应关系。
-
前端路由
由于 SPA 中,各个功能页面的切换是通过动态组件的切换实现的,所以就需要,将 hash 地址和组件之间对应起来,而这就是前端路由的作用。
工作方式:
点击路由链接
=> 浏览器 URL 的 Hash 值变化
=> 被监听到并查询路由对应组件
=> 渲染组件到指定占位符
vue-router
-
定义 & 安装
vue-router 是 vue 官方的路由解决方案
安装
// vue3 必须安装 vue-router 4.x版本 npm install vue-router@next -S
-
创建路由模块
-
创建路由实例对象
// router/index.js import { createRouter, createWebHashHistory } from 'vue-router' const router = new createRouter({ history: createWebHashHistory(), routes: [ { path: '/', redirect: '/movie'}, { path: '/movie', component: 'Movie'} ] }) export default router
-
在 main.js 中给 spa 实例挂载路由模块
// main.js ... import router from './router/index.js' const app = createApp(App) app.use(router) ...
-
-
嵌套路由——子路由
嵌套路由可以直接在对应的父路由下,添加
children
路由节点。const router = new createRouter({ history: createWebHashHistory(), routes: [ { path: '/', redirect: '/movie'}, { path: '/movie', component: 'Movie'}, { path: '/about', component: 'About', children: [ { path: 'tab1', component: 'Tab1'}, { path: 'tab2', component: 'Tab2'} ] } ]
-
动态路由——传参
可以在路由声明中用
:param
项,给组件传递数据。-
路由声明
const router = new createRouter({ history: createWebHashHistory(), routes: [ { path: '/', redirect: '/movie'}, // 声明一个 id 参数项 { path: '/movie/:id', component: 'Movie'}, // 允许 props 传值 { path: '/movie/:id', component: 'Movie', props: true},
-
接收
组件有两种方式接收动态路由的传参,
第一,使用
$router.params.id
访问第二,使用
props
接受参数,但在声明要令 props 值为 true
-
-
声明式导航
上面已经讲述了路由的声明,那么接下来的工作就是,如何让程序动态展示路由组件,以及如何正确传值,这就是导航。
-
占位符
<router-view>
导航需要动态展示组件的位置。
-
路由链接
<router-link>
类似
<a>
标签,点击即可跳转到响应路由 hash 地址,进而在对应的<router-view>
占位符渲染相应组件。可通过
:to
属性指定跳转路由及参数。<template> <router-link :to="'/about'">关于</router-link> <router-link :to="'/movie/' + movieId">电影</router-link> </template>
-
路由高亮
路由高亮即对点击选中的路由连接
<router-link>
组件的选择器和样式控制问题,有两种方式。-
默认的高亮 class 类
在点击路由链接后,vue 会给对应的
<router-link>
元素添加一个router-link-active
类名选择器。<style lang='less' scoped> .router-link-active { background-color: grey, font-weight: bold } </style>
-
自定义路由高亮 class 类
当然,我们也可以在路由定义处,去自定义高亮 class 类名
const router = new createRouter({ history: createWebHashHistory(), linkActiveClass: 'router-active' routes: [ { path: '/', redirect: '/movie'}, ...
-
-
-
编程式导航
相较于声明式导航使用路由链接进行导航,编程式导航调用 api 实现导航,类似于原生 js 中的
location.href
函数。常用的编程式导航如下:
-
$router.push()
跳转到指定 hash 地址
<button @click='gotoMovie(3)'>跳转 Movie 3</button> methods: { gotoMovei(id) { this.$router.push('/movie/${id}') } }
-
$router.go()
表示前进,后退,常用的是
$router.go(-1)
表示返回上一页
-
-
命名路由
当嵌套路由过长时,可以使用
name
属性给路由规则命名,命名路由的name
属性必须唯一。// 命名路由 const router = createRouter({ history: createWebHistory(), routes: [ { path: '/movie/:id', name: 'mov', component: Movie, props: true } ] }) // 声明式导航 <router-link :to="{ name: 'mov', params: { id: 3} }">跳转到电影</router-link> // 编程式导航 <button @click="gotoMovie(3)">跳转</button> methods: { gotoMovie(id) { this.$router.push({ name: 'mov', params: { id: 3} }) } }
-
导航守卫
导航守卫是用来控制路由的访问权限的。
-
定义导航守卫
router 对象的
beforeEach()
函数接收一个函数对象参数,每次拦截到路由请求都会调用其中函数。同时,该参数函数对象,含有三个形参
to
:目标路由对象,即 routes 数组中的元素对象。from
:离开的路由next
:放行函数,如果该形参缺省,则表示都方向。否则,只有调用了next()
才能放行。 -
next 函数的三种常用法
next()
直接放行next(false)
禁止放行,停留在当前页面next('/login')
强制路由重定向 -
案例-验证 token 并放行或拒绝访问
const router = createRouter({...}) router.beforeEach((to, from, next) => { const token = localStorage.getItem('token') if (to.path === 'manage' && !verify(token)) { next('/login') } else { next() } })
-
axios 配置和拦截器
-
全局导入,指定别名
$http
-
配置 baseURL
vue 3
// main.js import { createAPP } from 'vue' import App from './App.vue' import axios from 'axios' const app = createAPP(App) app.config.globalProperties.$http = axios axios.defaults.baseURL = 'https://example.api.com' app.mount('#app')
vue 2
import Vue from 'vue' import App from './App.vue' import axios from 'axios' Vue.prototype.$http = axios axios.defaults.baseURL = 'https://api.example.com' new Vue({ render: h => h(App) }).$mount('#app')
-
拦截器
在每次发起 ajax 请求和接收响应时自动触发,常用的场景就是 token 验证和 loading 效果
-
请求拦截器
axios.interceptors.request.use(成功的回调, 失败的回调)
// main.js axios.interceptors.request.use(config => { // 成功开始请求的回调 ... return config; }, error => { return Promise.reject(error) })
-
响应拦截器
axios.interceptors.response.use(成功的回调, 失败的回调)
// main.js axios.interceptors.resopnse.use(response => { // 成功响应的回调 ... return reponse }, error => { return Promise.reject(error) })
-
场景演示
// main.js import loading from 'element-ui' let loadingInstance = null axios.interceptors.request.use(config => { // 1. 添加 token 请求头 config.headers.Authorization = 'Bearer xxx' // 2. 添加 loading 展示效果,在响应拦截器一同演示 loadingInstance = loading.service({ fullscreen: true }) return config; }) axios.interceptors.resopnse.use(response => { // 响应成功,关闭 loading 展示效果 loadingInstance.close() return reponse })
-
-
跨域代理 proxy
域是同源策略的反面,同源策略就是同协议、同域名、同端口,是浏览器的一个安全行为策略,不符合同源策略的 ajax 请求,将会发送跨域请求,浏览器根据服务器响应头中的跨域资源共享 (CORS) 规则而决定是否允许发送跨域请求。
为解决开发时,本地运行的项目域名和生产环境 api 的接口域名端口不一致,导致的跨域请求限制问题 ,vue 提供了一个内置代理,会自动的将本地项目地址 localhost 不具有的 api 通过代理转发到真实的 api 地址,从而绕过了前端浏览器中的同源策略。(可能用到了请求拦截器)
-
配置跨域代理的真实地址
// vue.config.js module.exports = { devServer: { proxy: 'https://api.real.com' } } // main.js 与此同时将 aixos 的 baseURL 修改为本地项目地址 axios.defaults.baseURL = 'http://localhost:8080'
-
组件库 element-ui
组件库的使用查看文档即可,主要关注以下几个点
-
完整引入
-
按需引入
-
对组件库导入的封装
可以将组件库的导入代码,从
main.js
中抽离出来,单独封装到element-ui/index.js
中,使用时导入即可。// element-ui/index.js import Vue from 'vue' import { Button, Input } from 'element-ui' ... Vue.use(Button) Vue.use(Input) // main.js import './element-ui'