Akawa

ETY001的博客

由于 faucet 项目全部是手动搭建的环境,所以在把 react15 升级到 react18 后,

1
2
3
4
5
import { createRoot } from 'react-dom/client';

const appElement = document.getElementById('app');
const root = createRoot(appElement);
root.render(<h1>Hello, world</h1>);

使用上面的代码测试环境是否搭建成功的时候,报 React is not defined 错误。

原因是:在 React 18 中,虽然可以使用 createRoot 来渲染组件,
但仍然需要显式地导入 React 以支持 JSX 语法。

在 JSX 中,<h1>Hello, world</h1> 会被编译成 React.createElement('h1', null, 'Hello, world')
因此,即使你没有直接使用 React,它仍然需要被导入。

由于 babel 我也升级到最新了,在 7.9 版本后,可以使用 @babel/preset-react 来自动引入 JSX 转换。
而不用去显式的导入 React 了。

具体方法就是在 babel.config.js 中对 @babel/preset-react 增加配置如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
{
"presets": [
[
"@babel/preset-env",
{
"targets": "defaults"
}
],
[
"@babel/preset-react",
{
"runtime": "automatic" // 使用自动引入模式
}
]
]
}

如此设置后,再次编译执行,报错就没有了。


ET碎碎念,每周更新,欢迎订阅,点赞,转发!


好用不贵的VPS推荐

https://1hour.win

在 React 18 中,createStoreRedux 提供的一个函数,用于创建 Redux store,但从 Redux Toolkit v5 开始,createStore 已被标记为弃用,并建议使用 configureStore 作为替代。

configureStoreRedux Toolkit 中提供的一个函数,它简化了 Redux 的配置过程,内置了 Redux DevTools、默认的中间件配置等。

下面是如何使用 configureStore 替代 createStore 的一个简单示例:

1
2
3
4
5
6
7
8
import { configureStore } from '@reduxjs/toolkit';
import rootReducer from './reducers';

const store = configureStore({
reducer: rootReducer,
});

export default store;

这次重构 faucet 将会替换掉 createStore 方法。

Redux、React-Redux 和 Redux-Saga 是前端开发中常用的状态管理和异步数据处理工具。它们各自有不同的功能和用途。

1. Redux

Redux 是一个用于 JavaScript 应用的状态管理库。它提供了一种可预测的方式来管理应用的全局状态。

Redux 的核心概念包括:

  • Store: 存储应用的状态,是唯一的数据源。
  • Actions: 是描述状态变化的普通 JavaScript 对象。
  • Reducers: 是纯函数,接收当前状态和 action,返回新的状态。
  • Dispatch: 用于触发 action,从而引发状态的改变。

Redux 的特点:

  • 单一数据源: 整个应用只有一个状态树(store)。
  • 状态是只读的: 不能直接修改状态,必须通过 action 来描述状态变化。
  • 纯函数更新状态: Reducers 必须是纯函数,不得有副作用。

2. React-Redux

React-Redux 是官方提供的 Redux 和 React 的绑定库。它允许 React 组件与 Redux store 进行连接,使得组件能够访问 Redux 的状态并分发 actions。

React-Redux 的特点:

  • <Provider> 组件: 这个组件将 Redux store 提供给应用内所有的组件。
  • connect() 函数: 将 React 组件连接到 Redux store,允许组件从 store 中读取状态和分发 actions。
  • Hooks: useSelector 和 useDispatch 是 React-Redux 提供的 hooks,用于替代 connect(),更符合函数组件的使用方式。

Demo Code

1
2
3
4
5
6
7
8
9
10
11
12
import { Provider } from 'react-redux';
import { createStore } from 'redux';
import App from './App';
import rootReducer from './reducers';

const store = createStore(rootReducer);

const Root = () => (
<Provider store={store}>
<App />
</Provider>
);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import React from 'react';
import { useSelector, useDispatch } from 'react-redux';
import { increment } from './actions';

const Counter = () => {
const count = useSelector(state => state.count);
const dispatch = useDispatch();

return (
<div>
<span>{count}</span>
<button onClick={() => dispatch(increment())}>Increment</button>
</div>
);
};

3. Redux-Saga

Redux-Saga 是一个用于处理 Redux 应用中的异步操作的中间件。它基于 ES6 的 Generator 函数,使得处理复杂的异步逻辑(如异步 API 请求、并发请求、失败重试等)变得更直观和可管理。

Redux-Saga 的特点:

  • Sagas: Generator 函数,用于定义异步操作的逻辑。
  • Effects: Redux-Saga 提供了一系列 effects 函数(如 take, call, put 等)用于处理副作用(例如异步调用)。
  • 非阻塞调用: 通过 Generator 的 yield 机制,可以使异步操作的代码写起来像同步代码。

Demo Code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import { call, put, takeEvery } from 'redux-saga/effects';
import { fetchDataSuccess, fetchDataFailure } from './actions';
import api from './api';

