【uni-app 混合开发】使用 uni-app 开发跨端应用

813 阅读5分钟

快速上手

什么是uni-app

uni-app 是一个使用 Vue.js 开发所有前端应用的框架,开发者编写一套代码,可打包成 H5、小程序、APP 发布到多端

HBuilderX 是开发 uni-app 应用的神兵利器:

image-20231128195039834.png

下面是一些推荐的插件,更多插件请访问 插件市场

  • uni-app(vue2/vue3)编译器:将 Vue 代码打包到多端
  • dart-sass:解析 scss 语法
  • prettier:代码格式化
  • uni-ui:跨端 UI 组件库
  • pinia-plugin-unistorage:本地持久化,需要与 Pinia 配合使用
  • uni-ajax:参考 axios 封装的网络请求

创建uni-app项目

方式一 vue-cli方式

  1. 下载 Vue3版本 uni-app 模版
  2. 安装依赖 npm i
  3. 打包运行 npm run xxx

方式二 HBuilderX(推荐)

  1. 新建项目
  2. 选择模版
  3. 打包运行

image-20231128105402711.png

image-20231128105615616.png

运行uni-app项目

运行到浏览器

  1. 安装内置浏览器插件(可选)
  2. 打包运行

运行到小程序端

  1. manifest.json 文件中添加微信小程序 AppID
  2. 在小程序中开启服务端口 设置>安全>服务端口
  3. 打包运行

运行到安卓真机

  1. 安装 App真机运行 插件
  2. 在手机上开启USB调试 关于手机>版本号>连续点击开启开发者模式>允许USB调试
  3. 使用 USB 线连接电脑和手机,并选择传输文件模式
  4. 打包运行

对于不同的手机系统,开发者模式的开启存在差异,具体步骤请百度

uni-app目录结构

image-20231128200342757.png

eg.pages.json页面配置示例

{
    // 路由&页面配置
	"pages": [
		{
			"path": "pages/index/index",
			"style": {
				"navigationBarTitleText": "首页"
			}
		},
		{
			"path": "pages/list/list",
			"style": {
				"navigationBarTitleText": "列表页"
			}
		}
	],
    // 页面全局配置
	"globalStyle": {
		"navigationBarTextStyle": "black",
		"navigationBarTitleText": "uni-app",
		"navigationBarBackgroundColor": "#F8F8F8",
		"backgroundColor": "#F8F8F8"
	},
    // 底部TabBar配置
    "tabBar": {
		"color": "#999",
		"selectedColor": "#333",
		"borderStyle": "white",
		"position": "bottom",
		"list": [{
			"text": "首页",
			"pagePath": "pages/index/index",
			"iconPath": "/static/home_default.png",
			"selectedIconPath": "/static/home_active.png"
		}, {
			"text": "列表页",
			"pagePath": "pages/list/list",
			"iconPath": "/static/list_default.png",
			"selectedIconPath": "/static/list_active.png"
		}]
	},
	"uniIdRouter": {}
}

更多配置项请参考:uni-app 全局配置

App.vue 是根组件,相当于小程序中 app.js、app.wxss 两个文件的组合,常用来定义全局样式

uni-app生命周期

uni-app 项目中 页面的生命周期 参考小程序的生命周期,组件的生命周期 参考 Vue 的生命周期。不同生命周期的可用情况如下:

image-20231128210432872.png

eg.生命周期示例

<script setup>
  // Vue组件生命周期
  import { onMounted } from 'vue'
  // 小程序生命周期 只能在页面上使用
  import { onLaunch, onLoad } from '@dcloudio/uni-app'
  onLoad((params)=>{
      console.log('页面参数:', params)
      getList()
  })
</script>

uni-app混合开发

Q:uni-app 项目的开发模式是?

A:基于 Vue2/Vue3语法 结合 微信小程序 组件、内置API 、生命周期进行混合开发

image-20231128152344839.png

eg.uni-app混合开发示例

<template>
	<view style="padding: 10px">
		<view>我是块元素</view>
		<text>我是行内元素</text>
		<text>我是行内元素</text>
		<text>我是行内元素</text>
	</view>
	<view style="border: 1px solid red; padding: 10px; margin: 10px">
		<view v-for="item in list" :key="item">{{ item }}</view>
	</view>
	<button type="primary" @click="addItem">添加</button>
</template>

<script setup>
    // 结合 Vue3 的 setup 语法使用【应用生命周期】和【页面生命周期】需要用到 @dcloudio/uni-app 包
    import { onLoad } from '@dcloudio/uni-app';
    import { onMounted, ref } from 'vue';
    const list = ref([1, 2, 3]);
    const addItem = () => {
        list.value.push(Math.floor(Math.random() * 10) + 1);
        uni.showToast({
            title: '添加成功!'
        });
    };
    onLoad((params) => {
        console.log('组件加载了', params);
    });
</script>

进阶用法

分包加载

分包的好处:

  • 实现页面的按需加载
  • 减少主包体积,突破小程序包大小限制

