阐述 UniApp 在多端 `Web`、`小程序` 和 `App` 之间,如何实现代码复用和版本管理。

各位老铁,早上好啊!今天咱们唠唠 UniApp 这个 “万能选手”,看看它怎么在 Web、小程序、App 之间玩转代码复用和版本管理,让咱们开发效率蹭蹭往上涨。

开场白:UniApp 是个啥?

简单来说,UniApp 就是一个使用 Vue.js 语法开发多端应用的框架。你写一套代码,它就能编译成 Web 页面、微信/支付宝/百度小程序、iOS/Android App。是不是听着就很省心?

代码复用:一份代码,多处开花

UniApp 的核心思想就是 “Write Once, Run Everywhere” (一次编写,到处运行)。它是怎么做到的呢?主要靠以下几个法宝:

  1. 组件化开发:
    • UniApp 基于 Vue.js,所以组件化是基本操作。咱们把页面拆分成一个个独立的组件,比如 Header、Footer、List Item 等等。这些组件可以在不同的页面和平台上复用。
    • 举个例子,假设咱们有个通用的按钮组件 MyButton.vue:
<template>
  <button class="my-button" @click="handleClick">{{ text }}</button>
</template>

<script>
export default {
  props: {
    text: {
      type: String,
      default: '按钮'
    }
  },
  methods: {
    handleClick() {
      this.$emit('click'); // 触发点击事件,父组件可以监听
    }
  }
};
</script>

<style scoped>
.my-button {
  background-color: #409EFF;
  color: white;
  padding: 10px 20px;
  border: none;
  border-radius: 5px;
  cursor: pointer;
}
</style>
*   这个 `MyButton` 组件可以在 Web 页面、小程序和 App 中直接使用,只需要在需要的地方引入即可。
  1. 条件编译:
    • 不同平台总有些差异,比如 API 不一样,或者 UI 展现形式不一样。这时候就需要用到条件编译。UniApp 允许咱们针对不同的平台编写不同的代码。
    • 条件编译使用 #ifdef#ifndef 指令,根据不同的平台标识来决定是否编译某段代码。
<template>
  <view>
    <text>Hello UniApp</text>
    <!-- #ifdef MP-WEIXIN -->
    <button @click="navigateToMiniProgram">跳转到小程序</button>
    <!-- #endif -->
    <!-- #ifdef H5 -->
    <button @click="navigateToWebPage">跳转到网页</button>
    <!-- #endif -->
  </view>
</template>

<script>
export default {
  methods: {
    navigateToMiniProgram() {
      // 小程序跳转逻辑
      uni.navigateTo({
        url: '/pages/index/index'
      });
    },
    navigateToWebPage() {
      // Web 页面跳转逻辑
      window.location.href = 'https://www.example.com';
    }
  }
};
</script>
*   在这个例子中,`#ifdef MP-WEIXIN` 包裹的代码只会编译到微信小程序中,`#ifdef H5` 包裹的代码只会编译到 Web 页面中。
*   常用的平台标识:
    *   `H5`: Web 页面
    *   `MP-WEIXIN`: 微信小程序
    *   `MP-ALIPAY`: 支付宝小程序
    *   `MP-BAIDU`: 百度小程序
    *   `APP-PLUS`: App (原生渲染)
    *   `APP-PLUS-NVUE`: App (Weex 渲染)
  1. 平台特有 API:
    • UniApp 提供了一套统一的 API,尽量抹平了不同平台的差异。但是,有些平台特有的 API 还是需要单独处理。
    • 比如,微信小程序有 wx.getUserInfo 获取用户信息,而 Web 页面可能需要用其他方式实现。
    • 咱们可以使用 uni.getSystemInfo 获取平台信息,然后根据平台信息来调用不同的 API。
uni.getSystemInfo({
  success: (res) => {
    if (res.platform === 'devtools') {
      // 开发者工具
    } else if (res.platform === 'android') {
      // Android
    } else if (res.platform === 'ios') {
      // iOS
    }
  }
});
  1. UI 适配:
    • 不同平台的 UI 规范不一样,比如字体大小、颜色、间距等等。UniApp 提供了 uni-app 内置的组件库,这些组件在不同平台上的表现基本一致。
    • 当然,如果需要更精细的 UI 控制,可以使用 CSS 样式,并结合条件编译来针对不同平台进行适配。

