app
Hybrid
webview
嵌入在原生应用中 ,实现前端的混合式开发,大多数 Hybrid App 混合式开发框架都是基于WebView模式进行二次开发的。 webview就是原生应用中的浏览器引擎。
运行在webview中的JS代码有能力调用原生的系统API,没有传统浏览器沙箱的限制。沙箱的存在是因为,你永远不能完全信任加载的web内容,所以不能允许它调用原生的系统API。而在webview中开发人员通常可以完全控制加载的内容,恶意代码进入并在设备上造成混乱的可能性很低。
Android容器 在安卓客户端中,webView容器与手机自带的浏览器内核一致,多为android-chrome。不存在兼容性和性能问题。
RN容器 在react-native开发中,从rn 0.37版本开始官方引入了组件,在安卓中调用原生浏览器,在IOS中默认调用的是UIWebView容器。从IOS12开始,苹果正式弃用UIWebView,统一采用WKWebView。
通信
前端通知客户端
在H5页面里触发链接跳转,App Webview检测到链接跳转再进行拦截,因此可以通过URL上携带参数来告知App下一步应该做些什么。
1 | import React, { Component } from "react"; |
以上执行了location.href = "lsbox://mask?toggle=1"
来通知App打开遮罩层
lsbox
:前端和客户端统一定义链接跳转的协议(喜欢怎样定义协议都行)mask
:App需要执行的动作(喜欢怎样定义动作都行)toggle=1
:动作执行参数(自定义参数,用于告知App怎样做)
1 | 如果同步触发两个或以上的location.href(下一个location.href接着上一个location.href),App可能只会接收到一个location.href发出的通知,所以需要对下一个location.href使用setTimeout设置通知时间间隔(可使用Async/Await封装优化) |
客户端通知前端
使用window.handleFunc = function() {}
这样的形式来定义注入的方法。
1 | import React, { Component } from "react"; |
以上在组件加载完成后通过window.addNum = this.addNum.bind(this)
将指定方法全局暴露到window
上,App Webview可直接操作这些方法来控制H5页面
JSBridge
是 Native 和非 Native 之间的桥梁 ,简单来讲,主要是 给 JavaScript 提供调用 Native 功能的接口,让混合开发中的『前端部分』可以方便地使用地址位置、摄像头甚至支付等 Native 功能。
electron
项目搭建
package.json 中指定的 main
文件是 Electron 应用的入口。 这个文件控制 **主程序 (main process)**,它运行在 Node.js 环境里,负责控制您应用的生命周期、显示原生界面、执行特殊操作并管理渲染器进程 (renderer processes)
在 package.json 的 scripts
字段中添加一个 start
命令,内容为 electron .
。 这个命令会告诉 Electron 在当前目录下寻找主脚本,并以开发模式运行它。
1 | { |
vue3
vue3+elc模板:npm create vite@latest electron-vue3-demo – –template vue-ts
npm install electron -D
electron-builder打包工具:将已有的electron应用打包成msi格式和exe可执行文件的工具
npm install electron-builder -Dvite 结合 electron 的库
npm install vite-plugin-electron -D
Vue3项目转electron
https://www.bilibili.com/video/BV1SS4y1h7CL/?vd_source=f2241cf7d6cc0ae541b49e6fd828988b
vue2
- vue-cli-plugin-electron-builder:https://nklayman.github.io/vue-cli-plugin-electron-builder/guide/configuration.html
调试
依赖工具
rimraf:快速删除文件或目录工具
1
2
3在打包生成的文件夹中,会有一个app.asar,它是Electron应用程序的主业务文件压缩包,要知道项目中哪些文件被pack到安装包,可以通过解压app.asar进行查看
npm i asar -g
asar extract app.asar ./app-folderelectron-devtools-installer:electron 开发工具
electron-updater模块检测更新
配置
nsis
1 | createDesktopShortcut表示创建快捷方式图标 |
加载资源
https://www.jianshu.com/p/43e8a2f04422
重点看resource文件夹下的app.asar文件,这个文件就是electron-builder打包后的应用入口文件
添加files配置项:electron-builder配置项files中包含的文件都在
应用程序根目录/resources/app.asar/
目录下。1
2
3
4
5
6//electron-builder.json
{
productName: 'xxx',
appId: 'xxx',
files: ['./main', './build'], // 包含指定文件
}它们的作用就是打包额外的文件
配置
extraFiles
后,electron-builder会在打包时将extraFiles
中指定的文件复制到打包后应用程序的根目录下(Windows/Linux)
,或者Content目录下(MacOS)
配置
extraResources
后,electron-builder会在打包时将extraResources
中指定的文件复制到打包后应用程序的根目录/resources文件夹下(Windows)
,或者Content/resources文件夹下(MacOS)
- extraFiles
extraFiles
为额外文件 - extraResources
extraResources
为额外资源
比如有这么一个需求:在用户没有网络时,为用户提供某个静态文件的下载;那就必须将这个静态文件打包进应用程序,用户下载时使用electron主进程的node程序读取这个文件返回给用户。
- extraFiles
安装包放置
服务器
1 | 第一步、在build中配置"publish"字段: |
github
1 | 第一步,依然是配置"publish"字段。 |
检查更新
1 | const { autoUpdater } = require('electron-updater') |
概念
Electron 是利用 web 前端技术进行桌面应用开发的一套框架,它是由 Github 开发的,利用HTML、CSS、JavaScript 来构建跨平台桌面应用程序的一个开源库。Electron 通过将 Chromium 和 Node.js 合并到同一个运行时环境中,并将其打包成 Mac、Windowns、Linux 系统下的应用来实现这一目的。
chrome 和 chromium 用户界面几乎一摸一样,但是还是有一些差异的。比如 chrome 的系统标题和边框是被默认禁用的,而 chromium 是默认开启的。另外chrome地址栏里有分享功能,我们可以分享网站,但是 chromium 没有这个功能。界面的细微差别主要是这两个浏览器面向的用户是不一样的,一般来说使用 chrome 的用户是普通用户,用它来浏览网页使用,而使用 chromium 大部分是极客、开发人员和体验新功能的的用户。
典型的三层结构, 和 MVC 非常相似:
- M – 通用混合层。 C/C++ 封装核心、通用的业务模块以及业务数据存储。
- V – UI 层。视图层,使用跨平台视图解决方案,对于性能要求较高的部分使用原生实现。比如 Electron
- C – 平台桥接层。介于 M 和 V 之间,桥接
通用混合层
接口,同时也为 UI 层暴露一些平台相关的特性。比如在桌面端,这里会通过 Node 原生模块桥接通用混合层, 同时也补充一些 Electron 缺失或不完美的功能。
Main
BrowserWindow
在 Electron 中,每个窗口展示一个页面,后者可以来自本地的 HTML,也可以来自远程 URL
1 | const { app, BrowserWindow } = require('electron') |
- app,它着您应用程序的事件生命周期。
- BrowserWindow,它负责创建和管理应用窗口。
Electron 的许多核心模块都是 Node.js 的事件触发器,遵循 Node.js 的异步事件驱动架构。 app 模块就是其中一个。
在 Electron 中,只有在 app 模块的
ready
事件触发后才能创建 BrowserWindows 实例。 您可以通过使用app.whenReady()
API 来监听此事件,并在其成功后调用createWindow()
方法。
app
关闭所有窗口时退出应用
在 Windows 和 Linux 上,我们通常希望在关闭一个应用的所有窗口后让它退出。 要在您的Electron应用中实现这一点,您可以监听 app 模块的 window-all-closed
事件,并调用 app.quit()
来退出您的应用程序。此方法不适用于 macOS。
1 | app.on('window-all-closed', () => { |
没有窗口打开则打开一个窗口
因为窗口无法在 ready
事件前创建,你应当在你的应用初始化后仅监听 activate
事件。 要实现这个,仅监听 whenReady()
回调即可。
1 | app.whenReady().then(() => { |
preload
Electron 的主进程是一个拥有着完全操作系统访问权限的 Node.js 环境。 除了 Electron 模组 之外,您也可以访问 Node.js 内置模块 和所有通过 npm 安装的包。 另一方面,出于安全原因,渲染进程默认跑在网页页面上,而并非 Node.js里。为了将 Electron 的不同类型的进程桥接在一起,我们需要使用被称为 预加载 的特殊脚本。
BrowserWindow 的预加载脚本运行在具有 HTML DOM 和 Node.js、Electron API 的有限子集访问权限的环境中
预加载脚本在渲染器加载网页之前注入。
渲染器加载时的DOM展示
1 | function domReady( |
contextIsolation
contextIsolation
定义上下文隔离
在一般的我们的前端项目中,渲染html页面的js中是运行在浏览器环境中的,而在Electron中,我们会发现Node中模块在其中也可以使用。(同时需要配置nodeIntegration
为true
)这样的话,在一个复杂项目中,就可以造成污染,重名等问题。
于是Electron特意增加了上下文隔离这一概念。开启上下文隔离的条件是contextIsolation
属性设置为true
一旦开启该条件,渲染页面的js中无法引入Electron和Node的各种模块。因此,如果想在其中使用,需要配置preload.js,使用contextBridge
(上下文桥,这个名字不错)来暴露全局接口到渲染页面的脚本中
使用 contextBridge
和 preload
:通过 contextBridge
和 preload
机制,可以安全地将部分 Electron API 暴露给渲染进程,而无需开启 nodeIntegration
。这种方式更安全,能够限制渲染进程的权限,并减少潜在的安全风险。你可以在主进程中使用 contextBridge.exposeInMainWorld
方法将需要的 Electron API 暴露给渲染进程,然后在渲染进程中使用 window.api
来访问这些 API。
contextBridge通信
如果你想为渲染器添加需要特殊权限的功能,可以通过 contextBridge 接口定义 全局对象。
1 | //创建一个将应用中的 Chrome、Node、Electron 版本号暴露至渲染器的预加载脚本 |
1 | //为了将脚本附在渲染进程上,在 BrowserWindow 构造器中使用 webPreferences.preload 传入脚本的路径。 |
1 | const information = document.getElementById('info') |
虽然预加载脚本与其所附着的渲染器在共享着一个全局
window
对象,但您并不能从中直接附加任何变动到window
之上,因为contextIsolation
是默认的。语境隔离(Context Isolation)意味着预加载脚本与渲染器的主要运行环境是隔离开来的,以避免泄漏任何具特权的 API 到您的网页内容代码中。
preload.js
1
2
3 window.myAPI = {
desktop: true
}renderer.js
1 console.log(window.myAPI)
与Typescript一同使用
在这个 preload.ts
脚本中:
preload.ts
1 | contextBridge.exposeInMainWorld('electronAPI', { |
您可以创建一个 renderer.d.ts
类型声明文件,并且全局增强 Window
接口。
renderer.d.ts
1 | export interface IElectronAPI { |
以上所做皆是为了确保在您编写渲染进程的脚本时, TypeScript 编译器将会知晓electronAPI
合适地在您的全局window
对象中
renderer.ts
1 | window.electronAPI.loadPreferences() |
IPC进程间通信
官方文档:
ipcMain从主进程到渲染进程的异步通信。
ipcMain
是一个 EventEmitter 的实例。 当在主进程中使用时,它处理从渲染器进程(网页)发送出来的异步和同步信息。 从渲染器进程发送的消息将被发送到该模块。ipcRenderer:从渲染器进程到主进程的异步通信。
ipcRenderer
是一个 EventEmitter 的实例。 你可以使用它提供的一些方法从渲染进程 (web 页面) 发送同步或异步的消息到主进程。 也可以接收主进程回复的消息。
进程模型
主进程:每个 Electron 应用都有一个单一的主进程,作为应用程序的入口点。 主进程在 Node.js 环境中运行,这意味着它具有
require
模块和使用所有 Node.js API 的能力。- 进程间通信
- 窗口管理。使用
BrowserWindow
模块创建和管理应用程序窗口 - 全局通用服务。
- 一些只能或适合在主进程做的事情。例如浏览器下载、全局快捷键处理、托盘、session。
- 维护一些必要的全局状态
- 上面说的
通用混合层
也跑在这个进程。通过 Node C++ 插件暴露接口。
渲染进程(Renderer Process):每个 Electron 应用有一个或多个渲染进程, 对应多个 Web 页面
除此之外还有 GPU 进程、扩展进程等等。
ipcMain
ipcMain从主进程到渲染进程的异步通信。ipcMain
是一个 EventEmitter 的实例。 当在主进程中使用时,它处理从渲染器进程(网页)发送出来的异步和同步信息。 从渲染器进程发送的消息将被发送到该模块。
同步
ipcMain.on(channel, listener) 主进程监听来自渲染进程的通信,
**ipcMain.once(channel, listener)**只监听一次事件
ipcMain.removeListener(channel, listener) 参数跟定义时的监听函数一致才能被移除
ipcMain.removeAllListeners([channel])
异步的来回通信
ipcMain.handle(channel, listener) 主进程的监听函数
ipcRenderer.invoke(channel, …args) 渲染进程的发送消息的函数
ipcMain.handleOnce(channel, listener) 单个执行一次
**ipcMain.removeHandler(channel)**移除监听函数
1 | send 和 on |
渲染进程->主进程并返回数据
1 | //preload.js |
1 | //main.js |
1 | //renderer.js |
渲染器进程到主进程(单向)
渲染线程 到 主线程 需要通过 ipcRenderer.send
发送 —> ipcMain.on
来监听
ipcMain.on 和 ipcRenderer.send
示例描述:从 渲染进程中操作主进程的关闭操作,也就是从渲染进程中关闭electron
(1)主进程:electron/main.js
1 | const { ipcMain } = require('electron'); |
(2)预加载文件:electron/preload.js
1 | //向渲染器进程暴露一个全局的 window.electronAPI 变量。 |
(3)渲染进程文件:xxx.vue
1 | <div> |
渲染器进程到主进程(双向)
ipcMain.handle 和 ipcRenderer.invoke
示例描述:从渲染进程中 调用 主进程的弹窗选择文件后返回文件路径给 渲染进程
- 在主进程中,我们将创建一个 handleFileOpen() 函数,它调用electron的 dialog.showOpenDialog 并返回用户选择的文件路径值。
- 每当渲染器进程通过 dialog:openFile 通道发送 ipcRender.invoke 消息时,此函数被用作一个回调。然后,返回值将作为一个 Promise 返回到最初的 invoke 调用。
1)主进程:electron/main.js
1 | const { ipcMain,dialog } = require('electron'); |
(2)预加载文件:electron/preload.js
1 | import { contextBridge, ipcRenderer } from 'electron' |
(3)渲染进程文件:xxx.vue
1 | <div> |
主进程到渲染器进程
主线程 到 渲染线程 通过 webContents.send
来发送 —>ipcRenderer.on
来监听
Electron 中的消息端口Remote 模块
在渲染进程里如果要使用到 BrowserWindow 这些属性的话就必须使用 remote
,使用 remote
模块, 你可以调用 main
进程对象的方法
配置
窗口
https://juejin.cn/post/6888907382806544392#heading-12
顶部标题栏配置
1 | new BrowserWindow({ |
标题栏title和html中的title是一样的
自定义
https://www.toutiao.com/article/7146401504377799180/?wid=1690858382324
1 | new BrowserWindow({ |
1 | <template> |
菜单
https://www.misterma.com/archives/896/
https://zhuanlan.zhihu.com/p/426436147
菜单栏图标
1 | { |
托盘
系统托盘图标
1 | let iconPath = path.join(__static, "./zero.ico"); |
系统托盘自动消失
https://blog.csdn.net/liu19721018/article/details/109046186
dialog
1 | ipcMain.on('logout',(e)=>{ |
API
生命周期
ready: app 初始化完成
dom-ready: 一个窗口中的文本加载完成
did-finish-load: 导航完成时触发
closed: 当窗口关闭时触发,此时应删除窗口引用
window-all-closed: 所有窗口都被关闭时触发
退出
before-quit: 在关闭窗口之前触发
will-quit: 在窗口关闭并且应用退出时触发
quit: 当所有窗口被关闭时触发
退出方式
默认退出
如果没有监听
window-all-closed
事件并且所有窗口都关闭了,默认的行为是退出程序如果监听了
window-all-closed
事件,那么在你的所有窗口都关闭时会去执行
quit退出
当开发者调用了 app.quit(),如果此时所有窗口已经关闭,直接触发quit事件;否则Electron 会首先触发before-quit,然后开始关闭所有的窗口并且触发 will-quit事件,在这种情况下 window-all-closed 事件不会被触发,所以你可以放心在window-all-closed里使用app.quit(),而不用担心会出现无限递归。
调用app.quit()并不能保证程序一定会退出,在before-quit,will-quit中调用event.preventDefault()或者在window的close事件回调函数中阻止窗口关闭,都可以使程序退出失败。也就是说quit不是独裁者,更像是众议院,老大提出退出操作,然后众人商量,退出的愿望可能被否决
exit退出
既然有
app.quit
这种温和的退出方式,那么也会有更加粗暴的退出方案,那就是app.exit
。与
quit
这种民主相比,exit
更像是独裁者,他会直接执行退出操作,不跟任何人商量,只要保证你的程序中的逻辑合理,是完全可以拿来使用的
process
process.platform
变量,您可以针对特定平台运行特定代码。 请注意,Electron 目前只支持三个平台:win32
(Windows), linux
(Linux) 和 darwin
(macOS) 。
cookies
electron webview clear cookies
Electron Webview 是 Electron 框架中的一个标签,用于在桌面应用中显示网页内容。要清除 Webview 中的 cookies,可以使用以下方法之一:
- 在 Webview 加载网页之前,使用
session.defaultSession.cookies.remove()
方法删除所有 cookies。例如:
1 | lua复制代码const {session} = require('electron') |
- 在 Webview 加载网页之后,使用
webview.getWebContents().session.clearStorageData()
方法清除所有本地存储数据,包括 cookies。例如:
1 | go复制代码const {webContents} = require('electron') |
注意:需要确保在调用这些方法之前已经加载了 Webview 标签。
网站信息
app.setAboutPanelOptions(options)
- 选项 对象
- applicationName string (可选) - 应用程序的名字
- applicationVersion string (可选) - 应用程序版本
- copyright string (可选) - 版权信息
- version string (可选) macOS - 应用程序版本号
- credits string (可选) macOS Windows - 信用信息。
- authors string[] (可选) Linux - 应用程序作者列表。
- website string (可选) Linux - 应用程序的网站。
- iconPath string (可选) Linux Windows - 以JPEG 或 PNG 文件格式为应用程序图标路径。 在 Linux 上,将显示为 64x64 像素,同时保留纵横比。
事件
(1)、ready 当electron完成初始化时被触发
app.on(‘ready’, () => {
createWindow();
})
(2)、window-all-closed 所有窗口被关闭
app.on(‘window-all-closed’, function () {
})
(3)、quit 在所有应用程序退出时发出
app.on(‘window-all-closed’, function () {
if (process.platform !== ‘darwin’) {
app.quit()
}
})
(4)、open-file 在应用中要打开一个文件时发出
(5)、open-url 应用中要打开一个URL网址时发出
(6)、activate 当应用被激活时发出
webContents
did-finish-load 导航完成时触发,即选项卡的旋转器将停止旋转,并指派 onload 事件后
mainWindow.webContents.on(‘did-finish-load’,()=>{
console.log(“did-finish-load”)
})
dom-ready 一个框架中的文本加载完成后触发该事件
mainWindow.webContents.on(‘dom-ready’,()=>{
console.log(“dom-ready”)
})
uniapp
目录结构
一个uni-app工程,默认包含如下目录及文件:
1 | ┌─cloudfunctions 云函数目录(阿里云为aliyun,腾讯云为tcb,详见uniCloud) |
资源路径说明
编译到任意平台时,
static
目录下的文件均会被打包进去,非static
目录下的文件(vue、js、css 等)被引用到才会被包含进去。static
目录下的js
文件不会被编译,如果里面有es6
的代码,不经过转换直接运行,在手机设备上会报错。css
、less/scss
等资源同样不要放在static
目录下,建议这些公用的资源放在common
目录下。```
// 绝对路径,@指向项目根目录,在cli项目中@指向src目录
import add from ‘@/common/add.js’1
2
3
4
5
6
7
8
9
10
11
- 本地背景图片/字体文件的引用路径推荐使用以 ~@ 开头的绝对路径。
## 页面样式与布局
`uni-app` 支持的通用 css 单位包括 px、rpx
- px 即屏幕像素
- rpx 即响应式px,一种根据屏幕宽度自适应的动态单位。以750宽的屏幕为基准,750rpx恰好为屏幕宽度。屏幕变宽,rpx 实际显示效果会等比放大,但在 App 端和 H5 端屏幕宽度达到 960px 时,默认将按照 375px 的屏幕宽度进行计算,{
“globalStyle”: {"rpxCalcMaxDeviceWidth": 960, // rpx 计算所支持的最大设备宽度,单位 px,默认值为 960 "rpxCalcBaseDeviceWidth": 375, // rpx 计算使用的基准设备宽度,设备实际宽度超出 rpx 计算所支持的最大设备宽度时将按基准宽度计算,单位 px,默认值为 375 "rpxCalcIncludeWidth": 750 // rpx 计算特殊处理的值,始终按实际的设备宽度计算,单位 rpx,默认值为 750
},
}1
2
3
4
5
6
7
8
9
- match-media组件 为组件指定一组 media query 媒体查询规则
vue页面支持下面这些普通H5单位,但在nvue里不支持:
- rem 根字体大小可以通过 [page-meta](https://uniapp.dcloud.io/component/page-meta?id=page-meta) 配置
- vh viewpoint height,视窗高度,1vh等于视窗高度的1%
- vw viewpoint width,视窗宽度,1vw等于视窗宽度的1%若设计稿宽度为 750px,元素 A 在设计稿上的宽度为 100px,那么元素 A 在 uni-app 里面的宽度应该设为:750 * 100 / 750,结果为:100rpx。
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
**tip:**
- rpx不支持动态横竖屏切换计算,使用rpx建议锁定屏幕方向
## 生命周期
https://uniapp.dcloud.io/collocation/frame/lifecycle?id=%e5%ba%94%e7%94%a8%e7%94%9f%e5%91%bd%e5%91%a8%e6%9c%9f
- `uni-app` 支持如下**应用生命周期**函数:
| 函数名 | 说明 |
| :------------------- | :----------------------------------------------------------- |
| onLaunch | 当`uni-app` 初始化完成时触发(全局只触发一次) |
| onShow | 当 `uni-app` 启动,或从后台进入前台显示 |
| onHide | 当 `uni-app` 从前台进入后台 |
| onError | 当 `uni-app` 报错时触发 |
| onUniNViewMessage | 对 `nvue` 页面发送的数据进行监听,可参考 [nvue 向 vue 通讯](https://uniapp.dcloud.io/use-weex?id=nvue-向-vue-通讯) |
| onUnhandledRejection | 对未处理的 Promise 拒绝事件监听函数(2.8.1+) |
| onPageNotFound | 页面不存在监听函数 |
| onThemeChange | 监听系统主题变化 |
**注意**
- 应用生命周期仅可在`App.vue`中监听,在其它页面监听无效。
- onlaunch里进行页面跳转,如遇白屏报错,请参考https://ask.dcloud.net.cn/article/35942
- **页面生命周期**
| onInit | 监听页面初始化,其参数同 onLoad 参数,为上个页面传递的数据,参数类型为 Object(用于页面传参),触发时机早于 onLoad | 百度小程序 | 3.1.0+ |
| ----------------------------------- | ------------------------------------------------------------ | ------------------------------------------------------------ | ------ |
| onLoad | 监听页面加载,其参数为上个页面传递的数据,参数类型为 Object(用于页面传参),参考[示例](https://uniapp.dcloud.io/api/router?id=navigateto) | | |
| onShow | 监听页面显示。页面每次出现在屏幕上都触发,包括从下级页面点返回露出当前页面 | | |
| onReady | 监听页面初次渲染完成。注意如果渲染速度快,会在页面进入动画完成前触发 | | |
| onHide | 监听页面隐藏 | | |
| onUnload | 监听页面卸载 | | |
| onResize | 监听窗口尺寸变化 | App、微信小程序 | |
| onPullDownRefresh | 监听用户下拉动作,一般用于下拉刷新,参考[示例](https://uniapp.dcloud.io/api/ui/pulldown) | | |
| onReachBottom | 可在pages.json里定义具体页面底部的触发距离[onReachBottomDistance](https://uniapp.dcloud.io/collocation/pages),比如设为50,那么滚动页面到距离底部50px时,就会触发onReachBottom事件。 | | |
| onTabItemTap | 点击 tab 时触发,参数为Object,具体见下方注意事项 | 微信小程序、支付宝小程序、百度小程序、H5、App(自定义组件模式) | |
| onShareAppMessage | 用户点击右上角分享 | 微信小程序、百度小程序、字节跳动小程序、支付宝小程序 | |
| onPageScroll | 监听页面滚动,参数为Object | nvue暂不支持 | |
| onNavigationBarButtonTap | 监听原生标题栏按钮点击事件,参数为Object | App、H5 | |
| onBackPress | 监听页面返回,返回 event = {from:backbutton、 navigateBack} ,backbutton 表示来源是左上角返回按钮或 android 返回键;navigateBack表示来源是 uni.navigateBack ;详细说明及使用:[onBackPress 详解](http://ask.dcloud.net.cn/article/35120)。支付宝小程序只有真机能触发,只能监听非navigateBack引起的返回,不可阻止默认行为。 | app、H5、支付宝小程序 | |
| onNavigationBarSearchInputChanged | 监听原生标题栏搜索输入框输入内容变化事件 | App、H5 | 1.6.0 |
| onNavigationBarSearchInputConfirmed | 监听原生标题栏搜索输入框搜索事件,用户点击软键盘上的“搜索”按钮时触发。 | App、H5 | 1.6.0 |
| onNavigationBarSearchInputClicked | 监听原生标题栏搜索输入框点击事件 | App、H5 | 1.6.0 |
| onShareTimeline | 监听用户点击右上角转发到朋友圈 | 微信小程序 | 2.8.1+ |
| onAddToFavorites | 监听用户点击右上角收藏 | 微信小程序 | 2.8.1+ |
- **组件生命周期**
`uni-app` 组件支持的生命周期,与vue标准组件的生命周期相同。这里没有页面级的onLoad等生命周期:
| 函数名 | 说明 | 平台差异说明 | 最低版本 |
| :------------ | :----------------------------------------------------------- | :----------- | :------- |
| beforeCreate | 在实例初始化之后被调用。[详见](https://cn.vuejs.org/v2/api/#beforeCreate) | | |
| created | 在实例创建完成后被立即调用。[详见](https://cn.vuejs.org/v2/api/#created) | | |
| beforeMount | 在挂载开始之前被调用。[详见](https://cn.vuejs.org/v2/api/#beforeMount) | | |
| mounted | 挂载到实例上去之后调用。[详见](https://cn.vuejs.org/v2/api/#mounted) 注意:此处并不能确定子组件被全部挂载,如果需要子组件完全挂载之后在执行操作可以使用`$nextTick`[Vue官方文档](https://cn.vuejs.org/v2/api/#Vue-nextTick) | | |
| beforeUpdate | 数据更新时调用,发生在虚拟 DOM 打补丁之前。[详见](https://cn.vuejs.org/v2/api/#beforeUpdate) | 仅H5平台支持 | |
| updated | 由于数据更改导致的虚拟 DOM 重新渲染和打补丁,在这之后会调用该钩子。[详见](https://cn.vuejs.org/v2/api/#updated) | 仅H5平台支持 | |
| beforeDestroy | 实例销毁之前调用。在这一步,实例仍然完全可用。[详见](https://cn.vuejs.org/v2/api/#beforeDestroy) | | |
| destroyed | Vue 实例销毁后调用。调用后,Vue 实例指示的所有东西都会解绑定,所有的事件监听器会被移除,所有的子实例也会被销毁。[详见](https://cn.vuejs.org/v2/api/#destroyed) | | |
## 路由
### API
框架以栈的形式管理当前所有页面, 当发生路由切换的时候,页面栈的表现如下:
| 路由方式 | 页面栈表现 | 触发时机 |
| ---------- | --------------------------------- | ------------------------------------------------------------ |
| 初始化 | 新页面入栈 | uni-app 打开的第一个页面 |
| 打开新页面 | 新页面入栈 | 调用 API [uni.navigateTo](https://uniapp.dcloud.io/api/router?id=navigateto) 、使用组件 [](https://uniapp.dcloud.io/component/navigator?id=navigator) |
| 页面重定向 | 当前页面出栈,新页面入栈 | 调用 API [uni.redirectTo](https://uniapp.dcloud.io/api/router?id=redirectto) 、使用组件 [](https://uniapp.dcloud.io/component/navigator?id=navigator) |
| 页面返回 | 页面不断出栈,直到目标返回页 | 调用 API [uni.navigateBack](https://uniapp.dcloud.io/api/router?id=navigateback) 、使用组件 [](https://uniapp.dcloud.io/component/navigator?id=navigator) 、用户按左上角返回按钮、安卓用户点击物理back按键 |
| Tab 切换 | 页面全部出栈,只留下新的 Tab 页面 | 调用 API [uni.switchTab](https://uniapp.dcloud.io/api/router?id=switchtab) 、使用组件 [](https://uniapp.dcloud.io/component/navigator?id=navigator) 、用户切换 Tab |
| 重加载 | 页面全部出栈,只留下新的页面 | 调用 API [uni.reLaunch](https://uniapp.dcloud.io/api/router?id=relaunch) 、使用组件 [](https://uniapp.dcloud.io/component/navigator?id=navigator) |
### 传参
#### navigateBack//page-A
let pages = getCurrentPages();
let currPage = pages[pages.length - 1]; // 当前页的实例
this.cityCul = currPage.$vm.cityCul
//page-B
let pages = getCurrentPages()
let nowPage = pages[pages.length - 1]; //当前页页面实例
let prevPage = pages[pages.length - 2]; //上一页页面实例
prevPage.$vm.cityCul = this.CityCul
uni.navigateBack({
url: “../index/index?city=” + this.CityCul
});
1 |
|
平台判断有2种场景,一种是在编译期判断,一种是在运行期判断。
- 编译期判断 编译期判断,即条件编译,不同平台在编译出包后已经是不同的代码。详见:条件编译
1 | // #ifdef H5 |
运行期判断 运行期判断是指代码已经打入包中,仍然需要在运行期判断平台,此时可使用
uni.getSystemInfoSync().platform
判断客户端环境是 Android、iOS 还是小程序开发工具(在百度小程序开发工具、微信小程序开发工具、支付宝小程序开发工具中使用uni.getSystemInfoSync().platform
返回值均为 devtools)。1
2
3
4
5
6
7
8
9
10
11switch(uni.getSystemInfoSync().platform){
case 'android':
console.log('运行Android上')
break;
case 'ios':
console.log('运行iOS上')
break;
default:
console.log('运行在开发者工具上')
break;
}
页面样式与尺寸
uni-app
支持的通用 css 单位包括 px、rpx
px 即屏幕像素
rpx 即响应式px,一种根据屏幕宽度自适应的动态单位。
rpx 是相对于基准宽度的单位,可以根据屏幕宽度进行自适应。
1
2
3
4
5
6
7开发者可以通过设计稿基准宽度计算页面元素 rpx 值,设计稿 1px 与框架样式 1rpx 转换公式如下:
设计稿 1px / 设计稿基准宽度 = 框架样式 1rpx / 750rpx
750 * 元素在设计稿中的宽度 / 设计稿基准宽度
举例说明:
若设计稿宽度为 750px,元素 A 在设计稿上的宽度为 100px,那么元素 A 在 uni-app 里面的宽度应该设为:750 * 100 / 750,结果为:100rpx。
若设计稿宽度为 640px,元素 A 在设计稿上的宽度为 100px,那么元素 A 在 uni-app 里面的宽度应该设为:750 * 100 / 640,结果为:117rpx。固定值
uni-app
中以下组件的高度是固定的,不可修改:
组件 | 描述 | App | H5 |
---|---|---|---|
NavigationBar | 导航栏 | 44px | 44px |
TabBar | 底部选项卡 | HBuilderX 2.3.4之前为56px,2.3.4起和H5调为一致,统一为 50px。但可以自主更改高度) |
renderjs
renderjs
是一个运行在视图层的js。它比WXS更加强大。它只支持app-vue和h5。
renderjs
的主要作用有2个:
- 大幅降低逻辑层和视图层的通讯损耗,提供高性能视图交互能力
- 在视图层操作dom,运行for web的js库
使用方式
设置 script 节点的 lang 为 renderjs
1 | <script module="test" lang="renderjs"> |
使用vue注意事项
uniapp中nvue与vue的区别
uni-app是逻辑和渲染分离的,渲染层在app端提供了两套排版引擎。
小程序方式的webview渲染,和weex方式的原生渲染,两种渲染引擎可以自己根据需要选。
vue文件走的webview渲染
nvue走weex方式的原生渲染
组件和js写法是一样的,css不一样,原生排版的能用的css必须是flex布局
虽然nvue也可以多端编译,输出H5和小程序,但nvue的css写法受限,所以如果你不开发App,那么不需要使用nvue。
一个App中可以同时使用两种页面,比如首页使用nvue,二级页使用vue页面,hello uni-app示例就是如此。
nvue适用场景
在App端某些vue页面表现不佳的场景下使用 nvue 作为强化补充。这些场景如下:
- 需要高性能的区域长列表或瀑布流滚动。webview的页面级长列表滚动时没有性能问题的(就是滚动条覆盖webview整体高度),但页面中某个区域做长列表滚动,则需要使用nvue的
list
、recycle-list
、waterfall
等组件(详见)。这些组件的性能要高于vue页面里的区域滚动组件scroll-view
。 - 复杂高性能的自定义下拉刷新。uni-app的pages.json里可以配置原生下拉刷新,但引擎内置的下拉刷新样式只有雪花和circle圈2种样式。如果你需要自己做复杂的下拉刷新,推荐使用nvue的refresh组件。当然插件市场里也有很多vue下的自定义下拉刷新插件,只要是基于renderjs或wxs的,性能也可以商用,只是没有nvue的
refresh
组件更极致。 - 左右拖动的长列表。在webview里,通过
swiper
+scroll-view
实现左右拖动的长列表,前端模拟下拉刷新,这套方案的性能不好。此时推荐使用nvue,比如新建uni-app项目时的新闻示例模板,就采用了nvue,切换很流畅。 - 实现区域滚动长列表+左右拖动列表+吸顶的复杂排版效果,效果可参考hello uni-app模板里的
swiper-list
。详见 - 如需要将软键盘右下角按钮文字改为“发送”,则需要使用nvue。比如聊天场景,除了软键盘右下角的按钮文字处理外,还涉及聊天记录区域长列表滚动,适合nvue来做。
- 解决前端控件无法覆盖原生控件的层级问题。当你使用
map
、video
、live-pusher
等原生组件时,会发现前端写的view
等组件无法覆盖原生组件,层级问题处理比较麻烦,此时使用nvue更好。或者在vue页面上也可以覆盖一个subnvue(一种非全屏的nvue页面覆盖在webview上),以解决App上的原生控件层级问题。详见 - 如深度使用
map
组件,建议使用nvue。除了层级问题,App端nvue文件的map功能更完善,和小程序拉齐度更高,多端一致性更好。 - 如深度使用
video
,建议使用nvue。比如如下2个场景:video内嵌到swiper中,以实现抖音式视频滑动切换,例子见插件市场;nvue的视频全屏后,通过cover-view
实现内容覆盖,比如增加文字标题、分享按钮。 - 直播推流:nvue下有
live-pusher
组件,和小程序对齐,功能更完善,也没有层级问题。 - 对App启动速度要求极致化。App端v3编译器模式下,如果首页使用nvue且在manifest里配置fast模式,那么App的启动速度可以控制在1秒左右。而使用vue页面的话,App的启动速度一般是3秒起,取决于你的代码性能和体积。
nvue开发与vue开发的常见区别
基于原生引擎的渲染,虽然还是前端技术栈,但和web开发肯定是有区别的。
- nvue 页面控制显隐只可以使用
v-if
不可以使用v-show
- nvue 页面只能使用
flex
布局,不支持其他布局方式。页面开发前,首先想清楚这个页面的纵向内容有什么,哪些是要滚动的,然后每个纵向内容的横轴排布有什么,按 flex 布局设计好界面。 - nvue 页面的布局排列方向默认为竖排(
column
),如需改变布局方向,可以在manifest.json
->app-plus
->nvue
->flex-direction
节点下修改,仅在 uni-app 模式下生效。详情。 - nvue页面编译为H5、小程序时,会做一件css默认值对齐的工作。因为weex渲染引擎只支持flex,并且默认flex方向是垂直。而H5和小程序端,使用web渲染,默认不是flex,并且设置
display:flex
后,它的flex方向默认是水平而不是垂直的。所以nvue编译为H5、小程序时,会自动把页面默认布局设为flex、方向为垂直。当然开发者手动设置后会覆盖默认设置。 - 文字内容,必须、只能在
<text>
组件下。不能在<div>
、<view>
的text
区域里直接写文字。否则即使渲染了,也无法绑定js里的变量。 - 只有
text
标签可以设置字体大小,字体颜色。 - 布局不能使用百分比、没有媒体查询。
- nvue 切换横竖屏时可能导致样式出现问题,建议有 nvue 的页面锁定手机方向。
- 支持的css有限,不过并不影响布局出你需要的界面,
flex
还是非常强大的。详见 - 不支持背景图。但可以使用
image
组件和层级来实现类似web中的背景效果。因为原生开发本身也没有web这种背景图概念 - css选择器支持的比较少,只能使用 class 选择器。详见
- nvue 的各组件在安卓端默认是透明的,如果不设置
background-color
,可能会导致出现重影的问题。 class
进行绑定时只支持数组语法。- Android端在一个页面内使用大量圆角边框会造成性能问题,尤其是多个角的样式还不一样的话更耗费性能。应避免这类使用。
- nvue页面没有
bounce
回弹效果,只有几个列表组件有bounce
效果,包括list
、recycle-list
、waterfall
。 - 原生开发没有页面滚动的概念,页面内容高过屏幕高度并不会自动滚动,只有部分组件可滚动(
list
、waterfall
、scroll-view/scroller
),要滚得内容需要套在可滚动组件下。这不符合前端开发的习惯,所以在 nvue 编译为 uni-app模式时,给页面外层自动套了一个scroller
,页面内容过高会自动滚动。(组件不会套,页面有recycle-list
时也不会套)。后续会提供配置,可以设置不自动套。 - 在 App.vue 中定义的全局js变量不会在 nvue 页面生效。
globalData
和vuex
是生效的。 - App.vue 中定义的全局css,对nvue和vue页面同时生效。如果全局css中有些css在nvue下不支持,编译时控制台会报警,建议把这些不支持的css包裹在条件编译里,
APP-PLUS-NVUE
- 不能在
style
中引入字体文件,nvue 中字体图标的使用参考:加载自定义字体。如果是本地字体,可以用plus.io
的API转换路径。 - 目前不支持在 nvue 页面使用
typescript/ts
。 - nvue 页面关闭原生导航栏时,想要模拟状态栏,可以参考文章。但是,仍然强烈建议在nvue页面使用原生导航栏。nvue的渲染速度再快,也没有原生导航栏快。原生排版引擎解析
json
绘制原生导航栏耗时很少,而解析nvue的js绘制整个页面的耗时要大的多,尤其在新页面进入动画期间,对于复杂页面,没有原生导航栏会在动画期间产生整个屏幕的白屏或闪屏。
Tips
uni-app
完整支持Vue
模板语法data
必须声明为返回一个初始数据对象的函数(注意函数内返回的数据对象不要直接引用函数外的对象);否则页面关闭时,数据不会自动销毁,再次打开该页面时,会显示上次数据。1
2
3
4
5data() {
return {
title: 'Hello'
}
}
条件编译
条件编译是用特殊的注释作为标记,在编译时根据这些特殊的注释,将注释里面的代码编译到不同平台。
写法:以 #ifdef 或 #ifndef 加 %PLATFORM% 开头,以 #endif 结尾。
- #ifdef:if defined 仅在某平台存在
- #ifndef:if not defined 除了某平台均存在
- **%PLATFORM%**:平台名称
#ifdef APP-PLUS 需条件编译的代码 #endif | 仅出现在 App 平台下的代码 |
---|---|
#ifndef H5 需条件编译的代码 #endif | 除了 H5 平台,其它平台均存在的代码 |
#ifdef H5 || MP-WEIXIN 需条件编译的代码 #endif | 在 H5 平台或微信小程序平台存在的代码(这里只有||,不可能出现&&,因为没有交集) |
%PLATFORM% 可取值如下:
值 | 平台 |
---|---|
APP-PLUS | App |
APP-PLUS-NVUE | App nvue |
H5 | H5 |
MP-WEIXIN | 微信小程序 |
MP-ALIPAY | 支付宝小程序 |
MP-BAIDU | 百度小程序 |
MP-TOUTIAO | 字节跳动小程序 |
pages.json 的条件编译
下面的页面,只有运行至 App 时才会编译进去。
API
页面实例
getApp
getApp()
函数用于获取当前应用实例,一般用于获取globalData 。
实例
1 | const app = getApp() |
注意:
- 不要在定义于
App()
内的函数中,或调用App
前调用getApp()
,可以通过this.$scope
获取对应的app实例 - 通过
getApp()
获取实例之后,不要私自调用生命周期函数。 - v3模式加速了首页
nvue
的启动速度,当在首页nvue
中使用getApp()
不一定可以获取真正的App
对象。对此v3版本提供了const app = getApp({allowDefault: true})
用来获取原始的App
对象,可以用来在首页对globalData
等初始化
getCurrentPages()
getCurrentPages()
函数用于获取当前页面栈的实例,以数组形式按栈的顺序给出,第一个元素为首页,最后一个元素为当前页面。
注意: getCurrentPages()
仅用于展示页面栈的情况,请勿修改页面栈,以免造成页面状态错误。
每个页面实例的方法属性列表:
方法 | 描述 | 平台说明 |
---|---|---|
page.$getAppWebview() | 获取当前页面的webview对象实例 | App |
page.route | 获取当前页面的路由 |
1 | let pages = getCurrentPages() |
Tips:
navigateTo
,redirectTo
只能打开非 tabBar 页面。switchTab
只能打开tabBar
页面。reLaunch
可以打开任意页面。- 页面底部的
tabBar
由页面决定,即只要是定义为tabBar
的页面,底部都有tabBar
。 - 不能在
App.vue
里面进行页面跳转。
$getAppWebview()
uni-app
在 getCurrentPages()
获得的页面里内置了一个方法 $getAppWebview()
可以得到当前webview的对象实例,从而实现对 webview 更强大的控制。在 html5Plus 中,plus.webview具有强大的控制能力,可参考:WebviewObject。
但uni-app
框架有自己的窗口管理机制,请不要自己创建和销毁webview,如有需求覆盖子窗体上去,请使用原生子窗体subNvue。
注意:此方法仅 App 支持
示例:
获取当前页面 webview 的对象实例
1 | export default { |
获取指定页面 webview 的对象实例
getCurrentPages()
可以得到所有页面对象,然后根据数组,可以取指定的页面webview对象
1 | var pages = getCurrentPages(); |