cosyer's Blog

Blog


  • 首页

  • 友链

  • 留言板

  • 归档

  • 关于

  • 搜索

react 标签

2月
23
更新于
2月23
2021
JS

reacti18next实践

发表于 2021-02-23 | 热度 ℃
| 字数统计: 429 (字) | 阅读时长: 2 (分钟)

安装依赖

1
yarn add react-i18next i18next i18next-browser-languagedetector

配置多语言JSON

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// en-us.json
{
"common": {
"cancel": "Cancel"
},
"join": "join",
"retry": "Retry after {{timer}}s",
"choosePerson": "Selected <0>{{num}}</0> person"
}

// zh-cn.json
{
"common": {
"cancel": "取消"
},
"join": "加入",
"retry": "{{timer}}秒后重试",
"choosePerson": "选择人数<0>{{num}}</0>, 没选择人数<1>{{num1}}</1>"
}

// 变量需要通过{{}}格式包裹,组件需要通过<数字></数字>格式包裹

定义i18n.tsx

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
import LanguageDetector from 'i18next-browser-languagedetector';
import i18n from "i18next";
import enUsTrans from "../public/locales/en-us.json";
import zhCnTrans from "../public/locales/zh-cn.json";
import {
initReactI18next
} from 'react-i18next';

i18n.use(LanguageDetector) //嗅探当前浏览器语言
.use(initReactI18next) //init i18next
.init({
//引入资源文件
resources: {
en: {
translation: enUsTrans,
},
zh: {
translation: zhCnTrans,
},
},
//选择默认语言,选择内容为上述配置中的key,即en/zh
fallbackLng: "en",
debug: false,
interpolation: {
escapeValue: false, // not needed for react as it escapes by default
},
})

export default i18n;

主文件引用

1
2
3
4
5
6
7
8
9
10
11
12
import React from 'react'
import App from 'next/app'
import '../config/i18n'

class MyApp extends App {
render() {
const { Component, pageProps } = this.props
return <Component {...pageProps} />
}
}

export default MyApp

使用方式

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
import React from 'react'
import { NextPage } from 'next'
import { useTranslation, Trans, Translation } from 'react-i18next'
// import i18n from "i18next";

const Home: NextPage = () => {
let { t , i18n } = useTranslation()

return (
<div >
<div>
<button onClick={()=>i18n.changeLanguage(i18n.language=='en'?'zh':'en')}>{i18n.language=='en'?'zh':'en'}</button>
</div>

{/* 3种常用使用方式 */}
<h1>{t('home')}</h1>
<h2><Trans>home</Trans></h2>
<Translation>{t => <h3>{t('home')}</h3>}</Translation>

<h1>
{i18n.t("common.cancel")}
<div>{i18n.t("retry", {retry: 3})}</div>
<Trans i18nKey={"choosePerson"} values={{num: 3, num1: 4}} components={[<div>content</div>, <span>content</span>]}></Trans>
</h1>
</div>
)
}

export default Home
11月
23
更新于
11月23
2020
React

react源码解析系列三(React Fiber)

发表于 2020-11-23 | 热度 ℃
| 字数统计: 1,206 (字) | 阅读时长: 7 (分钟)

Fiber

每一个ReactElement对应一个Fiber对象

记录节点的各种状态

串联整个应用形成的结构

例如:FiberRoot的current指向RootFiber 的 child —->App 的child —> div 的child—->input 的sibling

属性:

1
2
3
4
5
return // 指向父节点(每个节点只会有一个父节点)

child // 子节点

sibling // 兄弟节点

Update

用于记录组件状态的改变

存放于Fiber对象的UpdateQueue中:UpdateQue单项链表的结构

