Nuxt.js實現一個SSR的前端博客

爲何要用Nuxt.js

公司現有的項目只有落地頁是經過前端自己server讀取pug文件進行服務端渲染的,固然是爲了首屏加載速度以及SEO。Nuxt.js 是一個基於Vue.js的通用應用框架,預設了利用Vue.js開發服務端渲染的應用所須要的各類配置,只須要安裝官方文檔的要求進行開發,就能夠很好的解決SSR的問題。咱們以一個簡單的博客爲例,來實踐一下Nuxt.js。javascript

項目介紹

當前基於Nuxt.js的簡化版博客,包括註冊、登陸、文章列表頁面、文章詳情頁、以及用戶列表頁等幾個頁面,用戶信息使用了Vux進行存儲,異步數據使用了asyncData進行獲取,配合了nuxtServerInit、cookie來處理刷新頁面後Vux數據丟失的問題,同時使用了error模板頁面處理常規錯誤,使用了中間件進行了簡單的權限校驗。該項目不足點,統一封裝了axios的方法,可是沒有考慮到服務端請求接口,token的處理。css

目錄結構

  • assets: 資源文件。用於組織未編譯的靜態資源如 LESS、SASS或 JavaScript。
  • components: 組件。
  • layouts: page: 模板頁面,默認爲 default.vue能夠在這個目錄下建立全局頁面的統一佈局,或是錯誤處理頁面頁,須要提供一個nuxt 標籤,相似於router-view
  • middleware: 中間件,放置自定義的中間件,會在加載組件以前調用。能夠在頁面中調用: middleware: '中間件名稱'。
  • pages: 頁面,index.vue 爲根頁面,Nuxt.js 框架讀取該目錄下全部的 .vue文件並自動生成對應的路由配置,如須要動態參數id,則能夠添加_id的文件,必須是下劃線加參數名。
  • plugin: 插件,用於組織那些須要在 根Vue.js應用實例化以前須要運行的 Javascript 插件。
  • static: 靜態文件,靜態文件目錄 static用於存放應用的靜態文件,此類文件不會被 Nuxt.js 調用 Webpack 進行構建編譯處理。
  • store: 用於組織vuex狀態管理。具體使用請移步至 官網。
  • nuxt.config.js: nuxt.config.js文件用於組織Nuxt.js 應用的個性化配置,配置head,loading,css,plugins等。

Nuxt.js生命週期

1. incoming Request 瀏覽器發出的請求)
2. nuxtServerInit	服務端接受請求後,要檢查當前有沒有 nuxtServerInit配置項,若是有就執行這個函數
3. store action 用來操做vuex
4. middleware 能夠作jWT等一些操做。
5. validate() 檢驗參數,參數檢驗失敗,能夠在layout裏的error裏面進行捕捉。
6. asyncData()& fetch()  asyncData用來渲染組件,fetch用來渲染vuex
7. Render
複製代碼

Nuxt擴展之後的生命週期和方法如下:前端

beforeCreate: ƒ beforeCreate()
components: {NuxtLoading: {…}}
computed: {isOffline: ƒ}
context: {isStatic: false, isDev: true, isHMR: true, app: {…}, payload: undefined, …}
created: ƒ created()
data: ƒ data()
head: {title: "nuxt-meituan-ssr", meta: Array(3), link: Array(1), style: Array(0), script: Array(0)}
methods: {refreshOnlineStatus: ƒ, refresh: ƒ, errorChanged: ƒ, setLayout: ƒ, loadLayout: ƒ}
mounted: ƒ mounted()
nuxt: {…}
render: ƒ render(h, props)
router: VueRouter {app: Vue, apps: Array(1), options: {…}, beforeHooks: Array(2), resolveHooks: Array(0), …}
watch: {nuxt.err: "errorChanged"}

複製代碼

注意:vue

  1. Vue.js生命週期的鉤子只有beforeCreate和created會在服務端和客戶端渲染。
  2. 以上生命週期裏都獲取不到window對象。
  3. asyncData和fetch咱們能夠拿到數據,不要嘗試掛載數據到data上,此時獲取不到this對象。

開發總結

如何修改默認啓動端口?

能夠在package.json下面修改配置,以下。java

"config":{
    "nuxt":{
        "host":"127.0.0.1",
        "port":"3304"
    }
}
複製代碼

如何添加全局的樣式?

能夠在assets裏添加全局Css文件,如在assets下的Css文件夾目錄下添加了一個index.css文件,而後在nuxt-config.js裏配置該css文件路徑便可。 css:['~assers/css/index.css']ios

