Node.js課程知識講解大全(二)

二. 模塊化

Node.js所有的API都是基于模塊發(fā)布和使用的,因此在真正的學習Node.js之前照弥,我們需要先了解模塊化開發(fā)这揣。

2.1 模塊化的由來

JavaScript誕生初期,不像成熟的面向對象語言(如Java)擁有與生俱來的模塊系統(tǒng)机打,甚至沒有人把它當做一門真正的編程語言片迅。

JavaScript初期引入模塊的方式都是通過script標簽來引入代碼,但這樣很容易產(chǎn)生如下問題:

全局的變量污染芥挣,并且無法找到源頭耻台。

過多的script標簽會造成雜亂無章的代碼。

使用某個庫蹋砚,卻爆出沒有相關庫的依賴。

無法追蹤的錯誤和報錯位置娇豫。

為了解決JavaScript模塊化問題畅厢,程序員們一開始采用「命令空間」的方式來約束代碼框杜,讓看起來凌亂的編程現(xiàn)狀有所緩解。但是這僅僅屬于一種約定振劳,并沒有從技術上解決根本問題油狂。

2.2 CommonJS規(guī)范

Node.js首先采用了CommonJS規(guī)范,CommonJS并非一門新的API弱贼,也不是標準模塊系統(tǒng)磷蛹,只不過是目前Node.js使用最廣泛的模塊系統(tǒng)(規(guī)范)味咳。CommonJS 規(guī)范是為了解決 JavaScript 的作用域問題而定義的模塊形式,可以使每個模塊它自身的命名空間中執(zhí)行槽驶。

2.2.1 CommonJs 基本使用規(guī)則

該規(guī)范的主要內容是:

每個js文件就是一個模塊掂铐,模塊有自己的作用域。在一個文件里面定義的變量聂受、函數(shù)烤镐、類棍鳖,都是私有的,對其他文件不可見镜悉。

每個模塊內部侣肄,module變量(默認擁有)代表當前模塊。這個變量是一個對象吼具,它的exports屬性(即module.exports)是對外的接口矩距。加載某個模塊,其實是加載該模塊的module.exports屬性陡蝇。

模塊必須通過 module.exports 導出對外的變量或接口哮肚。

// moduleA.js

varx=5;

varaddX=function(value) {

returnvalue+x;

};

module.exports.x=x;

module.exports.addX=addX;

通過 require() 來導入其他模塊的輸出到當前模塊作用域中绽左。

// moduleB.js

varexample=require('./moduleA.js');

console.log(example.x);// 5

console.log(example.addX(1));// 6

CommonJS模塊的特點如下。

所有代碼都運行在模塊作用域戏蔑,不會污染全局作用域鲁纠。

模塊可以多次加載,但是只會在第一次加載時運行一次情龄,然后運行結果就被緩存了捍壤,以后再加載鹃觉,就直接讀取緩存結果。要想讓模塊再次運行祷肯,必須清除緩存。

模塊加載的順序翼闹,按照其在代碼中出現(xiàn)的順序蒋纬。

2.2.2 內部實現(xiàn)原理

正如上面所說,node執(zhí)行js文件時會默認把文件代碼封裝到一個module變量中法牲,它是Module的一個實例琼掠。

functionModule(id,parent) {

this.id=id;

this.exports={};

this.parent=parent;

// ...

}

每個模塊內部瓷蛙,都有一個module對象,代表當前模塊横堡。它有以下屬性:

module.id 模塊的識別符冠桃,通常是帶有絕對路徑的模塊文件名食听。

module.filename 模塊的文件名,帶有絕對路徑葬项。

module.loaded 返回一個布爾值迹蛤,表示模塊是否已經(jīng)完成加載。

module.parent 返回一個對象嚷量,表示調用該模塊的模塊逆趣。

module.children 返回一個數(shù)組汗贫,表示該模塊要用到的其他模塊。

module.exports 表示模塊對外輸出的值部蛇。

嘗試執(zhí)行以下代碼:

// example.js

varjquery=require('jquery');

exports.$=jquery;

console.log(module);

打印信息:

