实现一个前端动态模块组件(Vite+原生JS)
笔记哥 /
05-22 /
31点赞 /
0评论 /
433阅读
# 1. 引言
在前面的文章《使用Vite创建一个动态网页的前端项目》我们实现了一个动态网页。不过这个动态网页的实用价值并不高,在真正实际的项目中我们希望的是能实现一个动态的模块组件。具体来说,就是有一个页面控件同时在多个页面中使用,那么我们肯定想将这个页面控件封装起来,以便每个页面需要的时候调用一下就可以生成。注意,这个封装起来模块组件应该要包含完整的HTML+JavaScript+CSS,并且要根据从后端访问的数据来动态填充页面内容。其实像VUE这样的前端框架就是这种设计思路,同时这也是GUI程序开发的常见思维模式。
# 2. 实现
## 2.1 项目组织
在这里笔者实现的例子是一个博客网站上的分类专栏控件。分类专栏是一般通过后端获取的,但是这里笔者就将其模拟成直接域内获取一个数据categories.json,里面的内容如下:
```json
[
{
"firstCategory": {
"articleCount": 4,
"iconAddress": "三维渲染.svg",
"name": "计算机图形学"
},
"secondCategories": [
{
"articleCount": 2,
"iconAddress": "opengl.svg",
"name": "OpenGL/WebGL"
},
{
"articleCount": 2,
"iconAddress": "专栏分类.svg",
"name": "OpenSceneGraph"
},
{ "articleCount": 0, "iconAddress": "threejs.svg", "name": "three.js" },
{ "articleCount": 0, "iconAddress": "cesium.svg", "name": "Cesium" },
{ "articleCount": 0, "iconAddress": "unity.svg", "name": "Unity3D" },
{
"articleCount": 0,
"iconAddress": "unrealengine.svg",
"name": "Unreal Engine"
}
]
},
{
"firstCategory": {
"articleCount": 4,
"iconAddress": "计算机视觉.svg",
"name": "计算机视觉"
},
"secondCategories": [
{
"articleCount": 0,
"iconAddress": "图像处理.svg",
"name": "数字图像处理"
},
{
"articleCount": 0,
"iconAddress": "特征提取.svg",
"name": "特征提取与匹配"
},
{
"articleCount": 0,
"iconAddress": "目标检测.svg",
"name": "目标检测与分割"
},
{ "articleCount": 4, "iconAddress": "SLAM.svg", "name": "三维重建与SLAM" }
]
},
{
"firstCategory": {
"articleCount": 11,
"iconAddress": "地理信息系统.svg",
"name": "地理信息科学"
},
"secondCategories": []
},
{
"firstCategory": {
"articleCount": 31,
"iconAddress": "代码.svg",
"name": "软件开发技术与工具"
},
"secondCategories": [
{ "articleCount": 2, "iconAddress": "cplusplus.svg", "name": "C/C++" },
{ "articleCount": 19, "iconAddress": "cmake.svg", "name": "CMake构建" },
{ "articleCount": 2, "iconAddress": "Web开发.svg", "name": "Web开发" },
{ "articleCount": 7, "iconAddress": "git.svg", "name": "Git" },
{ "articleCount": 1, "iconAddress": "linux.svg", "name": "Linux开发" }
]
}
]
```
这个数据的意思是将分类专类分成一级分类专栏和二级分类专栏,每个专栏都有名称、文章数、图标地址属性,这样便于我们填充到页面中。
新建一个components目录,在这个目录中新建category.html、category.js、category.css这三个文件,正如前文所说的,我们希望这个模块组件能同时具有结构、行为和样式的能力。这样,这个项目的文件组织结构如下所示:
my-native-js-app
├── public
│ └── categories.json
├── src
│ ├── components
│ │ ├── category.css
│ │ ├── category.html
│ │ └── category.js
│ └── main.js
├── index.html
└── package.json
## 2.2 具体解析
先看index.html页面,代码如下所示:
```html
Vite App
```
基本都没有什么变化,只是增加了一个名为`category-section-placeholder`的元素,这个元素会用来挂接在js中动态创建的分类专栏目录元素。
接下来看main.js文件:
```js
import './components/category.js'
```
里面其实啥都没干,只是引入了一个category模块。那么就看一下这个category.js文件:
```js
import "./category.css";
// 定义一个变量来存储获取到的分类数据
let categoriesJson = null;
// 使用MutationObserver监听DOM变化
const observer = new MutationObserver((mutations) => {
mutations.forEach((mutation) => {
if (
mutation.type === "childList" &&
mutation.target.id === "category-section-placeholder"
) {
// 在这里调用函数来填充数据
populateCategories(categoriesJson);
}
});
});
// 配置观察选项
const config = { childList: true, subtree: true };
// 开始观察目标节点
const targetNode = document.getElementById("category-section-placeholder");
observer.observe(targetNode, config);
// 获取分类数据
async function fetchCategories() {
try {
const backendUrl = import.meta.env.VITE_BACKEND_URL;
const response = await fetch("/categories.json");
if (!response.ok) {
throw new Error("网络无响应");
}
categoriesJson = await response.json();
// 加载Category.html内容
fetch("/src/components/category.html")
.then((response) => response.text())
.then((data) => {
document.getElementById("category-section-placeholder").innerHTML =
data;
})
.catch((error) => {
console.error("Failed to load Category.html:", error);
});
} catch (error) {
console.error("获取分类专栏失败:", error);
}
}
// 填充分类数据
function populateCategories(categories) {
if (!categories || !Array.isArray(categories)) {
console.error("Invalid categories data:", categories);
return;
}
const categoryList = document.querySelector(".category-list");
categories.forEach((category) => {
const categoryItem = document.createElement("li");
categoryItem.innerHTML = `
${category.firstCategory.name} ${category.firstCategory.articleCount}篇`;
if (category.secondCategories.length != 0) {
categoryItem.innerHTML += `
```
fetch接口是按照文本的方式来获取category.html的,在这里的`document.getElementById("category-section-placeholder").innerHTML = data;`表示将这段文本序列化到`category-section-placeholder`元素的子节点中。程序执行到这里并没有结束,通过对DOM的变化监听,继续执行`populateCategories`函数,如下所示:
```cpp
// 使用MutationObserver监听DOM变化
const observer = new MutationObserver((mutations) => {
mutations.forEach((mutation) => {
if (
mutation.type === "childList" &&
mutation.target.id === "category-section-placeholder"
) {
// 在这里调用函数来填充数据
populateCategories(categoriesJson);
}
});
});
// 配置观察选项
const config = { childList: true, subtree: true };
// 开始观察目标节点
const targetNode = document.getElementById("category-section-placeholder");
observer.observe(targetNode, config);
```
`populateCategories`的具体实现思路是:现在分类专栏的数据已经有了,根节点元素`category-list`也已经知道,剩下的就是通过数据来拼接HTML字符串,然后序列化到`category-list`元素的子节点下。代码如下所示:
```csharp
```
```js
const categoryList = document.querySelector(".category-list");
categories.forEach((category) => {
const categoryItem = document.createElement("li");
categoryItem.innerHTML = `
${category.firstCategory.name} ${category.firstCategory.articleCount}篇`;
if (category.secondCategories.length != 0) {
categoryItem.innerHTML += `
-
${category.secondCategories
.map(
(subcategory) => `
-
${subcategory.name} ${subcategory.articleCount}篇
`
)
.join("")}
分类专栏
-
${category.secondCategories
.map(
(subcategory) => `
-
${subcategory.name} ${subcategory.articleCount}篇
`
)
.join("")}
本文来自投稿,不代表本站立场,如若转载,请注明出处:http//www.knowhub.vip/share/2/3606
- 热门的技术博文分享
- 1 . ESP实现Web服务器
- 2 . 从零到一:打造高效的金仓社区 API 集成到 MCP 服务方案
- 3 . 使用C#构建一个同时问多个LLM并总结的小工具
- 4 . .NET 原生驾驭 AI 新基建实战系列Milvus ── 大规模 AI 应用的向量数据库首选
- 5 . 在Avalonia/C#中使用依赖注入过程记录
- 6 . [设计模式/Java] 设计模式之工厂方法模式
- 7 . 5. RabbitMQ 消息队列中 Exchanges(交换机) 的详细说明
- 8 . SQL 中的各种连接 JOIN 的区别总结!
- 9 . JavaScript 中防抖和节流的多种实现方式及应用场景
- 10 . SaltStack 远程命令执行中文乱码问题
- 11 . 推荐10个 DeepSeek 神级提示词,建议搜藏起来使用
- 12 . C#基础:枚举、数组、类型、函数等解析
- 13 . VMware平台的Ubuntu部署完全分布式Hadoop环境
- 14 . C# 多项目打包时如何将项目引用转为包依赖
- 15 . Chrome 135 版本开发者工具(DevTools)更新内容
- 16 . 从零创建npm依赖,只需执行一条命令
- 17 . 关于 Newtonsoft.Json 和 System.Text.Json 混用导致的的序列化不识别的问题
- 18 . 大模型微调实战之训练数据集准备的艺术与科学
- 19 . Windows快速安装MongoDB之Mongo实战
- 20 . 探索 C# 14 新功能:实用特性为编程带来便利