Nodejs知識點

Nodejs基礎(chǔ)部分

為什么要學(xué)習(xí)Node耿眉?

  • Node使用Js語法去開發(fā)后端應(yīng)用
  • 一些公司要求前端掌握Node開發(fā),公司業(yè)務(wù)已經(jīng)在用Node開發(fā)
  • Node生態(tài)系統(tǒng)比較活躍媳否,有大量的開源庫可以使用
  • 現(xiàn)在又很多的前端開發(fā)工具大多都是基于Node開發(fā)的

Node是什么?

  • Node是一個基于Chrome V8引擎的Javascript代碼運行環(huán)境

  • 瀏覽器(軟件)能夠運行Javascript代碼,瀏覽器就是Javascript代碼的運行環(huán)境

  • Node(軟件)能夠運行Javascript代碼士嚎,Node就是Javascript代碼的運行環(huán)境

  • 在瀏覽器中全局對象是window,在node中全局對象是global

  • global對象下面同樣也有console.log\setTimeout等方法悔叽,只不過和window重名莱衩,并不是window的方法

Node運行環(huán)境搭建

1. 安裝

  • Node官方推薦大家使用Node穩(wěn)定版本(LTS版本)
  • NodeJs官網(wǎng)
  • 可以使用系統(tǒng)命令行工具powershell(在開始菜單搜索powershell即可打開)查看當前node安裝版本

2. 安裝失敗解決方法

1. 錯誤代號 2502、2503

失敗原因:系統(tǒng)賬戶權(quán)限不足

解決:

以管理員身份運行powershell命令行工具

輸入運行安裝包命令 msiexec /package (node安裝包位置路徑\安裝包名字)

比如msiexec /package C:\Node\node\node-v10.13.0-x64.msi

2. 執(zhí)行命令報錯

命令行中輸入 node -v 報錯

失敗原因: Node安裝目錄寫入環(huán)境變量失敗

解決:將Node安裝目錄手動添加到環(huán)境變量娇澎,配置完畢重啟命令行窗口

PATH環(huán)境變量:存儲系統(tǒng)中的目錄笨蚁,在命令行中執(zhí)行命令的時候系統(tǒng)會自動去這些目錄中查找命令的位置

Nodejs入門

1. Nodejs的組成

  • Javascript 由三部分組成: ECMAScript、DOM、BOM
  • Nodejs是由ECMAScript及Node環(huán)境提供的一些附加API組成括细,包括文件伪很、網(wǎng)絡(luò)、路徑等等一些更加強大的API

2. Nodejs基礎(chǔ)語法

  • 所有的ECMAScript語法在Node環(huán)境中都可以使用奋单,也就是說Js中變量聲明锉试,循環(huán),條件語句等等览濒,在Node環(huán)境下都可以使用

小技巧

  • 找到要打開的文件目錄呆盖,按住shift鍵,點擊右鍵贷笛,會出現(xiàn)‘在此處打開powershell窗口’应又,可以直接在命令行工具中打開想要執(zhí)行的文件
  • 在命令行工具中執(zhí)行node命令時,如果文件名過長乏苦,我們只需要輸入文件名的前幾個字符株扛,然后按下Tab鍵,命令行工具會自動補全我們要打開的文件名邑贴,以及文件的路徑席里。
  • 之前執(zhí)行過的命令只需要按鍵盤的上鍵就可以輸出執(zhí)行過的命令行
  • 命令行中輸入clear然后按回車,所有之前輸出的命令都被清除掉了

Nodejs模塊化開發(fā)

1. 開發(fā)規(guī)范

  • Nodejs規(guī)定一個Javascript文件就是一個模塊拢驾,模塊內(nèi)部定義的變量和函數(shù)默認情況下外部無法得到
  • 模塊內(nèi)部可以使用exports對象進行成員導(dǎo)出奖磁,使用require方法進行導(dǎo)入其他模塊

2. 成員導(dǎo)出

var version = 1.0;
const sayHi = name => `你好,${ name }`;

exports.version = version;
exports.sayHi = sayHi;

3. 成員導(dǎo)入

let a = require('./b.js');
// 導(dǎo)入時后綴可以去掉

console.log(a.version)
console.log(a.sayHi('zhangsan'));

4. 成員導(dǎo)出的另一種方式

module.exports.version = version;
module.exports.sayHi = sayHi;

exportsmodule.exports 的別名(地址引用關(guān)系)繁疤,導(dǎo)出對象最終以 module.exports 為準

Nodejs系統(tǒng)模塊

系統(tǒng)模塊: Node運行環(huán)境提供的api咖为,因為這些api都是以模塊化的方式開發(fā)的,所以我們又稱Node運行環(huán)境提供的api為系統(tǒng)模塊

一稠腊、 fs模塊(文件操作系統(tǒng))

二躁染、 path模塊 (路徑操作)

1. 路徑拼接

為什么要進行路徑拼接?

  • 不同的操作系統(tǒng)路徑的分隔符不統(tǒng)一
  • /public/uploads/a
  • Windows上是\
  • Linux上是/
  • 我們現(xiàn)在寫的代碼有可能要運行在Linux操作系統(tǒng)的架忌,因為Linux通常被用作運行網(wǎng)站的服務(wù)器

比如網(wǎng)站中的頭像上傳功能吞彤,用戶上傳的頭像文件實際上是要存儲在服務(wù)器硬盤的某個文件夾中的,那么我們在程序文件中要想找到這個文件夾叹放,就必須要拼接這個文件夾的路徑饰恕,我們就需要使用系統(tǒng)模塊path,它在內(nèi)部會判斷你當前使用的操作系統(tǒng)是什么井仰,然后使用操作系統(tǒng)對應(yīng)的路徑分隔符進行拼接埋嵌。

語法: path.join('路徑', '路徑', ...)

const path = require('path');

const finaPath = path.join('public', 'uploads', 'a');
console.log(finaPath)

相對路徑VS絕對路徑

  • 大多數(shù)時候使用絕對路徑,因為相對路徑有時候相對的是命令行工具的當前工作目錄
  • 在讀取文件或設(shè)置文件路徑時都會選擇絕對路徑
  • 使用__dirname獲取當前文件所在的絕對路徑
fs.readFile(path.join(__dirname, 'demo'), 'utf-8', (err, doc) => {
    if(err == null) {
      console.log(doc)
    }
})

Nodejs第三方模塊

什么是第三方模塊俱恶?

別人寫好的雹嗦,具有特定功能的范舀,我們能直接使用的模塊就是第三方模塊,由于第三方模塊通常都是由多個文件組成并且被放置在一個文件夾中了罪,所有又稱為包锭环。

1. 獲取第三方模塊

npmjs.com: 第三方模塊的存儲和分發(fā)倉庫

npm(node package manager): node的第三方模塊管理工具

  • 下載:npm install 模塊名稱
  • 卸載:npm uninstall 模塊名稱

全局安裝和本地安裝

  • 命令行工具:全局安裝
  • 庫文件:本地安裝

2. nodemon 第三方模塊

下載:

npm install nodemon -g

  • nodemon是一個命令行工具,用于輔助開發(fā)

  • 在Nodejs中泊藕,每次修改文件都要在命令行中重新執(zhí)行該文件田藐,非常繁瑣

  • nodemon能夠在文件修改后自動執(zhí)行

