阅读 2110

「每日一瞥 📰 」1203 ~ 1221

  • Node.js 内存更高效的大文件处理方法
  • React 元素的 $$typeof 属性是干嘛的?
  • React 是怎么区分 class 和 function 的?
  • Redux v6.0.0 released
  • 学习一下 React Hooks
  • setState 并没有做什么
  • 为不断演化而不断优化
  • Babel 7.2.0 发布
  • Redux 与 Context 取舍
  • Hooks vs Lifecycle
  • 纯 News 向:state of js in 2018
  • React.memo() 和 useMemo Hooks
  • console.log 以外的骚操作

Node.js 内存更高效的大文件处理方法

在 Node.js 中,我们可以使用 fs.readFile、fs.writeFile 来实现一个文件的复制,但毫无疑问的是,当我们想要处理几个G甚至几十G的文件时,这种方法会导致内存问题。

这时,我们可能会考虑利用 stream 和 buffer,将大文件劈成许多小的 chunks 来进行传输:

  1. 监听 Readable Stream 读入的 chunks;
  2. 利用 Writable stream 将监听到的 chunks 写到磁盘;
  3. 跟踪整个监听过程(说白了就是 readable.on('data', ...)

一般来说上述方案已经是较为合理的做法了,但是问题仍然存在,那就是 node 会将内存占用飚的极高,我们可怜的小 Mac 难免会嗡嗡作响。

问题的根源是什么呢?读写速度不一致。因为读的速度很快,但写操作却来不及全部处理,这时就会将过量的数据先存入 RAM,写操作本身变成了瓶颈,且存入 RAM 也会间接影响性能。

事实上这篇文章就是在推荐用 Node 的管道写法来处理,现在的 Node 已经支持这种写法。管道的方法遵循 Back pressure 机制,一般可能翻译成「背压」,其实这是在说一种现象:在数据流从上游生产者向下游消费者传输的过程中,上游生产速度大于下游消费速度,导致下游的 Buffer 溢出。 pipe 方法可以有效控制磁盘的读写速度,避免这一现象的发生。

使用的时候,写法上很容易调整到 pipe 的写法,将 writeable.write(chunk) 换成 readabale.pipe(writeable) 即可。这种方式可以只以很低的内存占用来完成大文件操作,同时因为没有过度占用内存,还可以得到更快的速度。

关于 pipe 的原理可以参考下面这个图。

具体对这一机制的描述可见 Node 文档

源地址:medium.com/dev-bits/wr…

React 元素的 $$typeof 属性是干嘛的?

用 React 久了,就一定知道 typepropskeyref等属性,但有一个属性我们未必很了解,就是 $$typeof,它持有的是一个 Symbol 类型的值,如下所示:

{
  type: 'marquee',
  props: {
    bgcolor: '#ffa7c4',
    children: 'hi',
  },
  key: null,
  ref: null,
  $$typeof: Symbol.for('react.element'), // 🧐 Who dis
}
复制代码

我们会用 React 的 JSX 写下很多 View,在模板中插入许多量。如果有的量是外部录入的,那我们就要提防 XSS 了。当然,React 会默认进行 escape 来预防这类事情,如果我们不得不强制展示 HTML,我们可以利用 dangerouslySetInnerHTML 来处理。

但即使如此,安全性还是不够的,这就有了 $$typeof 发挥的地方。

考虑这样一个场景:如果服务端开了个口子允许用户存储 JSON 对象,而用户需要一个字符串,这就可能存在问题。事实上我就曾经遇到过文中所说的场景-,-,例子如下:

// Server could have a hole that lets user store JSON
let expectedTextButGotJSON = {
  type: 'div',
  props: {
    dangerouslySetInnerHTML: {
      __html: '/* put your exploit here */'
    },
  },
  // ...
};
let message = { text: expectedTextButGotJSON };

// Dangerous in React 0.13
<p>
  {message.text}
</p>
复制代码

当然,React 从 0.14 版本就开始对每个 React 元素标记一个 Symbol 符号。这个方法是有效,原因是你不可能在 JSON 中放 Symbol,这样即使服务端返回了一个 JSON,React也不会去解析包含恶意脚本的 JSON。

当然中的当然,这也不是万灵药,但确实对上述场景有用。一个题外话是,对于不支持 Symbol 的环境,React 会将 $$typeof 设置为 0xeac7,原因就是这个串长得像 「React」。

源地址: overreacted.io/why-do-reac…

React 是怎么区分 class 和 function 的?

当我们在写一个组件的时候,可以通过函数或类的方式来定义,比如下面这种:

function Greeting() {
  return <p>Hello</p>;
}
// ... or
class Greeting extends React.Component {
  render() {
    return <p>Hello</p>;
  }
}
复制代码

当我们使用的时候,从来不会关注究竟是怎么定义的,而是简简单单的写个标签就 ok。

// Class or function — whatever.
<Greeting />
复制代码

但其实仔细想一想,这两种定义方式自然从使用上是不同的。根据 JavaScript 基础知识,如果是函数的定义方法,那么我们自然是要调用函数;如果是类的定义方法,那么我们自然是要实例化一个对象。

// 1. 当使用函数定义时 Your code
function Greeting() {
  return <p>Hello</p>;
}

// Inside React
const result = Greeting(props); // <p>Hello</p>

// 2. 当使用类定义时 Your code
class Greeting extends React.Component {
  render() {
    return <p>Hello</p>;
  }
}

// Inside React
const instance = new Greeting(props); // Greeting {}
const result = instance.render(); // <p>Hello</p>
复制代码

因此可以看出,React 本身是要知道定义的方式然后再决定如何得到渲染的结果的。那么是怎么知道的呢?

这篇文章探讨的很详细,但在这里,我们可以做个很简单的总结:

  • 在 React.Component 类的原型上挂上一个 flag 量,这个 flag 就是个空的对象;
  • 然后我们在用类定义的方式写 React 组件时,我们都会做的一件事就是 extends 继承一下,而「继承」会使得类定义的 React 组件在原型上共享到 flag;
  • 这样一来,所有通过类定义的方式都会继承 React.Component,也就得到了能够标记其定义方式的 flag 量。
// Inside React
class Component {}
Component.prototype.isReactComponent = {};

// We can check it like this
class Greeting extends Component {}
console.log(Greeting.prototype.isReactComponent); // ✅ Yes
复制代码

源地址: overreacted.io/how-does-re…

Redux v6.0.0 released

Redux 正式发布了 6.0 版本,支持 React 16.4 中的 Context API。

相应的,这次更新有一些大变化:

  • connect 所使用的参数 withRef 变更为 forwardRef。如果 forwardRef 置为 true,当我们给被 connect 包裹的组件加一个 ref 时,会直接拿到被包裹的组件。
  • 我们不会再需要将 store 传给被 connect 的组件了,以后传的是自定义的 context。但在开发时通常会使用 react-redux,不直接使用 redux 的 store 来操作,所以没啥特别感觉。

而这带来了一些影响:

  • 最明显的就是用到 store 的那些包要改写了。目前 redux 将 store 放在 context 里,对于依赖它的包仍可以访问到,只是不会再提供开放的 API 了。当然不久的将来估计就没了。
  • 还有一个是在 constructor 或 componentWillMount 里 dispatch 产生的行为可能有变化。此前的行为是:父子组件会各自独立的去读取 store,而子组件在挂载时会立刻使用更新过的状态。现在的话因为共用状态,所以就不会出现这种情形(我的理解就是子组件会用父组件还没用上的新状态)。但是目前有的应用可能会依赖前者的特性,所以会存在一些问题。

学习一下 React Hooks

今天就看看 React Hooks。官方文档应该算是一手的学习资源,就撸它了。下面的内容尽量保证已消化的清晰易懂。

首先 React Hooks 到底是什么呢,文档有一句并没有着重强调的话可以拿来总结,但是略有些不好翻译,我的理解是:Hooks 可以让你在函数式组件里做到一些与状态、生命周期相关的事。什么意思呢,就是说 Hooks 并不应用在 class 定义的组件里,且我们不需要通过 this.statethis.setState 和那些个 cmpDMcmpDU 生命周期来做事情了。下面的例子可以说是最基本但没啥用的例子:

import { useState } from 'react';

function Example() {
  // Declare a new state variable, which we'll call "count"
  const [count, setCount] = useState(0);

  return (
    <div>
      <p>You clicked {count} times</p>
      <button onClick={() => setCount(count + 1)}>
        Click me
      </button>
    </div>
  );
}
复制代码

useState 就是一个 Hook。useState 以参数作为状态初始值,以数组解构的形式返回需要的东西,包括当前的状态值和一个更新该状态的函数。也就是说,我们可以通过 setCount 来更新 count,不再需要 this.state 来初始化,不再需要 this.setState 来更新状态。

上面这几句话对应老长的一篇文档-,-。

那么在继续之前,我们要搞清楚为什么要搞这么个东西,它是要解决什么问题?

其一,代码复用层面,我们可能会通过封装 HOC 等方式来复用逻辑,而这可能会带来大量的 wrapper,导致臃肿。 其二,代码组织层面,一个深有体会的点是我们在使用 React 的时候简直就是面向生命周期编程。有时候你会发现接手的代码里,相关的逻辑分散在各个生命周期里;有时候你又会发现,不相关的逻辑写在一坨里。Hooks 让你把一个组件拆分成若干更小的、内部逻辑相关的函数。相比较而言会更清晰。 其三,学习成本层面,这个就不说了,应该不存在的。

但 Hooks 厉害的地方还不止于上面的例子,useEffect 就真的让人爽到飞起了。

useEffect 可以看做 componentDidMount、componentDidUpdate、和 componentWillUnmount 的大集合,它是一个完整的处理副作用的机制。以下面例子为例,我们用 class 写的代码可能是这样的:

class Example extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      count: 0
    };
  }

  componentDidMount() {
    document.title = `You clicked ${this.state.count} times`;
  }

  componentDidUpdate() {
    document.title = `You clicked ${this.state.count} times`;
  }

  render() {
    return (
      <div>
        <p>You clicked {this.state.count} times</p>
        <button onClick={() => this.setState({ count: this.state.count + 1 })}>
          Click me
        </button>
      </div>
    );
  }
}
复制代码

