现代前端框架的重要概念

许多初学者经常会问 “我需要学习哪个框架 ?” 以及 “学习框架前需要掌握多少 JS 或者 TS ?” 无数带有主观色彩的文章都在宣传作者首选框架或库的优势,而不是向读者展示其背后的概念以做出更明智的决定。所以让我们先解决第二个问题

学习框架前需要掌握多少 JS 或者 TS

尽可能多地去学以让更好的你理解它们所基于的概念。你将需要了解基本数据类型、函数、基本运算符和文档对象模型 ( DOM ),这是 HTML 和 CSS 在 JS 中的表示。除此之外的一切也都 OK,但并不严格要求某个精通框架或库。

如果你是一个完完全全的新手,JS for cats 应该是一个不错的入门资料。持续学习,直到你感到自信为止,然后继续前进,直到你再次感到自信为止。当掌握了足够的 JS / TS 知识后,你就可以开始学习框架。其他的知识你可以并行学习。

哪些重要概念

State (状态)

Effects (副作用)

Memoization (记忆化)

Templating and rendering (模板与渲染)

所有现代框架都从这些概念中派生出它们的功能

state

State 只是为你的应用程序提供动力的数据。它可能在全局级别,适用于应用程序的大部分组件,或适用于单个组件。让我们写一个计数器的简单例子来说明一下。它保留的计数是 state 。我们可以读取 state 或者写入 state 以增加计数

最简单的表示通常是一个变量,其中包含我们的状态所包含的数据:

letcount =0;constincrement= () => { count++; };constbutton =document.createElement('button'); button.textContent= count; button.addEventListener('click', increment);document.body.appendChild(button);

但这个代码有个问题:类似调用 increment 方法一样去修改 count 的值 ,并不会自动修改 button 的文案。我们需要手动去更新所有的内容,但这样的做法在复杂场景下代码的可维护性 & 扩展性都不是很好。

让 count 自动更新依赖它的使用方的能力称之为 reactivity(响应式) 。这是通过订阅并重新运行应用程序的订阅部分来更新的。

几乎所有的现代前端框架和库都拥有让 state 变成 reactivity 的能力。基本上可以分为 3 种解决方案,采用其中至少一种或者多种混用来实现这个能力:

Observables / Signals (可观察的 / 信号)

Reconciliation of immutable updates (协调不可变的更新)

Transpilation (转译)

这些概念还是直接用英文表达比较贴切 🤣

Observables / Signals (可观察的 / 信号)

Observables 基本上是在读取 state 的时候通过一个订阅方法来收集依赖,然后在更新的时候触发依赖的更新

conststate= (initialValue) => ({_value: initialValue,get:function() {/* 订阅 */;returnthis._value;  },set:function(value) {this._value= value;/* 触发更新 */;  }});

knockout 是最早使用这个概念的框架之一,它使用带有 / 不带参数的相同函数进行写/读访问

这种模式最近有开始有框架通过 signals 来实现,比如 Solid.js 和preact signals ;相同的模式也在 Vue 和Svelte 中使用到。RxJS 为 Angular 的 reactive 层提供底层能力,是这一模式的延伸,超越了简单状态。Solid.js 用 Stores(一些通过 setter 方法来操作的对象)的方式进一步抽象了 signals

Reconciliation of immutable states(协调不可变的更新)

不可变意味着如果对象的某个属性发生改变,那么整个对象的引用就会发生改变。所以协调器做的事情就包括通过简单的引用对比就判断出对象是否发生了改变

conststate1 = {todos: [{text:'understand immutability',complete:false}],currentText:''};// 更新 currentText 属性conststate2 = {todos: state1.todos,currentText:'understand reconciliation'};// 添加一个 todoconststate3 = {todos: [    state1.todos[0],    {text:'understand reconciliation',complete:true}  ],currentText:''};// 由于不可变性,这里将会报错state3.currentText='I am not immutable!';

