【b站李同学的Lee】Part 2 模块化开发 NodeJS+Gulp基础入门+实战

发布于:2024-04-18 ⋅ 阅读:(17) ⋅ 点赞:(0)

课程地址:【NodeJS+Gulp基础入门+实战】 https://www.bilibili.com/video/BV1aE411n737/?share_source=copy_web&vd_source=b1cb921b73fe3808550eaf2224d1c155

目录

4 Node.js模块化开发

4.1 JavaScript开发弊端

4.1.1 文件依赖

4.1.2 命名冲突

4.2 生活中的模块化开发

4.3 软件中的模块化开发

4.4 Node.js中模块化开发规范

4.5  模块成员导出

4.6 模块成员导入

4.7 模块成员导出的另一种方式

4.8 模块导出两种方式的联系与区别

5 系统模块

5.1 什么是系统模块

5.2 fs 文件模块

5.2.1 导入模块

5.2.2 读取文件内容

5.2.3 写入文件内容

5.3 path 路径操作

5.3.1 为什么要进行路径拼接

5.3.2 实际应用场景

5.3.3 路径拼接的API语法

5.3.4 相对路径 vs 绝对路径

6 第三方模块

6.1 什么是第三方模块

6.2 获取第三方模块

 6.2.1 下载第三方模块

6.2.2 删除第三方模块

6.2.3 安装模式

6.3 nodemon第三方模块

6.3.1 作用

6.3.2 使用步骤

6.4 nrm 第三方模块

6.4.1 作用

6.4.2 使用步骤

6.5 Gulp第三方模块

6.5.1 GUlp

6.5.2 用途

6.5.3 使用步骤

6.5.4 Gulp提供的方法

6.5.5 Gulp插件

1 gulp-htmlmin插件:压缩html

2 gulp-file-include插件:抽取公共代码

3 gulp-css插件:less语法转css语法

4 gulp-csso插件:压缩css文件

5 gulp-babel插件:es6代码转换es5

6 gulp-uglify:压缩混淆JavaScript

7 总结

6.5.6 任务汇总

7 package.json文件

7.1 node_modules文件夹的问题

7.2 package.json文件的作用

7.2.1 记录项目依赖的第三方模块

插:7.2.2 package.json文件的scripts

7.3 项目依赖

7.4 开发依赖

7.5 packag-lock.json文件的作用

8 Node.js中模块的加载机制

8.1 模块查找规则-当模块有路径但没有后缀时

8.2 模块查找规则-当模块没有路径且没有后缀时


4 Node.js模块化开发

模块化开发是软件的一种开发方式。

什么是模块化开发,为什么要使用模块化开放。

4.1 JavaScript开发弊端

JavaScript在使用时存在两大问题,文件依赖和命名冲突

4.1.1 文件依赖

文件依赖:文件与文件之间依赖关系不明确。

JavaScript中,文件的依赖关系是由文件引入的先后顺序决定的。

下图中,后引入的文件会可能会依赖先引入的文件,只是可能,不绝对。

 这种依赖关系上的不确定会带来困扰。比如:实际开发中,会接到二手项目,比如要实现轮播图功能。在开发中发现这个项目实际已经实现好了,只需要将那个页面的js拿过来使用。假设是slider.js,但是拿过来发现无效,其实slider.js依赖scroll.js,而scroll.js又依赖jQuery.js文件。

这个分析的过程比较痛苦,如果文件之间的依赖关系不需要人为维护,自动就可以维护,这种是理想的一种情况。

模块化开发中,文件之间的依赖关系不需要人为维护,在文件内部有明确的代码说明当前的文件依赖哪些文件,不需要人为分析。

4.1.2 命名冲突

命名冲突:命名冲突导致代码覆盖。

JavaScript中,文件之间是完全开放的。

比如a.js中声明的变量在后续引入的文件(b.js,c.js,d.js)中是可以访问到的。

又由于JavaScript语法本身不严谨,如果在后续引入的文件里声明同名的变量,他不会报错,而是把前面文件中的同名变量覆盖掉。这就导致程序存在潜在的不确定性,即程序不健壮。

