- 👋 Hi, I’m @ASkyBig
- 👀 I’m interested in basketball and dota1.
- 🌱 I’m currently learning low code.
- 📫 How to reach me empzd2s4MTg=
knowledge_point's Introduction
knowledge_point's People
knowledge_point's Issues
vue cli 学习
vue.config.js 里面的代理配置,需要重启服务才行,调试了半天。。。
react 小结
setState
会触发渲染,有的时候你的数据没有改变,但是只要调用了setState
,就会重新渲染,浪费性能。所以就有了shouldComponentUpdate
生命周期进行props
和state
的对比,但是每次都要写就很麻烦,于是有了 PureComponent,自动帮你做了。后来,又出了函数组件,没有那些生命周期了,就增加了React.memo (<your function component>)
来完成shouldComponentUpdate
要做的事情。
useEffect(() => {
handleArticleClick(bookList[0].bookId, bookList[0].chapterCount)
}, [props.bookList])
useEffect(() => {
const currentCount = countRef.current
Axios.$get(`/api/v1/write/book/list?time=${Date.now()}`)
.then(res => {
console.log('currentCount', currentCount)
console.log('countRef', countRef)
if (currentCount !== countRef.current) {
return
}
if (res.data.code === 0 && !!res.data.data) {
let tempList = res.data.data.bookList
setBookList(tempList)
const bookItem = getBookItemById(bookId, tempList)
if (bookItem) {
setBookItemInfo(bookItem)
setChapterCount(bookItem.chapterCount)
}
}
})
return () => {
console.log('unmount')
countRef.current += 1
}
}, [bookId])
function handleArticleClick(bookId, chapterCount) {
setBookId(bookId)
}
node
1、http.createServer 会响应两次
因为浏览器默认一次会请求 favicon.ico,添加下面代码就行:
if (req.url === '/favicon.ico') {
return;
}
2、Access-Control-Allow-Origin 设置多域名
if (req.headers.origin === 'http://localhost:2999' || req.headers.origin === 'http://localhost:3000') {
res.writeHead(200, {'content-Type':'text/html;charset=UTF-8', 'Access-Control-Allow-Origin': '*'})
}
3、遍历获取文件名和大小
const fs = require('fs');
const path = require('path');
const getAllFileName = (rootName, arr = []) => {
const fileNameArr = fs.readdirSync(rootName);
fileNameArr.forEach(fileName => {
const fullPath = path.join(rootName, fileName);
const stat = fs.statSync(fullPath);
if (stat.isDirectory()) {
getAllFileName(fullPath, arr)
} else {
arr.push({pathName: fullPath, fileSize: stat.size});
}
})
return arr;
}
const res = getAllFileName('./src/pages/analysis')
console.log('res:', res);
4、创建多级文件夹
const enhanceMkdirsSync = dirname => {
if (fs.existsSync(dirname)) {
return true;
} else {
if (enhanceMkdirsSync(path.dirname(dirname))) {
fs.mkdirSync(dirname);
return true;
}
}
}
5、~ 和 ^ 的区别
antd 使用
最近从Element UI转到了Antd
1、modal 隐藏 确定
和 取消按钮
<Modal
title="查看导出记录"
visible={false}
onOk={() => {console.log('ok')}}
onCancel={() => {
console.log('cancel')
setRecordShow(false)
} }
footer={
[] // 设置footer为空,去掉 取消 确定默认按钮
}
>
2、datepick 只能选择当天
// 禁止选择今天之前的日期
const disabledDate = useCallback((current) => {
// 这里不能用 <=,你用=就不能选择今天了
return current && current < moment().startOf('day').hours(0).minutes(0).seconds(0)
}, [])
3、rangerpicker 作为受控组件,可以重置它的value
const [startTime, setStartTime] = useState(null)
const [endTime, setEndTime] = useState(null)
<RangePicker
showTime={{ format: 'HH:mm' }}
format="YYYY-MM-DD"
value={[startTime, endTime]}
onChange={(value) => {
// value 是个数组,清空后需要将起始结束时间置空
if (value && value.length === 0) {
setStartTime(null)
setEndTime(null)
} else {
setStartTime(moment(value[0].format('YYYY-MM-DD')))
setEndTime(moment(value[1].format('YYYY-MM-DD')))
}
}}
onOk={([start, end]) => {
setStartTime(moment(new Date(start).getTime()))
setEndTime(moment(new Date(end).getTime()))
}}
/>
<Button
onClick={() => {
setStartTime(null)
setEndTime(null)
}}
>
重置
</Button>
4、antd 的 table 的rowkey一定要唯一,否则切换页码的时候会发生旧数据附加到请求的数据上
就是下面的这个 rowKey
有问题,cbid 会有重复的:
可以看到,请求的数据是10条,但是展示的不止10条,这个bug找了好久。
5、select
组件,如果 value
设置为空,那么 placeholder
不会显示,需要设置成 undefined
。
<Select value={ruleArr[index].type === '' ? undefined : ruleArr[index].type} placeholder={'标签类型'} style={{width: '20%'}} disabled={isPlatDisabled} onChange={handlePlatChange}>
{
platOptions.map(item => <Option value={item.value}>{item.label}</Option>)
}
</Select>
5、表格添加序号
const columns = [
{
title: '序号',
key: 'index',
render: (text, record, index) => index + 1,
}
]
6、覆盖组件样式
产品小姐姐说antd自带的跳转至某页的input太小了,显示不全:
所以需要覆盖样式,本来想自适应宽度,但是要监听输入的内容,会有重复渲染的问题,还是直接用一个宽度覆盖原样式:
7、表格列筛选条件
拿到请求结果后筛选出该列所有的情况,去重,设置成 {text, value}
的形式
const uniqueKeyArr = [...new Set(data.map(item => item.action_type))]
const actionFilterArr = uniqueKeyArr.map(item => ({text: item, value: item}))
// table column
{
title: '行为',
dataIndex: 'action_type',
width: '15%',
filters: actionFilterArr,
onFilter: (value, record) => record.action_type === value,
},
8、table 和 select 自定义空态
// table
<Table
columns={columns}
dataSource={dataSource}
locale={{ emptyText: '暂无数据' }}
/>
// Select
<Select
notFoundContent='暂无数据'
allowClear
onChange={(value: string) => {
}}
>
{
(groupList || []).map((item: {
id: string | number,
name: string | number
}) => {
return <Option key={item.id} value={item.id}>{item.name}</Option>
})
}
</Select>
9、antd 组件的英文切换为中文
import { ConfigProvider } from 'antd';
import zhCN from 'antd/es/locale/zh_CN';
return (
<ConfigProvider locale={zhCN}>
<App />
</ConfigProvider>
10、 message 默认延时 3s,可以自定义延时,如果设置为 0 则不会自动消失。
今天发生了一个问题,上传某个apk,产品说还没有成功loading就消失了,我看下了接口16s。。。我这边没有发生这个问题是因为我连接的有线,在loading的默认时间之前接口就好了,所以没有复现,所以要加上 0 这个参数
git 常用命令
- 新建分支并关联到远程分支
如果远程新建了一个分支,本地没有该分支,可以利用git checkout --track origin/branch_name
,这时本地会新建一个分支名叫 branch_name ,会自动跟踪远程的同名分支 branch_name。
- 如果 readme 里面的 gif 过大,无法显示,并报错
Content length exceeded
,则使用img
标签的形式就可以了。例子
远程分支不存在,本地新建分支并推送到远程
- 本地 git checkout -b branch_name 之后,推送到远程,git push --set-upstream origin branch_name:branch_name ,这两个branch_name 之间不能有空格,否则会报
error: dst ref xxx from more than one src
已经提交的文件需要 ignore
例如,gitlab
提交了 vscode
文件夹,本地不想删除,但是仓库也不想继续跟踪了:
https://segmentfault.com/q/1010000000430426
git rm -r --cached .vscode
更新 .gitignore 忽略掉目标文件
git commit -m "We really don't want Git to track this anymore!"
git push
重命名本地分支
git branch -m [old] [new]
查看某分支的父级
git reflog show [branch_name]
// or
git reflog <branch_name>
node 获取最近的提交分支及信息
const { execSync } = require('child_process');
const info = execSync('git log --pretty=format:"%an|%h|%s" -1').toString().trim().split('|');
const branchName = execSync('git rev-parse --abbrev-ref HEAD').toString().trim();
�恢复删除的分支
- 使用 git log -g 找回之前提交的 commit
- 使用 git branch recover_branch [新分支] commit_id 命令用这个 commit 创建一个分支
git branch recover_branch_abc 12345xxx
git branch -a
* develop
recover_branch_abc
remotes/origin-dev/develop
- 切换到 recover_branch_abc 分支
Failed to connect to github.com port 443: Operation timed out
无法拉取远程分支,看看是否是代理的问题,去 /etc/hosts 文件里注释掉GitHub的代理
git 提交规范
使用 [commitlint](https://github.com/conventional-changelog/commitlint) 工具,常用有以下几种类型:
- feat :新功能
- fix :修复 bug
- chore :对构建或者辅助工具的更改
- refactor :既不是修复 bug 也不是添加新功能的代码更改
- style :不影响代码含义的更改 (例如空格、格式化、少了分号)
- docs :只是文档的更改
- perf :提高性能的代码更改
- revert :撤回提交
- test :添加或修正测试
举例
git commit -m 'feat: add list'
撤销本地 commit
git reset --soft HEAD^ // 撤销commit 代码改变仍然保留
或
git reset --soft HEAD~1
- --mixed:表示不删除工作控件改动过的代码,撤销 commit,并且撤销 git add . 操作,这个为默认参数(git reset --mixed HEAD^ 和 git reset HEAD^ 操作效果一样)
- --soft:表示不删除工作空间代码,撤销 commit,保留
git add .
操作 - --hard:表示删除工作空间代码,撤销 commit,撤销
git add .
操作,在完成这个操作之后恢复到上一次 commit 的状态(丢弃改动的代码)
CSS
-
vertical-align 起作用的前提是元素为 inline 水平元素或 table-cell 元素,包括 span, img,input, button, td 以及通过 display 改变了显示水平为 inline 水平或者 table-cell 的元素。这也意味着,默认情况下,div, p 等元素设置 vertical-align 无效
-
绝对定位下
margin: 0 auto
不能居中 -
transform: translateZ (0)
可以用来开启硬件加速 。 -
transform
如果和animation
连用,animation
里面translate
会覆盖transform
里的translate
属性。所以如果我们的全局loading
如果是animation
动画的,要垂直居中就要写在animation
里面。
// styled component
const PageLoadingBox = styled.div`
position: fixed;
top: 0;
left: 0;
width: 100vw;
height: 100vh;
background-color: rgba(143, 151, 173, 0.2);
`
const ImgRotate = keyframes`
0% {
transform: translate(-50%, -50%) rotate(0deg);
}
100% {
transform: translate(-50%, -50%) rotate(360deg);
}
`
const ImageBox = styled.img`
position: absolute;
top: 50%;
left: 50%;
width: 200px;
height: 200px;
animation: 2s ${ImgRotate} infinite linear;
`
Css module 作者建议:
(1). 不使用选择器,只使用 class 名来定义样式
(2). 不层叠多个 class,只使用一个 class 把所有样式定义好
(3). 不嵌套
(4). 使用 composes 组合来实现复用
.demo::-webkit-scrollbar {
display: none; /* Chrome Safari */
}
.demo {
scrollbar-width: none; /* firefox */
-ms-overflow-style: none; /* IE 10+ */
overflow-x: hidden;
overflow-y: auto;
}
话萌APP开发总结
1、自己开发的模块中的调试信息例如 debugger
和 console.log
等提交到 git 之前要删掉,否则对其他人来说是个干扰。还会影响性能。
2、作为前端,要尽早让后端把 mock 接口和数据写好,否则会阻塞前端进度,并且开发都看不到效果。
3、再说后端数据返回的不对的前提,先看看自己传过去的表单对不对。(很尴尬,发送了一个格式化的时间,后端要的是时间戳==! )
- 有没有漏字段
- 字段类型是不是和约定的一致
4、1 > undefined 结果为 false
5、代码提交需要遵循一个规范
6、Object.assign 要注意不同对象中的同名字段
今天遇到的一个 bug,一个 model 对象合并两个数据对象,一个是该条记录的 id, 一个是显示 index 的 id,一个合并就把该条记录 id 覆盖了。
如果有同名字段就要注意了,下面两种写法 id 就不一样了。
Object.assign(model, data.posInfo, data.advInfo);
Object.assign(model, data.advInfo, data.posInfo);
7、接口不同页面的相同字段应该保持一致
列表页的时间是 timestamp
,那编辑页的时间字段不应该是 onlineTime
,接口评审的时候应该提醒后端同学。
列表页接口:
编辑页接口:
8、for 循环记得带 break
,减少遍历次数,还有注意里面的异步方法可能导致的闭包问题。
for (var i=1; i<=5; i++) {
if( i === 3) break;
setTimeout( function timer() {
console.log( i );
}, i*1000 );
} // 3, 3
9、如果前端一条请求依赖后端多个接口,不需要后端作为中间层统一调完给我们,直接分开请求,避免中间层多个请求的最长路径。
10、控制台 console
菜单里面的 filter
过滤输入框是实时的,如果你发现输入的内容没有打印出来,看看是不是 filter
里面不经意输入了啥。
没有输入(空格不影响):
有输入:
11、一些较小的 icon
按钮可以增大点击区域,方便点击。
这不,测试提了一个 bug
:
13、Android
的默认字体是灰色,iOS
是黑色,要让两端统一,记得加颜色。
15、有默认值的用三目运算符,不要用逻辑运算符。
后台管理有一个图片上传功能,然后显示后端返回的图片大小。默认值我设置的是 0。
但是后端 size 返回空的时候,this.width
就变成了 false
。不过讲道理,应该返回大小给我的:D
// 这样不好
this.width = !!data.posInfo.imageSize && data.posInfo.imageSize.match(/\d+/g)[0]; // false && 1 返回 false
this.height = !!data.posInfo.imageSize && data.posInfo.imageSize.match(/\d+/g)[1];
// 这样好
this.width = data.posInfo.imageSize ? data.posInfo.imageSize.match(/\d+/g)[0] : 0;
this.height = data.posInfo.imageSize ? data.posInfo.imageSize.match(/\d+/g)[1] : 0;
16、setHours() 方法用于设置指定的时间的小时字段。比如,展示某些列表记录的时间,你从后台获得时间戳,看它是 一天前
还是 两天前
还是 一年前
:
let timestampFromBackend = new Date(new Date().getTime() - 86400 * 1000 -1).getTime()
new Date(timestampFromBackend).setHours(0, 0, 0, 0) // 这就是这个时间戳的当天零点
代码片段
css 旋转裁剪图片
<View style='padding-bottom: 18px; width: 88px; height: 93px; transform: rotate(10deg); overflow: hidden;'>
<Image style='width: 88px;height: 93px;border-radius: 20px; padding-top: 19px; padding-left: 137px;margin-left: -129px; transform: rotate(-10deg);'
src={banner3}
/>
</View>
<View style="position: absolute;margin-left: 97px;padding-right: 112px;padding-top: 14px;overflow: hidden;padding-bottom: 18px;height: 93px;width: 88px;transform: rotate(10deg);">
<Image style='transform: rotate(-10deg);width: 88px;height: 93px;border-radius: 20px; padding-bottom: 15px;margin-left: -149px;padding-left: 138px;padding-top: 19px;'
src={banner2}
/>
</View>
function DateFormate (time, fmt) {
var o = {
"M+" : time.getMonth()+1, //月份
"d+" : time.getDate(), //日
"h+" : time.getHours(), //小时
"m+" : time.getMinutes(), //分
"s+" : time.getSeconds(), //秒
"q+" : Math.floor((time.getMonth()+3)/3), //季度
"S" : time.getMilliseconds() //毫秒
};
if(/(y+)/.test(fmt)){
fmt=fmt.replace(RegExp.$1, (time.getFullYear()+"").substr(4 - RegExp.$1.length));
}
for(var k in o){
if(new RegExp("("+ k +")").test(fmt)){
fmt = fmt.replace(
RegExp.$1, (RegExp.$1.length==1) ? (o[k]) : (("00"+ o[k]).substr((""+ o[k]).length)));
}
}
return fmt;
}
DateFormate(new Date(), "yyyy-MM-dd hh:ss") // "2019-10-30 19:21"
图片大小尺寸前端验证
handlePreview (file) {
if (!file) {
return
}
const img = new Image()
img.src = URL.createObjectURL(file.raw)
img.onload = (e) => {
if (e.target.width !== 1000 || e.target.height !== 280) {
this.$message.error('图片尺寸或大小错误,请重新上传');
return;
}
if (file.size > 150 * 1024) {
this.$message.error('图片尺寸或大小错误,请重新上传');
return;
}
uploadImg(file.raw, '/upload/image').then((result) => {
console.log('result', result)
this.imageUrl = result.data.data.url;
})
}
}
转义字符
const escapeHTML = str =>
str.replace(
/[&<>'"]/g,
tag =>({'&': '&', '<': '<', '>': '>', "'": ''', '"': '"' }[tag] || tag)
);
全角转半角
let newStr = ''
for (let i = 0; i < strTelNum.length; i++) {
let code = strTelNum.charCodeAt(i)
if (code >= 65281 && code <= 65373) {
newStr += String.fromCharCode(strTelNum.charCodeAt(i) - 65248)
} else if (code == 12288) {
newStr += String.fromCharCode(strTelNum.charCodeAt(i) - 12288 + 32)
} else {
newStr += strTelNum.charAt(i)
}
}
字符串数字相加
由于精度限制,两个数字相加会有问题。比如我们公司的 BookId 就很长,超过了 2^53 - 1。
var bigAdd = function(str1, str2) {
let arr1 = str1.length > str2.length ? str1.split('').map(Number) : str2.split('').map(Number)
let arr2 = str1.length > str2.length ? str2.split('').map(Number) : str1.split('').map(Number)
let len = arr1.length
let temp_arr = Array(len - arr2.length).fill(0).concat(arr2)
let res = []
let flag = false // 标记是否有进位
for(let i = len - 1; i >= 0; i--) {
let val = arr1[i] + temp_arr[i]
if (flag) {
if (i === 0) {
if ( val + 1 >= 10) {
res.unshift(val + 1 - 10)
res.unshift(1)
} else {
res.unshift(val + 1)
}
} else {
if ( val + 1 >= 10) {
res.unshift(val + 1 - 10)
flag = true
} else {
res.unshift(val + 1)
flag = false
}
}
} else {
if (i === 0) {
if (val >= 10) {
res.unshift(val - 10)
res.unshift(1)
} else {
res.unshift(val)
}
} else {
if (val >= 10) {
res.unshift(val - 10)
flag = true
} else {
res.unshift(val)
flag = false
}
}
}
}
return res.join('')
};
bigAdd('9007199254740992', '9') // '9007199254741001'
readme 目录结构代码方式
├── README.md
├── dist/
├── package.json
├── src
│ ├── app.js
│ ├── assets // 全局资源
│ ├── components/ // 全局通用组件
│ ├── global.less // 全局样式
│ ├── layouts/ // 全局模版
│ ├── pages // 页面
│ ├── styles
│ │ └── _var.less
│ └── utils
│ └── request.js // 请求的简易封装
│ └── tools.js // 工具类方法
└── webpack.config.js
设置当天的cookie
function setCurDayCookie(name, value) {
var curDate = new Date(); // Sat Sep 18 2021 13:55:02 GMT+0800 (**标准时间)
var curTime = curDate.getTime(); // 1631944649281
var curDay = curDate.toLocaleDateString(); // 2021/9/18
var curNearNextDayTime = new Date(curDay).getTime() - 1; // 当天零点开始的时间戳,-1 是防止 00:00:00
var todayPassedTime = curTime - curNearNextDayTime; // 今天已经过去的时间
var leftTimestamp = 24 * 60 * 60 * 1000 - todayPassedTime; // 今天剩下的时间
var timeSet = curTime + leftTimestamp; // 可以缓存的时间的时间戳
document.cookie = name + "=" + value + ";expires=" + new Date(timeSet).toUTCString() + ";path=/";
}
或者
// 获取用户本地剩余时间(时间戳)
var leftTime = ((new Date()).getTime()) % (86400000);
// 计算用户今天还剩下多少时间(时间戳)
var cookieTime = 86400000 - leftTime;
docker 知识
最近做一个内部项目,没有后端,得自己搞,正在学习 docker。
docker-compose
Docker Compose 是一个用来定义和运行复杂应用的 docker 工具。
可以通过使用 Compose 在一个文件中定义一个多容器应用,使用 docker-compose up 命令来启动应用。
docker-compose up -d
up
参数
构建,(重新)创建,启动,链接一个服务相关的容器。
链接的服务都将会启动,除非他们已经运行。
默认情况, docker-compose up
将会整合所有容器的输出,并且退出时,所有容器将会停止。
如果使用 docker-compose up -d
,将会在后台启动并运行所有的容器。
默认情况,如果该服务的容器已经存在, docker-compose up
将会停止并尝试重新创建他们(保持使用 volumes-from
挂载的卷),以保证 docker-compose.yml
的修改生效。如果你不想容器被停止并重新创建,可以使用 docker-compose up --no-recreate
。如果需要的话,这样将会启动已经停止的容器。
mysql 命令
1、查看所有的表
show tables;
2、查看某一个表
select * from tablename;
3、查看表列名
show columns from tablename;
4、查看表某列的全部信息
select columnname from tablename;
5、创建一个表
create table image(
-> id INT NOT NULL AUTO_INCREMENT,
-> name varchar(255) not null,
-> type int not null,
-> height double not null,
-> width double not null,
-> metadata text not null,
-> url varchar(255) not null,
-> is_deleted tinyint(4),
-> parent_image_id int not null,
-> PRIMARY KEY(id)
-> )ENGINE=InnoDB DEFAULT CHARSET=utf8;
6、向表中增加一列
alter table tablename add column columnname varchar(255) not null;
7、查询表中某列数据
select columnname from tablename;
8、更新某列数据
update tablename set columnname='1080x375';
9、修改字段名
字段的类型要有,不然就会报错:
alter table [tablename] change [cloumnname] [newColumnname] [columnType];
10、删除某个字段
alter table [tablename] drop [fieldname];
11、删除多个字段
alter table [tablename] drop [columnname1], drop [columnname2]
12、增加多个字段
alter table [tablename] add [columnname1] [type], add [columnname2] [type];
设计理念
photoShop
你不知道的 js 知识点
小时候我的成绩相当好:)考试我有一个经验,和别人拉开差距的地方就是一些不常用的知识点。常用的只要你不是特别笨都会做。写代码应该也是一样的,我要把一些大家容易忽略的记录下来,扎实我的基本功:)看到了就要条件反射想到这些东西
parseInt
parseInt () 函数可解析一个字符串,并返回一个整数。如果第二个参数小于 2 或者大于 36,则 parseInt () 将返回 NaN。这个反正我没有用过。
这个常见的用法就是parseInt('16px') ,拿到数字16。
要记住两点:
-
开头和结尾的空格是允许的。
parseInt(' 16px ')
也是可以的,不需要trim
字符串 -
如果它以 “0x” 或 “0X” 开头,将以 16 为基数。我们在使用时,要考虑字符串有没有可能出现这种情况
parseInt(' 0xA ')
返回 10
判断是不是数组
大多数人只会两种方式,我们也要知道另外两种方式
1、Array.isArray(obj),这是最常用的
2、Object.prototype.toString.call(arrObj) === '[object Array]',这个用的也很多,一般用在工具方法中
3、Array.prototype.isPrototypeOf (obj),这个基本见不到,检测一个对象是否存在于另一个对象的原型链中
4、arrObj.constructor === Array,这个也不常用,这个构造函数其实不在arrObj上,而是在 Arrry.prototype 上
浏览器知识
- 如果下载 CSS 文件阻塞了,会阻塞 DOM 树的合成吗?会阻塞页面的显示吗?
当从服务器接收 HTML 页面的第一批数据时,DOM 解析器就开始工作了,在解析过程中,如果遇到了 JS 脚本,如下所示:
<html>
<body>
极客时间
<script>
document.write("--foo")
</script>
</body>
</html>
那么 DOM 解析器会先执行 JavaScript 脚本,执行完成之后,再继续往下解析。
那么第二种情况复杂点了,我们内联的脚本替换成 js 外部文件,如下所示:
<html>
<body>
极客时间
<script type="text/javascript" src="foo.js"></script>
</body>
</html>
这种情况下,当解析到 JavaScript 的时候,会先暂停 DOM 解析,并下载 foo.js 文件,下载完成之后执行该段 JS 文件,然后再继续往下解析 DOM。这就是 JavaScript 文件为什么会阻塞 DOM 渲染。
我们再看第三种情况,还是看下面代码:
<html>
<head>
<style type="text/css" src = "theme.css" />
</head>
<body>
<p> 极客时间 </p>
<script>
let e = document.getElementsByTagName('p')[0]
e.style.color = 'blue'
</script>
</body>
</html>
当我在 JavaScript 中访问了某个元素的样式,那么这时候就需要等待这个样式被下载完成才能继续往下执行,所以在这种情况下,CSS 也会阻塞 DOM 的解析。
所以 JS 和 CSS 都有可能会阻塞 DOM 解析。
js 随手记
Array.prototype.slice()
- MDN上的slice()方法介绍:Array.prototype.slice() 方法返回一个新的数组对象,这一对象是一个由 begin和 end(不包括end)决定的原数组的浅拷贝。原始数组不会被改变。
原数组不会改变应该针对简单类型:
var arr = [1, 2, 3];
var arr_new = arr.slice(1) // arr_new: [2, 3], arr: [1, 2, 3]
arr_new[0] = 0; // arr_new: [0,3], arr: [1, 2, 3]
要是是个复杂类型就会改变:
var arr = [{a: 1}, {b: 2}, {c: 3}]
var arr_new = arr.slice(1) // arr_new: [{b: 2}, {c: 3}], arr: [{a: 1}, {b: 2}, {c: 3}]
arr_new[0].b = 0; // arr_new: [{b: 0}, {c: 3}], arr: [{a: 1}, {b: 0}, {c: 3}]
JSON.parse(JSON.stringify()) 实现深拷贝要小心呐
var target = {
a: 1,
b: 2,
hello: function() {
console.log("Hello, world!");
}
};
var target_new = JSON.parse(JSON.stringify(target));
console.log(target_new); // {a: 1, b: 2}
判断当前浏览器是否支持webP格式
function checkWebp() {
try {
return document.createElement('canvas').toDataURL('image/webp').indexOf('data:image/webp') == 0
}
catch(err) {
return false
}
}
类不存在变量提升(hoist),这一点与 ES5 完全不同。 ES6 不会把类的声明提升到代码头部。这种规定的原因与继承有关,必须保证子类在父类之后定义。
子类必须在constructor方法中调用super方法,否则新建实例时会报错。这是因为子类自己的this对象,必须先通过父类的构造函数完成塑造,得到与父类同样的实例属性和方法,然后再对其进行加工,加上子类自己的实例属性和方法。如果不调用super方法,子类就得不到this对象。
数组一维扁平化
function flatten(arr) {
return arr.reduce((result, item) => {
return result.concat(Array.isArray(item) ? flatten(item) :item;
}, [])
}
判断空对象
JSON.stringify(obj) === '{}'
for in 是遍历对象的可枚举属性,两点:1、可枚举,2、属性,不是属性值。
chrome 控制台输出的数据,你可用 copy(data)
复制到剪切板
js map 方法不会对空数组进行检测,也不会改变原数组。
var arr =[];
arr[1] = 2;
arr[2] = 3;
var newArr = arr.map(value => ++value); // newArr: [empty, 3, 4], arr: [empty, 2, 3]
距今天多久的代码段
const timeFormat1 = ( timestamp ) => {
let nowTime = new Date().getTime()
let duration = (nowTime - timestamp) / 1000
let result
switch (true) {
case duration <= 60: {
result = '刚刚'
break
}
case duration <= 60 * 60: {
let time = Math.ceil(+`${duration / 60}`)
result = `${time}分钟前`
break
}
case duration <= 60 * 60 * 24: {
let time = Math.ceil(+`${duration / 60 / 60}`)
result = `${time}小时前`
break
}
case duration <= 60 * 60 * 24 * 30: {
let time = Math.ceil(+`${duration / 60 / 60 / 24}`)
result = `${time}天前`
break
}
case duration <= 60 * 60 * 24 * 30 * 12: {
let time = Math.ceil(+`${duration / 60 / 60 / 24 / 30}`)
result = `${time}个月前`
break
}
default: {
let time = Math.ceil(+`${duration / 60 / 60 / 24 / 30 / 12}`)
result = `${time}年前`
}
}
return result
}
const timeFormat2 = ( timestamp ) => {
let nowTime = new Date().getTime()
let todayBeginTime = new Date().setHours(0,0,0,0) // 今天零点的时间戳
let yesTime = nowTime - 1000 * 60 * 60 *24 // 获取昨天这个时候的时间
let yesBeginTime = new Date(yesTime).setHours(0,0,0,0) // 昨天零点的时间戳
let beforeYesTime = new Date(nowTime) - 1000 * 60 * 60 * 24 * 2 // 获取前天这个时候的时间
let beforeYesBeginTime = new Date(beforeYesTime).setHours(0,0,0,0) // 前天零点的时间戳
let thisYearTime = new Date(new Date().getFullYear() + '-01-01').getTime() // 今年的一月一号时间戳
let duration = (nowTime - timestamp) / 1000
let result
switch (true) {
case duration < 60: {
result = '刚刚'
break
}
case duration < 60 * 60: {
let time = Math.ceil(+`${duration / 60}`)
result = `${time}分钟前`
break
}
case timestamp > todayBeginTime: {
let hour = new Date(timestamp).getHours()
let min = new Date(timestamp).getMinutes()
result = `今天 ${hour > 9 ? hour : `0${hour}`}:${min > 9 ? min : `0${min}`}`
break
}
case timestamp > yesBeginTime: {
let hour = new Date(timestamp).getHours()
let min = new Date(timestamp).getMinutes()
result = `昨天 ${hour > 9 ? hour : `0${hour}`}:${min > 9 ? min : `0${min}`}`
break
}
case timestamp > beforeYesBeginTime: {
let hour = new Date(timestamp).getHours()
let min = new Date(timestamp).getMinutes()
result = `前天 ${hour > 9 ? hour : `0${hour}`}:${min > 9 ? min : `0${min}`}`
break
}
case timestamp > thisYearTime: {
let mon = new Date(timestamp).getMonth() + 1
let date = new Date(timestamp).getDate()
let hour = new Date(timestamp).getHours()
let min = new Date(timestamp).getMinutes()
result = `${mon > 9 ? mon : `0${mon}`}-${date > 9 ? date : `0${date}`} ${hour > 9 ? hour : `0${hour}`}:${min > 9 ? min : `0${min}`}`
break
}
default: {
let year = new Date(timestamp).getFullYear()
let mon = new Date(timestamp).getMonth() + 1
let date = new Date(timestamp).getDate()
result = `${year}-${mon > 9 ? mon : `0${mon}`}-${date > 9 ? date : `0${date}`}`
}
}
return result
}
12、hash 属性是一个可读可写的字符串,该字符串是 URL 的锚部分(从 # 号开始的部分)
13、npm install -g 的安装目录 user/local/lib/node_modules
14、后端返回的 json 中的整型数据到 js 会丢失精度,记得用 bigInt 或者字符串
15、for in 和 for of
let aArray = ['a',123,{a:'1',b:'2'}]
for(let index in aArray){
console.log(`${aArray[index]}`);
}
for(var value of aArray){
console.log(value);
}
aArray.name = 'demo'
for(let index in aArray){
console.log(`${aArray[index]}`);
}
for(var value of aArray){
console.log(value);
}
16、清空数组的小技巧
let arr = [1, 2, 3]
arr.length = 0
// arr: []
17、map 和 foreach 的区别
- map 依次执行后会返回一个数组
- foreach 只会执行回调,并返回 undefined
18、setTimeout 有好多参数
一直都用了两个,其实后面可以加参数:
function foo(p1, p2) {
console.log('p1==', p1, 'p2==', p2)
}
setTimeout(foo, 2000, 'a', 'b')
// p1== a p2== b
19、利用 Math.random() 生成 1~16 位随机数字
function randomNum(len) {
return Math.random().toString().substr(2, len) // 2 是因为去掉前面的 '0.'
}
20、驼峰命名和中划线相互转换
// "abcDefGh"
"abc-def-gh".replace(/-\w+/g, item => item.charAt(1).toUpperCase() + item.slice(2))
// "abc-def-gh"
"abcDefGh".replace(/[A-Z]/g, item => '-' + item.toLowerCase())
21、数字千位分隔符
// 12345678 ===> 12,345,678
// way 1
'12345678'.replace(/(?=(\d{3})+$)/g, ',')
// way 2
'12345678'.split('').reverse().join('').replace(/\d{3}/g, item => item + ',').split('').reverse().join('')
// way 3
Number(12345678).toLocaleString()
22、String.raw
// "Hi\n5!"
String.raw`Hi\n${2+3}!`
// "Hi
// 5!"
`Hi\n${2+3}!`
23、不要直接用 obj.hasOwnProperty([property])
// 如果对象里面有这个属性则会报错
var obj = { hasOwnProperty: 1, a: 1 }
obj.hasOwnProperty('a') // Uncaught TypeError: obj.hasOwnProperty is not a function
如果恶意的客户端传给服务器的 json 里面有类似的字段,则会导致服务奔溃。
正确的方式:Object.prototype.hasOwnProperty.call(obj, 'a') // true
24、console.log({a})可以输出键值对
var a = 1
console.log({a})
// {a: 1}
25、计算字符串的字节
new Blob(['a']).size // 1
new Blob([1, 2, 3]).size // 3
26、setTimeout
浏览器的页面是通过消息队列和事件循环系统来驱动的。settimeout 的函数会被加入到延迟消息队列中,
等到执行完 Task 任务之后就会执行延迟队列中的任务。然后分析几种场景下面的 setimeout 的执行方式。
- 如果执行一个很耗时的任务,会影响延迟消息队列中任务的执行
- 存在嵌套带调用时候,系统会设置最短时间间隔为 4s(超过 5 层)
- 未激活的页面,setTimeout 最小时间间隔为 1000ms
- 延时执行时间的最大值 2^31 - 1 = 2147483647,溢出会导致定时器立即执行
- setTimeout 设置回调函数 this 会是回调时候对应的 this 对象,可以使用箭头函数解决
var name= 1;
var MyObj = {
name: 2,
showName: function(){
console.log(this.name);
}
}
setTimeout(MyObj.showName,1000)
// 方法一 箭头函数
setTimeout(() => {
MyObj.showName()
}, 1000);
//或者function函数
setTimeout(function() {
MyObj.showName();
}, 1000)
// 方法二
setTimeout(MyObj.showName.bind(MyObj), 1000)
27、When will requestAnimationFrame be executed?
28 根据 [0, 7) || (0, 7] 判断星期几
function getWeekDay(n) {
return '星期' + ['日', '一', '二', '三', '四', '五', '六'][n%7]
}
29、循环请求
命令式:
for (let i = 0; i < 5; i++) {
$.post('/api/' + i, data[i])
}
函数式:
[1,2,3,4,5].forEach(item => {
$.post('/api' + item, data[item])
})
30、Unix时间戳和js时间戳不一样
unix是按照秒算的,js的是毫秒,所以会导致后台传过来的时候显示成1970年
31、timestamp format
// 格式化时间
const dateTimeFormat = (time, fmt) => {
const t = new Date(time)
let o = {
"M+": t.getMonth() + 1, //月份
"D+": t.getDate(), //日
"h+": t.getHours(), //小时
"m+": t.getMinutes(), //分
"s+": t.getSeconds(), //秒
"q+": ~~((t.getMonth() + 3) / 3), //季度
"S": t.getMilliseconds() //毫秒
}
if (/(y+)/i.test(fmt)) {
fmt = fmt.replace(RegExp.$1, (t.getFullYear() + "").substr(4 - RegExp.$1.length))
}
for (let k in o) {
if (new RegExp("(" + k + ")").test(fmt))
fmt = fmt.replace(RegExp.$1, (RegExp.$1.length == 1) ? (o[k]) : (("00" + o[k]).substr(("" + o[k]).length)))
}
return fmt
}
32、随机色
const randomColour = () => '#'+(Math.random()*0xFFFFFF<<0).toString(16);
33、判断浏览器是否支持 passive
:
// 检查浏览器是否支持 passive 属性
export let passiveSupported = false;
try {
var options = Object.defineProperty({}, 'passive', {
get: function () {
passiveSupported = true;
return passiveSupported
}
});
window.addEventListener('test', null, options);
} catch (err) {
// do nothing
}
// 设置passive: true 可以提高性能
target.addEventListener(event, handler, passiveSupported
? { passive: true }
: false
);
Object.defineProperty
比如,你需要禁止设置对象某些属性,可以考虑这样:
const arr = ['a', 'b', 'c'];
const obj = {}
arr.forEach(item => {
Object.defineProperty(obj,item, {
get() {
throw new Error('not support')
}
})
})
obj.a; // Uncaught Error: not support
多条件判断可以利用数组:
如果是下面这样的,可扩展性不是很高,你加一个条件需要再 if
里面加 day === xx
:
function test(day) {
if (day === 'Monday' || day === 'Tuesday') {
// do something
}
}
可以改成这样,这样你再扩展时,直接加到数组里面即可:
function test(day) {
const dayArr = ['Monday', 'Tuesday']
if (dayArr.includes(day)) {
// do something
}
}
对象数组去重
利用对象的 key
是唯一的这个特性。
例如,对同名的属性进行去重:
const objArr = [{name: 'axe', age: 20}, {name: 'spe', age: 21}, {name: 'axe', age: 20}]
let res = {}
for (let i = 0; i < objArr.length; i++) {
res[objArr[i]['name']] = objArr[i]
}
res = Object.values(res) // [{name: 'axe', age: 20}, {name: 'spe', age: 21}]
当然,可能是 name
一样,但是 age
不一样,这个时候不想去重:
const objArr = [{name: 'axe', age: 20}, {name: 'spe', age: 21}, {name: 'axe', age: 20}, {name: 'axe', age: 10}]
let res = {}
for (let i = 0; i < objArr.length; i++) {
res[`${objArr[i]['name']}-${objArr[i]['age']}`] = objArr[i]
}
res = Object.values(res)
/*
* [{"name": "axe", "age": 20},
* {"name": "spe", "age": 21},
* {"name": "axe", "age": 10}]
*/
当然,你可能还想把名字相同的放到一起:
const objArr = [{name: 'axe', age: 20}, {name: 'spe', age: 21}, {name: 'axe', age: 20}, {name: 'axe', age: 10}]
let res = {}
for (let i = 0; i < objArr.length; i++) {
res[`${objArr[i]['name']}-${objArr[i]['age']}`] = objArr[i]
}
res = Object.values(res).sort((a, b) => a.name > b.name ? 1 : -1)
/*
* [{"name": "axe", "age": 10},
* {"name": "axe", "age": 20},
* {"name": "spe", "age": 21}]
*/
更进一步,对相同 name
的根据 age
的大小排序:
const objArr = [{name: 'axe', age: 20}, {name: 'spe', age: 21}, {name: 'axe', age: 20}, {name: 'axe', age: 30}, {name: 'axe', age: 5}]
let res = {}
for (let i = 0; i < objArr.length; i++) {
res[`${objArr[i]['name']}-${objArr[i]['age']}`] = objArr[i]
}
const sortFn = (a, b) => {
if (a.name > b.name) return 1
else if (a.name < b.name) return -1
else if (a.age > b.age) return 1
else return -1
}
res = Object.values(res).sort(sortFn)
/*
* [{"name": "axe", "age": 5},
* {"name": "axe", "age": 20},
* {"name": "axe", "age": 30},
* {"name": "spe", "age": 21}]
*/
布尔值取反
1、利用 异或,对于每一个比特位,当两个操作数相应的比特位有且只有一个 1 时,结果为 1,否则为 0。简而言之,相同为 0, 不同为 1.
let isA = true;
isA ^= 1; // 0
isA ^= 1; // 1
2、利用 取反
ture * 1 为 1
let isA = true
isA = !isA*1 // 0
isA = !isA*1 // 1
async 函数
async 函数返回值是一个 Promise,如果要用到里面的返回值,需要 then
来接收:
const foo = async () => {return 1;}
const a = foo(); // Promise {<fulfilled>: 1}
a.then(res => console.log('res ===', res)); // res === 1
保留四位小数,移除多余的 0
toFixed
- 语法:
NumberObject.toFixed(num)
- num默认值为0
- 返回字符串
parseFloat
- 语法: parseFloat(string)
- 字符串中只返回第一个数字
- 开头和结尾的空格是允许的
- 如果字符串的第一个字符不能被转换为数字,那么 parseFloat() 会返回 NaN
- 如果小数点后面有多余的 0,会自动移除,parseFloat('10.00') 返回 10,parseFloat('10.10') 返回 10.1
(Number(text).toFixed(4)).replace(/(\.?0+)$/, '')
// or
parseFloat(Number('123.1020111').toFixed(4))
随机生成中文字符串
// 0x9FA5 和 0x4E00 是十六进制数,分别对应十进制的 40869 和 19968。这两个数值是 Unicode 编码中汉字的起始和结束的编码。
// 0x9FA5 - 0x4E00 这个表达式的结果是汉字在 Unicode 编码中的范围,即 20901。这意味着 Unicode 编码中有 20901 个汉字。
String.fromCharCode(Math.floor(Math.random() * (0x9FA5 - 0x4E00 + 1) ) + 0x4E00)
浏览器兼容性问题
- js new Date 在 iOS端出现 NaN
// 在 ios 端中,使用如下方法会获得 NaN,安卓手机则是正常计算
new Date("2020-04-25 12:00:00").getTime()
// 解决方法:用正则把 '-' 换成 '/' ,让时间格式变成以下格式
const date ="2020/04/25 12:00:00".replace(/-/g,"/")
new Date(date).getTime()
seo 相关问题
最近需要做 seo,先熟悉一下相关名词
SEO 优化中的 TDK 三大标签是什么
- TDK 标签包括标题标签(title)也就是 T,网站的描述标签(description)也就是 D,关键词标签(keywords)也就是 K 等。
- 在优化网站的时候,每个网页都要布局 TDK 标签,如主页,列表,栏目页,文章内容等。
- title 也就是一个网页的名字,要覆盖优化的关键词,高度概括网页的主题内容
nofollow 标签是什么
nofollow 标签是 HTML 页面中 a 标签的属性值,主要是用来告诉搜索引擎不要追踪某个页面或者特定链接,以避免权重的流失。
简而言之,如果 A 网页上的链接指向 B 网页,如果在此链接上添加 nofollow 标签,那么搜索引擎蜘蛛将不会再从 A 网页访问 B 网页,也不会将 A 网页计算为 B 网页的外链,自然也不会分散 A 网页的权重。
react-native
1、rn 在老的机型上崩溃,因为 没有source
issue
初探微信小游戏
HTTP 学习
- HTTPS 是 HTTP Secure,还是 HTTP
- 以共享秘钥方式加密时必须将秘钥也发给对方。如果秘钥可以安全送达,那数据也可以送达,那要秘钥干嘛呢
- HTTP 422 返回码 表示服务器理解请求实体的 content type, 并且请求实体的语法是正确的,但是服务器不能处理所包含的指示。
重要提示:客户端不应在不修改的情况下重复發送此请求。
- Cookie 相同域名不同端口也是可以共享的。
- 如果 Set-Cookie 中有 secure 属性值,那么只有在 https 的时候才会发送 Cookie。
- 状态码 400 是参数类型不一致
echarts 使用
legend 不显示全部数据
const selectObj = {};
topTenBookArr.forEach((item, index) => {
if (index >= 5) {
selectObj[item.bookName] = false
}
})
//...
legend: {
data: topTenBookArr.map(item => item.bookName),
// selected_item.key_name = false; 会报错
// 原因是: json 解析时 key_name 都按字符串解析,不识别变量.
selected: selectObj
},
Mac 快捷键
- 新建一个文件
touch index.js
- 显示隐藏文件
command + shift + .
- 打开
Chrome
控制台
command + option + i
- 打开控制台移动模式
command + shift + m
- 打开 .ssh 文件
open ~/.ssh
- webstorm 代码格式化
command + option + L
- 打开 host 文件
command + shift + g
/etc/hosts
command + option + D
- Mac 终端打开vscode
- 打开控制面板(⇧⌘P)
- 输入 shell command
在提示里看到 Shell Command: Install ‘code’ command in PATH, 就可以了。 - 使用:
code .
- docker 从副屏幕还原到主屏幕
鼠标缓慢滑到主屏幕下面
canvas学习
Linux 命令
-
rz
上传文件,不能是文件夹 -
rm 参数
-i 删除前逐一询问确认。
-f 即使原档案属性设为唯读,亦直接删除,无需逐一确认。
-r 将目录及以下之档案亦逐一删除。
-
uname
uname -a
即uname -all
, 查看系统信息 -
curl cip.cc
查看位置信息
正则
- 数字、字母、汉字5个字之内
/^[\u4e00-\u9fa5a-zA-Z0-9]{1,5}$/.test(str)
- 只能输入数字、-,并且中划线 - 只能是一位,不能在首尾出现,整个长度范围是 5~10 位
/^\d(?!(\d*-\d*){2,}$)(\d|-){3,8}\d$/.test(str)
- 金钱千位符
'12345678'.replace(/(?=(\d{3})+$)/g, ',') // 12,345,678
- html 结构代码替换
// 将 h1 下面的 <br /> 标签移除,不影响其他标签下的 <br />
// 懒惰
"<html><body><h2>我是<br /> h2</h2><h1> sdfsdfs<br/> sdfsfsdf<br/></h1>2343242<h1> sdfsdfs<br/> sdfsfs</h1></body></html>".replace(/<h1>.*?<\/h1>/g, item => {
return item.replace(/<br\s*\/>/g, '')
}) // "<html><body><h2>我是<br /> h2</h2><h1> sdfsdfs sdfsfsdf</h1>2343242<h1> sdfsdfs sdfsfs</h1></body></html>"
- . \s\S \w\W
\s 表示,只要出现空白就匹配
\S 表示,非空白就匹配
\w 匹配包括下划线的任何单词字符。等价于 “[A-Za-z0-9_]"。
\W 匹配任何非单词字符。等价于 “[^A-Za-z0-9_]"。
那么它们的组合,表示所有的都匹配,与它相对应的,有 [\w\W] 等,意义完全相同、
另外要说的一点是,为什么有 "." 这个通配符了,还要这样的用法。
其实,[\s\S] 与 [\w\W] 这样的用法,比 "."所匹配的还要多,因为"." 是不会匹配换行的,所有出现有换行匹配的时候,人们就习惯 使用 [\s\S] 或者 [\w\W] 这样的完全通配模式。
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.