vue 服務器端渲染 nuxt.js初探

開頭還是來一段廢話: 年關將近,給大家拜個早年,願大家年會都能抽大獎,來年行大運。

廢話不多說,直接進正文

項目環境:

前端vue項目, 需要將新增的幾個路由頁面做seo處理。

在調研 插件 prerender-spa-plugin後,發現無法滿足 vuex 以及 plugins 等要求時,果斷選用了 nuxt.js做服務器渲染。

下面是在項目中整理的 文檔 和 問題

nuxt.js 是一個基於 Vue.js 的通用應用框架


它預設了利用 Vue.js 開發 服務端渲染(SSR, Server Side Render) 的應用所需要的各種配置,同時也可以一鍵生成靜態站點。
值得一提的是,nuxt是基於node.js的,後端如果是其他語言時,是否考慮到再加一層node.js的合理性

鏈接地址: https://zh.nuxtjs.org/guide/installation

利用npx腳手架創建項目

鏈接地址: https://zh.nuxtjs.org/guide/installation

會提供以下選項

  1. 在集成的服務器端框架之間進行選擇:   Express / Koa ...

  2. 選擇您喜歡的UI框架:   Bootstrap /  Element UI ...

  3. 選擇你想要的Nuxt模式  (Universal or SPA)    普通類型 / 單頁應用

  4. 添加 axios module 以輕鬆地將HTTP請求發送到您的應用程序中。

  5. 添加 EsLint 以在保存時代碼規範和錯誤檢查您的代碼。

  6. 添加 Prettier 以在保存時格式化/美化您的代碼。

注意:
      1. 如果項目自帶分支等git信息時, 需要將npx生產的目錄裏面隱藏的git 文件刪除
              因爲npx生成文件時,默認爲master 分支,類似於 gitmodule 子分支性質
      
      2. 其中第3點,選擇 Universal 時 纔會默認輸出靜態頁,也就是能夠seo的,當選擇spa時,則無法seo
              可修改 nuxt.config.js 中的配置項 mode: 'Universal' 來定義類型
              
              
              
              

啓動項目

命令: npm run dev 默認命令

這時會報錯,說未指定ip 什麼的,

需配置項:

      nuxt.config.js 中

      server: {
        // port: '3000',     // 定義 輸出端口 ,默認爲3000 
        host:'0.0.0.0'      // 定義 輸出 ip 
      },


      注意: 
      
      在server 目錄中國的index.js中 會讀取 nuxt.config.js 中的配置項,當不存在時會賦值默認值

      const {
        host = process.env.HOST || '127.0.0.1',
        port = process.env.PORT || 3000   // 默認配置條件下,修改此處無效 仍舊爲3000端口
      } = nuxt.options.server

      頁面上的注意點有:

      css 都默認加載到 頁面上了;

      處理方式有2種

          1. 在 nuxt.config.js 文件 header 配置 link 外鏈這些公共樣式 (下面有具體說明)

          2. 在 nuxt.config.js 文件 build 配置 中 自定義文件路徑 以及hash值 (下面有具體說明)
          
          

項目目錄結構

1. 資源目錄 (assets)

        用於組織未編譯的靜態資源如 LESS、SASS 或 JavaScript。

2. 組件目錄 (components)

        用於組織應用的 Vue.js 組件。Nuxt.js 不會擴展增強該目錄下 Vue.js 組件,
          即這些組件不會像頁面組件那樣有 asyncData 方法的特性。

3. 佈局目錄 (layouts) 該目錄名爲Nuxt.js保留的,不可更改。

        用於組織應用的佈局組件。 

4. 中間件目錄 (middleware)

        目錄用於存放應用的中間件
        
            文件名的名稱將成爲中間件名稱(middleware/auth.js將成爲 auth 中間件)。
            一箇中間件接收 context 作爲第一個參數:
        
        具體參考: https://zh.nuxtjs.org/guide/routing#中間件

5. 頁面目錄 (page) 該目錄名爲Nuxt.js保留的,不可更改。

        用於組織應用的路由及視圖。Nuxt.js 框架讀取該目錄下所有的 .vue 文件並自動生成對應的路由配置。

        nuxt 會根據文件夾名稱以及目錄結構動態生產 router, 無需額外配置。

