Redian新闻
>
全新JavaScript框架Qwik:以独特可恢复性方式带来更快的网页应用

全新JavaScript框架Qwik:以独特可恢复性方式带来更快的网页应用

公众号新闻

作者|Bruno Couriol
译者 | 马可薇
策划 | 丁晓昀

AngularJS 的创造者 Misko Hevery 近期宣布了新网络框架 Qwik 测试版本的推出,声称无论应用程序有多大,Qwik 都能够体感快速地构建。在多数情况下,Qwik 会先下载 1KB 的 JavaScript,在需要的时候才会懒加载或预取时间处理程序和应用程序代码。

在一次名为《如何从主线程中移除 99% 的 JavaScript》的演讲中,Hevery 是如此介绍 Qwik 背后的原理的:

Qwik 的目标很简单,确保再复杂的网站都能在谷歌页面速度评分项上拿到 100/100……归根究底,就是要让互动时间尽可能地缩短……

如你所见,行业中的大多数都能在优化图片和 CSS 上做到尽善尽美,但 JavaScript 方面却又大多不怎样。因为这对于互联网上的每个人来说都是系统性的问题,我的意思是说,问题根源在于工具而不是开发者。

(用于优化 JavaScript 交付速度的)工具是 Qwik 关注的问题。

Misko 将 JavaScript 在互动时间指标上负面的表现归因于水化。水化会出现在连接服务器的渲染时。服务器接收到客户端对页面的请求后,做出对应查询以填充界面,并将结果返回给客户端。虽然对用户来说,服务器端的页面渲染显示通常要比客户端渲染的页面要快(如更快的首次内容绘制),但页面却并不是立即就可交互的,客户端还需要下载并执行页面上提供互动性的 JavaScript 脚本。

在多数框架中,这种首次交付的 HTML 与应用程序的 JavaScript 协调的过程通常被称作是水和。在水和过程中,web 应用程序框架将事件处理程序和 DOM 元素相连接,并初始化应用程序状态。水化之后用户操作将会被事件处理程序捕捉,从而使页面可交互。

Qwik 保留了服务器端的渲染,通过在服务器上运行应用程序以避免水化:将所有相关状态信息序列化,将页面内容和序列化的状态一起以 HTML 的形式发送给客户端。这些相关的状态信息包括时间监听器、内部数据结构,以及应用状态。借助序列化的状态,客户端可以接力完成服务器端没有执行完的任务。

处理交互性的 JavaScript 加载默认是延迟进行,一般是直到用户实际使用交互时才启动,也就是说一个按钮的事件处理程序最晚可以在用户点击按钮时加载。这种即时的 JavaScript 获取加上预取策略,利用浏览器的本地能力,在不影响页面交互性的前提下完成了文件的加载。

在 Qwik 文档中有详细的介绍:

Qwik 只会预取当前页面需要的代码,避免下载与静态组件相关的代码。最坏的情况是 Qwik 预取的代码量与现有框架的最佳情况相同,而在大多数情况下,Qwik 所预取的代码只会比现有框架要少。

除主线程之外的其他线程都可以做到代码预取,大多数浏览器甚至支持主线程之外的代码 AST 语法预分析。

如果用户在预取完成之前开始交互,浏览器会自动优先交互模块于其他预取模块。

Qwik 可以将应用程序分解成部分,这些分块可以按照用户交互的概率高低顺序进行下载。

Qwik 网站为开发者提供了教程、实例,以及一个供学习和尝试 Qwik 用的在线运行平台。以一个简单的计数器为例,由一个按钮和显示按钮被点击次数的文本框组成,实现方法如下:

import { component$, useStore } from '@builder.io/qwik';
export const App = component$(() => { const store = useStore({ count: 0 });
return ( <div> <p>Count: {store.count}</p> <p> <button onClick$={() => store.count++}>Click</button> </p> </div> );});

开发者可以通过 Qwik 的 component$ API 创建可恢复组件,有状态的组件可以通过 useStore API 显示其对某段状态的依赖。开发者通过在处理程序的名字后附加 $ 字符创建可恢复的事件处理程序(如前文例子中的 onclick$ )。通过这些由开发者手动添加的提示,Qwik 可以将应用程序文件打包,以实现并优化 JavaScript 的懒加载。前文的计数器程序在服务器端渲染的 HTML 如下:

