整合营销服务商

电脑端+手机端+微信端=数据同步管理

免费咨询热线:

不完全预测:八大前端JavaScript趋势和工具

不完全预测:八大前端JavaScript趋势和工具
文共3125字,预计学习时长6分钟



图片来源:codeburst @webrealizer


JavaScript的世界正在快速发展。


前端开发(和网络开发)的世界正在以极快的速度发展。如今,如果不借助前端或Webpack、React Hooks、Jest、Vue和NG元素,你会很快被远远抛下。不过,情况正在发生改变。


尽管开发人员和技术人员在前端领域中的数量逐年上升,生态系统却有标准化的趋势。新技术和工具的出现正在改变当下的规则。


总体趋势肯定会是一种基于组件构成的用户界面标准化,会影响从样式到测试甚至状态管理的所有方面,并且总体具有更好的模块度。这将包括围绕web组件、ES模块、组件焦点工具等技术构建。


以下是对未来几年前端开发的一些不完全预测,仅供参考。


1. 与框架无关的Web组件



这大体上代表了未来。因为这些纯web组件与框架无关,可以在没有框架或任何框架拼写标准化的情况下工作。因为不使用JS语言,并受到很多浏览器的支持。其bundle的大小和消耗也将是最优的,而且VDOM呈现震撼人心。


这些组件提供自定义元素,这是一个允许定义新的html标签的Javascript应用程序编程接口,用于指定布局的HTML模板,当然还有本质上特定于组件的影子DOM。


在这个领域中需要了解的主要工具是Lit-html, StencilJS,

SvelteJS当然还有 Bit,用于可重用的可以在任何地方直接共享、使用和开发的模块组件。


当考虑到用户界面开发的未来,以及组件时代中模块度、可重用性、封装性和标准化的原则时,web组件就是答案。


2. 框架冲突的未来?


现在,在NPM下载中React仍然是前端中的“女王”


我们不会深入探讨“哪个更好,为什么更好”,相反,如果退回一步的话,你会注意到更重要更宏大的部分。围绕组件的前端技术的总体“市场份额”正在增长。新开发人员也在快速涌入,工具的使用空间也越来越大。


那么未来5年内哪个框架会成为支配呢?没有人知道。但可以肯定地说,它将是在原生JS生态系统中发挥作用的最佳位置,web组件在其中支配着文档对象模型dom。React在NPM中下载量最高。然而——看看这些数字。似乎在实际web使用中差距非常小。


令人震惊吧?



实际上,Vue和React在实际使用中很接近

随着未来与框架无关的web组件的标准化,不少人都想知道可能会对用户界面框架冲突产生的影响。事实上,我们都知道React确实不是一个框架。

3. 组件分离,重用和构成



heBit组件:未来的代码共享、重用和开发

当谈到在不久的将来的前端开发和用户界面组件时,不可能忽视 Bit惊人的promise功能。


Bit(开放源)分离并将组件(或任何可重用的JS代码)转换为共享的构建块,可供在所有项目和应用中使用和共享。神奇的是——还可以使用Bit从不同项目开发相同组件,同时完全控制源代码更改和整个依赖图。


简单地说,通过Bit可以立即在另一个项目中使用一个项目中的组件,并开发和同步更改这两个组件。团队工作时,这个工作流将通过Bit的组件中心 bit.dev 加强,可以在其中组织和共享团队代码。


组件中心提供了在组件上共享和协作所需的一切,从漂亮的搜索和发现体验到实时组件playground,持续集成和持续交付的充分支持等。


通过Bit可以充分构建应用程序,即时获得团队和开源社区写入的所有组件,并立即共享新组件或建议对现有组件进行更新。

4. ES模块和内容分发网络



ES模块是在浏览器中用模块工作的标准,被ECMAScript标准化。使用ES模块可以很容易地将功能封装进模块中,可以通过内容分发网络等方式使用。随着Firefox 60的发布,所有主流的浏览器都将支持ES模块,Node mteam正致力将ES模块支持添加到Node.js中。另外,用于WebAssembly的ES模块整合将在未来几年内实现。想象一下,JS组件与Bit分离,并通过 bit.dev内容分发网络使用。


5. 组件级别的状态管理



那么状态管理有什么新变化呢?我们只需要在Redux中就能管理一切事务吗?


