uni-app 之 跨平台开发

一、概念

1. 原生 VS 跨平台

原生开发

优点

性能稳定,使用流畅,用户体验好、功能齐全,安全性有保证,兼容性好 ,可使用手机所有硬件功能等

缺点

开发周期长、维护成本高、迭代慢、部署慢、新版本必须重新下载应用
不支持跨平台,必须同时开发多端代码

跨平台开发

优点

  • 可以跨平台,一套代码搞定iOS、Android、微信小程序、H5应用等

  • 开发成本较低,开发周期比原生短

  • 适用于跟系统交互少、页面不太复杂的场景

缺点

  • 对开发者要求高,除了本身JS的了解,还必须熟悉一点原生开发

  • 不适合做高性能、复杂用户体验,以及定制高的应用程序。比如:抖音、微信、QQ等

  • 同时开发多端兼容和适配比较麻烦、调试起来不方便

2. 跨平台框架对比

3. 认识uni-app

4. uni-app VS 微信小程序

相同点

  • 都是接近原生的体验、打开即用、不需要安装

  • 都可开发微信小程序、都有非常完善的官方文档

不同点

  • 跨平台

  • uni-app : 支持跨平台,编写一套代码,可以发布到多个平台

  • 微信小程序 : 不支持

  • 工程化

  • uni-app : 纯Vue体验、高效、统一、工程化强

  • 微信小程序 : 工程化弱、使用小程序开发语言

  • 复杂程度

  • uni-app : 适合不太复杂的应用,因为需要兼容多端,多端一起兼容和适配增加了开发者心智负担

  • 微信小程序 : 适合较复杂、定制性较高、兼容和稳定性更好的应用

如何选择

需要跨平台、不太复杂的应用选 uni-app,复杂的应用使用uni-app反而增加了难度。
不需要跨平台、较复杂、对兼容和稳定性要求高的选原生微信小程序

5. uni-app架构图

二、uni-app初体验

1. 开发工具

Hbuilder X : 是通用的前端开发工具,但为 uni-app 做了特别强化
网址 : hbuilderx

ps : 用Vue3的Composition API 建议用 HBuilder X最新Alpha版,旧版有兼容问题

2. 创建项目

可视化界面创建 ( 推荐 )

  • 点工具栏里的文件 -> 新建 -> 项目 ( 快捷键Ctrl+N )

  • 选择uni-app类型,输入工程名,选择模板,选择Vue版本,点击创建即可

Vue-CLI 命令行创建

3. 运行uni-app

在 浏览器 运行

点击工具栏的运行 -> 运行到浏览器 -> 选择浏览器

在 微信开发者工具 运行

如果自动启动失败,可用微信开发者工具手动打开项目
项目在unpackage/dist/dev/mp-weixin路径下

01 - 开启服务端口

微信开发者工具需要开启服务端口:
小程序开发工具设置 -> 安全(目的是让HBuilder可以启动微信开发者工具)

02 - 运行

在 手机或模拟器 运行

这里是mac电脑 + ios手机环境

ps : 之所以要用XCode新建一个项目,先打开模拟器,再用hbuilderx打开相同的模拟器
是为了减少bug,不这么做容易有奇奇怪怪的小问题

XCode 设置

01 - 安装XCode
02 - 新建Xcode项⽬
03 - 选择项⽬类型
04 - 填写项⽬信息
05 - 选择路径
06 - 启动Xcode项⽬生成一个IOS模拟器
07 - 运⾏Xcode项⽬的效果

Hbuilderx-Alpha 设置

01 - 安装手机模拟器插件
02 - 配置adb路径(可跳过)
可不配 : 不配会自动去寻找默认的
  • HBuilderX正式版adb目录位置 :

  • windows : 安装路径下的 tools/adbs 目录

  • mac : HBuilderX.app/Contents/tools/adbs目录;

  • HBuilderX Alpha版的adb目录位置:

  • windows : 安装路径下的 plugins/launcher/tools/adbs 目录

  • mac下 : /Applications/HBuilderX-Alpha.app/Contents/HBuilderX/plugins/launcher/tools/adbs目录

  • 在adbs目录下运行./adb ,即可使用adb命令(Win和Mac一样)

