Appearance
1、crypto-js 加密解密
JS
/* eslint-disable */
import CryptoJS from 'crypto-js'
const key = CryptoJS.enc.Utf8.parse('0102030405060708') //十六位十六进制数作为秘钥
const iv = CryptoJS.enc.Utf8.parse('0102030405060708') //十六位十六进制数作为秘钥偏移量
/**
* aes 解密方法
*/
function AesDecrypt(word) {
let encryptedHexStr = CryptoJS.enc.Hex.parse(word)
let srcs = CryptoJS.enc.Base64.stringify(encryptedHexStr)
let decrypt = CryptoJS.AES.decrypt(srcs, key, {
iv: iv,
mode: CryptoJS.mode.CBC,
padding: CryptoJS.pad.Pkcs7
})
let decryptedStr = decrypt.toString(CryptoJS.enc.Utf8)
return decryptedStr.toString()
}
/**
* aes 加密方法
*/
function AesEncrypt(word) {
let srcs = CryptoJS.enc.Utf8.parse(word)
let encrypted = CryptoJS.AES.encrypt(srcs, key, {
iv: iv,
mode: CryptoJS.mode.CBC,
padding: CryptoJS.pad.Pkcs7
})
return encrypted.ciphertext.toString().toUpperCase()
}
/**
* base64 加密方法
*/
function Base64Encode(val) {
let str = CryptoJS.enc.Utf8.parse(val)
let base64 = CryptoJS.enc.Base64.stringify(str)
return base64
}
/**
* base64 解密方法
*/
function Base64Decode(val) {
let words = CryptoJS.enc.Base64.parse(val)
return words.toString(CryptoJS.enc.Utf8)
}
// 暴露接口
export default {
AesEncrypt,
AesDecrypt,
Base64Encode,
Base64Decode
}
2、localstorage 存储
JS
/**
* 一个浏览器同时打开两个tab,localstorage和cookie是共享的,sessionstorage是不共享的
* sessionstorage 仅在当前网页会话下有效,关闭页面或浏览器后会被清除
* localStorage 理论上永久有效的,除非主动清除。
*/
import { validatenull } from '@/utils/validate'
const keyName = '-'
/**
* 存储localStorage
*/
export const setStorage = ({ name, content, type } = {}) => {
name = keyName + name
const obj = {
dataType: typeof content,
content: content,
type: type,
datetime: new Date().getTime()
}
if (type) sessionStorage.setItem(name, JSON.stringify(obj))
else localStorage.setItem(name, JSON.stringify(obj))
}
/**
* 获取 localStorage
*/
export const getStorage = (name) => {
name = keyName + name
let obj = sessionStorage.getItem(name)
if (validatenull(obj)) obj = localStorage.getItem(name)
if (validatenull(obj)) return
try {
obj = JSON.parse(obj)
} catch (e) {
return obj
}
let content
if (obj.dataType === 'string') {
content = obj.content
} else if (obj.dataType === 'number') {
content = Number(obj.content)
} else if (obj.dataType === 'boolean') {
content = eval(obj.content)
} else if (obj.dataType === 'object') {
content = obj.content
}
return content
}
/**
* 删除localStorage
*/
export const removeStorage = ({ name, type } = {}) => {
name = keyName + name
if (type) {
sessionStorage.removeItem(name)
} else {
localStorage.removeItem(name)
}
}
/**
* 获取全部localStorage
*/
export const getAllStorage = ({ type } = {}) => {
const list = []
if (type) {
for (let i = 0; i <= sessionStorage.length; i++) {
list.push({
name: sessionStorage.key(i),
content: getStorage({
name: sessionStorage.key(i),
type: 'session'
})
})
}
} else {
for (let i = 0; i <= localStorage.length; i++) {
list.push({
name: localStorage.key(i),
content: getStorage({
name: localStorage.key(i)
})
})
}
}
return list
}
/**
* 清空全部localStorage
*/
export const clearStorage = ({ type } = {}) => {
if (type) {
sessionStorage.clear()
} else {
localStorage.clear()
}
}
3、router 路由
js
import router from './index'
import Store from '@/store/index'
import NProgress from 'nprogress'
import 'nprogress/nprogress.css'
router.beforeEach((to, from, next) => {
NProgress.start()
// 进入404
if (from.name === '404' || to.path === '/404') next()
// 进入不存在路由
if (to.path !== '/404' && to.matched.length === 0) {
next({
path: '/404'
})
}
// 权限判断
if (to.matched.some((res) => res.meta.requireAuth)) {
// 获取用户信息
let userInfo = Store.state.stoUser
// 找不到 则local重载用户信息
if (!userInfo) {
next({
path: '/login'
})
}
// 判断进入页面是否需要权限
if (!to.meta.auth) {
next()
return
}
// 需要权限并且有权限进入
if (userInfo.vue_role?.web_auth_arr.includes(to.meta.auth)) {
next()
} else {
// 需要权限但无权限进入
next({ name: '403' })
}
} else {
next()
}
})
router.afterEach(() => {
NProgress.done()
})
4、api 请求
JS
import axios from 'axios'
import store from '@/store'
import AES from '@/utils/aesUtil'
import VueConfig from '@/config/config'
import sign from '@/utils/sign'
// 框架
import { message } from 'ant-design-vue'
let URL_PREFIX = `${process.env.VUE_APP_BASE_URL}/master_web`
// 创建实例
let instance = axios.create({
baseURL: URL_PREFIX,
timeout: 1000,
headers: { 'X-Custom-Header': 'foobar' }
})
let isRefreshing = false // 标记是否正在刷新 token
let requests = [] // 存储待重发请求的数组
// Post请求
let POST = function (url, data, options) {
let code, result
return new Promise(function (resolve, reject) {
instance
.post(url, data, options)
.then((res) => {
code = res.data.code
result = res.data.result
if (code !== 200) throw codeStatus(code, result)
resolve(result)
})
.catch((err) => {
message.error(err)
reject(err)
})
})
}
// code 详情
const codeStatus = function (code, result) {
let errInfo = '请求接口出错'
// 默认常见类型
switch (code) {
case 403:
errInfo = '无权限请求'
break
case 404:
errInfo = '找不到页面'
break
case 412:
errInfo = '请求头出错'
break
case 500:
errInfo = '服务器请求出错'
break
default:
break
}
// 重定向 错误信息
if (typeof result === 'string') {
errInfo = result
if (result.indexOf('登录') !== -1) routerRedirect('/login', 2000)
}
// 返回错误信息
return errInfo
}
// 刷新令牌
const refreshToken = async function () {
return POST('/refresh_token', {
account: store.state.stoUser?.account
})
}
// 路由重定向 跳转登录页
const routerRedirect = function (rUrl, time) {
// 跳转页面
setTimeout(() => {
router.replace({
path: rUrl
})
}, time)
}
// 添加请求拦截器
instance.interceptors.request.use(
(config) => {
// Token
config.headers.token = store.state.stoUser?.token || null
config.headers.refToken = store.state.stoUser?.refToken || null
// Sign
if (VueConfig.IsOpenSign) {
const { vueSignature, vueTimestamp, vueNonce } = sign.signatureInterface(config.data)
config.headers['vue-signature'] = vueSignature
config.headers['vue-timestamp'] = vueTimestamp
config.headers['vue-nonce'] = vueNonce
}
// AES
if (VueConfig.IsOpenAesParams) {
data = { reqAes: AES.AesEncrypt(JSON.stringify(data)) } || { reqAes: undefined }
}
return config
},
(error) => {
// 对响应错误做点什么
return Promise.reject(error)
}
)
// 添加响应拦截器
instance.interceptors.response.use(
(response) => {
if (VueConfig.IsOpenAesDec) {
if (typeof response.data.result !== 'string' || !JSON.parse(AES.AesDecrypt(response.data.result))) {
throw '尝试解密失败/数据格式不对'
}
response.data.result = JSON.parse(AES.AesDecrypt(response.data.result))
}
// 对响应数据做点什么
if (response.data?.code === 4002 && !response.config.url.includes('/refresh_token')) {
const { config } = response
if (!isRefreshing) {
isRefreshing = true
return refreshToken()
.then((res) => {
// 获取存储用户信息
let userInfo = store.state.stoUser
userInfo.token = res.token
// 更新用户token存储信息
store.dispatch('saveUser', userInfo)
config.headers.token = res.token
// token 刷新后将数组的方法重新执行
requests.forEach((cb) => cb(token))
requests = [] // 重新请求完清空
// 再次执行请求
return instance(config)
})
.catch((err) => {
console.log('抱歉,您的登录状态已失效,请重新登录!')
return Promise.reject(err)
})
.finally(() => {
isRefreshing = false
})
} else {
// 返回未执行 resolve 的 Promise
return new Promise((resolve) => {
// 用函数形式将 resolve 存入,等待刷新后再执行
requests.push((token) => {
config.headers.token = token
resolve(instance(config))
})
})
}
}
return response
},
(error) => {
return Promise.reject(error)
}
)
export { POST }
5、常用 babel 配置(包含 ES2015+(ES6+)的语法等)
babel-preset-env 是一个智能预设,允许你使用最新的 JavaScript,而无需微观管理目标环境需要哪些语法转换(以及可选的浏览器 polyfill)。这既让你的生活更轻松,也让 JavaScript 包更小!
npm install @babel/cli @babel/core @babel/preset-env --save-dev
babel.config.js 配置文件
js
module.exports = {
presets: [
[
'@babel/preset-env',
{
modules: false, // 默认auto,将此设置为 false 将保留 ES 模块。仅当你打算将原生 ES 模块发送到浏览器时才使用此选项
targets: {
// 份额大于0.25%,最新的两个版本,ie>=8 , 排除废弃版本, (83.8%)
browsers: ['>0.25%', 'last 2 versions', 'ie >= 8', 'not dead']
},
useBuiltIns: 'usage', // 按需引入必要的 polyfill 来兼容目标浏览器。
corejs: 3 // babel只转换语法,而数组的includes方法是属于ES6新增的API,所以需要corejs转换
}
]
],
plugins: []
}
6、节流防抖
节流
js
// 函数节流
var canRun = true
document.getElementById('throttle').onscroll = function () {
if (!canRun) {
//判断是否已空闲,如果在执行中,则直接return
return
}
canRun = false
setTimeout(function () {
console.log('函数节流')
canRun = true
}, 1000)
}
- 限制规定时间内只触发的次数
防抖
js
// 函数防抖
document.getElementById('debounce').onscroll = function () {
clearTimeout(timer) // 清除未执行的代码,重置回初始化状态
timer = setTimeout(function () {
console.log('函数防抖')
}, 1000)
}
- 限制规定时间内连续频繁触发
防抖、节流另一种实现方法
js
// 防抖
export const debounce = (() => {
let timer = null
return(callback, wait = 800) => {
timer && clearTimeout(timer)
timer = setTimeout(callback, wait)
}
})()
// 节流
export const throttle = (() => {
let last = 0
return(callback, wait = 800) => {
let now = + new Date()
if (now - last > wait) {
callback()
last = now
}
}
})()
// 使用示例
loadList() {
debounce(() => {
console.log('防抖,0.5s内只要最后一次触发')
}, 500)
throttle(() => {
console.log('节流,0.5s内只能触发一次')
}, 500)
}