《Webpack5 核心原理与应用实践》学习笔记-> Vue服务端渲染

简介: 《Webpack5 核心原理与应用实践》学习笔记-> Vue服务端渲染


服务端渲染就是由服务端返回页面给客户端,老牌技术有jspapsphp等技术,而像vuereact等技术是属于客户端渲染。

客户端渲染的优势就是更少的带宽,更快的响应速度(因为请求和响应的内容变少)。

问题就是seo不友好,Time-To-Content 更长(因为要等所有的资源加载完毕才会开始渲染页面)。

SSR(Server Side Render) 正是为了解决这些问题而出现的技术。

本质上,SSR 是一种在服务端将组件渲染 HTML 字符串并发送到浏览器,最后在浏览器上将这些 HTML 片段“激活”为客户端上可交互的应用技术。


搭建vue环境


本次项目为新搭建的环境,所以不使用之前的配置文件,这里默认是空的目录。

这边建议新建一个空目录,然后npm init -y生成一个package.json就可以跟着我开始了。


  • 根目录下新建src目录
  • src目录下新建App.vue文件,内容如下


<template>
  <div :class="['main', cls]">
    <h3>{{ message }}</h3>
    <button @click="handleClick">Toggle</button>
  </div>
</template>
<script>
import { ref, computed } from "@vue/reactivity";
export default {
  setup() {
    const isActivity = ref(false);
    const cls = computed(() => (isActivity.value ? "activate" : "deactivate"));
    const handleClick = () => {
      isActivity.value = !isActivity.value;
    };
    return {
      isActivity,
      message: "Hello World",
      cls,
      handleClick,
    };
  },
};
</script>
<style>
h3 {
  color: #42b983;
}
.main {
  position: fixed;
  top: 0;
  left: 0;
  bottom: 0;
  right: 0;
  padding: 20px 12px;
  transition: background 0.3s linear;
}
.activate {
  background: #000;
}
.deactivate {
  background: #fff;
}
</style>

需要为客户端、服务端环境分别准备项目 Entry 文件,所以需要创建客户端的entry-client.js和服务端的entry-server.js


  • src目录下新建entry-client.js,内容如下


import { createSSRApp } from "vue";
import App from "./App.vue";
createSSRApp(App).mount("#app");

  • src目录下新建entry-server.js,内容如下


import { createSSRApp } from "vue";
import App from "./App.vue";
export default () => {
    return createSSRApp(App);
};

搭建ssr环境

ssr需要有两份资源文件,一份client客户端,一份server服务端。

  • 根目录下新建webpack.base.js文件,主要是复用配置


const path = require('path');
const { VueLoaderPlugin } = require("vue-loader");
module.exports = {
    output: {
        filename: '[name].js',
        path: path.resolve(__dirname, 'dist')
    },
    module: {
        rules: [
            {
                test: /.js$/,
                use: {
                    loader: 'babel-loader',
                }
            },
            {
                test: /.vue$/,
                use: {
                    loader: 'vue-loader'
                }
            }
        ]
    },
    resolve: {
        extensions: ['.ts', '.js'],
    },
    plugins: [
        new VueLoaderPlugin()
    ]
}

  • 根目录下新建webpack.client.js文件,主要是生成客户端资源的配置


const Merge = require("webpack-merge");
const path = require("path");
const HtmlWebpackPlugin = require("html-webpack-plugin");
const {WebpackManifestPlugin} = require("webpack-manifest-plugin");
const base = require("./webpack.base");
// 继承自 `webpack.base.js`
module.exports = Merge.merge(base, {
    mode: "development",
    entry: {
        // 入口指向 `entry-client.js` 文件
        client: path.join(__dirname, "./src/entry-client.js"),
    },
    output: {
        publicPath: "/",
    },
    module: {
        rules: [{test: /.css$/, use: ["style-loader", "css-loader"]}],
    },
    plugins: [
        // 这里使用 webpack-manifest-plugin 记录产物分布情况
        // 方面后续在 `server.js` 中使用
        new WebpackManifestPlugin({fileName: "manifest-client.json"}),
        // 自动生成 HTML 文件内容
        new HtmlWebpackPlugin({
            templateContent: `
<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <title>Webpack App</title>
</head>
<body>
  <div id="app" />
</body>
</html>
  `,
        }),
    ],
});

  • 根目录下新建webpack.server.js文件,主要是生成服务端资源的配置


