ssr服务器端渲染

news/2024/7/10 0:48:35 标签: vue

1 SSR

全称:server side render(服务器端渲染),让我们可以在服务器端渲染应用程序
前端渲染问题:
1 白屏时间长,影响用户体验
2 不利于搜索引擎优化(SEO)
所以我们要在服务器端渲染应用程序
服务器端渲染:vue为服务器端渲染提供了模板–vue-server-render
该模块提供了createRenderer方法,来创建渲染器
渲染器提供了renderToString方法,渲染应用程序组件
渲染器实现了promise规范,因此可以通过then方法监听成功,可以通过cache方法监听失败

渲染模板

渲染模板:想在模板中渲染应用程序分成两步:
第一步 在createRenderer方法中,通过template引入模板文件
第二步 在模板文件中,通过 定义应用程序渲染位置
前后端同步渲染
浏览器端渲染(前端):
1 白屏时间长,影响用户体验 2 不利于SEO优化
服务器端渲染(后端):
无法绑定交互
为了解决浏览器端与服务器端渲染的问题,我们要使用前后端同步渲染技术

  • 1 html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <!-- 定义渲染位置 -->
    <!-- 注意,不要添加空格 -->
    <!--vue-ssr-outlet-->
</body>
</html>
  • 2 组件文件test.js
// 引入vue
let Vue = require('vue');
// 实例化
module.exports = new Vue({
    template: `
        <div>
            <h1 @click="clickH1">hello {{msg}}</h1>
            
        </div>
    `,
    data: {
        msg: 'test'
    },
    methods: {
    // 服务器端渲染无法绑定交互,所以此点击事件不会生效
        clickH1() {
            console.log('click h1');
        }
    },
})
  • 3 服务器端
// 引入express
let express = require('express');
// 引入ejs
let ejs = require('ejs');
// 引入vue在服务器端的渲染器
let { createRenderer } = require('vue-server-renderer');
// 引入fs
let fs = require('fs');
// 引入path
let path = require('path');
// 引入组件
let testApp = require('./test');

// 获取根目录
let root = process.cwd();

// 创建渲染器
let render = createRenderer({
    // 同步读取,引入模板文件
    template: fs.readFileSync(path.join(root, './views/index.html'), 'utf-8')
})


// 定义应用程序
let app = express()
// 拓展名
app.engine('.html', ejs.__express)

// 定义路由
app.get('/', (req, res) => {
    // 渲染字符串
    render.renderToString(testApp)
        .then(
            // 渲染成功
            data => res.end(data),
            // 渲染失败
            err => console.log('fail', err)
        )
})

// 监听端口号
app.listen(3000)

此时,就将组件渲染出来了,但是没有交互
在这里插入图片描述
在这里插入图片描述

2 前后端同步渲染

前端渲染
是为了给用户使用,因此希望资源加载的块(压缩,打包,添加指纹等等)
为了更好的效果,我们要加载样式;为了看到页面,我们要处理模板等,。。。
但是服务器端渲染不需要考虑这些问题,只需要一个应用程序组件,并最终将其渲染成字符串
因此前后端渲染有不同的入口文件,有不同的webpack配置
有不同的入口文件
前端入口文件,要让应用程序组件上树;后端入口文件,只需要一个应用程序组件
不同的webpack配置
前端要考虑样式,模板,性能优化(压缩,打包,添加指纹等)等
后端只要将ES Module规范,编译成CommonJS规范即可

  • 目录部署

config 存储配置文件
home 前端开发的项目
views 模板目录
static 静态资源
app.js 服务器入口文件

  • 服务器端配置

1 不需要模板,不用写html插件
2 不需要样式,不用写style-loader,不需要拆分样式
3 不需要性能优化、拆分模块,添加指纹,服务器端本地引入文件,因此打包在一起就可以了
4 通过target:node配置,说明给node使用
5 在output.libraryTarget:commonjs2,将ES Module规范转成commonjs规范
6 使用vue-server-renderer插件,发布json文件
在参数对象中,通过filename属性,可以自定义json文件的发布位置

  • 服务器端渲染