// 定义 Saga
function* fetchDataSaga(action) {
try {
const data = yield call(api.fetchData, action.payload);
yield put(fetchDataSuccess(data));
} catch (error) {
yield put(fetchDataFailure(error));
}
}

// 监听特定的 action
function* watchFetchData() {
yield takeEvery('FETCH_DATA_REQUEST', fetchDataSaga);
}

export default watchFetchData;

总结

  • Redux: 用于管理全局状态,提供一个规范化的状态管理框架。
  • React-Redux: 是 Redux 和 React 的连接工具,让 React 组件可以访问 Redux 的状态和 actions。
  • Redux-Saga: 处理复杂的异步操作,让异步逻辑的管理更加简单和可维护。

这三个工具通常配合使用,以实现复杂的状态管理和异步数据处理。


ET碎碎念,每周更新,欢迎订阅,点赞,转发!


好用不贵的VPS推荐

https://1hour.win

最近在弄faucet 的重构工作,因为要全面使用新的依赖和模式,遇到了很多问题。

比如最近一周一直被 jest 无法在 ESM 模式下工作的问题卡住。

搜索引擎 + chatgpt 多方面的尝试都没有找到有效方案。

直到昨天看到 jest 官方文档里有专门的一页说这个,才搞定问题。

解决方案就是两步。

第一步是用空配置 {} 替换掉之前的 jest.config.jstransform,即关闭 transform

第二步增加环境变量 --experimental-vm-modules,以启用 node 的实验 API,因为 jest 使用的 node 的实验 API 实现的 ESM 支持,这也就意味着,可以卸载掉 babel-jest 插件了。

启用实验 API 这里, jest 官方文档只给了两个 CLI 下的使用例子,而我们的项目使用的是 package.json 中的 scripts 方式。

因此要想在 scripts 里启用,可以按照下面的格式:

1
2
3
"scripts": {
"jest": "NODE_OPTIONS=--experimental-vm-modules jest"
},

每次看完用完,因为别的项目又切换到非 js 语言,过段时间就又忘了,所以写下来总结一下,要不然每次都要现搜索。

使用 import 导入的时候是否加扩展名?

是的,在 ESM 模式下,使用 import 时需要包含文件的扩展名。

这是因为 ESM 模块解析严格遵循文件路径规范,不像 CommonJS 那样自动推断 .js、.json 或 .mjs 等扩展名。

假设有下面的文件结构

1
2
3
4
project/
├── app.js
└── utils/
└── helper.js

在 app.js 中,你需要这样导入 helper.js

1
import { myHelperFunction } from './utils/helper.js';

加 {} 和不加 {} 的区别

在 JavaScript 中,import 语句的语法有两种主要形式:具名导入和默认导入。

加 {} 和不加 {} 的区别在于你是导入模块中的一个具名导出还是默认导出。

1. 具名导入 (Named Import)

1
2
3
4
5
6
7
8
9
10
11
12
13
// utils.js
export const myFunction = () => {
console.log('This is myFunction');
};

export const anotherFunction = () => {
console.log('This is anotherFunction');
};

// main.js
import { myFunction } from './utils.js';

myFunction(); // 输出: This is myFunction

2. 默认导入 (Default Import)

1
2
3
4
5
6
7
8
9
10
11
// utils.js
const myFunction = () => {
console.log('This is myFunction');
};

export default myFunction;

// main.js
import myFunc from './utils.js';

myFunc(); // 输出: This is myFunction

这里对我来说,如果不看文档,纯靠我自己的经验
我一直以为不加 {} 的时候,是把整个文件导出的
也就是按照例子的代码,我的直观感觉应该是 myFunc.myFunction()

之前尝试过好几次,使用 xmodmap 来修改键位,但是都是以失败告终。

每次都是以为懂了,结果都是失败。

最近在一台 Chromebook 上装了 Archlinux,再次尝试调换键位。

想把 Search 键和 左Control 键对换位置。

下面是按键对应的信息

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
--- xev 获取到两个按键的信息 ---
keycode 133 (keysym 0xffeb Super_L)
keycode 37 (keysym 0xffe3 Control_L)

--- xmodmap -pke | grep Control_L 和 grep Super_L 的信息---
keycode 37 = Control_L NoSymbol Control_L
keycode 133 = Super_L Super_L Super_L Super_L Caps_Lock Super_L Caps_Lock
keycode 206 = NoSymbol Super_L NoSymbol Super_L

--- xmodmap -pm 的信息 ---
shift Shift_L (0x32), Shift_R (0x3e)
lock Caps_Lock (0x42)
control Control_L (0x25), Control_R (0x69)
mod1 Alt_L (0x40), Alt_L (0xcc), Meta_L (0xcd)
mod2 Num_Lock (0x4d)
mod3 ISO_Level5_Shift (0xcb)
mod4 Super_L (0x85), Super_R (0x86), Super_L (0xce), Hyper_L (0xcf)
mod5 ISO_Level3_Shift (0x5c)

