首页 > 移动开发 > 微信开发 > 从零搭建商城小程序:完整源码解析 + 前后端交互实战
项目概述与准备工作
在移动互联网时代,小程序以其“无需下载、即用即走”的特性,成为电商领域的重要入口。今天,我将带大家从零开始搭建一个完整的商城小程序,深入解析前后端架构设计与交互实现。
1.1技术栈选择
• 源码及演示:xcxyms.top
•前端:微信小程序原生开发(WXML、WXSS、JavaScript)
•后端:Node.js+Express+MySQL
•开发工具:微信开发者工具、Visual Studio Code
1.2项目结构规划
miniprogram/ ├── pages/ # 页面文件 │ ├── index/ # 首页 │ ├── category/ # 分类页 │ ├── cart/ # 购物车 │ └── mine/ # 个人中心 ├── components/ # 自定义组件 ├── utils/ # 工具函数 ├── config/ # 配置文件 └── app.js # 小程序入口 backend/ ├── routes/ # 路由文件 ├── controllers/ # 控制器 ├── models/ # 数据模型 └── app.js # 服务入口
小程序前端架构实现
1.应用初始化配置
在app.js中进行全局配置:
// app.js
App({
globalData: {
userInfo: null,
baseURL: 'https://your-domain.com/api'
},
onLaunch() {
// 检查更新
this.checkUpdate()
// 获取用户信息
this.getUserInfo()
},
// 封装网络请求
request(options) {
return new Promise((resolve, reject) => {
wx.request({
url: this.globalData.baseURL + options.url,
data: options.data || {},
header: {
'content-type': 'application/json',
'token': wx.getStorageSync('token')
},
method: options.method || 'GET',
success: (res) => {
if (res.data.code === 200) {
resolve(res.data.data)
} else {
reject(res.data.message)
}
},
fail: reject
})
})
}
})
2.首页商品展示实现
首页是小程序的门面,需要展示轮播图、商品分类和推荐商品。
<!-- pages/index/index.wxml -->
<view class="container">
<!-- 搜索框 -->
<view class="search-bar">
<input placeholder="搜索商品" bindinput="onSearchInput" />
</view>
<!-- 轮播图 -->
<swiper indicator-dots autoplay interval="3000">
<swiper-item wx:for="{{banners}}" wx:key="id">
<image src="{{item.image}}" mode="aspectFill" />
</swiper-item>
</swiper>
<!-- 商品分类 -->
<view class="categories">
<view
class="category-item"
wx:for="{{categories}}"
wx:key="id"
bindtap="goCategory"
data-id="{{item.id}}"
>
<image src="{{item.icon}}" />
<text>{{item.name}}</text>
</view>
</view>
<!-- 推荐商品 -->
<view class="recommend">
<view class="section-title">热门推荐</view>
<view class="product-list">
<view
class="product-item"
wx:for="{{products}}"
wx:key="id"
bindtap="goProductDetail"
data-id="{{item.id}}"
>
<image src="{{item.cover}}" />
<view class="product-info">
<view class="product-name">{{item.name}}</view>
<view class="product-price">¥{{item.price}}</view>
</view>
</view>
</view>
</view>
</view>
对应的JavaScript逻辑:
// pages/index/index.js
Page({
data: {
banners: [],
categories: [],
products: [],
page: 1,
loading: false
},
onLoad() {
this.loadHomeData()
},
async loadHomeData() {
try {
const app = getApp()
// 并行请求数据
const [banners, categories, products] = await Promise.all([
app.request({ url: '/banner/list' }),
app.request({ url: '/category/list' }),
app.request({ url: '/product/list', data: { page: 1, limit: 10 } })
])
this.setData({
banners,
categories: categories.slice(0, 8), // 只显示前8个分类
products
})
} catch (error) {
wx.showToast({ title: '加载失败', icon: 'error' })
}
},
// 上拉加载更多
onReachBottom() {
if (this.data.loading) return
this.setData({ loading: true })
this.loadMoreProducts()
},
async loadMoreProducts() {
const app = getApp()
const nextPage = this.data.page + 1
try {
const newProducts = await app.request({
url: '/product/list',
data: { page: nextPage, limit: 10 }
})
if (newProducts.length > 0) {
this.setData({
products: [...this.data.products, ...newProducts],
page: nextPage
})
} else {
wx.showToast({ title: '没有更多了', icon: 'none' })
}
} finally {
this.setData({ loading: false })
}
}
})

