[译]现代框架存在的根本缘由

原文连接:medium.com/dailyjs/the…javascript

前言

我曾见过许多人盲目地使用像 ReactAngularVue 这样的现代框架。这些框架提供了许多有趣的东西,但一般人们会忽略它们存在的根本缘由。html

并非咱们所想的如下缘由:前端

  1. 它们基于组件;
  2. 它们有强大的社区;
  3. 它们有不少第三方库来解决问题;
  4. 它们有不少第三方组件;
  5. 它们有浏览器扩展工具来帮助调试;
  6. 它们适合作单页应用。

最基本的、最根本的、最深入的缘由是:java

UI 与状态同步很是困难数组

为何

假设你在开发一个这样需求:浏览器

用户能够经过发送邮件来邀请其余用户。bash

UI 交互设计以下:服务器

  1. 输入框有一个空状态(带有提示信息)
  2. 输入邮箱后展现相应的 邮箱,每一个地址的右侧都有一个删除按钮。

原型以下: babel

这个表单是一个包含电子邮件地址和惟一标识符的对象数组。最初它将是空的。输入邮件回车后,向该数组中添加一项并更新 UI。当用户点击删除时,删除对应的项并更新 UI。app

感觉到了吗?每次更改状态时,都须要更新 UI。

我听到你再说,那又怎样?OK,让咱们看看如何在不用框架的状况下实现它。

原生实现相对复杂的 UI

// html
<html>
  <body>
    <div id="addressList">
      <form>
        <input>
        <p class="help">Type an email address and hit enter</p>
        <ul>
        </ul>
      </form>
    </div>
  </body>
</html>

// js
class AddressList {
  constructor(root) {
    // state variables
    this.state = []
    // UI variables
    this.root = root
    this.form = root.querySelector('form')
    this.input = this.form.querySelector('input')
    this.help = this.form.querySelector('.help')
    this.ul = root.querySelector('ul')
    this.items = {} // id -> li element
    // event handlers
    this.form.addEventListener('submit', e => {
      e.preventDefault()
      const address = this.input.value
      this.input.value = ''
      this.addAddress(address)
    })
    this.ul.addEventListener('click', e => {
      const id = e.target.getAttribute('data-delete-id')
      if (!id) return // user clicked in something else  
      this.removeAddress(id)
    })
  }
  addAddress(address) {
    // state logic
    const id = String(Date.now())
    this.state = this.state.concat({ address, id })
    // UI logic
    this.updateHelp()
    const li = document.createElement('li')
    const span = document.createElement('span')
    const del = document.createElement('a')
    span.innerText = address
    del.innerText = 'delete'
    del.setAttribute('data-delete-id', id)
    this.ul.appendChild(li)
    li.appendChild(del)
    li.appendChild(span)
    this.items[id] = li
  }
  removeAddress(id) {
    // state logic
    this.state = this.state.filter(item => item.id !== id)
    // UI logic
    this.updateHelp()
    const li = this.items[id]
    this.ul.removeChild(li)
  }
  // utility method
  updateHelp() {
    if (this.state.length > 0) {
      this.help.classList.add('hidden')
    } else {
      this.help.classList.remove('hidden')
    }
  }
}

const root = document.getElementById('addressList')
new AddressList(root);
复制代码

codepen 地址:codepen.io/gimenete/em…

以上代码很好地说明了使用原生 JavaScript 实现一个相对复杂的 UI 所需的工做量。

在这个例子中,HTML 负责建立静态页面,JavaScript 经过 document.createElement 改变 DOM 结构。

这引来了第一个问题:

构建 UI 相关的 JavaScript 代码比较复杂,并且 UI 构建分为了两部分。咱们本能够用 innerHTML,虽然它有更高的可读性,但下降了页面的性能,同时可能存在 CSRF 漏洞。

咱们也可使用模板引擎,但若是是大面积地修改 DOM,会面临两个问题:效率不高与须要从新绑定事件处理器。

但这不是最大问题。最大的问题是每当状态发生改变时都要手动更新 UI。每次状态更新时,都须要不少代码来改变 UI。当添加电子邮件地址时,只须要两行代码来更新状态,但要十三行代码更新 UI。并且咱们已经让 UI 尽量简单了!

它不只难以编写并且难以推理,更重要的是:它也很是脆弱。

假设咱们咱们须要实现将列表与服务器同步的功能,咱们须要将数据同服务器返回的数据做对比。

咱们须要写大量代码,使 DOM 更新更加高效。但若是有任何微小的错误,视图将与数据再也不同步。

所以,为了保持视图与状态同步,咱们须要写大量乏味且脆弱的代码。

响应式拯救一切

之因此使用框架不是由于社区,不是由于工具,不是由于生态,不是由于第三方库......

目前为止,框架最大的改进是保证 UI 和数据同步。

只要你清楚框架的使用规则,就能够很愉快的使用他们。

We define the UI in a single shot, not having to write particular UI code in every action, and we always get the same output due to a particular state: the framework automatically updates it after the state changes.

框架是如何工做的呢?

有两个基本的策略:

  1. 从新渲染整个组件,如 React。当组件中的状态发生改变时,在内存中计算出新的 DOM 结构后与已有的 DOM 结构进行对比。实际上,这是很是昂贵的。于是采起虚拟 DOM ,经过对比状态变化先后虚拟 DOM 的不一样,计算出变化后再改变真实 DOM 结构。这个过程称为调和(reconciliation)。

  2. 经过观察者监测变化,如 Angular 和 Vue。应用中状态的属性会被监测,当它们发生变化时,相应的 DOM 元素会从新渲染。

Web components 怎么样

不少状况,人们会把 React、 Angular 和 Vue 与 Web components 进行对比。这些人显然不理解这些框架所提供的最大好处:保持 UI 与状态同步。

Web components 并不提供这种同步机制。它只是提供了一个<template> 标签。若是你在应用中使用 Web components 时,想保持 UI 与状态同步,则须要开发者手工完成,或者使用相关库。

本身开发一个框架?

若是热衷于了解底层原理,想知道虚拟 DOM 的具体实现。那,为什么不试着在不使用框架的状况下,仅使用虚拟 DOM 来重写原生 UI呢?

这里是框架的核心,全部组件的基础类。

我喜欢学习事物的原理 —— 虚拟 DOM 实现。那么,为何咱们学习 Virtual DOM 的实现呢?

这是框架的核心,是任何组件的基类。

这里是重写后的 AddressList 组件(使用 babel 来支持 JSX )。

如今 UI 是声明式的,没有使用任何框架。咱们添加新逻辑来改变状态的同时,再也不须要编写额外的代码来保持 UI 同步。

结论

  1. 现代 JavaScript 框架解决的主要问题是保持 UI 与状态同步。
  2. 使用原生 JavaScript 编写复杂、高效而又易于维护的 UI 界面几乎是不可能的。
  3. Web components 并无提供解决 UI 与状态同步的方案。
  4. 使用现有的虚拟 DOM 库去开发本身的框架并不困难,但不建议。

最后

若是你想进【大前端交流群】,关注公众号点击“交流加群”添加机器人自动拉你入群。关注我第一时间接收最新干货。

相关文章
相关标签/搜索