Node.js

目录

​​

1.命令行窗口(小黑屏)、CMD窗口、终端、shell

Web服务器开发    

Node.js 第1天

上午总结

总结

   day02

代码风格问题

Node.js 第2天课堂笔记

知识点

反馈

代码风格

复习

上午总结

服务端渲染和客户端渲染的区别

留言板实践

           

  day03

Node.js 第3天课堂笔记

知识点

反馈

复习

上午总结

下午总结

①优先从缓存加载

②核心模块

③ 路径形式的模块

④第三方模块

package.json 包描述文件

npm 

npm常用命令

解决npm被(qiang)问题

Express 基本使用

修改完代码自动重启

基本路由

在Express中配置使用art-template模板引擎

在Express中获取表单GET请求参数

在Express中获取表单POST请求体数据

 异步编程

回调函数

Promise(回调地狱)

模块标识中的 / 和文件操作路径中的 /

上午总结

演讲

增删改查

 异步编程

回调函数

​        基于原生XMLHttpRequest封装get方法:

Promise

Promise基本语法:

 ​

封装Promise版本的ReadFile 

+ 模块作用域

    package.json和package-lock.json

​    path路径操作模块

    MongoDB

启动:

停止:

    连接和退出数据库   

基本命令

在Node中如何操作MongoDB数据

使用官方的mongodb包来操作

 使用第三方mongoose来操作MongoDB数据库

Mongoose

设计Scheme发布Model 

增加数据

查询数据

删除数据

更新数据


1.命令行窗口(小黑屏)、CMD窗口、终端、shell


    - 开始菜单 --> 运行 --> CMD --> 回车
    - 常用的指令:
        dir 列出当前目录下的所有文件
        cd 目录名 进入到指定的目录
        md 目录名 创建一个文件夹
        rd 目录名 删除一个文件夹    

    - 目录
        . 表示当前目录
        .. 表示上一级目录

        
     环境变量(windows系统中变量) 
        path
            C:\work\jdk\jdk1.7.0_75/bin;
            %CATALINA_HOME%/bin;
            C:\work\soft\tools\AppServ\Apache24\bin;
            C:\work\soft\tools\AppServ\php5;
            C:\Users\lilichao\AppData\Local\Programs\Fiddler;
            C:\work\environment\Egret\Egret Wing 3\bin;
            C:\Users\lilichao\AppData\Roaming\npm;
            C:\Program Files\MongoDB\Server\3.2\bin;
            C:\Users\lilichao\Desktop\hello
            
        - 当我们在命令行窗口打开一个文件,或调用一个程序时,
            系统会首先在当前目录下寻找文件程序,如果找到了则直接打开
            如果没有找到则会依次到环境变量path的路径中寻找,直到找到为止
            如果没找到则报错
            

        - 所以我们可以将一些经常需要访问的程序和文件的路径添加到path中,
            这样我们就可以在任意位置来访问这些文件和程序了
            
I/O (Input/Output)

    - I/O操作指的是对磁盘的读写操作

进程和线程

          进程:

                      ①进程负责为程序的运行提供必备的环境

                      ②进程就相当于工厂中的车间

         线程:

                      ①线程是计算机中的最小的计算单位,线程负责执行进程中的程序

                       ②线程就相当于工厂中的工人

         单线程

         多线程
    
Node
    - Node是对ES标准一个实现,Node也是一个JS引擎
    - 通过Node可以使js代码在服务器端执行
    - Node仅仅对ES标准进行了实现,所以在Node中不包含DOM 和 BOM    
    - Node中可以使用所有的内建对象
        String Number Boolean Math Date RegExp Function Object Array
        而BOM和DOM都不能使用
            但是可以使用 console 也可以使用定时器(setTimeout() setInterval())
            
    - Node可以在后台来编写服务器
        Node编写服务器都是单线程的服务器
        - 进程
            - 进程就是一个一个的工作计划(工厂中的车间)
        - 线程
            - 线程是计算机最小的运算单位(工厂中的工人)
                线程是干活的
                
    - 传统的服务器都是多线程的
        - 每进来一个请求,就创建一个线程去处理请求
        
    - Node的服务器单线程的
        - Node处理请求时是单线程,但是在后台拥有一个I/O线程池
    

 

Web服务器开发    
 

  1.  ip 地址用来定位计算机
  2.  端口号用来定位具体的应用程序
  3. 所有需要联网通信的应用程序都会占用一个端口号,范围为0-65536
  4. 可以同时开启多个服务,但一定要确保不同服务占用 的端口号不一致才可以。

 

Node.js 第1天

上午总结

  • Node.js 是什么

    • JavaScript 运行时

    • 既不是语言,也不是框架,它是一个平台

  • Node.js 中的 JavaScript

    • 没有 BOM、DOM

    • EcmaScript 基本的 JavaScript 语言部分

    • 在 Node 中为 JavaScript 提供了一些服务器级别的 API

      • 文件操作的能力

      • http 服务的能力

