nuxt.js實現服務端渲染ssr(環境配置、 多環境開發、進程守護、服務端鏡像)

nuxt.js是一個基於 Vue.js 的輕量級應用框架,可用來建立服務端渲染 (SSR) 應用,也可充當靜態站點引擎生成靜態站點應用——nuxt官網css

根據本身在這上方面一條路走到黑,摸滾帶爬的踩坑經驗,接下來我將從如下幾個方面給各位接觸到或者將來接觸到這方面知識的碼友進行全方位的講解和剖析。html

  1. 背景
  2. nuxt框架的概述
  3. 百度蜘蛛爬蟲的機制
  4. nuxt初探(開發環境搭建)
  5. 深刻nuxt ssr(服務端渲染)
  6. 多環境配置 (開發、測試、生產)
  7. Nginx反向代理
  8. pm2守護node進程配置
  9. 服務端docker容器部署前端工程
  10. 總結
背景

最近因公司業務需求,須要對商城官網作seo優化。由於初版前端用的是純vue寫的,經Vue-cli集成的webpack打包還有代碼壓縮處理後生成一串js的靜態文件。因百度蜘蛛沒法對純js的網頁進行爬取收錄。因此不利於網站的排名。緊接咱們和領導開會拍板使用服務端渲染(如下用ssr指代),nuxt恰好是vue團隊打造的基於vue的ssr的框架,簡單易於上手,避免了前端攻城獅們本身用node.js搭建服務。但初始咱們採用的是nuxt的靜態化部署,nuxt generate ,這樣能夠解決一部分需求,但每次更新內容後都要前端手動執行命令打包。並且經過百度蜘蛛爬取趨勢圖顯示這種方式並不理想。最後,咱們仍是在原來的基礎上採用nuxt的第二種方式前端作ssr處理,上線後經百度蜘蛛爬取趨勢圖顯示有很大程度的提升,利於seo,且官網被百度收錄的頁面也逐漸增長。由此告一段落。前端

nuxt框架的概述

nuxt做爲一個框架,則集成了vue2vue-routevuexvue ssrvue-meta等組件/框架,其是用webpack、和vue-loaderbabel-loader來處理代碼的自動化構建工做(打包、代碼分層、壓縮等) nuxt框架提供了兩種部署方式: 1. 靜態化部署(預渲染)-- 經過 nuxt generate 命令實現。該命令依據應用的路由配置將每個路由靜態化成爲對應的 HTML 文件 2. ssr部署 -- 先經過nuxt build編譯構建再經過nuxt start開啓一個web服務 在服務端調取接口時,主要是用到了asyncData/fetch方法。使得咱們能夠在設置組件的數據以前能異步獲取或處理數據。vue

asyncData方法會在組件(限於頁面組件)每次加載以前被調用。它能夠在服務端或路由更新以前被調用。 在這個方法被調用的時候,第一個參數被設定爲當前頁面的上下文對象,你能夠利用 asyncData方法來獲取數據,Nuxt.js 會將 asyncData 返回的數據融合組件 data 方法返回的數據一併返回給當前組件。node

注意:因爲asyncData方法是在組件 初始化 前被調用的,因此在方法內是沒有辦法經過 this 來引用組件的實例對象。

fetch 方法用於在渲染頁面前填充應用的狀態樹(store)數據, 與 asyncData 方法相似,不一樣的是它不會設置組件的數據。 類型: Function 若是頁面組件設置了 fetch 方法,它會在組件每次加載前被調用(在服務端或切換至目標路由以前)。fetch 方法的第一個參數是頁面組件的上下文對象 context,咱們能夠用 fetch 方法來獲取數據填充應用的狀態樹。爲了讓獲取過程能夠異步,你須要返回一個 Promise,Nuxt.js 會等這個 promise 完成後再渲染組件。react

警告: 您沒法在內部使用this獲取組件實例,fetch是在組件初始化以前被調用 webpack

大概瞭解了這些知識就能夠作ssr工做了,路由環境配置等可到nuxt中文官網 查閱ios

百度蜘蛛爬蟲的機制

百度蜘蛛是百度搜索引擎的一個自動化程序,它會不斷的訪問收集互聯網上的網頁、文章、視頻等,經過抓取連接來收錄網站,計算網站的權重和排名。純html等靜態化網站對百度蜘蛛比較友好,且百度蜘蛛幾乎不會爬取js動態的網站,如vue/react構建的且經webpack/gulp等構建工具壓縮處理過的網站。百度蜘蛛爬取網站是從主站開始爬,一次根據網站暴露的內鏈依次往深層次爬取。meta的設置,以及網站TDK的優化,網站結構優化,外鏈,文章原創等一樣對SEO有很大做用,但本文主要是從技術層面入手,則主要是針對網站內鏈的處理以及基於vue等如今技術流作ssr處理。nginx

