前端学习之路---node.js(二)
-
知識點? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?下一篇? ? vue(一)
? + 黑盒子、啞巴
? + 寫代碼讓它變得更智能
? + 按照你設計好的套路供用戶使用
? + 最少兩次請求,發起 ajax 在客戶端使用模板引擎渲染
? + 客戶端拿到的就是服務端已經渲染好的
? + url.parse()
? + header('location')
? ? * 301 永久重定向 瀏覽器會記住
? ? ? - a.com b.com
? ? ? - a 瀏覽器不會請求 a 了
? ? ? - 直接去跳到 b 了
? ? * 302 臨時重定向 瀏覽器不記憶
? ? ? - a.com b.com
? ? ? - a.com 還會請求 a
? ? ? - a 告訴瀏覽器你往 b
? + EcmaScript 5 提供的
? ? * 不兼容 IE 8
? + jQuery 的 each 由 jQuery 這個第三方庫提供
? ? * jQuery 2 以下的版本是兼容 IE 8 的
? ? * 它的 each 方法主要用來遍歷 jQuery 實例對象(偽數組)
? ? * 同時它也可以作為低版本瀏覽器中 forEach 替代品
? ? * jQuery 的實例對象不能使用 forEach 方法,如果想要使用必須轉為數組才可以使用
? ? * `[].slice.call(jQuery實例對象)`
? + 301 永久重定向,瀏覽器會記住
? + 302 臨時重定向
? + 每個模塊中都有一個 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` 這個用來重新建立引用關系的
? + 之所以讓大家明白這個道理,是希望可以更靈活的去用它
? + JavaScript 既能寫前端也能寫服務端
? + 優先從緩存加載
? + 核心模塊
? + 路徑形式的模塊
? ? * `./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`
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 + ycallback(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.responseTextcallback(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 方法的返回值 // 如果遍歷結束還沒有符合該條件的元素,則返回 undefinedvar 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: 'admin@admin.com' // })// 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 和 __filename
? + **動態的** 獲取當前文件或者文件所處目錄的絕對路徑
? + 用來解決文件操作路勁的相對路徑問題
? + 因為在文件操作中,相對路徑相對于執行 `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.queryquery(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.postapp.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.') })??
總結
以上是生活随笔為你收集整理的前端学习之路---node.js(二)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 什么是关键科学问题
- 下一篇: 2017年html5行业报告,云适配发布