UniApp学习知识总结与心得

探索 UniApp:从新手到实战的全方位学习心得

在移动应用开发领域不断演进的今天,跨平台开发框架成为众多开发者提高效率、降低成本的有力工具。UniApp 作为一款基于 Vue.js 的跨平台开发框架,以其 “一套代码,多端部署” 的特性脱颖而出,吸引了无数开发者深入探索。在系统学习 UniApp 并将其应用于多个项目的过程中,我积累了丰富的经验和深刻的见解,下面将与大家分享这段充实的学习历程。

一、初遇 UniApp:快速上手的惊喜

初次接触 UniApp,是在了解到它能极大简化多平台应用开发流程之后。当时,面对不同平台(如 iOS、Android、小程序、H5 等)需要分别编写代码的繁琐工作,UniApp 的出现就像一道曙光。其基于 Vue.js 的语法体系,对于有 Vue 基础的开发者来说,几乎没有学习成本。

(一)熟悉的语法,快速搭建基础项目

UniApp 的模板语法与 Vue 高度相似,这使得我在创建第一个 UniApp 项目时就倍感亲切。以一个简单的待办事项应用为例,在模板(<template>)部分,使用熟悉的 HTML 标签结合 Vue 指令来构建页面结构:

<template>
  <view class="container">
    <view class="input-section">
      <input v-model="newTodo" placeholder="添加待办事项"/>
      <button @click="addTodo">添加</button>
    </view>
    <view class="todo-list">
      <view v-for="(todo, index) in todos" :key="index" class="todo-item">
        <checkbox v-model="todo.completed" />
        <text :class="{ completed: todo.completed }">{{ todo.text }}</text>
        <button @click="deleteTodo(index)">删除</button>
      </view>
    </view>
  </view>
</template>

在这段代码中,v-model实现了数据的双向绑定,方便获取和更新用户输入的待办事项内容;v-for指令用于循环渲染待办事项列表,每个事项都有对应的唯一key,这是 Vue 列表渲染的最佳实践,有助于提高渲染效率;@click指令绑定了点击事件,分别实现添加、删除待办事项以及切换事项完成状态的功能。

(二)数据驱动的开发模式,轻松管理状态

<script>部分,定义数据和方法同样遵循 Vue 的响应式原理。

<script>
export default {
  data() {
    return {
      newTodo: '',
      todos: []
    };
  },
  methods: {
    addTodo() {
      if (this.newTodo.trim()!== '') {
        this.todos.push({
          text: this.newTodo,
          completed: false
        });
        this.newTodo = '';
      }
    },
    deleteTodo(index) {
      this.todos.splice(index, 1);
    }
  }
};
</script>

data函数返回的对象中的属性会被 Vue 自动追踪其变化,当数据发生改变时,视图会自动更新。比如在addTodo方法中,向this.todos数组添加新的待办事项后,页面上的待办事项列表会立即更新显示新增内容。这种数据驱动的开发模式,让开发者专注于数据的处理和业务逻辑的实现,而无需手动操作 DOM 来更新视图,大大提高了开发效率和代码的可维护性。

(三)便捷的样式编写,适配多端需求

在样式(<style>)方面,UniApp 支持使用 CSS、SCSS、LESS 等多种样式语言。通过合理运用scoped属性,可以确保组件的样式只在当前组件内生效,避免样式冲突。

<style scoped>
.container {
  padding: 20px;
}
.input-section {
  display: flex;
  margin-bottom: 20px;
}
.input-section input {
  flex: 1;
  height: 40px;
  padding: 0 10px;
  border: 1px solid #ccc;
  border-radius: 5px 0 0 5px;
}
.input-section button {
  width: 100px;
  height: 40px;
  border: none;
  background-color: #1aad19;
  color: white;
  border-radius: 0 5px 5px 0;
}
.todo-list {
  list-style-type: none;
  padding: 0;
}
.todo-item {
  display: flex;
  align-items: center;
  margin-bottom: 10px;
}
.todo-item checkbox {
  margin-right: 10px;
}
.todo-item.completed {
  text-decoration: line-through;
  color: #999;
}
.todo-item button {
  margin-left: auto;
  background-color: #ff5722;
  color: white;
  border: none;
  width: 60px;
  height: 30px;
  border-radius: 5px;
}
</style>

通过这样的样式设置,在不同平台上都能呈现出较为一致的视觉效果。同时,UniApp 还提供了一些平台特定的样式类,方便针对不同平台进行微调,以适应各平台的设计规范和用户习惯。

通过这个简单的待办事项应用,我初步体验到了 UniApp 开发的便捷性和高效性,也为后续深入学习和实践奠定了坚实的基础。

