UniApp框架概述与优势
UniApp是一个使用Vue.js开发所有前端应用的框架,开发者编写一套代码,可发布到iOS、Android、Web(响应式)、以及各种小程序(微信/支付宝/百度/头条/QQ/快手/钉钉/淘宝)、快应用等多个平台。它具有开发效率高、学习成本低、生态丰富等显著优势。
环境搭建与项目创建
首先需要安装HBuilderX IDE,这是UniApp官方推荐的开发工具。
安装与创建项目
# 通过HBuilderX可视化创建
1. 打开HBuilderX → 文件 → 新建 → 项目
2. 选择uni-app项目类型
3. 输入项目名称,选择模板(推荐使用默认模板)
4. 点击创建
# 或者通过CLI方式创建(需先安装vue-cli)
npm install -g @vue/cli
vue create -p dcloudio/uni-preset-vue my-project
项目目录结构
my-project/
├── pages/ # 页面文件目录
│ ├── index/
│ │ ├── index.vue # 首页页面
│ │ └── index.json # 页面配置文件
│ └── ...
├── static/ # 静态资源目录
├── components/ # 自定义组件目录
├── uni_modules/ # uni模块目录
├── App.vue # 应用入口文件
├── main.js # 应用入口js
├── manifest.json # 应用配置文件
├── pages.json # 页面路由配置文件
└── uni.scss # 全局scss变量文件
核心配置文件详解
pages.json – 全局页面配置
{
"pages": [
{
"path": "pages/index/index",
"style": {
"navigationBarTitleText": "首页",
"enablePullDownRefresh": true,
"backgroundColor": "#F8F8F8"
}
},
{
"path": "pages/product/list",
"style": {
"navigationBarTitleText": "商品列表",
"enablePullDownRefresh": false
}
}
],
"globalStyle": {
"navigationBarTextStyle": "black",
"navigationBarTitleText": "uni-app",
"navigationBarBackgroundColor": "#FFFFFF",
"backgroundColor": "#FFFFFF",
"app-plus": {
"titleView": false
}
},
"tabBar": {
"color": "#7A7E83",
"selectedColor": "#007AFF",
"backgroundColor": "#FFFFFF",
"list": [
{
"pagePath": "pages/index/index",
"iconPath": "static/tabbar/home.png",
"selectedIconPath": "static/tabbar/home_active.png",
"text": "首页"
},
{
"pagePath": "pages/cart/cart",
"iconPath": "static/tabbar/cart.png",
"selectedIconPath": "static/tabbar/cart_active.png",
"text": "购物车"
}
]
}
}
manifest.json – 应用配置
{
"name": "我的电商应用",
"appid": "__UNI__XXXXXX",
"description": "一个基于UniApp开发的电商应用",
"versionName": "1.0.0",
"versionCode": "100",
"transformPx": false,
"app-plus": {
"usingComponents": true,
"nvueStyleCompiler": "uni-app",
"compilerVersion": 3,
"splashscreen": {
"alwaysShowBeforeRender": true,
"waiting": true,
"autoclose": true,
"delay": 0
}
},
"mp-weixin": {
"appid": "wxxxxxxxxxxxxxx",
"setting": {
"urlCheck": false
},
"usingComponents": true
}
}
实战:电商应用首页开发
首页Vue组件结构
<template>
<view class="container">
<!-- 搜索栏 -->
<view class="search-bar">
<view class="search-box" @click="navigateToSearch">
<uni-icons type="search" size="18"></uni-icons>
<text class="placeholder">搜索商品</text>
</view>
</view>
<!-- 轮播图 -->
<swiper class="swiper" circular indicator-dots autoplay interval="3000">
<swiper-item v-for="(item, index) in bannerList" :key="index">
<image :src="item.image" mode="aspectFill" @click="navigateTo(item.url)"></image>
</swiper-item>
</swiper>
<!-- 分类导航 -->
<view class="category-nav">
<view
class="nav-item"
v-for="(item, index) in categoryList"
:key="index"
@click="navigateToCategory(item.id)"
>
<image :src="item.icon"></image>
<text>{{ item.name }}</text>
</view>
</view>
<!-- 商品列表 -->
<view class="product-section">
<view class="section-title">
<text>热门推荐</text>
<text class="more" @click="navigateToProductList">查看更多 ></text>
</view>
<view class="product-list">
<view
class="product-item"
v-for="(product, index) in productList"
:key="index"
@click="navigateToProductDetail(product.id)"
>
<image class="product-image" :src="product.image" mode="aspectFill"></image>
<view class="product-info">
<text class="product-name">{{ product.name }}</text>
<text class="product-desc">{{ product.description }}</text>
<view class="product-price">
<text class="current-price">¥{{ product.price }}</text>
<text class="original-price" v-if="product.originalPrice">¥{{ product.originalPrice }}</text>
</view>
</view>
</view>
</view>
</view>
</view>
</template>
<script>
export default {
data() {
return {
bannerList: [
{ image: '/static/banner/1.jpg', url: '/pages/product/detail?id=1' },
{ image: '/static/banner/2.jpg', url: '/pages/product/detail?id=2' },
{ image: '/static/banner/3.jpg', url: '/pages/product/detail?id=3' }
],
categoryList: [
{ id: 1, name: '手机数码', icon: '/static/category/phone.png' },
{ id: 2, name: '家用电器', icon: '/static/category/appliance.png' },
{ id: 3, name: '服装鞋帽', icon: '/static/category/clothes.png' },
{ id: 4, name: '美妆个护', icon: '/static/category/beauty.png' },
{ id: 5, name: '生鲜食品', icon: '/static/category/food.png' }
],
productList: [
{
id: 1,
name: '高端智能手机',
description: '超清全面屏,超长续航',
price: 3999,
originalPrice: 4999,
image: '/static/product/1.jpg'
},
{
id: 2,
name: '无线蓝牙耳机',
description: '主动降噪,高清音质',
price: 699,
originalPrice: 899,
image: '/static/product/2.jpg'
}
]
}
},
methods: {
navigateToSearch() {
uni.navigateTo({
url: '/pages/search/search'
})
},
navigateTo(url) {
uni.navigateTo({
url: url
})
},
navigateToCategory(categoryId) {
uni.navigateTo({
url: `/pages/product/list?categoryId=${categoryId}`
})
},
navigateToProductList() {
uni.navigateTo({
url: '/pages/product/list'
})
},
navigateToProductDetail(productId) {
uni.navigateTo({
url: `/pages/product/detail?id=${productId}`
})
}
}
}
</script>
状态管理与数据请求
使用Vuex进行状态管理
// store/index.js
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
const store = new Vuex.Store({
state: {
userInfo: null,
cartCount: 0,
token: uni.getStorageSync('token') || ''
},
mutations: {
SET_USER_INFO(state, userInfo) {
state.userInfo = userInfo
},
SET_CART_COUNT(state, count) {
state.cartCount = count
},
SET_TOKEN(state, token) {
state.token = token
uni.setStorageSync('token', token)
},
CLEAR_USER_DATA(state) {
state.userInfo = null
state.token = ''
state.cartCount = 0
uni.removeStorageSync('token')
}
},
actions: {
// 登录动作
async login({ commit }, loginData) {
try {
const res = await uni.request({
url: '/api/user/login',
method: 'POST',
data: loginData
})
if (res.data.code === 200) {
commit('SET_TOKEN', res.data.data.token)
commit('SET_USER_INFO', res.data.data.userInfo)
return Promise.resolve(res.data)
} else {
return Promise.reject(res.data)
}
} catch (error) {
return Promise.reject(error)
}
},
// 获取购物车数量
async getCartCount({ commit }) {
try {
const res = await uni.request({
url: '/api/cart/count',
method: 'GET'
})
if (res.data.code === 200) {
commit('SET_CART_COUNT', res.data.data.count)
}
} catch (error) {
console.error('获取购物车数量失败', error)
}
}
}
})
export default store
封装网络请求
// utils/request.js
const BASE_URL = 'https://api.example.com'
const request = (options) => {
return new Promise((resolve, reject) => {
const { url, method = 'GET', data = {}, header = {} } = options
// 添加token到请求头
const token = uni.getStorageSync('token')
if (token) {
header['Authorization'] = 'Bearer ' + token
}
uni.request({
url: BASE_URL + url,
method,
data,
header,
success: (res) => {
if (res.statusCode === 200) {
resolve(res.data)
} else if (res.statusCode === 401) {
// token过期,跳转到登录页
uni.navigateTo({
url: '/pages/login/login'
})
reject(res.data)
} else {
reject(res.data)
}
},
fail: (error) => {
reject(error)
}
})
})
}
// 封装常用请求方法
export const get = (url, data = {}) => {
return request({ url, method: 'GET', data })
}
export const post = (url, data = {}) => {
return request({ url, method: 'POST', data })
}
export const put = (url, data = {}) => {
return request({ url, method: 'PUT', data })
}
export const del = (url, data = {}) => {
return request({ url, method: 'DELETE', data })
}
export default request
常用UI组件与自定义组件
使用uni-ui组件库
// 安装uni-ui
npm install @dcloudio/uni-ui
// 在页面中使用
<template>
<view>
<uni-nav-bar title="商品详情" left-icon="back" @clickLeft="goBack"></uni-nav-bar>
<uni-swipe-action>
<uni-swipe-action-item :options="options" @click="onSwipeClick">
<view class="cart-item">
<image class="product-image" :src="product.image"></image>
<view class="product-info">
<text class="product-name">{{ product.name }}</text>
<text class="product-price">¥{{ product.price }}</text>
</view>
</view>
</uni-swipe-action-item>
</uni-swipe-action>
<uni-load-more :status="loadStatus"></uni-load-more>
</view>
</template>
<script>
import uniNavBar from '@dcloudio/uni-ui/lib/uni-nav-bar/uni-nav-bar.vue'
import uniSwipeAction from '@dcloudio/uni-ui/lib/uni-swipe-action/uni-swipe-action.vue'
import uniLoadMore from '@dcloudio/uni-ui/lib/uni-load-more/uni-load-more.vue'
export default {
components: {
uniNavBar,
uniSwipeAction,
uniLoadMore
},
data() {
return {
options: [
{
text: '删除',
style: {
backgroundColor: '#dd524d'
}
}
],
loadStatus: 'more'
}
},
methods: {
goBack() {
uni.navigateBack()
},
onSwipeClick(e) {
if (e.index === 0) {
this.deleteCartItem()
}
}
}
}
</script>
自定义弹窗组件
// components/custom-modal.vue
<template>
<view class="modal-mask" v-if="visible" @click="close">
<view class="modal-container" @click.stop>
<view class="modal-header">
<text class="modal-title">{{ title }}</text>
<uni-icons
type="close"
size="24"
class="close-icon"
@click="close"
></uni-icons>
</view>
<view class="modal-content">
<slot></slot>
</view>
<view class="modal-footer" v-if="showFooter">
<button class="btn-cancel" @click="cancel">取消</button>
<button class="btn-confirm" @click="confirm">确认</button>
</view>
</view>
</view>
</template>
<script>
export default {
name: 'CustomModal',
props: {
visible: {
type: Boolean,
default: false
},
title: {
type: String,
default: '提示'
},
showFooter: {
type: Boolean,
default: true
}
},
methods: {
close() {
this.$emit('update:visible', false)
this.$emit('close')
},
cancel() {
this.$emit('cancel')
this.close()
},
confirm() {
this.$emit('confirm')
}
}
}
</script>
平台差异化处理与打包发布
条件编译处理平台差异
// 平台条件编译
<template>
<view>
<!-- #ifdef MP-WEIXIN -->
<button open-type="getUserInfo" @getuserinfo="getUserInfo">微信登录</button>
<!-- #endif -->
<!-- #ifdef APP-PLUS -->
<button @click="appLogin">APP登录</button>
<!-- #endif -->
<!-- #ifdef H5 -->
<button @click="h5Login">H5登录</button>
<!-- #endif -->
</view>
</template>
// JS中的条件编译
onLoad() {
// #ifdef MP-WEIXIN
console.log('微信小程序平台')
// #endif
// #ifdef APP-PLUS
console.log('APP平台')
// #endif
}
// 样式中的条件编译
.example {
color: #000000;
/* #ifdef MP-WEIXIN */
padding: 10rpx;
/* #endif */
/* #ifdef APP-PLUS */
padding: 20rpx;
/* #endif */
}
应用打包与发布
# 微信小程序发布流程
1. 在HBuilderX中选择"发行" → "小程序-微信"
2. 输入小程序名称和AppID
3. 点击发行,生成微信小程序代码包
4. 使用微信开发者工具打开生成的代码包
5. 在微信开发者工具中上传代码
6. 登录微信小程序后台,提交审核并发布
# APP打包流程
1. 在HBuilderX中选择"发行" → "原生App-云打包"
2. 选择打包平台(iOS/Android)
3. 配置证书和签名(Android使用自有证书,iOS需要苹果开发者账号)
4. 选择模块和权限配置
5. 点击打包,等待云端打包完成
6. 下载打包好的安装包进行分发
# H5发布流程
1. 在HBuilderX中选择"发行" → "网站-H5手机版"
2. 配置网站标题和域名
3. 点击发行,生成H5资源文件
4. 将生成的文件部署到服务器
性能优化与最佳实践
UniApp性能优化策略
- 图片优化:使用合适的图片格式和尺寸,懒加载图片
- 数据缓存:合理使用本地存储和Vuex状态管理
- 组件优化:使用v-if和v-show合理控制组件渲染
- 减少setData:避免频繁调用setData,合并数据更新
- 分包加载:使用分包机制减少主包体积
- 按需引入:组件和API按需引入,减少打包体积
分包配置示例
// pages.json
{
"pages": [
{
"path": "pages/index/index",
"style": { ... }
}
],
"subPackages": [
{
"root": "pagesA",
"pages": [
{
"path": "product/list",
"style": { ... }
},
{
"path": "product/detail",
"style": { ... }
}
]
},
{
"root": "pagesB",
"pages": [
{
"path": "user/center",
"style": { ... }
},
{
"path": "user/settings",
"style": { ... }
}
]
}
],
"preloadRule": {
"pages/index/index": {
"network": "all",
"packages": ["pagesA"]
}
}
}
总结
通过本教程,我们全面学习了UniApp的开发流程和核心技术:
- UniApp环境搭建和项目结构
- 核心配置文件和页面路由管理
- Vue.js在UniApp中的应用和状态管理
- 网络请求封装和API调用
- UI组件使用和自定义组件开发
- 多平台差异化处理和条件编译
- 应用打包发布和性能优化策略
UniApp作为跨平台开发框架,极大提高了开发效率,让开发者能够用一套代码构建多端应用。掌握这些技术将使你能够快速开发高质量的商业级应用。