⚠️温馨提示: 文档中包含【1个】暂不支持的区域,请通过搜索关键字【暂不支持的文档区域】进行后续处理

data-visualize-notes.zip

很多同学可能第一次来,我分享了很多干货公开课,希望对你的技术成长有帮助!免费分享都在这里,需要 文档和配套视频找咨询老师 领取~

【飞书文档】备战金九银十,进阶大前端,涨薪全突破! 跳槽涨薪需求同学必看

【飞书文档】项目没有难点亮点?字节大佬带你前端项目弯道超车

【飞书文档】字节大佬带你弯道超车,深入剖析大厂面试真题~

【飞书文档】面试被算法干吐了?字节大佬带你突破极限,冲击 30K+ 必掌握算法与 WebAssembly 技术 冲刺年包 50W+ 同学必看


【飞书文档】高级前端专家如何做性能优化?特邀字节大佬细数飞书应用优化细节【超详细内培版】

【飞书文档】高级前端专家如何做项目架构与工程化设计? 特邀字节大佬细数字节开源项目架构细节【超详细内培版】

【飞书文档】小小微前端,轻松拿捏,特邀字节大佬开讲微前端架构与源码剖析【内部培训版】 35K+ 同学必看

【飞书文档】高级前端专家如何做性能优化?特邀字节大佬细数飞书应用优化细节(二)【内部培训版】


【飞书文档】前端破局 AI 应用开发,特邀字节大佬分享字节系 AI 场景落地应用与 AI 引擎编排流程 探索前端新方向同学福音

【飞书文档】React18 源码深度剖析,字节面试官教你轻松拿捏高级前端专家面试框架原理题

【飞书文档】Vue3 源码深度剖析,字节面试官教你轻松拿捏高级前端专家面试框架原理题

【飞书文档】Vite 构建过程与源码深度剖析,你怎么也想不到一线大厂工程化构建面试会问这么深!

【飞书文档】脚手架/CLI 工具原理与开发实践,特邀字节前端专家带你体悟大厂团队基建与研发工作流 团队基建必看

【飞书文档】Webpack 原理深度解读与面试专项突击,字节面试官带你手撕难缠打包构建面试原理题 工程化与构建原理深入


【飞书文档】冲击中大厂筹备与涨薪突击最优方案,特邀字节面试官带你体验大厂面试全流程

【飞书文档】字节面试专场——中厂在职学员冲击大厂 30K+,看看面试官如何评价 大厂模拟面试,问得很深慎看

【飞书文档】前端性能与异常监控平台全链路设计与实践,字节架构师:掌握这整套拿个 40K+ 不在话下吧

【飞书文档】飞书文档协同编辑器技术揭秘,特邀字节架构师分享富文本编辑器方案细节

  1. 双引擎
  2. 海量数据渲染
  3. 3D 渲染与模型优化
  • 初中级 】有了解过 Echarts 或 Antv 原理吗,请简单说说?
  • 中高级 】请详细说说可视化引擎设计与开发细节
  • 专家级 】从零到一架构一个类似字节 DataWind 大数据可视化平台,简要说说你的设计思路

初中级 】有了解过 Echarts 或 Antv 原理吗,请简单说说?

回答他俩的原理,一定要从渲染引擎入手。两者的底层,均为 canvas 或 svg。

框架核心要点

Echarts 核心要点

  • Canvas 渲染 :ECharts 默认使用 HTML5 的 Canvas 技术。Canvas 是一种基于像素的渲染方式,适合绘制大量图形元素,如折线图、柱状图等,具有较高的绘制性能。
  • 数据驱动渲染 :ECharts 采用数据驱动的方法,将输入的数据映射到图表的各个元素(如坐标轴、图形等)上。用户传入的数据集可以动态更新,ECharts 会根据数据的变化进行重绘。
  • 图表组件和布局系统 :ECharts 的核心由多个独立的组件组成,如坐标轴、图例、网格等。各个组件之间通过布局系统进行协同工作,可以自由组合,形成多种图表。
  • 响应式和交互式设计 :ECharts 提供了良好的交互性和响应性,支持事件处理(如点击、鼠标悬停)和自适应屏幕大小变化。同时,ECharts 支持动画效果,使得图表在数据更新或页面加载时更生动。
  • 扩展性和定制化 :ECharts 提供了高度的扩展性,用户可以通过插件或自定义组件实现定制化的图表。

Antv 核心要点

  • 图形语法(Grammar of Graphics) :AntV 的核心库 G2 使用图形语法来描述图表,图形语法是一种将数据映射到可视化元素(如点、线、区域)的规范化方法。这种方法使得用户可以自由组合不同的图表元素,而不依赖于固定的图表类型。
  • 数据映射和几何标记 :数据经过转换后,会映射到几何标记(如点、线、柱状等),再通过视觉通道(颜色、形状、大小等)进行呈现。这种映射关系非常灵活,可以轻松实现复杂的可视化需求。
  • 渲染引擎 :与 ECharts 类似,AntV 也使用 CanvasSVG 作为底层渲染技术。但由于 AntV 主要基于图形语法,它的图表更加模块化和灵活,可以根据不同的数据类型和显示需求进行组合。
  • 数据处理与变换 :AntV 支持对数据进行复杂的变换和处理,例如数据分组、过滤、聚合等操作,这些操作可以方便地用于生成不同类型的图表。
  • 交互与动画 :AntV 提供了丰富的交互和动画效果,用户可以在图表中加入事件监听器,实现点击、悬停等交互行为。同时,通过动画可以增强用户的视觉体验。

Echarts 渲染引擎

ECharts 提供两种渲染机制: CanvasSVG ,它们的工作机制不同,适用场景也有所区别。

  • Canvas 渲染机制

    • Canvas 是基于像素的绘图技术,适合大量图形元素的快速绘制。
    • ECharts 中的图形元素如折线、柱状、散点等,都会在 Canvas 上通过像素进行绘制。
    • Canvas 的优点是性能好,尤其在大数据量场景下,渲染速度较快。缺点是元素不可编辑,因为 Canvas 是一块位图,无法直接操作图形中的某一部分。
  • SVG 渲染机制

    • SVG 是基于 DOM 的矢量图形技术,图形元素以 DOM 节点的形式存在,因此每个图形都是独立的,可以直接操作和控制。
    • ECharts 会根据用户配置决定是否使用 SVG 渲染。如果选择 SVG,图形会被转化为一系列的 DOM 节点,如 <rect><circle> 等。
    • SVG 的优点是可编辑、可放大缩小而不失真,适合小数据量且需要复杂交互的场景。缺点是当数据量大时,性能表现较差,因为每个图形元素都需要单独的 DOM 节点管理。

Canvas 原理剖析

Canvas 是 HTML5 中提供的一种基于位图的绘图技术,允许开发者通过 JavaScript 在网页上绘制图形。Canvas 的工作方式是通过 API 直接在一个固定大小的矩形区域内进行像素级的操作,并且绘制的内容不会保存为 DOM 元素。Canvas 通常用于渲染复杂的图形,如游戏、动画、数据可视化等。因为它是基于像素的渲染,所以在大量数据渲染或实时更新图表时性能较好。

特点

  • 高性能,适合大数据量或实时渲染场景。
  • 基于像素,内容一旦绘制无法直接修改或操作单个图形元素。

SVG 原理剖析

SVG (Scalable Vector Graphics)是一种基于 XML 的矢量图形技术,它允许图形以矢量的形式描述,并且作为 DOM 元素存在。SVG 图形可以随页面放大缩小而不失真,并且每个图形元素都可以直接操作或编辑,因此特别适合需要交互性或响应性的图形展示。

特点

  • 基于 DOM,图形元素可以独立操作和编辑。
  • 适合较小数据量且需要复杂交互的场景。
  • 矢量图,缩放不失真,但在大数据量下性能不如 Canvas。

实现 Echarts 柱状图

Canvas 引擎

基础实现

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Canvas Bar Chart</title>
</head>
<body>
<canvas id="canvasBarChart" width="400" height="300"></canvas>
<script>
    const canvas = document.getElementById('canvasBarChart');
    const ctx = canvas.getContext('2d');
    
    // 数据
    const data = [60, 80, 120, 180, 150, 200];
    const labels = ['A', 'B', 'C', 'D', 'E', 'F'];
    
    // 柱状图参数
    const chartHeight = 250;
    const chartWidth = 350;
    const barWidth = 40;
    const padding = 20;  // 留出一定的左右间距
    const axisOffset = 40; // 坐标轴的偏移量

    // 清除画布
    ctx.clearRect(0, 0, canvas.width, canvas.height);
    
    // 绘制柱状图
    data.forEach((value, index) => {
        const x = axisOffset + padding + index * (barWidth + padding); // X 轴上的位置
        const y = chartHeight - value; // Y 轴上的位置
        ctx.fillStyle = '#007acc';
        ctx.fillRect(x, y, barWidth, value);  // 绘制柱子

        // 绘制数据标签
        ctx.fillStyle = '#000';
        ctx.textAlign = 'center';
        ctx.fillText(labels[index], x + barWidth / 2, chartHeight + 20);  // 绘制X轴标签
        ctx.fillText(value, x + barWidth / 2, y - 5);  // 显示数值
    });

    // 绘制坐标轴
    ctx.beginPath();
    ctx.moveTo(axisOffset, chartHeight);  // Y 轴起点 (左下角)
    ctx.lineTo(axisOffset, 0);  // 向上绘制 Y 轴
    ctx.moveTo(axisOffset, chartHeight);  // X 轴起点 (左下角)
    ctx.lineTo(chartWidth, chartHeight);  // 向右绘制 X 轴
    ctx.stroke();
