(深度)开源框架/库的伟大与罪恶

7,146 阅读10分钟

前几天我发了一篇《是时候该换掉你的axios了》的文章引起了持续的热议,各位小伙伴也给出了自己的看法,本来只是随便一发的对比axios和alova优劣势的文章,但是却遭到了一些同学的质疑。

思想碰撞是我们进步的阶梯,我没有要贬低这些质疑的意思,相反,我要感谢他们(质疑者)推着我思考和前进,在这边我想对这些声音做出自己回应。

因为我从这些质疑声中看到了,很多人的代码思维都还停留在使用层面,而缺少更深度的理解和思考,这篇文章我想聊聊我对开源框架和库更深入的观点,从更深入的角度聊聊我为什么看好alova!!!

看完我保证你一定会有所收获的,没有的话你尽管喷。 还不知道alova的可以预先了解下:

alova的Github地址

alova官网

如果你不知道axios的话,直通车在这

本文主要讨论编程背后的东西,而不是某个技术点的解决方案,欢迎大家参与共同讨论!

省流目录

  1. react和vue的伟大
  2. (解答各位的质疑)alova的优势在究竟哪里
  3. (升华)开源框架罪恶史

react和vue的伟大

深有体会的是,开源框架和库已经实实在在地给我们带来了太多便利,在深入讨论alova前,我们先从来react和vue等大家最熟悉的UI框架说起。

这些UI框架究竟给我们带来了什么,过去的王者jquery可谓雄霸天下,jquery所解决的问题是提供一种更简单兼容性更好的dom操作api,让大家不需要关注兼容性问题,但对于新手来说,照样会把全部代码写在一个js文件中然后把代码乱放,来欣赏一段意大利泡面一样的jquery代码:

// ❌整个页面的js处理都集中在这里了
(function(win) {
    // 处理页头按钮点击事件
    $('#header .button').click(function() {
        // ...
    });

    // 处理页脚某个tag的鼠标移入事件
    $('#footer .tag').mouseenter(function() {
        // ...
    });

    // 处理主界面轮播图的事件
    $('#swiper .btn-left').mouseenter(function() {
        // ...
    });
    $('#swiper .btn-right').mouseenter(function() {
        // ...
    });
})(window);

项目以html、js、css为大模块进去分开,结构大概是这样的

WechatIMG684.jpeg

一团扭来扭去还弯弯曲曲的代码,可维护性直降负分,谁愿意接盘谁倒霉,随着项目逐渐复杂,每改动一处都像是一锤子锤在接盘侠的胸口上 —— 痛苦无比,只要团队里有新手,不管技术leader怎么制定模块拆分的规范都无法避免这种事的发生,最终接盘侠们也抛了盘……

为什么呢?

因为leader的规范只是写在文档上的意见型规范,你可以不遵守也能让项目跑起来。而react和vue等MVVM UI框架(以下简称UI框架)的优势就立即凸显出来了,这让它们很快把过去的王者jquery击败了,这些UI框架的最伟大之处就在于,它们提供了一种可维护性更高的代码组织模式 —— 组件化,并强制大家都使用这种方式来开发应用。组件化中,html、js、css以组件为最小单元聚合,组件之间相互解耦,大概是这样的

WechatIMG683.jpeg

它们形成了一种制约型规范,不遵守项目就无法跑起来,这种方式让天底下的前端开发者达成了统一的模块化共识,就像你家的电源插槽那样统一。

我觉得以上就是框架和库的区别!!!

这种制约型规范才能让组件库百花齐放,各种组件库开始诞生进一步为大家减少开发量。

这就是制约型规范的威力,可能你突然就懂了为什么要用husky了。

而使用数据驱动视图的MVVM模式,虽然相比于jq也是降维打击的存在,但我觉得它的重要性并没有组件化伟大。

