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

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

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

目录

初始化项目

模板命令操作

仓库文档操作

模板下载操作

模板渲染操作 

初始化项目

关于脚手架的基本概念和一些常用工具的讲解,上篇文章 地址 已经讲解的比较清楚了,本篇文章不再赘述,接下来我们还是初始化一个新的项目进行操作,终端执行 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;并带着你…...

职坐标AIoT技能培训课程实战解析

职坐标AIoT技能培训课程以人工智能与物联网技术深度融合为核心&#xff0c;构建了“理论实战行业应用”三位一体的教学体系。课程体系覆盖Python编程基础、传感器数据采集、边缘计算开发、云端服务部署及智能硬件开发全链路&#xff0c;通过分层递进的知识模块帮助学员建立系统…...

Yocto Project 快速构建

此文为机器辅助翻译&#xff0c;仅供个人学习使用&#xff0c;如有翻译不当之处欢迎指正 1 Yocto 项目快速构建 1.1 欢迎&#xff01; 本简短文档将引导您完成使用 Yocto Project 进行典型镜像构建的流程&#xff0c;并介绍如何为特定硬件配置构建环境。您将使用 Yocto Proj…...

git相关配置

git相关配置 欢迎使用Markdown编辑器修改Git默认编辑器为vimgit配置默认用户名和密码&#xff1a; 欢迎使用Markdown编辑器 修改Git默认编辑器为vim #方法1&#xff1a;直接执行 git config --global core.editor vim#方法2&#xff1a;修改git的配置文件.git/config文件&am…...

ci/cd全流程实操

本次采用架构,gitlab + jenkins + 镜像仓库+ k8s 准备工作 一、gitlab部署 拉取镜像 部署环境: macbook m2中docker部署gitlab (m2平台架构问题,这里只能用yrzr/gitlab-ce-arm64v8 这个容器镜像) docker pull yrzr/gitlab-ce-arm64v8 在 Docker 里,–privileged=tr…...

Python中in和is关键字详解和使用

在 Python 中&#xff0c;in 和 is 是两个常用但含义不同的关键字&#xff0c;初学者很容易混淆它们的用法。下面是关于它们的详细解释、注意事项及常见示例。 一、关键字 in&#xff1a;成员运算符 1. 功能 用于判断某个元素是否存在于序列&#xff08;如列表、元组、字符串…...

ACM模式用Scanner和System.out超时的解决方案和原理

Hi~&#xff01;这里是奋斗的明志&#xff0c;很荣幸您能阅读我的文章&#xff0c;诚请评论指点&#xff0c;欢迎欢迎 ~~ &#x1f331;&#x1f331;个人主页&#xff1a;奋斗的明志 &#x1f331;&#x1f331;所属专栏&#xff1a;笔试强训 &#x1f4da;本系列文章为个人学…...

微服务中服务降级和异常的区别

在Java中&#xff0c;服务降级和异常处理是两个相关但不同的概念。它们的主要区别如下&#xff1a; 1. 服务降级&#xff08;Service Degradation&#xff09;: 定义&#xff1a;服务降级是指在系统中某个服务或功能出现问题时&#xff0c;通过采取某些策略来降低服务的质量或…...

MYSQL创建索引的原则

创建索引的原则包括&#xff1a; 表中的数据量超过10万以上时考虑创建索引。 选择查询频繁的字段作为索引&#xff0c;如查询条件、排序字段或分组字段。 尽量使用复合索引&#xff0c;覆盖SQL的返回值。 如果字段区分度不高&#xff0c;可以将其放在组合索引的后面。 对于…...

29、魔法微前端——React 19 模块化架构

一、时空结界分割术&#xff08;模块化架构设计&#xff09; 1. 次元切割协议 // 主应用入口const HogwartsMain () > {const [subApps] useState({potion: React.lazy(() > import(./PotionShop)),library: React.lazy(() > import(./LibraryApp)),quidditch: R…...

【PmHub后端篇】PmHub 中缓存与数据库一致性的实现方案及分析