总结

  • Node 中的 JavaScript

    • EcmaScript

      • 变量

      • 方法

      • 数据类型

      • 内置对象

      • Array

      • Object

      • Date

      • Math

    • 模块系统

      • 在 Node 中没有全局作用域的概念

      • 在 Node 中,只能通过 require 方法来加载执行多个 JavaScript 脚本文件

      • require 加载只能是执行其中的代码,文件与文件之间由于是模块作用域,所以不会有污染的问题

        • 模块完全是封闭的

        • 外部无法访问内部

        • 内部也无法访问外部

      • 模块作用域固然带来了一些好处,可以加载执行多个文件,可以完全避免变量命名冲突污染的问题

      • 但是某些情况下,模块与模块是需要进行通信的

      • 在每个模块中,都提供了一个对象:exports

      • 该对象默认是一个空对象

      • 你要做的就是把需要被外部访问使用的成员手动的挂载到 exports 接口对象中

      • 然后谁来 require 这个模块,谁就可以得到模块内部的 exports 接口对象

      • 还有其它的一些规则,具体后面讲,以及如何在项目中去使用这种编程方式,会通过后面的案例来处理

    • 核心模块

      • 核心模块是由 Node 提供的一个个的具名的模块,它们都有自己特殊的名称标识,例如

        • fs 文件操作模块

        • http 网络服务构建模块

        • os 操作系统信息模块

        • path 路径处理模块

        • 。。。。

      • 所有核心模块在使用的时候都必须手动的先使用 require 方法来加载,然后才可以使用,例如:

        • var fs = require('fs')

  • http

    • require

    • 端口号

      • ip 地址定位计算机

      • 端口号定位具体的应用程序

    • Content-Type

      • 服务器最好把每次响应的数据是什么内容类型都告诉客户端,而且要正确的告诉

      • 不同的资源对应的 Content-Type 是不一样,具体参照:http://tool.oschina.net/commons

      • 对于文本类型的数据,最好都加上编码,目的是为了防止中文解析乱码问题

    • 通过网络发送文件

      • 发送的并不是文件,本质上来讲发送是文件的内容

      • 当浏览器收到服务器响应内容之后,就会根据你的 Content-Type 进行对应的解析处理

  • 模块系统

  • Node 中的其它的核心模块

  • 做一个小管理系统:

    • CRUD

  • Express Web 开发框架

    • npm install express

   day02

