三阶段-node

三阶段安排:

三阶段: 两个月 node express mvc项目结构搭建 1 vue (移动端m站 电商) 3 react 2周 + 2-3天

微信(小程序 公众号 app 查漏补缺(大屏可视化 echarts 地图 ))

git remote add origin(地址)  git push origin master 

npm (node 包 管理器)

npm网址:https://www.npmjs.com/

功能:管理项目、管理项目中依赖的资源包

// 初始化 创建一个package.json
npm init [-y/--yes]
/*
安装包  注意区分 环境
production 生产环境,生产环境的包项目上线后还需要这个包 如 jquery
development 开发环境 辅助项目开发,上线后不需要 如 gulp-babel

安装包 一定注意区分开发环境和生产环境
*/ 
npm install 包名[@版本号][--save/--save-dev][-g]
// eg 简写

npm i 包 [-S/D] // i install 简写 -S --save简写 -D --save-dev简写

npm install // 当我们拉取代码,是没有node_modules目录的,执行npm install自动安装项目的所有的依赖包(自动读取package.json文件)
npm run 脚本名称
npm uninstall 包[-g]
/* 装一个包,版本错误 需要先卸载再安装 */

npm update 包名 // 更新版本
npm info 包名 // 查看包信息
npm publish // 查看包的所有历史版本信息 

 

npm 官方服务器地址 美国

npm 淘宝源

  • 使用cnpm     

  •  

    npm install cnpm -g --registry=https://registry.npm.taobao.org
    // npm命令 由npm 改为cnpm 就行 cnpm install 包名 
  • nrm 切换源
  • npm i nrm -g // 全局安装
    nrm ls //查看可用源
    nrm use 源  // 切换源

    nvm (切换 node版本)

yarn 第三方包管理器(react 默认包管理器是yarn)

yarn.lock

npm i yarn -g
yarn init 
yarn add 包 // 默认安装到生产还环境 npm i 包 -S
yarn add 包 --dev // 安装到开发环境
yarn remove 包 // 移除一个包
yarn add 包 --global // 全局安装
yarn // 安装项目所以依赖 npm install
yarn 脚本名 // 执行脚本 npm run 脚本

时常要关注的计算机网站:  segmentfault    掘金

 

nodejs 中模块化

commonjs

  • 避免了 变量污染问题(每个模块有自己的作用域)

  • 按需加载 不同 模块(js 运行效率更高 在 js 中动态引入)

系统模块 全局模块

直接引入 直接使用

const fs = require("fs");
fs.readFile();

第三方模块

当前项目 通过 npm i 安装在 node_modules 中的模块

const 模块名 = require("模块名");

总结: 系统模块和第三方模块,使用时直接 require 写模块名(不要写路径)

注意:当我们引入一个模块 直接引入模块名时,优先找全局模块,如果没有查找 node_modules 也没有报错

自定义模块

  • 一个文件 没有导出 接口 那么整个文件就是一个模块,可以直接引入这个文件,相当于这个文件的代码直接在引入代码中运行(引入模块文件有自己作用域)
// a.js
const fn = () => {
  console.log("我是一个函数");
};
fn();
// b.js
require("./a.js");
/*
1,直接require即可不需要使用变量接收模块 (没有向外导出模块)
2,即使是同一目录下,也需要 加 路径 ./否则会解析成全局模块或者 node_modules中的第三方模块
*/
  • 一个文件 导出多个接口
// a.js
exports.a = 100;
exports.b = [1,2,3,4];
exports.c = ()=>{
  console.log('我是一个函数');
}
// b.js引入
// 全部引入
const obj = require('./a.js')
// obj是一个对象 三个属性 分别是 a/b/c
// 解构赋值 按需引入
const { a,c } = require('./a.js')
// 定义两个变量a,c值是 a.js中a模块和c模块的值
  • 一个文件只能向外导出一个接口
// a.js

module.exports =// 引入 b.js
const 变量  = require('./a.js')

