node的基础知识 | 青训营笔记

107 阅读8分钟

这是我参与「第四届青训营 」笔记创作活动的的第10天

本篇主要讲 node 的一些基础知识。

安装

//查询安装的node.js版本只需要在终端运行:
node -v
//即可查询到电脑所安装的版本号

fs文件系统模块

  • fs.writeFile()读取文件

    fs.readFile(path[,options],callback)
    

    案例:

    // 导入 fs 模块,来操作文件
    const fs = require('fs')
    ​
    // 调用方法读取文件
    // 参数1:读取文件的存放路径
    // 参数2:读取文时采用的编码格式,一般默认指定 utf-8
    // 参数3:回调函数,拿到读取失败和成功的结果 err datatStr
    fs.readFile('./11.txt','utf-8',(err,dataStr)=>{
        // 打印失败的结果
        console.log(err)
        console.log('-----------')
        console.log(dataStr)
    })
    //如果成功,则err的值为null,
    //如果失败,则err打印的为错误对象,dataStr的值为 undefined
    

    判断文件是否读取成功

    const fs = require('fs')
    ​
    fs.readFile('./1.txt','utf-8',(err,dataStr)=>{
        if(err){
            return console.log('文件读取失败'+err.message);
        }else{
            console.log('文件读取成功'+dataStr);
        }
    })
    
  • fs.writeFile写入文件

    fs.writeFile(path,data[,options],callback)
    

    案例:

    const fs = require('fs')
    ​
    // 参数一:表示文件的存放路径
    // 参数二:表示要写入的内容
    // 参数三:回调函数
    fs.writeFile('./2.txt','abcd',(e
                                   rr)=>{
        // 如果文件写入成功,则 err 的值等于 null
        // 如果文件写入失败,则 err 的值等于一个错误对象
        if(err){
            console.log('文件写入失败',err.message);
        }else{
            console.log('文件写入成功')
        }
    })
    

    练习:成绩管理

    // 导入需要的 fs 文件系统模块
    // 使用 readFile() 方法,读取素材目录下的 3.txt 文件
    // 判断文件读取是否失败
    // 文件读取成功后,处理成绩数据const fs = require('fs')
    ​
    fs.readFile('./3.txt','utf-8',(err,dataStr)=>{
        if(err){
            return console.log('文件读取'+err.message)
        }else{
            // console.log('文件读取读取成功'+dataStr)
    ​
            // 4.1先把成绩的数据,按照空格进行分隔
            const arrOld = dataStr.split(' ')
            // 4.2循环分割后的数组,每一项数据,进行字符串的替换操作
            const arrNew = []
            arrOld.forEach(item=>{
                arrNew.push(item.replace('=',':'))
            })
            // 4.3把新数组中的每一项,进行合并,得到一个新的字符串
            const newStr = arrNew.join('\r\n')
            // 4.4把处理完成的数据,写入到新文件当中
            fs.writeFile('./4.txt',newStr,'utf-8',(err)=>{
                if(err){
                    return console.log('文件写入失败'+err.message)
                }
                console.log('文件写入成功')
            })
        }
    })
    ​
    

path模块处理路径模块

路径拼接时的问题

//我们在使用fs操作文件时,如果提供的路径是相对路径,浏览器会帮我们动态拼接这个文件路径,例如:
//根路径为 PS D:\前端workspace\笔记>  
//然后fs模块中的路径为 ./01.txt
//此时,浏览器会帮我们拼接文件路径 D:\前端workspace\笔记\01.txt
//但如果路径拼接时搞错了根路径 比如D:\前端workspace
//此时拼接就会有问题

解决方式

  • 使用绝对路径

    这种方式的移植性非常的差,不利于后期的维护

  • 使用__dirname

//__dirname表示当前文件的存放路径
const fs = require('fs')
​
fs.readFile(__dirname+'./1.txt','utf-8',(err,dataStr)=>{
    if(err){
        return console.log('文件读取失败'+err.message);
    }else{
        console.log('文件读取成功'+dataStr);
    }
})