03 - 选择模拟器
04 - 选择模拟器
05 - 效果
有个很严重的问题,我的这样没有热更新,每次改完得重新运行,我要崩溃,有没有大佬指点迷津

ios模拟器问题 : 未修复

三、uni-app的架构和配置

1. 目录结构

2. 开发规范

为了实现多端兼容,综合考虑编译速度、运行性能等因素,uni-app 约定了如下开发规范:
  • 页面文件遵循

  • 组件标签靠近小程序规范

  • 接口能力(JS API)

  • 靠近微信小程序规范 => 但需将前缀 wx 替换为 uni

  • 数据绑定及事件处理

  • 同 Vue.js 规范 => 例如使用@click

  • 生命周期

  • 可用小程序的

  • 也可用vue的

  • 为兼容多端运行

  • 建议使用flex布局

  • 推荐使用rpx单位

  • uni-app的官网文档

3. main.js

main.js是 uni-app 的入口文件,主要作用是:
  • 初始化vue实例

  • 定义全局组件

  • 定义全局属性

  • 安装插件,如:pinia、vuex 等

01 - 初始化vue实例

02 - 定义全局组件

import { createSSRApp } from 'vue'

//引入组件
import myComponent from './components/my-component/my-component.vue'

export function createApp() {
  const app = createSSRApp(App) 
  //调用app.component方法全局注册组件
  app.component('my-component', myComponent)
  return {
    app
  }
}

03 - 定义全局属性

// vue2
Vue.prototype.$http = () => {}

// vue3
export function createApp() {
  const app = createSSRApp(App);
  app.config.globalProperties.$http = () => {}
  return {
      app
  }
}

04 - 安装插件,如:pinia、vuex 等

import { createSSRApp } from 'vue'
// 当然这里要自己先封一下 Pinia
import pinia from '.stores'
export function createApp() {
  const app = createSSRApp(App);
    app.use(pinia);
    return {
      app
      Pinia // 此处必须将 Pinia 返回
  }
}

4. App.vue

App.vue : 入口组件
  • App.vue是uni-app的入口组件,所有页面都是在App.vue下进行切换

  • App.vue本身不是页面,这里不能编写视图元素,也就是没有<template>元素

App.vue的作用是 :
  • 应用的生命周期

  • 编写全局样式

  • 定义全局数据 globalData

01 - 应用生命周期

注意:应用生命的周期仅可在App.vue中监听,在页面监听无效。
<script>
    export default {
        /*
         * App应用的生命周期
        */
        onLaunch: function(option) {
            // 可以获取到小程序设置的启动参数
            console.log('App Launch')
        },
        onShow: function() {
            console.log('App Show')
        },
        onHide: function() {
            console.log('App Hide')
        }
    }
</script>

<style >
/*每个页面公共css */
</style>

02 - 编写全局样式

全局样式 : App.vue 中style的样式,作用于每一个页面(style标签不支持scoped)
App.vue 中通过 @import 语句可以导入外联样式,一样作用于每一个页面
局部样式 : 在 pages 目录下 的 vue 文件的style中的样式为局部样式,只作用对应的页面,并会覆盖 App.vue 中相同的选择器
style标签不支持scoped,默认就是局部样式
<script>
export default {
    onLaunch: function() {
    },
    onShow: function() {
    },
    onHide: function() {
    }
}
</script>
<!-- 这里不支持写scoped属性 -->
<style lang='scss'>
    /*每个页面公共css */
    
    /* 这个文件中定义的变量 */
    @import '@/static/css/init.scss';
    @import '@/static/css/base.css';
    
    .title{
        color: red;
        /* 这里可以使用变量,但是其他页面无法使用,若想使用,那个页面也得导入scss文件 */
        border: 2rpx solid $mainColor;
    }
</style>

03 - 定义全局数据 globalData