但我们用 useEffect 就是这样的:

import { useState, useEffect } from 'react';

function Example() {
  const [count, setCount] = useState(0);

  useEffect(() => {
    document.title = `You clicked ${count} times`;
  });

  return (
    <div>
      <p>You clicked {count} times</p>
      <button onClick={() => setCount(count + 1)}>
        Click me
      </button>
    </div>
  );
}
复制代码

我们不用去在意什么 document,把他当做某些容易理解的副作用即可,比如 fetch?我们只需要知道,很多时候我们会在几个生命周期方法里写上一样或有很多重合的逻辑,这使得我们不得不管杀又管埋(从而增加出错的几率),还搞得代码量无形变多。后者的写法明显比前者会少,你不再需要区分挂载后如何、更新后又如何。

useEffect 做了什么呢?我们声明了一个状态 count,然后在副作用里用到了它,因此可以在副作用里读取到 count 最新的状态值。当我们的组件被渲染时,React 会替我们在 DOM 更新后触发副作用,每一次 render 都会,包括首次渲染。我们可能会觉得这样是不是太激烈了,但这是故意设计的。每次重新渲染,触发的副作用也是一个新的副作用,从某种程度上讲,副作用本身就成了渲染结果的一部分,似乎也就不那么「副」了。

具体可以再看看文档,以后有更深入的体会再更。

