node.js学习日记day1

nodejs-01

http模块

创建一个http服务器:

// 引入http模块
var http = require('http');

// 创建一个web服务
/**
 * request 表示获取url传过来的信息
 * response 给浏览器响应信息
 */
http.createServer(function (request, response) {
    //  设置响应头
  response.writeHead(200, {'Content-Type': 'text/plain'});
  //  输出一句话并且结束响应
  response.end('Hello World');
}).listen(8081);   // 端口

console.log('Server running at http://127.0.0.1:8081/');
const http = require('http')
const url = require('url')
http.createServer((req, res)=>{
    // console.log(req.url)
    res.writeHead(200, {"Content-type":"text/html;charset='utf-8'"})// 解决中文乱码 
    res.write(`<head><meta charset="UTF-8"></head>`)  // 解决中文乱码
    console.log(req.url)  // 获取浏览器访问的地址
    if(req.url !== '/favicon.ico'){  //当请求了多个url的时候可能无法获取自己所需要的值
        let userinfo = url.parse(req.url, true).query
        console.log(userinfo);
    }
    res.end('wdnmd')

}).listen(8111)

url模块中的parse方法:

const url = require('url')
let api = 'http://www.itying.com?name=zhangsan&age=20'

// console.log(url.parse(api, true))
let temp = url.parse(api, true).query
console.log(temp.name);

该方法可以把url中包含的信息解析出来,包括selection、query等

commonjs

自定义模块

把公共的模块抽离成一个单独的js文件,作为一个模块,在该js文件内部通过exports 或者 module.exports暴露属性或者方法

要声明定义一个模块的时候,先在js文件中把相应的功能完成,然后使用exports.函数名(自定义) = 模块中的函数 来使该函数暴露,可以被其他模块引用

function formatApi(api){
    return `http://www.itying.com/${api}` 
}
exports.formatApi = formatApi

当需要引用一个模块的时候,在js文件头使用require引用该模块即可使用

const http = require('http')
const tools = require('./module/tools.js')  // 使用自定义模块,相对路径引用

console.log(tools)
http.createServer((req, res)=>{
    // console.log(req.url)
    res.writeHead(200, {"Content-type":"text/html;charset='utf-8'"})// 解决中文乱码 
    res.write(`<head><meta charset="UTF-8"></head>`)  // 解决中文乱码
    let api = tools.formatApi('api/plist')
    res.write(api)
    res.end('')
}).listen(8111)

exports和module.exports暴露模块的不同

  1. exports
exports.get = function(){

}
exports.post = function(){
    
}

或者:

let obj = {
    get:function(){
        console.log('从服务器获取数据')
    },
    post:function(){
        console.log('提交数据')
    }
}
exports.obj1 = obj

打印结果:

E:\nodejs\demo04>node commonjs02.js
{ obj1: { get: [Function: get], post: [Function: post] } }
  1. module.exports
let obj = {
    get:function(){
        console.log('从服务器获取数据')
    },
    post:function(){
        console.log('提交数据')
    }
}

// exports.obj1 = obj
module.exports = obj

打印结果

E:\nodejs\demo04>node commonjs02.js
{ get: [Function: get], post: [Function: post] }

模块中方法如果是独立的,则通过exports一个一个进行暴露,如果所有的方法都放在了一个对象里,通过module.exports统一暴露

除了前面提到的const a = require(模块), 还有另一种引用方法

当自定义模块存放在工程路径下的/node_modules 时,引用就可以直接使用模块文件夹的名称,无需使用相对路径

const axios = require('./node_modules/axios/index')

const axios = require('axios')   // 两种方法都可以引用同一个模块

但是需要注意的是,这种引用模块的方法默认是寻找axios文件夹下的index.js文件,如果没有该文件则会报错

如果非要用别的名字命名,可以在插件所在目录中使用cmd命令输入 npm init --yes,执行完毕后可以使用模块文件夹名称来引用模块

npm与包

完全符合CommonJs规范的包目录一般包含如下文件

  • package.json: 包描述文件
  • bin 用于存放可执行二进制文件的目录
  • lib: 用于存放JavaScript代码的目录
  • doc: 用于存放文档的目录

在工程目录下使用 npm init --yes命令后即可使用npm安装第三方模块

npm i 命令可以查找package.json中工程的依赖,并且为该工程重新安装这些依赖 发送工程文件给他人时,一般不发送node_modules文件夹

npm命令

安装模块 npm install 模块 --save 把依赖添加到package-json文件中 -g 全局安装