<script>
    export default {
        // 定义全局变量
        globalData: {
            text: 'text'
        },
        onLaunch: function() {
            // getApp对象还未获取,暂时可以使用this.globalData获取globalData
            console.log('App Launch', this.globalData) // App Launch {text: 'text'}
        },
        onShow: function() {
            // 可通过 getApp().globalData.*    来操作,其他页面也是这样调用
            console.log('App Show', getApp().globalData) // App Show {text: 'text'}
        },
        onHide: function() {
            console.log('App Hide')
        }
    }
</script>

// 页面使用
onShow() {
  // getApp() => 全部页面自带,都可以使用
  console.log('page', getApp().globalData);
}

5. uni.scss

uni.scss 全局样式文件
  • 为了方便整体控制应用风格,默认定义了uni-app框架内置全局变量,也可以存放自定义的全局变量等

  • 在uni.scss中定义的变量,无需 @import 就可以在任意组件中直接使用

  • 使用uni.scss中的变量,需在 HBuilderX 里面安装 scss 插件(dart-sass插件),

  • 然后在该组件的 style 上加 lang=“scss”,重启即可生效

注意事项 :
  • 这里的uni-app框架内置变量和后面uni-ui组件库的内置变量是不一样的

  • uni.scss定义的变量是全局可以直接使用,App.vue定义的变量只能在当前组件中使用

uni.scss的作用 :
  • 定义自定义的全局的样式变量

  • 重写uni-app内置的样式变量

  • 重写uni-un内置的样式变量

6. page.json

page.json : 全局页面配置(兼容h5、weapp、app )
  • pages.json 文件用来对 uni-app 进行全局配置,类似微信小程序中app.json

  • 决定页面的路径、窗口样式、原生的导航栏、底部的原生tabbar 等

{
    "pages": [
        // 页面窗口的配置,优先级比全局的要高
        // 首页
        {
            "path": "pages/index/index",
            "style": {
                // 相当于配置页面样式
                "navigationBarTitleText": "空我,核弹踢!"
            }
        }
    ],
    // 相当于整个小程序的顶部窗口
    "globalStyle": {
        // 导航栏标题的颜色 black/white
        "navigationBarTextStyle": "black",
        // 导航栏标题的文字,整体的,会被上面的单个页面配置的覆盖
        "navigationBarTitleText": "迪迦奥特曼,冲啊",
        // 顶部背景颜色
        "navigationBarBackgroundColor": "#ff8198",
        // 下拉刷新时可以看到的那个
        "backgroundColor": "#F8F8F8"
    }
}

7. manifest.json

manifest.json : 应用配置
  • Android平台相关配置

  • iOS平台相关配置

  • Web端相关的配置

  • 微信小程序相关配置

  • ......

四、uni-ui组件库

uni-ui是DCloud提供的一个UI组件库,一套基于Vue组件、flex布局的跨全端UI框架
uni-ui不包括uni-app框架提供的基础组件,而是基础组件的补充
网址 : uni-ui

1. 安装 uni-ui 组件库

方式一 : uni_modules按需安装

通过 uni_modules(插件模块化规范)单独安装组件
  • 1. 官网找到扩展组件清单,然后将所需要的组件导入到项目,导入后直接使用,无需import和注册

  • 2. 若想切换应用风格,可以在uni.scss导入uni-ui提供的内置scss变量,然后重启应用

  • ps :需要登录 DCloud 账号才能安装

方式二 : uni_modules全部安装

方式三 : 创建项目时安装

ps: 该方式使得全部组件都下载了

方式四 : npm 安装

2. 定制 uni-ui 主题风格

  • 安装dart-sass插件(一般都会提示,并自动安装)

  • 在项目根目录的uni.scss文件中引入uni-ui组件库的variable.scss变量文件

  • 然后就可以使用或修改对应的scss变量

  • 变量主要定义的是主题色

3. 组件样式穿透

// 小程序 和 app 有效果
.uni-forms-item_label{
    color:red !important;
}

// 小程序 和 app 和 h5
:deep(.uni-forms-item_label){
    color:red !important;
}

// 小程序 和 app 和 h5
:global(.uni-forms-item_label){
    color:red !important;
}

五、跨端兼容实现

