Deepseek工具:H5+Vue 项目转微信小程序报告生成工具

注意:AI结果,仅供参考

使用方法:

1. 环境要求

  • Node.js 16+

  • 项目目录中包含 package.json 和源代码

2. 安装依赖

bash

复制代码
npm install glob fs-extra @babel/parser @babel/traverse

3. 运行脚本

将脚本保存为 analyze-migration.js,然后在项目根目录执行:

bash

复制代码
node analyze-migration.js [项目路径]

如果不指定路径,默认为当前目录。

4. 输出结果

会在项目根目录生成 migration-report.md 报告文件。

三、核心代码

bash 复制代码
const fs = require('fs-extra');
const path = require('path');
const glob = require('glob');
const parser = require('@babel/parser');
const traverse = require('@babel/traverse').default;

// 配置
const config = {
  targetDir: process.argv[2] || '.',
  outputFile: path.join(process.argv[2] || '.', 'migration-report.md'),
};

// 浏览器 API 黑名单
const browserApis = [
  'window', 'document', 'localStorage', 'sessionStorage', 'location', 'history',
  'alert', 'confirm', 'prompt', 'fetch', 'XMLHttpRequest', 'WebSocket',
  'IntersectionObserver', 'ResizeObserver', 'Canvas', 'Image', 'FileReader'
];

// 第三方库兼容性映射
const libCompat = {
  // UI 库(不兼容)
  'element-ui': '❌ 不兼容,需替换为 uni-ui 或 uView',
  'ant-design-vue': '❌ 不兼容,需替换为 uni-ui 或 uView',
  'vant': '❌ 不兼容,需替换为 uni-ui 或 uView',
  'vuetify': '❌ 不兼容',
  // 图表库(需适配)
  'echarts': '⚠️ 有微信小程序版本,但绘图逻辑需重写',
  'highcharts': '⚠️ 无官方小程序版,需替换',
  // 工具库(兼容)
  'lodash': '✅ 纯 JS 库,可保留',
  'dayjs': '✅ 纯 JS 库,可保留',
  'moment': '✅ 纯 JS 库,可保留',
  // 其他
  'axios': '⚠️ 需替换为 uni.request',
  'vue-router': '⚠️ 需替换为 pages.json 配置',
  'vuex': '✅ 可保留,持久化需改用 uni.storage',
  'pinia': '✅ 可保留,持久化需改用 uni.storage',
};

// 1. 读取 package.json
function readPackageJson() {
  const pkgPath = path.join(config.targetDir, 'package.json');
  if (!fs.existsSync(pkgPath)) {
    console.error('未找到 package.json');
    process.exit(1);
  }
  return JSON.parse(fs.readFileSync(pkgPath, 'utf-8'));
}

// 2. 分析依赖
function analyzeDependencies(pkg) {
  const deps = { ...pkg.dependencies, ...pkg.devDependencies };
  const analysis = {};
  for (const [lib, version] of Object.entries(deps)) {
    if (libCompat[lib]) {
      analysis[lib] = { version, status: libCompat[lib] };
    } else {
      // 默认未知,标记为需人工确认
      analysis[lib] = { version, status: '❓ 未知,需人工确认' };
    }
  }
  return analysis;
}

// 3. 扫描源代码,检测浏览器 API 和 Vue 特性
function scanSourceCode() {
  const patterns = ['**/*.{vue,js,ts}', '!node_modules/**', '!dist/**'];
  const files = glob.sync(patterns, { cwd: config.targetDir, absolute: true });
  
  let results = {
    browserApiUsage: new Set(),
    vueFeatures: {
      customDirective: false,
      dynamicComponent: false,
      compositionApi: false,
      ts: false,
      renderFunction: false,
    },
    uiLibraries: new Set(),
    hardwareApiUsage: new Set(),
  };

  for (const file of files) {
    const content = fs.readFileSync(file, 'utf-8');
    const ext = path.extname(file);

    // 检测浏览器 API(简单正则)
    for (const api of browserApis) {
      const regex = new RegExp(`\\b${api}\\b`, 'g');
      if (regex.test(content)) {
        results.browserApiUsage.add(api);
      }
    }

    // 检测 Vue 特性(通过 AST 分析更准确,这里简单正则)
    if (content.includes('Vue.directive') || content.includes('app.directive')) {
      results.vueFeatures.customDirective = true;
    }
    if (content.includes('<component :is') || content.includes(':is=')) {
      results.vueFeatures.dynamicComponent = true;
    }
    if (content.includes('composition-api') || content.includes('setup(') || content.includes('<script setup')) {
      results.vueFeatures.compositionApi = true;
    }
    if (file.endsWith('.ts') || content.includes('lang="ts"')) {
      results.vueFeatures.ts = true;
    }
    if (content.includes('render(') && ext === '.vue') {
      results.vueFeatures.renderFunction = true;
    }

    // 检测 UI 库引用
    for (const lib of Object.keys(libCompat)) {
      if (content.includes(lib)) {
        results.uiLibraries.add(lib);
      }
    }

    // 检测硬件 API
    const hardwareKeywords = ['wx.scanCode', 'uni.scanCode', 'getLocation', 'chooseImage', 'requestPayment'];
    for (const kw of hardwareKeywords) {
      if (content.includes(kw)) {
        results.hardwareApiUsage.add(kw);
      }
    }
  }

  return results;
}

