找回密码
 立即注册
首页 业界区 安全 使用@uni-helper/vite-plugin-uni-pages生成小程序主包 ...

使用@uni-helper/vite-plugin-uni-pages生成小程序主包使用不了分包组件问题

锷稠 1 小时前
uniapp项目(vue3+ts+vite+unocss)
前提: 开发新项目为了主包不超包;我把一个分包(shared)easycom了,
1.png

然后主包我想用都写在这个分包中组件,其他分包共用的组件也写在这个分包中相当'共享分包'.
根据已有知识配置当前页面,
  1. definePage({
  2.   style: {
  3.     navigationBarTitleText: '',
  4.   },
  5.   // ✅ 在这里配置微信小程序特有的选项
  6.   componentPlaceholder: {
  7.     'shared-my-info': 'view',
  8.     'shared-my-details': 'view',
  9.     'shared-my-calendar': 'view',
  10.     'shared-my-activities': 'view',
  11.   },
  12. })
复制代码
微信小程序生成了page.json我的引用的页面componentPlaceholder,到这没问题;
问题: 对应页面的json文件没有生成对应代码
  1. componentPlaceholder: {
  2.     'shared-my-info': 'view',
  3.     'shared-my-details': 'view',
  4.     'shared-my-calendar': 'view',
  5.     'shared-my-activities': 'view',
  6.   },