二、深入 UniApp:跨平台开发的魅力与挑战

随着学习的深入,我开始探索 UniApp 在跨平台开发方面的强大功能,同时也遇到了一些挑战。

(一)跨平台开发的强大功能

  1. 一套代码,多端部署:UniApp 最大的优势之一就是能够使用一套代码构建出适用于多个平台的应用。在完成上述待办事项应用的开发后,我只需简单配置manifest.json文件,就能将应用打包成小程序、APP(通过 HBuilderX 的云打包功能)以及在 H5 端运行。这意味着我无需为每个平台单独编写代码,大大节省了开发时间和精力。
  2. 丰富的组件和 API:UniApp 提供了丰富的组件库,涵盖了各种常见的 UI 组件(如按钮、输入框、列表、导航栏等)以及功能性组件(如地图、摄像头、音频 / 视频播放等)。这些组件在不同平台上具有统一的使用方式,并且能够自动适配各平台的特性。例如,使用uni - map组件在小程序、APP 和 H5 端都能快速实现地图展示功能,开发者无需关注不同平台地图 API 的差异。同时,UniApp 还封装了大量的原生 API,方便调用设备的各种功能,如调用uni.getLocation获取设备位置信息,在各平台上都能轻松实现定位功能。

(二)跨平台开发中的挑战及解决方法

  1. 平台差异带来的兼容性问题:尽管 UniApp 努力实现各平台的统一开发体验,但不同平台之间仍存在一些差异,这可能导致兼容性问题。例如,在小程序端和 APP 端,某些 CSS 属性的支持程度不同。在小程序中,transform属性的某些值可能无法正常显示,而在 APP 中则可能存在性能问题。解决这类问题需要查阅 UniApp 官方文档中关于各平台差异的说明,针对不同平台进行特定的样式调整。可以使用@platform指令,在样式文件中针对特定平台编写不同的样式代码:
/* 针对小程序平台的样式 */
@platform mp {
 .todo-item {
    padding: 10px;
  }
}
/* 针对APP平台的样式 */
@platform app {
 .todo-item {
    padding: 15px;
  }
}
  1. 性能优化问题:在多端部署的过程中,性能优化是一个关键问题。不同平台对性能的要求和表现各不相同,例如小程序有严格的代码体积限制,APP 则对页面加载速度和流畅度要求较高。为了优化性能,我学会了一些技巧。在代码层面,避免在组件的createdmounted钩子函数中执行过多的复杂操作,尽量使用异步加载数据的方式,减少页面初始化时间。对于图片等资源,进行合理的压缩和优化,避免加载过大的文件。同时,利用 UniApp 提供的性能分析工具,如 HBuilderX 中的真机调试和性能监控功能,定位性能瓶颈并进行针对性优化。

三、实战项目:UniApp 在电商 APP 开发中的应用

为了更深入地掌握 UniApp,我参与了一个电商 APP 的开发项目。这个项目涵盖了商品展示、购物车管理、订单结算、用户中心等多个核心功能模块,充分考验了我对 UniApp 的理解和运用能力。

(一)商品展示模块:数据获取与渲染

在商品展示模块中,需要从后端服务器获取商品数据,并在页面上进行展示。使用uni.request方法向服务器发起 HTTP 请求获取商品列表数据:

获取到数据后,在模板中使用v - for指令进行循环渲染:

<template>
  <view class="goods - list">
    <view v - for="(goods, index) in goodsList" :key="index" class="goods - item">
      <image :src="goods.imageUrl" mode="aspectFill" class="goods - image" />
      <view class="goods - info">
        <text class="goods - title">{{ goods.title }}</text>
        <text class="goods - price">¥{{ goods.price }}</text>
      </view>
    </view>
  </view>
</template>

为了提高用户体验,还添加了下拉刷新和上拉加载更多的功能。利用 UniApp 提供的onPullDownRefreshonReachBottom生命周期函数实现:

export default {
  data() {
    return {
      goodsList: [],
      page: 1,
      pageSize: 10
    };
  },
  onPullDownRefresh() {
    this.page = 1;
    this.getGoodsList();
    uni.stopPullDownRefresh();
  },
  onReachBottom() {
    this.page++;
    this.getGoodsList();
  },
  methods: {
    getGoodsList() {
      uni.request({
        url: `https://example.com/api/goods?page=${this.page}&pageSize=${this.pageSize}`,
        method: 'GET',
        success: (res) => {
          if (res.statusCode === 200) {
            if (this.page === 1) {
              this.goodsList = res.data;
            } else {
              this.goodsList = [...this.goodsList, ...res.data];
            }
          }
        },
        fail: (err) => {
          console.error('获取商品列表失败', err);
        }
      });
    }
  }
};