但这可能很难实现充分利用组件,使模块得到重用。React新的Context API 和 Hooks意味着不需要第三方库,便可以在功能组件级别管理状态,从而提高模块度和可重用性。


因此,展望未来,我们可以尝试更多地从封装组件而较少从全球应用商店的角度来考虑状态管理。


6. 构成样式化组件



Bit模块:由独立逻辑和主题组件构成样式

过去两年有很多关于样式化组件的讨论。从内联层叠样式表或层叠样式表模块到JS中的层叠样式表和样式组件,甚至像stylable这样的中途解决方案,有很多解决方案。

未来几年,样式可以作为一种构成。这意味着,组件设计体系应该同时包含逻辑组件和主题组件,可以使用Bit之类的工具构成。通过这种方式可以创建一个设计体系,根据需要进行发展和改变,不会将一个复杂的库强加给不愿意去应用的开发人员。设计工具本身如Sketch an Figma,利用will组件来达到这个目的(结合Bi得到最终的组件设计体系。

7. 用于数据驱动应用程序的GraphQL应用程序编程接口客户端



令人兴奋的是,在客户端有很大的可能性来通过组件使用GraphQL。使用阿波罗可以轻松通过GraphQL构建获取数据的用户界面组件。结合Bit能从正在处理的项目中导入和开发这些组件。


通过对应用程序编程接口的智能管理,可以简化围绕数据驱动应用程序开发的工作流,加快开发速度。所以对未来几年的展望绝对是值得的。


8. 基于组件的设计工具



随着组件变为了设计体系,设计师和开发人员双方都在向彼此靠近。也就是从双方来看,这是可能的。


Sketch已经在设计组件之间创建了依赖关系,因此可以模块化的方式设计和更新。代码组件的整合已经出现,这只是时间问题。


Figma这类的工具是彻底基于可重用的用户界面元素构建的。Framer Team正在为编写代码的设计人员构建一个工具,能够在一定程度上控制将用户界面元素转换为可重用的React组件。


通过Bit可以将设计的组件转换为可重用的构建块,并可以在任何地方直观发现、使用甚至开发,从而弥补与开发人员之间的差距。Bit +组件设计工具未来大有可为。通过内容分发网络使用Bit和web组件是个完整的构成。

留言 点赞 关注

我们一起分享AI学习与发展的干货

欢迎关注全平台AI垂类自媒体 “读芯术”

eact18内核探秘:手写React高质量源码迈向高阶开发

download: https://www.666xit.com/4202/


RN简介React Native(简称RN)是Facebook于2015年4月开源的跨渠道移动运用开发结构,是Facebook新近开源的JS结构React在原生移动运用渠道的衍生产品,现在支撑iOS和安卓两大渠道。RN运用Javascript言语,类似于HTML的JSX,以及CSS来开发移动运用,因此了解Web前端开发的技术人员只需很少的学习就能够进入移动运用开发范畴。

React Native

看起来很像

React

,只不过其根底组件是原生组件而非 web 组件。要了解

React Native

运用的根本结构,首要需求了解一些根本的

React

的概念,比如

JSX

语法、组件、

state

状况以及

props

特色。

React Native开发特色:

一次学习,随处编写:运用React Native可认为iOS和Android操作系统开发运用程序,不同渠道上的代码依据渠道会有一些微小的差异。混合开发:React Native代码开发的模块与原生代码开发的模块能够双向通信、无缝联接;高效的移动运用开发:(1)共同的UI完结结构(2)组件化开发(3)跨渠道移植代码敏捷(4)自动匹配不同屏幕巨细的手机高效的移动运用开发调试高效的运用热更新有效下降移动运用装置包体积学习门槛低、开发难度低运用React Native开发的价值为了得到React Native开发的长处,运用React Native开发的APP也需求付出必定的价值。(1)内存耗费大运用React Native开发的程序运行所需的内存比原生代码开发的程序略多。(2)运行速度运用React Native开发的代码运行速度比原生代码略慢。

React 与 React Native 除了在编码体现层都运用 JSX 语法外,在 React 与 React Native 的底层都有 Virtual DOM 与 DOM 之间的映射与转换,以完结了页面组件高效更新的前端体现。

现在最新版本是0.59React Native中文网

React Native

React

的关系及特色:

React

是根底结构,是一套根底规划完结理念,开发者不能直接运用它来开发移动运用或网页。在

React

之上开展出了

React.js

结构用来开发网页,开展出来

React Native

用来开发移动运用。底层原理是相同的,都是运用js完结虚拟dom树来驱动页面的烘托,react是驱动HTML dom的烘托,react native是驱动原生组件的烘托。

React.js

:目的 是为了使前端的V层更具组件化,能更好的复用,它能够运用简略的html标签创立更多的自界说组件标签,内部绑定事情,同时能够让你从操作dom中解脱出来,只需求操作数据就会改动相应的dom。

二者都是依据组件(

component

)开发,然后组件和组件之间经过

props

传递办法,每个组件都有一个状况

(state)

,当某个办法改动了这个状况值时,整个组件就会重绘,然后到达改写。另外,说到重绘就要说到

虚拟dom

了,便是用js模仿

dom

结构,等整个组件的

dom

更新结束,它会有一个

diff

的进程,对比出哪些组件发生了改动,然后才烘托到页面,简略来说只更新了比较之前改动了的部分,而不是全部改写,所以功率很高。

虚拟DOM(Virtual DOM)

的机制:在浏览器端用

Javascript

完结了一套

DOM API

。依据

React

进行开发时一切的

DOM

结构都是经过虚拟

DOM

进行,每当数据改动时,React都会从头构建整个DOM树,然后React将当时整个DOM树和上一次的DOM树进行对比,得到DOM结构的差异,然后只是将需求改动的部分进行实际的浏览器DOM更新。而且React能够批处理虚拟DOM的改写,在一个事情循环(Event Loop)内的两次数据改动会被兼并。

2.React详解

React 官网React GitHub 地址React菜鸟教程React介绍学习生态介绍

Vue生态:Vue + Vue-Router + Vuex + Axios + Babel + WebpackReact生态:React + React-Router + Redux + Axios + Babel + Webpack

2.1.React 简介

React是一个声明式,高效且灵活的用于构建用户界面的 JavaScript 库。运用 React 能够将一些简短、独立的代码片段组合成复杂的 UI 界面,这些代码片段被称作“组件”。

React

特色

1.声明式规划 React采用声明范式,能够轻松描绘运用。2.高效 React经过对DOM的模仿,最大极限地减少与DOM的交互。3.灵活 React能够与已知的库或结构很好地合作。4.JSX JSX是JavaScript语法的扩展。React 开发不必定运用 JSX ,但咱们主张运用它。5.组件 经过React构建组件,使得代码愈加容易得到复用,能够很好的运用在大项目的开发中。6.单向呼应的数据流 React完结了单向呼应的数据流,然后减少了重复代码,这也是它为什么比传统数据绑定更简略。2.2 React运用

1.React 环境装备装置

运用 React CDN 库经过 npm 运用 React运用 create-react-app 快速构建 React 开发环境2.3React render 烘托

屏幕输出:Hello, React

Title

ReactDOM.render(

Hello, React

,document.getElementById('root'));


3.React JSX

React 运用 JSX 来代替惯例的 JavaScript。

1.React JSX简介

JSX:JavaScript XML,一种类似于XML的JS扩展语法。也能够了解成:契合 XML 标准的 JS 语法。

JSX语法的实质:以 React.createElement 的形式来完结的,并没有直接把 用户写的 HTML代码,烘托到页面上。运用babel转换工具将 JSX语法 转换为 JS语法。

咱们不需求必定运用 JSX,但它有以下长处:

JSX 履行更快,由于它在编译为 JavaScript 代码后进行了优化。它是类型安全的,在编译进程中就能发现错误。运用 JSX 编写模板愈加简略快速。

const element=

Hello, world!

;


这种看起来或许有些奇怪的标签语法既不是字符串也不是 HTML。它被称为 JSX, 一种 JavaScript 的语法扩展。 咱们推荐在 React 中运用 JSX 来描绘用户界面。JSX 是在 JavaScript 内部完结的。

2.JSX的根本语法

(1)在 JSX内部 写 JS代码:假如要在 JSX 语法内部,书写 JS 代码,那么,一切的JS代码有必要写到 {} 的内部。在{}内部,能够写任何契合JS标准的代码。

例如:

var myTitle='这是运用变量界说的 tilte 值'// 运用JSX语法 创立虚拟DOM目标var vDom=(

Hello, React!这是标题);