复制代码
期望生成但是没有,最后我写了vite方法处理生成的json文件如果有引用shared的组件让其生成上面的配置.
vite文件如下,如果需要的话可以拿走,注意我自动生成的pages.json.js如果你是pages.json记得改
  1. import type { Plugin } from 'vite'
  2. import path from 'node:path'
  3. import process from 'node:process'
  4. import fs from 'fs-extra'
  5. /**
  6. * 页面配置同步插件配置接口
  7. *
  8. * 用于解决微信小程序 componentPlaceholder 需要同时配置在 pages.json 和页面 .json 文件的问题
  9. * 自动将 pages.json 中的 componentPlaceholder 配置同步到对应的页面 .json 文件中
  10. */
  11. export interface SyncPageConfigOptions {
  12.   /** 是否启用插件 */
  13.   enable?: boolean
  14.   /**
  15.    * 是否显示详细日志,便于调试和监控同步过程
  16.    */
  17.   verbose?: boolean
  18.   /** 自定义日志前缀,用于区分不同插件的日志输出 */
  19.   logPrefix?: string
  20.   /**
  21.    * 目标平台,默认为 'mp-weixin'
  22.    * 可以扩展到其他小程序平台如 'mp-alipay', 'mp-baidu' 等
  23.    */
  24.   targetPlatform?: string
  25. }
  26. /**
  27. * 默认配置
  28. */
  29. const DEFAULT_OPTIONS: Required<SyncPageConfigOptions> = {
  30.   enable: true,
  31.   verbose: true,
  32.   logPrefix: '[sync-page-config]',
  33.   targetPlatform: 'mp-weixin',
  34. }
  35. /**
  36. * 页面配置同步插件
  37. *
  38. * 功能说明:
  39. * 1. 解决微信小程序 componentPlaceholder 配置问题
  40. * 2. 自动将 pages.json 中的 componentPlaceholder 配置同步到对应页面的 .json 文件
  41. * 3. 支持主包页面和分包页面
  42. * 4. 智能合并现有配置,避免覆盖其他配置
  43. *
  44. * 使用场景:
  45. * - 使用 @uni-helper/vite-plugin-uni-pages 插件
  46. * - 在页面中使用 definePage({ componentPlaceholder: {...} })
  47. * - 需要微信小程序平台支持自定义组件的占位配置
  48. *
  49. * 工作原理:
  50. * 1. 在构建完成后读取 dist/[mode]/[platform]/pages.json
  51. * 2. 遍历所有页面配置,查找包含 componentPlaceholder 的页面
  52. * 3. 将 componentPlaceholder 配置同步到对应的页面 .json 文件
  53. * 4. 如果页面 .json 文件不存在,则创建新文件
  54. * 5. 如果已存在,则智能合并配置
  55. */
  56. export function syncPageConfig(options: SyncPageConfigOptions = {}): Plugin {
  57.   const config = { ...DEFAULT_OPTIONS, ...options }
  58.   // 如果插件被禁用,返回一个空插件
  59.   if (!config.enable) {
  60.     return {
  61.       name: 'sync-page-config-disabled',
  62.       apply: 'build',
  63.       writeBundle() {
  64.         // 插件已禁用,不执行任何操作
  65.       },
  66.     }
  67.   }
  68.   return {
  69.     name: 'sync-page-config',
  70.     apply: 'build', // 只在构建时应用
  71.     enforce: 'post', // 在其他插件执行完毕后执行
  72.     async writeBundle() {
  73.       const { verbose, logPrefix, targetPlatform } = config
  74.       try {
  75.         // 获取项目根目录路径
  76.         const projectRoot = process.cwd()
  77.         // 构建模式:'build' (生产环境) 或 'dev' (开发环境)
  78.         const buildMode = process.env.NODE_ENV === 'production' ? 'build' : 'dev'
  79.         const platform = process.env.UNI_PLATFORM || targetPlatform
  80.         // 只处理目标平台(默认微信小程序)
  81.         if (platform !== targetPlatform) {
  82.           // if (verbose) {
  83.           //   console.log(`${logPrefix} 当前平台 ${platform} 不是目标平台 ${targetPlatform},跳过同步`)
  84.           // }
  85.           return
  86.         }
  87.         // 构建 pages.json.js 文件路径 - 微信小程序生成的是 pages.json.js
  88.         const pagesJsonPath = path.resolve(
  89.           projectRoot,
  90.           'dist',
  91.           buildMode,
  92.           platform,
  93.           'pages.json.js',
  94.         )
  95.         // 检查 pages.json.js 是否存在
  96.         const pagesJsonExists = await fs.pathExists(pagesJsonPath)
  97.         if (!pagesJsonExists) {
  98.           if (verbose) {
  99.             console.warn(`${logPrefix} pages.json.js 不存在,跳过同步操作`)
  100.             console.warn(`${logPrefix} 文件路径: ${pagesJsonPath}`)
  101.           }
  102.           return
  103.         }
  104.         // if (verbose) {
  105.         //   console.log(`${logPrefix} 开始同步页面配置...`)
  106.         //   console.log(`${logPrefix} 构建模式: ${buildMode}`)
  107.         //   console.log(`${logPrefix} 目标平台: ${platform}`)
  108.         //   console.log(`${logPrefix} pages.json.js 路径: ${pagesJsonPath}`)
  109.         // }
  110.         // 直接读取并解析 JavaScript 文件
  111.         const fileContent = await fs.readFile(pagesJsonPath, 'utf8')
  112.         const pagesJson = parseJavaScriptConfigFile(fileContent, verbose, logPrefix)
  113.         if (!pagesJson) {
  114.           console.error(`${logPrefix} ❌ 解析配置文件失败,无法继续`)
  115.           return
  116.         }
  117.         // 调试:显示解析结果
  118.         if (verbose) {
  119.           // console.log(`${logPrefix} 解析成功,找到配置:`)
  120.           // console.log(`${logPrefix} - 主包页面数: ${pagesJson.pages?.length || 0}`)
  121.           // console.log(`${logPrefix} - 分包数量: ${pagesJson.subPackages?.length || 0}`)
  122.           // 查找 me 页面
  123.           const mePage = pagesJson.pages?.find((p: any) => p.path === 'pages/me/me')
  124.           // if (mePage) {
  125.           //   console.log(`${logPrefix} - 找到 me 页面: ${mePage.path}`)
  126.           //   if (mePage.componentPlaceholder) {
  127.           //     console.log(`${logPrefix} - me 页面有 componentPlaceholder:`, Object.keys(mePage.componentPlaceholder))
  128.           //   }
  129.           // }
  130.         }
  131.         let totalProcessed = 0
  132.         // 处理主包页面
  133.         if (pagesJson.pages && Array.isArray(pagesJson.pages)) {
  134.           totalProcessed += await processPages(
  135.             pagesJson.pages,
  136.             path.resolve(projectRoot, 'dist', buildMode, platform),
  137.             '',
  138.             verbose,
  139.             logPrefix,
  140.           )
  141.         }
  142.         // 处理分包页面
  143.         if (pagesJson.subPackages && Array.isArray(pagesJson.subPackages)) {
  144.           for (const subPackage of pagesJson.subPackages) {
  145.             if (subPackage.pages && subPackage.root) {
  146.               const subPackageRoot = path.resolve(
  147.                 projectRoot,
  148.                 'dist',
  149.                 buildMode,
  150.                 platform,
  151.                 subPackage.root,
  152.               )
  153.               totalProcessed += await processPages(
  154.                 subPackage.pages,
  155.                 subPackageRoot,
  156.                 subPackage.root,
  157.                 verbose,
  158.                 logPrefix,
  159.               )
  160.             }
  161.           }
  162.         }
  163.         // if (verbose) {
  164.         //   console.log(`${logPrefix} ✅ 页面配置同步完成`)
  165.         //   console.log(`${logPrefix} 共处理 ${totalProcessed} 个页面的 componentPlaceholder 配置`)
  166.         // }
  167.       }
  168.       catch (error) {
  169.         console.error(`${logPrefix} ❌ 同步页面配置失败:`, error)
  170.         console.error(`${logPrefix} 错误详情:`, error instanceof Error ? error.message : String(error))
  171.         console.error(`${logPrefix} 堆栈:`, error instanceof Error ? error.stack : '无堆栈信息')
  172.         // 不抛出错误,避免影响整个构建过程
  173.       }
  174.     },
  175.   }
  176. }
  177. /**
  178. * 解析 JavaScript 配置文件
  179. * 专门处理微信小程序的 pages.json.js 格式
  180. */
  181. function parseJavaScriptConfigFile(
  182.   jsContent: string,
  183.   verbose: boolean,
  184.   logPrefix: string,
  185. ): any {
  186.   try {
  187.     // 根据你提供的文件格式,这是一个 CommonJS 模块
  188.     // 我们需要执行这个 JavaScript 代码来获取 exports
  189.     // 方法1:使用 Function 构造函数创建一个安全的执行环境
  190.     const moduleCode = `
  191.       const exports = {};
  192.       const module = { exports };
  193.       ${jsContent};
  194.       return module.exports;
  195.     `
  196.     const getModuleExports = new Function(moduleCode)
  197.     const result = getModuleExports()
  198.     // if (verbose) {
  199.     //   console.log(`${logPrefix} 成功解析 JavaScript 配置文件`)
  200.     // }
  201.     return result
  202.   }
  203.   catch (error) {
  204.     console.error(`${logPrefix} ❌ 解析 JavaScript 配置文件失败:`, error)
  205.     // 方法2:尝试简单的正则解析(备选方案)
  206.     try {
  207.       const result: any = {}
  208.       // 提取 pages 数组
  209.       const pagesMatch = jsContent.match(/const pages = (\[[\s\S]*?\]);/)
  210.       if (pagesMatch) {
  211.         try {
  212.           const pagesCode = pagesMatch[1]
  213.           // 将 JavaScript 数组转换为 JSON
  214.           const jsonStr = pagesCode
  215.             .replace(/(['"])?(\w+)(['"])?\s*:/g, '"$2":')
  216.             .replace(/'/g, '"')
  217.             .replace(/,\s*\]/g, ']') // 移除尾随逗号
  218.           result.pages = JSON.parse(jsonStr)
  219.           // if (verbose) {
  220.           //   console.log(`${logPrefix} 使用正则成功解析 pages`)
  221.           // }
  222.         }
  223.         catch (e) {
  224.           console.warn(`${logPrefix} ⚠️ 正则解析 pages 失败:`, e)
  225.         }
  226.       }
  227.       // 提取 subPackages 数组
  228.       const subPackagesMatch = jsContent.match(/const subPackages = (\[[\s\S]*?\]);/)
  229.       if (subPackagesMatch) {
  230.         try {
  231.           const subPackagesCode = subPackagesMatch[1]
  232.           const jsonStr = subPackagesCode
  233.             .replace(/(['"])?(\w+)(['"])?\s*:/g, '"$2":')
  234.             .replace(/'/g, '"')
  235.             .replace(/,\s*\]/g, ']')
  236.           result.subPackages = JSON.parse(jsonStr)
  237.           // if (verbose) {
  238.           //   console.log(`${logPrefix} 使用正则成功解析 subPackages`)
  239.           // }
  240.         }
  241.         catch (e) {
  242.           console.warn(`${logPrefix} ⚠️ 正则解析 subPackages 失败:`, e)
  243.         }
  244.       }
  245.       if (result.pages || result.subPackages) {
  246.         return result
  247.       }
  248.       throw new Error('两种解析方法都失败了')
  249.     }
  250.     catch (fallbackError) {
  251.       console.error(`${logPrefix} ❌ 备用解析方案也失败:`, fallbackError)
  252.       return null
  253.     }
  254.   }
  255. }
  256. /**
  257. * 处理页面数组
  258. */
  259. async function processPages(
  260.   pages: any[],
  261.   baseDir: string,
  262.   rootPath: string,
  263.   verbose: boolean,
  264.   logPrefix: string,
  265. ): Promise<number> {
  266.   let processedCount = 0
  267.   // 安全检查:确保 pages 是数组
  268.   if (!Array.isArray(pages)) {
  269.     console.warn(`${logPrefix} ⚠️ pages 不是数组,跳过处理`)
  270.     return 0
  271.   }
  272.   for (const page of pages) {
  273.     // 安全检查:确保 page 是对象
  274.     if (!page || typeof page !== 'object') {
  275.       console.warn(`${logPrefix} ⚠️ 页面配置不是对象,跳过`)
  276.       continue
  277.     }
  278.     // 检查是否有 componentPlaceholder 配置
  279.     const hasPlaceholder = page.componentPlaceholder
  280.       && typeof page.componentPlaceholder === 'object'
  281.       && Object.keys(page.componentPlaceholder).length > 0
  282.     if (hasPlaceholder) {
  283.       try {
  284.         // 获取页面路径,确保移除 .vue 扩展名
  285.         let pagePath = page.path
  286.         if (pagePath && typeof pagePath === 'string' && pagePath.endsWith('.vue')) {
  287.           pagePath = pagePath.slice(0, -4) // 移除 .vue
  288.         }
  289.         if (!pagePath || typeof pagePath !== 'string') {
  290.           console.warn(`${logPrefix} ⚠️ 页面路径无效: ${pagePath}`)
  291.           continue
  292.         }
  293.         // 构建目标 .json 文件路径
  294.         const jsonFilePath = path.join(baseDir, `${pagePath}.json`)
  295.         // 确保目录存在
  296.         await fs.ensureDir(path.dirname(jsonFilePath))
  297.         // 读取或创建页面配置
  298.         let pageConfig: any = {}
  299.         if (await fs.pathExists(jsonFilePath)) {
  300.           try {
  301.             const existingContent = await fs.readFile(jsonFilePath, 'utf8')
  302.             pageConfig = JSON.parse(existingContent)
  303.           }
  304.           catch (readError) {
  305.             if (verbose) {
  306.               console.warn(`${logPrefix} ⚠️ 无法读取现有配置文件 ${jsonFilePath},将创建新配置`)
  307.             }
  308.           }
  309.         }
  310.         // 确保必要的字段存在
  311.         if (!pageConfig.navigationBarTitleText && page.style?.navigationBarTitleText) {
  312.           pageConfig.navigationBarTitleText = page.style.navigationBarTitleText
  313.         }
  314.         // 确保 usingComponents 存在
  315.         if (!pageConfig.usingComponents) {
  316.           pageConfig.usingComponents = {}
  317.         }
  318.         // 合并 componentPlaceholder
  319.         const placeholder = page.componentPlaceholder || {}
  320.         pageConfig.componentPlaceholder = {
  321.           ...(pageConfig.componentPlaceholder || {}),
  322.           ...placeholder,
  323.         }
  324.         // 写入文件
  325.         await fs.writeFile(jsonFilePath, JSON.stringify(pageConfig, null, 2))
  326.         processedCount++
  327.         if (verbose) {
  328.           const logRootPrefix = rootPath ? `[${rootPath}] ` : ''
  329.           const placeholderCount = Object.keys(placeholder).length
  330.           // console.log(`${logPrefix} ${logRootPrefix}✅ 已同步: ${pagePath} → ${placeholderCount} 个占位组件`)
  331.         }
  332.       }
  333.       catch (pageError) {
  334.         console.error(`${logPrefix} ❌ 处理页面 ${page?.path || '未知页面'} 失败:`, pageError)
  335.       }
  336.     }
  337.     else if (verbose) {
  338.       // 调试信息:显示没有 componentPlaceholder 的页面
  339.       const logRootPrefix = rootPath ? `[${rootPath}] ` : ''
  340.       // console.log(`${logPrefix} ${logRootPrefix}跳过: ${page.path || '未知页面'} (无 componentPlaceholder)`)
  341.     }
  342.   }
  343.   return processedCount
  344. }
  345. /**
  346. * 创建页面配置同步插件的便捷函数
  347. *
  348. * 这是一个便捷的工厂函数,用于快速创建插件实例
  349. * 特别适用于在 vite.config.ts 中进行条件性插件配置
  350. *
  351. * 使用示例:
  352. * ```typescript
  353. * // 在 vite.config.ts 中
  354. * plugins: [
  355. *   // 仅在微信小程序平台启用
  356. *   createSyncPageConfigPlugin(
  357. *     UNI_PLATFORM === 'mp-weixin',
  358. *     { verbose: mode === 'development' }
  359. *   ),
  360. * ]
  361. * ```
  362. */
  363. export function createSyncPageConfigPlugin(
  364.   enable: boolean = true,
  365.   options: Omit<SyncPageConfigOptions, 'enable'> = {},
  366. ): Plugin {
  367.   return syncPageConfig({ enable, ...options })
  368. }
复制代码
 

来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!

相关推荐

您需要登录后才可以回帖 登录 | 立即注册