// 4. 检测路由与状态管理
function detectRouterAndStore(pkg, sourceResults) {
  let router = null;
  if (pkg.dependencies['vue-router']) {
    router = 'vue-router 已安装';
  }
  let store = null;
  if (pkg.dependencies['vuex']) {
    store = 'vuex 已安装';
  } else if (pkg.dependencies['pinia']) {
    store = 'pinia 已安装';
  }
  return { router, store };
}

// 5. 检测构建工具(通过配置文件)
function detectBuildTool() {
  const root = config.targetDir;
  if (fs.existsSync(path.join(root, 'vite.config.js')) || fs.existsSync(path.join(root, 'vite.config.ts'))) {
    return 'Vite';
  } else if (fs.existsSync(path.join(root, 'webpack.config.js'))) {
    return 'Webpack';
  } else if (fs.existsSync(path.join(root, 'vue.config.js'))) {
    return 'Vue CLI (Webpack)';
  }
  return '未知';
}

// 6. 静态资源分析(简单统计)
function analyzeAssets() {
  const patterns = ['**/*.{png,jpg,jpeg,gif,svg,ttf,woff,woff2}', '!node_modules/**', '!dist/**'];
  const files = glob.sync(patterns, { cwd: config.targetDir, absolute: true });
  let totalSize = 0;
  let largeFiles = [];
  for (const file of files) {
    const size = fs.statSync(file).size;
    totalSize += size;
    if (size > 100 * 1024) { // 超过 100KB
      largeFiles.push({ file: path.relative(config.targetDir, file), size });
    }
  }
  return { totalSize, largeFiles };
}

