七天搞定node.js---第三天

自学node.js第三天的笔记。

知识点

  • 模块系统
    • 核心模块
    • 第三方模块
    • 自己写的模块
  • npm
  • package.json
  • Express
    • 第三方 Web 开发框架
    • 高度封装了 Http 模块
    • 更加专注于业务,而非底层细节
    • 知其所以然
  • 增删改查
    • 使用文件来保存数据库(锻炼异步编码)
  • MongDB
    • (所有方法都封装好了)
  • 在 Node 中使用 art-template 模板引擎
    • 安装
    • 加载
    • template.render()
  • 客户端渲染和服务端渲染的区别
    • 最少两次请求,发起 ajax 在客户端使用模板引擎渲染
    • 客户端拿到的就是服务端已经渲染好的
  • 处理留言本案例首页数据列表渲染展示
  • 处理留言本案例发表留言功能
    • 路径
    • 设计好的请求路径
    • $GET 直接或查询字符串数据
    • Node 中需要自己动手来解析
      • url.parse()
    • /pinglun?name=jack&message=hello
    • split(‘?’)
    • name=jack&message=hello
    • split(‘&’)
    • name=jack message=hello
    • forEach()
    • name=jack.split(‘=’)
    • 0 key
    • 1 value
  • 掌握如何解析请求路径中的查询字符串
    • url.parse()
  • 如何在 Node 中实现服务器重定向
    • header(‘location’)
    • 301 永久重定向 浏览器会记住
      • a.com b.com
      • a 浏览器不会请求 a 了
      • 直接去跳到 b 了
    • 302 临时重定向 浏览器不记忆
      • a.com b.com
      • a.com 还会请求 a
      • a 告诉浏览器你往 b
  • Node 中的 Console(REPL)使用

each和forEach

  • art-template 和 jQuery 一毛钱关系都没有
  • each 是 art-template 的模板语法,专属的
  • {{each 数组}}
  • <li>{{ $value }}</li>
  • {{/each}}这是 art-template 模板引擎支持的语法,只能在模板字符串中使用
  • $.each(数组, function)
  • $(‘div’).each(function) 一般用于遍历 jQuery 选择器选择到的伪数组实例对象
  • forEach 是 EcmaScript 5 中的一个数组遍历函数,是 JavaScript 原生支持的遍历方法 可以遍历任何可以被遍历的成员
  • jQuery 的 each 方法和 forEach 几乎一致
  • 由于 forEach 是 EcmaScript 5 中的,所以低版本浏览器不支持
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<!DOCTYPE html>
<html lang="en">

<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>demo</title>
</head>

<body>
<script src="node_modules/jquery/dist/jquery.js"></script>
<script>
// ie8 以下版本不支持,用jquery就能很好解决兼容问题
// ;
// ['ssq', '木槿', 'test', '666'].forEach(function(item, index) {
// console.log(item)
// })
</script>
</body>

</html>

ie8 以下版本不支持
ie8

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<!DOCTYPE html>
<html lang="en">

<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>demo</title>
</head>

<body>
<script src="node_modules/jquery/dist/jquery.js"></script>
<script>
// 遍历 jquery 元素
$.each(['ssq', '木槿', 'test', '666'], function(index, item) {
console.log(item)
})
</script>
</body>

</html>

这里还是报错,原因是jQuery版本安装的是最新版本,2以上的版本都不支持
jQuery高版本
最后把jQuery换成了1.11.0版本后就成功兼容了
1.11.0

Node 中的模块系统

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

  • EcmaScript语言
    • 和浏览器不一样,在 Node 中没有 Bom、Dom
  • 核心模块
    • 文件操作的 fs
    • http 服务的 http
    • url 路径操作模块
    • path 路径处理模块
    • os 操作系统信息
  • 第三方模块
    • art-template
    • 必须通过 npm 来下载才可以使用
  • 自己写的模块
    • 自己创建的文件

什么是模块化

  • 文件作用域
  • 通信规则
    • 加载 require
    • 导出

CommonJS 模块规范

在 Node 中的 JavaScript 还有一个很重要的概念:模块系统。

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

加载require

