提交 3969441b 编写于 作者: fxy060608's avatar fxy060608

wip(app): uni-app-plus

上级 24cce19a
此差异已折叠。
......@@ -31,10 +31,7 @@ declare namespace UniApp {
nvue?: {
'flex-direction': 'column' | 'row'
}
globalStyle: {
navigationBar: PageNavigationBar
pullToRefresh?: PageRefreshOptions
maxWidth?: number
globalStyle: PagesJsonPageStyle & {
rpxCalcMaxDeviceWidth?: number
rpxCalcBaseDeviceWidth?: number
// rpxCalcIncludeWidth?: number
......@@ -158,6 +155,10 @@ declare namespace UniApp {
onReachBottomDistance?: number
pageOrientation?: 'auto' | 'portrait' | 'landscape'
backgroundColor?: string
maxWidth?: string | number
// app-plus
animationType?: string
animationDuration?: number
}
interface PageRouteMeta extends PagesJsonPageStyle {
id?: number
......@@ -171,7 +172,6 @@ declare namespace UniApp {
topWindow?: boolean
leftWindow?: boolean
rightWindow?: boolean
maxWidth?: string | number
}
interface PagesJsonPageOptions {
......
......@@ -6588,6 +6588,9 @@
});
function publishHandler(event, args = {}) {
const pageId = plus.webview.currentWebview().id;
{
console.log(`[VIEW][${Date.now()}]:`, event, args, pageId);
}
plus.webview.postMessageToUniNView({
type: "subscribeHandler",
args: {
......
......@@ -102,7 +102,7 @@ export function isTabBarPage(path = '') {
const route = getRouteOptions(path)
return route && route.meta.isTabBar
} catch (e) {
if (process.env.NODE_ENV !== 'production') {
if (__DEV__) {
console.log('getCurrentPages is not ready')
}
}
......
......@@ -15,7 +15,7 @@ function getLocationSuccess(
) {
const coords = position.coords
if (type !== position.coordsType) {
if (process.env.NODE_ENV !== 'production') {
if (__DEV__) {
console.log(
`UNIAPP[location]:before[${position.coordsType}][lng:${coords.longitude},lat:${coords.latitude}]`
)
......@@ -29,7 +29,7 @@ function getLocationSuccess(
if (coordArray) {
coords.longitude = coordArray[0]
coords.latitude = coordArray[1]
if (process.env.NODE_ENV !== 'production') {
if (__DEV__) {
console.log(
`UNIAPP[location]:after[${type}][lng:${coords.longitude},lat:${coords.latitude}]`
)
......
import { parseUrl } from '@dcloudio/uni-shared'
import { getRouteMeta } from '@dcloudio/uni-core'
import {
API_NAVIGATE_TO,
API_TYPE_NAVIGATE_TO,
......@@ -6,9 +8,84 @@ import {
NavigateToProtocol,
} from '@dcloudio/uni-api'
import { ANI_DURATION, ANI_SHOW } from '../../constants'
import { navigate } from './utils'
import { showWebview } from './webview'
import { registerPage } from '../../framework/page'
export const navigateTo = defineAsyncApi<API_TYPE_NAVIGATE_TO>(
API_NAVIGATE_TO,
({ url }, { resolve, reject }) => {},
(args, { resolve, reject }) => {
const { url, animationType, animationDuration } = args
const { path, query } = parseUrl(url)
const [aniType, aniDuration] = initAnimation(
path,
animationType,
animationDuration
)
navigate(
path,
() => {
_navigateTo({
url,
path,
query,
aniType,
aniDuration,
})
.then(resolve)
.catch(reject)
},
(args as any).openType === 'appLaunch'
)
},
NavigateToProtocol,
NavigateToOptions
)
interface NavigateToOptions {
url: string
path: string
query: Record<string, any>
aniType: string
aniDuration: number
}
function _navigateTo({
url,
path,
query,
aniType,
aniDuration,
}: NavigateToOptions): Promise<undefined> {
// TODO eventChannel
return new Promise((resolve) => {
showWebview(
registerPage({ url, path, query, openType: 'navigateTo' }),
aniType,
aniDuration,
() => {
resolve(undefined)
}
)
})
}
function initAnimation(
path: string,
animationType?: string,
animationDuration?: number
) {
const { globalStyle } = __uniConfig
const meta = getRouteMeta(path)!
return [
animationType ||
meta.animationType ||
globalStyle.animationType ||
ANI_SHOW,
animationDuration ||
meta.animationDuration ||
globalStyle.animationDuration ||
ANI_DURATION,
] as const
}
......@@ -67,7 +67,7 @@ function pendingNavigate() {
return
}
const { callback } = pendingNavigator
if (process.env.NODE_ENV !== 'production') {
if (__DEV__) {
console.log(`pendingNavigate:${pendingNavigator.path}`)
}
pendingNavigator = false
......@@ -86,7 +86,7 @@ export function navigateFinish() {
}
// 创建预加载
const preloadWebview = createPreloadWebview()
if (process.env.NODE_ENV !== 'production') {
if (__DEV__) {
console.log(`navigateFinish.preloadWebview:${preloadWebview.id}`)
}
if (!pendingNavigator) {
......
import { navigateFinish } from './utils'
export function closeWebview(
webview: PlusWebviewWebviewObject,
animationType: string,
animationDuration: number
) {
webview[(webview as any).__preload__ ? 'hide' : 'close'](
animationType as any,
animationDuration
)
}
export function showWebview(
webview: PlusWebviewWebviewObject,
animationType: string,
animationDuration: number,
showCallback: Function,
delay?: number
) {
if (typeof delay === 'undefined') {
delay = (webview as any).nvue ? 0 : 100
}
if (__DEV__) {
console.log(`[show][${Date.now()}]`, delay)
}
const execShowCallback = function () {
if (execShowCallback._called) {
if (__DEV__) {
console.log('execShowCallback.prevent')
}
return
}
execShowCallback._called = true
showCallback && showCallback()
navigateFinish()
}
execShowCallback._called = false
setTimeout(() => {
const timer = setTimeout(() => {
if (__DEV__) {
console.log(`[show.callback.timer][${Date.now()}]`)
}
execShowCallback()
}, animationDuration + 150)
webview.show(animationType as any, animationDuration, () => {
if (__DEV__) {
console.log(`[show.callback][${Date.now()}]`)
}
if (!execShowCallback._called) {
clearTimeout(timer)
}
execShowCallback()
})
}, delay)
}
const downgrade = plus.os.name === 'Android' && parseInt(plus.os.version!) < 6
export const ANI_SHOW = downgrade ? 'slide-in-right' : 'pop-in'
export const ANI_DURATION = 300
export const VIEW_WEBVIEW_PATH = '_www/__uniappview.html'
export const VIEW_WEBVIEW_PATH = '_www/__uniappview.html'
import { hasOwn } from '@vue/shared'
import { NAVBAR_HEIGHT, ON_REACH_BOTTOM_DISTANCE } from '@dcloudio/uni-shared'
import { initEntry } from '../app/initEntry'
import { initRouteOptions } from './initRouteOptions'
import { createWebview } from '../webview'
import { initRouteOptions } from './routeOptions'
import { createWebview, initWebview } from '../webview'
import { createPage } from './define'
import { PageNodeOptions } from '../dom/Page'
import { getStatusbarHeight } from '../../../helpers/statusBar'
......@@ -22,7 +22,7 @@ interface RegisterPageOptions {
query: Record<string, string>
openType: OpenType
webview?: PlusWebviewWebviewObject
eventChannel: unknown
// eventChannel: unknown
}
export function registerPage({
......@@ -45,10 +45,19 @@ export function registerPage({
webview = plus.webview.getWebviewById(webview.id)
;(webview as any).nvue = routeOptions.meta.isNVue
}
routeOptions.meta.id = parseInt(webview.id!)
if (__DEV__) {
console.log(`[uni-app] registerPage(${path},${webview.id})`)
}
initWebview(webview, path, query, routeOptions.meta)
const route = path.substr(1)
;(webview as any).__uniapp_route = route
if (!(webview as any).nvue) {
createPage(
parseInt(webview.id!),
......
import { getRouteOptions } from '@dcloudio/uni-core'
import { getRouteOptions, initRouteMeta } from '@dcloudio/uni-core'
import { OpenType } from '.'
export function initRouteOptions(path: string, openType: OpenType) {
......@@ -6,6 +6,9 @@ export function initRouteOptions(path: string, openType: OpenType) {
const routeOptions = JSON.parse(
JSON.stringify(getRouteOptions(path)!)
) as UniApp.UniRoute
routeOptions.meta = initRouteMeta(routeOptions.meta)
if (
openType === 'reLaunch' ||
(!__uniConfig.realEntryPagePath && getCurrentPages().length === 0) // redirectTo
......
......@@ -2,6 +2,8 @@ import { ON_WEBVIEW_READY } from '../../../constants'
import { createNVueWebview } from './nvue'
import { getPreloadWebview, getWebviewId } from './utils'
export * from './init'
export * from './preload'
export interface CreateWebviewOptions {
......
export function onWebviewClose(webview: PlusWebviewWebviewObject) {
const { popupSubNVueWebviews } = webview as any
if (!popupSubNVueWebviews) {
return
}
webview.addEventListener('close', () => {
Object.keys(popupSubNVueWebviews).forEach((id) => {
if (__DEV__) {
console.log(
`UNIAPP[webview][${webview.id}]:popupSubNVueWebview[${id}].close`
)
}
popupSubNVueWebviews[id].close('none')
})
})
}
import { onWebviewClose } from './close'
import { onWebviewResize } from './resize'
const WEBVIEW_LISTENERS = {
pullToRefresh: 'onPullDownRefresh',
titleNViewSearchInputChanged: 'onNavigationBarSearchInputChanged',
titleNViewSearchInputConfirmed: 'onNavigationBarSearchInputConfirmed',
titleNViewSearchInputClicked: 'onNavigationBarSearchInputClicked',
titleNViewSearchInputFocusChanged: 'onNavigationBarSearchInputFocusChanged',
} as const
export function initWebviewEvent(webview: PlusWebviewWebviewObject) {
const { emit } = UniServiceJSBridge
const id = parseInt(webview.id!)
Object.keys(WEBVIEW_LISTENERS).forEach((name) => {
webview.addEventListener(name as any, (e) => {
emit(WEBVIEW_LISTENERS[name as keyof typeof WEBVIEW_LISTENERS], e, id)
})
})
onWebviewClose(webview)
onWebviewResize(webview)
// TODO
// if (plus.os.name === 'iOS') {
// !(webview as any).nvue && onWebviewRecovery(webview, routeOptions)
// onWebviewPopGesture(webview)
// }
}
import { debounce } from '@dcloudio/uni-shared'
export function onWebviewResize(webview: PlusWebviewWebviewObject) {
const onResize = function ({
width,
height,
}: {
width: number
height: number
}) {
const landscape = Math.abs(plus.navigator.getOrientation()) === 90
const res = {
deviceOrientation: landscape ? 'landscape' : 'portrait',
size: {
windowWidth: Math.ceil(width),
windowHeight: Math.ceil(height),
},
}
UniServiceJSBridge.emit('onViewDidResize', res) // API
UniServiceJSBridge.emit('onResize', res, parseInt(webview.id!)) // Page lifecycle
}
webview.addEventListener('resize' as any, debounce(onResize, 50))
}
import { initWebviewStyle } from './style'
import { initSubNVues } from './subNVues'
export function initWebview(
webview: PlusWebviewWebviewObject,
path: string,
query: Record<string, any>,
routeMeta: UniApp.PageRouteMeta
) {
// 首页或非 nvue 页面
if (webview.id === '1' || !routeMeta.isNVue) {
initWebviewStyle(webview, path, query, routeMeta)
}
initSubNVues(webview, path, routeMeta)
}
import { parseWebviewStyle } from '../style'
import { initUniPageUrl, initDebugRefresh } from '../utils'
export function initWebviewStyle(
webview: PlusWebviewWebviewObject,
path: string,
query: Record<string, any>,
routeMeta: UniApp.PageRouteMeta
) {
const webviewStyle = parseWebviewStyle(path, routeMeta)
webviewStyle.uniPageUrl = initUniPageUrl(path, query)
const isTabBar = !!routeMeta.isTabBar
if (!routeMeta.isNVue) {
webviewStyle.debugRefresh = initDebugRefresh(isTabBar, path, query)
} else {
// android 需要使用
webviewStyle.isTab = isTabBar
}
if (__DEV__) {
console.log('[uni-app] updateWebview', webviewStyle)
}
webview.setStyle(webviewStyle)
}
export function initSubNVues(
webview: PlusWebviewWebviewObject,
path: string,
routeMeta: UniApp.PageRouteMeta
) {}
......@@ -11,7 +11,7 @@ export function createNVueWebview({
webviewStyle,
}: CreateWebviewOptions) {
const curWebviewId = genWebviewId()
const curWebviewStyle = parseWebviewStyle(curWebviewId, path, routeOptions)
const curWebviewStyle = parseWebviewStyle(path, routeOptions.meta)
;(curWebviewStyle as any).uniPageUrl = initUniPageUrl(path, query)
if (__DEV__) {
console.log('[uni-app] createWebview', curWebviewId, path, curWebviewStyle)
......
import { VIEW_WEBVIEW_PATH } from '../constants'
import { VIEW_WEBVIEW_PATH } from '../../constants'
import { genWebviewId } from './utils'
export let preloadWebview: PlusWebviewWebviewObject & {
......
import { mergePageMeta } from '@dcloudio/uni-core'
import { initNVue } from './nvue'
import { initBackgroundColor } from './backgroundColor'
import { initPopGesture } from './popGesture'
import { initPullToRefresh } from './pullToRefresh'
import { initTitleNView } from './titleNView'
import { DebugRefresh, InitUniPageUrl } from '../utils'
export function parseWebviewStyle(
id: number,
path: string,
routeOptions: UniApp.UniRoute
): PlusWebviewWebviewStyles {
routeMeta: UniApp.PageRouteMeta
): PlusWebviewWebviewStyles & {
uniPageUrl?: InitUniPageUrl
debugRefresh?: DebugRefresh
isTab?: boolean
} {
const webviewStyle: PlusWebviewWebviewStyles = {
bounce: 'vertical',
}
const routeMeta = mergePageMeta(id, routeOptions.meta)
Object.keys(routeMeta).forEach((name) => {
if (WEBVIEW_STYLE_BLACKLIST.indexOf(name) === -1) {
......
......@@ -19,10 +19,28 @@ function encode(val: Parameters<typeof encodeURIComponent>[0]) {
return val as string
}
export function initUniPageUrl(path: string, query: Record<string, string>) {
export type InitUniPageUrl = ReturnType<typeof initUniPageUrl>
export type DebugRefresh = ReturnType<typeof initDebugRefresh>
export function initUniPageUrl(path: string, query: Record<string, any>) {
const queryString = query ? stringifyQuery(query, encode) : ''
return {
path: path.substr(1),
query: queryString ? queryString.substr(1) : queryString,
}
}
export function initDebugRefresh(
isTab: boolean,
path: string,
query: Record<string, any>
) {
const queryString = query ? stringifyQuery(query, encode) : ''
return {
isTab,
arguments: JSON.stringify({
path: path.substr(1),
query: queryString ? queryString.substr(1) : queryString,
}),
}
}
......@@ -10,7 +10,7 @@ export const UniViewJSBridge = /*#__PURE__*/ extend(ViewJSBridge, {
function publishHandler(event: string, args: unknown = {}) {
const pageId = plus.webview.currentWebview().id
if (process.env.NODE_ENV !== 'production') {
if (__DEV__) {
console.log(`[VIEW][${Date.now()}]:`, event, args, pageId)
}
;(plus.webview as any).postMessageToUniNView(
......
export const jsContext = {
VUE3: true,
APP_PLUS: false,
H5: true,
MP_360: false,
......@@ -18,6 +19,7 @@ export const jsContext = {
APP_NVUE: false,
}
export const htmlContext = {
VUE3: true,
APP_PLUS: false,
H5: true,
MP_360: false,
......
......@@ -22,9 +22,9 @@ function initGlobalStyle() {
return JSON.parse(JSON.stringify(__uniConfig.globalStyle || {}))
}
export function mergePageMeta(
id: number,
pageMeta: UniApp.PageRouteMeta
export function initRouteMeta(
pageMeta: UniApp.PageRouteMeta,
id?: number
): UniApp.PageRouteMeta {
const globalStyle = initGlobalStyle()
const res = extend({ id }, globalStyle, pageMeta)
......
......@@ -429,7 +429,7 @@ const PAGE_META_KEYS = ["navigationBar", "pullToRefresh"];
function initGlobalStyle() {
return JSON.parse(JSON.stringify(__uniConfig.globalStyle || {}));
}
function mergePageMeta(id, pageMeta) {
function initRouteMeta(pageMeta, id) {
const globalStyle = initGlobalStyle();
const res = shared.extend({ id }, globalStyle, pageMeta);
PAGE_META_KEYS.forEach((name) => {
......@@ -6546,9 +6546,9 @@ function usePageRoute() {
}
function initPageMeta(id) {
if (__UNI_FEATURE_PAGES__) {
return vue.reactive(normalizePageMeta(JSON.parse(JSON.stringify(mergePageMeta(id, vueRouter.useRoute().meta)))));
return vue.reactive(normalizePageMeta(JSON.parse(JSON.stringify(initRouteMeta(vueRouter.useRoute().meta, id)))));
}
return vue.reactive(normalizePageMeta(JSON.parse(JSON.stringify(mergePageMeta(id, __uniRoutes[0].meta)))));
return vue.reactive(normalizePageMeta(JSON.parse(JSON.stringify(initRouteMeta(__uniRoutes[0].meta, id)))));
}
function normalizePageMeta(pageMeta) {
if (__UNI_FEATURE_PULL_DOWN_REFRESH__) {
......@@ -9565,7 +9565,7 @@ function setNavigationBar(pageMeta, type, args, resolve, reject) {
const { frontColor, backgroundColor, animation: animation2 } = args;
const { duration, timingFunc } = animation2;
if (frontColor) {
navigationBar.titleColor = frontColor === "#000000" ? "#000" : "#fff";
navigationBar.titleColor = frontColor === "#000000" ? "#000000" : "#ffffff";
}
if (backgroundColor) {
navigationBar.backgroundColor = backgroundColor;
......
......@@ -816,7 +816,7 @@ const PAGE_META_KEYS = ["navigationBar", "pullToRefresh"];
function initGlobalStyle() {
return JSON.parse(JSON.stringify(__uniConfig.globalStyle || {}));
}
function mergePageMeta(id2, pageMeta) {
function initRouteMeta(pageMeta, id2) {
const globalStyle = initGlobalStyle();
const res = extend({ id: id2 }, globalStyle, pageMeta);
PAGE_META_KEYS.forEach((name) => {
......@@ -13026,9 +13026,9 @@ function usePageRoute() {
}
function initPageMeta(id2) {
if (__UNI_FEATURE_PAGES__) {
return reactive(normalizePageMeta(JSON.parse(JSON.stringify(mergePageMeta(id2, useRoute().meta)))));
return reactive(normalizePageMeta(JSON.parse(JSON.stringify(initRouteMeta(useRoute().meta, id2)))));
}
return reactive(normalizePageMeta(JSON.parse(JSON.stringify(mergePageMeta(id2, __uniRoutes[0].meta)))));
return reactive(normalizePageMeta(JSON.parse(JSON.stringify(initRouteMeta(__uniRoutes[0].meta, id2)))));
}
function normalizePageMeta(pageMeta) {
if (__UNI_FEATURE_PULL_DOWN_REFRESH__) {
......@@ -17917,7 +17917,7 @@ function setNavigationBar(pageMeta, type, args, resolve, reject) {
const { frontColor, backgroundColor, animation: animation2 } = args;
const { duration, timingFunc } = animation2;
if (frontColor) {
navigationBar.titleColor = frontColor === "#000000" ? "#000" : "#fff";
navigationBar.titleColor = frontColor === "#000000" ? "#000000" : "#ffffff";
}
if (backgroundColor) {
navigationBar.backgroundColor = backgroundColor;
......
......@@ -28,7 +28,7 @@ const scrollBehavior: RouterOptions['scrollBehavior'] = (
function createRouterOptions(): RouterOptions {
return {
history: initHistory(),
strict: !!__uniConfig.router.strict,
strict: !!__uniConfig.router!.strict,
routes: __uniRoutes as unknown as RouteRecordRaw[],
scrollBehavior,
}
......@@ -45,7 +45,7 @@ function removeCurrentPages(delta: number = 1) {
}
function initHistory() {
let { base } = __uniConfig.router
let { base } = __uniConfig.router!
if (base === '/') {
base = ''
}
......
......@@ -4,7 +4,7 @@ import { useRoute } from 'vue-router'
import { NAVBAR_HEIGHT, parseQuery } from '@dcloudio/uni-shared'
import {
mergePageMeta,
initRouteMeta,
normalizePullToRefreshRpx,
PolySymbol,
} from '@dcloudio/uni-core'
......@@ -48,9 +48,9 @@ function initPageMeta(id: number) {
normalizePageMeta(
JSON.parse(
JSON.stringify(
mergePageMeta(
id,
useRoute().meta as unknown as UniApp.PageRouteMeta
initRouteMeta(
useRoute().meta as unknown as UniApp.PageRouteMeta,
id
)
)
)
......@@ -59,7 +59,7 @@ function initPageMeta(id: number) {
}
return reactive<UniApp.PageRouteMeta>(
normalizePageMeta(
JSON.parse(JSON.stringify(mergePageMeta(id, __uniRoutes[0].meta)))
JSON.parse(JSON.stringify(initRouteMeta(__uniRoutes[0].meta, id)))
)
)
}
......@@ -83,7 +83,7 @@ function normalizePageMeta(pageMeta: UniApp.PageRouteMeta) {
)
const { type, style } = navigationBar
if (style !== 'custom' && type !== 'transparent') {
pullToRefresh.offset +=
pullToRefresh.offset! +=
NAVBAR_HEIGHT + (__NODE_JS__ ? 0 : safeAreaInsets.top)
}
pageMeta.pullToRefresh = pullToRefresh
......
......@@ -32,7 +32,8 @@ function setNavigationBar(
const { frontColor, backgroundColor, animation } = args
const { duration, timingFunc } = animation
if (frontColor) {
navigationBar.titleColor = frontColor === '#000000' ? '#000' : '#fff'
navigationBar.titleColor =
frontColor === '#000000' ? '#000000' : '#ffffff'
}
if (backgroundColor) {
navigationBar.backgroundColor = backgroundColor
......
......@@ -130,6 +130,97 @@ function scrollTo(scrollTop, duration) {
scrollTo(duration);
}
const encode = encodeURIComponent;
function stringifyQuery(obj, encodeStr = encode) {
const res = obj
? Object.keys(obj)
.map((key) => {
let val = obj[key];
if (typeof val === undefined || val === null) {
val = '';
}
else if (shared.isPlainObject(val)) {
val = JSON.stringify(val);
}
return encodeStr(key) + '=' + encodeStr(val);
})
.filter((x) => x.length > 0)
.join('&')
: null;
return res ? `?${res}` : '';
}
/**
* Decode text using `decodeURIComponent`. Returns the original text if it
* fails.
*
* @param text - string to decode
* @returns decoded string
*/
function decode(text) {
try {
return decodeURIComponent('' + text);
}
catch (err) { }
return '' + text;
}
function decodedQuery(query = {}) {
const decodedQuery = {};
Object.keys(query).forEach((name) => {
try {
decodedQuery[name] = decode(query[name]);
}
catch (e) {
decodedQuery[name] = query[name];
}
});
return decodedQuery;
}
const PLUS_RE = /\+/g; // %2B
/**
* https://github.com/vuejs/vue-router-next/blob/master/src/query.ts
* @internal
*
* @param search - search string to parse
* @returns a query object
*/
function parseQuery(search) {
const query = {};
// avoid creating an object with an empty key and empty value
// because of split('&')
if (search === '' || search === '?')
return query;
const hasLeadingIM = search[0] === '?';
const searchParams = (hasLeadingIM ? search.slice(1) : search).split('&');
for (let i = 0; i < searchParams.length; ++i) {
// pre decode the + into space
const searchParam = searchParams[i].replace(PLUS_RE, ' ');
// allow the = character
let eqPos = searchParam.indexOf('=');
let key = decode(eqPos < 0 ? searchParam : searchParam.slice(0, eqPos));
let value = eqPos < 0 ? null : decode(searchParam.slice(eqPos + 1));
if (key in query) {
// an extra variable for ts types
let currentValue = query[key];
if (!shared.isArray(currentValue)) {
currentValue = query[key] = [currentValue];
}
currentValue.push(value);
}
else {
query[key] = value;
}
}
return query;
}
function parseUrl(url) {
const [path, querystring] = url.split('?', 2);
return {
path,
query: parseQuery(querystring),
};
}
function plusReady(callback) {
if (typeof callback !== 'function') {
return;
......@@ -769,89 +860,6 @@ function callOptions(options, data) {
}
}
const encode = encodeURIComponent;
function stringifyQuery(obj, encodeStr = encode) {
const res = obj
? Object.keys(obj)
.map((key) => {
let val = obj[key];
if (typeof val === undefined || val === null) {
val = '';
}
else if (shared.isPlainObject(val)) {
val = JSON.stringify(val);
}
return encodeStr(key) + '=' + encodeStr(val);
})
.filter((x) => x.length > 0)
.join('&')
: null;
return res ? `?${res}` : '';
}
/**
* Decode text using `decodeURIComponent`. Returns the original text if it
* fails.
*
* @param text - string to decode
* @returns decoded string
*/
function decode(text) {
try {
return decodeURIComponent('' + text);
}
catch (err) { }
return '' + text;
}
function decodedQuery(query = {}) {
const decodedQuery = {};
Object.keys(query).forEach((name) => {
try {
decodedQuery[name] = decode(query[name]);
}
catch (e) {
decodedQuery[name] = query[name];
}
});
return decodedQuery;
}
const PLUS_RE = /\+/g; // %2B
/**
* https://github.com/vuejs/vue-router-next/blob/master/src/query.ts
* @internal
*
* @param search - search string to parse
* @returns a query object
*/
function parseQuery(search) {
const query = {};
// avoid creating an object with an empty key and empty value
// because of split('&')
if (search === '' || search === '?')
return query;
const hasLeadingIM = search[0] === '?';
const searchParams = (hasLeadingIM ? search.slice(1) : search).split('&');
for (let i = 0; i < searchParams.length; ++i) {
// pre decode the + into space
const searchParam = searchParams[i].replace(PLUS_RE, ' ');
// allow the = character
let eqPos = searchParam.indexOf('=');
let key = decode(eqPos < 0 ? searchParam : searchParam.slice(0, eqPos));
let value = eqPos < 0 ? null : decode(searchParam.slice(eqPos + 1));
if (key in query) {
// an extra variable for ts types
let currentValue = query[key];
if (!shared.isArray(currentValue)) {
currentValue = query[key] = [currentValue];
}
currentValue.push(value);
}
else {
query[key] = value;
}
}
return query;
}
function debounce(fn, delay) {
let timeout;
const newFn = function () {
......@@ -949,6 +957,7 @@ exports.normalizeDataset = normalizeDataset;
exports.normalizeTarget = normalizeTarget;
exports.once = once;
exports.parseQuery = parseQuery;
exports.parseUrl = parseUrl;
exports.passive = passive;
exports.plusReady = plusReady;
exports.removeLeadingSlash = removeLeadingSlash;
......
......@@ -138,6 +138,11 @@ declare interface Options {
*/
export declare function parseQuery(search: string): Record<string, any>;
export declare function parseUrl(url: string): {
path: string;
query: Record<string, any>;
};
export declare function passive(passive: boolean): {
passive: boolean;
};
......
import { camelize, extend, isString, isHTMLTag, isSVGTag, capitalize, isPlainObject, isArray } from '@vue/shared';
import { camelize, extend, isString, isPlainObject, isArray, isHTMLTag, isSVGTag, capitalize } from '@vue/shared';
function formatKey(key) {
return camelize(key.substring(5));
......@@ -126,6 +126,97 @@ function scrollTo(scrollTop, duration) {
scrollTo(duration);
}
const encode = encodeURIComponent;
function stringifyQuery(obj, encodeStr = encode) {
const res = obj
? Object.keys(obj)
.map((key) => {
let val = obj[key];
if (typeof val === undefined || val === null) {
val = '';
}
else if (isPlainObject(val)) {
val = JSON.stringify(val);
}
return encodeStr(key) + '=' + encodeStr(val);
})
.filter((x) => x.length > 0)
.join('&')
: null;
return res ? `?${res}` : '';
}
/**
* Decode text using `decodeURIComponent`. Returns the original text if it
* fails.
*
* @param text - string to decode
* @returns decoded string
*/
function decode(text) {
try {
return decodeURIComponent('' + text);
}
catch (err) { }
return '' + text;
}
function decodedQuery(query = {}) {
const decodedQuery = {};
Object.keys(query).forEach((name) => {
try {
decodedQuery[name] = decode(query[name]);
}
catch (e) {
decodedQuery[name] = query[name];
}
});
return decodedQuery;
}
const PLUS_RE = /\+/g; // %2B
/**
* https://github.com/vuejs/vue-router-next/blob/master/src/query.ts
* @internal
*
* @param search - search string to parse
* @returns a query object
*/
function parseQuery(search) {
const query = {};
// avoid creating an object with an empty key and empty value
// because of split('&')
if (search === '' || search === '?')
return query;
const hasLeadingIM = search[0] === '?';
const searchParams = (hasLeadingIM ? search.slice(1) : search).split('&');
for (let i = 0; i < searchParams.length; ++i) {
// pre decode the + into space
const searchParam = searchParams[i].replace(PLUS_RE, ' ');
// allow the = character
let eqPos = searchParam.indexOf('=');
let key = decode(eqPos < 0 ? searchParam : searchParam.slice(0, eqPos));
let value = eqPos < 0 ? null : decode(searchParam.slice(eqPos + 1));
if (key in query) {
// an extra variable for ts types
let currentValue = query[key];
if (!isArray(currentValue)) {
currentValue = query[key] = [currentValue];
}
currentValue.push(value);
}
else {
query[key] = value;
}
}
return query;
}
function parseUrl(url) {
const [path, querystring] = url.split('?', 2);
return {
path,
query: parseQuery(querystring),
};
}
function plusReady(callback) {
if (typeof callback !== 'function') {
return;
......@@ -765,89 +856,6 @@ function callOptions(options, data) {
}
}
const encode = encodeURIComponent;
function stringifyQuery(obj, encodeStr = encode) {
const res = obj
? Object.keys(obj)
.map((key) => {
let val = obj[key];
if (typeof val === undefined || val === null) {
val = '';
}
else if (isPlainObject(val)) {
val = JSON.stringify(val);
}
return encodeStr(key) + '=' + encodeStr(val);
})
.filter((x) => x.length > 0)
.join('&')
: null;
return res ? `?${res}` : '';
}
/**
* Decode text using `decodeURIComponent`. Returns the original text if it
* fails.
*
* @param text - string to decode
* @returns decoded string
*/
function decode(text) {
try {
return decodeURIComponent('' + text);
}
catch (err) { }
return '' + text;
}
function decodedQuery(query = {}) {
const decodedQuery = {};
Object.keys(query).forEach((name) => {
try {
decodedQuery[name] = decode(query[name]);
}
catch (e) {
decodedQuery[name] = query[name];
}
});
return decodedQuery;
}
const PLUS_RE = /\+/g; // %2B
/**
* https://github.com/vuejs/vue-router-next/blob/master/src/query.ts
* @internal
*
* @param search - search string to parse
* @returns a query object
*/
function parseQuery(search) {
const query = {};
// avoid creating an object with an empty key and empty value
// because of split('&')
if (search === '' || search === '?')
return query;
const hasLeadingIM = search[0] === '?';
const searchParams = (hasLeadingIM ? search.slice(1) : search).split('&');
for (let i = 0; i < searchParams.length; ++i) {
// pre decode the + into space
const searchParam = searchParams[i].replace(PLUS_RE, ' ');
// allow the = character
let eqPos = searchParam.indexOf('=');
let key = decode(eqPos < 0 ? searchParam : searchParam.slice(0, eqPos));
let value = eqPos < 0 ? null : decode(searchParam.slice(eqPos + 1));
if (key in query) {
// an extra variable for ts types
let currentValue = query[key];
if (!isArray(currentValue)) {
currentValue = query[key] = [currentValue];
}
currentValue.push(value);
}
else {
query[key] = value;
}
}
return query;
}
function debounce(fn, delay) {
let timeout;
const newFn = function () {
......@@ -884,4 +892,4 @@ function getEnvLocale() {
return (lang && lang.replace(/[.:].*/, '')) || 'en';
}
export { BACKGROUND_COLOR, BUILT_IN_TAGS, COMPONENT_NAME_PREFIX, COMPONENT_PREFIX, COMPONENT_SELECTOR_PREFIX, DATA_RE, NAVBAR_HEIGHT, NODE_TYPE_COMMENT, NODE_TYPE_ELEMENT, NODE_TYPE_PAGE, NODE_TYPE_TEXT, ON_REACH_BOTTOM_DISTANCE, PLUS_RE, PRIMARY_COLOR, RESPONSIVE_MIN_WIDTH, SCHEME_RE, SELECTED_COLOR, TABBAR_HEIGHT, TAGS, UNI_SSR, UNI_SSR_DATA, UNI_SSR_GLOBAL_DATA, UNI_SSR_STORE, UNI_SSR_TITLE, UniBaseNode, UniCommentNode, UniElement, UniEvent, UniInputElement, UniNode, UniTextAreaElement, UniTextNode, WEB_INVOKE_APPSERVICE, addFont, cache, cacheStringFunction, callOptions, createRpx2Unit, debounce, decode, decodeAttr, decodeTag, decodedQuery, defaultRpx2Unit, encodeAttr, encodeTag, formatDateTime, getCustomDataset, getEnvLocale, getLen, initCustomDataset, invokeArrayFns, isBuiltInComponent, isCustomElement, isNativeTag, isServiceCustomElement, isServiceNativeTag, normalizeDataset, normalizeTarget, once, parseQuery, passive, plusReady, removeLeadingSlash, sanitise, scrollTo, stringifyQuery, updateElementStyle };
export { BACKGROUND_COLOR, BUILT_IN_TAGS, COMPONENT_NAME_PREFIX, COMPONENT_PREFIX, COMPONENT_SELECTOR_PREFIX, DATA_RE, NAVBAR_HEIGHT, NODE_TYPE_COMMENT, NODE_TYPE_ELEMENT, NODE_TYPE_PAGE, NODE_TYPE_TEXT, ON_REACH_BOTTOM_DISTANCE, PLUS_RE, PRIMARY_COLOR, RESPONSIVE_MIN_WIDTH, SCHEME_RE, SELECTED_COLOR, TABBAR_HEIGHT, TAGS, UNI_SSR, UNI_SSR_DATA, UNI_SSR_GLOBAL_DATA, UNI_SSR_STORE, UNI_SSR_TITLE, UniBaseNode, UniCommentNode, UniElement, UniEvent, UniInputElement, UniNode, UniTextAreaElement, UniTextNode, WEB_INVOKE_APPSERVICE, addFont, cache, cacheStringFunction, callOptions, createRpx2Unit, debounce, decode, decodeAttr, decodeTag, decodedQuery, defaultRpx2Unit, encodeAttr, encodeTag, formatDateTime, getCustomDataset, getEnvLocale, getLen, initCustomDataset, invokeArrayFns, isBuiltInComponent, isCustomElement, isNativeTag, isServiceCustomElement, isServiceNativeTag, normalizeDataset, normalizeTarget, once, parseQuery, parseUrl, passive, plusReady, removeLeadingSlash, sanitise, scrollTo, stringifyQuery, updateElementStyle };
export * from './dom'
export * from './url'
export * from './plus'
export * from './tags'
export * from './vdom'
......
import { parseQuery } from './query'
export function parseUrl(url: string) {
const [path, querystring] = url.split('?', 2)
return {
path,
query: parseQuery(querystring),
}
}
此差异已折叠。
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册