</script>
</body>
</html>

以上实现存在以下问题:

  1. 4K 屏幕适配问题
  2. 无出场动画

高分辨率屏幕兼容

在 4K 屏幕上,Canvas 绘制的图形可能显得不清晰或比例不正确,这是因为在高分辨率设备(如 4K 屏)上,Canvas 的默认大小与设备的实际像素密度不匹配。要在高分辨率(如 4K)屏幕上让 Canvas 图形保持清晰,需要进行 **像素密度缩放**,具体方法是调整 Canvas 的 物理像素 和 **CSS 像素**。

  1. 检测设备像素比 (devicePixelRatio)

浏览器提供 window.devicePixelRatio ,用来表示设备像素与 CSS 像素的比例。例如,普通屏幕的 devicePixelRatio 通常为 1,而 4K 屏幕的比例可能为 2 或更高。

  1. 调整 Canvas 尺寸

我们可以根据 devicePixelRatio 来设置 Canvas 的实际像素尺寸,并通过 CSS 设置 Canvas 的显示尺寸。这样,Canvas 可以在高分辨率屏幕上绘制清晰的图形。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Canvas Bar Chart on 4K Screen</title>
    <style>
        canvas {
            width: 400px; /* 设置 Canvas 在页面中的显示尺寸 */
            height: 300px;
        }
    </style>
</head>
<body>
<canvas id="canvasBarChart"></canvas>
<script>
    const canvas = document.getElementById('canvasBarChart');
    const ctx = canvas.getContext('2d');
    
    // 获取设备像素比
    const dpr = window.devicePixelRatio || 1;

    // 设置画布的 CSS 尺寸
    const cssWidth = 400;
    const cssHeight = 300;

    // 设置实际画布大小,根据设备像素比调整
    canvas.width = cssWidth * dpr;
    canvas.height = cssHeight * dpr;

    // 缩放上下文以适应高分辨率
    ctx.scale(dpr, dpr);

    // 数据
    const data = [60, 80, 120, 180, 150, 200];
    const labels = ['A', 'B', 'C', 'D', 'E', 'F'];
    
    // 柱状图参数
    const chartHeight = 250;
    const chartWidth = 350;
    const barWidth = 40;
    const padding = 20;  // 留出一定的左右间距
    const axisOffset = 40; // 坐标轴的偏移量

    // 清除画布
    ctx.clearRect(0, 0, canvas.width, canvas.height);
    
    // 绘制柱状图
    data.forEach((value, index) => {
        const x = axisOffset + padding + index * (barWidth + padding); // X 轴上的位置
        const y = chartHeight - value; // Y 轴上的位置
        ctx.fillStyle = '#007acc';
        ctx.fillRect(x, y, barWidth, value);  // 绘制柱子

        // 绘制数据标签
        ctx.fillStyle = '#000';
        ctx.textAlign = 'center';
        ctx.fillText(labels[index], x + barWidth / 2, chartHeight + 20);  // 绘制X轴标签
        ctx.fillText(value, x + barWidth / 2, y - 5);  // 显示数值
    });

    // 绘制坐标轴
    ctx.beginPath();
    ctx.moveTo(axisOffset, chartHeight);  // Y 轴起点 (左下角)
    ctx.lineTo(axisOffset, 0);  // 向上绘制 Y 轴
    ctx.moveTo(axisOffset, chartHeight);  // X 轴起点 (左下角)
    ctx.lineTo(chartWidth, chartHeight);  // 向右绘制 X 轴
    ctx.stroke();
</script>
</body>
</html>
  1. 获取设备像素比 ( **devicePixelRatio** **)**:

    • 我们通过 window.devicePixelRatio 获取设备的像素比,例如在 4K 屏幕上通常为 2 或更高。
  2. 调整 Canvas 的实际尺寸

    • 我们将 Canvas 的 实际像素尺寸 设置为 CSS 尺寸 * devicePixelRatio ,这样可以让 Canvas 使用足够的像素来渲染图像,确保在高分辨率屏幕上图像清晰。
  3. 缩放上下文

    • 调用 ctx.scale(dpr, dpr) ,通过缩放绘图上下文来适应高像素密度的屏幕。这样可以确保所有绘制操作仍然以 CSS 尺寸为基准进行计算,但实际绘制使用更多的像素。

出场动画

为了在 Canvas 中实现类似 ECharts 的柱状图出场动画,可以使用 **JavaScript 的动画函数**,例如 requestAnimationFrame ,来逐帧绘制柱状图,使得柱状图从底部逐渐“增长”到最终高度,形成动态的动画效果。

关键思路
  • 初始状态下,柱子的高度为 0。
  • 使用 requestAnimationFrame 逐帧更新每个柱子的高度,直到其达到最终值。
  • 动画过程通过计算每一帧的时间间隔以及目标高度来平滑过渡。
代码实现
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Canvas Bar Chart with Animation</title>
    <style>
        canvas {
            width: 400px;
            height: 300px;
        }
    </style>
</head>
<body>
<canvas id="canvasBarChart"></canvas>
<script>
    const canvas = document.getElementById('canvasBarChart');
    const ctx = canvas.getContext('2d');
    
    // 获取设备像素比
    const dpr = window.devicePixelRatio || 1;

    // 设置画布的 CSS 尺寸
    const cssWidth = 400;
    const cssHeight = 300;

    // 设置实际画布大小,根据设备像素比调整
    canvas.width = cssWidth * dpr;
    canvas.height = cssHeight * dpr;

    // 缩放上下文以适应高分辨率
    ctx.scale(dpr, dpr);

    // 数据
    const data = [60, 80, 120, 180, 150, 200];
    const labels = ['A', 'B', 'C', 'D', 'E', 'F'];
    
    // 柱状图参数
    const chartHeight = 250;
    const chartWidth = 350;
    const barWidth = 40;
    const padding = 20;  // 留出一定的左右间距
    const axisOffset = 40; // 坐标轴的偏移量
    const duration = 1000; // 动画持续时间(毫秒)
    const startTime = Date.now(); // 动画开始时间

    // 绘制坐标轴
    function drawAxes() {
        ctx.beginPath();
        ctx.moveTo(axisOffset, chartHeight);  // Y 轴起点 (左下角)
        ctx.lineTo(axisOffset, 0);  // 向上绘制 Y 轴
        ctx.moveTo(axisOffset, chartHeight);  // X 轴起点 (左下角)
        ctx.lineTo(chartWidth, chartHeight);  // 向右绘制 X 轴
        ctx.stroke();
    }

    // 绘制柱状图
    function drawBars(progress) {
        ctx.clearRect(0, 0, canvas.width / dpr, canvas.height / dpr); // 清除画布
        drawAxes();  // 重绘坐标轴

        data.forEach((value, index) => {
            const x = axisOffset + padding + index * (barWidth + padding); // X 轴上的位置
            const finalHeight = value;  // 柱子的最终高度
            const currentHeight = finalHeight * progress;  // 当前帧柱子的高度,根据动画进度调整
            const y = chartHeight - currentHeight; // 当前帧的柱子顶部位置

            ctx.fillStyle = '#007acc';
            ctx.fillRect(x, y, barWidth, currentHeight);  // 绘制柱子

            // 绘制数据标签
            ctx.fillStyle = '#000';
            ctx.textAlign = 'center';
            ctx.fillText(labels[index], x + barWidth / 2, chartHeight + 20);  // 绘制X轴标签
            if (progress === 1) {
                ctx.fillText(finalHeight, x + barWidth / 2, y - 5);  // 最终显示数值
            }
        });
    }

    // 动画函数
    function animate() {
        const elapsed = Date.now() - startTime;  // 计算动画进行的时间
        const progress = Math.min(elapsed / duration, 1);  // 动画进度,确保不超过 1

        drawBars(progress);  // 根据进度绘制图形

        if (progress < 1) {
            requestAnimationFrame(animate);  // 若动画未结束,继续下一帧
        }
    }

    // 开始动画
    animate();

</script>
</body>
</html>
关键点解析
  1. 使用 requestAnimationFrame ** 实现动画**:

    • requestAnimationFrame 是 JavaScript 中常用的帧动画方法,它可以确保在浏览器每次重绘时执行绘制操作,从而实现流畅的动画效果。
  2. 动画进度计算

    • 通过 Date.now() 获取当前时间,与动画的开始时间对比,计算动画的进度 progress
    • progress 的取值范围为 [0, 1] ,随着时间推进,它逐渐从 0 增加到 1,用来控制柱状图高度的增长。
  3. 根据动画进度调整柱子高度

    • 每帧都重新绘制柱子,柱子的当前高度 currentHeight 是根据 progress 逐渐从 0 增长到最终值。 currentHeight = finalHeight * progress
  4. 清除画布并重绘坐标轴

    • 每次绘制柱子之前,使用 ctx.clearRect 清除画布内容,并重新绘制坐标轴,保证每一帧柱子都是在干净的画布上绘制。
优化出场动画
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Canvas Bar Chart with Elastic Animation</title>
    <style>
        canvas {
            width: 400px;
            height: 300px;
        }
    </style>