如果文件之间是半封闭的状态,比如在当前文件中,有哪些代码希望其他的文件能够访问,那么就开放这些代码。这是一种理想的状态。

模块化开发可以解决上述问题。

总结:模块化开发可以解决上述文件依赖和命名冲突问题。

4.2 生活中的模块化开发

电脑,组装方式就是模块化的方式,包括显示器、键盘、CPU等硬件。

模块化方式的好处:某个模块坏了,只需要换一个新的模块就好了。

4.3 软件中的模块化开发

一个功能就是一个模块,多个模块可以组成完整应用,抽离一个模块不会影响其他功能的运行。

4.4 Node.js中模块化开发规范

  • Node.js规定一个JavaScript文件就是一个模块,模块内部定义的变量和函数默认情况下在外部无法得到
  • 模块内部可以使用exports对象进行成员导出,使用requires方法导入其他模块。

exports导出和requires导入

4.5  模块成员导出

4.6 模块成员导入

就不具体实践了。

导入模块时后缀可省略,比如上图的b.js的 .js 可以省略。

4.7 模块成员导出的另一种方式

可以通过module_exports导出模块成员。

4.8 模块导出两种方式的联系与区别

exports是module.exports 的别名(地址引用关系),导出对象最终以module.exports为准。

即exports和module.exports默认情况下指向的是同一个对象,两者指向同一块内存空间。

如果某天两者的地址引用发生变化,最终要以module.exports为准。

解释

在上图情况下,直接给exports对象赋值,此时是无效的。最终导出以module.exports为准。

代码实践,这里我就不实践了。

5 系统模块

5.1 什么是系统模块

Node运行环境提供的API,因为这些API都是以模块化的方式进行开发的,所以又称Node运行环境提供的API为系统模块。比如文件模块、系统模块等。

5.2 fs 文件模块

f:File文件,s:system,文件操作系统。

5.2.1 导入模块

5.2.2 读取文件内容

应用场景:当客户端访问服务器端时,请求html网页,服务器端需要先在硬盘里找到这个文件并读取文件内容,再将文件内容返回给客户端。

 函数参数有中括号,表示是可选参数。

callback:回调函数。约定当某个API的参数是回调函数时,就使用callback作为标识。

回调函数的作用

这个API是读取文件内容的API,是硬盘在读取文件内容,这个过程需要时间,因此不能通过API的返回值直接拿到文件的读取结果。

因此要定义一个函数,当文件内容读取完成后,硬盘会通知该API文件读取完成,可以调用回调函数。

然后在回调函数调用过程中,将文件读取结果通过函数的参数的方式传递过来。

 示例

5.2.3 写入文件内容

应用场景:网站运行中,要监控网站的运行情况。在网站运行的过程中是否有报错的情况。希望当程序运行报错时,能够将错误写到错误日志中,即写到文件里。程序员上班后,可以通过查看错误日志来知道是否出现错误了。

第一个参数是文件地址,如果写入的地址没有这个文件,那么API会自动创建一个同名文件。

写入操作也是耗时的操作,因此无法通过API的返回值获取API执行的结果,结果要通过回调函数的方式获取。如果写入操作成功,那么回调函数的第一个参数error是null,否则error就是一个对象,包含错误信息。

示例

5.3 path 路径操作

只讲一个非常重要的路劲拼接API。

5.3.1 为什么要进行路径拼接

  • 不同操作系统的路径分隔符不统一
    • 路径分隔符:实际文件夹之间的分割符。/public/uploads/avatar
    • windows系统是 /  \
    • linux系统是  /  (为什么要考虑linux系统?linux系统通常被用作网站的服务器,现在写的代码以后可能运行在linux下的)

5.3.2 实际应用场景

实际应用场景:网站的用户头像上传功能。

用户上传头像文件,是要存储在服务器硬盘的某个文件夹中。在程序文件中,要想找到这个文件夹,就必须拼接文件夹的路径,此时需要使用系统模块path,在内部会判断当前所使用的操作系统类型,然后使用对应的路径分隔符进行路径拼接,这样代码才会通用。

5.3.3 路径拼接的API语法

