天天看點

建構項目//pc端、移動端H5

文章目錄

    • 移動端
    • pc端
    • 建構項目
    • 我的步驟
      • 示例一:vue init webpack 檔案名
      • 示例二:vue create 檔案名
      • vue.config.js 配置
      • ui架構包
      • 安裝異步程式設計架構axios
      • 關于前端登入存儲 token
      • js-cookie
      • 安裝mock
      • 在vue項目中使用Nprogress.js進度條

vue-cli

安裝 ui 庫(eg:pc端用element ui、移動端用 vuex)

移動端

vux

github位址

pc端

elementUI

餓了麼github位址

vue-admin-template

PanJiaChen/vue-element-admin

建構項目

vue前端開發項目架構搭建(node+webpack+vue)

Vue項目環境搭建總結

前端架構搭建-搭建vue環境(學習筆記)

其它:

vue-cli 3.x搭建項目以及其中vue.config.js檔案的配置

Vue 建立項目後沒有 webpack.config.js(vue.config.js) 檔案

解決Vue-cli3沒有vue.config.js檔案夾及配置vue項目域名

Vue vue-config.js(字段屬性詳細介紹)

我的步驟

由于本機已有 nodejs、npm、vue-cli等,是以直接建立項目即可

示例一:vue init webpack 檔案名

vue init webpack 檔案名

建構項目//pc端、移動端H5

示例二:vue create 檔案名

參考:超級詳細的Vue-cli3使用教程

vue create 檔案名

? Please pick a preset: Manually select features
? Check the features needed for your project: (Press <space> to select, <a> to toggle all, <i> to invert selection)
>( ) Babel                              //  代碼編譯
 ( ) TypeScript                         //  ts
 ( ) Progressive Web App (PWA) Support  //  支援漸進式網頁應用程式
 ( ) Router                             //  vue路由
 ( ) Vuex                               //  狀态管理模式
 ( ) CSS Pre-processors                 //  css預處理
 ( ) Linter / Formatter                 //  代碼風格、格式校驗
 ( ) Unit Testing                       //  單元測試
 ( ) E2E Testing                        //  端對端測試
           

一般項目開發隻需要選擇Babel、Router、Vuex就足夠了。

vue.config.js 配置

module.exports = {
  devServer: {
    // 端口号
    port: 8080,
    // 配置不同的背景API位址
    proxy: {
      '/api': {
        target: 'http://www.dzm.com',
        ws: false,
        changeOrigin: true,
        pathRewrite: {
          '^/api': ''
        }
      }
    }
  }
}

           

ui架構包

element-ui官網:http://element-cn.eleme.io/#/zh-CN/component/installation

npm 安裝

完整引入

在 main.js 中寫入以下内容:

import Vue from 'vue';
import ElementUI from 'element-ui';
import 'element-ui/lib/theme-chalk/index.css';
import App from './App.vue';

Vue.use(ElementUI);

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

安裝異步程式設計架構axios

axios中文文檔|axios中文網:http://www.axios-js.com/zh-cn/docs/

vue項目中axios攔截器的使用和配置

  • 安裝_使用 npm:
npm install axios
           
  • src/utils/request.js 中引入 axios

建立 axios 執行個體

const service = axios.create({
  baseURL: process.env.VUE_APP_BASE_API, // url = base url + request url
  // withCredentials: true, // send cookies when cross-domain requests
  timeout: 5000 // request timeout
})
           

配置請求攔截器

service.interceptors.request.use({})

// request interceptor
service.interceptors.request.use(
  config => {
    // do something before request is sent

    if (store.getters.token) {
      // let each request carry token
      // ['X-Token'] is a custom headers key
      // please modify it according to the actual situation
      config.headers['X-Token'] = getToken()
    }
    return config
  },
  error => {
    // do something with request error
    console.log(error) // for debug
    return Promise.reject(error)
  }
)
           

配置響應攔截器

service.interceptors.response.use({})

