node.js使用文档+webpack+MongoDB搭建服务器

起步

什么是node.js

定义:基于chrome v8引擎的javascript 运行环境

特点:事件驱动,非阻塞式I/O的模型,轻量和高效,单线程、单进程

nodejs能做什么?

可以解析js代码(没有浏览器安全级别的限制)

提供很多系统级别的API:

文件的读写

进程的管理

网络的通信

。。。

v8

谷歌开源的一个高性能 JavaScript 引擎
采用 C++ 编写
Google Chrome 浏览器用的就是这个引擎
V8 可以单独运行,也可以嵌入 C++ 应用当中
V8 会编译、执行 JavaScript 代码,并一样会管理内存、垃圾回收
V8 的高性能以及跨平台等特性,所以它也是 Node.js 的 JavaScript 引擎
JIT 编译出即时机器码,极大提高效率
借鉴java vm 垃圾回收,精确垃圾回收

准备Node.js

使用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 进入命令行

  1. 在浏览器和命令行中运行代码

  2. 在浏览器和node中运行 window process对象

  3. BOM、DOM

编写一个可执行的js文件

1:编写index.js

function greeting(){
   return “hello world”;
}
greeting();
  1. node index.js执行代码

  2. 安装nodemon 实时监测文件的变化

    npm install -g nodemon //全局安装

模块包/CommonJS规范

http://www.commonjs.org
http://javascript.ruanyifeng.com/nodejs/module.html

  • Server-side JavaScript applications
  • Command line tools
  • Desktop GUI-based applications
  • Hybrid applications (Titanium, Adobe AIR)

每个文件就是一个模块,在模块里面定义的变量、函数、类都是私有的

module 代表的当前模块 ,exports属性,代表向外提供接口

require加载模块,读取并执行一个js文件,然后返回该模块的exports对象

模块加载的顺序,按照代码中出现的顺序

模块可以多次加载,第一次加载运行之后,会被缓存

require内部处理流程

模块内容导出:

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模块

在 Node.js 模块系统中,每个文件都被视为一个独立的模块。

内置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();

npm包发布流程

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]

module.exports vs exports

深入了解模块原理

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使用入门

NPM 常用命令

NPM提供了很多命令,例如install和publish,使用npm help可查看所有命令。

使用npm help <command>可查看某条命令的详细帮助,例如npm help install。

在package.json所在目录下使用npm install . -g可先在本地安装当前命令行程序,可用于发布前的本地测试。

使用npm update <package>可以把当前目录下node_modules子目录里边的对应模块更新至最新版本。

使用npm update <package> -g可以把全局安装的对应命令行程序更新至最新版。

使用npm cache clear可以清空NPM本地缓存,用于对付使用相同版本号发布新版本代码的人。

使用npm unpublish <package>@<version>可以撤销发布自己发布过的某个版本代码。

设置npm的registry

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三位,
分别代表主版本号、次版本号和补丁版本号。当代码变更时,版本号按以下原则更新。

  • 如果只是修复bug,需要更新Z位。
  • 如果是新增了功能,但是向下兼容,需要更新Y位。
  • 如果有大变动,向下不兼容,需要更新X位。

软件阶段说明

  • Base:此版本表示该软件仅仅是一个假页面链接,通常包括所有的功能和页面布局,但是页面中的功能都没有做完整的实现,只是做为整体网站的一个基础架构。

  • Alpha :软件的初级版本,表示该软件在此阶段以实现软件功能为主,通常只在软件开发者内部交流,一般而言,该版本软件的Bug较多,需要继续修改,是测试版本。测试人员提交Bug经开发人员修改确认之后,发布到测试网址让测试人员测试,此时可将软件版本标注为alpha版。

  • Beta :该版本相对于Alpha 版已经有了很大的进步,消除了严重错误,但还需要经过多次 测试来进一步消除,此版本主要的修改对象是软件的UI。修改的的Bug 经测试人员测试确认后可发布到外网上,此时可将软件版本标注为 beta版。

  • RC :该版本已经相当成熟了,基本上不存在导致错误的Bug,与即将发行的正式版本相差无几。

  • Release:该版本意味“最终版本”,在前面版本的一系列测试版之后,终归会有一个正式的版本,是最终交付用户使用的一个版本。该版本有时也称标准版。

Package.json版本号规则

  • npm install
  • npm install @version
  • 波浪符号(~) ~1.15.2 >=1.15.2 <1.16.0
  • 插入符号(^) ^3.3.4 >=3.3.4 <4.0.0

创建第一个Web服务器

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')
})


url

//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/'))

