Nuxt專案總結

愛運動的女孩兒發表於2019-09-26

公司最近要做一個新產品的官網,要求要對SEO友好,首先想到的是做服務端渲染,Vue.js的服務端渲染vue-server-renderer需要node服務.自己搭建是相當複雜的,所以我們使用了Nuxt.js。

Nuxt.js簡介:

  • Nuxt.js是基於Vue的服務端渲染應用框架.
  • 整合了Vue2,Vue-Router,Vue SSR,Vuex,Vue-Meta.
  • 可以輕鬆地通過配置實現服務端渲染或單頁模式.
  • Nuxt.js根據pages檔案目錄自動生成路由配置.
  • 通過nuxt generate命令依據應用的路由配置將每一個路由編譯成為對應的 HTML 檔案,實現Vue應用的靜態化.

搭建過程:

  • 首先用NUXT的腳手架create-nuxt-app生成專案
yarn create nuxt-app <專案名>
執行命令後,會提示你進行一系列的選擇,按專案需要進行選擇即可
需要注意的是選擇Nuxt模式(Universal or SPA),因為我們要做服務端渲染,所以選擇Universal,選擇之後在nuxt.config.js配置檔案中會生成配置mode:'universal'
複製程式碼
  • 然後會生成這樣的一個專案結構(紅框的目錄是根據需要自己建的)

    Nuxt專案總結
    Nuxt.js的檔案打包和頁面路由都是依據目錄來生成的,所以如果沒有額外配置,不要修改目錄的名稱

  • 下面依次介紹下生成的目錄:

    assets:該目錄下可以放css,scss,img,js,打包時會被編譯處理

    components:放元件,單純的vue檔案,沒有Nuxt的擴充套件,不能執行asyncData之類的

    layouts:佈局檔案,預設有default.vue,如果不配置,pages裡的頁面檔案預設的佈局檔案都是default.vue,如果想自定義佈局檔案,需要在layouts中新建佈局檔案,並在pages的頁面檔案中進行配置,比如新建error佈局檔案:

    //layouts/error.vue
    <template>
      <div>
        <nuxt />
      </div>
    </template>
    //pages/404.vue
    export default {
        layout:'error'
    }
    複製程式碼

    middleware:中介軟體目錄,中介軟體檔案中允許自定義一個函式在頁面渲染之前執行,可以在layouts,pages和nuxt.config.js中配置,中介軟體接收 context 作為第一個引數,栗子:

    //middleware/stats.js
    export default function (context) {
      context.userAgent = process.server ? context.req.headers['user-agent'] : navigator.userAgent
    }
    //nuxt.config.js
    router: {
      middleware: 'stats'
    }
    複製程式碼

    pages:頁面目錄,Nuxt.js會自動讀取該目錄下的檔案,生成路由配置,比如目錄結構是這樣的:

    pages/
      --| mobile/
        -----| index.vue
        -----| one.vue
      --| index.vue
    複製程式碼

    Nuxt.js會自動解析成路由:

    router: {
      routes: [
        {
          name: 'index',
          path: '/',
          component: 'pages/index.vue'
        },
        {
          name: 'mobile',
          path: '/mobile',
          component: 'pages/mobile/index.vue'
        },
        {
          name: 'mobile-one',
          path: '/mobile/one',
          component: 'pages/mobile/one.vue'
        }
      ]
    }
    複製程式碼

    Nuxt.js還支援比較複雜的動態路由,可以參考官方文件進行目錄配置.

    Nuxt.js還擴充套件了頁面元件,最常用的是asyncData,可以在服務端或者路由更新時執行,支援非同步資料處理,其他的還有當前頁面的head,middle,切換動畫transition配置等等,所有的擴充套件配置如下,可以根據需要進行頁面的個性化配置,覆蓋nginx.config.js中的全域性配置.

    Nuxt專案總結

    plugins:外掛目錄,配置完後可以全域性呼叫,ui外掛,或者js外掛,像element-ui,vue-lazyload,都可以在這裡進行配置,配置方法:

    //使用swiper外掛
    //plugins/vue-swiper.js
    import Vue from 'vue'
    import VueAwesomeSwiper from 'vue-awesome-swiper'
    Vue.use(VueAwesomeSwiper)
    
    //nuxt.config.js
    css:['swiper/dist/css/swiper.css'],
    plugins:[{ src: '~plugins/vue-swiper', ssr: false }]
    ssr:預設是true,表示在服務端執行,設定false表示該檔案只在客戶端打包引入
    複製程式碼

    static:靜態檔案目錄,不需要webpack構建編譯處理的檔案,比如在head中引入的icon就放在這個檔案下

    store:Vuex檔案目錄,通過新建store/index.js啟用Vuex,簡單的可以像下面這樣使用,如果資料較多,可以把模組分解為單獨的檔案state.js,actions.js,mutations.js和getters.js,參考官方文件進行配置.

    //store/index.js
    export const state = () => ({
      counter: 0
    })
    export const mutations = {
        increment (state) {
          state.counter++
        }
    }
    export const actions = {
        getData (state) {
           //使用@nuxt/axios可以直接呼叫this.$axios
           this.$axios.post('/xxx')
        }
    }
    export const getters = {}
    複製程式碼

    nuxt.config.js:全域性的配置檔案,會覆蓋nuxt的預設配置

  • 專案中用到的nuxt.config.js配置

    1.head(對SEO友好的配置,PWA配置)

     head: {
                title: 'xxx',
                meta: [
                  { charset: 'utf-8' },
                  { name: 'viewport', content: 'width=device-width, initial-scale=1' },
                  { hid: 'description', name: 'description', content: 'xxxx' },
                  { hid: 'keywords', name: 'keywords', content: 'xxx' }
                ],
                link: [
                  {
                    rel: 'icon',
                    type: 'image/png',
                    sizes: '16x16',
                    href: '/favicon-16x16.png'
                  },
                  {
                    rel: 'icon',
                    type: 'image/png',
                    sizes: '96x96',
                    href: '/favicon-96x96.png'
                  },
                  {
                    rel: 'icon',
                    type: 'image/png',
                    sizes: '32x32',
                    href: '/favicon-32x32.png'
                  },
                  {
                    rel: 'icon',
                    type: 'image/png',
                    sizes: '192x192',
                    href: '/android-icon-192x192.png'
                  },
                  {
                    rel: 'apple-touch-icon',
                    sizes: '57x57',
                    href: '/apple-icon-57x57.png'
                  },
                  {
                    rel: 'apple-touch-icon',
                    sizes: '60x60',
                    href: '/apple-icon-60x60.png'
                  },
                  {
                    rel: 'apple-touch-icon',
                    sizes: '72x72',
                    href: '/apple-icon-72x72.png'
                  },
                  {
                    rel: 'apple-touch-icon',
                    sizes: '76x76',
                    href: '/apple-icon-76x76.png'
                  },
                  {
                    rel: 'apple-touch-icon',
                    sizes: '114x114',
                    href: '/apple-icon-114x114.png'
                  },
                  {
                    rel: 'apple-touch-icon',
                    sizes: '120x120',
                    href: '/apple-icon-120x120.png'
                  },
                  {
                    rel: 'apple-touch-icon',
                    sizes: '144x144',
                    href: '/apple-icon-144x144.png'
                  },
                  {
                    rel: 'apple-touch-icon',
                    sizes: '152x152',
                    href: '/apple-icon-152x152.png'
                  },
                  {
                    rel: 'apple-touch-icon',
                    sizes: '180x180',
                    href: '/apple-icon-180x180.png'
                  },
                  {
                    rel: 'manifest',
                    href: '/manifest.json'
                  }
                ]
            },
    複製程式碼

    現在有些瀏覽器支援將網頁新增到主螢幕,方便使用者直接開啟.
    apple-touch-icon和manifest是配置新增到主螢幕的顯示的圖示和名稱等資訊.
    app-touch-icon是針對safari瀏覽器的.

    mainfest檔案格式:

    {
        "short_name": "短名稱",
        "name": "這是一個完整名稱",
        "icon": [
            {
                "src": "icon.png",
                "type": "image/png",
                "sizes": "48x48"
            }
            ...//多個不同大小的圖示
        ],
        "start_url": "index.html"
    }
    複製程式碼

    關於mainfest的介紹

    2.css

    //引入全域性的css檔案
    css: ['~/assets/css/normalize.css']
    //如果要使用sass,需要安裝node-sass和sass-loader
    npm install --save-dev node-sass sass-loader
    css: ['~/assets/css/main.scss']
    Nuxt.js會自動識別引入的副檔名,webpack會使用相應的前處理器進行解析
    需要注意的是如果要寫一些scss的變數或函式,就需要在build中進行一些配置
    複製程式碼

    3.使用swiper外掛

    //plugins/vue-swiper.js
    import Vue from 'vue'
    import VueAwesomeSwiper from 'vue-awesome-swiper'
    Vue.use(VueAwesomeSwiper)
    
    //nuxt.config.js
    css:['swiper/dist/css/swiper.css'],
    plugins:[{ src: '~plugins/vue-swiper', ssr: false }]
    複製程式碼

    4.配置404頁面

    因為專案使用靜態部署,所以Nuxt官網上說的方式(在layouts中寫error.vue頁面),因為並沒有在pages中新加頁面,構建時並不會生成對應的靜態頁,所以這種方式並不起作用.可以實現的方法是在plugins中監聽路由,如果沒有匹配到已經存在的路由,跳轉到404頁面

    //pages/error/index.vue
    <template>
        <div>
            404了
        </div>
    </template>
    
    //plugins/route.js
    export default ({ app }) => {
      const {context} = app
      const {matched} = context.route
      if(!matched.length) {
        window.location.href='/error'
      }
    }
    //nuxt.config.js
    plugins:[{ src: '~plugins/route.js', ssr: false}]
    複製程式碼

    5.設定代理

    //nuxt.config.js
    modules: ['@nuxtjs/proxy'],
    proxy: {
        '/api': {
          target: 'https://http://xxx.com',
          changeOrigin: true
         }
    }
    複製程式碼

    6.NUXT有自帶的axios模組@nuxtjs/axios,如果要用自帶的模組,可以這樣配置axios和proxy

    modules: [
    '@nuxtjs/axios',
    '@nuxtjs/proxy'
    ],
    //axios的一些配置
    axios: {
        proxy: true,
        retry: { retries: 1 }//失敗重試次數
    },
    proxy: {
        '/api': {
          target: 'https://xxx.com',
          changeOrigin: true
        }
    }
    如果要對axios請求設定統一攔截器,可以在外掛中配置
    //plugins/axios.js
    export default function ({ $axios, redirect }) {
      $axios.setHeader('Content-Type', 'application/json', ['post'])
      $axios.setToken('123', 'Bearer', ['post'])
      $axios.onRequest(config => {
        //設定請求攔截
      })
      $axios.onError(error => {
        //設定報錯處理
      })
      window.$axios = $axios //掛載到window上,方便在任何js檔案中呼叫
    }
    //nuxt.config.js
    plugins:[{src:'~plugins/axios',ssr:false}]
    //在vue檔案中使用
    mounted() {
      this.$axios.post('/xxx')
    }
    asyncData({app}) {
      app.$axios.post('/xxx')
    }
    //在store/actions中使用
    export const actions = {
        getData (state) {
           this.$axios.post('/xxx')
        }
    }
    複製程式碼

    但是發現@nuxtjs/axios在asyncData中使用執行nuxt generate靜態構建時會失敗,目前專案中還是使用的是axios,在api目錄中封裝了axios的呼叫,像在一般的Vue專案中使用一樣

    7.自定義服務埠

    server: {
        port: 8000, // default: 3000
    }
    複製程式碼

    nuxt.config.js還可以進行很多其他的配置,後面再學習補充.