1
2
3
4
5
6
7
8
9
// 默认得到的是对象
// 使用对象中的成员必须 . 点某个成员来访问
// 有时候,对于一个模块,仅仅就想导出一个方法
var fooExports = require('./foo')

// ReferenceError:foo is not defined
// console.log(foo)

console.log(fooExports)

语法:

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

两个作用:

  • 执行被加载模块中的代码
  • 得到被加载模块中的exports导出对象

导出exports

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
var foo = 'bar'

function add(x, y) {
return x + y
}

// exports = add //打印出空对象,不行

// 如果一个模块需要直接导出某个成员,而非挂载的方式
// 那这个时候必须使用下面这种方式
module.exports = add

// 你可以认为在每个模块的最好 return 了这个 exports

// 只能到我想要给你的成员
// 这样做的目的是为了解决变量命名冲突的问题
// exports.add = add

// exports 是一个对象
// 可以通过多次为这个对象添加成员实现对外导出多个内部成员

// exports.str = 'hello'

// 若希望加载得到直接就是一个:
// 方法
// 字符串
// 数字
// 数组
  • Node 中是模块作用域,默认文件中所有的成员只在当前文件模块有效
  • 对于希望可以被其他模块访问的成员,我们就需要把这些公开的成员都挂载到exports接口对象中就可以了

导出多个成员(必须在对象中):

1
2
3
4
5
6
7
8
exports.a = 123
exports.b = 'hello'
exports.c = function(){
console.log('ccc')
}
exports.d = {
foo:'bar'
}

导出单个成员(拿到的就是:函数、字符串):

1
module.exports = 'hello'

以下情况会覆盖:

1
2
3
4
5
6
module.exports = 'hello'

// 输出的是这条函数,后者会覆盖前者。
module.exports = function(x , y){
return x + y
}

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

1
2
3
4
5
6
module.exports = {
add:function(){
return x + y
},
str: 'hello'
}

原理解析

exportsmodule.exports的一个引用:

1
2
3
4
5
6
console.log(exports === module.exports)// => true

exports.foo = 'bar'

//等价于
module.exports.foo = 'bar'

require 方法加载规则

  • 核心模块
    • 模块名
  • 第三方模块
    • 模块名
  • 用户自己写的模块
    • 路径
  • 优先从缓存加载
  • 判断模块标识
    • 核心模块
    • 第三方模块
    • 自己写的模块

npm

  • 全称:node package manager

npm 网站

https://www.npmjs.com

npm 命令行工具

npm 的第二层含义就是一个命令行工具,只要你安装了 node 就已经安装了 npm
npm 也有版本这个概念
可以通过在命令行中输入npm --version查看版本号
升级npm:

1
npm install --global npm

常用命令

  • npm init
    • npm init -y 跳过向导,快速生成 pockage.json
  • npm install
    • 根据 package.json 中的 dependencies 生成 node_modules
    • 简写 npm i
  • npm install 包名
    • 只下载包
    • 简写 npm i 包名
  • npm install 包名 –save
    • 下载并且保存依赖项(package.json 文件中的 dependencies 选项)
    • 简写 npm i 包名 -S
  • npm uninstall 包名
    • 只删除,如果有依赖项会依然保存
    • 简写 npm un 包名
  • npm uninstall –save 包名
    • 删除包的同时也会把依赖信息也去除
    • 简写 npm un -S 包名
  • npm help
    • 查看使用帮助
  • npm 命令 –help
    • 查看指定命令的使用帮助

解决 npm 被墙问题

npm 存储包的服务器在国外,有时候会被墙,速度很慢。
http://npm.taobao.org/ 淘宝的开发团队把 npm 在国内做了一个备份。
安装淘宝镜像 cnpm:

1
2
3
// 在任意目录执行都可以
// --global 表示安装到全局,而非当前目录
npm install --global cnpm

接下来你安装包的时候把之前的 npm 替换成 cnpm
举个例子:

1
2
3
4
5
// 这里还是走国外的 npm 服务器,速度比较慢
npm install jquery

// 使用 cnpm 就会通过淘宝的服务器来下载 jquery
cnpm install jquery

如果不想安装 cnpm 又想使用淘宝的服务器来下载:

1
npm install jquery --registry=https://registry.npm.taobao.org