Query String

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


HTTP实战

HTTP,(maoyan.com)(get)

//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);

拉钩代理例子(request)

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-proxy-middleware)

//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 ...')
})

http.request 请求发送、接收(post get)

d:get是使用url拿到传过来的参数,post使用 res.on(‘data’, (chunk) => {})接收传过来的参数

post

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...')
  }
})

get

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...')
  }
})

HTTP爬虫Request (cheerio)

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')

File System

  • 得到文件与目录的信息:stat
  • 创建一个目录:mkdir
  • 创建文件并写入内容:writeFile,appendFile
  • 读取文件的内容:readFile
  • 列出目录的东西:readdir
  • 重命名目录或文件:rename
  • 删除目录与文件:rmdir,unlink
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));

Stream

读取文件流
可读流的事件
可写的文件流
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数据加密

crypto模块的目的是为了提供通用的加密和哈希算法。用纯JavaScript代码实现这些功能不是不可能,但速度会非常慢。Nodejs用C/C++实现这些算法后,通过cypto这个模块暴露为JavaScript接口,这样用起来方便,运行速度也快。

MD5和SHA1

MD5是一种常用的哈希算法,用于给任意数据一个“签名”。这个签名通常用一个十六进制的字符串表示.

const crypto = require('crypto');

const hash = crypto.createHash('md5');

hash.update('hello world');

console.log(hash.digest('hex'));

Hmac

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'));

AES

对称加密算法,加密和解密都用相同的秘钥

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、检查文件是否存在,如果不存在,返回错误页面,如果存在

  • 判断文件是文件还是文件夹,打开文件待读取
  • 设置response header
  • 发送文件到客户端

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 = ['<ul>'];
  files.forEach((item, index) => {
    result.push(`<li>${item}</li>`)
  })
  result.push('</ul>')
  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;

socket

websocket

https://www.runoob.com/html/html5-websocket.html

WebSocket 是 HTML5 开始提供的一种在单个 TCP 连接上进行全双工通讯的协议。

WebSocket 使得客户端和服务器之间的数据交换变得更加简单,允许服务端主动向客户端推送数据。在 WebSocket API 中,浏览器和服务器只需要完成一次握手,两者之间就直接可以创建持久性的连接,并进行双向数据传输。

在 WebSocket API 中,浏览器和服务器只需要做一个握手的动作,然后,浏览器和服务器之间就形成了一条快速通道。两者之间就直接可以数据互相传送。

现在,很多网站为了实现推送技术,所用的技术都是 Ajax 轮询。轮询是在特定的的时间间隔(如每1秒),由浏览器对服务器发出HTTP请求,然后由服务器返回最新的数据给客户端的浏览器。这种传统的模式带来很明显的缺点,即浏览器需要不断的向服务器发出请求,然而HTTP请求可能包含较长的头部,其中真正有效的数据可能只是很小的一部分,显然这样会浪费很多的带宽等资源。

HTML5 定义的 WebSocket 协议,能更好的节省服务器资源和带宽,并且能够更实时地进行通讯。
在这里插入图片描述

d:

websocket实现思路

  • new WebSocket, WebSocket 有自己独有的协议ws

  • WebSocket服务端使用 ws第三方模块,接收客户端发来的信息并做处理返回给客户端

websocket的五步法:
第一步:new WebSocket(“ws://localhost:9998/echo”);  打开一个websocket
第二步:onopen :连接建立时触发
第三步:onmessage:客户端接收服务端数据时触发
第四步:onerror:通信发生错误时触发
第五步:close:连接关闭时触发

实现原理

index.html

<!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 + '<br>';
      }

      //用于连接成功之后的回调函数
      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>

websocketServer.js

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)
  }
}

Socket.io

Net

Express介绍和安装

https://www.expressjs.com.cn/
这个翻译的版本比较好http://caibaojian.com/expressjs/index.html

npm install express --save

helloworld

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 ...')
})

自定义中间件外部引入

在这里插入图片描述
myself-middleware.js

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 ...')
})

路由中间件

app.js

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 ...')
})

user.js(用户的路由中间件的模块)

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 generator

搭建框架
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

什么是EJS

EJS是一个简单高效的模板语言,通过数据和模板,可以生成HTML标记文本。可以说EJS是一个JavaScript库,EJS可以同时运行在客户端和服务器端,客户端安装直接引入文件即可,服务器端用npm包安装。

EJS的特点

  • 快速编译和渲染
  • 简单的模板标签
  • 自定义标记分隔符
  • 支持文本包含
  • 支持浏览器端和服务器端
  • 模板静态缓存
  • 支持express视图系统

