4、框架适配与重构:从jQuery迁移到React/Vue、从AngularJS迁移到现代框架、组件化拆分策略

说实话,做界面移植最头疼的往往不是业务逻辑,而是框架层面的「代沟」。我见过太多团队,把jQuery代码直接塞进React项目里,结果状态管理乱成一锅粥。今天咱们就聊聊,怎么把老框架的代码,体面地迁移到现代框架上。

4.1 从jQuery到React/Vue:不只是换API

很多人觉得,jQuery到React就是换个写法。其实不然。jQuery是直接操作DOM,React/Vue是数据驱动视图。这个思维转变,才是最大的坎。

核心差异一句话:jQuery是你告诉DOM「怎么做」,React/Vue是你告诉框架「要什么」。

4.1.1 迁移策略:渐进式替换

我建议不要搞「大爆炸式重写」。风险太高,而且业务等不起。我习惯的做法是:

  1. 先搭架子:在现有页面中嵌入一个React/Vue根节点,比如 <div id="app"></div>。
  2. 逐个模块替换:把页面拆成独立功能块,比如搜索框、表格、弹窗,一个一个迁移。
  3. 共享状态用事件桥接:老代码和新框架之间,通过自定义事件或全局状态对象通信。

小技巧:迁移期间,可以用一个「包装器组件」把jQuery插件包起来。比如在React里用 useEffect 初始化jQuery插件,卸载时清理。这样能平滑过渡。

4.1.2 代码示例:jQuery vs React

看个实际例子。老代码是这样的:

// jQuery 方式
$('#btn').on('click', function() {
  var count = parseInt($('#count').text()) + 1;
  $('#count').text(count);
});

迁移到React后:

// React 方式
function Counter() {
  const [count, setCount] = useState(0);
  return (
    <div>
      <span>{count}</span>
      <button onClick={() => setCount(c => c + 1)}>+1</button>
    </div>
  );
}

你看,React里没有 $('#count') 这种选择器了。数据在 state 里,视图自动跟着变。这个思维转变,说白了就是「别碰DOM,只管数据」。

4.2 从AngularJS迁移到现代框架

AngularJS(1.x)到Angular(2+)或者React/Vue,这个跨度更大。AngularJS的双向绑定和脏检查机制,跟现代框架的虚拟DOM、单向数据流完全是两码事。

我曾经接手过一个AngularJS项目,控制器里到处都是 $scope 和 $watch。迁移时我踩了不少坑,这里分享几个关键点。

4.2.1 告别$scope,拥抱组件化

AngularJS里,控制器和视图是通过 $scope 绑定的。现代框架里,组件自己管自己的状态。迁移时,我习惯把每个 controller 对应成一个组件。

AngularJS 概念 现代框架对应 迁移要点
$scope 组件状态(state/data) 把 $scope 上的属性拆到组件的 data 或 state 里
$watch 计算属性 / useEffect 用 computed 或 useEffect 替代监听逻辑
$emit/$broadcast 事件总线 / 状态管理 用 Vuex/Pinia 或 Redux/Zustand 替代
指令(directive) 组件 / 自定义指令 大部分指令可以直接封装成组件

注意:AngularJS的 $digest 循环在现代框架里不存在。如果你在迁移后遇到视图不更新,多半是直接修改了对象属性而不是替换引用。记得用不可变数据的方式更新状态。

4.2.2 依赖注入的迁移

AngularJS的依赖注入很强大,但也容易让人写出「大控制器」。迁移时,我建议把服务(service)拆成独立的工具函数或 composables(Vue)/ hooks(React)。

// AngularJS 服务
app.service('UserService', function($http) {
  this.getUser = function(id) {
    return $http.get('/api/user/' + id);
  };
});

// Vue 3 组合式函数
export function useUser() {
  const getUser = async (id) => {
    return await fetch('/api/user/' + id).then(r => r.json());
  };
  return { getUser };
}

你看,迁移后代码更轻量,而且不依赖框架的「魔法」了。测试也更好写。

4.3 组件化拆分策略

框架迁移只是第一步。真正让项目「活」起来的,是合理的组件化拆分。我见过不少项目,迁移完了,结果一个组件几千行代码,比老项目还难维护。

4.3.1 拆分的核心原则

我个人习惯用这几个维度来判断要不要拆:

  • 单一职责:一个组件只做一件事。比如「用户头像组件」只管显示头像,别在里面写登录逻辑。
  • 可复用性:如果一段UI在三个以上地方出现,就该抽成组件。
  • 可测试性:组件越纯(不依赖外部状态),越好测。

我的经验:刚开始拆的时候,宁可多拆几个小组件,也别硬塞成一个。拆多了可以合并,拆少了重构成本更高。

4.3.2 拆分层次:从页面到原子

我习惯用「原子设计」的思路来分层。下面这张图能帮你理解:

组件化拆分层次 原子组件 按钮、输入框、图标 分子组件 搜索栏、表单组 组织组件 导航栏、数据表格 页面 完整视图 说明: 原子组件是最小单元,不可再分。 分子组件由原子组合而成,有具体功能。 组织组件由分子和原子构成,形成独立模块。 实际案例:用户管理页面 原子:Button(按钮)、Input(输入框)、Avatar(头像)、Badge(徽标) 分子:SearchBar(搜索栏)、UserCard(用户卡片)、Pagination(分页) 组织:UserTable(用户表格)、FilterPanel(筛选面板) 页面:UserManagementPage(用户管理页) → 组合以上所有组件

4.3.3 拆分时的避坑指南

我曾经在一个项目里,把每个按钮都拆成独立组件,结果光按钮就有20多种变体,维护起来反而更累。后来我总结了几条经验:

  • 别过度抽象:如果一个组件只有一处使用,而且逻辑简单,先别急着拆。等出现第二个使用者再说。
  • Props 不要超过5个:如果一个组件的 props 超过5个,说明它可能承担了太多职责。考虑拆成子组件或用插槽(slot)。
  • 状态提升要适度:把状态放到最近的共同父组件里。别一上来就扔到全局 store 里,那会让组件变得「不纯」。

我曾经踩过的坑:迁移时为了「好看」,把所有逻辑都塞进组件里,结果组件之间耦合严重。后来我改用「容器组件 + 展示组件」的模式——容器组件管数据和逻辑,展示组件只管渲染。这样一拆,代码清晰多了。

4.4 迁移后的验证与测试

框架迁移完,不代表就完事了。我习惯做三件事:

  1. 视觉对比:用像素级对比工具(比如 Percy)对比迁移前后的页面截图。
  2. 功能回归:把老项目的测试用例在新项目上跑一遍。如果没有测试,就手动走一遍核心流程。
  3. 性能基线:记录迁移前后的首屏加载时间、交互响应时间。现代框架不一定比jQuery快,但用户体验应该更好。

嗯,框架适配和重构这件事,说白了就是「用新思维解决老问题」。别想着一步到位,慢慢来,反而更快。