尝试创建了一份 .Xmodmap 配置如下:

1
2
3
4
5
6
remove control = Control_L
remove mod4 = Super_L
add mod4 = Control_L
add control = Super_L
keycode 37 = Super_L
keycode 133 = Control_L

结果失败了。

不过经过不懈搜索,发现了一个替代方案 – keyd.

这个项目就是为了解决各种键盘问题的,非常感谢这个项目。

同时还找到了一份 针对 Chromebook 的 keyd 配置

通过下面的命令来获取一下按键的名字

1
sudo keyd monitor

根据得到的键位信息,在上面的那份配置的基础上,增加下面的内容

1
2
3
4
5
[main]
....

leftmeta = leftcontrol
leftcontrol = leftmeta

重新载入一下

1
sudo keyd reload

搞定!

咱就说,Linux 下很多东西(iptables啊,X11啊)的配置也不知道为啥搞的很反人类,明明是可以搞的这么简单的啊!

最近我的 BTS 见证人总是收到丢块报警,排查了原因,大概率是因为 API 和 出块都使用同一个程序的原因。

从 Abit 那里了解到,API 请求和出块并没有做线程优先级,所以在有大量 API 请求进入或者长耗时操作的时候,都会对出块造成影响。

对于底层的 C++ 改起来太麻烦,主要我也不会改,所以思路变换一下,那么就在入口位置进行拦截。

最初尝试使用 nginx 的 limit_conn 来限流,但是没有效果。

主要原因是 Websocket 握手建立连接后,就不会再触发规则了,而所有的请求都以 Websocket 的消息形式与后端交互了。

于是换成了 openresty,通过 lua 来建立一个 Websocket 的消息转发机制,符合条件的放行,不符合的拦截。

最终代码越写越复杂,成型的规则就是在 IP 黑名单中的,且 method 也在黑名单里,那么就限流。

具体的代码实现在这里:https://github.com/ety001/openresty/blob/master/scripts/lua/bts/limit_req.lua,可以作为参考。

SPS的机制概述

  • 用户提交提案,提案只要有投票就算是激活了。
  • 提案的钱来自 steem.dao 账号,理论上每小时结算一次,按照投票数排序提案,顺次发钱,发完为止(因为每小时发放数额有限)。

资金来源

steem.dao 账号的资金来源于每次出块时 10% 的 steem,按照喂价中位数转化为 SBD 后存入,同时更新 dgp.sps_interval_ledger(相关逻辑在 database::process_funds() 中)。

dgp.sps_interval_ledger 会在每小时结算的时候清零(相关逻辑在 sps_processor::record_funding() 中)。

资金发放

公式一:

1
每天发放资金量 = steem.dao中的 SBD 数量 / 100 + 每日通胀

公式二:

1
当前小时发放资金 = (当前块时间戳 - 上次发放时间戳)/(24*3600) * 每天发放资金量

发放资金的时候,会按照投票数,从多到少排序所有激活的提案。

然后遍历排序好的提案,每个提案从公式二计算出的资金里发钱,直到公式二计算的资金发完为止(该逻辑在 sps_processor::transfer_payments() )。

备注1: 当前块时间戳,在区块浏览器的某个块的 timestamp 字段。
备注2: 上次发放时间戳,可以从 dgp.last_budget_time 获得。
备注3: SPS 中的通胀在当前的 HF23 版本还没有设计好,所有涉及到通胀的地方都是 0。
备注4: 公式一和公式二在 sps_processor::calculate_maintenance_budget() 中。
备注5: https://steemdb.io/block/79880772 在这个页面的 Virtual Ops 下可以看到当时那个小时的结算情况。

当前状态

目前排名第一的提案,每小时提取 24w SBD 到 steem.dao 账号,而系统每小时发放的资金大约在 1600 SBD,所以第一名的提案把钱都花完了,相当于变相停止了 SPS 系统。

但是目前排名第一的提案截止到 2029年12月31日,在其后面的还有一个截止 2030年3月1日的。这就意味着,最后这个提案可以从 2030年1月1日 到 2030年3月1日,每小时获得 1000 SBD。

在 Archlinux 下通过 snapcraft 安装了 Tradingview。但是登陆是通过浏览器网页登陆后,跳转回程序完成登陆。

而 Chrome 浏览器调用 xdg-open 打开 tradingview:// 格式的 URI 时,提示找不到程序,无法打开。

解决方法就是在 ~/.config/mimeapps.list 中,在 [Default Applications] 块里加入下面一行

1
x-scheme-handler/tradingview=tradingview_tradingview.desktop

保存退出后,可以执行下面的命令,查看是否添加成功

1
xdg-mime query default x-scheme-handler/tradingview

之后,再次登陆即可成功跳转到 Tradingview 程序。

0%