Nuxt.js实现⼀个SSR的前端博客的⽰例代码
为什么要⽤Nuxt.js
公司现有的项⽬只有落地页是通过前端本⾝server读取pug⽂件进⾏服务端渲染的,当然是为了⾸屏加载速度以及SEO。Nuxt.js 是⼀个基于Vue.js的通⽤应⽤框架,预设了利⽤Vue.js开发服务端渲染的应⽤所需要的各种配置,只需要安装官⽅⽂档的要求进⾏开发,就可以很好的解决SSR的问题。我们以⼀个简单的博客为例,来实践⼀下Nuxt.js。
项⽬介绍
当前基于Nuxt.js的简化版博客,包括注册、登录、⽂章列表页⾯、⽂章详情页、以及⽤户列表页等⼏个页⾯,⽤户信息使⽤了Vux进⾏存储,异步数据使⽤了asyncData进⾏获取,配合了nuxtServerInit、cookie来处理刷新页⾯后Vux数据丢失的问题,同时使⽤了error模板页⾯处理常规错误,使⽤了中间件进⾏了简单的权限校验。该项⽬不⾜点,统⼀封装了axios的⽅法,但是没有考虑到服务端请求接⼝,token的处理。
⽬录结构
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.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: {: "errorChanged"}
注意:
Vue.js⽣命周期的钩⼦只有beforeCreate和created会在服务端和客户端渲染。
以上⽣命周期⾥都获取不到window对象。
asyncData和fetch我们可以拿到数据,不要尝试挂载数据到data上,此时获取不到this对象。
开发总结
如何修改默认启动端⼝?
可以在package.json下⾯修改配置,如下。
"config":{
"nuxt":{
"host":"127.0.0.1",
"port":"3304"
}
}
如何添加全局的样式?
可以在assets⾥添加全局Css⽂件,如在assets下的Css⽂件夹⽬录下添加了⼀个index.css⽂件,然后在nuxt-config.js⾥配置该css⽂件路径即可。 css:['~assers/css/index.css']
通过别名访问图⽚在template⾥是正确的,为何在Css设置背景图却报错?
在css配置的是,需要将'~/'后⾯的'/'去除掉。
<img src="~/static/logo.jpg"/>
backround-image:url('~static/logo.jpg');
如何添加路由动画?
同样,我们在Css⽂件⾥添加⼀些动画代码,⼀般样式会在其后⾯添加-active和-leave-active,其实和Vue动画形式⼀致。其中以page开头的动画,默认会作⽤于全部页⾯,如果想给特定的页⾯加动画,可以在对应的页⾯script⾥引⽤,如 transitions: 'bounce'即可。
.
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) }
nginx部署前端项目}
@keyframes bounce-out {
0% { transform: scale(1) }
50% { transform: scale(1.01) }
100% { transform: scale(1) }
}
路由参数如何传递?
同Vue-router,有声明式和编程式两种⽅式,⽆⾮是标签变成了 router.push(...)
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. 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 }) {
('my-api/stars')
.
then((res) => {
storemit('setStars', res.data)
})
}
}
</script>
// 或者使⽤ async 或 await 的模式简化代码如下:
<template>
<h1>Stars: {{ $store.state.stars }}</h1>
</template>
<script>
export default {
async fetch ({ store, params }) {
let { data } = ('my-api/stars')
storemit('setStars', data)
}
}
</script>
如何动态修改title的内容?
如果是写死的,可以直接修改head的配置。
head() {
return {
/
/ title: '',这⾥⼀旦声明,在asyncdata⾥修改也不起作⽤,直接以这个为准
meta: [
{
hid: 'description', // fig 替换唯⼀标识 hid { hid: 'description', name: 'description', content: 'Nuxt.js project' }
name: 'content',
content: '⽂章详情'
}
]
}
},
如果是动态数据从数据源⾥获取,然后通过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 函数,该函数仅在每个服务器端渲染中运,可以使⽤kie获取浏览器的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 (kie) {
let parsedResult = {};
kie.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.ken || !store.state.user.userInfo.name) {
// error({
// message: 'You are not connected',
// statusCode: 403
// })
redirect('/');
}
}
nginx部署
npm run build
选择build以后的四个⽂件: .nuxt, static, fig.js, package.json上传到服务器。
pm2 pm2 start npm --name 'package.json.name' -- run start
nginx配置
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论