【全栈第三课】通过ChatGPT快速入门NodeJS

前言

往期全栈课程:
Vue从入门到精通
微信小程序从入门到精通

Node.js基础

简介

Node.js是什么?

Node.js 是一个基于 Chrome V8 引擎的 JavaScript 运行环境。Node.js 使用了一个事件驱动非阻塞式 I/O的模型,使其轻量又高效。Node.js 的包管理工具 npm 是全球最大的开源库生态系统。

Node.js 不是一门语言,也不是 JavaScript 的框架,也不是像Nginx一样的Web服务器 ,Node.js 是 JavaScript 在服务器端的运行环境(平台)。

Node.js的组成

在 Node.js 里运行 JavaScript,跟在 Chrome 里运行 JavaScript 有什么不同?

二者采用的是同样的 JS 引擎。在 Node.js 里写 JS,和在前端写 JS,几乎没有不同。在写法上的区别在于:Node.js 没有浏览器、页面标签相关的 API(不能操作BOM对象和DOM对象),但是新增了一些 Node.js 相关的 API。通俗来说,对于开发者而言,在前端写 JS 是用于控制浏览器;而 Node.js 环境写 JS 可以控制整个计算机。

我们知道,JavaScript 的组成分为三个部分:

  • ECMAScript:描述了JS的语法和基本对象
  • DOM(document object model):DOM 是文档对象模型,处理网页内容的方法和接口。是W3C 的标准;
  • BOM(browser object model);BOM 是浏览器对象模型,提供与浏览器交互的方法和接口。各个浏览器厂商根据 DOM在各自浏览器上的实现;

ECMAScript 是 JS 的语法;DOM 和 BOM 浏览器端为 JS 提供的 API。

而 Node.js 的组成分为:

  • ECMAScript。ECMAScript 的所有语法在 Node 环境中都可以使用。
  • Node 环境提供的一些附加 API(包括文件、网络等相关的 API)。

如下图所示:

image-20221102140543274

知名度较高的Node.js开源项目

image-20221102140856507

  • express:Node.js 中著名的 web 服务框架。

  • Koa:下一代的 Node.js 的 Web 服务框架。所谓的“下一代”是相对于 Express 而言的。

  • Egg:2016 年,阿里巴巴研发了知名的 Egg.js 开源项目,号称企业级 Web 服务框架。Egg.js 是基于 Koa 开发的。

  • mocha:是现在最流行的 JavaScript 测试框架,在浏览器和 Node 环境都可以使用。

  • PM2:node 多进程管理。

  • jade:非常优秀的模板引擎,不仅限于 js 语言。

  • CoffeeScript:用简洁的方式展示 JavaScript 优秀的部分。

  • Atom:编辑器。

  • VS Code:最酷炫的编辑器。

  • socket.io:实时通信框架。

Node.js的特点

  • 异步、非阻塞 IO 模型
  • 事件循环
  • 单线程
  • 总结:轻量和高效

Node.js 的性能和效率非常高。

传统的 Java 语言是一个请求开启一个线程,当请求处理完毕后就关闭这个线程。而 Node.js 则完全没有采用这种模型,它本质上就是一个单线程。

你可能会疑问:一个线程如何服务于大量的请求、如何处理高并发的呢?这是因为,Node.js 采用的是异步的、非阻塞的模型。

这里所谓的“单线程”,指的是 Node 的主线程只有一个。为了确保主线程不被阻塞,主线程是用于接收客户端请求。但不会处理具体的任务。而 Node 的背后还有一个线程池,线程池会处理长时间运行的任务(比如 IO 操作、网络操作)。线程池里的任务是通过队列和事件循环的机制来执行。

环境安装

通过 Node.js 安装包产生的问题

  • 安装新版本时,需要覆盖旧版本;而且以前版本安装的很多全局工具包,需要重新安装。
  • 无法回滚到之前的旧版本。
  • 无法在多个版本之间切换(很多时候,不同的项目需要使用特定版本。或者,我想临时尝鲜一下新版本的特性)

因此不推荐使用安装包的形式安装,Win推荐使用NVM,Mac使用n

安装教程如下:

mac:https://juejin.cn/post/7065701236925792293

win:https://juejin.cn/post/6844903861308637192

image-20230706105910828

真好用!!!

包管理工具

由于 Node 是一套轻内核的平台,虽然提供了一系列的内置模块,但是不足以满足开发者的需求,于是乎出现了包(package)的概念: 与核心模块类似,就是将一些预先设计好的功能或者说 API 封装到一个文件夹,提供给开发者使用。

Node 本身并没有太多的功能性 API,所以市面上涌现出大量的第三方人员开发出来的 Package。

npm

NPM:Node Package Manager。官方链接: https://www.npmjs.com/

Node.js 发展到现在,已经形成了一个非常庞大的生态圈。包的生态圈一旦繁荣起来,就必须有工具去来管理这些包。

NPM 的常用命令

npm init
npm install 包名 –g  (uninstall,update)
npm install 包名 --save-dev (uninstall,update)
npm list -g (不加-g,列举当前目录下的安装包)
npm info 包名(详细信息) npm info 包名 version(获取最新版本)
npm install md5@1(安装指定版本)
npm outdated(  检查包是否已经过时)"dependencies": {    "md5": "^2.1.0"  }  ^ 表示 如果 直接npm install 将会 安md52.*.*  	最新版本"dependencies": {    "md5": "~2.1.0"  }  ~ 表示 如果 直接npm install 将会 安装md5 2.1.*  最新版本"dependencies": {    "md5": "*"  }  * 表示 如果 直接npm install 将会 安装 md5  最新版本

nrm

和npm一样都是包管理工具,喜欢那个用哪个

yarn

和npm一样都是包管理工具,喜欢那个用哪个

https://yarn.bootcss.com/,推荐这个

对比npm:速度超快: Yarn 缓存了每个下载过的包,所以再次使用时无需重复下载。 同时利用并行下载以最大化资源利用率,因此安装速度更快。超级安全: 在执行代码之前,Yarn 会通过算法校验每个安装包的完整性。开始新项目yarn init 
添加依赖包yarn add [package] yarn add [package]@[version] yarn add [package] --dev 
升级依赖包yarn upgrade [package]@[version] 
移除依赖包yarn remove [package]安装项目的全部依赖yarn install 

cnpm

和npm一样都是包管理工具,喜欢那个用哪个

pnpm

使用文档如下:https://www.pnpm.cn/motivation

CommonJS

前言

网站越来越复杂,js代码、js文件也越来越多,会遇到一些问题

  • 文件依赖
  • 全局污染、命名冲突

