入坑 Node.js 1

news2024/12/23 8:51:22

原文:https://blog.iyatt.com/?p=14717

前言

前面刚刚对 Spring Boot 有了个概念,再来学学 Node.js,顺便当学 JavaScript,为后面入前端做准备。

环境

Node.js 20.12.2

官方 API 文档:https://nodejs.org/docs/latest/api/
CommonJS:https://nodejs.org/api/modules.html
ECMAScript Modules:https://nodejs.org/api/modules.html

模块导入方式

分为 CommonJS(CJS)和 ECMAScript Modules(ESM)。

CJS 使用 require 导入,使用 modules.export 或 exports 导出。ESM 使用 import 导入,使用 export 导出。

CJS 在运行时加载模块,导入和导出是同步的。ESM 是静态加载,在代码解析的时候进行,导入和导出操作是异步的。

扩展名使用 .js 时默认识别为 CJS,扩展名使用 .mjs 时默认识别为 ESM。Node.js 的早期版本只支持 CJS,后面的开始支持 ESM,新项目可以直接使用 ES。

本篇实践以 ESM 进行,下面展示两种方式的对比。
下面的例子会在 8080 端口创建一个 http 服务,展示字符串“Hello World!”,可以通过浏览器访问:http://localhost:8080

CJS

const http = require('node:http');

const hostname = '127.0.0.1';
const port = 8080;

const server = http.createServer((req, res) => {
  res.statusCode = 200;
  res.setHeader('Content-Type', 'text/plain');
  res.end('Hello World!\n');
});

server.listen(port, hostname, () => {
  console.log(`Server running at http://${hostname}:${port}/`);
});

file

ESM
导入指定模块

import { createServer } from 'node:http';

const hostname = '127.0.0.1';
const port = 8080;

const server = createServer((req, res) => {
  res.statusCode = 200;
  res.setHeader('Content-Type', 'text/plain');
  res.end('Hello World!\n');
});

server.listen(port, hostname, () => {
  console.log(`Server running at http://${hostname}:${port}/`);
});

变量修饰

变量修饰有三种:var、let 和 const
const 和一般编程语言里一样,表示常量,声明时必须初始化,且不可再次赋值,具有块级作用域。如果 const 修饰的是一个对象或数组,虽然不能更换变量索引的对象或数组,但是可以修改对象属性或数组元素。
let 相当于一般编程语言里的局部变量,声明时可以初始化也可以不初始化,后期可以再次赋值,也是块级作用域,比如在大括号内声明的只能在大括号内访问。
var 与另外两种不同,如果在函数外声明,则为全局变量,整个程序中都可以访问。在函数内声明,则仅在函数内可访问。还可以多次声明,后续声明覆盖前面的声明。

同步与异步

Node.js 提供的很多函数都分为同步和异步两个版本,同步版函数通常名字多一个 Sync。
同步可以这样理解:你要泡茶,得先烧水,在烧水得过程中就在旁边等着,直到水烧开了,才倒水泡茶。
异步:同样泡茶,开始烧水,但是你不在旁边等着,跑去看电视了,等水烧好了,再回来倒水泡茶。
异步执行的时候,如果一个操作会花一些实践,那么就不会干等着,会去先执行别的任务。如果是同步就会等着完成一件再做另外一件。从性能来说,异步的性能更高,不会让计算机闲着,但是现实不是总能异步的,如果后续的操作都依赖前面的工作结果,就必须采用同步,等待完成后得到结果才能执行别的任务。应用中根据实际需要来决定使用同步还是异步。
下面用写文件来展示同步和异步

异步
从执行结果可以看到,使用异步写文件,写文件这个操作会花费“较多”时间,但是主线程不会等着它完成,而是先去执行后面的打印“hello world”,在打印这个操作完成以后,写文件的动作才完成。

import { writeFile } from 'node:fs';

writeFile('test.txt', 'hello world', (err) =>
{
    if (err)
    {
        console.error(err);
        return;
    }
    console.log('写入成功');
});

console.log('hello world');

file

同步
同步写文件,在执行写文件的时候就会阻塞主线程,直到完成以后才能继续往下执行。

import { writeFileSync } from 'node:fs';

