
简介基于 Vue2 的 H5 结婚请帖前端设计源码面向婚礼策划人员、前端学习者和需要制作电子邀请函的团队相比传统纸质请帖提供可交互、可传播的移动端页面适合婚礼、周年庆、活动邀请等场景。压缩包共 87 个文件大小约 34.07MB以 Vue 组件、JavaScript 脚本、图片素材和样式文件为主js/vue 文件承载页面交互与组件逻辑jpg/png/webp 图片提供婚庆视觉素材css/html 完成布局与页面结构另含 json/env 配置与说明文本等辅助文件整体工程结构清晰便于按模块修改。源码内置首页、留言板、音乐、时间轴、地址展示等功能模块并提供 api 请求封装、路由配置、环境变量配置等工程化实践。已有 500 人学习下载适合用作 Vue2 移动端项目实战参考也可作为婚礼邀请 H5 快速改版的基础模板节省从零搭建成本。1. 从模板到工程一份可以拆着玩的 Vue2 请帖项目婚礼邀请页这类 H5 需求外面能买到的模板大多数是把图片和文字写死在页面里改个时间都要翻半天代码。这套基于 Vue2 的 H5 结婚请帖前端设计方案不一样它把时间线、留言板、邀请函这些业务块拆成了独立模块数据结构、API 请求、OSS 资源路径全部走配置文件。拿到手之后替换照片、换音乐、改场地信息半天时间就能交付一版能扫码打开的邀请页。技术栈是 Vue 2.6 Vue Router 3 Axios前端构建用 Vue CLI 4适合婚礼策划行业做定制化交付也适合想把前端工程化流程从头到尾捋一遍的同学。源码里 87 个文件涵盖了环境变量、路由、公共样式、工具函数、资源托管等完整链路可读性比一般商业模板高不少。2. Vue2 工程骨架与多环境配置解析2.1 目录结构里的分工逻辑拿到源码先别急着npm install把目录结构看懂后面改东西才知道去哪找。项目根目录放的是构建配置babel.config.js管语法转译vue.config.js是 Vue CLI 的入口配置.env.development和.env.production两个环境变量文件分别对应本地联调和线上构建。src下面按业务划分views/页面级组件包含home邀请函主页、timeLine恋爱时间线、leaveBoard留言板、addLeave添加留言。components/公共组件musicView是背景音乐播放器。router/index.js前端路由表。server/API 层request.js封装 Axios 实例apis.js统一管理接口地址。utils/工具函数regulars.js是表单校验正则集aliyunoss.js是阿里云 OSS 直传封装。assets/静态资源所有婚礼主题的 PNG 图片和背景图都集中放在这。这个结构对几百人规模的团队来说可能显得简单但对一个单页 H5 项目来说边界划得足够清楚。图片、逻辑、请求、页面各自归位后续替换内容不需要动页面结构。2.2 环境变量如何影响接口地址和资源路径.env.development和.env.production两个文件是这套项目的一个关键设计点。先看一个常见的配置形态# .env.production NODE_ENVproduction VUE_APP_API_URLhttps://api.example.com VUE_APP_OSS_URLhttps://cdn.example.com# .env.development NODE_ENVdevelopment VUE_APP_API_URLhttp://localhost:3000 VUE_APP_OSS_URLhttp://localhost:8080在src/config/env.js里通过process.env.VUE_APP_API_URL读取对应值再用它去初始化 Axios 的baseURL。这样做的好处很直接发版不用改代码只需要在构建机上设置对应的环境参数。如果请帖需要对接不同的接口服务商有的负责留言存储、有的负责表单提交可以在env.js里再拆一层export default { apiUrl: process.env.VUE_APP_API_URL, ossUrl: process.env.VUE_APP_OSS_URL, uploadUrl: process.env.VUE_APP_UPLOAD_URL }这样扩展出来的字段在server/request.js里按模块引就行。这里的坑在于Vue CLI 的环境变量必须以VUE_APP_开头才会被自动注入到客户端代码里不带前缀的变量只能在vue.config.js里通过process.env读取页面里访问不到。2.3 路由注册与页面映射router/index.js里用 Vue Router 的 history 或 hash 模式注册页面。请帖这种场景部署环境往往是对象存储加 CDN没有服务端做 history 回退支持所以把mode设成hash会更省心const router new VueRouter({ mode: hash, routes: [ { path: /, name: home, component: () import(/views/home/index.vue) }, { path: /timeline, name: timeLine, component: () import(/views/timeLine/index.vue) }, { path: /leave-board, name: leaveBoard, component: () import(/views/leaveBoard/index.vue) }, { path: /add-leave, name: addLeave, component: () import(/views/addLeave/index.vue) } ] })页面全部用懒加载首屏只加载home组件用户滑动到访客留言区块时才请求leaveBoard的代码块。对微信内置浏览器这类移动端环境这一步能明显缩短白屏时间。路由参数在邀请函场景里常用于区分来宾身份比如在链接上加?guestxxtable3在home组件里用this.$route.query接住后拼到欢迎文案中实现“不同人打开看到不同称呼”的个性化效果。2.4 API 请求模块的封装方式server/request.js基于 Axios 做了一层薄封装核心作用有三个统一设置baseURL、统一注入 token 或签名参数、统一拦截错误状态。可以看一下基本骨架import axios from axios import env from /config/env.js const service axios.create({ baseURL: env.apiUrl, timeout: 10000, headers: { Content-Type: application/json } }) service.interceptors.request.use(config { // 从 localStorage 读取访客身份标识 const guestId localStorage.getItem(guestId) if (guestId) { config.headers[X-Guest-Id] guestId } return config }, error Promise.reject(error)) service.interceptors.response.use( response response.data, error { // 统一处理 401、网络超时等异常 return Promise.reject(error) } ) export default serviceapis.js则把留言列表、提交留言等接口收敛为函数页面组件里只import { getLeaveList, addLeave } from /server/apis.js然后调用不直接拼 URL。这样接口地址变更时只改一个文件避免页面里散落几十处请求代码。需要注意如果接口返回结构是{ code: 0, data: [...] }需要在响应拦截器里直接返回response.data否则每个调用方都要做一层解包。3. 时间线、留言板与邀请函核心业务实现3.1 时间线组件的数据映射与动效设计views/timeLine是这套请帖里交互最重的一个模块整体采用纵向时间轴布局交替排列的节点展示情侣从认识到求婚的关键节点。数据结构很简单timelineList: [ { id: 1, date: 2019-03-10, title: 第一次见面, desc: 咖啡店偶遇, icon: xin1.png }, { id: 2, date: 2020-10-03, title: 第一次旅行, desc: 海边看日出, icon: xin2.png }, { id: 3, date: 2022-06-05, title: 求婚成功, desc: 准备婚礼, icon: xin3.png } ]渲染时用v-for遍历根据索引奇偶性决定节点在左侧还是右侧。这个模块的代码逻辑并不复杂但有一个值得说的细节图片资源命名和date字段保持语义一致。翻看assets目录你会发现图片文件是2019-3-10.png、2022-6-5.png这类命名而不是img1.png。这种命名方式在开发期看不出优势一旦新人要换照片按日期找文件比反复预览比对快得多。滚带动效方面时间线组件通常在mounted里监听页面滚动通过getBoundingClientRect()判断节点是否进入视口再给节点追加active类名触发透明度变化checkVisible() { const nodes document.querySelectorAll(.timeline-item) nodes.forEach(item { const top item.getBoundingClientRect().top if (top window.innerHeight * 0.85) { item.classList.add(active) } }) }这里的0.85是触发阈值意思是元素进入视口底部 85% 位置时就开始播放动画。阈值调小到0.5动画触发会变晚适合页面元素较多的场景。代码里用原生classList而不是 Vue 的:class绑定是因为该操作发生在组件外部避免依赖当前组件的响应式更新机制。3.2 留言板表单校验与提交逻辑addLeave和leaveBoard构成一个完整的留言闭环。utils/regulars.js里提供了常用的校验正则重点看手机号和微信号的校验规则export const isMobile value /^1[3-9]\d{9}$/.test(value) export const isWechat value /^[a-zA-Z][a-zA-Z0-9_-]{5,19}$/.test(value) export const isName value /^[\u4e00-\u9fa5·]{2,10}$/.test(value)为什么留言要校验微信号因为请帖的留言板本质上是熟人社交场景新人拿到留言后需要回访或确认到场人数。校验规则统一放在utils/regulars.js而不是散落在组件里是为了在addLeave和leaveBoard两个地方共用手写逻辑时保持口径一致。提交留言的接口调用在server/apis.js里维护比如export const addLeave data { return service.post(/api/leave, data) }表单本身在addLeave组件里用v-model双向绑定提交前做一次全校验。另外一个常见做法是给提交按钮加loading状态防止用户频繁点击造成重复提交async handleSubmit() { if (!isName(this.form.name)) { this.$toast(请输入正确的姓名) return } this.submitting true try { await addLeave(this.form) this.$router.push(/leave-board) } finally { this.submitting false } }这里把this.$router.push(/leave-board)放在提交成功之后跳转时列表页通过路由参数或状态管理刷新数据以保证新留言立即可见。整个流程里最容易被忽略的一点是表单字符白名单姓名用中文和间隔号正则微信号用字母开头加数字下划线的规则从源头过滤掉注入脚本。3.3 邀请函主页的富文本内容渲染views/home是打开页面的第一屏承担欢迎语、婚礼时间和地点核心信息。新人的婚纱照、婚礼主题图通常由index-bg.png、hua.webp这类视觉文件渲染而文案内容写在data里data() { return { bride: 小雅, groom: 阿哲, weddingDate: 2023-02-13, address: 杭州西溪宾馆, mapUrl: https://apis.map.qq.com/... } }这种写法和直接在模板里写死文字相比好处是数据字段和组件结构分离同样的home组件替换data里的值即可输出另一对新人的完整请帖。如果需要对接后端管理系统这些字段可以替换为async created() { this.info await getWeddingInfo() }的动态拉取模式组件模板完全不用改动。地址部分通常配一个“查看地图”按钮点击后跳转到地图应用的 URI。这里有个细节address1.png、address2.png、address3.png三张图片对应的可能是一张地图截图拆出的三个定位点。如果不想用地图 SDK直接在新人场地实拍图上叠加坐标点标记交互效果更轻。4. 图片、音乐与 OSS 资源托管实践4.1 婚礼主题图片的目录组织与引用方式Assets 目录下的图片资源按用途可以分为三类背景装饰图hua.webp、index-bg.png、新人照片xin1.png、xin2.png、xin3.png、功能图标music-icon.png、caidan.png、liuyan.png、delect.png。引用方式分为两种在组件模板中使用相对路径在 CSS 中使用url()。在vue.config.js中可以通过chainWebpack调整图片压缩规则把单张超过 10KB 的图片交给file-loader处理小于 10KB 的转成 base64 内联到 JS 或 CSS 中。这样请求数能减少十几条// vue.config.js module.exports { chainWebpack: config { config.module .rule(images) .use(url-loader) .loader(url-loader) .tap(options { options.limit 10 * 1024 return options }) } }不过要提醒一句hua.webp这类背景图往往体积在几百 KB 甚至上 MB即使压缩后扔在 CSS 里也会拖慢首屏。对这个项目我的建议是 hero 区背景图不要走 CSSbackground-image改成img标签配合loadinglazy让浏览器决定在接近视口时再请求。4.2 背景音乐播放器微信兼容的处理方案components/musicView实现的是带旋转动画的音乐开关。这里有一个绕不开的问题iOS 微信内置浏览器不支持页面加载完成后自动播放音频必须由用户触摸交互触发Audio.play()。常见的做法是把播放逻辑绑定在首次点击任意位置的事件上// musicView/index.vue handleFirstTouch() { if (this.audio.paused) { this.audio.play() this.audio.volume 0.6 this.rotating true } }然后在home组件的mounted里注册全局触摸监听document.addEventListener(touchstart, this.handleFirstTouch, { once: true }){ once: true }表示第一次触摸后自动解绑避免后续每次点击都触发。如果你在 Android 端调试时发现自动播放无效不妨检查一下music-icon.png的旋转动画是否由animation驱动而音频本身是否处于preloadauto状态。如果项目使用vue-audio等封装库则需要确认库是否在visibilitychange事件里做了暂停处理。4.3 借助 aliyunoss.js 做图片直传当留言板支持用户上传婚礼祝福图片时直接以 base64 形式存数据库会把接口请求体撑爆。源码里utils/aliyunoss.js封装了阿里云 OSS 直传能力基本使用方式如下import OSS from ali-oss import env from /config/env.js const client new OSS({ region: env.ossRegion, accessKeyId: env.ossAccessKeyId, accessKeySecret: env.ossAccessKeySecret, bucket: env.ossBucket }) export function uploadImage(file) { const fileName wedding/${Date.now()}-${file.name} return client.put(fileName, file).then(res res.url) }注意accessKeyId放在客户端里有被窃取的风险。通常的生产做法是先从业务后端换取 STS 临时凭证再把credentials传给 OSS 客户端。源码里直接暴露密钥的方式更多是给中小型活动场景做一个快速工程化演示。如果你要对外发布建议至少把写权限限定在uploads/路径下并设置生命周期规则定期清理过期图片。5. 构建验证与 H5 适配排错清单5.1 构建产物的体积分析这套源码的构建命令在package.json里定义scripts: { dev: vue-cli-service serve, build: vue-cli-service build, build:analyze: vue-cli-service build --report }本地执行npm run build后dist/目录下的 JS 和 CSS 文件会带 hash 后缀。初次打包完建议执行npm run build:analyze生成体积报告重点看两个指标home路由对应的 chunk 是否超过 200KBmusicView组件是否被单独拆包。如果发现整个 vendor 包特别大可以在vue.config.js里加splitChunks配置把第三方库拆出来configureWebpack: config { config.optimization.splitChunks { chunks: all, cacheGroups: { vendors: { name: chunk-vendors, test: /[\\/]node_modules[\\/]/, priority: 10 } } } }这里的priority决定分组命中优先级数值越大越优先匹配。5.2 微信浏览器与 WebView 的显示差异这套项目里最容易出现的线上问题是打包后某些图片无法显示。首查public/index.html的meta nameviewport配置确保内容包含widthdevice-width, initial-scale1, maximum-scale1, user-scalableno否则在 Android WebView 里可能出现页面整体缩小的情况。第二个高发问题涉及路由模式。如果路由使用了history模式部署在 Nginx 上需要配置try_files回退到index.html否则用户刷新/timeline页面时会收到 404。请帖场景建议直接改成 hash 模式把路由迁移成本降到最低。第三个坑来自 CSS 的100vh高度问题。iOS Safari 和微信内置浏览器的100vh会超出可视区出现底部按钮被遮挡的现象。项目中如果有吸底按钮最好把高度改成100dvh或者用window.innerHeight动态设置最小高度。以下是一个简单的兼容写法mounted() { this.$nextTick(() { const vh window.innerHeight this.$refs.page.style.minHeight ${vh}px }) }这种做法的思路是不用 CSS 单位而是直接用 JS 读取当前 WebView 的实际可视高度赋给页面容器。5.3 上线前的手动验证清单我一般会在微信开发者工具和真机各跑一遍完整流程重点检查以下节点留言提交成功后列表页是否自动刷新并展示新内容。背景音乐首次触摸是否能正常播放切后台再切回来状态是否正确。从朋友圈点开链接后图片懒加载是否被微信拦截导致底图空白。安卓低版本 WebView 对webp格式的支持情况如不支持需要退回png后缀文件。验证用的本地服务如果不想搭 Nginx可以用npx serve dist跑一个静态服务器局域网内手机访问调试。所有问题确认完毕后再把dist/目录整体上传到 OSS 或 CDN并手动清一次 CDN 缓存。最后随手把package.json里的build命令加一条cross-env NODE_ENVproduction vue-cli-service build node scripts/postbuild.js让构建完成后自动把产物同步到你常用的部署目录。本文还有配套的精品资源点击获取