公司現有的項目只有落地頁是經過前端自己server讀取pug文件進行服務端渲染的,固然是爲了首屏加載速度以及SEO。Nuxt.js 是一個基於Vue.js的通用應用框架,預設了利用Vue.js開發服務端渲染的應用所須要的各類配置,只須要安裝官方文檔的要求進行開發,就能夠很好的解決SSR的問題。咱們以一個簡單的博客爲例,來實踐一下Nuxt.js。javascript
當前基於Nuxt.js的簡化版博客,包括註冊、登陸、文章列表頁面、文章詳情頁、以及用戶列表頁等幾個頁面,用戶信息使用了Vux進行存儲,異步數據使用了asyncData進行獲取,配合了nuxtServerInit、cookie來處理刷新頁面後Vux數據丟失的問題,同時使用了error模板頁面處理常規錯誤,使用了中間件進行了簡單的權限校驗。該項目不足點,統一封裝了axios的方法,可是沒有考慮到服務端請求接口,token的處理。css
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
能夠在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
在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)
}
複製代碼
能夠在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.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({ 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 }
}
複製代碼
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
複製代碼
head() {
return {
// title: '',這裏一旦聲明,在asyncdata裏修改也不起做用,直接以這個爲準
meta: [
{
hid: 'description', // nuxt.config 替換惟一標識 hid { hid: 'description', name: 'description', content: 'Nuxt.js project' }
name: 'content',
content: '文章詳情'
}
]
}
},
複製代碼
asyncData({ app, params }) {
const id = params.id;
return getArticleDetail({ id })
.then(result => {
app.head.title = result.title;
})
.catch(err => {})
}
複製代碼
登陸成功之後,咱們會在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('/');
}
}
複製代碼
查看網頁源代碼能夠看到:
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實現了一個服務端渲染的簡易博客。