项目创建
刀刀
12/31/2024
0 字
0 分钟
搭建
创建项目
shvue create 项目名称
配置项
css预处理器、babel、router、vuex
n(哈希模式)
less
配置的模块,单独放一个文件夹
不保存预设
下载
axios
shellyarn add axios
下载
Vant
Vant
组件库配置具体的可以查看官方文档,地址:Vant2.x。
shellyarn add vant@latest-v2 -S
shellyarn add babel-plugin-import -D
jsmodule.exports = { plugins: [ ['import', { libraryName: 'vant', libraryDirectory: 'es', style: true }, 'vant'] ] };
重启项目即可查看效果。
配置
本项目是由 vue2
脚手架搭建,引用了 vant@2
组件库,主要做了以下几个方面:
Vuex
:Vuex
通过模块化管理,在大总管index.js
文件中统一引用导出。axios
:axios
二次封装,封装请求基准路径、超时时间、请求拦截器(设置请求头等)、响应拦截器(不同状态码的相应提示)等。components
:全局子组件一个个导入到main.js
文件不仅导致代码量冗余,且不利于后续维护,因此统一导入到index.js
大总管文件统一导出引用mixins
:部分功能函数多个页面需要使用,通过mixins
可以让代码复用,减少冗余,方便维护。filter
:过滤器,统一封装,main.js
文件引入使用。
vuex
getters
为了方便使用,对部分高频使用的变量通过 getters
引用:
export default {
token: (state) => state.login.token,
userinfo: (state) => state.login.userinfo,
}
封装挂载
vuex
模块化的好处是各个模块分工明确,统一管理,利于维护。先在 index.js
文件中导入相应的配置并导出,在 main.js
文件中导入使用。
import Vue from 'vue'
import Vuex from 'vuex'
import login from './modules/login'
Vue.use(Vuex)
export default new Vuex.Store({
getters,
modules: {
login,
}
})
import store from './store'
const vue = new Vue({
store,
render: h => h(App)
}).$mount('#app')
使用
可通过辅助函数 mapState
、mapAction
、mapGetters
等获取数据使用。
axios
封装挂载
在相应的 js
文件中引入 axios
并导出,并设置基准路径、超时时间以及请求头。其中基准路径和超时时间通过外部动态导入获取:
import axios from 'axios'
import { BASE_URL, TIME_OUT } from '@/config'
import store from '@/store'
import router from '@/router';
import { Toast } from 'vant';
// 创建实例后修改默认值
// 创建axios实例
let instance = axios.create({
baseURL: BASE_URL,
timeout: TIME_OUT,
headers: {
post: {
'Content-Type': 'application/x-www-form-urlencoded'
}
}
});
export default instance;
接下来设置请求拦截器和响应拦截器。
请求拦截器
请求拦截器主要封装以下几点:
- 设置请求的请求头,如部分
post
请求的请求头为application/json
,上传文件的请求头要为multipart/form-data
等。 - 携带
token
,获取token
并根据后端接口文档传给相应字段。 - (可选)显示
loading
效果,为了用户体验感更好,每次调接口时都显示loading
效果,表示正在请求中。
/**
* 请求拦截器
* 每次请求前,如果存在token则在请求头中携带token
*/
instance.interceptors.request.use(
config => {
Toast.loading({
duration: 9000,
message: '加载中...',
forbidClick: true,
});
// 判断当前post请求是否是application/json
if (config.type && config.type === 'json') {
config.headers['Content-Type'] = 'application/json';
}
if(config.url === '/zhijiao-edu/upload/save') {
config.headers['Content-Type'] = 'multipart/form-data';
}
// 每次发送请求之前判断是否存在token,如果存在,则统一在http请求的header都加上token,不用每次请求都手动添加了
// 即使本地存在token,也有可能token是过期的,所以在响应拦截器中要对返回状态进行判断
const token = store.getters.token;
token && (config.headers['X-Access-Token'] = token);
return config
},
error => {
return Promise.error(error);
}
)
响应拦截器
响应拦截器主要封装以下几点:
- 根据状态码给予页面不同的反馈。
- 200:请求成功,获取数据渲染
- 401 / 403:用户未登录或用户没有该权限,跳转到登录页面提示用户登录
- 404:接口不存在,给予提示
- 500:服务器错误,给予提示
- 关闭请求拦截器显示的
loading
效果。
// 响应拦截器
instance.interceptors.response.use(
// 请求成功
res => {
Toast.clear()
// 状态码为200
if (res.status === 200 && res.data.code === 200) {
return Promise.resolve(res.data)
} else {
errorHandle(res.data.code, res.data.msg || res.data.message);
return Promise.reject(res)
}
},
// 请求失败
error => {
Toast.clear()
const { response } = error;
if (response) {
// 请求已发出,但是不在2xx的范围
errorHandle(response.status, response.data.message);
return Promise.reject(response);
} else {
// 处理断网的情况
// eg:请求超时或断网时,更新state的network状态
// network状态在app.vue中控制着一个全局的断网提示组件的显示隐藏
// 关于断网组件中的刷新重新获取数据,会在断网组件中说明
if (!window.navigator.onLine) {
} else {
return Promise.reject(error);
}
}
}
);
提示、跳转登录、状态码判断单独抽出封装为函数,更利于后期的维护!
/**
* 提示函数
* 禁止点击蒙层、显示一秒后关闭
*/
const tip = msg => {
Toast({
message: msg,
duration: 1000,
forbidClick: true
});
}
/**
* 跳转登录页
* 携带当前页面路由,以期在登录页面完成登录后返回当前页面
*/
const toLogin = () => {
router.replace({
path: '/login'
});
}
/**
* 请求失败后的错误统一处理
* @param {Number} status 请求失败的状态码
*/
const errorHandle = (status, other) => {
// 状态码判断
switch (status) {
// 401: 未登录状态,跳转登录页
case 401:
toLogin();
break;
// 403 token过期
// 清除token并跳转登录页
case 403:
tip('登录过期,请重新登录');
localStorage.removeItem('token');
store.commit('loginSuccess', null);
setTimeout(() => {
toLogin();
}, 1000);
break;
// 404请求不存在
case 404:
tip('请求的资源不存在');
break;
// 500请求失败
case 500:
tip(other);
break;
default:
break;
}
}
使用
接口中引入封装好的 axios
,在 index.js
大总管文件中统一导入到一个对象内,导出到 main.js
入口文件。通过 prototype
挂载到 Vue
的原型上,后续可通过 this
直接获取到。
接口模块
js
文件jsimport axios from '@/utils/request' const data = { // 轮播图 bannerApi() { return axios.get('/client/banner/list') }, } export default data
JSON 格式接口发送格式
jsconst enterprise = { xxxApi(data) { return axios.post('/xxxx/xxxx/xxxx', data, {type:'json'}) }, }
大总管
index.js
文件jsimport data from './modules/data' export default { data, }
入口文件
jsimport api from './api' Vue.prototype.$api = api
后续可通过
this.$api.data
获取到data.js
内的请求。
components
子组件复用是项目代码优化中常见的形式,如何高效、低代码实现全局子组件引入也是必要的,在 index.js
大总管文件通过 install(Vue) {}
统一注册子组件并导出。入口文件引入注册即可。
index.js
通过Vue.component()
注册main.js
通过Vue.use()
注册
// 在此统一注册components的组件
import UploadImg from './modules/UploadImg.vue'
export default {
install(Vue) {
Vue.component('UploadImg', UploadImg)
}
}
// 注册自己的插件
import components from '@/components'
Vue.use(components)
拓展—— install
:
Vue有一个概念:【插件】。插件通常用来为 Vue 添加全局功能。插件的功能范围没有严格的限制——一般有下面几种:。官方文档请移步:插件 。
插件对外暴露一个 install
的方法,该方法第一个参数是 Vue
构造器,因此可通过该参数开发新的插件及全局注册组件等。
mixins
当同一种功能多个页面都需要使用时,每个页面都写一个相同的函数显然不是一个优解。把这些封装到 mixins
中,全局或局部引入该 mixins
文件,通过 mixins
实现同一种函数多页面复用,减少代码冗余和杂糅。
封装挂载
- 在
mixins/index.js
文件中导出方法事件、变量等(该文件用来全局注册,因此只存放使用频率高的事件方法) - 入口文件全局导入
export default {
data() {
return {
}
},
created() {
},
methods: {
// 本地存储
setItem(name, value) {
if (typeof value === 'object') {
localStorage.setItem(name, JSON.stringify(value))
} else {
localStorage.setItem(name, value)
}
},
// 获取本地存储
getItem(name) {
return localStorage.getItem(name)
},
// 删除本地存储
removeItem(name) {
localStorage.removeItem(name)
},
// 筛选数据字典获取数据
filterDict(arr, v) {
let obj = arr.find(item => item.value === v)
return obj ? obj.text : v
},
// 过滤时间
filterTime(times, type) {
let date = new Date(times)
const year = date.getFullYear()
const month = date.getMonth() + 1
const day = date.getDate()
const hour = date.getHours()
const minute = date.getMinutes()
const second = date.getSeconds()
switch (type) {
case 'YYYY':
return `${year}`
case 'YYYY-MM':
return `${year}-${month >= 10 ? month : '0' + month}`
case 'YYYY-MM-DD':
return `${year}-${month >= 10 ? month : '0' + month}-${day >= 10 ? day : '0' + day}`
case 'YYYY-MM-DD hh:mm':
return `${year}-${month >= 10 ? month : '0' + month}-${day >= 10 ? day : '0' + day} ${hour >= 10 ? hour : '0' + hour}:${minute >= 10 ? minute : '0' + minute}`
case 'YYYY-MM-DD hh:mm:ss':
return `${year}-${month >= 10 ? month : '0' + month}-${day >= 10 ? day : '0' + day} ${hour >= 10 ? hour : '0' + hour}:${minute >= 10 ? minute : '0' + minute}:${second >= 10 ? second : '0' + second}`
default:
return `${year}-${month >= 10 ? month : '0' + month}-${day >= 10 ? day : '0' + day}`
}
},
}
}
// 全局mixin
import mixins from '@/mixins'
Vue.mixin(mixins)
使用
本项目后台是由低代码平台 jeecg-boot
开发,有用于传参的数据字典,因此有许多页面都会用到字典翻译,在 vuex
的 getters.js
文件中获取到登录时保存的字典数组 dict
,把字典和所要翻译的变量传递过来,返回要的翻译字段即可。
<script>
import { mapGetters, mapActions } from "vuex";
export default {
computed: {
...mapGetters([
"userinfo",
"dict",
]),
},
};
</script>
<template>
<div class="type" v-if="isRole">{{ filterDict(dict.user_role, userinfo.role) }}</div>
</template>
filters
官方文档可见:过滤器 。
封装挂载
创建 filters/index.js
文件,用于存放过滤器。main.js
文件全局注册过滤器。
export function a(e) {
return e + 1
}
// 注册过滤器
import * as filters from '@/filters'
Object.keys(filters).forEach(key => {
Vue.filter(key, filters[key])
})
使用
<template>
<div class="home">
<div>{{ 'daodao' | a }}</div> // daodao1
<div>{{ b() }}</div>
</div>
</template>