前端工程化之自动化测试

自动化测试

  • 自动化测试
    • 为什么需要测试?
    • 什么时候需要考虑测试
    • 测试类型
    • 前端测试框架
    • 单元测试
    • Jest 重点掌握
      • 项目示例
        • package.json
        • src/utils/math.ts
        • src/utils/math.test.ts
        • 进行测试
        • jest.config.js
        • 覆盖率
          • 直观看覆盖率
            • coverage/lcov-report/index.html
        • src/main.test.ts
        • tsconfig.json
        • 生命周期逻辑
        • src/utils/math.test.ts
        • src/utils/math2.test.ts
        • 快照测试
        • src/hooks/useBoolean.ts
        • src/components/Button/index.tsx
        • src/components/Button/Button.test.tsx
        • coverage/lcov-report/index.html
    • Vitest 重点掌握
      • 项目示例
        • 文件内容
          • package.json
          • vite.config.js
          • plugins/myPlugin.js
          • src/components/hello/index.vue
          • src/components/hello/\_\_test\_\_/index.spec.js
          • test/basic.test.js
          • 启动测试
          • src/components/hello/\_\_test\_\_/index.spec.js
          • 按 u 更新快照:
          • test-ui
    • E2E测试 —— 端到端测试
      • puppeteer 框架处理
        • 安装
        • 项目示例
          • package.json
          • jest.config.js
          • .babelrc
          • .babelrc
          • 前端
            • public/login.html
            • public/index.html
          • 后端
            • server/index.js
          • puppeteer/index.js
          • puppeteer/screen.js
          • 启动服务

提示:自动化测试:写基础包的一定要进行自动化测试,为了让使用者使用的更放心,使用开源包也是看是否有自动化测试,还要看测试覆盖范围是否是比较齐全的,小厂的话不太关注,中大厂的话使用第三方的包是非常关注自动化测试的

自动化测试

为什么需要测试?

工作当中基本上是写业务代码,提交后将我们的功能提交给测试进行验证,为了保障整体的稳定性

  • 及时发现问题
  • 拆分模块的话,需要写单测
  • 提高代码质量

什么时候需要考虑测试

  • 公共库一类项目,一定要前端测试
  • 中长期项目的迭代,建议需要测试,单元测试,E2E测试
  • 重构场景,相关代码有了测试后,接手项目时候,可能并不了解业务逻辑,业务架构,重构项目会面临大的风险,但是有了单元测试,可以看到之前测试

测试类型

  1. 单元测试 unit test,小的功能模块进行测试
  2. E2E测试,端到端的测试,测试 UI+接口 相关内容,会将一些固定的流程,变成自动化任务,之后跑一下自动化任务,通过这种E2E测试在模拟设备上做一些模拟的交互,将流程固化,人工只需要看每个步骤的效果,截图等
  3. 集成测试,模块测试
  4. UI 测试 整体页面出发,看页面布局,看页面是否有空窗等,在布局维度,看是否与预期相符 => 使用场景:灰度发布,UI布局的验证,稳定性 => 是否白屏

前端测试框架

  • 测试框架:Jest、vitest(vite)、@vue/test-utils、mocha(少用)
  • 断言库:Assert
  • 工具:无头浏览器 puppeteer(node环境下的浏览器,使用chrome内核),可以读取对应文件,以及对文件进行爬取,获取当前文件结构,还有截屏功能

单元测试

通过确定的测试用例来编写测试代码,保证测试用例通过,以及测试覆盖率范围
判断测试用例的结果是true还是false,表明测试通过与否

例子:
test.js:

// test.js
const chalk = require('chalk')
// 测试原理部分的内容
// 一般测试框架(jest)需要做的事情// 描述测试场景
function describe(desc, fn) {console.log(chalk.green(desc))fn()
}// 单元用例测试描述
function it(desc, fn) {console.log(chalk.yellow(desc))fn()
}// 作比对,看是 true、还是 false,这个库其实在测试领域有一个专业术语叫:断言库
function expect(result) {return {toBe(actual) {if (result != actual) {console.log(chalk.red('FAIL', result, actual))throw new Error(`预期值和实际值不相等,预期${actual},实际${result}`)}console.log(chalk.green('PASS', result, actual))},toEqual(actual) {if (result !== actual) {throw new Error(`预期值和实际值不相等,预期${actual},实际${result}`)}},}
}module.exports = {describe,it,expect,
}

