青山冶金 發表於 2024-3-30 14:33:00

服务器端渲染Nuxt.js Next.js

<h2 id="传统服务端渲染">传统服务端渲染</h2>
<p><code>art-template</code>包是一个模板解析器,其官网会有解析器的语法和使用</p>
<pre><code>const express = require('express')
const fs = require('fs')
const template = require('art-template')

const app = express()

app.get('/', (req, res) =&gt; {
// 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)
})

</code></pre>
<p>这种方式不合理,不先进,应用前后端完全耦合在一起,网页越来越复杂,前端没有发挥效果,服务端压力大</p>
<h2 id="现代的服务器渲染">现代的服务器渲染</h2>
<p>客户端渲染因为javaScript执行完成后才会渲染,首屏加载慢,不利于SEO<br>
以下是现代服务器端渲染方式<br>
<img src="https://img2024.cnblogs.com/blog/2953841/202403/2953841-20240330130419985-1500401993.png"><br>
1.客户端发起请求<br>
2.服务器端渲染首屏内容+生成客户端SPA相关资源<br>
3.服务器端生成首屏资源发送给客户端<br>
4.客户端直接展示服务器端渲染好的首屏内容<br>
5.首屏中的SPA相关资源执行之后会激活客户端Vue<br>
6.之后客户端所有的交互都由客户端SPA处理</p>
<h2 id="nuxtjs">Nuxt.js</h2>
<p>Nuxt会监听pages目录中的文件更改,依据pages目录中的所有<code>.vue</code>文件生成路由配置</p>
<h3 id="动态路由需要创建对应的以下划线作为前缀的-vue-文件">动态路由(需要创建对应的以下划线作为前缀的 Vue 文件)</h3>
<pre><code>&lt;nuxt-link :to="'/article/' + item.id"&gt;{{ item.title }}&lt;/nuxt-link&gt;
</code></pre>
<p><img src="https://img2024.cnblogs.com/blog/2953841/202403/2953841-20240330133328563-615109893.png"></p>
<h3 id="嵌套路由创建内嵌子路由你需要添加一个-vue-文件同时添加一个与该文件同名的目录用来存放子视图组件在父组件-vue-文件-内增加--用于显示子视图内容">嵌套路由(创建内嵌子路由,你需要添加一个 Vue 文件,同时添加一个与该文件同名的目录用来存放子视图组件,在父组件( .vue 文件) 内增加 <nuxt-child> 用于显示子视图内容。</nuxt-child></h3>
<h3 id="asyncdata-方法">asyncData 方法</h3>
<p>Nuxt.js增加了一个叫<em>asyncData</em>的方法,使得我们可以在设置组件的数据之前能<br>
异步获取或处理数据。<em>asyncData</em>含有参数上下文对象context,<code>context.params.id</code>获取路由动态参数<br>
用于需要首屏渲染的页面,此时渲染的页面需要服务端返回给客户端,<em>asyncData</em>方法是服务端执行的,例如新闻文章列表页面为了首屏加载快,服务端去获取新闻列表信息的接口,需要返回数据,方法返回的数据会与组件的 data 合并</p>
<pre><code>async asyncData ({ params }) {
    const { data } = await getArticle(params.slug)
    const { article } = data
    const md = new MarkdownIt()
    article.body = md.render(article.body)
    return {
      article
    }
},
</code></pre>
<h3 id="自定义路由规则">自定义路由规则</h3>
<p>添加nuxt.config.js文件,在页面中可以自定义路由规则,添加插件(添加全局功能和第三方库集成)、添加全局样式,页面布局等等</p>
<pre><code>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'
]
}
</code></pre>
<h3 id="nuxt中集成的store与vue区别-nuxtserverinit">Nuxt中集成的store与vue区别 nuxtServerInit</h3>
<pre><code>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)
}
}
</code></pre>
<h3 id="nuxt中的插件机制">Nuxt中的插件机制</h3>
<h4 id="设置全局过滤器">设置全局过滤器</h4>
<pre><code>import Vue from 'vue'
import dayjs from 'dayjs'

// {{ 表达式 | 过滤器 }}
Vue.filter('date', (value, format = 'YYYY-MM-DD HH:mm:ss') =&gt; {
return dayjs(value).format(format)
})
</code></pre>
<p>使用全局的过滤器</p>
<pre><code>{{ article.createdAt | date('MMM DD, YYYY') }}
</code></pre>
<h4 id="获取全局的上下文对象">获取全局的上下文对象</h4>
<p>服务器在请求拦截器中获取到存储的store中的数据</p>
<pre><code>/**
* 基于 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 }) =&gt; {

// 请求拦截器
// Add a request interceptor
// 任何请求都要经过请求拦截器
// 我们可以在请求拦截器中做一些公共的业务处理,例如统一设置 token
request.interceptors.request.use(function (config) {
    // Do something before request is sent
    // 请求就会经过这里
    const { user } = store.state

    if (user &amp;&amp; user.token) {
      config.headers.Authorization = `Token ${user.token}`
    }

    // 返回 config 请求配置对象
    return config
}, function (error) {
    // 如果请求失败(此时请求还没有发出去)就会进入这里
    // Do something with request error
    return Promise.reject(error)
})
}
</code></pre>
<h3 id="nuxt中的中间件机制">Nuxt中的中间件机制</h3>
<p>在middleware文件夹中默认导出函数,文件名即中间件的名字,应用给需要登录才能进入的页面设置权限</p>
<pre><code>/**
* 验证是否登录的中间件
*/
export default function ({ store, redirect }) {
// If the user is not authenticated
if (!store.state.user) {
    return redirect('/login')
}
}
</code></pre>
<p>使用在.vue文件夹中</p>
<pre><code>export default {
middleware: 'authenticated',
name: 'SettingsIndex'
}
</code></pre>
<h2 id="next">Next</h2>
<h3 id="两种预渲染方式">两种预渲染方式</h3>
<p><strong>服务器端渲染</strong><br>
<img src="https://img2024.cnblogs.com/blog/2953841/202409/2953841-20240909123318949-826892115.png"><br>
<img src="https://img2024.cnblogs.com/blog/2953841/202409/2953841-20240909123420086-1543207486.png"></p>
<p><strong>静态生成</strong><br>
<img src="https://img2024.cnblogs.com/blog/2953841/202409/2953841-20240909121027763-1291554450.png"><br>
<img src="https://img2024.cnblogs.com/blog/2953841/202409/2953841-20240909121123948-1838923530.png"><br>
<img src="https://img2024.cnblogs.com/blog/2953841/202409/2953841-20240909122345241-777794229.png"><br>
<img src="https://img2024.cnblogs.com/blog/2953841/202409/2953841-20240909122428122-1560196651.png"></p>
<h3 id="api-routes">API Routes</h3>
<p>在 Next.js 中,API Routes 允许你在应用内直接定义后端 API 路由<br>
创建 API Routes:在 pages/api 目录下创建文件,每个文件自动成为一个 API 端点。<br>
处理请求:通过 req 对象访问请求数据,通过 res 对象返回响应。<br>
处理不同的 HTTP 请求:可以处理 GET、POST、PUT、DELETE 等请求。<br>
服务器端操作:在 API Routes 中执行服务器端逻辑,如数据库操作。</p><br><br>
来源:https://www.cnblogs.com/zhixy/p/18105431
頁: [1]
查看完整版本: 服务器端渲染Nuxt.js Next.js