本文整理高德地图 Web JS API 2.0 在原生 JavaScript 和 Vue 3 项目中的接入方式,并给出点标记、信息窗体、定位、地址解析、路线规划等常见功能示例。

本文示例面向中国大陆版 Web JS API 2.0。Key、配额、计费与合规要求可能调整,上线前请以高德开放平台控制台和官方文档为准。

一、接入前准备

1.1 创建应用和 Key

  1. 登录高德开放平台控制台
  2. 进入「应用管理」并创建应用。
  3. 在应用中添加 Key,服务平台选择「Web 端(JS API)」。
  4. 保存生成的 Key安全密钥 securityJsCode
  5. 在控制台配置可使用该 Key 的域名白名单,并为开发、测试、生产环境分别创建 Key。

2021 年 12 月 2 日之后申请的 Web 端 Key 必须配合安全密钥使用。注意不要误选「Web 服务」Key,Web 服务 Key 主要供服务端 REST API 使用。

1.2 安全密钥配置

开发环境可直接配置明文安全密钥,但它最终会出现在浏览器代码中:

阅读全文 »

基于 Cytoscape.js v3.34 官方文档 整理

一、什么是 Cytoscape.js

Cytoscape.js 是一个由多伦多大学 Donnelly Centre 开发的开源 JavaScript 图论/网络可视化库,采用 MIT 许可证,已在《Bioinformatics》(2016、2023)期刊发表学术论文。不同于 AntV X6(偏图编辑器),Cytoscape.js 专注于图数据的可视化分析,擅长处理大规模节点-边关系展示与交互。

支持的图模型

图类型支持
有向图(Directed)
无向图(Undirected)
混合图(Mixed)
多重图(Multigraph,多重边)
自环(Loops)
复合图(Compound,父子节点嵌套)