多个Update可以同时存在:例如一个事件里面存在三个setState,创建三个update放到UpdateQueue中

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
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
// 创建或更新updatequeue
export function enqueueUpdate<State>(fiber: Fiber, update: Update<State>) {
// Update queues are created lazily.
const alternate = fiber.alternate;
let queue1;
let queue2;
if (alternate === null) {
// There's only one fiber.
queue1 = fiber.updateQueue;
queue2 = null;
if (queue1 === null) {
queue1 = fiber.updateQueue = createUpdateQueue(fiber.memoizedState);
}
} else {
// There are two owners.
queue1 = fiber.updateQueue;
queue2 = alternate.updateQueue;
if (queue1 === null) {
if (queue2 === null) {
// Neither fiber has an update queue. Create new ones.
queue1 = fiber.updateQueue = createUpdateQueue(fiber.memoizedState);
queue2 = alternate.updateQueue = createUpdateQueue(
alternate.memoizedState,
);
} else {
// Only one fiber has an update queue. Clone to create a new one.
queue1 = fiber.updateQueue = cloneUpdateQueue(queue2);
}
} else {
if (queue2 === null) {
// Only one fiber has an update queue. Clone to create a new one.
queue2 = alternate.updateQueue = cloneUpdateQueue(queue1);
} else {
// Both owners have an update queue.
}
}
}
if (queue2 === null || queue1 === queue2) {
// There's only a single queue.
appendUpdateToQueue(queue1, update);
} else {
// There are two queues. We need to append the update to both queues,
// while accounting for the persistent structure of the list — we don't
// want the same update to be added multiple times.
if (queue1.lastUpdate === null || queue2.lastUpdate === null) {
// One of the queues is not empty. We must add the update to both queues.
appendUpdateToQueue(queue1, update);
appendUpdateToQueue(queue2, update);
} else {
// Both queues are non-empty. The last update is the same in both lists,
// because of structural sharing. So, only append to one of the lists.
appendUpdateToQueue(queue1, update);
// But we still need to update the `lastUpdate` pointer of queue2.
queue2.lastUpdate = update;
}
}

if (__DEV__) {
if (
fiber.tag === ClassComponent &&
(currentlyProcessingQueue === queue1 ||
(queue2 !== null && currentlyProcessingQueue === queue2)) &&
!didWarnUpdateInsideUpdate
) {
warningWithoutStack(
false,
'An update (setState, replaceState, or forceUpdate) was scheduled ' +
'from inside an update function. Update functions should be pure, ' +
'with zero side-effects. Consider using componentDidUpdate or a ' +
'callback.',
);
didWarnUpdateInsideUpdate = true;
}
}
}

expirationTime

详解见:https://react.jokcy.me/book/update/expiration-time.html

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// ReactFiberReconciler.js中
export function updateContainer(
element: ReactNodeList,
container: OpaqueRoot,
parentComponent: ?React$Component<any, any>,
callback: ?Function,
): ExpirationTime {
const current = container.current; // Fiber对象
//!important
const currentTime = requestCurrentTime();
const expirationTime = computeExpirationForFiber(currentTime, current);

return updateContainerAtExpirationTime(
element,
container,
parentComponent,
expirationTime,
callback,
);
}

requestCurrentTime

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
// 目前粗略的理解目前为止到js加载完成的时间差
function requestCurrentTime() {
// 已经进入到渲染阶段了
if (isRendering) {
// We're already rendering. Return the most recently read time.
return currentSchedulerTime;
}
// Check if there's pending work.
findHighestPriorityRoot(); // 从调度队列找到权限最高的root
if (
nextFlushedExpirationTime === NoWork ||
nextFlushedExpirationTime === Never
) {
// If there's no pending work, or if the pending work is offscreen, we can
// read the current time without risk of tearing.
recomputeCurrentRendererTime(); // 如下
currentSchedulerTime = currentRendererTime;
return currentSchedulerTime;
}
// There's already pending work. We might be in the middle of a browser
// event. If we were to read the current time, it could cause multiple updates
// within the same event to receive different expiration times, leading to
// tearing. Return the last read time. During the next idle callback, the
// time will be updated.
return currentSchedulerTime;
}

recomputeCurrentRendererTime

1
2
3
4
5
function recomputeCurrentRendererTime() {
// 从js加载完成到现在为止的时间间隔
const currentTimeMs = now() - originalStartTimeMs; // originalStartTimeMs: 初始的now()
currentRendererTime = msToExpirationTime(currentTimeMs);
}

msToExpirationTime

1
2
3
4
5
const UNIT_SIZE = 10
export function msToExpirationTime(ms: number): ExpirationTime {
// Always add an offset so that we don't clash with the magic number for NoWork.
return ((ms / UNIT_SIZE) | 0) + MAGIC_NUMBER_OFFSET;
}

