Skip to main content

9-6

1 总结设计模式

TODO

2 利用Excel快速批量插入数据到mysql数据库

[2] 快速导入100万条Excel数据到MySql.https://blog.csdn.net/CSDN1887/article/details/83862035

3 useScrollPosition()

A React hook for updating components when the scroll position of the window on the y-axis changes.

4 remix在线编译器无法下载(or下载慢)怎么办?

用本地搭建一个remix

step1:

yarn global add remix-ide

并将yarn的目录放入环境变量

export PATH="/User/jiangjin/.yarn/bin:$PATH"

step2:

remix-ide

提示找不到remixd,删除与remixd相关代码

vim /Users/jiangjin/.config/yarn/global/node_modules/remix-ide/bin/remix-ide
# 删除以下内容
var remixd = require('remixd')

var router = new remixd.Router(65520, remixd.services.sharedFolder, { remixIdeUrl: 'http://localhost:8080' }, (webSocket) => {
remixd.services.sharedFolder.setWebSocket(webSocket)
remixd.services.sharedFolder.setupNotifications(folder)
remixd.services.sharedFolder.sharedFolder(folder, false)
})

router.start()

step3:

下载solc包,下载地址:https://github.com/ethereum/solc-bin/tree/gh-pages/bin

放入/Users/jiangjin/.config/yarn/global/node_modules/remix-ide/assets/solc-bin/bin目录下(请新建)

并更改 /Users/jiangjin/.config/yarn/global/node_modules/remix-ide/assets/js/0.7.7/app.js文件,改动如下:

//baseurl: 'https://solc-bin.ethereum.org/bin'
baseurl: 'http://localhost:8080/assets/solc-bin/bin'

//var url = 'https://ethereum.github.io/solc-bin/bin/soljson-' + versionString + '.js'
var url = 'http://localhost:8080/assets/solc-bin/bin/soljson-' + versionString + '.js'

step4:

remix-ide
# 重启ide

附:我自己下载了0.5.4, 0.5.16, 0.7.2, 0.7.5这四个版本,需要的可以下载

这是整个assets目录包,下载地址:https://nahaohao.lanzoui.com/iSdMatpiyri

参考

[1] solc-bin编译器下载.https://github.com/ethereum/solc-bin/tree/gh-pages/bin

[2] Remix本地环境搭建.https://blog.csdn.net/qq_32501663/article/details/110876930

5 如何去掉input type=number时输入框内的上下箭头

input::-webkit-outer-spin-button,
input::-webkit-inner-spin-button {
-webkit-appearance: none;
}
input[type="number"]{
-moz-appearance: textfield;
}

6 如何适配iphoneX-ios

两步

Step 1

html头部加入 viewport-fit=cover

<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no, viewport-fit=cover">

Step 2

.fixed-bottom{
bottom: calc(67px + constant(safe-area-inset-bottom));
bottom: calc(67px + env(safe-area-inset-bottom));
/* 如果不支持env或者constant不受影响 */
}

说明:兼容不同IOS版本写法

padding-bottom: constant(safe-area-inset-bottom);
/* 兼容 IOS < 11.2 */
padding-bottom: env(safe-area-inset-bottom);
/* 兼容 IOS >= 11.2 */

7 react-swiper结合

Swiper React Components.https://swiperjs.com/react#swiper-props

首先,官网出了swiper7,就只有swiper7的文档了,我死活没找到以前的文档,

swiper6+react的文档https://github.com/nolimits4web/swiper-website/blob/v6/src/pages/react.mdx

api文档https://www.swiper.com.cn/api/autoplay/19.html

因为ES module的问题,我用不了swiper7,

大概使用步骤,

Step1: 安装

yarn add swiper@^6 # 指定安装6的版本

Step2: 引入文件

// swiper bundle stylesimport 'swiper/swiper-bundle.min.css'// swiper core stylesimport 'swiper/swiper.min.css'// modules stylesimport 'swiper/components/navigation/navigation.min.css'
import 'swiper/components/pagination/pagination.min.css'...

Step3: 拿到swiper对象

