蛋疼のNode学习之路(持续更新...)

蛋疼のNode学习之路(持续更新…)

Day1

Previously on ‘Preface’,对于一个几乎零基础的我来说,搭建博客的过程是十分痛苦的,这现在只是一个简单的修改博客主题和编辑的过程啊(前端知识盲区o(╥﹏╥)o)。所以我决定花上一定时间来学习Node.js。希望学完之后我能更好的运维我的BLog(顺便复【yu】习一下Web相关知识,HTML5,CSS3,JavaScript什么的都快忘光光啦)话不多说,今天开始第一天的学习!

①那么,Node.js是乜嘢?(what?)

解释:

  • 不是一门语言,不是库,不是框架。而是一个js的运行时 ,通俗地说就是能够一个能够运行js的平台环境。

  • 可提供服务器级别的api(需要引入相关的模块,比如文件读写需要’fs’,也就是File-System):文件读写,网络服务构建,网络通信,http服务器(http模块)……//后面会讲模块系统

特性:

  1. 事件驱动
  2. 非阻塞IO模型(异步)————————————-啊啥是同步和异步?,又要去恶补了o((⊙﹏⊙))o
  3. 轻量和高效

与Node.js相关的东东

npm:全称是Node Package Manager。世界上最大的开源生态系统(大多数的js相关的包都在npm里)可在命令行通过下列语法获取相关包

1
npm install [jquery]

②Node.js能干啥哇?(do what?)

  • web服务器后台(能干类似活的有Java,,PHP,Python,balabala…….)

  • 命令行工具

    • npm
    • git
    • hexo (这个我熟哈哈)

③参考资源(resource)

  • 《深入浅出Node.js》(偏理论)
  • 《Node.js权威指南》
  • JavaScript标准参考教程(alpha)作者:阮一峰(惊了,英文名也叫Frank)
  • Node入门(fen)
  • cNode社区

④安装Node(install)

会的都会,不会的戳一戳下面的教程:

参考教程:https://www.runoob.com/nodejs/nodejs-install-setup.html

⑤模块系统(Module system)

  1. 在Node中,每一个js文件都是一个模块,另外Node中也没有全局作用域的概念。

  2. 在Node中,只能通过require()方法加载js文件。

  3. require()方法只加载其中的代码。既然没有全局作用域,那么各个js文件之间是模块作用域。

    • 模块全封闭。
    • 外部和内部之间是无法互相访问的。
  4. 其实每个模块中有都一个exports专属的对象,该对象中可以封装各自模块中的数据和方法,方便其他模块应用

    • 举个例子

      1
      var demo =require('./a')//调用模板a,并且获得a的exports对象,并且用一个变量demo接收

——————-分割线,下面进入简单的实操环节——————–

  1. 启动一个简单服务器
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
var http = require('http')//创建http对象
var server = http.createServer()//利用http对象创建一个服务器对象server
server.listen(4000, function () {//选择端口
console.log("贪玩蓝月服务器,琦动!")
})
server.on('request', function (request, response) {//server监听是否收到客户端发送过来的request,收到后根据回调函数再返回响应
console.log("收到请求")
response.writeHeader(200, {'Content-Type': 'text/html;charset=utf-8'})//<-----------解决乱码的方法
var url = request.url
if (url == '/') {
response.end("欢迎来到贪玩蓝月,是兄弟就来砍我")
}
else if(url=='/login'){
response.end("貪玩藍月登錄註冊界面...")
}
else if(url=='/register'){
response.end("快進到貪玩藍月の註冊界面")
}
else{
response.end("404 not found...")
}

})

但是第一次运行的时候出现这种情况…….

第一反应:响应头没有设置utf-8编码

不用慌,在回调函数里的第一行加上

1
response.writeHeader(200, {'Content-Type': 'text/html;charset=utf-8'})

运行后的截图

当然一个网站肯定不能只是傻傻地响应同一段内容,服务器必须根据浏览器(客户端)发来的请求信息来对应地响应合适的内容。

实现方法也特别简单,加入一组if-else的判断就可。(详情见上代码line10-21)

实现效果:

And:

2.模块系统