源地址:react文档。

setState 并没有做什么

本文提出了一个问题:**当我们用 setState 更新状态并进而更新 DOM 时,究竟是 React 还是 React DOM 在做这一切呢?**看上去,更新 DOM 像是 React DOM 在做的事,但我们调用的是 this.setState,React.Component 这个类也肯定是定义在 React 中的。因此上述问题就成了一个无法简单回答的问题。

事实上,如果真的是 React 在负责更新 DOM,那么难道 this.setState 在浏览器、服务端、移动端上都可以跑了吗?比方说 React Native 中我们也是一样使用 this.setState 更新状态,但是 Android 和 iOS 上可没有 DOM,有的只是 View。

结论其实是:React.Component 只是将状态更新的任务委托给了平台特定的代码。换句话说,我们的 this.setState 本身可能并没有做什么了不起的事情。

React 从 0.14 版本就做了拆分,React 包本身只负责暴露定义组件的接口,而具体实现就交给各个渲染器(如 react-dom、 react-dom/server、 react-native、 react-test-renderer、 react-art 等)。react 包本身只负责提供 React 特性,但并不关心具体是如何实现这些特性的;渲染器包如 react-dom、 react-native 等,提供的就是 React 特性的具体实现以及平台特定的逻辑。这些渲染器包有很多一样的代码 (如 React 过去的调和算法),但这些代码也只是各个渲染器实现中的某些细节。