const swiperRef = useRef<SwiperClass | undefined>(undefined) 
// 拿到实例化的swiper就可以直接调用其API了
<Swiper
spaceBetween={50}
slidesPerView={1}
onSlideChange={(e: SwiperClass) => {
// console.log('slide change', e, e.activeIndex)
setCurrentTabIndex(e.activeIndex)
}}
onSwiper={(e) => {
console.log('[e](onSwiper):', e)
swiperRef.current = e // 这里可以拿到实例化后的swiper
}}
>
<SwiperSlide></SwiperSlide>
</Swiper>

8 CJS和ESM等模块概述

⚠️ 目前有诸如webpack、vite等打包工具,其支持各种模块导入、导出写法,所以不必担心有这么多模块,这么多模块写法,是历史发展问题,了解即可。

有以下模块规范:CJS、AMD、CMD、UMD、ESM等规范,下面从特点、环境、应用、语法、示例五个方面来对比

CJS

特点:

  1. 同步阻塞加载
  2. 缓存加载

环境:服务器环境

应用:nodeJs实现了CJS规范

语法:

导入:require('路径')
导出:module.exports和exports

说明:exports是module.exports的一个引用,即const exports = module.exports

示例

// a.js
module.exports = {
a: 'hello world'
}

// b.js
var moduleA = require('./a.js');
console.log(moduleA.a); // 打印出hello world

AMD

特点:异步加载

环境:浏览器

应用:requireJs实现AMD规范

语法:

导入:require(['模块名称'], function ('模块变量引用'){// 代码});
导出:define(function (){return '值');

示例:

// a.js
define(function (){
  return {
   a:'hello world'
  }
});
// b.js
require(['./a.js'], function (moduleA){
console.log(moduleA.a); // 打印出:hello world
});

CMD

特点:CMD与AMD类似,是基于AMD改进的一种规范,对依赖模块的执行时机不同,CMD就近依赖,AMD前置依赖

环境:浏览器

应用:seaJs实现CMD规范

语法:

导入:define(function(require, exports, module) {});
导出:define(function(require, exports, module) {});

说明:相对于AMD,CMD中多出了几个钩子参数 require, exports, module

示例:

// a.js
define(function (require, exports, module){
  exports.a = 'hello world';
});
// b.js
define(function (require, exports, module){
var moduleA = require('./a.js');
console.log(moduleA.a); // 打印出:hello world
});

UMD

特点:是一个CJS、AMD和浏览器环境的融合怪,没有具体出名的实现应用,可以看一下常规实现方法:

(function (root, factory) {
if (typeof define === 'function' && define.amd) {
//AMD
define(['jquery'], factory);
} else if (typeof exports === 'object') {
//Node, CommonJS之类的
module.exports = factory(require('jquery'));
} else {
//浏览器全局变量(root 即 window)
root.returnExports = factory(root.jQuery);
}
}(this, function ($) {
//方法
function myFunc(){};
//暴露公共方法
return myFunc;
}));

ESM

特点:ESM是ES2015(ES6)推出的模块规范,支持同步和按需加载

环境:浏览器

语法:

导入:import {模块名A,模块名B...} from '模块路径'
导出:exportexport default
异步导入:import('模块路径').then()方法

示例:

// a.js
export default const a = 123
export const b = 456
// b.js
import a, { b } from 'a.js'

// 异步导入
Promise.all([
import('./module1.js'),
import('./module2.js'),
import('./module3.js'),
])
.then(([module1, module2, module3]) => {
···
});

总结

CJS、AMD、CMD、UMD、ESM它们是一步一步发展而来的,都是前一个规范无法满足开发者多种需求而被创造出来的。目前流行的是ESM规范。

(完)

[1] JS模块规范:AMD、UMD、CMD、commonJS、ES6 module.https://segmentfault.com/a/1190000012419990

9 注意123..toFixed()将丢弃小数部分,与bignumber.js的 tofixed 行为不一样

123.456..toFixed()
=> '123'

而bignumber.js中如下:

const bignumber = new Bignumber(123.456)
bignumber.toFixed()
// 将bignumber转为字符串,不会成为科学计数法bignumber.toString() // 转为字符串,超过7位则称为科学计数法(123.111).toFixed() // 转为字符串,丢弃小数部分
=> '123.456'

联想:如何将数字进行科学计数法?

num.toExponential()
//"1.23456e+5" 转化为科学计数法Number("1.23456e+5") //123456
//Exponential 指数

10 useOnClickOutside-点击外部,回调handler

一般手机端使用(弹出层打开之后,点击其他区域关闭弹出层)

/* 
* @Author: jiangjin
* @Date: 2021-09-07 16:15:28
* @LastEditTime: 2021-09-07 16:18:29
* @LastEditors: jiangjin
* @Description:
* 点击外部,回调handler
*/

/**
* @param node
* @param handler 回调函数,一般进行模态框关闭
*/
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])
}