(二)购物车模块:数据存储与交互

购物车模块涉及到数据的存储和复杂的交互逻辑。使用uni.setStorageSyncuni.getStorageSync方法在本地存储购物车数据,确保用户在关闭应用后再次打开时购物车内容依然存在。

methods: {
  addToCart(goods) {
    let cart = uni.getStorageSync('cart') || [];
    let existGoods = cart.find(c => c.id === goods.id);
    if (existGoods) {
      existGoods.quantity++;
    } else {
      goods.quantity = 1;
      cart.push(goods);
    }
    uni.setStorageSync('cart', cart);
    this.$emit('cart - updated');
  },
  updateCartItemQuantity(goods, quantity) {
    let cart = uni.getStorageSync('cart');
    let index = cart.findIndex(c => c.id === goods.id);
    if (index!== -1) {
      cart[index].quantity = quantity;
      if (quantity <= 0) {
        cart.splice(index, 1);
      }
      uni.setStorageSync('cart', cart);
      this.$emit('cart - updated');
    }
  },
  getCartList() {
    return uni.getStorageSync('cart') || [];
  }
}

在模板中,根据购物车数据进行渲染,并实现全选、反选、计算总价等功能:

<template>
  <view class="cart - container">
    <view class="cart - list">
      <view v - for="(cartItem, index) in cartList" :key="index" class="cart - item">
        <checkbox v - model="cartItem.checked" />
        <image :src="cartItem.imageUrl" mode="aspectFill" class="cart - item - image" />
        <view class="cart - item - info">
          <text class="cart - item - title">{{ cartItem.title }}</text>
          <text class="cart - item - price">¥{{ cartItem.price }}</text>
          <view class="quantity - control">
            <button @click="updateCartItemQuantity(cartItem, cartItem.quantity - 1)" :disabled="cartItem.quantity <= 1">-</button>
            <input v - model="cartItem.quantity" type="number" class="quantity - input" />
            <button @click="updateCartItemQuantity(cartItem, cartItem.quantity + 1)">+</button>
          </view>
        </view>
      </view>
    </view>
    <view class="cart - footer">
      <checkbox v - model="isAllChecked" @change="toggleAllChecked" />全选
      <text class="total - price">总价:¥{{ totalPrice }}</text>
      <button @click="submitOrder">去结算</button>
    </view>
  </view>
</template>
export default {
  data() {
    return {
      cartList: [],
      isAllChecked: false
    };
  },
  computed: {
    totalPrice() {
      return this.cartList.filter(c => c.checked).reduce((sum, cartItem) => sum + cartItem.price * cartItem.quantity, 0);
    }
  },
  methods: {
    toggleAllChecked() {
      this.cartList.forEach(cartItem => {
        cartItem.checked = this.isAllChecked;
      });
      uni.setStorageSync('cart', this.cartList);
    },
    submitOrder() {
      // 提交订单逻辑,这里省略实际接口调用
      console.log('提交订单', this.cartList.filter(c => c.checked));
    },
    onLoad() {
      this.cartList = this.getCartList();
    }
  }
};

(三)订单结算与用户中心模块:页面跳转与数据交互

在订单结算模块,需要将购物车中选中的商品信息传递到结算页面,并进行订单的生成和提交操作。使用uni.navigateTo方法进行页面跳转,并通过url参数传递数据:

<!-- 购物车页面 -->
<button @click="submitOrder">去结算</button>
// 购物车页面的submitOrder方法
submitOrder() {
  let selectedGoods = this.cartList.filter(c => c.checked);
  let orderInfo = {
    goods: selectedGoods,
    totalPrice: this.totalPrice
  };
  uni.navigateTo({
    url: `/pages/order/order - confirm?orderInfo=${encodeURIComponent(JSON.stringify(orderInfo))}`
  });
}

在订单确认页面,通过onLoad生命周期函数获取传递过来的数据:

<template>
  <view class="order - confirm - container">
    <view class="order - goods - list">
      <view v - for="(goods, index) in orderGoods" :key="index" class="order - goods - item">
        <image :src="goods.imageUrl" mode="aspectFill" class="order - goods - image" />
        <view class="order - goods - info">
          <text class="order - goods - title">{{ goods.title }}</text>
          <text class="order - goods - price">¥{{ goods.price }}</text>
          <text class="order - goods - quantity">数量:{{ goods.quantity }}</text>
        </view>
      </view>
    </view>
    <view class="order - total - price">总价:¥{{ orderTotalPrice }}</view>
    <button @click="submitOrder">提交订单</button>
  </view>
