日常开发小技巧

菇太帷i Lv4

数组按名称排序

  1. 经常遇到开发过程中需要对数组中的元素进行按名称排序,可以使用字符串的localeCompare方法
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    let satelliteList = [[
    {
    "name": "JL1GF03B",
    "sensors": []
    },
    {
    "name": "ZY303",
    "sensors": []
    },
    {
    "name": "JL1GF04A",
    "sensors": []
    },
    {
    "name": "GF7",
    "sensors": []
    },
    {
    "name": "JL1GF03D03",
    "sensors": []
    },
    {
    "name": "ZY301a",
    "sensors": []
    },
    {
    "name": "BJ3A1",
    "sensors": []
    },
    {
    "name": "ZY02C",
    "sensors": []
    }
    ]];
    satelliteList.sort((a, b) => {
    // localeCompare() 方法返回一个数字来指示一个参考字符串是否在排序顺序前面或之后或与给定字符串相同。
    return a.name.localeCompare(b.name);
    });
    console.log(satelliteList)

环境安装

  1. **npm ci 类似npm install,但它适用于自动化环境,例如测试平台、持续集成和部署,或者您想要确保对依赖项进行全新安装的任何情况。注意该命令需要 npm>=5.7
    • npm i依赖package.json,而npm ci依赖package-lock.json。
    • 当package-lock.json中的依赖于package.json不一致时,npm ci退出但不会修改package-lock.json。
    • npm ci只可以一次性的安装整个项目依赖,但无法添加单个依赖项。
    • npm ci安装包之前,会删除掉node_modules文件夹,因此他不需要去校验已下载文件版本与控制版本的关系,也不用校验是否存在最新版本的库,所以下载的速度更快。
    • npm安装时,不会修改package.json与package-lock.json。

面板拖拽

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
/**
* 拖拽边界线
* @param {*} e
*/
const handleDragLineMouseDown = (e) => {
// 鼠标按下时的X坐标
let startX = e.clientX;
// 记录content初始宽度
let startWidth = sidecontainerRef.current.offsetWidth;
document.onmousemove = (e) => {
// 鼠标当前位置
let moveX = e.clientX;
let newWidth = startWidth + startX - moveX;
sidecontainerRef.current.style.width = `${newWidth}px`;
};
document.onmouseup = () => {
document.onmousemove = null;
document.onmouseup = null;
};
};


<div>
<div className={sider}>Sider</div>
<div
ref={sideborderRef}
className="draggable"
onMouseDown={handleDragLineMouseDown}
onMouseUp={() => {
document.onmousemove = null;
document.onmouseup = null;
}}></div>
<div ref={sidecontainerRef} className={content}>Content</div>
</div>

双击复制

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
import React, { useState, useCallback } from 'react';

const CopyableText = ({ text }) => {
const [feedback, setFeedback] = useState('');

const copyToClipboard = useCallback(async () => {
// 首先尝试使用 Clipboard API
if (navigator.clipboard && navigator.clipboard.writeText) {
try {
await navigator.clipboard.writeText(text);
setFeedback('Copied!');
setTimeout(() => setFeedback(''), 2000);
} catch (err) {
console.error('Failed to copy text: ', err);
fallbackCopyToClipboard();
}
} else {
// 如果 Clipboard API 不可用,使用回退方法
fallbackCopyToClipboard();
}
}, [text]);

const fallbackCopyToClipboard = () => {
const textArea = document.createElement('textarea');
textArea.value = text;
document.body.appendChild(textArea);
textArea.select();
try {
document.execCommand('copy');
setFeedback('Copied!');
setTimeout(() => setFeedback(''), 2000);
} catch (err) {
console.error('Fallback: Oops, unable to copy', err);
setFeedback('Copy failed');
setTimeout(() => setFeedback(''), 2000);
}
document.body.removeChild(textArea);
};

return (
<div>
<span
onDoubleClick={copyToClipboard}
style={{
userSelect: 'none',
cursor: 'pointer',
padding: '10px',
backgroundColor: '#f0f0f0',
display: 'inline-block',
borderRadius: '4px',
}}
>
{text}
</span>
{feedback && (
<div
style={{
marginTop: '10px',
color: feedback === 'Copied!' ? 'green' : 'red',
}}
>
{feedback}
</div>
)}
</div>
);
};

