Node.js与前端开发实战|青训营学习

158 阅读14分钟

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

node.js基本介绍

node.js是基于chrome V8引擎的js运行环境

  1. 浏览器是js的前端运行环境
  2. node.js是js的后端运行环境
  3. node.js中无法调用DOM和BOM等浏览器内置API

node.js的使用

node.js中执行js文件

  1. 先切换到文件目录下--> node js文件名
  2. 文件右键 powershell打开--> node js文件名

终端中的快捷键

  1. 向上箭头,快速定位到上一次执行的命令
  2. tab键,自动补全文件名
  3. esc键,快速清空当前输入的命令

fs文件系统模块

fs是node.js提供用来操作文件的模块,它提供了一系列的方法和属性,用来满足用户对文件操作的需求

在使用fs之前,需要先导入const fs = require('fs')

文件读取 fs.readFile()

fs.readFile(path[,options],callback) 可以读取指定文件的内容
参数:

  1. 参数1:必选参数,字符串,表示文件路径。
  2. 参数2:可选参数,表示以什么编码格式来读取文件,默认utf8
  3. 参数3:必选参数,文件读取完成后,通过回调函数拿到读取得结果,err失败 datastr成功
const fs = require('fs');
fs.readFile('./file/01.txt','utf8',function(err,dataStr){
    //打印结果
    //1.打印成功err值为 null
    //2.打印失败err的值为 错误对象,dataStr的值为 undefined
    console.log(err);
    //打印读取数据
    console.log(dataStr)
)

判断文件是否读取成功

const fs = require('fs');
fs.readFile('./file/01.txt','utf8',function(err,result){
if(err){
    return console.log("文件读取失败"+err.message);
)
console.log(result);

写入文件内容 fs.writeFile()

fs.writeFile(file,data[,options],callback) 可以读取指定文件的内容
参数:

  1. 参数1:必选参数,字符串,表示文件路径(不一定是存在的)
  2. 参数2:必选参数,表示写入内容
  3. 参数3:可选参数,表示以什么编码格式来读取文件,默认utf8
  4. 参数4:必选参数,文件写入后的回调函数

注意:只能创建文件,不能创建路径,新写入的内容会覆盖原先的内容

const fs = require('fs');
fs.writeFile('./file/01.txt','abcd','utf8',function(err){
    //写入结果
    //1.写入成功err值为 null
    //2.写入失败err的值为 错误对象
    console.log(err);
)

案例

arr.split(),arr.replace(),arr.join() 对数据进行处理

//引入fs模块
const fs = require('fs');
//读取文件
fs.readFile('./成绩.txt','utf8',function (err,dataStr) {
   //判断是否读取成功
   if(err){
      return console.log('读取失败'+err.message);
   }
//    console.log(dataStr);
   // 处理数据
   const arrOld = dataStr.split(' ');
   const arrNew =[];
   arrOld.forEach(item => {
      arrNew.push(item.replace('=',':'));
   })
   const newStr = arrNew.join('\r\n');
//    console.log(newStr);
   //将处理完成后的数据写入新的文件中
   fs.writeFile('./成绩-OK.txt',newStr,'utf8',function (err) {
      if(err){
            return console.log('写入失败');
      }
      console.log('写入成功');
   })
});

path路径模块

path模块是node.js官方提供的,用来处理路径的模块,它提供了一系列的方法和属性,用来满足用户对路径的处理需求

在使用path模块之前,需要先导入 const path = require('path')

路径问题

在使用fs模块操作文件时,如果提供的操作路径是以./或../开头的相对路径时,很容易出现路径动态拼接错误问题
原因:代码在运行时,会以执行node命令所在的目录动态拼接出被操作文件的完整路径
解决

  1. 使用完整路径,'C://files//01.txt',但移植性差不易于维护;
  2. 可使用'__dirname+'file/01.txt''进行拼接,'__dirname'表示当前js文件所在的目录;

路径拼接 path.join()

path.join([,path])用来将多个路径片段拼接成一个完整的路径字符串 '../'会抵消前面的一层路径

const pathStr = path.join('/a','/b/c','../',''/d),
console.log(pathStr);//\a\b\d

path.basename() 获取路径中的文件名

path.basename(path[,ext])用来从路径字符串中,将文件名解析出来

  1. 参数1:必选参数,表示一个路径的字符串;
  2. 参数2:可选参数,表示文件的扩展名;
const fpath = 'a/s/v/index.html'
const nameWithoutExt0 = path.basename(fpath);
console.log(nameWithoutExt0);//index.html
const nameWithoutExt1 = path.basename(fpath,'.html');
console.log(nameWithoutExt1);//index
const nameWithoutExt2 = path.basename(fpath, 'html');
console.log(nameWithoutExt2);//index.

path.extname() 获取路径中文件扩展名

path.extname(path) 用来获取文件扩展名,返回一个字符串

const fpath = 'a/s/v/index.html'
const ext1 = path.extname(fpath);
console.log(ext1);//.html

fs和path的综合案例

  1. 正则表达式
  2. fs模块
  3. path模块
  4. exec() 方法在一个指定字符串中执行一个搜索匹配。返回一个结果数组或 [null]
  5. writeFile只能创建文件,不能创建路径,新写入的内容会覆盖原先的内容
const fs = require('fs');
const path = require('path')

//正则表达式
const regStyle = /<style>[\s\S]*<\/style>/
const regScript = /<script>[\s\S]*<\/script>/

fs.readFile(path.join(__dirname, '../素材/index.html'), 'utf8', (err, dataStr) => {
      if (err) return console.log('读取失败');
      // console.log(dataStr);
      resolveCSS(dataStr);
      resolveJS(dataStr);
      resolveHTML(dataStr);
})

//写入CSS样式
function resolveCSS(htmlStr) {
      const r1 = regStyle.exec(htmlStr);
      const newCSS = r1[0].replace('<style>', '').replace('</style>', '');
      fs.writeFile(path.join(__dirname,'../素材/index.css'),newCSS,'utf8',err => {
            if(err) return console.log('写入CSS样式失败'+err.message)
            console.log('写入css样式成功');
      })
}
//写入JS脚本
function resolveJS(htmlStr) {
      const r1 = regStyle.exec(htmlStr);
      const newCSS = r1[0].replace('<script>', '').replace('</script>', '');
      fs.writeFile(path.join(__dirname,'../素材/index.js'), newCSS, 'utf8', err => {
            if (err) return console.log('写入JS脚本失败' + err.message)
            console.log('写入JS脚本成功');
      })
}
//写入HTML文件
function resolveHTML(htmlStr){
      const newHTML = htmlStr.replace(regStyle,'<link rel="stylesheet" href="./index.css">').
      replace(regScript,'<script src="./index.js"></script>');
      fs.writeFile(path.join(__dirname,'../素材/index.html'),newHTML,(err) => {
            if (err) return console.log('写入HTML失败' + err.message)
            console.log('写入HTML成功');
      })
}

http模块

http模块是node.js提供的用来创建web服务器的模块,通过http模块提供的http.createServer()方法,就能方便的把一台普通的电脑,变成一台web服务器,从而对外提供web资源服务

在使用http模块之前,需要引入 const http = require('http')

服务器的基本知识

  • IP地址:互联网上每台计算机上的唯一地址,通常使用“点分十进制”表示成(a.b.c.d)的形式,其中,abcd都是0-255之前的十进制整数;
  • 域名服务器:提供IP地址和域名之间转换服务的服务器;
  • 端口号:客户端发送过来的网络请求,通过端口号,可以准确的交给对应的web服务进行处理,在实际应用中,URL中的80端口可以被省略;

创建一个基本的web服务器

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

模块化

node.js 中根据来源的不同,将模块分为了三个大类,分别是:

  1. 内置模块:内置模块是由node.js官方提供的,例如fs,path,http
  2. 自定义模块:用户创建的每个js文件,都是自定义模块
  3. 第三方模块:由第三方开发出来的模块,并非官方提供的内置模块,也不是自定义模块,使用前需要先下载
//内置fs模块
const fs = require('fs')
//自定义模块,可以省略.js后缀名
const comment = require('./comment.js')
//第三方模块
const moment = require('moment')

使用require()方法加载其他模块是,会执行被加载模块中的代码

模块作用域

定义:和函数作用域类似,在自定义模块中定义的变量和方法等成员,只能在当前模块内被访问,这种模块级别的访问限制,就叫做模块作用域

作用:防止全局变量污染。<script>标签引入会造成全局变量污染,就是引入的js文件中的相同名称的变量会起冲突

module对象

在每一个.js自定义模块中都有一个module对象,它里面存储了和当前模块有关的信息

image.png

module.exports对象——向外共享模块作用域内的成员

在自定义模块中,可以使用module.exports对象,将模块内的成员共享出去,供外界使用。在外界用require()方法导入自定义模块时,得到的就是module.exports所指向的对象

exports对象

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

console.log(module.exports===exports);//true

module.exports 与 exports的区别

require()模块时,永远得到的是module.exports指向的对象
为了避免混乱,在同一个模块不要同时使用module.exports和exports

test01.js

exports.username = 'marry';
module.exports= {
      username:'tom',
      age:18
}
console.log(exports === module.exports);//false

exe01.js

const test = require('../素材/test')
console.log(test);//输出module.exports[{ username: 'tom', age: 18 }]

image.png

node.js中的模块化规范

node.js遵循了commonJS模块化规范,commonJS规定了模块的特性和各模块之间如何相互依赖

CommonJS规定

  1. 每个模块内部,module变量代表当前模块;
  2. module变量是一个对象,它的exports属性(即module.exports)是对外的接口
  3. 加载某个模块,其实是加载该模块的module.exports属性。require()方法用域加载模块

包与npm

node.js中的·第三方模块又叫做包 由于node.js的内置模块仅提供了一些底层的API,导致在基于内置模块进行项目开发的时候,效率很低。 包是基于内置模块封装出来的,提供了更高级,更方便的API,极大的提高了开发效率 包和内置模块之间的快洗,类似于jquery和浏览器内置API之间的关系

包在哪下载 搜索包

在项目中安装包的命令npm install 包的完整名称

安装指定版本的包npm i 包名@版本号

一次安装多个包,包名用空格分开

版本号介绍

  1. 第一位数字:大版本
  2. 第二位数字:功能版本
  3. 第三位数字:bug修复版本

版本号提升规则:只要前面的版本号增长了,则后面的版本号归零

//导入需要的包
const moment = require(''moment);
//查阅文档搜索包的用法
const dt = moment().format('YYYY-MM-DD HH:mm:ss');

初次安装包后多了哪些文件

  1. node_modules文件夹用来存放所有已安装到项目中的包,require()导入第三方包时,就是从这个目录中查找并加载包
  2. package-lock.json配置文件用来记录node_modules目录下的每一个包的下载信息,例如报的名字、版本号、下载地址等

包管理配置文件

npm规定,在项目根目录中,必须提供一个叫做package.json的包管理配置文件。用来记录与项目有关的一些配置信息。例如:

  • 项目名称、版本号、描述等
  • 项目中都用到了哪些包
  • 哪些包只在开发期间会用到
  • 哪些包在开发和部署时都需要用到

快速创建package.json

命令:npm init -y

上述命令只能在英文的目录下成功运行,所以,项目文件夹的名称一定要使用英文命名,不要使用中文,不能出现空格

如果希望一次性安装所有的包,可以运行npm install(npm i) image.png

卸载包

命令:npm uninstall 包名

devDependencies节点和dependencies节点

如果某些包只在项目开发阶段会用到,在项目上线之后不会用到,则建议把这些包记录到devDependencies节点中 如果某些包在开发和项目上线之后都需要用到,则建议把这些包记录到dependencies节点

  1. 安装指定的包,并记录到devDependencies节点中npm i 包名 -D
  2. 上述命令是简写形式,等价于npm install 包名 --save-dev

解决下包速度慢的问题

image.png 切换npm的下包镜像源

//查看当前的下包镜像源
npm config get registry
//将下包的镜像源切换为淘宝镜像源
npm config set registry=http://registry.npm.taobao.org/
//检查镜像源是否下载成功
npm config get registry

nrm——一个切换下包的镜像源

为了更方便的切换下包的镜像源,我们可以安装nrm这个小工具,利用nrm提供的终端命令,可以快速查看和切换下包的镜像源

//通过npm包管理器,将nrm安装为安全可用的工具
npm i nrm -g
//查看所有可用的镜像源
nrm ls
//将下包的镜像源切换为taobao镜像
nrm use taobao

image.png

image.png

项目包和全局包

项目包

那些被安装在项目的node_modules目录下的包都是项目包

  1. 开发依赖包:被记录到devDependencies节点中的包,只在开发期间会用到
  2. 核心依赖包:被记录在dependencies节点中的包,在开发期间和项目上线之后都会用到
//开发依赖包
npm i 包名 -D
//核心依赖包
npm i 包名

全局包

在执行npm i 命令时,如果提供了-g参数,则会把包安装为全局包 全局包会被安装在C:\Users\admin\AppData\Roaming\npm\node_modules目录下

  1. 全局安装指定包: npm i 包名 -g
  2. 卸载全局安装的包: npm uninstall 包名 -g

i5ting_toc——一个可以把md文档转为html页面的一个小工具

使用步骤如下:

//将i5ting_toc安装为全局包
npm install -gi5ing_toc
//调用i5ting_toc,将md->html
i5ing_toc -f 要转换的md文件路径 -o

规范包的结构

一个规范的包,它的组成结构,必须符合以下三点要求:

  1. 包必须以单独的目录而存在
  2. 包的顶级目录下要必须包含package.json这个包管理配置文件
  3. package.json中必须包含name,version,main这三个属性,分别代表包的名字,版本号,包的入口

更多参考规范

开发属于自己的包

示例

  1. 初始化包的基本结构
    • 新建xxx文件夹,作为包的根目录
    • 在xxx文件夹中,新建【package.json,index.js,README.md】
  2. 初始化package.json(npm中搜索一下有没有撞名)
{
    "name": "xxx",
    "versoin": "1.0.0",
    "main": "index.js",
    "description": "提供了格式化时间,HTMLEScape的功能",
    "keywords": ["xxx","dateFormat","escape"],
    "license": "ISC"
}

更多license

  1. 在index.js中定义格式化时间的方法

image.png 5. 在index.js中定义转义HTML的方法 image.png 6. 将不同功能进行模块化拆分 image.png

  1. 编写包的说明文档README.md: 安装方式、导入方式、格式化时间、转义HTML中的特殊字符、还原HTML中的特殊字符、开源协议
  2. 发布包
    • 注册npm账号
    • 登录npm账号:npm账号注册完成后,可以在终端执行npm login命令,依次出入用户名,密码,邮箱后,即可登陆成功,注意:在运行npm login 命令之前必须先把下包的服务器地址切换为npm的官方服务器,否则会导致发布包失败
  3. 把包发布到npm上:将终端切换到包的根目录之后,运行npm publish命令,即可将包发布到npm上
  4. 删除已经发布的包:运行npm unpublish 包名 --force,即可从npm删除已发布的包
    • npm unpublish命令只能删除72小时以内发布的包
    • npm unpublish 删除的包,在24小时内不允许重复发布
    • 发布包的时候要慎重,尽量不要往npm上发布没有意义的包

模块的加载机制

优先从缓存中加载

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

内置模块的加载机制

内置模块是由node.js官方提供的模块,内置模块的加载优先级最高 例如,require('fs')始终返回内置的fs模块,即使在node_modules目录下有名字相同的包也叫做fs

自定义模块的加载机制

使用require()加载自定义模块时,必须指定以./或是../开头的路径标识符。在加载自定义模块的时候。如果没有指定./或是../这样的路径标识符,则node会把它当作内置模块或是第三方模块进行加载
同时,在使用require()导入自定义模块时,如果省略文件扩展名,则node.js会按顺序分别尝试加载以下的文件:

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

第三方模块加载机制

如果传递require()的模块标识符不是一个内置模块,也没有./或是../开头,则node.js会从当前模块的父目录开始,尝试从/node_modules文件夹中加载第三方模块
如果没有找到对应的第三方模块,则移动到再上一层的父目录中,进行加载直到文件系统的根目录

image.png

目录作为模块

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

  1. 在被加载的目录下查找一个叫做package.json的文件,并寻找main属性,作为require()加载的入口
  2. 如果目录里没有package.json文件,或者main入口不存在或者无法解析,则node.js将会试图加载目录下的index.js文件
  3. 如果上述两步都失败了,则node.js会在终端打印错误信息,报告模块的缺失:Error:Cannot find module 'XXX'