代码风格问题

 当你采用了无分号的代码风格的时候,只需要注意以下情况就不会有上面的问题了:

    当一行代码是以:

      (

       [

       `

   开头的时候,则在前面补上一个分号用以避免一些语法解析错误。

  所以你会发现在一些第三方的代码中能看到一上来就以一个 ; 开头。

 结论:

   无论你的代码是否有分号,都建议如果一行代码是以 (、[、` 开头的,则最好都在其前面补上一个分号。

 有些人也喜欢玩儿一些花哨的东西,例如可以使用 ! ~ 等。

Node.js 第2天课堂笔记

知识点

反馈

    • HTML 也是标记语言

    • markdown 标记语言

    • # 就是标题

    • -* 就是列表

    • **加粗内容**

    • GFM

代码风格

var foo = 'bar'
var foo ='bar'
var foo= 'bar'
var foo = "bar"
​
if (true) {
  console.log('hello') 
}
​
if (true) {
    console.log('hello') 
}
​
if (true ){
      console.log('hello') 
}

为了约定大家的代码风格,所以在社区中诞生了一些比较规范的代码风格规范:dnsajkndkjsabnjkdnjksandjknsajkdnjkasnjkdnjksandjknsajkdnjksajkdnas

复习

上午总结

  • 代码风格

  • 无分号

    • (

    • [

    • `

    • 最好前面补分号,避免一些问题

    • 《编写可维护的 JavaScript》

    • 不仅是功能,还要写的漂亮

  • 服务端渲染

    • 说白了就是在服务端使用模板引擎

    • 模板引擎最早诞生于服务端,后来才发展到了前端

  • 服务端渲染和客户端渲染的区别

    • 客户端渲染不利于 SEO 搜索引擎优化

    • 服务端渲染是可以被爬虫抓取到的,客户端异步渲染是很难被爬虫抓取到的

    • 所以你会发现真正的网站既不是纯异步也不是纯服务端渲染出来的

    • 而是两者结合来做的

    • 例如京东的商品列表就采用的是服务端渲染,目的了为了 SEO 搜索引擎优化

    • 而它的商品评论列表为了用户体验,而且也不需要 SEO 优化,所以采用是客户端渲染

留言板实践

           

 浏览器收到 HTML 响应内容之后,就要开始从上到下依次解析,

    当在解析的过程中,如果发现:

      link

      script

      img

      iframe

      video

      audio

    等带有 src 或者 href(link) 属性标签(具有外链的资源)的时候,浏览器会自动对这些资源发起新的请求。

  day03

Node.js 第3天课堂笔记

知识点

  • 增删改查

  • 登陆

  • 注册

  • 头像

    • 服务端图片

    • 水印

    • 图片水印

  • 找回密码

  • 密码修改

  • 模块系统

    • 核心模块

    • 第三方模块

    • 自己写的模块

    • 加载规则以及加载机制

    • 循环加载

  • npm

  • package.json

  • Express

    • 第三方 Web 开发框架

    • 高度封装了 http 模块

    • 更加专注于业务,而非底层细节

    • 知其所以然

  • 增删改查

    • 使用文件来保存数据(锻炼异步编码)

  • MongoDB

    • (所有方法都封装好了)

反馈

  • 希望老师再推荐一些前端学习的书籍,谢谢!

    • 《JavaScript 高级编程》第3班

    • 学习,解决问题

    • 书本可以更好的系统的整理学过的内容,了解一些细节

    • 《JavaScript 语言精粹》

  • seo的资料,嘿嘿

    • 网站运营 SEO

    • SEO 运营专员

    • 百度、Google、搜狗、

  • 最后老师那个怎么做案例的步骤真的是很有用 觉得今天的反馈 大概又是夸老师的比较多 老师声音很有特点

  • 老师讲的很仔细,虽然语速有点快但是会重复很多遍,即使第一遍没听会第二遍第三遍也懂了.很好.

  • 使用markdown一次只能打开一个文件,不知道怎么建文件夹,是需要安插件吗?

  • 老师,软件版本的升级是以什么作为理论支持的,为什么跳跃间隙可以这么大?还有,看上了老师的电子图书馆,瞬间好爱学习呀,真的!

    • 软件开发版本里面涉及到软件工程学:

    • x.x.x

      • 0.0.1

      • 0.0.2

      • 1.1.5

      • 1.9.2

      • 2(新增功能比较多,甚至可能去除了某些功能).5(加入了新功能).0(修复bug,提升性能)

      • 大版本

      • 一般是这些客户端软件、技术框架开发者比较理解的多

      • 做网站很少涉及到版本的概念,网站的目的就是快

  • art-template里面用的语法是jQuery吗, each什么的 我晕了 each,forEach, 遍历的全混了

    • art-template 和 jQuery 一毛钱关系都没有

    • each 是 art-template 的模板语法,专属的

    • {{each 数组}}

    • <li>{{ $value }}</li>

    • {{/each}} 这是 art-template 模板引擎支持的语法,只能在模板字符串中使用

    • $.each(数组, function)

    • $('div').each(function) 一般用于遍历 jQuery 选择器选择到的伪数组实例对象

    • forEach 是 EcmaScript 5 中的一个数组遍历函数,是 JavaScript 原生支持的遍历方法 可以遍历任何可以被遍历的成员

    • jQuery 的 each 方法和 forEach 几乎一致

    • 由于 forEach 是 EcmaScript 5 中的,所以低版本浏览器不支持

  • 每一次的复习贼重要 老师很不错 我喜欢

  • 在以后的工作中 用到node.js的地方多吗? 在留言本的案例中 点击发表留言跳转页面的路径是url路径 和之前写的页面跳转写的文件路径还是有点分不清。

    • 技多不压身

    • Node 对于前端来讲是进阶高级前端开发工程师必备的技能

    • 屌丝最容易逆袭的职业

    • 见得东西多了你就不怕了

    • 为所欲为

  • 老师讲的挺清晰的 可是第一节太困了 路径有点没转变过来

  • 如果从a中调用b中的数据,又从b中调用a中的数据,执行a代码,为什么把b中的执行完后才会执行a,而不是在b调用a的时候a中的代码继续执行

    • a 加载了 b

      • 执行 b 中的代码

      • 同时得到 b 中导出的接口对象:exports

      • 执行 b 的过程中发现 b 也在 require a

      • b 就会反过来执行 a

      • a 中又加载 b

      • b 又反过来加载 a

      • 这就是循环加载

      • 如果你一旦出现了这种情况,说明你的思路有问题。

      • jQuery.js (可能不可能出现 jQuery 依赖了 main)

      • main.js 依赖了 jQuery

      • 这个问题是矛盾。

    • b 中也加载了 a

    •  

    • 网页中所有的路径其实都是 url 路径,不是文件路径

  • 问题就是不知道问题是什么,写案例的时候似懂非懂

  • 感觉思维有点跟不上,

复习

  • 网站开发模型

    • 黑盒子、哑巴

    • 写代码让它变得更智能

    • 按照你设计好的套路供用户使用

  • 在 Node 中使用 art-template 模板引擎

    • 安装

    • 加载

    • template.render()

  • 客户端渲染和服务端渲染的区别

    • 最少两次请求,发起 ajax 在客户端使用模板引擎渲染

    • 客户端拿到的就是服务端已经渲染好的

  • 处理留言本案例首页数据列表渲染展示

  • 处理留言本案例发表留言功能

    • 路径

    • 设计好的请求路径

    • $GET 直接或查询字符串数据

    • Node 中需要咱们自己动手来解析

      • url.parse()

    • /pinglun?name=jack&message=hello

    • split('?')

    • name=jack&message=hello

    • split('&')

    • name=jack message=hello

    • forEach()

    • name=jack.split('=')

    • 0 key

    • 1 value

  • 掌握如何解析请求路径中的查询字符串

    • url.parse()

  • 如何在 Node 中实现服务器重定向

    • header('location')

      • 301 永久重定向 浏览器会记住

        • a.com b.com

        • a 浏览器不会请求 a 了

        • 直接去跳到 b 了

      • 302 临时重定向 浏览器不记忆

        • a.com b.com

        • a.com 还会请求 a

        • a 告诉浏览器你往 b

  • Node 中的 Console(REPL)使用

上午总结

  • jQuery 的 each 和 原生的 JavaScript 方法 forEach

    • EcmaScript 5 提供的

      • 不兼容 IE 8

    • jQuery 的 each 由 jQuery 这个第三方库提供

      • jQuery 2 以下的版本是兼容 IE 8 的

      • 它的 each 方法主要用来遍历 jQuery 实例对象(伪数组)

      • 同时它也可以作为低版本浏览器中 forEach 替代品

      • jQuery 的实例对象不能使用 forEach 方法,如果想要使用必须转为数组才可以使用

      • [].slice.call(jQuery实例对象)

  • 模块中导出多个成员和导出单个成员

  • 导出多个

  • exports.a=123
    exports.b='hello'
    exports.c=function (){
        console.log('ccc')
    }
    

    也可以这么写

  • module.exports={
        foo:'bar',
        add:function(){
            console.log('jhhh')
        }
    }

     

    导出单个

  • module.exports='hello'

     

  • 301 和 302 状态码区别

    • 301 永久重定向,浏览器会记住

    • 302 临时重定向

  • exports 和 module.exports 的区别

    • 每个模块中都有一个 module 对象

    • module 对象中有一个 exports 对象

    • 我们可以把需要导出的成员都挂载到 module.exports 接口对象中

    • 也就是:moudle.exports.xxx = xxx 的方式

    • 但是每次都 moudle.exports.xxx = xxx 很麻烦,点儿的太多了

    • 所以 Node 为了你方便,同时在每一个模块中都提供了一个成员叫:exports

    • exports === module.exports 结果为 trues

    • 所以对于:moudle.exports.xxx = xxx 的方式 完全可以:expots.xxx = xxx

    • 当一个模块需要导出单个成员的时候,这个时候必须使用:module.exports = xxx 的方式

    • 不要使用 exports = xxx 不管用

    • 因为每个模块最终向外 return 的是 module.exports

    • exports 只是 module.exports 的一个引用

    • 所以即便你为 exports = xx 重新赋值,也不会影响 module.exports

    • 但是有一种赋值方式比较特殊:exports = module.exports 这个用来重新建立引用关系的

    • 之所以让大家明白这个道理,是希望可以更灵活的去用它

  • Node 是一个比肩 Java、PHP 的一个平台

    • JavaScript 既能写前端也能写服务端

moudle.exports = {
  a: 123
}
​
// 重新建立 exports 和 module.exports 之间的引用关系
exports = module.exports
​
exports.foo = 'bar'
Array.prototype.mySlice = function () {
  var start = 0
  var end = this.length
  if (arguments.length === 1) {
    start = arguments[0]
  } else if (arguments.length === 2) {
    start = arguments[0]
    end = arguments[1]
  }
  var tmp = []
  for (var i = start; i < end; i++) {
    // fakeArr[0]
    // fakeArr[1]
    // fakeArr[2]
    tmp.push(this[i])
  }
  return tmp
}
​
var fakeArr = {
  0: 'abc',
  1: 'efg',
  2: 'haha',
  length: 3
}
​
// 所以你就得到了真正的数组。 
[].mySlice.call(fakeArr)

下午总结

  • jQuery 的 each 和 原生的 JavaScript 方法 forEach

  • 301 和 302 的区别

  • 模块中导出单个成员和导出多个成员的方式

  • module.exports 和 exports 的区别

  • require 方法加载规则

    • ①优先从缓存加载

    • 结果:

  • ②核心模块

  • // 核心模块的本质也是文件

    // 核心模块文件已经被编译到了二进制文件中了,我们只需要按照名字来加载就可以了

    // require('fs')

    // require('http')

  • ③ 路径形式的模块

  • //  ./ 当前目录,不可省略

    //  ../ 上一级目录,不可省略

    //  /xxx 几乎不用

    //  d:/a/foo.js 几乎不用

    //  首位的 / 在这里表示的是当前文件模块所属磁盘根路径

    //  .js 后缀名可以省略

    // require('./foo.js')

  • ④第三方模块

    • node_modules

    • // 凡是第三方模块都必须通过 npm 来下载

      // 使用的时候就可以通过 require('包名') 的方式来进行加载才可以使用

      // 不可能有任何一个第三方包和核心模块的名字是一样的

      // 既不是核心模块、也不是路径形式的模块

      //    先找到当前文件所处目录中的 node_modules 目录

      //    node_modules/art-template

      //    node_modules/art-template/package.json 文件

      //    node_modules/art-template/package.json 文件中的 main 属性

      //    main 属性中就记录了 art-template 的入口模块

      //    然后加载使用这个第三方包

      //    实际上最终加载的还是文件

    • //    如果 package.json 文件不存在或者 main 指定的入口模块是也没有

      //    则 node 会自动找该目录下的 index.js

      //    也就是说 index.js 会作为一个默认备选项

    • //    如果以上所有任何一个条件都不成立,则会进入上一级目录中的 node_modules 目录查找

      //    如果上一级还没有,则继续往上上一级查找

      //    。。。

      //    如果直到当前磁盘根目录还找不到,最后报错:

      //      can not find module xxx

    • // 注意:我们一个项目有且只有一个 node_modules,放在项目根目录中,这样的话项目中所有的子目录中的代码都可以加载到第三方包

      // 不会出现有多个 node_modules

    •  

      // 模块查找机制

      //    优先从缓存加载

      //    核心模块

      //    路径形式的文件模块

      //    第三方模块

      //      node_modules/art-template/

      //      node_modules/art-template/package.json

      //      node_modules/art-template/package.json main

      //      index.js 备选项

      //      进入上一级目录找 node_modules,兄弟路径的不可以

      //      按照这个规则依次往上找,直到磁盘根目录还找不到,最后报错:Can not find moudle xxx

      //    一个项目有且仅有一个 node_modules 而且是存放到项目的根目录

       

  • package.json 包描述文件

    • dependencies 选项的作用,它可以通过 --save来获得

    • 这个文件可以通过npm init来获得

    • 目前对我们来说,最有用的是那个dependencies选项,可以用来帮我们保存第三方包的依赖信息。

    • 建议每个项目的根目录下都有一个package.json

    • 建议执行 npm install 包名  的时候都加上--save这个选项,目的是用来保存依赖项信息

    • 如果你的node_modules删除了也不用担心,我们只需要:npm install  就会自动把package.json中的dependences中的所有依赖项都下载回来。

  • npm 

  • npm命令行工具
  • npm install --global npm  (自己升级自己)
  • npm常用命令

  • npm init
  •        npm init -y 可以跳过向导,快速生成
  • npm install    
  •        npm  i  简写
  •       一次性吧dependencies选项中的依赖项全部安装
  • npm install 包名
  •        只下载
  •        npm  i  包名
  • npm install --save  包名
  •         下载并保存依赖项(package.json文件中的dependencies选项
  •          npm i -S 包名
  • npm uninstall 包名
  •         只删除,如果有依赖项会依然保存
  •         npm un 包名
  • npm uninstall --save 包名
  •         删除的同时也会把依赖信息也去除
  •         npm un -S 包名
  • npm help
  •        查看使用帮助
  • npm 命令 --help
  •         查看指定命令的使用帮助
  • 解决npm被(qiang)问题

  • npm 存储包文件的服务器在国外,有时候会被qiang,速度很慢,所以我们需要解决这个问题
  • https://developer.aliyun.com/mirror/NPM?from=tnpm淘宝的开发团队在国内做了一个备份
  • 查看npm配置信息
  •     npm config list
  • Express 基本使用

  • npm install --save express
  • 修改完代码自动重启

  • 我们可以使用第三方命名航工具:nodemon 来帮我们解决频繁修改代码重启服务器问题
  • nodemon 是一个基于Node.js开发的一个第三方命令行工具,我们使用的时候需要独立安装
  • npm install --global nodemon   (--global来安装的包都可以在任意目录进行)
  • 使用: nodemon app.js
  • 它会监视你的文件变化,当文件发生变化的时候,自动帮你重启服务器
  • 基本路由

  • 路由器:
  •     请求方法
  •     请求路径
  •     请求处理函数
  • get
  • app.get('/',function (req,res) {
        res.send('Hello world')
    })

    post

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

  • 在Express中配置使用art-template模板引擎

  • art-template-GitHub仓库
  • 安装:
  •    
    npm install --save art-template
    npm install --save express-art-template
  • 配置使用 art-template 模板引擎

  • // 第一个参数,表示,当渲染以 .art 结尾的文件的时候,使用 art-template 模板引擎

    // express-art-template 是专门用来在 Express 中把 art-template 整合到 Express 中

    // 虽然外面这里不需要加载 art-template 但是也必须安装

    // 原因就在于 express-art-template 依赖了 art-template

    app.engine('html', require('express-art-template'))



     

    // Express 为 Response 相应对象提供了一个方法:render

    // render 方法默认是不可以使用,但是如果配置了模板引擎就可以使用了

    // res.render('html模板名', {模板数据})

    // 第一个参数不能写路径,默认会去项目中的 views 目录查找该模板文件

    // 也就是说 Express 有一个约定:开发人员把所有的视图文件都放到 views 目录中


     

    app.get('/',function (req,res){

       res.render('404.html')

    })

  • // 如果想要修改默认的 views 目录,则可以

    // app.set('views', render函数的默认路径)

在Express中获取表单GET请求参数

Express内置了一个API,可以直接获得req.query来获取 

在Express中获取表单POST请求体数据

  • 在Express中没有内置获取表单POST请求体的API,这里我们需要使用一个第三方包:body-parser
  • 安装:   npm install --save body-parser
  • 配置:
  • // 配置 body-parser 中间件(插件,专门用来解析表单 POST 请求体)

    // parse application/x-www-form-urlencoded

    app.use(bodyParser.urlencoded({ extended: false }))

    // parse application/json

    app.use(bodyParser.json())

 异步编程

回调函数

目的:获取异步操作的结果

eg:

调用:

save:

调用: 

Promise(回调地狱)

无法保证顺序的代码:

 

 

模块标识中的 / 和文件操作路径中的 /

使用的所有文件操作的 API 都是异步的

文件操作中的相对路径可以省略 ./

在模块加载中,相对路径中的 ./ 不能省略 

 在文件操作的相对路径中

   ./data/a.txt 相对于当前目录

  data/a.txt   相对于当前目录

   /data/a.txt  绝对路径,当前文件模块所处磁盘根目录

  c:/xx/xx...  绝对路径

 

// 这里如果忽略了 . 则也是磁盘根目录

//require('/data/foo.js')

 

上午总结

演讲

说服 PPT 脑图 markdown 结构思维

  • 找痛点 why 为什么

  • 解决方案 what 是什么

  • 怎么去使用 how 怎么用

  • where 在哪儿用

  • when 什么时候用

  • 文件路径中的 / 和模块标识中的 /

  • nodemon

  • Express

    • art-template 模板引擎的配置

    • body-parser 解析表单 POST 请求体

  • 技术只是一种解决问题的手段、工具而已

    • 第三方的东西,不要纠结

    • 先以解决问题为主

  • 详解了 express 静态服务 API

    • app.use('/public/', express.static('./public'))

  • crud

  • 文件路径中的 / 和模块标识中的 /

  • Express 中配置使用 art-template 模板引擎

  • Express 中配置使用 body-parser

  • Express 中配置处理静态资源

  • CRUD 案例中单独提取路由模块

  • 增删改查

app_1.js

/**
 * app.js 入门模块
 * 职责:
 *   创建服务
 *   做一些服务相关配置
 *     模板引擎
 *     body-parser 解析表单 post 请求体
 *     提供静态资源服务
 *   挂载路由
 *   监听端口启动服务
 */




var express=require('express')
var router=require('./router_1')
var app=express()
var bodyParser=require('body-parser')

app.use('/node_modules/',express.static('./node_modules/'))
app.use('/public/',express.static('./public/'))


app.engine('html',require('express-art-template'))

//配置模板引擎和body-parser一定要在app.use(router)挂载路由之前
// parse application/x-www-form-urlencoded
app.use(bodyParser.urlencoded({ extended: false }))
// parse application/json
app.use(bodyParser.json())

//把路由容器挂载到app服务中
app.use(router)


app.listen(3000,function(){
    console.log('running 3000.....')
})

router_1.js

/**
 * router.js 路由模块
 * 职责:
 *   处理路由
 *   根据不同的请求方法+请求路径设置具体的请求处理函数
 * 模块职责要单一,不要乱写
 * 我们划分模块的目的就是为了增强项目代码的可维护性
 * 提升开发效率
 */



var fs = require('fs')
var Student = require('./student_1')

//修改成功,说明updateById方法正确
// Student.updateById({
//     id:1,
//     name:'zhangxiaosan'

// },function(err){
//     if(err){
//         console.log('修改失败')
//     }
//     console.log('修改成功')
// })

// Express 提供了一种更好的方式
// 专门用来包装路由的
var express = require('express')

// 1. 创建一个路由容器
var router = express.Router()

// 2. 把路由都挂载到 router 路由容器中

/*
 * 渲染学生列表页面
 */
router.get('/students', function (req, res) {
  Student.find(function (err, students) {
    if (err) {
      return res.status(500).send('Server error.')
    }
    res.render('index_1.html', {
      fruits: [
        '苹果',
        '香蕉',
        '橘子'
      ],
      students: students
    })
  })
})

/*
 * 渲染添加学生页面
 */
router.get('/students/new', function (req, res) {
  res.render('new_1.html')
})

/*
 * 处理添加学生
 */
router.post('/students/new', function (req, res) {
  // 1. 获取表单数据
  // 2. 处理
  //    将数据保存到 db.json 文件中用以持久化
  // 3. 发送响应
  Student.save(req.body, function (err) {
    if (err) {
      return res.status(500).send('Server error.')
    }
    res.redirect('/students')
  })
})

/*
 * 渲染编辑学生页面
 */
router.get('/students/edit', function (req, res) {
  // 1. 在客户端的列表页中处理链接问题(需要有 id 参数)
  // 2. 获取要编辑的学生 id
  // 
  // 3. 渲染编辑页面
  //    根据 id 把学生信息查出来
  //    使用模板引擎渲染页面
  //console.log(req.query.id)
  Student.findById(parseInt(req.query.id), function (err, student) {
    if (err) {
      return res.status(500).send('Server error.')
    }
   // console.log(student)
    res.render('edit_1.html', {
      student: student
    })
  })
})

/*
 * 处理编辑学生
 */
router.post('/students/edit', function (req, res) {
  // 1. 获取表单数据
  //    req.body
  // 2. 更新
  //    Student.updateById()
  // 3. 发送响应
  //console.log(req.body)
  Student.updateById(req.body, function (err) {
    if (err) {
      return res.status(500).send('Server error.')
    }
    res.redirect('/students')
  })
})

/*
 * 处理删除学生
 */
router.get('/students/delete', function (req, res) {
  // 1. 获取要删除的 id
  // 2. 根据 id 执行删除操作
  // 3. 根据操作结果发送响应数据

  Student.deleteById(req.query.id, function (err) {
    if (err) {
      return res.status(500).send('Server error.')
    }
    res.redirect('/students')
  })
})

// 3. 把 router 导出
module.exports = router

// 这样也不方便
// module.exports = function (app) {
//   app.get('/students', function (req, res) {
//     // readFile 的第二个参数是可选的,传入 utf8 就是告诉它把读取到的文件直接按照 utf8 编码转成我们能认识的字符
//     // 除了这样来转换之外,也可以通过 data.toString() 的方式
//     fs.readFile('./db.json', 'utf8', function (err, data) {
//       if (err) {
//         return res.status(500).send('Server error.')
//       }

//       // 从文件中读取到的数据一定是字符串
//       // 所以这里一定要手动转成对象
//       var students = JSON.parse(data).students

//       res.render('index.html', {
//         fruits: [
//           '苹果',
//           '香蕉',
//           '橘子'
//         ],
//         students: students
//       })
//     })
//   })

//   app.get('/students/new', function (req, res) {

//   })

//   app.get('/students/new', function (req, res) {

//   })

//   app.get('/students/new', function (req, res) {

//   })

//   app.get('/students/new', function (req, res) {

//   })

//   app.get('/students/new', function (req, res) {

//   })
// }

 students_1.js

/**
 * student.js
 * 数据操作文件模块
 * 职责:操作文件中的数据,只处理数据,不关心业务
 *
 * 这里才是我们学习 Node 的精华部分:奥义之所在
 * 封装异步 API
 */



var fs = require('fs')

var dbPath = './db.json'

/**
 * 获取学生列表
 * @param  {Function} callback 回调函数
 */
exports.find = function (callback) {
  fs.readFile(dbPath, 'utf8', function (err, data) {
    if (err) {
      return callback(err)
    }
    callback(null, JSON.parse(data).students)
  })
}

/**
 * 根据 id 获取学生信息对象
 * @param  {Number}   id       学生 id
 * @param  {Function} callback 回调函数
 */
exports.findById = function (id, callback) {
  fs.readFile(dbPath, 'utf8', function (err, data) {
    if (err) {
      return callback(err)
    }
    var students = JSON.parse(data).students
    var ret = students.find(function (item) {
      return item.id === parseInt(id)
    })
    callback(null, ret)
  })
}

/**
 * 添加保存学生
 * @param  {Object}   student  学生对象
 * @param  {Function} callback 回调函数
 */
exports.save = function (student, callback) {
  fs.readFile(dbPath, 'utf8', function (err, data) {
    if (err) {
      return callback(err)
    }
    var students = JSON.parse(data).students

    // 添加 id ,唯一不重复
    student.id = students[students.length - 1].id + 1

    // 把用户传递的对象保存到数组中
    students.push(student)

    // 把对象数据转换为字符串
    var fileData = JSON.stringify({
      students: students
    })

    // 把字符串保存到文件中
    fs.writeFile(dbPath, fileData, function (err) {
      if (err) {
        // 错误就是把错误对象传递给它
        return callback(err)
      }
      // 成功就没错,所以错误对象是 null
      callback(null)
    })
  })
}

/**
 * 更新学生
 */
exports.updateById = function (student, callback) {
  fs.readFile(dbPath, 'utf8', function (err, data) {
    if (err) {
      return callback(err)
    }
    var students = JSON.parse(data).students

    // 注意:这里记得把 id 统一转换为数字类型
    student.id = parseInt(student.id)

    // 你要修改谁,就需要把谁找出来
    // EcmaScript 6 中的一个数组方法:find
    // 需要接收一个函数作为参数
    // 当某个遍历项符合 item.id === student.id 条件的时候,find 会终止遍历,同时返回遍历项
    var stu = students.find(function (item) {
      return item.id === student.id
    })

    // 这种方式你就写死了,有 100 个难道就写 100 次吗?
    // stu.name = student.name
    // stu.age = student.age

    // 遍历拷贝对象
    for (var key in student) {
      stu[key] = student[key]
    }

    // 把对象数据转换为字符串
    var fileData = JSON.stringify({
      students: students
    })

    // 把字符串保存到文件中
    fs.writeFile(dbPath, fileData, function (err) {
      if (err) {
        // 错误就是把错误对象传递给它
        return callback(err)
      }
      // 成功就没错,所以错误对象是 null
      callback(null)
    })
  })
}

/**
 * 删除学生
 */
exports.deleteById = function (id, callback) {
  fs.readFile(dbPath, 'utf8', function (err, data) {
    if (err) {
      return callback(err)
    }
    var students = JSON.parse(data).students

    // findIndex 方法专门用来根据条件查找元素的下标
    var deleteId = students.findIndex(function (item) {
      return item.id === parseInt(id)
    })
    //console.log(deleteId)
    // 根据下标从数组中删除对应的学生对象
    students.splice(deleteId, 1)

    // 把对象数据转换为字符串
    var fileData = JSON.stringify({
      students: students
    })

    // 把字符串保存到文件中
    fs.writeFile(dbPath, fileData, function (err) {
      if (err) {
        // 错误就是把错误对象传递给它
        return callback(err)
      }
      // 成功就没错,所以错误对象是 null
      callback(null)
    })
  })
}

 异步编程

回调函数

不成立的情况:

 回调函数


    
    基于原生XMLHttpRequest封装get方法:

// setTimeout

    // readFile

    // wirteFile

    // readdir

    // ajax

    // 往往异步 API 都伴随有一个回调函数

 function get(url, callback) {
      var oReq = new XMLHttpRequest()
      // 当请求加载成功之后要调用指定的函数
      oReq.onload = function () {
        // 我现在需要得到这里的 oReq.responseText
        callback(oReq.responseText)
      }
      oReq.open("get", url, true)
      oReq.send()
    }

    get('data.json', function (data) {
      console.log(data)
    })

Promise

Promise本身不是异步,但是内部往往都是封装一个异步任务

Promise基本语法:

 

 调用

p1
  .then(function (data) {
    console.log(data)
    return p2
  }, function (err) {
    console.log('读取文件失败了', err)
  })

 图解:

Promise api

var fs = require('fs')

var p1 = new Promise(function (resolve, reject) {
  fs.readFile('./data/a.txt', 'utf8', function (err, data) {
    if (err) {
      reject(err)
    } else {
      resolve(data)
    }
  })
})

var p2 = new Promise(function (resolve, reject) {
  fs.readFile('./data/b.txt', 'utf8', function (err, data) {
    if (err) {
      reject(err)
    } else {
      resolve(data)
    }
  })
})

var p3 = new Promise(function (resolve, reject) {
  fs.readFile('./data/c.txt', 'utf8', function (err, data) {
    if (err) {
      reject(err)
    } else {
      resolve(data)
    }
  })
})

p1
  .then(function (data) {
    console.log(data)
    // 当 p1 读取成功的时候
    // 当前函数中 return 的结果就可以在后面的 then 中 function 接收到
    // 当你 return 123 后面就接收到 123
    //      return 'hello' 后面就接收到 'hello'
    //      没有 return 后面收到的就是 undefined
    // 上面那些 return 的数据没什么卵用
    // 真正有用的是:我们可以 return 一个 Promise 对象
    // 当 return 一个 Promise 对象的时候,后续的 then 中的 方法的第一个参数会作为 p2 的 resolve
    // 
    return p2
  }, function (err) {
    console.log('读取文件失败了', err)
  })
  .then(function (data) {
    console.log(data)
    return p3
  })
  .then(function (data) {
    console.log(data)
    console.log('end')
  })

 

p1,p2,p3都是new 出来的Promise对象

封装Promise版本的ReadFile 

var fs = require('fs')

function pReadFile(filePath) {
  return new Promise(function (resolve, reject) {
    fs.readFile(filePath, 'utf8', function (err, data) {
      if (err) {
        reject(err)
      } else {
        resolve(data)
      }
    })
  })
}

pReadFile('./data/a.txt')
  .then(function (data) {
    console.log(data)
    return pReadFile('./data/b.txt')
  })
  .then(function (data) {
    console.log(data)
    return pReadFile('./data/c.txt')
  })
  .then(function (data) {
    console.log(data)
  })

+ 模块作用域

  + 可以使用 API 来进行文件与文件之间的依赖加载

  + 在 Node 这个环境中对 JavaScript 进行了特殊的模块化支持 CommonJS

  + JavaScript 天生不支持模块化

    * require

    * exports

    * Node.js 才有的

  + 在浏览器中也可以像在 Node 中的模块一样来进行编程

    * `<script>` 标签来引用加载,而且你还必须考虑加载的顺序问题

    * require.js 第三方库 AMD

    * sea.js     第三方库 CMD

  + 无论是 CommonJS、AMD、CMD、UMD、EcmaScript 6 Modules 官方规范

    * 都是为了解决 JavaScript 的模块化问题

    * CommonJS、AMD、CMD 都是民间搞出来的

    * EcmaScript 是官方规范定义

    * 官方看民间都在乱搞,开发人员为了在不同的环境使用不同的 JavaScript 模块化解决方案

    * 所以 EcmaScript 在 2015 年发布了 EcmaScript 2016 官方标准

    * 其中就包含了官方对 JavaScript 模块化的支持

    * 也就是说语言天生就支持了

    * 但是虽然标准已经发布了,但是很多 JavaScript 运行换将还不支持

    * Node 也是只在 8.5 版本之后才对 EcmaScript 6 module 进行了支持

    * 后面学 Vue 的时候会去学习

    * less 编译器 > css

    * EcmaScript 6 -> 编译器 -> EcmaScript 5

    * 目前的前端情况都是使用很多新技术,然后利用编译器工具打包可以在低版本浏览器运行。

    * 使用新技术的目的就是为了提高效率,增加可维护性

    package.json和package-lock.json


    path路径操作模块

参考文档https://nodejs.org/dist/latest-v13.x/docs/api/path.html

eg:

Node中的其它成员

在每个模块中,除了require、exports等模块相关API之外,还有两个特殊的成员:

__dirname可以用来获取当前文件模块所属目录的绝对路径(动态获取)

__filename可以用来获取当前文件的绝对路径(动态获取 )

__dirname和__filename是不受执行node命令所属路径影响的

文件操作路径中,相对路径设计的就是相对于执行 node 命令所处的路径

// fs.readFile('C:/Users/lpz/Desktop/nodejs/06/code/foo/a.txt', 'utf8', function (err, data) {
//   if (err) {
//     throw err
//   }
//   console.log(data)
// })

// console.log(__dirname + '/a.txt')

// C:\Users\lpz\Desktop\nodejs\06\code
fs.readFile(path.join(__dirname, './a.txt'), 'utf8', function (err, data) {
  if (err) {
    throw err
  }
  console.log(data)
})

 模块中的路径标识和文件操作中的相对路径标识不一致

模块中的路径标识就是相对于当前文件模块,不受执行 node 命令所处路径影响

require('./b')

MongoDB


   参考菜鸟教程https://www.runoob.com/mongodb/mongodb-tutorial.html

启动:

     mongodb默认使用执行mongod命令所处盘符根目录下的/data/db 作为自己的数据存储目录

     所以在第一次执行该命令之前先自己手动新建一个 /data/db

    如果想要修改默认的数据存储目录,可以:   mongod --dbpath=数据存储目录路径

停止:

     在开启服务的控制台,直接按ctrl+c即可停止

     或者直接关闭开启服务的控制台也可以


    
连接和退出数据库   

mongo :该命令默认连接本机的MongoDB 服务

exit:在连接状态输入exit退出连接

 

基本命令

show dbs : 查看显示所有数据库

db : 查看当前操作的数据库

 use 数据库名称: 切换到指定的数据 (如果没有会新建)

插入数据: 


            
        

在Node中如何操作MongoDB数据

使用官方的mongodb包来操作

https://github.com/mongodb/node-mongodb-native

 使用第三方mongoose来操作MongoDB数据库

网址:https://mongoosejs.com/

MongoDB数据库的基本概念

eg:

Mongoose

设计Scheme发布Model 

var mongoose = require('mongoose')

var Schema = mongoose.Schema

// 1. 连接数据库
// 指定连接的数据库不需要存在,当你插入第一条数据之后就会自动被创建出来
mongoose.connect('mongodb://localhost/itcast')

// 2. 设计文档结构(表结构)
// 字段名称就是表结构中的属性名称
// 约束的目的是为了保证数据的完整性,不要有脏数据
var userSchema = new Schema({
  username: {
    type: String,
    required: true // 必须有
  },
  password: {
    type: String,
    required: true
  },
  email: {
    type: String
  }
})

// 3. 将文档结构发布为模型
//    mongoose.model 方法就是用来将一个架构发布为 model
//    第一个参数:传入一个大写名词单数字符串用来表示你的数据库名称
//                 mongoose 会自动将大写名词的字符串生成 小写复数 的集合名称
//                 例如这里的 User 最终会变为 users 集合名称
//    第二个参数:架构 Schema
//   
//    返回值:模型构造函数
var User = mongoose.model('User', userSchema)

增加数据

var admin = new User({
  username: 'zs',
  password: '123456',
  email: 'admin@admin.com'
})

admin.save(function (err, ret) {
  if (err) {
    console.log('保存失败')
  } else {
    console.log('保存成功')
    console.log(ret)
  }
})

查询数据

查询所有:

User.find(function (err, ret) {
  if (err) {
    console.log('查询失败')
  } else {
    console.log(ret)
  }
})

按条件查询所有

User.find({
  username: 'zs'
}, function (err, ret) {
  if (err) {
    console.log('查询失败')
  } else {
    console.log(ret)
  }
})

按条件查询单个

User.findOne({
  username: 'zs'
}, function (err, ret) {
  if (err) {
    console.log('查询失败')
  } else {
    console.log(ret)
  }
})

删除数据

根据条件删除所有:

User.remove({
  username: 'zs'
}, function (err, ret) {
  if (err) {
    console.log('删除失败')
  } else {
    console.log('删除成功')
    console.log(ret)
  }
})

根据条件删除一个:

Model.findOneAndRemove(conditions,[options],[callback])

根据id删除一个:

Model.findByIdAndRemove(id,[options],[callback]) 

更新数据

根据id更新一个:

User.findByIdAndUpdate('5a001b23d219eb00c8581184', {
  password: '123'
}, function (err, ret) {
  if (err) {
    console.log('更新失败')
  } else {
    console.log('更新成功')
  }
})

根据条件更新所有:

Model.update(conditions,doc,[options],[callback])

根据指定条件更新一个:

Model.findOneAndUpdate([conditions],[update],[options],[callback])

 

中间件

 中间件:处理请求的,本质就是个函数

 

 在 Express 中,对中间件有几种分类

 

 当请求进来,会从第一个中间件开始进行匹配

    如果匹配,则进来

       如果请求进入中间件之后,没有调用 next 则代码会停在当前中间件

       如果调用了 next 则继续向后找到第一个匹配的中间件

    如果不匹配,则继续判断匹配下一个中间件

    

 不关心请求路径和请求方法的中间件

也就是说任何请求都会进入这个中间件

中间件本身是一个方法,该方法接收三个参数:

   Request 请求对象

  Response 响应对象

  next     下一个中间件

 当一个请求进入一个中间件之后,如果不调用 next 则会停留在当前中间件

所以 next 是一个方法,用来调用下一个中间件的

调用 next 方法也是要匹配的(不是调用紧挨着的那个)

app.use(function (req, res, next) {
  console.log('1')
  next()
})

app.use(function (req, res, next) {
  console.log('2')
  next()
})

app.use(function (req, res, next) {
  console.log('3')
  res.send('333 end.')
})

app.use(function (req, res, next) {
  console.log(1)
  next()
})


app.use('/b', function (req, res, next) {
  console.log('b')
})

// 以 /xxx 开头的路径中间件
app.use('/a', function (req, res, next) {
  console.log('a')
  next()
})

app.use(function (req, res, next) {
  console.log('2')
  next()
})

app.use('/a', function (req, res, next) {
  console.log('a 2')
})

除了以上中间件之外,还有一种最常用的
严格匹配请求方法和请求路径的中间件
app.get
app.post

app.use(function (req, res, next) {
  console.log(1)
  next()
})

app.get('/abc', function (req, res, next) {
  console.log('abc')
  next()
})

app.get('/', function (req, res, next) {
  console.log('/')
  next()
})

app.use(function (req, res, next) {
  console.log('haha')
  next()
})

app.get('/abc', function (req, res, next) {
  console.log('abc 2')
})

app.use(function (req, res, next) {
  console.log(2)
  next()
})

app.get('/a', function (req, res, next) {
  console.log('/a')
})

app.get('/', function (req, res, next) {
  console.log('/ 2')
})

// 如果没有能匹配的中间件,则 Express 会默认输出:Cannot GET 路径

app.listen(3000, function () {
  console.log('app is running at port 3000.')
})

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值