vue 新技巧

使用 Vue2.6 提供的新 API Vue.observable 手动打造一个 Vuex(简单的 vuex 替代方案)

  1. 创建 store
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import Vue from 'vue'

// 通过Vue.observable创建一个可响应的对象
export const store = Vue.observable({
userInfo: {},
roleIds: []
})

// 定义 mutations, 修改属性
export const mutations = {
setUserInfo(userInfo) {
store.userInfo = userInfo
},
setRoleIds(roleIds) {
store.roleIds = roleIds
}
}

  1. 在组件中引用
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<template>
<div>
{{ userInfo.name }}
</div>
</template>
<script>
import { store, mutations } from '../store'
export default {
computed: {
userInfo() {
return store.userInfo
}
},
created() {
mutations.setUserInfo({
name: '子君'
})
}
}
</script>

attrs

  1. 父组件上定义的属性,样式 可以直接完全让子组件继承

  2. 子组件可以设置inheritAttrs: false 父组件中的样式 不在跟元素中继承 在你定义的 v-bind=”$attrs” 的元素中继承

按钮权限

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
app.directive('has', {
// 比如 v-has:add="'user-create'"(注意,一定要用字符串引起来,否则是个函数了,)
// el是绑定过的元素 , binding.args = 'add' binding.value = 'user-crate'
//使用 USER.VUE <el-button type="danger" v-has="'user-patch-delete'"@click="handlePatchDel">批量删除</el-button>
beforeMount: function (el, binding) {
let actionList = storage.getItem('actionList')
let value = binding.value
let hasPermission = actionList.includes(value)
if (!hasPermission) {
el.style = 'display:none'
//光隐藏不够,删了他,做个异步因为在beforeMount钩子了 他还没有渲染到DOM上
setTimeout(() => {
el.parentNode.removeChild(el)
}, 0)
}
}
})

404 页面

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
//路由配置
{
name: '404',
path: '/404',
meta: {
title: '页面不存在'
},
component: () => import('@/views/404.vue')
}


// 判断当前地址是否可以访问
function checkPermission(path) {
console.log(router.getRoutes()) //所有路由的数组
let hasPermission = router
.getRoutes()
.filter((route) => route.path == path).length
if (hasPermission) {
return true
} else {
return false
}
}
// 导航守卫
router.beforeEach((to, from, next) => {
if (checkPermission(to.path)) { //或者直接用 router.hasRoute(to.name)
document.title = to.meta.title
next()
} else {
next('/404')
}
})


storeage 封装

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
import config from '../config/index.js'

//这个项目所有的storage存储在一个命名空间对象上

export default {
getStorage() {
return JSON.parse(window.localStorage.getItem(config.namespace) || '{}')
},

setItem(key, val) {
let storage = this.getStorage()
storage[key] = val
window.localStorage.setItem(config.namespace, JSON.stringify(storage))
},

getItem(key) {
return this.getStorage()[key]
},
clearItem(key) {
let storage = this.getStorage()
delete storage[key]
window.localStorage.setItem(config.namespace, JSON.stringify(storage))
},
clearAlliItem() {
window.localStorage.clear()
}
}


config.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
const env = import.meta.env.MODE || 'production'

const envConfig = {
development: {
baseApi: '/api',
mockApi:
'https://www.fastmock.site/mock/3aa8fc6d77553c0405584d0dc88c5457/api'
},
test: {
baseApi: 'test.xxx.com',
mockApi:
'https://www.fastmock.site/mock/3aa8fc6d77553c0405584d0dc88c5457/api'
},
production: {
baseApi: 'prod.xxx.com',
mockApi: ''
}
}

export default {
env,
mock: false,
namespace: 'manager',
...envConfig[env]
}


request.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
import axios from 'axios'
import config from '../config'
import { ElMessage } from 'element-plus'
import router from '../router'
import storage from './storage'

const NETWORKERROR = '网络错误'
const TOKEN_INVALID = 'TOKEN认证失败,请重新登录'
const ERROR_OK = 200

const service = axios.create({
baseURL: config.baseApi,
timeout: 8000
})

service.interceptors.request.use((req) => {
//TOKEN判断
// const headers = req.headers
// const userInfo = storage.getItem('userInfo')
// let token = ''
// if (userInfo) {
// token = userInfo.token
// }
// if (!headers.Authorization) headers.Authorization = 'Bearer ' + token

const headers = req.headers
const { token } = storage.getItem('userInfo') || {}

if (!headers.Authorization)
headers.Authorization = 'Bearer ' + token ? token : ''

return req
})

service.interceptors.response.use((res) => {
const { code, data, msg } = res.data
if (code === ERROR_OK) {
return data
} else if (code === 500001) {
ElMessage.error(TOKEN_INVALID)
setTimeout(() => {
router.push('/login')
}, 1500)
return Promise.reject(TOKEN_INVALID)
} else {
ElMessage.error(msg || NETWORKERROR)
return Promise.reject(msg || NETWORKERROR)
}
})

export default function request(options) {
options.method = options.method || 'get'
if (options.method.toLowerCase() === 'get') {
options.params = options.data
}
//取全局的MOCK 就是config/index.js配置的mock:false
let isMock = config.mock
if (typeof options.mock !== 'undefined') {
isMock = options.mock
}

//以防万一 线上地址一定用线上接口
if (config.env === 'production') {
service.defaults.baseURL = config.baseApi
} else {
service.defaults.baseURL = isMock ? config.mockApi : config.baseApi
}

return service(options)
}

;['get', 'post', 'put', 'delete', 'patch'].forEach((item) => {
request[item] = (url, data, options) => {
return request({
url,
method: item,
data,
...options
})
}
})


将一个 prop 限制在一个类型的列表中

1
2
3
4
5
6
7
8
9
10
11
12
13
export default {
name: 'Image',
props: {
src: {
type: String,
},
style: {
type: String,
validator: s => ['square', 'rounded'].includes(s)
}
}
};

这个验证函数接受一个 prop,如果 prop 有效或无效,则返回 true 或 false。

当单单传入的 true 或 false 来控制某些条件不能满足需求时,我通常使用这个方法来做。

按钮类型或警告类型(信息、成功、危险、警告)是最常见的用法、、。颜色也是一个很好的用途。

页面加载进度条

1
2
3
4
5
6
7
8
9
10
11
import NProgress from 'nprogress';

router.beforeEach(() => {
NProgress.start();
});

router.afterEach(() => {
NProgress.done();
});


移动端 100vh 问题

  • 在移动端使用 100vh 时,发现在 Chrome、Safari 浏览器中,因为浏览器栏和一些导航栏、链接栏导致不一样的呈现:

  • 你以为的 100vh === 视口高度

  • 实际上 100vh === 视口高度 + 浏览器工具栏(地址栏等等)的高度

  • 安装 vh-check npm install vh-check --save

1
2
3
import vhCheck from 'vh-check';
vhCheck('browser-address-bar');

  • 定义一个 CSS Mixin
1
2
3
4
5
6
@mixin vh($height: 100vh) {
height: $height;
height: calc(#{$height} - var(--browser-address-bar, 0px));
}


之后就是哪里不会点哪里。