参数个数不固定。

示例

说明:path.join方法不属于耗时操作,所以可以通过返回值接收拼接的结果。

5.3.4 相对路径 vs 绝对路径

  • 大多数情况下使用绝对路径,除非相对路径是相对于当前文件本身的,但是大部分情况下相对路径相对的是命令行工具的当前工作目录
    • 命令行工具的工作目录是可变的,因此文件里写相对路径是不安全的。
  • 文件读取或设置文件路径时都会选择绝对路径
  • 使用 __dirname获取当前文件所在的绝对路径
    • 注意:__dirname 前缀有2个下划线
  • 注意:requires方法比较特殊,它的相对路径就是相对于当前文件,所以使用requires方法时可以使用相对路径。

示例

① 获取当前文件夹所在的绝对路径

② 然后拼接文件名称,就得到helloworld.js文件的绝对路径。

6 第三方模块

6.1 什么是第三方模块

别人写好的、具有特定功能的、我们能直接使用的模块即第三方模块,由于第三方模块通常都是由多个文件组成并且被放置在一个文件夹中,所以又名包。

看到包,就知道,它实际上指的就是第三方模块的集合。

第三方模块有2种存在形式:

  • 以js文件的形式存在,提供实现项目具体功能的API接口
  • 以命令行工具形式存在,辅助项目开发。

6.2 获取第三方模块

第三方模块非官方模块,是由开发者提供的。

如果有很强大的模块,但是不是所有人的人都知道该模块的开发者,因此就无法得到这个模块,因此需要一个公共的平台来存储和分发第三方模块。

此时就诞生了npmjs.com网站

npmjs.com:第三方模块的存储和分发仓库。

在这个网站中存储了非常多的第三方模块,到目前为止有80多万个。

在网站中下载模块,npmjs网站提供了命令行工具npm。

npm: node package manager, node的第三方模块管理工具

实际上,npm也是node的第三方模块。

只不过这个模块每个node开发者都需要使用,所以不需要单独安装,在node安装成功后就已经被集成了。

 6.2.1 下载第三方模块

  • 下载:npm install 模块名称
    • npm是命令,install:安装
    • 下载需要联网
    • 控制台一堆warn,但是没有error,模块就是下载成功的。

模块的下载位置:默认是命令行工具当前的工作目录下。上图下载地址就是demo1文件夹下。

在demo1文件夹下,多了一个node_modules文件夹和package.json文件

node_modules是npm工具创建的,下载的第三方模块就放到这个文件夹下。

package.json文件在后面会解释。

6.2.2 删除第三方模块

  • 卸载:npm uninstall package 模块名称

示例

formidable已被删除

6.2.3 安装模式

  • 本地安装:将模块下载到当前项目供当前项目使用
  • 全局安装:将模块安装到一个公共目录里,所有项目都可以使用这个模块。

6.2.1里的安装属于本地安装,只有当前项目可以使用这个模块。

一般来说,将库文件这种第三方模块进行本地安装,将命令行工具这种第三方模块进行全局安装。让所有项目都能使用到这个命令行工具。

6.3 nodemon第三方模块

6.3.1 作用

nodemon是一个命令行工具,用以辅助项目开发。

问题:在Node.js中,每次修改文件都要在命令行工具中重新执行该文件,非常繁琐。

如果在修改文件后有工具帮助去执行改文件就比较好了。

nodemon作用在此,他可以监控文件的保存操作,当发生保存操作时会重新执行该文件。

6.3.2 使用步骤

共2步

1 使用npm install nodemon -g 下载

-g,g:global,代表全局安装。

即全局安装了nodemon后,在任何项目里都可以使用nodemon这个工具。

 只要没看到红色的错误信息,就代表下载成功。

2 在命令行工具中使用nodemon命令代替node命令执行文件

此时在监控文件的保存操作,当文件保存后,会再次执行该文件。

修改文件并保存,控制台会再次输出文件运行结果。

要在命令行进行其他操作,终止目前nodemon的操作,使用ctrl+C终止当前的命令行工作。

命令行里的ctrl+c不是复制,是终止操作的作用。

