0%

先来复习一下react 常用的Hook

1
useCallback(fn,deps)相当于useMemo(()=> fn,deps)

1、 useMemouseCallback接收的参数都是一样,都是在其依赖项发生变化后才执行,都是返回缓存的值,区别在于useMemo返回的是函数运行的结果useCallback返回的是函数

2、 useCallback应该和React.memo配套使用,缺了一个都可能导致性能不升反而下降。

3、记下遇到的bug ,此处存在疑惑,因为我将两个调换顺序后报错了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
let filterProductByBrands = useCallback((() => {
return originalProducts.filter(
(product) => product.brand.brandName.indexOf(selectedBrand) >= 0
)
})(),[originalProducts, selectedBrand])

相当于

let filterProductByBrands = useMemo(() => {
return originalProducts.filter(
(product) => product.brand.brandName.indexOf(selectedBrand) >= 0
)
},[originalProducts, selectedBrand])


安装json-server

用npm安装json-server可以创建一个简单的调用后端数据调用接口

1
npm install json-server -g

建立一个json文件,如react-db.json (注意:json文件里面不要写注释,注意格式,不要写多余的逗号,否则运行的时候会报错

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
{
"products": [
{
"id": 1,
"productName": "iPhone",
"price": 8900,
"quantity": 1
},
{
"id": 2,
"productName": "iPad Pro",
"price": 124000,
"quantity": 3
},
{
"id": 3,
"productName": "荣耀p30",
"price": 4000,
"quantity": 3
},
{
"id": 4,
"productName": "小米",
"price": 3000,
"quantity": 8
},
{
"id": 5,
"productName": "iPad 11",
"price": 3000,
"quantity": 34
}
]
}

执行以下指令,将react-db.json的数据上传到json-server

1
json-server react-db.json --watch --port=5000

这样就可以生成对应的API了

GET请求

向服务器获取数据

1
2
3
4
5
6
7
8
9
10
11
12
13
componentDidMount = async () => {
const response = await fetch('http://localhost:5000/customers', {
method: 'GET',
})
if (response.ok) {
const body = await response.json()
if (body) {
this.setState({ customers: body, customersCount: body.length})
} else {
return 'Error:' + response.status
}
}
}

POST请求

向服务器发送数据,比如说注册用户信息

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
  //以下可实现点击提交按钮注册新用户并将页面跳转到顾客列表页面
onSaveClick = async (event) => {
event.preventDefault()
//获取用户填写的信息
var newcustomer = {
name: this.state.name,
address: { city: this.state.city },
phone: this.state.phone,
photo: this.state.photo,
}

var response = await fetch('http://localhost:5000/customers ',
{method:"POST",
body: JSON.stringify(newcustomer),
headers: {"Content-type": "application/json"}
})
var body =await response.json()//请求发送成功后跳转页面
if (body){
history.replace('/customers')
}
}

PUT请求

向服务器发送更改后的信息,比如修改用户信息,链接要带上具体的id,表明更新的是哪一条用户数据

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
onSaveClick = async (event) => {
event.preventDefault()
var newcustomer = {
name: this.state.name,
address: { city: this.state.city },
phone: this.state.phone,
photo: this.state.photo,
}
var id = this.props.match.params.id
var response = await fetch(`http://localhost:5000/customers/${id} `, {
method: 'PUT',
body: JSON.stringify(newcustomer),
headers: { 'Content-type': 'application/json' },
})
var body = await response.json()
if (body) {
history.replace('/customers')
}
}

DELETE请求

删除服务器数据。和PUT一样,也要带上具体的id,表明删除的是哪一条数据,注意数据删除是删除数据库中的数据,本地数据还是没有更新的,此时要用setState更新下状态,更新后的状态filter出不包含已删除的数据(用id做判断))

1
2
3
4
5
6
7
8
9
10
11
12
onClickDelete =async (id) =>{
if (window.confirm("确定要删除吗?")) {
var response = await fetch(`http://localhost:5000/customers/${id}`, {method: "DELETE"})
if (response.ok){
let allcustomers = [...this.state.customers];
console.log(allcustomers);
allcustomers = allcustomers.filter(cust => {return cust.id !== id})
this.setState({customers: allcustomers})
}
}
}

