v-island,vue3打造的ssg框架-02,构建基本主题渲染逻辑

124 阅读3分钟

前言

本篇是承接上篇《v-island,vue3打造的ssg框架-01,初始化项目》,完成项目的最基本搭建后,继续完成主题渲染逻辑,也就是如何使用vue渲染我们的文章UI。

1、安装vue3

我们使用vite作为web服务以及前端编译工具,所以除了安装vue3还要安装解析vue的插件,如下:

- pnpm install vue @vitejs/plugin-vue

然后在dev.ts中新增代码,修改后如下内容:

- import { createServer } from "vite";
- import vue from "@vitejs/plugin-vue";
-
- const rollupOptions = {
-   external: ["vue"],
- };
-
- export default function createDevServer(root: string) {
-   return createServer({
-     root,
-     plugins: [vue()],
-     build: { rollupOptions },
-   });
- }

2、创建主题的入口

我们先在src下新建runtime和theme-default文件夹,runtime文件夹主题的入口文件,theme-default文件夹存放主题的ui文件,runtime文件夹下再创建main.ts,theme-default文件夹下创建Layout文件夹和index.ts文件,Layout下再创建index.vue文件,具体文件目录结构如下

- bin
   |--- index.js
- src
   |--- node
         |--- cli.ts
         |--- dev.ts
   |--- runtime
         |--- main.ts
   |--- theme-default
         |--- Layout
               |---index.vue
         |--- index.ts
- package.json
- pnpm-lock.yaml
- tsconfig.json

创建完各文件和文件夹后,向文件中添加代码,首先是vue的入口,添加如下代码:

import { createApp } from "vue";

import Layout from "../theme-default";

createApp(Layout).mount("#root");

然后向主题ui添加简单模版代码:

/** /src/theme-default/index.ts */
import Layout from "./Layout/index.vue";

export default Layout;
// /src/theme-default/Layout/index.vue
<template>
  <div>12313</div>
  <button @click="count++">count is {{ count }}</button>
</template>

<script lang="ts">
import { ref } from "vue";

export default {
  name: "Layout",
  setup() {
    const count = ref(0);

    return {count}
  }
};

</script>

这将是页面主题UI的入口。完成这些只是页面的基本架构,但是目前web服务还读取不到这个入口,接下来将写一个vite的插件来读取这个入口,然后解析到页面中。

3、完成解析vue入口的插件

首先在node文件夹下创建一个存放公用常量的地方,文件夹为/constants/index.ts,然后创建插件编写的目录,同样存放在node文件夹,/plugin-island/indexHtml.ts,当读取到vue入口后,要把整体塞进一个模版html,这样才能访问,因此在根目录再创建一个template.html,整体目录如下:

- bin
   |--- index.js
- src
   |--- node
         |--- constants
               |--- index.ts
         |--- plugin-island
               |--- indexHtml.ts
         |--- cli.ts
         |--- dev.ts
   |--- runtime
         |--- main.ts
   |--- theme-default
         |--- Layout
               |---index.vue
         |--- index.ts
- package.json
- pnpm-lock.yaml
- tsconfig.json
- template.html

接下来添加代码,template.html添加如下代码:

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>v-island</title>
</head>

<body>
  <div id="root"></div>
</body>

</html>

公用常量添加一些路径常量:

/** /src/node/constants/index.ts */

import { join } from "path";

export const PACKAGE_ROOT = join(__dirname, "..", "..", "..");

export const CLIENT_ENTRY_PATH = join(
  PACKAGE_ROOT,
  "src",
  "runtime",
  "main.ts"
);

export const DEFAULT_HTML_PATH = join(PACKAGE_ROOT, "template.html");

vite的插件indexHtml的主要作用是读取template.html,然后把解析的vue入口插入到模版中,这样web服务启动后就可以访问到我们的主题了,插件代码如下:

/** /src/node/plugin-island/indexHtml.ts */

import { readFile } from "fs/promises";
import { Plugin } from "vite";
import { CLIENT_ENTRY_PATH, DEFAULT_HTML_PATH } from "../constants";

export function pluginIndexHtml(): Plugin {
  return {
    name: "island:index-html",
    apply: "serve",
    transformIndexHtml(html) {
      return {
        html,
        tags: [
          {
            tag: "script",
            attrs: {
              type: "module",
              src: `/@fs/${CLIENT_ENTRY_PATH}`
            },
            injectTo: "body"
          }
        ]
      }
    },
    configureServer(server) {
      return () => {
        server.middlewares.use(async (req, res, next) => {
          let html = await readFile(DEFAULT_HTML_PATH, "utf-8");

          try {
            html = await server.transformIndexHtml(
              req.url,
              html,
              req.originalUrl
            );
            res.statusCode = 200;
            res.setHeader("Content-Type", "text/html");
            res.end(html);
          } catch (e) {
            return next(e);
          }
        });
      };
    },
  };
}

然后在dev.ts中把自定义插件加入,如下:

- import { createServer } from "vite";
- import vue from "@vitejs/plugin-vue";
- import { pluginIndexHtml } from "./plugin-island/indexHtml";
-
- const rollupOptions = {
-   external: ["vue"],
- };
-
- export default function createDevServer(root: string) {
-   return createServer({
-     root,
-     plugins: [pluginIndexHtml(), vue()],
-     build: { rollupOptions },
-   });
- }

4、启动服务

完成这个插件后,重新编译一下脚本服务,pnpm run dev,编译后的脚本无需重新安装,直接v-island dev,既可以启动服务,启动后服务如下:

image.png

然后访问 http://127.0.0.1:5173 ,可以看到页面如下:

image.png

到此已经完成主题UI的简单渲染。

码云地址(已迁移到github,但是commit没有迁移了,所以留下码云地址)

本章代码在该分支

本项目github仓库地址(包含最新代码)