创建项目:使用 Vue CLI 创建项目
加入Git版本管理
使用TS开发Vue项目-总结创建组件的方式
个人建议:No Class APIs,只用 Options APIs
Class 语法仅仅是一种写法而已,最终还是要转换为普通的组建数据结构
装饰器语法还没有正式定稿发布,建议了解即可,正式发布以后在选择使用也可以。
使用 Options APIs 最好使用 export default Vue.extend({ ... }) 而不是 export default { ... }
代码格式规范
介绍
如何自定义代码格式校验规范
开启分号的校验规则
选用的框架: element-ui
// vue.config.js
module.exports = {
css: {
loaderOptions: {
// 默认情况下 `sass` 选项会同时对 `sass` 和 `scss` 语法同时生效
// 因为 `scss` 语法在内部也是由 sass-loader 处理的
// 但是在配置 `prependData` 选项的时候
// `scss` 语法会要求语句结尾必须有分号,`sass` 则要求必须没有分号
// 在这种情况下,我们可以使用 `scss` 选项,对 `scss` 语法进行单独配置
scss: {
prependData: `@import "~@/styles/variables.scss";`
}
}
}
}
接口处理
// vue.config.js
module.exports = {
css: {
loaderOptions: {
// 默认情况下 `sass` 选项会同时对 `sass` 和 `scss` 语法同时生效
// 因为 `scss` 语法在内部也是由 sass-loader 处理的
// 但是在配置 `prependData` 选项的时候
// `scss` 语法会要求语句结尾必须有分号,`sass` 则要求必须没有分号
// 在这种情况下,我们可以使用 `scss` 选项,对 `scss` 语法进行单独配置
scss: {
prependData: `@import "~@/styles/variables.scss";`
}
}
},
devServer: {
proxy: {
'/boss': {
target: 'http://eduboss.lagou.com',
changeOrigin: true // 把请求头中的 host 配置为 target
},
'/front': {
target: 'http://edufront.lagou.com',
changeOrigin: true
}
}
}
}
import axios from 'axios'
// 创建axios实例
const service = axios.create({
})
export default service
初始化路由组件
import Vue from 'vue'
import VueRouter, { RouteConfig } from 'vue-router'
import Layout from '@/layout/index.vue'
Vue.use(VueRouter)
// 路由配置规则
const routes: Array = [
{
path: '/login',
name: 'login',
component: () => import(/* webpackChunkName: 'login' */ '@/views/login/index.vue')
},
{
path: '/',
component: Layout,
children: [
{
path: '', // 默认子路由
name: 'home',
component: () => import(/* webpackChunkName: 'home' */ '@/views/home/index.vue')
},
{
path: '/role',
name: 'role',
component: () => import(/* webpackChunkName: 'role' */ '@/views/role/index.vue')
},
{
path: '/menu',
name: 'menu',
component: () => import(/* webpackChunkName: 'menu' */ '@/views/menu/index.vue')
},
{
path: '/resource',
name: 'resource',
component: () => import(/* webpackChunkName: 'resource' */ '@/views/resource/index.vue')
},
{
path: '/course',
name: 'course',
component: () => import(/* webpackChunkName: 'course' */ '@/views/course/index.vue')
},
{
path: '/user',
name: 'user',
component: () => import(/* webpackChunkName: 'user' */ '@/views/user/index.vue')
},
{
path: '/advert',
name: 'advert',
component: () => import(/* webpackChunkName: 'advert' */ '@/views/advert/index.vue')
},
{
path: '/advert-space',
name: 'advert-space',
component: () => import(/* webpackChunkName: 'advert-space' */ '@/views/advert-space/index.vue')
}
]
},
{
path: '*',
name: '404',
component: () => import(/* webpackChunkName: '404' */ '@/views/error-page/404.vue')
}
]
const router = new VueRouter({
routes
})
export default router
接口测试
封装请求模块
import axios from 'axios'
import store from '@/store'
import { Message } from 'element-ui'
import router from '@/router'
import qs from 'qs'
const request = axios.create({
// 配置选项
// baseURL,
// timeout
})
function redirectLogin () {
router.push({
name: 'login',
query: {
redirect: router.currentRoute.fullPath
}
})
}
function refreshToken () {
return axios.create()({
method: 'POST',
url: '/front/user/refresh_token',
data: qs.stringify({
// refresh_token 只能使用1次
refreshtoken: store.state.user.refresh_token
})
})
}
// 请求拦截器
request.interceptors.request.use(function (config) {
// 我们就在这里通过改写 config 配置信息来实现业务功能的统一处理
const { user } = store.state
if (user && user.access_token) {
config.headers.Authorization = user.access_token
}
// 注意:这里一定要返回 config,否则请求就发不出去了
return config
}, function (error) {
// Do something with request error
return Promise.reject(error)
})
// 响应拦截器
let isRfreshing = false // 控制刷新 token 的状态
let requests: any[] = [] // 存储刷新 token 期间过来的 401 请求
request.interceptors.response.use(function (response) { // 状态码为 2xx 都会进入这里
// console.log('请求响应成功了 => ', response)
// 如果是自定义错误状态码,错误处理就写到这里
return response
}, async function (error) { // 超出 2xx 状态码都都执行这里
// console.log('请求响应失败了 => ', error)
// 如果是使用的 HTTP 状态码,错误处理就写到这里
// console.dir(error)
if (error.response) { // 请求发出去收到响应了,但是状态码超出了 2xx 范围
const { status } = error.response
if (status === 400) {
Message.error('请求参数错误')
} else if (status === 401) {
// token 无效(没有提供 token、token 是无效的、token 过期了)
// 如果有 refresh_token 则尝试使用 refresh_token 获取新的 access_token
if (!store.state.user) {
redirectLogin()
return Promise.reject(error)
}
// 刷新 token
if (!isRfreshing) {
isRfreshing = true // 开启刷新状态
// 尝试刷新获取新的 token
return refreshToken().then(res => {
if (!res.data.success) {
throw new Error('刷新 Token 失败')
}
// 刷新 token 成功了
store.commit('setUser', res.data.content)
// 把 requests 队列中的请求重新发出去
requests.forEach(cb => cb())
// 重置 requests 数组
requests = []
return request(error.config)
}).catch(err => {
console.log(err)
store.commit('setUser', null)
redirectLogin()
return Promise.reject(error)
}).finally(() => {
isRfreshing = false // 重置刷新状态
})
}
// 刷新状态下,把请求挂起放到 requests 数组中
return new Promise(resolve => {
requests.push(() => {
resolve(request(error.config))
})
})
} else if (status === 403) {
Message.error('没有权限,请联系管理员')
} else if (status === 404) {
Message.error('请求资源不存在')
} else if (status >= 500) {
Message.error('服务端错误,请联系管理员')
}
} else if (error.request) { // 请求发出去没有收到响应
Message.error('请求超时,请刷新重试')
} else { // 在设置请求时发生了一些事情,触发了一个错误
Message.error(`请求失败:${error.message}`)
}
// 把请求失败的错误对象继续抛出,扔给上一个调用者
return Promise.reject(error)
})
export default request
模块开发思路
发布部署
const express = require('express')
const app = express()
const path = require('path')
const createProxyMiddleware = require('http-proxy-middleware')
// 托管了 dist 目录,当访问 / 的时候,默认会返回托管目录中的 index.html 文件
app.use(express.static(path.join(__dirname, '../dist')))
app.use('/boss', createProxyMiddleware({
target: 'http://eduboss.lagou.com',
changeOrigin: true
}))
app.use('/front', createProxyMiddleware({
target: 'http://edufront.lagou.com',
changeOrigin: true
}))
app.listen(3000, () => {
console.log('running...')
})
源码下载