// response interceptor
service.interceptors.response.use(
  response => {
    const res = response.data

    // if the custom code is not 20000, it is judged as an error.
    if (res.code !== 20000) {
      Message({
        message: res.message || 'Error',
        type: 'error',
        duration: 5 * 1000
      })

      // 50008: Illegal token; 50012: Other clients logged in; 50014: Token expired;
      if (res.code === 50008 || res.code === 50012 || res.code === 50014) {
        // to re-login
        MessageBox.confirm('You have been logged out, you can cancel to stay on this page, or log in again', 'Confirm logout', {
          confirmButtonText: 'Re-Login',
          cancelButtonText: 'Cancel',
          type: 'warning'
        }).then(() => {
          store.dispatch('user/resetToken').then(() => {
            location.reload()
          })
        })
      }
      return Promise.reject(new Error(res.message || 'Error'))
    } else {
      return res
    }
  },
  error => {
    console.log('err' + error) // for debug
    Message({
      message: error.message,
      type: 'error',
      duration: 5 * 1000
    })
    return Promise.reject(error)
  }
)
           

輸出 axios 執行個體

export default service
           

例子:

import axios from 'axios'
import { MessageBox, Message } from 'element-ui'
import store from '@/store'
import { getToken } from '@/utils/auth'

// create an axios instance
const service = axios.create({
  baseURL: process.env.VUE_APP_BASE_API, // url = base url + request url
  // withCredentials: true, // send cookies when cross-domain requests
  timeout: 5000 // request timeout
})

// request interceptor
service.interceptors.request.use(
  config => {
    // do something before request is sent

    if (store.getters.token) {
      // let each request carry token
      // ['X-Token'] is a custom headers key
      // please modify it according to the actual situation
      config.headers['X-Token'] = getToken()
    }
    return config
  },
  error => {
    // do something with request error
    console.log(error) // for debug
    return Promise.reject(error)
  }
)

// response interceptor
service.interceptors.response.use(
  /**
   * If you want to get http information such as headers or status
   * Please return  response => response
  */

  /**
   * Determine the request status by custom code
   * Here is just an example
   * You can also judge the status by HTTP Status Code
   */
  response => {
    const res = response.data

    // if the custom code is not 20000, it is judged as an error.
    if (res.code !== 20000) {
      Message({
        message: res.message || 'Error',
        type: 'error',
        duration: 5 * 1000
      })

      // 50008: Illegal token; 50012: Other clients logged in; 50014: Token expired;
      if (res.code === 50008 || res.code === 50012 || res.code === 50014) {
        // to re-login
        MessageBox.confirm('You have been logged out, you can cancel to stay on this page, or log in again', 'Confirm logout', {
          confirmButtonText: 'Re-Login',
          cancelButtonText: 'Cancel',
          type: 'warning'
        }).then(() => {
          store.dispatch('user/resetToken').then(() => {
            location.reload()
          })
        })
      }
      return Promise.reject(new Error(res.message || 'Error'))
    } else {
      return res
    }
  },
  error => {
    console.log('err' + error) // for debug
    Message({
      message: error.message,
      type: 'error',
      duration: 5 * 1000
    })
    return Promise.reject(error)
  }
)

export default service

           

src/utils/auth.js

import Cookies from 'js-cookie'

const TokenKey = 'vue_admin_template_token'

export function getToken() {
  return Cookies.get(TokenKey)
}

export function setToken(token) {
  return Cookies.set(TokenKey, token)
}

export function removeToken() {
  return Cookies.remove(TokenKey)
}
           

例子:

import axios from "axios";
// import * as auth from "@/plugins/auth"
import {
  Notification as notification,
  MessageBox as Modal,
  Loading
} from "element-ui";
import store from "@/store";
import Vue from "vue";
import * as auth from "@/plugins/auth";