核心特性

  • 图论原生支持:内置节点度、最短路径(Dijkstra/A*)、连通分量、PageRank 等图算法
  • 丰富布局算法:8 种核心内置布局(circle、concentric、breadthfirst、grid、cose、random、preset、null),通过官方第一方扩展可扩展至 15+(dagre、klay、avsdf、cola、fcose、elk 等)
  • 高性能渲染:Canvas 2D 渲染,支持数千节点的流畅交互
  • 移动端友好:原生触摸手势支持(pinch-zoom、pan 双指缩放)
  • 可扩展架构:通过 cytoscape.use(ext) 注册第一方及社区扩展,涵盖布局、UI 控件、数据导入等
  • 多模块格式:支持 UMD(cytoscape.min.js)、ESM(cytoscape.esm.min.mjs)、CommonJS(cytoscape.cjs.js
  • 跨环境运行:支持所有现代浏览器,Node.js headless 模式可用于纯计算/服务端渲染

适用场景

阅读全文 »

移动端适配终极方案

移动端适配绕不开两个 PostCSS 插件:postcss-pxtorem(px → rem)和 postcss-px-to-viewport(px → vw/vh)。二者都是编译期自动转换,写 px 即可适配不同屏幕,但原理和适用场景完全不同。本文从配置到踩坑一次性讲透。

一、两句话理解区别

方案原理一句话
px → rem把 px 转成 rem,通过 <html>font-size 缩放按比例放大缩小根字号
px → vw把 px 转成 vw/vh,通过视口宽度自动缩放直接按视口百分比计算

对比:

1
2
3
4
5
6
7
8
9
10
11
12
设计稿 375px 宽,一个 100px 的盒子:

px → rem:
转换后 width: 1rem
html font-size = 375 / 10 = 37.5px
→ 实际宽度 = 1 × 37.5 = 37.5px (375 屏)
→ iPhone 14 Pro Max (430) : 1 × 43 = 43px ✅

px → vw:
转换后 width: 26.67vw
→ 实际宽度 = 375 × 26.67% = 100px (375 屏)
→ iPhone 14 Pro Max (430) : 430 × 26.67% = 114.7px ✅

二、postcss-pxtorem

2.1 安装

阅读全文 »

Vue3 中 SCSS/Less 快捷写法

Vue3 单文件组件(SFC)天然支持预处理器,但在日常开发中很多写法都停留在原生 CSS 水平。本文整理一套 SCSS/Less 的快捷写法,涵盖嵌套、变量、Mixin、循环、函数等核心特性,写完就能提效 50% 以上。

一、Vue3 样式块基础配置

1.1 <style> 标签的四种形态

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
<!-- 1. scoped:样式隔离 -->
<style scoped lang="scss">
.box { ... }
</style>

<!-- 2. module:CSS Modules -->
<style module lang="scss">
.active {
color: red;
}
</style>
<!-- 使用时:<div :class="$style.active"> -->

<!-- 3. v-bind:JS 变量驱动 CSS -->
<script setup>
const primaryColor = ref("#1890ff");
</script>
<style scoped>
.header {
color: v-bind(primaryColor);
}
</style>

<!-- 4. 多块并行 -->
<style scoped lang="scss">
/* 组件样式 */
</style>
<style lang="scss">
/* 全局样式 */
</style>

1.2 Vite 全局注入变量

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// vite.config.ts
export default defineConfig({
css: {
preprocessorOptions: {
scss: {
// 每个 .vue 文件自动注入
additionalData: `
@use "@/styles/variables.scss" as *;
@use "@/styles/mixins.scss" as *;
`,
},
},
},
});

Less 同理:

阅读全文 »

每个 Vue3 项目启动后第一件事往往是写一大堆全局样式:重置默认行为、封装布局工具类、定义主题变量……这些代码复用率极高,但每次都从头写一遍很低效。本文整理了一份开箱即用的 SCSS 通用样式集,所有值均通过变量、Map、Mixin 驱动,覆盖 Reset、布局、间距、文字、主题、响应式、动画等高频场景,复制进项目就能用。

一、全局变量 —— 所有值的唯一来源

分两个文件:_vars-only.scss 存放纯 Map 和变量(不产生 CSS),供 additionalData 注入;variables.scss 在此基础上输出 CSS 自定义属性,由 index.scss 一次性引入。

styles/_vars-only.scss

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
// styles/_vars-only.scss
// ═══════════════════════════════════════
// 仅 Map + 变量,不输出任何 CSS
// Vite 通过 additionalData 自动注入
// ═══════════════════════════════════════

// ====== 主题色 Map ======
$theme-colors: (
"primary": #1677ff,
"success": #52c41a,
"warning": #faad14,
"danger": #ff4d4f,
"info": #909399,
);

// ====== 语义色 Map ======
$semantic-colors: (
"bg-page": #f5f5f5,
"bg-white": #fff,
"text": #333,
"text-secondary": #666,
"text-disabled": #bbb,
"text-placeholder": #c0c4cc,
"border": #eee,
"border-light": #f0f0f0,
);

// ====== 暗色主题覆盖 Map ======
$dark-theme-colors: (
"bg-page": #141414,
"bg-white": #1e1e1e,
"text": #e5e5e5,
"text-secondary": #999,
"text-disabled": #555,
"border": #333,
"border-light": #2a2a2a,
);

// ====== 字体 Map ======
$font-family-base: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif;
$font-size-base: 14px;
$font-sizes: (10, 12, 13, 14, 15, 16, 18, 20, 22, 24, 28, 32, 36, 40, 48);
$font-weights: (
"normal": 400,
"medium": 500,
"semibold": 600,
"bold": 700,
);
$line-heights: (
"tight": 1.25,
"normal": 1.5,
"relaxed": 1.75,
);

// ====== 圆角 Map ======
$radius-map: (
"sm": 4px,
"base": 8px,
"md": 12px,
"lg": 16px,
"full": 50%,
);

// ====== 间距 Map ======
$spacing-sizes: (0, 2, 4, 6, 8, 10, 12, 14, 16, 20, 24);
$spacing-base: 16px;

// ====== 阴影 Map ======
$shadow-map: (
"sm": 0 1px 3px rgba(0, 0, 0, 0.08),
"base": 0 2px 8px rgba(0, 0, 0, 0.1),
"lg": 0 4px 16px rgba(0, 0, 0, 0.12),
);

// ====== 过渡变量 ======
$transition-duration: 0.3s;
$transition-timing: ease;
$transition-duration-base: $transition-duration;
$transition-base: all $transition-duration $transition-timing;

// ====== 断点 Map ======
$breakpoints: (
"sm": 576px,
"md": 768px,
"lg": 992px,
"xl": 1200px,
"xxl": 1400px,
);

// ====== SCSS 变量(引用 Map 值) ======
$color-primary: map-get($theme-colors, "primary");
$color-success: map-get($theme-colors, "success");
$color-warning: map-get($theme-colors, "warning");
$color-danger: map-get($theme-colors, "danger");
$color-info: map-get($theme-colors, "info");

$color-bg-page: map-get($semantic-colors, "bg-page");
$color-bg-white: map-get($semantic-colors, "bg-white");
$color-text: map-get($semantic-colors, "text");
$color-text-secondary: map-get($semantic-colors, "text-secondary");
$color-text-disabled: map-get($semantic-colors, "text-disabled");
$color-text-placeholder: map-get($semantic-colors, "text-placeholder");
$color-border: map-get($semantic-colors, "border");
$color-border-light: map-get($semantic-colors, "border-light");

$radius-sm: map-get($radius-map, "sm");
$radius-base: map-get($radius-map, "base");
$radius-md: map-get($radius-map, "md");
$radius-lg: map-get($radius-map, "lg");
$radius-full: map-get($radius-map, "full");

$shadow-sm: map-get($shadow-map, "sm");
$shadow-base: map-get($shadow-map, "base");
$shadow-lg: map-get($shadow-map, "lg");

styles/variables.scss

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
// styles/variables.scss
// 发布 CSS 自定义属性(_vars-only 的变量已由 additionalData 全局注入)

// ====== CSS 自定义属性(运行时切换) ======
:root {
// 主题色
@each $name, $color in $theme-colors {
--color-#{$name}: #{$color};
}
// 语义色
@each $name, $color in $semantic-colors {
--color-#{$name}: #{$color};
}
// 圆角
@each $name, $value in $radius-map {
--radius-#{$name}: #{$value};
}
// 阴影
@each $name, $value in $shadow-map {
--shadow-#{$name}: #{$value};
}
--font-size-base: #{$font-size-base};
--transition-base: #{$transition-base};
}

// 暗色主题
[data-theme="dark"] {
@each $name, $color in $dark-theme-colors {
--color-#{$name}: #{$color};
}
}

二、CSS Reset —— 变量驱动的基础重置

阅读全文 »

前言

在现代 Web 应用中,富文本编辑器是一个不可或缺的组件。无论是内容管理系统、博客平台还是在线文档工具,都离不开一个功能完善的编辑器。

本文将详细介绍两款主流富文本编辑器 —— wangEditorTinyMCE 的集成与定制方法,并提供完整的可运行 Demo。

两款编辑器对比速览

特性wangEditorTinyMCE
开源协议MITMIT(核心)/ 商业(高级插件)
包体积轻量(~200KB)较大(~1MB+,按需加载)
上手难度低,配置简洁中,插件体系丰富
插件生态内置基本功能丰富,50+ 官方插件
自定义扩展Boot 注册机制插件 API + setup 回调
适合场景轻量内容编辑、移动端复杂文档编辑、企业级应用
Vue/React 集成官方适配包官方封装组件

第一部分:wangEditor

一、为什么需要自定义编辑器行为?

市面上的富文本编辑器大多开箱即用,但在实际业务场景中,我们常常需要:

阅读全文 »

本文详细介绍如何在 Vue 3 + Element Plus 项目中集成天地图 JavaScript API v4.0,涵盖 SDK 加载、地图选点定位、多边形绘制/编辑、轨迹展示等常见场景的完整实现方案。

一、为什么选择天地图

天地图(TianDiTu)是国家地理信息公共服务平台,提供标准的 JavaScript API,相较于国外地图服务具有以下优势:

  • 国内访问稳定,加载速度快
  • 提供卫星影像、混合地图等多种图层
  • API 覆盖标记、折线、多边形、信息窗、比例尺等常用功能
  • 完全免费,无需注册信用卡

二、SDK 动态加载方案

天地图 JS API 通过 CDN 加载,建议在组件内按需动态引入,避免首屏加载不必要的资源。

2.1 加载函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
const TIANDITU_TK = "your_api_key_here";

/**
* 动态加载天地图 JavaScript SDK
* SDK 加载完成后 window.T 全局对象可用
*/
const loadTdtScript = () => {
return new Promise((resolve, reject) => {
// 已加载则直接返回,避免重复加载
if (window.T) return resolve();

const script = document.createElement("script");
script.src = `https://api.tianditu.gov.cn/api?v=4.0&tk=${TIANDITU_TK}`;
script.onload = () => resolve();
script.onerror = () => reject(new Error("天地图加载失败"));
document.head.appendChild(script);
});
};
阅读全文 »

本文是 Compose 系列配套参考手册,包含每个控件的完整参数、示例和注意事项。配合 Compose 从 0 到 1 食用更佳。


前言:Compose 控件体系概览

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
@Composable 函数(所有 UI 都是函数)
├── Text(文本)
├── Button(按钮)
├── TextField / OutlinedTextField(输入框)
├── Image(图片)
├── Checkbox(复选框)
├── Switch(开关)
├── RadioButton(单选按钮)
├── Slider(滑动条)
├── Column(纵向布局)
├── Row(横向布局)
├── Box(层叠布局)
├── LazyColumn / LazyRow(列表控件)
├── Scaffold(页面骨架)
│ ├── TopAppBar(顶部栏)
│ ├── BottomAppBar / NavigationBar(底部栏)
│ └── FloatingActionButton(浮动按钮)
└── Modifier(外观修饰,适用于所有控件)

Modifier 是所有控件共用的外观修饰系统,详见第十四章。

Text —— 文本

作用

显示文字。Compose 中最基础的控件,替代传统 TextView

阅读全文 »

本文深入剖析 Compose 的四个核心概念,帮你从「会用」进阶到「理解」。配合 Compose 从 0 到 1 阅读效果最佳。

@Composable —— UI 就是函数

作用

@Composable 是 Compose 的灵魂注解。被它标记的函数就是一个 UI 组件,可以直接在别的函数里调用。

核心规则

规则说明
函数名首字母大写约定俗成,与普通函数区分(如 MyButton 而非 myButton
只能被 @Composable 函数调用普通函数不能直接调用 Composable 函数
没有返回值Composable 函数通常返回 Unit(描述 UI,不返回对象)
可接收任意参数通过参数控制 UI 外观和行为,实现复用
默认没有顺序调用顺序不代表实际排列顺序(需要在布局容器中才有顺序)

示例

1
2
3
4
5
6
7
8
9
10
11
12
13
// 定义一个 Composable 控件
@Composable
fun MyTitle() {
Text(text = "我是标题")
}

// 在别的地方直接调用函数名就能用
@Composable
fun MyPage() {
MyTitle()
MyTitle()
MyTitle() // 调三次就显示三个标题
}
阅读全文 »

Compose 页面生命周期完全指南

Compose 的生命周期不是单一概念,而是三层叠加模型

  1. Android 生命周期LifecycleOwner)—— Activity / Fragment 的传统生命周期
  2. 组合生命周期(Composition)—— Composable 进入/离开组合树
  3. Effect 生命周期LaunchedEffect / DisposableEffect)—— 副作用随 key 变化

理解这三层如何协作,是写出无内存泄漏、无状态错乱的 Compose 代码的关键。


一、三层生命周期全景图

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
     onCreate()     →  Composition    →  Composition
onStart() 首次进入 重组
onResume()
│ │ │
▼ ▼ ▼
┌─────────────────────────────────────────────────┐
│ Activity Lifecycle │
│ Lifecycle.Event.ON_START / ON_RESUME / ... │
└─────────────────────────────────────────────────┘


┌─────────────────────────────────────────────────┐
│ Composition(组合) │
│ 进入组合 → 重组(recomposition) → 退出组合 │
└─────────────────────────────────────────────────┘


┌─────────────────────────────────────────────────┐
│ Effect(副作用) │
│ LaunchedEffect → key 变化 → 协程取消 │
│ DisposableEffect → key 变化 → dispose → 重启 │
└─────────────────────────────────────────────────┘

关键区别

阅读全文 »