6.4 nrm 第三方模块

6.4.1 作用

nrm: node registry manager,npm下载地址切换工具。

作用:可以切换快速npm的下载地址。

场景:npm的下载地址在国外,国内下载速度慢,甚至出现失败的情况。可以使用nrm来切换国内国外的下载地址。

为了提供下载速度,国内有公司建立了专门的服务器用以存储node的第三方模块。

比如阿里巴巴建立的服务器,每隔10分钟就同步npmjs.com网站。所以他可以完全替代官方的下载地址。

6.4.2 使用步骤

1 使用npm instaill nrm -g下载

2 查询可用下载地址列表 nrm ls

ls: list,列表

 

 一般使用taobao的下载地址。

3 切换npm 下载地址:nrm use 下载地址名称

6.5 Gulp第三方模块

6.5.1 GUlp

Gulp:基于node平台开发的前端构建工具。

将机械化操作编写成任务,想要执行机械化操作时执行一个命令行命令,任务就自动执行了。

场景:项目开发完成,部署到线上,为加快网站运行速度,通常都会将网站的html、js、css文件进行合并压缩。

以前没有构建工具时,都是手动去做这些事情。这些操作繁琐浪费时间不需要动脑。

所以构建工具就诞生了。构建工具允许将这些操作编写成任务,当要执行某个任务时,在命令行工具中执行命令,任务就能自动执行了。

实质:用机器代替手工,提高开发效率。

6.5.2 用途

  • 项目上线,HTML、CSS、JavaScript压缩合并重命名
  • 语法转换(es6、less)
    • 浏览器还不完全支持es6,要转化es6为es5语法
    • less语法转css语法,浏览器完全不支持less
  • 公共文件抽离
    • 将网站的头部代码抽取到公共组件后,如果头部有变化,只需要修改这个公共组件。
  • 修改文件浏览器自动刷新
    • 前端开发时,修改html等文件时,都需要手动刷新浏览器才能看到效果。
    • Gulp:修改文件后,浏览器可以自动刷新

6.5.3 使用步骤

一共5步。

1 npm install gulp下载gulp库文件

当前下载的是gulp库文件,因此命令没有加 -g 参数。

库文件都是本地安装,只有当前项目可用。命令行工具是全局安装。

2 在项目根目录下建立gulpfile.js文件

文件名称是gulp要求的,不可随意更改。

3 重构项目的文件夹结构,src目录放置源代码文件,dist目录放置构建后(处理后)文件。

4 在gulpfile.js文件中编写任务

5 命令行工具执行任务

实践

1 npm install gulp下载gulp库文件

2 在项目根目录下建立gulpfile.js文件

3 重构项目的文件夹结构,src目录放置源代码文件,dist目录放置构建后(处理后)文件。

up有自己的项目文件,放到src下。(我找不到,这里就看视频里的了)

4 在gulpfile.js文件中编写任务

要求:必须将要处理的代码写到pipe方法里。

5 命令行工具执行任务

这里不执行整个文件,而是执行gulpfile.js里的first任务

注意:gulp第三方模块不只提供了库文件,还提供了一个同名的命令行工具。

可以使用gulp提供的命令来执行first任务。

使用gulp命令行工具,需要下载,下载指令:npm install gulp-cli -g

命令行工具一般来说都是全局安装。

当前项目用,其他项目也可能会用到这个命令行工具,所以需要全局安装。

安装完成后,命令行窗口可以使用gulp开头的命令。

执行first任务的命令:gulp first

注意:gulp命令会自动去当前项目的根目录下找gulpfile.js文件,在文件中找到first任务,然后去执行这个任务的回调函数。

提示:

1 使用gulpfile文件去执行这个任务

2 开始执行first任务

3 输出任务内容

4 结束任务

5 命令行返回了当前的工作目录。

6.5.4 Gulp提供的方法

代码示例

① 在gulpgfile.js里引用gulp;

② 使用gulp.task方法建立任务

③ 当前要建立的任务的名字

④ 获取要处理的文件,这里写文件路径

⑤ pipe:管道。将需要处理的代码放到pipe里,那么他就会自动执行里面的代码。

