过去的历史,和一点点将来 UI 框架的幻想

回顾

jQuery

记得 13 年刚开始接触前端的时候,最兴奋的是用原生 JS 手写了一个左右漂浮的广告,那时候 jQuery 大行其道,如今回过头看,这或许是经过 JS 间接操做 UI 的起点,jQuery 的核心部分也是对 Dom 对象的包装,经过正则匹配来处理 CSS HTML,网上处处都是可随意 copy 的 jQuery 效果代码,和各类 jQuery Plugin,那时候的前端并不须要太多的专业计算机知识,理论上复制粘贴同样能完成大部分“诡异”的需求,若是要类比下,jQuery 的年代其实还挺超前,好比至今搁浅的 webComponent 和 jQuery Plugin 其实很像,都是经过网络来分享的包裹了 JS CSS HTML 的一个 Widget,固然内部原理相差甚远。前端

jQuery 不算是个 UI 框架,可是它开启了对 Dom 的间接操做,这是现代 UI 框架的基础理念,影响深远vue

Handlebar

随着需求的日益复杂,愈来愈多的需求须要经过响应后端数据来渲染网页,经过 jQuery 来响应后端数据操做 Dom 变得愈来愈低效和难以维护,因而 Handlebar 横空出世,不少年轻的前端开发们可能没有听过这个模板框架,但在 14 年的时候它但是大名鼎鼎,经过 Handlebar 你能够提早书写须要更新的 Dom 模板,大大提升了操做 Dom 的效率react

Angular 1.0

不得不说 angular 是个跨时代的产物,从某种意义上来说算是第一个 UI 框架也不为过,相对于开发 SPA 须要使用 Dom 操做库,业务逻辑处理框架好比 Backbone,加上一个好用的模板处理框架好比 Handlebar,angular 第一次把这些特性整合到一块儿,还不是杂糅的那种而是很是完整的整合成一个新东西,这是集 UI框架/模块依赖/打包工程于一体,因此可想而知,出来就大火,不过从 2020 往回看 angular 1.0 的大一统思想过于大包大揽,加上做者本身都以为丧心病狂的脏检查机制,以致于后期 API 更新乏力,随着 React 的崛起,逐渐淡出。ios

React

15年的时候,React 忽然就火了起来,最核心要属 virtualdom,借助 virtualdom,在渲染性能上的出色表现,React 开始崭露头角,并且其函数式的编程哲学也很是吸引人,那一段时间很是多的前端爱好者都致力于将函数式编程引入 JavaScript 的世界,加上 JavaScript 自己极强的可塑性,函数一等公民的底子,让一贯在业务开发领域偏门的函数式编程走入大众视野,然后的 Redux 的出现,那一场时间旅行的精彩演讲更是将 immutable,thuck 等函数式概念推广的深刻人心,以致于那一段时间无论 Redux 写起来有多繁琐,可是我都致力于把全部逻辑都往上面搬,仿佛若是不这么作,代码就很差维护了同样web

事实上,直到 Hooks 出现以前,React 的函数式编程哲学都有点不三不四,基于 class component 构建的复杂应用,绑上 Redux 这个函数式状态管理库,怎么看怎么别扭,加上 HOC 冗长的调用链,写起来还真是有点考验耐心,因此才会有 Dva 等基于 Redux 的 platform 来下降其编写代码的成本,但本质上也是将 Redux 经过一顿包装猛如虎,从函数式变成面向对象的方式,引入 module 等概念来矫正这种别扭编程

但 React 是以函数式而生的,包括 HOC 都是为了将 class component 变得更函数化一点儿而提出来的,直到开发团队设计了 Hooks,这个以去掉命令式中条件判断,内部采用闭包锁定每一次的做用域,去变量化等方式来让 React 完全函数化,面对新的 React,此时史无前例的接近一切皆函数的概念,但同时也让这种“别扭”达到了顶峰,社区大量的人吐槽没法好好的写代码,不管是 useMemo 仍是 useCallback 都让习惯了面向对象风格和命令式代码的开发者无所适从,若是说之前还只是用 immutable 来避免对象的可变性带来的麻烦,但大部分时候咱们仍是在使用 this,经过对象引用来共享数据,而如今就变成了咱们得忘记面向对象,忘记命令式,若是你想绕过 Hooks 的常量性,你得用 ref 来维持对一个可变对象的引用,一切彷佛都倒过来了,咱们须要从新学习如何编写代码,Hooks 带来的心智成本几乎是颠覆性的,但说实话我本人仍是很喜欢函数式的,也很是赞叹这种巧妙的设计,不过以 React 这种设计方式,估计对喜欢面向对象风格的人来讲怎么看都是别扭吧。axios

