公司最近要做一個新產品的官網,要求要對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.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中的全域性配置.
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" } 複製程式碼
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還可以進行很多其他的配置,後面再學習補充.