Vue.js + Vuex + TypeScript 实战项目开发与项目优化

一、shim-vue.d.ts、shims-tsx.d.ts作用

Vue.js + Vuex + TypeScript 实战项目开发与项目优化_第1张图片
shim-vue.d.ts的作用

// ts是识别不了以 .vue结尾的文件 比如:import xxx from 'xxx.vue'

declare module '*.vue' {
  import Vue from 'vue'
  export default Vue
}

Vue.js + Vuex + TypeScript 实战项目开发与项目优化_第2张图片
shims-tsx.d.ts文件作用

// 使用jsx 补充一些类型声明

import Vue, { VNode } from 'vue'

declare global {
  namespace JSX {
    // tslint:disable no-empty-interface
    interface Element extends VNode {}
    // tslint:disable no-empty-interface
    interface ElementClass extends Vue {}
    interface IntrinsicElements {
      [elem: string]: any;
    }
  }
}

二、OptionsAPI、ClassAPIs

https://cn.vuejs.org/v2/guide/typescript.html#ad

OptionsAPI

如果您在声明组件时更喜欢基于类的 API,则可以使用官方维护的 vue-class-component 装饰器:

import Vue from 'vue'
const Component = Vue.extend({
  // 类型推断已启用
  data() {
    a: 1
  },
  methods: {
     test() {}
  }
})

const Component = {
  // 这里不会有类型推断,
  // 因为 TypeScript 不能确认这是 Vue 组件的选项
}

ClassAPIs

import Vue from 'vue'
import Component from 'vue-class-component'

// @Component 修饰符注明了此类为一个 Vue 组件
@Component({
  // 所有的组件选项都可以放在这里
  template: ''
})
export default class MyComponent extends Vue {
  // 初始数据可以直接声明为实例的 property
  message: string = 'Hello!'

  // 组件方法也可以直接声明为实例的方法
  onClick (): void {
    window.alert(this.message)
  } 
}

三、把样式组件注入到全局

Vue.js + Vuex + TypeScript 实战项目开发与项目优化_第3张图片
Vue.js + Vuex + TypeScript 实战项目开发与项目优化_第4张图片

项目中需要用到样式变量,为了避免每个组件都去引入,我们可以把样式组件注入到全局。

https://cli.vuejs.org/zh/guide/css.html#css-modules

vue.config.js

// 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.js + Vuex + TypeScript 实战项目开发与项目优化_第5张图片
try { // 捕获异常
} catch(err) {
console.log(err)
}

五、路由守卫-身份认证

https://router.vuejs.org/zh/guide/advanced/meta.html

router.beforeEach((to, from, next) => {
  // to.matched 是一个数组(匹配到是路由记录)
  if (to.matched.some(record => record.meta.requiresAuth)) {  // vue文档
    if (!store.state.user) {
      // 跳转到登录页面
      next({
        name: 'login',
        query: { // 通过 url 传递查询字符串参数
          redirect: to.fullPath // 把登录成功需要返回的页面告诉登录页面
        }
      })
    } else {
      next() // 允许通过
    }
  } else {
    next() // 允许通过
  }

  // // 路由守卫中一定要调用 next,否则页码无法展示
  // next()
  // if (to.path !== '/login') {
  //   // 校验登录状态
  // }
})

六、登录成功跳转回原来页面

当token失效的时候,会返回登录页,并且路径会拼接redirect参数 值是之前页面路由做了编码

在这里插入图片描述
login.vue

Vue.js + Vuex + TypeScript 实战项目开发与项目优化_第6张图片
Vue.js + Vuex + TypeScript 实战项目开发与项目优化_第7张图片
.native的作用是穿透组件,将事件传递给根元素。

七、处理token过期时间比较短的问题,避免频繁登录

有得项目为了安全,token过期时间设置得比较短,可能几分钟,那为了避免频繁登录,我们该如何处理?

有两种方式
Vue.js + Vuex + TypeScript 实战项目开发与项目优化_第8张图片
我们采用第二种方式

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({
  // 配置选项
  // timeout
})

function redirectLogin () {
  router.push({
    name: 'login',
    query: {
      redirect: router.currentRoute.fullPath // 返回到原页面
    }
  })
}

function refreshToken () {
  return axios.create()({ // 再次创建一个axios
    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) // 这里很重要,拿到重新获得的token之后,要把失败的请求重新发送出去
        }).catch(err => {
          console.log(err)
          Message.warning('登录已过期,请重新登录')
          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

return request(error.config)

error.config,本次失败的请求

Vue.js + Vuex + TypeScript 实战项目开发与项目优化_第9张图片
处理token过期-关于多次请求的问题

如果在token过期的情况下发起多次请求

在这里插入图片描述
Vue.js + Vuex + TypeScript 实战项目开发与项目优化_第10张图片
获取token只会成功一次,第二次会失败,那如何避免调用第二次?

这里 使用到了开关,用了一个flag去判断

Vue.js + Vuex + TypeScript 实战项目开发与项目优化_第11张图片

怎样把漏掉的请求发出去

在这里插入图片描述


数据加载期间 接口禁用

Vue.js + Vuex + TypeScript 实战项目开发与项目优化_第12张图片

八、修改element-ui样式

在这里插入图片描述

九、阿里云视频点播服务

十、打包后本地预览

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...')
})

你可能感兴趣的:(typescript,vue.js,javascript)