(重要)制定规范的目的除了规范变量名和注释外,更重要的是在代码组织层面实现高聚合、低耦合, 不陌生吧,而且我觉得vue的SCF、指令相比react的jsx又进一步做到了强制型规范。

(解答各位的质疑)alova的优势在究竟哪里

如果你理解了上面的规范问题,我们再接着聊相比于react-queryswrvueuse的useFetch以及ahook的useRequest等,alova有什么特别之处?我很认同作者在《是时候该换掉你的axios了》中的评论:

1.png

在得知我还要再发一篇深度解析文后,在周一晚上,作者找到了我,聊了3个小时,他表达了不希望alova被我带偏了,那次聊过后也体会到了作者的思想,大概如下:

js库可以分为规范型js库和功能型js库

规范型js库

像react和vue等UI框架除了提供api外,还提供了组件化(制约型规范),很显然是规范型的库,即框架。

功能型js库

如axios、moment、lodash、jquery等都是属于功能型js库,它们更多的价值是提供便于调用的api,而react-query和swr等确实是使用use hook管理请求以及使用请求缓存的先驱者,但好像除了这些再没有规范层面的影子,因此更倾向于把它们归类到功能型js库中,贴上示例。

react-query 官方示例简析

function Example() {
  // 在每次useQuery中调用fetch或axios发送请求,耦合度较高
  // 需手动维护queryKey
  const { isLoading, error, data } = useQuery({
    queryKey: ['repoData'],
    queryFn: () =>
      fetch('https://api.github.com/repos/tannerlinsley/react-query').then(
        (res) => res.json(),
      ),
  })

  if (isLoading) return 'Loading...'
  if (error) return 'An error has occurred: ' + error.message
  return (
    <div>
      <h1>{data.name}</h1>
      <p>{data.description}</p>
      <strong>👀 {data.subscribers_count}</strong>{' '}
      <strong>✨ {data.stargazers_count}</strong>{' '}
      <strong>🍴 {data.forks_count}</strong>
    </div>
  )
}

swr 官方示例简析

// 耦合的方式请求
const fetcher = url => fetch(url).then(r => r.json())
function App () {
  const { data, error } = useSWR('/api/data', fetcher)
  // ...
}

// 解耦的方式
import useSWR, { SWRConfig } from 'swr'
function Dashboard () {
  const { data: events } = useSWR('/api/events')
  const { data: projects } = useSWR('/api/projects')
  const { data: user } = useSWR('/api/user', { refreshInterval: 0 }) // override
  // ...
}
 
function App () {
  return (
    <SWRConfig
      value={{
        refreshInterval: 3000,
        fetcher: (resource, init) => fetch(resource, init).then(res => res.json())
      }}
    >
      <Dashboard />
    </SWRConfig>
  )
}

我觉得vercel团队是有思考到这点的,只是把解耦的方式作为了一个可选项。

alova的不同之处

虽然alova相比于axios、react query、swr等前辈大咖,alova只能算是新秀中的新秀,其他库已经发展了5到10年了,作者好像也比较低调。但在我看来alova在解决思想上和前辈们相比会更加深入,也更加彻底。

alova的作者提出了一个叫 RSM规范(请求场景模型) 的模型,并让alova遵循这个模型来设计,官方示例代码如下:

import { createAlova } from 'alova';
import GlobalFetch from 'alova/GlobalFetch';
import VueHook from 'alova/vue';

const alovaInstance = createAlova({
    // 假设我们需要与这个域名的服务器交互
    baseURL: 'https://api.alovajs.org',

    // VueHook可以帮我们用vue的ref函数创建请求相关的,可以被Alova管理的状态
    statesHook: VueHook,

    // 请求适配器,我们推荐并提供了fetch请求适配器
    requestAdapter: GlobalFetch(),
    
    // 全局请求前钩子
    beforeRequest(method) {  
        // ... 
    },
    
    // 全局响应钩子
    responsed: {
        onSuccess(response, method) {
            // ...
        },
        onError(err) {
            // ...
        }
    }
});

