G.Ark Blog

个人技术笔记与生活记录

节点分享网站代码

发布:2025/8/1字数:7302阅读:25 分钟

1. 项目结构

/project-root
│
├── index.html       # 网站主页面
└── nodes.json       # 节点数据文件,存储节点信息

2. 代码详解

2.1 index.html

这是网站的主页面,使用纯前端技术实现,包含:

  • 页面结构和样式设计
  • 节点列表动态渲染
  • 复制节点链接功能
  • 刷新按钮重新加载节点数据
<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8" />
  <meta name="viewport" content="width=device-width, initial-scale=1" />
  <title>G.Ark 节点列表</title>
  <style>
    body {
      font-family: "Segoe UI", Tahoma, Geneva, Verdana, sans-serif;
      background-color: #f5f7fa;
      margin: 0;
      padding: 20px;
      color: #333;
    }
    header {
      text-align: center;
      margin-bottom: 30px;
    }
    header h1 {
      margin: 0;
      font-size: 2rem;
      color: #2c3e50;
    }
    header p {
      margin: 5px 0 0;
      color: #666;
      font-size: 1rem;
    }
    #refresh-btn {
      display: inline-block;
      margin: 10px auto 30px;
      padding: 10px 20px;
      background-color: #3498db;
      color: white;
      border: none;
      border-radius: 5px;
      cursor: pointer;
      font-size: 1rem;
      transition: background-color 0.3s ease;
    }
    #refresh-btn:hover {
      background-color: #2980b9;
    }
    .node-list {
      max-width: 900px;
      margin: 0 auto;
      background: white;
      border-radius: 8px;
      box-shadow: 0 4px 12px rgba(0,0,0,0.1);
      padding: 20px;
    }
    .node-item {
      border-bottom: 1px solid #eee;
      padding: 15px 0;
      display: flex;
      flex-wrap: wrap;
      align-items: center;
    }
    .node-item:last-child {
      border-bottom: none;
    }
    .node-info {
      flex: 1 1 70%;
      min-width: 250px;
    }
    .node-title {
      font-weight: 600;
      font-size: 1.1rem;
      margin-bottom: 6px;
      color: #34495e;
    }
    .node-protocol {
      font-size: 0.9rem;
      color: #7f8c8d;
      margin-bottom: 6px;
    }
    .node-link {
      font-family: monospace;
      font-size: 0.85rem;
      word-break: break-all;
      background: #ecf0f1;
      padding: 6px 8px;
      border-radius: 4px;
      margin-bottom: 6px;
      user-select: text;
    }
    .node-meta {
      font-size: 0.85rem;
      color: #95a5a6;
    }
    .copy-btn {
      flex: 0 0 100px;
      background-color: #27ae60;
      color: white;
      border: none;
      border-radius: 5px;
      padding: 8px 12px;
      cursor: pointer;
      font-size: 0.9rem;
      transition: background-color 0.3s ease;
      margin-left: 15px;
      white-space: nowrap;
    }
    .copy-btn:hover {
      background-color: #1e8449;
    }
    @media (max-width: 600px) {
      .node-item {
        flex-direction: column;
        align-items: flex-start;
      }
      .copy-btn {
        margin-left: 0;
        margin-top: 8px;
        width: 100%;
      }
    }
  </style>