export default CopyableText;

判断鼠标点击的位置是否在元素内

Event:currentTarget

Event.currentTarget 是绑定事件处理器的元素,即事件处理函数正在处理的那个元素。在事件冒泡过程中,event.currentTarget 始终指向绑定事件的元素。

关键点:

  • currentTarget 的作用:
    • 在事件处理函数内部,currentTarget 指向绑定事件监听器的元素,而不是触发事件的元素(这点与 event.target 不同)。
  • 作用范围:
    • currentTarget 的值仅在事件处理器内部可用。在事件处理器外部访问它时,它的值将为 null
  • 注意事项:
    • 如果在事件处理器内部获取 Event 对象的引用,并尝试在事件处理器外部访问其 currentTarget 属性,它将返回 null
MouseEvent.relatedTarget

只读属性 MouseEvent.relatedTarget 是鼠标事件的次要目标(如果存在),它包括:

事件名称 target relatedTarget
focusin EventTarget 获取焦点 EventTarget 失去焦点
focusout EventTarget 失去焦点 The EventTarget 获取焦点
mouseenter 指针设备进入EventTarget 指针设备离开EventTarget
mouseleave 指针设备离开 EventTarget 指针设备进入 EventTarget
mouseout 指针设备离开 EventTarget The EventTarget
mouseover 指针设备进入 EventTarget 指针设备离开 EventTarget
dragenter 指针设备进入 EventTarget 指针设备离开 EventTarget
如果事件没有次要目标,relatedTarget 将返回 null.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
/**
* 当你点击 `div` 内部的按钮时,背景颜色会变成绿色,表示点击发生在元素内部。
* 当你点击 `div` 外部的区域时,背景颜色会变成红色,表示点击发生在元素外部。
*/
const ClickInsideOutsideExample: React.FC = () => {
  const [isInside, setIsInside] = useState<boolean | null>(null);
  const handleBlur = (e: React.FocusEvent<HTMLDivElement>) => {
  // 使用 relatedTarget 和 currentTarget进行判断
    if (
      e.relatedTarget === null ||
      !e.currentTarget.contains(e.relatedTarget)
    ) {
      setIsInside(false); // 用户点击了元素外部
    } else {
      setIsInside(true); // 用户点击了元素内部
    }
  };
  return (
    <div>
      <div
        tabIndex={-1} // 使得 div 能够接收 focus blur 事件
        onBlur={handleBlur}
        style={{
          height: "100px",
          backgroundColor:
            isInside === null ? "#f0f0f0" : isInside ? "#a2f5a2" : "#f5a2a2",
          border: "1px solid #ccc",
          width: "300px",
          textAlign: "center",
        }}
      >
        <div
          onClick={() => setIsInside(true)}
          style={{
            height: "100%",
          }}
        >
          {isInside === null
            ? "点击这里"
            : isInside
            ? "点击位置在内部"
            : "点击位置在外部"}
        </div>
      </div>
      <p>点击盒子外部观察变化</p>
    </div>
  );
};

Leaflet 遮罩效果

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
<!DOCTYPE html>
<html lang="zh">
<head>
  <meta charset="UTF-8">
  <title>北京地图反向遮罩效果</title>
  <link rel="stylesheet" href="https://unpkg.com/leaflet@1.7.1/dist/leaflet.css" />
  <style>
    #map {
      height: 100vh;
      width: 100%;
    }
    body {
      margin: 0;
      padding: 0;
    }
  </style>
