AI摘要
本文详细介绍了如何使用uni-app框架开发一个支持微信小程序和H5的零工平台前端应用,包括技术选型、架构设计、核心功能实现及部署经验。项目采用Vue.js 2.6 + uni-app 3.0 + ColorUI技术栈,实现了模块化架构设计、分包加载策略、双重身份系统、地理位置服务、智能搜索与筛选、实时消息系统等功能。同时,文章还分享了响应式设计与适配、性能优化实践、部署与发布经验以及开发经验总结和未来规划。
本文将详细介绍我如何使用uni-app框架开发一个支持微信小程序和H5的零工平台前端应用,包含技术选型、架构设计、核心功能实现及部署经验。
前言
在当今移动互联网时代,跨平台开发已成为提高开发效率的重要手段。本次我选择uni-app框架开发了一个智慧零工平台的前端应用,该平台致力于为零工与雇主搭建高效便捷的双向服务桥梁。
项目概况
- 项目名称: 智慧零工平台前端系统
- 技术栈: Vue.js 2.6 + uni-app 3.0 + ColorUI
- 支持平台: 微信小程序 + H5
- 项目地址: GitHub - Gigplatform_front
- 在线预览: https://lgpt.ybyq.wang/
技术选型分析
为什么选择uni-app?
在众多跨平台解决方案中,我最终选择了uni-app,主要基于以下考虑:
- 一套代码多端运行: 支持编译到微信小程序、H5、App等10+平台
- 学习成本低: 基于Vue.js语法,前端开发者容易上手
- 生态完善: 拥有丰富的组件库和插件市场
- 性能优异: 接近原生应用的性能表现
- 社区活跃: DCloud官方维护,社区支持良好
核心技术栈
{
"前端框架": "Vue.js 2.6.11",
"跨平台框架": "uni-app 3.0",
"UI组件库": "ColorUI 2.1.6",
"样式预处理": "SCSS/SASS",
"状态管理": "Vuex",
"构建工具": "webpack",
"开发工具": "HBuilderX"
}
项目架构设计
整体架构
项目采用模块化架构设计,清晰分离业务逻辑和技术实现:
smart-gig-platform-front/
├── api/ # API接口层
├── components/ # 公共组件
├── pages/ # 页面文件
├── employerPackage/ # 雇主端分包
├── static/ # 静态资源
├── store/ # 状态管理
├── colorui/ # UI组件库
└── utils/ # 工具函数
分包策略
为了优化小程序包体积,我采用了分包加载策略:
{
"subPackages": [
{
"root": "employerPackage",
"name": "employer",
"pages": [
"pages/center/index",
"pages/postJob/index",
"pages/resume/index"
]
}
]
}
这样可以将雇主端功能独立打包,减少主包体积,提升首屏加载速度。
核心功能实现
1. 双重身份系统
这是项目的一大特色功能,用户可以在零工和雇主身份间无缝切换:
<template>
<view class="identity-switch">
<view class="switch-container">
<view
class="switch-item"
:class="{ active: currentRole === 'worker' }"
@click="switchRole('worker')"
>
<image src="/static/img/worker-icon.png" />
<text>我是零工</text>
</view>
<view
class="switch-item"
:class="{ active: currentRole === 'employer' }"
@click="switchRole('employer')"
>
<image src="/static/img/employer-icon.png" />
<text>我是雇主</text>
</view>
</view>
</view>
</template>
<script>
export default {
data() {
return {
currentRole: 'worker'
}
},
methods: {
switchRole(role) {
this.currentRole = role
this.$store.commit('setUserRole', role)
// 切换底部tabBar
if (role === 'employer') {
uni.reLaunch({
url: '/employerPackage/pages/center/index'
})
} else {
uni.reLaunch({
url: '/pages/index/index'
})
}
}
}
}
</script>
2. 地理位置服务
实现基于位置的工作推荐功能:
// 获取用户位置
async getUserLocation() {
try {
const res = await uni.getLocation({
type: 'wgs84'
})
this.userLocation = {
latitude: res.latitude,
longitude: res.longitude
}
// 获取附近工作
await this.getNearbyJobs()
} catch (error) {
console.error('获取位置失败:', error)
uni.showToast({
title: '位置获取失败',
icon: 'none'
})
}
},
// 计算距离
calculateDistance(lat1, lon1, lat2, lon2) {
const R = 6371 // 地球半径(km)
const dLat = (lat2 - lat1) * Math.PI / 180
const dLon = (lon2 - lon1) * Math.PI / 180
const a = Math.sin(dLat/2) * Math.sin(dLat/2) +
Math.cos(lat1 * Math.PI / 180) * Math.cos(lat2 * Math.PI / 180) *
Math.sin(dLon/2) * Math.sin(dLon/2)
const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a))
return R * c
}
3. 智能搜索与筛选
实现多维度的工作搜索功能:
<template>
<view class="search-container">
<!-- 搜索栏 -->
<view class="search-bar">
<input
v-model="searchKeyword"
placeholder="搜索工作职位、公司名称"
@confirm="handleSearch"
/>
<button @click="handleSearch">搜索</button>
</view>
<!-- 筛选条件 -->
<view class="filter-section">
<picker
:value="selectedCategory"
:range="categories"
range-key="name"
@change="onCategoryChange"
>
<view class="filter-item">
{{ selectedCategory ? categories[selectedCategory].name : '选择分类' }}
</view>
</picker>
<picker
:value="selectedSalary"
:range="salaryRanges"
@change="onSalaryChange"
>
<view class="filter-item">
{{ selectedSalary ? salaryRanges[selectedSalary] : '薪资范围' }}
</view>
</picker>
</view>
<!-- 搜索结果 -->
<scroll-view
scroll-y
class="job-list"
@scrolltolower="loadMore"
>
<job-card
v-for="job in jobList"
:key="job.id"
:job-data="job"
@click="goToDetail(job.id)"
/>
</scroll-view>
</view>
</template>
<script>
export default {
data() {
return {
searchKeyword: '',
selectedCategory: null,
selectedSalary: null,
jobList: [],
page: 1,
hasMore: true
}
},
methods: {
async handleSearch() {
this.page = 1
this.jobList = []
await this.loadJobs()
},
async loadJobs() {
if (!this.hasMore) return
try {
const params = {
page: this.page,
size: 10,
keyword: this.searchKeyword,
categoryId: this.selectedCategory,
salaryRange: this.selectedSalary
}
const response = await this.$api.getJobList(params)
const newJobs = response.data.list
if (this.page === 1) {
this.jobList = newJobs
} else {
this.jobList.push(...newJobs)
}
this.hasMore = newJobs.length === 10
this.page++
} catch (error) {
console.error('加载工作列表失败:', error)
}
}
}
}
</script>
4. 实时消息系统
实现雇主与零工间的实时通信:
// WebSocket连接管理
class MessageService {
constructor() {
this.socket = null
this.reconnectAttempts = 0
this.maxReconnectAttempts = 5
}
connect(userId) {
try {
this.socket = uni.connectSocket({
url: `wss://lgpt.ybyq.wang/ws/${userId}`,
header: {
'Authorization': uni.getStorageSync('token')
}
})
this.socket.onOpen(() => {
console.log('WebSocket连接成功')
this.reconnectAttempts = 0
})
this.socket.onMessage((res) => {
const message = JSON.parse(res.data)
this.handleMessage(message)
})
this.socket.onClose(() => {
console.log('WebSocket连接关闭')
this.reconnect()
})
this.socket.onError((error) => {
console.error('WebSocket错误:', error)
this.reconnect()
})
} catch (error) {
console.error('WebSocket连接失败:', error)
}
}
sendMessage(message) {
if (this.socket) {
this.socket.send({
data: JSON.stringify(message)
})
}
}
handleMessage(message) {
// 处理接收到的消息
switch (message.type) {
case 'chat':
this.updateChatList(message)
break
case 'notification':
this.showNotification(message)
break
case 'status_update':
this.updateApplicationStatus(message)
break
}
}
reconnect() {
if (this.reconnectAttempts < this.maxReconnectAttempts) {
setTimeout(() => {
this.reconnectAttempts++
this.connect()
}, 2000 * this.reconnectAttempts)
}
}
}
响应式设计与适配
多端适配策略
为了确保在不同平台上的一致体验,我采用了以下适配策略:
// 使用rpx单位进行响应式设计
.container {
padding: 20rpx;
// H5端特殊处理
/* #ifdef H5 */
max-width: 750rpx;
margin: 0 auto;
/* #endif */
// 微信小程序特殊处理
/* #ifdef MP-WEIXIN */
padding-top: calc(20rpx + env(safe-area-inset-top));
/* #endif */
}
// 统一的颜色变量
$primary-color: #4CAF50;
$success-color: #4CAF50;
$warning-color: #FF9800;
$danger-color: #F44336;
// 字体大小规范
$font-size-xs: 22rpx;
$font-size-sm: 24rpx;
$font-size-base: 28rpx;
$font-size-lg: 32rpx;
$font-size-xl: 36rpx;
组件化开发
开发了一系列可复用组件,提高开发效率:
<!-- job-card.vue - 工作卡片组件 -->
<template>
<view class="job-card" @click="handleClick">
<view class="job-header">
<view class="job-title">{{ jobData.title }}</view>
<view class="job-salary text-primary">{{ jobData.salary }}</view>
</view>
<view class="job-info">
<view class="company-name">{{ jobData.companyName }}</view>
<view class="job-location">
<text class="icon-location"></text>
{{ jobData.location }}
</view>
</view>
<view class="job-tags">
<view
v-for="tag in jobData.tags"
:key="tag"
class="tag"
>
{{ tag }}
</view>
</view>
<view class="job-footer">
<view class="publish-time">{{ formatTime(jobData.publishTime) }}</view>
<view class="apply-count">{{ jobData.applyCount }}人申请</view>
</view>
</view>
</template>
<script>
export default {
name: 'JobCard',
props: {
jobData: {
type: Object,
required: true
}
},
methods: {
handleClick() {
this.$emit('click', this.jobData)
},
formatTime(timestamp) {
const now = Date.now()
const diff = now - timestamp
const hours = Math.floor(diff / (1000 * 60 * 60))
if (hours < 1) return '刚刚发布'
if (hours < 24) return `${hours}小时前`
const days = Math.floor(hours / 24)
return `${days}天前`
}
}
}
</script>
性能优化实践
1. 图片优化
// 图片懒加载
Vue.directive('lazy-load', {
bind(el, binding) {
const observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
el.src = binding.value
observer.unobserve(el)
}
})
})
observer.observe(el)
}
})
// 图片压缩处理
function compressImage(file, quality = 0.8) {
return new Promise((resolve) => {
const canvas = document.createElement('canvas')
const ctx = canvas.getContext('2d')
const img = new Image()
img.onload = () => {
canvas.width = img.width
canvas.height = img.height
ctx.drawImage(img, 0, 0)
canvas.toBlob(resolve, 'image/jpeg', quality)
}
img.src = URL.createObjectURL(file)
})
}
2. 数据缓存策略
// API数据缓存
class CacheManager {
constructor() {
this.cache = new Map()
this.expireTime = 5 * 60 * 1000 // 5分钟
}
set(key, data) {
this.cache.set(key, {
data,
timestamp: Date.now()
})
}
get(key) {
const item = this.cache.get(key)
if (!item) return null
if (Date.now() - item.timestamp > this.expireTime) {
this.cache.delete(key)
return null
}
return item.data
}
clear() {
this.cache.clear()
}
}
// 使用示例
async function fetchJobList(params) {
const cacheKey = `job_list_${JSON.stringify(params)}`
const cachedData = cacheManager.get(cacheKey)
if (cachedData) {
return cachedData
}
const response = await api.getJobList(params)
cacheManager.set(cacheKey, response.data)
return response.data
}
3. 代码分割与按需加载
// 路由懒加载
const routes = [
{
path: '/pages/jobDetail/jobDetail',
component: () => import('@/pages/jobDetail/jobDetail.vue')
},
{
path: '/pages/employer/index',
component: () => import('@/pages/employer/index.vue')
}
]
// 组件按需引入
export default {
components: {
JobCard: () => import('@/components/job-card.vue'),
FilterPanel: () => import('@/components/filter-panel.vue')
}
}
部署与发布
H5部署
-
构建生产版本
npm run build:h5
-
服务器配置
server { listen 80; server_name lgpt.ybyq.wang; root /www/wwwroot/lgpt.ybyq.wang/h5; index index.html; # SPA路由支持 location / { try_files $uri $uri/ /index.html; } # API代理 location /api/ { proxy_pass https://lgpt.ybyq.wang:8081/api/; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; } # 静态资源缓存 location ~* \.(js|css|png|jpg|jpeg|gif|svg|woff|woff2)$ { expires 1y; add_header Cache-Control "public, immutable"; } # Gzip压缩 gzip on; gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript; }
微信小程序发布
-
构建小程序版本
npm run build:mp-weixin
-
小程序配置
{ "appid": "your-app-id", "projectname": "智慧零工平台", "setting": { "urlCheck": false, "es6": true, "postcss": true, "minified": true, "newFeature": true }, "compileType": "miniprogram" }
-
发布流程
- 使用微信开发者工具导入项目
- 配置合法域名白名单
- 进行真机测试
- 提交审核并发布
开发经验总结
遇到的挑战
-
跨平台兼容性问题
- 不同平台API差异
- 样式表现不一致
- 事件处理机制差异
-
性能优化挑战
- 小程序包体积限制
- 长列表渲染性能
- 图片加载优化
-
用户体验优化
- 网络异常处理
- 加载状态管理
- 错误边界处理
最佳实践
-
代码组织
// 统一的错误处理 Vue.config.errorHandler = (err, vm, info) => { console.error('Vue Error:', err, info) // 上报错误日志 reportError(err, info) } // 全局混入 Vue.mixin({ methods: { $showLoading(title = '加载中...') { uni.showLoading({ title }) }, $hideLoading() { uni.hideLoading() }, $showToast(title, icon = 'none') { uni.showToast({ title, icon }) } } })
-
API请求封装
// request.js const request = (options) => { return new Promise((resolve, reject) => { uni.request({ url: BASE_URL + options.url, method: options.method || 'GET', data: options.data || {}, header: { 'Content-Type': 'application/json', 'Authorization': getToken(), ...options.header }, success: (res) => { if (res.data.code === 0) { resolve(res.data) } else { uni.showToast({ title: res.data.message, icon: 'none' }) reject(res.data) } }, fail: (error) => { uni.showToast({ title: '网络错误', icon: 'none' }) reject(error) } }) }) }
-
状态管理
// store/index.js import Vue from 'vue' import Vuex from 'vuex' Vue.use(Vuex) export default new Vuex.Store({ state: { userInfo: null, userRole: 'worker', // worker | employer jobList: [], messageList: [] }, mutations: { SET_USER_INFO(state, userInfo) { state.userInfo = userInfo uni.setStorageSync('userInfo', userInfo) }, SET_USER_ROLE(state, role) { state.userRole = role uni.setStorageSync('userRole', role) } }, actions: { async login({ commit }, loginData) { try { const response = await api.login(loginData) const { token, user } = response.data uni.setStorageSync('token', token) commit('SET_USER_INFO', user) return response } catch (error) { throw error } } } })
未来规划
-
功能扩展
- 增加语音通话功能
- 实现AR技能展示
- 加入AI智能推荐
-
技术升级
- 升级到Vue 3 + Composition API
- 引入TypeScript增强类型安全
- 使用Vite提升构建性能
-
用户体验优化
- 加入暗黑模式支持
- 优化无障碍访问
- 提升加载性能
结语
通过这次uni-app跨平台开发实践,我深刻体会到了现代前端技术的强大能力。uni-app让我们能够用一套代码覆盖多个平台,大大提高了开发效率。
项目中最有价值的经验包括:
- 合理的架构设计是项目成功的基础
- 组件化开发能显著提高代码复用率
- 性能优化需要从多个维度综合考虑
- 用户体验是产品成功的关键因素
希望这篇文章能为正在学习uni-app或准备进行跨平台开发的朋友们提供一些参考和启发。
项目链接:
- 🌐 在线预览: https://lgpt.ybyq.wang/
- 💻 GitHub仓库: https://github.com/BXCQ/Gigplatform_front
- 📚 技术博客: https://blog.ybyq.wang/
如果您对项目有任何问题或建议,欢迎在GitHub上提Issue或通过邮箱 676567473@qq.com 联系我!