Vue

我写过一点 Vue,网上总说 Vue 抄袭 React,而后尤雨溪忙着四处灭火,我想持此见解的人应该很多,不过说句公道话,开源技术自己就是彼此借鉴来发展的,只不过 Vue 的名气大因此树大招风,从技术层面讲,我以为 Vue 和 React 是彻底两种不一样的设计初衷,即使互相有彼此的借鉴但背后的设计哲学是彻底不同的,和 React 的函数式编程哲学不一样的是 Vue 我以为是沿袭了 angular 1.0 中的设计,实际上是比较正统的面向对象风格,包括响应式数据驱动,经过操做数据来控制 UI,这些在 angular 1.0 中其实都有体现,模板的指令化也是命令式的风格而不是函数式,你看 React 就历来没内置 IF 啥的组件,因此 Vue 可以拥有如此高的人气,也是其面向对象的风格更容易被广大开发所接受,毕竟咱们都是学 C 出身的,大学里也受过面向对象编程的教育,底子在那里,确实更容易接受这种编程风格,其实对于这两种编程风格,我却是以为正好是个太极,彼之长乃吾之短也后端

举个例子,假如咱们要开发一个用户的 profile,从面相对象的角度来思考,咱们可能会这么写数组

class UserProfile extends Widget {
    async constructor(){
        const profile = await axios.get('user/profile')
        this.email = profile.email
        this.name = profile.name
        this.phone = profile.phone
        // other propotype
    }
    get name(){
        return 'username:' + this.name
    }
    get email(){
        return emailHandler(this.email)
    }
    set name(localNameString){
        this.name = nameValid(localNameString)
    }
    @axios('pose','user/profile/upload')
    upload(){
        return {
            name:this.name,
            email:this.email,
            phone:this.phone
        }
    }
    render(){
        return {
            `<div class="name">{{name}}</div> <div class="email">{{email}}</div> <div class="phone">{{phone}}</div> `
        }
    } 
}
复制代码

把目前能用的特性都用上, 面向对象的核心是一切皆对象, 对象是最小单位, 经过继承和混入以对象为核心来构建应用, 由于在设计上最小代码单位是 class, 因此就不会出现将属于类的逻辑和属于函数的逻辑混用的状况, 在 JavaScript 这种灵活性极强的语言中, 咱们很容易将业务逻辑书写在 class 之外的地方, 而不是像上面的示例同样将一切封装到类里, 事实上在 Hooks 出现以前 React 就是这种状况, 基于 class 的 component 和一堆工具函数或者函数级别的代码混用, 最小单位不一致, 混合风格编程致使的逻辑复用性很可贵到保障, HOC 算是一种折中方案, 可是和 render props 同样随着业务代码的膨胀, 很是容易陷入深层嵌套, 愈来愈难以维护, 函数式的 React 急需一种函数式的解决方案将逻辑函数化, 完全摆脱面向对象带来的困扰, 因而 Hooks 来了, 从某种意义上讲, Hooks 像个英雄.浏览器

有了 Hooks, 咱们应该试着忘记面向对象, 对你来讲, 函数是最小单位, 没有属性, 也没有方法, 一切都是函数链运算的结果, 没有变量也没有条件运行, 循环只能用递归, 没有 for 循环, 在这些约束下, 上面那个例子若是用 Hooks 加函数式的思惟来写的话

function upload(profile){
    axios.post('user/profile', profile)
}
function useSetProfile(profile){
    const [profile, setProfile] = useState([{
        name:profile.name,
        email:profile.email,
        phone:profile.phone
    }])
    return setProfile
}
asnyc function useSyncUserProfile(callback,deps){
    useEffect(async ()=>{
        const profile = await axios.get('user/profile/upload')
        callback(profile)
    },deps)
}
function userProfileWidget(profile){
    const setProfile = useSetProfile({
        name:'jaka',
        email:'1123@qq.com',
        phone:'1123123'
    })
    useSyncUserProfile(setProfile,[profile])
    function onBtnClick(){
        setProFile(profile)
        upload()
    }
    return render(
        `<div class="name">{{name}}</div> <div class="email">{{email}}</div> <div class="phone">{{phone}}</div> <button onClick={() => onBtnClick()}></button> `
    )
}
复制代码