path模块的其他运用方法

  • path.join()

    path.join([...paths])
    

    案例:

    const path = require('path')
    ​
    // 注意:../ 会抵消前面的路径
    const pathStr = path.join('/a','/b/c','../','./d','/e')
    console.log(pathStr)// \a\b\d\econst pathStr2 = path.join(__dirname,'./1.txt')
    console.log(pathStr2)//D:\前端workspace\笔记\你懂毛啊\node\code\内置path模块\1.txt 
    
  • path.basename()

    // path.basename()获取路径中的文件名
    //使用path.basename()方法,可以获取文件路径的最后一部分,经常通过这个方法获取路径中的文件名
    //path.basename(path[,ext])
    //path<string> 必选参数,标识一个路径的字符串
    //ext<string>可选参数,表示文件的扩展名
    const fpath = '/a/b/c/index.html'
    var fullname = path.basename(fpath)
    console.log(fullname)//输出index.htmlvar nameWithoutExt = path.basename(fpath,'.html')
    console.log(nameWithoutExt)//输出index//path.extname()可以获取路径中的扩展名部分
    const fext = path.extname(fpath)
    console.log(fext)//输出.html
    

http模块

创建最基本的的服务器

// 1.导入 http 模块
const http = require('http')
// 2.创建 web 服务器实例
const server = http.createServer()
// 3.为服务器实例绑定 request 事件,监听客户端的请求
server.on('request',(req,res)=>{
    console.log('Someone visit our web server.')
})
// 启动服务器
server.listen(8080,()=>{
    console.log('server running at http://127.0.0.1:8080')
})

req和res对象

const http = require('http')
const server = http.createServer()
server.on('request',(req,res)=>{
    // req.url 是客户端请求的 url 地址
    const url = req.url
    // req.method 是客户端请求的 method 类型
    const method = req.method
    const str = `你请求的 url 是 ${url} , 然后,你解决的方法是 ${method}`
​
    // 调用 res.setHeader() 方法,设置 Content-type 响应头,解决中文乱码问题
    res.setHeader('Content-Type','text/html;charset=utf-8')
​
    // 调用 res.end() 方法,向客户端响应一些内容
    res.end(str)
})
server.listen(8080,()=>{
    console.log('server running at http://127.0.0.1:8080')
})

根据不同的url响应不同的html内容

const http = require('http')
const server = http.createServer()
server.on('request',function(req,res){
    const url = req.url
    let content = `<h1>404 NOT found!</h1>`//设置默认值为 404 NOT found
    if(url==='/'||url==='/index.html'){
        content = `<h1>首页</h1>`//用户请求的是首页
    }else if(url==='/about.html'){
        content = `<h1>关于页面</h1>`//用户请求的是关于页面
    }
    res.setHeader('Content-Type', 'text/html;charset=utf-8');
    res.end(content)
})
​
server.listen(8888,()=>{
    console.log('server running at http://127.0.0.1:8888');
})

模块化

模块的分类

  • 内置模块:*内置模块是由Node.js官方提供的,例如fs,path,http等
  • 自定义模块:*用户创建的每个js文件,都是自定义模块
  • 第三方模块:*由第三方开发的模块,并非是官方提供的内置模块,也不是用户创建的自定义模块,使用前需要下载

加载模块

//1.加载内置的fs模块
const fs = require('fs')
//2.加载自定义模块
const custom = require('./custom.js')
//3.加载第三方模块
const monment = require('moment')

模块作用域

//1.js
const username = '张三'function sayHello(){
    console.log('大家好,我是'+username)
}
//2.js
const custom = require('./1.js')
​
console.log(custom)// {}

打印的对象是一个空对象,由此可以得出模块作用域的好处:防止全局污染,就是防止多个模块中定义相同的变量或者函数名,导致变量或者函数名之间相互覆盖,甚至会导致程序员不知道调用的到底是哪个变量或者函数名

向外共享模块作用域中的对象

CommonJS 规范

Nodejs 遵循了 CommonJS 模块化规范,CommonJS 规定了模块的特性和各模块之间呼和相互依赖。

  • 每个模块内部,module 变量代表当前模块
  • module 变量是一个对象,它的 exports 属性(即 module.exports) 是对外的接口。
  • 加载某个模块,其实是加载该莫夸的 module.exports 属性。require 方法用于记载模块。
什么是module对象