3. nrm 第三方模塊

  1. 下載

npm install -g nrm

  • 這個模塊的主要作用就是切換第三方模塊的下載地址
  • 因為npm的下載地址在國外,下載過程時間往往比較長吱七,使用nrm ls命令可以查看當前可切換的所有地址
  • 然后我們使用命令 nrm use taobao就可以切換下載地址為淘寶鏡像
  • 我們重新運行使用npm install ***的時候汽久,會發(fā)現(xiàn)下載速度明顯提升,主要原因就是切換了下載地址為國內(nèi)的淘寶鏡像

4. Gulp 第三方模塊

基于node平臺開發(fā)的前端構(gòu)建工具

  • 項目上線踊餐,html景醇、css、js合并壓縮
  • 語法轉(zhuǎn)換(less吝岭、es6...)
  • 公共文件抽離
  • 修改文件瀏覽器自動刷新
gulp使用
  1. 庫文件下載:

npm install gulp

  1. 項目的根目錄下建立gulpfile.js文件三痰,這個文件名是固定的不能更改

  2. 重構(gòu)項目的文件夾結(jié)構(gòu),src 目錄放置源代碼窜管,dist 目錄放置構(gòu)建后文件

  3. gulpfile.js 中編寫任務(wù)

  4. 在命令行工具中執(zhí)行 gulp 任務(wù)

gulp 提供的方法
  • gulp.src() 獲取任務(wù)要處理的文件
  • gulp.dest() 輸出文件
  • gulp.task() 建立gulp任務(wù)
  • gulp.watch() 監(jiān)聽文件變化
  • gulp.pipe()執(zhí)行函數(shù)
gulp 命令行工具

命令行工具下載:

npm install gulp-cli -g

gulp 插件

gulp本身屬于輕內(nèi)核的第三方模塊散劫,所提供的方法只有上面四種,所有的其他功能都是通過插件的方式實現(xiàn)的幕帆。

比如:

  • gulp-htmlmin:html文件壓縮
  • gulp-csso:壓縮css
  • gulp-babel:Javascript語法轉(zhuǎn)化
  • gulp-less:less語法轉(zhuǎn)化
  • gulp-uglify:壓縮混淆Javascript
  • gulp-file-include:公共文件包含
  • browsersync:瀏覽器實時同步
var gulp = require('gulp');//基礎(chǔ)庫
// 引入我們的gulp組件
/*html*/
var sass            = require('gulp-ruby-sass'),            // CSS預(yù)處理/Sass編譯
    cssmin = require('gulp-minify-css'),                    //壓縮css
    useref = require('gulp-useref'),//
    rev = require('gulp-rev'),
    htmlmin =require('gulp-htmlmin'),
    revReplace = require('gulp-rev-replace'),
    revCollector = require('gulp-rev-collector'),    //gulp-rev插件 用于html更改版本后的路徑替換
    uglify      = require('gulp-uglify'),           // JS文件壓縮
    eslint      = require('gulp-eslint'),
    imagemin        = require('gulp-imagemin'),     // imagemin 圖片壓縮
   // pngquant      = require('imagemin-pngquant'), // imagemin 深度壓縮
    rename          = require('gulp-rename'),           // 文件重命名
    changed         = require('gulp-changed'),          // 只操作有過修改的文件
    concat          = require("gulp-concat"),       // 文件合并
    clean           = require('gulp-clean');                // 文件清理
     sourcemaps = require('gulp-sourcemaps');
var livereload = require('gulp-livereload'),//網(wǎng)頁自動刷新
    webserver =require('gulp-webserver');//本地服務(wù)器
var   usemin = require('gulp-usemin');
var babel = require('gulp-babel');//配置es6
 var    plumber = require('gulp-plumber'); //輸出錯誤日志
var sequence = require('gulp-sequence');
/* 全局設(shè)置
* -------------------------------*/
var srcPath ={
  html: 'src',
  sass: 'src/sass',
  css:'src/css',
  script:'src/js',
  image: 'src/images',
  json: 'src/json'
};

var destPath = {
  html: 'dist',
  css: 'dist/css',
  script: 'dist/js',
  image: 'dist/images'
};

 /*html 樣式  圖片 js處理*/
 //更換html里面的路徑
gulp.task('htmlmin',function(){
    var opations ={
        removeComments: true,//清除HTML注釋
        collapseWhitespace: true,//壓縮HTML
        collapseBooleanAttributes: true,//省略布爾屬性的值 <input checked="true"/> ==> <input />
        removeEmptyAttributes: true,//刪除所有空格作屬性值 <input id="" /> ==> <input />
        //removeScriptTypeAttributes: true,//刪除<script>的type="text/javascript"
        //removeStyleLinkTypeAttributes: true,//刪除<style>和<link>的type="text/css"
        //minifyJS: true,//壓縮頁面JS
        minifyCSS: true//壓縮頁面CSS
    };
    gulp.src(destPath.html)
        .pipe(htmlmin(opations))
        .pipe(gulp.dest(destPath.html))
});

// 清理文件
gulp.task('clean', function() {
    return gulp.src( [destPath.css+'/*.css',srcPath.css+'/*.css'], {read: false} ) // 清理maps文件
        .pipe(clean());
});

gulp.task('rev',function(){
    return gulp.src(['src/json/rev-manifest.json',srcPath.html+'/**/*.html'])
        .pipe( revCollector({
             replaceReved: true
         }) )
     .pipe(changed( destPath.html ))
     .pipe(gulp.dest( destPath.html ));

});

gulp.task("sass",function(){
    return sass('src/sass/**/*.sass',{style: 'compact', sourcemap: true})
            .on('error', function(err){
                console.error('Error!',err.message);//顯示編譯錯誤信息
            })
        .pipe(sourcemaps.init())//地圖存放
        .pipe(rename({ suffix: '.min' })) // 重命名
        .pipe(gulp.dest('src/css'))
});

gulp.task('cssmin', function () {
    gulp.src('src/css/*.css')
        .pipe(cssmin())
        .pipe(rev())
        .pipe(gulp.dest('dist/css'))
        .pipe(rev.manifest())
        .pipe(gulp.dest(srcPath.json));
});

gulp.task('sequence', function (done) {
    sequence(
            'clean',['sass','cssmin','rev'],done)
});

gulp.task('eslint',function(){
    return gulp.src([srcPath.script+'/!*.js','!node_modules/!**'])
        //.pipe(eslint({configFile: 'eslintrc.json'}))
        .pipe(eslint.format())
        .pipe(eslint.failAfterError());
});

gulp.task('script', function() {
    return gulp.src( [srcPath.script+'/*.js'] ) // 指明源文件路徑获搏、并進行文件匹配,排除 .min.js 后綴的文件
        .pipe(changed( destPath.script )) // 對應(yīng)匹配的文件
        .pipe(sourcemaps.init()) // 執(zhí)行sourcemaps
        .pipe(rename({ suffix: '.min' })) // 重命名
        .pipe(uglify()) // 使用uglify進行壓縮失乾,并保留部分注釋
        .pipe(sourcemaps.write('maps')) // 地圖輸出路徑(存放位置)
        .pipe(gulp.dest( destPath.script )); // 輸出路徑
});

