前端老铁别再瞎改state了!Immutable.js让你数据稳如老狗(附避坑指
/ 定义一个User结构id: null,name: '',email: '',age: 0,});// 创建实例const user1 = new User({ name: '张三', email: 'zhangsan@example.com' });// '张三'// 0,默认值// 不能随意加字段,会忽略或者报错(取决于严格模式)const user2 = new User({ name:
前端老铁别再瞎改state了!Immutable.js让你数据稳如老狗(附避坑指
前端老铁别再瞎改state了!Immutable.js让你数据稳如老狗(附避坑指南)
说实话,我刚开始写React那会儿,真是被state这玩意儿折腾得够呛。你懂的,就是那种明明代码看着没问题,界面死活不更新,或者更诡异的是——数据明明改了,但日志打印出来跟没改一样。后来我才慢慢悟了,这哪是React的锅啊,分明是咱JavaScript里那个"引用赋值"在搞事情。
为啥你总在React里搞出莫名其妙的bug?
这事儿得从JavaScript的基本功说起。咱们平时写代码,对象和数组都是引用类型,啥意思呢?就是说当你写 const newState = oldState 的时候,你以为newState是个全新的对象,实际上它就是个指针,跟oldState指着同一块内存呢。
来,看个真实的翻车现场:
// 你以为的安全操作,实际上是个大坑
const handleUpdate = () => {
const newState = state; // 兄弟,这只是个引用啊!
newState.user.name = '张三'; // 你以为是改newState,实际上state也被改了
setState(newState); // React一看:引用没变?那我不更新
};
看到没?React的setState(或者useState的setter)判断要不要重新渲染,默认就是浅比较。你传给它的newState和state在内存里是同一个地址,React直接判定"没变化",页面当然不更新。更坑的是,有时候你确实触发了更新,但因为其他地方也引用了这个对象,那些地方的数据也跟着变了——这就是传说中的"副作用",debug的时候能让你怀疑人生。
还有一种更隐蔽的情况,就是在Redux里。你reducer里要是直接改state,Redux-devtools都检测不到变化,时间旅行调试直接失效。我就见过有同事在reducer里写 state.list.push(newItem),然后回来问我为啥Redux不工作…兄弟,你这是在mutation的边缘疯狂试探啊。
Mutable数据结构到底有多坑?
咱再多说几句这个mutable的坑。在复杂应用里,数据往往是嵌套的,比如用户信息里套着订单列表,订单里又套着商品详情。这种时候你要是想复制一份数据来改,用展开运算符都费劲:
// 这代码看着就累,层级深了直接写哭
const newState = {
...state,
user: {
...state.user,
profile: {
...state.user.profile,
address: {
...state.user.profile.address,
city: '北京' // 我就想改个城市,写了这么多层...
}
}
}
};
这还只是个四层嵌套,要是遇到表格数据、树形结构,这代码能写出颈椎病来。而且手写的展开运算符特别容易漏层级,漏了就又变成浅拷贝,bug找上门只是时间问题。
还有个性能问题。你想啊,每次都要深拷贝整个对象树,数据量大的时候那不得卡死?而且React的shouldComponentUpdate或者React.memo,它怎么判断数据变没变?如果每次都要深度遍历比较,那性能直接爆炸。所以immutable的思路就很有诱惑力了——如果数据不可变,那比较两个数据是否相等就简单了,直接 oldData === newData,O(1)复杂度,美滋滋。
Immutable.js真能救你于水火?
说实话,我第一次听说Immutable.js的时候,内心是抗拒的。又要学个新库?又要搞一堆新API?我原生JS还没玩明白呢。但真被mutable数据坑了几次之后,我还是屈服了。用了一段时间之后,真香。
它解决的核心问题就是:让你能像操作普通对象数组那样操作数据,但底层保证每次操作都返回全新的数据结构,而且共享未修改的部分(结构共享),性能还不差。这就好比给你一辆车,开着跟普通车一样,但自带防弹功能,还不用你额外操心。
Immutable.js是个啥玩意儿
别被名字吓到,它其实就是个"数据防篡改工具箱"。你想啊,immutable翻译成"不可变",听起来挺高大上的,实际上就是个规矩:数据一旦创建就不能改,要改就创建新的。这跟咱们平时写代码的习惯确实反着来,但用习惯了之后,你会发现思维都变清晰了。
核心思想:每次改数据都给你整个新的,旧的纹丝不动
Immutable.js底层用的是一种叫"持久化数据结构"(Persistent Data Structure)的东西,配上"结构共享"(Structural Sharing)的优化。啥意思呢?就是说当你修改一个Map或者List的时候,它不会把整个数据结构复制一遍,而是只复制修改的那条路径上的节点,其他的都共享。
画个图你就明白了(虽然我没图,但你想象一下):假设你有个大树形结构,改一个叶子节点,Immutable.js只会重新创建从根到那个叶子的路径上的节点,其他的分支还是指向原来的内存地址。这样既保证了不可变性,又不会浪费内存和性能。
跟原生JS对象、数组说拜拜,换上Map、List这些新面孔
这是Immutable.js最让人不习惯的地方。你不能直接 obj.key = value 这样赋值了,也不能 arr.push(item) 这样添加元素。你得用 map.set('key', value) 和 list.push(item),而且这些方法都返回新的实例,原来的不动。
刚开始确实烦,感觉写个代码啰里啰嗦的。但习惯了之后,你会发现这种"显式"的操作方式反而让数据流更清晰。而且Immutable.js提供的API比原生JS丰富多了,很多复杂操作一行代码就搞定,后面我会详细说。
手把手带你玩转Immutable.js
好了,废话不多说,咱们直接上手。先从最基础的安装开始,然后一个个API过,保证你看完就能拿去用。
从安装到基本API:Map、List、Set怎么用才不翻车
安装很简单,npm或者yarn都行:
npm install immutable
# 或者
yarn add immutable
然后引入,建议这样写:
import { Map, List, Set, fromJS, toJS } from 'immutable';
Map:对象的专业替代品
Map就是用来替代普通对象的,但功能强大多了:
// 创建一个Map,可以传普通对象,也可以传数组
const user = Map({
name: '李四',
age: 25,
hobbies: List(['coding', 'gaming']) // 嵌套List也没问题
});
// 获取值,用get,支持链式调用
console.log(user.get('name')); // '李四'
console.log(user.getIn(['hobbies', 0])); // 'coding',获取嵌套值
// 设置值,返回新的Map,原Map不变
const newUser = user.set('age', 26);
console.log(user.get('age')); // 还是25
console.log(newUser.get('age')); // 26
// 批量设置,用merge
const updatedUser = user.merge({
age: 26,
city: '上海'
});
// 删除值
const userWithoutAge = user.delete('age');
// 判断是否存在某个key
console.log(user.has('name')); // true
// 获取所有keys或values
console.log(user.keySeq().toArray()); // ['name', 'age', 'hobbies']
console.log(user.valueSeq().toArray()); // ['李四', 25, List]
看到没?API设计得很直观,就是方法名跟原生JS有些区别,适应几天就好了。
List:数组的升级版
List替代数组,但 immutable 版本的:
// 创建List,可以传数组
const numbers = List([1, 2, 3, 4, 5]);
// 添加元素,push返回新List,原List不变
const moreNumbers = numbers.push(6);
console.log(numbers.size); // 5,原List还是5个
console.log(moreNumbers.size); // 6
// 在头部添加,用unshift
const prefixed = numbers.unshift(0);
// 插入到指定位置
const inserted = numbers.insert(2, 99); // 在索引2处插入99
// 删除,用delete(注意不是splice)
const removed = numbers.delete(2); // 删除索引2的元素
// 切片,跟数组的slice一样
const sliced = numbers.slice(1, 3); // [2, 3]
// 查找索引
const index = numbers.indexOf(3); // 2
// 包含判断
const hasThree = numbers.includes(3); // true
// 排序、反转、过滤、映射,都有,而且都返回新List
const doubled = numbers.map(n => n * 2);
const evens = numbers.filter(n => n % 2 === 0);
const sorted = List([3, 1, 4, 1, 5]).sort();
Set:去重神器
Set就是集合,自动去重:
const set1 = Set([1, 2, 3, 3, 3]);
console.log(set1.toArray()); // [1, 2, 3],重复的3被去掉了
// 并集、交集、差集,数学课代表狂喜
const set2 = Set([2, 3, 4]);
const union = set1.union(set2); // Set [1, 2, 3, 4]
const intersect = set1.intersect(set2); // Set [2, 3]
const subtract = set1.subtract(set2); // Set [1]
嵌套数据更新?别慌,updateIn和setIn来救场
这是Immutable.js最实用的功能之一。前面说了,深拷贝嵌套对象很麻烦,但在Immutable.js里,一行代码搞定:
// 假设有个复杂的嵌套结构
const data = fromJS({
company: {
departments: [
{
name: '技术部',
employees: [
{ name: '王五', level: 'P5' },
{ name: '赵六', level: 'P6' }
]
}
]
}
});
// 场景:把技术部第一个员工的level改成P6
// 原生JS写法要疯,Immutable.js这样写:
const newData = data.setIn(
['company', 'departments', 0, 'employees', 0, 'level'],
'P6'
);
// 更复杂的场景:给这个员工涨薪,基于当前薪资计算
const newData2 = data.updateIn(
['company', 'departments', 0, 'employees', 0, 'salary'],
(salary = 10000) => salary * 1.2 // 涨20%,如果salary不存在默认10000
);
// 在数组中间插入元素?用updateIn配合update
const newData3 = data.updateIn(
['company', 'departments', 0, 'employees'],
employees => employees.insert(1, Map({ name: '新来的', level: 'P4' }))
);
看到 setIn 和 updateIn 的威力了吧?路径用数组表示,想多深就多深。setIn 是直接设置值,updateIn 是拿到当前值,经过你的函数处理后再设置,适合那种基于旧值计算新值的场景。
还有个 getIn 用来取值,前面示例里用过了,这三个方法配合起来,处理嵌套数据简直不要太爽。
深度比较变简单了?是的,===就能判断内容是否一样
这是immutable数据最爽的地方之一。因为每次修改都返回新实例,所以比较两个数据是否相等,直接用 === 就行了,不需要深度遍历:
const map1 = Map({ a: 1, b: 2 });
const map2 = map1.set('b', 2); // 设置相同的值
const map3 = map1.set('b', 3); // 设置不同的值
console.log(map1 === map2); // true!因为值没变,Immutable.js会返回原实例
console.log(map1 === map3); // false,值变了,返回新实例
// 这在React优化里太好用了
const MyComponent = React.memo(({ data }) => {
// 如果data是Immutable对象,直接比较引用就行
console.log('渲染了');
return <div>{data.get('name')}</div>;
});
// 父组件里
const [user, setUser] = useState(Map({ name: '张三', age: 20 }));
const handleClick = () => {
setUser(user.set('age', 20)); // 设置相同的值
};
// 点多少次按钮,MyComponent都不会重新渲染,因为user引用没变
这个特性在shouldComponentUpdate或者React.memo里简直是神器,性能优化零成本。
toJS()和fromJS()这俩双胞胎,什么时候用哪个?
这俩方法是你跟普通JavaScript世界打交道的桥梁,必须搞清楚。
fromJS:JS对象 → Immutable对象
当你从后端拿到JSON数据,或者要处理现有的普通对象时,用它:
// 后端返回的数据
const apiResponse = {
users: [
{ id: 1, name: '张三' },
{ id: 2, name: '李四' }
],
total: 100
};
// 转成Immutable
const immutableData = fromJS(apiResponse);
// 现在你可以用Immutable的各种方法了
const firstUser = immutableData.getIn(['users', 0]);
const newData = immutableData.set('total', 101);
注意,fromJS 是深度转换,会把所有嵌套的对象和数组都转成Map和List。如果你只想转一层,用 Map(apiResponse) 或 List(apiResponse)。
toJS:Immutable对象 → 普通JS对象
当你要把数据传给不支持Immutable的库(比如一些图表库、表单库),或者要提交给后端时,用它:
// 假设你处理完数据,要传给一个普通的JS库
const immutableConfig = Map({
width: 100,
height: 200,
colors: List(['red', 'blue'])
});
// 转回去
const plainConfig = immutableConfig.toJS();
console.log(plainConfig); // { width: 100, height: 200, colors: ['red', 'blue'] }
// 在React组件里渲染时,如果你用了Immutable存state,记得取值的时候转一下
// 或者在render里转,或者提前转好存起来
const UserList = ({ users }) => {
// users是Immutable List
return (
<ul>
{users.toJS().map(user => (
<li key={user.id}>{user.name}</li>
))}
</ul>
);
};
重要警告:toJS() 每次调用都会返回一个全新的普通对象,即使Immutable数据没变。这意味着如果你在React组件里直接在render里调用 toJS(),会导致每次渲染都是新的引用,优化全白费。所以要么在useMemo里转,要么在数据进入组件前就转好。
Immutable.js香在哪,又臭在哪
用了这么久,咱们客观评价一下,不吹不黑。
优点拉满:性能优化有门道、状态追踪不再玄学、避免意外副作用
性能优化真的香
前面说了,immutable数据用 === 比较就行,这让React的性能优化变得简单。在Redux里,selector用reselect写起来也更高效,因为比较成本极低。而且Immutable.js的结构共享机制,让大数据量的更新也不会卡,它不会真的复制整个对象树。
状态追踪清清楚楚
因为数据不可变,每次变化都是新对象,你在Redux DevTools里看时间旅行,或者在console里打印日志,不会遇到那种"我明明打印了,怎么值变了"的诡异情况。数据流向变得特别清晰,从哪来,到哪去,中间经过哪些变换,一目了然。
副作用?不存在的
函数式编程的核心优势,纯函数好测试、好推理。你的reducer、你的数据处理函数,输入确定输出就确定,不用担心哪个角落偷偷改了全局状态。团队开发的时候,这点尤其重要,能减少很多"谁改了我的数据"的撕逼。
缺点也扎心:学习曲线有点陡、调试时控制台看懵了、bundle体积悄悄涨
学习成本确实存在
团队里来了新人,得先培训一波Immutable.js的API,不能上来就写。而且API跟原生JS有差异,经常有人写着写着就 obj.key = value 了,然后问为啥不更新。还有就是跟TypeScript配合的时候,类型定义要写对,不然一堆红线看着头疼。
调试体验确实拉胯
你在控制台 console.log 一个Immutable对象,看到的是 Map { "key": "value" } 这种,或者更糟的是 t.Map{},展开看结构也不直观。解决方案是装个浏览器插件(Immutable.js Object Formatter),或者在打印前 .toJS(),但总归是麻烦。
// 这样看很难受
console.log(immutableMap); // Map { size: 2, _root:... }
// 这样看舒服多了,但别忘了生产环境别这么干
console.log(immutableMap.toJS()); // { name: '张三', age: 20 }
// 或者用这个技巧,只在开发环境转
const log = (label, immutableData) => {
if (process.env.NODE_ENV === 'development') {
console.log(label, immutableData.toJS());
}
};
包体积确实大了点
Immutable.js压缩后还有几十KB,对于追求极致首屏速度的项目,可能得掂量掂量。不过现在都有tree shaking,按需引入能小不少。或者你可以考虑用immer,那个更轻量,但Immutable.js的功能确实更强大。
跟React/Vue配合起来爽不爽?Redux用户狂喜,但Vue党可能觉得多此一举
React + Redux:绝配
这是Immutable.js最经典的战场。Redux要求reducer纯函数、不可变数据,Immutable.js完美契合。而且React的优化机制(shouldComponentUpdate、React.memo)配合Immutable的引用比较,简直是天作之合。很多大型React项目(比如Facebook自己的一些产品)都用这套组合拳。
Vue:可能没必要
Vue的响应式系统本身就是基于数据劫持的,它追踪的是数据的变化,而不是引用。你在Vue里直接改对象属性,Vue能检测到的。所以Vue官方也不推荐用Immutable.js,觉得多此一举。当然,如果你跟React项目共享代码,或者就是喜欢immutable的编程风格,也可以用,但确实不是必须的。
MobX:看情况
MobX也是响应式的,跟Vue类似,但MobX也支持observable的map和array。用不用Immutable.js看团队喜好,但MobX本身的数据结构已经是响应式的了,再加一层Immutable可能有点冗余。
真实项目里怎么用才不翻车
理论说了一堆,咱们来点实战的。我在几个项目里用过Immutable.js,总结了一些经验。
状态管理场景:Redux + Immutable.js 经典组合拳
这是最常见的用法。整个Redux的state树都用Immutable对象存,reducer里全部用Immutable的API操作:
// store.js
import { createStore } from 'redux';
import { Map, fromJS } from 'immutable';
import rootReducer from './reducers';
const initialState = fromJS({
user: null,
posts: [],
ui: {
loading: false,
error: null
}
});
const store = createStore(rootReducer, initialState);
// reducers/user.js
import { Map } from 'immutable';
const initialUserState = Map({
profile: null,
preferences: Map({
theme: 'light',
language: 'zh-CN'
})
});
export default function userReducer(state = initialUserState, action) {
switch (action.type) {
case 'SET_USER_PROFILE':
return state.set('profile', Map(action.payload));
case 'UPDATE_PREFERENCE':
// 更新嵌套属性,用setIn
return state.setIn(['preferences', action.key], action.value);
case 'RESET_USER':
return initialUserState;
default:
return state;
}
}
// selectors.js - 配合reselect使用
import { createSelector } from 'reselect';
const getUserState = state => state.get('user');
const getPostsState = state => state.get('posts');
export const getUserProfile = createSelector(
[getUserState],
userState => userState.get('profile')
);
export const getUserTheme = createSelector(
[getUserState],
userState => userState.getIn(['preferences', 'theme'])
);
// 在组件里使用
import { useSelector } from 'react-redux';
const UserProfile = () => {
// 用selector取值,已经是immutable对象了
const profile = useSelector(getUserProfile);
// 渲染的时候要转JS,或者直接用get
return (
<div>
<h1>{profile?.get('name')}</h1>
<p>{profile?.get('email')}</p>
</div>
);
};
这套组合用了好几年,稳得很。就是写selector的时候要注意,返回的最好是primitive值(string、number这种),这样在组件里用的时候更方便。
表单处理、表格编辑这类高频更新场景实测效果
表单数据通常结构复杂,而且更新频繁,用Immutable.js管理很合适:
// 一个复杂的表单状态
const [formData, setFormData] = useState(fromJS({
basicInfo: {
name: '',
email: '',
phone: ''
},
address: {
province: '',
city: '',
detail: ''
},
items: [] // 动态添加的表单项
}));
// 处理嵌套字段变化
const handleBasicChange = (field, value) => {
setFormData(prev => prev.setIn(['basicInfo', field], value));
};
// 处理动态数组
const addItem = () => {
setFormData(prev => prev.updateIn(['items'], items =>
items.push(Map({ name: '', quantity: 1, price: 0 }))
));
};
const updateItem = (index, field, value) => {
setFormData(prev => prev.setIn(['items', index, field], value));
};
const removeItem = (index) => {
setFormData(prev => prev.updateIn(['items'], items => items.delete(index)));
};
// 提交前验证并转JS
const handleSubmit = () => {
const plainData = formData.toJS();
// 验证逻辑...
api.submitForm(plainData);
};
表格编辑也是类似,特别是那种可以行内编辑、拖拽排序的表格,Immutable.js的API让数据处理变得简单很多。比如拖拽排序,用 insert 和 delete 组合一下就行,不用自己写一堆splice逻辑。
别一上来就全量替换,先从局部复杂状态试试水
如果你是个Immutable.js新手,别一上来就把整个项目的state都换成Immutable。建议先从那些让你头疼的复杂状态开始,比如:
- 深层嵌套的配置对象
- 需要频繁CRUD的列表数据
- 多组件共享的、容易出副作用的状态
局部试用没问题了,再考虑全面替换。这样风险小,团队也有时间适应。
遇到问题别砸键盘,排查思路在这
用Immutable.js这些年,我也踩过不少坑,总结一些常见问题。
为啥组件不更新?八成是你忘了toJS或者shouldComponentUpdate写错了
最常见的情况:
// 错误示范1:在组件里直接比较immutable对象
const MyComponent = ({ data }) => {
useEffect(() => {
console.log('data变了');
}, [data]); // 如果data是immutable对象,这样比较的是引用,可能有问题
};
// 错误示范2:shouldComponentUpdate里直接比较
class MyComponent extends React.Component {
shouldComponentUpdate(nextProps) {
return this.props.data !== nextProps.data; // 如果data是plain object,这比较的是引用
}
}
// 正确做法:确保比较的是immutable对象,或者转成primitive值
const MyComponent = ({ data }) => {
// 如果data是immutable,直接用
const name = data.get('name');
useEffect(() => {
console.log('name变了');
}, [name]); // 依赖primitive值
};
还有一种情况是,你在父组件里把immutable对象转成了JS对象传给子组件,但转的时候没用useMemo,导致每次渲染都是新对象,子组件优化失效:
// 错误:每次渲染都生成新对象
const Parent = () => {
const data = useSelector(state => state.get('data'));
return <Child data={data.toJS()} />; // 每次toJS都返回新对象
};
// 正确:缓存转换结果
const Parent = () => {
const data = useSelector(state => state.get('data'));
const plainData = useMemo(() => data.toJS(), [data]);
return <Child data={plainData} />;
};
内存爆了?小心无限嵌套或忘记转换回普通对象
Immutable.js本身有结构共享,内存管理挺好的,但如果你搞出循环引用,或者无限嵌套的数据结构,那也会炸。还有就是,如果你把immutable对象存到了全局变量、localStorage,或者传给了Web Worker,记得先 toJS(),不然序列化的时候会出问题。
控制台打印全是t.Map{}?装个浏览器插件或者console.log前.toJS()一下
前面说过了,这是调试体验问题。建议团队统一装Chrome插件"Immutable.js Object Formatter",然后在代码规范里约定:开发环境的console.log统一用包装函数,自动转JS。
老司机私藏技巧大放送
最后分享一些我积累的技巧,让你用得更爽。
用Record定义结构化数据,比Map更安全还带默认值
Record是Map的"严格版",你定义好结构后,不能随意添加新字段,还有默认值:
import { Record } from 'immutable';
// 定义一个User结构
const User = Record({
id: null,
name: '',
email: '',
age: 0,
isActive: true
});
// 创建实例
const user1 = new User({ name: '张三', email: 'zhangsan@example.com' });
console.log(user1.get('name')); // '张三'
console.log(user1.get('age')); // 0,默认值
// 不能随意加字段,会忽略或者报错(取决于严格模式)
const user2 = new User({ name: '李四', unknownField: 'test' });
console.log(user2.get('unknownField')); // undefined
// 设置值,返回新的Record
const user3 = user1.set('age', 25);
// 可以用get方法直接访问属性(需要配置)
// 或者用destructuring,但要先toJS
Record的好处是类型安全,团队协作的时候,防止有人手滑写错字段名。配合TypeScript用更爽。
结合immer?别混着用!除非你想给自己加难度
immer是另一个实现immutable的库,语法更贴近原生JS(用proxy实现),体积也更小。但我不建议在一个项目里混用Immutable.js和immer,除非你在做渐进式迁移。两个库的API风格完全不同,团队维护成本翻倍。选一个,坚持到底。
TypeScript怎么配?类型提示搞对了开发效率翻倍
Immutable.js有官方的类型定义,但用起来有点啰嗦:
import { Map, List, fromJS } from 'immutable';
// 定义类型
interface User {
name: string;
age: number;
}
// Map的类型要这样写
const userMap: Map<string, any> = Map({ name: '张三', age: 20 });
// 更好的方式是用泛型(如果确定结构)
const userMap2 = Map<string, string | number>({ name: '张三', age: 20 });
// 从JS对象转的时候,类型推断可能失效,需要手动指定
const data = fromJS({ users: [] }) as Map<string, List<any>>;
// 或者定义整个state的类型
interface AppState {
user: Map<string, any>;
posts: List<Map<string, any>>;
}
const initialState: AppState = {
user: Map(),
posts: List()
};
TypeScript配置好了,IDE提示很爽,但前期写类型定义确实费时间。建议用Record代替Map,类型定义会更清晰。
懒加载+按需引入,别让Immutable.js拖慢首屏速度
如果你只用了Immutable.js的一小部分功能,可以按需引入:
// 别这样,全引入了
import Immutable from 'immutable';
// 这样,只引入需要的
import { Map, List } from 'immutable';
// 或者更狠的,用babel-plugin-lodash那种思路,只引入具体方法
// 不过Immutable.js的tree shaking支持还行,按需引入模块就够了
对于不是首屏必须的逻辑,可以用React.lazy或者动态import,把Immutable.js的代码分割出去:
// 假设这个组件用了很重的Immutable操作
const HeavyDataProcessor = React.lazy(() => import('./HeavyDataProcessor'));
// 在需要的地方
<Suspense fallback={<Loading />}>
<HeavyDataProcessor />
</Suspense>
好了,差不多就这些。Immutable.js这玩意儿,刚开始用确实觉得麻烦,但习惯了之后,你会发现数据管理变得特别踏实。特别是大型项目,团队人多的时候,immutable的数据流能避免很多低级错误。当然,小项目或者Vue项目,可能确实没必要上这么重的方案,immer或者干脆手写深拷贝可能更合适。
反正工具就是工具,没有银弹,只有适不适合。希望这篇能让你少踩几个坑,写代码的时候心里更有底。有啥问题,咱们评论区(哦不对,咱们私下)再聊。

更多推荐



所有评论(0)