</head>
<body>
<canvas id="canvasBarChart"></canvas>
<script>
    const canvas = document.getElementById('canvasBarChart');
    const ctx = canvas.getContext('2d');
    
    // 获取设备像素比
    const dpr = window.devicePixelRatio || 1;

    // 设置画布的 CSS 尺寸
    const cssWidth = 400;
    const cssHeight = 300;

    // 设置实际画布大小,根据设备像素比调整
    canvas.width = cssWidth * dpr;
    canvas.height = cssHeight * dpr;

    // 缩放上下文以适应高分辨率
    ctx.scale(dpr, dpr);

    // 数据
    const data = [60, 80, 120, 180, 150, 200];
    const labels = ['A', 'B', 'C', 'D', 'E', 'F'];
    
    // 柱状图参数
    const chartHeight = 250;
    const chartWidth = 350;
    const barWidth = 40;
    const padding = 20;  // 留出一定的左右间距
    const axisOffset = 40; // 坐标轴的偏移量
    const duration = 1000; // 动画持续时间(毫秒)
    const startTime = Date.now(); // 动画开始时间

    // Elastic 缓动函数(带有弹性和阻尼效果)
    function easeOutElastic(t) {
        const c4 = (2 * Math.PI) / 3;
        return t === 0
            ? 0
            : t === 1
            ? 1
            : Math.pow(2, -10 * t) * Math.sin((t * 10 - 0.75) * c4) + 1;
    }

    // 绘制坐标轴
    function drawAxes() {
        ctx.beginPath();
        ctx.moveTo(axisOffset, chartHeight);  // Y 轴起点 (左下角)
        ctx.lineTo(axisOffset, 0);  // 向上绘制 Y 轴
        ctx.moveTo(axisOffset, chartHeight);  // X 轴起点 (左下角)
        ctx.lineTo(chartWidth, chartHeight);  // 向右绘制 X 轴
        ctx.stroke();
    }

    // 绘制柱状图
    function drawBars(progress) {
        ctx.clearRect(0, 0, canvas.width / dpr, canvas.height / dpr); // 清除画布
        drawAxes();  // 重绘坐标轴

        data.forEach((value, index) => {
            const x = axisOffset + padding + index * (barWidth + padding); // X 轴上的位置
            const finalHeight = value;  // 柱子的最终高度
            const easedProgress = easeOutElastic(progress);  // 使用 Elastic 缓动函数计算当前进度
            const currentHeight = finalHeight * easedProgress;  // 当前帧柱子的高度,根据动画进度调整
            const y = chartHeight - currentHeight; // 当前帧的柱子顶部位置

            ctx.fillStyle = '#007acc';
            ctx.fillRect(x, y, barWidth, currentHeight);  // 绘制柱子

            // 绘制数据标签
            ctx.fillStyle = '#000';
            ctx.textAlign = 'center';
            ctx.fillText(labels[index], x + barWidth / 2, chartHeight + 20);  // 绘制X轴标签
            if (progress === 1) {
                ctx.fillText(finalHeight, x + barWidth / 2, y - 5);  // 最终显示数值
            }
        });
    }

    // 动画函数
    function animate() {
        const elapsed = Date.now() - startTime;  // 计算动画进行的时间
        const progress = Math.min(elapsed / duration, 1);  // 动画进度,确保不超过 1

        drawBars(progress);  // 根据进度绘制图形

        if (progress < 1) {
            requestAnimationFrame(animate);  // 若动画未结束,继续下一帧
        }
    }

    // 开始动画
    animate();

</script>
</body>
</html>

SVG 引擎

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>SVG Bar Chart</title>
</head>
<body>
<svg id="svgBarChart" width="400" height="300">
    <g transform="translate(10, 0)">
        <!-- 坐标轴 -->
        <line x1="0" y1="250" x2="300" y2="250" stroke="black"/>  <!-- X轴 -->
        <line x1="0" y1="0" x2="0" y2="250" stroke="black"/>    <!-- Y轴 -->
    </g>
</svg>

<script>
    const svg = document.getElementById('svgBarChart');

    // 数据
    const data = [60, 80, 120, 180, 150, 200];
    const labels = ['A', 'B', 'C', 'D', 'E', 'F'];

    // 柱状图参数
    const barWidth = 40;
    const padding = 10;

    data.forEach((value, index) => {
        const x = padding + index * (barWidth + padding);
        const y = 250 - value;

        // 绘制柱子
        const rect = document.createElementNS('http://www.w3.org/2000/svg', 'rect');
        rect.setAttribute('x', x);
        rect.setAttribute('y', y);
        rect.setAttribute('width', barWidth);
        rect.setAttribute('height', value);
        rect.setAttribute('fill', '#007acc');
        svg.appendChild(rect);

        // 绘制X轴标签
        const text = document.createElementNS('http://www.w3.org/2000/svg', 'text');
        text.setAttribute('x', x + barWidth / 3);
        text.setAttribute('y', 270);
        text.textContent = labels[index];
        svg.appendChild(text);

        // 显示数值
        const valueText = document.createElementNS('http://www.w3.org/2000/svg', 'text');
        valueText.setAttribute('x', x + barWidth / 3);
        valueText.setAttribute('y', y - 5);
        valueText.textContent = value;
        svg.appendChild(valueText);
    });
</script>
</body>
</html>

不过在正式项目开发中,我们会选择使用 d3 来开发自定义图表。整个过程会变得非常简单。

D3.js 是一个强大的数据驱动文档(Data-Driven Documents)库,它可以用来绑定数据,并动态生成基于数据的可视化内容。它大大简化了 SVG 图形的创建和更新。

D3.js 重构后的柱状图代码

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>D3 Bar Chart with Animation</title>
    <script src="https://d3js.org/d3.v7.min.js"></script>
    <style>
        svg {
            width: 400px;
            height: 300px;
        }
    </style>
</head>
<body>
    <svg id="svgBarChart"></svg>

    <script>
        // 数据
        const data = [60, 80, 120, 180, 150, 200];
        const labels = ['A', 'B', 'C', 'D', 'E', 'F'];
        
        // SVG 宽高
        const svgWidth = 400;
        const svgHeight = 300;
        const barWidth = 40;
        const padding = 20;
        const axisOffset = 40;

        // 创建 SVG 容器
        const svg = d3.select('#svgBarChart')
            .attr('width', svgWidth)
            .attr('height', svgHeight);
        
        // 定义比例尺
        const xScale = d3.scaleBand()
            .domain(labels)
            .range([axisOffset, svgWidth - padding])
            .padding(0.1);

        const yScale = d3.scaleLinear()
            .domain([0, d3.max(data)])
            .range([svgHeight - axisOffset, 0]);

        // 绘制X轴
        svg.append('g')
            .attr('transform', `translate(0,${svgHeight - axisOffset})`)
            .call(d3.axisBottom(xScale));

        // 绘制Y轴
        svg.append('g')
            .attr('transform', `translate(${axisOffset},0)`)
            .call(d3.axisLeft(yScale).ticks(5));

        // 创建柱子
        svg.selectAll('.bar')
            .data(data)
            .enter()
            .append('rect')
            .attr('class', 'bar')
            .attr('x', (d, i) => xScale(labels[i]))
            .attr('y', svgHeight - axisOffset)  // 初始位置在X轴线上
            .attr('width', xScale.bandwidth())
            .attr('height', 0)  // 初始高度为 0
            .attr('fill', '#007acc')
            .transition()  // 动画开始
            .duration(1000)  // 动画持续时间
            .ease(d3.easeElasticOut)  // 弹性缓动函数
            .attr('y', d => yScale(d))  // 更新Y位置
            .attr('height', d => svgHeight - axisOffset - yScale(d));  // 更新高度

        // 添加数值标签
        svg.selectAll('.label')
            .data(data)
            .enter()
            .append('text')
            .attr('class', 'label')
            .attr('x', (d, i) => xScale(labels[i]) + xScale.bandwidth() / 2)
            .attr('y', d => yScale(d) - 5)  // 初始位置在柱子上方
            .attr('text-anchor', 'middle')
            .text(d => d)
            .style('opacity', 0)  // 初始透明度为 0
            .transition()  // 为文本添加动画
            .delay(1000)  // 延迟 1 秒,等待柱子动画完成
            .duration(500)
            .style('opacity', 1);  // 动画后变为可见
    </script>
</body>
</html>

关键点说明:

  1. D3.js 选择器 ( d3.select )

    • 使用 d3.select 来选取 SVG 容器,并动态创建图形元素(如矩形、文字等)。
  2. 比例尺 ( scale )

    • 使用 d3.scaleBand 创建横向的刻度比例尺,用来计算每个柱子的位置。
    • 使用 d3.scaleLinear 创建纵向的刻度比例尺,用来计算每个柱子的高度。
  3. 坐标轴 ( axis )

    • 使用 d3.axisBottomd3.axisLeft 来生成 X 轴和 Y 轴。
  4. 过渡动画 ( transition )

    • 为柱状图添加了弹性缓动动画,使用 d3.transition()d3.easeElasticOut() 来实现柱子由小到大的动画效果。
    • 柱子高度和位置的变化在 1 秒内完成。
    • 数值标签动画通过延迟 ( delay ) 来同步,使得它们在柱子动画完成后逐渐显示。
  5. 动画效果

    • 柱子会从底部开始逐渐伸展,并带有弹性效果,类似于弹簧被拉伸后回弹的视觉感。
    • 数值标签也通过动画淡入效果展示。

中高级 】请详细说说可视化引擎设计与开发细节

可视化引擎的设计和开发涉及许多复杂的工程和技术要点。它的目标是将数据通过高效的渲染方式呈现给用户,通常用于生成各种图表、地图、三维场景等。一个优秀的可视化引擎不仅要有高效的 渲染性能 ,还要有灵活的 扩展性 、强大的 交互能力 和良好的 用户体验

