微信小程序招聘项目源码实战:从封装到登录全流程解析
2026/9/14 14:24:58 网站建设 项目流程

简介:微信小程序“无需安装、即用即走”的特性,让其在移动招聘场景中的应用越来越普遍。这份人才岗位招聘的微信小程序页面源码,面向小程序开发者和初级前端学习者,完整展示职位浏览、简历提交、个人中心等招聘核心模块的前端实现与交互逻辑,可作为课程设计或商业项目的基础框架。资源为zip压缩包,共235个文件,包含44个wxml页面结构、46个wxss样式、41个js逻辑脚本、41个json配置和58个png图片素材,整体仅683KB,页面代码与静态资源按模块存放,检索学习非常方便。从预览信息来看,源码覆盖首页、登录、个人中心等页面,以及请求接口封装、本地存储、用户鉴权等关键脚本,便于理解小程序与后端的数据交互链路。已有178人学习下载,适合希望对照完整页面源码、快速上手微信小程序组件与API开发的用户。

1. 招聘小程序源码拆解:从zip包到可运行页面

把“人才岗位招聘的微信小程序页面源码.zip”解压开,里面没有复杂的服务端工程,而是 banner 轮播图、book.jpg 占位图和 api.js、login.js、user.js、course.js、storage.js 这几个核心前端模块。这是一套以人才岗位招聘为业务场景的原生微信小程序前端工程,覆盖职位浏览、分类筛选、个人登录、用户中心等高频操作链路。它适合两类人:一是准备小程序上岗或做毕业设计的前端开发者,拷贝后改域名、换图片、接后端接口就能跑;二是想拆请求封装和缓存层的中级工程师,读它比啃动辄上千行的开源框架更直观。它不是 uniapp 工程,也没有依赖第三方 UI 库,微信开发者工具里打开即用,不需要构建链,这正是拿来做二次开发最舒服的地方。

2. 页面骨架与路由注册:app.json、Banner 资源和 WXML 布局解析

2.1 zip 包内文件结构分析

先看文件职责。banner1.jpg、banner2.jpg、banner3.jpg 对应首页顶部轮播位,book.jpg 是简历模板或内容占位图;api.js 是请求统一出口,storage.js 管缓存读写,login.js 处理登录链路,user.js 维护用户状态,course.js 在招聘场景里承担职位类目配置,存放行业分类、技能标签这类枚举数据。按微信小程序规范,完整工程还需要补充 app.json、app.js、project.config.json 和各页面的 .wxml/.wxss/.json 文件,下面是重构后的参照目录:

miniprogram/ ├── images/ │ ├── banner1.jpg │ ├── banner2.jpg │ ├── banner3.jpg │ └── book.jpg ├── pages/ │ ├── index/ # 首页:职位信息流+轮播 │ ├── login/ # 登录页 │ ├── user/ # 个人中心 │ └── job/ # 职位列表/详情/简历提交 ├── utils/ │ ├── api.js # 请求封装 │ ├── storage.js # 缓存封装 │ ├── login.js # 微信登录逻辑 │ ├── user.js # 用户信息管理 │ └── course.js # 职位类目与技能标签配置 └── app.json

提示:zip 里只给了核心 js 和图片,页面容器需按上述结构补齐。文件命名保持一致,可以减少后续改引用路径的工作量。

2.2 app.json 路由注册与首屏加载顺序

微信小程序的页面注册完全依赖 app.json 的 pages 字段,第一个元素的路径决定启动页。这里把 pages/index/index 放在首位,打开小程序直接进入职位信息流;登录页和用户中心按业务频次排在后面。window 节点统一控制导航栏背景色和标题,招聘场景建议用白色导航栏加品牌主色,避免深色背景突出文字可读性问题。

{ "pages": [ "pages/index/index", "pages/login/login", "pages/user/user", "pages/job/list", "pages/job/detail", "pages/resume/edit" ], "window": { "navigationBarTitleText": "人才岗位招聘", "navigationBarBackgroundColor": "#ffffff", "navigationBarTextStyle": "black", "backgroundColor": "#f5f6f8" }, "style": "v2" }

pages 数组的顺序直接决定首屏渲染哪个目录下的 index.js,后续页面用 wx.navigateTo 跳转时也只认这里的注册路径。漏注册会导致运行时报page route not found,这是新手最容易卡住的地方。navigationBarTitleText 支持在单个页面的 json 文件里覆盖,比如职位详情页可以把标题改成岗位名称。

2.3 首页 Banner 轮播与职位卡片的 WXML 布局

首页结构很简单:顶部 swiper 轮播三张 banner 图片,下面紧跟职位列表卡片。swiper 是微信内置组件,不需要额外插件,常见的坑是图片尺寸不一致导致轮播跳动,所以 image 必须加mode="aspectFill",同时把 swiper 高度固定在一个安全值,比如height: 360rpx左右。

