微信号:frontshow

介绍:InfoQ大前端技术社群:囊括前端、移动、Node全栈一线技术,紧跟业界发展步伐。

Web前端框架:是解药还是毒药?

2018-06-30 21:01 无明 译
作者|Juan Carlos Arias Ambriz
译者|无明
编辑|覃云

要使用现代的前端框架,你需要下载开发环境和依赖,编译代码,然后在浏览器上运行。这个是好是坏?究竟是什么导致了这种不必要的复杂性?是因为我们构建的网站太复杂,还是因为框架本身就很复杂?

从 90 年代以来,Web 开发已经发生了巨大的变化,我们可以做到出非常接近原生应用的体验,而开发流程也变得与以前不一样。对于 Web 前端开发人员来说,那种只需打开记事本,输入几行代码,在浏览器中运行,然后上传到 FTP 文件夹的日子已经一去不复返。

过去的 Web 前端开发

我必须先说明这个显而易见的事实:世界已经不像 10 年前那样。唯一不变的是变化。那个时候,我们只有少数的几种浏览器,但是存在很多兼容性问题。我们现在有更多的浏览器,但更少的兼容性问题。为什么?因为 jQuery。jQuery 提供了一个标准的通用库来操作 DOM,无需操心它在每个浏览器以及同一浏览器不同版本上是如何运行的——兼容性问题在 2000 年代是开发者的噩梦。

现在的大部分浏览器都提供了标准的方式来操作 DOM,因此近年来对这种通用库的需求大大减少了。我们不再需要 jQuery,但仍然可以找到一些非常有用的插件依赖了 jQuery。换句话说,Web 框架可能不是必需的,但仍然很有用。这是大多数 Web 框架的共性,从 React、Angular、Vue 和 Ember,到样式模型(如 Bootstrap)。

为什么人们要使用框架?

使用 Web 开发框架有哪些好处,它们有什么独特的地方?

时间就是金钱。客户可能不会关心你使用的是哪个框架,他们只关心结果,而且越快越好。现成的框架让你从一开始就有一种进度感,而这恰恰是客户所希望的。此外,你开发得越快,赚的钱就越多,因为使用框架节省下来的时间可以用来做更多的项目。

社区的支持。在选择框架时,这是非常重要的一点——当你遇到问题时可以找谁帮忙?到了某个时候,你需要做一些框架本来不打算做的事情,或者框架不让你使用某些功能,这个时候就要求助社区。这个时候开发陷入了困境(特别是对于自由开发者来说),因为你现在处在一个虚拟的世界中,如果你是团队中唯一的前端开发人员,也就意味着你是唯一能够找到解决方案的人。但是,如果你使用的前端框架有强大的社区在支持,那么在世界另一端可能会出现另一个解决过相同问题的人,他们可以帮助到你。

美好的标准。你有没有注意到,当你在阅读自己写的代码时,是不是觉得很容易就看懂?或者至少比看其他人的代码更容易?你有自己的思考方式,有自己的命名和组织代码的方式。这就是标准。从你安装框架的那一刻起,它们就为我们提供了一种标准,引导你按照某种特定的方式思考和编码。你不需要花时间和团队一起制定标准,只需要遵循框架提供的标准就可以了,这让团队合作变得更加容易。如果你要查找某个函数,很容易就能找到它,因为你知道它一定存在于某个文件中。

当框架失效时

几年前,如果有人说,“我不使用框架,我看不到框架能够给我带来真正的好处”,那么肯定会有一伙人带着火把和叉子来到你家门口。但是今天,越来越多的人在问自己:“为什么我要使用框架?我真的需要它们吗?难道不使用框架就真的很难写好代码?“如果你是众多从来没有想过不使用框架编程的人当中的一员,那么我不得不告诉你一些残酷的事实。

“一刀切”的解决方案是一个谎言。你能够想象开发一个可以解决所有问题的软件是怎样的一种体验吗?这是 Web 开发框架的主要问题之一。如果项目有特殊的需求,我们倾向于通过添加库或插件来扩展框架。没有任何一个框架能够 100% 满足你的需求,没有任何一个框架提供的所有东西对你来说都是有用的。

加载太多用不到的代码会导致更多的延迟。另一个问题是,“一刀切”的思维方式导致代码效率低下。举个例子,jQuery 中的 $(‘sku-product’).html(’SKU 909090’); 最终需要被翻译成 document.getElementById(‘sku-production’).innerHTML ='SKU 909090’;。当然,像这样一两行的代码差异似乎不算什么,但在 React 中,为了渲染变化的页面元素,它需要创建整个 DOM 表示,并分析发生变化的部分。而如果从一开始就瞄准想要改变的内容是不是更容易?

你走过的杂草丛里藏着荆棘。你是否曾经遇到过这样的情况,当你尝试在框架中加入一个库时,发现这个版本的库与这个版本的框架不能放在一起使用?有时候为了让它们能够放在一起使用,需要花费很多的精力,还不如自己写代码把问题解决了。而且,由于你所使用的框架和库通常建立在其他框架和库之上,而这些框架和库可能存在不可预料的兼容性问题,所以问题可能会呈指数级增长,变得更加复杂,从而达到无法收拾的地步。