</head>
<body>
  <header>
    <h1>G.Ark</h1>
    <p>不定时更新 节点投稿: <a href="mailto:1314047@Gmail.com">1314047@Gmail.com</a></p>
    <button id="refresh-btn" title="刷新页面">刷新</button>
  </header>

  <section class="node-list" aria-label="节点列表">
    <!-- 节点条目将由 JS 动态生成 -->
  </section>

  <script>
    const nodeListEl = document.querySelector('.node-list');

    function createNodeItem(node) {
      const item = document.createElement('article');
      item.className = 'node-item';

      const info = document.createElement('div');
      info.className = 'node-info';

      const title = document.createElement('h3');
      title.className = 'node-title';
      title.textContent = node.title;
      info.appendChild(title);

      const protocol = document.createElement('div');
      protocol.className = 'node-protocol';
      protocol.textContent = `协议: ${node.protocol}`;
      info.appendChild(protocol);

      const link = document.createElement('div');
      link.className = 'node-link';
      link.textContent = node.link;
      info.appendChild(link);

      const meta = document.createElement('div');
      meta.className = 'node-meta';
      meta.textContent = `添加时间: ${node.added}`;
      info.appendChild(meta);

      const copyBtn = document.createElement('button');
      copyBtn.className = 'copy-btn';
      copyBtn.textContent = '复制';
      copyBtn.setAttribute('aria-label', `复制节点链接: ${node.title}`);
      copyBtn.addEventListener('click', () => {
        navigator.clipboard.writeText(node.link).then(() => {
          copyBtn.textContent = '已复制';
          setTimeout(() => {
            copyBtn.textContent = '复制';
          }, 1500);
        }).catch(() => {
          alert('复制失败,请手动复制');
        });
      });

      item.appendChild(info);
      item.appendChild(copyBtn);

      return item;
    }

    function renderNodes(nodes) {
      nodeListEl.innerHTML = '';
      nodes.forEach(node => {
        nodeListEl.appendChild(createNodeItem(node));
      });
    }

    async function loadNodes() {
      try {
        const response = await fetch('nodes.json');
        if (!response.ok) throw new Error('网络错误,加载节点失败');
        const nodes = await response.json();
        renderNodes(nodes);
      } catch (error) {
        nodeListEl.innerHTML = `<p style="color:red; text-align:center;">加载节点失败:${error.message}</p>`;
      }
    }

    document.getElementById('refresh-btn').addEventListener('click', () => {
      loadNodes();
    });

    // 页面加载时初始化
    loadNodes();
  </script>
</body>
</html>

2.2 nodes.json

这是节点数据文件,采用 JSON 格式存储节点信息。每个节点包含:

  • title:节点名称
  • protocol:协议类型(如 V2ray、VLESS、Hysteria2 等)
  • link:节点链接
  • added:添加时间

示例内容如下:

