@font-face
规则中新增了一个font-display
声明,开发者可以根据 Web Font 的加载时间来决定如何渲染或降级处理。有了 Web Font,开发者可以在项目中加入丰富的排版功能。但需要权衡的是,下载字体会耗费一些时间。而下载时间难免会受到网络状况的影响,继而干扰用户体验。说实话,如果连页面展示都费劲,谁还会在乎你用多炫的字体呢。
字体下载可能比较慢,为了减轻风险,大多数浏览器都采用了超时处理。一旦超时,就使用后备字体。理想很美好,现实很无奈,浏览器在实现上各有自己的一套。
Browser | Timeout | Fallback | Swap |
---|---|---|---|
Chrome 35+ | 3 seconds | Yes | Yes |
Opera | 3 seconds | Yes | Yes |
Firefox | 3 seconds | Yes | Yes |
Internet Explorer | 0 seconds | Yes | Yes |
Safari | No timeout | N/A | N/A |
Chrome 和 Firefox 超时时间为 3 秒,超时后使用后备字体。若字体最终勉强加载成功,它将替换后备字体,重新渲染文本。
IE 浏览器超时时间为 0 秒,也就是说,会立即渲染文本。若所请求的字体尚不可用,则使用后备字体。一旦请求字体可用,将重新渲染文本。
Safari 没有超时行为(或者说,至少在基准网络超时之前什么也没干)。
更糟糕的是,这些规则对应用造成的影响,在很大程度上不受开发者控制。关注性能的开发者也许更乐意使用后备字体,使网页内容更快地展现,当更美观的 Web Font 下载完成后再加以使用。使用 Font Loading API 之类的工具,可以覆盖浏览器的某些默认行为,提升性能。但这将引入额外的 JavaScript 代码 —— 要么把代码内联到页面中;要么请求外部文件,而这可能带来额外的 HTTP 延时。
CSS 工作组为此提出了新方案:为@font-face
增加新的font-display
声明,用于控制字体下载完成之前的渲染行为。
与一些浏览器目前使用的字体超时行为类似,font-display
将字体下载过程划分为三个阶段。
首先是字体阻塞阶段(font block period)。在此期间,如果字体未完成加载,则尝试使用它的任何元素必须以不可见的后备字体形式呈现;否则正常使用该字体。
紧接着字体阻塞阶段的是字体交换阶段(font swap period)。在此期间,如果字体未完成加载,则尝试使用它的任何元素必须使用后备字体进行渲染;否则正常使用该字体。
紧接着是字体故障阶段(font failure period)。此时若字体未完成加载,则将其标记为下载失败,并使用常规后备字体;否则正常使用该字体。
理解以上三个阶段,根据字体是否下载成功、何时下载完成等情况,就可以可以使用font-display
来决定如何渲染字体了。
要使用font-display
,请在@font-face
规则中添加代码:
@font-face { |
目前,font-display
支持auto | block | swap | fallback | optional
五种值。
auto
使用的字体展示策略与浏览器一致。当前,大多数浏览器使用的默认策略类似block
。
block
给予字体一个较短的阻塞时间(大多数情况下推荐使用 3s)和无限大的交换时间。换言之,如果字体未加载完成,浏览器将首先绘制“隐形”文本;一旦字体加载完成,立即切换字体。为此,浏览器将创建一个匿名字体,其类型与所选字体相似,但所有字形都不含“墨水”。使用特定字体渲染文本之后页面方才可用,只有这种情况下才应该使用 block
。
使用 swap
,则阻塞阶段时间为 0,交换阶段时间无限大。也就是说,如果字体没有完成加载,浏览器会立即绘制文字,一旦字体加载成功,立即切换字体。与 block
类似,如果使用特定字体渲染文本对页面很重要,且使用其他字体渲染仍将显示正确的信息,才应使用 swap
。Logo 文字就很适合使用 swap
,因为以合理的后备字体显示公司名称仍将正确传递信息,而且最终会以官方字体的样式展现。
使用 fallback
时,阻塞阶段时间将非常小(多数情况下推荐小于 100ms),交换阶段也比较短(多数情况下建议使用 3 秒钟)。换言之,如果字体没有加载,则首先会使用后备字体渲染。一旦加载成功,就会切换字体。但如果等待时间过久,则页面将一直使用后备字体。如果希望用户尽快开始阅读,而且不因新字体的载入导致文本样式发生变动而干扰用户体验,fallback
是一个很好的选择。举个例子,正文文本就符合这个条件。
使用 optional 时,阻塞阶段时间会非常小(多数情况下建议低于 100ms),交换阶段时间为 0。与 fallback
类似,如果字体能够为页面效果增色不少,但并非特别重要时,使用 optional
正好。使用 optional
时,将由浏览器来决定是否开始下载字体。可以不下载,也可以给予字体较低的优先级,一切取决于浏览器是否认为对用户最有利。当用户处于弱网络下,这是非常有用的,下载字体可能并非对资源最好的利用。
启用 Chrome 49 桌面版、Opera 或 Opera for Android的 “Experimental Web Platform Features flag” 后可以使用 font-display
。(译者注:该文发表于 2016 年。根据译者 2017 年 8 月 4 日访问caniuse获取的信息,Chrome 60 正式引入 font-display
。)
点击链接可以查看font-display
的效果。对关注性能的开发者来说,font-display
可是相当有用的工具啊!
JavaScript 工具包(toolkit)和框架所做的大量工作,都集中于尝试修复、规范或优化浏览器的功能实现。此类工作需要做出许多假设,这些假设包括:问题是什么,开发人员将如何使用我们的工具,以及我们对未来的期望。
但这些假设经常是错误的。更有甚者,在很长一段时间内,这些选择可能貌似正确,直至某天我们被问题反咬一口。在这个无知的幸福时期当中,我们的工具包可能变得相当受欢迎,并成为大型复杂代码库的重要组成部分。
事件冒泡允许源自子节点的事件向父级节点“冒泡”(bubble)。这种行为导致 JavaScript 开发者使用松散的设计模式来识别我们所关心的接收事件的节点 —— 通常使用 CSS 选择器 —— 同时将事件监听器添加到该节点的父级节点上。
一旦这种模式进入工具包之中,设计 API 时须做出一些假设。在开始阶段,这些假设主要围绕性能与效率展开。
事件代理(Event Delegation)是处理事件的实际方法之一。然而,这种方法论适用于所有项目吗?实际上,更好的问题可能是,每个工具包的所基于的假设是否与你的项目需求相符。要想知道某个 API 是否适合当下项目,就要了解这些工具是建立在哪些假设之上的,并且理解每个工具包如何解释它们。
一起来看看,在思考如何有效管理 DOM 事件时可能会产生的一些假设。
在你能够提出 API 存在的继发原因之前,不要创建新的 API。随着浏览器厂商们对运行时的投入增加,你的功能实现总有一天会比原生实现慢。我所在的 SitePen 有一个项目依赖于数组拼接(splice)速度。我们发现,在某些情况下,手动操作索引和数组长度能够带来显著的性能提升。但我们无法定位到特定浏览器、浏览器版本或平台,因为无法进行运行时功能测试以确定我们的实现是否比原生 API 快。
保持谨慎,确保已收集到足够的信息,可以降级使用原生实现 —— 无论是已存在的,还是理想情况下可能存在的。这项工作的另一个名字叫“预防过时”(future proofing)。在某些情况下,你可能会使用必需参数超出绝对需要的 API,但如果它l能够保证轻松地过渡到更优秀的原生 API,那么完全可以如此。一个很好的例子是最终获得原生支持的 querySelectorAll
API,之前许多开发人员假设这种事永远不会发生。
事件代理可能会以数种方式呈现。例如两种特殊情况:大量节点上的少量事件,以及少数节点上的大量事件。如果针对其中之一进行优化,则可能会为另一个带来明显的瓶颈。虽然使用事件代理可能只需要向单个节点添加一个事件侦听器,但识别触发回调的节点的复杂方法对性能的影响可能不成比例。快速触发大量事件(例如鼠标移动或滚动事件)正是使用事件代理的场景。
在考虑事件代理时,很容易认为我们只需要关心用户交互。这可能导致我们假设节点始终是文档的一部分,然后开始思考,为何不在 document 对象上添加单个事件处理程序呢?DOM 事件并非总是用户交互的结果 —— 我们也有人为事件、自定义事件以及加载事件等。如果想要监听的节点不在文档中,而监听器却绑定在 document 对象上,我们永远得不到通知。如果在 API 中无法区别监听器是添加到 document 上,抑或是添加到我们所传递的参数上,则能够理解为什么会出现这种情况。
如果一个工具包提供一个仅用于支持代理的事件处理 API —— 需要父级节点和标识子节点的选择器 —— 则无法将事件监听器直接添加到某个节点。即使是使用 CSS 选择器,也引入了更高级的功能,可以轻松地使用另一种选择器语法或简单函数。
如上所述,DOM 事件冒泡是事件代理模式存在的前提。但是了解完整规范所涉及的内容之后,你会发现,事件冒泡是可以取消的。你的实现可能会将 stopPropagation 方法为空函数的自定义事件传递给回调函数;或者,你可能只会记录问题,并限制事件代理 API 的使用。这两种方法都有问题,但是如果你打算像为 document 对象事件处理程序那样工作,添加大量可取消的事件层可能放大副作用。
一旦代码编写完成,很可能就会弃而不顾。但浏览器正在以我们无法想象、预测的方式向前发展,我们在编写代码时所做的假设可能会被证明是错误的,尽管我们尽了最大的努力。
为什么要在项目中使用事件代理?
原生实现太慢了吗?对现代浏览器来说不太可能。
是否有更好的 API 来执行事件代理?目前还没有 —— 如果你需要事件代理,这是一个很好的模式。
该工具包的性能优化是否符合项目需求?如果它专注于特殊情况,可能不会。
工具包的实现中有没有什么不适用于你的项目的内容?阅读文档,这些通常都会标出。
是否有副作用?遇到错误前你可能不会发现这一点,所以要特别注意。
人们在不了解创作假设的情况下,所有设计模式都有成为反模式的风险,所以对项目中使用的任何新工具都应当回答同样的问题。如果你所做的似乎是在抄近路,要特别小心。谨慎、多加思考,才能使项目发光。
]]>若你已潜心皈依 MVP,并坚信在一个月的时间足以完成一个能够创造价值且达到安全要求的产品 —— 听我一句劝,三思而行,不要急着做“产品原型”。也许在阅读完本文列出的清单之后,你会承认确实忽略了很多关键的安全问题。至少不要欺骗那些潜在用户,让他们知道眼下的产品并不完整,而只是一个并非绝对安全的原型。
这个清单很简单,远远谈不上完备。我开发安全 Web 应用的经验已逾 14 年,在这段时间中也曾遭遇痛苦。痛定思痛,也学到不少东西,这份清单就包括了其中一些问题。创建 Web 应用程序时,望你认真思考。
如果你也有一些可以添加到本清单中的内容,请在评论区留言告知。
对用于识别用户身份的数据、其他敏感数据(如访问令牌、邮箱地址或订单详情等)进行加密处理。
若数据库支持低成本空闲加密(如 AWS Aurora),请启动加密,保护磁盘数据。确保所有备份都已加密。
对数据库访问用户帐户使用最小权限。不要使用数据库 root 帐户。
用专门的密码存储方式保存密钥。不要使用硬编码。
只使用 SQL 预处理语句,严防 SQL 注入。举例来讲,请使用 npm-mysql2 替代 npm-mysql。
请确保生产环境中的所有版本的软件的所有部件都经过漏洞扫描。包括 O/S,库,包。这应该在 CI-CD 过程中自动化。
开发环境的安全,与生产环境同等重要。在安全的隔离的开发环境中构建软件。
务必使用合适的加密工具(如 bcrypt)对所有密码进行 hash 处理。千万不要自己编写加密算法,使用合适的随机数据对加密方法进行初始化。
使用简单而恰当的密码规则,鼓励用户使用较长的随机密码。
对所有服务提供商的登录,使用多因素身份验证。
确保针对 API 的 DOS 攻击不会使网站瘫痪。至少限制对那些较慢 API (如登录、令牌生成等)的请求频率。
对于由用户提交的数据、请求等,强制进行大小、结构等方面的合理限制。
对于 DDOS, 使用全局缓存代理服务(如 CloudFlare)减灾。遭遇 DDOS 攻击后即启用,而在平时则可用于 DNS 查询。
全站使用 TLS。不要局限于登录表单。
对 cookie 设置 httpOnly 和 secure,并使用 path 和 domain 进行限定。
使用 CSP,避免 unsafe-* 之类的后门。配置过程虽痛苦,但相当划算。
在客户端响应中使用 X-Frame-Option、X-XSS-Protection 响应头。
使用 HSTS 响应,强制只允许 TLS 访问。将所有 HTTP 请求重定向到 HTTPS。
所有表单使用 CSRF token;并使用 SameSite Cookie 为新浏览器一次性解决 CSRF 问题。
确保公开 API 中没有可枚举资源。
确保用户在使用 API 前已通过认证、授权。
验证客户端输入,为用户提供快速反馈,但不要信任用户输入的内容。
使用服务器上的白名单验证用户输入的最后一位。绝对不要直接将用户内容注入到响应中。切勿在 SQL 语句中直接使用用户输入的数据。
确保打开所有服务的最小端口(minimum ports)。尽管采用模糊策略实现安全等于没有保护,但非标准端口将增加攻击难度。
后端数据库和服务交由私有 VPC 托管,任何公共网络都不可见。配置 AWS 安全组、对等连接 VPC 时,要格外小心,服务可能在无意中被 VPC 公开。
从单独的 VPC 和对等连接 VPC 中独立出逻辑服务,以提供业务间通信。
确保所有服务只接收来自一组最小 IP 地址集合的数据。
限制出站 IP 和端口流量,尽可能减小 APT 和 botification。
始终使用 AWS IAM role,不要使用 root 凭证。
对运维、开发只开放最小访问权限。
定期修改密码和访问密钥。
确保能够进行热升级。确保可以完全自动化快速更新软件。
使用 Terraform 之类的工具创建所有基础设施,而不是通过云端控制台。基础设施应该被定义为“代码”,并能通过按钮来重建。对于任何在云中手动创建的资源保持零容忍 —— Terraform 可以对配置进行核查。
对所有服务使用集中式日志记录。无需通过 SSH 访问或检索日志。
除一次性诊断外,不要通过 SSH 进入服务。使用 SSH,通常意味着没有将重要任务自动化处理。
不要永远在任何 AWS 服务组上开放 22 端口。
创建immutable hosts,而不是那些需要不断修补、升级的长寿命服务器。(见Immutable Infrastructure Can Be More Secure).
对设计、实现进行核查。
进行渗透测试 —— 自己尝试黑掉自己的程序,同时也有他人在进行测试。
构建威胁防御模型。列出可能的威胁与对手,并按照优先次序排列。
进行安全事故演练。也许有一天能派上用场。
试用了众成翻译正在内测的新版翻译工具,借助谷歌翻译 API,速度比以前快多了。但也有一些缺憾,较以往逐句手动翻译字斟句酌不同,往往会迁就自动翻译结果,时间都花在调整工作上了。
以下为正文:
本文转载自:众成翻译
译者:文蔺
链接:http://www.zcfy.cc/article/2719
原文:https://medium.freecodecamp.com/so-whats-this-graphql-thing-i-keep-hearing-about-baf4d36c20cf
如果你像我一样,在听到一个新技术的时候,可能会经历三个阶段:
又来一个 JavaScript 库?jQuery 在手,天下我有!
嗯,要不还是去看看这个久闻大名的东西吧…
救命啊!我必须立马学会它,否则就要死在沙滩上了!
身处这个快节奏时代,保持理智需要一定的诀窍:处于第二、三阶段之间,兴趣刚刚激发,趁着仍在时代前列,开始学习新东西吧。
所以,现在正是学习 GraphQL的好时机。
简而言之,GraphQL 是一种描述如何请求数据的语法,通常用于客户端向服务器请求数据。GraphQL 有三个主要特点:
那么如何开始学习 GraphQL 呢?它在实践中长什么样子?怎么开始使用它?请继续阅读,寻找答案吧!
GraphQL 起源于又大又老的 Facebook 应用。不过,即便是更简单的应用程序,也经常受到传统 REST API 的限制。
举个栗子,假设你需要展示帖子列表,每个帖子下面展示一个带有用户名和头像的点赞列表。很简单,调整 API,加入一个点赞用户数组:
然而,我们要做移动 APP 了,结果你会发现,加载额外数据拉低了整体速度。所以这时候需要两个端点(endpoints),一个包含点赞信息,一个不含点赞信息。
看热闹不嫌事大,再添加一个因素:帖子存储在 MySQL 数据库中,点赞数据存储在 Redis 中!现在该怎么办呢?
将这种情况推广到管理大量数据源、 API 客户端的 Facebook 身上,也就不难想象,为什么老式 REST API 开始捉襟见肘了。
Facebook 提出的解决方案在概念上非常简单:不再使用多个“愚蠢”端点,而是使用可以处理复杂查询、根据客户端需求拼合数据的单个“智能”端点。
实际上,GraphQL 层位于客户端和一个或多个数据源之间,按照你的指示接收客户端请求,然后获取必要的数据。困惑不解?下面打打比方!
旧式 REST 模型就好像先订购比萨饼,然后请商店送货上门,接着再打电话给干衣店上门取衣服。三个店铺,三个电话。
而 GraphQL 就像一个私人助理。只需要给出三个地方的地址,然后告诉 Ta 你想要什么(“衣服要干洗,一个大比萨饼,两打鸡蛋” ),然后就可以坐等结果了。
换言之,GraphQL 建立了一套用来与神通广大的个人助理交流的标准语言。
Google 图片的结果显示,典型的个人助理是八臂金刚。
在实践中,GraphQL API 围绕三个主要构建块组织:模式(schema)、查询(query)、解析器(resolver)。
你对 GraphQL 个人助理的发出的请求就是查询,如下所示:
query { |
通过query
关键字声明新查询,然后请求一个名为stuff
的字段。GraphQL 查询的好处是支持嵌套字段,所以我们可以更进一步:
query { |
如你所见,查询的客户端无需关心数据来自哪个“店铺”。按需请求,让 GraphQL 服务器负责其余的事。
值得注意的是,查询字段也可以指向数组。例如,下面是查询帖子列表时的一种常见模式:
query { |
查询字段也支持参数。如果想显示某个特定帖子,可以在post
字段中添加一个id
参数:
query { |
最后,如果想使该id
参数动态化,可以定义一个变量,然后在查询中重用它(请注意,我们也在这里命名了查询):
query getMyPost($id: String) { |
使用 GitHub 的 GraphQL API Explorer 可以很好地实践前面的例子。尝试以下查询:
query { |
GraphQL 自动补全功能
请注意,当我们尝试在描述下面输入新的字段名称时,IDE 将通过 GraphQL API 自动补全字段名称。漂亮!
The Anatomy of a GraphQL Query
你可以在 《解析 GraphQL Query》 这篇优秀文章中了解有关GraphQL 查询的更多信息。
如果没有干洗店的地址,即使是世界上最好的个人助理也无法完成干洗的任务。
同样,GraphQL 服务器也不会知道如何处理传入的查询,除非我们使用解析器来告诉它怎么做。
解析器告诉 GraphQL 如何获取、在哪里获取与给定字段相对应的数据。例如,前面的post
字段的解析器可能看起来像下面这样(使用Apollo 的 GraphQL-Tools 模式生成器):
Query: { |
我们将解析器放在 Query 上,因为我们希望直接在 root 级别查询帖子。但也可以使用子字段的解析器,例如帖子的author
字段:
Query: { |
还需要注意,解析器不限于返回数据库文档。例如,也许你想添加一个commentsCount
字段:
Post: { |
在这里,关键概念是通过使用 GraphQL,API schema、数据库schema 被解耦。换言之,数据库中可能没有任何author
或commentsCount
字段,但我们可以通过解析器的力量来“模拟”它们。
如你所见,我们可以在解析器中编写所需的任意代码。这就是为什么你也可以让它们修改数据库的原因,在这种情况下,它们被称为突变(mutation)解析器。
所有这些好东西都可以通过 GraphQL 的类型化架构系统实现。我今天的目标是给读者一个简短的概述,而不是详尽的介绍,所以我不会在这里涉及任何细节内容。
话虽如此,如果你想了解更多,建议查看 GraphQL 文档。
休息一下,回答几个常见问题。
那位朋友,后面的观众!对,就是你。貌似你想问一些事情。来吧,别害羞!
没太多关系,真的,GraphQL 与 Neo4j 等图形数据库没有任何关系。“Graph” 来源于通过使用字段、子字段爬取 API 图形的想法;而“QL”则代表“查询语言”。
如果你还没有碰到 GraphQL 所着力解决的 REST 痛点,那么我会说这是一件好事!
在 REST 上使用 GraphQL 可能不会影响应用程序的整体用户体验,所以切换到它并非生死攸关的问题。话虽如此,我还得建议你,在有机会的时候,可以在一个小型项目上尝试使用 GraphQL。
是的,你可以!GraphQL 只是一个规范,你可以将其与任何平台上的任何库、客户端(例如,Apollo拥有WebQL的GraphQL客户端,iOS,Angular等)一起使用,也可以自己调用 GraphQL 服务器。
再次声明,GraphQL 只是一个规范,这意味着你可以使用 GraphQL 实现,而不运行任何由 Facebook 编写的代码。
而 Facebook 的支持,对 GraphQL 生态系统肯定是加分项,在这一点上,我相信社区足够大,即使 Facebook 停止使用,GraphQL也将蓬勃发展。
解析器是我们自己编写的,所以可以解决该级别的任何安全问题。
例如,如果让客户端指定limit
参数来控制它接收到的文档数量,那么您可能希望对该号码加以限制以避免 DoS 攻击,以免客户端会一遍又一遍地请求数百万个文档。
一般来说,至少需要两个组件来运行 GraphQL 驱动的应用程序:
请继续阅读,了解更多相关信息。
现在,我们对 GraphQL 的工作有了很好的了解,再来谈谈这个领域的一些主要玩家。
我们需要的第一块砖是 GraphQL 服务器。GraphQL 只是一个规范,所以这为一些相互竞争的实现敞开了大门。
这是 GraphQL 的原始参考实现。可以与 express-graphql 一起用来创建 API 服务器。
Apollo 团队还拥有自己的一体化 GraphQL 服务器实现。它不像原始实现那样广为流传,但有着很好的文档、支持,并且迅速获得成功。
GraphQL.org 上有各种其他平台(PHP,Ruby 等)的 GraphQL 实现列表。
在技术上,虽然可以直接查询 GraphQL API,而无需专门的客户端库,但它绝对可以让你的生活更轻松。
Relay 是 Facebook 的 GraphQL 工具包。我没有用过,但是据悉它主要是针对 Facebook 自己的需求量身定制的,并且可能对于大多数使用场景来说有些过度设计。
Apollo 是后来居上者。典型的 Apollo 客户端主包括以下两部分:
请注意,在默认情况下,Apollo-client 使用 Redux 存储其数据,这是非常好的,因为 Redux 本身是一个拥有丰富生态系统的非常成熟的状态管理库。
Apollo Devtools Chrome 扩展
尽管 GraphQL 相当新颖,但已经有一些前景看好的开源应用程序可以利用。
首先是免责声明:我是 VulcanJS 的主要维护者。我创建了 VulcanJS,让人们可以利用 React / GraphQL 技术栈的优势,而无需编写太多的样板。你可以将其视为“现代网络生态系统的 Rails”,因为它可以在几个小时内构建 CRUD 应用程序(如 Instagram 克隆)。
Gatsby 是一个 React 静态站点生成器,目前由 GraphQL 1.0 驱动。这个乍看奇怪的组合,实际上是相当强大的。在构建过程中,Gatsby 可以从多个 GraphQL API 获取数据,然后使用它们创建一个完全静态的客户端 React 应用程序。
GraphiQL 是用于查询 GraphQL 端点的非常方便的浏览器端 IDE。
GraphiQL
由于 GraphQL 查询的嵌套特性,单个查询可以轻松触发数十个数据库调用。为了避免发生性能问题,我们可以使用由 Facebook 开发的批处理和缓存库,如 DataLoader。
Create GraphQL Server 是一个命令行实用程序,可以轻松快速构建由 Node 服务器和 Mongo 数据库驱动的 GraphQL 服务器。
与 Create GraphQL Server 类似,GraphQL-up 可以让我们快速启动由 GraphCool 服务驱动的 GraphQL 后端。
最后,还有一些 “提供 GraphQL 后端服务” 的公司为我们处理整个服务器端的事情,这可能是试水 GraphQL 生态系统的好方法。
结合 GraphQL 和 AWS Lambda 的灵活的后端平台,提供免费开发者计划。
另一个 GraphQL 后端服务,也有免费计划。它提供了与 Graphcool 大量相同的功能。
已经有很多地方可以学习 GraphQL 了。
GraphQL 官方网站有一些很好的起步文档。
LearnGraphQL 是由 Kadira 的同学创建的互动课程。
LearnApollo 是由Graphcool 制作的免费课程,LearnGraphQL 的接力者。
Apollo 博客中有大量关于 Apollo 和 GraphQL 的详细的、写得很好的帖子。
关于 Graphcool 团队策划的 GraphQL 简报。
另一个很好的简报,除了 GraphQL 之外,它还提供 React 和 Meteor 的消息。
一个教程系列,教你如何使用 GraphQL 构建对讲机。
GraphQL 链接、资源的详尽列表。
那么如何将新获得的 GraphQL 知识付诸实践呢?可以试试下面这些:
如果你已经熟悉 Next.js 和 React,这个示例将允许你使用Graphcool 设置 GraphQL 端点,然后使用 Apollo 进行查询。
Vulcan 教程将带你在服务器和客户端上设置一个简单的 GraphQL 数据层。由于 Vulcan 是一个一体化平台,因此无需任何设置即可开始使用。如果需要帮助,请不要犹豫,来访问我们的 Slack 频道!
Chroma 博客有一个构建遵循组件驱动的开发方法的 React / GraphQL 应用程序的六部分教程。
乍看 GraphQL 可能很复杂,因为它是一种跨领域技术。但是,如果你花时间了解基础概念,我想你会发现很多东西只是有意义的。
所以无论你是否真的使用它,我相信值得花时间熟悉 GraphQL。越来越多的公司和框架正在采用它,并且在未来几年内可能最终成为网络的关键组成部分之一。
同意?不同意?有问题吗?在评论中告诉我。如果你喜欢这篇文章,请考虑分享它!
感谢 Tom Coleman 和 Jonas Helfer。
]]>在 Chrome 中新建标签页,输入网址的时候,浏览器会根据书签、历史记录进行自动补全,通过 Tab 键可以在浏览器提供的补全选项中切换。
有时可以发现,点击 Tab 的时候,地址栏右侧会出现“按 tab
键可通过 XX 进行搜索”的提示。
再次点击 Tab,地址栏中文字,就变成了一段蓝色文字:“使用 XX 搜索”。
这时候就可以直接在地址栏中输入你要搜索的关键词,然后点击回车键,页面就会直接跳转到搜索页。
很多网站都能通过这种方式进行快捷搜索。常用的如谷歌、百度这些搜索引擎,后来发现,知乎、掘金、Github 等等网站也能实现。
奇舞周刊网站提供了技术文章的搜索功能,和一般搜索有所不同的是,奇舞周刊的 url 是将搜索词放在 url 的 hash 部分的。这样是否也能实现呢?
带着这个问题,首先通过搜索引擎找答案。找到了,是一个叫做 OpenSearch 的方案。
解决方案很简单,在 HTML 文档的 head
部分添加一个 <link>
标签:
<link |
接着创建一个 XML 文件。文件内容如下:
<OpenSearchDescription xmlns="http://a9.com/-/spec/opensearch/1.1/"> |
上面这段是最简单的配置。ShortName
相当于前面举例中的 “XX”。Description
字段目前尚未在实际操作的 UI 呈现中看到。Url
字段的关键在 template
属性,该属性值是搜索页地址模板。
需要注意的是,这个 XML 文档必须放在当前域名下的 server 上。关于 OpenSearch XML 文档的各个地址配置,请参考 OpenSearch 规范。
此外,根据 Chromium 的文档描述,还有一种方式可以实现 “Tab to search”,即当网站中存在一个用户提交的表单时,Chrome 会自动将该网站添加到可搜索站点列表中。该表单必须满足以下几点条件:
onsubmit
;input
元素;input
元素必须保持默认状态。最后需要注意一点,无论是哪种方式,在地址栏中选择的网址不能带有路径,这时候才能完成 “Tab to search” 的任务。
更多信息,请查阅参考文档。
浏览器 | 备注 |
---|---|
Safari 10.1 | (无) |
Chrome Canary 60 | 打开 chrome:flags 启用“实验性网络平台功能” |
Firefox 54 | 打开 about:config 启用 dom.moduleScripts.enabled |
Edge 15 | 打开 about:flags 启用“实验性 JavaScript 功能” |
<script type="module"> |
// utils.js |
只需为 script
元素添加 type=module
属性,浏览器就会把该元素对应的内联脚本或外部脚本当成 ECMAScript 模块进行处理。
目前已经有一些 很棒的关于 ECMAScript 模块的文章了,不过我还是想分享一些和浏览器相关的东西,它们都是我在测试代码、阅读规范的过程中学习到的。
// 支持 |
有效的路径符号应当符合以下条件规则之一:
new URL(moduleSpecifier)
的时候才不会报错。/
开头。./
开头。../
开头。其他形式的符号被保留下来,未来将用于其他功能(如引入[import]内置模块)。
nomodule
属性向后兼容<script type="module" src="module.js"></script> |
支持 type=module
的浏览器将会忽略带有 nomodule
属性的 script
标签。这意味着我们可以为支持模块的浏览器提供模块形式的代码,同时为那些不支持模块的浏览器提供降级处理。
nomodule
(issue)。nomodule
(issue)。nomodule
,但在最新的技术预览版中已经解决了此问题。对于 10.1 来说,有一个相当棒的解决方案。<!-- 这个脚本会在… --> |
Live demo。脚本执行顺序为 2.js
, 1.js
, 3.js
。
获取脚本会导致 HTML parser 阻塞,这简直太太太太恶心了。对正常的脚本,我们可以使用 defer
属性来防止阻塞,脚本将延迟至文档解析完毕后执行,同时保持与其他使用 defer
的脚本之间的执行顺序。模块脚本的默认行为与 defer
相同 —— 无法使模块脚本阻塞 HTML parser。
模块脚本与使用 defer
的正常脚本使用相同的执行队列。
<!-- 这个脚本会在… --> |
Live demo。执行顺序依次为 1.js
、内联脚本、内联模块、2.js
。
正常的内联脚本会忽略 defer
属性,而内联模块则总是延迟执行,无论是否引入其他内容。
async
对内联、外部模块同样适用<!-- 脚本将会在其引入的模块加载完成后立即执行 --> |
Live demo。先完成加载的脚本先执行。
与正常脚本相同,带有 async
属性的脚本在下载时不会阻塞 HTML parser,一旦加载完毕,立即执行。不同的是,async
对内联模块也同样适用。
使用 async
时,脚本的执行顺序可能会和它们在 DOM 中出现的顺序不尽相同。
async
(issue)。<!-- 1.js 只执行一次 --> |
引入同一个模块多次的时候,模块只会执行一次。这对 HTML 中的模块脚本同样适用 —— 在同一个页面中,URL 相同的模块只会执行一次。
<!-- 模块不会执行,因其未通过 CORS 检查 --> |
与正常脚本不同,模块脚本(及其引入的脚本)是通过 CORS 获取的。这意味着,跨域模块脚本必须返回类似 Access-Control-Allow-Origin: *
这样的有效的响应头。
<!-- 请求脚本时会携带相关凭证 (如 cookie) --> |
在请求同源的情况下,多数基于 CORS 的 API 都会发送凭证信息(credentials,如 Cookie)。但 fetch()
和模块脚本恰恰例外 —— 除非手动声明,否则是不会发送相关凭证的。
对于一个同源的模块脚本,可以为其添加 crossorigin
属性(这看起来挺怪的,我已经在规范中提出这个问题了),这样在请求时就可以携带相关凭证了。如果你还想将凭证发给其他域,请使用 crossorigin="use-credentials"
。需要注意的是,接收凭证的域必须返回 Access-Control-Allow-Credentials: true
的响应头。
此外,还有一个与“模块只会执行一次”这条规则相关的问题。浏览器是通过 URL 来区别不同模块的,所以如果你先请求了一个模块而不携带任何凭证,紧接着又携带凭证信息去请求该模块,那么第二次得到的依然是不携带凭证的请求所返回的模块。这正是我在上面代码中的 URL 后面加上“?”的原因。
crossorigin
属性,Safari 在请求同源脚本时也不会携带凭证信息(issue)。crossorigin
属性,则又不会携带 (issue)。Firefox 是唯一正确实现这一点的浏览器 —— 好样的!
不同于普通脚本,对于通过 module 引入的脚本,服务器必须返回合法的 MIME type,否则脚本将不会执行。
以上就是我目前所学习到的所有内容。浏览器开始支持 ES6 模块,简直太开心啦~
]]>今天一早,朋友圈被一个名为 Prepack 的工具刷爆了。
周刊君很好奇地看了下官网(prepack.io),这么厉害的工具,有必要第一时间向大家介绍一下(还能不能好好过个青年节了)。
看 “Prepack” 这个名字就能大概知道,它的作用,肯定是在发布前(“pre”)对代码动了些什么手脚。官网介绍 Prepack 是“一个使 JavaScript 跑得更快的工具”。那么它到底做了些什么厉害的事情呢?
根据官网首页信息,Prepack “能够消除那些可以本可以在编译(compile)阶段完成的运行时计算”,将代码中的某些部分替换为一系列赋值语句,这样一来就可以省去很多中间计算和对象的分配工作。
下面是 Prepack 官网给出的一个例子:
(function () { |
经过 Prepack 的处理,上面这段代码变成了下面这样:
(function () { |
原来的 .forEach
调用没有了,一系列的中间转换过程也不见了。借用官网的说法,“多数的计算都在 Prepack 编译时进行了预初始化”。
再举一个 Fibonacci 的例子:
// 处理前 |
据官网介绍,Prepack 的实现依赖以下几个方面:
Prepack 是在 AST 这一层级对代码进行操作的。通过 Babel 来解析源码,并生成优化后的代码。关于 Babel 与 AST,周刊君推荐两篇文章:
Prepack 的核心部分是“一个大致兼容 ECMAScript 5 的编译器”(an almost ECMAScript 5 compatible interpreter),而这个编译器是通过 JavaScript 实现的。这个编译器可以追踪、撤销包括对象变化在内的所有的操作。这样一来就能进行推理性的优化(speculative optimizations)。
除了计算具体值,Prepack 的编译器还可以操作抽象值,而这些抽象值通常都来自于代码与环境的交互。如 Date.now
所返回的就是抽象值。此外,根据官网首页的描述,还可以通过 __abstract()
这样的辅助工具函数,手动插入抽象值。Prepack 会追踪发生在抽象值之上的操作,如果有分支情况,则会对所有可能性进行探查。
官网介绍说,“因此,Prepack 为 JavaScript 实现了一套符号执行引擎”。为了方便大家理解,周刊君特地引用了维基百科上的一段话:
符号执行 (Symbolic Execution)是一种程序分析技术。其可以通过分析程序来得到让特定代码区域执行的输入。使用符号执行分析一个程序时,该程序会使用符号值作为输入,而非一般执行程序时使用的具体值。在达到目标代码时,分析器可以得到相应的路径约束,然后通过约束求解器来得到可以触发目标代码的具体值。
官网的描述有点复杂。关于抽象解释,请移步维基百科。有兴趣的可以去官网阅读原文。
初始化阶段结束时,Prepack 会捕获最终的堆。按顺序遍历堆,生成新的代码,创建、链接堆中的可及对象。
如前所述,堆中的一些值可能是对抽象值进行计算的结果。Prepack 将会根据这些值生成执行计算的代码,其计算过程与源程序相同。
需要注意的是,Prepack 并未完整模拟浏览器、Node 环境,Prepack 对 document
和 window
并没有多少了解。对这样一些属性求值的时候,将会得到 undefined
。如果需要在这样一些地方使用 Prepack,必须通过一些工具函数实现。
# 安装 |
除了上面的基本用法之外,还支持如 sourceMap 等更多选项。这里就不一一介绍了。
此外,官方介绍称,“Prepack 目前仍处于早期开发阶段,尚未做好投入生产环境的准备”。不过,还是响应 Prepack 的号召吧,“try it out, give feedback, and help fix bugs”。
想要进一步关于该项目的发展计划,可以访问官网首页的 Roadmap 部分。
Closure Compiler 同样会对 JavaScript 代码进行优化。Prepack 比 Closure Compiler 走得更远的地方在于执行了初始化阶段的全局代码,展开循环、递归。官网的一个说法是,“Prepack 着眼于运行时性能,而 Closure Compiler 的重点在于代码体积”。
]]>