寫在開頭的開頭
目標搭建一個Vue Cli 中後臺專案的基礎。一步一步搭建,希望能有所收益。後臺可能是這樣的(參考 AdminLTE)
學習的時候,希望多翻閱文件,資料地址也會引入到原文
主要包含
- 側邊選單 aside
- 右側 container
- 頭部 header
- 主體 main。頁面主要變化的部分
資料
- Vue CLI.
- 原始碼 GitHub E-Admin.
將要做什麼
- 開發環境 @vue/cli 建立專案
- 字型庫 & SVG使用
- 網路請求 axios封裝api
- 資料管理 vuex
- vue router 做好路由守衛
- 附錄
- vue.config.js
- webpack-bundle-analyzer 使用
- VUE 9個效能優化祕密?(vue-9-perf-secrets)
@vue/cli 建立專案
開發環境:
+ Win10 x64
+ node v10.15.3
+ npm v6.4.1
+ @vue/cli 3.6.3
複製程式碼
vue create project-name
複製程式碼
縮減篇幅,省略具體建立過程。
- step.1 選擇設定,預設設定或者自定義Manually
- step.2 自定義配置,有 TypeScript支援,也有PWA。
- step.3 路由模式選擇 history
- step.4 CSS 編譯器選擇
- step.5 ESLint 以及配置選擇
- step.6 何時檢查 Lint
- step.7 ESLint 配置檔案寫在哪裡,可以單獨檔案,也可以在package.json
- step.8 是否儲存配置資訊,下次專案使用 N
- 自動安裝依賴
目錄規劃
.
├── build 專案構建配置
├── public 打包所需靜態資源
└── src
├── api AJAX請求
└── assets 專案靜態資源
├── icons SVG 圖示資源
├── fonts 字型圖示資源
└── images 圖片資源
├── components 業務元件
├── config 專案執行配置
├── directive 自定義指令
├── libs 封裝工具函式
├── router 路由配置
├── store Vuex配置
└── views 頁面檔案
複製程式碼
字型庫 && SVG
main.js 引入全域性 阿里 Ant 圖示字型庫
SVG
圖示字型,也可以用SVG代替,方案可以做一個SVG元件,通過名字,載入不同的 SVG圖示,通過size,控制圖示大小,color,控制顏色即可
網路請求 axios封裝api
// 安裝
npm i axios
複製程式碼
- 統一捕獲介面報錯 : 用的axios內建的攔截器
- 彈窗提示: 引入 Element UI的Message元件
- 報錯重定向: 路由鉤子
- 基礎鑑權: 服務端過期時間戳和token,還有藉助路由的鉤子
- 客戶端支援防止 CSRF/XSRF
- 表單序列化: 我這邊直接用qs(npm模組),你有時間也可以自己寫
- 號外,對請求佇列優化,尤其是翻頁時多次請求。(未完成)
請求攔截器 Axios.interceptors.request
帶上自己需要的引數,比如CSRF/XSRF,基礎鑑權 token,請求時間戳
對請求資料做處理,轉表單,或轉Json
對錯誤的情況統一處理
響應攔截器 Axios.interceptors.interceptors
對響應結果統一處理,響應結果狀態status 判斷解封裝,錯誤處理等
axios可配置
import axios from "axios";
import qs from "qs";
import { Message } from "element-ui";
import router from "../router";
const Axios = axios.create({
baseURL: "/", // 因為我本地做了反向代理
timeout: 10000,
responseType: "json",
withCredentials: true, // 是否允許帶cookie這些
headers: {
"Content-Type": "application/x-www-form-urlencoded;charset=utf-8"
}
});
//POST傳參序列化(新增請求攔截器)
Axios.interceptors.request.use(
config => {
// 在傳送請求之前做某件事
if (
config.method === "post"
) {
// 序列化 若是能直接接受json 格式,可以不用 qs 來序列化的
config.data = qs.stringify(config.data);
}
// 若是有做鑑權token , 就給頭部帶上token
// 若是需要跨站點,存放到 cookie 會好一點,限制也沒那麼多,有些瀏覽環境限制了 localstorage 的使用
// 這裡localStorage一般是請求成功後我們自行寫入到本地的,因為你放在vuex重新整理就沒了
// 一些必要的資料寫入本地,優先從本地讀取
if (localStorage.token) {
config.headers.Authorization = localStorage.token;
}
return config;
},
error => {
// error 錯誤處理
Message({
// 餓了麼的訊息彈窗元件,類似toast
showClose: true,
message: error && error.data.error.message,
type: 'error'
});
return Promise.reject(error.data.error.message);
}
);
//返回狀態判斷(新增響應攔截器)
Axios.interceptors.response.use(
res => {
//對響應資料做些事
if (res.data && !res.data.success) {
Message({
// 餓了麼的訊息彈窗元件,類似toast
showClose: true,
message: res.data.error.message.message
? res.data.error.message.message
: res.data.error.message,
type: "error"
});
return Promise.reject(res.data.error.message);
}
return res;
},
error => {
// 使用者登入的時候會拿到一個基礎資訊,比如使用者名稱,token,過期時間戳
// 直接丟localStorage或者sessionStorage
if (!window.localStorage.getItem("loginUserBaseInfo")) {
// 若是介面訪問的時候沒有發現有鑑權的基礎資訊,直接返回登入頁
router.push({
path: "/login"
});
} else {
// 若是有基礎資訊的情況下,判斷時間戳和當前的時間,若是當前的時間大於伺服器過期的時間
// 乖乖的返回去登入頁重新登入
let lifeTime =
JSON.parse(window.localStorage.getItem("loginUserBaseInfo")).lifeTime *
1000;
let nowTime = new Date().getTime(); // 當前時間的時間戳
console.log(nowTime, lifeTime);
console.log(nowTime > lifeTime);
if (nowTime > lifeTime) {
Message({
showClose: true,
message: "登入狀態資訊過期,請重新登入",
type: "error"
});
router.push({
path: "/login"
});
} else {
// 下面是介面回撥的satus ,因為我做了一些錯誤頁面,所以都會指向對應的報錯頁面
if (error.response.status === 403) {
router.push({
path: "/error/403"
});
}
if (error.response.status === 500) {
router.push({
path: "/error/500"
});
}
if (error.response.status === 502) {
router.push({
path: "/error/502"
});
}
if (error.response.status === 404) {
router.push({
path: "/error/404"
});
}
}
}
// 返回 response 裡的錯誤資訊
let errorInfo = error.data.error ? error.data.error.message : error.data;
return Promise.reject(errorInfo);
}
);
export default Axios
複製程式碼
Vuex 資料管理
面試通常都會被問到,資料通訊的問題,跨元件之間如何實現資料管理。當然方法不止 vuex
,本地儲存Local Storage,Cookies 也可以實現。專案小的時候,可以不使用vuex,專案比較簡單時,store 都在一個檔案中,或者 getter,actions,mutations,mutations-types,state拆分。應用比較大的時候,可以按模組拆分。
import Vue from 'vue'
import Vuex from 'vuex'
// 按模組拆分
import app from './module/app'
// 開啟 vuex logs
import createLogger from 'vuex/dist/logger'
Vue.use(Vuex)
const debug = process.env.NODE_ENV !== 'production'
export default new Vuex.Store({
state: {
},
mutations: {
},
actions: {
},
// 按模組引入
modules: {
app
},
strict: debug,
plugins: debug ? [createLogger()] : []
})
複製程式碼
Vue Router. 做好路由守衛
包含的功能:
- 巢狀的路由/檢視表
- 模組化的、基於元件的路由配置
- 路由引數、查詢、萬用字元
- 基於 Vue.js 過渡系統的檢視過渡效果
- 細粒度的導航控制
- 帶有自動啟用的 CSS class 的連結
- HTML5 歷史模式或 hash 模式,在 IE9 中自動降級
- 自定義的滾動條行為
高階進階
- 導航守衛
- 路由元資訊
- 過度動效
- 資料獲取
- 滾動行為
- 路由懶載入
我們可以做什麼
- 巢狀路由/檢視表,子路由
children
可以更好的組織頁面 - 路由引數,跳轉,傳參,匹配
- 過渡效果,滾動行為
- 導航守衛,許可權限定
- 路由懶載入,結合 Vue 的非同步元件和 Webpack 的程式碼分割功能,輕鬆實現路由元件的懶載入
- 判斷使用者是否已經登入,控制進入許可權頁面。
- 定義路由的時候可以配置
meta
欄位,個性化定製一些功能 - 等等
全域性前置守衛
const router = new VueRouter({ ... })
router.beforeEach((to, from, next) => {
// todo 在路由守衛中,可以判斷使用者登入情況,鑑權,
})
複製程式碼
側邊選單欄配置
使用者登入後,獲取使用者許可權,比對 routers
提交一份資料到 vuex
根據使用者許可權更新資料,渲染選單欄目
vue.config.js
調整 webpack 配置最簡單的方式就是在 vue.config.js 中的 configureWebpack 選項提供一個物件:
// vue.config.js
module.exports = {
configureWebpack: {
plugins: [
new MyAwesomeWebpackPlugin()
]
}
}
複製程式碼
該物件將會被 webpack-merge 合併入最終的 webpack 配置。
文件中,提及鏈式操作 (高階)、修改 Loader 選項、替換Loader、新建Loader、修改外掛,等等。
環境變數和模式
NODE_ENV
如果在環境中有預設的 NODE_ENV,你應該移除它或在執行 vue-cli-service 命令的時候明確地設定 NODE_ENV。
模式
模式是 Vue CLI 專案中一個重要的概念。預設情況下,一個 Vue CLI 專案有三個模式:
development
模式用於vue-cli-service serve
production
模式用於vue-cli-service build
和vue-cli-service test:e2e
test
模式用於vue-cli-service test:unit
注意模式不同於 NODE_ENV
,一個模式可以包含多個環境變數。也就是說,每個模式都會將 NODE_ENV
的值設定為模式的名稱——比如在 development 模式下 NODE_ENV
的值會被設定為 "development"
。
你可以通過為 .env
檔案增加字尾來設定某個模式下特有的環境變數。比如,如果你在專案根目錄建立一個名為 .env.development
的檔案,那麼在這個檔案裡宣告過的變數就只會在 development
模式下被載入。
你可以通過傳遞 --mode
選項引數為命令列覆寫預設的模式。例如,如果你想要在構建命令中使用開發環境變數,請在你的 package.json
指令碼中加入:
"dev-build": "vue-cli-service build --mode development",
複製程式碼
在客戶端側程式碼中使用環境變數
只有以 VUE_APP_
開頭的變數會被 webpack.DefinePlugin
靜態嵌入到客戶端側的包中。你可以在應用的程式碼中這樣訪問它們:
console.log(process.env.VUE_APP_SECRET)
複製程式碼
vue.config.js
- 有了
NODE_ENV
可以對不同環境做出不同的配置 - 資料夾別名
resolve.alias
- 設定代理
devServer
,解決跨域開發問題
const path = require('path')
// Webpack包檔案分析器
// const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin
function resolve (dir) {
return path.join(__dirname, './', dir)
}
// 專案部署基礎 (webpack 的 devServer 地址)
// process.env.NODE_ENV
// 正式環境 production
// 開發環境 development
// 預設情況下,我們假設你的應用將被部署在域的根目錄下,
// 例如:https://www.my-app.com/
// 預設:'/'
// 如果您的應用程式部署在子路徑中,則需要在這指定子路徑
// 例如:https://www.foobar.com/my-app/
// 需要將它改為'/my-app/'
const BASE_URL = process.env.NODE_ENV === 'production'
? '/my-app/'
: '/'
module.exports = {
// 這裡是對環境的配置,不同環境對應不同的BASE_API,以便 axios 的請求地址不同 baseUrl 從 Vue CLI 3.3 起已棄用,請使用publicPath
publicPath: BASE_URL,
// tweak internal webpack configuration.
// see https://github.com/vuejs/vue-cli/blob/dev/docs/webpack.md
// 如果你不需要使用eslint,把lintOnSave設為false即可
lintOnSave: false,
/**
* 對內部的 webpack 配置進行更細粒度的修改
* https://github.com/neutrinojs/webpack-chain see
* https://github.com/vuejs/vue-cli/blob/dev/docs/webpack.md
* @param config
*/
chainWebpack: (config) => {
// key,value自行定義,比如.set('@@', resolve('src/components'))
config.resolve.alias
.set('@', resolve('src'))
.set('api', resolve('src/api'))
.set('common', resolve('src/common'))
.set('components', resolve('src/components'))
},
/**
* 調整 webpack 配置
* https://cli.vuejs.org/zh/guide/webpack.html#%E7%AE%80%E5%8D%95%E7%9A%84%E9%85%8D%E7%BD%AE%E6%96%B9%E5%BC%8F
* @param config
*/
configureWebpack: config => {
// 生產and測試環境
let pluginsPro = [
// Webpack包檔案分析器(https://github.com/webpack-contrib/webpack-bundle-analyzer)
// new BundleAnalyzerPlugin()
]
// 開發環境
let pluginsDev = []
if (process.env.NODE_ENV === 'production') {
// 為生產環境修改配置... process.env.NODE_ENV !== 'development'
config.plugins = [...config.plugins, ...pluginsPro]
} else {
// 為開發環境修改配置...
config.plugins = [...config.plugins, ...pluginsDev]
}
},
// 打包時不生成.map檔案
productionSourceMap: false,
// webpack-dev-server 相關配置 https://webpack.js.org/configuration/dev-server/
// 這裡寫你呼叫介面的基礎路徑,來解決跨域,如果設定了代理,那你本地開發環境的axios的baseUrl要寫為 '' ,即空字串
// devServer: {
// proxy: 'localhost:3000'
// }
devServer: {
// host: 'localhost',
host: '0.0.0.0',
port: 8000, // 埠號
https: false, // https:{type:Boolean}
open: true, // 配置自動啟動瀏覽器
hotOnly: true, // 熱更新
// 配置跨域處理,只有一個代理
proxy: {
'/my-app/*': {
target: 'http://xxx.xxx.xxx.xxx:xxxx/',
changeOrigin: true,
pathRewrite: {
'^/my-app': ''
}
},
'/SocketWeb/*': {
target: 'http://xxx.xxx.xxx.xxx:xxxx',
changeOrigin: true,
// websocket支援
ws: true,
secure: false
}
}
}
}
複製程式碼
開發基礎環境可以了,開發中遇到的資料夾別名,請求跨域的問題解決了,自定義配置,也可以更具自己公司的情況配置。還有比較自定義的部分可以參照文件配置。
webpack-bundle-analyzer
安裝
$ npm intall webpack-bundle-analyzer --save-dev
複製程式碼
- 配置vue.config.js 開啟註釋
webpack-bundle-analyzer
的部分。 - 在script中新增
// 在執行 build 時,後臺面新增 --report
npm run build --report
// 或者在script中新增新命令
"analyz": "npm_config_report=true npm run build"
複製程式碼