</head>
<body>
  <div id="map"></div>
  <script src="https://unpkg.com/leaflet@1.7.1/dist/leaflet.js"></script>
  <script>
    // 创建地图
    var map = L.map('map', {
      attributionControl: false,
      zoomControl: true,  // 启用缩放控件
      zoomPosition: 'bottomright'  // 设置缩放控件位置
    }).setView([39.904211, 116.407395], 8);
   
    // 添加底图
    L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png').addTo(map);

    // 添加自定义缩放级别显示控件
    L.Control.ZoomLevel = L.Control.extend({
      options: {
        position: 'topleft'
      },
      onAdd: function (map) {
        var container = L.DomUtil.create('div', 'leaflet-control-zoomlevel leaflet-bar leaflet-control');
        var zoomLevel = L.DomUtil.create('a', 'leaflet-control-zoom-level', container);
        zoomLevel.style.width = '30px';
        zoomLevel.style.height = '30px';
        zoomLevel.style.lineHeight = '30px';
        zoomLevel.style.textAlign = 'center';
        zoomLevel.style.textDecoration = 'none';
        zoomLevel.style.color = '#666';
        zoomLevel.style.fontSize = '14px';
        zoomLevel.style.cursor = 'default';
        map.on('zoomend', function () {
          zoomLevel.innerHTML = map.getZoom();
        });

        // 初始显示
        zoomLevel.innerHTML = map.getZoom();
        return container;
      }
    });

    // 添加缩放级别控件到地图
    new L.Control.ZoomLevel().addTo(map);

    // 创建北京的GeoJSON数据
    var beijingGeoJSON = {
      "type": "Feature",
      "properties": {},
      "geometry": {
        "type": "Polygon",
        "coordinates": [[
          [116.2336667847, 40.2164962769],
          [116.4365355492, 40.2972064208],
          [116.5616861979, 40.3539367676],
          [116.7755639553, 40.3539367676],
          [116.9456965637, 40.3978271484],
          [117.1046804428, 40.4088134766],
          [117.2767965698, 40.3759155273],
          [117.4049365234, 40.2862243652],
          [117.4268870544, 40.1566162109],
          [117.3907293701, 39.9871520996],
          [117.3077204227, 39.8885498047],
          [117.2465913391, 39.7790527344],
          [117.2027066040, 39.6475524902],
          [117.1046804428, 39.5819091797],
          [116.9895813465, 39.5051269531],
          [116.8614413929, 39.4503173828],
          [116.7113192081, 39.4173583984],
          [116.5341135025, 39.4063720703],
          [116.3639808941, 39.4283447266],
          [116.2336667847, 39.4722290039],
          [116.1274654865, 39.5600585938],
          [116.0741984940, 39.7021789551],
          [116.0741984940, 39.8336791992],
          [116.1055149078, 39.9981384277],
          [116.1934063721, 40.1017456055],
          [116.2336667847, 40.2164962769]
        ]]
      }
    };



    // 创建世界范围的GeoJSON
    var worldGeoJSON = {
      "type": "Feature",
      "properties": {},
      "geometry": {
        "type": "Polygon",
        "coordinates": [[
          [-180, -90],
          [180, -90],
          [180, 90],
          [-180, 90],
          [-180, -90]
        ]]
      }
    };

    // 创建遮罩效果
    function createMask(worldGeoJSON, holeGeoJSON) {
      return {
        "type": "Feature",
        "properties": {},
        "geometry": {
          "type": "Polygon",
          "coordinates": [
            worldGeoJSON.geometry.coordinates[0],
            holeGeoJSON.geometry.coordinates[0]
          ]
        }
      };
    }

    // 创建遮罩层
    var maskGeoJSON = createMask(worldGeoJSON, beijingGeoJSON);
    L.geoJSON(maskGeoJSON, {
      style: {
        fillColor: 'black',
        fillOpacity: 0.6,
        color: 'none',
        weight: 0
      }
    }).addTo(map);

    // 添加北京边界
    L.geoJSON(beijingGeoJSON, {
      style: {
        color: '#ff0000',
        weight: 2,
        fillOpacity: 0,
        fill: false
      }
    }).addTo(map);

    // 自动缩放到北京区域
    map.fitBounds(L.geoJSON(beijingGeoJSON).getBounds(), {
      padding: [50, 50]
    });
  </script>
</body>
</html>
  • 标题: 日常开发小技巧
  • 作者: 菇太帷i
  • 创建于 : 2024-08-30 09:23:00
  • 更新于 : 2025-09-18 06:39:53
  • 链接: https://blog.gutawei.com/2024/08/30/日常开发小技巧/
  • 版权声明: 本文章采用 CC BY-NC-SA 4.0 进行许可。
评论