Vue、Node全棧專案~面向小白的部落格系統~

漆黑之牙·發表於2020-11-03
個人部落格系統

前言

程式碼質量問題輕點噴(去年才學的前端),有啥建議歡迎聯絡我,聯絡方式見最下方,感謝!

頁面有啥bug也可以反饋給我,感謝! 這是一套包含前後端程式碼的個人部落格系統,歡迎各位提出建議,本來打算用nuxt來書寫,但是想學了react之後用next來寫,後面會用reactSSR來重構!

部落格該有的功能都有,可以寫文章,可以評論,可以留言,甚至可以玩遊戲(雖然還沒完善),等等讓你來發現。

這是一個小型的全棧專案,主要是檢驗並記錄一下自己的學習成果

運用的技術Vue、Vue-Cli、Element-Ui、後端NodeJs、資料庫MySql等各種技術

看到這裡了,請你們不要白嫖,點個star(O(∩_∩)O)

前臺頁面

  • 頁面設計 借鑑開源專案「白茶」

「支援PC端和移動端,響應式頁面,更多頁面戲節讓你去發掘」

1. 主頁

2. 文章列表

3. 聽雨

4. Demo

5. 關於我

6. 留言板

頁面詳情看這裡

後臺頁面

  1. 主頁
  2. 釋出文章
  3. 關於我
  4. 評論列表
  5. 設定

其他的例如文章列表等我後面會別補上,「主要是想讓你們研究透程式碼,自己去寫,因為真的很簡單(手動狗頭)」

頁面詳情看這裡

後端相關

  1. 資料庫操作使用 sequelize
  2. 後端框架使用 koa

「sequelize」「koa」在此專案中的使用有機會我會寫成對應的文章出來

資料庫表結構

「這個部落格裡本來打算實現文章分類功能,後面感覺不需要,就沒用到這個表了」

  1. 使用者表 ID 使用者名稱 使用者賬號 使用者密碼 生日 頭像 建立時間
  2. 文章表 ID 使用者ID 文章標題 文章圖片 文章music 文章內容 分類ID(外來鍵) 文章摘要 釋出時間 瀏覽人數 喜歡人數 評論數(通過文章留言板去查) 分類ID
  3. 文章留言表 ID 留言文章ID 留言使用者ID 留言內容 留言時間
  4. 分類表 ID 分類名稱
  5. 留言表 ID 留言使用者ID 留言內容
  6. 使用者是否喜歡文章表 ID 使用者ID 文章ID (通過多對多關係生產)

表關係

  1. 一個分類有多個文章(一對多)
  2. 一篇文章可以有多個評論(一對多)
  3. 一個使用者可以評論多篇文章(一對多)
  4. 一個使用者可以留言多次(一對多)
  5. 一篇文章可以被多個使用者喜歡,一個使用者可以喜歡多個文章(多對多)

程式碼目錄介紹及執行介紹

後端程式碼採用三層架構模式

models 是表現層

services 是業務邏輯層

routes 是資料訪問層

  1. 「backstage」 是後臺管理系統
  2. 「client」 是前端

「拉取程式碼後先在根目錄和client資料夾裡面和backstage都執行 npm install安裝依賴」

「之後首先要在跟目錄建立public和encrypt資料夾」

「public資料夾裡面要建立upload資料夾,上傳檔案需要,打包前端程式碼也是打包到這個目錄」

「encrypt裡面要建立dbEncrypt.js和ossEncrypt.js」

可隨時在models/tables/db.js裡面切換
本地開發就用localDbInfo裡面的,注意要在db.js切換

——————————資料庫請自行安裝————————————

dbEncrypt.js
    module.exports = {
    localDbInfo: {
        dbName: 'XXX', // 資料庫名稱
        userName: 'XXX', // 資料庫使用者名稱
        password: 'XXX', // 資料庫密碼
        host: {
            host: 'localhost',
            dialect: 'mysql'
        }
    },
    aliDbInfo: { // 伺服器上的資料庫資訊,同上
        dbName: 'XXX',
        userName: 'XXX',
        password: 'XXX',
        host: {
            host: 'XXX',
            dialect: 'mysql'
        }
    }
}
如果沒有伺服器,本地開發的話就不需要這個了
我寫了兩個上傳檔案的介面,/upload和/ossUpload,沒有伺服器就切換成/upload就行了
ossEncrypt.js
    module.exports = {
        region: 'XXX', // OSS region
        accessKeyId: 'XXXXXX', OSS accessKeyId
        accessKeySecret: 'XXXXXX', OSS accessKeySecret
        bucket: 'XXXX', // OSS bucket名
    }

接下來就可以愉快的玩耍了

「確保資料庫資訊填寫正確之後 在根目錄輸入在 npm sync 同步資料庫,提示同步完成即可」 「然後輸入npm start 開啟服務,這樣本地服務就跑起來了」 「接下來就跟平時開發一樣跑前端就行了,進入client或者backstage 輸入 npm run dev 進入開發模式」