联想:如果handler变化了,useRef会重新赋值吗?

经过上次测试,useState无论初始值是否变化,都只会执行一次

TODO

11 如何使用 docusaurus 文档框架?

Docusaurus是facebook开源的一个用于搭建文档项目,与gitbook等雷同

如何修改网站信息?

如何修改样式?

请看文档:https://docusaurus.io/zh-CN/docs/next

12 useEffect的 return 什么时候执行?

const [state, setState] = useState(1)
useEffect(() => {
return () => {
console.log("[APP卸载了]:")
}
}, [state])
return (
<>
{" "}
<button
onClick={() => {
setState((prev) => prev + 1)
}}
>
刷新
</button>{" "}
</>
)

以下情况会刷新:

  • 组件销毁
  • useEffect依赖的数据变化的时候也会更新

13 项目引入了redux,如何开启redux-tools插件呢?

详见地址:https://github.com/zalmoxisus/redux-devtools-extension#installation

分两种情况:

第一种,如果没有引入中间件

const composeEnhancers = (window as any).__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;
const store = createStore( reducer, /* preloadedState, */ composeEnhancers);

第二种,如果引入了中间件,则这样使用:

import { createStore, applyMiddleware, compose } from "redux"
const composeEnhancers =
(window as any).__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose
const store = createStore(
reducer,
/* preloadedState, */ composeEnhancers(applyMiddleware(...middleware))
)

14 data signature does not match function rewardInfo.

函数调用错误,

try {
// 注意:解码的时候是decodeFunctionResult,而非decodeFunctionData
// 而编码的时候是encodeFunctionData
result = contractInterface.decodeFunctionResult(fragment, data)
} catch (err) {
console.debug("[解析合约参数失败](toPackageState)")
return { valid: true, loading: false, error: true, syncing, result }
}

interface中有四个方法比较雷同:

方法名
encodeFunctionData
encodeFunctionResult
decodeFunctionData
decodeFunctionResult

一般用 encodeFunctionData-decodeFunctionResult 的组合

如果用decodeFunctionData则会验证头部函数信息,而目前multicall合约返回值并没有头部函数信息。

15 注意...扩展运算符是浅拷贝

// state.callResults如下结构:{  [97]:{    "0x01Af8d162E217eE0eF22f7ddb52488870335ca12-0xcbecf6b5000000000000000000000000bf992941f09310b53a9f3436b0f40b25bccc2c33": {      "data": "0x00000000000000000000000000000000000000000000000000002658f2d67c27",        "blockNumber": 12298223    }  }}const isTrue = callResultsUpdate == state.callResultsconst isTrue1 = callResultsUpdate?.[97] == state.callResults?.[97]console.debug('[isTrue]:', isTrue, isTrue1)=> false true

16 shallowEqual的true为不更新,false为更新

17 Line 0: Parsing error: Cannot read property 'map' of undefined

此问题发生原因为 react-scripts 版本和 typescript 版本不匹配而导致的,项目中

react-scripts版本为3,而typescrip为4

解决方法:统一版本即可

# 注意安装最新版react-scripts的第四版,否则还会出现问题yarn add react-scripts@^4yarn add -D typescript@^4.4

检查 pakeage.json的版本

"react-scripts": "^4","typescript": "^4.4"

联想:^ ~的含义?

一句话:

  • ~1.15.2 := >=1.15.2 <1.16.0

  • ^3.3.4 := >=3.3.4 <4.0.0

附:MAJOR,MINOR,PATCH的含义

1.15.2对应就是MAJOR,MINOR.PATCH:1是marjor version;15是minor version;2是patch version。

  • MAJOR:这个版本号变化了表示有了一个不可以和上个版本兼容的大更改。

  • MINOR:这个版本号变化了表示有了增加了新的功能,并且可以向后兼容。

  • PATCH:这个版本号变化了表示修复了bug,并且可以向后兼容。

https://blog.csdn.net/njweiyukun/article/details/70309066

18 保留有效小数-向上取整-Decimal-Bignumber