举例来说,当 React 16.3 引入了 Context API 时,React 包暴露了 React.createContext() 方法,但这个方法本身并不真正实现 context 特性。对于 React DOM 和 React DOM Server,它们都要做不同的实现代码。createContext() 只返回一些对象:

// A bit simplified
function createContext(defaultValue) {
  let context = {
    _currentValue: defaultValue,
    Provider: null,
    Consumer: null
  };
  context.Provider = {
    $$typeof: Symbol.for('react.provider'),
    _context: context
  };
  context.Consumer = {
    $$typeof: Symbol.for('react.context'),
    _context: context,
  };
  return context;
}
复制代码

这样当我们使用 <MyContext.Provider> 或 <MyContext.Consumer> 时,React DOM 和 React DOM Server 会以不同的方式来处理 context。

那么话又说回来了,setState 到底做了什么?

答案就是,每个渲染器都会为创建的类创建特定的域,而这个域就是一个更新器,而这个东西,是 React DOM、 React DOM Server 或者 React Native 在创建实例之后纷纷要去提供具体实现的:

// Inside React DOM
const inst = new YourComponent();
inst.props = props;
inst.updater = ReactDOMUpdater;

// Inside React DOM Server
const inst = new YourComponent();
inst.props = props;
inst.updater = ReactDOMServerUpdater;

// Inside React Native
const inst = new YourComponent();
inst.props = props;
inst.updater = ReactNativeUpdater;
复制代码

而 React。Component 的 setState 的实现,就是委托给渲染器去创建组件实例

// ReactBaseClasses.js
Component.prototype.setState = function(partialState, callback) {
  invariant(
    typeof partialState === 'object' ||
      typeof partialState === 'function' ||
      partialState == null,
    'setState(...): takes an object of state variables to update or a ' +
      'function which returns an object of state variables.',
  );
  this.updater.enqueueSetState(this, partialState, callback, 'setState');
};
复制代码

这就是为什么 this.setState() 即使它被定义在 React 包里仍可以更新 DOM 的原因.

源地址:overreacted.io/how-does-se…

Redux 作者更新关键词

Mark Erikson 在 twitter 上问道,如果他用 Predictable、 Centralized、 Debuggable、 Flexible 来形容 Redux,那么大家会希望用什么词语来形容 React Redux,他会以这些关键词作为「卖点」放到首页上。

于是有个评论说了如下的话:

总之,Mark Erikson 在吸取了评论区的诸多意见后,最终在首页发出了最新的 4 个关键词:

  • Official:由 Redux 团队专门维护,持续跟进 React 和 Redux 最新的 API;

  • Predictable:贴合 React 组件模型,使用者只需要从 Redux 取出数据交给组件,而组件将会以 props 的形式接收;

  • Encapsulated:封装了与 store 交互的逻辑;

  • Optimized:做了复杂的性能优化,让使用者的组件只有在数据真的变化时再重新渲染。

源地址:

Babel 7.2.0 发布

Private Instance Methods

Babel 7.1.0 的版本开始支持 private static fields,而近日发布的 7.2.0 更进一步的支持了 private instance method。

有了这个 feat,我们就可以写下面这种代码:

class Person {
  #age = 19;

  #increaseAge() {
    this.#age++;
  }

  birthday() {
    this.#increaseAge();
    alert("Happy Birthday!");
  }
}
复制代码

issue#8654 中有人针对如下代码提了个问题:console.log 打印出的是 true 还是 false?

class A {
  #method() {}
  
  getMethod() {
    return this.#method;
  }
}

// true
console.log(new A().getMethod() === new A().getMethod());
复制代码

littledan 回复说两个 new A() 创建的对象持有的私有方法是同一个。

所以说,「面向对象的 JS」在 Babel 的帮助下出现了,你呢?

管道操作符

这个主要是说该版本支持了智能管道提案的核心部分,并支持了 # 作为占位符。比如下面的代码:

// "Smart"
const result = 2 |> double |> 3 + # |> toStringBase(2, #); // "111"

// "Simple"
const result = 2 |> double |> (x => 3 + x) |> (x => toStringBase(2, x));
复制代码