{id:'.',

exports: {'$': [Function] },

parent:null,

filename:'/path/to/example.js',

loaded:false,

children:

[ {id:'/path/to/node_modules/jquery/dist/jquery.js',

exports: [Function],

parent: [Circular],

filename:'/path/to/node_modules/jquery/dist/jquery.js',

loaded:true,

children: [],

paths: [Object] } ],

paths:

['/home/user/deleted/node_modules',

'/home/user/node_modules',

'/home/node_modules',

'/node_modules']

}

module.exports屬性表示當前模塊對外輸出的接口咐蝇,其他文件加載該模塊有序,實際上就是讀取module.exports變量。為了方便警绩,Node為每個模塊提供一個exports變量肩祥,指向module.exports混狠。這等同在每個模塊頭部疾层,有一行這樣的命令。

varexports=module.exports;

可以直接使用exports予弧,但注意湖饱,不能直接將exports變量指向一個值琉历,因為這樣等于切斷了exports與module.exports的聯(lián)系:

exports.area=function(r) {

returnMath.PI*r*r;

};

exports.circumference=function(r) {

return2*Math.PI*r;

};

exports=function(x) {console.log(x)};//這樣做不行,因為相當于在文件內部定義一個私有變量了彪置,通過require無法拿到

CommonJS加載是同步的蝇恶,因此使用CommonJS無法做到按需加載撮弧。

2.3 ES6中的模塊化

ES6模塊是ECMA組織從語言層面提出的標準模塊化API姚糊,未來完全可以取代CommonJS和其他的模塊化系統(tǒng)授舟。

2.3.1 基本使用方式

ES6模塊的語法完全不同于CommonJS释树,因為ES6 模塊不是對象,而是通過export命令顯式指定導出的代碼秸仙,再通過import命令導入:

// person.js

exportdefaultconstperson={}

exportage=30

使用

importperson,{age}from'person'

2.3.2 模塊的加載原理

import 方式導入模塊是基于“編譯時加載”或者靜態(tài)加載桩盲,即 ES6 可以在編譯時就完成模塊加載赌结,效率要比 CommonJS 模塊的加載方式高。

它具有如下優(yōu)勢:

基于編譯時加載襟交,可以讓模塊在編譯階段就能進行靜態(tài)語法分析伤靠,從而提前預警語法錯誤或者做類型校驗宴合。

未來Node.js和瀏覽器都將支持,可以一統(tǒng)天下贞言。

ES6模塊默認在嚴格模式下執(zhí)行阀蒂,即默認在模塊頭部加入:"use strict"。

## 附:嚴格模式主要有以下限制酗失。

- 變量必須聲明后再使用

- 函數(shù)的參數(shù)不能有同名屬性昧绣,否則報錯

- 不能使用`with`語句

- 不能對只讀屬性賦值,否則報錯

- 不能使用前綴 0 表示八進制數(shù)删壮,否則報錯

- 不能刪除不可刪除的屬性兑牡,否則報錯

- 不能刪除變量`delete prop`,會報錯发绢,只能刪除屬性`delete global[prop]`

- `eval`不會在它的外層作用域引入變量

- `eval`和`arguments`不能被重新賦值

- `arguments`不會自動反映函數(shù)參數(shù)的變化

- 不能使用`arguments.callee`

- 不能使用`arguments.caller`

- 禁止`this`指向全局對象

- 不能使用`fn.caller`和`fn.arguments`獲取函數(shù)調用的堆棧

- 增加了保留字(比如`protected`边酒、`static`和`interface`)

尤其注意:頂層的this指向undefined狸窘,不應該在頂層代碼使用this翻擒。

2.3.3 詳解import和export命令

模塊功能主要由兩個命令構成:export和import。

export命令用于規(guī)定模塊的對外接口劳吠。

一個模塊就是一個獨立的文件巩趁。該文件內部的所有變量议慰,外部無法獲取,除非通過export導出:

// profile.js

exportvarfirstName='Michael';

exportvarlastName='Jackson';

exportvaryear=1958;

或者寫成:

// profile.js

varfirstName='Michael';

varlastName='Jackson';

varyear=1958;

?

export{firstName,lastName,yearasage};

export 中的as關鍵字可以重命名導出的變量名草讶。

export無法直接導出值或變量:

// 報錯

export1;

// 報錯

varm=1;

exportm;

?

// 下面是正確的寫法

?

// 寫法一

exportvarm=1;

// 寫法二

varm=1;

export{m};

// 寫法三

varn=1;

export{nasm};

