《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


目录
相关文章
|
14天前
|
JavaScript
vue异步渲染
vue异步渲染
|
7天前
|
缓存 JavaScript 前端开发
「offer来了」从基础到进阶原理,从vue2到vue3,48个知识点保姆级带你巩固vuejs知识体系
该文章全面覆盖了Vue.js从基础知识到进阶原理的48个核心知识点,包括Vue CLI项目结构、组件生命周期、响应式原理、Composition API的使用等内容,并针对Vue 2与Vue 3的不同特性进行了详细对比与讲解。
「offer来了」从基础到进阶原理,从vue2到vue3,48个知识点保姆级带你巩固vuejs知识体系
|
9天前
|
JavaScript 测试技术 Windows
vue配置webpack生产环境.env.production、测试环境.env.development(配置不同环境的打包访问地址)
本文介绍了如何使用vue-cli和webpack为Vue项目配置不同的生产和测试环境,包括修改`package.json`脚本、使用`cross-env`处理环境变量、创建不同环境的`.env`文件,并在`webpack.prod.conf.js`中使用`DefinePlugin`来应用这些环境变量。
25 2
vue配置webpack生产环境.env.production、测试环境.env.development(配置不同环境的打包访问地址)
|
6天前
|
JavaScript 前端开发 IDE
Vue学习笔记5:用Vue的事件监听 实现数据更新的实时视图显示
Vue学习笔记5:用Vue的事件监听 实现数据更新的实时视图显示
|
6天前
|
JavaScript 前端开发 API
Vue学习笔记4:用reactive() 实现数据更新的实时视图显示
Vue学习笔记4:用reactive() 实现数据更新的实时视图显示
|
6天前
|
JavaScript 前端开发
Vue学习笔记8:解决Vue学习笔记7中用v-for指令渲染列表遇到两个问题
Vue学习笔记8:解决Vue学习笔记7中用v-for指令渲染列表遇到两个问题
|
6天前
|
JavaScript 前端开发 API
Vue学习笔记7:使用v-for指令渲染列表
Vue学习笔记7:使用v-for指令渲染列表
|
8天前
|
人工智能 JavaScript 索引
Duplicate keys detected: This may cause an update error.【Vue遍历渲染报错的解决】
这篇文章讨论了在Vue中进行列表渲染时遇到的“Duplicate keys detected”错误。这个错误通常发生在使用 `v-for` 指令渲染列表时,如果没有为每个循环项指定一个唯一的 `key` 属性,或者指定的 `key` 属性值重复了。文章提供了导致错误的原始代码示例,并给出了修正后的代码,通过在 `key` 绑定中加入索引确保 `key` 的唯一性。此外,文章还解释了为什么需要唯一 `key` 以及如何解决这个问题。
Duplicate keys detected: This may cause an update error.【Vue遍历渲染报错的解决】
|
8天前
vue2的响应式原理学“废”了吗?继续观摩vue3响应式原理Proxy
该文章对比了Vue2与Vue3在响应式原理上的不同,重点介绍了Vue3如何利用Proxy替代Object.defineProperty来实现更高效的数据响应机制,并探讨了这种方式带来的优势与挑战。
vue2的响应式原理学“废”了吗?继续观摩vue3响应式原理Proxy
|
8天前
|
JavaScript 前端开发 UED
组件库实战 | 用vue3+ts实现全局Header和列表数据渲染ColumnList
该文章详细介绍了如何使用Vue3结合TypeScript来开发全局Header组件和列表数据渲染组件ColumnList,并提供了从设计到实现的完整步骤指导。
下一篇
无影云桌面