Quantcast
Channel: CodeSky 代码之空
Viewing all 195 articles
Browse latest View live

从 PostCSS 与 cssnext 中看 CSS 的新特性

$
0
0

今天刚好看了一下 PostCSS,看到了 postcss-cssnext 的网站,觉得用来学习一些新特性(虽然现在来看似乎不怎么新)。

先来介绍一下 PostCSS 与 Sass / Less / Stylus 相比的区别,也就是后处理器和预处理器的区别。

预处理器与后处理器

对于预处理器(Sass / Less / Stylus)而言,就像 JavaScript 的方言语法 coffeescript,或者 HTML 的方言语法 pug(jade) 那样,你需要学习一套新的不同的语法,这些 CSS 方言通过编译器编译成 CSS 文件,最终浏览器实现解析。

而 PostCSS 可以让你无缝迁移到现代化的 CSS 中,本身并没有任何功能,只由 postcss 的 plugin 来实现相关的功能,也就是说,等到浏览器实现了,你随时可以去掉这个插件,由原生浏览器来解析,而预处理器则是全家桶。

postcss-cssnext

cssnext 提供了一些最新的 CSS 语法支持,让你不用担心于浏览器是否支持这些特性,因此,配合 postcss 实用,就取得了最佳的效果。

特性列表

  • 自动提供浏览器前缀支持
  • 自定义属性与 var() 支持
  • 自定义属性集合与 @apply 支持
  • 简化的、更安全的 calc()
  • 可自定义的媒体查询
  • 媒体查询范围
  • 自定义选择器
  • 嵌套
  • image-set()
  • color()
  • hwb()
  • gray()
  • #rrggbbaa 颜色
  • rgba() 的降级方案
  • rebeccapurple 颜色
  • font-variant 属性
  • filter 属性
  • initial
  • rem 单位的降级方案
  • :any-link 伪类
  • :mathces 伪类
  • :not 伪类
  • :: 伪元素语法的降级方案
  • overflow-wrap 属性的降级方案
  • 不区分大小写的属性
  • 功能增强的 rga()
  • 功能增强的 hsl()
  • system-ui 字体

功能介绍

说了这么多,然而我们还是不知道,这些功能到底是用来干嘛的,所以凑合翻译一下……

自动提供浏览器前缀支持

自动添加(以及删除过时 / 没用的前缀),由 autoprefixer 实现。

自定义属性与 var() 支持

自定义属性的当前转换旨在提供一种限定在 :root 选择器中、面向未来的、由原生 CSS 自定义属性提供的新特性。

使用特性:

:root {
  --mainColor: red;
}

a {
  color: var(--mainColor);
}

注:仅限 :root 选择器来定义变量。

W3C 官方特性规范 | 插件文档

自定义属性集合与 @apply 支持

允许你在已命名的自定义属性中存储一套变量,然后在其他类型规则中引用它。

:root {
  --danger-theme: {
    color: white;
    background-color: red;
  };
}

.danger {
  @apply --danger-theme;
}

注:仅限 :root 选择器来定义变量。

规范 | 插件文档

简化的、更安全的 calc()

使用优化预分析 var() 引用来允许你更安全的用 calc() 使用自定义变量。

:root {
  --fontSize: 1rem;
}

h1 {
  font-size: calc(var(--fontSize) * 2);
}

规范 | 插件文档

可自定义的媒体查询

一个更好的方法来实现语义化的媒体查询。

@custom-media --small-viewport (max-width: 30em);
/* check out media queries ranges for a better syntax !*/

@media (--small-viewport) {
  /* styles for small viewport */
}

W3C 官方特性规范 | 插件文档

媒体查询范围

允许用 <=>= 来取代 minmax(使得语法更具有可读性)。

@media (width >= 500px) and (width <= 1200px) {
  /* your styles */
}

/* or coupled with custom media queries */
@custom-media --only-medium-screen (width >= 500px) and (width <= 1200px);

@media (--only-medium-screen) {
  /* your styles */
}

W3C 官方特性规范 | 插件文档

自定义选择器

允许你创造自己的选择器

@custom-selector :--button button, .button;
@custom-selector :--enter :hover, :focus;

:--button {
  /* styles for your buttons */
}
:--button:--enter {
  /*
    hover/focus styles for your button

    Read more about :enter proposal
    http://discourse.specifiction.org/t/a-common-pseudo-class-for-hover-and-focus/877
   */
}

W3C 官方特性规范 | 插件文档

嵌套

允许你使用嵌套选择器。

a {
  /* direct nesting (& MUST be the first part of selector)*/
  & span {
    color: white;
  }

  /* @nest rule (for complex nesting) */
  @nest span & {
    color: blue;
  }

  /* media query automatic nesting */
  @media (min-width: 30em) {
    color: yellow;
  }
}

规范 | 插件文档

image-set() 函数

允许你根据不同的用户设备来提供不同的图片解决方案。

.foo {
    background-image: image-set(url(img/test.png) 1x,
                                url(img/test-2x.png) 2x,
                                url(my-img-print.png) 600dpi);
}

W3C 官方特性规范 | 插件文档

color() 函数

