Vue-SSR学习笔记

本文详细介绍了如何创建一个Vue.js服务端渲染应用,包括设置Vue实例、使用vue-server-renderer创建渲染器、处理HTTP请求、构建服务器、模板插入及客户端激活等步骤。同时,讲解了服务端渲染对于SEO的重要性以及Nuxt.js框架的工作原理。
摘要由CSDN通过智能技术生成

一、创建一个Vue实例

先创建一个服务器入口文件server.js,安装Vue提供的服务端渲染的工具npm install vue vue-server-renderer --save
如果是客户端渲染会有<div id=”app”></div>的标签,最终内容是通过JS动态渲染进去的,但对SEO不太友好,所以要用服务端渲染
先渲染一个Vue实例

const Vue = require('vue')
const app = new Vue({
    template: '<div>Hello Vue SSR!</div>'
})

然后使用vue-server-renderer的createRenderer方法创建一个渲染器,再调用renderToString把Vue实例渲染成字符串返回

const renderer = require('vue-server-renderer').createRenderer()
renderer.renderToString(app, (err, html) => {
    if(err) throw err;
    console.log(html)
})

执行server.js node server.js 可以看到渲染后的html模板结果
在这里插入图片描述

接下来就是创建一个服务器,然后把html模板结果返回
下载express框架 npm install express --save 引入并执行express

const server = require('express')()

然后处理get请求