这里的操作是获取文件后,直接输出到硬盘dist的css文件夹下(实际就是复制文件的操作)。

  •  gulp.src(): 获取任务要处理的文件
  • gulp.dest(): 输出文件
  • gulp.task(): 建立gulp任务
  • gulp.watch(): 监控文件变化

6.5.5 Gulp插件

Gulp本身提供的功能非常少,其他的功能都是通过插件实现的。

1 插件介绍

2 插件使用

步骤1 下载插件

步骤2 gulpfile文件里引入插件

步骤3 调用插件

1 gulp-htmlmin插件:压缩html

实践

任务

任务1:html文件的代码压缩操作

步骤1 下载插件

--save参数可以去掉(最新npm版本里这个参数没啥用了)

步骤2 引用插件

步骤3 调用插件

1 gulp.src('./src/*.html') 中的*.html表示所有的html文件。

2 pipe里的命令,直接复制插件网站里的代码。

这里参数collapseWhitespace: true,压缩html代码时,要压缩空格。

因为gulp插件数量很多,不可能记住所有插件的使用方式,

现在只需要做到有什么插件可以做什么事情。只需要记住插件的名字,使用的时候去文档里查API就可以了。

3 代码输出到dist目录下。

4 效果:文件里的所有代码都变成一行(这就是压缩的意思)

2 gulp-file-include插件:抽取公共代码

任务2 抽取公共代码

插件:gulp-file-include

1 下载插件

2 引用插件

3 调用插件

要先抽取公共方法,然后压缩代码,然后输出文件。

1 公共方法,放置在common文件夹下。

common文件夹里存储公共代码的片段。

2 在使用公共方法的地方使用include语法调用公共方法。

用公共代码片段表述更严谨

3 gulp抽取公共方法

4 执行任务

可以看到压缩后的代码文件都有公共代码片段。

优点:只需要使用include指令调用公共方法,压缩后的文件会自动插入公共组件。

3 gulp-css插件:less语法转css语法

gulp-css:less语法转换为css语法。

表现出来就是less文件转css文件。

同理,下载插件,引入插件,调用插件。

p18,具体就不看了,用到的时候再看吧。

4 gulp-csso插件:压缩css文件

gulp-csso: 压缩css文件。

同理,下载插件,引入插件,调用插件。

p18,具体就不看了,用到的时候再看吧。

5 gulp-babel插件:es6代码转换es5

下载插件:

注意:

1 下载多个插件通过空格隔开

2 后面两个插件是babel所依赖的模块。

调用插件的代码在官网有。

优点:不需要等到浏览器支持es6 的时候再去使用es6,使用gulp-babel可以将es6转换为es5。

6 gulp-uglify:压缩混淆JavaScript

ugly:丑陋的

uglify:丑化、混淆

前面已经压缩好了html、css、JavaScript文件。

src目录下除了html、css、JavaScript外,还有image目录,lib目录。

因此接下来的任务是拷贝文件夹。将image和lib文件夹拷贝到dist目录下。这样代码才能运行起来。

拷贝文件夹可以直接去gulp的示例里看下,这里不再赘述。

7 总结

下载插件、引用插件、调用插件。

6.5.6 任务汇总

通过以上多个任务,完成项目的构建。

但是是否可以执行一个任务,其他任务可以跟着一起执行。

构建任务

在命令行代码里执行下图的default任务时,这个任务会依次执行htmlmin、cssmin、jsmin和copy任务。

注意:任务名叫default,那么在命令行工具里执行此任务时,不需要写任务名字,只需要写gulp,命令行工具会自动找名为default的任务执行。

7 package.json文件

7.1 node_modules文件夹的问题

1 文件夹以及文件过多过碎,当我们将项目整体拷贝给别人的时候,传输速度会很慢。

2 复杂的模块依赖关系需要被记录,确保模块的版本和当前保持一致。否则会导致当前项目运行报错。

7.2 package.json文件的作用

项目描述文件,记录了当前项目信息,例如项目名称,版本,作者,github地址,当前项目依赖了哪些第三方模块等。

使用npm init -y命令生成。

