From f98cb16b7caf3af65958bd1fb815a3a1c2e5d7c5 Mon Sep 17 00:00:00 2001 From: "@chen-li-xing" <19912973651> Date: Tue, 14 Mar 2023 09:29:53 +0800 Subject: [PATCH 01/20] xiuxiuxiu --- "47\346\235\216\345\201\245/.keep" | 0 .../2023-02-14-\346\250\241\345\235\227.md" | 31 ++++ ...72\346\234\254\346\250\241\345\235\227.md" | 84 ++++++++++ .../2023-02-17-http.md" | 46 ++++++ ...66\346\234\215\345\212\241\345\231\250.md" | 93 +++++++++++ .../2023-02-21.\351\242\230\347\233\256.md" | 35 ++++ .../2023-02-23-Promise.md" | 6 + .../2023-02-24-koa.md" | 40 +++++ .../2023-02-27-koaRouter.md" | 62 ++++++++ .../2023-02-28-post.md" | 53 +++++++ .../2023-03-02-\351\207\215\346\236\204.md" | 142 +++++++++++++++++ .../2023-03-03-\351\207\215\346\236\204.md" | 37 +++++ .../2023-03-06-\351\207\215\346\236\204.md" | 150 ++++++++++++++++++ ...41\346\235\277\345\274\225\346\223\216.md" | 42 +++++ ...31\346\200\201\346\226\207\344\273\266.md" | 94 +++++++++++ .../20230310-Sequelize.md" | 106 +++++++++++++ 16 files changed, 1021 insertions(+) delete mode 100644 "47\346\235\216\345\201\245/.keep" create mode 100644 "47\346\235\216\345\201\245/2023-02-14-\346\250\241\345\235\227.md" create mode 100644 "47\346\235\216\345\201\245/2023-02-16-\345\237\272\346\234\254\346\250\241\345\235\227.md" create mode 100644 "47\346\235\216\345\201\245/2023-02-17-http.md" create mode 100644 "47\346\235\216\345\201\245/2023-02-20-\346\226\207\344\273\266\346\234\215\345\212\241\345\231\250.md" create mode 100644 "47\346\235\216\345\201\245/2023-02-21.\351\242\230\347\233\256.md" create mode 100644 "47\346\235\216\345\201\245/2023-02-23-Promise.md" create mode 100644 "47\346\235\216\345\201\245/2023-02-24-koa.md" create mode 100644 "47\346\235\216\345\201\245/2023-02-27-koaRouter.md" create mode 100644 "47\346\235\216\345\201\245/2023-02-28-post.md" create mode 100644 "47\346\235\216\345\201\245/2023-03-02-\351\207\215\346\236\204.md" create mode 100644 "47\346\235\216\345\201\245/2023-03-03-\351\207\215\346\236\204.md" create mode 100644 "47\346\235\216\345\201\245/2023-03-06-\351\207\215\346\236\204.md" create mode 100644 "47\346\235\216\345\201\245/2023-03-07-\346\250\241\346\235\277\345\274\225\346\223\216.md" create mode 100644 "47\346\235\216\345\201\245/2023-03-09-\345\244\204\347\220\206\351\235\231\346\200\201\346\226\207\344\273\266.md" create mode 100644 "47\346\235\216\345\201\245/20230310-Sequelize.md" diff --git "a/47\346\235\216\345\201\245/.keep" "b/47\346\235\216\345\201\245/.keep" deleted file mode 100644 index e69de29..0000000 diff --git "a/47\346\235\216\345\201\245/2023-02-14-\346\250\241\345\235\227.md" "b/47\346\235\216\345\201\245/2023-02-14-\346\250\241\345\235\227.md" new file mode 100644 index 0000000..f202e9f --- /dev/null +++ "b/47\346\235\216\345\201\245/2023-02-14-\346\250\241\345\235\227.md" @@ -0,0 +1,31 @@ +# 模块 +### 1.要在模块中对外输出变量,用: +module.exports = variable; +'use strict'; +//先创一个hello.js文件,里面可以写方法 +function add(a,b){ + return a+b; +} + +//可以写数组 +let arr=[1,2,3]; + +//可以写对象 +let a={ + name:'呵呵', + age:66 +} +//在模块中对外输出变量,输出的变量可以是任意对象、函数、数组等等。 +module.exports=a +###2.要引入其他模块输出的对象,用: +var foo = require('other_module'); +//再创一个main.js文件 +'use strict'; +//引入其他模块输出的对象 +let add=require('./hello'); +//输出 +console.log(add); +#CommonJS规范 +这种模块加载机制被称为CommonJS规范。在这个规范下,每个.js文件都是一个模块,它们内部各自使用的变量名和函数名都互不冲突,例如,hello.js和main.js都申明了全局变量var s = 'xxx',但互不影响。 + +一个模块想要对外暴露变量(函数也是变量),可以用module.exports = variable;,一个模块要引用其他模块暴露的变量,用var ref = require('module_name');就拿到了引用模块的变量。 \ No newline at end of file diff --git "a/47\346\235\216\345\201\245/2023-02-16-\345\237\272\346\234\254\346\250\241\345\235\227.md" "b/47\346\235\216\345\201\245/2023-02-16-\345\237\272\346\234\254\346\250\241\345\235\227.md" new file mode 100644 index 0000000..053b6ad --- /dev/null +++ "b/47\346\235\216\345\201\245/2023-02-16-\345\237\272\346\234\254\346\250\241\345\235\227.md" @@ -0,0 +1,84 @@ +# 基本模块 +###1.process 如果我们想要在下一次事件响应中执行代码,可以调用process.nextTick(): + process.nextTick(()=>{ + console.log('只好第二了'); + }) + console.log('哈哈我先'); +### 2.异步读文件 +//文件不在桌面的异步读取一个文本文件 + let fs=require('fs'); + fs.readFile('hh.txt','utf-8',(err,data)=>{ + if(err){ + console.log(err); + }else{ + console.log(data); + } + }) +//文件在桌面的异步读取一个文本文件 +fs.readFile('C:/Users/Administrator/Desktop/nn.txt','utf-8',(err,data)=>{ + if(err){ + console.log(err); + }else{ + console.log(data); + } +}) +//如果没写utf-8,返回的就是回调函数的data参数将返回一个Buffer对象。在Node.js中,Buffer对象就是一个包含零个或任意个字节的数组(注意和Array不同)。 +//要想看懂文本文件的内容就.toString() +fs.readFile('C:/Users/Administrator/Desktop/nn.txt',(err,data)=>{ + if(err){ + console.log(err); + }else{ + console.log(data.toString());//toString()一下就好 + } +}) +//把一个String转换成Buffer +fs.readFile('hh.txt','utf-8',(err,data)=>{ + if(err){ + console.log(err); + }else{ + console.log(data); + console.log(Buffer.from(data,'utf-8')); + } +}) +console.log('哈哈'); +//因为是异步的所以会比哈哈慢 +### 3.同步读文件 +//除了标准的异步读取模式外,fs也提供相应的同步读取函数。同步读取的函数和异步函数相比,多了一个Sync后缀,并且不接收回调函数,函数直接返回结果。 + let s= fs.readFileSync('hh.txt','utf-8'); + console.log(s); + console.log('哈哈'); +//因为是同步所以比哈哈快 +### 4.写文件 +//异步将数据写入文件是通过fs.writeFile()实现的: + fs.writeFile('hh.txt','哈哈啦啦啦哈哈哈哈哈',function(err){ + if(err){ + console.log(err); + }else{ + console.log('写入成功'); + } + }) + console.log('哈哈'); +//因为异步所以比哈哈慢 +//writeFile()的参数依次为文件名、数据和回调函数。如果传入的数据是String,默认按UTF-8编码写入文本文件, +//如果传入的参数是Buffer,则写入的是二进制文件。回调函数由于只关心成功与否,因此只需要一个err参数。 +//和readFile()类似,writeFile()也有一个同步方法,叫writeFileSync(): + fs.writeFileSync('hh.txt','啦啦啦啦啦') + console.log('哈哈'); + +//写入追加 + fs.writeFile('hh.txt','三生三世十里桃花',{flag:'a'},(err)=>{ + if(err){ + console.log(err); + }else{ + console.log('成功'); + } + }) +### 5.stat对象 +//如果我们要获取文件大小,创建时间等信息,可以使用fs.stat(),它返回一个Stat对象,能告诉我们文件或目录的详细信息: + fs.stat('hh.txt',(err,data)=>{ + if(err){ + console.log(err); + }else{ + console.log(data); + } + }) \ No newline at end of file diff --git "a/47\346\235\216\345\201\245/2023-02-17-http.md" "b/47\346\235\216\345\201\245/2023-02-17-http.md" new file mode 100644 index 0000000..4617cce --- /dev/null +++ "b/47\346\235\216\345\201\245/2023-02-17-http.md" @@ -0,0 +1,46 @@ +# http +Node.js开发的目的就是为了用JavaScript编写Web服务器程序。因为JavaScript实际上已经统治了浏览器端的脚本,其优势就是有世界上数量最多的前端开发人员。如果已经掌握了JavaScript前端开发,再学习一下如何将JavaScript应用在后端开发,就是名副其实的全栈了。 + +# HTTP协议 +要理解Web服务器程序的工作原理,首先,我们要对HTTP协议有基本的了解。如果你对HTTP协议不太熟悉,先看一看HTTP协议简介。 + +# HTTP服务器 +要开发HTTP服务器程序,从头处理TCP连接,解析HTTP是不现实的。这些工作实际上已经由Node.js自带的http模块完成了。应用程序并不直接和HTTP协议打交道,而是操作http模块提供的request和response对象。 + +request对象封装了HTTP请求,我们调用request对象的属性和方法就可以拿到所有HTTP请求的信息; + +response对象封装了HTTP响应,我们操作response对象的方法,就可以把HTTP响应返回给浏览器。 + +用Node.js实现一个HTTP服务器程序非常简单。我们来实现一个最简单的Web程序hello.js,它对于所有请求,都返回Hello world!: + +'use strict'; + +// 导入http模块: +var http = require('http'); + +// 创建http server,并传入回调函数: +var server = http.createServer(function (request, response) { + // 回调函数接收request和response对象, + // 获得HTTP请求的method和url: + console.log(request.method + ': ' + request.url); + // 将HTTP响应200写入response, 同时设置Content-Type: text/html: + response.writeHead(200, {'Content-Type': 'text/html'}); + // 将HTTP响应的HTML内容写入response: + response.end('

Hello world!

'); +}); + +// 让服务器监听8080端口: +server.listen(8080); + +console.log('Server is running at http://127.0.0.1:8080/'); +在命令提示符下运行该程序,可以看到以下输出: + +$ node hello.js +Server is running at http://127.0.0.1:8080/ +不要关闭命令提示符,直接打开浏览器输入http://localhost:8080,即可看到服务器响应的内容 + +同时,在命令提示符窗口,可以看到程序打印的请求信息: + +GET: / +GET: /favicon.ico +这就是我们编写的第一个HTTP服务器程序! \ No newline at end of file diff --git "a/47\346\235\216\345\201\245/2023-02-20-\346\226\207\344\273\266\346\234\215\345\212\241\345\231\250.md" "b/47\346\235\216\345\201\245/2023-02-20-\346\226\207\344\273\266\346\234\215\345\212\241\345\231\250.md" new file mode 100644 index 0000000..ef043d2 --- /dev/null +++ "b/47\346\235\216\345\201\245/2023-02-20-\346\226\207\344\273\266\346\234\215\345\212\241\345\231\250.md" @@ -0,0 +1,93 @@ +#文件服务器 +让我们继续扩展一下上面的Web程序。我们可以设定一个目录,然后让Web程序变成一个文件服务器。要实现这一点,我们只需要解析request.url中的路径,然后在本地找到对应的文件,把文件内容发送出去就可以了。 + +解析URL需要用到Node.js提供的url模块,它使用起来非常简单,通过parse()将一个字符串解析为一个Url对象: + +'use strict'; + +var url = require('url'); + +console.log(url.parse('http://user:pass@host.com:8080/path/to/file?query=string#hash')); +结果如下: + +Url { + protocol: 'http:', + slashes: true, + auth: 'user:pass', + host: 'host.com:8080', + port: '8080', + hostname: 'host.com', + hash: '#hash', + search: '?query=string', + query: 'query=string', + pathname: '/path/to/file', + path: '/path/to/file?query=string', + href: 'http://user:pass@host.com:8080/path/to/file?query=string#hash' } +处理本地文件目录需要使用Node.js提供的path模块,它可以方便地构造目录: + +'use strict'; + +var path = require('path'); + +// 解析当前目录: +var workDir = path.resolve('.'); // '/Users/michael' + +// 组合完整的文件路径:当前目录+'pub'+'index.html': +var filePath = path.join(workDir, 'pub', 'index.html'); +// '/Users/michael/pub/index.html' +使用path模块可以正确处理操作系统相关的文件路径。在Windows系统下,返回的路径类似于C:\Users\michael\static\index.html,这样,我们就不关心怎么拼接路径了。 + +最后,我们实现一个文件服务器file_server.js: + +'use strict'; + +var + fs = require('fs'), + url = require('url'), + path = require('path'), + http = require('http'); + +// 从命令行参数获取root目录,默认是当前目录: +var root = path.resolve(process.argv[2] || '.'); + +console.log('Static root dir: ' + root); + +// 创建服务器: +var server = http.createServer(function (request, response) { + // 获得URL的path,类似 '/css/bootstrap.css': + var pathname = url.parse(request.url).pathname; + // 获得对应的本地文件路径,类似 '/srv/www/css/bootstrap.css': + var filepath = path.join(root, pathname); + // 获取文件状态: + fs.stat(filepath, function (err, stats) { + if (!err && stats.isFile()) { + // 没有出错并且文件存在: + console.log('200 ' + request.url); + // 发送200响应: + response.writeHead(200); + // 将文件流导向response: + fs.createReadStream(filepath).pipe(response); + } else { + // 出错了或者文件不存在: + console.log('404 ' + request.url); + // 发送404响应: + response.writeHead(404); + response.end('404 Not Found'); + } + }); +}); + +server.listen(8080); + +console.log('Server is running at http://127.0.0.1:8080/'); +没有必要手动读取文件内容。由于response对象本身是一个Writable Stream,直接用pipe()方法就实现了自动读取文件内容并输出到HTTP响应。 + +在命令行运行node file_server.js /path/to/dir,把/path/to/dir改成你本地的一个有效的目录,然后在浏览器中输入http://localhost:8080/index.html: + +只要当前目录下存在文件index.html,服务器就可以把文件内容发送给浏览器。观察控制台输出: + +200 /index.html +200 /css/uikit.min.css +200 /js/jquery.min.js +200 /fonts/fontawesome-webfont.woff2 +第一个请求是浏览器请求index.html页面,后续请求是浏览器解析HTML后发送的其它资源请求。 \ No newline at end of file diff --git "a/47\346\235\216\345\201\245/2023-02-21.\351\242\230\347\233\256.md" "b/47\346\235\216\345\201\245/2023-02-21.\351\242\230\347\233\256.md" new file mode 100644 index 0000000..96d31ec --- /dev/null +++ "b/47\346\235\216\345\201\245/2023-02-21.\351\242\230\347\233\256.md" @@ -0,0 +1,35 @@ +# 题目 +const http=require('http'); +const fs=require('fs'); + +const server=http.createServer((req,res)=>{ + let url=req.url; + console.log(url); + if (fs.existsSync('.'+url)) { + if (fs.existsSync('.'+url+'/index.html')) { + url='.'+url+'/index.html' + }else if(fs.existsSync('.'+url)){ + url='.'+url + } + }else if(fs.existsSync('.'+url+'.html')){ + url='.'+url+'.html' + }else if(fs.existsSync('.'+url+'.txt')){ + url='.'+url+'.txt' + }else if(fs.existsSync('.'+url+'.md')){ + url='.'+url+'.md' + }else if(url==='/'){ + url='./index.html' + } + + fs.readFile(url,'utf-8',(err,data)=>{ + if (err) { + res.end('404') + }else{ + res.end(data) + } + }) +}) + +server.listen(8088); + +console.log('ojbk'); \ No newline at end of file diff --git "a/47\346\235\216\345\201\245/2023-02-23-Promise.md" "b/47\346\235\216\345\201\245/2023-02-23-Promise.md" new file mode 100644 index 0000000..a132210 --- /dev/null +++ "b/47\346\235\216\345\201\245/2023-02-23-Promise.md" @@ -0,0 +1,6 @@ + + +# 一、读取文件的三种方法 +## 1、同步 +## 2、Promise +## 3、anync/await \ No newline at end of file diff --git "a/47\346\235\216\345\201\245/2023-02-24-koa.md" "b/47\346\235\216\345\201\245/2023-02-24-koa.md" new file mode 100644 index 0000000..4482ae7 --- /dev/null +++ "b/47\346\235\216\345\201\245/2023-02-24-koa.md" @@ -0,0 +1,40 @@ +# koa入门 +## 创建koa2工程 +首先,我们创建一个目录hello-koa并作为工程目录用VS Code打开。然后,我们创建app.js,输入以下代码: + +// 导入koa,和koa 1.x不同,在koa2中,我们导入的是一个class,因此用大写的Koa表示: +const Koa = require('koa'); + +// 创建一个Koa对象表示web app本身: +const app = new Koa(); + +// 对于任何请求,app将调用该异步函数处理请求: +app.use(async (ctx, next) => { + await next(); + ctx.response.type = 'text/html'; + ctx.response.body = '

Hello, koa2!

'; +}); + +// 在端口3000监听: +app.listen(3000); +console.log('app started at port 3000...') +对于每一个http请求,koa将调用我们传入的异步函数来处理: + +async (ctx, next) => { + await next(); + // 设置response的Content-Type: + ctx.response.type = 'text/html'; + // 设置response的内容: + ctx.response.body = '

Hello, koa2!

'; +} +其中,参数ctx是由koa传入的封装了request和response的变量,我们可以通过它访问request和response,next是koa传入的将要处理的下一个异步函数。 + +上面的异步函数中,我们首先用await next();处理下一个异步函数,然后,设置response的Content-Type和内容。 + +由async标记的函数称为异步函数,在异步函数中,可以用await调用另一个异步函数,这两个关键字将在ES7中引入。 + +现在我们遇到第一个问题:koa这个包怎么装,app.js才能正常导入它? + +方法一:可以用npm命令直接安装koa。先打开命令提示符,务必把当前目录切换到hello-koa这个目录,然后执行命令: + +npm install koa \ No newline at end of file diff --git "a/47\346\235\216\345\201\245/2023-02-27-koaRouter.md" "b/47\346\235\216\345\201\245/2023-02-27-koaRouter.md" new file mode 100644 index 0000000..69493a2 --- /dev/null +++ "b/47\346\235\216\345\201\245/2023-02-27-koaRouter.md" @@ -0,0 +1,62 @@ + +## koa-router +为了处理URL,我们需要引入koa-router这个middleware,让它负责处理URL映射。 + +我们把上一节的hello-koa工程复制一份,重命名为url-koa。 + +先在package.json中添加依赖项: +``` +"koa-router": "7.0.0" +``` +然后用npm install安装。 + + +接下来,我们修改app.js,使用koa-router来处理URL: +``` +const Koa = require('koa'); + +// 注意require('koa-router')返回的是函数: +const router = require('koa-router')(); + +const app = new Koa(); + +// log request URL: +app.use(async (ctx, next) => { + console.log(`Process ${ctx.request.method} ${ctx.request.url}...`); + await next(); +}); + +// add url-route: +router.get('/hello/:name', async (ctx, next) => { + var name = ctx.params.name; + ctx.response.body = `

Hello, ${name}!

`; +}); + +router.get('/', async (ctx, next) => { + ctx.response.body = '

Index

'; +}); + +// add router middleware: +app.use(router.routes()); + +app.listen(3000); +console.log('app started at port 3000...'); +``` +注意导入koa-router的语句最后的()是函数调用: +``` +const router = require('koa-router')(); +``` +相当于: +``` +const fn_router = require('koa-router'); +const router = fn_router(); +``` +然后,我们使用router.get('/path', async fn)来注册一个GET请求。可以在请求路径中使用带变量的/hello/:name,变量可以通过ctx.params.name访问。 + +再运行app.js,我们就可以测试不同的URL: +``` +输入首页:http://localhost:3000/ +``` +``` +输入:http://localhost:3000/hello/koa +``` \ No newline at end of file diff --git "a/47\346\235\216\345\201\245/2023-02-28-post.md" "b/47\346\235\216\345\201\245/2023-02-28-post.md" new file mode 100644 index 0000000..5e4aaba --- /dev/null +++ "b/47\346\235\216\345\201\245/2023-02-28-post.md" @@ -0,0 +1,53 @@ + +# 处理post请求 +用router.get('/path', async fn)处理的是get请求。如果要处理post请求,可以用router.post('/path', async fn)。 + +用post请求处理URL时,我们会遇到一个问题:post请求通常会发送一个表单,或者JSON,它作为request的body发送,但无论是Node.js提供的原始request对象,还是koa提供的request对象,都不提供解析request的body的功能! + +所以,我们又需要引入另一个middleware来解析原始request请求,然后,把解析后的参数,绑定到ctx.request.body中。 + +koa-bodyparser就是用来干这个活的。 + +我们在package.json中添加依赖项: +``` +"koa-bodyparser": "3.2.0" +``` +然后使用npm install安装。 + +下面,修改app.js,引入koa-bodyparser: +``` +const bodyParser = require('koa-bodyparser'); +``` +在合适的位置加上: +``` +app.use(bodyParser()); +``` +由于middleware的顺序很重要,这个koa-bodyparser必须在router之前被注册到app对象上。 + +现在我们就可以处理post请求了。写一个简单的登录表单: +``` +router.get('/', async (ctx, next) => { + ctx.response.body = `

Index

+
+

Name:

+

Password:

+

+
`; +}); + +router.post('/signin', async (ctx, next) => { + var + name = ctx.request.body.name || '', + password = ctx.request.body.password || ''; + console.log(`signin with name: ${name}, password: ${password}`); + if (name === 'koa' && password === '12345') { + ctx.response.body = `

Welcome, ${name}!

`; + } else { + ctx.response.body = `

Login failed!

+

Try again

`; + } +}); +``` +注意到我们用var name = ctx.request.body.name || ''拿到表单的name字段,如果该字段不存在,默认值设置为''。 + +类似的,put、delete、head请求也可以由router处理。 \ No newline at end of file diff --git "a/47\346\235\216\345\201\245/2023-03-02-\351\207\215\346\236\204.md" "b/47\346\235\216\345\201\245/2023-03-02-\351\207\215\346\236\204.md" new file mode 100644 index 0000000..6c4b992 --- /dev/null +++ "b/47\346\235\216\345\201\245/2023-03-02-\351\207\215\346\236\204.md" @@ -0,0 +1,142 @@ + +## 重构 +现在,我们已经可以处理不同的URL了,但是看看app.js,总觉得还是有点不对劲。 + + +所有的URL处理函数都放到app.js里显得很乱,而且,每加一个URL,就需要修改app.js。随着URL越来越多,app.js就会越来越长。 + +如果能把URL处理函数集中到某个js文件,或者某几个js文件中就好了,然后让app.js自动导入所有处理URL的函数。这样,代码一分离,逻辑就显得清楚了。最好是这样: +``` +url2-koa/ +| ++- .vscode/ +| | +| +- launch.json <-- VSCode 配置文件 +| ++- controllers/ +| | +| +- login.js <-- 处理login相关URL +| | +| +- users.js <-- 处理用户管理相关URL +| ++- app.js <-- 使用koa的js +| ++- package.json <-- 项目描述文件 +| ++- node_modules/ <-- npm安装的所有依赖包 +``` +于是我们把url-koa复制一份,重命名为url2-koa,准备重构这个项目。 + +我们先在controllers目录下编写index.js: +``` +var fn_index = async (ctx, next) => { + ctx.response.body = `

Index

+
+

Name:

+

Password:

+

+
`; +}; + +var fn_signin = async (ctx, next) => { + var + name = ctx.request.body.name || '', + password = ctx.request.body.password || ''; + console.log(`signin with name: ${name}, password: ${password}`); + if (name === 'koa' && password === '12345') { + ctx.response.body = `

Welcome, ${name}!

`; + } else { + ctx.response.body = `

Login failed!

+

Try again

`; + } +}; + +module.exports = { + 'GET /': fn_index, + 'POST /signin': fn_signin +}; +``` + +这个index.js通过module.exports把两个URL处理函数暴露出来。 + +类似的,hello.js把一个URL处理函数暴露出来: +``` +var fn_hello = async (ctx, next) => { + var name = ctx.params.name; + ctx.response.body = `

Hello, ${name}!

`; +}; + +module.exports = { + 'GET /hello/:name': fn_hello +}; +``` +现在,我们修改app.js,让它自动扫描controllers目录,找到所有js文件,导入,然后注册每个URL: +``` +// 先导入fs模块,然后用readdirSync列出文件 +// 这里可以用sync是因为启动时只运行一次,不存在性能问题: +var files = fs.readdirSync(__dirname + '/controllers'); + +// 过滤出.js文件: +var js_files = files.filter((f)=>{ + return f.endsWith('.js'); +}); + +// 处理每个js文件: +for (var f of js_files) { + console.log(`process controller: ${f}...`); + // 导入js文件: + let mapping = require(__dirname + '/controllers/' + f); + for (var url in mapping) { + if (url.startsWith('GET ')) { + // 如果url类似"GET xxx": + var path = url.substring(4); + router.get(path, mapping[url]); + console.log(`register URL mapping: GET ${path}`); + } else if (url.startsWith('POST ')) { + // 如果url类似"POST xxx": + var path = url.substring(5); + router.post(path, mapping[url]); + console.log(`register URL mapping: POST ${path}`); + } else { + // 无效的URL: + console.log(`invalid URL: ${url}`); + } + } +} +``` + +如果上面的大段代码看起来还是有点费劲,那就把它拆成更小单元的函数: +``` +function addMapping(router, mapping) { + for (var url in mapping) { + if (url.startsWith('GET ')) { + var path = url.substring(4); + router.get(path, mapping[url]); + console.log(`register URL mapping: GET ${path}`); + } else if (url.startsWith('POST ')) { + var path = url.substring(5); + router.post(path, mapping[url]); + console.log(`register URL mapping: POST ${path}`); + } else { + console.log(`invalid URL: ${url}`); + } + } +} + +function addControllers(router) { + var files = fs.readdirSync(__dirname + '/controllers'); + var js_files = files.filter((f) => { + return f.endsWith('.js'); + }); + + for (var f of js_files) { + console.log(`process controller: ${f}...`); + let mapping = require(__dirname + '/controllers/' + f); + addMapping(router, mapping); + } +} + +addControllers(router); +``` + +确保每个函数功能非常简单,一眼能看明白,是代码可维护的关键。 \ No newline at end of file diff --git "a/47\346\235\216\345\201\245/2023-03-03-\351\207\215\346\236\204.md" "b/47\346\235\216\345\201\245/2023-03-03-\351\207\215\346\236\204.md" new file mode 100644 index 0000000..8937472 --- /dev/null +++ "b/47\346\235\216\345\201\245/2023-03-03-\351\207\215\346\236\204.md" @@ -0,0 +1,37 @@ + +## Controller Middleware +最后,我们把扫描controllers目录和创建router的代码从app.js中提取出来,作为一个简单的middleware使用,命名为controller.js: +``` +const fs = require('fs'); + +function addMapping(router, mapping) { + ... +} + +function addControllers(router, dir) { + ... +} + +module.exports = function (dir) { + let + controllers_dir = dir || 'controllers', // 如果不传参数,扫描目录默认为'controllers' + router = require('koa-router')(); + addControllers(router, controllers_dir); + return router.routes(); +}; +``` +这样一来,我们在app.js的代码又简化了: +``` +... + +// 导入controller middleware: +const controller = require('./controller'); + +... + +// 使用middleware: +app.use(controller()); + +... +``` +经过重新整理后的工程url2-koa目前具备非常好的模块化,所有处理URL的函数按功能组存放在controllers目录,今后我们也只需要不断往这个目录下加东西就可以了,app.js保持不变。 \ No newline at end of file diff --git "a/47\346\235\216\345\201\245/2023-03-06-\351\207\215\346\236\204.md" "b/47\346\235\216\345\201\245/2023-03-06-\351\207\215\346\236\204.md" new file mode 100644 index 0000000..e9b8612 --- /dev/null +++ "b/47\346\235\216\345\201\245/2023-03-06-\351\207\215\346\236\204.md" @@ -0,0 +1,150 @@ +# 重构 +1. a.js +```js +//引入模块 +const Koa=require('koa'); +const registerRouter=require('./controllers') +const templating=require('./templating') +const staticFiles=require('./staticFiles') +//实例化 +let app=new Koa(); + + +//注册路由 +registerRouter(app) + +//端口号 +let port=8088; +app.listen(port) + +console.log(`http://localhost:${port}`); +``` +2. 控制器 + * 新建一个controllers文件夹,里面有各个控制器 + 1. index.js + ```js + const router=require('koa-router')(); + const bodyParser=require('koa-bodyparser'); + const {findControllers,registerRouter2}=require('../utils/tools') + + function registerRouter(app){ + //找到所有文件 + let files=findControllers() + //遍历注册 + registerRouter2(files,router) + + app.use(router.routes()); + app.use(bodyParser()) + } + + module.exports=registerRouter; + ``` + 2. roles.js + ```js + + let list=[ + { + id:1, + name:'哈哈', + img:url + },{ + id:2, + name:'嘻嘻', + img:url + },{ + id:3, + name:'呵呵', + img:url + },{ + id:4, + name:'嘿嘿', + img:url + },{ + id:5, + name:'呐呐', + img:url + }, + ] + + async function getAll(ctx,next){ + + + } + async function getById(ctx,next){ + + } + async function addItem(ctx,next){ + + } + async function updateItem(ctx,next){ + + } + async function delItem(ctx,next){ + + } + + + module.exports={ + 'get /roles':getAll, + 'get /roles/:id':getById, + 'post /roles':addItem, + 'put /roles/:id':updateItem, + 'delete /roles/:id':delItem, + } + ``` +3. 路由系统封装 + * 新建一个utils文件夹,再新建一个tools.js + ```js + 'use strict'; + + const fs = require('fs'); + + // 查找所有路由文件 + function findControllerFiles(path) { + // 如果传入指定目录,则从指定目录查找路由文件,否则默认从 controllers目录下查找路由文件 + path = path || './controllers'; + + // 使用fs模块读取指定目录下的文件 + let files = fs.readdirSync(path); + + // 返回过滤后的路由文件(查找到的文件过滤掉非.js结尾的、 并且不能是index.js的文件) + return files.filter(item => { + return item.endsWith('.js') && item !== 'index. js'; + }); + } + + //注册路由(传入所有的路由文件) + function registryRouter(files,router) { + // 遍历路由文件 + files.forEach(file => {// 形如这个样子的字符串: 'users.js' + + // 加载指定文件模块,返回形式是一个对象 + let tmpModule = require('../controllers/' + file); + // 遍历对象 + for (let key in tmpModule) { + // 使用空格分割字符串 + let arr = key.split(' '); + + let method = arr[0];//http的方法,可能是:get post put delete中的其中一种 + let url = arr[1];//请求地址,不包含主机地址:localhost:port + let fn = tmpModule[key];//请求需要调用的异步函数 + + if (method === 'get') { + router.get(url, fn); + } else if (method === 'post') { + router.post(url, fn); + } else if (method === 'put') { + router.put(url, fn); + } else if (method === 'delete') { + router.delete(url, fn); + } + } + }) + } + + module.exports = { + findControllerFiles, + registryRouter + } + ``` + \ No newline at end of file diff --git "a/47\346\235\216\345\201\245/2023-03-07-\346\250\241\346\235\277\345\274\225\346\223\216.md" "b/47\346\235\216\345\201\245/2023-03-07-\346\250\241\346\235\277\345\274\225\346\223\216.md" new file mode 100644 index 0000000..06d36ca --- /dev/null +++ "b/47\346\235\216\345\201\245/2023-03-07-\346\250\241\346\235\277\345\274\225\346\223\216.md" @@ -0,0 +1,42 @@ +# 模板引擎 +1. 新建一个views的文件夹,来一个index.html +```html + + + + + + + Document + + +

hello {{name}}

+ + +``` +2. 新建一个a.js +```js +'use strict'; + +const Koa=require('koa'); +const router=require('koa-router')(); +const bodyParser=require('koa-bodyparser'); +const nunjucks=require('nunjucks'); + +let app=new Koa(); + +let a=nunjucks.configure('views', { autoescape: true }); +let s=a.render('index.html', { name: '哈哈哈' }) +console.log(s);//这里输出的是1那里index.html的整个内容 + +router.get('/',async (ctx,next)=>{ + ctx.body=s +}) + +app.use(router.routes()); +app.use(bodyParser()) +app.listen(8188) +console.log('http://localhost:8188'); +// app.listen(8088) + +``` \ No newline at end of file diff --git "a/47\346\235\216\345\201\245/2023-03-09-\345\244\204\347\220\206\351\235\231\346\200\201\346\226\207\344\273\266.md" "b/47\346\235\216\345\201\245/2023-03-09-\345\244\204\347\220\206\351\235\231\346\200\201\346\226\207\344\273\266.md" new file mode 100644 index 0000000..86e25ce --- /dev/null +++ "b/47\346\235\216\345\201\245/2023-03-09-\345\244\204\347\220\206\351\235\231\346\200\201\346\226\207\344\273\266.md" @@ -0,0 +1,94 @@ + +# 处理静态文件的middleware +1. 新建一个statics文件夹里面用来放静态文件 +2. 控制器,再原有的基础上加点料 +```js +const fs=require('fs') + +let url='../statics/img/IU-LILAC.jpg'; + +let list=[ + { + id:1, + name:'不知道', + img:url + },{ + id:2, + name:'不晓得', + img:url + },{ + id:3, + name:'认不到', + img:url + },{ + id:4, + name:'不认识', + img:url + },{ + id:5, + name:'晓不得', + img:url + }, +] + +async function getAll(ctx,next){ + + ctx.render('index.html',{list:list}) +} +async function getById(ctx,next){ + +} +async function addItem(ctx,next){ + +} +async function updateItem(ctx,next){ + +} +async function delItem(ctx,next){ + +} +async function findImg(ctx,next){ + ctx.type='image/jpeg'; + ctx.body=fs.readFileSync('./statics/img/IU-LILAC.jpg') +} + +module.exports={ + 'get /roles':getAll, + 'get /roles/:id':getById, + 'post /roles':addItem, + 'put /roles/:id':updateItem, + 'delete /roles/:id':delItem, + 'get /statics/img/IU-LILAC.jpg':findImg +} +``` +3. 新建一个处理静态文件的middleware +```js +'use strict'; + +const fs=require('fs'); +const mime=require('mime') + +function staticFiles(){ + return async (ctx,next)=>{ + let tmpPath=ctx.request.path; + if (tmpPath.startsWith('/statics')) { + let fullUrl= __dirname+tmpPath; + + if (fs.existsSync(fullUrl)) { + ctx.type=mime.getType(fullUrl); + ctx.body=fs.readFileSync(fullUrl); + }else{ + ctx.body='404' + } + }else{ + await next() + } + } +} + +module.exports=staticFiles; +``` +4. 然后在app.js里面插入中间件就好了 +```js +app.use(staticFiles()) +``` diff --git "a/47\346\235\216\345\201\245/20230310-Sequelize.md" "b/47\346\235\216\345\201\245/20230310-Sequelize.md" new file mode 100644 index 0000000..36a5425 --- /dev/null +++ "b/47\346\235\216\345\201\245/20230310-Sequelize.md" @@ -0,0 +1,106 @@ +# Sequelize + +## 安装 + 1. 使用 npm 安装sequelize + npm i sequelize # 这将安装最新版本的 Sequelize + 2. 使用 yarn 安装sequelize + yarn add sequelize + 3. 你还必须手动为所选数据库安装驱动程序: + ### 使用 npm + npm i pg pg-hstore # PostgreSQL + npm i mysql2 # MySQL + npm i mariadb # MariaDB + npm i sqlite3 # SQLite + npm i tedious # Microsoft SQL Server + npm i ibm_db # DB2 + ### 使用 yarn + yarn add pg pg-hstore # PostgreSQL + yarn add mysql2 # MySQL + yarn add mariadb # MariaDB + yarn add sqlite3 # SQLite + yarn add tedious # Microsoft SQL Server + yarn add ibm_db # DB2 + + +## 连接到数据库 + 1. 要连接到数据库,必须创建一个 Sequelize 实例. 这可以通过将连接参数分别传递到 Sequelize 构造函数或通过传递一个连接 URI 来完成: +```js + const { Sequelize } = require('sequelize'); + + // 方法 1: 传递一个连接 URI + const sequelize = new Sequelize('sqlite::memory:') // Sqlite 示例 + const sequelize = new Sequelize('postgres:// user:pass@example.com:5432/dbname') // Postgres 示例 + + // 方法 2: 分别传递参数 (sqlite) + const sequelize = new Sequelize({ + dialect: 'sqlite', + storage: 'path/to/database.sqlite' + }); + + // 方法 3: 分别传递参数 (其它数据库) + const sequelize = new Sequelize('database', 'username', 'password', { + host: 'localhost', + dialect: /* 选择 'mysql' | 'mariadb' | 'postgres' | 'mssql' 其一 */ + }); +``` + +## 测试链接 +```js +async function test(){ + try { + await sequelize.authenticate(); + console.log('Connection has been established successfully.'); + } catch (error) { + console.error('Unable to connect to the database:', error); + } +} + +test() + +``` + +## 关闭连接 +1. 默认情况下,Sequelize 将保持连接打开状态,并对所有查询使用相同的连接. 如果你需要关闭连接,请调用 sequelize.close()(这是异步的并返回一个 Promise) + + +## 定义模型 +```js +let users=sequelize.define('Users',{ + id:{ + type:DataTypes.INTEGER, + primaryKey:true + }, + username:DataTypes.STRING, + age:DataTypes.INTEGER +},{ + timestamps:false +}) +``` +用sequelize.define()定义Model时,传入名称Users,默认的表名就是Users。第二个参数指定列名和数据类型,如果是主键,需要更详细地指定。第三个参数是额外的配置,我们传入{ timestamps: false }是为了关闭Sequelize的自动添加timestamp的功能。所有的ORM框架都有一种很不好的风气,总是自作聪明地加上所谓“自动化”的功能,但是会让人感到完全摸不着头脑。 + +接下来,我们就可以往数据库中塞一些数据了。我们可以用Promise的方式写: +```js +users.sync().then(()=>{ + users.create({ + id:1, + username:'呵呵', + age:16 + }) +}) +``` +也可以用await写: +```js +async function xixi(){ + users.create({ + id:2, + username:'嘻嘻', + age:17 +}) +} +xixi() +``` + +## Promises 和 async/await + +Sequelize 提供的大多数方法都是异步的,因此返回 Promises. 它们都是 Promises, 因此你可以直接使用Promise API(例如,使用 then, catch, finally). +当然,使用 async 和 await 也可以正常工作. \ No newline at end of file -- Gitee From e2f6623049ca55434e1f11beea47432f18968458 Mon Sep 17 00:00:00 2001 From: "@chen-li-xing" <19912973651> Date: Tue, 14 Mar 2023 09:50:50 +0800 Subject: [PATCH 02/20] agian2 --- .../20230213-\345\274\200\345\255\246.md" | 2 - ...\273\272\347\216\257\345\242\203launch.md" | 33 ---- .../20230216-\346\250\241\345\235\227.md" | 25 --- ...33\345\273\272\347\275\221\347\253\231.md" | 71 ------- ...66\346\234\215\345\212\241\345\231\250.md" | 28 --- .../20230221-promise.md" | 36 ---- .../20230224-\347\273\203\344\271\240.md" | 57 ------ .../20230225-koa.md" | 9 - .../20230227-\350\267\257\347\224\261.md" | 26 --- .../20230228-router.md" | 21 --- .../20230302-\351\207\215\346\236\204.md" | 34 ---- .../20230303-url\345\244\204\347\220\206.md" | 18 -- .../20230306-\345\260\201\350\243\205.md" | 123 ------------- .../20230307-Nunjucks.md" | 174 ------------------ .../20230309-MVC.md" | 173 ----------------- ...0-\346\225\260\346\215\256\345\272\223.md" | 82 --------- 16 files changed, 912 deletions(-) delete mode 100644 "33\347\211\233\346\226\207\350\275\251/20230213-\345\274\200\345\255\246.md" delete mode 100644 "33\347\211\233\346\226\207\350\275\251/20230214-\346\220\255\345\273\272\347\216\257\345\242\203launch.md" delete mode 100644 "33\347\211\233\346\226\207\350\275\251/20230216-\346\250\241\345\235\227.md" delete mode 100644 "33\347\211\233\346\226\207\350\275\251/20230217-\345\210\233\345\273\272\347\275\221\347\253\231.md" delete mode 100644 "33\347\211\233\346\226\207\350\275\251/20230220-\346\226\207\344\273\266\346\234\215\345\212\241\345\231\250.md" delete mode 100644 "33\347\211\233\346\226\207\350\275\251/20230221-promise.md" delete mode 100644 "33\347\211\233\346\226\207\350\275\251/20230224-\347\273\203\344\271\240.md" delete mode 100644 "33\347\211\233\346\226\207\350\275\251/20230225-koa.md" delete mode 100644 "33\347\211\233\346\226\207\350\275\251/20230227-\350\267\257\347\224\261.md" delete mode 100644 "33\347\211\233\346\226\207\350\275\251/20230228-router.md" delete mode 100644 "33\347\211\233\346\226\207\350\275\251/20230302-\351\207\215\346\236\204.md" delete mode 100644 "33\347\211\233\346\226\207\350\275\251/20230303-url\345\244\204\347\220\206.md" delete mode 100644 "33\347\211\233\346\226\207\350\275\251/20230306-\345\260\201\350\243\205.md" delete mode 100644 "33\347\211\233\346\226\207\350\275\251/20230307-Nunjucks.md" delete mode 100644 "33\347\211\233\346\226\207\350\275\251/20230309-MVC.md" delete mode 100644 "33\347\211\233\346\226\207\350\275\251/20230310-\346\225\260\346\215\256\345\272\223.md" diff --git "a/33\347\211\233\346\226\207\350\275\251/20230213-\345\274\200\345\255\246.md" "b/33\347\211\233\346\226\207\350\275\251/20230213-\345\274\200\345\255\246.md" deleted file mode 100644 index 6c7758f..0000000 --- "a/33\347\211\233\346\226\207\350\275\251/20230213-\345\274\200\345\255\246.md" +++ /dev/null @@ -1,2 +0,0 @@ -开学第一课,终于回到了学校。 -开始本学期的专业课课程,第一门课node.js,第二门课vue.js \ No newline at end of file diff --git "a/33\347\211\233\346\226\207\350\275\251/20230214-\346\220\255\345\273\272\347\216\257\345\242\203launch.md" "b/33\347\211\233\346\226\207\350\275\251/20230214-\346\220\255\345\273\272\347\216\257\345\242\203launch.md" deleted file mode 100644 index 07a6d1d..0000000 --- "a/33\347\211\233\346\226\207\350\275\251/20230214-\346\220\255\345\273\272\347\216\257\345\242\203launch.md" +++ /dev/null @@ -1,33 +0,0 @@ -``` -{ - // 使用 IntelliSense 了解相关属性。 - // 悬停以查看现有属性的描述。 - // 欲了解更多信息,请访问: https://go.microsoft.com/fwlink/?linkid=830387 - "version": "0.2.0", - "configurations": [ - { - "type": "node", - "request": "launch", - "name": "Launch Program", - "skipFiles": [ - "/**" - ], - "program": "${workspaceFolder}\\index.js" - } - ] -} -``` - -``` -'use strict' - - -var fs=require('./greet'); -kunkun.readfile('kunkun.txt','utf-8',function(err,data){ - if (err) { - console.log(err); - }else{ - console.log(data); - } -}); -``` \ No newline at end of file diff --git "a/33\347\211\233\346\226\207\350\275\251/20230216-\346\250\241\345\235\227.md" "b/33\347\211\233\346\226\207\350\275\251/20230216-\346\250\241\345\235\227.md" deleted file mode 100644 index 65c0b97..0000000 --- "a/33\347\211\233\346\226\207\350\275\251/20230216-\346\250\241\345\235\227.md" +++ /dev/null @@ -1,25 +0,0 @@ -创建一个函数,这样我们就可以在其他地方调用这个函数: -``` -'use strict'; - -var s = 'Hello'; - -function greet(name) { - console.log(s + ', ' + name + '!'); -} - -module.exports = greet; -``` - - -编写一个main.js文件,调用hello模块的greet函数: -``` -'use strict'; - -// 引入hello模块: -var greet = require('./hello'); - -var s = 'Michael'; - -greet(s); // Hello, Michael! -``` \ No newline at end of file diff --git "a/33\347\211\233\346\226\207\350\275\251/20230217-\345\210\233\345\273\272\347\275\221\347\253\231.md" "b/33\347\211\233\346\226\207\350\275\251/20230217-\345\210\233\345\273\272\347\275\221\347\253\231.md" deleted file mode 100644 index 0f83490..0000000 --- "a/33\347\211\233\346\226\207\350\275\251/20230217-\345\210\233\345\273\272\347\275\221\347\253\231.md" +++ /dev/null @@ -1,71 +0,0 @@ -服务器创建笔记 -pt upgrade -y 更新数据 - -apt install nginx -y 安装 nginx服务器 - -systemct1 status 面板服务,执行服务器确保没问题 - -systemct1 enable nginx 自启动开机nginx系统 - -systemct1 status nginx 查看 nginx系统 状态 - -systemct1 stop nginx 结束状态 - -ssh root@hd.changex.top - -cd /var/www 进入 这个目录才能使用进入变量等等操作 访问方便 - -mkdir hd.changex.top 创建目录 - -rmdir hd.changex.top 删除目录 - -ls 查看目录 - -ls -al 查看当前目录信息 - -vim ~/.bashrc 进入环境变量 - -source ~/.bashrc 刷新变量 - - :wq! 退出环境变量 - - ll 查看当前状态 - ll -a 查看更多的当前状态 - -cd hd.change.top 进入创建的目录 - - 11月7日视频42分钟 上传内容 - -在最普通的模式下打(root@iZwz9fhv99le48i6gtgwovZ:~#):后面打 cd /etc/nginx/conf.d/ 网站配置文件 - -vim hd.changex.top.conf 进入配置文件,创建配置文件 然后打o字母 - -进入配置文件后 -server { - listen 80; - server_name hd.changex.top; - - location / { - root /var/www/hd.changex.top/文件名 - index index.html; - - } - - -} - -nginx -t 检查语法是否错误 - -nginx -s reload 重新加载配置,保存配置 - -cd 返回上级指令 - -vim /root/.bashrc 进入root的环境变量 - -apt install -y git 安装git - -scp -r 文件名 root@hd.changex.top:/var/www/hd.changex.top 上传文件夹的 - - rm -f 文件 删除文件 - - rm -r 文件夹 删除文件夹+ \ No newline at end of file diff --git "a/33\347\211\233\346\226\207\350\275\251/20230220-\346\226\207\344\273\266\346\234\215\345\212\241\345\231\250.md" "b/33\347\211\233\346\226\207\350\275\251/20230220-\346\226\207\344\273\266\346\234\215\345\212\241\345\231\250.md" deleted file mode 100644 index 6becd3d..0000000 --- "a/33\347\211\233\346\226\207\350\275\251/20230220-\346\226\207\344\273\266\346\234\215\345\212\241\345\231\250.md" +++ /dev/null @@ -1,28 +0,0 @@ -##### 解析URL需要用到Node.js提供的url模块,它使用起来非常简单,通过parse()将一个字符串解析为一个Url对象: -``` -Url { - protocol: 'http:', - slashes: true, - auth: 'user:pass', - host: 'host.com:8080', - port: '8080', - hostname: 'host.com', - hash: '#hash', - search: '?query=string', - query: 'query=string', - pathname: '/path/to/file', - path: '/path/to/file?query=string', - href: 'http://user:pass@host.com:8080/path/to/file?query=string#hash' } -``` -处理本地文件目录需要使用Node.js提供的path模块,它可以方便地构造目录: -``` -'use strict'; - -var path = require('path'); - -// 解析当前目录: -var workDir = path.resolve('.'); // '/Users/michael' - -// 组合完整的文件路径:当前目录+'pub'+'index.html': -var filePath = path.join(workDir, 'pub', 'index.html'); -``` \ No newline at end of file diff --git "a/33\347\211\233\346\226\207\350\275\251/20230221-promise.md" "b/33\347\211\233\346\226\207\350\275\251/20230221-promise.md" deleted file mode 100644 index a9262e1..0000000 --- "a/33\347\211\233\346\226\207\350\275\251/20230221-promise.md" +++ /dev/null @@ -1,36 +0,0 @@ -### Promise是JS中进行异步编程的新的解决方案 - -#### promise 是一个构造函数 -#### Promise 的构造函数接收一个函数为参数,并且传入两个参数:resolve,reject,分别表示异步操作执行成功后的回调函数和异步操作执行失败后的回调函数。 -#### 指定回调函数的方法更加灵活 -#### 支持链式调用 ,可以解决回调函数问题 - -### 基础定义 -#### Promise 构造函数: Promise (excutor) {} -#### excutor 函数: 同步执行 (resolve, reject) => {} -#### resolve 函数: 内部定义成功时我们调用的函数 value => {} -#### reject 函数: 内部定义失败时我们调用的函数 reason => {} - -### .then() 方法用来预先指定成功和失败的回调函数,调用 .then() 方法时,成功的回调函数是必选的,失败的回调函数是可选的 -#### 返回一个新的 promise 对象 - -``` -Promise.prototype.then(onResolved, onRejected) => {}` -onResolved 函数: 成功的回调函数 (value) => {} -onRejected 函数: 失败的回调函数 (reason) => {} -``` - -### Promise.resolve 方法: -### 说明:返回一个 成功/失败 的 promise 对象 -``` -Promise.resolve(value) => {}` -value: 成功的数据或 promise 对象 -``` - -### Promise.reject 方法: -``` -说明:返回一个 失败 的 promise 对象 -Promise.reject(reason) => {} ` -reason: 失败的原因 -``` - diff --git "a/33\347\211\233\346\226\207\350\275\251/20230224-\347\273\203\344\271\240.md" "b/33\347\211\233\346\226\207\350\275\251/20230224-\347\273\203\344\271\240.md" deleted file mode 100644 index c3114fe..0000000 --- "a/33\347\211\233\346\226\207\350\275\251/20230224-\347\273\203\344\271\240.md" +++ /dev/null @@ -1,57 +0,0 @@ -'use strict' -var http = require('http'); -var fs = require('fs');//引入文件读取模块 - -var server= http.createServer(function(req,res){ - - //客户端输入的url,例如如果输入localhost:8888/index.html - var url = '.'+ req.url; - console.log(url); - if (fs.existsSync (url) ) { - if (fs.existsSync (url+'/index.html')) { - url=url+'/index.html';} - }else if (fs.existsSync (url+'.html') ) { - url=url+'.html'; - }else if (fs.existsSync(url+'.txt')) { - url=url+'.txt'; - }else if (fs.existsSync(url+'.md')) { - url=url+'.md'; - }else if(url='/'){ - url='./index.html'; - } - - - - fs.readFile( url ,'utf-8', function(err,data){ - - /* - - 一参为文件路径 - - 二参为回调函数 - - 回调函数的一参为读取错误返回的信息,返回空就没有错误 - - 二参为读取成功返回的文本内容 - - */ - - if(err){ - res.writeHeader(404,{ - 'content-type' : 'text/html;charset="utf-8"' - }); - res.write('

404错误

你要找的页面不存在

'); - res.end(); - }else{ - res.writeHeader(200,{ - 'content-type' : 'text/html;charset="utf-8"' - }); - res.write(data);//将index.html显示在客户端 - res.end(); - - } - }); - -}).listen(8888); - -console.log('http://127.0.0.1:8888'); \ No newline at end of file diff --git "a/33\347\211\233\346\226\207\350\275\251/20230225-koa.md" "b/33\347\211\233\346\226\207\350\275\251/20230225-koa.md" deleted file mode 100644 index 6470551..0000000 --- "a/33\347\211\233\346\226\207\350\275\251/20230225-koa.md" +++ /dev/null @@ -1,9 +0,0 @@ -koa是Express的下一代基于Node.js的web框架, -``` -app.use(async (ctx, next) => { - await next(); - var data = await doReadFile(); - ctx.response.type = 'text/plain'; - ctx.response.body = data; -}); -``` \ No newline at end of file diff --git "a/33\347\211\233\346\226\207\350\275\251/20230227-\350\267\257\347\224\261.md" "b/33\347\211\233\346\226\207\350\275\251/20230227-\350\267\257\347\224\261.md" deleted file mode 100644 index 4aa4278..0000000 --- "a/33\347\211\233\346\226\207\350\275\251/20230227-\350\267\257\347\224\261.md" +++ /dev/null @@ -1,26 +0,0 @@ -``` -app.use(async (ctx, next) => { - if (ctx.request.path === '/') { - ctx.response.body = 'index page'; - } else { - await next(); - } -}); - - -app.use(async (ctx, next) => { - if (ctx.request.path === '/test') { - ctx.response.body = 'TEST page'; - } else { - await next(); - } -}); - -app.use(async (ctx, next) => { - if (ctx.request.path === '/error') { - ctx.response.body = 'ERROR page'; - } else { - await next(); - } -}); -``` \ No newline at end of file diff --git "a/33\347\211\233\346\226\207\350\275\251/20230228-router.md" "b/33\347\211\233\346\226\207\350\275\251/20230228-router.md" deleted file mode 100644 index f91624b..0000000 --- "a/33\347\211\233\346\226\207\350\275\251/20230228-router.md" +++ /dev/null @@ -1,21 +0,0 @@ -``` -'use strict' - -const koa=require('koa'); -const router=require('router')(); -console.log(router); -const app=new koa(); - -router.get('/abc',async(ctx,next)=>{ - ctx.body='我是abc'; -}) -router.get('/def',async(ctx,next)=>{ - ctx.body='我是def'; -}) - -app.use(router,router()); -let port=3000; -app.listen(port); - -console.log(`http://localhost:${port}`); -``` \ No newline at end of file diff --git "a/33\347\211\233\346\226\207\350\275\251/20230302-\351\207\215\346\236\204.md" "b/33\347\211\233\346\226\207\350\275\251/20230302-\351\207\215\346\236\204.md" deleted file mode 100644 index 0a1559a..0000000 --- "a/33\347\211\233\346\226\207\350\275\251/20230302-\351\207\215\346\236\204.md" +++ /dev/null @@ -1,34 +0,0 @@ -Controller Middleware -最后,我们把扫描controllers目录和创建router的代码从app.js中提取出来,作为一个简单的middleware使用,命名为controller.js: - -const fs = require('fs'); - -function addMapping(router, mapping) { - ... -} - -function addControllers(router, dir) { - ... -} - -module.exports = function (dir) { - let - controllers_dir = dir || 'controllers', // 如果不传参数,扫描目录默认为'controllers' - router = require('koa-router')(); - addControllers(router, controllers_dir); - return router.routes(); -}; -这样一来,我们在app.js的代码又简化了: - -... - -// 导入controller middleware: -const controller = require('./controller'); - -... - -// 使用middleware: -app.use(controller()); - -... -经过重新整理后的工程url2-koa目前具备非常好的模块化,所有处理URL的函数按功能组存放在controllers目录,今后我们也只需要不断往这个目录下加东西就可以了,app.js保持不变。 \ No newline at end of file diff --git "a/33\347\211\233\346\226\207\350\275\251/20230303-url\345\244\204\347\220\206.md" "b/33\347\211\233\346\226\207\350\275\251/20230303-url\345\244\204\347\220\206.md" deleted file mode 100644 index 989b203..0000000 --- "a/33\347\211\233\346\226\207\350\275\251/20230303-url\345\244\204\347\220\206.md" +++ /dev/null @@ -1,18 +0,0 @@ -``` -const fs = require('fs'); // 文件模块 -const Koa = require('koa'); -const router = require('koa-router')(); // 处理url与express一样 都需要引入相关的处理模块 -const bodyParser = require('koa-bodyparser'); // 处理原始请求json字符串解析模块 -const app = new Koa(); // 实例化koa,K大写代表引入的是koa2 -app.use(bodyParser()); -app.use(router.routes()); // URL处理模块 - -app.use(async (ctx, next) => { // ctx 你可以理解为客户端 存储着所有的信息 username path 等信息 - console.log(ctx.request.path, 'asdsad'); // 获取浏览器地址信息 判断是否路径为/index - if (ctx.request.path === '/index') { - ctx.response.body = 'index page'; // 发送数据Index page - } else { - await next(); - } -}); -``` \ No newline at end of file diff --git "a/33\347\211\233\346\226\207\350\275\251/20230306-\345\260\201\350\243\205.md" "b/33\347\211\233\346\226\207\350\275\251/20230306-\345\260\201\350\243\205.md" deleted file mode 100644 index b37f544..0000000 --- "a/33\347\211\233\346\226\207\350\275\251/20230306-\345\260\201\350\243\205.md" +++ /dev/null @@ -1,123 +0,0 @@ -# 封装 - -文件app.js -```js -'use strict' -const Koa = require('koa'); -const controllers = require('./controller') - -let app = new Koa(); -controllers(app) - -let port = 8088; -app.listen(port); -console.log(`http://localhost:${port}`); -``` - -index.js -```js -'use strict' - - -const router = require('koa-router')(); -const bodyparser = require('koa-bodyparser'); -const {findControllers,registryRouter} = require('../utils/tools') - -//处理路由 -function processRouter(app) { - - //1、找到所有路由文件 - let controllers = findControllers() - //2、遍历注册所有路由 - - registryRouter(controllers,router) - - app.use(bodyparser()) - app.use(router.routes()); - -} -module.exports = processRouter -``` - -tools.js -```js -'use strict' - -const fs = require('fs') - -//1、找到所有路由文件 -function findControllers(path) { - path = path || './controller/'; - console.log(path); - let files = fs.readdirSync(path) - // console.log(files); - return files.filter(item => { - return item !== 'index.js' - }) -} - -//2、遍历注册所有路由 -function registryRouter(controllerFiles,router) { - controllerFiles.forEach(item => { - let tmpModule = require('../controller/' + item.replace('.js', '')); - console.log(tmpModule); - for (let key in tmpModule) { - let tmpArr = key.split(' '); - - let method = tmpArr[0]; - let url = tmpArr[1]; - let fn = tmpModule[key]; - - if (method === 'get') { - router.get(url,fn) - } else if (method === 'post') { - router.post(url,fn) - } else if (method === 'put') { - router.put(url,fn) - } else if (method === 'delete') { - router.delete(url,fn) - } - - } - }) -} - -module.exports = { - findControllers, - registryRouter -} -``` - -users.js -```js -'use strict' - - -async function getAll(ctx, next) { - ctx.body = '这里是getAll' -} - -async function getById(ctx, next) { - ctx.body = '这里是getById' -} - -async function addItem(ctx, next) { - ctx.body = '这里是addItem' -} - -async function updateItem(ctx, next) { - -} - -async function delItem(ctx, next) { - -} - -module.exports = { - 'get /users':getAll, - 'get /users/:id':getById, - 'post /users':addItem, - 'put /users/:id':updateItem, - 'delete /users/:id':delItem -} -``` \ No newline at end of file diff --git "a/33\347\211\233\346\226\207\350\275\251/20230307-Nunjucks.md" "b/33\347\211\233\346\226\207\350\275\251/20230307-Nunjucks.md" deleted file mode 100644 index a506af2..0000000 --- "a/33\347\211\233\346\226\207\350\275\251/20230307-Nunjucks.md" +++ /dev/null @@ -1,174 +0,0 @@ -# Nunjucks - -Nunjucks是什么东东?其实它是一个模板引擎。 - -那什么是模板引擎? - -模板引擎就是基于模板配合数据构造出字符串输出的一个组件。比如下面的函数就是一个模板引擎: -``` -function examResult (data) { - return `${data.name}同学一年级期末考试语文${data.chinese}分,数学${data.math}分,位于年级第${data.ranking}名。` -} -``` -如果我们输入数据如下: -``` -examResult({ - name: '小明', - chinese: 78, - math: 87, - ranking: 999 -}); -``` -该模板引擎把模板字符串里面对应的变量替换以后,就可以得到以下输出: - -小明同学一年级期末考试语文78分,数学87分,位于年级第999名。 - - -## 简单逻辑 -模板还需要能执行一些简单逻辑,比如,要按条件输出内容,需要if实现如下输出: -``` -{{ name }}同学, -{% if score >= 90 %} - 成绩优秀,应该奖励 -{% elif score >=60 %} - 成绩良好,继续努力 -{% else %} - 不及格,建议回家打屁股 -{% endif %} -``` -所以,我们需要一个功能强大的模板引擎,来完成页面输出的功能。 - - -我们创建一个use-nunjucks的VS Code工程结构如下: -``` -use-nunjucks/ -| -+- .vscode/ -| | -| +- launch.json <-- VSCode 配置文件 -| -+- views/ -| | -| +- hello.html <-- HTML模板文件 -| -+- app.js <-- 入口js -| -+- package.json <-- 项目描述文件 -| -+- node_modules/ <-- npm安装的所有依赖包 -``` -其中,模板文件存放在views目录中。 - -我们先在package.json中添加nunjucks的依赖: -``` -"nunjucks": "2.4.2" -``` -注意,模板引擎是可以独立使用的,并不需要依赖koa。用npm install安装所有依赖包。 - -紧接着,我们要编写使用Nunjucks的函数render。怎么写?方法是查看Nunjucks的官方文档,仔细阅读后,在app.js中编写代码如下: -``` -const nunjucks = require('nunjucks'); - -function createEnv(path, opts) { - var - autoescape = opts.autoescape === undefined ? true : opts.autoescape, - noCache = opts.noCache || false, - watch = opts.watch || false, - throwOnUndefined = opts.throwOnUndefined || false, - env = new nunjucks.Environment( - new nunjucks.FileSystemLoader('views', { - noCache: noCache, - watch: watch, - }), { - autoescape: autoescape, - throwOnUndefined: throwOnUndefined - }); - if (opts.filters) { - for (var f in opts.filters) { - env.addFilter(f, opts.filters[f]); - } - } - return env; -} - -var env = createEnv('views', { - watch: true, - filters: { - hex: function (n) { - return '0x' + n.toString(16); - } - } -}); -``` -变量env就表示Nunjucks模板引擎对象,它有一个`render(view, model)`方法,正好传入view和model两个参数,并返回字符串。 - -创建env需要的参数可以查看文档获知。我们用autoescape = opts.autoescape && true这样的代码给每个参数加上默认值,最后使用`new nunjucks.FileSystemLoader('views')`创建一个文件系统加载器,从views目录读取模板。 - -我们编写一个`hello.html`模板文件,放到views目录下,内容如下: -``` -

Hello {{ name }}

-``` -然后,我们就可以用下面的代码来渲染这个模板: -``` -var s = env.render('hello.html', { name: '小明' }); -console.log(s); -``` -获得输出如下: -``` -

Hello 小明

-``` -咋一看,这和使用JavaScript模板字符串没啥区别嘛。不过,试试: -``` -var s = env.render('hello.html', { name: '' }); -console.log(s); -``` -获得输出如下: -``` -

Hello <script>alert("小明")</script>

-``` -这样就避免了输出恶意脚本。 - -此外,可以使用Nunjucks提供的功能强大的tag,编写条件判断、循环等功能,例如: -``` - - -

Fruits List

- {% for f in fruits %} -

{{ f }}

- {% endfor %} - -``` -Nunjucks模板引擎最强大的功能在于模板的继承。仔细观察各种网站可以发现,网站的结构实际上是类似的,头部、尾部都是固定格式,只有中间页面部分内容不同。如果每个模板都重复头尾,一旦要修改头部或尾部,那就需要改动所有模板。 - -更好的方式是使用继承。先定义一个基本的网页框架base.html: -``` - -{% block header %}

Unnamed

{% endblock %} -{% block body %}
No body
{% endblock %} -{% block footer %}
copyright
{% endblock %} - - -``` -base.html定义了三个可编辑的块,分别命名为header、body和footer。子模板可以有选择地对块进行重新定义: -``` -{% extends 'base.html' %} - -{% block header %}

{{ header }}

{% endblock %} - -{% block body %}

{{ body }}

{% endblock %} -``` -然后,我们对子模板进行渲染: -``` -console.log(env.render('extend.html', { - header: 'Hello', - body: 'bla bla bla...' -})); -``` -输出HTML如下: -``` - -

Hello

-

bla bla bla...

-
copyright
<-- footer没有重定义,所以仍使用父模板的内容 - -``` \ No newline at end of file diff --git "a/33\347\211\233\346\226\207\350\275\251/20230309-MVC.md" "b/33\347\211\233\346\226\207\350\275\251/20230309-MVC.md" deleted file mode 100644 index 8f9cbd5..0000000 --- "a/33\347\211\233\346\226\207\350\275\251/20230309-MVC.md" +++ /dev/null @@ -1,173 +0,0 @@ - -## MVC -我们已经可以用koa处理不同的URL,还可以用Nunjucks渲染模板。现在,是时候把这两者结合起来了! - -当用户通过浏览器请求一个URL时,koa将调用某个异步函数处理该URL。在这个异步函数内部,我们用一行代码: -``` -ctx.render('home.html', { name: 'Michael' }); -``` -通过Nunjucks把数据用指定的模板渲染成HTML,然后输出给浏览器,用户就可以看到渲染后的页面了: - -## mvc - -这就是传说中的MVC:Model-View-Controller,中文名“模型-视图-控制器”。 - -异步函数是C:Controller,Controller负责业务逻辑,比如检查用户名是否存在,取出用户信息等等; - -包含变量{{ name }}的模板就是V:View,View负责显示逻辑,通过简单地替换一些变量,View最终输出的就是用户看到的HTML。 - -MVC中的Model在哪?Model是用来传给View的,这样View在替换变量的时候,就可以从Model中取出相应的数据。 - -上面的例子中,Model就是一个JavaScript对象: -``` -{ name: 'Michael' } -``` -下面,我们根据原来的url2-koa创建工程view-koa,把koa2、Nunjucks整合起来,然后,把原来直接输出字符串的方式,改为ctx.render(view, model)的方式。 - -工程view-koa结构如下: -``` -view-koa/ -| -+- .vscode/ -| | -| +- launch.json <-- VSCode 配置文件 -| -+- controllers/ <-- Controller -| -+- views/ <-- html模板文件 -| -+- static/ <-- 静态资源文件 -| -+- controller.js <-- 扫描注册Controller -| -+- app.js <-- 使用koa的js -| -+- package.json <-- 项目描述文件 -| -+- node_modules/ <-- npm安装的所有依赖包 -``` -在package.json中,我们将要用到的依赖包有: -``` -"koa": "2.0.0", -"koa-bodyparser": "3.2.0", -"koa-router": "7.0.0", -"nunjucks": "2.4.2", -"mime": "1.3.4", -"mz": "2.4.0" -``` -先用npm install安装依赖包。 - -然后,我们准备编写以下两个Controller: - -1. 处理首页 GET / - -我们定义一个async函数处理首页URL/: -``` -async (ctx, next) => { - ctx.render('index.html', { - title: 'Welcome' - }); -} -``` -注意到koa并没有在ctx对象上提供render方法,这里我们假设应该这么使用,这样,我们在编写Controller的时候,最后一步调用ctx.render(view, model)就完成了页面输出。 - -2. 处理登录请求 POST /signin -我们再定义一个async函数处理登录请求/signin: -``` -async (ctx, next) => { - var - email = ctx.request.body.email || '', - password = ctx.request.body.password || ''; - if (email === 'admin@example.com' && password === '123456') { - // 登录成功: - ctx.render('signin-ok.html', { - title: 'Sign In OK', - name: 'Mr Node' - }); - } else { - // 登录失败: - ctx.render('signin-failed.html', { - title: 'Sign In Failed' - }); - } -} -``` -由于登录请求是一个POST,我们就用ctx.request.body.拿到POST请求的数据,并给一个默认值。 - -登录成功时我们用signin-ok.html渲染,登录失败时我们用signin-failed.html渲染,所以,我们一共需要以下3个View: -``` -index.html -signin-ok.html -signin-failed.html -``` - -## 编写View -在编写View的时候,我们实际上是在编写HTML页。为了让页面看起来美观大方,使用一个现成的CSS框架是非常有必要的。我们用Bootstrap这个CSS框架。从首页下载zip包后解压,我们把所有静态资源文件放到/static目录下: -``` -view-koa/ -| -+- static/ - | - +- css/ <- 存放bootstrap.css等 - | - +- fonts/ <- 存放字体文件 - | - +- js/ <- 存放bootstrap.js等 -``` -这样我们在编写HTML的时候,可以直接用Bootstrap的CSS,像这样: -``` - -``` -现在,在使用MVC之前,第一个问题来了,如何处理静态文件? - -我们把所有静态资源文件全部放入/static目录,目的就是能统一处理静态文件。在koa中,我们需要编写一个middleware,处理以/static/开头的URL。 - -## 编写middleware -我们来编写一个处理静态文件的middleware。编写middleware实际上一点也不复杂。我们先创建一个static-files.js的文件,编写一个能处理静态文件的middleware: -``` -const path = require('path'); -const mime = require('mime'); -const fs = require('mz/fs'); - -// url: 类似 '/static/' -// dir: 类似 __dirname + '/static' -function staticFiles(url, dir) { - return async (ctx, next) => { - let rpath = ctx.request.path; - // 判断是否以指定的url开头: - if (rpath.startsWith(url)) { - // 获取文件完整路径: - let fp = path.join(dir, rpath.substring(url.length)); - // 判断文件是否存在: - if (await fs.exists(fp)) { - // 查找文件的mime: - ctx.response.type = mime.lookup(rpath); - // 读取文件内容并赋值给response.body: - ctx.response.body = await fs.readFile(fp); - } else { - // 文件不存在: - ctx.response.status = 404; - } - } else { - // 不是指定前缀的URL,继续处理下一个middleware: - await next(); - } - }; -} - -module.exports = staticFiles; -``` -staticFiles是一个普通函数,它接收两个参数:URL前缀和一个目录,然后返回一个async函数。这个async函数会判断当前的URL是否以指定前缀开头,如果是,就把URL的路径视为文件,并发送文件内容。如果不是,这个async函数就不做任何事情,而是简单地调用await next()让下一个middleware去处理请求。 - -我们使用了一个mz的包,并通过require('mz/fs');导入。mz提供的API和Node.js的fs模块完全相同,但fs模块使用回调,而mz封装了fs对应的函数,并改为Promise。这样,我们就可以非常简单的用await调用mz的函数,而不需要任何回调。 - -所有的第三方包都可以通过npm官网搜索并查看其文档: -``` -https://www.npmjs.com/ -``` -最后,这个middleware使用起来也很简单,在app.js里加一行代码: -``` -let staticFiles = require('./static-files'); -app.use(staticFiles('/static/', __dirname + '/static')); -``` -注意:也可以去npm搜索能用于koa2的处理静态文件的包并直接使用。 diff --git "a/33\347\211\233\346\226\207\350\275\251/20230310-\346\225\260\346\215\256\345\272\223.md" "b/33\347\211\233\346\226\207\350\275\251/20230310-\346\225\260\346\215\256\345\272\223.md" deleted file mode 100644 index f7fea44..0000000 --- "a/33\347\211\233\346\226\207\350\275\251/20230310-\346\225\260\346\215\256\345\272\223.md" +++ /dev/null @@ -1,82 +0,0 @@ -# 使用Sequelize - -## 安装 -Sequelize 的使用可以通过 npm (或 yarn). -``` -# 使用 npm -npm i sequelize # 这将安装最新版本的 Sequelize -# 使用 yarn -yarn add sequelize -``` -你还必须手动为所选数据库安装驱动程序: -``` -# 使用 npm -npm i pg pg-hstore # PostgreSQL -npm i mysql2 # MySQL -npm i mariadb # MariaDB -npm i sqlite3 # SQLite -npm i tedious # Microsoft SQL Server -npm i ibm_db # DB2 -# 使用 yarn -yarn add pg pg-hstore # PostgreSQL -yarn add mysql2 # MySQL -yarn add mariadb # MariaDB -yarn add sqlite3 # SQLite -yarn add tedious # Microsoft SQL Server -yarn add ibm_db # DB2 -``` - -## 连接到数据库 -要连接到数据库,必须创建一个 Sequelize 实例. 这可以通过将连接参数分别传递到 Sequelize 构造函数或通过传递一个连接 URI 来完成: -``` -const { Sequelize } = require('sequelize'); - -// 方法 1: 传递一个连接 URI -const sequelize = new Sequelize('sqlite::memory:') // Sqlite 示例 -const sequelize = new Sequelize('postgres://user:pass@example.com:5432/dbname') // Postgres 示例 - -// 方法 2: 分别传递参数 (sqlite) -const sequelize = new Sequelize({ - dialect: 'sqlite', - storage: 'path/to/database.sqlite' -}); - -// 方法 3: 分别传递参数 (其它数据库) -const sequelize = new Sequelize('database', 'username', 'password', { - host: 'localhost', - dialect: /* 选择 'mysql' | 'mariadb' | 'postgres' | 'mssql' 其一 */ -}); -``` -## 测试连接 -你可以使用 .authenticate() 函数测试连接是否正常: -``` -try { - await sequelize.authenticate(); - console.log('Connection has been established successfully.'); -} catch (error) { - console.error('Unable to connect to the database:', error); -} -``` - -## 使用 sequelize.define: -``` -const { Sequelize, DataTypes } = require('sequelize'); -const sequelize = new Sequelize('sqlite::memory:'); - -const User = sequelize.define('User', { - // 在这里定义模型属性 - firstName: { - type: DataTypes.STRING, - allowNull: false - }, - lastName: { - type: DataTypes.STRING - // allowNull 默认为 true - } -}, { - // 这是其他模型参数 -}); - -// `sequelize.define` 会返回模型 -console.log(User === sequelize.models.User); // true -``` \ No newline at end of file -- Gitee From c9e8f3c07b2625144683e196b6d48536e57b0f7d Mon Sep 17 00:00:00 2001 From: "@chen-li-xing" <19912973651> Date: Tue, 14 Mar 2023 10:02:00 +0800 Subject: [PATCH 03/20] yes --- ...46\347\254\254\344\270\200\350\257\276.md" | 2 - ...JS\346\250\241\345\235\227\345\214\226.md" | 25 -- .../0030220\344\275\234\344\270\232.md" | 81 ----- .../003NodeJs.md" | 4 - ...351\227\250URL\345\244\204\347\220\206.md" | 327 ------------------ .../20230228-\345\244\204\347\220\206url.md" | 327 ------------------ ...71\347\233\256\351\207\215\346\236\204.md" | 140 -------- .../20230303-\351\207\215\346\236\204.md" | 36 -- ...15\346\236\204\344\274\230\345\214\226.md" | 129 ------- ...41\345\235\227\345\274\225\346\223\216.md" | 214 ------------ .../20230309-MVC.md" | 327 ------------------ ...45\346\225\260\346\215\256\345\272\223.md" | 284 --------------- 12 files changed, 1896 deletions(-) delete mode 100644 "12\351\276\232\344\270\234\350\276\211/001\345\274\200\345\255\246\347\254\254\344\270\200\350\257\276.md" delete mode 100644 "12\351\276\232\344\270\234\350\276\211/002nodeJS\346\250\241\345\235\227\345\214\226.md" delete mode 100644 "12\351\276\232\344\270\234\350\276\211/0030220\344\275\234\344\270\232.md" delete mode 100644 "12\351\276\232\344\270\234\350\276\211/003NodeJs.md" delete mode 100644 "12\351\276\232\344\270\234\350\276\211/20230227-koa\345\205\245\351\227\250URL\345\244\204\347\220\206.md" delete mode 100644 "12\351\276\232\344\270\234\350\276\211/20230228-\345\244\204\347\220\206url.md" delete mode 100644 "12\351\276\232\344\270\234\350\276\211/20230302-URL\345\244\204\347\220\206\350\257\267\346\261\202\345\222\214\351\241\271\347\233\256\351\207\215\346\236\204.md" delete mode 100644 "12\351\276\232\344\270\234\350\276\211/20230303-\351\207\215\346\236\204.md" delete mode 100644 "12\351\276\232\344\270\234\350\276\211/20230306-\351\207\215\346\236\204\344\274\230\345\214\226.md" delete mode 100644 "12\351\276\232\344\270\234\350\276\211/20230307-\346\250\241\345\235\227\345\274\225\346\223\216.md" delete mode 100644 "12\351\276\232\344\270\234\350\276\211/20230309-MVC.md" delete mode 100644 "12\351\276\232\344\270\234\350\276\211/20230310-\350\277\236\346\216\245\346\225\260\346\215\256\345\272\223.md" diff --git "a/12\351\276\232\344\270\234\350\276\211/001\345\274\200\345\255\246\347\254\254\344\270\200\350\257\276.md" "b/12\351\276\232\344\270\234\350\276\211/001\345\274\200\345\255\246\347\254\254\344\270\200\350\257\276.md" deleted file mode 100644 index 4b36de4..0000000 --- "a/12\351\276\232\344\270\234\350\276\211/001\345\274\200\345\255\246\347\254\254\344\270\200\350\257\276.md" +++ /dev/null @@ -1,2 +0,0 @@ -# Hello Node! - diff --git "a/12\351\276\232\344\270\234\350\276\211/002nodeJS\346\250\241\345\235\227\345\214\226.md" "b/12\351\276\232\344\270\234\350\276\211/002nodeJS\346\250\241\345\235\227\345\214\226.md" deleted file mode 100644 index d9f4676..0000000 --- "a/12\351\276\232\344\270\234\350\276\211/002nodeJS\346\250\241\345\235\227\345\214\226.md" +++ /dev/null @@ -1,25 +0,0 @@ -## nodeJS模块化 - -### 模块分类: - -- 内置模块 - `由Node.js官方提供的,例如 fs、path、http等` - -- 自定义模块 - - `用户创建的js文件` - -- 第三方模块 - - `非官方提供的内置模块,使用前需download` - -### 加载模块 - -`使用require()方法加载需要的模块,加载时会执行被加载模块的代码` - - - -### 导出模块 - -`使用moduls.exports将模块内的成员共享出去` - diff --git "a/12\351\276\232\344\270\234\350\276\211/0030220\344\275\234\344\270\232.md" "b/12\351\276\232\344\270\234\350\276\211/0030220\344\275\234\344\270\232.md" deleted file mode 100644 index baed181..0000000 --- "a/12\351\276\232\344\270\234\350\276\211/0030220\344\275\234\344\270\232.md" +++ /dev/null @@ -1,81 +0,0 @@ -```javascript - -'use strict' -/** - * 在浏览器输入http://localhost:8080/时,会返回404,原因是程序识别出HTTP请求的不是文件 - * 而是目录。请修改file_server.js,如果遇到请求的路径是目录, - * 则自动在目录下依次搜索index.html、default.html,如果找到了,就返回HTML文件的内容。 - * -*/ -let http = require('http') // 导入模块 -let fs = require('fs') //文件模块 -//创建服务器 -let server = http.createServer((req,res)=>{ - // 获取url路径 localhost:8080/{url} - let url = req.url; - // /url -> url - url = url.substring(1,url.length); - //查看url文件状态 - fs.stat(url,(err,stat)=>{ - // 访问 localhost:8080 - if(url=="") { - res.end("welcome!"); - }else if(err) {//未找到文件 - res.end("not find"); - return; - }else if(stat.isDirectory()){//不为空,并且有找到文件判断是否为目录 - //在目录下依次搜索index.html、default.html - fs.readdir(url,(err,files)=>{//read dir - let flag = true; - //查询有没有index.html - files.map(x=>{ - let path; - if(x=="index.html"){ - path = `./${url}/${x}`; - fs.readFile(path,"utf-8",(err,data)=>{ - res.end(data); - }); - flag=false; //找到index.html,未找到默认为true - return; - } - }); - //查询default.html - if(flag){ - let flag = true; - files.map(x=>{ - let path; - if(x=="default.html"){ - path = `./${url}/${x}`; - fs.readFile(path,"utf-8",(err,data)=>{ - res.end(data); - }); - flag = false;//找到default.html - return; - } - }); - if(flag){ - res.end("404_Not_find") - } - } - }); - return; - } - //不是目录 - fs.readFile(url,(err,data)=>{ - if(err){ - res.end("404"); - return; - } - res.end(data); - }) - }) -}); -server.listen("8080"); - - - - - - - -``` \ No newline at end of file diff --git "a/12\351\276\232\344\270\234\350\276\211/003NodeJs.md" "b/12\351\276\232\344\270\234\350\276\211/003NodeJs.md" deleted file mode 100644 index 422f7fc..0000000 --- "a/12\351\276\232\344\270\234\350\276\211/003NodeJs.md" +++ /dev/null @@ -1,4 +0,0 @@ -## promise - -promise异步编程,太复杂,ES7新特性:async将普通函数转为promise,使用await 设置变量表示需要等待执行结果 - diff --git "a/12\351\276\232\344\270\234\350\276\211/20230227-koa\345\205\245\351\227\250URL\345\244\204\347\220\206.md" "b/12\351\276\232\344\270\234\350\276\211/20230227-koa\345\205\245\351\227\250URL\345\244\204\347\220\206.md" deleted file mode 100644 index 223c2f4..0000000 --- "a/12\351\276\232\344\270\234\350\276\211/20230227-koa\345\205\245\351\227\250URL\345\244\204\347\220\206.md" +++ /dev/null @@ -1,327 +0,0 @@ -# 处理URL - -在hello-koa工程中,我们处理http请求一律返回相同的HTML,这样虽然非常简单,但是用浏览器一测,随便输入任何URL都会返回相同的网页。 - - -正常情况下,我们应该对不同的URL调用不同的处理函数,这样才能返回不同的结果。例如像这样写: -``` -app.use(async (ctx, next) => { - if (ctx.request.path === '/') { - ctx.response.body = 'index page'; - } else { - await next(); - } -}); - -app.use(async (ctx, next) => { - if (ctx.request.path === '/test') { - ctx.response.body = 'TEST page'; - } else { - await next(); - } -}); - -app.use(async (ctx, next) => { - if (ctx.request.path === '/error') { - ctx.response.body = 'ERROR page'; - } else { - await next(); - } -}); -``` -这么写是可以运行的,但是好像有点蠢。 - -应该有一个能集中处理URL的middleware,它根据不同的URL调用不同的处理函数,这样,我们才能专心为每个URL编写处理函数。 - -## koa-router -为了处理URL,我们需要引入koa-router这个middleware,让它负责处理URL映射。 - -我们把上一节的hello-koa工程复制一份,重命名为url-koa。 - -先在package.json中添加依赖项: -``` -"koa-router": "7.0.0" -``` -然后用npm install安装。 - -接下来,我们修改app.js,使用koa-router来处理URL: -``` -const Koa = require('koa'); - -// 注意require('koa-router')返回的是函数: -const router = require('koa-router')(); - -const app = new Koa(); - -// log request URL: -app.use(async (ctx, next) => { - console.log(`Process ${ctx.request.method} ${ctx.request.url}...`); - await next(); -}); - -// add url-route: -router.get('/hello/:name', async (ctx, next) => { - var name = ctx.params.name; - ctx.response.body = `

Hello, ${name}!

`; -}); - -router.get('/', async (ctx, next) => { - ctx.response.body = '

Index

'; -}); - -// add router middleware: -app.use(router.routes()); - -app.listen(3000); -console.log('app started at port 3000...'); -``` -注意导入koa-router的语句最后的()是函数调用: -``` -const router = require('koa-router')(); -``` -相当于: -``` -const fn_router = require('koa-router'); -const router = fn_router(); -``` -然后,我们使用router.get('/path', async fn)来注册一个GET请求。可以在请求路径中使用带变量的/hello/:name,变量可以通过ctx.params.name访问。 - -再运行app.js,我们就可以测试不同的URL: -``` -输入首页:http://localhost:3000/ -``` -``` -输入:http://localhost:3000/hello/koa -``` - -## 处理post请求 -用router.get('/path', async fn)处理的是get请求。如果要处理post请求,可以用router.post('/path', async fn)。 - -用post请求处理URL时,我们会遇到一个问题:post请求通常会发送一个表单,或者JSON,它作为request的body发送,但无论是Node.js提供的原始request对象,还是koa提供的request对象,都不提供解析request的body的功能! - -所以,我们又需要引入另一个middleware来解析原始request请求,然后,把解析后的参数,绑定到ctx.request.body中。 - -koa-bodyparser就是用来干这个活的。 - -我们在package.json中添加依赖项: -``` -"koa-bodyparser": "3.2.0" -``` -然后使用npm install安装。 - -下面,修改app.js,引入koa-bodyparser: -``` -const bodyParser = require('koa-bodyparser'); -``` -在合适的位置加上: -``` -app.use(bodyParser()); -``` -由于middleware的顺序很重要,这个koa-bodyparser必须在router之前被注册到app对象上。 - -现在我们就可以处理post请求了。写一个简单的登录表单: -``` -router.get('/', async (ctx, next) => { - ctx.response.body = `

Index

-
-

Name:

-

Password:

-

-
`; -}); - -router.post('/signin', async (ctx, next) => { - var - name = ctx.request.body.name || '', - password = ctx.request.body.password || ''; - console.log(`signin with name: ${name}, password: ${password}`); - if (name === 'koa' && password === '12345') { - ctx.response.body = `

Welcome, ${name}!

`; - } else { - ctx.response.body = `

Login failed!

-

Try again

`; - } -}); -``` -注意到我们用var name = ctx.request.body.name || ''拿到表单的name字段,如果该字段不存在,默认值设置为''。 - -类似的,put、delete、head请求也可以由router处理。 - -## 重构 -现在,我们已经可以处理不同的URL了,但是看看app.js,总觉得还是有点不对劲。 - - -所有的URL处理函数都放到app.js里显得很乱,而且,每加一个URL,就需要修改app.js。随着URL越来越多,app.js就会越来越长。 - -如果能把URL处理函数集中到某个js文件,或者某几个js文件中就好了,然后让app.js自动导入所有处理URL的函数。这样,代码一分离,逻辑就显得清楚了。最好是这样: -``` -url2-koa/ -| -+- .vscode/ -| | -| +- launch.json <-- VSCode 配置文件 -| -+- controllers/ -| | -| +- login.js <-- 处理login相关URL -| | -| +- users.js <-- 处理用户管理相关URL -| -+- app.js <-- 使用koa的js -| -+- package.json <-- 项目描述文件 -| -+- node_modules/ <-- npm安装的所有依赖包 -``` -于是我们把url-koa复制一份,重命名为url2-koa,准备重构这个项目。 - -我们先在controllers目录下编写index.js: -``` -var fn_index = async (ctx, next) => { - ctx.response.body = `

Index

-
-

Name:

-

Password:

-

-
`; -}; - -var fn_signin = async (ctx, next) => { - var - name = ctx.request.body.name || '', - password = ctx.request.body.password || ''; - console.log(`signin with name: ${name}, password: ${password}`); - if (name === 'koa' && password === '12345') { - ctx.response.body = `

Welcome, ${name}!

`; - } else { - ctx.response.body = `

Login failed!

-

Try again

`; - } -}; - -module.exports = { - 'GET /': fn_index, - 'POST /signin': fn_signin -}; -``` - -这个index.js通过module.exports把两个URL处理函数暴露出来。 - -类似的,hello.js把一个URL处理函数暴露出来: -``` -var fn_hello = async (ctx, next) => { - var name = ctx.params.name; - ctx.response.body = `

Hello, ${name}!

`; -}; - -module.exports = { - 'GET /hello/:name': fn_hello -}; -``` -现在,我们修改app.js,让它自动扫描controllers目录,找到所有js文件,导入,然后注册每个URL: -``` -// 先导入fs模块,然后用readdirSync列出文件 -// 这里可以用sync是因为启动时只运行一次,不存在性能问题: -var files = fs.readdirSync(__dirname + '/controllers'); - -// 过滤出.js文件: -var js_files = files.filter((f)=>{ - return f.endsWith('.js'); -}); - -// 处理每个js文件: -for (var f of js_files) { - console.log(`process controller: ${f}...`); - // 导入js文件: - let mapping = require(__dirname + '/controllers/' + f); - for (var url in mapping) { - if (url.startsWith('GET ')) { - // 如果url类似"GET xxx": - var path = url.substring(4); - router.get(path, mapping[url]); - console.log(`register URL mapping: GET ${path}`); - } else if (url.startsWith('POST ')) { - // 如果url类似"POST xxx": - var path = url.substring(5); - router.post(path, mapping[url]); - console.log(`register URL mapping: POST ${path}`); - } else { - // 无效的URL: - console.log(`invalid URL: ${url}`); - } - } -} -``` - -如果上面的大段代码看起来还是有点费劲,那就把它拆成更小单元的函数: -``` -function addMapping(router, mapping) { - for (var url in mapping) { - if (url.startsWith('GET ')) { - var path = url.substring(4); - router.get(path, mapping[url]); - console.log(`register URL mapping: GET ${path}`); - } else if (url.startsWith('POST ')) { - var path = url.substring(5); - router.post(path, mapping[url]); - console.log(`register URL mapping: POST ${path}`); - } else { - console.log(`invalid URL: ${url}`); - } - } -} - -function addControllers(router) { - var files = fs.readdirSync(__dirname + '/controllers'); - var js_files = files.filter((f) => { - return f.endsWith('.js'); - }); - - for (var f of js_files) { - console.log(`process controller: ${f}...`); - let mapping = require(__dirname + '/controllers/' + f); - addMapping(router, mapping); - } -} - -addControllers(router); -``` - -确保每个函数功能非常简单,一眼能看明白,是代码可维护的关键。 - -## Controller Middleware -最后,我们把扫描controllers目录和创建router的代码从app.js中提取出来,作为一个简单的middleware使用,命名为controller.js: -``` -const fs = require('fs'); - -function addMapping(router, mapping) { - ... -} - -function addControllers(router, dir) { - ... -} - -module.exports = function (dir) { - let - controllers_dir = dir || 'controllers', // 如果不传参数,扫描目录默认为'controllers' - router = require('koa-router')(); - addControllers(router, controllers_dir); - return router.routes(); -}; -``` -这样一来,我们在app.js的代码又简化了: -``` -... - -// 导入controller middleware: -const controller = require('./controller'); - -... - -// 使用middleware: -app.use(controller()); - -... -``` -经过重新整理后的工程url2-koa目前具备非常好的模块化,所有处理URL的函数按功能组存放在controllers目录,今后我们也只需要不断往这个目录下加东西就可以了,app.js保持不变。 \ No newline at end of file diff --git "a/12\351\276\232\344\270\234\350\276\211/20230228-\345\244\204\347\220\206url.md" "b/12\351\276\232\344\270\234\350\276\211/20230228-\345\244\204\347\220\206url.md" deleted file mode 100644 index 223c2f4..0000000 --- "a/12\351\276\232\344\270\234\350\276\211/20230228-\345\244\204\347\220\206url.md" +++ /dev/null @@ -1,327 +0,0 @@ -# 处理URL - -在hello-koa工程中,我们处理http请求一律返回相同的HTML,这样虽然非常简单,但是用浏览器一测,随便输入任何URL都会返回相同的网页。 - - -正常情况下,我们应该对不同的URL调用不同的处理函数,这样才能返回不同的结果。例如像这样写: -``` -app.use(async (ctx, next) => { - if (ctx.request.path === '/') { - ctx.response.body = 'index page'; - } else { - await next(); - } -}); - -app.use(async (ctx, next) => { - if (ctx.request.path === '/test') { - ctx.response.body = 'TEST page'; - } else { - await next(); - } -}); - -app.use(async (ctx, next) => { - if (ctx.request.path === '/error') { - ctx.response.body = 'ERROR page'; - } else { - await next(); - } -}); -``` -这么写是可以运行的,但是好像有点蠢。 - -应该有一个能集中处理URL的middleware,它根据不同的URL调用不同的处理函数,这样,我们才能专心为每个URL编写处理函数。 - -## koa-router -为了处理URL,我们需要引入koa-router这个middleware,让它负责处理URL映射。 - -我们把上一节的hello-koa工程复制一份,重命名为url-koa。 - -先在package.json中添加依赖项: -``` -"koa-router": "7.0.0" -``` -然后用npm install安装。 - -接下来,我们修改app.js,使用koa-router来处理URL: -``` -const Koa = require('koa'); - -// 注意require('koa-router')返回的是函数: -const router = require('koa-router')(); - -const app = new Koa(); - -// log request URL: -app.use(async (ctx, next) => { - console.log(`Process ${ctx.request.method} ${ctx.request.url}...`); - await next(); -}); - -// add url-route: -router.get('/hello/:name', async (ctx, next) => { - var name = ctx.params.name; - ctx.response.body = `

Hello, ${name}!

`; -}); - -router.get('/', async (ctx, next) => { - ctx.response.body = '

Index

'; -}); - -// add router middleware: -app.use(router.routes()); - -app.listen(3000); -console.log('app started at port 3000...'); -``` -注意导入koa-router的语句最后的()是函数调用: -``` -const router = require('koa-router')(); -``` -相当于: -``` -const fn_router = require('koa-router'); -const router = fn_router(); -``` -然后,我们使用router.get('/path', async fn)来注册一个GET请求。可以在请求路径中使用带变量的/hello/:name,变量可以通过ctx.params.name访问。 - -再运行app.js,我们就可以测试不同的URL: -``` -输入首页:http://localhost:3000/ -``` -``` -输入:http://localhost:3000/hello/koa -``` - -## 处理post请求 -用router.get('/path', async fn)处理的是get请求。如果要处理post请求,可以用router.post('/path', async fn)。 - -用post请求处理URL时,我们会遇到一个问题:post请求通常会发送一个表单,或者JSON,它作为request的body发送,但无论是Node.js提供的原始request对象,还是koa提供的request对象,都不提供解析request的body的功能! - -所以,我们又需要引入另一个middleware来解析原始request请求,然后,把解析后的参数,绑定到ctx.request.body中。 - -koa-bodyparser就是用来干这个活的。 - -我们在package.json中添加依赖项: -``` -"koa-bodyparser": "3.2.0" -``` -然后使用npm install安装。 - -下面,修改app.js,引入koa-bodyparser: -``` -const bodyParser = require('koa-bodyparser'); -``` -在合适的位置加上: -``` -app.use(bodyParser()); -``` -由于middleware的顺序很重要,这个koa-bodyparser必须在router之前被注册到app对象上。 - -现在我们就可以处理post请求了。写一个简单的登录表单: -``` -router.get('/', async (ctx, next) => { - ctx.response.body = `

Index

-
-

Name:

-

Password:

-

-
`; -}); - -router.post('/signin', async (ctx, next) => { - var - name = ctx.request.body.name || '', - password = ctx.request.body.password || ''; - console.log(`signin with name: ${name}, password: ${password}`); - if (name === 'koa' && password === '12345') { - ctx.response.body = `

Welcome, ${name}!

`; - } else { - ctx.response.body = `

Login failed!

-

Try again

`; - } -}); -``` -注意到我们用var name = ctx.request.body.name || ''拿到表单的name字段,如果该字段不存在,默认值设置为''。 - -类似的,put、delete、head请求也可以由router处理。 - -## 重构 -现在,我们已经可以处理不同的URL了,但是看看app.js,总觉得还是有点不对劲。 - - -所有的URL处理函数都放到app.js里显得很乱,而且,每加一个URL,就需要修改app.js。随着URL越来越多,app.js就会越来越长。 - -如果能把URL处理函数集中到某个js文件,或者某几个js文件中就好了,然后让app.js自动导入所有处理URL的函数。这样,代码一分离,逻辑就显得清楚了。最好是这样: -``` -url2-koa/ -| -+- .vscode/ -| | -| +- launch.json <-- VSCode 配置文件 -| -+- controllers/ -| | -| +- login.js <-- 处理login相关URL -| | -| +- users.js <-- 处理用户管理相关URL -| -+- app.js <-- 使用koa的js -| -+- package.json <-- 项目描述文件 -| -+- node_modules/ <-- npm安装的所有依赖包 -``` -于是我们把url-koa复制一份,重命名为url2-koa,准备重构这个项目。 - -我们先在controllers目录下编写index.js: -``` -var fn_index = async (ctx, next) => { - ctx.response.body = `

Index

-
-

Name:

-

Password:

-

-
`; -}; - -var fn_signin = async (ctx, next) => { - var - name = ctx.request.body.name || '', - password = ctx.request.body.password || ''; - console.log(`signin with name: ${name}, password: ${password}`); - if (name === 'koa' && password === '12345') { - ctx.response.body = `

Welcome, ${name}!

`; - } else { - ctx.response.body = `

Login failed!

-

Try again

`; - } -}; - -module.exports = { - 'GET /': fn_index, - 'POST /signin': fn_signin -}; -``` - -这个index.js通过module.exports把两个URL处理函数暴露出来。 - -类似的,hello.js把一个URL处理函数暴露出来: -``` -var fn_hello = async (ctx, next) => { - var name = ctx.params.name; - ctx.response.body = `

Hello, ${name}!

`; -}; - -module.exports = { - 'GET /hello/:name': fn_hello -}; -``` -现在,我们修改app.js,让它自动扫描controllers目录,找到所有js文件,导入,然后注册每个URL: -``` -// 先导入fs模块,然后用readdirSync列出文件 -// 这里可以用sync是因为启动时只运行一次,不存在性能问题: -var files = fs.readdirSync(__dirname + '/controllers'); - -// 过滤出.js文件: -var js_files = files.filter((f)=>{ - return f.endsWith('.js'); -}); - -// 处理每个js文件: -for (var f of js_files) { - console.log(`process controller: ${f}...`); - // 导入js文件: - let mapping = require(__dirname + '/controllers/' + f); - for (var url in mapping) { - if (url.startsWith('GET ')) { - // 如果url类似"GET xxx": - var path = url.substring(4); - router.get(path, mapping[url]); - console.log(`register URL mapping: GET ${path}`); - } else if (url.startsWith('POST ')) { - // 如果url类似"POST xxx": - var path = url.substring(5); - router.post(path, mapping[url]); - console.log(`register URL mapping: POST ${path}`); - } else { - // 无效的URL: - console.log(`invalid URL: ${url}`); - } - } -} -``` - -如果上面的大段代码看起来还是有点费劲,那就把它拆成更小单元的函数: -``` -function addMapping(router, mapping) { - for (var url in mapping) { - if (url.startsWith('GET ')) { - var path = url.substring(4); - router.get(path, mapping[url]); - console.log(`register URL mapping: GET ${path}`); - } else if (url.startsWith('POST ')) { - var path = url.substring(5); - router.post(path, mapping[url]); - console.log(`register URL mapping: POST ${path}`); - } else { - console.log(`invalid URL: ${url}`); - } - } -} - -function addControllers(router) { - var files = fs.readdirSync(__dirname + '/controllers'); - var js_files = files.filter((f) => { - return f.endsWith('.js'); - }); - - for (var f of js_files) { - console.log(`process controller: ${f}...`); - let mapping = require(__dirname + '/controllers/' + f); - addMapping(router, mapping); - } -} - -addControllers(router); -``` - -确保每个函数功能非常简单,一眼能看明白,是代码可维护的关键。 - -## Controller Middleware -最后,我们把扫描controllers目录和创建router的代码从app.js中提取出来,作为一个简单的middleware使用,命名为controller.js: -``` -const fs = require('fs'); - -function addMapping(router, mapping) { - ... -} - -function addControllers(router, dir) { - ... -} - -module.exports = function (dir) { - let - controllers_dir = dir || 'controllers', // 如果不传参数,扫描目录默认为'controllers' - router = require('koa-router')(); - addControllers(router, controllers_dir); - return router.routes(); -}; -``` -这样一来,我们在app.js的代码又简化了: -``` -... - -// 导入controller middleware: -const controller = require('./controller'); - -... - -// 使用middleware: -app.use(controller()); - -... -``` -经过重新整理后的工程url2-koa目前具备非常好的模块化,所有处理URL的函数按功能组存放在controllers目录,今后我们也只需要不断往这个目录下加东西就可以了,app.js保持不变。 \ No newline at end of file diff --git "a/12\351\276\232\344\270\234\350\276\211/20230302-URL\345\244\204\347\220\206\350\257\267\346\261\202\345\222\214\351\241\271\347\233\256\351\207\215\346\236\204.md" "b/12\351\276\232\344\270\234\350\276\211/20230302-URL\345\244\204\347\220\206\350\257\267\346\261\202\345\222\214\351\241\271\347\233\256\351\207\215\346\236\204.md" deleted file mode 100644 index 49dec6f..0000000 --- "a/12\351\276\232\344\270\234\350\276\211/20230302-URL\345\244\204\347\220\206\350\257\267\346\261\202\345\222\214\351\241\271\347\233\256\351\207\215\346\236\204.md" +++ /dev/null @@ -1,140 +0,0 @@ -**Node.js**——**URL处理请求和项目重构** -=== -在一个**Node.js的web应用项目**里,**URL的处理**可以说是非常繁琐的,所以要去 *进行包装重构,这样能让代码更加简洁,逻辑更加清晰明了* ,创建一个文件夹去将这些不同页面访问URL处理进行封装成模块,只用去引用 - -一 .url的处理请求: ---- - -### 1 .http的请求方法: -+ URL处理,http的请求方法: - -| **方法**: | 描述: | -| ----------- | ------------------------------------------------------------ | -| **GET** | 请求指定的页面信息,并返回实体主体 | -| **HEAD** | 类似于 GET 请求,只不过返回的响应中没有具体的内容,用于获取报头 | -| **POST** | 向指定资源提交数据进行处理请求(例如提交表单或者上传文件)。数据被包含在请求体中。POST 请求可能会导致新的资源的建立和/或已有资源的修改 | -| **PUT** | 从客户端向服务器传送的数据取代指定的文档的内容 | -| **DELETE** | 请求服务器删除指定的页面 | -| **CONNECT** | HTTP/1.1 协议中预留给能够将连接改为管道方式的代理服务器 | -| **OPTIONS** | 允许客户端查看服务器的性能 | -| **TRACE** | 回显服务器收到的请求,主要用于测试或诊断 | -| **PATCH** | 是对 PUT 方法的补充,用来对已知资源进行局部更新 | - - > 常用的请求方法有**get、post、put、delete**这些请求,会常写这些请求的处理方法和模块 - -### 2 .对URL处理引用的模块: -+ 用Koa框架在对URL路由处理时,会引用到很多的模块 - > 会引用到的模块代码: - ```js - const - Koa = require('Koa'), - router = require('kao-router')(), - bodyParser = require('koa-bodyparser'); - fs = require('fs'); - ``` - > 这些模块的 - -二 .项目模块扫描重构: ---- -+ 我们把项目重构,很多模块都存放在一个文件夹下,包装好,所以在调用的时候,将他们都扫描一遍,不用再去一个一个的调用,也是为了减少代码的写入量 - - > 现在,我们修改**主程序js文件**让它自动**扫描controllers目录**,*找到所有js文件,导入,然后注册每个URL* : - ```js - // 先导入fs模块,然后用readdirSync列出文件 - // 这里可以用sync是因为启动时只运行一次,不存在性能问题: - var files = fs.readdirSync(__dirname + '/controllers'); - // 过滤出.js文件: - var js_files = files.filter((f)=>{ - return f.endsWith('.js'); - }); - // 处理每个js文件: - for (var f of js_files) { - console.log(`process controller: ${f}...`); - // 导入js文件: - let mapping = require(__dirname + '/controllers/' + f); - for (var url in mapping) { - if (url.startsWith('GET ')) { - // 如果url类似"GET xxx": - var path = url.substring(4); - router.get(path, mapping[url]); - console.log(`register URL mapping: GET ${path}`); - } else if (url.startsWith('POST ')) { - // 如果url类似"POST xxx": - var path = url.substring(5); - router.post(path, mapping[url]); - console.log(`register URL mapping: POST ${path}`); - } else { - // 无效的URL: - console.log(`invalid URL: ${url}`); - } - } - } - ``` - > 如果上面的大段代码看起来还是有点费劲,那就把它拆成更小单元的函数: - ```js - function addMapping(router, mapping) { - for (var url in mapping) { - if (url.startsWith('GET ')) { - var path = url.substring(4); - router.get(path, mapping[url]); - console.log(`register URL mapping: GET ${path}`); - } else if (url.startsWith('POST ')) { - var path = url.substring(5); - router.post(path, mapping[url]); - console.log(`register URL mapping: POST ${path}`); - } else { - console.log(`invalid URL: ${url}`); - } - } - } - - function addControllers(router) { - var files = fs.readdirSync(__dirname + '/controllers'); - var js_files = files.filter((f) => { - return f.endsWith('.js'); - }); - - for (var f of js_files) { - console.log(`process controller: ${f}...`); - let mapping = require(__dirname + '/controllers/' + f); - addMapping(router, mapping); - } - } - - addControllers(router); - ``` - + 确保每个函数功能非常简单,一眼能看明白,是代码可维护的关键 - -三 .设置 Middleware ---- -将一些在**controllers目录和route要引入的模块封装好在一个模块中**,这样去减少主程序js文件中的繁琐代码 -+ 最后把扫描**controllers目录**和**创建router的代码**从 *入口js文件中提取出来,作为一个简单的middleware使用* ,命名为**index.js**存放在**controllers目录**: - ```js - const fs = require('fs'); - - function addMapping(router, mapping) { - ... - } - function addControllers(router, dir) { - ... - } - module.exports = function (dir) { - let - // 如果不传参数,扫描目录默认为'controllers' - controllers_dir = dir || 'controllers', - router = require('koa-router')(); - addControllers(router, controllers_dir); - return router.routes(); - }; - ``` - > 这样一来,我们在**主程序的js文件代码**就又简化了: - ```js - ... - // 导入controller middleware: - const controller = require('./controller'); - ... - // 使用middleware: - app.use(controller()); - ... - ``` -+ 经过重新整理后的项目具备非常好的模块化,所有**处理URL**的***函数按功能组存放在controllers目录,今后我们也只需要不断往这个目录下加东西就可以了,主程序的js文件保持不变*** \ No newline at end of file diff --git "a/12\351\276\232\344\270\234\350\276\211/20230303-\351\207\215\346\236\204.md" "b/12\351\276\232\344\270\234\350\276\211/20230303-\351\207\215\346\236\204.md" deleted file mode 100644 index a42640e..0000000 --- "a/12\351\276\232\344\270\234\350\276\211/20230303-\351\207\215\346\236\204.md" +++ /dev/null @@ -1,36 +0,0 @@ -## Controller Middleware -最后,我们把扫描controllers目录和创建router的代码从app.js中提取出来,作为一个简单的middleware使用,命名为controller.js: -``` -const fs = require('fs'); - -function addMapping(router, mapping) { - ... -} - -function addControllers(router, dir) { - ... -} - -module.exports = function (dir) { - let - controllers_dir = dir || 'controllers', // 如果不传参数,扫描目录默认为'controllers' - router = require('koa-router')(); - addControllers(router, controllers_dir); - return router.routes(); -}; -``` -这样一来,我们在app.js的代码又简化了: -``` -... - -// 导入controller middleware: -const controller = require('./controller'); - -... - -// 使用middleware: -app.use(controller()); - -... -``` -经过重新整理后的工程url2-koa目前具备非常好的模块化,所有处理URL的函数按功能组存放在controllers目录,今后我们也只需要不断往这个目录下加东西就可以了,app.js保持不变。 \ No newline at end of file diff --git "a/12\351\276\232\344\270\234\350\276\211/20230306-\351\207\215\346\236\204\344\274\230\345\214\226.md" "b/12\351\276\232\344\270\234\350\276\211/20230306-\351\207\215\346\236\204\344\274\230\345\214\226.md" deleted file mode 100644 index ea6a65d..0000000 --- "a/12\351\276\232\344\270\234\350\276\211/20230306-\351\207\215\346\236\204\344\274\230\345\214\226.md" +++ /dev/null @@ -1,129 +0,0 @@ -# 重构优化 - -简单来说就是新建一个工具文件,将可以复用的工具类代码放进去,看起来非常舒适 - -## 代码: - -### app.js -```js -'use strict' -const koa=require('koa'); -const controllers=require('./controllers') - -let app=new koa(); - -controllers(app); - -let port=5000 -app.listen(port) -console.log(`kk:http://localhost:${port}`); -``` - -### tools.js - -```js -'use strict' -const fs=require('fs'); - -//1.找到所有路由 -function findAllRouter(path) { - path=path||'./controllers'; - - let files=fs.readdirSync(path) - return files.filter( - item=>{ - return item!=='index.js' - } - ) - -} - -//遍历并注册路由 - -function resign(obj,router) { - for (const key in obj) { - let sp=key.split(' '); - let rou=sp[0]; - let url=sp[1]; - let ff=obj[key]; - if(rou=='get'){ - router.get(url,ff) - }else if(rou=='post'){ - router.post(url,ff) - }else if(rou=='put'){ - router.put(url,ff) - } - else if(rou=='delete'){ - router.delete(url,ff) - } - } - - ; -} - - -module.exports={ - findAllRouter,resign -} - -``` - - -### index.js -```js -'use strict' - -const router=require('koa-router')(); -const bodyparser=require('koa-bodyparser'); -const {findAllRouter,resign}=require('../utils/tools') - -function process(app) { - let files=findAllRouter(); - files.forEach( - item=>{ - let cons=require('../controllers/'+item.replace('.js','')) - resign(cons,router) - } - ) - app.use(router.routes()); - app.use(bodyparser()) -} - -module.exports=process - -``` - -### 还是各种数据.js - -重复太多只放一个 - -```js -'use strict' - -function getAll(ctx,next) { - ctx.body='1111'; -} -function getById(ctx,next) { - ctx.body='2222'; -} -function insertItem(ctx,next) { - -} -function updateById(ctx,next) { - -} -function deleteById(ctx,next) { - -} - - -module.exports={ - 'get /student':getAll, - 'get /student/:id':getById, - 'post /student':insertItem, - 'put /student/:id':updateById, - 'delete /student/:id':deleteById - -} - -``` \ No newline at end of file diff --git "a/12\351\276\232\344\270\234\350\276\211/20230307-\346\250\241\345\235\227\345\274\225\346\223\216.md" "b/12\351\276\232\344\270\234\350\276\211/20230307-\346\250\241\345\235\227\345\274\225\346\223\216.md" deleted file mode 100644 index c666e2b..0000000 --- "a/12\351\276\232\344\270\234\350\276\211/20230307-\346\250\241\345\235\227\345\274\225\346\223\216.md" +++ /dev/null @@ -1,214 +0,0 @@ -# 使用Nunjucks - -## Nunjucks -Nunjucks是什么东东?其实它是一个模板引擎。 - -那什么是模板引擎? - -模板引擎就是基于模板配合数据构造出字符串输出的一个组件。比如下面的函数就是一个模板引擎: -``` -function examResult (data) { - return `${data.name}同学一年级期末考试语文${data.chinese}分,数学${data.math}分,位于年级第${data.ranking}名。` -} -``` -如果我们输入数据如下: -``` -examResult({ - name: '小明', - chinese: 78, - math: 87, - ranking: 999 -}); -``` -该模板引擎把模板字符串里面对应的变量替换以后,就可以得到以下输出: - -小明同学一年级期末考试语文78分,数学87分,位于年级第999名。 - -模板引擎最常见的输出就是输出网页,也就是HTML文本。当然,也可以输出任意格式的文本,比如Text,XML,Markdown等等。 - -有同学要问了:既然JavaScript的模板字符串可以实现模板功能,那为什么我们还需要另外的模板引擎? - -因为JavaScript的模板字符串必须写在JavaScript代码中,要想写出新浪首页这样复杂的页面,是非常困难的。 - -输出HTML有几个特别重要的问题需要考虑: - -## 转义 -对特殊字符要转义,避免受到XSS攻击。比如,如果变量name的值不是小明,而是小明,模板引擎输出的HTML到了浏览器,就会自动执行恶意JavaScript代码。 - -## 格式化 -对不同类型的变量要格式化,比如,货币需要变成12,345.00这样的格式,日期需要变成2016-01-01这样的格式。 - -## 简单逻辑 -模板还需要能执行一些简单逻辑,比如,要按条件输出内容,需要if实现如下输出: -``` -{{ name }}同学, -{% if score >= 90 %} - 成绩优秀,应该奖励 -{% elif score >=60 %} - 成绩良好,继续努力 -{% else %} - 不及格,建议回家打屁股 -{% endif %} -``` -所以,我们需要一个功能强大的模板引擎,来完成页面输出的功能。 - -## Nunjucks -我们选择Nunjucks作为模板引擎。Nunjucks是Mozilla开发的一个纯JavaScript编写的模板引擎,既可以用在Node环境下,又可以运行在浏览器端。但是,主要还是运行在Node环境下,因为浏览器端有更好的模板解决方案,例如MVVM框架。 - -如果你使用过Python的模板引擎jinja2,那么使用Nunjucks就非常简单,两者的语法几乎是一模一样的,因为Nunjucks就是用JavaScript重新实现了jinjia2。 - -从上面的例子我们可以看到,虽然模板引擎内部可能非常复杂,但是使用一个模板引擎是非常简单的,因为本质上我们只需要构造这样一个函数: -``` -function render(view, model) { - // TODO:... -} -``` -其中,view是模板的名称(又称为视图),因为可能存在多个模板,需要选择其中一个。model就是数据,在JavaScript中,它就是一个简单的Object。render函数返回一个字符串,就是模板的输出。 - -下面我们来使用Nunjucks这个模板引擎来编写几个HTML模板,并且用实际数据来渲染模板并获得最终的HTML输出。 - -我们创建一个use-nunjucks的VS Code工程结构如下: -``` -use-nunjucks/ -| -+- .vscode/ -| | -| +- launch.json <-- VSCode 配置文件 -| -+- views/ -| | -| +- hello.html <-- HTML模板文件 -| -+- app.js <-- 入口js -| -+- package.json <-- 项目描述文件 -| -+- node_modules/ <-- npm安装的所有依赖包 -``` -其中,模板文件存放在views目录中。 - -我们先在package.json中添加nunjucks的依赖: -``` -"nunjucks": "2.4.2" -``` -注意,模板引擎是可以独立使用的,并不需要依赖koa。用npm install安装所有依赖包。 - -紧接着,我们要编写使用Nunjucks的函数render。怎么写?方法是查看Nunjucks的官方文档,仔细阅读后,在app.js中编写代码如下: -``` -const nunjucks = require('nunjucks'); - -function createEnv(path, opts) { - var - autoescape = opts.autoescape === undefined ? true : opts.autoescape, - noCache = opts.noCache || false, - watch = opts.watch || false, - throwOnUndefined = opts.throwOnUndefined || false, - env = new nunjucks.Environment( - new nunjucks.FileSystemLoader('views', { - noCache: noCache, - watch: watch, - }), { - autoescape: autoescape, - throwOnUndefined: throwOnUndefined - }); - if (opts.filters) { - for (var f in opts.filters) { - env.addFilter(f, opts.filters[f]); - } - } - return env; -} - -var env = createEnv('views', { - watch: true, - filters: { - hex: function (n) { - return '0x' + n.toString(16); - } - } -}); -``` -变量env就表示Nunjucks模板引擎对象,它有一个`render(view, model)`方法,正好传入view和model两个参数,并返回字符串。 - -创建env需要的参数可以查看文档获知。我们用autoescape = opts.autoescape && true这样的代码给每个参数加上默认值,最后使用`new nunjucks.FileSystemLoader('views')`创建一个文件系统加载器,从views目录读取模板。 - -我们编写一个`hello.html`模板文件,放到views目录下,内容如下: -``` -

Hello {{ name }}

-``` -然后,我们就可以用下面的代码来渲染这个模板: -``` -var s = env.render('hello.html', { name: '小明' }); -console.log(s); -``` -获得输出如下: -``` -

Hello 小明

-``` -咋一看,这和使用JavaScript模板字符串没啥区别嘛。不过,试试: -``` -var s = env.render('hello.html', { name: '' }); -console.log(s); -``` -获得输出如下: -``` -

Hello <script>alert("小明")</script>

-``` -这样就避免了输出恶意脚本。 - -此外,可以使用Nunjucks提供的功能强大的tag,编写条件判断、循环等功能,例如: -``` - - -

Fruits List

- {% for f in fruits %} -

{{ f }}

- {% endfor %} - -``` -Nunjucks模板引擎最强大的功能在于模板的继承。仔细观察各种网站可以发现,网站的结构实际上是类似的,头部、尾部都是固定格式,只有中间页面部分内容不同。如果每个模板都重复头尾,一旦要修改头部或尾部,那就需要改动所有模板。 - -更好的方式是使用继承。先定义一个基本的网页框架base.html: -``` - -{% block header %}

Unnamed

{% endblock %} -{% block body %}
No body
{% endblock %} -{% block footer %}
copyright
{% endblock %} - - -``` -base.html定义了三个可编辑的块,分别命名为header、body和footer。子模板可以有选择地对块进行重新定义: -``` -{% extends 'base.html' %} - -{% block header %}

{{ header }}

{% endblock %} - -{% block body %}

{{ body }}

{% endblock %} -``` -然后,我们对子模板进行渲染: -``` -console.log(env.render('extend.html', { - header: 'Hello', - body: 'bla bla bla...' -})); -``` -输出HTML如下: -``` - -

Hello

-

bla bla bla...

-
copyright
<-- footer没有重定义,所以仍使用父模板的内容 - -``` -## 性能 -最后我们要考虑一下Nunjucks的性能。 - -对于模板渲染本身来说,速度是非常非常快的,因为就是拼字符串嘛,纯CPU操作。 - -性能问题主要出现在从文件读取模板内容这一步。这是一个IO操作,在Node.js环境中,我们知道,单线程的JavaScript最不能忍受的就是同步IO,但Nunjucks默认就使用同步IO读取模板文件。 - -好消息是Nunjucks会缓存已读取的文件内容,也就是说,模板文件最多读取一次,就会放在内存中,后面的请求是不会再次读取文件的,只要我们指定了noCache: false这个参数。 - -在开发环境下,可以关闭cache,这样每次重新加载模板,便于实时修改模板。在生产环境下,一定要打开cache,这样就不会有性能问题。 - -Nunjucks也提供了异步读取的方式,但是这样写起来很麻烦,有简单的写法我们就不会考虑复杂的写法。保持代码简单是可维护性的关键。 \ No newline at end of file diff --git "a/12\351\276\232\344\270\234\350\276\211/20230309-MVC.md" "b/12\351\276\232\344\270\234\350\276\211/20230309-MVC.md" deleted file mode 100644 index 2dccca0..0000000 --- "a/12\351\276\232\344\270\234\350\276\211/20230309-MVC.md" +++ /dev/null @@ -1,327 +0,0 @@ -# 使用MVC - -## MVC -我们已经可以用koa处理不同的URL,还可以用Nunjucks渲染模板。现在,是时候把这两者结合起来了! - -当用户通过浏览器请求一个URL时,koa将调用某个异步函数处理该URL。在这个异步函数内部,我们用一行代码: -``` -ctx.render('home.html', { name: 'Michael' }); -``` -通过Nunjucks把数据用指定的模板渲染成HTML,然后输出给浏览器,用户就可以看到渲染后的页面了: - -## mvc - -这就是传说中的MVC:Model-View-Controller,中文名“模型-视图-控制器”。 - -异步函数是C:Controller,Controller负责业务逻辑,比如检查用户名是否存在,取出用户信息等等; - -包含变量{{ name }}的模板就是V:View,View负责显示逻辑,通过简单地替换一些变量,View最终输出的就是用户看到的HTML。 - -MVC中的Model在哪?Model是用来传给View的,这样View在替换变量的时候,就可以从Model中取出相应的数据。 - -上面的例子中,Model就是一个JavaScript对象: -``` -{ name: 'Michael' } -``` -下面,我们根据原来的url2-koa创建工程view-koa,把koa2、Nunjucks整合起来,然后,把原来直接输出字符串的方式,改为ctx.render(view, model)的方式。 - -工程view-koa结构如下: -``` -view-koa/ -| -+- .vscode/ -| | -| +- launch.json <-- VSCode 配置文件 -| -+- controllers/ <-- Controller -| -+- views/ <-- html模板文件 -| -+- static/ <-- 静态资源文件 -| -+- controller.js <-- 扫描注册Controller -| -+- app.js <-- 使用koa的js -| -+- package.json <-- 项目描述文件 -| -+- node_modules/ <-- npm安装的所有依赖包 -``` -在package.json中,我们将要用到的依赖包有: -``` -"koa": "2.0.0", -"koa-bodyparser": "3.2.0", -"koa-router": "7.0.0", -"nunjucks": "2.4.2", -"mime": "1.3.4", -"mz": "2.4.0" -``` -先用npm install安装依赖包。 - -然后,我们准备编写以下两个Controller: - -1. 处理首页 GET / - -我们定义一个async函数处理首页URL/: -``` -async (ctx, next) => { - ctx.render('index.html', { - title: 'Welcome' - }); -} -``` -注意到koa并没有在ctx对象上提供render方法,这里我们假设应该这么使用,这样,我们在编写Controller的时候,最后一步调用ctx.render(view, model)就完成了页面输出。 - -2. 处理登录请求 POST /signin -我们再定义一个async函数处理登录请求/signin: -``` -async (ctx, next) => { - var - email = ctx.request.body.email || '', - password = ctx.request.body.password || ''; - if (email === 'admin@example.com' && password === '123456') { - // 登录成功: - ctx.render('signin-ok.html', { - title: 'Sign In OK', - name: 'Mr Node' - }); - } else { - // 登录失败: - ctx.render('signin-failed.html', { - title: 'Sign In Failed' - }); - } -} -``` -由于登录请求是一个POST,我们就用ctx.request.body.拿到POST请求的数据,并给一个默认值。 - -登录成功时我们用signin-ok.html渲染,登录失败时我们用signin-failed.html渲染,所以,我们一共需要以下3个View: -``` -index.html -signin-ok.html -signin-failed.html -``` - -## 编写View -在编写View的时候,我们实际上是在编写HTML页。为了让页面看起来美观大方,使用一个现成的CSS框架是非常有必要的。我们用Bootstrap这个CSS框架。从首页下载zip包后解压,我们把所有静态资源文件放到/static目录下: -``` -view-koa/ -| -+- static/ - | - +- css/ <- 存放bootstrap.css等 - | - +- fonts/ <- 存放字体文件 - | - +- js/ <- 存放bootstrap.js等 -``` -这样我们在编写HTML的时候,可以直接用Bootstrap的CSS,像这样: -``` - -``` -现在,在使用MVC之前,第一个问题来了,如何处理静态文件? - -我们把所有静态资源文件全部放入/static目录,目的就是能统一处理静态文件。在koa中,我们需要编写一个middleware,处理以/static/开头的URL。 - -## 编写middleware -我们来编写一个处理静态文件的middleware。编写middleware实际上一点也不复杂。我们先创建一个static-files.js的文件,编写一个能处理静态文件的middleware: -``` -const path = require('path'); -const mime = require('mime'); -const fs = require('mz/fs'); - -// url: 类似 '/static/' -// dir: 类似 __dirname + '/static' -function staticFiles(url, dir) { - return async (ctx, next) => { - let rpath = ctx.request.path; - // 判断是否以指定的url开头: - if (rpath.startsWith(url)) { - // 获取文件完整路径: - let fp = path.join(dir, rpath.substring(url.length)); - // 判断文件是否存在: - if (await fs.exists(fp)) { - // 查找文件的mime: - ctx.response.type = mime.lookup(rpath); - // 读取文件内容并赋值给response.body: - ctx.response.body = await fs.readFile(fp); - } else { - // 文件不存在: - ctx.response.status = 404; - } - } else { - // 不是指定前缀的URL,继续处理下一个middleware: - await next(); - } - }; -} - -module.exports = staticFiles; -``` -staticFiles是一个普通函数,它接收两个参数:URL前缀和一个目录,然后返回一个async函数。这个async函数会判断当前的URL是否以指定前缀开头,如果是,就把URL的路径视为文件,并发送文件内容。如果不是,这个async函数就不做任何事情,而是简单地调用await next()让下一个middleware去处理请求。 - -我们使用了一个mz的包,并通过require('mz/fs');导入。mz提供的API和Node.js的fs模块完全相同,但fs模块使用回调,而mz封装了fs对应的函数,并改为Promise。这样,我们就可以非常简单的用await调用mz的函数,而不需要任何回调。 - -所有的第三方包都可以通过npm官网搜索并查看其文档: -``` -https://www.npmjs.com/ -``` -最后,这个middleware使用起来也很简单,在app.js里加一行代码: -``` -let staticFiles = require('./static-files'); -app.use(staticFiles('/static/', __dirname + '/static')); -``` -注意:也可以去npm搜索能用于koa2的处理静态文件的包并直接使用。 - -## 集成Nunjucks -集成Nunjucks实际上也是编写一个middleware,这个middleware的作用是给ctx对象绑定一个render(view, model)的方法,这样,后面的Controller就可以调用这个方法来渲染模板了。 - -我们创建一个templating.js来实现这个middleware: -``` -const nunjucks = require('nunjucks'); - -function createEnv(path, opts) { - var - autoescape = opts.autoescape === undefined ? true : opts.autoescape, - noCache = opts.noCache || false, - watch = opts.watch || false, - throwOnUndefined = opts.throwOnUndefined || false, - env = new nunjucks.Environment( - new nunjucks.FileSystemLoader(path || 'views', { - noCache: noCache, - watch: watch, - }), { - autoescape: autoescape, - throwOnUndefined: throwOnUndefined - }); - if (opts.filters) { - for (var f in opts.filters) { - env.addFilter(f, opts.filters[f]); - } - } - return env; -} - -function templating(path, opts) { - // 创建Nunjucks的env对象: - var env = createEnv(path, opts); - return async (ctx, next) => { - // 给ctx绑定render函数: - ctx.render = function (view, model) { - // 把render后的内容赋值给response.body: - ctx.response.body = env.render(view, Object.assign({}, ctx.state || {}, model || {})); - // 设置Content-Type: - ctx.response.type = 'text/html'; - }; - // 继续处理请求: - await next(); - }; -} - -module.exports = templating; -``` -注意到createEnv()函数和前面使用Nunjucks时编写的函数是一模一样的。我们主要关心tempating()函数,它会返回一个middleware,在这个middleware中,我们只给ctx“安装”了一个render()函数,其他什么事情也没干,就继续调用下一个middleware。 - -使用的时候,我们在app.js添加如下代码: -``` -const isProduction = process.env.NODE_ENV === 'production'; - -app.use(templating('views', { - noCache: !isProduction, - watch: !isProduction -})); -``` -这里我们定义了一个常量isProduction,它判断当前环境是否是production环境。如果是,就使用缓存,如果不是,就关闭缓存。在开发环境下,关闭缓存后,我们修改View,可以直接刷新浏览器看到效果,否则,每次修改都必须重启Node程序,会极大地降低开发效率。 - -Node.js在全局变量process中定义了一个环境变量env.NODE_ENV,为什么要使用该环境变量?因为我们在开发的时候,环境变量应该设置为'development',而部署到服务器时,环境变量应该设置为'production'。在编写代码的时候,要根据当前环境作不同的判断。 - -注意:生产环境上必须配置环境变量NODE_ENV = 'production',而开发环境不需要配置,实际上NODE_ENV可能是undefined,所以判断的时候,不要用NODE_ENV === 'development'。 - -类似的,我们在使用上面编写的处理静态文件的middleware时,也可以根据环境变量判断: -``` -if (! isProduction) { - let staticFiles = require('./static-files'); - app.use(staticFiles('/static/', __dirname + '/static')); -} -``` -这是因为在生产环境下,静态文件是由部署在最前面的反向代理服务器(如Nginx)处理的,Node程序不需要处理静态文件。而在开发环境下,我们希望koa能顺带处理静态文件,否则,就必须手动配置一个反向代理服务器,这样会导致开发环境非常复杂。 - -## 编写View -在编写View的时候,非常有必要先编写一个base.html作为骨架,其他模板都继承自base.html,这样,才能大大减少重复工作。 - -编写HTML不在本教程的讨论范围之内。这里我们参考Bootstrap的官网简单编写了base.html。 - -## 运行 -一切顺利的话,这个view-koa工程应该可以顺利运行。运行前,我们再检查一下app.js里的middleware的顺序: - -第一个middleware是记录URL以及页面执行时间: -``` -app.use(async (ctx, next) => { - console.log(`Process ${ctx.request.method} ${ctx.request.url}...`); - var - start = new Date().getTime(), - execTime; - await next(); - execTime = new Date().getTime() - start; - ctx.response.set('X-Response-Time', `${execTime}ms`); -}); -``` -第二个middleware处理静态文件: -``` -if (! isProduction) { - let staticFiles = require('./static-files'); - app.use(staticFiles('/static/', __dirname + '/static')); -} -``` -第三个middleware解析POST请求: -``` -app.use(bodyParser()); -``` -第四个middleware负责给ctx加上render()来使用Nunjucks: -``` -app.use(templating('views', { - noCache: !isProduction, - watch: !isProduction -})); -``` -最后一个middleware处理URL路由: -``` -app.use(controller()); -``` - -现在,在VS Code中运行代码,不出意外的话,在浏览器输入localhost:3000/,可以看到首页内容. - -直接在首页登录,如果输入正确的Email和Password,进入登录成功的页面. - -如果输入的Email和Password不正确,进入登录失败的页面. - -怎么判断正确的Email和Password?目前我们在signin.js中是这么判断的: -``` -if (email === 'admin@example.com' && password === '123456') { - ... -} -``` -当然,真实的网站会根据用户输入的Email和Password去数据库查询并判断登录是否成功,不过这需要涉及到Node.js环境如何操作数据库,我们后面再讨论。 - -## 扩展 -注意到ctx.render内部渲染模板时,Model对象并不是传入的model变量,而是: -``` -Object.assign({}, ctx.state || {}, model || {}) -``` -这个小技巧是为了扩展。 - -首先,model || {}确保了即使传入undefined,model也会变为默认值{}。Object.assign()会把除第一个参数外的其他参数的所有属性复制到第一个参数中。第二个参数是ctx.state || {},这个目的是为了能把一些公共的变量放入ctx.state并传给View。 - -例如,某个middleware负责检查用户权限,它可以把当前用户放入ctx.state中: -``` -app.use(async (ctx, next) => { - var user = tryGetUserFromCookie(ctx.request); - if (user) { - ctx.state.user = user; - await next(); - } else { - ctx.response.status = 403; - } -}); -``` -这样就没有必要在每个Controller的async函数中都把user变量放入model中。 \ No newline at end of file diff --git "a/12\351\276\232\344\270\234\350\276\211/20230310-\350\277\236\346\216\245\346\225\260\346\215\256\345\272\223.md" "b/12\351\276\232\344\270\234\350\276\211/20230310-\350\277\236\346\216\245\346\225\260\346\215\256\345\272\223.md" deleted file mode 100644 index 009769c..0000000 --- "a/12\351\276\232\344\270\234\350\276\211/20230310-\350\277\236\346\216\245\346\225\260\346\215\256\345\272\223.md" +++ /dev/null @@ -1,284 +0,0 @@ -# 使用Sequelize - -## 访问MySQL -当我们安装好MySQL后,Node.js程序如何访问MySQL数据库呢? - -访问MySQL数据库只有一种方法,就是通过网络发送SQL命令,然后,MySQL服务器执行后返回结果。 - -我们可以在命令行窗口输入mysql -u root -p,然后输入root口令后,就连接到了MySQL服务器。因为没有指定--host参数,所以我们连接到的是localhost,也就是本机的MySQL服务器。 - -在命令行窗口下,我们可以输入命令,操作MySQL服务器: -``` -mysql> show databases; -+--------------------+ -| Database | -+--------------------+ -| information_schema | -| mysql | -| performance_schema | -| test | -+--------------------+ -4 rows in set (0.05 sec) -``` -输入exit退出MySQL命令行模式。 - -对于Node.js程序,访问MySQL也是通过网络发送SQL命令给MySQL服务器。这个访问MySQL服务器的软件包通常称为MySQL驱动程序。不同的编程语言需要实现自己的驱动,MySQL官方提供了Java、.Net、Python、Node.js、C++和C的驱动程序,官方的Node.js驱动目前仅支持5.7以上版本,而我们上面使用的命令行程序实际上用的就是C驱动。 - -目前使用最广泛的MySQL Node.js驱动程序是开源的mysql,可以直接使用npm安装。 - -## ORM -如果直接使用mysql包提供的接口,我们编写的代码就比较底层,例如,查询代码: -``` -connection.query('SELECT * FROM users WHERE id = ?', ['123'], function(err, rows) { - if (err) { - // error - } else { - for (let row in rows) { - processRow(row); - } - } -}); -``` -考虑到数据库表是一个二维表,包含多行多列,例如一个pets的表: -``` -mysql> select * from pets; -+----+--------+------------+ -| id | name | birth | -+----+--------+------------+ -| 1 | Gaffey | 2007-07-07 | -| 2 | Odie | 2008-08-08 | -+----+--------+------------+ -2 rows in set (0.00 sec) -``` -每一行可以用一个JavaScript对象表示,例如第一行: -``` -{ - "id": 1, - "name": "Gaffey", - "birth": "2007-07-07" -} -``` -这就是传说中的ORM技术:Object-Relational Mapping,把关系数据库的表结构映射到对象上。是不是很简单? - -但是由谁来做这个转换呢?所以ORM框架应运而生。 - -我们选择Node的ORM框架Sequelize来操作数据库。这样,我们读写的都是JavaScript对象,Sequelize帮我们把对象变成数据库中的行。 - -用Sequelize查询pets表,代码像这样: -``` -Pet.findAll() - .then(function (pets) { - for (let pet in pets) { - console.log(`${pet.id}: ${pet.name}`); - } - }).catch(function (err) { - // error - }); -``` -因为Sequelize返回的对象是Promise,所以我们可以用then()和catch()分别异步响应成功和失败。 - -但是用then()和catch()仍然比较麻烦。有没有更简单的方法呢? - -可以用ES7的await来调用任何一个Promise对象,这样我们写出来的代码就变成了: -``` -var pets = await Pet.findAll(); -``` -真的就是这么简单! - -await只有一个限制,就是必须在async函数中调用。上面的代码直接运行还差一点,我们可以改成: -``` -(async () => { - var pets = await Pet.findAll(); -})(); -``` -考虑到koa的处理函数都是async函数,所以我们实际上将来在koa的async函数中直接写await访问数据库就可以了! - -这也是为什么我们选择Sequelize的原因:只要API返回Promise,就可以用await调用,写代码就非常简单! - -## 实战 -在使用Sequlize操作数据库之前,我们先在MySQL中创建一个表来测试。我们可以在test数据库中创建一个pets表。test数据库是MySQL安装后自动创建的用于测试的数据库。在MySQL命令行执行下列命令: -``` -grant all privileges on test.* to 'www'@'%' identified by 'www'; - -use test; - -create table pets ( - id varchar(50) not null, - name varchar(100) not null, - gender bool not null, - birth varchar(10) not null, - createdAt bigint not null, - updatedAt bigint not null, - version bigint not null, - primary key (id) -) engine=innodb; -``` -第一条grant命令是创建MySQL的用户名和口令,均为www,并赋予操作test数据库的所有权限。 - -第二条use命令把当前数据库切换为test。 - -第三条命令创建了pets表。 - -然后,我们根据前面的工程结构创建hello-sequelize工程,结构如下: -``` -hello-sequelize/ -| -+- .vscode/ -| | -| +- launch.json <-- VSCode 配置文件 -| -+- init.txt <-- 初始化SQL命令 -| -+- config.js <-- MySQL配置文件 -| -+- app.js <-- 使用koa的js -| -+- package.json <-- 项目描述文件 -| -+- node_modules/ <-- npm安装的所有依赖包 -``` -然后,添加如下依赖包: -``` -"sequelize": "3.24.1", -"mysql": "2.11.1" -``` -注意mysql是驱动,我们不直接使用,但是sequelize会用。 - -## 用npm install安装。 - -config.js实际上是一个简单的配置文件: -``` -var config = { - database: 'test', // 使用哪个数据库 - username: 'www', // 用户名 - password: 'www', // 口令 - host: 'localhost', // 主机名 - port: 3306 // 端口号,MySQL默认3306 -}; - -module.exports = config; -``` -下面,我们就可以在app.js中操作数据库了。使用Sequelize操作MySQL需要先做两件准备工作: - -第一步,创建一个sequelize对象实例: -``` -const Sequelize = require('sequelize'); -const config = require('./config'); - -var sequelize = new Sequelize(config.database, config.username, config.password, { - host: config.host, - dialect: 'mysql', - pool: { - max: 5, - min: 0, - idle: 30000 - } -}); -``` -第二步,定义模型Pet,告诉Sequelize如何映射数据库表: -``` -var Pet = sequelize.define('pet', { - id: { - type: Sequelize.STRING(50), - primaryKey: true - }, - name: Sequelize.STRING(100), - gender: Sequelize.BOOLEAN, - birth: Sequelize.STRING(10), - createdAt: Sequelize.BIGINT, - updatedAt: Sequelize.BIGINT, - version: Sequelize.BIGINT -}, { - timestamps: false - }); -``` -用sequelize.define()定义Model时,传入名称pet,默认的表名就是pets。第二个参数指定列名和数据类型,如果是主键,需要更详细地指定。第三个参数是额外的配置,我们传入{ timestamps: false }是为了关闭Sequelize的自动添加timestamp的功能。所有的ORM框架都有一种很不好的风气,总是自作聪明地加上所谓“自动化”的功能,但是会让人感到完全摸不着头脑。 - -接下来,我们就可以往数据库中塞一些数据了。我们可以用Promise的方式写: -``` -var now = Date.now(); - -Pet.create({ - id: 'g-' + now, - name: 'Gaffey', - gender: false, - birth: '2007-07-07', - createdAt: now, - updatedAt: now, - version: 0 -}).then(function (p) { - console.log('created.' + JSON.stringify(p)); -}).catch(function (err) { - console.log('failed: ' + err); -}); -``` -也可以用await写: -``` -(async () => { - var dog = await Pet.create({ - id: 'd-' + now, - name: 'Odie', - gender: false, - birth: '2008-08-08', - createdAt: now, - updatedAt: now, - version: 0 - }); - console.log('created: ' + JSON.stringify(dog)); -})(); -``` -显然await代码更胜一筹。 - -查询数据时,用await写法如下: -``` -(async () => { - var pets = await Pet.findAll({ - where: { - name: 'Gaffey' - } - }); - console.log(`find ${pets.length} pets:`); - for (let p of pets) { - console.log(JSON.stringify(p)); - } -})(); -``` -如果要更新数据,可以对查询到的实例调用save()方法: -``` -(async () => { - var p = await queryFromSomewhere(); - p.gender = true; - p.updatedAt = Date.now(); - p.version ++; - await p.save(); -})(); -``` -如果要删除数据,可以对查询到的实例调用destroy()方法: -``` -(async () => { - var p = await queryFromSomewhere(); - await p.destroy(); -})(); -``` -运行代码,可以看到Sequelize打印出的每一个SQL语句,便于我们查看: -``` -Executing (default): INSERT INTO `pets` (`id`,`name`,`gender`,`birth`,`createdAt`,`updatedAt`,`version`) VALUES ('g-1471961204219','Gaffey',false,'2007-07-07',1471961204219,1471961204219,0); -``` - -## Model -我们把通过sequelize.define()返回的Pet称为Model,它表示一个数据模型。 - -我们把通过Pet.findAll()返回的一个或一组对象称为Model实例,每个实例都可以直接通过JSON.stringify序列化为JSON字符串。但是它们和普通JSON对象相比,多了一些由Sequelize添加的方法,比如save()和destroy()。调用这些方法我们可以执行更新或者删除操作。 - -所以,使用Sequelize操作数据库的一般步骤就是: - -首先,通过某个Model对象的findAll()方法获取实例; - -如果要更新实例,先对实例属性赋新值,再调用save()方法; - -如果要删除实例,直接调用destroy()方法。 - -注意findAll()方法可以接收where、order这些参数,这和将要生成的SQL语句是对应的。 - -## 文档 -Sequelize的API可以参考官方文档。 \ No newline at end of file -- Gitee From 41ebe45a8b5e067ab4c9be3456f7b2f189d051c3 Mon Sep 17 00:00:00 2001 From: "@chen-li-xing" <19912973651> Date: Tue, 14 Mar 2023 10:59:02 +0800 Subject: [PATCH 04/20] os --- ...3-02-13 Nodejs\345\210\235\350\257\206.md" | 27 -- ...72\346\234\254\344\272\206\350\247\243.md" | 52 --- ...73\347\273\237\346\250\241\345\235\227.md" | 111 ------ ...55\345\273\272\346\225\231\347\250\213.md" | 41 -- ...66\346\234\215\345\212\241\345\231\250.md" | 99 ----- .../2023-02-21 \347\273\203\344\271\240.md" | 74 ---- .../2023-02-23 promise async await .md" | 354 ------------------ .../2023-02-24 koa.md" | 92 ----- ...middleware \345\244\204\347\220\206url.md" | 83 ---- ...47\220\206post\350\257\267\346\261\202.md" | 52 --- ...24\261\345\212\237\350\203\275\302\267.md" | 73 ---- ...03\344\271\240\345\237\272\346\234\254.md" | 84 ----- ...023-03-06 \351\207\215\346\236\204anew.md" | 151 -------- ...55\351\207\215\350\275\275 nunjucks.md.md" | 194 ---------- ...31\346\200\201\350\265\204\346\272\220.md" | 108 ------ .../2023-03-10 sequelize.md" | 85 ----- .../img/2023-02-20.png" | Bin 5447 -> 0 bytes .../img/230224.png" | Bin 17500 -> 0 bytes .../img/httpmokuai/img01.png" | Bin 7662 -> 0 bytes .../img/httpmokuai/img02.png" | Bin 16751 -> 0 bytes .../img/wangzhan.png" | Bin 56974 -> 0 bytes ...230213-\350\256\244\350\257\206Node.js.md" | 76 ---- ...56\345\222\214\346\250\241\345\235\227.md" | 80 ---- ...73\347\273\237\346\250\241\345\235\227.md" | 227 ----------- ...21\347\253\231\351\203\250\347\275\262.md" | 79 ---- ...66\346\234\215\345\212\241\345\231\250.md" | 99 ----- ...01\346\226\207\344\273\266\345\244\271.md" | 70 ---- ...1\346\226\207\344\273\266\345\244\2712.md" | 44 --- ...345\222\214Koa\346\241\206\346\236\266.md" | 89 ----- ...345\222\214URL\345\244\204\347\220\206.md" | 209 ----------- ...02\345\222\214\351\207\215\346\236\204.md" | 126 ------- ...71\347\233\256\351\207\215\346\236\204.md" | 140 ------- ...5\346\236\204\351\241\271\347\233\2562.md" | 17 - ...30306-node.js-\346\241\206\346\236\266.md" | 45 --- ...41\346\235\277\345\274\225\346\223\216.md" | 24 -- .../230309-node.js-MVC.md" | 36 -- ...56\345\272\223\350\277\236\346\216\245.md" | 69 ---- ...3\350\275\275\345\234\260\345\235\200.png" | Bin 151142 -> 0 bytes ...0\347\232\204\346\225\210\346\236\234.png" | Bin 9579 -> 0 bytes 39 files changed, 3110 deletions(-) delete mode 100644 "03\350\214\203\345\217\266\345\205\260/2023-02-13 Nodejs\345\210\235\350\257\206.md" delete mode 100644 "03\350\214\203\345\217\266\345\205\260/2023-02-14 Nodejs\345\237\272\346\234\254\344\272\206\350\247\243.md" delete mode 100644 "03\350\214\203\345\217\266\345\205\260/2023-02-16 \345\237\272\346\234\254 \347\263\273\347\273\237\346\250\241\345\235\227.md" delete mode 100644 "03\350\214\203\345\217\266\345\205\260/2023-02-17 http\346\250\241\345\235\227\344\270\216\347\275\221\347\253\231\346\220\255\345\273\272\346\225\231\347\250\213.md" delete mode 100644 "03\350\214\203\345\217\266\345\205\260/2023-02-20 http\346\226\207\344\273\266\346\234\215\345\212\241\345\231\250.md" delete mode 100644 "03\350\214\203\345\217\266\345\205\260/2023-02-21 \347\273\203\344\271\240.md" delete mode 100644 "03\350\214\203\345\217\266\345\205\260/2023-02-23 promise async await .md" delete mode 100644 "03\350\214\203\345\217\266\345\205\260/2023-02-24 koa.md" delete mode 100644 "03\350\214\203\345\217\266\345\205\260/2023-02-27 middleware \345\244\204\347\220\206url.md" delete mode 100644 "03\350\214\203\345\217\266\345\205\260/2023-02-28 \345\244\204\347\220\206post\350\257\267\346\261\202.md" delete mode 100644 "03\350\214\203\345\217\266\345\205\260/2023-03-02 \351\207\215\346\236\204\350\267\257\347\224\261\345\212\237\350\203\275\302\267.md" delete mode 100644 "03\350\214\203\345\217\266\345\205\260/2023-03-03 \347\273\203\344\271\240\345\237\272\346\234\254.md" delete mode 100644 "03\350\214\203\345\217\266\345\205\260/2023-03-06 \351\207\215\346\236\204anew.md" delete mode 100644 "03\350\214\203\345\217\266\345\205\260/2023-03-07 \347\203\255\351\207\215\350\275\275 nunjucks.md.md" delete mode 100644 "03\350\214\203\345\217\266\345\205\260/2023-03-09 \345\244\204\347\220\206\351\235\231\346\200\201\350\265\204\346\272\220.md" delete mode 100644 "03\350\214\203\345\217\266\345\205\260/2023-03-10 sequelize.md" delete mode 100644 "03\350\214\203\345\217\266\345\205\260/img/2023-02-20.png" delete mode 100644 "03\350\214\203\345\217\266\345\205\260/img/230224.png" delete mode 100644 "03\350\214\203\345\217\266\345\205\260/img/httpmokuai/img01.png" delete mode 100644 "03\350\214\203\345\217\266\345\205\260/img/httpmokuai/img02.png" delete mode 100644 "03\350\214\203\345\217\266\345\205\260/img/wangzhan.png" delete mode 100644 "05\345\210\230\350\203\241/230213-\350\256\244\350\257\206Node.js.md" delete mode 100644 "05\345\210\230\350\203\241/230214-node.\346\226\207\344\273\266\343\200\201\350\267\257\345\276\204\343\200\201\351\205\215\347\275\256\345\222\214\346\250\241\345\235\227.md" delete mode 100644 "05\345\210\230\350\203\241/230216-node.js\347\232\204\345\237\272\346\234\254\346\250\241\345\235\227\345\222\214\346\226\207\344\273\266\347\263\273\347\273\237\346\250\241\345\235\227.md" delete mode 100644 "05\345\210\230\350\203\241/230217-node.js\347\232\204http\346\250\241\345\235\227\345\222\214\347\275\221\347\253\231\351\203\250\347\275\262.md" delete mode 100644 "05\345\210\230\350\203\241/230220-node.js\347\232\204\346\226\207\344\273\266\346\234\215\345\212\241\345\231\250.md" delete mode 100644 "05\345\210\230\350\203\241/230221-node.js\347\232\204Web\347\250\213\345\272\217\345\210\244\346\226\255\346\226\207\344\273\266\343\200\201\346\226\207\344\273\266\345\244\271.md" delete mode 100644 "05\345\210\230\350\203\241/230223-node.js\347\232\204Web\347\250\213\345\272\217\345\210\244\346\226\255\346\226\207\344\273\266\343\200\201\346\226\207\344\273\266\345\244\2712.md" delete mode 100644 "05\345\210\230\350\203\241/230224-node.js\347\232\204Web\345\274\200\345\217\221\345\222\214Koa\346\241\206\346\236\266.md" delete mode 100644 "05\345\210\230\350\203\241/230227-node.js-Koa\346\241\206\346\236\266\345\222\214URL\345\244\204\347\220\206.md" delete mode 100644 "05\345\210\230\350\203\241/230228-node.js-URL\345\244\204\347\220\206\347\232\204post\350\257\267\346\261\202\345\222\214\351\207\215\346\236\204.md" delete mode 100644 "05\345\210\230\350\203\241/230302-node.js-URL\345\244\204\347\220\206\350\257\267\346\261\202\345\222\214\351\241\271\347\233\256\351\207\215\346\236\204.md" delete mode 100644 "05\345\210\230\350\203\241/230303-node.js-\351\207\215\346\236\204\351\241\271\347\233\2562.md" delete mode 100644 "05\345\210\230\350\203\241/230306-node.js-\346\241\206\346\236\266.md" delete mode 100644 "05\345\210\230\350\203\241/230307-node.js-\346\250\241\346\235\277\345\274\225\346\223\216.md" delete mode 100644 "05\345\210\230\350\203\241/230309-node.js-MVC.md" delete mode 100644 "05\345\210\230\350\203\241/230310-node.js-\346\225\260\346\215\256\345\272\223\350\277\236\346\216\245.md" delete mode 100644 "05\345\210\230\350\203\241/imgs/Node.js\344\270\213\350\274\211/Node\345\256\230\347\275\221\344\270\213\350\275\275\345\234\260\345\235\200.png" delete mode 100644 "05\345\210\230\350\203\241/imgs/http\346\250\241\345\235\227/\347\256\200\345\215\225http\346\234\215\345\212\241\345\231\250\347\232\204\346\225\210\346\236\234.png" diff --git "a/03\350\214\203\345\217\266\345\205\260/2023-02-13 Nodejs\345\210\235\350\257\206.md" "b/03\350\214\203\345\217\266\345\205\260/2023-02-13 Nodejs\345\210\235\350\257\206.md" deleted file mode 100644 index c085627..0000000 --- "a/03\350\214\203\345\217\266\345\205\260/2023-02-13 Nodejs\345\210\235\350\257\206.md" +++ /dev/null @@ -1,27 +0,0 @@ -# 主要功能 - - V8引擎本身使用了一些最新的编译技术。这使得用Javascript这类脚本语言编写出来的代码运行速度获得了极大提升,又节省了开发成本。对性能的苛求是Node的一个关键因素。 Javascript是一个事件驱动语言,Node利用了这个优点,编写出可扩展性高的服务器。Node采用了一个称为“事件循环(event loop)”的架构,使得编写可扩展性高的服务器变得既容易又安全。提高服务器性能的技巧有多种多样。Node选择了一种既能提高性能,又能减低开发复杂度的架构。这是一个非常重要的特性。并发编程通常很复杂且布满地雷。Node绕过了这些,但仍提供很好的性能。 - - Node采用一系列“非阻塞”库来支持事件循环的方式。本质上就是为文件系统、数据库之类的资源提供接口。向文件系统发送一个请求时,无需等待硬盘(寻址并检索文件),硬盘准备好的时候非阻塞接口会通知Node。该模型以可扩展的方式简化了对慢资源的访问, 直观,易懂。尤其是对于熟悉onmouseover、onclick等DOM事件的用户,更有一种似曾相识的感觉。 - - 虽然让Javascript运行于服务器端不是Node的独特之处,但却是其一强大功能。不得不承认,浏览器环境限制了我们选择编程语言的自由。任何服务器与日益复杂的浏览器客户端应用程序间共享代码的愿望只能通过Javascript来实现。虽然还存在其他一些支持Javascript在服务器端 运行的平台,但因为上述特性,Node发展迅猛,成为事实上的平台。 - -# 运行环境 - - Node作为一个新兴的前端框架,后台语言,有很多吸引人的地方:RESTful API,单线程。 - - Node可以在不新增额外线程的情况下,依然可以对任务进行并发处理 —— Node.js是单线程的。它通过事件循环(event loop)来实现并发操作,对此,我们应该要充分利用这一点 —— 尽可能的避免阻塞操作,取而代之,多使用非阻塞操作。 - -# 功能模块 - - Node使用Module模块去划分不同的功能,以简化应用的开发。Modules模块有点像C++语言中的类库。每一个Node的类库都包含了十分丰富的各类函数,比如http模块就包含了和http功能相关的很多函数,可以帮助开发者很容易地对比如http,tcp/udp等进行操作,还可以很容易的创建http和tcp/udp的服务器。 - - 要在程序中使用模块是十分方便的,只需要如下: - - 在这里,引入了http类库,并且对http类库的引用存放在http变量中了。这个时候,Node会在我们应用中搜索是否存在node_modules的目录,并且搜索这个目录中是否存在http的模块。如果Node.js找不到这个目录,则会到全局模块缓存中去寻找,用户可以通过相对或者绝对路径,指定模块的位置,比如: - - var myModule = require('./myModule.js');\ - - - 模块中包含了很多功能代码片断,在模块中的代码大部分都是私有的,意思是在模块中定义的函数方法和变量,都只能在同一个模块中被调用。当然,可以将某些方法和变量暴露到模块外,这个时候可以使用exports对象去实现。 - diff --git "a/03\350\214\203\345\217\266\345\205\260/2023-02-14 Nodejs\345\237\272\346\234\254\344\272\206\350\247\243.md" "b/03\350\214\203\345\217\266\345\205\260/2023-02-14 Nodejs\345\237\272\346\234\254\344\272\206\350\247\243.md" deleted file mode 100644 index 291bdc5..0000000 --- "a/03\350\214\203\345\217\266\345\205\260/2023-02-14 Nodejs\345\237\272\346\234\254\344\272\206\350\247\243.md" +++ /dev/null @@ -1,52 +0,0 @@ -# 安装Node.js - - 目前Node.js的最新版本是13.5.x。首先,从Node.js官网下载对应平台的安装程序,网速慢的童鞋请移步国内镜像。 - - 12.7 - 在Windows上安装时务必选择全部组件,包括勾选Add to Path。 - - 安装完成后,在Windows环境下,请打开命令提示符,然后输入node -v,如果安装正常,你应该看到v7.6.0这样的输出: - - C:\Users\IEUser>node -v - v7.6.0 - - 继续在命令提示符输入node,此刻你将进入Node.js的交互环境。在交互环境下,你可以输入任意JavaScript语句,例如100+200,回车后将得到输出结果。 - - 要退出Node.js环境,连按两次Ctrl+C。 - - 在Mac或Linux环境下,请打开终端,然后输入node -v,你应该看到如下输出: - - $ node -v - v10.15.3 - - 如果版本号小于v7.6.0,说明Node.js版本不对,后面章节的代码不保证能正常运行,请重新安装最新版本。 - -# npm - - npm其实是Node.js的包管理工具(package manager)。 - - 其实npm已经在Node.js安装的时候顺带装好了。我们在命令提示符或者终端输入npm -v,应该看到类似的输出: - - C:\>npm -v - 4.1.2 - 如果直接输入npm,你会看到类似下面的输出: - - C:\> npm - - Usage: npm - - where is one of: - ... - 上面的一大堆文字告诉你,npm需要跟上命令。现在我们不用关心这些命令,后面会一一讲到。 - -# node程序小结 - -+ 用文本编辑器写JavaScript程序,然后保存为后缀为.js的文件,就可以用node直接运行这个程序了。 - -+ Node的交互模式和直接运行.js文件有什么区别呢? - -+ 直接输入node进入交互模式,相当于启动了Node解释器,但是等待你一行一行地输入源代码,每输入一行就执行一行。 - -+ 直接运行node hello.js文件相当于启动了Node解释器,然后一次性把hello.js文件的源代码给执行了,你是没有机会以交互的方式输入源代码的。 - -+ 在编写JavaScript代码的时候,完全可以一边在文本编辑器里写代码,一边开一个Node交互式命令窗口,在写代码的过程中,把部分代码粘到命令行去 验证,事半功倍!前提是得有个27'的超大显示器! \ No newline at end of file diff --git "a/03\350\214\203\345\217\266\345\205\260/2023-02-16 \345\237\272\346\234\254 \347\263\273\347\273\237\346\250\241\345\235\227.md" "b/03\350\214\203\345\217\266\345\205\260/2023-02-16 \345\237\272\346\234\254 \347\263\273\347\273\237\346\250\241\345\235\227.md" deleted file mode 100644 index 3ebe588..0000000 --- "a/03\350\214\203\345\217\266\345\205\260/2023-02-16 \345\237\272\346\234\254 \347\263\273\347\273\237\346\250\241\345\235\227.md" +++ /dev/null @@ -1,111 +0,0 @@ -# 模块 - - 为了编写可维护的代码,我们把很多函数分组,分别放到不同的文件里,这样,每个文件包含的代码就相对较少,很多编程语言都采用这种组织代码的方式。在Node环境中,一个.js文件就称之为一个模块(module)。 - -# 使用模块的优点 - - + 大大提高了代码的可维护性 - - + 编写代码不必从零开始,当一个模块编写完毕,就可以被其他地方引用 - - + 避免函数名和变量名冲突 - -# CommonJS规范 - - 在这个规范下,每个.js文件都是一个模块,它们内部各自使用的变量名和函数名都互不冲突,例如,hello.js和main.js都申明了全局变量var s = 'xxx',但互不影响。 - - 一个模块想要对外暴露变量(函数也是变量),可以用module.exports = variable;,一个模块要引用其他模块暴露的变量,用var ref = require('module_name');就拿到了引用模块的变量。 - -# 结论 - - 要在模块中对外输出变量,用: -``` - module.exports = variable; -``` - 输出的变量可以是任意对象、函数、数组等等。 - - 要引入其他模块输出的对象,用: -``` - var foo = require('other_module'); -``` - 引入的对象具体是什么,取决于引入模块输出的对象。 - -# global - -在前面的JavaScript课程中,我们已经知道,JavaScript有且仅有一个全局对象,在浏览器中,叫window对象。而在Node.js环境中,也有唯一的全局对象,但不叫window,而叫global,这个对象的属性和方法也和浏览器环境的window不同。进入Node.js交互环境,可以直接输入: -``` -> global.console - -Console { - log: [Function: bound ], - info: [Function: bound ], - warn: [Function: bound ], - error: [Function: bound ], - dir: [Function: bound ], - time: [Function: bound ], - timeEnd: [Function: bound ], - trace: [Function: bound trace], - assert: [Function: bound ], - Console: [Function: Console] } -``` - -# process - -process也是Node.js提供的一个对象,它代表当前Node.js进程。通过process对象可以拿到许多有用信息 - -``` -> process === global.process; -true -> process.version; -'v5.2.0' -> process.platform; -'darwin' -> process.arch; -'x64' -> process.cwd(); //返回当前工作目录 -'/Users/michael' -> process.chdir('/private/tmp'); // 切换当前工作目录 -undefined -> process.cwd(); -'/private/tmp' -``` - -# 判断JavaScript执行环境 - -有很多JavaScript代码既能在浏览器中执行,也能在Node环境执行,但有些时候,程序本身需要判断自己到底是在什么环境下执行的,常用的方式就是根据浏览器和Node环境提供的全局变量名称来判断: -``` -if (typeof(window) === 'undefined') { - console.log('node.js'); -} else { - console.log('browser'); -} -``` - -# fs - -Node.js内置的fs模块就是文件系统模块,负责读写文件。 - -和所有其它JavaScript模块不同的是,fs模块同时提供了异步和同步的方法。 - -回顾一下什么是异步方法。因为JavaScript的单线程模型,执行IO操作时,JavaScript代码无需等待,而是传入回调函数后,继续执行后续JavaScript代码。比如jQuery提供的getJSON()操作: -``` -$.getJSON('http://example.com/ajax', function (data) { - console.log('IO结果返回后执行...'); -}); -console.log('不等待IO结果直接执行后续代码...'); -``` -而同步的IO操作则需要等待函数返回: -``` -// 根据网络耗时,函数将执行几十毫秒~几秒不等: -var data = getJSONSync('http://example.com/ajax'); -``` - -同步操作的好处是代码简单,缺点是程序将等待IO操作,在等待时间内,无法响应其它任何事件。而异步读取不用等待IO操作,但代码较麻烦。 - -## 异步还是同步 - -在fs模块中,提供同步方法是为了方便使用。那我们到底是应该用异步方法还是同步方法呢? - -由于Node环境执行的JavaScript代码是服务器端代码,所以,绝大部分需要在服务器运行期反复执行业务逻辑的代码,必须使用异步代码,否则,同步代码在执行时期,服务器将停止响应,因为JavaScript只有一个执行线程。 - -服务器启动时如果需要读取配置文件,或者结束时需要写入到状态文件时,可以使用同步代码,因为这些代码只在启动和结束时执行一次,不影响服务器正常运行时的异步执行。 \ No newline at end of file diff --git "a/03\350\214\203\345\217\266\345\205\260/2023-02-17 http\346\250\241\345\235\227\344\270\216\347\275\221\347\253\231\346\220\255\345\273\272\346\225\231\347\250\213.md" "b/03\350\214\203\345\217\266\345\205\260/2023-02-17 http\346\250\241\345\235\227\344\270\216\347\275\221\347\253\231\346\220\255\345\273\272\346\225\231\347\250\213.md" deleted file mode 100644 index 624820e..0000000 --- "a/03\350\214\203\345\217\266\345\205\260/2023-02-17 http\346\250\241\345\235\227\344\270\216\347\275\221\347\253\231\346\220\255\345\273\272\346\225\231\347\250\213.md" +++ /dev/null @@ -1,41 +0,0 @@ -# HTTP服务器 -用Node.js实现一个HTTP服务器程序非常简单。我们来实现一个最简单的Web程序hello.js,它对于所有请求,都返回Hello world!: -``` -'use strict'; - -// 导入http模块: -var http = require('http'); - -// 创建http server,并传入回调函数: -var server = http.createServer(function (request, response) { - // 回调函数接收request和response对象, - // 获得HTTP请求的method和url: - console.log(request.method + ': ' + request.url); - // 将HTTP响应200写入response, 同时设置Content-Type: text/html: - response.writeHead(200, {'Content-Type': 'text/html'}); - // 将HTTP响应的HTML内容写入response: - response.end('

Hello world!

'); -}); - -// 让服务器监听8080端口: -server.listen(8080); - -console.log('Server is running at http://127.0.0.1:8080/'); -``` -在命令提示符下运行该程序,可以看到以下输出: -``` -$ node hello.js -Server is running at http://127.0.0.1:8080/ -``` -不要关闭命令提示符,直接打开浏览器输入http://localhost:8080,即可看到服务器响应的内容 - -同时,在命令提示符窗口,可以看到程序打印的请求信息: -``` -GET: / -GET: /favicon.ico -``` - - -# 网站 - -![](./img/wangzhan.png) \ No newline at end of file diff --git "a/03\350\214\203\345\217\266\345\205\260/2023-02-20 http\346\226\207\344\273\266\346\234\215\345\212\241\345\231\250.md" "b/03\350\214\203\345\217\266\345\205\260/2023-02-20 http\346\226\207\344\273\266\346\234\215\345\212\241\345\231\250.md" deleted file mode 100644 index dfb50b8..0000000 --- "a/03\350\214\203\345\217\266\345\205\260/2023-02-20 http\346\226\207\344\273\266\346\234\215\345\212\241\345\231\250.md" +++ /dev/null @@ -1,99 +0,0 @@ -# 文件服务器 - -让我们继续扩展一下上面的Web程序。我们可以设定一个目录,然后让Web程序变成一个文件服务器。要实现这一点,我们只需要解析request.url中的路径,然后在本地找到对应的文件,把文件内容发送出去就可以了。 - -解析URL需要用到Node.js提供的url模块,它使用起来非常简单,通过parse()将一个字符串解析为一个Url对象: -``` -'use strict'; - -var url = require('url'); - -console.log(url.parse('http://user:pass@host.com:8080/path/to/file?query=string#hash')); -``` -结果如下: -``` -Url { - protocol: 'http:', - slashes: true, - auth: 'user:pass', - host: 'host.com:8080', - port: '8080', - hostname: 'host.com', - hash: '#hash', - search: '?query=string', - query: 'query=string', - pathname: '/path/to/file', - path: '/path/to/file?query=string', - href: 'http://user:pass@host.com:8080/path/to/file?query=string#hash' } -``` -处理本地文件目录需要使用Node.js提供的path模块,它可以方便地构造目录: -``` -'use strict'; - -var path = require('path'); - -// 解析当前目录: -var workDir = path.resolve('.'); // '/Users/michael' - -// 组合完整的文件路径:当前目录+'pub'+'index.html': -var filePath = path.join(workDir, 'pub', 'index.html'); -// '/Users/michael/pub/index.html' -``` -使用path模块可以正确处理操作系统相关的文件路径。在Windows系统下,返回的路径类似于C:\Users\michael\static\index.html,这样,我们就不关心怎么拼接路径了。 - -最后,我们实现一个文件服务器file_server.js: -``` -'use strict'; - -var - fs = require('fs'), - url = require('url'), - path = require('path'), - http = require('http'); - -// 从命令行参数获取root目录,默认是当前目录: -var root = path.resolve(process.argv[2] || '.'); - -console.log('Static root dir: ' + root); - -// 创建服务器: -var server = http.createServer(function (request, response) { - // 获得URL的path,类似 '/css/bootstrap.css': - var pathname = url.parse(request.url).pathname; - // 获得对应的本地文件路径,类似 '/srv/www/css/bootstrap.css': - var filepath = path.join(root, pathname); - // 获取文件状态: - fs.stat(filepath, function (err, stats) { - if (!err && stats.isFile()) { - // 没有出错并且文件存在: - console.log('200 ' + request.url); - // 发送200响应: - response.writeHead(200); - // 将文件流导向response: - fs.createReadStream(filepath).pipe(response); - } else { - // 出错了或者文件不存在: - console.log('404 ' + request.url); - // 发送404响应: - response.writeHead(404); - response.end('404 Not Found'); - } - }); -}); - -server.listen(8080); - -console.log('Server is running at http://127.0.0.1:8080/'); -``` -没有必要手动读取文件内容。由于response对象本身是一个Writable Stream,直接用pipe()方法就实现了自动读取文件内容并输出到HTTP响应。 - -在命令行运行node file_server.js /path/to/dir,把/path/to/dir改成你本地的一个有效的目录,然后在浏览器中输入http://localhost:8080/index.html: - -只要当前目录下存在文件index.html,服务器就可以把文件内容发送给浏览器。观察控制台输出: -``` -200 /index.html -200 /css/uikit.min.css -200 /js/jquery.min.js -200 /fonts/fontawesome-webfont.woff2 -``` -第一个请求是浏览器请求index.html页面,后续请求是浏览器解析HTML后发送的其它资源请求。 diff --git "a/03\350\214\203\345\217\266\345\205\260/2023-02-21 \347\273\203\344\271\240.md" "b/03\350\214\203\345\217\266\345\205\260/2023-02-21 \347\273\203\344\271\240.md" deleted file mode 100644 index 6e6cdd5..0000000 --- "a/03\350\214\203\345\217\266\345\205\260/2023-02-21 \347\273\203\344\271\240.md" +++ /dev/null @@ -1,74 +0,0 @@ -# 练习 - -在浏览器输入http://localhost:8080/时,会返回404,原因是程序识别出HTTP请求的不是文件,而是目录。请修改file_server.js,如果遇到请求的路径是目录,则自动在目录下依次搜索index.html、default.html,如果找到了,就返回HTML文件的内容。 - -```javascript -'use strict' - -let http = require('http') // 导入模块 -let fs = require('fs') //文件模块 -//创建服务器 -let server = http.createServer((req,res)=>{ - // 获取url路径 localhost:8080/{url} - let url = req.url; - // /url -> url - url = url.substring(1,url.length); - //查看url文件状态 - fs.stat(url,(err,stat)=>{ - // 访问 localhost:8080 - if(url=="") { - res.end("welcome!"); - }else if(err) {//未找到文件 - res.end("not find "); - return; - }else if(stat.isDirectory()){//不为空,并且有找到文件判断是否为目录 - //在目录下依次搜索index.html、default.html - fs.readdir(url,(err,files)=>{//read dir - let flag = true; - //查询有没有index.html - files.map(x=>{ - let path; - if(x=="index.html"){ - path = `./${url}/${x}`; - fs.readFile(path,"utf-8",(err,data)=>{ - res.end(data);//结束数据 - }); - flag=false; //找到index.html,未找到默认为true - return; - } - }); - //查询default.html - if(flag){ - let flag = true; - files.map(x=>{ - let path; - if(x=="default.html"){ - path = `./${url}/${x}`; - fs.readFile(path,"utf-8",(err,data)=>{ - res.end(data); - }); - flag = false;//找到default.html - return; - } - }); - //未找到文件 - if(flag){ - res.end("404_Not_find") - } - } - }); - return; - } - //不是目录 - fs.readFile(url,(err,data)=>{ - if(err){ - res.end("404"); - return; - } - res.end(data); - }) - }) -}); -server.listen("8080"); -``` -![效果图](./img/2023-02-20.png) \ No newline at end of file diff --git "a/03\350\214\203\345\217\266\345\205\260/2023-02-23 promise async await .md" "b/03\350\214\203\345\217\266\345\205\260/2023-02-23 promise async await .md" deleted file mode 100644 index 549868b..0000000 --- "a/03\350\214\203\345\217\266\345\205\260/2023-02-23 promise async await .md" +++ /dev/null @@ -1,354 +0,0 @@ -# promise用来做什么? - -我们的需求是一次的去执行异步代码 - -我们的做法是在异步请求成功后的回调函数里,执行下一个异步请求 - -但是这样就出现了回调地狱(回调函数中嵌套了回调函数,代码的阅读性 低,维护不变,让人看着害怕) - -promise就是用来解决回调地狱的 - - 回调地狱示例: - -```js - -// 需求:一次的读取a,b,c这三个文件 -const fs = require("fs"); - -// 读a文件 -fs.readFile(`${__dirname}/etc/a.txt`, "utf-8", (err, data) => { - if (err) { - console.log(err); - } else { - console.log(data); - // 读b文件 - fs.readFile(`${__dirname}/etc/b.txt`, "utf-8", (err, data) => { - if (err) { - console.log(err); - } else { - console.log(data); - // 读c文件 - fs.readFile(`${__dirname}/etc/c.txt`, "utf-8", (err, data) => { - if (err) { - console.log(err); - } else { - console.log(data); - } - }); - } - }); - } -}); - - ``` - -# promise工作流程 - -es6的语法,es6.ruanyifeng.com - -Promise对象是一个构造函数 ,用来生成promise实例 - -Promise构造函数接受一个函数作为参数 - -这个作为参数的函数,又有两个参数,这两个参数分别是resolve和reject - -这两个参数它们也是函数,只不过这两个函数由 javascript 引擎提供,不用自己部署 - -异步操作成功后调用resolve()方法,他内部调用了then()里面的第一个参数函数 - -异步操作成功后调用reject()方法,他内部调用了then()里面的第二个参数函数. - -```js -const fs = require("fs"); -// 调用Promise构造函数,创建一个promise的实例 -let p = new Promise((resolve, reject) => { - // 写异步操作(读文件) - fs.readFile(`${__dirname}/etc/a.txt`, "utf-8", (err, data) => { - if (!err) { - // 操作成功(读文件成功) - resolve(data); // 调用resolve方法 - // 调用then()里面的第一个参数函数 - } else { - reject(err); // 调用reject方法 - // 调用then()里面的第二个参数函数 - } - }); -}); - -p.then( - (data) => { - console.log(data); - }, - (err) => { - console.log(err); - } -); - -``` - -# promise原理 - -Promise对象代表一个异步操作. - -有三种状态: pending (进行中)、fulfilled (已成功)和rejected (已失败) - -Promise对象的状态改变,只有两种可能:从pending变 为fulfilled和从pending变为rejected。 - -只有异步操作的结果,可以决定当前是哪一种状态,任何其他操作都无法改变这个状态 - -如果异步操作成功了(读文件成功了),从pending (进行中)变为 fulfilled (已成功) ; - -如果异步操作失败了(读文件失败了),从pending (进行中)变为 rejected (已失败) ; - -状态如果已经确定了, 就不会再去改变这个状态了 - -# promise特点及其封装 - -Promise新建后就会立即执行 - -所以不要在promise里面写其他的代码,只写这个异步操作的代码就可以了 - -```js -const fs = require("fs"); -function getPromise(filename) { - // 调用Promise构造函数,创建一个promise的实例 - return new Promise((resolve, reject) => { - // 写异步操作(读文件) - fs.readFile(`${__dirname}/etc/${filename}.txt`, "utf-8", (err, data) => { - if (!err) { - // 操作成功(读文件成功) - resolve(data); // 调用resolve方法 - // 调用then()里面的第一个参数函数 - } else { - reject(err); // 调用reject方法 - // 调用then()里面的第二个参数函数 - } - }); - }); -} - -// console.log(getPromise("a")); -getPromise("a").then( - (data) => { - console.log(data); - }, - (err) => { - console.log(err); - } -); -``` - -# promise正确写法 - -+ promise如何解决回调地狱 - - + 链式编程解决 - - + 我们用promise解决的问题:让异步操作有顺序,并且不能有回调地狱 - -让异步操作有顺序本质是: - -异步操作实际上是没有顺序的 - -在异步操作成功后的回调函数里返回另外的promise,调用他的then方法 - -```js -const fs = require("fs"); -function getPromise(filename) { - // 调用Promise构造函数,创建一个promise的实例 - return new Promise((resolve, reject) => { - // 写异步操作(读文件) - fs.readFile(`${__dirname}/etc/${filename}.txt`, "utf-8", (err, data) => { - if (!err) { - // 操作成功(读文件成功) - resolve(data); // 调用resolve方法 - // 调用then()里面的第一个参数函数 - } else { - reject(err); // 调用reject方法 - // 调用then()里面的第二个参数函数 - } - }); - }); -} - -// console.log(getPromise("a")); -getPromise("a") - .then((data) => { - console.log(data); - //调用函数得到一个读b文件的promise对象并返回 - return getPromise("b"); - }) - .then((data) => { - console.log(data); - //调用函数得到一个读c文件的promise对象并返回 - return getPromise("c"); - }) - .then((data) => { - console.log(data); - }); -``` - -# promise的其他方法 - -+ catch() - - 能够抓取错误的 -```js - const fs = require("fs"); - function getPromise(filename) { - // 调用Promise构造函数,创建一个promise的实例 - return new Promise((resolve, reject) => { - // 写异步操作(读文件) - fs.readFile(`${__dirname}/etc/${filename}.txt`, "utf-8", (err, data) => { - if (!err) { - // 操作成功(读文件成功) - resolve(data); // 调用resolve方法 - // 调用then()里面的第一个参数函数 - } else { - reject(err); // 调用reject方法 - // 调用then()里面的第二个参数函数 - } - }); - }); - } - - // console.log(getPromise("a")); - getPromise("a") - .then((data) => { - console.log(data); - //调用函数得到一个读b文件的promise对象并返回 - return getPromise("b"); - }) - .then((data) => { - console.log(data); - //调用函数得到一个读c文件的promise对象并返回 - return getPromise("c"); - }) - .then((data) => { - console.log(data); - }) - .catch((err) => { - console.log(err); - }); -``` - -+ all() - -```js - const fs = require("fs"); - function getPromise(filename) { - // 调用Promise构造函数,创建一个promise的实例 - return new Promise((resolve, reject) => { - // 写异步操作(读文件) - fs.readFile(`${__dirname}/etc/${filename}.txt`, "utf-8", (err, data) => { - if (!err) { - // 操作成功(读文件成功) - resolve(data); // 调用resolve方法 - // 调用then()里面的第一个参数函数 - } else { - reject(err); // 调用reject方法 - // 调用then()里面的第二个参数函数 - } - }); - }); - } - - let p1 = getPromise("a"); - let p2 = getPromise("b"); - let p3 = getPromise("c"); - - // Promise.all()方法用于将多个 Promise 实例,包装成一个新的 Promise 实例 - let pAll = Promise.all([p1, p2, p3]); - // 一个都不能少,每一个promise都要读取成功才会成功,相当于是并且 - pAll.then((data) => { - console.log(data); - }); -``` - -+ race -```js - const fs = require("fs"); - function getPromise(filename) { - // 调用Promise构造函数,创建一个promise的实例 - return new Promise((resolve, reject) => { - // 写异步操作(读文件) - fs.readFile(`${__dirname}/etc/${filename}.txt`, "utf-8", (err, data) => { - if (!err) { - // 操作成功(读文件成功) - resolve(data); // 调用resolve方法 - // 调用then()里面的第一个参数函数 - } else { - reject(err); // 调用reject方法 - // 调用then()里面的第二个参数函数 - } - }); - }); - } - - let p1 = getPromise("a"); - let p2 = getPromise("b"); - let p3 = getPromise("c"); - - // Promise.race()方法同样是将多个 Promise 实例,包装成一个新的 Promise 实例 - let pRace = Promise.race([p1, p2, p3]); - // 只要有一个promise执行成功,那这个pRace就成功,相当于是或者 - pRace.then((data) => { - console.log(data); - }); -``` -# 什么是async/await - - async/await是es7推出的一套关于异步的终极解决方案;主要作用就是转异步为同步。 - -# async/await语法格式 - -```js -//可写一个函数返回的是异步的promise对象,也可以定义一个异步函数; - function fn(){ - return new Promise((resolve,reject)=>{ - resolve(666) - }) - } - let p1=new Promise((n1,n2)=>{ //也可以直接写一个promise对象 - n1(999) - }) - // 在需要使用上面异步函数的函数前面,加上async声明,声明这是一个异步函数 - async function fn2(){ - // 在异步函数前面加上await,函数执行就会等待用await声明的异步函数执行完毕之后,在往下执行 - let a1=await fn() - let p=await p1 - console.log(a1,p) - } - fn2() -``` - -注意:async/await不能捕获错误,可以使用try/catch来进行错误的捕获 - -```js - function fn() { - return new Promise((resolve, reject) => { - resolve(666) - }) - } - let p1 = new Promise((n1, n2) => { - n1(999) - }) - async function fn2() { - try { - let a1 = await fn() - let p = await p1 - console.log(a1, p) - } catch (err) { - console.log(err) - } - } - fn2() -``` - -# 小结: - -+ 1.async函数在声明形式上和普通函数没有区别,函数声明式,函数表达式,对象方法,class方法和箭头函数等都可以声明async函数。 - -+ 2.任何一个await语句后面的 Promise 对象变为reject状态,那么整个async函数都会中断执行。 - -+ 3.async函数返回的 Promise 对象,必须等到内部所有await命令后面的 Promise 对象执行完,才会发生状态改变,除非遇到return语句或者抛出错误。也就是说,只有async函数内部的异步操作执行完,才会执行then方法指定的回调函数。 diff --git "a/03\350\214\203\345\217\266\345\205\260/2023-02-24 koa.md" "b/03\350\214\203\345\217\266\345\205\260/2023-02-24 koa.md" deleted file mode 100644 index 3e834f7..0000000 --- "a/03\350\214\203\345\217\266\345\205\260/2023-02-24 koa.md" +++ /dev/null @@ -1,92 +0,0 @@ -# 一、koa是什么? - ## 1) koa定义: - koa是一个基于node实现的一个新的轻量级web框架,它是由express框架的原 班人马打造的。 - ## 2)特点: - (1)免除重复繁琐的回调函数嵌套,提高错误处理效率 - (2) 独特的中间件流程控制 - (3) 典型洋葱模型 - ## 3)优点 : - 简洁 , 表达力强, 自由度高 - ## 4)适用场景: -# 二、使用步骤 -## 1. 使用前提条件 - - Koa需要 node v7.6.0或更高版本 - -## 2. 搭建初始项目结构 -### 1>安装依赖 -```js - // 初始化项目会生成package.json和node_modules - npm init - - // 安装koa - npm install --save koa -``` -### 2> 在根目录下,创建app.js 并全局配 -```js - // 引入koa - const koa = require("koa") - const app = new koa() - - - //渲染页面 - // ctx 是 context的简写 , 包含respose 和 request - app.use((ctx)=>{ - ctx.body = "hello world" - }) - - - // 监听 - app.listen("8888", ()=>{ - console.log(" koa服务启动 ") - }) - - - /** - - 在终端运行: node app.js 后,会打印出 “koa服务启动” ,在浏览器输入 localhost:8888 , - 就会显示 hello world - - **/ -``` -### 3> 文件会生成: - -![](./img/230224.png) - -## 3. 添加路由 -```js - // 安装依赖 koa-router 或者 @koa/router - npm install koa-router - - - // 实例化路由 - const Router = require("koa-router") - const router = new Router() - - - // 设置首页路由 - router.get("/",async (ctx)=>{ - ctx.body = "首页" - }) - - - // 全局配置 - app.use(router.routes(), router.allowedMethods()) - ``` - - -## 4. 安装热更新nodemon -```js - // 安装依赖 - // nodemon必须全局安装 ,否则会报错 - npm install nodemon -g - - - *// 可在package.json中找到"scripts",配置项目启动命令npm run dev: - - "scripts": { - "test": "echo \"Error: no test specified\" && exit 1", - "dev":"nodemon app.js" - }, - -``` diff --git "a/03\350\214\203\345\217\266\345\205\260/2023-02-27 middleware \345\244\204\347\220\206url.md" "b/03\350\214\203\345\217\266\345\205\260/2023-02-27 middleware \345\244\204\347\220\206url.md" deleted file mode 100644 index fa0091d..0000000 --- "a/03\350\214\203\345\217\266\345\205\260/2023-02-27 middleware \345\244\204\347\220\206url.md" +++ /dev/null @@ -1,83 +0,0 @@ -# koa middleware - -核心代码是: - -```js -app.use(async (ctx, next) => { - await next(); - ctx.response.type = 'text/html'; - ctx.response.body = '

Hello, koa2!

'; -}); -``` -每收到一个http请求,koa就会调用通过app.use()注册的async函数,并传入ctx和next参数。 - -我们可以对ctx操作,并设置返回内容。但是为什么要调用await next()? - -原因是koa把很多async函数组成一个处理链,每个async函数都可以做一些自己的事情,然后用await next()来调用下一个async函数。我们把每个async函数称为middleware,这些middleware可以组合起来,完成很多有用的功能。 - -middleware的顺序很重要,也就是调用app.use()的顺序决定了middleware的顺序。 - -此外,如果一个middleware没有调用await next(),会怎么办?答案是后续的middleware将不再执行了。这种情况也很常见. - -# 处理URL - -## koa-router -为了处理URL,我们需要引入koa-router这个middleware,让它负责处理URL映射。 - -我们把上一节的hello-koa工程复制一份,重命名为url-koa。 - -先在package.json中添加依赖项: -``` -"koa-router": "7.0.0" -``` -然后用npm install安装。 - -接下来,我们修改app.js,使用koa-router来处理URL: -```js -const Koa = require('koa'); - -// 注意require('koa-router')返回的是函数: -const router = require('koa-router')(); - -const app = new Koa(); - -// log request URL: -app.use(async (ctx, next) => { - console.log(`Process ${ctx.request.method} ${ctx.request.url}...`); - await next(); -}); - -// add url-route: -router.get('/hello/:name', async (ctx, next) => { - var name = ctx.params.name; - ctx.response.body = `

Hello, ${name}!

`; -}); - -router.get('/', async (ctx, next) => { - ctx.response.body = '

Index

'; -}); - -// add router middleware: -app.use(router.routes()); - -app.listen(3000); -console.log('app started at port 3000...'); -``` -注意导入koa-router的语句最后的()是函数调用: -``` -const router = require('koa-router')(); -``` -相当于: -``` -const fn_router = require('koa-router'); -const router = fn_router(); -``` -然后,我们使用router.get('/path', async fn)来注册一个GET请求。可以在请求路径中使用带变量的/hello/:name,变量可以通过ctx.params.name访问。 - -再运行app.js,我们就可以测试不同的URL: -``` -输入首页:http://localhost:3000/ -``` -``` -输入:http://localhost:3000/hello/koa -``` \ No newline at end of file diff --git "a/03\350\214\203\345\217\266\345\205\260/2023-02-28 \345\244\204\347\220\206post\350\257\267\346\261\202.md" "b/03\350\214\203\345\217\266\345\205\260/2023-02-28 \345\244\204\347\220\206post\350\257\267\346\261\202.md" deleted file mode 100644 index cdab559..0000000 --- "a/03\350\214\203\345\217\266\345\205\260/2023-02-28 \345\244\204\347\220\206post\350\257\267\346\261\202.md" +++ /dev/null @@ -1,52 +0,0 @@ -# 处理post请求 -用router.get('/path', async fn)处理的是get请求。如果要处理post请求,可以用router.post('/path', async fn)。 - -用post请求处理URL时,我们会遇到一个问题:post请求通常会发送一个表单,或者JSON,它作为request的body发送,但无论是Node.js提供的原始request对象,还是koa提供的request对象,都不提供解析request的body的功能! - -所以,我们又需要引入另一个middleware来解析原始request请求,然后,把解析后的参数,绑定到ctx.request.body中。 - -koa-bodyparser就是用来干这个活的。 - -我们在package.json中添加依赖项: -``` -"koa-bodyparser": "3.2.0" -``` -然后使用npm install安装。 - -下面,修改app.js,引入koa-bodyparser: -``` -const bodyParser = require('koa-bodyparser'); -``` -在合适的位置加上: -``` -app.use(bodyParser()); -``` -由于middleware的顺序很重要,这个koa-bodyparser必须在router之前被注册到app对象上。 - -现在我们就可以处理post请求了。写一个简单的登录表单: -```js -router.get('/', async (ctx, next) => { - ctx.response.body = `

Index

-
-

Name:

-

Password:

-

-
`; -}); - -router.post('/signin', async (ctx, next) => { - var - name = ctx.request.body.name || '', - password = ctx.request.body.password || ''; - console.log(`signin with name: ${name}, password: ${password}`); - if (name === 'koa' && password === '12345') { - ctx.response.body = `

Welcome, ${name}!

`; - } else { - ctx.response.body = `

Login failed!

-

Try again

`; - } -}); -``` -注意到我们用var name = ctx.request.body.name || ''拿到表单的name字段,如果该字段不存在,默认值设置为''。 - -类似的,put、delete、head请求也可以由router处理。 diff --git "a/03\350\214\203\345\217\266\345\205\260/2023-03-02 \351\207\215\346\236\204\350\267\257\347\224\261\345\212\237\350\203\275\302\267.md" "b/03\350\214\203\345\217\266\345\205\260/2023-03-02 \351\207\215\346\236\204\350\267\257\347\224\261\345\212\237\350\203\275\302\267.md" deleted file mode 100644 index c0ab377..0000000 --- "a/03\350\214\203\345\217\266\345\205\260/2023-03-02 \351\207\215\346\236\204\350\267\257\347\224\261\345\212\237\350\203\275\302\267.md" +++ /dev/null @@ -1,73 +0,0 @@ -# 重构路由功能 - -实现路由功能: -```js -//server.js -var http = require('http') //输入node.js核心模块 - -function startServer(route){ - var onRequest = function(req,res) { - route(req.url) //传入请求的路径 - } - var server = http.createServer(onRequest) -server.listen(3000) //监听一个端口 -} - -module.exports.startServer = startServer; //输出模块 -``` -我们新建一个server.js模块,在这个模块中,我们定义了一个startServer函数,这个函数监听了一个3000端口,函数执行的时候调用route方法。我们先将这个模块输出 -```js -//index.js -var server = require("./server"); //输入./server模块 -var router = require('./router'); //输入./router模块 -var handler = require("./handler") //输入./handler模块 -var handle = {}; -handle["/"] = handler.home; -handle["/home"] = handler.home; -handle["/list"] = handler.list; -handle["/add"] = handler.add -server.startServer(router.route,handle) -``` -route()接收到了请求路径,我们希望它接收到不同的路径会调用不同的方法,因此我们可以先把要执行的方法作为属性值传给一个handle对象,它的键就等于route接收到的请求路径。于是我们分别把创建route.js和handler.js,在index.js中把请求路径和handle对象传给route函数 -```js - //server.js -var http = require('http') //引入node.js核心模块 - -function startServer(route,handle){ -var onRequest = function(req,res) { -route(req.url,handle,res) //传入请求的路径 -} -var server = http.createServer(onRequest) -server.listen(3000) //监听一个端口 -} -module.exports.startServer = startServer; //输出模块 - - -//route.js -function route(pathname,handle,res){ - if(typeof handle[pathname] == "function"){ - handle[pathname](res) - }else{ - res.end("404:connot find anything") //未匹配到路径的时候,响应“404”页面 - } -} -module.exports.route = route; - - -//handler.js -function home (res){ - res.end("home") -} -function list (res){ - res.end("list") -} -function add(res){ - res.end("add") -} -module.exports = { - home:home, - list: list, - add:add -} -``` -因为我们希望在地址栏输入路径的时候,页面会响应出响应的内容,所以我们要把响应参数res传递给route函数,它又会把这个参数传递给handler里的方法,这个我们就能很灵活地根据请求的地址响应我们想要的内容。 diff --git "a/03\350\214\203\345\217\266\345\205\260/2023-03-03 \347\273\203\344\271\240\345\237\272\346\234\254.md" "b/03\350\214\203\345\217\266\345\205\260/2023-03-03 \347\273\203\344\271\240\345\237\272\346\234\254.md" deleted file mode 100644 index 7444e33..0000000 --- "a/03\350\214\203\345\217\266\345\205\260/2023-03-03 \347\273\203\344\271\240\345\237\272\346\234\254.md" +++ /dev/null @@ -1,84 +0,0 @@ -# app.js -```js -'use strict' - -//引用模块 -const Koa=require('koa'); - -//引入index.js的模块 -let index=require('./controller') - - -//实例化 -let app=new Koa(); - -index(app); -// console.log(obj) - - -//监视端口 -let posh=8000; -app.listen(posh); - -console.log(`网站:http://localhost:${posh}`); -``` - -# index.js - -```js -'use strict' -//模块 -const fs = require('fs'); - -//koa-router:是函数调用 -const router = require('koa-router')(); -//koa-bodyparser不是函数调用 -const bodyParser = require('koa-bodyparser') - - - -//请求的方法函数:注册指定模块的所有定义路由 -function fn_url(obj) { - for (let key in obj) { - // console.log(key) - let ad = key.split(' ')//将字符串分割成数组 - // console.log('是'+ad) - let method = ad[0]//请求方法 - let url = ad[1]//请求路径 - let fn = obj[key]//函数方法 - - if (method === 'get') { - router.get(url, fn) - } else if (method === 'post') { - router.post(url, fn) - } else if (method === 'put') { - router.put(url, fn) - } else if (method === 'delete') { - router.delete(url, fn) - } - } -} - -//判断请求路径的方法函数 -function url(app) { - let filtes = fs.readdirSync('./controller')//用同步获取控制器文件夹中的文件 - console.log(filtes) - let filt = filtes.filter(item => { - return item !== 'index.js'//排除index.js文件 - }) - - filt.forEach(item => { - //replace() 方法用于在字符串中用一些字符替换另一些字符,或替换一个与正则表达式匹配的子串。 - let obj = require('./' + item.replace('.js', ''))//调用的路由模块 - console.log('修改后:' + obj) - - fn_url(obj); - }) - - app.use(bodyParser()) - app.use(router.routes()) -} - -console.log(url) -module.exports = url; -``` diff --git "a/03\350\214\203\345\217\266\345\205\260/2023-03-06 \351\207\215\346\236\204anew.md" "b/03\350\214\203\345\217\266\345\205\260/2023-03-06 \351\207\215\346\236\204anew.md" deleted file mode 100644 index 48989b3..0000000 --- "a/03\350\214\203\345\217\266\345\205\260/2023-03-06 \351\207\215\346\236\204anew.md" +++ /dev/null @@ -1,151 +0,0 @@ -# 重构 - -1. app.js - -```js -//引入模块 -const Koa=require('koa'); -const registerRouter=require('./controllers') -const templating=require('./templating') -const staticFiles=require('./staticFiles') -//实例化 -let app=new Koa(); - - -//注册路由 -registerRouter(app) - -//端口号 -let port=8088; -app.listen(port) - -console.log(`http://localhost:${port}`); -``` -2. 控制器(controllers) - index.js - ```js - const router=require('koa-router')(); - const bodyParser=require('koa-bodyparser'); - const {findControllers,registerRouter2}=require('../utils/tools') - - function registerRouter(app){ - //找到所有文件 - let files=findControllers() - //遍历注册 - registerRouter2(files,router) - - app.use(router.routes()); - app.use(bodyParser()) - } - - module.exports=registerRouter; - ``` - roles.js -```js - - let list=[ - { - id:1, - name:'小范', - img:url - },{ - id:2, - name:'小徐', - img:url - },{ - id:3, - name:'小陈', - img:url - },{ - id:4, - name:'小王', - img:url - },{ - id:5, - name:'小许', - img:url - }, - ] - - async function getAll(ctx,next){ - - - } - async function getById(ctx,next){ - - } - async function addItem(ctx,next){ - - } - async function updateItem(ctx,next){ - - } - async function delItem(ctx,next){ - - } - - - module.exports={ - 'get /roles':getAll, - 'get /roles/:id':getById, - 'post /roles':addItem, - 'put /roles/:id':updateItem, - 'delete /roles/:id':delItem, - } - ``` -3. 路由系统封装(utils——>tools.js) - -``` js - 'use strict'; - - const fs = require('fs'); - - // 查找所有路由文件 - function findControllerFiles(path) { - // 如果传入指定目录,则从指定目录查找路由文件,否则默认从 controllers目录下查找路由文件 - path = path || './controllers'; - - // 使用fs模块读取指定目录下的文件 - let files = fs.readdirSync(path); - - // 返回过滤后的路由文件(查找到的文件过滤掉非.js结尾的、 并且不能是index.js的文件) - return files.filter(item => { - return item.endsWith('.js') && item !== 'index. js'; - }); - } - - //注册路由(传入所有的路由文件) - function registryRouter(files,router) { - // 遍历路由文件 - files.forEach(file => {// 形如这个样子的字符串: 'users.js' - - // 加载指定文件模块,返回形式是一个对象 - let tmpModule = require('../controllers/' + file); - // 遍历对象 - for (let key in tmpModule) { - // 使用空格分割字符串 - let arr = key.split(' '); - - let method = arr[0];//http的方法,可能是:get post put delete中的其中一种 - let url = arr[1];//请求地址,不包含主机地址:localhost:port - let fn = tmpModule[key];//请求需要调用的异步函数 - - if (method === 'get') { - router.get(url, fn); - } else if (method === 'post') { - router.post(url, fn); - } else if (method === 'put') { - router.put(url, fn); - } else if (method === 'delete') { - router.delete(url, fn); - } - } - }) - } - - module.exports = { - findControllerFiles, - registryRouter - } - ``` - \ No newline at end of file diff --git "a/03\350\214\203\345\217\266\345\205\260/2023-03-07 \347\203\255\351\207\215\350\275\275 nunjucks.md.md" "b/03\350\214\203\345\217\266\345\205\260/2023-03-07 \347\203\255\351\207\215\350\275\275 nunjucks.md.md" deleted file mode 100644 index 1ad64e8..0000000 --- "a/03\350\214\203\345\217\266\345\205\260/2023-03-07 \347\203\255\351\207\215\350\275\275 nunjucks.md.md" +++ /dev/null @@ -1,194 +0,0 @@ - -## 一.热重载:nodemon - -### 安装 - -+ 方法一:全局安装 -```js -$ npm i -g nodemon - -删除: npm un -g nodemon -``` - -+ 方法二:本地安装 -```js -$ npm i -D nodemon - -注意:本地安装需要在 package.json 文件的 script 脚本中指定要需要执行的命令 - -{ - "script": { - "dev": "nodemon app.js" - } -} -使用 npm dev 运行 -``` - - - -## 二.nunjucks模板引擎 - -### 安装nunjucks -``` - npm add nunjucks/yarn add nunjucks -``` - -### 用法 - -### nunjucks 插值语法 - -+ nunjucks 的插值语法和我们平常写 VUE 的语法一样, 只需要写双大括号{{}} 即可以完成插值引用. - -### 渲染:render -#### index.html -```js - - - - - - - 练习 - - - {{username}}敲代码 - - -``` -#### users.js -```js -'use strict' - -const nunjucks=require('nunjucks') - -nunjucks.configure('views',{ - autoescape:true -}) - -async function getAll(ctx, next) { - ctx.body=nunjucks.render('index.html',{username:"码农"}) -} - -async function getById(ctx, next) { - -} - -async function addItem(ctx, next) { - -} - -async function updateItem(ctx, next) { - -} - -async function delItem(ctx, next) { - -} - -module.exports = { - 'get /users': getAll, - 'get /users/:id': getById, - 'post /users': addItem,//新增 - 'put /users/:id': updateItem,//修改 - 'delete /users/:id': delItem, -} -``` - -### 循环:for in 循环 - -+ 开始和结束语句都要在 **{% %}**中间 - -+ 要一个结束循环的标志. {% endfor %} - -#### use.html -```js - - - - - - - 生活 - - -
    - {% for item in items %} - - - -
  • {{item.name}}
  • - - {% endfor %} -
- - -``` -#### users,js -```js -'use strict' - -const nunjucks=require('nunjucks') - -nunjucks.configure('views',{ - autoescape:true -}) - -async function getAll(ctx, next) { - ctx.body=nunjucks.render('use.html',{items:[ - {id:1,name:"HTML"}, - {id:2,name:"SQL"}, - {id:3,name:"JS"}, - {id:4,name:"NODEJS"}, - ]}) -} - -async function getById(ctx, next) { - // let id = ctx.request.params.id; - - // let data = list.filter(item => { - // return item.id == id - // }) - - // ctx.body = data -} - -async function addItem(ctx, next) { - -} - -async function updateItem(ctx, next) { - -} - -async function delItem(ctx, next) { - -} - -module.exports = { - 'get /users': getAll, - 'get /users/:id': getById, - 'post /users': addItem,//新增 - 'put /users/:id': updateItem,//修改 - 'delete /users/:id': delItem, -} -``` - -### nunjucks.configure() -``` -传入 path 指定存放模板的目录,opts 可让某些功能开启或关闭,这两个变量都是可选的。path 的默认值为当前的工作目录,opts 提供以下功能: -``` -+ autoescape (默认值: true) 控制输出是否被转义,查看 Autoescaping - -+ throwOnUndefined (default: false) 当输出为 null 或 undefined 会抛出异常 -+ trimBlocks (default: false) 自动去除 block/tag 后面的换行符 -+ lstripBlocks (default: false) 自动去除 block/tag 签名的空格 -+ watch (默认值: false) 当模板变化时重新加载。使用前请确保已安装可选依赖 chokidar。 -+ noCache (default: false) 不使用缓存,每次都重新编译 -+ web 浏览器模块的配置项 - + useCache (default: false) 是否使用缓存,否则会重新请求下载模板 - + async (default: false) 是否使用 ajax 异步下载模板 -+ express 传入 express 实例初始化模板设置 -+ tags: (默认值: see nunjucks syntax) 定义模板语法,查看 Customizing Syntax -``` -configure 返回一个 Environment 实例, 他提供了简单的 api 添加过滤器 (filters) 和扩展 (extensions) -``` \ No newline at end of file diff --git "a/03\350\214\203\345\217\266\345\205\260/2023-03-09 \345\244\204\347\220\206\351\235\231\346\200\201\350\265\204\346\272\220.md" "b/03\350\214\203\345\217\266\345\205\260/2023-03-09 \345\244\204\347\220\206\351\235\231\346\200\201\350\265\204\346\272\220.md" deleted file mode 100644 index 423d146..0000000 --- "a/03\350\214\203\345\217\266\345\205\260/2023-03-09 \345\244\204\347\220\206\351\235\231\346\200\201\350\265\204\346\272\220.md" +++ /dev/null @@ -1,108 +0,0 @@ -# 处理静态资源 -1. 问题描述 - 浏览器在收到服务器返回的HTML内容之后,就要开始从上到下依次解析,当在解析的过程中,如果发现: - - link - script - img - iframe - video - audio - - 等带有src或者link 的href属性标签的时候,浏览器会自动对这些静态资源发起一个新的请求。我们要对这些新的请求进行处理 - -2. 解决方法 - -为了方便的统一处理这些静态资源,所以我们约定把所有的静态资源都存放在 public目录,目的是为了统一处理 - -如果请求路径是以 /public/ 开头的,则服务器认为你要获取 public中的某个资源,所以我们就直接可以把请求路径当作文件路径来直接进行读取 - -3. 代码演示 - -# index.html文件 -```js - - - - - 首页 - - - - -

灰太狼

- - - -``` -注意:在服务端中,文件中的路径就不要去写相对路径了。因为这个时候所有的资源都是通过url标识来获取的。 -服务器开放 /public/ 目录 -所以代码中的请求路径都写成:/public/xxx -/ 在这里就是url根路径的意思。 -浏览器在真正发请求的时候会最终把url拼接到 http:/ /127.0.0.1:3000上 - -# app.js文件 - -如果不对那些静态资源进行处理,我们看一下是什么情况 -```js -let http = require('http'); -let fs = require('fs'); - -http.createServer(function (request,response) { - console.log("服务器收到请求"); - let url = request.url; - if (url === '/') { - fs.readFile('./views/index.html',function (err,data) { - if (err) { - response.end('404'); - return; - } - response.end(data) - }) - } -}).listen(3000,function () { - console.log('http://127.0.0.1:3000'); -}) -``` -![](./img/jingtaiziyuanchuli.png) - -使用node app.js启动服务器,访问 http://127.0.0.1:3000,打开控制台,结果如图所示。原因就在于我们没有写对这些静态资源进行处理的代码,服务器读取不到它们,也就无法返回给浏览器。下面重新写app.js - - -# app.js(对静态资源进行处理) - -```js -let http = require('http'); -let fs = require('fs'); - -http.createServer(function (request,response) { - console.log("服务器收到请求"); - let url = request.url; - if (url === '/') { - fs.readFile('./views/index.html',function (err,data) { - if (err) { - response.end('404'); - return; - } - response.end(data) - }) - } else if (url.indexOf('/public/') !== -1) { - //此处是关键,当发现请求路径中含有 /public/,我们把请求路径当作文件路径来直接进行读取 - fs.readFile('.' + url,function (err,data) { - if (err) { - response.end('404'); - return; - } - response.end(data); - }) - } -}).listen(3000,function () { - console.log('http://127.0.0.1:3000'); -}) -``` - -我们所有的静态资源都放在了public文件夹中,所以资源的请求路径前都会有 /public/ -所以说如果请求路径是以 /public/ 开头的,则服务器认为你要获取 public中的某个资源,我们就直接可以把请求路径当作文件路径来直接进行读取 - -我们重新启动node app.js,访问 http://127.0.0.1:3000,刷新一下,页面马上就可以显示出来。打开控制台看看,资源也都请求到了。 - diff --git "a/03\350\214\203\345\217\266\345\205\260/2023-03-10 sequelize.md" "b/03\350\214\203\345\217\266\345\205\260/2023-03-10 sequelize.md" deleted file mode 100644 index 0304108..0000000 --- "a/03\350\214\203\345\217\266\345\205\260/2023-03-10 sequelize.md" +++ /dev/null @@ -1,85 +0,0 @@ -# 一.使用Sequelize - -## 安装Sequelize -```js -使用 npm -npm i sequelize /npm add sequelize - -使用 yarn -yarn add sequelize - -``` - -## 为所选数据库安装驱动程序 - -+ 上课讲mysql -```js -使用 npm -npm i pg pg-hstore # PostgreSQL -npm i mysql2 # MySQL -npm i mariadb # MariaDB -npm i sqlite3 # SQLite -npm i tedious # Microsoft SQL Server -npm i ibm_db # DB2 - - -使用 yarn -yarn add pg pg-hstore # PostgreSQL -yarn add mysql2 # MySQL -yarn add mariadb # MariaDB -yarn add sqlite3 # SQLite -yarn add tedious # Microsoft SQL Server -yarn add ibm_db # DB2 -``` -## 详细的 -+ Sequelize的中文文档: https://www.sequelize.cn/core-concepts/getting-started - -# 二.安装mysql - -安装步骤的详细地址: https://blog.csdn.net/SoloVersion/article/details/123760428 - -+ 装5.7.x的更稳定(也不知道是不是真的) - -# 三.使用 - -## 运行mysql - -方法一: -+ 右键打开任务管理器,在服务里打开mysql - -方法二: - -+ win+R,打开运行框,输入services.msc直接打开服务,在里面打开mysql - - -## 使用命令行,查看数据库的连接 - -+ win+R,输入cmd - -+ 输入mysql,看看能不能正常运行,如果不可以就有添加一下环境变量 - -+ 连接一下,输入 -``` - mysql -u root -p -``` -+ 输入密码,成功就连接上了 - -+ 创建数据库,**必须用;结束** -``` - create database 库名; -``` - -+ 进入数据库 -``` - use 库名 -``` - -+ 查看库中的表 -``` - show tables -``` - -+ 查看表的内容 -``` - select * from 表名 -``` \ No newline at end of file diff --git "a/03\350\214\203\345\217\266\345\205\260/img/2023-02-20.png" "b/03\350\214\203\345\217\266\345\205\260/img/2023-02-20.png" deleted file mode 100644 index d5156ec5ec667ff25974db87abfa50f3678ff239..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 5447 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 XK=H#z2Ieg35J*c+=YFBev!MS1n8C{X diff --git "a/03\350\214\203\345\217\266\345\205\260/img/230224.png" "b/03\350\214\203\345\217\266\345\205\260/img/230224.png" deleted file mode 100644 index ec33e298727201beff032b711aef07a75ba592cd..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 17500 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 diff --git "a/03\350\214\203\345\217\266\345\205\260/img/httpmokuai/img01.png" "b/03\350\214\203\345\217\266\345\205\260/img/httpmokuai/img01.png" deleted file mode 100644 index f30b31b7eb81492c6919fa40e7a77305256f0982..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 7662 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

Vq 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 diff --git "a/03\350\214\203\345\217\266\345\205\260/img/httpmokuai/img02.png" "b/03\350\214\203\345\217\266\345\205\260/img/httpmokuai/img02.png" deleted file mode 100644 index 62c88a1dd191aeea5ff7359d87dad06090ff35e6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 16751 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 z M;{)B;>wq;k>}h@qXo35}zeaY-w*|4G<7&Fx9S?C^+L1@UD|LoeEdw%Ku9f-{ zCL<|muW|L>3%Jn6F0bHu^+zydy}jS&gJMrn;UnFyqwT}kaONj~>F-lpv4-*Bjc1*u zp}gqAo(U-uNjsKnGZ*C7;??@cqn}vr)97WMA>d9|%}%$VnKu$K)}Zo0s>vPk#4<+{0Cm)gIk zugh)IjR*LBK>BrNCxlgzdAhpJ8H#-jOSaJxehYn%Q95r1(!7_iLz?;RDc_N`O(}eQ zP*AD6W2#*<_l=i4RleTz8ZR}*ICta6L^|lpMYDBpU)SN}Iy=FV9L_^g!qSuvh|eRr zRzS*hau(6J^7+#j?YKDmFbh7ZNK04)C<<#R3j*ptGaT|&d z4*aTqSyhZ27~dCt^?i5;C5AY?K~4)6q;QE?q4`4`JHp$ekdyr2IguJvu?)WOGOw+2 ztW1l4^?2iMh1ovpK+#)G+*j1VPru#$k4}AE$+>;LO@%l?MSG8l`HrJ^h7p69u{%Y!+I}7C7I(fHlK-e_{=hfqOxSjI5_Z(LC5ao{VqI@<$3d}*@NRQ< z+^fH{aiwke?3-Zj`GpQj^wWoX*q(A!FwKV8P&F+$DG0;<4ohOp$8Q zHT8Km=R5vzCVRy2-J!U=3!soDG}peb686+Iwz|Jcj5|7&$M}^^v>!&n^c(XsK0GaC zZ|C-nxR`#+G(&1WF0!J;uKC%`>LNw*Scs_&UrM>E1s+pr4?YFvD1DO?3d)e_Ip+DE z?Gw-3JI5yggj9swTs^5~$!1jA;DO@P zj5>5|p9$QPqQ3va0uiZ_6==A|^#OV1K|b~?HikT^7;yM79PNrGZKWd>BF-2n`S_eJ3AC@Z5t$M!67yYcEx|(at^uB+DSBT^B z+4p`llAquo$d_{eirvB6ieQg5Kp5>qCLG|alb^Wo{5LHp2|QbH{u+*#(-GBeBVvre z6aqHlsHCm+8lS8mxO2Jj(i@1GXaL!)LM)PY+kgQ~*=<$s%w?`EwDA`imxm2%sh7Bf zHGX!vCJYOZaJeJk5WTlHu#EEvvr;+^@z?NGYXP5Za9(kX@W|M3g4jLewkR{R$t|M0 z6pi2(zBFvRhXEb9!N7@IJlE6FWMB2L%9oXkj%I};!x9Oh-$NTBDios4*^BdIpG5YG zt|stoIOFiQLcc$c;^I;k3Bf>Q9KU⋙tUAYBy3z>am!kP!%EkE*}q$;@-n$X|Xj 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 YQjW7PeZ;%>-_YK}d&YMwb)LNZKXYC-egFUf diff --git "a/05\345\210\230\350\203\241/230213-\350\256\244\350\257\206Node.js.md" "b/05\345\210\230\350\203\241/230213-\350\256\244\350\257\206Node.js.md" deleted file mode 100644 index 40b4645..0000000 --- "a/05\345\210\230\350\203\241/230213-\350\256\244\350\257\206Node.js.md" +++ /dev/null @@ -1,76 +0,0 @@ -Node.js是目前非常火热的技术,但是它的诞生经历却很奇特。 - -众所周知,在Netscape设计出JavaScript后的短短几个月,JavaScript事实上已经是前端开发的唯一标准。 - -后来,微软通过IE击败了Netscape后一统桌面,结果几年时间,浏览器毫无进步。(2001年推出的古老的IE 6到今天仍然有人在使用!) - -没有竞争就没有发展。微软认为IE6浏览器已经非常完善,几乎没有可改进之处,然后解散了IE6开发团队!而Google却认为支持现代Web应用的新一代浏览器才刚刚起步,尤其是浏览器负责运行JavaScript的引擎性能还可提升10倍。 - -先是Mozilla借助已壮烈牺牲的Netscape遗产在2002年推出了Firefox浏览器,紧接着Apple于2003年在开源的KHTML浏览器的基础上推出了WebKit内核的Safari浏览器,不过仅限于Mac平台。 - -随后,Google也开始创建自家的浏览器。他们也看中了WebKit内核,于是基于WebKit内核推出了Chrome浏览器。 - -Chrome浏览器是跨Windows和Mac平台的,并且,Google认为要运行现代Web应用,浏览器必须有一个性能非常强劲的JavaScript引擎,于是Google自己开发了一个高性能JavaScript引擎,名字叫V8,以BSD许可证开源。 - -现代浏览器大战让微软的IE浏览器远远地落后了,因为他们解散了最有经验、战斗力最强的浏览器团队!回过头再追赶却发现,支持HTML5的WebKit已经成为手机端的标准了,IE浏览器从此与主流移动端设备绝缘。 - -浏览器大战和Node有何关系? - -话说有个叫Ryan Dahl的歪果仁,他的工作是用C/C++写高性能Web服务。对于高性能,异步IO、事件驱动是基本原则,但是用C/C++写就太痛苦了。于是这位仁兄开始设想用高级语言开发Web服务。他评估了很多种高级语言,发现很多语言虽然同时提供了同步IO和异步IO,但是开发人员一旦用了同步IO,他们就再也懒得写异步IO了,所以,最终,Ryan瞄向了JavaScript。 - -因为JavaScript是单线程执行,根本不能进行同步IO操作,所以,JavaScript的这一“缺陷”导致了它只能使用异步IO。 - -选定了开发语言,还要有运行时引擎。这位仁兄曾考虑过自己写一个,不过明智地放弃了,因为V8就是开源的JavaScript引擎。让Google投资去优化V8,咱只负责改造一下拿来用,还不用付钱,这个买卖很划算。 - -于是在2009年,Ryan正式推出了基于JavaScript语言和V8引擎的开源Web服务器项目,命名为Node.js。虽然名字很土,但是,Node第一次把JavaScript带入到后端服务器开发,加上世界上已经有无数的JavaScript开发人员,所以Node一下子就火了起来。 - -在Node上运行的JavaScript相比其他后端开发语言有何优势? - -最大的优势是借助JavaScript天生的事件驱动机制加V8高性能引擎,使编写高性能Web服务轻而易举。 - -其次,JavaScript语言本身是完善的函数式语言,在前端开发时,开发人员往往写得比较随意,让人感觉JavaScript就是个“玩具语言”。但是,在Node环境下,通过模块化的JavaScript代码,加上函数式编程,并且无需考虑浏览器兼容性问题,直接使用最新的ECMAScript 6标准,可以完全满足工程上的需求。 - - 我还听说过io.js,这又是什么鬼? - -因为Node.js是开源项目,虽然由社区推动,但幕后一直由Joyent公司资助。由于一群开发者对Joyent公司的策略不满,于2014年从Node.js项目fork出了io.js项目,决定单独发展,但两者实际上是兼容的。 - -然而中国有句古话,叫做“分久必合,合久必分”。分家后没多久,Joyent公司表示要和解,于是,io.js项目又决定回归Node.js。 - -具体做法是将来io.js将首先添加新的特性,如果大家测试用得爽,就把新特性加入Node.js。io.js是“尝鲜版”,而Node.js是线上稳定版,相当于Fedora Linux和RHEL的关系。 - - -认识**node.js**和安装 -=== - -一 .认识**Node.js** ---- -### 1.什么是**Node.js** : - + 就是运行在服务端的**JavaScript** - - Node.js 是一个基于 *Chrome JavaScript* 运行时建立的一个平台 - Node.js 是一个事件驱动 I/O 服务端 **JavaScript** 环境,基于 **Google 的 V8 引擎,V8 引擎执行 Javascript 的速度非常快,性能非常好** - + 如果你在了解**Node.js**之前有JavaScript的基础,在使用时就会大大提高学习效率,这对于**后端**的服务部署**提高了性能** -### 2.**Node.js**的发展历程 : -1. **混沌期**:发布初期,创始人 *Ryan Dahl* 带著他的团队开发出了**以 Web 为中心的**“**Web.js**”,一切都是非常混乱,API大多都还除外研究阶段s -2. **成长期**:**Node.js** 的核心用户 *Isaac Z. Schlueter* 开发出奠定了 **Node.js** 如今地位的重要工具--**npm**。同时也为他后来成为 *Ryan* 的接班人的重要条件 -3. **高速期**:**connect, express, socket.io** 等库的出现吸引了一大波爱好者加入到 **Node.js** 开发者的阵营中来。**CoffeeScript** 的出现更是让不少 *Ruby* 和 *Python* 开发者找到了学习的理由。期间一大波以 **Node.js** 作为运行环境的 CLI 工具涌现,其中不乏有用于加速前端开发的优秀工具,如 **less, UglifyJS, browserify, grunt** 等等。**Node.js** 的发展势如破竹 -4. **更迭期**:经过了一大批一线工程师的探索实践后,**Node.js** 也开始进入了时代的更迭期,新模式代替旧模式,新技术代替旧技术,好实践代替旧实践。ES6 也开始出现在 **Node.js** 世界中 -5. **分裂期**:ES6 的发展越来越明显,v8 也对 ES6 中的部分特性实现了支持,如 **Generator** 等等,利用--**harmony**作为开启阀门。后来,诞生了 **Io.js** 分支,再后来也回到了 **Node.js** 主线上 -6. **飞速发展期**:随著 ES2015 的发展和最终定稿,一大批利用 ES2015 特性开发的新模块出现,如原 **express** 核心团队所开发的 **koa** - - > 作者:时见疏星 - > 链接:https://www.jianshu.com/p/5b9b245fcefa - > 来源:简书 - -### 3.**Node.js**的作用 : -+ **node.js**可以大大提升了开发的性能以及便利. 我们知道 **Apache + PHP** 以及 **Java** 的 **Servlet** 都可以用来开发动态网页,**Node.js** 的作用与他们类似,只不过是使用 **JavaScript** 来开发,它大大提升了开发的性能以及便利 -+ 使用 ***node*** 开发还可以使用配套的**npm**包管理工具 **NPM**是随同**NodeJS**一起安装的包管理工具,能解决**NodeJS**代码部署上的很多问题 - -二 .安装**Node.js** ---- -+ 点击这个**链接[Node.js下载安装](https://nodejs.org/en/download/)**就可以去**官网**找寻你需要的相应版本,进行下载安装 -![官网下载地址截图](./imgs/Node.js%E4%B8%8B%E8%BC%89/Node%E5%AE%98%E7%BD%91%E4%B8%8B%E8%BD%BD%E5%9C%B0%E5%9D%80.png) - + . 安装**Node.js**之后,可以在终端中打入```node -v```查看版号 - > node -v - > 18.02.5 - + 类似这个样子,查看版号,就知道是否是你需要的了 \ No newline at end of file diff --git "a/05\345\210\230\350\203\241/230214-node.\346\226\207\344\273\266\343\200\201\350\267\257\345\276\204\343\200\201\351\205\215\347\275\256\345\222\214\346\250\241\345\235\227.md" "b/05\345\210\230\350\203\241/230214-node.\346\226\207\344\273\266\343\200\201\350\267\257\345\276\204\343\200\201\351\205\215\347\275\256\345\222\214\346\250\241\345\235\227.md" deleted file mode 100644 index 7085f0e..0000000 --- "a/05\345\210\230\350\203\241/230214-node.\346\226\207\344\273\266\343\200\201\350\267\257\345\276\204\343\200\201\351\205\215\347\275\256\345\222\214\346\250\241\345\235\227.md" +++ /dev/null @@ -1,80 +0,0 @@ -**Node.js**文件的运行、路径、调试配置和模块 -=== - -一 .创建**Node.js**文件去运行: ---- -+ 当安装配置好 **Node** 环境,在终端中就可以 ``node 文件.js``让 **js** 文件运行起来了,正常它需要用浏览器去运行 - ->创建一个文件:**mian.js** -```js - 'use strict'; - - console.log('Hello World'); -``` -> ``node main.js`` -> 就会在终端中输出 -> ``Hello World`` -+ 如果只是在终端中输入**node**,就会切换交互模式,**js** 代码就会写一行执行一行 - - -二 .文件的路径: ---- -路径也分为本地路径和费本地路径俩大类 - -1. 本地路径 - + **相对路径**:***./文件*** 相对的,路径指定更灵活更加简洁 - + **绝对路径**:***盘符:\父级文件夹\子级文件夹\文件*** 不管如何都是指定一个完整的文件路径 -2. 非本地路径 - + **网络路径**:***协议://主机:端口/路径*** - - -三 .调试配置: ---- -我们在写**node.js文件**都会用到编辑器,一般使用 ***VS code*** 编辑器就可以了 -+ 在编写项目文件时,会把他们都放在一个文件内,我们可以在内部配置调试环境 - 1. 先创建一个 **.vscode** 隐藏文件 - 2. 在该文件下创建一个 **launch.json** 配置文件 - 3. 在里面编写配置 - ```json - { - "vesion":"0.2.0" - "configurations": [ - { - "type": "pwa-node", - "request": "launch", - "name": "Launch Program", - "program": "${file}" - }] - } - ``` - + 在某些编辑器里面你也可以这样去配置调试文件,可以对代码进行调试,并且可以断点去调节 - -四 .**Node.js**模块: ---- -什么是模块? -+ 相对于你的每一个的**js**文件都可以是模块,他可以像方法一样去使用,并且可以大大减少**代码量的维护和代码的重写率**,这样就可以提高代码的效率和利用率,方便了很多 - -> 1. 现在有一个**fun.js**这样的文件 -```js - 'use strict'; - - let fun = function(x,y){ - rutern x+y; - } - - module.exports = fun; -``` ->这样就可以暴露出**fun**()方法,就可以在别的**js**文件中引用了,这里函数、数组、对象等变量都可以像这样暴露出在别的文件中引用了 - -> 2. 现在有一个**Main.js**文件 -```js - 'use strict' - - let fun = require('./fun'); - let a = fun(5,6); - - console.log(a); -``` ->就会打印出 *11* ,正常都是在**require**(写入相对路径),否则Node会依次在**内置模块、全局模块和当前模块**下查找,并报错 - ->每个**js**文件就是一个模块,所以各自内部的变量名和函数名都互不冲突 \ No newline at end of file diff --git "a/05\345\210\230\350\203\241/230216-node.js\347\232\204\345\237\272\346\234\254\346\250\241\345\235\227\345\222\214\346\226\207\344\273\266\347\263\273\347\273\237\346\250\241\345\235\227.md" "b/05\345\210\230\350\203\241/230216-node.js\347\232\204\345\237\272\346\234\254\346\250\241\345\235\227\345\222\214\346\226\207\344\273\266\347\263\273\347\273\237\346\250\241\345\235\227.md" deleted file mode 100644 index d774a3f..0000000 --- "a/05\345\210\230\350\203\241/230216-node.js\347\232\204\345\237\272\346\234\254\346\250\241\345\235\227\345\222\214\346\226\207\344\273\266\347\263\273\347\273\237\346\250\241\345\235\227.md" +++ /dev/null @@ -1,227 +0,0 @@ -**Node.js**的基本模块和文件系统模块 -=== -因为**Node.js**是运行在服务区端的 *JavaScript* 环境,服务器程序和浏览器程序相比,最大的特点是没有浏览器的安全限制了,而且,服务器程序必须能接收网络请求,读写文件,处理二进制内容,所以,**Node.js**内置的常用模块就是为了实现基本的服务器功能。*这些模块在浏览器环境中是无法被执行的,因为它们的底层代码是用* ***C/C++*** 在**Node.js**运行环境中实现的 - -一 .基本模块: ---- -+ **Node.js**和 ***JavaScript*** (***window***) 全局变量一样也有自己的全局变量 (**global**) - ```js - > global.console - Console { - log: [Function: bound ], - info: [Function: bound ], - warn: [Function: bound ], - error: [Function: bound ], - dir: [Function: bound ], - time: [Function: bound ], - timeEnd: [Function: bound ], - trace: [Function: bound trace], - assert: [Function: bound ], - Console: [Function: Console] } - ``` -+ **process** 是**Node.js**提供的一个对象,可以获取当前**Node.js**进程,拿到一些有用的信息,它也属于**global**的属性 - ```js - > process === global.process; - true - > process.version; - 'v5.2.0' - > process.platform; - 'darwin' - > process.arch; - 'x64' - > process.cwd(); //返回当前工作目录 - '/Users/michael' - > process.chdir('/private/tmp'); // 切换当前工作目录 - undefined - > process.cwd(); - '/private/tmp' - ``` - 1. > *JavaScript*的程序是由事件驱动的单线程模式,所以**Node.js**也是一样,在 **process** 中可以调用 ``process.nextTick()``,将事件下一次响应 - ```js - process.nextTick(function(){ - console.log('我后一步'); - }); - console.log('我先一步'); - ``` - > 我先一步 - >我后一步 - - 2. > **Node.js**进程本身的事件就是由**process**对象来处理, 如果想要退出进程,我们可以响应 **exit** 事件,在程序退出时执行一个回调函数 - ```js - process.on('exit',function(code){ - console.log('about to exit with code' + code); - }); - ``` - - 3. > 判断**JavaScript**执行环境: - + 有很多JavaScript代码既能在浏览器中执行,也能在Node环境中执行,所以有时候要程序去判断当前执行的环境 - ```js - if(type(window) === 'undefined'){ - console.log('node.js'); - }else{ - console.log('browser'); - } - //或者是global,if返回的就是browser - ``` - -二 .文件系统模块: ---- -+ Node.js内置的**fs**模块就是文件系统模块,负责读写文件,和所有其他 ***JavaScript*** 模块不同,**fs**模块同时提供了异步和同步的方法 - -### **异步和同步**: -1. **异步**:因为 *JavaScript*是单线程模式,执行异步时,*JavaScript* 代码无需等待,而是在传回参数后,继续执行 *JavaScript* 代码 -2. **同步**:因为 *JavaScript*是单线程模式,执行同步时,*JavaScript* 代码需要等待 - -### 读文件: -1. **异步读文件**: - >按照 *JavaScript* 的标准,异步读取一个文本文件的代码如下: - ```js - 'use strict'; - - var fs = require('fs'); - - fs.readFile('sample.txt', 'utf-8', function (err, data) { - if (err) { - console.log(err); - } else { - console.log(data); - } - }); - ``` - + 请注意,**sample.txt**文件必须在当前目录下,且文件编码为**utf-8** - - > 异步读取时,传入的回调函数接收两个参数,当正常读取时,*err* 参数为 *null* ,*data* 参数为读取到的 *String* - > 当读取发生错误时,*err* 参数代表一个错误对象,*data*为*undefined* - > 这也是**Node.js**标准的回调函数:**第一个参数代表错误信息,第二个参数代表结果** - - > 由于 *err* 是否为 *null* 就是判断是否出错的标志,所以通常的判断逻辑总是: - ```js - if (err) { - // 出错了 - } else { - // 正常 - } - ``` -+ 如果我们要读取的文件不是文本文件,而是二进制文件,怎么办? - >下面的例子演示了如何读取一个图片文件: - ```js - 'use strict'; - - var fs = require('fs'); - - fs.readFile('sample.png', function (err, data) { - if (err) { - console.log(err); - } else { - console.log(data); - console.log(data.length + ' bytes'); - } - }); - ``` - > 当读取二进制文件时,不传入文件编码时,回调函数的 *data* 参数将返回一个**Buffer**对象。在**Node.js**中,**Buffer**对象就是***一个包含零个或任意个字节的数组***(*注意和Array不同*)。 - - > **Buffer**对象可以和 *String* 作转换,例如,把一个**Buffer**对象转换成 *String*: - ```js - // Buffer -> String - var text = data.toString('utf-8'); - console.log(text); - ``` - > 或者把一个 *String* 转换成**Buffer**: - ```js - // String -> Buffer - var buf = Buffer.from(text, 'utf-8'); - console.log(buf); - ``` -2. **同步读文件**: -+ 除了标准的异步读取模式外,**fs** 也提供相应的同步读取函数。同步读取的函数和异步函数相比,多了一个 ***Sync*** 后缀,并且不接收回调函数,函数直接返回结果 - - > 用**fs**模块同步读取一个文本文件的代码如下: - ```js - 'use strict'; - - var fs = require('fs'); - - var data = fs.readFileSync('sample.txt', 'utf-8'); - console.log(data); - ``` - > 可见,原异步调用的回调函数的*data*被函数直接返回,函数名需要改为**readFileSync**,其它参数不变 - - > 如果同步读取文件发生错误,则需要用``try...catch``捕获该错误: - ```js - try { - var data = fs.readFileSync('sample.txt', 'utf-8'); - console.log(data); - } catch (err) { - // 出错了 - } - ``` -### 写文件: -1. **异步写文件**: - >将数据写入文件是通过 ``fs.writeFile()`` 实现的: - ```js - 'use strict'; - - var fs = require('fs'); - - var data = 'Hello, Node.js'; - fs.writeFile('output.txt', data, function (err) { - if (err) { - console.log(err); - } else { - console.log('ok.'); - } - }); - ``` - > **writeFile**()的参数依次为***文件名、数据和回调函数***。如果传入的数据是 *String* ,默认按**UTF-8**编码写入文本文件,如果传入的参数是**Buffer**,则写入的是二进制文件。回调函数由于只关心成功与否,因此只需要一个 *err* 参数 - -2. **同步写文件**: - > 这个是将文件同步写入的 ``writeFileSync()``: - ```js - 'use strict'; - - var fs = require('fs'); - - var data = 'Hello, Node.js'; - fs.writeFileSync('output.txt', data); - ``` -#### ***异步还是同步?*** -+ 在fs模块中,提供同步方法是为了方便使用。那我们到底是应该用异步方法还是同步方法呢? - - 1. 由于**Node**环境执行的**JavaScript**代码是服务器端代码,所以,绝大部分需要在服务器运行期反复执行业务逻辑的代码,必须使用异步代码,否则,同步代码在执行时期,服务器将停止响应,因为**JavaScript**只有一个执行线程 - 2. 服务器启动时如果需要读取配置文件,或者结束时需要写入到状态文件时,可以使用同步代码,因为这些代码只在启动和结束时执行一次,不影响服务器正常运行时的异步执行。 - -### **stat**对象: -+ 如果我们要获取文件大小,创建时间等信息,可以使用``fs.stat()``,它返回一个**Stat**对象,能告诉我们文件或目录的详细信息: - ```js - 'use strict'; - - var fs = require('fs'); - - fs.stat('sample.txt', function (err, stat) { status - if (err) { - console.log(err); - } else { - // 是否是文件: - console.log('isFile: ' + stat.isFile()); - // 是否是目录: - console.log('isDirectory: ' + stat.isDirectory()); - if (stat.isFile()) { - // 文件大小: - console.log('size: ' + stat.size); - // 创建时间, Date对象: - console.log('birth time: ' + stat.birthtime); - // 修改时间, Date对象: - console.log('modified time: ' + stat.mtime); - } - } - }); - ``` - > 运行结果如下: - ```js - isFile: true - isDirectory: false - size: 181 - birth time: Fri Dec 11 2015 09:43:41 GMT+0800 (CST) - modified time: Fri Dec 11 2015 12:09:00 GMT+0800 (CST) - ``` - > stat()也有一个对应的同步函数statSync() \ No newline at end of file diff --git "a/05\345\210\230\350\203\241/230217-node.js\347\232\204http\346\250\241\345\235\227\345\222\214\347\275\221\347\253\231\351\203\250\347\275\262.md" "b/05\345\210\230\350\203\241/230217-node.js\347\232\204http\346\250\241\345\235\227\345\222\214\347\275\221\347\253\231\351\203\250\347\275\262.md" deleted file mode 100644 index 9e23e02..0000000 --- "a/05\345\210\230\350\203\241/230217-node.js\347\232\204http\346\250\241\345\235\227\345\222\214\347\275\221\347\253\231\351\203\250\347\275\262.md" +++ /dev/null @@ -1,79 +0,0 @@ -**Node.js**的***http***模块和网站的部署 -=== -**Node.js**的开发目的就是为了用***JavaScript***编写*web*服务器程序因为***JavaScript***实际上已经统治了浏览器端的脚本,其优势就是有世界上数量最多的前端开发人员。如果已经掌握了***JavaScript***前端开发,再学习一下如何将***JavaScript***应用在后端开发,就是名副其实的全栈了 - -一 .**Node.js**的***http***模块 ---- - -+ **HTTP**协议: - >要理解**Web**服务器程序的工作原理,首先,我们要对**HTTP**协议有基本的了解。如果你对**HTTP**协议不太熟悉,先看一看[**HTTP协议简介**](https://www.cnblogs.com/ranyonsue/p/5984001.html) - -+ **HTTP**服务器 - >要开发**HTTP服务器程序**,从头处理**TCP连接**,解析**HTTP**是不现实的。这些工作实际上已经由**Node.js**自带的 *http* 模块完成了。应用程序并不直接和**HTTP协议**打交道,而是操作 *http模块* 提供的 *request* 和 *response* 对象。 - 1. **request对象**封装了**HTTP请求**,我们调用 *request* 对象的属性和方法就可以拿到所有**HTTP请求的信息**; - 2. **response对象**封装了**HTTP响应**,我们操作 *response* 对象的方法,就可以把**HTTP响应返回给浏览器**。 -+ 创建**HTTP**应用服务器 - - > 用**Node.js**实现一个**HTTP**服务器程序非常简单。我们来实现一个最简单的Web程序,它对于所有请求,都返回Hello world!: - ```js - 'use strict'; - - let http = require('http'); - - let server = http.createServer(function(request,response){ - console.log(request.method + ': ' + request.url); - - response.writeHead(200, {'Content-Type': 'text/html'}); - response.end('

Hello world!

'); - - }).listen(5050); - ``` - ![http服务器效果](./imgs/http%E6%A8%A1%E5%9D%97/%E7%AE%80%E5%8D%95http%E6%9C%8D%E5%8A%A1%E5%99%A8%E7%9A%84%E6%95%88%E6%9E%9C.png) - - > 同时,在命令提示符窗口,可以看到程序打印的请求信息: - ```js - GET: / - GET: /favicon.ico - ``` - -二 .网站的部署 ---- -+ 网站多个部署的步骤: - 1. 先将域名解析,添加记录值;别忘了给自己的服务器加密 - 2. 打开终端: - > ssh root@**域名/IP地址** - 输入自己服务器的秘钥 - - > **apt update** *更新 Linux 的系统上的软件包列表* - > **apt upgrade -y** *命令将软件包升级到最新版本,-y 代表这个命令接下来的所有询问默认yes* - > **apt install nginx** *安装 **Nginx** ,接着的询问选 Y* - > **systemctl status nginx** *查看nginx的状态* - > **systemctl stop nginx** *关闭nginx服务* - > **systemctl start nginx** *开启nginx服务* - > **cd /var/www** *cd是用来切换目录的* - > **mkdir 文件1 文件2** *用来创建文件夹/文件或多个文件夹/文件* - > **rm** *可以用来删除 -i 文件/ -r 文件夹,-f 可以强制删除* - > **vim ~/.bashrc** *打开编辑器* - > **source ~/.bashrc** *执行刚修改的初始化文件,使之立即生效* - > **ll** *可以查看目录和文件了* - 3. 创建好文件,就可以配置了: - > **cd /etc/nginx/conf.d** - > **vim 域名.conf** *就会进入编辑器去配置了* - ```vim - server { - listen 80; - server_name 域名; - - location / { - root /var/www/文件夹名 - index 文件名.html - } - } - //保存退出 - ``` - 4. 远程上传或下载文件/文件夹: - > 打开终端,cd 切换到文件夹或文件所在位置 - > scp -r 文件夹 root@域名/IP地址:路径 远程地址路径 - > scp 文件 root@域名/IP地址:路径 远程地址路径 - - \ No newline at end of file diff --git "a/05\345\210\230\350\203\241/230220-node.js\347\232\204\346\226\207\344\273\266\346\234\215\345\212\241\345\231\250.md" "b/05\345\210\230\350\203\241/230220-node.js\347\232\204\346\226\207\344\273\266\346\234\215\345\212\241\345\231\250.md" deleted file mode 100644 index 0e07e44..0000000 --- "a/05\345\210\230\350\203\241/230220-node.js\347\232\204\346\226\207\344\273\266\346\234\215\345\212\241\345\231\250.md" +++ /dev/null @@ -1,99 +0,0 @@ -**Node.js**的文件服务器 -=== - -文件服务器 ---- -+ 扩展**Web程序**。我们可以设定一个目录,然后让**Web程序**变成一个文件服务器。要实现这一点,我们只需要解析 **request.url** 中的路径,然后在本地找到对应的文件,把文件内容发送出去就可以了 - - > 解析URL需要用到**Node.js**提供的 ***url* 模块** ,它使用起来非常简单,通过 **parse**() 将一个字符串解析为一个**Url对象**: - ```js - 'use strict'; - - let url = require('url'); - - console.log(url.parse('http://user:pass@host.com:8080/path/to/file?query=string#hash')); - ``` - - > 结果如下: - ```js - Url { - protocol: 'http:', - slashes: true, - auth: 'user:pass', - host: 'host.com:8080', - port: '8080', - hostname: 'host.com', - hash: '#hash', - search: '?query=string', - query: 'query=string', - pathname: '/path/to/file', - path: '/path/to/file?query=string', - href: 'http://user:pass@host.com:8080/path/to/file?query=string#hash' } - ``` -+ 处理本地文件目录需要使用**Node.js**提供的 ***path* 模块**,它可以方便地构造目录: - ```JS - 'use strict'; - - let path = require('path'); - // 解析当前目录: - let workDir = path.resolve('.'); // '/Users/michael' - // 组合完整的文件路径:当前目录+'pub'+'index.html': - let filePath = path.join(workDir, 'pub', 'index.html'); - // '/Users/michael/pub/index.html' - ``` - > 使用 **path模块** 可以正确处理操作系统相关的文件路径。在***Windows系统***下,返回的路径类似于**C:\Users\michael\static\index.html** - -+ 最后,我们实现一个文件服务器 **file_server.js**: - ```js - 'use strict'; - - let - fs = require('fs'), - url = require('url'), - path = require('path'), - http = require('http'); - - // 从命令行参数获取root目录,默认是当前目录: - let root = path.resolve(process.argv[2] || '.'); - console.log('Static root dir: ' + root); - - // 创建服务器: - let server = http.createServer(function (request, response) { - // 获得URL的path,类似 '/css/bootstrap.css': - let pathname = url.parse(request.url).pathname; - // 获得对应的本地文件路径,类似 '/srv/www/css/bootstrap.css': - let filepath = path.join(root, pathname); - // 获取文件状态: - fs.stat(filepath, function (err, stats) { - if (!err && stats.isFile()) { - // 没有出错并且文件存在: - console.log('200 ' + request.url); - // 发送200响应: - response.writeHead(200); - // 将文件流导向response: - fs.createReadStream(filepath).pipe(response); - } else { - // 出错了或者文件不存在: - console.log('404 ' + request.url); - // 发送404响应: - response.writeHead(404); - response.end('404 Not Found'); - } - }); - }); - - server.listen(8080); - console.log('Server is running at http://127.0.0.1:8080/'); - ``` - > 没有必要手动读取文件内容。由于**response对象**本身是一个 ***Writable Stream***,直接用 *pipe*() 方法就实现了自动读取文件内容并输出到**HTTP响应** - -+ 在命令行运行``node file_server.js /path/to/dir``把 **/path/to/dir** 改成你本地的一个有效的目录,然后在浏览器中输入``**http://localhost:8080/index.html**``: - - > 只要当前目录下存在文件 *index.html* ,服务器就可以把文件内容发送给浏览器。观察控制台输出: - ```js - 200 /index.html - 200 /css/uikit.min.css - 200 /js/jquery.min.js - 200 /fonts/fontawesome-webfont.woff2 - ``` - > 第一个请求是浏览器请求 *index.html* 页面,后续请求是 *浏览器解析HTML后发送的其它资源请求* \ No newline at end of file diff --git "a/05\345\210\230\350\203\241/230221-node.js\347\232\204Web\347\250\213\345\272\217\345\210\244\346\226\255\346\226\207\344\273\266\343\200\201\346\226\207\344\273\266\345\244\271.md" "b/05\345\210\230\350\203\241/230221-node.js\347\232\204Web\347\250\213\345\272\217\345\210\244\346\226\255\346\226\207\344\273\266\343\200\201\346\226\207\344\273\266\345\244\271.md" deleted file mode 100644 index defda69..0000000 --- "a/05\345\210\230\350\203\241/230221-node.js\347\232\204Web\347\250\213\345\272\217\345\210\244\346\226\255\346\226\207\344\273\266\343\200\201\346\226\207\344\273\266\345\244\271.md" +++ /dev/null @@ -1,70 +0,0 @@ -**Node.js**的web程序判断文件、文件夹 -=== - 当我们创建了一个文件服务器,可以更多的扩展,去实现的我们所需要的结果,像是 **开发web程序** 。因为用 **node.js** 去创建服务器,所以只用去掌握好***JavaScript语言***,因为拥有异步执行,也可以减少等待代码执行的时间,只用等代码的返回 - -创建服务器 ---- -+ 先用node.js去创建服务器 - > 创建一个main.js,在里面实现服务器: - ```js - 'use strict' - - let http = require('http'), - fs = require('fs'); - - let server = http.createServer( - function(request,response){ - response.writeHead(200,{"content-type":"text/html;charset=utf-8"}); - response.end('Hello world!'); - } - ).listen(8080); - - console.log('Server is runing at http://localhost:8080'); - ``` - > 这样就简单的创建好了一个 **web程序** ,接下来实现文件和文件夹的判断 - -判断文件和文件夹 ---- -+ 我们要引用到**fs 模块**,去使用 ***stat.isDirectory***() 方法去*判断文件夹和文件,我们来简单的用代码实现一下* - - > 创建一个文件夹 *Project*,再创建 *main.js* 和 *index.html* 文件,利用代码去实现判断: - ```js - 'use strict' - - let fs = require('fs'), - http = require('http'); - - let server = http.createServer( - function(request,response){ - - let path ='.'+request.url; - fs.stat(path,function(err,stats){ - if(!err){ - if(path == './'){ - response.writeHead(200,{"content-type":"text/html;charset=utf-8"}); - console.log(path); - response.end('可以判断文件或文件夹'); - } - else if(stats.isDirectory()==true){ - response.writeHead(200,{"content-type":"text/html;charset=utf-8"}); - console.log(path); - response.end('这是文件夹'); - } - else{ - response.writeHead(200,{"content-type":"text/html;charset=utf-8"}); - console.log(path); - response.end('这是文件'); - } - } - else{ - response.writeHead(404,{"content-type":"text/html;charset=utf-8"}); - console.log(path); - response.end('不存在该文件、文件夹或不是文件和文件夹'); - } - }); - } - ).listen(8080); - - console.log('http://localhost:8080'); - ``` - > 像这样开发了一个判断文件夹和文件的**web程序** \ No newline at end of file diff --git "a/05\345\210\230\350\203\241/230223-node.js\347\232\204Web\347\250\213\345\272\217\345\210\244\346\226\255\346\226\207\344\273\266\343\200\201\346\226\207\344\273\266\345\244\2712.md" "b/05\345\210\230\350\203\241/230223-node.js\347\232\204Web\347\250\213\345\272\217\345\210\244\346\226\255\346\226\207\344\273\266\343\200\201\346\226\207\344\273\266\345\244\2712.md" deleted file mode 100644 index 5cdc292..0000000 --- "a/05\345\210\230\350\203\241/230223-node.js\347\232\204Web\347\250\213\345\272\217\345\210\244\346\226\255\346\226\207\344\273\266\343\200\201\346\226\207\344\273\266\345\244\2712.md" +++ /dev/null @@ -1,44 +0,0 @@ -**Node.js**的web程序判断文件、文件夹 *2* -=== -用**Node.js**去做一个***文件、文件夹的判断程序***,在判断文件和文件夹时,我们往往也会将文件的内容也显示出来,当然也有多种方法去实现我们想要的效果,有*同步、promise和其他方法!* - -一 .判断文件、文件夹的同步方法: ---- -+ 同步方法的文件、文件夹判断,代码是比较简单的,但是在遇到一些大文件的读写时,就会大大增加文件读写成功后返回相应的速度,所以一般在处理一些小文件时,还是可以的的,代码量也不是很大 - > 创建一个main.js文件,去写我们的同步方法代码: - ```js - 'use strict'; - - let fs = require('fs'), - http = require('http'); - - function readFile(path){ - let result = 404; - - let stats = fs.statSync(path); - - if(stats.isDirectory()){ - let newPath = path+'index.html'; - let data = fs.readFileSync(newPath,'utf-8'); - result = data; - } - return result; - } - - let server = http.createServer(function(request,response){ - let path = '.'+request.url; - let datas = readFile(path); - response.end(datas); - }).listen('8080'); - - console.log("server is runing at http://localhost:8080"); - ``` - > 虽然是同步方法,代码很简单,但是功能性是很差的 - -二 .用promise方法判断文件、文件夹: ---- -+ 用promise方法,就可以使得异步执行成功,但会加大代码的编写难度,但是会提升响应速度 - -三 .用async/await去判断文件、文件夹: ---- -+ async/await这个东西其实是跟promise一样,并且不用像promise还要实例一下,用起来还是很方便的 \ No newline at end of file diff --git "a/05\345\210\230\350\203\241/230224-node.js\347\232\204Web\345\274\200\345\217\221\345\222\214Koa\346\241\206\346\236\266.md" "b/05\345\210\230\350\203\241/230224-node.js\347\232\204Web\345\274\200\345\217\221\345\222\214Koa\346\241\206\346\236\266.md" deleted file mode 100644 index e5b4d7b..0000000 --- "a/05\345\210\230\350\203\241/230224-node.js\347\232\204Web\345\274\200\345\217\221\345\222\214Koa\346\241\206\346\236\266.md" +++ /dev/null @@ -1,89 +0,0 @@ -**Node.js**——**web开发和Koa框架** -=== -在**web应用**开发之前,网页都是静态设计,以供浏览的,在有了 *JavaScript* 的语言之后,使得页面可以交互,随着发展,在网页上开发 **web应用**,也实现了起来,并且因为有了**node.js**可以在服务端运行的**JavaScript**的代码,*使得web应用兴起……,还有koa框架等等的开发,使得代码量更加简洁* - -一 .Web开发: ---- -### web应用的发展进程: -+ 最早的软件都是运行在大型机上,软件的使用通过“哑终端”登录到大型机上去运行软件,后面因为 *PC机* 的流行,软件主要运行在桌面上了,而数据库这样的软件运行在服务器端,这种 ***Client/Server模式*** 简称 **CS架构**,后来互联网的兴起,人们发现,**CS架构**不适合 **Web**,*最大的原因是 **Web应用程序** 的修改和升级非常迅速*,而 **CS架构** 需要每个客户端逐个升级桌面App,因此,***Browser/Server模式*** 开始流行,简称 **BS架构**,在 **BS架构** 下,客户端只需要浏览器,应用程序的逻辑和数据都存储在服务器端。浏览器只需要请求服务器,获取Web页面,并把Web页面展示给用户即可,服务器端升级后,客户端无需任何部署就可以使用到新的版本,*因此,**BS架构** 迅速流行起来* - + 现在大部分软件都可以用web提供服务,都被称为是web应用 - -### web应用经历的几个阶段: -1. **静态Web页面**:由文本编辑器直接编辑并生成静态的**HTML页面**,如果要修改Web页面的内容,就需要再次编辑**HTML源文件**,早期的互联网Web页面就是静态的; -2. **CGI**:由于静态Web页面无法与用户交互,比如用户填写了一个注册表单,静态Web页面就无法处理。要处理用户发送的动态数据,出现了 *Common Gateway Interface* ,简称**CGI,用C/C++编写**。 -3. **ASP/JSP/PHP**:由于Web应用特点是修改频繁,用 *C/C++* 这样的低级语言非常不适合Web开发,而脚本语言由于开发效率高,与HTML结合紧密,因此,迅速取代了**CGI模式**。ASP是微软推出的用VBScript脚本编程的Web开发技术,而JSP用Java来编写脚本,PHP本身则是开源的脚本语言。 -4. **MVC**:为了解决直接用脚本语言嵌入HTML导致的可维护性差的问题,Web应用也引入了 **Model-View-Controller的模式** ,来简化Web开发。ASP发展为ASP.Net,JSP和PHP也有一大堆MVC框架。 -5. **Node.js**: 由于Node.js把JavaScript引入了服务器端,因此,原来必须使用PHP/Java/C#/Python/Ruby等其他语言来开发服务器端程序,现在可以使用Node.js开发了! - -### Node.js开发web服务端: -> 用 **Node.js开发Web服务器端**,有几个显著的优势: -1. 后端语言也是 *JavaScript* ,以前掌握了 *前端JavaScript* 的开发人员,现在可以同时编写后端代码 -2. 前后端统一使用 *JavaScript* ,就没有切换语言的障碍了 -3. 速度快,非常快!这得益于**Node.js天生是异步的** - -+ 在**Node.js**诞生后的短短几年里,出现了无数种***Web框架、ORM框架、模版引擎、测试框架、自动化构建工具*** - > 1. 常见的Web框架包括:Express,Sails.js,koa,Meteor,DerbyJS,Total.js,restify…… - > 2. ORM框架比Web框架要少一些:Sequelize,ORM2,Bookshelf.js,Objection.js…… - > 3. 模版引擎PK:Jade,EJS,Swig,Nunjucks,doT.js…… - > 4. 测试框架包括:Mocha,Expresso,Unit.js,Karma…… - > 5. 构建工具有:Grunt,Gulp,Webpack…… - + 目前,在npm上已发布的开源Node.js模块数量超过了30万个 - -二 .Koa的介绍: ---- -+ koa是Express的下一代基于Node.js的web框架,目前有1.x和2.0两个版本。 - -### 历史 - -1. **Express是第一代最流行的web框架**,它对**Node.js**的 *http* 进行了封装,虽然**Express**的**API**很简单,但是它是基于**ES5的语法**,要实现异步代码,只有一个方法:回调。 - + 如果异步嵌套层次过多,代码写起来就非常难看,虽然可以用async这样的库来组织异步代码,但是用回调写异步实在是太痛苦了 - -2. 随着新版**Node.js**开始支持**ES6**,*Express的团队又基于ES6的 generator 重新编写了下一代 **web框架koa***。和**Express**相比,**koa 1.0**使用 *generator* 实现异步,代码看起来像同步的用 *generator* 实现异步比回调简单了不少,但是***generator的本意并不是异步***,**Promise**才是为异步设计的,但是**Promise**的写法比较复杂,为了简化异步代码,**ES7**(目前是草案,还没有发布)引入了新的**关键字 async 和 await** ,***可以轻松地把一个function变为异步模式,这是JavaScript未来标准的异步代码,非常简洁,并且易于使用***。 - -3. **koa**团队并没有止步于**koa 1.0**,他们非常超前地基于**ES7**开发了**koa2**,和***koa 1相比,koa2完全使用Promise并配合async来实现异步*** - - > **koa2**的代码看上去像这样: - ```js - app.use(async (ctx, next) => { - await next(); - var data = await doReadFile(); - ctx.response.type = 'text/plain'; - ctx.response.body = data; - }); - ``` - > 出于兼容性考虑,目前koa2仍支持generator的写法,但下一个版本将会去掉 - -三 .Koa的入门: ---- - -+ 创建**koa2工程** - - >首先,我们创建一个目录并作为工程目录用**VS Code打开**。然后,我们创建 *app.js*,输入以下代码: - ```js - // 导入koa,和koa 1.x不同,在koa2中,我们导入的是一个class,因此用大写的Koa表示: - const Koa = require('koa'); - // 创建一个Koa对象表示web app本身: - const app = new Koa(); - // 对于任何请求,app将调用该异步函数处理请求: - app.use(async (ctx, next) => { - await next(); - ctx.response.type = 'text/html'; - ctx.response.body = '

Hello, koa2!

'; - }); - // 在端口3000监听: - app.listen(3000); - console.log('app started at port 3000...'); - ``` - > 对于每一个**http请求**,**koa** 将调用我们传入的异步函数来处理: - ```js - async (ctx, next) => { - await next(); - // 设置response的Content-Type: - ctx.response.type = 'text/html'; - // 设置response的内容: - ctx.response.body = '

Hello, koa2!

'; - } - ``` - + 其中,参数ctx是由koa传入的封装了request和response的变量,我们可以通过它访问request和response,next是koa传入的将要处理的下一个异步函数 - - > 上面的异步函数中,我们首先用 **await next**() ;处理下一个异步函数,然后,设置 *response* 的Content-Type和内容。由async标记的函数称为异步函数,在异步函数中,可以用await调用另一个异步函数,这两个关键字将在ES7中引入。 \ No newline at end of file diff --git "a/05\345\210\230\350\203\241/230227-node.js-Koa\346\241\206\346\236\266\345\222\214URL\345\244\204\347\220\206.md" "b/05\345\210\230\350\203\241/230227-node.js-Koa\346\241\206\346\236\266\345\222\214URL\345\244\204\347\220\206.md" deleted file mode 100644 index 3443365..0000000 --- "a/05\345\210\230\350\203\241/230227-node.js-Koa\346\241\206\346\236\266\345\222\214URL\345\244\204\347\220\206.md" +++ /dev/null @@ -1,209 +0,0 @@ -**Node.js——Koa框架和URL处理** -=== -用**Node.js**的**包npm管理器去下载Koa**,使用**Koa框架**,用**Koa框架**去创建一个web应用,代码会简单很多,更加简洁方便,还有**URL的处理**,用到Koa-router,[**路由**](https://baike.baidu.com/item/%E8%B7%AF%E7%94%B1/363497)(这里指网路工程术语的路由),在对应的请求显示不同的响应,路由也是用到比较多的 -> 下载Koa之后,去引用Koa和实例化: -```js - //先去引用模块: - const Koa = require('Koa'); - //再去实例化: - let app = new Koa(); -``` -> 这样就是去引用到Koa和实例 - -一 .**Koa框架**: ---- -### 1 .下载Koa: -+ 下载Koa有俩种方法: - 1. **方法一**:直接用npm命令下载Koa,并且创建一个文件夹,并在改文件下去进行: - ```ps - c://……/文件夹> npm install Koa@2.0.0 - ``` - > npm会把koa2以及koa2依赖的所有包全部安装到当前文件夹目录下 - - 2. **方法二**:创建一个文件夹,在这个文件夹目录下创建一个package.json,这个文件描述了会用到哪些包,完整的文件内容如下: - ```json - { - "name": "hello-koa2", - "version": "1.0.0", - "description": "Hello Koa 2 example with async", - "main": "app.js", - "scripts": { - "start": "node app.js" - }, - "keywords": [ - "koa", - "async" - ], - "author": "Michael Liao", - "license": "Apache-2.0", - "repository": { - "type": "git", - "url": "https://github.com/michaelliao/learn-javascript.git" - }, - "dependencies": { - "koa": "2.0.0" - } - } - ``` - > 其中,*dependencies* 描述了我们的工程依赖的包以及版本号。其他字段均用来描述项目信息,可任意填写 - - > 然后,我们在文件夹目录下执行``npm install``就可以把所需包以及依赖包一次性全部装好: - ```ps - C:\...\hello-koa> npm install - ``` - >很显然,第二个方法更靠谱,因为我们只要在**package.json**正确设置了依赖,npm就会把所有用到的包都装好 - - + 注意,任何时候都可以直接删除整个**node_modules目录**,因为用``npm install``命令可以完整地重新下载所有依赖。并且,这个目录不应该被放入版本控制中 - > 紧接着,我们在**package.json**中添加依赖包: - ```json - "dependencies": { - "koa": "2.0.0" - } - ``` - > 然后使用``npm install``命令安装后,在VS Code中 **执行js文件** ,调试控制台输出如下: - ```ps - node --debug-brk=40645 --nolazy app.js - Debugger listening on port 40645 - app started at port 3000... - ``` - > 我们打开浏览器,输入http://localhost:3000,即可看到效果. - -### 2 .用npm启动程序 -+ 还可以直接用命令``node app.js``在命令行启动程序,或者用``npm start``启动。``npm start``命令会让npm执行定义在**package.json文件**中的**start对应命令**: - ```json - "scripts": { - "start": "node app.js", - "":"IMP" - } - ``` - -### 3 .koa middleware -+ 让我们再仔细看看koa的执行逻辑。核心代码是: - ```js - app.use(async (ctx, next) => { - await next(); - ctx.response.type = 'text/html'; - ctx.response.body = '

Hello, koa2!

'; - }); - ``` - > 每收到一个http请求,koa就会调用通过app.use()注册的async函数,并传入ctx和next参数 - -+ 我们可以对ctx操作,并设置返回内容。但是为什么要调用await next()?原因是koa把很多async函数组成一个处理链,每个async函数都可以做一些自己的事情,然后用await next()来调用下一个async函数。我们把每个async函数称为middleware,这些middleware可以组合起来,完成很多有用的功能 - - > 例如,可以用以下3个middleware组成处理链,依次打印日志,记录处理时间,输出HTML: - ```js - app.use(async (ctx, next) => { - console.log(`${ctx.request.method} ${ctx.request.url}`); // 打印URL - await next(); // 调用下一个middleware - }); - - app.use(async (ctx, next) => { - const start = new Date().getTime(); // 当前时间 - await next(); // 调用下一个middleware - const ms = new Date().getTime() - start; // 耗费时间 - console.log(`Time: ${ms}ms`); // 打印耗费时间 - }); - - app.use(async (ctx, next) => { - await next(); - ctx.response.type = 'text/html'; - ctx.response.body = '

Hello, koa2!

'; - }); - ``` - - + middleware的顺序很重要,也就是调用app.use()的顺序决定了middleware的顺序 - -+ 此外,如果一个middleware没有调用await next(),会怎么办?答案是后续的middleware将不再执行了。这种情况也很常见,例如,一个检测用户权限的middleware可以决定是否继续处理请求,还是直接返回403错误: - ```js - app.use(async (ctx, next) => { - if (await checkUserPermission(ctx)) { - await next(); - } else { - ctx.response.status = 403; - } - }); - ``` - > 理解了middleware,我们就已经会用koa了! - - + 最后注意ctx对象有一些简写的方法,例如ctx.url相当于ctx.request.url,ctx.type相当于ctx.response.type - -二 .URL处理 ---- - -+ 在我们处理http请求一律返回相同的HTML,这样虽然非常简单,但是用浏览器一测,随便输入任何URL都会返回相同的网页 - - >正常情况下,我们应该对不同的URL调用不同的处理函数,这样才能返回不同的结果。例如像这样写: - ```js - app.use(async (ctx, next) => { - if (ctx.request.path === '/') { - ctx.response.body = 'index page'; - } else { - await next(); - } - }); - app.use(async (ctx, next) => { - if (ctx.request.path === '/test') { - ctx.response.body = 'TEST page'; - } else { - await next(); - } - }); - app.use(async (ctx, next) => { - if (ctx.request.path === '/error') { - ctx.response.body = 'ERROR page'; - } else { - await next(); - } - }); - ``` - > 应该有一个能集中**处理URL的middleware**,它根据不同的URL调用不同的处理函数,这样,我们才能专心为每个URL编写处理函数 - -### **koa-router**: -+ 为了**处理URL**,我们需要引入**koa-router**这个 *middleware*,让它负责**处理URL映射** - - >先在**package.json**中添加依赖项: - ```json - "koa-router": "7.0.0" - ``` -+ 然后用``npm install``安装 - - >接下来,使用koa-router来处理URL: - ```js - const Koa = require('koa'); - // 注意require('koa-router')返回的是函数: - const router = require('koa-router')(); - const app = new Koa(); - // log request URL: - app.use(async (ctx, next) => { - console.log(`Process ${ctx.request.method} ${ctx.request.url}...`); - await next(); - }); - // add url-route: - router.get('/hello/:name', async (ctx, next) => { - var name = ctx.params.name; - ctx.response.body = `

Hello, ${name}!

`; - }); - - router.get('/', async (ctx, next) => { - ctx.response.body = '

Index

'; - }); - // add router middleware: - app.use(router.routes()); - app.listen(3000); - console.log('app started at port 3000...'); - ``` - > 注意导入**koa-router**的语句 ***最后的()是函数调用***: - ```js - const router = require('koa-router')(); - ``` - > 相当于: - ```js - const fn_router = require('koa-router'); - const router = fn_router(); - ``` -+ 然后,我们使用``router.get('/path', async fn)``来注册一个**GET请求**。可以在请求路径中使用带变量的``/hello/:name``,变量可以通过**ctx.params.name访问** - > 再运行,我们就可以测试不同的URL: - ```JS - 输入首页:http://localhost:3000/ - 输入:http://localhost:3000/hello/koa - ``` \ No newline at end of file diff --git "a/05\345\210\230\350\203\241/230228-node.js-URL\345\244\204\347\220\206\347\232\204post\350\257\267\346\261\202\345\222\214\351\207\215\346\236\204.md" "b/05\345\210\230\350\203\241/230228-node.js-URL\345\244\204\347\220\206\347\232\204post\350\257\267\346\261\202\345\222\214\351\207\215\346\236\204.md" deleted file mode 100644 index 96d8698..0000000 --- "a/05\345\210\230\350\203\241/230228-node.js-URL\345\244\204\347\220\206\347\232\204post\350\257\267\346\261\202\345\222\214\351\207\215\346\236\204.md" +++ /dev/null @@ -1,126 +0,0 @@ -**Node.js**——**URL处理的post请求和重构** -=== -在用**Node.js创建了web应用服务器**后,会有一些对 *URL的处理*,有**get、post、delete、put**等一类的请求,跟路由都有关系,但在一个比较完善的系统结构中,这种路由请求会很多,所以用重构,将他们去分类统一放在各自的文件夹中,这样更便于去写处理的代码,只用调用出来就好了,也可以让逻辑关系更加清晰 - -一 .URL处理的post请求: ---- - -用``router.get('/path', async fn)``处理的是**get请求**,如果要**处理post请求**,可以用``router.post('/path', async fn)`` - -+ 用**post请求处理URL**时,我们会遇到一个问题:*post请求通常会发送一个表单,或者JSON* ,它作为**request的body**发送,但无论是**Node.js**提供的***原始request对象,还是koa提供的request对象,都不提供解析request的body的功能*** - - + 所以,我们又需要引入另一个 *middleware* 来解析原始**request请求**,然后,把解析后的参数,绑定到`ctx.request.body`中 - -+ **koa-bodyparser**就是用来干这个活的 - - > 我们在**package.json**中添加依赖项: - ```json - "koa-bodyparser": "3.2.0" - ``` - > 然后使用`npm install`安装 - - > 下面修改js文件,引入koa-bodyparser: - ```js - const bodyParser = require('koa-bodyparser'); - // 在合适的位置加上: - app.use(bodyParser()); - ``` - + 由于 *middleware* 的顺序很重要,这个**koa-bodyparser**必须在**router**之前被 *注册到app对象上* - -> 现在我们就可以**处理post请求**了。写一个简单的登录表单: -```js - router.get('/', async (ctx, next) => { - ctx.response.body = `

Index

-
-

Name:

-

Password:

-

-
`; - }); - - router.post('/signin', async (ctx, next) => { - var - name = ctx.request.body.name || '', - password = ctx.request.body.password || ''; - console.log(`signin with name: ${name}, password: ${password}`); - if (name === 'koa' && password === '12345') { - ctx.response.body = `

Welcome, ${name}!

`; - } else { - ctx.response.body = `

Login failed!

-

Try again

`; - } - }); -``` -+ 注意到我们用`var name = ctx.request.body.name || ''`拿到表单的**name字段**,如果该字段不存在,默认值设置为'' - - > 类似的,**put、delete、head请求**也可以由**router处理**。 - -二 .重构项目 ---- -现在,我们已经可以处理不同的URL了,但是看看js文件,总觉得还是有点不对劲 - -+ 所有的URL处理函数都放到js文件里显得很乱,而且,每加一个URL,就需要修改js文件,随着URL越来越多,js文件就会越来越长 - - >如果能把URL处理函数集中到某个js文件,或者某几个js文件中就好了,然后让app.js自动导入所有处理URL的函数。这样,代码一分离,逻辑就显得清楚了,最好是这样: - ``` - url2-koa/ - | - +- .vscode/ - | | - | +- launch.json <-- VSCode 配置文件 - | - +- controllers/ - | | - | +- login.js <-- 处理login相关URL - | | - | +- users.js <-- 处理用户管理相关URL - | - +- app.js <-- 使用koa的js - | - +- package.json <-- 项目描述文件 - | - +- node_modules/ <-- npm安装的所有依赖包 - ``` - - > 我们先在controllers目录下编写index.js: - ```js - var fn_index = async (ctx, next) => { - ctx.response.body = `

Index

-
-

Name:

-

Password:

-

-
`; - }; - - var fn_signin = async (ctx, next) => { - var - name = ctx.request.body.name || '', - password = ctx.request.body.password || ''; - console.log(`signin with name: ${name}, password: ${password}`); - if (name === 'koa' && password === '12345') { - ctx.response.body = `

Welcome, ${name}!

`; - } else { - ctx.response.body = `

Login failed!

-

Try again

`; - } - }; - - module.exports = { - 'GET /': fn_index, - 'POST /signin': fn_signin - }; - ``` - > 这个index.js通过module.exports把两个URL处理函数暴露出来。 - - > 类似的,hello.js把一个URL处理函数暴露出来: - ```js - var fn_hello = async (ctx, next) => { - var name = ctx.params.name; - ctx.response.body = `

Hello, ${name}!

`; - }; - - module.exports = { - 'GET /hello/:name': fn_hello - }; - ``` \ No newline at end of file diff --git "a/05\345\210\230\350\203\241/230302-node.js-URL\345\244\204\347\220\206\350\257\267\346\261\202\345\222\214\351\241\271\347\233\256\351\207\215\346\236\204.md" "b/05\345\210\230\350\203\241/230302-node.js-URL\345\244\204\347\220\206\350\257\267\346\261\202\345\222\214\351\241\271\347\233\256\351\207\215\346\236\204.md" deleted file mode 100644 index f48d79e..0000000 --- "a/05\345\210\230\350\203\241/230302-node.js-URL\345\244\204\347\220\206\350\257\267\346\261\202\345\222\214\351\241\271\347\233\256\351\207\215\346\236\204.md" +++ /dev/null @@ -1,140 +0,0 @@ -**Node.js**——**URL处理请求和项目重构** -=== -在一个**Node.js的web应用项目**里,**URL的处理**可以说是非常繁琐的,所以要去 *进行包装重构,这样能让代码更加简洁,逻辑更加清晰明了* ,创建一个文件夹去将这些不同页面访问URL处理进行封装成模块,只用去引用 - -一 .url的处理请求: ---- - -### 1 .http的请求方法: -+ URL处理,http的请求方法: - -|**方法**:|描述:| -|---|---| -|**GET**|请求指定的页面信息,并返回实体主体| -|**HEAD**|类似于 GET 请求,只不过返回的响应中没有具体的内容,用于获取报头| -|**POST**|向指定资源提交数据进行处理请求(例如提交表单或者上传文件)。数据被包含在请求体中。POST 请求可能会导致新的资源的建立和/或已有资源的修改| -|**PUT**|从客户端向服务器传送的数据取代指定的文档的内容| -|**DELETE** |请求服务器删除指定的页面| -|**CONNECT**|HTTP/1.1 协议中预留给能够将连接改为管道方式的代理服务器| -|**OPTIONS**|允许客户端查看服务器的性能| -|**TRACE**|回显服务器收到的请求,主要用于测试或诊断| -|**PATCH**|是对 PUT 方法的补充,用来对已知资源进行局部更新| - - > 常用的请求方法有**get、post、put、delete**这些请求,会常写这些请求的处理方法和模块 - -### 2 .对URL处理引用的模块: -+ 用Koa框架在对URL路由处理时,会引用到很多的模块 - > 会引用到的模块代码: - ```js - const - Koa = require('Koa'), - router = require('kao-router')(), - bodyParser = require('koa-bodyparser'); - fs = require('fs'); - ``` - > 这些模块的 - -二 .项目模块扫描重构: ---- -+ 我们把项目重构,很多模块都存放在一个文件夹下,包装好,所以在调用的时候,将他们都扫描一遍,不用再去一个一个的调用,也是为了减少代码的写入量 - - > 现在,我们修改**主程序js文件**让它自动**扫描controllers目录**,*找到所有js文件,导入,然后注册每个URL* : - ```js - // 先导入fs模块,然后用readdirSync列出文件 - // 这里可以用sync是因为启动时只运行一次,不存在性能问题: - var files = fs.readdirSync(__dirname + '/controllers'); - // 过滤出.js文件: - var js_files = files.filter((f)=>{ - return f.endsWith('.js'); - }); - // 处理每个js文件: - for (var f of js_files) { - console.log(`process controller: ${f}...`); - // 导入js文件: - let mapping = require(__dirname + '/controllers/' + f); - for (var url in mapping) { - if (url.startsWith('GET ')) { - // 如果url类似"GET xxx": - var path = url.substring(4); - router.get(path, mapping[url]); - console.log(`register URL mapping: GET ${path}`); - } else if (url.startsWith('POST ')) { - // 如果url类似"POST xxx": - var path = url.substring(5); - router.post(path, mapping[url]); - console.log(`register URL mapping: POST ${path}`); - } else { - // 无效的URL: - console.log(`invalid URL: ${url}`); - } - } - } - ``` - > 如果上面的大段代码看起来还是有点费劲,那就把它拆成更小单元的函数: - ```js - function addMapping(router, mapping) { - for (var url in mapping) { - if (url.startsWith('GET ')) { - var path = url.substring(4); - router.get(path, mapping[url]); - console.log(`register URL mapping: GET ${path}`); - } else if (url.startsWith('POST ')) { - var path = url.substring(5); - router.post(path, mapping[url]); - console.log(`register URL mapping: POST ${path}`); - } else { - console.log(`invalid URL: ${url}`); - } - } - } - - function addControllers(router) { - var files = fs.readdirSync(__dirname + '/controllers'); - var js_files = files.filter((f) => { - return f.endsWith('.js'); - }); - - for (var f of js_files) { - console.log(`process controller: ${f}...`); - let mapping = require(__dirname + '/controllers/' + f); - addMapping(router, mapping); - } - } - - addControllers(router); - ``` - + 确保每个函数功能非常简单,一眼能看明白,是代码可维护的关键 - -三 .设置 Middleware ---- -将一些在**controllers目录和route要引入的模块封装好在一个模块中**,这样去减少主程序js文件中的繁琐代码 -+ 最后把扫描**controllers目录**和**创建router的代码**从 *入口js文件中提取出来,作为一个简单的middleware使用* ,命名为**index.js**存放在**controllers目录**: - ```js - const fs = require('fs'); - - function addMapping(router, mapping) { - ... - } - function addControllers(router, dir) { - ... - } - module.exports = function (dir) { - let - // 如果不传参数,扫描目录默认为'controllers' - controllers_dir = dir || 'controllers', - router = require('koa-router')(); - addControllers(router, controllers_dir); - return router.routes(); - }; - ``` - > 这样一来,我们在**主程序的js文件代码**就又简化了: - ```js - ... - // 导入controller middleware: - const controller = require('./controller'); - ... - // 使用middleware: - app.use(controller()); - ... - ``` -+ 经过重新整理后的项目具备非常好的模块化,所有**处理URL**的***函数按功能组存放在controllers目录,今后我们也只需要不断往这个目录下加东西就可以了,主程序的js文件保持不变*** \ No newline at end of file diff --git "a/05\345\210\230\350\203\241/230303-node.js-\351\207\215\346\236\204\351\241\271\347\233\2562.md" "b/05\345\210\230\350\203\241/230303-node.js-\351\207\215\346\236\204\351\241\271\347\233\2562.md" deleted file mode 100644 index dc6eb56..0000000 --- "a/05\345\210\230\350\203\241/230303-node.js-\351\207\215\346\236\204\351\241\271\347\233\2562.md" +++ /dev/null @@ -1,17 +0,0 @@ -# 项目重构 -在一个Node.js的web应用项目里,URL的处理可以说是非常繁琐的,所以要去 进行包装重构,这样能让代码更加简洁,逻辑更加清晰明了 ,创建一个文件夹去将这些不同页面访问URL处理进行封装成模块,只用去引用 -## 设置 Middleware -将一些在controllers目录和route要引入的模块封装好在一个模块中,这样去减少主程序js文件中的繁琐代码 - -最后把扫描controllers目录和创建router的代码从 入口js文件中提取出来,作为一个简单的middleware使用 ,命名为index.js存放在controllers目录: - -这样一来,我们在主程序的js文件代码就又简化了: - -... -// 导入controller middleware: -const controller = require('./controller'); -... -// 使用middleware: -app.use(controller()); -... -经过重新整理后的项目具备非常好的模块化,所有处理URL的函数按功能组存放在controllers目录,今后我们也只需要不断往这个目录下加东西就可以了,主程序的js文件保持不变 \ No newline at end of file diff --git "a/05\345\210\230\350\203\241/230306-node.js-\346\241\206\346\236\266.md" "b/05\345\210\230\350\203\241/230306-node.js-\346\241\206\346\236\266.md" deleted file mode 100644 index c521929..0000000 --- "a/05\345\210\230\350\203\241/230306-node.js-\346\241\206\346\236\266.md" +++ /dev/null @@ -1,45 +0,0 @@ -### 框架 - - -小步骤 -```js -// 引入Koa模块 -const Koa = require('koa'); -//导入控制器模块,并且处理post请求参数的反序列化,注册路由 -const controller = require('./controllers') -//创建实例 -let app = new Koa(); -//调用控制器模块 -controller(app); -//监听运行 -let port = 3000; -app.listen(port); -//打印下服务地址 -console.log(`http://127.0.0.1:${post}`); - -``` - -注册路由 - -```js -const bodyparser = require('koa-bodyparser') - -const { findFiles, registiR } = require('../utils/help') - -function initRouter(app) { - //找出所有的路由文件 - let contFiles = findFiles(); - //注册所有定义好的路由 - - registiR(contFiles); - //需要引入bodyParser模块的帮助,使post请求获取参数方便 - app.use(bodyparser()); - - //注册路由中间件 - app.use(router.touters()); - -} -module.exports = initRouter; - - -``` \ No newline at end of file diff --git "a/05\345\210\230\350\203\241/230307-node.js-\346\250\241\346\235\277\345\274\225\346\223\216.md" "b/05\345\210\230\350\203\241/230307-node.js-\346\250\241\346\235\277\345\274\225\346\223\216.md" deleted file mode 100644 index 3f590d7..0000000 --- "a/05\345\210\230\350\203\241/230307-node.js-\346\250\241\346\235\277\345\274\225\346\223\216.md" +++ /dev/null @@ -1,24 +0,0 @@ -## Nunjucks -变量env就表示Nunjucks模板引擎对象,它有一个render(view, model)方法,正好传入view和model两个参数,并返回字符串。 - -创建env需要的参数可以查看文档获知。我们用opts.noCache || false这样的代码给每个参数加上默认值,最后使用new nunjucks.FileSystemLoader('views')创建一个文件系统加载器,从views目录读取模板。 - - -# 练习 -```js -'use strict' -const nunjucks = require('nunjucks'); - -let env = nunjucks.configure('views',{ - autoescape:true,watch:true,noCache:true -}); -function temp(){ - return async(ctx,next)=>{ - ctx.render =function(view,data){ - ctx.body = env.render(view,data); - } - await next; - } -} -module.exports=temp; -``` \ No newline at end of file diff --git "a/05\345\210\230\350\203\241/230309-node.js-MVC.md" "b/05\345\210\230\350\203\241/230309-node.js-MVC.md" deleted file mode 100644 index db88f9a..0000000 --- "a/05\345\210\230\350\203\241/230309-node.js-MVC.md" +++ /dev/null @@ -1,36 +0,0 @@ -## 封装静态资源 -如何上传 - - -# 练习 -```js -'use strict'; - -//安装mine使用css文件 -const mime = require('mime'); -//异步文件的引用 -const fs = require('fs/promises'); - -function statics(){ - return async (ctx, next) => { - let tmpPath = ctx.request.path; - console.log(tmpPath); - let fullPath = __dirname + tmpPath; - // await next(); - if (tmpPath.startsWith('/statics')) { - //判断这个文件是否存在,存在则读取,返回给响应,否则返回404 - let stat = await fs.stat(fullPath); - if (stat.isFile()) { - ctx.type = mime.getType(fullPath); - ctx.body = await fs.readFile(fullPath); - } else { - ctx.response.status = 404; - } - } else { - await next(); - } - } -} - -module.exports=statics; -``` \ No newline at end of file diff --git "a/05\345\210\230\350\203\241/230310-node.js-\346\225\260\346\215\256\345\272\223\350\277\236\346\216\245.md" "b/05\345\210\230\350\203\241/230310-node.js-\346\225\260\346\215\256\345\272\223\350\277\236\346\216\245.md" deleted file mode 100644 index 6028eae..0000000 --- "a/05\345\210\230\350\203\241/230310-node.js-\346\225\260\346\215\256\345\272\223\350\277\236\346\216\245.md" +++ /dev/null @@ -1,69 +0,0 @@ -# 数据库的连接 - -引入sequelize这个orm工具 - - - -```js -'use strict'; -//引人Sequelize这个ORM工具 -const { Sequelize, DataTypes } = require('sequelize'); -const config = require('./config'); - -let db = new Sequelize(config.database,config.uid,config.pwd,{ - host :config.host ; - dialect:'mysql' -}) - -let User = db.define('user', { - id: { - type: DataTypes.STRING, - primaryKey: true - }, - username: DataTypes.STRING, - password: DataTypes.STRING, - avatar: DataTypes.STRING -}); -// 定义模型 -let Role=db.define('roles',{ - id: { - type: DataTypes.STRING, - primaryKey: true - }, - roleName:DataTypes.STRING -}) -// 将模型同步到数据库,即根据模型定义,生成相应的数据库表 -db.sync({force:true}).then(() => { - // 将数据插入到数据表 - User.create({ - id: '1', - username: 'admin', - password: '113', - avatar: '888' - }); - - Role.create({ - id:'3', - roleName:'super admin' - }) - -}); - - - - -let app = new Koa(); - -// 处理静态资源的逻辑:判断path是不是包含statics,是则表示都是静态资源,否则放行await next(); -app.use(statics()); - -let config = { - database: 'db2', - host: 'localhost', - uid: 'root', - pwd: 'root', - port: 5432 -} - -module.exports = config; -``` \ No newline at end of file diff --git "a/05\345\210\230\350\203\241/imgs/Node.js\344\270\213\350\274\211/Node\345\256\230\347\275\221\344\270\213\350\275\275\345\234\260\345\235\200.png" "b/05\345\210\230\350\203\241/imgs/Node.js\344\270\213\350\274\211/Node\345\256\230\347\275\221\344\270\213\350\275\275\345\234\260\345\235\200.png" deleted file mode 100644 index f18161ea3f01d26fe97dd5f25cd6627e67415420..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 151142 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 KrBtnG6aIfNuq9#u diff --git "a/05\345\210\230\350\203\241/imgs/http\346\250\241\345\235\227/\347\256\200\345\215\225http\346\234\215\345\212\241\345\231\250\347\232\204\346\225\210\346\236\234.png" "b/05\345\210\230\350\203\241/imgs/http\346\250\241\345\235\227/\347\256\200\345\215\225http\346\234\215\345\212\241\345\231\250\347\232\204\346\225\210\346\236\234.png" deleted file mode 100644 index a55aa0be546e034651456afc604c9cb369a28bab..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 9579 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 Date: Tue, 14 Mar 2023 11:00:53 +0800 Subject: [PATCH 05/20] c --- ...72\346\234\254\346\246\202\345\277\265.md" | 23 ----- ...04\347\254\254\344\272\214\350\257\276.md" | 30 ------- ...72\346\234\254\346\250\241\345\235\227.md" | 51 ----------- .../20230217-fs.md" | 69 --------------- ...tp\346\234\215\345\212\241\345\231\250.md" | 26 ------ ...01\344\270\252\347\275\221\347\253\231.md" | 17 ---- ...66\346\234\215\345\212\241\345\231\250.md" | 20 ----- ...11\347\247\215\346\226\271\346\263\225.md" | 65 -------------- .../20230224-koa.md" | 13 --- ...346\255\245\350\256\244\350\257\206Koa.md" | 27 ------ .../20230228-\350\267\257\347\224\261.md" | 42 --------- ...47\220\206post\350\257\267\346\261\202.md" | 17 ---- .../20230303-\351\207\215\346\236\204.md" | 59 ------------- ...05\345\206\215\345\260\201\350\243\205.md" | 85 ------------------- ...215\225\346\270\262\346\237\223nujucks.md" | 14 --- ...08-koa\347\273\223\345\220\210nunjucks.md" | 24 ------ ...31\346\200\201\345\233\276\347\211\207.md" | 35 -------- 17 files changed, 617 deletions(-) delete mode 100644 "06\351\231\210\345\230\211\351\221\253/20230213-js\347\232\204\345\237\272\346\234\254\346\246\202\345\277\265.md" delete mode 100644 "06\351\231\210\345\230\211\351\221\253/20230214-js\347\232\204\347\254\254\344\272\214\350\257\276.md" delete mode 100644 "06\351\231\210\345\230\211\351\221\253/20230216-\345\237\272\346\234\254\346\250\241\345\235\227.md" delete mode 100644 "06\351\231\210\345\230\211\351\221\253/20230217-fs.md" delete mode 100644 "06\351\231\210\345\230\211\351\221\253/20230217-http\346\234\215\345\212\241\345\231\250.md" delete mode 100644 "06\351\231\210\345\230\211\351\221\253/20230217-\345\215\201\344\270\252\347\275\221\347\253\231.md" delete mode 100644 "06\351\231\210\345\230\211\351\221\253/20230220\345\237\272\346\234\254\346\226\207\344\273\266\346\234\215\345\212\241\345\231\250.md" delete mode 100644 "06\351\231\210\345\230\211\351\221\253/20230223-\344\270\211\347\247\215\346\226\271\346\263\225.md" delete mode 100644 "06\351\231\210\345\230\211\351\221\253/20230224-koa.md" delete mode 100644 "06\351\231\210\345\230\211\351\221\253/20230227-\345\210\235\346\255\245\350\256\244\350\257\206Koa.md" delete mode 100644 "06\351\231\210\345\230\211\351\221\253/20230228-\350\267\257\347\224\261.md" delete mode 100644 "06\351\231\210\345\230\211\351\221\253/20230302-\345\244\204\347\220\206post\350\257\267\346\261\202.md" delete mode 100644 "06\351\231\210\345\230\211\351\221\253/20230303-\351\207\215\346\236\204.md" delete mode 100644 "06\351\231\210\345\230\211\351\221\253/20230306-\345\260\201\350\243\205\345\206\215\345\260\201\350\243\205.md" delete mode 100644 "06\351\231\210\345\230\211\351\221\253/20230307-\347\256\200\345\215\225\346\270\262\346\237\223nujucks.md" delete mode 100644 "06\351\231\210\345\230\211\351\221\253/20230308-koa\347\273\223\345\220\210nunjucks.md" delete mode 100644 "06\351\231\210\345\230\211\351\221\253/20230310-\345\244\204\347\220\206\351\235\231\346\200\201\345\233\276\347\211\207.md" diff --git "a/06\351\231\210\345\230\211\351\221\253/20230213-js\347\232\204\345\237\272\346\234\254\346\246\202\345\277\265.md" "b/06\351\231\210\345\230\211\351\221\253/20230213-js\347\232\204\345\237\272\346\234\254\346\246\202\345\277\265.md" deleted file mode 100644 index 51d2771..0000000 --- "a/06\351\231\210\345\230\211\351\221\253/20230213-js\347\232\204\345\237\272\346\234\254\346\246\202\345\277\265.md" +++ /dev/null @@ -1,23 +0,0 @@ -# js的基本概念 -## node.js简介 -``` -node.js是一个js运行环境,是一个可以快速构建网络服务及应用的平台,是用js语言构建的服务平台,可用于后端建立服务器 -``` -## node.js与js的区别 -``` -node.js 主要应用后端 ‘平台运行环境’(一个基于Chrome JavaScript运行时建立的平台,它是对Google V8引擎进行了封装的运行环境) -简单的说node.js就是把浏览器的解释器封装起来作为服务器运行平台,用类似javascript的结构语法进行编程,在node.js上运行 -Javascript主要应用前端,是编程语言 ,客户端编程语言(需要浏览器的javascript解释器进行解释执行) -``` -## node.js的缺点 -``` -nodejs可靠性比较低,一个地方报错会导致整个程序崩溃,需要守护进程或者docker重启来解决 -单进程,单线程,只支持单核cpu,不能充分的利用多核cpu服务器 -``` -## node.js的优点 -``` -1.事件驱动 -2.异步编程 -3.非阻塞模式的IO -4.轻量高效 -``` \ No newline at end of file diff --git "a/06\351\231\210\345\230\211\351\221\253/20230214-js\347\232\204\347\254\254\344\272\214\350\257\276.md" "b/06\351\231\210\345\230\211\351\221\253/20230214-js\347\232\204\347\254\254\344\272\214\350\257\276.md" deleted file mode 100644 index 150ae5b..0000000 --- "a/06\351\231\210\345\230\211\351\221\253/20230214-js\347\232\204\347\254\254\344\272\214\350\257\276.md" +++ /dev/null @@ -1,30 +0,0 @@ -## 命令行模式和交互式模式 - -``` -交互模式的代码是输入一行,执行一行,而命令行模式下直接运行.py文件是一次性执行该文件内的所有代码 -``` - -## 模块 - -``` -随着一个文件的程序越来越长,越来越不容易维护。 -为了编写可维护的代码,我们把很多函数分组,分别放到不同的文件里,在Node环境中,一个.js文件就称之为一个模块 -``` - -## 模块的好处 - -``` -最大的好处是大大提高了代码的可维护性,编写代码不必从零开始 -``` - -如果我们要输出的是一个函数或数组,那么,只能给module.exports赋值: - -```js -module.exports = function () { return 'foo'; }; -``` - -当我们用require()获取module时,Node找到对应的module,把这个module的exports变量返回,这样,另一个模块就顺利拿到了模块的输出: - -```js -var greet = require('./hello'); -``` \ No newline at end of file diff --git "a/06\351\231\210\345\230\211\351\221\253/20230216-\345\237\272\346\234\254\346\250\241\345\235\227.md" "b/06\351\231\210\345\230\211\351\221\253/20230216-\345\237\272\346\234\254\346\250\241\345\235\227.md" deleted file mode 100644 index 4d5e917..0000000 --- "a/06\351\231\210\345\230\211\351\221\253/20230216-\345\237\272\346\234\254\346\250\241\345\235\227.md" +++ /dev/null @@ -1,51 +0,0 @@ -# 基本模块 - -# global - -在Node.js环境中,有唯一的全局对象,不叫window,而叫global,这个对象的属性和方法也和浏览器环境的window不同。进入Node.js交互环境,可以直接输入: - -``` -> global.console - -Console { - log: [Function: bound ], - info: [Function: bound ], - warn: [Function: bound ], - error: [Function: bound ], - dir: [Function: bound ], - time: [Function: bound ], - timeEnd: [Function: bound ], - trace: [Function: bound trace], - assert: [Function: bound ], - Console: [Function: Console] } -``` - -用Node执行上面的代码node test.js,你会看到,打印输出是: - -``` -nextTick was set! -nextTick callback! -``` - -这说明传入process.nextTick()的函数不是立刻执行,而是要等到下一次事件循环。 - -Node.js进程本身的事件就由process对象来处理。如果我们响应exit事件,就可以在程序即将退出时执行某个回调函数: - -``` -// 程序即将退出时的回调函数: -process.on('exit', function (code) { - console.log('about to exit with code: ' + code); -}); -``` - -# 判断JavaScript执行环境 - -有很多JavaScript代码既能在浏览器中执行,也能在Node环境执行,但有些时候,程序本身需要判断自己到底是在什么环境下执行的,常用的方式就是根据浏览器和Node环境提供的全局变量名称来判断: - -``` -if (typeof(window) === 'undefined') { - console.log('node.js'); -} else { - console.log('browser'); -} -``` \ No newline at end of file diff --git "a/06\351\231\210\345\230\211\351\221\253/20230217-fs.md" "b/06\351\231\210\345\230\211\351\221\253/20230217-fs.md" deleted file mode 100644 index e5134b2..0000000 --- "a/06\351\231\210\345\230\211\351\221\253/20230217-fs.md" +++ /dev/null @@ -1,69 +0,0 @@ -# fs - -## 异步读文件 - -``` -var fs = require('fs'); - -fs.readFile('sample.txt', 'utf-8', function (err, data) { - if (err) { - console.log(err); - } else { - console.log(data); - } -}); -``` - -## 写文件 - -``` -var fs = require('fs'); - -var data = 'Hello, Node.js'; -fs.writeFile('output.txt', data, function (err) { - if (err) { - console.log(err); - } else { - console.log('ok.'); - } -}); -``` - -## stat对象 - -如果我们要获取文件大小,创建时间等信息,可以使用fs.stat(),它返回一个Stat对象,能告诉我们文件或目录的详细信息: - -``` -'use strict'; - -var fs = require('fs'); - -fs.stat('sample.txt', function (err, stat) { status - if (err) { - console.log(err); - } else { - // 是否是文件: - console.log('isFile: ' + stat.isFile()); - // 是否是目录: - console.log('isDirectory: ' + stat.isDirectory()); - if (stat.isFile()) { - // 文件大小: - console.log('size: ' + stat.size); - // 创建时间, Date对象: - console.log('birth time: ' + stat.birthtime); - // 修改时间, Date对象: - console.log('modified time: ' + stat.mtime); - } - } -}); -``` - -运行结果如下: - -``` -isFile: true -isDirectory: false -size: 181 -birth time: Fri Dec 11 2015 09:43:41 GMT+0800 (CST) -modified time: Fri Dec 11 2015 12:09:00 GMT+0800 (CST) -``` \ No newline at end of file diff --git "a/06\351\231\210\345\230\211\351\221\253/20230217-http\346\234\215\345\212\241\345\231\250.md" "b/06\351\231\210\345\230\211\351\221\253/20230217-http\346\234\215\345\212\241\345\231\250.md" deleted file mode 100644 index 84db7f8..0000000 --- "a/06\351\231\210\345\230\211\351\221\253/20230217-http\346\234\215\345\212\241\345\231\250.md" +++ /dev/null @@ -1,26 +0,0 @@ -# HTTP服务器 - -``` -要开发HTTP服务器程序,从头处理TCP连接,解析HTTP是不现实的。这些工作实际上已经由Node.js自带的http模块完成了。应用程序并不直接和 -HTTP协议打交道,而是操作http模块提供的request和response对象。 - -request对象封装了HTTP请求,我们调用request对象的属性和方法就可以拿到所有HTTP请求的信息; - -response对象封装了HTTP响应,我们操作response对象的方法,就可以把HTTP响应返回给浏览器。 -``` - -1. 请示相应的协议,本身是基于ICP/IP协议(request,response) -2. 无状态 -3. 常见方法:put post get delete options -4. websocket ws这个东西可以让服务器直接向客户端(浏览器、app、各种小程序等)发送消息 - -``` -var http = require('http'); -var server = http.createServer(function(req,res){ - res.writeHead(200,'sb'); - res.end('yuchengxin BigSB'); -}) -server.listen(8080); -console.log('点这里http://localhost:8080/') -``` - diff --git "a/06\351\231\210\345\230\211\351\221\253/20230217-\345\215\201\344\270\252\347\275\221\347\253\231.md" "b/06\351\231\210\345\230\211\351\221\253/20230217-\345\215\201\344\270\252\347\275\221\347\253\231.md" deleted file mode 100644 index 93c0af6..0000000 --- "a/06\351\231\210\345\230\211\351\221\253/20230217-\345\215\201\344\270\252\347\275\221\347\253\231.md" +++ /dev/null @@ -1,17 +0,0 @@ -# 十个网站 - -``` -b1.laia.wang -b2.laia.wang -b3.laia.wang -b4.laia.wang -b5.laia.wang -b6.laia.wang -b7.laia.wang -b8.laia.wang -b9.laia.wang -b10.laia.wang -``` - - - diff --git "a/06\351\231\210\345\230\211\351\221\253/20230220\345\237\272\346\234\254\346\226\207\344\273\266\346\234\215\345\212\241\345\231\250.md" "b/06\351\231\210\345\230\211\351\221\253/20230220\345\237\272\346\234\254\346\226\207\344\273\266\346\234\215\345\212\241\345\231\250.md" deleted file mode 100644 index 39d4378..0000000 --- "a/06\351\231\210\345\230\211\351\221\253/20230220\345\237\272\346\234\254\346\226\207\344\273\266\346\234\215\345\212\241\345\231\250.md" +++ /dev/null @@ -1,20 +0,0 @@ -# 文件服务器 -``` -var fs = require('fs'); -var http = require('http'); -var server = http.createServer((req,res)=>{ - var url = '.' + req.url; - if(url==='./'){ - url = './index.html'; - } - - fs.readFile(url,'utf-8',(err,data)=>{ - if(err){ - res.end('404'); - }else{ - res.writeHead(200,"'Content-Type':'text/html'",) - res.end(data); - } - }) -}).listen(8088); -``` \ No newline at end of file diff --git "a/06\351\231\210\345\230\211\351\221\253/20230223-\344\270\211\347\247\215\346\226\271\346\263\225.md" "b/06\351\231\210\345\230\211\351\221\253/20230223-\344\270\211\347\247\215\346\226\271\346\263\225.md" deleted file mode 100644 index 2a83864..0000000 --- "a/06\351\231\210\345\230\211\351\221\253/20230223-\344\270\211\347\247\215\346\226\271\346\263\225.md" +++ /dev/null @@ -1,65 +0,0 @@ -# 第一种 -``` -var fs = require('fs'); -var http = require('http'); - -function ReadFile(url){ - var result = '404'; - - var stats = fs.statSync(url); - if(stats.isDirectory()){ - var tmpUrl = '.' + url +'index.html'; - var data = fs.readFileSync(tmpUrl,'utf-8'); - result = data; - } - return result; -} - -var server = http.createServer((req,res)=>{ - var url = req.url; - var data = ReadFile(url); - res.writeHead(200,"'Content-Type':'text/html'"); - res.end(data); -}).listen('8088'); -``` - -# 第二种 -``` -var fs = require('fs'); -var http = require('http'); - -function ReadFile(url){ - return new Promise((res,rej)=>{ - fs.readFile(url,'utf-8',(err,data)=>{ - if(err){ - rej('404'); - }else{ - fs.stat(url,(err,stat)=>{ - if(err){ - rej('404'); - }else{ - if(stat.isDirectory()){ - res(data); - } - } - }) - } - }) - }) -} - -var server = http.createServer((req,res)=>{ - var url = req.url; - var data = ReadFile(url); - data.then((x)=>{ - res.end('成功'); - }).catch((x)=>{ - res.end('404'); - }); -}).listen('8088'); -``` - -# 第三种 -``` -await async -``` \ No newline at end of file diff --git "a/06\351\231\210\345\230\211\351\221\253/20230224-koa.md" "b/06\351\231\210\345\230\211\351\221\253/20230224-koa.md" deleted file mode 100644 index 9479108..0000000 --- "a/06\351\231\210\345\230\211\351\221\253/20230224-koa.md" +++ /dev/null @@ -1,13 +0,0 @@ -# Koa -## koa的基本使用 -``` -// 安装 npm install koa - -var Koa = require('koa'); -var app = new Koa(); -app.use(async(ctx,next)=>{ - await next(); - ctx.type = 'text/html'; - ctx.body = '231'; -}).listen('8089'); -``` \ No newline at end of file diff --git "a/06\351\231\210\345\230\211\351\221\253/20230227-\345\210\235\346\255\245\350\256\244\350\257\206Koa.md" "b/06\351\231\210\345\230\211\351\221\253/20230227-\345\210\235\346\255\245\350\256\244\350\257\206Koa.md" deleted file mode 100644 index 4db0ce0..0000000 --- "a/06\351\231\210\345\230\211\351\221\253/20230227-\345\210\235\346\255\245\350\256\244\350\257\206Koa.md" +++ /dev/null @@ -1,27 +0,0 @@ - # 初步认识Koa - - ## 1.安装koa - ``` - npm install koa - ``` -## 2.koa基本写法 -``` - 引入koa - const Koa = require('koa'); -new 一个koa对象 - let app = new Koa(); - 调用该异步函数处理请求 - app.use(async (ctx,next)=>{ - ctx有一个请求对象和一个响应对象 - ctx.response.body = '1和2'; - 用await next()来调用下一个async函数 - await next(); - }) - app.use(async (ctx,next)=>{ - console.log('222'); - } - app.use(async (ctx,next)=>{ - console.log('333'); - } - app.listen('8080'); - ``` \ No newline at end of file diff --git "a/06\351\231\210\345\230\211\351\221\253/20230228-\350\267\257\347\224\261.md" "b/06\351\231\210\345\230\211\351\221\253/20230228-\350\267\257\347\224\261.md" deleted file mode 100644 index 7a77db5..0000000 --- "a/06\351\231\210\345\230\211\351\221\253/20230228-\350\267\257\347\224\261.md" +++ /dev/null @@ -1,42 +0,0 @@ -# Router -## 安装 koa 和 router -``` -npm install koa -npm install koa-router -``` -## 使用router -``` -const Koa = require('koa'); -const router = require('koa-router')(); -let app = new Koa(); -router.get('/abc',async (ctx,next)=>{ - ctx.body = '123'; -}) -app.use(router.routes()); -app.listen(1000); -``` -## koa传参 -``` -1.问号传参 -在你寻址后面/写上?id=你要写的参数 -const Koa = require('koa'); -const router = require('koa-router')(); -let app = new Koa(); -router.get('/abc',async (ctx,next)=>{ - let item = ctx.query.id; - ctx.body = item; -}) -app.use(router.routes()); -app.listen(1000); -2.冒号传参 -直接在寻址后面/写上你要写的参数 -const Koa = require('koa'); -const router = require('koa-router')(); -let app = new Koa(); -router.get('/abc/:id',async (ctx,next)=>{ - let id = ctx.params.id; - ctx.body = id; -}) -app.use(router.routes()); -app.listen(1000); -``` \ No newline at end of file diff --git "a/06\351\231\210\345\230\211\351\221\253/20230302-\345\244\204\347\220\206post\350\257\267\346\261\202.md" "b/06\351\231\210\345\230\211\351\221\253/20230302-\345\244\204\347\220\206post\350\257\267\346\261\202.md" deleted file mode 100644 index 72d1108..0000000 --- "a/06\351\231\210\345\230\211\351\221\253/20230302-\345\244\204\347\220\206post\350\257\267\346\261\202.md" +++ /dev/null @@ -1,17 +0,0 @@ -# 处理post请求 -``` -处理post请求,要用router.post('/path', async fn) -post请求通常会发送一个表单,或者JSON,它作为request的body发送, -但无论是Node.js提供的原始request对象,还是koa提供的request对象, -都不提供解析request的body的功能! - -我们又需要引入另一个middleware来解析原始request请求,然后,把解析后的参数,绑定到ctx.request.body中。 -koa-bodyparser就是用来干这个活的。 - -安装依赖件 -npm install koa-bodyparser - -const bodyParser = require('koa-bodyparser'); -最后koa对象app注册 -app.use(bodyParser) -``` \ No newline at end of file diff --git "a/06\351\231\210\345\230\211\351\221\253/20230303-\351\207\215\346\236\204.md" "b/06\351\231\210\345\230\211\351\221\253/20230303-\351\207\215\346\236\204.md" deleted file mode 100644 index fbb2d10..0000000 --- "a/06\351\231\210\345\230\211\351\221\253/20230303-\351\207\215\346\236\204.md" +++ /dev/null @@ -1,59 +0,0 @@ -# 重构 -``` -现在,我们已经可以处理不同的URL了,但是看看app.js,总觉得还是有点不对劲。 - -所有的URL处理函数都放到app.js里显得很乱,而且,每加一个URL,就需要修改app.js。随着URL越来越多,app.js就会越来越长。 - -如果能把URL处理函数集中到某个js文件,或者某几个js文件中就好了,然后让app.js自动导入所有处理URL的函数。这样,代码一分离,逻辑就显得清楚了。 -``` -``` -1.建一个controllers文件夹 -2.里面建各种类型的js -3.然后建一个主要js为导出的 -``` -``` -代码如下: -const router = require('koa-router'); -const fs = require('fs'); -const bodyParse = require('koa-bodyparser'); - -function findController(path){ - path = path || './controllers'; - let files = fs.readdirSync(path); - return files.filter(item=>{ - return item !== 'index.js'; - }) -} - -function registryRouter(obj){ - for(key in obj){ - let tmpArr = key.split(' '); - let method = tmpArr[0]; - let url = tmpArr[1]; - let fn = obj[key]; - if(method==='get'){ - router.get(url,fn); - } - } -} - -function processRouter(app){ - let files = findController(); - files.forEach(element => { - let tmpModule = require('./'+element.replace('.js','')); - registryRouter(tmpModule); - }); - app.use('koa-router'); - app.use('koa-bodyparse'); -} -module.exports = processRouter; -``` -``` -最后用外面js引入 -const Koa = require('koa'); -let app = new Koa(); -let Controllers = require('./controllers'); -Controllers(app); -app.listen(6000); - -``` \ No newline at end of file diff --git "a/06\351\231\210\345\230\211\351\221\253/20230306-\345\260\201\350\243\205\345\206\215\345\260\201\350\243\205.md" "b/06\351\231\210\345\230\211\351\221\253/20230306-\345\260\201\350\243\205\345\206\215\345\260\201\350\243\205.md" deleted file mode 100644 index c171667..0000000 --- "a/06\351\231\210\345\230\211\351\221\253/20230306-\345\260\201\350\243\205\345\206\215\345\260\201\350\243\205.md" +++ /dev/null @@ -1,85 +0,0 @@ -# 重构再现 -## controllers模块 -### index.js -``` -const router = require('koa-router')(); -let {findController,registryRouter} = require('../utils/ros'); - -function processRouter(app){ - let path = findController(); - registryRouter(path,router); - app.use(router.routes()) -} - -module.exports = processRouter; - -``` -### roles.js -``` -let roles = [ - { - name:'好人', - id:1 - }, - { - name:'坏人', - id:2 - }, - { - name:'孙悟空', - id:3 - } -] - -async function getAll(ctx,next){ - ctx.body = roles; -} - -module.exports = { - 'get /roles':getAll -} - -``` -## utils工具模块 -### ros.js -``` -const fs = require("fs") - -function findController(path){ - path = path || './controllers/'; - let file = fs.readdirSync(path); - return file.filter(item=>{ - return item !== 'index.js'; - }) -} - - -function registryRouter(obj,router){ - obj.forEach(item=>{ - let tmpModule = require('../controllers/'+item.replace('.js','')); - for(let key in tmpModule){ - let tmpArr = key.split(' '); - let method = tmpArr[0]; - let url = tmpArr[1]; - let fn = tmpModule[key]; - if(method==='get'){ - router.get(url,fn); - } - } - }) -} - -module.exports = { - findController,registryRouter -} - -``` -## 主模块app.js -``` -const koa = require('koa'); -let app = new koa(); -const controllers = require('./controllers'); -controllers(app); -app.listen(8080); - -``` diff --git "a/06\351\231\210\345\230\211\351\221\253/20230307-\347\256\200\345\215\225\346\270\262\346\237\223nujucks.md" "b/06\351\231\210\345\230\211\351\221\253/20230307-\347\256\200\345\215\225\346\270\262\346\237\223nujucks.md" deleted file mode 100644 index 9bc78a4..0000000 --- "a/06\351\231\210\345\230\211\351\221\253/20230307-\347\256\200\345\215\225\346\270\262\346\237\223nujucks.md" +++ /dev/null @@ -1,14 +0,0 @@ -# 简单渲染 -## index.html 模块 -``` -{{name}} -``` -## js模块 -``` -const nunjucks = require('nunjucks'); -nunjucks.configure({ - noCache:true, -}) -let a = nunjucks.render('index.html',{name:'cjx'}); -console.log(a); -``` \ No newline at end of file diff --git "a/06\351\231\210\345\230\211\351\221\253/20230308-koa\347\273\223\345\220\210nunjucks.md" "b/06\351\231\210\345\230\211\351\221\253/20230308-koa\347\273\223\345\220\210nunjucks.md" deleted file mode 100644 index 7dd7167..0000000 --- "a/06\351\231\210\345\230\211\351\221\253/20230308-koa\347\273\223\345\220\210nunjucks.md" +++ /dev/null @@ -1,24 +0,0 @@ -# 浏览器上渲染 -## index.html -``` -{{name}} -``` -## js模块 -``` -const Koa = require('koa'); -let app = new Koa(); -const nunjucks = require('nunjucks'); -nunjucks.configure({ - noCache:true, -}) -app.use(async (ctx,next)=>{ - ctx.body = = nunjucks.render('index.html',{name:'cjx'}); -}) - -app.listen(10000) -``` -## 显示 -``` -登入localhost:10000就能看见cjx -``` - diff --git "a/06\351\231\210\345\230\211\351\221\253/20230310-\345\244\204\347\220\206\351\235\231\346\200\201\345\233\276\347\211\207.md" "b/06\351\231\210\345\230\211\351\221\253/20230310-\345\244\204\347\220\206\351\235\231\346\200\201\345\233\276\347\211\207.md" deleted file mode 100644 index 9398713..0000000 --- "a/06\351\231\210\345\230\211\351\221\253/20230310-\345\244\204\347\220\206\351\235\231\346\200\201\345\233\276\347\211\207.md" +++ /dev/null @@ -1,35 +0,0 @@ -# 没封装版 -## html模块 -``` - -``` -## js模块 -``` -const Koa = require('koa'); -const router = require('koa-router')(); -const nunjucks = require('nunjucks'); -const fs = require('fs').promises; -let app = new Koa(); -const mime = require('mime'); - -router.get('/roles',async (ctx,next)=>{ - await next(); - ctx.body = nunjucks.render('roles.html',{iii:'./statics/imgs/ccc.jpg'}); -}) - -app.use(async (ctx,next)=>{ - await next(); - let tmpUrl = ctx.request.url; - if(tmpUrl.startsWith('/statics')){ - let fullUrl = __dirname + tmpUrl; - ctx.type = mime.getType(fullUrl); - ctx.body = await fs.readFile(fullUrl); - } -}) -app.use(router.routes()); -app.listen(8080); -``` -## 显示 -``` -输入localhost:8080/roles -``` \ No newline at end of file -- Gitee From 8dc13b6f66cbd2e2a9af755a02a7f4a9de7f07f2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=99=8C=E7=84=B6?= <2575984447@qq.com> Date: Sun, 19 Mar 2023 12:14:04 +0000 Subject: [PATCH 06/20] =?UTF-8?q?=E5=88=A0=E9=99=A4=E6=96=87=E4=BB=B6=2047?= =?UTF-8?q?=E6=9D=8E=E5=81=A5/2023-02-17-http.md?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../2023-02-17-http.md" | 46 ------------------- 1 file changed, 46 deletions(-) delete mode 100644 "47\346\235\216\345\201\245/2023-02-17-http.md" diff --git "a/47\346\235\216\345\201\245/2023-02-17-http.md" "b/47\346\235\216\345\201\245/2023-02-17-http.md" deleted file mode 100644 index 4617cce..0000000 --- "a/47\346\235\216\345\201\245/2023-02-17-http.md" +++ /dev/null @@ -1,46 +0,0 @@ -# http -Node.js开发的目的就是为了用JavaScript编写Web服务器程序。因为JavaScript实际上已经统治了浏览器端的脚本,其优势就是有世界上数量最多的前端开发人员。如果已经掌握了JavaScript前端开发,再学习一下如何将JavaScript应用在后端开发,就是名副其实的全栈了。 - -# HTTP协议 -要理解Web服务器程序的工作原理,首先,我们要对HTTP协议有基本的了解。如果你对HTTP协议不太熟悉,先看一看HTTP协议简介。 - -# HTTP服务器 -要开发HTTP服务器程序,从头处理TCP连接,解析HTTP是不现实的。这些工作实际上已经由Node.js自带的http模块完成了。应用程序并不直接和HTTP协议打交道,而是操作http模块提供的request和response对象。 - -request对象封装了HTTP请求,我们调用request对象的属性和方法就可以拿到所有HTTP请求的信息; - -response对象封装了HTTP响应,我们操作response对象的方法,就可以把HTTP响应返回给浏览器。 - -用Node.js实现一个HTTP服务器程序非常简单。我们来实现一个最简单的Web程序hello.js,它对于所有请求,都返回Hello world!: - -'use strict'; - -// 导入http模块: -var http = require('http'); - -// 创建http server,并传入回调函数: -var server = http.createServer(function (request, response) { - // 回调函数接收request和response对象, - // 获得HTTP请求的method和url: - console.log(request.method + ': ' + request.url); - // 将HTTP响应200写入response, 同时设置Content-Type: text/html: - response.writeHead(200, {'Content-Type': 'text/html'}); - // 将HTTP响应的HTML内容写入response: - response.end('

Hello world!

'); -}); - -// 让服务器监听8080端口: -server.listen(8080); - -console.log('Server is running at http://127.0.0.1:8080/'); -在命令提示符下运行该程序,可以看到以下输出: - -$ node hello.js -Server is running at http://127.0.0.1:8080/ -不要关闭命令提示符,直接打开浏览器输入http://localhost:8080,即可看到服务器响应的内容 - -同时,在命令提示符窗口,可以看到程序打印的请求信息: - -GET: / -GET: /favicon.ico -这就是我们编写的第一个HTTP服务器程序! \ No newline at end of file -- Gitee From c57801e912d25ad1cb6187c5df502b1fc18c1927 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=99=8C=E7=84=B6?= <2575984447@qq.com> Date: Sun, 19 Mar 2023 12:14:16 +0000 Subject: [PATCH 07/20] =?UTF-8?q?=E5=88=A0=E9=99=A4=E6=96=87=E4=BB=B6=2047?= =?UTF-8?q?=E6=9D=8E=E5=81=A5/2023-02-16-=E5=9F=BA=E6=9C=AC=E6=A8=A1?= =?UTF-8?q?=E5=9D=97.md?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...72\346\234\254\346\250\241\345\235\227.md" | 84 ------------------- 1 file changed, 84 deletions(-) delete mode 100644 "47\346\235\216\345\201\245/2023-02-16-\345\237\272\346\234\254\346\250\241\345\235\227.md" diff --git "a/47\346\235\216\345\201\245/2023-02-16-\345\237\272\346\234\254\346\250\241\345\235\227.md" "b/47\346\235\216\345\201\245/2023-02-16-\345\237\272\346\234\254\346\250\241\345\235\227.md" deleted file mode 100644 index 053b6ad..0000000 --- "a/47\346\235\216\345\201\245/2023-02-16-\345\237\272\346\234\254\346\250\241\345\235\227.md" +++ /dev/null @@ -1,84 +0,0 @@ -# 基本模块 -###1.process 如果我们想要在下一次事件响应中执行代码,可以调用process.nextTick(): - process.nextTick(()=>{ - console.log('只好第二了'); - }) - console.log('哈哈我先'); -### 2.异步读文件 -//文件不在桌面的异步读取一个文本文件 - let fs=require('fs'); - fs.readFile('hh.txt','utf-8',(err,data)=>{ - if(err){ - console.log(err); - }else{ - console.log(data); - } - }) -//文件在桌面的异步读取一个文本文件 -fs.readFile('C:/Users/Administrator/Desktop/nn.txt','utf-8',(err,data)=>{ - if(err){ - console.log(err); - }else{ - console.log(data); - } -}) -//如果没写utf-8,返回的就是回调函数的data参数将返回一个Buffer对象。在Node.js中,Buffer对象就是一个包含零个或任意个字节的数组(注意和Array不同)。 -//要想看懂文本文件的内容就.toString() -fs.readFile('C:/Users/Administrator/Desktop/nn.txt',(err,data)=>{ - if(err){ - console.log(err); - }else{ - console.log(data.toString());//toString()一下就好 - } -}) -//把一个String转换成Buffer -fs.readFile('hh.txt','utf-8',(err,data)=>{ - if(err){ - console.log(err); - }else{ - console.log(data); - console.log(Buffer.from(data,'utf-8')); - } -}) -console.log('哈哈'); -//因为是异步的所以会比哈哈慢 -### 3.同步读文件 -//除了标准的异步读取模式外,fs也提供相应的同步读取函数。同步读取的函数和异步函数相比,多了一个Sync后缀,并且不接收回调函数,函数直接返回结果。 - let s= fs.readFileSync('hh.txt','utf-8'); - console.log(s); - console.log('哈哈'); -//因为是同步所以比哈哈快 -### 4.写文件 -//异步将数据写入文件是通过fs.writeFile()实现的: - fs.writeFile('hh.txt','哈哈啦啦啦哈哈哈哈哈',function(err){ - if(err){ - console.log(err); - }else{ - console.log('写入成功'); - } - }) - console.log('哈哈'); -//因为异步所以比哈哈慢 -//writeFile()的参数依次为文件名、数据和回调函数。如果传入的数据是String,默认按UTF-8编码写入文本文件, -//如果传入的参数是Buffer,则写入的是二进制文件。回调函数由于只关心成功与否,因此只需要一个err参数。 -//和readFile()类似,writeFile()也有一个同步方法,叫writeFileSync(): - fs.writeFileSync('hh.txt','啦啦啦啦啦') - console.log('哈哈'); - -//写入追加 - fs.writeFile('hh.txt','三生三世十里桃花',{flag:'a'},(err)=>{ - if(err){ - console.log(err); - }else{ - console.log('成功'); - } - }) -### 5.stat对象 -//如果我们要获取文件大小,创建时间等信息,可以使用fs.stat(),它返回一个Stat对象,能告诉我们文件或目录的详细信息: - fs.stat('hh.txt',(err,data)=>{ - if(err){ - console.log(err); - }else{ - console.log(data); - } - }) \ No newline at end of file -- Gitee From 610e3f6fe48e05e86ce25f5285f0b8c75d6f65be Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=99=8C=E7=84=B6?= <2575984447@qq.com> Date: Sun, 19 Mar 2023 12:14:28 +0000 Subject: [PATCH 08/20] =?UTF-8?q?=E5=88=A0=E9=99=A4=E6=96=87=E4=BB=B6=2047?= =?UTF-8?q?=E6=9D=8E=E5=81=A5/2023-02-14-=E6=A8=A1=E5=9D=97.md?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../2023-02-14-\346\250\241\345\235\227.md" | 31 ------------------- 1 file changed, 31 deletions(-) delete mode 100644 "47\346\235\216\345\201\245/2023-02-14-\346\250\241\345\235\227.md" diff --git "a/47\346\235\216\345\201\245/2023-02-14-\346\250\241\345\235\227.md" "b/47\346\235\216\345\201\245/2023-02-14-\346\250\241\345\235\227.md" deleted file mode 100644 index f202e9f..0000000 --- "a/47\346\235\216\345\201\245/2023-02-14-\346\250\241\345\235\227.md" +++ /dev/null @@ -1,31 +0,0 @@ -# 模块 -### 1.要在模块中对外输出变量,用: -module.exports = variable; -'use strict'; -//先创一个hello.js文件,里面可以写方法 -function add(a,b){ - return a+b; -} - -//可以写数组 -let arr=[1,2,3]; - -//可以写对象 -let a={ - name:'呵呵', - age:66 -} -//在模块中对外输出变量,输出的变量可以是任意对象、函数、数组等等。 -module.exports=a -###2.要引入其他模块输出的对象,用: -var foo = require('other_module'); -//再创一个main.js文件 -'use strict'; -//引入其他模块输出的对象 -let add=require('./hello'); -//输出 -console.log(add); -#CommonJS规范 -这种模块加载机制被称为CommonJS规范。在这个规范下,每个.js文件都是一个模块,它们内部各自使用的变量名和函数名都互不冲突,例如,hello.js和main.js都申明了全局变量var s = 'xxx',但互不影响。 - -一个模块想要对外暴露变量(函数也是变量),可以用module.exports = variable;,一个模块要引用其他模块暴露的变量,用var ref = require('module_name');就拿到了引用模块的变量。 \ No newline at end of file -- Gitee From 44f249238d896b9d9f335d037186a59d46984e07 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=99=8C=E7=84=B6?= <2575984447@qq.com> Date: Sun, 19 Mar 2023 12:14:44 +0000 Subject: [PATCH 09/20] =?UTF-8?q?=E5=88=A0=E9=99=A4=E6=96=87=E4=BB=B6=2047?= =?UTF-8?q?=E6=9D=8E=E5=81=A5/20230310-Sequelize.md?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../20230310-Sequelize.md" | 106 ------------------ 1 file changed, 106 deletions(-) delete mode 100644 "47\346\235\216\345\201\245/20230310-Sequelize.md" diff --git "a/47\346\235\216\345\201\245/20230310-Sequelize.md" "b/47\346\235\216\345\201\245/20230310-Sequelize.md" deleted file mode 100644 index 36a5425..0000000 --- "a/47\346\235\216\345\201\245/20230310-Sequelize.md" +++ /dev/null @@ -1,106 +0,0 @@ -# Sequelize - -## 安装 - 1. 使用 npm 安装sequelize - npm i sequelize # 这将安装最新版本的 Sequelize - 2. 使用 yarn 安装sequelize - yarn add sequelize - 3. 你还必须手动为所选数据库安装驱动程序: - ### 使用 npm - npm i pg pg-hstore # PostgreSQL - npm i mysql2 # MySQL - npm i mariadb # MariaDB - npm i sqlite3 # SQLite - npm i tedious # Microsoft SQL Server - npm i ibm_db # DB2 - ### 使用 yarn - yarn add pg pg-hstore # PostgreSQL - yarn add mysql2 # MySQL - yarn add mariadb # MariaDB - yarn add sqlite3 # SQLite - yarn add tedious # Microsoft SQL Server - yarn add ibm_db # DB2 - - -## 连接到数据库 - 1. 要连接到数据库,必须创建一个 Sequelize 实例. 这可以通过将连接参数分别传递到 Sequelize 构造函数或通过传递一个连接 URI 来完成: -```js - const { Sequelize } = require('sequelize'); - - // 方法 1: 传递一个连接 URI - const sequelize = new Sequelize('sqlite::memory:') // Sqlite 示例 - const sequelize = new Sequelize('postgres:// user:pass@example.com:5432/dbname') // Postgres 示例 - - // 方法 2: 分别传递参数 (sqlite) - const sequelize = new Sequelize({ - dialect: 'sqlite', - storage: 'path/to/database.sqlite' - }); - - // 方法 3: 分别传递参数 (其它数据库) - const sequelize = new Sequelize('database', 'username', 'password', { - host: 'localhost', - dialect: /* 选择 'mysql' | 'mariadb' | 'postgres' | 'mssql' 其一 */ - }); -``` - -## 测试链接 -```js -async function test(){ - try { - await sequelize.authenticate(); - console.log('Connection has been established successfully.'); - } catch (error) { - console.error('Unable to connect to the database:', error); - } -} - -test() - -``` - -## 关闭连接 -1. 默认情况下,Sequelize 将保持连接打开状态,并对所有查询使用相同的连接. 如果你需要关闭连接,请调用 sequelize.close()(这是异步的并返回一个 Promise) - - -## 定义模型 -```js -let users=sequelize.define('Users',{ - id:{ - type:DataTypes.INTEGER, - primaryKey:true - }, - username:DataTypes.STRING, - age:DataTypes.INTEGER -},{ - timestamps:false -}) -``` -用sequelize.define()定义Model时,传入名称Users,默认的表名就是Users。第二个参数指定列名和数据类型,如果是主键,需要更详细地指定。第三个参数是额外的配置,我们传入{ timestamps: false }是为了关闭Sequelize的自动添加timestamp的功能。所有的ORM框架都有一种很不好的风气,总是自作聪明地加上所谓“自动化”的功能,但是会让人感到完全摸不着头脑。 - -接下来,我们就可以往数据库中塞一些数据了。我们可以用Promise的方式写: -```js -users.sync().then(()=>{ - users.create({ - id:1, - username:'呵呵', - age:16 - }) -}) -``` -也可以用await写: -```js -async function xixi(){ - users.create({ - id:2, - username:'嘻嘻', - age:17 -}) -} -xixi() -``` - -## Promises 和 async/await - -Sequelize 提供的大多数方法都是异步的,因此返回 Promises. 它们都是 Promises, 因此你可以直接使用Promise API(例如,使用 then, catch, finally). -当然,使用 async 和 await 也可以正常工作. \ No newline at end of file -- Gitee From 62abf55a645ddf1ccf1e06731fa4c88ec6f3bae4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=99=8C=E7=84=B6?= <2575984447@qq.com> Date: Sun, 19 Mar 2023 12:14:50 +0000 Subject: [PATCH 10/20] =?UTF-8?q?=E5=88=A0=E9=99=A4=E6=96=87=E4=BB=B6=2047?= =?UTF-8?q?=E6=9D=8E=E5=81=A5/2023-02-20-=E6=96=87=E4=BB=B6=E6=9C=8D?= =?UTF-8?q?=E5=8A=A1=E5=99=A8.md?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...66\346\234\215\345\212\241\345\231\250.md" | 93 ------------------- 1 file changed, 93 deletions(-) delete mode 100644 "47\346\235\216\345\201\245/2023-02-20-\346\226\207\344\273\266\346\234\215\345\212\241\345\231\250.md" diff --git "a/47\346\235\216\345\201\245/2023-02-20-\346\226\207\344\273\266\346\234\215\345\212\241\345\231\250.md" "b/47\346\235\216\345\201\245/2023-02-20-\346\226\207\344\273\266\346\234\215\345\212\241\345\231\250.md" deleted file mode 100644 index ef043d2..0000000 --- "a/47\346\235\216\345\201\245/2023-02-20-\346\226\207\344\273\266\346\234\215\345\212\241\345\231\250.md" +++ /dev/null @@ -1,93 +0,0 @@ -#文件服务器 -让我们继续扩展一下上面的Web程序。我们可以设定一个目录,然后让Web程序变成一个文件服务器。要实现这一点,我们只需要解析request.url中的路径,然后在本地找到对应的文件,把文件内容发送出去就可以了。 - -解析URL需要用到Node.js提供的url模块,它使用起来非常简单,通过parse()将一个字符串解析为一个Url对象: - -'use strict'; - -var url = require('url'); - -console.log(url.parse('http://user:pass@host.com:8080/path/to/file?query=string#hash')); -结果如下: - -Url { - protocol: 'http:', - slashes: true, - auth: 'user:pass', - host: 'host.com:8080', - port: '8080', - hostname: 'host.com', - hash: '#hash', - search: '?query=string', - query: 'query=string', - pathname: '/path/to/file', - path: '/path/to/file?query=string', - href: 'http://user:pass@host.com:8080/path/to/file?query=string#hash' } -处理本地文件目录需要使用Node.js提供的path模块,它可以方便地构造目录: - -'use strict'; - -var path = require('path'); - -// 解析当前目录: -var workDir = path.resolve('.'); // '/Users/michael' - -// 组合完整的文件路径:当前目录+'pub'+'index.html': -var filePath = path.join(workDir, 'pub', 'index.html'); -// '/Users/michael/pub/index.html' -使用path模块可以正确处理操作系统相关的文件路径。在Windows系统下,返回的路径类似于C:\Users\michael\static\index.html,这样,我们就不关心怎么拼接路径了。 - -最后,我们实现一个文件服务器file_server.js: - -'use strict'; - -var - fs = require('fs'), - url = require('url'), - path = require('path'), - http = require('http'); - -// 从命令行参数获取root目录,默认是当前目录: -var root = path.resolve(process.argv[2] || '.'); - -console.log('Static root dir: ' + root); - -// 创建服务器: -var server = http.createServer(function (request, response) { - // 获得URL的path,类似 '/css/bootstrap.css': - var pathname = url.parse(request.url).pathname; - // 获得对应的本地文件路径,类似 '/srv/www/css/bootstrap.css': - var filepath = path.join(root, pathname); - // 获取文件状态: - fs.stat(filepath, function (err, stats) { - if (!err && stats.isFile()) { - // 没有出错并且文件存在: - console.log('200 ' + request.url); - // 发送200响应: - response.writeHead(200); - // 将文件流导向response: - fs.createReadStream(filepath).pipe(response); - } else { - // 出错了或者文件不存在: - console.log('404 ' + request.url); - // 发送404响应: - response.writeHead(404); - response.end('404 Not Found'); - } - }); -}); - -server.listen(8080); - -console.log('Server is running at http://127.0.0.1:8080/'); -没有必要手动读取文件内容。由于response对象本身是一个Writable Stream,直接用pipe()方法就实现了自动读取文件内容并输出到HTTP响应。 - -在命令行运行node file_server.js /path/to/dir,把/path/to/dir改成你本地的一个有效的目录,然后在浏览器中输入http://localhost:8080/index.html: - -只要当前目录下存在文件index.html,服务器就可以把文件内容发送给浏览器。观察控制台输出: - -200 /index.html -200 /css/uikit.min.css -200 /js/jquery.min.js -200 /fonts/fontawesome-webfont.woff2 -第一个请求是浏览器请求index.html页面,后续请求是浏览器解析HTML后发送的其它资源请求。 \ No newline at end of file -- Gitee From 0e9b759b8a9ab6819aa0d7cfbcef1a9ebd4ebe4d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=99=8C=E7=84=B6?= <2575984447@qq.com> Date: Sun, 19 Mar 2023 12:14:57 +0000 Subject: [PATCH 11/20] =?UTF-8?q?=E5=88=A0=E9=99=A4=E6=96=87=E4=BB=B6=2047?= =?UTF-8?q?=E6=9D=8E=E5=81=A5/2023-02-21.=E9=A2=98=E7=9B=AE.md?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../2023-02-21.\351\242\230\347\233\256.md" | 35 ------------------- 1 file changed, 35 deletions(-) delete mode 100644 "47\346\235\216\345\201\245/2023-02-21.\351\242\230\347\233\256.md" diff --git "a/47\346\235\216\345\201\245/2023-02-21.\351\242\230\347\233\256.md" "b/47\346\235\216\345\201\245/2023-02-21.\351\242\230\347\233\256.md" deleted file mode 100644 index 96d31ec..0000000 --- "a/47\346\235\216\345\201\245/2023-02-21.\351\242\230\347\233\256.md" +++ /dev/null @@ -1,35 +0,0 @@ -# 题目 -const http=require('http'); -const fs=require('fs'); - -const server=http.createServer((req,res)=>{ - let url=req.url; - console.log(url); - if (fs.existsSync('.'+url)) { - if (fs.existsSync('.'+url+'/index.html')) { - url='.'+url+'/index.html' - }else if(fs.existsSync('.'+url)){ - url='.'+url - } - }else if(fs.existsSync('.'+url+'.html')){ - url='.'+url+'.html' - }else if(fs.existsSync('.'+url+'.txt')){ - url='.'+url+'.txt' - }else if(fs.existsSync('.'+url+'.md')){ - url='.'+url+'.md' - }else if(url==='/'){ - url='./index.html' - } - - fs.readFile(url,'utf-8',(err,data)=>{ - if (err) { - res.end('404') - }else{ - res.end(data) - } - }) -}) - -server.listen(8088); - -console.log('ojbk'); \ No newline at end of file -- Gitee From 538a4f0b87277546373dc10f5c78588dafca1091 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=99=8C=E7=84=B6?= <2575984447@qq.com> Date: Sun, 19 Mar 2023 12:15:04 +0000 Subject: [PATCH 12/20] =?UTF-8?q?=E5=88=A0=E9=99=A4=E6=96=87=E4=BB=B6=2047?= =?UTF-8?q?=E6=9D=8E=E5=81=A5/2023-02-23-Promise.md?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- "47\346\235\216\345\201\245/2023-02-23-Promise.md" | 6 ------ 1 file changed, 6 deletions(-) delete mode 100644 "47\346\235\216\345\201\245/2023-02-23-Promise.md" diff --git "a/47\346\235\216\345\201\245/2023-02-23-Promise.md" "b/47\346\235\216\345\201\245/2023-02-23-Promise.md" deleted file mode 100644 index a132210..0000000 --- "a/47\346\235\216\345\201\245/2023-02-23-Promise.md" +++ /dev/null @@ -1,6 +0,0 @@ - - -# 一、读取文件的三种方法 -## 1、同步 -## 2、Promise -## 3、anync/await \ No newline at end of file -- Gitee From 1fa564488aa00073e45b94266ce377e87641ea8e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=99=8C=E7=84=B6?= <2575984447@qq.com> Date: Sun, 19 Mar 2023 12:15:12 +0000 Subject: [PATCH 13/20] =?UTF-8?q?=E5=88=A0=E9=99=A4=E6=96=87=E4=BB=B6=2047?= =?UTF-8?q?=E6=9D=8E=E5=81=A5/2023-02-24-koa.md?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../2023-02-24-koa.md" | 40 ------------------- 1 file changed, 40 deletions(-) delete mode 100644 "47\346\235\216\345\201\245/2023-02-24-koa.md" diff --git "a/47\346\235\216\345\201\245/2023-02-24-koa.md" "b/47\346\235\216\345\201\245/2023-02-24-koa.md" deleted file mode 100644 index 4482ae7..0000000 --- "a/47\346\235\216\345\201\245/2023-02-24-koa.md" +++ /dev/null @@ -1,40 +0,0 @@ -# koa入门 -## 创建koa2工程 -首先,我们创建一个目录hello-koa并作为工程目录用VS Code打开。然后,我们创建app.js,输入以下代码: - -// 导入koa,和koa 1.x不同,在koa2中,我们导入的是一个class,因此用大写的Koa表示: -const Koa = require('koa'); - -// 创建一个Koa对象表示web app本身: -const app = new Koa(); - -// 对于任何请求,app将调用该异步函数处理请求: -app.use(async (ctx, next) => { - await next(); - ctx.response.type = 'text/html'; - ctx.response.body = '

Hello, koa2!

'; -}); - -// 在端口3000监听: -app.listen(3000); -console.log('app started at port 3000...') -对于每一个http请求,koa将调用我们传入的异步函数来处理: - -async (ctx, next) => { - await next(); - // 设置response的Content-Type: - ctx.response.type = 'text/html'; - // 设置response的内容: - ctx.response.body = '

Hello, koa2!

'; -} -其中,参数ctx是由koa传入的封装了request和response的变量,我们可以通过它访问request和response,next是koa传入的将要处理的下一个异步函数。 - -上面的异步函数中,我们首先用await next();处理下一个异步函数,然后,设置response的Content-Type和内容。 - -由async标记的函数称为异步函数,在异步函数中,可以用await调用另一个异步函数,这两个关键字将在ES7中引入。 - -现在我们遇到第一个问题:koa这个包怎么装,app.js才能正常导入它? - -方法一:可以用npm命令直接安装koa。先打开命令提示符,务必把当前目录切换到hello-koa这个目录,然后执行命令: - -npm install koa \ No newline at end of file -- Gitee From ce935ff0d96d86f634e00c81fb4f1beb14ce700d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=99=8C=E7=84=B6?= <2575984447@qq.com> Date: Sun, 19 Mar 2023 12:15:20 +0000 Subject: [PATCH 14/20] =?UTF-8?q?=E5=88=A0=E9=99=A4=E6=96=87=E4=BB=B6=2047?= =?UTF-8?q?=E6=9D=8E=E5=81=A5/2023-02-27-koaRouter.md?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../2023-02-27-koaRouter.md" | 62 ------------------- 1 file changed, 62 deletions(-) delete mode 100644 "47\346\235\216\345\201\245/2023-02-27-koaRouter.md" diff --git "a/47\346\235\216\345\201\245/2023-02-27-koaRouter.md" "b/47\346\235\216\345\201\245/2023-02-27-koaRouter.md" deleted file mode 100644 index 69493a2..0000000 --- "a/47\346\235\216\345\201\245/2023-02-27-koaRouter.md" +++ /dev/null @@ -1,62 +0,0 @@ - -## koa-router -为了处理URL,我们需要引入koa-router这个middleware,让它负责处理URL映射。 - -我们把上一节的hello-koa工程复制一份,重命名为url-koa。 - -先在package.json中添加依赖项: -``` -"koa-router": "7.0.0" -``` -然后用npm install安装。 - - -接下来,我们修改app.js,使用koa-router来处理URL: -``` -const Koa = require('koa'); - -// 注意require('koa-router')返回的是函数: -const router = require('koa-router')(); - -const app = new Koa(); - -// log request URL: -app.use(async (ctx, next) => { - console.log(`Process ${ctx.request.method} ${ctx.request.url}...`); - await next(); -}); - -// add url-route: -router.get('/hello/:name', async (ctx, next) => { - var name = ctx.params.name; - ctx.response.body = `

Hello, ${name}!

`; -}); - -router.get('/', async (ctx, next) => { - ctx.response.body = '

Index

'; -}); - -// add router middleware: -app.use(router.routes()); - -app.listen(3000); -console.log('app started at port 3000...'); -``` -注意导入koa-router的语句最后的()是函数调用: -``` -const router = require('koa-router')(); -``` -相当于: -``` -const fn_router = require('koa-router'); -const router = fn_router(); -``` -然后,我们使用router.get('/path', async fn)来注册一个GET请求。可以在请求路径中使用带变量的/hello/:name,变量可以通过ctx.params.name访问。 - -再运行app.js,我们就可以测试不同的URL: -``` -输入首页:http://localhost:3000/ -``` -``` -输入:http://localhost:3000/hello/koa -``` \ No newline at end of file -- Gitee From d35a844219bf90ad0d090588955a9ff98bb385fc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=99=8C=E7=84=B6?= <2575984447@qq.com> Date: Sun, 19 Mar 2023 12:15:28 +0000 Subject: [PATCH 15/20] =?UTF-8?q?=E5=88=A0=E9=99=A4=E6=96=87=E4=BB=B6=2047?= =?UTF-8?q?=E6=9D=8E=E5=81=A5/2023-02-28-post.md?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../2023-02-28-post.md" | 53 ------------------- 1 file changed, 53 deletions(-) delete mode 100644 "47\346\235\216\345\201\245/2023-02-28-post.md" diff --git "a/47\346\235\216\345\201\245/2023-02-28-post.md" "b/47\346\235\216\345\201\245/2023-02-28-post.md" deleted file mode 100644 index 5e4aaba..0000000 --- "a/47\346\235\216\345\201\245/2023-02-28-post.md" +++ /dev/null @@ -1,53 +0,0 @@ - -# 处理post请求 -用router.get('/path', async fn)处理的是get请求。如果要处理post请求,可以用router.post('/path', async fn)。 - -用post请求处理URL时,我们会遇到一个问题:post请求通常会发送一个表单,或者JSON,它作为request的body发送,但无论是Node.js提供的原始request对象,还是koa提供的request对象,都不提供解析request的body的功能! - -所以,我们又需要引入另一个middleware来解析原始request请求,然后,把解析后的参数,绑定到ctx.request.body中。 - -koa-bodyparser就是用来干这个活的。 - -我们在package.json中添加依赖项: -``` -"koa-bodyparser": "3.2.0" -``` -然后使用npm install安装。 - -下面,修改app.js,引入koa-bodyparser: -``` -const bodyParser = require('koa-bodyparser'); -``` -在合适的位置加上: -``` -app.use(bodyParser()); -``` -由于middleware的顺序很重要,这个koa-bodyparser必须在router之前被注册到app对象上。 - -现在我们就可以处理post请求了。写一个简单的登录表单: -``` -router.get('/', async (ctx, next) => { - ctx.response.body = `

Index

-
-

Name:

-

Password:

-

-
`; -}); - -router.post('/signin', async (ctx, next) => { - var - name = ctx.request.body.name || '', - password = ctx.request.body.password || ''; - console.log(`signin with name: ${name}, password: ${password}`); - if (name === 'koa' && password === '12345') { - ctx.response.body = `

Welcome, ${name}!

`; - } else { - ctx.response.body = `

Login failed!

-

Try again

`; - } -}); -``` -注意到我们用var name = ctx.request.body.name || ''拿到表单的name字段,如果该字段不存在,默认值设置为''。 - -类似的,put、delete、head请求也可以由router处理。 \ No newline at end of file -- Gitee From 26304df99b75d1807e95ef099efac202762f1b53 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=99=8C=E7=84=B6?= <2575984447@qq.com> Date: Sun, 19 Mar 2023 12:15:35 +0000 Subject: [PATCH 16/20] =?UTF-8?q?=E5=88=A0=E9=99=A4=E6=96=87=E4=BB=B6=2047?= =?UTF-8?q?=E6=9D=8E=E5=81=A5/2023-03-03-=E9=87=8D=E6=9E=84.md?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../2023-03-03-\351\207\215\346\236\204.md" | 37 ------------------- 1 file changed, 37 deletions(-) delete mode 100644 "47\346\235\216\345\201\245/2023-03-03-\351\207\215\346\236\204.md" diff --git "a/47\346\235\216\345\201\245/2023-03-03-\351\207\215\346\236\204.md" "b/47\346\235\216\345\201\245/2023-03-03-\351\207\215\346\236\204.md" deleted file mode 100644 index 8937472..0000000 --- "a/47\346\235\216\345\201\245/2023-03-03-\351\207\215\346\236\204.md" +++ /dev/null @@ -1,37 +0,0 @@ - -## Controller Middleware -最后,我们把扫描controllers目录和创建router的代码从app.js中提取出来,作为一个简单的middleware使用,命名为controller.js: -``` -const fs = require('fs'); - -function addMapping(router, mapping) { - ... -} - -function addControllers(router, dir) { - ... -} - -module.exports = function (dir) { - let - controllers_dir = dir || 'controllers', // 如果不传参数,扫描目录默认为'controllers' - router = require('koa-router')(); - addControllers(router, controllers_dir); - return router.routes(); -}; -``` -这样一来,我们在app.js的代码又简化了: -``` -... - -// 导入controller middleware: -const controller = require('./controller'); - -... - -// 使用middleware: -app.use(controller()); - -... -``` -经过重新整理后的工程url2-koa目前具备非常好的模块化,所有处理URL的函数按功能组存放在controllers目录,今后我们也只需要不断往这个目录下加东西就可以了,app.js保持不变。 \ No newline at end of file -- Gitee From 111f580084479760c2e1ed76f0db85f93f2a3faa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=99=8C=E7=84=B6?= <2575984447@qq.com> Date: Sun, 19 Mar 2023 12:15:42 +0000 Subject: [PATCH 17/20] =?UTF-8?q?=E5=88=A0=E9=99=A4=E6=96=87=E4=BB=B6=2047?= =?UTF-8?q?=E6=9D=8E=E5=81=A5/2023-03-02-=E9=87=8D=E6=9E=84.md?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../2023-03-02-\351\207\215\346\236\204.md" | 142 ------------------ 1 file changed, 142 deletions(-) delete mode 100644 "47\346\235\216\345\201\245/2023-03-02-\351\207\215\346\236\204.md" diff --git "a/47\346\235\216\345\201\245/2023-03-02-\351\207\215\346\236\204.md" "b/47\346\235\216\345\201\245/2023-03-02-\351\207\215\346\236\204.md" deleted file mode 100644 index 6c4b992..0000000 --- "a/47\346\235\216\345\201\245/2023-03-02-\351\207\215\346\236\204.md" +++ /dev/null @@ -1,142 +0,0 @@ - -## 重构 -现在,我们已经可以处理不同的URL了,但是看看app.js,总觉得还是有点不对劲。 - - -所有的URL处理函数都放到app.js里显得很乱,而且,每加一个URL,就需要修改app.js。随着URL越来越多,app.js就会越来越长。 - -如果能把URL处理函数集中到某个js文件,或者某几个js文件中就好了,然后让app.js自动导入所有处理URL的函数。这样,代码一分离,逻辑就显得清楚了。最好是这样: -``` -url2-koa/ -| -+- .vscode/ -| | -| +- launch.json <-- VSCode 配置文件 -| -+- controllers/ -| | -| +- login.js <-- 处理login相关URL -| | -| +- users.js <-- 处理用户管理相关URL -| -+- app.js <-- 使用koa的js -| -+- package.json <-- 项目描述文件 -| -+- node_modules/ <-- npm安装的所有依赖包 -``` -于是我们把url-koa复制一份,重命名为url2-koa,准备重构这个项目。 - -我们先在controllers目录下编写index.js: -``` -var fn_index = async (ctx, next) => { - ctx.response.body = `

Index

-
-

Name:

-

Password:

-

-
`; -}; - -var fn_signin = async (ctx, next) => { - var - name = ctx.request.body.name || '', - password = ctx.request.body.password || ''; - console.log(`signin with name: ${name}, password: ${password}`); - if (name === 'koa' && password === '12345') { - ctx.response.body = `

Welcome, ${name}!

`; - } else { - ctx.response.body = `

Login failed!

-

Try again

`; - } -}; - -module.exports = { - 'GET /': fn_index, - 'POST /signin': fn_signin -}; -``` - -这个index.js通过module.exports把两个URL处理函数暴露出来。 - -类似的,hello.js把一个URL处理函数暴露出来: -``` -var fn_hello = async (ctx, next) => { - var name = ctx.params.name; - ctx.response.body = `

Hello, ${name}!

`; -}; - -module.exports = { - 'GET /hello/:name': fn_hello -}; -``` -现在,我们修改app.js,让它自动扫描controllers目录,找到所有js文件,导入,然后注册每个URL: -``` -// 先导入fs模块,然后用readdirSync列出文件 -// 这里可以用sync是因为启动时只运行一次,不存在性能问题: -var files = fs.readdirSync(__dirname + '/controllers'); - -// 过滤出.js文件: -var js_files = files.filter((f)=>{ - return f.endsWith('.js'); -}); - -// 处理每个js文件: -for (var f of js_files) { - console.log(`process controller: ${f}...`); - // 导入js文件: - let mapping = require(__dirname + '/controllers/' + f); - for (var url in mapping) { - if (url.startsWith('GET ')) { - // 如果url类似"GET xxx": - var path = url.substring(4); - router.get(path, mapping[url]); - console.log(`register URL mapping: GET ${path}`); - } else if (url.startsWith('POST ')) { - // 如果url类似"POST xxx": - var path = url.substring(5); - router.post(path, mapping[url]); - console.log(`register URL mapping: POST ${path}`); - } else { - // 无效的URL: - console.log(`invalid URL: ${url}`); - } - } -} -``` - -如果上面的大段代码看起来还是有点费劲,那就把它拆成更小单元的函数: -``` -function addMapping(router, mapping) { - for (var url in mapping) { - if (url.startsWith('GET ')) { - var path = url.substring(4); - router.get(path, mapping[url]); - console.log(`register URL mapping: GET ${path}`); - } else if (url.startsWith('POST ')) { - var path = url.substring(5); - router.post(path, mapping[url]); - console.log(`register URL mapping: POST ${path}`); - } else { - console.log(`invalid URL: ${url}`); - } - } -} - -function addControllers(router) { - var files = fs.readdirSync(__dirname + '/controllers'); - var js_files = files.filter((f) => { - return f.endsWith('.js'); - }); - - for (var f of js_files) { - console.log(`process controller: ${f}...`); - let mapping = require(__dirname + '/controllers/' + f); - addMapping(router, mapping); - } -} - -addControllers(router); -``` - -确保每个函数功能非常简单,一眼能看明白,是代码可维护的关键。 \ No newline at end of file -- Gitee From 86fadb4923537499daceeb8ae98232f2301a2e71 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=99=8C=E7=84=B6?= <2575984447@qq.com> Date: Sun, 19 Mar 2023 12:15:50 +0000 Subject: [PATCH 18/20] =?UTF-8?q?=E5=88=A0=E9=99=A4=E6=96=87=E4=BB=B6=2047?= =?UTF-8?q?=E6=9D=8E=E5=81=A5/2023-03-06-=E9=87=8D=E6=9E=84.md?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../2023-03-06-\351\207\215\346\236\204.md" | 150 ------------------ 1 file changed, 150 deletions(-) delete mode 100644 "47\346\235\216\345\201\245/2023-03-06-\351\207\215\346\236\204.md" diff --git "a/47\346\235\216\345\201\245/2023-03-06-\351\207\215\346\236\204.md" "b/47\346\235\216\345\201\245/2023-03-06-\351\207\215\346\236\204.md" deleted file mode 100644 index e9b8612..0000000 --- "a/47\346\235\216\345\201\245/2023-03-06-\351\207\215\346\236\204.md" +++ /dev/null @@ -1,150 +0,0 @@ -# 重构 -1. a.js -```js -//引入模块 -const Koa=require('koa'); -const registerRouter=require('./controllers') -const templating=require('./templating') -const staticFiles=require('./staticFiles') -//实例化 -let app=new Koa(); - - -//注册路由 -registerRouter(app) - -//端口号 -let port=8088; -app.listen(port) - -console.log(`http://localhost:${port}`); -``` -2. 控制器 - * 新建一个controllers文件夹,里面有各个控制器 - 1. index.js - ```js - const router=require('koa-router')(); - const bodyParser=require('koa-bodyparser'); - const {findControllers,registerRouter2}=require('../utils/tools') - - function registerRouter(app){ - //找到所有文件 - let files=findControllers() - //遍历注册 - registerRouter2(files,router) - - app.use(router.routes()); - app.use(bodyParser()) - } - - module.exports=registerRouter; - ``` - 2. roles.js - ```js - - let list=[ - { - id:1, - name:'哈哈', - img:url - },{ - id:2, - name:'嘻嘻', - img:url - },{ - id:3, - name:'呵呵', - img:url - },{ - id:4, - name:'嘿嘿', - img:url - },{ - id:5, - name:'呐呐', - img:url - }, - ] - - async function getAll(ctx,next){ - - - } - async function getById(ctx,next){ - - } - async function addItem(ctx,next){ - - } - async function updateItem(ctx,next){ - - } - async function delItem(ctx,next){ - - } - - - module.exports={ - 'get /roles':getAll, - 'get /roles/:id':getById, - 'post /roles':addItem, - 'put /roles/:id':updateItem, - 'delete /roles/:id':delItem, - } - ``` -3. 路由系统封装 - * 新建一个utils文件夹,再新建一个tools.js - ```js - 'use strict'; - - const fs = require('fs'); - - // 查找所有路由文件 - function findControllerFiles(path) { - // 如果传入指定目录,则从指定目录查找路由文件,否则默认从 controllers目录下查找路由文件 - path = path || './controllers'; - - // 使用fs模块读取指定目录下的文件 - let files = fs.readdirSync(path); - - // 返回过滤后的路由文件(查找到的文件过滤掉非.js结尾的、 并且不能是index.js的文件) - return files.filter(item => { - return item.endsWith('.js') && item !== 'index. js'; - }); - } - - //注册路由(传入所有的路由文件) - function registryRouter(files,router) { - // 遍历路由文件 - files.forEach(file => {// 形如这个样子的字符串: 'users.js' - - // 加载指定文件模块,返回形式是一个对象 - let tmpModule = require('../controllers/' + file); - // 遍历对象 - for (let key in tmpModule) { - // 使用空格分割字符串 - let arr = key.split(' '); - - let method = arr[0];//http的方法,可能是:get post put delete中的其中一种 - let url = arr[1];//请求地址,不包含主机地址:localhost:port - let fn = tmpModule[key];//请求需要调用的异步函数 - - if (method === 'get') { - router.get(url, fn); - } else if (method === 'post') { - router.post(url, fn); - } else if (method === 'put') { - router.put(url, fn); - } else if (method === 'delete') { - router.delete(url, fn); - } - } - }) - } - - module.exports = { - findControllerFiles, - registryRouter - } - ``` - \ No newline at end of file -- Gitee From 8805689f2668c2bcdb32e15e88f9d0e432ab007a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=99=8C=E7=84=B6?= <2575984447@qq.com> Date: Sun, 19 Mar 2023 12:15:59 +0000 Subject: [PATCH 19/20] =?UTF-8?q?=E5=88=A0=E9=99=A4=E6=96=87=E4=BB=B6=2047?= =?UTF-8?q?=E6=9D=8E=E5=81=A5/2023-03-07-=E6=A8=A1=E6=9D=BF=E5=BC=95?= =?UTF-8?q?=E6=93=8E.md?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...41\346\235\277\345\274\225\346\223\216.md" | 42 ------------------- 1 file changed, 42 deletions(-) delete mode 100644 "47\346\235\216\345\201\245/2023-03-07-\346\250\241\346\235\277\345\274\225\346\223\216.md" diff --git "a/47\346\235\216\345\201\245/2023-03-07-\346\250\241\346\235\277\345\274\225\346\223\216.md" "b/47\346\235\216\345\201\245/2023-03-07-\346\250\241\346\235\277\345\274\225\346\223\216.md" deleted file mode 100644 index 06d36ca..0000000 --- "a/47\346\235\216\345\201\245/2023-03-07-\346\250\241\346\235\277\345\274\225\346\223\216.md" +++ /dev/null @@ -1,42 +0,0 @@ -# 模板引擎 -1. 新建一个views的文件夹,来一个index.html -```html - - - - - - - Document - - -

hello {{name}}

- - -``` -2. 新建一个a.js -```js -'use strict'; - -const Koa=require('koa'); -const router=require('koa-router')(); -const bodyParser=require('koa-bodyparser'); -const nunjucks=require('nunjucks'); - -let app=new Koa(); - -let a=nunjucks.configure('views', { autoescape: true }); -let s=a.render('index.html', { name: '哈哈哈' }) -console.log(s);//这里输出的是1那里index.html的整个内容 - -router.get('/',async (ctx,next)=>{ - ctx.body=s -}) - -app.use(router.routes()); -app.use(bodyParser()) -app.listen(8188) -console.log('http://localhost:8188'); -// app.listen(8088) - -``` \ No newline at end of file -- Gitee From 3708d612491590aebc5d697086c6e9489e86b3fa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=99=8C=E7=84=B6?= <2575984447@qq.com> Date: Sun, 19 Mar 2023 12:16:06 +0000 Subject: [PATCH 20/20] =?UTF-8?q?=E5=88=A0=E9=99=A4=E6=96=87=E4=BB=B6=2047?= =?UTF-8?q?=E6=9D=8E=E5=81=A5/2023-03-09-=E5=A4=84=E7=90=86=E9=9D=99?= =?UTF-8?q?=E6=80=81=E6=96=87=E4=BB=B6.md?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...31\346\200\201\346\226\207\344\273\266.md" | 94 ------------------- 1 file changed, 94 deletions(-) delete mode 100644 "47\346\235\216\345\201\245/2023-03-09-\345\244\204\347\220\206\351\235\231\346\200\201\346\226\207\344\273\266.md" diff --git "a/47\346\235\216\345\201\245/2023-03-09-\345\244\204\347\220\206\351\235\231\346\200\201\346\226\207\344\273\266.md" "b/47\346\235\216\345\201\245/2023-03-09-\345\244\204\347\220\206\351\235\231\346\200\201\346\226\207\344\273\266.md" deleted file mode 100644 index 86e25ce..0000000 --- "a/47\346\235\216\345\201\245/2023-03-09-\345\244\204\347\220\206\351\235\231\346\200\201\346\226\207\344\273\266.md" +++ /dev/null @@ -1,94 +0,0 @@ - -# 处理静态文件的middleware -1. 新建一个statics文件夹里面用来放静态文件 -2. 控制器,再原有的基础上加点料 -```js -const fs=require('fs') - -let url='../statics/img/IU-LILAC.jpg'; - -let list=[ - { - id:1, - name:'不知道', - img:url - },{ - id:2, - name:'不晓得', - img:url - },{ - id:3, - name:'认不到', - img:url - },{ - id:4, - name:'不认识', - img:url - },{ - id:5, - name:'晓不得', - img:url - }, -] - -async function getAll(ctx,next){ - - ctx.render('index.html',{list:list}) -} -async function getById(ctx,next){ - -} -async function addItem(ctx,next){ - -} -async function updateItem(ctx,next){ - -} -async function delItem(ctx,next){ - -} -async function findImg(ctx,next){ - ctx.type='image/jpeg'; - ctx.body=fs.readFileSync('./statics/img/IU-LILAC.jpg') -} - -module.exports={ - 'get /roles':getAll, - 'get /roles/:id':getById, - 'post /roles':addItem, - 'put /roles/:id':updateItem, - 'delete /roles/:id':delItem, - 'get /statics/img/IU-LILAC.jpg':findImg -} -``` -3. 新建一个处理静态文件的middleware -```js -'use strict'; - -const fs=require('fs'); -const mime=require('mime') - -function staticFiles(){ - return async (ctx,next)=>{ - let tmpPath=ctx.request.path; - if (tmpPath.startsWith('/statics')) { - let fullUrl= __dirname+tmpPath; - - if (fs.existsSync(fullUrl)) { - ctx.type=mime.getType(fullUrl); - ctx.body=fs.readFileSync(fullUrl); - }else{ - ctx.body='404' - } - }else{ - await next() - } - } -} - -module.exports=staticFiles; -``` -4. 然后在app.js里面插入中间件就好了 -```js -app.use(staticFiles()) -``` -- Gitee