/* 
注意:
  一个文件只能出现一次module.exports如果出现多次module.exports那么下面会覆盖上面的
  一个文件即出现了 module.exports 也出现了 exports 那么以module.exports为准
*/

node中常用的全局模块

fs文件系统

全局变量

__dirname // 当前文件所在目录的绝对路径
__filename // 当前文件所在的绝对路径
process.cwd() // 当前进程运行所在的绝对路径(js在哪个目录下运行的)
/* 
绝对路径:
  如果有服务器环境
  那么 (绝对路径)根目录是服务器监听目录(phpstudy apache监听 WWW目录)
  如果没有服务器环境。那么绝对路径根目录是 盘符
  
注意:
  / 写在路径最前面 代表绝对路径根目录

*/

path模块

操作路径 path.join() 方法会将所有给定的 path 片段连接到一起(使用平台特定的分隔符作为定界符),然后规范化生成的路径。

长度为零的 path 片段会被忽略。 如果连接后的路径字符串为长度为零的字符串,则返回 '.',表示当前工作目录。

path.join('/目录1', '目录2', '目录3/目录4', '目录5', '..');
// 返回: '/目录1/目录2/目录3/目录4'

path.join('目录1', {}, '目录2');
// 抛出 'TypeError: Path must be a string. Received {}'
// 结合__dirname
path.join(__dirname,'a','b')

path.resolve([...paths]) 分析 分界符 得出 绝对路径

path.resolve("a","b","c")
// 当前目录下
// d://当前目录/a/b/c
path.resolve("a","/b","c")
// d://b/c

path.extname(path) 得到一个路径 指向文件的后缀名

URL模块

组成部分:协议名 主机(域名) 端口 path(/a/b/c) search(?query) hash https://www.xxx.com:3000/a/b/c?a=10&b=30#aaa https 协议名 www.xxx.com 域名 主机名 3000 端口 /a/b/c path 路径 ?a=10&b=3 search 不带? a=10&b=3 query #aa hash

 

http 