[
  {
    "title": "V2ray Nodeloc@October1899<侵删>",
    "protocol": "URL",
    "link": "<https://vlog.573193.xyz/aliyun/alist/apikey?token=009d1d8592249e0165799c2c60d0de38>",
    "added": "2025/10/18 14:46:55"
  },
  {
    "title": "V2ray Linux@LS131<侵删>",
    "protocol": "URL",
    "link": "<https://v1.mk/nUscDPS>",
    "added": "2025/10/16 20:47:16"
  },
  {
    "title": "V2ray Linux@liui<侵删>",
    "protocol": "URL",
    "link": "<https://pastee.dev/d/HhB0GgfI/0>",
    "added": "2025/10/13 01:20:16"
  },
  {
    "title": "美国IP 匿名大佬分享<侵删>",
    "protocol": "Hysteria2",
    "link": "hysteria2://1c90ba9c-05ec-4fc1-9d2c-3d5459bef2af@us.hyleet.fitness:27015?sni=us.hyleet.fitness&obfs=salamander&obfs-password=7ORoCXVodF1kVmro&insecure=0#%F0%9F%87%BA%F0%9F%87%B8%7CUS%7C%40wxgqlfx%7C385",
    "added": "2025/10/11 10:20:16"
  },
  {
    "title": "美国IP 匿名大佬分享<侵删>",
    "protocol": "VLESS",
    "link": "vless://6006efbc-2751-44c5-8789-d38304d4303a@ico.org.uk:2087?encryption=none&security=tls&sni=frag2.mAhSaAMiNi.CC&alpn=h2%2Chttp%2F1.1&fp=chrome&type=ws&path=%2FCPI#%F0%9F%8C%90%7CUN%7C%40wxgqlfx%7C370",
    "added": "2025/10/11 10:15:45"
  },
  {
    "title": "美国IP 匿名大佬分享<侵删>",
    "protocol": "VLESS",
    "link": "vless://33e0c3a8-49e6-46dc-bb51-b39fe67b62c3@172.67.191.189:2096?encryption=none&security=tls&sni=J0in-Te1egram-eiiim.danesh-bonyan.Store&fp=chrome&type=ws&host=eiiim.danesh-bonyan.Store&path=%2Fjoin.----------------Telegram-EiiiM----------------Telegram-EiiiM---------------.Telegram-EiiiM.#%F0%9F%8C%90%7CUN%7C%40wxgqlfx%7C361",
    "added": "2025/10/11 10:14:37"
  },
  {
    "title": "v2ray订阅 Github @v2rayfree<侵删>",
    "protocol": "tuic",
    "link": "<https://raw.githubusercontent.com/free-nodes/v2rayfree/main/v2>",
    "added": "2025/10/7 12:51:37"
  },
  {
    "title": "IPV6小鸡TUIC 理解 @caanyying 佬友分享<侵删>",
    "protocol": "tuic",
    "link": "tuic://c44e5941-82a3-41d8-8b77-65df629aab4a%3Ac44e5941-82a3-41d8-8b77-65df629aab4a@[2001:470:1f18:7c::29]:60604?sni=www.bing.com&alpn=h3&congestion_control=bbr#LinuxDo@caanyying",
    "added": "2025/10/7 09:35:37"
  },
  {
    "title": "IPV6小鸡Hy2 歇斯底里 @caanyying 佬友分享<侵删>",
    "protocol": "hysteria2",
    "link": "hysteria2://c44e5941-82a3-41d8-8b77-65df629aab4a@[2001:470:1f18:7c::29]:30168?sni=www.bing.com&alpn=h3&insecure=1#LinuxDo@caanyying",
    "added": "2025/10/7 09:34:42"
  },
  {
    "title": "土耳其Hy2 歇斯底里 佬友分享<侵删>",
    "protocol": "hysteria2",
    "link": "hysteria2://09fceaf1-5813-4745-b8ad-725fc5a8c426@46.203.248.72:50443?alpn=h3&insecure=1#LinuxDo@ZSF",
    "added": "2025/10/7 09:31:00"
  },
  {
    "title": "波兰家宽Hy2 歇斯底里 大口机 佬友分享<侵删>",
    "protocol": "hysteria2",
    "link": "hysteria2://P51fCW7FQzJBXyuTbn8p7AyE@83.168.90.27:40443?insecure=1#LinuxDo@ZSF",
    "added": "2025/10/7 08:57:56"
  }
]

3. 使用说明

  1. index.htmlnodes.json 放在同一目录下。
  2. 使用支持静态文件服务的环境打开 index.html,例如本地启动一个简单的 HTTP 服务器(推荐使用 VSCode 的 Live Server 插件,或命令行 python -m http.server)。
  3. 页面会自动加载 nodes.json 中的节点数据并展示。
  4. 点击“复制”按钮即可将节点链接复制到剪贴板,方便粘贴使用。
  5. 点击“刷新”按钮可以重新加载最新的节点数据。

4. 代码亮点

  • 纯前端实现:无需后端支持,简单易用。
  • 响应式设计:适配手机和桌面端,用户体验良好。
  • 无刷新复制功能:使用 Clipboard API 实现一键复制。
  • 易于扩展:只需更新 nodes.json 即可动态更新节点列表。

5. 后续优化建议

  • 增加节点搜索和筛选功能,方便用户快速定位所需节点。
  • 支持节点分类展示,提升页面结构清晰度。
  • 增加用户投稿功能,方便社区共同维护节点库。
  • 使用框架(如 React/Vue)重构,提高代码可维护性。