Smart 版本就是能支持占位符,比下面的 Simple 版本更简单一点。我之前只是听过这个提案,今天又看了下管道操作符是怎么个意思呢?

管道操作符的语法是这样的:

expression |> function
复制代码

我们拿 MDN 的例子来展示就很明了了:

const double = (n) => n * 2;
const increment = (n) => n + 1;

// 没有用管道操作符
double(increment(double(5))); // 22

// 用上管道操作符之后
5 |> double |> increment |> double; // 22
复制代码

总的来说,这玩意可以以一种更容易读的方式对函数进行链式调用,很适合单参数函数的场景。

Plugin Names

这个主要是说官方插件为各个版本的 Babel 提供了自己的名字,从而能做一些类似 Time Travel 这样的事。我们可以看看改版后的 Babeljs.io

AzKc37i.gif

源地址:babeljs.io/blog/2018/1…

源地址: github.com/reduxjs/rea…

为不断演化而不断优化

Dan Abramov 在他的个人博客上分享了他对优秀甚至卓越的 API 设计的看法。

我们通常会认为「可读性」、「正确性」、「高性能」是「首要标准」,无论有怎样的妥协、平衡,这一首要标准都会占据在一个好的程序员的意识中。但是总的来说,再优雅的代码也会被繁复变化的需求所打倒,因此 Dan 认为,在可读性的基础上,更应该追求「第二标准」:对用户使用这些 API 的诉求会发生演化这一事实有意识

总的来说,好的 API 设计会将变化考虑其中。

源地址:overreacted.io/optimized-f…

自定义 Hooks 优势盘点

Dan 最近更的很频繁,今天更的文章主要是介绍 React Hooks 一些为什么如此设计的点,但在此之前他建议阅读下文档中自定义 Hooks 的部分。

文中强调 Hooks 一个强大之处在于,Hooks 之间是可以相互传值的。从文档和这篇文章来看,使用自定义 Hooks,我们可以很好地抽象业务逻辑并复用,其效果甚至比 render props 和高阶组件还好。相比较过去在生命周期中写出来的分散的、重复的内容,自定义 Hooks 可以做得更好,且让使用者更少的考虑除业务逻辑以外的东西。

下面一个例子就是看从列表中选中的好友是否在线这么个功能,我们可以注意到 useFriendStatus 就是一个自定义 Hooks,它抽出的逻辑明显具有复用性:

const friendList = [
  { id: 1, name: 'Phoebe' },
  { id: 2, name: 'Rachel' },
  { id: 3, name: 'Ross' },
];

function ChatRecipientPicker() {
  const [recipientID, setRecipientID] = useState(1);
  const isRecipientOnline = useFriendStatus(recipientID);

  return (
    <>
      <Circle color={isRecipientOnline ? 'green' : 'red'} />
      <select
        value={recipientID}
        onChange={e => setRecipientID(Number(e.target.value))}
      >
        {friendList.map(friend => (
          <option key={friend.id} value={friend.id}>
            {friend.name}
          </option>
        ))}
      </select>
    </>
  );
}

function useFriendStatus(friendID) {
  const [isOnline, setIsOnline] = useState(null);
  const handleStatusChange = (status) => setIsOnline(status.isOnline);
  useEffect(() => {
    ChatAPI.subscribeToFriendStatus(friendID, handleStatusChange);
    return () => {
      ChatAPI.unsubscribeFromFriendStatus(friendID, handleStatusChange);
    };
  });
  return isOnline;
}
复制代码

这整个逻辑能够跑通的原因就在于,useFriendStatus 作为一个被抽出来的自定义 Hooks ,可以在 ChatRecipientPicker 内使用到最新的状态 recipientID。这就是指 Hooks 之间可以传值。

const [recipientID, setRecipientID] = useState(1);
const isRecipientOnline = useFriendStatus(recipientID);
复制代码

总结起来有这么几点:

  • 因为能在函数体中调用 Hooks,我们可以轻松的在他们之间传值,而不用再写多层组件来转换或者用一些方法来记忆中间计算;

  • 我们能够在副作用里轻松的引用到状态,不必担心在闭包中拿不到值;

  • 我们也不再需要有意识的匹配输入、输出,现在是一个整体的风格;

  • 我们也不再需要通过高阶组件、反向继承这些技术来抽象业务逻辑。