<!DOCTYPE html><html  q:container="paused"  q:version="0.11.1"  q:render="ssr"  q:base="/repl/21kry8ac4hl/build/">  <html>    <head q:head>      <title q:head>Tutorial</title>    </head>    <body>      <!--qv q:id=0 q:key=AkbU84a8zes:-->      <div>        <p>          Count:          <!--t=1-->0<!---->        </p>        <p>          <button            on:click="app_component_div_p_button_onclick_8dwua0cjar4.js#App_component_div_p_button_onClick_8dWUa0cJAr4[0]"            q:id="2"          >            Click          </button>        </p>      </div>      <!--/qv-->    </body>  </html>  <script type="qwik/json">    {"ctx":{"#2":{"r":"0!"}},"objs":[{"count":"1"},0],"subs":[["2 #0 0 #1 data count"]]}</script>  <script id="qwikloader">    ((e,t)=>{const n="__q_context__",o=window,r=new Set,i=t=>e.querySelectorAll(t),s=(e,t,n=t.type)=>{i("[on"+e+"\\:"+n+"]").forEach((o=>l(o,e,t,n)))},a=(e,t)=>new CustomEvent(e,{detail:t}),c=(t,n)=>(t=t.closest("[q\\:container]"),new URL(n,new URL(t.getAttribute("q:base"),e.baseURI))),l=async(t,o,r,i=r.type)=>{const s="on"+o+":"+i;t.hasAttribute("preventdefault:"+i)&&r.preventDefault();const a=t._qc_,l=null==a?void 0:a.li.filter((e=>e[0]===s));if(l&&l.length>0){for(const e of l)await e[1].getFn([t,r],(()=>t.isConnected))(r,t);return}const d=t.getAttribute(s);if(d)for(const o of d.split("\n")){const i=c(t,o),s=b(i),a=performance.now(),l=u(await import(i.href.split("#")[0]),s),d=e[n];if(t.isConnected)try{e[n]=[t,r,i],f("qsymbol",{symbol:s,element:t,reqTime:a}),await l(r,t)}finally{e[n]=d}}},f=(t,n)=>{e.dispatchEvent(a(t,n))},u=(e,t)=>{if(t in e)return e[t];for(const n of Object.values(e))if("object"==typeof n&&n&&t in n)return n[t]},b=e=>e.hash.replace(/^#?([^?[|]*).*$/,"$1")||"default",d=e=>e.replace(/([A-Z])/g,(e=>"-"+e.toLowerCase())),p=async e=>{let t=d(e.type),n=e.target;for(s("-document",e,t);n&&n.getAttribute;)await l(n,"",e,t),n=e.bubbles&&!0!==e.cancelBubble?n.parentElement:null},v=e=>{s("-window",e,d(e.type))},w=()=>{var n;const s=e.readyState;if(!t&&("interactive"==s||"complete"==s)&&(t=1,f("qinit"),(null!=(n=o.requestIdleCallback)?n:o.setTimeout).bind(o)((()=>f("qidle"))),r.has("qvisible"))){const e=i("[on\\:qvisible]"),t=new IntersectionObserver((e=>{for(const n of e)n.isIntersecting&&(t.unobserve(n.target),l(n.target,"",a("qvisible",n)))}));e.forEach((e=>t.observe(e)))}},q=(e,t,n,o=!1)=>e.addEventListener(t,n,{capture:o}),y=t=>{for(const n of t)r.has(n)||(q(e,n,p,!0),q(o,n,v),r.add(n))};if(!e.qR){const t=o.qwikevents;Array.isArray(t)&&y(t),o.qwikevents={push:(...e)=>y(e)},q(e,"readystatechange",w),w()}})(document);</script>  <script>    window.qwikevents.push("click")</script></html>

注意,HTML 文件是通过以下方式强化的:

  • q: 属性,如q:base,q:id,q:key 。
  • 包含特定框架信息的HTML注释,如<!--qv q:id=0 q:key=AkbU84a8zes:-->
  • 序列化状态,如<script type="qwik/json"> {"ctx": ..., "objs":[{"count":"1"},0], "subs":[["2 #0 0 #1 data count"]]} </script>
  • 用于在客户端恢复应用程序执行的Qwik脚本,如<script id="qwikloader"> ... </script>,window.qwikevents.push("click")

Qwik 的在线代码运行平台可以让开发者了解到程序代码是如何被切割打包的,还是用前面的计数器为例,客户端的打包方式如下:

如截图所示,计数器的应用程序被分成了三个脚本。当用户点击按钮时,动态下载并执行其中两个脚本(Qwik 运行时间和 click 事件处理程序的代码)。

开发者可以参考 Qwik 文档以了解具体执行情况以及代码拆分的原理。Qwik 的网站给出了大量包括教程、示例,以及演示在内的信息,还有一个可互动的在线代码运行平台。Qwik 社区中同样也有一个非常简单的电子商务示例,一般对电子商务的厂商来说,页面加载速度越高收入也会增加。Qwik 的团队成员有 AngularJS 的创造者 Miško Hevery、基于 Go 语言 web 架构 Gin 的创造者 Manu Almeida、web 组件编译器 Stencil 的创造者 Adam Bradley,以及 Ionic UI 工具集。

目前,Qwik 已推出测试版,且采用 MIT 许可开源,欢迎各位在遵循 Qwik 行为准则的前提下贡献代码。

原文链接:

https://www.infoq.com/news/2022/10/qwik-fast-web-app-resumability/

声明:本文为InfoQ翻译,未经许可禁止转载。

今日好文推荐

我们这群90后,正在字节跳动“死磕”Linux内核 | 卓越技术团队访谈录

硅谷技术新焦点:摆脱缝合怪的多云设计,才是云计算的归宿

马斯克称 Twitter 可能破产;Meta 暴裁 1.1 万人,小扎承认犯了错;GitHub 年度报告:印度开发者增速超中国 | Q 资讯

动动嘴就能写代码了!Copilot测试新功能“嘿,GitHub”,告别键盘编码

微信扫码关注该文公众号作者

戳这里提交新闻线索和高质量文章给我们。
相关阅读
极简光3.0焕新出击,带来更佳全光体验马斯克:推特可能破产马斯克警告:推特可能破产调查:React 仍然是使用最广泛的前端框架,TypeScript 是优先选项打破985校史!她以独作身份投中顶刊,曾因换方向重读博士7年,科研之路也“坎坎坷坷”……苹果应用商店将迎来更多广告,QQ安卓版支持平板,华为发布EMUI 13,AirPods Max性能被砍,这就是今天的其它大新闻!中南大学湘雅医学院一本科生以独立一作发表SCI英国女王离世后的第一个圣诞!查尔斯圣诞演讲曝光,夏洛特可爱炸裂!赞扬威廉凯特,只字未提哈里夫妇!马斯克警告:推特可能破产!花440亿美元买个破推特,值得吗别拖了!加拿大"毕业工签延长"政策将结束!留学生这样操作,可恢复过期签证!当UI框架遇到JavaScript开发人员加拿大央行行长放狠话:加息幅度不足恐带来更大风险!2022 北京车展停办;马斯克警告员工:推特可能破产;深圳元宇宙研究院成立 | 极客早知道一种让AI更快的可重构处理器新一波JavaScript Web框架女王去世当天,意外收到她的回信建国后首次!华科副研究员以独作身份投中数学顶刊,曾因换方向重读博士7年艺术家专访|王光乐:以独特的形式语言、极具理性且克制的表达对生命的哲思让孩子开心就好 ???[电脑] 铝坨坨性价比之选-QwertyKeys 75%中国重启出入境,澳洲房市将迅速增长!预计2024年可恢复以往水平!NeurIPS 2022 | 利用多光照信息的单视角NeRF算法,可恢复场景几何与材质信息俄这一决定“带来更多不确定性”丽江留美三杰三部门发规AI换脸需显著标识,微博原版网页版下线,多家手机应用商店上线大屏专区,跑跑卡丁车韩服确认停运,这就是今天的其它大新闻!这个“复杂”的PPT框架图,该如何美化?参差的2023,我看到富人都在报复性消费,普通人却在报复性存钱……新一波 JavaScript Web 框架NeurIPS 2022 | 利用多光照信息的单视角NeRF算法S^3-NeRF,可恢复场景几何与材质信息女王离世后第一个圣诞:查尔斯圣诞演讲,处处女王身影,夏洛特可爱炸裂!使用USB-C的机型可确保实现更快的数据传输速度龙卷风健康快递 212有毒的“心灵”鸡汤!打破985校史!她以独作身份投中数学顶刊,曾因换方向重读博士7年
logo
联系我们隐私协议©2024 redian.news
Redian新闻
Redian.news刊载任何文章,不代表同意其说法或描述,仅为提供更多信息,也不构成任何建议。文章信息的合法性及真实性由其作者负责,与Redian.news及其运营公司无关。欢迎投稿,如发现稿件侵权,或作者不愿在本网发表文章,请版权拥有者通知本网处理。