(2)当编译引擎在编译JSX代码的时分,假如遇到了<,会把它当作 HTML代码 去编译;假如遇到了 {}, 会把方括号里边的代码当作 一般JS代码 去编译。

(3)在JSX中,假如要为元素添加class特色,则有必要写成className,由于 class在ES6中是一个关键字;和class类似,label标签的 for 特色需求替换为 htmlFor。

代码举例:

// 运用JSX语法 创立虚拟DOM目标var vDom=(

Hello, React!千古壹号


);


(4)在JSX创立DOM的时分,一切的节点,有必要有唯一的根元素进行包裹。

(5)假如要写注释,注释有必要放到 {} 内部。例如:

// 运用JSX语法 创立虚拟DOM目标var vDom=(// 这一行是注释

Hello, React!

千古壹号

{/*这一行也是注释 */});


最后,再举个比如:

Document

//页面中的实在容器元素var containDiv=document.getElementById("app");var arr=[]for (var i=0; i < 6; i++) {var p=这个是p标签


// 注意这个地方的写法: key={i}arr.push(p)}//1、运用JSX语法 创立虚拟DOM目标var vDom=(Hello, React!{arr});//2、烘托虚拟DOM目标ReactDOM.render(vDom, containDiv); // 参数1:虚拟DOM目标;参数2:页面中的容器