创建测试文件:
__index__.test.js

// 创建测试文件
// __index__.test.jsconst { describe, it, expect } = require('./test')// 定义 sum 函数
function sum(a, b) {return a + b
}// 测试用例
describe('用来测试相关内容', () => {it('测试 sum 函数', () => {expect(sum(1, 1)).toBe(2)})it('测试 sum 函数其他的场景', () => {expect(sum(1, 2)).toBe(4)})
})

Jest 重点掌握

项目示例

在这里插入图片描述

package.json
{"name": "react-test-demo","version": "1.0.0","description": "","main": "index.js","scripts": {"test": "jest"},"keywords": [],"license": "ISC","dependencies": {"react": "18.2.0","react-dom": "18.2.0"},"devDependencies": {"@types/react": "18.2.73","@babel/core": "7.23.2","@babel/preset-env": "7.23.2","@babel/preset-react": "7.22.15","@babel/preset-typescript": "7.23.2","babel-loader": "9.1.3","typescript": "5.2.2","webpack": "5.89.0","webpack-cli": "5.1.4","jest": "29.7.0","@jest/globals": "29.7.0","ts-jest": "29.1.2","react-test-renderer": "18.2.0"}
}
src/utils/math.ts
export const sum = (a: number, b: number) => a + b
export const subtract = (a: number, b: number) => a - b
export const multiply = (a: number, b: number) => a * b
export const divide = (a: number, b: number) => a / b
export const pow = (a: number, b: number) => a ** b
src/utils/math.test.ts
import { describe, expect, test } from '@jest/globals'describe('math test', () => {test('sum test', () => {expect(1 + 2).toBe(3)})
})
进行测试

npx ts-jest config:init

=> 生成 jest.config.js文件

/** @type {import('ts-jest').JestConfigWithTsJest} */
module.exports = {preset: 'ts-jest',testEnvironment: 'node',
};

pnpm run test

在这里插入图片描述

jest.config.js

增加配置:

/** @type {import('ts-jest').JestConfigWithTsJest} */
module.exports = {preset: 'ts-jest',testEnvironment: 'node',collectCoverage: true, //开启当前测试覆盖率collectCoverageFrom: ['src/**/*.{ts,tsx}'], //测试覆盖率的范围,tsx:需要进行快照测试
}

重新执行:

pnpm run test

=> 生成测试覆盖率的结果 以及 coverage 目录文件

在这里插入图片描述
在这里插入图片描述

覆盖率
  • stmts 语句覆盖率
  • branch 分支覆盖率
  • funcs 函数覆盖率
  • lines 行覆盖率
直观看覆盖率
coverage/lcov-report/index.html

页面从浏览器中打开
在这里插入图片描述
math.ts 点进去看:
在这里插入图片描述
由于main.ts中没有一个被引用,因此为 Statements 语句 为 0

src/main.test.ts

内容改了后:

import { describe, expect, test } from '@jest/globals'
import { sum } from './math'
describe('math test', () => {test('sum test', () => {expect(sum(1,2)).toBe(3)})
})

pnpm run test

执行结果:
在这里插入图片描述
点进去 main.ts,发现只有 sum 没标红
在这里插入图片描述