-y:yes,不填写任何信息,全部都使用默认值。

位置:项目根目录下。

7.2.1 记录项目依赖的第三方模块

一次下载多个第三方模块。

package.json文件

示例:

1 A将当前项目传递给B,但是没有传递node_modules文件夹。

B要怎么运行这个项目呢?

2 B执行命令  npm install 

此时npm会自动到项目的根目录下查找package.json文件的dependencies属性,根据属性值下载第三方模块。

总结:

1 文件夹以及文件过多过碎,当我们将项目整体拷贝给别人的时候,传输速度会很慢。

第一个问题解决,在传输项目时不需要传输node_modules文件夹,别人拿到项目后根据package.json文件使用npm install下载项目所依赖的第三方模块。

插:7.2.2 package.json文件的scripts

存储命令的别名。

在项目里创建app.js文件。

执行命令。

格式: npm run 命令别名。

优点:命令比较长的时候,可以给命令起别名。执行命令的时候只需要npm run 命令别名就可以了。

7.3 项目依赖

在项目的开发阶段和线上运行阶段,都需要依赖的第三方包,被称为项目依赖

使用npm install 包名命令下载的文件会默认添加到package.json文件的dependencies字段中。

比如项目使用了jQuery第三方库文件,代码中写了很多依赖jQuery的代码,如果项目线上运行时缺少了jQuery,项目就会运行失败。

7.4 开发依赖

在项目的开发阶段需要依赖,线上运行阶段不需要依赖的第三方包,被称为开发依赖

比如gulp,项目开发完成后就可以不使用gulp了。

使用 npm install 包名 --save -dev,将包添加到package.json文件的devDependencies字段中,以此区分项目依赖和开发依赖。

将项目依赖和开发依赖进行区分的好处:在不同的运行环境下下载不同的依赖。

开发环境:使用npm install ,去下载全部的依赖。

线上运行环境(处于服务器环境下):使用npm install --production,只会下载package.json的dependencies内容。

production是生产环境,就是线上运行环境。

7.5 packag-lock.json文件的作用

下载第三方模块时,npm会同时产生packag-lock.json文件,这个文件会详细记录模块与模块之间的依赖关系。

作用:

1 锁定包的版本,确保再次下载时不会因为包的版本不同而产生问题。

2 加快下载速度,该文件中记录了项目所依赖第三方包的树状结构和包的下载地址,重新安装只需下载即可,不需要做额外的工作。

(这里讲的是package.json文件的scripts,没有进一步讲package-lock.json文件)

8 Node.js中模块的加载机制

无论使用哪种模块,都是先require引入模块。

在require内部,对模块的查找是有一定规则的,在引用系统模块和第三方模块时都只写了模块名称,没有写模块路径。

那么require方法是怎么查找模块的呢?

8.1 模块查找规则-当模块有路径但没有后缀时

在使用require方法引入模块

require('./find.js');

require('./find');

1 require方法根据模块路径查找模块,如果是完整路径,直接引入模块.

2 如果模块后缀省略,先找同名js文件,再找同名文件夹

3 如果找到同名文件夹,就在文件夹内找index.js并执行;

4 如果文件夹中没有index.js,就去当前同名文件夹中的package.json文件中查找main选项中的入口文件

5 如果指定的入口文件不存在或没有指定入口文件,就会报错,模块没有被找到

8.2 模块查找规则-当模块没有路径且没有后缀时

使用require方法引入模块时,没有写模块路径和后缀,只写了模块的名字

require('find');

1 Node.js会假设他是系统模块,在系统模块里查找,如果有就引入该系统模块;

2 如果没有这个系统模块,Node.js会去node_modules文件夹中;

3 首先看node_modules里是否有同名的js文件,如果有就执行该同名js文件;

4 如果没有该同名js文件,会去node_modules里看是否有同名文件夹,如果有就在同名文件夹下找index.js,如果有就执行index.js。

5 如果没有index.js,则查看该同名文件夹中的package.json文件中的main选项,如果有入口文件,就执行;

6 如果main选择指定的入口文件不存在或没有指定入口文件,就找不到模块,即报错。