Redux 与 Context 取舍

文章开篇有些损,附首段原文如下,我主要针对后面的内容进行总结。

The complaint that is often voiced by inexperienced React developers, is that Redux actually complicates their work instead of simplifying it. This is understandable – this tool was invented for managing data flow in more complex applications and seems to be an overkill for simple SPAs or websites.

一、学习 Redux 存在的问题

  1. 得在没有用 Redux 的大项目中工作,才能看出 Redux 是要解决哪些问题(有过痛苦才有体感),有哪些替代方案以及为什么它们更有效和方便。
  2. Redux 教程总是用一些原生 JS 也很好写的 Demo 来示范,给人一种是不是搞复杂了的感觉。

二、Redux 旨在解决的问题

  1. 分离「发生的」和「要做的」。这里提到一个点是,多个 reducer 重复相同的模式是一种比较差的实践,应该使用更简单的 reducer 替换并重用(添加参数)。
  2. 传递 props 时要穿过一堆用不到的 component 单单是这个场景的话,其实 Context 其实是够的:
import React, { Component } from 'react';

const MyContext = React.createContext();


class AppProvider extends Component {
  state = { data: {}}; // state should be populated with data

  render() {
    return (
      <MyContext.Provider value={this.state}>
      {this.props.children}
    </MyContext.Provider>
    );
  }
}

class MyComponent extends Component {
  render() {
    return (
      <AppProvider>
        <div className="my-component">
          <MyContext.Consumer>
            {(context) => context.data}
          </MyContext.Consumer>
          <ComponentWrapper />
        </div>
      </AppProvider>
    );
  }
}

class ComponentWrapper extends Component {
  render() {
    return (
      <div className="wrapper">
        <Subcomponent />
      </div>
    );
  }
}

class Subcomponent extends Component {
  render() {
    return (
      <div className="subcomponent">
        <MyContext.Consumer>
          {(context) => context.data}
        </MyContext.Consumer>
      </div>
    );
  }
}
复制代码
  1. 传递「handle」时穿过一堆用不到的 component 与传递一般值类型的 props 不同的是,回调对数据的修改就是「自下而上」的感觉,而这和 React 的设计感觉有些相反。

三、何时使用 Redux

  1. 需要各种使用中间件,比如打 log,打错误日志,根据服务端的响应发其他请求等。
  2. 多方数据共同影响单个组件/视图
  3. 简化调试有着强烈的需求。
  4. 分离 data 和 view,将数据请求和具体渲染分离。
  5. 观察者模式。只需将组件连接到 Redux store,而不是在整个应用程序中创建多个发布者和订阅者。

四、何时 Redux 会变得太重而 Context 已然足够

  1. 想从每个组件访问管理的状态,这不足以成为使用 Redux 的理由,Redux 的东西还是更多一些。
  2. 不需要中间件,只是用 Reducers 执行简单的可预测操作,接收数据的顺序与应用程序无关,也不需要 Redux。
  3. 需要「全局数据」这样的东西。

只是上面三个理由,还是 Context 吧。

源地址:ideamotive.co/blog/redux-…

Hooks vs Lifecycle

今天决定拿 Hooks 相关文章继续做些学习。下面拿两个 case 来进行分析和总结。

一个 useState 的 case

如下是一个简单的计数器组件,上面的实现是 Hooks,下面走的 class 方式。

undefined

注意到有四点差异:

  • 在顶部用数组解构的方式定义状态,以初始状态定义一次,并随时可以用 setStateVar 来修改。
  • Hooks 用 setStateVar 来定义单个值,class 内用 setState 及对象来 merge 到新的状态。
  • 在函数式组件中访问状态,你只需要通过这个状态的「名字」,而在类定义组件中则需要 this.state.stateVar,相对是要繁琐了一些。
  • 需要 import { useState } from 'react' 来使用useState。

如果我们需要使用多个状态,那么就多次使用 useState:

undefined

一个 useEffect 的 case

另一个重要的 Hook 是 useEffect,它试图取代生命周期,充当「一体化」生命周期方法。当组件挂载、更新或卸载时,就会触发 effects。如下是 class 写法的代码:

undefined

用生命周期来管理一些逻辑的缺点如今已经无需多说,我们来看看 Hooks 的方式,一并分析:

undefined