如你所见,未变更项目的引用被重新使用。如果协调器检测到不同的对象引用,那么它将重新运行所有的组件,让所有的组件的 state (props, memos, effects, context) 都使用最新的这个对象。由于读取访问是被动的,所以需要手动指定对响应值的依赖。

很显然,你不会用上面这种方式定义 state 。要么你是从一个已经存在的属性构造 state ,要么你会使用 reducer 来构造 state。一个 reducer 函数就是接收一个 state 对象然后返回一个新的 state 对象。

reactpreact 就使用这种模式。它适合与 vDOM 一起使用,我们将在稍后描述模板时探讨它。

并不是所有的框架都借助 vDOM 将 state 变成完成响应式。例如 Mithril.JS 要不是在 state 修改后触发对应的生命周期事件,要不是手动调用 m.redraw() 方法,才能够触发更新

Transpilation(转译)

Transpilation 是在构建阶段,重写我们的代码让代码可以在旧的浏览器运行或者赋予代码其他的能力;在这种情况下,转译则是被用于把一个简单的变量修改成响应式系统的一部分。

Svelte 就是基于转译器,该转译器还通过看似简单的变量声明和访问为他们的响应式系统提供能力

另外,Solid.js 也是使用 Transpilation ,但 Transpilation 只使用到模版上,没有使用到 state 上

Effects

大部分情况下,我们需要做的更多是操作响应式的 state,而很少需要操作基于 state 的 DOM 渲染。我们需要管理好副作用,这些副作用是由于视图更新之外的状态变化而发生的所有事情(虽然有些框架把视图更新也当作是副作用,例如

 https://b23.tv/5CVC7NX

https://b23.tv/BY7NBFf

https://b23.tv/0eHr98g

https://b23.tv/qVGZIUB

记得之前 state 的例子中,我们故意把订阅操作的代码留空。现在让我们把这些留空补齐来处理副作用,让程序能够响应更新

constcontext = [];conststate= (initialValue) => ({_subscribers:newSet(),_value: initialValue,get:function() {constcurrent = context.at(-1);if(current) {this._subscribers.add(current); }returnthis._value;  },set:function(value) {if(this._value=== value) {return; }this._value= value;this._subscribers.forEach(sub=>sub());  }});consteffect= (fn) => {constexecute= () => {    context.push(execute);try{fn(); }finally{ context.pop(); }  };execute();};

上面代码基本上是对 preact signals或者Solid.js 响应式 state 的简化版本,它不包含错误处理和复杂状态处理(使用一个函数接收之前的状态值,返回下一个状态值),但这些都是很容易就可以加上的

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,839评论 6 482
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,543评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 153,116评论 0 344
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,371评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,384评论 5 374
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,111评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,416评论 3 400
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,053评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,558评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,007评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,117评论 1 334
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,756评论 4 324
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,324评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,315评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,539评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,578评论 2 355
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,877评论 2 345

推荐阅读更多精彩内容

  • 什么是跨域? 跨域是指一个域下的文档或脚本试图去请求另一个域下的资源,这里跨域是广义的。 广义的跨域: 1.) 资...
    Jokery阅读 567评论 0 0
  • cookie 前言 网络早期最大的问题之一是如何管理状态。简而言之,服务器无法知道两个请求是否来自同一个浏览器。当...
    O蚂蚁O阅读 549评论 0 0
  • 背景 在编码的过程中变量命名是一个容易忽略,又容易犯头疼的问题。例如在复杂的页面布局中 Class 的命名,同样一...
    沐深阅读 1,962评论 0 2
  • 前端应用的 bundle 体积是影响应用性能的主要方面之一,我们看下取自 HTTP Archive - Loadi...
    涅槃快乐是金阅读 149评论 0 1
  • WebSocket 出现前 构建网络应用的过程中,我们经常需要与服务器进行持续的通讯以保持双方信息的同步。通常这种...
    NeWolf阅读 5,450评论 1 1