可视化引擎设计要点

架构设计

可视化引擎通常采用模块化的设计来提高代码的可维护性、扩展性以及复用性。一个典型的可视化引擎架构可以分为以下几个模块:

  • 数据层 :负责接收并处理用户输入的数据。可以支持多种数据格式,如 CSV、JSON 等。数据层还需支持数据转换、过滤、聚合等操作。

  • 渲染层 :负责将数据映射为可视化图形。渲染层通常与底层的图形库(如 Canvas、SVG 或 WebGL)紧密结合,负责将图表实际绘制到屏幕上。

  • 组件层 :组件层由各种可视化组件构成,比如图形、坐标轴、图例、工具栏等。这些组件是可视化引擎的核心,负责处理各类图表的绘制逻辑。

  • 交互层 :提供用户与图表的交互功能,如缩放、拖拽、点击、悬停等。交互层负责捕捉用户事件并将其反馈到图表上。

  • 布局系统 :负责图表的布局计算,确保各个组件合理排布,避免重叠和错位。通常会通过计算布局来分配图例、坐标轴和图形元素的空间。

渲染引擎设计

渲染是可视化引擎的核心功能,主要有以下三种常用的渲染技术:

  • Canvas :基于像素的绘图方法,适用于大量数据的快速渲染。它对每个像素进行操作,因此在图形更新时需要重新绘制整个图表。
  • SVG :基于 DOM 的矢量图形技术,适合小规模、可交互和需要高精度操作的场景。SVG 的性能不如 Canvas,但由于其 DOM 操作方便,适合复杂的图形和小数据集。
  • WebGL :基于 GPU 的渲染技术,支持三维图形和大规模数据的高性能渲染。WebGL 能在图形性能和动态更新方面表现出色,适合复杂的三维可视化和高数据量场景。

渲染引擎的关键设计点

  • 渲染目标管理 :根据用户设备选择合适的渲染目标,比如对低性能设备使用 SVG 或 Canvas,而对高性能设备使用 WebGL。
  • 离屏渲染 :为提高性能,可以使用离屏缓冲区绘制复杂的图形或图层,然后将结果合成到主屏幕上。
  • 分层渲染 :将图表的不同部分分为多个层,如背景、网格、数据点、标签等,以减少不必要的重绘。只有发生变化的图层需要更新。

数据驱动模型

可视化引擎的核心是**数据驱动**。数据是如何映射到可视化元素上的,决定了引擎的灵活性和功能性。一个好的可视化引擎应该支持以下特性:

  • 数据到图形的映射 :数据与图形元素之间的映射关系由用户通过配置来定义。例如,折线图中,X 轴代表时间,Y 轴代表数值。数据映射可以通过线性比例尺、对数比例尺或分类比例尺来处理。

  • 数据的动态更新 :引擎应该支持数据的实时更新。当数据变化时,图表也能动态响应变化。引擎需要追踪数据的变化,并只更新发生变化的部分,而不是全部重新渲染。

  • 数据转换与聚合 :在可视化之前,数据可能需要进行一些预处理,例如排序、分组、聚合等操作。引擎应提供便捷的 API 来处理这些操作,支持用户根据需求自定义数据转换逻辑。

组件系统设计

一个灵活的可视化引擎通常采用**组件化**设计,使得各个部分(如坐标轴、图形、图例、工具栏等)可以独立开发、复用和组合。每个组件通常需要有以下能力:

  • 独立渲染 :每个组件负责自己的渲染逻辑,比如坐标轴组件渲染刻度、标签,图形组件负责渲染折线或柱状图等。

  • 配置化设计 :组件应该支持用户通过配置来定制其样式、行为。例如,用户可以定义坐标轴的刻度数量、图例的位置、图形的颜色等。

  • 生命周期管理 :组件应该有完整的生命周期管理机制,包括初始化、更新、销毁等阶段。引擎在数据变化时,能够通知相关组件进行更新,而不会影响其他部分。

交互设计

交互是现代可视化引擎的重要功能,用户需要通过鼠标或触摸屏与图表进行交互操作。常见的交互设计包括:

  • 鼠标悬停与点击 :用户鼠标悬停或点击图形时,显示相应的数据提示框(tooltip)或触发一些行为(如选择某一数据点)。

  • 缩放与拖拽 :在折线图或地图等图表中,用户需要通过鼠标滚轮或触摸手势进行缩放,或者通过拖拽查看不同的数据区域。引擎需要支持平滑的缩放与拖拽操作,并能够实时更新可视化内容。

  • 事件驱动 :引擎需要提供事件监听机制,允许用户捕捉特定的交互事件(如点击、双击、悬停等)并执行相应的回调操作。

动画与过渡

动画可以大大提升用户体验,特别是在数据变化或图表更新时,使用平滑的动画能够帮助用户更好地理解数据的变化。可视化引擎中的动画设计包括:

  • 初始动画 :图表首次加载时,可以通过动画(如柱状图从底部逐渐伸展、折线图线条逐渐绘制出来等)让图表内容更具动态性。

  • 过渡动画 :当数据发生变化时,图表可以通过过渡动画来平滑更新,例如条形图的条形长度缓慢变化而不是突然更新,这种动画过渡可以提升用户的视觉体验。

  • 自定义缓动函数 :支持多种缓动函数(如线性、弹性、缓入缓出等),以控制动画效果的加速、减速或回弹行为。

性能优化

可视化引擎的性能至关重要,尤其是在处理大量数据或复杂交互时。常见的性能优化策略包括:

  • 虚拟 DOM :类似于 React 的虚拟 DOM 技术,先在内存中计算好渲染结果,然后只将变化部分渲染到真实 DOM 中,从而减少不必要的 DOM 操作。

  • 增量更新 :当数据或图表配置发生变化时,只更新受影响的部分,而不是重新绘制整个图表。这可以通过精细的状态管理来实现。

  • GPU 加速 :对于高数据量的场景,可以使用 WebGL 进行 GPU 加速渲染。WebGL 能通过 GPU 并行计算,大大提升渲染性能。

  • 大数据可视化 :在面对大量数据时,不能直接渲染所有数据点,需要采用下采样、聚合或分页的方式,只显示必要的数据子集。例如,显示趋势图时,可以只绘制关键的拐点或峰值。

跨平台与兼容性

现代可视化引擎通常需要支持多种平台和设备,包括桌面浏览器、移动设备,甚至是服务器端渲染。因此,在设计时需要考虑:

  • 响应式设计 :通过 CSS 或 JavaScript 动态调整图表的尺寸和布局,以适应不同大小的屏幕。引擎需要能够自适应窗口大小变化,动态调整图表的布局和比例。

  • 设备兼容性 :支持触屏设备的手势操作,如缩放、拖拽等交互行为。对于移动设备,确保图表的渲染

性能和交互体验。

扩展性与插件系统

为了使可视化引擎具备更高的扩展性,应该设计成插件化结构,使开发者能够在引擎的基础上扩展新的功能或图表类型。

  • 插件系统 :允许开发者通过插件形式扩展引擎的功能,如自定义图表类型、特殊的渲染效果、扩展交互行为等。

  • 模块化架构 :每个功能或组件尽可能模块化,便于开发者在不修改核心代码的情况下,扩展或替换某些功能。

可视化引擎代码实现

为了实现一个扩展性良好的可视化引擎,结合 插件化设计思想策略模式 ,我们可以将图表的绘制逻辑与图表类型分离,通过定义通用的 图表数据协议 ,使引擎可以支持更多的图表类型(如折线图、饼图等)。插件化设计和策略模式可以让我们在不修改核心代码的情况下,通过添加新的插件或策略来扩展引擎的功能。

图表数据协议设计

图表数据协议是整个可视化引擎的核心,用于定义图表的输入格式,使得不同的图表类型(如柱状图、折线图、饼图等)可以基于相同的数据结构进行绘制。

通用的数据协议设计思路:

  • type :图表的类型(如 bar、line、pie 等)。
  • data :图表的数据信息。通常包含数据值、标签等。
  • options :图表的配置选项。可以包含样式、颜色、动画设置、比例尺等。
  • series :定义多组数据时使用的配置项,比如多条折线、多个柱状图系列。

示例数据协议:

{
  "type": "bar", // 图表类型
  "data": {
    "labels": ["A", "B", "C", "D", "E", "F"],
    "values": [60, 80, 120, 180, 150, 200]
  },
  "options": {
    "title": "Example Bar Chart",
    "xAxisLabel": "Categories",
    "yAxisLabel": "Values",
    "colors": ["#007acc", "#ff5733", "#33ff57", "#f7e033", "#9c33ff", "#ff3385"]
  }
}

插件化设计思想

  • 核心引擎 :核心引擎只负责管理渲染、布局和数据协议的解析,不直接耦合具体图表的绘制逻辑。
  • 插件系统 :通过插件化设计,图表的绘制逻辑以插件的形式存在。每种图表类型的插件实现其特定的绘制逻辑,核心引擎根据图表类型动态加载相应的插件进行绘制。

策略模式

通过 策略模式设计 ,我们可以将不同图表类型的绘制算法封装成不同的策略类。引擎根据传入的数据协议,选择合适的策略来渲染图表。策略模式使得图表的类型扩展非常简单,只需要增加新的策略类。

具体实现架构

我们首先定义图表绘制的策略接口,之后为每种图表类型(如柱状图、折线图等)实现具体的策略类。

图表绘制渲染器接口( ChartRenderer ):