部署

首先你得有一個自己的伺服器,我用的是阿里雲

這裡推薦一個我用的伺服器工具 xShell和xFtp

然後點選右邊的「免費授權頁面」填寫相應資料即可下載

  1. 首先需要安裝「nodeJs」,百度安裝方式很多,請自選其一,安裝完成後node -v,能列印出版本代表安裝完成, 安裝完成後設定為淘寶映象:「npm config set registry https://registry.npm.taobao.org」

    選裝[「nvm」(node管理工具), 「git」]

  2. nginx 安裝可以看這裡,或者百度教程很多

  3. 「mysql」 百度一下,你就知道~~

  4. navicat(資料庫操作工具), 可以使用「navicat序號產生器」破解,教程點此

  5. 「開啟navicat連線上伺服器的資料庫,連線成功即可」

  6. 「pm2」(PM2是node程式管理工具,可以利用它來簡化很多node應用管理的繁瑣任務,如效能監控、自動重啟、負載均衡等,而且使用非常簡單)

    安裝命令:npm install pm2@latest -g

    pm2 -v列印版本即安裝成功

  7. 開啟伺服器和阿里雲上相應的埠(3306,5008,80)

  8. 「nginx配置」

    為什麼是5008,因為我koa監聽的是5008埠

    這裡給出我的nginx配置

    user  root root;
    worker_processes auto;
    error_log  /www/logs/nginx_error.log  crit;
    pid        /www/server/nginx/logs/nginx.pid;
    worker_rlimit_nofile 51200;
    events
        {
            use epoll;
            worker_connections 51200;
            multi_accept on;
        }

    http
        {
            include       mime.types;
            #include luawaf.conf;

        # 設定快取路徑並且使用一塊最大100M的共享記憶體,用於硬碟上的檔案索引,包括檔名和請求次數,每個檔案在1天內若不活躍(無請求)則從硬碟上淘汰,硬碟快取最大10G,滿了則根據LRU演算法自動清除快取。
        proxy_cache_path /var/cache/nginx/cache levels=1:2 keys_zone=imgcache:100m inactive=1d max_size=10g;

     include proxy.conf;

        default_type  application/octet-stream;
        
        server_names_hash_bucket_size 512;
        client_header_buffer_size 32k;
        large_client_header_buffers 4 32k;
        client_max_body_size 100m;

        sendfile   on;
        tcp_nopush on;

        keepalive_timeout 60;

        tcp_nodelay on;

        fastcgi_connect_timeout 300;
        fastcgi_send_timeout 300;
        fastcgi_read_timeout 300;
        fastcgi_buffer_size 64k;
        fastcgi_buffers 4 64k;
        fastcgi_busy_buffers_size 128k;
        fastcgi_temp_file_write_size 256k;
     fastcgi_intercept_errors on;
        
        #開啟和關閉gzip模式
        gzip on;
        #gizp壓縮起點,檔案大於1k才進行壓縮
        gzip_min_length  1k;
        # 設定壓縮所需要的緩衝區大小,以4k為單位,如果檔案為7k則申請2*4k的緩衝區 
        gzip_buffers     4 16k;
        #nginx對於靜態檔案的處理模組,開啟後會尋找以.gz結尾的檔案,直接返回,不會佔用cpu進行壓縮,如果找不到則不進行壓縮
        gzip_static on;
        # 識別http協議的版本,早起瀏覽器可能不支援gzip自解壓,使用者會看到亂碼
        gzip_http_version 1.1;
        # gzip 壓縮級別,1-9,數字越大壓縮的越好,也越佔用CPU時間
        gzip_comp_level 1;
        # 進行壓縮的檔案型別。
        gzip_types     text/plain application/json application/javascript application/x-javascript text/javascript text/css application/xml image/jpeg image/gif image/png video/mpeg audio/x-pn-realaudio audio/x-midi audio/basic audio/mpeg audio/ogg audio/* video/mp4;
        # 啟用應答頭"Vary: Accept-Encoding"
        gzip_vary on;
        # nginx做為反向代理時啟用,off(關閉所有代理結果的資料的壓縮),expired(啟用壓縮,如果header頭中包括"Expires"頭資訊),no-cache(啟用壓縮,header頭中包含"Cache-Control:no-cache"),no-store(啟用壓縮,header頭中包含"Cache-Control:no-store"),private(啟用壓縮,header頭中包含"Cache-Control:private"),no_last_modefied(啟用壓縮,header頭中不包含"Last-Modified"),no_etag(啟用壓縮,如果header頭中不包含"Etag"頭資訊),auth(啟用壓縮,如果header頭中包含"Authorization"頭資訊)
        gzip_proxied   expired no-cache no-store private auth;
        # (IE5.5和IE6 SP1使用msie6引數來禁止gzip壓縮 )指定哪些不需要gzip壓縮的瀏覽器(將和User-Agents進行匹配),依賴於PCRE庫
        gzip_disable   "MSIE [1-6]\.";

        limit_conn_zone $binary_remote_addr zone=perip:10m;
     limit_conn_zone $server_name zone=perserver:10m;

        server_tokens off;
        access_log off;

        # 是否啟用在on-the-fly方式壓縮檔案,啟用後,將會在響應時對檔案進行壓縮並返回。
        brotli on;
        # 啟用後將會檢查是否存在帶有br擴充套件的預先壓縮過的檔案。如果值為always,則總是使用壓縮過的檔案,而不判斷瀏覽器是否支援。
        brotli_static always;
        # 設定壓縮質量等級。取值範圍是0到11.
        brotli_comp_level 6;
        # 設定緩衝的數量和大小。大小預設為一個記憶體頁的大小,也就是4k或者8k。
        brotli_buffers 16 8k;
        # 設定需要進行壓縮的最小響應大小。
        brotli_min_length 20;
        # 指定對哪些內容編碼型別進行壓縮。text/html內容總是會被進行壓縮
        brotli_types text/plain application/json application/javascript application/x-javascript text/javascript text/css application/xml image/jpeg image/gif image/png video/mpeg audio/x-pn-realaudio audio/x-midi audio/basic audio/mpeg audio/ogg audio/* video/mp4;
    server {
        listen    80;
        # 您的域名
        server_name xxxxxx.xxx; 
        location ^~ / {
            proxy_cache imgcache;
            proxy_cache_key $scheme$proxy_host$uri$is_args$args;
            proxy_cache_valid  200 304 302 24h;   
            proxy_pass http://www.域名:5008;
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;    
            proxy_set_header Host $http_host
            proxy_set_header X-Nginx-Proxy true;
            proxy_redirect off;
            rewrite ^.+(?<!js|css|png|map)$ /index.html break;
            autoindex on;
            index index.htm index.html;
            set $fallback_uri /index.html;
            if ($http_accept !~ text/html) {
                set $fallback_uri /null;
            }
            try_files $uri $uri$fallback_uri = 404;
        }
        location ^~ /pc {
            proxy_pass http://www.域名:5008/back;
            index index.htm index.html;
        }
    }
    include /www/server/panel/vhost/nginx/*.conf;
    }

一切準備工作已就緒,可以把專案部署到伺服器上了

「執行 npm run build 命令分別打包前臺和後臺」 執行打包後的結構是這樣的

接下來把專案(除client和backstage和node_modules以外)放置到伺服器的dist資料夾中(然後打該目錄執行「npm i」下載依賴)

由於對nginx不熟悉,所以我這裡public檔案放置得做一些改動,熟悉的可以自行配置(順便教下我。。)

就是把pc資料夾裡面的放置放到同級

皆大歡喜,做到這裡就完成了。接下使用 域名就能訪問啦

例如我的部落格

日誌記錄

放置到伺服器後,出了問題,肯定不像本地開發除錯一樣方便,所以我們需要「日誌記錄」,來定位問題

我採用的庫是 log4js打不開就得翻牆,我只記錄了介面呼叫記錄,需要sql呼叫記錄的可以自行加上 npm i koa-log4

const log4js = require('koa-log4')
const path = require('path')

log4js.configure({
    appenders: {
        api: {
            type'dateFile',
            filename: path.resolve(__dirname, 'logs''api''logging.log'),
            maxLogSize: 1024 * 1024, // 配置檔案的最大位元組數
            keepFileExt: 3, // 最多儲存3天
            layout: {
                type'pattern',
                pattern: '%c [%d{yyyy-MM-dd hh:mm:ss}] [%p]:%m%n'
            }
        },
        default: {
            type'stdout'
        }
    },
    categories: {
        api: {
            appenders: ['api'],
            level: 'all'
        },
        default: {
            appenders: ['default'],
            level: 'all'
        }
    }
})

process.on("exit", () => {
    log4js.shutdown()
})

const apiLogger = log4js.getLogger("api")

exports.apiLogger = apiLogger

然後建立一箇中介軟體

// apiLoggerMiddleware.js
const { apiLogger } = require('../logger'

// 處理錯誤的中介軟體
module.exports = async (ctx, next) => {
    try {
        await next();
    }
    finally {
        apiLogger.debug(`${ctx.method} ${ctx.path} ${JSON.stringify(ctx.body)}`)
    }

};

//init.js
app.use(require('./apiLoggerMiddleware')) // API請求日誌

首頁載入速度優化

用vue-cli正常打包後,會生成「多個chunk-hash.js 和 chunk-hash.css」,些許增加訪問速度,所以我們需要把對應和合為一個

合併chunk-hash.js

在所有非同步元件前增加以下程式碼,目的是把打包的chunk統一

合併chunk-hash.css

vue.config.js

module.exports = {
 configureWebpack: config => {
     // 公共程式碼抽離
      config.optimization.splitChunks.cacheGroups = {
        vendor: {
          chunks: 'all',
          test: /node_modules/,
          name: 'vendor',
          minChunks: 1,
          maxInitialRequests: 5,
          minSize: 0,
          priority: 100
        },
        common: {
          chunks: 'all',
          test: /[\\/]src[\\/]js[\\/]/,
          name: 'common',
          minChunks: 2,
          maxInitialRequests: 5,
          minSize: 0,
          priority: 60
        },
        styles: {
          name: 'styles',
          test: /\.(le|sa|sc|c)ss$/,
          chunks: 'all',
          reuseExistingChunk: true,
          minChunks: 1,
          enforce: true
        }
      }
    }
}

index.html

「XXX是我的名字」

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="description" content="XXX,一名前端工程師,這是我的個人部落格,網站文章隨便寫,想寫啥寫啥">
    <meta name="keywords" content="個人部落格,XXX,前端,技術,WEB,blog,BLOG,搭建部落格,前端技術,VUE部落格,XXX的部落格">
    <meta name="anthor" content="XXX,123456789@qq.com">
    <meta name="robots" content="部落格, 前端, blog, 個人部落格, XXX, Yong,XXX的部落格,web,VUE,React">
    <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no">
    <link rel="icon" href="https://qiheizhiya.oss-cn-shenzhen.aliyuncs.com/image/favicon.ico">
    <!-- 使用CDN的CSS檔案 -->
    <% for (var i in htmlWebpackPlugin.options.cdn &&
    htmlWebpackPlugin.options.cdn.css) { %>
    <link
            href="<%= htmlWebpackPlugin.options.cdn.css[i] %>"
            rel="stylesheet"
    />
    <% } %>
    <title>漆黑之牙</title>
  </head>
  <body>
    <noscript>
      <strong>We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
    </noscript>
    <div id="app"></div>
    <!-- built files will be auto injected -->

    <!-- 使用CDN的JS檔案 -->
    <% for (var i in htmlWebpackPlugin.options.cdn &&
    htmlWebpackPlugin.options.cdn.js) { %>
    <script src="<%= htmlWebpackPlugin.options.cdn.js[i] %>"></script>
    <% } %>
    <!-- 使用CDN的JS檔案 -->
  </body>
</html>

cdn

vue.config.js

const isProduction = process.env.NODE_ENV === 'production';
const cdn = {
  externals: {
    'vue''Vue',
    'vuex''Vuex',
    'vue-router''VueRouter',
    'axios''axios',
    "element-ui""ELEMENT",
  },
  css: [
    'https://lib.baomitu.com/element-ui/2.13.2/theme-chalk/index.css'
  ],
  js: [
    'https://cdn.bootcss.com/vue/2.6.10/vue.min.js',
    'https://cdn.bootcss.com/vue-router/3.1.3/vue-router.min.js',
    'https://cdn.bootcss.com/vuex/3.1.2/vuex.min.js',
    'https://lib.baomitu.com/element-ui/2.13.2/index.js',
    'https://cdn.bootcss.com/axios/0.19.2/axios.min.js'
  ]
}
module.exports = {
 chainWebpack: config => {
    // 注入cdn
    config.plugin('html').tap(args => {
      // 生產環境或本地需要cdn時,才注入cdn
      if (isProduction) { args[0].cdn = cdn }
      return args
    })
  },
  configureWebpack: config => {
   config.externals = cdn.externals
  }
}

前端gzip

npm i -d compression-webpack-plugin

vue.config.js

//也是在configureWebpack中
//gzip壓縮
  config.plugins.push(new CompressionPlugin({
    filename: '[path].gz[query]',
    //壓縮演算法
    algorithm: 'gzip',
    //匹配檔案
    test: /\.js$|\.css$|\.html$|\.woff$|\.ttf$|\.eot$|/,
    //壓縮超過此大小的檔案,以位元組為單位
    threshold: 1024,
    minRatio: 0.8,
    //刪除原始檔案只保留壓縮後的檔案
    deleteOriginalAssets: isProduction
  }))

「完整配置請自行看client中的vue.config.js」

優化前:1M頻寬才首屏載入「10多秒」

優化後:正常情況下首屏載入「1,2秒」

最後

不知不覺寫的太多了

專案無模板,純手寫

分享自己的一個全棧簡單專案給大家,有什麼建議/bug/優化可以提一下,感謝!!。

如果看到這裡,就請幫忙點個star吧!!

喜歡技術的也可以加我,一起進步。

郵箱: 953136447@qq.com

微訊號:qwer880620

# <center>個人部落格系統</center>
## 前言> 程式碼質量問題輕點噴(去年才學的前端),有啥建議歡迎聯絡我,聯絡方式見最下方,感謝!>> 頁面有啥bug也可以反饋給我,感謝!這是一套包含前後端程式碼的個人部落格系統,歡迎各位提出建議,本來打算用nuxt來書寫,但是想學了react之後用next來寫,後面會用reactSSR來重構!
部落格該有的功能都有,可以寫文章,可以評論,可以留言,甚至可以玩遊戲(雖然還沒完善),等等讓你來發現。
這是一個小型的全棧專案,主要是檢驗並記錄一下自己的學習成果
運用的技術Vue、Vue-Cli、Element-Ui、後端NodeJs、資料庫MySql等各種技術
看到這裡了,請你們不要白嫖,點個[star(O(∩_∩)O)](https://github.com/qiheizhiya/myBlog)
### 前臺頁面- 頁面設計 借鑑開源專案**白茶**  **支援PC端和移動端,響應式頁面,更多頁面戲節讓你去發掘**
1. 主頁
2. 文章列表
3. 聽雨
4. Demo
5. 關於我
6. 留言板
頁面詳情看[這裡](http://www.llongjie.top)### 後臺頁面1. 主頁2. 釋出文章3. 關於我4. 評論列表5. 設定   其他的例如文章列表等我後面會別補上,**主要是想讓你們研究透程式碼,自己去寫,因為真的很簡單(手動狗頭)**
頁面詳情看[這裡](http://www.llongjie.top/pc)### 後端相關1. 資料庫操作使用 [sequelize](https://github.com/demopark/sequelize-docs-Zh-CN)2. 後端框架使用 [koa](https://www.w3cways.com/doc/koa/)
**sequelize**和**koa**在此專案中的使用有機會我會寫成對應的文章出來
> 資料庫表結構>**這個部落格裡本來打算實現文章分類功能,後面感覺不需要,就沒用到這個表了**1. 使用者表 ID 使用者名稱 使用者賬號 使用者密碼 生日  頭像  建立時間    2. 文章表 ID 使用者ID 文章標題 文章圖片 文章music 文章內容 分類ID(外來鍵) 文章摘要 釋出時間  瀏覽人數 喜歡人數 評論數(通過文章留言板去查) 分類ID  3. 文章留言表  ID 留言文章ID 留言使用者ID  留言內容 留言時間4. 分類表 ID 分類名稱 5. 留言表 ID 留言使用者ID 留言內容6. 使用者是否喜歡文章表 ID 使用者ID 文章ID (通過多對多關係生產)
> 表關係1. 一個分類有多個文章(一對多)2. 一篇文章可以有多個評論(一對多)3. 一個使用者可以評論多篇文章(一對多)4. 一個使用者可以留言多次(一對多)5. 一篇文章可以被多個使用者喜歡,一個使用者可以喜歡多個文章(多對多)   ### 程式碼目錄介紹及執行介紹後端程式碼採用三層架構模式> models 是表現層> > services 是業務邏輯層> > routes 是資料訪問層>1.  **backstage** 是後臺管理系統2.  **client** 是前端

**拉取程式碼後先在根目錄和client資料夾裡面和backstage都執行 npm install安裝依賴**
**之後首先要在跟目錄建立public和encrypt資料夾**
**public資料夾裡面要建立upload資料夾,上傳檔案需要,打包前端程式碼也是打包到這個目錄**
**encrypt裡面要建立dbEncrypt.js和ossEncrypt.js** 
``` 可隨時在models/tables/db.js裡面切換本地開發就用localDbInfo裡面的,注意要在db.js切換
——————————資料庫請自行安裝————————————
dbEncrypt.js    module.exports = {    localDbInfo: {        dbName: 'XXX', // 資料庫名稱        userName: 'XXX', // 資料庫使用者名稱        password: 'XXX', // 資料庫密碼        host: {            host: 'localhost',            dialect: 'mysql'        }    },    aliDbInfo: { // 伺服器上的資料庫資訊,同上        dbName: 'XXX',        userName: 'XXX',        password: 'XXX',        host: {            host: 'XXX',            dialect: 'mysql'        }    }}``````如果沒有伺服器,本地開發的話就不需要這個了我寫了兩個上傳檔案的介面,/upload和/ossUpload,沒有伺服器就切換成/upload就行了ossEncrypt.js    module.exports = {        region: 'XXX', // OSS region        accessKeyId: 'XXXXXX', OSS accessKeyId        accessKeySecret: 'XXXXXX', OSS accessKeySecret        bucket: 'XXXX', // OSS bucket名    }```
接下來就可以愉快的玩耍了
**確保資料庫資訊填寫正確之後 在根目錄輸入在 npm sync 同步資料庫,提示同步完成即可****然後輸入npm start 開啟服務,這樣本地服務就跑起來了****接下來就跟平時開發一樣跑前端就行了,進入client或者backstage 輸入 npm run dev 進入開發模式**
### 部署首先你得有一個自己的伺服器,我用的是阿里雲
這裡推薦一個我用的伺服器工具[xShell和xFtp](https://www.netsarang.com/zh/xshell-download/) 
然後點選右邊的**免費授權頁面**填寫相應資料即可下載
1. 首先需要安裝**nodeJs**,百度安裝方式很多,請自選其一,安裝完成後node -v,能列印出版本代表安裝完成, 安裝完成後設定為淘寶映象:**npm config set registry https://registry.npm.taobao.org**> 選裝[**nvm**(node管理工具), **git**]2. **[nginx](http://www.llongjie.top/detail/55)** 安裝可以看這裡,或者百度教程很多 3. **mysql** 百度一下,你就知道~~4. **[navicat](http://www.navicat.com.cn/)**(資料庫操作工具), 可以使用**navicat序號產生器**破解,教程點此5. **開啟navicat連線上伺服器的資料庫,連線成功即可**6. **pm2**(PM2是node程式管理工具,可以利用它來簡化很多node應用管理的繁瑣任務,如效能監控、自動重啟、負載均衡等,而且使用非常簡單)> 安裝命令:npm install pm2@latest -g        > pm2 -v列印版本即安裝成功7. 開啟伺服器和阿里雲上相應的埠(3306,5008,80)8. **nginx配置**
為什麼是5008,因為我koa監聽的是5008埠    > 這裡給出我的nginx配置```    user  root root;    worker_processes auto;    error_log  /www/logs/nginx_error.log  crit;    pid        /www/server/nginx/logs/nginx.pid;    worker_rlimit_nofile 51200;    events        {            use epoll;            worker_connections 51200;            multi_accept on;        }
    http        {            include       mime.types;            #include luawaf.conf;
        # 設定快取路徑並且使用一塊最大100M的共享記憶體,用於硬碟上的檔案索引,包括檔名和請求次數,每個檔案在1天內若不活躍(無請求)則從硬碟上淘汰,硬碟快取最大10G,滿了則根據LRU演算法自動清除快取。        proxy_cache_path /var/cache/nginx/cache levels=1:2 keys_zone=imgcache:100m inactive=1d max_size=10g;
include proxy.conf;
        default_type  application/octet-stream;                server_names_hash_bucket_size 512;        client_header_buffer_size 32k;        large_client_header_buffers 4 32k;        client_max_body_size 100m;
        sendfile   on;        tcp_nopush on;
        keepalive_timeout 60;
        tcp_nodelay on;
        fastcgi_connect_timeout 300;        fastcgi_send_timeout 300;        fastcgi_read_timeout 300;        fastcgi_buffer_size 64k;        fastcgi_buffers 4 64k;        fastcgi_busy_buffers_size 128k;        fastcgi_temp_file_write_size 256k;fastcgi_intercept_errors on;                #開啟和關閉gzip模式        gzip on;        #gizp壓縮起點,檔案大於1k才進行壓縮        gzip_min_length  1k;        # 設定壓縮所需要的緩衝區大小,以4k為單位,如果檔案為7k則申請2*4k的緩衝區         gzip_buffers     4 16k;        #nginx對於靜態檔案的處理模組,開啟後會尋找以.gz結尾的檔案,直接返回,不會佔用cpu進行壓縮,如果找不到則不進行壓縮        gzip_static on;        # 識別http協議的版本,早起瀏覽器可能不支援gzip自解壓,使用者會看到亂碼        gzip_http_version 1.1;        # gzip 壓縮級別,1-9,數字越大壓縮的越好,也越佔用CPU時間        gzip_comp_level 1;        # 進行壓縮的檔案型別。        gzip_types     text/plain application/json application/javascript application/x-javascript text/javascript text/css application/xml image/jpeg image/gif image/png video/mpeg audio/x-pn-realaudio audio/x-midi audio/basic audio/mpeg audio/ogg audio/* video/mp4;        # 啟用應答頭"Vary: Accept-Encoding"        gzip_vary on;        # nginx做為反向代理時啟用,off(關閉所有代理結果的資料的壓縮),expired(啟用壓縮,如果header頭中包括"Expires"頭資訊),no-cache(啟用壓縮,header頭中包含"Cache-Control:no-cache"),no-store(啟用壓縮,header頭中包含"Cache-Control:no-store"),private(啟用壓縮,header頭中包含"Cache-Control:private"),no_last_modefied(啟用壓縮,header頭中不包含"Last-Modified"),no_etag(啟用壓縮,如果header頭中不包含"Etag"頭資訊),auth(啟用壓縮,如果header頭中包含"Authorization"頭資訊)        gzip_proxied   expired no-cache no-store private auth;        # (IE5.5和IE6 SP1使用msie6引數來禁止gzip壓縮 )指定哪些不需要gzip壓縮的瀏覽器(將和User-Agents進行匹配),依賴於PCRE庫        gzip_disable   "MSIE [1-6]\.";
        limit_conn_zone $binary_remote_addr zone=perip:10m;limit_conn_zone $server_name zone=perserver:10m;
        server_tokens off;        access_log off;
        # 是否啟用在on-the-fly方式壓縮檔案,啟用後,將會在響應時對檔案進行壓縮並返回。        brotli on;        # 啟用後將會檢查是否存在帶有br擴充套件的預先壓縮過的檔案。如果值為always,則總是使用壓縮過的檔案,而不判斷瀏覽器是否支援。        brotli_static always;        # 設定壓縮質量等級。取值範圍是0到11.        brotli_comp_level 6;        # 設定緩衝的數量和大小。大小預設為一個記憶體頁的大小,也就是4k或者8k。        brotli_buffers 16 8k;        # 設定需要進行壓縮的最小響應大小。        brotli_min_length 20;        # 指定對哪些內容編碼型別進行壓縮。text/html內容總是會被進行壓縮        brotli_types text/plain application/json application/javascript application/x-javascript text/javascript text/css application/xml image/jpeg image/gif image/png video/mpeg audio/x-pn-realaudio audio/x-midi audio/basic audio/mpeg audio/ogg audio/* video/mp4;    server {        listen    80;        # 您的域名        server_name xxxxxx.xxx;         location ^~ / {            proxy_cache imgcache;            proxy_cache_key $scheme$proxy_host$uri$is_args$args;            proxy_cache_valid  200 304 302 24h;               proxy_pass http://www.域名:5008;            proxy_set_header X-Real-IP $remote_addr;            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;                proxy_set_header Host $http_host;             proxy_set_header X-Nginx-Proxy true;            proxy_redirect off;            rewrite ^.+(?<!js|css|png|map)$ /index.html break;            autoindex on;            index index.htm index.html;            set $fallback_uri /index.html;            if ($http_accept !~ text/html) {                set $fallback_uri /null;            }            try_files $uri $uri/ $fallback_uri = 404;        }        location ^~ /pc {            proxy_pass http://www.域名:5008/back;            index index.htm index.html;        }    }    include /www/server/panel/vhost/nginx/*.conf;    }
    ```一切準備工作已就緒,可以把專案部署到伺服器上了
**執行 npm run build 命令分別打包前臺和後臺**執行打包後的結構是這樣的
![](https://i.iter01.com/images/61e830a5ce101448294791578218bd2482c7bc67213d3348dcafcf9b61c13d56.png)
接下來把專案(除client和backstage和node_modules以外)放置到伺服器的dist資料夾中(然後打該目錄執行**npm i**下載依賴)
![](https://i.iter01.com/images/8b70eaf1885e72a35e2af0dc057f46a5573daa1ec7ab8eccd3faca5d11899977.png)
由於對nginx不熟悉,所以我這裡public檔案放置得做一些改動,熟悉的可以自行配置(順便教下我。。)
![](https://i.iter01.com/images/7079a3e620286f3271e19c1b98c221a09edf0a4ae5de1ae800f8f9f5e36fd2f2.png)
就是把pc資料夾裡面的放置放到同級
皆大歡喜,做到這裡就完成了。接下使用 域名就能訪問啦
[例如我的部落格](http://www.llongjie.top)
### 日誌記錄放置到伺服器後,出了問題,肯定不像本地開發除錯一樣方便,所以我們需要**日誌記錄**,來定位問題
我採用的庫是 [log4js](https://log4js-node.github.io/log4js-node/)打不開就得翻牆,我只記錄了介面呼叫記錄,需要sql呼叫記錄的可以自行加上npm i koa-log4
```const log4js = require('koa-log4')const path = require('path')
log4js.configure({    appenders: {        api: {            type: 'dateFile',            filename: path.resolve(__dirname, 'logs', 'api', 'logging.log'),            maxLogSize: 1024 * 1024, // 配置檔案的最大位元組數            keepFileExt: 3, // 最多儲存3天            layout: {                type: 'pattern',                pattern: '%c [%d{yyyy-MM-dd hh:mm:ss}] [%p]:%m%n'            }        },        default: {            type: 'stdout'        }    },    categories: {        api: {            appenders: ['api'],            level: 'all'        },        default: {            appenders: ['default'],            level: 'all'        }    }})
process.on("exit", () => {    log4js.shutdown()})
const apiLogger = log4js.getLogger("api")
exports.apiLogger = apiLogger```
然後建立一箇中介軟體
```// apiLoggerMiddleware.jsconst { apiLogger } = require('../logger') 
// 處理錯誤的中介軟體module.exports = async (ctx, next) => {    try {        await next();    }    finally {        apiLogger.debug(`${ctx.method} ${ctx.path} ${JSON.stringify(ctx.body)}`)    }
};
//init.jsapp.use(require('./apiLoggerMiddleware')) // API請求日誌```

### 首頁載入速度優化用vue-cli正常打包後,會生成**多個chunk-hash.js 和 chunk-hash.css**,些許增加訪問速度,所以我們需要把對應和合為一個#### 合併chunk-hash.js在所有非同步元件前增加以下程式碼,目的是把打包的chunk統一
![](https://i.iter01.com/images/1206c83ea7d5d4a3295bec3745b319345ce2794707e33ef2c9d334fbc6593bf6.png)#### 合併chunk-hash.cssvue.config.js```module.exports = {configureWebpack: config => {    // 公共程式碼抽離      config.optimization.splitChunks.cacheGroups = {        vendor: {          chunks: 'all',          test: /node_modules/,          name: 'vendor',          minChunks: 1,          maxInitialRequests: 5,          minSize: 0,          priority: 100        },        common: {          chunks: 'all',          test: /[\\/]src[\\/]js[\\/]/,          name: 'common',          minChunks: 2,          maxInitialRequests: 5,          minSize: 0,          priority: 60        },        styles: {          name: 'styles',          test: /\.(le|sa|sc|c)ss$/,          chunks: 'all',          reuseExistingChunk: true,          minChunks: 1,          enforce: true        }      }    }}```index.html
**XXX是我的名字**```<!DOCTYPE html><html lang="en">  <head>    <meta charset="utf-8">    <meta http-equiv="X-UA-Compatible" content="IE=edge">    <meta name="description" content="XXX,一名前端工程師,這是我的個人部落格,網站文章隨便寫,想寫啥寫啥">    <meta name="keywords" content="個人部落格,XXX,前端,技術,WEB,blog,BLOG,搭建部落格,前端技術,VUE部落格,XXX的部落格">    <meta name="anthor" content="XXX,123456789@qq.com">    <meta name="robots" content="部落格, 前端, blog, 個人部落格, XXX, Yong,XXX的部落格,web,VUE,React">    <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no">    <link rel="icon" href="https://qiheizhiya.oss-cn-shenzhen.aliyuncs.com/image/favicon.ico">    <!-- 使用CDN的CSS檔案 -->    <% for (var i in htmlWebpackPlugin.options.cdn &&    htmlWebpackPlugin.options.cdn.css) { %>    <link            href="<%= htmlWebpackPlugin.options.cdn.css[i] %>"            rel="stylesheet"    />    <% } %>    <title>漆黑之牙</title>  </head>  <body>    <noscript>      <strong>We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>    </noscript>    <div id="app"></div>    <!-- built files will be auto injected -->
    <!-- 使用CDN的JS檔案 -->    <% for (var i in htmlWebpackPlugin.options.cdn &&    htmlWebpackPlugin.options.cdn.js) { %>    <script src="<%= htmlWebpackPlugin.options.cdn.js[i] %>"></script>    <% } %>    <!-- 使用CDN的JS檔案 -->  </body></html>
```#### cdnvue.config.js``` const isProduction = process.env.NODE_ENV === 'production';const cdn = {  externals: {    'vue': 'Vue',    'vuex': 'Vuex',    'vue-router': 'VueRouter',    'axios': 'axios',    "element-ui": "ELEMENT",  },  css: [    'https://lib.baomitu.com/element-ui/2.13.2/theme-chalk/index.css'  ],  js: [    'https://cdn.bootcss.com/vue/2.6.10/vue.min.js',    'https://cdn.bootcss.com/vue-router/3.1.3/vue-router.min.js',    'https://cdn.bootcss.com/vuex/3.1.2/vuex.min.js',    'https://lib.baomitu.com/element-ui/2.13.2/index.js',    'https://cdn.bootcss.com/axios/0.19.2/axios.min.js'  ]}module.exports = {chainWebpack: config => {    // 注入cdn    config.plugin('html').tap(args => {      // 生產環境或本地需要cdn時,才注入cdn      if (isProduction) { args[0].cdn = cdn }      return args    })  },  configureWebpack: config => {  config.externals = cdn.externals  }}```#### 前端gzipnpm i -d compression-webpack-plugin
vue.config.js```//也是在configureWebpack中//gzip壓縮  config.plugins.push(new CompressionPlugin({    filename: '[path].gz[query]',    //壓縮演算法    algorithm: 'gzip',    //匹配檔案    test: /\.js$|\.css$|\.html$|\.woff$|\.ttf$|\.eot$|/,    //壓縮超過此大小的檔案,以位元組為單位    threshold: 1024,    minRatio: 0.8,    //刪除原始檔案只保留壓縮後的檔案    deleteOriginalAssets: isProduction  }))```
**完整配置請自行看client中的vue.config.js**
優化前:1M頻寬才首屏載入**10多秒**
優化後:正常情況下首屏載入**1,2秒**
### 最後
不知不覺寫的太多了
專案無模板,純手寫
分享自己的一個全棧簡單專案給大家,有什麼建議/bug/優化可以提一下,感謝!!。
如果看到這裡,就請幫忙點個[star](https://github.com/qiheizhiya/myBlog)吧!!
喜歡技術的也可以加我,一起進步。
郵箱: 953136447@qq.com
微訊號:qwer880620

相關文章