From fc80c34208473c1d015ce4cadeebc01a4b3ede89 Mon Sep 17 00:00:00 2001 From: jry Date: Fri, 24 Jul 2026 18:17:13 +0800 Subject: [PATCH 1/7] feat: support nvue pages --- README.md | 23 ++++++++ src/index.ts | 48 ++++++++++------ src/page.ts | 92 ++++++++++++++++++++++++++---- src/utils.ts | 31 ++++++++-- test/transform.test.ts | 125 ++++++++++++++++++++++++++++++++++++++--- 5 files changed, 279 insertions(+), 40 deletions(-) diff --git a/README.md b/README.md index 19af487..cc48ff2 100644 --- a/README.md +++ b/README.md @@ -22,6 +22,7 @@ - 自定义虚拟根组件文件命名(App.ku.vue文件命名支持更换) - 更高灵活度的获取虚拟根组件实例(获取KuRootView的Ref) - 自动提取PageMeta到页面顶层(自动提升小程序PageMeta[用于阻止滚动穿透]组件) +- 支持 nvue 页面使用虚拟根组件 ### 📦 安装 @@ -287,6 +288,27 @@ export default defineConfig({ +
+ + + (点击展开) 功能四:支持 nvue 页面 + +
+ +Root 会识别 `pages.json` 中对应的 `.nvue` 页面,并在页面内本地引入虚拟根组件进行包裹。由于 nvue 页面不依赖 Vue 全局组件注册,插件会自动把 `App.ku.vue` 作为局部组件注入到 nvue 页面中。 + +支持特性: + +- `pages.json` 中未写扩展名时,会同时匹配已存在的 `.vue` 与 `.nvue` 页面文件 +- nvue 页面会被 `` 包裹,效果与普通页面的 `` 对齐 +- `PageMeta` / `page-meta` 会继续自动提取到页面顶层 +- `template root="uniKuRoot"` 与 `enabledGlobalRef` 仍然生效 + +> [!Note] +> 如果 `App.ku.vue` 需要包裹 nvue 页面,请确保其中使用 nvue 支持的组件与样式。组件库主题、原生导航栏、TabBar 等平台特定逻辑建议放在业务或组件库自身运行时中处理。 + +
+ ### ✨ 例子 > [!TIP] @@ -535,6 +557,7 @@ function showToast() { - [x] 支持热更新 - [x] 支持VueSFC - [x] 支持小程序PageMeta +- [x] 支持 nvue 页面 - [ ] 支持 App.ku.vue 内直接编写控制逻辑 - [ ] 补全单元测试 diff --git a/src/index.ts b/src/index.ts index fa2ec9d..e2991a3 100644 --- a/src/index.ts +++ b/src/index.ts @@ -2,13 +2,13 @@ import type { MagicString } from '@vue/compiler-sfc' import type { FSWatcher } from 'chokidar' import type { Plugin } from 'vite' -import { resolve } from 'node:path' +import { dirname, relative, resolve } from 'node:path' import process from 'node:process' import chokidar from 'chokidar' -import { createFilter } from 'vite' +import { createFilter, normalizePath } from 'vite' -import { transformPage } from './page' +import { transformNvuePage, transformPage } from './page' import { rebuildKuApp, registerKuApp } from './root' import { loadPagesJson, normalizePlatformPath, toArray } from './utils' @@ -46,10 +46,23 @@ export default function UniKuRoot(options?: UniKuRootOptions): Plugin { ...options, } - const rootPath = process.env.UNI_INPUT_DIR || (`${process.env.INIT_CWD}\\src`) - const appKuPath = resolve(rootPath, `${options.rootFileName}.vue`) - const pagesPath = resolve(rootPath, 'pages.json') - const excludedPaths = toArray(options.excludePages).filter(Boolean).map(path => resolve(rootPath, path!)) + const rootFileName = String(options.rootFileName || 'App.ku').replace(/\.vue$/i, '') + const rootPath = normalizePath(process.env.UNI_INPUT_DIR || resolve(process.env.INIT_CWD || process.cwd(), 'src')) + const appKuPath = normalizePath(resolve(rootPath, `${rootFileName}.vue`)) + const pagesPath = normalizePath(resolve(rootPath, 'pages.json')) + const excludedPaths = toArray(options.excludePages).filter(Boolean).map(path => normalizePath(resolve(rootPath, path!))) + const mainFiles = [ + normalizePath(resolve(rootPath, 'main.ts')), + normalizePath(resolve(rootPath, 'main.js')), + ] + + const getRelativeImportPath = (fromFile: string, toFile: string) => { + let importPath = normalizePath(relative(dirname(fromFile), toFile)) + if (!importPath.startsWith('.')) { + importPath = `./${importPath}` + } + return importPath + } let pagesJson = loadPagesJson(pagesPath, rootPath) @@ -72,25 +85,26 @@ export default function UniKuRoot(options?: UniKuRootOptions): Plugin { }, async transform(code, id) { let ms: MagicString | null = null + const isSfcBlock = id.includes('?') + const cleanId = normalizePath(id.split('?')[0]) - const filterMain = createFilter([ - `${rootPath}/main.ts`, - `${rootPath}/main.js`, - ]) - if (filterMain(id)) { - ms = await registerKuApp(code, options.rootFileName) + const filterMain = createFilter(mainFiles) + if (filterMain(cleanId)) { + ms = await registerKuApp(code, rootFileName) } const filterKuRoot = createFilter(appKuPath) - if (filterKuRoot(id)) { + if (filterKuRoot(cleanId)) { ms = await rebuildKuApp(code, options.enabledVirtualHost) } - const pageId = hasPlatformPlugin ? normalizePlatformPath(id) : id + const pageId = hasPlatformPlugin ? normalizePlatformPath(cleanId) : cleanId const filterPage = createFilter(pagesJson, excludedPaths) - if (filterPage(pageId)) { - ms = await transformPage(code, options.enabledGlobalRef) + if (!isSfcBlock && filterPage(pageId)) { + ms = cleanId.endsWith('.nvue') + ? await transformNvuePage(code, getRelativeImportPath(cleanId, appKuPath), options.enabledGlobalRef) + : await transformPage(code, options.enabledGlobalRef) } if (ms) { diff --git a/src/page.ts b/src/page.ts index 8a4e5a3..4fc07e1 100644 --- a/src/page.ts +++ b/src/page.ts @@ -1,11 +1,17 @@ +import type { SFCDescriptor, SFCScriptBlock } from '@vue/compiler-sfc' import { MagicString } from '@vue/compiler-sfc' import { findNode, parseSFC } from './utils' -export async function transformPage(code: string, enabledGlobalRef = false) { - const sfc = await parseSFC(code) - const ms = new MagicString(code) +function getPageRootRefSource(sfc: SFCDescriptor, enabledGlobalRef = false) { + const pageTempAttrs = sfc.template?.attrs + if (pageTempAttrs && pageTempAttrs.root) { + return `ref="${pageTempAttrs.root as string}"` + } + return enabledGlobalRef ? 'ref="uniKuRoot"' : '' +} +function wrapTemplate(ms: MagicString, sfc: SFCDescriptor, tagName: string, rootRefSource = '') { const pageTempStart = sfc.template?.loc.start.offset const pageTempEnd = sfc.template?.loc.end.offset @@ -21,17 +27,83 @@ export async function transformPage(code: string, enabledGlobalRef = false) { ms.remove(metaTempStart, metaTempEnd) } - const pageTempAttrs = sfc.template?.attrs + if (pageTempStart != null && pageTempEnd != null) { + const refSource = rootRefSource ? ` ${rootRefSource}` : '' + const pageMetaPrefix = pageMetaSource ? `\n${pageMetaSource}\n` : '\n' + ms.appendLeft(pageTempStart, `${pageMetaPrefix}<${tagName}${refSource}>`) + ms.appendRight(pageTempEnd, `\n\n`) + } +} - let pageRootRefSource = enabledGlobalRef ? 'ref="uniKuRoot"' : '' +export async function transformPage(code: string, enabledGlobalRef = false) { + const sfc = await parseSFC(code) + const ms = new MagicString(code) - if (pageTempAttrs && pageTempAttrs.root) { - pageRootRefSource = `ref="${pageTempAttrs.root as string}"` + wrapTemplate(ms, sfc, 'global-ku-root', getPageRootRefSource(sfc, enabledGlobalRef)) + + return ms +} + +function getNvueRootImport(componentImportPath: string) { + return `import GlobalKuRoot from '${componentImportPath}'\n` +} + +function findExportDefaultObjectStart(scriptContent: string) { + const exportDefaultIndex = scriptContent.indexOf('export default') + if (exportDefaultIndex < 0) { + return -1 + } + + const afterExportDefault = scriptContent.slice(exportDefaultIndex + 'export default'.length) + const defineComponentMatch = afterExportDefault.match(/^\s*defineComponent\s*\(\s*\{/) + if (defineComponentMatch) { + return exportDefaultIndex + 'export default'.length + defineComponentMatch[0].length + } + + const objectMatch = afterExportDefault.match(/^\s*\{/) + if (objectMatch) { + return exportDefaultIndex + 'export default'.length + objectMatch[0].length } - if (pageTempStart && pageTempEnd) { - ms.appendLeft(pageTempStart, `\n${pageMetaSource}\n`) - ms.appendRight(pageTempEnd, `\n\n`) + return -1 +} + +function ensureNvueOptionsComponent(ms: MagicString, script: SFCScriptBlock) { + const componentsMatch = script.content.match(/components\s*:\s*\{/) + if (componentsMatch && componentsMatch.index != null) { + ms.appendLeft(componentsMatch.index + componentsMatch[0].length, ' GlobalKuRoot,') + return + } + + const exportDefaultObjectStart = findExportDefaultObjectStart(script.content) + if (exportDefaultObjectStart >= 0) { + ms.appendLeft( + exportDefaultObjectStart, + '\n components: { GlobalKuRoot },', + ) + return + } + + ms.appendLeft(script.loc.end.offset, '\nexport default {\n components: { GlobalKuRoot },\n}\n') +} + +export async function transformNvuePage(code: string, componentImportPath: string, enabledGlobalRef = false) { + const sfc = await parseSFC(code) + const ms = new MagicString(code) + + wrapTemplate(ms, sfc, 'GlobalKuRoot', getPageRootRefSource(sfc, enabledGlobalRef)) + + if (sfc.scriptSetup) { + ms.appendLeft(sfc.scriptSetup.loc.start.offset, getNvueRootImport(componentImportPath)) + } + else if (sfc.script) { + ms.appendLeft(sfc.script.loc.start.offset, getNvueRootImport(componentImportPath)) + ensureNvueOptionsComponent(ms, sfc.script) + } + else { + ms.append( + `\n\n`, + ) } return ms diff --git a/src/utils.ts b/src/utils.ts index 9e1d891..8e656d4 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -1,5 +1,5 @@ import type { SFCDescriptor } from '@vue/compiler-sfc' -import { readFileSync } from 'node:fs' +import { existsSync, readFileSync } from 'node:fs' import { extname, join } from 'node:path' import process from 'node:process' @@ -18,8 +18,29 @@ export async function parseSFC(code: string): Promise { } } +const PAGE_FILE_EXTS = ['.vue', '.nvue'] + +export function formatPagePaths(root: string, path: string): string[] { + const joinedPath = join(root, path) + const pathExt = extname(joinedPath) + + if (pathExt) { + return [normalizePath(joinedPath)] + } + + const pageFilePaths = PAGE_FILE_EXTS + .map(fileExt => `${joinedPath}${fileExt}`) + .filter(filePath => existsSync(filePath)) + + if (pageFilePaths.length) { + return pageFilePaths.map(filePath => normalizePath(filePath)) + } + + return [normalizePath(`${joinedPath}.vue`)] +} + export function formatPagePath(root: string, path: string) { - return normalizePath(`${join(root, path)}.vue`) + return formatPagePaths(root, path)[0] } export function loadPagesJson(path: string, rootPath: string): string[] { @@ -31,10 +52,10 @@ export function loadPagesJson(path: string, rootPath: string): string[] { return [ ...pages - .map((page: any) => formatPagePath(rootPath, page.path)), + .flatMap((page: any) => formatPagePaths(rootPath, page.path)), ...subPackages - .map(({ pages = {}, root = '' }: any) => { - return pages.map((page: any) => formatPagePath(join(rootPath, root), page.path)) + .map(({ pages = [], root = '' }: any) => { + return pages.flatMap((page: any) => formatPagePaths(join(rootPath, root), page.path)) }) .flat(), ] diff --git a/test/transform.test.ts b/test/transform.test.ts index 0e78514..68f8144 100644 --- a/test/transform.test.ts +++ b/test/transform.test.ts @@ -1,22 +1,99 @@ -import { mkdtempSync, rmSync, writeFileSync } from 'node:fs' +import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from 'node:fs' import { tmpdir } from 'node:os' import { join } from 'node:path' +import { normalizePath } from 'vite' import { afterEach, describe, expect, it } from 'vitest' +import { transformNvuePage, transformPage } from '../src/page' import { loadPagesJson } from '../src/utils' -const tempDirectories: string[] = [] +const tempDirs: string[] = [] afterEach(() => { - tempDirectories.splice(0).forEach(directory => rmSync(directory, { recursive: true, force: true })) + while (tempDirs.length) { + rmSync(tempDirs.pop()!, { recursive: true, force: true }) + } }) -describe('loadPagesJson', () => { +function createTempRoot() { + const rootPath = mkdtempSync(join(tmpdir(), 'uni-ku-root-')) + tempDirs.push(rootPath) + return rootPath +} + +describe('page transforms', () => { + it('wraps vue pages and keeps page-meta above the root component', async () => { + const code = ` +` + + const result = (await transformPage(code, true)).toString() + + expect(result).toContain('\n') + expect(result).toContain('\n\n') + expect(result.match(//g)).toHaveLength(1) + }) + + it('locally imports the root component for nvue script setup pages', async () => { + const code = ` + + +` + + const result = (await transformNvuePage(code, '../App.ku.vue', true)).toString() + + expect(result).toContain('') + expect(result).toContain('\n\n') + expect(result).toContain('import GlobalKuRoot from \'../App.ku.vue\'\n\nconst label') + }) + + it('registers the local root component for nvue options api pages', async () => { + const code = ` + + +` + + const result = (await transformNvuePage(code, '../../App.ku.vue')).toString() + + expect(result).toContain('import GlobalKuRoot from \'../../App.ku.vue\'') + expect(result).toContain('components: { GlobalKuRoot, LocalThing }') + expect(result).toContain('') + }) + + it('adds an options api script when an nvue page has no script block', async () => { + const code = ` +` + + const result = (await transformNvuePage(code, './App.ku.vue')).toString() + + expect(result).toContain('') + expect(result).toContain('\n`, + ) + } + + return ms +} + function getPageRootRefSource(sfc: SFCDescriptor, enabledGlobalRef = false) { const pageTempAttrs = sfc.template?.attrs if (pageTempAttrs && pageTempAttrs.root) { @@ -35,15 +66,6 @@ function wrapTemplate(ms: MagicString, sfc: SFCDescriptor, tagName: string, root } } -export async function transformPage(code: string, enabledGlobalRef = false) { - const sfc = await parseSFC(code) - const ms = new MagicString(code) - - wrapTemplate(ms, sfc, 'global-ku-root', getPageRootRefSource(sfc, enabledGlobalRef)) - - return ms -} - function getNvueRootImport(componentImportPath: string) { return `import GlobalKuRoot from '${componentImportPath}'\n` } @@ -86,25 +108,3 @@ function ensureNvueOptionsComponent(ms: MagicString, script: SFCScriptBlock) { ms.appendLeft(script.loc.end.offset, '\nexport default {\n components: { GlobalKuRoot },\n}\n') } - -export async function transformNvuePage(code: string, componentImportPath: string, enabledGlobalRef = false) { - const sfc = await parseSFC(code) - const ms = new MagicString(code) - - wrapTemplate(ms, sfc, 'GlobalKuRoot', getPageRootRefSource(sfc, enabledGlobalRef)) - - if (sfc.scriptSetup) { - ms.appendLeft(sfc.scriptSetup.loc.start.offset, getNvueRootImport(componentImportPath)) - } - else if (sfc.script) { - ms.appendLeft(sfc.script.loc.start.offset, getNvueRootImport(componentImportPath)) - ensureNvueOptionsComponent(ms, sfc.script) - } - else { - ms.append( - `\n\n`, - ) - } - - return ms -} diff --git a/src/utils.ts b/src/utils.ts index 8e656d4..6c37536 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -1,6 +1,6 @@ import type { SFCDescriptor } from '@vue/compiler-sfc' import { existsSync, readFileSync } from 'node:fs' -import { extname, join } from 'node:path' +import { dirname, extname, join, relative } from 'node:path' import process from 'node:process' import { parse as VueParser } from '@vue/compiler-sfc' @@ -43,7 +43,7 @@ export function formatPagePath(root: string, path: string) { return formatPagePaths(root, path)[0] } -export function loadPagesJson(path: string, rootPath: string): string[] { +export function loadPagePaths(path: string, rootPath: string): string[] { const pagesJsonRaw = readFileSync(path, 'utf-8') const pagesJson = jsonParse(pagesJsonRaw) @@ -152,3 +152,8 @@ export function toArray(value: T | T[]): T[] { return Array.isArray(value) ? value : [value] } + +export function getRelativePath(fromFile: string, toFile: string) { + const importPath = normalizePath(relative(dirname(fromFile), toFile)) + return importPath.startsWith('.') ? importPath : `./${importPath}` +} diff --git a/test/transform.test.ts b/test/transform.test.ts index 68f8144..f52b73a 100644 --- a/test/transform.test.ts +++ b/test/transform.test.ts @@ -6,7 +6,7 @@ import { normalizePath } from 'vite' import { afterEach, describe, expect, it } from 'vitest' import { transformNvuePage, transformPage } from '../src/page' -import { loadPagesJson } from '../src/utils' +import { loadPagePaths } from '../src/utils' const tempDirs: string[] = [] @@ -103,7 +103,7 @@ describe('pages.json loading', () => { }], })) - expect(loadPagesJson(pagesJsonPath, '/project/src')).toEqual([ + expect(loadPagePaths(pagesJsonPath, '/project/src')).toEqual([ '/project/src/pages/index.vue', '/project/src/packages/example/pages/detail.vue', ]) @@ -131,7 +131,7 @@ describe('pages.json loading', () => { ] }`) - const pages = loadPagesJson(join(rootPath, 'pages.json'), rootPath) + const pages = loadPagePaths(join(rootPath, 'pages.json'), rootPath) expect(pages).toEqual([ normalizePath(join(rootPath, 'pages', 'index.vue')), @@ -140,4 +140,4 @@ describe('pages.json loading', () => { normalizePath(join(rootPath, 'pkg', 'sub', 'profile.nvue')), ]) }) -}) \ No newline at end of file +}) From 439b000287c2e907bc652bcbc1f5e25b1fd73e86 Mon Sep 17 00:00:00 2001 From: Skiyee <319619193@qq.com> Date: Sat, 25 Jul 2026 15:42:21 +0800 Subject: [PATCH 3/7] =?UTF-8?q?fix(examples):=20=E5=B0=86=E7=A4=BA?= =?UTF-8?q?=E4=BE=8B=E7=BB=84=E4=BB=B6=E6=A0=87=E7=AD=BE=E6=94=B9=E4=B8=BA?= =?UTF-8?q?=20view?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 统一使用 uni-app 的 view 标签,避免在 App/nvue 场景下使用不兼容的 div。 --- examples/src/KuRoot.vue | 38 +++++++-------- examples/src/components/GlobalToast.vue | 64 ++++++++++++------------- examples/src/layouts/default.vue | 12 ++--- 3 files changed, 57 insertions(+), 57 deletions(-) diff --git a/examples/src/KuRoot.vue b/examples/src/KuRoot.vue index c8cdeb3..a29450b 100644 --- a/examples/src/KuRoot.vue +++ b/examples/src/KuRoot.vue @@ -1,19 +1,19 @@ - - - + + + diff --git a/examples/src/components/GlobalToast.vue b/examples/src/components/GlobalToast.vue index 7eed55a..f686279 100644 --- a/examples/src/components/GlobalToast.vue +++ b/examples/src/components/GlobalToast.vue @@ -1,32 +1,32 @@ - - - - - + + + + + diff --git a/examples/src/layouts/default.vue b/examples/src/layouts/default.vue index 679067a..ae56a72 100644 --- a/examples/src/layouts/default.vue +++ b/examples/src/layouts/default.vue @@ -1,6 +1,6 @@ - + From a50c6afd1228e2b600dccb9a20ba9c53f338615f Mon Sep 17 00:00:00 2001 From: Skiyee <319619193@qq.com> Date: Sat, 25 Jul 2026 15:42:37 +0800 Subject: [PATCH 4/7] =?UTF-8?q?chore(examples):=20=E8=A1=A5=E5=85=85=20App?= =?UTF-8?q?=20=E5=B9=B3=E5=8F=B0=E4=BE=9D=E8=B5=96=E4=B8=8E=E8=84=9A?= =?UTF-8?q?=E6=9C=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 新增 uni-app-plus / uni-app-vite 依赖,并提供 dev:app、build:app 脚本便于本地验证 nvue。 --- examples/package.json | 6 +- pnpm-lock.yaml | 177 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 182 insertions(+), 1 deletion(-) diff --git a/examples/package.json b/examples/package.json index e9c66cf..700b6d5 100644 --- a/examples/package.json +++ b/examples/package.json @@ -6,10 +6,14 @@ "dev": "uni", "build": "uni build", "dev:mp-weixin": "uni -p mp-weixin", - "build:mp-weixin": "uni build -p mp-weixin" + "build:mp-weixin": "uni build -p mp-weixin", + "dev:app": "uni -p app", + "build:app": "uni build -p app" }, "dependencies": { "@dcloudio/uni-app": "3.0.0-4040520250104002", + "@dcloudio/uni-app-plus": "3.0.0-4040520250104002", + "@dcloudio/uni-app-vite": "3.0.0-4040520250104002", "@dcloudio/uni-components": "3.0.0-4040520250104002", "@dcloudio/uni-h5": "3.0.0-4040520250104002", "@dcloudio/uni-mp-weixin": "3.0.0-4040520250104002", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index f0d2237..bdff914 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -66,6 +66,12 @@ importers: '@dcloudio/uni-app': specifier: 3.0.0-4040520250104002 version: 3.0.0-4040520250104002(@dcloudio/types@3.4.21)(postcss@8.5.6)(rollup@4.52.0)(vue@3.5.21(typescript@5.9.2)) + '@dcloudio/uni-app-plus': + specifier: 3.0.0-4040520250104002 + version: 3.0.0-4040520250104002(postcss@8.5.6)(rollup@4.52.0)(vite@5.2.1(@types/node@24.7.0)(terser@5.44.0))(vue@3.5.21(typescript@5.9.2)) + '@dcloudio/uni-app-vite': + specifier: 3.0.0-4040520250104002 + version: 3.0.0-4040520250104002(postcss@8.5.6)(rollup@4.52.0)(vite@5.2.1(@types/node@24.7.0)(terser@5.44.0))(vue@3.5.21(typescript@5.9.2)) '@dcloudio/uni-components': specifier: 3.0.0-4040520250104002 version: 3.0.0-4040520250104002(postcss@8.5.6)(rollup@4.52.0)(vue@3.5.21(typescript@5.9.2)) @@ -827,6 +833,18 @@ packages: '@dcloudio/types@3.4.21': resolution: {integrity: sha512-rsv3XfAaD/dtuVboPeYh+vPcULnWyozGaGKHWyN0dYRm7L1uypFUM30qNYMj9iNmbAENuBjV177S1gNEBIvdDA==} + '@dcloudio/uni-app-plus@3.0.0-4040520250104002': + resolution: {integrity: sha512-skRBNw1LZQdQdXGFoPaDlo4Baq27k4yAb2LHeVbbJS0Rx8aRSe7ePuy1NeBomCTAQuOJqA+ZAbyVD6yZSNINFw==} + + '@dcloudio/uni-app-uts@3.0.0-4040520250104002': + resolution: {integrity: sha512-+HZ6cZsmpWeKHvgTHiFUZP2vFLFoDkp4wwbTYk/ZwEUAkYVMtlCoz+CfWCvfNhBPj5bGDPGJauL1/Zf/zv7pmQ==} + + '@dcloudio/uni-app-vite@3.0.0-4040520250104002': + resolution: {integrity: sha512-PxmTt0e3J8P1mCxFzRRlyltC0h4/bVI2KvnFEuoepVk7yUapl8El+yw5ZaEmBMi/fhySlE5yUQJkTrirvUF8wg==} + + '@dcloudio/uni-app-vue@3.0.0-4040520250104002': + resolution: {integrity: sha512-wBsuDOzE3ymT0G61fiCcG1l5kvQtCl7r0PVF/FT//fALuc3l9UbV4ZXNvkwUlhezPJVWtcYQX3p+1m4nTkZt5Q==} + '@dcloudio/uni-app@3.0.0-4040520250104002': resolution: {integrity: sha512-t6uewI90Yeuf3BQJF2Qk/NnDiPzYL1aoIeNXF4RCM282WyZGPAr5Kj+uqajaMAbLcOfZgmHqjWDRoSUzDSFpEg==} peerDependencies: @@ -875,6 +893,9 @@ packages: '@dcloudio/uni-mp-weixin@3.0.0-4040520250104002': resolution: {integrity: sha512-6UH4FNTqwh/Y5ZQ9fKmCRiaFy7BAOBfsQz2Pilay0TtScvz4em9mE688VVhuwhbvLHnmHSbiu6cPEUGgMb1Svw==} + '@dcloudio/uni-nvue-styler@3.0.0-4040520250104002': + resolution: {integrity: sha512-So6+GEOPcODJrjdY4aD5xUVOdEFRdQxxlwV0WNwvqJAP5xEb3WyeaEdmWsHo5yB50qkcSoCLXi13N/y0x0hAIw==} + '@dcloudio/uni-push@3.0.0-4040520250104002': resolution: {integrity: sha512-ph2gii/AEmh42Ch4QwO/rv7oVDMhg88JnBDdg8KNLCwMY/WUTdYn3EUEuyOFP+pkVFyHk2YJ8rD2pwCmcjSfYw==} @@ -1796,56 +1817,67 @@ packages: resolution: {integrity: sha512-aL6hRwu0k7MTUESgkg7QHY6CoqPgr6gdQXRJI1/VbFlUMwsSzPGSR7sG5d+MCbYnJmJwThc2ol3nixj1fvI/zQ==} cpu: [arm] os: [linux] + libc: [glibc] '@rollup/rollup-linux-arm-musleabihf@4.52.0': resolution: {integrity: sha512-BTs0M5s1EJejgIBJhCeiFo7GZZ2IXWkFGcyZhxX4+8usnIo5Mti57108vjXFIQmmJaRyDwmV59Tw64Ap1dkwMw==} cpu: [arm] os: [linux] + libc: [musl] '@rollup/rollup-linux-arm64-gnu@4.52.0': resolution: {integrity: sha512-uj672IVOU9m08DBGvoPKPi/J8jlVgjh12C9GmjjBxCTQc3XtVmRkRKyeHSmIKQpvJ7fIm1EJieBUcnGSzDVFyw==} cpu: [arm64] os: [linux] + libc: [glibc] '@rollup/rollup-linux-arm64-musl@4.52.0': resolution: {integrity: sha512-/+IVbeDMDCtB/HP/wiWsSzduD10SEGzIZX2945KSgZRNi4TSkjHqRJtNTVtVb8IRwhJ65ssI56krlLik+zFWkw==} cpu: [arm64] os: [linux] + libc: [musl] '@rollup/rollup-linux-loong64-gnu@4.52.0': resolution: {integrity: sha512-U1vVzvSWtSMWKKrGoROPBXMh3Vwn93TA9V35PldokHGqiUbF6erSzox/5qrSMKp6SzakvyjcPiVF8yB1xKr9Pg==} cpu: [loong64] os: [linux] + libc: [glibc] '@rollup/rollup-linux-ppc64-gnu@4.52.0': resolution: {integrity: sha512-X/4WfuBAdQRH8cK3DYl8zC00XEE6aM472W+QCycpQJeLWVnHfkv7RyBFVaTqNUMsTgIX8ihMjCvFF9OUgeABzw==} cpu: [ppc64] os: [linux] + libc: [glibc] '@rollup/rollup-linux-riscv64-gnu@4.52.0': resolution: {integrity: sha512-xIRYc58HfWDBZoLmWfWXg2Sq8VCa2iJ32B7mqfWnkx5mekekl0tMe7FHpY8I72RXEcUkaWawRvl3qA55og+cwQ==} cpu: [riscv64] os: [linux] + libc: [glibc] '@rollup/rollup-linux-riscv64-musl@4.52.0': resolution: {integrity: sha512-mbsoUey05WJIOz8U1WzNdf+6UMYGwE3fZZnQqsM22FZ3wh1N887HT6jAOjXs6CNEK3Ntu2OBsyQDXfIjouI4dw==} cpu: [riscv64] os: [linux] + libc: [musl] '@rollup/rollup-linux-s390x-gnu@4.52.0': resolution: {integrity: sha512-qP6aP970bucEi5KKKR4AuPFd8aTx9EF6BvutvYxmZuWLJHmnq4LvBfp0U+yFDMGwJ+AIJEH5sIP+SNypauMWzg==} cpu: [s390x] os: [linux] + libc: [glibc] '@rollup/rollup-linux-x64-gnu@4.52.0': resolution: {integrity: sha512-nmSVN+F2i1yKZ7rJNKO3G7ZzmxJgoQBQZ/6c4MuS553Grmr7WqR7LLDcYG53Z2m9409z3JLt4sCOhLdbKQ3HmA==} cpu: [x64] os: [linux] + libc: [glibc] '@rollup/rollup-linux-x64-musl@4.52.0': resolution: {integrity: sha512-2d0qRo33G6TfQVjaMR71P+yJVGODrt5V6+T0BDYH4EMfGgdC/2HWDVjSSFw888GSzAZUwuska3+zxNUCDco6rQ==} cpu: [x64] os: [linux] + libc: [musl] '@rollup/rollup-openharmony-arm64@4.52.0': resolution: {integrity: sha512-A1JalX4MOaFAAyGgpO7XP5khquv/7xKzLIyLmhNrbiCxWpMlnsTYr8dnsWM7sEeotNmxvSOEL7F65j0HXFcFsw==} @@ -2061,41 +2093,49 @@ packages: resolution: {integrity: sha512-34gw7PjDGB9JgePJEmhEqBhWvCiiWCuXsL9hYphDF7crW7UgI05gyBAi6MF58uGcMOiOqSJ2ybEeCvHcq0BCmQ==} cpu: [arm64] os: [linux] + libc: [glibc] '@unrs/resolver-binding-linux-arm64-musl@1.11.1': resolution: {integrity: sha512-RyMIx6Uf53hhOtJDIamSbTskA99sPHS96wxVE/bJtePJJtpdKGXO1wY90oRdXuYOGOTuqjT8ACccMc4K6QmT3w==} cpu: [arm64] os: [linux] + libc: [musl] '@unrs/resolver-binding-linux-ppc64-gnu@1.11.1': resolution: {integrity: sha512-D8Vae74A4/a+mZH0FbOkFJL9DSK2R6TFPC9M+jCWYia/q2einCubX10pecpDiTmkJVUH+y8K3BZClycD8nCShA==} cpu: [ppc64] os: [linux] + libc: [glibc] '@unrs/resolver-binding-linux-riscv64-gnu@1.11.1': resolution: {integrity: sha512-frxL4OrzOWVVsOc96+V3aqTIQl1O2TjgExV4EKgRY09AJ9leZpEg8Ak9phadbuX0BA4k8U5qtvMSQQGGmaJqcQ==} cpu: [riscv64] os: [linux] + libc: [glibc] '@unrs/resolver-binding-linux-riscv64-musl@1.11.1': resolution: {integrity: sha512-mJ5vuDaIZ+l/acv01sHoXfpnyrNKOk/3aDoEdLO/Xtn9HuZlDD6jKxHlkN8ZhWyLJsRBxfv9GYM2utQ1SChKew==} cpu: [riscv64] os: [linux] + libc: [musl] '@unrs/resolver-binding-linux-s390x-gnu@1.11.1': resolution: {integrity: sha512-kELo8ebBVtb9sA7rMe1Cph4QHreByhaZ2QEADd9NzIQsYNQpt9UkM9iqr2lhGr5afh885d/cB5QeTXSbZHTYPg==} cpu: [s390x] os: [linux] + libc: [glibc] '@unrs/resolver-binding-linux-x64-gnu@1.11.1': resolution: {integrity: sha512-C3ZAHugKgovV5YvAMsxhq0gtXuwESUKc5MhEtjBpLoHPLYM+iuwSj3lflFwK3DPm68660rZ7G8BMcwSro7hD5w==} cpu: [x64] os: [linux] + libc: [glibc] '@unrs/resolver-binding-linux-x64-musl@1.11.1': resolution: {integrity: sha512-rV0YSoyhK2nZ4vEswT/QwqzqQXw5I6CjoaYMOX0TqBlWhojUf8P94mvI7nuJTeaCkkds3QE4+zS8Ko+GdXuZtA==} cpu: [x64] os: [linux] + libc: [musl] '@unrs/resolver-binding-wasm32-wasi@1.11.1': resolution: {integrity: sha512-5u4RkfxJm+Ng7IWgkzi3qrFOvLvQYnPBmjmZQ8+szTK/b31fQCnleNl1GgEt7nIsZRIf5PLhPwT0WM+q45x/UQ==} @@ -2219,6 +2259,10 @@ packages: '@vue/compiler-ssr@3.5.21': resolution: {integrity: sha512-vKQ5olH5edFZdf5ZrlEgSO1j1DMA4u23TVK5XR1uMhvwnYvVdDF0nHXJUblL/GvzlShQbjhZZ2uvYmDlAbgo9w==} + '@vue/consolidate@1.0.0': + resolution: {integrity: sha512-oTyUE+QHIzLw2PpV14GD/c7EohDyP64xCniWTcqcEmTd699eFqTIwOmtDYjcO1j3QgdXoJEoWv1/cCdLrRoOfg==} + engines: {node: '>= 0.12.0'} + '@vue/devtools-api@6.6.4': resolution: {integrity: sha512-sGhTPMuXqZ1rVOk32RylztWkfXTRhuS7vgAKv0zjqk8gbsHkJ7xfFf+jbySxt7tWObEJwyKaHMikV/WGDiQm8g==} @@ -2784,9 +2828,27 @@ packages: peerDependencies: postcss: ^8.0.9 + css-font-size-keywords@1.0.0: + resolution: {integrity: sha512-Q+svMDbMlelgCfH/RVDKtTDaf5021O486ZThQPIpahnIjUkMUslC+WuOQSWTgGSrNCH08Y7tYNEmmy0hkfMI8Q==} + + css-font-stretch-keywords@1.0.1: + resolution: {integrity: sha512-KmugPO2BNqoyp9zmBIUGwt58UQSfyk1X5DbOlkb2pckDXFSAfjsD5wenb88fNrD6fvS+vu90a/tsPpb9vb0SLg==} + + css-font-style-keywords@1.0.1: + resolution: {integrity: sha512-0Fn0aTpcDktnR1RzaBYorIxQily85M2KXRpzmxQPgh8pxUN9Fcn00I8u9I3grNr1QXVgCl9T5Imx0ZwKU973Vg==} + + css-font-weight-keywords@1.0.0: + resolution: {integrity: sha512-5So8/NH+oDD+EzsnF4iaG4ZFHQ3vaViePkL1ZbZ5iC/KrsCY+WHq/lvOgrtmuOQ9pBBZ1ADGpaf+A4lj1Z9eYA==} + + css-list-helpers@2.0.0: + resolution: {integrity: sha512-9Bj8tZ0jWbAM3u/U6m/boAzAwLPwtjzFvwivr2piSvyVa3K3rChJzQy4RIHkNkKiZCHrEMWDJWtTR8UyVhdDnQ==} + css-select@5.2.2: resolution: {integrity: sha512-TizTzUddG/xYLA3NXodFM0fSbNizXjOKhqiQQwvhlspadZokn1KDy0NZFS0wuEubIYAV5/c1/lAr0TaaFXEXzw==} + css-system-font-keywords@1.0.0: + resolution: {integrity: sha512-1umTtVd/fXS25ftfjB71eASCrYhilmEsvDEI6wG/QplnmlfmVM5HkZ/ZX46DT5K3eblFPgLUHt5BRCb0YXkSFA==} + css-tree@2.2.1: resolution: {integrity: sha512-OA0mILzGc1kCOCSJerOeqDxDQ4HOh+G8NbOJFOTgOCzpw7fCBubk0fEyxp8AgOL/jvLgYA/uV0cMbe43ElF1JA==} engines: {node: ^10 || ^12.20.0 || ^14.13.0 || >=15.0.0, npm: '>=7.0.0'} @@ -4640,6 +4702,9 @@ packages: parse-bmfont-xml@1.1.6: resolution: {integrity: sha512-0cEliVMZEhrFDwMh4SxIyVJpqYoOWDJ9P895tFuS+XuNzI5UBmBk5U5O4KuJdTnZpSBI4LFA2+ZiJaiwfSwlMA==} + parse-css-font@4.0.0: + resolution: {integrity: sha512-lnY7dTUfjRXsSo5G5C639L8RaBBaVSgL+5hacIFKsNHzeCJQ5SFSZv1DZmc7+wZv/22PFGOq2YbaEHLdaCS/mQ==} + parse-gitignore@2.0.0: resolution: {integrity: sha512-RmVuCHWsfu0QPNW+mraxh/xjQVw/lhUCUru8Zni3Ctq3AoMhpDTq0OVdKS6iesd6Kqb7viCV3isAL43dciOSog==} engines: {node: '>=14'} @@ -5683,6 +5748,9 @@ packages: resolution: {integrity: sha512-4/u/j4FrCKdi17jaxuJA0jClGxB1AvU2hw/IuayPc4ay1XGaJs/rbb4v5WKwAjNifjmXK9PIFyuPiaK8azyR9w==} engines: {node: '>=14.0.0'} + unquote@1.1.1: + resolution: {integrity: sha512-vRCqFv6UhXpWxZPyGDh/F3ZpNv8/qo7w6iufLpQg9aKnQ71qM4B5KiI7Mia9COcjEhrO9LueHpMYjYzsWH3OIg==} + unrs-resolver@1.11.1: resolution: {integrity: sha512-bSjt9pjaEBnNiGgc9rUiHGKv5l4/TGzDmYw3RhnkJGtLhbnnA/5qJj7x3dNDCRx/PJxu774LlH8lCOlB4hEfKg==} @@ -5827,6 +5895,7 @@ packages: whatwg-encoding@1.0.5: resolution: {integrity: sha512-b5lim54JOPN9HtzvK9HFXvBma/rnfFeqsic0hSpjtDbVxR3dJKLc+KB4V6GgiGOvl7CY/KNh8rxSo9DKQrnUEw==} + deprecated: Use @exodus/bytes instead for a more spec-conformant and faster implementation whatwg-mimetype@2.3.0: resolution: {integrity: sha512-M4yMwr6mAnQz76TbJm914+gPpB/nCwvZbJU28cUD6dR004SAxDLOOSUaB1JDRqLtaOV/vi0IC5lEAGFgrjGv/g==} @@ -6909,6 +6978,83 @@ snapshots: '@dcloudio/types@3.4.21': {} + '@dcloudio/uni-app-plus@3.0.0-4040520250104002(postcss@8.5.6)(rollup@4.52.0)(vite@5.2.1(@types/node@24.7.0)(terser@5.44.0))(vue@3.5.21(typescript@5.9.2))': + dependencies: + '@dcloudio/uni-app-uts': 3.0.0-4040520250104002(postcss@8.5.6)(rollup@4.52.0)(vue@3.5.21(typescript@5.9.2)) + '@dcloudio/uni-app-vite': 3.0.0-4040520250104002(postcss@8.5.6)(rollup@4.52.0)(vite@5.2.1(@types/node@24.7.0)(terser@5.44.0))(vue@3.5.21(typescript@5.9.2)) + '@dcloudio/uni-app-vue': 3.0.0-4040520250104002 + debug: 4.4.3 + fs-extra: 10.1.0 + licia: 1.48.0 + postcss-selector-parser: 6.1.2 + transitivePeerDependencies: + - '@nuxt/kit' + - '@vueuse/core' + - postcss + - rollup + - supports-color + - ts-node + - vite + - vue + + '@dcloudio/uni-app-uts@3.0.0-4040520250104002(postcss@8.5.6)(rollup@4.52.0)(vue@3.5.21(typescript@5.9.2))': + dependencies: + '@babel/parser': 7.28.4 + '@babel/types': 7.28.4 + '@dcloudio/uni-cli-shared': 3.0.0-4040520250104002(postcss@8.5.6)(rollup@4.52.0)(vue@3.5.21(typescript@5.9.2)) + '@dcloudio/uni-i18n': 3.0.0-4040520250104002 + '@dcloudio/uni-nvue-styler': 3.0.0-4040520250104002 + '@dcloudio/uni-shared': 3.0.0-4040520250104002 + '@jridgewell/gen-mapping': 0.3.13 + '@jridgewell/trace-mapping': 0.3.31 + '@rollup/pluginutils': 5.3.0(rollup@4.52.0) + '@vue/compiler-core': 3.4.21 + '@vue/compiler-dom': 3.4.21 + '@vue/compiler-sfc': 3.4.21 + '@vue/consolidate': 1.0.0 + '@vue/shared': 3.4.21 + debug: 4.4.3 + es-module-lexer: 1.7.0 + estree-walker: 2.0.2 + fs-extra: 10.1.0 + magic-string: 0.30.19 + picocolors: 1.1.1 + source-map-js: 1.2.1 + unimport: 3.14.6(rollup@4.52.0) + transitivePeerDependencies: + - '@nuxt/kit' + - '@vueuse/core' + - postcss + - rollup + - supports-color + - ts-node + - vue + + '@dcloudio/uni-app-vite@3.0.0-4040520250104002(postcss@8.5.6)(rollup@4.52.0)(vite@5.2.1(@types/node@24.7.0)(terser@5.44.0))(vue@3.5.21(typescript@5.9.2))': + dependencies: + '@dcloudio/uni-cli-shared': 3.0.0-4040520250104002(postcss@8.5.6)(rollup@4.52.0)(vue@3.5.21(typescript@5.9.2)) + '@dcloudio/uni-i18n': 3.0.0-4040520250104002 + '@dcloudio/uni-nvue-styler': 3.0.0-4040520250104002 + '@dcloudio/uni-shared': 3.0.0-4040520250104002 + '@rollup/pluginutils': 5.3.0(rollup@4.52.0) + '@vitejs/plugin-vue': 5.1.0(vite@5.2.1(@types/node@24.7.0)(terser@5.44.0))(vue@3.5.21(typescript@5.9.2)) + '@vue/compiler-dom': 3.4.21 + '@vue/compiler-sfc': 3.4.21 + debug: 4.4.3 + fs-extra: 10.1.0 + picocolors: 1.1.1 + transitivePeerDependencies: + - '@nuxt/kit' + - '@vueuse/core' + - postcss + - rollup + - supports-color + - ts-node + - vite + - vue + + '@dcloudio/uni-app-vue@3.0.0-4040520250104002': {} + '@dcloudio/uni-app@3.0.0-4040520250104002(@dcloudio/types@3.4.21)(postcss@8.5.6)(rollup@4.52.0)(vue@3.5.21(typescript@5.9.2))': dependencies: '@dcloudio/types': 3.4.21 @@ -7179,6 +7325,11 @@ snapshots: - utf-8-validate - vue + '@dcloudio/uni-nvue-styler@3.0.0-4040520250104002': + dependencies: + parse-css-font: 4.0.0 + postcss: 8.5.6 + '@dcloudio/uni-push@3.0.0-4040520250104002(postcss@8.5.6)(rollup@4.52.0)(vue@3.5.21(typescript@5.9.2))': dependencies: '@dcloudio/uni-cli-shared': 3.0.0-4040520250104002(postcss@8.5.6)(rollup@4.52.0)(vue@3.5.21(typescript@5.9.2)) @@ -8638,6 +8789,8 @@ snapshots: '@vue/compiler-dom': 3.5.21 '@vue/shared': 3.5.21 + '@vue/consolidate@1.0.0': {} + '@vue/devtools-api@6.6.4': {} '@vue/reactivity@3.5.21': @@ -9242,6 +9395,16 @@ snapshots: dependencies: postcss: 8.5.6 + css-font-size-keywords@1.0.0: {} + + css-font-stretch-keywords@1.0.1: {} + + css-font-style-keywords@1.0.1: {} + + css-font-weight-keywords@1.0.0: {} + + css-list-helpers@2.0.0: {} + css-select@5.2.2: dependencies: boolbase: 1.0.0 @@ -9250,6 +9413,8 @@ snapshots: domutils: 3.2.2 nth-check: 2.1.1 + css-system-font-keywords@1.0.0: {} + css-tree@2.2.1: dependencies: mdn-data: 2.0.28 @@ -11647,6 +11812,16 @@ snapshots: xml-parse-from-string: 1.0.1 xml2js: 0.5.0 + parse-css-font@4.0.0: + dependencies: + css-font-size-keywords: 1.0.0 + css-font-stretch-keywords: 1.0.1 + css-font-style-keywords: 1.0.1 + css-font-weight-keywords: 1.0.0 + css-list-helpers: 2.0.0 + css-system-font-keywords: 1.0.0 + unquote: 1.1.1 + parse-gitignore@2.0.0: {} parse-headers@2.0.6: {} @@ -12665,6 +12840,8 @@ snapshots: acorn: 8.15.0 webpack-virtual-modules: 0.6.2 + unquote@1.1.1: {} + unrs-resolver@1.11.1: dependencies: napi-postinstall: 0.3.3 From ba667fb6cc9927af8800db1437d87a8fdea70fd5 Mon Sep 17 00:00:00 2001 From: Skiyee <319619193@qq.com> Date: Sat, 25 Jul 2026 15:42:50 +0800 Subject: [PATCH 5/7] =?UTF-8?q?feat(examples):=20=E5=A2=9E=E5=8A=A0=20nvue?= =?UTF-8?q?=20=E6=BC=94=E7=A4=BA=E9=A1=B5=E9=9D=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 新增 setup 与 Options API 两套 nvue 示例页,并在首页加入跳转入口。 --- examples/src/pages.json | 12 +++++++++ examples/src/pages/index.vue | 14 ++++++++++ examples/src/pages/nvue-demo.nvue | 39 ++++++++++++++++++++++++++++ examples/src/pages/nvue-options.nvue | 35 +++++++++++++++++++++++++ 4 files changed, 100 insertions(+) create mode 100644 examples/src/pages/nvue-demo.nvue create mode 100644 examples/src/pages/nvue-options.nvue diff --git a/examples/src/pages.json b/examples/src/pages.json index a7e4e19..f35755b 100644 --- a/examples/src/pages.json +++ b/examples/src/pages.json @@ -23,6 +23,18 @@ "style": { "navigationBarTitleText": "uni-app" } + }, + { + "path": "pages/nvue-demo", + "style": { + "navigationBarTitleText": "nvue demo" + } + }, + { + "path": "pages/nvue-options", + "style": { + "navigationBarTitleText": "nvue options" + } } ], "globalStyle": { diff --git a/examples/src/pages/index.vue b/examples/src/pages/index.vue index b55fa8a..6c7753d 100644 --- a/examples/src/pages/index.vue +++ b/examples/src/pages/index.vue @@ -9,6 +9,14 @@ function handleClick() { return globalToastState.value ? hideToast() : showToast() } +function goNvueDemo() { + uni.navigateTo({ url: '/pages/nvue-demo' }) +} + +function goNvueOptions() { + uni.navigateTo({ url: '/pages/nvue-options' }) +} + const uniKuRoot = ref() onMounted(() => { @@ -32,6 +40,12 @@ const color = 'red' + + diff --git a/examples/src/pages/nvue-demo.nvue b/examples/src/pages/nvue-demo.nvue new file mode 100644 index 0000000..38baa40 --- /dev/null +++ b/examples/src/pages/nvue-demo.nvue @@ -0,0 +1,39 @@ + + + + + diff --git a/examples/src/pages/nvue-options.nvue b/examples/src/pages/nvue-options.nvue new file mode 100644 index 0000000..d95dac9 --- /dev/null +++ b/examples/src/pages/nvue-options.nvue @@ -0,0 +1,35 @@ + + + + + From e6a509c6dc1d154ab684bf937d8b2b3154cc98b1 Mon Sep 17 00:00:00 2001 From: Skiyee <319619193@qq.com> Date: Sat, 25 Jul 2026 15:43:08 +0800 Subject: [PATCH 6/7] =?UTF-8?q?test:=20=E5=A2=9E=E5=8A=A0=20examples=20nvu?= =?UTF-8?q?e=20=E8=BD=AC=E6=8D=A2=E6=A0=A1=E9=AA=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 基于示例工程验证 nvue 页面路径解析与 GlobalKuRoot 本地注入,并补充 example:verify:nvue 脚本。 --- package.json | 1 + test/examples-nvue.test.ts | 49 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 50 insertions(+) create mode 100644 test/examples-nvue.test.ts diff --git a/package.json b/package.json index 438c39a..f86ac00 100644 --- a/package.json +++ b/package.json @@ -44,6 +44,7 @@ "example:build:h5": "pnpm build && pnpm -C examples run build", "example:dev:mp-weixin": "pnpm build && pnpm -C examples run dev:mp-weixin", "example:build:mp-weixin": "pnpm build && pnpm -C examples run build:mp-weixin", + "example:verify:nvue": "vitest run test/examples-nvue.test.ts", "prepare": "simple-git-hooks", "lint": "eslint . --fix", "prepublishOnly": "pnpm build" diff --git a/test/examples-nvue.test.ts b/test/examples-nvue.test.ts new file mode 100644 index 0000000..05aa5e8 --- /dev/null +++ b/test/examples-nvue.test.ts @@ -0,0 +1,49 @@ +import { readFileSync } from 'node:fs' +import { dirname, join, resolve } from 'node:path' +import { fileURLToPath } from 'node:url' + +import { normalizePath } from 'vite' +import { describe, expect, it } from 'vitest' + +import { transformNvuePage } from '../src/page' +import { getRelativePath, loadPagePaths } from '../src/utils' + +const testDirectory = dirname(fileURLToPath(import.meta.url)) +const examplesSourceRoot = resolve(testDirectory, '../examples/src') +const pagesJsonPath = join(examplesSourceRoot, 'pages.json') +const appKuPath = join(examplesSourceRoot, 'KuRoot.vue') + +describe('examples nvue pages', () => { + it('resolves nvue demo pages from examples pages.json', () => { + const pagePaths = loadPagePaths(pagesJsonPath, examplesSourceRoot) + + expect(pagePaths).toContain(normalizePath(join(examplesSourceRoot, 'pages/nvue-demo.nvue'))) + expect(pagePaths).toContain(normalizePath(join(examplesSourceRoot, 'pages/nvue-options.nvue'))) + }) + + it('wraps examples/src/pages/nvue-demo.nvue with a local GlobalKuRoot import', async () => { + const pagePath = join(examplesSourceRoot, 'pages/nvue-demo.nvue') + const sourceCode = readFileSync(pagePath, 'utf-8') + const rootImportPath = getRelativePath(pagePath, appKuPath) + + const result = (await transformNvuePage(sourceCode, rootImportPath)).toString() + + expect(rootImportPath).toBe('../KuRoot.vue') + expect(result).toContain(`import GlobalKuRoot from '${rootImportPath}'`) + expect(result).toContain('') + expect(result).toContain('') + }) + + it('registers GlobalKuRoot for examples/src/pages/nvue-options.nvue', async () => { + const pagePath = join(examplesSourceRoot, 'pages/nvue-options.nvue') + const sourceCode = readFileSync(pagePath, 'utf-8') + const rootImportPath = getRelativePath(pagePath, appKuPath) + + const result = (await transformNvuePage(sourceCode, rootImportPath)).toString() + + expect(result).toContain(`import GlobalKuRoot from '${rootImportPath}'`) + expect(result).toContain('components: { GlobalKuRoot },') + expect(result).toContain('') + expect(result).toContain('') + }) +}) From 9916d41244518d31b39112d8012a17ef709606ef Mon Sep 17 00:00:00 2001 From: Skiyee <319619193@qq.com> Date: Sat, 25 Jul 2026 15:43:34 +0800 Subject: [PATCH 7/7] =?UTF-8?q?docs:=20=E7=B2=BE=E7=AE=80=20README=20?= =?UTF-8?q?=E4=B8=AD=E7=9A=84=20nvue=20=E8=AF=B4=E6=98=8E?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 去掉与功能清单重复的 nvue 展开段落,并清理过时 roadmap 项。 --- README.md | 22 ---------------------- 1 file changed, 22 deletions(-) diff --git a/README.md b/README.md index cc48ff2..d5ce82e 100644 --- a/README.md +++ b/README.md @@ -288,27 +288,6 @@ export default defineConfig({ -
- - - (点击展开) 功能四:支持 nvue 页面 - -
- -Root 会识别 `pages.json` 中对应的 `.nvue` 页面,并在页面内本地引入虚拟根组件进行包裹。由于 nvue 页面不依赖 Vue 全局组件注册,插件会自动把 `App.ku.vue` 作为局部组件注入到 nvue 页面中。 - -支持特性: - -- `pages.json` 中未写扩展名时,会同时匹配已存在的 `.vue` 与 `.nvue` 页面文件 -- nvue 页面会被 `` 包裹,效果与普通页面的 `` 对齐 -- `PageMeta` / `page-meta` 会继续自动提取到页面顶层 -- `template root="uniKuRoot"` 与 `enabledGlobalRef` 仍然生效 - -> [!Note] -> 如果 `App.ku.vue` 需要包裹 nvue 页面,请确保其中使用 nvue 支持的组件与样式。组件库主题、原生导航栏、TabBar 等平台特定逻辑建议放在业务或组件库自身运行时中处理。 - -
- ### ✨ 例子 > [!TIP] @@ -558,7 +537,6 @@ function showToast() { - [x] 支持VueSFC - [x] 支持小程序PageMeta - [x] 支持 nvue 页面 -- [ ] 支持 App.ku.vue 内直接编写控制逻辑 - [ ] 补全单元测试 ### 🤔 与uni-helper-layouts的区别