const Merge = require("webpack-merge");
const path = require("path");
const {WebpackManifestPlugin} = require("webpack-manifest-plugin");
const base = require("./webpack.base");
module.exports = Merge.merge(base, {
    mode: "production",
    entry: {
        server: path.join(__dirname, "src/entry-server.js"),
    },
    target: "node",
    output: {
        // 打包后的结果会在 node 环境使用
        // 因此此处将模块化语句转译为 commonjs 形式
        libraryTarget: "commonjs2",
    },
    module: {
        rules: [
            {
                test: /.css$/,
                use: [
                    // 注意,这里用 `vue-style-loader` 而不是 `style-loader`
                    // 因为 `vue-style-loader` 对 SSR 模式更友好
                    "vue-style-loader",
                    {
                        loader: "css-loader",
                        options: {
                            esModule: false,
                        },
                    },
                ],
            },
        ],
    },
    plugins: [
        // 这里使用 webpack-manifest-plugin 记录产物分布情况
        // 方面后续在 `server.js` 中使用
        new WebpackManifestPlugin({fileName: "manifest-server.json"}),
    ],
});

为了方便构建,所以我这里还是将生成资源文件的命令封装到了package.json文件中,如下


{
    "scripts": {
      "build:client": "npx webpack --config webpack.client.js",
      "build:server": "npx webpack --config webpack.server.js"
    }
}

完成上述操作后,执行npm run build:clientnpm run build:server分别生成客服端和服务端的资源文件。


使用node搭建发布环境



  • 根目录下新建server.js,内容如下


const express = require("express");
const path = require("path");
const { renderToString } = require("@vue/server-renderer");
// 通过 manifest 文件,找到正确的产物路径
const clientManifest = require("./dist/manifest-client.json");
const serverManifest = require("./dist/manifest-server.json");
const serverBundle = path.join(
    __dirname,
    "./dist",
    serverManifest["server.js"]
);
// 这里就对标到 `entry-server.js` 导出的工厂函数
const createApp = require(serverBundle).default;
const server = express();
server.get("/", async (req, res) => {
    const app = createApp();
    const html = await renderToString(app);
    const clientBundle = clientManifest["client.js"];
    res.send(`
<!DOCTYPE html>
<html>
    <head>
      <title>Vue SSR Example</title>
    </head>
    <body>
      <!-- 注入组件运行结果 -->
      <div id="app">${html}</div>
      <!-- 注入客户端代码产物路径 -->
      <!-- 实现 Hydrate 效果 -->
      <script src="${clientBundle}"></script>
    </body>
</html>
    `);
});
server.use(express.static("./dist"));
server.listen(3000, () => {
    console.log("服务启动成功:http://localhost:3000");
);

  • 命令行:node server.js运行查看效果吧

由于这里涉及到的依赖比较多,所以这一节就没有讲安装依赖的代码了,所以这里直接附上package.json文件


{
  "name": "vue-ssr",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "build:client": "npx webpack --config webpack.client.js",
    "build:server": "npx webpack --config webpack.server.js",
    "test": "echo "Error: no test specified" && exit 1"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "@babel/core": "^7.18.9",
    "@vue/server-renderer": "^3.2.37",
    "babel-loader": "^8.2.5",
    "css-loader": "^6.7.1",
    "express": "^4.18.1",
    "html-webpack-plugin": "^5.5.0",
    "style-loader": "^3.3.1",
    "vue": "^3.2.37",
    "vue-loader": "^17.0.0",
    "vue-style-loader": "^4.1.3",
    "webpack": "^5.74.0",
    "webpack-cli": "^4.10.0",
    "webpack-manifest-plugin": "^5.0.0",
    "webpack-merge": "^5.8.0"
  }
}

总结


本文介绍如何使用 Webpack 开发 Vue 应用,从最基础的 Vue SFC 文件编译;到复用更多基础编译工具;再到 SSR、SSG 等高阶用法;以及如何借助 Vue CLI 迅速搭建开发环境。


虽然 Vue 官方团队已经推出了一套更轻、更快的编译工具:Vite,但相关生态还不太成熟,短期内使用 Webpack 依然会是更保险可控的方案,因此依然值得投入时间精力深入学习 Webpack。


