1. 程式人生 > >vue-小愛ADMIN系列文章(二):微信微博等分享,國際化,前端效能優化,nginx伺服器部署

vue-小愛ADMIN系列文章(二):微信微博等分享,國際化,前端效能優化,nginx伺服器部署

最近在做我的小愛ADMIN後臺管理系統,結合當前市場後臺管理系統對相關功能的需求,我又開始新增了一些新的功能和元件,如分享功能元件,專案國際化功能;專案完成後,部署在nginx伺服器,發現首次訪問的速度特別慢,嚴重的影響了使用者體驗,因此,我又開始進行了一系列的前端效能優化;以及將優化後的專案部署到nginx伺服器二級子目錄的注意細節。

  • 效果演示地址
  • github地址

分享功能

背景說明

用微信,微博等做網站的第三方登入及用微信和支付寶進行支付,都需要註冊開發者賬號和新增網站應用,比較麻煩。另外,註冊的資訊如果在前端頁面裡面進行公開,缺乏安全性。第三方分享功能不需要註冊開發者賬號和新增網站應用,使用者資訊相對保密,使用方法也相對簡單。

前端ui呈現和分享渠道

封裝了8個常用的分享元件,包含仿簡書網站的底部和側欄分享元件、仿掘金網站分享元件、仿新浪網站分享元件和其他一些網站橫向排列的分享元件。包含的分享渠道有:微信、微博、qq、qq空間、豆瓣等。

分享元件的封裝和分享方法的集合

分享效果如圖:

分享元件的封裝:share/index.vue

<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.js

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;
    }
}

微博分享後的效果,如圖:


qq分享後的效果,如圖:

Vue專案實現國際化(i18n)

背景說明

由於本專案需要多語言的支援,我們需要做國際化。我們使用 vue-i18n 來實現多語言的介面。

使用步驟

1.安裝vue-i18n

npm install vue-i18n --save

關於語言包,我們有幾種方式:一種是每個語言包一個獨立的js放到專案裡;或者將語言的對照寫在 .vue 檔案裡, 或者載入遠端的JSON語言包
我們的後臺介面需要支援的語言通常不多,更新也不會非常的頻繁,所以我們將語言包放在專案裡,規劃專案目錄,增加 lang 目錄來存放語言對照。

2.新建lang資料夾,用於儲存語言包並匯出

中文語言包配置: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中儲存的語言類別。

3.在專案入口檔案main.js中引入i18n

// i18n國際化
import i18n from "@/lang";
new Vue({
  router,
  store,
  i18n,  // 便於可以直接在元件中通過this.$i18n使用,也可以按需引用
  render: h => h(App),
}).$mount('#app')

4.頁面元件中使用方法$t()

<div class='welcome'>
    <span class="name">{{$t('commons.hi')}},</span>
    <span class='name avatarname'> {{ $t(`commons.${name}`)}}</span>
</div>

注意:$t('commons.hi'),這是直接匯入的方法;$t(commons.${name}),這是匯入變數的方法。

5.點選切換語言方法

效果如圖:

 <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);
}

基於cli3.0的vue專案的效能優化

詳細效能優化配置,請參考:vue.config.js

vue-cli 3.0 build包太大導致首屏載入過長,嚴重的影響了使用者體驗。因此,我們需要從以下方面提供相應的解決方案。

1.productionSourceMap:false

可以使得打包過後的檔案不包含未壓縮的.map檔案,減少壓縮後代碼體積。

2.專案中引入圖片壓縮(圖片線上免費壓縮網站 https://www.yasuotu.com/)或者將圖片放到cdn上面進行引用。

3.路由懶載入

原因:“懶載入也叫延遲載入,即在需要的時候進行載入,隨用隨載。在單頁應用中,如果沒有應用懶載入,運用webpack打包後的檔案將會異常的大,造成進入首頁時,需要載入的內容過多,延時過長,不利於使用者體驗,而運用懶載入則可以將頁面進行劃分,需要的時候載入頁面,可以有效的分擔首頁所承擔的載入壓力,減少首頁載入用時。”

方法一:resolve

vue-router配置路由 , 使用vue的非同步元件技術 , 可以實現按需載入 .
但是,這種情況下一個元件生成一個js檔案

程式碼如下:

{
  path: '/login',
  name: 'login',
  component:function(resolve){
     require(['@/page/login.vue'],resolve)
  }
}

方法二:官網方法 import()

程式碼如下:

{ 
    path: '/login',
    name: 'login',
    component:() => import('@/page/login')
}

方法三:webpack的require,ensure()

這種情況下,多個路由指定相同的chunkName,會合並打包成一個js檔案。

程式碼如下:

  path: '/login',
  name: 'login',
  component: r => require.ensure([], () => r(require('@/page/login')), 'demo')

4. 伺服器開啟Gzip(compression-webpack-plugin)

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;

5. 生成環境剔除debuger和console(TerserPlugin/UglifyJsPlugin)

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']
          }
        }
      })
    }
}

6. 啟用CDN加速(configureWebpack,config.externals)

背景:在Vue專案中,引入到工程中的所有js、css檔案,編譯時都會被打包進vendor.js,瀏覽器在載入該檔案之後才能開始顯示首屏。若是引入的庫眾多,那麼vendor.js檔案體積將會相當的大,影響首開的體驗。

解決方法:將引用的外部js、css檔案剝離開來,不編譯到vendor.js中,而是用資源的形式引用,這樣瀏覽器可以使用多個執行緒非同步將vendor.js、外部的js等載入下來,達到加速首開的目的。

外部的庫檔案,可以使用CDN資源,或者別的伺服器資源等。

步驟:

(1).配置要忽略的生產環境下被打包的檔案

// 忽略生產環境打包的檔案
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'
}

(2).定義不同環境下的cdn資料

具體cdn資料,請參考:https://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'
      ]
  }
}

(3).新增CDN引數到htmlWebpackPlugin配置中

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
})

(4).更改專案Router,Vuex,nprogress,mock,VueI18n等引入方式

意思是隻在開發環境引入相關的包,生產環境用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包。

7.首屏加個loading小菊花動畫;

效能優化前後的資料對比:

優化前:

優化後:

nginx伺服器部署

專案開發完成後,我們將進行伺服器的部署;部署分為:部署在跟目錄和部署在子目錄,這兩種情況,前端publicPath配置也是不一樣的,否則,伺服器資源會顯示404,無法載入。

注意:本專案伺服器為 windows系統,所以以下配置為windows系統配置;如果你是linux系統伺服器,請參考linux伺服器部署配置。

準備工作:

下載nginx伺服器;

下載網址:請參考;選擇該穩定版本下載;下載完成後,將該檔案上傳到你的伺服器目錄並解壓。如圖:

執行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即可進行正常訪問;

nginx常用命令如下

  • 啟動服務:start nginx
  • 配置檔案修改重啟服務:nginx -s reload
  • 快速停止或關閉Nginx:nginx -s stop
  • 正常停止或關閉Nginx:nginx -s quit
  • 檢視Nginx的版本號:nginx -V
  • 檢視windows工作管理員下Nginx的程序命令:tasklist /fi "imagename eq nginx.exe"

專案說明

小愛ADMIN是完全開源免費的管理系統整合方案,可以直接應用於相關後臺管理系統模板;很多重點地方都做了詳細的註釋和解釋。如果你也一樣喜歡前端開發,歡迎加入我們的討論/學習群,群內可以提問答疑,分享學習資料; 歡迎加入答疑qq群。

技術答疑

相關推薦

no