事情的起因是这样的,有一个粉丝朋友跟我述说了她的焦虑:都 2024 年了,她的团队还在用 jQuery 开发项目,她觉得自己距离 React、距离 Vue 好遥远。觉得自己是被时代抛弃的弃子,她目前的状态就是每天都活在极度的焦虑当中,每次听到有人说行情不好,或者哪哪家公司又在裁员,都感觉心惊肉跳。
为了从根上解决她的焦虑,于是就有了这个标题。我知道看到这个标题,很多人第一反应是不理解,jQuery 不就是远古时代的产物吗,不都已经被淘汰了吗?它能比 React/Vue 更好?这不会是一篇标题党的文章吧?
但,我要非常明确的是,这不是标题党,而是在说一个客观事实。接下来,我来给大家分析一下,为什么 jQuery 比 React/Vue 更好。
一、技术选型的核心目的
作为一个前端开发,在搞技术选型的时候,一定一定要记住,我们有两个核心目标,一个是能够更高效更舒适的开发项目,而另外一个,就是要在开发项目的过程中,凝聚出属于你自己的核心竞争力。第一个目标更多的是偏向于团队,第二个目标更多的是要偏向于个人,你想要有一个持续的,健康的职业生涯,这两者缺一不可。只是说,第二个目标只能憋在心里,不能广而告之。
然而事实上,许多人,不管是主动选择,还是被动选择,往往只关注了第一个目标,而忽略了第二个目标。有的人是憋着不说,有的人是压根想不到。这就导致了,这些想不到的很多人,用 Vue/React 用久了,会感觉自己变成了一个废物,演变成一年经验用七年。
这种情况在 Vue 使用者的身上会体现得更加明显。
当然,我不是在说所有人,而是大多数人
所以有的人虽然没有刚才那个同学那种焦虑情绪,也很熟练的在使用 React/Vue,但就是想要获得一个 offer 还是非常困难。因为他们虽然已经熟练使用 React/Vue 开发页面,但这就是一个普通的页面仔啊,工作了 five 年,没有凝聚出核心竞争力,成了一个废物。
因此在做技术选型的考虑上,在我的选择序列里,React 永远都要比 Vue 更值得选择,只因为 React 离原生 JavaScript 更近,没有创造更多的语法,没有那么多黑箱操作,自由度更高。这让我有更多的机会在开发项目的过程中,做到提高开发效率的同时,还能兼顾自己核心竞争力的提升。
而在这两个点的权衡上,jQuery 实际上可以做得更好。
二、我们陷入了 React/Vue 的骗局
React/Vue 最核心的东西,就是组件化。能借助 webpack/vite,在 JS 模块中,把其他静态资源当成模块引入,从而在大型项目中,提高文件结构的可读性和易用性。并利用数据驱动 UI 的方式,在一定程度上简化了开发负担。
但是,我们在学习 React/Vue 时,都被他们骗了。React/Vue 说,我们要构建一个大型项目,需要一个全局状态管理器,我们应该把所有的状态都放到顶层的 store 里。于是 redux 成为了入门 React 必学的技术栈。然而事实上,全局状态管理,我觉得是一种弱智的,简单粗暴的解决方案,他能够让你在思考组件拆分的时候,完全不考虑数据的归属问题,反正随便放在哪里都能轻松访问。
以致于,大部分的前端开发,都是被这种骗局培养成了高效低能的开发者,不管你是用 React,还是用 Vue,有可能都没有逃过这个骗局。当你还在和别人争论 React、Vue 谁会淘汰谁的问题时,你可能还没有发现,这两个家伙构建了一个非常坚固的信息茧房,把所有的前端都圈在里面,然后合伙把你淘汰掉。
所以我认真的思考了一下,真的有很多数据需要全局共享吗?
所以在很多年前,当我经验逐渐丰富起来的时候,我在其他客户端开发解决方案中,见识了更多的开发模式,然后我发现了这个骗局。我们大多数项目,并不需要全局状态管理。甚至也不需要逻辑那么笨重的数据驱动。
三、数据驱动的本质
当全局状态管理没那么有必要的时候,也就意味着,我们的项目数据结构不会那么复杂,所以数据驱动 UI 这个事情,带来的好处,就显得非常有限了。
在 React 中,你这样写:
function Hello() {
const [year, setYear] = useState(2024)
function clickHandler() {
setYear(year + 1)
}
return (
hello {year}
++
)
}
当我们利用 jQuery 如何写呢,看一下代码:
hello {{:year}}
++
var data = {
year: 2014
};
function hello() {
data.year += 1
template.link("#result", data);
}
var template = $.templates("#temp");
template.link("#result", data);
注意看,我们其实也可以利用了 jQuery 生态中的模板语言来代替 DOM 操作,达到类似的目的。我们可以相对清晰的知道当我要改变一个数据时,有两个事情要完成,一个是改变数据,一个是重新修改 UI.
我们也可以缩小修改的范围,从而达到最极限的性能,自由度非常高。
function hello() {
data.year += 1
$('#label').text(`hello ${data.year}`)
}
在复杂度和性能之间,我们可以自由的做出取舍。我们完全没有必要在所有场景,都去花费那么大的代价去考虑如何将数据与 UI 绑定在一起。不管是 Vue 的依赖收集,还是 React 的 diff,在这上面都花费了大量的心思,增加了巨大的心智负担,关键逻辑还变成了黑箱操作。
四、React 的 返祖现象
事实上,熟悉 React 新官网的朋友应该知道,React 已经开始出现返祖现象了。也就是官方文档把 useEffect 定性为一种逃脱方案。当我们发生点击事件时,如果需要修改其他的逻辑,新官方文档建议我们不要去修改状态,而是直接把逻辑写在回调函数里
// 官方文档不推荐
useEffect(() => {
loading && api().then(() => {})
}, [loading])
function clickHandler() {
setLoading(true)
}
// 官方文档推荐
function clickHandler() {
setLoading(true)
api().then(() => {})
}
我为什么要称这种方式为返祖现象呢,因为你熟悉 jQuery 的使用的话,你就会发现这本身是再正常不过的逻辑了,但是新的官方文档确要花费大量的篇幅去解释为什么应该这样做。这在我看来是非常诡异的事情。
然后呢,我又要花大量的心思去解释我为什么不认同官方文档的这种观点。
五、当我们在 jQuery 中能自定义组件时
我们要达成的一个共识就是,单向数据流是一个被包装出来的高大上概念。说白了就是函数的嵌套执行。
注意看这段代码:
function Parent(data) {
const {pname, ...otherName} = data
return (
`${pname}${Children(otherName)}`
)
}
function Children(name) {
const {cname, ...otherName} = name
return (
`${cname}${Grandson(otherName)}`
)
}
function Grandson(name) {
return (
`${name.gname}`
)
}
是不是感觉很熟悉,很像 React。当我执行 Parent() 的时候,所有的子元素和孙子元素都会重新执行,从而数据就有了流向。当我要修改数据的时候只需要。
function click() {
data.gname = 'TOM'
parent(data)
}
这就是数据驱动 UI。这就是单向数据流。
但是实际上,这不是 React 组件,他只是普通的函数,返回了一个字符串而已。因此,要非常注意的是,他也满足了单向数据流的规则。但是他的写法也只是函数里执行函数。
如果你要改变 data 时,只需要重新执行一下 Parent(data) 。这样,我们的每一个子组件,都会重新执行。所以我想说的是,构建一个自定义组件确实太简单了,我们当然也可以在 jQuery 的生态里,基于模板自定义组件。
function renderBoldP(value) {
return "" + value + "
";
}
$.views.tags("boldp", renderBoldP);
React 的语法跟我刚才的写法非常相似。但是,React 最大的问题就是,嵌套层级太多了,以致于我们在执行顶层组件 Parent()
时,成本偏高。在 jQuery 中,就可以完全不用担心这个问题,我们可以自由选择层级,而不必把嵌套层级扩大到整个项目。有可能你只是想要修改一个小小的地方而已。
灵活,就是 jQuery 最大的优势。
事实上,当你要研发大型高性能的前端项目时,React 和 jQuery 最终都会殊途同归。我们也会想办法在 React 中放弃自顶向下的 diff,然后把改动缩小在可控的范围里。但是在 React 中要做到这个事情需要非常深厚的功底,而在 jQuery 中却非常容易。因为我们并不需要去迁就庞杂的 diff 流程,只是简单的执行一个目标函数而已。
六、当你需要双向绑定时
当你想要在特定的场景里需要双向绑定时,jQuery 的生态里也有非常多的方案来支撑这个场景。
$("#changeObjects").on("click", function() {
$.observable(person).setProperty({
name: "newName",
address: {street: "New Street"},
phones: [{number: "123 123 1234"}, {number: "321 321 4321"}]
});
});
所以,当 jQuery 可以自定义组件,可以支持单向数据流,可以支持双向绑定,这不就齐活了吗?
七、趋势是什么
不要问未来的趋势是什么,问就是 jQuery。什么所谓的 Vue3,Solid,svelte,都不是最终形态,他们通通都在走向返祖的道路,未来的趋势就是 jQuery。所以如果你的团队里,还在使用 jQuery,正说明你们团队在领先世界,这是我内心最真实的想法。所以你不需要过于焦虑,你要做的事情只是把 jQuery 用好,用透,去利用 jQuery 的生态构建一套开发效率很高的架构出来,然后回过头来,你会发现,React/Vue 你只需要一天就能学会。
没有一个团队,会拒绝得了精通 jQuery 的人。因为你 jQuery 用得好,很大程度上能代表你原生能力相对会强一些,基础非常扎实。