直接回答:React 里 render 的多行返回必须用括号包住,否则分号插入会让它返回 undefined;class 组件的方法默认不绑定 this,要么在构造函数里 bind,要么用箭头函数定义。

react中render返回的就是要渲染的,return多行的时候可以

1
2
3
4
return (
/************/
)

*者可以单行

1
return <div><div/>

多个标签必须要用(),这样经过()运算,多行的就变成一个对象了

constructor是React 组件的构造方法

  • 在 React 组件的构造方法 constructor 当中,你可以通过 this.state 为该组件设置自身的状态数据 需要保存状态的时候才使用构造函数

在使用 JavaScript classes 时,你必须调用 super(); 方法才能在继承父类的子类中正确获取到类型的 this 。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class Square extends React.Component {
constructor() {
super();
this.state = {
value: null,
};
}

render() {
return (
<button className="square" onClick={() => alert('click')}>
{this.props.value}
</button>
);
}
}

Object.assign() 这样对数据进行了替换修改,rect中状态不能直接修改数据,只能这样进行替换修改 .slice()方法也可以做到

1
2
var player = {score: 1, name: 'Jeff'};
var newPlayer = Object.assign({}, player, {score: 2});

将函数转换为类

  1. 你可以通过5个步骤将函数组件 Clock 转换为类
  2. 创建一个名称扩展为 React.Component 的ES6 类
  3. 创建一个叫做render()的空方法
  4. 将函数体移动到 render() 方法中
  5. 在 render() 方法中,使用 this.props 替换 props
  6. 删除剩余的空函数声明

1. 使用类就允许我们使用其它特性,例如局部状态、生命周期钩子

2. 类组件应始终使用props调用基础构造函数。

事件处理器会成为类的一个方法

1
2
3
4
constructor(props) {
super(props);
this.state = {date: new Date()};
}

将声明周期方法添加到类

每当Clock组件第一次加载到DOM中的时候,我们都想生成定时器,这在React中被称为挂载 同样,每当Clock生成的这个DOM被移除的时候,我们也会想要清除定时器,这在React中被称为卸载。

1
2
3
4
5
6
7
8
componentDidMount() {
/***挂载***/
//当组件输出到DOM后会执行这个钩子
}

componentWillUnmount() {
/**卸载**/
}

阻止默认行为

React中不能通过返回false来阻止默认行为,只能通过e.preventDefault();

1
2
3
4
function handleClick(e) {
e.preventDefault();
console.log('The link was clicked.');
}

react跨页面传值

1. routerRedux方式

1
2
3
4
5
6
7
8
9
10
import { routerRedux } from 'dva/router';

goOrder (record){//页面跳转+参数
this.props.dispatch(routerRedux.push({
pathname: '/giveData/queryOrder',
params: record.userId
}))
}

<a className={styles.oprLink} onClick={()=>this.goOrder(record)}>查订单</a>

使用this.props.location.params接收值

2. link方式

1
2
3
4
5
import { Link } from 'react-router-dom';
或者
import { Link } from 'dva/router';

<Link to={{pathname:'/user',query:id}}>添加用户</Link>

使用v2,v3this.props.location.query或者v4的this.props.location.search接收值

关于connect

connect之后model中的state就变成了被包装组件的props

判断对象是否为空

使用JSON.stringify(Object)==='{}'判断对象是否为空

npm的四种安装方式

1
2
3
4
5
6
7
npm install moduleName # 安装模块到项目目录下

npm install -g moduleName # -g 的意思是将模块安装到全局,具体安装到磁盘哪个位置,要看 npm config prefix 的位置。

npm install -save moduleName # -save 的意思是将模块安装到项目目录下,并在package文件的dependencies节点写入依赖。

npm install -save-dev moduleName # -save-dev 的意思是将模块安装到项目目录下,并在package文件的devDependencies节点写入依赖。

那么问题来了,在项目中我们应该使用四个命令中的哪个呢?这个就要视情况而定了。下面对这四个命令进行对比,看完后你就不再这么问了。

npm install moduleName 命令

  1. 安装模块到项目node_modules目录下。
  2. 不会将模块依赖写入devDependencies或dependencies 节点。
  3. 运行 npm install 初始化项目时不会下载模块。

npm install -g moduleName 命令

  1. 安装模块到全局,不会在项目node_modules目录中保存模块包。
  2. 不会将模块依赖写入devDependencies或dependencies 节点。
  3. 运行 npm install 初始化项目时不会下载模块。

