前言
本篇是承接上篇《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,既可以启动服务,启动后服务如下:
然后访问 http://127.0.0.1:5173 ,可以看到页面如下:
到此已经完成主题UI的简单渲染。