Pull the tenth bullet in the notes of high paid employment course at the front end (module 3-6)

This module mainly explains some technical points encountered in the actual combat of Vue, and explains how to use Vue for development in the actual combat through the actual operation of a course management project.

Using TypeScript in Vue projects

There are two ways to use ts in Vue projects. One is to use TypeScript in new projects; 2, Use TypeScript in an existing Vue project.

Use TypeScript in new projects
Use the @ vue/cli tool to create. Select ts. There will be two TS files in the created project.

The shims-ts.d.ts file is the type supplement of jsx syntax

/**
 * Jsx Type declaration supplement
 */
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
    }
  }
}

shims-vue.d.ts is Type declaration of Vue file

/**
 * import xx from 'xxx.vue'
 * ts Unrecognized vue file
 * Pass this statement Vue modules are Vue
 */
declare module '*.vue' {
  import Vue from 'vue'
  export default Vue
}

Use TypeScript in an existing project
Just use @ vue/cli to install the ts plug-in.

vue add @vue/typescript

TypeScript mode definition vue component

TypeScript defines vue components in two ways:

  1. Using Vue Component or Vue Extend defines components, that is, components are defined in the way of OptionApi.

    import Vue from 'vue'
    
    export default Vue.extend({
      name: 'App'
    })
    
  2. Use the Vue class component decorator. Decorator syntax has not been finalized and is not stable. It is not recommended to use in generation.

    import Vue from 'vue'
    import Component from 'vue-class-component'
    
    @Component({
      name: 'App' // Option parameters
    })
    export default class App extends Vue {
      // The initial data can be directly declared as the property of the instance
      message: string = 'Hello!'
    
      // Component methods can also be declared directly as instance methods
      onClick (): void {
        window.alert(this.message)
      }
    }
    

Using elementUI in Vue projects

To use the elementUI in a Vue project, simply install it and then import it.

There are two situations for the introduction of elementUI: complete introduction and on-demand introduction.
Complete introduction
Introduce the entire Element. In main Add the following code to TS

import ElementUI from 'element-ui'
import 'element-ui/lib/theme-chalk/index.css'
Vue.use(ElementUI)

On demand import
You need to use the babel plugin component plug-in. After installing the plug-in, modify the babel configuration as follows:

{
  "presets": [["es2015", { "modules": false }]],
  "plugins": [
    [
      "component",
      {
        "libraryName": "element-ui",
        "styleLibraryName": "theme-chalk"
      }
    ]
  ]
}

In main Register the components to be used in TS

import Vue from 'vue';
import { Button, Select } from 'element-ui';
import App from './App.vue';

Vue.component(Button.name, Button);
Vue.component(Select.name, Select);
/* Or write as
 * Vue.use(Button)
 * Vue.use(Select)
 */

new Vue({
  el: '#app',
  render: h => h(App)
});

Style processing in projects

Create the following four style files in src/styles

Then in main Introducing the global style file index scss

import Vue from 'vue'
import App from './App.vue'
import router from './router'
import store from './store'

import ElementUI from 'element-ui'
// import 'element-ui/lib/theme-chalk/index.css'

// The global style is introduced. The style of element is introduced into the global style
import './styles/index.scss'

Vue.use(ElementUI)

Vue.config.productionTip = false

new Vue({
  router,
  store,
  render: h => h(App)
}).$mount('#app')

Shared global style variables
To use global style variables in a component, you need to import a global style variable file into the style file before using it

<style lang="scss" scoped>
@import './style/variable.scss';
.text {
  color: $success-color;
}
</style>

However, this requires the introduction of style files in the components used, which is more troublesome. Set shared global style variables To avoid this trouble.
Create vue.com under the root directory config. JS, add the following code. The content in prependData is injected into the style of each component.

module.exports = {
  css: {
    loaderOptions: {
      // By default, the 'sass' option will take effect for both' sass' and 'scss' syntax
      // Because the 'scss' syntax is also handled internally by sass loader
      // However, when configuring the 'prependData' option
      // `scss ` syntax requires a semicolon at the end of a statement, ` sass' requires no semicolon
      // In this case, we can use the 'scss' option to configure the' scss' syntax separately
      scss: {
        prependData: '@import "~@/styles/variables.scss";'
      }
    }
  }
}

In this way, you can directly use global style variables in the component without manually importing files in the component.

<style lang="scss" scoped>
.text {
  color: $success-color;
}
</style>

Interface processing

Interface processing is to configure cross domain processing for the development environment so that the background interface can be accessed normally.
Client configuration server proxy cross domain Need to be in Vue config. Configure the agent of devServer in. JS. The request containing the attribute string in the proxy will be intercepted and replaced with target. After the following configuration http://localhost:8099/boss The first request will be forwarded to http://eduboss.lagou.com Down.

module.exports = {
  devServer: {
    proxy: {
      '/boss': {
        target: 'http://eduboss.lagou.com ',
        // ws: true, // websocket protocol
        changeOrigin: true // Modify the host in the request header
      },
      '/front': {
        target: 'http://edufront.lagou.com',
        changeOrigin: true // Modify the host in the request header
      }
    }
  }
}

Vue router route interception

router. Beforeeach ((to, from, next) = > {}) / / the callback will be triggered before the route jump. Next must be called, otherwise the route will not change
//Route interception, and then judge whether you have logged in here. If not, redirect to the login page and record the current address