useEffect 就是 componentDidMount、componentDidUpdate 和 componentWillUnmount 的集合体。此外,我们可以多次调用 hooks,拆分特定需要考虑的逻辑,但又能够放在一起。

我们来总结下 useEffect 是怎么工作的:

useEffect 本身是一个函数,每次在组件渲染后的更新它都会被执行。我们提供给 useEffect 的参数也是函数,这个函数就是每次渲染发生时真正会被调用的方法。useEffect 的返回是可选的,而这个返回会在组建卸载之前被调用。因此,我们会在返回中做一些取消订阅、删除定时器等等清理工作。

一些总结

  1. 如果使用 Hooks 来编写组件,就可以使用 Hooks 来访问状态和生命周期。
  2. 状态变量要单一赋值,而不要赋值成整个对象,遵循 const [stateVar, setStateVar] = setState(stateVarInitialValue) 的语法。
  3. useEffect 是用来存放那些在组件渲染后、在组件卸载前所做的事,如下所示:
    undefined
  4. 和 useState 一样,useEffect 也会在函数式组件中执行多次,且 effects 们会被串联起来。

源地址:levelup.gitconnected.com/react-hooks…

纯 News 向:state of js in 2018

今天看看 stateofjs 上发的今年 JS 的情况调查(里面的可视化效果挺不错的)。下面挑一些有意思的讲讲:

各国前端的平均收入

貌似还是北美牛逼啊,没想到澳洲也挺好的。

undefined

开发者的时间都用来干嘛了

这一条真的与我国国情严重不符,看来还是调查数据涵盖了更多非中国的用户。

undefined

性别分布

当年决定写前端的时候,我是真以为前端至少比 Java 妹子多。

undefined

语言、方言分布

TS 是有点牛的,都不统计 ES5 了。

undefined

下面的图可看出,ES6 和 TS 高度受欢迎、高度使用、高度满意和令人安心。

undefined

前端框架

从「使用并会再次使用」的占比来看,Vue 虽赶不上 React,但也是茁壮成长了,而且可以看出,两者都属于那种留得住人的。与之相对比的是 Angular,还蛮多人不乐意再用的。

undefined

为防止被说拿百分比制造「统计假象」,这里附上统计数的版本:

undefined

我们拿前面的四分图来看,可以看出,满意度最高的是 React 和 Vue,而且不分上下,而 Angular 属于用的多但风评不佳的那种。感觉上 React 肯定会是制霸前端,但是也不会压倒 Vue 了,这个世界就是这样…

undefined

前端框架 vs 工资

先看统计数版本,可以看出 Preact、Polymer、Ember好像工资挺高的,不过统计人数比较少,还是要看三板斧。

undefined

从百分比的版本可以看出,还是 React 的天下,使用 Vue 的同学统计上会低一点。

undefined

数据层

Redux 很火,GraphQL 很火。

undefined

服务端框架

从使用度来看,Express 是毋庸置疑的霸主地位,Koa 没想到真不太行,而且看得出听说过它的人也没有那么多,第二居然是 Next.js。

undefined

四分图可以更明显的看出 Express 的优势,Koa 难道在国外不火的吗?

undefined

移动端 & 桌面

主要也就是 Electron 和 RN,记得两年前 Electron 我都没听说过,那时候搜到的还是 NW.js。

undefined

其他语言

Python 是真的好使,PHP 果然是最好的语言。

undefined

浏览器 API

PWA 在国外是很火。WebGL 和 WebVR 应该也有潜力。

undefined

构建工具

Webpack 一家独大,不知道 Parcel 能不能发育起来。

undefined

其他

不附图了。越来越多的人认为 JS 在往着正确的方向发展,越来越多的人认为构建一个 JS app 变得简单,并越来越享受开发 JS apps,越来越少的人认为 JS 生态变化的快了

undefined

最后

好啦,差不多看完了。

源地址:2018.stateofjs.com/demographic…

React.memo() 和 useMemo Hooks

晚饭的时候同事提到 memo,回来又看了一下发现 Hooks 其实还挺多的。基本的三板斧就是 useState、useEffect 和 useContext,当然前两个基本也就够了。

useMemo

useMemo 是一个用于优化性能的 Hook,想象一个场景,你想根据传入的 props 来做些计算,比如讲一个对象数组 map 成另一种结构,或者将一个数组的数据整合成一个值,或者做排序或过滤等等,都是 useMemo 可以施展拳脚的地方。