在软件开发项目中&#xff0c;缓存的使用十分普遍。缓存作为一种存储机制&#xff0c;能够暂时保存数据&#xff0c;从而加速数据的读取和访问。然而&#xff0c;当数据同时存在于缓存和数据库中时&#xff0c;如何保证两者的数据一致性成为了一个关键问题。在 PmHub 项目中&am…...

Verilog HDL 语言整理

Verilog HDL 语言 Verilog HDL 简介 硬件描述语言Hardware Description Language是一种用形式化方法即文本形式 来描述和设计数字电路和数字系统的高级模块化语言 Verilog HDL&#xff08;Hardware Description Language&#xff09;是一种硬件描述语言&#xff0c;用于建模…...

[250516] OpenAI 升级 ChatGPT:GPT-4.1 及 Mini 版上线!

目录 ChatGPT 迎来重要更新&#xff1a;GPT-4.1 和 GPT-4.1 mini 正式上线用户如何访问新模型&#xff1f;技术亮点与用户体验优化 ChatGPT 迎来重要更新&#xff1a;GPT-4.1 和 GPT-4.1 mini 正式上线 OpenAI 宣布在 ChatGPT 平台正式推出其最新的 AI 模型 GPT-4.1 和 GPT-4.…...

R语言学习--Day03--数据清洗技巧

在一般情况下&#xff0c;我们都是在数据分析的需求前提下去选择使用R语言。而实际上&#xff0c;数据分析里&#xff0c;百分之八十的工作&#xff0c;都是在数据清洗。并不只是我们平时会提到的异常值处理或者是整合格式&#xff0c;更多会涉及到将各种各样的数据整合&#x…...

文件系统交互实现

关于之前的搭建看QT控件文件系统的实现-CSDN博客&#xff0c;接下来是对本程序的功能完善&#xff0c;我想着是这样设计的&#xff0c;打开一个目录以后&#xff0c;鼠标选中一个项可以是目录&#xff0c;也可以是文件&#xff0c;右键可以出现一个菜单选择操作&#xff0c;比如…...

SqlHelper 实现类,支持多数据库,提供异步操作、自动重试、事务、存储过程、分页、缓存等功能。

/// <summary> /// SqlHelper 实现类&#xff0c;支持多数据库&#xff0c;提供异步操作、自动重试、事务、存储过程、分页、缓存等功能。 /// </summary> public class SqlHelper : IDbHelper {private readonly IDbConnectionFactory _connectionFactory;private…...

DevExpressWinForms-RichEditControl-基础应用

RichEditControl-基础应用 在企业级WinForms应用开发中&#xff0c;富文本编辑与文档处理是常见需求。DevExpress WinForms的RichEditControl作为一款功能强大的富文本编辑控件&#xff0c;提供了媲美Microsoft Word的文档处理能力&#xff0c;支持复杂格式编辑、打印导出、界…...

Elasticsearch 索引副本数

作者&#xff1a;来自 Elastic Kofi Bartlett 解释如何配置 number_of_replicas、它的影响以及最佳实践。 更多阅读&#xff1a;Elasticsearch 中的一些重要概念: cluster, node, index, document, shards 及 replica 想获得 Elastic 认证&#xff1f;查看下一期 Elasticsearc…...

RabbitMQ 扇形交换器工作原理详解

目录 一、扇形交换器简介二、扇形交换器工作原理2.1 消息广播机制2.2 路由键的忽略三、代码示例3.1 生产者代码3.2 消费者代码四、实际应用场景4.1 日志收集系统4.2 实时通知系统4.3 事件驱动架构五、总结在 RabbitMQ 的众多交换器类型中,扇形交换器(Fanout Exchange)是一种…...

IDEA中springboot项目中连接docker

具体内容如下&#xff1a; 1、在Linux中安装docker 使用安装命令&#xff1a; apt-get install docker.io 还有一个是更新软件并安装docker&#xff1a; sudo apt-get update sudo apt-get install docker-ce docker-ce-cli containerd.io 运行docker systemctl start …...