程序模块化包括:

  • 日期模块
  • 数学计算模块
  • 日志模块
  • 登陆认证模块
  • 报表展示模块等。

所有这些模块共同组成了程序软件系统。

一次编写,多次使用,才是提高效率的核心。

模块化规范

模块化规范的引入

假设我们引入模块化,首先可能会想到的思路是:在一个文件中引入多个js文件。如下:

<body><script src="zepto.js"></script><script src="fastClick.js"></script><script src="util/login.js"></script><script src="util/base.js"></script><script src="util/city.js"></script>
</body>

但是这样做会带来很多问题:

  • 请求过多:引入十个js文件,就有十次http请求。
  • 依赖模糊:不同的js文件可能会相互依赖,如果改其中的一个文件,另外一个文件可能会报错。

以上两点,最终导致:难以维护

于是,这就引入了模块化规范。

CommonJS 的基本语法

在 CommonJS 中,每个文件都可以当作一个模块:

  • 在服务器端:模块的加载是运行时同步加载的。
  • 在浏览器端: 模块需要提前编译打包处理。首先,既然同步的,很容易引起阻塞;其次,浏览器不认识require语法,因此,需要提前编译打包。

模块的暴露和引入

Node.js 中只有模块级作用域,两个模块之间的变量、方法,默认是互不冲突,互不影响,这样就导致一个问题:模块 A 要怎样使用模块B中的变量&方法呢?这就需要通过 exports 关键字来实现。

Node.js中,每个模块都有一个 exports 接口对象,我们可以把公共的变量、方法挂载到这个接口对象中,其他的模块才可以使用。

接下来详细讲一讲模块的暴露、模块的引入。

exports

exports对象用来导出当前模块的公共方法或属性。别的模块通过 require 函数调用当前模块时,得到的就是当前模块的 exports 对象。

// 相当于是:给 exports 对象添加属性
exports.xxx = value

这个 value 可以是任意的数据类型。

注意:暴露的关键词是exports,不是export。其实,这里的 exports 类似于 ES6 中的 export 的用法,都是用来导出一个指定名字的对象。

const name = 'zs'const foo = function(value){return value * 2
}exports.day01_name = name
exports.day01_foo = foo

module.exports

module.exports用来导出一个默认对象,没有指定对象名。

语法格式:

// 方式一:导出整个 exports 对象
module.exports = value;// 方式二:给 exports 对象添加属性
module.exports.xxx = value;

这个 value 可以是任意的数据类型。

代码举例:

// 方式1
module.exports = {name: '我是 module1',foo(){console.log(this.name);}
}// 我们不能再继续写 module.exports = value2。因为重新赋值,会把 exports 对象 之前的赋值覆盖掉。// 方式2
const age = 28;
module.exports.age = age;

module.exports 还可以修改模块的原始导出对象。比如当前模块原本导出的是一个对象,我们可以通过 module.exports 修改为导出一个函数。如下:

module.exports = function () {console.log('hello world')
}

区别

最重要的区别:

  • 使用exports时,只能单个设置属性 exports.a = a;
  • 使用module.exports时,既单个设置属性 module.exports.a,也可以整个赋值 module.exports = obj

其他要点:

  • Node中每个模块的最后,都会执行 return: module.exports
  • Node中每个模块都会把 module.exports指向的对象赋值给一个变量 exports,也就是说 exports = module.exports
  • module.exports = XXX,表示当前模块导出一个单一成员,结果就是XXX。
  • 如果需要导出多个成员,则必须使用 exports.add = XXX; exports.foo = XXX。或者使用 module.exports.add = XXX; module.export.foo = XXX

require

require函数用来在一个模块中引入另外一个模块。传入模块名,返回模块导出对象。

语法格式

const module1 = require('模块名');

解释:

  • 内置模块:require的是包名
  • 下载的第三方模块:require的是包名
  • 自定义模块:require的是文件路径。文件路径既可以用绝对路径,也可以用相对路径。后缀名.js可以省略。

代码举例

const m1 = require('./main.js');const module2 = require('./main');const module3 = require('Demo/src/main.js');m1.method()

require()函数的两个作用

  • 执行导入的模块中的代码。
  • 返回导入模块中的接口对象。

内置模块

前置知识点

箭头函数

// 传统函数
function add(a, b) {return a + b;
}// 箭头函数
const add = (a, b) => a + b;

回调函数

回调函数常用于异步操作和事件处理

  • 当异步操作完成后,可以使用回调函数进行操作。
  • 当事件发生时可以使用回调函数进行操作处理一些逻辑

回调函数的应用

readableStream.on('data', (chunk) => {console.log('Data:', chunk);
});

你可能知道.on是一个方法然后括号里面传入了两个参数,一个参数是data,一个参数是被()=>{}修饰的

那么这个被()=>{}修饰的部分就是回调函数,回调函数一般是将回调函数做为参数传递给另一个函数,那么为了方便就可以用上面提到的箭头函数来传递。这样是不是很方便呢,也不用想给回调函数起个什么名字了哈哈。

在前端开发中回调函数和箭头函数非常常见,一定要理解不然都看不懂这代码啥意思。

http模块

要使用 HTTP 服务器和客户端,则必须 require('http')

const http  = require('http')// 创建本地服务器来从其他接收数据
const server = http.createServer((req,res) => {res.writeHead(200, { 'Content-Type': 'application/json' });res.end(JSON.stringify({data: 'Hello World!'}));
})
server.listen(8000);

结果展示

image-20230707103448113

jsonp

JSON 是一种数据格式,而 JSONP 是一种用于跨域数据请求的技术。JSONP 利用动态创建 <script> 标签的方式来获取跨域数据,但它也存在一些安全风险。在现代的 Web 开发中,由于浏览器支持跨域资源共享(CORS)等技术,JSONP 的使用已经相对较少,更多地使用 JSON 和 Ajax 进行数据交互。

模拟get

var http = require('http')
var https = require('https')// 1、接口 2、跨域
const server = http.createServer((request, response) => {var url = request.url.substr(1)var data = ''response.writeHeader(200, {'content-type': 'application/json;charset=utf-8','Access-Control-Allow-Origin': '*'})https.get(`https://m.lagou.com/listmore.json${url}`, (res) => {res.on('data', (chunk) => {data += chunk})res.on('end', () => {response.end(JSON.stringify({ret: true,data}))})})})server.listen(8080, () => {console.log('localhost:8080')
})

效果如下:

image-20230707171549597