// 7. 生成报告
function generateReport(pkg, depsAnalysis, sourceScan, routerStore, buildTool, assets) {
  const lines = [];

  lines.push('# H5+Vue 项目转微信小程序可行性评估报告\n');
  lines.push(`生成时间:${new Date().toLocaleString()}\n`);
  lines.push(`项目路径:${config.targetDir}\n`);

  // 1. 技术栈概览
  lines.push('## 1. 技术栈概览\n');
  lines.push(`- **Vue 版本**:${pkg.dependencies['vue'] || '未检测到'}`);
  lines.push(`- **构建工具**:${buildTool}`);
  lines.push(`- **TypeScript**:${sourceScan.vueFeatures.ts ? '是' : '否'}`);
  lines.push(`- **Composition API**:${sourceScan.vueFeatures.compositionApi ? '是' : '否'}`);
  lines.push(`- **自定义指令**:${sourceScan.vueFeatures.customDirective ? '是' : '否'}`);
  lines.push(`- **动态组件**:${sourceScan.vueFeatures.dynamicComponent ? '是' : '否'}`);
  lines.push(`- **渲染函数**:${sourceScan.vueFeatures.renderFunction ? '是' : '否'}\n`);

  // 2. 依赖兼容性
  lines.push('## 2. 第三方依赖兼容性分析\n');
  lines.push('| 依赖包 | 版本 | 兼容性评估 |');
  lines.push('|--------|------|------------|');
  for (const [lib, info] of Object.entries(depsAnalysis)) {
    lines.push(`| ${lib} | ${info.version} | ${info.status} |`);
  }
  lines.push('');

  // 3. 浏览器 API 使用情况
  lines.push('## 3. 浏览器 API 使用情况\n');
  if (sourceScan.browserApiUsage.size > 0) {
    lines.push('以下 API 在小程序中不可用,需要替换:');
    for (const api of sourceScan.browserApiUsage) {
      lines.push(`- \`${api}\``);
    }
  } else {
    lines.push('未检测到明显的浏览器专有 API(仅通过正则扫描,可能存在漏报)。');
  }
  lines.push('');

  // 4. UI 库使用
  if (sourceScan.uiLibraries.size > 0) {
    lines.push('## 4. UI 库使用情况\n');
    lines.push('检测到以下 UI 库,均需替换为 uni-app 兼容的组件库:');
    for (const lib of sourceScan.uiLibraries) {
      lines.push(`- ${lib}`);
    }
    lines.push('');
  }

  // 5. 路由与状态管理
  lines.push('## 5. 路由与状态管理\n');
  lines.push(`- **路由**:${routerStore.router || '未使用 vue-router'}`);
  lines.push(`- **状态管理**:${routerStore.store || '未使用'}`);
  if (routerStore.router) {
    lines.push('  - ⚠️ vue-router 需替换为 pages.json 配置,路由守卫需重写。');
  }
  if (routerStore.store) {
    lines.push('  - ⚠️ 状态管理逻辑可保留,但持久化需改用 uni.storage。');
  }
  lines.push('');

  // 6. 硬件能力调用
  if (sourceScan.hardwareApiUsage.size > 0) {
    lines.push('## 6. 硬件能力调用\n');
    lines.push('检测到以下硬件相关 API,小程序有对应能力但调用方式不同:');
    for (const api of sourceScan.hardwareApiUsage) {
      lines.push(`- \`${api}\``);
    }
    lines.push('');
  }

  // 7. 静态资源
  lines.push('## 7. 静态资源分析\n');
  lines.push(`- **总大小**:${(assets.totalSize / 1024 / 1024).toFixed(2)} MB`);
  lines.push(`- **大文件(>100KB)**:${assets.largeFiles.length} 个`);
  if (assets.largeFiles.length > 0) {
    lines.push('  建议将大文件上传至 CDN,避免影响小程序包体积。');
  }
  lines.push('');

  // 8. 综合评估与建议
  lines.push('## 8. 综合评估与建议\n');

  let score = 100;
  if (sourceScan.browserApiUsage.size > 0) score -= 20;
  if (sourceScan.uiLibraries.size > 0) score -= 20;
  if (sourceScan.vueFeatures.dynamicComponent) score -= 10;
  if (routerStore.router) score -= 10;
  if (assets.largeFiles.length > 5) score -= 10;
  score = Math.max(0, score);

  lines.push(`**兼容性评分**:${score}/100(分数越高,迁移成本越低)\n`);

  if (score >= 80) {
    lines.push('✅ **建议**:项目兼容性较好,可考虑使用 uni-app 进行快速迁移,预计工作量较小。');
  } else if (score >= 50) {
    lines.push('⚠️ **建议**:项目存在中等程度的不兼容项,需预留充足时间进行适配。推荐使用跨端框架重构,同时保留 H5 版本。');
  } else {
    lines.push('❌ **建议**:项目兼容性较差,直接迁移成本较高。建议评估是否单独开发小程序,或仅将部分页面嵌入 WebView。');
  }

  lines.push('\n---\n');
  lines.push('*注:本报告基于静态扫描生成,可能存在误判或漏报,请结合人工审查最终确认。*');

  return lines.join('\n');
}

// 主函数
async function main() {
  const pkg = readPackageJson();
  const depsAnalysis = analyzeDependencies(pkg);
  const sourceScan = scanSourceCode();
  const routerStore = detectRouterAndStore(pkg, sourceScan);
  const buildTool = detectBuildTool();
  const assets = analyzeAssets();

  const report = generateReport(pkg, depsAnalysis, sourceScan, routerStore, buildTool, assets);
  fs.writeFileSync(config.outputFile, report, 'utf-8');
  console.log(`报告已生成:${config.outputFile}`);
}

main().catch(console.error);
相关推荐
方安乐2 小时前
ESLint代码规范(二)
前端·javascript·代码规范
zzginfo2 小时前
var、let、const、无申明 四种变量在赋值前,使用的情况
开发语言·前端·javascript
私人珍藏库2 小时前
[Windows] Cap 0.4.81
windows·工具·录屏·软件·多功能
贺小涛2 小时前
Vue介绍
前端·javascript·vue.js
cch89183 小时前
React Hooks的支持
前端·javascript·react.js
鹏程十八少3 小时前
9. Android Shadow插件化如何解决资源冲突问题和实现tinker热修复资源(源码分析4)
android·前端·面试
蜡台3 小时前
vue.config.js 配置
前端·javascript·vue.js·webpack
qq_381338503 小时前
微前端架构下的状态管理与通信机制深度解析:从 qiankun 源码到性能优化实战
前端·状态模式