// Full config:  https://github.com/axios/axios#request-config
// axios.defaults.baseURL = '/rms'
// axios.defaults.headers.common['Authorization'] = AUTH_TOKEN;
// axios.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded';

let reqNum = 0;
let loadingInstance = null;

const err = error => {
  if (error.response) {
    let data = error.response.data;
    // const token = auth.getToken()
    // console.log("------異常響應------", token)
    // console.log("------異常響應------", error.response.status)
    switch (error.response.status) {
      case 403:
        notification.error({
          title: "系統提示",
          message: "拒絕通路",
          duration: 4000
        });
        break;
      case 511:
        Modal.alert("很抱歉,登入已過期,請重新登入", {
          title: "登入已過期",
          showClose: false,
          confirmButtonText: "重新登入",
          callback: () => {
            store.dispatch("Logout");
          }
        });
        throw new Error("Token失效,請重新登入");
      case 500:
        //notification.error({ message: '系統提示', description:'Token失效,請重新登入!',duration: 4})
        if (data.message == "Token失效,請重新登入") {
          // update-begin- --- author:scott ------ date:20190225 ---- for:Token失效采用彈框模式,不直接跳轉----
          // store.dispatch('Logout').then(() => {
          //     window.location.reload()
          // })
          Modal.alert("很抱歉,登入已過期,請重新登入", {
            title: "登入已過期",
            showClose: false,
            confirmButtonText: "重新登入",
            callback: () => {
              store.dispatch("Logout");
            }
          });
          // update-end- --- author:scott ------ date:20190225 ---- for:Token失效采用彈框模式,不直接跳轉----
        } else {
          notification.error({
            title: "系統提示",
            message: data,
            duration: 4000
          });
        }
        break;
      case 404:
        notification.error({
          title: "系統提示",
          message: "很抱歉,資源未找到!",
          duration: 4000
        });
        break;
      case 504:
        notification.error({
          title: "系統提示",
          message: "網絡逾時"
        });
        break;
      case 401:
        notification.error({
          title: "系統提示",
          message: "未授權,請重新登入",
          duration: 4000
        });
        break;

      case 302:
        window.location.href = `${process.env.VUE_APP_PublicPath}api/${window.location.host.includes("it.") && !window.location.href.includes(process.env.VUE_APP_PublicPath+"a")?'oauth/':''}login?backUrl=http://${window.location.host}${process.env.VUE_APP_PublicPath}%23/`;
        break;
      default:
        notification.error({
          title: "系統提示",
          message: data,
          duration: 4000
        });
        break;
    }
  }
  return Promise.reject(error);
};

let config = {
  baseURL: process.env.VUE_APP_PublicPath.replace(/\/$/, ""),
  timeout: 60 * 1000, // Timeout
  withCredentials: true // Check cross-site Access-Control
};

export const _axios = axios.create(config);

