定义:基于chrome v8引擎的javascript 运行环境
特点:事件驱动,非阻塞式I/O的模型,轻量和高效,单线程、单进程
可以解析js代码(没有浏览器安全级别的限制)
提供很多系统级别的API:
文件的读写
进程的管理
网络的通信
。。。
谷歌开源的一个高性能 JavaScript 引擎
采用 C++ 编写
Google Chrome 浏览器用的就是这个引擎
V8 可以单独运行,也可以嵌入 C++ 应用当中
V8 会编译、执行 JavaScript 代码,并一样会管理内存、垃圾回收
V8 的高性能以及跨平台等特性,所以它也是 Node.js 的 JavaScript 引擎
JIT 编译出即时机器码,极大提高效率
借鉴java vm 垃圾回收,精确垃圾回收
使用nvm安装并维护多个node.js版本
1:nvm:https://github.com/nvm-sh/nvm
Mac:
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.35.3/install.sh | bash
或
wget -qO- https://raw.githubusercontent.com/nvm-sh/nvm/v0.35.3/install.sh | bash
#mac 环境变量
export NVM_DIR="$HOME/.nvm"
[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh" # This loads nvm
[ -s "$NVM_DIR/bash_completion" ] && \. "$NVM_DIR/bash_completion" # This loads nvm bash_completion
nvm --version
windows 安装:
https://github.com/coreybutler/nvm-windows
参考:https://www.cnblogs.com/dreamsqin/p/10885082.html
下载:Nvm-setup.zip
使用:
nvm install 版本号
nvm install --lts 长时间支持的稳定版本
nvm use 版本号
nvm uninstall 版本号
nvm ls-remote //查看所有的node 版本信息
$ node 进入命令行
在浏览器和命令行中运行代码
在浏览器和node中运行 window process对象
BOM、DOM
1:编写index.js
function greeting(){
return “hello world”;
}
greeting();
node index.js执行代码
安装nodemon 实时监测文件的变化
npm install -g nodemon //全局安装
http://www.commonjs.org
http://javascript.ruanyifeng.com/nodejs/module.html
每个文件就是一个模块,在模块里面定义的变量、函数、类都是私有的
module 代表的当前模块 ,exports属性,代表向外提供接口
require加载模块,读取并执行一个js文件,然后返回该模块的exports对象
模块加载的顺序,按照代码中出现的顺序
模块可以多次加载,第一次加载运行之后,会被缓存
模块内容导出:
module.exports = "字符串";//可以的
module.exports.msg = '字符串'//可以的
exports.msg = '字符串'//可以的
exports = '字符串'//不行的
检查Moducle._cache, 是否缓存了指定模块
如果缓存没有的话,就创建一个新的module实例将它保存到缓存
module.load()去加载指定的模块
如果解析的过程中,出现异常,就从缓存中删除该模块
返回该模块的module.exports
global.address='beijing'
// a.js
exports.x = 'a1';
console.log('a.js ', require('./b.js').x);
exports.x = 'a2';
// b.js
exports.x = 'b1';
console.log('b.js ', require('./a.js').x);
exports.x = 'b2';
// main.js
console.log('main.js ', require('./a.js').x); //b.js a1 a.js b2 main.js a2
//console.log('main.js ', require('./b.js').x); //a.js b1 b.js a2 main.js b2
如果上面两个console.log同时执行,打印/b.js a1 a.js b2 main.js a2 main.js b2 ,因为模块被缓存了,不会再重新加载了, require('./b.js').x的值还是第一次加载的值b2
在 Node.js 模块系统中,每个文件都被视为一个独立的模块。
const http = require('http');
const path = require('path');
const qs = require('querystring');
//涉及文件的读写,。。。
const fs = require('fs')
const url = require('url');
// var rs = path.resolve(__dirname, '../dev/');
// console.log(rs);
var gp18 = require('gp18-custom-001');
console.log(gp18.count(), gp18.getStudentlist())
function GP18() { }
GP18.prototype = {
count() {
return 38;
},
getStudentlist() {
return [
{
name: 'zhangyongfeng',
age: 18
},
{
name: 'zhouguanglei',
age: 20
}
]
}
}
module.exports = new GP18();
1:注册npm账号
注册地址:https://www.npmjs.com/
2:创建你要发布的项目
执行npm init , 生成Package.json , 添加包的名称(唯一),版本号,入口文件
3:执行发布
npm login
npm publish
4:更新
改变package.json里面版本号, npm publish
5:使用方式
npm i *****, 安装自己使用的包了
6:卸载已经发布的包
npm unpublish [email protected]
hello.js
var s = 'Hello';
var name = 'world';
console.log(s + ' ' + name + '!');
Node.js加载了hello.js后,它可以把代码包装一下,变成这样执行:
(function () {
// 读取的hello.js代码:
var s = 'Hello';
var name = 'world';
console.log(s + ' ' + name + '!');
// hello.js代码结束
})();
这样一来,原来的全局变量s现在变成了匿名函数内部的局部变量。如果Node.js继续加载其他模块,这些模块中定义的“全局”变量s也互不干扰。
所以,Node利用JavaScript的函数式编程的特性,轻而易举地实现了模块的隔离。
但是,模块的输出module.exports怎么实现?
这个也很容易实现,Node可以先准备一个对象module:
// 准备module对象:
var module = {
id: 'hello',
exports: {}
};
var load = function (module) {
// 读取的hello.js代码:
function greet(name) {
console.log('Hello, ' + name + '!');
}
module.exports = greet;
// hello.js代码结束
return module.exports;
};
var exported = load(module);
var exports= module.exports;
//exports="hellworld"; 错误
// 保存module:
save(module, exported);
可见,变量module是Node在加载js文件前准备的一个变量,并将其传入加载函数,我们在hello.js中可以直接使用变量module原因就在于它实际上是函数的一个参数:
module.exports = greet;
通过把参数module传递给load()函数,hello.js就顺利地把一个变量传递给了Node执行环境,Node会把module变量保存到某个地方。
由于Node保存了所有导入的module,当我们用require()获取module时,Node找到对应的module,把这个module的exports变量返回,这样,另一个模块就顺利拿到了模块的输出:
NPM提供了很多命令,例如install和publish,使用npm help可查看所有命令。
使用npm help 可查看某条命令的详细帮助,例如npm help install。
在package.json所在目录下使用npm install . -g可先在本地安装当前命令行程序,可用于发布前的本地测试。
使用npm update 可以把当前目录下node_modules子目录里边的对应模块更新至最新版本。
使用npm update -g可以把全局安装的对应命令行程序更新至最新版。
使用npm cache clear可以清空NPM本地缓存,用于对付使用相同版本号发布新版本代码的人。
使用npm unpublish @可以撤销发布自己发布过的某个版本代码。
1.原npm地址
npm config set registry http://registry.npmjs.org
2.设置国内镜像
a.通过config命令
npm config set registry https://registry.npm.taobao.org
npm info underscore (如果上面配置正确这个命令会有字符串response)
b.命令行指定
npm --registry https://registry.npm.taobao.org info underscore
c.编辑 ~/.npmrc 加入下面内容
registry = https://registry.npm.taobao.org
3.使用nrm管理registry地址
a.下载nrm
npm install -g nrm
b.添加registry地址
nrm add npm http://registry.npmjs.org
nrm add taobao https://registry.npm.taobao.org
c.切换npm registry地址
nrm use taobao
nrm use npm
搜索镜像: https://npm.taobao.org
建立或使用镜像,参考: https://github.com/cnpm/cnpmjs.org
GNU 风格的版本号命名格式
Major_Version_Number.Minor_Version_Number[.Revision_Number[.Build_Number]]
主版本号.子版本号[.修正版本号[.编译版本号]]
语义版本号分为X.Y.Z三位,
分别代表主版本号、次版本号和补丁版本号。当代码变更时,版本号按以下原则更新。
Base:此版本表示该软件仅仅是一个假页面链接,通常包括所有的功能和页面布局,但是页面中的功能都没有做完整的实现,只是做为整体网站的一个基础架构。
Alpha :软件的初级版本,表示该软件在此阶段以实现软件功能为主,通常只在软件开发者内部交流,一般而言,该版本软件的Bug较多,需要继续修改,是测试版本。测试人员提交Bug经开发人员修改确认之后,发布到测试网址让测试人员测试,此时可将软件版本标注为alpha版。
Beta :该版本相对于Alpha 版已经有了很大的进步,消除了严重错误,但还需要经过多次 测试来进一步消除,此版本主要的修改对象是软件的UI。修改的的Bug 经测试人员测试确认后可发布到外网上,此时可将软件版本标注为 beta版。
RC :该版本已经相当成熟了,基本上不存在导致错误的Bug,与即将发行的正式版本相差无几。
Release:该版本意味“最终版本”,在前面版本的一系列测试版之后,终归会有一个正式的版本,是最终交付用户使用的一个版本。该版本有时也称标准版。
const http = require('http')
var app = http.createServer((request, response) => {
let str = request.url;
response.write(str);
response.end();
})
// .listen(3000, (err) => {
// if (err) {
// console.log(err);
// } else {
// console.log('localtion start 3000')
// }
// })
app.listen(3000, () => {
console.log('localtion start 3000')
})
//parseQueryString:解析url的query,解析为js对象
//slashesDenoteHost,如果为true,则 协议的//之后至下一个/之前的字符串解析为host
url.parse(urlString[, parseQueryString[,slashesDenoteHost]])
url.format(urlObject)
url.resolve(from, to)
const url = require('url');
var strUrl = '//www.lagou.com/a/b?name=zhangsan&age=20';
// var rs = url.parse(strUrl, true, true)
var params = {
protocol: 'https:',
host: 'www.lagou.com',
port: '8080',
hostname: 'www.lagou.com',
hash: '#position',
search: '?name=zhangsan&age=20',
pathname: '/a',
}
rs = url.format(params)
console.log(rs);
console.log(url.resolve(rs, '../c/'))
querystring.escape(str)
querystring.parse(str[, sep[, eq[, options]]])
querystring.stringify(obj[, sep[, eq[, options]]])
querystring.unescape(str)
const qs = require('querystring');
var strUrl = 'https://www.baidu.com/s?a^3714#b^5568';
// var tmp = qs.escape(strUrl);
// console.log(tmp);
// console.log(qs.unescape(tmp))
// console.log(qs.parse(strUrl, '&', '='));
var params = {
name: 'wanghao',
age: 20
}
// console.log(qs.stringify(params));//name=wanghao&age=20
console.log(qs.stringify(params, '#', '^'));//name^wanghao#age^20
//https://maoyan.com/edimall/product
const http = require('http');//如果是https则需要导入https模块
let str = 'http://localhost:3005/positionlist';
http.get(str, (res) => {
let { statusCode } = res;
var error;
//1**:通知,2**:成功,3** 重定向,4**:客户端错误,5** 服务器错误
//302 移动 304缓存
if (statusCode != 200) {
error = new Error('数据异常')
}
//内容的编码,决定浏览器用什么的形式,来读取这个文件
let contentType = res.headers["content-type"];
console.log(contentType)
if (!/application\/json/.test(contentType)) {
error = new Error('数据内容不正确');
}
if (error) {
console.log(error.message);
//释放内存
res.resume();
return;
}
var rawdata = '';
res.on('data', function (chunk) {
rawdata += chunk;
})
//d:接收数据
res.on('end', function () {
try {
console.log(rawdata)
} catch (err) {
console.log(err);
}
})
}).on('error', function (err) {
console.log(err);
})
d:当不知道完整路径的时候可以使用url.format拼接路径
const http = require('http');
const url = require('url')
// let str = 'http://localhost:3005/positionlist';
var params = {
protocol: 'http:',
host: 'localhost:3005',
pathname: '/positionlist'
}
let str = url.format(params);
d:当访问第一个地址时返回第二个地址的数据
//http://localhost:8099/listmore.json?pageNo=2&pageSize=15
//https://m.lagou.com/listmore.json?pageNo=2&pageSize=15
const https = require('https');
const http = require('http');
const baseUrl = 'https://m.lagou.com';
http.createServer((req, res) => {
let url = req.url;
console.log(url);
// res.end('hello world')
res.setHeader('Content-Type', 'application/json;charset=utf-8')
res.setHeader('Access-Control-Allow-Origin', '*.baidu.com')
//m.baidu.com,fe.baidu.com
https.get(baseUrl + url, function (res1) {
//d:数据接收时是数据流,一块一块的返回
var rawdata = '';
res1.on('data', function (chunk) {
rawdata += chunk;
})
//d:数据接收完毕,输送到界面
res1.on('end', function () {
res.write(rawdata);
res.end();
})
})
}).listen(8099, function () {
console.log('localhost 8099 start ...')
})
//http://localhost:8099/fetch/listmore.json?pageNo=2&pageSize=15
//https://m.lagou.com/listmore.json?pageNo=2&pageSize=15
//http-proxy-middleware
const https = require('https');
const http = require('http');
const proxy = require('http-proxy-middleware');
const baseUrl = 'https://m.lagou.com';
http.createServer((req, res) => {
if (/\/fetch/.test(req.url)) {
var params = {
target: 'https://m.lagou.com',
changeOrigin: true,
pathRewrite: {
'^/fetch': ''
}
}
// proxy(params)返回的是一个函数
var demoProxy = proxy(params);
return demoProxy(req, res);
} else {
res.write('hello world')
res.end();
}
}).listen(8099, function () {
console.log('localhost 8099 start ...')
})
d:get是使用url拿到传过来的参数,post使用 res.on(‘data’, (chunk) => {})接收传过来的参数
d:client发送数据,server接收数据
client.js
const http = require('http')
const qs = require('querystring');
var user = {
name: 'wanghao',
age: 20
}
//server url:http://localhost:3000
var app = http.request({
protocol: 'http:',
hostname: 'localhost',
port: 3000,
method: 'post'
}, (res) => {
//d:接收服务端返回的数据
var raw = '';
res.on('data', (chunk) => {
raw += chunk;
})
res.on('end', (chunk) => {
console.log(raw)
})
})
//数据发送 d:发送数据需要用qs.stringify转换
app.write(qs.stringify(user))
app.end();
Server.js
const http = require('http');
//d:创建一个server接收client发送的数据
var serverApp = http.createServer((request, response) => {
//d:接收数据,流式接收
var rawdata = ''
request.on('data', (chunk) => {
rawdata += chunk;
})
//d:数据接收完成之后
request.on('end', () => {
console.log(rawdata);
//d:给client应答
response.write('ok');
response.end();
})
})
serverApp.listen(3000, (err) => {
if (!err) {
console.log('localhost 3000 start...')
}
})
Client.js
const http = require('http')
const qs = require('querystring');
var user = {
name: 'wanghao',
age: 20
}
//server url:http://localhost:3000
var app = http.request({
protocol: 'http:',
hostname: 'localhost',
port: 3000,
path: '/user?' + qs.stringify(user),
method: 'GET'
}, (res) => {
var raw = '';
res.on('data', (chunk) => {
raw += chunk;
})
res.on('end', (chunk) => {
console.log(raw)
})
})
app.end();
server.js
const http = require('http');
const url = require('url');
var serverApp = http.createServer((request, response) => {
//d:get是使用url拿到传过来的参数
let strUrl = request.url;
let query = url.parse(strUrl, true).query;
console.log(query.name, query.age)
response.write('ok:' + query.name);
response.end();
})
serverApp.listen(3000, (err) => {
if (!err) {
console.log('localhost 3000 start...')
}
})
https://www.microsoftstore.com.cn/
yarn add cheerio
//https://www.microsoftstore.com.cn/
const https = require('https');
const $ = require('cheerio');
function filterData(data) {
// console.log(data);
var items = $(data).find('.listContainerInner li');
// console.log(items.length);
var result = [];
$(items).each((index, item) => {
let $item = $(item);
var param = {
url: $item.find('img').attr('data-src'),
name: $item.find('.name h4').text().replace(/\n|\t/g, ''),
price: $item.find('.price strong').text()
}
result.push(param);
})
console.log(result);
}
var app = https.request({
protocol: 'https:',
hostname: 'www.microsoftstore.com.cn',
port: 443,
method: 'GET'
}, (res) => {
var rawdata = '';
res.on('data', (chunk) => {
rawdata += chunk;
})
res.on('end', () => {
// console.log(rawdata)
filterData(rawdata);
})
})
app.end()
// /api/login d:登录
// /api/logout d:退出
const http = require('http');
const qs = require('querystring');
http.createServer((req, res) => {
var url = req.url;
var rawdata = '';
//d:判断请求方式
console.log('method:', req.method);
res.setHeader('Content-Type', 'application/json;charset=utf-8;')
//d:接收数据
req.on('data', (chunk) => {
rawdata += chunk;
})
req.on('end', () => {
console.log(url);
switch (url) {
case '/api/login':
let username = qs.parse(rawdata).username;
var result = {
code: 1,
message: '登录成功',
username
}
//d:不能直接返回对象,需要转换成json字符串
res.end(JSON.stringify(result));
break;
case "/api/logout":
var result = {
code: 1,
message: '退出成功'
}
res.write(JSON.stringify(result));
res.end();
}
})
}).listen(3000, () => {
console.log('3000 start ...')
})
const eventEmitter = require('events');
//d:自定义事件需要继承eventEmitter
class MyEventEmitter extends eventEmitter {
constructor(){
super()
}
}
const em = new MyEventEmitter();
//d:事件订阅
// em.on('play', function (movie) {
// console.log('play ....', movie)
// })
//d:只执行一次
em.once('play', function (movien,a) {
//d:this===em
console.log('play ....', movie,a,this===em)
})
//d:事件派发
em.emit('play', '封神榜',"a")
em.emit('play', '西游记')
自定义事件:
var Events = {
listens: {},
//d:订阅事件
on: function (eventName, cb) {
if (this.listens[eventName]) {
this.listens[eventName].push(cb);
} else {
this.listens[eventName] = [cb];
}
},
//d:派发事件,循环获取有多少个订阅事件
trigger: function (eventname) {
for (let i = 0; i < this.listens[eventname].length; i++) {
this.listens[eventname][i].call();
}
}
}
Events.on('loginsuccess', () => {
console.log('login success')
})
Events.trigger('loginsuccess')
Events.trigger('loginsuccess')
const fs = require('fs');
//创建一个文件,添加内容,异步方式,回调函数:错误优先的原则
fs.writeFile('./log.txt', 'hello world', function (err) {
})
//同步创建文件,不用写回调函数
fs.writeFileSync('./log1.txt', 'hello')
//异步删除文件
fs.unlink('./log1.txt', function () {
})
//同步删除文件
fs.unlinkSync()
//修改文件的名字
fs.renameSync('log.txt', 'lognew.txt')
console.log('hello world')
//追加内容
fs.appendFileSync('lognew.txt', 'gp18')
//文件内容的读取
let rs = fs.readFileSync('lognew.txt')
console.log(rs.toString());
//文件夹
//创建文件夹
if (!fs.existsSync('./log')) {//如果不存在文件夹,就创建一个
fs.mkdirSync('./log')
}
fs.writeFileSync('./log/1.txt', 'hello world !');
//如果文件内,存在内容,不能直接删除
fs.rmdirSync('./log')
//删除文件夹使用递归
//删除文件夹
function delfile(src) {
//读取文件的状态信息
var rs = fs.statSync(src);
if (rs.isDirectory()) {
//读取文件夹下的数据
var files = fs.readdirSync(src)
files.forEach((item, index) => {
let curPath = src + '/' + item;
//if (fs.statSync(curPath).isDirectory()) {
delfile(curPath);
//} else {
// fs.unlinkSync(curPath);
//}
})
fs.rmdirSync(src);
} else {
fs.unlinkSync(src)
}
}
delfile('./log')
json的基本操作
const fs = require('fs');
var data = require('./data.json')
let user = {
name: 'wanghao',
age: 20
}
//创建并添加内容
// fs.writeFileSync('data.json', JSON.stringify(user));
console.log(data);
//修改json文件的内容
data.address = 'beijing';
fs.writeFileSync('data.json', JSON.stringify(data));
读取文件流
可读流的事件
可写的文件流
pipe链式使用
pipe
const fs = require('fs');
const zlib = require('zlib');
fs.writeFileSync('log.txt', 'gp18');
fs.createReadStream('log.txt')
.pipe(zlib.createGzip())//这时候压缩文件还在内存中
.pipe(fs.createWriteStream('log.txt.gzip'));//输出到物理目录
crypto模块的目的是为了提供通用的加密和哈希算法。用纯JavaScript代码实现这些功能不是不可能,但速度会非常慢。Nodejs用C/C++实现这些算法后,通过cypto这个模块暴露为JavaScript接口,这样用起来方便,运行速度也快。
MD5是一种常用的哈希算法,用于给任意数据一个“签名”。这个签名通常用一个十六进制的字符串表示.
const crypto = require('crypto');
const hash = crypto.createHash('md5');
hash.update('hello world');
console.log(hash.digest('hex'));
Hmac算法也是一种哈希算法,它可以利用MD5或SHA1等哈希算法。不同的是,Hmac还需要一个密钥;
只要密钥发生了变化,那么同样的输入数据也会得到不同的签名,因此,可以把Hmac理解为用随机数“增强”的哈希算法。
const crypto = require('crypto');
const hash = crypto.createHmac('sha256', 'secret-key');
hash.update('hello world');
console.log(hash.digest('hex'));
对称加密算法,加密和解密都用相同的秘钥
const crypto = require('crypto');
//数据加密
function aesEncrypt(data, key) {
//用指定的算法和秘钥,返回一个cipher对象
const cipher = crypto.createCipher('aes192', key)
var crypted = cipher.update(data, 'utf8', 'hex')
crypted += cipher.final('hex');
return crypted;
}
//数据解密
function aesDecrypt(encrypted, key) {
const decipher = crypto.createDecipher('aes192', key)
var decrypted = decipher.update(encrypted, 'hex', 'utf8');
decrypted += decipher.final('utf8')
return decrypted;
}
var data = ' hello gp18';
var key = 'password';
//d:加密数据
var encrypted = aesEncrypt(data, key)
console.log('encrypted:', encrypted)
//d:解密数据
var decrypted = aesDecrypt(encrypted, key)
console.log('decrypted:', decrypted)
通过启动一个服务访问index.html页面,并且加载图片和css资源
const http = require('http');
const fs = require('fs')
const data = require('./user.json')
http.createServer((req, res) => {
var url = req.url;
//d:设置默认路径加载 index.html
if (url === '/') {
url = '/index.html'
}
if (url.indexOf('favicon.ico') > -1) {
res.end('')
}
//d:获取该服务下的完整路径
var fullpath = __dirname + url;
console.log(url);
//d:通过url判断文件类型
switch (url) {
case "/index.html":
res.setHeader('Content-Type', 'text/html');
break;
case "/logo.png":
res.setHeader('Content-Type', 'image/png');
break;
case "/index.js":
res.setHeader('Content-Type', 'application/x-javascript');
break;
case "/api/getlist":
res.setHeader('Content-Type', 'application/json;chartset=utf-8;')
res.write(JSON.stringify(data))
res.end()
return;
}
//d:读取路由内容并输出
let result = fs.readFileSync(fullpath)
res.write(result);
res.end()
})
.listen(3000, function () {
console.log('localhost 3000 start...')
})
d:步骤:
1、在本地启动一个server,等待来自客户端的请求
2、当请求抵达时,根据请求的url,设置静态资源的路径为www
,映射到文件的位置
3、检查文件是否存在,如果不存在,返回错误页面,如果存在
4、等待来自客户端的下一个请求
Server.js
const http = require('http');
const content = require('./util/content')
const proxy = require('http-proxy-middleware')
const mimeTypes = require('./util/mimeTypes')
const path = require('path');
//静态资源的路径
const statPath = __dirname + '/www';
//解决History强制刷新的问题
function refershHistory(res) {
res.writeHead(302, {
'location': 'http://localhost:3000/'
})
res.end();
return false;
}
//设置代理
function setProxy(url, req, res) {
if (/\/api/.test(url)) {
console.log('proxy')
let options = {
target: 'http://localhost:3005',
changeOrigin: true,
pathRewrite: {
'^/api': ''
}
}
let statProxy = proxy(options);
return statProxy(req, res)
}
if (/\/static/.test(url)) {
console.log('proxy')
let options = {
target: 'https://www.lgstatic.com',
changeOrigin: true,
pathRewrite: {
'^/static': ''
}
}
let statProxy = proxy(options);
return statProxy(req, res)
}
}
var app = http.createServer((req, res) => {
let url = req.url;
//默认索引页
if (url === '/') {
url = "/index.html"
}
//history强制刷新问题
if (/\?position|search|profile/.test(url)) {
return refershHistory(res);
}
//代理
if (/\/api/.test(url) || /\/static/.test(url)) {
return setProxy(url, req, res);
}
let extname = path.extname(url).substring(1);
let conentType = mimeTypes[extname] ? mimeTypes[extname] : "text/html";
res.setHeader('Content-Type', conentType)
let result = content(url, statPath);
res.write(result);
res.end();
})
app.listen(3000, function (err) {
if (!err) {
console.log('localhost 3000 start ...')
}
})
util/content.js 根据url读文件内容
const fs = require('fs');
//d:statPath绝对路径
function content(src, statPath) {
let fullPath = statPath + src;
console.log('fullPath:', fullPath)
//d:判断是文件夹还是文件
if (fs.existsSync(fullPath)) {
let stat = fs.statSync(fullPath);
if (stat.isDirectory()) {
return readDir(fullPath);
} else {
return fs.readFileSync(fullPath);
}
} else {
return 'file not found'
}
}
//d:读取文件夹里内容
function readDir(src) {
let files = fs.readdirSync(src);
let result = [''
];
files.forEach((item, index) => {
result.push(`${item}`)
})
result.push('')
return result.join(' ')
}
module.exports = content;
util/mimeType.js
let mimes = {
'css': 'text/css',
'less': 'text/css',
'gif': 'image/gif',
'html': 'text/html',
'ico': 'image/x-icon',
'jpeg': 'image/jpeg',
'jpg': 'image/jpeg',
'js': 'text/javascript',
'map': 'text/javascript',
'json': 'application/json',
'pdf': 'application/pdf',
'png': 'image/png',
'svg': 'image/svg+xml',
'swf': 'application/x-shockwave-flash',
'tiff': 'image/tiff',
'txt': 'text/plain',
'wav': 'audio/x-wav',
'wma': 'audio/x-ms-wma',
'wmv': 'video/x-ms-wmv',
'xml': 'text/xml',
'woff2': 'application/octet-stream',
'woff': 'application/octet-stream',
'ttf': 'application/octet-stream'
}
module.exports = mimes;
https://www.runoob.com/html/html5-websocket.html
WebSocket 是 HTML5 开始提供的一种在单个 TCP 连接上进行全双工通讯的协议。
WebSocket 使得客户端和服务器之间的数据交换变得更加简单,允许服务端主动向客户端推送数据。在 WebSocket API 中,浏览器和服务器只需要完成一次握手,两者之间就直接可以创建持久性的连接,并进行双向数据传输。
在 WebSocket API 中,浏览器和服务器只需要做一个握手的动作,然后,浏览器和服务器之间就形成了一条快速通道。两者之间就直接可以数据互相传送。
现在,很多网站为了实现推送技术,所用的技术都是 Ajax 轮询。轮询是在特定的的时间间隔(如每1秒),由浏览器对服务器发出HTTP请求,然后由服务器返回最新的数据给客户端的浏览器。这种传统的模式带来很明显的缺点,即浏览器需要不断的向服务器发出请求,然而HTTP请求可能包含较长的头部,其中真正有效的数据可能只是很小的一部分,显然这样会浪费很多的带宽等资源。
HTML5 定义的 WebSocket 协议,能更好的节省服务器资源和带宽,并且能够更实时地进行通讯。
d:
new WebSocket, WebSocket 有自己独有的协议ws
WebSocket服务端使用 ws第三方模块
,接收客户端发来的信息并做处理返回给客户端
websocket的五步法:
第一步:new WebSocket(“ws://localhost:9998/echo”); 打开一个websocket
第二步:onopen :连接建立时触发
第三步:onmessage:客户端接收服务端数据时触发
第四步:onerror:通信发生错误时触发
第五步:close:连接关闭时触发
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
#msglist {
width: 400px;
height: 500px;
background: #eeeeee;
}
</style>
</head>
<body>
<div id="msglist">
</div>
<textarea id="msg" rows="5" cols="10">
</textarea>
<button id="btn">留言</button>
<script>
var $btn = document.querySelector("#btn");
var $msg = document.querySelector("#msg")
var $list = document.querySelector('#msglist')
//http => new WebSocket('ws://***')
//https => new WebSocket('wss://***') 不能是ip
var ws = new WebSocket('ws://localhost:8099')
function wsEvent() {
//用于指定当从服务器接受到信息时的回调函数
ws.onmessage = (msg) => {
console.log(msg.data)
$list.innerHTML += msg.data + '
';
}
//用于连接成功之后的回调函数
ws.onopen = () => {
console.log('open ...')
}
//用于指定连接关闭后的回调函数
ws.onclose = () => {
console.log('close')
reconnect()
}
ws.onerror = () => {
console.log('error')
reconnect()
}
}
wsEvent();
$btn.addEventListener('click', () => {
//数据的发送
ws.send($msg.value)
})
//重连
function reconnect() {
if (ws.readyState === 2 || ws.readyState === 3) {
ws = new WebSocket('ws://localhost:8099');
wsEvent();
}
}
</script>
</body>
</html>
ws
第三方模块https://www.npmjs.com/package/ws
const ws = require('ws');
var websocketServer = new ws.Server({ port: 8099 });
//所有的客户端列表
var clientlist = {}
var id = 0;
websocketServer.on('connection', (client) => {
client.id = id++;
clientlist[id] = client;
console.log('connection...')
client.send('welcome to back')
client.on('message', (msg) => {
console.log(msg)
boardcast(client.id + ":" + msg);
})
//client退出时的事件
client.on('close', () => {
boardcast(client.id + " 下线了");
delete clientlist[client.id];
})
})
//广博通知,所有客户端
function boardcast(message) {
for (let o in clientlist) {
clientlist[o].send(message)
}
}
https://www.expressjs.com.cn/
这个翻译的版本比较好http://caibaojian.com/expressjs/index.html
npm install express --save
const express = require('express');
const app = express();
app.get('/', (req, res) => {
res.send('hello world');// res.write + res.end
})
app.listen(3000, () => {
console.log('localhost start 3000 ...')
})
const express = require('express');
const app = express();
app.get('/', (req, res) => {
res.send('hello world');// res.write + res.end
})
app.post('/', (req, res) => {
res.write('post hello world');
res.end()
})
app.put('/', (req, res) => {
res.send('put hello world')
})
app.delete('/', (req, res) => {
res.send('delete hello world')
})
app.listen(3000, () => {
console.log('localhost start 3000 ...')
})
const express = require('express');
const path = require('path')
const app = express();
//可以配置多个静态资源目录, d:静态资源目录可以直接用server+文件名进行访问
app.use(express.static('public'))
// app.use(express.static('files'))
//可以给目录添加前缀
// app.use('/static', express.static('files'))
//配置为绝对路径 d:当server目录和静态资源目录不在同级下时,可以配置绝对路径
app.use('/static', express.static(path.resolve(__dirname, './files')))
app.listen(3000, () => {
console.log('localhost start 3000 ...')
})
const express = require('express');
const path = require('path')
const app = express();
var myLogger = function (req, res, next) {
console.log('log ....')
//转向下一个路由,中间件
next();
}
var requestTime = function (req, res, next) {
console.log(new Date().getTime())
next();
}
//应用中间件,中间件有前后顺序
app.use(requestTime, myLogger);
// app.use(myLogger);
app.get('/', function (req, res) {
res.send('hello world');
})
app.listen(3000, () => {
console.log('localhost start 3000 ...')
})
module.exports = function (){
return function (req,res,next){
console.log("myselt middleware")
next()
}
}
app.js内导入
const mySelfMidd = require("./myself-middleware.js")
app.use(mySelfMidd )
const express = require('express');
const app = express();
//应用中间件
// app.use('*', function (req, res, next) {
// console.log('method:', req.method);
// console.log('middleware:', req.params)
// next();
// });
app.use('*', function (req, res, next) {
console.log(1)
next();
}, function (req, res, next) {
console.log(2)
next();
});
app.get('/user/:id/books/:bookid', function (req, res) {
console.log('get:', req.params)
res.send('hello world');//d:直接返回
})
app.listen(3000, () => {
console.log('localhost start 3000 ...')
})
const express = require('express');
const bodyParser = require('body-parser');
const userRouter = require('./user');//d:导入user模块
const app = express();
app.use('/static', express.static('files'))
app.use(function (req, res, next) {
console.log('application middleware ..')
next();
})
//extended: false 表示是否使用系统模块 querystring ,官方推荐
//extended: true 使用第三方qs来处理
app.use(bodyParser.urlencoded({ extended: false }))
app.use(bodyParser.json())//d:最终输出的数据都是以json输出的
//d:中间件是有前后顺序的,使用body-parser需要放在路由的user模块之前,当使用路由中间件的时候才会成功使用body-parser
app.use('/user', userRouter)//d:使用user模块,添加了一个 /user 的前缀
//错误优先的原则
app.use(function (err, req, res, next) {
console.log(err.stack);
res.status(200).send('somting is wrong!')
})
app.listen(3000, () => {
console.log('localhost start 3000 ...')
})
const express = require('express');
var router = express.Router();
//针对user下所有的路由接口
router.use(function (req, res, next) {
//获取传过来的用户信息
//let username = req.body.username;
//从数据库查询
//let rs = db.getUserinfo(uername)
// if(rs){next()}
// else {
// res.send('用户不存在')
// }
console.log('权限验证')
next();
})
//针对user下 /:id 的接口做权限验证
router.use('/:id', function (req, res, next) {
console.log('id 权限验证')
next();
})
//http://localhost:3000/user/10
// router.get("/:id", function (req, res) {
// console.log(req.params.id)
// res.send(req.params.id)
// })
//d:获取名字的接口
router.get('/getname', function (req, res) {
// res.setHeader('Content-Type','')
console.log("get name ...")
//d:可以直接使用json返回
res.json({
name: '肖战',
age: 20
})
})
//crud: create read update delete
//数据的添加,消息体在body上
//建议全部更新
router.post('/add', function (req, res) {
//用req.body获取post数据,需要装body-parser中间件
console.log(req.body);
res.send(req.body)
})
//获取数据,请求的参数在url地址上,在不同的浏览器上有长度的限制
//长度的限制是浏览器限制的,不是http协议限制的
//http://localhost:3000/user/getinfo?username=hanye&age=20
router.get('/getinfo', function (req, res) {
//req.query 不需要装任何中间件,或者配置
console.log(req.query)
res.send(req.query)
})
//数据的更新,消息体在body 上
//建议全部更新
router.put('/r1', function (req, res) {
res.send('r1')
});
//数据的更新,消息体在body 上
//建议部分更新 {name:'zhangsan',age:20,address:"beijing"}
router.patch('/r2', function (req, res) {
res.send('r2')
})
//数据的删除,消息体在body上
router.delete('/r3', function (req, res) {
res.send('r3')
})
//数据的协商,测试服务端的一些功能
router.options('/r4', function (req, res) {
res.send('r4')
})
// head
// 类似于get请求,返回的响应中没有类容,用于获取报头
router.get('/r5', function (req, res) {
throw new Error('error message')
})
module.exports = router;
通常写在最后面,参数必须是四个
router.get('/r5', function (req, res) {
throw new Error('error message')//抛出异常
})
//错误优先的原则
app.use(function (err, req, res, next) {
console.log(err.stack);
res.status(200).send('somting is wrong!')
})
express.static //静态资源的设置,html,图片,。。.
express.json //解析传入的的数据请求,为json格式
express.urlencoded parses incoming requests with URL-encoded payloads. NOTE: Available with Express 4.16.0+
官方推荐:https://www.expressjs.com.cn/resources/middleware.html
app.use(bodyParser.urlencoded({ extended: false }))
app.use(bodyParser.json())
get: http://localhost:3000/user/42
http://localhost:3000/users/34/books/8989
http://localhost:3000/flights/LAX-SFO
埋点:http://localhost:3000/user/hm.gif?id=1&keyword=
Post: http://localhost:3000/user/add {username:‘xiaoming’,age:20}
const express = require('express');
var router = express.Router();
//http://localhost:3000/user/42
// router.get('/:id', (req, res) => {
// res.send(req.params.id)
// })
//http://localhost:3000/user/66/books/88
router.get('/:id/books/:bookid', (req, res) => {
res.json({
id: req.params.id,
bookid: req.params.bookid
})
})
// http://localhost:3000/user/flights/LAX-SFO
router.get('/flights/:from-:to', (req, res) => {
res.send({
from: req.params.from,
to: req.params.to,
})
})
//http://localhost:3000/user/hm.gif?id=1&keyword=china
router.get('/hm.gif', (req, res) => {
res.send(req.query)
})
//http://localhost:3000/user/add
router.post('/add', (req, res) => {
res.send({
code: 1,
message: '数据获取成功',
data: req.body //需要装第三方中间件,body-parser
})
})
module.exports = router;
搭建框架
express --view==ejs ./
安装依赖
npm install -g express-generator
//异常处理
var createError = require('http-errors');
var express = require('express');
var path = require('path');
var cookieParser = require('cookie-parser');
//日志处理模块
var logger = require('morgan');
var indexRouter = require('./routes/index');
var usersRouter = require('./routes/users');
var app = express();
// view engine setup
//指定模板存储的位置
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'ejs');
app.use(logger('dev'));
app.use(express.json());
app.use(express.urlencoded({ extended: false }));
app.use(cookieParser());
app.use(express.static(path.join(__dirname, 'public')));
app.use('/', indexRouter);
app.use('/users', usersRouter);
// catch 404 and forward to error handler
app.use(function (req, res, next) {
next(createError(404, 'file not found!'));
});
// error handler
app.use(function (err, req, res, next) {
// set locals, only providing error in development
//全局的配置信息
res.locals.message = err.message;
res.locals.error = req.app.get('env') === 'development' ? err : {};
// render the error page
res.status(500);
//参数是模板的名字
res.render('error');
});
module.exports = app;
EJS是一个简单高效的模板语言,通过数据和模板,可以生成HTML标记文本。可以说EJS是一个JavaScript库,EJS可以同时运行在客户端和服务器端,客户端安装直接引入文件即可,服务器端用npm包安装。
Render(str,data,[option]):直接渲染字符串并生成html
str:需要解析的字符串模板
data:数据
option:配置选项
<% %>流程控制标签
<%= %>输出标签(原文输出HTML标签)
<%- %>输出标签(HTML会被浏览器解析)
<%# %>注释标签
% 对标记进行转义
https://www.mongodb.com/download-center/community
MongoDB是一个基于分布式文件存储的数据库。由C++语言编写。旨在为WEB应用提供可扩展的高性能数据存储解决方案。
它的特点:高性能、易部署、易使用,存储数据非常方便。
为了从命令提示符下运行MongoDB服务器,你必须从MongoDB目录的bin目录中执行mongod.exe文件。
或者将bin配置到环境变量path中。
mongod --dbpath d:\data\db
安装不成功的原因:
1:操作系统版本较低,dll缺失,建议装3.6版本
2:闪退 (cmd : 进入到安装目录 的bin目录,执行 mongod.exe ,看详细的错误信息 )
在Mac OS上面安装MongoDB,你可以通过编译源代码来安装 ,也可以在Mac OS上使用Homebrew安装。
使用Homebrew安装MongoDB:
方法一:$ brew install mongodb ( brew install [email protected] )
方法二:https://github.com/mongodb/homebrew-brew
启动:
mongod --config /usr/local/etc/mongod.conf
一个mongodb中可以建立多个数据库。
MongoDB的默认数据库为"db",该数据库存储在data目录中。
MongoDB的单个实例可以容纳多个独立的数据库,每一个都有自己的集合和权限,不同的数据库也放置在不同的文件中。
集合就是 MongoDB 文档组,类似于 RDBMS (关系数据库管理系统:Relational Database Management System)中的表格。
集合存在于数据库中,集合没有固定的结构,这意味着你在对集合可以插入不同格式和类型的数据,但通常情况下我们插入集合的数据都会有一定的关联性。
文档是一个键值(key-value)对(即BSON)。MongoDB 的文档不需要设置相同的字段,并且相同的字段不需要相同的数据类型,这与关系型数据库有很大的区别,也是 MongoDB 非常突出的特点。
一个简单的文档例子如下:
{“genres”: [“犯罪”,“剧情” ],“title”: “肖申克的救赎”}
(1)添加
db.users.save({name:‘zhangsan’,age:25,sex:true})
db.users.save({name:‘lisi’,age:20,sex:true,address:‘beijing’})
(2)修改
//1:查询条件
//2:更新字段
//3: 是否需要将参数2插入到集合当中,如果没有查询到记录,配置为false,否则为true
//4:更新一条,还是多条, true:多条记录,false:单条记录
db.users.update({age:25},{$set:{name:‘changeName’}},false,true)
(3)删除
db.users.remove({age:23})
(1)查询所有记录
db.users.find()
(2)查询去重后数据
db.users.distinct('name')
(3)查询age = 20的记录
db.users.find({age:20})
(4)查询age > 22的记录
db.users.find({age:{$gt:22}})
(5)查询age < 22的记录
db.users.find({age:{$lt:22}})
(6)查询age >= 23的记录
db.users.find({age:{$gte:23}})
(7)查询age <= 23的记录
db.users.find({age:{$lte:23}})
(8)查询age >= 23 并且 age <= 25
db.users.find({age:{$gte:23,$lte:25}})
(9)查询name中包含 change的数据
db.users.find({name:/change/})
mysql:select * from users where name '%change%'
(10)查询name中以ch开头的
db.users.find({name:/^ch/})
(11)查询指定列name、age数据
db.users.find({},{name:1,age:1,_id:0})
(12)查询指定列name、age数据, age > 23
db.users.find({age:{$gt:23}},{name:1,age:1,_id:0}
(13)按照年龄排序
升序:db.users.find({},{name:1,age:1,_id:0}).sort({age:1})
降序:db.users.find({},{name:1,age:1,_id:0}).sort({age:-1})
(14)查询name = lisi, age = 20的数据
db.users.find({name:'lisi',age:20},{name:1,age:1,_id:0})
(15)查询前3条数据
db.users.find().limit(3)
(16)查询5条以后的数据
db.users.find(),skip(5)
(17)查询在3-5之间的数据
db.users.find().limit(3).skip(2)
(18)or与 查询
db.users.find({$or:[{age:25},{age:23}]})
(19)查询第一条数据
db.users.findOne()
(20)查询某个结果集的记录条数
db.users.find({age:20}).count()
导入数据:
db.movies.insertMany([{},{}])
//查询所有数据
db.movies.find()
//查询电影列
db.movies.find({},{title:1,_id:0})
//查询电影名称、年份 , 评分
db.movies.find({},{title:1,year:1,_id:0,'rating.average':1})
//按照评分排序
db.movies.find({},{title:1,year:1,_id:0,'rating.average':1}).sort({'rating.average':-1})
//按照年份排序
db.movies.find({},{title:1,year:1,_id:0,'rating.average':1}).sort({year:-1})
//模糊查询
db.movies.find({title:/人/},{title:1,_id:0})
//获取总条数
db.movies.find({title:/人/},{title:1,_id:0}).count()
//根据翻页获取指定页面的数据
db.movies.find({},{title:1,_id:0}).skip(5).limit(5)
db.js
const mongoose = require('mongoose');
mongoose.connect('mongodb://127.0.0.1:27017/games', {
useNewUrlParser: true,
useUnifiedTopology: true
})
var db = mongoose.connection;
db.on('error', console.error.bind(console, 'connection error:'));
db.once('open', function () {
console.log('open....')
})
module.exports = mongoose;
Index.js
const db = require('./db');
var UserModel = db.model('users', {
name: String,
age: Number,
sex: Boolean,
address: String
})
async function find() {
let rs = await UserModel.find({});
console.log(rs);
}
find();
function add() {
UserModel.insertMany([{ name: 'user5', age: 20 }, { name: 'user6', age: 21 }], function (err, docs) {
console.log(docs)
})
}
// add();
// async function update() {
// let rs = await UserModel.update({ name: 'user6' }, { $set: { age: 31 } })
// console.log(rs);
// }
// update();
// async function del() {
// let rs = await UserModel.remove({ name: "user6" })
// console.log(rs);
// }
// del()
官网https://www.webpackjs.com/concepts/
Entry :入口
output:出口
Plugins: 插件,执行范围更广的任务。插件的范围包括,从打包优化和压缩,一直到重新定义环境中的变量
Loader:翻译官,把浏览器不识别的内容做转换
yarn add webpack webpack-cli //基础依赖
yarn add webpack-dev-server -D //热更新
yarn add mini-css-extract-plugin -D//将css文件提取为独立的文件的插件
yarn add html-webpack-plugin -D // 将模板文件和js文件整合到一块
yarn add sass-loader node-sass -D
插件:
mini-css-extract-plugin
html-webpack-plugin
loader
babel-loader
style-loader
css-loader
sass-loader
创建webpack.config.dev.js 配置开发环境
const path = require('path')
const htmlWebpackPlugin = require('html-webpack-plugin')
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
module.exports = {
//开发模式:development,代码调试, 生产模式:production ,代码压缩,优化
//如果mode不配置,会有警告提示信息
mode: "development",
//代码调试,可以看到源代码
devtool: 'inline-source-map',
//入口文件,如果不配置,默然找根目录下 /src/index.js
// entry:"./src/js/index.js",
entry: {//多页面,两个入口
'main': "./src/js/index.js",
'detail': "./src/js/detai.js"
},
//打包完成,输出文件的位置(不能写相对路径),可以不配置,默认输出到dist目录
output: {
path: path.resolve(__dirname, './dev'),
filename: '[name]-[hash].js'
},
//d:热更新
devServer: {
port: 9099,
//打开浏览器
open: true
},
// d:将模板文件和js文件整合到一块
plugins: [
new htmlWebpackPlugin({
title: 'webpack demo home',
//模板文件路径
template: "./src/views/index.html",
//自动存储到output 配置的目录
filename: 'index.html',
chunks: ['main']//指定模块,让index.html的页面的内容只加载该页面对应的js的内容,如果不加的话也会同时加载detail页面的内容
}),
//d:多页面配置
new htmlWebpackPlugin({
title: 'webpack demo home',
//模板文件路径
template: "./src/views/detail.html",
//自动存储到output 配置的目录
filename: 'detail.html',
chunks: ['detail']
}),
//d:将css文件提取为独立的文件的插件
new MiniCssExtractPlugin({
//页面引用的样式文件名
filename: '[name]-[hash].css',
// chunkFilename: 'common.css',
})
],
module: {
rules: [
{
test: /\.s[ac]ss$/,
//执行顺序:从右向左
use: [
// 将 JS 字符串生成为 style 节点
'style-loader',
// MiniCssExtractPlugin.loader, //这个和style-loader二者选其一,都是使用css的方式
//将 CSS 转化成 CommonJS 模块
'css-loader',
//把.scss文件文件转换为.css文件
'sass-loader'
]
}
]
}
}
前后端分离: H5+Nodejs+express
前端(FE): RMVC
后端: RM(V)C
技术栈: es6+webpack+jQuery+nodejs+express+MongoDB+handlebar+…
UI框架: https://adminlte.io/