uni-app能实现一套代码、多端运行,核心是通过编译器 + 运行时实现的

  • 编译器:将uni-app统一代码编译生成每个平台支持的特有代码

  • 如在小程序平台,编译器将.vue文件拆分生成wxml、wxss、js等。

  • 运行时:动态处理数据绑定、事件代理,保证 Vue和对应宿主平台 数据的一致性

1. 存在的问题

跨平台存在的问题:

  • uni-app 已将常用的组件、JS API 封装到框架中,按照 uni-app 规范开发即可保证多平台兼容

  • 大部分业务均可直接满足。

  • 每个平台有自己的一些特性,因此会存在一些无法跨平台的情况

  • 大量写 if else,会造成代码执行性能低下和管理混乱

  • 编译到不同的工程后二次修改,会让后续升级变的很麻烦

跨平台兼容解决方案:

  • 在 C 语言中,通过 #ifdef、#ifndef 的方式,为 windows、mac 等不同 os 编译不同的代码

  • uni-app 参考这个思路,为 uni-app 提供了条件编译手段,在一个工程里优雅的完成了平台个性化实现

2. 条件编译

01 - 具体语法

// #ifdef  %PLATFORM%
平台特有的API实现
// #endif

02 - 支持的文件

<template>
    <view class="content">
        页面!!!!
        <!-- 只在h5中显示 -->
        <!-- #ifdef H5 -->
        <view>
            <uni-badge text="1" type="primary"></uni-badge>
        </view>
        <!-- #endif -->

        <!-- 在微信中 或 app中显示 -->
        <!-- #ifdef MP-WEIXIN || APP-PLUS -->
        <view class="img"></view>
        <!-- #endif -->
    </view>
</template>

<script>
    export default {
        data() {
            return {
                title: '1999888'
            }
        },
        onLoad() {
            // #ifdef H5
            document.title = 'h5 !!!!!!!'
            // 可以在这里操作dom
            // #endif

            // #ifdef MP-WEIXIN
            // pages.json中设定的优先级比较高
            wx.setNavigationBarTitle({
                title: 'WXapp'
            })
            // #endif

            // #ifdef APP-PLUS
            // 这个是跨端的
            uni.setNavigationBarTitle({
                title: 'APP'
            })
            // #endif
        },
        onShow() {
            console.log('page', getApp().globalData);
        },
    }
</script>

<style lang="scss">
    /* #ifdef H5 */
    .uni-badge--x {
        background-color: blue;
    }

    /* #endif */

    /* #ifdef APP-PLUS || MP-WEIXIN */
    .img {
        height: 200rpx;
        background: url("~@/static/abc/logo.png") no-repeat;
        background-size: contain;
    }

    /* #endif */
</style>

03 - 注意事项

Android 和 iOS 平台不支持条件编译
如需区分 Android、iOS 平台,请通过调用 uni.getSystemInfo 来获取平台信息

六、页面 - Page

1. 新建页面

01 - 新建页面

可直接新建页面,不用新建目录,会带同名目录

02 - 在pages.json中配置

一般会自动生成,如果没有,手写即可

03 - 配置tabBar

2. 页面路由

uni-app 有两种页面路由跳转方式: 和小程序一模一样
使用navigator组件跳转
调用API跳转(类似小程序,与vue-router不同)

API跳转

跳转到 TabBar 页面

uni.switchTab
跳转到 tabBar 页面,并关闭其他所有非 tabBar 页面
<template>
    <view class="content">
        <button type="primary" @click="jumpFn">switchTab</button>
    </view>
</template>

<script>
    export default {
        methods: {
            jumpFn() {
                uni.switchTab({
                    // 需要跳转的 tabBar 页面的路径(需在 pages.json 的 tabBar 字段定义的页面),路径后不能带参数
                    url: '/pages/about/about',
                    success: (res) => {
                        console.log('success', res); // success {errMsg: 'switchTab:ok'}
                    },
                    fail: (err) => {
                        console.log('fail', err);
                    },
                    complete: (res) => {
                        console.log('complete', res); // complete {errMsg: 'switchTab:ok'}
                    }
                })
            }
        }
    }
</script>

跳转到 普通 页面