經過別名訪問圖片在template裏是正確的,爲什麼在Css設置背景圖卻報錯?

在css配置的是,須要將'~/'後面的'/'去除掉。nginx

<img src="~/static/logo.jpg"/> 
    backround-image:url('~static/logo.jpg');
複製代碼

如何添加路由動畫?

一樣,咱們在Css文件裏添加一些動畫代碼,通常樣式會在其後面添加-active和-leave-active,其實和Vue動畫形式一致。其中以page開頭的動畫,默認會做用於所有頁面,若是想給特定的頁面加動畫,能夠在對應的頁面script裏引用,如 transitions: 'bounce'便可。vuex

.page-enter-active, .page-leave-active {
    transition: opacity .3s
  }
  .page-enter, .page-leave-active {
    opacity: 0
  }
  .bounce-enter-active {
    animation: bounce-in .8s;
  }
  .bounce-leave-active {
    animation: bounce-out .5s;
  }
  @keyframes bounce-in {
    0% { transform: scale(1) }
    50% { transform: scale(1.01) }
    100% { transform: scale(1) }
  }
  @keyframes bounce-out {
    0% { transform: scale(1) }
    50% { transform: scale(1.01) }
    100% { transform: scale(1) }
  }
  
複製代碼

路由參數如何傳遞?

同Vue-router,有聲明式和編程式兩種方式,無非是標籤變成了 router.push(...)npm

nuxt-link :to="{name:'article',params:{id:1234}}" >聲明式</nuxt-link>
    // 編程式
    this.$router.push({
        name:'article',
        params:{
            id:1234
        }
    })
複製代碼

動態路由如何進行參數檢驗?

Nuxt.js提供了一個validate的生命週期鉤子,能夠在此進行參數的校驗。以文章詳情校驗id爲例,咱們須要判斷傳入的id是不是數字,能夠像下面這樣處理。編程

validate({ params }) {
    return /^\d+$/.test(params.id)
  }
複製代碼

如何添加404等錯誤頁面?

能夠在layout下新建一個error.vue頁面,內容以下,當訪問一個不存在的頁面的時候,或者參數檢驗失敗的時候,或者咱們在middleware中間件處理拋出異常的時候,都會跳轉到該頁面。

<template>
  <div class="container">
    <h1 v-if="error.statusCode === 404">頁面不存在</h1>
    <h1 v-else>應用發生錯誤異常</h1>
    <nuxt-link to="/">首 頁</nuxt-link>
  </div>
</template>

<script>
export default {
  props: ['error'],
  layout: 'blog' // 指定模板頁面
}
</script>
複製代碼

middleware中的文件拋出錯誤

export default function({ store, error, redirect }) {
    if (!store.state.user.userInfo.auth) {
           error({
             message: '沒有權限哦!',
            statusCode: 403
          })
    }
}

複製代碼

頂部進度條如何設置?

loading 屬性配置 能夠在nuxt-config.js設置loading的顏色,使用了this.nextTick來調用它,由於loading可能沒法在created裏當即使用。此種配置loading有嚴重的缺陷,沒法知道真正的加載進度。也能夠自定義加載組件,loading: '~components/loading.vue'。

export default {
  mounted () {
    this.$nextTick(() => {
      this.$nuxt.$loading.start()
      setTimeout(() => this.$nuxt.$loading.finish(), 500)
    })
  }
 }
複製代碼

異步數據如何獲取?

Nuxt.js提供了兩個函數,asyncData和fetch函數。asyncData 獲取組件的數據,fetch 在渲染頁面以前獲取數據填充應用的狀態樹(store)。

  • asyncData可使用promise也可使用async函數,記住,此時返回的東西須要用一個對象進行包裹,不能掛載到data裏,此時沒有this對象。
// 方式一
  asyncData({ app,params,route,query,error}) {
      return  getUserlist({}).then(res => {
       let user = [];
         user = res.list
         console.log(user,'user')
         return {user}
       })
         .catch(err => {
           console.log(err)
        })
},

// 方式二
async asyncData({ app }) {
    let data = await getUserlist({});
    let user = data.list;
    return { user }
}
複製代碼
  • fetch函數同上,可使用promise也可使用async函數,一般會commit一個mutation。
