内置模块Path
常见的内置模块
path模块
- path模块用于对路径和文件进行处理,提供了很多好用的方法
- 并且我们知道在Mac OS,Linux和window上的路径时不一样的
- window上会使用\或者\来作为文件路径的分隔符,现在的window也支持/;
- 在Mac OS,Linx的Unix操作系统上使用/来作为文件路径的分隔符;
- 由于操作系统的分隔符不一样,如果我们将window使用的分隔符部署到linux上面会运行不了,我们就需要path模块解决这个问题,而不要使用固定的拼接
- 扩展:可移植操作系统接口(Portable,Operating System Interface,缩写为POSIX) Linux和Mac OS实现了POSIX接口,Window部分电脑实现POSIX
const path=require('path')
const basePath="user/why";
const filename="abc.txt";
const filepath=path.resolve(basePath,filename);
path其他方法
cosnt path=require('path')
//1.获取路径的信息
cosnt filepath="/user/why/abc.txt";
console.log(path.dirname(filepath));//路径:/user/why
console.log(path.basename(filepath));//文件名字:abc.txt
console.log(path.extname(filepath))//后缀名:.txt
//2.join路径拼接,相对于比较笨重,给他什么路径就拼接字符串加分隔符
const basepath='User/why';
const filename='abc.txt';
const filepath=path.join(basepath,filename);
console.log(filepath)//User/why/abc.txt
//3.resolve路径拼接
//resolve会判断拼接的路径字符串中,是否有以/或./或../开头的路径,如果有表示这是一个绝对路径,会返回对应的拼接路径,如果没有就会和当前执行文件所在的文件夹进行路径的拼接
const basepath='User/why';
const filename='abc.txt';
const filepath2=path.resolve(basepath,filename)
console.log(filepath2) // User/why/abc.txt
const basepath2='./User/why';
const filename2='abc.txt';
const filepath2=path.resolve(basepath,filename)
console.log(filepath2) // /Users/pyk/Node/05/内置模块/User/why/abc.txt
const basepath3='/User/why';
const filename3='/pyk/abc.txt';
const filepath3=path.resolve(basepath3,filename3)
//如果filename3是/开头,就会把他当成绝对路径输出 /pyk/abc.txt
//如果filename3是./开头,就会把他当成相对路径,输出/User/why/pyk/abc.txt
//如果filename3是../开头,就会去寻找上一位路径拼接,输出/User/pyk/abc.txt
console.log(filepath3)
内置模块fs
- fs的是File System的缩写,表示文件系统
- 对于任何一个服务器语言或者框架通常都会有自己的文件系统
- 因为服务器需要将各种数据,文件等放置到不同的地方
- 比如用户数据可能大多数是放到数据库中的
- 比如某些配置文件或者用户资源都是以文件的形式存在于操作系统上的
- Node也有自己的文件系统操作模块,就是fs
- 借助于Node帮我们封装的文件系统,我们可以在任何的操作系统(window,Mac OS,Linux)上面直接去操作文件
- 这也是Node可以开发服务器的一大原因,也是它可以成为前端自动化脚本等热门工具的原因
- Node的文件系统API非常的多,没必要一个一个去学习,等用到的时候再去查询 http://nodejs.cn/api/fs.html
- 但是这些API都提供了三种操作方式
- 方式一:同步操作文件:代码会被阻塞,不会继续执行
- 方式二:异步回调函数操作文件,代码不会被阻塞,需要传入回调函数,当获取到结果时,回调函数被执行
- 方式三:异步Promise操作文件:代码不会被阻塞,通过fs.promsie调用方法操作,会返回一个Promise,可以通过.then,.catch进行处理
const fs=require('fs')
//案例:读取文件的信息
cosnt filepath="./abc.txt"
//方式一:同步操作
const info=fs.statSync(filepath);
console.log("这里的代码会等上面的代码操作完才会执行")
//方式二:异步操作
fs.stat(filepath,(err,info)=>{
if(err){
console.log(err)
return
}
console.log(info)
})
console.log("这里的代码会直接执行,不用等待")
//方式三:promise
fs.promise.stat(filepath).then(info=>{
console.log(info)
}).catch(err=>{
console.log(err)
})
console.log("这里的代码会直接执行,不用等待")
文件描述符
- 在POSIX系统上,对于每个进程,内核都维护着一张当前打开着的文件和资源表格
- 每个打开的文件都分配了一个成为文件描述符的简单的数字标识符
- 在系统层,所有文件系统操作都使用这些文件描述符来标识和跟踪每个特定的文件
- 在window系统使用了一个虽然不同但概念上类似的机制跟踪资源
- 为了简单用户的工作,Node.js抽象出操作系统之间的特定差异,为所有打开的文件分配一个数字型号的文件描述符号
- fs.open()方法用于分配新的文件描述符,一旦被分配,则文件描述符可用于从文件读取数据,向文件写入数据,或请求关于文件的信息
const fs=require('fs');
fs.open('./abc.txt',(err,fd)=>{
if(err){
console.log(err);
return
}
console.log(fd);
//通过描述符获取文件的信息fd
fs.fstat(fd,(err,info)=>{
//获取到文件的信息
console.log(info)
})
})
文件的读写
- 如果我们希望对文件的内容进行操作,这个时候可以使用文件的读写
- fs.readFile(path[,options],callback):读取文件的内容
- fs.writeFile(file,data[,options],callback):在文件中写入内容;
- options:flag常见取值 w打开文件写入,默认值;w+打开文件进行读写,如果不存在则创建文件,r+打开文件进行读写,如果不存在则抛出异常,r打开文件读取,读取时的默认值;a打开要写入的文件,将流放在文件末尾,如果不存在则创建文件,a+打开文件以进行读写,将流放在文件末尾,如果不存在则创建文件
//abc.txt
哈哈
//index.js
const fs=require('fs')
const content="你好,pyk"
fs.writeFile('./abc.txt',content,{flag:"a"},err=>{
console.log(err)
})
//abc.txt里面是 哈哈你好,pyk
- options:encoding选项
- 目前基本都是UTF-8编码
fs.readFile('./abc.txt',(err,data)=>{
console.log(data)//不传递encoding默认返回Buffer
})
fs.readFile('./abc.txt',{encoding:'utf-8'},(err,data)=>{
console.log(data)//正常读取 你好pyk
})
文件夹操作
const fs=require('fs')
const path=require('path')
//1.创建文件夹
const dirname="./why";
if(!fs.existsSynv(dirname)){//如果文件不存在就创建这个文件
fs.mkdir(dirname,err=>{
console.log(err)
})
}
//2.读取文件夹中的所有文件包括文件夹
fs.readdir(dirname,(err,files)=>{
console.log(files)
})
//文件夹有子文件夹的情况读取所有文件,需要先判断每个文件是不是文件夹是的话就再遍历这个文件夹
//dirname参数是文件夹
function getFiles(dirname){
//withFileTypes:true得到的files是一个对象,他会有isDirectory方法判断它是不是一个文件夹,是的话就递归调用
fs.readdir(dirname,{withFileTypes:true},(err,files)=>{
//遍历每一个文件
for(let file of files){
if(file.isDirectory){
//把父文件夹的路径和子文件夹的路径进行拼接
const filepath=path.resolve(dirname,file.name)
getFiles(filepath)
}else{
console.log(file.name)
}
}
})
}
getFiles(dirname)
//3.文件夹的重命名
//第一个参数的旧名字,第二个参数是新名字
fs.rename("./why","./kobe",err=>{
console.log(err)
})
文件夹复制案例:将30个day文件夹视频复制
const fs=require('fs')
const path=require('path')
//程序运行时候传入起始路径和目前路径
//获取起始路径
const srcDir=process.argv[2];
//获取目标路径
const destDir=process.argv[3];
let i=0;
while(i<30){
i++;
//padStart(2,0)的意思是i有几位,不足用0补齐,
const num='day'+(i+'').padStart(2,0);
//起始路径拼接
const srcPath=path.resolve(srcDir,num);
//目标路径拼接
const destPath=path.resolve(destDir,num);
//continue 结束本次循环,而不终止整个循环的执行。而是结束本次循环,进行下一次循环。
//判断文件夹是否存在,存在就说明之前已经创建过了
if(fs.existsSync(destPath)){continue}
fs.mkdir(destPath,(err)=>{
if(!err){
console.log('文件创建成功开始拷贝',num)
}
//遍历目录下的所有的文件
const srcFiles=fs.raeddirSync(srcPath)
//循环每一个文件
for(cosnt file of srcFiles){
//判断是否是mp4文件
if(file.endsWith('mp4')){
//拷贝源
const srcFile=path.resolve(srcPath,file)
//拷贝目的路径和名字
const destFile=path.resolve(destPath,file);
//拷贝的函数
fs.copyFileSync(srcFile,destFile);
console.log('拷贝成功')
}
}
})
}
events模块
- Node中的核心API都是基于异步事件驱动的:
- 在这个体系中,某些对象(Emitters)发出某一个事件
- 我们可以监听这个事件(监听器Listeners),并且传入的回调函数,这个回调函数会在监听到事件时调用;
- 发出事件和监听事件都是通过EventEmitter类来完成的,它们都属于event对象
- emitter.on(eventName,listener):监听事件,也可以使用addListener
- emitter.off(eventName,listenet):移除事件监听,也可以使用removeListener
- emitter.emit(eventName[,…args]):发出事件,可以携带一些参数
const EventEmitter =require('events');
//1.创建发射器
const emitter=new EventEmitter();
//2.监听某一个事件
//addListtener是on的alias简写:
emitter.on('click',(args1,args2,arg3)=>{
//'coderwhy','james','kobe'
console.log('1监听到click事件',args1,args2,args3)
})
const listeners2=(args1,args2,args3)=>{
console.log('2监听到click事件',args1,args2,args3)
}
emitter.on('click',listeners2)
const listeners3=function(arg1,arg2,arg3){
console.log(arguments)//可以打印出'coderwhy','james','kobe',因为这个不是箭头函数,有this指向
console.log('3监听到click事件',args1,args2,args3)
}
emitter.on('click',listeners3)
//3.发出一个事件
setTimeout(()=>{
emitter.emit('click','coderwhy','james','kobe');//这里会触发上面两次监听器
emitter.off('click',listeners2)
emitter.emit('click','coderwhy','james','kobe');//这里只会触发一个监听器,还有一个被取消了
},1000)
const EventEmitter =require('events');
//1.创建发射器
const emitter=new EventEmitter();
//2.监听某一个事件
emitter.on('click',(args)=>{
console.log('1监听到click事件',args)
})
emitter.on('tap',(args)=>{
console.log('1监听到click事件',args)
})
//3.获取注册的事件
console.log(emitter.eventNames())//获取注册的事件 ['click','tap']
console.log(emitter.listenerCount(click))//获取注册事件的数量
console.log(emitter.listeners('click'))//获取具体的函数
不太常用的emitter方法
const EventEmitter =require('events');
//1.创建发射器
const emitter=new EventEmitter();
//2.监听一次
emitter.once('click',(args)=>{
console.log('1监听到click事件',args)
})
//将本次监听放在最前面执行
emitter.prependListener('click',(args)=>{
console.log('1监听到click事件',args)
})
//3.发出一个事件
setTimeout(()=>{
//移除所有监听器
emitter.removeAllListeners();
//移除指定监听器
emitter.removeAllListeners('click');
emitter.emit('click','coderwhy');
},1000)
共享你的代码
- 我们已经掌握了如何在javascript中可以通过模块化的方式将代码划分为一个个小的结构;
- 在以后的开发过程中可以通过模块化的方式封装自己的代码,并且封装成一个工具;这个工具我们可以让同事通过导入的方式进行共享,如果我们要分享给世界的程序员使用,我们可以上传到github,其他程序员通过github下载我们的代码手动的引用,但是这样也带来了问题,要让大家都指定你的github地址,还要手动去下载,在不需要的时候,还要手动删除相关的依赖,当遇到包升级的时候,还需要重复操作,这样子非常的繁琐,由此就需要一个专业的工具进行对包的管理,安装,升级,删除
包管理工具npm
- 包管理工具npm:
- Node Package Manager,也就是node包管理器
- 但是目前已经不仅仅是Node包管理器了,在前端项目中我们也在使用它来管理依赖的包
- 比如express,koa,react,react-dom,axios,babel,webpack等等
- npm管理的包在http://www.npmjs.com/可以查看,搜索
- npm管理的包实际上存在registry上面的
- 我们包装一个包其实也是从registry上面下载的,切换淘宝镜像时候也是有registry这个关键字的
项目配置文件
- 事实上,我们每一个项目都会有一个对应的配置文件,无论是前端项目还是后端项目
- 这个配置文件会记录着你项目的名称,版本号,项目描述等
- 也会记录着你项目所依赖的其他库的信息和依赖库的版本号
- 这个配置无聊在Node环境下(无论是前端还是后端)就是package.json
- npm init 生成package.json,要自己填写信息
- npm init -y 所有信息使用默认的
- 必须填写的属性:
- name,version
- name是项目的名称
- version是当前项目的版本号
- descripttion是描述信息,很多时候是作为项目的基本描述
- author是作者相关信息(发布时用到)
- license是开源协议
- 非必须填写属性:
- private属性:记录当前的项目是否是私有的;当值为true时,npm是不能发布它的,这是防止私有项目或模块发布出去的方式
- dependencies属性,是指无论是开发还是生产环境都需要依赖的包;通常是我们项目实际开发用到的一些库模块
- devDepencies属性:一些包在生产环境是不需要的,比如webpack,Babel等,这个时候,我们会通过npm install --save-dev,将它安装到devDepencies属性中,这个东西在前端项目不大,因为在打包的时候,webpack会根据依赖关系进行打包,如果服务器在生产环境保证不安装这些包,我们需要在服务器通过npm install --production来安装文件的依赖,就不会安装devDepencies的包
- 在项目里面package.json的main属性:在前端项目里面用处不大,设置程序的入口,但是实际打包是webpack的mian生效,而package.json里面的main是在你发布一个模块的时候用到,例如我们使用const axios require(‘axios’) 这里就会去查找axios的package.json里面的main属性
- script属性:本质上是一个脚本命令,在命令行输入npm run start 会去执行scripe里面的start对应的值,当作命令,对于常用的start,test,stop,restart可以省去run,直接npm start运行
版本管理的问题
- 发现安装的版本依赖出现:^2.0.3~2.0.3,这是什么意思呢?
- npm的包通常需要遵守semver版本规范
- semver版本规范:2.0.3(x,y,z)
- X主版本号(major):当你做了不兼容的API修改(可能不兼容之前的版本)
- Y次版本号(minor):当你做了向下的功能性新增(新功能增加,但是兼容之前的版本)
- Z修订号:当你做了向下兼容的问题修正(没有新功能,修复了之前的bug)
- 解释一下^结合~的区别
- ^x.y.z表示x是保持不变的,y和z永远是最新的版本
- ~x.y.z:表示x和y保持不变的,z永远是最新的版本
常见的属性
- engines属性用于指定node和NPM的版本号,在安装的过程中,会先检查对应的引擎版本,如果不符合就会报错,事实上也可以指定所在的操作系统"os":[’'darwin",“linux”],只是很少用到
- browserslist属性:用于配置打包后的javascript浏览器情况,参考。否则我们需要手动的添加polyfills来支持某些语法,也就是说它是为webpack等打包工具服务的一个属性
npm install命令
- 安装npm包分两种情况
- 全局安装(globa install):npm intall yam -g;全局安装是直接将某个包安装到全局,通常很多人都对全局安装有一个误解;使用npm全局的都是一些工具包,yarn,webpack等,并不是axios,exress,koa等库文件;所以全局安装以后并不能让我们在所有的项目中使用axios,因为require(‘axios’)并不会全局查找,而webpack是会安装配置到环境变量
- 局部(项目)安装(local install): npm isntall axios;项目安装会在当前目录下生产一个node_modules文件夹,我们之前讲解require查找顺序时有讲解过这个包什么情况下被查找。局部安装分为开发时依赖和生产时依赖:
安装开发和生产依赖
npm install axios
npm i axios开发者
npm intall axios --save-dev
npm intall axios -D
npm i axios -D
根据package.json中的依赖包安装 npm install
npm install 原理
很多同学之前应该已经会了npm install,
- npm原理图解析
npm install会检测是有package-lock.json文件: - 没有lock文件:
1.分析依赖关系,这是因为我们可能会包会依赖其他关系的包,并且多个包之间会产生相同依赖的情况;
2.从registry仓库中下载压缩包(如果我们设置了镜像,那么会从镜像文件夹下载安装包)
3.获取到压缩包后回对压缩包进行缓存(从npm5开始有的);
4.将压缩包解压到项目的node_module文件夹中(前面我们讲过,require的查找顺序会在该包下面查找)
5.生成package-lock.json文件- 有lock文件:
1.检测lock中的包的版本是否和package.json中一致(会按照semver版本规范检测),不一致的情况,那么会重新构建依赖关系,直接走顶层的流程
2.一致的情况,会去优先查找缓存
没有找到,会从registry仓库下载,直接走顶层的流程
查找到,会获取缓存中的压缩文件,并且将压缩文件解压到node_module文件夹中
- 有lock文件:
packge-lock.json
- package-lock.json文件解析;
- name:项目的名称:
- version:项目的版本
- lockfileVersion:lock文件的版本
- require:使用requires来跟着模块的依赖关系
- dependencies:项目的依赖
1.当前项目依赖axios,但是axios依赖follow-redireacts;
2.axios的属性如下:
version表示实际安装的axios的版本
resolved用来记录下载的地址,registry的仓库中的位置
requires记录当前模块的依赖
integrity用来从缓存中获取索引,再通过索引去获取压缩包文件
npm其他命令
卸载某个依赖包:
npm uninstall package
npm uninstall pack --save-dev
npm uninstall packge -D
强制重新构建build
npm rebuild
清楚缓存
npm cache clean
Yarn工具
- 另一个包管理工具yarn:
- yarn事故facebook,google,exponent和tilde联合推出的一个新的JS包管理工具;
- yarn是为了弥补npm的一些缺陷而出现的;
- 早期的npm存在很多缺陷,比如安装依赖速度很慢,版本依赖混乱等一系列问题;虽然从npm5版本开始,进行了很多的升级和改进,但是依然很多人喜欢使用yarn
cnpm工具
- 由于一些特殊原因,某些情况下我们没办法很好的从https://registry.npmjs.org下载下来一些需要的包。
- 查看npm镜像:npm config get registry
- 我们可以直接设置npm的镜像
- npm config set registry https://registry.npm.taobao.org
但是对于大部分来说不希望将npm镜像修改了
第一:不太希望随意修改npm原本从官方下来波的渠道
第二:担心某天淘宝的镜像挂了或不维护了,又要改来改去
这个时候,我们可以使用cnpm,并且将cnpm设置为淘宝的镜像
//下载并设置镜像npm install - g cnpm --registry=https://registry.npm.taobao.org
//查看镜像 cnpm config get registry #https://r.npm.taobao.org
npx工具
npx是npm5.2之后自带的一个命令
- npx的作用非常多,但是比较常见的是使用它来调用项目中的某个模块的指令