后端服务架构设计
1.Express服务搭建
// backend/app.js
const express = require('express')
const cors = require('cors')
const mysql = require('mysql2/promise')
const app = express()
// 中间件
app.use(cors())
app.use(express.json())
// 数据库连接池
const pool = mysql.createPool({
host: 'localhost',
user: 'root',
password: 'password',
database: 'mall',
waitForConnections: true,
connectionLimit: 10,
queueLimit: 0
})
// 路由挂载
app.use('/api/auth', require('./routes/auth'))
app.use('/api/product', require('./routes/product'))
app.use('/api/cart', require('./routes/cart'))
app.use('/api/order', require('./routes/order'))
// 错误处理中间件
app.use((err, req, res, next) => {
console.error(err.stack)
res.status(500).json({
code: 500,
message: '服务器内部错误',
data: null
})
})
const PORT = 3000
app.listen(PORT, () => {
console.log(`服务器运行在 http://localhost:${PORT}`)
})
2.商品管理接口实现
// backend/routes/product.js
const express = require('express')
const router = express.Router()
const productController = require('../controllers/productController')
// 商品列表
router.get('/list', async (req, res) => {
try {
const { page = 1, limit = 10, categoryId } = req.query
const offset = (page - 1) * limit
const [products, total] = await productController.getProducts({
offset,
limit: parseInt(limit),
categoryId
})
res.json({
code: 200,
message: 'success',
data: {
list: products,
total,
page: parseInt(page),
totalPages: Math.ceil(total / limit)
}
})
} catch (error) {
res.status(500).json({
code: 500,
message: error.message,
data: null
})
}
})
// 商品详情
router.get('/:id', async (req, res) => {
try {
const product = await productController.getProductById(req.params.id)
if (!product) {
return res.status(404).json({
code: 404,
message: '商品不存在',
data: null
})
}
res.json({
code: 200,
message: 'success',
data: product
})
} catch (error) {
res.status(500).json({
code: 500,
message: error.message,
data: null
})
}
})
module.exports = router
对应的控制器和数据模型:
// backend/controllers/productController.js
const productModel = require('../models/productModel')
class ProductController {
async getProducts({ offset, limit, categoryId }) {
const where = {}
if (categoryId) {
where.category_id = categoryId
}
const products = await productModel.find(where, offset, limit)
const total = await productModel.count(where)
return [products, total]
}
async getProductById(id) {
return await productModel.findById(id)
}
}
module.exports = new ProductController()
购物车功能完整实现
购物车是电商核心功能,需要处理商品增删改查和状态同步。
// pages/cart/cart.js
Page({
data: {
cartItems: [],
selectedAll: false,
totalPrice: 0,
totalCount: 0
},
onShow() {
this.loadCartData()
},
async loadCartData() {
try {
const app = getApp()
const cartItems = await app.request({ url: '/cart/list' })
this.setData({ cartItems }, () => {
this.calculateTotal()
})
} catch (error) {
console.error('加载购物车失败:', error)
}
},
// 选择商品
selectItem(e) {
const { id } = e.currentTarget.dataset
const { cartItems } = this.data
const index = cartItems.findIndex(item => item.id === id)
if (index !== -1) {
cartItems[index].selected = !cartItems[index].selected
this.setData({ cartItems }, () => {
this.calculateTotal()
this.checkSelectAll()
})
}
},
// 修改商品数量
async changeQuantity(e) {
const { id, type } = e.currentTarget.dataset
const { cartItems } = this.data
const index = cartItems.findIndex(item => item.id === id)
if (index === -1) return
const newQuantity = type === 'add'
? cartItems[index].quantity + 1
: Math.max(1, cartItems[index].quantity - 1)
try {
const app = getApp()
await app.request({
url: '/cart/update',
method: 'POST',
data: { productId: id, quantity: newQuantity }
})
cartItems[index].quantity = newQuantity
this.setData({ cartItems }, () => {
this.calculateTotal()
})
} catch (error) {
wx.showToast({ title: '更新失败', icon: 'error' })
}
},
// 计算总价和数量
calculateTotal() {
const { cartItems } = this.data
let totalPrice = 0
let totalCount = 0
cartItems.forEach(item => {
if (item.selected) {
totalPrice += item.price * item.quantity
totalCount += item.quantity
}
})
this.setData({
totalPrice: totalPrice.toFixed(2),
totalCount
})
},
// 全选/取消全选
toggleSelectAll() {
const { cartItems, selectedAll } = this.data
const newSelected = !selectedAll
cartItems.forEach(item => {
item.selected = newSelected
})
this.setData({
cartItems,
selectedAll: newSelected
}, () => {
this.calculateTotal()
})
},
// 去结算
async checkout() {
const { cartItems, totalPrice } = this.data
const selectedItems = cartItems.filter(item => item.selected)
if (selectedItems.length === 0) {
wx.showToast({ title: '请选择商品', icon: 'none' })
return
}
// 创建订单
try {
const app = getApp()
const order = await app.request({
url: '/order/create',
method: 'POST',
data: {
items: selectedItems,
totalPrice
}
})
// 跳转到订单确认页
wx.navigateTo({
url: `/pages/order/confirm?id=${order.id}`
})
} catch (error) {
wx.showToast({ title: '创建订单失败', icon: 'error' })
}
}
})

