最近在作個人小愛ADMIN後臺管理系統,結合當前市場後臺管理系統對相關功能的需求,我又開始新增了一些新的功能和組件,如分享功能組件,項目國際化功能;項目完成後,部署在nginx服務器,發現首次訪問的速度特別慢,嚴重的影響了用戶體驗,所以,我又開始進行了一系列的前端性能優化;以及將優化後的項目部署到nginx服務器二級子目錄的注意細節。javascript
用微信,微博等作網站的第三方登陸及用微信和支付寶進行支付,都須要註冊開發者帳號和添加網站應用,比較麻煩。另外,註冊的信息若是在前端頁面裏面進行公開,缺少安全性。第三方分享功能不須要註冊開發者帳號和添加網站應用,用戶信息相對保密,使用方法也相對簡單。css
封裝了8個經常使用的分享組件,包含仿簡書網站的底部和側欄分享組件、仿掘金網站分享組件、仿新浪網站分享組件和其餘一些網站橫向排列的分享組件。包含的分享渠道有:微信、微博、qq、qq空間、豆瓣等。html
分享效果如圖:前端
分享組件的封裝:share/index.vuevue
<template>
<div class="shareContainer" ref="shareContainer">
<el-row :gutter="20">
<el-col :span="6">
<heng-share @shareToQQ="shareToQQ" @shareToQQzone="shareToQQzone" @shareToWeibo="shareToWeibo" @shareToDouban="shareToDouban"></heng-share>
</el-col>
<el-col :span="6">
<invite-share @shareToQQ="shareToQQ" @shareToQQzone="shareToQQzone" @shareToWeibo="shareToWeibo" @shareToDouban="shareToDouban"></invite-share>
</el-col>
<el-col :span="6">
<jianshu-share @shareToWeixin="shareToWeixin" @shareToQQ="shareToQQ" @shareToQQzone="shareToQQzone" @shareToWeibo="shareToWeibo" @shareToDouban="shareToDouban"></jianshu-share>
</el-col>
<el-col :span="6">
<jianshu-left-share @shareToWeixin="shareToWeixin" @shareToQQ="shareToQQ" @shareToQQzone="shareToQQzone" @shareToWeibo="shareToWeibo" @shareToDouban="shareToDouban"></jianshu-left-share>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="6">
<info-share @shareToQQ="shareToQQ" @shareToQQzone="shareToQQzone" @shareToWeibo="shareToWeibo" @shareToDouban="shareToDouban"></info-share>
</el-col>
<el-col :span="6">
<juejin-share @shareToQQ="shareToQQ" @shareToQQzone="shareToQQzone" @shareToWeibo="shareToWeibo" @shareToDouban="shareToDouban"></juejin-share>
</el-col>
<el-col :span="6">
<sina-share @shareToQQ="shareToQQ" @shareToQQzone="shareToQQzone" @shareToWeibo="shareToWeibo" @shareToDouban="shareToDouban"></sina-share>
</el-col>
<el-col :span="6">
<yan-share @shareToQQ="shareToQQ" @shareToQQzone="shareToQQzone" @shareToWeibo="shareToWeibo" @shareToDouban="shareToDouban"></yan-share>
</el-col>
</el-row>
<wx-code-modal v-if="wxModal.show" :wxModal="wxModal" @hideWxCodeModal="hideWxCodeModal"></wx-code-modal>
</div>
</template>
複製代碼
分享方法的集合: utils/share.jsjava
import { weibo,qq,qqZone,douban,shareUrl,shareTitle } from "@/utils/env";
import * as mutils from "@/utils/mUtils";
function getParamsUrl(obj){
let paramsUrl = '';
for(let key in obj){
paramsUrl += key+'='+obj[key]+'&'
}
return paramsUrl;
}
export function shareConfig(type,obj){
let baseUrl = '';
if(mutils.isEmpty(obj)){
obj = {};
}
switch(type){
case 'weibo':
const weiboData = {
'url':shareUrl, // 內容連接,默認當前頁面location
'title':shareTitle, // 可選參數, 默認當前頁title
'pic':obj.pic || weibo.pic, // 分享圖片的路徑(可選),多張圖片經過"||"分開。
'count':'y', /**是否顯示分享數,y|n(可選)*/
'searchPic':true // 是否要自動抓取頁面上的圖片。true|falsetrue:自動抓取,false:不自動抓取。
}
baseUrl = weibo.weiboUrl+'?appkey='+weibo.weiboAppkey+getParamsUrl(weiboData);
window.open(baseUrl,'_blank');
break;
case 'qq':
const qqData = {
'url':shareUrl,
'title':shareTitle,
'pics':obj.pic || qq.pic, //QZone接口暫不支持發送多張圖片的能力,若傳入多張圖片,則會自動選入第一張圖片做爲預覽圖。
'source':obj.source || qq.source, // 分享來源
'desc':obj.desc || qq.desc,
'summary':obj.summary || qq.summary,
}
baseUrl = qq.baseUrl+'?'+getParamsUrl(qqData)
window.open(baseUrl,'_blank');
break;
case 'qqZone':
const qqZoneData = {
'url':shareUrl,
'title':shareTitle,
'pics':obj.pic || (qqZone.pic).split(','),
'sharesource':obj.sharesource || qqZone.sharesource, // 分享來源
'desc':obj.desc || qqZone.desc,
'summary':obj.summary || qqZone.summary,
}
baseUrl = qqZone.baseUrl+'?'+getParamsUrl(qqZoneData)
window.open(baseUrl,'_blank');
break;
case 'douban':
const doubanData = {
'href':shareUrl,
'name':shareTitle,
'image':obj.pic || douban.pic,
}
baseUrl = douban.baseUrl+'?'+getParamsUrl(doubanData)
window.open(baseUrl,'_blank');
break;
}
}
複製代碼
微博分享後的效果,如圖:linux
qq分享後的效果,如圖:webpack
因爲本項目須要多語言的支持,咱們須要作國際化。咱們使用 vue-i18n 來實現多語言的界面。ios
npm install vue-i18n --save
複製代碼
關於語言包,咱們有幾種方式:一種是每一個語言包一個獨立的js放到項目裏;或者將語言的對照寫在 .vue 文件裏, 或者加載遠程的JSON語言包 咱們的後臺界面須要支持的語言一般很少,更新也不會很是的頻繁,因此咱們將語言包放在項目裏,規劃項目目錄,增長 lang 目錄來存放語言對照。nginx
中文語言包配置:src/lang/zh.js
const zh = {
// layout
commons: {
xiaoai: '小愛',
admin: '管理員',
editor: '趙曉編',
quit: '退出',
hi: '您好',
index: '首頁',
userManage: '用戶管理',
share: '分享功能',
infoManage: '信息管理',
infoShow: '我的信息',
infoShow1: '我的信息子菜單1',
infoShow2: '我的信息子菜單2',
infoShow3: '我的信息子菜單3',
infoShow4: '我的信息子菜單4',
infoShow5: '我的信息子菜單5',
infoModify: '修改信息',
infoModify1:'修改信息子菜單1',
infoModify2:'修改信息子菜單2',
infoModify3:'修改信息子菜單3',
fundManage: '資金管理',
fundList: '資金流水',
chinaTabsList: '區域投資',
fundData: '資金數據',
fundPosition: '投資分佈',
typePosition: '項目分佈',
incomePayPosition: '收支分佈',
permission: '權限設置',
pagePer: '頁面權限',
directivePer: '按鈕權限',
errorPage: '錯誤頁面',
page401:'401',
page404:'404',
wechatNumber: '微信號'
},
index:{
yearLoss:'年度總盈虧',
yearProfit:'年度收益率',
potentialInvestor:'潛在投資人',
intentionInvestor:'意向投資人',
waitExamineInvestor:'待審投資人',
examiningInvestor:'審覈中投資人',
tenMillion:'千萬元',
person:'人'
}
}
export default zh;
複製代碼
英文語言包配置:src/lang/en.js
const zh = {
// layout
commons: {
xiaoai: 'Ai.',
admin: 'Admin',
editor: 'Editor',
quit: 'Sign Out',
hi: 'Hi',
index: 'Dashboard',
userManage: 'Users',
share: 'Share',
infoManage: 'Infos',
infoShow: 'InfoShow',
infoShow1: 'InfoShow1',
infoShow2: 'InfoShow2',
infoShow3: 'InfoShow3',
infoShow4: 'InfoShow4',
infoShow5: 'InfoShow5',
infoModify: 'InfoModify',
infoModify1:'InfoModify1',
infoModify2:'InfoModify2',
infoModify3:'InfoModify3',
fundManage: 'Money',
fundList: 'MoneyList',
chinaTabsList: 'AreaList',
fundData: 'FundData',
fundPosition: 'FundPosition',
typePosition: 'TypePosition',
incomePayPosition: 'IncomePayPosition',
permission: 'Permission',
pagePer: 'PagePermission',
directivePer: 'DirectivePermission',
errorPage: 'ErrorPage',
page401:'401',
page404:'404',
wechatNumber: 'wechat'
},
index:{
yearLoss:'Year Loss',
yearProfit:'Year Profit',
potentialInvestor:'Potential Investor',
intentionInvestor:'Intention Investor',
waitExamineInvestor:'Wait Examine Investor',
examiningInvestor:'Examining Investor',
tenMillion:'Ten Million',
person:'P'
}
}
export default zh;
複製代碼
導出配置:src/lang/index.js
// 引入i18n國際化插件
import { getToken} from '@/utils/auth'
import Vue from 'vue'
import VueI18n from 'vue-i18n'
process.env.NODE_ENV === "development" ? Vue.use(VueI18n) : null;
import enLocale from './en'
import zhLocale from './zh'
// 註冊i18n實例並引入語言文件,文件格式等下解析
const i18n = new VueI18n({
locale: getToken('lang') || 'en',
messages: {
zh: {
...zhLocale
},
en: {
...enLocale
},
}
});
export default i18n;
複製代碼
注意: locale: getToken('lang') || 'en',主要用來存儲已經點擊過的語言項,以便在項目刷新的時候,還可以拿到cookie中存儲的語言類別。
// i18n國際化
import i18n from "@/lang";
new Vue({
router,
store,
i18n, // 便於能夠直接在組件中經過this.$i18n使用,也能夠按需引用
render: h => h(App),
}).$mount('#app')
複製代碼
<div class='welcome'>
<span class="name">{{$t('commons.hi')}},</span>
<span class='name avatarname'> {{ $t(`commons.${name}`)}}</span>
</div>
複製代碼
注意:t(commons.${name}
),這是導入變量的方法。
效果如圖:
<el-submenu index="1" popper-class="langItem">
<template slot="title">
<img :src="langLogo" class='langAvatar' alt="">
</template>
<el-menu-item index="1-1" @click="changeLocale('zh')">
<img :src="chinaImg" class='langAvatar' alt="">
<span class="intro">中文</span>
</el-menu-item>
<el-menu-item index="1-2" @click="changeLocale('en')">
<img :src="americaImg" class='langAvatar' alt="">
<span class="intro">EngList</span>
</el-menu-item>
</el-submenu>
複製代碼
// 切換語言
changeLocale(type){
setToken('lang',type);
this.$i18n.locale = type;
if(type === 'en'){
this.langLogo = this.americaImg;
}else{
this.langLogo = this.chinaImg;
}
setToken('langLogo',this.langLogo);
}
複製代碼
詳細性能優化配置,請參考:vue.config.js
vue-cli 3.0 build包太大致使首屏加載過長,嚴重的影響了用戶體驗。所以,咱們須要從如下方面提供相應的解決方案。
可使得打包事後的文件不包含未壓縮的.map文件,減小壓縮後代碼體積。
緣由:「懶加載也叫延遲加載,即在須要的時候進行加載,隨用隨載。在單頁應用中,若是沒有應用懶加載,運用webpack打包後的文件將會異常的大,形成進入首頁時,須要加載的內容過多,延時過長,不利於用戶體驗,而運用懶加載則能夠將頁面進行劃分,須要的時候加載頁面,能夠有效的分擔首頁所承擔的加載壓力,減小首頁加載用時。」
vue-router配置路由 , 使用vue的異步組件技術 , 能夠實現按需加載 . 可是,這種狀況下一個組件生成一個js文件
代碼以下:
{
path: '/login',
name: 'login',
component:function(resolve){
require(['@/page/login.vue'],resolve)
}
}
複製代碼
代碼以下:
{
path: '/login',
name: 'login',
component:() => import('@/page/login')
}
複製代碼
這種狀況下,多個路由指定相同的chunkName,會合並打包成一個js文件。
代碼以下:
path: '/login',
name: 'login',
component: r => require.ensure([], () => r(require('@/page/login')), 'demo')
複製代碼
const CompressionPlugin = require("compression-webpack-plugin"); // gzip壓縮,優化http請求,提升加載速度
configureWebpack:config => {
// 爲生產環境修改配置...
if (process.env.NODE_ENV === 'production') {
// 開啓gzip壓縮
config.plugins.push(new CompressionPlugin({
algorithm: 'gzip',
test: new RegExp("\\.(" + ["js", "css"].join("|") + ")$"), // 匹配文件擴展名
// threshold: 10240, // 對超過10k的數據進行壓縮
threshold: 5120, // 對超過5k的數據進行壓縮
minRatio: 0.8,
cache: true, // 是否須要緩存
deleteOriginalAssets:false // true刪除源文件(不建議);false不刪除源文件
}))
}
}
複製代碼
注意:使用compression-webpack-plugin開啓服務器Gzip,因此也須要在服務端進行配置,以便可以解析gzip文件;nginx端配置以下:
gzip on;
gzip_comp_level 4;
gzip_buffers 4 16k;
gzip_types text/plain text/css application/javascript application/x-javascript text/xml application/xml application/xml+rss text/javascript;
gzip_min_length 1k;
gzip_http_version 1.1;
複製代碼
const TerserPlugin = require('terser-webpack-plugin')
configureWebpack:config => {
// 爲生產環境修改配置...
if (process.env.NODE_ENV === 'production') {
// 去除console來減小文件大小,效果同'UglifyJsPlugin'
new TerserPlugin({
cache: true,
parallel: true,
sourceMap: true, // Must be set to true if using source-maps in production
terserOptions: {
compress: {
warnings: false,
drop_console: true,
drop_debugger: true,
pure_funcs: ['console.log']
}
}
})
}
}
複製代碼
背景:在Vue項目中,引入到工程中的全部js、css文件,編譯時都會被打包進vendor.js,瀏覽器在加載該文件以後才能開始顯示首屏。如果引入的庫衆多,那麼vendor.js文件體積將會至關的大,影響首開的體驗。
解決方法:將引用的外部js、css文件剝離開來,不編譯到vendor.js中,而是用資源的形式引用,這樣瀏覽器可使用多個線程異步將vendor.js、外部的js等加載下來,達到加速首開的目的。
外部的庫文件,可使用CDN資源,或者別的服務器資源等。
步驟:
// 忽略生產環境打包的文件
config.externals = {
"vue": "Vue",
"vue-router": "VueRouter",
"vuex": "Vuex",
"vue-i18n": "VueI18n",
"axios": "axios",
'element-ui': 'ELEMENT',
'echarts':'echarts',
'mockjs':'Mock',
'nprogress':'NProgress',
'js-cookie':'Cookies'
}
複製代碼
具體cdn數據,請參考:www.bootcdn.cn/
const cdn = {
// 開發環境
dev: {
css: [],
js: []
},
// 生產環境
build: {
css: [
'https://cdn.bootcss.com/element-ui/2.11.1/theme-chalk/index.css',
'https://cdn.bootcss.com/nprogress/0.2.0/nprogress.min.css'
],
js: [
'https://cdn.bootcss.com/vue/2.6.10/vue.min.js',
'https://cdn.bootcss.com/vue-router/3.1.2/vue-router.min.js',
'https://cdn.bootcss.com/vuex/2.3.1/vuex.min.js',
'https://cdn.bootcss.com/axios/0.19.0/axios.min.js',
'https://cdn.bootcss.com/vue-i18n/8.13.0/vue-i18n.min.js',
'https://cdn.bootcss.com/element-ui/2.11.1/index.js',
'https://cdn.bootcss.com/echarts/3.8.5/echarts.min.js',
'https://cdn.bootcss.com/Mock.js/1.0.1-beta3/mock-min.js',
'https://cdn.bootcss.com/nprogress/0.2.0/nprogress.min.js',
'https://cdn.bootcss.com/js-cookie/2.2.0/js.cookie.min.js'
]
}
}
複製代碼
config
.plugin('html')
.tap(args => {
if (process.env.NODE_ENV === 'production') {
args[0].cdn = cdn.build
}
if (process.env.NODE_ENV === 'development') {
args[0].cdn = cdn.dev
}
return args
})
複製代碼
意思是隻在開發環境引入相關的包,生產環境用cdn外鏈。
process.env.NODE_ENV === "development" ? Vue.use(Router) : null;// router/index.js
process.env.NODE_ENV === "development" ? Vue.use(Vuex) : null;// store/index.js
process.env.NODE_ENV === "development" && import('nprogress/nprogress.css') // src/permission.js
process.env.NODE_ENV === "development" ? Vue.use(Mock) : null;//mockjs/index.js
process.env.NODE_ENV === "development" ? Vue.use(VueI18n) : null;//lang/index.js
複製代碼
效果如圖:
注意:使用cdn外鏈,減小打包文件體積;更多適用於在生產環境,而在開發環境,咱們還能夠繼續用之前的npm包。
性能優化先後的數據對比:
優化前:
優化後:
項目開發完成後,咱們將進行服務器的部署;部署分爲:部署在跟目錄和部署在子目錄,這兩種狀況,前端publicPath配置也是不同的,不然,服務器資源會顯示404,沒法加載。
注意:本項目服務器爲 windows系統,因此如下配置爲windows系統配置;若是你是linux系統服務器,請參考linux服務器部署配置。
準備工做:
下載網址:請參考;選擇該穩定版本下載;下載完成後,將該文件上傳到你的服務器目錄並解壓。如圖:
運行npm run build.將打包後的項目文件dist,複製到你的服務器目錄(個人dist文件目標爲:C:\ownprogram\vue\vue-touzi,個人nginx文件所在目標爲:C:\ownprogram\nginx-1.8.1);接下來,開始對nginx-1.8.1/config/nginx.config文件進行配置。
本項目部署的跟目錄爲:C:\ownprogram\vue\vue-touzi\dist\,默認爲80端口; vue.config.js中publicPath配置,以下:
module.exports = {
publicPath: process.env.NODE_ENV === "production" ? "./" : "/"
}
複製代碼
nginx.config,以下:
http {
include mime.types;
default_type application/octet-stream;
add_header Access-Control-Allow-Origin *;
add_header Access-Control-Allow-Headers X-Requested-With;
add_header Access-Control-Allow-Methods GET,POST,OPTIONS;
gzip on;
gzip_comp_level 4;
gzip_buffers 4 16k;
gzip_types text/plain text/css application/javascript application/x-javascript text/xml application/xml application/xml+rss text/javascript;
gzip_min_length 1k;
gzip_http_version 1.1;
server {
listen 80;
server_name localhost;
charset utf-8;
#access_log logs/host.access.log main;
location / {
root C:\ownprogram\vue\vue-touzi\dist\;
index index.html index.htm;
try_files $uri $uri/ /permission/index.html;
proxy_set_header Accept-Encoding 'gzip';
}
location /permission {
alias C:\ownprogram\vue\vue-touzi\dist\permission;
index index.html;
try_files $uri $uri/ /permission/index.html;
proxy_set_header Accept-Encoding 'gzip';
}
}
複製代碼
由於本項目C:\ownprogram\vue\vue-touzi\dist\默認爲跟目錄,屬於dist/permission及爲二級子目錄;
router/index.js配置,以下:
//註冊路由
export default new Router({
mode:'history', // 默認爲'hash'模式
base: '/permission/', // 添加跟目錄,對應服務器部署子目錄
routes: constantRouterMap
})
複製代碼
vue.config.js中publicPath配置,以下:
module.exports = {
publicPath: process.env.NODE_ENV === "production" ? "/permission/" : "/"
}
複製代碼
nginx.config,新增location配置,以下:
location /permission {
alias C:\ownprogram\vue\vue-touzi\dist\permission;
index index.html;
try_files $uri $uri/ /permission/index.html;
proxy_set_header Accept-Encoding 'gzip';
}
複製代碼
配置完成後,保存文件;重啓nginx便可進行正常訪問;
小愛ADMIN是徹底開源免費的管理系統集成方案,能夠直接應用於相關後臺管理系統模板;不少重點地方都作了詳細的註釋和解釋。若是你也同樣喜歡前端開發,歡迎加入咱們的討論/學習羣,羣內能夠提問答疑,分享學習資料; 歡迎加入答疑qq羣。