使用@uni-helper/vite-plugin-uni-pages生成小程序主包使用不了分包组件问题
uniapp项目(vue3+ts+vite+unocss)前提: 开发新项目为了主包不超包;我把一个分包(shared)easycom了,
然后主包我想用都写在这个分包中组件,其他分包共用的组件也写在这个分包中相当'共享分包'.
根据已有知识配置当前页面,
definePage({
style: {
navigationBarTitleText: '',
},
// ✅ 在这里配置微信小程序特有的选项
componentPlaceholder: {
'shared-my-info': 'view',
'shared-my-details': 'view',
'shared-my-calendar': 'view',
'shared-my-activities': 'view',
},
})微信小程序生成了page.json我的引用的页面componentPlaceholder,到这没问题;
问题: 对应页面的json文件没有生成对应代码
componentPlaceholder: {
'shared-my-info': 'view',
'shared-my-details': 'view',
'shared-my-calendar': 'view',
'shared-my-activities': 'view',
},期望生成但是没有,最后我写了vite方法处理生成的json文件如果有引用shared的组件让其生成上面的配置.
vite文件如下,如果需要的话可以拿走,注意我自动生成的pages.json.js如果你是pages.json记得改
import type { Plugin } from 'vite'
import path from 'node:path'
import process from 'node:process'
import fs from 'fs-extra'
/**
* 页面配置同步插件配置接口
*
* 用于解决微信小程序 componentPlaceholder 需要同时配置在 pages.json 和页面 .json 文件的问题
* 自动将 pages.json 中的 componentPlaceholder 配置同步到对应的页面 .json 文件中
*/
export interface SyncPageConfigOptions {
/** 是否启用插件 */
enable?: boolean
/**
* 是否显示详细日志,便于调试和监控同步过程
*/
verbose?: boolean
/** 自定义日志前缀,用于区分不同插件的日志输出 */
logPrefix?: string
/**
* 目标平台,默认为 'mp-weixin'
* 可以扩展到其他小程序平台如 'mp-alipay', 'mp-baidu' 等
*/
targetPlatform?: string
}
/**
* 默认配置
*/
const DEFAULT_OPTIONS: Required<SyncPageConfigOptions> = {
enable: true,
verbose: true,
logPrefix: '',
targetPlatform: 'mp-weixin',
}
/**
* 页面配置同步插件
*
* 功能说明:
* 1. 解决微信小程序 componentPlaceholder 配置问题
* 2. 自动将 pages.json 中的 componentPlaceholder 配置同步到对应页面的 .json 文件
* 3. 支持主包页面和分包页面
* 4. 智能合并现有配置,避免覆盖其他配置
*
* 使用场景:
* - 使用 @uni-helper/vite-plugin-uni-pages 插件
* - 在页面中使用 definePage({ componentPlaceholder: {...} })
* - 需要微信小程序平台支持自定义组件的占位配置
*
* 工作原理:
* 1. 在构建完成后读取 dist///pages.json
* 2. 遍历所有页面配置,查找包含 componentPlaceholder 的页面
* 3. 将 componentPlaceholder 配置同步到对应的页面 .json 文件
* 4. 如果页面 .json 文件不存在,则创建新文件
* 5. 如果已存在,则智能合并配置
*/
export function syncPageConfig(options: SyncPageConfigOptions = {}): Plugin {
const config = { ...DEFAULT_OPTIONS, ...options }
// 如果插件被禁用,返回一个空插件
if (!config.enable) {
return {
name: 'sync-page-config-disabled',
apply: 'build',
writeBundle() {
// 插件已禁用,不执行任何操作
},
}
}
return {
name: 'sync-page-config',
apply: 'build', // 只在构建时应用
enforce: 'post', // 在其他插件执行完毕后执行
async writeBundle() {
const { verbose, logPrefix, targetPlatform } = config
try {
// 获取项目根目录路径
const projectRoot = process.cwd()
// 构建模式:'build' (生产环境) 或 'dev' (开发环境)
const buildMode = process.env.NODE_ENV === 'production' ? 'build' : 'dev'
const platform = process.env.UNI_PLATFORM || targetPlatform
// 只处理目标平台(默认微信小程序)
if (platform !== targetPlatform) {
// if (verbose) {
// console.log(`${logPrefix} 当前平台 ${platform} 不是目标平台 ${targetPlatform},跳过同步`)
// }
return
}
// 构建 pages.json.js 文件路径 - 微信小程序生成的是 pages.json.js
const pagesJsonPath = path.resolve(
projectRoot,
'dist',
buildMode,
platform,
'pages.json.js',
)
// 检查 pages.json.js 是否存在
const pagesJsonExists = await fs.pathExists(pagesJsonPath)
if (!pagesJsonExists) {
if (verbose) {
console.warn(`${logPrefix} pages.json.js 不存在,跳过同步操作`)
console.warn(`${logPrefix} 文件路径: ${pagesJsonPath}`)
}
return
}
// if (verbose) {
// console.log(`${logPrefix} 开始同步页面配置...`)
// console.log(`${logPrefix} 构建模式: ${buildMode}`)
// console.log(`${logPrefix} 目标平台: ${platform}`)
// console.log(`${logPrefix} pages.json.js 路径: ${pagesJsonPath}`)
// }
// 直接读取并解析 JavaScript 文件
const fileContent = await fs.readFile(pagesJsonPath, 'utf8')
const pagesJson = parseJavaScriptConfigFile(fileContent, verbose, logPrefix)
if (!pagesJson) {
console.error(`${logPrefix} ❌ 解析配置文件失败,无法继续`)
return
}
// 调试:显示解析结果
if (verbose) {
// console.log(`${logPrefix} 解析成功,找到配置:`)
// console.log(`${logPrefix} - 主包页面数: ${pagesJson.pages?.length || 0}`)
// console.log(`${logPrefix} - 分包数量: ${pagesJson.subPackages?.length || 0}`)
// 查找 me 页面
const mePage = pagesJson.pages?.find((p: any) => p.path === 'pages/me/me')
// if (mePage) {
// console.log(`${logPrefix} - 找到 me 页面: ${mePage.path}`)
// if (mePage.componentPlaceholder) {
// console.log(`${logPrefix} - me 页面有 componentPlaceholder:`, Object.keys(mePage.componentPlaceholder))
// }
// }
}
let totalProcessed = 0
// 处理主包页面
if (pagesJson.pages && Array.isArray(pagesJson.pages)) {
totalProcessed += await processPages(
pagesJson.pages,
path.resolve(projectRoot, 'dist', buildMode, platform),
'',
verbose,
logPrefix,
)
}
// 处理分包页面
if (pagesJson.subPackages && Array.isArray(pagesJson.subPackages)) {
for (const subPackage of pagesJson.subPackages) {
if (subPackage.pages && subPackage.root) {
const subPackageRoot = path.resolve(
projectRoot,
'dist',
buildMode,
platform,
subPackage.root,
)
totalProcessed += await processPages(
subPackage.pages,
subPackageRoot,
subPackage.root,
verbose,
logPrefix,
)
}
}
}
// if (verbose) {
// console.log(`${logPrefix} ✅ 页面配置同步完成`)
// console.log(`${logPrefix} 共处理 ${totalProcessed} 个页面的 componentPlaceholder 配置`)
// }
}
catch (error) {
console.error(`${logPrefix} ❌ 同步页面配置失败:`, error)
console.error(`${logPrefix} 错误详情:`, error instanceof Error ? error.message : String(error))
console.error(`${logPrefix} 堆栈:`, error instanceof Error ? error.stack : '无堆栈信息')
// 不抛出错误,避免影响整个构建过程
}
},
}
}
/**
* 解析 JavaScript 配置文件
* 专门处理微信小程序的 pages.json.js 格式
*/
function parseJavaScriptConfigFile(
jsContent: string,
verbose: boolean,
logPrefix: string,
): any {
try {
// 根据你提供的文件格式,这是一个 CommonJS 模块
// 我们需要执行这个 JavaScript 代码来获取 exports
// 方法1:使用 Function 构造函数创建一个安全的执行环境
const moduleCode = `
const exports = {};
const module = { exports };
${jsContent};
return module.exports;
`
const getModuleExports = new Function(moduleCode)
const result = getModuleExports()
// if (verbose) {
// console.log(`${logPrefix} 成功解析 JavaScript 配置文件`)
// }
return result
}
catch (error) {
console.error(`${logPrefix} ❌ 解析 JavaScript 配置文件失败:`, error)
// 方法2:尝试简单的正则解析(备选方案)
try {
const result: any = {}
// 提取 pages 数组
const pagesMatch = jsContent.match(/const pages = (\[[\s\S]*?\]);/)
if (pagesMatch) {
try {
const pagesCode = pagesMatch
// 将 JavaScript 数组转换为 JSON
const jsonStr = pagesCode
.replace(/(['"])?(\w+)(['"])?\s*:/g, '"$2":')
.replace(/'/g, '"')
.replace(/,\s*\]/g, ']') // 移除尾随逗号
result.pages = JSON.parse(jsonStr)
// if (verbose) {
// console.log(`${logPrefix} 使用正则成功解析 pages`)
// }
}
catch (e) {
console.warn(`${logPrefix} ⚠️ 正则解析 pages 失败:`, e)
}
}
// 提取 subPackages 数组
const subPackagesMatch = jsContent.match(/const subPackages = (\[[\s\S]*?\]);/)
if (subPackagesMatch) {
try {
const subPackagesCode = subPackagesMatch
const jsonStr = subPackagesCode
.replace(/(['"])?(\w+)(['"])?\s*:/g, '"$2":')
.replace(/'/g, '"')
.replace(/,\s*\]/g, ']')
result.subPackages = JSON.parse(jsonStr)
// if (verbose) {
// console.log(`${logPrefix} 使用正则成功解析 subPackages`)
// }
}
catch (e) {
console.warn(`${logPrefix} ⚠️ 正则解析 subPackages 失败:`, e)
}
}
if (result.pages || result.subPackages) {
return result
}
throw new Error('两种解析方法都失败了')
}
catch (fallbackError) {
console.error(`${logPrefix} ❌ 备用解析方案也失败:`, fallbackError)
return null
}
}
}
/**
* 处理页面数组
*/
async function processPages(
pages: any[],
baseDir: string,
rootPath: string,
verbose: boolean,
logPrefix: string,
): Promise<number> {
let processedCount = 0
// 安全检查:确保 pages 是数组
if (!Array.isArray(pages)) {
console.warn(`${logPrefix} ⚠️ pages 不是数组,跳过处理`)
return 0
}
for (const page of pages) {
// 安全检查:确保 page 是对象
if (!page || typeof page !== 'object') {
console.warn(`${logPrefix} ⚠️ 页面配置不是对象,跳过`)
continue
}
// 检查是否有 componentPlaceholder 配置
const hasPlaceholder = page.componentPlaceholder
&& typeof page.componentPlaceholder === 'object'
&& Object.keys(page.componentPlaceholder).length > 0
if (hasPlaceholder) {
try {
// 获取页面路径,确保移除 .vue 扩展名
let pagePath = page.path
if (pagePath && typeof pagePath === 'string' && pagePath.endsWith('.vue')) {
pagePath = pagePath.slice(0, -4) // 移除 .vue
}
if (!pagePath || typeof pagePath !== 'string') {
console.warn(`${logPrefix} ⚠️ 页面路径无效: ${pagePath}`)
continue
}
// 构建目标 .json 文件路径
const jsonFilePath = path.join(baseDir, `${pagePath}.json`)
// 确保目录存在
await fs.ensureDir(path.dirname(jsonFilePath))
// 读取或创建页面配置
let pageConfig: any = {}
if (await fs.pathExists(jsonFilePath)) {
try {
const existingContent = await fs.readFile(jsonFilePath, 'utf8')
pageConfig = JSON.parse(existingContent)
}
catch (readError) {
if (verbose) {
console.warn(`${logPrefix} ⚠️ 无法读取现有配置文件 ${jsonFilePath},将创建新配置`)
}
}
}
// 确保必要的字段存在
if (!pageConfig.navigationBarTitleText && page.style?.navigationBarTitleText) {
pageConfig.navigationBarTitleText = page.style.navigationBarTitleText
}
// 确保 usingComponents 存在
if (!pageConfig.usingComponents) {
pageConfig.usingComponents = {}
}
// 合并 componentPlaceholder
const placeholder = page.componentPlaceholder || {}
pageConfig.componentPlaceholder = {
...(pageConfig.componentPlaceholder || {}),
...placeholder,
}
// 写入文件
await fs.writeFile(jsonFilePath, JSON.stringify(pageConfig, null, 2))
processedCount++
if (verbose) {
const logRootPrefix = rootPath ? `[${rootPath}] ` : ''
const placeholderCount = Object.keys(placeholder).length
// console.log(`${logPrefix} ${logRootPrefix}✅ 已同步: ${pagePath} → ${placeholderCount} 个占位组件`)
}
}
catch (pageError) {
console.error(`${logPrefix} ❌ 处理页面 ${page?.path || '未知页面'} 失败:`, pageError)
}
}
else if (verbose) {
// 调试信息:显示没有 componentPlaceholder 的页面
const logRootPrefix = rootPath ? `[${rootPath}] ` : ''
// console.log(`${logPrefix} ${logRootPrefix}跳过: ${page.path || '未知页面'} (无 componentPlaceholder)`)
}
}
return processedCount
}
/**
* 创建页面配置同步插件的便捷函数
*
* 这是一个便捷的工厂函数,用于快速创建插件实例
* 特别适用于在 vite.config.ts 中进行条件性插件配置
*
* 使用示例:
* ```typescript
* // 在 vite.config.ts 中
* plugins: [
* // 仅在微信小程序平台启用
* createSyncPageConfigPlugin(
* UNI_PLATFORM === 'mp-weixin',
* { verbose: mode === 'development' }
* ),
* ]
* ```
*/
export function createSyncPageConfigPlugin(
enable: boolean = true,
options: Omit<SyncPageConfigOptions, 'enable'> = {},
): Plugin {
return syncPageConfig({ enable, ...options })
}
来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作! 谢谢分享,试用一下 这个好,看起来很实用 谢谢楼主提供! yyds。多谢分享 不错,里面软件多更新就更好了 过来提前占个楼 新版吗?好像是停更了吧。 过来提前占个楼 不错,里面软件多更新就更好了 谢谢分享,试用一下 新版吗?好像是停更了吧。 这个有用。 感谢分享,学习下。 谢谢楼主提供! 新版吗?好像是停更了吧。 谢谢分享,辛苦了 前排留名,哈哈哈 不错,里面软件多更新就更好了 热心回复!
页:
[1]
2