4.React 组件 状况(State&props) 生命周期详解

React 把组件看成是一个状况机(State Machines)。经过与用户的交互,完结不同状况,然后烘托 UI,让用户界面和数据保持一致。

React 里,只需更新组件的 state,然后依据新的 state 从头烘托用户界面(不要操作 DOM)。

创立组件的第一种方式:在React中,结构函数便是一个最根本的组件。假如想要把组件放到页面中,能够把结构函数的名称当作组件的名称,以 HTML标签形式引进页面中即可。创立组件的第二种方式:运用 class 关键字运用 function 创立的组件,叫做【无状况组件】;运用 class 创立的组件,叫做【有状况组件】。实质差异:有状况组件和无状况组件,最实质的差异,便是有无 state 特色。同时, class 创立的组件,有自己的生命周期函数,可是,function 创立的 组件,没有自己的生命周期函数。

state 和 props 主要的差异在于 props 是不可变的,而 state 能够依据与用户交互来改动。这便是为什么有些容器组件需求界说 state 来更新和修改数据。 而子组件只能经过 props 来传递数据。

组件API

设置状况:setState替换状况:replaceState设置特色:setProps替换特色:replaceProps强制更新:forceUpdate获取DOM节点:findDOMNode判别组件挂载状况:isMounted

组件的生命周期可分成三个状况:

在组件创立、到加载到页面上运行、以及组件被毁掉的进程中,总是伴随着各式各样的事情,这些在组件特定时期,触发的事情统称为组件的生命周期。Mounting:已插入实在 DOMUpdating:正在被从头烘托Unmounting:已移出实在 DOM

生命周期的办法有:

componentWillMount 在烘托前调用,在客户端也在服务端。componentDidMount : 在第一次烘托后调用,只在客户端。之后组件现已生成了对应的DOM结构,能够经过this.getDOMNode()来进行拜访。 假如你想和其他JavaScript结构一起运用,能够在这个办法中调用setTimeout, setInterval或许发送AJAX请求等操作(防止异步操作阻塞UI)。componentWillReceiveProps 在组件接纳到一个新的 prop (更新后)时被调用。这个办法在初始化render时不会被调用。shouldComponentUpdate 回来一个布尔值。在组件接纳到新的props或许state时被调用。在初始化时或许运用forceUpdate时不被调用。能够在你确认不需求更新组件时运用。componentWillUpdate在组件接纳到新的props或许state但还没有render时被调用。在初始化时不会被调用。componentDidUpdate 在组件完结更新后立即调用。在初始化时不会被调用。componentWillUnmount在组件从 DOM 中移除之前马上被调用。1.组件创立阶段

组件创立阶段的生命周期函数,有一个明显的特色:创立阶段的生命周期函数,在组件的一辈子中,只履行一次。

getDefaultProps初始化 props 特色默认值。

getInitialState初始化组件的私有数据。由于 state 是界说在组件的 constructor 结构器当中的,只需new 了 class类,必定会调用 constructor结构器。

componentWillMount()组件将要被挂载。此刻还没有开端烘托虚拟DOM。