Eslint + Prettier + stylelint + Husky + Lint-staged

创建样板文件

1
yarn create react-app myapp --typescript

在使用yarn的时候如果发现报错,可以尝试使用npm

添加依赖

1
yarn add prettier lint-staged husky stylelint stylelint-prettier stylelint-config-prettier stylelint-config-recommended eslint-config-prettier eslint-plugin-prettier -D

注意stylelit默认版本是14.1.0,react不支持,可能会报错,建议安装低版本。

添加配置文件

1
touch .eslintrc .eslintignore .gitattributes .prettierrc .stylelintrc

.eslintrc.js

后缀加上js,否则无法识别module.exports语句

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
module.exports = {
env: {
browser: true,
es2021: true,
},
extends: [
'eslint:recommended',
'plugin:react/recommended',
'plugin:@typescript-eslint/recommended',
],
parser: '@typescript-eslint/parser',
parserOptions: {
ecmaFeatures: {
jsx: true,
},
ecmaVersion: 13,
},
plugins: ['react', '@typescript-eslint', 'prettier'],
rules: {
'prettier/prettier': 'error',
},
extends: ['prettier'],
}

.eslintignore

设置不需要eslint语法检查的部分

1
2
# build
build/

.gitattributes

1
* text=auto eol=lf

.prettierrc

1
2
3
4
5
6
7
{
"trailingComma": "es5",
"tabWidth": 2,
"semi": false,
"singleQuote": true,
"endOfLine": "lf"
}

.stylelintrc

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
{
"extends": ["stylelint-prettier/recommended", "stylelint-config-standard"],
"plugins": ["stylelint-prettier"],
"rules": {
"prettier/prettier": true,
"unit-case": null,
"no-descending-specificity": null,
"block-no-empty": null,
"no-empty-source": [true, { "severity": "warning" }],
"declaration-colon-newline-after": null,
"function-name-case": null,
"indentation": null,
"no-invalid-double-slash-comments": null
}
}

在package.json条件以下字段

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
{
"scripts": {
"lint": "eslint \"src/**/*.{js,jsx,ts,tsx}\"",
"lint:fix": "yarn lint --fix",
"stylelint": "stylelint \"src/**/*.{css,scss}\"",
"stylelint:fix": "yarn stylelint --fix"
},
"lint-staged": {
"src/**/*.{js,jsx,ts,tsx,json,css,scss,md}": ["prettier --write", "git add"]
},
"eslintConfig": {
"parser": "babel-eslint"
},
"husky": {
"hooks": {
"pre-commit": "lint-staged"
}
}
}

这只是一个简略react的模板,仅供日常写代码使用,以下为vue比较详细的模板配置,可以参考下

https://juejin.cn/post/7033552881374461960

安装相关依赖

1
2
3
4
5
npm install mobx --save
npm install mobx-react --save
npm i customize-cra --save
npm i react-app-rewired --save
npm i @babel/plugin-proposal-decorators --save

项目根目录新建config-overrides.js文件

修改package.json文件中的script

1
2
3
4
5
6
"scripts": {
"start": "react-app-rewired start",
"build": "react-app-rewired build",
"test": "react-app-rewired test",
"eject": "react-app-rewired eject"
},

一、先安装扩展

https://github.com/zalmoxisus/redux-devtools-extension

在谷歌中安装此扩展,

二、再createStore()中添加第二个参数

方法一:直接添加以下代码

1
2
3
4
5
 const store = createStore(
reducer, /* preloadedState, */
+ window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__() //+后面的内容
);

方法二:通过安装依赖包

1
npm install redux-devtools-extension --save-dev

在入口文件index中引入

1
import { composeWithDevTools } from 'redux-devtools-extension'

在createStore()中添加,注意传进去的是一个函数的指向结果

1
const store = createStore(balanceReducer, composeWithDevTools())

最后,在开发者工具中就可以看到了redux了。

