当前位置: 首页 > article >正文

前端脚手架开发指南:提高开发效率的核心操作

前端脚手架通过自动化的方式可以提高开发效率并减少重复工作,而最强大的脚手架并不是现成的那些工具而是属于你自己团队量身定制的脚手架!本篇文章将带你了解脚手架开发的基本技巧,帮助你掌握如何构建适合自己需求的工具,并带着你一步步走向前端开发的全新高度。

目录

初始化项目

模板命令操作

仓库文档操作

模板下载操作

模板渲染操作 

初始化项目

关于脚手架的基本概念和一些常用工具的讲解,上篇文章 地址 已经讲解的比较清楚了,本篇文章不再赘述,接下来我们还是初始化一个新的项目进行操作,终端执行 pnpm init 初始化项目:

然后接下来我们执行如下命令安装脚手架需要的相关插件,不清楚的可以参考我上篇文章讲解:

pnpm install commander @inquirer/prompts chalk ini ora -s
pnpm install @types/node typescript nodemon -D

安装完成一些基础插件之后,接下来我们需要设置ts模块然后将ts编译成js然后在运行项目,终端执行如下命令生成ts配置文件,执行报错全局cmd安装一下 npm i -g typescript 即可:

tsc --init

然后我们根据自身情况配置如下内容即可,