解释:在demo中引入模板a—>执行a的代码,在控制台打印starting语句—>引入模板b—>打印模板b的opening和closed—>a中的引用结束,打印a的end—>打印从a模板获取的值foo=200

从中我们发现输出的foo值是模板a中的,而不是b模板中的。这也体现了模块作用域的好处:可以加载执行多个文件,可以完全避免变量命名而产生的冲突(认真脸)


⑥相关知识补充

  1. 同步(Synchronous)

    A程序调用B程序,必须等待B程序完成才能返回到A程序,A程序继续执行……从流程上看是一条”直线“

  2. 异步(Asynchronous)

    A程序调用B程序,此时A不会等待B程序是否执行完毕,而是继续往后执行自己的代码……从流程上看是出现了一条从主线产生的“支线”

  3. 回调函数

    A callback is a function that is passed as an argument to another function and is executed after its parent function has completed.(来自谷歌的解释)

    通俗讲就是

    ①你到一个商店买东西,刚好你要的东西没有货,于是你在店员那里留下了你的电话,过了几天店里有货了,店员就打了你的电话,然后你接到电话后就到店里去取了货。在这个例子里,你的电话号码就叫回调函数,你把电话留给店员就叫登记回调函数,店里后来有货了叫做触发了回调关联的事件,店员给你打电话叫做调用回调函数,你到店里去取货叫做响应回调事件。回答完毕。

    什么还不懂?行,还有个例子:~~②约会结束后你送你女朋友回家,离别时,你肯定会说:“到家了给我发条信息,我很担心你。” 对不,然后你女朋友回家以后还真给你发了条信息。小伙子,你有戏了。其实这就是一个回调的过程。你留了个参数函数(要求女朋友给你发条信息)给你女朋友,然后你女朋友回家,回家的动作是主函数。她必须先回到家以后,主函数执行完了,再执行传进去的函数,然后你就收到一条信息了。~~~(单身的看例子①就好了)

Day2

今天主要围绕模板引擎的概念以及应用方式、渲染这两点展开,并且相对应地结合两个小案例来更进一步的理解感受它们。话不多少,先介绍主要概念。

神么是模板引擎?

模板引擎(这里特指用于Web开发的模板引擎)是为了使用户界面与业务数据(内容)分离而产生的,它可以生成特定格式的文档,用于网站的模板引擎就会生成一个标准的HTML文档。这就好比于我们的写作模板,我们想写什么类型的文章比如说明文,记叙文,抒情文,应用文。它们都有固定的一套模式,我们只要按照这套模式来,往里面添加我们自己的内容即可。

常用模板:art-template(art-template 是一个简约、超快的模板引擎。
它采用作用域预声明的技术来优化模板渲染速度,从而获得接近 JavaScript 极限的运行性能,并且同时支持 NodeJS 和浏览器。)

安装方法:

1
npm install art-template --save

啥是渲染?

在电脑绘图中,渲染是指在指用软件从模型生成图像的过程。而在HTML中则是指如何加工并且显示出最终的HTML页面。我们又根据加工场景位置的不同把HTML渲染分为服务端渲染和客户端渲染。

互联网早期,用户使用浏览器浏览的都是一些没有复杂逻辑的、简单的页面,这些页面都是在后端将html拼接好的然后将之返回给前端完整的html文件,浏览器拿到这个html文件之后就可以直接解析展示了,而这也就是所谓的服务器端渲染了。而随着前端页面的复杂性提高,前端就不仅仅是普通的页面展示了,而可能添加了更多功能性的组件,复杂性更大,另外,彼时ajax的兴起,使得业界就开始推崇前后端分离的开发模式,即后端不提供完整的html页面,而是提供一些api使得前端可以获取到json数据,然后前端拿到json数据之后再在前端进行html页面的拼接,然后展示在浏览器上,这就是所谓的客户端渲染了,这样前端就可以专注UI的开发,后端专注于逻辑的开发。

