Skip to content

hub-web 搭建教程

本教程将带你从零搭建一个 hub-web 画廊网站,配合 Hub 仓库使用。

前置条件

我们提倡 hub 和 hub-web 分开

IMPORTANT

Hub(图片仓库)和 hub-web(画廊网站)应该是 两个独立的 GitHub 仓库

  • Hub 仓库(如 NoWayToFix/NWTFhub):纯图片存储,Bot 操作的对象
  • hub-web(如 NoWayToFix/NWTFhub-web):画廊网站,展示图片

不要把 web 代码放进 Hub 仓库,也不要把图片放进 web 仓库。

为什么要分开?

  • 职责分离:Hub 只管图片,web 只管展示
  • 部署灵活:web 部署到 Cloudflare Pages / Vercel,Hub 保持在 GitHub
  • 审核不受影响:Bot 创建的 PR 在 Hub 仓库中审核,web 不参与
  • 独立迭代:改网站样式不需要动 Hub,加图片不需要动 web

步骤 1:创建 web 仓库

bash
mkdir hub-web && cd hub-web
git init

步骤 2:初始化项目

bash
npm init -y

安装依赖:

bash
npm i vue vue-waterfall-plugin-next
npm i -D @vitejs/plugin-vue vite typescript vue-tsc shx

步骤 3:创建文件结构

bash
mkdir -p src/views src/assets public/imgs

创建以下文件:

index.html

html
<!doctype html>
<html lang="zh-CN">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>My Hub</title>
  </head>
  <body>
    <div id="app"></div>
    <script type="module" src="/src/main.ts"></script>
  </body>
</html>

vite.config.ts

ts
import { fileURLToPath, URL } from 'node:url'
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'

export default defineConfig({
  plugins: [vue()],
  resolve: {
    alias: {
      '@': fileURLToPath(new URL('./src', import.meta.url))
    }
  }
})

src/main.ts

ts
import { createApp } from 'vue'
import App from './App.vue'

createApp(App).mount('#app')

src/App.vue

vue
<script setup lang="ts">
import HomeView from './views/HomeView.vue'
</script>

<template>
  <HomeView />
</template>

<style>
body {
  margin: 0;
  padding: 0;
  font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
}
</style>

src/views/HomeView.vue

这是画廊的核心组件,使用瀑布流展示图片:

vue
<script setup lang="ts">
import { ref, onMounted } from 'vue'
import imageListData from '@/assets/imageList.json'
import { LazyImg, Waterfall } from 'vue-waterfall-plugin-next'
import 'vue-waterfall-plugin-next/dist/style.css'

const imageList = ref<{ id: string; src: string; name: string }[]>([])

function randomID(length = 6) {
  return Math.random().toString(36).slice(2, 2 + length) + Date.now().toString(36)
}

onMounted(() => {
  imageList.value = imageListData.map((imagePath: string) => ({
    id: randomID(),
    src: imagePath,
    name: imagePath.split('/').pop() || ''
  }))
})
</script>

<template>
  <h1 class="hubname">My Hub</h1>
  <Waterfall :list="imageList" :width="320" :gutter="16">
    <template #default="{ item }">
      <div class="image-wrapper">
        <LazyImg :url="item.src" alt="Image" />
        <p class="image-filename">{{ item.name }}</p>
      </div>
    </template>
  </Waterfall>
</template>

<style scoped>
h1 {
  text-align: center;
  font-size: 24px;
  margin-bottom: 20px;
}

.waterfall-list {
  background-color: transparent !important;
}

.image-wrapper {
  background: #fff;
  border-radius: 8px;
  box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1);
  overflow: hidden;
}

.image-wrapper img {
  width: 100%;
  height: auto;
  display: block;
}

.image-filename {
  padding: 8px;
  text-align: center;
  font-size: 16px;
  color: #333;
}
</style>

generateImageList.cjs