tsconfig.json
{"compilerOptions": {"jsx": "react", //react编译jsx"esModuleInterop": true //esm},"include": ["./src/**/*"], //包含文件"exclude": ["node_modules"] //不包含文件
}
生命周期逻辑
  • beforeAll 所有用例执行前调用
  • beforeEach 每个用例执行前
  • afterAll 所有测试完毕
  • afterEach 每个case end

断言钩子:

  • toBe 严格对比 => 内存空间一致
  • toEqual 值对比 => 对象递归遍历,对比值
const x = { a: { b: 3 } }
const y = { a: { b: 3 } }console.log(expect(x).toBe(y)) // fail
console.log(expect(x).toEqual(y)) // pass
src/utils/math.test.ts
import {afterAll,beforeAll,beforeEach,describe,expect,test,afterEach,
} from '@jest/globals'
import { divide, multiply, subtract, sum } from './math'describe('math test', () => {beforeAll(() => {console.log('beforeAll')})beforeEach(() => {console.log('beforeEach')})test('sum test', () => {expect(sum(1, 2)).toBe(3)})test('subtract test', () => {expect(subtract(2, 1)).toBe(1)})test('multiply test', () => {expect(multiply(2, 3)).toBe(6)})test('divide test', () => {expect(divide(6, 3)).toBe(2)})afterEach(() => {console.log('afterEach')})afterAll(() => {console.log('afterAll')})
})

pnpm run test

在这里插入图片描述
在这里插入图片描述

src/utils/math2.test.ts

借助AI工具生成测试用例:
在这里插入图片描述

import { expect, test } from '@jest/globals'
import { pow } from '../utils/math'test('Should return the correct result for positive integer powers', () => {expect(pow(2, 3)).toBe(8)expect(pow(3, 2)).toBe(9)expect(pow(5, 1)).toBe(5)
})
test('Should return 1 when the base and exponent are both 0', () => {expect(pow(0, 0)).toBe(1)
})
test('Should return NaN when the exponent is NaN', () => {expect(pow(2, NaN)).toBe(NaN)
})

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

快照测试

想要确保UI不会有意外变化的时候,快照是一个非常有用的工具
典型的是,在渲染UI组件后,会保存一个快照文件,主要是为了跟下次执行当前快照时候进行一个比对,看两者比对的结果,当前的case和快照的能否匹配上,匹配不上就是一个异常,当然也能更改快照测试的版本

src/hooks/useBoolean.ts
import { useState } from 'react'export const useBoolean = (initialValue: boolean) => {const [bol, setBol] = useState(initialValue)const toggle = () => setBol(!bol)return [bol, toggle] as const
}
src/components/Button/index.tsx
import React from 'react'
import { useBoolean } from '../../hooks/useBoolean'const bottonTypes = ['primary', 'secondary', 'tertiary'] as consttype ButtonType = (typeof bottonTypes)[number]interface ButtonProps {type: ButtonTypechildren: React.ReactNode
}export const Button: React.FC<ButtonProps> = ({type = 'primary',children,
}) => {const [isDisabled, toggle] = useBoolean(false)return (<buttondisabled={isDisabled}onClick={toggle}className={`button button--${type}`}>{children}</button>)
}
src/components/Button/Button.test.tsx
import renderer from 'react-test-renderer' //渲染当前组件
import { Button } from './index'
import React from 'react'
import { describe, it, expect } from '@jest/globals'describe('Button', () => {it('renders correctly', () => {const tree = renderer.create(<Button type="primary">Hello4</Button>).toJSON()expect(tree).toMatchSnapshot()})
})

pnpm run test

在这里插入图片描述

生成_snapshots_文件:
在这里插入图片描述
将 hello4 改为 hello 会报错:

import renderer from 'react-test-renderer'
import { Button } from './index'
import React from 'react'
import { describe, it, expect } from '@jest/globals'describe('Button', () => {it('renders correctly', () => {const tree = renderer.create(<Button type="primary">Hello</Button>).toJSON()expect(tree).toMatchSnapshot()})
})

在这里插入图片描述

更新快照:

npx jest --updateSnapshot

重新生成一个快照:
在这里插入图片描述
在这里插入图片描述

coverage/lcov-report/index.html

在这里插入图片描述
点击 utils:
在这里插入图片描述
点击 hooks:
在这里插入图片描述
点击 components/Button:
在这里插入图片描述

Vitest 重点掌握

vitest
一个原生支持 Vite 的测试框架

项目示例

在这里插入图片描述

文件内容
package.json
{"name": "my-vue3-app","private": true,"version": "1.0.20","type": "module","scripts": {"dev": "vite --debug","build": "vite build","preview": "vite preview",// 追加一些脚本"test": "vitest",  //使用Vitest进行测试"test:coverage": "vitest run --coverage", //开启测试覆盖率"test:ui": "vitest --ui" //使用UI的方式查看测试覆盖率的情况},"dependencies": {"@vitest/ui": "^3.0.9", //vitest --ui命令需要的依赖"vue": "^3.4.19"},"devDependencies": {"@vitejs/plugin-vue": "^5.0.4","@vitest/coverage-istanbul": "^3.0.8","@vitest/coverage-v8": "3.0.8", //跑测试用例"@vue/test-utils": "^2.4.6", //渲染当前组件的方法和工具类"happy-dom": "^17.4.2", //happy-dom和jsdom可以二选一,获取当前dom数据和结构,触发当前dom节点"jsdom": "^26.0.0","vite": "^5.1.4","vitest": "^3.0.8" //还需要安装vitest依赖}
}
vite.config.js

vitest 可以复用vite配置,因此在vite.config.js中需要添加test相关的配置项

/// <reference types="vitest" />import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import myVitePlugin from './plugins/myPlugin'// https://vitejs.dev/config/
export default defineConfig({plugins: [vue(), myVitePlugin()],// test相关的配置项test: {environment: 'jsdom', //jsdom/happy-dom模拟dom// 覆盖率coverage: {reporter: ['text', 'json', 'html'],// 设置覆盖文件夹reportsDirectory: './coverage',// 检查每个文件的阈值perFile: true,// 设置代码覆盖率阈值lines: 75,functions: 75,branches: 75,statements: 75,},},
})
plugins/myPlugin.js
import path from 'path'
import fs from 'fs'
// 控制台打印当前工程版本号
export default function myVitePlugin() {let version, configreturn {name: 'my-vite-plugin',configResolved(resolvedConfig) {config = resolvedConfigconst pkgPath = path.resolve(config.root, 'package.json')const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf-8'))version = pkg.version},buildStart() {console.log('当前工程版本号:1.0.0')},transform(code, id) {if (id.endsWith('main.js')) {const info = `console.log('当前工程版本号:${version}')`return `${code}\n${info}\n`}},}
}
src/components/hello/index.vue
<template><div>{{ count }} x {{ times }} = {{ result }}</div><button @click="times += 1">x1</button>
</template><script setup lang="ts">
import { computed, ref } from 'vue'const props = defineProps<{ count: number }>()const times = ref(2)
const result = computed(() => props.count * times.value)defineExpose(props)
</script>
src/components/hello/__test__/index.spec.js
// Hello/__test__/index.spec.ts
import { mount } from '@vue/test-utils'
import { describe, expect, test } from 'vitest'import Hello from '../index.vue'
describe('Hello', () => {test('挂载组件', async () => {// 断言当前组件存在expect(Hello).toBeTruthy()// 挂载当前组件内容const wrapper = mount(Hello, {props: {count: 4,},})// 获取组件的button触发点击事件,模拟用户点击await wrapper.get('button').trigger('click')// 点击后,组件的内容发生变化,断言的值也会发生变化 expect(wrapper.text()).toContain('4 x 3 = 12')// 再次点击await wrapper.get('button').trigger('click')// 断言组件再次内容变化expect(wrapper.text()).toContain('4 x 4 = 16')})// 对当前组件进行快照测试test('组件渲染快照', () => {const wrapper = mount(Hello, {props: {count: 5,},})expect(wrapper.html()).toMatchSnapshot()})
})
test/basic.test.js
import { assert, describe, expect, it } from 'vitest'describe('suite name', () => {it('foo', () => {assert.equal(Math.sqrt(4), 2)})it('bar', () => {expect(1 + 1).eq(2)})it('snapshot', () => {expect({ foo: 'bar' }).toMatchSnapshot()})
})
启动测试

pnpm run test

在这里插入图片描述

生成快照:
在这里插入图片描述

src/components/hello/__test__/index.spec.js

修改组件快照count的值

// Hello/__test__/index.spec.ts
import { mount } from '@vue/test-utils'
import { describe, expect, test } from 'vitest'import Hello from '../index.vue'
describe('Hello', () => {test('挂载组件', async () => {// 断言当前组件存在expect(Hello).toBeTruthy()// 挂载当前组件内容const wrapper = mount(Hello, {props: {count: 4,},})// 获取组件的button触发点击事件,模拟用户点击await wrapper.get('button').trigger('click')// 点击后,组件的内容发生变化,断言的值也会发生变化 expect(wrapper.text()).toContain('4 x 3 = 12')// 再次点击await wrapper.get('button').trigger('click')// 断言组件再次内容变化expect(wrapper.text()).toContain('4 x 4 = 16')})// 对当前组件进行快照测试test('组件渲染快照', () => {const wrapper = mount(Hello, {props: {count: 6, //修改组件快照count的值},})expect(wrapper.html()).toMatchSnapshot()})
})

报错:
在这里插入图片描述
在这里插入图片描述

按 u 更新快照:

在这里插入图片描述

test-ui

pnpm run test:ui

在这里插入图片描述

浏览器打开页面
在这里插入图片描述

在这里插入图片描述

E2E测试 —— 端到端测试

puppeteer 框架处理

  • 生成快照,图片/pdf 的形式进行存储
  • 爬取页面内容
  • 表单自动化提交
  • UI测试
  • 测试 chrome 扩展 (chrome内核就会让我们在node环境下去获取当前页面的内容,不会再去局限于浏览器环境下面)
  • 页面性能数据

重要的点

  • Browser 对应浏览器实例
  • Page,tab页面
  • ExecutionContext,js 的执行环境
  • ElementHandle 对应 dom 元素节点
  • jsHandler 对应dom中 js 对象
安装

pnpm install puppeteer

项目示例

在这里插入图片描述

package.json
{"name": "f2e-test","version": "1.0.0","description": "前端测试","main": "index.js","scripts": {"test": "jest --watchAll"},"keywords": [],"author": "","license": "ISC","devDependencies": {"@babel/core": "^7.20.5","@babel/preset-env": "^7.20.2","@babel/preset-typescript": "^7.18.6","@types/jest": "^29.2.4","body-parser": "^1.20.1","express": "^4.18.2","jest": "^29.3.1","puppeteer": "^19.4.1"}
}
jest.config.js
module.exports = {// 是否显示覆盖率报告collectCoverage: true,collectCoverageFrom: ['src/**/*'],coverageThreshold: {global: {statements: 90,functions: 90,branches: 90,},},
};
.babelrc
{"presets": ["@babel/preset-env", "@babel/preset-typescript"]
}
.babelrc
前端
public/login.html
<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8" /><meta name="viewport" content="width=device-width, initial-scale=1.0" /><title>login</title><style>div {text-align: center;}button {display: inline-block;line-height: 1;white-space: nowrap;cursor: pointer;text-align: center;box-sizing: border-box;outline: none;margin: 0;transition: 0.1s;font-weight: 500;padding: 12px 20px;font-size: 14px;border-radius: 4px;color: #fff;background-color: #409eff;border-color: #409eff;border: 0;}button:active {background: #3a8ee6;border-color: #3a8ee6;color: #fff;}input {display: block;margin: auto;margin-bottom: 10px;-webkit-appearance: none;background-color: #fff;background-image: none;border-radius: 4px;border: 1px solid #dcdfe6;box-sizing: border-box;color: #606266;font-size: inherit;height: 40px;line-height: 40px;outline: none;padding: 0 15px;transition: border-color 0.2s cubic-bezier(0.645, 0.045, 0.355, 1);}</style></head><body><div><input type="text" placeholder="请输入账号" class="account" /><input type="password" placeholder="请输入密码" class="password" /><button id="btn-login">登录</button></div><script src="https://cdn.bootcdn.net/ajax/libs/axios/0.21.0/axios.min.js"></script><script>document.querySelector('button').onclick = () => {axios.post('/login', {account: document.querySelector('.account').value,password: document.querySelector('.password').value,}).then((res) => {if (res.data.code == 0) {location.href = '/index.html';} else {alert(res.data.msg);}});};</script></body>
</html>
public/index.html
<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8" /><meta name="viewport" content="width=device-width, initial-scale=1.0" /><title>index</title></head><body>Hello World!</body>
</html>
后端
server/index.js
const bodyParser = require('body-parser');
const express = require('express');
const app = express();
const port = 8080;app.use(express.static('public'));
app.use(bodyParser.urlencoded({ extended: false }));
app.use(bodyParser.json());app.post('/login', (req, res) => {const { account, password } = req.body;// 由于没有注册功能,所以假定账号密码都为 adminif (account == 'admin' && password == 'admin') {res.send({msg: '登录成功',code: 0,});} else {res.send({msg: '登录失败,请输入正确的账号密码',code: 1,});}
});app.listen(port, () => {console.log(`Example app listening at http://localhost:${port}`);
});
puppeteer/index.js
const puppeteer = require('puppeteer');(async () => {// 无头浏览器,创建browser实例const browser = await puppeteer.launch({headless: false,});// 创建一个页面const page = await browser.newPage();// 访问页面await page.goto("http://localhost:8080/login.html");//   3. 初始页面截图await page.screenshot({path: `screenshot_${new Date().getTime()}.png`,});//   4. 获取dom元素 输入内容await page.type(".account", "admin");await page.type(".password", "admin");setTimeout(async () => {//   5.模拟点击操作const btnConfirm = await page.$("#btn-login");await Promise.all([btnConfirm.click(), page.waitForNavigation()]);//   6.最后截屏await page.screenshot({path: `screenshot_${new Date().getTime()}.png`,});}, 5000);//   elementhandle//// browser.close()
})()
puppeteer/screen.js
const puppeteer = require('puppeteer');
const sleep = (time) => {new Promise((resolve, reject) => {setTimeout(resolve, time);});
};(async () => {const browser = await puppeteer.launch({headless: false,});const page = await browser.newPage();await page.goto('https://baidu.com');//   await page.screenshot({//     path: `screenshot_${new Date().getTime()}.png`,//   });//   理解两个环境// node// page dom environment//   elementhandle jshandleconst input = await page.$('#form');await sleep(5000);await input.screenshot({path: `screenshot_${new Date().getTime()}.png`,});browser.close();
})();
启动服务

pnpm i

node server/index.js

node .\puppeteer\index.js

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

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

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

相关文章

分布式系统核心原理

CAP定理与权衡实践 CAP定理 一致性&#xff08;Consistency&#xff09; 强一致性&#xff1a;所有读写操作均基于最新数据&#xff08;如银行转账&#xff09;。 最终一致性&#xff1a;数据副本经过一段时间后达到一致&#xff08;如社交媒体的点赞数&#xff09;。 技术实现…

Step文件无法编辑怎么办?

Step文件无法编辑怎么办&#xff1f; 这里介绍两种方法&#xff0c; 1、 直接导入 准备step文件&#xff0c;solidworks导入后是这样&#xff0c;不能在上面直接编辑 图 1 点击右键&#xff0c;选择解除特征&#xff08;不同版本的可能不太一样&#xff0c;这里是solidworks2…

TIM_ITConfig() 和 TIM_Cmd()

在STM32的定时器中断配置中&#xff0c;TIM_ITConfig() 和 TIM_Cmd() 是两个关键函数&#xff0c;它们分别控制中断使能和定时器计数器的启停&#xff0c;作用层级不同。以下是详细解释&#xff1a; 1. TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE) 作用 启用定时器的特定中断…

TensorFlow 实现 Mixture Density Network (MDN) 的完整说明

本文档详细解释了一段使用 TensorFlow 构建和训练混合密度网络&#xff08;Mixture Density Network, MDN&#xff09;的代码&#xff0c;涵盖数据生成、模型构建、自定义损失函数与预测可视化等各个环节。 1. 导入库与设置超参数 import numpy as np import tensorflow as t…

数据结构实验7.2:二叉树的基本运算

文章目录 一&#xff0c;实验目的二&#xff0c;问题描述三&#xff0c;基本要求四&#xff0c;实验操作五&#xff0c;示例代码六&#xff0c;运行效果 一&#xff0c;实验目的 深入理解树与二叉树的基本概念&#xff0c;包括节点、度、层次、深度等&#xff0c;清晰区分二叉…

直线轴承常规分类知多少?

直线轴承的分类方式多样&#xff0c;以下是从材质、结构形状和常规系列三个维度进行的具体分类&#xff1a; 按主要材质分类 外壳材质&#xff1a;常见的有不锈钢&#xff0c;具有良好的耐腐蚀性&#xff0c;适用于一些对环境要求较高、易受腐蚀的工作场景&#xff1b;轴承…

websocket和SSE学习记录

websocket学习记录 websocket使用场景 即时聊天在线文档协同编辑实施地图位置 从开发角度来学习websocket开发 即使通信项目 通过node建立简单的后端接口,利用fs&#xff0c; path&#xff0c; express app.get(*, (req, res) > {const assetsType req.url.split(/)[…

CUDA编程中影响性能的小细节总结

一、内存访问优化 合并内存访问&#xff1a;确保相邻线程访问连续内存地址&#xff08;全局内存对齐访问&#xff09;。优先使用共享内存&#xff08;Shared Memory&#xff09;减少全局内存访问。避免共享内存的Bank Conflict&#xff08;例如&#xff0c;使用padding或调整访…

【双指针】对撞指针 快慢指针 移动零

文章目录 双指针介绍对撞指针快慢指针283. 移动零解题思路算法思路算法流程双指针介绍 ​ 算法中的双指针,并不一定是指我们平常在 c/c++ 使用的指针类型,更多时候其实是数组的下标等,因为它们也是有标识某个元素的功能,通常我们也就顺其自然地称其为 “指针” ! ​ 常见…

数据结构0基础学习堆

文章目录 简介公式建立堆函数解释 堆排序O(n logn)topk问题 简介 堆是一种重要的数据结构&#xff0c;是一种完全二叉树&#xff0c;&#xff08;二叉树的内容后面会出&#xff09;&#xff0c; 堆分为大小堆&#xff0c;大堆&#xff0c;左右结点都小于根节点&#xff0c;&am…

4.17--4.19刷题记录(贪心)

第一部分&#xff1a;准备工作 代码随想录中解释为&#xff1a;贪心的本质是选择每一阶段的局部最优&#xff0c;从而达到全局最优。 而我的理解为&#xff1a;贪心实质上是具有最优子结构的一种算法。所有的解都能由当前最优的解组成。 第二部分&#xff1a;开始刷题 &…

学习笔记十七——Rust 支持面向对象编程吗?

&#x1f9e0; Rust 支持面向对象编程吗&#xff1f; Rust 是一门多范式语言&#xff0c;主要以 安全、并发、函数式、系统级编程为核心目标&#xff0c;但它同时也支持面向对象的一些关键特性&#xff0c;比如&#xff1a; 特性传统 OOP&#xff08;如 Java/C&#xff09;Ru…

【Linux】43.网络基础(2.5)

文章目录 2.4 TCP/UDP对比2.4.1 用UDP实现可靠传输(经典面试题) 2.5 TCP 相关实验2.5.1 理解 listen 的第二个参数 2.4 TCP/UDP对比 我们说了TCP是可靠连接, 那么是不是TCP一定就优于UDP呢? TCP和UDP之间的优点和缺点, 不能简单, 绝对的进行比较TCP用于可靠传输的情况, 应用于…

three.js与webgl在buffer上的对应关系

一、three.js的类名 最近开始接触three.js 看到three.js中的一些类名和webgl的很相似 不自觉的就想对比一下 二、three.js中绘制4个点 // 创建点的几何体 const vertices new Float32Array([0.0, 0.0, 0.0, // 点11.0, 0.0, 0.0, // 点20.0, 1.0, 0.0, // 点30.…

DataWhale AI春训营 问题汇总

1.没用下载训练集导致出错&#xff0c;爆错如下。 这个时候需要去比赛官网下载对应的初赛训练集 unzip -d /mnt/workspace/sais_third_new_energy_baseline/data /mnt/workspace/sais_third_new_energy_baseline/初赛训练集.zip 在命令行执行这个命令解压 2.没定义测试集 te…

CANFD技术在新能源汽车通信网络中的应用与可靠性分析

一、引言 新能源汽车产业正处于快速发展阶段&#xff0c;其电子系统复杂度不断攀升&#xff0c;涵盖众多传感器、控制器与执行器。高效通信网络成为确保新能源汽车安全运行与智能功能实现的核心要素。传统CAN总线因带宽限制&#xff0c;难以满足高级驾驶辅助系统&#xff08;A…

Python字典深度解析:高效键值对数据管理指南

一、字典核心概念解析 1. 字典定义与特征 字典&#xff08;Dictionary&#xff09;是Python中​​基于哈希表实现​​的无序可变容器&#xff0c;通过键值对存储数据&#xff0c;具有以下核心特性&#xff1a; ​​键值对结构​​&#xff1a;{key: value}形式存储数据​​快…

C++中unique_lock和lock_guard区别

目录 1.自动锁定与解锁机制 2.灵活性 3.所有权转移 4.可与条件变量配合使用 5.性能开销 在 C 中&#xff0c;std::unique_lock 和 std::lock_guard 都属于标准库 <mutex> 中的互斥锁管理工具&#xff0c;用于简化互斥锁的使用并确保线程安全。但它们存在一些显著区别…

Nvidia显卡架构演进

1 简介 显示卡&#xff08;英语&#xff1a;Display Card&#xff09;简称显卡&#xff0c;也称图形卡&#xff08;Graphics Card&#xff09;&#xff0c;是个人电脑上以图形处理器&#xff08;GPU&#xff09;为核心的扩展卡&#xff0c;用途是提供中央处理器以外的微处理器帮…

下载electron 22.3.27 源码错误集锦

下载步骤同 electron源码下载及编译_electron源码编译-CSDN博客 问题1 从github 下载 dugite超时&#xff0c;原因没有找到 Validation failed. Expected 8ea2d0d3c9d9e4615069913207371ffe892dc10fb93975972f2f6e668f2e3b3a but got e3b0c44298fc1c149afbf4c8996fb92427ae41e…