arxiv等开源外文书数据的获取方式

一、一些基本说明 开放API接口文档&#xff1a;https://info.arxiv.org/help/api/user-manual.html#2-api-quickstart研究领域分类说明文档&#xff1a;https://arxiv.org/category_taxonomy 二、基于url接口方式检索并获取数据 本质是get方式&#xff0c;在url中传检索参数…...

ChatGPT再升级!

近日&#xff0c;OpenAI 正式发布 GPT-4.1 和轻量级版本 GPT-4.1mini&#xff0c;并已全面上线 ChatGPT 平台&#xff0c;迅速引发全球 AI 圈热议&#xff0c;标志着 ChatGPT 在智能化和效率上再登新高峰。 GPT-4.1 是为编程与任务处理优化的高性能模型。相较前作 GPT-4o&#…...

23、电网数据管理与智能分析 - 负载预测模拟 - /能源管理组件/grid-data-smart-analysis

76个工业组件库示例汇总 电网数据管理与智能分析组件 1. 组件概述 本组件旨在模拟一个城市配电网的运行状态&#xff0c;重点关注数据管理、可视化以及基于模拟数据的智能分析&#xff0c;特别是负载预测功能。用户可以通过界面交互式地探索电网拓扑、查看节点状态、控制时间…...

#跟着若城学鸿蒙# web篇-获取定位

前言 在业务中&#xff0c;某些网页上需要获取用户的地理位置&#xff0c;然后按照用户搜索的兴趣点与用户的距离远近进行排序&#xff0c;这就需要h5能够获取到用户的位置。 由于 web 组件基于Chromium M114 版本开发&#xff0c;前端就可以使用navigator.geolocation.getC…...

前端批量下载文件打包为zip

多文件需要一次性下载为zip文件 这是近期遇到的一个需求&#xff0c;本身是多文件上传的&#xff0c;下载时单个下载太慢又繁杂&#xff0c;用户希望能一次性批量下载&#xff0c;就选择了jszip import axios from "axios" import JSZip from "jszip" im…...

Vue百日学习计划Day9-15天详细计划-Gemini版

重要提示&#xff1a; 番茄时钟&#xff1a; 每个番茄钟为25分钟学习&#xff0c;之后休息5分钟。每完成4个番茄钟&#xff0c;进行一次15-30分钟的长休息。灵活性&#xff1a; JavaScript 的概念较多&#xff0c;尤其是 this、原型链、闭包和异步编程&#xff0c;可能需要更多…...

MySQL8.x新特性:与mysql5.x的版本区别

MySQL8.x新特性 1.与mysql5.x的区别&#xff1a;MySQL8.x新特性&#xff1a;与mysql5.x的版本区别-CSDN博客 2.窗口函数&#xff08;Window Functions&#xff09;&#xff1a;MySQL8.x新特性&#xff1a;窗口函数&#xff08;Window Functions&#xff09;-CSDN博客 引言 …...

RabbitMQ 消息模式实战:从简单队列到复杂路由(三)

精准投递&#xff1a;路由模式 路由模式详解 路由模式是 RabbitMQ 中一种功能强大且灵活的消息传递模式&#xff0c;它在发布订阅模式的基础上&#xff0c;引入了路由键&#xff08;Routing Key&#xff09;的概念&#xff0c;实现了消息的精准路由和分发 。在路由模式中&…...

STM32 定时器主从模式配置解析

STM32 定时器主从模式配置解析 下面这两行代码是配置STM32定时器主从模式的关键设置 代码功能解析 TIM_SelectInputTrigger(TIM3, TIM_TS_TI2FP2); // 选择从模式输出的触发源 TIM_SelectSlaveMode(TIM3, TIM_SlaveMode_Reset); // 选择从模式1. TIM_SelectInputTrigger(T…...

Leetcode76覆盖最小子串