分包的具体步骤如下:

  1. pages.json 中配置 subPackages 选项,同时在 root 属性中指定分包名称,在 pages 属性中指定分包下的页面
  2. 新建分包目录和页面,在新建页面时勾选具体的分包

eg.pages.json分包配置示例

// 分包前
// "subPackages": [{
//    "root": "pageA",
//    "pages": []
// }],
// 分包后
"subPackages": [{
    "root": "pageA",
    "pages": [{
        "path": "list/list",
        "style": {
            "navigationBarTextStyle": "black"
        }
    }]
}]

条件编译

条件编译是用特殊的注释作为标记,在编译时根据这些特殊的注释,将注释里面的代码编译到不同平台。点击查看条件编译中所有可用的平台标识

// #ifdef 平台标识
满足条件时执行的代码
// #endif

eg.三种类型条件编译示例

<template>
  <view>
    <!-- #ifdef H5 -->
    <view class="red big">我只在H5平台显示</view>
    <!-- #endif -->
    <!-- #ifdef MP-WEIXIN -->
    <view class="red big">我只在微信小程序中显示</view>
    <!-- #endif -->
  </view>
</template>

<script setup>
  // #ifdef H5
  console.log('H5平台才会执行的代码')
  // #endif

  // #ifndef H5
  console.log('H5以外平台会执行的代码')
  // #endif

  // #ifdef H5 || MP-WEIXIN
  console.log('H5平台或者微信小程序才会执行的代码')
  // #endif
</script>

<style lang="scss">
  .big {
    font-weight: bold;
    /* #ifdef H5 */
    font-size: 32px;
    /* #endif */
  }
  /* #ifdef H5 */
  .red {
    color: red;
  }
  /* #endif */
</style>

image-20231129111534209.png

image-20231129111419655.png

easycom组件规范

组件有三大类:内置组件、第三方组件和自定义组件(又分为全局组件和局部组件)

在 uni-app 项目中,只有 局部组件 需要先引入、注册后才能使用,其他组件可以直接使用(Vue3 引入即注册)。具体示例见 uni-app 项目与 Vue 组件引用方式对比

组件通信

在 uni-app 项目中,组件间的通信仍然采用 Vue2/Vue3 语法

eg.Vue3父子组件通信示例

<template>
  <view style="padding: 10px;border: 1px solid #333;">
    <view>我是父组件</view>
    <Son ref="sonRef" :money="money" @change-money="changeMoney"></Son>
  </view>
</template>

<script setup>
  import Son from './components/son.vue'
  import {
    onMounted,
    ref
  } from "vue";

  const money = ref(200)
  const changeMoney = (m) => {
    money.value += m
  }
  // ref模版引用
  const sonRef = ref(null)
  onMounted(() => {
    sonRef.value.validate()
  })
</script>
<template>
  <view style="padding: 10px;border: 1px solid #333;">
    <view>我是子组件</view>
    <view>
      老爸给的钱:{{money}}
      <button size="mini" @click="getMoney">+100</button>
    </view>

  </view>
</template>

<script setup>
  // 父传子
  defineProps({
    money: {
      type: Number,
      default: 100
    }
  })
  // 子传父
  const emit = defineEmits(['change-money'])
  const getMoney = () => {
    emit('change-money', 100)
  }
  // 暴露方法给父组件
  const validate = () => {
    console.log('子组件中的方法被调用了')
  }
  defineExpose({
    validate
  })
</script>

uni-fztx.gif

Pinia状态管理

pinia 是 Vue3 新一代状态管理工具,用来替代 Vuex

pinia-plugin-unistorage 插件在操作 pinia 中的全局状态时自动进行持久化

eg.Vue3全局状态管理示例

  1. 创建 Pinia 实例并注册
// main.js
import { createPinia } from 'pinia'
const pinia = createPinia()
app.use(pinia)
  1. 创建 Store 并导出
// stores/count.js
import { defineStore } from 'pinia'
import { computed, ref } from 'vue'

export const useCount = defineStore('count', () => {
  // state
  const count = ref(1)
  // getters
  const dbCount = computed(() => {
    return count.value * 2
  })
  // mutations&actions
  const addCount = () => {
    count.value++
  }
  const resetCount = () => {
    setTimeout(() => {
      count.value = 1
    }, 500)
  }
  // 返回全局数据和方法
  return {
    count,
    dbCount,
    addCount,
    resetCount
  }
},{
    // 开启后对 state 的数据读写都将持久化
    unistorage: {
        key: 'myCount',
        paths: ['count']
    }
})
  1. 导入 Store 并使用
<!-- pages/count/count.vue -->
<template>
	<view style="text-align: center;">
    <view>{{store.count}}-{{store.dbCount}}</view>
    <button type="primary" size="mini" @click="store.addCount">+1</button>
    <button size="mini" @click="store.resetCount">重置</button>
    <view>解构后的count:{{count}}</view>
  </view>
</template>
<script>
    import { useCount } from '@/stores/count.js'
    const store = useCount()
    // storeToRefs对数据进行解构处理后仍然能保持响应式
    import { storeToRefs } from 'pinia'
    const { count } = storeToRefs(store)
</script>

uni-pinia-count.gif