获取近一年时间
const dateFormat = 'YYYY-MM-DD';
const dateRange = {
"6month": [moment().subtract(6, 'months'), moment()], //近6个月
"1year": [moment().subtract(1, 'years'), moment()], // 近1年
"3year": [moment().subtract(3, 'years'), moment()], // 近3年
"5year": [moment().subtract(5, 'years'), moment()], // 近5年
"start": [moment(prod_start_time, dateFormat), moment()] // 从某一特定时间至今
}
js获取最后一位
1.length-1
2.arr.slice(-1)[0]
3.arr.at(-1) ES2022兼容性不好
4. arr.concat().pop(); pop会改变原数组,所以要先复制一份副本
双波浪线
Math.floor() 方法:将数字向下舍入到最接近的整数,并返回结果。如果传递的参数是整数,则该值不会被舍入。
~~ 两个按位非操作符,作用是干掉数字的小数部分。和强制类型转换的结果基本一样
onfinish
antd 3 中的form表单提交时,有onSubmit事件,而antd 4中则换成了onFinish事件,并且当使用onfinish事件时,要注意不能同时给Button绑定onClick事件视图出发提交。因为会请求两次提交事件,并且第一次接收不到值会导致提交失败或异常。
<Form.Item className="login-item">
<Button type="primary" htmlType="submit" className="login-form-button" style={{width: '100%'}}>登陆</Button>
<p style={{color: '#8f8f8f', fontSize: '12px', marginTop: '20px', textAlign: 'center'}}>用户名:admin,密码: admin</p>
</Form.Item>
控制 表单数据域
antd 3中使用getFieldDecorator 结合 Form.create。
import { Form, Icon, Input, Button } from 'antd';
function hasErrors(fieldsError) {
return Object.keys(fieldsError).some(field => fieldsError[field]);
}
class HorizontalLoginForm extends React.Component {
componentDidMount() {
// To disable submit button at the beginning.
this.props.form.validateFields();
}
handleSubmit = e => {
e.preventDefault();
this.props.form.validateFields((err, values) => {
if (!err) {
console.log('Received values of form: ', values);
}
});
};
render() {
const { getFieldDecorator, getFieldsError, getFieldError, isFieldTouched } = this.props.form;
// Only show error after a field is touched.
const usernameError = isFieldTouched('username') && getFieldError('username');
const passwordError = isFieldTouched('password') && getFieldError('password');
return (
<Form layout="inline" onSubmit={this.handleSubmit}>
<Form.Item validateStatus={usernameError ? 'error' : ''} help={usernameError || ''}>
{getFieldDecorator('username', {
rules: [{ required: true, message: 'Please input your username!' }],
})(
<Input
prefix={<Icon type="user" style={{ color: 'rgba(0,0,0,.25)' }} />}
placeholder="Username"
/>,
)}
</Form.Item>
<Form.Item validateStatus={passwordError ? 'error' : ''} help={passwordError || ''}>
{getFieldDecorator('password', {
rules: [{ required: true, message: 'Please input your Password!' }],
})(
<Input
prefix={<Icon type="lock" style={{ color: 'rgba(0,0,0,.25)' }} />}
type="password"
placeholder="Password"
/>,
)}
</Form.Item>
<Form.Item>
<Button type="primary" htmlType="submit" disabled={hasErrors(getFieldsError())}>
Log in
</Button>
</Form.Item>
</Form>
);
}
}
const WrappedHorizontalLoginForm = Form.create({ name: 'horizontal_login' })(HorizontalLoginForm);
ReactDOM.render(<WrappedHorizontalLoginForm />, mountNode);
我们推荐使用 Form.useForm 创建表单数据域进行控制。如果是在 class component 下,你也可以通过 ref 获取数据域。
这一点来看 antd 4 使用Form简单了很多。
全屏
使用react-full-screen,但要注意的是,useFullScreenHandle必须在函数组件中使用
import { FullScreen, useFullScreenHandle } from "react-full-screen";
const handle = useFullScreenHandle();
<FullScreen
handle={handle}
style={{ background: "#ffffff" }}
>
...
{!this.props.fullScreen ? <FullscreenOutlined onClick={() => this.fullScreenHandle(true)} /> :
<FullscreenExitOutlined onClick={() => this.fullScreenHandle(false)}/>}
</FullScreen>
fullScreenHandle = (tag) => {
tag ? handle.enter() : handle.exit();
}
这里为了使用redux而使用redux,其实获取全屏状态可以直接从handle中获取handle.active 就是状态。
antd 4 动态渲染Icon
从4.0开始,antd 不在内置 Icon 组件,需要使用独立的 @ant-design/icons 包
import * as Icon from '@ant-design/icons';
const icon = React.createElement(
Icon[item.icon],
{
style:{ fontSize: '16px'}
}
)
react中使用img标签
因为太不经常引入图片在项目中,所以忘记了引入标签的方法。
在react中使用img标签,使用相对路径引入不成功。所以需要用import from 导入图片,这样的好处是即使你打包到线上也不会出现路径丢失找不到文件的问题。
antd 换肤
想要在系统内写一个换肤功能,有多种配色方式。
基本配置:替换APP.css 中@import ‘~antd/dist/antd.min.css’; 为@import ‘~antd/dist/antd.variable.min.css’
使用:const theme_obj = {starBlue: '#1890ff', cutePink: '#ff8bba'}; ConfigProvider.config({ theme: { primaryColor: theme_obj[item]}, });
这里我使用的不是antd4文档中推荐的方法,我给public中的index.html的root盒子一个类名,给不同的类型设置不同的primaryColor。这样切换的时候就可以换肤了,不过没有做缓存,觉得没有必要,如果有必要也可以把切换的皮肤做一个缓存。
react 鼠标事件
onMouseDown 按下
onMouseMove 拖动
onMouseUp 抬起
withRouter
withRouter的作用是将一个组件包裹进Route里面, 然后react-router的三个对象history, location, match就会被放进这个组件的props属性中。仅仅是将一个不是Router但是想要通过路由跳转的组件包裹起来,变为可以路由跳转的组件,其余的路由如何配置跟它无关。
路由配置是另一个组件,HashRouter或者BrowserRouer包裹起来的配置好的Route组件。
不要将两者过于冗杂在一起思考会影响判断,也就是说路由页面可以定义在任何一个部分任何一个结构里,要使用这个路由也就是要跳转到这个路由页面,那么源头就要给一个LInk组件裹着或者使用withRouter包裹。
国际标准时间
2022-05-16T16:00:00.000+0000
装饰器 @修饰符
装饰器 decorator 是一种函数,是 Es6 的一个语法糖,是一种与类(class)相关的语法,用来注释或修改类和方法
装饰器本质上是一个函数,它可以让其他函数在不需要做任何代码变动的前提下增加额外功能。
可以抽象出某些页面共同的特点,使用装饰器创建公共的模版,其他被装饰的页面只要实现自己独特的部分就可以了。
类似父类,但又不是父类。
这里引入一个新的包装模式:装饰器模式,引入装饰器模式之后又要引入一个概念,高阶组件(即传入的参数是组件,返回一个新的组件,高阶组件是react应用中很重要的一部分,最大的特点就是重用组件逻辑。)
⒈ 装饰器对类的行为的改变时代码编译时发生的,而不是在运行时,这意味着,装饰器能在编译阶段运行代码,它本身就是编译时执行的函数
⒉ 装饰器只能用于类和类的方法,不能用于函数,因为它存在函数提升
轮询
普通轮询:
原理:每隔一段时间发一次请求来获取最新数据。setTimeout或者setTimeInterval发送ajax请求,DOM操作更新页面数据
缺陷:不稳定而且对服务器造成的压力比较大,耗费资源。而且实时性不高,可能你看到的跟别人看到的数据不一致。
优点:后端接口正常写就好,不需要做特殊对待。前端用定时器就好,而且即使换页面也可以一直请求,可以做到全局弹窗弹出消息的效果。
长轮询:
原理:浏览器发出请求。服务器资源如果没有就绪,不立即返回,而是在一个时间范围内,不断的去查询资源是否就绪,如果就绪返回资源,如果超时了还没有就绪就返回超时。
后端实现方式见后端总结
前端实现方式:
function test() {
axios.request({
url: "/test_apis",
method: "POST"
}).then(function (res) {
//如果返回数据
if (res.data != "") {
// 拿到数据
......
}
// 立即再发起一个请求,等着获取最新数据
get_votes()
})
}
长连接
websocket 是H5的一个新协议(格式: ‘ws://xxxx’),和HTTP是同层级的协议,让客户端与服务端建立长连接。
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>测试长连接</title>
</head>
<body>
<button onclick="test_send()">点击测试</button>
<button onclick="test_close()">点击关闭</button>
<script>
// Create WebSocket connection.
const socket = new WebSocket('ws://121.40.165.18:8800'); //连接
// 发送函数
function test_send() {
// Connection opened
socket.send('Hello Server!');
}
//关闭函数
function test_close() {
socket.onclose = function () {
// 关闭 websocket
alert("连接已关闭...");
};
socket.onclose();
}
//连接成功时
socket.addEventListener('open', function (event) {
socket.send('Hello Server!');
});
// 服务器推送过来的消息
socket.addEventListener('message', function (event) {
console.log('Message from server ', event.data);
});
var readyState = socket.readyState; //状态
console.log(readyState)
</script>
</body>
</html>
git使用
git stash 非常好用,但是一个不注意代码就没了,很可怕。
这里要注意的是git stash 之后 切出去分支,再切回来,然后没有第一时间使用恢复stash 而是选择git pull ,覆盖代码,这样再git stash list 就看不到上次提交的记录了。
这时使用git stash pop就可以了。但是要注意的是这种场景下的git stash pop好像只能恢复文件中更改的代码,新增的文件不会缓存,git stash pop之后新增的文件就没有了。巨坑!!!!
html使用import
路由懒加载
使用@loadable/component
也可以 import {Suspense,lazy} from ‘react’
Suspense,配合 lazy使用
基于react写一个滚动事件(滚动懒加载和滚动到顶部表头固定)
listenDomClick = () => {
const {head_fixed} = this.state;
if (this.state.active_tab != '3') {
scrollTop = 0;
document.getElementById('win-main').removeEventListener('scroll', this.listenDomClick); //移除滚动事件监听
} else {
document.getElementById('win-main').addEventListener('scroll', this.listenDomClick); //添加滚动事件监听
// 监听表头
if (!document.getElementById('table-head')) return false;
if ((!head_fixed) && (document.getElementById('table-head').offsetTop < document.getElementById('win-main').scrollTop ||
document.getElementById('table-head').offsetTop == document.getElementById('win-main').scrollTop)) {
// 如果表头滚动到头了
this.setState({head_fixed: true})
} else if (head_fixed && document.getElementById('table-head').offsetTop > document.getElementById('win-main').scrollTop) {
this.setState({head_fixed: false})
}
let clientHeight = document.documentElement.clientHeight || document.body.clientHeight; //窗口高度
let scrollHeight = document.getElementById('win-main').scrollHeight; //滚动条的总高度
if (scrollTop - document.getElementById('win-main').scrollTop > 0) return; // 向上滚动,不做判断
scrollTop = document.getElementById('win-main').scrollTop; //滚动区域到头部的距离
if ((Math.ceil(clientHeight) + Math.ceil(scrollTop) + 100 > scrollHeight || Math.ceil(clientHeight) + Math.ceil(scrollTop) + 100 == scrollHeight) && flag) {
flag = false;
timer = setTimeout(async () => {
let res = await this.IncomeAnalysisService.getList({reqPageNum: this.state.pageNum + 1, number: 10});
this.setState({dataSource: [...this.state.dataSource, ...res.data.list], pageNum: this.state.pageNum + 1});
clearInterval(timer);
flag = true; //规定时间到达后,将开关设置为true
}, 300)
}
}
}
About Babel
Babel是一个工具集,主要用于将ES6版本的JS代码转为ES5等向后兼容的JS代码,从而可以运行在低版本浏览器或其它环境中。
作用:语法转换,补齐API(提供垫片:polyfill,垫平差异)
向已有react 项目中引入ts
网上大多数都是从头开始创建一个使用ts的react 项目,这对已经成型的项目来说并不友好。
步骤:
(1) 命令行安装ts
npm install --save typescript @types/node @types/react @types/react-dom @types/jest
(2) 创建tsconfig.json配置文件,并进行配置(我的项目配置,可以针对自己代码习惯做配置)
{
"compilerOptions":{
"noImplicitAny": false, // 不需要显示地声明变量的类型any
"target": "es5", // 定义了编译后的js目标版本,可选项ES5,ES6/ES2015,ES2016,ES2017,ES2018,ES2019,ES2020,ESNext
"lib": [ // 列出了所有编译期间需要包含进来的库文件,通过这些库文件我们能够告诉ts编译器哪些功能能够使用
"dom", // document.getElementById("root")
"dom.iterable",
"esnext"
],
"allowJs": true,// 允许混合编译JavaScript文件
"skipLibCheck": true,
"esModuleInterop": true, // 允许我们使用commonjs的方式import默认文件, import React from 'react';
// "esModuleInterop": false, // import * as React from 'react';
"allowSyntheticDefaultImports": true,
"strict": true,
"forceConsistentCasingInFileNames": true,
"noFallthroughCasesInSwitch": true,
"module": "esnext", // 配置的是代码的模块系统,Node.js的CommonJS、ES6标准的esnext、requireJS的AMD
"moduleResolution": "node", // 决定了编译器的工作方式,
"resolveJsonModule": true,// "resolveJsonModule"如果为false,那么将无法识别.json后缀名文件.而且它必须和"moduleResolution"::"node"连用
"isolatedModules": true, // 编译器会将每个文件作为单独的模块来使用
"noEmit": true, // 表示当发生错误的时候,编译器不要生成JavaScript代码
"jsx": "react-jsx" // 允许编译器支持编译react代码,
}
}
(3) 将app.js index.js 改为tsx格式。这里可能会遇到报错
- Could not find a required file.
Name: index.js
找不到index.js文件,是因为你把后缀改成了tsx格式。
【解决方式】
在package.json下添加 : “main”: “src/index.tsx” 告诉他找的是Index.tsx文件而不是Index.js文件
- An import path cannot end with a ‘.ts’ extension.
引入app.tsx时报错,【解决方式】引入时直接引入src/app即可,将后缀名去掉。 - index.tsx 中报错 Argument of type ‘HTMLElement | null‘ is not assignable to parameter of type ‘HTMLElement‘.
这就是ts的语法问题了,但如果你对ts还不了解,想要启动项目,那就做以下处理:
【解决】使用类型断言(Type Assertion)
const elem : HTMLElement = document.getElementById(‘root’) as HTMLElement;
以上都配置完之后你的react项目就可以在使用js的基础上使用ts了。
启动任务时端口号占用了怎么办
我这里没有 用react的脚手架,所以是node 启动
"start": "set PORT=9002 && node scripts/start.js",
axios使用delete 的点
如果使用axios的delete方法调用接口成功但是没有删除数据,那十分有可能是因为axios 的delete的传参与post 的传参不同,一定要注意啊~~~~~呜呜呜呜
不同点:
如果服务端将参数当做 对象
var param={name:'jack',age:20}
axios
.delete("/test", {data: param})
.then(function(response) {
}
如果服务端将参数当做url 参数 接收
l例如:http:www.XXX.com?a=…&b=…
var param={name:'jack',age:20}
axios
.delete("/test", {params: param})
.then(function(response) {
}
less 与calc
less中常规使用calc函数不生效,less的运算方式和calc起了冲突,写法改为(这里注意空格也要加)
calc(~“100% - 250px”)