一个颜色函数来修改颜色(转换为 rgba()

a {
  color: color(red alpha(-10%));
}

a:hover {
color: color(red blackness(80%));
}

有很多颜色修饰符,记得看看这个链接

W3C 官方特性规范 | 插件文档

hwb() 函数

hsl() 相似,不过更容易阅读。(会转换为 rgba()

body {
  color: hwb(90, 0%, 0%, 0.5);
}

W3C 官方特性规范 | 插件文档

gray() 函数

允许你使用超过 50 种渐变的灰度值(转换为 rgba()),对于第一个参数,你可以使用一个 0 - 255 的数值或者百分比。

.foo {
  color: gray(85);
}

.bar {
  color: gray(10%, 50%);
}

W3C 官方特性规范 | 插件文档

rrggbbaa 颜色值

允许使用 4 位或者 8 位十六进制数来表示颜色(转换为 rgba()

body {
  background: #9d9c;
}

W3C 官方特性规范 | 插件文档

rgba() 的降级方案

如果你使用的是旧的浏览器(比如 IE8),那么把 rgba() 转换为实体颜色。

body {
  background: rgba(153, 221, 153, 0.8);
  /* you will have the same value without alpha as a fallback */
}

W3C 官方特性规范 | 插件文档

rebeccapurple 颜色

允许你使用新的颜色关键词。

body {
  color: rebeccapurple;
}

W3C 官方特性规范 | 插件文档

font-variant 属性

通过 font-feature-settings 降级的一种属性。你可以通过这个链接来查看浏览器支持

h2 {
  font-variant-caps: small-caps;
}

table {
  font-variant-numeric: lining-nums;
}

W3C 官方特性规范 | 插件文档

filter 属性

W3C 的 filters 只允许使用 url(data:*) 来转换 svg filter。

.blur {
    filter: blur(4px);
}

W3C 官方特性规范 | 插件文档

initial 值

允许你使用任何值的初始值。该值表示属性初始化值所指定的值,但这并不意味着浏览器的默认值。

比如,对于 display 属性,initial 时钟标示内联,因为这是属性指定的初始值。一个例子,div { display: initial } 并不代表 block,而是 inline

div {
  display: initial; /* inline */
}

杀手级特性:一次性初始化所有属性

div {
  all: initial; /* use initial for ALL PROPERTIES in one shot */
}

W3C 官方特性规范 | 插件文档

rem 单位

在旧浏览器里将 rem 降级为 px(比如 IE8)。

h1 {
  font-size: 1.5rem;
}

W3C 官方特性规范 | 插件文档

:any-link 伪类

允许你使用 :any-link 伪类。(只要有 :link 或者 :visited 都在 :any-link 的范围内)

nav :any-link {
  background-color: yellow;
}

W3C 官方特性规范 | 插件文档

:matches 伪类

允许你使用 :matches 伪类(可以看作 or 选择)

p:matches(:first-child, .special) {
  color: red;
}

W3C 官方特性规范 | 插件文档

:not 伪类

允许你使用支持多选择器的 :not 伪类,将此降级为只支持一个选择器的 :not

p:not(:first-child, .special) {
  color: red;
}

W3C 官方特性规范 | 插件文档

:: 伪元素语法降级

如果你的浏览器是旧浏览器,会将 :: 降级为 :

a::before {
  /* ... */
}

W3C 官方特性规范 | 插件文档

overflow-wrap 属性

overflow-wrap 转换为 word-wrap 属性(考虑到许多浏览器只支持 word-wrap)。

body {
overflow-wrap: break-word;
}

W3C 官方特性规范 | 插件文档

不区分大小写的属性

允许你使用不区分大小写的属性。

[frame=hsides i] {
  border-style: solid none;
}

W3C 官方特性规范 | 插件文档

功能增强的 rga()

允许你使用由空格分割的参数与可选的由斜线分割的不透明度新语法。

你也可以使用数字来表示颜色通道。

alpha 值接受百分比和数字,并且将 rgb() 作为可选参数。因此 rgb()rgba() 现在是彼此的别名。

div {
  background-color: rgb(100 222.2 100.9 / 30%);
}

W3C 官方特性规范 | 插件文档

功能增强的 hsl()

允许你使用由空格分割的参数与可选的由斜线分割的不透明度新语法。

hsl() 现在接受角度(deg, grad, rad, turn)以及用数字表示色调,用百分比或者数字来表示 alpha 值。所以 hsl()hsla() 现在也是彼此的别名。

div {
  color: hsl(90deg 90% 70%);
  background-color: hsl(300grad 25% 15% / 70%);
}

W3C 官方特性规范 | 插件文档

system-ui 字体

允许你使用 system-ui 通用字体系列。当前转换提供了一个实际的字体列表来作为降级方案。

body {
  font-family: system-ui;
}

W3C 官方特性规范 | 插件文档

写在最后

今天的博客好长,虽然我也知道翻译的很垃圾 T^T。通过这个列表,我们也能理解和阅读到一些新的特性了。

参考资料


[翻译]关于 Parcel 你所需要知道的一切:时下火爆的快速 Web 应用打包器

$
0
0

这是一次试翻,之后会每两周翻译一篇文章(练习英语阅读)。

原文:https://medium.freecodecamp.org/all-you-need-to-know-about-parcel-dbe151b70082

1_-Tcq85crClCEu_gYzn06gg.gif

真的吗?又是一个打包器或者说构建工具?完全正确,进化和创新的结合为你带来了 Parcel

15153265490377.jpg

Parcel 有什么特别之处以及为什么我要关心呢?

虽然 Webpack 用复杂性的成本带来了许多的可配置型,相对的,Parcel 带来的是简单性。Parcel 本身把自己称为「零配置」。

上述的详解——Parcel 提供了一个开箱即用的开发服务器。开发服务器将在你更改文件时自动重建你的应用,并支持热更新来实现快速开发。

Parcel 到底哪里好?

  • 快速的打包时间——Parcel 比 Webpack、Rollup 和 Browserify 都要快。

15153270021235.jpg

Parcel 跑分

不过有一点需要考虑:Webpack 依旧是极好的,有时候它打包的更快。

15153270833519.jpg

图片来源

  • Parcel 对于 JS / CSS/ HTML 和文件资源有有开箱即用的支持——不需要任何插件,更加便于用户使用。
  • 零配置需要。开箱即用的代码拆分,热模块重载,CSS 预处理器,开发服务器,缓存以及更多惊喜!
  • 更友好的错误日志。

15153272843589.jpg
15153272944862.jpg

Parcel 的错误处理

好了好了我知道了,那么我什么时候用 Parcel / Webpack 或者 Rollup 呢?

这完全取决于你!但是我个人会在以下情况下使用每一种打包器:

  • Parcel - 中小型项目(小于 1.5 万行代码)
  • Webpack - 大型以及企业级规模的项目
  • Rollup - 开发 NPM 包时

让我们试一把 Parcel 吧!

安装十分简单

npm install parcel-bundler --save-dev

我们把 parcel-bundler 这个 npm 包安装在本地。现在我们需要初始化一个 Node 项目。

15153275507165.jpg

接下来,创建 index.htmlindex.js 文件:

开始关联起我们的 index.htmlindex.js

15153275910740.jpg
15153275947316.jpg

最后把 parcel 命令添加进我们的 package.json

15153276185340.jpg

这是所有需要配置的项——节约时间的大神器!

接下来,启动我们的服务器。

15153276691350.jpg
15153276752140.jpg

太棒了!注意构建时间。

15153277488863.jpg

15 毫秒?哇,这实在是太快啦!

那么,HMR(热重载)工作的如何?

15153278052986.jpg

感觉上去也很快呢。

SCSS

15153278236404.jpg

我们所需要的全部就是一个 node-sass 包,我们很简单就能开工。

npm i node-sass && touch styles.scss

接下来,加上一些样式,在 index.js 中引入 styles.scss

15153279020786.jpg
15153279085033.jpg
15153279160477.jpg

生产环境构建

我们要做的全部就是在我们的 package.json 加上一个 build 命令:

15153279661277.jpg

跑个构建命令:

1_bPzZxDj7qAwfMFkPBy44Ow.gif

看看 Parcel 如何让我们的生活更轻松?

15153281690492.jpg

你可以指定一个特定的构建路径,像这样:

parcel build index.js -d build/output

React

15153282024521.jpg
设置 React 非常简单,我们需要做的全部就是安装我们的依赖以及设置我们的 .babelrc

npm install --save react react-dom babel-preset-env babel-preset-react && touch .babelrc

15153282672876.jpg

好啦好啦,接下来上重酬戏!在滚动到下面的页面之前,尝试着自己写写我们的初始化 React 组件。

15153284556446.jpg
15153284611633.jpg
15153284675592.jpg

Vue

15153284805159.jpg
按照大家的要求,接下来奉上 Vue 的例子。

从安装 vueparcel-plugin-vue 开始——后者是为了提供 .vue 的组件支持。

$ npm i --save vue parcel-plugin-vue

我们需要添加我们的根元素,引入 Vue 主页文件和初始化的 Vue 组件。

先从创建一个 Vue 的文件夹开始,同样,创建一个 index.jsapp.vue 吧。

$ mkdir vue && cd vue && touch index.js app.vue

现在,关联起我们的 index.jsindex.html

15153286440392.jpg

最后,初始化 Vue,写完我们的第一个 Vue 组件。

15153286672197.jpg
15153286723153.jpg
15153286760272.jpg

瞧!我们已经给 Vue 加上了 .vue 的支持。

TypeScript

15153287302511.jpg

这真是太简单了。只要安装上 TypeScript 我们就能跑起来。

npm i --save typescript

新建一个叫做 index.ts 的文件并将它添加进 index.html
15153287863565.jpg
15153287917864.jpg
15153287987311.jpg
很棒!

GitHub 源代码

WebDriver.io Chrome Driver setValue 不准确问题的研究

$
0
0

昨天在修一个旧程序的问题,在重新安装之后发现原来的程序跑不了了,一个模拟登录的程序,在输入正确的时候输出了 {login: false},在排除了是登录逻辑变更之后使用 waitUntil 暂停了一下发现在 setValue 时,driver.io 会少几个字母,例如:abcdefg 在输入后可能会变为 abcdfg

刚开始的时候以为是因为电脑卡的关系,后来释放了一下内存,依旧是这样,于是开始查 issue,实际上报 setValue 问题的人很多,官方都是 close 处理,然后查到了:

After update chrome browser to the Version 62.0.3202.75 (Official Build) (64-bit) spec symbols are missed in set text and browser settings page is opening unexpectedly on enter spec symbol or capital letter.

换句话说,这是一个 Chrome Driver 的 bug,在 Chrome Driver v2.33 已经解决了这个问题,可以尝试:

  1. 升级你的 Driver
  2. 使用其他浏览器的 Driver(逃

stroke-dashoffset 造成的高 CPU 与内存占用问题

$
0
0

最近在做一个前端项目(对我终于写前端了),在做动画的时候发现网页很卡,后来发现是 CSS 动画的问题,刚开始开启了 3D 加速,发现然而并没有什么卵用,后来我就开始搜一下这个属性有没有什么过渡方案,就引发了一连串的故事……

stroke-dashoffset 是啥

SVG 中 path 可以构造很多形状,如果要绘制一些动画效果,有两个 stroke 属性非常关键:stroke-dasharraystroke-dashoffset

stroke-dasharray 负责控制实线和虚线,stroke-dashoffset 负责控制每个实线的偏移量。这样组合起来就可以造成一种视觉上的偏移,具体可以阅读第一篇文章中 CodePen 的示例,这不是本文的重点,就不多说了。

关于这点,可以阅读以下两篇文章:

3D 加速怎么开

CSS 的动画无论是 animation 还是 transform 还是 transition 都是不会默认开启 3D 加速的,只有使用到 Z 轴 3D 效果的时候才会启用 3D 加速,因此「使用 Z 轴」就是 3D 加速的要义,我们可以用这种方法强制使用 Z 轴而不影响效果:

.cube {
  -webkit-transform: translateZ(0);
  -moz-transform: translateZ(0);
  -ms-transform: translateZ(0);
  -o-transform: translateZ(0);
  transform: translateZ(0);
  /* Other transform properties here */
}

如果遇到动画闪烁的现象时,可以用以下属性修复这个问题:

.cube {
  -webkit-backface-visibility: hidden;
  -moz-backface-visibility: hidden;
  -ms-backface-visibility: hidden;
  backface-visibility: hidden;

  -webkit-perspective: 1000;
  -moz-perspective: 1000;
  -ms-perspective: 1000;
  perspective: 1000;
  /* Other transform properties here */
}

参考文章:

曲线救国:用 transition 代替 animation

开启了之后,我发现,GPU 倒是用上了(占用率变高了),CPU 和内存使用率没降下来……(╯‵□′)╯︵┻━┻

当时我在想,是不是 animation 或者这个动画不支持 GPU 加速,所以在网上查了一下有没有曲线救国的方法:依靠 transition 确实可以实现,但是这样需要 JavaScript 去切换 className,所以我当时没有直接选择这种方案。(结果万万没想到 Bug 的 workaround 也是 JS 实现的)

真实的故事:stroke-dashoffset 的 Bug

偶然间我搜了一下 stroke-dashoffset CPU 的问题,结果找到了 chromium 的 issue,2012 年的(怎么又是 2012 年的 Bug),实际上在 Firefox 上也能重现,并且至今未修复。它说的就是 stroke-dashoffset 造成的 CPU 满载。

svg动画导致持续占用CPU 中也有类似的问题,是在底层进行修复的,从前端层无法修复,而对于桌面 Web 而言,没有办法打这么底层的补丁。

后来沿着查到了一个一毛一样的问题:How can I animate infinite marker movement down an SVG path without very high CPU usage?

使用了 JS 来代替 CSS 动画:

var lines = d3.selectAll('.flowline');

var offset = 1;
setInterval(function() {
  lines.style('stroke-dashoffset', offset);
  offset += 1;
}, 50);  

经过测试,CPU 和内存有效降低了 40% 左右,但止不住 Mac 风扇依旧在狂转,所以放弃了这个动画,因此只做一个记录,诸君根据需求自行取舍。

从 a == 1 && a == 2 && a == 3 === true 说起

$
0
0

最近这个问题火了一把,其实最开始我是在 Reddit 看到的,当时也没想到这个问题会火,后来拖稿拖到了现在。

StackOverflow 中有几个很有趣的实现形式(中文社区最近不太关心,不知道有没有人已经写过了):

toString()

const a = {
  i: 1,
  toString: function () {
    return a.i++;
  }
}

if(a == 1 && a == 2 && a == 3) {
  console.log('Hello World!');
}

这一种方法主要利用 a 是对象时比较会调用 toString(),那么我们自定义 toString() 就能做到结果为 true 的效果了,当然,规则还是不太好记的,与此同时,valueOf 也可以达到相同的效果。

相关阅读:[非严格相等 ==
](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Equality_comparisons_and_sameness)

零宽空格

下面一种方法非常的有趣,因为它利用了零宽空格造成了一种视觉上的相等:

var a= 1;
var a‍= 2; //one zero-width character
var a‍‍= 3; //two zero-width characters (or you can use the other one)
if(a==1&&a‍==2&&a‍‍==3) {
    console.log("Why hello there!")
}

然而实际上,这并不是三个相同的变量,鉴别的方法是通过 codePointAt 检验 unicode。

关于零宽空格的介绍:https://zh.wikipedia.org/wiki/%E9%9B%B6%E5%AE%BD%E7%A9%BA%E6%A0%BC

不同的 a

有了零宽字符的先例,就有人想到有相似的方法:

var a = 1;
var a = 2;
var а = 3;
if(a == 1 && a == 2 && а == 3) {
    console.log("Why hello there!")
}

实际上这是三个不同的 a:

  • a - Latin lower case A(拉丁小写 a)
  • a - Full Width Latin lower case A(全宽拉丁小写 a)
  • а - Cyrillic lower case A (另一种字母的小写 a)

( ⊙ o ⊙ )!

我只能说,城里人真会玩,刚开始我只是觉得这样的题目不适合当面试题,适合当段子,万万没想到,是在下输了。

[翻译]在 React 中抛弃 .bind(this)

$
0
0
原文:Losing .bind(this) in React

——在 React 组件中抛弃 .bind(this)

这将会成为历史。

如果你自己在使用 React,你或许会不得不写类似于 .bind(this) 的代码,当然,我知道:

  • 这看上去真的丑
  • 这在代码中占用了额外的空间

幸好,JavaScript 有一些提案中的特性可以让 .bind(this) 成为我们的过去。

在我解释如何抛弃 .bind(this) 之前,我将会给你看一段简单的例子来展示他可以被用在哪里。我们希望渲染一个点击了之后就能改变文本的按钮。为了实现这个效果,我们会写一个像下面差不多一个意思的组件:

import React, { Component } from "react";

class ButtonWithBind extends Component {
  constructor() {
    super();

    this.state = { toggle: false };
  }

  render() {
    const toggle = this.state.toggle;

    return (
      <div>
        <button onClick={this.toggleButton}>
          {toggle ? "ON" : "OFF"}
        </button>
      </div>
    );
  }

  toggleButton() {
    this.setState(prevState => ({ toggle: !prevState.toggle }));
  }
}

export default ButtonWithBind;

我们在构造器中中把 toggle 的开关状态设置为 false

此外,我们绑定了一个onClick 处理函数: toggleButton 函数,所以它会在按钮被点击的时候被调用。

我们也创造了一个简单的 toggleButton 函数,用以在被调用时改变状态。

棒极了,看上去我们准备好了。

如果我们直接去点击这个渲染出来的按钮,会得到一个下图一样的 TypeError

铛!它明明应该工作的🤔!

我们之所以得到一个错误是因为当 onClick 调用我们的 toggleButton 函数时,函数并没有定义。

通常,你会通过在 toggleButton 函数中绑定上 this 指针来修复这个问题,所以他看上去保持不变。让我们继续在构造函数上位函数上绑定上 this

this.toggleButton = this.toggleButton.bind(this);

添加上这个之后,我们的按钮组件看上去像这样:

import React, { Component } from "react";

class ButtonWithBind extends Component {
  constructor() {
    super();

    this.state = { toggle: false };

    this.toggleButton = this.toggleButton.bind(this);
  }

  render() {
    const toggle = this.state.toggle;

    return (
      <div>
        <button onClick={this.toggleButton}>
          {toggle ? "ON" : "OFF"}
        </button>
      </div>
    );
  }

  toggleButton() {
    this.setState(prevState => ({ toggle: !prevState.toggle }));
  }
}

export default ButtonWithBind;

试试吧,它应该可以正常运作了:

是的,没毛病。

🔪 .bind(this)

现在,让我们开始抛弃这个讨人厌的 .bind(this),为了做这个,我们将要使用 JavaScript 的实验中的公有类字段特性。公有类字段特性可以让你在你们的类中使用箭头函数语法。

toggleButton = () => {
  this.setState(prevState => ({ toggle: !prevState.toggle }));
}

一个箭头函数没有它自己的 this,不过他使用的是封闭的执行上下文的 this 值。箭头函数在词法上绑定它们的上下文,所以 this 实际上指向最原始的上下文。如果你要进行命名,这也被叫做词法环境。

从根本上来说,这让我们省下了代码中的 .bind(this)

注意,这是 JS 中的一个实验中的特性,这意味着她还没有被 ECMAScript 的标准所采纳,不过让我们保持手指交叉,做个🤞。在它被采纳之前,你可以配置 babel,使用 babel-plugin-transform-class-properties 来转换它。

可能的陷阱

记住,这可能会影响两件事。第一件是内存和性能。当你使用类字段来定义一个函数时,你的方法将驻留在类的每个实例上,而不是在原型上,因为原本使用了 bind 方法。你可以通过阅读 Donavon West 的一篇精彩的文章来深入理解——Demystifying Memory Usage using ES6 React Classes

第二件事是通过使用公用类字段来影响你如何编写单元测试,你将不能使用组件原型来进行这样的函数打桩:

const spy = jest.spyOn(ButtonWithoutBind.prototype, 'toggleButton'); expect(spy).toHaveBeenCalled();

不将不得不寻找另一种方法来打桩方法,比如在 props 中传递 spy 或者检查状态的变化。

在组件中使用

现在,让我们跳到我们在组件中如何使用公有类字段,并改变我们的 toggleButton 函数来丢掉 .bind(this)


import React, { Component } from "react";

class ButtonWithoutBind extends Component {
  constructor() {
    super();

    this.state = { toggle: false };
  }

  render() {
    const toggle = this.state.toggle;

    return (
      <div>
        <button onClick={this.toggleButton}>
          {toggle ? "ON" : "OFF"}
        </button>
      </div>
    );
  }

  toggleButton = () => {
    this.setState(prevState => ({ toggle: !prevState.toggle }));
  }
}
每个 React 开发者都曾经说过:看着 22 - 24 行,哇,太漂亮了!没有更多讨厌的 .bind(this) 了。

公有类字段还有一个好处,就是我们可以从构造函数中定义状态,然后细化我们的组件:

import React, { Component } from "react";

class ButtonWithoutBind extends Component {
  state = { toggle: false }

  render() {
    const toggle = this.state.toggle;

    return (
      <div>
        <button onClick={this.toggleButton}>
          {toggle ? "ON" : "OFF"}
        </button>
      </div>
    );
  }

  toggleButton = () => {
    this.setState(prevState => ({ toggle: !prevState.toggle }));
  }
}

export default ButtonWithoutBind;

而且,我们已经丢掉了 .bind(this),我们已经细化一丢丢我们的组件,可以称之为胜利了🏁!我们应该得到某种奖励,随意的去冰箱散步,拿一个冷的🍺或者一个巧克力🍫,又或者是任何你喜欢的东西。因为你刚刚学到了可以用在 React 中的全新的东西。

非常感谢 Kent C. Dodds 为此制作的视频。这篇文章没有他就不会存在。干杯🍻 Kent。

如果你喜欢你所看到的,就请👏并传播这个文章。另外,看看我的网站follow 我,我将发布更多 React 相关的文章,所以点击 Follow 保持关注🎥。

[翻译] Makefile - 失落的艺术

$
0
0
译者吐槽:尽管 Makefile 似乎与前端渐行渐远,不过还是有很多神奇海螺一般的魔法值得我们去入门,本文与前端的内容相结合进行了一番介绍和示例科普,适合所有对 Makefile 一脸懵逼的小伙伴。

原文:http://www.olioapps.com/blog/the-lost-art-of-the-makefile/

我做过许多 JavaScript 项目。JavaScript 目前的潮流是使用用 JavaScript 书写和配置的构建工具像 Gulp 或者 Webpack。我想要讨论的是 Make 的长处(尤其是 GNU Make)。

Make 是一种通用的构建工具,它自40年前推出以来一直在不断完善和改进。 Make 非常擅长简洁的表现构建步骤,此外它并不特定用于 JavaScript 项目。 它非常适合增量构建,在更改大型项目中的一个或两个文件后重新构建时,Make 可以节省大量时间。

Make 已经存在了足够长的时间来解决那些新出现的构建工具现在刚刚发现的问题。

尽管标题上称之为失落的艺术,但实际上 Make 仍然被广泛的使用着。不过我认为它在 JavaScript 中代表性仍显不足。你会更常见于 C/C++ 之类的的项目。

我的猜测是,JavaScript 社区中的大部分并没有一个 Unix 编程的背景,也一直没有一个很好的机会去了解 Make 有哪些功能。

在这里,我将会提供一个简单的入门。我会用我自己的 JavaScript 项目来介绍 Makefile 的内容。

你可以在这里找到完整的文件。

什么时候需要坚持使用 Webpack

Webpack 所做的工作非常的专业化。如果你正在编写一个前端应用,并且你需要打包代码,你的确需要 Webpack(或者相似的 Parcel)。

另一方面,如果你的需求很一般时,Make 是一个非常好的工具。我在编写客户端或者服务端库、Node App 时使用 Make。在那些情况下,我没有从 Webpack 的特殊功能中受益。

为什么 JavaScript 需要一个构建的步骤

让我们快速解决一下这个问题:为什么有些人希望在打包代码的过程中加入构建这个环节。

我希望能够编写同时针对浏览器和最新稳定版 Node 代码的 Stage 4 ECMAScript。我也希望我的代码中能包含 Flow 类型注释,我希望对我的代码进行处理,并且可以转换为纯 JavaScript。所以我使用 Make 来调用 Babel 转换代码。

介绍 Makefile

Make 会在当前文件夹寻找一个叫做 Makefile 的文件。一个 Makefile 是一系列像下述的列表:

target_file: prerequisite_file1 prerequisite_file2
    shell command to build target_file (必须用 tab 缩进而不是空格)
    another shell command (这些命令被称为菜单)

除非你另行制定,Make 会假定目标(在这个例子中是 target_file)和条件(prerequisite_file1prerequisite_file2)是文件或者目录。你可以要求 Make 由命令行构建一个目标:

$ make target_file

如果 target_file 不存在,或者 prerequisite_file1prerequisite_file2target_file 最近一次构建后被修改了,Make 将会执行给定的 shell 脚本。不过在此之前 Make 将会先检查在 Makefile 中是不是有针对 prerequisite_file1prerequisite_file2 的内容,并且按需构建或重新构建。

Makefile 规则的一个实际例子

一个最小的项目可能有一个叫做 src/index.js 的文件。我们需要一个规则来告诉 Make 转换该文件并将结果写入 lib/index.js。但是 Make会以相反的方式看待这件事:Make 希望被告知需要的结果,然后使用规则来指定如何产生这个结果。所以我们编写这个 Makefile 的规则的目标是 lib/index.js,条件是 src/index.js

lib/index.js: src/index.js
    mkdir -p $(dir $@)
    babel $< --out-file $@ --source-maps

这个菜单通过 babel 转换 src/index.js 生成 lib/index.js。Makefile 的菜单中的 shell 命令与你在 bash 中输入的内容几乎完全相同——但是请注意,Make 替换变量和在命令前以 $ 开头表达式将会被执行。你可以通过加倍 $ 来转义(比如说 cd $$HOME)。在上面的菜单中有两个特殊的变量 $< 是对条件列表的简称(本例中为 src/index.js),$@ 是目标的简称(本例中为 lib/index.js)。我们会在瞬间明白为什么这些变量是不可或缺的。

mkdir -p 行在 lib/ 目录不存在的情况下创建行。函数 dir 从文件路径中提取目录部分,因此 $(dir $@) 读作「包含被 $@ 引用的文件的目录路径」。

广义规则

当我们向项目中添加更多的文件时,为每个 JavaScript 文件编写一个 Makefile 目标将非常的繁琐,目标和条件可以使用通配符来创建一个模式:

lib/%: src/%
    mkdir -p $(dir $@)
    babel $< --out-file $@ --source-maps

这告诉 Make 任何的由 lib/ 开头的文件路径可以用给定的步骤来构建,并且目标依赖于 src/ 下的相对应的路径。无论什么字符串在 Make 中的目标中用 % 表示,他会替换条件中的相同位置的 % 字符串。现在我们更清楚为什么变量 $<$@ 是必须的了:在规则调用之前,我们并不知道这些变量的值。

为什么分别为每个源文件调用 Babel

通过一次调用,Babel 就可以传输目标树中的所有文件。但上面的规则会为 src 下的每个文件运行 babel,每次运行 babel 时都会有一些启动时间的开销,所以在进行一次提交中多次调用 babel 会更慢。但是,由于 Make 的增量构建能力,它将跳过 lib/ 下已经有最新结果的文件。我们运行增量构建的次数远远多于完整构建,所以我非常欣赏这种加速。

编辑:Hacker News 中的几位评论者(falcolas, Jtsummers, jlg23, nzoschke)指出 Make 可以并行的执行任务。因为 Make 规则中明确的列出了每个目标的依赖关系,所以知道哪些任务可以安全的并行运行。使用 make --jobs=4 命令可以一次执行最多 4 个 Babel 实例,这可以抵消为每个源文件运行单独的 Babel 实例的性能损失。

定位 Babel

我在 Makefile 的上述的规则中做了一点微小的改动:

babel := node_modules/.bin/babel

lib/%: src/%
    mkdir -p $(dir $@)
    $(babel) $< --out-file $@ --source-maps

babel 可执行文件由 babel-cli 这个 npm 包提供。我更喜欢将 babel-cli 安装作为项目的 dev dependency,这会导致 babel 的可执行文件安装在路径 node_modules/.bin/babel 上。这样,任何想要构建我项目的人都不必采用特殊步骤去安装一个全局的 babel-cli,但是大多数机器上,babel 并不会配置在可执行的 $PATH 中,为了避免输入可执行文件的路径,我将 babel 的位置分配给 Makefile 中的变量(babel := node_modules/.bin/babel),并且使用 Make 的变量替换将该路径作为菜单中的命令。

(专业提示:您可以将 node_modules/.bin 添加到您的 shell 的 $PATH 中,像这样:PATH="node_modules/.bin:$PATH"。这样可以很容易的运行当前目录中项目依赖项安装的可执行文件。与项目一起安装的优先级会高于全局安装的可执行文件,当你运行 npm 脚本时,npm 会自动进行这个 $PATH 优先级得陶正。不过我总是在 Makefile 中声明 babel 的 path,因为我不想假设任何人做了同样的设置,而且我也并不是总想从 npm 脚本中运行 make。)

转换整个项目

用如下的规则来转换 JavaScript 源文件:

$ make lib/index.js  # outputs lib/index.js and lib/index.js.map

Make 在 Makefile(lib/%) 中找到匹配的项目,展开通配符,通过展开 src/% 来找到匹配的源文件,并运行 babel。但是你可能不想为每个源文件手动运行 make。你想要的只是输入 make 并让它转换所有的源文件。记住 Make 需要被告知你想要的结果。为此,首先需要计算一份所有源文件的列表,并且将其分配给一个变量:

src_files := $(shell find src/ -name '*.js')

在这个任务右侧的表达式使用了 Make 内置的 shell 函数来运行外部de shell 命令。在这种情况下,我们应该使用 find 命令来递归的列出 src/ 下面所有扩展名为 .js 的文件。您可以使用另一个命令,比如 [fd][]——不过 find 更可能已经被安装在您同事的工作站和你的 CI 服务器中。

这下我们拿到了我们所有的文件列表。但是我们需要告诉 Make 我们需要的文件。对于每一个在 src 下面的文件,我们都希望在 lib 下面有一个对应的转换后的文件。我们可以通过将 Make 的 patsubst 函数应用到每个源文件中来计算该列表:

transpiled_files := $(patsubst src/%,lib/%,$(src_files))

替换表达式使用 % 作为通配符,与我们之前编写的规则相同,写起来更容易了。

现在我们可以定义一个列出我们想要的文件作为条件的目标。当我们请求该目标时,Make 会自动为每个源文件建立一个转义的结果:

all: $(transpiled_files)

目标名 all 是特殊的:当你在运行时没有指定 make 的目标时,他会执行 all 作为默认值。这是当目标不是文件或者目录时的特殊情况——all 只是一个标签。你需要像这样在你的 Makefile 中声明非文件的目标,这样 Make 就不会浪费时间,也不会为了在你的项目中找到匹配的文件而感到困惑。

.PHONY: all clean

哦,是的!或许你想要一种方法来删除已经构建的内容,以便你可以开始干净的构建,有了这个目标你可以运行 make clean 来达到这个效果:

clean:
    rm -rf lib

在 package.json 改变时自动安装 node modules

Make 非常强大,足以完成任何你可以想象的任务。你是否曾经提交更新给一个项目,并且在进行一些 debug 后发现你是忘了运行 yarn install 来更新你的依赖关系?你可以通过 Make 来做到!当你运行 yarn install 时,node_modules 目录将会创建或者更新。你可以在 Make 中添加一条规则将 node_modules 作为目标来更新 node_modules。

node_modules 的状态取决于 package.json 和 yarn.lock 的内容,所以这些文件应该作为条件被列出:

node_modules: package.json yarn.lock
    yarn install  # could be replaced with `npm install` if you prefer

这个针对 all 目标的变更添加了 node_modules 作为条件。

现在,当且仅当自上一次构建后 package.json 或者 yarn.lock 发生了变化时,Make 才会运行 yarn install。我在 $(transpiled_files) 之前放上了 node_modules,以防万一新的依赖可能会包含 babel 模块的更新,这样会影响到项目文件的构建。

监听文件并且重构更新

每个构建工具都应该有适用于快速开发的监听文件变更的选项。你可以通过 Make 与通用文件监听工具的组合来达到这个效果:

$ yarn global add watch
$ watch make src/

记得注意你没有监听 lib/ 目录,否则的话你会陷入一个无尽的构建循环中。

使用 Make 来分配 Flow 类型定义

我在上文中提到我也经常使用 flow 来检查我的项目,我想使用纯 JavaScript,但我也希望任何使用我的库的人使用 Flow 来从我的类型注释中获益。Flow 支持查找一个使用了 .js.flow 的文件扩展名。比如,当你导入一个名为 User 的模块时,JavaScript 运行库将查找名为 User.js 的文件,而 Flow 将在同一目录中另外查找名为 User.js.flow 的文件。该文件应该是具有类型注释的原始源文件。我的 Makefile 将 src/ 下的每个文件复制到 lib/ 的响应路径,并根据此规则添加 .flow 扩展名。

lib/%.js.flow: src/%.js
    mkdir -p $(dir $@)
    cp $< $@

为了确保 Flow 中所有的源文件按照此步骤运行,我计算了 .flow 的文件列表,我期望得到与我们预期相一致的结果:

flow_files := $(patsubst %.js,%.js.flow,$(transpiled_files))

另外,我在所有的任务的条件中包含了 flow_files

all: node_modules $(flow_files) $(transpiled_files)

进阶

Make 还有很多我在这里没有提及的功能。例如,Make 支持可以为特别复杂的用例计算规则的宏,用 Makefile 可以委托目标给其他 Makefile。这在分发 Make 库中非常有用。也可以用于构建过程中涉及到构建多个子项目组合在一起的多层项目,GNU Make Manual 可以找到更多有用的信息。

fixed 与 transform 相爱相杀的故事

$
0
0

上周和上上周一直苦恼于加动画,因为作为一个永远只能脑补出第一帧和最后一帧的动画渣,永远都不知道中间发生了什么,而这次遇到了一个问题,本身我的位置依靠的是 position: fixed 和一个依靠 display: flex 定位居中的元素来进行布局的,在动画表现时出现了位置不对的问题。

在一番 Debug 后(Emmm 具体的 Debug 方式就是控制变量法,人肉 Debug),我最后确定是 transform: scale(...)position: fixed 的冲突问题。

具体的例子可以看下面的 Demo:

在这里,我们可以看到 transform: scale(...) 导致了 position 的表现从 fixed 变成了 absolute,直接导致了这一问题。

关于这一点,在 MDN 里其实是有解释的,只是一直没有关注过这一句话:

fixed 属性会创建新的层叠上下文。当元素祖先的 transform 属性非 none 时,容器由视口改为该祖先。

好的好的,原来这个并不能算 Bug,反而称得上浏览器的 Feature。实际上这个 Feature 在不同浏览器的表现似乎也是不同的,这里暂不赘述,当然,也是有解决方案的。只要把父级元素的 scale 改为对本元素自身的操作即可。

也就是说,我们原本对于整体的缩放,现在变成了孤立的两个元素的缩放。

关于更详细的 transform 相关话题和 fixed 的坑,在查阅资料时发现了以下两篇文章可供参考:


Vue 再来唠唠双向同步

$
0
0

之前其实谈到了双向同步在 Vue 中的实现方法,尽管 Vue 在 2.x 移除了 .sync,在 2.3.0+ 又重新引入。这一次也并不能简单归纳为「重新引入」,而和 v-model 一样,改为了一种语法糖。

先来重新看下 Vue 官方对于 .sync 的定义:

<comp :foo.sync="bar"></comp>
<!-- 会被扩展为 -->
<comp :foo="bar" @update:foo="val => bar = val"></comp>

当子组件需要更新 foo 的值时,它需要显式地触发一个更新事件:

this.$emit('update:foo', newValue)

这一点和 v-modelvalue @change 一样,其实只是一个比较方便的语法糖。

不过当时一个同学遇到了一个问题:为什么我的 .sync 没有用了?

场景大概是一个数组遍历绑定每个值双向绑定给子组件,然后发现数组没有成功的被重新渲染,刚开始以为是在 .sync 上的问题,因为之前并不知道场景是在「数组」上,于是推测了半天都不对。

大家看我这么强调数组就知道了,在数组时一定要记得:不要直接覆盖数组的值:

arr[i] = xxx

这样赋值没有办法触发 Vue 的监听机制,这一点与你是否使用 v-model 或者 .sync 都无关,倒不如说双向绑定时特别需要注意这个情况,避免「无法更新」。

CSS 父级选择器(parent selector)探究

$
0
0

之前有个小伙伴问了一个问题,我觉得挺有意思,也是我之前有几次想到的,后来由于种种方法曲线救国,就这么忽悠过去了。

这次在 Google 又查了一下,依旧没有 parent selector,参考 CSS TRICKS介绍了两种方法:

有人建议这样来选中有 img 的 a 标签:

a < img { border: none; }

也有人建议这么使用:

a img:parent { background: none; }

然而并没有什么卵用,CSS 标准中并没有采纳。

不过在 Stackoverflow 中的答案,在 CSS4 中已经有这样的草案了:

a:has(> img)

然而并不是很靠谱,因为完全没有浏览器对这一草案特性进行了支持,真是个悲伤的故事。

另一方面,鉴于目前并没有 CSS 选择器可用,我们当然可以用 JavaScript 处理 parent 所需要的效果,但是同时,我们更寄希望于 CSS——毕竟,能用 CSS 的真的一点都不喜欢用 JavaScript。

在张鑫旭的博客中给了一种新的思路——使用了兄弟选择器来进行操作。

大致的意思是用一个子元素作为父元素子树中的最后一个元素,其大小和父元素一直,然后就可以处理一些本来希望让父元素表现的效果。不过,这样的效果种类毕竟有限,在例子中是实现了一个父级的 border 高亮,但是如果我要在选中子元素的时候让父元素菊花一紧(缩放大小)呢?似乎就不太可行了。

本文说了半天,其实就是想介绍一下未来和现在可以处理的方法集锦,属于抛砖引玉,希望大家多多海涵。

技术写作指南 - 起

$
0
0

这篇指南将会将你如何创建从手册到工作指南的任何内容。我们将帮你避免所有最常见的技术写作陷阱——从不良计划到过时出版。

译者按:本书原文分为不同的 Chapter,为了方便发(cou)表(gengxin)拆分成起承转合四章,小标题按照原章节。
原书:https://www.dozuki.com/Tech_Writing

Chapter 0 - 欢迎光临

所以,你决定写一本手册。我们衷心祝贺你在这里带领你自己 DIY。欢迎来到技术交流的世界!

15203306470298.png

现在,在这个时刻,你可以能会对自己轻笑:“技术交流?……这不是一个矛盾吗?”

我们懂了!不好的手册有过很多……Emmmm一言难尽。有一种普遍的体验:你拿出了小朋友的圣诞礼物,然后打开了组装手册,突然间,一切都变得可怕了起来,有什么地方错了错了!三个小时后,你费尽心机想要展示的是一个突变三轮车。小朋友会不高兴。

学习一些东西最好的方法是让一位专家来教你,不过手册是第二好的。一本很棒的手册,比如 iFixit 或者 Mackie 的手册,都是一位位老师。

我们推测你来这里的原因是——你想编写一本实际教导人们如何做事的手册。

我们需要帮助。该计划将教你如何撰写操作手册,工作指南以及服务手册——从计划到写作到出版,我们还将帮助你避免技术写作中最常见的缺陷。

手册很重要,无论您是在写关于如何对数控机床执行维护工作、使用视频编辑软件、烘焙鸡蛋奶酥、还是重新构建浏览器,你都是在教别人一些新的东西。如果你的手册成功了,读者将会做到一些在没有你的帮助下没法做到的事情。这真是非常 Amazing!

15203312172273.png

Chapter 1 - 在你写作之前先看看

卓有成效的写作是一项成就。现在的说明并不仅仅应该是一个有用的方向清单。他们应该拥抱这个时代的审美和惯例:高度可视化、时尚化、互动性和精心设计。当它们都被精巧的设计完毕时,这将会是一条通往优秀的途径。

15203314137637.png

现在,你可能醉心于写作。但是,在你开始奔向技术写作的夕阳之前,请记住:大部分手册和指南都是由没有第一手知识的人撰写的。我们认为者存在一个问题。编写一个好的手册需要的不仅仅是写作技巧,同时也需要理解。和技术写作有两条法则:

  • 了解产品和过程
  • 与专家交谈

了解产品和过程

技术写作的第一要求是知识,在你自己学会之前,你不能教别人怎么做。如何你正在编写汇编指南,请将产品放在一起。如何你正在写关于软件的内容,请使用这个程序。如果你正在编写一本产品的手册,你应该知道产品的内部和外部。使用它,把它拆分开,弄清楚它的工作原理和它这么做的意图。

一旦你认为你已经了解了这个过程,就试着交给别人。教学是巩固自己知识的一个很好的方法,并且通过观察你的学生的努力消化你所教授的内容会让你的手册变得更好。

和专家交谈

如何你不是你将写作内容这方面的专家,那么和别人交谈是个不错的选择。与开发人员、技术人员或者设计人员聊天。让他们给你一个产品、过程或者软件的演示。询问他们是如何创造的,为什么是怎么做的。然后,如果你需要帮助,记得不断的求助。

尽可能的多收集他们的故事,了解整个创造过程会帮你梳理你的理解。

通常如何编写手册

通常如何编写手册:技术编写者会根据原始功能的规格创建初稿。当然,真正的产品往往不符合最初制订的初稿,初稿完全是在浪费时间。作为令人沮丧的审查过程的一部分,工程师给作家潦草的手写笔记。技术作家编写另一份草稿,不过工程师又很快把它给撕了。这过程又一次重新开始。最后,文档终于发布了。

但它不一定这么绝对。工程师与作家之间的互动越频繁,最终的产品就会越好。

Chapter 2 - 简洁

样式提示 #1:单刀直入,一招毙命。然后停止写作。

如何你正在为了互联网写作,那么这条规则就适用了。出于某些原因,Chrome / Safari / Firefox 都被称为 Web 浏览器,而不是 Web 阅读器。人们不会阅读网站,他们只会匆匆过目,去寻找他们认为相关的单词和短语,普通人在一个网页上只会花上几秒的时间,只能阅读 20% 的文字。你写的越简洁,读者实际获取到的信息就越多。

从传统的角度来看,甚至纸质手册也不是「精读」的。没有人在晚上用手册卷起自己。像在网页上一样,人们只会寻找他们想要的信息。手册中信息越密集,人们就约难以挖掘到其中有用的信息。

比如说,看看这个厨房设备的保修声明,真实的故事:

示例1:我们建议您及时填写并寄回随附的产品注册卡,以便我们确认您的原始购买日期。但是,产品注册卡的寄回并不能减免消费者保留原始购买证明以获得保修权利的需要。如果(In event that)您没有购买日期的证明,则此产品的购买日期将为生产日期。

看着只有三句话,但它太啰嗦烦人了,一点都不人性化,下面是我们的修改:

示例2:请寄回填完的产品注册卡,以便我们确认您的购买时间,保留购买的原始凭证以确保您的保修权利。如果(If)您不知道购买时间,请改为生产时间。

这不更棒吗?

如何让段落更简洁

提炼重要的信息:将有用的信息前置。假设你的读者们不会在阅读整个段落中打嗝,当你从重要的信息开始时,你的读者将把重点放在这些信息身上——即使他们没有阅读完全文。

抛弃不重要的信息:读者只需要事实,所以删除任何离题的信息。消除额外的噪音,如果你在教我们如何重建汽车引擎,那么我们不需要听任何野马的历史。只要给我们说明就好。

检查你的字数:示例 1 有 76 个字,我们修改之后有 37 个字。言简意赅是一个伟大的目标!

专业提示:简洁,尽可能的减少单词而不改变含义

如何让句子更简洁

短句是你的朋友:自作聪明的作家长长喜欢用非常、非常、非常、非常长的句子。专业提示:真的不要这么做!太长的句子让人困惑。努力让句子不超过 24 个字。是的,我们知道——有时候你的产品名都会比这个长。不过,尽你所能,你的段落会因为健康的句子长度而变得更好。

这是一段来自反铲手册的长句:

将小型90°适配器接头组装到过滤器底座的出口端口并定向,使接头的自由端指向反向铲,并从水平向上成约30°角。

现在,这是我们用三句短句替换一个长句的版本:

将小型90°适配器接头连接到过滤器基座的端口。 配件的自由端应指向反铲。 将配件角度向上水平约30°。

删掉任何空洞的单词:空洞的单词就在那里——像长卷木中的一个包。回顾一下保修的范例,反铲机手册使用了如果(In the event of),其实就是 If 的同义词,但我们为什么要把一个词就能说通的话用四个字来表述。

减少被动语态的数量被动语态整天闲着刷存在感,然而并没有做多少实事。当然,不要过分追求而把你的每一个被动语态都删掉。有些句子要求使用被动语态——绝对不能替代它。但是,在你有选择的地方,使用一些主动语态来代替被动语态——将句子中的动词向前移动。有一个有趣的事实:本段不包含任何被动语态。

15203424144733.png

这里有一个汽车装配手册的例子:「如果你损坏了任何零件,可能是因为它们没有被妥善的保存好,或者使用了错误的工具来安装它们」。

这里同一句话中有三个被动动词,我们删修订了新版来修正:「不正确的存放工具或者使用错误的安装工具可能会导致部件的损坏」。

战略性的使用被动语态:不管你的英语老师在十年级时说过什么,使用被动语态都不会让你成为一个坏人。记得有目的的使用被动语态。除非你有不得不使用被动语态的理由,否则请切换成主动语态

以下是用户手册中被动语态的示例:「加高座椅应该被用于适当安装的安全带」。

谁安装的安全带?是谁在使用?这就是被动语态的问题:没人知道。句子的结构并不是《推理女神探》中的一集。没有人应该去猜测是谁做了什么。如果正在书写大方向,请以动词开头。

让我们尝试用主动语态重写:「使用加高座椅来正确安装儿童安全带」。

技术写作指南 - 承

$
0
0
译者按:本书原文分为不同的 Chapter,为了方便发(cou)表(gengxin)拆分成起承转合四章,小标题按照原章节。
原书:https://www.dozuki.com/Tech_Writing

Chapter 3 - 如水晶般明晰

想象一下你的话像一扇滑动玻璃门。现在脑补一下:撞向玻璃门——贼硬。这就是你的写作应该多干净:干净的要命。

避免混淆:一种常识性的方法

查看这个产品说明:

为了让来让粗略构思的机器设计理念尽善尽美,工作一直在进行着。该机器不仅能用于单边相减振器的你像反应电流,而且还能够自动同步红衣仪。这种机器就被称为涡轮机(Turbo-Encabulator)。

你 gai 到点了吗?好的,我们也没有。

涡轮机(Turbo-Encabulator)完全构成了。不过,当涡轮机的描述出现在 1946 年的时代周刊中出现的时候,大部分人都认为它是真的。为啥呢?因为他们之前看过其他不好的手册和产品说明。涡轮机(Turbo-Encabulator)是对技术写作的一种模仿,和所有的模仿一样,它太搞笑了,因为它基于现实。

另一方面,这是 100% 真实的:

延迟旋钮主扫水平方向,并且模仿机械制动,会在 0.00s 秒暂停。网格顶部是一个实心的三角符号和一个空心的三角符号。该符号表示触发点,并随着延迟时间旋钮一起移动。该符号表示时间参考点,也是放大/缩小的参考点。

当然,这个示波器操作手册不是为新手设计的。不过作者不必要的提到了「机械制动」,并且引用了一个没什么卵用的被动句。这使得句子变的难以理解。

那么,你如何避免让你的读者感到困惑?请记住,真人将会阅读你的文章,而且他们可能并不像你一样在技术领域拥有这样的知识。这里有几个建议可以让你的写作更加人性化:

使用朴实的语言:你知道吗?使用朴实的语言是一种运动。朴实的语言「关注读者」。它确保读者可以「快速轻松的发现他们想要的东西,了解他们需要的东西,并根据这种理解采取恰当的行动」。使用简单的英语,大多数人能理解的单词和间断的语句。

简单可以引人注目。我们在阅读 William Zinsser 的写作时写道:「林肯第二次就职演讲中的 701 个单词中,这本身就是一种经济奇迹。505 个是一个音节的词,122 个是两个音节的词。」

放下行话:或者至少尽可能的使用它。行话只有在特定的学科中使用。不过,没有行业之外的人知道这意味着什么。如果你一定需要行话,尽量提供上下文,简短的定义,甚至术语表。有的时候,行话只会让你显得更蠢。例如:

额外升降运输控制杆
将小件物品放在烤面包机的顶部附近,方便拆卸。

15203474004613.png

额外升降运输控制杠杆?你说……为了方便烤面包拆卸?对你来说可能挺好……但是我们这些人只称它为「控制杆」。

不要把动词转换成名词:动词很高兴成为动词。当它们不想成为名词时,不要强迫它们成为名词。动词转换的名词让你的句子不愉快,反过来让你的读者不高兴。

下面是一些关于汽车装配线完全不可分离的工作说明:

控制杆(手柄)完全压下:
用于让发动机离合器完全脱离变速器;平稳换挡意味着正确调整离合器电缆。

现在,我们不确定标点符号到底经历了什么——我们可能永远整理不出来正确的标点使用形式,但是这些动词的名词化我们还是可以纠正的。我们要这么做:

完全脱离变速箱的发动机。正确调整离合器电缆以实现平稳换挡。

冠词不是敌人:冠词是那些名词前的小词,比如这个(the),一个(an & a)。在编写指南时,人们倾向于完全忽略冠词。他们会说:「断开电源线与墙壁的连接」(Disconnect cord from wall),而不是「断开这个电源线与其墙壁的链接」(Disconnect the cord from the wall)。我们还没有把这种遗漏的原因找出来,不过我们推测可能是因为这种机械的辞令更像是根植于内心的呐喊一般。直到奇点打击,请自由的在需要的位置使用冠词吧。

15203903388594.png

将其转交给新手:当你的写作不够清晰时,有时一件事情可能很难表述清楚。想确定你是否表达清楚了吗?把你的作品交给一个对这个主题毫不知情的新手吧。尝试一下走廊可用性测试(Hallway Usability Test):把你正在做的事情交给五个只是碰巧经过走廊的人。每当有人说:「我不知道这啥意思」的时候,就代表你已经偏离了行进的轨道。

iFixit 把原版服务手册交给了毫无准备的艺术系学生来进行测试:我们给了他们一台计算机和我们的新手册,并看着他们使用手册来把它们拆开。每当他们遇到了困难,我们就知道我们的手册有些问题。我们从他们的尝试中学到了如果去改良我们的手册。

不要使用模棱两可的单词:有些单词会把你句子中的魅力偷走。「稍微」、「看起来」、「有点」、「很」、「一丢丢」就是这些坑爹的词。它们想你的读这表明,你的意思是……你所说的,并不是那么靠谱。螺丝「很难拧紧」还是「难以拧紧」?是碰到你的前任「相当不舒服」还是「彻头彻尾的不舒服」,说出你想表达的意思,而不是模棱两可。

Chapter 4 - 写出自己的风格

手册并不是通俗小说,但这并不意味着它们不得不称为助眠神器。说明很重要,人们确实需要他们。作为作者,我们的任务是不让阅读手册让人觉得这么痛苦。写出你自己的风格!

风格并不等同一诗意的语言和强烈的意象。每个作家,不管技术如何,都有属于自己的风格。风格是你作为作者与读者的交流方式。风格包括语气、幽默、以及你的写作形式。良好的风格有益于做出抉择——知道应该包括什么、应该省略什么,何时遵守规则、何时出其不意。

你可以打破我们所列出的所有规则,不过你应该有一个明确的理由。例如,在第二章,我们告诉你要单刀直入,但是有时候,解释一个功能最好的方法是讲一个故事。在某些特定的场合下,打破这个规则也很好。讲故事的好处超过了单刀直入的好处。但不要再没有充分理由的情况下绕开规则。

允许不正经编写——但这种不正经程度应该根据主题而有所不同。如果你正在为核电厂维护程序书写手册,最好是坚持正式的,类似商业的语气。如果你正在编写如何建立后院烧烤的说明,请随意以友好和不正经的方式书写。

15203945156827.png

Mackie 的用户指南就是一个给你的灵感。Mackie 的风格是对话式的。他们了解他们的听众——专业的音响工程师,以及如何让他们 gai 到点。他们像朋友一样与读者对话,同时仍然传递详细的指南和专家级的知识。

这里有一些 Mackie 风格的亮点:

幽默的指南:
为自己打包一顿丰富的午餐,然后去外面散个步。野餐、躺下、做梦。世界如此美妙。

15203946690269.png

现实主义
仔细看这个图标会引导你对功能和实用技巧做出一些解释。如果你要匆匆离开房间,请跳过这些步骤。

15203979462443.png

讲个故事
两用静音/调换 3-4 开关是 Mackie 的代表作。当 Greg 设计我们的第一个产品时,他不得不为每一个通道都配备一个静音开关。静音开关的功能正如他们的名字那样。他们通过「遗忘」信号来关闭信号。『哎,太浪费钱了!』他感叹道,『为什么不能让静音按钮将信号路由到有用的地方,比如单独的立体声总线?』
因此,两用静音/调换 3-4 开关提供了两种功能—— 静音(通常在混音或现场演出时使用)和信号路由(用于多轨和现场工作),作为额外的立体声总线。

想要发展你自己的独特风格?下面是一些注意事项

幽默:每个人都喜欢幽默。然而应用时,你需要格外小心。幽默和机智不可能在翻译中传神,所以只有当你在为母语读者写作时才能用到它们。毕竟,美国的笑话在冰岛、土库曼斯坦或者日本可能并不好笑。另外,幽默很难有度。它需要反复推敲。甚至有时候并不合适——不要再预防措施和任何可能产生法律后果的情况下使用幽默。另外,讽刺在你自己听来可能有趣,但对于大部分人,这只是尖酸刻薄。

尽管存在一些缺点,幽默有时候是最让人难忘的方式,你自可以谈论电容器直到你脸色发青,不过他们更会记住开怀大笑的一刻。

以下是一些关于如何写的幽默的提示:

  • 只在让你发笑时写下来
  • 消除有趣的部分,并且写得更好
  • 不要强行搞笑。只有当它本身是搞笑的时候,它的效果最好。
  • 不要影射他人。你唯一能开玩笑的人是你自己。
  • 在写作前读一些滑稽的东西。我们非常推荐一个有趣的新闻记者:Dave Barry

幽默主义者 Sherman Alexie 建议:「直率的表述——这会引起你发笑」。不要在办公室尝试,它有趣依旧,但你很可能被解雇。

设定语气:你的写作可以是严肃的、权威的、新闻式的、友善的、幽默的,或者任何你想要的形式。作为一名作家,你要设定一个语气。但是不要在文档的中途改变。找到一种形式并且坚持下去。

解读观众:一个经典写作难题:作家在提到读者时可以说「你」吗?有时候,人们应该避免用第二人称来产生距离感和公平感——尤其是在科学方面的主题。但如果你告诉读者该做什么,那么「你」或者「你的」是完全可以接受的。事实上,如果是有目的的使用,非正式的「你」听着更加自然和鼓舞人心。

例:「如何将图像上传到电脑」与「上传图像到电脑」
例:「从连接器上拔出导线时要小心」与「应该小心的从连接器上拔出导线」。

当然,最终选择取决于你。

使用缩略语:使用缩略语还是不使用缩略语,这是个问题。我们有个微小的答案:「使用他们更高贵。缩略语可以缩短你的句子,改善整体的流程。」如果你怀疑这一点,请尝试朗读一段没有缩略语的句子。这听起来太烦人类了。在缩略语和段落之间来回更显得像人能看的。

简短的段落是关键:在科技写作中,格式良好的五句段落被搁置了。你并不需要在你的手册中使用这种技巧或者主题句。同事,你也不需要利用长段落来进行断言。

当你开始一个新的想法时,你可以开始一个新段落(看看我们这里是怎么做的)读者喜欢短段落。简短更容易浏览。我们喜欢用简短的、声明式的子弹把内容分成一块块小的易于阅读的结构。

国际化:如果你计划向国际读者发布,那么你需要翻译你的手册,这是一个成本极高的过程。正确的风格将会让翻译的过程变得更容易。一般的风格要点是这样的:把你的词汇量限制在简单 / 常用的词汇中更为重要。但是当你在写作翻译中,避免使用笑话和成渝(他们翻译不好),适度的使用缩略语,并与你的措辞保持一致。例如,本教程将很难翻译成另一种语言,我们使用了很多口语和成渝,以及一些比喻句。而且我们并没有打算限制我们的词汇量。这是我们为了保证这个(长篇)教程提供的信息和参与感而做出的决定。这些事作为作家必须做出的取舍。

填充一本图书有足够的国际化差异,请查看The Content Wrangler

注意事项:如果你打算将手册翻译成其他语言,那么使用大量的缩略语可能会增加你的工作成本。

Chapter 5 - 读者

写作永远不会发生在真空中(除非你真的在真空中,这就让人觉得难以置信的不爽了)当你在写手册时,你总是在为别人而写的。找出那个人是谁。你对他们了解的越多,你写的就越好。

优化你的手册以符合目标群体的专业知识。例如:傻瓜系列丛书已经瞄准了一个小的用户群:一无所知的新手。对于砂管出版社有一本关于从写简历到教育儿童的书。每一个功能都附上插图、扩展解释、上下文和基本提示。

写给傻瓜的汽车修复中有一整章致力于改变你的汽车机油。它涵盖了汽车中的油含量,合成油的优缺点和说明——整整 15 页!然而,如果这本书的标题是汽车修理专家,那么整个章节可以归结为一句话:「换油」。除非他们要给超级跑车换油,否则汽车专家不需要 15 页来指导换油。

了解你的读者意味着指导要包括什么和不不包括什么。这意味着你要向他们解释一些事情需要多少步骤。这意味着你知道你的手册要多少篇幅,以及你可以用哪几种语言。

这里有几件需要你铭记于心的事情:

阅读水平 = 写作水平

你不应该高估你的读者,也不低估你的读者,尽管低估在我们的经历中很少见。

Flesh-Kincaid 可读性测试是确定文本可读性的最常见的方法。Flesch-Kincaid 以 0-100 来打分阅读的难易度,100 为最容易阅读的。另一个 Flesch-Kincaid 测试是用于评估阅读等级的(K - 12)。

普通美国成年人的阅读水平大概在九年级。

为了向你说明多少的手册会忽视这个问题。我们决定使用 Flesch-Kincaid 标准来测试一款热门的产品:苹果 Macbook Pro 的制造商手册。苹果的用户手册评分为 2.2,阅读等级为 24(可能你会想:不,成绩不会这么高!)。作为参照,莎士比亚的 麦克白 是 11 年级的阅读水平

有一个较为靠谱的经验法则:用户手册不应该比莎士比亚难读。

15204111592076.png
iFixit 的针对同一设备的维修手册大概在 4 年级的阅读水平。阅读水平打分阅读,你的读者就越可能理解你在说什么。

不确定如何衡量你的写作水平?你可以使用「可读性测试工具」等网站来衡量现有网站的可读性。Word 和 Excel 已经内置了可读性工具,你只要启用他们就行啦。

已有的知识

假设你的读者已经知道了一些知识?技术作家经常会忘记他的读者知道些什么,尤其是,他们不知道什么。机械工程师有时候会编写听起来像是为其他机械工程师编写的手册——但是如果目标群众不是超级精通技术的人,那么在没有解释的情况下去讨论「剪切载荷」、「螺旋齿轮传动」和「运动链」就成了一个问题。

军事组织在这方面做得尤其糟糕。他们有这么多的缩略语,所以与任何人沟通之前,你必须要花几个月时间学习所有的缩略语。以下是一个陆军舰载操作手册的例子:

a. 紧急培训。即将部署的所需的紧急需求或培训通常将会在 MACOM(比如 FORSCOM)从 DA 处获得。陆军航空兵部队将在调度船舶和其他资源方面获得更高级的指挥协助。

技术协作的标准做法是在第一次使用时明确缩写,将首字母缩写放在括号内。这确保了让读者一目了然。

技术写作指南 - 转

$
0
0
译者按:本书原文分为不同的 Chapter,为了方便发(cou)表(gengxin)拆分成起承转合四章,小标题按照原章节。
原书:https://www.dozuki.com/Tech_Writing

拍摄过程

有人曾经说过……一图胜千言。一点不错!不要只告诉读者怎么做,向它们展示一下怎么做。

纵观历史,图在手册上并没有得到足够多的关注——甚至是在服务手册上,照片大概是不太好的刹车与停下来的汽车之间的区别。标准化的圣杯,ISO 9001 文件通常只有文字。鉴于历史上印刷成本的高昂,这确实有一定道理。但是过去是过去,现在是现在。欢迎来到数字革命:世界成为了你的高分辨率的囊中之物。

15204293235165.png

iFixit 教人们怎么去修理他们的电器。这确实是一笔很冒险的生意。毕竟,在任何设备中都有大量的小部件和微小的连接器。以零插入力(ZIF)连接器为例,它们不仅更小,而且还配备有更小巧,更细腻的襟翼,必须撬开来并整个翻转过来。用错误的方法来做可能会破坏整个设备,不过这却是新手技术人员常犯的错误。这的确有相当的风险。

下面是 iFixit 的文本说明。用于拆卸 iPod Nano 中的 ZIF 电池连接器:

用手指按住浅色插座。然后使用撬棒的尖端将 ZIF 电线锁向上翻转 90°。

虽然这是很好的指南,但这样还不够,有太多错误的空间了。所以,iFixit 的每一步都包含高分辨率的彩色照片。这样,你可以放大并精确计算组件的外观,翻盖的位置以及撬动它的方法。像这样的照片拯救了许多 ZIP 连接器的生命:

15204298967646.png

照片带来生活中的指南。它让事情变得更清晰。将宜家手册iFixit 的自我修复指南相比较,根据清晰程度的不同,修理你的 iPhone 可能比组装一套橱柜更加容易。

15204300791102.png

摄影技巧

不是摄影专家?别担心!现代相机使得拍摄有用的照片变的非常简单快速。我们的技巧和教程让你在瞬间专业摄像。下面的每个链接都有来自 Dozuki 的关于产品摄影的详细步骤指南。

15204306247184.png

设置照片拍摄

拍摄照片最重要的组成部分是选择正确的相机。我们强烈建议你使用数码单反相机(DSLR)拍摄专业品质的指导图像。如果你没有 DSLR 相机,任何至少有 600 万像素的傻瓜相机将可以拍摄足够分辨率的图像。

我们公司(Dozuki)帮助公司创造服务手册工业工作指南等的分布流程

无论您决定使用哪种相机,每个手持相机都容易产生抖动和震动,从而导致图片模糊。使用三脚架,即使最便宜的那种,也能保证你的图像够清晰。

15204309703188.png

你的灯光越好,你的后期越少。不幸的是,你的旧床头灯并不会减少这个流程。别绝望!你可以使用简单的灯具,合适的灯泡与干净的白色表面可以构建一个相对便宜的 DIY 摄影工作室

如果你想投资专业照明设备,适当的照相灯具应装有三个火四个独立的灯泡。为了减少刺眼的眩光,请在每个固定装置的前部装上散光片。

15204313923545.png

拍摄完美的照片

光圈和 ISO:了解光圈和 ISO 将帮助你拍摄专业的照片。光圈设置改变了透过镜头的光亮,人们通常称其为「f-stop」。ISO 设置以牺牲图像颗粒感为代价来改变相机传感器的灵敏度。我们在教程中详细的解释了这两个问题,因此在您开始编写手续之前,请不断地练习、练习、练习,方法就是拍照。

快门速度:只要按下快门按钮就可能会导致相机震动,产生的模糊的图像。模糊是不可接受的。使用远程快门释放工具,计算机共享软件,比如 Nikon CaptureCanon Digital Photo Professional 或者 Soforbild(我们最喜欢的工具)。如果无法连接,请使用相机的自拍器以防设备碰撞。大多数较新的相机都有一个很好用的 2 秒定时器,就是用在这个地方的。

构图:在为手册拍照时,将所做的所有事情都放在框架的中心,并从用户的角度来观察。

特写:放大以获取特定动作的详细镜头,特别是在执行更小或者更复杂的任务时。不过要注意,在超缩放镜头之外中间视图会让观众不丢失上下文。

15204318998549.png

使用双手

如果你正在编写说明,我们建议你尽可能的在图片中出现双手。双手非常擅长展示每一步中描述的操作。与没有包含双手的图片不同,这些图片与实际执行的用户真正想做的任务相关联。用户手册中包含了处理复杂组件的首部特写,可以让用户更好地了解如何复现所需的操作。

iFixit 手册已经培训了数以百万计的新手电子维修工程师,通过使用循序渐进的带手部特写的高分辨率彩色照片展示该过程,看着这些图片,用户可以轻松的自行重复相同的过程。

专家提示:
不要用手遮掩动作。有时这意味着拿一件物品或者工具的方法和平常不同。它可能让人感到尴尬,但由此产生的图像会更清晰的表达你的动作。第二张图比第一张图更有效的展示了打结的方法。

15204323411479.png

处理你的照片

程序:很少有完美的图片,需要一些编辑处理才能使其被用于手册。有许多照片编辑程序,比如 Photoshop(要钱)和 Gimp(免费)可以使用,学习一个软件开始编辑你的照片

颜色和曝光:编辑时,调整光照水平以适当的平衡拜仁和黑人。调整色调和饱和度以纠正背景中的颜色错误。

编辑:并非所有的工作环境都非常干净,因此拍摄对象可能会受到头发、灰尘、污垢或者其他不完美之处的影响。如果可能,用一丢丢编辑魔法来去除瑕疵

标记:在使用摄影作品时,我们建议使用标记来突出显示照片的某些区域。如下面的示例(Dozuki 已经有一个内置的标记系统)。多色框和源泉非常适合突出你想要读者关注的区域。下面的标记颜色已经被选中,以便色彩辨识障碍人士可以区分。

15204328016866.png

使用其他视觉效果

当你想要解释某个作品是如何工作的、如何组装或者修复的时,视频、插图、图解和图表能够发挥意料不到的效果。更棒的是,视觉效果能让文章更简洁。

图表和插图

图表和插图是为读者提供设备、部件、组件的大图片布局的好方法。你包含的视觉内容越多,你必须使用的词语就越少。它们还擅长攻克难以察觉的隐藏元素,例如汽车的接线示意图。

仅当图像可以支持、替换、增加文本时才应该使用。

下面是一个来自 Mackie Onyx 模拟调音台的示意图,这是一台带有大量连接线,差头和装饰的极其复杂的机器。这个不错的图表能够清楚的显示这些插头的功能:

15204331942975.png

Mackie 的图表很棒。不过根据我们的经验,糟糕的图表远比好的多得多。大多数情况下,图表都不够完整,缺乏足够的描述。还有一些效果不好,太凌乱,或者没有足够的书面解释。想象一下,图标看起来像这样,试图把许多东西放在一起:

15204774805826.png

或者,这样怎么样?

15204774935115.png

当你处于复杂的维修或者安装工作中,错误的图表不会有任何帮助。

视频说明

人们喜欢的不仅仅是一张图片,人们更喜欢一张张动态的图片。如果你以电子刊物的形式发布,则可以直接将视频嵌入手册中,听上去很棒吧?

http://ifixit-guide-objects.s3.amazonaws.com/igo/video/ifixit/ofb4HQVGqgNuKSRk_MP4_592.mp4

人们被视频教程所吸引。它们复刻了我们刚开始学习时最希望的一对一,专家对学生的关系。视频教程非常流行,以至于 YouTube 已经成为了互联网中电脑维修设备配置物理等方面的权威。在 YouTube 视频中搜索「如何操作」已经获得了超过 1900 万的点击量。关于如何在两秒内折叠 T 恤的单个 YouTube 教程已经被查看了数百万次了。

视频教程可以让学习变得更容易,但是——一个大警告——他们也容易过度使用或者让人混乱。如果你视频的读者正在查找特定的信息,视频可能会让人心灰意冷。当他们只对定位螺丝感兴趣时,没有人想要观看 20 分钟的视频。

将视频作为文档的主要来源之前,请权衡你的选择。大多数情况下,单张照片或者好的图标在识别连接器、部件和过程方面更有用。而且,与视频不同,图片可以一眼就被看到。建议只有在其他视觉效果无法表述时才使用视频。

如果你想要将手册国际化,避免在图表中使用文字或者在照片中叠加文字——否则,您需要为每个地区制作不同版本的图片。

15204781484490.png

在以下情况下使用视频

  • 演示旋转步骤,比如如何打开一个不太好用的阀门。
  • 演示涉及到的行为,比如为缝纫机穿针引线,撬锁或者打结。
  • 演示不能通过图片或者文字进行描述的状态,比如测试你制作的蛋挞是否「足够松软」,确定引擎正在发出哪种叮当声,或者确定你搅拌的混凝土是否足够厚。
  • 演示使用多大的力量,比如在实际弹出之前,你需要多大的力气才能拉动 iMac 的盖子。

如果你已经决定采用视频指南,那么在编辑室里运用这些规则。视频指南应该简洁、明确、清晰。

我们拍摄了很多独立的视频教程,但我们发现使用视频最好的方法是将他们之间嵌入到在线的逐步的指南中。简短的视频剪辑与图表、照片和文本在一起能发挥非常好的效果,以增强你的教程的可读性。

视频教程频繁出现的问题

问题:在本地化过程中,翻译音频需要额外的工作。

解决方案:视频教程中没有口语。

问题:更新视频来配合新版比更新照片和文本更不方便,成本更高

解决方案:明智的使用视频,避免可能无法用于未来的视频。并将原始视频文件保留在版本控制系统中。

问题:扫视特定信息的视频比扫视文本和照片更慢。

解决方案:缩短视频长度并提供具有针对性的具体信息。

问题:你无法搜索视频所涵盖的信息。

解决方案:给视频加上字母并且让文本可搜索,或者复制文本中的信息。

问题:手册有时需要打印。即使使用黑白打印,照片也可以平稳降级,但是视频信息会完全丢失。

解决方案:使用其他视觉效果,除非视频是必须的。那么就选择一个有用的缩略图图像。

以下是我们的经验法则:录像不应当超过 30 秒。有时 3 秒视频足以说明问题。视频越短越好。

图标和符号

任何专业的第一条规则都是不要受到伤害(尤其是因为伤害会让公司面临诉讼,正如我们在第九章讨论的那样)。因此,尽最大努力不要让人们处于着火或触电的危险之下。

如可能造成伤害,请说实话。但是这是一个难题:大多数读者阅读时,你需要的并不仅仅是大字报般的吸引读者的注意力。这就是图标和符号派上用处的地方。

一些手册,比如 For Dummies 系列,读者需要记住 12 个不同的图标。没有人会在他们的脑海中记住这个多符号。只有少数人会做危险操作时,不要使用一大堆符号。我们建议至少使用「注意」「小心」和「警告」作为安全标志。并非所有的图标都需要翻译。请务必使用普遍接受的图标,比如 splat 符号 或者 ISO 或 ANSI 发布的图标。

借助 Dozuki,你可以将警告符号集成到必要的步骤中。即使没有 Dozuki,你也不必盲目。国际标准化组织(ISO)有一套标准的国际警告和安全标志,而美国国家标准学会(ANSI)则公布美国的安全标准。如果你将国际出版,请咨询两个组织的标准。

15204805110983.png

Chapter 8 - 组织你的内容

没有单一正确的标准答案来组织你的手册。你创建的手册应该反映你正在撰写的任何产品或者流程。以下是我们遇到的组织难题中的一些常识解决方案。

大纲到永远

大纲是写作中必要的部分。大纲就像一个路线图。他们给了你的写作大方向,告诉你你该去往何方。没有大纲的工作就像试图从加州到纽约那般,你只知道该一路向东。

15204809418016.png

让自己置身于观众的位置

尝试从目标受众的角度想象事物。预测你的读者可能有疑问的地方,或者他们如何可以快速达成目标。以此来进行组织。

15204815921381.png

以任务为导向

人们阅读手册是因为他们想要搞懂一些事情。按照「快速开始」、「故障排除」、「更换点火开关」、「使用武士剑来防御僵尸」等活动来组织你的手册正文——无论你认为读者可能有兴趣完成那些任务。

15204816000724.png

使用列表

人们喜欢列表。在互联网上,名单几乎和猫一样流行。不幸的是,我们还没有找到适合的方式让非兽医将猫集成到技术文档中(噢,我们已经尝试过了)。但是,列表是一个导向。无论是编号还是符号,简短的列表都易于理解,具有高可读性。

不确定哪些章节或者小节适合你的手册?互联网上有很多现成的模板,调用它们,不过抵制完全借用它们的冲动。用其设计你自己的结构。看看其他类似流派的手册。找出你喜欢和不喜欢的点,用这些信息来改造你的手册。

15204816148660.png

编写有用的指南

我们多年来一直在尝试编写说明手册。我们所学到的最惨痛的经验教训之一是,伟大的手册不会一蹴而就,他们不得不经过反复的实验和改写。

有时候,读者会在意想不到的地方失败。我们要解释这一点很困难。在 PowerBook G4 上,光驱连接器非常难以看清。我们作家很难解释如何在不破坏连接器的情况下关闭驱动器。当我们在网上发布指南时,有人用了指南并成功破坏了他的电脑。

在我们的郑重道歉后,我们重写了指南。一次又一次。直到他达到完美,以下是重写的最终版:

15204826481259.png

我们在那次经历中学到了一些东西。现在,我们不等到人们损毁了他的东西再来重写。我们将立即征求用户的反馈意见。当我们发布 XBoxPS3 的修理包时,我们征求购买者的反馈。然后我们用他们的反馈来重写指南,我们还在指南中整合了评论功能,以便读者可以分享他们遇到的麻烦并且说出他们是如何克服的。

找到一些方法来获得你的指南的反馈。你不需要一个网站来做到这一点。处于负面反馈的接收端是你做的最艰难的一件事情——但他也是你写作中不可或缺的部分。如果有必要的话,贿赂一下朋友和同事,跪求他们的反馈。饼干、酒精或者是帮他们打杂——无论用什么办法。

公开这些反馈意见可能会让你的手册达到非常惊喜的高效。我们的读者告诉我们,iFixit 手册的评论通常与手册本身一样有用。

组织

编写指南的好处是它们已与组织。按照他们需要完成的顺序去编排。我们发现,使用高分辨率的图片、图表和视频进行逐步说明是设计指南最有效的方式。如果你使用视觉效果进行大部分的说明,那么大量段落就变得不再必要。

说到指南,一些准备工作可以防止让读者变的沮丧。以下是开始工作之前告诉读者的一些好消息:

  • 难度级别:新手应该了解程序的难度。
  • 所需时间:没有人喜欢弄清楚要花 4 小时才能完成的一项任务,这要花掉他们 4 小时。特别是如果他们本需要在 30 分钟前完成。确保预计的时间考虑了任何先决条件所涉及的时间。如果你是一名专家,并且正在为新手读者写作,那么假设他们会花费 20-50% 的时间来执行相同的任务。考虑你时间估计中的差异。
  • 所需材料:如果需要工具,部件和材料,请提前列出它们。

智能的内容复用

15204833311498.png

当你为单个设备或者机器(或者类似的设备)编写大量的手册时,复用内容可以节省一大堆时间、工作和空间。

复杂的任务需要很多依赖程序。例如,无论你是在更换汽车上的刹车片还是旋转轮胎,这两项任务都需要先取下轮胎。我们喜欢称之为相关任务。比如,取下轮胎这个条件——因为你在完成整个任务前必须先完成这个步骤。

复用作为先决条件的步骤,而不是在每次出现时重写它们。大部分出版手册将先决条件指南放在同一个地方,并要求读者在各个部分之间来回翻阅。所以先决条件被列为一个步骤:

  • 第一步:把你的右脚放进去。
  • 第二步:取下轮子,见先决条件中的 3/4。
  • 第三步:将右脚伸出来。

完成先决条件的步骤后,你会回到原来的指南并转到第三步——就想一本错综复杂的「选择你自己的冒险」书。在不同部分之间来回翻转并不方便,但是这是在纸上你可以做的选择中最好的。然而,在电脑上,你可以做的更好。

不要像对待印刷手册那样对待电脑中的手册。这样做对更先进的技术施加了不必要的限制。不要要求读者通过不同的网页来回查看先决条件。而是直接将先决条件指南集成到教程中。这样,你的读者需要的所有说明都在一个网页上。

例如,下面是一个用于移除 Macbook 电池的 iFixit 指南。电池拆卸指南是同一台计算机的 RAM 替换指南的先决条件。你可以看到,MacBook RAM 指南的前两步实际上是取出电池的步骤。Dozuki 软件不需要我们的用户像在印刷书记中那样在各个章节中来回翻阅,而是让我们在同一页面上列出所有步骤,以实现无缝的用户体验。

许多发布平台允许你智能复用内容,将某些过程标记为需要它们的指南中的先决条件,并自动插入于指南。

技术写作指南 - 合

$
0
0
译者按:本书原文分为不同的 Chapter,为了方便发(cou)表(gengxin)拆分成起承转合四章,小标题按照原章节。
原书:https://www.dozuki.com/Tech_Writing

Chapter 9 - 法律要求

到目前为止,本手册涵盖了很好的主题,但是你的手册中必须包涵一些内容。

文档应该使读者准备好安全的使用本产品。美国法律规定,手册必须列出「有意或无意但是可合理预见的产品使用方式」可能造成的任何危害。在下列情况下,你有法定义务警告消费者:

  • 所提供的产品是危险的
  • 危险是或者应该被制造商所知
  • 产品以通常或者预期的方法使用时存在的危险
  • 危险并不明显,但是对用户而言是众所周知的

未能「充分」警告消费者会让公司面临诉讼。那么,究竟是什么让一个警告「充分」?不错的问题。「充分」几乎不可定义。定义不够准确更容易。以下是手册不通用的几种常见问题:

  • 未能警告用户如何正确的使用本产品
  • 未能警告正确使用产品带来的风险
  • 未能警告任何合理可预见的产品滥用

关键的共同点是,列出的所有内容都可能导致人身伤害或者死亡。

你的读者也必须能够看到这些安全信息。所以,警告应该从其他文件中脱颖而出,可能带有图标、彩色字体或者粗体字。它们也应该易于被读者所理解。一个令人困惑的警告与一点都不警告同样糟糕。

许多公司在手册开始时列出警告,以作为突出说明。这很好,不过也请列出读者在你的指南中可能遇到的危险。

当然,将页面和警告页作为先发制人打击诉讼的前锋确实很有诱惑力。很多公司都是这么做的。如果你自己作死把舌头和插头一起黏在插座上,那么任何电子产品对你都是一种危险。只要合理的警告。我们有时会看到各种奇怪的警告:「不要吃你的 iPod Shuffle」「睡觉时不要使用吹风机」「不要把冲击钻作为家庭牙科套装」等等。

15204859039705.png

在你讨论一系列不太可能但是有点微小的可能存在的警告前,我们已经警告过自己:太多的警告,尤其是荒谬的警告,会让整个靠谱的部分看起来很蠢。然后,就没有人认真对待警告——甚至真的警告都置之不理。请记住:仅当危险「合理可预见」时警告用户。参考 Goldilocks 原则找到没有足够警告和太多警告之间的平衡地带。

责任法律因国家而已。欧洲的责任法规非常严格,亚洲的责任法律也开始效仿。如果你计划再国际上销售你的产品,则必须考虑任何区域责任要求。在你发布手册之前了解责任要求和相关国际产品责任。与往常一样,向律师咨询如何编排警告的具体内容。

15204859101943.png

出版

现在,你有很多关于如何发布手册的选择。打印是其中的经典。PDF 本质上是一本以数字形式出版的数据。是现在大多数手册的默认选择。PDF 可以完成工作,但随着人们转向手机来使用手册,PDF 正在迅速变得过时。

15204914733546.png

许多手机正在通过在线发布平台来展示他们的手册。比如 wikis,就像 SecondLife 的粉丝制作的用户指南。索尼为 PlayStation 3 制作了一份定制的在线说明书,而不是标准 PDF。很多公司正在将平板电脑部署到工厂车间,用移动文档站来代替 PDF 工作指南。

现在每个人都带着智能手机,床头柜上有一台平板电脑。信息移动化——你的手册也应该这样。优化你的手册在移动设备上的表现。PDF 手册通常在手机上无法正常工作。源于纸张的传统固定宽度,它们内容太密集,难以被搜索和导航。它们在平板电脑上工作的更好,但错过了现在可能实现的大部分高级功能。

无论你选择如何发布,手册中的信息都应该很容易查阅。用户讨厌翻阅或者滚动浏览数百页来找到它们想要的一个句子。因此,如果你想要在网络上发行内容,请整合 kickbutt 搜索功能,如果你已经撰写了大量印刷手册,请编写目录和索引。

如果你需要一些灵感,请查看 iFixit 的 Android 和 iOS 原生移动应用程序。该软件是开源的,所以你甚至可以复制代码来构建自己的移动手册。

让你的手册可以在网上轻松浏览还有一个另外的关键性优势:它可以被谷歌收录。大多数用户在访问制造商网站之前会用谷歌搜索。iFixit 的用户主要通过谷歌进入——大多数人通过网页搜索来找到我们的指南。

让你的手册很容易在网上被搜索到。考虑一下潜在用户进行故障排查时可能在 Google 搜索框中键入的内容。并且在手册中使用这种语言。随着人们改变他们的搜索条件,计划再未来调整你的手册。

无障碍还意味着针对不同类型的受众进行优化。如果做得不错,盲人阅读器也很容易阅读你的手册,任何人都可以使用翻译服务以她们的母语查看网络手册。用纸质手册获得同样的可访问性是不可能的,PDF 同样很难做到。

变得更好:知识管理

发布并不是你工作的结束。文档需要不断发展。不要挣扎:你的手册会在某些时候过时——而且通常是以你从未想到的方式。

有时候,写作本身就是个错误。我们是人,写作很难。每隔一段时间,作家都会犯错误。当这种事情发生时,人们开始搞事情。没关系。任何产品的软件、硬件或者手册的 1.0 版本都不完美。

你可能会找到一个更简单的方法来处理任务,也许使用平头螺丝刀比飞利浦更容易;也许有新工具可以让事情变得简单;或者可能是流水线工人发现装配产品的一种更快捷的方式。

其他时候,产品本身也会发生变化。生产线在写完手册后进行变更:添加螺丝,重新配置磨具或者进行其他细微(或者并不太小)的更改。

规划是不可避免的。如果很难发布更改,那么你可能不会以你应该更新的节奏那样去更新文档。

如果完全有可能,我们建议你可以选择一个方便轻松更新的发布方式。不幸的是,更新纸质手册根本没用简单的方法。在 1910 年制作的「空气制动和列车信号手册」(见下图)中,作者直接在过时的信息熵粘帖更新。2012 年,丰田不得不召回数千辆汽车,只因为他们在手册中弄错了几句话。

15204921775359.png

发布 PDF 并不会给你任何灵活性。如果你已经在 1000 台笔记本电脑中发布了 1000 本 PDF 手册,则必须手动更新这些手册。没劲。如果安全关键更新漏掉了 5% 的用户会发生什么情况?这只是我们倾向于倡导中央数据存储来取代标准打印 / PDF 工作流的其中一个原因。

整合反馈

15204923776687.png

你需要你的客户——不仅仅是为了保持你的业务存在,而且还要保持你的内容蓬勃发展。你的客户能够让你变得更好。你获得的反馈越多,获得的建议越多,文档(和公司)的效果就约好。

客户可以指出文字混淆的地方;他们可以告诉你哪里的图像并没有什么卵用。招募用户帮助你改进手册。如果你提供在线版本,请为用户提供指定的问答场所,以提供反馈和编辑建议。如果你以印刷形式出版,请向读者提供他们可以联系的人以便提出问题和反馈。

API 意味着一次编写,随处发布

Google 地图安装在美国数以百万计的智能手机上。尽管 Google 不断更新和扩展它们的地图,但用户始终可以获得最新版本的地图。这怎么可能?因为人们想要的信息并未下载到他们的手机上——与此同时,每次 Google 更新地图时都不必重新下载应用程序。这些信息存在于别处,并且不断向所有连接的设备发布更新。这就是一个 API。

API 意思是应用程序编程接口:如果你在线发布,那么它可以帮助你无缝更新文档。传统上,文档更新是通过 WORD 或者 inDesign,亦或者 PDF 推送到网络、机构和归档中。如果你对 Word 中的文本进行更改,则需要对其他格式也进行更改。当每个人都有更新的副本时,又要再更新一次。使用 API 可以让你对一个中心点进行更改。然后所有更改都会立即推送到每个发布平台。

15204925841304.png

oManual 等包含 API 规范和支持移动应用的现代文档格式开始使用这项功能。

写完之后

创造你的指南只是一个开始。一旦完成初稿,就该在现场进行测试,简单修改,部署工作,聆听用户,管理未来的更新。

编辑

15204929949578.png
编辑、编辑、编辑。我们不能过分强调这个步骤的重要性。编辑组织、内容、流程和语法。你的读者得到一个打磨后的产品很重要。毕竟,当设计说明书和手册时,校对可能是福音和灾难的区别。

以下有两句话:「夏日乐趣的终极指南:烧烤小孩和家庭」和「夏日乐趣的终极指南:烧烤,孩子和家庭」。

那就对了。你和吃人之间只差一个逗号。这就是我们为什么一遍又一遍的编辑和校对。然后,当我们认为它是完美的时候,我们也邀请别人来编辑它。

大声的朗读自己的手册。如果你自己能听到,你就会发现自己的错误。

可用性测试

测试你的新说明书。请你的测试人员从头到尾按照说明进行操作。如果他们成功完成了成品而不是制造出一个怪物。那么你已经完成了作为作者的工作。一定要问你的测试人员哪些部分很难理解,为什么呢。

更新

有更新文档的计划。如果你在写一本手册,你必须承诺保持它的可靠性。每个六个月阅读一次文档以保最新。

准备好了

恭喜!我们向你致敬!欢迎来到「技术作家秘密小组」。它是个独特而微小的组织。我们甚至有一个秘密手势,一旦来到秘密会所,我们会教你的。但淡定些,那么高级。由于我们没告诉房地产经理人我们秘密会所的位置,所以目前我们没几个钱。

15204935158780.png

今天,来聊聊 CDN 原理

$
0
0

最近为了解释一个系统的工作原理给一个大概什么都不懂的人,煞费苦心的解释了一堆,才发现我并不能比较浅显易懂的去解释一些原理,自己对于一些原理掌握的也并不是非常精准,所以决定从 CDN 开始慢慢介绍一些东西的原理练手——向阮老师学习!

CDN 是个啥

先从 Google 出来的介绍说起:

CDN的全称是Content Delivery Network,即内容分发网络。 其基本思路是尽可能避开互联网上有可能影响数据传输速度和稳定性的瓶颈和环节,使内容传输的更快、更稳定。

看到这里你可能只看懂了「更快更稳」,不过其实讲原理,归根到底也就是把一些不接地气的概念简单化,人类化。从最简单的说起。

CDN 的优缺点

优点:

  1. 访问加速
  2. 减轻源站负载
  3. 扛攻击

缺点:

  1. CDN 基础建设复杂、投资极大

CDN 访问原理

接下来说到重点了,CDN 是如何实现他们这些优点的,我们访问的时候到底经过了哪些步骤。

15224987547274.png

(这是一张网上找来的图)

首先,我们作为终端用户(END USERS)在地址栏中键入 codesky.me,浏览器发现本地没有关于 codesky.me 的 DNS 缓存,于是向网站的 DNS 服务器发起请求。

然后,网站的 DNS 域名服务器设置了 CName,指向了某个 CDN 服务(比如 cdn.codesky.com),去请求 CDN 中的智能 DNS 均衡负载系统。

均衡负载系统解析域名,把对用户响应最快的节点(CDN Node)返回给用户,然后用户向该节点发出请求。

如果是第一次访问该内容,CDN 服务器会向源站(Origin Server)请求数据并缓存,否则的话,直接在缓存节点中找到该数据,将请求结果发给用户。

对于最简单的 CDN 系统而言,只要一台 DNS 调度服务器和一个节点服务器即可,但在复杂的应用中,会存在多级缓存,多台 Cache 来协同工作。

真实的 CDN

作为 CDN 的使用方,在使用中其实常常会遇到各种坑,正好之前有一阵子也在和 CDN 打交道,顺手介绍一波。

在过去,CDN 还是很贵的东西,比如网宿;但是自从阿里、腾讯纷纷做云计算之后,CDN 就变的便宜了起来。

作为一个符合中国国情的 CDN,不仅考虑到了地理位置导致的访问速度不佳,还得考虑不同网络供应商的问题。

尽管 CDN 本意是可以让服务更加稳定,在某些情况下,甚至源站不可用,缓存的数据也可以让用户访问。

但是实际情况下,某个节点不稳定依旧会导致一些用户访问时出现错误,对于这种问题,尽管「刷新就好」,但依旧会让用户导致不满。

另外,如果不注意缓存时间的设计,可能会让某些节点成为「脏节点」,需要手动清理 CDN 缓存。

参考资料

  1. CDN是什么?
  2. 深度剖析:CDN内容分发网络技术原理

CSS 再谈 pointer-event

$
0
0

之前在 JavaScript 优化拖移效果 中,我曾经简单讲过:

之后,如果需要在移动时取消所有鼠标的响应时间,可以通过调整 pointer-events 样式来修改,在变更拖动状态时修改 body 的 style 即可。

但没有进行过详细的介绍,这次正好结合了其他点仔细讲讲。

pointer-event 其实也有许多值,可以在 MDN 中 pointer-events 详细的描述了这些值,当然,大部分都是 SVG only,我们今天考虑的自然不是 SVG Only 的属性,而是通用的属性。

说白了也就两个:autononeauto 自不必说,其实和 initial 差不多一个意思。而 none 不仅可以实现事件禁用,还可以实现点击穿透。

点击穿透

正式介绍一下,什么叫点击穿透?

15225845381133.jpg

假设你想实现这样一个效果,其中图片是一层(绘制在 canvas 上),拖拽和控制点的矩形框是另一层,那么「矩形框层」必须在 Canvas 层的上方,但是在图片层自带了拖动事件,只要矩形框跟着动就行了,怎么办——所以就需要用到穿透了——pointer-event: none。搞定。

也就是说,使用了点击穿透这个效果,可以把原来覆盖的图层无视,直接把鼠标事件作用于被覆盖的图层。

当然,大家一定要说,那为什么不直接让图片层跟着矩形框动呢——你说的很有道理,但为了面子我必须说:我们这是特定的某个应用场景。

事件禁用

如果你的事件禁用还在强行写一长串 JS,那你就大错特错了。pointer-event: none 可以用一句 CSS 做到这个效果,何乐而不为呢。

当然,对于事件禁用,让人注意的其实是一个题外话,试试下面的 demo(两个矩形框里也隐藏了按钮):

我们会发现,只有 opacity: 0 才有效,也就是说,被我们一直忽略的 visibility 也有这种效果,所以我们可以放心大胆的直接用 visibility 来隐藏一些元素,不用特地写上 pointer-event: none 了。

MIME 与 Nodejs 的小故事

$
0
0

之前同学问了我一个问题,他用 Java 写的客户端并发出的请求在 Java 的服务端可以接收,他想改成 Node.js 的服务端,但是就遇到了没法正确处理的问题。

经过 Header 的查看,首先我们发现了 Header 中没有 content-type,最初我的思考角度在于 content-type 没有的情况下,会不会存在 Java 服务端的默认值而 Node.js 的服务器并没有处理。由于他说传的是二进制文件,想当然的想到了 multipart/form-data,但是对于 multipart 而言,应该是有特殊首部要求的,明显不满足这个条件。

后来查了很久,才发现 MDN 中有这么一句话:

对于 text 文件类型若没有特定的 subtype,就使用 text/plain。类似的,二进制文件没有特定或已知的 subtype,即使用 application/octet-stream

现在我们知道了 header,下一步就是怎么处理了,因为 koa-body 不对这种类型进行处理,而 express 的 body-parser 中已经带了 raw 方法来处理这种情况,可以直接使用,koa 也有人做了一个中间件 raw-body

更重要的是,我们仍然想要了解到底发生了啥,到底要怎么写,于是我们翻到了 Node 官方的帮助 demo,借此自己糊了一个 middleware:

const Koa = require('koa')
const app = new Koa()

const mid = (ctx, next) => {
  const req = ctx.req
  const body = []
  req.on('data', (chunk) => body.push(chunk))
  req.on('end', () => {
    ctx.request.body = body 
    next()
  })
}


app.use(mid)
app.use((ctx, next) => {
  console.log(ctx.request.body)
})

app.listen(3000)

参考链接:

koa-router 8 升级迁移手册

$
0
0
在公司内有小伙伴用了 koa-router 8,但是我发现他的用法并不正确,没有注意到 breaking changes。于是翻译一波这个……

如果想要体验一下最新版本,只要 npm install koa-router@next 即可。

更新日志

7.x 中:

router.use('/nested/path', otherRouter.routes())
router.use('/users', userAuth());

8.x 的变更:

const router = new Router({prefix: '/nested/path'});
router.nest('/nested/path', otherRouter);

const router = new Router({prefix: '/users'});
router.use(userAuth());

7.x 中:

parentRouter.use(childRouter.routes());

8.x 的变更:

parentRouter.nest(childRouter);

// use -> nest
forums.use(posts.routes());

简言之,上面变成了下面这种形式:

forums.nest(posts);

在使用完 .routes() 之后在声明路由将不再适用。你必须在使用 .routes() 之前声明好路由。.routes() 是一个在那个时段的路由的快照。在使用完 .routes 后没有变更将会对上一个 routes() 调度产生影响。

app.use(router.routes());
router.post('/some-endpoint', () => {});

需要变更为:

router.post('/some-endpoint', () => {});
// 必须在 调用 .routes() 前注册路由!
app.use(router.routes());

.use() 不再支持路径前缀。取而代之的,嵌套一个路由。

const router = new Router();
router.use('/admin', authenticateAdmin);
router.get('/secrets', () => {});
app.use(router.routes());

变更为:

const secretsRouter = new Router();
const adminRouter = new Router();
secretsRouter.get('/secrets', () => {});
adminRouter.nest('/admin', secretsRouter);
app.use(adminRouter.routes());

调用 routes() 后对路由进行变更不会影响先前由 routes() 返回的中间件。

当路由嵌套时,仍然可对其进行更改。在路由或者任何祖先上调用 routes() 后,路由的更改将被忽略。


router.use('*', ...)

变更为:

router.use(...);

「按 URL 出现顺序运行参数中间件」变更为根据 param 调用的顺序。


router.get('users-index', '/users', () => { ... });
router.url('users-index', {}, { query: { page: 1 } });
// => /users?page=1

变更为:

router.get('users-index', '/users', () => { ... });
router.url('users-index', { page: 1 });
// => /users?page=1

重定向命名路由:

旧的页面的 GET 的处理程序因为首先被定义,所以它首先被匹配。在定义重定向时,她其实只是另一个符合匹配的处理程序。重定向你的处理程序,而不是重定向这种方法。

const router = create()
    .get('newpage', '/newpage', () => {})
    .get('oldpage', '/oldpage', () => {})
    .redirect('oldpage', 'newpage');

变更为:

const router = create()
    .get('newpage', '/newpage', () => {})
    .get('oldpage', '/oldpage', (ctx) => ctx.redirect(ctx.router.path('oldpage')));

参考资料:

Vue 多级组件透传新方法:provide/inject

$
0
0

provide / inject 是 2.2 新增的方法,可以以一个祖先组件向所有子孙后代注入依赖(一个内容)。

Vue 官方警告:

provideinject 主要为高阶插件/组件库提供用例。并不推荐直接用于应用程序代码中。

当然,警告只是警告,你完全可以正常使用。

使用方法非常像 dataprops 的组合大礼包:

var Provider = {
  provide: {
    foo: 'bar'
  },
  // ...
}
var Child = {
  inject: ['foo'],
  created () {
    console.log(this.foo) // => "bar"
  }
  // ...
}

唯一的区别是你不用再一层层的传入了,过去用的 event-bus 虽然可以解决深层问题,但是会导致整个 event-emit 组成过于混乱,难以维护。使用 provide / inject 可以保证父子单向数据流的清晰性。

在 React 中 Context 的 Provider / Consumer 也有相同的效果,由于还没有具体使用过,对 React 本身也只有一面之缘,留待以后在了解,感兴趣的同学可以阅读文档了解。

参考文章:

CSS word-break:是谁动了我的 break-all

$
0
0

今天,我的一个同学想要简单的实现一个文本根据 container 自动换行的效果,嗯,可以说是非常 basic 了,然而……依旧遇到了一点坑:

如下:

JS Bin on jsbin.com

为什么明明是 break-all 了,我的标点符号还是不能被自动断行。

MDN 说了和没说一样,只覆盖了字符集,而没有覆盖到标点符号和单字的问题。

然后我们查阅了 stackoverflow,大概意思是这样的,这是一个 Feature,而并非一个 Bug。

在 W3C 的草案中有一个 issue 中的一个表格很清晰的指明了道路:

ValueWhenBreak AtPreferred Width
word-break: break-allAnytimeExcept certain punctutationSame as line break

更多懒得打了,请查阅:https://github.com/w3c/csswg-drafts/issues/1171#issuecomment-293149991

只要单纯的 word-wrap,反而能够实现这个效果:

JS Bin on jsbin.com

当然,实际上,效果是有差异的,因为它不能严格的按照字母换行,还是按照 word 来区分。

之后我们还讨论了一下关于 word-break 的适用场景,实际上,是否需要 word-break 还是需要根据场景来决定,根据我的脑补,做出了以下总结:

  • 当 Container 大小不定而文本内容固定的时候,可以考虑用 word-break: break-all,因为这种时候文本内容是可控的,通常来说不会出现过多符号在一起的情况下。
  • 当文本内容不在开发者的可控范围内的情况,请勿使用 break-all,鬼知道你的用户到底想要以什么姿势使用。
Viewing all 195 articles
Browse latest View live




Latest Images