// imagemin 圖片壓縮
gulp.task('images', function(){
    return gulp.src( srcPath.image+'/**/*' ) // 指明源文件路徑常熙,如需匹配指定格式的文件,可以寫成 .{png,jpg,gif,svg}
        .pipe(changed( destPath.image ))
        .pipe(imagemin({
            progressive: true, // 無損壓縮JPG圖片
            svgoPlugins: [{removeViewBox: false}]  // 不要移除svg的viewbox屬性
            //use: [pngquant()] // 深度壓縮PNG
        }))
        .pipe(gulp.dest( destPath.image )); // 輸出路徑
});

gulp.task('concat', function () {
    return gulp.src( srcPath.script+'/*.min.js' )  // 要合并的文件
        .pipe(concat('libs.js')) // 合并成libs.js
        .pipe(rename({ suffix: '.min' })) // 重命名
        .pipe(gulp.dest( destPath.script )); // 輸出路徑
});

gulp.task('useref', function () {
    return gulp.src('src/*.html')
        .pipe(useref())
        .pipe(gulp.dest('src'));
});

// 文件合并
gulp.task('concat', function () {
    return gulp.src( srcPath.script+'/*.min.js' )  // 要合并的文件
        .pipe(concat('libs.js')) // 合并成libs.js
        .pipe(rename({ suffix: '.min' })) // 重命名
        .pipe(gulp.dest( destPath.script )); // 輸出路徑
});

// 本地服務(wù)器
gulp.task('webserver', function() {
    gulp.src( destPath.html ) // 服務(wù)器目錄(.代表根目錄)
        .pipe(webserver({ // 運行g(shù)ulp-webserver
            livereload: true, // 啟用LiveReload
            open: true // 服務(wù)器啟動時自動打開網(wǎng)頁
        }));
});

// 默認任務(wù)
gulp.task('webserver',function(){
    gulp.src('./')//服務(wù)器跟目錄
        .pipe(webserver({
            livereload:true,//啟用livereload
            open:true //服務(wù)器啟動時自動打開頁面
        }))
});

//壓縮合并js  還有css
gulp.task('usemin', function() {
    return gulp.src(srcPath.html+'/**/*.html')
        .pipe(usemin({
            js: [ uglify()],
            css:[ cssmin() ]
        }))
        .pipe(gulp.dest(srcPath.html));
});

// 監(jiān)聽任務(wù)
gulp.task('watch',function(){
    gulp.watch( 'src/sass/**/*.sass', ['sequence']);/// / 監(jiān)聽根目錄下所有.html文件
});

gulp.task('default',['webserver','watch']); //默認任務(wù)

/*es6*/
gulp.task('es6', function() {
    return gulp.src('src/es6/app.js')
        .pipe(plumber())
        .pipe(babel({
            presets: ['es2015']
        }))
        .pipe(gulp.dest('dist/js'));
});

/*-------------- 發(fā)布環(huán)境*/
/* = 發(fā)布環(huán)境( Release Task )
 -------------------------------------------------------------- */

// 樣式處理
gulp.task('sassRelease', function () {
    return sass( srcPath.css, { style: 'compressed' }) // 指明源文件路徑碱茁、并進行文件匹配(編譯風(fēng)格:壓縮)
        .on('error', function (err) {
            console.error('Error!', err.message); // 顯示錯誤信息
        })
        .pipe(gulp.dest( destPath.css )); // 輸出路徑
});

// 腳本壓縮&重命名
gulp.task('scriptRelease', function() {
    return gulp.src( [srcPath.script+'/*.js','!'+srcPath.script+'/*.min.js'] ) // 指明源文件路徑裸卫、并進行文件匹配,排除 .min.js 后綴的文件
        .pipe(rename({ suffix: '.min' })) // 重命名
        .pipe(uglify({ preserveComments:'some' })) // 使用uglify進行壓縮纽竣,并保留部分注釋
        .pipe(gulp.dest( destPath.script )); // 輸出路徑
});

// 打包發(fā)布
gulp.task('release', ['clean'], function(){ // 開始任務(wù)前會先執(zhí)行[clean]任務(wù)
    return gulp.start('sassRelease','scriptRelease','images'); // 等[clean]任務(wù)執(zhí)行完畢后再執(zhí)行其他任務(wù)
});

/* = 幫助提示( Help )
 -------------------------------------------------------------- */
gulp.task('help',function () {
    console.log('----------------- 開發(fā)環(huán)境 -----------------');
    console.log('gulp default       開發(fā)環(huán)境(默認任務(wù))');
    console.log('gulp html      HTML處理');
    console.log('gulp sass      樣式處理');
    console.log('gulp script        JS文件壓縮&重命名');
    console.log('gulp images        圖片壓縮');
    console.log('gulp concat        文件合并');
    console.log('----------------發(fā)布環(huán)境-----------------');
    console.log('gulp release       打包發(fā)布');
    console.log('gulp clean     清理文件');
    console.log('gulp sassRelease       樣式處理');
    console.log('gulp scriptRelease 腳本壓縮&重命名');
    console.log('---------------------------------------------');
});

package.json 文件

package.json 是項目的描述文件墓贿,記錄了當前項目信息,例如項目名稱蜓氨、版本聋袋、作者、GitHub地址语盈、當前項目依賴了哪些第三方模塊

使用 npm init -y 命令可以快速生成該文件 -y意思就是不填寫任何信息全部使用默認舱馅。

使用 npm install 可以自動下載描述文件中的所有第三方模塊

1. 項目依賴

  • 在項目開發(fā)階段和線上運行階段缰泡,都需要依賴的第三方模塊刀荒,稱為項目依賴
  • 使用 npm install 命令下載會默認被添加到 package.json 文件的 dependencies 字段中

2. 開發(fā)依賴

  • 在項目開發(fā)階段需要依賴代嗤,線上運行階段不需要依賴的第三方包,稱為開發(fā)依賴
  • 使用 npm install 命令下載會默認被添加到 package.json 文件的 devDependencies 字段中

3. package-lock.json文件的作用

  • 鎖定包的版本缠借,確保再次下載的時候不會因為包版本不同而產(chǎn)生問題
  • 加快下載速度干毅,因為該文件已經(jīng)記錄了項目所依賴的第三方包的樹狀結(jié)構(gòu)和包的下載地址,重新安裝時只需下載即可泼返,不需要做額外工作

Nodejs模塊的加載機制

  • require方法根據(jù)模塊路徑查找模塊硝逢,如果時完整路徑,直接引入模塊
  • 如果模塊后綴省略绅喉,先找同名js文件再找同名文件夾
  • 如果找到了同名文件夾渠鸽,找文件夾中的index.js
  • 如果文件夾中沒有index.js就會去當前文件夾中的package.js文件中查找選項中的入口文件

Nodejs服務(wù)器端部分

  • 網(wǎng)站應(yīng)用程序主要是由客戶端和服務(wù)器端組成
  • 客戶端:在瀏覽器中運行的部分,就是用戶可以看到并與之交互的界面程序柴罐,使用html\css\js構(gòu)建
  • 服務(wù)器端:在服務(wù)器中運行的部分徽缚,負責(zé)存儲數(shù)據(jù)和處理應(yīng)用邏輯
  • Node網(wǎng)站服務(wù)器:能夠接收客戶端請求,并且能夠響應(yīng)請求