在这个阶段,不能去操作DOM元素,但能够操作特色、状况、function。相当于 Vue 中的Create()函数。

render()第一次开端烘托真实的虚拟DOM。当render履行完,内存中就有了完好的虚拟DOM了。

意思是,此刻,虚拟DOM在内存中创立好了,可是还没有挂在到页面上。

在这个函数内部,不能去操作DOM元素,由于还没return之前,虚拟DOM还没有创立;当return履行结束后,虚拟DOM就创立好了,可是还没有挂在到页面上。

componentDidMount()当组件(虚拟DOM)挂载到页面之后,会进入这个生命周期函数。

只需进入到这个生命周期函数,则必定阐明,页面上现已有可见的DOM元素了。此刻,组件现已显示到了页面上,state上的数据、内存中的虚拟DOM、以及浏览器中的页面,现已彻底保持一致了。

当这个办法履行完,组件就进入都了 运行中 的状况。所以说,componentDidMount 是创立阶段的最后一个函数。

在这个函数中,咱们能够放心的去 操作 页面上你需求运用的 DOM 元素了。假如咱们想操作DOM元素,最早只能在 componentDidMount 中进行。相当于 Vue 中的 mounted() 函数

2、组件运行阶段

有一个明显的特色,依据组件的state和props的改动,有选择性的触发0次或屡次。

componentWillReceiveProps()组件将要接纳新特色。只有当父组件中,经过某些事情,从头修改了 传递给 子组件的 props 数据之后,才会触发这个钩子函数。

shouldComponentUpdate()判别组件是否需求被更新。此刻,组件尚未被更新,可是,state 和 props 肯定是最新的。

componentWillUpdate()组件将要被更新。此刻,组件还没有被更新,在进入到这个生命周期函数的时分,内存中的虚拟DOM还是旧的,页面上的 DOM 元素也是旧的。(也便是说,此刻操作的是旧的 DOM元素)

render此刻,又要依据最新的 state 和 props,从头烘托一棵内存中的 虚拟DOM树。当 render 调用结束,内存中的旧DOM树,现已被新DOM树替换了!此刻,虚拟DOM树现已和组件的 state 保持一致了,都是最新的;可是页面还是旧的。

componentDidUpdate此刻,组件完结更新,页面被从头烘托。此刻,state、虚拟DOM 和 页面现已彻底保持同步。

3、组件毁掉阶段

一辈子只履行一次。

componentWillUnmount: 组件将要被卸载。此刻组件还能够正常运用。生命周期对比:

vue中的生命周期图React Native 中组件的生命周期

组件生命周期的履行次序总结

1、Mounting:

constructor()componentWillMount()render()componentDidMount()2、Updating:

componentWillReceiveProps(nextProps):接纳父组件传递过来的特色shouldComponentUpdate(nextProps, nextState):一旦调用 setState,就会触发这个办法。办法默认 return true;假如 return false,后续的办法就不会走了。componentWillUpdate(nextProps, nextState)render()componentDidUpdate(prevProps, prevState)3、Unmounting:

componentWillUnmount()5.React 事情处理

React 元素的事情处理和 DOM 元素类似。可是有一点语法上的不同:

React 事情绑定特色的命名采用驼峰式写法,而不是小写。假如采用 JSX 的语法你需求传入一个函数作为事情处理函数,而不是一个字符串(DOM 元素的写法)

//HTML 一般写法是:激活按钮//React 中写法为:激活按钮

多初学者经常会问 “我需要学习哪个框架 ?” 以及 “学习框架前需要掌握多少 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 以增加计数

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

let count=0; 
const increment=()=> { count++; }; 
const button=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 的时候通过一个订阅方法来收集依赖,然后在更新的时候触发依赖的更新