// 請求攔截器
_axios.interceptors.request.use(
  config => {
    let cancel;
    // 設定cancelToken對象
    config.cancelToken = new axios.CancelToken(c => {
      cancel = c;
    });

    //console.log(cancel);

    let urlList = ['task/getTaskTypeList', '/dict/getDictItems'];
    let isFilter = urlList.some(item => {
      let number = config.url.indexOf(item);
      return number > -1;
    })
    if (isFilter && !store.getters.isIntercept) {
      try {
        cancelRequest(config.url, cancel, "重複請求");
      } catch (e) {
        //
      }
    }
    config.headers["X-Requested-With"] = "XMLHttpRequest";

    if (
      config.url.indexOf("/rmsApi/") >= 0 ||
      config.url.indexOf("/api/") < 0
    ) {
      config.withCredentials = false;
    }

    if (auth.getAuthorization()) {
      config.headers["Authorization"] = auth.getAuthorization(); // 讓每個請求攜帶自定義token 請根據實際情況自行修改
    }

    if (auth.getSystemUid()) {
      config.headers["SystemUid"] = auth.getSystemUid(); // 讓每個請求攜帶自定義token 請根據實際情況自行修改
    } else {
      console.error("SystemUid not find");
    }

    if (config.responseType != "arraybuffer") {
      setTimeout(() => {
        if (!loadingInstance) {
          if (reqNum > 0) {
            loadingInstance = Loading.service({
              target: ".el-main",
              fullscreen: false,
              text: "資料請求中...",
              background: "rgba(0,0,0,0.1)"
            });
          }
        }
      }, 500);
      reqNum++;
    }
    // Do something before request is sent
    // const token = auth.getToken()
    // if (token) {
    //   config.headers['X-Access-Token'] = token // 讓每個請求攜帶自定義 token 請根據實際情況自行修改
    // }
    if (config.method === "get") {
      let params = config.params;
      let Nparams = {};
      for (let item in params) {
        if (item.indexOf("like_") == 0) {
          Nparams[item.replace("like_", "")] = "*" + params[item] + "*";
        } else if (item.indexOf("left_like_") == 0) {
          Nparams[item.replace("left_like_", "")] = params[item] + "*";
        } else if (item.indexOf("right_like_") == 0) {
          Nparams[item.replace("right_like_", "")] = "*" + params[item];
        } else {
          Nparams[item] = params[item];
        }
      }
      config.params = Nparams;

      if (config.url.indexOf("/round/pagelist") >= 0) {
        config.params.column = config.params.column || "createTime"
        config.params.order = config.params.order || "DESC"
      }


      // 中央督察、自治區督察,分别給所有接口加 systemType
      // 整改管理兩個督察類型分别在所有接口加 systemtype
      // routertag為'zydc'時添加 systemType=1
      var a = {
        systemType: 1
      }
      var b = {
        systemType: 2
      }
      if (store.getters.routerTag == 'zydc') {
        config.params = Object.assign(config.params, a)
      } else if (store.getters.routerTag == 'zzqdc') {
        config.params = Object.assign(config.params, b)
      } else if (store.getters.routerTag == 'zggl') {
        if (store.getters.zgglSystemType == '1') {
          config.params = Object.assign(config.params, a)
        } else if (store.getters.zgglSystemType == '2') {
          config.params = Object.assign(config.params, b)
        }
      }
    }

    return config;
  },
  error => {
    reqNum++;
    // Do something with request error
    return Promise.reject(error);
  }
);

// Add a response interceptor 響應攔截器
_axios.interceptors.response.use(
  function (response) {
    if (response.headers.sessionkey) {
      store.commit('Set_sessionkey', response.headers.sessionkey);
    }
    closeLoading();
    if (response.config.responseType == "arraybuffer") {
      const fileName = response.headers.filename;
      return Promise.resolve({
        fileName: decodeURI(fileName),
        data: response.data
      })
    }
    // Do something with response data
    if (!response.status >= 200 && !response.status < 300) {
      notification.warning({
        title: "系統提示",
        message: response.data,
        duration: 4000
      });
      return Promise.reject(response.data);
    }


    return response.data;
  },
  function (error) {
    closeLoading();
    // Do something with response error
    return err(error);
    //return Promise.reject(error);
  }
);
           
  • 在 src/api/下的 js 檔案引入 axios 執行個體,然後寫各種請求方法
import request from '@/utils/request'

export function login(data) {
  return request({
    url: '/vue-admin-template/user/login',
    method: 'post',
    data
  })
}

export function getInfo(token) {
  return request({
    url: '/vue-admin-template/user/info',
    method: 'get',
    params: { token }
  })
}

export function logout() {
  return request({
    url: '/vue-admin-template/user/logout',
    method: 'post'
  })
}

           

關于前端登入存儲 token

localStorage存儲方式:很詳細的一篇文章!vue實作token使用者登入

Vue重新整理token,判斷token是否過期、失效的最簡便的方法

結合 vuex 存儲 token

在store檔案的state中初始化token,因為state中的資料不支援直接修改,是以我們需要定義方法