与时俱进也是一个问题。即使你只使用一个前端框架,当一个新的主要版本发布时,也可能会发生很大的变化,以至于你之前费劲写好的代码无法在新版本上运行。它们可能只是一些烦人的小变化,但你却不得不在大量文件上重写代码。跟上框架的更新步伐是件相当有挑战性的事情,但同样的,框架完全停止更新或无法跟上技术发展的趋势,也会带来很大的麻烦。AngularJS 和 Backbone 初始版本都是在 2010 年发布的,今天,Angular 已经成为推出了第 5 个大版本,但却没有人关注 Backbone。如果你把赌注压在错误的框架身上,可能会让公司陷入一个艰难的境地,最终不得不重新来过。

当你手里握着一把锤子,一切看起来都像钉子。如果你经常使用 Web 框架,可能就会遇到这种情况。比方说,你想要使用 Framework X 来构建一个类似 YouTube 这样的平台。你决定在这个平台上使用 Flash 播放器,尽管这在现在听起来很荒谬,但这是框架提供的,你就随它去了。框架本身提供了非常强烈的约束,例如,React 强制你以特定的方式使用 JSX。那么我们有其他选择吗?有是有,但有人用吗?虽然说这并不总是一件坏事,但如果你需要运行复杂的动画,可能只需要一个动画框架,而不是整个 React。有些人为了将一个节点添加到某个元素中而将 jQuery 添加到页面中,但实际上这样的操作可以直接在 JS 中用 document.getElementById(’id_of_node’).appendChild(node); 来完成。

Eval 邪恶,.innerHTML 狡猾

我想花点时间单独探讨这一点,因为我认为这是很多人“无框架不编程”的原因之一。大多数代码在往 DOM 中加入元素时,都会使用.innerHTML 属性注入一堆的 HTML。我们似乎都赞同使用 eval 来运行 JavaScript 代码是不好的,但在这里我想强调的是.innerHTML。当你将 HTML 代码作为纯字符串注入时,就无法引用任何新创建的节点。的确,你可能可以通过 getElementsByClassName 或给它们分配 id 来获取它们,但这样做并不实际,因为当你试图改变新创建节点的值时,需要再次渲染整个 HTML。

在刚开始编程时这很好,不需要太多经验就可以轻松完成很多简单的事情。但现代网站往往更像是应用程序,非常复杂——这意味着我们需要不断改变节点的值,如果你总是通过.innerHTML 重新渲染整个 HTML 结构,需要付出很高的成本。React 通过 DOM 影子有效地解决了这个问题,Angular 通过使用绑定在页面上修改显示值解决了这个问题。当然,通过跟踪创建的节点并将它们保存在变量中,在后续使用或更新它们,这样也可以很容易地解决这个问题。还有其他一些原因应该避免使用.innerHTML,如 http://www.grauw.nl/blog/entry/475。

关于“无框架不编程”的谬误

时间就是金钱。是的,这个在上面就已经提到过。很多人觉得如果他们不使用流行的框架,就会马上跌回到 90 年代,那个时候,<marquee>是每个人最喜欢的标签,Geocities 网站上闪动的 GIF 是一种时尚潮流,Alta Vista 是最受欢迎的搜索网站,点击计数器无处不在。

使用 Web 框架在刚开始似乎可以节省很多时间,但到了某个时候,就会转盈为亏。你需要花时间研究怎样才能让框架做哪些它们做不了的事情,如何往框架中添加额外的库让它们更好地发挥作用,然后你会发现,遵循框架标准所写的代码无法运行了,需要进行重写。而如果你没有使用框架,在刚开始时走得慢,但你会稳步前进。这两者的总体时间不会有太大的区别。

我的代码会比长城还要长。不使用框架就像去影院看电影,而不是订阅流媒体服务。虽然你无法即时看到你想看的数百部电影,但你也不必花钱购买那些你可能永远不会去看的电影。你可以只写必要的代码。

框架是否有用?当然有用,但通常没有必要。你编写的每行代码都有更多含义,因为你无需遵循框架的标准。使纯 JavaScript 似乎需要写更多的代码,因为创建 DOM 元素需要几行代码,包括创建元素、将它附加到 DOM,或者再添加一个样式类,而在 JSX 中一行代码就可以搞定。但是如果使用像 jQuery 或 React 这样的库,你会发现,纯 JS 的代码长度与它们非常接近。有时会更长,但有时也会更短。

没有必要重新发明轮子。这句话成了计算机专家的口头禅。这是事实——它不一定专指框架。例如,在 Web 应用程序使用 Ajax 发送和保存数据是很普遍的事情,不使用框架并不意味着需要每次重新编写重复的代码。你可以创建自己的库,也可以从其他人那里提取代码。代码库越小就越容易根据需求进行修改或调整,因此当你的项目有某些特定的需求时,它们会派上用场。相比被困在第三方库或框架的代码海洋中,修改一两百行代码似乎是件很幸福的事。

