UniApp面试题总结

1,341 阅读7分钟

uni-app跨端原理

uni-app 分 编译器和运行时(runtime) ,实现一套代码,多端运行主要是这两部分配合完成的, 编译器将开发者的代码进行编译,编译的输出物由每个平台各自的runtime进行解析。

微信小程序登陆流程

  • 通过wx.login()调用小程序api获取登陆凭证code,将code传给服务端
  • 服务端调用微信的登陆接口,把appid(小程序ID),appsecret(小程序密钥)、code码传给微信后台
    • appID的获取(微信小程序后台---开发--开发设置---小程序ID)
    • appsecret的获取(微信小程序后台---开发--开发设置---小程序密钥)
  • 微信后台验证成功后,返回给服务器用户的唯一标识(openid)和本次登陆的会话密钥(session_key)
  • 服务端会与openid相关联生成token,返回给小程序端
  • 小程序端将token存入缓存,再次向服务端发送请求时,携带token,服务端验证token后返回小程序需要的业务数据

uni-app的生命周期

应用生命周期

onLanuch – uni-app 初始化完成时触发(全局只触发一次)
onShow – uni-app启动,或从后台进入前台显示
onHide – uni-app从前台进入后台
onError – 当uni-app 报错时触发
onUNiNViewMessage – 对 nvue 页面发送的数据进行监听
onUnhandledRejection – 对未处理的Promise拒绝事件监听函数
onPageNotFound – 页面不存在监听函数
onThemeChange – 监听系统主题变化

页面生命周期

onInit – 监听页面初始化,参数同onLoad参数,为上个页面传递的数据,参数类型为Object,触发时机早于onLoad
onLoad – 监听页面加载,其参数为上个页面传递的数据,参数类型为Object
onShow – 监听页面显示,页面每次出现在屏幕上都触发,包括从下级页面返回露出当前页面
onReady – 监听页面初次渲染
onHide – 监听页面隐藏
onUnload – 监听页面隐藏
onResize – 监听窗口尺寸变化

组件生命周期(vue的生命周期)

uniappA页面传值

  • 第一种:URL参数传递 :通过在跳转链接中添加参数,在目标页面通过this.$route.params获取传递的参数
<!-- 跳转页面 -->
<uni-link :url="'/pages/targetPage/targetPage?param1=' + value1 + '&param2=' + value2">跳转到目标页面</uni-link>

// 在目标页面获取参数
export default {
  mounted() {
    const param1 = this.$route.params.param1;
    const param2 = this.$route.params.param2;
    console.log(param1, param2);
  }
}

  • 第二种:Vuex状态管理
  • 第三种:本地存储:使用uni.setStorage和uni.getStorage等方法,将数据存储在本地,在另一个页面读取
// 在页面A中保存数据到本地存储
uni.setStorage({
  key: 'yourDataKey',
  data: yourData,
});

// 在页面B中从本地存储中读取数据
uni.getStorage({
  key: 'yourDataKey',
  success: function (res) {
 const pageData = res.data;
  },
});

uni-app实现跨端适配

使用条件编译模式,对js代码、css、template在某个环境中生效。 条件编译 以 #ifdef+环境名 开头  以#endif 结尾, 限制一段代码只在某个平台存在

以 #ifndef+环境名 开头  以#endif 结尾, 限制一段代码除了某平台均存在

/* uni.scss*/
/* #ifdef H5*/
.class{
 color:red
}
/* #endif*/

uniapp封装组件