版本管理:让代码井然有序

版本管理是软件开发中非常重要的一环。UniApp 项目的版本管理主要涉及到以下几个方面:

  1. 代码版本管理 (Git):

    • 这是最基础的版本管理方式。使用 Git 可以跟踪代码的修改历史,方便回滚和协作开发。
    • 常用的 Git 命令:
      • git init: 初始化 Git 仓库
      • git add .: 添加所有文件到暂存区
      • git commit -m "提交信息": 提交代码
      • git push origin main: 推送代码到远程仓库 (GitHub, GitLab 等)
      • git pull origin main: 从远程仓库拉取代码
      • git branch: 查看分支
      • git checkout -b feature/new-feature: 创建并切换到新的分支
      • git merge feature/new-feature: 合并分支
  2. 应用版本号:

    • 每个应用都需要一个版本号,用来区分不同的版本。UniApp 项目的版本号在 manifest.json 文件中配置。
{
  "versionName": "1.0.0",
  "versionCode": "100"
}
*   `versionName` 是给用户看的版本号,比如 "1.0.0"。
*   `versionCode` 是给程序用的版本号,必须是整数,每次发布新版本都要递增。
*   在 App 中,`versionName` 会显示在应用信息页面,`versionCode` 用于应用升级判断。
*   在小程序中,`versionName` 和 `versionCode` 也会用于版本管理和更新。
  1. 平台差异化版本管理:

    • 有时候,不同的平台需要发布不同的版本,比如 Web 版可能需要修复一个 Bug,而 App 版不需要。
    • 这时候,可以利用 Git 的分支管理功能,为每个平台创建一个独立的分支。
    • 比如,可以创建 web 分支、app 分支、mp-weixin 分支等等。
    • 然后在每个分支上进行相应的修改和发布。
    • 这种方式可以保证每个平台的代码都是独立的,不会互相影响。
  2. 发布流程自动化:

    • 手动发布应用非常繁琐,容易出错。可以考虑使用自动化工具来简化发布流程。
    • 常用的自动化工具:
      • Jenkins: 一个开源的自动化服务器,可以配置各种构建和发布任务。
      • Fastlane: 一个专门用于 iOS 和 Android 应用发布的工具,可以自动打包、签名、上传应用。
      • GitHub Actions: GitHub 提供的 CI/CD 服务,可以直接在 GitHub 仓库中配置自动化流程。
    • 使用自动化工具,可以实现代码提交后自动构建、测试、打包、发布应用,大大提高效率。

UniApp 项目结构:代码复用的基石

一个良好的项目结构是代码复用的基础。UniApp 官方推荐的项目结构如下:

├── components           # 公共组件
│   ├── MyButton.vue
│   └── ...
├── pages                # 页面
│   ├── index
│   │   ├── index.vue
│   │   └── ...
│   └── ...
├── static               # 静态资源
│   ├── images
│   │   ├── logo.png
│   │   └── ...
│   └── ...
├── App.vue              # 应用入口
├── main.js              # 应用配置
├── manifest.json        # 应用清单
├── pages.json           # 页面路由配置
└── uni.scss             # 全局样式
  • components 目录存放公共组件,可以在不同的页面中复用。
  • pages 目录存放页面文件,每个页面一个目录。
  • static 目录存放静态资源,比如图片、字体等等。
  • App.vue 是应用的入口组件,类似于 Vue 项目的 App.vue
  • main.js 是应用的入口文件,用于初始化 Vue 实例、注册全局组件、配置 Vuex 等等。
  • manifest.json 是应用的清单文件,用于配置应用的基本信息,比如应用名称、版本号、图标等等。
  • pages.json 是页面的路由配置文件,用于配置页面的路径、标题、导航栏样式等等。
  • uni.scss 是全局样式文件,可以在这里定义全局的样式变量和样式规则。

实战演练:一个简单的 TodoList 应用