class ChartRenderer {
  draw(svg, data, options) {
    throw new Error("This method should be overridden by subclasses");
  }
}

柱状图渲染器( BarChartRenderer ):

class BarChartRenderer extends ChartRenderer {
  draw(svg, data, options) {
    const labels = data.labels;
    const values = data.values;
    const colors = options.colors || ["#007acc"];
    
    // 定义比例尺
    const xScale = d3.scaleBand()
      .domain(labels)
      .range([40, svg.attr('width') - 20])
      .padding(0.1);

    const yScale = d3.scaleLinear()
      .domain([0, d3.max(values)])
      .range([svg.attr('height') - 40, 0]);

    // 绘制X轴
    svg.append('g')
      .attr('transform', `translate(0,${svg.attr('height') - 40})`)
      .call(d3.axisBottom(xScale));

    // 绘制Y轴
    svg.append('g')
      .attr('transform', `translate(40,0)`)
      .call(d3.axisLeft(yScale).ticks(5));

    // 绘制柱状图
    svg.selectAll('.bar')
      .data(values)
      .enter()
      .append('rect')
      .attr('class', 'bar')
      .attr('x', (d, i) => xScale(labels[i]))
      .attr('y', yScale(0))
      .attr('width', xScale.bandwidth())
      .attr('height', 0)
      .attr('fill', (d, i) => colors[i % colors.length])
      .transition()
      .duration(1000)
      .ease(d3.easeElasticOut)
      .attr('y', d => yScale(d))
      .attr('height', d => svg.attr('height') - 40 - yScale(d));
  }
}

折线图渲染器( LineChartRenderer ):

class LineChartRenderer extends ChartRenderer {
  draw(svg, data, options) {
    const labels = data.labels;
    const values = data.values;
    const color = options.color || "#007acc";

    const xScale = d3.scalePoint()
      .domain(labels)
      .range([40, svg.attr('width') - 20]);

    const yScale = d3.scaleLinear()
      .domain([0, d3.max(values)])
      .range([svg.attr('height') - 40, 0]);

    // 绘制X轴
    svg.append('g')
      .attr('transform', `translate(0,${svg.attr('height') - 40})`)
      .call(d3.axisBottom(xScale));

    // 绘制Y轴
    svg.append('g')
      .attr('transform', `translate(40,0)`)
      .call(d3.axisLeft(yScale).ticks(5));

    // 绘制折线
    const line = d3.line()
      .x((d, i) => xScale(labels[i]))
      .y(d => yScale(d))
      .curve(d3.curveMonotoneX);  // 平滑曲线

    svg.append('path')
      .datum(values)
      .attr('fill', 'none')
      .attr('stroke', color)
      .attr('stroke-width', 2)
      .attr('d', line)
      .attr('opacity', 0)
      .transition()
      .duration(1000)
      .attr('opacity', 1);
  }
}