<view class="page"> <swiper class="banner-swiper" indicator-dots="{{true}}" autoplay="{{true}}" interval="4500" circular="{{true}}" bindchange="onBannerChange" > <swiper-item wx:for="{{banners}}" wx:key="index"> <image src="{{item}}" mode="aspectFill" class="banner-img" /> </swiper-item> </swiper> <view class="job-card" wx:for="{{jobList}}" wx:key="id" bindtap="goDetail">const BASE_URL = 'https://api.example.com/v1' const request = (options) => { return new Promise((resolve, reject) => { const token = wx.getStorageSync('token') wx.request({ url: BASE_URL + options.url, method: options.method || 'GET', data: options.data || {}, header: { 'Content-Type': 'application/json', 'Authorization': token ? 'Bearer ' + token : '' }, timeout: 10000, success: (res) => { if (res.statusCode === 200 && res.data.code === 0) { resolve(res.data.data) } else if (res.statusCode === 401) { // 登录态失效,清除本地状态并回登录页 wx.removeStorageSync('token') wx.navigateTo({ url: '/pages/login/login' }) reject(new Error('login expired')) } else { wx.showToast({ title: res.data.message || '请求失败', icon: 'none' }) reject(new Error(res.data.message)) } }, fail: (err) => { wx.showToast({ title: '网络异常,请检查连接', icon: 'none' }) reject(err) } }) }) } module.exports = { request }

BASE_URL 指向后端接口根路径,改成实际环境地址时只需要动这一处。Authorization 从 storage 读取 token,配合第 4 章的登录流程实现无感携带。timeout 设 10000 毫秒,招聘信息流场景下超过这个时长用户基本已经离开页面,再久没有意义。成功判断采用“HTTP 状态码 + 业务 code 双重校验”,后端无论返回什么,只要业务上失败都能走到统一的 toast 提示。

3.2 storage.js:带过期时间的缓存封装

微信自带的 wx.setStorageSync 只支持key-value,没有过期时间概念。招聘类数据有个典型特征:职位列表可以缓存几分钟,但用户 token 过期后必须立刻失效。storage.js 的封装思路是写入时附带 expireAt 时间戳,读取时对比当前时间,过期则删除并返回 null。

const cache = { set(key, value, expireSeconds = 1800) { const payload = { value, expireAt: Date.now() + expireSeconds * 1000 } wx.setStorageSync(key, payload) }, get(key) { const payload = wx.getStorageSync(key) if (!payload) return null if (payload.expireAt && Date.now() > payload.expireAt) { wx.removeStorageSync(key) return null } return payload.value }, remove(key) { wx.removeStorageSync(key) } } module.exports = cache

set 方法第三个参数是过期秒数,默认 1800 秒即 30 分钟。职位列表缓存设 300 秒,用户信息设 86400 秒,token 不设有效期,由后端返回的过期时间决定。get 方法每次读取都检查 expireAt,避免页面拿到死数据。这样设计后,任何模块要缓存数据都是三行代码:引入 storage、set、get,业务方不需要知道底层存的是什么结构。

3.3 index.js:首页数据加载与缓存优先策略

首页 onLoad 启动后会同时请求 banner 和职位列表。常见做法是缓存优先:先读 storage 里的职位列表立即渲染,再请求接口刷新,刷新成功后更新缓存并重新 setData。这样即使用户在弱网环境,进入首页也不会白屏。

const api = require('../../utils/api.js') const storage = require('../../utils/storage.js') Page({ data: { banners: [], jobList: [], loading: true }, onLoad() { this.loadBanners() this.loadJobList() }, loadBanners() { api.request({ url: '/banners' }) .then((data) => { this.setData({ banners: data.map(item => item.imageUrl) }) }) .catch(() => {}) }, loadJobList() { const cached = storage.get('jobListCache') if (cached) { this.setData({ jobList: cached, loading: false }) } api.request({ url: '/jobs', data: { page: 1, pageSize: 10 } }) .then((data) => { this.setData({ jobList: data.list }) storage.set('jobListCache', data.list, 300) }) .catch(() => {}) .finally(() => { this.setData({ loading: false }) }) } })

onLoad 里两个加载函数并行触发,互不阻塞。缓存命中后先渲染一次,接口返回后把列表替换为最新数据,并把新缓存写回。loading 标志位用于控制骨架屏显示,缓存命中时直接置为 false,让用户感知到“秒开”,接口失败时 catch 里保持缓存数据展示,体验不会断裂。这个模式比单纯依赖 loading 组件更贴近真实业务。

4. 登录与会话管理:login.js、user.js 与 code 换 token 的完整链路

4.1 login.js:wx.login 获取 code 并交换 token

微信小程序的登录不涉及密码输入,核心链路是:wx.login 获取临时 code,把 code 传给后端,后端用 code 向微信接口换取 openid,再生成业务 token 返回给前端。code 有效期只有 5 分钟且只能用一次,所以拿到后要立刻调接口。下面是一个贴近工程实践的 login.js 核心代码:

const api = require('./api.js') const storage = require('./storage.js') const login = () => { return new Promise((resolve, reject) => { wx.login({ success: (res) => { if (!res.code) { reject(new Error('wx.login failed')) return } api.request({ url: '/login', method: 'POST', data: { code: res.code } }).then((data) => { storage.set('token', data.token) storage.set('userInfo', data.userInfo, 86400) resolve(data) }).catch(reject) }, fail: reject }) }) } module.exports = { login }

存储策略上,token 不设过期时间,是否失效由后端在下一次请求时通过 401 告知;userInfo 存一天,避免用户每次冷启动都重新拉取资料。登录成功的返回值里通常会包含用户 id、昵称、头像,这些一并写入缓存后,user.js 就有了数据来源。登录失败时需要区分两种情况:wx.login 本身失败,说明微信环境异常,提示重启小程序;code 换 token 失败,说明后端没有配置好,优先检查服务端有没有用 code 正常请求微信接口。

4.2 登录态在请求层的统一透传

token 的注入已经在 api.js 里完成,页面和业务模块不需要关心请求头长什么样。登录态失效的处理同样集中在 api.js:当后端返回 401 时,清除本地 token 并跳转到登录页。这里有一个关键点,跳转前要先检查当前页面栈,避免在登录页本身也触发跳转造成死循环。下面的表格列出了登录链路里最常见的三种状态及其对应处理方式。

场景触发条件处理策略
有效登录态storage 里有 token,且每次都透传正常发起请求,401 后进入失效流程
token 过期后端返回 401清除 token 和 userInfo,跳转登录页
未登录抢跑直接访问需要鉴权的接口先做 checkLogin,再决定是否拉起登录流程

4.3 user.js:用户信息读取与退出登录

user.js 是登录链路的收尾模块。它对外提供三个能力:读取当前用户、检查登录状态、退出登录。读取用户信息时优先返回内存里的 userInfo,其次读缓存,最后返回 null 提示调用方去登录。退出登录需要同时清掉 token 和 userInfo,再调用后端的 logout 接口做设备侧失效。

const storage = require('./storage.js') const user = { getUserInfo() { return storage.get('userInfo') || null }, isLogin() { return !!storage.get('token') }, logout() { storage.remove('token') storage.remove('userInfo') wx.reLaunch({ url: '/pages/index/index' }) } } module.exports = user

isLogin 是纯前端判断,只要存在 token 就视为已登录,真正靠得住的是后端校验。退出时用 wx.reLaunch 而不是 wx.navigateBack,是为了把页面栈重置,防止用户按返回键回到需要登录态的个人中心后看到空白页。

5. 招聘核心流程落地:职位类目配置、筛选联动与简历表单校验

5.1 course.js 职位类目与筛选条件的数据约定

course.js 在招聘小程序里承担的是“数据字典”角色,存放职位分类、技能标签、学历要求、工作经验档位等候选值。个人中心展示用户技能树时也用同一份数据源,避免多处硬编码后改一处漏一处。这类配置数据体积小且基本不变,适合直接放在前端 js 文件里,不必每次筛选都请求后端。

const course = { jobCategories: ['前端开发', '后端开发', '移动端', '测试', '运维', '产品', '设计'], skillTags: ['微信小程序', 'Vue', 'React', 'Node.js', 'Java', 'Python'], education: ['大专', '本科', '硕士', '博士'], experience: ['经验不限', '1年以下', '1-3年', '3-5年', '5年以上'] } module.exports = course

筛选面板从 course.js 读取候选值渲染分类栏,用户点击某个分类后,index.js 把选中的索引映射成对应字符串,组装进请求参数。这种做法把“枚举值的展示”和“业务判断”解耦,后续后端如果要求传数字类型 id,只需改 course.js 里每一项的结构,不影响页面代码。

5.2 职位列表筛选的数据流与页面联动

筛选交互的完整链路是:点击分类 → 更新 data 里的 activeId → 重置页码 → 调用 api 请求 → 替换 jobList。这里最容易出的问题是页面分页逻辑和筛选条件互相干扰,筛选后页码没有归 1,导致出现连续两页相同数据。下面是一个带筛选状态的加载函数:

// pages/index/index.js const api = require('../../utils/api.js') const storage = require('../../utils/storage.js') const course = require('../../utils/course.js') Page({ data: { categories: course.jobCategories, activeCategory: '前端开发', jobList: [], page: 1, hasMore: true }, onSelectCategory(e) { const category = e.currentTarget.dataset.category this.setData({ activeCategory: category, page: 1, hasMore: true, jobList: [] }) this.loadJobList() }, loadJobList() { const params = { page: this.data.page, pageSize: 10, category: this.data.activeCategory } api.request({ url: '/jobs', data: params }) .then((data) => { const list = this.data.page === 1 ? data.list : this.data.jobList.concat(data.list) this.setData({ jobList: list, hasMore: list.length < Number(data.total) ? true : false }) }) } })

onSelectCategory 先把 jobList 清空、页码重置为 1,再拉新数据,避免旧岗位和新筛选结果混在一起。请求参数里 category 直接传字符串,后端负责匹配职位表中的分类字段。hasMore 字段配合触底加载,onReachBottom 钩子里判断 hasMore 为 true 才把 page 加 1 并继续请求,否则直接 return。这套模式在原生小程序里不需要引入额外依赖。

5.3 简历提交页的表单校验与防重复提交

简历提交页是招聘流程的汇聚点,手机号、学历、工作年限、期望岗位都是必填项。表单校验不能只依赖后端,前端要先拦一道,常见校验包括手机号格式、必填项是否为空、工作年限与学历选项是否已选。每次 setData 时同步校验整张表单,提交按钮的 disabled 状态实时绑定校验结果,从入口处杜绝无效提交。

const validatePhone = (phone) => { return /^1[3-9]\d{9}$/.test(phone) } const validateResume = (form) => { if (!form.name || form.name.trim().length === 0) { return { valid: false, message: '请填写姓名' } } if (!validatePhone(form.phone)) { return { valid: false, message: '手机号格式不正确' } } if (!form.education) { return { valid: false, message: '请选择学历' } } if (!form.experience) { return { valid: false, message: '请选择工作年限' } } return { valid: true } } module.exports = { validateResume }

手机号校验用^1[3-9]\d{9}$,目前三大运营商的号段都能覆盖,不支持的虚拟号段后续单独维护。submit 事件里先跑 validateResume,返回 message 时用 wx.showToast 提示,并 return 阻断提交请求。表单提交按钮还要加一个布尔标志位 submitting,提交成功后置为 true,防止用户双击连发两份简历。面试场景里重复投递是最尴尬的错误之一,前端拦截成本最低。

6. 开发者工具联调与典型报错排查清单

6.1 工程导入与空白目录排查

微信开发者工具导入项目时,要注意目录选择到miniprogram/这一层,而不是最外层 zip 解压的根目录。如果导入后看到一片空白,优先检查 app.json 的 pages 数组里所有路径是否都存在对应文件,哪怕少一个页面文件,整个工程都编译不过。另一个高频问题是 project.config.json 缺失,工具会提示“项目配置文件不存在”,此时可以新建一个空白项目后,把miniprogramRoot字段指向 miniprogram 目录即可。

6.2 合法域名校验与开发模式跳过

真机预览时最常见的报错是url not in domain list。开发阶段可以在开发者工具右上角“详情 → 本地设置”里勾选“不校验合法域名”,但上线发布必须把接口域名配置到小程序管理后台的 request 合法域名白名单。域名必须为 HTTPS,ICP 备案、SSL 证书过期都会导致真机请求失败,开发工具里正常而手机预览挂掉,绝大部分是这两个原因。

6.3 典型报错与排查对照表

症状可能原因处理方式
首页白屏,console 报data is not definedsetData 里写了未声明的字段名对照 data 对象逐字检查字段拼写
banner 不显示,图片路径 404图片文件不存在或大小写不匹配检查 images 目录文件名和 wxml 的 src 是否完全一致
真机预览请求失败HTTPS 证书问题或域名未加白名单用浏览器打开接口地址,确认证书状态和服务可用性
登录后回到首页,下拉刷新又提示未登录token 写入了不同 storage key统一通过 storage.js 的 set/get 操作,禁止页面里直接wx.setStorageSync
列表滚动时出现跳帧和抖动图片未固定宽高,触底加载后页面高度突变给 job-card 固定 min-height,图片尺寸在数据层预处理

6.4 缓存版本号:一个改动成本极低的稳定性技巧

storage.js 的 key 全部加上版本号后缀,例如jobListCache_v2userInfo_v2,而不是笼统的jobListuserInfo。原因是小程序更新发版后,旧版本写入的缓存结构可能与新代码不兼容,轻则字段缺失,重则页面渲染异常。升级缓存 key 版本号后,旧缓存变成无人读取的孤儿数据,会在用户清理微信缓存时自然释放。代价只是多写几个字符,但能省掉一整个“发版后老用户页面崩掉”的排查链路。配合第 3 章的过期时间设计,这套缓存体系在招聘、电商、内容类小程序里迁移使用都很低成本。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询