服务端渲染aka后端渲染(service-side render)の优缺点:

  • 优点
  1. 减轻前端压力
  2. 不用占用前端的资源,比如在用手机浏览网页时,浏览器的负担减轻,因此能够节省手机的电量
  3. 有利于SEO(Search Engine Optimization)搜索引擎优化。爬虫能够爬取到在后端的HTML文档
  • 缺点
  1. 不利于前后端分离,开发效率低。使用服务器端渲染,则无法进行分工合作,则对于前端复杂度高的项目,不利于项目高效开发。另外,如果是服务器端渲染,则前端一般就是写一个静态html文件,然后后端再修改为模板,这样是非常低效的,并且还常常需要前后端共同完成修改的动作; 或者是前端直接完成html模板,然后交由后端。另外,如果后端改了模板,前端还需要根据改动的模板再调节css,这样使得前后端联调的时间增加。
  2. 占用服务器端资源。即服务器端完成html模板的解析,如果请求较多,会对服务器造成一定的访问压力。而如果使用前端渲染,就是把这些解析的压力分摊了前端,而这里确实完全交给了一个服务器。

客户端渲染aka前端渲染(client-side render)的优缺点:

  • 艾达王替矩(advantages):

    1. 前后端分离,前端能够自定义UI而不用去过度依赖后端
  • diss艾达王替矩:

    1. 前端的响应速度会变慢,这就是为什么我们在浏览一些网页时,经常会*“爱的魔力转圈圈”*。(后端的渲染速度要比前端的快)
    2. 不利于SEO。目前比如百度、谷歌的爬虫对于SPA都是不认的,只是记录了一个页面,所以SEO很差。因为服务器端可能没有保存完整的html,而是前端通过js进行dom的拼接,那么爬虫无法爬取信息。 除非搜索引擎的seo可以增加对于JavaScript的爬取能力,这才能保证seo。

小案例一:模拟Apacheの目录HTML(服务端渲染)

在此之前我们还需要用到fs模块的readFile()和readdir()方法,简单介绍一下这两个方法:

  • readFile():
    能够读取指定路径下的文件并保存到data中,data是一个二进制的数据流(里面全是010101),因此要将其转变为我们认识的字符串需要用到toString()方法。

  • readdir():

    读取指定目录下的所有文件夹名,并返回一个由这些文件夹名构成的字符串数组files。

    1
    2
    3
    4
    5
    6
    7
    var fs = require('fs');
    fs.readdir('readdirtest', function(err,files){
     if(err){
      console.log(err);
     }
     console.log(files);
    })

操作描述:

  1. 加载必要的模块fs,http…

  2. 设置端口号xxxx

  3. 为服务器设置响应信息

    • 读取模板文件template.html(目录显示页),并且将要替换的区域使用特殊符号进行标记,这里我使用了***-.-***这个标记

    • 读取目录列表的所有文件夹

    • 并将要替换的部分用一个变量content表示

    • 用forEach方法依次取出files里的文件夹名并构建content(见代码20—24)

    • 将第一步读取的data转换为字符串

    • 简单字符串解析替换操作,将template中替换的区域-.-(特殊标记)用content代替

    • 发送响应

下面是一小段代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
var fs = require('fs')
var http = require('http')
var server = http.createServer()
var wwwDir = 'F:/www'

server.on('request', function (req, res) {

fs.readFile('./template.html', function (err, data) {//获得模板template.html
if (err) {
console.log('找不到文件......请重试')
}


fs.readdir(wwwDir, function (err, files) {//得到wwwDir的目录列表中的文件名和目录名
if (err) {
return console.log('读取目录失败')
}
console.log(files)//files是一个字符数组,元素为目录中的文件名
var content = ''//将模板中要替换的部分构建为content
files.forEach(function (item) {//利用forEach,取出files数组的每个元素(item),进行渲染${item}替换
content += `<tr><td data-value="新建 Microsoft Word 文档.docx"><a class="icon file" draggable="true" href="/F:/www/%E6%96%B0%E5%BB%BA%20Microsoft%20Word%20%E6%96%87%E6%A1%A3.docx">${item}</a></td>
<td class="detailsColumn" data-value="0">0 B</td>
<td class="detailsColumn" data-value="1612679895">2021/2/7 下午2:38:15</td>
</tr>`
})


data = data.toString()//将二进制流的data转换为字符串
data=data.replace('-.-', content)//简单字符串解析替换操作,将template中替换的区域-.-(特殊标记)用content代替
res.end(data)//发送响应数据
})
})
})

