You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在无框架网站中专业可扩展地管理Routes、Template Engines与Web Components?

无框架Web项目整合实践指南

一、模板引擎与Web Components结合落地

核心思路:用模板引擎做服务端渲染的页面骨架,Web Components封装可复用前端组件,两者通过服务端传递的数据完成衔接,既保留服务端渲染的优势,又实现前端组件的隔离与复用。

实际案例(EJS + 原生Web Components)

1. 服务端模板渲染页面骨架(EJS)

views/layout.ejs - 通用页面布局:

<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
  <title><%= pageTitle %></title>
  <!-- 延迟加载Web Components定义脚本 -->
  <script src="/components/user-card.js" defer></script>
</head>
<body>
  <header><%= include('partials/header', { navItems }) %></header>
  <main>
    <!-- 从服务端数据库获取的用户数据,通过模板引擎注入到Web Components属性 -->
    <% users.forEach(user => { %>
      <user-card 
        name="<%= user.name %>" 
        avatar="<%= user.avatarUrl %>" 
        bio="<%= user.bio %>"
      ></user-card>
    <% }) %>
  </main>
  <footer><%= include('partials/footer') %></footer>
</body>
</html>

2. Web Components封装可复用UI

public/components/user-card.js - 自定义用户卡片组件:

class UserCard extends HTMLElement {
  constructor() {
    super();
    // 用Shadow DOM隔离组件样式,避免全局污染
    const shadow = this.attachShadow({ mode: 'open' });
    
    // 从DOM属性读取服务端传递的数据
    const name = this.getAttribute('name');
    const avatar = this.getAttribute('avatar');
    const bio = this.getAttribute('bio');
    
    shadow.innerHTML = `
      <style>
        .card { border: 1px solid #eee; padding: 1rem; border-radius: 8px; max-width: 300px; margin: 1rem 0; }
        .avatar { width: 60px; height: 60px; border-radius: 50%; object-fit: cover; }
        .name { font-weight: 600; margin: 0.5rem 0; }
        .bio { color: #666; font-size: 0.9rem; line-height: 1.4; }
      </style>
      <div class="card">
        <img class="avatar" src="${avatar}" alt="${name}">
        <div class="name">${name}</div>
        <div class="bio">${bio}</div>
      </div>
    `;
  }

  // 监听属性变化,实现组件动态更新
  static get observedAttributes() {
    return ['name', 'avatar', 'bio'];
  }

  attributeChangedCallback(attrName, oldVal, newVal) {
    if (oldVal === newVal) return;
    const targetEl = this.shadowRoot.querySelector(
      attrName === 'avatar' ? '.avatar' : `.${attrName}`
    );
    if (targetEl) {
      attrName === 'avatar' ? targetEl.src = newVal : targetEl.textContent = newVal;
    }
  }
}

customElements.define('user-card', UserCard);

3. Node.js路由处理(Express)

app.get('/users', async (req, res) => {
  // 从数据库查询用户数据
  const users = await db.query('SELECT name, avatar_url as avatarUrl, bio FROM users');
  // 渲染EJS模板并传递数据
  res.render('users', { 
    pageTitle: '用户列表', 
    users, 
    navItems: await db.query('SELECT title, path FROM nav_items') 
  });
});

二、Node.js数据库管理路由的可扩展实现

核心思路:将路由规则(路径、请求方法、处理逻辑、权限等)存储到数据库,启动时加载路由配置并动态注册到Express,实现路由的灵活管理与业务逻辑解耦。

实际案例

1. 数据库路由表设计(MySQL)

CREATE TABLE routes (
  id INT AUTO_INCREMENT PRIMARY KEY,
  path VARCHAR(255) NOT NULL UNIQUE, -- 路由路径,如 '/users/:id'
  method ENUM('GET', 'POST', 'PUT', 'DELETE') DEFAULT 'GET',
  handler VARCHAR(100) NOT NULL, -- 对应Controller中的方法名
  template VARCHAR(100), -- 对应模板文件(页面路由用)
  requires_auth BOOLEAN DEFAULT FALSE, -- 是否需要认证
  created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

2. 路由加载与注册工具

lib/router-loader.js:

const fs = require('fs');
const path = require('path');

// 加载所有业务Controller
const loadControllers = () => {
  const controllers = {};
  const controllerDir = path.join(__dirname, '../controllers');
  fs.readdirSync(controllerDir).forEach(file => {
    if (file.endsWith('.js')) {
      const ctrl = require(path.join(controllerDir, file));
      Object.assign(controllers, ctrl);
    }
  });
  return controllers;
};

// 从数据库加载路由并注册到Express
const registerRoutes = async (app, db) => {
  const controllers = loadControllers();
  const routes = await db.query('SELECT * FROM routes');

  routes.forEach(route => {
    const { method, path, handler, template, requires_auth } = route;
    const middlewareChain = [];

    // 加入认证中间件(如果需要)
    if (requires_auth) {
      middlewareChain.push((req, res, next) => {
        // 替换为实际的认证逻辑(如Session/JWT验证)
        if (!req.session?.user) return res.redirect('/login');
        next();
      });
    }

    // 加入业务处理逻辑
    middlewareChain.push(async (req, res) => {
      try {
        // 调用对应Controller方法获取数据
        const data = await controllers[handler](req, db);
        // 页面路由渲染模板,API路由返回JSON
        template ? res.render(template, { ...data }) : res.json(data);
      } catch (err) {
        console.error(err);
        res.status(500).send('服务器内部错误');
      }
    });

    // 注册路由到Express
    app[method.toLowerCase()](path, ...middlewareChain);
  });
};

module.exports = { registerRoutes };

3. 业务Controller示例

controllers/userController.js:

// 获取用户列表
exports.getUsers = async (req, db) => {
  const users = await db.query('SELECT name, avatar_url as avatarUrl, bio FROM users');
  return { pageTitle: '用户列表', users };
};

// 获取单个用户详情
exports.getUserDetail = async (req, db) => {
  const user = await db.query('SELECT * FROM users WHERE id = ?', [req.params.id]);
  return { pageTitle: user[0].name, user: user[0] };
};

4. 启动时加载路由

const express = require('express');
const { registerRoutes } = require('./lib/router-loader');
const db = require('./lib/db'); // 数据库连接模块

const app = express();

// 配置模板引擎与静态资源
app.set('view engine', 'ejs');
app.set('views', './views');
app.use(express.static('public'));

// 动态注册路由并启动服务
(async () => {
  await registerRoutes(app, db);
  app.listen(3000, () => console.log('服务运行在 http://localhost:3000'));
})();

三、落地建议与资源方向

  • 模板引擎选择:除EJS外,Pug、Handlebars都是成熟选项,核心逻辑一致——服务端传递数据到页面,为Web Components提供属性输入。
  • Web Components进阶:重点掌握Shadow DOM样式隔离、自定义元素生命周期、属性/属性绑定三个核心点,无需依赖任何框架即可实现组件化。
  • 实战项目推荐:从零搭建个人博客系统——用模板引擎渲染文章列表/详情页,Web Components封装评论区、标签云组件,数据库管理文章分类、页面路由,完全脱离前端框架。

内容的提问来源于stack exchange,提问作者Peter SH

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.28 11:24:53