在上述示例中,我们使用 https.request() 方法创建了一个请求对象,指定了请求的参数和路径,包括查询参数 page=2。我们向 reqres.in/api/users 路径发送了一个 GET 请求。

当收到响应时,我们监听 response 对象的 data 事件来获取响应数据。在 end 事件中,我们处理完整的响应数据。

请注意,上述示例中使用的是 HTTPS 请求,端口号默认为 443。如果你希望使用 HTTP 请求,你可以将 options 中的 port 设置为 80,并使用 http 模块替代 https 模块。

模拟post

const https = require('https');// POST 请求的选项
const options = {hostname: 'reqres.in', // API 的主机名port: 443, // API 的端口号path: '/api/users', // API 的路径method: 'POST', // 请求方法headers: {'Content-Type': 'application/json', // 请求的内容类型},
};// 创建请求对象
const request = https.request(options, (response) => {let data = '';// 接收响应数据response.on('data', (chunk) => {data += chunk;});// 响应结束时处理数据response.on('end', () => {console.log('Response:', data);});
});// 请求数据
const postData = JSON.stringify({ name: 'John Doe', job: 'Developer' }); // 要发送的数据
request.write(postData);// 发送请求
request.end();

请求效果如下:

image-20230707171327677

在上述示例中,我们使用 https.request() 方法创建一个请求对象,指定了请求的参数和头部信息。我们向 reqres.in/api/users 路径发送了一个 POST 请求。我们使用 JSON 格式的数据对象 { name: 'John Doe', job: 'Developer' } 作为请求主体,并通过 request.write() 方法将其写入请求。

当收到响应时,我们监听 response 对象的 data 事件来获取响应数据。在 end 事件中,我们处理完整的响应数据。

请注意,上述示例使用的是 HTTPS 请求,端口号为 443。如果你希望使用 HTTP 请求,你可以将 options 中的 port 设置为 80,并使用 http 模块替代 https 模块。

url模块

parse

const url = require('url')
const urlString = 'https://www.google.com/'
const parsedStr = url.parse(urlString)
console.log(parsedStr)

效果如下:

看结果知道是用来解析url的

注意:url.parse返回的是一个Url对象

image-20230707104116723

format

const url = require('url')
const urlObject = {protocol: 'https:',slashes: true,auth: null,host: 'www.baidu.com:443',port: '443',hostname: 'www.baidu.com',hash: '#tag=110',search: '?id=8&name=mouse',query: { id: '8', name: 'mouse' },pathname: '/ad/index.html',path: '/ad/index.html?id=8&name=mouse'
}
const parsedObj = url.format(urlObject)
console.log(parsedObj)

效果如下:

看来是用来格式化URL的

image-20230707104513217

resolve

const url = require('url')var a = url.resolve('/one/two/three', 'four') var b = url.resolve('http://example.com/', '/one')var c = url.resolve('http://example.com/one', '/two')console.log(a + "," + b + "," + c)

拼接url的,效果如下:

image-20230707105538082

querystring模块

parse和stringify

image-20230707105941647

escape/unescape

可以看出一个是将特殊字符进行转义,一个是对转义后的特殊字符进行解析

转义的好处是可以防止SQL注入

image-20230707111100282

escapeunescape 函数是 JavaScript 中的字符串转义函数,它们可以用于防止 SQL 注入攻击。然而,需要注意的是,这两个函数在最新的 ECMAScript 标准中已被废弃,因为它们并不是完全安全可靠的转义方式。相反,现代的开发实践推荐使用预编译语句等技术来防止 SQL 注入。

预编译是一种执行之前先定义好SQL语句模版,其中包含占位符,而不是直接插入用户提供的数据。然后将用户输入的数据作为参数传递给预编译语句,数据库连接库会负责将数据安全地插入到查询中,避免了SQL注入的风险。

event模块

const EventEmitter = require('events');// 创建自定义事件发射器
class MyEmitter extends EventEmitter {}// 创建事件发射器实例
const myEmitter = new MyEmitter();// 注册事件监听器
myEmitter.on('myEvent', (arg) => {console.log('Event received:', arg);
});// 触发事件
myEmitter.emit('myEvent', 'Hello, world!');

在上述示例中,我们创建了一个自定义的事件发射器 MyEmitter,通过继承 EventEmitter 类。然后,我们创建了 MyEmitter 的实例 myEmitter。接下来,我们使用 on() 方法注册了一个监听器,监听名为 myEvent 的事件,并定义了事件发生时的回调函数。最后,我们使用 emit() 方法触发了 myEvent 事件,并传递了一个参数 'Hello, world!'

当触发 myEvent 事件时,注册的监听器会执行相应的回调函数,并输出事件的参数。

events 模块的使用让事件驱动的编程更加简洁和灵活。它被广泛应用于 Node.js 中处理异步操作、网络请求、事件处理等场景,以及许多第三方模块和库中。

fs文件操作模块

Node.js 的 fs(File System)模块是一个内置模块,用于处理文件系统操作。它提供了一组方法,用于读取、写入、更新和删除文件,以及处理文件和目录的相关操作。

const fs = require('fs');//写入
fs.writeFile('file.txt', 'Hello, world!', (err) => {if (err) {console.error(err);return;}console.log('File written successfully!');
});// 读取文件
fs.readFile('file.txt', 'utf8', (err, data) => {if (err) {console.error(err);return;}console.log(data);
});// 判断文件是否存在
if (fs.existsSync('file.txt')) {console.log('File exists');
} else {console.log('File does not exist');
}

异步方法采用回调函数作为参数,在操作完成时被调用。这样可以在等待操作完成时,继续执行其他任务,提高程序的并发性和响应性。

例如,异步读取文件的方法是 fs.readFile(),它接受一个回调函数作为参数,当文件读取完成时会调用该回调函数,并传递读取到的数据。

stream流模块

const fs = require('fs');// 创建可读流
const readableStream = fs.createReadStream('file.txt', 'utf8');// 创建可写流
const writableStream = fs.createWriteStream('output.txt', 'utf8');// 将可读流的数据写入可写流
readableStream.pipe(writableStream);// 监听可读流的 'data' 事件
readableStream.on('data', (chunk) => {console.log('Data:', chunk);
});// 监听可写流的 'finish' 事件
writableStream.on('finish', () => {console.log('Write complete.');
});

在示例中,readableStream.on('data', callback)writableStream.on('finish', callback) 是两个事件监听的示例。on() 方法用于注册事件监听器,并指定要监听的事件名称和相应的回调函数。