server.listen(4001, function () {
console.log('服务器,琦动!')
})

结果截图:

小案例二:art-template

需要一个用于储存模板的html和一个js文件

html模板:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>This is Frank</title>
</head>

<body>
<p>My name is {{ name }}.</p>
<p>I am {{ age }} years old.</p>
<p>I am from {{ province }}.</p>
<p>I like {{ each hobbies }} {{ $value }} {{/each}}</p>
</body>

</html>

如果没有art-template,运行结果如下:

加了art-template后:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
var fs=require('fs')
var http=require('http')
var template=require('art-template')
http
.createServer(function(req,res){
var url=req.url
if(url==='/'){
fs.readFile('./Frank.html',function(err,data){
if(err){
return res.end('你访问的网址不存在噢')
}
data=data.toString()
var Frank=template.render(data,{
name:'Frank',
age:20,
province:'ChongQing',
hobbies:['打篮球','写博客','吹水']
})
res.end(data)
})
}

})
.listen(3000,function(){
console.log('琦动!')

由此可见,art-template只关注中的值,不会去关心html中的格式。因此可以理解为***js数据传到html中展示出来***

Day3

Node中的模块系统

模块の定义:

node应用由模块组成,采用的commonjs模块规范。每一个文件就是一个模块,拥有自己独立的作用域,变量,以及方法等,对其他的模块都不可见。CommonJS规范规定,每个模块内部,module变量代表当前模块。这个变量是一个对象,它的exports属性(即module.exports)是对外的接口。加载某个模块,其实是加载该模块的module.exports属性。require方法用于加载模块。


使用Node编写应用程序主要就是在使用:

  • Ecmascript

  • 核心模块

    • 文件操作fs
    • http服务的http
    • url路径操作的模块
    • path路径处理模块
    • os操作系统模块
  • 第三方模块

    • art-template
    • 必须通过npm下载
  • 开发者自个儿写的模块(自定义的)

模块化

  • 文件作用域

  • 通信规则

    • 加载require
    • 导出

CommonJs模块规范

这就是要谈论的——模 块 系 统

  • 模块作用域
  • 使用require方法用来加载模块
  • 使用exports接口对象来导出模块中的成员

加载require

操作:

1
var 自定义变量名称=require('模块')

两个作用:

  • 执行被加载模块中的代码

  • 得到被加载模块中的exports导出接口对象

导出exports

  • Node中是模块作用域,默认文件中所有成员只在当前文件模块有效
  • 如果我们想要访问其他模块的成员,可以把这些成员加载到exports上去

导出多个成员:

1
2
3
4
5
exports.demo=foo
exports.test='233'
exports.fun=function(){
xxxxxxxxxxxxx
}

也可以这样导出多个成员:

1
2
3
4
5
6
module.exports={
add: function(){
return xxxxxxx
},
foo:'123'
}

导出单个成员(指定的):

1
2
3
4
module.exports='666'
module.exports=function(){

}

注意:在使用module.exports时,后一个的会覆盖前一个的module.exports

原理

在底层代码中:exports是module.exports的一个引用—>exports=module.exports

1
2
3
console.log(exports===module.exports)//会返回true
exports.foo='666'
module.exports.foo='666'//等价

require方法加载规则

  • 核心模块

    • 模块名
  • 第三方模块

    • 模块名
  • 开发者自己写的

    • 路径
  • 优先从缓存加载

    • 加载过的模块会暂存入缓存中,若下次还有调用,直接从缓存中读取, 能有效提高模块加载效率

package.json

每一个项目最好有一个包描述文件(package.json,就像产品的说明书)。可通过npm init生成 。有了该文件,再用npm install就能把文件里的依赖项全部下载回来,就相当于原先有了备份,现在进行备份还原

  1. 在每个项目的根目录下应该有一个package.json文件
  2. 每次安装第三方包时,应该在后面加上``–save`,可以用来保存依赖项信息

npm

  • 命令行工具

1
npm install --global npm//更新命令
  • 常用命令

    • npm init -y//快速生成package.json文件
      
      1
      2
      3

      - ```shell
      npm install
    • npm install 包名//下载指定包
      
      1
      2
      3

      - ```shell
      npm install --save 包名//下载指定包并保存到package.json的dependencies中
    • npm uninstall 包名
      
      1
      2
      3

      - ```shell
      npm help//查看使用帮助
    • npm 命令 --help//查看指定命令的使用帮助
      
      1
      2
      3
      4
      5
      6


      - 如何解决npm被墙的问题

      - 安装淘宝的cnpm:

      npm install --global cnpm//--global表示安装到全局,不能省略。以后就能用cnpm代替npm了
      1
      2
      3

      - 如果不想安装cnpm但是又想用淘宝的服务器下载,可以通过:

      npm config set registry https://registry.npm.taobao.org//默认使用淘宝服务器下载
      1
      2
      3
      4
      5

      ## Express

      一种Web开发框架

npm install express –save//安装express

1
2
3
4
5
6
7
8
9
10
11
12
13
14

拿到框架的第一步当然是Hello World!

```javascript
var express=require('express')
var app=express()//相当于以前的server对象

app.listen(3000,function(){
console.log('服务器,琦动')
})

app.get('/',function(req,res){//以get方法请求'/',并执行对应的处理函数
res.send('Hello World!')
})

静态服务(静态 资源获取)

1
2
3
4
5
6
7
8
9
10
11
12
// 开放静态资源
// 1.当以/public/开头的时候,去./public/目录中找对应资源
// 访问:http://127.0.0.1:3000/public/login.html
app.use('/public/',express.static('./public/'));

// 2.当省略第一个参数的时候,可以通过省略/public的方式来访问
// 访问:http://127.0.0.1:3000/login.html
// app.use(express.static('./public/'));

// 3.访问:http://127.0.0.1:3000/a/login.html
// a相当于public的别名
// app.use('/a/',express.static('./public/'));

在Express中获取表单请求数据

获取get请求数据:

Express内置了一个api,可以直接通过req.query来获取数据

1
2
3
// 通过requery方法获取用户输入的数据
// req.query只能拿到get请求的数据
var comment = req.query;
获取post请求数据:

在Express中没有内置获取表单post请求体的api,这里我们需要使用一个第三方包body-parser来获取数据。

安装:

1
npm install --save body-parser;

配置:

// 配置解析表单 POST 请求体插件(注意:一定要在 app.use(router) 之前 )

1
2
3
4
5
6
7
8
9
10
11
12
13
14
var express = require('express')
// 引包
var bodyParser = require('body-parser')

var app = express()

// 配置body-parser
// 只要加入这个配置,则在req请求对象上会多出来一个属性:body
// 也就是说可以直接通过req.body来获取表单post请求数据
// parse application/x-www-form-urlencoded
app.use(bodyParser.urlencoded({ extended: false }))

// parse application/json
app.use(bodyParser.json())

使用:

1
2
3
4
5
6
app.use(function (req, res) {
res.setHeader('Content-Type', 'text/plain')
res.write('you posted:\n')
// 可以通过req.body来获取表单请求数据
res.end(JSON.stringify(req.body, null, 2))
})

在express中配合和使用art-template

安装:

1
2
npm install --svae art-template
npm install --save express-art-template

配置:

1
app.engine('html',require('express-art-template'))//第一个参数表示渲染以art结尾的文件

使用:

1
2
3
4
5
6
app.get('/',function(req,res){
//express默认会从项目的views目录去寻找index.html
res.render('index.html',{
title:'hello world'
})
})

如果希望修改视图渲染的目录(原本是views),可:

1
app.set('views',目录路径)

CRUD(待更…)

其他项

①修改完代码自动重启

以前每次修改完代码都要傻乎乎地去node xxx.js现在有了nodemon这一个第三方命令行工具,就能解决这一问题。

安装方法,懂得都懂

1
npm install nodemon --global

②封装异步API

回调函数:获取异步操作的结果

1
2
3
4
5
6
7
8
9
10
11
function fn(callback){
// var callback = funtion(data){ console.log(data); }
setTimeout(function(){
var data = 'hello';
callback(data);
},1000);
}
// 如果需要获取一个函数中异步操作的结果,则必须通过回调函数的方式来获取
fn(function(data){
console.log(data);
})

③关于json文件格式の坑

json文件格式要求极其严格,比如

致命错误在:id为5的一行末尾的逗号,一定要去掉!!!***结尾***的一个花括号不能有逗号(就这个错误折磨了我半个小时……)

小项目

一个简单的表单提交网站

路由设计

请求方法 请求路径 get参数 post参数 备注
GET /students 渲染首页
GET /students/new 渲染添加学生页面
POST /students/new name,age,gender,hobbies 处理添加学生请求
GET /students/edit id 渲染编辑页面
POST /students/edit id,name,age,gender,hobbies 处理编辑请求
GET /students/delete id 处理删除请求

提取路由模块

router.js:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
/**
* router.js路由模块
* 职责:
* 处理路由
* 根据不同的请求方法+请求路径设置具体的请求函数
* 模块职责要单一,我们划分模块的目的就是增强代码的可维护性,提升开发效率
*/
var fs = require('fs');

// Express专门提供了一种更好的方式
// 专门用来提供路由的
var express = require('express');
// 1 创建一个路由容器
var router = express.Router();
// 2 把路由都挂载到路由容器中

var Students=require('./student')

router.get('/students', function(req, res) {
// res.send('hello world');
// readFile的第二个参数是可选的,传入utf8就是告诉他把读取到的文件直接按照utf8编码,直接转成我们认识的字符
// 除了这样来转换,也可以通过data.toString()来转换
Students.find(function(err,students){
if (err) {
return res.status(500).send('Server error.')
}

res.render('index.html',{students:students})
})

});
router.get('/students/new',function(req,res){
res.render('new.html')
});

router.post('/students/new',function(req,res){
//获取表单数据
console.log(JSON.stringify(req.body))
//处理数据
//将数据写入json文件
//发送响应
//读取json文件并转换为对象-->在对象中添加数据-->将对象转换为字符串-->将字符串写入json文件
Students.save(req.body,function (err){
if(err){
return res.status(500).send('Server error.')
}
res.redirect('/')
})
});


router.get('/students/edit',function(req,res){

});

router.post('/students/edit',function(req,res){

});

router.get('/students/delete',function(req,res){

});

// 3 把router导出
module.exports = router;

app.js:

1
2
3
4
5
6
var router = require('./router');

// router(app);
// 把路由容器挂载到app服务中
// 挂载路由
app.use(router);

数据操作文件模块

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
/**
* student.js
* 数据操作文件模块
* 职责:操作文件中的数据,只处理数据,不关心业务
*/
var fs = require('fs');
var dbPath = './db.json'
/**
* 获取所有学生列表
* return []
*/
exports.find = function (callback) {
fs.readFile(dbPath, 'utf8', function (err, data) {
if (err) {
return callback(err)
}
callback(null, JSON.parse(data).students)
})
}


/**
* 获取添加保存学生
*/
exports.save = function (student, callback) {
fs.readFile(dbPath, 'utf8', function (err, data) {
if (err) {
return callback(err)
}
//将json字符串转换为数组对象
var students = JSON.parse(data).students
//将对象保存到数组对象中
if (students.length==0) {
student.id =1
}
else{
student.id=students[students.length-1].id+1
}
students.push(student)
//将数组对象转换为json文件(字符串)
var newData = JSON.stringify({students: students})
//字符串写入到文件中
fs.writeFile(dbPath, newData, function (err) {
if (err) {
return callback(err)
}
callback(null)
})
})
}

/**
* 更新学生
*/
exports.update = function () {

}

/**
* 删除学生
*/
exports.delete = function () {

}

成功插入数据の截图:

点击submit,成功重定向回主页并渲染完成の截图

小结:

通过这个小项目的练习,可以体会到当进行异步编程时,要想获取异步函数的数据就要使用回调函数

Day???2022.11.25

简单的服务端

1
2
3
4
5
6
require('http').createServer(function (req, res) {
res.writeHead(200)
res.end('Halo')
}).listen(3000, function () {
console.log('3000 is running...')
})

简单的客户端

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
require('https').request({
host: '127.0.0.1',
prot: 3000,
url: '/',
method: 'GET'
}, function (res) {
body = '',
res.setEncoding('utf8')
res.on('data', function(chunk) {
body += chunk
})
res.on('end', function() {
console.log('\n We got: \033[96m' + body + '\033[39m\n')
})
res.on('error', function(err) {
console.log(err)
})
}).end()

了解res和req对象

了解一些常用的API

  • writeHead
  • end
  • on(‘data’, callback)
  • on(‘end’, callback)
  • on(‘error’, callback)

马赛克网站

服务端

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
let http = require('http')
let fs = require('fs')

// 创建服务器
let server = http.createServer(function (req, res) {
if (req.url == '/') {
serve(__dirname + '/mosaic.html', 'text/html')
}

function serve(path, type) {
res.writeHead(200, { 'Content-Type': type })
fs.createReadStream(path).pipe(res)

}
})
server.listen(4001, function () {
console.log('4001')
})

mosaic.html

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
<!DOCTYPE html>
<html lang="en">

<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<style>
#canvas {
image-rendering: pixelated;
display: none;
}
</style>

<body>
<img src="">
<canvas id="canvas"></canvas>
<br>
<input id="imgFile" type="file" accept="image/*" onchange="uploadImg(this)"/>
<label for="compress-times">请选择压缩倍数:</label>
<input id="compress-times" type="number" value="10">
<button id="mosaic-btn">打码</button>
</body>
<script>
// 思路:
// 在其中需要设置canvas的宽度和高度,即width和height属性,这两个值分别是canvas区域包括的像素的宽度和高度。而使用CSS还可以设置canvas的视在宽度和高度:
// #canvas{
// width:800px;
// height:600px;
// }
// CSS中设置的宽度和高度可以称为【视在宽度和高度】,即表面看起来的宽高,而【实际的像素宽高则是由canvas行内样式定义】的,如果两处设置的值不同,就会进行缩放显示。
// 再利用image-rendering: pixelated对缩放后的图片进行像素化处理

const canvas = document.querySelector('canvas')
const ctx = canvas.getContext('2d')
const img = document.querySelector('img')
const imgFile = document.querySelector('#imgFile')
const mosaicBtn = document.querySelector('#mosaic-btn')
const compressTimes = document.getElementById('compress-times')

//将canvas显示的实际像素宽高大小设置成和图片一样大
function matchCanvasToImgSize(img) {
canvas.style.setProperty('width', img.width + 'px');
canvas.style.setProperty('height', img.height + 'px');
}

//在图片加载出来后(异步加载后),才能进行下一步操作,否则会出现图片未加载就执行drawImage,最后失败
img.onload = function () {
matchCanvasToImgSize(img)
}

//给打码按钮绑定事件
//点击后获取压缩的倍数
//然后将canvas的实际绘画区域缩小,也就是让实际绘画区域和可视区域成比例
mosaicBtn.onclick = function () {
//matchCanvasToImgSize(img)
compressTimes_value = compressTimes.value
canvas.width = img.width / compressTimes_value
canvas.height = img.height / compressTimes_value
ctx.drawImage(img, 0, 0, img.width / compressTimes_value, img.height / compressTimes_value)
canvas.style.display = 'block'
}

//添加图片,将其转换成base64编码
function uploadImg(input) {
let file = input.files[0];
if (file) {
let reader = new FileReader();
reader.readAsDataURL(file);
reader.onload = function (result) {
img.src = result.target.result;
}
}
ctx.clearRect(0, 0, img.width, img.height)
canvas.style.display = 'none'
}

</script>

</html>

通过Connect实现一个简单网站

注意:书上的connect版本过低,因此有些写法会有所改变,下面是最新的写法

1
2
3
4
5
6
7
8
let connect = require('connect')
let serveStatic = require('serve-static')
// 创建服务器
let server = connect();
// 处理静态文件
server.use(serveStatic(__dirname + '/website'))

server.listen(3000)

其中serveStatic是一个中间件,中间件(middleware)是一个函数,它本身接收一系列配置,然后返回一个函数,所以最后执行相关操作的是这个被返回的函数

一些中间件

static

connect允许该中间件能够挂载到url上,允许任意一个url匹配到文件系统的任意一个目录

1
server.use('/using-static', serveStatic(__dirname  + '/website'))

query

能够获取查询字符串这一部分的数据

body parse

用于获取post请求的数据