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
+ `;
+});
+
+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
+ `;
+};
+
+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
- `;
-});
-
-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
- `;
-};
-
-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
- `;
-});
-
-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
- `;
-};
-
-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
-```
-
-
-# 网站
-
-
\ 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");
-```
-
\ 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> 文件会生成:
-
-
-
-## 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
- `;
-});
-
-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');
-})
-```
-
-
-使用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
zcmd^nbyQYe+viPpcY`1yAR*l#jg$x?B_W*x0wUcFA`JqPDj_^{h#;ksH;8lyQX(bY
zv(Jq_@xI?T-^^MwYfbov>~qfUeZ}wkT^k~`G?ehLsj*=&7@mr<{B0NvIRlj2F_FRl
zWiG5OFc>3DMPBxf7t)V5{}`H4GoItkL7Hy$A1sbJ-pY!qxbKYfM;`Tu(dNHS{qV$<
zP}6f{;}BM=mprrnk}A37f_|B~o0KhbyOL&}88qLRp>GVwt!
z0V>!AP~8^F2sIxwmS;dQW0|$W=CCR{rG^t$z%OJ*}=?{P0-DeVm=Zy|3+C~&_~loqy2LKbNyBD
zF%(+nbUh*TLuF#__wKB<3--~eOCCHe?tN=k>iG2t&f~LgIT);S!fTt;B3&nquQK6&
zJ=^PN&tX=Ba7>EZv;EZ*x03~Z&BZaBvu9|&I4Oo+e!DpLpHP*+K`W2k^{E2i^mu)EX@)nTA^+8~)X$^_
zVca`$>)sh=4UPnoRhiQJ>+$N=wOL+2*Cz4TCy`dsjo3xI^Ow#=zUYY7uCY8`8tgAi
znBn%-pPYh!t}2H4w0Ts1%a*i2^X&;u+!?zoB153D=mFCQ^W!>mKu@g3;yyjW$;~-@
z?KVHW#NazA>O(GKpCi{@8=fs%?Pk9?R$ffiNblt?R&_R2cf=<37Og~bYsKA4T(911
z{^Ohc1-XyEno!Vm-ef?N6sd8>aB&)v!dn&VIFPLBe*D8Z=OX{6IQ)huSTyGodS6Pq
zZQo%X2JsAUKE4`fB`}1uzk^s6U9v?25J`d(u%vk3P-(C^VsTe-2I9}T@mCCC4kLZG
zXY-M4@8W8s+0OUvc~HW2aPLl4S(oNI2eLT&P_*=Ig~vOTR1M#MG*v~Li&4D4-U@BP
z*jqQwfWwy6SH3p)z_LqVOBy+g#r+f4%f05{tGyo0>1Bv7wLP0JoIPS
z;qJk;-}9kt&2Qbq*k*cGM=>Cl+aE-N|bQ3S=OsI
zlGy%Me?mQR8`p7n?sE&>im0${PMi~%9=coiyZ4nC+zdz6qR)|dJq0Wo(D|}It>anL
z;1n*KjNcF{b4G%;&DS4@#OUVd;eC$k>8j;tHWpx+bObX5w!X`(>+RN+@9#}^CO+Ah
zIF59dTx$tS(cuUas$Juv_X&_|bh@mP=!>nkF@8gDuntzQH5ex1vbmoSEbg0c*nIEo082;{*Pi$T
z<+7XiXBX!L6!c;VU_rTIWnZA}<*X*8e%T&~WxJ*VUmq4&U3wZWezIC<*mY=GfDZ
z>ejGcpnxZzvMUF^bQ9#DU3M6~J2ex!taL$+fxkXw0K|QgA+z5B$j>Y1n~fUc{0*zP
z>bYu|V6!QbIh*@nsW*!bfGLPr`pN4DLVG%p9gdNaR=1g;!Go*G({;$FWU+83m)t
zW$gt+o)&zE&d$5C4EPowQQ|eMYb`>f*|JZ?QbhNveX$)6ejKqB(74a5O#0%%Bt?=Z
z35?jEVlW6FER2^Zq={Pb9`H+B#tV9;3{vvlqy{WyLQOcs3GZ?BR3%Vqzn6)d~QC{d7;fw;Nglz
zagNXVd#CfUI_dpeq^+GKnsM(M#SsP!)ni4RCyCN~DXu8n*K%NN_j3x)RqN5arV^GL
zc=?W#HwS1Z8XkdVzBxCMo%KT+pyMz;<;{MwNWd^2MYHp_!}Ad79NeBeOZZZEpykAq
z(rnVLw1M&dq^Nk`OnY(6OhT;8xeeP*NoOD%xnli=HRE)|r_t%Mc=`@EHuBm{Jd2O-
zca&Nb2xupQbUhx>`||GnfJ4?aVMf+he-*L1jd8_|@f~H^*`UR1-HK~A4O+)Pap0Q1
z^Y5cu_s_yz1Ud;c9FD2d`VK$b1-4DcGfuB|Gv0Yt8XW1WVjrDv294(WWZ>pm``zVztJ;MqxBHek
zz(Vj{~Y_ce9-MF=e@jZR!EmV<&I9F+D~LOy!PnlcyG#R3mP~#ww%s|2D(->
zcgNX_!|0~-{iC8sw53PyLkJ!yw|&_{?hvGP41kBfumQEpq}q_LJTbI-RvEcG3K%Nl3>
zC&?#LSf)x=3@+P()ehrN*uzUY4%EBB8T6;T4R?LQ+3iv2AhETw`&o+1d)+?1uq6!m
zGdCMH)&BA~TVNsTKo^oM5VE)YqpGRjoXnfh7?`XhOP~HJqOQ2^`1K5*nflh)f%Rbb
z{A=AFl6{ac0UsFEkk4_#yVCZ`SA4LF`B`Rv?iz4O^nwT7Ow(zfDvdR(pnZzZw=Z#`
zLw;MAT%QMg^09vJVcETfZE#L4pW3i{!Pn&7Dxt_uAEAbQqUFLQJxTpl9T$XOi9<
z@~pra6*>H%%FLb!ss%0#%+Gdn;u!LV2A3v^6Y>C~@P|&Wrz6u1XJ*x%2ViA3yHDjP
z!C_n6{|b{#3#@J-(VW<@Zv`$k{gb1@D3H>Ivo!IHKL}996d6#(_o(&$&@@YTBv5CV
zz*?Q?SDy&yk=X6M3G*_5#m)Z%yJA^ABS$pM{G474ZCqgV#|ZAe1QdZ;0r31q~uO8K_1h;SWE$|
zjEorp0XE3<7;kRfM2v4JgE@Ky_K%OqHoxX(D2qADI5-qSC__R>s4gQTfP^H#l!3fi
z?W(6jYInGI%{p2BMVLK@b+Q8$K)E7Q1DV&`C(<$xM9KL^-Kok0PadbrCOSRzttUsq
zL;|hhsHpNJptn3MOiUz|iQP1Na+GR?N0URCVu+$VRGj$h?ca(iF48&nEOfznp$%4*
zFD6qI2Rn_bD08`AgJMO<$&nHBDkUj*uV6IZbge*84c{o2kUQuMOoR*FMrSxqwvyJZ
z&pd!!aGjF0=l543Sl&I`(cY8VLL#G3U7Bq(8caGoXhBV@emrq>w1b__FehLY0hnbeAyS2y3!rQGm2X#oN29{MH
z>FPej`&Fn!c2nmu7sqe2n;N)l5oZDVigxPdV=td;p@3;mHJ&!#d%a*MS{1^X*XU>)
za$>@qeFdgpL&U%e>&6Ckk@R>`xU|9gnN;eMq)L^bLj=b#7-EAA&KXmAXh{h#Ixo8G
zju#FsX;pG#Mm46vN(FtNZn`XoAXn6NYMrm#E?RPb)=A0@?n@%9!-Dm>3IsIBqC~$m
z({_iaVLJx~6T4TW6VD4F<=%@!=M}awRhO@##WQspY`wDa1W(eEur$&pB@q9z!yx<(
zP2KrMgEwmBW6(bI%_WpFwQoA-r
zVX@Rz!IfC;)4f7A?U!bFi=~O|NjO-A+=l4j6|?rs7sXk+;B^j}*W-YF*j>QP?hgdVKJ1_ahE$RzKbYWmM)=TR~vDEMi!T2ei
z?@*
zz`l5$G_R!jA0Eoo#?)j{iR0eI{A$ViEXvij&L*9qd+aH{!KZsyAN%>3xIb3DHja40
z(sX}JWt8KpC)(_5;@&Imc$2J#c?Ff!WNaT
zw@-JzR<@Eaq0Os(N7TE1Q*b^Q)p+WF`ZYCLt2O7-0>uiUynAsA`qXV#>_p-$sFHwa
z0N4|b5Oaj#0?OPGPhsV}*0j()d$vX}?TLDqhFY38DCJ!GvDb=-!6!Dh@UCU06S$zO
zsgKRvgU9Bmmv06I{}2z}vEj_g>Y&19w^*U_eiioA&&bhcif$c)7y33y5R+SYF>~T_
zT1c(9q^W@)I}pF0Z~v|JaIp2`&kjrt#G73E2cGE5+e=`ehv+DuD80i>UYmWJIpIyY
z(;q*OL%$wgeea=k4ySA#U2JY>2s2P2$O;TPDlzMKocWZaPJ0Lh(QiBn%n+!M+E{kG
zx>B1@wCgg9^SL0f?&@m-ooXWPS1HZ+%55_iZC0*;|6pnM8QD&T)Mie=W}>%+$yVQy
zkX|G(0Oraqx!w^p?$BvZ%Rq{-qS7aU>|=5-tOsF*Sysu1!tDu>|0YzxaGWj<_#ZY+
zhWTwxP&F&uoWuxtY&1N0c)AMrYpx2bC4lXCMTd1QYsT+eG@N5Gv1)O$kS_LKR#48w
zb?dt_6nC@I$O0IP>a9@pD}0a{C9>^)p&$19=YZaKyF_zIV7ipW@jLl_YH#0tM(T_b
zDgV&;xYfr
z^h#z+_s`hjY;Yh0ZdN~(sf-sLYGY`Gp{HK<&x5*B{9)l21q_tq&qZ%|6vV_CeBEM6
z$?|^{mNFsDARU~n=waO-L&uR{lGT@j^cg#pKj0ibyt8oAdTEKtQE2J_8BzNnAV+UUofnB*4WMUfpk6apLbrOlTIC{8Cfv>_Ix&KOiN`2vf;jLhQX
zZObBWEcZ1JAZ;ughv9ELkrj|qj<6Dq9=ev-xDGTzQQO(2G?uzQuNqr$-TfX=k);e1
z7E8*nuUGk%hqy}#Mj2s{dxI<|gbtANu#h5HQOgHDglzrsDS$sLE{09<1kTU@)iSAo
zKQiUT_BL)@zQqG`KMB5EcAFkAb7beX|8Ctf|Ic*NQoOk_NH^1a3CsEU`J}TcwMeTi
zCHRnX{dZ@WqY~u}yGWQ5glJ}7GP7qOm1_Z)6`3-$BRVV}o#7#X93cE1Gs-G}^K-RD
zuC|hv0E*FzBD&ySz1(8plG#o0eSkdd{5n%cvQrvxioo65pw6u@8C**g
z?z=MaxjLW7K?0JF+#K-~HRgKRUct8!j<%>nf`^Z*?u2J}
z;6{}I=)yTdG~%enJT)*7!X0c*isC|0SN4DlO#L
znr4`5*qk59iO3OtIOVf1Gb@e#g=8xt8m#zaG9gh}L||V?5LV{=r0%}XM~vp8_Di~;wzz{u
zK=9`$BCHCHZ)`%sOQz@85sn30TA~Q!#vAH;Z&Wol2xLtkAZ5WNN>WDzk_5On2^vNd
zVJ1nww_i)mTpxSKHE
z(cT8L?%X-Q7SOj6^DoTWW&}Y~q*kLF%VBiELH_{83IsW2GMd^5+1J(W&Z%FsE_EhKWlymSv}t!K?WFJ&tu
zpyqKUi@n6<5#PbqZaj(gih`K}5S0d_9eP7|u+wncSXb8wV`)kCX|;v6J0j-z4bn3;
z81#;wAABu-*Ky*k)I%eU;YZ`NH7XE<
z7r-Ns=N*+746rmAF!I!`XQ#Ku_}NoOb_q7tZB+1c^Z-d6d0fm
zHO1MaaIb@USM&c!xT=dWD(
zib;(_+f!*5pqyrkriVv}MFMahVJrc0v*j*cNFQY%f
zt#MW3SZxPIrqZ7eg9a^od&6OtKLU%qULWD*k9%g`J|s6e(|kC(u79`81+{!-i~re2
z2P(;DAD=ZTgr}z8vg%n$zm+}3E!N!ofZwpo;4e^{==b)2A*$tOAudZrxcyggvBA`~
zg6tVZtNcbZR58L5Ir-rrmXlH*?DT!};V`gO!&uwME1{&D(|cS0B=y80)~m4IBt%*
zK1Ud!Mi4+p|5=iR0E}WKK%;#|SDnNGus?V!6RX|Kt?iqZ7i*(ZW0Jnen6ao&ElUIE
zD(2P`gkTC`pK6IL++pWRBXumH1LbEI+U_qj3c~`%VKqeDWN&UA(Q-q0pCdLmoH?61
zE;rOBMV4tAc<*O%Nv@A`EJ9y8i5q`Z?02UUdI5Obx4wsE2ayyect>wc&POa4JxrgV
zzSG*ee-Zuj-Fe+%JcGqk8}m>DCO+P4fIUAhy%)$Bji`ZmsB*f{>2l+_zyQW(EkF{HX_Rl;!WA5FBIW_|k4v6;Zta0vc=1M}jKbLJ7*G
zqoLHcBfC}&MMMGAaTdA2yt7!%>2*<1M|5DO5Q2DlDMLXR7-W0KjAVE9VN`@(M!zfy
zJVDs?3Krg9WJrM_nxKPZP7h>p^O4}LxXA#4zz2W!<6RO4rCkSfPi-bkNj
zmMKfn0>N?0zhF|8cliI?KxqZ7TQr_;+Pq$uHRqm9NNy+~h|E+3hKU)E+flU)%{$3+w(rQ
zn>oi3{5z^UKP{(<=CWtCE2cgKZnvX=2rJ;lEh|hlevwz~DiGG>3(CgTxTZ?%2!S6{
z?z8*@dhH~Z3I1%#K$bJHEk!z6IIc@5aKx(BN!bJGtOgKZgf;((H&J56g7T>50E(u3
zP>U?7P)Ywcv}y&>H_im`Qup@W^sCh`ZIzf|N(AuRPn&JY;lO8ukPhLdAS#2ptt<+7
zV;IzbH6b_I$4o0CI=cxtrk(}x@G!x35p>|4tcW1wfNYze7xpado&XK7#zzuap7D_?Et}uQhP&@XCO6xD9iNi
zV;5%9xp=9li*w`C_~>2&;kZrp~{HC1t;FUoKkoUOp7ZSyXirffLD)iUezZL%ts=6as)MyA=
zaeeiZ8dVlUSHQVM5wWI@0E;0=R|P1r0#@2Wi>n(#$_nb@=$)>=zZz0OL?$5jpoRfL
z5DzPfK&F>qOsqa?)=Wx@{&DlA^euXi0>Rx_<8rxC(;aY
zA%nRK@Plw~9*rArRNPp_%m?+OV2}fV=)eCl9+^7O-57zKr*Z@LE>i({`myqPHl{k*
zW@LN$huxy?0`Fd;IL{6A99HR*5Z*s!oq3{!x%LWhzrR4RHvqmv&$t(&E@@%?a-gqK
z5UF~Jgv$Z@Tn^FCz`-{D;+q=)lzz}-Q-UCnW6E9i?j>;pRtQv)(_2*rO73?90hNKj
z0R)dR3$3h0pv5InE^U2{bj2))UJnm&Jwd|Ve}Afr7|Y)1;>p&{zo
zcq-?bLZSgB4K|k2_d=>LGC(zdZzts(Op5|!HJQBHD*K{Q*xu7Nul!FHxFte>P?dBj
zx}!vM@D_-iqb;CQRx1RMSLwRWd=MFz3UnzD_V7jZv4`kNX%IHXq#qIH#z+p5Y6`wH
z6F70w!Jd6za>23DNat+F|Jb$e7p~^6c~4ZI*WPoBwaT(OsY>0$GVh~z{(;N$sS
zPQN~8VpR+<_=;%+hbZSix#fBhsmSbqkjq`ve**Dbh`j!b%3kXon{tBlOULo$YX$Gp
zgr6Ll1L_PLFpOV-8-obvPX2!~&BFgeM3;EwoA3ylYh;!|tXe6EE64R>MM0q7HU)4k
z4Ji$tAOuI$hb{FPBIVcc;;w@IgXgaBmqvrQBzpkf?)wG`Oh5pqHNp;)GK^#qL|&aH
zCj}%eN33a~H-~lYqUvBc2NZ8T9&jA=XYk(%I_yo|Dx^rJFu{>LbN;jmJuu^c#eyxkn*>#C%0FA7
zz8oVa?!`6=v(4(ov+!`nv!btlGrfRu2c|W_1$7i`M|w$zBS51aze_7$TPR-gMeRKV
zX^d+RCqZ%xqAg?Q9V3B^Im)ZO-uuLb0q5!l`8=R&j+L)%mwfR98al(WhcJZ@6#ZIk
zo^8hUcJDl*pv8!kp?q1mr9Oa5+np5Z6DsADQUF^MMzgY8+zBuF~k7O-Z
zD@&lHY(H!!JOddV6u0`?k2}lZ0jYMBt4fCd{G>m7$yb*;Kah*S`izaGAbLYU%HNx`
z1kaHnV3#KD7gJqc*(!@=a43iJloBu?zoLQPKM_$uCGcbl1$5!&RFLARMAZe#=ha{T
ztXxtg+=KBI?Ip8y%HXpboKSdP$KEn1!vZpyAV>!$sgbWcm#;=Y%Pd2-G6ZCSo(i<7
z0)b05f`>*u0_Lx=$QBvlrj)0KB>@Ivxam9X9+4)9*_#n#YxHczkw*2UC8a=scoId~SSRk8D_aM4N5ffNWPpTAoW>gs6
zmwZ{iO}AYa>HEOh$W~&*Z+i=1A{(W82U|FPb|Ej+eQ8_VGlW^-NeeHnrX~f{q9B6I
zaFFuV@1gS@+h#(U?5*B-Hgr-nZV;ou1l{-Iv&j#v$P{F|NdDt$Hy0MD
zXqCD@bewvb%V-a#YbKpW22lW~d_tYC1}@(?Ihj~F95>i=
zL2Z^Or`hoF?31lZUcb#V{USYO++I3T66V7ia803o5|NWKu8}i0-N;P?;odauFQQ&J
zqM?YQk~>zW@dyDyp~S;OG6c4|%%~rwS6FIsnU~)KL}Vz-+k8N;muEuJ&H(A@@_-9N
z8-qjJwcj1LHI?cq(>5IJc3}R>n4SX*{RO0w(c`2g{JpCg65VzFHQ0hk`+p05TyE^e
zH%^j(#K`0?dz24+-ZPmllKiuTg3M5A{7yqzyl}ZhmQ`vnC2*JIKf0IvZd>hSan5$9zC&5l(O(Z>le(FF{}kK*F4>z4d6_v9
zPfq{0I)R7@3TtAPG;dMw&&1;+VafmwU&gI_I?7Gs^vtv4-FdjzQTU5gf7;g_CC%r|yZ)E~u%w;Y
zc9M%A>9L?~GuV7vQ|JBDXI`_T&@||BL&lN61_w!i0l-`LgOBVtxddVN+WTF#AnbGa$)1cdc|Hf`9~ed0C=ht>YKr
zzk@EJNn3-Mc)))xL5>92Ly)z|C;F8LF#X5fm4D^|^1=t8v>Za4
zUcWY{`sbAxNgs$gL9h#Vb;$s<05k(Aa3Yjqfv^;yO~EA*bl_1}x@d#vu{WL|5q6@3
zqy|K{!KMb9VyH=B;Gk~S(M9KiG(pz5GN2t(Un{C2QUgXfP~1azwdW6Pm>^FbWp7ad4%8?to0
z|FGNzNUD&{unXKa8G6S4xxxt0kEB-(6#+2Q0-uev`>z>C$}yDybl?t@UNcXtJrhJx
z3gi6bzaCv7wicj`=zk;~KzIdR?g8}dNj{2}lx(O@U$4%>I+ZpFaSb&e3-88@2U+3wk9MyZ{vKC23gp`&t|7n%HGu>`vu3>Ou|c0$`$
zf6t1btS~&MRA72h4c!Wp5)~ELh}&Pd##W_DoDBuCap&+zZBk-YHCJNe#b?iME?_7Q
z!lk`=gUf$@RLP>E(Go-N>YqDR69gBq8X?O4UxP2)-!JMveJo83VbcKjXB;mRF+VTr
zFg*Djv%~3IF9z%_GUEwb?*AauNEtVNwv-kqSQG4fXA)ilaxKOM6mKiXDrwa-OzK~S
z$n$t;v$(tRZP7g~>>Yv&JfdKmxIHWy`w5OcU66rVT*%hO&T1GaEbzjIihXW50E?SS
zC53Cck>bq%T~Q0^2DONPZKd$UMi>4C=i6B80RqP2$^$a#+WPxGuZVVedLj;1rkU}K
zzXnhkezs$yZ~f~zhi_ie`#|~TU6Hv?V?~LSt&u0&E{e@coAkCT*ci?AJrUi+E#5rY
z5haTS6q9$##)Sp>?PNy8+Mj1cIllfvHKXT$Z}iDLdcXq#{^_BjSG>&5jI{WkHMf-9
zjpJFjd9N2oy}eUb)|nh3|ICY0*WR8|ltQ@TD%Co@tjfl+cgk+ep-D37
zuHibDyenOke28-J6(=mXA7>gWy41LbdJ1TIuEAOG896t)ofP~6Putb6`nkDT=!^v{
zXp~}QdFxHpGY{_^#`MlVEjZ4Qpj)9!9g4kk|Cu!|fye&Y0Zrf<$Kmu?%6I#p?zx^u
zs=~K@)wB5|j4U<}wVG(2G@KOv=+3v1*g)!fc;9-~{~b|%cF=4r#@2z_cs%HZus!ze
z!p-%uH{10X56C~hq?hHT-8X-;N`+!VW*C!hE3&2w`~5cl!JT+UO8h64;e2BJi}w5T
zW{KL8L8c-{FS~w~>zYD94jmHz-
zSDc)N-F9x4ilOzlx2a#~h5C8(yc7S|lLu~=lw<(LPF+}-II=It4@!~Yz*G}=l>fZf
z!s85io%`51YI}QlFqb-l*aIQ__*g^(rCgp+IuvWMt)XbA1X0Ru{r42OUX`f_4{)wG
zr+3tT%cK}|9m4tZJyHBspSA#s171a?BA-1IiI9JNfmKtEMA6@g=fF2#8B+2>Ba~jHtt>V`P)`vG_8H~7%ziOy=zT>iRfADGXPGx7rf&8<>te#iMTiFf|
zuU-N2>`Aqqt&Esa{Og*Udg4e_`j4Nh15b9)-{)W*&*6l5y4zh_fm3d0+YpNam2?sO
z{K^XkbmN$qNHU|t?_&>^NQo}3gx*wqdsGJT_W3P6D<&jNc4AKZh?oZ#YbT%lKSv3(
zRb^&YVA67vjIo96$O%{&va_C?!A{@owk&N&;KO2HXfb;no^(ISl#nx31K$EkJ?baJ
zzYQ0M5@L3K<9z$(u|yt0vwAO0f+FxKg
zJ>&N<@RG-HmE#-V=py&O@N#~pgz13Q`9M11J-+8jg3oa^A@Yl{!-uD2o+rcBAvRs0
z;W5(TiRF*#tqA;YA?ANfsDwpEeg6IyzakObP;vTbfKnaFVe_oG_H{E0_io&0hS#E@gdb+QS)}
z#|aZ8<8?`WT_gI$T{6hA#Z+I%j1?%V3JfY%DvV5INjB2U#z6x)XxH?+7&jLiz6+o$
zPbse`nr{J?%d+4Ed4mUNAyz8|Za4}oQQJ7ab!2Gd)vH{dl)v}8O*kVXf+?A&S?B)9
z4pwyCeT^bv0Zht-Rz^-W@+94$1EXrJ*F3ye&cS?9r?)J31M~bO>hzpQJ0aZvjiN5o
zzVe>n<1UlrGrv@cOcuWHf$a+;zDhz*L#DH-$3u&)_5n7xL&;Jl=>o+ov4R*A_xz=v^M^-$q>
z$is0JmL81p?Djm;x9d}Cs#vBEA8sG-t;t2y)YRN!WiHIh$$96#Iy5vyyiX`s`sNJ*
zK7M&w*;lN~1Hi}TSUf&HX3Oz0ubafd!70-#NxF?$Szn*_@xjlXo#?12QUxSlVPT9`
z^U80Fi=wpnOsK9>N8d;?`}DN50#3)YQ9rdRMG%Z8Q=85j<{B*v6fIYLDI
z&wY%Ir{VcZ6y)R%LWU+LGe3U(Xl-pJ@IP$#oOxWFmv<$z&+lY%*6;hP;_JqZy9*2#
z_4W0K^PSO-j*erxIyyV2I|IdT8JU^I#l_DxtQ8a#9zQ<%@q>&0+2yC>mYygYX?-)|qDcADXCKY+ndaiG5o
z(5aBxv)3=aNh|67*}T$}p`90O)4TneH*dGQXgr$U78NalnY_@lv9PePv|MbOJ=*|6
zo6LE)RBtVoM%d2Yo`69Lw{_JWn+b-=1Uo!C?@8qW!rXHDvxhu-@l|m)H4RN>TAFh4
zFi-PV5Fv8>O<+RcvXzaE2HM(z#z#j--EnkP`doS?d&2LI`kehwCf&u(;7SZC9F>|X
zDj&f@UGeF}+~+$Y<*x*uq-SMGJe==%F7COsyzIXf%y8%K-PDwnrNzZ@huV>m5yLts
z^;@?dRNYh1=1=eKX7Kmf`#!QZ`sS{#uJK#%M-Lu6u(VWAJ=$p6wBk?CIo~Y?Z9a&m
zxE=jmOi!m`LZuH!Y^s8+tgKLGr&M=5gS5wwG&9QK;bEc7^@sjjGUY|rKR$5
zgn6%D+X1ok^`$!@B`0@saBy&P`Z6+t#Y9d{ZsPSdw6CvE+;aoyR%uxo89Dh(gPW4-
zs*6bWlMT^tq!bj(i;F;vCme*BF2EW6v!Y$i)KU_s<
z!R9tSS|i`M8uGHV^j7ik;^LzJ=>miEbmOCw{dEVSrpn6gx0}A2n4ovqEp>HA2M5#m
z&dyGRVIcnY`Sa&HckW;^agp~74i4UJ`I*eD6bI8HMT;N6zH%imI1fl_$^EnMy}h>{
zJ*r9Kr;i6FgolS`w#B!pprFI44kaxujh&MdW$xgFn>?D(9HvBq;`#=?&43fcup>9^1iZj&x%&YmuTKj(nk
z&d$w!wmYl|Os+<@M5H8tdQAIQb`B0%4L`qgpawot46X%9a+u$gIZ8@OqNAg?QA3i5
zA$y|6`m9o)%cF^|sUQiQ=i|qZ!9?#TfGuxscBq?l6r$M>6cMSfsL;~W3(Eo)?d{qA
zr1Z)7K&c*Q05IU5v%Yt=Nxy+JA)}z6AR{xoYj4jx
qQqbNaU@S<29QKX>X@`B{&rVp-TIjNC0sd+TOhrLMzFf{U=)VE_P{KU`
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
zcmeHMS5y;7xQ@%Nt{{kl6agc$f=CB}g+NddA|hg=NtcpH2_S@)05(ASA_CHvs?-ol
zs7aI-I*SOQS3?UVB$R}bNriwDsE$XmGx^W@Q3;r@Cb
z&oH;YRxMz4Gg6;4PSD)v0$#1F-w%lmML1o~4Q0f1OMwE<56Zqj@%6Ua{Se;J-PADp
z522*Iy6w}?0oA@9h8ID_k|;`L@IlO6G5jRp;nn5%oZB_hGT%MW++wP9<~N)jT+@VBTA!ngakg1RCGIt4{MOsZFmr=L)X`8b
zS1O2hK%iCUp|UqMsN-q7e3H`;h}GD8pN1r9D)7JP}@)Vg$Veq
zpt(388@tLn1s08XInen?d(;o`KtWFLzRsnNnkHz|RZTyywVE#`H+C{|kS3BbnH3El
zuFgC*0}J^mLC{0i{;D`Fc3K>2TopL6ib-C$tupgnCR(o7^}$bEID0f~|E886g<&Ij
z7-w)e!eaZg{buqwS>r7iDm1d(&?E-O9+{Y(2@34XMO2uUmUVX5+RaPSm&YbQNyq8TL^hXPFH{#HHg
z6xiNuwPY~MzPrGJ34;DQ>jdIGu~CC?Oj1GZ`eS_bs@-lW06S!Vv6TK~5A@~%Js*lw
zJJ&JW59EdyF9EUx(_C|`w`UTi2BKt%Z>-JCB<)`sgJTpkg2LpQ-Bt!C|Ej+_m0Yjn
z=`uQ$iuQkz4^e#1>aSf|%~%nBGv9Iefg6Hy#0crEaI{ZC+4=5!`iC!a1DtB{{gRj8Ue1b
z2M_!O9jo0PK!+>OIn-2rDOiG|45H1@fyS)JYgU?`}JM_6K
za{PIPnUKD#420La%93qrZ0z&&+B*y{G5wOR>cyv~+1)y4&q`(PMmHbyD)6=^78|Bm
zIj|v-mDc~eQL`;a)yP~l58XBBxO;)@WGIK5XA7Z;arUWa&G*QxuSl6a!d`z&UEoEP
z&fFPuViQo}Ec93IJvqs@#*^
zhjt-5;d$=r;m?xh*tnh)TJ6{R0%`boFukKnw5Ewwn>(aAd05YQ;+W(U?%_rH#*Xx5
zG6Gb1=Ib;GE%JQmj6+IUV7lA-w#vh>&si~`%iFgq7OUM4KQDbR4eb{x#Pl%VnzI%0
z1Y{kvO!XG`38}-4pw#5@k~dNUU@1J~tfS@3%^WgFM*B-koc;P-6RoOc_arM~!S}~c
zW0~Q*fv3n`)MpisgT-w^yZS%t1}b_a35Z_$nbODA_~7GjmO2;LWeyq(uv92%Tv(>n}B#-o*bM27cXs|+0XmiahW}>&(9bH<|CY7`=t9Q62naMr
z9Z?;L5N*ya)Q-@8-l;vJ(^Ch$Yrv}C#>rO&r#bY+xi0hE=F9MF{il%ys&bpS)a&Ei
zvRsW
UCSvebFFU2Br})#^s*p=bIcz9qQL#r
zIotggXt}%5y7(H-n9qQokbg#onVggR_2X&x4mfWcJ6>N>Cw8
z7kXQ)*IhbGU5Lg#DnmV$e9wLI=@s|x5u@}i=H>G@T-M_YNePwbv;y@c#Aj!Dv$>&f~joNTZMVbbptu*XHGJ_?dl9+U3|?aFA6UhJ3gA#QcwJwsB?605Nb
z_4>mjI@)DoJb^!&;YOrwWTX9ukUp(gpFo_9P2*(XN`$=PYA7k<0Pe*px8(~m6EUA2
z3hk(=PUC+B=k(c@bF7Q}!Qn1?+gTEFG`%g@(J1S7rGKNTy6wHC@2L3$OYogAg(^j&kS7`U{5e(U3r4_D*59wjydlWVZtw_j`9&lb(mPKRl
zcD$)xB1VXjR+1w0@v2yKu)s4duYES5MHU}2Kw4t(9%sTq>3$*FTX&)8@$a{Y$0ejn
z*4P7TP-JOV4@rv0N*g0LMRbMTS8TzkYaS(k=Hf7h^-#-AcJM(E&JQM#FK-*kUEbxE
zRsT(46R0tB2He}u+-QSFbxZCoQ|VXiQO5{$nc+vk*XbW9x+k~l`i$|?YekK$(|df<
ze2|Qka~-m-LOYNehw6p4B2HU&O=hv$2cP!j4f$4v)is=w}2GS_HctP*i4V)-Qmkx4hw(6ed!}bZ8;wD5sw2rDABFQvawetgzwE
zeZ+Bu?-dU>%##tF6}()UCQjjaVul!4ukI_f{9HG{>9tsiUZj%2_Ke?xL95~{YBx35
zjb^H*uHJfirBwbv6!1m4{p06ml(8-|jHck_0~`nX;P%}mby>$&C_z#jEZ_bK@b#!E
zi*e)F6|V?u0p@==e>xAiEbV*-a5?{m`OZne-+wQ^>Ek{mi2-h2(bQS`2mthmmHZ00
zdsmF-UueE)A$ZDsR6}(^g-~C{Y)QRDVI>9z;pUFx%Y9zvH>a(x!eW{u;a8qq+*per
zWSmvedT_9{Z1*A{aId52S=bmr@w{eJ>ss8I&iVO=J$UjXs|HjcRP16rBc0UgVq
z8Lrm%
zF=p?`)8%pH1p~^xx!n!QGf}=h#BcLbKFiL%ukDBZW<&gDi@cFL{fuArr%WA@4gnh%
z|HN3f-xk|Q9B`Z4P258K<|Lh=adK&Wy+92i0SG3Z>jD7IpYL*drZfyYFAd=PKlt9B
z<{xg9{ojbsHXW3B{!S-H^NlrX!t0pPlW7pcC#H2+Er?-F{mG~TLIWr?^dNRJRkKn$
z^BKe26R!8oO>i0evHPESr!xa(ULm*_`VAmAS8&GVt^SmYA#tqnur%z+M{99UEW&7|E2Rmx
zax=hZPD}zjEA>Q6;R!((J{b-{ea5}W^`<0%GOOZkWpiA@Ho}(@VG8Bda@h1#dm#{$
z8oT!)7-xv}GL>u@Q_1>0UIYRE!}6I;s-C@6H-eVFRl*wCe1EK{#JjgrTjc
zO%~MU3tB2^9)S@$#P#*CYf4lUkH^u
zc|gq*KEF2;=3S?)ZNyw&jy!y;^FzpX$^aMyzR{4r*}`0gQDEZgepFR1etdS)t`2(n04bYZ;o}ptcwUa8iv%@vd29TJn^>jA
zCe}A}cx57K-VFnGzXKlk47kz;r!!HQFN;|BmD!1$&*AoPVoG!@A34ycxu6JN{t}d%
z_w*5bC6nO~52^l%It!Qgnaa#*>QMrHY@wAL@RF+48{DU;(<#WgZXCT!q6ud{n?BXF
zR%XZcLL;G9VB-NfmlhCi52@R1_*-6k{U1p=DSFK46ux%27D2IH08VXh4%4n&`T=|)>}gHRgH
zq(PQ!`sl|gO!JLauME2(iB6hfSQQ@McyusLUHq9wNE}U>L_C2%7P0+{lC2wWk9iSf
zvnpjUQqgAJx%Yq`7P5q&F0p761H_T8WN3^DA+c#^8Y?FxC2X&s0W13?j(s4XqRx#-
z@X=XcIn=?%23JS6nXN*D0vZ$M@8w`ovyB;CrRGpFBnm^kO^gmh3(#B^+7O3aWZiU!
z1Oy0+;x(G%qM~a-{4^DCD_$uxPI;loVZ{Pe;OS}GQ^aZ-=v${vSd7g!QbCxe$4JUE
zm+hdcEW^#K&n}wHpK`jTl&Cs3CyKnca&FYk6c_*rnivy(Tiie+Ke@NtZp}H;XQpp@
zJ5H&a`N9&@u;0Uy$ZvJMb=j}DJcR^9vkbS(?_<8-=>#g8Ad(1
zm70EJB@N+=V()AI^d$j_P->=||0Rjc(Scw6HH6=~O*}{3(0(u%J!1X4QA+!5?oG>K
z$wRA5+X5Uo;)Rj)F012cc2&MPL7mTPz^rnAD{!de9f!!sM>x6ob1K;T)fU+SR>aA)
zMQnCI#V!@iydx?j9{-xd2+g`!QW_*M1Mf?Ro@@N>eGX8qDgqfAmoHT25<{uzi(kH<
z7d>P+R~Q`T6I7bz7;R-RZ;xy58ccF5m;n%)6NhWaRx6cxfq>L$4F*snn+LmK)_uQ`NujZ7T}a7LM}l6rhtAwsm<)GPEG#
zi~jmSa}v{Jv;ku3oDf?A8xSQR3RqREn=LYvX-wmwN0sGcT0_r2ZMix?HP3YY6L8B{
zz&SW%X>uG}xyz!t#bHj|L>wP178UZ5U!dM6iCS4J{=5A(#G_dghV>5r8IeQ_Eis-j
zvY}nV2!FmA>CN((wNQ`<|H;4=9!s#zhCDA!h!Qz!vr$0`~VR{|6TfFim(bns*Me1V(e(SgTuAI40NP?=F2=G7`yI
zKQUP@?PIy6Jh#5x+}SmET)cCm4smCq;P)W!_x$$k3-_21QWXEA(4kvwv&~^$nm{4r
zsxozLJ$`AMPP*gi|A_38!jU_(bH|)6BzP~aPAr|HyHZkGEYrIjz=CnVm+k1ENQ-F)>S`qiKrJ#Hj6Hg&MpgZjv`R4dzTI+GB
zoZ8z++k(8C;{a6VDck&)hT;DiW^ROSoD9dg1^3q@GLo0z*I&_vT12@Tq4gp)TM9}{%7W=Nv;
zn+XDJ>sCH7_Bj&JhJQB3
z6aKiWd7ls^4#@u7NVrQKv43++D$Jath^=UGoiwQ1O}D#1iKqr)8*<)}h10Z+Akc^n
z9PJc_Q}&|m9{VZ=-Z!+^gQt!^t3UnJ#nkR&=?!s%no;Y*$~Fn@@~cW899HbebLk1l
z0w&%YL;Y}X?qjOos2QjbIdm*%-a=X0cc3O;Z9`_HF!{T#{9k|Bdhj#_kyH%t_!w0)
zI(ArqhG`BrqsF__9)2<1XJRVs5&LI;2q&0G)B_&mQzS30#?9hi4`1zEzgXB*>c^Q&
z{)S&&lX?$3mSS1lbAVz+r_`|teBdAFb!@TcbBz8K1O4yV@c&l%7w$6uf%qSY|8e?X
idKmcs$h6Ak`4z##=OM1Ba{P}AFfn>?r|P!ToBsiY7v^37
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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