'data' 是监听事件的名称,它是可读流 readableStream 发出的事件。当可读流有新的数据块可用时,会触发 'data' 事件。通过注册监听器来捕获 'data' 事件,可以在每次有新数据时执行相应的回调函数。

类似地,'finish' 事件是可写流 writableStream 发出的事件。当所有数据都被写入可写流后,会触发 'finish' 事件。通过注册监听器来捕获 'finish' 事件,可以在写入完成时执行相应的回调函数。

zlib模块

const zlib = require('zlib');
const data = 'Hello, world!';zlib.gzip(data, (err, compressedData) => {if (err) {console.error(err);return;}console.log('Compressed data:', compressedData);
});

data 是要压缩的原始数据,而 compressedData 是压缩后的数据。

crypto模块

crypto 模块是 Node.js 的内置模块之一,提供了加密和解密的功能。它通过提供一组加密算法和相关的操作函数,用于处理数据的加密、解密、签名、哈希等安全操作。

const crypto = require('crypto');
const data = 'Hello, world!';//创建了一个哈希对象 hash,使用的哈希算法是 SHA-256。
const hash = crypto.createHash('sha256');
hash.update(data);//执行哈希计算并获取哈希值。digest() 方法用于计算哈希对象的摘要。它接受一个参数来指定输出的格式,例如 'hex' 表示以十六进制字符串的形式输出。调用 digest('hex') 方法后,它返回计算得到的哈希值,并将其赋值给变量 hashedData。
const hashedData = hash.digest('hex');
console.log('Hashed data:', hashedData);

路由

基础

var fs = require("fs")
var path = require("path")function render(res, path) {res.writeHead(200, { "Content-Type": "text/html;charset=utf8" })res.write(fs.readFileSync(path, "utf8"))res.end()
}const route = {"/login": (req, res) => {render(res, "./static/login.html")},"/home": (req, res) => {render(res, "./static/home.html")},"/404": (req, res) => {res.writeHead(404, { "Content-Type": "text/html;charset=utf8" })res.write(fs.readFileSync("./static/404.html", "utf8"))}
}

获取参数

get请求

    "/api/login":(req,res)=>{const myURL = new URL(req.url, 'http://127.0.0.1:3000');console.log(myURL.searchParams.get("username"))   render(res,`{ok:1}`)}

post请求

"/api/login": (req, res) => {var post = '';// 通过req的data事件监听函数,每当接受到请求体的数据,就累加到post变量中req.on('data', function (chunk) {post += chunk;});// 在end事件触发后,通过querystring.parse将post解析为真正的POST请求格式,然后向客户端返回。req.on('end', function () {post = JSON.parse(post);render(res, `{ok:1}`)});}

静态资源处理

function readStaticFile(req, res) {const myURL = new URL(req.url, 'http://127.0.0.1:3000')var filePathname = path.join(__dirname, "/static", myURL.pathname);if (fs.existsSync(filePathname)) {// console.log(1111)res.writeHead(200, { "Content-Type": `${mime.getType(myURL.pathname.split(".")[1])};charset=utf8` })res.write(fs.readFileSync(filePathname, "utf8"))res.end()return true} else {return false}
}

Express

image-20230708142711781

安装

yarn init
yarn add express

路由

Express 提供了 app.get()app.post()app.put()app.delete() 等方法用于定义路由。你可以根据 HTTP 请求方法和 URL 路径,将请求映射到相应的处理函数上。

匹配路径的时候可以上正则表达式

下面是一个基本的路由示例:

const express = require('express');
const app = express();app.get('/', (req, res) => {res.send('Hello, world!');
});app.post('/users', (req, res) => {// 处理创建用户的逻辑
});app.put('/users/:id', (req, res) => {// 处理更新用户的逻辑
});app.delete('/users/:id', (req, res) => {// 处理删除用户的逻辑
});app.listen(3000, () => {console.log('Server started on port 3000');
});

image-20230708143315149

中间件

Express 中间件允许你在请求和响应之间执行一些额外的逻辑。中间件函数可以用来处理请求、修改请求或响应对象、进行身份验证、记录日志等。你可以使用 app.use() 或特定的 HTTP 方法来注册中间件。

const express = require('express');
const app = express();// 自定义中间件
const middleware1 = (req, res, next) => {console.log('Middleware 1');next(); // 调用 next() 将请求传递给下一个中间件或路由处理函数
};const middleware2 = (req, res, next) => {console.log('Middleware 2');next(); // 调用 next() 将请求传递给下一个中间件或路由处理函数
};// 注册中间件
app.use(middleware1);
app.use(middleware2);// 路由处理函数
app.get('/', (req, res) => {res.send('Hello, world!');
});app.listen(3000, () => {console.log('Server started on port 3000');
});

在上述示例中,我们定义了两个中间件函数 middleware1middleware2,它们分别在控制台打印一些信息,并调用 next() 将请求传递给下一个中间件或路由处理函数。这样,当我们访问根路径 / 时,请求将依次通过两个中间件,并最终到达路由处理函数,返回响应。

中间件的应用场景包括但不限于:身份验证、日志记录、错误处理、请求预处理等。它们可以对请求进行处理、修改请求或响应对象,并决定请求的控制流程。中间件提供了一种可扩展的机制,用于在请求和响应之间执行额外的逻辑。

获取请求参数

Express 提供了多种方式来获取请求参数,包括 URL 参数、查询参数、请求体参数等。你可以通过 req.paramsreq.queryreq.body 等属性来访问这些参数。

const express = require('express');
const app = express();app.get('/users/:id', (req, res) => {const userId = req.params.id; // 获取 URL 参数console.log(userId)const queryParams = req.query; // 获取查询参数// 处理逻辑
});app.post('/users', (req, res) => {const userData = req.body; // 获取请求体参数// 处理逻辑
});app.listen(3000, () => {console.log('Server started on port 3000');
});

托管静态文件

Express 提供了 express.static() 方法来托管静态文件,例如 CSS、JavaScript、图像等。你只需将静态文件所在的目录作为参数传递给 express.static(),然后通过访问对应的 URL 路径即可获取静态文件。

const express = require('express');
const app = express();app.use(express.static('public')); // 托管 public 目录下的静态文件app.get('/', (req, res) => {res.send('Hello, world!');
});app.listen(3000, () => {console.log('Server started on port 3000');
});

image-20230708150655251

服务端渲染(模板引擎)

Express 可以与各种模板引擎配合使用,实现服务端渲染的功能。你可以选择适合你的项目的模板引擎,如 EJS、Pug、Handlebars 等,并将其与 Express 集成,通过模板引擎渲染动态内容并生成 HTML。

