自动注册组件
在 components
中创建 index.js
,用来扫描全局组件对象并自动注册:
import Vue from 'vue'
// 自动加载 global 目录下的 .js 结尾的文件
const componentsContext = require.context('./global', true, /\.js$/)
componentsContext.keys().forEach(component => {
const componentConfig = componentsContext(component)
/**
* 兼容 import export 和 require module.export 两种规范
*/
const ctrl = componentConfig.default || componentConfig
Vue.component(ctrl.name, ctrl)
})
2
3
4
5
6
7
8
9
10
11
12
13
最后在 main.js
中引入 import '@/components'
即可。
自动导入路由
import Vue from 'vue'
import Router from 'vue-router'
Vue.use(Router)
let routes = []
cosnt routeContext = require.context('./', true, /index\.js$/)
routeContext.keys().forEach(route => {
if (route.startsWith('./index')) return
const routeModule = routeContext(route)
routes = [...routes, ...(routeModule.default || routeModule)]
})
export default new Router({
mode: 'history',
base: process.env.BASE_URL,
routes: routes
})
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
配置路由权限
vue-router
提供了非常方便的钩子,可以让我们在做路由跳转的时候进行统一操作,比如常见的权限验证。
首先,需要在 src/utils
下创建 auth.js
,用于存储 token
:
import Cookies from 'js-cookie'
const TokenKey = 'project-token'
export function getToken() {
return Cookies.get(TokenKey)
}
export function setToken(token) {
return Cookies.set(TokenKey, token)
}
export function removeToken() {
return Cookies.remove(TokenKey)
}
2
3
4
5
6
7
8
9
10
11
12
13
14
在 src/utils
下创建 permission.js
:
import store from '@/store'
import router from '@/router'
import NProgress from 'nprogress'
import 'nprogress/nprogress.css'
import { getToken } from './auth'
import { Message } from 'element-ui'
// 路由白名单
const whiteList = ['/login']
router.beforeEach((to, from, next) => {
NProgress.start()
if (getToken()) {
if (to.path === '/login') {
next({
path: '/'
})
NProgress.done()
} else {
// 实时拉取用户的信息
store
.dispatch('GetUserInfo')
.then(res => {
next()
})
.catch(err => {
store.dispatch('FedLogOut').then(() => {
Message.error('拉取用户信息失败,请重新登录!' + err)
next({
path: '/'
})
})
})
}
} else {
if (whiteList.includes(to.path)) {
next()
} else {
next('/login')
NProgress.done()
}
}
})
router.afterEach(() => {
NProgress.done()
})
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
封装 Axios
在 src
目录下创建 utils
, 并创建 axios.js
用来封装 axios
:
import axios from 'axios'
const service = axios.create({
baseURL: process.env.BASE_URL,
timeout: 10000
})
// request 拦截器
service.interceptors.request.use(
config => {
const token = localStorage.getItem('token')
if (token) {
config.headers['Authorization'] = token
}
return config
},
error => {
return Promise.reject(error)
}
)
// response 拦截器
service.interceptors.response.use(
response => {
const status = response.status
if (status === 200) {
return Promise.resolve(response.data)
} else {
return Promise.reject('error')
}
},
error => {
// 断网或请求超时
if (!error.response) {
if (error.message.includes('timeout')) {
Message.error('请求超时,请检查网络是否连接正常')
} else {
Message.error('请求失败,请检查网络是否已连接')
}
return
}
const status = error.response.status
switch (status) {
// 未登录
case 401:
router.replace({
path: '/login',
query: {
redirect: router.currentRoute.fullPath
}
})
break
// 登录过期
case 403:
Message({
type: 'error',
message: '登录信息过期,请重新登录'
})
localStorage.removeItem('token')
setTimeout(() => {
router.replace({
path: '/login',
query: {
redirect: router.currentRoute.fullPath
}
})
}, 1000)
break
// 请求错误
case 404:
Message({
message: '网络请求错误',
type: 'error'
})
break
default:
Message({
message: error.response.data.message,
type: 'error'
})
}
return Promise.reject(error)
}
)
export default service
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
84
85
86
既然要使用 axios
,必不可少的需要配置环境变量以及需要请求的地址,这里可以简单的修改 poackage.json
:
"scripts": {
"dev": "vue-cli-service serve --project-mode dev",
"test": "vue-cli-service serve --project-mode test",
"pro": "vue-cli-service serve --project-mode pro",
"pre": "vue-cli-service serve --project-mode pre",
"build:dev": "vue-cli-service build --project-mode dev",
"build:test": "vue-cli-service build --project-mode test",
"build:pro": "vue-cli-service build --project-mode pro",
"build:pre": "vue-cli-service build --project-mode pre",
"build": "vue-cli-service build",
"lint": "vue-cli-service lint"
},
2
3
4
5
6
7
8
9
10
11
12
同时修改 vue.config.js
:
const path = require('path')
function resolve(dir) {
return path.join(__dirname, './', dir)
}
module.exports = {
chainWebpack: config => {
// 配置BASE_API
config.plugin('define').tap(args => {
const argv = process.argv
const mode = argv[argv.indexOf('--project-mode') + 1]
args[0]['process.env'].MODE = `"${mode}"`
args[0]['process.env'].BASE_URL = 'http://192.168.1.187:8080'
// args[0]['process.env'].BASE_URL = JSON.stringify(process.env.BASE_URL)
return args
})
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
这样的好处是方便管理、后期维护,还可以和后端的微服务对应,建立多文件存放不同模块的 api
,剩下的就是你使用到哪个 api
时,自己引入便可。
全局引用 SVG
首先在 src/components
下创建 SvgIcon.vue
:
<template>
<svg :class="svgClass" aria-hidden="true">
<use :xlink:href="iconName"></use>
</svg>
</template>
<script lang="ts">
import { Component, Vue, Prop } from 'vue-property-decorator'
@Component({
name: 'SvgIcon'
})
export default class extends Vue {
@Prop({ required: true }) private icon!: string
@Prop({ default: '' }) private className!: string
get iconName() {
return `#icon-${this.icon}`
}
get svgClass() {
if (this.className) {
return 'svg-icon ' + this.className
} else {
return 'svg-icon'
}
}
}
</script>
<style lang="scss" scoped>
.svg-icon {
overflow: hidden;
fill: currentColor;
}
</style>
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
在 src
下创建 icons
文件夹,并在内部创建 svg
文件夹用于存放 svg
图标,创建 index.js
作为入口文件:
import Vue from 'vue'
import SvgIcon from '@/components/SvgIcon.vue'
Vue.component('svg-icon', SvgIcon)
const svgContext = require.context('./svg', false, /\.svg$/)
const svgIcon = context => context.keys().map(context)
svgIcon(svgContext)
2
3
4
5
6
7
8
通过 svg-sprite-loader
对项目中使用的 svg
进行处理:
const path = require('path')
function resolve(dir) {
return path.join(__dirname, './', dir)
}
module.exports = {
chainWebpack: config => {
// 配置SVG
config.module
.rule('svg')
.exclude.add(resolve('src/icons'))
.end()
config.module
.rule('icons')
.test(/\.svg$/)
.include.add(resolve('src/icons'))
.end()
.use('svg-sprite-loader')
.loader('svg-sprite-loader')
.options({
symbolId: 'icon-[name]'
})
.end()
}
}
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
最后在 main.js
中引入 import '@/icons'
即可。
全局 Loading 效果
平时写网络请求之前一般都需要开启 loading
效果,成功之后结束 loading
效果,这就会产生大量重复代码,我们可以结合 axios
和 vuex
统一处理。
首先,在 store/modules/app.js
里写 loading
效果:
const app = {
state: {
requestLoading: 0
},
mutations: {
SET_LOADING: (state, status) => {
if (status === 0) {
state.requestLoading = 0
return
}
state.requestLoading = status ? ++state.requestLoading : --state.requestLoading
}
},
actions: {
SetLoading({ commit }, status) {
commit('SET_LOADING', status)
}
}
}
export default app
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
再来修改一下 utils/axios.js
:
import axios from 'axios'
import store from '@/store'
const service = axios.create({
baseURL: process.env.BASE_URL,
timeout: 10000
})
// request拦截器
service.interceptors.request.use(
config => {
store.dispatch('SetLoading', true)
return config
},
error => {
setTimeout(function() {
store.dispatch('SetLoading', 0)
}, 500)
Promise.reject(error)
}
)
// response拦截器
service.interceptors.response.use(
response => {
const res = response.data
store.dispatch('SetLoading', false)
return res
},
error => {
store.dispatch('SetLoading', false)
return Promise.reject(error)
}
)
export default service
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
最后在 src/components
下创建 RequestLoading.vue
组件:
<template>
<transition name="fade-transform" mode="out-in">
<div class="request-loading-component" v-if="requestLoading">
<svg-icon icon-class="loading" />
</div>
</transition>
</template>
<script>
import { mapGetters } from 'vuex'
export default {
name: 'RequestLoading',
computed: {
...mapGetters([
'requestLoading'
])
}
}
</script>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21