{"compilerOptions": {"target": "es6", // 编译成es6代码"module": "NodeNext", // 模块选择es6"outDir": "bin","moduleResolution": "nodenext", // 模块解析策略"esModuleInterop": true, // 允许导入非ES模块"resolveJsonModule": true, // 允许导入json模块"rootDir": "src", // 根目录"baseUrl": "./src" // 基础目录},"include": ["src"],"exclude": ["node_modules"],
}

然后我们设置编译打包内容如下所示,执行打包命令组织和执行我们定义的关键字就能执行了

但是每次写完代码都要重新打包然后再执行一遍,很费时间所以这里我们通过nodemon来设置自动编译打包执行,nodemon提供了许多实用的命令行选项帮助定制其行为,以下是一些常用的选项:

参数说明
-w 或 --watch指定监视的文件或文件夹
-e 或 --ext指定要监视的文件扩展名
-i 或 --ignore指定要忽略的文件或文件夹
-d 或 --delay设置文件变化后的延迟重启时间(单位为秒)
--exec执行指定的命令而不是直接启动 node 命令
-r 或 --require加载一个模块,通常用于加载环境配置或预处理脚本

根据上面的规则配置了如下script命令,时刻监听src目录下所以ts文件,一旦有变化就执行tsc:

"scripts": {"test": "echo \"Error: no test specified\" && exit 1","dev": "nodemon --watch ./src --ext ts --exec tsc","build": "tsc"
},

效果如下,当我们修改ts文件之后,就会立即热更新编译成js文件,效果不错:

模板命令操作

        随着脚手架框架的不断累积和完善,模板命令也会越来越复杂以适应不同场景下的模板创建,所以我们需要对我们的模板创建命令进行一个抽离和封装以方便后期简化操作,这里我们直接在src目录下新建一个commands文件夹,里面存放封装commands命令的内容以及命令对应要执行的函数内容,当然这里根据个人喜好配置,博主设置的内容如下所示:

基础options设置:在基础的封装options函数中,这里我将读取的json文件里面的内容传递了进去,对于json文件的读取,前端tsconfig中已经设置了 "resolveJsonModule": true, // 允许导入JSON模块 这个配置,我之前还用的好好的,能直接引入pack文件然后读取使用,后面可能由于ts版本或者其他因素版本的影响导致读取不了数据了,这里我就抽离了一个工具函数,两个方法都能读取json文件的内容:

import fs from 'fs';
import { createRequire } from "module";
import { fileURLToPath } from "url";
import { dirname, join } from "path";const _filename = fileURLToPath(import.meta.url);
const _dirname = dirname(_filename);
const require = createRequire(_filename);
const pkg = require(join(_dirname, '../../package.json'));
const loadJSON = (path) => JSON.parse(fs.readFileSync(new URL(path, import.meta.url)).toString());
const Pack = loadJSON('../../package.json');export {Pack,pkg
}

拿到json文件里面的内容数据之后,我们就可以传递到基础配置当中,直接设置脚手架的名称、描述、作者及版本等相关项目的配置:

import { program } from "commander";const baseOptions = (Pack) => {program.name(Pack.name).version(Pack.version, '-v, --version', '输出当前版本号').usage('<command>(必填项) [options](可选项)').description(`${Pack.description} (作者:${Pack.author})`).addHelpText("after", `\nRun yyue-cli <command> --help for detailed usage of given command.\n`)
};export default baseOptions;

command命令配置:接下来开始写command命令配置,这个是大头,用户在第一次接触脚手架使用的使用,都是通过command设置的命令配置,才能了解到脚手架如何使用。因为command命令配置后期可能随着脚手架的复杂度的升高会导致产生各自各样的命令,所以这里我们需要对其命令配置抽离出一个对象,然后通过循环遍历的方式来生成对应的command配置,也很方便,具体实现的代码如下所示:

import { program } from "commander";// 配置指令命令
const mapActions = {create: {alias: 'c',description: 'create a new project',options: [{ flags: "-f, --force", description: "overwrite target directory if it exists" }],examples: ['yyue-cli create <project-name>'],action: async (name, option) => (await import("../hook/create.js")).default(name, option),},config: {alias: 'conf',description: 'config project variable',options: [{ flags: "-g, --get <k>", description: "get value from path" },{ flags: "-s, --set <k> <v>", description: "set value to path" },{ flags: "-d, --delete <k>", description: "delete value from path" }],examples: ['yyue-cli conf set <k><v>', 'yyue-cli conf get <k>'],action: async (value, option) => (await import("../hook/config.js")).default(value, option),},'*': {alias: '',description: 'command not found',options: [],examples: ['yyue-cli <cmd>'],action: () => console.log('command not found'),}
};const CustomCommand = () => {// 循环创建命令Reflect.ownKeys(mapActions).forEach((key: string) => {const { alias, description, options, action } = mapActions[key];const cmd = program.command(key) // 配置命令名称.alias(alias) // 配置命令别名.description(description); // 配置命令描述if (key === 'create') {cmd.argument('<project-name>', 'name of the project');} else if(key === 'config') {cmd.argument('<k>', 'key of the variable').argument('<v>', 'value of the variable');} else if (key === '*') {cmd.argument('<cmd>', 'command of the project');}// 配置选项options?.forEach((option) => {cmd.option(option.flags, option.description);});cmd.action(action); // 配置命令执行函数});// 监听用户的help事件program.on('--help', () => {console.log("\nExamples:")Reflect.ownKeys(mapActions).forEach((key: string) => {const { examples } = mapActions[key];examples?.forEach((example) => {console.log(`  ${example}`);})});})
};export default CustomCommand;

每个命令的执行都有其对应的action函数来进行执行,这里的action也是大头,所以说这里我们仍然将其抽离出封装成一个hook函数,方便后期的维护,增大耦合度才能让项目的维护更加方便,运行的效果如下所示,感觉还是不错的哈:

仓库文档操作

当我们通过模板的一些创建命令交互式的选择好我们想要创建的模板之后,我们就需要从仓库中拉取事先配置好的模板到本地目录中,因为github有时候由于网络原因访问过慢,所以我们可以将模板都配置到国内的 码云 远程仓库当中,该仓库也配置了 api文档 方便对仓库当中的一些项目进行操作,这也方便了我们远程拉取模板的操作,如下所示我们点击右上角的申请授权之后,每个接口都会自动添加对于的access_token,后面我们根据自身情况来选择传递对于的参数:

然后我们在untils工具文件夹下封装一个axios工具函数,这里注意一下当我们对对git服务进行请求的时候,可能会出现如下的问题,导致这个问题的原因就是因为git服务的ssl协议没有通过验证,我们可以重新生成正规的SSL证书,当然有可能我们的gitlab就是用的ip地址,这时候也可以通过关闭验证直接解决,具体直接通过如下代码所示:

git SSL certificate problem: unable to get local issuer certificate

// axios基础封装
import axios from 'axios'
import * as https from 'https'const http = axios.create({baseURL: 'https://gitee.com/api/v5',timeout: 5000,httpsAgent: new https.Agent({rejectUnauthorized: false // 拒绝校验未被授权的证书(SSL证书)})
})// 请求拦截器
http.interceptors.request.use(config => {// 请求拦截器return config
}, error => {return Promise.reject(error)
})// 响应拦截器
http.interceptors.response.use(res => {return res.data},  error => {return Promise.reject(error)}
)export default http

接下来我们就可以创建一个api文件夹,然后可以调用git服务接口来获取仓库的一些相关信息:

// 统一管理仓库的相关接口
import http from '@utils/http.js'const access_token = '你的授权token值'
// 统一管理接口
enum API {REPOS_URL = '/user/repos', // 列出授权用户的所有仓库
}
// 列出授权用户的所有仓库接口
export const reqGetRepositoriesProjects = (data) =>http.get<any, any>(API.REPOS_URL, {headers: {'Authorization': `Bearer ${access_token}`,},data: { access_token, ...data }})

接口写完之后我们就可以在command命令行中的action事件函数当中调用改接口,可以看到我们仓库当中的所以仓库信息都被打印出来了:

当然gitee申请授权的token是有过期时间的,如果想设置不过期的token需要打开个人中心,然后找到私人令牌,然后给选择令牌的过期是时间是永不过期即可

当然后面如果配置好模板之后,想把模板设置私有仓库下载的话,可以设置一下ssh密钥,执行如下命令在git bash命令行上,生成ssh key:

ssh-keygen -t ed25519 -C "Gitee SSH Key"

输入命令一直回车即可: 

查看生成的 SSH 公钥和私钥,输出:私钥文件 id_ed25519;公钥文件 id_ed25519.pub

ls ~/.ssh/

读取公钥文件 ~/.ssh/id_ed25519.pub,输出密钥之后,复制到gitee的ssh公钥配置上:

cat ~/.ssh/id_ed25519.pub

模板下载操作

当我们配置好模板命令、交互选择以及仓库文档等操作之后,我们就可以下载我们的模板了,获取到项目模板名称和对应的版本之后,我们就可以直接下载了,上篇文章: 地址 我们下载模板的库是 git-clone,这里不再过多赘述,这里我们通过gitee的命令获取gitee上的私有仓库,并且仓库的特征关键字是template,如下所示:

接下来我们通过上面的一个简单的示例,把私有仓库当中的的template-test内容down到本地当中,如下所示:

模板渲染操作 

        如果用户想定制下载模板中的内容,这里我们就需要对模板渲染进行操作,拿package.json举例,用户可以根据终端交互命令选择的项目名称和一些其他操作,根据相对于的询问生成最终下载的模板的package.json内容,核心原理就是将下载的模板文件依次遍历根据用户填写的信息渲染模板,然后将渲染的模板拷贝到执行目录下,这里我们需要将模板渲染用到的插件进行安装,终端执行如下命令操作:

// metalsmith: 遍历所有文件目录配置json渲染
pnpm i metalsmith -D

安装完成之后,这里我把渲染模板文件的功能函数抽离出来,具体的代码如下所示:

import Metalsmith from 'metalsmith';
import { promisify } from 'util';
import { ejs } from 'consolidate';
import path from 'path';
import fs from 'fs-extra';let { render } = ejs;
render = promisify(render);export const handleTemplateRenders = async (name, metadataData = {}) => {const projectRoot = path.join(process.cwd(), name); // 获取项目根路径if (!fs.pathExistsSync(projectRoot)) { // 确保项目目录存在console.error(`项目目录不存在: ${projectRoot}`);return;}// 配置元数据const metadata = {name: name,author: 'Your Name',date: new Date().toLocaleDateString(),...metadataData};// 创建一个临时变量来存储需要处理的文件let filesToProcess = [];// 创建 Metalsmith 实例await new Promise<void>((resolve, reject) => {Metalsmith(projectRoot).source('.') // 从项目根目录读取文件.destination('.') // 输出到项目根目录(覆盖原始文件).clean(false) // 不清除目标目录,避免删除其他文件.metadata(metadata) // 设置元数据// 第一个插件:收集需要处理的文件.use((files, metalsmith, done) => {// 过滤需要处理的文件类型filesToProcess = Reflect.ownKeys(files).filter((file: any) => {const fileInfo = files[file];const content = fileInfo.contents.toString();const hasEjsTags = content.includes('<%') && content.includes('%>'); // 检查文件是否包含 EJS 标签return hasEjsTags;});done();})// 第二个插件:处理 EJS 模板.use(async (files, metalsmith, done) => {const meta = metalsmith.metadata();try {for (const file of filesToProcess) {const fileInfo = files[file];const originalContent = fileInfo.contents.toString();const renderedContent = await render(originalContent, meta); // 渲染 EJS 模板// 更新文件内容files[file].contents = Buffer.from(renderedContent);}done();} catch (err) {console.error('渲染模板时出错:', err);done(err);}}).build(err => {if (err) {console.error('Metalsmith 构建失败:', err);reject(err);} else {resolve();}});});// 验证渲染结果validateRenderResults(name);
};// 验证渲染结果的辅助函数
function validateRenderResults(name) {const projectRoot = path.join(process.cwd(), name);const packageJsonPath = path.join(projectRoot, 'package.json');if (fs.pathExistsSync(packageJsonPath)) {try {const content = fs.readFileSync(packageJsonPath, 'utf8');const pkg = JSON.parse(content);console.log('\n=== 渲染结果验证 ===');console.log('package.json 中的 name:', pkg.name);console.log('package.json 中的 author:', pkg.author);if (pkg.name === '<%= name %>' || pkg.author === '<%= author %>') {console.error('❌ 渲染失败: EJS 模板语法未被正确替换');} else {console.log('✅ 渲染成功: EJS 模板语法已被正确替换');}} catch (err) {console.error('验证渲染结果时出错:', err);}}
}

上面代码封装的功能函数中,形参name就是项目文件夹,metadataData就是你要渲染的模板的实际数据,这里我在仓库当中设置一个模板语法的package.json文件,如下所示可以看到我们的项目名称以及对应的作者名称都是需要通过用户输入来动态渲染的:

这里我们在下面完模板之后,调用一下替换模板语法的函数,这里就会当模板下载之后就会立即遍历整个文件夹,找到对应的有模板语法的文件,然后进行替换:

实现的效果如下所示,可以看到效果非常好,后期也可以根据自身的项目需求,让这个模板渲染变得更加复杂以适应不同的项目情况,这些都是可以的:

当然我们还可以通过EJS来实现模板渲染, EJS(Embedded JavaScript)是一个模板引擎,允许在HTML中插入动态内容,可以通过EJS渲染数据并生成最终的HTML页面,终端执行如下命令安装插件: 

// ejs: 动态渲染数据并生成最终的HTML页面
pnpm i ejs -D

EJS允许在HTML模板中嵌入JavaScript代码,用来动态生成内容,基本案例如下所示:

const ejs = require('ejs');const data = { title: 'Hello World', body: 'This is a test.' };ejs.renderFile('template.ejs', data, (err, str) => {if (err) {console.error(err);} else {console.log(str);  // 渲染后的 HTML 内容}
});

Consolidate.是一个模板引擎的统一接口,它提供了一种统一的方式来使用多种模板引擎(如 EJS、Pug等),通过Consolidate可以轻松地切换不同的模板引擎,终端执行如下命令安装插件:

// consolidate: 返回渲染函数,统一所有模板引擎
pnpm i consolidate -D

Consolidate.会根据传入的模板引擎来调用相应的渲染方法,基本用法如下所示:

const consolidate = require('consolidate');
const ejs = consolidate.ejs;ejs.renderFile('template.ejs', { title: 'Hello' }, (err, html) => {if (err) throw err;console.log(html);  // 渲染后的 HTML
});

相关文章:

前端脚手架开发指南:提高开发效率的核心操作

前端脚手架通过自动化的方式可以提高开发效率并减少重复工作&#xff0c;而最强大的脚手架并不是现成的那些工具而是属于你自己团队量身定制的脚手架&#xff01;本篇文章将带你了解脚手架开发的基本技巧&#xff0c;帮助你掌握如何构建适合自己需求的工具&#xff0c;并带着你…...

搜索引擎工作原理|倒排索引|query改写|CTR点击率预估|爬虫

写在前面 使用搜索引擎是我们经常做的事情&#xff0c;搜索引擎的实现原理。 什么是搜索引擎 搜索引擎是一种在线搜索工具&#xff0c;当用户在搜索框输入关键词时&#xff0c;搜索引擎就会将与该关键词相关的内容展示给用户。比较大型的搜索引擎有谷歌&#xff0c;百度&…...

Python实例题:Python自动工资条

目录 Python实例题 题目 python-automatic-payroll-slipPython 自动生成工资条脚本 代码解释 加载文件&#xff1a; 获取表头&#xff1a; 写入表头&#xff1a; 生成工资条&#xff1a; 保存文件&#xff1a; 运行思路 注意事项 Python实例题 题目 Python自动工资…...

Function Calling万字实战指南:打造高智能数据分析Agent平台

个人主页&#xff1a;Guiat 归属专栏&#xff1a;科学技术变革创新 文章目录 1. Function Calling&#xff1a;智能交互的新范式1.1 Function Calling 技术概述1.2 核心优势分析 2. 数据分析Agent平台架构设计2.1 系统架构概览2.2 核心组件解析2.2.1 函数注册中心2.2.2 Agent控…...

spark MySQL数据库配置

Spark 连接 MySQL 数据库的配置 要让 Spark 与 MySQL 数据库实现连接&#xff0c;需要进行以下配置步骤。下面为你提供详细的操作指南和示例代码&#xff1a; 1. 添加 MySQL JDBC 驱动依赖 你得把 MySQL 的 JDBC 驱动添加到 Spark 的类路径中。可以通过以下两种方式来完成&a…...

python四则运算计算器

python四则运算计算器 是谁说&#xff0c;python不好写计算器的&#xff0c;我亲自写个无ui的计算器功能&#xff0c;证明这是谣言 step1:C:\Users\wangrusheng\Downloads\num.txt 15 - 4 * 3 10 / 2(5 3) * 2 6 / 31/2 * 8 3/4 * 4 - 0.52.5 * (4 1.6) - 9 / 3-6 12 * (…...

线对板连接器的兼容性问题:为何老旧设计难以满足现代需求?

线对板连接器作为电子设备的核心纽带&#xff0c;正面临前所未有的兼容性挑战。某智能工厂升级生产线时发现&#xff0c;沿用十年的2.54毫米间距连接器&#xff0c;在接入新型工业相机时出现30%的信号丢包率&#xff0c;而切换至0.4毫米超密间距连接器后&#xff0c;数据传输速…...

AI517 AI本地部署 docker微调(失败)

本地部署AI 计划使用OLLAMA进行本地部署 修改DNS 访问github 刷新缓存 配置环境变量 OLLAMA安装成功 部署成功 计划使用docker进行微调 下载安装docker 虚拟化已开启 开启上面这些 准备下载ubuntu docker ragflow dify 用git去泡...

VR和眼动控制集群机器人的方法

西安建筑科技大学信息与控制工程学院雷小康老师团队联合西北工业大学航海学院彭星光老师团队&#xff0c;基于虚拟现实&#xff08;VR&#xff09;和眼动追踪技术实现了人-集群机器人高效、灵活的交互控制。相关研究论文“基于虚拟现实和眼动的人-集群机器人交互方法” 发表于信…...

python训练营打卡第26天

函数专题1&#xff1a;函数定义与参数 知识点回顾&#xff1a; 函数的定义变量作用域&#xff1a;局部变量和全局变量函数的参数类型&#xff1a;位置参数、默认参数、不定参数传递参数的手段&#xff1a;关键词参数传递参数的顺序&#xff1a;同时出现三种参数类型时 作业&…...

TiDB 中新 Hash Join 的设计与性能优化

原文来源&#xff1a; https://tidb.net/blog/11667c37 本文作者&#xff1a;徐飞 导读 在数据库管理系统&#xff08;DBMS&#xff09;中&#xff0c;连接操作&#xff08;Join&#xff09;是查询处理的核心环节之一&#xff0c;其性能直接影响到整个系统的响应速度和效率…...

1.共享内存(python共享内存实际案例,传输opencv frame)

主进程程序 send.py import cv2 import numpy as np from multiprocessing import shared_memory, resource_trackercap cv2.VideoCapture(0) if not cap.isOpened():print("无法打开 RTSP 流&#xff0c;请检查地址、网络连接或 GStreamer 配置。") else:# 创建共…...

网页常见水印实现方式

文章目录 1 明水印技术实现1.1 DOM覆盖方案1.2 Canvas动态渲染1.3 CSS伪元素方案2 暗水印技术解析2.1 空域LSB算法2.2 频域傅里叶变换3 防篡改机制设计3.1 MutationObserver防护3.2 Canvas指纹追踪4 前后端实现对比5 攻防博弈深度分析5.1 常见破解手段5.2 进阶防御策略6 选型近…...

oracle主备切换参考

主备正常切换操作参考&#xff1a;RAC两节点->单机 &#xff08;rac和单机的操作区别&#xff1a;就是关闭其它节点&#xff0c;剩一个节点操作即可&#xff09; 1.主库准备 检查状态 SQL> select inst_id,database_role,OPEN_MODE from gv$database; INST_ID DATA…...

Java大师成长计划之第25天:Spring生态与微服务架构之容错与断路器模式

&#x1f4e2; 友情提示&#xff1a; 本文由银河易创AI&#xff08;https://ai.eaigx.com&#xff09;平台gpt-4-turbo模型辅助创作完成&#xff0c;旨在提供灵感参考与技术分享&#xff0c;文中关键数据、代码与结论建议通过官方渠道验证。 在微服务架构中&#xff0c;系统通常…...

【ARM】MDK如何将变量存储到指定内存地址

1、 文档目标 在嵌入式系统开发中&#xff0c;通过MDK&#xff08;Microcontroller Development Kit&#xff09;进行工程配置&#xff0c;将指定的变量存储到指定的内存地址上是一项非常重要的技术。这项操作不仅能够满足特定硬件架构的需求&#xff0c;还能优化系统的性能和…...

Unity3D仿星露谷物语开发44之收集农作物

1、目标 在土地中挖掘后&#xff0c;洒下种子后逐渐成长&#xff0c;然后使用篮子收集成熟后的农作物&#xff0c;工具栏中也会相应地增加该农作物。 2、修改CropStandard的参数 Assets -> Prefabs -> Crop下的CropStandard&#xff0c;修改其Box Collider 2D的Size(Y…...

langchain—chatchat

署部 下载项目 git clone --recursive https://github.com/chatchat-space/Langchain-Chatchat.git 进入目录 cd Langchain-Chatchat anaconda环境准备 创建python环境 conda create -n langchain_env python3.10 -y 激活环境 conda activate langchain_env 验证pyhton环境…...

经典算法 求C(N, K) % mod,保证mod是质数

求C(N, K) % mod&#xff0c;保证mod是质数 问题描述 给你三个整数N,K,mod保证mod是一个质数&#xff0c;求组合数C(N, K) % mod。 输入描述 输入有多组&#xff0c;输入第一行为两个整数T&#xff0c;mod。接下来2 - T 1行&#xff0c;每行输入N&#xff0c; K。 输出描…...

【LeetCode 热题 100】二叉树的最大深度 / 翻转二叉树 / 二叉树的直径 / 验证二叉搜索树

⭐️个人主页&#xff1a;小羊 ⭐️所属专栏&#xff1a;LeetCode 热题 100 很荣幸您能阅读我的文章&#xff0c;诚请评论指点&#xff0c;欢迎欢迎 ~ 目录 二叉树的中序遍历二叉树的最大深度翻转二叉树对称二叉树二叉树的直径二叉树的层序遍历将有序数组转换为二叉搜索树验…...

关于软件测试开发的一些有趣的知识

文章目录 一、什么是测试&#xff1f;二、为什么要软件测试软件测试三、测试的岗位有哪些四 、软件测试和开发的区别五、走测试岗位为什么还要学开发。4、优秀的测试人员具备的素质我为什么走测试岗位 一、什么是测试&#xff1f; 其实这个问题说简单也不简单&#xff0c;说难…...

uni-app 开发HarmonyOS的鸿蒙影视项目分享:从实战案例到开源后台

最近&#xff0c;HBuilderX 新版本发布&#xff0c;带来了令人兴奋的消息——uni-app 现在支持 Harmony Next 平台的 App 开发。这对于开发者来说无疑是一个巨大的福音&#xff0c;意味着使用熟悉的 Vue 3 语法和开发框架&#xff0c;就可以为鸿蒙生态贡献自己的力量。 前言 作…...

售前工作.工作流程和工具

第一部分 售前解决方案及技术建议书的制作 售前解决方案编写的标准操作步骤SOP: 售前解决方案写作方法_哔哩哔哩_bilibili 第二部分 投标过程关键活动--商务标技术方案 1. 按项目管理--售前销售项目立项 销售活动和销售线索的跟踪流程和工具 1&#xff09;拿到标书&#xff…...

GPU与NPU异构计算任务划分算法研究:基于强化学习的Transformer负载均衡实践

点击 “AladdinEdu&#xff0c;同学们用得起的【H卡】算力平台”&#xff0c;H卡级别算力&#xff0c;按量计费&#xff0c;灵活弹性&#xff0c;顶级配置&#xff0c;学生专属优惠。 引言 在边缘计算与AI推理场景中&#xff0c;GPU-NPU异构计算架构已成为突破算力瓶颈的关键技…...

学习ai课程大纲

以下是一个通用的 AI 课程大纲&#xff0c;涵盖从基础到进阶的核心内容&#xff0c;适用于大学课程或自学规划。你可以根据自身需求&#xff08;如入门、进阶、专项方向&#xff09;调整内容和深度。 人工智能&#xff08;AI&#xff09;课程大纲 第一部分&#xff1a;基础理论…...

基于CentOS7制作OpenSSL 1.1的RPM包

背景&#xff1a;CentOS7 已经不再维护了&#xff0c;有时候需要升级某些组件&#xff0c;网上却没有相关的资源了。尤其是制作OpenSSH 9.6 的RPM包&#xff0c;就会要求OpenSSL为1.1的版本。基于此&#xff0c;还是自己制作吧&#xff0c;以下是踩坑过程。 1、官网提供的源码包…...

数据分析_Python

1 分析内容 1.1 数据的整体概述 提供数据集的基本信息,包括数据量、时间跨度、地理范围和主要字段. import pandas as pd# 创建示例数据 data {姓名: [张三, 李四, 王五, 赵六, 钱七, 孙八, 周九, 吴十],年龄: [25, 30, 35, 40, 45, 50, 55, 60],性别: [男, 男, 女, 女, 男,…...

TCP/UDP协议原理和区别 笔记

从简单到难吧 区别就是TCP一般用于安全稳定的需求&#xff0c;UDP一般用于不那么需要完全数据的需求&#xff0c;比如说直播&#xff0c;视频等。 再然后就是TPC性能慢于UDP。 再然后我们看TCP的原理&#xff08;三次握手&#xff0c;数据传输&#xff0c;四次挥手&#xff0…...

深入浅出:C++数据处理类与计算机网络的巧妙类比

深入浅出&#xff1a;C数据处理类与计算机网络的巧妙类比 引言 在计算机编程中&#xff0c;我们常常会遇到一些看似简单的代码结构&#xff0c;却能巧妙地映射到复杂的计算机网络概念中。本文将通过一个简单的C数据处理类&#xff0c;探讨其与计算机网络中硬件设备和协议的类…...

【滑动窗口】LeetCode 209题解 | 长度最小的子数组

长度最小的子数组 前言&#xff1a;滑动窗口一、题目链接二、题目三、算法原理解法一&#xff1a;暴力枚举解法二&#xff1a;利用单调性&#xff0c;用滑动窗口解决问题那么怎么用滑动窗口解决问题&#xff1f;分析滑动窗口的时间复杂度 四、编写代码 前言&#xff1a;滑动窗口…...