const express = require('express');
const app = express();// 设置 EJS 作为模板引擎
app.set('view engine', 'ejs');// 路由处理函数
app.get('/', (req, res) => {const data = {title: 'My Server-side Rendered Page',message: 'Hello, world!',};res.render('index', data); // 渲染名为 "index" 的模板,并传递数据
});app.listen(3000, () => {console.log('Server started on port 3000');
});

接下来,在同级目录下创建一个名为 views 的文件夹,并在其中创建一个名为 index.ejs 的文件。在 index.ejs 文件中,我们可以使用 EJS 模板引擎的语法来嵌入动态数据。

<!DOCTYPE html>
<html>
<head><title><%= title %></title>
</head>
<body><h1><%= message %></h1>
</body>
</html>

在上述示例中,我们创建了一个根路径的路由处理函数,使用 res.render() 方法渲染了一个名为 index 的模板,并传递了一个包含动态数据的对象。这里的动态数据包括 titlemessage。在模板中,我们使用 <%= %> 语法来输出动态数据。

当我们启动服务器并访问 http://localhost:3000 时,Express 会根据我们的路由处理函数生成动态的 HTML 页面,并将其发送给客户端进行展示。此时,服务器已经在响应中将动态数据注入到 HTML 页面中,完成了服务端渲染的过程。

MongoDB

这里我使用docker的方式来安装运行。

docker pull mongo:4docker run -p 27017:27017 --name mongo \
-v /mydata/mongo/db:/data/db \
-d mongo:4

使用Navicat来做可视化操作

这里做一个简单的查询,我们可以知道MongoDB更适合和Node结合使用,因为他存储的数据格式是BSON格式的和JSON比较像

image-20230708152743903

CRUD示例

运行的mongodb版本要和nodejs中安装的mongodb包的依赖版本一致

image-20230708154434519

安装依赖:

yarn add mongodb@4.2.2  

CRUD测试:

const { MongoClient } = require('mongodb');// MongoDB 连接 URI
const uri = 'mongodb://localhost:27017';// 数据库名称和集合名称
const dbName = 'leadnews-history';
const collectionName = 'ap_user_search';
// 连接 MongoDB
MongoClient.connect(uri, (err, client) => {if (err) {console.error('Failed to connect to MongoDB:', err);return;}console.log('Connected to MongoDB');const db = client.db(dbName);const collection = db.collection(collectionName);// 创建文档(Create)const document = { name: 'John', age: 25 };collection.insertOne(document, (err, result) => {if (err) {console.error('Failed to insert document:', err);} else {console.log('Document inserted:', result.insertedId);}// 读取文档(Read)collection.find().toArray((err, documents) => {if (err) {console.error('Failed to find documents:', err);} else {console.log('Documents found:', documents);}// 更新文档(Update)const filter = { name: 'John' };const update = { $set: { age: 30 } };collection.updateOne(filter, update, (err, result) => {if (err) {console.error('Failed to update document:', err);} else {console.log('Document updated:', result.modifiedCount);}// 删除文档(Delete)const deleteFilter = { name: 'John' };collection.deleteOne(deleteFilter, (err, result) => {if (err) {console.error('Failed to delete document:', err);} else {console.log('Document deleted:', result.deletedCount);}// 关闭连接client.close();});});});});
});

接口规范与业务分层

RESTful

RESTful(Representational State Transfer)是一种软件架构风格,用于设计网络应用程序的通信方式。它是一种基于 HTTP 协议的设计模式,旨在创建可扩展、灵活和易于维护的 Web 服务。

使用 RESTful 架构,可以设计出易于理解、可扩展和可维护的 Web 服务。以下是 RESTful 架构的一些关键特点和示例:

  • 使用 HTTP 方法来表示对资源的操作:
    • GET:获取资源的表示。
    • POST:创建新资源。
    • PUT:更新资源。
    • DELETE:删除资源。
  • 使用 URI(Uniform Resource Identifier)来标识资源:
    • /users:表示用户资源。
    • /products:表示产品资源。
  • 使用状态码(Status Code)来表示请求结果:
    • 200:成功的请求。
    • 201:已成功创建资源。
    • 404:未找到资源。
    • 500:服务器内部错误。
  • 使用响应的内容类型(Content-Type)来指示返回的数据格式:
    • application/json:返回 JSON 格式的数据。
    • application/xml:返回 XML 格式的数据。
  • 使用超链接(Hyperlinks)来表示资源之间的关系:
    • 使用链接来实现导航、发现和操作资源。

通过遵循 RESTful 架构的原则,可以设计出符合标准、易于理解和易于扩展的 Web 服务。RESTful 架构通常用于构建基于 Web 的 API(Application Programming Interface),支持客户端和服务器之间的数据交互和操作。

需要注意的是,RESTful 架构是一种设计模式和原则,并不是具体的技术或实现。在实际开发中,可以使用不同的技术和框架来实现 RESTful Web 服务,例如使用 Node.js、Express、Django、Spring 等。

URI 是用于唯一标识实体的名称,而 URL 则是用于定位实体的具体位置或资源的地址。

比如你要找一个人名为张三的人,那么URI就是张三,URL可以理解为张三的具体位置比如身份证上的地址

MVC

MVC(Model-View-Controller)是一种常用的软件架构模式,用于组织和设计应用程序的结构。它将应用程序分为三个主要组件:模型(Model)、视图(View)和控制器(Controller),每个组件负责不同的任务。

下面是 MVC 架构中各个组件的角色和职责:

  1. 模型(Model)
    • 模型表示应用程序的数据和业务逻辑。
    • 它负责处理数据的存储、读取、验证和更新。
    • 模型通常是应用程序的核心,它不依赖于视图或控制器,独立地管理数据和业务逻辑。
  2. 视图(View)
    • 视图是用户界面的表示。
    • 它负责将模型中的数据以可视化的方式呈现给用户。
    • 视图通常是被动的,不处理业务逻辑,只负责展示数据。
  3. 控制器(Controller)
    • 控制器充当模型和视图之间的中间人,负责处理用户的输入和操作。
    • 它接收用户的请求,并根据请求调用适当的模型来处理数据和逻辑。
    • 控制器还负责将模型的数据更新反映到视图上。

MVC 架构的优点包括:

  • 分离关注点:MVC 将应用程序的不同方面(数据、显示、控制逻辑)分离,使代码更易于维护和测试。
  • 可扩展性:由于模块化和分层的设计,MVC 架构允许开发人员独立地修改和扩展各个组件。
  • 代码复用:模型和视图可以在不同的应用程序场景中重用,提高了开发效率。
  • 可测试性:MVC 架构使单元测试和集成测试更容易进行,因为各个组件的职责清晰明确。