最近心情不太好,糟糕事情比较多,心态有点崩,来写我的博客舒缓下心情,又是努力生活的一天呀🤗🤗

安装介绍

如果你是想探索linux系统的小白,ArchLinux可以说是一个不错的选择后,听说它有点难装,但我自己装下来感觉还行,这个过程其实挺需要耐心的,特别是你对操作的结果会存在一丝忧虑,比如我就很害怕把我自己的电脑搞崩了,到时候可真是崩溃,但这个探索的过程总的来说还是很享受的。

废话不多说,直接上链接: https://zhuanlan.zhihu.com/p/138951848

这位知乎up主写得十分详细,直接按照他的步骤做就好了,有一部需要注意的是,在联无线网的时候需要确定下你家的wifi名称是不是中文,如果是中文的话你在搜索的时候是显示不出来的,当然网也就连不了,除了修改你家wifi名称,就是连你自己的热点了,不过可能网会很慢,所以尽量按照up主说的那样安装镜像。

在使用nano编辑器的时候用Ctr +X+Y保存退出。

在安装Display Manager和桌面环境的时候建议安装KDE,这个和windows系统的比较相似,容易适应,后续坑也比较少。

安装双核系统会出现电脑时间不一致的情况,windows系统显示的事件会比正常时间少8小时,具体介绍及解决方案看这里:https://sspai.com/post/55983#!,建议使用windows第一种方案

安装好之后呢,安装下paru(ArchLinux的包管理助手)https://linux.cn/article-13122-1.html

常用的pacman命令

pacman是ArchLinux的包管理工具

安装或者升级单个软件包

1
pacman -S 包名称

删除包及相应的配置文件及依赖

1
pacman -Rsn 包名称

升级系统并同步仓库数据

1
pacman -Syu 包名称

查询包

1
pacman -Ss 包名称

查询已经安装的包

1
pacman -Qs 包名称

linux中的shell,bash,终端(terminal)的区别

要说清终端是什么,我们先来看看操作系统的组成。简化来说,操作系统分为两个部分,一部分称作内核,另一部分成为用户交互界面。内核部分负责系统的全部逻辑操作,由海量命令组成,这一部分是系统运行的命脉,不与用户接触;交互界面则是开机之后所有我们所看到的东西,比如窗口,软件,应用程序等等。

总的来说,终端是用户来和操作系统内核交互的一个软件, 它是一个命令行解释器,把用户输入的命令传输到内核去执行 ,终端是前端,shell就是后端; 而shell解释器有很多种类,bash是其中的一种。

补充: shell命令:可以让shell工具解释的命令(代码)

今天学习一下如何创建自己的WebPack配置以使用WebPack和Babel捆绑小型JavaScript实用程序库。

初始化

在一个文件夹下执行以下命令

1
$ npm init -y

改命令会创建一个package.json文件夹, -y表示按照默认配置生成模板

1
2
demo
|-- package.json
1
2
3
4
5
$ mkdir src //创建src文件夹
$ cd src
$ touch addDOMContent.js
$ touch capital.js
$ touch index.js //主文件夹

文件目录如下

1
2
3
4
5
6
demo
|-- package.json
|-- src
|--index.js
|--capital.js
|--addDOMContent.js

向src中的文件中添加内容

capital.js

1
2
3
4
5
6
7
function capital(string) {
const capitalizedString =
string.substring(0, 1).toUpperCase() + string.substring(1)
return capitalizedString
}

export default capital

addDOMContent.js

1
2
3
4
5
6
7
function addDOMContent(content) {
const node = document.createElement("h1")
node.innerText = content
document.body.appendChild(node)
}

export default addDOMContent

index.js

1
2
3
4
import capital from "./capital"
import addDOMContent from "./addDOMContent"

export { capital, addDOMContent }

安装 babel语法转换器

此时如果直接执行任意一个js文件,会发现程序报错,原因是导出的时候使用的是es6的语法,所以需要安装将es6语法装换成es5语法的相关依赖

1
$ npm i --save-dev webpack webpack-cli @babel/core @babel/preset-env babel-loader

此时,package.json文件会多出以下配置