setToken(設定token)

getToken(擷取token)

,然後我們就可以在登入接口處引入

this.$store.commit('setToken',JSON.stringify(res.data.token))

将背景傳來的token存入Vuex和localStorage中

,為什麼還要存入localStorage,Vuex中的狀态

一旦頁面重新整理就不再存在

,為了保持目前狀态,需要

通過localStorage中提取狀态再傳值給Vuex

import Vue from 'vue'
import Vuex from 'vuex'
 
Vue.use(Vuex)
 
export default new Vuex.Store({
  state: {
    token:''  //初始化token
  },
  mutations: {
    //存儲token方法
    //設定token等于外部傳遞進來的值
    setToken(state, token) {
        state.token = token
        localStorage.token = token //同步存儲token至localStorage
      },
  },
 getters : {
  //擷取token方法
  //判斷是否有token,如果沒有重新指派,傳回給state的token
  getToken(state) {
    if (!state.token) {
      state.token = localStorage.getItem('token')
    }
    return state.token
    }
  },
  actions: {
 
  }
})
           

js-cookie

github位址:https://github.com/js-cookie/js-cookie

https://www.npmjs.com/package/js-cookie

js-cookie中文文檔

vue項目中js-cookie的使用存儲token

NPM 安裝

npm install --save js-cookie
           

src/utils/auth.js 引入 Cookies

例子:

import Cookies from 'js-cookie'

const TokenKey = 'vue_admin_template_token'

export function getToken() {
  return Cookies.get(TokenKey)
}

export function setToken(token) {
  return Cookies.set(TokenKey, token)
}

export function removeToken() {
  return Cookies.remove(TokenKey)
}
           

安裝mock

MockJS快速入門

前後端分離,前端通過mock開發,無需等待後端接口開發好了再開發

npm install mockjs

移除 mock

vue.config.js

中移除

webpack-dev-server

proxy

after

這個Middleware就可以了。

proxy: {
  // change xxx-api/login => mock/login
  // detail: https://cli.vuejs.org/config/#devserver-proxy
  [process.env.VUE_APP_BASE_API]: {
    target: `http://localhost:${port}/mock`,
    changeOrigin: true,
    pathRewrite: {
      ['^' + process.env.VUE_APP_BASE_API]: ''
    }
  }
},
after: require('./mock/mock-server.js')
           

mock-server

隻會在開發環境中使用,線上生産環境目前使用

MockJs

進行模拟。如果不需要請移除。具體代碼:main.js

import { mockXHR } from '../mock'
if (process.env.NODE_ENV === 'production') {
  mockXHR()
}
           
/**
 * If you don't want to use mock-server
 * you want to use MockJs for mock api
 * you can execute: mockXHR()
 *
 * Currently MockJs will be used in the production environment,
 * please remove it before going online ! ! !
 */
if (process.env.NODE_ENV === 'production') {
  const { mockXHR } = require('../mock')
  mockXHR()
}
           

修改

最常見的操作就是:你本地模拟了了一些資料,待後端完成接口後,逐漸替換掉原先 mock 的接口。

我們以src/api/role.js中的getRoles接口為例。它原本是在mock/role/index.js中 mock 的資料。現在我們需要将它切換為真實後端資料,隻要在mock/role/index.js找到對應的路由,之後将它删除即可。這時候你可以在network中,檢視到真實的資料。

// api 中聲明的路由
export function getRoles() {
  return request({
    url: '/roles',
    method: 'get'
  })
}

//找到對應的路由,并删除
{
    url: '/roles',
    type: 'get',
    response: _ => {
      return {
        code: 20000,
        data: roles
      }
    }
  },
           

在vue項目中使用Nprogress.js進度條

https://blog.csdn.net/qq_35844177/article/details/70171054

npm install --save nprogress

src/permission.js 中引入

import NProgress from 'nprogress'
import 'nprogress/nprogress.css'
           

繼續閱讀