一、项目规划与技术选型
本教程将使用Uniapp开发一个完整的社交类应用,支持微信小程序、Android和iOS三端发布。
核心功能模块:
- 用户认证系统(手机号+验证码)
- 朋友圈动态发布与浏览
- 即时通讯(文字+图片)
- 地理位置社交
- 用户个人主页
- 消息通知系统
技术架构:
- 前端框架:Uniapp 3.0 + Vue2
- UI组件库:uView UI 2.0
- 状态管理:Vuex
- 即时通讯:Socket.io + WebSocket
- 地图服务:腾讯位置服务
- 后端接口:RESTful API
二、项目初始化与工程配置
1. 创建Uniapp项目
# 通过HBuilderX创建项目
文件 → 新建 → 项目 → Uniapp项目
选择默认模板,勾选Vue2版本
# 或者使用CLI方式
vue create -p dcloudio/uni-preset-vue social-app
cd social-app
# 安装必要依赖
npm install uview-ui socket.io-client qs
2. 项目目录结构优化
social-app/
├── common/ # 公共资源
│ ├── icons/ # 图标资源
│ └── styles/ # 公共样式
├── components/ # 组件目录
│ ├── base/ # 基础组件
│ └── business/ # 业务组件
├── pages/ # 页面目录
│ ├── home/ # 首页
│ ├── social/ # 社交功能
│ └── user/ # 用户中心
├── static/ # 静态资源
├── store/ # Vuex状态管理
├── utils/ # 工具函数
│ ├── api/ # 接口封装
│ ├── libs/ # 第三方库
│ └── helpers/ # 辅助函数
├── App.vue # 应用入口
├── main.js # 主入口文件
└── manifest.json # 应用配置
3. 基础配置 (main.js)
import Vue from 'vue'
import App from './App'
import uView from 'uview-ui'
import store from './store'
Vue.use(uView)
Vue.prototype.$store = store
// 全局挂载常用方法
import { http } from '@/utils/api'
Vue.prototype.$http = http
// 开发环境启用调试
if (process.env.NODE_ENV === 'development') {
Vue.config.productionTip = true
} else {
Vue.config.productionTip = false
}
const app = new Vue({
store,
...App
})
app.$mount()
三、核心功能实现
1. 用户认证系统
实现手机号登录组件:
<template>
<view class="login-container">
<u-form :model="form" :rules="rules" ref="loginForm">
<u-form-item prop="phone">
<u-input v-model="form.phone" placeholder="请输入手机号" />
</u-form-item>
<u-form-item prop="code">
<u-input v-model="form.code" placeholder="请输入验证码" />
<u-button
slot="right"
@click="getSmsCode"
:disabled="codeDisabled">
{{ codeText }}
</u-button>
</u-form-item>
</u-form>
<u-button
type="primary"
@click="handleLogin"
:loading="loading">
登录
</u-button>
</view>
</template>
<script>
export default {
data() {
return {
form: {
phone: '',
code: ''
},
rules: {
phone: [
{ required: true, message: '请输入手机号', trigger: 'blur' },
{ pattern: /^1[3-9]d{9}$/, message: '手机号格式不正确' }
],
code: [
{ required: true, message: '请输入验证码', trigger: 'blur' },
{ pattern: /^d{6}$/, message: '验证码格式不正确' }
]
},
codeText: '获取验证码',
codeDisabled: false,
loading: false
}
},
methods: {
async getSmsCode() {
if (!this.form.phone) {
return this.$u.toast('请输入手机号')
}
let countdown = 60
this.codeDisabled = true
this.codeText = `${countdown}s后重新获取`
const timer = setInterval(() => {
countdown--
this.codeText = `${countdown}s后重新获取`
if (countdown <= 0) {
clearInterval(timer)
this.codeText = '获取验证码'
this.codeDisabled = false
}
}, 1000)
try {
await this.$http.post('/sms/send', { phone: this.form.phone })
this.$u.toast('验证码已发送')
} catch (e) {
clearInterval(timer)
this.codeText = '获取验证码'
this.codeDisabled = false
}
},
async handleLogin() {
try {
this.loading = true
const res = await this.$http.post('/auth/login', this.form)
// 存储用户token
uni.setStorageSync('token', res.data.token)
// 更新Vuex状态
this.$store.commit('user/SET_USER_INFO', res.data.userInfo)
// 跳转到首页
uni.reLaunch({ url: '/pages/home/index' })
} finally {
this.loading = false
}
}
}
}
</script>
2. 朋友圈动态功能
<template>
<view class="moment-container">
<scroll-view
scroll-y
@scrolltolower="loadMore"
refresher-enabled
@refresherrefresh="refreshData">
<moment-post
v-for="item in momentList"
:key="item.id"
:data="item"
@like="handleLike"
@comment="handleComment" />
<u-loadmore
:status="loadingStatus"
:load-text="loadText" />
</scroll-view>
<u-button
class="post-btn"
type="primary"
shape="circle"
@click="gotoPost">
<u-icon name="plus"></u-icon>
</u-button>
</view>
</template>
<script>
import MomentPost from '@/components/business/MomentPost'
export default {
components: { MomentPost },
data() {
return {
momentList: [],
page: 1,
pageSize: 10,
loadingStatus: 'loadmore',
loadText: {
loadmore: '上拉加载更多',
loading: '正在加载...',
nomore: '没有更多了'
}
}
},
onLoad() {
this.loadData()
},
methods: {
async loadData() {
this.loadingStatus = 'loading'
try {
const res = await this.$http.get('/moment/list', {
params: { page: this.page, pageSize: this.pageSize }
})
if (this.page === 1) {
this.momentList = res.data.list
} else {
this.momentList = [...this.momentList, ...res.data.list]
}
this.loadingStatus = res.data.list.length {
uni.stopPullDownRefresh()
}, 1000)
},
loadMore() {
if (this.loadingStatus === 'nomore') return
this.page++
this.loadData()
},
handleLike(momentId) {
// 处理点赞逻辑
},
handleComment(momentId) {
// 处理评论逻辑
},
gotoPost() {
uni.navigateTo({ url: '/pages/social/post' })
}
}
}
</script>
四、即时通讯实现
1. WebSocket连接管理
// utils/libs/socket.js
import io from 'socket.io-client'
import { getToken } from '@/utils/auth'
let socket = null
const eventCallbacks = {}
export const initSocket = () => {
if (socket) return socket
socket = io('https://your-websocket-server.com', {
transports: ['websocket'],
query: { token: getToken() }
})
socket.on('connect', () => {
console.log('Socket connected')
})
socket.on('disconnect', () => {
console.log('Socket disconnected')
})
socket.on('error', (err) => {
console.error('Socket error:', err)
})
// 通用消息处理
socket.on('message', (data) => {
const callback = eventCallbacks[data.event]
if (callback) {
callback(data.payload)
}
})
return socket
}
export const onSocketEvent = (event, callback) => {
eventCallbacks[event] = callback
}
export const emitSocketEvent = (event, payload) => {
if (socket) {
socket.emit('message', { event, payload })
}
}
2. 聊天页面实现
<template>
<view class="chat-container">
<scroll-view
scroll-y
:scroll-top="scrollTop"
scroll-with-animation
class="message-list">
<view
v-for="(msg, index) in messages"
:key="index"
:class="['message-item', msg.sender === userInfo.id ? 'right' : 'left']">
<image
:src="msg.avatar"
class="avatar" />
<view class="content">
<text v-if="msg.type === 'text'">{{ msg.content }}</text>
<image
v-else-if="msg.type === 'image'"
:src="msg.content"
mode="widthFix"
@click="previewImage(msg.content)" />
</view>
</view>
</scroll-view>
<view class="input-area">
<u-input
v-model="inputMsg"
placeholder="输入消息..."
@confirm="sendText" />
<u-button
type="primary"
@click="sendText">
发送
</u-button>
</view>
</view>
</template>
<script>
import { onSocketEvent, emitSocketEvent } from '@/utils/libs/socket'
export default {
data() {
return {
messages: [],
inputMsg: '',
scrollTop: 0,
userInfo: {}
}
},
onLoad(options) {
this.userInfo = this.$store.state.user.info
this.targetId = options.targetId
// 初始化WebSocket
this.initSocket()
// 加载历史消息
this.loadHistory()
},
methods: {
initSocket() {
onSocketEvent('chat_message', this.handleNewMessage)
},
handleNewMessage(msg) {
if (msg.sender === this.targetId || msg.sender === this.userInfo.id) {
this.messages.push(msg)
this.scrollToBottom()
}
},
async loadHistory() {
const res = await this.$http.get('/chat/history', {
params: { targetId: this.targetId }
})
this.messages = res.data.list
this.$nextTick(() => {
this.scrollToBottom()
})
},
scrollToBottom() {
setTimeout(() => {
this.scrollTop = 99999
}, 100)
},
sendText() {
if (!this.inputMsg.trim()) return
const msg = {
type: 'text',
content: this.inputMsg,
sender: this.userInfo.id,
receiver: this.targetId,
avatar: this.userInfo.avatar,
time: Date.now()
}
emitSocketEvent('chat_message', msg)
this.messages.push(msg)
this.inputMsg = ''
this.scrollToBottom()
},
previewImage(url) {
uni.previewImage({
urls: [url]
})
}
}
}
</script>
五、多平台适配技巧
1. 条件编译处理平台差异
// 获取系统状态栏高度
function getStatusBarHeight() {
// #ifdef MP-WEIXIN
const info = uni.getSystemInfoSync()
return info.statusBarHeight
// #endif
// #ifdef APP-PLUS
return plus.navigator.getStatusbarHeight()
// #endif
// #ifdef H5
return 0
// #endif
}
// 平台特定样式处理
<view class="container" :style="{ paddingTop: statusBarHeight + 'px' }">
<!-- 内容区域 -->
</view>
2. 原生能力封装
// utils/native.js
export const chooseImage = (options = {}) => {
return new Promise((resolve, reject) => {
// #ifdef APP-PLUS
plus.gallery.pick(
res => resolve(res.files),
err => reject(err),
options
)
// #endif
// #ifdef MP-WEIXIN || H5
uni.chooseImage({
count: options.count || 9,
sizeType: options.sizeType || ['original', 'compressed'],
sourceType: options.sourceType || ['album', 'camera'],
success: res => resolve(res.tempFilePaths),
fail: err => reject(err)
})
// #endif
})
}
export const getLocation = () => {
return new Promise((resolve, reject) => {
// #ifdef APP-PLUS
plus.geolocation.getCurrentPosition(
pos => resolve({
latitude: pos.coords.latitude,
longitude: pos.coords.longitude
}),
err => reject(err)
)
// #endif
// #ifdef MP-WEIXIN
uni.getLocation({
type: 'gcj02',
success: res => resolve(res),
fail: err => reject(err)
})
// #endif
// #ifdef H5
if (navigator.geolocation) {
navigator.geolocation.getCurrentPosition(
pos => resolve({
latitude: pos.coords.latitude,
longitude: pos.coords.longitude
}),
err => reject(err)
)
} else {
reject(new Error('浏览器不支持地理位置'))
}
// #endif
})
}
六、性能优化方案
1. 图片懒加载与压缩
<!-- 使用uView的懒加载组件 -->
<u-lazy-load
:image="item.image"
:loading-img="placeholderImg"
:error-img="errorImg">
</u-lazy-load>
// 图片压缩处理
async compressImage(filePath) {
// #ifdef APP-PLUS
const result = await new Promise((resolve) => {
plus.zip.compressImage({
src: filePath,
dst: `_doc/compressed/${Date.now()}.jpg`,
quality: 70,
width: '50%',
height: '50%'
}, resolve)
})
return result.target
// #endif
// #ifdef MP-WEIXIN
const res = await uni.compressImage({
src: filePath,
quality: 70
})
return res.tempFilePath
// #endif
// #ifdef H5
return filePath // H5端暂不处理压缩
// #endif
}
2. 分包加载配置
// manifest.json 配置
{
"mp-weixin": {
"optimization": {
"subPackages": true
}
}
}
// pages.json 分包配置
{
"pages": [...],
"subPackages": [
{
"root": "pages/social",
"pages": [
{ "path": "moment/index" },
{ "path": "chat/index" }
]
},
{
"root": "pages/user",
"pages": [
{ "path": "profile/index" },
{ "path": "settings/index" }
]
}
],
"preloadRule": {
"pages/home/index": {
"network": "all",
"packages": ["pages/social"]
}
}
}
七、打包发布流程
1. 微信小程序发布
# 生产环境打包
npm run build:mp-weixin
# 使用微信开发者工具
1. 导入项目:选择/unpackage/dist/build/mp-weixin目录
2. 上传代码:点击"上传"按钮
3. 登录微信公众平台提交审核
2. App打包发布
# Android打包
npm run build:app-plus
# iOS打包
1. 使用HBuilderX打开项目
2. 选择"发行" → "原生App-云打包"
3. 配置证书和描述文件
4. 提交打包并下载ipa文件
5. 上传到App Store Connect
八、总结与扩展
本教程完整实现了基于Uniapp的社交应用开发:
- 搭建了跨平台开发环境
- 实现了核心社交功能
- 集成了即时通讯能力
- 优化了多端适配方案
- 完成了应用发布流程
扩展方向:
- 音视频通话功能集成
- 社交推荐算法实现
- AR虚拟形象功能
- 国际化多语言支持
完整项目代码已上传GitHub:https://github.com/example/uniapp-social-app