Live Note

Remain optimistic

useMemo

useMemo 是拿来保持一个对象引用不变的。useMemo 和 useCallback 都是 React 提供来做性能优化的。比起 classes, Hooks 给了开发者更高的灵活度和自由,但是对开发者要求也更高了,因为 Hooks 使用不恰当很容易导致性能问题。

假设有个 component,在 dataConfig 变化的时候重新去 fetchData:

1
2
3
4
5
6
<Child
fetchData={() => {
// fetch data
}}
dataConfig={{ id: getId(queryId) }}
/>

如果是个 Class Component,会这么写:

1
2
3
4
5
6
7
class Child extends React.Component<Props> {
componentWillReceiveProps(nextProps: Props) {
if (nextProps.dataConfig !== this.props.dataConfig) {
nextProps.fetchData(nextProps.dataConfig)
}
}
}

使用 Hooks 后长这样:

1
2
3
4
5
const Child = ({ fetchData, dataConfig }: Props) => {
useEffect(() => {
fetchData(dataConfig)
}, [fetchData, dataConfig])
}

使用 Class Component 时我们需要手动管理依赖,但是使用 Hooks 时会带来副作用:React 使用的是Object.is(),如果fetchData的 reference 变了,也会触发 useEffect
虽然逻辑上 React 的处理是合理的,但是还是需要手动去解决它导致的性能问题:官方提供了 useCallback 这个 hooks,用于解决函数引用问题。

Read more »

项目文件目录

1
2
3
4
5
6
7
8
9
10
11
12
-Demo
|--build
|--dist
|--css
|--js
|--view
|--node_modules
|--src
|--
|--package.json
|--webpack.config.js
|--webpack.production.config.js
  • src:代码开发目录
  • build:开发环境 webpack 输出目录
  • dist:生产环境 webpack 输出目录
  • package.json:项目配置
  • webpack.config.js:开发环境配置
  • webpack.production.config.js:生产环境配置

webpack 配置文件

需命名为 webpack.config.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
const path = require("path") // 模块

module.exports = {
mode: "development",
entry: path.join(__dirname, "./src/main.js"), // 入口文件
output: {
path: path.join(__dirname, "./dist"), // 输出文件
filename: "bundle.js",
},
plugins: [
// 插件
],
module: {
rules: [
// 路由规则
{
test: /\.css$/,
use: ["style-loader", "css-loader"],
},
{
test: /\.(png|jpg|gif|bmp|jpeg)$/,
use: "url-loader?limit=1111&name=[hash:8]-[name].[ext]",
},
{
test: /\.(ttf|eot|svg|woff|woff2)$/,
use: "url-loader",
},
{
test: /\.js$/,
use: "babel-loader",
exclude: /node_modules/,
},
],
},
}

webpack-dev-server

  • 通过 npm 安装:npm i webpack-dev-server
  • 运行命令:webpack-dev-server –devtool eval –port 9876 –progress –colors – compress –hot –inline –content-base ./build
    可以在 package.json 中的 script 加一个启动项。
  • –devtool eval:在报错时精确到文件的行号
  • –progress:显示编译的输出内容进度
  • –compress:启用 gzip 压缩
  • –hot:热更新,无需刷新浏览器
  • –colors:显示编译的输出内容颜色
  • –inline:自动刷新模式。默认为 iframe。
  • –content-base:设置输出目录。

Function

function has special rules:

  1. It must work for every possible input value
  2. And it has only one relationship for each input value

Although each input will only have one output, but for different inputs may have the same output.

Pure function

Given all these, pure functions have a big set of advantages. They are easier to read and understand, as they do one thing. There is no need to look outside the function code and check for variables. There is no need to think how changing the value of the variable will affect other functions. No mutations in other functions will affect the result of the pure function for a specific input.
Pure functions are easier to test, as all dependencies are in the function definition and they do one thing.
For Example:

1
2
3
4
5
6
7
8
9
10
var arr = [1, 2, 3]

// Pure
arr.slice(0, 2) // [1, 2]
arr.slice(0, 2) // [1, 2]
arr.slice(2, 3) // [3]

// Impure
arr.splice(0, 2) // [1, 2]
arr.splice(0, 2) // [3]

Another Example:

1
2
3
4
5
6
7
8
9
10
11
// Impure
var sign = 3

// The return value depends on the system status
var isBigger = (num) => num > 3

// Pure
var isBigger = (num) => {
var sign = 3
return num > sign
}

开始新一轮的学习

大晴天,白天上课,晚上班会。在傍晚时发现了有一丝的腰疼。晚上回到寝室突然又有点心绞痛的迹象···。
年纪轻轻应该不会有大碍,应该吧···毕竟也没有去检查过身体。
今天开始应该会把兼职辞了,开始新的学习吧。驾照什么的···就留着寒暑假有空再说吧。

date: 22:35 2018/10/30

  • before: abcd
  • after: acdb

===第一轮遍历开始===

a(之后)vs a(之前)
key 不变,可复用
此时 a 对应的 oldFiber(之前的 a)在之前的数组(abcd)中索引为 0
所以 lastPlacedIndex = 0;

继续第一轮遍历…

c(之后)vs b(之前)
key 改变,不能复用,跳出第一轮遍历
此时 lastPlacedIndex === 0;
===第一轮遍历结束===

===第二轮遍历开始===
newChildren === cdb,没用完,不需要执行删除旧节点
oldFiber === bcd,没用完,不需要执行插入新节点

将剩余 oldFiber(bcd)保存为 map

// 当前 oldFiber:bcd
// 当前 newChildren:cdb

继续遍历剩余 newChildren

key === c 在 oldFiber 中存在
const oldIndex = c(之前).index;
此时 oldIndex === 2; // 之前节点为 abcd,所以 c.index === 2
比较 oldIndex 与 lastPlacedIndex;

如果 oldIndex >= lastPlacedIndex 代表该可复用节点不需要移动
并将 lastPlacedIndex = oldIndex;
如果 oldIndex < lastplacedIndex 该可复用节点之前插入的位置索引小于这次更新需要插入的位置索引,代表该节点需要向右移动

在例子中,oldIndex 2 > lastPlacedIndex 0
lastPlacedIndex = 2;
c 节点位置不变

继续遍历剩余 newChildren

// 当前 oldFiber:bd
// 当前 newChildren:db

key === d 在 oldFiber 中存在
const oldIndex = d(之前).index;
oldIndex 3 > lastPlacedIndex 2 // 之前节点为 abcd,所以 d.index === 3
lastPlacedIndex = 3;
d 节点位置不变

继续遍历剩余 newChildren

// 当前 oldFiber:b
// 当前 newChildren:b

key === b 在 oldFiber 中存在
const oldIndex = b(之前).index;
oldIndex 1 < lastPlacedIndex 3 // 之前节点为 abcd,所以 b.index === 1
则 b 节点需要向右移动
===第二轮遍历结束===

最终 acd 3 个节点都没有移动,b 节点被标记为移动