export default {
  fetch ({ store, params }) {
    return axios.get('http://my-api/stars')
    .then((res) => {
      store.commit('setStars', res.data)
    })
  }
}
</script>
// 或者使用 async 或 await 的模式簡化代碼以下:
<template>
  <h1>Stars: {{ $store.state.stars }}</h1>
</template>

<script>
export default {
  async fetch ({ store, params }) {
    let { data } = await axios.get('http://my-api/stars')
    store.commit('setStars', data)
  }
}
</script
複製代碼

如何動態修改title的內容?

  1. 若是是寫死的,能夠直接修改head的配置。
head() {
    return {
      // title: '',這裏一旦聲明,在asyncdata裏修改也不起做用,直接以這個爲準
      meta: [
        {
          hid: 'description',  // nuxt.config 替換惟一標識 hid { hid: 'description', name: 'description', content: 'Nuxt.js project' }
          name: 'content',
          content: '文章詳情'
        }
      ]
    }
  },
複製代碼
  1. 若是是動態數據從數據源裏獲取,而後經過asynData裏的app對象,動態修改head的title。
asyncData({ app, params }) {
    const id = params.id;
    return getArticleDetail({ id })
      .then(result => {
            app.head.title = result.title;
      })
      .catch(err => {})
  }
複製代碼

如何進行權限JWT驗證?

登陸成功之後,咱們會在cookie和Vuex中緩存token信息,當界面刷新的時候,會走store裏的nuxtServerInit 函數,該函數僅在每一個服務器端渲染中運,可使用req.headers.cookie獲取瀏覽器的cookie,再次更新store裏的值,接着會走到中間件,中間件進行驗證,若是有token信息則繼續,沒有則跳轉到登陸頁。
1. 爲何要在nuxtServerInit更新store的值?
    須要在middleware裏使用,不然刷新後store裏的值爲空了。
2. 客戶端調用接口能夠拿到token,服務器端如何拿到?
    能夠經過nuxtServerInit裏的req拿到請求信息的cookie,而後請求接口。
3. 先後端分離,刷新的時候如何保證用戶名、token等信息依然存在?
    能夠像上面同樣,每次取cookie的值再次更新store,但這樣有一個問題,cookie可能會被篡改,後端代碼須要作驗證。也能夠每次刷新從新經過token請求接口,更新用戶信息。
複製代碼

store代碼

import Vue from 'vue';
import Vuex from 'vuex';
import user from './modules/user';
import { COOKIE_KEY } from '~/assets/js/constant.js';
Vue.use(Vuex);
const store = () =>
    new Vuex.Store({
        modules: {
            user
        },
        actions: {
            async nuxtServerInit({ commit, dispatch }, { req, app }) {
                if (req.headers.cookie) {
                    let parsedResult = {};
                    req.headers.cookie.split(';').forEach(cookie => {
                        const currentCookie = cookie.split('=');
                        parsedResult[currentCookie[0].trim()] = (currentCookie[1] || '').trim();
                    });
                    const userInfo = {
                        name: parsedResult[COOKIE_KEY.NAME],
                        token: parsedResult[COOKIE_KEY.TOKEN]
                    };
                    commit('user/setUserInfo',userInfo);
                }
            }
        }
    });

export default store;
複製代碼

中間件代碼

export default function({ store, error, redirect }) {
    if (!store.state.user.userInfo.token || !store.state.user.userInfo.name) {
        //   error({
        //     message: 'You are not connected',
        //     statusCode: 403
        //   })
        redirect('/');
    }
}

複製代碼

nginx部署

  1. npm run build
  2. 選擇build之後的四個文件: .nuxt, static, nuxt.config.js, package.json上傳到服務器。
  3. pm2 pm2 start npm --name 'package.json.name' -- run start
  4. nginx配置

查看網頁源代碼能夠看到:

server{
            listen 3000;
            server_name   felix12345.club; 
            gzip on;
            gzip_buffers 32 4K;
            gzip_comp_level 6;
            gzip_min_length 100;
            gzip_types application/javascript text/css text/xml;
            gzip_disable "MSIE [1-6]\."; 
            gzip_vary on;
            proxy_buffer_size 64k;
            proxy_buffers   32 32k;
            proxy_busy_buffers_size 128k;
            location / {
                root   /data/ww/nuxt;
                proxy_pass    http://127.0.0.1:3002;
                proxy_set_header X-Real-IP $remote_addr;
            }
        }

複製代碼

這樣,使用Nuxt.js實現了一個服務端渲染的簡易博客。

在線訪問地址: http://felix12345.club:3000/article/

相關文章
相關標籤/搜索