服务器端渲染Nuxt.js Next.js
传统服务端渲染
art-template
包是一个模板解析器,其官网会有解析器的语法和使用
const express = require('express')
const fs = require('fs')
const template = require('art-template')
const app = express()
app.get('/', (req, res) => {
// 1. 获取页面模板
const templateStr = fs.readFileSync('./index.html', 'utf-8')
// 2. 获取数据
const data = JSON.parse(fs.readFileSync('./data.json', 'utf-8'))
// 3. 渲染:数据 + 模板 = 最终结果
const html = template.render(templateStr, data)
// 4. 把渲染结果发送给客户端
res.send(html)
})
这种方式不合理,不先进,应用前后端完全耦合在一起,网页越来越复杂,前端没有发挥效果,服务端压力大
现代的服务器渲染
客户端渲染因为javaScript执行完成后才会渲染,首屏加载慢,不利于SEO
以下是现代服务器端渲染方式
1.客户端发起请求
2.服务器端渲染首屏内容+生成客户端SPA相关资源
3.服务器端生成首屏资源发送给客户端
4.客户端直接展示服务器端渲染好的首屏内容
5.首屏中的SPA相关资源执行之后会激活客户端Vue
6.之后客户端所有的交互都由客户端SPA处理
Nuxt.js
Nuxt会监听pages目录中的文件更改,依据pages目录中的所有.vue
文件生成路由配置
动态路由(需要创建对应的以下划线作为前缀的 Vue 文件)
<nuxt-link :to="'/article/' + item.id">{{ item.title }}</nuxt-link>
嵌套路由(创建内嵌子路由,你需要添加一个 Vue 文件,同时添加一个与该文件同名的目录用来存放子视图组件,在父组件( .vue 文件) 内增加 用于显示子视图内容。
asyncData 方法
Nuxt.js增加了一个叫asyncData的方法,使得我们可以在设置组件的数据之前能
异步获取或处理数据。asyncData含有参数上下文对象context,context.params.id
获取路由动态参数
用于需要首屏渲染的页面,此时渲染的页面需要服务端返回给客户端,asyncData方法是服务端执行的,例如新闻文章列表页面为了首屏加载快,服务端去获取新闻列表信息的接口,需要返回数据,方法返回的数据会与组件的 data 合并
async asyncData ({ params }) {
const { data } = await getArticle(params.slug)
const { article } = data
const md = new MarkdownIt()
article.body = md.render(article.body)
return {
article
}
},
自定义路由规则
添加nuxt.config.js文件,在页面中可以自定义路由规则,添加插件(添加全局功能和第三方库集成)、添加全局样式,页面布局等等
module.exports = {
router: {
linkActiveClass: 'active',
// 自定义路由表规则
extendRoutes (routes, resolve) {
// 清除 Nuxt.js 基于 pages 目录默认生成的路由表规则
routes.splice(0)
}
},
server: {
host: '0.0.0.0',
port: 3000
},
// 注册插件
plugins: [
'~/plugins/request.js',
'~/plugins/dayjs.js'
]
}
Nuxt中集成的store与vue区别 nuxtServerInit
export const actions = {
// nuxtServerInit 是一个特殊的 action 方法
// 这个 action 会在服务端渲染期间自动调用
// 作用:初始化容器数据,传递数据给客户端使用
nuxtServerInit ({ commit }, { req }) {
let user = null
// 如果请求头中有 Cookie
if (req.headers.cookie) {
// 使用 cookieparser 把 cookie 字符串转为 JavaScript 对象
const parsed = cookieparser.parse(req.headers.cookie)
try {
user = JSON.parse(parsed.user)
} catch (err) {
// No valid cookie found
}
}
// 提交 mutation 修改 state 状态
commit('setUser', user)
}
}
Nuxt中的插件机制
设置全局过滤器
import Vue from 'vue'
import dayjs from 'dayjs'
// {{ 表达式 | 过滤器 }}
Vue.filter('date', (value, format = 'YYYY-MM-DD HH:mm:ss') => {
return dayjs(value).format(format)
})
使用全局的过滤器
{{ article.createdAt | date('MMM DD, YYYY') }}
获取全局的上下文对象
服务器在请求拦截器中获取到存储的store中的数据
/**
* 基于 axios 封装的请求模块
*/
import axios from 'axios'
// 创建请求对象
export const request = axios.create({
baseURL: 'https://conduit.productionready.io'
})
// 通过插件机制获取到上下文对象(query、params、req、res、app、store...)
// 插件导出函数必须作为 default 成员
export default ({ store }) => {
// 请求拦截器
// Add a request interceptor
// 任何请求都要经过请求拦截器
// 我们可以在请求拦截器中做一些公共的业务处理,例如统一设置 token
request.interceptors.request.use(function (config) {
// Do something before request is sent
// 请求就会经过这里
const { user } = store.state
if (user && user.token) {
config.headers.Authorization = `Token ${user.token}`
}
// 返回 config 请求配置对象
return config
}, function (error) {
// 如果请求失败(此时请求还没有发出去)就会进入这里
// Do something with request error
return Promise.reject(error)
})
}
Nuxt中的中间件机制
在middleware文件夹中默认导出函数,文件名即中间件的名字,应用给需要登录才能进入的页面设置权限
/**
* 验证是否登录的中间件
*/
export default function ({ store, redirect }) {
// If the user is not authenticated
if (!store.state.user) {
return redirect('/login')
}
}
使用在.vue文件夹中
export default {
middleware: 'authenticated',
name: 'SettingsIndex'
}