nuxt初探(開發環境搭建)

安裝

我這裏採用nuxt.js團隊建立的腳手架creat-nuxt-app搭建。git

注: 開發必備環境 npx (npm v5.2.0+) node (v4.0+)

個人開發環境npm v5.5.1 node v8.9.1 win10 在終端輸入如下命令

npx create-nuxt-app nuxt-demo
複製代碼

而後你會看到

nuxt-1.png
nuxt-2.png
當執行命令

npm run dev
複製代碼

當你看到以下圖恭喜你成功搭建起項目

nuxt-3.png
當你和後端交互時,爲了解決跨域問題這邊須要經過 proxy利用本地 node服務器作個反向代理 在 nuxt.config.js中配置

module.exports= {
	modules: [
		'@nuxtjs/axios'
	],
	axios: {
		proxy: true, //開啓代理
		credentials: true, //跨域請求需使用憑證
	},
	proxy: [
		['/api',{ 
			target: 'http://example.com/api', // (後端請求地址)
			changeOrigin: true,
			pathRewrite: {'^/api': ''}
		}]
	]
}

複製代碼

附上剛搭建完的nuxt.config.jspackage.json

// nuxt.config.js

module.exports = {
  mode: 'universal',
  /*
  ** Headers of the page
  */
  head: {
    title: process.env.npm_package_name || '',
    meta: [
      { charset: 'utf-8' },
      { name: 'viewport', content: 'width=device-width, initial-scale=1' },
      { hid: 'description', name: 'description', content: process.env.npm_package_description || '' }
    ],
    link: [
      { rel: 'icon', type: 'image/x-icon', href: '/favicon.ico' }
    ]
  },
  /*
  ** Customize the progress-bar color
  */
  loading: { color: '#fff' },
  /*
  ** Global CSS
  */
  css: [
    'element-ui/lib/theme-chalk/index.css'
  ],
  /*
  ** Plugins to load before mounting the App
  */
  plugins: [
    '@/plugins/element-ui'
  ],
  /*
  ** Nuxt.js dev-modules
  */
  buildModules: [
    // Doc: https://github.com/nuxt-community/eslint-module
    '@nuxtjs/eslint-module',
  ],
  /*
  ** Nuxt.js modules
  */
  modules: [
    // Doc: https://axios.nuxtjs.org/usage
    '@nuxtjs/axios',
    '@nuxtjs/pwa',
    // Doc: https://github.com/nuxt-community/dotenv-module
    '@nuxtjs/dotenv',
  ],
  /*
  ** Axios module configuration
  ** See https://axios.nuxtjs.org/options
  */
  axios: {
  },
  /*
  ** Build configuration
  */
  build: {
    transpile: [/^element-ui/],
    /*
    ** You can extend webpack config here
    */
    extend (config, ctx) {
    }
  }
}

複製代碼
//package.json
{
  "name": "nuxt-demo",
  "version": "1.0.0",
  "description": "for a nuxt demo about ssr",
  "author": "kevin xie",
  "private": true,
  "scripts": {
    "dev": "cross-env NODE_ENV=development nodemon server/index.js --watch server",
    "build": "nuxt build",
    "start": "cross-env NODE_ENV=production node server/index.js",
    "generate": "nuxt generate",
    "lint": "eslint --ext .js,.vue --ignore-path .gitignore .",
    "test": "jest"
  },
  "dependencies": {
    "nuxt": "^2.0.0",
    "cross-env": "^5.2.0",
    "express": "^4.16.4",
    "element-ui": "^2.4.11",
    "@nuxtjs/axios": "^5.3.6",
    "@nuxtjs/pwa": "^3.0.0-0",
    "@nuxtjs/dotenv": "^1.4.0"
  },
  "devDependencies": {
    "nodemon": "^1.18.9",
    "@nuxtjs/eslint-config": "^2.0.0",
    "@nuxtjs/eslint-module": "^1.0.0",
    "babel-eslint": "^10.0.1",
    "eslint": "^6.1.0",
    "eslint-plugin-nuxt": ">=0.4.2",
    "@vue/test-utils": "^1.0.0-beta.27",
    "babel-jest": "^24.1.0",
    "jest": "^24.1.0",
    "vue-jest": "^4.0.0-0"
  }
}

複製代碼

深刻nuxt ssr(服務端渲染)