uni.navigateTo

保留当前页面,跳转到应用内的某个页面,使用uni.navigateBack可以返回到原页面

<template>
    <view class="content">
        <button type="primary" @click="jumpFn">navigateTo</button>
    </view>
</template>

<script>
    export default {
        methods: {
            jumpFn() {
                uni.navigateTo({
                    // 需要跳转的应用内非 tabBar 的页面的路径 , 路径后可以带参数
                    url: '/pages/detail/detail?name=coder&age=18',
                    // 仅支持在app端看到运动 效果
                    animationType: 'fade-in',
                    animationDuration: 2000,
                    success: (res) => {
                        console.log('success:', res);
                    },
                    fail: (err) => {
                        console.log('fail:', err);
                    },
                    complete: (res) => {
                        console.log('complete:', res);
                    },
                })
            }
        }
    }

</script>
uni.navigateBack
关闭当前页面,返回上一页面或多级页面
可通过 getCurrentPages() 获取当前的页面栈,决定需要返回几层
// 此处是A页面
uni.navigateTo({
    url: 'B?id=1'
});

// 此处是B页面
uni.navigateTo({
    url: 'C?id=1'
});

// 在C页面内 navigateBack,将返回A页面
uni.navigateBack({
    delta: 2,
    success: (res) => {
        console.log('success:', res);
    },
    fail: (err) => {
        console.log('fail:', err);
    },
    complete: (res) => {
        console.log('complete:', res);
    },
});
uni.redirectTo
关闭当前页面,跳转到应用内的某个页面
uni.redirectTo({
    url:'test?id=1',
    success: (res) => {
        console.log('success:', res);
    },
    fail: (err) => {
        console.log('fail:', err);
    },
    complete: (res) => {
        console.log('complete:', res);
    },
});
uni.reLaunch
关闭所有页面,打开到应用内的某个页面
uni.reLaunch({
    url:'test?id=1',
    success: (res) => {
        console.log('success:', res);
    },
    fail: (err) => {
        console.log('fail:', err);
    },
    complete: (res) => {
        console.log('complete:', res);
    },
});

Navigator跳转

跳转到 tabBar 页面

open-type="switchTab" 对应 uni.switchTab 的功能
<navigator open-type="switchTab" url="../profy/profy">跳转到个人</navigator>

跳转到 普通 页面

路由跳转中相互传值

若有特殊字符
传递时 : encodeURIComponent 编码
接受时 : decodeURIComponent 解码

url查询字符 和 直接修改值

首页 => 详情页
jumpFn() {
    uni.navigateTo({
        // 需要跳转的应用内非 tabBar 的页面的路径 , 路径后可以带参数
        url: '/pages/detail/detail?name=coder&age=18',
        // 仅支持在app端看到运动 效果
        animationType: 'fade-in',
        animationDuration: 2000,
        success: (res) => {
            console.log('success:', res);
        },
        fail: (err) => {
            console.log('fail:', err);
        },
        complete: (res) => {
            console.log('complete:', res);
        },
    })
}
详情页 => 首页
export default {
    // 可在这获取到首页传递过来的值
    onLoad(option) {
        console.log(option); // {name: 'coder', age: '18'}
    },

    /**
     * 写在这里的理由 : 用户可能点击顶部状态栏中的返回键,写在这不管怎么返回,都会触发
     */
    // 页面被注销时调用
    onUnload() {
        // 1. 获取到已经存在的所有页面
        const pages = getCurrentPages()
        // 2. 拿到上一个页面的实例  当前页面为最后一个,所以上一个页面是减2
        const prePage = pages[pages.length - 2]

        /**
         * 3. 设置值
         */

        // H5端
        // #ifdef H5 
        prePage.arr[1].name = 'ccc'
        // #endif

        // 微信端,有个$vm
        // #ifdef MP-WEIXIN 
        prePage.$vm.arr[1].name = 'ccc'
        // #endif

        // app端,上述两种都可满足,奇怪
        // #ifdef APP-PLUS
        // prePage.arr[1].name = 'ccc'
        prePage.$vm.arr[1].name = 'ccc'
        // #endif
    }
}