6. 靜態文件目錄 (static)

        用於存放應用的靜態文件,此類文件不會被 Nuxt.js 調用 Webpack 進行構建編譯處理。 
        服務器啓動的時候,該目錄下的文件會映射至應用的根路徑 / 下。
        
        一般用於 放置公共css,以及 js 文件, 但是如果不想這些css和js走根目錄的話,
        需要將這些css放置到 assets中,然後在 nuxt.config.js中 配置 build 選項 下面會具體說明

7. Store 目錄

        用於組織應用的 Vuex 狀態樹 文件
        
        注意: 普通的spa 項目中拋出一個實例對象即可, store爲
        
        export default new Vuex.Store({
          actions,
          getters,
        })
        
          這裏則需要拋出一個 實例函數對象
        
          const store = () => {
            return new Vuex.Store({
              state,
              getters,
              mutations,
              actions
            })
          }
        
          export default store

8. nuxt.config.js 

      用於組織Nuxt.js 應用的個性化配置,以便覆蓋默認配置

9. package.json

      省略...
      

別名

    ~ 或 @           // src目錄
    
    ~~ 或 @@         // 根目錄
    
    默認情況下,src目錄和根目錄相同
    
    
    

頁面間路由的跳轉

    要在頁面之間使用路由,建議使用<nuxt-link> 標籤。
    
    js 中仍然可以使用 $router.push 等方法
    

路由跳轉時的頁面間過渡效果

    Nuxt.js 默認使用的過渡效果名稱爲 page
    
    需要在 assets/目錄下創建 main.css 添加全局樣式
    
      .page-enter-active, .page-leave-active {
        transition: opacity .5s;
      }
      .page-enter, .page-leave-active {
        opacity: 0;
      }
    
    然後添加到 nuxt.config.js 文件中:
    
      module.exports = {
        css: [
          'assets/main.css'
        ]
      }
    
    更多過渡效果: https://zh.nuxtjs.org/guide/routing#過渡動效
    
    

頭部信息 (Meta 標籤 ,全局樣式)

    nuxt.config.js 裏定義應用所需的所有默認 meta 標籤
    
    head: {
      meta: [
        { charset: 'utf-8' },
        { name: 'viewport', content: 'width=device-width, initial-scale=1' }
        { hid: 'description', name: 'description', content: '' }
      ],
      link: [   // 這裏可以引用全局的樣式,但是會默認走根目錄
        { rel: 'stylesheet', href: 'https://fonts.googleapis.com/css?family=Roboto' }
    
        { rel: 'stylesheet', href: '~/static/common.js' }    // 文件一般都放在static目錄下
      ]
    }
    
    具體參考:https://zh.nuxtjs.org/api/configuration-head

異步數據 (asyncData方法,限於page頁面組件,components中不適用)

    這裏包括  asyncData鉤子  /  fetch 鉤子 / 。。。
    
    【fetch】 用於在渲染頁面前填充應用的狀態樹(store)數據, 與 asyncData 方法類似,不同的是它不會設置組件的數據
    
    【asyncData】 主要用於請求ajax 填充data中的數據
    
    每次加載之前被調用。它可以在服務端或路由更新之前被調用。
    
    asyncData ({ params }) {
      return axios.get(`https://my-api/posts/${params.id}`)
      .then((res) => {
    
        // 賦值給頁面 data中的數據
        return { title: res.data.title }
      })
    }
    
    或者變換爲同步請求
    
    async asyncData() {
        let formData = {}
    
        let ajaxData = await axios({
          method: "post",
          url: url,
          data: qs.stringify(formData),
          retryDelay : 1000,
          withCredentials : true,
          responseType : 'json',
          timeout : 60000,
          'Content-Type' : 'application/x-www-form-urlencoded'
        })
    }
    
    注意添加 catch
    
    
    注意: 
      
      這個異步請求函數, 第一次執行環境爲node環境中,也就是服務器端,後續刷新頁面則執行環境爲client 客戶端
    
      本地開發時,如果在客戶端直接請求完整路徑時會經常遇到跨域問題,所以需要在 asyncData 中區分環境變量
    
      process.env.VUE_ENV 區分 是server 還是 client
    
      然後根據不同的環境配置不同的 url , 並且在 client時, 需要做服務器端代理請求,需要給url增加一層代理標識
    
      例如:client環境中
    
        url =  '/api' + '/get-user-info';
    
        nuxt.config.js 中
    
        /*
        ** 處理代理跨域問題
        */
        axios: {
          proxy: true,
          prefix: '/api',       // 增加請求標識
          credentials: true,
        },
        proxy: {
          '/api': {
            // 代理地址
            target: (process.env.NODE_ENV == 'production') ?'http://test.' : 'http://www.' ,
            changeOrigin: true,
            pathRewrite: {
              '^/api': ''           // 將標識 替換爲 ‘’
            },
          },
        }
    
    
        錯誤處理 : 
    
            context中提供了一個 error(params) 方法,你可以通過調用該方法來顯示錯誤信息頁面。
            params.statusCode 可用於指定服務端返回的請求狀態碼。
    
        asyncData ({ params, error }) {
          return axios.get(`https://my-api/posts/${params.id}`)
          .then((res) => {
            return { title: res.data.title }
          })
          .catch((e) => {
            error({ statusCode: 404, message: 'Post not found' })
          })
        } 
        
        

