vue实践,全副武装你的前端项目

  1. 接口模块处理
    1.1 axios二次封装
    这里封装的依据是后台传的JWT,已封装好的请跳过。
    import axios from ‘axios’
    import router from ‘…/router’
    import {MessageBox, Message} from ‘element-ui’

let loginUrl = ‘/login’
// 根据环境切换接口地址
axios.defaults.baseURL = process.env.VUE_APP_API
axios.defaults.headers = {‘X-Requested-With’: ‘XMLHttpRequest’}
axios.defaults.timeout = 60000

// 请求拦截器
axios.interceptors.request.use(
config => {
if (router.history.current.path !== loginUrl) {
let token = window.sessionStorage.getItem(‘token’)
if (token == null) {
router.replace({path: loginUrl, query: {redirect: router.currentRoute.fullPath}})
return false
} else {
config.headers[‘Authorization’] = 'JWT ’ + token
}
}
return config
}, error => {
Message.warning(error)
return Promise.reject(error)
})
复制代码紧接着的是响应拦截器(即异常处理)
axios.interceptors.response.use(
response => {
return response.data
}, error => {
if (error.response !== undefined) {
switch (error.response.status) {
case 400:
MessageBox.alert(error.response.data)
break
case 401:
if (window.sessionStorage.getItem(‘out’) === null) {
window.sessionStorage.setItem(‘out’, 1)
MessageBox.confirm(‘会话已失效! 请重新登录’, ‘提示’, {confirmButtonText: ‘重新登录’, cancelButtonText: ‘取消’, type: ‘warning’}).then(() => {
router.replace({path: loginUrl, query: {redirect: router.currentRoute.fullPath}})
}).catch(action => {
window.sessionStorage.clear()
window.localStorage.clear()
})
}
break
case 402:
MessageBox.confirm(‘登陆超时 !’, ‘提示’, {confirmButtonText: ‘重新登录’, cancelButtonText: ‘取消’, type: ‘warning’}).then(() => {
router.replace({path: loginUrl, query: {redirect: router.currentRoute.fullPath}})
})
break
case 403:
MessageBox.alert(‘没有权限!’)
break
// …忽略
default:
MessageBox.alert(连接错误${error.response.status})
}
return Promise.resolve(error.response)
}
return Promise.resolve(error)
})
复制代码这里做的处理分别是会话已失效和登陆超时,具体的需要根据业务来作变更。
最后是导出基础请求类型封装。
export default {
get (url, param) {
if (param !== undefined) {
Object.assign(param, {_t: (new Date()).getTime()})
} else {
param = {_t: (new Date()).getTime()}
}
return axios({method: ‘get’, url, params: param})
},
// 不常更新的数据用这个
getData (url, param) {
return axios({method: ‘get’, url, params: param})
},
post (url, param, config) {
return axios.post(url, param, config)
},
put: axios.put,
_delete: axios.delete
}
复制代码其中给get请求加上时间戳参数,避免从缓存中拿数据。
除了基础请求类型,还有很多类似下载、上传这种,需要特殊的的请求头,此时可以根据自身需求进行封装。

浏览器缓存是基于url进行缓存的,如果页面允许缓存,则在一定时间内(缓存时效时间前)再次访问相同的URL,浏览器就不会再次发送请求到服务器端,而是直接从缓存中获取指定资源。

1.2 请求按模块合并

模块的请求:
import http from ‘@/utils/request’
export default {
A (param) { return http.get(’/api/’, param) },
B (param) { return http.post(’/api/’, param) }
C (param) { return http.put(’/api/’, param) },
D (param) { return http._delete(’/api/’, {data: param}) },
}
复制代码utils/api/index.js:
import http from ‘@/utils/request’
import account from ‘./account’
// 忽略…
const api = Object.assign({}, http, account, *…其它模块*)
export default api
复制代码1.3 global.js中的处理
在global.js中引入:
import Vue from ‘vue’
import api from ‘./api/index’
// 略…

const errorHandler = (error, vm) => {
console.error(vm)
console.error(error)
}

Vue.config.errorHandler = errorHandler
export default {
install (Vue) {
// 添加组件
// 添加过滤器
})
// 全局报错处理
Vue.prototype. t h r o w = ( e r r o r ) = > e r r o r H a n d l e r ( e r r o r , t h i s ) V u e . p r o t o t y p e . throw = (error) => errorHandler(error, this) Vue.prototype. throw=(error)=>errorHandler(error,this)Vue.prototype.http = api
// 其它配置
}
}
复制代码写接口的时候就可以简化为:
async getData () {
const params = {/…key : value…/}
let res = await this. h t t p . A ( p a r a m s ) r e s . c o d e = = = 4000 ? ( t h i s . a S a t a = r e s . d a t a ) : t h i s . http.A(params) res.code === 4000 ? (this.aSata = res.data) : this. http.A(params)res.code===4000?(this.aSata=res.data):this.message.warning(res.msg)
}
复制代码2. Vue组件动态注册

来自
@SHERlocked93:Vue 使用中的小技巧

