最强echarts封装(优化依赖自动渲染清除大小自适应,主题切换)
liuian 2025-01-31 14:02 14 浏览
前言
我们在书写echarts组件的时候,往往会发现:
其打包的chunk包是全量的,比较大。
调用api麻烦,要注意dom的渲染时机,资源清除,大小自适应。
不支持css变量,无法动态换肤的。
本文就围绕以上几点进行提供一个解决方案。
技术栈
Vue/React Echarts TS 本文以Vue为例,文末会贴上React实现方案
github仓库地址: github.com/Freedom-FJ/…
依赖引入
首先为了解决echarts的全量引入问题,我们需要单独书写一个echarts依赖引入文件。
- 引入部分的 echarts/components 和 charts 等,把我们项目中用到的echarts部分依赖引入进来
- 用 echarts.use 挂载到我们的echarts实例上。
- 书写 echarts.draw 语法糖,可以一键对dom节点进行初始化,清除并绘制新的echarts对象。
utils/echarts/index.ts
ts复制代码/*
* @Author: mjh
* @Date: 2023-08-11 12:16:33
* @LastEditors: mjh
* @LastEditTime: 2023-08-15 10:20:14
* @Description:
*/
import * as echarts from 'echarts/core'
import { GraphicComponent, GridComponent, LegendComponent, PolarComponent, TitleComponent, TooltipComponent } from 'echarts/components'
import { BarChart, BoxplotChart, LineChart, PieChart, RadarChart } from 'echarts/charts'
import { UniversalTransition } from 'echarts/features'
import { CanvasRenderer } from 'echarts/renderers'
echarts.use([
GraphicComponent,
GridComponent,
TooltipComponent,
LegendComponent,
TitleComponent,
LineChart,
BarChart,
PieChart,
BoxplotChart,
CanvasRenderer,
UniversalTransition,
RadarChart,
PolarComponent,
])
// import * as echarts from 'echarts'
// 初始化语法糖
const draw = (dom: HTMLElement, option: Record<string, any>) => {
const chart = echarts.init(dom)
chart.clear()
chart.setOption(option)
return chart
}
export default {
...echarts,
draw
} as any
接下来我们要书写一个 echarts 组件,只需要我们传入option 就可以自动的绘制echarts节点。
书写组件
简单组件
我们先写一个简单的vue显示echarts的组件,我们需要在初始化的时候获取到dom节点,并且监听option如参,动态的更新渲染echarts实例。 但是不要忘了在组件销毁的时候清除echarts实例,防止占用内存。
vue复制代码<template>
<div ref="domBox" :style="{ width, height }">
<div ref="domRef" :style="{ width, height }" />
</div>
</template>
<script lang="ts" setup>
import { watch, ref, onMounted, onUnmounted } from 'vue'
import type { ECharts } from 'echarts'
import echarts from './index'
const props = defineProps({
width: {
type: String,
default: '100%',
},
height: {
type: String,
default: '100%',
},
options: {
type: Object,
default: null,
},
})
const domRef = ref(null)
const domBox = ref(null)
let chartObj: null | ECharts = null
onMounted(() => {
if (!domRef.value) return
init()
if (props.options)
drawOption()
})
onUnmounted(() => {
if (chartObj) {
chartObj.dispose()
chartObj = null
}
})
watch(() => props.options, () => drawOption())
// 初始化
const init = () => {
chartObj = (echarts.init(domRef.value) as any)
}
const drawOption = () => {
if(!chartObj) return
chartObj.setOption(props.options)
}
</script>
监听dom更新
不要忘了我们在绘制echarts实例的时候,如果我们已经绘制完了,但是我们的外部盒子尺寸发生变化了怎么办,这个时候我们就要监听我们的外部盒子的尺寸变化来动态的 resize 我们的echarts实例。
MutationObserver 接口提供了监视对 DOM 树所做更改的能力。创建并返回一个新的 MutationObserver 它会在指定的 DOM 发生变化时被调用。 其的 observe() 方法配置了 MutationObserver 对象的回调方法以开始接收与给定选项匹配的 DOM 变化的通知。 根据配置,观察者会观察 DOM 树中的单个 Node,也可能会观察被指定节点的部分或者所有的子孙节点。 要停止 MutationObserver(以便不再触发它的回调方法),需要调用 MutationObserver.disconnect() 方法。
语法
js复制代码var mutationObserver = new MutationObserver(callback);
mutationObserver.observe(target[, options])
- callback
- 一个回调函数,每当被指定的节点或子树以及配置项有 DOM 变动时会被调用。回调函数拥有两个参数:一个是描述所有被触发改动的 MutationRecord 对象数组,另一个是调用该函数的 MutationObserver 对象。
对于其option下面我们需要用到以下几个语法
- subtree 可选
- 当为 true 时,将会监听以 target 为根节点的整个子树。包括子树中所有节点的属性,而不仅仅是针对 target。默认值为 false。
- childList 可选
- 当为 true 时,监听 target 节点中发生的节点的新增与删除(同时,如果 subtree 为 true,会针对整个子树生效)。默认值为 false。
- attributes 可选
- 当为 true 时观察所有监听的节点属性值的变化。默认值为 true,当声明了 attributeFilter 或 attributeOldValue,默认值则为 false。
- characterData 可选
- 当为 true 时,监听声明的 target 节点上所有字符的变化。默认值为 true,如果声明了 characterDataOldValue,默认值则为 false
获取实例监听变化
js复制代码const observer = new MutationObserver((mutationsList) => {
// 循环寻找我们的echarts dom实例,发现更新的内容包含则调用 resize 方法更新echarts的尺寸
for (const mutation of mutationsList)
if (mutation.target === echartDomBox.value) chartObj && chartObj.resize()
})
observer.observe(domBox.value, {
attributes: true,
childList: false,
characterData: true,
subtree: true
})
我们将其应用到组建内,同时为了增加交互的舒适我们还可以利用echarts api 的 showLoading 加上loading的动画效果,(也可以使用 hideLoading 方法隐藏效果),为我们组件在未渲染实例时提升交互体验。
vue复制代码<template>
<div ref="domBox" :style="{ width, height }">
<div ref="domRef" :style="{ width, height }" />
</div>
</template>
<script lang="ts" setup>
import { watch, ref, onMounted, onUnmounted, nextTick } from 'vue'
import type { ECharts } from 'echarts'
import echarts from './index'
const props = defineProps({
width: {
type: String,
default: '100%',
},
height: {
type: String,
default: '100%',
},
options: {
type: Object,
default: null,
},
})
const domRef = ref(null)
const domBox = ref(null)
let chartObj: null | ECharts = null
let observer: null | MutationObserver = null // dom 监听
onMounted(() => {
if (!domRef.value) return
init()
!props.options && chartObj.showLoading({
text: '',
color: '#409eff',
textColor: '#000',
maskColor: 'rgba(255, 255, 255, .95)',
zlevel: 0,
lineWidth: 2,
})
if (props.options)
drawOption()
observer = new MutationObserver((mutationsList) => {
for (const mutation of mutationsList)
if (mutation.target === domBox.value) chartObj && chartObj.resize()
})
// 注意: 要放在nextTick内,,因为初始化时我们已经进行了一次option的更新操作
nextTick(() => {
domBox.value && (observer as MutationObserver).observe(domBox.value, {
attributes: true,
childList: false,
characterData: true,
subtree: true
})
})
setTimeout(() => {
chartObj && chartObj.resize()
}, 1000)
})
onUnmounted(() => {
if (chartObj) {
chartObj.dispose()
chartObj = null
}
// 注意销毁监听器
observer && observer.disconnect()
})
watch(() => props.options, () => drawOption())
// 初始化
const init = () => {
chartObj = (echarts.init(domRef.value) as any)
}
const drawOption = () => {
if(!chartObj) return
chartObj.hideLoading()
chartObj.setOption(props.options)
}
</script>
皮肤切换
主题切换现在比较主流的而且适配最好的是CSS变量的方案,也就是所有的颜色都采用css变量进行替换。我们只需要引入新的样式表,不需要额外的js代码,浏览器会自动更新CSSOM树,重绘所有dom的颜色,实现主体切换。
但是echarts不支持皮肤切换,因为其颜色是通过option传入的,使用canvas渲染实例,而且其传入颜色变量的字符串是无法识别的,必须直接传入颜色值,不支持css变量动态渲染,但是我们可以帮他做这个操作。
我们在echarts 传入的opiton的所有颜色处,做一层代理,我们传入所有的颜色都是变量的字符串例如:'var(--dv-color-1)' 在渲染的时候通过中间函数,将所有的颜色变量转化成真正的颜色值例如: #fff 那么只需要监听主题的切换然后重新在将传入的option进行更新颜色值再更新echarts即可。
好了思路有了,那我们开干!。
切换颜色值
首先书写一个 replaceVarStrings 中间函数用于将option内的所有颜色变量字符串都转化为真正的颜色值,匹配 var() 字符串并将其中间的颜色提取出来。 当然我们需要用到宏api getComputedStyle 实时动态计算获取我们的dom实例的样式属性,其返回值的 getPropertyValue 方法传入变量名称字符串就会返回实时的变量颜色值。
但是getComputedStyle会导致浏览器的重排,都为求一个“即时性”和“准确性”。
ts复制代码/**
* echarts样式
*/
export const useThemeValue = (styleVariables: string) => {
return getComputedStyle(document.documentElement).getPropertyValue(styleVariables)
}
export function replaceVarStrings(obj: Record<string, any>) {
const newObj: Record<string, any> = Array.isArray(obj) ? [] : {}
for (const key in obj) {
if (typeof obj[key] === 'object') {
newObj[key] = replaceVarStrings(obj[key]) // 递归处理子对象
}
else if (typeof obj[key] === 'string' && obj[key].startsWith('var(') && obj[key].endsWith(')')) {
const varContent = obj[key].slice(4, -1) // 提取括号内的内容
newObj[key] = useThemeValue(varContent) // 替换为括号内的内容
}
else {
newObj[key] = obj[key] // 其他情况直接复制值
}
}
return newObj
}
监听主题切换
以我们的element-plus 组件库主题切换为例,其切换为 dark 模式是在html 的标签上增加一个 dark的class类
html复制代码<!-- 正常 模式 -->
<html> </html>
<!-- dark 模式 -->
<html class="dark"> </html>
通过一套 dark 的变量样式覆盖原来的html下的样式
css复制代码html {
--dv-color-background-base: #000b1a;
--dv-color-background-overlay: #000b1a;
--dv-color-background-page: #000b1a;
}
/* dark 模式 */
html .dark {
--dv-color-background-base: #000b1a;
--dv-color-background-overlay: #000b1a;
--dv-color-background-page: #000b1a;
}
剩下只需要监听html dom节点的class列表即可。 但是监听 div span document这种很常见,可是怎么监听 html啊? 其实html 标签也是一个标签,你可以将它当作一个dom盒子,只需要像获取div一样 直接 document.querySelector('html') 即可获取到他的对象了。 监听class类还是用上文讲到的 MutationObserver ,只是不同的是 observer 方法的入参属性加上了 attributes, attributeFilter 配置:
- attributes 可选 当为 true 时观察所有监听的节点属性值的变化。默认值为 true,当声明了 attributeFilter 或 attributeOldValue,默认值则为 false。
- attributeFilter 可选 一个用于声明哪些属性名会被监听的数组。如果不声明该属性,所有属性的变化都将触发通知。
当然监听dom的方法有很多 对于针对dom大小的监听也可以使用其他方法来实现,文末的React实现方案就是用 ResizeObserver 来监听的, 我们vue就以 MutationObserver 为例。
接下来写一个简单的hook,返回一个响应式的isDark变量,用于让我们的echarts组件监听即可,在此我定义为 isDark: boolean 的形式,如果涉及到多套主题的话,也可以对下面代码进行修改,把返回值改成 theme: 'dark', 字符串的形式。
ts复制代码/**
* @name: 判断当前主题hook
* @desc:
* @return {*}
*/
export const useTheme = () => {
const htmlDom = document.querySelector('html')
if (!htmlDom) return { isDark: ref(false) }
const isDark = ref(!!htmlDom.classList.contains('dark'))
// 创建 MutationObserver 实例
const observer = new MutationObserver((mutationsList) => {
for (const mutation of mutationsList) {
if (mutation.type === 'attributes' && mutation.attributeName === 'class') {
const currentClass = (mutation.target as any).className
isDark.value = currentClass.includes('dark')
}
}
})
// 配置 MutationObserver 监听的选项
const observerOptions = {
attributes: true,
attributeFilter: ['class'],
}
// 开始监听目标节点
observer.observe(htmlDom, observerOptions)
return {
isDark
}
}
有了这些前置条件,这不是咱们的组件就呼之欲出了,只需要修改 drawOption 方法进行中间颜色代理,并且增加一个监听器监听 isDark 变量即可。
vue复制代码<template>
<div ref="domBox" :style="{ width, height }">
<div ref="domRef" :style="{ width, height }" />
</div>
</template>
<script lang="ts" setup>
import type { ECharts } from 'echarts'
import { watch, ref, onMounted, onUnmounted, nextTick } from 'vue'
import { replaceVarStrings, useTheme } from './utils'
import echarts from './index'
const props = defineProps({
width: {
type: String,
default: '100%',
},
height: {
type: String,
default: '100%',
},
options: {
type: Object,
default: null,
},
})
const { isDark } = useTheme()
const domRef = ref(null)
const domBox = ref(null)
let chartObj: null | ECharts = null
let observer: null | MutationObserver = null // dom 监听
onMounted(() => {
if (!domRef.value) return
init()
drawOption()
observer = new MutationObserver((mutationsList) => {
for (const mutation of mutationsList)
if (mutation.target === domBox.value) chartObj && chartObj.resize()
})
nextTick(() => {
domBox.value && (observer as MutationObserver).observe(domBox.value, {
attributes: true,
childList: false,
characterData: true,
subtree: true
})
})
setTimeout(() => {
chartObj && chartObj.resize()
}, 1000)
})
onUnmounted(() => {
if (chartObj) {
chartObj.dispose()
chartObj = null
}
observer && observer.disconnect()
})
watch(() => props.options, () => drawOption())
watch(() => isDark.value, () => drawOption())
// 初始化
const init = () => {
chartObj = (echarts.init(domRef.value) as any)
}
const drawOption = () => {
if(!chartObj) return
if(!props.options) {
chartObj.clear()
chartObj.showLoading({
text: '',
color: '#409eff',
textColor: '#000',
maskColor: 'rgba(255, 255, 255, .95)',
zlevel: 0,
lineWidth: 2,
})
}
else {
chartObj.hideLoading()
chartObj.setOption(replaceVarStrings(props.options))
}
}
</script>
lazy模式
为了提高组建的扩展性,我们也可以为组建增加一个非自动化的lazy模式。当我们开启了lazy模式以后,组建将不会自动的收集option依赖更新dom,也不用传入响应式的option,而是需要外部调用组建expose的方法控制更新时机。
vue复制代码<template>
<div ref="domBox" :style="{ width, height }">
<div ref="domRef" :style="{ width, height }" />
</div>
</template>
<script lang="ts" setup>
import { watch, ref, onMounted, onUnmounted, nextTick } from 'vue'
import { replaceVarStrings, useTheme } from './utils'
import type { ECharts } from 'echarts'
import echarts from './index'
const props = defineProps({
width: {
type: String,
default: '100%',
},
height: {
type: String,
default: '100%',
},
lazy: {
type: Boolean,
default: false,
},
options: {
type: Object,
default: null,
},
})
const { isDark } = useTheme()
const domRef = ref(null)
const domBox = ref(null)
let chartObj: null | ECharts = null
let observer: null | MutationObserver = null // dom 监听
onMounted(() => {
if (!domRef.value) return
init()
if(props.lazy) return
drawOption()
observer = new MutationObserver((mutationsList) => {
for (const mutation of mutationsList)
if (mutation.target === domBox.value) resize()
})
nextTick(() => {
domBox.value && (observer as MutationObserver).observe(domBox.value, {
attributes: true,
childList: false,
characterData: true,
subtree: true
})
})
})
onUnmounted(() => {
if (chartObj) {
chartObj.dispose()
chartObj = null
}
observer?.disconnect()
})
watch(() => props.options, () => !props.lazy && drawOption())
watch(() => isDark.value, () => !props.lazy && drawOption())
// 绘制方法
const drawOption = (options = props.options) => {
if(!chartObj) return
if(!options) {
chartObj.clear()
chartObj.showLoading({
text: '',
color: '#409eff',
textColor: '#000',
maskColor: 'rgba(255, 255, 255, .95)',
zlevel: 0,
lineWidth: 2,
})
}
else {
chartObj.hideLoading()
chartObj.setOption(replaceVarStrings(options))
}
}
// 初始化
const init = () => {
chartObj = (echarts.init(domRef.value) as any)
}
// 重绘 自适应尺寸
const resize = () => {
chartObj?.resize()
}
defineExpose({
drawOption,
resize,
init
})
</script>
使用
vue复制代码<template>
<div style="height: 300px;width: 200px;">
<BaseECharts :options="options" />
</div>
</template>
<script setup lang="ts">
import BaseECharts from '@/utils/echarts/BaseECharts.vue'
const options = ref<any>({
xAxis: {
type: 'category',
data: [],
},
color: ['var(--dv-color-danger)'], // 颜色的变量字符串
yAxis: {
type: 'value',
},
series: [
{
data: [],
type: 'line',
},
],
})
setTimeout(() => {
options.value = {
xAxis: {
type: 'category',
data: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'],
},
color: ['var(--dv-color-danger)'], // 颜色的变量字符串
yAxis: {
type: 'value',
},
series: [
{
data: [150, 230, 224, 218, 135, 147, 260],
type: 'line',
},
],
}
}, 3000)
</script>
react
对于react方案,我们在实现useTheme 上会有一些不一样,我们的变量需要用useState实现,注意 useState 避免放在条件return语句之后。 useTheme:
ts复制代码/**
* @name: 判断当前主题hook
* @desc:
* @return {*}
*/
export const useTheme = () => {
const htmlDom = document.querySelector('html');
const [isDark, setIsDark] = useState(!!htmlDom?.classList.contains('dark'));
const observer = useRef<MutationObserver>();
if (!htmlDom) return { isDark: false };
// 创建 MutationObserver 实例
if (!observer.current) {
observer.current = new MutationObserver((mutationsList) => {
for (const mutation of mutationsList) {
if (mutation.type === 'attributes' && mutation.attributeName === 'class') {
const currentClass = (mutation.target as HTMLElement).className;
setIsDark(currentClass.includes('dark'));
}
}
});
}
// 配置 MutationObserver 监听的选项
const observerOptions = {
attributes: true,
attributeFilter: ['class'],
};
// 开始监听目标节点
observer.current.observe(htmlDom, observerOptions);
return {
isDark,
};
};
在组件实现这边我们换用更为简单的api ResizeObserver 接口可以监视 Element 内容盒或边框盒或者 SVGElement 边界尺寸的变化。
注意记得在 useEffect 的 return 语句中将我们的 ResizeObserver 监听器和实例销毁。
组件:
tsx复制代码/*
* @Author: mjh
* @Date: 2023-11-25 16:04:13
* @LastEditors: mjh
* @LastEditTime: 2023-11-25 23:23:28
* @Description:
*/
import { useEffect, useRef } from 'react';
import echarts from './index';
import { replaceVarStrings, useTheme } from './utils';
export interface EchartControllerProps {
width?: string;
height?: string;
options?: Record<string, any> | null;
}
export default function EchartController(props: EchartControllerProps) {
const { height = '100%', width = '100%', options } = props;
const chartRef = useRef<any>();
const cInstance = useRef<any>();
const { isDark } = useTheme();
useEffect(() => {
if (!chartRef.current) return;
if (!cInstance.current) {
cInstance.current = echarts.init(chartRef.current);
}
const observer = new ResizeObserver(() => {
cInstance.current.resize();
});
observer.observe(chartRef.current);
return () => {
cInstance.current?.dispose()
observer.disconnect();
};
}, []);
useEffect(() => {
if (!cInstance.current) return;
if (!options) {
cInstance.current.showLoading({
text: '',
color: '#409eff',
textColor: '#000',
maskColor: 'rgba(255, 255, 255, .95)',
zlevel: 0,
lineWidth: 2,
});
return;
}
cInstance.current.hideLoading();
cInstance.current.setOption(replaceVarStrings(options));
}, [options, isDark]);
return (
<div ref={chartRef} style={{ height, width }} />
);
}
交互事件
- 我的大部分项目都不会涉及到echarts交互事件,所以暂时没有封装。
- 如果出现需求,可以简单做一个组件内部的事件代理即可,也可以根据需求做一些简单的语法弹,但是还是以场景和需求为准。
参考资料
developer.mozilla.org/zh-CN/docs/… developer.mozilla.org/zh-CN/docs/…
作者:Freedom风间 链接:https://juejin.cn/post/7304959484828844043 来源:稀土掘金 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
相关推荐
- 【常识】如何优化Windows 7
-
优化Windows7可以让这个经典系统运行更流畅,特别是在老旧硬件上。以下是经过整理的实用优化方案,分为基础优化和进阶优化两部分:一、基础优化(适合所有用户)1.关闭不必要的视觉效果右键计算机...
- 系统优化!Windows 11/10 必做的十个优化配置
-
以下是为Windows10/11用户整理的10个必做优化配置,涵盖性能提升、隐私保护和系统精简等方面,操作安全且无需第三方工具:1.禁用不必要的开机启动项操作路径:`Ctrl+S...
- 最好用音频剪辑的软件,使用方法?
-
QVE音频剪辑是一款简单实用的软件,功能丰富,可编辑全格式音频。支持音频转换、合并、淡入淡出、变速、音量调节等,无时长限制,用户可自由剪辑。剪辑后文件音质无损,支持多格式转换,便于存储与跨设备播放,满...
- Vue2 开发总踩坑?这 8 个实战技巧让代码秒变丝滑
-
前端开发的小伙伴们,在和Vue2打交道的日子里,是不是总被各种奇奇怪怪的问题搞得头大?数据不响应、组件传值混乱、页面加载慢……别慌!今天带来8个超实用的Vue2实战技巧,每一个都能直击痛...
- Motion for Vue:为Vue量身定制的强大动画库
-
在前端开发中,动画效果是提升用户体验的重要手段。Vue生态系统中虽然有许多动画库,但真正能做到高性能、易用且功能丰富的并不多。今天,我们要介绍的是MotionforVue(motion-v),...
- CSS view():JavaScript 滚动动画的终结
-
前言CSSview()方法可能会标志着JavaScript在制作滚动动画方面的衰落。如何用5行CSS代码取代50多行繁琐的JavaScript,彻底改变网页动画每次和UI/U...
- 「大数据」 hive入门
-
前言最近会介入数据中台项目,所以会推出一系列的跟大数据相关的组件博客与文档。Hive这个大数据组件自从Hadoop诞生之日起,便作为Hadoop生态体系(HDFS、MR/YARN、HIVE、HBASE...
- 青铜时代的终结:对奖牌架构的反思
-
作者|AdamBellemare译者|王强策划|Tina要点运维和分析用例无法可靠地访问相关、完整和可信赖的数据。需要一种新的数据处理方法。虽然多跳架构已经存在了几十年,并且可以对...
- 解析IBM SQL-on-Hadoop的优化思路
-
对于BigSQL的优化,您需要注意以下六个方面:1.平衡的物理设计在进行集群的物理设计需要考虑数据节点的配置要一致,避免某个数据节点性能短板而影响整体性能。而对于管理节点,它虽然不保存业务数据,但作...
- 交易型数据湖 - Apache Iceberg、Apache Hudi和Delta Lake的比较
-
图片由作者提供简介构建数据湖最重要的决定之一是选择数据的存储格式,因为它可以大大影响系统的性能、可用性和兼容性。通过仔细考虑数据存储的格式,我们可以增强数据湖的功能和性能。有几种不同的选择,每一种都有...
- 深入解析全新 AWS S3 Tables:重塑数据湖仓架构
-
在AWSre:Invent2024大会中,AWS发布了AmazonS3Tables:一项专为可扩展存储和管理结构化数据而设计的解决方案,基于ApacheIceberg开放表格...
- Apache DataFusion查询引擎简介
-
简介DataFusion是一个查询引擎,其本身不具备存储数据的能力。正因为不依赖底层存储的格式,使其成为了一个灵活可扩展的查询引擎。它原生支持了查询CSV,Parquet,Avro,Json等存储格式...
- 大数据Hadoop之——Flink Table API 和 SQL(单机Kafka)
-
一、TableAPI和FlinkSQL是什么TableAPI和SQL集成在同一套API中。这套API的核心概念是Table,用作查询的输入和输出,这套API都是批处理和...
- 比较前 3 名Schema管理工具
-
关注留言点赞,带你了解最流行的软件开发知识与最新科技行业趋势。在本文中,读者将了解三种顶级schema管理工具,如AWSGlue、ConfluentSchemaRegistry和Memph...
- 大数据技术之Flume
-
第1章概述1.1Flume定义Flume是Cloudera提供的一个高可用的,高可靠的,分布式的海量日志采集、聚合和传输的系统。Flume基于流式架构,灵活简单。1.2Flume的优点1.可以和...
- 一周热门
-
-
Python实现人事自动打卡,再也不会被批评
-
Psutil + Flask + Pyecharts + Bootstrap 开发动态可视化系统监控
-
一个解决支持HTML/CSS/JS网页转PDF(高质量)的终极解决方案
-
【验证码逆向专栏】vaptcha 手势验证码逆向分析
-
再见Swagger UI 国人开源了一款超好用的 API 文档生成框架,真香
-
网页转成pdf文件的经验分享 网页转成pdf文件的经验分享怎么弄
-
C++ std::vector 简介
-
python使用fitz模块提取pdf中的图片
-
《人人译客》如何规划你的移动电商网站(2)
-
Jupyterhub安装教程 jupyter怎么安装包
-
- 最近发表
- 标签列表
-
- python判断字典是否为空 (50)
- crontab每周一执行 (48)
- aes和des区别 (43)
- bash脚本和shell脚本的区别 (35)
- canvas库 (33)
- dataframe筛选满足条件的行 (35)
- gitlab日志 (33)
- lua xpcall (36)
- blob转json (33)
- python判断是否在列表中 (34)
- python html转pdf (36)
- 安装指定版本npm (37)
- idea搜索jar包内容 (33)
- css鼠标悬停出现隐藏的文字 (34)
- linux nacos启动命令 (33)
- gitlab 日志 (36)
- adb pull (37)
- table.render (33)
- uniapp textarea (33)
- python判断元素在不在列表里 (34)
- python 字典删除元素 (34)
- react-admin (33)
- vscode切换git分支 (35)
- vscode美化代码 (33)
- python bytes转16进制 (35)