• Node.js开发、CommondJS 、ES-Module模块化设计


    目录

     Node.js是什么

    基础使用

    Node的REPL

    全局变量

     模块化设计

    CommondJS规范

     基础使用exports和module.exports

    require

     CommondJS优缺点

    AMD和CMD规范

    ES_Module

     基本使用方法

    导出

    导入

     结合使用

    默认导出

    ES Module解析流程


     Node.js是什么

     Node与浏览器的对比

     在浏览器中,HTML与CSS交给Blink处理,如果其发现了JS代码,就会交给V8处理

    而Node是直接V8处理JS代码,Node主要由JS(api)、C++和C语言编写,libuv主要绑定js与操作系统的操作

    下载地址

    Node.js

    基础使用

    可以在VScode中的终端使用node

    1. console.log(1123123);
    2. console.log('asdada');

    在终端中直接  node XXX.js(XXX是文件名,在运行时终端的文件域必须和该文件一致)

      如果要从终端输入参数

    1. console.log(1123123);
    2. console.log('asdada');
    3. console.log(process.argv);

    结果:

    1. 1123123
    2. asdada
    3. [
    4. 'C:\\Program Files\\nodejs\\node.exe',
    5. 'C:\\Users\\12860\\Desktop\\品优购项目\\test.js',
    6. 'num1=123'
    7. ]

    也就是说  process.argv输出的是一个数组,你输入的数据在数组的第3个之后  

    Node的REPL

     

    在控制台输入node,可以一行一行输入并执行js代码

     用的很少

    全局变量

    例如global,类似于window

    console.log(global);

    可以看到js中的全局变量

    1. 1> Object [global] {
    2. global: [Circular *1],
    3. queueMicrotask: [Function: queueMicrotask],
    4. clearImmediate: [Function: clearImmediate],
    5. setImmediate: [Function: setImmediate] {
    6. [Symbol(nodejs.util.promisify.custom)]: [Getter]
    7. },
    8. structuredClone: [Function: structuredClone],
    9. clearInterval: [Function: clearInterval],
    10. clearTimeout: [Function: clearTimeout],
    11. setInterval: [Function: setInterval],
    12. setTimeout: [Function: setTimeout] {
    13. [Symbol(nodejs.util.promisify.custom)]: [Getter]
    14. },
    15. atob: [Function: atob],
    16. btoa: [Function: btoa],
    17. performance: Performance {
    18. nodeTiming: PerformanceNodeTiming {
    19. name: 'node',
    20. entryType: 'node',
    21. startTime: 0,
    22. duration: 33.55670002102852,
    23. nodeStart: 2.157499998807907,
    24. v8Start: 4.655900001525879,
    25. bootstrapComplete: 23.560699999332428,
    26. environment: 13.318800002336502,
    27. loopStart: -1,
    28. loopExit: -1,
    29. idleTime: 0
    30. },
    31. timeOrigin: 1668353099915.782
    32. },
    33. fetch: [AsyncFunction: fetch]
    34. }

    对于浏览器来说有window,对于node来说有global,所以创造了一个总的全局变量,在两个地方都能用,叫globalthis 

     模块化设计

    解释一下第二点

    对于之前的做法,如果我有很多js文件,第一个文件aaa.js定义了var name = 111

    第二个文件bbb.js定义var name = 222

    当我在第三个js文件通过scrip src方式引入这两个js文件时,这个name变量就会变得模糊

    而模块化设计的要求是,不同模块的变量是不会互相影响的,且外部无法访问

    但是肯定不希望所有的变量,外部都不能访问,所有可以通过一些方法导出自己希望外部能访问的内容,既第三点

     立即执行函数可以解决变量冲突的问题,如果在aaa.js和bbb.js中的代码都放入立即执行函数中,由于函数本身就有作用域,所以不会互相影响

    但是如果外部想调用变量,就必须确定立即执行函数的返回对象,这会增加使用难度

    CommondJS规范

     基础使用exports和module.exports

    在test.js里面写

    1. const name = 'lmx'
    2. const age= 18
    3. const fn = function(){
    4. console.log(123);
    5. }
    6. exports.name = name
    7. exports.age = age
    8. exports.fn = fn

     在mian.js写

    1. const test = require('./test.js')
    2. console.log(test);
    3. console.log(test.name);
    4. test.fn()

    必须要用node运行main.js

    其实本质就是引用赋值

    test.js创建了exports对象,然后往里面写入元素name age fn,但是exports变量保留的是该对象的地址

    而require也是获取的对象的地址,所以两者指向的是同一个对象

    那么在test.js里面改变值,也会影响到main.js

    例如,test.js:

    1. const name = 'lmx'
    2. const age= 18
    3. const fn = function(){
    4. console.log(123);
    5. }
    6. exports.name = name
    7. exports.age = age
    8. exports.fn = fn
    9. setTimeout(()=>{
    10. exports.name = 'qweqwe'
    11. },2000)

    main.js

    1. const test = require('./test.js')
    2. console.log(test);
    3. console.log(test.name);
    4. test.fn()
    5. setTimeout(()=>{
    6. console.log(test.name);
    7. },2500)

    { name: 'lmx', age: 18, fn: [Function: fn] }
    lmx
    123
    qweqwe

    但是上述方法在开发中使用的很少,一般用module.exports

    将test.js中的exprots改为:

    1. const name = 'qwe'
    2. const age = 17
    3. module.exports.name = name
    4. module.exports.age = age

    main.js不需要改

    看上去还变复杂了,从本质上来说,这种方法会创造一个module对象,该对象里面包含了一个exports,指向之前创造的对象

    所以说这里左边的exports和module.exports其实是一个东西,因为指向的是同一个对象

    在require的时候,其实node本质上拿到的是module.exports,但是因为module.exports===exports,所有现在看来两种方式是一样的

     真实开发做法是

    1. const name = 'qwe'
    2. const age = 17
    3. console.log(age);
    4. module.exports={
    5. name,
    6. age
    7. }
    8. exports.name = 'hhhhh'

     本质是,这里会创造一个新的对象,module.exports指向这个对象,那么这个对象跟之前的exports对象就没有关系了

    使用require获取的也是这个新的对象,所有改exports.name没有意义了

    结果是{ name: 'qwe', age: 17 }

    require

    例如,内置模块包含了很多方法

    1. const test = require('path')
    2. console.log(test);
    1. 1> {
    2. resolve: [Function: resolve],
    3. normalize: [Function: normalize],
    4. isAbsolute: [Function: isAbsolute],
    5. join: [Function: join],
    6. relative: [Function: relative],
    7. toNamespacedPath: [Function: toNamespacedPath],
    8. dirname: [Function: dirname],
    9. basename: [Function: basename],
    10. extname: [Function: extname],
    11. format: [Function: bound _format],
    12. parse: [Function: parse],
    13. sep: '\\',
    14. delimiter: ';',
    15. win32: [Circular *1],
    16. posix: <ref *2> {
    17. resolve: [Function: resolve],
    18. normalize: [Function: normalize],
    19. isAbsolute: [Function: isAbsolute],
    20. join: [Function: join],
    21. relative: [Function: relative],
    22. toNamespacedPath: [Function: toNamespacedPath],
    23. dirname: [Function: dirname],
    24. basename: [Function: basename],
    25. extname: [Function: extname],
    26. format: [Function: bound _format],
    27. parse: [Function: parse],
    28. sep: '/',
    29. delimiter: ':',
    30. win32: [Circular *1],
    31. posix: [Circular *2],
    32. _makeLong: [Function: toNamespacedPath]
    33. },
    34. _makeLong: [Function: toNamespacedPath]
    35. }

     意思是有的时候可以省略.js等

    第三种情况,X即不是内置模块,也不是文件,例如

    const test = require('why')

    它会找上层文件夹中下的node_modules文件夹,这个文件夹名字是固定的,然后再在node_modules文件夹里面找why文件夹,再找why文件夹下的index.js

    如果没找到node_modules,就返回再上一层文件夹接着找,直到找到或者找到根目录为止

    所以在npm install axios的时候,其实就是创建了一个node_modules文件夹,把axios相关的文件放进去了

     CommondJS优缺点

    AMD和CMD规范

     已经用的很少了,做一个了解吧

     

    ES_Module

    ES module是ES6新增的JS模块化的功能,如果浏览器支持ES6则可以直接使用

    如果不支持,一般是通过webpack打包之后再接入浏览器

     基本使用方法

    在html引入js这么写,加type = 'module',这样就可以创建独立的作用域,不会互相影响

    但是在打开时,必须打开服务,例如使用live server

    1. <script src="./test.js" type="module">script>

    导出

    1. const name = 'why'
    2. const age =14
    3. export{
    4. name,
    5. age
    6. }

    这个地方,export{}不是新建的一个对象,只是大括号将标识符包含起来

    也可以在定义的时候直接导出

    1. const name = 'why'
    2. export const age =14

    导入

    1. import {name,age }from './test.js'
    2. console.log(name );
    3. console.log(age);

    导入的时候可以起个别名

    1. import {name as fname,age }from './test.js'
    2. console.log(fname );
    3. console.log(age);

    导入语句只能写在顶端,不能写在逻辑代码里面 

    例如

    1. let flag = true
    2. if(flag){
    3. import{name,age} from './test.js'
    4. }

    这种会直接报错

    但是我就是想放在逻辑里面使用呢,比如当某种条件成立的时候才导入

    可以用到import函数

    1. let flag = true
    2. if(flag){
    3. let promise1 = import('./test.js')
    4. promise1.then(res=>{
    5. console.log(res.name);
    6. console.log(res.age);
    7. res.fn()
    8. })
    9. }

    因为该函数是异步的,其返回值是一个promise,调用then拿到结果 

     结合使用

    如果现在有很多js文件,基本上每个文件都导出了一些方法

    如果index,html想得到这些方法,就得一个一个script src=

    一般的做法是,再新建一个index.js,里面将那些js文件导入,再直接导出,所以在html文件中就可以直接只导入index.js

    那么在index.js里面就可以这么写

    1. export{getdata,getname} from './aaa.js'
    2. export{getdata1,getname2} from './bbb.js'

    默认导出

    如果一个js文件,他的文件名就已经代表了他的功能,且只需要导出一个东西,就可以使用默认导出

    1. const name = 'why'
    2. export default name

     如果导出的函数,甚至可以不写函数名字

    1. export default function(){
    2. console.log('asdasd');
    3. }

    很显然,一个模块只能有一个默认导出

    之前的导入中 ,都需要导出和导入的变量名字保持一直(除非使用as改名),使用默认导出,就可以在导入的时候自己设置一个名字

    1. import aaa from './test.js'
    2. console.log(aaa);

    ES Module解析流程

    首先第一步,对于js文件进行下载,解析为 模块记录module record,例如如果在index.html中引入了main.js,那么会首先下载这个js文件。

    如果js文件中也存在导入语句,那么跟第一步一样,先下载对应的js文件,创建模块记录,这里是会直接搜索文件的最开始部分有没有import语句,所以不能将其放入逻辑判断中。

    这里还会生成一个映射表 module map,放入js文件的地址和对应的  module record,在这个图中main.js引入的counter.js,如果别的文件也引入了counter.js,那么就可以在这个表中直接拿到 module record

    第二步,如果有导出语句,例如export const age = 18,那么会生成一个模块环境记录  module environment record 放置 age变量,但是它是没有值的。

    值得注意的是,上述两步是没有执行一行代码的

    第三步,运行,填充环境记录中的变量值,例如填充age 的值是多少,在后续别的模块import这个age的时候,就可以从这里拿到

  • 相关阅读:
    2023年【公路水运工程施工企业安全生产管理人员】新版试题及公路水运工程施工企业安全生产管理人员模拟试题
    【Golang】使用代码绘制图表的开源库对比
    excel高级绘图技巧100讲(六)-甘特图在项目进度上的实战应用案例
    [iOS]-weak底层原理(sidetable相关,附带引用计数原理)
    网络程序设计——重叠I/O模型
    国产龙芯双核64位系统迅为2K开发板应用到工控轨道交通电力能源等领域
    问题 F: 案例6-1.2:邻接表存储图的广度优先遍历
    Python实现机器学习(下)— 数据预处理、模型训练和模型评估
    9月12日作业
    MySQL性能优化-范式设计和反范式设计
  • 原文地址:https://blog.csdn.net/qq_54517101/article/details/127838256