一、uni-app 简介
1.1 核心特点
| 跨平台 | |
| 基于 Vue | |
| 组件化 | |
| 插件丰富 | |
| 性能优化 | |
| 条件编译 |
1.2 安装与创建
# 全局安装 Vue CLInpm install -g @vue/cli# 创建 uni-app 项目vue create -p dcloudio/uni-preset-vue my-project# 或者使用 HBuilderX(推荐)# 下载 HBuilderX,创建 uni-app 项目# 进入项目cd my-project# 运行到浏览器npm run dev:h5# 运行到微信小程序npm run dev:mp-weixin
1.3 项目结构
my-project/├── src/│ ├── pages/ # 页面文件│ │ ├── index/│ │ │ └── index.vue # 首页│ │ └── ...│ ├── components/ # 公共组件│ ├── static/ # 静态资源│ ├── utils/ # 工具函数│ ├── store/ # Vuex 状态管理│ ├── App.vue # 应用入口│ ├── main.js # 主入口│ ├── manifest.json # 应用配置文件│ └── pages.json # 页面路由配置├── unpackage/ # 打包输出目录├── package.json└── vue.config.js # Vue 配置
二、pages.json 路由配置
2.1 基本配置
{"pages": [{"path": "pages/index/index","style": {"navigationBarTitleText": "首页"}},{"path": "pages/user/user","style": {"navigationBarTitleText": "我的"}}],"globalStyle": {"navigationBarTextStyle": "black","navigationBarTitleText": "Uni-app","navigationBarBackgroundColor": "#F8F8F8","backgroundColor": "#F8F8F8"},"tabBar": {"color": "#7A7E83","selectedColor": "#3cc51f","borderStyle": "black","backgroundColor": "#ffffff","list": [{"pagePath": "pages/index/index","iconPath": "static/tab/home.png","selectedIconPath": "static/tab/home-active.png","text": "首页"},{"pagePath": "pages/user/user","iconPath": "static/tab/user.png","selectedIconPath": "static/tab/user-active.png","text": "我的"}]}}
三、页面生命周期
3.1 页面生命周期函数
<template><view><text>{{ message }}</text></view></template><script>export default {data() {return {message: 'Hello Uni-app'}},// 页面生命周期(按执行顺序)onLoad(options) {// 页面加载时触发,接收页面参数console.log('onLoad', options)// 可以在这里获取数据this.fetchData()},onShow() {// 页面显示时触发console.log('onShow')},onReady() {// 页面初次渲染完成时触发console.log('onReady')},onHide() {// 页面隐藏时触发console.log('onHide')},onUnload() {// 页面卸载时触发console.log('onUnload')},// 下拉刷新onPullDownRefresh() {console.log('下拉刷新')// 刷新数据this.refreshData()// 停止下拉刷新uni.stopPullDownRefresh()},// 上拉加载onReachBottom() {console.log('上拉加载更多')this.loadMore()},// 分享onShareAppMessage() {return {title: '分享标题',path: '/pages/index/index'}},// 页面滚动onPageScroll(e) {console.log('滚动距离:', e.scrollTop)},methods: {fetchData() {// 获取数据逻辑},refreshData() {// 刷新数据逻辑},loadMore() {// 加载更多逻辑}}}</script>
3.2 应用生命周期(App.vue)
<script>export default {onLaunch(options) {// 应用初始化时触发console.log('App Launch', options)// 可以在这里做登录检查},onShow(options) {// 应用从后台进入前台时触发console.log('App Show', options)},onHide() {// 应用从前台进入后台时触发console.log('App Hide')},onError(err) {// 应用发生错误时触发console.error('App Error:', err)}}</script>
四、常用组件
4.1 视图容器
<template><view><!-- 视图容器 --><viewclass="container"><text>文本内容</text></view><!-- 滚动视图 --><scroll-viewscroll-ystyle="height: 300px;"><viewv-for="item in list":key="item.id">{{ item.name }}</view></scroll-view><!-- 滑块视图(轮播图) --><swiperindicator-dotsautoplayinterval="3000"><swiper-itemv-for="img in images":key="img.id"><image:src="img.url"mode="aspectFill" /></swiper-item></swiper></view></template>
4.2 表单组件
<template><view><!-- 输入框 --><inputv-model="username"placeholder="请输入用户名"type="text"maxlength="20"/><!-- 文本域 --><textareav-model="desc"placeholder="请输入描述"maxlength="200" /><!-- 选择器 --><pickermode="selector":range="options" @change="onPickerChange"><view>{{ selected }}</view></picker><!-- 日期选择器 --><pickermode="date":value="date" @change="onDateChange"><view>{{ date }}</view></picker><!-- 开关 --><switch:checked="isChecked" @change="onSwitchChange" /><!-- 按钮 --><buttontype="primary" @click="handleSubmit">提交</button><buttontype="default" @click="handleCancel">取消</button></view></template>
4.3 媒体组件
<template><view><!-- 图片 --><imagesrc="/static/logo.png"mode="aspectFit"style="width: 200px; height: 200px;"@load="onImageLoad"@error="onImageError"/><!-- 视频 --><videosrc="https://example.com/video.mp4"controlsstyle="width: 100%; height: 300px;"@play="onVideoPlay"@pause="onVideoPause"/><!-- 地图 --><map:longitude="longitude":latitude="latitude"style="width: 100%; height: 300px;":markers="markers"/></view></template>
五、API 调用
5.1 网络请求
// GET 请求uni.request({url: 'https://api.example.com/users',method: 'GET',success: (res) => {console.log(res.data)this.userList = res.data},fail: (err) => {console.error(err)uni.showToast({title: '请求失败',icon: 'none'})}})// POST 请求uni.request({url: 'https://api.example.com/users',method: 'POST',data: {name: 'Alice',age: 25},header: {'Content-Type': 'application/json'},success: (res) => {console.log('创建成功', res.data)},fail: (err) => {console.error('创建失败', err)}})// 使用 Promiseconst request = (options) => {return new Promise((resolve, reject) => {uni.request({...options,success: (res) => resolve(res.data),fail: (err) => reject(err)})})}// 使用async fetchUsers() {try {const data = await request({url: 'https://api.example.com/users',method: 'GET'})this.userList = data} catch (err) {console.error(err)}}
5.2 提示框
// Toast 提示uni.showToast({title: '操作成功',icon: 'success',duration: 2000})// 确认框uni.showModal({title: '提示',content: '确认删除吗?',success: (res) => {if (res.confirm) {console.log('用户点击确定')this.deleteData()} else {console.log('用户点击取消')}}})// 加载框uni.showLoading({title: '加载中...',mask: true})// 隐藏加载框setTimeout(() => {uni.hideLoading()}, 2000)// 操作列表uni.showActionSheet({itemList: ['拍照', '从相册选择'],success: (res) => {console.log('选择了第', res.tapIndex + 1, '项')}})
5.3 导航跳转
// 跳转(保留当前页面)uni.navigateTo({url: '/pages/detail/detail?id=1'})// 重定向(关闭当前页面)uni.redirectTo({url: '/pages/login/login'})// 切换 Tabuni.switchTab({url: '/pages/user/user'})// 返回上一页uni.navigateBack({delta: 1 // 返回层数})// 带参数跳转uni.navigateTo({url: `/pages/detail/detail?id=${id}&name=${encodeURIComponent(name)}`})// 接收参数(在目标页面 onLoad 中)onLoad(options) {const id = options.idconst name = decodeURIComponent(options.name || '')}
5.4 本地存储
// 同步存储uni.setStorageSync('userInfo', { name: 'Alice', age: 25 })const userInfo = uni.getStorageSync('userInfo')uni.removeStorageSync('userInfo')// 异步存储uni.setStorage({key: 'token',data: 'abc123',success: () => {console.log('存储成功')}})uni.getStorage({key: 'token',success: (res) => {console.log('获取成功', res.data)}})// 清除所有uni.clearStorageSync()
5.5 设备信息
// 获取系统信息const systemInfo = uni.getSystemInfoSync()console.log('系统信息:', systemInfo)// platform: 'android' / 'ios' / 'devtools'// 获取网络状态uni.getNetworkType({success: (res) => {console.log('网络类型:', res.networkType)}})// 监听网络变化uni.onNetworkStatusChange((res) => {console.log('网络状态变化:', res.isConnected, res.networkType)})
六、条件编译
6.1 平台判断
<template><view><!-- #ifdef H5 --><view>这是 H5 页面</view><!-- #endif --><!-- #ifdef MP-WEIXIN --><view>这是微信小程序</view><!-- #endif --><!-- #ifdef APP-PLUS --><view>这是 App</view><!-- #endif --><!-- #ifndef H5 --><view>非 H5 平台显示</view><!-- #endif --></view></template><script>export default {methods: {// #ifdef H5h5Method() {console.log('H5 特有方法')},// #endif// #ifdef MP-WEIXINwxMethod() {console.log('微信小程序特有方法')},// #endif// 通用方法commonMethod() {// #ifdef H5this.h5Method()// #endif// #ifdef MP-WEIXINthis.wxMethod()// #endif}}}</script><style>/* 条件编译样式 *//* #ifdef H5 */.h5-style {color: red;}/* #endif *//* #ifdef MP-WEIXIN */.wx-style {color: green;}/* #endif */</style>
6.2 平台常量
// 判断平台const isH5 = process.env.UNI_PLATFORM === 'h5'const isWeixin = process.env.UNI_PLATFORM === 'mp-weixin'const isApp = process.env.UNI_PLATFORM === 'app-plus'// 使用if (isH5) {console.log('H5 平台')} else if (isWeixin) {console.log('微信小程序')}
七、Vuex 状态管理
7.1 创建 Store
// store/index.jsimport Vue from 'vue'import Vuex from 'vuex'Vue.use(Vuex)export default new Vuex.Store({state: {userInfo: null,token: '',isLogin: false},mutations: {SET_USER_INFO(state, userInfo) {state.userInfo = userInfo},SET_TOKEN(state, token) {state.token = tokenuni.setStorageSync('token', token)},SET_LOGIN(state, isLogin) {state.isLogin = isLogin}},actions: {// 登录login({ commit }, { username, password }) {return new Promise((resolve, reject) => {uni.request({url: 'https://api.example.com/login',method: 'POST',data: { username, password },success: (res) => {const { token, userInfo } = res.datacommit('SET_TOKEN', token)commit('SET_USER_INFO', userInfo)commit('SET_LOGIN', true)resolve(res.data)},fail: reject})})},// 登出logout({ commit }) {commit('SET_TOKEN', '')commit('SET_USER_INFO', null)commit('SET_LOGIN', false)uni.removeStorageSync('token')}},getters: {isLogin: state => state.isLogin,userInfo: state => state.userInfo,token: state => state.token}})
7.2 在页面中使用
<template><view><viewv-if="isLogin"><text>欢迎, {{ userInfo.name }}</text><button @click="handleLogout">退出登录</button></view><viewv-else><button @click="handleLogin">登录</button></view></view></template><script>import { mapState, mapActions, mapGetters } from 'vuex'export default {computed: {...mapState(['userInfo', 'isLogin']),...mapGetters(['token'])},methods: {...mapActions(['login', 'logout']),async handleLogin() {try {await this.login({username: 'admin',password: '123456'})uni.showToast({title: '登录成功',icon: 'success'})} catch (err) {uni.showToast({title: '登录失败',icon: 'none'})}},handleLogout() {this.logout()uni.showToast({title: '已退出',icon: 'success'})}}}</script>
八、常用工具函数
8.1 页面跳转工具
// utils/navigate.jsexport const navigateTo = (url, params = {}) => {const queryString = Object.keys(params).map(key => `${key}=${encodeURIComponent(params[key])}`).join('&')const fullUrl = queryString ? `${url}?${queryString}` : urluni.navigateTo({ url: fullUrl })}export const redirectTo = (url, params = {}) => {const queryString = Object.keys(params).map(key => `${key}=${encodeURIComponent(params[key])}`).join('&')const fullUrl = queryString ? `${url}?${queryString}` : urluni.redirectTo({ url: fullUrl })}export const switchTab = (url) => {uni.switchTab({ url })}export const navigateBack = (delta = 1) => {uni.navigateBack({ delta })}
8.2 日期格式化
// utils/date.jsexport const formatDate = (date, format = 'YYYY-MM-DD HH:mm:ss') => {const d = new Date(date)const year = d.getFullYear()const month = String(d.getMonth() + 1).padStart(2, '0')const day = String(d.getDate()).padStart(2, '0')const hours = String(d.getHours()).padStart(2, '0')const minutes = String(d.getMinutes()).padStart(2, '0')const seconds = String(d.getSeconds()).padStart(2, '0')return format.replace('YYYY', year).replace('MM', month).replace('DD', day).replace('HH', hours).replace('mm', minutes).replace('ss', seconds)}export const getRelativeTime = (timestamp) => {const now = Date.now()const diff = now - timestampif (diff < 60000) return '刚刚'if (diff < 3600000) return `${Math.floor(diff / 60000)}分钟前`if (diff < 86400000) return `${Math.floor(diff / 3600000)}小时前`if (diff < 604800000) return `${Math.floor(diff / 86400000)}天前`return formatDate(timestamp, 'YYYY-MM-DD')}
九、性能优化
| 减少页面层级 | redirectTo 替代 navigateTo |
| 图片懒加载 | lazy-load 属性 |
| 列表渲染优化 | v-for 添加 key,虚拟列表 |
| 防止内存泄漏 | onUnload 中清理定时器和事件监听 |
| 网络请求缓存 | |
| 减少重绘 | v-show 代替 v-if 频繁切换 |
| 代码分包 |
十、常用命令
# 安装依赖npm install# 运行到 H5npm run dev:h5# 运行到微信小程序npm run dev:mp-weixin# 运行到 Appnpm run dev:app-plus# 构建 H5npm run build:h5# 构建微信小程序npm run build:mp-weixin# 构建 Appnpm run build:app-plus# 使用 HBuilderX 开发(推荐)# 直接打开项目,点击运行即可
十一、总结
| 跨平台 | |
| Vue 语法 | |
| 原生渲染 | |
| 插件丰富 | |
| 条件编译 | |
| 性能优化 |
END
点击
上方文字
关注我们
夜雨聆风