指定版本安装模块 npm install 模块@版本号

卸载模块 npm uninstall 模块

查看当前目录下安装的node模块 npm list

package.json

使用 npm init 或者 mpn init --yes生成该文件

{
  "name": "demo05",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "dependencies": { // 依赖
    "md5": "^2.3.0",
    "silly-datetime": "^0.1.2"
  },
    "devDependencies":{ //工具
        
    }
}

dependencies下版本号前的标识符意义

  • ^表示第一位版本号不变,后面两位取最新
  • ~表示前两位不变,最后一个取最新
  • *表示全部取最新

fs模块

操作文件的模块

  1. fs.stat 检测某一path是文件还是目录

    该方法接收两个参数,一个是string类型的path, 另一个是回调函数,回调函数中包括两个参数,分别是error和data,data返回文件或者目录的具体信息

fs.stat('./html', (err, data) => {
    if(err){
        console.error(err)
        return
    }
    console.log(`是文件:${data.isFile()}`)
    console.log(`是目录:${data.isDirectory()}`)
})

​ 输出结果:

E:\nodejs\demo06>node app.js
是文件:false
是目录:true
  1. fs.mkdir 创建目录

    该方法接收两个参数,一个是要创建的文件夹的路径,另一个是回调函数,回调函数中有一个参数error,错误信息(该方法还有其他options参数)

fs.mkdir('./css', err=>{
    if(err){
        console.log(err)
        return 
    }
    console.log('创建成功')
})
  1. fs.writeFile 创建写入文件

    该方法接受三个参数,一个是要创建的文件的路径,第二个是写入文件的内容,最后一个是回调函数

    如果多次调用该方法,则文件中的内容会被覆盖

fs.writeFile('./html/index.html', 'nihao', err => {
    if (err) {
        console.log(err)
        return
    }
})
  1. fs.appendFile 追加文件

    该方法接受3个参数,第一个是追加内容的文件的路径,第二是追加的内容,最后一个是回调函数

    如果要追加的文件不存在,则会自动创建这个文件,多次执行该方法则会一直在文件中追加内容(不覆盖)

fs.appendFile('./css/base.css', 'body{color:red}', err => {
    if (err) {
        console.log(err)
        return
    }
    console.log('append成功')
})
  1. fs.readFile读取文件

    该方法接受2个参数,第一个是要读取的文件,第二个参数是回调函数,回调函数内有两个参数,一个是error,另一个是data,表示读取到的内容

fs.readFile('./html/index.html', (err, data)=>{
    if (err) {
        console.log(err)
        return
    }
    console.log(data)
    console.log(data.toString())
})

​ 返回结果:

E:\nodejs\demo06>node app.js
<Buffer 6e 69 68 61 6f>

此处要注意的是,读取到的内容为16进制的buff数据,要转换为String数据使用toString方法即可。

  1. fs.readdir 读取目录

    该方法接受两个参数,一个是读取目录的路径,另一个是回调函数,err和data

    fs.readdir('./html', (err, data)=>{
        if (err) {
            console.log(err)
            return
        }
        console.log(data)
    })
    

    返回结果

    E:\nodejs\demo06>node app.js
    [ 'index.html' ]
    
  1. fs.rename 重命

    该方法接受三个参数,第一个是修改前的文件名,第二个是修改后的文件名,第三个是回调函数err

    fs.rename('./html/index.html', './html/index2.html', err=>{
        if (err) {
            console.log(err)
            return
        }
    })
    

    同时该函数也可以实现文件移动操作

    fs.rename('./css/index.css', './html/index.css', err=>{  // 此处前后路径不一样,也可以一边移动一边改名
        if (err) {
            console.log(err)
            return
        }
    })
    
  1. fs.rmdir 删除目录

    该方法接受两个参数,第一个是要删除的路径,第二个是回调函数

    fs.rmdir('./test', err=>{
        if (err) {
            console.log(err)
            return
        }
    })
    

    要注意的是,如果目录中含有目录或者文件,则无法删除该目录

  2. fs.unlink 删除文件

    该方法接受两个参数,第一个是要删除的文件,第二个是回调函数err

    fs.unlink('./html/test.js', err=>{
        if (err) {
            console.log(err)
            return
        }
    })
    

fs中的方法是异步方法

async、await和Promise

在没有promise之前,要获得异步方法内的值,要通过callback回调函数来获取

es6之后:

let p = new Promise(function(resolve, reject){
    setTimeout(function(){
        let name = '张三'
        resolve(name)
    }, 1000)
})