server.get('*', (req, res) => {}

所有的get请求都经过该中间件,里面创建一个Vue实例,它含有一个url属性返回请求路径,再把它渲染到页面

const app = new Vue({
        data: {
            url: req.url
        },
        template: '<div>当前访问的url是:{{url}}</div>'
    })

然后把Vue实例用渲染器渲染到页面上,如果出错则报错,否则就返回HTML模板

renderer.renderToString(app, (err, html) => {
        if(err) {
            res.status(500).end('服务器内部错误')
        } else{
            res.end(`
            <!DOCTYPE html>
            <html lang="zh">
                <meta charset="utf-8"></meta>
                <head><title>Hello SSR</title></head>
                <body>${html}</body>
            </html>
            `)
        }
    })

服务器运行在8080端口

server.listen(8080, () => {
    console.log('服务器运行在8080端口')
})

访问8080端口,可以看到页面渲染成功
在这里插入图片描述

然后查看源代码,可以看到他是显示一个完整的HTML模板

Nuxt也是这样做的,只不过做了很多方法的封装
在这里插入图片描述

二、使用模板

单独新建一个index.template.html文件要想服务器返回的HTML模板插入到template文件,先在template.html文件添加一个固定的注释节点

<body>
    <!--vue-ssr-outlet-->
</body>

引入template文件,那么渲染器就会把模板渲染到注释节点上去

const VueServerRenderer = require('vue-server-renderer')

    const template = fs.readFileSync('./index.template.html', 'utf-8')
    const renderer = VueServerRenderer.createRenderer({
        template
    })

所以就不用手写模板字符串了,不够优雅, 直接返回渲染后的模板

renderer.renderToString(app, (err, html) => {
        if(err) {
            res.status(500).end('服务器内部错误')
        } else{
            res.end(html)
        }
    })

此外还可以传入额外内容,例如定义一个上下文对象,保存title标题和meta元信息

const context = {
        title: 'Vue SSR',
        metas: `
        <meta name="keyword" content="vue,ssr">
        <meta name="description" content="vue ssr demo">
        `
    }

在渲染器第二个参数传入context

renderer.renderToString(app, context, (err, html) => {})

然后使用插值的方式插入模板文件
在这里插入图片描述

可以看到标题成功渲染,而元信息标签被转义后渲染,所以要使用{{{}}}插值则原封不动地渲染

在这里插入图片描述
在这里插入图片描述

三、服务端渲染Vue项目的源码结构

vue-ssr官方图
在这里插入图片描述

app.js就是整个Vue程序的入口文件,客户端渲染的Vue2项目流程

import Vue from 'vue'

new Vue().$mount()

但是我们服务端Node.js是没有DOM这个概念的,只有ECMAscript,所以$mount挂载的代码应该移动到客户端里面去,所以存在两个入口:一个服务端入口(Server entry)和一个客户端入口(Client entry)
所以先定义一个App.vue组件

<template>
  <div id="app">
      <div class="demo" @click="onClick">
          一段内容
      </div>
  </div>
</template>

<script>
export default {
    methods: {
        onClick() {
            console.log('handleClick')
        }
    }
}
</script>
<style>
.demo {
    width: 300px;
    height: 300px;
    background-color: orange;
}
</style>

app.js定义一个工厂函数返回一个渲染App组件的Vue实例,但先不挂载到页面,工厂函数是为了防止污染

import Vue from 'vue'
import App from './App.vue'

export function createApp() {
    const app = new Vue({
        render: h => h(App)
    })
    return { app }
}

这时回到客户端入口,引入并执行工厂函数,并把App组件挂载到#app节点即可

import { createApp } from './app.js'

const { app } = createApp()
app.$mount('#app')

服务端入口一样创建Vue实例,但是导出一个函数在每次请求时执行创建并返回一个新实例防止污染。

import { createApp } from "./app";

export default () => {
    const { app } = createApp()
    return app
}

中间通过webpack打包客户端入口打包到browser,服务端的打包到Node server,server bundle作用是渲染器根据它渲染出Vue的服务端渲染结果,他是一个静态内容字符串,插入到HTML里面,返回到浏览器端,所以他没有动态交互逻辑。此时就需要客户端打包后的结果经过hydrate(激活)动态逻辑。

先配置webpack配置
webpack.base是公共配置

const path = require('path')
const VueLoaderPlugin = require('vue-loader/lib/plugin')

const resolve = (dir) => {
    return path.resolve(__dirname, dir)
}
module.exports = {
    output: {
        filename: '[name].bundle.js',
        path: resolve('../dist')
    },
    resolve: {
        extensions: ['.js', '.vue']
    },
    module: {
        rules: [
            {
                test: /\.js$/,
                use: {
                    loader: 'babel-loader',
                    options: {
                        presets: ['@babel/preset-env']
                    }
                },
                exclude: /node_modules/
            }, 
            {
                test: /\.css$/,
                use: ['vue-style-loader', 'css-loader']
            },
            {
                test: /\.vue$/,
                use: 'vue-loader'
            }
        ]
    },
    plugins: [
        new VueLoaderPlugin()
    ]
}

webpack.client是客户端特有配置,比如入口不一样

const path = require('path')

const { merge } = require('webpack-merge')
const  base = require('./webpack.base')
const resolve = (dir) => {
    return path.resolve(__dirname, dir)
}
module.exports = merge(base, {
    entry: {
        client: resolve('../src/entry-client.js')
    }
})

webpack.server是服务端特有配置,它要配置和node风格匹配的相关输入和目标等

const path = require('path')

const { merge } = require('webpack-merge')
const  base = require('./webpack.base')
const resolve = (dir) => {
    return path.resolve(__dirname, dir)
}
module.exports = merge(base, {
    entry: {
        server: resolve('../src/entry-server.js')
    },
    target: 'node',
    output: {
        libraryTarget: 'commonjs2'
    },
    plugins: [
        new HtmlWebpackPlugin({
            filename: 'index.ssr.html',
            template: resolve('../public/index.ssr.html'),
            excludeChunks: ['server'],    // 与server相关的js不需要,只需要模板字符串
            minify: {
                removeComments: false     // 不希望删掉注释节点
            }
        })
    ]
})

运行npm run client:build打包得到dist目录下client.bundle.js客户端的代码
在这里插入图片描述
然后再运行npm run server:build得到服务端打包后的html文件和服务端的js代码,但server.bundle.js不会直接引入到index.ssr.html,而是在服务器中使用
在这里插入图片描述

四、客户端激活

用渲染器的createBundleRenderer方法可以把如css之类的样式插入到模板,第一个参数是打包后的server文件,然后配置打包后的html模板

const serverBundle = fs.readFileSync('./dist/server.bundle.js', 'utf-8')
const template = fs.readFileSync('./dist/index.ssr.html', 'utf-8')

const renderer = VueServerRenderer.createBundleRenderer(serverBundle, {
    template
})

再把服务端渲染的结果返回到浏览器

server.get('*', (req, res) => {
    renderer.renderToString().then(html => {
        res.send(html)
    })
})

此时可以看到html渲染完成
在这里插入图片描述
查看源代码可以看到是服务端渲染后的结果
在这里插入图片描述
但是css样式和点击事件不生效,那是因为html中没有引入相应的client打包后的js文件进行样式添加和交互逻辑的添加,所以需要进行客户端的激活。本质上是把客户端打包后的结果放在服务端生成的html文件上

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Vue SSR</title>
</head>
<body>
    <!--vue-ssr-outlet-->
    <script src="./client.bundle.js"></script>
</body>
</html>

运行服务器可以看到client文件已被激活,但是还是不生效
在这里插入图片描述
原因是client.bundle.js不被视作为静态资源文件,所以需要配置静态资源

server.use(express.static(path.resolve(__dirname, 'dist')))

此时js文件能够正确加载,样式生效
在这里插入图片描述
点击事件也能正常触发
在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Vue SSRVue 服务器端渲染)是一种将 Vue 应用程序在服务器端渲染成 HTML 的技术。它可以提供更好的首次加载性能和更好的搜索引擎优化(SEO)。 下面是 Vue SSR 的详细使用及部署步骤: 1. 创建 Vue 项目:首先,你需要创建一个 Vue 项目。你可以使用 Vue CLI 来创建一个新的项目,或者在现有的项目中添加 SSR 功能。 2. 配置服务器:为了支持服务器端渲染,你需要设置一个服务器来处理 Vue SSR。你可以选择使用 Node.js、Express、Koa 或其他支持服务器端 JavaScript 运行环境。在服务器中,你需要安装并配置相应的依赖项,如 `vue-server-renderer` 和 `vue-router`。 3. 创建服务器入口:在服务器端代码中,你需要创建一个入口文件,用于处理客户端请求并渲染 Vue 应用。这个入口文件通常会使用 `createRenderer` 方法来创建一个渲染器,并将其绑定到服务器实例上。 4. 创建客户端入口:与传统的客户端渲染不同,Vue SSR 需要在客户端上重新激活 Vue 应用。因此,你需要创建一个客户端入口文件,用于在浏览器中重新挂载 Vue 应用。 5. 创建路由配置:如果你的应用使用了 Vue Router,你需要在服务器和客户端上都创建相同的路由配置。这样,在服务器端渲染时可以根据当前的 URL 来加载正确的组件。 6. 编写模板文件:为了在服务器端渲染期间生成 HTML,你需要编写一个模板文件。这个模板文件可以包含一些占位符,用于插入 Vue 应用生成的内容。 7. 服务器部署:最后,你需要将服务器端代码部署到一个服务器上,以便可以通过 URL 访问到你的 Vue SSR 应用。 需要注意的是,Vue SSR配置和使用相对复杂,需要一定的前端和服务器端开发经验。如果你是初学者,建议先熟悉 Vue 和服务器端开发的基础知识,再尝试使用 Vue SSR。 以上是关于 Vue SSR 的简要介绍和部署步骤,希望对你有所帮助!如果你有更多的问题,请随时提问。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值