提交 d1635321 编写于 作者: D DCloud_LXH

feat(App): webview

上级 78a59e0e
......@@ -16,3 +16,8 @@ export const ACTION_TYPE_DICT = 0
export type Value = string | number | boolean | null
export type Dictionary = Value[]
export type DictAction = [typeof ACTION_TYPE_DICT, Dictionary]
export const WEBVIEW_INSERTED = 'webviewInserted'
export const WEBVIEW_REMOVED = 'webviewRemoved'
export const WEBVIEW_ID_PREFIX = 'webviewId'
export const WEB_INVOKE_APPSERVICE = 'WEB_INVOKE_APPSERVICE'
import { subscribeServiceMethod } from '@dcloudio/uni-core'
import { ON_WEBVIEW_READY, VD_SYNC } from '../../../../constants'
import {
ON_WEBVIEW_READY,
VD_SYNC,
WEBVIEW_INSERTED,
WEBVIEW_REMOVED,
WEB_INVOKE_APPSERVICE,
} from '../../../../constants'
import { onVdSync } from '../../dom'
import { onPlusMessage } from '../initGlobalEvent'
import { subscribeAd } from './ad'
import { subscribeNavigator } from './navigator'
import { subscribeWebviewReady } from './webviewReady'
import { onWebviewInserted, onWebviewRemoved } from './webviewLifeCycle'
import {
onWebInvokeAppService,
WebInvokeAppService,
} from '../../../onWebInvokeAppService'
export function initSubscribeHandlers() {
const { subscribe, subscribeHandler } = UniServiceJSBridge
......@@ -16,6 +27,13 @@ export function initSubscribeHandlers() {
}
)
onPlusMessage<{
data: Parameters<WebInvokeAppService>[0]
webviewIds: string[]
}>(WEB_INVOKE_APPSERVICE, ({ data, webviewIds }) => {
onWebInvokeAppService(data, webviewIds)
})
if (__uniConfig.renderer !== 'native') {
// 非纯原生
subscribe(ON_WEBVIEW_READY, subscribeWebviewReady)
......@@ -23,5 +41,7 @@ export function initSubscribeHandlers() {
subscribeServiceMethod()
subscribeAd()
subscribeNavigator()
subscribe(WEBVIEW_INSERTED, onWebviewInserted)
subscribe(WEBVIEW_REMOVED, onWebviewRemoved)
}
}
function findPage(pageId: string) {
const page = getCurrentPages().find(
(page) => page.$page.id === parseInt(pageId)
)
if (!page) {
return console.error(`Page[${pageId}] not found`)
}
return page
}
export function onWebviewInserted(data: any, pageId: string) {
const page = findPage(pageId)
page && ((page as any).__uniapp_webview = true)
}
export function onWebviewRemoved(data: any, pageId: string) {
const page = findPage(pageId)
page && delete (page as any).__uniapp_webview
}
......@@ -33,6 +33,7 @@ import {
Value,
VD_SYNC,
} from '../../../constants'
import { getPageById } from '@dcloudio/uni-core'
export default class UniPageNode extends UniNode implements IUniPageNode {
pageId: number
......@@ -352,3 +353,26 @@ export function createPageNode(
) {
return new UniPageNode(pageId, pageOptions, setup)
}
export function getPageNode(pageId: string): UniPageNode | null {
const page = getPageById(parseInt(pageId))
if (!page) return null
return (page as any).__page_container__ as UniPageNode
}
export function findNodeByTagName(
tagName: string,
uniNode: UniNode
): UniNode | null {
if (uniNode.nodeName === tagName.toLocaleUpperCase()) {
return uniNode
}
const { childNodes } = uniNode
for (let i = 0; i < childNodes.length; i++) {
const uniNode = findNodeByTagName(tagName, childNodes[i])
if (uniNode) {
return uniNode
}
}
return null
}
import { getPageNode, findNodeByTagName } from './framework/dom/Page'
import { createUniEvent } from '@dcloudio/uni-shared'
type Name =
| 'navigateTo'
| 'navigateBack'
| 'switchTab'
| 'reLaunch'
| 'redirectTo'
| 'postMessage'
type WebInvokeData = {
name: Name
arg: any
}
export type WebInvokeAppService = (
webInvokeData: WebInvokeData,
pageIds: string[]
) => void
export const onWebInvokeAppService: WebInvokeAppService = (
{ name, arg },
pageIds
) => {
if (name === 'postMessage') {
onMessage(pageIds[0], arg)
} else {
uni[name](arg)
}
}
function onMessage(pageId: string, arg: any) {
const page = getPageNode(pageId)
if (!page) {
return
}
const uniNode = findNodeByTagName('web-view', page)
uniNode?.dispatchEvent(
createUniEvent({
type: 'onMessage',
target: Object.create(null),
currentTarget: Object.create(null),
detail: {
data: [arg],
},
})
)
}
......@@ -247,6 +247,7 @@ export default /*#__PURE__*/ defineBuiltInComponent({
onBeforeUnmount(() => {
if (map) {
map.close()
_setMap(null)
}
})
......@@ -274,7 +275,7 @@ export default /*#__PURE__*/ defineBuiltInComponent({
type Callback = (res: any) => void
function useMapMethods(props: Props, trigger: CustomEventTrigger) {
let map: Map
let map: Map | null
function moveToLocation(
resolve: Callback,
{
......@@ -368,18 +369,18 @@ function useMapMethods(props: Props, trigger: CustomEventTrigger) {
}
}
}
map.addOverlay(nativeMarker as unknown as PlusMapsOverlay)
map?.addOverlay(nativeMarker as unknown as PlusMapsOverlay)
// 此处5+文档中PlusMapsMarker对象只有方法,没有属性
// @ts-expect-error
map.__markers__.push(nativeMarker)
map.__markers_map__[id + ''] = nativeMarker
map && (map.__markers_map__[id + ''] = nativeMarker)
})
}
function _clearMarkers() {
if (!map) return
const markers = map.__markers__
markers.forEach((marker) => {
map.removeOverlay(marker as unknown as PlusMapsOverlay)
map?.removeOverlay(marker as unknown as PlusMapsOverlay)
})
map.__markers__ = []
map.__markers_map__ = {}
......@@ -396,7 +397,7 @@ function useMapMethods(props: Props, trigger: CustomEventTrigger) {
if (!map) return
if (map.__lines__.length > 0) {
map.__lines__.forEach((circle) => {
map.removeOverlay(circle as unknown as PlusMapsOverlay)
map?.removeOverlay(circle as unknown as PlusMapsOverlay)
})
map.__lines__ = []
}
......@@ -423,7 +424,7 @@ function useMapMethods(props: Props, trigger: CustomEventTrigger) {
if (width) {
polyline.setLineWidth(width)
}
map.addOverlay(polyline as unknown as PlusMapsOverlay)
map?.addOverlay(polyline as unknown as PlusMapsOverlay)
// 此处5+文档中PlusMapsPolyline对象只有方法,没有属性
// @ts-expect-error
map.__lines__.push(polyline)
......@@ -433,7 +434,7 @@ function useMapMethods(props: Props, trigger: CustomEventTrigger) {
if (!map) return
if (map.__circles__.length > 0) {
map.__circles__.forEach((circle) => {
map.removeOverlay(circle as unknown as PlusMapsOverlay)
map?.removeOverlay(circle as unknown as PlusMapsOverlay)
})
map.__circles__ = []
}
......@@ -458,7 +459,7 @@ function useMapMethods(props: Props, trigger: CustomEventTrigger) {
if (strokeWidth) {
nativeCircle.setLineWidth(strokeWidth)
}
map.addOverlay(nativeCircle as unknown as PlusMapsOverlay)
map?.addOverlay(nativeCircle as unknown as PlusMapsOverlay)
// 此处5+文档中PlusMapsCircle对象只有方法,没有属性
// @ts-expect-error
map.__circles__.push(nativeCircle)
......@@ -485,7 +486,7 @@ function useMapMethods(props: Props, trigger: CustomEventTrigger) {
_addMarkers,
_addMapLines,
_addMapCircles,
_setMap(_map: Map) {
_setMap(_map: Map | null) {
map = _map
},
}
......
import { ref, watch, onBeforeUnmount, Ref, computed } from 'vue'
import { extend } from '@vue/shared'
import { defineBuiltInComponent } from '@dcloudio/uni-components'
import { getCurrentPageId } from '@dcloudio/uni-core'
import { getRealPath } from '../../../platform/getRealPath'
import {
WEBVIEW_INSERTED,
WEBVIEW_REMOVED,
WEBVIEW_ID_PREFIX,
} from '../../../constants'
import { NAVBAR_HEIGHT } from '@dcloudio/uni-shared'
import { useNative } from '../../../helpers/useNative'
const props = {
src: {
type: String,
default: '',
},
webviewStyles: {
type: Object,
default() {
return {}
},
},
}
let webview: PlusWebviewWebviewObject | null
const insertHTMLWebView = ({
htmlId,
src,
webviewStyles,
}: {
htmlId: string
src: string
webviewStyles: PlusWebviewWebviewStyles
}) => {
const parentWebview = plus.webview.currentWebview()
// fixed by hxy web-view 组件所在的 webview 不注入 uni-app 框架
const styles: PlusWebviewWebviewStyles & {
'uni-app': string
isUniH5: boolean
} = extend(webviewStyles, {
'uni-app': 'none',
isUniH5: true,
})
const parentTitleNView = parentWebview.getTitleNView()
if (parentTitleNView) {
let defaultTop: number = NAVBAR_HEIGHT + parseFloat(styles.top || '0')
if (plus.navigator.isImmersedStatusbar()) {
defaultTop += plus.navigator.getStatusbarHeight()
}
styles.top = String(defaultTop)
styles.bottom = styles.bottom || '0'
}
webview = plus.webview.create(src, htmlId, styles)
if (parentTitleNView) {
webview.addEventListener('titleUpdate', function () {
const title = webview?.getTitle()
parentWebview.setStyle({
titleNView: {
// iOS titleText 为空字符串时 按钮会隐藏
titleText: !title || title === 'null' ? ' ' : title,
},
})
})
}
plus.webview.currentWebview().append(webview as any)
}
const removeHTMLWebView = () => {
plus.webview.currentWebview().remove(webview as any)
webview?.close('none')
webview = null
}
export default /*#__PURE__*/ defineBuiltInComponent({
name: 'WebView',
props,
setup(props) {
const pageId = getCurrentPageId()
const containerRef: Ref<HTMLElement | null> = ref(null)
const { position, hidden, onParentReady } = useNative(containerRef)
const webviewStyles = computed(() => props.webviewStyles)
onParentReady(() => {
const htmlId = ref(WEBVIEW_ID_PREFIX + pageId)
insertHTMLWebView({
htmlId: htmlId.value,
src: getRealPath(props.src),
webviewStyles: webviewStyles.value,
})
UniViewJSBridge.publishHandler(WEBVIEW_INSERTED, {}, pageId)
if (hidden.value) webview?.hide()
})
onBeforeUnmount(() => {
removeHTMLWebView()
UniViewJSBridge.publishHandler(WEBVIEW_REMOVED, {}, pageId)
})
watch(
() => props.src,
(val) => {
const realPath = getRealPath(val) || ''
if (!realPath) {
return
}
if (
/^(http|https):\/\//.test(realPath) &&
props.webviewStyles.progress
) {
webview?.setStyle({
progress: {
color: props.webviewStyles.progress.color,
},
})
}
webview?.loadURL(realPath)
}
)
watch(webviewStyles, (webviewStyles) => {
webview?.setStyle(webviewStyles)
})
watch(hidden, (val) => {
webview && webview[val ? 'hide' : 'show']()
})
return () => <uni-web-view ref={containerRef} />
},
})
import { UniNodeJSON } from '@dcloudio/uni-shared'
import '../../../../../style/webview.css'
import WebView from '../../../components/web-view'
import { UniComponent } from './UniComponent'
......
uni-web-view {
display: inline-block;
position: absolute;
left: 0;
right: 0;
top: 0;
bottom: 0;
}
......@@ -13,11 +13,15 @@ import {
useAttrs,
} from '@dcloudio/uni-components'
import { getRealPath } from '@dcloudio/uni-platform'
import { updateElementStyle, once } from '@dcloudio/uni-shared'
import {
updateElementStyle,
once,
ON_WEB_INVOKE_APP_SERVICE,
} from '@dcloudio/uni-shared'
import { onWebInvokeAppService } from '../../../service/onWebInvokeAppService'
const Invoke = /*#__PURE__*/ once(() =>
UniServiceJSBridge.on('onWebInvokeAppService', onWebInvokeAppService)
UniServiceJSBridge.on(ON_WEB_INVOKE_APP_SERVICE, onWebInvokeAppService)
)
const props = {
......
......@@ -60,7 +60,7 @@ export class UniEvent {
}
}
function createUniEvent(evt: Record<string, any>) {
export function createUniEvent(evt: Record<string, any>) {
if (evt instanceof UniEvent) {
return evt
}
......
......@@ -5,6 +5,7 @@ export {
UniEventListener,
parseEventName,
normalizeEventType,
createUniEvent,
} from './Event'
export {
ATTR_CLASS,
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册