p.then(function(data){
    console.log(data)
})

async用于声明一个异步的function。 await用于等待一个异步方法执行完毕,await必须用在async定义的方法里面

// 普通方法
function test(){
    return 'wd'
}
console.log(test())
wd


// 异步方法
async function test(){
    return new Promise((res, rej)=>{
        setTimeout(()=>{
            let name = 'zhangsan'
            res(name)
        }, 1000)
    })
}

async function main(){
    let data = await test()
    console.log(data)
}
main()

异步获取目录数组

let dirArr=[]
let path = '/wwwroot'

async function isDir(path) {
    return new Promise((res, rej) => {
        fs.stat(path, (error, stats) => {
            if (error) {
                return
            }
            if (stats.isDirectory()) {
                res(true)
            } else {
                res(false)
            }
        })
    })
}
// 要注意的是await要用在async里面,即await外部的方法就必须是async
function main(){  
    fs.readdir(path, async (err, data)=>{
        if(err) return 
        for(let i = 0; i < data.length; i++){
            if(await isDir(`${path}/${data[i]}`)){
                dirArr.push(data[i])
            }
        }
        console.log(dirArr)
    })
}
main()

以流的方式读取写入文件

读取文件——读取流

const fs = require('fs')

let readStream = fs.createReadStream('./data/12345.pdf') // 创建一个读取流
let count = 0  // 流读取文件是以部分读取的方式进行,监听读取了几次
let str = ''   // 部分读取,把部分读取出来的内容存放到str里面去
readStream.on('data', data=>{
    str+=data // 把内容拼接到data中
    count++
})

readStream.on('end', ()=>{
    // console.log(str)
    console.log(count)
})

写入文件——写入流

const fs = require('fs')
let str = ''

for(let i = 0; i < 500; i++){
    str+='我很快就学会node.js了!\n'
}
let writeStream = fs.createWriteStream('./data/output.txt') // 创建写入流
writeStream.write(str) // 以流的方式异步写入文件

//标记写入完成
writeStream.end('说完了')

// 如果要监听写入完成事件必须有writeStream.end('说完了') 
writeStream.on('finish', ()=>{
    
    console.log('写入完成')
})

管道流

const fs = require('fs')
let readStream = fs.createReadStream('./data/12345.pdf')
let writeStream = fs.createWriteStream('./data/54321.pdf')
readStream.pipe(writeStream)  // 读取流的pipe方法

http,url,fs等模块搭建一个简单http服务器

页面跳转的实现

导入

  1. 获取url后面传进来的地址

    • 对获取的url进行解析,强跳转index.html
    • 获取url的后缀,并且使用过滤器过滤,使css、js等文件正常访问
  2. 通过fs模块读取文件

    • 过滤掉无关的url(/favicon.ico)
  3. 把获取到的url拼接到文件读取流的参数中,从本地读取html,css等文件

const http = require('http')
const fs = require('fs')
const common = require('./module/common')
const path = require('path')
const url = require('url')
http.createServer(function (req, res) {
    // 1. 获取地址
    let pathName = url.parse(req.url).pathname
    // 判断url是否为/ 如果是的话就把'/index.html'赋给pathName,强行跳转index
    pathName = pathName === '/' ? '/index.html' : pathName
    // 通过path模块获取url的后缀名
    let extName = path.extname(pathName)
    // 2. 通过fs模块读取文件,
    if (pathName !== '/favicon.ico') { // 先过滤掉其他无关的url
        // 把获取到的url拼接到文件读取流的参数中,读取HTML文件
        fs.readFile(`./static${pathName}`, (err, data) => {
            if (err) {
                res.writeHead(404, { 'Content-Type': 'text/html;charset="utf-8"' })
                res.end('404')
            }
            //过滤文件,使不同的静态文件都可以以正确的文件头渲染
            let mime = common.getMime(extName)
            res.writeHead(200, { 'Content-Type': ''+mime+';charset="utf-8"' })
            res.end(data)
        })
    }

}).listen(8111)

common模块:

//单独函数使用exports导入
exports.getMime=function(extname){
    switch(extname){
        case '.html': return 'text/html'
        case '.css': return 'text/css'
        case '.js': return 'text/javascript'
        default:
            return 'text/html'
    }
}
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,884评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,755评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,369评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,799评论 1 285
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,910评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,096评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,159评论 3 411
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,917评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,360评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,673评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,814评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,509评论 4 334
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,156评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,882评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,123评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,641评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,728评论 2 351