EJS的成员函数

Render(str,data,[option]):直接渲染字符串并生成html
str:需要解析的字符串模板
data:数据
option:配置选项

EJS的常用标签

<% %>流程控制标签
<%= %>输出标签(原文输出HTML标签)
<%- %>输出标签(HTML会被浏览器解析)
<%# %>注释标签
% 对标记进行转义

Includes


MongoDB

https://www.mongodb.com/download-center/community

MongoDB是一个基于分布式文件存储的数据库。由C++语言编写。旨在为WEB应用提供可扩展的高性能数据存储解决方案。

它的特点:高性能、易部署、易使用,存储数据非常方便。

在Windows系统上安装

为了从命令提示符下运行MongoDB服务器,你必须从MongoDB目录的bin目录中执行mongod.exe文件。
或者将bin配置到环境变量path中。

mongod --dbpath d:\data\db

安装不成功的原因:

1:操作系统版本较低,dll缺失,建议装3.6版本

2:闪退 (cmd : 进入到安装目录 的bin目录,执行 mongod.exe ,看详细的错误信息 )

在Mac系统上安装

在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中可以建立多个数据库。

MongoDB的默认数据库为"db",该数据库存储在data目录中。

MongoDB的单个实例可以容纳多个独立的数据库,每一个都有自己的集合和权限,不同的数据库也放置在不同的文件中。

MongoDB 集合

集合就是 MongoDB 文档组,类似于 RDBMS (关系数据库管理系统:Relational Database Management System)中的表格。

集合存在于数据库中,集合没有固定的结构,这意味着你在对集合可以插入不同格式和类型的数据,但通常情况下我们插入集合的数据都会有一定的关联性。

MongoDB 文档

文档是一个键值(key-value)对(即BSON)。MongoDB 的文档不需要设置相同的字段,并且相同的字段不需要相同的数据类型,这与关系型数据库有很大的区别,也是 MongoDB 非常突出的特点。

一个简单的文档例子如下:
{“genres”: [“犯罪”,“剧情” ],“title”: “肖申克的救赎”}

MongoDB 数据类型

  • Object ID :Documents 自生成的 _id
  • String: 字符串,必须是utf-8
  • Boolean:布尔值,true 或者false
  • Integer:整数 (Int32 Int64 你们就知道有个Int就行了,一般我们用Int32)
  • Double:浮点数 (没有float类型,所有小数都是Double)
  • Arrays:数组或者列表,多个值存储到一个键
  • Object:如果你学过Python的话,那么这个概念特别好理解,就是Python中的字典,这个数据类型就是字典
  • Null:空数据类型 , 一个特殊的概念,None Null
  • Timestamp:时间戳
  • Date:存储当前日期或时间unix时间格式 (我们一般不用这个Date类型,时间戳可以秒杀一切时间类型)

数据库常用命令

  1. (1)Help查看命令提示
  2. help
  3. db.help()
  4. db.test.help()
  5. db.test.find().help()
  6. (2)创建/切换数据库
  7. use music
  8. (3)查询数据库
  9. show dbs
  10. (4)查看当前使用的数据库db/db.getName()
  11. (5)显示当前DB状态
  12. db.stats()
  13. (6)查看当前DB版本
  14. db.version()
  15. (7)查看当前DB的链接机器地址
  16. db.getMongo()
  17. (8)删除数据库
  18. db.dropDatabase()

Collection集合操作

  1. (1)创建一个集合
  2. db.createCollection(“collName”, {size: 20, capped: true, max: 100});
  3. db.collName.isCapped(); //判断集合是否为定容量
  4. (2)得到指定名称的集合
  5. db.getCollection(“account”);
  6. (3)得到当前db的所有集合
  7. db.getCollectionNames();
  8. (4)显示当前db所有集合的状态
  9. db.printCollectionStats();

添加、修改与删除集合数据

(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)

Node连接mongoDB

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()


webpack

官网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

webpack常用的插件和常用的 loader有哪些

插件:
mini-css-extract-plugin
html-webpack-plugin

loader
babel-loader
style-loader
css-loader
sass-loader

自定义配置文件

创建webpack.config.dev.js 配置开发环境

添加快捷指令
在这里插入图片描述
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/

框架搭建

FE

BE

web应用安全

常见漏洞

常见安全技术

sql注入

xss跨站脚本攻击

csrf 跨站请求

Npm scripts

总结

Node.js优点、缺点

异步编程

猜你喜欢

转载自blog.csdn.net/weixin_44157964/article/details/104931730