我这里把vue-cli的部分给省略了,我是学习webpack的,所以只记录和webpakc相关的,其他的当做拓展学习,这里提一下Nuxt.jsQuasar,感兴趣的可以自己去找资源学习,瑞思拜。

示例代码:vue-ssr


目录
相关文章
|
6月前
|
JavaScript 前端开发 算法
vue渲染页面的原理
vue渲染页面的原理
218 56
|
6月前
|
JavaScript 前端开发 UED
vue2和vue3的响应式原理有何不同?
大家好,我是V哥。本文详细对比了Vue 2与Vue 3的响应式原理:Vue 2基于`Object.defineProperty()`,适合小型项目但存在性能瓶颈;Vue 3采用`Proxy`,大幅优化初始化、更新性能及内存占用,更高效稳定。此外,我建议前端开发者关注鸿蒙趋势,2025年将是国产化替代关键期,推荐《鸿蒙 HarmonyOS 开发之路》卷1助你入行。老项目用Vue 2?不妨升级到Vue 3,提升用户体验!关注V哥爱编程,全栈开发轻松上手。
401 2
|
7月前
|
移动开发 JavaScript API
Vue Router 核心原理
Vue Router 是 Vue.js 的官方路由管理器,用于实现单页面应用(SPA)的路由功能。其核心原理包括路由配置、监听浏览器事件和组件渲染等。通过定义路径与组件的映射关系,Vue Router 将用户访问的路径与对应的组件关联,支持哈希和历史模式监听 URL 变化,确保页面导航时正确渲染组件。
|
8月前
|
敏捷开发 人工智能 JavaScript
Figma-Low-Code:快速将Figma设计转换为Vue.js应用,支持低代码渲染、数据绑定
Figma-Low-Code 是一个开源项目,能够直接将 Figma 设计转换为 Vue.js 应用程序,减少设计师与开发者之间的交接时间,支持低代码渲染和数据绑定。
485 3
Figma-Low-Code:快速将Figma设计转换为Vue.js应用,支持低代码渲染、数据绑定
|
10月前
|
JavaScript 前端开发 API
Vue.js响应式原理深度解析:从Vue 2到Vue 3的演进
Vue.js响应式原理深度解析:从Vue 2到Vue 3的演进
260 17
|
10月前
|
JavaScript 前端开发 API
介绍一下Vue中的响应式原理
介绍一下Vue中的响应式原理
190 1
|
10月前
|
监控 JavaScript 算法
深度剖析 Vue.js 响应式原理:从数据劫持到视图更新的全流程详解
本文深入解析Vue.js的响应式机制,从数据劫持到视图更新的全过程,详细讲解了其实现原理和运作流程。
|
11月前
|
JavaScript
webpack学习三:webpack初始化整合配置vue,一步一步的抽离代码块整合vue。
这篇文章是关于如何在webpack环境中配置Vue.js,包括安装Vue.js、解决报错、理解el与template的区别、使用SPA模式、抽离模板为对象、封装为单独的js文件、安装vue-loader时遇到的问题及解决方案,以及整个过程的总结。
223 2
webpack学习三:webpack初始化整合配置vue,一步一步的抽离代码块整合vue。
|
10月前
|
JavaScript
Vue 双向数据绑定原理
Vue的双向数据绑定通过其核心的响应式系统实现,主要由Observer、Compiler和Watcher三个部分组成。Observer负责观察数据对象的所有属性,将其转换为getter和setter;Compiler解析模板指令,初始化视图并订阅数据变化;Watcher作为连接Observer和Compiler的桥梁,当数据变化时触发相应的更新操作。这种机制确保了数据模型与视图之间的自动同步。
|
10月前
|
缓存 JavaScript 搜索推荐
Vue SSR(服务端渲染)预渲染的工作原理
【10月更文挑战第23天】Vue SSR 预渲染通过一系列复杂的步骤和机制,实现了在服务器端生成静态 HTML 页面的目标。它为提升 Vue 应用的性能、SEO 效果以及用户体验提供了有力的支持。随着技术的不断发展,Vue SSR 预渲染技术也将不断完善和创新,以适应不断变化的互联网环境和用户需求。
254 9