const state=(initialValue)=> ({
  _value: initialValue,
  get: function() {
    /* 订阅 */;
    return this._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(协调不可变的更新)

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

const state1={
  todos: [{ text: 'understand immutability', complete: false }],
  currentText: ''
};
// 更新 currentText 属性
const state2={
  todos: state1.todos,
  currentText: 'understand reconciliation'
};
// 添加一个 todo
const state3={
  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 对象。

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

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

Transpilation(转译)

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

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

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

Effects

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

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

const context=[];

const state=(initialValue)=> ({
  _subscribers: new Set(),
  _value: initialValue,
  get: function() {
    const current=context.at(-1);
    if (current) { this._subscribers.add(current); }
    return this._value;
  },
  set: function(value) {
    if (this._value===value) { return; }
    this._value=value;
    this._subscribers.forEach(sub=> sub());
  }
});

const effect=(fn)=> {
  const execute=()=> {
    context.push(execute);
    try { fn(); } finally { context.pop(); }
  };
  execute();
};
复制代码

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

这允许我们使前面的示例具有响应性:

const count=state(0);
const increment=()=> count.set(count.get() + 1);
const button=document.createElement('button');
effect(()=> {
  button.textContent=count.get();
});
button.addEventListener('click', increment);
document.body.appendChild(button);
复制代码

? 可以尝试运行一下上面 Effect 的两个代码块的例子,源代码地址在 这里

在大多数情况下,框架允许在不同生命周期,让 Effect 在渲染 DOM 之前、期间或之后运行。

Memoization

Memoization 意味着缓存 state 值的计算结果,并且在结果的依赖发生改变的时候进行更新。它基本上是一种返回派生(derived) state 的 Effect

在某些会重新运行其组件函数的框架中,如 react 和 preact,允许在它所依赖的状态没有改变时避免这部分组件重新渲染

对于其他框架,情况恰恰相反:它允许你选择部分组件进行响应式更新,同时缓存之前的计算

对于我们简单的响应系统,memo 大概是这样实现

const memo=(fn)=> {
  let memoized;
  effect(()=> {
    if (memoized) {
      memoized.set(fn());
    } else {
      memoized=state(fn());
    }
  });
  return memoized.get;
};
复制代码

Templating and rendering

现在有了原始的、派生的和缓存形式的 state,我们想把它展示给用户。在我们的例子中,我们直接操作 DOM 来添加按钮和更新按钮的内容文案。

为了提升开发体验,几乎所有的现代框架都支持 DSL 来在代码中编写类似于所需输出的内容。虽然有不同的风格,比如 .jsx.vue.svelte 文件,但这一切都归结为用类似于 HTML 的代码来表示 DOM。所以基本上是

<div>Hello, World</div>

// 在你的 JS 代码中
// 变成你的 HTML:

<div>Hello, World</div>
复制代码

你可以能会问:“在哪里放置我的 state ?”。非常好的问题,大部分的情况 下,{} 用于在属性和节点中表达动态内容。

最常用的 JS 模板语言扩展无疑是 JSX。在 react 中,它被编译为存粹的 JavaScript 语言,允许创建对于 DOM 的虚拟表示,也就是经常被提到的「虚拟文档对象」或者简称为 vDOM。

这是基于创建 JS 对象比访问 DOM 快得多的前提,所以如果你可以用创建 JS 对象替换访问 DOM,那么你就可以节省时间

然而,如果你的项目在任何情况下都没有大量的 DOM 修改或者只是创建不需要修改的对象;那么上面这个方案的优点就会变成缺点,那这个时候就需要使用 memoization 来将缺点的影响降到最小。

// 1. 源代码
<div>Hello, {name}</div>

// 2. 转译成 js 代码
createElement("div", null, "Hello, ", name);

// 3. 执行 js 后返回的对象
{
  "$$typeof": Symbol(react.element),
  "type": "div",
  "key": null,
  "ref": null,
  "props": {
    "children": "Hello, World"
  },
  "_owner": null
}

// 4. 渲染 vdom
/* HTMLDivElement */<div>Hello, World</div>
复制代码

JSX 不仅仅用在 react,也用在了 Solid.js。例如,使用 Solid 转译器更彻底地改变代码

// 1. 源代码
<div>Hello, {name()}</div>

// 2. 转译成 js 代码
const _tmpl$=/*#__PURE__*/_$template(`<div>Hello, </div>`, 2);
(()=> {
  const _el$=_tmpl$.cloneNode(true),
    _el$2=_el$.firstChild;
  _$insert(_el$, name, null);
  return _el$;
})();

// 3. 渲染 vdom
/* HTMLDivElement */<div>Hello, World</div>
复制代码

虽然转译之后的代码一开始看到会觉得挺吓人,但它更容易解释其中代码的逻辑。首先,模版的静态部分被创建出来;然后,创建出来的对象被克隆并创建一个新的实例,新的实例包含被添加的动态部分,以及将动态部分的更新与 state 的更新关联起来。

Svelte 在转译的时候做的工作更多,不仅仅处理了模版,还处理了 state

// 1. 源代码
<script>
let name='World';
setTimeout(()=> { name='you'; }, 1000);
</script>

<div>Hello, {name}</div>

// 2. 转译成 js 代码
/* 生成自 Svelte v3.55.0 版本 */
import {
        SvelteComponent,
        append,
        detach,
        element,
        init,
        insert,
        noop,
        safe_not_equal,
        set_data,
        text
} from "svelte/internal";

function create_fragment(ctx) {
        let div;
        let t0;
        let t1;

        return {
                c() {
                        div=element("div");
                        t0=text("Hello, ");
                        t1=text(/*name*/ ctx[0]);
                },
                m(target, anchor) {
                        insert(target, div, anchor);
                        append(div, t0);
                        append(div, t1);
                },
                p(ctx, [dirty]) {
                        if (dirty & /*name*/ 1) set_data(t1, /*name*/ ctx[0]);
                },
                i: noop,
                o: noop,
                d(detaching) {
                        if (detaching) detach(div);
                }
        };
}

function instance($$self, $$props, $$invalidate) {
        let name='World';

        setTimeout(
                ()=> {
                        $$invalidate(0, name='you');
                },
                1000
        );

        return [name];
}

class Component extends SvelteComponent {
        constructor(options) {
                super();
                init(this, options, instance, create_fragment, safe_not_equal, {});
        }
}

export default Component;

// 3. 执行 JS 代码
/* HTMLDivElement */<div>Hello, World</div>
复制代码

当然也有例外,在 Mithril.js 中,虽然可以使用 JSX,但鼓励你编写 JS 代码

// 1. 源代码
const Hello={
  name: 'World',
  oninit: ()=> setTimeout(()=> {
    Hello.name='you';
    m.redraw();
  }, 1000),
  view: ()=> m('div', 'Hello, ' + Hello.name + '!')
};

// 2. 执行 JS 代码
/* HTMLDivElement */<div>Hello, World</div>
复制代码

有的人会觉得这样做的开发体验不太好,但有的人更希望对自己的代码有更多的控制权。这取决于他们想要解决的是哪一类的问题,缺少 transpilation 这个步骤也可能成为优点。

许多其他框架也允许在不进行 transpilation 的情况下使用,尽管很少有人这样推荐。

“现在我应该学习什么框架或者库?”

我有一些好消息和一些坏消息要告诉你

坏消息是:没有银弹。没有任何一个框架是在所有层面都优于其他框架的。任何一个框架都有它的优点和妥协。React 有它的 hook 规则,Angular 缺乏简单的 signals,Vue 的向后兼容性问题,Svelte 的伸缩性不太好,Solid.js 禁止解构,Mithril.js 不是真正的响应式,等等

好消息是:没有错误选择 —— 除非项目的要求确实受到限制,无论是在捆绑包大小还是性能方面。每个框架都可以完成工作。有些人可能需要解决他们的设计决策,这可能会使你的速度变慢,但无论如何你都能够获得可行的结果。

话虽这么说,没有框架也可能是一个可行的选择。许多项目都被过度使用 JavaScript 破坏了,而带有一些交互性的静态页面也可以完成这项工作。

现在你已经了解了这些框架和库所应用的概念,请选择最适合你当前任务的方案。不要为下个项目的框架选型而感到担心。你不需要学习所有的内容。

如果你尝试一个新的框架,我发现最有帮助的事情之一就是关注它的社区,无论是在社交媒体、Discord、github 还是其他地方。他们可以告诉你哪些方法适合他们的框架,这将帮助你更快地获得更好的解决方案。

冲吧,你可以有个人喜好!

如果你的主要目标是就业,我建议学习 React 或者 Vue。 如果你想要轻松的获取性能和控制体验,请尝试 Solid.js

但请记住,所有其他选择都同样有效。 你不应该因为我这么说就选择一个框架,而应该使用最适合你的框架。

如果你看完了整篇文章,感谢你的耐心等待。 希望对你有所帮助。 在这里发表你的评论,祝你有美好的一天

本文原文地址:https://dev.to/lexlohr/concepts-behind-modern-frameworks-4m1g

本文代码仓库地址:https://github.com/zidanDirk/concepts-behind-modern-frameworks/