引擎核心逻辑( ChartEngine

引擎核心根据图表类型选择不同的策略来绘制图表。不同的图表类型对应不同的绘制策略。

class ChartEngine {
  constructor() {
    this.strategies = {
      'bar': new BarChartRenderer(),
      'line': new LineChartRenderer(),
      // 可以继续添加其他图表策略,如饼图、散点图等
    };
  }

  drawChart(svg, config) {
    const { type, data, options } = config;
    
    if (!this.strategies[type]) {
      throw new Error(`No Renderer found for chart type: ${type}`);
    }

    this.strategies[type].draw(svg, data, options);
  }
}

使用引擎渲染图表

我们可以通过调用 ChartEngine ,并传入相应的图表配置来渲染不同类型的图表。

示例:渲染柱状图

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Plugin-based Chart Engine with D3.js</title>
  <script src="https://d3js.org/d3.v7.min.js"></script>
  <style>
    svg {
      width: 400px;
      height: 300px;
    }
  </style>
</head>
<body>
  <svg id="chart"></svg>

  <script>
    const chartConfig = {
      type: 'bar',  // 可切换为 'line' 等其他类型
      data: {
        labels: ['A', 'B', 'C', 'D', 'E', 'F'],
        values: [60, 80, 120, 180, 150, 200]
      },
      options: {
        title: 'Example Bar Chart',
        colors: ['#007acc', '#ff5733', '#33ff57', '#f7e033', '#9c33ff', '#ff3385']
      }
    };

    const svg = d3.select('#chart');
    const chartEngine = new ChartEngine();

    chartEngine.drawChart(svg, chartConfig);  // 渲染图表
  </script>
</body>
</html>

扩展性与插件支持

由于每种图表类型的绘制逻辑被封装在各自的策略类中,扩展新图表类型非常简单,只需:

  1. 创建一个新的策略类,实现 draw 方法。
  2. ChartEngine 中注册该策略。

此外,通过插件系统,外部开发者可以很方便地为引擎添加新的图表类型、布局算法或交互方式,而不需要修改引擎的核心逻辑。

专家级 】从零到一架构一个类似字节 DataWind 大数据可视化平台,简要说说你的设计思路

分层设计

数据源层设计

数据源层 主要负责处理数据的接入和存储,作为整个系统的数据基础,确保能够灵活、高效地从多种不同的数据源获取数据。

数据接入

在 Node.js 服务端,可以利用多种库和工具来实现数据接入:

  • Kafka :使用 kafka-nodekafkajs 库接入流式数据。Kafka 适合实时性高的数据传输,可以高效处理大数据量的日志或事件流。
  • 数据库 :对于关系型数据,可以通过 node-postgresmysql 库接入 PostgreSQL、MySQL 数据库,实现数据的存储与查询。
  • 对象存储 :通过 AWS SDK 或阿里云 SDK,接入如 Amazon S3 或阿里云 OSS 等对象存储,适合存储大型数据文件。
  • HTTP/REST API 数据源 :对于通过 API 接入的数据源,使用 axios 或内置的 http 模块进行 HTTP 请求,定期获取数据。

数据存储

根据数据类型选择合适的存储方案:

  • 时序数据 :可使用 InfluxDB 来处理和存储时序数据,Node.js 端通过 influx 库与 InfluxDB 进行交互。
  • 列式存储 :对于查询需求较强的数据,可使用 ClickHouse,Node.js 可以通过 clickhouse 库与其连接,支持大规模数据的快速查询。
  • NoSQL 数据库 :如 MongoDB ,可以通过 mongoose 库进行文档型数据的存储和查询,适用于半结构化数据的存储。

数据处理层设计

数据处理层的核心在于对原始数据进行清洗、转换、聚合,以便后续分析和展示。

数据清洗

  • Flink 和 Spark :数据清洗的分布式处理任务可由 Apache Flink 或 Spark 完成。Node.js 中可以通过 grpcrestful 接口与这些处理引擎通信,实现数据的异步处理。
  • Node.js Stream :对于需要实时清洗的小规模流式数据,Node.js 的 stream 模块也可以进行有效处理。通过管道流对数据进行预处理,包括去重、格式标准化、数据补全等。

数据转换与聚合

  • Flink 实时处理 :可以通过 Kafka 将数据流传递到 Flink,Flink 实时处理后再传回 Node.js 进行后续处理。Node.js 可以使用 kafkajs 库从 Flink 中获取处理好的数据。
  • Node.js 聚合处理 :对于非高并发场景,Node.js 本地可通过 MapReduce 模式对批量数据进行聚合。使用 lodash 等工具库对数据进行分组、统计、计算等操作。

缓存与索引

  • Redis 缓存 :针对热门查询和数据集,可在 Node.js 中集成 redis 库,将热点数据缓存到 Redis,减少后端查询负担。
  • Elasticsearch :对于需要快速全文搜索的数据,可以通过 @elastic/elasticsearch 库与 Elasticsearch 进行交互,建立索引并提供全文检索服务。

数据分析层设计

数据分析层负责对清洗、转换后的数据进行进一步处理,并提供分析结果给可视化层。

查询系统

  • Presto 或 Druid 集成 :可以将数据导入 Presto 或 Apache Druid,Node.js 可以通过 REST API 与 Presto 或 Druid 进行交互,处理用户的复杂 SQL 查询,返回快速的分析结果。
  • ClickHouse :对于超大规模数据查询,可使用 clickhouse 库对接 ClickHouse。ClickHouse 擅长处理列式存储和大规模聚合查询,非常适合 OLAP(在线分析处理)。

算法模型支持

  • Node.js 调用机器学习模型 :通过 REST API 将数据传递给 TensorFlow Serving 或 PyTorch Serve 等模型服务,Node.js 在接收到分析结果后,再将其返回前端用于展示或进一步计算。
  • Spark 集成 :对于大数据下的机器学习算法,可以借助 Apache Spark 的 MLib,Node.js 通过 HTTP 接口与 Spark 集群通信,进行大规模数据的模型训练和预测。

实时数据分析

  • Flink 实时分析 :对于流数据,Flink 能够实时处理并返回结果,Node.js 可从 Kafka 或 Redis 订阅实时结果,将这些数据直接推送到前端展示。
  • Socket.IO 推送 :Node.js 使用 Socket.IO 推送实时分析结果,前端通过 WebSocket 实时更新可视化内容,保证数据的实时性。

可视化展示层设计

可视化展示是平台的核心功能,确保用户能够直观地看到数据变化和分析结果。

前端技术选择

  • Echarts 与 D3.js :前端使用 Echarts 和 D3.js 进行动态数据展示,支持多种图表形式,如折线图、饼图、散点图、热力图等。通过 WebSocket 实现实时数据更新,用户能够看到数据的动态变化。
  • WebGL 支持 :对于大规模的可视化场景,如海量数据点的绘制,Echarts 也支持 WebGL 渲染,加速数据点的展示,提升用户体验。

仪表盘系统

  • 可配置仪表盘 :前端可以实现类似 Superset 或 Grafana 的自定义仪表盘功能。用户能够拖拽不同的可视化组件,配置不同的数据源,并根据自己的需求调整布局。
  • 可视化编辑器 :提供一个基于 Node.js 的可视化编辑器服务,允许用户通过图形界面设计数据图表。图表配置通过 JSON 格式存储,前端根据该配置渲染不同的图表。

数据下采样

  • 大数据量渲染优化 :为了避免前端直接渲染过大的数据集,可以在 Node.js 中进行数据下采样,使用 simplify.js 等工具对数据点进行简化处理,保持数据趋势不变的同时减少前端计算量。

用户交互层设计

用户交互层旨在为用户提供友好、灵活的操作界面和查询工具。

用户权限管理

  • OAuth 2.0 :通过 passport.js 集成 OAuth 2.0 实现用户认证和授权,用户可以通过第三方服务(如 Google、GitHub)登录系统。
  • RBAC(基于角色的权限控制) :Node.js 中实现基于角色的权限控制系统,通过定义用户角色(管理员、分析师、访客等),不同角色访问不同功能和数据集。

自定义查询与多维度分析

  • SQL 查询界面 :前端提供 SQL 编辑器,用户可以编写自定义查询,后端 Node.js 将这些查询请求转发到 Presto 或 ClickHouse,返回查询结果。
  • 多维度数据分析 :用户可以在前端选择不同的维度和指标,Node.js 后端通过解析用户选择,构造相应的查询,支持钻取分析和交叉分析。

数据报表导出与分享

  • 报表生成与导出 :Node.js 使用 pdfkitpuppeteer 生成 PDF 格式的报表,同时支持导出 Excel 表格。用户可以通过界面自定义报表内容和格式。
  • 分享与协作 :用户能够生成仪表盘的共享链接,或将当前数据报告分享到团队内部,通过 URL 可以直接访问仪表盘。

平台的可扩展性

平台的扩展性设计能够保证系统面对大量用户和数据时的可用性。

弹性伸缩

  • 基于云服务的弹性伸缩 :使用如 AWS Lambda 或 Kubernetes 进行服务容器化与弹性伸缩。Node.js 服务端可以通过 Kubernetes 自动管理实例的扩展和缩减,保证在高负载下仍然能快速响应。

微服务架构

  • Node.js 微服务拆分 :将平台按功能模块拆分成多个微服务,如数据处理服务、查询服务、可视化服务等。通过 grpc

RESTful API 进行微服务间的通信。

  • 负载均衡 :使用 Nginx 或基于云服务的负载均衡器(如 AWS ELB)分发请求,确保多个服务节点之间的流量均衡,提升系统的并发处理能力。

监控与报警

  • Prometheus + Grafana :使用 Prometheus 监控 Node.js 服务的性能指标,如内存使用、CPU 负载、请求延迟等。通过 Grafana 设置可视化报警面板,当出现异常情况时自动触发报警(如邮件或 Slack 通知)。

技术栈总结

  • 前端 :React / Vue.js + Echarts / D3.js。
  • 后端 :Node.js + RESTful API / gRPC。
  • 数据处理 :Kafka + Flink / Spark + Presto / Druid / ClickHouse。
  • 数据存储 :MongoDB / ClickHouse / HDFS / Elasticsearch。
  • 缓存与搜索 :Redis + Elasticsearch。
  • 用户管理 :OAuth 2.0 / JWT + RBAC。

核心实现

因为是全栈项目,我们依然选择使用 nestjs 来讲解,如果你在纠结 nodejs 服务端最终企业及应用如何选择技术栈,那么我也推荐 nestjs。

NestJS 项目初始化

首先,使用 NestJS CLI 初始化项目:

npm i -g @nestjs/cli
nest new datawind-backend

接着,按照需求安装必要的依赖库:

npm install @nestjs/mongoose @nestjs/jwt passport-jwt kafkajs @nestjs/redis clickhouse ioredis axios

Kafka 数据流处理模块

使用 Kafka 作为数据流处理的核心,借助 kafkajs 在 NestJS 中实现 Kafka 生产者和消费者。

Kafka 模块配置

kafka.module.ts 中配置 Kafka 连接。

// src/kafka/kafka.module.ts
import { Module } from '@nestjs/common';
import { KafkaService } from './kafka.service';

@Module({
  providers: [KafkaService],
  exports: [KafkaService],
})
export class KafkaModule {}

Kafka 服务

创建 Kafka 服务来管理生产者和消费者。

// src/kafka/kafka.service.ts
import { Injectable, OnModuleInit } from '@nestjs/common';
import { Kafka } from 'kafkajs';

@Injectable()
export class KafkaService implements OnModuleInit {
  private kafka = new Kafka({
    clientId: 'datawind',
    brokers: ['localhost:9092'],
  });

  private producer = this.kafka.producer();
  private consumer = this.kafka.consumer({ groupId: 'datawind-group' });

  async onModuleInit() {
    await this.producer.connect();
    await this.consumer.connect();

    await this.consumer.subscribe({ topic: 'data-stream', fromBeginning: true });
    await this.consumer.run({
      eachMessage: async ({ topic, partition, message }) => {
        console.log(`Received message: ${message.value.toString()}`);
        // 处理数据并保存到数据库(ClickHouse 或其他)
      },
    });
  }

  async sendMessage(topic: string, message: string) {
    await this.producer.send({
      topic,
      messages: [{ value: message }],
    });
  }
}

ClickHouse 数据存储模块

通过 clickhouse 库连接 ClickHouse 数据库,存储和查询大规模数据。

ClickHouse 模块配置

创建 ClickHouse 模块和服务。

// src/clickhouse/clickhouse.module.ts
import { Module } from '@nestjs/common';
import { ClickhouseService } from './clickhouse.service';

@Module({
  providers: [ClickhouseService],
  exports: [ClickhouseService],
})
export class ClickhouseModule {}

ClickHouse 服务

clickhouse.service.ts 中实现与 ClickHouse 的连接和操作。

// src/clickhouse/clickhouse.service.ts
import { Injectable } from '@nestjs/common';
import { Client } from 'clickhouse';

@Injectable()
export class ClickhouseService {
  private clickhouse = new Client({
    url: 'http://localhost',
    port: 8123,
  });

  async insertData(eventTime: string, value: number) {
    const query = `INSERT INTO my_table (event_time, value) VALUES ('${eventTime}', ${value})`;
    return this.clickhouse.query(query).toPromise();
  }

  async queryData(limit: number) {
    const query = `SELECT * FROM my_table ORDER BY event_time DESC LIMIT ${limit}`;
    return this.clickhouse.query(query).toPromise();
  }
}

ClickHouse 数据表创建

确保在 ClickHouse 中创建了相应的表:

CREATE TABLE my_table (
  event_time DateTime,
  value Float32
) ENGINE = MergeTree()
ORDER BY event_time;

Redis 缓存模块

使用 ioredis 进行缓存操作,提高系统性能,减少频繁的数据库查询。

Redis 模块配置

redis.module.ts 中配置 Redis 连接。

// src/redis/redis.module.ts
import { Module } from '@nestjs/common';
import { RedisService } from './redis.service';
import { RedisModule as NestRedisModule } from 'nestjs-redis';

@Module({
  imports: [
    NestRedisModule.register({
      host: 'localhost',
      port: 6379,
    }),
  ],
  providers: [RedisService],
  exports: [RedisService],
})
export class RedisModule {}

Redis 服务

redis.service.ts 中实现 Redis 缓存逻辑。

// src/redis/redis.service.ts
import { Injectable } from '@nestjs/common';
import { RedisService as NestRedisService } from 'nestjs-redis';
import { Redis } from 'ioredis';

@Injectable()
export class RedisService {
  private redisClient: Redis;

  constructor(private readonly redisService: NestRedisService) {
    this.redisClient = this.redisService.getClient();
  }

  async getCache(key: string): Promise<string> {
    return this.redisClient.get(key);
  }

  async setCache(key: string, value: string, ttl: number) {
    await this.redisClient.set(key, value, 'EX', ttl);
  }
}

RESTful API 控制器

通过 NestJS 控制器提供 RESTful API 接口,前端可以通过这些接口查询数据。

// src/app.controller.ts
import { Controller, Get, Param } from '@nestjs/common';
import { ClickhouseService } from './clickhouse/clickhouse.service';
import { RedisService } from './redis/redis.service';

@Controller('api/data')
export class AppController {
  constructor(
    private readonly clickhouseService: ClickhouseService,
    private readonly redisService: RedisService,
  ) {}

  @Get(':limit')
  async getData(@Param('limit') limit: number) {
    const cacheKey = `recent_data_${limit}`;
    const cachedData = await this.redisService.getCache(cacheKey);

    if (cachedData) {
      return JSON.parse(cachedData);
    }

    const data = await this.clickhouseService.queryData(limit);
    await this.redisService.setCache(cacheKey, JSON.stringify(data), 60);
    
    return data;
  }
}

JWT 用户认证模块

使用 NestJS PassportJWT 实现用户认证和权限管理。

JWT 模块配置

auth.module.ts 中配置 JWT 和 Passport。

// src/auth/auth.module.ts
import { Module } from '@nestjs/common';
import { JwtModule } from '@nestjs/jwt';
import { PassportModule } from '@nestjs/passport';
import { AuthService } from './auth.service';
import { JwtStrategy } from './jwt.strategy';

@Module({
  imports: [
    PassportModule,
    JwtModule.register({
      secret: 'your_jwt_secret',
      signOptions: { expiresIn: '1h' },
    }),
  ],
  providers: [AuthService, JwtStrategy],
  exports: [AuthService],
})
export class AuthModule {}

JWT 策略

jwt.strategy.ts 中定义 JWT 策略。

// src/auth/jwt.strategy.ts
import { Injectable } from '@nestjs/common';
import { PassportStrategy } from '@nestjs/passport';
import { ExtractJwt, Strategy } from 'passport-jwt';

@Injectable()
export class JwtStrategy extends PassportStrategy(Strategy) {
  constructor() {
    super({
      jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
      secretOrKey: 'your_jwt_secret',
    });
  }

  async validate(payload: any) {
    return { userId: payload.sub, username: payload.username };
  }
}

认证服务

auth.service.ts 实现登录和生成 JWT。

// src/auth/auth.service.ts
import { Injectable } from '@nestjs/common';
import { JwtService } from '@nestjs/jwt';

@Injectable()
export class AuthService {
  constructor(private readonly jwtService: JwtService) {}

  async login(user: any) {
    const payload = { username: user.username, sub: user.userId };
    return {
      access_token: this.jwtService.sign(payload),
    };
  }
}

登录 API

// src/auth/auth.controller.ts
import { Controller, Post, Body } from '@nestjs/common';
import { AuthService } from './auth.service';

@Controller('auth')
export class AuthController {
  constructor(private readonly authService: AuthService) {}

  @Post('login')
  async login(@Body() loginDto: { username: string; password: string }) {
    // 验证用户名和密码逻辑
    if (loginDto.username === 'admin' && loginDto.password === 'password') {
      return this.authService.login({ username: 'admin', userId: 1 });
    }
    return { message: 'Invalid credentials' };
  }
}

受保护的路由

在需要受保护的路由中使用 @UseGuards(AuthGuard('jwt')) 进行 JWT 验证。

// src/app

.controller.ts
import { Controller, Get, UseGuards } from '@nestjs/common';
import { AuthGuard } from '@nestjs/passport';

@Controller('api/protected')
export class ProtectedController {
  @Get()
  @UseGuards(AuthGuard('jwt'))
  getProtectedData() {
    return { message: 'This is protected data' };
  }
}

最终项目结构

最终的项目结构可能如下所示:

src/
  ├── app.controller.ts
  ├── auth/
  │   ├── auth.controller.ts
  │   ├── auth.module.ts
  │   ├── auth.service.ts
  │   ├── jwt.strategy.ts
  ├── clickhouse/
  │   ├── clickhouse.module.ts
  │   ├── clickhouse.service.ts
  ├── kafka/
  │   ├── kafka.module.ts
  │   ├── kafka.service.ts
  ├── redis/
  │   ├── redis.module.ts
  │   ├── redis.service.ts
  ├── main.ts

简历优化(30K+ 简历样板)

技能描述

  • 熟练掌握 HTML、CSS、JavaScript、Typescript 以及 OOP、FP、 AOP 等设计思想

  • 掌握样式体系构建与落地,对 css 预编译、css in js、module css 以及 utility-first CSS 有深入研究,并从零改良过样式体系以支持 SSR、SSG

  • 熟悉 React、Vue 相关技术栈,熟悉 React、Vue 及相关技术框架的实现原理

  • 掌握构建工具 Webpack、Vite 等,掌握编译工具 Babel,并深入理解其原理,并参与 Rspack 构建

  • 丰富的数据可视化经验,熟悉 Canvas、svg 开发范式,理解 Echarts、Antv 原理,能根据业务需求基于 d3、zrender 开发自定义渲染引擎

  • 丰富的跨端开发经验,熟练使用 Taro、Flutter、React-Native 开发跨端应用,对构建 hybird App 有丰富经验,深入理解跨端开发编译原理

  • 基于 Node.js 开发脚手架、打包构建优化工具及中间件服务

  • 掌握常用设计模式、算法与安全知识,追求开发高质量、高可维护性代码,追求极致产品体验

  • 团队管理经验,并在项目架构设计与性能优化方面具有丰富经验

  • 算法与编程技术

    • 精通各种算法题的分类及解决方法,包括排序与查找、数据结构、动态规划、贪心算法、回溯算法、分治算法、图论算法、数学算法等。
    • 重点掌握动态规划的基本概念、解题步骤和经典题目。
  • 3D 数字孪生平台开发经验

    • 熟练使用 WebGL 和 WebAssembly 技术,开发高效的 3D 渲染引擎。
    • 精通正射影像和倾斜摄影技术,具备 Tile 和模型(包括白膜和精模)的处理经验,能够使用 Blender 进行模型制作。
    • 熟悉材质、光效和粒子系统的实现与优化。
  • 全面性能优化能力

    • 具备打包构建优化经验,熟练使用 Webpack 进行模块打包,掌握 chunk、treeshaking、happypack、cache-loader 等优化技巧,并使用 Webpack Module Federation 进行模块联邦管理。
    • 精通资源优化,能够有效进行图片、字体压缩,管理请求队列,并通过 OSS 和 CDN 提升资源加载速度。
    • 具有应用性能优化经验,包括数据结构优化和应用模块更新。
    • 深入了解缓存机制,熟悉强缓存(Expiration、Cache-Control)、协商缓存(Etag)和策略缓存(Service-Worker)的配置与管理。

项目描述

这个环节至关重要,很多同学不重视,简历随便写一写就开始投递了,结果投出去几百份可能一家公司面试都没有,结果就在怀疑前端行情出问题了

STAR 法则:

  • 遇到了什么问题 question,需求
  • 怎么评估解决方案,方案对比,方案落地 react 状态管理(redux、mobx、jotai、recoil)Vue3 -> Pinia
  • 具体方案落地
  • 结果反思,细节优化思考

大家先看这段描述:

技术栈:Java、Vue2、echarts、WEui、BaiduMap、JavaScript 、HTTP数据库:MySQL管理工具:SVN

责任描述:

1)产品前端研发负责人,主要负责整体样式沟通,样式调配实现;门户、管理平台、移动端、智端、可视化等前端内容实现;