npm install -save moduleName 命令

  1. 安装模块到项目node_modules目录下。
  2. 会将模块依赖写入dependencies 节点。
  3. 运行 npm install 初始化项目时,会将模块下载到项目目录下。
  4. 运行npm install --production或者注明NODE_ENV变量值为production时,会自动下载模块到node_modules目录中。

npm install -save-dev moduleName 命令

  1. 安装模块到项目node_modules目录下。
  2. 会将模块依赖写入devDependencies 节点。
  3. 运行 npm install 初始化项目时,会将模块下载到项目目录下。
  4. 运行npm install --production或者注明NODE_ENV变量值为production时,不会自动下载模块到node_modules目录中。

总结

devDependencies 节点下的模块是我们在开发时需要用的,比如项目中使用的 gulp ,压缩css、js的模块。这些模块在我们的项目部署后是不需要的,所以我们可以使用 -save-dev 的形式安装。像 express 这些模块是项目运行必备的,应该安装在 dependencies 节点下,所以我们应该使用 -save 的形式安装。

eslint 的三大通用规则

eslint官方提供了3种预安装包:

1. Google标准

eslint-config-google

执行安装:

1
npm install eslint eslint-config-google -g

2. Airbnb标准

eslint-config-airbnb它依赖eslint, eslint-plugin-import, eslint-plugin-react, and eslint-plugin-jsx-a11y等插件,并且对各个插件的版本有所要求。

你可以执行以下命令查看所依赖的各个版本:

1
npm info "eslint-config-airbnb@latest" peerDependencies

你会看到以下输出信息,包含每个了每个plugins的版本要求

1
2
3
4
{ eslint: '^3.15.0',
'eslint-plugin-jsx-a11y': '^3.0.2 || ^4.0.0',
'eslint-plugin-import': '^2.2.0',
'eslint-plugin-react': '^6.9.0' }

知道了每个plugins的版本要求后,代入以下命令执行安装即可使用:

1
npm install eslint-config-airbnb eslint@^#.#.# eslint-plugin-jsx-a11y@^#.#.# eslint-plugin-import@^#.#.# eslint-plugin-react@^#.#.# -g

3. Standard标准

eslint-config-standard它是一些前端工程师自定的标准。

执行安装:

1
npm install eslint-config-standard eslint-plugin-standard eslint-plugin-promise -g

npm镜像

1
2
npm config set registry https://registry.npm.taobao.org --global
npm config set disturl https://npm.taobao.org/dist --global

Yarn、React Native 的命令行工具(react-native-cli)

Yarn是 Facebook 提供的替代 npm 的工具,可以加速 node 模块的下载。React Native 的命令行工具用于执行创建、初始化、更新项目、运行打包服务(packager)等任务。

1
npm install -g yarn react-native-cli

安装完 yarn 后同理也要设置镜像源:

1
2
yarn config set registry https://registry.npm.taobao.org --global
yarn config set disturl https://npm.taobao.org/dist --global

对象为空,操纵对象属性会报错

yiled双向传递消息

1
2
3
4
5
6
7
8
9
10
function* foo(x) {
var y = x * (yield 'hello');
return y;
}
var it = foo(6);
var res = it.next();
console.log(res.value);
res = it.next(7);
console.log(res.value);

在第一次运行it.next(),开始执行生成器函数,将hello返回给res(执行了yield后面的语句,将后面语句的结果返回,如果后面是函数,就执行函数,将函数结果返回,有return就返回,没有返回undefined),第二次执行next(),会将7传入给yield,继续运行至结尾,将最后返回的Y值返还给res 只有暂停的yield才能接受next()传来的值,所以第一个next()不会传值 最后一个next()返回的是return返回的值,如果没有return 就返回undefined

箭头函数的this是词法作用域,this绑定在创建它的对象上

关于import 的一点理解

1
2
3
4
5
6
import test from './haha'
// 这里是haha中有export default 然后test相当于是他的别名
import {test} from './haha'
// 这种方法是haha文件里面有export test,import 的{}中的名字必须和export中的一样

