乐于分享
好东西不私藏

一文吃透 uni-app 目录结构,组件、页面基础开发

一文吃透 uni-app 目录结构,组件、页面基础开发
      uni-app 是一个使用 Vue.js 开发所有前端应用的框架,开发者编写一套代码,可发布到 iOS、Android、Web(响应式)、以及各种小程序(微信/支付宝/百度/头条/QQ/快手/钉钉/淘宝)、快应用等多个平台。

一、uni-app 简介

1.1 核心特点

 特点
说明
 跨平台
一套代码,多端发布(iOS、Android、H5、小程序、快应用)
 基于 Vue
使用 Vue.js 语法,学习成本低
 组件化
支持自定义组件,复用性强
 插件丰富
官方和社区提供大量插件
 性能优化
原生渲染,接近原生体验
 条件编译
可针对不同平台编写差异化代码

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>    <!-- 输入框 -->    <input      v-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>    <!-- 图片 -->    <image      src="/static/logo.png"      mode="aspectFit"      style="width: 200px; height: 200px;"      @load="onImageLoad"      @error="onImageError"    />    <!-- 视频 -->    <video      src="https://example.com/video.mp4"      controls      style="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',    age25  },  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',  duration2000})// 确认框uni.showModal({  title'提示',  content'确认删除吗?',  success(res) => {    if (res.confirm) {      console.log('用户点击确定')      this.deleteData()    } else {      console.log('用户点击取消')    }  }})// 加载框uni.showLoading({  title'加载中...',  masktrue})// 隐藏加载框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({  delta1 // 返回层数})// 带参数跳转uni.navigateTo({  url`/pages/detail/detail?id=${id}&name=${encodeURIComponent(name)}`})// 接收参数(在目标页面 onLoad 中)onLoad(options) {  const id = options.id  const name = decodeURIComponent(options.name || '')}

5.4 本地存储

// 同步存储uni.setStorageSync('userInfo', { name'Alice', age25 })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 H5    h5Method() {      console.log('H5 特有方法')    },    // #endif    // #ifdef MP-WEIXIN    wxMethod() {      console.log('微信小程序特有方法')    },    // #endif    // 通用方法    commonMethod() {      // #ifdef H5      this.h5Method()      // #endif      // #ifdef MP-WEIXIN      this.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: {    userInfonull,    token'',    isLoginfalse  },  mutations: {    SET_USER_INFO(state, userInfo) {      state.userInfo = userInfo    },    SET_TOKEN(state, token) {      state.token = token      uni.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.data            commit('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: {    isLoginstate => state.isLogin,    userInfostate => state.userInfo,    tokenstate => 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}` : url  uni.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}` : url  uni.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 - timestamp  if (diff < 60000return '刚刚'  if (diff < 3600000return `${Math.floor(diff / 60000)}分钟前`  if (diff < 86400000return `${Math.floor(diff / 3600000)}小时前`  if (diff < 604800000return `${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

点击

上方文字

关注我们