构建前自动生成图片列表:

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

const imagesDir = path.join(__dirname, 'public', 'imgs')
const outputFilePath = path.join(__dirname, 'src', 'assets', 'imageList.json')

fs.readdir(imagesDir, (err, files) => {
  if (err) {
    console.error('Error reading images directory:', err)
    return
  }

  const imageFiles = files.filter((file) => /\.(jpg|jpeg|png|gif|webp|bmp|svg)$/.test(file))
  const imagePaths = imageFiles.map((file) => `/imgs/${file}`)

  fs.writeFile(outputFilePath, JSON.stringify(imagePaths, null, 2), (err) => {
    if (err) {
      console.error('Error writing image list file:', err)
    } else {
      console.log('Image list generated successfully.')
    }
  })
})

步骤 4:配置构建脚本

package.json 中设置:

json
{
  "type": "module",
  "scripts": {
    "getphotos": "shx rm -rf temp_repo public/imgs && (git clone --depth 1 https://github.com/YOUR-USERNAME/YOUR-HUB-REPO temp_repo && shx mkdir -p public/imgs && shx cp -r temp_repo/images/* public/imgs/ && shx rm -rf temp_repo) || shx mkdir -p public/imgs",
    "dev": "npm run getphotos && node generateImageList.cjs && vite",
    "prebuild": "npm run getphotos && node generateImageList.cjs",
    "build": "vite build",
    "preview": "vite preview"
  }
}

YOUR-USERNAME/YOUR-HUB-REPO 替换成你的 Hub 仓库地址。

INFO

getphotos 脚本会:

  1. 从 Hub 仓库 clone 图片到 public/imgs/
  2. 如果 clone 失败(比如仓库为空),则创建空的 public/imgs/ 目录

步骤 5:本地预览

bash
npm run dev

打开浏览器访问 http://localhost:5173,你应该能看到瀑布流画廊。

步骤 6:部署到 Cloudflare Pages

  1. 将 hub-web 仓库推送到 GitHub
  2. 登录 Cloudflare Dashboard → Pages
  3. 创建项目,连接 GitHub 仓库

配置:

配置
构建命令npm run build
输出目录dist
Node.js 版本18
  1. 部署完成后,Cloudflare 会给你一个 xxx.pages.dev 域名
  2. 你也可以绑定自定义域名

自动更新

当 Hub 仓库合入新的 PR 后,需要重新构建 hub-web 才能看到新图片。你可以:

  • 手动触发:在 Cloudflare Pages 控制台点击 Retry deployment
  • 自动触发:在 Hub 仓库设置 GitHub Actions,合入 PR 后用 Cloudflare API 触发重新部署

进阶定制

添加背景

App.vue 中添加背景元素:

vue
<template>
  <div class="background"></div>
  <div class="overlay"></div>
  <HomeView />
</template>

<style>
.background {
  position: fixed;
  top: 0; left: 0;
  width: 100%; height: 100%;
  background-image: url('你的背景图URL');
  background-size: cover;
  background-attachment: fixed;
  z-index: -2;
}
.overlay {
  position: fixed;
  top: 0; left: 0;
  width: 100%; height: 100%;
  background-color: rgba(255, 255, 255, 0.6);
  z-index: -1;
}
</style>

使用镜像拉取图片

如果 CI 环境无法直连 GitHub,可以修改 getphotos 脚本使用镜像:

bash
"getphotos": "shx rm -rf temp_repo public/imgs && (git clone --depth 1 https://gh-proxy.org/https://github.com/YOUR-USERNAME/YOUR-HUB-REPO temp_repo && shx mkdir -p public/imgs && shx cp -r temp_repo/images/* public/imgs/ && shx rm -rf temp_repo) || shx mkdir -p public/imgs"

自定义画廊标题

修改 HomeView.vue 中的 <h1> 标签:

vue
<h1 class="hubname">你的Hub名称</h1>