// 创建一个Get实例,描述一次Get请求的信息
const todoListGetter = alovaInstance.Get('/todo/list', {
    // 请求头
    headers: {
        'Content-Type': 'application/json;charset=UTF-8'
    },
    // params参数将会以?的形式拼接在url后面
    params: {
        userId: 1
    },
    
    // 单独为这个请求设置缓存时间
    localCache: 50000,
    // 单独为这个请求开启请求共享
    shareRequest: true,
    // 数据转换
    transformData(result) {
        // ...
    },
    // ...
});

从以上示例中,可以看出alova的以下两个优势:

  1. alova以请求适配器、存储适配器、UI框架适配器的方式,将请求工具和请求方式进行了强制解耦,实现了低耦合,提高了代码的可移植性,而不是可选项。
  2. alova中的Method概念,用于实现RSM的请求行为,它也强制将请求相关的信息、请求行为相关的信息高度聚合在了Method中,实现了高聚合。

作者原话告诉我:alova提供了高聚合低耦合的代码组织方式,可以更大程度地管理请求相关的代码,随着项目越来越复杂,它的优势也就会慢慢显现出来,而且在编码方面也更加统一了。同时保持了高度了灵活性,供用户提供了足够的自定义空间。

因此,我会把alova放在介于规范型库和功能型库的中间位置

最后,据官网介绍,alova的目标是通过按业务场景来实现兼开发体验和用户体验为一体,而alova的高度灵活性就可以支持自定义策略,你可以封装了公司内部用,比如你的公司很喜欢用某种处理来优化性能,或者写成alova插件一样的自己的策略库,那alova刚好都可以提供支持。

说到这边,好像alova的use hook形式显得好像没那么重要了,这个就要问作者了🙄。不懂有没有解答大家的疑问。

(升华)开源框架罪恶史

react和vue都更关注UI层面,它们简化了UI层面的问题,但据我的理解,目前从js逻辑的层面却还是没有统一制约规范,或相关的js逻辑框架来解决这个问题,新手和高手写出的代码可维护性上还是有一定差距的。UI框架让事情简单化的同时,也养肥了一堆躺在UI框架的成绩上好吃懒做的人,这些人往往会有以下两个问题:

  1. 不再思考规范问题、模块之间的界限和协作,甚至都意识不到这些东西的存在,然后不分职责、把代码到处乱写,请想象有一只狗到处乱拉……,然后堆积成的叫什么山😎。
  2. 他们只享受上层成果,不再关注底层的东西,哪怕稍微底层一点的,因为确实用不着了,就像我们不再关注垃圾回收的问题(C语言里是需要手动回收的),更离谱的是,有些人连getElementByIdappendChildinsertBefore是干什么的,更别问他们nodetype有几种类型,fragment是什么,注释是不是一种node等等。

可想而知,尖端人才变少是有理由的,知道为什么高级前端难招了吧!!!当然也不乏一小部分喜欢钻研的同学,喜欢知其然知其所以然。

这好像是时代发展、科技进步带来的无法避免的副作用,它们为我们提供了强大且便利的工具外,也在慢慢摧残着这么一大群人,和平年代不需要那么多英雄。这让我又想到了《反脆弱》里的观点 —— 现代化是这个世界的脆弱推手。

插个题外话,毕竟react和vue接管的是UI层面的组件化规范,但在js逻辑层面的规范都还停留在文档上,还没有形成制约型规范,在这个层面新手朋友一样可以把它们写成意大利面,有兴趣的同学也可以开始思考一下了,有没有可能从js逻辑层面提出制约型规范,然后形成一个新的js框架,就像UI框架组件化规范一样,如果有人做到了,可否回来留言告诉我下😌😌😌

结尾

这篇文章并没有去解决一个具体问题,能看到这里的同学,看来都是学习和交流的同学。

那问题来了,你们理解的代码规范是怎样的呢?