-
知识点
- 网站开发模型
+ 黑盒子、哑巴
+ 写代码让它变得更智能
+ 按照你设计好的套路供用户使用 - 客户端渲染和服务端渲染的区别
+ 最少两次请求,发起 ajax 在客户端使用模板引擎渲染
+ 客户端拿到的就是服务端已经渲染好的 - 掌握如何解析请求路径中的查询字符串
+ url.parse() - 如何在 Node 中实现服务器重定向
+ header('location')
* 301 永久重定向 浏览器会记住
- a.com b.com
- a 浏览器不会请求 a 了
- 直接去跳到 b 了
* 302 临时重定向 浏览器不记忆
- a.com b.com
- a.com 还会请求 a
- a 告诉浏览器你往 b - jQuery 的 each 和 原生的 JavaScript 方法 forEach
+ EcmaScript 5 提供的
* 不兼容 IE 8
+ jQuery 的 each 由 jQuery 这个第三方库提供
* jQuery 2 以下的版本是兼容 IE 8 的
* 它的 each 方法主要用来遍历 jQuery 实例对象(伪数组)
* 同时它也可以作为低版本浏览器中 forEach 替代品
* jQuery 的实例对象不能使用 forEach 方法,如果想要使用必须转为数组才可以使用
* `[].slice.call(jQuery实例对象)` - 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` 结果为 `true`s
+ 所以对于:`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 既能写前端也能写服务端 - require 方法加载规则
+ 优先从缓存加载
+ 核心模块
+ 路径形式的模块
* `./xxx`
* `../xxxx`
* `/xxxx` / 在这里表示的是磁盘根路径
* `c:/xxx`
+ 第三方模块
* 第三方模块的标识就是第三方模块的名称(不可能有第三方模块和核心模块的名字一致)
* npm
- 开发人员可以把写好的框架、库发布到 npm 上
- 使用者在使用的时候就可以很方便的通过 npm 来下载
* 使用方式:`var 名字 = require('npm install 的那个包名')`
* node_modules
* node_modules/express
* node_modules/express/package.json
* node_modules/express/package.json main
* 如果 package.json 或者 package.json main 不成立,则查找备选项:index.js
* 如果以上条件都不成立,则继续进入上一级目录中的 node_modules 按照上面的规则继续查找
* 如果直到当前文件模块所属磁盘根目录都找不到,最后报错:`can not find module xxx` - node中模块系统 main.js里导入foo.js:
// 默认得到的是对象 // 使用对象中的成员必须 . 点儿某个成员来访问 // 有时候,对于一个模块,我仅仅就是希望导出一个方法就可以了 var fooExports = require('./foo') // ReferenceError: foo is not defined // console.log(foo) console.log(fooExports)foo.js
var foo = 'bar' function add(x, y) { return x + y } // 这种方式不行。 // exports = add // 如果一个模块需要直接导出某个成员,而非挂载的方式 // 那这个时候必须使用下面这种方式 module.exports = 'hello' module.exports = function (x, y) { return x + y } module.exports = { add: function () { return x + y }, str: 'hello' } // 你可以认为在每个模块的最后 return 了这个 exports // 只能得到我想要给你的成员 // 这样做的目的是为了解决变量命名冲突的问题 // exports.add = add // exports 是一个对象 // 我们可以通过多次为这个对象添加成员实现对外导出多个内部成员 // exports.str = 'hello' -
模块原理 main.js
var fooExports = require('./foo') console.log(fooExports) // 如果你实在分不清楚 exports 和 module.exports // 你可以选择忘记 exports // 而只使用 module.exports 也没问题 // // module.exports.xxx = xxx // moudle.exports = {}foo.js
// 在 Node 中,每个模块内部都有一个自己的 module 对象 // 该 module 对象中,有一个成员叫:exports 也是一个对象 // 也就是说如果你需要对外导出成员,只需要把导出的成员挂载到 module.exports 中 // 我们发现,每次导出接口成员的时候都通过 module.exports.xxx = xxx 的方式很麻烦,点儿的太多了 // 所以,Node 为了简化你的操作,专门提供了一个变量:exports 等于 module.exports // var module = { // exports: { // foo: 'bar', // add: function // } // } // 也就是说在模块中还有这么一句代码 // var exports = module.exports // module.exports.foo = 'bar' // module.exports.add = function (x, y) { // return x + y // } // 两者一致,那就说明,我可以使用任意一方来导出内部成员 // console.log(exports === module.exports) // exports.foo = 'bar' // module.exports.add = function (x, y) { // return x + y // } // 当一个模块需要导出单个成员的时候 // 直接给 exports 赋值是不管用的 // exports.a = 123 // exports = {} // exports.foo = 'bar' // module.exports.b = 456 // 给 exports 赋值会断开和 module.exports 之间的引用 // 同理,给 module.exports 重新赋值也会断开 // 这里导致 exports !== module.exports // module.exports = { // foo: 'bar' // } // // 但是这里又重新建立两者的引用关系 // exports = module.exports // exports.foo = 'hello' // {foo: bar} exports.foo = 'bar' // {foo: bar, a: 123} module.exports.a = 123 // exports !== module.exports // 最终 return 的是 module.exports // 所以无论你 exports 中的成员是什么都没用 exports = { a: 456 } // {foo: 'haha', a: 123} module.exports.foo = 'haha' // 没关系,混淆你的 exports.c = 456 // 重新建立了和 module.exports 之间的引用关系了 exports = module.exports // 由于在上面建立了引用关系,所以这里是生效的 // {foo: 'haha', a: 789} exports.a = 789 // 前面再牛逼,在这里都全部推翻了,重新赋值 // 最终得到的是 Function module.exports = function () { console.log('hello') } // 真正去使用的时候: // 导出多个成员:exports.xxx = xxx // 导出多个成员也可以:module.exports = { // } // 导出单个成员:module.exports // 谁来 require 我,谁就得到 module.exports // 默认在代码的最后有一句: // 一定要记住,最后 return 的是 module.exports // 不是 exports // 所以你给 exports 重新赋值不管用, // return module.exports -
package.json 包描述文件
+ 就是产品的说明书
+ `dependencies` 属性,用来保存项目的第三方包依赖项信息
+ 所以建议每个项目都要有且只有一个 package.json (存放在项目的根目录)
+ 我们可以通过 `npm init [--yes]` 来生成 package.json 文件
+ 同样的,为了保存依赖项信息,我们每次安装第三方包的时候都要加上:`--save` 选项。 -
npm 常用命令
+ install
+ uninstall -
路由设计
| 请求方法 | 请求路径 | get 参数 | post 参数 | 备注 | |----------|------------------|----------|--------------------------------|------------------| | GET | /studens | | | 渲染首页 | | GET | /students/new | | | 渲染添加学生页面 | | POST | /studens/new | | name、age、gender、hobbies | 处理添加学生请求 | | GET | /students/edit | id | | 渲染编辑页面 | | POST | /studens/edit | | id、name、age、gender、hobbies | 处理编辑请求 | | GET | /students/delete | id | | 处理删除请求 | | | | | | | -
模块标识
var fs = require('fs') // 咱们所使用的所有文件操作的 API 都是异步的 // 就像你的 ajax 请求一样 // 文件操作中的相对路径可以省略 ./ // fs.readFile('data/a.txt', function (err, data) { // if (err) { // return console.log('读取失败') // } // console.log(data.toString()) // }) // 在模块加载中,相对路径中的 ./ 不能省略 // Error: Cannot find module 'data/foo.js' // require('data/foo.js') // require('./data/foo.js')('hello') // 在文件操作的相对路径中 // ./data/a.txt 相对于当前目录 // data/a.txt 相对于当前目录 // /data/a.txt 绝对路径,当前文件模块所处磁盘根目录 // c:/xx/xx... 绝对路径 // fs.readFile('./data/a.txt', function (err, data) { // if (err) { // console.log(err) // return console.log('读取失败') // } // console.log(data.toString()) // }) // 这里如果忽略了 . 则也是磁盘根目录 require('/data/foo.js') -
封装异步API
// function fn(callback) { // // var callback = function (data) { console.log(data) } // // setTimeout(function () { // // var data = 'hello' // // return data // // }, 1000) // // var data = '默认数据' // // setTimeout(function () { // // data = 'hello' // // }, 1000) // // return data // setTimeout(function () { // var data = 'hello' // callback(data) // }, 1000) // } // // 调用 fn ,得到内部的 data // // console.log(fn()) // // 如果需要获取一个函数中异步操作的结果,则必须通过回调函数来获取 // fn(function (data) { // console.log(data) // }) function fn(callback) { // var callback = function (data) { console.log(data) } setTimeout(function () { var data = 'hello' callback(data) }, 1000) } // 如果需要获取一个函数中异步操作的结果,则必须通过回调函数来获取 fn(function (data) { console.log(data) }) // $.get('dsadsadsa?foo=bar', function (data) { // }) // $.ajax({ // url: 'dsadsa', // type: 'get', // data: { // foo: 'bar' // }, // // 使用者只负责传递,封装这需要去调用 // success: function () { // } // }) // function ajax(options) { // options.success(data) // } -
回调函数
-
promiseAPI代码图示
-
promise链式调用
-
回调函数
+ 异步编程
+ 如果需要得到一个函数内部异步操作的结果,这是时候必须通过回调函数来获取
+ 在调用的位置传递一个函数进来
+ 在封装的函数内部调用传递进来的函数 -
find、findIndex、forEach
+ 数组的遍历方法,都是对函数作为参数一种运用
+ every
+ some
+ includes
+ map
+ reduce -
package-lock.json 文件的作用
+ 下载速度快了
+ 锁定版本 -
JavaScript 模块化
+ Node 中的 CommonJS
+ 浏览器中的
* AMD require.js
* CMD sea.js
+ EcmaScript 官方在 EcmaScript 6 中增加了官方支持
+ EcmaScript 6
+ 后面我们会学,编译工具 -
js中的一等公民函数
// 一种数据类型 // 参数 // 返回值 // 函数太灵活了,无所不能 // 一般情况下,把函数作为参数的目的就是为了获取函数内部的异步操作结果 // JavaScript 单线程、事件循环 function add(x, y) { return x + y } // add(10, 20) // console.log(1) // // 不会等待 // setTimeout(function () { // console.log(2) // console.log('hello') // }, 0) // console.log(3) function add(x, y, callback) { console.log(1) setTimeout(function () { var ret = x + y callback(ret) }, 1000) } add(10, 20, function (ret) { console.log(ret) }) // 注意:凡是需要得到一个函数内部异步操作的结果 // setTimeout // readFile // writeFile // ajax // 这种情况必须通过:回调函数 -
封装ajax方法
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Document</title> </head> <body> <script> // setTimeout // readFile // wirteFile // readdir // ajax // 往往异步 API 都伴随有一个回调函数 // var ret = fn() // $.get('dsadas', fucntion () {}) // var ret = $.get() 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) }) </script> </body> </html> -
find 和 findIndex
// EcmaScript 6 对数组新增了很多方法 // find // findIndex // find 接收一个方法作为参数,方法内部返回一个条件 // find 会遍历所有的元素,执行你给定的带有条件返回值的函数 // 符合该条件的元素会作为 find 方法的返回值 // 如果遍历结束还没有符合该条件的元素,则返回 undefined var users = [ {id: 1, name: '张三'}, {id: 2, name: '张三'}, {id: 3, name: '张三'}, {id: 4, name: '张三'} ] Array.prototype.myFind = function (conditionFunc) { // var conditionFunc = function (item, index) { return item.id === 4 } for (var i = 0; i < this.length; i++) { if (conditionFunc(this[i], i)) { return this[i] } } } var ret = users.myFind(function (item, index) { return item.id === 2 }) console.log(ret) -
express处理404
// Express 对于没有设定的请求路径,默认会返回 Cat not get xxx // 如果你想要定制这个 404 // 需要通过中间件来配置 // 咱们讲中间件的时候说一下如何处理 // 只需要在自己的路由之后增加一个 app.use(function (req, res) { // 所有未处理的请求路径都会跑到这里 // 404 }) -
callback
var fs = require('fs') fs.readFile('./data/a.txt', 'utf8', function (err, data) { if (err) { // return console.log('读取失败') // 抛出异常 // 1. 阻止程序的执行 // 2. 把错误消息打印到控制台 throw err } console.log(data) fs.readFile('./data/b.txt', 'utf8', function (err, data) { if (err) { // return console.log('读取失败') // 抛出异常 // 1. 阻止程序的执行 // 2. 把错误消息打印到控制台 throw err } console.log(data) fs.readFile('./data/c.txt', 'utf8', function (err, data) { if (err) { // return console.log('读取失败') // 抛出异常 // 1. 阻止程序的执行 // 2. 把错误消息打印到控制台 throw err } console.log(data) }) }) }) -
mongoose所有的API都支持promise
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) // 4. 当我们有了模型构造函数之后,就可以使用这个构造函数对 users 集合中的数据为所欲为了(增删改查) // ********************** // #region /新增数据 // ********************** // var admin = new User({ // username: 'zs', // password: '123456', // email: '[email protected]' // }) // admin.save(function (err, ret) { // if (err) { // console.log('保存失败') // } else { // console.log('保存成功') // console.log(ret) // } // }) // ********************** // #endregion /新增数据 // ********************** // ********************** // #region /查询数据 // ********************** // User.find(function (err, ret) { // if (err) { // console.log('查询失败') // } else { // console.log(ret) // } // }) // 用户注册 // 1. 判断用户是否存在 // 如果已存在,结束注册 // 如果不存在,注册(保存一条用户信息) User.find() .then(function (data) { console.log(data) }) // User.findOne({ username: 'aaa' }, function (user) { // if (user) { // console.log('已存在') // } else { // new User({ // username: 'aaa', // password: '123', // email: 'dsadas' // }).save(function () { // }) // } // }) // User.findOne({ // username: 'aaa' // }) // .then(function (user) { // if (user) { // // 用户已存在,不能注册 // console.log('用户已存在') // } else { // // 用户不存在,可以注册 // return new User({ // username: 'aaa', // password: '123', // email: 'dsadas' // }).save() // } // }) // .then(function (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) // } // }) // ********************** // #endregion /查询数据 // ********************** // ********************** // #region /删除数据 // ********************** // User.remove({ // username: 'zs' // }, function (err, ret) { // if (err) { // console.log('删除失败') // } else { // console.log('删除成功') // console.log(ret) // } // }) // ********************** // #endregion /删除数据 // ********************** // ********************** // #region /更新数据 // ********************** // User.findByIdAndUpdate('5a001b23d219eb00c8581184', { // password: '123' // }, function (err, ret) { // if (err) { // console.log('更新失败') // } else { // console.log('更新成功') // } // }) // ********************** // #endregion /更新数据 // ********************** -
MongoDB 数据库
+ 灵活
+ 不用设计数据表
+ 业务的改动不需要关心数据表结构
+ DBA 架构师 级别的工程师都需要掌握这项技能
* 设计
* 维护
* 分布式计算 -
mongoose
+ mongodb 官方包也可以操作 MongoDB 数据库
+ 第三方包:WordPress 项目开发团队
+ 设计 Schema
+ 发布 Model(得到模型构造函数)
* 查询
* 增加
* 修改
* 删除 -
Promise
+ http://es6.ruanyifeng.com/#docs/promise
+ callback hell 回调地狱
+ 回调函数中套了回调函数
+ Promise(EcmaScript 6 中新增了一个语法 API)
+ 容器
* 异步任务(pending)
* resolve
* reject
+ then 方法获取容器的结果(成功的,失败的)
+ then 方法支持链式调用
+ 可以在 then 方法中返回一个 promise 对象,然后在后面的 then 方法中获取上一个 then 返回的 promise 对象的状态结果 -
path 模块
- __dirname 和 __filenamevar fs = require('fs') var path = require('path') // 一般在开发命令行工具的时候,这个设计是必须有用的一个特性 // npm // webpack // __dirname 和 __filename 这俩哥们儿就是专门用来动态的获取当前文件以及文件所属目录的绝对路径 fs.readFile(path.join(__dirname, './00-文件路径.js'), function (err, data) { if (err) { throw err } console.log(data.toString()) })
+ **动态的** 获取当前文件或者文件所处目录的绝对路径
+ 用来解决文件操作路劲的相对路径问题
+ 因为在文件操作中,相对路径相对于执行 `node` 命令所处的目录
+ 所以为了尽量避免这个问题,都建议文件操作的相对路劲都转为:**动态的绝对路径**
+ 方式:path.join(__dirname, '文件名') -
art-template 模板引擎(include、block、extend)
+ include
+ extend
+ block -
表单同步提交和异步提交区别
+ 字符串交互
+ 请求(报文、具有一定格式的字符串)
+ HTTP 就是 Web 中的沟通语言
+ 服务器响应(字符串)
+ 01
+ 服务器端重定向针对异步请求无效 -
Express 中配置使用 express-session 插件
+ 插件也是工具
+ 你只需要明确你的目标就可以了
+ 我们最终的目标就是使用 Session 来帮我们管理一些敏感信息数据状态,例如保存登陆状态
+ 写 Session
* req.session.xxx = xx
+ 读 Session
* req.session.xxx
+ 删除 Session
* req.session.xxx = null
* 更严谨的做法是 `delete` 语法
* delete req.session.xxx -
中间件
var http = require('http') var url = require('url') var cookie = require('./middlewares/cookie') var postBody = require('./middlewares/post-body') var query = require('./middlewares/query') var session = require('./middlewares/session') var server = http.createServer(function (req, res) { // 解析表单 get 请求体 // 解析表单 post 请求体 // 解析 Cookie // 处理 Session // 使用模板引擎 // console.log(req.query) // console.log(req.body) // console.log(req.cookies) // console.log(req.session) // 解析请求地址中的 get 参数 // var urlObj = url.parse(req.url, true) // req.query = urlObj.query query(req, res) // 解析请求地址中的 post 参数 // req.body = { // foo: 'bar' // } postBody(req, res) // 解析 Cookie // req.cookies = { // isLogin: true // } cookie(req, res) // 配置 Session // req.session = {} session(req, res) // 配置模板引擎 res.render = function () { } if (req.url === 'xxx') { // 处理 // query、body、cookies、session、render API 成员 } else if (url === 'xx') { // 处理 } // 上面的过程都是了为了在后面做具体业务操作处理的时候更方便 }) server.listen(3000, function () { console.log('3000. running...') }) -
express-middleware
var express = require('express') var app = express() // 中间件:处理请求的,本质就是个函数 // 在 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.') })