</template>
export default {
  data() {
    return {
      orderGoods: [],
      orderTotalPrice: 0
    };
  },
  onLoad(option) {
    if (option.orderInfo) {
      let orderInfo = JSON.parse(decodeURIComponent(option.orderInfo));
      this.orderGoods = orderInfo.goods;
      this.orderTotalPrice = orderInfo.totalPrice;
    }
  },
  methods: {

四,优化 UniApp 项目性能

  1. 代码层面优化
    • 合理使用生命周期钩子函数:避免在createdmounted钩子函数中执行过多复杂操作,因为这些操作会在组件创建或挂载时同步执行,可能导致页面加载卡顿。例如,数据请求尽量使用异步方式,可在created钩子函数中发起异步请求,而不是在该函数内处理大量数据计算。
    • 优化数据绑定:减少不必要的数据绑定,避免在模板中使用复杂的表达式。因为数据绑定会增加 Vue.js 的响应式追踪开销。如可以在计算属性中提前处理复杂逻辑,再在模板中使用计算属性。
    • 使用计算属性和监听器优化数据处理:对于依赖其他数据的动态数据,使用计算属性而不是在模板中重复计算。计算属性会缓存结果,只有在依赖数据变化时才会重新计算。而对于需要监听数据变化并执行副作用操作的场景,合理使用监听器,避免频繁触发不必要的操作。
  2. 资源管理优化
    • 图片优化:对图片资源进行压缩处理,降低图片文件大小,减少加载时间。可以使用图像编辑工具或在线压缩工具,在不影响图片质量的前提下减小文件体积。同时,根据不同平台和设备的分辨率,选择合适尺寸的图片,避免加载过大的图片资源。例如,在高清屏幕设备上使用高清图片,在普通屏幕设备上使用标准清晰度图片。
    • 字体优化:减少字体文件的使用,如果必须使用自定义字体,选择精简的字体子集,去除不必要的字符集。同时,对字体文件进行压缩,降低文件大小。
    • 代码分包:对于小程序平台,将代码按功能模块进行分包。可以将常用的基础包和不常用的功能模块分别打包,在小程序启动时只加载基础包,用户使用到特定功能时再加载对应的分包,从而加快小程序的启动速度。在 UniApp 项目中,可以通过配置pages.json文件来实现代码分包。
  3. 组件使用优化
    • 避免频繁渲染不必要的组件:对于一些不经常变化的组件,使用v-if指令而不是v-show指令。v-show是通过 CSS 的display属性来控制组件的显示和隐藏,无论初始状态如何,组件都会被渲染;而v-if是根据条件判断是否渲染组件,在条件不满足时不会渲染组件,从而减少不必要的渲染开销。
    • 组件复用与缓存:对于频繁使用的组件,进行复用和缓存。可以使用keep-alive组件包裹需要缓存的组件,keep-alive会缓存组件的状态,当组件被切换时,不会重新创建和销毁组件,而是保留其状态,提高组件的切换效率。例如,在一个多页面应用中,对于经常切换的表单页面组件,可以使用keep-alive进行缓存。
  4. 页面加载和渲染优化
    • 懒加载:对于列表页面中的图片、组件等元素,使用懒加载技术。在元素进入视口时才进行加载,避免一次性加载大量资源,提高页面的初始加载速度。在 UniApp 中,可以使用uni-lazyload组件或自定义指令来实现图片的懒加载。
    • 减少页面层级嵌套:页面布局中尽量减少组件的层级嵌套,因为层级嵌套过多会增加渲染复杂度和时间。可以通过合理设计页面结构,将复杂的布局拆分成多个简单的组件,降低渲染难度。
    • 使用虚拟列表:对于长列表数据的展示,使用虚拟列表技术。虚拟列表只渲染当前视口内的列表项,当用户滚动列表时,动态加载新的列表项,避免一次性渲染大量列表项导致的性能问题。UniApp 社区有一些虚拟列表组件可供使用,如vue-virtual-scroll-list,可以引入并适配到 UniApp 项目中。
  5. 性能监控与分析
    • 使用开发工具的性能分析功能:利用 HBuilderX 等开发工具提供的性能分析功能,如真机调试中的性能监控面板,可以查看页面加载时间、渲染帧率、内存使用等性能指标。通过分析这些指标,定位性能瓶颈所在,有针对性地进行优化。
    • 用户反馈与数据分析:收集用户反馈,分析应用在实际使用中的性能问题。可以通过设置错误上报机制,收集用户使用过程中的异常信息和性能数据,进一步优化应用性能。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值