解决方案:uni-app非nvue模式下切换主题后,App端闪屏、闪白、闪黑、状态栏主题色不稳定

  • 主题切换采用的方案是 css 变量,但此方案理论上可以解决任何因为切换主题而引起的闪屏
  • 使用的编译版本是 3.1.X (也就是HBuilderx的版本号)
  • 2.6.X 之后禁止在js内动态引入css。因为2.6.X之后好像默认都是V3版本编译,所以这是一条死路

问题初探:
uni-app 使用非nvue模式进行编译App,会使用 web-view进行渲染。调试过程中发现,在进行加载静态资源的时候会首先读取默认的配置文件,并进行App主题的初始化,并且在加载其它页面的时候也会去重载默认的配置文件,然后再以加载我们正常的业务页面,在渲染业务页面之前的这段时间内,主题色会一直保持manifest.json内默认的配置色,或在main.js文件中预先加载的主题色。所以以这个思路去处理一下加载的静态文件,以处理切换主题色后闪屏的问题。

解决步骤

给App添加主题色的缓存标识

1
plus.storage.setItem("theme", this.color)

这段代码是通过 HTML5+的Api,为App添加主题标识的缓存。最佳放置位置为业务页面中,切换主题色的函数内(点击按钮切换主题时触发的函数内)。

新建 __uniappview.html 文件

__uniappview.html 文件是uni-app App 模式的入口页,所以在这里修改比较合理

__uniappview.html 源文件:github

新建:__uniappview.html (建议放至 [ assets ] 目录下,默认是没有这个目录)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
<!DOCTYPE html>
<html lang="zh-CN">

<head>
<meta charset="UTF-8" />
<script>
var __UniViewStartTime__ = Date.now();
var coverSupport = 'CSS' in window && typeof CSS.supports === 'function' && (CSS.supports('top: env(a)') ||
CSS.supports('top: constant(a)'))
document.write(
'<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0' +
(coverSupport ? ', viewport-fit=cover' : '') + '" />')
</script>
<title>View</title>

<script type="text/javascript">
function PlusReady() {

if (typeof PlusReady.instance === 'object') {
return PlusReady.instance
}

// 获取app缓存的主题类型
this.color = plus.storage.getItem('theme')
this.cssPath = ''
// 根节点,用于保存页面根样式,防止切换页面闪屏的样式
this.style = document.createElement('style')
this.link = document.createElement('link')
// dom 是否更新过
this.domStatus = false

this.setTheme = function() {
// 依据主题类型初始化主题相关值
if (this.color == 'black') {
plus.navigator.setStatusBarStyle('light') // 设置原生bar主题为浅色
this.cssPath = "static/css/black.css" // 加载深色主题
this.style.innerText =
`
body, html, #app, page {
background: #1E1E1E !important;
}
` // 设置根节点为深色,防止切换页面闪白
} else {
plus.navigator.setStatusBarStyle("dark") // 设置原生bar主题为深色
this.cssPath = "static/css/white.css" // 加载浅色主题
this.style.innerText =
`
body, html, #app, page {
background: #FFFFFF !important;
}
` // 设置根节点为浅色,防止切换页面闪黑
}
// 初始化link
this.link.type = 'text/css';
this.link.rel = 'stylesheet';
this.link.href = this.cssPath;
}

this.pushHead = function() {
// 节点载入head
document.head.appendChild(this.link)
document.head.appendChild(this.style)
}

return PlusReady.instance = this
}
function plusReadyTheme (str) {
let plusReady = new PlusReady()
plusReady.color = plus.storage.getItem("theme")
plusReady.setTheme()
}
if (window.plus) {
let plusReady = new PlusReady()
plusReady.setTheme()
plusReady.pushHead()
} else {
// h5+ 加载后,初始化参数并添加dom到head
document.addEventListener('plusready', function() {
let plusReady = new PlusReady()
plusReady.setTheme()
if (!plusReady.domStatus) {
plusReady.pushHead()
}
}, false);
}
</script>
<link rel="stylesheet" href="view.css" />
</head>

<body>
<div id="app"></div>
<script src="__uniappes6.js"></script>
<script src="view.umd.min.js"></script>
<script src="app-view.js"></script>
</body>

</html>

在调试过程中发现,首先加载的是这个HTML,所以把设置主题色的代码放在了这个HTML
。这个HTML是在编译过程中导入的入口文件,每次离线打包后也可看到这个文件。

引入 copy-webpack-plugin 依赖

项目内使用的是:5.0.0 版本

1
npm install -S copy-webpack-plugin

创建 vue.config.js 文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
const path = require('path')
const CopyWebpackPlugin = require('copy-webpack-plugin')

module.exports = {
configureWebpack: {
plugins: [
new CopyWebpackPlugin([
{
from: path.join(__dirname, 'assets'), // 项目内把 __uniappview.html 文件放在了 assets 目录下
force: true // 强制覆盖
}
])
]
}
}

状态栏颜色不稳定

这个问题也是默认配置的坑,初步观察的原理很简单,就是页面已经渲染出来了,但是又做了跳转当前页面动作、之类的动作,导致页面没有重新加载,但是配置重新加载了~ 所以uni-app的壳就把状态栏主题色恢复为默认了。
解决办法就是只要当前页面栈已经存在某个页面,就不做跳转之类的动作!

结论

以上方案,可解决闪屏问题,但是有一个小问题。就是在切换主题色后,切换的第一个页面还会闪一下。业务至此,接着搬下一块砖!!! 有更好的解决方案,或谁把这个遗留问题解决了下方评论,万分感激~