try
{
    writeFileSync('test.txt', 'hello world');
    console.log('写入成功');
}
catch (err)
{
    console.error(err);
    process.exit(1);
}

console.log('hello world');

file

文件操作 fs

上面同步与异步举例使用的写文件操作,这里就略过了。

换行符

在不同的操作系统中,默认的换行符是不一样的。
Windows:\r\n(回车符+换行符)
Unix/Linux/macOS:\n(换行符),其中早期的 macOS 采用的换行符是 \r(回车符)
要保证良好的跨平台性,就不要指定某一种,但是自己写每种情况又显得多余,因为 Node.js 提供了换行符。像下面这样导入 EOL就行,这是一个换行符字符串。

import { EOL from 'os';

追加文件

专用文件追加函数

import { writeFileSync, appendFileSync, appendFile } from 'fs';
import { EOL } from 'os';

try
{
    writeFileSync('test.txt', 'hello world' + EOL); // 写入文件
    appendFileSync('test.txt', 'hello Node.js' + EOL); // 同步追加文件
}
catch (err)
{
    console.error(err);
    process.exit(1);
}

appendFile('test.txt', 'hello hello' + EOL, err => // 异步追加文件
{
    if (err)
    {
        console.error(err);
        return;
    }
    console.log('写入成功');
});

写文件追加模式

import { writeFileSync } from 'fs';
import { EOL } from 'os';

try
{
    writeFileSync('test.txt', 'hello world' + EOL); // 写入文件
    writeFileSync('test.txt', 'hello Node.js' + EOL, { flag: 'a' }); // 追加文件
    console.log('写入成功');
}
catch
{
    console.error(err);
    process.exit(1);
}

流式写文件

类似一般编程语言里的打开文件操作,打开后会创建一个操作文件的句柄,通过句柄来读写文件,最后关闭句柄。

import { createWriteStream } from 'fs';
import { EOL } from 'os';

const ws = createWriteStream('test.txt');

ws.on('finish', () => // 监听写入完成事件
{
    console.log('写入文件成功');
});

ws.on('error', (err) => // 监听写入错误事件
{
    console.error('写入文件失败:', err);
    return;
});

// 写入文件
ws.write('hello' + EOL);
ws.write('world' + EOL);

// 结束写入
ws.end();

读文件

import { readFileSync, readFile } from 'node:fs';

// 同步
try
{
    const data = readFileSync('test.txt');
    console.log(data.toString());
}
catch(err)
{
    console.error(err);
}

// 异步
readFile('test.txt', (err, data) =>
{
    if (err)
    {
        console.error(err);
        process.exit(1);
    }
    console.log(data.toString());
});

流式读文件

按缓存大小读取

import { createReadStream } from 'node:fs';

var rs = createReadStream('test.txt');

rs.on('data', (data) =>
{
    console.log(data.toString());
});

rs.on('error', (error) =>
{
    console.log(error);
});

rs.on('end', () =>
{
    console.log('(读取完成)');
});

按行读取

import { createReadStream } from 'node:fs';
import { createInterface } from 'node:readline';

var rs = createReadStream('test.txt');
const rl = createInterface(
{
    input: rs,
    crlfDelay: Infinity
});

rl.on('line', (line) => {
    console.log(line);
});

rl.on('error', (error) => {
    console.log(error);
});

rl.on('close', () => {
    console.log('(读取完成)');
});

复制文件

使用一个 69M 的视频文件测试

一次性复制

import { readFileSync, writeFileSync } from 'node:fs';

try
{
    const data = readFileSync('test1.mp4');
    writeFileSync('test2.mp4', data);
}
catch(error)
{
    console.error(error);
}

console.log(process.memoryUsage().rss / 1024 / 1024);

使用内存 106M
file

流式复制

import { createReadStream, createWriteStream } from 'node:fs';

const rs = createReadStream('test1.mp4');
const ws = createWriteStream('test2.mp4');

rs.on('data', (chunk) => {
   ws.write(chunk); 
});
// 也可以使用管道
// rs.pipe(ws);

rs.on('error', (err) => {
    console.errot(err);
});

console.log(process.memoryUsage().rss / 1024 / 1024);

使用内存 36M
在读写的文件较大时,使用流式读写会比较节省内存,默认缓冲区大小为 64KB,一次性最多读入 64KB 到内存,等取出后才能继续读取。
file

其它文件操作

如重命名文件/移动文件,创建文件夹,删除文件夹,查看文件信息等等,参考文档:https://nodejs.org/api/fs.html

路径 path

在 CJS 中可以直接使用 __dirname__filename 获取文件所在目录和文件自身路径的,但是 ESM 中不可用。

CJS

console.log(__dirname);
console.log(__filename);

file

ESM
获取目录和路径的实现参考

import { fileURLToPath } from 'url';
import { dirname, resolve } from 'path';

const __filename = fileURLToPath(import.meta.url);
var __dirname = dirname(__filename); // 方法一:已知文件全名的情况下
var __dirname = resolve(); // 方法二:未知文件全名的情况下

console.log(__dirname);
console.log(__filename);

file

路径拼接

在不同的操作系统下路径连接符号不同,在 Windows 下是反斜杠,在 Linux 下是斜杠。通过 Node.js 的路径拼接函数就能根据所在平台进行处理,保证跨平台性。
获取操作系统路径分割符

import { sep } from 'node:path';

console.log(sep);

file

拼接路径

import { resolve } from 'node:path';

const path1 = resolve('D:', 'hello', 'world', 'test.txt');
console.log(path1);

const path2 = resolve('hello', 'world', 'test.txt');
console.log(path2);

file

路径解析

import { parse, resolve } from 'node:path';

const path = resolve('index.mjs');
const parseObject = parse(path);
console.log(parseObject);

返回结果是一个对象,包含了根目录,目录,文件名,扩展名,纯文件名
file

其它函数

文档:https://nodejs.org/api/path.html

Web 服务 http

简单的 web 服务器

import { createServer } from 'node:http';

const server = createServer((req, res) =>
{
    res.setHeader('Content-Type', 'text/html;charset=UTF-8');
    res.end('你好,世界!');
})

const port = 80;
server.listen(port, () =>
{
    console.log(`服务器运行在 http://localhost:${port}/`);
});

file

获取请求

import { createServer } from 'node:http';
import { parse } from 'node:url';

const server = createServer((req, res) =>
{
    console.log('-'.repeat(100));
    console.log('请求 URL:' + req.url);
    console.log('请求方法:' + req.method);
    console.log('http 版本:' + req.httpVersion);
    console.log('请求头:' + JSON.stringify(req.headers));
    console.log(parse(req.url, true));
    console.log('-'.repeat(100));

    // 回复客户端
    res.setHeader('Content-Type', 'text/html;charset=UTF-8');
    res.end('你好,世界!');
});

const port = 80;
server.listen(port, () =>
{
    console.log(`服务器运行在 http://localhost:${port}/`);
});

访问:http://localhost/submit?s1=123&s2=abc
file
服务器端获取
file

另外一种解析方式

import { createServer } from 'node:http';

const server = createServer((req, res) =>
{
    console.log('-'.repeat(100));
    console.log('请求 URL:' + req.url);
    console.log('请求方法:' + req.method);
    console.log('http 版本:' + req.httpVersion);
    console.log('请求头:' + JSON.stringify(req.headers));
    let url = new URL(req.url, `http://${req.headers.host}`);
    console.log('pathname: ' + url.pathname);
    console.log('search: ' + url.search);
    console.log('searchParams: ' + url.searchParams);
    console.log(url.searchParams.get('s1') + ' ' + url.searchParams.get('s2'));
    console.log('-'.repeat(100));

    // 回复客户端
    res.setHeader('Content-Type', 'text/html;charset=UTF-8');
    res.end('你好,世界!');
})

const port = 80;
server.listen(port, () =>
{
    console.log(`服务器运行在 http://localhost:${port}/`);
});

file

应用

请求

import { createServer } from 'node:http';

const server = createServer((req, res) => 
{
    let { method } = req;
    let { pathname } = new URL(req.url, `http://${req.headers.host}`);
    
    res.setHeader('Content-Type', 'text/html; charset=utf-8');
    console.log(method, pathname);
    if (method === 'GET' && pathname === '/login')
    {
        res.end('登录页面');
    }
    else if (method === 'GET' && pathname === '/register')
    {
        res.end('注册页面');
    }
    else
    {
        res.statusCode = 404;
        res.end('Not Found');
    }
});

const port = 80;
server.listen(port, () =>
{
    console.log(`服务器运行在 http://localhost:${port}/`);
});

file
file
file

响应

加载 html 文件作为响应内容

index.mjs

import { createServer } from 'node:http';
import { readFileSync } from 'node:fs';

const server = createServer((req, res) => 
{
    let data = readFileSync('index.html');
    res.setHeader('Content-Type', 'text/html; charset=utf-8');
    res.end(data);
});

const port = 80;
server.listen(port, () =>
{
    console.log(`服务器运行在 http://localhost:${port}/`);
});

index.html

<!DOCTYPE html>
<html lang="zh">
    <head>
        <meta charset="UTF-8">
        <title>表格</title>
        <style>
            td{
                padding: 20px 40px;
            }
            table tr:nth-child(odd){
                background-color: #f11212;
            }
            table tr:nth-child(even){
                background-color: #5b0af1;
            }
            table, td{
                border-collapse: collapse;
            }
        </style>
    </head>
    <body>
        <table border="1">
            <tr><td>1</td><td>2</td><td>3</td></tr>
            <tr><td>4</td><td>5</td><td>6</td></tr>
            <tr><td>7</td><td>8</td><td>9</td></tr>
            <tr><td>10</td><td>11</td><td>12</td></tr>
        </table>
        <script>
            let tds = document.querySelectorAll('td');
            tds.forEach(item => {
                item.onclick = function(){
                    item.style.backgroundColor = '#000000';
                }
            })
        </script>
    </body>
</html>

点击单元格变色
file

html、css、js 拆分

index.html

<!DOCTYPE html>
<html lang="zh">
    <head>
        <meta charset="UTF-8">
        <title>表格</title>
        <link rel="stylesheet" href="index.css">
    </head>
    <body>
        <table border="1">
            <tr><td>1</td><td>2</td><td>3</td></tr>
            <tr><td>4</td><td>5</td><td>6</td></tr>
            <tr><td>7</td><td>8</td><td>9</td></tr>
            <tr><td>10</td><td>11</td><td>12</td></tr>
        </table>
        <script src="index.js"></script>
    </body>
</html>

index.css

td{
    padding: 20px 40px;
}
table tr:nth-child(odd){
    background-color: #f11212;
}
table tr:nth-child(even){
    background-color: #5b0af1;
}
table, td{
    border-collapse: collapse;
}

index.js

let tds = document.querySelectorAll('td');
tds.forEach(item => {
    item.onclick = function(){
        item.style.backgroundColor = '#000000';
    }
})

main.mjs

import { createServer } from 'node:http';
import { readFileSync } from 'node:fs';

const server = createServer((req, res) => 
{
    var { pathname } = new URL(req.url, `http://${req.headers.host}`);
    if (pathname === '/'){
        res.setHeader('Content-Type', 'text/html; charset=utf-8');
        let html = readFileSync('index.html', 'utf-8');
        res.end(html);
    }
    else if (pathname.endsWith('.css')){
        res.setHeader('Content-Type', 'text/css; charset=utf-8');
        let css = readFileSync(pathname.slice(1), 'utf-8');
        res.end(css);
    }
    else if (pathname.endsWith('.js')){
        res.setHeader('Content-Type', 'application/javascript; charset=utf-8');
        let js = readFileSync(pathname.slice(1), 'utf-8');
        res.end(js);
    }
    else{
        res.statusCode = 404;
        res.end('404 Not Found');
    }
});

const port = 80;
server.listen(port, () =>
{
    console.log(`服务器运行在 http://localhost:${port}/`);
});
部署静态资源站

用的我主页的源码,主页地址:https://iyatt.com
文件结构如图
file

下面是 Node.js 代码

import { createServer } from 'node:http';
import { readFile } from 'node:fs';
import { extname, resolve } from 'node:path';

const root = resolve('homepage'); // 网站根目录
const mimeTypes = { // 支持的文件类型和对应的MIME类型(开发中可以使用第三方模块)
    '.html': 'text/html; charset=utf-8',
    '.css': 'text/css',
    '.js': 'application/javascript',
    '.png': 'image/png',
    '.jpg': 'image/jpeg',
    '.gif': 'image/gif',
    '.ico': 'image/x-icon',
};

const server = createServer((req, res) => 
{
    const { pathname } = new URL(req.url, `http://${req.headers.host}`);

    if (req.method !== 'GET') { // 只处理 GET 请求
        res.statusCode = 405;
        res.end('<h1>405 Method Not Allowed</h1>');
        return;
    }

    if (pathname === '/') { // 访问根目录跳转 index.html
        res.statusCode = 301;
        res.setHeader('Location', '/index.html');
        res.end();
    }
    else {
        const ext = extname(pathname);
        readFile(resolve(root, pathname.slice(1)), (err, data) => {
            if (err) {
                switch (err.code) {
                    case 'ENOENT': { // 文件不存在
                        res.statusCode = 404;
                        res.end('<h1>404 Not Found</h1>');
                        break;
                    }
                    case 'EPERM': { // 权限不足
                        res.statusCode = 403;
                        res.end('<h1>403 Forbidden</h1>');
                        break;
                    }
                    default: { // 其他错误
                        res.statusCode = 500;
                        res.end('<h1>500 Internal Server Error</h1>');
                        break;
                    }
                }
            }
            else {
                if (mimeTypes[ext]) { // 设定已知的 Content-Type
                    res.setHeader('Content-Type', mimeTypes[ext]);
                }
                else { // 未知的产生下载行为
                    res.setHeader('Content-Type', 'application/octet-stream');
                }
                res.end(data);
            }
        });
    }
});

const port = 80;
server.listen(port, () =>
{
    console.log(`服务器运行在 http://localhost:${port}/`);
});

正常访问
file

访问资源中的一张图片
file

找不到文件
file

没有权限访问文件
file

下载行为
file

模块

基于 ESM 的模块导出

导出

自定义模块实现 1
针对单个函数、变量导出,在要导出的函数和变量前加上 export

modules.mjs

export function testFunction1() {
    console.log('测试函数1');
}

export function testFunction2() {
    console.log('测试函数2');
}

export const testConstant = '这是一个常量';

自定义模块实现 2
集中导出,使用 export {} 把要导出的函数、变量放进去

modules.mjs

function testFunction1() {
    console.log('测试函数1');
}

function testFunction2() {
    console.log('测试函数2');
}

const testConstant = '这是一个常量';

export { testFunction1, testFunction2, testConstant }

使用模块
index.mjs

export function testFunction1() {
    console.log('测试函数1');
}

export function testFunction2() {
    console.log('测试函数2');
}

export const testConstant = '这是一个常量';

file

别名

给要导出的内容设置别名,使用集中导出

modules.mjs

function testFunction1() {
    console.log('测试函数1');
}

function testFunction2() {
    console.log('测试函数2');
}

const testConstant = '这是一个常量';

export { testFunction1 as test1, testFunction2 as test2, testConstant as test }

index.mjs

import { test1, test2, test } from "./modules.mjs";

console.log(test);
test1();
test2();

默认导出

前面的普通导出,在导入使用的时候需要添加一个括号,而默认导出可以不用添加括号。只是在一个模块中只允许一个默认导出,使用方法在普通导出的基础上把 export 换成 export default 就行。如果是设置一个变量为默认导出不能直接在 const/var/let 前写,要额外写导出。比如

const testConstant = '这是一个常量';
export default testConstant;

下面将一个函数默认导出
modules.mjs

export function testFunction1() {
    console.log('测试函数1');
}

export default function testFunction2() {
    console.log('测试函数2');
}

使用
如果一次性导入多个,默认导出的必须写在前面
index.mjs

import testFunction2, { testFunction1 } from "./modules.mjs";

testFunction1();
testFunction2();

包管理工具

Node.js 的官方包管理工具是 npm,也有一些第三方的包管理工具,比如 yarn。
关于 npm 的官方说明:https://nodejs.org/en/learn/getting-started/an-introduction-to-the-npm-package-manager

包安装或依赖安装

安装包使用命令

npm install
# 或
npm i

安装指定包可以在命令后跟上包名,搜索包可前往:https://www.npmjs.com/
如果要全局安装就加上参数 -g,一般是命令工具采用全局安装的方式,这样不管在什么路径下都能使用,可以参考要安装的东西的文档决定什么方式安装。使用命令查看全局安装路径

npm root -g

file

如果不使用 -g 参数,默认安装是在当前工作路径下创建一个文件夹 node_modules,并在里面放置安装的东西。另外在工作路径下会产生一个 package-lock.json 文件,里面会记录安装的包的名字、版本、地址、校验信息。在发布自己开发的软件的时候通常不打包 node_modules 文件夹,可以极大地缩小打包体积,在用户使用这个软件的时候可以通过上面的安装命令来自动完成依赖安装,安装的时候不需要指定包名,会读取 package-lock.json 文件获取开发者使用的依赖。
站在软件开发者的角度,对于使用的依赖又分普通依赖和开发依赖,默认安装是标注为普通依赖,即使用 -S 参数,使用 -D 参数安装的则为开发依赖。开发者编写一个软件安装的普通依赖,发布出去,使用 npm i 自动安装依赖会同样安装。而开发依赖一般只是用于开发者测试使用,用户运行开发者编写的软件并不依赖,可以不需要安装,开发者使用 -D 安装这些依赖,则发布出去,用户安装依赖时就不会安装这些依赖。(下图是文档原文)
file

简单来说,如果开发者编写一个软件用到的某些依赖的功能是要集成到编写的软件中,这种依赖开发者就要安装为普通依赖,也可以叫做生产依赖。同时另外存在一些依赖,它们不是软件功能的组成,但是是开发者进行开发需要使用的工具或者测试框架,只是开发者需要,软件运行本身不用,开发者就要把这些依赖作为开发依赖安装。

创建一个项目

创建一个文件夹,终端工作路径切换到文件夹下,执行

npm init

默认项目名会使用文件夹的名称,但是项目名称不能用中文,如果文件夹含有中文,就自行设置英文名称,也可以直接设置其它名称
file

上面的命令就是引导创建一个 package.json 文件
file

配置命令别名

我写了一个源文件 index.mjs

console.log('Hello, world!');

修改 package.json
中 scripts 部分,添加了两个别名 server 和 start 和别名对应执行的命令
file

就可以使用 npm run 别名 的方式执行,其中 start 这个别名特殊,可以直接通过 npm start 执行
file

在项目极其复杂,运行时添加参数较多的情况下,通过别名可以更方便的运行

发布包

在 npm 源站注册一个账号:https://www.npmjs.com/

然后创建一个示例演示发布
创建一个包名为 iyatt-package
file

编写源码
index.mjs

export function add(num1, num2) {
    return num1 + num2;
}

如果修改过 npm 源站的,在进行发布操作的时候要换回官方的源站才行,镜像站不支持发布包。

npm 登录注册的账号

npm login

发布

npm publish

file

在 npm 源站上就能搜到了
file

可以执行命令从源站下载安装这个包
file

写一段代码测试包调用

import { add } from 'iyatt-package';

console.log(add(1, 2));

file

如果后面要发布新版本的包,把 package.json 里的版本改一下,再执行发布命令就可以。
如果要删除发布的包可以到 npm 源站上操作,更为方便。

版本管理

用于管理 Node.js 版本的工具挺多的,比如 nvm 和 n 等,其中 n 不支持 Windows,Windows 下推荐使用 nvm-windows: https://github.com/coreybutler/nvm-windows

需要前往项目页 Release 下载安装包,项目页上有使用说明,可以用于升级 Node.js,在多个版本之间切换等等。

如果是 Linux 可以使用 n 来管理,安装也方便,直接使用 npm

npm i -g n

npm 源站上有 n 命令的使用说明:https://www.npmjs.com/package/n

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

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

相关文章

iOS OC项目中引入SwiftUI文件

iOS OC项目中引入SwiftUI文件 1、创建SwiftUI文件 2、第一次创建时&#xff0c;Xcode会提示桥接&#xff0c;选择 Creat Bridging Header即可。 3、创建swift管理类 /**在UIKit中使用SwiftUI&#xff0c;需要使用UIHostingController对SwiftUI进行包装&#xff0c;返回的是U…

小游戏:贪吃蛇

&#x1f381;个人主页&#xff1a;我们的五年 &#x1f50d;系列专栏&#xff1a;贪吃蛇 &#x1f337;追光的人&#xff0c;终会万丈光芒 目录 &#x1f3dd;1.头文件&#xff1a; &#x1f3dd;2.实现文件&#xff1a; &#x1f3dd;3.测试文件 &#xff1a; 前言&#…

STM32自动光控窗帘程序+Proteus仿真图 H桥L298驱动电机

目录 1、前言 2、仿真图 3、源程序 资料下载地址&#xff1a;STM32自动光控窗帘程序Proteus仿真图 H桥L298驱动电机 1、前言 基于STM32F103设计的智能光控窗帘&#xff0c;包含STM32芯片、光敏电阻、LCD1602显示屏、电机驱动控制模块等。 备注&#xff1a;通过ARM内部的…

网络安全数字孪生:一种新颖的汽车软件解决方案

摘要 随着汽车行业转变为数据驱动的业务&#xff0c;软件在车辆的开发和维护中发挥了核心作用。随着软件数量的增加&#xff0c;相应的网络安全风险、责任和监管也随之增加&#xff0c;传统方法变得不再适用于这类任务。相应的结果是整车厂和供应商都在努力应对汽车软件日益增加…

CAS和synchronized原理

synchronized与CAS Synchronized 原理加锁工作过程一些优化 CAS实现原子类 小结 Synchronized 原理 synchronized 既可以是乐观锁, 也可以是悲观锁.synchronized 既可以是轻量级锁, 也可以是重量级锁.synchronized 重量级锁是由系统的互斥锁实现的; 轻量级锁是基于自旋锁实现的…

Windows 安全中心:页面不可用 你的 IT 管理员已限制对此应用的某些区域的访问,并且你尝试访问的项目不可用。有关详细信息,请与 IT 支持人员联系。

问题 1&#xff1a;Windows 安全中心提示&#xff1a;【页面不可用 你的 IT 管理员已限制对此应用的某些区域的访问&#xff0c;并且你尝试访问的项目不可用。有关详细信息&#xff0c;请与 IT 支持人员联系。】 修复 Microsoft.SecHealthUI 方法 1&#xff1a;命令自动重装安…

linq select 和selectMany的区别

Select 和 SelectMany 都是 LINQ 查询方法&#xff0c;但它们之间有一些区别。 Select 方法用于从集合中选择特定的属性或对集合中的元素进行转换&#xff0c;并返回一个新的集合。例如&#xff1a; var numbers new List<int> { 1, 2, 3, 4, 5 }; var squaredNumbers…

sc2024项目consul

1. 什么是consul HashiCorp Consul是一款服务网络解决方案&#xff0c;可让团队管理服务之间以及内部部署和多云环境及运行时的安全网络连接。consul提供服务发现、服务治理、流量管理和对网络基础设施设备的自动更新。(添加链接描述)Consul使用Go语言开发 2. 功能 多数据中…

【PCL】教程global_hypothesis_verification 通过验证模型假设来实现 3D 对象识别与位姿估计...

测试程序1 milk.pcd milk_cartoon_all_small_clorox.pcd 终端输出1&#xff1a; Model total points: 12575; Selected Keypoints: 193 Scene total points: 307200; Selected Keypoints: 7739 [pcl::SHOTEstimation::computeFeature] The local reference frame is not valid!…

windows驱动开发-内存概述

“90%的程序问题都是由内存引起的&#xff0c;剩下的10%是使用内存引起的&#xff01;”这是一句非常经典的论证&#xff0c;实际上&#xff0c;在程序开发中&#xff0c;内存问题就是最大的问题&#xff0c;没有之一。 现代的计算机体系中&#xff0c;内存承载了太多的功能&a…

解决“该扩展程序未列在 Chrome 网上应用店中,并可能是在您不知情的情况下添加的”的方法

一、问题 安装插件出现时“该扩展程序未列在 Chrome 网上应用店中&#xff0c;并可能是在您不知情的情况下添加的” 二、解决方法 1、把需要安装的第三方插件&#xff0c;后缀.crx 改成 .rar&#xff0c;然后解压&#xff0c;得到一个文件夹 2、再打开chrome://extensions/谷歌…

Visual Studio Code使用

目录 1.python的调试 2.c的运行 方法1&#xff1a; 方法2&#xff1a; 3.c的调试 3.1调试方法一&#xff1a;先生成执行文件&#xff0c;再调试 3.2调试方法二&#xff1a;同时生成执行文件&#xff0c;调试 4.tasks.json 与launch.json文件的参考 4.1C生成执行文件tas…

linux之进程通信

目录 一、进程通信介绍 1.目的 2.发展 3.进程通信是什么&#xff0c;怎么通信&#xff1f; 二、管道 1.介绍 2.匿名管道 1.单向通信管道原理 2.代码实现 3.管道特征 4.管道的四种情况 5.管道的应用场景 使用管道实现一个简易版本的进程池 3.命名管道 1.思考 2.…

燃冬之yum、vim和你

了解了很多指令和权限&#xff0c;搞点真枪实弹来瞅瞅 学Linux不是天天就在那掰扯指令玩&#xff0c;也不是就研究那个权限 准备好迎接Linux相关工具的使用了么码农桑~ yum 软件包 什么是软件包呢&#xff1f; 首先来举个生活中常见点的例子&#xff1a;比如我的手机是华为…

PLC无线通讯技术在汽车喷涂车间机械手臂上的应用

一、项目背景 在汽车生产装配工艺中&#xff0c;机械臂目前已经广泛地应用于装配、搬运等工业生产中&#xff0c;在机械臂系列产品中&#xff0c;汽车喷漆自动控制喷涂机械装置以其独特的优势&#xff0c;能够根据油漆喷涂量的大小&#xff0c;严格控制喷嘴与喷漆面之间距离等…

【函数式接口使用✈️✈️】配合策略模式实现文件处理的案例

目录 &#x1f378;前言 &#x1f37b;一、功能描述 &#x1f37a;二、面向对象设计模式 &#x1f379;三、策略模式 &#x1f366;四、策略 VS 面向对象 &#x1f368;章末 &#x1f378;前言 小伙伴们大家好&#xff0c;上周初步了解了下函数式接口&#xff0c;Consume…

「最没存在感」港姐冠军入行10年不受捧,与相恋4年男友分手

昨日&#xff08;4月21日&#xff09;一众歌手艺人齐集红馆举行《全港运动全城跃动第九届全港运动会开幕礼》录影&#xff0c;TVB亦派出不少的歌手艺人小花表演。其中一部分是邵珮诗与黄婧灵大跳拉丁舞&#xff0c;同属身材丰满的二人跳起上来视觉极夸张。 而平常经常露出姣好身…

powershell@命令行提示符样式配置自定义@pwsh重写prompt显示电量内存时间等信息

文章目录 abstract流行的powershell prompt模块示例 powershell原生修改Prompt函数配置文档Prompt命令来自哪里 简单修改带上电量和时间的Prompt 复杂修改预览FAQ:没有必要修改相关仓库地址样式选择平衡样式花哨样式响应性能 小结 abstract 在 PowerShell 中&#xff0c;可以通…

【git】git ignore如何添加core/config.py忽略

在Git中&#xff0c;.gitignore文件用于指定不被Git追踪的文件和文件夹。要添加core/config.py文件到.gitignore中&#xff0c;你需要编辑.gitignore文件并添加以下行&#xff1a; core/config.py这行表示Git应该忽略名为config.py的文件&#xff0c;它位于core目录下。确保在…

Codeforces Round 821 (Div. 2) D2. Zero-One

题目 #include <bits/stdc.h> using namespace std; #define int long long #define pb push_back #define fi first #define se second #define lson p << 1 #define rson p << 1 | 1 const int maxn 1e5 5, inf 1e18, maxm 4e4 5; const int N 1e6;c…