router.beforeEach((to, from, next) => {
  if (to.matched.some(item => item.meta.auth)) { // Judge whether the target address needs to be logged in. meta is a user-defined data object, which is passed in when defining the route
    if (!store.state.user) { // If you are not logged in, redirect to the login page and record the page address
      next({
        name: 'login',
        query: {
          redirect: to.fullPath
        }
      })
    } else {
      next()
    }
  } else {
    next()
  }
})

axios request interception

The callback function will be executed before each request is sent, and the config returned by the callback function will be used as the configuration of the final request.

// request interceptor 
request.interceptors.request.use(config => {
  unResponseNum++
  // Set header Authorization
  if (store.state.user && store.state.user.access_token) {
    config.headers.Authorization = store.state.user.access_token
  }
  return config
})

axios response interception

After the request returns the response, if the returned status code is 2xx, the first callback function will be executed, that is, the successful callback function, and the non 2xx status code will execute the second callback function, that is, the failed callback function.

request.interceptors.response.use(res => {}, err => {})

token expiration processing

When the token expires, a 401 status code will be returned. You need to decide whether to refresh the token or log in again according to the situation. The overall process is as follows:

  1. First, you need to determine whether the token has expired or has not logged in
  2. If the token expires, refresh the token according to the refresh token attribute of the record
  3. In the process of refreshing the token, there may be requests to return 401, and the requests to return 401 need to be recorded. After refreshing the token, reissue these requests with a new token.
  4. There is a special request. Some 401 requests return only after the token is refreshed. You need to judge whether all requests have returned. After all requests return, you can reissue the request.
import axios from 'axios'
import store from '@/store/index'
import { Message } from 'element-ui'
import router from '@/router'
import qs from 'qs'

const request = axios.create({
  // configuration option
})

function redirectLogin () {
  router.push({
    name: 'login',
    query: {
      redirect: router.currentRoute.fullPath
    }
  })
  needReRequest = [] // Clear requests that need to be retransmitted
}

function refleshToken () {
  return axios.create()({
    method: 'POST',
    url: '/front/user/refresh_token',
    data: qs.stringify({ refreshtoken: store.state.user.refresh_token })
  })
}

// Number of requests not returned
let unResponseNum = 0

// request interceptor 
request.interceptors.request.use(config => {
  unResponseNum++
  // Set header Authorization
  if (store.state.user && store.state.user.access_token) {
    config.headers.Authorization = store.state.user.access_token
  }
  return config
})

let isRefleshToken = false // Is the token being refreshed
let needReRequest: any[] = [] // 401 request during refresh token

function doReRequest () {
  if (unResponseNum === 0) {
    needReRequest.forEach(cb => cb())
    needReRequest = [] // Empty request cache
  }
}

// Response interceptor
request.interceptors.response.use(res => {
  unResponseNum--
  doReRequest()
  return new Promise((resolve, reject) => { // A successful response of 2xx will be executed here
    if (res.data.state === 1) {
      resolve(res)
    } else {
      Message.error(res.data.message)
      reject(res)
    }
  })
}, error => { // The failure response will be executed here
  unResponseNum--
  if (error.response) { // The request has a non 2xx response
    const { status } = error.response
    // Judging by status
    if (status === 400) { // Request parameter error
      Message.error('Request parameter error')
    } else if (status === 401) { // The token is invalid. The token may not have been passed and expired
      // Judge whether the token has expired
      if (!store.state.user) { // No token has been recorded. Jump to the login page
        redirectLogin()
        return Promise.reject(error)
      }
      if (!isRefleshToken) { // The token was not refreshed
        isRefleshToken = true
        // There is already a token. Refresh the token
        return refleshToken().then(response => { // If the request is successful, refresh the user information in the store and re request the retrieval of 401 status
          const { data } = response
          if (data.state === 1) {
            // Re record token
            store.commit('setUser', data.content)
            // Resend 401 request during refresh token
            doReRequest()
            // Re request
            return request(error.config)
          } else {
            throw new Error(data)
          }
        }).catch(() => { // Failed to refresh token. Please login again
          // Clear saved user information
          store.commit('setUser', null)
          if (unResponseNum === 0) { // Jump to login only when all requests are returned
            redirectLogin()
          }
          return Promise.reject(error)
        }).finally(() => {
          isRefleshToken = false
        })
      }
      // The token is being refreshed. To return a pending request, use promise to suspend the request and save it
      return new Promise(resolve => {
        needReRequest.push(() => {
          resolve(request(error.config))
        })
        doReRequest()
      })
    } else if (status === 403) { // You do not have permission to access the resource
      Message.error('No permission, please contact the administrator')
    } else if (status === 404) {
      Message.error('The requested resource does not exist')
    } else if (status >= 500) {
      Message.error('An error occurred in the server. Please contact the administrator')
    }
  } else if (error.request) { // A request was made, but there was no response
    Message.error('Request timed out, please try again!')
  } else { // An error was triggered while setting the request
    Message.error(`Request failed: ${error.message}`)
  }
  doReRequest()
  return Promise.reject(error)
})

export default request

Keywords: Vue

Added by ryanbutler on Tue, 08 Feb 2022 12:52:33 +0200