• 考研备考管家
    一、概述1.1 案例介绍考研备考管家是一款面向考研学子的 AI 智能学习管理网站,覆盖数学一、英语一、政治、408计算机基础四门统考科目,提供知识图谱追踪、刷题记录、艾宾浩斯错题复习、智能排期、AI 管家对话、专注计时等一站式备考功能。前端采用 Vue3 + Element Plus 手绘手账风格,后端采用 Flask + SQLite + DeepSeek 大模型,全栈可一键构建运行。项目地址:https://github.com/LeoMay23/ExamPreparationDEMO展示:https://www.bilibili.com/video/BV1MpKh6nEJr/?spm_id_from=333.1387.upload.video_card.click&vd_source=69e3b625d4662860466dccf85672cf571.2 适用对象高校学生个人开发者1.3 案例时间本案例总时长预计100分钟。1.4 案例流程说明:环境准备:安装 Node.js、Python,配置 DeepSeek 大模型 API Key;后端构建:安装 Python 依赖,初始化 SQLite 数据库与知识点种子数据,启动 Flask 服务;前端构建:安装 npm 依赖,配置 Vite 代理,构建并启动 Vue3 开发服务器;功能体验:注册登录 → 考研档案 → 仪表盘 → 科目中心 → 刷题记录 → 错题本 → 智能计划 → AI 管家 → 专注学习 → 复盘中心。1.5 资源总览本案例预计花费10元。所有依赖均为开源免费软件,大模型 API 可使用 DeepSeek 免费额度。资源名称规格单价(元)Node.jsv22.x免费Python3.10+免费DeepSeek APIdeepseek-v4-pro10元华为云码道(CodeArts)代码智能体专业版代金券兑换二、环境和资源准备2.1 安装 Node.js 与 Python确保本地已安装:Node.js v18+(推荐 v22.x),下载地址:cid:link_1Python 3.10+,下载地址:cid:link_0验证安装:node --version python --version 2.2 配置 DeepSeek 大模型 API Key注册 DeepSeek 开放平台账号在「API Keys」页面创建新的 API Key,复制保存。在项目 backend/ 目录下创建 .env 文件,填入以下内容:DEEPSEEK_BASE_URL=https://api.deepseek.com DEEPSEEK_AUTH_TOKEN=sk-xxxxxxxxxxxxxxxxxxxxxxxx DEEPSEEK_MODEL=deepseek-v4-pro2.3 获取项目代码将项目代码下载到本地工作目录:git clone <项目仓库地址> ExamPreparation cd ExamPreparation三、构建考研备考管家应用3.1 项目结构说明ExamPreparation/ ├── backend/ # Flask 后端 │ ├── app.py # 应用入口,注册蓝图 │ ├── config.py # 配置(DB路径、DeepSeek API) │ ├── database.py # 数据库表定义 + 种子数据 + 迁移 │ ├── requirements.txt # Python 依赖 │ ├── .env # 环境变量(API Key 等) │ ├── agent/ # AI Agent 模块 │ │ ├── llm_client.py # DeepSeek LLM 客户端 │ │ ├── orchestrator.py # Agent 编排器 │ │ ├── prompts.py # Prompt 模板 │ │ ├── tool_registry.py # 工具注册 │ │ └── tool_executor.py # 工具执行器 │ ├── planner/ # 排期引擎 │ │ ├── scheduler.py # 6维权重4阶段排期算法 │ │ └── time_slot.py # 空闲时段计算 │ ├── routes/ # API 路由(11个蓝图) │ │ ├── auth.py # 用户注册/登录/档案 │ │ ├── subjects.py # 科目中心(14个端点) │ │ ├── mistakes.py # 错题本 │ │ ├── plans.py # 智能计划 │ │ ├── chat.py # AI 管家对话 │ │ ├── sessions.py # 专注学习 Session │ │ └── ... │ └── services/ # 业务逻辑层 │ ├── subject_service.py # 科目中心(知识树/刷题/词汇/背诵/算法) │ ├── mistake_service.py # 错题本(艾宾浩斯复习) │ ├── planner_service.py # 计划服务(生成/延后/提前/锁定) │ ├── ai_butler_service.py# AI管家(今日概览/提醒/专注建议) │ ├── user_service.py # 用户系统 │ └── ... ├── web/ # Vue3 前端 │ ├── package.json # npm 依赖 │ ├── vite.config.js # Vite 配置(代理 /api → localhost:5000) │ └── src/ │ ├── main.js # 应用入口 │ ├── assets/main.css # 全局手绘风格 CSS │ ├── router/index.js # 路由(15个页面 + 守卫) │ ├── stores/ # Pinia 状态管理 │ │ ├── user.js # 用户状态(登录/档案/倒计时) │ │ ├── subject.js # 科目状态(知识树/统计/刷题) │ │ └── mistake.js # 错题状态 │ ├── utils/api.js # Axios 封装(拦截器注入 user_id/token) │ ├── components/ │ │ └── layout/MainLayout.vue # 侧边栏 + 主内容区 │ └── views/ # 15个页面 │ ├── Login.vue # 登录注册 │ ├── Onboarding.vue # 考研档案 │ ├── Dashboard.vue # 仪表盘(倒计时+4科卡片+今日计划+错题) │ ├── Subjects.vue # 科目中心入口 │ ├── math/MathIndex.vue # 数学一 │ ├── english/EnglishIndex.vue # 英语一 │ ├── politics/PoliticsIndex.vue # 政治 │ ├── cs408/Cs408Index.vue # 408 │ ├── Plan.vue # 智能计划 │ ├── Chat.vue # AI 管家 │ ├── Focus.vue # 专注学习 │ ├── Mistakes.vue # 错题本 │ ├── Review.vue # 复盘中心 │ ├── Tasks.vue # 计划管理 │ └── Settings.vue # 设置 └── HLD-KaoyanPrep-Website-20260710.md # HLD 设计文档 3.2 后端:安装依赖并启动服务步骤1:进入后端目录,安装 Python 依赖:cd backend pip install -r requirements.txtrequirements.txt 内容如下:Flask==3.0.3 flask-cors==4.0.1 requests==2.32.3 python-dotenv步骤2:启动 Flask 服务:python app.py启动成功后,服务运行在 http://localhost:5000。首次启动时会自动:创建 SQLite 数据库 aiplanpal.db建立全部 25 张表插入知识点种子数据(数学64个、政治32个、408共55个节点)关键代码讲解:数据库初始化与种子数据backend/database.py 中的 init_db() 函数负责数据库的创建与迁移:def init_db(): with closing(sqlite3.connect(DB_PATH)) as conn: conn.row_factory = sqlite3.Row conn.execute("PRAGMA foreign_keys = ON") create_tables(conn) # 创建全部25张表 migrate_existing_tables(conn) # 迁移旧表结构 + 种子数据 conn.commit() 考研专用表包括:表名用途math_knowledge_nodes数学知识点(高数/线代/概率论,64个节点)math_practice_records数学刷题记录english_vocabulary_progress英语词汇复习进度(艾宾浩斯间隔)english_reading_records英语阅读训练记录english_writing_records英语写作记录politics_knowledge_nodes政治知识点(马原/毛中特/史纲/思修,32个节点)politics_choice_records政治选择题记录politics_memorization_progress政治背诵进度cs408_knowledge_nodes408知识点(数据结构/计组/OS/网络,55个节点)cs408_practice_records408刷题记录cs408_algorithm_records408算法训练记录mistake_records错题本(艾宾浩斯复习)种子数据示例——数学知识点在 _seed_knowledge_nodes() 中批量插入:math_nodes = [ ("higher_math", "函数与极限", "函数的概念", 2, "core", "unstarted", 3.0), ("higher_math", "函数与极限", "极限的概念与性质", 3, "core", "unstarted", 4.0), ("higher_math", "一元微分学", "微分中值定理", 5, "core", "unstarted", 6.0), # ... 共64个节点 ] conn.executemany( "INSERT INTO math_knowledge_nodes (subject, chapter, section, difficulty, importance, mastery, estimated_hours) VALUES (?, ?, ?, ?, ?, ?, ?)", math_nodes, ) 关键代码讲解:DeepSeek LLM 客户端backend/agent/llm_client.py 封装了与大模型的交互,支持普通对话和 JSON 结构化输出:def chat_completion(messages, max_tokens=900, temperature=0.2, response_format=None, timeout_seconds=45): if not DEEPSEEK_AUTH_TOKEN: raise LLMUnavailableError("DEEPSEEK_AUTH_TOKEN is not configured") payload = { "model": DEEPSEEK_MODEL, "messages": messages, "temperature": temperature, "max_tokens": max_tokens, } if response_format: payload["response_format"] = response_format response = requests.post( f"{DEEPSEEK_BASE_URL.rstrip('/')}/chat/completions", headers={ "Authorization": f"Bearer {DEEPSEEK_AUTH_TOKEN}", "Content-Type": "application/json", }, json=payload, timeout=timeout_seconds, ) response.raise_for_status() # ... 解析返回 降级策略:当 DEEPSEEK_AUTH_TOKEN 未配置时,所有 AI 功能会自动降级为本地规则引擎,不会报错阻断服务。关键代码讲解:智能排期引擎backend/services/planner_service.py 中的 generate_plans() 实现了6维权重4阶段的动态排期:def generate_plans(user_id=DEFAULT_USER_ID): tasks = fetch_tasks(user_id) if not tasks: # 无任务时自动创建8个默认考研任务 default_tasks = [ ("高数极限与连续刷题", "math", 90, "4", 3), ("英语阅读理解训练", "english", 60, "3", 2), ("政治马原选择题", "politics", 45, "3", 2), ("408数据结构算法练习", "cs408", 60, "4", 3), # ... 共8个 ] for title, task_type, est_min, priority, difficulty in default_tasks: db.execute("INSERT INTO tasks (...) VALUES (...)") # 删除未锁定的旧计划,保留用户锁定的计划 db.execute("DELETE FROM study_plans WHERE user_id = ? AND locked = 0 AND status IN (...)") # 调用排期算法生成新计划 generated = schedule_tasks(tasks, courses, existing_plans=locked_plans, days=7) 排期策略:schedule_tasks() 综合考虑任务优先级、难度、截止时间、课表冲突、用户偏好时段和当前备考阶段(基础/强化/冲刺/模考)6个维度,自动生成7天学习计划。3.3 前端:安装依赖并启动开发服务器步骤1:进入前端目录,安装 npm 依赖:cd web npm install 步骤2:启动 Vite 开发服务器:npm run dev启动成功后,浏览器访问 http://localhost:3000。关键代码讲解:全局手绘风格 CSSweb/src/assets/main.css 定义了整套手绘手账风格的设计令牌::root { --ink: #171717; /* 墨黑:边框、文字 */ --paper: #f7eed7; /* 网格纸背景 */ --cream: #fff7e8; /* 奶油白卡片 */ --yellow: #f8d46b; /* 黄色强调 */ --blue: #86b7d9; /* 蓝色强调 */ --red: #e1452d; /* 红色强调 */ --green: #4c9b54; /* 绿色强调 */ --border-thick: 4px solid var(--ink); /* 粗黑边 */ --shadow: 6px 6px 0 var(--ink); /* 偏移投影 */ --radius: 16px; /* 大圆角 */ --radius-pill: 999px; /* 药丸圆角 */ --color-math: #4A90D9; /* 数学蓝 */ --color-english: #7B68EE; /* 英语紫 */ --color-politics: #e1452d; /* 政治红 */ --color-cs408: #4c9b54; /* 408绿 */ } 网格纸背景通过 CSS 渐变实现:body { background: linear-gradient(90deg, rgba(23,23,23,0.03) 1px, transparent 1px), linear-gradient(0deg, rgba(23,23,23,0.025) 1px, transparent 1px), var(--paper); background-size: 24px 24px; } 关键代码讲解:AI 管家对话与确认卡片web/src/views/Chat.vue 实现了 AI 管家对话界面,支持确认卡片(confirm_card)交互:<div v-if="msg.confirmCard" class="confirm-card"> <div class="confirm-title">{{ msg.confirmCard.title }}</div> <div class="confirm-summary">{{ msg.confirmCard.summary }}</div> <ul v-if="msg.confirmCard.details?.length" class="confirm-details"> <li v-for="(d, di) in msg.confirmCard.details" :key="di">{{ d }}</li> </ul> <div class="confirm-actions"> <el-button v-for="(action, ai) in msg.confirmCard.actions" :key="ai" :type="ai === 0 ? 'primary' : 'default'" size="small" @click="handleConfirm(msg.confirmCard, action, i)" :disabled="msg.confirmed"> {{ action }} </el-button> </div> </div> 交互流程:用户发送消息 → 后端 Agent 分析意图 → 如果需要执行操作(如生成计划),返回 confirm_card → 用户点击确认 → 前端发送 confirmed_tool → 后端执行操作并返回结果。3.4 构建生产版本前端构建生产版本:cd web npm run build构建成功后,静态文件输出到 web/dist/ 目录,可直接部署到任何静态服务器。后端无需额外构建步骤,直接运行 python app.py 即可。3.5 运行效果展示登录注册页面用户首次访问时进入登录页面,支持注册和登录。页面采用居中布局,手绘风格卡片和药丸按钮。考研档案登录后进入考研档案页面,设置目标院校、专业、考研日期(日期选择器)、当前阶段、每日可用时间等。仪表盘仪表盘是用户首页,核心展示:考研倒计时:大号数字显示距考研天数,右侧显示当前阶段和考试日期四科进度卡片:数学/英语/政治/408 各一张卡片,显示掌握进度和正确率,点击进入对应科目今日计划:展示当天学习块列表,支持一键生成待复习错题:展示最近5条待复习错题AI 管家:显示今日概览和建议科目中心科目中心入口展示四门科目的卡片,每张卡片带有科目色带、图标和简介,hover 时有手绘风格的旋转偏移动画。数学一页面数学一页面包含三个 Tab:知识图谱:按高数/线代/概率论分组展示知识点,每个知识点可设置掌握度(未开始/学习中/已练习/已掌握)刷题记录:表单录入题源、题号、知识点、结果、耗时、错误类型统计:展示总刷题数、正确率、知识点掌握分布(进度条)、最近20条刷题记录(药丸标签)英语一页面英语一页面包含四个 Tab:词汇管理:展示今日待复习词汇,支持艾宾浩斯间隔复习阅读训练:录入年份、篇号、正确数、耗时写作工坊:录入作文类型、题目、内容统计:展示阅读篇数、写作篇数、最近阅读记录、最近写作记录政治页面政治页面包含三个 Tab:知识框架:按马原/毛中特/史纲/思修/时政分组,标注核心考点和考试频率选择题训练:录入题源、题号、单选/多选、结果、错误类型背诵管理:今日待背诵内容,支持艾宾浩斯间隔复习统计:展示选择题总数、正确率、最近选择题记录408计算机基础页面408页面包含四个 Tab:知识图谱:按数据结构/计组/OS/网络分组,标注算法类知识点刷题记录:录入子科目、题源、题号、题型、结果、耗时算法训练:录入算法名、语言、代码、时间/空间复杂度统计:展示刷题总数、正确率、算法题数、最近刷题记录、最近算法记录错题本错题本页面功能完整:筛选栏:按科目和掌握状态筛选统计药丸:总错题、已掌握、未掌握、今日待复习错题卡片列表:左侧色带标识科目,展示题目内容、题源、错误类型、复习次数操作按钮:复习(艾宾浩斯)、已掌握详情弹窗:展示题目内容、我的答案、正确答案、解析添加错题弹窗:手动录入完整错题信息自动创建:在数学/政治/408刷题时,答错的题目会自动创建错题记录。智能计划智能计划页面展示7天学习计划,支持:一键生成计划(6维权重4阶段排期算法)开始/完成/延后/锁定计划AI 辅助调整(延后时自动寻找合适空闲时段)AI 管家AI 管家是对话式交互界面:支持自然语言对话确认卡片交互(如「生成今日计划」会弹出确认卡片)4种管家风格(温柔鼓励型/朋友陪伴型/严肃型/极简提醒型)专注学习专注学习页面提供番茄钟计时器:选择科目后自动推荐专注时长(数学50分钟、英语40分钟、政治30分钟、408为45分钟)大号圆形计时器,手绘风格暂停/继续/提前结束AI 专注建议卡片结束时自动记录 Session 到后端复盘中心复盘中心展示学习统计数据,包括各科目专注时长、完成率等。设置设置页面支持修改:考研日期(日期选择器)当前备考阶段管家名称和风格四、技术要点总结4.1 前后端分离架构层级技术栈说明前端Vue3 + Vite + Element Plus + Pinia手绘风格 SPA,15个页面后端Flask + SQLite + DeepSeek11个蓝图,25张表,14个科目端点AIDeepSeek API + Agent4种管家风格,降级为规则引擎4.2 关键设计决策降级策略:所有 AI 功能在无 LLM 时自动降级为本地规则引擎,确保核心功能可用艾宾浩斯复习:错题本和词汇/背诵模块均采用 [1,2,4,7,15,30] 天间隔复习6维排期:综合优先级、难度、截止时间、课表、偏好时段、备考阶段6个维度手绘风格:通过 CSS 变量体系统一管理,粗黑边 + 偏移投影 + 药丸标签 + 网格纸背景自动错题:刷题答错时自动创建错题记录,无需手动添加默认任务:首次生成计划时自动创建8个考研默认任务4.3 已知注意事项Element Plus 的 el-input-number 组件需要 min-width: 140px 和排除全局 wrapper 样式覆盖axios 拦截器中 POST 请求无 body 时 user_id 不会被注入,所有无 body 的 POST/PUT 必须传 {}Python 代码中不能使用中文引号 ""(与 f-string 冲突),必须用 「」 替代esbuild 偶尔需要清理 node_modules 重装才能正常构建五、开发流程5.1 设计开发大纲5.2 基于初始文档构建网站5.3 逐步修改细节
  • [技术干货] lightningcss 编译打包问题,哪位勇士能解决。运行npm run build 的时候,会遇到。
    lightningcss 编译打包问题,哪位勇士能解决。运行npm run build 的时候,会遇到。
  • [使用说明] Matepad Edge下的Codearts IDE中,文件监视器数量过少
    如题,Matepad Edge下的Codearts IDE虽然可以正常使用,但是需要启动的时候,就会出现文件监视器数量过少的错误提示。Matebook Pro下则没有这个问题。具体错误(Node.js)如下:/storage/Users/currentUser/Develop/Website/Liantu-Game/node_modules/react-scripts/scripts/start.js:19  throw err;  ^Error: ENOSPC: System limit for number of file watchers reached, watch '/storage/Users/currentUser/Develop/Website/Liantu-Game/public'    at FSWatcher.<computed> (node:internal/fs/watchers:254:19)    at Object.watch (node:fs:2551:36)    at createFsWatchInstance (/storage/Users/currentUser/Develop/Website/Liantu-Game/node_modules/chokidar/lib/nodefs-handler.js:119:15)    at setFsWatchListener (/storage/Users/currentUser/Develop/Website/Liantu-Game/node_modules/chokidar/lib/nodefs-handler.js:166:15)    at NodeFsHandler._watchWithNodeFs (/storage/Users/currentUser/Develop/Website/Liantu-Game/node_modules/chokidar/lib/nodefs-handler.js:331:14)    at NodeFsHandler._handleDir (/storage/Users/currentUser/Develop/Website/Liantu-Game/node_modules/chokidar/lib/nodefs-handler.js:567:19)    at process.processTicksAndRejections (node:internal/process/task_queues:103:5)    at async NodeFsHandler._addToNodeFs (/storage/Users/currentUser/Develop/Website/Liantu-Game/node_modules/chokidar/lib/nodefs-handler.js:617:16)    at async /storage/Users/currentUser/Develop/Website/Liantu-Game/node_modules/chokidar/index.js:451:21    at async Promise.all (index 0)Emitted 'error' event on FSWatcher instance at:    at FSWatcher._handleError (/storage/Users/currentUser/Develop/Website/Liantu-Game/node_modules/chokidar/index.js:647:10)    at NodeFsHandler._addToNodeFs (/storage/Users/currentUser/Develop/Website/Liantu-Game/node_modules/chokidar/lib/nodefs-handler.js:645:18)    at process.processTicksAndRejections (node:internal/process/task_queues:103:5)    at async /storage/Users/currentUser/Develop/Website/Liantu-Game/node_modules/chokidar/index.js:451:21    at async Promise.all (index 0) {  errno: -28,  syscall: 'watch',  code: 'ENOSPC',  path: '/storage/Users/currentUser/Develop/Website/Liantu-Game/public',  filename: '/storage/Users/currentUser/Develop/Website/Liantu-Game/public'}Node.js v24.13.0虽然文件监视器数量可以被看到,但是因没有su或sudo权限,其相关文件也是只有读取权限,因此无法修改文件监视器的数量。还请改善一下这个问题! 
  • [交流吐槽] npm还是有权限问题
    npm run dev> app@0.0.0 dev> vitefile:///data/storage/el2/base/files/IDEProjects/demo/node_modules/rolldown/dist/shared/binding-eAM5CAoo.mjs:514                if (loadErrors.length > 0) throw new Error("Cannot find native binding. npm has a bug related to optional dependencies (https://github.com/npm/cli/issues/4828). Please try `npm i` again after removing both package-lock.json and node_modules directory.", { cause: loadErrors.reduce((err, cur) => {                                                 ^Error: Cannot find native binding. npm has a bug related to optional dependencies (https://github.com/npm/cli/issues/4828). Please try `npm i` again after removing both package-lock.json and node_modules directory.    at file:///data/storage/el2/base/files/IDEProjects/demo/node_modules/rolldown/dist/shared/binding-eAM5CAoo.mjs:514:36    at file:///data/storage/el2/base/files/IDEProjects/demo/node_modules/rolldown/dist/shared/binding-eAM5CAoo.mjs:10:48    at file:///data/storage/el2/base/files/IDEProjects/demo/node_modules/rolldown/dist/shared/parse-cfElc8c8.mjs:41:46    at ModuleJob.run (node:internal/modules/esm/module_job:413:25)    at async onImport.tracePromise.__proto__ (node:internal/modules/esm/loader:660:26)    at async CAC.<anonymous> (file:///data/storage/el2/base/files/IDEProjects/demo/node_modules/vite/dist/node/cli.js:570:27) {  [cause]: Error: Error loading shared library /data/storage/el2/base/files/IDEProjects/demo/node_modules/@rolldown/binding-openharmony-arm64/rolldown-binding.openharmony-arm64.node: Permission denied      at Module._extensions..node (node:internal/modules/cjs/loader:1920:18)      at Module.load (node:internal/modules/cjs/loader:1481:32)      at Module._load (node:internal/modules/cjs/loader:1300:12)      at TracingChannel.traceSync (node:diagnostics_channel:328:14)      ... 2 lines matching cause stack trace ...      at require (node:internal/modules/helpers:152:16)      at requireNative (file:///data/storage/el2/base/files/IDEProjects/demo/node_modules/rolldown/dist/shared/binding-eAM5CAoo.mjs:444:21)      at file:///data/storage/el2/base/files/IDEProjects/demo/node_modules/rolldown/dist/shared/binding-eAM5CAoo.mjs:482:18      at file:///data/storage/el2/base/files/IDEProjects/demo/node_modules/rolldown/dist/shared/binding-eAM5CAoo.mjs:10:48 {    code: 'ERR_DLOPEN_FAILED',    cause: Error: Cannot find module './rolldown-binding.openharmony-arm64.node'    Require stack:    - /data/storage/el2/base/files/IDEProjects/demo/node_modules/rolldown/dist/shared/binding-eAM5CAoo.mjs        at Module._resolveFilename (node:internal/modules/cjs/loader:1421:15)        at defaultResolveImpl (node:internal/modules/cjs/loader:1059:19)        at resolveForCJSWithHooks (node:internal/modules/cjs/loader:1064:22)        at Module._load (node:internal/modules/cjs/loader:1227:37)        at TracingChannel.traceSync (node:diagnostics_channel:328:14)        at wrapModuleLoad (node:internal/modules/cjs/loader:245:24)        at Module.require (node:internal/modules/cjs/loader:1504:12)        at require (node:internal/modules/helpers:152:16)        at requireNative (file:///data/storage/el2/base/files/IDEProjects/demo/node_modules/rolldown/dist/shared/binding-eAM5CAoo.mjs:439:12)        at file:///data/storage/el2/base/files/IDEProjects/demo/node_modules/rolldown/dist/shared/binding-eAM5CAoo.mjs:482:18 {      code: 'MODULE_NOT_FOUND',      requireStack: [        '/data/storage/el2/base/files/IDEProjects/demo/node_modules/rolldown/dist/shared/binding-eAM5CAoo.mjs'      ]    }  }}Node.js v24.13.0
  • [互动交流] 鸿蒙6.0 codeArts 支持nodejs版本控制工具
    缺少node版本安装控制工具,只有一个内置的node版本,不方便啊!!!
  • [问题求助] nvm-windows安装后设置华为源地址问题
    方式三:使用nvm-windows安装首先请参考此处(cid:link_0)安装nvm-windows,然后执行如下命令安装对应的版本:nvm node_mirror https://mirrors.huaweicloud.com/nodejs/nvm npm_mirror https://mirrors.huaweicloud.com/npm-software/nvm install latestnvm use latest按照上面的方法设置后在CMD中执行 nvm install latest报错:Error retrieving "https://mirrors.huaweicloud.com/nodejs/latest/SHASUMS256.txt": HTTP Status 418各位大佬们,这个需要怎么解决呢?
  • [技术干货] Vscode中launch.json与tasks.json文件的详细介绍【转】
    launch.json文件launch.json 文件是 Visual Studio Code (VS Code) 中用于配置调试会话的文件。它定义了调试器如何启动和运行程序。以下是 launch.json 文件的详细配置说明,包括常见的属性及其用途。基本结构launch.json 文件通常位于 .vscode 目录下,具有以下基本结构:主要属性每个配置块代表一个调试配置,包含多个属性。以下是一些常见属性的说明:type: 调试器类型,如 python, cppdbg, node, java, 等。request: 调试请求类型,通常为 launch(启动)或 attach(附加)。name: 配置名称,用户可以在调试配置列表中看到。program: 要调试的程序路径或文件。args: 传递给程序的命令行参数,数组形式。cwd: 当前工作目录。env: 环境变量设置。sourceMaps: 是否启用源映射(通常用于 JavaScript 调试)。preLaunchTask: 调试前要执行的任务(通常用于编译等)。postDebugTask: 调试结束后要执行的任务。stopOnEntry: 是否在程序入口处停止。console: 控制台类型,如 integratedTerminal, externalTerminal, 或 internalConsole。justMyCode: 是否只调试用户代码(用于 Python)。pythonPath: Python 可执行文件的路径(用于 Python)。常见配置项1. Pythonprogram: 要调试的 Python 文件。pythonPath: Python 解释器路径。args: 传递给 Python 脚本的命令行参数。env: 环境变量。console: 控制台类型。2. C++program: 可执行文件路径。args: 命令行参数。stopAtEntry: 是否在程序入口处暂停。cwd: 当前工作目录。environment: 环境变量。externalConsole: 是否使用外部控制台。MIMode: 调试器模式(如 gdb, lldb)。miDebuggerPath: 调试器路径。3. Node.jsprogram: 要调试的 Node.js 文件。args: 命令行参数。runtimeExecutable: Node.js 可执行文件路径。runtimeArgs: 传递给 Node.js 的参数。env: 环境变量。sourceMaps: 是否启用源映射。outFiles: 编译输出文件路径。tasks.jsontasks.json 文件是 Visual Studio Code (VS Code) 中用于配置任务(Tasks)的文件。这些任务可以是编译代码、运行测试、构建项目等自动化任务。以下是 tasks.json 文件的详细配置说明,包括常见的属性及其用途。主要属性每个任务配置块代表一个任务,包含多个属性。以下是一些常见属性的说明:label: 任务的名称或标签,用于在任务列表中标识任务。type: 任务类型,例如 shell 或 process。shell 表示任务将在 shell 中运行,process 表示任务将作为独立的进程运行。command: 要执行的命令,可以是编译器、构建工具、脚本等。args: 传递给命令的参数,数组形式。group: 任务分组,可以设置为 build 或 test,用于标识构建任务或测试任务。presentation: 控制任务输出的呈现方式,例如是否显示在终端中,是否清除之前的输出等。problemMatcher: 配置错误和警告的匹配器,用于从任务输出中解析错误和警告。options: 任务执行的选项,例如环境变量、当前工作目录等。常见配置项label任务的标签名称,用于在 VS Code 任务列表中标识任务。1"label": "build"type任务类型,可以是 shell 或 process。shell 表示任务将在 shell 中运行,process 表示任务将作为独立的进程运行。1"type": "shell"command要执行的命令,例如编译器、脚本或构建工具。1"command": "g++"args传递给命令的参数,数组形式。123456"args": [    "-g",    "${file}",    "-o",    "${fileDirname}/${fileBasenameNoExtension}.out"]group任务分组,用于标识任务的类别,可以是 build 或 test。1234"group": {    "kind": "build",    "isDefault": true}presentation控制任务输出的呈现方式。123456"presentation": {    "echo": true,    "reveal": "always",    "focus": false,    "panel": "shared"}problemMatcher用于解析任务输出中的错误和警告。VS Code 内置了多种匹配器,例如 $gcc, $eslint 等。1"problemMatcher": ["$gcc"]options任务执行的选项,例如环境变量、当前工作目录等。
  • [技术干货] 本地JS文件批量压缩
    最近在维护一个小后台项目,有段JS需要压缩上传到CDN存储服务器。由于之前压缩的JS文件都比较少,都是手动压缩的。这次需要压缩的文件比较多,所以用了批量压缩。特此记录一下,方便大家和自己以后再用到的时候备忘。回到顶部v准备工作安装nodejs首先在本地安装node.js和npm,一般npm集成于nodejs,即安装nodejs,同时也安装了npm。node.js下载地址,下载以后直接不停下一步就行,全部使用默认选项即可。下载完成后打开CMD,node -v检测是否安装成功,安装成功则会显示nodejs版本号。安装uglify插件在cmd命令行执行:npm install uglify-js -g回到顶部v开始压缩压缩的时候将下面的代码拷贝下来,然后生成bat文件,再运行bat文件(有些电脑可能需要windows管理员身份运行),然后依次输入当前的JS文件目录。再输入生成输出压缩后JS的目录即可。@ECHO OFF setlocal enabledelayedexpansion set source_path=%1 set target_dir=%2 IF [%1]==[] ( rem echo please input javascript file or directory set /p source_path=please input javascript file or directory: ) IF [%2]==[] ( rem echo please input output directory set /p target_dir=please input output directory: ) rem source path exists? FOR %%i IN (%source_path%) DO SET FileAttrib=%%~ai if "%FileAttrib%" equ "" ( rem not found file attribute, source path not exist echo source path ^(%source_path%^) doesn't exist exit /b 0 ) ELSE IF "%FileAttrib:~0,1%" equ "d" ( rem source path is directory and not end with \, append \ to source path IF %source_path:~-1% neq \ ( set source_path=%source_path%\ ) ) echo source path is %source_path% rem target path exists? FOR %%i IN (%target_dir%) DO SET fa=%%~ai IF [%fa%]==[] ( rem target path not exist, make it mkdir %target_dir% ) IF %target_dir:~-1% neq \ ( rem append \ to target path set target_dir=%target_dir%\ ) echo target path is %target_dir% IF [%FileAttrib:~0,1%]==[d] ( for /r %source_path% %%I in (*.js) do ( set file_name=%%~nI set parent=%%~dpI set target_parent=%target_dir%!parent:%source_path%=! if not exist !target_parent! mkdir !target_parent! cd !target_parent! if [!file_name:~-4!] neq [.min] ( set w= uglifyjs %%I -m -c -O ascii_only=true -o !target_parent!%%~nI.min.js rem uglify .js file echo uglifyjs from "%%I" to "!target_parent!%%~nI.min.js" start cmd /c "!w!" ) else ( rem copy min.js file echo copy file from "%%~dpnI.js" to "!target_parent!%%~nI.js" start cmd /c "copy %%~dpnI.js !target_parent!%%~nI.js" ) ) ) else ( for %%I in (%source_path%) do ( IF "%%~xI" EQU ".js" ( set file_name=%%~nI if [!file_name:~-4!] neq [.min] ( rem uglify .js file set val=%target_dir%%%~nI.min.js echo uglifyjs from "%%I" to "!val!" start cmd /c "uglifyjs %%I -m -c -O ascii_only=true -o !val!" ) else ( rem copy min.js file echo copy file from "%%I" to "%target_dir%%%~nI.js" start cmd /c "copy %%I %target_dir%%%~nI.js" ) ) ) ) echo done
  • 你敢信?比 setTimeout 还快 80 倍的定时器【转】
    起因很多人都知道,setTimeout是有最小延迟时间的,根据MDN 文档 setTimeout:实际延时比设定值更久的原因:最小延迟时间中所说:在浏览器中,setTimeout()/setInterval() 的每调用一次定时器的最小间隔是 4ms,这通常是由于函数嵌套导致(嵌套层级达到一定深度)。在HTML Standard规范中也有提到更具体的:Timers can be nested; after five such nested timers, however, the interval is forced to be at least four milliseconds.简单来说,5 层以上的定时器嵌套会导致至少 4ms 的延迟。用如下代码做个测试:12345678910111213141516171819202122232425let a = performance.now();setTimeout(() => {  let b = performance.now();  console.log(b - a);  setTimeout(() => {    let c = performance.now();    console.log(c - b);    setTimeout(() => {      let d = performance.now();      console.log(d - c);      setTimeout(() => {        let e = performance.now();        console.log(e - d);        setTimeout(() => {          let f = performance.now();          console.log(f - e);          setTimeout(() => {            let g = performance.now();            console.log(g - f);          }, 0);        }, 0);      }, 0);    }, 0);  }, 0);}, 0);在浏览器中的打印结果大概是这样的,和规范一致,第五次执行的时候延迟来到了 4ms 以上。探索假设就需要一个「立刻执行」的定时器呢?有什么办法绕过这个 4ms 的延迟吗,在 MDN 文档的角落里有一些线索:如果想在浏览器中实现 0ms 延时的定时器,可以参考这里所说的window.postMessage()。这篇文章里的作者给出了这样一段代码,用postMessage来实现真正 0 延迟的定时器:12345678910111213141516171819202122232425(function () {  var timeouts = [];  var messageName = 'zero-timeout-message';  // 保持 setTimeout 的形态,只接受单个函数的参数,延迟始终为 0。  function setZeroTimeout(fn) {    timeouts.push(fn);    window.postMessage(messageName, '*');  }  function handleMessage(event) {    if (event.source == window && event.data == messageName) {      event.stopPropagation();      if (timeouts.length > 0) {        var fn = timeouts.shift();        fn();      }    }  }  window.addEventListener('message', handleMessage, true);  // 把 API 添加到 window 对象上  window.setZeroTimeout = setZeroTimeout;})();由于postMessage的回调函数的执行时机和setTimeout类似,都属于宏任务,所以可以简单利用postMessage和addEventListener('message')的消息通知组合,来实现模拟定时器的功能。这样,执行时机类似,但是延迟更小的定时器就完成了。再利用上面的嵌套定时器的例子来跑一下测试:全部在 0.1 ~ 0.3 毫秒级别,而且不会随着嵌套层数的增多而增加延迟。测试从理论上来说,由于postMessage的实现没有被浏览器引擎限制速度,一定是比 setTimeout 要快的。设计一个实验方法,就是分别用postMessage版定时器和传统定时器做一个递归执行计数函数的操作,看看同样计数到 100 分别需要花多少时间。实验代码:1234567891011121314151617181920212223242526272829303132333435363738394041424344function runtest() {  var output = document.getElementById('output');  var outputText = document.createTextNode('');  output.appendChild(outputText);  function printOutput(line) {    outputText.data += line + '\n';  }  var i = 0;  var startTime = Date.now();  // 通过递归 setZeroTimeout 达到 100 计数  // 达到 100 后切换成 setTimeout 来实验  function test1() {    if (++i == 100) {      var endTime = Date.now();      printOutput(        '100 iterations of setZeroTimeout took ' +        (endTime - startTime) +        ' milliseconds.'      );      i = 0;      startTime = Date.now();      setTimeout(test2, 0);    } else {      setZeroTimeout(test1);    }  }  setZeroTimeout(test1);  // 通过递归 setTimeout 达到 100 计数  function test2() {    if (++i == 100) {      var endTime = Date.now();      printOutput(        '100 iterations of setTimeout(0) took ' +        (endTime - startTime) +        ' milliseconds.'      );    } else {      setTimeout(test2, 0);    }  }}实验代码很简单,先通过setZeroTimeout也就是postMessage版本来递归计数到 100,然后切换成 setTimeout计数到 100。直接放结论,这个差距不固定,在 mac 上用无痕模式排除插件等因素的干扰后,以计数到 100 为例,大概有 80 ~ 100 倍的时间差距。在硬件更好的台式机上,甚至能到 200 倍以上。Performance 面板只是看冷冰冰的数字还不够过瘾,打开 Performance 面板,看看更直观的可视化界面中,postMessage版的定时器和setTimeout版的定时器是如何分布的。这张分布图非常直观的体现出了上面所说的所有现象,左边的postMessage版本的定时器分布非常密集,大概在 5ms 以内就执行完了所有的计数任务。而右边的setTimeout版本相比较下分布的就很稀疏了,而且通过上方的时间轴可以看出,前四次的执行间隔大概在 1ms 左右,到了第五次就拉开到 4ms 以上。作用也许有同学会问,有什么场景需要无延迟的定时器?其实在 React 的源码中,做时间切片的部分就用到了。12345678910111213141516171819const channel = new MessageChannel();const port = channel.port2;// 每次 port.postMessage() 调用就会添加一个宏任务// 该宏任务为调用 scheduler.scheduleTask 方法channel.port1.onmessage = scheduler.scheduleTask;const scheduler = {  scheduleTask() {    // 挑选一个任务并执行    const task = pickTask();    const continuousTask = task();    // 如果当前任务未完成,则在下个宏任务继续执行    if (continuousTask) {      port.postMessage(null);    }  },};React 把任务切分成很多片段,这样就可以通过把任务交给postMessage的回调函数,来让浏览器主线程拿回控制权,进行一些更优先的渲染任务(比如用户输入)。为什么不用执行时机更靠前的微任务呢?关键的原因在于微任务会在渲染之前执行,这样就算浏览器有紧急的渲染任务,也得等微任务执行完才能渲染。总结可以了解如下几个知识点:setTimeout的 4ms 延迟历史原因,具体表现。如何通过postMessage实现一个真正 0 延迟的定时器。postMessage定时器在 React 时间切片中的运用。为什么时间切片需要用宏任务,而不是微任务。本文转载于:https://juejin.cn/post/7229520942668824633
  • [技术干货] Node.js 未来会在前端领域一家独大吗?
    Node.js 团队近期新发布20.6.0版本,在今年4月的版本上增强了安全性和API引入,同时也增加对新操作系统的支持以及对 TypeScript 兼容性的优化,虽然现在该版本处于测试阶段,你觉得Node.js在未来会呈现怎样的发展态势呢?Node.js新版本发布,你觉得哪一性能提升最实用?你觉得Node.js与JAVA相比,其优势在哪里?未来会超越JAVA吗?
  • [技术干货] 前端使用node.js连接sql.server数据库教程- 前端
     最近项目中要用到node写接口然后连接公司现有的sql.server数据库,再把执行结果返回给前端(还是我),因为之前一直做前端这块,后端这方面不是很懂,花了很长的时间终于研究出来了(还是太菜了,走了很多弯路),所以写个博客,一是复习巩固,二是给其他有需要的小伙伴一个参考,尽量少走弯路,废话不多说,直接上代码  1.首先在自己的数据库新建一张表,用于接下来的测试    2.在自己的项目文件夹中建两个文件,config.js & mssql.js  3.封装数据库信息,config.js  //config.js let app = {     user: 'xxxxxx', //这里写你的数据库的用户名     password: 'xxxxxx',//这里写数据库的密码     server: 'localhost',      database: 'testDB', // 数据库名字     port: 1433, //端口号,默认1433     options: {         encrypt: false,  //加密,设置为true时会连接失败 Failed to connect to localhost:1433 - self signed certificate         enableArithAbort: false     },     pool: {         min: 0,         max: 10,         idleTimeoutMillis: 3000     } }   module.exports = app  4.对sql语句的二次封装 , mssql.js  //mssql.js /**  *sqlserver Model  **/ const mssql = require("mssql"); const conf = require("./config.js");   const pool = new mssql.ConnectionPool(conf) const poolConnect = pool.connect()   pool.on('error', err => {     console.log('error: ', err) }) /**  * 自由查询  * @param sql sql语句,例如: 'select * from news where id = @id'  * @param params 参数,用来解释sql中的@*,例如: { id: id }  * @param callBack 回调函数  */ let querySql = async function (sql, params, callBack) {     try {         let ps = new mssql.PreparedStatement(await poolConnect);         if (params != "") {             for (let index in params) {                 if (typeof params[index] == "number") {                     ps.input(index, mssql.Int);                 } else if (typeof params[index] == "string") {                     ps.input(index, mssql.NVarChar);                 }             }         }         ps.prepare(sql, function (err) {             if (err)                 console.log(err);             ps.execute(params, function (err, recordset) {                 callBack(err, recordset);                 ps.unprepare(function (err) {                     if (err)                         console.log(err);                 });             });         });     } catch (e) {         console.log(e)     } };   /**  * 按条件和需求查询指定表  * @param tableName 数据库表名,例:'news'  * @param topNumber 只查询前几个数据,可为空,为空表示查询所有  * @param whereSql 条件语句,例:'where id = @id'  * @param params 参数,用来解释sql中的@*,例如: { id: id }  * @param orderSql 排序语句,例:'order by created_date'  * @param callBack 回调函数  */ let select = async function (tableName, topNumber, whereSql, params, orderSql, callBack) {     try {         let ps = new mssql.PreparedStatement(await poolConnect);         let sql = "select * from " + tableName + " ";         if (topNumber != "") {             sql = "select top(" + topNumber + ") * from " + tableName + " ";         }         sql += whereSql + " ";         if (params != "") {             for (let index in params) {                 if (typeof params[index] == "number") {                     ps.input(index, mssql.Int);                 } else if (typeof params[index] == "string") {                     ps.input(index, mssql.NVarChar);                 }             }         }         sql += orderSql;         console.log(sql);         ps.prepare(sql, function (err) {             if (err)                 console.log(err);             ps.execute(params, function (err, recordset) {                 callBack(err, recordset);                 ps.unprepare(function (err) {                     if (err)                         console.log(err);                 });             });         });     } catch (e) {         console.log(e)     } };   /**  * 查询指定表的所有数据  * @param tableName 数据库表名  * @param callBack 回调函数  */ let selectAll = async function (tableName, callBack) {     try {         let ps = new mssql.PreparedStatement(await poolConnect);         let sql = "select * from " + tableName + " ";         ps.prepare(sql, function (err) {             if (err)                 console.log(err);             ps.execute("", function (err, recordset) {                 callBack(err, recordset);                 ps.unprepare(function (err) {                     if (err)                         console.log(err);                 });             });         });     } catch (e) {         console.log(e)     } };   /**  * 添加字段到指定表  * @param addObj 需要添加的对象字段,例:{ name: 'name', age: 20 }  * @param tableName 数据库表名  * @param callBack 回调函数  */ let add = async function (addObj, tableName, callBack) {     try {         let ps = new mssql.PreparedStatement(await poolConnect);         let sql = "insert into " + tableName + "(";         if (addObj != "") {             for (let index in addObj) {                 if (typeof addObj[index] == "number") {                     ps.input(index, mssql.Int);                 } else if (typeof addObj[index] == "string") {                     ps.input(index, mssql.NVarChar);                 }                 sql += index + ",";             }             sql = sql.substring(0, sql.length - 1) + ") values(";             for (let index in addObj) {                 if (typeof addObj[index] == "number") {                     sql += addObj[index] + ",";                 } else if (typeof addObj[index] == "string") {                     sql += "'" + addObj[index] + "'" + ",";                 }             }         }         sql = sql.substring(0, sql.length - 1) + ") SELECT @@IDENTITY id"; // 加上SELECT @@IDENTITY id才会返回id         ps.prepare(sql, function (err) {             if (err) console.log(err);             ps.execute(addObj, function (err, recordset) {                 callBack(err, recordset);                 ps.unprepare(function (err) {                     if (err)                         console.log(err);                 });             });         });     } catch (e) {         console.log(e)     } };   /**  * 更新指定表的数据  * @param updateObj 需要更新的对象字段,例:{ name: 'name', age: 20 }  * @param whereObj 需要更新的条件,例: { id: id }  * @param tableName 数据库表名  * @param callBack 回调函数  */ let update = async function (updateObj, whereObj, tableName, callBack) {     try {         let ps = new mssql.PreparedStatement(await poolConnect);         let sql = "update " + tableName + " set ";         if (updateObj != "") {             for (let index in updateObj) {                 if (typeof updateObj[index] == "number") {                     ps.input(index, mssql.Int);                     sql += index + "=" + updateObj[index] + ",";                 } else if (typeof updateObj[index] == "string") {                     ps.input(index, mssql.NVarChar);                     sql += index + "=" + "'" + updateObj[index] + "'" + ",";                 }             }         }         sql = sql.substring(0, sql.length - 1) + " where ";         if (whereObj != "") {             for (let index in whereObj) {                 if (typeof whereObj[index] == "number") {                     ps.input(index, mssql.Int);                     sql += index + "=" + whereObj[index] + " and ";                 } else if (typeof whereObj[index] == "string") {                     ps.input(index, mssql.NVarChar);                     sql += index + "=" + "'" + whereObj[index] + "'" + " and ";                 }             }         }         sql = sql.substring(0, sql.length - 5);         ps.prepare(sql, function (err) {             if (err)                 console.log(err);             ps.execute(updateObj, function (err, recordset) {                 callBack(err, recordset);                 ps.unprepare(function (err) {                     if (err)                         console.log(err);                 });             });         });     } catch (e) {         console.log(e)     } };   /**  * 删除指定表字段  * @param whereSql 要删除字段的条件语句,例:'where id = @id'  * @param params 参数,用来解释sql中的@*,例如: { id: id }  * @param tableName 数据库表名  * @param callBack 回调函数  */ let del = async function (whereSql, params, tableName, callBack) {     try {         let ps = new mssql.PreparedStatement(await poolConnect);         let sql = "delete from " + tableName + " ";         if (params != "") {             for (let index in params) {                 if (typeof params[index] == "number") {                     ps.input(index, mssql.Int);                 } else if (typeof params[index] == "string") {                     ps.input(index, mssql.NVarChar);                 }             }         }         sql += whereSql;         ps.prepare(sql, function (err) {             if (err)                 console.log(err);             ps.execute(params, function (err, recordset) {                 callBack(err, recordset);                 ps.unprepare(function (err) {                     if (err)                         console.log(err);                 });             });         });     } catch (e) {         console.log(e)     } };   exports.config = conf; exports.del = del; exports.select = select; exports.update = update; exports.querySql = querySql; exports.selectAll = selectAll; exports.add = add;  5.接口代码,api/user.js  //user.js const express = require('express'); const db = require('../utils/mssql.js'); const moment = require('moment'); const router = express.Router();   /* GET home page. */ router.get('/info', function (req, res, next) {     db.selectAll('userInfo', function (err, result) {//查询所有userInfo表的数据         res.send(result.recordset)         // res.render('userInfo', { results: result.recordset, moment: moment });     }); }); router.post('/delete', function (req, res, next) {//删除一条id对应的userInfo表的数据     console.log(req.body, 77);     const { UserId } = req.body     const id = UserId     db.del("where id = @id", { id: id }, "userInfo", function (err, result) {         console.log(result, 66);         res.send('ok')     }); }); router.post('/update/:id', function (req, res, next) {//更新一条对应id的userInfo表的数据     var id = req.params.id;     var content = req.body.content;     db.update({ content: content }, { id: id }, "userInfo", function (err, result) {         res.redirect('back');     }); });   module.exports = router;  6.启动文件,server.js  //1.导入模块 const express = require('express')   //2.创建服务器 let server = express() server.use(express.urlencoded()) //中间件要写在启动文件里面   const cors = require('cors') server.use(cors())   const user = require('./api/user.js')   server.use('/', user)   //3.开启服务器 server.listen(8002, () => {     console.log('服务器已启动,在端口号8002') })  7.启动服务器   8.用postman测试接口        数据成功返回~~~ ———————————————— 版权声明:本文为CSDN博主「快乐的小鱼儿12」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。 原文链接:https://blog.csdn.net/m0_67296095/article/details/126326217 
  • [技术干货] Node.js使用多进程提高任务执行效率【转载】
    在Node.JS中使用多进程非常简单,合理使用多进程,可以解放硬件的能力,让软件的运行效率得到肉眼可见的提升。本文详细讲解了Node.js使用多进程提高任务执行效率的方法,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下最近我有个学员写了个 Node.js 脚本程序,定时从某个服务器下载文件,并向另一个云服务商上传文件。但是每次只能先下载一个文件,再去上传一个文件。效率比较低。于是他向我请教,怎么样才能提高效率?我告诉他应该用 Node 的多进程技术。什么是 Node 多进程?Node 是在单个线程中运行,我们虽然没办法开启额外的线程,但是可以开启进程集群。这样可以让下载任务和上传任务同时进行。使用多进程进行初步代码优化简单看了一下学员的代码,大概是这样:123456789101112const dl = require('./download.js')const ul = require('./upload.js')const source = require('./source.js')async function runTask() {  const { originUrl, targetUrl } = source.getNext()  const { data } = await dl(originUrl)  await ul(targetUrl, data)  runTask()}runTask()这个代码逻辑上是没问题的,但是它只能在 1 个 CPU 核心中运行。我们完全可以使用 Node.js 的多进程来利用 CPU 的多核心来增加这个程序的吞吐量。怎么改造呢?也非常简单。1234567891011121314151617181920212223242526const os = require('os')const cluster = require('cluster')const dl = require('./download.js')const ul = require('./upload.js')const source = require('./source.js')function run() {  if(cluster.isMaster) {    const numCPUs = os.cpus().length;    for(let idx = 0; idx < numCPUs; idx++) {      cluster.fork();    }  } else {    runTask()  }}async function runTask() {    const { originUrl, targetUrl } = source.getNext()    const { data } = await dl(originUrl)    await ul(targetUrl, data)    runTask()  }}run()在上面的代码中,我添加了 os 和 cluster 模块。os 模块可以告诉我们运行环境的 CPU 信息,我们可以通过它来做为创建进程数量的限制条件。然后通过 cluster.isMaster 来判断是否是主进程,因为只有主进程才拥有 fork 的能力。worker和master通信其实上面的代码还可以继续做更深层次的优化,仔细分析一下,下载速度和上传速度其实是不一致的。通常来说,下载速度会很慢,但上传速度会很快。我们可以让其他进程去下载文件,当下载成功之后,让主进程去上传文件。Node 中的多进程之间不会共享内存,所以我们可以通过消息传递的方式,让下载进程通知主进程去上传文件。12345678910111213141516171819202122232425262728const os = require('os')const cluster = require('cluster')const dl = require('./download.js')const ul = require('./upload.js')const source = require('./source.js')function run() {  if(cluster.isMaster) {    const numCPUs = os.cpus().length;    for(let idx = 0; idx < numCPUs; idx++) {      const worker = cluster.fork();      worker.on('message', ({ targetUrl, data }) => {        ul(targetUrl, data)      })    }  } else {    runTask()  }}async function runTask() {  const { originUrl, targetUrl } = source.getNext()  const { data } = await dl(originUrl)  process.send({ targetUrl, data })  runTask()}run()可以在主进程中通过 worker.on('message', (msg)=>{}) 的方式来监听子进程发送的消息。在子进程中通过 process.send 来向主进程发送消息。总结在 NodeJS 中使用多进程非常简单,合理使用多进程,可以解放硬件的能力,让软件的运行效率得到肉眼可见的提升。转载自https://www.jb51.net/article/263647.htm
  • [其他语言] 求助,鲲鹏服务器编译问题
    Node.js前端工程在arm机器安装依赖时出现很多前端组件找不到arm版的问题,很多依赖不兼容arm,请问是否有arm版本的前端组件库?
  • [技术干货] npm install安装报错:gyp info it worked if it ends with ok的解决方法【转载】
    今天新启动一个项目,在 npm install 安装依赖项时出现报错,所以下面这篇文章主要给大家介绍了关于npm install安装报错:gyp info it worked if it ends with ok的解决方法,需要的朋友可以参考下目录• 1.可能原因1:node和node-sass版本冲突, • 2.node.js卸载与安装 • 3.node-sass卸载安装, • 4.安装好后对应版本的node和node-sass后启动 • 总结 前端vue新项目 npm install安装报错 gyp info it worked if it ends with ok1.可能原因1:node和node-sass版本冲突,对应的node和node-sass的版本如下2.node.js卸载与安装之前有安装过弄过node.js的如果node.js版本过高需要重新安装低版本node.js,在控制面板卸载node.js,再在node官网 下载对应的msi包安装到之前的安装过node.js的文件夹下。低版本的node.js 只需直接下载高版本的msi包安装无需卸载之前的低版本node.js,这样不需要再去配置环境变量啥的,3.node-sass卸载安装,node-sass卸载:1npm uninstall node-sassnode-sass安装可以指定版本:npm install node-sass 布置的版本直接安装npm install node-sass@4.14.0 指定4.14版本的,如果安装报错试试换下淘宝镜像,一个好的解决方案在是直接在项目根目录下创建一个 .npmrc文件指定淘宝镜像文件内容:sass_binary_site=https://npm.taobao.org/mirrors/node-sass/ phantomjs_cdnurl=https://npm.taobao.org/mirrors/phantomjs/ electron_mirror=https://npm.taobao.org/mirrors/electron/ registry=https://registry.npm.taobao.org4.安装好后对应版本的node和node-sass后启动再npm install , npm run dev 没有报错啦总结到此这篇关于npm install安装报错:gyp info it worked if it ends with ok解决的文章就介绍到这了转载自https://www.jb51.net/article/255441.htm
  • [技术干货] Node.js 商标转让给 OpenJS 基金会[转载]
    原文链接:https://blog.csdn.net/mengyidan/article/details/122947823在昨日的情人节当天,OpenJS基金会为保证JavaScript开源社区的持续增长,正式宣布收购Node.js徽标和商标的所有权。即日起,为Node.js提供长期赞助与管理的Joyent公司将把Node.js商标的所有权与管理权转让给OpenJS基金会,后者将负责Node.js的商标管理与维护工作。图源:OpenJS基金会官网Node.js是基于Chrome 的V8 JavaScript 引擎构建的JavaScript 运行时,由美国软件工程师瑞安·达尔开发。起初,Node.js的开发和维护工作是由达尔主持,其当时所在的公司Joyen提供赞助,随后达尔离开,Node.js社区也经历了一些分支与合并事件。目前Node.js有OpenJS基金会持有和维护,当下的最新版本为Node.js 17.5.0,其中LTS版本为Node.js 16.14.0。Node.js官网:https://nodejs.org/en/其实在6年前,OpenJS基金会已经获得了Node.js商标的免费永久许可使用权,包括那个六边形图形,商标转让并不会对贡献者产生任何影响。就开源产品而言,商标不仅可以保护开源项目,还能识别代码的具体来源。据OpenJS官博介绍,他们的目标是确保OpenJS商标政策在法律上尽可能的灵活和容易理解,同时保证Node.js以及OpenJS其它产品的服务和质量。Joyent总裁兼首席运营官Sung Whan Moon也表示,Joyent一直相信开源能给开发者和企业创造更多机会,很高兴能看到Node.js为这么多开发者、企业的经济增长带来支撑。随着Node.js进入第2个十年,把它放在一个中立的、并且在需要时能够制定商标限制,充分保证了项目的完整性,OpenJS基金会会是Node.js商标所有权的最佳容身之处。Node.js作为一个健康社区,得到了许多公司的广泛支持,这些公司扩大了这个项目的规模和商业应用,包括彭博社、美国国家航空航天局、Netflix等等。Node.js刚刚交付了Node.js 17,并将Node.js 16移至长期支持(LTS)。Joyent商业集团负责人和OpenJS董事会白金董事Sean Johnson提到,Node.js的采用前景将比以往任何时候都要光明。IBM开放技术和开发者倡导副总裁、OpenJS基金会董事会主席Todd Moore表示,"就推动JavaScript技术的广泛采用以及关键Node.js解决方案和相关技术的持续发展的使命,OpenJS基金会已经做好了准备。"Node.js的维护者们也正在为Node.js未来十年的战略方向进行合作。关于OpenJS基金会OpenJS基金会致力于支持JavaScript生态系统和网络技术的健康发展,提供一个中立的组织来主持和维持项目,并为整个社区的利益合作资助活动。OpenJS基金会由38个开源JavaScript项目组成,包括Appium、Dojo、jQuery、Node.js和webpack,并得到30个企业和最终用户成员的支持,包括GoDaddy、Google、IBM、Intel、Joyent和Microsoft。这些成员认识到JavaScript生态系统的相互关联性,以及为代表重大共享价值的项目提供一个中央家园的重要性。关于Linux基金会Linux基金会成立于2000年,由1000多个成员支持,是世界上领先的开源软件、开放标准和开放硬件的合作之家。Linux基金会的项目,如Linux、Kubernetes、Node.js等,被认为对世界上最重要的基础设施的发展至关重要。其开发方法利用既定的最佳实践,并解决贡献者、用户和解决方案提供者的需求,以创造可持续的开放合作模式。
总条数:38 到第
上滑加载中