Webpack: 并行构建
概述
受限于 Node.js 的单线程架构,原生 Webpack 对所有资源文件做的所有解析、转译、合并操作本质上都是在同一个线程内串行执行,CPU 利用率极低,因此,理所当然地,社区出现了一些以多进程方式运行 Webpack,或 Webpack 构建过程某部分工作的方案(从而提升单位时间利用率),例如:
- HappyPack:多进程方式运行资源加载(Loader)逻辑;
- Thread-loader:Webpack 官方出品,同样以多进程方式运行资源加载逻辑;
- Parallel-Webpack:多进程方式运行多个 Webpack 构建实例;
- TerserWebpackPlugin:支持多进程方式执行代码压缩、uglify 功能。
这些方案的核心设计都很类似:针对某种计算任务创建子进程,之后将运行所需参数通过 IPC 传递到子进程并启动计算操作,计算完毕后子进程再将结果通过 IPC 传递回主进程,寄宿在主进程的组件实例,再将结果提交给 Webpack。
使用 HappyPack
HappyPack 能够将耗时的文件加载(Loader)操作拆散到多个子进程中并发执行,子进程执行完毕后再将结果合并回传到 Webpack 进程,从而提升构建性能。不过,HappyPack 的用法稍微有点难以理解,需要同时:
- 使用
happypack/loader代替原本的 Loader 序列; - 使用
HappyPack插件注入代理执行 Loader 序列的逻辑。
基本用法:
-
安装依赖:
yarn add -D happypack -
将原有
loader配置替换为happypack/loader,如:module.exports = {// ...module: {rules: [{test: /\.js$/,use: "happypack/loader",// 原始配置如:// use: [// {// loader: 'babel-loader',// options: {// presets: ['@babel/preset-env']// }// },// 'eslint-loader'// ]},],}, }; -
创建
happypack插件实例,并将原有 loader 配置迁移到插件中,完整配置:const HappyPack = require("happypack");module.exports = {// ...module: {rules: [{test: /\.js$/,use: "happypack/loader",// 原始配置如:// use: [// {// loader: 'babel-loader',// options: {// presets: ['@babel/preset-env']// }// },// 'eslint-loader'// ]},],},plugins: [new HappyPack({// 将原本定义在 `module.rules.use` 中的 Loader 配置迁移到 HappyPack 实例中loaders: [{loader: "babel-loader",option: {presets: ["@babel/preset-env"],},},"eslint-loader",],}),], };
配置完毕后,再次启动 npx webpack 命令,即可使用 HappyPack 的多进程能力提升构建性能。以 Three.js 为例,该项目包含 362 份 JS 文件,合计约 3w 行代码:

开启 HappyPack 前,构建耗时大约为 11000ms 到 18000ms 之间,开启后耗时降低到 5800ms 到 8000ms 之间,提升约47%。
上述示例仅演示了使用 HappyPack 加载单一资源类型的场景,实践中我们还可以创建多个 HappyPack 插件实例,来加载多种资源类型 —— 只需要用 id 参数做好 Loader 与 Plugin 实例的关联即可,例如:
const HappyPack = require('happypack');module.exports = {// ...module: {rules: [{test: /\.js?$/,// 使用 `id` 参数标识该 Loader 对应的 HappyPack 插件示例use: 'happypack/loader?id=js'},{test: /\.less$/,use: 'happypack/loader?id=styles'},]},plugins: [new HappyPack({// 注意这里要明确提供 id 属性id: 'js',loaders: ['babel-loader', 'eslint-loader']}),new HappyPack({id: 'styles',loaders: ['style-loader', 'css-loader', 'less-loader']})]
};
这里的重点是:
js、less资源都使用happypack/loader作为唯一加载器,并分别赋予id = 'js' | 'styles'参数;- 创建了两个
HappyPack插件实例并分别配置id属性,以及用于处理 js 与 css 的loaders数组; - 启动后,
happypack/loader与HappyPack插件实例将通过id值产生关联,以此实现对不同资源执行不同 Loader 序列。
上面这种多实例模式虽然能应对多种类型资源的加载需求,但默认情况下,HappyPack 插件实例 自行管理 自身所消费的进程,需要导致频繁创建、销毁进程实例 —— 这是非常昂贵的操作,反而会带来新的性能损耗。
为此,HappyPack 提供了一套简单易用的共享进程池接口,只需要创建 HappyPack.ThreadPool 对象,并通过 size 参数限定进程总量,之后将该例配置到各个 HappyPack 插件的 threadPool 属性上即可,例如:
const os = require('os')
const HappyPack = require('happypack');
const happyThreadPool = HappyPack.ThreadPool({// 设置进程池大小size: os.cpus().length - 1
});module.exports = {// ...plugins: [new HappyPack({id: 'js',// 设置共享进程池threadPool: happyThreadPool,loaders: ['babel-loader', 'eslint-loader']}),new HappyPack({id: 'styles',threadPool: happyThreadPool,loaders: ['style-loader', 'css-loader', 'less-loader']})]
};
使用 HappyPack.ThreadPool 接口后,HappyPack 会预先创建好一组工作进程,所有插件实例的资源转译任务会通过内置的 HappyThread 对象转发到空闲进程做处理,避免频繁创建、销毁进程。
最后,我们再来看看 HappyPack 的执行流程:
核心步骤:
happlypack/loader接受到转译请求后,从 Webpack 配置中读取出相应 HappyPack 插件实例;- 调用插件实例的
compile方法,创建HappyThread实例(或从HappyThreadPool取出空闲实例); HappyThread内部调用child_process.fork创建子进程,并执行HappyWorkerChannel文件;HappyWorkerChannel创建HappyWorker,开始执行 Loader 转译逻辑;
中间流程辗转了几层,最终由 HappyWorker 类重新实现了一套与 Webpack Loader 相似的转译逻辑,代码复杂度较高,大家稍作了解即可。
HappyPack 虽然确实能有效提升 Webpack 的打包构建速度,但它有一些明显的缺点:
- 作者已经明确表示不会继续维护,扩展性与稳定性缺乏保障,随着 Webpack 本身的发展迭代,可以预见总有一天 HappyPack 无法完全兼容 Webpack;
- HappyPack 底层以自己的方式重新实现了加载器逻辑,源码与使用方法都不如 Thread-loader 清爽简单,而且会导致一些意想不到的兼容性问题,如
awesome-typescript-loader; - HappyPack 主要作用于文件加载阶段,并不会影响后续的产物生成、合并、优化等功能,性能收益有限。
使用 Thread-loader
Thread-loader 与 HappyPack 功能类似,都是以多进程方式加载文件的 Webpack 组件,两者主要区别:
- Thread-loader 由 Webpack 官方提供,目前还处于持续迭代维护状态,理论上更可靠;
- Thread-loader 只提供了一个 Loader 组件,用法简单很多;
- HappyPack 启动后会创建一套 Mock 上下文环境 —— 包含
emitFile等接口,并传递给 Loader,因此对大多数 Loader 来说,运行在 HappyPack 与运行在 Webpack 原生环境相比没有太大差异;但 Thread-loader 并不具备这一特性,所以要求 Loader 内不能调用特定上下文接口,兼容性较差。
说一千道一万,先来看看基本用法:
-
安装依赖:
yarn add -D thread-loader -
将 Thread-loader 放在
use数组首位,确保最先运行,如:module.exports = {module: {rules: [{test: /\.js$/,use: ["thread-loader", "babel-loader", "eslint-loader"],},],}, };
启动后,Thread-loader 会在加载文件时创建新的进程,在子进程中使用 loader-runner 库运行 thread-loader 之后的 Loader 组件,执行完毕后再将结果回传到 Webpack 主进程,从而实现性能更佳的文件加载转译效果。
以 Three.js 为例,使用 Thread-loader 前,构建耗时大约为 11000ms 到 18000ms 之间,开启后耗时降低到 8000ms 左右,提升约37%。
此外,Thread-loader 还提供了一系列用于控制并发逻辑的配置项,包括:
workers:子进程总数,默认值为require('os').cpus() - 1;workerParallelJobs:单个进程中并发执行的任务数;poolTimeout:子进程如果一直保持空闲状态,超过这个时间后会被关闭;poolRespawn:是否允许在子进程关闭后重新创建新的子进程,一般设置为false即可;workerNodeArgs:用于设置启动子进程时,额外附加的参数。
使用方法跟其它 Loader 一样,都是通过 use.options 属性传递,如:
module.exports = {module: {rules: [{test: /\.js$/,use: [{loader: "thread-loader",options: {workers: 2,workerParallelJobs: 50,// ...},},"babel-loader","eslint-loader",],},],},
};
不过,Thread-loader 也同样面临着频繁的子进程创建、销毁所带来的性能问题,为此,Thread-loader 提供了 warmup 接口用于前置创建若干工作子进程,降低构建时延,用法:
const threadLoader = require("thread-loader");threadLoader.warmup({// 可传入上述 thread-loader 参数workers: 2,workerParallelJobs: 50,},[// 子进程中需要预加载的 node 模块"babel-loader","babel-preset-es2015","sass-loader",]
);
执行效果与 HappyPack.ThreadPool 相似,此处不再赘述。
与 HappyPack 相比,Thread-loader 有两个突出的优点,一是产自 Webpack 官方团队,后续有长期维护计划,稳定性有保障;二是用法更简单。但它不可避免的也存在一些问题:
- 在 Thread-loader 中运行的 Loader 不能调用
emitAsset等接口,这会导致style-loader这一类加载器无法正常工作,解决方案是将这类组件放置在thread-loader之前,如['style-loader', 'thread-loader', 'css-loader']; - Loader 中不能获取
compilation、compiler等实例对象,也无法获取 Webpack 配置。
这会导致一些 Loader 无法与 Thread-loader 共同使用,大家需要仔细加以甄别、测试。
使用 Parallel-Webpack
Thread-loader、HappyPack 这类组件所提供的并行能力都仅作用于文件加载过程,对后续 AST 解析、依赖收集、打包、优化代码等过程均没有影响,理论收益还是比较有限的。对此,社区还提供了另一种并行度更高,以多个独立进程运行 Webpack 实例的方案 —— Parallel-Webpack,基本用法:
-
安装依赖:
yarn add -D parallel-webpack -
在
webpack.config.js配置文件中导出多个 Webpack 配置对象,如:module.exports = [{entry: 'pageA.js',output: {path: './dist',filename: 'pageA.js'} }, {entry: 'pageB.js',output: {path: './dist',filename: 'pageB.js'} }]; -
执行
npx parallel-webpack命令。
-
Parallel-Webpack 会为配置文件中导出的每个 Webpack 配置对象启动一个独立的构建进程,从而实现并行编译的效果。底层原理很简单,基本上就是在 Webpack 上套了个壳:
- 根据传入的配置项数量,调用
worker-farm创建复数个工作进程; - 工作进程内调用 Webpack 执行构建;
- 工作进程执行完毕后,调用
node-ipc向主进程发送结束信号。
- 根据传入的配置项数量,调用
-
这种方式在需要同时执行多份配置的编译时特别有效,但若配置文件本身只是导出了单个配置对象则意义不大。
-
为了更好地支持多种配置的编译,Parallel-Webpack 还提供了
createVariants函数,用于根据给定变量组合,生成多份 Webpack 配置对象,如:const createVariants = require('parallel-webpack').createVariants const webpack = require('webpack')const baseOptions = {entry: './index.js' }// 配置变量组合 // 属性名为 webpack 配置属性;属性值为可选的变量 // 下述变量组合将最终产生 2*2*4 = 16 种形态的配置对象 const variants = {minified: [true, false],debug: [true, false],target: ['commonjs2', 'var', 'umd', 'amd'] }function createConfig (options) {const plugins = [new webpack.DefinePlugin({DEBUG: JSON.stringify(JSON.parse(options.debug))})]return {output: {path: './dist/',filename: 'MyLib.' +options.target +(options.minified ? '.min' : '') +(options.debug ? '.debug' : '') +'.js'},plugins: plugins} }module.exports = createVariants(baseOptions, variants, createConfig) -
上述示例使用
createVariants函数,根据variants变量搭配出 16 种不同的minified、debug、target组合,最终生成如下产物:[WEBPACK] Building 16 targets in parallel [WEBPACK] Started building MyLib.umd.js [WEBPACK] Started building MyLib.umd.min.js [WEBPACK] Started building MyLib.umd.debug.js [WEBPACK] Started building MyLib.umd.min.debug.js[WEBPACK] Started building MyLib.amd.js [WEBPACK] Started building MyLib.amd.min.js [WEBPACK] Started building MyLib.amd.debug.js [WEBPACK] Started building MyLib.amd.min.debug.js[WEBPACK] Started building MyLib.commonjs2.js [WEBPACK] Started building MyLib.commonjs2.min.js [WEBPACK] Started building MyLib.commonjs2.debug.js [WEBPACK] Started building MyLib.commonjs2.min.debug.js[WEBPACK] Started building MyLib.var.js [WEBPACK] Started building MyLib.var.min.js [WEBPACK] Started building MyLib.var.debug.js [WEBPACK] Started building MyLib.var.min.debug.js -
虽然,parallel-webpack 相对于 Thread-loader、HappyPack 有更高的并行度,但进程实例之间并没有做任何形式的通讯,这可能导致相同的工作在不同进程 —— 或者说不同 CPU 核上被重复执行。
-
例如需要对同一份代码同时打包出压缩和非压缩版本时,在 parallel-webpack 方案下,前置的资源加载、依赖解析、AST 分析等操作会被重复执行,仅仅最终阶段生成代码时有所差异。
-
这种技术实现,对单 entry 的项目没有任何收益,只会徒增进程创建成本;但特别适合 MPA 等多 entry 场景,或者需要同时编译出 esm、umd、amd 等多种产物形态的类库场景。
并行压缩
Webpack4 默认使用 Uglify-js 实现代码压缩,Webpack5 之后则升级为 Terser —— 一种性能与兼容性更好的 JavaScript 代码压缩混淆工具,两种组件都原生实现了多进程并行压缩能力。
以 Terser 为例,TerserWebpackPlugin 插件默认已开启并行压缩,开发者也可以通过 parallel 参数(默认值为 require('os').cpus() - 1)设置具体的并发进程数量,如:
const TerserPlugin = require("terser-webpack-plugin");module.exports = {optimization: {minimize: true,minimizer: [new TerserPlugin({parallel: 2 // number | boolean})],},
};
上述配置即可设定最大并行进程数为 2。此外,Webpack4 所使用的 uglifyjs-webpack-plugin 也提供了类似的功能,用法与 Terser 相同,此处不再赘述。
总结
受限于 JavaScript 的单线程架构,Webpack 构建时并不能充分使用现代计算机的多核 CPU 能力,为此社区提供了若干基于多进程实现的并行构建组件,包括文中介绍的 HappyPack、Thread-loader、Parallel-Webpack、Terser。
-
对于 Webpack4 之前的项目,可以使用 HappyPack 实现并行文件加载;
-
Webpack4 之后则建议使用 Thread-loader;
-
多实例并行构建场景建议使用 Parallel-Webpack 实现并行;
-
生产环境下还可配合
terser-webpack-plugin的并行压缩功能,提升整体效率。 -
理论上,并行确实能够提升系统运行效率,但 Node 单线程架构下,所谓的并行计算都只能依托与派生子进程执行,而创建进程这个动作本身就有不小的消耗 —— 大约 600ms,对于小型项目,构建成本可能可能很低,引入多进程技术反而导致整体成本增加,因此建议大家按实际需求斟酌使用上述多进程方案。
-
可以思考,有没有可能使用 Node Worker 实现多线程形式的 Webpack 并行构建?社区是否已经有相关组件?与多进程相比,可能存在怎么样的优缺点?
相关文章:
Webpack: 并行构建
概述 受限于 Node.js 的单线程架构,原生 Webpack 对所有资源文件做的所有解析、转译、合并操作本质上都是在同一个线程内串行执行,CPU 利用率极低,因此,理所当然地,社区出现了一些以多进程方式运行 Webpack࿰…...
Vue的介绍与使用
1.Vue的介绍 内容讲解 【1】Vue介绍 1.vue属于一个前端框架,底层使用原生js编写的。主要用来进行前端和后台服务器之间的一个交互。 2.Vue是一套构建用户界面的渐进式前端框架。 “渐进式框架”简单的来说你可以将Vue作为你的应用一部分嵌入其中,代理…...
MYSQL双主双从,使用Keepalived双机热备+LVS高可用群集
MYSQL双主双从,使用Keepalived双机热备LVS高可用群集 文档只记录KeepalivedLVSmysql主从,不包含检验,如需检验,请自行添加web服务器 一、IP规划 服务器IP备注master1192.168.100.131master2的从master2192.168.100.132maste…...
9.计算机视觉—目标检测
目录 1.物体检测边缘框目标检测数据集总结边缘框代码实现2.锚框:目标检测的一种方法IoU—交并比赋予锚框标号使用非极大值抑制(NMS)输出总结代码实现1.物体检测 边缘框 一个边缘框可以通过四个数字定义 (左上x,左上y),(右下x,右下y)(左上x,左上y,宽,高)(中间x,中间y…...
构造函数深入理解
目录 构造函数构造函数体赋值初始化列表初始化列表格式初始化列表的意义以及注意点const修饰的成员变量初始化对象成员具体初始化的地方缺省值存在的意义例子1例子2 初始化与赋值引用成员变量的初始化注意点1注意点2我的疑惑 自定义类型成员初始化例子1例子2例子3例子4 初始化列…...
Rocky Linux 9 快速安装docker 教程
前述 CentOS 7系统将于2024年06月30日停止维护服务。CentOS官方不再提供CentOS 及后续版本,不再支持新的软件和补丁更新。CentOS用户现有业务随时面临宕机和安全风险,并无法确保及时恢复。由于 CentOS Stream 相对不稳定,刚好在寻找平替系统…...
go语言并发编程1-Gouroutine
参考文档:www.topgoer.com 使用方法 直接包装成函数,go关键字触发即可 注意事项 1 main方法结束后,main方法内启动的子协程会立即结束,无论是否执行完毕; 启动多个groutine 使用sync包的WaitGroup来控制…...
Sylar服务器框架——Http模块
1、http.h 定义了HttpMethod和HttpStatus /* Request Methods */ #define HTTP_METHOD_MAP(XX) \XX(0, DELETE, DELETE) \XX(1, GET, GET) \XX(2, HEAD, HEAD) \XX(3, POST, POST) \XX(4, PUT, …...
7km远距离WiFi实时图传模块,无人机海上无线传输方案,飞睿智能WiFi MESH自组网技术
在浩瀚无垠的海洋上,无人机正在开启一场前所未有的技术创新。它们不再只是天空的舞者,更是海洋的守望者,为我们带来前所未有的视野和数据。而这一切的背后,都离不开一项创新性的技术——飞睿智能远距离WiFi实时图传模块与无线Mesh…...
2024年上半年网络工程师下午真题及答案解析
试题一(20分) 某高校网络拓扑如下图所示,两校区核心(CORE-1、CORE-2),出口防火墙(NGFW-1、NGFW-2)通过校区间光缆互联,配置OSPF实现全校路由收敛,两校区相距40km。两校区默认由本地…...
Jmeter下载、安装及配置
1 Jmeter介绍 Jmeter是进行负载测试的工具,可以在任何支持Java虚拟机环境的平台上运行,比如Windows、Linux、Mac。 Jmeter模拟一组用户向目标服务器发送请求,并统计目标服务器的性能信息,比如CPU、memory usage。 2 Jmeter下载 …...
掌握高效实用的VS调试技巧
🔥 个人主页:大耳朵土土垚 1.编程常见的错误 1.1编译型错误 编程编译型错误是指在编译代码时发现的错误。编译器在编译过程中会检查代码是否符合语法规范和语义要求,如果发现错误会产生编译错误。 直接看错误提示信息(双击&#…...
实验2 字符及字符串输入输出与分支程序设计实验
字符及字符串输入输出 从键盘输入两个一位十进制数,计算这两个数之和,并将结果在屏幕上显示出来。 分支程序设计 从键盘输入一字符,判断该字符是小写字母、大写字母、数字或者其他字符。若输入为小写字母,显示“You Input a Lo…...
docker容器间网络仿真工具-pumba
docker-tc&pumba docker-tc:docker-tc项目仓库 pumba:pumba项目仓库 这两个项目理论上都可以实现对容器间的网络环境进行各种模拟干预,包括延迟,丢包,带宽限制等。 但是我在实际使用时,发现docker-tc这个工具在进行网络进行模…...
A36 STM32_HAL库函数 之PCD通用驱动 -- B -- 所有函数的介绍及使用
A36 STM32_HAL库函数 之PCD通用驱动 -- B -- 所有函数的介绍及使用 1 该驱动函数预览1.11 HAL_PCD_SOFCallback1.12 HAL_PCD_ResetCallback1.13 HAL_PCD_SuspendCallback1.14 HAL_PCD_ResumeCallback1.15 HAL_PCD_ISOOUTIncompleteCallback1.16 HAL_PCD_ISOINIncompleteCallbac…...
vue2 + element三级菜单实现模板
需求: 需要一个含有三级菜单的结构模板,用于业务快速开发。 解决: sidebar.vue <template><el-menu :default-active"defaultActive" class"el-menu-vertical-demo" active-text-color"#ffd04b"&…...
vue H5页面video 视频流自动播放, 解决ios不能自动播放问题
视频组件 <videostyle"width: 100%; height: 100%;object-fit: fill"class"player"refplayer_big_boxcontrolspreloadautoplay //自动播放muted //是否静音playsinline"true"x5-playsinline""webkit-playsinline"tru…...
自闭症儿童:探索症状背后的多彩内心世界
在星启帆自闭症康复中心,我们每天与一群独特而珍贵的孩子相遇——他们,是自闭症谱系障碍的患儿。自闭症,这一复杂的神经发育障碍,以其多样化的症状表现,为每个孩子的生活轨迹绘上了不同的色彩。 自闭症孩子的症状各异…...
在Centos7上安装PostgreSQL16的详细步骤
文章目录 环境一、准备二、postgresql下载方法一:wget下载方法二:下载压缩包解压 三、创建用户组、用户四、创建数据主目录五、配置环境变量六、initdb初使化数据库七、配置服务八、设置开机自启动九、设置防火墙十、启动数据库服务 环境 CPU: 4 核心或以…...
MySQL 图形化界面
填完信息之后,圆圈处可以验证是否可以连接数据库 展示所有数据库(因为有的可能连上,却没有数据库显示)...
AI-调查研究-01-正念冥想有用吗?对健康的影响及科学指南
点一下关注吧!!!非常感谢!!持续更新!!! 🚀 AI篇持续更新中!(长期更新) 目前2025年06月05日更新到: AI炼丹日志-28 - Aud…...
Mac软件卸载指南,简单易懂!
刚和Adobe分手,它却总在Library里给你写"回忆录"?卸载的Final Cut Pro像电子幽灵般阴魂不散?总是会有残留文件,别慌!这份Mac软件卸载指南,将用最硬核的方式教你"数字分手术"࿰…...
《基于Apache Flink的流处理》笔记
思维导图 1-3 章 4-7章 8-11 章 参考资料 源码: https://github.com/streaming-with-flink 博客 https://flink.apache.org/bloghttps://www.ververica.com/blog 聚会及会议 https://flink-forward.orghttps://www.meetup.com/topics/apache-flink https://n…...
DeepSeek 技术赋能无人农场协同作业:用 AI 重构农田管理 “神经网”
目录 一、引言二、DeepSeek 技术大揭秘2.1 核心架构解析2.2 关键技术剖析 三、智能农业无人农场协同作业现状3.1 发展现状概述3.2 协同作业模式介绍 四、DeepSeek 的 “农场奇妙游”4.1 数据处理与分析4.2 作物生长监测与预测4.3 病虫害防治4.4 农机协同作业调度 五、实际案例大…...
Mobile ALOHA全身模仿学习
一、题目 Mobile ALOHA:通过低成本全身远程操作学习双手移动操作 传统模仿学习(Imitation Learning)缺点:聚焦与桌面操作,缺乏通用任务所需的移动性和灵活性 本论文优点:(1)在ALOHA…...
USB Over IP专用硬件的5个特点
USB over IP技术通过将USB协议数据封装在标准TCP/IP网络数据包中,从根本上改变了USB连接。这允许客户端通过局域网或广域网远程访问和控制物理连接到服务器的USB设备(如专用硬件设备),从而消除了直接物理连接的需要。USB over IP的…...
【电力电子】基于STM32F103C8T6单片机双极性SPWM逆变(硬件篇)
本项目是基于 STM32F103C8T6 微控制器的 SPWM(正弦脉宽调制)电源模块,能够生成可调频率和幅值的正弦波交流电源输出。该项目适用于逆变器、UPS电源、变频器等应用场景。 供电电源 输入电压采集 上图为本设计的电源电路,图中 D1 为二极管, 其目的是防止正负极电源反接, …...
(一)单例模式
一、前言 单例模式属于六大创建型模式,即在软件设计过程中,主要关注创建对象的结果,并不关心创建对象的过程及细节。创建型设计模式将类对象的实例化过程进行抽象化接口设计,从而隐藏了类对象的实例是如何被创建的,封装了软件系统使用的具体对象类型。 六大创建型模式包括…...
人工智能--安全大模型训练计划:基于Fine-tuning + LLM Agent
安全大模型训练计划:基于Fine-tuning LLM Agent 1. 构建高质量安全数据集 目标:为安全大模型创建高质量、去偏、符合伦理的训练数据集,涵盖安全相关任务(如有害内容检测、隐私保护、道德推理等)。 1.1 数据收集 描…...
Neko虚拟浏览器远程协作方案:Docker+内网穿透技术部署实践
前言:本文将向开发者介绍一款创新性协作工具——Neko虚拟浏览器。在数字化协作场景中,跨地域的团队常需面对实时共享屏幕、协同编辑文档等需求。通过本指南,你将掌握在Ubuntu系统中使用容器化技术部署该工具的具体方案,并结合内网…...