为了使用发布的json文件,vue-server-render提供了createBundleRenderer方法
第一个参数表示json文件
第二个参数表示配置对象
template定义模板文件的位置
插值语法:vue服务器端渲染允许我们在渲染页面的时候,向页面中插入一些数据
在renderToString方法中传递数据
在模板中,通过插值语法插入数据
{{}} 渲染文本插值
{{{}}} 渲染标签插值

  • 1 html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
	// 渲染位置
    <!--vue-ssr-outlet-->
</body>
</html>
<template>
<div id="app">
    <h1 @click="clickH1">app part -- {{msg}}-- {{$store.state.num}}</h1></div>
</template>
<script>
export default {
    data() {
        return {
            msg: 'hello'
        }
    },
    methods: {
        clickH1() {
            console.log('click h1');
        }
    }
}
</script>
  • 入口文件main.js
import Vue from 'vue';
// 引入应用程序
import App from './App';
// 引入store
import store from './store'
// 引入样式
import './style.scss';

export default new Vue({
    // 订阅
    store,
    // 渲染应用程序
    render: h => h(App)
})
  • 浏览器端入口文件entry-client.js
// 引入vue实例化对象,引入main.js
import app from './main';

// 上树
app.$mount('#app');
  • 服务器端入口文件entry-server.js
import app from './main';
服务器端不需要上树
// 暴露接口
export default app;
  • 定义webpack命令
{
    "scripts": {
        "client": "webpack --config ./config/webpack.client.js",
        "start:client": "webpack -w --config ./config/webpack.client.js",
        "serve": "webpack --config ./config/webpack.server.js",
        "start:serve": "webpack -w --config ./config/webpack.server.js",
        "start": "npm run client && npm run server"
    }
}
  • 浏览器端webpack配置
// 引入path
let path = require('path')
// vue插件
let { VueLoaderPlugin } = require('vue-loader');
// 引入html插件
let HtmlWebpackPlugin = require('html-webpack-plugin');
// 拆分css
let MiniCssExtractPluin = require('mini-css-extract-plugin')
// 压缩css
let OptimizeCssAssetsPlugin = require('optimize-css-assets-webpack-plugin');
// 获取根目录
const root = process.cwd();
module.exports = {
    // 模式
    mode: 'production',
    // 解决问题
    resolve: {
        // 别名
        alias: {
            vue$: 'vue/dist/vue.js',
            '@': path.join(root, './home/src'),
            '@v': path.join(root, './home/src/views'),
            '@c': path.join(root, './home/src/components')
        },
        // 扩展名
        extensions: ['.js', '.vue']
    },
    // 入口
    entry: './home/src/entry-client.js',
    // 发布
    output: {
        filename: 'static/[name].js',
        // 发布的相对位置位置
        path: root,
        // 修改进入静态文件的相对位置
        publicPath: '/'
    },
    // 模块
    module: {
        // 加载机
        rules: [
            // vue
            {
                test: /\.vue$/,
                loader: 'vue-loader'
            },
            // css
            {
                test: /\.css$/,
                use: [
                    // 'style-loader',
                    // 1 在样式文件的style-loader后面添加loader
                    MiniCssExtractPluin.loader,
                    'css-loader'
                ]
            },
            // less
            {
                test: /\.less$/,
                use: [
                    // 'style-loader',
                    MiniCssExtractPluin.loader,
                    'css-loader',
                    'less-loader'
                ]
            },
            // scss
            {
                test: /\.scss$/,
                use: [
                    // 'style-loader',
                    MiniCssExtractPluin.loader,
                    'css-loader',
                    'sass-loader'
                ]
            }
        ]
    },
    // 插件
    plugins: [
        // 使用vue加载机需要引入插件
        new VueLoaderPlugin(),
        // 处理模板
        new HtmlWebpackPlugin({
            // 模板位置
            template: './home/public/index.html',
            // 模板文件发布位置
            filename: './views/index.html',
            // 添加指纹
            hash: true,
            // 优化
            防止<!--vue-ssr-outlet-->被压缩
            minify: {
            
                collapseWhitespace: true,
                // 取消移除注释
                removeComments: false,
                removeRedundantAttributes:true,
                removeScriptTypeAttributes: true,
                removeStyleLinkTypeAttributes: true,
                useShortDoctype: true
            }
        }),
        //  2 定义文件发布的位置
        new MiniCssExtractPluin({
            filename: 'static/style.css'
        }),
        // 压缩css
        new OptimizeCssAssetsPlugin()
    ],
    // 拆分库文件
    optimization: {
        // 拆分库文件
        splitChunks: {
            cacheGroups: {
                lib: {
                    name: 'lib',
                    chunks: 'initial',
                    test: /node_modules/
                }
            }
        }
    }
}
  • 服务器端webpack配置
