2-3
1 ThreeJs 初探
问题一:ThreeJs 与 WebGL 和 OpenGL 的关系是什么?
总体来说,openGL 是规范,webGL 是实现者,threeJs 对 webGL 封装了一层
2 Ts 中如何进行忽略 this
// @ts-ignore
fn.apply(this, 123)
3 Iconify
介绍:iconify 是一个开源的图标库,统一的图标框架,超过 100 个图标集,一个库。 超过 100,000 个开源矢量图标。
它有很多个项目,@iconify/react
是其中一个,用于 react 库
使用示例
先安装
yarn add --dev @iconify/react
导入,并使用
import { Icon } from '@iconify/react';
<Icon icon="mdi-light:home" />
mdi-light:home
为图标,可以从这里查询.https://icon-sets.iconify.design/
[1] @iconify/react.https://www.npmjs.com/package/@iconify/react
4 [ts] JSX 元素类型'ReactNode'不是 JSX 元素的构造函数。类型“未定义”不能分配给类型“ ElementClass”。[2605]
解决方案:用 JSX.Element
来代替 ReactNode
interface Props {
children: JSX.Element[] | JSX.Element
}
问题一:那 ReactNode 和 JSX.Element 有什么区别❓
回答:JSX.Element≈
ReactElement⊂
ReactNode
interface ReactElement<P = any, T extends string | JSXElementConstructor<any> = string | JSXElementConstructor<any>> {
type: T
props: P
key: Key | null
}
// 可以看出props的默认值为any
// 从TypeScript网站:https:github.com/Microsoft/TypeScript/issues/6471
// 建议的做法是将props类型写为{children ?: any}
来看一下 JSX.Element 定义
namespace JSX {
// ...
interface Element extends React.ReactElement<any, any> {}
// ...
}
// 可以看出JSX.Element继承自ReactElement,且没有扩展,
// 可以认为两者
[1] ReactElement、ReactNode 以及 JSX.Element.https://www.jianshu.com/p/95ce2266450a
5 ReactRouter6 中的 Outlet-嵌套
它从 React Router 库中挑选了一个名为 Outlet
的最佳元素,为特定路由呈现任何匹配的子元素
import { Outlet } from 'react-router-dom'
[1] React Router v6 使用指南.https://segmentfault.com/a/1190000023684163
6 Yup
一个转化和验证数据的 js 库
[1] Yup.https://github.com/jquense/yup
7 InferType-ts 推断类型
type User = InferType<typeof userSchema>
/* {
name: string;
age: number;
email?: string | undefined
website?: string | null | undefined
createdOn: Date
}*/
8 Nestjs/crud
问题一:@nestjs/crud 是什么?怎么用?
[1] nestjsx/crud.description.https://github.com/nestjsx/crud/wiki/Controllers#description
9 在本机创建多个 github 用户
ssh-keygen -t rsa -C "[email protected]"
ssh-keygen -t rsa -C "[email protected]"
git config --global user.name "yhonismx"
git config --global user.email "[email protected]"
git config --global user.name "ginlink"
git config --global user.email "[email protected]"
问题一:如何在本机创建多个 github 用户?
问题二:如何使用指定账户提交代码(交互)?
9.1 创建多个 github 账户的私钥
这里以创建 master0
、master1
两个账号为例,其邮箱分别为
# 通过邮箱,在本机创建master0的秘钥
ssh-keygen -t rsa -b 4096 -C "[email protected]"
# 会有如下提示,请将新的秘钥保存到/Users/you/.ssh/id_rsa_master0
# Enter a file in which to save the key (/Users/you/.ssh/id_rsa):
# 之后一路回车
# 同理,通过邮箱,在本机创建master1的秘钥
ssh-keygen -t rsa -b 4096 -C "[email protected]"
# 保存到/Users/you/.ssh/id_rsa_master1
9.2 关联本机 ssh 和 github 服务器
拷贝 master0
公钥,注意是公钥
cat ~/.ssh/id_rsa_master0.pub
前往github.com登录master0
账号,在Settings -> SSH and GPG keys
里,点击New SSH key
填入拷贝的公钥
同理,拷贝 master1
公钥,之后登录 github 填入公钥
cat ~/.ssh/id_rsa_master1.pub
这样,本机就可以通过 ssh 连上 github 服务器了
9.3 测试连通性
1.测试连通性之前,先配置 config
vim ~/.ssh/config
2.填入,并保存
# master0
Host master0 # 通过Host来区别
HostName github.com # git服务器地址
User master0 # 用户
IdentityFile ~/.ssh/id_rsa_master0 # 私钥路径
# master0
Host master1
HostName github.com
User master1
IdentityFile ~/.ssh/id_rsa_master1
3.测试连通性
ssh -T git@master0
ssh -T git@master1
如果出现,则连通
Hi master0! You've successfully authenticated, but GitHub does not
provide shell access.
9.4 测试切换账号提交代码
通过 9.3 我们的两个账号都已经可以和 github 通讯了,那么接下来就是指定账号进行提交代码了。
# 使用master0
git config --local user.name "master0"
git config --local user.email "[email protected]"
# 使用master1
git config --local user.name "master1"
git config --local user.email "[email protected]"
以上是仓库级别的切换,如果想了解 local、global、system 的区别,请查看[1]
9.5 注意
- 先切换账号再
commit
代码,否则是上一个用户的提交记录
[1] git config 配置.https://www.cnblogs.com/fireporsche/p/9359130.html
[2] 管理切换多个 Git 账号.https://windomz.github.io/2017/03/01/%E5%A4%9Agit%E8%B4%A6%E5%8F%B7%E5%88%87%E6%8D%A2/
10 红胡子
https://www.kuaiyunyy.com/vodplay/285178-1-1.html
11 React 错误边界 ErrorBoundary
问题一:ErrorBoundary 是什么?解决了什么问题?
问题二:ErrorBoundary 不能处理什么?那这些问题如何解决?
12 如何整合@nestjs/crud 和 jwt 认证?
项目:yue-code-api
13 Js 中的连等赋值
var a = { n: 1 }
var b = a
a.x = a = { n: 2 }
alert(a.x) // --> undefined
alert(b.x) // --> {n: 2}
问题一:为什么 a.x
为 undefined?
因为 a.x = a = {n: 2}; 这一句 先确定引用
,再进行右结合赋值
所以 a.x 中的 a 还是原来的 a,而非新生成的 a
14 Js 中数组排序 b-a 为降序
15 Js 中 call(null | undefined) 表示 window 对象
16 Js 中 IIEF 中 this 指向 window
17 暂时性死区
存在于 let 和 const 声明的变量,表示变量无法先被使用再声明
function main() {
console.log('[]:', a) // 报引用错误
let a = 123
}
// ================================
function main() {
console.log('[]:', a) // undefined
var a = 123
}
18 Promise.all 如何不被一个错误打断?
async function main() {
const reses = await Promise.all(
[
Promise.reject({ code: 500, msg: "服务异常" }),
Promise.resolve({ code: 200, list: [] }),
Promise.resolve({ code: 200, list: [] })
].map(p => p.catch(e => e))
)
}
=>> res
[
{ code: 500, msg: "服务异常" },
{ code: 200, list: [] },
{ code: 200, list: [] }
]
注意
但这样写会有一个弊端,无法通过 catch 获取错误,此时所有请求都会成功,只能通过一些特征去判断错误
[1] Promise.all 哪怕一个请求失败了也能得到其余正确的请求结果的解决方案.https://blog.csdn.net/zwwgoodwill/article/details/105050693?spm=1001.2101.3001.6650.1&utm_medium=distribute.pc_relevant.none-task-blog-2%7Edefault%7ELandingCtr%7EHighlightScore-1.queryctrv2&depth_1-utm_source=distribute.pc_relevant.none-task-blog-2%7Edefault%7ELandingCtr%7EHighlightScore-1.queryctrv2&utm_relevant_index=2
19 react-helmet-async
介绍:是 react-helmet 的异步版本,用于管理页面的 head
作用:安全
const Page = forwardRef(({ children, title = '', ...other }, ref) => (
<Box ref={ref} {...other}>
<Helmet>
<title>{title}</title>
</Helmet>
{children}
</Box>
))
export default Page
20 change-case
介绍:转换一个字符串在 camelCase, PascalCase, Capital Case, snake_case, param-case, CONSTANT_CASE 和其他的库
import {
camelCase,
capitalCase,
constantCase,
dotCase,
headerCase,
noCase,
paramCase,
pascalCase,
pathCase,
sentenceCase,
snakeCase,
} from 'change-case'
[1] change-case.https://www.npmjs.com/package/change-case
21 列表排序算法
function descendingComparator<T>(a: T, b: T, orderBy: keyof T) {
if (b[orderBy] < a[orderBy]) {
return -1
}
if (b[orderBy] > a[orderBy]) {
return 1
}
return 0
}
type Order = 'asc' | 'desc'
function getComparator<Key extends keyof any>(
order: Order,
orderBy: Key
): (a: { [key in Key]: number | string }, b: { [key in Key]: number | string }) => number {
return order === 'desc'
? (a, b) => descendingComparator(a, b, orderBy)
: (a, b) => -descendingComparator(a, b, orderBy)
}
使用示例
const order = 'asc' // 'asc' | 'desc'
const orderBy = 'age'
const rows = [
{ name: 'John0', age: 18 },
{ name: 'John1', age: 20 },
{ name: 'John2', age: 10 },
]
rows.sort(getComparator(order, orderBy))
22 Eva Icons
[1] Eva Icons.https://akveo.github.io/eva-icons/#/
23 加入币种到钱包的逻辑
import { getTokenLogoURL } from './../components/CurrencyLogo/index'
import { Currency, Token } from 'plugins/@uniswap/sdk-core'
import { useCallback, useState } from 'react'
import { useActiveWeb3React } from 'hooks/web3'
export default function useAddTokenToMetamask(currencyToAdd: Currency | undefined): {
addToken: () => void
success: boolean | undefined
} {
const { library } = useActiveWeb3React()
const token: Token | undefined = currencyToAdd?.wrapped
const [success, setSuccess] = useState<boolean | undefined>()
const addToken = useCallback(() => {
if (library && library.provider.isMetaMask && library.provider.request && token) {
library.provider
.request({
method: 'wallet_watchAsset',
params: {
//@ts-ignore // need this for incorrect ethers provider type
type: 'ERC20',
options: {
address: token.address,
symbol: token.symbol,
decimals: token.decimals,
image: getTokenLogoURL(token.address),
},
},
})
.then((success) => {
setSuccess(success)
})
.catch((error) => {
setSuccess(false)
})
} else {
setSuccess(false)
}
}, [library, token])
return { addToken, success }
}
24 点击外部取消弹窗
import { RefObject, useEffect, useRef } from 'react'
export function useOnClickOutside<T extends HTMLElement>(
node: RefObject<T | undefined>,
handler: undefined | (() => void)
) {
const handlerRef = useRef<undefined | (() => void)>(handler)
useEffect(() => {
handlerRef.current = handler
}, [handler])
useEffect(() => {
const handleClickOutside = (e: MouseEvent) => {
if (node.current?.contains(e.target as Node) ?? false) {
return
}
if (handlerRef.current) handlerRef.current()
}
document.addEventListener('mousedown', handleClickOutside)
return () => {
document.removeEventListener('mousedown', handleClickOutside)
}
}, [node])
}
注意
- 同一个页面不可以出现相同的 node,否则无法正常工作,特别是
25 自动切换到响应以太坊网络
try {
await ethereum.request({
method: 'wallet_switchEthereumChain',
params: [{ chainId: '0xf00' }],
})
} catch (switchError) {
// This error code indicates that the chain has not been added to MetaMask.
if (switchError.code === 4902) {
try {
await ethereum.request({
method: 'wallet_addEthereumChain',
params: [
{
chainId: '0xf00',
chainName: '...',
rpcUrls: ['https://...'] /* ... */,
},
],
})
} catch (addError) {
// handle "add" error
}
}
// handle other "switch" errors
}
[1] wallet_switchEthereumChain
.https://docs.metamask.io/guide/rpc-api.html#unrestricted-methods
26 如何使用 Environment secrets?
一个完整的示例,主要 environment
起作用
name: build_and_deploy_web_to_110.99
on: workflow_dispatch
jobs:
build:
runs-on: ubuntu-latest
environment: deployEnv
env:
DOCKER_ACCESS_NAME: ${{ secrets.DOCKER_ACCESS_NAME }}
DOCKER_ACCESS_TOKEN: ${{ secrets.DOCKER_ACCESS_TOKEN }}
PRIVATE_KEY: ${{ secrets.PRIVATE_KEY }}
HOST_IP_99: ${{ secrets.HOST_IP_99 }}
HOST_ADMIN_99: ${{ secrets.HOST_ADMIN_99 }}
strategy:
matrix:
node-version: [14.x]
steps:
- uses: actions/checkout@v2
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v1
with:
node-version: ${{ matrix.node-version }}
- name: restore yarn
uses: actions/cache@v2
with:
path: |
node_modules
*/*/node_modules
key: ${{ runner.os }}-${{ hashFiles('**/yarn.lock') }}
- name: build
run: ./sh/build_web_to_110.99.sh
- name: deploy
run: ./sh/deploy_web_to_110.99.sh
注意
- 要加上
environment
属性,否则认为 secrets 为项目上下文 - 要在 jobs 属性下加入,否则提示 environment 错误
27 在 Nginx 中配置二级域名
该内容和 [27 用certbot管理https证书]
连用
共需要一个步骤,如下:
步骤一:修改 nginx 配置文件
我本机 nginx 配置路径为: /etc/nginx/conf.d/default.conf
详细解释,请参阅:[1]
server {
listen 80;
server_name _;
return 301 https://$host$request_uri;
}
server {
listen 443 ssl;
server_name gincool.com;
root /usr/share/nginx/html;
index index.html index.htm;
ssl_certificate /etc/letsencrypt/live/gincool.com/fullchain.pem; # managed by Certbot
ssl_certificate_key /etc/letsencrypt/live/gincool.com/privkey.pem; # managed by Certbot
}
server {
listen 443 ssl;
server_name api.gincool.com;
location / {
proxy_pass http://localhost:3050/;
}
ssl_certificate /etc/letsencrypt/live/gincool.com/fullchain.pem; # managed by Certbot
ssl_certificate_key /etc/letsencrypt/live/gincool.com/privkey.pem; # managed by Certbot
}
附 1:nginx 重启命令
nginx -s reload
[1] 在 Nginx 中配置二级域名.https://mincong.io/cn/nginx-subdomains/
27 用 certbot 管理 https 证书
步骤一:访问 cerbot 网站
https://certbot.eff.org/instructions?ws=nginx&os=ubuntufocal
选择自己的软件和操作系统
步骤二:根据网页下面的步骤进行操作
附 1:安装 snapd
# 安装snap
sudo apt-get install snapd
# 安装snapcraft
sudo apt-get install snapcraft
一般到第 7 步就完成了
最终 certbot 修改 default.conf 的文件内容为:
server {
listen 443 ssl;
server_name gincool.com;
root /usr/share/nginx/html;
index index.html index.htm;
# certbot加入了这些内容
ssl_certificate /etc/letsencrypt/live/gincool.com/fullchain.pem; # managed by Certbot
ssl_certificate_key /etc/letsencrypt/live/gincool.com/privkey.pem; # managed by Certbot
}
server {
listen 443 ssl;
server_name api.gincool.com;
location / {
proxy_pass http://localhost:3050/;
}
ssl_certificate /etc/letsencrypt/live/gincool.com/fullchain.pem; # managed by Certbot
ssl_certificate_key /etc/letsencrypt/live/gincool.com/privkey.pem; # managed by Certbot
}
附 2:更新证书
sudo certbot --nginx
28 以太坊网络中如何预估 gas 费呢?
export interface FeeData {
maxFeePerGas: null | BigNumber
maxPriorityFeePerGas: null | BigNumber
gasPrice: null | BigNumber
}
问题一:以太坊网络中有几种费用?
在 EIP-1559 之前,只有 gasPrice
在 EIP-1559 之后,出现了一些新名词
baseFeePerGas:基本费用,由每个区块头生成的每 gas 的基本费用
maxPriorityFeePerGas:小费,由用户设置
maxFeePerGas:用户愿意为交易支付的最大 Gas 费用,由用户设置
maxFeePerGas = baseFeePerGas + maxPriorityFeePerGas
所以 gasPrice 和 maxFeePerGas 不能同时使用,否则会报错
问题二:为什么有 EIP-1559?
为了消除链上 Gas 费较低的交易通常会长时间处于未决状态,EIP-1559 引入了一个更复杂、更公平的 gas 费用系统
问题三:那一条链既支持 EIP-1559 又支持传统非 EIP-1559 的交易,那该通过哪种方式发送交易呢?
建议通过 EIP-1559,虽然该方式手续费可能会高一些,但能够确保交易快速被执行,特别是在拥堵的链上。
如果在不拥堵的链上(测试网),那么用哪种都可以,因为手续费再低,也会有矿工帮忙