阅读 192

初识Node.js

背景

了解一门技术前,首先要了解一下它的背景,这里我摘取了node.js中文官网的一段话。

JavaScript 是一门被创建于 Netscape(作为用于在其浏览器 Netscape Navigator 中操纵网页的脚本工具)中的编程语言。

Netscape 的商业模式的其中一部分是出售 Web 服务器,其中包括一个被称为 Netscape LiveWire 的环境,该环境可以使用服务器端 JavaScript 创建动态页面。 不幸的是,Netscape LiveWire 并不十分成功,并且服务器端 JavaScript 也没有普及,直到引入了 Node.js。

引领 Node.js 兴起的一个关键因素是时机。 仅仅几年前,多亏 "Web 2.0" 应用程序(例如 Flickr、Gmail 等)向世界展示了 Web 上的现代体验,JavaScript 开始被视为一种更为严肃的语言。

随着许多浏览器竞相为用户提供最佳的性能,JavaScript 引擎也变得更好。 主流浏览器背后的开发团队都在努力为 JavaScript 提供更好的支持,并找出使 JavaScript 运行更快的方法。 多亏这场竞争,Node.js 使用的 V8 引擎(也称为 Chrome V8,是 Chromium 项目开源的 JavaScript 引擎)获得了显着的改进。

Node.js 恰巧构建于正确的地点和时间,但是运气并不是其今天流行的唯一原因。 它为 JavaScript 服务器端开发引入了许多创新思维和方法,这已经对许多开发者带来了帮助。

简单的概况一下上面的话就是:可以让前端开发人员使用javascript来开发服务器端的应用,这让之前只能发开前端页面的javascript,触及到了web应用领域。之后基于node.js的工具和框架层出不穷,比如vue.js框架、webpack打包工具等等,这大大地提高了前端开发人员的开发效率,而且还能优化服务器的性能,带来更好的用户体验。


什么是node.js?

用一句话概括就是:node.js是一个javascript的运行环境,它提供了基础的功能和内置API,来看下面两幅图,来区分一下在浏览器的javascript运行环境和在node.js的javascript运行环境有什么不同之处!

QQ截图20210319204005.png

QQ截图20210319204047.png

可以看出node.js的运行环境和谷歌浏览器的运行环境不同之处就在于,它们的内置API不同,这就是为什么node.js可以开发web应用了,因为这些内置API可以操作文件、数据库、创建web服务器等等,而在浏览器上的javascript运行环境,它能只对DOM和BOM对象进行相关的操作。

这样看来,node.js对前端开发者是一个极大的福利。在node.js没有出生前,如果一个前端开发者要进行服务器端的开发,那么它就必须去学习后端相关的开发语言,比如java、PHP等等,这就大大增加了前端开发者的学习负担。所以一个前端开发者如果能学会node.js的话,可以让自己胜任更多的工作。


什么是模块化?

我觉得在学习node.js的内置API或者第三方库之前,了解一下模块化的概念是很有必要的,接下来我就来说一说有关模块化的一些细节吧!

首先问一下自己,为什么需要模块化?

在我了解模块化之前,我一头雾水,我可以把所有的js代码都写在一个脚本标签里,为什么还要那么麻烦的写多个js文件?

这是因为在业务开发的时候,大多数情况下,都是一个团队进行开发一个项目的,团队里的每个人负责某一块的业务开发,到最后在把大家的代码整合到一起,完成项目的开发。这里就暴露出了如果不使用模块化开发的话,会造成全局变量的污染,导致程序不能正常的运转,降低了开发的效率和代码的维护,甚至还造成了资源的浪费!

有了模块化的思想,大家就不要担心全局变量的污染,大家可以随心所欲地声明变量^^(开个玩笑),而且提高了代码的复用性、可维护性。


那怎么加载模块呢?

因为node.js采用的是CommonJS模块规范,所以加载模块使用的是require()方法,而不是import!接下来我们用实现文件的读写操作的例子来说一下怎么加载模块。(其实很简单^^)。

const fs=require('fs');//导入内置API fs模块

fs.readFile('./node/file/zz.txt', 'utf8', function(err,data){
    console.log(err);
    console.log(data);
})

fs.writeFile('./node/file/zz.txt', 'Hello Node.js!!!',function(err,data){
    if(err){
      return  console.log(`写入的数据失败:${err}`)
    }
    else
        console.log(`成功写入数据:${data}`)
})

fs.writeFile('./node/file/text.txt','Hello  Node.js',function(err,data){
    if(err){
        return  console.log(`文件写入失败:${err}`)
    }
    else
        return  console.log(`文件写入成功:${data}`)
})
复制代码
fs.writeFile(file, data[, options], callback)
/*
1. file是一个必选参数,指定要写入数据的文件地址(可以是相对路径,也可以是绝对路径),但最好使用path模块的path.join()方法,来指定文件的路径,不然可能会访问不到文件
2. data是一个必选参数,指定要写入的数据
3. options是一个可选参数,用来指定写入数据的格式
4. callback是一个必选参数,当完成写操作后,会调用回调函数
*/
fs.readFile(file,[,options], callback)
/*
1. file是一个必选参数,指定要写入数据的文件地址
2. options是一个可选参数,用来指定写入数据的格式
3. callback是一个必选参数,当完成写操作后,会调用回调函数
*/
复制代码

假设上面代码都执行成功了,大家是否知道控制台上的输出顺序?

很抱歉,上面的输出顺序,我们不能确定,因为读写操作它是一个异步任务,当javascript解析引擎执行到读写操作的代码,就会将它们在主线程上挂起,等到它们执行完毕的时候,才会将回调函数发送到任务队列里,等执行栈的同步任务全部执行完毕时,将任务队列里的回调函数依次执行。我们不知道这些读写文件的大小,有可能第一个文件的数据量很大,导致它可能是最后一个执行回调函数!

总结一下上面说的意思:当一个读写操作依赖另一个读写操作的时候,就应该把该读写操作写入另一个读写操作的回调函数里。(T_T,这又会引起回调地狱的问题....)balabala,想解决回调地狱的问题,有兴趣的小伙伴可以查阅一下promise的相关资料哈!


如何向外共享模块的成员?

在node.js里,有两种方式可以向外共享模块的成员,一个是module.exports,另一个是exports,它们两个有上面区别呢?

都大家都知道,程序猿都很懒,能偷懒的事,尽量偷懒,所以exports其实是module.exports的简写。

这里建议大家在一个模块里,尽量不要混合使用的它们,这样会造成一些错误,接下来我就来说一说会造成哪些错误。

//ab.js
let a=18;
let b="小白";
let c='小黑';
exports.c=c;
module.exports={
    a,
    b,
}
复制代码
//index.js
const  a =require("./ab.js")
console.log(a)//{a:18,b:"小白"}
复制代码

可能有人会问,你上面不是说module.exports和exports指向同一个对象吗,那为什么对象a里面没有属性c?

let a=18;
let b="小白";
let c='小黑';
exports=module.exports;
exports.c=c;
module.exports={
    a,
    b,
}
复制代码

把上面的ab.js文件稍微修改一下,这样大家明白了把!其实在使用exports暴露成员变量之前,会将module.exports对象的地址值赋值给exports,但最后module.exports改变了地址值,指向了新的对象,这就是为什么对象a里没有属性c。

总结一下:require()方法导入的永远是module.exports指向的对象


今天先到这里,接下来我还会写一篇npm包管理工具的文章,有什么错误希望大佬们指出^^。

文章分类
前端
文章标签