const http = require('http');
http.get('http://www.tupianzj.com/meinv/mm/me/meituwang/',(res) => {
   res.setEncoding('utf8');
   let rawData = '';
   res.on('data',(chunk) => {
rawData += chunk;
}) //on在这里是用来监听
res.on('end',() => {
console.log(rawData);
});
}.on('reeor',(e) => {
console.log(`出现错误:${e.message}`);
})

 

 http的简易爬虫-爬网页的src

const http = require('http');
const cheerio = require('cheerio');
http.get('http://www.tupianzj.com/meinv/mm/me/meituwang/',(res) => {
   res.setEncoding('utf8');
   let rawData = '';
   let arr = [];
   res.on('data',(chunk) => {
     rawData += chunk;
   }) //on在这里是用来监听
   res.on('end',() => {
     const $ = cheerio.load(rawData);
     $('img').each((i,el) => {
         arr.push($(el).attr('src'))
     })
     console.log(arr)
   });
}.on('reeor',(e) => {
console.log(`出现错误:${e.message}`);
})

espress框架

nodejs: express      koa2(基于express封装)

egg.js(mvc框架)

expres启动服务器

const express = require('express');
const app = express();

// 新增一个路由
app.get('/a', (req,res)=>{
  res.send('你好啊');// 向前端返回数据可以是字符串或者对象
})

app.listen(3000, ()=>{
  console.log('start at port 3000')
})

nodemon 启动js

自动监听 文件变化,自动重新运行

npm i nodemon -g
// 运行js
nodemon js文件

接口文档(数据接口文档)

后端写数据接口(一个路由 请求方式 发送什么参数,给什么数据) 接口文档:

项目流程:(产品) 立项 -- 需求收集 -- 得到需求(整理) -- 产品经理 (设计原型) UI 设计图 -- 项目(后端+前端)设计数据库(接口文档)

前端拿到设计图要写页面(mock接口 接口文档)

后端写接口

前后端联调(联合调试)

测试 上线 迭代

c/s b/s

express 路由如何解析 请求参数

  •  get请求的参数解析
req.query // 自动序列化为对象
// username=小明&pwd=123456
{
  username: "小明",
  pwd: 123456
}
  • post 请求参数 (请求体 request body)

 xhr.setRequestHeader('content-type', 'application/x-www-form-urlencoded') //设置响应头

 

// x-www-form-urlencoded
/* 
机器(浏览器和服务器)传输数据 字符串 不能传多字节

key=v&key=v 且中间多字节 会转换成 % 加密格式
*/

 

application/json格式(需要后端支持)

formdata 对象可以传(urlencoded)

express的中间件

大部分框架设计都是渐进式 拔插式接口(使用中间件或者插件) 把很多其他的功能作为了 框架插件(中间件) 想要具备某个功能,只需要使用这个中间件(插件)即可

中间件语法

定义中间件

const middleware = (req,res,next)=>{
  req.a = 10;
  next();
}

使用中间件

// 作为全局中间 会拦截所有的请求
app.use(中间件)
// 作为局部中间件  拦截特定url
app.use(url,中间件)

body-parser 中间件 解析body请求的参数

安装body-parser

npm i body-parser -s

引入

const  bodyParser  = require (' body-parser ')
const express = require('express');
const bodyparser = require('body-parser');
const app = express();
app.use(bodyparser.urlencoded ({
   extendned: false
}));
app.use(bodyparser.json())
app.post('/b',(req,res) => {
   console.log (req.body);
   res.send ('我是一个post请求')
});
app.listen(3000, ()=>{
  console.log('start at port 3000')
})

登录小案例

const express = require('express');
const bodyparser = require('body-parser');
cosnt user = {
   username: '小明',
   pwd: 123456
}
const app = express();
app.use(bodyparser.urlencoded ({
   extendned: false
}));
app.use(bodyparser.json())
app.post('/login',(req,res) => {
   if (username === user.username) {
     if (parseInt(pwd) === parseInt(user.pwd)) {
        res.send('登录成功')
     }else {
        res.send('账户或密码不正确') 
     }
   }else {
       res.send('没有当前用户')
   }
});
app.listen(3000, ()=>{
  console.log('start at port 3000')
})

搭建MVC框架

应用程序一种设计模式 M model 数据模型(获取数据库数据的函数) C controller 控制器 (程序主要逻辑 嵌套路由中的) V view 视图(html/css)

路由中间件

路由 除了app.get .post 定义路由,路由中间件也可以定义路由 路由中间件是一个小型的app(应用程序实例)app上大部分方法 路由中间都有 eg: get post use

// routes/home.js
const router = require('express').Router();

router.get('/home', (req,res)=>{
  res.send('我是首页')
})

router.post('/home', (req,res)=>{
  res.send('我是首页的post请求')
})

module.exports = router;
// 在 index.js 入口函数 中引入并使用中间件即可
const homeRouter = require('./routes/home')
 
app.use(homeRouter);

静态资源中间件 服务器开启静态资源服务

public 

app.use('/static', express.static(path.join(__dirname,'public')))

模板引擎

模板是指网页结构(html) 用于动态渲染 数据 到模板(视图 html)上 功能:在html上写 类似于 语言语法(循环、判断、函数。。。)

jade (侵入式 破坏式)

div

  p

    你是p的内容

 

ejs 非侵入式 没有html原本写法,将.html后缀名改为.ejs express中如何 使用ejs模板引擎

// 安装
npm i ejs -S

// 设置模板(views)的目录
app.set('views', path.join(__dirname,'views'))
// 设置使用哪个模板引擎
app.set('view engine', 'ejs')
// 路由中使用 渲染哪个模板(ejs)
res.render('模板名字')// render第二个参数携带的变量可以在 模板中使用
// 输出
<%= 表达式 %>
/* 
表达式
js环境
需要使用了变量 必须是  render携带过来的变量
*/
<%%>  // 可以写任意js 一般写循环或者if分支结构
/* 
<ul>
    <% for(var i=0;i<arr.length;i++){ %>
      <li><%= arr[i] %></li>
    <% } %>
  </ul>
*/
<%- content %>
// 渲染富文本数据 转义成html 谨慎使用 有可能 xss攻击
// 引入公共模板
<%- include('head') -%>

渲染模板 语句:表达式 关键字 运算符组成 var a = 10; 表达式:运行后是一个值 (具体的值、变量也是表达式、运算、三目表达式、短路) 

multer 上传文件的 中间件

const router = require('express').Router();
const multer = require('multer');
const path = require('path')
var storage = multer.diskStorage({
  destination: function (req, file, cb) {
    cb(null, './public/uploads')
  },
  filename: function (req, file, cb) {
    // 自定义文件名的
    /* console.log(file);
    console.log(path.extname(file.originalname)) */
    // 后缀名
    const extname = path.extname(file.originalname);
    cb(null, file.fieldname + '-' + Date.now()+extname);
  }
})
const upload = multer({ storage: storage })
router.get('/upload', (req,res)=>{
  res.render('upload');
})

router.post('/upload', upload.single('img') ,(req,res)=>{
  res.send('上传成功');
})

module.exports = router;

// 在index文件中引入并使用
const uploadRouter = require('./routes/upload');
app.use(uploadRouter);

mongodb

非关系型数据  存储数据格式 就是json  mongodb redis

关系型数据:mysql oracle

对比
        关系型                               非关系型

        数据库(database) (数据仓库)       数据库(database)
        表     (table)                        collection 集合  [{},{},{}]
        行     row                            document 文档 json
        列 字段  col                           key

mongodb运行

  • 安装目录 bin下cmd 运行
mongod [--dbpath D:\h5-2008\node\data]
// dbpath指定mongodb数据库的存储目录 如果不指定 默认是盘符下的 data/db
  • 添加环境变量 将mongodb/bin 添加到环境变量中 就可以在任意一个目录的cmd中运行

mongodb 原生sql 语句

show dbs  查看有哪些数据库
use 数据库 切换到这个数据库
show collections 查看当前数据库下 有哪些 集合 (前提 你得先切换到目标数据库)
一下 所有的操作 都需要切换到 目标数据库才能执行

db.createCollection("名字")  创建一个集合
db.集合名.drop()
以上命令 都不重要(这些操作 我们一般会在可视化软件中操作)

下面 增删改查 才是重点

robot 3T mongodb可视化 

mongodb 非关系型数据

mysql oracle 关系型

关系型数据库
数据库
  表
    行
      字段

在可视化软件中操作mongod原生方法

最简单查看文档的方法就是find(),会检索集合中所有的文档结

db.集合名.find()  

要以格式化的方式显示结果,可以使用pretty()方法。

db.集合名.find().pretty()

1.固值寻找

寻找age集合里面所有含有属性值为wscats的文档结果,相当于where name = 'wscats'

db.age.find({name:"wscats"})

2.范值寻找

 

操作    语法    示例    等效语句
相等    {:}    `db.age.find({"name":"wscats"}).pretty()`    where name = 'wscats'
小于    {:{$lt:}}    `db.age.find({"likes":{$lt:50}}).pretty()`    where likes < 50
小于等于    {:{$lte:}}    `db.age.find({"likes":{$lte:50}}).pretty()`    where likes <= 50
大于    {:{$gt:}}    `db.age.find({"likes":{$gt:50}}).pretty()`    where likes > 50
大于等于    {:{$gte:}}    `db.age.find({"likes":{$gte:50}}).pretty()`    where likes >= 50
不等于    {:{$ne:}}    `db.age.find({"likes":{$ne:50}}).pretty()`    where likes != 50

 

3.AND和OR寻找

 

AND

在find()方法中,如果通过使用将它们分开传递多个键,则mongodb将其视为AND条件。 以下是AND的基本语法

寻找_id为1并且name为wscats的所有结果集

db.age.find(
{
   $and: [
      {"_id": 1}, {"name": "wscats"}
   ]
}
)

OR

 

在要根据OR条件查询文档,需要使用$or关键字。以下是OR条件的基本语法

 

寻找name为corrine或者name为wscats的所有结果集

db.age.find(
{
   $or: [
      {"name": "corrine"}, {“name“: "wscats"}
   ]
}
)

AND和OR等结合

相当于语句where title = "wscats" OR ( title = "corrine" AND _id < 5)

db.age.find({
$or: [{
 "title": "wscats"
}, {
 $and: [{
   "title": "corrine"
 }, {
   "_id": {
     $lte: 5
   }
 }]
}]
})

插入文档

文档的数据结构和JSON基本一样。 所有存储在集合中的数据都是BSON格式。 BSON是一种类json的一种二进制形式的存储格式,简称Binary JSON

要将数据插入到mongodb集合中,需要使用mongodb的insert()save()方法。

db.集合名.insert(document)

比如我们可以插入以下数据

db.wscats.insert({

title: 'MongoDB Tutorials', 
description: 'node_tutorials',
by: 'Oaoafly',
url: 'https://github.com/Wscats/node-tutorial',
tags: ['wscat','MongoDB', 'database', 'NoSQL','node'],
num: 100,
})

也可以支持插入多个,注意传入的是数组形式

 

db.wscats.insert([{
_id: 100,
title: ‘Hello’
},{
_id: 101,
title: ‘World’
}])

在插入的文档中,如果不指定id参数,那么mongodb会为此文档分配一个唯一的ObjectId 要插入文档,也可以使用db.post.save(document)。如果不在文档中指定id,那么save()方法将与insert()方法一样自动分配ID的值。如果指定id,则将以save()方法的形式替换包含**id**的文档的全部数据。

db.wscats.save({
_id: 111,
title: 'Oaoafly Wscats', 
})

更新文档

1.update()方法

寻找第一条title为wscats的值,并且更新值title为corrine和age为12

db.age.update({
'title': 'wscats'
}, {
$set: {
 'title': 'corrine',
 'age': 12
}
})

默认情况下,mongodb只会更新一个文档。要更新多个文档,需要将参数multi设置为true,还可以配合find方法里面的各种复杂条件判断来筛选结果,然后更新多个文档

寻找所有title为wscats的值,并且更新值title为corrine和age为12

db.age.update({
'title': 'wscats'
}, {
$set: {
 'title': 'corrine',
 'age': 12
}
}, {
multi: true
})

2.save()方法

_id主键为3的文档,覆盖新的值,注意_id为必传

db.age.save({
'_id':3,
'title': 'wscats'
})

删除文档

删除主键_id为3的文档,默认是删除多条

db.age.remove({
'_id':3
})

建议在执行remove()函数前先执行find()命令来判断执行的条件是否正确

如果你只想删除第一条找到的记录可以设置justOne为1,如下所示

db.age.remove({...},1)

全部删除

db.age.remove({})

Limit与Skip方法 (结合find方法使用)

Limit

如果你需要在mongodb中读取指定数量的数据记录,可以使用mongodb的Limit方法,limit()方法接受一个数字参数,该参数指定从mongodb中读取的记录条数。

db.age.find().limit(数量)

Skip

我们除了可以使用limit()方法来读取指定数量的数据外,还可以使用skip()方法来跳过指定数量的数据,skip方法同样接受一个数字参数作为跳过的记录条数。

db.age.find().limit(数量).skip(数量)
//skip()方法默认值为0
current: 当前页
pageSize:一个多少条

10
1  0 9
2  10 19
3  20 29

db.age.find().limit(pageSize).skip((current-1)*pageSize)

所以我们在实现分页的时候就可以用limit来限制每页多少条数据(一般固定一个值),用skip来决定显示第几页(一个有规律变动的值)

排序

在mongodb中使用使用sort()方法对数据进行排序,sort()方法可以通过参数指定排序的字段,并使用1和-1来指定排序的方式,其中1为升序排列,而-1是用于降序排列

1 升序排列 -1 降序排列

db.集合名.find().sort({键值(属性值):1})

age集合表重新根据_id主键进行降序排列

db.age.find().sort({
"_id": -1
})

利用mongodb(nodejs) 插件 连接 mongodb 了解 不推荐

内部写的是mongodb 原生的sql语法
 为什么不推荐:mongodb是非关系型数据库,对于数据不做验证,在实际开发过程中是可取的
 
 // Requires official Node.js MongoDB Driver 3.0.0+
    var mongodb = require("mongodb");

    var client = mongodb.MongoClient;
    var url = "mongodb://localhost:27017/";

    client.connect(url,{useUnifiedTopology:true}, function (err, client) {
        if(err){
            client.close();
        }
        var db = client.db("test");
        var collection = db.collection("stu");

        var query = {
            age:{
                $gt:20
            }
        };

        var cursor = collection.find(query);

        cursor.forEach(
            function(doc) {
                console.log(doc);
            }, 
            function(err) {
                client.close();
            }
        );

        // Created with Studio 3T, the IDE for MongoDB - https://studio3t.com/

    });

 

总结:

mongodb 数据是一 json格式存储的

数据库(相当于表)
    document (相当
  collection 集合于 行)
      key (键)


db.stu.find({
})
db.stu.insert([{}])

db.stu.update(
  {
    _id:1
  },
  {
    $set: {}
  }
)
db.stu.remove({})

// 查询
.sort({
  key: -1
})
.limit().skip()

db.xxx.find().sort({_id:-1}).limit(pageSize).skip((current-1)*pageSize)

mongod的缺点:mongodb原生语法很宽松,对于集合不会做任何验证

利用 mongoose 连接mongodb 重点

nodejs库 封装好很多的方法 用于连接或者操作数据库
 对于mongodb 数据库中的数据做了类型的验证  ***

 typejs  超集

 http://www.mongoosejs.net/docs/
 中文文档

连接:
    



 两个 重要概念

 Schema  计划  映射 了 一个 collection 里面所有的字段,对于字段做了 类型验证
    const mongoose = require("mongoose")
    const { Schema } = mongoose;
    定义个schema
    const cateSchema = new Schema({
        cateName:{
            type:String,
            required:true
        },
        cateIcon:String,
        pid:{
            type:Number,
            required:true
        },
        showHome:{
            type:Number,
            default:1
        }
    });
 model    collection的实例(collection),定义时需要传入 他的 Schema
    下 提供了很多的方法 用于操作 sql语句
    定义model
    const cateModel = mongoose.model("qf_cate",cateSchema)
    命名最好使用下划线命名法

model下的方法

mongoose的优点:操作数据库前,做了 验证

npm i mongoose //命令行下载mongoose

const mongoose = require('mongoose');
mongoose.connect('mongodb://localhost/test',{
  useNewUrlParser: true,
  useUnifiedTopology: true
});

var db = mongoose.connection;
db.on('error', console.error.bind(console, 'connection error:'));
db.once('open', function() {
  // we're conected!
  console.log('mongodb上线了')
});

/*
schema
理解为 集合(collection) 结构,
定义一个集合的字段以及字段类型
(操作集合如增加一条数据,都会经过schema验证,验证通过允许操作,否则报错)
*/ 

// 学生表结构
const stuSchema = new mongoose.Schema({
  a: String,
  b: {
    type: Number,
    required: true
  },
  c: {
    type: String,
    default: '我是默认值'
  },
  d:[String, Number]
})

// 数据类型有
String
Number
Date
Buffer
Boolean
Mixed
ObjectId
Array

// 创建表  model
const stuModel = mongoose.model('集合名', stuSchema);
stuModel.insertMany().then()
stuModel.find()
        .findOne()
stuModel.update()
stuModel.remove()

.sort().limit().skip()

 

posted @ 2021-03-08 11:32  技术活当赏  阅读(133)  评论(0)    收藏  举报