Vue项目中env文件的作用和配置
在实际项目的开发中,我们一般会经历项目的开发阶段、测试阶段和最终上线阶段,每一个阶段对于项目代码的要求可能都不尽相同,那么我们如何能够游刃有余的在不同阶段下使我们的项目呈现不同的效果,使用不同的功能呢?这里就需要引入环境的概念。
一般一个项目都会有以下 3 种环境:
-
开发环境(开发阶段,本地开发版本,一般会使用一些调试工具或额外的辅助功能)
-
测试环境(测试阶段,上线前版本,除了一些 bug 的修复,基本不会和上线版本有很大差别)
-
生产环境(上线阶段,正式对外发布的版本,一般会进行优化,关掉错误报告)
作为一名开发人员,我们可能需要针对每一种环境编写一些不同的代码并且保证这些代码运行在正确的环境中,那么我们应该如何在代码中判断项目所处的环境同时执行不同的代码呢?这就需要我们进行正确的环境配置和管理。
1. 配置文件
正确的配置环境首先需要我们认识不同环境配置之间的关系,如图所示:
我们从上图中可以了解到每一个环境其实有其不同的配置,同时它们也存在着交集部分,交集便是它们都共有的配置项,那么在 Vue 中我们应该如何处理呢?
我们可以在根目录下创建以下形式的文件进行不同环境下变量的配置:
.env # 在所有的环境中被载入
.env.local # 在所有的环境中被载入,但会被 git 忽略
.env.[mode] # 只在指定的模式中被载入
.env.[mode].local # 只在指定的模式中被载入,但会被 git 忽略
比如我们创建一个名为 .env.stage 的文件,该文件表明其只在 stage 环境下被加载,在这个文件中,我们可以配置如下键值对的变量:
NODE_ENV=stage
VUE_APP_TITLE=stage mode
注意:环境变量的名称必须以
VUE_APP_
开头
这时候我们怎么在 vue.config.js 中访问这些变量呢?很简单,使用 process.env.[name]
进行访问就可以了,比如:
// vue.config.jsconsole.log(process.env.NODE_ENV); // development(在终端输出)
当你运行 yarn serve
命令后会发现输出的是 development,因为 vue-cli-service serve
命令默认设置的环境是 development,你需要修改 package.json 中的 serve 脚本的命令为:
"scripts": {"serve": "vue-cli-service serve --mode stage",
}
--mode stage
其实就是修改了 webpack 4 中的 mode 配置项为 stage,同时其会读取对应 .env.[model] 文件下的配置,如果没找到对应配置文件,其会使用默认环境 development,同样 vue-cli-service build
会使用默认环境 production。
这时候如果你再创建一个 .env 的文件,再次配置重复的变量,但是值不同,如:
NODE_ENV=stagingVUE_APP_TITLE=staging modeVUE_APP_NAME=project
因为 .env 文件会被所有环境加载,即公共配置,那么最终我们运行 vue-cli-service serve
打印出来的是哪个呢?答案是 stage,但是如果是 .env.stage.local 文件中配置成上方这样,答案便是 staging,所以 .env.[mode].local 会覆盖 .env.[mode] 下的相同配置。同理 .env.local 会覆盖 .env 下的相同配置。
由此可以得出结论,相同配置项的权重:
.env.[mode].local > .env.[mode] > .env.local > .env
如果看完上例还未理解优先级和覆盖规则的关系的话,这里有一个更容易理解的例子:
当你运行 vue-cli-service serve
命令时,Vue CLI 会根据你指定的模式(如 --mode stage
)加载不同的环境变量文件。以下是常见的环境变量文件及其优先级:
-
.env.local
: 该文件用于本地环境的配置,优先级最高,适用于所有模式。如果在该文件中定义了环境变量,它们将覆盖其他文件中的同名变量。 -
.env.[mode].local
: 这是特定于某个模式的本地配置文件,如.env.stage.local
。它的优先级仅次于.env.local
,并且可以覆盖.env.[mode]
中的相同变量。 -
.env.[mode]
: 这是特定于某个模式的环境变量文件,例如.env.stage
。它包含该模式下的公共配置。 -
.env
: 这是一个通用的环境变量文件,适用于所有模式。它的优先级最低。
实际效果
-
如果没有定义
.env.local
或.env.stage.local
:- 当你运行
vue-cli-service serve --mode stage
时,Vue CLI 会加载的环境变量是来自.env
,.env.stage
,结果将是.env.stage
中的变量。
- 当你运行
-
如果定义了
.env.stage.local
:- 如果你在
.env.stage.local
中定义了某些变量,这些变量将覆盖.env.stage
中的同名变量。例如,如果你在.env.stage.local
中定义VUE_APP_TITLE=Staging
,而在.env.stage
中定义了VUE_APP_TITLE=Stage App
,那么最终在应用中访问process.env.VUE_APP_TITLE
时,将会得到Staging
。
- 如果你在
示例
假设你的文件结构如下:
project-root/
│
├── .env
│ VUE_APP_TITLE=My Vue App
│
├── .env.stage
│ VUE_APP_TITLE=Stage App
│
├── .env.stage.local
│ VUE_APP_TITLE=Staging
执行命令
当你运行以下命令时:
npm run serve -- --mode stage
最终结果
process.env.VUE_APP_TITLE
的值将是Staging
,因为它是从.env.stage.local
加载的,并覆盖了.env.stage
和.env
文件中的同名变量。
需要注意的是,除了相同配置项权重大的覆盖小的,不同配置项它们会进行合并操作,类似于 Javascript 中的 Object.assign 的用法。
拓展1
1.为什么使用 VUE_APP_
前缀?
在 Vue.js 中,环境变量以 VUE_APP_
前缀开头是特别重要的。这是因为 Vue CLI 仅会将以 VUE_APP_
开头的环境变量暴露给客户端的 JavaScript 代码。它的主要目的是为了区分环境变量,使得这些变量能够被 Vue 应用程序访问,同时避免与其他环境变量的冲突
1. 明确标识
VUE_APP_
前缀清楚地标识这些变量是特定于 Vue 应用的环境变量。这有助于开发者快速识别哪些变量是可以在 Vue 组件和其他代码中直接使用的。
2. 隔离命名空间
- 在同一个项目中,可能会有多个不同的工具、库或模块,它们也可能定义环境变量。通过使用
VUE_APP_
前缀,Vue 应用的环境变量就被隔离在一个命名空间下,从而减少命名冲突的可能性。例如,其他工具可能使用NODE_ENV
、API_URL
等变量名,但由于 Vue 的环境变量使用了特定前缀,就能避免混淆。
3. 安全性
- 在 Vue CLI 中,只有以
VUE_APP_
前缀开头的环境变量会被嵌入到构建的应用中,其他的环境变量则不会被暴露到客户端。这意味着,如果你在环境变量中定义了敏感信息(比如数据库密码),只需确保这些变量不以VUE_APP_
开头,它们就不会被打包和暴露,从而提高了安全性。
4. 一致性
- 使用统一的前缀可以为团队开发提供一致的命名约定,有助于代码的可读性和可维护性。团队中的每个成员都知道哪些环境变量是可用的,以及它们的作用,从而减少了沟通成本。
5. 便于配置
- 在不同的环境(如开发、测试、生产环境)中,你可以使用相同的命名约定来配置不同的值。只需在不同的
.env
文件中设置相应的值(如.env.development
、.env.production
),即可在构建时自动加载正确的环境变量。
这里用一个例子说明:
在一个 Vue 应用中,你可能会看到以下环境变量的定义:
# .env.development
VUE_APP_TITLE=My Development App
VUE_APP_API_URL=https://dev.api.example.com# .env.production
VUE_APP_TITLE=My Production App
VUE_APP_API_URL=https://api.example.com
这些变量在 Vue 组件中可以直接使用,比如:
console.log(process.env.VUE_APP_TITLE); // 输出 "My Development App" 或 "My Production App"
console.log(process.env.VUE_APP_API_URL); // 输出相应环境下的 API URL
2.APP
后缀的作用
1. 明确变量的作用
APP
后缀清晰地表明变量与应用程序的配置相关。这使得其他开发者在查看变量名时,可以迅速理解该变量的目的和使用场景。例如:VUE_APP_TITLE
:应用的标题。VUE_APP_API_URL
:API 的基本 URL。VUE_DB_CONNECTION_STRING
:数据库连接字符串。
2. 减少命名冲突
- 在大型项目中,可能会有多个不同模块或组件,每个模块可能都有自己的环境变量。如果每个模块都遵循相似的命名规则,你就可以有效地避免变量名的冲突。例如:
- 模块 A:
VUE_APP_MODULE_A_SETTING
- 模块 B:
VUE_APP_MODULE_B_SETTING
- 模块 A:
3. 提高可维护性
- 维护代码时,开发者可以更容易地识别和理解环境变量的来源和用途。尤其在团队协作中,这种清晰性可以显著减少沟通成本。
4. 便于环境切换
- 在某些情况下,可能需要为开发、测试和生产环境设置不同的变量。通过统一的命名约定,可以更方便地管理这些不同的环境变量。例如,开发环境可以使用
VUE_APP_DEV_TITLE
,而生产环境则可以使用VUE_APP_PROD_TITLE
。
5. 文档化和标准化
- 在项目文档中,清晰的命名约定可以作为标准操作程序的一部分,帮助新加入的开发者快速上手。文档中可以列出所有环境变量及其用途,形成良好的开发规范。
6. 版本控制和部署
- 在持续集成和持续部署(CI/CD)过程中,使用
APP
后缀可以帮助开发者快速识别与应用相关的配置,确保在不同环境下正确使用相应的变量。例如,你可以在不同的环境配置文件中定义各自的APP
相关变量,以便于版本控制和自动化部署。
示例:
假设你正在开发一个 Vue 应用,其中需要配置多个环境变量,可以使用 APP
后缀来组织它们:
# 应用基本设置
VUE_APP_TITLE=My Awesome App
VUE_APP_VERSION=1.0.0# API 配置
VUE_APP_API_URL=https://api.example.com
VUE_APP_API_TIMEOUT=5000# 用户设置
VUE_APP_USER_DEFAULT_LANGUAGE=en
VUE_APP_USER_ENABLE_NOTIFICATIONS=true
2. 环境注入
通过上述配置文件的创建,我们成功使用命令行的形式对项目环境进行了设置并可以自由切换,但是需要注意的是我们在 Vue 的前端代码中打印出的 process.env
与 vue.config.js 中输出的可能是不一样的,这需要普及一个知识点:webpack 通过 DefinePlugin 内置插件将 process.env 注入到客户端代码中。
// webpack 配置
{...plugins: [new webpack.DefinePlugin({'process.env': {NODE_ENV: JSON.stringify(process.env.NODE_ENV)}}),],...
}
由于 vue-cli 3.x 封装的 webpack 配置中已经帮我们完成了这个功能,所以我们可以直接在客户端代码中打印出 process.env 的值,该对象可以包含多个键值对,也就是说可以注入多个值,但是经过 CLI 封装后仅支持注入环境配置文件中以 VUE_APP_
开头的变量,而 NODE_ENV
和 BASE_URL
这两个特殊变量除外。比如我们在权重最高的 .env.stage.local 文件中写入:
NODE_ENV=stage2
VUE_APP_TITLE=stage mode2
NAME=vue
然后我们尝试在 vue.config.js 中打印 process.env
,终端输出:
{...npm_config_ignore_scripts: '',npm_config_version_git_sign: '',npm_config_ignore_optional: '',npm_config_init_version: '1.0.0',npm_package_dependencies_vue_router: '^3.0.1',npm_config_version_tag_prefix: 'v',npm_node_execpath: '/usr/local/bin/node',NODE_ENV: 'stage2',VUE_APP_TITLE: 'stage mode2',NAME: 'vue',BABEL_ENV: 'development',...
}
可以看到输出内容除了我们环境配置中的变量外还包含了很多 npm 的信息,但是我们在入口文件 main.js 中打印会发现输出:
{"BASE_URL": "/vue/","NODE_ENV": "stage2","VUE_APP_TITLE": "stage mode2"
}
可见注入时过滤调了非 VUE_APP_
开头的变量,其中多出的 BASE_URL
为你在 vue.config.js 设置的值,默认为 /,其在环境配置文件中设置无效。
拓展2
DefinePlugin插件
DefinePlugin
是 Webpack 提供的一个插件,用于在编译时创建一个全局常量,并替换代码中的变量。这在处理环境变量和配置时非常有用,因为它允许你在客户端代码中使用不同的值,而这些值在编译时就已经确定了。
主要作用
- 定义全局常量:可以在代码中使用的全局变量,通过替换的方式来提供。
- 环境变量替换:常用于替换
process.env.NODE_ENV
,以便于在开发和生产环境中使用不同的配置。
基本用法
在 Webpack 配置文件中,你可以这样使用 DefinePlugin
:
const webpack = require('webpack');module.exports = {// 其他配置...plugins: [new webpack.DefinePlugin({'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV || 'development'),'API_URL': JSON.stringify('https://api.example.com'),// 可以定义更多的环境变量})]
};
详细说明
-
创建全局常量: 你可以通过
DefinePlugin
创建任何你想要的全局常量。例如,在上面的示例中,API_URL
被定义为一个字符串。在代码其他地方,你可以直接使用API_URL
,Webpack 会在编译时将其替换为对应的字符串。 -
字符串化: 注意当定义常量时,通常需要使用
JSON.stringify
来确保常量在代码中是一个有效的 JavaScript 字符串。如果直接使用字符串,可能会导致语法错误。 -
环境变量: 在许多项目中,
process.env.NODE_ENV
是一个常见的用法。在开发环境下,它通常设置为'development'
,而在生产环境下则为'production'
。通过这种方式,你可以在代码中进行条件判断,比如:if (process.env.NODE_ENV === 'production') {// 生产环境的代码 } else {// 开发环境的代码 }
-
与其他工具结合:
DefinePlugin
通常与其他工具(如 Babel、Vue CLI 等)结合使用,以便在构建过程中正确处理环境变量。
示例
假设你有以下代码:
javascript
if (process.env.NODE_ENV === 'production') {console.log('This is production mode');
} else {console.log('This is development mode');
}
在 Webpack 配置中使用 DefinePlugin
:
const webpack = require('webpack');module.exports = {plugins: [new webpack.DefinePlugin({'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV || 'development')})]
};
在构建应用时,如果 NODE_ENV
为 'production'
,Webpack 会将代码中的 process.env.NODE_ENV
替换为 'production'
,这样在运行时就只会执行生产环境的代码。
3. 额外配置
以上我们通过新建配置文件的方式为项目不同环境配置不同的变量值,能够实现项目基本的环境管理,但是 .env 这样的配置文件中的参数目前只支持静态值,无法使用动态参数,在某些情况下无法实现特定需求,这时候我们可以在根目录下新建 config 文件夹用于存放一些额外的配置文件。
/* 配置文件 index.js */// 公共变量
const com = {IP: JSON.stringify('xxx')
};module.exports = {// 开发环境变量dev: {env: {TYPE: JSON.stringify('dev'),...com}},// 生产环境变量build: {env: {TYPE: JSON.stringify('prod'),...com}}
}
上方代码我们把环境变量分为了公共变量、开发环境变量和生产环境变量,当然这些变量可能是动态的,比如用户的 ip 等。现在我们要在 vue.config.js 里注入这些变量,我们可以使用 chainWebpack 修改 DefinePlugin 中的值:
/* vue.config.js *///从 config.js 文件中导入不同环境配置
const configs = require('./config');// 引入 webpack-merge 库,用于合并不同的 Webpack 配置对象。这样可以灵活地组合不同的配置,而不丢失原有的配置。
const merge = require('webpack-merge');// 根据环境判断使用哪份配置
const cfg = process.env.NODE_ENV === 'production' ? configs.build.env : configs.dev.env;module.exports = {...chainWebpack: config => {//plugin('define')访问 Webpack 中 DefinePlugin 插件通过修改这个插件的配置,可以更改全局变量的值config.plugin('define').tap(args => {let name = 'process.env';// 使用 merge方法将 原有的process.env 和根据环境选择的 cfg 进行合并这样可以确保 在原有的环境变量基础上添加新的变量,而不覆盖原有的值。args[0][name] = merge(args[0][name], cfg);return args})},...
}
args[0][name] = merge(args[0][name], cfg);
args[0]
: 这个数组的第一个元素是传递给DefinePlugin
的配置对象。merge(args[0][name], cfg)
: 这里的merge
函数用于将当前的process.env
对象与cfg
进行合并。- 合并: 将
cfg
中的环境变量添加到现有的process.env
对象中,确保在编译时可以访问这些变量。
最后我们可以在客户端成功打印出包含动态配置的对象:
{"NODE_ENV": "stage2","VUE_APP_TITLE": "stage mode2","BASE_URL": "/vue/","TYPE": "dev","IP": "xxx"
}
4. 实际场景
结合以上环境变量的配置,我们项目中一般会遇到一些实际场景: 比如在非线上环境我们可以给自己的移动端项目开启 vConsole 调试,但是在线上环境肯定不需要开启这一功能,我们可以在入口文件中进行设置,代码如下:
/* main.js */import Vue from 'vue'
import App from './App.vue'
import router from './router'
import store from './store'Vue.config.productionTip = false// 如果是非正式环境,加载 VConsole
if (process.env.NODE_ENV !== 'production') {var VConsole = require('vconsole/dist/vconsole.min.js');var vConsole = new VConsole();
}new Vue({router,store,render: h => h(App)
}).$mount('#app')
vConsole 是一款用于移动网页的轻量级,可扩展的前端开发工具,可以看作是移动端浏览器的控制台。
另外我们还可以使用配置中的 BASE_URL 来设置路由的 base 参数:
/* router.js */import Vue from 'vue'
import Router from 'vue-router'
import Home from './views/Home.vue'
import About from './views/About.vue'Vue.use(Router)let base = `${process.env.BASE_URL}`; // 获取二级目录export default new Router({mode: 'history',base: base, // 设置 base 值routes: [{path: '/',name: 'home',component: Home},{path: '/about',name: 'about',component: About}]
})
每一个环境变量你都可以用于项目的一些地方,它提供给了我们一种全局的可访问形式,也是基于 Node 开发的特性所在。
拓展3
1.webpack 通过 DefinePlugin 内置插件将 process.env 注入到客户端代码中时,`process.env.NODE_ENV` 为什么要进行 JSON.stringify 处理?
原因分析
-
JavaScript 语法: 当你在 JavaScript 代码中使用字符串时,这些字符串必须被正确地包裹在引号中。例如,
"production"
或者"development"
必须是字符串类型。而DefinePlugin
允许我们在编译时定义全局常量,如果不使用JSON.stringify
,将会导致生成的代码不符合 JavaScript 语法。// 如果没有 JSON.stringify,可能会变成这样 process.env.NODE_ENV === production // 这里的 production 会被认为是一个变量// 正确的写法应该是 process.env.NODE_ENV === 'production' // 这里的 'production' 是一个字符串
-
确保类型一致性:
JSON.stringify
可以确保传递给DefinePlugin
的值在任何情况下都是字符串。即使process.env.NODE_ENV
的值为undefined
或者其他类型(如数字),JSON.stringify
也会将其转换为字符串格式,并避免潜在的类型错误。 -
防止意外的值: 如果你直接将某个变量(例如
process.env.NODE_ENV
)传递给DefinePlugin
,而这个变量在某些情况下可能是未定义的或不符合预期的类型,直接使用可能导致运行时错误或逻辑错误。通过JSON.stringify
,你可以确保一切都是严格的字符串,从而避免这些问题。
示例
下面是一个简单的示例来展示没有 JSON.stringify
的可能后果:
const webpack = require('webpack');module.exports = {plugins: [new webpack.DefinePlugin({'process.env.NODE_ENV': process.env.NODE_ENV || 'development' // 没有字符串化})]
};// 结果可能是:
if (process.env.NODE_ENV === production) {// 这里会报错,因为 production 没有被定义为字符串
}
而使用 JSON.stringify
的情况下:
const webpack = require('webpack');module.exports = {plugins: [new webpack.DefinePlugin({'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV || 'development') // 使用字符串化})]
};// 结果是:
if (process.env.NODE_ENV === 'production') {// 这里是正确的,逻辑不会出错
}
总结
使用 JSON.stringify
在 DefinePlugin
中确保 process.env.NODE_ENV
被定义为一个合法的字符串,从而避免了潜在的语法错误和运行时错误。这是一个好的实践,确保你在客户端代码中处理常量时的安全性和一致性。
2.如何在 package.json 中的 scripts 字段中定义一些自定义脚本来切换不同的环境?
假设你有一个 Node.js 项目,并且希望根据不同的环境(如开发、测试和生产)来执行不同的脚本。你可以在 package.json
中定义如下脚本:
{"name": "your-project","version": "1.0.0","scripts": {"start": "NODE_ENV=production node server.js","dev": "NODE_ENV=development nodemon server.js","test": "NODE_ENV=test mocha","build": "webpack --mode production","build:dev": "webpack --mode development"}
}
-
start
: 用于生产环境启动应用,将NODE_ENV
设置为production
。在 Unix 系统中,环境变量可以通过KEY=value command
的方式设置。 -
dev
: 用于开发环境启动应用,将NODE_ENV
设置为development
。这里使用了nodemon
,它会自动重启 Node.js 应用程序。 -
test
: 用于测试环境,运行测试框架(如 Mocha),将NODE_ENV
设置为test
。 -
build
和build:dev
: 分别用于构建生产和开发版本,使用 Webpack 进行打包。
Windows 兼容性
在 Windows 上,设置环境变量的方式与 Unix 系统不同。为了确保你的脚本在不同操作系统上都能正常工作,你可以使用 cross-env
包。首先,你需要安装 cross-env
:
npm install --save-dev cross-env
然后修改 package.json
中的脚本如下:
{"name": "your-project","version": "1.0.0","scripts": {"start": "cross-env NODE_ENV=production node server.js","dev": "cross-env NODE_ENV=development nodemon server.js","test": "cross-env NODE_ENV=test mocha","build": "webpack --mode production","build:dev": "webpack --mode development"}
}
运行脚本
可以使用以下命令来运行不同的脚本:
- 启动生产环境:
npm start
- 启动开发环境:
npm run dev
- 运行测试:
npm test
- 构建生产版本:
npm run build
- 构建开发版本:
npm run build:dev
相关文章:
Vue项目中env文件的作用和配置
在实际项目的开发中,我们一般会经历项目的开发阶段、测试阶段和最终上线阶段,每一个阶段对于项目代码的要求可能都不尽相同,那么我们如何能够游刃有余的在不同阶段下使我们的项目呈现不同的效果,使用不同的功能呢?这里…...
基于致远OA+慧集通平台的企业主数据管理设计方案(一)
目标 1、实现集团组织主数据的集中统一管理,包括到主数据在致远中的审批新增、编辑、分发等操作; 2、实现集团用户系统权限的集中管理,统一在致远平台中为用户配置各系统中的权限,配置完成后,可以自动或手动的分发到…...
vue前端实现同步发送请求,可设置并发数量【已封装】
新建 TaskManager.js export default class TaskManager {constructor(maxConcurrentTasks 1) {// 最大并发任务数// to do// 并发任务数大于1 接口开始有概率返回空值,推测是后端问题this.maxConcurrentTasks maxConcurrentTasks;this.currentTasks 0;this.tas…...
vue3使用vant日历组件(calendar),自定义日历下标的两种方法
在vue3中使用vant日历组件(calendar)自定义下标的两种方法,推荐使用第二种: 日期下方加小圆点: 一、使用伪元素样式实现(::after伪元素小圆点样式会被覆盖,只能添加一个小圆点) 代码如下(示例…...
Java线程池面试题
为什么要用线程池 降低资源消耗:通过重复利用已创建的线程降低线程创建和销毁造成的消耗提高响应速度:当任务到达时,任务可以不需要等到线程创建就能立即执行方便管理线程:线程是稀缺资源,如果无条件地创建࿰…...
我的 2024 年终总结
2024 年,我离开了待了两年的互联网公司,来到了一家聚焦教育机器人和激光切割机的公司,没错,是一家硬件公司,从未接触过的领域,但这还不是我今年最重要的里程碑事件 5 月份的时候,正式提出了离职…...
Mysql8 数据库安装及主从配置
一、MySQL8 安装 下载 MySQL 8 的安装包并将其上传到服务器。将安装包解压到指定的目录,例如 /opt/mysql8。创建一个名为 mysql 的用户组和一个名为 mysql 的用户,并将用户添加到组中。同时,设置用户密码并更改用户的主目录和默认 shell。配…...
Unity中UGUI的Button动态绑定引用问题
Unity中UGUI的Button动态绑定引用问题 问题代码修改代码如下总结 问题代码 Button动态绑定几个连续的按钮事件时使用for循环的i做按钮的id发现按钮点击对应不上。如下代码 for (int i 0; i < 10; i) {btn[i].onClick.AddListener(() >{Click(i);}); }/// <summary&…...
测试基础之测试分类
软件测试是确保软件产品满足预期功能、性能和用户体验要求的关键环节。它的主要目的是通过系统化的方法发现并修复软件中的缺陷,从而提高软件的质量和可靠性。在软件开发生命周期的不同阶段执行测试,以尽早发现潜在的错误或类型,早期发现缺陷…...
VS2022 中的 /MT /MTd /MD /MDd 选项
我们有时编译时,需要配置这个 运行库,指定C/C++运行时库的链接方式。 如下图 那么这些选项的含义是什么? /MT:静态链接多线程库 /MT选项代表“Multi-threaded Static”,即多线程静态库。选择此选项时,编译器会从运行时库中选择多线程静态连接库来解释程序中的代码,…...
socket.io
import { ref } from "vue" import io from "socket.io-client" import { getToken } from "./auth" const socket ref(null) const serverUri import.meta.env.VITE_APP_API_URL// 你的服务器地址 // const serverUri "http://172.16.3…...
latex常见问题汇总
文章目录 单行多图显示双栏插入图片 单行多图显示 \begin{figure}[t!] % case 1\centering\setlength{\tabcolsep}{0.5pt} % 图片之间的距离为0.5 point\begin{tabular}{ccc}\includegraphics[width0.30\linewidth, height0.33\linewidth]{pic/xuLun/thin.png} &\includeg…...
从数据到决策:如何利用多维度交叉分析提升企业整体效能
随着“GenAI”技术的崛起,数据分析在各行各业的应用也发生了深远的变化。IDC中国的调研数据显示,68%的企业在落地GenAI应用时认为,梳理和整合内部数据资产是首要任务;66%的企业则表示,搭建数据湖等数据底座是推动智能化…...
Nmap基础入门及常用命令汇总
Nmap基础入门 免责声明:本文单纯分享技术,请大家使用过程中遵守法律法规~ 介绍及安装 nmap是网络扫描和主机检测的工具。作为一个渗透测试人员,必不可少的就是获取信息。那么nmap就是我们从互联网上获取信息的途径,我们可以扫描互…...
【gopher的java学习笔记】spring web接口404了怎么办
今天新搭了一个spring boot带spring web的工程,不得不说,这java的生态是比golang要齐全一点,各种脚手架工程应有尽有。 因为我们的目标是有个web service,所以spring boot的工程搭好之后,就寻思着给这个spring应用添加…...
constexpr 的概念及用途
constexpr 的概念及用途 constexpr 是 C11 引入的关键字,用于定义常量表达式。常量表达式是指在编译时能够求值的表达式,也就是说,constexpr 用来标识那些编译器在编译时就可以计算结果的变量、函数或对象。 constexpr 在 C 中非常重要&…...
开放世界目标检测 Grounding DINO
开放世界目标检测 Grounding DINO flyfish Grounding DINO 是一种开创性的开放集对象检测器,它通过结合基于Transformer的检测器DINO与基于文本描述的预训练技术,实现了可以根据人类输入(如类别名称或指代表达)检测任意对象的功…...
【Spring】基于XML的Spring容器配置—— <import>标签的使用
Spring容器是Spring框架的核心部分,负责管理应用程序中的对象及其生命周期。Spring容器的配置方式有多种,其中基于XML的配置方式仍然被广泛使用,尤其是在一些老旧项目中。本文将详细介绍Spring容器配置中的<import>标签的使用ÿ…...
GemPy 3 地质建模快速入门指南
GemPy 3简介 GemPy 3是一款基于Python的开源三维结构地质建模软件。 GemPy 3由德国的Terranigma Solutions公司维护,并在GitHub上进行开源开发。它允许用户从界面和方向数据中自动创建复杂的地质模型,并支持随机建模以解决参数和模型不确定性问题。新版…...
智慧农业物联网传感器:开启农业新时代
在当今科技飞速发展的时代,农业领域正经历着一场前所未有的变革,而智慧农业物联网传感器无疑是这场变革中的关键利器。它宛如农业的 “智慧大脑”,悄然渗透到农业生产的各个环节,为传统农业注入了全新的活力,让农业生产…...
Spring Boot应用开发实战:从入门到精通
一、Spring Boot 简介 1.1 什么是 Spring Boot? Spring Boot 是一个开源框架,旨在简化新 Spring 应用的初始搭建以及开发过程。它构建在 Spring 框架之上,利用了 Spring 的核心特性,如依赖注入(Dependency Injection&…...
salesforce Controlled by Parent 的对象如何实现部分情况 Parent可见,但是 该对象不可见
在 Salesforce 中,设置对象的访问控制为“Controlled by Parent”时,该对象的可见性通常由其主对象(Parent)的共享规则或权限决定。如果主对象可见,子对象也会自动继承可见性。然而,有时候我们希望实现一些…...
React 第二十节 useRef 用途使用技巧注意事项详解
简述 useRef 用于操作不需要在视图上渲染的属性数据,用于访问真实的DOM节点,或者React组件的实例对象,允许直接操作DOM元素或者是组件; 写法 const inpRef useRef(params)参数: useRef(params),接收的 …...
TCP/IP 邮件
TCP/IP邮件是互联网通信中非常重要的应用之一。当我们发送电子邮件时,我们实际上并没有直接使用TCP/IP协议,而是通过电子邮件程序,例如微软的Outlook、莲花软件的Notes或Netscape Communicator等来实现。这些电子邮件程序背后使用了不同的TCP…...
前缀和与差分
目录 前缀和 一维前缀和 二维前缀和 差分 一维差分 二维差分 进阶练习NOIP普及组与提高组 前缀和 前缀和是一种思想,代码短小精悍是它的特点。相比于数据较大时的从头至尾遍历和优化过的双指针方法来求区间和,前缀和在对于数据进行处理的速度上有…...
2024国赛A问题5
问题五 龙头最大速度优化模型的建立 问题五在问题四的曲线的基础上对速度进行了约束,即在逐步改变龙头速度的情况下,各个龙身的速度也会依次改变,给出龙头的最大行进速度,使得舞龙队各把手的速度均不超过 2 m/s。即可依此构建一个龙头速度的…...
香橙派5Plus启动报错bug: spinlock bad magic on cpu#6, systemd-udevd/443
一、问题 如图: 接上调试串口,每次启动都会报错。不过使用过程中没有发现有什么影响。 百度查阅,有一位博主提到,但是没有细说解决方案: spinlock变量没有初始化_spinlock bad magic on-CSDN博客https://blog.csdn.n…...
MySQL 常用程序介绍
以下是一些常用的MySQL程序: 程序名作⽤mysqldMySQL的守护进程即 MySQL 服务器,要使⽤MySQL 服务器 mysqld必须正在运⾏状态mysql MySQL客⼾端程序,⽤于交互式输⼊ SQL 语句或以批处理模式从⽂件执⾏SQL的命令⾏⼯具 mysqlcheck⽤于检查、修…...
DevOps实战:用Kubernetes和Argo打造自动化CI/CD流程(1)
DevOps实战:用Kubernetes和Argo打造自动化CI/CD流程(1) 架构 架构图 本设计方案的目标是在一台阿里云ECS服务器上搭建一个轻量级的Kubernetes服务k3s节点,并基于Argo搭建一套完整的DevOps CI/CD服务平台,包括Argo CD…...
RBAC模型
RBAC模型 1.概念 RBAC:role based access control,基于角色的权限控制 三个主体 - 用户 - 角色 - 权限 授权的本质是对用户授权角色,假设系统的用户数量特别多的话可以对用户设置用户组。 2.RBAC表基本设计 用户表 角色表 权限表 …...
CultureLLM 与 CulturePark:增强大语言模型对多元文化的理解
本文介绍团队刚刚在加拿大温哥华召开的顶会NeurIPS 2024上发表的两篇系列工作:CultureLLM 和CulturePark。此项研究以生成文化数据并训练文化专有模型为主要手段,旨在提升已有基础模型的多文化理解能力,使得其在认知、偏见、价值观、在线教育…...
sentinel学习笔记6-限流降级(上)
本文属于sentinel学习笔记系列。网上看到吴就业老师的专栏,写的好值得推荐,我整理的有所删减,推荐看原文。 https://blog.csdn.net/baidu_28523317/category_10400605.html sentinel 实现限流降级、熔断降级、黑白名单限流降级、系统自适应…...
redis cluster集群
华子目录 什么是redis集群redis cluster的体系架构什么是数据sharding?什么是hash tag集群中删除或新增节点,数据如何迁移?redis集群如何使用gossip通信?定义meet信息ping消息pong消息fail消息(不是用gossip协议实现的࿰…...
设计模式从入门到精通之(二)抽象工厂模式
抽象工厂模式:不同工厂背后的协作秘密 在上一期中,我们聊到了工厂模式,讲述了如何用一家咖啡店来帮我们制作不同类型的咖啡。那么,如果你不仅需要咖啡,还需要配套的甜品,比如蛋糕或饼干,这时应该…...
LeetCode:404.左叶子之和
跟着carl学算法,本系列博客仅做个人记录,建议大家都去看carl本人的博客,写的真的很好的! 代码随想录 LeetCode:404.左叶子之和 给定二叉树的根节点 root ,返回所有左叶子之和。 示例 1: 输入: …...
Java包装类型的缓存
Java 基本数据类型的包装类型的大部分都用到了缓存机制来提升性能。 Byte,Short,Integer,Long 这 4 种包装类默认创建了数值 [-128,127] 的相应类型的缓存数据,Character 创建了数值在 [0,127] 范围的缓存数据,Boolean 直接返回 True or Fal…...
2024网络安全学习路线 非常详细 推荐学习
关键词:网络安全入门、渗透测试学习、零基础学安全、网络安全学习路线 首先咱们聊聊,学习网络安全方向通常会有哪些问题 1、打基础时间太长 学基础花费很长时间,光语言都有几门,有些人会倒在学习 linux 系统及命令的路上ÿ…...
【ES6复习笔记】数值扩展(16)
介绍 在 JavaScript 中,数值扩展提供了一些额外的功能,使得处理数值变得更加方便。本教程将介绍一些常用的数值扩展方法和属性。 1. Number.EPSILON Number.EPSILON 是 JavaScript 表示的最小精度。它的值接近于 2.2204460492503130808472633361816E-…...
【mybatis】详解 # 和 $ 的区别,两者分别适用于哪种场景,使用 $ 不当会造成什么影响
# 和 $ 的区别 在MyBatis中,# 和 $ 是用来处理参数的两种不同方式,它们之间有一些重要的区别: # 符号: # 是用来进行参数占位符的,它会进行 SQL 注入防护。使用 # 时,MyBatis 会将参数值进行预处理&…...
【MySQL】索引 面试题
文章目录 适合创建索引的情况创建索引的注意事项MySQL中不适合创建索引的情况索引失效的常见情况 索引定义与作用 索引是帮助MySQL高效获取数据的有序数据结构,通过维护特定查找算法的数据结构(如B树),以某种方式引用数据…...
QT调用Sqlite数据库
QT设计UI界面,后台访问数据库,实现数据库数据的增删改查。 零售商店系统 数据库表: 分别是顾客表,订单详情表,订单表,商品表 表内字段详情如下: 在QT的Pro文件中添加sql,然后添加头…...
Flutter富文本实现学习
Flutter 代码如何实现一个带有富文本显示和交互的页面。 前置知识点学习 RealRichText RealRichText 和 ImageSpan 不是 Flutter 框架中内置的组件,而是自定义的组件或来自第三方库。这些组件的实现可以提供比标准 RichText 更丰富的功能,比如在富文本…...
实现某海外大型车企(T)Cabin Wi-Fi 需求的概述 - 4
大家好,我是Q,邮箱:1042484520qq.com。 今天我们在上几讲的基础上再扩展下 Cabin Wi-Fi 的功能需求,讲讲如何使能 5G TCU Wi-Fi STA Bridge 模式。 参考: 实现某海外大型车企(T)Cabin Wi-Fi 需求…...
【GeekBand】C++设计模式笔记15_Proxy_代理模式
1. “接口隔离” 模式 在组件构建过程中,某些接口之间直接的依赖常常会带来很多问题,甚至根本无法实现。采用添加一层间接(稳定)接口,来隔离本来互相紧密关联的接口是一种常见的解决方案。典型模式 FacadeProxyAdapte…...
项目练习:若依系统的svg-icon功能实现
文章目录 一、svg图片准备二、自定义Svg组件三、svg插件开发四、Svg组件使用 一、svg图片准备 src/assets/icons/svg 其中svg目录里,存放了所需要的图片 index.js import Vue from vue import SvgIcon from /components/SvgIcon// svg component// register glob…...
【踩坑/Linux】Vmware中的Ubuntu虚拟机无法访问互联网
Vmware中的Ubuntu虚拟机无法访问互联网 首先前提是我的系统是Ubuntu 16.04系统,vmware workstation选择的是NAT模式,虚拟机内连不上网络 ping www.baidu.com ping: unknown host www.baidu.com首先检查 DNS 解析服务:在虚拟机中打开命令提示…...
深入了解SpringIoc(续篇)
目录 注入 Bean 的方式有哪些? 构造函数注入还是 Setter 注入? Bean 的作用域有哪些? Bean 是线程安全的吗? Bean 的生命周期了解么? 注入 Bean 的方式有哪些? 依赖注入 (Dependency Injection, DI) 的常见方式: 构造函数注入:通过类的构造函…...
嵌入式硬件面试题
1、请问什么是通孔、盲孔和埋孔?孔径多大可以做机械孔,孔径多小必须做激光孔?请问激光微型孔可以直接打在元件焊盘上吗,为什么? 通孔是贯穿整个PCB的过孔,盲孔是从PCB表层连接到内层的过孔,埋孔…...
散斑/横向剪切/迈克尔逊/干涉条纹仿真技术分析
摘要 本博文提供了多种数据类型的干涉条纹仿真,并展示了它们对应的散斑干涉条纹。还分别给出了横向剪切干涉以及剪切散斑干涉条纹的仿真。 一、迈克尔逊干涉与散斑干涉仿真 下图为干涉条纹与对应的散斑干涉条纹的仿真示意图。其中,干涉条纹可认为是源…...
ViiTor实时翻译 2.2.1 | 完全免费的高识别率同声传译软件
ViiTor实时翻译是一款完全免费的实时语音翻译和同声传译软件,支持即时翻译、对话翻译、语音转文字、文本语音合成和AR翻译等功能。它的语音识别和翻译准确率特别高,速度非常快,音质高,并支持6种音色切换。该软件简单易用ÿ…...