用法如下,还是很好理解的,参数一是计算复杂的方法,参数二是输入,只有它们之中有变化时才会重新触发参数一,否则会返回记忆的结果。如果参数二没给,那就每次都算。。。

const memoizedValue = useMemo(() => computeExpensiveValue(a, b), [a, b]);
复制代码

我们看段代码:

const ExpensiveComputation = ({
  data,
  sortComparator,
  filterPredicate
}) => {
  const transformedData = useMemo(
    () => {
      return data
        .filter(filterPredicate)
        .sort(sortComparator);
    },
    [data, sortComparator, filterPredicate]
  );
  return <Table data={transformedData} />;
};
复制代码

useMemo 会检查传入的参数列表,确认输入是否发生变化,以此避免不必要的「计算量大」的 rerender。

React.memo

React.memo 并不是一个 hook,但它也是个用于性能优化的新 API。它有些类似 shouldComponentUpdate 和 PureComponent,但是在函数式组件里是没有这些东西的。另外一点是它只对 props 进行比较,这就与 shouldComponentUpdate 不一样了。

React.memo 其实是一个 HOC,用于优化性能的 HOC。

import React from 'react';

const MyDemoComponent = React.memo(function MyComponent(props) {
  // only renders if props have changed!
});

// can also be an es6 arrow function
const OtherDemo = React.memo(props => {
  return <div>my memoized component</div>;
});

// and even shorter with implicit return
const ImplicitDemo = React.memo(props => (
  <div>implicit memoized component</div>
));
复制代码

源地址:

medium.com/@vcarl/ever…

scotch.io/tutorials/r…

console.log 以外的骚操作

console.log()

console.log 是支持像 C 里的 sprintf 那种 format 方式的。

// Syntax
console.log(msg, values)
// Demo
const a = { x: { xx: '123', yy: '4545' }, y: { zz: '6543'} };
console.log("test hhh %o", a)
复制代码

undefined

占位符可以有 %s、%o(对象)、%d 等等。

还有一种比较骚的是 %c,它事实上是 css 的占位符:

undefined

如果是用来做点 log,其实还蛮炫的,不过用法在细节上有些 weird。

console.dir()

一般情况下,它的结果和 console.log 是一样的。但是当传入的是一个 DOM 时,就有如下区别:

undefined

undefined

可以看出,当你想看一个 DOM 元素细节的时候,console.dir 打印出来的可能更符合我们的期待。

console.warn()

它和 console.log 唯一的区别就是会变黄,但也不止于此,你可以在下图所示的地方做 filter,这样 log 就会变得更清晰:

undefined

console.table()

这个东西是极其骚的,但是以前没关注过。如果我们有下面这种数组数据:

const data = [{
  id: "7cb1-e041b126-f3b8",
  seller: "WAL0412",
  buyer: "WAL3023",
  price: 203450,
  time: 1539688433
},
{
  id: "1d4c-31f8f14b-1571",
  seller: "WAL0452",
  buyer: "WAL3023",
  price: 348299,
  time: 1539688433
},
{
  id: "b12c-b3adf58f-809f",
  seller: "WAL0012",
  buyer: "WAL2025",
  price: 59240,
  time: 1539688433
}];
复制代码

console.table 和 console.log 的对比如下:

undefined

还可以通过第二个参数指定打印出的列:

console.table(data, ["id", "price"])
复制代码

效果如下:

undefined

需要指出的是,console.table 方法最多打印 1000 行。

提前结束

这里只给出了文中提到的四种,其他可在最后原文链接里看到。

补充

今天听同事做技术分享的时候才发现,JSON.stringify 还有他写的 Demo 中的那种用法,具体可见 MDN。

// Syntax
JSON.stringify(value[, replacer[, space]])

// Demo
const a = { x: { xx: '123', yy: '4545' }, y: { zz: '6543'} };
console.log(JSON.stringify(a, '', 2))
复制代码

undefined

源地址:medium.com/@mattburges…

「每日一瞥」是团队内部日常业界动态提炼,发布时效可能略有延后。

文章可随意转载,但请保留此 原文链接

非常欢迎有激情的你加入 ES2049 Studio,简历请发送至 caijun.hcj(at)alibaba-inc.com

关注下面的标签,发现更多相似文章
评论