url查询字符 和 EventChannel修改

只有 uni.navigateTo 才能使用这个哦
首页 => 详情页
jumpFn() {
    uni.navigateTo({
        // 需要跳转的应用内非 tabBar 的页面的路径 , 路径后可以带参数,如果有特殊字符记得编码
        url: `/pages/detail/detail?name=${encodeURIComponent('coder')}&age=18`,
        events: {
            // 为指定事件添加一个监听器,获取被打开页面传送到当前页面的数据
            acceptDataFromOpenedPage: (data) => {
                console.log('acceptDataFromOpenedPage',
                    data) // acceptDataFromOpenedPage {name: 'coder'}
            },
            someEvent: (data) => {
                console.log('someEvent', data) // someEvent {age: 10}
            }
        },
        success: (res) => {
            console.log('success:', res);
            // 通过eventChannel向被打开页面传送数据,用此方式可不用在url上拼接参数
            res.eventChannel.emit('acceptDataFromOpenerPage', { data: 'data from starter page' })
        },
        fail: (err) => {
            console.log('fail:', err);
        },
        complete: (res) => {
            console.log('complete:', res);
        },
    })
}
详情页 => 首页
export default {
    // 可在这获取到首页传递过来的值
    onLoad(option) {
        console.log(option, decodeURIComponent(option.name)); // {name: 'coder', age: '18'}
    },

    /**
     * 写在这里的理由 : 用户可能点击顶部状态栏中的返回键,写在这不管怎么返回,都会触发
     */
    // 页面被注销时调用
    onUnload() {
        // 1. 拿到eventChanner渠道
        const eventChanner = this.getOpenerEventChannel()
        // 2. 通过渠道,传递数据给上一个页面
        eventChanner.emit('acceptDataFromOpenedPage', {
            name: 'coder'
        })
        eventChanner.emit('someEvent', {
            age: 10
        })
        // 3. 监听上一个页面传递过来的数据
        eventChanner.on('acceptDataFromOpenerPage', function(data) {
            console.log(data) // {data: 'data from starter page'}
        })
    }
}

vue3写法

首页 => 详情页
<template>
    <view class="content">
        <button type="primary" @click="jumpFn">navigateTo</button>
    </view>
</template>

<script setup>
    import { ref } from 'vue'
    const arr = ref([{
            name: 'coder',
            age: 12
        },
        {
            name: 'star',
            age: 18
        },
    ])
    const age = ref(-1)

    const jumpFn = () => {
        uni.navigateTo({
            // 需要跳转的应用内非 tabBar 的页面的路径 , 路径后可以带参数,如果有特殊字符记得编码
            url: `/pages/detail/detail?name=${encodeURIComponent('coder——饿$12${}')}&age=18`,
            events: {
                // 为指定事件添加一个监听器,获取被打开页面传送到当前页面的数据
                acceptDataFromOpenedPage: (data) => {
                    console.log('acceptDataFromOpenedPage',
                        data) // acceptDataFromOpenedPage {name: 'coder'}
                },
                someEvent: (data) => {
                    console.log('someEvent', data) // someEvent {age: 10}
                    age.value = data.age
                }
            },
            success: (res) => {
                console.log('success:', res);
            },
            fail: (err) => {
                console.log('fail:', err);
            },
            complete: (res) => {
                console.log('complete:', res);
            },
        })
    }
</script>
详情页 => 首页
<template>
    <view>
        detail-view
    </view>
</template>