若是你不須要作ssr處理的話直接執行nuxt generate則nuxt會爲你生成靜態化頁面,而後再執行nuxt build 打包後將dist文件夾推上服務器就能夠上線了,但這種seo不太友好但比以前vue構建好一點。 但要作ssr處理的話就需換種方式處理也就是nuxt提供的第二種方式,在服務器搭個node服務器而後直接在上面跑。這樣的話,配置須要改,且與後臺的請求也須要改,改成nuxt給咱們提供asyncData/fetch。由於nuxt也是基於vue開發的,因此生命週期也同樣,但nuxt在服務端中會觸發beforeCreatecreated兩個生命週期。其次,nuxt有本身一套服務器渲染流程。

nuxt-4.PNG

Nuxt.js 提供了幾種不一樣的方法來使用 asyncData 方法,你能夠選擇本身熟悉的一種來用:

  1. 返回一個 Promise, nuxt.js會等待該Promise被解析以後纔會設置組件的數據,從而渲染組件.
  2. 使用 asyncawait
  3. 使用回調函數
返回Promise
export default {
  asyncData ({ params }) {
    return axios.get(`https://my-api/posts/${params.id}`)
      .then((res) => {
        return { title: res.data.title }
      })
  }
}
複製代碼
使用async或await
export default {
  async asyncData ({ params }) {
    const { data } = await axios.get(`https://my-api/posts/${params.id}`)
    return { title: data.title }
  }
}
複製代碼
使用回調函數
export default {
  asyncData ({ params }, callback) {
    axios.get(`https://my-api/posts/${params.id}`)
      .then((res) => {
        callback(null, { title: res.data.title })
      })
  }
}
複製代碼

另外,還要注意一下在作登陸處理等須要獲取cookie或者服務端存儲的session時,須要使用到vuex狀態樹,在狀態樹中有個方法很是有用 nuxtServerInit

actions: {
  nuxtServerInit ({ commit }, { req }) {
    if (req.session.user) {
      commit('user', req.session.user)
    }
  }
}
複製代碼

多環境配置 (開發、測試、生產)

由於在一個工程項目中咱們都是有開發、測試、生產這樣的驗收流程的。而每每咱們測試和生產所鏈接的數據庫都是不一樣的,且請求域也不同。所以爲了開發方便咱們就很須要多環境的配置。在spa中咱們能夠在package.json中經過cross-env這個node環境變量設置,但在ssr中這個在服務端請求時不生效,這裏我當時找了好久google之類最後仍是經過 @nuxtjs/dotenv解決了。還有特別注意到 @nuxtjs/axios中有提供環境變量API_URL來複寫baseURLAPI_URL_BROWSER來複寫browserBaseURL,這在多環境配置中頗有用。 你能夠在nuxt.config.js中引入而且在module中使用。

require('dotenv').config({path: '.env'})
module.exports={
	modules: [
		'@nuxtjs/axios',
		['@nuxtjs/dotenv', {
		   filename: process.env.NODE_ENV == 'production' ? '.env.prod' : process.env.NODE_ENV == 'test' ? '.env.test' : '.env.dev'
		}]
	]
}
複製代碼

同時你能夠在根目錄下配置相關環境配置文件。

.env.dev

NODE_ENV=development
API_URL_BROWSER=http://localhost:3000
API_URL=http://localhost:3000/api
複製代碼

.env.test

NODE_ENV=test
API_URL_BROWSER=http://test.example.com
API_URL=http://test.example.com/api
複製代碼

.env.prod

NODE_ENV=production
API_URL_BROWSER=https://www.example.com
API_URL=https://www.example.com/api
複製代碼

當你用axios作跨域請求時,你就能夠自動根據環境給axios配置baseURL。

import axios from 'axios';
const axiosInstance = axios.create({
	timeout: 10000,
	baseURL: process.env.API_URL
})
複製代碼

在某些須要跳轉的連接nuxt-linka連接中你能夠聲明一個變量對域名作統一處理。

export default {
	data () {
		return {
			BASE_PATH: process.env.API_URL_BROWSER
		}
	}
}
複製代碼

Nginx反向代理

在生成環境中,我須要使用到nginx作代理服務器,解決跨域。由於咱們的項目的先後端可能部署在不一樣的服務上。

upstream nodenuxt {
    server 127.0.0.1:3000; #nuxt項目 監聽端口
    keepalive 64;
}
server {
    listen 80;
    server_name https://www.example.com; #訪問域名 
    location / {
        proxy_http_version 1.1;        
        proxy_set_header Upgrade $http_upgrade;  
        proxy_set_header Connection "upgrade";        
        proxy_set_header Host $host;        
        proxy_set_header X-Nginx-Proxy true;        
        proxy_cache_bypass $http_upgrade;        
        proxy_pass http://nodenuxt; #反向代理
    }
}
複製代碼

pm2守護node進程配置