// 引入path
let path = require('path')
// vue插件
let { VueLoaderPlugin } = require('vue-loader');
// 引入服务器端渲染插件
let VueServerRendererPlugin = require('vue-server-renderer/server-plugin');


// 获取根目录
const root = process.cwd();
module.exports = {
    // 模式
    mode: 'development',

    通过target:node配置,说明给node使用
    // .....告诉webpack给node使用
    target: 'node',



    // 解决问题
    resolve: {
        // 别名
        alias: {
            vue$: 'vue/dist/vue.js',
            '@': path.join(root, './home/src'),
            '@v': path.join(root, './home/src/views'),
            '@c': path.join(root, './home/src/components')
        },
        // 扩展名
        extensions: ['.js', '.vue']
    },
    // 入口
    entry: './home/src/entry-server.js',
    // 发布
    output: {
        filename: 'static/[name].js',
        // 发布的相对位置位置
        path: root,
        // 修改进入静态文件的相对位置
        publicPath: '/',

在output.libraryTarget:commonjs2,将ES Module规范转成commonjs规范
        // ......编译成commonjs规范
        libraryTarget: 'commonjs2'


    },
    // 模块
    module: {
        // 加载机
        rules: [
            // vue
            {
                test: /\.vue$/,
                loader: 'vue-loader'
            },
            // css
            {
                test: /\.css$/,
                use: [
                    'css-loader'
                ]
            },
            // less
            {
                test: /\.less$/,
                use: [
                    'css-loader',
                    'less-loader'
                ]
            },
            // scss
            {
                test: /\.scss$/,
                use: [
                    'css-loader',
                    'sass-loader'
                ]
            }
        ]
    },
    // 插件
    plugins: [
        // 使用vue加载机需要引入插件
        new VueLoaderPlugin(),
        // 发布位置ES Module规范,编译成CommonJS规范,输出到bundle.json
		使用vue-server-renderer插件,发布json文件
		在参数对象中,通过filename属性,可以自定义json文件的发布位置
        new VueServerRendererPlugin({
            filename: '/server/bundle.json'
        })
    ]
}
  • 服务器端app.js
// 引入express
let express = require('express');
// 引入ejs
let ejs = require('ejs');
let path = require('path');
let fs = require('fs');
const root = process.cwd();
// 引入vue
let Vue = require('vue');
// 引入vue在服务器端的渲染器
let { createBundleRenderer } = require('vue-server-renderer');


// 创建渲染器


为了使用发布的json文件,vue-server-render提供了createBundleRenderer方法


let renderer = createBundleRenderer(path.join(root, './server/bundle.json'), {
    // 传递模板
    template: fs.readFileSync(path.join(root, './views/index.html'), 'utf-8')
})


// 定义应用程序
let app = express()
// 拓展名
app.engine('.html', ejs.__express)
// 静态化
app.use('/static/', express.static(path.join(root, './static')))

