開發(fā)背景
來北京工作已經(jīng)一年多了绽榛,大大小小的項目也寫了不少灭美,磕磕碰碰到了現(xiàn)在铁坎,記錄一下
功能要點
- 1 公共網(wǎng)絡(luò)請求封裝
- 2 自定義dialog
- 3 base組件封裝
- 4 多環(huán)境打包優(yōu)化
- 5 路由按需加載梯捕,gzip開啟
- 6 骨架屏功能實現(xiàn)
上干貨
1 公共網(wǎng)絡(luò)請求封裝
這個功能其實在以前的文章中就有說到過傀顾,核心就是抽取接口地址的前綴短曾,根據(jù)后臺返回的接口內(nèi)容嫉拐,進(jìn)行統(tǒng)一的出來(彈對話框婉徘,執(zhí)行回調(diào))
需要注意的地方就是多個網(wǎng)絡(luò)時儒鹿,多個遮罩層的處理方式
這個不多說几晤,直接上代碼(基于axios)
//開始加載動畫效果
function startNetWork(msgText) {
//修復(fù)多接口訪問無法關(guān)閉浮層BUG
if (document.body.contains(loadDialog)) {
document.body.removeChild(loadDialog);
}
loadDialog = document.createElement('div');
loadDialog.className = 'popContainer';
let loadImage = document.createElement('img');
loadImage.src = 'static/img/load.svg';
let loadMsg = document.createElement('label');
loadMsg.innerText = msgText ? msgText : '加載中...';
loadDialog.appendChild(loadImage);
loadDialog.appendChild(loadMsg);
document.body.appendChild(loadDialog)
}
//關(guān)閉加載動畫效果
function stopNetWork() {
//修復(fù)多接口訪問無法關(guān)閉浮層BUG
if (document.body.contains(loadDialog)) {
document.body.removeChild(loadDialog);
}
}
// get網(wǎng)絡(luò)請求
function get(networkParam, data, vueObj, successCallback, errorCallback) {
let realUrl = preUrl + networkParam.url;
if (networkParam.showLoading) {
startNetWork();
}
let token = getUserData('token');
if (!token) {
// 跳轉(zhuǎn)回登錄頁
vueObj.$router.push({path: '/'})
}
axios.get(realUrl, {
params: data,
headers: {
token: token
}
}).then(function (response) {
stopNetWork();
console.log(response);
successCallback(vueObj, response.data)
}).catch(function (response) {
console.log(response, 'get axios');
stopNetWork();
errorCallback(vueObj, response);
})
}
2 自定義Toast
這個功能的原理跟網(wǎng)絡(luò)請求時的動畫比較相似圾浅,通過js動態(tài)生成div喷鸽,拼接到本頁body標(biāo)簽下,然后通過定時器移除div魁衙,達(dá)到Toast的效果
let info = {
background: 'black',
color: 'white',
width: '80%',
content: '輸出內(nèi)容',
borderRadius: '10px',
duration: 2000
};
// toast相關(guān)配置見上方
function toast(toastInfo) {
let duration = 800;
let m = document.createElement('div');
let color = toastInfo.color ? toastInfo.color : info.color;
let width = toastInfo.width ? toastInfo.width : info.width;
let background = toastInfo.background ? toastInfo.background : info.background;
let borderRadius = toastInfo.borderRadius ? toastInfo.borderRadius : info.borderRadius;
m.innerHTML = toastInfo.content ? toastInfo.content : info.content;
m.style.cssText = "width: " + width + ";min-width: 150px;opacity: 1;height: 30px;color: " +
color + ";line-height: 30px;text-align: center;border-radius: " + borderRadius + ";position: fixed;top: 40%;left: 10%;z-index: 999999;background: " + background + ";font-size: 16px;";
document.body.appendChild(m);
setTimeout(function () {
let d = 0.5;
m.style.webkitTransition = '-webkit-transform ' + d + 's ease-in, opacity ' + d + 's ease-in';
m.style.opacity = '0';
setTimeout(function () {
document.body.removeChild(m)
}, d * 1000);
}, duration);
}
3 base組件封裝
這個基vue存在的目的是為了解耦纯蛾,項目中所有的頁面文件不直接操作封裝好的網(wǎng)絡(luò)請求翻诉,等一些常用的util,而且通過這個基vue間接的操作碰煌,base起到了一個橋梁的作用芦圾,使得代碼更加的規(guī)范
export default {
name: "base",
data() {
return {
toastInfo: {
background: "red",
color: "white",
width: "80%",
content: "ce",
borderRadius: "20px",
duration: 500
}
};
},
methods: {
// 設(shè)置遮罩層
setShade(msgText) {
showShade(msgText);
},
testLoading() {
startNetWork();
},
// 設(shè)置對話框背景顏色
setToastBackGroud(color) {
this.toastInfo.background = color;
},
// 設(shè)置對話框邊框圓角 radiusPx 單位px
setToastBorderRadius(radiusPx) {
this.toastInfo.borderRadius = radiusPx;
},
// 彈出對話框 conten 彈出內(nèi)容
showToast(content) {
this.toastInfo.content = content;
toast(this.toastInfo);
},
//帶動畫效果的GET請求
get(params, data, vueObj, successCallback, errorCallback) {
let networkParam = {
url: "",
showLoading: true
};
if (typeof params == "string") {
networkParam.url = params;
} else {
networkParam = params;
}
get(
networkParam,
data,
vueObj,
function(_this, data) {
if (data.status == -1) {
localStorage.clear();
_this.muiTost(data.msg);
_this.$router.push({
path: "/"
});
}
if (data.data != undefined && data.data != null) {
successCallback(_this, data);
} else {
_this.showToast("未知錯誤");
}
},
function(_this, response) {
console.log("errorCallback");
if (response.response) {
errorCallback(_this, response.response.data);
return;
}
_this.showToast("未知錯誤");
}
);
},
//帶動畫效果的POST請求
post(params, data, vueObj, successCallback, errorCallback) {
let networkParam = {
url: "",
showLoading: true
};
if (typeof params == "string") {
networkParam.url = params;
} else {
networkParam = params;
}
post(
networkParam,
data,
vueObj,
function(_this, data) {
if (data.status == undefined || data.status == null) {
_this.showToast("未知錯誤");
} else if (data.status == 0 && successCallback != undefined) {
successCallback(_this, data);
} else if (data.status == -1) {
localStorage.clear();
_this.muiTost(data.msg);
_this.$router.push({
path: "/"
});
} else {
_this.showToast(data.msg);
}
},
function(_this, response) {
console.log("errorCallback");
if (response.response) {
errorCallback(_this, response.response.data);
return;
}
_this.showToast("未知錯誤");
}
);
}
}
};
//其他vue通過extends來繼承base
import base from '../page/base';
export default {
extends: base,
name: "Login"
}
4 多環(huán)境打包優(yōu)化
由于公司上線流程比較規(guī)范夜焦,有將項目部署到多個環(huán)境的需求,所以將抽象出一個build.js文件來設(shè)置環(huán)境名巷波。
// test test環(huán)境
// dev dev環(huán)境
// pre 預(yù)發(fā)布環(huán)境
// prod 生產(chǎn)環(huán)境
const buildTip = 'test';
module.exports.buildTip = buildTip;//將變量拋出抹镊,在路由文件以及打包文件等引入
其中路由文件以及打包文件中的相關(guān)配置是需要同步的
router/index.js
import Vue from 'vue'
import Router from 'vue-router'
import {buildTip} from "../util/build";
//適配多種環(huán)境打包
let base;
switch (buildTip) {
case "test":
base = '/demo/';
break;
case "dev":
base = '/demo/';
break;
case "pre":
base = '/demo/';
break;
case "prod":
base = '/demo/';
break;
}
Vue.use(Router);
export default new Router({
base: base,//將變量設(shè)置給base
mode: 'history',
routes: [
]
})
config/index.js
const build = require("../src/util/build");//引入build.js中的環(huán)境名稱
let indexDirName, assetsRoot, assetsPublicPath;
//適配多版本打包發(fā)布
switch (build.buildTip) {
case "test":
indexDirName = '../demo/index.html';
assetsRoot = '../demo/';
assetsPublicPath = '/demo/';
break;
case "dev":
indexDirName = '../demo/index.html';
assetsRoot = '../demo/';
assetsPublicPath = '/demo/';
break;
case "pre":
indexDirName = '../demo/index.html';
assetsRoot = '../demo/';
assetsPublicPath = '/demo/';
break;
case "prod":
indexDirName = '../demo/index.html';
assetsRoot = '../demo/';
assetsPublicPath = '/demo/';
break;
}
build: {
// Template for index.html
index: path.resolve(__dirname, indexDirName), //對應(yīng)以上的三個變量
// Paths
assetsRoot: path.resolve(__dirname, assetsRoot),//對應(yīng)以上的三個變量
assetsSubDirectory: 'static',
assetsPublicPath: assetsPublicPath,//對應(yīng)以上的三個變量
/**
* Source Maps
*/
productionSourceMap: false,
// https://webpack.js.org/configuration/devtool/#production
devtool: '#source-map',
// Gzip off by default as many popular static hosts such as
// Surge or Netlify already gzip all static assets for you.
// Before setting to `true`, make sure to:
// npm install --save-dev compression-webpack-plugin
productionGzip: true,//同時需要在nginx服務(wù)器做出gzip配置
productionGzipExtensions: ['js', 'css'],
// Run the build command with an extra argument to
// View the bundle analyzer report after build finishes:
// `npm run build --report`
// Set to `true` or `false` to always turn it on or off
bundleAnalyzerReport: process.env.npm_config_report
}
5 路由按需加載氨菇,gzip開啟
這個方面的作用是為了提高頁面訪問速度查蓉,屬于性能優(yōu)化
routes: [
{
path: '/',
name: 'home',
component: function (resolve) {
require(['../page/home'], resolve)//路由按需加載
}
}
, {
path: '/scroller',
name: 'scroller',
component: function (resolve) {
require(['../page/scroller'], resolve)
}
}
]
至于gzip,起初以為前端配置就可以了乌询,到最后才發(fā)現(xiàn)還需要服務(wù)端nginx的配合
productionGzip: true,//將false改為true,同時需要在nginx服務(wù)器做出gzip配置
productionGzipExtensions: ['js', 'css'],
nginx配置
gzip on; #開啟gzip
gzip_min_length 1k; #低于1kb的資源不壓縮
gzip_comp_level 5; #壓縮級別【1-9】,越大壓縮率越高豌研,同時消耗cpu資源也越多妹田,建議設(shè)置在4左右。
gzip_types text/plain application/javascript application/x-javascript text/javascript text/xml text/css;
gzip_disable "MSIE [1-6]\."; #配置禁用gzip條件鹃共,支持正則鬼佣。此處表示ie6及以下不啟用gzip(因為ie低版本不支持)
gzip_vary on; #是否添加“Vary: Accept-Encoding”響應(yīng)頭
6 骨架屏功能實現(xiàn)
骨架屏,在用戶體驗方面的作用比較大霜浴,實現(xiàn)起來并不難晶衷,自定義組件+v-if就可以實現(xiàn)該功能了
具體實現(xiàn)如下
<template>
<div class="container login column-center">
<Skeleton v-if="!init"></Skeleton>
<button @click="changeToastBackground">將對話框顏色改為紅色</button>
<button @click="toast">測試Toast對話框</button>
<button @click="testGet">測試Get請求</button>
<button @click="testPost">測試Post請求</button>
<button @click="testLoading">測試Loading</button>
<button @click="testShade">測試遮罩</button>
</div>
</template>
<script>
import base from '../page/base';
import Skeleton from "../personView/Skeleton"; //引入組件
export default {
extends: base,
name: "Login",
components: {
Skeleton
},
data() {
return {
init: false//設(shè)置相關(guān)變量
}
},
mounted() {
let that = this;
//模擬請求操作
setTimeout(function () {
that.init = true;
}, 2000)
},
methods: {
toast() {
this.showToast('修改成功');
},
testShade() {
this.setShade('測試')
},
testGet() {
this.get('User/detail', {}, this, function (that, data) {
console.log(data)
},
function (that, data) {
console.log(data);
if (!isNaN(data.responseCode)) {
that.showToast(data.responseMessage)
}
}
)
},
testPost() {
this.post('User/detail', {test: '1231'}, this, function (that, data) {
}, function (that, data) {
if (!isNaN(data.responseCode)) {
that.showToast(data.responseMessage)
}
})
},
changeToastBackground() {
this.setToastBackGroud('red')
}
}
}
</script>
<style scoped>
.login button {
border: 0;
padding: 0;
margin-top: 10px;
height: 40px;
width: 80%;
background-color: black;
color: white;
border-radius: 20px;
}
</style>
最后
附上封裝的demo地址,其中還有自己實現(xiàn)的上拉加載搜索功能阴孟,需要的可以下載來看看
下載地址