1. 創(chuàng)建web服務(wù)器

// 引用系統(tǒng)模塊
const http = require('http');
// 創(chuàng)建web服務(wù)器
const app = http.createServer();

// 當客戶發(fā)送請求的時候
// 為服務(wù)器添加request事件
app.on('request', (req, res) => {
  // 請求響應(yīng)
  res.end('<h1>hi, user</h1>');
})

// 監(jiān)聽端口革屠,listen方法
app.listen(3000)
console.log('服務(wù)器已啟動凿试,訪問localhost:3000')

2. http請求與響應(yīng)處理

客戶端和服務(wù)器端的溝通規(guī)范

HTTP: 超文本傳輸協(xié)議

報文

在http響應(yīng)和請求過程中傳遞的數(shù)據(jù)塊就叫報文,包括要傳送的數(shù)據(jù)和一些附加信息似芝,并且要遵守規(guī)定好的格式

請求報文

客戶端對服務(wù)器端說的話

請求方式:

  • GET 獲取數(shù)據(jù)的請求一般用get
  • POST 添加數(shù)據(jù)的請求一般用post那婉,一般的邏輯比如登錄操作,也用post
const http = require('http');
const app = http.createServer();

app.on('request', (req, res) => {
  // 獲取請求方式
  console.log(req.method);
})

app.listen(3000)

通過地址欄輸入網(wǎng)址的形式一般發(fā)送的是get請求

表單提交一般用post

// method 指定當前表單的提交方式
// action 指定當前表單提交的地址
<form method="post" action="http://localhost:3000">
  <input type="submit" value="提交"/>
</form>

請求地址:

app.on('request', (req, res) => {
  // 通過req.method 來獲取請求方式
  console.log(req.method)
  // 通過 req.url 來獲取請求地址
  console.log(req.url)
  // 獲取請求報文信息
  console.log(req.headers)
})

下面我們要實現(xiàn)根據(jù)不同的請求地址響應(yīng)不同的頁面

const http = require('http');
const app = http.createServer();

app.on('request', (req, res) => {
  if( req.url == '/index' || req.url == '/') {
    res.end('我是首頁')
  } else if(req.url == '/list') {
    res.end('我是list')
  } else {
    res.end('頁面不存在')
  }
})

app.listen(3000)

請求參數(shù):

get請求參數(shù):

在服務(wù)器端如何獲取到請求參數(shù)党瓮?