我们写组件的时候通常需要引入另外的组件:





复制代码写小项目这么引入还好,但等项目一臃肿起来…啧啧。
这里是借助webpack,使用 require.context() 方法来创建自己的模块上下文,从而实现自动动态require组件。
这个方法需要3个参数:

要搜索的文件夹目录
是否还应该搜索它的子目录
一个匹配文件的正则表达式。

在你放基础组件的文件夹根目录下新建componentRegister.js:
import Vue from ‘vue’
/**

  • 首字母大写
  • @param str 字符串
  • @example heheHaha
  • @return {string} HeheHaha
    /
    function capitalizeFirstLetter (str) {
    return str.charAt(0).toUpperCase() + str.slice(1)
    }
    /
    *
  • 对符合’xx/xx.vue’组件格式的组件取组件名
  • @param str fileName
  • @example abc/bcd/def/basicTable.vue
  • @return {string} BasicTable
    */
    function validateFileName (str) {
    return /^\S+.vueKaTeX parse error: Expected 'EOF', got '&' at position 13: /.test(str) &̲& str.replace(…/, (rs, $1) => capitalizeFirstLetter(KaTeX parse error: Expected 'EOF', got '}' at position 5: 1)) }̲ const requireC…/)
    // 找到组件文件夹下以.vue命名的文件,如果文件名为index,那么取组件中的name作为注册的组件名
    requireComponent.keys().forEach(filePath => {
    const componentConfig = requireComponent(filePath)
    const fileName = validateFileName(filePath)
    const componentName = fileName.toLowerCase() === ‘index’
    ? capitalizeFirstLetter(componentConfig.default.name)
    : fileName
    Vue.component(componentName, componentConfig.default || componentConfig)
    })
    复制代码最后我们在main.js中
    import ‘components/componentRegister.js’
    我们就可以随时随地使用这些基础组件,无需手动引入了。
  1. 页面性能调试:Hiper
    我们写单页面应用,想看页面修改后性能变更其实挺繁琐的。有时想知道是「正优化」还是「负优化」只能靠手动刷新查看network。而Hiper很好解决了这一痛点(其实Hiper是后台静默运行Chromium来实现无感调试)。

Hiper官方文档

我们开发完一个项目或者给一个项目做完性能优化以后,如何来衡量这个项目的性能是否达标?
我们的常见方式是在Dev Tool中的performance和network中看数据,记录下几个关键的性能指标,然后刷新几次再看这些性能指标。
有时候我们发现,由于样本太少,受当前「网络」、「CPU」、「内存」的繁忙程度的影响很重,有时优化后的项目反而比优化前更慢。
如果有一个工具,一次性地请求N次网页,然后把各个性能指标取出来求平均值,我们就能非常准确地知道这个优化是「正优化」还是「负优化」。
并且,也可以做对比,拿到「具体优化了多少」的准确数据。这个工具就是为了解决这个痛点的。
全局安装
sudo npm install hiper -g

或者使用 yarn:

sudo yarn global add hiper

复制代码性能指标

Key
Value

DNS查询耗时
domainLookupEnd - domainLookupStart

TCP连接耗时
connectEnd - connectStart

第一个Byte到达浏览器的用时
responseStart - requestStart

页面下载耗时
responseEnd - responseStart

DOM Ready之后又继续下载资源的耗时
domComplete - domInteractive

白屏时间
domInteractive - navigationStart

DOM Ready 耗时
domContentLoadedEventEnd - navigationStart

页面加载总耗时
loadEventEnd - navigationStart

developer.mozilla.org/zh-CN/docs/…

用例配置

当我们省略协议头时,默认会在url前添加https://

最简单的用法

hiper baidu.com

如何url中含有任何参数,请使用双引号括起来

hiper “baidu.com?a=1&b=2”

加载指定页面100次

hiper -n 100 “baidu.com?a=1&b=2”

禁用缓存加载指定页面100次

hiper -n 100 “baidu.com?a=1&b=2” --no-cache

禁JavaScript加载指定页面100次

hiper -n 100 “baidu.com?a=1&b=2” --no-javascript

使用GUI形式加载指定页面100次

hiper -n 100 “baidu.com?a=1&b=2” -H false

使用指定useragent加载网页100次

hiper -n 100 “baidu.com?a=1&b=2” -u “Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3359.181 Safari/537.36”
复制代码此外,还可以配置Cookie访问
module.exports = {

cookies: [{
name: ‘token’,
value: process.env.authtoken,
domain: ‘example.com’,
path: ‘/’,
httpOnly: true
}],

}
复制代码# 载入上述配置文件(假设配置文件在/home/下)
hiper -c /home/config.json

或者你也可以使用js文件作为配置文件

hiper -c /home/config.js
复制代码4. Vue高阶组件封装
我们常用的和就是一个高阶(抽象)组件。
export default {
name: ‘keep-alive’,
abstract: true,

}
复制代码所有的高阶(抽象)组件是通过定义abstract选项来声明的。高阶(抽象)组件不渲染真实DOM。
一个常规的抽象组件是这么写的:
import { xxx } from ‘xxx’
const A = () => {

}