相比第一个类的例子, 第二个例子中, 咱们的思考单位聚焦在"函数"上, 没有对象五脏俱全的完整性, 对于一个函数来讲只有入参/出参, 这样的好处天然显而易见的是更细的粒度, 更容易测试, 不过缺点也一样明显, 对于一个 function component 来讲要具有自说明性, 在函数命名上须要制定相应的规范, 不像面向对象类/属性/方法自然的协同概念, 函数这个概念对于描述真实世界来讲缺失太单调了些.

咱们再看看 Vue3.0 给出的一种编程风格, 事实上在 Vue3.0 柔和了函数式, 面向对象两种风格, 而且还加入了响应式来自动触发 getter/setter, 带一点流式编程的味道

<template>
  <div class="name">{{name}}</div>
  <div class="email">{{email}}</div>
  <div class="phone">{{phone}}</div>
  <button @click="onBtnClick">
  </button>
</template>
<script>
  import { reactive, computed } from 'vue'
  function uplaod(profile){
      axios.post('user/profile/upload', profile)
  }
  function useSyncUserProfile(state){
      watchEffect(async ()=>{
        const profile = await axios.get('user/profile')
        state.name = profile.name
        state.email = profile.email
        phone = profile.phone
      })
  }
  export default {
    setup() {
      const state = reactive({
        name: 'jaka',
        email:'1123@qq.com',
        phone: 1833333,
      })
      onMounted(){
        useSyncUserProfile(state)
      }
      function onBtnClick() {
        upload(state)
      }

      return {
        state,
        onBtnClick,
      }
    },
  }
</script>
复制代码

Vue 在引入函数式 API 的基础上并无增长相似 React 那样的强约束, 依然保有生命周期, 从大结构上来看像一个函数组合成的对象, 能够算是对象函数吧...

因此 Vue 不像 React 那样设计的很是完全, 糅合了面向对象可描述性和函数式的细粒度组合性, 不过 JavaScript 自己并非响应式的, 因此 Vue 也有它自身的问题, 在 2.0 是没法内部自动劫持数组, 没法对对象新增属性自动劫持, 3.0 的 proxy 解决了这个问题, 不过带来的新问题就是一旦响应式对象被解构一切就 over 了, 从这个角度看, Vue 核心依然是面向对象的, 和 React 彻底不一样, 他的 Hooks 也是创建在 JavaScript 函数即对象的基础上, 而不是像 React 那样使用了函数闭包的特性.

不过不管是 React 仍是 Vue, 我以为函数式 API 的引入其实都增长了框架的上手门槛, 这个上手是指能好好写....你得了解许多新的底层细节, 遵循一些约定, 而不像向过去那样只要把一切交给框架, 框架就会帮你作好代码级别的约束

虽然这些都仍是刚刚出炉的热乎概念, 但都是过去了, 本文到这里难免要开始作些 YY 的幻想, 将来几年 UI 框架会变成什么样子呢?

幻想

将函数坚持到底的 React

目前 React 的 Concurrent 已经在实验室里了, 始终坚持函数式的回报就是, 一切皆函数很是有利于将渲染颗粒度控制到堆栈级, 毕竟 JavaScript 堆栈就是函数堆栈, 利用浏览器的一些魔法 API 好比 requestidlecallback, 充分利用 cpu 时间片的间隙见缝插针的往里面塞可执行函数, 将 60fps 渲染变得更简单, 固然这不是幻想这只是展望, 毕竟已经在路上了, 若是再日后想想, 咱们都知道在最近火热的 ServerLess 常常会提到 函数即服务 的概念, 微服务被函数所替代, 因此后端都函数即服务了, 若是前端实现了函数即渲染...感受这是先后函数大统一呀, 因此将来的应用是能够经过一堆函数自动串联在一块儿跑的么? 🤔 相比 webComponent 那不太靠谱的包装方式, 若是引入 Deno 那种模块网络化的思想, 把函数组件分发到网络上, 由于粒度的一致性和函数自然容易被机器理解的特性, 这不就实现了 webComponent 的愿景么, 可能之后咱们能够这么写代码

import dataPicker from 'https://function/ui/component/data-picker'
import useWeiBoUserData from 'https://function/hooks/use-webbo-user-data'

async function app(){
    const weiboUserData = await useWeiBoUserData()
    render(){
        return {
            ``` <dataPicker></dataPicker> <div>{weiboUserData.toJSON()}</div> ```
        }
    }
}

// HTML
<div app="react"></div>
复制代码

不须要繁重的打包编译, 浏览器内置 ui 框架, 而后经过网络就能共享函数组件, 函数逻辑, 函数服务...感受要进入新时代了

若是你还有什么脑洞能够开一开, 不妨留在评论里, 或许若干牛后回来还能吹个水, 昔日我就预言....

相关文章
相关标签/搜索