2)实现组织管理、人员管理、党员关系转接、待办通知、绩效考核(复杂功能算法实现)、发展党员(25个流程)、可视化等主体功能,兼容性优化、适配1920*1080屏幕以及响应式布局实现;

3)相关功能开发,包含前后端、数据库;

4)门户框架搭建、门户整体设计、后端接口、门户前端 UI 实现等;

5)微信小程序框架搭建、小程序页面设计及开发、知识图谱技术预演;

6 )项目经理工作辅助,包含需求沟通、UI设计沟通、交付材料项目经历整理、前端代码质量管理、部分功能设计。

深度优化一下

工作内容和成果

  • 架构设计 】参与智慧管理平台整体架构设计、技术选型与方案评审,担任全栈开发,完成相关核心模块
  • 企微开发 】对接企业微信生态,基于企微 SDK 完成平台支付、消息推送、机器人等功能开发
  • 可视化 】主导完成平台可视化渲染引擎(可视化图表的组件,数据协议)设计与开发,基于 echarts (svgRenderer、canvasRenderer 一千万行数据的表格渲染【不能使用 虚拟滚动 】 canvas table,chunk)封装业务图表库,服务于平台可视化场景
  • 地图开发 】使用百度地图 SDK,封装业务地图渲染器(MapRenderer),包含:地图撒点、地区数据下钻等功能
  • 小程序与App 】基于 uniapp 实现智慧党建用户端多端开发落地,产物编译为 H5、微信小程序两端应用
  • 团队基建 】推进团队业务组件库、图表库与基础库沉淀,完成 10+ 个业务组件沉淀,以此提升了团队协同开发效率
  • 优化 】设计产品响应式系统,基于 media query 设计响应式端点规则,适配不同端应用的展示
  • 自研 OA 打通 】...

STAR

【技术栈】

  • Java、Vue2、ECharts、WEui、BaiduMap、JavaScript、HTTP
  • 数据库 :MySQL
  • 管理工具 :SVN

【产品前端研发负责人】

  • 情境 (Situation) :担任产品前端研发负责人,负责门户、管理平台、移动端、智端、可视化等前端内容的实现。
  • 任务 (Task) :主要任务是与设计团队沟通,调配和实现整体样式,确保产品界面的一致性和用户体验。
  • 行动 (Action) :我协调设计与开发团队,定期召开样式沟通会,亲自进行样式的调配与实现,并负责不同平台和设备的前端内容开发。
  • 结果 (Result) :成功实现了多个平台的前端开发工作,提升了产品的用户体验和一致性,获得了团队和用户的高度评价。

【实现复杂功能及优化】

  • 情境 (Situation) :项目需要实现复杂功能算法和大规模功能模块,包括组织管理、人员管理、党员关系转接、待办通知、绩效考核、发展党员(25个流程)和可视化功能。
  • 任务 (Task) :负责上述复杂功能的实现,并优化其兼容性和响应式布局。
  • 行动 (Action) :通过设计和实现复杂算法,确保各模块的功能性;进行兼容性优化,使系统适配1920*1080屏幕和响应式布局。
  • 结果 (Result) :成功实现并优化了所有复杂功能,系统在不同设备和分辨率下均表现良好,提高了用户操作的流畅度和满意度。