需要注意的是,MVC 是一种架构模式,而不是具体的技术或框架。许多编程语言和框架都支持 MVC 架构,例如,ASP.NET MVC、Ruby on Rails、Spring MVC 等。

Node.js 也可以使用 MVC 架构模式来组织和设计应用程序的结构。尽管 Node.js 本身并没有内置的 MVC 框架,但你可以使用许多第三方模块和框架来实现 MVC 架构。

  1. Express.js:Express.js 是一个流行的轻量级 Node.js Web 框架,它提供了灵活的路由、中间件和视图模板等功能,可以很好地支持 MVC 架构的开发。
  2. Koa:Koa 是由 Express.js 团队创建的下一代 Node.js Web 框架,它采用了更现代的异步函数(async/await)风格,同样可以用于构建 MVC 应用程序。

登录鉴权

cookie和session

Cookie 作为客户端存储会话标识符的机制,而 Session 则是服务器端存储会话信息的机制。它们配合使用,确保用户身份的验证和会话状态的维持。

token

客户端发起请求,后端校验没问题的话使用jwt工具类生成token返回给客户端

用户下次登录会携带token,后端使用拦截器或过滤器对token进行校验

文件上传管理

Multer 是一个 node.js 中间件,用于处理 multipart/form-data 类型的表单数据,它主要用于上传文件。

安装:

yarn add multer

测试:

