From 7cc0bc56a380fc62acabc3367a614f3a0cb0ff8b Mon Sep 17 00:00:00 2001 From: fxy060608 Date: Mon, 2 Aug 2021 14:56:11 +0800 Subject: [PATCH] wip(app): nvue --- packages/shims-uni-app.d.ts | 1 + packages/uni-app-plus/build.json | 7 ++- .../uni-app-plus/dist/uni-app-service.es.js | 58 ++++++++++++++++--- .../uni-app-plus/src/service/api/index.ts | 1 + .../src/service/api/plugin/restoreGlobal.ts | 43 ++++++++++++++ .../src/service/framework/page/register.ts | 2 + .../service/framework/webview/init/index.ts | 3 +- .../service/framework/webview/style/index.ts | 1 - .../src/json/app/manifest/index.ts | 9 ++- .../src/json/app/manifest/launchwebview.ts | 4 +- .../src/json/app/manifest/tabBar.ts | 5 +- .../uni-cli-shared/src/json/app/pages/code.ts | 3 +- packages/uni-core/src/helpers/hook.ts | 11 ++-- packages/vite-plugin-uni/src/cli/action.ts | 3 +- packages/vite-plugin-uni/src/cli/build.ts | 38 ++++++------ packages/vite-plugin-uni/src/cli/utils.ts | 16 ++--- rollup.config.js | 21 +++++++ 17 files changed, 171 insertions(+), 55 deletions(-) create mode 100644 packages/uni-app-plus/src/service/api/plugin/restoreGlobal.ts diff --git a/packages/shims-uni-app.d.ts b/packages/shims-uni-app.d.ts index 55b6ebf3e..a0e60b602 100644 --- a/packages/shims-uni-app.d.ts +++ b/packages/shims-uni-app.d.ts @@ -68,6 +68,7 @@ declare namespace UniApp { autoclose: boolean } onReady: (fn: Function) => void + serviceReady: boolean } interface UniRoute { diff --git a/packages/uni-app-plus/build.json b/packages/uni-app-plus/build.json index fa1f95260..95cbf7eb1 100644 --- a/packages/uni-app-plus/build.json +++ b/packages/uni-app-plus/build.json @@ -6,7 +6,7 @@ "output": { "name": "serviceContext", "format": "iife", - "banner": "export function createServiceContext(Vue,weex, plus,instanceContext){\nconst setTimeout = instanceContext.setTimeout;\nconst clearTimeout = instanceContext.clearTimeout;\nconst setInterval = instanceContext.setInterval;\nconst clearInterval = instanceContext.clearInterval;\nconst __uniConfig = instanceContext.__uniConfig;\nconst __uniRoutes = instanceContext.__uniRoutes;\n", + "banner": "export function createServiceContext(weex, plus, instanceContext){\nconst Vue = instanceContext.Vue;\nlet setTimeout = instanceContext.setTimeout;\nlet clearTimeout = instanceContext.clearTimeout;\nlet setInterval = instanceContext.setInterval;\nlet clearInterval = instanceContext.clearInterval;\nconst __uniConfig = instanceContext.__uniConfig;\nconst __uniRoutes = instanceContext.__uniRoutes;\n", "footer": "const uni = serviceContext.uni;\nconst getApp = serviceContext.getApp;\nconst getCurrentPages = serviceContext.getCurrentPages;\nconst UniServiceJSBridge = serviceContext.UniServiceJSBridge;\nreturn serviceContext;\n}", "globals": { "vue": "Vue" @@ -26,7 +26,10 @@ "__UNI_FEATURE_I18N_ZH_HANS__": "true", "__UNI_FEATURE_I18N_ZH_HANT__": "true" }, - "external": ["vue"] + "external": ["vue"], + "replaceAfterBundled": { + "__VUE__": "vue" + } }, { "input": { diff --git a/packages/uni-app-plus/dist/uni-app-service.es.js b/packages/uni-app-plus/dist/uni-app-service.es.js index 3d1aa3083..14d872e11 100644 --- a/packages/uni-app-plus/dist/uni-app-service.es.js +++ b/packages/uni-app-plus/dist/uni-app-service.es.js @@ -1,8 +1,9 @@ -export function createServiceContext(Vue,weex, plus,instanceContext){ -const setTimeout = instanceContext.setTimeout; -const clearTimeout = instanceContext.clearTimeout; -const setInterval = instanceContext.setInterval; -const clearInterval = instanceContext.clearInterval; +export function createServiceContext(weex, plus, instanceContext){ +const Vue = instanceContext.Vue; +let setTimeout = instanceContext.setTimeout; +let clearTimeout = instanceContext.clearTimeout; +let setInterval = instanceContext.setInterval; +let clearInterval = instanceContext.clearInterval; const __uniConfig = instanceContext.__uniConfig; const __uniRoutes = instanceContext.__uniRoutes; @@ -1867,8 +1868,12 @@ var serviceContext = (function (vue) { return; } // 兼容 nvue - const hooks = (vm._$weex ? vm.$options : vm.$)[name] - ; + { + if (vm.__call_hook) { + return vm.__call_hook(name, args); + } + } + const hooks = vm.$[name]; return hooks && invokeArrayFns(hooks, args); } @@ -9511,6 +9516,9 @@ var serviceContext = (function (vue) { let lastStatusBarStyle; let oldSetStatusBarStyle = plus.navigator.setStatusBarStyle; + function restoreOldSetStatusBarStyle(setStatusBarStyle) { + oldSetStatusBarStyle = setStatusBarStyle; + } function newSetStatusBarStyle(style) { lastStatusBarStyle = style; oldSetStatusBarStyle(style); @@ -9694,7 +9702,8 @@ var serviceContext = (function (vue) { function initWebview(webview, path, query, routeMeta) { // 首页或非 nvue 页面 if (webview.id === '1' || !routeMeta.isNVue) { - initWebviewStyle(webview, path, query, routeMeta); + // path 必须参数为空,因为首页已经在 manifest.json 中设置了 uniNView,不能再次设置,否则会二次加载 + initWebviewStyle(webview, '', query, routeMeta); } initWebviewEvent(webview); } @@ -10103,6 +10112,36 @@ var serviceContext = (function (vue) { }; } + function restoreGlobal(newVue, newWeex, newPlus, newSetTimeout, newClearTimeout, newSetInterval, newClearInterval) { + // 确保部分全局变量 是 app-service 中的 + // 若首页 nvue 初始化比 app-service 快,导致框架处于该 nvue 环境下 + // plus 如果不用 app-service,资源路径会出问题 + // 若首页 nvue 被销毁,如 redirectTo 或 reLaunch,则这些全局功能会损坏 + // 设置 vue3 + // @ts-ignore 最终vue会被替换为vue + vue = newVue; + if (plus !== newPlus) { + if ((process.env.NODE_ENV !== 'production')) { + console.log(`[restoreGlobal][${Date.now()}]`); + } + weex = newWeex; + // @ts-ignore + plus = newPlus; + restoreOldSetStatusBarStyle(plus.navigator.setStatusBarStyle); + plus.navigator.setStatusBarStyle = newSetStatusBarStyle; + /* eslint-disable no-window-assign */ + // @ts-ignore + setTimeout = newSetTimeout; + // @ts-ignore + clearTimeout = newClearTimeout; + // @ts-ignore + setInterval = newSetInterval; + // @ts-ignore + clearInterval = newClearInterval; + } + __uniConfig.serviceReady = true; + } + const EventType = { load: 'load', close: 'close', @@ -10947,6 +10986,8 @@ var serviceContext = (function (vue) { return { $: {}, onNVuePageCreated(vm, curNVuePage) { + vm.$ = {}; // 补充一个 nvue 的 $ 对象,模拟 vue3 的,不然有部分地方访问了 $ + vm.$getAppWebview = () => webview; // 替换真实的 nvue 的 vm initPageVm(vm, pageInstance); const pages = getAllPages(); @@ -11361,6 +11402,7 @@ var serviceContext = (function (vue) { shareWithSystem: shareWithSystem, requestPayment: requestPayment, __vuePlugin: index$1, + restoreGlobal: restoreGlobal, createRewardedVideoAd: createRewardedVideoAd, createFullScreenVideoAd: createFullScreenVideoAd, createInterstitialAd: createInterstitialAd, diff --git a/packages/uni-app-plus/src/service/api/index.ts b/packages/uni-app-plus/src/service/api/index.ts index 8c33876ed..c4630a1fd 100644 --- a/packages/uni-app-plus/src/service/api/index.ts +++ b/packages/uni-app-plus/src/service/api/index.ts @@ -56,6 +56,7 @@ export * from './plugin/registerRuntime' export * from './plugin/share' export * from './plugin/requestPayment' export * from './plugin/vuePlugin' +export * from './plugin/restoreGlobal' export * from './ad/rewardedVideoAd' export * from './ad/fullScreenVideoAd' diff --git a/packages/uni-app-plus/src/service/api/plugin/restoreGlobal.ts b/packages/uni-app-plus/src/service/api/plugin/restoreGlobal.ts new file mode 100644 index 000000000..1688c8765 --- /dev/null +++ b/packages/uni-app-plus/src/service/api/plugin/restoreGlobal.ts @@ -0,0 +1,43 @@ +import { + newSetStatusBarStyle, + restoreOldSetStatusBarStyle, +} from '../../statusBar' + +export function restoreGlobal( + newVue: unknown, + newWeex: unknown, + newPlus: unknown, + newSetTimeout: unknown, + newClearTimeout: unknown, + newSetInterval: unknown, + newClearInterval: unknown +) { + // 确保部分全局变量 是 app-service 中的 + // 若首页 nvue 初始化比 app-service 快,导致框架处于该 nvue 环境下 + // plus 如果不用 app-service,资源路径会出问题 + // 若首页 nvue 被销毁,如 redirectTo 或 reLaunch,则这些全局功能会损坏 + + // 设置 vue3 + // @ts-ignore 最终__VUE__会被替换为vue + __VUE__ = newVue + if (plus !== newPlus) { + if (__DEV__) { + console.log(`[restoreGlobal][${Date.now()}]`) + } + weex = newWeex + // @ts-ignore + plus = newPlus + restoreOldSetStatusBarStyle(plus.navigator.setStatusBarStyle) + plus.navigator.setStatusBarStyle = newSetStatusBarStyle + /* eslint-disable no-global-assign */ + // @ts-ignore + setTimeout = newSetTimeout + // @ts-ignore + clearTimeout = newClearTimeout + // @ts-ignore + setInterval = newSetInterval + // @ts-ignore + clearInterval = newClearInterval + } + __uniConfig.serviceReady = true +} diff --git a/packages/uni-app-plus/src/service/framework/page/register.ts b/packages/uni-app-plus/src/service/framework/page/register.ts index d1541fcf6..8a1ac5f8d 100644 --- a/packages/uni-app-plus/src/service/framework/page/register.ts +++ b/packages/uni-app-plus/src/service/framework/page/register.ts @@ -163,6 +163,8 @@ function createNVueVm( return { $: {}, // navigateBack 时,invokeHook 会调用 $ onNVuePageCreated(vm: ComponentPublicInstance, curNVuePage: unknown) { + ;(vm as any).$ = {} // 补充一个 nvue 的 $ 对象,模拟 vue3 的,不然有部分地方访问了 $ + vm.$getAppWebview = () => webview // 替换真实的 nvue 的 vm initPageVm(vm, pageInstance) const pages = getAllPages() diff --git a/packages/uni-app-plus/src/service/framework/webview/init/index.ts b/packages/uni-app-plus/src/service/framework/webview/init/index.ts index 80f0adbb8..519895450 100644 --- a/packages/uni-app-plus/src/service/framework/webview/init/index.ts +++ b/packages/uni-app-plus/src/service/framework/webview/init/index.ts @@ -10,7 +10,8 @@ export function initWebview( ) { // 首页或非 nvue 页面 if (webview.id === '1' || !routeMeta.isNVue) { - initWebviewStyle(webview, path, query, routeMeta) + // path 必须参数为空,因为首页已经在 manifest.json 中设置了 uniNView,不能再次设置,否则会二次加载 + initWebviewStyle(webview, '', query, routeMeta) } initSubNVues(webview, path, routeMeta) initWebviewEvent(webview) diff --git a/packages/uni-app-plus/src/service/framework/webview/style/index.ts b/packages/uni-app-plus/src/service/framework/webview/style/index.ts index 617b69151..ede829e41 100644 --- a/packages/uni-app-plus/src/service/framework/webview/style/index.ts +++ b/packages/uni-app-plus/src/service/framework/webview/style/index.ts @@ -23,7 +23,6 @@ export function parseWebviewStyle( routeMeta[name as keyof UniApp.PageRouteMeta] } }) - initNVue(webviewStyle, routeMeta, path) initPopGesture(webviewStyle, routeMeta) initBackgroundColor(webviewStyle, routeMeta) diff --git a/packages/uni-cli-shared/src/json/app/manifest/index.ts b/packages/uni-cli-shared/src/json/app/manifest/index.ts index 15bdb97f9..c3b19edc2 100644 --- a/packages/uni-cli-shared/src/json/app/manifest/index.ts +++ b/packages/uni-cli-shared/src/json/app/manifest/index.ts @@ -19,7 +19,6 @@ export function normalizeAppManifestJson( initAppStatusbar(initDefaultManifestJson(), pagesJson), userManifestJson ) - initArguments(manifestJson, pagesJson) initPlus(manifestJson, pagesJson) initNVue(manifestJson, pagesJson) @@ -27,8 +26,12 @@ export function normalizeAppManifestJson( initSplashscreen(manifestJson, userManifestJson) initConfusion(manifestJson) initUniApp(manifestJson) - initLaunchwebview(manifestJson, pagesJson) // 依赖 initArguments 先执行 - initTabBar(manifestJson, pagesJson) // 依赖 initLaunchwebview 先执行 + // 依赖 initArguments 先执行 + initTabBar( + initLaunchwebview(manifestJson, pagesJson), + manifestJson, + pagesJson + ) return manifestJson } diff --git a/packages/uni-cli-shared/src/json/app/manifest/launchwebview.ts b/packages/uni-cli-shared/src/json/app/manifest/launchwebview.ts index 2b8eb74f7..8dd54ecde 100644 --- a/packages/uni-cli-shared/src/json/app/manifest/launchwebview.ts +++ b/packages/uni-cli-shared/src/json/app/manifest/launchwebview.ts @@ -13,8 +13,6 @@ export function initLaunchwebview( } } catch (e) {} } - // 标记入口页,方便后边的 initTabBar 使用 - process.env.UNI_ENTRY_PAGE_PATH = entryPagePath manifestJson.plus.useragent.value = 'uni-app' extend(manifestJson.plus.launchwebview, { @@ -26,8 +24,8 @@ export function initLaunchwebview( const entryPage = pagesJson.pages.find((p) => p.path === entryPagePath) if (entryPage?.style.isNVue) { manifestJson.plus.launchwebview.uniNView = { path: entryPagePath + '.js' } - manifestJson.plus.launchwebview.id = '2' } else { manifestJson.launch_path = '__uniappview.html' } + return entryPagePath } diff --git a/packages/uni-cli-shared/src/json/app/manifest/tabBar.ts b/packages/uni-cli-shared/src/json/app/manifest/tabBar.ts index ccfb72556..091115968 100644 --- a/packages/uni-cli-shared/src/json/app/manifest/tabBar.ts +++ b/packages/uni-cli-shared/src/json/app/manifest/tabBar.ts @@ -3,6 +3,7 @@ import { SELECTED_COLOR, TABBAR_HEIGHT } from '@dcloudio/uni-shared' const TABBAR_WHITE = 'rgba(255,255,255,0.4)' const TABBAR_BLACK = 'rgba(0,0,0,0.4)' export function initTabBar( + entryPagePath: string, manifestJson: Record, pagesJson: UniApp.PagesJson ) { @@ -23,9 +24,7 @@ export function initTabBar( tabBar.height = `${parseFloat(tabBar.height!) || TABBAR_HEIGHT}px` // 首页是 tabBar 页面 - const item = tabBar.list.find( - (page) => page.pagePath === process.env.UNI_ENTRY_PAGE_PATH - ) + const item = tabBar.list.find((page) => page.pagePath === entryPagePath) if (item) { ;(tabBar as any).child = ['lauchwebview'] ;(tabBar as any).selected = tabBar.list.indexOf(item) diff --git a/packages/uni-cli-shared/src/json/app/pages/code.ts b/packages/uni-cli-shared/src/json/app/pages/code.ts index f1a469dec..e17f3e690 100644 --- a/packages/uni-cli-shared/src/json/app/pages/code.ts +++ b/packages/uni-cli-shared/src/json/app/pages/code.ts @@ -12,8 +12,9 @@ if (typeof Promise !== 'undefined' && !Promise.prototype.finally) { } ` export const restoreGlobalCode = ` +import * as vue from 'vue' if(uni.restoreGlobal){ - uni.restoreGlobal(weex,plus,setTimeout,clearTimeout,setInterval,clearInterval) + uni.restoreGlobal(vue,weex,plus,setTimeout,clearTimeout,setInterval,clearInterval) } ` diff --git a/packages/uni-core/src/helpers/hook.ts b/packages/uni-core/src/helpers/hook.ts index 96613503c..56c37813e 100644 --- a/packages/uni-core/src/helpers/hook.ts +++ b/packages/uni-core/src/helpers/hook.ts @@ -31,11 +31,12 @@ export function invokeHook( return } // 兼容 nvue - - const hooks = - __PLATFORM__ === 'app' - ? ((vm as any)._$weex ? vm.$options : vm.$)[name as string] - : vm.$[name as string] + if (__PLATFORM__ === 'app') { + if ((vm as any).__call_hook) { + return (vm as any).__call_hook(name, args) + } + } + const hooks = vm.$[name as string] return hooks && invokeArrayFns(hooks, args) } diff --git a/packages/vite-plugin-uni/src/cli/action.ts b/packages/vite-plugin-uni/src/cli/action.ts index 1d0456b28..c7bc46d6c 100644 --- a/packages/vite-plugin-uni/src/cli/action.ts +++ b/packages/vite-plugin-uni/src/cli/action.ts @@ -7,12 +7,13 @@ import { createServer, createSSRServer } from './server' import { initEnv } from './utils' export async function runDev(options: CliOptions & ServerOptions) { + extend(options, { watch: true, minify: false }) initEnv('dev', options) try { if (options.platform === 'h5') { await (options.ssr ? createSSRServer(options) : createServer(options)) } else { - await build(extend(options, { watch: true })) + await build(options) } if (options.platform === 'app') { await runNVue('dev') diff --git a/packages/vite-plugin-uni/src/cli/build.ts b/packages/vite-plugin-uni/src/cli/build.ts index 8514e63a3..bb40391ec 100644 --- a/packages/vite-plugin-uni/src/cli/build.ts +++ b/packages/vite-plugin-uni/src/cli/build.ts @@ -1,17 +1,14 @@ import fs from 'fs-extra' import path from 'path' -import { build as buildByVite, BuildOptions } from 'vite' +import { build as buildByVite, BuildOptions, InlineConfig } from 'vite' import { CliOptions } from '.' import { addConfigFile, cleanOptions } from './utils' export async function build(options: CliOptions) { await buildByVite( - addConfigFile({ - root: process.env.VITE_ROOT_DIR, - logLevel: options.logLevel, - clearScreen: options.clearScreen, - build: cleanOptions(options) as BuildOptions, - }) + addConfigFile( + initBuildOptions(options, cleanOptions(options) as BuildOptions) + ) ) } @@ -24,12 +21,7 @@ export async function buildSSR(options: CliOptions) { ssrBuildClientOptions.outDir = process.env.UNI_OUTPUT_DIR process.env.UNI_SSR_CLIENT = 'true' await buildByVite( - addConfigFile({ - root: process.env.VITE_ROOT_DIR, - logLevel: options.logLevel, - clearScreen: options.clearScreen, - build: ssrBuildClientOptions, - }) + addConfigFile(initBuildOptions(options, ssrBuildClientOptions)) ) const ssrServerDir = path.resolve(outputDir, 'server') process.env.UNI_OUTPUT_DIR = ssrServerDir @@ -54,12 +46,7 @@ export async function buildSSR(options: CliOptions) { process.env.UNI_SSR_CLIENT = '' process.env.UNI_SSR_SERVER = 'true' await buildByVite( - addConfigFile({ - root: process.env.VITE_ROOT_DIR, - logLevel: options.logLevel, - clearScreen: options.clearScreen, - build: ssrBuildServerOptions, - }) + addConfigFile(initBuildOptions(options, ssrBuildServerOptions)) ) // copy ssr-manfiest.json to server const assets = ['ssr-manifest.json', 'index.html'] @@ -70,3 +57,16 @@ export async function buildSSR(options: CliOptions) { } }) } + +function initBuildOptions( + options: CliOptions, + build: BuildOptions +): InlineConfig { + return { + root: process.env.VITE_ROOT_DIR, + logLevel: options.logLevel, + clearScreen: options.clearScreen, + mode: process.env.NODE_ENV, + build, + } +} diff --git a/packages/vite-plugin-uni/src/cli/utils.ts b/packages/vite-plugin-uni/src/cli/utils.ts index 87ee5a6a8..276d95f06 100644 --- a/packages/vite-plugin-uni/src/cli/utils.ts +++ b/packages/vite-plugin-uni/src/cli/utils.ts @@ -75,14 +75,14 @@ export function initEnv(type: 'dev' | 'build', options: CliOptions) { process.env.UNI_OUTPUT_DIR = (options as BuildOptions).outDir! } // tips - if (isInHBuilderX() && options.platform === 'app') { - return ( - console.error( - `当前项目 Vue 版本为3,暂不支持编译至 App 端,近期将升级支持。` - ), - process.exit(1) - ) - } + // if (isInHBuilderX() && options.platform === 'app') { + // return ( + // console.error( + // `当前项目 Vue 版本为3,暂不支持编译至 App 端,近期将升级支持。` + // ), + // process.exit(1) + // ) + // } if (process.env.UNI_PLATFORM === 'app') { initNVueEnv() } diff --git a/rollup.config.js b/rollup.config.js index 96d88e2e4..d3834d882 100644 --- a/rollup.config.js +++ b/rollup.config.js @@ -132,6 +132,27 @@ function createConfig(entryFile, output, buildOption) { }) ) } + if (buildOption.replaceAfterBundled) { + const replacements = buildOption.replaceAfterBundled + plugins.push({ + name: 'replace-after-bundled', + generateBundle(_options, bundles) { + Object.keys(bundles).forEach((name) => { + const bundle = bundles[name] + if (!bundle.code) { + return + } + Object.keys(replacements).forEach((replacement) => { + bundle.code = bundle.code.replace( + new RegExp(replacement, 'g'), + replacements[replacement] + ) + }) + }) + }, + }) + } + return { input: resolve(entryFile), external, -- GitLab