<script setup>
    import { ref, getCurrentInstance } from 'vue'
    import { onLoad, onUnload } from "@dcloudio/uni-app";
    
    // 方式二 : 也可在defineProps中拿到传递过来的值
    const props = defaineProps({
        name: String,
        age: Number
    })
    console.log(option.name,option.age)

    // 方式一 : 可在这获取到首页传递过来的值
    onLoad((option) => {
        console.log(option, decodeURIComponent(option.name)); // {name: 'coder', age: '18'}
    })
    
    // 拿到当前实例,可以理解为this
    const instance = ref(getCurrentInstance().proxy)
    /**
     * 写在这里的理由 : 用户可能点击顶部状态栏中的返回键,写在这不管怎么返回,都会触发
     */
    // 页面被注销时调用
    onUnload(() => {
        // 1. 拿到eventChanner渠道
        const eventChanner = instance.value.getOpenerEventChannel()
        // const eventChanner = getCurrentInstance().proxy.getOpenerEventChannel()
        // 2. 通过渠道,拿到回调函数,并且传递数据给上一个页面
        eventChanner.emit('acceptDataFromOpenedPage', {
            name: 'coder'
        })
        eventChanner.emit('someEvent', {
            age: 10
        })
    })
</script>

3. 事件总线

01 - 注意事项

  • 需先监听,再触发事件,比如:你在A界面触发,然后跳转到B页面后才监听是不行的

  • 通常on 和 off 是同时使用,可以避免多次重复监听

  • 适合页面返回传递参数、适合跨组件通讯,不适合界面跳转传递参数

02 - 页面通讯

uni.$emit

触发全局的自定义事件,附加参数都会传给监听器回调
uni.$emit('emitFn',{msg:'页面更新'})

uni.$on

监听全局的自定义事件
事件可以由 uni.$emit 触发,回调函数会接收所有传入事件触发函数的额外参数
uni.$on('emitFn',(data) => {
   console.log('监听到事件来自 emitFn ,携带参数 msg 为:' + data.msg);
})

uni.$once

监听全局的自定义事件
事件可以由 uni.$emit 触发,但是只触发一次,在第一次触发之后移除监听器
uni.$once('emitFn',(data) => {
   console.log('监听到事件来自 emitFn ,携带参数 msg 为:' + data.msg);
})

uni.$off

移除全局自定义事件监听器
// 注 : 这样并不能清除监听过的emitFn
uni.$off('emitFn',(data) => {
   console.log('监听到事件来自 emitFn ,携带参数 msg 为:' + data.msg);
})

03 - 代码示例

需先监听,再触发事件 ,比如:你在A界面触发,然后跳转到B页面后才监听是不行的
A监听,B发射,A再监听才能拿到值

A组件监听

<template>
    <view class="content">
        <button type="primary" @click="jumpFn">navigateTo</button>
    </view>
</template>

<script setup>
    import { onLoad, onUnload } from "@dcloudio/uni-app";
    // 跳转到详情页
    const jumpFn = () => {
        uni.navigateTo({
            url: `/pages/detail/detail`,
        })
    }
    const emitFn = (res) => {
        console.log('emitFn', res);
    }
    onLoad(() => {
        // 监听
        uni.$on('emitFn', emitFn)
    })
    onUnload(() => {
        // 移除
        uni.$off('emitFn', emitFn)
    })
</script>

B组件发射

<template>
    <view>
        detail-view
    </view>
</template>

<script setup>
    import { onUnload } from "@dcloudio/uni-app";
    onUnload(() => {
        uni.$emit('emitFn', {
            msg: '冲啊,迪迦奥特曼!!!'
        })
    })
</script>

4. 生命周期

tabBar的页面不会销毁,不会触发onUnload

Options API

export default {
    /**
     * 都会触发
     * */
    // 1.页面的生命周期
    onLoad(options) {
        console.log('onLoad');
    },
    onShow() {
        console.log('onShow');
        console.log(this);
    },
    onReady() {
        console.log('onReady');
    },
    onHide() {
        console.log('onHide');
    },
    onUnload() {
        console.log('onUnload');
    },
    onPullDownRefresh() {
        console.log('onPullDownRefresh');
        setTimeout(() => {
            uni.stopPullDownRefresh()
        }, 1000)
    },
    onReachBottom() {
        console.log('onReachBottom');
    },
    // 2.Vue组件的生命周期
    beforeCreate() {
        console.log('beforeCreate');
    },
    created() {
        console.log('created');
    },
    beforeMount() {
        console.log('beforeMount');
    },
    mounted() {
        console.log('mounted');
    },
    beforeDestroy() {
        console.log('beforeDestroy');
    },
    destroyed() {
        console.log('destroyed');
    }
}

