大神的node書,免費javascript
視頻:https://node.university/courses/short-lectures/lectures/3949510css
另外一本書:全棧JavaScript,學習backbone.js node.js and MongoDB. html
1,2章: 前端
node沒有window, 所以也就沒有document對象模型,沒有DOM,沒有hierarchy of element。java
node有global object.(小寫字母),能夠在任何node環境,文件,app中使用。node
你能夠在global object上建立property,同時它也有內建的properties。這些properties也是global的,所以能夠用在anywhere。git
在browser,有內建的modules。github
可是node沒有core modules,經過文件系統可使用各類modules。web
進入node控制檯,直接在terminal輸入node, 這是一個virtual 環境,一般稱爲read-eval-print-loopajax
能夠直接執行Node.js/JavaScript代碼。
⚠️:
require()方法,是node.js的modules功能,在chrome browser 控制檯上會報告❌ReferenceError。
語法:
node filename node -e "直接輸入javaScript代碼" //如$ node -e "console.log(new Date())
參數e, 是evaluate script, -e, --eval=...
Node.js使用chrome v8引擎和ESCAScript,所以大多數語法和前端js相似。
大多數時候支持自動typecasing。primitives包括:String, Number, Boolean, Undefined, Null。
Everything else is an object。包括:Class, Function, Array, RegExp。
在Js,String, Number, Boolean對象有幫助方法:
//例子: 'a' === new String('a') //false, 由於使用new String會返回對象 //因此用toString() 'a' === new String('a').toString() // true //或者使用==,執行自動typecasing, ===加入了類型判斷
Buffer是Node.js增長的數據類型。
它是一個有效的數據存儲data store.
它功能上相似Js的ArrayBuffer。
⚠️:(具體內容,如何建立,使用未看。)
Node8之後的版本都支持ES6。
好比,箭頭函數,使用class, 能夠extend另外一個對象{...anotherObject}, 動態定義屬性名,使用super()關鍵字,使用函數的短語法。
在Node.js,函數最重要,它們是對象!能夠有屬性。
使用function expression定義一個函數,能夠anonymous。例子:
//outer 'this' const f = () => {
//still outer "this" console.log('Hi') return true }
JavaScript把函數當成對象,因此函數也能夠做爲參數傳遞給另外一個函數,嵌套函數會發生callbacks。
let arr4 = new Array(1,"Hi", {a:2}, () => {console.log('boo')}) arr4[3]() // boo
從Array.prototype,global object繼承了一些方法。
JavaScript沒有classes的概念,對象都是直接繼承自其餘對象,即prototypal inheritance!
在JS有幾種繼承模式的類型:
ES6,引用了class,但本質未變,只是寫法上更方便。使用了new , class, extends關鍵字。
具體見以前博客:https://www.cnblogs.com/chentianwei/p/10197813.html
傳統的是函數繼承模式:
//函數user let user = function(ops) { return { firstName: ops.firstName || 'John', lastName: ops.lastName || 'Doe', email: ops.email || 'test@test.com', name: function() { return this.firstName + this.lastName} } } //繼承函數user, let agency = function(ops) { ops = ops || {} var agency = user(ops) agency.customers = ops.customers || 0 agency.isAegncy = true return agency }
每一個Node.js script的運行,都是一個系統進程。
可使用process對象獲得,當前進程的相關信息:
process.pid
process.cwd()
node -e "console.log(process.pid)"
瀏覽器中的window, document對象都不存在於Node.js.
global是全局對象,可使用大量方法。如console, setTimeout(), global.process, global.require(), global.module
例子: global.module
Module { id: '<repl>', exports: {}, parent: undefined, filename: null, loaded: false, children: [], paths: [ '/Users/chentianwei/repl/node_modules', '/Users/chentianwei/node_modules', '/Users/node_modules', '/node_modules', '/Users/chentianwei/.node_modules', '/Users/chentianwei/.node_libraries', '/Users/chentianwei/.nvm/versions/node/v11.0.0/lib/node' ] }
process對象有一系列的有用的信息和方法:
//退出當前進程,若是是在node環境編輯器,直接退出回到終端目錄: process.exit()
⚠️在node環境,直接輸入process,獲得process對象的全部方法和信息。
module.exports = (app) => { // return app }
const messages = require('./routes/messages.js')
真實案例使用:
const messages = require(path.join(__dirname, 'routes', 'messages.js'))
解釋:
_dirname得到絕對路徑, 在加上routes/message.js,獲得真實的路徑。
核心/基本模塊
Node.js不是一個沉重的標準庫。核心模塊很小,但足夠創建任何網絡應用。
Networking is at the core of Node.js!
主要但不是所有的core modules, classes, methods, and events include the following:
http
(http://nodejs.org/api/http.html#http_http): Allows to create HTTP clients and serversutil
(http://nodejs.org/api/util.html): Has a set of utilitiesquerystring
(http://nodejs.org/api/querystring.html): Parses query-string formatted dataurl
(http://nodejs.org/api/url.html): Parses URL datafs
(http://nodejs.org/api/fs.html): Works with a file system (write, read)
Buffer
and String
types
留意咱們須要package.json, node_modules文件夾來在本地安裝modules:
$ npm install <name>
例子:
npm install superagent //而後在program.js,引進這個模塊。 const superagent = require('superagent')
使用npm的一大優點,全部依賴都是本地的。
好比:
module A 使用modules B v1.3,
module C 使用modules B v2.0
A,C有它們各自的B的版本的本地拷貝。
這種策略比Ruby和其餘一些默認使用全局安裝的平臺更好。
最好不要把node_modules文件夾放入Git repository,當這個程序是一個模塊會被其餘app使用的話。
固然,推薦把node_modules放入會部署的app中,這樣能夠防備因爲依賴更新致使的意外損害。
callbacks讓node.js異步。使用promise, event emitters,或者async library能夠防止callback hell
Node.js主要用於創建networking app包括web apps。
由於自然的異步和內建的模塊(net, http),Node.js在networks方面快速發展。
下面是一個例子:
建立一個server object, 定義請求handler, 傳遞數據回到recipient,而後開啓server.js。
const http = require('http') const port = 3000 const server = http.createServer((req, res) => { res.writeHead(200, {'Content-Type': 'text/plain'}) res.end('Hello World\n') }).listen(port, () => { console.log(`Server running at http://localhost:${port}`) })
首先,須要用http module。並設置服務port.
而後,建立一個server, 它有一個回調函數,函數包括response的處理代碼。
爲了設置right header和status code:
res.writeHead(200, {'Content-Type': 'text/plain'})
再輸出一個字符串,使用end symbol.
req和res參數是關於一個HTTP request和response data的信息。另外這2個參數可使用stream(這是一個模塊)
再而後,爲了讓server接受請求requests,使用listen()方法
最後,再terminal輸入:
node server.js
terminals上顯示console.log的信息:
Server running at http://localhost:3000 //在瀏覽器打開鏈接,可看到'Hello World'字樣。這是res.end()實現的。
現代軟件開發者,可使用如Chrome Developer Tools, Firfox Firebug。
由於Node.js和瀏覽器 JavaScript環境相似,因此咱們可使用大量豐富的Debug工具:
最好的debugger是 console.log(), 😄。由於它不會打斷interrupt the flow。
首先,把debugger關鍵字,放在代碼內。
而後,使用開啓一個js文件的檢查:
node inspect program.js
使用:
next
, n
: step to the next statementcont
, c
: continue until the next debugger/break point更多的見the official web site(http://nodejs.org/api/debugger.html).
非GUI,不直觀。
備註(原書提供的node-inspector安裝不上,打開其git,提示我看這篇文章:
使用chrome自帶的EevTools便可。
用法:
全部的chrome devtool功能均可以使用。
node官方的debug文檔:
https://nodejs.org/en/docs/guides/debugging-getting-started/
缺點是:沒法在原文件上斷點。dubugger!
Visual Studio Code (https://code.visualstudio.com/nodejs
被推薦的一個免費的跨平臺的Node.js編輯器,包括內建terminal, Node.js debugging。和大量擴展功能。
被高度推薦:使用方法見(廖雪峯)
atom能夠在原文件斷點可是功能弱,可使用chrome代替。
Node.js程序儲存在內存,若是改變source code, 咱們須要重啓進程process(i.e., node).
手動killing 進程並重開啓一個新的. (Control + C on mac)
⚠️提示:使用Express.js,它會自動reload模版文件,爲每次的新請求。因此server無需重啓。
(摘錄)
Node.js相比Ruby或Java是一個比較年輕的平臺。Express是很流行的框架之一。
Express是web框架,基於core Node.js http和 Connect (http://www.senchalabs.org/connect) 組件。
組件被稱爲中間件middleware。它們是框架哲學的基石,配置大於約定。
所以,Express是高度配置的,在開發階段是靈活的和高度客制的。
若是你寫node web apps, 只使用core Node.js modules,你會發現你反覆的一遍遍的造輪子:
Express.js提供了MVC-like的結構爲你的web apps。
models可使用 Mongoose (http://mongoosejs.com) or Sequelize (http://sequelizejs.com) libraries 。
Express.js相似Ruby on Rails. 區別是rails是約定大於配置。
雖然Express是最流行的框架,但仍有不一樣特點的新框架出現,如Meteor。
一個主文件,通常叫server.js, app.js, index.js。
通常這個文件是node命令的開始, 或者做爲模塊export這個文件 。
在這個文件內,咱們作:
當Express.js app運行,它監聽請求。每一個進來的請求經過一個定義的中間件鏈條和路徑被處理processed。
經過execution flow進行控制。
建立Express.js app使用,2種方法:
1. express-generator:一個全局的npm包,提供命令行工具來快速的建立程序手腳架--推薦快速的prototyping和服務端(thick server)渲染程序。
2. express: 一個本地的包模塊在Node.js app's的node_modules文件夾內--推薦任何程序,須要import express(使用require()或import)
看看當前版本,而後安裝:
npm view express npm i -g express-generator@latest
express --version
注意⚠️mac用戶,可能須要安裝權限。使用sudo。
創建一個文件夾,進入,而後建立package.json,
npm init
而後安裝一個版本:
$ npm install express@4.15.4 --exact
{ "name": "hello-simple", "version": "1.0.0", "description": "", "main": "index.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1" }, "author": "", "license": "ISC", "dependencies": { "express": "^4.15.4" } }
package-lock.json
,用於鎖定版本。
若是想要改變版本:
npm install express@4.15.5 --save
server.js
fileconst express = require('express') let app = express() app.all('*', (req, res) => { res.send('Welcome to Practical node.js!') }) app.listen(3000, () => { return console.log('Open at localhost:3000') })
Then launch it with node server.js
to see "Welcome to Practical Node.js!" in a browser at http://localhost:3000.
Comparable with Ruby on Rails and many other web frameworks, Express.js comes with a CLI for jump-starting your development process
幫助命令:
$ express -h
//運行一個terminal命令,建立手腳架 express [options] [dir|appname]
-v
, --view <engine>
: Add view support (defaults to pug)-c <engine>
, --css <engine>
: Add stylesheet <engine>
support, such as LESS (http://lesscss.org), Stylus(http://learnboost.github.io/stylus) or Compass(http://compass-style.org) (by default, plain CSS is used)--git
: Add .gitignore-f
, --force
: Force app generation on a nonempty directory下面的步驟:
$ express -c styl express-styl //根據terminal上的提示:輸入下面的代碼,進入文件夾並安裝相關依賴。 $ cd express-styl && npm install
//運行app $ DEBUG=express-styl:* npm start
建立了一個app,👍!
進入express-styl/app.js:
const express = require('express'); const path = require('path'); const favicon = require('serve-favicon'); const logger = require('morgan'); const cookieParser = require('cookie-parser'); const bodyParser = require('body-parser'); const stylus = require('stylus'); const index = require('./routes/index'); const users = require('./routes/users'); let app = express(); // view engine setup app.set('views', path.join(__dirname, 'views')); app.set('view engine', 'jade'); // uncomment after placing your favicon in /public //app.use(favicon(path.join(__dirname, 'public', 'favicon.ico'))); app.use(logger('dev')); app.use(bodyParser.json()); app.use(bodyParser.urlencoded({ extended: false })); app.use(cookieParser()); app.use(stylus.middleware(path.join(__dirname, 'public'))); app.use(express.static(path.join(__dirname, 'public'))); app.use('/', index); app.use('/users', users); // catch 404 and forward to error handler app.use(function(req, res, next) { var err = new Error('Not Found'); err.status = 404; next(err); }); // 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(err.status || 500); res.render('error'); }); module.exports = app;
server文件有routes, 來自routes文件夾。
Express app 被輸出:module.exports
被髮射伴隨listen(),在bin/www文件內。
下面看一下app.js內的代碼:
能夠在express-styl/app.js內看到自動生成的2個routes:
var indexRouter = require('./routes/index');
var usersRouter = require('./routes/users');
//...
app.use('/', indexRouter);
app.use('/users', usersRouter);
第一行app.use():處理全部到home page的請求。如:http://localhost:3000/
第二行app.use():處理到/users,如http://localhost:3000/users.
2個routes處理URLs是大小寫字母敏感的。
默認, Express.js不容許開發者經過query string arguments 來導航routes
GET: www.webapplog.com/books/?id=10&ref=201
而是,使用middleware:
app.use((req, res, next) => {
next()
})
// next是一個回調函數。
開發者也能夠完成response, 經過使用send(), end(), render()或其餘Express method,
或者傳遞一個❌對象給next()來回調!
app.use((req, res, next) => { if (!req.session.loggedIN) { return next(new Error('Not enough permissions')) } if (req.session.credits === 0) { return res.render('not-enough-credits.pug') } next() })
下面是另外一個例子:
用條件邏輯來處理一個查詢string, 使用req.query對象:
app.use((req, res, next) => { if (req.query.id) { //處理id,而後當完成後,調用next() } else if (req.query.author) { //和id相同的方式approach } else if (req.query.id && req.query.ref) { //當id and ref存在時,處理。 } else { next() } }) app.get('/about', (req, res, next) => { //這裏的代碼,在query string middleware以後執行。 })
只要request同樣,每一個req或者res對象在隨後的中間件函數或者request handler functions內,req或res對象仍是這個req or res對象。
req對象->中間件1函數->中間件2函數->請求處理函數->...->req對象(內部的key/value發生了變化)
這個特色,讓開發者能夠decorate a reference or a value。例如:
讓第一個中間件的req對象獲得數據庫傳入的數據,隨後的第2箇中間件中的req對象,就是第一個中間件執行完代碼後的req對象, 這個req對象包含數據庫數據。
(個人我的理解:相似Promise.then()鏈條傳遞的promise對象.)
app.use((req, res, next) => { req.db = const db = mongoskin.db('mongodb://@localhost:27017/test') }) //在上一個中間件執行後,req對象新增了db屬性。這個req對象被傳入下一個中間件 app.use((req, res, next) => { req.articles = req.db.collection('articles') }) //上箇中間件執行完成後,req對象又新增了articles屬性。這個req對象被傳入下一個中間件: app.post('/users', (req, res, next) => {
// use req.db or req.articles req.db.collection('users').insert({}, {}, (error, results)=>{ req.articles.insert({}, {}, (error, results)=>{ res.send() }) }) })
回到app.js 文件。對root請求處理, 是"/",至關於routes/index.js。
來自HTTP request 的Everything 都在req對象內,而且把結果寫入res對象。
在express-styl/routes文件夾,分別存在index.js和users.js,這2個文件導出後,會被app.js引入並使用。
var express = require('express') var router = express.Router() // 獲得home , res對象使用render()方法。 router.get('/', function(req,res, next) { res.render('index', {title: 'Express'}) })
module.exports = router
//users.js var express = require('express'); var router = express.Router(); /* GET users listing. */ router.get('/', function(req, res, next) { res.send('respond with a resource'); }); module.exports = router;
中間件是Express.js框架的支柱,脊樑骨。
在express-styl/app.js,每行/每一個聲明在routes上面,都是中間件。
這個中間件包括pass-through functions。當請求在中間件內旅行時,中間件會對請求request作有用或有幫助的事情。
const express = require('express'); const path = require('path'); const favicon = require('serve-favicon'); const logger = require('morgan'); const cookieParser = require('cookie-parser'); const bodyParser = require('body-parser'); const stylus = require('stylus'); //... app.use(favicon(path.join(__dirname, 'public', 'favicon.ico'))); app.use(logger('dev')); app.use(bodyParser.json()); app.use(bodyParser.urlencoded()); app.use(cookieParser()); app.use(express.static(path.join(__dirname, 'public')));
例如:
bodyParser(), cookieParser()
add HTTP request payload (req.body
) 並 parsed cookie data(req.cookie)
app.use(logger('dev')),在terminal上打印每個請求。
在Express v3, 這些中間件是內建的module。
在v4之後,Express Generator 聲明和包含了app.js 和 package.json, 咱們使用npm install來增長鬚要的modules。如:static-favicon
, morgan
, cookie-parser
and body-parser.
這是咱們在一個典型的Express.js中的app.js內,定義配置聲明。使用app.set()
第一個參數是名字,第二個參數是值。
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'pug');
* Module dependencies. var app = require('../app'); var debug = require('debug')('express-styl:server'); var http = require('http');
//Get port from environment and store in Express. //定義port變量,並賦值。當server啓動後,會被用到。 var port = normalizePort(process.env.PORT || '3000'); app.set('port', port);
建立http server
var server = http.createServer(app);
//監聽提供的端口,並當error和listening事件,發生時執行onError, onListening回調函數。 server.listen(port); server.on('error', onError); server.on('listening', onListening);
onError和onListening回調函數,定義在這個www文件內。
Pug是一個模版引擎。相似Ruby on Rails的Haml。它使用whitespace和縮排列。
doctype html html head title= title link(rel='stylesheet', href='/stylesheets/style.css') body block content
第4章會講解它。(相似RoR的slim)可用可不用,每感受效率提升多少,看開發團隊是否用。
建立一個簡單的blog頁面,及相關功能。
從一個開發者的視角,app有如下元素:
這個程序包括全部的CRUD元素。另外使用兩個方法發數據到server:
第一種是現代開發網頁棄用的方式,速度太慢。
第2種是發送和接收數據,經過REST API/HTTP request和渲染客戶端HTML。這些行爲使用前端框架,例如React, Angular, Vue.js等等( many others (http://todomvc.com))。這些框架十分流行。
在hood罩子下面,事實上全部前端都使用jQuery's ajax()方法。
爲了演示,本例子使用REST API經過$.ajax()。
同時,這個例子不使用CLI手腳架。逐步演示代碼。若是建立一個Express.js。讓你理解在這個框架下代碼是如何組織在一塊兒工做的。
開始把,建立咱們的程序文件夾。
一個例子,不使用generators, 額外的modules和middleware。包括如下部分:
Express.js是高度配置的,全部文件夾均可以重命名。不過,默認的文件夾不要更名:
這就夠了。若是想要爲以後的章節的其餘案例建立新的文件夾,可建立:
mkdir hello-world cd hello-world mkdir {public,public/css,public/img,public/js,db,views,views/includes,routes}
上一步,沒有使用Express.js Generator。
npm不只是一個註冊器,也是一個依賴管理工具。它永久創建程序文件package.json。
npm init //建立package.json
$ npm install express //安裝最新的穩定版本。
⚠️推薦使用明確的指定版本,使用@符號。
npm install express@4.15.4 --save
再安裝
另外一個建立package.json文件的方法是拷貝粘貼代碼到package.json, 而後運行npm install
對腳本進行修改:如⬇️所見:
{{ "name": "hello-advanced", "version": "0.0.1", "private": true, "scripts": { "start": "node app.js" }, "dependencies": { "express": "4.15.4", "pug": "2.0.0-rc.4" } }
main entry point,即設定一個主文件main file:
通常使用app.js或者index.js, 執行這個腳本文件使用如下隨意的一個:
$ node app.js
$ node app
$ npm start
//由於在package.json內設定了"scripts": { "start": "node app.js"}
下一步,讓咱們建立app.js
主文件的結構基本內容包括:
1到7的順序很重要,由於請求從上到下的通過中間件的鏈條。
打開app.js,而後:
//引入模塊。 // path module 用於處理文件和目錄的路徑。 const express = require('express') const http = require('http') const path = require('path') // Express使用一個函數模式,執行函數,獲得一個實例。 let app = express();
// 使用express實例方法set('name', 'value')來進行配置 app.set('appName', 'hello-advanced')
還須要定義一些配置在app.js:
若是想要使用環境變量提供的port number,使用模塊process的方法:
process.env.PORT
代碼以下:
app.set('port', process.env.PORT || 3000) app.set('views', path.join(_dirname, 'views')) app.set('view engine', 'html')
__dirname
is an absolute path to the folder with the source code script (a file in which the global variable is called). 獲得程序代碼所在的文件夾。
本application是"/Users/chen/node_practice/hello-world"
//如何查詢 node inspect app.js //而後在控制檯輸入__dirname,便可返回路徑
path.join([...paths])是path模塊的方法之一
// ..返回上一個文件夾 path.join('/foo', 'bar', 'baz/asdf', 'quux', '..'); // Returns: '/foo/bar/baz/asdf'
中間件是Express.js框架的backbone。
它包括:
Middleware用於組織和複用代碼,本質就是帶參數的函數。(第6章會作更多的講解)
下一個組件是routes。Routes處理requests。定義路徑使用幫助方法app.VERB(url, fn1, fn2, ...)
fn:request handlers
url:是URL pattern in RegExp
VERB:是get , post, put, patch, del, all, 用於捕捉不一樣的請求。
Routes按它們被定義的順序,被處理。通常routes被放在middleware後面,可是一些中間件放在routes後面。例如error handler。
下圖展現一個請求的旅行:
在本app Hello World, 只使用一個route,來獲得全部關於URLs的方法的請求(*通配符號wildcard百搭牌)
app.all('*', (req, res) => {
res.render('index', {msg: 'Welcome to Practical Note.js!'})
})
在這個請求內,使用res.render()函數渲染視圖模版。res.render()的第一個參數是名字'index', 第二個參數是數據對象data object。
res.render(viewName, data, callback(error, html))
express的方法。
render()方法調用後,調用core http組件的end()方法,用於完成response。
換言之,中間件的鏈條不會在res.render()後繼續執行代碼。
(第4章,詳細分析)
可是http.createServer(app).listen()也能夠生效。這2個鏈接的方法都是核心模塊http的方法。
監聽鏈接。
http.createServer(app).listen(app.get('port'), () => { console.log(`Express server listening on port ${app.get('port')}`) })
你也可使用https.createServer(app).listen()
for the HTTPS support, 當你準備好部署你的server到產品。
在運行server以前,咱們須要建立views/index.html文件
Express默認使用jade模版,也能夠自定義如Pug,
若是想使用原生html模版,須要安裝ejs (點擊查看原文解釋)
npm install ejs //引入ejs var ejs = require('ejs') //設置html engine app.engine('html', ejs.__express) //設置視圖引擎, 'view engine'表示沒有指定文件模版格式時,默認使用的引擎插件。 app.set('view engine', 'html')
注:在express搭建的服務器中,html引擎沒有被配置,直接添加便可;視圖引擎已配置,修改配置便可。
index.html內的代碼:
<h1>hello</h1> <p>You are welcome</p> // 插入傳入模版的數據。 <p><%= msg %></p>
app.engine(ext, callback)
http://expressjs.com/en/4x/api.html#app.engine
Registers the given template engine callback
as ext
.
默認,Express將require()這個基於文件擴展的engine。
app.engine('pug', require('pug').__express);
app.engine('html', require('ejs').renderFile); //也能夠用__express方法
$ node app
第2章,學習使用Express.js, 知道它是如何工做的。瞭解使用手腳架來生成apps。
經過Blog app案例,瞭解了建立這個程序的過程。
最後,咱們接觸了一些主題:settings, request process, routes, Ajax server side, ejs模塊產生模版。
下一章,談談驅動測試開發。另外會增長一個數據庫到Blog routes。展現如何把數據轉入到HTML pages!