const http = require('http');
// 用于處理url地址的內(nèi)置模塊
const url = require('url);
const app = http.createServer();

app.on('request', (req, res) => {
  // 通過url.parse()方法可以返回請求地址以及參數(shù)的一些內(nèi)容
  // 第一個參數(shù)為要解析的url地址
  // 第二個參數(shù)為將查詢參數(shù)解析成對象的形式
  let {query, pathname} = url.parse(req.url, true);

  if( pathname == '/index' || pathname == '/') {
    res.end('我是首頁')
  } else if(pathname == '/list') {
    res.end('我是list')
  } else {
    res.end('頁面不存在')
  }
})

app.listen(3000)

post請求參數(shù):

  • 參數(shù)被放置在請求體中進行傳輸
  • 獲取post參數(shù)需要使用data事件和end事件
  • 使用querystring系統(tǒng)模塊將參數(shù)轉(zhuǎn)換為對象格式
// html部分
<form action="http://localhost:3000" method="post">
  <input type="text" name="username">
  <input type="password" name="password">
  <input type="submit" value="提交">
</form>

// js
const http = require('http');
const app = http.createServer();
// 處理請求參數(shù)模塊
const querystring  = require('querystring');

app.on('request', (req, res) => {
  // post是通過事件來接收的详炬,data事件和end事件
  // data當請求參數(shù)傳遞的時候觸發(fā)data事件
  // 當參數(shù)傳遞完成的時候觸發(fā)end事件

  // post參數(shù)不是一次就接收完的,我們需要先聲明一個變量
  let postParams = '';

  // 監(jiān)聽參數(shù)傳輸事件
  req.on('data', params => {
    postParams += params;
  })

  // 監(jiān)聽參數(shù)傳輸完畢事件
  req.on('end', () => {
    // 這個時候我們得到的內(nèi)容是一個參數(shù)字符串(username=zhangsan&password=1234)
    // 這個時候我們還可以使用url模塊來處理參數(shù)嗎寞奸?
    // url模塊是用來處理請求地址的痕寓,現(xiàn)在我們是直接想處理這樣的一個字符串
    // 如果這時候要轉(zhuǎn)換成對象的形式
    console.log(querystring.stringify(postParams));
  })

  // 對于客戶端的每一次請求,服務(wù)器端都要去做出響應(yīng)蝇闭,所以end必須要加呻率!否則就會處于一個等待狀態(tài)
  res.end('OK')

})

app.listen(3000)
響應(yīng)報文

服務(wù)器端對客戶端說的話

HTTP狀態(tài)碼

  • 200請求成功
  • 404請求的資源沒有找到
  • 500服務(wù)器端錯誤
  • 400客戶端請求有語法錯誤

設(shè)置狀態(tài)碼

const http = require('http');
const app = http.createServer();

app.on('request', (req, res) => {
  // 設(shè)置狀態(tài)碼
  res.writeHead(400)
  res.end('hello')
})

app.listen(3000)

內(nèi)容類型

const http = require('http');
const app = http.createServer();

app.on('request', (req, res) => {
  // 第一個參數(shù)為狀態(tài)碼
  // 第二個參數(shù)為一個對象,可以設(shè)置內(nèi)容類型呻引,編碼類型
  res.writeHead(200, {
    'content-type': 'text/plain;charset=utf8'
  })
  res.end('<h1>你好nodejs</h1>')
})

app.listen(3000)

路由

路由是指客戶端請求地址與服務(wù)器端程序代碼的對應(yīng)關(guān)系礼仗,簡單的說,就是請求什么響應(yīng)什么

// 實現(xiàn)路由功能
// 獲取客戶端的請求方式
// 獲取客戶端的請求地址

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

const app = http.createServer();

app.on('request', (req, res) => {
  // 獲取請求方式
  const method = req.method.toLowerCase();
  // 獲取請求地址
  const pathname = url.parse(req.url).pathname

  res.writeHead('200', {
    'content-type': 'text/html;charset=utf8'
  })

  if(method == 'get') {
    if(pathname == '/' || pathname == '/index') {
      res.end('home')
    } else if (pathname == '/list') {
      res.end('list')
    } else {
      res.end('no page')
    }
  }else if(method == 'post') {

  }
})

app.listen(3000)

靜態(tài)資源訪問

服務(wù)器不需要處理逻悠,可以直接響應(yīng)給客戶端的資源就是靜態(tài)資源比如css元践、js、image文件

const http = require('http');
const url = require('url');
const path = require('path');
const fs = require('fs');
const mime = require('mime');

const app = http.createServer();

app.on('request', (req, res) => {
  let pathname = url.parse(req.url).pathname;
  pathname == '/' ? 'default.html' : pathname;

  // 拼接查找靜態(tài)文件存儲的地址
  let realPath = path.join(__dirname, 'public' + pathname);

  // 利用第三方模塊mime返回要訪問的文件的類型
  let type = mime.getType(realPath);
  // 讀取文件
  fs.readFile(realPath, (error, result) => {
    if(error != null) {
      res.writeHead(404, {
        'content-type': 'text/html;charset=utf8'
      })
      res.end('文件讀取失斖恕单旁!');
      return;
    }
    res.writeHead(200, {
      'content-type': type
    })
    res.end(result);
  })
})

app.listen(3000)

動態(tài)資源訪問

相同的請求地址可以傳遞不同的請求參數(shù),得到不同的響應(yīng)資源饥伊,這就是動態(tài)資源

3. Nodejs異步編程

1. 同步API象浑,異步API

同步API:只有當前API執(zhí)行完成后蔫饰,才能繼續(xù)執(zhí)行下一個API

console.log(111)
console.log(222)
// 111,222

異步API:當前API的執(zhí)行不會阻塞后續(xù)代碼的執(zhí)行

console.log(111)
setTimeout(() => {
  console.log(222)
}, 2000)

console.log(333)
// 打印結(jié)果為111愉豺,333篓吁,222
// 也就是說定時器并沒有阻塞后邊代碼的執(zhí)行

同步API可以從返回值中拿到API執(zhí)行的結(jié)果,但是異步API是不可以的

// 同步
function sum (a, b) {
  return a + b
}

const result = sum(1, 2)

// 異步
function getMsg() {
  setTimeout(() => {
    return {msg: 'hello'}
  }, 2000)
}

const msg = getMsg() // undefined
// 異步API中我們是無法通過返回值的方式去拿到返回結(jié)果的

異步API可以使用回調(diào)函數(shù)拿到返回結(jié)果

function getMsg(callback) {
  setTimeout(() => {
    callback({
      msg: 'hello'
    })
  }, 2000)
}

getMsg((data) => {
  console.log(data) // hello
})

同步API和異步API的執(zhí)行順序不一樣蚪拦,同步從上到下依次執(zhí)行杖剪,前面的代碼會阻塞后面的代碼的執(zhí)行

異步API不會等待API執(zhí)行完成后再向下執(zhí)行代碼

可以用Promise解決Nodejs異步編程回調(diào)地獄問題

function getData() {
  setTimeout(() => {
    var name = '張三';
    return name;
  }, 100)
}
console.log(getData())// 這時候我們是獲取不到內(nèi)部name的數(shù)據(jù)的

解決方法一:利用增加一個callback的方式

function getData(callback) {
  setTimeout(() => {
    var name = '張三';
    callback(name);
  }, 100)
}

getData(function(aaa) {
  console.log(aaa) // 這個aaa就是上面的name
})

解決方法二:Promise解決

var p = new Promise(function(resole, reject) {
  setTimeout(() => {
    var name = '張三';
    resolve(name);
  }, 100)
})

// 獲取name
p.then((data) => {
  console.log(data); // 此時的data就是name的數(shù)據(jù)
})

使用async和await:

/**
 * 普通方法
  function test() {
    return '你好驻右,nodejs';
  }

  console.log(test());
 */
 
async function test() {
  return '你好洪橘,nodejs';
}
 
console.log(test()); // Promise { '你好,nodejs' }

async function main() {
  var data = await test() // await必須得用在async里面,獲取異步方法里面的數(shù)據(jù)
  console.log(data);
}
main();

異步函數(shù)(async和await)是異步編程語法的終極解決方案瞻惋,它可以讓我們將異步代碼寫成同步形式括袒,讓代碼不再有回調(diào)函數(shù)嵌套孩擂,使代碼更清晰

async關(guān)鍵字

  • 普通函數(shù)定義前加async關(guān)鍵字,普通函數(shù)變成異步函數(shù)
  • 異步函數(shù)默認返回Promise對象
  • 在異步函數(shù)內(nèi)部使用return關(guān)鍵字進行結(jié)果返回箱熬,結(jié)果會被包裹的promise對象中return關(guān)鍵字替代了resolve方法
  • 在異步函數(shù)內(nèi)部使用throw關(guān)鍵字拋出程序異常
  • 調(diào)用異步函數(shù)then方法獲取異步函數(shù)執(zhí)行結(jié)果
  • 調(diào)用異步函數(shù)catch方法獲取異步函數(shù)執(zhí)行的錯誤信息

await關(guān)鍵字

  • await只能出現(xiàn)在異步函數(shù)中
  • await 后面只能寫Promise對象类垦,寫其他類型的API是不可以的
  • await 關(guān)鍵字可以暫停異步函數(shù)向下執(zhí)行,直到promise返回結(jié)果

數(shù)據(jù)庫及環(huán)境搭建

1. 為什么要使用數(shù)據(jù)庫

  • 動態(tài)網(wǎng)站中的數(shù)據(jù)都是存儲在數(shù)據(jù)庫中
  • 數(shù)據(jù)庫可以用來持久存儲客戶端通過表單收集的用戶信息
  • 數(shù)據(jù)庫軟件本身可以對數(shù)據(jù)進行高效的管理

2. 什么是數(shù)據(jù)庫

數(shù)據(jù)庫即存儲數(shù)據(jù)的倉庫城须,可以將數(shù)據(jù)進行有序的分門別類的存儲蚤认,它是獨立與語言之外的軟件,可以通過API操作它

常見的數(shù)據(jù)庫有:mysql糕伐、mongoDB砰琢、oracle

模板引擎

模板引擎是node的第三方模塊

讓開發(fā)者以更加友好的方式拼接字符串,使項目代碼更加清晰易于維護

1. art-template模板引擎

是由騰訊公司出品良瞧,是目前運行最快的模板引擎

下載: npm install art-template

使用 const template = require('art-template')引入模板引擎

告訴模板引擎要拼接的數(shù)據(jù)和模板在哪 const html = template('模板路徑', 數(shù)據(jù))

// app.js中引入模板引擎
const path = require('path')
const template = require('art-template')

// template方法是用來拼接字符串的
// 第一個參數(shù)是模板路徑陪汽,這里要寫絕對路徑
// 第二個參數(shù)是要在模板中顯示的數(shù)據(jù), 對象類型
// 返回拼接好的字符串
const views = path.join(__dirname, 'views', 'index.art')

const html = template(views, {
  name: '張三',
  age: 20
})

// index.art模板
// 普通的html結(jié)構(gòu)

<div>
  <span>{{name}}</span>
  <span>{{age}}</span>
</div>

模板語法

art-template同時支持兩種模板語法: 標準語法和原始語法

標準語法可以讓模板更容易讀寫褥蚯,原始語法具有強大的邏輯處理能力

輸出

兩種語法都是可以進行運算并返回運算結(jié)果的挚冤,比如三目運算符

標準語法: {{ data }}

原始語法: <%= data %>

原文輸出

如果數(shù)據(jù)中攜帶html標簽,默認模板引擎是不會解析標簽的赞庶,會將其轉(zhuǎn)義后輸出

假設(shè)content內(nèi)容為<h2>你好</h2>训挡,如果我們需要將h2標簽解析后輸出,我們需要如下操作:

標準語法: {{ @ content }}

原始語法:<%- content %>

條件判斷

在模板中可以根據(jù)條件來決定顯示哪塊html代碼

標準語法:{{ if 條件 }} ... {{ 條件 else if }} ... {{/if}}

{{ if age > 18}}
  年齡大于18
{{ else if age < 15 }}
  年齡小于十五
{{ else }}
  年齡不符合
{{ /if }}

原始語法:<% if (條件) { %> ... <%} %>

<% if (age > 18) { %> 
  年齡大于18 
<% } else if (age < 15) { %>
  年齡小于15
<% } else { %>
  年齡不符合要求
<% } %>
循環(huán)

標準語法:{{ each 數(shù)據(jù) }} {{ /each }}

{{ each target}}
  {{ $index }} {{ $value }}
{{ /each }}

原始語法:

<% for( var i = 0; i < target.length; i++) { %>
  <%= i %> <%= target[i] %>
<% } %>
// 標準語法
<ul>
  {{each users}}
    <li>
      {{$value.name}}
      {{$value.age}}
    </li>
  {{/each}}
</ul>

// 原始語法
<ul>
  <% for( var i = 0; i < users.length; i++) { %>
    <li>
      <%= users[i].name %>
      <%= users[i].age %>
    </li>
  <% } %>
</ul>
子模板

使用子模板可以將網(wǎng)站公共部分(頭部等)抽離到單獨的文件中

標準語法:{{ include '模板' }}

原始語法:<% include('模板') %>

{{ include './header.art' }}

<% include('./header.art') %>
模板繼承

使用模板繼承可以將網(wǎng)站html骨架抽離到單獨的文件中歧强,其中頁面模板可以繼承骨架文件

P192-P200部分澜薄,未完成

Express框架

Express是一個基于Node平臺的web應(yīng)用程序開發(fā)框架,它提供了一系列的強大特性摊册,幫助你創(chuàng)建各種web應(yīng)用肤京,我們可以使用 npm install express 命令下載,是node的第三方模塊

1. Express框架特性

(1)提供了方便簡潔的路由定義方式

所謂簡潔路由方式就比如我們之前使用router這個第三方模塊定義路由的方式茅特,router這個第三方模塊實際上就是從express框架中抽取出來的

(2)對獲取http請求參數(shù)進行了簡化處理

框架提供了更加簡潔的方式接收請求參數(shù)忘分,也就是說使用express框架不再對請求參數(shù)的格式進行轉(zhuǎn)化棋枕,框架讓我們拿到的直接就是對象類型,現(xiàn)在我們也不需要對請求對象添加data事件以及end事件饭庞,框架內(nèi)部接收完請求參數(shù)并處理完以后,將參數(shù)作為請求對象的屬性讓我們直接獲取熬荆。

(3)對模板引擎支持程度高舟山,方便渲染動態(tài)html頁面

框架可以非常容易的和模板引擎協(xié)同工作,方便開發(fā)

(4)框架提供了中間件機制卤恳,有效控制http請求

所謂中間件累盗,我們暫且可以簡單理解為對請求的攔截

(5)擁有大量第三方中間件對功能進行擴展

可以理解為基于框架還有另外的一些插件可以供我們實現(xiàn)各種功能

Express初體驗:

// 引入express框架
// 這個返回值其實是一個方法,通過調(diào)用這個方法我們就可以創(chuàng)建網(wǎng)站服務(wù)器
// 不再需要引入http模塊創(chuàng)建網(wǎng)站服務(wù)器
const express = require('express')

// 創(chuàng)建網(wǎng)站服務(wù)器
const app = express()
// express路由和router路由第三方模塊的使用方法一致
app.get('/', (req, res) => {
  // 對客戶端進行響應(yīng)突琳,不再使用原生node的end方法進行響應(yīng)
  // 取而代之的是send()
  // 1.send會自動檢測響應(yīng)內(nèi)容的類型若债,幫我們把類型自動設(shè)置到響應(yīng)頭中
  // 2.還可以幫我們設(shè)置響應(yīng)內(nèi)容的類型編碼
  // 3.還可以幫我們自動設(shè)置http狀態(tài)碼
  res.send('hello express')

})

app.get('/list', (req, res) => {
  // 使用框架可以直接向客戶端響應(yīng)一個對象,這在原生的node中end是做不到的
  res.send({name: 'zhangsan', age: 20})
})

// 監(jiān)聽端口
app.listen(3000);
console.log('網(wǎng)站服務(wù)器啟動成功');

2. 中間件

中間件實際上就是框架提供的一堆方法拆融,可以接收客戶端發(fā)來的請求蠢琳,可以對請求做出響應(yīng),也就是說中間件的作用是專門用來接收請求處理請求的镜豹,對于同一個請求express運行我們設(shè)置多個中間件傲须,會按照中間件設(shè)置的順序依次處理

中間件分為兩部分:第一部分是框架提供的用于接收請求的中間件方法,第二部分是開發(fā)人員提供的用于處理請求的方法(請求處理函數(shù))

比如路由就是中間件趟脂,get和post方法就是框架提供的接收請求的方式泰讽,方法的第二個參數(shù)就是開發(fā)人員提供的用于處理請求的方法。

可以針對同一個請求昔期,設(shè)置多個中間件已卸,對同一個請求多次處理

默認情況下,請求從上到下一次匹配中間件硼一,一旦匹配成功累澡,就會終止匹配

可以調(diào)用next方法將請求的控制權(quán)交給下一個中間件,知道遇到結(jié)束請求的中間件

app.get('/', (req, res, next) => {
  req.name = '張三'
  next()
})

app.get('/', (req, res) => {
  req.send (req.name)
})

1. app.use中間件用法

我們有時候接收請求既想接收get請求般贼,也想接收post請求永乌,這時候我們就會用到app.use中間件

app.use中間件不區(qū)分接收的請求方式,匹配所有的請求方式具伍,可以直接傳入請求處理函數(shù)翅雏,代表接收所有的請求

app.use((req, res, next) => {
  console.log(req.url)
  next()
})

app.use第一個參數(shù)也可以傳入請求地址,代表不論什么請求方式人芽,只要是這個請求地址就接收這個請求

app.use('/admin', (req, res, next) => {
  console.log(req.url)
  next()
})
// 中間件概念
const express = require('express')

const app = express()

// 接收所有請求的中間件
app.use((req, res, next) => {
  console.log('走了app.use中間件1')
  next()
})

app.use('/request', (req, res, next) => {
  console.log('走了app.use中間件2')
  next()
})

// 當我們請求路徑設(shè)置為/list望几,那么此時就不會走/request這個中間件,所以只會打印'走了app.use中間件1'
app.get('/list', (req, res) => {
  res.send('zhangsan')
})

// 監(jiān)聽端口
app.listen(3000);
console.log('網(wǎng)站服務(wù)器啟動成功');

2. 中間件應(yīng)用

2.1 路由保護

客戶端在訪問需要登錄的頁面時萤厅,可以先用中間件判斷用戶登錄狀態(tài)橄抹,如果用戶未登錄靴迫,則攔截請求,直接響應(yīng)楼誓,禁止用戶進入需要登錄的頁面

// 中間件應(yīng)用
const express = require('express')

const app = express()

app.use('/admin', (req, res, next) => {
  let isLogin = false;

  if (isLogin) {
    // 讓請求繼續(xù)向下執(zhí)行
    next()
  } else {
    res.send('您還沒有登錄玉锌,拒絕訪問')
  }
})

app.get('/admin', (req, res) => {
  res.send('您已經(jīng)登錄,可以訪問')
})


// 監(jiān)聽端口
app.listen(3000);
console.log('網(wǎng)站服務(wù)器啟動成功');
2.2 網(wǎng)站維護公告

在所有路由的最上面定義接收所有請求的中間件疟羹,直接為客戶端做出響應(yīng)主守,網(wǎng)站正在維護

// 中間件應(yīng)用
const express = require('express')

const app = express()

app.use((req, res, next) => {
  // 此時并沒有調(diào)用next方法,此時不會繼續(xù)向下執(zhí)行
  res.send('當前網(wǎng)站正在維護')
})

app.use('/admin', (req, res, next) => {
  let isLogin = false;

  if (isLogin) {
    // 讓請求繼續(xù)向下執(zhí)行
    next()
  } else {
    res.send('您還沒有登錄榄融,拒絕訪問')
  }
})

app.get('/admin', (req, res) => {
  res.send('您已經(jīng)登錄参淫,可以訪問')
})


// 監(jiān)聽端口
app.listen(3000);
console.log('網(wǎng)站服務(wù)器啟動成功');
2.3 定義404頁面
// 中間件應(yīng)用
const express = require('express')

const app = express()


app.use('/admin', (req, res, next) => {
  let isLogin = true;

  if (isLogin) {
    // 讓請求繼續(xù)向下執(zhí)行
    next()
  } else {
    res.send('您還沒有登錄,拒絕訪問')
  }
})

app.get('/admin', (req, res) => {
  res.send('您已經(jīng)登錄愧杯,可以訪問')
})
// 在所有路由的最后面添加一個404頁面的中間件涎才,并設(shè)置狀態(tài)碼為404,支持鏈式調(diào)用
app.use((req, res, next) => {
  res.status(404).send('當前頁面不存在')
})


// 監(jiān)聽端口
app.listen(3000);
console.log('網(wǎng)站服務(wù)器啟動成功');

3. 錯誤處理中間件

在程序執(zhí)行過程中力九,不可避免的會出現(xiàn)一些無法預(yù)料的錯誤耍铜,比如讀取文件失敗,數(shù)據(jù)庫連接失敗跌前,express框架提供了錯誤處理中間件來處理錯誤业扒,要知道的是,在執(zhí)行過程中一旦出現(xiàn)錯誤就不能向下執(zhí)行了舒萎,如果想要在出錯以后繼續(xù)向下執(zhí)行我們就需要在程序中捕獲錯誤程储,加入錯誤處理。

錯誤處理中間件是一個統(tǒng)一處理錯誤的地方

app.use((err, req, res, next) => {
  res.status(500).send('服務(wù)器發(fā)生未知錯誤)
})

接下來我們來寫一個簡單的錯誤處理:

// 錯誤處理中間件
const express = require('express')

const app = express()

app.get('/index', (req, res) => {
  // 拋出一個錯誤
  // 同步處理代碼
  throw new Error('程序發(fā)生了未知錯誤')
})

// 設(shè)置錯誤處理中間件
app.use((err, req, res, next) => {
  res.status(500).send(err.message)
})


// 監(jiān)聽端口
app.listen(3000);
console.log('網(wǎng)站服務(wù)器啟動成功');

注意:錯誤處理中間件只能處理同步代碼拋出的錯誤臂寝,如果是異步代碼章鲤,錯誤處理中間件是無法捕獲到的,這時候我們需要手動去觸發(fā)這個錯誤處理中間件

當程序出錯時咆贬,調(diào)用next方法败徊,并且將錯誤信息通過參數(shù)形式傳遞給next方法就可以觸發(fā)錯誤處理中間件

// 錯誤處理中間件
const express = require('express')
const fs = require('fs')

const app = express()

app.get('/index', (req, res, next) => {
  fs.readFile('/demo.txt', 'utf8', (err, result) => {
    if(err != null) {
      // 此時next傳遞了參數(shù),代表要傳遞給錯誤處理中間件
      next(err)
    }
  })
})

// 設(shè)置錯誤處理中間件
app.use((err, req, res, next) => {
  res.status(500).send(err.message)
})


// 監(jiān)聽端口
app.listen(3000);
console.log('網(wǎng)站服務(wù)器啟動成功');

4. 異步函數(shù)錯誤捕獲

在nodejs中掏缎,異步API的錯誤信息都是通過回調(diào)函數(shù)獲取的皱蹦,支持Promise對象的異步API發(fā)生錯誤可以通過catch方法捕獲,異步函數(shù)執(zhí)行如果發(fā)生錯誤要如何捕獲錯誤呢眷蜈?

try catch可以捕獲異步函數(shù)以及其他異步代碼在執(zhí)行過程中發(fā)生的錯誤沪哺,但是不能捕獲其他類型API發(fā)生的錯誤(比如說回調(diào)函數(shù)或者Promise對象)

// 異步函數(shù)錯誤的捕獲
const express = require('express')
const fs = require('fs')
const promisify = require('util').promisify;
const readFile = promisify(fs.readFile);

const app = express()

app.get('/index', async (req, res, next) => {
  try {
    await readFile('./aaa.js') 
  } catch (error) {
    next(error)
  }
})

// 設(shè)置錯誤處理中間件
app.use((err, req, res, next) => {
  res.status(500).send(err.message)
})


// 監(jiān)聽端口
app.listen(3000);
console.log('網(wǎng)站服務(wù)器啟動成功');

3. Express請求處理

1. 構(gòu)建模塊化路由

雖然我們現(xiàn)在可以用app.get 或者 app.post 方法可以創(chuàng)建一個路由,但是在真實開發(fā)中路由是非常多的酌儒,如果我們把所有的路由信息羅列在一個文件中辜妓,那將是非常可怕的。為了解決這個問題提供了模塊化構(gòu)建方式籍滴,可以將不同類別的路由放置在不同的模塊中酪夷,方便管理

接下來我們看創(chuàng)建模塊化路由的基礎(chǔ)代碼:

// 構(gòu)建模塊化路由的基礎(chǔ)代碼
const express = require('express')

const app = express()
// 創(chuàng)建路由對象
const home = express.Router();
// 將路由和請求路徑進行匹配
app.use('/home', home);
// 在home下繼續(xù)創(chuàng)建路由(二級路由)
home.get('/index', (req, res) => {
  res.send('歡迎來到展示頁面')
})

// 監(jiān)聽端口
app.listen(3000);
console.log('網(wǎng)站服務(wù)器啟動成功');

參照上面的例子,我們可以把home和admin模塊分別放到單獨的js中孽惰,作為單獨的模塊晚岭,方便我們引用

// home.js
const home = express.Router();
home.get('/index', (req, res) => {
  res.send('歡迎來到首頁')
})

module.exports = home
// admin.js
const admin = express.Router();
admin.get('/index', (req, res) => {
  res.send('歡迎來到管理頁面')
})

moudle.exports = admin
// app.js
const home = require('./route/home');
const admin = require('./route/admin');

app.use('/home', home);
app.use('/admin', admin);

2. GET請求參數(shù)的獲取

Express框架中使用req.query即可獲取GET參數(shù),框架內(nèi)部會將GET參數(shù)轉(zhuǎn)換為對象并返回

const express = require('express')

const app = express()

app.get('/index', (req, res) => {
  // 獲取get請求參數(shù)
  res.send(req.query)
})

app.listen(3000)

3. POST請求參數(shù)的獲取

Express中接收post請求參數(shù)需要借助第三方包 body-parser

const express = require('express')
// 目前body-parser已經(jīng)被棄用勋功,直接使用express就可以替代它
const bodyParser = require('body-parser') 
const app = express()
// 攔截所有請求坦报,對請求進行處理
// extended: false 方法內(nèi)部使用querystring模塊處理請求參數(shù)的格式
// extended: true 方法內(nèi)部使用第三方模塊qs處理請求參數(shù)的方式
app.use(express.urlencoded({extended: false}))

app.post('/add', (req, res) => {
  res.send(req.body)
})

app.listen(3000)

4. Express路由參數(shù)

const express = require('express')
const app = express()

app.get('/index/:id/:name', (req, res) => {
  // 獲取get請求參數(shù) params 方法
  res.send(req.params)
})
app.listen(3000)

// 訪問地址 http://localhost:3000/index/123/zhangsan 返回值為:{ id: 123, name: zhangsan }

5. 靜態(tài)資源處理

通過Express內(nèi)置的express.static可以方便地托管靜態(tài)文件,例如img\css\js等文件

app.use(express.static('/static/public'));

使用app.use中間價攔截所有的請求酝润,然后將請求交給express.static這個方法來處理燎竖,并且將靜態(tài)資源目錄告訴static方法璃弄,在方法內(nèi)部會判斷客戶端發(fā)來的請求要销,是否是靜態(tài)資源請求,如果是夏块,方法內(nèi)部直接將靜態(tài)資源響應(yīng)給客戶端疏咐,中止當前請求。如果發(fā)來的請求不是靜態(tài)資源請求脐供,方法內(nèi)部會調(diào)用next方法將請求的控制權(quán)交給下一個中間件浑塞,開啟靜態(tài)資源訪問后,靜態(tài)資源就可以用這樣的方式訪問了:

const express = require('express')
const path = require('path')

const app = express()
// 實現(xiàn)靜態(tài)資源訪問功能
app.use(express.static(path.join(__dirname, 'public')))

// 還可以給文件路徑添加一個虛擬路徑
app.use('/aaa', express.static(path.join(__dirname, 'public')))
// 這樣我們通過絕對路徑 + /aaa/public 也可以訪問到靜態(tài)資源文件

app.listen(3000)

6. express-art-template模板引擎

為了使art-template 模板引擎更好的和express框架配合政己,模板引擎官方在原art-template模板引擎的基礎(chǔ)上封裝了express-art-template

使用npm install art-template express-art-template命令進行安裝

const express = require('express')
const path = require('path')

const app = express()
// 告訴express框架使用什么模板引擎渲染什么后綴的模板文件
app.engine('art', require('express-art-template'))
// 告訴express框架酌壕,模板存放的位置使什么
// 第一個參數(shù)views是固定的,不可變的
// 第二個views是文件夾名字歇由,可變
app.set('views', path.join(__dirname, 'views'))
// 告訴express框架模板的默認后綴是什么
app.set('view engine', 'art');

app.get('/index', (req, res) => {
  // 在這里我們是用render方法渲染函數(shù)卵牍,這個方法是express提供的
  // 第一個參數(shù)是模板的名字,第二個參數(shù)是一個對象沦泌,這個對象里面的屬性在模板中是可以直接拿到的
  // res.render這個方法內(nèi)部幫我們做了很多事情:
  // 1. 拼接了模板路徑
  // 2. 拼接了模板后綴
  // 3. 哪一個模板和哪一個數(shù)據(jù)進行拼接
  // 4. 將拼接結(jié)果響應(yīng)給了客戶端
  res.render('index', { 
    msg: 'message'
  })
})

app.listen(3000)
app.locals對象

將變量設(shè)置到app.locals對象下面糊昙,這個數(shù)據(jù)在所有的模板中都可以獲取到

app.locals.users = [{
  name: 'zhangsan',
  age: 20
},{
  name: 'lisi',
  age: 22
}]
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市谢谦,隨后出現(xiàn)的幾起案子释牺,更是在濱河造成了極大的恐慌,老刑警劉巖回挽,帶你破解...
    沈念sama閱讀 218,858評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件没咙,死亡現(xiàn)場離奇詭異,居然都是意外死亡千劈,警方通過查閱死者的電腦和手機镜撩,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,372評論 3 395
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人袁梗,你說我怎么就攤上這事宜鸯。” “怎么了遮怜?”我有些...
    開封第一講書人閱讀 165,282評論 0 356
  • 文/不壞的土叔 我叫張陵淋袖,是天一觀的道長。 經(jīng)常有香客問我锯梁,道長即碗,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,842評論 1 295
  • 正文 為了忘掉前任陌凳,我火速辦了婚禮剥懒,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘合敦。我一直安慰自己初橘,他們只是感情好,可當我...
    茶點故事閱讀 67,857評論 6 392
  • 文/花漫 我一把揭開白布充岛。 她就那樣靜靜地躺著保檐,像睡著了一般。 火紅的嫁衣襯著肌膚如雪崔梗。 梳的紋絲不亂的頭發(fā)上夜只,一...
    開封第一講書人閱讀 51,679評論 1 305
  • 那天,我揣著相機與錄音蒜魄,去河邊找鬼扔亥。 笑死,一個胖子當著我的面吹牛谈为,可吹牛的內(nèi)容都是我干的旅挤。 我是一名探鬼主播,決...
    沈念sama閱讀 40,406評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼峦阁,長吁一口氣:“原來是場噩夢啊……” “哼谦铃!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起榔昔,我...
    開封第一講書人閱讀 39,311評論 0 276
  • 序言:老撾萬榮一對情侶失蹤驹闰,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后撒会,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體嘹朗,經(jīng)...
    沈念sama閱讀 45,767評論 1 315
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,945評論 3 336
  • 正文 我和宋清朗相戀三年诵肛,在試婚紗的時候發(fā)現(xiàn)自己被綠了屹培。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片默穴。...
    茶點故事閱讀 40,090評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖褪秀,靈堂內(nèi)的尸體忽然破棺而出蓄诽,到底是詐尸還是另有隱情,我是刑警寧澤媒吗,帶...
    沈念sama閱讀 35,785評論 5 346
  • 正文 年R本政府宣布仑氛,位于F島的核電站,受9級特大地震影響闸英,放射性物質(zhì)發(fā)生泄漏锯岖。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,420評論 3 331
  • 文/蒙蒙 一甫何、第九天 我趴在偏房一處隱蔽的房頂上張望出吹。 院中可真熱鬧,春花似錦辙喂、人聲如沸捶牢。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,988評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽叫确。三九已至跳芳,卻和暖如春芍锦,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背飞盆。 一陣腳步聲響...
    開封第一講書人閱讀 33,101評論 1 271
  • 我被黑心中介騙來泰國打工娄琉, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人吓歇。 一個月前我還...
    沈念sama閱讀 48,298評論 3 372
  • 正文 我出身青樓孽水,卻偏偏與公主長得像,于是被迫代替她去往敵國和親城看。 傳聞我的和親對象是個殘疾皇子女气,可洞房花燭夜當晚...
    茶點故事閱讀 45,033評論 2 355

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