u-uploader:(同时上传图片和视频

背景:官方上传只能上传图片,不能同时上传图片和视频的组件

全局水印组件:(信息展示的保密性)

uniapp封装方法

  • Toast提示
/**
 * 提示方法
 * @param {String} title 提示文字
 * @param {String}  icon icon图片
 * @param {Number}  duration 提示时间
 */
export function toast(title, icon = 'none', duration = 1500) {
    if(title) {
        uni.showToast({
            title,
            icon,
            duration
        })
    }
}

  • 图片预览
/**
 * 预览图片
 * @param {Array} urls 图片链接
 */
export function previewImage(urls, itemList = ['发送给朋友', '保存图片', '收藏']) {
    uni.previewImage({
        urls,
        longPressActions: {
            itemList,
            fail: function (error) {
                console.error(error,'===previewImage')
            }
        }
    })
}

  • 图片下载
/**
 * 保存图片到本地
 * @param {String} filePath 图片临时路径
 **/
export function saveImage(filePath) {
    if (!filePath) return false
    uni.saveImageToPhotosAlbum({
        filePath,
        success: (res) => {
            toast('图片保存成功', 'success')
        },
        fail: (err) => {
            if (err.errMsg === 'saveImageToPhotosAlbum:fail:auth denied' || err.errMsg === 'saveImageToPhotosAlbum:fail auth deny') {
                uni.showModal({
                    title: '提示',
                    content: '需要您授权保存相册',
                    showCancel: false,
                    success: (modalSuccess) => {
                        uni.openSetting({
                            success(settingdata) {
                                if (settingdata.authSetting['scope.writePhotosAlbum']) {
                                    uni.showModal({
                                        title: '提示',
                                        content: '获取权限成功,再次点击图片即可保存',
                                        showCancel: false
                                    })
                                } else {
                                    uni.showModal({
                                        title: '提示',
                                        content: '获取权限失败,将无法保存到相册哦~',
                                        showCancel: false
                                    })
                                }
                            },
                            fail(failData) {
                                console.log('failData', failData)
                            }
                        })
                    }
                })
            }
        }
    })
}


Uni-app分包策略

背景

微信小程序之所以需要分包,主要是为了解决小程序官方限制了主包体积和总体积的大小,如果应用体积超限,我们将不能发布到应用官方,最终会上不了线。整个小程序所有分包大小不能超过20M,单个分包/主包大小不能超过2M

核心思路:

将代码划分成不同的包,打开一个包中的某个 页面,才加载这个包的代码。优化小程序首次启动的下载时间。

  • 主包:默认启动页面/TabBar(标签)页面,以及一些所有分包需要用到的公共资源/JS脚本
  • 分包:根据开发者的配置进行划分
      1. 使用 subpackages 进行分包路径声明,subpackages 配置路径外的目录会被打包到主包中
      1. tabBar 里配置的路径必须放在主包里
      1. 不同的分包之间的资源不能相互引用,但都可引用主包中的资源

TabBar标签页面: image.png

分包的好处:

  • 提高首页加载速度:随着小程序项目规模的增大,首页所需的代码和资源也会越来越多,导致首页加载时间变长,影响用户体验。通过分包,可以将部分代码和资源拆分到其他子包中,在首页加载时只需加载必需的核心代码,从而减少首页的加载时间。
  • 优化性能:小程序的性能对用户体验至关重要。通过分包,可以将一些与首页无关的功能模块或页面、大型资源文件等拆分到子包中,子包的使用也可以帮助有效减少小程序包的体积,提升小程序的加载速度。
  • 分包预下载:分包可以提前加载用户即将使用的功能模块,从而加快跳转到对应页面的速度。通过合理的分包策略和预下载机制,可以在用户交互前就将页面所需的代码和资源提前加载好,确保用户流畅的使用体验。

制定合理的分包策略

小程序包拆分为主包和子包,其中主包包含了小程序的首页和一些常用基础功能模块,而子包则包含了其他功能模块和页面。主包在用户第一次打开小程序时会被下载和加载,而子包则根据需要来动态下载和加载。 制定分包策略的建议:

  • 根据功能模块拆分:将小程序的功能模块拆分成不同的子包。比如:tabbar 模块、用户模块、推送模块等等。
  • 根据资源引用拆分:自定义组件、JS 文件、静态资源仅被一个分包使用时则把它划为同一个分包中,如果是公共的资源被各个分包使用,则将其划为主包子云啊
  • 分包预下载配置:通过分包预下载机制,在用户需要时能够快速加载,配置 preloadRule 后,在进入小程序某个页面时,由框架自动预下载可能需要的分包,提升进入后续分包页面时的启动速度,减少用户等待时间,提升用户体验。

具体分包操作

  • 项目的目录结构

image.png

  • 使用optimization属性开启分包优化
    • manifest.json文件中加入"optimization": {"subPackages": true}
  • pages.json,使用subPackages配置分包信息,定义每个子包的路径、名称和需要包含的页面
// pages.json
{
  //主包:只存放Tabbar页面及公共页面
  “pages”:[
  
  ],
  "subPackages":[
    {
      "root":"packageA",  //分包的根目录
      "pages":[{"path":"detail/index"}]  //该分包下的所有页面
    }
  ]
}

分包预下载

分包预下载:进入小程序某个页面时,框架自动预下载可能需要的分包,提升进入后续分包页面时的启动速度。 在pages.json文件的preloadRule节点中配置分包预下载规则,预下载的行为,会在进入指定的页面时触发。

{
  "preloadRule": { // 分包预下载规则配置
    "packageA/detail/index": { // 触发分包预下载的页面路径
      // network 表示在指定的网络模式下进行预下载
      // 可选值为:all(不限网络) 和 wifi(仅 wifi 模式下进行预下载)
      // 默认值为:wifi
      "network": "all",
      // packages 表示进入页面后预下载哪些分包
      // 可以通过 root 或 name 指定预下载哪些分包
      // 如果是 __APP__ 表示下载所有包
      "packages": ["packageA"]
    }
  },
}