1
2
3
4
5
6
7
"devDependencies": {
"@babel/core": "^7.16.0",
"@babel/preset-env": "^7.16.4",
"babel-loader": "^8.2.3",
"webpack": "^5.64.1",
"webpack-cli": "^4.9.1"
}

创建 webpack.config.js 文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
const path = require("path")

module.exports = {
entry: path.resolve(__dirname, "src/index.js"), //打包入口文件
output: {
path: path.resolve(__dirname, "dist"),//打包后的文件夹
filename: "index_bundle.js", //打包后的文件名称
library: "$", // 库名称
libraryTarget: "umd", // 通用模块定义
},
module: {
rules: [
{
test: /\.(js)$/,
exclude: /node_modules/, //不解析node_modules文件夹中的文件
use: "babel-loader", // 用babel-loader解析所有的以.js为后缀的所有文件
},
],
},
mode: "development",
}

更多配置内容看这里:https://webpack.docschina.org/configuration/

创建一个 .babelrc 文件

添加如下内容

1
2
3
{
presets: ["@babel/preset-env"]
}

在package.json中添加如下内容

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
{
"name": "demo",
"version": "1.0.0",
"description": "",
+ "main": "dist/index_bundle.js",
+ "scripts": {
+ "build": "webpack"
+ },//+表示添加的内容
"keywords": [],
"author": "Hridayesh Sharma",
"license": "ISC",
"dependencies": {},
"devDependencies": {
"@babel/core": "^7.10.4",
"@babel/preset-env": "^7.11.0",
"babel-loader": "^8.1.0",
"webpack": "^4.44.1",
"webpack-cli": "^3.3.12",
}
}

打包编译

1
$ npm run build 

此时会生成打包后的文件

创建index.html文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Demo</title>
</head>
<body>
<script src="dist/index_bundle.js"></script>
<script>
console.log($)
alert($.capital("hridayesh"))
$.addDOMContent("Well It Works Fine!!!")
</script>
</body>
</html>

最后在浏览器中打开该静态index.html文件,完成!!!!

什么是decorator

decoratotor, 即装饰器,本质其实就是一个函数,用来拓展类属性和类方法

 ES6 引入了这项功能,目前 Babel 转码器已经支持Decorator

  首先,安装babel-corebabel-plugin-transform-decorators。由于后者包括在babel-preset-stage-0之中,所以改为安装babel-preset-stage-0亦可

1
$ npm install babel-core babel-plugin-transform-decorators

  然后,设置配置文件.babelrc

1
2
3
{
"plugins": ["transform-decorators"]
}

  这时,Babel就可以对Decorator转码了

  脚本中打开的命令如下

1
babel.transform("code", {plugins: ["transform-decorators"]})

用法

decorator修饰对象为下面两种:、

1、类的修饰

2、类属性的修饰

类的修饰

对类本身进行修饰的时候,装饰器接受一个参数,这个参数是要修饰的类本身

1
2
3
4
5
6
7
8
9
10
@testable
class MyTestableClass{
//.....
}
//target指代MyTestableClass,为MyTestableClass添加静态属性
function testable(target) {
target.isTestable = true
}
console.log(MyTestableClass.isTestable); //true

如果想要传递参数,可以在装饰器外面再封装一个函数

1
2
3
4
5
6
7
8
9
10
11
12
@testable(true)
class MyTestableClass{
//.....
}

function testable(isTestable) {
return function (target) {
target.isTestable = isTestable;
}
}

console.log(MyTestableClass.isTestable); //true

类属性的修饰

当对类属性进行修饰时,可接受三个参数

1、类的原型对象

2、需要封装的属性名

3、装饰属性名的描述对象

1
2
3
4
5
6
7
8
9
10
11
12
function readOnly(target, name, descriptor) {
descriptor.writable = false;
return descriptor;
}
//使用readOnly方法修饰类的name方法
class Person{
@readOnly
name(){
return `${this.first}${this.last}`
}
}

使用场景

1、使用react-redux的时候,如果写成下面这种形式,既不雅观也比较麻烦,可以写成装饰器的模式

