两套体系完整讲透。全程大白话,所有代码可直接用,数据库/语法转换全部用官方自带工具(达梦 DTS、金仓
  KDTS、Babel、PostCSS),不自研轮子。

  ---
  第一部分:PHP 存量系统信创迁移方法论与踩坑全解

  一、先搞懂"信创迁移"到底在迁什么

  大白话:你原来的 PHP 系统跑在 Intel/AMD 的 x86 服务器 + CentOS + MySQL/Oracle + Apache/Nginx
  上。信创就是把这一整套换成国产的:

  ┌────────────┬───────────────┬─────────────────────────────────────────────────────┐
  │    层级    │  原来(海外)   │                   信创(国产替换)                    │
  ├────────────┼───────────────┼─────────────────────────────────────────────────────┤
  │ CPU        │ Intel x86     │ 鲲鹏/飞腾(ARM64)、海光/兆芯(x86)、龙芯(LoongArch)   │
  ├────────────┼───────────────┼─────────────────────────────────────────────────────┤
  │ 操作系统   │ CentOS/RedHat │ 麒麟 Kylin V10、统信 UOS                            │
  ├────────────┼───────────────┼─────────────────────────────────────────────────────┤
  │ 数据库     │ MySQL/Oracle  │ 达梦 DM8、人大金仓 KingbaseES、OceanBase、openGauss │
  ├────────────┼───────────────┼─────────────────────────────────────────────────────┤
  │ Web 中间件 │ Apache/Nginx  │ 东方通 TongWeb、Nginx(国产编译版可继续用)           │
  ├────────────┼───────────────┼─────────────────────────────────────────────────────┤
  │ 浏览器     │ Chrome/Edge   │ 奇安信、360、红莲花(都是 Chromium 内核)             │
  └────────────┴───────────────┴─────────────────────────────────────────────────────┘

  最大的坑就一句话:CPU 从 x86 变成了 ARM64,字节序和指令集变了,PHP
  本身没问题(开源能重编),但你装的那些扩展(swoole、redis、各种 .so)很多没有 ARM 版,要重新编译。

  ---
  二、完整迁移流程(7 步法)1步 资产盘点  → 第2步 兼容性评估  → 第3步 环境搭建(ARM+国产OS)
     → 第4步 PHP及扩展重编译  → 第5步 数据库迁移(用官方工具)
     → 第6步 代码SQL方言改造  → 第7步 回归测试+性能压测

  第 1:资产盘点(别上来就动手)

  先把家底摸清楚。用这个脚本一次性扫出你系统依赖了什么:

  #!/bin/bash
  # 文件名你自己存,我这里只给代码:asset_scan.sh
  # 作用:盘点 PHP 版本、所有扩展、用了哪些数据库函数、有没有写死 x86 的东西

  echo "======== 1. PHP 版本 ========"
  php -v

  echo "======== 2. 已加载扩展(重点看哪些是第三方.so) ========"
  php -m

  echo "======== 3. 扩展物理文件位置(看有没有现成ARM版) ========"
  php -i | grep extension_dir
  ls -lh $(php -config --extension-dir 2>/dev/null || php -i | grep '^extension_dir' | awk '{print $3}')

  echo "======== 4. 代码里用了哪些危险的数据库特有写法 ========"
  # mysql_* 老函数(PHP7已删)、Oracle的OCI、SQL方言
  grep -rn "mysql_query\|mysql_connect\|mysqli_\|oci_\|->query(" --include="*.php" . | head -50

  echo "======== 5. 代码里有没有写死的本地命令/路径(ARM下可能没有) ========"
  grep -rn "exec(\|shell_exec(\|system(\|/usr/bin/\|\.exe" --include="*.php" . | head -50

  echo "======== 6. composer 依赖里有没有带C扩展的包 ========"
  cat composer.json 2>/dev/null | grep -i "ext-"

  盘点输出三张清单:
  1. PHP 扩展清单(哪些要重编)
  2. SQL 写法清单(哪些方言要改)
  3. 系统调用清单(exec 调的命令 ARM 上有没有)

  ---2:兼容性评估(判断哪些会爆雷)

  按这个表自查,这是 90% 的坑所在:

  ┌────────────────────────────┬──────────────────────────────────────┬──────────────────────────────────────────┐
  │           风险点           │              为什么会坑              │                 怎么处理                 │
  ├────────────────────────────┼──────────────────────────────────────┼──────────────────────────────────────────┤
  │ swoole / swow              │ 协程库深度依赖底层,早期版本没 ARM 包 │ 用 swoole 5.x+,官方已支持 ARM64,重编即可 │
  ├────────────────────────────┼──────────────────────────────────────┼──────────────────────────────────────────┤
  │ php-redis / mongodb 等 .so │ 二进制只编了 x86                     │ 用 pecl install 在 ARM 机器上现编        │
  ├────────────────────────────┼──────────────────────────────────────┼──────────────────────────────────────────┤
  │ mysql_ 老函数*             │ PHP7 已彻底删除                      │ 全部换成 PDO(顺便为换库做准备)           │
  ├────────────────────────────┼──────────────────────────────────────┼──────────────────────────────────────────┤
  │ Oracle OCI8                │ 达梦/金仓没有 OCI 接口               │ 换成 PDO,SQL 方言改造                    │
  ├────────────────────────────┼──────────────────────────────────────┼──────────────────────────────────────────┤
  │ GD / ImageMagick           │ 依赖系统图形库                       │ 国产 OS 源里装 libpng/libjpeg 再编       │
  ├────────────────────────────┼──────────────────────────────────────┼──────────────────────────────────────────┤
  │ 写死的 /usr/bin/x86_64...  │ ARM 路径不一样                       │ 改成动态查找 which                       │
  ├────────────────────────────┼──────────────────────────────────────┼──────────────────────────────────────────┤
  │ 整数/位运算依赖 32 位      │ LoongArch/ARM 都是 64 位 LP64        │ 一般无影响,但位掩码要 review             │
  └────────────────────────────┴──────────────────────────────────────┴──────────────────────────────────────────┘

  ---3:搭建信创环境

  以最主流的 麒麟 V10(ARM64)+ 达梦 DM8 为例。

  # 看清楚架构,确认是 aarch64(ARM64)
  uname -m          # 输出 aarch64 就对了
  cat /etc/os-release   # 确认 Kylin V10

  # 麒麟用的是自己的 yum 源,先装编译 PHP 必需的基础库
  sudo yum install -y gcc gcc-c++ make autoconf \
    libxml2-devel openssl-devel libcurl-devel \
    libjpeg-turbo-devel libpng-devel freetype-devel \
    oniguruma-devel sqlite-devel libzip-devel

  ---4:PHP 及扩展重编译(核心)

  大白话:PHP 源码是跨平台的,在 ARM 机器上重新 ./configure && make 就能编出 ARM 版,关键是扩展也得在 ARM 机器上现编。

  # ===== 编译 PHP 8.3 本体(ARM64) =====
  cd /usr/local/src
  wget https://www.php.net/distributions/php-8.3.0.tar.gz
  tar -zxvf php-8.3.0.tar.gz
  cd php-8.3.0

  # configure 不需要指定架构,编译器自动认 aarch64
  ./configure \
    --prefix=/usr/local/php \
    --with-config-file-path=/usr/local/php/etc \
    --enable-fpm \
    --with-openssl \
    --with-curl \
    --enable-mbstring \
    --with-zlib \
    --enable-gd --with-jpeg --with-freetype \
    --with-pdo-mysql \
    --enable-sockets \
    --enable-opcache

  make -j$(nproc)        # nproc 自动用满核数
  sudo make install

  # ===== 用 pecl 现编扩展(它会自动针对 ARM 编) =====
  sudo /usr/local/php/bin/pecl install redis
  sudo /usr/local/php/bin/pecl install swoole   # 5.x 已原生支持ARM

  # 在 php.ini 里加上
  echo "extension=redis.so" >> /usr/local/php/etc/php.ini
  echo "extension=swoole.so" >> /usr/local/php/etc/php.ini

  # 验证扩展真的是 ARM64 的
  file /usr/local/php/lib/php/extensions/*/redis.so
  # 输出里要有 "ARM aarch64" 字样才对

  踩坑提醒:
  - 别从 x86 机器拷 .so 过来,file 命令一查就是 x86-64,装上直接 segfault。
  - swoole 一定用 5.x,4.x 老版本 ARM 下编译报错多。

  ---
  第 5 步:数据库迁移(重点:全部用官方自带工具,不要手写脚本搬数据)

  这是你特别强调的"用自带工具不自研"。各家国产库都有官方图形化迁移工具,直接把 MySQL/Oracle
  的表结构+数据+索引一键搬过去。

  方案 A:迁到达梦 DM8 ——用官方 DTS(DM Data Transfer Service)

  达梦自带 dts 工具,在 达梦安装目录/tool/dts 下,图形界面操作:
  1. 新建迁移 →选源(MySQL)→填 MySQL 连接
  2. 选目标(DMSQL)→填达梦连接
  3. 勾选要迁的表 →勾"迁移对象包含:表结构+数据+索引+约束"
  4. 高级选项:把 "对象名大小写" 设为和原库一致(达梦默认大写,这是大坑!)
  5. 点开始,它自动建表+灌数据

  命令行批量方式(适合自动化):

  # 达梦命令行迁移工具 dmfldr / dts_console
  # 达梦提供 dts 的命令行版,导出一个迁移配置 xml 后执行
  cd /opt/dmdbms/tool
  ./dts_console /file=migrate_config.xml /log=migrate.log
  # migrate_config.xml 在图形界面里点"保存配置"就能生成,不用手写

  方案 B:迁到人大金仓 KingbaseES ——用官方 KDTS + 实时同步用 KFS

  # KDTS:全量迁移工具(金仓安装包自带,Tools/kdts 目录)
  # 图形界面:配置源 MySQL →目标 Kingbase →自动转换数据类型 →迁移
  # 它内置了 MySQL→Kingbase的类型映射,不用你自己对照

  # KFS:迁移完之后,业务还在跑,用 KFS 做增量实时同步,实现"双跑不停机"
  # 配置好后 MySQL 的新增改删会实时同步到 Kingbase,等切换窗口一到直接切

  方案 C:迁到 OceanBase ——用官方 OMS(OceanBase Migration Service)

  OMS 是图形化平台,功能最全:
  - 结构迁移 + 全量迁移 + 增量同步 + 反向同步(回滚保险)
  - MySQL 模式几乎零改造(OceanBase 高度兼容 MySQL 协议)
  若你原库是 MySQL,迁 OceanBase 最省事,SQL 基本不用改。

  选型大白话建议:
  - 原来是 MySQL →优先 OceanBase(兼容 MySQL,代码几乎不改)。
  - 政府/国企强制要求达梦/金仓 →用 达梦 DTS / 金仓 KDTS,但 SQL 方言要改(见第 6 步)。
  - 原来是 Oracle →达梦兼容 Oracle 语法最好,用达梦,改动最小。

  ---
  第 6 步:代码 SQL 方言改造(改造量最大的一步)

  大白话:不同数据库 SQL 写法有差异,达梦/金仓不认 MySQL 的某些语法。最优策略——用PDO
  把数据库访问收口到一个地方,改一处就行,别在几百个文件里散着写 SQL。

  6.1 先把所有数据库访问统一成 PDO(标准做法,最优方案)

  <?php
  // 统一数据库连接层 ——改库只改这一个文件的 DSN
  class Db
  {
      private static ?PDO $pdo = null;

      public static function conn(): PDO
      {
          if (self::$pdo !== null) {
              return self::$pdo;
          }

          // ===== 切换数据库就改这里的 DSN,业务代码一行不动 =====

          // 原 MySQL:
          // $dsn = 'mysql:host=127.0.0.1;port=3306;dbname=app;charset=utf8mb4';

          // 达梦 DM8(走官方 PDO_DM 驱动 或 ODBC):
          // $dsn = 'dm:host=127.0.0.1;port=5236';

          // 人大金仓 / openGauss(兼容 PostgreSQL 协议,用 pdo_pgsql):
          // $dsn = 'pgsql:host=127.0.0.1;port=54321;dbname=app';

          // OceanBase(兼容 MySQL,继续用 pdo_mysql):
          $dsn = 'mysql:host=127.0.0.1;port=2883;dbname=app;charset=utf8mb4';

          self::$pdo = new PDO($dsn, 'username', 'password', [
              PDO::ATTR_ERRMODE            => PDO::ERRMODE_EXCEPTION,
              PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
              PDO::ATTR_EMULATE_PREPARES   => false, // 用真预处理,防注入也更兼容
          ]);
          return self::$pdo;
      }
  }

  业务里全部用预处理,杜绝 SQL 注入,也最容易跨库:

  <?php
  // 所有查询都走这个标准姿势
  $stmt = Db::conn()->prepare('SELECT id, name FROM users WHERE age > ? AND city = ?');
  $stmt->execute([18, '北京']);
  $rows = $stmt->fetchAll();

  6.2 常见 SQL 方言差异对照表(达梦/金仓踩坑清单)

  ┌──────────────────────────────────┬─────────────────────────────────┬───────────────────────────────────────────┐
  │            MySQL 写法            │        达梦 / 金仓 不认         │               跨库正确写法                │
  ├──────────────────────────────────┼─────────────────────────────────┼───────────────────────────────────────────┤
  │ `column` 反引号                  │ 不支持反引号                    │ 用双引号 "column" 或不加引号              │
  ├──────────────────────────────────┼─────────────────────────────────┼───────────────────────────────────────────┤
  │ LIMIT 10 OFFSET 20               │ 达梦用                          │ 用标准 OFFSET 20 ROWS FETCH NEXT 10 ROWS  │
  │                                  │ LIMIT,金仓支持但语法略不同      │ ONLY                                      │
  ├──────────────────────────────────┼─────────────────────────────────┼───────────────────────────────────────────┤
  │ IFNULL(a,b)                      │ 不支持                          │ 用标准 COALESCE(a,b)                      │
  ├──────────────────────────────────┼─────────────────────────────────┼───────────────────────────────────────────┤
  │ NOW() / CURDATE()                │ 部分不支持                      │ 达梦用 SYSDATE,金仓用 CURRENT_TIMESTAMP   │
  ├──────────────────────────────────┼─────────────────────────────────┼───────────────────────────────────────────┤
  │ GROUP_CONCAT()                   │ 不支持                          │ 达梦 WM_CONCAT,金仓 STRING_AGG            │
  ├──────────────────────────────────┼─────────────────────────────────┼───────────────────────────────────────────┤
  │ INSERT ... ON DUPLICATE KEY      │ 不支持                          │ 用标准 MERGE INTO                         │
  │ UPDATE                           │                                 │                                           │
  ├──────────────────────────────────┼─────────────────────────────────┼───────────────────────────────────────────┤
  │ auto_increment                   │ 写法不同                        │ 达梦/金仓用序列 SEQUENCE                  │
  ├──────────────────────────────────┼─────────────────────────────────┼───────────────────────────────────────────┤
  │ 表名/字段名大小写不敏感          │ 达梦默认全大写                  │ 迁移时统一,或代码里全大写                 │
  └──────────────────────────────────┴─────────────────────────────────┴───────────────────────────────────────────┘

  最优做法:用查询构造器 / ORM 屏蔽方言,别手写裸 SQL。 推荐用 Laravel 的 Eloquent / 查询构造器 或 Doctrine DBAL,它们内置
  了各数据库的"语法转换器(Grammar)",你写一份代码,它自动翻译成对应数据库的方言——这就是你要的"用自带语法转换工具不自研":

  <?php
  // 用 Laravel 查询构造器,跨库零改造(它内部按驱动自动生成对应方言SQL)
  use Illuminate\Database\Capsule\Manager as Capsule;

  $capsule = new Capsule;
  $capsule->addConnection([
      'driver'   => 'pgsql',   // 金仓/openGauss 用 pgsql;OceanBase 用 mysql;达梦用社区dm驱动
      'host'     => '127.0.0.1',
      'port'     => '54321',
      'database' => 'app',
      'username' => 'user',
      'password' => 'pwd',
  ]);
  $capsule->setAsGlobal();
  $capsule->bootEloquent();

  // 业务代码完全不感知底层是什么库,框架自动翻译方言
  $users = Capsule::table('users')
      ->where('age', '>', 18)
      ->orderBy('id')
      ->limit(10)->offset(20)   // 框架自动转成各库正确的分页语法
      ->get();

  ---
  第 7 步:回归测试 + 性能压测

  # 1. 功能回归:跑你原有的 PHPUnit 测试套件
  ./vendor/bin/phpunit

  # 2. 接口回归:用 ab 压测对比 x86 和 ARM 下的接口表现
  ab -n 5000 -c 100 http://127.0.0.1/api/users

  # 3. 重点压数据库:达梦/金仓的执行计划和 MySQL 不同,慢SQL要重新优化
  #    达梦看执行计划:
  #    EXPLAIN SELECT ... ;  然后对慢查询补索引

  ARM 性能踩坑: ARM 单核主频可能低于 Intel,但核多。PHP-FPM 的 pm.max_children 要按 ARM 核数调大,把多核吃满,整体吞吐不输
  x86。

  ---
  三、PHP 信创迁移踩坑 Top 10 速查

  1. 从 x86 拷 .so 到 ARM →必崩,所有扩展 ARM 机现编。
  2. 达梦默认大写表名 →迁移和代码大小写不一致,查不到数据。迁移时统一。
  3. mysql_ 老函数* →PHP7+ 已删,全换 PDO。
  4. 反引号 ` →达梦/金仓不认,换双引号。
  5. 自增主键 →国产库要建 SEQUENCE,迁移工具能自动转,别手建漏了。
  6. 中文乱码 →字符集统一 UTF-8,达梦建库时指定 CHARSET=UTF-8。
  7. swoole 4.x →ARM 编译报错,升 5.x。
  8. exec 调系统命令 →ARM 上命令路径/参数不同,用 which 动态找。
  9. 时间函数 NOW()/SYSDATE →方言不同,用 ORM 或 COALESCE 类标准函数。
  10. 不做增量同步直接停机切 →数据丢失风险,用 KFS/OMS 做双跑同步再切。

  ---
  ---
  第二部分:国产浏览器与 PHP 前端交互兼容性适配体系

  一、先搞懂国产浏览器到底是什么

  大白话:国产浏览器(奇安信、360 安全浏览器、红莲花、UOS 浏览器)绝大多数是基于 Chromium 内核改的,所以现代 JS/CSS
  基本都支持。真正的坑是两个:

  1. 双核浏览器:360、搜狗这类有"极速模式(Chromium)"和"兼容模式(IE 内核 Trident)"两套内核。政府老系统常被强制用 IE
  兼容模式,那就是 IE8/IE9 级别的远古环境,你的 ES6、fetch、flex 全跪。
  2. Chromium 版本偏老:有些国产浏览器内核停留在 Chromium 8x、9x,最新的 ES2022 语法、?. 可选链可能不支持。

  核心策略一句话:用 Babel 把新 JS 翻译成老 JS,用 PostCSS+Autoprefixer 把新 CSS 翻译成老 CSS,用 core-js 把缺失的 API
  补上。这些全是业界标准工具,绝不自研。

  ---
  二、完整适配流程

  第1步 探测目标浏览器内核与版本  → 第2步 定 browserslist 目标
    → 第3步 Babel转译JS+core-js补丁  → 第4步 PostCSS转译CSS
    → 第5步 PHP后端按UA下发对应资源  → 第6步 IE兼容模式兜底处理
    → 第7步 真机回归测试

  ---
  第 1 步:探测目标浏览器(先知道敌人是谁)

  后端用 PHP 解析 UA,前端用 JS 探测能力。先做个探测页跑一遍:

  <?php
  // 后端探测:解析 User-Agent,判断是哪个国产浏览器、什么内核
  function detectBrowser(string $ua): array
  {
      $result = ['name' => 'unknown', 'engine' => 'unknown', 'mode' => 'modern'];

      // 国产浏览器特征(它们 UA 里都带各自标识)
      if (stripos($ua, 'QihooBrowser') !== false || stripos($ua, 'QiHoo') !== false) {
          $result['name'] = '奇安信/360';
      } elseif (stripos($ua, 'UOSBrowser') !== false) {
          $result['name'] = '统信UOS浏览器';
      } elseif (stripos($ua, 'Hongmeng') !== false || stripos($ua, 'HarmonyOS') !== false) {
          $result['name'] = '鸿蒙';
      }

      // 关键:判断是不是落到了 IE 兼容模式(Trident=IE内核)
      if (stripos($ua, 'Trident') !== false || stripos($ua, 'MSIE') !== false) {
          $result['engine'] = 'Trident(IE内核)';
          $result['mode']   = 'legacy';   // 远古模式,要兜底!
      } elseif (stripos($ua, 'Chrome') !== false) {
          // 抓出 Chromium 版本号,判断老不老
          preg_match('/Chrome\/(\d+)/', $ua, $m);
          $ver = (int)($m[1] ?? 0);
          $result['engine'] = "Chromium $ver";
          $result['mode']   = $ver < 80 ? 'old-chromium' : 'modern';
      }
      return $result;
  }

  // 用法
  $info = detectBrowser($_SERVER['HTTP_USER_AGENT'] ?? '');
  // $info['mode'] 决定后面下发哪套前端资源

  ---
  第 2 步:用 browserslist 声明你要兼容到多老

  大白话:browserslist 是一个配置文件,你写明"我要支持到 IE9、Chrome
  49",后面所有工具(Babel、PostCSS)都读它,自动决定翻译到什么程度。这是整个体系的总开关。

  // package.json 里加一段(这就是 browserslist 配置,业界标准,所有工具都认它)
  {
    "browserslist": [
      "> 0.5%",
      "last 2 versions",
      "Chrome >= 49",
      "ie >= 9",
      "Firefox ESR"
    ]
  }

  如果确认要兼容 IE 兼容模式(政府场景),就把 ie >= 9 写进去,Babel 会自动转到 ES5。

  ---
  第 3 步:Babel 转译 JS + core-js 补 API(最关键)

  大白话:你写 ES6+ 的现代 JS(箭头函数、const、async/await、可选链),Babel 自动翻译成老浏览器能跑的 ES5。语法它翻译,新
  API(Promise、fetch、Array.includes)它用 core-js 补丁补上。全自动,你只管写现代代码。

  # 安装 Babel 全家桶 + core-js(都是官方标准库,不自研)
  npm install --save-dev @babel/core @babel/cli @babel/preset-env
  npm install core-js@3

  // babel.config.js  ——Babel 配置,核心是 preset-env + corejs
  module.exports = {
    presets: [
      ['@babel/preset-env', {
        // 不写 targets,它自动读 package.json 里的 browserslist
        useBuiltIns: 'usage',   // 关键:只按你代码实际用到的API来打补丁,体积最小
        corejs: 3,              // 用 core-js@3 提供 Promise/fetch等垫片
      }],
    ],
  };

  # 执行转译:把现代源码 src/ 翻译成老浏览器能跑的 dist/
  npx babel src --out-dir dist

  效果对比(Babel 自动帮你做的翻译):

  // 你写的现代代码(src/app.js)
  const getUser = async (id) => {
    const res = await fetch(`/api/user?id=${id}`);
    const data = await res.json();
    return data?.name ?? '匿名';   // 可选链 + 空值合并
  };

  // Babel 自动翻译成 ES5(dist/app.js),IE 也能跑(配合core-js的Promise/fetch补丁)
  // 你完全不用手写这些兼容代码,Babel 全自动生成

  踩坑提醒: 一定要装 core-js 并设 useBuiltIns: 'usage'。光转语法不补 API,IE 里 Promise is not defined 照样崩。

  ---
  第 4 步:PostCSS 转译 CSS + Autoprefixer 自动加前缀

  大白话:CSS 也一样。你写标准的 flex、grid,Autoprefixer 自动加 -webkit-、-ms- 前缀让老内核认识。同样读
  browserslist,全自动。

  npm install --save-dev postcss postcss-cli autoprefixer postcss-preset-env

  // postcss.config.js
  module.exports = {
    plugins: [
      // postcss-preset-env:把现代CSS(嵌套、自定义属性)翻译成老CSS
      require('postcss-preset-env')({ stage: 2 }),
      // autoprefixer:自动加浏览器前缀,也是读 browserslist
      require('autoprefixer'),
    ],
  };

  npx postcss src/style.css -o dist/style.css

  效果:

  /* 你写的现代 CSS */
  .box { display: flex; user-select: none; }

  /* Autoprefixer 自动加前缀后(老国产内核/IE兼容模式能认) */
  .box {
    display: -webkit-box;
    display: -ms-flexbox;
    display: flex;
    -webkit-user-select: none;
    -ms-user-select: none;
    user-select: none;
  }

  最优整合方案:别手动跑 babel/postcss 命令,用 Vite 打包,它内部集成了这一整套。

  // vite.config.js ——最优:一个配置全搞定,生产构建自动 Babel+PostCSS+legacy 兜底
  import { defineConfig } from 'vite';
  import legacy from '@vitejs/plugin-legacy';

  export default defineConfig({
    plugins: [
      // 这个官方插件自动为老浏览器生成 ES5 版本 + 注入 polyfill,IE11也能跑
      legacy({
        targets: ['ie >= 11', 'Chrome >= 49'],
        additionalLegacyPolyfills: ['regenerator-runtime/runtime'],
      }),
    ],
  });

  @vitejs/plugin-legacy 是最省事的——它会自动产出现代版和老旧版两套bundle,现代浏览器加载现代版,老国产浏览器自动降级加载
  ES5 版,完全自动。

  ---5:PHP 后端按浏览器下发对应资源(差异化下发)

  大白话:用第 1 步探测的结果,给现代浏览器发现代 bundle,给 IE 兼容模式发 ES5 兜底版。这叫"自适应下发"<?php
  // PHP 模板里,根据探测结果决定加载哪套前端资源
  $info = detectBrowser($_SERVER['HTTP_USER_AGENT'] ?? '');
  ?>
  <!DOCTYPE html>
  <html>
  <head>
    <meta charset="utf-8">
    <!-- 关键!强制 360/搜狗双核浏览器用"极速模式(Chromium)"而不是IE兼容模式 -->
    <meta name="renderer" content="webkit">
    <!-- 强制 IE 用最高级内核,别用兼容视图 -->
    <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">

    <?php if ($info['mode'] === 'legacy'): ?>
      <!-- 落到IE内核了:加载 ES5 兜底版 + 提示升级 -->
      <script nomodule src="/dist/legacy/app.es5.js"></script>
      <link rel="stylesheet" href="/dist/legacy/style.css">
    <?php else: ?>
      <!-- 现代内核:加载现代版,用 type=module 让老浏览器自动忽略 -->
      <script type="module" src="/dist/modern/app.js"></script>
      <link rel="stylesheet" href="/dist/modern/style.css">
    <?php endif; ?>
  </head>
  <body>
    <div id="app"></div>
  </body>
  </html>

  <meta name="renderer" content="webkit"> 这行是国产双核浏览器的命脉——它告诉360/搜狗:"用 Chromium 极速内核渲染我,别用
  IE 兼容内核"。加了这行,90% 的国产浏览器兼容问题直接消失。

  ---6:IE 兼容模式兜底(实在躲不掉的远古环境)

  如果客户死活要求在 IE 兼容模式下能用,加这层兜底:

  <!--<head> 最前面用条件注释引入IE专用polyfill(只有IE认条件注释) -->
  <!--[if lte IE 9]>
    <script src="https://polyfill.io/v3/polyfill.min.js?features=es6,fetch,Promise"></script>
  <![endif]-->

  // 前端能力探测降级:发现是远古环境就给个友好提示,引导用极速模式
  if (!window.fetch || !window.Promise) {
    document.getElementById('app').innerHTML =
      '<div style="padding:40px;text-align:center;">' +
      '检测到您正在使用兼容(IE)模式,请点击地址栏右侧的<b>闪电图标</b>切换到"极速模式"以获得最佳体验。' +
      '</div>';
  }

  ---7:真机回归测试

  大白话:国产浏览器有自己的怪癖,模拟器不准,必须真机测。 重点测这几样:

  // 兼容性自检脚本,在目标国产浏览器里跑一遍,看哪些挂了
  const checks = {
    'ES6箭头函数':   (() => { try { eval('(()=>{})'); return true; } catch { return false; } })(),
    'Promise':       typeof Promise !== 'undefined',
    'fetch':         typeof fetch !== 'undefined',
    'Flexbox':       CSS.supports && CSS.supports('display', 'flex'),
    'Grid':          CSS.supports && CSS.supports('display', 'grid'),
    'CSS变量':       CSS.supports && CSS.supports('--a', '0'),
  };
  console.table(checks);  // 哪个是 false 就针对性补 polyfill

  测试矩阵建议覆盖:奇安信浏览器、360(极速+兼容双模式)、统信 UOS 浏览器、红莲花 ×麒麟/UOS 操作系统。

  ---
  三、前端兼容踩坑 Top 8 速查

  1. 双核浏览器默认进了 IE 兼容模式 →加 <meta name="renderer" content="webkit"> 强制极速内核。
  2. 只转语法没补 API →装 core-js,Promise is not defined 才不会崩。
  3. 手写兼容代码 →别!用 Babel+Autoprefixer 自动转,自研必出错。
  4. 国产浏览器 Chromium 版本偏老 →browserslist 里目标定低一点,如 Chrome >= 495. 字体问题 →国产 OS 默认无微软雅黑,CSS 里 font-family 加 "思源黑体","Noto Sans CJK" 兜底。
  6. HTTPS 国密证书 →国产浏览器可能要求 SM2 国密证书,Nginx 要配国密版。
  7. <meta charset> 没写或写晚 →中文乱码,必须放 <head> 第一行。
  8. fetch 跨域 →老国产内核 CORS 实现有差异,后端 PHP 记得发全 Access-Control-Allow-* 头。

  ---
  四、两套体系的"最优工具链"总结

  ┌─────────────────┬─────────────────────────────────────────────────────┐
  │      任务       │         最优工具(全是官方/业界标准,不自研)          │
  ├─────────────────┼─────────────────────────────────────────────────────┤
  │ MySQL→达梦迁移 │ 达梦官方 DTS                                        │
  ├─────────────────┼─────────────────────────────────────────────────────┤
  │ MySQL→金仓迁移 │ 金仓 KDTS(全量)+ KFS(增量同步)                      │
  ├─────────────────┼─────────────────────────────────────────────────────┤
  │ MySQL→OceanBase│ OMS 迁移服务(最省事)                                │
  ├─────────────────┼─────────────────────────────────────────────────────┤
  │ Oracle→国产库  │ 达梦(Oracle 兼容最好)                               │
  ├─────────────────┼─────────────────────────────────────────────────────┤
  │ PHP 数据库跨库  │ PDO + Laravel查询构造器/Doctrine DBAL(自动方言转换) │
  ├─────────────────┼─────────────────────────────────────────────────────┤
  │ PHP 扩展 ARM 化 │ pecl 现编(不要拷贝 .so)                             │
  ├─────────────────┼─────────────────────────────────────────────────────┤
  │ JS 语法转译     │ Babel @babel/preset-env                             │
  ├─────────────────┼─────────────────────────────────────────────────────┤
  │ JS API 补丁     │ core-js@3                                           │
  ├─────────────────┼─────────────────────────────────────────────────────┤
  │ CSS 转译加前缀  │ PostCSS + Autoprefixer + postcss-preset-env         │
  ├─────────────────┼─────────────────────────────────────────────────────┤
  │ 一键打包+降级   │ Vite + @vitejs/plugin-legacy                        │
  └─────────────────┴─────────────────────────────────────────────────────┘

  ---
  需要我针对你的具体场景再往下钻吗?比如:
  - 你现在用的是 MySQL 还是 Oracle,目标是 达梦还是金仓?我给你专门那条线的完整迁移脚本。
  - 你的 PHP 框架是 Laravel / ThinkPHP / 原生?改造方式不一样,我给对应的改法。
  - 国产浏览器具体是哪几款?我帮你定精确的 browserslist。

  告诉我环境,我直接给你贴合实际的完整代码。

更多推荐