它只适用于小项目。这是一个非常普遍的谬误。目前,我正在负责一个系统,用于管理公司所有的线上内容,包括一个类似 Google Drive 的模块。无论有没有使用框架,我都会遇到非常类似的问题,这些差别可以忽略不计。但是,如果不使用框架,整个代码库会更小,更易于管理。

我想要证明

理论讨论告一段落,接下来让我们来看看现实世界中的例子。几天前,我需要展示商店的商标 logo。最初的代码使用了 jQuery,但它有一个问题,在使用 Mozilla 加载时,还没有上传 logo 的品牌显示的是一个损坏的图标。我们不能因为有些公司未上传 logo 就让商店页面看起来不完整,但问题是这个功能需要上线。

以下是 jQuery 代码:

var list_brand_names = ['amazon', 'apple', 'nokia'];
var img_out = '';
for (i=0;i<list_brand_names.length;i++) {
   var brandName = list_brand_names[i].toLowerCase();
  img_out += "<a href='/pages/" + brandName + "'><img src='images/" + brandName + "' /></a>";
}
jQuery("#brand-images").html(img_out);

我们不过多地讨论 jQuery 的优点和缺点,这段代码的问题是,我们没有引用新创建的图像。虽然我们可以通过不修改代码的方式解决这个问题,但先让我们借机看看如何在不使用任何第三方库的情况下完成此任务:

var brands = ['amazon', 'apple', 'nokia'];
var brand_images = document.getElementById("brand-images");
for (var iBrand = 0; iBrand < brands.length; iBrand++) {
  var link = document.createElement('a');
  link.setAttribute('href', '/pages/' + brands[iBrand]);
  link.style.display = 'none';
  brand_images.appendChild(link);

  var image = new Image();
  image.src = "images/" + brands[iBrand] + "png";
  image.onload = function(){
    this.parentNode.style.display = '';
  }
  link.appendChild(image);
}

最初的 jQuery 代码是六行,而纯 JS 解决方案是十二行。为了解决这个问题,先隐藏每个图像,直到它被加载,所以需要两倍的代码。然后让我们看看其他替代方案,jQuery 可以解决这个问题吗?

img_out += "<a href='/pages/" + brandName + "' style="display:none"><img src='images/" + brandName + "' onload="showImage(this)"/></a>";
function showImage(image){
  image.parentNode.style.display = "";
}

现在 jQuery 和纯 JS 之间只差了三行代码,但是在 jQuery 中,我们可以看到 img_out 那一行非常长,我们需要仔细看才知道它在做什么。通过使用节点函数来添加属性和函数可能会让代码更加冗长,但是每一行代码都更有更清晰、更精确的含义,在将来更易于阅读和维护。

让我们来看看 React:

function BrandLink(props) {
  var url = "images/" + props.brand + ".png";
  return (<a href="{props.brand}"><img src={url}/></a>);
}
class Brands extends React.Component {
  constructor() {
 super();
 this.state = {brands: ['amazon', 'apple', 'nokia']};
  }
  render() {
 const links = this.state.brands.map((step, move) => {
   return (<BrandLink brand={step} key={step}/>);
 });
 return (<div className="brands">{links}</div>);
  }
}
ReactDOM.render(<Brands />, document.getElementById("root"));

这段代码不会比纯 JS 的代码短。

对于不同的例子,结果都会有所不同。有时候,jQuery 代码会更短。有时候,React 会胜出。有时候,纯 JS 比其它两者的代码都短。我的目的不是要证明其中一个优于另一个,而是要证明,在代码长度方面,使用纯 JS 和使用框架没有显著区别。

结论

就像现实生活中的问题一样,没有什么是非黑即白的。不使用框架有可能是你的项目的最佳解决方案,但也可能是其他项目的噩梦。就像使用工具一样,关键的不只是学习如何使用它们,还要知道在什么时候使用它们更合适,以及它们都有哪些优点和缺点。在纯 JS 中进行编码就像使用框架一样,在感觉游刃有余之前,需要花点时间掌握它。

但至少对于我来说,关键的区别在于框架一直在变化,即使某个框架可以流行很长一段时间,不同版本之间也会发生巨大变化。纯 JS 将是一个更长期的选择,除非它被一些其他语言所取代。即便如此,从一种语言切换到另一种语言要比从一种框架切换到另一个框架要容易得多。在项目耗费的时间和精力大致相当的情况下,如何减少知识贬值以及可以带到下一个项目的经验才是更重要的考虑因素。

  原文链接

https://www.toptal.com/javascript/are-big-front-end-frameworks-bad

前端之巅

「前端之巅」是 InfoQ 旗下关注大前端技术的垂直社群。紧跟时代潮流,共享一线技术,欢迎关注。


前端之巅

InfoQ大前端技术社群


 
前端之巅 更多文章 SpriteJS:重新定义Canvas API 关于PWA,你需要注意这9点 前端周报:Airbnb 宣布放弃使用 React Native,Vue超越了React? 太平洋保险CIMS系统微前端实践 2018年,JavaScript的发展状态
猜您喜欢 一个命令搞定所有Linux系统信息(结尾有惊喜) 为什么复杂的组织系统中需要敏捷领导力?(下) 码上行动申请指南 【干货】PHP中的防御性编程 杭州第二次Spark meetup topic解析