订单系统设计与实现
订单状态机设计
// backend/models/orderModel.js
class OrderModel {
// 订单状态枚举
static STATUS = {
PENDING: 0, // 待支付
PAID: 1, // 已支付
SHIPPED: 2, // 已发货
DELIVERED: 3, // 已收货
CANCELLED: 4, // 已取消
REFUNDED: 5 // 已退款
}
// 创建订单
async createOrder(userId, orderData) {
const connection = await pool.getConnection()
try {
await connection.beginTransaction()
// 1. 创建订单主记录
const [orderResult] = await connection.execute(
`INSERT INTO orders (user_id, order_no, total_amount, status, address)
VALUES (?, ?, ?, ?, ?)`,
[
userId,
this.generateOrderNo(),
orderData.totalPrice,
this.STATUS.PENDING,
JSON.stringify(orderData.address)
]
)
const orderId = orderResult.insertId
// 2. 创建订单商品明细
for (const item of orderData.items) {
await connection.execute(
`INSERT INTO order_items
(order_id, product_id, product_name, price, quantity, total_price)
VALUES (?, ?, ?, ?, ?, ?)`,
[
orderId,
item.productId,
item.productName,
item.price,
item.quantity,
item.price * item.quantity
]
)
// 3. 扣减库存
await connection.execute(
'UPDATE products SET stock = stock - ? WHERE id = ?',
[item.quantity, item.productId]
)
}
// 4. 清空购物车中已结算商品
await connection.execute(
'DELETE FROM cart WHERE user_id = ? AND product_id IN (?)',
[userId, orderData.items.map(item => item.productId)]
)
await connection.commit()
return { orderId, orderNo: orderResult.order_no }
} catch (error) {
await connection.rollback()
throw error
} finally {
connection.release()
}
}
// 生成订单号
generateOrderNo() {
const date = new Date()
const year = date.getFullYear()
const month = (date.getMonth() + 1).toString().padStart(2, '0')
const day = date.getDate().toString().padStart(2, '0')
const random = Math.floor(Math.random() * 10000).toString().padStart(4, '0')
return `${year}${month}${day}${random}`
}
}
用户认证与支付集成
1.微信登录集成
// utils/auth.js
const login = () => {
return new Promise((resolve, reject) => {
wx.login({
success: async (res) => {
if (res.code) {
try {
const app = getApp()
const result = await app.request({
url: '/auth/wxlogin',
method: 'POST',
data: { code: res.code }
})
// 存储token
wx.setStorageSync('token', result.token)
wx.setStorageSync('userInfo', result.userInfo)
resolve(result.userInfo)
} catch (error) {
reject(error)
}
} else {
reject(new Error('登录失败'))
}
},
fail: reject
})
})
}
// 检查登录状态
const checkLogin = () => {
return new Promise((resolve) => {
const token = wx.getStorageSync('token')
if (!token) {
// 跳转到登录页
wx.navigateTo({ url: '/pages/auth/login' })
resolve(false)
} else {
// 验证token有效性
wx.checkSession({
success: () => resolve(true),
fail: () => {
wx.removeStorageSync('token')
wx.navigateTo({ url: '/pages/auth/login' })
resolve(false)
}
})
}
})
}
2.微信支付集成
// utils/auth.js
const login = () => {
return new Promise((resolve, reject) => {
wx.login({
success: async (res) => {
if (res.code) {
try {
const app = getApp()
const result = await app.request({
url: '/auth/wxlogin',
method: 'POST',
data: { code: res.code }
})
// 存储token
wx.setStorageSync('token', result.token)
wx.setStorageSync('userInfo', result.userInfo)
resolve(result.userInfo)
} catch (error) {
reject(error)
}
} else {
reject(new Error('登录失败'))
}
},
fail: reject
})
})
}
// 检查登录状态
const checkLogin = () => {
return new Promise((resolve) => {
const token = wx.getStorageSync('token')
if (!token) {
// 跳转到登录页
wx.navigateTo({ url: '/pages/auth/login' })
resolve(false)
} else {
// 验证token有效性
wx.checkSession({
success: () => resolve(true),
fail: () => {
wx.removeStorageSync('token')
wx.navigateTo({ url: '/pages/auth/login' })
resolve(false)
}
})
}
})
}
性能优化与最佳实践
1.图片懒加载优化
// 在页面配置中启用图片懒加载
// pages/index/index.json
{
"usingComponents": {},
"enablePullDownRefresh": true,
"onReachBottomDistance": 50,
"lazyCodeLoading": "requiredComponents"
}
// 使用IntersectionObserver实现自定义懒加载
const initLazyLoad = () => {
const observer = wx.createIntersectionObserver(this)
observer.relativeToViewport({ bottom: 100 }).observe('.lazy-image', (res) => {
if (res.intersectionRatio > 0) {
// 加载图片
this.loadImage(res.dataset.src, res.id)
}
})
}
2.数据缓存策略
// utils/cache.js
class CacheManager {
constructor() {
this.prefix = 'mall_'
}
set(key, data, expire = 3600) {
const cacheData = {
data,
expire: Date.now() + expire * 1000
}
try {
wx.setStorageSync(this.prefix + key, cacheData)
} catch (error) {
console.error('缓存设置失败:', error)
}
}
get(key) {
try {
const cacheData = wx.getStorageSync(this.prefix + key)
if (!cacheData) return null
if (Date.now() > cacheData.expire) {
this.remove(key)
return null
}
return cacheData.data
} catch (error) {
console.error('缓存获取失败:', error)
return null
}
}
// 首页数据缓存示例
async getHomeData(forceRefresh = false) {
const cacheKey = 'home_data'
if (!forceRefresh) {
const cached = this.get(cacheKey)
if (cached) return cached
}
const app = getApp()
const data = await app.request({ url: '/home/data' })
// 缓存1小时
this.set(cacheKey, data, 3600)
return data
}
}
总结
通过本文的详细拆解,我们完成了从零搭建商城小程序的完整探索。从基础框架搭建到前后端交互实现,从购物车逻辑到微信支付集成,每个模块都展示了实战开发的核心要点。这个过程中,我们不仅掌握了小程序开发的工具链和技术栈,更重要的是理解了电商系统的业务逻辑与数据流转。在快速迭代的互联网时代,掌握小程序开发技能已不仅仅是加分项,而是许多场景下的必备能力。希望本项目的完整实现,能为您提供可复用的开发模式和清晰的架构思路。
来自 “ ITPUB博客 ” ,链接:https://blog.itpub.net/70049276/viewspace-3107787/ ,如需转载,请注明出处,否则将追究法律责任。