export default {
name: ‘xxx’,
abstract: true,
props: [’…’, ‘…’],
// 生命周期钩子函数
created () {

},

destroyed () {

},
render() {
const vnode = this.$slots.default

return vnode
},
})
复制代码4.1 防抖/节流 抽象组件
关于防抖和节流是啥就不赘述了。这里贴出组件代码:

改编自:Vue实现函数防抖组件

const throttle = function(fn, wait=50, isDebounce, ctx) {
let timer
let lastCall = 0
return function (…params) {
if (isDebounce) {
if (timer) clearTimeout(timer)
timer = setTimeout(() => {
fn.apply(ctx, params)
}, wait)
} else {
const now = new Date().getTime()
if (now - lastCall < wait) return
lastCall = now
fn.apply(ctx, params)
}
}
}

export default {
name: ‘Throttle’,
abstract: true,
props: {
time: Number,
events: String,
isDebounce: {
type: Boolean,
default: false
},
},
created () {
this.eventKeys = this.events.split(’,’)
this.originMap = {}
this.throttledMap = {}
},
render() {
const vnode = this.$slots.default[0]
this.eventKeys.forEach((key) => {
const target = vnode.data.on[key]
if (target === this.originMap[key] && this.throttledMap[key]) {
vnode.data.on[key] = this.throttledMap[key]
} else if (target) {
this.originMap[key] = target
this.throttledMap[key] = throttle(target, this.time, this.isDebounce, vnode)
vnode.data.on[key] = this.throttledMap[key]
}
})
return vnode
},
})
复制代码通过第三个参数isDebounce来控制切换防抖节流。
最后在main.js里引用:
import Throttle from ‘…/Throttle’

Vue.component(‘Throttle’, Throttle)
复制代码使用方式

实现来自:让在Vue中使用的EventBus也有生命周期

class EventBus {
constructor (vue) {
if (!this.handles) {
Object.defineProperty(this, ‘handles’, {
value: {},
enumerable: false
})
}
this.Vue = vue
// _uid和EventName的映射
this.eventMapUid = {}
}
setEventMapUid (uid, eventName) {
if (!this.eventMapUid[uid]) this.eventMapUid[uid] = []
this.eventMapUid[uid].push(eventName) // 把每个_uid订阅的事件名字push到各自uid所属的数组里
}
$on (eventName, callback, vm) {
// vm是在组件内部使用时组件当前的this用于取_uid
if (!this.handles[eventName]) this.handles[eventName] = []
this.handles[eventName].push(callback)
if (vm instanceof this.Vue) this.setEventMapUid(vm._uid, eventName)
}
$emit () {
let args = […arguments]
let eventName = args[0]
let params = args.slice(1)
if (this.handles[eventName]) {
let len = this.handles[eventName].length
for (let i = 0; i < len; i++) {
this.handles[eventName]i
}
}
}
KaTeX parse error: Expected '}', got 'EOF' at end of input: …> { this.off(event)
})
}
KaTeX parse error: Expected 'EOF', got '}' at position 58: …eventName] } }̲ // 写成Vue插件形式,直…EventBus)进行使用
let $EventBus = {}

KaTeX parse error: Expected '}', got 'EOF' at end of input: … Vue.prototype.eventBus = new EventBus(Vue)
Vue.mixin({
beforeDestroy () {
// 拦截beforeDestroy钩子自动销毁自身所有订阅的事件
this. e v e n t B u s . eventBus. eventBus.offVmEvent(this._uid)
}
})
}

export default KaTeX parse error: Expected '}', got 'EOF' at end of input: …(',') this.eventBus.$on(‘home-on’, (…args) => {
console.log('home KaTeX parse error: Expected 'EOF', got '}' at position 49: …ext = text }̲, this) // 注意第三…eventBus.$emit(‘home-on’, ‘这是home $emit参数’, ‘ee’)
}, 1000)
},
beforeDestroy () {
// 这里就不需要手动的off销毁eventBus订阅的事件了
}
复制代码6. webpack插件:真香
6.1 取代uglifyjs 的Terser Plugin
在二月初项目升级Vue-cli3时遇到了一个问题:uglifyjs不再支持webpack4.0。找了一圈,在Google搜索里查到Terser Plugin这个插件。

我主要用到了其中这几个功能:

cache,启用文件缓存。
parallel,使用多进程并行来提高构建速度。
sourceMap,将错误消息位置映射到模块(储存着位置信息)。
drop_console,打包时剔除所有的console语句
drop_debugger,打包时剔除所有的debugger语句

作为一个管小组前端的懒B,很多时候写页面会遗留console.log,影响性能。设置个drop_console就非常香。以下配置亲测有效。
const TerserPlugin = require(‘terser-webpack-plugin’)

new TerserPlugin({
cache: true,
parallel: true,
sourceMap: true, // Must be set to true if using source-maps in production
terserOptions: {
compress: {
drop_console: true,
drop_debugger: true
}
}
})

你可能感兴趣的:(IT,前端,vue,前端,项目)