// 定义路由
app.get('/', (req, res) => {


    插值语法:vue服务器端渲染允许我们在渲染页面的时候,向页面中插入一些数据
	在renderToString方法中传递数据
    // 渲染
    renderer.renderToString()
        .then(
            html => res.end(html),
            err => console.log(err)
        )
})

// 监听端口号
app.listen(3000)

服务器端渲染的目的是为了SEO优化,并且让用户更快速的看到页面,浏览器端渲染的目的是,当页面加载之后,再次绑定交互,这就是前后端同步渲染

插值

	在renderToString方法中传递数据
	在模板中,通过插值语法插入数据
		{{}}	渲染文本插值
		{{{}}}	渲染标签插值
		
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    {{{seo}}}
    <title>{{title}}</title>
</head>
<body>
    <!--vue-ssr-outlet-->
</body>
</html>

服务器端app.js

插值语法:vue服务器端渲染允许我们在渲染页面的时候,向页面中插入一些数据
	在renderToString方法中传递数据
    // renderToString的参数表示提供的数据
    renderer.renderToString({
        title: 'hello',
        seo: `  
            <meta name="keywords" content="test">
            <meta name="description" content="It's just a test">
        `
    })

可以将标签插进去
在这里插入图片描述


http://www.niftyadmin.cn/n/734934.html

相关文章

gradle引用有classify的库

2019独角兽企业重金招聘Python工程师标准>>> com.app:common:1.0.0:{classify} 转载于:https://my.oschina.net/sfshine/blog/1924575

npm install

转载自(https://www.cnblogs.com/yetiezhu/p/12813532.html) 转载自(https://www.limitcode.com/detail/59a15b1a69e95702e0780249.html) 这里是引用 1 npm install 安装某个指定的版本 在npm中安装固定的版本号package&#xff0c;只需要在其后加 ‘版本号’ npm install …

栈跟队列

栈跟队列的区别&#xff1a;栈&#xff1a;先进后出&#xff0c;限定只能在表的一端进行插入和删除&#xff08;表尾&#xff09;&#xff0c;栈只能从头部取出数据&#xff0c;也就是最先放入的需要遍历整个栈最后才能取出&#xff0c;而且遍历时还得为数据开辟临时空间。队列…

ESXi创建datastore报错Fail to create VMFS datastore解决方法

在VMware vShere/ESXi中创建新的数据仓库datastore时&#xff0c;弹出下图所示的错误提示&#xff01;“Fail to create VMFS datastore VMFS xxxx”- cannot change the host configuration.对于首次遇到这个错误的人可能无从下手&#xff0c;那么下面就分享一下解决这个错误提…

promise async await

首先定义一个函数 let func1 () > new Promise((resolve, reject) > setTimeout(() > resolve(12345), 1000))函数func1执行的时候&#xff0c;会返回一个promise对象 let func1 () > new Promise((resolve, reject) > setTimeout(() > resolve(12345), …

「python」邮件脚本

整理一下&#xff0c;python 脚本发邮件的函数。 from email.mime.text import MIMEText from email.mime.multipart import MIMEMultipart from email.header import Headerdef sendMail():发送邮件# 发件人的邮箱账号sender "********" # 发件人的邮箱密码sender_…

vue-新建项目1

使用vue create新建项目之后&#xff0c;要封装一下axios和请求响应劫持 1 新建config文件夹&#xff0c;然后新建index.js文件&#xff0c;写配置 config / index.js export default {title: demo,baseUrl: {dev: http://localhost:3000, // 开发的时候后台接口的地址pro: …

Angular.js和Vue.js 深度对比

本文翻译自&#xff1a;https://dzone.com/articles/co... 转载请注明出自&#xff1a;葡萄城官网&#xff0c;葡萄城为开发者提供专业的开发工具、解决方案和服务&#xff0c;赋能开发者。 Vue.js 是开源的 JavaScript 框架&#xff0c;能够帮助开发者构建出美观的 Web 界面。…