import Decimal from "decimal.js"
// 设置Decimal精度和取整方式
Decimal.set({ precision: DECIMAL_PRECISION, rounding: DECIMAL_ROUNDING,})
// TODO 设置其有效位数为5位,向上取整
const bigLeftRangeValue = new Decimal('0.000111116').toSignificantDigits(5)

bigLeftRangeValue.toFixed()
=> 0.00011112

[1] decimal.js文档.https://mikemcl.github.io/decimal.js/#modes

19 迁移git项目

# 查看git remote -v
git remote set-url origin https://github.com/USERNAME/REPOSITORY.git# 查看是否生效git remote -v

之后把远端main分支改名,后将本地main分支推送上去,再删除远端改名的main分支即可(建议先备份分支,再操作)

联想:git删除远端分支

git push origin :main# 表示将空分支推送给远端main

联想:如何新增远端仓库呢?

git remote add origin https://github.com/octocat/Spoon-Knife.git

https://docs.github.com/cn/github/getting-started-with-github/getting-started-with-git/managing-remote-repositories

20 ts忽略文件检测

  • 单行忽略(添加到特定行的行前来忽略这一行的错误)
  • 跳过对某些文件的检查 (添加到该文件的首行才起作用)
// @ts-ignore
// @ts-nocheck

21 useEffect依赖变化的问题-只有组件刷新才会走useEffect吗?

答案是否定的,即使页面没有引用变化的数据,数据变化了,也会走useEffect,就拿Updater组件来说:

export default function Updater(){

const latestBlockNumber = useBlockNumber()
useEffect(() => {
debugger
}, [latestBlockNumber])

return null
}

即使页面什么都不渲染,但只要 latestBlockNumber 变化,都会走useEffect

22 git打标签-git tag

git tag -a v1.0

回车之后进入vim书写注释

删除本地

git tag -d v1.0

删除远端

git push origin :v1.0

推送tag

git push origin v1.0

23 As与Ts

https://www.assemblyscript.org/quick-start.html

24 引入的组件会执行return之前的内容,即使没有渲染

25 jest测试中的it中变量是有范围的

两个it之间是状态隔离的

it('add listeners', () => {
store.dispatch(
addMulticallListenerAction({
chainId: 97,
calls: [call1],
})
)
expect(store.getState().callLiteners[97]).toEqual({
[`${DAI_ADDRESS}-0x`]: { ['1']: 1 },
})
})
it('remove listeners', () => {
store.dispatch(
addMulticallListenerAction({
chainId: 97,
calls: [call1],
})
)

store.dispatch(
removeMulticallListenersAction({
chainId: 97,
calls: [call1],
})
)

expect(store.getState().callLiteners[97]).toEqual({ [`${DAI_ADDRESS}-0x`]: {} })
})

26 要实现鼠标点击效果

:hover,
:focus {
background-color: ${({ pending, theme }) => (pending ? darken(0.05, theme.primary1) : lighten(0.05, theme.black))};
border: 1px solid ${({ theme }) => darken(0.1, theme.primary1)};
box-shadow: unset;
}
:active {
border: 1px solid ${({ theme }) => darken(0.6, theme.white)};
}

思想:给active的border加深多一些即可

27 在组件传参的时候不要传递key属性,应该用其他名称(key为关键字),否则为undefined

<>
{popupList.map((item) => {
return <PopupItem {...item} popKey={item.key} key={item.key} />
})}
</>

28 一个组件需要调用另一个组件的子组件的方法,通过ref的方式最为方便

联想:既是受控组件又是非受控组件,则外部通过ref来改变状态

思想:要想改变非受控组件的状态(内部维护的状态),则通过ref引用其暴露的函数去改变状态

useEffect(() => {
if (!setIndexRef) return

setIndexRef.current = (index: number | undefined | null) => setcurrIndex(index)
}, [setIndexRef])

当然也可以通过传递函数的方式,再外部组件使用ref引用

29 受控组件和非受控组件

一般都使用受控组件,但复杂的组件中两个是一起用的,有一些受控组件的经验:

  1. 组件传参方式尽量采用单个变量,而非整个对象

  2. 如果不是业务组件,内部不要写太多逻辑,只渲染数据即可

    需要什么数据,就传入什么数据

联想:受控组件和非受控组件组合的最佳实践?