const express = require('express');
const multer = require('multer');// 创建 Express 应用
const app = express();// 配置 multer 中间件
const storage = multer.diskStorage({destination: (req, file, cb) => {// 指定上传文件的保存目录cb(null, 'uploads/');},filename: (req, file, cb) => {// 指定上传文件的文件名cb(null, file.originalname);}
});
const upload = multer({ storage });// 处理文件上传的 POST 请求
app.post('/upload', upload.single('file'), (req, res) => {// 上传文件的处理逻辑// req.file 包含了上传的文件信息res.send('文件上传成功');
});// 启动服务器
app.listen(3000, () => {console.log('服务器已启动,监听端口 3000');
});

在上述代码中,我们定义了 storage 对象,其中包含了 destinationfilename 属性,它们都是函数。这些函数在执行时,接收三个参数:req(请求对象)、file(上传的文件对象)和 cb(回调函数)。

cb 函数需要在适当的时机被调用,以通知 multer 文件上传的状态和结果。通过调用 cb 函数,我们可以将错误对象或者指定的值传递给 multer,以便它能够继续处理文件上传过程。

const upload = multer({ storage }) 的意思是创建一个 multer 实例,并将配置对象 storage 传递给它。这样可以创建一个处理文件上传的中间件,我们可以在路由处理程序中使用 upload 中间件来处理文件上传的请求。

image-20230708164623672

Koa2

Koa2 是一个基于 Node.js 的 Web 框架,它提供了一套优雅的中间件(middleware)机制,用于处理 Web 请求和响应。

1.安装 Koa2 :
yarn add koa2.创建 Koa2 应用:
const Koa = require('koa');
const app = new Koa();3.编写中间件:
Koa2 使用中间件机制来处理请求和响应。
中间件是一个函数,它可以访问请求对象和响应对象,并执行一些操作。
Koa2 中间件通过 app.use() 方法注册。
以下是一个简单的示例,展示了一个中间件的使用:
app.use(async (ctx, next) => {// 执行一些操作// ...// 调用下一个中间件await next();
});4.处理路由:
Koa2 不内置路由功能,可以使用第三方模块(如 koa-router)来处理路由。
安装 koa-router 模块:
yarn add koa-router使用 koa-router 创建和处理路由:
const Router = require('koa-router');
const router = new Router();
// 定义路由
router.get('/', async (ctx) => {ctx.body = 'Hello, Koa!';
});
// 注册路由中间件
app.use(router.routes());5.启动服务器:
使用 app.listen() 方法来启动服务器,并监听指定的端口:
const port = 3000;
app.listen(port, () => {console.log(`Server running on port ${port}`);
});

疑问1:在 Koa2 中,中间件函数通常会接收两个参数:ctxnext

  • ctx(Context):ctx 是一个上下文对象,它封装了请求和响应的相关信息。通过 ctx,你可以访问请求的头部、参数、URL 等信息,并设置响应的状态码、头部、内容等。ctx 提供了许多属性和方法来操作请求和响应。
  • nextnext 是一个函数,用于调用下一个中间件函数。当一个中间件完成它的任务后,可以通过调用 next() 来将控制权移交给下一个中间件函数。如果不调用 next(),后续的中间件将不会执行。
  • 在中间件函数中,asyncawait 是用于处理异步操作的关键字。通过将中间件函数声明为 async,可以在其中使用 await 来等待异步操作的完成。

疑问2:await意思是让异步变成同步了吗?

实际上,await 并不会将异步操作变成同步操作。它只是用于在异步操作执行完成之前,暂停函数的执行,并等待异步操作的结果。

需要注意的是,只有在 async 函数内部才能使用 await 关键字,而且 async 函数本身也是异步的,它返回一个 Promise 对象,因此调用 async 函数时需要使用 await 或者处理返回的 Promise 对象。

疑问3:promise是什么东西?

Promise 是 JavaScript 中用于处理异步操作的一种机制,它提供了一种更优雅和可靠的方式来处理异步任务的结果。

在传统的回调函数模式中,异步操作的结果通常通过回调函数传递。这种方式可能导致回调地狱(callback hell)的问题,代码可读性较差,逻辑复杂。而 Promise 提供了一种链式调用的方式,使得异步操作的处理更加清晰和可维护。

疑问4:回调地狱什么意思

回调地狱(Callback Hell)指的是在异步编程中,多层嵌套的回调函数形成的一种代码结构,导致代码可读性差、难以理解和维护的问题。

当有多个异步操作需要依次执行时,往往会出现回调函数嵌套的情况。每个异步操作的回调函数又会包含另一个异步操作,这样就形成了嵌套的回调函数结构,导致代码缩进层级过深,难以跟踪和理解。

例如,以下是一个简化的回调地狱示例:

asyncOperation1(function(result1) {asyncOperation2(result1, function(result2) {asyncOperation3(result2, function(result3) {// ... 更多的回调嵌套});});
});

这种回调地狱的代码结构很难阅读和维护,因为代码逻辑被分散在多个回调函数中,而且代码缩进深,增加了理解的难度。此外,错误处理也变得复杂,很难在合适的位置捕获和处理错误。

疑问5:通过将中间件函数声明为 async,可以在其中使用 await 来等待异步操作的完成。但是很多地方没有使用await啊,它怎么处理异步操作结束后的结果呢?

当中间件函数中没有使用 await 关键字时,它仍然是一个异步函数,它会返回一个 Promise 对象。在 Koa2 中,中间件函数可以返回一个 Promise 对象,用于处理异步操作的结果。如果没有使用 await,则返回的 Promise 对象会立即被解析,不会等待异步操作的完成。

你可以通过在中间件函数中使用 .then().catch() 方法来处理 Promise 对象的结果。

例如,以下是一个示例代码:

app.use((ctx, next) => {// 异步操作const promise = someAsyncFunction();promise.then((result) => {// 处理异步操作成功的结果ctx.body = result;// 调用下一个中间件return next();}).catch((error) => {// 处理异步操作失败的结果console.error(error);// 响应错误状态码ctx.status = 500;});
});

在上述示例中,someAsyncFunction() 返回一个 Promise 对象。我们通过调用 .then() 方法来处理异步操作成功的结果,通过调用 .catch() 方法来处理异步操作失败的结果。

.then() 方法中,我们可以设置相应的响应体(ctx.body),并通过调用 next() 来调用下一个中间件。在 .catch() 方法中,我们可以处理错误情况,并设置相应的响应状态码。

即使没有使用 await,仍然可以通过 Promise 的方法链式调用来处理异步操作的结果。这样可以保持代码的可读性,并且依然能够处理异步操作的成功和失败情况。

Mysql

身为后端开发人员,mysql我可太熟悉了,这里简单介绍下node操作Mysql的过程。

const mysql = require('mysql2');// 创建数据库连接
const connection = mysql.createConnection({host: '127.0.0.1',user: 'root',password: '123456',database: 'guli',
});// 连接数据库
connection.connect((err) => {if (err) {console.error('Failed to connect to MySQL:', err);return;}console.log('Connected to MySQL');
});// 创建数据
const createData = (data) => {const sql = 'INSERT INTO nodejs_test (name, age) VALUES (?, ?)';const values = [data.name, data.age];connection.query(sql, values, (err, result) => {if (err) {console.error('Failed to create data:', err);return;}console.log('Data created:', result);});
};// 读取数据
const readData = () => {const sql = 'SELECT * FROM nodejs_test';connection.query(sql, (err, results) => {if (err) {console.error('Failed to read data:', err);return;}console.log('Data read:', results);});
};// 更新数据
const updateData = (id, data) => {const sql = 'UPDATE nodejs_test SET name = ?, age = ? WHERE id = ?';const values = [data.name, data.age, id];connection.query(sql, values, (err, result) => {if (err) {console.error('Failed to update data:', err);return;}console.log('Data updated:', result);});
};// 删除数据
const deleteData = (id) => {const sql = 'DELETE FROM nodejs_test WHERE id = ?';const values = [id];connection.query(sql, values, (err, result) => {if (err) {console.error('Failed to delete data:', err);return;}console.log('Data deleted:', result);});
};// 调用相应的函数执行 CRUD 操作
createData({ name: 'zs', age: '22' });
readData();
updateData(1, { name: 'zs', age: '23' });
deleteData(1);// 断开数据库连接
connection.end((err) => {if (err) {console.error('Failed to disconnect from MySQL:', err);return;}console.log('Disconnected from MySQL');
});

Socket编程

WebSocket 服务器端代码:

javascriptCopy code
const WebSocket = require('ws');// 创建 WebSocket 服务器
const wss = new WebSocket.Server({ port: 8080 });// 监听连接事件
wss.on('connection', (ws) => {console.log('Client connected');// 监听消息事件ws.on('message', (message) => {console.log('Received message from client:', message);});// 监听关闭事件ws.on('close', () => {console.log('Client disconnected');});
});

WebSocket 客户端代码:

javascriptCopy code
const WebSocket = require('ws');// 创建 WebSocket 客户端连接
const socket = new WebSocket('ws://localhost:8080');// 监听连接事件
socket.onopen = () => {console.log('Connected to server');// 发送消息到服务器socket.send('Hello from client');
};// 监听消息事件
socket.onmessage = (event) => {console.log('Received message from server:', event.data);
};// 监听关闭事件
socket.onclose = () => {console.log('Disconnected from server');
};

在上述代码中,我们使用 ws 模块创建了一个 WebSocket 服务器,并监听在端口 8080。当客户端连接到服务器时,会触发 connection 事件,在事件处理程序中监听客户端的消息和关闭事件。

客户端通过创建一个 WebSocket 客户端连接,连接到服务器的 WebSocket 端口。在连接建立后,客户端发送一条消息到服务器,并监听服务器发送的消息和关闭事件。

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

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

相关文章

听歌识曲--用python实现一个音乐检索器的功能

作者&#xff1a;唯心不易 字体&#xff1a;[增加 减小] 类型&#xff1a;转载 时间&#xff1a;2016-11-15 我要评论 本篇文章中主要介绍了用python实现一个音乐检索器&#xff0c;类似于QQ音乐的摇一摇识曲&#xff0c;有兴趣的同学可以了解一下。 听歌识曲&#xff0c;顾名思…

教你如何用Python抓取QQ音乐歌单及分析

学了python也有一段时间了&#xff0c;对于爬虫&#xff0c;后端的框架也有一些了解&#xff0c;但是都是在学习的时候跟着别人写的&#xff0c;感觉都不是自己的知识一样。我去年就给网易云音乐提了一个建议&#xff0c;就是通过播放量或者一个受欢迎程度来排序&#xff0c;然…

用python爬取音乐APP歌单

这篇文章&#xff0c;我们就来讲讲怎样爬取歌单&#xff0c;并且播放量从高到低排列&#xff0c;下面是爬取结果 一 核心代码如下 1.需要导入的包有 from urllib import parse from lxml import etree from urllib3 import disable_warnings import requests2.设置请求头部信息…

这几款音乐人必备的软件,你了解吗?

近几年随着各种关于音乐的综艺节目和自媒体盛行&#xff0c;音乐变得更加贴近民众。除了歌手以外的&#xff0c;音乐生产工作中的其他角色&#xff0c;例如编曲师、混音师、母带师等也渐渐走进大众的视野。 EarMaster&#xff1a;souurl.cn/b6rSSt Guitar Pro&#xff1a;sou…

某某星图sign参数解密分析

​ 大家好&#xff0c;我是TheWeiJun&#xff0c;欢迎来到我的公众号。今天给大家带来星图sign参数的解密分析&#xff0c;希望大家能够喜欢。如果你觉得我的文章内容有用&#xff0c;记得点赞&#x1f44d;&#x1f3fb;关注&#xff01; 特别声明&#xff1a;本公众号文章…

苹果发布黄色版 iPhone 14,定价 5999 元起;大神李沐被曝离职投身大模型创业;Atlassian 裁员 |极客头条

「极客头条」—— 技术人员的新闻圈&#xff01; CSDN 的读者朋友们早上好哇&#xff0c;「极客头条」来啦&#xff0c;快来看今天都有哪些值得我们技术人关注的重要新闻吧。 整理 | 梦依丹 出品 | CSDN&#xff08;ID&#xff1a;CSDNnews&#xff09; 一分钟速览新闻点&…

神经网络中的损失函数

在《神经网络中常见的激活函数》一文中对激活函数进行了回顾&#xff0c;下图是激活函数的一个子集—— 而在神经网络领域中的另一类重要的函数就是损失函数&#xff0c;那么&#xff0c;什么是损失函数呢&#xff1f; 损失函数是将随机事件或其有关随机变量的取值映射为非负实…

Sam Altman 最新访谈:谈谈 AI 的未来,还有中国 AI 发展现状

Sam Altman最新访谈:这次他更结构化地谈了AI的未来,还有中国AI现状 目录 Sam Altman最新访谈:这次他更结构化地谈了AI的未来,还有中国AI现状 划重点:

苹果发布黄色版 iPhone 14,定价 5999 元起;大神李沐被曝离职投身大模型创业;Atlassian 裁员 |极客头条...

「极客头条」—— 技术人员的新闻圈&#xff01; CSDN 的读者朋友们早上好哇&#xff0c;「极客头条」来啦&#xff0c;快来看今天都有哪些值得我们技术人关注的重要新闻吧。 整理 | 梦依丹 出品 | CSDN&#xff08;ID&#xff1a;CSDNnews&#xff09; 一分钟速览新闻点&#…

正在改变一切的Ai聊天机器人

ChatGPT通过Al生成的内容让我们眼花缭乱&#xff0c;从一个聊天机器人转化成推动一个创新时代的技术&#xff0c;但&#xff0c;也并非人人都知道&#xff0c;那么&#xff0c;你知道Chatgpt到底是什么吗&#xff1f; 文章目录 前言一、什么是Chatgpt&#xff1f;二、如何使用C…

人工智能、机器学习、深度学习、神经网络概念说明

目录 1.机器学习的范围2.机器学习的方法2.1回归算法2.2神经网络2.3 SVM&#xff08;支持向量机&#xff09;2.4聚类算法2.5降维算法2.6推荐算法2.7总结 3 机器学习的分类3.1 监督学习3.2 无监督学习3.3 强化学习 4 机器学习模型的评估5 机器学习的应用 6 机器学习的子类--深度学…

chatgpt赋能python:处理超大文本文件的Python技巧

处理超大文本文件的Python技巧 在今天互联网时代&#xff0c;我们每天都会处理大量的数据。有些数据集非常庞大&#xff0c;可能包含数百万行甚至数十亿行。其中最常见的就是文本文件。如何在Python中高效地处理超大文本文件呢&#xff1f;在本文中&#xff0c;我们将提供一些…

chatgpt赋能python:Python文本段落提取:优化文章阅读体验

Python文本段落提取&#xff1a;优化文章阅读体验 在阅读长篇文章时&#xff0c;我们往往需要快速地找到自己感兴趣的内容&#xff0c;这就需要滚动查找。但是如果一篇文章没有明确的章节分析和标记&#xff0c;那么这个查找过程就会变得非常麻烦。因此&#xff0c;一些文本段…

chatgpt赋能python:Python文本纠错:改善你的SEO和文字质量

Python文本纠错&#xff1a;改善你的SEO和文字质量 作为一名有10年Python编程经验的工程师&#xff0c;无论是对于数据分析还是自然语言处理&#xff0c;我都有自己独特的编程技巧和工具。在这篇文章里&#xff0c;我将和大家分享使用Python进行文本纠错&#xff0c;从而改善你…

机器学习面试手册.PDF

点击上方“AI遇见机器学习”&#xff0c;选择“星标”公众号 第一时间获取价值内容 分享《机器学习面试手册》 &#xff0c;文末提供 高清PDF下载方式。 内容概览 Preparing for Coding Interviews Imbalanced Data in Classification Bayes Theorem and Classifier Convolutio…

最难毕业季,会Python简直可以开挂!

上半年&#xff0c;“史上最多毕业生1076万”冲上热搜&#xff0c;阅读量达4.6亿次&#xff0c;众多应届大学生感叹想要成为打工人竟如此艰难&#xff01; 不仅如此&#xff0c;前几天上线的《怎么办&#xff0c;脱口秀大会》中&#xff0c;知名辩手、武汉大学新闻与传播学院讲…

Midjourney入门指南:简单提示词,搞定高质量应用设计

Midjourney是一款文本到图像的AI工具&#xff0c;可以根据纯文本描述生成图片&#xff0c;例如UI屏幕、应用程序图标、产品图片、标志和吉祥物等。虽然它不能替代UI设计师&#xff0c;但它可以在产品设计和视觉探索的早期阶段成为有帮助的工具。要使用Midjourney&#xff0c;您…

Google公布2022年度最热门Chrome扩展

Google 从去年开始一直在大力推行 Manifest V3 扩展&#xff0c;虽然遭到了不少外部阻力&#xff0c;但这并没有影响 Chrome 扩展程序生态的繁荣。那么在过去一年&#xff0c;有哪些扩展脱颖而出&#xff0c;获得了 Google 和用户的青睐呢&#xff1f;日前&#xff0c;Google 就…

《写给Python程序员的GPT指南》.pdf

点击上方“Python与机器智能”&#xff0c;选择“星标”公众号 第一时间获取价值内容 分享新书&#xff1a;《写给Python程序员的GPT指南》 &#xff0c;文末提供高清PDF下载&#xff01; 通过学习&#xff0c;可灵活使用OpenAI GPT-3、GPT-4、DALLE 2等模型开发AI应用&#xf…

【壁纸】(可商用) 70枚壁纸高清免费

小样上有分辨率尺寸&#xff0c;可以参考&#xff0c;欢迎下载。 查看付费内容 文字20个图片6张 评论推荐Ta