app 端 uniapp 是默认开启 scoped 的!!
什么叫做基座呢?现在你制作好了一个 app 的代码,要运行到手机上,而且想要实现一个效果,就是电脑端这边的代码一改,手机上显示的页面马上变化,那你说这个功能谁来提供呢?就是一个叫做安卓基座的东西,以下详细介绍:
-
基座的本质 基座是一个预编译的安卓原生应用框架(APK),包含以下核心组件:
- WebView 引擎:用于渲染 HTML/CSS/JS 构成的
uni-app页面。 - JavaScript 引擎(如 V8):解析执行
uni-app的业务逻辑代码。 - 原生插件桥接层:通过
uni.requireNativePlugin调用原生功能(如相机、蓝牙)。 - 热更新模块:支持代码修改后实时同步到设备,无需重新安装 APK。
- WebView 引擎:用于渲染 HTML/CSS/JS 构成的
-
与完整 APK 的区别
- 基座是非完整应用,仅提供运行环境,不包含业务代码。
- 业务代码(
Vue/JS文件)通过热更新动态注入基座,实现快速调试。
首先要指定 adb 路径,什么是 adb?
本质就是一个让电脑可以使用某种协议和手机连接,电脑可以传输命令给手机,手机端可以执行命令的工具,举个例子:
典型工作流程
- 用户输入命令
adb install app.apk。 - ADB Client 连接本机 ADB Server(端口 5037)。
- Server 检测目标设备,将命令转发至设备的
adbd进程。 adbd执行 APK 安装操作,返回结果给 Server。- Server 将结果传回 Client,终端显示安装成功与否。
好,介绍完 adb 是什么,刚刚的图片中提到了:
adb 默认路径就是你电脑里安装的 adb,在环境变量里面全局安装的那一个,当然你也可以指定,安卓模拟器的安装目录录也是自带一个 adb 的。
好,那接着来介绍:是什么?以
mumu 模拟器举例:
MuMu主程序端口:模拟器作为Windows应用运行时占用的端口,用于管理窗口/输入/虚拟机等自身功能。ADB调试端口:模拟器内部Android系统的调试入口,专供adb命令连接虚拟设备。
一个安卓模拟器会有两个重要的端口,一个就是安卓模拟器这个程序在 Windows 里需要占据一个端口来进行运行,另外一个是 adb 调试端口,怎么理解?安卓模拟器本质上是一个 Windows 桌面应用然后内部包了一个安卓虚拟机,这个安卓虚拟机这是一个沙箱构造,就是里面的内容是不能出去的,这也保证安卓模拟机外部是安全的,就算有病毒,也只会在安卓虚拟机内部感染,那现在其实就是你外部要怎么和内部的这个安卓虚拟机进行通信,那其实已经显而易见,对不对?就是 adb 调试端口。
所以图片这里要让你指定一个安卓模拟器端口,指定的是什么?指定的是这个安卓虚拟机的端口,也就是 adb 调试端口。
这个问题是怎么发生的呢?基座版本(4.66)和编译时使用的SDK版本(3.8.12)不匹配,正是造成弹窗警告的根本原因。还记得基座是什么吗?一个存放业务代码的容器,对不对?那这个基座是在什么时候进行下载的呢?在你的安卓模拟器上第一次运行 app 程序的时候,会自动下载这个安卓基座,这个安卓基座里面其实就包含了,比如调用安卓原生系统的照相机,蓝牙等功能,此时 hbuildx 就会下载对应版本的安卓基座,好,那现在容器有了你要往里面放业务代码对不对?那你是不是得先编译你原来的 vue 代码?
"@dcloudio/uni-app": "3.0.0-alpha-3081220230802001",
使用这个包进行编译代码,他的版本是 30812 也就是 3.8.12,那就刚好对上了,那此时要怎么解决这个问题呢?那你就更新一下这个包,把它更新到4.66版本,是不是问题就解决了?或者想办法让我们的基座版本降级。
-
更新到最新正式版
npx @dcloudio/uvm@latest -
更新到最新 Alpha 版
npx @dcloudio/uvm@latest alpha -
更新到正式版指定版本
npx @dcloudio/uvm@latest 3.2.0 -
更新到 Alpha 版指定版本
npx @dcloudio/uvm@latest 3.2.0-alpha
我发现使用 @dcloudio/uvm 这个包可以直接更新项目里的多个依赖版本。就比如我现在有5个依赖,5个依赖都会同时更新,原理是什么?怎么实现的呢?最简单的方式就是你每次调用这个包,就修改 package.json 中的依赖为最新或者指定版本号,然后再执行 npm i 或者 pnpm i。
但是我发现 "@dcloudio/uni-app" 这个包根本就没有 4.66 版本,也就是说,基座版本和代码 sdk 版本一定是不匹配的,那只能屏蔽掉这个兼容性检查弹窗了:
"app-plus": {
"compatible": { //可选,JSON对象,uni-app兼容模式
"ignoreVersion": false, //可选,Boolean类型,是否忽略版本兼容检查提示
}
}
通过在 manifest.json 内部添加这么一个配置来直接忽略这个版本兼容检查提示。
这个按钮是打开 app 调试控制台的,但是在当前 4.66 版本会有 bug,点几次 hbuildx 直接闪退。但是没办法,目前只能使用这种方式调试 app 页面。
编译前原始状态(小程序)
<navigator
class="category-item"
hover-class="none"
open-type="switchTab"
url="/pages/category/category"
>
<!-- 内部内容 -->
</navigator>
编译后转换结果(App)
<uni-navigator class="category-item" data-v-44ae5a81="">
<a class="navigator-wrap" href="/pages/category/category"></a>
<!-- 内部内容转换结果 -->
</uni-navigator>
这个 a 标签其实是多余的,点击 uni-navigator 内部调用 uni.switchTab 这个 api 进行跳转,这非常好,你的样式作用在 <uni-navigator> 上,也不会因为内部多了一个隐藏的 <a> 标签而错乱。
其他的元素和样式转换规则,其实和 uniapp 小程序转 H5 是一模一样,我在那边的笔记都有记录,去那边看就行,这里着重提一点,就是因为我们的 App 和 H5 几乎一模一样,只是页面容器有区别,还有这个 navgitor 标签有区别,APP 多了原生的导航栏和 tabBar,其他的几乎一模一样,那同样也会出现一个问题,就比如我现在这一个页面有 200 张图片,如果我没有对图片进行懒加载的话,200 个请求一下子就出去了,所以呢使用条件编译配合 img 标签实现懒加载:
<!-- #ifdef H5 || APP-PLUS -->
<img :src="item.picture" alt="" class="picture" loading="lazy" />
<!-- #endif -->
有同学会问,为什么不用 uniapp 提供的 image 标签实现懒加载呢?因为 image 标签的懒加载功能在我们的 App 端是实现不了的呢。
为什么会出现这个问题,就是 .viewport 底部的 margin-bottom 失效了?这个问题解决了:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<style>
html, body {
height: 100%;
margin: 0;
padding: 0;
}
.main {
width: 500px;
height: 100%;
background-color: red;
overflow: hidden;
}
.box {
height: 100%;
background-color: blue;
margin-bottom: 50px;
}
</style>
</head>
<body>
<div class="main">
<div class="box"></div>
</div>
</body>
</html>
以上代码展示的效果是:
可以注意到 margin-bottom 完美隐身,和我们之前遇到的情况一样,因为 .main 占据视口高度,.box 也是占据视口高度,此时的 margin-bottom 被 overflow: hidden; 隐藏掉了。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<style>
html, body {
height: 100%;
margin: 0;
padding: 0;
}
.main {
display: flex; // 新添加的
flex-direction: column; // 新添加的
width: 500px;
height: 100%;
background-color: red;
overflow: hidden;
}
.box {
height: 100%;
background-color: blue;
margin-bottom: 50px;
}
</style>
</head>
<body>
<div class="main">
<div class="box"></div>
</div>
</body>
</html>
新的效果是:
此时可以看见 margin-bottom 又回来了,我猜测此时的 height: 100% 其实相当于一个 flex: 1 的效果,所以可以看见 margin-bottom,那上面这个 bug 其实就已经解决掉了。
所以我们就知道了,在 app 端,你要保持和小程序端一样的布局,小程序端的页面容器是 page,app 端的页面容器是 #app 元素,在 app 中通过在全局文件中进行条件编译来设置页面容器的样式:
/* #ifdef APP-PLUS */
#app {
height: 100%;
display: flex;
flex-direction: column;
overflow: hidden;
background-color: #f7f7f8;
}
/* #endif */
那你肯定还会有一个疑惑,导航栏和 tabBar 为什么没有在这个 webview 调试控制台显示出来呢?
-
原生组件渲染机制:
UniApp在编译为 App 时,导航栏(NavigationBar)和TabBar是由原生系统(Android/iOS)渲染的,而非通过 WebView。- 您的页面内容(
Vue组件)则运行在 WebView 内部,因此调试工具只能捕获 WebView 中的内容。
-
调试工具的局限性:
- WebView 调试工具只能访问 WebView 内部的 DOM 结构。
- 原生导航栏和
TabBar是覆盖在 WebView 上层的独立控件,不在 WebView 的 DOM 树中,因此不可见。
|-------------------------------|
| 导航栏 | → 系统原生渲染(状态栏)
|-------------------------------|
| |
| WebView 页面内容区域 | → 您的 Vue 页面(可被调试工具捕获)
| |
|-------------------------------|
| TabBar (Native TabBar) | → UniApp 通过原生代码渲染
|-------------------------------|
这和 uniapp 生成的网页端很不一样,因为 uniapp 生成的网页端,导航栏和 TabBar 都是使用 dom 元素生成的。
小程序特有功能,比如微信登录,又比如微信专属的动画效果:
还有微信特有的按钮:
这些都得用条件编译给他去掉。
uniapp 打包的 app 实现了和微信小程序一样的缓存页面栈,就是会自动缓存已经访问并且没有销毁的页面,核心原理是什么?
接下来解决 H5 端和 app 端骨架屏失效的问题,为什么微信小程序没问题,在这边就会有问题呢?先来介绍微信小程序的骨架屏为什么可以正常显示:
接下来再来介绍为什么 H5 端跟 App 端不能正常显示骨架屏:
/* #ifdef H5 || APP-PLUS */
/* 引入骨架屏宽高样式 */
@import './style/CategoryPanel.scss';
@import './style/HotPanel.scss';
@import '@/components/style/XtxSwiper.scss';
@import '@/components/style/XtxGuess.scss';
/* #endif */
在骨架屏组件内部使用这种方式来引入骨架屏的宽高样式,记住啊,这里后缀要加 .scss,不然会找不到这个文件,因为 vite 默认不会解析 .scss 后缀文件,
*// Vite 默认的解析器会尝试查找这些扩展名* const defaultResolveExtensions = [ '.mjs', '.js', '.ts', '.jsx', '.tsx', '.json', '.vue', '.svelte', '.marko', '.astro' *// 框架文件扩展* ];
也可以在 vite 中配置:
这样就可以正确识别了,不过最简单的还是把路径写全。
为什么打包的时候需要提供一个 App 证书?明明就是使用 uniapp 提供的安卓基座在手机上是可以运行这么一个 App 的。这个时候为什么不用证书呢?
adb 和 USB 传输 正是开发阶段无需手动处理证书的关键。下面详解这背后的工作原理和限制:
🔧 一、adb 调试的本质:临时特权通道
当你通过 USB 连接手机并运行 adb install app-debug.apk 时:
-
设备层面:
- 手机需开启 USB 调试模式(开发者选项内)。
- 首次连接时需授权电脑 调试权限(弹出“允许 USB 调试吗?”)。
-
系统行为:
adb进程获得临时特权 → 可绕过常规安装器的证书严格校验。- 自动使用 调试证书(如
debug.keystore)签名并静默安装。
✅ 这就是为什么你“点一下就能装” ——本质是系统为开发者开了“后门”!
🚫 二、USB 调试的局限性
1. 无法分发给他人
-
好友手机需同时满足:
- 开启 USB 调试(普通用户不会开)
- 用数据线连接你的电脑(物理隔离)
-
微信/QQ 发送 APK → 安装时走 普通安装流程,无证书即被拦截。
2. 调试包无法上架商店
-
用
debug.keystore签名的 APK:- 包名强制包含
.debug后缀 - 密钥有效期仅 365 天 → 过期后无法更新
- 包名强制包含
3. 设备管理麻烦
- 每台测试手机都需连接电脑执行
adb install→ 不适用于大规模测试。
生成安卓证书是应用上架的关键步骤,以下是开发者自己生成证书的几种方案:
方案一:Android Studio 生成
不使用,所以就不详细描述了。
方案二:通过 uni-app 本地生成(HBuilderX)
步骤
1.使用云打包,其实就是 uniapp 官方帮你打包 apk:
2.生成应用标识和应用名称:
应用标识必须是你要做一些操作之前,你必须登录嘛,这个应用标识只有你登录了 hbuildx 的账号之后才能获取,它就防止你游客可以使用云打包功能。
应用名称就是 App 名称。
3.设置应用图标:
4.进行云打包配置:
5.生成云端证书,点击 使用云端证书 右边的 详情,进入 uniapp 的开发者中心
此时云端证书就生成好了。
6.选择快速安心打包:
设置隐私弹窗,隐私弹框配置如下:
{
"version": "1", // 隐私政策版本号(必填)。应用升级时需提高版本号以重新弹出协议框
"prompt": "template", // 是否使用原生隐私弹框:'template'-启用模板样式,'none'-不启用
"title": "服务协议和隐私政策", // 主弹框标题文本内容(必填)
"message": " 请你务必审慎阅读、充分理解“服务协议”和“隐私政策”各条款...", // 主弹框富文本内容(必填),支持a/font/br等HTML标签
"buttonAccept": "同意并接受", // 主弹框同意按钮文本(默认值"同意")
"buttonRefuse": "暂不同意", // 主弹框拒绝按钮文本(留空则不显示此按钮)
"hrefLoader": "system", // 链接加载方式:'system'-系统浏览器(不支持项目内路径),'default'-应用内置Webview(默认)
"backToExit": "false", // 点返回键是否退出应用:true-允许退出,false-禁止退出(默认)。应用市场审核敏感项
"second": { // 二次确认弹窗配置(message不为空时触发)
"title": "确认提示", // 二次弹窗标题
"message": " 进入应用前,你需先同意...", // 二次弹窗富文本内容
"buttonAccept": "同意并继续", // 二次弹窗同意按钮文本
"buttonRefuse": "退出应用" // 二次弹窗拒绝按钮文本
},
"disagreeMode": { // 用户拒绝协议的响应配置
"support": false, // 是否启用游客模式:true-拒绝后进游客态,false-直接退出(默认)
"loadNativePlugins": false, // 拒绝时是否加载原生插件:true-加载(默认),false-禁用插件
"visitorEntry": false, // 是否显示游客按钮(HBuilderX 3.6.7+):true-显示额外游客按钮
"showAlways": true // 拒绝后下次启动是否再弹:true-持续弹出,false-仅弹一次(默认)
},
"styles": { // 视觉样式定制
"backgroundColor": "#FFFFFF", // 弹框背景色(#RRGGBB格式)
"borderRadius": "12px", // 弹框圆角值(逻辑像素单位px)
"title": { // 标题样式
"color": "#1A1A1A" // 标题文本颜色
},
"buttonAccept": { // 同意按钮样式
"color": "#4285F4" // 按钮文本颜色
},
"buttonRefuse": { // 拒绝按钮样式
"color": "#5F6368" // 按钮文本颜色
},
// HBuilderX 3.6.7+ 新增
"buttonVisitor": { // 游客按钮样式(需配合disagreeMode.visitorEntry使用)
"color": "#FF9800" // 游客按钮文本颜色
}
}
}
没有在上面写出来的属性都是完全不支持的,
这里说是这么说,经过我的测试,你这个属性不管你怎么设置?你拒绝协议后,下次默认还是继续弹出的。
over。