覆盖最小子串 代码来自b站左程云 class Solution {public String minWindow(String str, String tar) {char[] s str.toCharArray();char[] t tar.toCharArray();int[] cnt new int[256];for (char cha : t) { cnt[cha]--;}int len Integer.MAX_VALUE;int debt t.length…...

Perl语言深度考查:从文本处理到正则表达式的全面掌握

阅读原文 前言&#xff1a;为什么Perl依然值得学习&#xff1f; "这个脚本用Perl写只需要5分钟&#xff01;"——在当今Python大行其道的时代&#xff0c;你依然能在不少企业的运维部门听到这样的对话。Perl作为一门有着30多年历史的语言&#xff0c;凭借其强大的文…...

idea中Lombok失效的解决方案

Lombok 是一个 Java 库&#xff0c;旨在通过注解简化 Java 代码的编写&#xff0c;减少样板代码&#xff0c;提高开发效率。它通过自动生成常见的代码&#xff08;如 getter、setter、构造函数等&#xff09;来减少开发者的手动编码工作。 一般Lombok失效有四步排查方案&#…...

【LeetCode 热题 100】动态规划 系列

&#x1f4c1; 70. 爬楼梯 状态标识&#xff1a;爬到第i层楼梯时&#xff0c;有多少种方法。 状态转移方程&#xff1a;dp[i] dp[i-1] dp[i-2]&#xff0c;表示从走一步和走两步的方式。 初始化&#xff1a;dp[1] 1 , dp[2] 2。 返回值&#xff1a;dp[n]&#xff0c;即走到…...

刷leetcodehot100返航版--双指针5/16

for (int i 0, j 0; i < n; i ) { while (j < i && check(i, j)) j ; // 具体问题的逻辑 } 常见问题分类&#xff1a; (1) 对于一个序列&#xff0c;用两个指针维护一段区间 (2) 对于两个序列&#xff0c;维护某种次序&#xff0c;比如归并排序中…...

DAY24元组和OS模块

元组 元组的特点&#xff1a; 有序&#xff0c;可以重复&#xff0c;这一点和列表一样元组中的元素不能修改&#xff0c;这一点非常重要&#xff0c;深度学习场景中很多参数、形状定义好了确保后续不能被修改。 很多流行的 ML/DL 库&#xff08;如 TensorFlow, PyTorch, Num…...

CSS:三大特性

文章目录 一、层叠性二、继承性三、优先级 一、层叠性 二、继承性 可以在MDN网站上查看属性是否可以被继承 例如color 三、优先级...

Cross-Site Scripting(XSS)

1. XSS介绍 跨站脚本攻击&#xff08;Cross-Site Scripting&#xff09;简称XSS&#xff0c;人们经常将跨站脚本攻击&#xff08;Cross Site Scripting&#xff09;缩写为CSS&#xff0c;但这会与层叠样式表&#xff08;Cascading Style Sheets&#xff0c;CSS&#xff09;的缩…...

掌握HTML文件上传:从基础到高级技巧

HTML中input标签的上传文件功能详解 一、基础概念 1. 文件上传的基本原理 在Web开发中&#xff0c;文件上传是指将本地计算机中的文件&#xff08;如图片、文档、视频等&#xff09;传输到服务器的过程。HTML中的<input type"file">标签是实现这一功能的基础…...

WebRTC中的几个Channel

一、我指的是谁&#xff1f; 以视频为例&#xff0c;常见的有&#xff1a;MediaChannel、VideoMediaChannel、WebRtcVideoChannel、BaseChannel、VideoChannel&#xff0c;那么&#xff0c;为什么要这么多Channel&#xff0c;只写一个叫做SuperChannel行不行&#xff08;很多程…...

【设计模式】- 行为型模式1

模板方法模式 定义了一个操作中的算法骨架&#xff0c;将算法的一些步骤推迟到子类&#xff0c;使得子类可以不改变该算法结构的情况下重定义该算法的某些步骤 【主要角色】&#xff1a; 抽象类&#xff1a;给出一个算法的轮廓和骨架&#xff08;包括一个模板方法 和 若干基…...

容器化-k8s-使用和部署

一、K8s 使用 1、基本概念 集群: 由 master 节点和多个 slaver 节点组成,是 K8s 的运行基础。节点: 可以是物理机或虚拟机,是 K8s 集群的工作单元,运行容器化应用。Pod: K8s 中最小的部署单元,一个 Pod 可以包含一个或多个紧密相关的容器,这些容器共享网络和存储资源。…...

黑马k8s(九)

1.Pod-生命周期概述 2.Pod生命周期-创建和终止 3.Pod生命周期-初始化容器...

Android trace中CPU的RenderThread与GPU

Android trace中CPU的RenderThread与GPU RenderThread是系统的GPU绘制线程&#xff0c;GPU渲染就是通常所谓的硬件加速&#xff0c;如果应用关闭硬件加速&#xff0c;就没有了RenderThread&#xff0c;只有UI Thread&#xff0c;即Android主线程。 Android GPU渲染SurfaceFlin…...

测试工程师如何学会Kubernetes(k8s)容器知识

Kubernetes(K8s)作为云原生时代的关键技术之一&#xff0c;对于运维工程师、开发工程师以及测试工程师来说&#xff0c;都是一门需要掌握的重要技术。作为一名软件测试工程师&#xff0c;学习Kubernetes是一个有助于提升自动化测试、容器化测试以及云原生应用测试能力的重要过程…...

接触感知 钳位电路分析

以下是NG板接触感知电路的原理图。两极分别为P3和P4S&#xff0c;电压值P4S < P3。 电路结构分两部分&#xff0c;第一部分对输入电压进行分压钳位。后级电路使用LM113比较器芯片进行电压比较&#xff0c;输出ST接触感知信号。 钳位电路输出特性分析 输出电压变化趋势&a…...

码蹄集——圆包含

MT1181 圆包含 输入2个圆的圆心的坐标值&#xff08;x&#xff0c;y&#xff09;和半径&#xff0c;判断断一个圆是否完全包含另一个圆&#xff0c;输出YES或者NO。另&#xff1a;内切不算做完全包含。 格式 输入格式&#xff1a;输入整型&#xff0c;空格分隔。 每行输入一组…...

ConcurrentSkipListMap的深入学习

目录 1、介绍 1.1、线程安全 1.2、有序性 1.3、跳表数据结构 1.4、API 提供的功能 1.5、高效性 1.6、应用场景 2、数据结构 2.1、跳表&#xff08;Skip List&#xff09; 2.2、节点类型&#xff1a; 1.Node 2.Index 3.HeadIndex 2.3、特点 3、选择层级 3.1、随…...

ProfibusDP主站转modbusTCP网关接DP从站网关通讯案例

ProfibusDP主站转modbusTCP网关接DP从站网关通讯案例 在工业自动化领域&#xff0c;Profibus DP和Modbus TCP是两种常见的通信协议。Profibus DP广泛应用于过程自动化、工厂自动化等场景&#xff0c;而Modbus TCP则常见于楼宇自动化、能源管理等领域。由于设备和系统之间往往存…...

第一次做逆向

题目来源&#xff1a;ctf.show 1、下载附件&#xff0c;发现一个exe和一个txt文件 看看病毒加没加壳&#xff0c;发现没加那就直接放IDA 放到IDA找到main主函数&#xff0c;按F5反编译工具就把他还原成类似C语言的代码 然后我们看逻辑&#xff0c;将flag.txt文件的内容进行加…...

【项目】自主实现HTTP服务器:从Socket到CGI全流程解析

00 引言 ​ 在构建高效、可扩展的网络应用时&#xff0c;理解HTTP服务器的底层原理是一项必不可少的技能。现代浏览器与移动应用大量依赖HTTP协议完成前后端通信&#xff0c;而这一过程的背后&#xff0c;是由网络套接字驱动的请求解析、响应构建、数据传输等一系列机制所支撑…...