![image](https://note.youdao.com/favicon.ico)

umi设置base之后index.html中引入的文件路径不对

需要指定base、publicbaseh、和cssPublicPath,不然会导致css中不能正确访问图片

关于react中需要使用页面script标签中的变量

之前做的react中使用百度地图的时候需要在script标签中引入百度API,但是在react中无法使用BMap变量,我采取的办法是在window中获取,后来发现,在webpack中可以进行配置,实际是使用的

1
2
3
4
5
6
7
module.exports = {
...
externals: {
"BMap": "BMap",
},
...
}

这里面的BMap其实就是标签引入的script中在全局有了一个变量,变量的名字是BMap 可以参考这个webpack的介绍,和这个echarts中使用BMap

rect中的ref

ref实际是在组件dom加载到页面以后触发的,

1
2
3
4
5
addMap = (a) => {
// 在这里进行一些操作
};

<div id="addMap" ref={(a) => this.addMap(a)} style={{ width: '100%', height: 300 }} />

上面的代码会在id为addMap实例化之后调用addMap函数,然后函数里面可以进行一些组件实例化之后才能进行的操作,比如画地图之类的

跨域时nginx的配置方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
server {
listen 8001;
server_name localhost;
location / {
add_header Access-Control-Allow-Origin *;
add_header Access-Control-Allow-Methods 'GET, POST, OPTIONS';
add_header Access-Control-Allow-Headers 'DNT,X-Mx-ReqToken,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Authorization';

if ($request_method = 'OPTIONS') {
return 204;
}
proxy_pass http://192.168.1.100:5000;

}
}

二.解释

1. Access-Control-Allow-Origin

服务器默认是不被允许跨域的。给Nginx服务器配置Access-Control-Allow-Origin *后,表示服务器可以接受所有的请求源(Origin),即接受所有跨域的请求。

2. Access-Control-Allow-Headers 是为了防止出现以下错误:

Request header field Content-Type is not allowed by Access-Control-Allow-Headers in preflight response. 这个错误表示当前请求Content-Type的值不被支持。其实是我们发起了"application/json"的类型请求导致的。这里涉及到 一个概念:预检请求(preflight request),请看下面"预检请求"的介绍。

3. Access-Control-Allow-Methods 是为了防止出现以下错误:

Content-Type is not allowed by Access-Control-Allow-Headers in preflight response.

4.给OPTIONS 添加 204的返回,是为了处理在发送POST请求时Nginx依然拒绝访问的错误

发送"预检请求"时,需要用到方法 OPTIONS ,所以服务器需要允许该方法。

三、 预检请求(preflight request)

其实上面的配置涉及到了一个W3C标准:CROS,全称是跨域资源共享 (Cross-origin resource sharing),它的提出就是为了解决跨域请求的。

跨域资源共享(CORS)标准新增了一组HTTP首部字段,允许服务器声明哪些源站有权限访问哪些资源。另外,规范要求,对那 些可能对服务器数据产生副作用的HTTP 请求方法(特别是 GET 以外的 HTTP 请求,或者搭配某些 MIME 类型的 POST 请求),浏览器必须首先使用 OPTIONS 方法发起一个预检请求(preflight request),从而获知服务端是否允许该跨域请求。服务器确认允许之后,才发起实际的 HTTP 请求。在预检请求的返回中,服务器端也可以通知客户端,是否需要携带身份凭证(包括 Cookies 和 HTTP 认证相关数据)。

其实Content-Type字段的类型为application/json的请求就是上面所说的搭配某些 MIME 类型的 POST 请求,CORS规定,Content-Type不属于以下MIME类型的,都属于预检请求:

application/x-www-form-urlencoded multipart/form-data text/plain

所以 application/json的请求 会在正式通信之前,增加一次"预检"请求,这次"预检"请求会带上头部信息 Access-Control-Request-Headers: Content-Type:

OPTIONS /api/test HTTP/1.1 Origin: http://foo.example Access-Control-Request-Method: POST Access-Control-Request-Headers: Content-Type ... 省略了一些

服务器回应时,返回的头部信息如果不包含Access-Control-Allow-Headers: Content-Type则表示不接受非默认的的Content-Type。即出现以下错误:

Request header field Content-Type is not allowed by Access-Control-Allow-Headers in preflight response.

参考原文

阮一峰【跨域资源共享 CORS 详解】

typescript类型检查中报错如何规避

在 css-module 中使用 由于 Pro 使用了 css-module,你可能需要

1
import style from './index.style.less';

这时候 typescript 会报错,你可以 使用

1
const style = require('./index.less');

避开这个问题。社区有很多相关讨论,暂时没有最好的办法,只有相对完美的 typings-for-css-modules-loader,同理导入图片,css,svg 也可以通过这种方式避开类型检查。

.? 可选链操作符

在引用为空(nullish ) (null 或者 undefined) 的情况下不会引起错误,该表达式短路返回值是 undefined,也可以和函数调用一起用,如果函数不存在则返回undefined

空值合并操作符(??)

??是一个逻辑操作符,当左侧的操作数为 null 或者 undefined 时,返回其右侧操作数,否则返回左侧操作数。

界面添加下载excel文档功能

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
export const download = (id, name) => {
const oTable = document.getElementById(id).innerHTML;
const excelHtml = `
<html>
<head>
<meta charset='utf-8' />
<style>${str1Css}</style>
</head>
<body>
${oTable}
</body>
</html>
`;

// 创建 Blob 对象
const excelBlob = new Blob([excelHtml], { type: 'application/vnd.ms-excel' });
const oA = document.getElementById('dlink');

// 会产生一个类似blob:d3958f5c-0777-0845-9dcf-2cb28783acaf 这样的 URL 字符串
// 这里 URL.createObjectURL(excelBlob) 会创建一个URL 的 Blob 对象
oA.href = URL.createObjectURL(excelBlob);
oA.download = `${name}.xls`;

oA.click();
};

添加水印

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
export const addWaterMarker = (str, ss1) => {
if (!ss1) {
return;
}
const can = document.createElement('canvas');
const body = ss1;
body.appendChild(can);
can.width = 350; // 画布的宽
can.height = 200; // 画布的高度
can.style.display = 'none';
const cans = can.getContext('2d');
cans.rotate((-20 * Math.PI) / 180); // 画布里面文字的旋转角度
cans.font = '16px Microsoft JhengHei'; // 画布里面文字的字体
cans.fillStyle = 'rgba(17, 17, 17, 0.50)'; // 画布里面文字的颜色
cans.textAlign = 'left'; // 画布里面文字的水平位置
cans.textBaseline = 'bottom'; // 画布里面文字的垂直位置
cans.fillText(str, -10, 120); // 画布里面文字的间距比例
body.style.backgroundImage = `url(${can.toDataURL('image/png')})`; // 把画布插入到body中
};

最大化功能

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
const fullScreen = (el) => {
var rfs =
el.requestFullScreen ||
el.webkitRequestFullScreen ||
el.mozRequestFullScreen ||
el.msRequestFullScreen,
wscript;

if (typeof rfs != 'undefined' && rfs) {
rfs.call(el);
return;
}

if (typeof window.ActiveXObject != 'undefined') {
wscript = new ActiveXObject('WScript.Shell');
if (wscript) {
wscript.SendKeys('{F11}');
}
}
};

PM2的使用

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
1、 pm2需要全局安装
npm install -g pm2
2、进入项目根目录
2.1 启动进程/应用 pm2 start bin/www 或 pm2 start app.js

2.2 重命名进程/应用 pm2 start app.js --name wb123

2.3 添加进程/应用 watch pm2 start bin/www --watch

2.4 结束进程/应用 pm2 stop www

2.5 结束所有进程/应用 pm2 stop all

2.6 删除进程/应用 pm2 delete www

2.7 删除所有进程/应用 pm2 delete all

2.8 列出所有进程/应用 pm2 list

2.9 查看某个进程/应用具体情况 pm2 describe www

2.10 查看进程/应用的资源消耗情况 pm2 monit

2.11 查看pm2的日志 pm2 logs

2.12 若要查看某个进程/应用的日志,使用 pm2 logs www

2.13 重新启动进程/应用 pm2 restart www

2.14 重新启动所有进程/应用 pm2 restart all

2.15 启动NPM命令 pm2 start npm -- run start:no-ui

背景模糊

1
backdrop-filter: blur(4px) grayscale(100%);

关于useRef<boolean>();的另外一种用法,第一次渲染不运行某个函数

1
const prevVisibleRef = useRef<boolean>();

当第一次渲染的时候prevVisibleRef值为underfind可以在useEffect中,给赋值为true,这样就避免在第一次的时候报错

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
const useResetFormOnCloseModal = ({
form,
visible,
}: {
form: FormInstance;
visible: boolean;
}) => {
const prevVisibleRef = useRef<boolean>();
useEffect(() => {
prevVisibleRef.current = visible;
}, [visible]);
const prevVisible = prevVisibleRef.current;
useEffect(() => {
if (!visible && prevVisible) {
form.resetFields();// 页面第一次运行的时候,form并没有实例话,只有当第一次render之后,Effect之前才实例话,在这之前调用会报错,这里的最大作用就是,在第一次运行的时候不调用form.resetFields,也就是可以第一次不运行
}
}, [visible]);
};

这篇笔记整理自我自己的实践记录,如果做法有出入,或者你踩过别的坑,欢迎到留言板一起聊聊。

站内搜索

没有找到内容!