computeExpirationForFiber

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
36
37
38
39
40
41
42
43
44
45
46
function computeExpirationForFiber(currentTime: ExpirationTime, fiber: Fiber) {
let expirationTime;
if (expirationContext !== NoWork) {
// An explicit expiration context was set;
expirationTime = expirationContext;
} else if (isWorking) {
if (isCommitting) {
// Updates that occur during the commit phase should have sync priority
// by default.
expirationTime = Sync;
} else {
// Updates during the render phase should expire at the same time as
// the work that is being rendered.
expirationTime = nextRenderExpirationTime;
}
} else {
// No explicit expiration context was set, and we're not currently
// performing work. Calculate a new expiration time.
if (fiber.mode & ConcurrentMode) {
if (isBatchingInteractiveUpdates) {
// This is an interactive update
expirationTime = computeInteractiveExpiration(currentTime); // 如下
} else {
// This is an async update
expirationTime = computeAsyncExpiration(currentTime); // 如下
}
// If we're in the middle of rendering a tree, do not update at the same
// expiration time that is already rendering.
if (nextRoot !== null && expirationTime === nextRenderExpirationTime) {
expirationTime += 1;
}
} else {
// This is a sync update
expirationTime = Sync;
}
}
if (isBatchingInteractiveUpdates) {
// This is an interactive update. Keep track of the lowest pending
// interactive expiration time. This allows us to synchronously flush
// all interactive updates when needed.
if (expirationTime > lowestPriorityPendingInteractiveExpirationTime) {
lowestPriorityPendingInteractiveExpirationTime = expirationTime;
}
}
return expirationTime;
}

computeInteractiveExpiration

1
2
3
4
5
6
7
8
9
10
export const HIGH_PRIORITY_EXPIRATION = __DEV__ ? 500 : 150;
export const HIGH_PRIORITY_BATCH_SIZE = 100;

export function computeInteractiveExpiration(currentTime: ExpirationTime) {
return computeExpirationBucket(
currentTime,
HIGH_PRIORITY_EXPIRATION,
HIGH_PRIORITY_BATCH_SIZE,
);
}

computeAsyncExpiration

1
2
3
4
5
6
7
8
9
10
11
12
export const LOW_PRIORITY_EXPIRATION = 5000;
export const LOW_PRIORITY_BATCH_SIZE = 250;

export function computeAsyncExpiration(
currentTime: ExpirationTime,
): ExpirationTime {
return computeExpirationBucket(
currentTime,
LOW_PRIORITY_EXPIRATION,
LOW_PRIORITY_BATCH_SIZE,
);
}

computeExpirationBucket

1
2
3
4
5
6
7
8
9
10
11
12
13
function computeExpirationBucket(
currentTime,
expirationInMs,
bucketSizeMs,
): ExpirationTime {
return (
MAGIC_NUMBER_OFFSET +
ceiling(
currentTime - MAGIC_NUMBER_OFFSET + expirationInMs / UNIT_SIZE,
bucketSizeMs / UNIT_SIZE,
)
);
}

计算为了将固定时间间断的,多次setState数据的更新当成一次更新,在这个时间间断内优先级是一样的

11月
11
更新于
11月11
2020
React

react源码解析系列二(React Render)

发表于 2020-11-11 | 热度 ℃
| 字数统计: 2,272 (字) | 阅读时长: 11 (分钟)

创建更新的方式

  1. render || hydrate
  2. setState
  3. forceUpdate

render的步骤

  1. 创建 ReactRoot
  2. 创建FiberRoot RootFiber
  3. 创建更新

后续的是进入调度后,由调度器进行管理


阅读全文 »
11月
23
更新于
11月23
2020
React

react源码解析系列一(React相关API)

发表于 2020-11-03 | 热度 ℃
| 字数统计: 4,092 (字) | 阅读时长: 19 (分钟)

今天开始学习react源码相关的内容,源码基于版本v16.6.0。React16相较于之前的版本是核心上的一次重写,虽然主要的API都没有变化,但是增加了很多能力。并且首次引入了Fiber的概念,之后新的功能都是围绕Fiber进行实现,比如AsyncMode,Profiler等。

React与ReactDom的区别

问题:react仅仅1000多行代码,而react-dom却将近2w行

