前言
文章详情 PayPal 为什么从 Java 迁移到 Node.js:性能提高一倍,文件代码减少44%-阿里云开发者社区 (aliyun.com)
其实这个一直让我很纠结,技术的选型,各种语言的demo都测试过,从php,java,python,golang再到nodejs,让我感觉java是最健壮的,但是我个人的技能树目前大概率是前端无疑了,而前端nodejs永远是一个加分项
目前的状态是什么都会,又什么都不精,我选择的路是做一个全栈开发工程师,难道错了吗?
要么使用现有的工具,要么造一个能发版的工具
震惊,大佬们都在用!node.js十个成功的大项目 - 干货分享 - HR老王说求职 (codeforest.cn)
后端总体设计模型
单台机器使用pm2将进程开到最大
多个服务器开启并行服务作为容灾
所有的请求方式使用封装请求,先请求主服务器1秒内没有做出回应或发生错误就请求容灾服务器1,容灾服务器1.5秒内无响应或错误,启动容灾服务器2,以此类推,每个容灾服务器都拥有完整的服务,区别在于性能不同,编号越是往前性能越好
nodejs爬虫
看到这我都惊呆了!!nodejs对于浏览器的亲和度简直就是亲生儿子,卧槽,我高低要试试用nodejs写个爬虫脚本试试
(55条消息) Node.js实现简单爬虫 讲解_拾荒李的博客-CSDN博客_nodejs爬虫
npm
将npm列表保存到文件 | (1r1g.com)
npm list -json > npmlist.json
npm list -json -g > npmlist.json
但是这上面只有这么列出安装包却没有说这么安装安装包
前置话题
框架选择
1 搭建express项目
1.1 安装webstrom
1.1.1 使用webstrom创建项目
使用webstrom创建项目是最佳的选择
1.1.2使用npm命令创建项目
参照文档进行
安装 Express - Express 中文文档 | Express 中文网 (expressjs.com.cn)
npm install express --save
express 项目名称
cd 项目名称
npm install
启动
npm start
1.1.3 查看express版
npm list express
1.1.4 修改启动端口
(55条消息) 修改express的端口,指定端口号_小飞飞¹²³的博客-CSDN博客_express 修改端口
app.js中添加代码
process.env.PORT = 2000;
1.1.5 识别@路径
https://www.cnblogs.com/hanzhe/articles/15712736.html
在项目根目录创建jsconfig.json就行
{
“compilerOptions”: {
“baseUrl”: “./”,
“paths”: {
“@/": ["src/”]
}
},
“exclude”: [“node_modules”, “dist”]
}
1.2 热更新
在开发中使用热更新是极其有必要的
(55条消息) express 开发 热更新_peade的博客-CSDN博客_express 热更新
一定要加 -D ,因为这个包中有.exe文件,会和pkg包冲突导致无法打包
(55条消息) npm安装时-S -D分别的意思_阿川阿川的博客-CSDN博客
-S
即–save(保存)
包名会被注册在package.json的dependencies里面,在生产环境下这个包的依赖依然存在
-D
即–dev(开发)
包名会被注册在package.json的devDependencies里面,仅在开发环境下存在的包用-D,如babel,sass-loader这些解析器
注意:在使用npm install一个插件的时候,需要添加上-s或-d,不然不会再package.json中显示。
npm install node-dev -D
// package.json 里的script中,配置
“dev”: “node-dev ./bin/www”
// 启动项目
npm run dev
1.3打包成exe文件
将文件打包成exe可以非常方便的发布
nodejs打包成exe - 简书 (jianshu.com)
1.3.1 安装pkg包
npm install pkg
或者全局安装
npm install -g pkg
1.3.2 打包node项目
pkg -t win 启动页.js
1.3.3 打包express项目
打包express项目稍有不同,其打包的是package.json文件,并且在打包前要对package.json文件进行修改才能完成打包
1.3.3.1 修改package.json
关于修改,还是要看看打包博客
(55条消息) pkg学习–使用pkg打包应用_zoepriselife316的博客-CSDN博客_pkg 打包
添加,根据实际的项目添加文件夹即可
“bin”: “./bin/www”,
“pkg”: {
“assets”: [
“public//*",
"views//*”
]
},
打包
pkg -t win package.json
打包后生成exe文件
运行成功示例
1.4 使用@作为路径
通常情况下,没有@作为主路径引入文件是极其痛苦的事情
使用了@后,引入文件极其快乐
(55条消息) Node项目使用@代替根目录_少年码农历险记的博客-CSDN博客_node 根目录
1.4.1 安装 module-alias
npm i --save module-alias
配置package.json
“//配置路径简写//”: “https://blog.csdn.net/qq_42376617/article/details/108919255”,
“_moduleAliases”: {
“@”: “.”,
“public”: “./public”,
“public_js”: “./public/javascripts”,
“public_img”: “./public/javascripts”,
“public_css”: “./public/javascripts”,
“gaojian”: “./www/www.xn–nyqx82p.com”
},
“_moduleDirectories”: [
“node_modules_custom”
],
待解决:webstrom无法识别@路径
使用@路径方便是真的方便,但是也产生了新的问题,webstrom无法识别配置后的路径,目前没有好的解决办法
(55条消息) 解决WebStorm 2021无法识别@(别名路径)的问题_沉默的熊4531的博客-CSDN博客_webstorm 无法解析@
1.5 pm2服务器部署项目
PM2 是一个带有负载均衡功能的 Node 应用的进程管理器。
还有一个特殊的启动方式
forever 后台启动nodejs_猿大师的博客-CSDN博客_forever nodejs
全局安装
npm install forever -g
其他具体的操作可以去看网址介绍
启动项目
pm2启动express项目 - 爱码网 (likecs.com)
pm2 start ./bin/www --name=“自己能认识的名称”
pm2 start ./bin/www --name=“nodejs生产环境”
pm2 start ./bin/www --name=“nodejs生产环境” -i 4
(60条消息) npm pm2 的安装及常用命令_雲小妖的博客-CSDN博客_npm pm2
命令行安装 pm2
npm install pm2 -g
#后台运行pm2,启动4个app.js ( # 也可以把’max’ 参数传递给 start,# 正确的进程数目依赖于Cpu的核心数目)
pm2 start app.js -i 4
命名进程
pm2 start app.js --name my-api
pm2 list # 显示所有进程状态
pm2 monit # 监视所有进程
pm2 logs # 显示所有进程日志
pm2 stop all # 停止所有进程
pm2 restart all # 重启所有进程
pm2 reload all # 0秒停机重载进程 (用于 NETWORKED 进程)
pm2 stop 0 # 停止指定的进程
pm2 restart 0 # 重启指定的进程
pm2 startup # 产生 init 脚本 保持进程活着
pm2 web # 运行健壮的 computer API endpoint (http://localhost:9615)
pm2 delete 0 # 杀死指定的进程
pm2 delete all # 杀死全部进程
随便使用了一个pm2 monit ,实在是让我惊叹
-h
Usage: pm2 [cmd] app
Options:
-V, --version output the version number
-v --version print pm2 version
-s --silent hide all messages
--ext <extensions> watch only this file extensions
-n --name <name> set a name for the process in the process list
-m --mini-list display a compacted list without formatting
--interpreter <interpreter> set a specific interpreter to use for executing app, default: node
--interpreter-args <arguments> set arguments to pass to the interpreter (alias of --node-args)
--node-args <node_args> space delimited arguments to pass to node
-o --output <path> specify log file for stdout
-e --error <path> specify log file for stderr
-l --log [path] specify log file which gathers both stdout and stderr
--filter-env [envs] filter out outgoing global values that contain provided strings (default: )
--log-type <type> specify log output style (raw by default, json optional)
--log-date-format <date format> add custom prefix timestamp to logs
--time enable time logging
--disable-logs disable all logs storage
--env <environment_name> specify which set of environment variables from ecosystem file must be injected
-a --update-env force an update of the environment with restart/reload (-a <=> apply)
-f --force force actions
-i --instances <number> launch [number] instances (for networked app)(load balanced)
--parallel <number> number of parallel actions (for restart/reload)
--shutdown-with-message shutdown an application with process.send('shutdown') instead of process.kill(pid, SIGINT)
-p --pid <pid> specify pid file
-k --kill-timeout <delay> delay before sending final SIGKILL signal to process
--listen-timeout <delay> listen timeout on application reload
--max-memory-restart <memory> Restart the app if an amount of memory is exceeded (in bytes)
--restart-delay <delay> specify a delay between restarts (in milliseconds)
--exp-backoff-restart-delay <delay> specify a delay between restarts (in milliseconds)
-x --execute-command execute a program using fork system
--max-restarts [count] only restart the script COUNT times
-u --user <username> define user when generating startup script
--uid <uid> run target script with <uid> rights
--gid <gid> run target script with <gid> rights
--namespace <ns> start application within specified namespace
--cwd <path> run target script from path <cwd>
--hp <home path> define home path when generating startup script
--wait-ip override systemd script to wait for full internet connectivity to launch pm2
--service-name <name> define service name when generating startup script
-c --cron <cron_pattern> restart a running process based on a cron pattern
-c --cron-restart <cron_pattern> (alias) restart a running process based on a cron pattern
-w --write write configuration in local folder
--no-daemon run pm2 daemon in the foreground if it doesn't exist already
--source-map-support force source map support
--only <application-name> with json declaration, allow to only act on one application
--disable-source-map-support force source map support
--wait-ready ask pm2 to wait for ready event from your app
--merge-logs merge logs from different instances but keep error and out separated
--watch [paths] watch application folder for changes (default: )
--ignore-watch <folders|files> List of paths to ignore (name or regex)
--watch-delay <delay> specify a restart delay after changing files (--watch-delay 4 (in sec) or 4000ms)
--no-color skip colors
--no-vizion start an app without vizion feature (versioning control)
--no-autorestart start an app without automatic restart
--no-treekill Only kill the main process, not detached children
--no-pmx start an app without pmx
--no-automation start an app without pmx
--trace enable transaction tracing with km
--disable-trace disable transaction tracing with km
--sort <field_name:sort> sort process according to field's name
--attach attach logging after your start/restart/stop/reload
--v8 enable v8 data collecting
--event-loop-inspector enable event-loop-inspector dump in pmx
--deep-monitoring enable all monitoring tools (equivalent to --v8 --event-loop-inspector --trace)
-h, --help output usage information
Commands:
start [options] [name|namespace|file|ecosystem|id...] start and daemonize an app
trigger <id|proc_name|namespace|all> <action_name> [params] trigger process action
deploy <file|environment> deploy your json
startOrRestart <json> start or restart JSON file
startOrReload <json> start or gracefully reload JSON file
pid [app_name] return pid of [app_name] or all
create return pid of [app_name] or all
startOrGracefulReload <json> start or gracefully reload JSON file
stop [options] <id|name|namespace|all|json|stdin...> stop a process
restart [options] <id|name|namespace|all|json|stdin...> restart a process
scale <app_name> <number> scale up/down a process in cluster mode depending on total_number param
profile:mem [time] Sample PM2 heap memory
profile:cpu [time] Profile PM2 cpu
reload <id|name|namespace|all> reload processes (note that its for app using HTTP/HTTPS)
id <name> get process id by name
inspect <name> inspect a process
delete|del <name|id|namespace|script|all|json|stdin...> stop and delete a process from pm2 process list
sendSignal <signal> <pm2_id|name> send a system signal to the target process
ping ping pm2 daemon - if not up it will launch it
updatePM2 update in-memory PM2 with local PM2
update (alias) update in-memory PM2 with local PM2
install|module:install [options] <module|git:/> install or update a module and run it forever
module:update <module|git:/> update a module and run it forever
module:generate [app_name] Generate a sample module in current folder
uninstall|module:uninstall <module> stop and uninstall a module
package [target] Check & Package TAR type module
publish|module:publish [options] [folder] Publish the module you are currently on
set [key] [value] sets the specified config <key> <value>
multiset <value> multiset eg "key1 val1 key2 val2
get [key] get value for <key>
conf [key] [value] get / set module config values
config <key> [value] get / set module config values
unset <key> clears the specified config <key>
report give a full pm2 report for https://github.com/Unitech/pm2/issues
link [options] [secret] [public] [name] link with the pm2 monitoring dashboard
unlink unlink with the pm2 monitoring dashboard
monitor [name] monitor target process
unmonitor [name] unmonitor target process
open open the pm2 monitoring dashboard
plus|register [options] [command] [option] enable pm2 plus
login Login to pm2 plus
logout Logout from pm2 plus
dump|save [options] dump all processes for resurrecting them later
cleardump Create empty dump file
send <pm_id> <line> send stdin to <pm_id>
attach <pm_id> [comman] attach stdin/stdout to application identified by <pm_id>
resurrect resurrect previously dumped processes
unstartup [platform] disable the pm2 startup hook
startup [platform] enable the pm2 startup hook
logrotate copy default logrotate configuration
ecosystem|init [mode] generate a process conf file. (mode = null or simple)
reset <name|id|all> reset counters for process
describe <name|id> describe all parameters of a process
desc <name|id> (alias) describe all parameters of a process
info <name|id> (alias) describe all parameters of a process
show <name|id> (alias) describe all parameters of a process
env <id> list all environment variables of a process id
list|ls list all processes
l (alias) list all processes
ps (alias) list all processes
status (alias) list all processes
jlist list all processes in JSON format
sysmonit start system monitoring daemon
slist|sysinfos [options] list system infos in JSON
prettylist print json in a prettified JSON
monit launch termcaps monitoring
imonit launch legacy termcaps monitoring
dashboard|dash launch dashboard with monitoring and logs
flush [api] flush logs
reloadLogs reload all logs
logs [options] [id|name|namespace] stream logs file. Default stream all logs
kill kill daemon
pull <name> [commit_id] updates repository for a given app
forward <name> updates repository to the next commit for a given app
backward <name> downgrades repository to the previous commit for a given app
deepUpdate performs a deep update of PM2
serve|expose [options] [path] [port] serve a directory over http via port
autoinstall
examples display pm2 usage examples
*
2 express项目基础
2.1 路由
2.2.1 添加路由
2.1.1 中间件
NodeJS 中间件机制 - 简书 (jianshu.com)
(55条消息) 中间件的分类_Blizzard前端的博客-CSDN博客_中间件类型及版本
中间件的存在可以帮我们过滤掉恶意攻击,验证和通用操作等等,中间件可以在接口前调用也可以在接口后调用,类似洋葱模型
中间件这一部分是不难的,但是逻辑比较多,我总结的优先级为:
next()在末尾的通常情况下:
应用级中间件前>路由级中间件前>请求>路由级中间件后>应用级中间件后>错误级别中间件
但是,如果请求内容以next()为分界线输出内容则优先级别为:
应用级中间件前>路由级中间件前>请求next()前>路由级中间件后>请求next()后>级中间件后>错误级别中间件
当我将所有内容都以next()为界限分割后发现了一个特别有意思的事情:
总结:
next之后的内容调用的先后和调用之前的内容恰好完全相反,我们可以利用这个特性在全局中间件中极其方便的写入调用前和调用后的内容
2.1.1.1 应用级中间件
即在所有的请求前或请求后执行的中间件
// 使用@等作为便捷路径
require(‘module-alias/register’)
var createError = require(‘http-errors’);
var express = require(‘express’);
var path = require(‘path’);
var cookieParser = require(‘cookie-parser’);
var logger = require(‘morgan’);
var indexRouter = require(‘./routes/index’);
var usersRouter = require(‘./routes/users’);
// 高健的网站
var gaojianRouter = require(‘./www/www.xn–nyqx82p.com/routes/index’)
var app = express();
// view engine setup 视图引擎设置
app.set(‘views’, path.join(__dirname, ‘views’));
app.set(‘view engine’, ‘pug’);
app.use(logger(‘dev’));
app.use(express.json());
app.use(express.urlencoded({extended: false}));
app.use(cookieParser());
app.use(express.static(path.join(__dirname, ‘public’)));
// 应用级别中间件
app.use((req, res, next) => {
/*
* next()之前的操作,在请求之前触发,第一个触发
* /
console.log(“触发应用级别全局中间件1”)
next();
/
* next()之后的操作,在所有请求和中间件结束后,最后一个触发
* */
// console.log(“触发应用级别全局中间件1-next()之后的内容”)
})
app.use(‘/’, indexRouter);
app.use(‘/users’, usersRouter);
app.use(‘/gaojian’, gaojianRouter)
// usersRouter
// 应用级别中间件
app.use((req, res, next) => {
console.log(“触发应用级别全局中间件2”)
// next()
console.log(“触发应用级别全局中间件2-next()之后的内容”)
})
// catch 404 and forward to error handler 全局中间件
app.use(function (req, res, next) {
next(createError(404));
});
// error handler 错误级别中间件,系统错误时触发
app.use(function (err, req, res, next) {
// set locals, only providing error in development
console.log('发生了错误!' + err.message)
res.locals.message = err.message;
res.locals.error = req.app.get('env') === 'development' ? err : {};
// render the error page
res.status(err.status || 500);
res.render('error');
});
// 设置端口号
process.env.PORT = 3000;
module.exports = app;
2.1.1.2 路由级别的中间件
var express = require(‘express’);
var router = express.Router();
const app = express()
//应用级别的中间件(局部中间件)
// app.get(‘/’, mw1, (req, res) => {
// res.send(‘Home page.’)
// })
var npm = require(‘gaojian/public/javascripts/mac.js’);
var npm = require(‘@/node_modules/getmac’);
const getMAC = require(“getmac”);
//获取mac地址
// var mac = ‘’;
// // //获取机器mac地址
// npm.getMAC(function(err,macAddress){
// if (err) throw err;
// var mac = macAddress; //获取mac地址
// console.log(mac);
// });
// console.log(npm.getMAC(‘444’))
const interfaces = require(‘os’).networkInterfaces();
// console.log(“Mac================” + JSON.stringify(interfaces));
var os = require(“os”);
const net = require(“net”);
var mac = ‘’
var networkInterfaces = os.networkInterfaces();
for (var i in networkInterfaces) {
for (var j in networkInterfaces[i]) {
if (networkInterfaces[i][j][“family”] === “IPv4” && networkInterfaces[i][j][“mac”] !== “00:00:00:00:00:00” && networkInterfaces[i][j][“address”] !== “127.0.0.1”) {
mac = networkInterfaces[i][j][“mac”]
}
}
}
console.log(mac) //01:02:03:0a:0b:0c
var json = {
name: ‘gaojian’,
age: 25,
say: ‘my ini word’,
os: interfaces,
mac: mac
}
// 定义第一个全局中间件
const mw1 = function(req,res,next){
console.log(‘调用了第1个局部中间件’)
next()
console.log(‘调用了第1个局部中间件-next()之后的内容’)
}
router.use(mw1)
// 定义第二个全局中间件(简化)
router.use((req, res, next) => {
console.log(‘调用了第2个局部中间件’)
next()
console.log(‘调用了第2个局部中间件-next()之后的内容’)
})
/* GET users listing. */
router.all(‘/’, function (req, res, next) {
// console.log(“请求打印1”)
// res.send({ip: req.ip, …json,});
res.send({})
console.log(“请求打印”)
next();
// next() 之后的打印会在所有的中间件执行完后触发
console.log(“请求打印-next()之后的内容”)
});
router.use((req, res, next) => {
console.log(‘调用了第3个局部中间件’)
next()
console.log(‘调用了第3个局部中间件-next()之后的内容’)
})
router.use((req, res, next) => {
console.log(‘调用了第4个局部中间件’)
next()
})
module.exports = router;
2.1.1.3 错误级别的中间件
app.js中写,用于所有中间件的最后的底牌,用来防止系统崩溃
// error handler 错误级别中间件,系统错误时触发
app.use(function (err, req, res, next) {
// set locals, only providing error in development
console.log('发生了错误!' + err.message)
res.send('Error:' + err.message)
res.locals.message = err.message;
res.locals.error = req.app.get('env') === 'development' ? err : {};
// render the error page
res.status(err.status || 500);
res.render('error');
});
2.1.1.4 express内置的中间件
2.1.1.5 第三方中间件
没用过
2.1.2 获取参数
(56条消息) Express获取请求参数_疯狂的建波的博客-CSDN博客
2.1.2.1 获取get,post与路径参数
(56条消息) Node基础——express接口获取request参数的三种方式_甜甜酷盖的博客-CSDN博客_express 获取参数
var express = require(‘express’);
var router = express.Router();
const app = express()
// http://127.0.0.1:3000/gj?name=gaojian&age=9
router.all(‘/’, function(req, res, next) {
res.send(req.query);
console.log(req.query);
})
// http://127.0.0.1:3000/gj/index/gaojian/5444
router.all(‘/index/:name/:age’,(req,res) => {
// 响应给客户端
// 通过req.params获取请求参数
res.send(req.params);
console.log(req.params);
})
module.exports = router;
2.1.2.2 获取post参数
相比获取post参数获取比get有很大不同
(64条消息) express 解析post请求的数据格式_脑瓜不疼的博客-CSDN博客_express 解析post
(64条消息) axios POST提交数据的三种请求方式写法_wangwei830的博客-CSDN博客_axios post方法
(64条消息) nodejs body-parser中间件处理类型 multipart/form-data 的 POST 表单数据,req.body无法接收到数据_ray_zzzzz的博客-CSDN博客
首先post请求头有三种(实际使用是三种,总共有四种),而且奇葩的是使用不同的请求头就要不同包来解析
Content-Type: application/x-www-form-urlencoded
Content-Type: application/json
Content-Type: multipart/form-data
text/xml 这种格式需要额外下载包,express默认不支持
Content-Type: application/x-www-form-urlencoded
var bodyParser = require(‘body-parser’);
var app = express();
// 中间件
app.use(bodyParser.urlencoded({
extended:true
}));
Content-Type: application/json
var bodyParser = require(‘body-parser’);
var app = express();
// 中间件
app.use(bodyParser.json());
Content-Type: multipart/form-data
文件上传通常使用这种格式,理论上是用这个请求头就能满足一切post需求
var multipart = require(‘connect-multiparty’);
var app = express();
// 中间件
app.use(multipart(undefined));
解析参数
// files是上传文件的参数,body是字符串参数
res.send({…req.body,…req.files});
2.1.2.3 获取hearder参数
var express = require(‘express’);
var router = express.Router();
router.get(‘/header’,(req,res) => {
// req.get(‘Content-Type’)
res.send(req.headers);
console.log(req);
})
module.exports = router;
2.1.2.4 获取cookie参数
我才知道服务器端可以直接获取访问用户的cookie,如果是这样的话,可操作性就很高了
router.get(‘/cookie’,(req,res) => {
// req.get(‘Content-Type’)
res.send(res.json(req.cookies));
console.log(res.json(req.cookies));
})
2.1.2.5 获取session参数
2.1.3 设置请求频率
2.1.3.1 设置全局接口请求频率
2.1.3.2 设置单接口请求频率
2.1.4 RESTful路由风格
(56条消息) express请求与响应_lhrdlp的博客-CSDN博客_express 响应
相当于同一个路由,相同的参数,不同的请求方式,执行不同的结果,这样就完美的避免了路由泛滥,完美
设置后前端使用axios去请求
axios入门(1)—— axios常用五种请求方法介绍(get、post、put、patch、delete) - kwstu2018 - 博客园 (cnblogs.com)
1、get请求
用于获取数据。
2、post请求
用于提交数据(新建)、包括表单提交及文件上传。
3、put请求
用于更新数据(修改),将所有数据都推送到后端。
4、patch请求
用于更新数据(修改),只将修改的数据推送到后端。
5、delete请求
用于删除数据。
2.1.5 响应对象
(56条消息) express请求与响应_lhrdlp的博客-CSDN博客_express 响应
2.1.5.1 返回任意数据 send(data)
语法:
res.send(data) 可以返回任意类型的数据
参数:要响应的内容
//普通字符串
// res.send(‘首页’);
//JSON数据
// res.send({“name”:“why”,‘age’:18});
//普通文本
res.send(<h2>普通文本</h2>
);
注意:不能发送数字,如果发送发送数字会被当成状态码.
send(123),这样写会报错;
send() 方法只能出现一次,重复无效还报错,因为send内含end()结束响应
2.1.5.1.1 设置状态码
设置状态码并返回内容可以使用如下的方法:
res.status(201).send(‘我就是一串字符串’);
send方法的优点:
send 方法内部会自动检测要响应内容的类型;
send 方法会自动设置http状态码;
send 方法会自动帮我们设置响应内容的内容类型及编码.
2.1.5.2 直接返回文件 sendFile(path, callback)
响应文件
除了可以直接返回文件内容外还可以直接返回文件
语法:
res.sendFile( path, callback)
path: 返回文件的路径,需要注意的是这里的路径必须是绝对路径,否则会报错;
callback: 回调函数,接受一个参数
可以设置页面指向vue或react项目编译后的html页面
app.get(‘/’,(req,res)=> {
res.sendFile(${__dirname}/public/index2.html
,function (err) {
// console.log(arguments);
if(err) {
res.send(‘抱歉哦亲!你访问的页面不存在’)
}
res.send()
})
})
变为绝对路径的方法:
${__dirname}/public/index.html
path.join(__dirname,‘./public/index.html’)
2.1.5.3 返回json数据 json(jsonData)
我们除了可以利用send返回JSON数据外,还可以单独使用json方法响应JSON数据,返回JSON数据,会自动设置响应头
语法:
res.json(jsonData) // 返回json对象,一般针对ajax应用
app.get(‘/’,(req,res) =>{
// res.json(‘{“name”:“why”,“age”:18}’);
// 可以是真正的JSON数据
// res.json(JSON.stringify({name:“aa”}))
// 如果是对象会自动被转为JSON数据
res.json({name:"小明",age: 8})
});
也可以通过sendFile方法响应json文件
app.get(‘/’,(req,res) =>{
res.sendFile(${__dirname}/public/index.json
,(err) => {
if(err) {
res.send(‘你访问的页面不存在’);
return;
}
res.send();
})
})
2.1.5.4 重定向 redirect(301, “/user”)
重定向到指定的URL路径(浏览器地址变为设置的地址)
语法:
res.redirect(“/user”)
// 第一个参数也可以是状态码
res.redirect(301, “/user”)
这个方法最常见的是用于登录页面和用户页面的跳转
app.get(‘/user’,(req,res) => {
if(req.query.token == ‘why’) {
res.sendFile(${__dirname}/public/user.html
)
}else{
res.redirect(‘/login’);
}
})
app.get(‘/login’,(req,res) => {
res.sendFile(${__dirname}/public/login.html
)
})
当我们访问用户页面并且设置token=why时就会跳转到用户页面
当我们访问用户页面并且但不设置token时会自动跳转到login页面
2.1.5.5 下载 download(“./xxx.zip”)
语法:
res.download(“./xxx.zip”) 下载当前目录下的xxx.zip文件
//下载当前目录下的test.zip文件
app.get(‘/data’,(req,res) => {
res.download(‘./test.zip’);
})
2.1.5.6 结束响应 end()
结束响应顾名思义就是结束前后端之间连接的方法
res.end()
在结束响应时也可以发送数据,如:
res.end(‘响应结束’)
2.1.5.7 传送JSONP响应 jsonp(jsonData)
该方法用来传送JSONP响应.
语法:res.jsonp(jsonData)
jsonp返回给前端的依旧是json数据,但是前端在使用jsonp时需要路径传递上callback参数.
如果不传递得到的就是json数据.
如果传递了callback那么得到的就是以callback值为函数名执行
示例:
app.get(“/jsonp”,(req,res,next) => {
// console.log(1)
res.jsonp({name:‘why’})
})
3.x 模板
webstorm 编写pug与pug的编译配置 - 爱码网 (likecs.com)
3 typescript
让我万万没有想到的是typescript的安装和使用是如此的简单…
Express框架中使用TypeScript - 简书 (jianshu.com)
TypeScript是一种由微软开发的开源、跨平台的编程语言。它是JavaScript的超集,最终会被编译为JavaScript代码。
TypeScript的作者是安德斯·海尔斯伯格,C#的首席架构师。它是开源和跨平台的编程语言。它是JavaScript的一个超集,而且本质上向这个语言添加了可选的静态类型和基于类的面向对象编程。
TypeScript扩展了JavaScript的语法,所以任何现有的JavaScript程序可以运行在TypeScript环境中。TypeScript是为大型应用的开发而设计,并且可以编译为JavaScript。
3.1 安装
typescript的使用是:使用终端在编译端口
重点: 安装的时候出现: ‘tsc’ 不是内部或外部命令,也不是可运行的程序 或批处理文件。
网上有各种奇葩解决方案,什么重装啦,什么修改文件删除文件啦什么清除缓存啦,更离谱的设置什么path环境什么乱七八糟的,全部是错的,都没看到问题的本质
在踩了无数的坑在被网友的水文中反复蹂躏中我终于得出结论:
出现这种问题最大的原因是cmd的权限太低,没有权限去更改重写文件,只需要管理员启动powershell即可完美解决,百度误我
npm install typescript
3.2 使用
让我玩玩没想到的是typescript的使用是如此简单,网上的那些教程大多已经过时了
我们使用只需要达到两点:
1.批量编译文件
2.编译后文件的输出位置
然而上面两点只需要一个命令行就行
在项目的根目录,运行
tsc --init
会在页面生成tsconfig.json文件
3.2.1 启动自动编译
启动,启动后只要是项目中的ts文件改动了,就会自动在相同目录生成对应的js文件,方便至极
tsc -w
还能自己设置一些东西,但是一般不需要
tsconfig.json编译选项 - 月半弯儿 - 博客园 (cnblogs.com)
{
“compileOnSave”: false,
“compilerOptions”: {
// “outDir”: “./dist/”,//输出目录,一定不要写,不写会在原来的目录下产生js,否则全部转移到你写的目录里面,那是真的恶心
“baseUrl”: "./", //直接设置成 ./ 会将所有的文件夹遍历,一个个的去ts遍历,但是这样不知道能不能排除node_modules文件夹,就先这样用吧
“sourceMap”: false,//把 ts 文件编译成 js 文件的时候,同时生成对应的 map 文件
“removeComments”: true,//编译 js 的时候,删除掉注释
“declaration”: false,
“moduleResolution”: “node”,//模块的解析
“emitDecoratorMetadata”: true,//给源码里的装饰器声明加上设计类型元数据。查看issue #2577了解更多信息。
“experimentalDecorators”: true,//启用实验性的ES装饰器。
“target”: “es6”,//编译目标平台
//exclude:不包含的编译目录
//noImplicitAny:true/false;为 false 时,如果编译器无法根据变量的使用来判断类型时,将用 any 类型代替。为 true 时,进行强类型检查,会报错
“typeRoots”: [
“node_modules/@types”
],
“lib”: [//添加需要的解析的语法,否则TS会检测出错。
“es2016”,
“dom”
]
}
}
3.3 忽视错误
前端 - TS忽略类型检查(行、文件)_个人文章 - SegmentFault 思否
-
单行忽略(添加到特定行的行前来忽略这一行的错误)
// @ts-ignore -
跳过对某些文件的检查 (添加到该文件的首行才起作用)
// @ts-nocheck -
对某些文件的检查
// @ts-check
4 sass
Sass 教程 | 菜鸟教程 (runoob.com)
你也许不需要 devDependencies - 知乎 (zhihu.com)
5 less
更推荐less,主要原因是我看到在ssr框架官网中明确表明了使用的是less而不是sass
Less 快速入门 | Less.js 中文文档 - Less 中文网 (bootcss.com)
安装
npm install -g less
也可以安装在项目中
npm install -D less
单文件编译
lessc styles.less styles.css
批量编译
在项目根目录创建 less.js 文件,在文件中粘贴
/*
批量编译less为css
node less.js
*/
var fs = require(‘fs’),
path = require(‘path’),
exec = require(‘child_process’).exec,
sourcePath, targetPath;
//获取命令行中的路径
process.argv.forEach(function (val, index, array) {
if (index == 2) {
sourcePath = val;
}
if (index == 3) {
targetPath = val;
}
})
var lessc = function (rootPath, targetPath) {
//取得当前绝对路径
rootPath = path.resolve(rootPath);
//目标路径绝对路径
targetPath = path.resolve(targetPath);
//判断目录是否存在
fs.exists(rootPath, function (exists) {
//路径存在
if (exists) {
//获取当前路径下的所有文件和路径名
var childArray = fs.readdirSync(rootPath);
if (childArray.length) {
for (var i = 0; i < childArray.length; i++) {
var currentFilePath = path.resolve(rootPath, childArray[i]);
var currentTargetPath = path.resolve(targetPath, childArray[i])
//读取文件信息
var stats = fs.statSync(currentFilePath);
//若是目录则递归调用
if (stats.isDirectory()) {
lessc(currentFilePath, currentTargetPath);
} else {
//判断文件是否为less文件
if (path.extname(currentFilePath) === “.less”) {
var newFilePath = path.resolve(targetPath, path.basename(currentFilePath, ‘.less’) + “.css”);
if (!fs.existsSync(targetPath)) {
fs.mkdirSync(targetPath);
}
console.log(newFilePath);
exec("lessc -x " + currentFilePath + " > " + newFilePath);
}
}
}
}
} else {
console.log(“directory is not exists”);
}
});
}
lessc(‘./’, ‘./’);
node less.js
6 webpack
webpack 中文文档 | webpack 中文网 (webpackjs.com)
概念 | webpack 中文网 (webpackjs.com)
菜鸟教程的不对
Webpack 入门教程 | 菜鸟教程 (runoob.com)
typescript,sass,less,scss,等等一个个去编译是在太麻烦了,typescript还好一点,less批量编译简直要了老命,没法监听文件变化自动编译,
webpack还是必须要的
(57条消息) 使用webpack打包typescript_知了还没睡的博客-CSDN博客_webpack打包typescript
7 vite
使用插件 | Vite 官方中文文档 (vitejs.dev)
webpack还没搞明白,又多了个vite,具查到的资料显示与webpack的区别是vite是按需编译,编译要比webpack快,但是似乎生态webpack更多
要说通用还是得webpack比较稳啊,先看看实际情况谁好用吧
8 websoket
长连接解决方案,有了这个我就能开发出实时游戏,直播,聊天软件,单机游戏的联机模式等等,就几乎没有做不了的东西了,终于被我发现
以前这块一直是我最大的盲区 2022年8月12日 10:28:36
发现的过程都很传奇
从这里云服务器 | 菜鸟工具 (runoob.com)点击腾讯云服务器
搭建微信小程序服务:剪刀石头布小游戏 - 腾讯云实验室 (tencent.com)
看到websokit就百度了以下,发现 WebSocket 教程 - 阮一峰的网络日志 (ruanyifeng.com)
了解到这就是我一直寻找的方案,极度兴奋
webSocket粗谈 - 简书 (jianshu.com)
2022年10月24日 19:22:22
已经基本实现了前后端通信的基本demo哈哈哈哈,使用的是socket.io框架,是真的牛皮和方便
9 https
10 prototype 原型链
可以代替class,class是根据这个封装的语法糖
菜鸟教程在线编辑器 (runoob.com)
function Person (name) {
this.name = name
}
Person.prototype.new= function () {
console.log(${this.name} says hello
)
}
Person.prototype.talk = function () {
console.log(${this.name} says hello
)
}
new Person(‘高健’).new()
//
function Person(first, last, age, eye) {
this.firstName = first;
this.lastName = last;
this.age = age;
this.eyeColor = eye;
}
Person.prototype.name = function() {
return this.firstName + " " + this.lastName
};
var myFather = new Person(“John”, “Doe”, 50, “blue”);
document.getElementById(“demo”).innerHTML =
"我的父亲是 " + myFather.name();
defineProperty
极为重要的一个函数,有了这个我就再也不用担心自己在object原型链中添加方法背for in 枚举出来啦,开心
/*
- 怎么在原型链上添加属性且不会被for in 检索
- https://www.zhihu.com/question/64046262/answer/215893191
- https://blog.csdn.net/qq_54527592/article/details/119084505
- */
Object.defineProperty(Object.prototype, “x”, {
enumerable: false, //控制属性是否可以枚举,默认值是false, 妈妈再也不用担心被 for in 出来啦~
writable: false, //控制属性是否可以被修改,默认值是false
configurable: false, //控制属性是否可以被删除,默认值是false
value: function () {
console.log(‘aa小公主’, this)
}
});
({a: 8}).x()
// aa小公主 { a: 8 }
11 class
xxx 附录:一些增加效率的插件
xxx.1 json5
JSON5 - 更人性化的 JSON - 掘金 (juejin.cn)
序列化 配置 json json5 - 芒果鱼 - 博客园 (cnblogs.com)
xxx.2 获取mac地址
下面这玩意不准,但是说明了参数
node.js获取本机mac地址 | 码农家园 (codenong.com)
address 分配的 IPv4 或 IPv6 地址。
netmask
IPv4 或 IPv6 的子网掩码。
family
IPv4 或 IPv6。
mac
网络接口的 MAC 地址。
internal
如果网络接口是不可远程访问的环回接口或类似接口,则为 true,否则为 false。
scopeid
数值型的 IPv6 作用域 ID(仅当 family 为 IPv6 时指定)。
cidr
以 CIDR 表示法分配的带有路由前缀的 IPv4 或 IPv6 地址。如果 netmask 无效,则此属性会被设为 null。