前端工程化之手搓webpack5 --【elpis全栈项目】

前端工程化之手搓webpack5 --【elpis全栈项目】

导读



基本流程:输入 – 编译 – 输出

被引擎读取
打包
输入--业务文件:pages
编译--引擎编译
输出--产物文件:dist


我们要做的就是:

  1. 配置引擎自动读取业务文件夹pages(比如一些xxx.vue文件);
  2. 对业务代码进行编译、分包、压缩等操作;
  3. 得到一个dist文件夹(包含html\css\js等)

最后将dist部署到服务器就行了,这应该是任何一个工程化工具实现的最基本逻辑了吧。

所以,以webpack为例我们有必要认识一些基本配置属性:
在这里插入图片描述

以我的项目为例,我们来一步步配置出一个适合自己的webpack脚手架。以下是目录:

以下目录都在app文件夹下:|-- pages 				业务文件夹: 存放vue等文件
|-- public				产出文件夹: 打包后输出的dist文件会生成在这里
|-- webpack				|-- config			webpack不同环境配置|-- webpack.base.js|-- webapck.dev.js|-- webapck.prod.js|-- dev.js      	启动 开发 环境的的入口文件|-- prod.js			启动 生产 环境的的入口文件
  • 这是我的pages文件夹,用以实现一个项目多页面入口;(这个js就简单的认为他是个一般的SPA项目的mian.js
    在这里插入图片描述
  • 最后prod环境要实现这样一个效果:将代码分割、分包、压缩、提取公共方法、树摇…等等。在这里插入图片描述
  • dev环境要实现这样一个效果:本地起一个服务,将dist放到本地的服务器上、实现HRM(热更新)在这里插入图片描述

一、 基本配置: webpack.base.js


我们将公共配置,集中提取放置到这个base文件中,避免重复配置。
基本思路:

  1. entry:配置入口文件的路径
const pageEntries = {};
// 生成一个绝对路径,例如:/user/elpis/app/pages/**/entry.*.js
const entryList = path.resolve(process.cwd(), './app/pages/**/entry.*.js');
// glob.sync是一个同步方法,会返回一个包含所有匹配文件路径的数组。
glob.sync(entryList).forEach(file => {// file会输出:'/user/elpis/app/pages/page1/entry.page1.js'const entryName = path.basename(file, '.js');  // 会提取文件名并去掉 .js, 例如:entry.page1pageEntries[entryName] = file;
})module.exports = {// entry: {entry.page1: '/user/elpis/app/pages/page1/entry.page1.js', ...}entry: pageEntries  
}
  1. module:规定不同的文件,分别需要用什么loader去解析。用test属性配置文件的匹配规则;用include指定范围;用use指定使用哪个loaderoptions则是对loader的配置。
 module.exports ={...module: {rules: [{test: /\.css$/,use: ['style-loader', 'css-loader']},{test: /\.js$/,include: [ path.resolve(process.cwd(), '/app/pages')], // 只对业务代码进行babel,加快打包速度use: { loader: 'babel-loader' }},{test: /\.(png|jpe?g|gif)(\?.+)?$/,use: { loader: 'url-loader',options: {limit: 300,  esMoule: false }   // 小于300kb的图片会被转成base64编码、禁用esModule}}]}...}
  1. resolve: 配置一些解析时候的具体行为,是个优化项,看个人需要配置。例如
module.exports ={...resolve: {// 定义别名,方便引入业务代码: import { xxx } from '$common/xxx';alias: { $common: path.resolve(process.cwd(), './app/pages/common') }}...
}
  1. plugins: 使用插件。
module.exports ={...plugins: [new VueLoaderPlugin()  // 处理 .vue 文件,这个插件是必须的...]...
}
  1. optimization: 输出优化。代码分割,模块分割,缓存,treeShaing,压缩等优化策略
module.exports ={...optimization: {splitChunks:{...}, // 代码分割, 具体的看下文runtimeChunk: true 	// 将 webpack 运行时生成的代码打包到 runtime.js}...
}

由此我们得到了一个完整的 base.js 的配置:

const glob = require('glob')
const path = require('path')
const webpack = require('webpack')
const { VueLoaderPlugin } = require('vue-loader')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const CleanWebpackPlugin = require('clean-webpack-plugin')const pageEntries = {};
const htmlWebpackPluginList = [];// 获取 app/pages 目录下的所有页面入口文件(entry.xx.js)
const entryList = path.resolve(process.cwd(), './app/pages/**/entry.*.js');
glob.sync(entryList).forEach(file => {const entryName = path.basename(file, '.js');// 构造 entrypageEntries[entryName] = file;// 构造最终渲染的页面文件htmlWebpackPluginList.push(new HtmlWebpackPlugin({// 产物最终模版filename: path.resolve(process.cwd(), './app/public/dist/', `${entryName}.tpl`),// 指定模版文件template: path.resolve(process.cwd(), './app/view/entry.tpl'),// 要注入的代码块chunks: [entryName]}))
})/*** webpack 基础配置*/
module.exports = {// 入口配置entry: pageEntries,// 模块解析配置module: {rules: [{test: /\.vue$/,use: {loader: 'vue-loader'}},{test: /\.js$/,include: [path.resolve(process.cwd(), '/app/pages') // 只对业务代码进行babel,加快打包速度],use: {loader: 'babel-loader'}},{test: /\.(png|jpe?g|gif)(\?.+)?$/,use: {loader: 'url-loader',options: {limit: 300, // 小于300kb的图片会被转成base64编码esMoule: false // 禁用esModule}}}, {test: /\.css$/,use: ['style-loader', 'css-loader']}, {test: /\.less$/,use: ['style-loader', 'css-loader', 'less-loader']},{test: /\.(eot|svg|ttf|woff|woff2)(\?\S*)?$/, // 例如:file.woff2?v=abc&opt=1use: 'file-loader'}]},// 产物输出路径, 因为开发和生产环境输出不一致,所以在各自环境中进行配置output: {},// 配置模块解析的具体行为(定义 webpack 在打包时,如何找到并解析具体模块的路径)resolve: {// 尝试按顺序解析这些后缀名。如果有多个文件有相同的名字,但后缀名不同,webpack 会解析列在数组首位的后缀的文件 并跳过其余的后缀// 能够使用户在引入模块时不带扩展:import File from '../path/to/file';extensions: ['.js', '.vue', '.less', '.css'],// 配置别名: import { xxx } from '$common/xxx';alias: {$page: path.resolve(process.cwd(), './app/pages'), // 定义别名,方便引入业务代码$common: path.resolve(process.cwd(), './app/pages/common'),$widgets: path.resolve(process.cwd(), './app/pages/widgets'),$store: path.resolve(process.cwd(), './app/pages/store'),}},// 配置 webpack 插件plugins: [// 处理 .vue 文件,这个插件是必须de// 它的职能是将定义过的其他规则复制并应用到 .vue 文件中// 例如,如果有一条匹配规则 /\.js$/ 的规则, 那么他会应用到 .vue 文件中的 script 板块中new VueLoaderPlugin(),// 把第三方库暴露到 window context 下 // 任何文件都可以直接使用 Vue,Webpack 会自动将其映射为 require('vue')。// 例如 new Vue( { el: '#app', render: h => h(App) } );new webpack.ProvidePlugin({ Vue: 'vue' }),// 定义全局常量new webpack.DefinePlugin({__VUE_OPRIONS_API__: 'true', // 禁用选项式 API 支持__VUE_PRO_DEVTOOLS: 'false', // 禁用 vue 调试工具__VUE_PRO_HYDRATION_MISMATCH_DETAILS__: 'false' // 禁用生产环境构建下激活 (hydration) 不匹配的详细警告}),// 显示打包进度new webpack.ProgressPlugin(),// 每次 build 前清空 public/dist 目录new CleanWebpackPlugin(['public/dist'], {root: path.resolve(process.cwd(), './app/'),exclude: [],verbose: true,dry: false}),// 构造最终渲染的页面模版...htmlWebpackPluginList,],// 配置打包输出优化(代码分割,模块分割,缓存,treeShaing,压缩等优化策略)optimization: {/*** 把 js 文件打包成3种类型* 1. verdor: 第三方 lib 库, 基本不会改动, 除非依赖版本升级* 2. common: 业务组件代码的公共部分抽取出来, 改动较少* 3. ebnty.{page}:  不同页面 entry 里的业务组件代码的差异部分,会经常改动* 目的: 把改动和引用频率不一样的 js 区分出来,已达到更好利用浏览器缓存的效果*/splitChunks: {chunks: 'all', // 对同步和异步模块都进行分割maxAsyncRequests: 10, // 每次异步加载的最大并行请求数maxInitialRequests: 10, // 入口点的最大并行请求数cacheGroups: {vendor: { // 第三方库test: /[\\/]node_modules[\\/]/, // 打包node_modules 目录下的模块name: 'vendor', //模块名称priority: 20, // 优先级,数字越大越优先enforce: true, // 强制执行reuseExistingChunk: true, // 复用已有的公共 chunk},common: { // 业务组件公共代码name: 'common',minChunks: 2, // 被两处引用即被归为公共模块minSize: 1024 * 1, // 最小分割文件大小 priority: 10, // 优先级reuseExistingChunk: true, // 复用已有的公共 chunk}},},// 将 webpack 运行时生成的代码打包到 runtime.jsruntimeChunk: true},
}

二、 生产环境配置:webpack.prod.js + prod.js


生产环境的配置主要集中在打包优化上,比如:代码分割、压缩、分包、树摇等等。
基本思路:

  1. 基于base.js,合并配置
const baseConfig = require('./webpack.base.js');
const webpackConfig = merge.smart(baseConfig, {mode:'',output:''...
})
  1. mode:指定模式,为生产环境。指定生产环境之后会默认开启一些配置,比如 tree sharking。
mode:'production'
  1. output: 配置产物输出路径。属性path 指定的是 Webpack 打包后文件输出的物理路径, 属性publicPath 指定的文件在服务器上的访问路径
 output: {filename: 'js/[name]_[chunkhash:8].bundle.js', // 具体看 文档output/#template-stringspath: path.join(process.cwd(), './app/public/dist/prod'),publicPath: '/dist/prod', // 输出目录的公共 URLcrossOriginLoading: 'anonymous' // 允许跨域加载}
  1. module: 对每个模块要使用的loader等配置。使用thread-loader实现多线程打包
module: {rules: [{test: /\.js$/,include: [path.resolve(process.cwd(), './app/pages')],use: [{loader: 'thread-loader', // 多线程编译loaderoptions: {workers: os.cpus().length, // 使用 CPU 核心数}},{loader: 'babel-loader',options: {presets: ['@babel/preset-env'], // 用于语法转换和按需引入 polyfill (处理旧版本浏览器兼容,填补 API/新特性 缺失)plugins: ['@babel/plugin-transform-runtime'] // 用于复用辅助代码和模块化 polyfill。}}]}]}
  1. plugins: 插件配置。使用MiniCssExtractPlugin插件,提取css公共部分等等。
  2. optimization: 优化配置。使用TerserWebpackPlugin提升压缩阶段的性能
  3. 最后将webpack.prod.js的配置传入prod.js,prod.js通过webpack()方法启动打包
const webpackProdConfig = require('./config/webpack.prod.js');
webpack(webpackProdConfig, (err, stats) => {...做一些执行打包的回调处理}))

生产环境全量配置:wbepack.prod.js

const path = require('path');
const merge = require('webpack-merge');
const os = require('os');
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
const CSSMinimizerPlugin = require('css-minimizer-webpack-plugin')
const HtmlWebpackInjectAttributesPlugin = require('html-webpack-inject-attributes-plugin')
const TerserWebpackPlugin = require('terser-webpack-plugin')// 基类配置
const baseConfig = require('./webpack.base.js');const webpackConfig = merge.smart(baseConfig, {// 指定生产环境mode: 'production',// 生产环境的 out put 配置output: {filename: 'js/[name]_[chunkhash:8].bundle.js',path: path.join(process.cwd(), './app/public/dist/prod'),publicPath: '/dist/prod', // 输出目录的公共 URLcrossOriginLoading: 'anonymous' // 允许跨域加载},module: {rules: [{test: /\.css$/,use: [MiniCssExtractPlugin.loader, "thread-loader"]}, {test: /\.js$/,include: [path.resolve(process.cwd(), './app/pages')],use: [{loader: 'thread-loader', // 多线程编译loaderoptions: {workers: os.cpus().length, // 使用 CPU 核心数}},{loader: 'babel-loader',options: {presets: ['@babel/preset-env'], // 用于语法转换和按需引入 polyfill (处理旧版本浏览器兼容,填补 API/新特性 缺失)plugins: ['@babel/plugin-transform-runtime'] // 用于复用辅助代码和模块化 polyfill。}}]}]},// performance 用于控制性能提示信息, 默认为 warning; 文件体积过大, 入口过多, 资源加载方式等情况下会提示警告performance: {hints: false},plugins: [// 提取 css 的公共部分, 有效利用缓存new MiniCssExtractPlugin({chunkFilename: 'css/[name]_[contenthash:8].bundle.css'}),// 优化并压缩 cssnew CSSMinimizerPlugin(),// 浏览器在请求资源时不发送用户的身份凭证new HtmlWebpackInjectAttributesPlugin({crossorgin: 'anonymous'})],optimization: { // 优化配置// 使用 TerserWebpackPlugin 的并发和缓存,提升压缩阶段的性能minimize: true,minimizer: [new TerserWebpackPlugin({cache: true, // 启用缓存来加速构建过程parallel: true, // 利用多核 CPU 并行压缩terserOptions: {compress: {drop_console: true // 移除 console.log}}})]}
});module.exports = webpackConfig;

生产环境启动配置:prod.js

const webpack = require('webpack');
const webpackProdConfig = require('./config/webpack.prod.js');console.log('\nbuilding... \n');// 如果你不向 webpack 传入可执行的回调函数, 它会返回一个 webpack Compiler 实例并在其中进行操作
// const compiler = webpack(webpackProdConfig);
// 区别在于compiler.run()更具灵活性、控制粒度等适合多次打包, 直接传入一个回调函数 (err, stats)=>{} 则只会执行一次打包, 更适用于生存环境的场景
webpack(webpackProdConfig, (err, stats) => {// 配置文件错误if (err) {console.log('❗err: \n', err)return}// stats.hasErrors()判断缺失的 module,语法错误等, 还有个 stats.hasWarnings() 方法,可以用来判断是否有警告信息if (stats.hasErrors()) {const info = stats.toJson()console.error(info.errors);}// process.stdout.write 更高效,适合大量数据输出 (console.log 是基于它实现的)process.stdout.write(`${stats.toString({colors: true,// 在控制台输出色彩信息modules: false, // 不显示每个模块的打包信息children: false, // 不显示子编译任务的信息chunks: false, // 不显示每个代码块的信息chunkModules: false // 显示代码块中模块的信息})}\n`)
});

三、 开发环境配置:webpack.dev.js + dev.js


开发环境则需要,配置 HMR 实现热更新

  1. 通过merge合并base和dev的配置
 const webpackConfig = merge.smart(baseConfig,{mode:'',....
})
  1. 通过修改base.js的entry来配置HRM
const baseConfig = require('./webpack.base.js');// 开发阶段的 entry 配置需要加入 hmr
Object.keys(baseConfig.entry).forEach(v => {// 第三方包不作为 hmr 的入口if (v !== 'vendor') {baseConfig.entry[v] = [baseConfig.entry[v], // 主入口文件// hmr 更新入口,官方指定的 hmr 路径`webpack-hot-middleware/client?path=http://${host}:${port}/${hmrPath}&timeout=${timeout}`,]}
})
  1. 指定mode 等于 ’development‘
  2. devtool: 等于 eval-cheap-module-source-map时。 soure-map 配置 便于开发时调试
  3. output: 配置产物输出路径。与生产环境不同的是,开发环境的产物需要放到本地服务器上。通过设置 globalObject: 'this', Webpack 会根据运行环境自动选择正确的全局对象。
 output: {filename: 'js/[name]_[chunkhash:8].bundle.js',path: path.resolve(process.cwd(), './app/public/dist/dev/'),publicPath: `http://${host}:${port}/public/dist/dev/`, // 输出目录的公共 URLglobalObject: 'this'}
  1. plugins: 通过配置webpack.HotModuleReplacementPlugin插件实现热模块替换
 plugins: [ new webpack.HotModuleReplacementPlugin({ multiStep: false }) ]
  1. 自定义一个服务,将产物文件放到本地服务器上。主要是用到两个中间件: 用devMiddleware监控文件改动,用hotMiddleware实现热更新,通知浏览器刷新
const { webpackConfig, DEV_SERVER_CONFIG } = require('./config/webpack.dev');
const app = express();
const compiler = webpack(webpackConfig)app.use(devMiddleware(compiler, {}))
app.use(hotMiddleware(compiler, {}))
app.listen(DEV_SERVER_CONFIG.PORT, () => {})

开发环境打包配置:webpack.dev.js

const path = require('path');
const merge = require('webpack-merge');
const webpack = require('webpack')// 基类配置
const baseConfig = require('./webpack.base.js');// dev-server 配置
const DEV_SERVER_CONFIG = {HOST: '127.0.0.1',PORT: 9200,HMR_PATH: '__webpack_hmr',TIMEOUT: 20000,
}
const { HOST: host, PORT: port, HMR_PATH: hmrPath, TIMEOUT: timeout } = DEV_SERVER_CONFIG;// 开发阶段的 entry 配置需要加入 hmr 
Object.keys(baseConfig.entry).forEach(v => {console.log('--------v-------', v)// 第三方包不作为 hmr 的入口  有时候可能会手动配置第三方库 entry: { vendor: ['vue', 'lodash']} 将其打包到一个单独的文件中// 与 splitChunks 的区别: 自动从 node_modules 中提取第三方库。更灵活,适用于复杂的项目。if (v !== 'vendor') {baseConfig.entry[v] = [baseConfig.entry[v], // 主入口文件// hmr 更新入口,官方指定的 hmr 路径`webpack-hot-middleware/client?path=http://${host}:${port}/${hmrPath}&timeout=${timeout}`,]}
})const webpackConfig = merge.smart(baseConfig, {// 指定开发环境mode: 'development',// soure-map 配置 便于开发时调试devtool: 'eval-cheap-module-source-map',// 开发环境的 out put 配置output: {filename: 'js/[name]_[chunkhash:8].bundle.js',path: path.resolve(process.cwd(), './app/public/dist/dev/'),publicPath: `http://${host}:${port}/public/dist/dev/`, // 输出目录的公共 URLglobalObject: 'this' // 用于指定 Webpack 打包代码时引用的全局对象。配置成 'this' Webpack 会根据运行环境自动选择正确的全局对象。},// 开发阶段插件plugins: [// HotModuleReplacementPlugin 用于实现热模块替换 (Hot Module Replacement - HMR)// 模块热替换允许在应用程序运行时替换模块// 极大的提升开发效率, 因为能让应用程序一直保持运行状态new webpack.HotModuleReplacementPlugin({ multiStep: false }),]
});module.exports = {webpackConfig,  // webpack 配置DEV_SERVER_CONFIG // devServer 配置, 暴露给dev.js使用
};

开发环境启动配置:dev.js

// 本地开发启动devServer
const express = require('express');
const path = require('path');
const webpack = require('webpack');
const devMiddleware = require('webpack-dev-middleware');
const hotMiddleware = require('webpack-hot-middleware');// 从 webpack.dev.js 获取 webpack 配置 和 devServer 配置
const { webpackConfig, DEV_SERVER_CONFIG } = require('./config/webpack.dev');const app = express();const compiler = webpack(webpackConfig)// 指定静态文件目录
app.use(express.static(path.join(__dirname, '../public/dist')));// 引用 devMiddleware 中间件 (监控文件改动)
app.use(devMiddleware(compiler, {writeToDisk: (filPath) => filPath.endsWith('.tpl'), // 落地文件publicPath: webpackConfig.output.publicPath, // 资源路径// headers 配置headers: {'Access-Control-Allow-Origin': '*','Access-Control-Allow-Methods': 'GET, POST, PUT, DELETE, PATCH, OPTIONS','Access-Control-Allow-Headers': 'X-Requested-With, contnet-type, Authorization'},stats: {colors: true}
}))// 引用 hotMiddleware 中间件 (热更新)
app.use(hotMiddleware(compiler, {path: `/${DEV_SERVER_CONFIG.HMR_PATH}`,log: () => { }
}))console.info('请等待webpack初次构建完成...')const port = DEV_SERVER_CONFIG.PORT;
app.listen(port, () => {console.log("🚀 ~ app.listening on port:", port)
})

四、 配置npm启动打包


pagkage.json:

在这里插入图片描述

因为开发环境下,产物文件都放在本地服务器上,所以需要通过配置--max_old_space_size分配好足够的内存。

    "build:dev": "node --max_old_space_size=4096 ./app/webpack/dev.js","build:prod": "node ./app/webpack/prod.js"

更多参考:wbepack中文文档

全文特别鸣谢: 抖音“哲玄前端”,《全栈实践课》

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【ArcGIS Pro二次开发实例教程】(1):图层的前置、后置

一、简介 此工具要实现的功能是:将内容框中当前选定的图层移到最顶层或最底层。 主要技术要点包括: 1、Config.daml文件设置(UI设置) 2、按钮的图片和位置设置 3、当前选定图层的获取 4、图层在内容列表中位置的获取和移动 …

2023最新版IDEA创建一个SpringBoot项目 (详细教程)

在IDEA中创建一个SpringBoot项目 springboot是我们java开发中最流行的框架之一,下面我们看看如何在idea中创建一个springboot项目。 文章目录 在IDEA中创建一个SpringBoot项目一、springboot简单介绍二、快速开始1.新建项目2.配置Maven3.测试 总结 一、springboot简…

Spring SpEL表达式由浅入深

标题 前言概述功能使用字面值对象属性和方法变量引用#this 和 #root变量获取类的类型调用对象(类)的方法调用类构造器类型转换运算符赋值运算符条件(关系)表达式三元表达式Elvis 操作符逻辑运算instanceof 和 正则表达式的匹配操作符 安全导航操作员数组集合(Array 、List、Map…

Jetpack Compose 学习笔记(四)—— CompositionLocal 与主题

Compose 中的主题内的代码大量定义了 CompositionLocal,因此在学习主题之前,先学习 CompositionLocal 铺垫好基础。 1、CompositionLocal 1.1 显式传参与隐式传参 显式传参需要依赖函数的调用,层层传递;隐式传参通过创建全局变量…

利用webworker解决性能瓶颈案例

目录 js单线程的问题webworker的基本使用webworker的常见应用可视化优化导出Excel js单线程的问题 众所周知,js不擅长计算,计算是同步的,大规模的计算会让js主线程阻塞,导致界面完成卡死。比如有一个600多亿次的计算,…

【数据结构05】排序

系列文章目录 【数据结构05】排序 . 【算法思想04】二分查找 文章目录 系列文章目录[toc] 1. 基本思想与实现1.1 插入类排序1.1.1 直接插入排序(*)1.1.2 折半插入排序1.1.3 希尔排序(*) 1.2 交换类排序1.2.1 冒泡排序&#xff08…

数据挖掘——概论

数据挖掘——概论 数据、信息和知识的关联和联系数据挖掘数据挖掘定义数据挖掘的步骤数据挖掘的主要内容 数据、信息和知识的关联和联系 数据、信息和知识是知识工作者感知和认识客观事物的3个连贯的阶段。 数据无处不在,聚合/集成后转化为信息,结构化后…

Hello 2025

Hello 2025 A. MEX Table 题意: 给出 0 到 n ∗ m − 1 的数字,排列成 n m 的表格,最大化 ∑ i 1 n mex ⁡ ( { a i , 1 , a i , 2 , … , a i , m } ) ∑ j 1 m mex ⁡ ( { a 1 , j , a 2 , j , … , a n , j } ) 给出0到n*m-1的数字&…

[答疑]用例规约:系统请求3dsMax创建体块

DDD领域驱动设计批评文集 做强化自测题获得“软件方法建模师”称号 《软件方法》各章合集 第五元素 2024-12-22 12:39 潘老师,请教一个问题:在需求工作流书写系统用例规约时,这个“计算”(改变)可不可以写多个内容&…

第431场周赛:最长乘积等价子数组、计算字符串的镜像分数、收集连续 K 个袋子可以获得的最多硬币数量、不重叠区间的最大得分

Q1、最长乘积等价子数组 1、题目描述 给你一个由 正整数 组成的数组 nums。 如果一个数组 arr 满足 prod(arr) lcm(arr) * gcd(arr),则称其为 乘积等价数组 ,其中: prod(arr) 表示 arr 中所有元素的乘积。gcd(arr) 表示 arr 中所有元素的…

小程序租赁系统开发的优势与应用前景分析

内容概要 小程序租赁系统是一种新兴的数字化解决方案,旨在为用户提供更加便捷与高效的租赁服务。它通常包括一系列功能,如在线浏览、即时预定、支付功能以及用户反馈机制。这些系统在使用上极为友好,让用户能够轻松选择所需的商品或服务&…

Uniapp Android 本地离线打包(详细流程)

一、简介 App 离线 SDK 暂时不支持 Kotlin,未来不清楚。 uniapp 提供了 云打包 与 本地打包 两种方案,云打包 需要排队且还有次数限制,本地打包 则就没有这些限制,而且会 本地打包 对开发 原生插件 有很大的帮助。 细节&#x…

GPT系统重大升级,开创国内先河:o1支持图片识别功能正式上线

文章目录 零、前言一、授权码登录体验优化:一步直达聊天界面二、全新“项目”功能:让工作更有条理三、语音功能升级:全新交互体验四、o1支持图片识别五、总结 零、前言 我是虚竹哥,目标是带十万人玩转ChatGPT。 亲爱的用户&…

音视频入门基础:MPEG2-PS专题(3)——MPEG2-PS格式简介

一、引言 本文对MPEG2-PS格式进行简介。 进行简介之前,请各位先下载MPEG2-PS的官方文档。ITU-T和ISO/IEC都分别提供MPEG2-PS的官方文档。但是ITU提供的文档是免费的,ISO/IEC是付费的,所以我们主要阅读ITU提供的官方文档,比如较新…

概述(讲讲python基本语法和第三方库)

我是北子,这是我自己写的python教程,主要是记录自己的学习成果方便自己日后复习, 我先学了C/C,所以这套教程中可能会将很多概念和C/C去对比,所以该教程大概不适合零基础的人。 it seems that python nowadays 只在人工…

Java jni调用nnom rnn-denoise 降噪

介绍&#xff1a;https://github.com/majianjia/nnom/blob/master/examples/rnn-denoise/README_CN.md 默认提供了一个wav的例子 #include <stdint.h> #include <stdlib.h> #include <stdio.h> #include <math.h> #include <string.h>#include …

【软考网工笔记】计算机基础理论与安全——网络规划与设计

HFC 混合光纤同轴电缆网 HFC: Hybrid Fiber - Coaxial 的缩写&#xff0c;即混合光纤同轴电缆网。是一种经济实用的综合数字服务宽带网接入技术。 HFC 通常由光纤干线、同轴电缆支线和用户配线网络三部分组成&#xff0c;从有线电视台出来的节目信号先变成光信号在干线上传输…

记录一次电脑被入侵用来挖矿的过程(Trojan、Miner、Hack、turminoob)

文章目录 0、总结1、背景2、端倪3、有个微软的系统更新&#xff0c;就想着更新看看&#xff08;能否冲掉问题&#xff09;4、更新没成功&#xff0c;自动重启电脑5、风险文件&#xff08;好家伙命名还挺规范&#xff0c;一看名字就知道出问题了&#xff09;6、开机有一些注册表…

Qt 5.14.2 学习记录 —— 일 新项目

文章目录 1、创建2、查看代码 ---- main.cpp3、查看代码 ---- widgt.h4、查看代码 ---- widgt.cpp和widget.ui5、查看代码 ---- Empty.pro6、运行产生的中间文件 1、创建 左上角的文件&#xff0c;新建文件或项目。如果要写一个GUI程序&#xff0c;应当选择Application&#x…

vscode如何离线安装插件

在没有网络的时候,如果要安装插件,就会麻烦一些,需要通过离线安装的方式进行。下面记录如何在vscode离线安装插件。 一、下载离线插件 在一台能联网的电脑中,下载好离线插件,拷贝到无法联网的电脑上。等待安装。 vscode插件商店地址:https://marketplace.visualstudio.co…