答: React主要定义基础的概念,比如节点定义和描述相关,真正的实现代码都是在ReactDom里面的,也就是“平台无关”概念,针对不同的平台有不同的实现,但 基本的概念都定义在React里。

React16.6.0使用FlowType做类型检查

Flow 是 facebook 出品的 JavaScript 静态类型检查⼯具。所谓类型检查,就是在编译期尽早发现(由类型错误引起的)bug,⼜不影响代码运⾏(不需要运⾏时动态检查类型),使编写 JavaScript 具有和编写 Java 等强类型语⾔相近的体验。

官方网站

简单示例🌰

1
2
3
npm install -g flow-bin
flow init
touch index.js

1
2
3
4
5
6
7
8
// index.js 进行类型注释
/*@flow*/
function add(x: number, y: number): number {
return x + y
}
add('Hello', 11)

// 报错

阅读全文 »
7月
28
更新于
7月28
2020
React

Docker

发表于 2020-07-28 | 热度 ℃
| 字数统计: 672 (字) | 阅读时长: 2 (分钟)

前言

react-native开发使用的是JS,但是并不是纯正的JS,而是一种JSX语言,就是在JS中嵌入XML语言,因此,只要有一些JS的语法基础的原生开发者,就可以肯容易理解JSX的语法了,在RN中,推荐使用ES6的语法。

性能

使用react-native开发的最大的有点在于开发效率,加入APP并不复杂的话,那么完全可以使用纯JS开发,也就是Android和iOS公用一套界面和逻辑。极大的提高了开发效率。在性能上,RN的表现比原生弱一些,但 是远好于H5。所以总体来看,其实RN的未来还是可以期待的。

运行机制

RN是运行JS的,Android是运行Java字节码的,所以,实际上JS代码的最终运行是通过一层封装,把JS的代码映射成原生代码,而界面上的元素最终使用的也是原生的组件,而不是自己渲染(所以在性能上,RN比H5要 好很多)。

Component简介

在Android中,主要交互容器是activity或Fragment,而在RN中,界面的交互容器是Component:组件。我觉得Component和原生的Fragment其实很像,都存在于activity中,都受制于activity的生命周期,都可卸 载和装载。


阅读全文 »
11月
18
更新于
11月18
2020
JS

30分钟精通React Hooks

发表于 2020-05-12 | 热度 ℃
| 字数统计: 8,708 (字) | 阅读时长: 38 (分钟)

它来了,它来了,16.8版本hooks成功加入(^▽^)

你还在为该使用无状态组件(Function)还是有状态组件(Class)而烦恼吗? —— 拥有了hooks,你再也不需要写Class了,你的所有组件都将是Function。

你还在为搞不清使用哪个生命周期钩子函数而日夜难眠吗? —— 拥有了Hooks,生命周期钩子函数可以先丢一边了。

你在还在为组件中的this指向而晕头转向吗? —— 既然Class都丢掉了,哪里还有this?你的人生第一次不再需要面对this。


阅读全文 »
5月
13
更新于
5月13
2020
JS

React Refs揭密

发表于 2019-02-17 | 热度 ℃
| 字数统计: 1,198 (字) | 阅读时长: 5 (分钟)

什么是Ref

React的官方介绍是这样的:

In the typical React dataflow, props are the only way that parent components interact with their children. To modify a child, you re-render it with new props. However, there are a few cases where you need to imperatively modify a child outside of the typical dataflow. The child to be modified could be an instance of a React component, or it could be a DOM element. For both of these cases, React provides an escape hatch.

其中提到了这几个概念:

在典型的React数据流理念中,父组件跟子组件的交互都是通过传递属性(properties)实现的。如果父组件需要修改子组件,只需要将新的属性传递给子组件,由子组件来实现具体的绘制逻辑。

在特殊的情况下,如果你需要命令式(imperatively)的修改子组件,React也提供了应急的处理办法–Ref。

Ref 既支持修改DOM元素,也支持修改自定义的组件。


阅读全文 »
12月
15
更新于
12月15
2018
JS

为什么react和immutable成为了好基友

发表于 2018-11-21 | 热度 ℃
| 字数统计: 2,418 (字) | 阅读时长: 11 (分钟)

工作中,React社区推崇搭配一起使用Immutable,就像咖啡牛奶伴侣一样。众所周知React的性能优化我们可以优化组件的嵌套层级,