當咱們配置以上內容後在服務端執行npm start項目能夠跑起來,但咱們的項目總會掛掉的,爲了使咱們服務進程常駐,咱們要用到pm2來守護node進程。而且能夠用它來作自動重啓、性能監控以及負載均衡。 你能夠全局安裝npm install pm2 -g 其餘麻油說 執行命令能夠成功,但我這邊仍是沒成功pm2 start npm --name "nuxt-demo" -- run start //(nuxt-demo爲你的package.json中的項目名) 咱們知道package.json這個文件,當咱們執行npm run dev的時候,其實使用npm去啓動了./node_modules/nuxt/bin/nuxt這個文件。當咱們cd到咱們的項目目錄以後,咱們最終能夠執行以下命令來啓動:

pm2 start ./node_modules/nuxt/bin/nuxt.js -- start
複製代碼

這樣能夠將項目跑起來,但當項目報錯時咱們沒法看到日誌,所以我這裏在根目錄經過配置pm2.config.js

module.exports = {
	apps: [
		{
			name: 'nuxt-demo',//項目名稱
			cwd: './',//當前工做路徑
			script: 'npm',//實際啓動腳本
			args: 'run start',//參數
			autorestart: true, //自動重啓
			error_file: 'logs/nuxt-demo-err.log',//錯誤日誌
			out_file: 'logs/nuxt-demo-out.log', //正常運行日誌
			exec_mode: 'cluster_mode',// 應用啓動模式,支持fork和cluster模式
			min_uptime: '60s', //應用運行少於時間被認爲是異常啓動
			restart_delay: '60s',//重啓時延
			instance: 4,//開啓4個實例,僅在cluster模式有效,用於負載均衡
			watch: true,//監控變化的目錄,一旦變化,自動重啓
			watch: ['.nuxt', 'nuxt.config.js'],//監控變化的目錄
			watch_delay: 1000,//監控時延
			ignore_watch: ['node_modules'],//從監控目錄中排除
			watch_options: { // 監聽配置
				'followSymlinks': false,
				'usePolling': true
			}
		}
	]
}
複製代碼

這裏我是在package.json中引用這個配置文件的。在服務器中執行命令就行。(如有報錯可能須要安裝babel轉譯

npm install babel-cli babel-core babel-preset-es2015 --save-dev
複製代碼
{
	"scripts": {
		"dev": "cross-env NODE_ENV=development nodemon server/index.js --watch server --exec babel-node",
		"build": "nuxt build",
		"start:test": "cross-env NODE_ENV=test node server/index.js pm2 start pm2.config.js --exec babel-node",
		"start:prod": "cross-env NODE_ENV=production node server/index.js pm2 start.config.js --exec babel-node",
	}
}
複製代碼

服務端docker容器部署前端工程

創建Dockerfile 參考這篇文章

FROM node:alpine

RUN mkdir -p /usr/local/service/nuxtdemo
RUN chomd -R 777 /usr/local/service/nuxtdemo
WORKDIR /usr/local/service/nuxtdemo
COPY nuxt_demo.tar.gz /usr/local/service/nuxtdemo
RUN cd /usr/local/service/nuxtdemo && tar xf nuxt_demo.tar.gz
RUN rm -rf nuxt_demo.tar.gz

ENV HOST "0.0.0.0"
RUN npm config set registry https://registry.npm.taobao.org
RUN npm install -g pm2
RUN npm install
RUN npm run build
EXPOSE 3000
CMD ["npm", "run", "start:test"]
#CMD ["npm", "run", "start:prod"]
複製代碼

而後構建鏡像

docker build -t nuxt-demo
複製代碼

啓動容器

docker run -dt -p 3000:3000 nuxt-demo
複製代碼

注意:這裏服務端部署後須要0.0.0.0:3000訪問,則咱們前端還需配置主機號和端口,我這邊在package.json中沒成功,在nuxt.config.js中配置成功了。

module.exports = {
	server: {
		host: '0.0.0.0',
		port: 3000
	}
}
複製代碼

總結

總得來講,在coding world中要不斷的學習,沒什麼問題解決不了,堅持就對了。如有小夥伴對以上內容有不解歡迎評論,我會盡力爲你答疑解惑的。最後喜歡這篇文章的小夥伴關注一下小生並點個贊。 附:

史記·大疫

己亥末,庚子春,荊楚大疫,染者數萬,衆惶恐,舉國防,皆閉戶,道無車舟,萬巷空寂。然外狼亦動,垂涎而候,華夏腹背芒刺。幸龍魂不死,風雨而立。醫無私,警無畏,民齊心!政者,醫者,兵者,扛鼎逆行勇戰矣!商客,名家,百姓,仁義者,鄰邦獻物捐資,嘆山川異域,風月同天,豈曰無衣,與子同裳!能者竭力,萬民同心。月餘,疫除,終勝。此後百年,風調雨順,國泰民安!

相關文章
相關標籤/搜索