但是每一次手动这样加参数很麻烦,所以我们可以把这个选项加入配置文件中:

1
2
3
4
npm config set registry https://registry.npm.taobao.org

// 查看 npm 配置信息
npm config list

只要经过了上面命令的配置,则你以后所有的 npm install 都会默认通过淘宝的服务器来下载。

package.json

建议每一个项目都要有一个package.json
这个文件可以通过 npm init 的方式来自动初始化。

在初始化会以向导的方式询问:

1
2
3
4
5
6
7
8
name:项目名字
version:项目版本号
description:项目描述
git repository:github仓库地址
keywords:关键词,别人通过npm下载你的项目时
main:项目入口文件
author:项目作者
license:开源协议

对于目前来讲,最重要的是 dependencies 选项,这个可以用来保存第三方包的依赖信息。
如果的你的 node_modules 文件丢失了,也不用担心,只需要: npm install 就会自动把 package.json 中的 dependencies 中的所有的依赖项都下载回来。

  • 建议每个根目录下都有一个 package.json 文件;
  • 建议执行 npm install 包名 的时候都加上 --save这个选项,目的是用来保存依赖信息。

Express

原生的 http 在某些方面表现不足以应对我们的开发需求,所以我们就需要使用框架来加快我们的开发效率,框架的目的就是提高效率,让我们的代码更高度统一。

在 Node 中,有很多 Web 开发框架,这里以 express 为主。

Express 的整体感知:

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
//0.安装
//1.引包

var express = require('express')

//2.创建你服务器应用程序
// 也就是原来的 http.createServer

var app = express()

//在 Express 中开放资源就是一个 API 的事
// 公开指定的目录
app.use('/public/', express.static('./public/'))
app.use('/static/', express.static('./static/'))
// 当服务器收到 get 请求 / 的时候,执行回调函数
app.get('/', function(req, res) {
res.send('hello express!')
})
app.get('/about', function(req, res) {
res.send('about~你好!')
})
// 以前:
// 得到路径
// 一个一个判断
// 显得非常不优雅

// 相当于 server.listen
app.listen(3000, function() {
console.log('app is running at port 3000。')
})

MongoDB

总结

  • jQuery 的 each 和 原生的 JavaScript 方法 forEach
    • EcmaScript 5 提供的
      • 不兼容 IE 8
    • jQuery 的 each 由 jQuery 这个第三方库提供
      • jQuery 2 以下的版本是兼容 IE 8 的
      • 它的 each 方法主要用来遍历 jQuery 实例对象(伪数组)
      • 同时它也可以作为低版本浏览器中 forEach 替代品
      • jQuery 的实例对象不能使用 forEach 方法,如果想要使用必须转为数组才可以使用
      • [].slice.call(jQuery实例对象)
  • 模块中导出多个成员和导出单个成员
  • 301 和 302 状态码区别
    • 301 永久重定向,浏览器会记住
    • 302 临时重定向
  • exports 和 module.exports 的区别
    • 每个模块中都有一个 module 对象
    • module 对象中有一个 exports 对象
    • 我们可以把需要导出的成员都挂载到 module.exports 接口对象中
    • 也就是:moudle.exports.xxx = xxx 的方式
    • 但是每次都 moudle.exports.xxx = xxx 很麻烦,点儿的太多了
    • 所以 Node 为了你方便,同时在每一个模块中都提供了一个成员叫:exports
    • exports === module.exports 结果为 trues
    • 所以对于:moudle.exports.xxx = xxx 的方式 完全可以:expots.xxx = xxx
    • 当一个模块需要导出单个成员的时候,这个时候必须使用:module.exports = xxx 的方式
    • 不要使用 exports = xxx 不管用
    • 因为每个模块最终向外 return 的是 module.exports
    • exports 只是 module.exports 的一个引用
    • 所以即便你为 exports = xx 重新赋值,也不会影响 module.exports
    • 但是有一种赋值方式比较特殊:exports = module.exports 这个用来重新建立引用关系的
    • 可以更灵活的去用它
  • Node 是一个比肩 Java、PHP 的一个平台
    • JavaScript 既能写前端也能写服务端
1
2
3
4
5
6
7
8
moudle.exports = {
a: 123
}