避免不必要的重绘,以及shouldComponentUpdate来判别组件是否会因为当前属性(props)和状态(state)变化而导致组件输出变化。

一提到React,大家第一时间就想到的虚拟DOM(Virtual DOM)和伴随其带来的高性能(在虚拟dom上进行节点的更改最后在反映到真实dom上)。

但是React提供的是声明式的API(declarative API),好的一方面是让我们编写程序更加方便,但另一方面,却使得我们不太了解内部细节。


阅读全文 »
5月
08
更新于
5月08
2020
JS

正则总结

发表于 2018-10-14 | 热度 ℃
| 字数统计: 3,817 (字) | 阅读时长: 15 (分钟)

正则表达式脱离语言,和数据结构与算法一样,作为程序员的软技能。目前存在的问题:不受重视 优点:

  • 显著的提升代码质量
  • 灵活多变
  • 强大的数据校验支持
1
2
3
4
var regex1 = /cosyer/g;
var regex2 = new RegExp('cosyer', 'g');
var regex3 = new RegExp(/cosyer/, 'g');
var regex4 = new RegExp(/cosyer/g);

其中的模式(pattern)部分可以是任何简单或复杂的正则表达式,可以包含字符类、限定符、分组、 向前查找以及反向引用。每个正则表达式都可带有一或多个标志(flags),用以标明正则表达式的行为。 正则表达式的匹配模式支持下列 3 个标志。

  • g:表示全局(global)模式,即模式将被应用于所有字符串,而非在发现第一个匹配项时立即停止;
  • i:表示不区分大小写(case-insensitive)模式,即在确定匹配项时忽略模式与字符串的大小写;
  • m:表示多行(multiline)模式,即在到达一行文本末尾时还会继续查找下一行中是否存在与模式匹配的项。

新增的修饰符:

  • u: Unicode模式。用来处理Unicode大于 \uFFFF的单个字符(超过\uFFFF会被程序员解析为两个字符)
  • y: 粘连模式。和g一样作为全局匹配,区别是g的下一次匹配不要求位置,但是y下一次匹配要求紧跟这这次匹配项之后
  • s: dotAll模式。正则表达式中点(.)作为匹配(除换行符\n,回车符\r,行分隔符,段分隔符)任意单个字符,支持点(.)真正匹配所有单个字符

两种使用方式

1
2
var a1 = /\+d/g; // 字面形式
var a2 = new RegExp('\\+d','g') // 构造函数

在JavaScript中建议使用字面形式的正则表达式,因为不需要担心字符串中的转义字符。比如上面示例代码中字面形式使用\d而构造函数使用的是\d;

1
2
3
var text = "aaa "; 
var pattern1 = /\s$/; //匹配字符串末尾的空格
pattern1.exec(text);

阅读全文 »
7月
01
更新于
7月01
2020
JS

动手实现redux

发表于 2018-10-03 | 热度 ℃
| 字数统计: 741 (字) | 阅读时长: 4 (分钟)

概念:

  • 一个app只有一个store,一个store管理着一个全局state含有以下方法
    • getState: 获取 state;
    • dispatch: 触发 action, 更新 state;
    • subscribe: 订阅数据变更,注册监听器;
  • createStore 传入reducer,返回getState, dispatch, subscribe
  • action是一个至少有type这个键的对象,可以写一个creactAction 函数去return生成action对象
  • createStore.dispatch(action) 根据action这个对象去更新state
  • dispatch是一个函数,内部又将执行reducer函数
  • reducer也是一个函数,传入state,action, 输出一个新的state . (switch case return…)
    • 遵守数据不可变,不要去直接修改 state,而是返回出一个 新对象;
    • 默认情况下需要 返回原数据,避免数据被清空;
    • 最好设置 初始值,便于应用的初始化及数据稳定;

redux


阅读全文 »
12
陈宇(cosyer)

陈宇(cosyer)

不去做的话永远也做不到。

159 日志
10 分类
51 标签
RSS
GitHub Twitter E-Mail FB Page
推荐阅读
  • Callmesoul
  • JsTips
  • Personal Site
  • Resume
© 2021 陈宇(cosyer)
终于等到你(UV):   |   欢迎再来(PV):
Blog总字数: 312.5k字
苏ICP备17005342号-1