[toc]
目录
canvas
- 概述:H5新增双闭合标签,它主要的功能是可以让我们写一些小游戏。
- 资料:API、手册
一. canvas的基本使用
1
2
3
4
5
6
7
8
9
10
11<head>
<meta charset="UTF-8">
<title>Document</title>
<style>
* { margin: 0; padding: 0;}
canvas { border: 1px solid black;}
</style>
</head>
<body>
<canvas width="600px" height="300px"></canvas>
</body>
注意:canvas有默认的宽和高(300*150px),如果要改宽高,只能在行间样式中修改!canvas最终相当于是一张图片
- canvas绘制基本图形
如果想在canvas上绘制图形,需要使用canvas2D上下文;
nodeJS
开门注
官网介绍:
- Node.js是一个构建在Chrome浏览器V8引擎上的JavaScript运行环境;
- Node.js使用了事件驱动、非阻塞I/O模型,这些都使它轻量、好用;
- Node.js的包生态(npm),是世界上最大的开源生态系统。
1. JavaScript到底出了什么问题?
先说说异步I/O和同步I/O:绝大多数的网站I/O是非常多的,I就是input(数据的读取),O就是output(数据的写入),但I/O的时候CPU命令磁盘去做事情,此时CPU就闲置了。这种模式叫做同步I/O
- 同步(synchronous):当系统遇见了一个需要耗费大量时间的事情时,选择死等。
- 异步(Asynchronous):当系统遇见了一个需要耗费大量时间的事情时,不死等,先做后面的时期,耗时事情做完之后,执行回调函数。
1
2
3
4
5
6
7
8
9
10
11var fs = require("fs");
fs.readFile("./test.txt", function(err, data){
console.log(data.toString()); // 后输出
})
var sum = 0;
for(var i=0; i<=100; i++){
sum += i;
}
console.log(sum); // 先输出
Node.js解决性能问题使用非常极端的思想:预期一堆服务员闲着,还不如一个服务员往死里用。
2. Node.js创建服务器(node.js)
1 | // 读取内置模块http,这个模块是开发服务器用的 |
运行:在文件所在目录下打开终端,使用下面的命令1
node node.js
Node.js是一个让JavaScript运行在服务器端的开发平台,它让JavaScript的触角伸到了服务器端。但Node.js似乎和其他服务端语言比如PHP、ASP、JSP有点不同:
- Node.js不是一种独立的语言,与PHP、JSP、Python、Ruby的“既是语言,也是平台”不同,Node.js使用JavaScript进行编程,运行在JavaScript引擎上(V8)
- 与PHP、JSP等相比,Node.js跳过了Apache、Nginx、IIS等HTTP服务器,它自己不用建设在任何服务器软件之上。Node.js的许多设计理念与经典架构(LAMP)有着很大的不同,可以提供强大的伸缩能力.
- Node.js自身哲学,就是花最小的硬件成本,追求更高的并发,更高的处理性能。
- Node.js没有根目录的概念。
1
2
3graph LR
A(浏览器) -->|1.发送request请求|B[服务器2.nodejs程序在这里执行]
B[服务器:2.nodejs程序在这里执行] -->|3.返回response响应|A(浏览器)
3. Node.js的特点:
- 单线程特性
1
2
3
4
5
6
7
8
9
10var http = require("http");
var a = 0;
var server = http.createServer(function (req, res) {
a++;
res.setHeader("Content-Type", "text/html;charset=UTF-8");
// 每个服务器都必须有一个end,即使为空!否则页面会一直处于loading状态,end里面必须是字符串或者二进制
res.end(a.toString());
});
// 监听
server.listen(3000);
每次刷新a都会自增,说明没有新开进程,证明Node.js是单线程的
Node.js拥有JS的所有核心语法,包括ES6语法。但是Node.js没有BOM的东西,比如window对象等。
下面的程序一旦刷到6666,将抛出错误,然后程序会挂起,不能再被访问!侧面证明了Node.js是单线程的!1
2
3
4
5
6
7
8
9
10
11var http = require("http");
var server = http.createServer(function (req, res) {
var num = parsInt(Math.random() * 1000)
if(num === 6666){
throw new Error("错误!6666出现了!这个人的ip是" + req.connection.remoteAddress);
}
res.end("你的数字是" + num);
});
// 监听
server.listen(3000);
- 异步I/O特性:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15var http = require("http"); // 创建服务器用的
var fs = require("fs"); // 读取文件用的
var ip = req.connection.remoteAddress;
console.log(ip + "来了!开始读取文件!");
var server = http.createServer(function (req, res) {
// 来客人之后做的事情
fs.readFile("./test.txt", function(err, data){
res.end(data);
console.log(ip + "读取文件完毕!");
});
});
// 监听
server.listen(3000);
可以看到一个ip开始读到读完之间,会插入别的ip开始读的情况!这就是异步I/O。==只要I/O越多,Node.js宏观上越并行==
而如果==计算越多,Node.js宏观上越不并行==,CPU将被某个线程单独占用!1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25var http = require("http"); // 创建服务器用的
var fs = require("fs"); // 读取文件用的
var ip = req.connection.remoteAddress;
console.log(ip + "来了!开始计算水仙花数!");
for(var i=1000; i<=9999; i++){
var ge = i % 10,
shi = parsInt(i / 10) % 10,
bai = parsInt(i / 100) % 10,
qian = parsInt(i / 1000) % 10;
var sum = Math.pow(ge,4) + Math.pow(shi,4) + Math.pow(bai,4) + Math.pow(qian,4);
if(sum == i){
console.log(ip + "算出了水仙花数!")
}
}
console.log(ip + "开始读取文件!");
var server = http.createServer(function (req, res) {
// 来客人之后做的事情
fs.readFile("./test.txt", function(err, data){
res.end(data);
console.log(ip + "读取文件完毕!");
});
});
// 监听
server.listen(3000);
Node.js适合I/O多的业务,例如:用户表单收集、考试系统、打分系统、聊天室、图文直播、提供JSON的API(为前台Angular等使用)……而不适合计算多的业务!
- 事件驱动特性:事件驱动是Node.js的底层机制,我们只需要了解Node.js不会“上错菜”的原因就是事件驱动——事件环。事件环保证了Node.js可以高效准确的运行而不会紊乱。
4. 路由机制
1 | var http = require("http"); |
Node.js是没有像Apache那种真实物理文件映射关系的!这叫做顶层路由设计。能够制作顶层路由设计的语言目前比较流行的只有Node.js和Python
5. 模块
url模块、path模块、querystring模块
一个完整的URL包括querystring部分(就是GET请求查询字符串)、甚至hash部分。但req.url不包括hash(hash是前端标识,用于解决ajax无法回退等问题,所以在后端不用得到)
1 | // 例如:http://127.0.0.1:3000/a.html?id=123#456 |
而此时我们想得到querystring中的某一部分(l例如文件名),如果用正则提炼就太麻烦了,Node.js中提供了内置模块:url、path、querystring它们都可以服务于URL的识别。
- url模块:
1
2
3
4
5
6
7
8
9
10
11
12// 例如:http://127.0.0.1:3000/a.html?id=123#456
var http = require("http");
var url = require("url");
http.createServer(function (req, res) {
// 将URL的字符串转为JSON对象
var urlJson = url.parse(req.url);
console.log(urlJson);
res.end("");
}).listen(3000, "127.0.0.1");
此时url的parse方法会将地址整理为一个对象,但无法正确识别protocol(协议)、host(主机名)、port(端口号)等等。在Linux系统下可以完全识别。
当有多个参数时,用url.parse(req.url, true)形成对象,方便存入数据库:1
2
3
4
5
6
7
8
9
10
11
12// 例如:http://127.0.0.1:3000/a.html?id=123#456
var http = require("http");
var url = require("url");
http.createServer(function (req, res) {
// 将URL的字符串转为JSON对象
var urlJson = url.parse(req.url, true); // 增加true
console.log(urlJson);
res.end("");
}).listen(3000, "127.0.0.1");
path模块:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16// 例如:http://127.0.0.1:3000/a.html?id=123#456
var http = require("http");
var url = require("url");
var path = require("path");
http.createServer(function (req, res) {
// 将URL的字符串转为JSON对象
var urlJson = url.parse(req.url, true); // 增加true
// 得到文件路径
var pathName = urlJson.pathname;
// 得到拓展名
var extName = path.extname(pathName);
res.end("");
}).listen(3000, "127.0.0.1");querystring模块:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17// 例如:http://127.0.0.1:3000/a.html?id=123#456
var http = require("http");
var url = require("url");
var path = require("path");
var querystring = require("querystring");
http.createServer(function (req, res) {
// 将URL的字符串转为JSON对象
var urlJson = url.parse(req.url);
// 得到查询字符串
var qs = urlJson.query;
// 转为查询对象,和url.parse加上true非常类似
var qsJson = querystring.parse(qs);
res.end("");
}).listen(3000, "127.0.0.1");
小项目:静态资源服务器 - 创建一个文件夹myweb,我们的程序能够自动为里面的文件、图片、css、js加上路由
1 | var http = require("http"); |
至此,静态资源服务器的框架已经完备,但还有几个点不够完善,比如下行文件中没有文件类型,所以借助++mime类型++修改为: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
35var http = require("http");
var fs = require("fs");
var url = require("url");
var path = require("path");
var querystring = require("querystring");
http.createServer(function (req, res) {
// 得到用户想要读取什么文件(输入的网址)
var pathName = url.parse(req.url).pathname;
// 得到拓展名
var extName = path.extname(pathName);
// 准备一个映射关系对
var mime = {
".jpg" : "image/jpeg",
".jpeg" : "image/jpeg",
".gif" : "image/gif",
".png" : "image/png",
".html" : "text/html;chartset=UTF-8",
".css" : "text/css",
".js" : "application/x-javascript"
}
// 真的去读取这个文件(在myweb里找到这个文件并呈现)
fs.readFile("./myweb/" + pathName, function(err,data){
if(err){
res.end("没有这个文件");
return;
}
// 检查是否属于已有的mime类型
if(mime.hasOwnProperty(extName)){
res.setHeader("content-type", mime[extName]);
}
res.end(data);
});
}).listen(3000, "127.0.0.1");
第二个问题:不能自动识别index文件。比如用户输入:http://127.0.0.1:3000/b 意思是要读取b文件夹中的index.html!解决思路就是如果用户输入了一个URL不存在拓展名,则自动补全/index.html1
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
50var http = require("http");
var fs = require("fs");
var url = require("url");
var path = require("path");
var querystring = require("querystring");
http.createServer(function (req, res) {
// 得到用户想要读取什么文件(输入的网址)
var pathName = url.parse(req.url).pathname;
// 得到拓展名
var extName = path.extname(pathName);
// 如果URL没有拓展名,则表示是文件夹,此时自动补全/index.html
if(!extName){
/*
此处还有问题!如果网址不是以/结尾,此时会造成浏览器识别文件路径层次有问题!
比如:
http://127.0.0.1:3000/a 和
http://127.0.0.1:3000/a/ 不一样!
对于同样一张图片,前者认为是同级目录下的图片,后者认为是a文件夹下的图片!所以要再次判断是否结尾是/,不是的话就需要重定向
*/
if(pathName.substr(-1) != "/"){
res.writeHead(302, { "Location" : pathName + "/" });
}
pathName += "/index.html";
}
// 准备一个映射关系对
var mime = {
".jpg" : "image/jpeg",
".jpeg" : "image/jpeg",
".gif" : "image/gif",
".png" : "image/png",
".html" : "text/html;chartset=UTF-8",
".css" : "text/css",
".js" : "application/x-javascript"
}
// 真的去读取这个文件(在myweb里找到这个文件并呈现)
fs.readFile("./myweb/" + pathName, function(err,data){
if(err){
res.end("没有这个文件");
return;
}
// 检查是否属于已有的mime类型
if(mime.hasOwnProperty(extName)){
res.setHeader("content-type", mime[extName]);
}
res.end(data);
});
}).listen(3000, "127.0.0.1");
还有一个问题,就是304状态码(not modified)。当文件没有改变时,拒绝传输文件!也就是当用户狂点刷新时,其实不发送请求。原理是通过session和cookie保存历史镜像,每次提交时进行比对,如果没有改变则不响应。
小结:
1.Node.js中的fs模块、mongdb模块基本上都是异步方法。一定要记住(当做规矩背),==异步方法不能通过return返回,不能通过等号来接收数据。必须通过回调函数传实参的模式来传输数据。==
2.内置模块用require无条件、无路径的引用,是Node.js天生就有的。
- 自定义模块:每一个js文件。==Node.js、webpack、seajs等使用CMD(通用模块定义)规范==;Angular、requirejs等使用的是AMD(异步模块定义)规范。
01.js
1 | require("./test.js"); |
test.js
1 | var fs = require("fs"); |
在01.js中引用test.js,会先打出:我是test.js,然后再打出:我是01.js,最后打出:图片读取完毕。说明:
- require()谁就会运行谁
- require(“./test.js”);中的”./“不能省略
- require的js文件中如果有异步函数,将不会等待,先执行主文件之后的程序,等异步结束后回调函数会执行。
- 如果A引用B,此时Node.js发现B又引用了A,则会阻止这次循环引用。
6. 作用域和暴露
- exports暴露:当文件有多个东西对外暴露时,使用这个方法
01.js
1 | require("./test.js"); |
test.js
1 | var a = 100; |
此时将报错,01.js打印出undefined,找不到a,因为==Node.js中每个js都是单独的作用域==(闭包),和DOM开发不同,浏览器中var一个a此时a会自动成为window对象的属性,此时js文件和js文件可以共享作用域;但是Node.js中每个js文件天生是隔离作用域的,所以想要访问其内部的变量或方法,必须先通过exports.xx = xx暴露出去
01.js
1 | var test = require("./test.js"); |
test.js
1 | var a = 100; |
- module.exports暴露:当文件只有一个东西对外暴露时,用这个方法
01.js
1 | var People = require("./People.js"); |
People.js
1 | function People(name, sex, age){ |
7. 神奇的node_modules文件夹
如果我们写了一个引用没有”./“,则说明引用的是node_modules文件夹里的东西1
var People = require("People.js");
node_modules文件夹只要存在于要引用它的js文件的祖先路径中,就可以直接引用,无视路径层级
8. npm的世界
Node.js的开发者们希望全世界的程序猿都可以贡献自己的代码,避免重复造轮子,所以主导了一个社区,叫做npm(node package management, node包管理器)
- npm的使用
官网:https://www.npmjs.com
淘宝镜像:http://npm.taobao.org
我们可以到npm上面直接寻找我们想要的模块,然后直接npm install就可以了 - 使用package.json来管理依赖
npm有一个创造性的举动,可以让开发者声明自己的项目的全部依赖。在项目目录下创建一个叫做package.json的文件来记录。初次创建项目时,可以在项目文件夹下,通过1
npm init
命令来初始化一个package.json文件,而使用–save命令,在安装依赖的同时,会把依赖写进package.json文件1
npm install xxx --save
安装时也可以强制安装某个版本1
npm install xxx#^1.0.0 --save
关于版本号的常用符号:
|符号|对应的版本号含义|
|:–|:–|
|确数(1.0.2)|确定的某个版本|
|>、>=、<、<=|高于/低于…某个版本|
|~|大约近似等于某个版本,如果有就一定用这个版本,没有就用最近的|
|^|寻找和某个版本兼容的版本,如果有就一定用这个版本,没有就用最近的|
|*|匹配任何版本|
|laste|寻找最新版本|
- 全局安装淘宝镜像
1
npm install xxx -g // 全局安装某个包
一些CLI(命令行程序)、工程化的东西安装在全局1
npm install -g cnpm --registry=https://registry.npm.taobao.org
9. GET和POST请求
先安装一个小型服务器
1 | cnpm install serve-static --save |
然后去淘宝镜像上根据包名查找对应的API,抄一些用法过来,详见nootbook/get&post
- GET请求:
01.js
1 | var finalhandler = require('finalhandler') |
public/index.html
1 | <!DOCTYPE html> |
- POST请求
02.js
1 | /* |
10. formidable模块:npm上可以下载一个formidable的模块,用来处理post请求。甚至可以处理图片、zip文件等的上传。
1 | npm install formidable --save // 安装依赖 |
具体代码详见03.js和upload.html1
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/*
演示POST请求通过formidable模块来处理参数
*/
var finalhandler = require('finalhandler')
var http = require('http')
var serveStatic = require('serve-static')
var url = require("url")
var fs = require("fs")
var querystring = require("querystring")
const formidable = require('formidable')
const path = require('path')
// 配置静态资源服务器
var serve = serveStatic('public', {
'index': ['index.html', 'index.htm']
})
// Create server
var server = http.createServer(function onRequest(req, res) {
// 路由
var pathName = url.parse(req.url).pathname;
if (pathName == "/shangchuan") {
// 照抄npm.taobao.org上的API
const form = formidable({
multiples: true
});
// 设置上传的文件存放在哪里
form.uploadDir = "./uploads"
form.parse(req, (err, fields, files) => {
// fields 表示普通控件
// files 表示文件控件
// 检测是否有文件
if (!files.touxiang) {
return
}
// 检测是否有上传文件
if (!files.touxiang.name) {
res.end("请选择文件")
return
}
// 得到拓展名
var extname = path.extname(files.touxiang.name);
// 为上传的文件加上拓展名
fs.rename(files.touxiang.path, files.touxiang.path + extname, function() {
res.end("上传成功")
})
});
return;
}
// 使用静态资源。这个要放在下面。
serve(req, res, finalhandler(req, res));
})
// Listen
server.listen(3000)
console.log("服务器已经运行在3000端口");
待补充(两个大包)
11. Express.js框架:
解决了哪些问题
- 路由不方便制作,尤其是正则表达式路由
- 静态资源服务不方便
- 页面呈递不方便,没有模板引擎
1 | cnpm install express --save |
基本使用方法: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
29var express = require("express");
var app = express();
// 中间件(注意顺序)
app.get("/", function(){
res.send("我是首页");
})
app.get("/music", function(){
res.send("音乐频道");
})
app.get("/news", function(){
res.send("新闻频道");
})
app.get("/student/:xuehao", function(){
if(/^[\d]{6}$/.test(req.params.xuehao)){
res.send("学生频道,学号" + req.params.xuehao);
}else{
res.send("学号有误,应该是6位数字");
}
})
app.get("/student/:123", function(){
res.send("123是班长" + req.params.xuehao);
// 这个中间件将不会被匹配到,因为上面匹配到了学号就不会再往下走了
})
// 静态化public文件夹,无论get还是post都能使用
app.use(express.static("./public"));
// 将public文件夹中的东西映射到jingtai文件夹中(访问路径也变为/jingtai/xxx)
app.use("./jingtai", express.static("./public"));
app.listen(3000);
中间件特点:
- 输出使用res.send()而不是res.end()
- 自动使用pathname与/news进行比较,也就是说会自动过滤掉querystring、hash等等
- 可以用:来引导一个变量,此时特别方便做一个正则的路由,并用req.params.xxx获取
需求:当访问 / 时,其实真正想访问的页面是public/haha.html
1 | ... |
==res常用的函数是send和sendFile。sendFile表示发送一个页面给用户。注意sendFile必须使用绝对路径当做参数,而__dirname表示当前这个js文件的绝对路径==
12. 模板引擎:express可以象PHP一样使用后台语言模板,此时最好用的模板引擎叫做ejs模板引擎。
API:https://npm.taobao.org/package/ejs1
cnpm install ejs --save
基本使用方法:
01.js
1 | var express = require("express"); |
此时当用户访问 / 的时候,会自动使用views文件夹里的shouye.ejs文件当做模板。字典就是后面传入的json。
views/shouye.ejs
1 | <!DOCTYPE html> |
注意:ejs就是html,写法完全一样!
13. MVC设计模式:
MVC是一种使用Model(模型)、View(视图)、Controller(控制器)设计创建Web应用程序的模式。
- Model(模型)是应用程序中处理应用程序数据逻辑的部分,通常模型对象负责在数据库中存取数据。
- View(视图)是应用程序中处理数据显示的部分,通常视图是依据模型数据创建的。
- Controller(控制器)是应用程序中处理用户交互的部分,通常控制器负责从视图读取数据,控制用户输入,并向模型发送数据。
1
2
3
4graph TB
A(Controller) -->B(Model)
B(Model) -->A(Controller)
A(Controller) -->C(View)
因数计算器和点餐系统代码详见作业
14. MongoDB芒果数据库
NoSQL简介
- SQL就是Structor Query Language(结构化语言)。MySQL、Oracle、SQL Server数据库都是SQL数据库,在大数据时代有些场景使用它们显得太笨重,因为SQL有一个非常大的缺点,就是限制字段:
1
比如现在数据库中已经有10万条数据了(在今天10万条都不算大数据),如果想从下一条数据开始增加字段,此时之前的10万条数据都需要被更改!表的字段是不能自由更改的,不能某一个条目有一些字段,另外的条目没有!
而SQL的优势在于能够轻松执行复杂查找,例如主从关系查找等。但在大数据时代SQL的优势在衰减(基本都是简单查询),而缺点在被放大。所以此时NoSQL(Not Only SQL)这种==非关系型数据库==应运而生。
NoSQL中没有字段的概念,每个条目可以自由设置字段。
MongoDB安装与配置详见踩坑之各种报错篇,创建好数据库后就可以进行各种指令操作数据库了。
1 | > use student |
15. Node.js和MongoDB的连接
- 先安装依赖
1
cnpm install mongodb --save
文档与集合的关系:
- 文档
- 集合
在REPL环境中最常用的命令语句,除了上面用到的,还有:1
2
3> use student // 使用student数据库,没有就创建
> show dbs // 显示所有数据库列表
> show collections // 显示当前数据库的所有集合列表
增删改查:API
增(mongodb/01.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
46const MongoClient = require('mongodb').MongoClient;
// const assert = require('assert'); // 断言
// 连接的url
const url = 'mongodb://localhost:27017';
// 数据库名
const dbName = 'student';
// 建立一个新连接
const client = new MongoClient(url);
// 用connect方法连接服务
client.connect(function (err, client) {
// assert.equal(null, err); // 断言验证
if (err) {
console.log("数据库连接失败!")
return
}
console.log("数据库连接成功");
// 用db代替数据库名
const db = client.db(dbName);
// 在指定的表内插入一条数据
db.collection('banji01').insertOne({
"name": "刘备",
"age" : 12
}, function (err, r) {
// assert.equal(null, err); // 断言验证
// assert.equal(1, r.insertedCount); // 断言验证
if (err) {
console.log("数据插入失败!")
return
}
console.log("成功插入了" + r.insertedCount + "条数据")
// 插入多条数据的方法
db.collection('banji01').insertMany([{ a: 2 }, { a: 3 }], function (err, r) {
assert.equal(null, err);
assert.equal(2, r.insertedCount);
// 关闭数据库
client.close();
});
});
});删
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
34const MongoClient = require('mongodb').MongoClient;
const url = 'mongodb://localhost:27017';
const dbName = 'myproject';
const client = new MongoClient(url);
client.connect(function(err, client) {
assert.equal(null, err);
console.log("Connected correctly to server");
const db = client.db(dbName);
const col = db.collection('removes');
// Insert multiple documents
col.insertMany([{a:1}, {a:2}, {a:2}], function(err, r) {
assert.equal(null, err);
assert.equal(3, r.insertedCount);
// Remove a single document
col.deleteOne({a:1}, function(err, r) {
assert.equal(null, err);
assert.equal(1, r.deletedCount);
// Update multiple documents
col.deleteMany({a:2}, function(err, r) {
assert.equal(null, err);
assert.equal(2, r.deletedCount);
client.close();
});
});
});
});查
1
2
3
4
5
6
7
8
9
10
11db.collection('banji01').find({"name":"小明"}).toArray(
// 注意与其他的不同,这里find的结果要先转成数组
function(err,results){
if (err) {
console.log("数据查询失败!")
return
}
console.log(results) // 打印出查询到的条目
db.close()
}
)
多个查询条件时,例如寻找姓名是小明且年龄为12的学生,用“ ,”隔开。1
2
3
4
5
6
7
8
9
10
11
12
13db.collection('banji01').find({
"name":"小明",
"age":12
}).toArray(
function(err,results){
if (err) {
console.log("数据查询失败!")
return
}
console.log(results) // 打印出查询到的条目
db.close()
}
)
查询条件中的大于、小于条件1
2
3
4
5
6
7
8
9
10
11
12
13db.collection('banji01').find({
"age":{$gt : 12} // {$gt : 12}表示大于12
//"age":{$lt : 12} // {$lt : 12}表示大于12
}).toArray(
function(err,results){
if (err) {
console.log("数据查询失败!")
return
}
console.log(results) // 打印出查询到的条目
db.close()
}
)
或逻辑:用$or来引导一个数组,里面是或的条件。例如寻找年龄大于12岁的男生或者年龄小于11岁的女生1
2
3
4
5
6
7
8
9
10
11
12
13
14
15db.collection('banji01').find({
{$or : [
{ "age":{$gt:12} , "sex": "男"},
{ "age":{$lt:11} , "sex": "女"}
]}
}).toArray(
function(err,results){
if (err) {
console.log("数据查询失败!")
return
}
console.log(results) // 打印出查询到的条目
db.close()
}
)
- 改:分为增量改和彻底改两种。彻底改用处不大,就是把一条数据完全改成另一条数据,此举危险!
1
2
3
4
5
6
7
8
9
10
11
12
13db.collection('banji01').updateOne(
{"name": "刘备"},
{"age" : 12}
, function (err, r) {
if (err) {
console.log("数据修改失败!")
return
}
console.log("成功")
// 此时"name": "刘备"会被替换成"age" : 12
// "name": "刘备"这个属性就没了!
client.close();
});
增量改:用$set引导1
2
3
4
5
6
7
8
9
10
11
12db.collection('banji01').updateOne(
{"name": "刘备"},
{$set:{"age" : 12}}
, function (err, r) {
if (err) {
console.log("数据修改失败!")
return
}
console.log("成功")
// 此时刘备的age会被替换成12!
client.close();
});
16. MongoDB
数据库 = 存储东西 + 数据库操作API。
React
一、Dashboard是什么
dashboard即仪表盘项目,通俗地讲,就是xxx管理平台,特点:
- 单页面应用(SPA,single page application):以#号为分隔,#号前不变,#号后发生改变,也就是说网页没有发生跳转,而是DOM的整体上树、下树。这样做的好处是,让本地变量可以在本地持久的使用。
- 组件化:
- 所有的DOM元素都是动态上树的,页面上没有了HTML清单,所有的部件都是组件,组件套组件,再集体被js上树。
综上所述,React和Vue,包括Angular,就是提供组件化开发的,单页面应用的,动态上树的模块化开发框架
二、webpack构建工具
解决问题:webpack是Node.js的工作流工具。在它推出之前,前端不认识exports暴露出来的东西,也不认识require这个引入命令,虽然es6推出了export暴露和import引入语法,但仍不能被浏览器识别。为了解决这个问题,之前推出了commonjs和seajs,后来都被webpack取代,由于commonjs是最先提出来的,所以此类规范被称为CMD(通用模块定义)规范。
安装:
1
2npm install -g webpack
npm install -g webpack-cliwebpack的基本使用
main.js
1
2
3import {mianji} from "./yuan.js"
}
alert(mianji(10));
yuan.js
1
2
3 export const mianji = function(r){
return 3.14 * r * r;
}
index.html想要弹出圆面积,就必须引用这两个符合CMD规范的js文件,但此时用webpack命令1
webpack main.js -o all.js
就会生成all.js这个打包后的文件,此时webpack做了三个事情:
- 从main.js出发,顺着import链寻找每一个依赖的js文件
- 将每一个js文件智能打包。如果某个js文件一个export都没用,将不会被打包。
- 去import、export化,也就是说all.js是IE8兼容的。
- 使用webpack.config.js来指导webpack的工作
webpack配置API1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18const path = require('path');
module.exports = {
mode: "production", // 生产环境
entry: "./app/entry", // 入口文件,从这里应用程序开始执行webpack 开始打包
output: { // webpack出口文件
path: path.resolve(__dirname, "dist"), // string
// 所有输出文件的目标路径
// 必须是绝对路径(使用 Node.js 的 path 模块)
filename: "bundle.js", // string
// 打包成bundle.js文件
},
watch: true // 默认为false,开启后实时监控webpack的修改并自动更新!
}
配置好后,只需在终端执行下面语句,即可实现修改文件自动打包(终端不能关闭)1
webpack
三、ES6中的暴露和导入
暴露(export const),注意一定要有const!
yuan.js
1
2
3export const function mianji(r){
return 3.14 * r * r;
}导入(import{xx,xx} from ‘./xxx’),引入的名字和暴露的名字要一致
index.html
1
2
3import {mianji} from './yuan.js'
alert(mianji(10));命名空间:解决引入js文件暴露方法同名问题
yuan.js
1
2
3export const mianji = function(r){
return 3.14 * r * r;
}
fang.js
1
2
3 export const mianji = function(r){
return r * r;
}
index.html
1
2
3
4
5 import * as yuan from './yuan.js'
import * as fang from './fang.js'
alert(yuan.mianji(10));
alert(fang.mianji(10));
- 默认暴露:如果一个js文件是一个类,此时我们不希望有命名空间,就用export default来暴露
people.js
1
2
3
4
5
6
7
8
9
10export default class People{ // 定义类固定写法,注意没()
constructor(name,age,sex){
this.name = name;
this.age = age;
this.sex = sex;
}
changge(){ // 类中的方法要这样写!
alert(`我是${this.name},我今年${this.age}岁啦!`)
}
}
index.html
1
2
3
4 import People from './people.js'
var xiaoming = new People("小米",12,"男");
xiaoming.changge();
四、webpack的高级使用
- babel-loader:babel是用来翻译高级语法的,需要通过-g安装的工具,需要用.babelrc文件进行指导。如果想让webpack在打包过程中顺便对每一个js文件用babel进行翻译,就需要loader(加载器)来帮助。
1
2
3
4// 安装依赖
cnpm i --save-dev @babel/core
cnpm i --save-dev babel-loader
cnpm i --save-dev @babel/preset-env
- babel-core:表示babel的核心。
- babel-loader:表示babel的加载器。
- babel-preset-env:表示babel的预处理器,让babel对ecmascript new version进行翻译。
此处需要特别注意版本问题,原则上babel-loader 8.x对应babel-core 7.x 而babel-loader 7.x对应babel-core 6.x!所以如果按照传统安装方式,很可能babel-loader和babel-core版本不对应产生报错Error: Cannot find module ‘@babel/core’ 而 webpack.config.json中的设置也有所改变:
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 const path = require('path');
module.exports = {
mode: "production", // "production" | "development" | "none"
entry: "./app/entry",
output: {
path: path.resolve(__dirname, "dist"),
filename: "bundle.js"
},
module: { // 关于模块配置
rules: [ // 模块规则(配置 loader、解析器等选项)
{
test: /\.jsx?$/, // 正则以.js结尾的文件
include: [
path.resolve(__dirname, "app") // 包括app文件夹
],
exclude: [
path.resolve(__dirname, "node_modules") //不包括node_modules文件夹
],
loader: "babel-loader",
options: {
// presets: ["env"] 此处变为:
presets: ["@babel/preset-env"]
}
}
]
}
}
- webpack-dev-server
1
cnpm i -g webpack-dev-server
webpack-dev-server会创建一个虚拟的服务,避免开发阶段频繁修带来的磁盘频繁读写,造成对磁盘的伤害。所以目录结构更改为:
- project
- www
- app
- fang.js
- yuan.js
- people.js
- index.html
- app
- node_modules
- package.json
- webpack.config.js
- www
此时配置文件webpack.config.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
28const path = require('path');
module.exports = {
mode: "development",
entry: "./www/app/entry", // 增加www/
output: {
path: path.resolve(__dirname, "www/dist"), // 增加www/
filename: "bundle.js",
publicPath: "/xuni" // 虚拟打包路径
},
module: {
rules: [
{
test: /\.jsx?$/,
include: [
path.resolve(__dirname, "www/app") // 增加www/
],
exclude: [
path.resolve(__dirname, "node_modules")
],
loader: "babel-loader",
options: {
presets: ["@babel/preset-env"]
}
}
]
}
}
以上配置注意路径问题,只有一个加./的,其余都是www/ ;然后就可以这样启动项目:1
webpack-dev-server --content-base ./www --port 8080
- –content-base表示以./www文件夹作为一个根目录
- –port表示端口号是8080
webpack-dev-server在进行两个事情: - 在打包js文件(就是webpack的事情)
- 静态化www文件夹,等于帮我们写了一个app.use(express.static(“www”));
注意:index.html中引入的文件路径要换成:
1
<script src="xuni/bundle.js"></script>
项目的物理磁盘上并没有xuni文件夹,而是通过webpack-dev-server将/xuni/bundle.js文件路由到了打包的文件,这样做的好处是频繁更改也不毁磁盘,而且会自动更新!
但启动项目每次都要写“webpack-dev-server –content-base ./www –port 8080”太麻烦了,此时我们可以通过更改package.json(身份证)文件的配置来进行简化:
package.json
1
2
3 "scripts" : {
"dev" : "webpack-dev-server --content-base ./www --port 8080"
}
这样就可以通过以下命令来启动项目:1
npm run dev
React配置和起步
React需要建立在webpack + babel-loader上
依赖
1
2
3 npm i --save react
npm i --save react-dom
npm i --save babel-preset-react
package.json的options需要增加一项:1
2
3options: {
presets: ["@babel/preset-env","@babel/preset-react"]
}
Vue
一、简介
vue提供了一种“引包法”,初学者在这个模式下学习基本语法,不需要webpack,不需要CMD打包
二、启动
vue的包:https://cn.vuejs.org/js/vue.js,使用方式如下:
1 | <body> |
这里是唯一的一次书写new运算符。这是vue的语法,因为vue是一个构造函数,注意它并不是组件的构造函数,就是启动语法而已。
React中任何一个组件都是一个class(类),并且这个类继承于React.Component。当你放标签的时候,相当于实例化了这个类。
Vue也是一样,任何一个组件也是一个类。但Vue有一个非常大的特点:++语法隐藏底层细节++
三、MVVM特性
清单式语法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23<body>
<div id="app">
<h1>{{a}}</h1>
<button @click="add()">按我+1</button>
</div>
<script type="text/javascript" src="./jslib/vue.js"></script>
<script>
new Vue({
// 运行点
el : "#app",
// 数据
data: {
a : 100
}
// 方法
methods: {
add(){
this.a++
}
}
})
</script>
</body>双向绑定机理
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15<body>
<script>
var obj = {
a : 8
}
Object.defineProperties(obj, {
"a" : {
set(){
alert("改了!快通知视图跟着变!")
}
}
})
</script>
</body>
四、指令
指令,即标签身上的属性,只是这些属性有特殊性、功能性
- v-if和v-show
- v-if决定元素是否上树
- v-show决定元素是否显示
如果需要触发组件的生命周期钩子函数,此时让组件携带v-if,反之用v-show
- v-for:用来实现循环某个节点,循环节点必须绑key
1
2
3
4
5<ul>
<li v-for="(item, index) in arr" v-bind:key="index">
{{ index }} - {{ item }}
</li>
</ul>
特别的,可以循环自然数1
<p v-for="item in 5">{{ item }}</p>
99乘法表
1 | <table> |
- v-bind(==重难点==)
v-bind指令可以让任何W3C属性变“动态”。
1 | <body> |
注意:v-bind:xxx=”…” 冒号里面已经是动态的了,所以在里面的写法要和js语法保持一致!特别注意连字符!
1 | <input tyoe="text" v-bind:value="a" /> |
- v-on:事件监听
- v-model:双向绑定
调色板案例
1 | <!DOCTYPE html> |
- computed和methods