Composition API

<script setup>
    import { ref, onBeforeMount, onMounted } from 'vue'
    import {
        onLoad,
        onShow,
        onReady,
        onHide,
        onUnload,
        onPullDownRefresh,
        onReachBottom
    } from '@dcloudio/uni-app'
    
    // 1.页面的生命周期
    onLoad((options) => {
        console.log('onLoad');
    })
    onShow(() => {
        console.log('onShow');
    })
    onReady(() => {
        console.log('onReady');
    })
    onHide(() => {
        console.log('onHide');
    })
    onUnload(() => {
        console.log('onUnload');
    })
    onPullDownRefresh(() => {
        console.log('onPullDownRefresh');
        setTimeout(() => {
            uni.stopPullDownRefresh()
        }, 1000)
    })
    onReachBottom(() => {
        console.log('onReachBottom');
    })
    // 2.Vue组件的生命周期
    onBeforeMount(() => {
        console.log('onBeforeMount');
    })
    onMounted(() => {
        console.log('onMounted');
    })
</script>

七、系统API调用

1. 网络请求

简单使用

偷个懒,文档上复制的~
uni.request({
    url: 'https://www.example.com/request', //仅为示例,并非真实接口地址。
    data: {
        text: 'uni.request'
    },
    header: {
        'custom-header': 'hello' //自定义请求头信息
    },
    success: (res) => {
        console.log(res.data);
        this.text = 'request success';
    }
});

01 - 进行封装

目录结构

request目录

index.js
/**
 * 封装请求
 */

import { BASE_URL, TIME_OUT } from './config.js'

// 封装成类
class StarRequest {
    // 设定公共URL + 超时时间
    constructor(url, timeout) {
        this.baseUrl = url
        this.timeout = timeout
    }
    request({ url, method = 'GET', data } = {}) {
        // 返回一个promise
        return new Promise((resolve, reject) => {
            uni.request({
                url: this.baseUrl + url,
                timeout: this.timeout,
                method,
                data,
                success: (res) => {
                    resolve(res.data)
                },
                fail: reject
            })
        })
    }
    get() {
        return this.request(option)
    }
    post() {
        return this.request({ ...option, method: 'POST' })
    }
}
// 可创建多个实例,请求不同的地址
export const sRequest = new StarRequest(BASE_URL, TIME_OUT)
config.js
/**
 * 设定常量
 */
export const BASE_URL = 'http://www.baidu.com/api'
export const TIME_OUT = 10000

modules目录

common.js
/**
 * 封装公共逻辑
 */
import { sRequest } from '@/service/request/index.js'

// 获取公共的字典数据
export const getAllDics = () => {
    return sRequest.request({
        url: '/common/dic'
    })
}

统一导出index.js

/**
 *     作统一导出
 */

// 公共数据
export { getAllDics } from './modules/common'

// ...

02 - 使用

import { onLoad, onUnload } from "@dcloudio/uni-app";
import { getAllDics } from '@/service/index.js'

// 封装一下请求,可以写在utils中,这里为了方便写在这
const awaitWrap = (promise) => {
    // 就不用写try...catch了
    return promise.then(res => [res, null]).catch(err => [null, err])
}

onLoad(async () => {
    // 使用请求
    const [res, err] = await awaitWrap(getAllDics())
    // 如果不为空,说明抛出了异常,返回即可
    if (err !== null) return console.log('请求错误');
    // 拿到数据
    console.log(res);
})

2. 数据缓存

sync : 同步,不存储完,不会执行后续代码

八、Vue文档总结

Vue2 - Options API

Vue3 - Composition API

九、打包配置

H5 - 打包配置

1. 修改manifest.json的配置

2. 发行 - 打包H5

3. 部署到服务器即可

可看我的另一篇文章 : 云原生 之 docker

微信小程序 - 打包配置

1. 拿到小程序appId

2. 修改manifest.json的配置

3. 发行-微信小程序

4. 在微信开发者工具中进行上传

APP - 配置

待更新,未操作~

猜你喜欢

转载自blog.csdn.net/a15297701931/article/details/129592721