細品NodeJS

一、Node.js是什么

Node.js? is a JavaScript runtime built on Chrome's V8 JavaScript engine.

1政敢、特性

Node.js 可以解析JS代碼(沒有瀏覽器安全級別的限制)提供很多系統(tǒng)級別的API由缆,如:

  • 文件的讀寫 (File System)
  • 進程的管理 (Process)
  • 網(wǎng)絡通信 (HTTP/HTTPS)
  • ……

2匾七、舉例

2.1 瀏覽器安全級別的限制

Ajax測試

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>browser-safe-sandbox</title>
</head>
<body>
  <div>browser-safe-sandbox</div>
  <script>
    const xhr = new XMLHttpRequest()
    xhr.open('get', 'https://m.maoyan.com/ajax/moreClassicList?sortId=1&showType=3&limit=10&offset=30&optimus_uuid=A5518FF0AFEC11EAAB158D7AB0D05BBBD74C9789D9F649898982E6542C7DD479&optimus_risk_level=71&optimus_code=10', false)
    xhr.send()
  </script>
</body>
</html>

瀏覽器預覽

browser-sync start --server --files **/* --directory

2.2 文件的讀寫 (File System)

const fs = require('fs')

fs.readFile('./ajax.png', 'utf-8', (err, content) => {
  console.log(content)
})

2.3 進程的管理(Process)

function main(argv) {
  console.log(argv)
}

main(process.argv.slice(2))

運行

node 2.3-process.js argv1 argv2

2.4 網(wǎng)絡通信(HTTP/HTTPS)

const http = require("http")

http.createServer((req,res) => {
  res.writeHead(200, {
    "content-type": "text/plain"
  })
  res.write("hello nodejs")
  res.end()
}).listen(3000)

二卒蘸、Node 相關(guān)工具

1填抬、NVM: Node Version Manager

1.1 Mac 安裝 nvm

https://github.com/nvm-sh/nvm/blob/master/README.md

1.2 Windows 安裝 nvm

nvm-windows
nodist

2、NPM: Node Package Manager

2.1 全局安裝package

$ npm install forever --global (-g)
$ forever
$ npm uninstall forever --global
$ forever

全局安裝包的目錄

  • Mac

    /Users/felix/.nvm/versions/node/nvm各個版本/bin/
    
  • Windows

    C:\Users\你的用戶名\AppData\Roaming\npm\node_modules
    

2.2 本地安裝package

$ cd ~/desktop
$ mkdir gp-project
$ cd gp-project
$ npm install underscore
$ npm list (ls)

2.3 package.json初始化

$ pwd
$ npm init -y
$ ls
$ cat package.json

2.4 使用package.json

$ npm install underscore --save
$ cat package.json
$ npm install lodash --save-dev
$ cat package.json
$ rm -rf node_modules
$ ls
$ npm install
$ npm uninstall underscore --save
$ npm list | grep underscore
$ cat package.json

2.5 安裝指定版本的包

$ pwd
$ npm list
$ npm info underscore
$ npm view underscore versions
$ npm install underscore@1.8.0
$ npm list
$ npm uninstall underscore
$ npm list

2.6 更新本地安裝的包

$ npm info underscore
$ npm view underscore versions
$ npm install underscore@1.4.4 --save-dev
$ npm list | grep gulp
$ npm outdated //~2.0.0表示patch, ^2.0.0表示minor * 表示xx最新版本
$ npm list | grep gulp
$ npm update

2.7 清除緩存

npm cache clean --force

2.8 上傳自己的包

2.8.1 編寫模塊

保存為index.js

exports.sayHello = function(){ 
  return 'Hello World'; 
}
2.8.2 初始化包描述文件

$ npm init package.json

{ 
  "name": "gp19-npm", 
  "version": "1.0.1", 
  "description": "gp19 self module", 
  "main": "index.js",
  "scripts": { 
    "test": "make test" 
  }, 
  "repository": { 
    "type": "Git", 
    "url": "git+https://github.com/lurongtao/gp19-npm.git" 
  }, 
  "keywords": [ 
    "demo" 
  ], 
  "author": "Felixlu", 
  "license": "ISC", 
  "bugs": { 
    "url": "https://github.com/lurongtao/gp19-npm/issues" 
  }, 
  "homepage": "https://github.com/lurongtao/gp19-npm#readme", 
}
2.8.3 注冊npm倉庫賬號
https://www.npmjs.com 上面的賬號
felix_lurt/qqmko09ijn
$ npm adduser
2.8.4 上傳包
$ npm publish

坑:403 Forbidden

查看npm源:npm config get registry
切換npm源方法一:npm config set registry http://registry.npmjs.org
切換npm源方法二:nrm use npm
2.8.5 安裝包
$ npm install gp19-npm
2.8.6 卸載包
查看當前項目引用了哪些包 :
npm ls
卸載包:
npm unpublish --force
2.8.7 使用引入包
var hello = require('gp19-npm')
hello.sayHello()

2.9 npm 腳本

Node 開發(fā)離不開 npm辆童,而腳本功能是 npm 最強大宜咒、最常用的功能之一。

一把鉴、什么是 npm 腳本故黑?

npm 允許在 package.json 文件里面,使用 scripts 字段定義腳本命令庭砍。

{
  // ...
  "scripts": {
    "build": "node build.js"
  }
}

二场晶、執(zhí)行順序

如果 npm 腳本里面需要執(zhí)行多個任務,那么需要明確它們的執(zhí)行順序怠缸。

script1.js

var x = 0
console.log(x)

script2.js

var y = 0
console.log(y)
"scripts": {
  "script1": "node script1.js",
  "script2": "node script2.js"
}

如果是并行執(zhí)行(即同時的平行執(zhí)行)诗轻,可以使用 & 符號。

$ npm run script1 & npm run script2

如果是繼發(fā)執(zhí)行(即只有前一個任務成功揭北,才執(zhí)行下一個任務)扳炬,可以使用 && 符號。

$ npm run script1 && npm run script2

三罐呼、簡寫形式

常用的 npm 腳本簡寫形式鞠柄。

npm start 是 npm run start

四、變量

npm 腳本有一個非常強大的功能嫉柴,就是可以使用 npm 的內(nèi)部變量厌杜。

首先,通過 npm_package_ 前綴,npm 腳本可以拿到 package.json 里面的字段夯尽。比如瞧壮,下面是一個 package.json。

注意:一定要在 npm 腳本中運行(如:npm run view)才可以匙握,直接在命令行中運行JS(如:node view.js)是拿不到值的

{
  "name": "foo", 
  "version": "1.2.5",
  "scripts": {
    "view": "node view.js"
  }
}

那么咆槽,變量 npm_package_name 返回 foo,變量 npm_package_version 返回 1.2.5圈纺。

// view.js
console.log(process.env.npm_package_name); // foo
console.log(process.env.npm_package_version); // 1.2.5

上面代碼中秦忿,我們通過環(huán)境變量 process.env 對象,拿到 package.json 的字段值蛾娶。如果是 Bash 腳本灯谣,可以用npm_package_name 和npm_package_version 取到這兩個值。

npmpackage前綴也支持嵌套的package.json字段蛔琅。

"repository": {
  "type": "git",
  "url": "xxx"
},
scripts: {
  "view": "echo $npm_package_repository_type"
}

上面代碼中胎许,repository 字段的 type 屬性,可以通過 npm_package_repository_type 取到罗售。

下面是另外一個例子辜窑。

"scripts": {
  "install": "foo.js"
}

上面代碼中,npm_package_scripts_install 變量的值等于 foo.js寨躁。

然后穆碎,npm 腳本還可以通過 npmconfig 前綴,拿到 npm 的配置變量朽缎,即 npm config get xxx 命令返回的值惨远。比如,當前模塊的發(fā)行標簽话肖,可以通過 npm_config_tag 取到北秽。

"view": "echo $npm_config_tag",

注意,package.json 里面的 config 對象最筒,可以被環(huán)境變量覆蓋贺氓。

{ 
  "name" : "foo",
  "config" : { "port" : "8080" },
  "scripts" : { "start" : "node server.js" }
}

上面代碼中,npm_package_config_port 變量返回的是 8080床蜘。這個值可以用下面的方法覆蓋辙培。

$ npm config set foo:port 80

最后,env命令可以列出所有環(huán)境變量邢锯。

"env": "env"

2.10 npm 安裝 git 上發(fā)布的包

# 這樣適合安裝公司內(nèi)部的git服務器上的項目
npm install git+https://git@github.com:lurongtao/gp-project.git

# 或者以ssh的方式
npm install git+ssh://git@github.com:lurongtao/gp-project.git

2.11 cross-env 使用

2.11.1 cross-env是什么

運行跨平臺設置和使用環(huán)境變量的腳本

2.11.2 出現(xiàn)原因

當您使用 NODE_ENV=production, 來設置環(huán)境變量時扬蕊,大多數(shù) Windows 命令提示將會阻塞(報錯)。(異常是Windows上的Bash丹擎,它使用本機Bash尾抑。)換言之歇父,Windows 不支持 NODE_ENV=production 的設置方式。

2.11.3 解決

cross-env 使得您可以使用單個命令再愈,而不必擔心為平臺正確設置或使用環(huán)境變量榜苫。這個迷你的包(cross-env)能夠提供一個設置環(huán)境變量的 scripts,讓你能夠以 Unix 方式設置環(huán)境變量翎冲,然后在 Windows 上也能兼容運行垂睬。

2.11.4 安裝

npm install --save-dev cross-env

2.11.5 使用
{
  "scripts": {
    "build": "cross-env NODE_ENV=production webpack --config build/webpack.config.js"
  }
}

NODE_ENV環(huán)境變量將由 cross-env 設置 打印 process.env.NODE_ENV === 'production'

3、NRM: npm registry manager

3.1 手工切換源

3.1.1 查看當前源
npm config get registry
3.1.2 切換淘寶源
npm config set registry https://registry.npm.taobao.org

3.2 NRM 管理源

NRM (npm registry manager)是npm的鏡像源管理工具抗悍,有時候國外資源太慢驹饺,使用這個就可以快速地在 npm 源間切換。

3.2.1 安裝 nrm

在命令行執(zhí)行命令缴渊,npm install -g nrm逻淌,全局安裝nrm。

3.2.2 使用 nrm

執(zhí)行命令 nrm ls 查看可選的源疟暖。 其中,帶*的是當前使用的源田柔,上面的輸出表明當前源是官方源俐巴。

3.2.3 切換 nrm

如果要切換到taobao源,執(zhí)行命令nrm use taobao硬爆。

3.2.4 測試速度

你還可以通過 nrm test 測試相應源的響應時間欣舵。

nrm test

4、NPX: npm package extention

npm 從5.2版開始缀磕,增加了 npx 命令缘圈。它有很多用處,本文介紹該命令的主要使用場景袜蚕。

Node 自帶 npm 模塊糟把,所以可以直接使用 npx 命令。萬一不能用牲剃,就要手動安裝一下遣疯。

$ npm install -g npx

4.1 調(diào)用項目安裝的模塊

npx 想要解決的主要問題,就是調(diào)用項目內(nèi)部安裝的模塊凿傅。比如缠犀,項目內(nèi)部安裝了Mocha。

$ npm install -D mocha

一般來說聪舒,調(diào)用 Mocha 辨液,只能在項目腳本和 package.json 的scripts字段里面,如果想在命令行下調(diào)用箱残,必須像下面這樣滔迈。

# 項目的根目錄下執(zhí)行
$ node-modules/.bin/mocha --version

npx 就是想解決這個問題,讓項目內(nèi)部安裝的模塊用起來更方便,只要像下面這樣調(diào)用就行了亡鼠。

$ npx mocha --version

npx 的原理很簡單赏殃,就是運行的時候,會到node_modules/.bin路徑和環(huán)境變量$PATH里面间涵,檢查命令是否存在仁热。

由于 npx 會檢查環(huán)境變量$PATH,所以系統(tǒng)命令也可以調(diào)用勾哩。

# 等同于 ls
$ npx ls

注意抗蠢,Bash 內(nèi)置的命令不在$PATH里面,所以不能用思劳。比如迅矛,cd是 Bash 命令,因此就不能用npx cd潜叛。

4.2 避免全局安裝模塊

除了調(diào)用項目內(nèi)部模塊秽褒,npx 還能避免全局安裝的模塊。比如威兜,create-react-app 這個模塊是全局安裝销斟,npx 可以運行它,而且不進行全局安裝椒舵。

$ npx create-react-app my-react-app

上面代碼運行時蚂踊,npx 將 create-react-app 下載到一個臨時目錄,使用以后再刪除笔宿。所以犁钟,以后再次執(zhí)行上面的命令,會重新下載 create-react-app泼橘。

注意涝动,只要 npx 后面的模塊無法在本地發(fā)現(xiàn),就會下載同名模塊侥加。比如捧存,本地沒有安裝http-server模塊,下面的命令會自動下載該模塊担败,在當前目錄啟動一個 Web 服務昔穴。

$ npx http-server

4.3 --no-install 參數(shù)和 --ignore-existing 參數(shù)

如果想讓 npx 強制使用本地模塊,不下載遠程模塊提前,可以使用--no-install參數(shù)吗货。如果本地不存在該模塊,就會報錯狈网。

$ npx --no-install http-server

反過來宙搬,如果忽略本地的同名模塊笨腥,強制安裝使用遠程模塊,可以使用--ignore-existing參數(shù)勇垛。比如脖母,本地已經(jīng)安裝了http-server,但還是想使用遠程模塊闲孤,就用這個參數(shù)谆级。

$ npx --ignore-existing http-server

三、模塊/包 與 CommonJS

1讼积、模塊/包分類

Node.js 有三類模塊肥照,即內(nèi)置的模塊、第三方的模塊勤众、自定義的模塊舆绎。

1.1 內(nèi)置的模塊

Node.js 內(nèi)置模塊又叫核心模塊,Node.js安裝完成可直接使用们颜。如:

const path = require('path')
var extname = path.extname('index.html')
console.log(extname)

1.2 第三方的Node.js模塊

第三方的Node.js模塊指的是為了實現(xiàn)某些功能吕朵,發(fā)布的npmjs.org上的模塊,按照一定的開源協(xié)議供社群使用窥突。如:

npm install chalk
const chalk = require('chalk')
console.log(chalk.blue('Hello world!'))

1.3 自定義的Node.js模塊

自定義的Node.js模塊边锁,也叫文件模塊,是我們自己寫的供自己使用的模塊波岛。同時,這類模塊發(fā)布到npmjs.org上就成了開源的第三方模塊音半。

自定義模塊是在運行時動態(tài)加載则拷,需要完整的路徑分析、文件定位曹鸠、編譯執(zhí)行過程煌茬、速度相比核心模塊稍微慢一些,但是用的非常多彻桃。

1.3.1 模塊定義坛善、接口暴露和引用接口

我們可以把公共的功能 抽離成為一個單獨的 js 文件 作為一個模塊,默認情況下面這個模塊里面的方法或者屬性邻眷,外面是沒法訪問的眠屎。如果要讓外部可以訪問模塊里面的方法或者屬性,就必須在模塊里面通過 exports 或者 module.exports 暴露屬性或者方法肆饶。

m1.js:

const name = 'gp19'

const sayName = () => {
  console.log(name)
}

console.log('module 1')

// 接口暴露方法一:
module.exports = {
  say: sayName
}

// 接口暴露方法二:
exports.say = sayName

// 錯誤改衩!
exports = {
  say: sayName
}

main.js:

const m1 = require('./m1')
m1.say()
1.3.2 模塊的循環(huán)引用

由于 exports 使用方式方式不對,會在兩個不同 js 循環(huán)引用的情況下驯镊,導致其中一個 js 無法獲取另外一個 js 的方法葫督,從而導致執(zhí)行報錯竭鞍。如:

  • a.js
exports.done = false
const b = require('./b.js')
console.log('in a, b.done = %j', b.done)
exports.done = true
console.log('a done')
  • b.js
console.log('b starting')
exports.done = false
const a = require('./a.js')
console.log('in b, a.done = %j', a.done)
exports.done = true
console.log('b done')
  • main.js
console.log('main starting')
const a = require('./a.js')
const b = require('./b.js')
console.log('in main, a.done = %j, b.done = %j', a.done, b.done)

main.js 首先會 load a.js, 此時執(zhí)行到const b = require('./b.js');的時候,程序會轉(zhuǎn)去loadb.js, 在b.js中執(zhí)行到const a = require('./a.js'); 為了防止無限循環(huán)橄镜,將a.jsexports的未完成副本返回到b.js模塊偎快。然后b.js完成加載,并將其導出對象提供給a.js模塊洽胶。

我們知道nodeJs的對每個js文件進行了一層包裝稱為module晒夹,module中有一個屬性exports,當調(diào)用require('a.js')的時候其實返回的是module.exports對象妖异,module.exports初始化為一個{}空的object惋戏,所以在上面的例子中,執(zhí)行到b.js中const a = require('./a.js');時不會load新的a module, 而是將已經(jīng)load但是還未完成的a module的exports屬性返回給b module他膳,所以b.js拿到的是a module的exports對象响逢,即:{done:false}, 雖然在a.js中exports.done被修改成了true,但是由于此時a.js未load完成棕孙,所以在b.js輸出的a module的屬性done為false舔亭,而在main.js中輸出的a module的屬性done為true. Nodejs通過上面這種返回未完成exports對象來解決循環(huán)引用的問題。

四蟀俊、常用內(nèi)置模塊

這里介紹幾個常用的內(nèi)置模塊:url, querystring, http, events, fs, stream, readline, crypto, zlib

1钦铺、url

1.1 parse

url.parse(urlString[, parseQueryString[, slashesDenoteHost]])

const url = require('url')
const urlString = 'https://www.baidu.com:443/ad/index.html?id=8&name=mouse#tag=110'
const parsedStr = url.parse(urlString)
console.log(parsedStr)

1.2 format

url.format(urlObject)

const url = require('url')
const urlObject = {
  protocol: 'https:',
  slashes: true,
  auth: null,
  host: 'www.baidu.com:443',
  port: '443',
  hostname: 'www.baidu.com',
  hash: '#tag=110',
  search: '?id=8&name=mouse',
  query: { id: '8', name: 'mouse' },
  pathname: '/ad/index.html',
  path: '/ad/index.html?id=8&name=mouse',
  href: 'https://www.baidu.com:443/ad/index.html?id=8&name=mouse#tag=110'
}
const parsedObj = url.format(urlObject)
console.log(parsedObj)

1.3 resolve

url.resolve(from, to)

const url = require('url')
var a = url.resolve('/one/two/three', 'four')
var b = url.resolve('http://example.com/', '/one')
var c = url.resolve('http://example.com/one', '/two')
console.log(a + "," + b + "," + c)

2、querystring

2.1 parse

querystring.parse(str[, sep[, eq[, options]]])

const querystring = require('querystring')
var qs = 'x=3&y=4'
var parsed = querystring.parse(qs)
console.log(parsed)

2.2 stringify

querystring.stringify(obj[, sep[, eq[, options]]])

const querystring = require('querystring')
var qo = {
  x: 3,
  y: 4
}
var parsed = querystring.stringify(qo)
console.log(parsed)

2.3 escape/unescape

querystring.escape(str)

const querystring = require('querystring')
var str = 'id=3&city=北京&url=https://www.baidu.com'
var escaped = querystring.escape(str)
console.log(escaped)

querystring.unescape(str)

const querystring = require('querystring')
var str = 'id%3D3%26city%3D%E5%8C%97%E4%BA%AC%26url%3Dhttps%3A%2F%2Fwww.baidu.com'
var unescaped = querystring.unescape(str)
console.log(unescaped)

3肢预、http/https

3.1 get

var http = require('http')
var https = require('https')

// 1矛洞、接口 2、跨域
const server = http.createServer((request, response) => {
  var url = request.url.substr(1)

  var data = ''

  response.writeHeader(200, {
    'content-type': 'application/json;charset=utf-8',
    'Access-Control-Allow-Origin': '*'
  })

  https.get(`https://m.lagou.com/listmore.json${url}`, (res) => {

    res.on('data', (chunk) => {
      data += chunk
    })

    res.on('end', () => {
      response.end(JSON.stringify({
        ret: true,
        data
      }))
    })
  })

})

server.listen(8080, () => {
  console.log('localhost:8080')
})

3.2 post:服務器提交(攻擊)

const https = require('https')
const querystring = require('querystring')

const postData = querystring.stringify({
  province: '上海',
  city: '上海',
  district: '寶山區(qū)',
  address: '同濟支路199號智慧七立方3號樓2-4層',
  latitude: 43.0,
  longitude: 160.0,
  message: '求購一條小魚',
  contact: '13666666',
  type: 'sell',
  time: 1571217561
})

const options = {
  protocol: 'https:',
  hostname: 'ik9hkddr.qcloud.la',
  method: 'POST',
  port: 443,
  path: '/index.php/trade/add_item',
  headers: {
    'Content-Type': 'application/x-www-form-urlencoded',
    'Content-Length': Buffer.byteLength(postData)
  }
}

function doPost() {
  let data

  let req = https.request(options, (res) => {
    res.on('data', chunk => data += chunk)
    res.on('end', () => {
      console.log(data)
    })
  })

  req.write(postData)
  req.end()
}

// setInterval(() => {
//   doPost()
// }, 1000)

3.3 跨域:jsonp

const http = require('http')
const url = require('url')

const app = http.createServer((req, res) => {
  let urlObj = url.parse(req.url, true)

  switch (urlObj.pathname) {
    case '/api/user':
      res.end(`${urlObj.query.cb}({"name": "gp145"})`)
      break
    default:
      res.end('404.')
      break
  }
})

app.listen(8080, () => {
  console.log('localhost:8080')
})

3.4 跨域:CORS

const http = require('http')
const url = require('url')
const querystring = require('querystring')

const app = http.createServer((req, res) => {
  let data = ''
  let urlObj = url.parse(req.url, true)

  res.writeHead(200, {
    'content-type': 'application/json;charset=utf-8',
    'Access-Control-Allow-Origin': '*'
  })

  req.on('data', (chunk) => {
    data += chunk
  })

  req.on('end', () => {
    responseResult(querystring.parse(data))
  })

  function responseResult(data) {
    switch (urlObj.pathname) {
      case '/api/login':
        res.end(JSON.stringify({
          message: data
        }))
        break
      default:
        res.end('404.')
        break
    }
  }
})

app.listen(8080, () => {
  console.log('localhost:8080')
})

3.5 跨域:middleware(http-proxy-middware)

const http = require('http')
const proxy = require('http-proxy-middleware')

http.createServer((req, res) => {
  let url = req.url

  res.writeHead(200, {
    'Access-Control-Allow-Origin': '*'
  })

  if (/^\/api/.test(url)) {
    let apiProxy = proxy('/api', { 
      target: 'https://m.lagou.com',
      changeOrigin: true,
      pathRewrite: {
        '^/api': ''
      }
    })

    // http-proy-middleware 在Node.js中使用的方法
    apiProxy(req, res)
  } else {
    switch (url) {
      case '/index.html':
        res.end('index.html')
        break
      case '/search.html':
        res.end('search.html')
        break
      default:
        res.end('[404]page not found.')
    }
  }
}).listen(8080)

3.6 爬蟲

const https = require('https')
const http = require('http')
const cheerio = require('cheerio')

http.createServer((request, response) => {
  response.writeHead(200, {
    'content-type': 'application/json;charset=utf-8'
  })

  const options = {
    protocol: 'https:',
    hostname: 'maoyan.com',
    port: 443,
    path: '/',
    method: 'GET'
  }

  const req = https.request(options, (res) => {
    let data = ''
    res.on('data', (chunk) => {
      data += chunk
    })

    res.on('end', () => {
      filterData(data)
    })
  })

  function filterData(data) {
    let $ = cheerio.load(data)
    let $movieList = $('.movie-item')
    let movies = []
    $movieList.each((index, value) => {
      movies.push({
        title: $(value).find('.movie-title').attr('title'),
        score: $(value).find('.movie-score i').text(),
      })
    })

    response.end(JSON.stringify(movies))
  }

  req.end()
}).listen(9000)

4烫映、Events

const EventEmitter = require('events')

class MyEventEmitter extends EventEmitter {}

const event = new MyEventEmitter()

event.on('play', (movie) => {
  console.log(movie)
})

event.emit('play', '我和我的祖國')
event.emit('play', '中國機長')

5沼本、File System

const fs = require('fs')
const fsP = require('fs').promises

// 創(chuàng)建文件夾
fs.mkdir('./logs', (err) => {
  console.log('done.')
})

// 文件夾改名
fs.rename('./logs', './log', () => {
  console.log('done')
})

// 刪除文件夾
fs.rmdir('./log', () => {
  console.log('done.')
})

// 寫內(nèi)容到文件里
fs.writeFile(
  './logs/log1.txt',
  'hello',
  // 錯誤優(yōu)先的回調(diào)函數(shù)
  (err) => {
    if (err) {
      console.log(err.message)
    } else {
      console.log('文件創(chuàng)建成功')
    }
  }
)

// 給文件追加內(nèi)容
fs.appendFile('./logs/log1.txt', '\nworld', () => {
  console.log('done.')
})

// 讀取文件內(nèi)容
fs.readFile('./logs/log1.txt', 'utf-8', (err, data) => {
  console.log(data)
})

// 刪除文件
fs.unlink('./logs/log1.txt', (err) => {
  console.log('done.')
})

// 批量寫文件
for (var i = 0; i < 10; i++) {
  fs.writeFile(`./logs/log-${i}.txt`, `log-${i}`, (err) => {
    console.log('done.')
  })
}

// 讀取文件/目錄信息
fs.readdir('./', (err, data) => {
  data.forEach((value, index) => {
    fs.stat(`./${value}`, (err, stats) => {
      // console.log(value + ':' + stats.size)
      console.log(value + ' is ' + (stats.isDirectory() ? 'directory' : 'file'))
    })
  })
})

// 同步讀取文件
try {
  const content = fs.readFileSync('./logs/log-1.txt', 'utf-8')
  console.log(content)
  console.log(0)
} catch (e) {
  console.log(e.message)
}

console.log(1)

// 異步讀取文件:方法一
fs.readFile('./logs/log-0.txt', 'utf-8', (err, content) => {
  console.log(content)
  console.log(0)
})
console.log(1)

// 異步讀取文件:方法二
fs.readFile('./logs/log-0.txt', 'utf-8').then(result => {
  console.log(result)
})

// 異步讀取文件:方法三
function getFile() {
  return new Promise((resolve) => {
    fs.readFile('./logs/log-0.txt', 'utf-8', (err, data) => {
      resolve(data)
    })
  })
}

;(async () => {
  console.log(await getFile())
})()

// 異步讀取文件:方法四
const fsp = fsP.readFile('./logs/log-1.txt', 'utf-8').then((result) => {
  console.log(result)
})

console.log(fsP)

// watch 監(jiān)測文件變化
fs.watch('./logs/log-0.txt', () => {
  console.log(0)
})

6、Stream

const fs = require('fs')

const readstream = fs.createReadStream('./note.txt')
const writestream = fs.createWriteStream('./note2.txt')

writestream.write(readstream)

7锭沟、Zlib

const fs = require('fs')
const zlib = require('zlib')

const gzip = zlib.createGzip()

const readstream = fs.createReadStream('./note.txt')
const writestream = fs.createWriteStream('./note2.txt')

readstream
  .pipe(gzip)
  .pipe(writestream)

writestream.write(readstream)

8抽兆、ReadLine

const readline = require('readline')

const rl = readline.createInterface({
  input: process.stdin,
  output: process.stdout
})

rl.question('What do you think of Node.js? ', (answer) => {
  // TODO: Log the answer in a database
  console.log(`Thank you for your valuable feedback: ${answer}`)

  rl.close()
})

9、Crypto

const crypto = require('crypto')

const secret = 'abcdefg'
const hash = crypto.createHmac('sha256', secret)
                   .update('I love you')
                   .digest('hex')
console.log(hash)

四族淮、路由

var http = require('http')
var fs = require('fs')

http.createServer( function ( req, res ) {

  switch ( req.url ) {
    case '/home':
      res.write('home')
      res.end()
      break
    case '/mine':
      res.write('mine')
      res.end()
      break
    case '/login': 
      fs.readFile( './static/login.html',function ( error , data ) {
        if ( error ) throw error  
        res.write( data )
        res.end()
      })
      break
    case '/fulian.jpg':
      fs.readFile( './static/fulian.jpg', 'binary', function( error , data ) {
        if( error ) throw error 
        res.write( data, 'binary' )
        res.end()
      })
      break
    default: 
      break
   }

 }).listen( 8000, 'localhost', function () {
   console.log( '服務器運行在: http://localhost:8000' )
 })

五辫红、靜態(tài)資源服務

5.1 readStaticFile

/modules/readStaticFile.js

// 引入依賴的模塊
var path = require('path')
var fs = require('fs')
var mime = require('mime')

function readStaticFile(res, filePathname) {

  var ext = path.parse(filePathname).ext
  var mimeType = mime.getType(ext)

  // 判斷路徑是否有后綴, 有的話則說明客戶端要請求的是一個文件 
  if (ext) {
    // 根據(jù)傳入的目標文件路徑來讀取對應文件
    fs.readFile(filePathname, (err, data) => {
    // 錯誤處理
      if (err) {
        res.writeHead(404, { "Content-Type": "text/plain" })
        res.write("404 - NOT FOUND")
        res.end()
      } else {
        res.writeHead(200, { "Content-Type": mimeType })
        res.write(data)
        res.end()
      }
    });
    // 返回 true 表示, 客戶端想要的 是 靜態(tài)文件
    return true
  } else {
    // 返回 false 表示, 客戶端想要的 不是 靜態(tài)文件
    return false
  }
}

// 導出函數(shù)
module.exports = readStaticFile

5.2 server

/server.js

// 引入相關(guān)模塊
var http = require('http');
var url = require('url');
var path = require('path');
var readStaticFile = require('./modules/readStaticFile');

// 搭建 HTTP 服務器
var server = http.createServer(function(req, res) {
  var urlObj = url.parse(req.url);
  var urlPathname = urlObj.pathname;
  var filePathname = path.join(__dirname, "/public", urlPathname);

  // 讀取靜態(tài)文件
  readStaticFile(res, filePathname);
});

// 在 3000 端口監(jiān)聽請求
server.listen(3000, function() {
  console.log("服務器運行中.");
  console.log("正在監(jiān)聽 3000 端口:")
})

5.3 最終目錄結(jié)構(gòu)

[圖片上傳失敗...(image-f47052-1651418882033)]

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市祝辣,隨后出現(xiàn)的幾起案子贴妻,更是在濱河造成了極大的恐慌,老刑警劉巖蝙斜,帶你破解...
    沈念sama閱讀 218,284評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件揍瑟,死亡現(xiàn)場離奇詭異,居然都是意外死亡乍炉,警方通過查閱死者的電腦和手機绢片,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,115評論 3 395
  • 文/潘曉璐 我一進店門滤馍,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人底循,你說我怎么就攤上這事巢株”畋觯” “怎么了颜骤?”我有些...
    開封第一講書人閱讀 164,614評論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長腰埂。 經(jīng)常有香客問我祠挫,道長那槽,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,671評論 1 293
  • 正文 為了忘掉前任等舔,我火速辦了婚禮骚灸,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘慌植。我一直安慰自己甚牲,他們只是感情好,可當我...
    茶點故事閱讀 67,699評論 6 392
  • 文/花漫 我一把揭開白布蝶柿。 她就那樣靜靜地躺著丈钙,像睡著了一般。 火紅的嫁衣襯著肌膚如雪交汤。 梳的紋絲不亂的頭發(fā)上雏赦,一...
    開封第一講書人閱讀 51,562評論 1 305
  • 那天,我揣著相機與錄音芙扎,去河邊找鬼喉誊。 笑死,一個胖子當著我的面吹牛纵顾,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播栋盹,決...
    沈念sama閱讀 40,309評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼施逾,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了例获?” 一聲冷哼從身側(cè)響起汉额,我...
    開封第一講書人閱讀 39,223評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎榨汤,沒想到半個月后蠕搜,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,668評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡收壕,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,859評論 3 336
  • 正文 我和宋清朗相戀三年妓灌,在試婚紗的時候發(fā)現(xiàn)自己被綠了轨蛤。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,981評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡虫埂,死狀恐怖祥山,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情掉伏,我是刑警寧澤缝呕,帶...
    沈念sama閱讀 35,705評論 5 347
  • 正文 年R本政府宣布,位于F島的核電站斧散,受9級特大地震影響供常,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜鸡捐,卻給世界環(huán)境...
    茶點故事閱讀 41,310評論 3 330
  • 文/蒙蒙 一栈暇、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧闯参,春花似錦瞻鹏、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,904評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽赫悄。三九已至馏慨,卻和暖如春埂淮,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背写隶。 一陣腳步聲響...
    開封第一講書人閱讀 33,023評論 1 270
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留慕趴,地道東北人。 一個月前我還...
    沈念sama閱讀 48,146評論 3 370
  • 正文 我出身青樓冕房,卻偏偏與公主長得像躏啰,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子耙册,可洞房花燭夜當晚...
    茶點故事閱讀 44,933評論 2 355

推薦閱讀更多精彩內(nèi)容