《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


目录
相关文章
|
1天前
|
存储 缓存 JavaScript
解密前端框架Vue.js的响应式原理
作为当下最流行的前端框架之一,Vue.js的响应式原理是其核心之一。本文将深入探讨Vue.js的响应式原理,从数据劫持、依赖收集到更新视图的完整流程,帮助读者更好地理解Vue.js框架的工作方式。
|
16天前
|
JavaScript 前端开发 API
|
19天前
|
JavaScript 前端开发 开发者
深入理解前端框架Vue.js的数据响应式原理
本文将深入探讨Vue.js前端框架中的数据响应式原理,包括双向绑定、依赖追踪和虚拟DOM等核心概念。通过详细解析Vue.js内部实现机制,读者能够更好地理解其工作原理,并在实际开发中更灵活地运用。
|
20天前
|
设计模式 JavaScript 前端开发
第十六章 vue数据监测原理
第十六章 vue数据监测原理
35 0
|
20天前
|
JavaScript 前端开发 算法
探究前端框架Vue.js的响应式原理
本文将深入探讨前端框架Vue.js的核心特性——响应式原理。我们将介绍Vue.js中的数据绑定、依赖追踪和虚拟DOM等概念,并通过具体的示例代码解析其工作机制。通过了解Vue.js的响应式原理,开发者可以更好地利用这一特性构建灵活、高效的前端应用。
|
21天前
|
JavaScript 前端开发 API
Vue中的render函数和template渲染原理有什么不同?
Vue中的render函数和template渲染原理有什么不同?
10 0
|
22天前
|
存储 JavaScript 前端开发
从入门到项目实战 - Vue 列表渲染
从入门到项目实战 - Vue 列表渲染
40 0
|
25天前
|
JavaScript 算法 前端开发
Vue diff 算法探秘:如何实现快速渲染
Vue diff 算法探秘:如何实现快速渲染
Vue diff 算法探秘:如何实现快速渲染
|
25天前
|
存储 缓存 JavaScript
深入探索 Vue 响应式原理:数据驱动视图的奥秘
深入探索 Vue 响应式原理:数据驱动视图的奥秘
深入探索 Vue 响应式原理:数据驱动视图的奥秘
|
26天前
|
JavaScript 前端开发 编译器
解密Vue 3:透过原理看框架,揭开它的神秘面纱
解密Vue 3:透过原理看框架,揭开它的神秘面纱

相关产品

  • 云迁移中心