第三方插件的使用

      例如:element-ui
    
      需要在 plugins/ 中 添加 element-ui.js
    
        import Vue from 'vue'
        import Element from 'element-ui/lib/element-ui.common'
        import locale from 'element-ui/lib/locale/lang/en'
    
        export default () => {
          Vue.use(Element, { locale })
        }
    
    
      在 uuxt.config.js 中
    
      plugins: [
        "~/plugins/element-ui",
        // {src : '~/plugins/ga.js' , ssr : false}  是否做ssr處理, false時爲在客戶端才加載
      ],
      
    
      這樣全局就可以使用了
    
      注意:
          在使用第三方插件時需要注意 插件內部很多地方都會用到window對象,在服務端會報錯,所以需要將ssr設置爲false
          
          在生產環境中, 有一些插件,在多個頁面中引用,這樣會造成多次加載打包的現象
        
          所以: 在 build配置項中增加配置
        
            build: {
              vendor:['axios', 'qs'],        // 防止多次打包
            }
            

page 函數鉤子生命週期 以及window 對象

      經常會在 第三方組件或者調用的時候 遇到window對象報錯問題
    
      asyncData() {
        console.log(window) // 服務端報錯
        console.log(this)   // undefined
      },
      fetch() {    
        console.log(window) // 服務端報錯
      },
      created () {
        console.log(window) // undefined
      },
      mounted () {
        console.log(window) // Window {postMessage: ƒ, blur: ƒ, focus: ƒ, close: ƒ, frames: Window, …}
      }
      
      

clipboard.png

css js 文件打包文件夾處理

  在 uuxt.config.js 中 , 只需配置生產環境中

  build: {
    extractCSS: { allChunks: true },    // css 獨立打包 link 的形式加載
    publicPath: '/sample/assets/', //sample/essays 打包的默認路徑爲 ‘_nuxt’ 或者可以指定cdn 域名
    filenames:{         // css 和 js  img 打包時指定文件夾 
      app: ({ isDev }) => isDev ? '[name].js' : '[chunkhash].js',
      chunk: ({ isDev }) => isDev ? '[name].js' : '[chunkhash].js',
      css: ({ isDev }) => isDev ? '[name].js' : '[contenthash].css',
      img: ({ isDev }) => isDev ? '[path][name].[ext]' : '[hash:7].[ext]'
    }
  },

  輸出 css link 路徑: /sample/essays/[contenthash].css

  注意: 靜態資源文件路徑名 不能和頁面路由名稱相同  publicPath 默認配置 '/' 無效
  
  

部署

  先 npm run build 打包 client文件和 server 文件

  然後 npm runb start 啓動服務器

  pm2 管理

  pm2 start npm --name "my-nuxt" -- run start


  部署時 需要注意 如果是 從其他地方重定向 到  nuxt 環境中的頁面是, 需要額外配置一個 css / js 重定向路由,並且需要注意 header頭部信息,防止出現 css 文件返回頭部信息爲 Content-Type text/plain
  

目前項目中只運用到這麼多,後續項目遷移時遇到更多的問題會做補充,如果大家遇到過其他的坑點可以在下面評論中總結出來以及解決方案

文章較長,謝謝大家賞臉!感謝各位!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章