【全面功能开发】

  • 情境 (Situation) :需要进行前后端和数据库的全面开发,确保系统各功能模块的无缝集成。
  • 任务 (Task) :开发和实现相关功能,包括前端UI、后端接口和数据库交互。
  • 行动 (Action) :采用Java、Vue2等技术,开发并调试各功能模块,与后端团队密切合作,确保接口的准确性和数据的一致性。
  • 结果 (Result) :成功完成了所有功能模块的开发和集成,系统运行稳定,性能优异,受到了客户的好评。

【微信小程序开发与知识图谱预演】

  • 情境 (Situation) :项目需要开发微信小程序,并进行知识图谱技术的预演。
  • 任务 (Task) :负责小程序框架的搭建、页面设计及开发,同时进行知识图谱的技术预演。
  • 行动 (Action) :使用WEui、JavaScript等技术,设计并开发小程序页面,进行知识图谱的技术预演和验证。
  • 结果 (Result) :成功搭建了微信小程序框架,完成了页面设计和开发工作,知识图谱技术预演顺利,通过了技术验证。

【项目经理工作辅助】

  • 情境 (Situation) :在项目中辅助项目经理,确保项目需求沟通顺畅,UI设计协调到位,交付材料齐全,前端代码质量高。
  • 任务 (Task) :辅助项目经理进行需求沟通、UI设计沟通、交付材料整理、前端代码质量管理以及部分功能设计。
  • 行动 (Action) :积极参与需求和UI设计的沟通,整理和管理项目交付材料,进行代码审查和质量管理,并参与功能设计。
  • 结果 (Result) :成功辅助项目经理完成了项目的各项工作,提高了项目的开发效率和交付质量,确保了项目的顺利进行。

其实还不够,这些项目才是求职香饽饽

你可能不具备这些项目的实战经验,很多同学写了很多年管理系统,简单增删改查项目,如果不跳出这个圈子,很难在薪资上有非常大的突破!

  • 大厂 UI 组件库(Vue3)整体设计与开发实践(monorepo 架构)

  • 大厂业务 Hooks 库(React 18)整体设计与开发实践(从零到一的架构、规范流程)

  • 企业级脚手架工具开发实践

  • 企业级文档编辑器飞书文档开发实践

  • 前端性能、异常与行为监控

  • 3D 可视化数字孪生低代码实战 💥

    • 基于 cesium(arcGis、超图) 方案的 WebGIS 开发实践
    • 基于 openlayer、mapbox 开发
    • 基于 WebGL 3D 可视化开发实践

年包 50W+ 薪资长线规划

核心要素

  1. 全面的技术储备

    1. 框架基础

      • Vue 和 React 经验:包括 Vue3 + Typescript 和 React18(Hooks、Concurrent)
      • 掌握框架原理:React 生态库(React-Router、Redux)和 Vue 生态库原理
    2. 工程化能力

      • 构建工具:Webpack、Vite、Rspack、ESBuild、swc
      • CI/CD 自动化:自动化构建和自动化部署
    3. 基建能力

      • Node.js、命令行工具开发(Cli)
      • UI 库、图表库、工具库开发
    4. 业务领域经验

      • 管理系统和图表类应用
      • 可视化、编辑器、云表格、低代码平台、SaaS 产品、数字孪生、三维可视化
  2. 项目经验

    • 参与过至少两个大型项目,并主导过一个复杂项目
    1. 管理系统:包括项目搭建、技术方案选择、技术栈构建、CI/CD 流程
    2. 其他领域项目:如可视化、编辑器、云表格、低代码平台、SaaS 产品、数字孪生、三维可视化
  3. 面试表现

    1. 个人介绍

      • 准备个人介绍草稿,涵盖基本信息、技术栈和项目重难点
    2. STAR 法则

      • 问题描述:阐述遇到的问题或需求
      • 解决方案评估:方案对比与选择(如 React 状态管理:redux、mobx、jotai、recoil,Vue3 使用 Pinia)
      • 方案实施:具体的实施步骤
      • 反思与优化:项目反思及优化建议
    3. 面试准备

      • 重点复习知识点:如 v8 内存管理、Promise A+ 规范、事件循环、this、面向对象编程原型
      • 技术储备:结合项目经验展示技术在项目中的应用
  4. 学历提升

    1. 现有学历:大专
    2. 未来计划:尽快取得本科证书(不需要注明具体年限)
    3. 学历背景:民办学校
    4. 内推建议:应对学历和工作经历问题,通过内推提升机会

补足短板

  • 项目简单,管理后台一做就是大半年,天天 CRUD

    • 看开源项目(react-hook-form ts 类型、hook 处理、状态管理、架构 Provider,keyPath)
    • 找一些不错的项目练手
    • 拿好的项目,学习完放在简历里, 可视化、编辑器、云表格、低代码、SaaS 产品、数字孪生、三维可视化
    • 1.自研:15K, 2.外包:20K ,这个火坑, 灰(供需决定)
  • 技术栈掌握不深不广,只会用 Vue2 (Vue3 + Typescript),React18(React stack reconciler)

  • 架构、方案设计没碰过

    • React,create-react-app、umi,没有真正从零到一去设计初始化过一个项目
    • Vue,Vue CLI,Vite/Webpack。Vue2 CLI 创出来项目 1. webpack、2.vite
  • 没有专精的技能或业务

    • 自驱 (假定自己是 Leader),项目的赢利点、商业价值【 可视化、编辑器、白板、团队基建、AI产品
    • 与生俱来有些东西,好奇心、自驱力、清晰规划
    • 我:管理平台,HPE(官网、后台管理),云表格(维格表、飞书云表格)、云编辑器(CKEditor 老【html string】)(语雀、【石墨文档】)

妙码学院——全程陪跑、督学、内推

⚠️ 暂不支持的文档区域,【文档小组件】

  • 跨端开发 Taro(Taro 编译器 compiler、运行时 runtime)、uniapp
  • 协同编辑器(文档类、画板类)wangEditor、CKEditor
  • 团队基建工程(UI 库、图表库、Cli -> 产生产物 npm 包)
  • 3D 可视化数字孪生 bigdata
  • 低代码平台

课程体系 2.0 升级,除了基础知识夯实,融入了更多项目实战内容,包含:

  • 企业级脚手架工具开发实践

  • 企业级文档编辑器飞书文档开发实践

  • 大厂 UI 组件库(Vue3)整体设计与开发实践

  • 大厂业务 Hooks 库(React 18)整体设计与开发实践

  • 埋点与数据监控平台实战

  • 3D 可视化数字孪生实战 💥

    • 基于 cesium 方案的 WebGIS 开发实践
    • 基于 WebGL 3D 可视化开发实践

妙码学院全网独家项目实战矩阵

所有项目实战,均完全 从零到一手写纯原创 ,项目架构与编码规范真一线大厂级。

  • 微前端在分拆原子应用场景下的落地与实践

  • 协同编辑器从零到一架构实现

  • 推动团队基建落地

    • React UI 库
    • Vue Composition API 库
    • 企业级脚手架
  • 数字孪生平台整体架构设计

  • 企业级无代码可视化平台实践(Vue3)

  • 低代码平台设计与实现

    • 编排引擎
    • 流程引擎
    • 编辑器
    • 代码执行器与 JavaScript 沙箱
  • 用户行为分析 SDK 及监控可视化整体实现

  • 可视化渲染引擎设计与实现

  • 基于 RAG / Agent 前端 AI 应用流程编排平台

不同阶段对前端人的硬性要求

以下是对您提供的内容进行权威和专业化改写的建议:

1~3年

在此阶段,重点在于评估个人的基础知识和热情。对前端基础、计算机原理、网络通信和算法等领域的要求较高。由于在此阶段难以评估业务深度,因此更多关注基础知识的掌握程度。

  • 关键在于通过学术教育或网络资源加强基础知识;
  • 在简历中以多种方式展示对前端的热情,展现个人潜力;
  • 积极探索前沿技术,关注国内外技术动态;
  • 尝试开发小型项目或参与社区开源项目;
  • 建立技术博客,以输出促进知识吸收。

3~5年

此阶段通常是向成为独立工程师发展的关键时期,避免重复使用有限的经验。

  • 关注社区中关于进阶的资料和路线,强化基础知识;

  • 深入掌握常用框架的高级用法,探索其原理;

  • 在业务开发中不仅完成功能,还需考虑项目结构设计、封装基础工具、设计和开发基础组件;

  • 思考提高团队效率的方法,例如:

    • 集成代码检验和风格统一插件(如 eslint、stylelint、prettier、spellcheck);
    • 从工程化角度提高本地开发效率,优化webpack构建,探索esbuild、vite等工具;
    • 对于多项目开发,整理差异和统一部分,建立内部脚手架以减少重复工作;
    • 尝试搭建CI/CD平台,维护公司内部的通用npm包;
    • 培养软技能,如沟通协作,协调各角色共同推进目标。

5年以上

进入此阶段,可能朝技术专家或管理方向发展。期望您能够独立负责高复杂度项目,突破关键技术难题。

  • 负责技术调研,关注行业趋势,选择最优技术方案,具备决策能力;
  • 拥有丰富的技术经验和技术储备,能够解决遇到的困难,并有自己的方法论;
  • 协助或主导业务目标制定,合理推动项目达成预期效果;
  • 是否具有团队领导经验,能够协调跨团队项目,处理团队成员情绪问题,解决技能分布不平衡等问题;
  • 打造技术氛围,促进团队共同成长。

职业规划指导

评论区选取三位同学互动,其他同学也可以联系咨询老师,文字方式提供辅导解答。

【未知组件reminder】