从0到1搭建一个组件库

最近我开启了一个新项目,基于echarts进行二次封装,希望能为Vue3项目量身打造一套高效、易用的图表组件库,取名为 v-echarts。

        目前雏形已经搭建完成,先把整个搭建过程做一个记录。后续再持续迭代、完善该图表组件库。

 v-echarts 文档地址:v-echarts | Vue3 Charts Component Library (yoguoer.github.io)

v-echarts 仓库地址:https://github.com/yoguoer/v-echarts.git

        效果如下:

有兴趣的小伙伴们可以加入我呀!~ 要是能给我点个小星星就更好啦!~~

笔者在搭建自己的组件库时,趁着项目当前足够空白,专门切出了一个 demo 分支,存下来当做模板以备不时之需。
可以切换到 demo 分支,查看比较干净的项目代码。地址如下:GitHub - yoguoer/v-echarts: v-echarts 是基于Element-plus和 echarts 进行二次封装的图表组件文档

这里我以项目 v-echarts-Library 为例。首先,我们来看一下最终的目录结构,建立一个印象先:

一、主项目搭建

1、创建Vue3项目

npm init vite@latest

2、根据需要选择配置(这里选择了使用 TypeScript)

3、进入项目,安装依赖,并运行项目

  cd v-echarts-Librarynpm installnpm run dev

此时的目录结构:

        可以根据需要,集成一些其他配置,比如 .prettierrc、.stylelintrc、.eslintrc 等等。

二、文档项目搭建

1、在根目录下创建一个 docs 文件夹,这里将会是我们的文档项目,然后进入该文件夹

2、安装vitepress,集成vitepress及完善其配置

pnpm add vitepress -D

3、根目录下创建目录 docs并在 其目录中创建 index.md,其文件就是文档首页内容,可根据需要书写,测试时可先随便写点:

# Hello Vitepress 

4、添加 scripts

"scripts": {"docs:dev": "vitepress dev docs","docs:build": "vitepress build docs"
},

5、安装 postcss 防止vitePress 的样式去污染组件的样式

pnpm add postcss -D

6、在docs 目录下新建 postcss.config.mjs文件, 内容为:

import { postcssIsolateStyles } from 'vitepress'
​
export default {plugins: [postcssIsolateStyles({includeFiles: [/vp-doc\.css/] // defaults to /base\.css/})]
}

7、在根目录中添加 pnpm-workspace.yaml 文件,键入如下内容:

packages: # pnpm-workspace.yaml 定义了 工作空间 的根目录,并能够使您从工作空间中包含 / 排除目录 。 默认情况下,包含所有子目录。- packages/* # 将 packages 目录下所有文件夹项目视为独立项目,能独立安装 node_modules- docs

启动vitepress文档,页面如下

配置 vitepress

1、首页配置(即:docs/index.md文件)如下修改:

---
title: v-echarts
titleTemplate: Vue3 Charts Component Library
description: Vue3 Charts Component Library
head:- - meta- name: descriptioncontent: v-echarts is a Vue3 Charts Component Library- - meta- name: keywordscontent: v-echarts echarts Vue3 Charts Component Librarylayout: home# titleTemplate: 选项卡描述
editLink: true
lastUpdated: true
hero:name: v-echartstext: vue3图表组件tagline: Vue3 中基于echarts二次封装的图表组件文档image:src: /img/avator.jpgalt: v-echartsactions:- theme: brandtext: 安装指南link: /zh-CN/guide/howToUse- theme: brandtext: 组件预览link: /zh-CN/components/common-charts/组件总览
features:- icon: 🔨title: 实际项目details: 实际项目中碰到的疑点、难点,致力于更优的自我。- icon: 🧩title: 基础组件details: 基于Element-plus和echarts进行二次封装,使用组件 Demo 快速体验交互细节。- icon: ✈️title: Vue驱动。details: 享受 Vue3 + vite3 的开发体验,在 Markdown 中使用 Vue 组件,同时可以使用 Vue 来开发自定义主题。
---

启动vitepress文档,页面如下

2、在 docs 目录下新建一个 zh-CN 文件夹,用来存放文档,并在其中建一个 组件使用文档components 和一个 指南手册文档guide

3、在 components 目录中创建对应组件渲染.md文件

3、在 docs 目录下新建目录 .vitepress,在该目录中创建 config.ts 文件

        为了文件更加清晰,笔者将 nav、sidebar、footer 都分别成了独立的 .ts 文件,放在一个 config 文件夹中,然后再在 config.ts 文件中导入。

① 配置nav(定义顶部导航)。configs/nav.ts:

export default [{text: '指南',link: '/zh-CN/guide/howToUse'},{ text: '基础图表', link: '/zh-CN/components/common-charts/test/readme' },{text: '博客',items: [{ text: 'CSDN', link: 'https://blog.csdn.net/Vivien_CC' },{ text: 'github', link: 'https://github.com/yoguoer' },]}

② 配置页脚。configs/footer.ts:

export default {
message: 'v-echarts design by Vivien',
copyright: 'Copyright © 2024-present Vivien'
}

③ 配置侧边栏。configs/sidebar.ts:

export default {
'/zh-CN/guide': [{text:'环境准备',items:[{text:'如何使用',link:'/zh-CN/guide/howToUse'}]},{text: '使用指南',items: [{ text: '基础图表', link: '/zh-CN/guide/common-charts' },]},
],
'/zh-CN/components/': [{text: '基础图表',items: [{ text: '测试', link: '/zh-CN/components/common-charts/test/readme' },{ text: '折线图', link: '/zh-CN/components/common-charts/Bar/readme' },{ text: '条形图', link: '/zh-CN/components/common-charts/Line/readme' },{ text: '饼图', link: '/zh-CN/components/common-charts/Pie/readme' },{ text: '仪表盘', link: '/zh-CN/components/common-charts/Guage/readme' },]},
]
}

④ 最终config.ts 文件如下

import { defineConfig } from 'vitepress'
import nav from './configs/nav'
import sidebar from './configs/sidebar'
import footer from './configs/footer'export default defineConfig({
title: 'v-echarts',
description: 'v-echarts 是一个基于echarts和Vue3封装的图表组件库,主要用于快速构建数据可视化页面。',
lang: 'zh-CN',
base: `/v-echarts`,
head: [['link',{rel: 'icon',href: '/img/vivien-logo.svg'}]
],
lastUpdated: true,
themeConfig: {logo: '/img/vivien-logo.svg',// siteTitle: 'v-echarts',// outline: 3,socialLinks: [{ icon: 'github', link: 'https://github.com/yoguoer' }],nav,sidebar,footer,search: {provider: 'local'}
},
})

将Markdown文件中的代码示例转换为可显示在网页上的代码片段

我们使用一个插件来将 Markdown 文件中的代码示例转换为可显示在网页上的代码片段。

1、 在 docs文件夹下添加一个plugins文件夹,并创建 source-code.ts 文件,键入以下内容

import * as path from 'path'
import * as fsPromises from 'fs/promises'
import Prism from 'prismjs'
import loadLanguages from 'prismjs/components/index' // 语法高亮loadLanguages(['markup', 'css', 'javascript'])
// 将源码使用 <pre> 标签包裹
const warp = code => `<pre v-pre><code>${code}</code></pre>`
/*** 实现原理是将一个字符串按照特定的模式(.*?source-code="(.*)")进行匹配,然后将匹配到的结果按照:::分割成数组* @param _ * @returns */
function sourceSplit(_: string) {const result = /.*?source-code="(.*)"/.exec(_)const originPath = (result && result[1]) ?? ''return originPath.split(':::')
}// 项目包路径
const packagesPath = path.resolve(__dirname, '../../packages/')
/*** 用于将 Markdown 文件中的代码示例转换为可显示在网页上的代码片段。* 它首先会提取模块代码中的 source-code 属性,然后读取相应的文件,并将文件内容替换到属性中。* 这样,浏览器就可以直接解析模块代码中的代码片段了* docs:https://rollupjs.org/plugin-development/#transform* @param src string 源代码字符串* @param id  string | SourceMap;* @returns */
const sourceCode = () => {return {//在每个传入模块请求时被调用  transform 钩子async transform(src: string, id: string) {const mdFile = '.md' // 文件类型// 文件非 markodwn 类型,返回if (!id.includes(mdFile)) return// 提取示例组件中source-code属性值const reg = /source-code="(.*)"/g// 示例组件中不存在source-code属性,返回if (!src.match(reg)) return//使用正则表达式从模块代码中提取 source-code 属性,并将其替换为实际的代码内容。const match = src.match(reg)?.map(_ => {// 获取示例组件项目名和文件地址,通过 “项目名:::组件路由地址” 进行分割const [packageName, compPath] = sourceSplit(_)// 获取项目类型,react 使用ant design 组件,使用 tsx 语法, Vue 使用const suffix = packageName.includes('ant') ? 'tsx' : 'vue'// 返回读取的文件内容return fsPromises.readFile(path.resolve(packagesPath, `${packageName}/demo/${compPath}.${suffix}`), 'utf-8')})const filesRes = await Promise.all(match)//正则表达式 /source-code="(.*)"/g 可能会匹配到多个匹配项,因此在替换时需要使用 i++ 来确保每次替换都是唯一的。let i = 0//读取模块代码中的所有文件,并将其内容合并到模块代码中return src.replace(reg, (str) => {// 获取示例组件项目名和文件地址,通过 “项目名:::组件路由地址” 进行分割const [packageName, compPath] = sourceSplit(str)// 获取示例组件地址const compPathStrArr = compPath.split('/')// iframe src 地址const iframeSrc = compPathStrArr[compPathStrArr.length - 1]// 正则匹配 @Vi-Echarts 路径别名替换为原路径const searchString = new RegExp(`@/Vi-Echarts/${packageName}`, 'g')const replaceString = `Vi-Echarts/${packageName}`const file = filesRes[i] ? (filesRes[i] as string).replace(searchString, replaceString) : nulli++// 返回编码后的源码文件内容return `source-code="${encodeURIComponent(warp(Prism.highlight(file, Prism.languages.markup, 'markup')))}" raw-source="${encodeURIComponent(file)}" lib-type="${packageName}" iframe-src="${iframeSrc}"`})}}
}
export default sourceCode

2、 在 docs/vite.config.ts 中导入插件并使用

import sourceCode from './plugins/source-code'
import { defineConfig } from 'vite'
import path from 'path'
import { alias } from './plugins/alias'export default defineConfig(async ({ command, mode }) => {const alia = await alias()return {server: {proxy: {'/assets': {target: 'http://localhost:8080',changeOrigin: true}}},plugins: [sourceCode()//将 Markdown 文件中的代码示例转换为可显示在网页上的代码片段],resolve: {alias: [...alia, // 统一项目包别名{find: '@/',replacement: path.join(__dirname, '/')}]}}
})

封装渲染 Vue 的组件

1、在.vitepress文件夹下,新建一个components文件夹,用于存储我们的vue渲染模板

2、总体组件概况

代码如下:

<template><el-row class="intro-card-container"><el-col :span="12" v-for="item in lists" :key="item"><el-card class="card-container" shadow="hover" @click="viewComponent(item.link)"><template #header><div class="card-header"><span>{{ item.title }}</span><el-tag v-if="item.tag" :type="item.tagType || 'primary'">{{ item.tag }}</el-tag></div></template><div class="card-footer"><el-button:type="item.link ? 'primary' : 'info'"round:disabled="!item.link"@click="viewComponent(item.link)">查看</el-button></div></el-card></el-col></el-row>
</template><script setup>
const props = defineProps({lists: {// 组件库名称type: Array,default: null,},
});const viewComponent = (link) => {if (!link) {return false;}window && window.open(link);
};
</script>
<script>
export default {name: "IntroCard",
};
</script><style scoped lang="less">
.intro-card-container {display: flex;justify-content: flex-start;.card-container {margin: 5px;}.card-header {display: flex;justify-content: space-between;}.card-footer {display: flex;justify-content: flex-end;}
}
</style>

3、单个组件预览

代码如下:

<template><div class="container" :class="{ 'full-screen-container': isFullScreen }"><div class="demo"><!--操作菜单--><div class="menu"><iclass="icon"v-for="icon in iconColorArr":key="icon.color":style="{ backgroundColor: icon.color }"><el-iconv-if="icon.name === 'scale'"class="d-caret"@click="handleToggleFullScreen"><DCaret/></el-icon></i></div><!--demo展示组件,使用 iframe 内嵌组件--><iframeclass="elp-iframe":class="{ 'full-screen-iframe': isFullScreen }":src="`${baseUrl[props.libType]}#/${props.iframeSrc}`":style="{ height: demoHeight }"/></div><div class="options"><el-tooltip content="全屏预览" placement="bottom"><el-icon class="option-item" @click="handleToggleFullScreen"><FullScreen/></el-icon></el-tooltip><el-tooltip content="复制代码" placement="bottom"><el-icon class="option-item" @click="copyCode"><CopyDocument /></el-icon></el-tooltip><el-tooltip content="查看源码" placement="bottom"><span class="option-item code-btn" @click="handleToggleCode">&lt;/&gt;</span></el-tooltip></div><El-collapse-transition><div class="source-code" v-if="isShowCode"><!--demo 源码内嵌显示--><div class="decode" v-html="decoded" /><div class="hide-code-btn"><el-button type="info" link :icon="CaretTop" @click="handleToggleCode">隐藏源代码</el-button></div></div></El-collapse-transition></div>
</template><script setup>
import { ref, computed, onMounted } from "vue";
import {ElIcon,ElTooltip,ElCollapseTransition,ElButton,ElMessage,
} from "element-plus";
import { FullScreen, DCaret, CaretTop, CopyDocument } from "@element-plus/icons-vue";
import { useClipboard } from "@vueuse/core";
import "prismjs/themes/prism-tomorrow.css";const props = defineProps({libType: { // 组件库名称type: String,default: "element-plus",},iframeSrc: {type: String,default: "",},demoHeight: {type: String,default: "320px",},sourceCode: {type: String,default: "",},rawSource: {type: String,default: "",},
});const decoded = computed(() => {return decodeURIComponent(props.sourceCode);
});
// 组件库 demo 构建地址
const baseUrl = {"common-charts": import.meta.env.VITE_COMMON_DEV_BASE,
};const iconColorArr = [{ name: "", color: "#646cff" },{ name: "", color: "#9499ff" },{ name: "scale", color: "#bcc0ff" },
];const isFullScreen = ref(false);
const isShowCode = ref(false);
//全屏预览
const handleToggleFullScreen = () => (isFullScreen.value = !isFullScreen.value);
//查看源码
const handleToggleCode = () => (isShowCode.value = !isShowCode.value);
//复制源码
const { copy, isSupported } = useClipboard({source: decodeURIComponent(props.rawSource),read: false,
});const copyCode = async () => {if (!isSupported) {ElMessage.error("不支持复制");}try {await copy();ElMessage.success("复制成功");} catch (e) {ElMessage.error(e.message);}
};onMounted(() => {// 监听键盘 esc键,退出全屏document.addEventListener("keyup", (e) => {if (e.key === "Escape" || e.key === "Esc") isFullScreen.value = false;});
});
</script>
<script>
export default {name: "vEchartsDemo",
};
</script><style scoped lang="less">
@menu-height: 32px;.container {border: 1px solid #dcdfe6b2;border-radius: var(--ve-border-radius-base);
}
.full-screen-container {position: fixed;left: 0;top: 0;width: 100vw;height: 100vh;background-color: #fff;z-index: 10000;
}
.demo {.menu {border-radius: var(--ve-border-radius-base) var(--ve-border-radius-base) 0 0;height: @menu-height;line-height: @menu-height;background-color: var(--ve-custom-block-details-bg);padding: 0 16px;.icon {position: relative;display: inline-block;width: 15px;height: 15px;margin-right: 5px;&:hover {.d-caret {display: block;cursor: pointer;}}.d-caret {display: none;position: absolute;left: 50%;top: 50%;transform: translate(-50%, -50%) rotate(-45deg);color: var(--ve-c-text-2);font-size: 13px;}}}.elp-iframe {width: 100%;padding: 15px;border: 0;}.full-screen-iframe {width: 100vw;height: calc(100vh - @menu-height) !important;}
}
.options {border-top: 1px solid #dcdfe6b2;height: 40px;display: flex;align-items: center;justify-content: flex-end;.option-item {margin-right: 8px;cursor: pointer;color: var(--ve-c-text-2);font-size: 12px;&:hover {color: var(--ve-c-text-1);}}
}
.source-code {background-color: #f6f8fa;position: relative;border-top: 1px solid #dcdfe6b2;.decode {padding: 0 16px;}.hide-code-btn {border-top: 1px solid #dcdfe6b2;border-radius: 0 0 var(--ve-border-radius-base) var(--ve-border-radius-base);position: sticky;bottom: 0;height: 40px;display: flex;align-items: center;justify-content: center;font-size: 14px;background-color: var(--vp-code-block-bg);z-index: 10;.icon {margin-right: 8px;}&::hover{color: black;}}
}
</style>

4、在index.ts中导出

import vEchartsDemo from './vEcharts-demo.vue';
import IntroCard from './intro-card.vue';
export const globals = [vEchartsDemo, IntroCard];

三、组件项目搭建

1、首先需要创建一个 packages 目录,用来存放组件,这里面是可以独立运行的项目

笔者开发的是图表组件库,分为两个项目:基础图表库 common-charts 和动态图表库 dynamic-charts

- packages
---- common-charts // 基础图表
---- dynamic-charts // 动态图表

packages 中的独立项目,搭建过程和我们的普通项目其实是一致的,也可以参考前面的住项目搭建步骤,这里只记录额外需要的部分。

2、每个图表库中都有一个用于全局注册组件的文件 withInstall.ts 和用于遍历并注册组件的 install.ts, 以及最外层的用于导出所有组件的 index.ts

为了使目录结构更清晰,将 withInstall.ts 和 install.ts 放在一个utils文件夹里

① common-charts/utils/withInstall.ts

import { App, Plugin } from "vue"// 定义一个类型,用于表示带有 install 方法的组件
type SFCWithInstall<T> = T & Plugin// 定义一个函数,用于给组件添加 install 方法
export const withInstall = <T, E extends Record<string, any>>(main: T, extra?: E) => {// 给 main 添加 install 方法;(main as SFCWithInstall<T>).install = (app: App) => {// 遍历 main 和 extra 中的组件,将它们注册到 app 中for (const comp of [main, ...Object.values(extra ?? {})]) {app.component(comp.name, comp)}}// 如果有 extra,则将 extra 中的组件添加到 main 中if (extra) {for (const [compName, comp] of Object.entries(extra)) {;(main as Record<string, any>)[compName] = comp}}// 将 T 断言为具体的类型 T & plugin & Record<string, any>return main as SFCWithInstall<T> & E
}

② common-charts/utils/install.ts

import vEchartsTest from '../components/test'  // 导入已经开发好的组件// 存储组件列表
const components = [vEchartsTest,
]// 插件注册:在 Vue 项目的入口文件中,通过 ( app.use(插件) ) 进行注册
const installComponents = (app: any) => {components.forEach((comp: any) => { // 遍历并全局注册组件// app.component(comp.name as string, comp)app.use(comp)})
}// 导出组件
export const installer = (app: any, router?: any) => {// 导出的对象必须具有 install,才能被 Vue.use() 方法安装installComponents(app)
}

③ common-charts/index.ts

export * from './components'
export { installer as commonChartsInstall } from './install'

目录结构如下:

四、组件开发

接下来,将以 common-charts 中的 test 作为例子来介绍整个过程

1、packages 中的每个文件夹,都存放着组件开发目录 components 和组件测试目录 demo

-- packages
|--- common-charts |--- components //组件开发目录|--- demo //组件测试目录
|---  dynamic-charts |--- components|--- demo

2、每个单独的组件开发目录,最外层的 index.ts 用于整合所有组件,并对外暴露出去(代码如第 5 点所示)

3、components 中的每个组件,都应该归类于单独的目录下,包含其组件源码目录 src,和 index.ts,以便于外部引用

这里以组件 vEchartsTest 为例

4、在 components 和 demo 中分别新建一个 test 文件夹

  • components 文件夹中:

① 最外层的 components/index.ts 用于整合并导出组件

export * from './test'
export * from './Bar'
export * from './Line'
export * from './Pie'

① components/test/src/index.vue 用于开发我们的组件,在这里就先随便写点东西好了(注意,组件必须声明 name,这个 name 就是组件的标签)

<template><span>哈哈哈哈哈哈</span><br/><el-input placeholder="hahaha" style="width: 300px;"/>
</template><script setup lang="ts" name="Test">
</script>
<script>
export default {name: "Test",
};
</script>

② components/test/index.ts 用于导出 test 组件

import Test from './src/index.vue' // 引入组件
import { withInstall } from '../../withInstall' // 引入 withInstall 函数// 使用 withInstall 注册组件并导出组件
const vEchartsTest = withInstall(Test)
export default vEchartsTest
  • demo 文件夹中:

demo /test/test-demo.vue 用于使用 test 组件,提供使用示例

注意,这里的代码将会成为文档中 ”查看源码“ 功能中见到的代码和”复制代码“功能中得到的代码

<template><vEchartsTest />
</template><script setup lang="ts">
import vEchartsTest from '../../components/test'
</script>

五、书写文档

在这里,我们使用前面已经封装好的 Vue 渲染组件来引入组件使用示例即可

# 哈哈
测试一下测试一下
## 示例### 常规使用<vEcharts-demodemo-height="300px"source-code="common-charts:::test/test-demo"
/>

效果如下:

六、配置路由

写完文档后,就可以在 config.ts 中配置顶部导航栏 nav 和侧边栏 sidebar 了。

这里需要根据自己的文件所在路径去配置!

笔者是创建了一个 zh-CN 文件夹,里面包含指南文档 guide 和组件文档 components(里面的目录结构又和 packages 文件夹里面的一一对应)

nav:[{ text: '基础图表', link: '/zh-CN/components/common-charts/test/readme' },
]
sidebar:['/zh-CN/components/': [{text: '基础图表',items: [{ text: '测试', link: '/zh-CN/components/common-charts/test/readme' },]},]
]

完成

经过这些操作步骤,我们的组件库雏形就已经搭建完成啦!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://xiahunao.cn/news/3267629.html

如若内容造成侵权/违法违规/事实不符,请联系瞎胡闹网进行投诉反馈,一经查实,立即删除!

相关文章

RustDesk远程控屏软件使用教学

RustDesk自建服务器使用教学RustDesk远程控屏软件使用教学 下载软件后 右键管理员运行 点击右上角设置按钮 管理员运行 保证启动服务 点击左侧导航栏网络按钮 复制域名或者ip地址到 ID服务器 输入框 然后点击应用即可

移动式气象站:科技赋能,精准预报的新篇章

在这个气候多变、极端天气频发的时代&#xff0c;气象信息的准确性与及时性成为了社会各界关注的焦点。从农业生产到城市规划&#xff0c;从航空航海到日常生活&#xff0c;气象服务无处不在&#xff0c;其重要性不言而喻。而在这场气象科技的变革中&#xff0c;移动式气象站以…

友思特应用 | 硅片上的光影贴合:UV-LED曝光系统在晶圆边缘曝光中的高效应用

导读 晶圆边缘曝光是帮助减少晶圆涂布过程中多余的光刻胶对电子器件影响的重要步骤。友思特 ALE/1 和 ALE/3 UV-LED 高性能点光源&#xff0c;作为唯一可用于宽带晶圆边缘曝光的 i、h 和 g 线的 LED 解决方案&#xff0c;可高效实现WEE系统设计和曝光需求。 晶圆边缘曝光及处…

The Llama 3 Herd of Models.Llama 3 模型论文全文

现代人工智能(AI)系统是由基础模型驱动的。本文提出了一套新的基础模型,称为Llama 3。它是一组语言模型,支持多语言、编码、推理和工具使用。我们最大的模型是一个密集的Transformer,具有405B个参数和多达128K个tokens的上下文窗口。本文对Llama 3进行了广泛的实证评价。我们…

day06 1.算法的相关概念2.排序算法3.查找算法

一、算法的相关概念 程序 数据结构 算法 算法是程序设计的灵魂&#xff0c;结构是程序设计的肉体 算法&#xff1a;计算机解决问题的方法或步骤 1.1 算法的特性 1> 确定性&#xff1a;算法中每一条语句都有确定的含义&#xff0c;不能模棱两可 2> 有穷性&#xff1a;…

【Linux】从零开始认识多线程 --- 线程ID

在这个浮躁的时代 只有自律的人才能脱颖而出 -- 《觉醒年代》 1 前言 上一篇文章中讲解了线程控制的基本接口&#xff1a; 线程创建pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine) (void *), void *arg);: pthread_t *thread :输出…

使用API有效率地管理Dynadot域名,设置过期域名抢注请求

简介 Dynadot是通过ICANN认证的域名注册商&#xff0c;自2002年成立以来&#xff0c;服务于全球108个国家和地区的客户&#xff0c;为数以万计的客户提供简洁&#xff0c;优惠&#xff0c;安全的域名注册以及管理服务。 Dynadot平台操作教程索引&#xff08;包括域名邮箱&…

深入理解SQL中的INNER JOIN操作

本文介绍了INNER JOIN的定义、使用场景、计算方法及与其他JOIN的比较。INNER JOIN是关系数据库中常用的操作&#xff0c;用于返回两个表中匹配的行&#xff0c;只有在连接条件满足时才返回数据。本文详细解释了INNER JOIN的语法及其在一对多、多对多关系中的应用&#xff0c;通…

【github】使用KeepassXC 解决github Enable two-factor authentication (2FA) 第二因子认证

下载 https://github.com/keepassxreboot/keepassxc/releases/download/2.7.9/KeePassXC-2.7.9-Win64.msi 代理地址 https://dgithub.xyz/keepassxreboot/keepassxc/releases/download/2.7.9/KeePassXC-2.7.9-Win64.msi 由于该软件不允许截图&#xff0c;以下操作参考官网 …

如何检查代理IP地址是否被占用

使用代理IP时&#xff0c;有时候会发现IP仍然不可用&#xff0c;可能是因为已经被其他用户或者网络占用了。为了检测代理IP是否被占用&#xff0c;我们可以采用一些方法进行验证测试&#xff0c;以保证代理IP的有效性和稳定性。 1.ARP缓存方法 ARP缓存法是一种简单有效的检测代…

【Python面试题收录】Python编程基础练习题①(数据类型+函数+文件操作)

本文所有代码打包在Gitee仓库中https://gitee.com/wx114/Python-Interview-Questions 一、数据类型 第一题&#xff08;str&#xff09; 请编写一个Python程序&#xff0c;完成以下任务&#xff1a; 去除字符串开头和结尾的空格。使用逗号&#xff08;","&#…

kotlin示例

以下代码是我写的练习程序&#xff0c;更好的代码可以从这里查看&#xff1a;代码 生日卡片 package com.example.happybirthdayimport android.os.Bundle import androidx.activity.ComponentActivity import androidx.activity.compose.setContent import androidx.activity…

使用echo写入多行文字到文件时换行的处理

目标 想使用echo写入如下内容到文件program.c里 #include<stdio.h> int main(){printf("hello!\n"); } 需要处理 1、如何处理行换 2、代码中的换行如何处理 实际例子 创建文件夹 mkdir test cd test chmod 777 . 创建文件写入内容 查看 cat -n program.c…

Flink入门(更新中)

目录 一、Flink 1.1 基本概念 1.1.1 flink简介 1.2 flink编程模版 1.3 常用概念 1.2.1 datastream 1.2.2 算子、Task 1.2.3 多流操作 1.2.6 时间语义 二、Flink编程实战(Java) 2.1 wordcount 一、Flink 1.1 基本概念 1.1.1 flink简介 1.图片介绍 性能&#xff1a…

[练习]如何使用递归算法?

&#x1f3a5; 个人主页&#xff1a;Dikz12&#x1f525;个人专栏&#xff1a;算法(Java)&#x1f4d5;格言&#xff1a;吾愚多不敏&#xff0c;而愿加学欢迎大家&#x1f44d;点赞✍评论⭐收藏 目录 1. 递归概述 2.汉诺塔问题 题目描述​编辑 题解 代码实现 3…

package.json中对peerDependencies的理解

peerDependencies只要是用来限制依赖的&#xff0c;最近在开发的时候有遇到这样的问题&#xff0c;所以研究了一下 "peerDependencies": {"vue/composition-api": "^1.0.5","vue/runtime-core": "^3.0.0","echarts&q…

【Android】Activity生命周期与五种启动模式

文章目录 生命周期返回栈Activity状态生命周期方法 启动模式standard模式singleTask模式singleTop模式singleInstance模式singleInstancePerTask模式配置方式 生命周期 返回栈 每个Activity的状态由它在Activity栈&#xff08;又叫“回退栈back stack”&#xff09;中的位置决…

【ACM出版】2024年教育人工智能国际学术会议(ISAIE 2024,9月6-8)

2024年教育人工智能国际学术会议&#xff08;ISAIE 2024&#xff09;将于2024年9月6-8日在中国西安举行。本届会议由西京学院主办。 会议主要围绕人工智能在教育领域的最新研究成果展开&#xff0c;为来自国内外高等院校、科学研究所、企事业单位的专家、教授、学者、工程师等提…

windows安装redis设置密码、修改端口、提供外部访问

windows安装redis设置密码、修改端口、提供外部访问 一、前言1. 设置密码2. 修改端口3. 允许外部访问4. 注意事项 一、前言 设置Redis在Windows上设置密码、修改端口以及允许外部访问&#xff0c;需要进行以下步骤&#xff1a; 下载地址 https://github.com/tporadowski/redi…

快速入门Jupyter notebook

快速入门 Jupyter notebook 一、前言&#xff08;一&#xff09;优点&#xff08;二&#xff09;特点&#xff08;三&#xff09;调用运行&#xff08;四&#xff09;新建 二、认识界面快捷键&#xff08;一&#xff09;三种模式&#xff08;1&#xff09;蓝色模式&#xff1a;…