每一个js自定义都有一个module对象,里面有很多属性,通过这些属性就能实现向外界共享数据

使用module.exports向外暴露对象
// 1.js
const username = '张三'function sayHello(){
    console.log('大家好,我是'+username)
}
​
module.exports = {
    username,
    sayHello
}
​
// 2.js
const custom = require('./1.js')
console.log(custom)
console.log(custom.username)

使用 require 方法引入自定义模块的时候,实际上得到的就是 module.exports 对象且永远以 module.exports 为准。

module.exports.const username = '张三'module.exports.function sayHello(){
    console.log('大家好,我是'+username)
}
​
module.exports = {
    name:'zs',
    sayhi:()={
        console.log('lalal')
    }
}
//此时,该文件向外暴露的对象是 有name和sayhi 这两个变量的对象
使用 exports 向外暴露对象

由于 module.exports 单词写起来比较复杂,为了简化向外共享成员的代码,Node 提供了 exports 对象。默认情况下,exports 和 module.exports 指向同一个对象。

console.log(module.exports === exports) // ture

但是,最终共享的结果,还是以 module.exports 指向的对象为准。

const username = '张三'function sayHello(){
    console.log('大家好,我是'+username)
}
​
exports = {
    username
}

上面这里,exports重新创建了一个对象并指向它,此时,exports 对象中的确有 username 这个属性,但是,module.exports对象中没有任何属性,所以引用过后是一个空对象

下面,我们可以进行一些小练习:

Snipaste_2022-08-22_16-05-42.png

Snipaste_2022-08-22_16-06-06.png

Snipaste_2022-08-22_16-06-28.png

Snipaste_2022-08-22_16-06-54.png

更多关于CommonJS具体的内容请参考CommonJS规范 -- JavaScript 标准参考教程(alpha) (ruanyifeng.com)

模块的加载机制

优先从模块中进行加载

模块在第一次加载之后会被缓存,这也意味着多次调用require()不会执行代码多次,注意,无论是内置模块,用户自定义模块,还是第三方模块,他们都会优先从缓存中加载,从而提高模块的加载效率。

//1.js
console.log('ok')
//2.js
require('./1.js')
require('./1.js')
require('./1.js')
//结果只会打印一次ok

内置模块的加载机制

内置模块是由node.js官方提供的模块,内置模块的加载优先级最高。

例如:require('fs')始终返回内置的fs模块,即使在node_modules目录下有名字相同的包也叫作fs

自定义模块加载机制

在使用require()加载自定义模块时,必须制定./../开头的路径标识符。在加载自定义模块时,如果没有制定./../这样的路径标识符,则node会把它当做内置模块第三方模块进行加载。

同时,在使用require()导入自定义模块时,如果省略了文件的扩展名,则node.js会按照顺序分别尝试加载以下的文件:

  • 按照确切的文件名进行加载
  • 补全.js扩展名进行加载
  • 补全.json扩展名进行加载
  • 补全.node扩展名进行加载
  • 加载失败,终端报错

第三方模块的加载机制

如果传递给require()的模块标识符不是一个内置模块,也没有以./../开头,则node.js会从当前模块的父目录开始,尝试从/node_modules文件夹中加载第三方模块。

例如,假设在C:\User\code\project\foo.js文件里调用了require('tools'),则node.js会按以下的顺序查找。

  1. C:\User\code\project\node_modules\tools
  2. C:\User\code\node_modules\tools
  3. C:\User\node_modules\tools
  4. C:\node_modules\tools

目录作为模块

模块加载机制2.png

当把目录作为标识符,传递给require()进行加载的时候,有三种加载方式:

  1. 在被加载的目录下查找一个叫做package.json的文件,并寻找main属性,作为require()加载的入口

模块加载机制1.png

一般来说,main节点中的入口是index.js

  1. 如果目录里没有package.json文件,或者main入口不存在或无法解析,则Node.js将会尝试加载目录下的index.js
  2. 如果以上两步都失败了,则Node.js会在终端打印错误消息,报告莫夸的缺失:Error:Cannot find module 'xxx'

结语

文章如果有不正确的地方,欢迎指正,共同学习,共同进步。

若有侵权,请联系作者删除。