// 重新建立 exports 和 module.exports 之间的引用关系
exports = module.exports

exports.foo = 'bar'
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
Array.prototype.mySlice = function () {
var start = 0
var end = this.length
if (arguments.length === 1) {
start = arguments[0]
} else if (arguments.length === 2) {
start = arguments[0]
end = arguments[1]
}
var tmp = []
for (var i = start; i < end; i++) {
// fakeArr[0]
// fakeArr[1]
// fakeArr[2]
tmp.push(this[i])
}
return tmp
}

var fakeArr = {
0: 'abc',
1: 'efg',
2: 'haha',
length: 3
}

// 所以你就得到了真正的数组。
[].mySlice.call(fakeArr)
  • module.exports 和 exports 的区别
  • require 方法加载规则
    • 优先从缓存加载
    • 核心模块
    • 路径形式的模块
    • 第三方模块
      • node_modules
  • package.json 包描述文件
    • dependencies 选项的作用
  • npm 常用命令
  • Express 基本使用

  • 使用 Express 把之前的留言本案例改造一下

index.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
<!DOCTYPE html>
<html lang="en">

<head>
<meta charset="UTF-8">
<title>留言本</title>
<!--
浏览器收到 HTML 响应内容之后,就要开始从上到下依次解析,
当在解析的过程中,如果发现:
link
script
img
iframe
video
audio
等带有 src 或者 href(link) 属性标签(具有外链的资源)的时候,浏览器会自动对这些资源发起新的请求。
-->
<!--
注意:在服务端中,文件中的路径就不要去写相对路径了。
因为这个时候所有的资源都是通过 url 标识来获取的
我的服务器开放了 /public/ 目录
所以这里的请求路径都写成:/public/xxx
/ 在这里就是 url 根路径的意思。
浏览器在真正发请求的时候会最终把 http://127.0.0.1:3000 拼上

不要再想文件路径了,把所有的路径都想象成 url 地址
-->
<link rel="stylesheet" href="/public/lib/bootstrap/dist/css/bootstrap.css">
</head>

<body>
<div class="header container">
<div class="page-header">
<h1>Example page header <small>Subtext for header</small></h1>
<a class="btn btn-success" href="/post">发表留言</a>
</div>
</div>
<div class="comments container">
<ul class="list-group">
{{each comments}}
<li class="list-group-item">{{ $value.name }}说:{{ $value.message }} <span class="pull-right">{{ $value.dateTime }}</span></li>
{{/each}}
</ul>
</div>
</body>

</html>

post.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
<!DOCTYPE html>
<html lang="en">

<head>
<meta charset="UTF-8">
<title>Document</title>
<link rel="stylesheet" href="/public/lib/bootstrap/dist/css/bootstrap.css">
</head>

<body>
<div class="header container">
<div class="page-header">
<h1><a href="/">首页</a> <small>发表评论</small></h1>
</div>
</div>
<div class="comments container">
<!--
以前表单是如何提交的?
表单中需要提交的表单控件元素必须具有 name 属性
表单提交分为:
1. 默认的提交行为
2. 表单异步提交

action 就是表单提交的地址,说白了就是请求的 url 地址
method 请求方法
get
post
-->
<form action="/post" method="post">
<div class="form-group">
<label for="input_name">你的大名</label>
<input type="text" class="form-control" required minlength="2" maxlength="10" id="input_name" name="name" placeholder="请写入你的姓名">
</div>
<div class="form-group">
<label for="textarea_message">留言内容</label>
<textarea class="form-control" name="message" id="textarea_message" cols="30" rows="10" required minlength="5" maxlength="20"></textarea>
</div>
<button type="submit" class="btn btn-default">发表</button>
</form>
</div>
</body>

</html>

404.html页面

1
2
3
4
5
6
7
8
9
10
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
<h1>抱歉! 您访问的页面失联啦...</h1>
</body>
</html>

admin页面

1
2
3
4
5
6
7
8
9
10
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
<h1>admin {{ title }}</h1>
</body>
</html>

源代码可以去我的 github 克隆

-------------本文结束感谢您的阅读-------------
木槿前端不求人,有空就来坐坐。
0%