Webpack5 基本使用 - 3(完结)

环境区分

可以定义多个配置文件,通过 webpack-merge 合并配置文件。
安装 webpack-merge

yarn add webpack-merge

公共配置

// webpack.common.js
const path = require('path')
const HtmlWebpackPlugin = require('html-webpack-plugin')module.exports = {entry: path.join(__dirname, 'index'),module: {rules: [{test: /\.js$/,use: ['babel-loader'],include: path.join(__dirname, 'src'),exclude: /node_modules/},]},plugins: [new HtmlWebpackPlugin({template: path.join(__dirname, 'src/index.html'),filename: 'index.html'})]
};

开发环境配置

// webpack.dev.js
const path = require('path');
const webpack = require('webpack');
const { merge } = require('webpack-merge');
const webpackCommonConf = require('./webpack.common.js');module.exports = merge(webpackCommonConf, {mode: 'development',plugins: [// 定义环境变量new webpack.DefinePlugin({// window.ENV = 'production'ENV: JSON.stringify('development')})],devServer: {port: 8080, // 修改端口号hot: true, // 开启 HMR:开启后 index.html 不会自动刷新static: {watch: true, // 自动刷新浏览器staticOptions: {progress: true, // 显示打包的进度条contentBase: path.join(__dirname, 'dist') // 指定运行代码的目录,输出在内存中,看不到}},open: true, // 自动打开浏览器compress: true, // 启动 gzip 压缩// 设置代理proxy: {// 将本地 /api/xxx 代理到 localhost:3000/api/xxx'/api': 'http://localhost:3000',// 将本地 /api2/xxx 代理到 localhost:3000/xxx'/api2': {target: 'http://localhost:3000',pathRewrite: {'/api2': ''}}}}
});
// package.json
{"name": "webpack5-zql","version": "1.0.0","description": "","main": "index.js","scripts": {"dev": "webpack-dev-server --config webpack.dev.js",},
}

生产环境配置

// webpack.prod.js
const path = require('path');
const webpack = require('webpack');
const { merge } = require('webpack-merge');
const webpackCommonConf = require('./webpack.common.js');module.exports = merge(webpackCommonConf, {mode: 'production',output: {filename: 'bundle.[contenthash:8].js', // 输出文件名,打包代码时加上 hash 戳可以命中缓存path: path.join(__dirname, 'dist'), // 输出的文件目录clean: true, //// publicPath: 'http://cdn.abc.com'  // 修改所有静态文件 url 的前缀(如 cdn 域名),这里暂时用不到},plugins: [new webpack.DefinePlugin({// window.ENV = 'production'ENV: JSON.stringify('production')})]
});
// package.json
{"name": "webpack5-zql","version": "1.0.0","description": "","main": "index.js","scripts": {"build": "webpack --config webpack.prod.js"},
}

提升开发体验

  • SourceMap(源代码映射):用来生成源代码与构建后代码一一映射的文件。
  • 它会生成一个 xxx.map 文件,里面包含换代码和构建后代码每一行、每一列的映射关系。当构建后代码出错了,会通过 xxx.map 文件,从构建后代码出错的位置找到映射后源代码出错的位置,帮助我们更快的找到错误根源。

通过 webpack.devtool 配置源代码映射。

开发环境

  • 开发环境:cheap-module-source-map
  • 打包编译速度快,只包含行映射,没有列映射。
module.exports = {mode: 'development',devtool: 'cheap-module-source-map'
};

生产环境

  • 生产环境:source-map
  • 包含行、列映射,打包速度编译慢
module.exports = {mode: 'production',devtool: 'source-map'
};

优化打包构建速度

HMR(仅开发环境)

  • 开发时,我们修改了其中一个模块代码,webpack 默认会将所有模块全部重新打包,速度很慢,所以我们需要做到修改某个模块代码,就只有这个模块代码需要重新打包编译,其他的模块不变,这样打包速度就可以加快。
  • Hot Module Replacement (热模块替换):在程序运行中,替换、添加或删除模块,而无序重新打包整个模块。
module.exports = {mode: 'development',devServer: {hot: true // 开启 HMR,默认是 true}
};
  • 开发环境下,css 文件默认支持 HMR,是因为 style-loader 做了支持,如果使用 MiniCssExtractPlugin.loadercssHMR 会失效,所有开发环境下请使用 style-loader ,生产环境下使用 MiniCssExtractPlugin.loader

  • js 文件默认是不支持 HMR 的,修改 js 文件依然会全部代码重新打包,如果想让某个 js 文件支持 HMR,那么需要在入口文件中写上如下代码:

if (module.hot) {// 首先判断是否支持 HMR 功能,因为有些低版本浏览器是不支持的module.hot.accept('./common/utils/add.js'); // 接收 add.js,一旦 add.js 发生变化,就只加载这个文件// 由于开发项目这样写起来很麻烦,所以可以采用 loader 自动实现// 开发 vue 项目可以使用 vue-loader// 开发 react 项目可以使用 react-hot-loader
}

HMR 原理

webpack-dev-server 会创建两个服务:express 服务(提供静态资源) 和 socket 服务(服务器可以主动发送文件到客户端)。

  • express server 负责直接提供静态资源的服务:打包后的资源直接被浏览器请求和解析。
  • HMR Socket Server,是一个 socket 的长连接,建立连接后双方可以通信,服务器可以直接发送文件到客户端。而 http 请求必须要浏览器主动发起请求。

当服务器监听到对应的模块发生变化时,会生成两个文件。 manifest.json 文件记录更新的位置信息等配置信息,update chunk .js 文件记录实际更新的具体内容。通过长连接,可以直接将这两个文件主动发送给浏览器,浏览器拿到两个新的文件后,通过 HMR runtime 机制(webpack 在打包的时候提供),加载这两个文件,并且针对修改的模块进行更新。

oneOf

优化生产环境构建打包速度。

正常来讲,一种文件只能被一个 loader 处理。当一种文件要被多个 loader 处理,那么一定要指定 loader 执行的先后顺序:比如处理 js 时,先执行 eslint 再执行 babel

假如有 10loader,打包一种文件时就会轮询 10loader
如果用了oneOf,只要匹配到了这个loader就不会再往后面继续轮询。但是oneOf里面不能有两个配置处理同一种类型文件,相同的话需要抽出一个放在oneOf 外面,然后指定优先执行。

module.exports = {module: {rules: [{test: /\.(js|jsx)/,loader: 'eslint-loader',enforce: 'pre', // 指定优先执行},{oneOf: [// oneOf 里的 loader 只会执行一个{test: /\.(js|jsx)/,loader: 'babel-loader',exclude: /node_modules/},{test: /\.css/,use: ['style-css','css-loader']}]}]}
};

include / exclude

开发时我们需要使用第三方库或插件,所有文件都下载到 mode_modules 中了,而这些文件是不需要编译可以直接使用的。
所以我们在对 js 文件处理时,需要排查 node_modules 下面的文件。
include:包含,只处理 xxx 文件
exclude:排除,除了 xxx 文件以外的其他文件都要处理
includeexclude 只能写一个,要们包含,要么排除。

module.exports = {module: {rules: [{test: /\.js$/,loader: 'babel-loader',exclude: "mode_modules" // 默认值 mode_modules}]}
};
module.exports = {module: {rules: [{test: /\.js$/,loader: 'babel-loader',include: path.join(__dirname, 'src')}]}
};

cache

每次打包时 js 文件都要经过 eslint 检查和 babel 编译,速度比较慢。
我们可以缓存之前的 eslint 检查和 babel 编译结果,这样第二次打包时速度就会更快了。
cache - 会对 eslint 检查和 babel 编译结果进行缓存。

module.exports = {module: {rules: [{test: /\.js$/,use: 'babel-loader',include: path.resolve(__dirname, 'src'),options: {cacheDirectory: true, // 开启 babel 缓存,会默认缓存到 node_modules/.cache 目录下cacheCompression: false // 关闭缓存的压缩}},]},plugins: [new EslintPlugin({context: path.resolve(__dirname, 'src'), // 需要检测的目录extensions: ['js', 'jsx', 'json'], // 需要检查的文件类型fix: true, // 自动修复cache: true, // 开球 eslint 缓存cacheLocation: path.resolve(__dirname, '../node_modules/.cache/eslintcache') // 指定缓存的位置})]
};

Thread

当项目越来越大时,打包速度越来越慢。
我们想要继续提升打包速度,其实就是要提升 js 的打包速度,因为其他文件都比较少。
而对 js 文件的处理主要是 eslint、babel、Terser 三个工具,所以我们要提升他们的运行速度。我们可以开启多进程同时处理 js 文件,这样速度就比之前的单进程打包更快了。

  • 多进程打包:开启电脑的多个进程同时干一件事,速度更快。

  • 注意:仅在特别耗时的操作中使用,因为每个进程启动就大约有 600ms 左右的开销。

  • 启动进程的数量就是我们 cpu 的核数

// 由于每个电脑获取 cpu 核数方式都不一样
// 所以使用 nodejs 核心模块来直接使用
const os = require('os'); // 返回 cpu 的一些信息
const threads = os.cpus().length; // cpu 核数

安装 thread-loader

yarn add thread-loader
  • 一般在 babel-loader 进行打包的时候使用,因为处理语法转换很耗时。
  • 一般放在需要处理的那个 loader 之后调用。
const os = require('os');const threads = os.cpus().length;module.exports = {module: {rules: [{test: /\.js$/,exclude: /mode_modules/,use: [{loader: 'thread-loader',options: {workers: threads // 开启多进程和设置进程数量}},{loader: 'babel-loader',options: {cacheDirectory: true, // 开启 babel 缓存,会默认缓存到 node_modules/.cache 目录下cacheCompression: false // 关闭缓存的压缩}}]}]},plugins: [new EslintPlugin({context: path.resolve(__dirname, 'src'), // 需要检测的目录extensions: ['js', 'jsx', 'json'], // 需要检查的文件类型fix: true, // 自动修复cache: true, // 开球 eslint 环迅cacheLocation: path.resolve(__dirname, '../node_modules/.cache/eslintcache'), // 指定缓存的位置threads // 开启多进程和设置进程数量})],optimization: {minimize: true,minimizer: [new TerserWebpackPlugin({parallel: threads // 开启多进程和设置进程数量})]}
};

减少代码体积

Tree Shaking

开发时我们定义了一些工具函数库,或者引用第三方工具函数库或组件库。如果诶呦特殊处理的话,我们打包时会引入整个库,但是实际上我们可能只用上极小部分的功能,这样把真个库都打包进来,题久就太大了。

  • Tree Shaking:通常用于描述移除 js 中没有使用上的代码。注意:它依赖 es module
  • weboack 生产模式下已经默认开启了这个功能,无需其他配置。

减小 babel 体积

babel 为编译的每个文件都插入了辅助代码,使体积过大。
babel 对一些公共方法使用了非常小的辅助代码,比如 _extend。默认情况下会被添加到每一个需要它的文件中。你可以将这些辅助代码作为一个独立的模块,来避免重复引入。
@babel/plugin-transform-runtime:禁用了 babel 自动对每个文件的 runtime 注入,而是引入 @babel/plugin-transform-runtime 并且使所有辅助代码从这里引用。
安装 @babel/plugin-transform-runtime

yarn add  @babel/plugin-transform-runtime
module.exports = {module: {rules: [{test: /\.js$/,exclude: /mode_modules/,use: [{loader: 'babel-loader',options: {cacheDirectory: true, // 开启 babel 缓存,会默认缓存到 node_modules/.cache 目录下cacheCompression: false, // 关闭缓存的压缩plugins: ['@babel/plugin-transform-runtime'] // 较少代码体积}}]}]},
};
// 第二种配置
// .babelrc
{"presets": ["@babel/preset-env"],"plugins": ["@babel/plugin-transform-runtime"]
}

压缩图片

安装 image-minimizer-webpack-plugin、imagemin

yarn add image-minimizer-webpack-plugin imagemin -D

无损压缩:下载 imagemin-gifsicle、 imagemin-jpegtran、 imagemin-optipng、 imagemin-svgo

yarn add  imagemin-gifsicle imagemin-jpegtran imagemin-optipng imagemin-svgo -D

有损压缩:下载 imagemin-gifsicle、 imagemin-mozjpeg、 imagemin-pngquant、 imagemin-svgo

yarn add imagemin-gifsicle imagemin-mozjpeg imagemin-pngquant imagemin-svgo -D
module.exports = {optimization: {minimize: true,minimizer: [// 压缩图片new ImageMinimizerWebpackPlugin({minimizer: {implementation: ImageMinimizerWebpackPlugin.imageminGenerate,options: {plugins: [['gifsicle', { interlaced: true }],['jpegtran', { progressive: true }],['optipng', { optimizationLevel: 5 }],['svgo',{plugins: ['preset-default','prefixIds',{name: 'sortAttrs',params: {xmlnsOrder: 'alphabetical'}}]}]]}}})]}
};

优化代码运行性能

code split

打包代码时会将所有 js 文件打包到一个文件中,体积太大了。我们如果只要渲染首页,就应该只加载首页的 js 文件,其他文件不应该加载。
所以我们需要将打包生成的文件进行代码分割,生成多个 js 文件,渲染哪个页面就只加载某个 js 文件,这样加载的资源就少,速度就更快。

code split:主要做了两件事。

  • 分割文件:将打包生成的文件进行分割,生成多个 js 文件
  • 按需加载:需要哪个文件就加载哪个文件

多入口提取文件

正常情况下,有几个入口就会输出几个 bundle,如果 AB 都引用了模块 C,那么输出后的 bundleAbundleB,模块 C 就会分别打包两次。
那么我们可以将 C 单独输出成一个 bundleC,然后bundleAbundleB 去复用。

// add.js
const add = (a, b) => a + b;export default add;
// index.js
import add from '@utils/add';console.log(add(1, 6));
// other.js
import add from '@utils/add';console.log(add(1, 4));
const path = require('path');module.exports = {mode: 'production',entry: {app: './src/index.js',main: './src/other.js'},output: {path: path.resolve(__dirname, 'dist'),filename: '[name].js'},optimization: {splitChunks: {chunks: 'all', // 对所有模块都进行分割// 以下是默认值// minSize: 20000, // 分割代码最小的大小// minRemainingSize: 0, // 类似于 minSize 最后取保提取的文件大小不能为0// minChunks: 1, // 至少被引用的次数,满足条件才会代码分割// mazAsyncRequests: 30, // 按需加载时并行加载的文件的最大数量// maxInitialRequests: 30, // 入口 js 文件最大并行请求数量// enforceSizeThreshold: 50000, // 超过 50kb 一定会单独打包(此时会忽略 minRemainingSize、mazAsyncRequests、maxInitialRequests)// cacheGroups: { // 分组,哪些模块要打包到一个组//     defaultVendors: { // 组名//         test: /[\\/]node_modules[\\/]/, // 需要打包到一起的模块//         priority: -10, // 权重(越大越高)//         reuseExistingChunk: true // 如果当前 chunk 包含已从主 bundle 中拆分出的模块,则它将被重用,而不是生成新的模块//     },//     default: { // 其他没有写的配置会使用默认值//         minChunks: 2, // 这里的 minChunks 权重更大//         priority: -20,//         reuseExistingChunk: true//     }// },// 修改配置cacheGroups: {default: { // 其他没有写的配置会使用默认值minSize: 0, // 我们定义的文件体积太小了,所以要改打包的最小文件体积minChunks: 2, // 这里的 minChunks 权重更大priority: -20,reuseExistingChunk: true}}}}
};

在这里插入图片描述

多入口按需加载

实现按需加载,动态导入模块。

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>Title</title>
</head>
<body><h1 class="title">hello webpack5</h1><button id="btn">按钮</button>
</body>
</html>
// add.js
const add = (a, b) => a + b;export default add;
// count.js
export const count = (a, b) => a - b;
// index.js
import add from './add';console.log(add(1, 6));
// other.js
document.getElementById('btn').onclick = function () {// 动态引入 -> 实现按需加载// 即使只被引用了一次,也会代码分割import('./count.js').then(({ count }) => {alert(count(1, 4));});
};
const path = require('path'),HtmlWebpackPlugin = require('html-webpack-plugin');module.exports = {mode: 'production',entry: {app: './src/index.js',main: './src/other.js'},output: {path: path.resolve(__dirname, 'dist'),filename: '[name].js'},plugins: [new HtmlWebpackPlugin({template: path.resolve(__dirname, 'src/index.html'),filename: 'index.html'})],optimization: {splitChunks: {chunks: 'all', // 对所有模块都进行分割// 修改配置cacheGroups: {default: { // 其他没有写的配置会使用默认值minSize: 0, // 我们定义的文件体积太小了,所以要改打包的最小文件体积minChunks: 2, // 这里的 minChunks 权重更大priority: -20,reuseExistingChunk: true}}}}
};

单入口提取文件按需加载

// count.js
export const count = (a, b) => a - b;
// index.js
document.getElementById('btn').onclick = function () {// 动态引入 -> 实现按需加载// 即使只被引用了一次,也会代码分割import('./count.js').then(({ count }) => {alert(count(1, 4));});
};
const path = require('path'),HtmlWebpackPlugin = require('html-webpack-plugin');module.exports = {mode: 'production',entry: './src/index.js',output: {path: path.resolve(__dirname, 'dist'),filename: 'bundle.js'},plugins: [new HtmlWebpackPlugin({template: path.resolve(__dirname, 'src/index.html'),filename: 'index.html'})],optimization: {splitChunks: {chunks: 'all', // 对所有模块都进行分割}}
};
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>Title</title>
</head>
<body><h1 class="title">hello webpack5</h1><button id="btn">按钮</button>
</body>
</html>

给模块重命名

// index.js
document.getElementById('btn').onclick = function () {// 动态引入 -> 实现按需加载// 即使只被引用了一次,也会代码分割// 通过注释 webpackChunkName 命名import(/* webpackChunkName: 'math' */'./count.js').then(({ count }) => {alert(count(1, 4));});
};
  • chunkFilename 使用 [name] (对应 webpackChunkName
const path = require('path'),HtmlWebpackPlugin = require('html-webpack-plugin');module.exports = {mode: 'production',entry: './src/index.js',output: {path: path.resolve(__dirname, 'dist'),filename: 'bundle.[name].js',chunkFilename: 'chunk/[name].chunk.js', // 给打包输出的其他文件命名},plugins: [new HtmlWebpackPlugin({template: path.resolve(__dirname, 'src/index.html'),filename: 'index.html'})],optimization: {splitChunks: {chunks: 'all', // 对所有模块都进行分割}}
};

Preload / Prefetch

使用 code split 可以做代码分割,同时会使用 import 动态导入的语法来进行代码按需加载(懒加载)。但是加载速度还不够好,比如,用户是点击那个按钮才下载资源的,如果资源体积较大,那么用户会感觉到明显的卡顿效果。
我们想在浏览器空闲时间,加载后续需要使用的资源,就需要用上 PreloadPrefetch 技术。

  • Preload:告诉浏览器立即加载资源

  • Prefetch:告诉浏览器在空闲时才开始加载资源

  • Preload加载优先级高,Prefetch优先级低;

  • Preload只加载当前页面需要使用的资源,Prefetch可以加载当前页面资源,也可以下载一个页面需要使用的资源。

  • 都会加载资源,并不执行;都有缓存;兼容性比较差。

安装 @vue/preload-webpack-plugin

yarn add @vue/preload-webpack-plugin -D
const path = require('path'),HtmlWebpackPlugin = require('html-webpack-plugin'),PreloadPlugin = require('@vue/preload-webpack-plugin'); module.exports = {mode: 'production',entry: './src/index.js',output: {path: path.resolve(__dirname, 'dist'),filename: 'bundle.[name].js',chunkFilename: 'chunk/[name].chunk.js', // 给打包输出的其他文件命名},plugins: [new HtmlWebpackPlugin({template: path.resolve(__dirname, 'src/index.html'),filename: 'index.html'}),// new PreloadPlugin({//     rel: 'prefetch'// }),new PreloadPlugin({rel: 'preload',as: 'script'})],optimization: {splitChunks: {chunks: 'all', // 对所有模块都进行分割}}
};

文件资源缓存

当缓存的资源发生变化时,希望重新加载这个资源;当有一个文件发生变化,只让这一个文件的缓存失效,不影响其他文件缓存。

hash

  • 给打包好的文件名添加哈希值,哈希值不变就不会重新打包。

存在问题:因为 jscss 同时使用一个hash值,如果修改了js,重新打包,会导致所有缓存失效,css 也会重新打包。

// 给文件名添加hashmodule.exports = {output: { path: path.resolve(__dirname, '/build'),// 输出文件带 8 位哈希值,每次重新打包都会生成一个唯一的 hash 值filename: 'js/built.[hash:8].js'}
};

chunkhash

  • 根据chunk生成hash值,如果打包来源于同一个chunk,那么hash值一样。

存在问题:jscsshash值还是一样的。因为css是在js中被引入的,所以同属一个chunk

contenthash

  • contenthash 是根据文件的内容生成 hash 值,不同文件 hash 值一定不一样。

存在问题:如果 index.js 引入了 count.js,打包出来的 index.bundle.jscount.chunk.js,如果 修改了 count.js 的内容,那么 index.js 的缓存也会失效。

module.exports = {output: { path: path.resolve(__dirname, '/build'),// 输出文件带 10 位哈希值,每次重新打包都会生成一个唯一的 hash 值filename: 'js/built.[contenthash:10].js',chunkFilename: 'js/chunk/[name].[contenthash:8].chunk.js', // 给打包输出的其他文件命名}
};

runtime

使用 runtime 就可以避免 contenthash 的问题。

  • runtimeChunk:它会把文件之间依赖的 hash 值,单独提取出来打包成一个文件去保管。
  • A 引用 BB 发生变化,只有 Bruntime会发生变化,不会影响到 A
const path = require('path'),HtmlWebpackPlugin = require('html-webpack-plugin');module.exports = {mode: 'production',entry: './src/index.js',output: {path: path.resolve(__dirname, 'dist'),filename: '[name].[contenthash:8].bundle.js',chunkFilename: 'js/chunk/[name].[contenthash:8].chunk.js', // 给打包输出的其他文件命名clean: true},plugins: [new HtmlWebpackPlugin({template: path.resolve(__dirname, 'src/index.html'),filename: 'index.html'})],optimization: {splitChunks: {chunks: 'all', // 对所有模块都进行分割},// 把文件之间依赖的 `hash` 值,单独提取出来打包成一个文件去保管runtimeChunk: {name: entrypoint => `runtime-${entrypoint.name}.js`}}
};

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

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

相关文章

Spring第七天(AOP)

简介 AOP(Aspect Oriented Programing)面向切面编程&#xff0c;一种编程范式&#xff0c;指导开发者如何组织程序结构 作用 在不惊动原始设计的基础上为其进行功能增强 Spring理念&#xff1a;无入侵式/无侵入式 基本概念 连接点(JoinPoint) : 程序执行过程中的任意位置&a…

5分钟做自己的微信红包封面

文章目录 怎么制作自己的红包封面&#xff1f;开通红包封面的要求如下&#xff1a;收费情况制作具体网站&#xff1a;https://chatapi.onechat.fun/register?affYoU6 提交审核logo封面、挂件、气泡证明材料 发放红包封面其他 怎么制作自己的红包封面&#xff1f; 开通红包封面…

离了个大谱,只因外貌被换角?

♥ 为方便您进行讨论和分享&#xff0c;同时也为能带给您不一样的参与感。请您在阅读本文之前&#xff0c;点击一下“关注”&#xff0c;非常感谢您的支持&#xff01; 文 |猴哥聊娱乐 编 辑|徐 婷 校 对|侯欢庭 电视剧选角也能如此抓马&#xff0c;真是让人大开眼界。还没开…

开始学习Vue2(脚手架,组件化开发)

一、单页面应用程序 单页面应用程序&#xff08;英文名&#xff1a;Single Page Application&#xff09;简 称 SPA&#xff0c;顾名思义&#xff0c;指的是一个 Web 网站中只有唯一的 一个 HTML 页面&#xff0c;所有的功能与交互都在这唯一的一个页面内完成。 二、vue-cli …

mp4文件可以转成mp3音频吗

现在是个非常流行刷短视频一个年代&#xff0c;刷短视似乎成了人们休闲娱乐的一种方式&#xff0c;在日常刷短视频过程中&#xff0c;肯定会有很多同学被短视频 bgm 神曲洗脑&#xff0c;比如很多被网红翻唱带火的歌曲&#xff0c;例如其中"不负人间”&#xff0c;就是其中…

【GitHub项目推荐--12 年历史的 PDF 工具开源了】【转载】

最近在整理 PDF 的时候&#xff0c;有一些需求普通的 PDF 编辑器没办法满足&#xff0c;比如 PDF 批量合并、编辑等。 于是&#xff0c;我就去 GitHub 上看一看有没有现成的轮子&#xff0c;发现了这个 PDF 神器「PDF 补丁丁」&#xff0c;让人惊讶的是这个 PDF 神器有 12 年的…

安全基础~通用漏洞2

文章目录 知识补充盲注Boolean盲注延时盲注报错注入二次注入 知识补充 盲注常用 if(条件,5,0) #条件成立 返回5 反之 返回0 left(database(),1)&#xff0c;database() #left(a,b)从左侧截取a的前b位 盲注 盲注就是在注入过程中&#xff0c;获取的数据不能回显至前端页面。 …

HYBBS 表白墙网站PHP程序源码 可封装成APP

源码介绍 PHP表白墙网站源码&#xff0c;可以做校园内的&#xff0c;也可以做校区间的&#xff0c;可封装成APP。告别QQ空间的表白墙吧。 安装PHP5.6以上随意 上传程序安装&#xff0c;然后设置账号密码&#xff0c;登陆后台切换模板手机PC都要换开启插件访问前台。 安装完…

1. Matplotlib的Figure基础概念

1. Matplotlib的Figure基础概念 一 **角色和作用**二 **类比&#xff1a;**三 **基本使用示例** Matplotlib是一个用于绘制二维图形的Python库&#xff0c;广泛应用于数据可视化领域。其灵活性和强大的功能使得用户能够轻松创建各种类型的图表&#xff0c;包括折线图、散点图、…

Linux服务器系统修改SSH端口教程

修改端口号是通过修改SSH的配置文件实现的&#xff0c;在服务器终端先激活root用户&#xff0c;然后输入&#xff1a; vim /etc/ssh/sshd_config找到#Port 22这个位置 键盘按i进入编辑模式 删除掉Port 22前面的#&#xff0c;然后键盘按一下回车键&#xff08;如果没有#可不必…

k8s---helm

Helm是什么&#xff1f; 在没有helm之前。部署一个服务&#xff0c;需要deployment、service、ingress、挂在卷等等相关配置都需要人工来配置。 helm的作用就是通过打包的方式&#xff0c;把需要人工编写的配置集成在一起。是一键式的部署服务。类似于yum功能。 由官方提供的…

Web开发4:单元测试

在Web开发中&#xff0c;单元测试是一种重要的开发实践&#xff0c;它可以帮助我们确保代码的质量和可靠性。通过编写和运行单元测试&#xff0c;我们可以验证代码的正确性&#xff0c;减少错误和缺陷&#xff0c;并提高代码的可维护性。本文将介绍单元测试的概念、好处以及如何…

探索无限:山海鲸可视化搭建的地理展览馆的奇幻之旅

在浩瀚的数字世界中&#xff0c;你是否曾渴望探寻那未曾踏足的秘境&#xff0c;感受大自然的鬼斧神工&#xff0c;或是追溯那遥远的文明印记&#xff1f;现在&#xff0c;山海鲸可视化为你揭开了这一奇幻的探索之旅。 置身山海鲸的世界&#xff0c;就如同手握一把神奇的钥匙&am…

Java框架篇面试题

&#x1f4d5;作者简介&#xff1a; 过去日记&#xff0c;致力于Java、GoLang,Rust等多种编程语言&#xff0c;热爱技术&#xff0c;喜欢游戏的博主。 &#x1f4d7;本文收录于java面试题系列&#xff0c;大家有兴趣的可以看一看 &#x1f4d8;相关专栏Rust初阶教程、go语言基…

c语言小游戏之扫雷

目录 一&#xff1a;游戏设计理念及思路 二&#xff1a;初步规划的游戏界面 三&#xff1a;开始扫雷游戏的实现 注&#xff1a;1.创建三个文件&#xff0c;test.c用来测试整个游戏的运行&#xff0c;game.c用来实现扫雷游戏的主体&#xff0c;game.h用来函数声明和包含头文…

如何在飞书创建企业ChatGPT智能问答助手应用并实现公网远程访问(1)

文章目录 前言环境列表1.飞书设置2.克隆feishu-chatgpt项目3.配置config.yaml文件4.运行feishu-chatgpt项目5.安装cpolar内网穿透6.固定公网地址7.机器人权限配置8.创建版本9.创建测试企业10. 机器人测试 前言 在飞书中创建chatGPT机器人并且对话&#xff0c;在下面操作步骤中…

Java 数据结构集合

文章目录 Java 数据结构1. 基本数据结构1.1 数组 (Array)1.2 链表 (Linked List)1.3 栈 (Stack)1.4 队列 (Queue)双向队列优先级队列 2. 树形数据结构2.1 二叉树 (Binary Tree)2.2 堆 (Heap) 3. 散列数据结构3.1 哈希表 (Hash Map)3.2 LinkedHashMap3.3 TreeMapConcurrentHashM…

【python文件】生成的csv文件没两行数据之间有一个空行

问题描述 用python代码将数据写入csv文件&#xff0c;但生成的csv文件没两行数据之间有一个空行&#xff0c;如下图所示&#xff1a; 解决办法 在open函数中添加newline&#xff0c;如以下代码所示&#xff0c;即可解决这一问题。 with open(r"C:\Users\xxx\Desktop\DR…

Linux的奇妙冒险———vim的用法和本地配置

vim的用法和本地配置 一.vim的组成和功能。1.什么是vim2.vim的多种模式 二.文本编辑&#xff08;普通模式&#xff09;的快捷使用1.快速复制&#xff0c;粘贴&#xff0c;剪切。2.撤销&#xff0c;返回上一步操作3.光标的控制4.文本快捷变换5.批量化操作和注释 三.底行模式四.v…

【征服Redis12】redis的主从复制问题

从现在开始&#xff0c;我们来讨论redis集群的问题&#xff0c;在前面我们介绍了RDB和AOF两种同步机制&#xff0c;那你是否考虑过这两个机制有什么用呢&#xff1f;其中的一个重要作用就是为了集群同步设计的。 Redis是一个高性能的键值存储系统&#xff0c;广泛应用于Web应用…