咱们来做一个简单的 TodoList 应用,演示 UniApp 的代码复用和版本管理。

  1. 创建项目:
    • 使用 vue-cli 创建 UniApp 项目:
vue create -p dcloudio/uni-preset-5 my-todo-app
*   选择一个模板,比如 "Hello UniApp"。
  1. 编写代码:
    • pages/index/index.vue:
<template>
  <view class="container">
    <view class="header">
      <input type="text" class="input" placeholder="添加任务" v-model="newTask" @confirm="addTask" />
      <button class="add-button" @click="addTask">添加</button>
    </view>
    <view class="list">
      <view class="item" v-for="(item, index) in todoList" :key="index">
        <checkbox :checked="item.completed" @change="toggleComplete(index)" />
        <text class="text" :class="{ completed: item.completed }">{{ item.text }}</text>
        <button class="delete-button" @click="deleteTask(index)">删除</button>
      </view>
    </view>
  </view>
</template>

<script>
export default {
  data() {
    return {
      newTask: '',
      todoList: [
        { text: '学习 UniApp', completed: false },
        { text: '完成 TodoList', completed: true }
      ]
    };
  },
  methods: {
    addTask() {
      if (this.newTask.trim() === '') {
        return;
      }
      this.todoList.push({ text: this.newTask, completed: false });
      this.newTask = '';
    },
    toggleComplete(index) {
      this.todoList[index].completed = !this.todoList[index].completed;
    },
    deleteTask(index) {
      this.todoList.splice(index, 1);
    }
  }
};
</script>

<style scoped>
.container {
  padding: 20px;
}

.header {
  display: flex;
  margin-bottom: 20px;
}

.input {
  flex: 1;
  padding: 10px;
  border: 1px solid #ccc;
  border-radius: 5px;
}

.add-button {
  padding: 10px 20px;
  background-color: #409EFF;
  color: white;
  border: none;
  border-radius: 5px;
  margin-left: 10px;
  cursor: pointer;
}

.list {
  border-top: 1px solid #ccc;
  padding-top: 20px;
}

.item {
  display: flex;
  align-items: center;
  margin-bottom: 10px;
}

.text {
  flex: 1;
  margin-left: 10px;
}

.completed {
  text-decoration: line-through;
  color: #999;
}

.delete-button {
  padding: 5px 10px;
  background-color: #F56C6C;
  color: white;
  border: none;
  border-radius: 5px;
  margin-left: 10px;
  cursor: pointer;
}
</style>
  1. 运行项目:
    • 运行到 Web 页面:
npm run dev:h5
*   运行到微信小程序:
npm run dev:mp-weixin
*   运行到 App:
npm run dev:app-plus
*   你会发现,同一份代码,可以在不同的平台上运行,而且 UI 表现基本一致。
  1. 条件编译:
    • 假设需要在微信小程序中添加一个分享按钮:
<template>
  <view class="container">
    <!-- ... 其他代码 ... -->
    <!-- #ifdef MP-WEIXIN -->
    <button @click="share">分享</button>
    <!-- #endif -->
  </view>
</template>

<script>
export default {
  // ... 其他代码 ...
  methods: {
    // ... 其他代码 ...
    share() {
      // 微信小程序分享逻辑
      uni.share({
        provider: 'weixin',
        type: 0,
        success: function (res) {
          console.log('share success');
        },
        fail: function (err) {
          console.log('share fail', err);
        }
      });
    }
  }
};
</script>
*   这段代码只会编译到微信小程序中,在其他平台上不会显示分享按钮。
  1. 组件化:
    • 将添加任务的输入框和按钮封装成一个组件 AddTask.vue:
<template>
  <view class="header">
    <input type="text" class="input" placeholder="添加任务" v-model="newTask" @confirm="addTask" />
    <button class="add-button" @click="addTask">添加</button>
  </view>
</template>

<script>
export default {
  data() {
    return {
      newTask: ''
    };
  },
  methods: {
    addTask() {
      if (this.newTask.trim() === '') {
        return;
      }
      this.$emit('add', this.newTask); // 触发 add 事件,传递 newTask
      this.newTask = '';
    }
  }
};
</script>