1
2
3
4
5
6
7
class MyReactComponent extends React.Comment {}
export default connect(mapStateToProps, mapDispatchToProps)(MyReactComponent);
//装饰器模式
@connect(mapDispatchToProps, mapStateToProps)
// eslint-disable-next-line prettier/prettier
export default class MyReactComponent extends React.Component{}

mixins也可以写成装饰器的模式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
function mixins(...list) {
return function (target) {
Object.assign(target.proptype, ...list);
};
}

//使用
const Foo = {
foo() {
console.log('foo');
},
};

@mixins(Foo)
class MyClass {}
let obj = new MyClass();
obj.foo(); //foo

一、递归

在函数的内部,可以调用其它的函数,如果一个函数在内部调用自身,这个函数就是递归函数

1
2
3
4
5
6
7
function pow(x, n) {
if (n == 1) {
return x;
} else {
return x * pow(x, n - 1);
}
}

二、尾递归

在函数的尾部直接调用自身。在递归调用的过程中系统为每一层的返回点、局部变量等开辟了栈来存储,递归次数过多容易造成栈溢出。而采用尾递归时,函数所有的递归调用都出现在函数的末尾,只存在一个调用记录,所以永远不会发生栈溢出错误。

通常的递归

1
2
3
4
5
6
7
8
9
function factorial(n) {
if (n === 1) {
return 1;
} else {
return n * factorial(n - 1);
}
}

console.log(factorial(3)); //6

尾递归

尾递归一般需要初始化一个参数

1
2
3
4
5
6
7
8
9
function factorial(n, init = 1) {
if (n === 1) {
return init; //返回的条件变了
} else {
return factorial(n - 1, init * n);
}
}

console.log(factorial(3)); //6

三、应用场景

数组求和

1
2
3
4
5
6
7
8
9
function sumArray(arr, total = 0) {
if (arr.length === 0) {
return total;
}
return sumArray(arr, total + arr.pop());
}

let arr = [1, 3, 4, 5];
console.log(sumArray(arr));

求斐波那契数列

1、1、2、3、5、8、13、21、34、……在数学上,斐波那契数列以如下被以递推的方法定义:F(1)=1,F(2)=1, F(n)=F(n-1)+F(n-2)(n>=3,n∈N* )

正常递归

1
2
3
4
5
6
7
8
9
10
11
12
13
let a = 1; //相当与第n-2的值
let b = 1; //相当与第n-1的值
let sum = 1; //相当与前两项的和,也是n的值
function fn(n) {
if (n < 3) {
sum = 1;
}
for (i = 3; i <= n; i++) {
return fn(n - 2) + fn(n - 1);
}
return sum;
}
console.log(fn(8)); //21

尾递归

1
2
3
4
5
6
7
8
function factorial2(n, start = 1, total = 1) {
if (n <= 2) {
return total;
} else {
return factorial2(n - 1, total, start + total);
}
}
console.log(factorial2(4)); //3

数组扁平化

1
2
3
4
5
6
7
8
9
10
11
12
let arr = [1, 2, 3, [4, 5, 6, [7, 8, 9]]];
function flat(arr, result = []) {
arr.forEach((value) => {
if (Array.isArray(value)) {
result = result.concat(flat(value, []));
} else {
result.push(value);
}
});
return result;
}
console.log(flat(arr)); //[1, 2, 3, 4, 5,6, 7, 8, 9]

数组对象格式化

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
//数组对象格式化
let obj = {
a: '1',
b: {
c: '2',
D: {
E: '3',
},
},
};

function nameTo_(object) {
let regObj = new RegExp('([A-Z]+)', 'g');
for (let i in object) {
if (object.hasOwnProperty(i)) {
let temp = object[i];
if (regObj.test(i.toString())) {
temp = object[
i.replace(regObj, function (result) {
return '_' + result.toLowerCase();
})
] = object[i];
delete object[i];
}
if (
typeof temp === 'object' ||
Object.prototype.toString.call(temp) === '[object Array]'
) {
nameTo_(temp);
}
}
}
return object;
}

console.log(nameTo_(obj));