• 我们最喜爱的五大web新标准
    本文来源知乎网由于我们为我们的客户创造开源软件、开发很多应用并改进他们的质量,我们一直在寻找能提高我们所开发的软件质量的东西。部分工作是在盯着一些让我们眼花的提议和新兴的标准,从而找到高效可用的部分。在这里,我们将探索已经开始使用的或者在将来工作中考虑用到的五个新兴的web标准。CSS变量/自定义属性在过去的十多年里,web工程师一直在用变量来创建和管理复杂的CSS系统,他们仍然是驱动对CSS预处理器产生需求的主要特性的一种,像Sass、less、Stylus。用得好的话,通过规范所有用来描述颜色、文字、边距等的值,他们能极大地增强大型代码库的可维护性。随着时间的推移,预处理器变量已经随着设计或习惯融合了一系列可共享的特性。 [*]有前缀:例如,为了防止与已有的CSS关键词冲突的 $或 @ [*]有作用域:在.container中可以用.container > .child访问$bgColor,但反过来就不成立了。 [*]可以被重写: $bgColor: blue;.container { $bgColor: red; background-color: $bgColor; /* will be red */} 原生的CSS变量(或“自定义属性”)采用了所有这些约定,这使得转换成原生的支持简单直观。CSS变量必须以两个破折号为前缀:--,他们的作用域为定义的选择器内,可以被后代继承,可能在后代中被重写。例如::root { --bgColor: periwinkle;}.container { background-color: var(--bgColor); /* will be periwinkle */}.container .child { --bgColor: lime; background-color: var(--bgColor); /* will be lime */} 在这个例子里,CSS变量和预处理器变量之间有一些明显的差异: [*]CSS变量在使用的时候必须被var()包裹 [*]--前缀与已有的预处理器使用的任何其他前缀都不一样,因此能一起使用。 [*]CSS变量必须在选择器内定义,所以最接近“全局”作用域是:root [*]预处理器不知道DOM,因此靠嵌套来实现继承。CSS变量的值继承自DOM树,这一点跟普通CSS变量的继承方式一样。 预处理器变量和CSS变量之间另一个显著差异在上文的代码中并不明显:因为它们没有被编译成静态值,CSS变量可能在浏览器运行的时候被更新。这意味着CSS变量可以在Javascript代码里读写,用来做计算或是动画。以下的Codepen示例示范了怎么样用CSS变量和Javascript来创建手风琴动画。codepen例子CSS变量能被 Firefox, Chrome, 和 Safari支持,当前版本的Edge部分支持: http://caniuse.com/#feat=css-variablesCSS模块化变量并不是唯一一种Javascript渗透到CSS的概念。特别是在近几年,Javascript开发者已经把目光转向CSS组织,在Atwood’s Law(阿特伍德定律,即:任何可以用JavaScript来写的应用,最终都将用JavaScript来写)的延伸上思考着“我能做的更好”。这里有一些好的理由可以证明: [*]不像JavaScript,CSS类名在全局的命名空间一直存在 [*]在大型的编译过的样式中解决冲突是靠不住的,并且容易产生意想不到的行为,或是仅仅通过增加权重解决 [*]样式透过DOM层级,能够用出人意料的方式改变子元素的样式 [*]用Javascript来管理CSS允许CSS规则以运行逻辑为基础 React在这种思路上尤其前卫,被Javascript控制的行内样式可以当做这些问题的答案。(可能不是样式渗透子元素,但是确实减少了一定数量的这种样式冲突的案例)。然而,它也有自己的一些缺点: [*]伪类(如: :hover或 :focus)在CSS很容易实现,但在Javascript必须被伪造 [*]在Javascript中重新实现媒体查询是劳动密集工作 [*]内联样式失去了被更高权重覆盖的能力,因为它们已经处于权重层次结构的顶端。 [*]在动态样式上切换类名、Css变量、函数(像 calc())已经解决了大多数难题 [*]性能:DOM数量是一方面,CSS能被缓存 CSS模块化,在某些方面来说是CSS开发者回归到被Javascript开发者侵入的地盘:是一种解决批评和改进样式表而不会完全消除它们的方法。CSS模块本质上归结为可以引入JavaScript的局部作用域的CSS文件,并编译成唯一的类名。例如,这里:JavaScript:import * as css from ‘css/buttonComponent.css’;const buttonHTML = `${buttonText}`; CSS:.root { background-color: #ffffff; color: blue; border: 1px solid blue;} 将会被编译成以下内容:HTML:="buttonComponent_root_**2718"[/color>Button with modular CSS CSS:.root { background-color: #ffffff; color: blue; border: 1px solid blue;} 因为 buttonComponent.css里的类是局部作用域的,并且在一个命名清晰的CSS文件中,所以就不再需要特定的类名了例如 .button。相反,推荐的格式是使用单个标准化的“根”类名称,如 .root或 .normal,然后使用特定状态的类名,如 .error, .success或 .disabled,所有这些类名可能在一定条件下应用于JavaScript 。CSS模块化还通过弱化写多个类名并使用composes解决样式容易覆盖的问题。composes关键字类似于Sass的 @extends的预处理器装饰器,除了在CSS中编译样式之外,composes以可预见的顺序返回多个命名空间的类名。例如:CSS:.root { display: inline-block; padding: 10px; color: blue;}.success { composes: root; color: green;} JavaScript:import * as css from 'css/buttonComponent.css';// css.success = 'buttonComponent_root_**2718 buttonComponent_success_pi3141'const buttonHTML = `${buttonText}`; HTML:="buttonComponent_root_**2718 buttonComponent_success_pi3141"[/color>Button with green text CSS也有一种更强的工具来增强样式的模块化: all关键词,与CSS模块化区分,能用来重置所有的属性到最原始的状态,例如.root { all: initial; }。因为这是一个新的CSS属性而不是依赖于webpack或Browserify编译器的类型,IE和Edge仍然缺少支持度。Async/Await/能让你的代码变得很棒随着越来越多的成功和偶尔(捕获)的错误,JavaScript用Promise来改进异步代码的处理。在promise出现以前,一个回调可能看起来像下面的嵌套末日: doAsyncFunction(function(result) { doSecondAsyncFunction(result, function(resultTwo) { doThirdAsyncFunction(resultTwo, function(resultThree) { // and so on… }, catchError); }, catchError);}, catchError); 以上代码用promise能被转换成链式顺序,用.then()完成请求回调,用.catch()结束:doAsyncFunction() .then(result => doSecondAsyncFunction(result)) .then(resultTwo => doThirdAsyncFunction(resultTwo)) // and so on… .catch(catchError); 跟早期金字塔形的传入回调和错误处理相比,这种链式语法显然更干净清晰,更容易理解。使用 async函数,开发人员不必等到写异步代码像同步代码一样直观的那天。使用async / await的初始示例如下所示:(async function() { try { const result = await doAsyncFunction(); const resultTwo = await doSecondAsyncFunction(result); const resultThree = await doThirdAsyncFunction(resultTwo); return resultThree; } catch(error) { catchError(error); }})(); 每个await将会中断代码的执行,直到它的promise成功回调,整段代码被包含在try/catch块内,就像同步代码一样。要记住的最重要的点:await可能只用在async 方法里作为特定的关键词,async方法本身还是异步的,不会阻塞周围的代码运行。这个三个promise的例子显示三个promise进程如何能够被逐个执行,Promise.all和Promise.race能与async/await同时运行:async function doAsyncStuff() { const [resultOne, resultTwo, resultThree] = await Promise.all([ doAsyncFunction(), doSecondAsyncFunction(), doThirdAsyncFunction() ]); // do stuff with resultOne, resultTwo, and resultThree} 在技术上,await甚至不需要通过promise,因为它将在Promise.resolve中包含任何非promise的值。任何promise解决方案都会被push到调用堆栈的末尾,这个事实可能会导致一些稍微奇怪但有趣的结果: async function getAnswer() { const answer = await 42; console.log(`The answer to life, the universe, and everything is ${answer}`);}getAnswer();console.log(‘Vogons blow up Earth’);// will log:// “Vogons blow up Earth”// The answer to life, the universe, and everything is 42 阻塞元素 / 惰性元素对于任何需要创建模态框并关注可访问性的开发人员来说,管理焦点一直是并且仍然是一个难以解决的问题。焦点应该从不被放到隐藏或是挡住的DOM元素,但做出合适的行为是痛苦而密集的。两个基本的监听聚焦事件选项,当人们尝试离开模态框,或是通过设置tabindex="-1"来实现手动移除所有聚焦中的非模态框元素。这两种解决方法通常以使用大型而脆弱的DOM查询来在代码里监听焦点元素结束。document.querySelectorAll('a[href], button:not([disabled]), area[href], input:not([disabled]), select:not([disabled]), textarea:not([disabled]), iframe, object, embed, *[tabindex], *[contenteditable]); 即使焦点已经被处理,隐藏的部分应该将aria-hidden设置为true,因此它不会被屏幕阅读器等辅助技术所读取。两个规范提案建议将从整体上解决模态问题:inert和blockingElements。首先,HTML属性inert,将会从关注的顺序里移除一段DOM树(好像所有可聚焦的元素都接受tabindex="-1"),并从辅助技术隐藏。blockingElements将几乎完全相反:暴露一堆“blocking elements”,这将有效地使所有其他DOM树变得惰性。例如,如果我有以下DOM结构:="content"[/color>="modal"[/color> Modal contentcolor> Other content, including linksbuttons/etc="sidebar"[/color>为了打开这个对话框,我将移除inert属性,并调用document.$blockingElements.push(document.querySelector('.modal')),这将渲染成不仅仅是直接的兄弟树inert,而且是父级和组先级的兄弟inert。inert和blockingElements仍然是提案,所以在任何当前的浏览器中都不是本地支持的。然而,有可用的polyfills可以使用它们现在使用: https://github.com/WICG/inert 和 https://github.com/PolymerLabs/blockingElements交叉观察器观察元素滚动到视口内一直都是滚动插件开发的内容,这些插件使用一些滚动事件监听(希望能节流)。现在,交叉观察器允许开发者用一些选项和回调来创建一个观察器来观察元素滚动到视口内,这仅仅用vanilla JavaScript就可以实现。IntersectionObserver与其他DOM观察很像(像MutationObserver),在这里,你可以用一个回调和选项来创建,然后在一个DOM元素上调用.observe。每当元素与视口的交叉距离增加10%时,简单实现更新可以如下所示:const observerOptions = { root: null, rootMargin: '0px' threshold: [0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1]};function intersectionCallback(entries) { entries.forEach((entry) => { const percent = Math.floor(entry.intersectionRatio * 100); console.log(`Element ${entry.target} scrolled ${percent}% into view`); });}const observer = new IntersectionObserver(intersectionCallback, observerOptions);observer.observe(myElement); 对象由以下属性组成: [*]root:包含滚动区域的元素(如果空着,默认为文档的视口viewport) [*]rootMargin:在根元素周围能够增加或收缩,用来计算临界点 [*]threshold:一组数字格式,表示回调应该触发的目标元素的可见性的百分比。例如[0,0.5,1],将在元素滚动过0%,50%、100%可见区域时触发。 为了停止观察特定元素——如果回调仅仅在元素第一次滚动的时候才需要,或是,它需要被用来观察大量元素滚动进入或退出视窗,只需要调用observer.unobserve(myElement);。断开整个观察,执行observer.disconnect();。在IntersectionObserver,一个使用unobserve()的好的案例是懒加载图片脚本,这个脚本在滚屏时用到。 function onImageIntersect(entries) { entries.forEach(entry => { entry.target.src = imageSource; observer.unobserve(entry.target); }}document.querySelectorAll('img').forEach(img => observer.observe(img)); 浏览器仍然在慢慢渐渐给出支持,但它不用polyfill就已经可以用在Chrome, Firefox和 Edge了。IE和Safari现在还缺少原生的支持。
总条数:25 到第
上滑加载中