<style scoped>
.header {
  display: flex;
  margin-bottom: 20px;
}

.input {
  flex: 1;
  padding: 10px;
  border: 1px solid #ccc;
  border-radius: 5px;
}

.add-button {
  padding: 10px 20px;
  background-color: #409EFF;
  color: white;
  border: none;
  border-radius: 5px;
  margin-left: 10px;
  cursor: pointer;
}
</style>
*   然后在 `pages/index/index.vue` 中使用该组件:
<template>
  <view class="container">
    <AddTask @add="addTask" />  <!-- 使用 AddTask 组件 -->
    <view class="list">
      <view class="item" v-for="(item, index) in todoList" :key="index">
        <checkbox :checked="item.completed" @change="toggleComplete(index)" />
        <text class="text" :class="{ completed: item.completed }">{{ item.text }}</text>
        <button class="delete-button" @click="deleteTask(index)">删除</button>
      </view>
    </view>
    <!-- #ifdef MP-WEIXIN -->
    <button @click="share">分享</button>
    <!-- #endif -->
  </view>
</template>

<script>
import AddTask from '../../components/AddTask.vue'; // 引入 AddTask 组件

export default {
  components: {
    AddTask // 注册 AddTask 组件
  },
  data() {
    return {
      todoList: [
        { text: '学习 UniApp', completed: false },
        { text: '完成 TodoList', completed: true }
      ]
    };
  },
  methods: {
    addTask(newTask) {
      // 接收 AddTask 组件传递的 newTask
      this.todoList.push({ text: newTask, completed: false });
    },
    toggleComplete(index) {
      this.todoList[index].completed = !this.todoList[index].completed;
    },
    deleteTask(index) {
      this.todoList.splice(index, 1);
    },
    share() {
      // 微信小程序分享逻辑
      uni.share({
        provider: 'weixin',
        type: 0,
        success: function (res) {
          console.log('share success');
        },
        fail: function (err) {
          console.log('share fail', err);
        }
      });
    }
  }
};
</script>

<style scoped>
.container {
  padding: 20px;
}

.list {
  border-top: 1px solid #ccc;
  padding-top: 20px;
}

.item {
  display: flex;
  align-items: center;
  margin-bottom: 10px;
}

.text {
  flex: 1;
  margin-left: 10px;
}

.completed {
  text-decoration: line-through;
  color: #999;
}

.delete-button {
  padding: 5px 10px;
  background-color: #F56C6C;
  color: white;
  border: none;
  border-radius: 5px;
  margin-left: 10px;
  cursor: pointer;
}
</style>
  1. 版本管理:
    • 将项目初始化为 git 仓库。
    • 然后分别创建 webmp-weixinapp 分支,然后根据需要在每个分支上进行修改。
    • 修改完成后,合并到主分支。

总结:UniApp 的优势与不足

  • 优势:
    • 代码复用率高: 一套代码可以编译成多个平台的应用,大大减少了开发成本。
    • 学习成本低: 基于 Vue.js,熟悉 Vue.js 的开发者可以快速上手。
    • 开发效率高: 丰富的组件库和 API,可以快速构建应用。
    • 跨平台能力强: 支持 Web、小程序、App 等多个平台。
  • 不足:
    • 性能: 在某些场景下,性能可能不如原生应用。
    • 平台特性: 有些平台特有的功能可能无法完全支持。
    • 生态: 相比原生应用,UniApp 的生态还不够完善。

彩蛋:一些实用技巧

  • 使用 HBuilderX 开发: HBuilderX 是 UniApp 官方推荐的开发工具,集成了 UniApp 的各种功能,可以提高开发效率。
  • 善用插件市场: UniApp 插件市场有很多第三方插件,可以扩展 UniApp 的功能。
  • 多看官方文档: UniApp 官方文档非常详细,包含了各种 API 和组件的用法。
  • 多交流学习: 加入 UniApp 的社区,与其他开发者交流学习。

结束语:

UniApp 是一个非常强大的多端应用开发框架,可以帮助咱们快速构建跨平台应用。希望今天的分享对大家有所帮助。记住,代码复用和版本管理是提高开发效率的关键,一定要重视起来! 下课!

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注