export必須在頂層作用域使用炉菲,無法在其他塊級作用域使用:

functionfoo() {

exportdefault'bar'// 語法錯誤

}

foo()

export default 命令可以導出默認的變量拍霜,import使用時可以指定任意名字

exportdefaultfunctionabc(){}

或者

functionabc(){}

export{abcasdefault}

使用

importcustomNamefrom'./export-default.js'

或者

import{defaultasabc}from'./export-default.js'

import`命令用于輸入其他模塊提供的功能。

export導出的接口都通過import來導入屿讽,import中也可以通過as對導入的變量進行重命名伐谈。

import{lastNameassurname}from'./profile.js';

import導入的變量都是只讀的,無法修改抠蚣。

import{a}from'./xxx.js'

?

a={};// Syntax Error : 'a' is read-only;

import 會導致導入的模塊自動加載執(zhí)行履澳,并且多次重復執(zhí)行同一句import語句距贷,只會執(zhí)行一次:

import'lodash';

import'lodash';//這句就不執(zhí)行了

模塊整體加載可以使用*號:

import*aspersonfromperson

import()方法的動態(tài)加載

import命令是基于編譯時加載,如果想使用運行時加載該怎么辦现横?

可以使用全局的import()方法阁最,注意:該方法是ES6提案中引入的方法速种,和import命令完全不是一碼事。

import()方法支持傳入一個模塊路徑贩据,返回一個Promise闸餐,在then方法中可以拿到模塊的引用舍沙。

import('./dialogBox.js')

.then(dialogBox=>{

dialogBox.open();

? })

.catch(error=>{

/* Error handling */

? })

2.4 其他模塊化解決方案

除了CommonJS被Node.js定為官方模塊化標準,以及ES6 Module被ECMA組織定為瀏覽器支持的原生標準外壹无,在模塊化探索年代還產(chǎn)生過一些其他的模塊化規(guī)范感帅,不過隨著時間的流逝失球,這些都被人們廢棄了帮毁。

2.4.1 AMD規(guī)范

require.js實現(xiàn)了AMD規(guī)范的

在ES6模塊化方案提出之前豺撑,瀏覽器實現(xiàn)異步加載模塊聪轿,都是基于AMD規(guī)范來實現(xiàn),其寫法如下:

// lib模塊

define(['package/lib'],function(lib){

functionhello(){

lib.log('hello world!');

? }

?

return{

foo:foo

? };

});

使用模塊:

require(['lib'],function(lib) {

lib.foo()

});

AMD規(guī)范必須一次性把所有依賴加載完畢灯抛,也無法做到按需加載对嚼,但可以做到異步加載外莲。

2.4.2 CMD規(guī)范

sea.js實現(xiàn)了CMD規(guī)范

CMD規(guī)范是AMD規(guī)范的一個變種偷线,為了讓AMD規(guī)范兼容CommonJS的風格沽甥。

define(function(require,exports,module) {

var$=require('jquery');

require.async(['./b','./c'],function(b) {

b.doSomething();

c.doSomething();

? });

exports.doSomething=...

module.exports=...

})

使用模塊也是按照如上的方式使用摆舟。

CMD相比AMD可以做到異步加載亥曹,但目前也幾乎每人使用,因為書寫太麻煩恨诱,而且ES6已經(jīng)把模塊規(guī)范化媳瞪。

三. Node.js常用的內置模塊

Node.js中提供了一些原生的模塊,我們稱之為內置模塊照宝。此外蛇受,也可以通過NPM命令安裝第三方模塊,安裝完畢后使用方式和內置模塊沒有什么不同厕鹃。

Node.js中的原生模塊直接通過require就能獲得,參考Node.js API文檔來使用這些原生模塊剂碴。接下來把将,針對一些常用模塊進行介紹。

3.1 fs模塊

fs 模塊以 POSIX 標準函數(shù)的方式提供了與文件系統(tǒng)進行交互的API忆矛,fs中的每個方法都分同步和異步兩種方式調用察蹲。通過fs模塊可以獲取一個目錄結構,及其子目錄或目錄樹下文件的特征,甚至可以讀寫递览、復制或者刪除文件叼屠。

什么是POSIX標準

可移植操作系統(tǒng)接口(Portable Operating System Interface of UNIX,縮寫為 POSIX )绞铃,POSIX標準定義了操作系統(tǒng)應該為應用程序提供的接口標準镜雨,保證了接口的可移植。

fs模塊中操作文件或文件夾的方式可以基于:同步儿捧、異步回調或者異步Promise三種模式荚坞。還記得Promise嗎?

文件夾讀取示例三種方式

constfs=require('fs')

// 使用同步的方式

constdirent=fs.readdirSync(__dirname)

console.log(dirent)

// 使用異步回調的方式

fs.readdir(__dirname, (err,dirent)=>{

if(err) {

console.error(err)

}else{

console.log(dirent)

?? }

})

// 使用Promise的方式菲盾,目前還不穩(wěn)定

fs.promises.readdir(__dirname).then((dirent)=>{

console.log(dirent)

}).catch((err)=>{

console.error(err)

})

和文件系統(tǒng)有關的全局變量

__dirname是Node.js提供的全局變量颓影,表示當前正在執(zhí)行的代碼文件所在的目錄絕對路徑。

__filename表示當前正在執(zhí)行的代碼文件的絕對路徑懒鉴。

注意以上變量和執(zhí)行環(huán)境是無關的诡挂,只和代碼文件的保存位置有關。

在不同路徑的js文件中加入下面代碼临谱,通過node命令執(zhí)行看看

console.log(__dirname)

console.log(__filename)

3.1.1 文件夾的操作

fs.access():檢查文件或文件夾是否存在璃俗。

fs.readdir():讀取文件夾。

fs.mkdir():創(chuàng)建文件夾悉默,如果文件夾已存在會導致錯誤城豁。

fs.rmdir():刪除文件夾,如果文件夾內部有子文件會導致錯誤抄课。

fs.rename():修改文件或文件夾名字唱星。

constfs=require('fs')

// 以utf-8格式讀取目錄的子文件名字

fs.readdir('/Users', {encoding:'utf-8'}, (err,files)=>{

if(err) {

console.error(err)

}else{

console.log(files)

?? }

})

// 檢查文件是否存在

fs.access(__dirname+'/temp',fs.constants.F_OK, (err)=>{

if(err) {

// 報錯表示不存在

// 創(chuàng)建文件夾

fs.mkdir(__dirname+'/temp', (err)=>{

if(err) {

console.error(err)

}else{

setTimeout(()=>{

// 2秒之后修改文件夾名為temp2

fs.rename(__dirname+'/temp',__dirname+'/temp2', (err)=>{

if(err)console.error(err)

? ? ? ? ? ? ? ? ?? })

},2000);

? ? ? ? ?? }

?

? ? ?? })

}else{

// 刪除文件夾

fs.rmdir(__dirname+'/temp', (err)=>{

if(err) {

console.error(err)

? ? ? ? ?? }

? ? ?? })

?? }

})

3.1.2 文件的操作

fs.writeFile():讀取或者創(chuàng)建文件。

fs.unlink():刪除文件及其文件鏈接(快捷方式)跟磨。

constfs=require('fs')

// 新建文件

fs.writeFile(__dirname+'/text.txt','', (err)=>{

if(err)throwerr

setTimeout(()=>{

// 刪除文件

fs.unlink(__dirname+'/text.txt', (err)=>{

if(err)throwerr

? ? ?? })

},3000);

})

3.1.3 文件屬性的獲取

通過fs.lstat()方法可以讀取文件屬性间聊,文件屬性保存在一個fs.Stats對象里面。

constfs=require('fs')

// 獲取文件屬性

fs.lstat(__filename, (err,stats)=>{

if(err)throwerr

if(stats.isFile) {

console.log(`${__filename}是文件,創(chuàng)建于${stats.birthtime},大小${stats.size}`)

?? }

})

3.1.4 大文件的讀寫

對于比較大型的文件(如視頻文件)抵拘,拷貝文件或讀取文件是分塊的以流的方式來讀取的哎榴,因為直接讀取整個文件會很容易超過內存容量。

const stream = fs.createReadStream('c:\\demo\1.txt');

let data = ''

stream.on('data', (trunk) => {

? data += trunk;

});

stream.on('end', () => {

? console.log(data);

});

文件流的讀寫比較復雜仑濒,推薦直接使用第三方庫:cp-file

3.2 path模塊

path模塊也是Node.js中經(jīng)常使用的模塊叹话,主要用于路徑的處理,由于Windows和Unix路徑的格式有差異墩瞳,因此在不同平臺上調用的方法可能得到只針對平臺的路徑驼壶。盡量不要使用和平臺相關的path API。

path.dirname():返回所在目錄名喉酌。

path.dirname('/foo/bar/baz/asdf/quux');

// 返回: '/foo/bar/baz/asdf'

path.extname:返回擴展名热凹。

path.extname('index.html');

// 返回: '.html'

path.join:可以使用平臺特定的分隔符作為定界符將所有給定的 path 片段連接在一起泵喘,然后規(guī)范化生成的路徑。

path.join('/foo','bar','baz/asdf','quux','..');

// 返回: '/foo/bar/baz/asdf'

path.normalize() :方法規(guī)范化給定的 path般妙,解析 '..' 和 '.' 片段纪铺。

path.normalize('C:\\temp\\\\foo\\bar\\..\\');

// 返回: 'C:\\temp\\foo\\'

path.resolve() 方法將路徑或路徑片段的序列解析為絕對路徑。

規(guī)則:

給定的路徑序列從右到左進行處理碟渺,每個后續(xù)的 path 前置鲜锚,直到構造出一個絕對路徑。

如果在處理完所有給定的 path 片段之后還未生成絕對路徑苫拍,則再加上當前工作目錄芜繁。

生成的路徑已規(guī)范化,并且除非將路徑解析為根目錄绒极,否則將刪除尾部斜杠骏令。

constpath=require('path')

console.log(path.resolve('/foo','/bar','./baz'))

// '/bar/baz'

console.log(path.resolve('/foo/bar','./baz'))

// '/foo/bar/baz'

console.log(path.resolve('foo/bar','./baz/'))

// '/<當前執(zhí)行路徑>/foo/bar/baz'

3.4 url模塊

url模塊主要處理URL(統(tǒng)一資源定位符),URL的格式構成:

協(xié)議+認證+主機+端口+路徑+查詢字符串+哈希值垄提。

可以參照下圖:

url模塊主要使用以下兩個API接口:

url.format():負責把一個url的json對象轉換成合法的url地址高职。

url.parse():負責把一個url地址轉換成一個url的json對象膛腐。

consturl=require('url')

constpath=url.format({

protocol:'https',

hostname:'example.com',

pathname:'/some/path',

query: {

page:1,

format:'json'

?? }

});

console.log(path)

// => 'https://example.com/some/path?page=1&format=json'

console.log(url.parse(path))

// Url {

// ? protocol: 'https:',

// ? slashes: true,

// ? auth: null,

// ? host: 'example.com',

// ? port: null,

// ? hostname: 'example.com',

// ? hash: null,

// ? search: '?page=1&format=json',

// ? query: 'page=1&format=json',

// ? pathname: '/some/path',

// ? path: '/some/path?page=1&format=json',

// ? href: 'https://example.com/some/path?page=1&format=json' }

3.5 querystring模塊

通過url.parse轉換的url對象中的query對象是一個字符串浪规,如果想進一步拿到查詢字符串的鍵值對招刹,需要再通過querystring來轉換。

querystring.stringify():可以把查詢字符串對象轉換成字符串高蜂。querystring.stringify默認會對非ASCII字符進行百分比編碼聪黎,即內部調用querystring.escape() 方法罕容。

querystring.parse():可以把查詢字符串轉換成對象备恤。querystring.parse默認會對經(jīng)過百分比編碼的字符進行解碼操作,即內部調用了querystring.unescape() 锦秒。

constqs=require('querystring')

conststr=qs.stringify({

name:'小明',

age:30,

description: ['動物','人']

})

console.log(str)

// name=%E5%B0%8F%E6%98%8E&age=30&description=%E5%8A%A8%E7%89%A9&description=%E4%BA%BA

constobj=qs.parse(str)

console.log(obj)

// [Object: null prototype] { name: '小明', age: '30', description: ['動物', '人'] }

3.6 http模塊

http/https模塊是Node.js中和網(wǎng)絡請求相關的核心模塊露泊,類似瀏覽器中的ajax,但是它除了可以請求數(shù)據(jù)旅择,還可以通過它搭建HTTP(S)服務惭笑。

接下來,我們使用http模塊模擬一個爬蟲案例生真。

什么是爬蟲沉噩?

對于一個 害怕蟲子的開發(fā)者來說,估計選擇爬蟲行業(yè)可以是一個磨煉意志的機會柱蟀。

爬蟲就是一段自動抓取互聯(lián)網(wǎng)信息的程序川蒙,從互聯(lián)網(wǎng)上抓取對于我們有價值的信息。

事實上长已,搜索引擎就是一個巨型的爬蟲畜眨,它可以輔助我們完成信息的檢索和歸類昼牛,讓我們以最快的速度找到我們最想要的信息。

接下來康聂,我們只是簡單地使用https模塊和第三方cheer.io模塊來爬去某一個網(wǎng)站的有用信息贰健。

因為目前大部分網(wǎng)站都是基于https協(xié)議的,所以就是用https模塊恬汁,它和http協(xié)議最大的區(qū)別就是https會對請求和資源加密解密伶椿。

編寫http代碼,實現(xiàn)資源獲取氓侧。

varhttp=require('http')

varhttps=require('https')

functiongetUrlResource(url,callback) {

varclient

if(url.startsWith('https')) {

client=https

}elseif(url.startsWith('http')) {

client=http

}else{

callback(newError('只能請求http/https協(xié)議的內容'))

return;

?? }

client.get(url, (res)=>{

const{statusCode}=res;

constcontentType=res.headers['content-type'];

?

leterror;

if(statusCode!==200) {

error=newError('請求失敗\n'+

`狀態(tài)碼: ${statusCode}`);

}elseif(!/^text\/html/.test(contentType)) {

error=newError('無效的 content-type.\n'+

`期望的是 text/html 但接收到的是 ${contentType}`);

? ? ?? }

if(error) {

// 消費響應數(shù)據(jù)來釋放內存悬垃。

res.resume();

callback(error)

return;

? ? ?? }

?

res.setEncoding('utf8');

letrawData='';

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

res.on('end', ()=>{

callback(null,rawData)

? ? ?? });

}).on('error', (e)=>{

callback(e)

?? });

}

編寫爬蟲代碼

varcheerio=require('cheerio')

getUrlResource('https://www.baidu.com/',function(e,data) {

if(e) {

console.error(e)

return

?? }

?

var$=cheerio.load(data)

var$imgs=$('img[src]')

if($imgs.length===0) {

console.log('沒有找到圖片資源')

return

?? }

// 注意執(zhí)行get才能拿到數(shù)組

varimgSrcValues=$imgs.map(function(i,el) {

return$(el).attr('src');

}).get()

varimgUrls=imgSrcValues.join(',\n');

console.log(imgUrls)

})

四. Express框架

Express 是Node.js開發(fā)中使用最廣泛的服務器框架,通過它可以快速的搭建一個Web容器并向外界提供Web服務甘苍。

Express基于Node.js的http/https搭建服務器尝蠕,通過中間件的即插即用方式來擴展功能。

4.1 基本使用

創(chuàng)建一個項目文件夾载庭,命名為express-demo并通過cd命令進入到express-demo目錄看彼。

使用npm init -y初始化項目,生成package.json項目描述文件囚聚。

使用npm install -save express安裝express靖榕。

創(chuàng)建一個index.js文件,寫入以下代碼訪問瀏覽器即可查看效果顽铸。

constexpress=require('express')

constapp=express()

app.get('/', (req,res)=>res.send('你好!'))

app.listen(3000, ()=>{

console.log('服務器創(chuàng)建完畢,監(jiān)聽3000端口')

})

打開終端茁计,在express-demo目錄下執(zhí)行:

nodeindex.js。

此外谓松,如果希望不在console中調試星压,而是在Chrome中通過dev tools調試,可以使用:

node--inspectindex.js

然后打開開發(fā)者工具鬼譬,里面有個Node圖標娜膘,點擊即可調試。

假如你希望每一次修改代碼都自動重啟Express优质,可以全局安裝nodemon竣贪,它和node的命令幾乎一樣。

npm install -g nodemon

nodemon --inspect index.js

打開瀏覽器巩螃,輸入:http://本機ip/ 查看效果演怎。

為了方便自動的檢測本機ip和自動打開瀏覽器測試,可以安裝兩個第三方npm包:openaddress避乏,portFinder爷耀。然后服務器啟動成功之后可以自動打開瀏覽器測試。

npminstall-saveopen address portfinder

constexpress=require('express')

consturl=require('url')

constopen=require('open')

constaddress=require('address')

constportfinder=require('portfinder')

constapp=express()

app.get('/', (req,res)=>res.send('你好!'))

// 防止3000端口被占用

portfinder.getPort({

port:3000,// minimum port

stopPort:3333// maximum port

},function(err,port){

if(err){

thrownewError('沒有找到可以啟動的端口')

? }

app.listen(port, ()=>{

console.log('服務器創(chuàng)建完畢,監(jiān)聽'+port+'端口')

? // 自動打開Chrome瀏覽器加載網(wǎng)頁

open(url.format({

protocol:'http',

port:port,

hostname:address.ip(),pathname:'/'

}), {app: ['google chrome','--incognito'] })

? });

});

可以把node index.js命令加入到package.json的scripts字段里面淑际,通過npm start啟動畏纲。

{

"name":"express-demo",

"main":"index.js",

"scripts": {

"start":"node index.js"

? },

"dependencies": {

"address":"^1.1.2",

"express":"^4.17.1",

"open":"^7.0.0"

? }

}

npmstart

?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末扇住,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子盗胀,更是在濱河造成了極大的恐慌艘蹋,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,968評論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件票灰,死亡現(xiàn)場離奇詭異女阀,居然都是意外死亡,警方通過查閱死者的電腦和手機屑迂,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,601評論 2 382
  • 文/潘曉璐 我一進店門浸策,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人惹盼,你說我怎么就攤上這事庸汗。” “怎么了手报?”我有些...
    開封第一講書人閱讀 153,220評論 0 344
  • 文/不壞的土叔 我叫張陵蚯舱,是天一觀的道長。 經(jīng)常有香客問我掩蛤,道長枉昏,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,416評論 1 279
  • 正文 為了忘掉前任揍鸟,我火速辦了婚禮兄裂,結果婚禮上,老公的妹妹穿的比我還像新娘阳藻。我一直安慰自己晰奖,他們只是感情好,可當我...
    茶點故事閱讀 64,425評論 5 374
  • 文/花漫 我一把揭開白布稚配。 她就那樣靜靜地躺著畅涂,像睡著了一般港华。 火紅的嫁衣襯著肌膚如雪道川。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,144評論 1 285
  • 那天立宜,我揣著相機與錄音冒萄,去河邊找鬼。 笑死橙数,一個胖子當著我的面吹牛尊流,可吹牛的內容都是我干的。 我是一名探鬼主播灯帮,決...
    沈念sama閱讀 38,432評論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼崖技,長吁一口氣:“原來是場噩夢啊……” “哼逻住!你這毒婦竟也來了?” 一聲冷哼從身側響起迎献,我...
    開封第一講書人閱讀 37,088評論 0 261
  • 序言:老撾萬榮一對情侶失蹤瞎访,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后吁恍,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體扒秸,經(jīng)...
    沈念sama閱讀 43,586評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 36,028評論 2 325
  • 正文 我和宋清朗相戀三年冀瓦,在試婚紗的時候發(fā)現(xiàn)自己被綠了伴奥。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,137評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡翼闽,死狀恐怖拾徙,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情感局,我是刑警寧澤锣吼,帶...
    沈念sama閱讀 33,783評論 4 324
  • 正文 年R本政府宣布,位于F島的核電站蓝厌,受9級特大地震影響玄叠,放射性物質發(fā)生泄漏。R本人自食惡果不足惜拓提,卻給世界環(huán)境...
    茶點故事閱讀 39,343評論 3 307
  • 文/蒙蒙 一读恃、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧代态,春花似錦寺惫、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,333評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至歉摧,卻和暖如春艇肴,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背叁温。 一陣腳步聲響...
    開封第一講書人閱讀 31,559評論 1 262
  • 我被黑心中介騙來泰國打工再悼, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人膝但。 一個月前我還...
    沈念sama閱讀 45,595評論 2 355
  • 正文 我出身青樓冲九,卻偏偏與公主長得像,于是被迫代替她去往敵國和親跟束。 傳聞我的和親對象是個殘疾皇子莺奸,可洞房花燭夜當晚...
    茶點故事閱讀 42,901評論 2 345

推薦閱讀更多精彩內容