前端工程化基础(一):Node模块化

Node模块化

Node.js是什么

官方定义:Node.js是一个基于V8 JavaScript引擎的JavaScript运行时的环境

  • Node.js基于V8引擎来执行 JavaScript代码,但是Node.js中不仅仅有V8
    • 我们知道,V8可以嵌入到C++应用程序中,因此无论是Chrome还是Node.js,都是嵌入了V8引擎来执行的JavaScript代码
    • 在Chrome浏览器中,除了运行JavaScript代码之外,还要解析、渲染HTML、CSS等相关渲染引擎,同时还需要提供支持浏览器操作的API、浏览器自己的事件循环等
    • 在Node.js中我们也需要进行一些额外的操作,比如 文件系统的读写、网络IO、加密、压缩解压文件等操作
    • Node.js是JS代码的运行环境,由JS/C++/C语音编写(libuv就是C语音编写

image.png

Node.js应用场景

  • 应用一:目前 前端开发的库都是以node包的形式进行管理的
  • 应用二:npm/yarn/pnpm工具成为前端开发使用的最多工具
  • 应用三:越来越多的公司使用 Node.js作为web服务器开发、中间件、代理服务器
  • 应用四:大量项目需要 借助Node.js完成前后端渲染的同构应用
  • 应用五:资深前端工程师,使用Node.js编写脚本工具
  • 应用六:使用Electron来开发桌面应用程序

Node.js的输入输出

  • 我们在用命令行执行JS文件的时候,可以在命令后面敲空格,之后输入内容 node js.js num=10
  • JS中可以通过process.argv接收
console.log(process.argv);
[
  'F:\\nodejs\\node.exe',
  'D:\\Mrzhang\\Study\\前端\\CSS\\code\\js.js',
  'num=10',
]
  • 输出 console.log()即可

Node.js中的全局对象

  • global:相当于浏览器中window
  • process:进程相关的内容:process.argv是比较常用的
  • console
  • 定时器函数
    • setTimeout
    • setInterval
    • setImmediate(function () {})
    • process.nextTick(function(){})

特殊的全局对象

这些对象实际上是 模块中的变量,只是 每个模块都有,看起来是全局变量

在命令行交互中不可以使用

  • 包括:__dirname/__filename/exports/module/require()
//显示文件所在目录(不包含文件名称)
console.log(__dirname);
//显示文件所在目录(包含文件名称)
console.log(__filename);

认识模块化开发

  • 目前的程序代码量是十分庞大的
  • 模块化的目的是将庞大程序的代码,拆分成一个个小的结构
  • 而这个结构有属于 自己的逻辑代码,有自己的作用域,定义变量的时候,不会影响到其他的结构
  • 同时 这个结构的某些变量,函数以及对象等,又希望暴露出去,让其余结构访问
  • 其余结构可以通过某种方式,导入 另外结构的变量、函数对象等内容
  • 上面所说的 结构就是 模块,按照这种 结构划分开发程序的过程,就是模块化开发的过程

模块化的提出,主要是为了应对前端页面更加复杂的局面

在ES正式提出模块化前,社区提出了模块化的规范CommonJS(依旧再用),AMD、CMD(后面的两者均不在用了)

在ES6的时候,正式提出了标准的模块化ESModule

CommonJS规范和Node关系

CommonJS是一个规范,最开始提出来的时候,主要应用于服务器的

  • Node是CommonJS在服务器端一个具有代表性的实现

  • Browserify是CommonJS在浏览器中的一种实现

  • webpack打包工具具备对CommonJS的支持和转换

  • 因为Node对CmmmonJS进行了支持和实现,因此在开发Node过程中可以使用模块化开发

    • 在Node中每一个JS文件都是单独的模块
    • exports和module.exports可以负责对模块中的内容进行导出
    • require函数可以帮助我们 导入其他模块中的内容
  • 需要导出的文件

let until_name = "until";

function foo() {
  console.log("zhangcheng");
}

function bar() {
  console.log("bar");
}

//导出相关变量
exports.until_name = until_name;
exports.foo = foo;
exports.bar = bar;
  • 需要导入的文件
//引入变量
const until = require("./until.js");

//可以通过until.的方式访问变量
console.log(until.until_name);
until.foo();
until.bar();
-----------------------------------------
//我们可以借助解构赋值的方式,简化代码
//引入变量
const { until_name, foo, bar } = require("./until.js");

console.log(until_name);
foo();
bar();

exports导出的本质

exports实际上是一个对象,通过require函数,将导入文件中的变量与exports进行了引用赋值

image.png

  • 以上可以通过代码进行验证,更改 exports.name的值,并在两个文件中打印,即可观察到

module.exports导出

CommonJS导出,实际上是通过module.exports进行导出的,而module.exports和exports是同一个对象

  • 因为module.exports和exports是同一个对象,因此可以写出以下导出代码
let until_name = "until";

function foo() {
  console.log("zhangcheng");
}

function bar() {
  console.log("bar");
}

//导出相关变量
module.exports.until_name = until_name;
module.exports.foo = foo;
module.exports.bar = bar;
  • 而在真实的开发中,我们常写出以下代码
let until_name = "until";

function foo() {
  console.log("zhangcheng");
}

function bar() {
  console.log("bar");
}

//导出相关变量
module.exports = {
  until_name,
  foo,
  bar,
};
  • 若在最后,通过exports进行更改相关变量,则在导入的文件中,不会受到影响
let until_name = "until";

function foo() {
  console.log("zhangcheng");
}

function bar() {
  console.log("bar");
}

//导出相关变量
module.exports = {
  until_name,
  foo,
  bar,
};
//在导入的时候,until_name为“until”,而不是“hhhh”
exports.until_name = "hhhh"
  • 接下来看以下内存图
    • 通过 module.exports进行导出

image.png

通过 module.exports = {}进行导出

image.png

  • 因此通过以上两幅图,可以看出,在内存中 module.exportsexports的对应关系

  • 那么我们通过维基百科中对 CommonJS规范的解析

    • CommonJS中没有module.exports的概念
    • 但是为了实现模块的导出,Node中使用的是 Module类每一个模块都是Module的一个实例,也就是 module
    • 所以在Node中真正用于导出的其实不是 exports,而是module.exports
    • 因为 module.exports = exports,所以,exports也可以进行导出

require的细节

我们知道 require是一个函数,可以帮助我们引入一个文件(模块)中导出的对象,接下来,我们就要看一下它的查找规则是什么样的

require(X)

  • 情况一:X是Node核心模块,比如path、http
//会直接返回核心模块,并停止查找
const http = require("http")
  • 情况二:X是 以./或者…/或者根目录开头的
    • 第一步:将X当作一个文件在对应的目录下查找
      • 如果有后缀名,就按照后缀名的格式查找对应的文件
      • 如果没有后缀名,会按照如下顺序
        • 直接查找文件X
        • 查找X.js文件
        • 查找X.json文件
        • 查找X.node文件
    • 第二步:若没有查找到X对应的文件,将X作为一个目录
      • 查找目录下面的index文件
        • 查找X/index.js文件
        • 查找X/index.json文件
        • 查找X/index.node文件
    • 如果都没有找到,那么就会报错
const until = require(./until)
//首先会将until当成文件,查找until.js/until.json/until.node
//若没有查到,就会将until当成目录,查找它下面的index.js/index.json/index.node文件
  • 情况三:直接是一个X,该X没有路径,且不是一个核心模块
    • 会查找本目录下,以及上级目录下中 node_module目录中的模块
const axios = require("axios")
//会在node_module目录中查找axios

模块加载过程

  • 结论一:模块在被第一次引入时,模块中的JS代码会被运行一次
const until = require(./until)
//until.js中的代码会先运行一次
  • 模块被多次引入时,会进行缓存,最终只加载一次
    • 因为每个模块对象module中有一个loaded属性
    • false表示还没有被加载,为true表示已经加载
    • 已经加载的模块,不会再次被加载
//打印------
console.log(------);
//运行until.js中的代码           
const until = require(./until)
//打印+++++
console.log(+++++);

//下面的引入不会再被加载
const until1 = require(./until)  
const until2 = require(./until)
const until3 = require(./until)
  • 如果出现循环引入的情况
    • 会按照图结构,进行深度优先算法进行加载

CommonJS的缺点

  • CommonJS加载模块是同步的
    • 这就意味着只有 等到对应的模块加载完毕,当前模块中的内容才能被运行
    • 这个再服务器中不会出现问题,因为服务器 加载的js文件都是本地文件,加载速度比较快
  • 如果应用到浏览器中
    • 浏览器 加载js文件需要先从服务器将文件下载下来,之后 再加载运行
    • 如果 引入的某个js文件运行时间过长,就会阻塞后面的js代码无法运行,即使是一些简单的DOM操作

认识ESModule

ES6提出的模块化,前提是浏览器支持

  • 与CommonJS不同之处

    • 一方面使用了import和export(对应的CommonJS的是require和module.exports
    • 另一方面采用了编译期的静态分析,并且加入了动态引用的方式
  • 采用ES Module会默认采用严格模式


  • 创建一个HTML文件
    • 引入 script标签,在标签中写入type = “module”
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
  </head>
  <body>
        <!--注意,在本地测试的时候,ES Module需要启用live server进行测试,单纯运行文件不行-->
    <script src="./main.js" type="module"></script>
		<!--until.js暴露变量-->
	<script src="./until.js" type="module"></script>
  </body>
</html>

  • 创建until.js暴露变量
    • 通过export进行暴露
    • 而此 export并不是对象,只是特殊语法,{标识符}
let until_name = "until";

function foo() {
  console.log("zhangcheng");
}

function bar() {
  console.log("bar");
}

//导出相关变量
export { until_name, foo, bar };
  • 创建 main.js引入 until.js暴露的变量
    • 通过 import进行引入跟的文件名一定要写后缀
import { until_name, foo, bar } from "./until.js";
console.log(until_name);
foo();
bar();

ESModule的导入导出扩展

  • 导出的三种方式
//通过export直接导出
export{name,foo}

//导出的时候取别名
export{name as unName}

//直接导出变量
export let name = "zhangcheng"
  • 导入的三种方式
//直接导入
import {name} from "./until.js"

//导入的时候取别名
import {name as unName} from "./until.js"

//导入的时候将这个module取别名
import * as foo from "./until.js"
foo.name

export和import结合使用

常见于开源的框架中

image.png

image.png

  • index.js中一般不需要写逻辑代码,仅做模块的导入导出即可

  • index.html文件
    • 该文件中通过 script标签引入了main.js文件
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
  </head>
  <body>
    <script src="./main.js" type="module"></script>
  </body>
</html>

  • main.js文件
    • 该文件直接引入了index.js文件
import { name, foo } from "./until/index.js";

console.log(name);
console.log(foo());
  • index.js文件
    • 该文件引入了tool.js和tool2.js文件,同时将变量暴露
//统一引入
import { name } from "./tool.js";
import { foo } from "./tool2.js";

//统一导出
export { name, foo };

------------------还可以做出以下优化
//export和import的结合
export { name } from "./tool.js";
export { foo } from "./tool2.js";


//写成这样也可以
export * from "./tool.js";
export * from "./tool2.js";
  • tool.js文件
export let name = "zhangcheng";
  • tool2.js文件
export function foo() {
  return "foo";
}

default用法

前面用到的都是有名字的导出,default是默认导出

  • 默认导出 是不需要指定名字的
  • 导入的时候不需要加{},且名字可以自己命名
  • 注意:一个文件只有一个默认导出

  • 默认导出方式
//默认导出方式一
function foo() {
  console.log(123);
}

export default foo;

//默认导出方式二
export default function(){
    console.log(123)
}
  • 引入方式
import aaa from "./until.js"
aaa()

import函数

当我们需要动态引入文件的时候,需要用到import函数

  • 正常使用import引入文件的时候,需要写在代码最顶层
import {name} from "./index.js"
//逻辑代码
  • 但是有时候需要按需引入一些文件,这时候就可以用到import函数
    • import函数返回的是一个Promise
let flag = true
if(flag){
    import("./index.js").then(res=>{
        console.log(res)
    })
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/350539.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Redis快的秘密,高性能设计epoll和IO多路复用探究

Redis快的原因&#xff0c;高性能设计epoll和IO多路复用探究 1、多路复用需要解决的问题 并发多客户端连接&#xff0c;在多路复用之前最简单和典型的方案&#xff1a;同步阻塞网络IO模型 这种模式的特点就是用一个进程来处理一个网络连接&#xff08;即一个用户请求&#x…

windows 下docker-compose 试玩 paperlsess

第一步安装&#xff1a;&#xff08;假设docker-compose已安装好&#xff09; 下载 docker-compose.yml &#xff0c;docker-compose.env: github下载地址 在docker-paperless.env增加环境变量,设置管理员账户信息&#xff1a; PAPERLESS_ADMIN_USER: admin PAPERLESS_ADMIN_P…

分页查询的使用

背景 在业务中我们在前端总是需要展示数据&#xff0c;将后端得到的数据进行分页处理&#xff0c;通过pagehelper实现动态的分页查询&#xff0c;将查询页数和分页数通过前端发送到后端&#xff0c;后端使用pagehelper&#xff0c;底层是封装threadlocal得到页数和分页数并动态…

[Linux基础开发工具---vim]关于vim的介绍、vim如何配置及vim的基本操作方法

一、vim及其三种模式的简单介绍 Vim是一个类似于Vi的文本编辑器&#xff0c;不同的是vim是vi的升级版本&#xff0c;它不仅兼容vi的所有指令&#xff0c;而且还有一些新的特性在里面,如多级撤销、多窗口操作、崩溃后也可以恢复、增加了稳定性、关键字自动补全、上下文自动补全等…

力扣1143. 最长公共子序列

动态规划 思路&#xff1a; 假设 dp[i][j] 是 text1[0:i] 和 text2[0:j] 最长公共子序列的长度&#xff1b;则 dp[0][j] 0&#xff0c;&#xff08;空字符串和任何字符串的最长公共子序列的长度都是 0&#xff09;&#xff1b;同理 dp[i][j] 0&#xff1b;状态转移方程&…

List使用addAll()方法报错

当使用Arrays.asList方式创建出来的list&#xff0c;在使用addAll方法的时候报错如下&#xff1a; Exception in thread "main" java.lang.UnsupportedOperationException 这个问题记录下&#xff0c;以防以后忘记。 下面是代码 List<String> objects new A…

股票交易维度和概念

股票&#xff1a;股份公司为筹集资金而发行给各个股东作为持股凭证并借以取得股息和红利的一种有价证券 好处&#xff1a;分红、送股配股、交易收益、本金少、易变现、避免货币贬值 金融标的投资风险与收益 股票分类 蓝筹股 经营业绩长期稳定增长的大公司&#xff0c;一般是…

【C++】入门基础

前言&#xff1a;C是在C的基础之上&#xff0c;容纳进去了面向对象编程思想&#xff0c;并增加了许多有用的库&#xff0c;以及编程范式等。熟悉C语言之后&#xff0c;对C学习有一定的帮助&#xff0c;因此从今天开始们将进入&#xff23;的学习。 &#x1f496; 博主CSDN主页:…

Java如何对OSS存储引擎的Bucket进行创建【OSS学习】

在前面学会了如何开通OSS&#xff0c;对OSS的一些基本操作&#xff0c;接下来记录一下如何通过Java代码通过SDK对OSS存储引擎里面的Bucket存储空间进行创建。 目录 1、先看看OSS&#xff1a; 2、代码编写&#xff1a; 3、运行效果&#xff1a; 1、先看看OSS&#xff1a; 此…

老师打学生违法吗该怎么处理

老师打学生&#xff1a;一个需要深入探讨的敏感话题。老师&#xff0c;肩负着教书育人的重任&#xff0c;面对学生的时候&#xff0c;法律、职业道德和个人修养常常需要我们做出权衡。那么&#xff0c;当老师打了学生这一行为发生时&#xff0c;我们该如何看待和处理呢&#xf…

蓝桥杯备战——6.串口通讯

1.分析原理图 由上图我们可以看到串口1通过CH340接到了USB口上&#xff0c;通过串口1我们就能跟电脑进行数据交互。 另外需要注意的是STC15F是有两组高速串口的&#xff0c;而且可以切换端口。 2.配置串口 由于比赛时间紧&#xff0c;我们最好不要去现场查寄存器手册&#x…

HCIP-BGP实验

实验拓扑 实验需求 1.r1上有两个换汇分别为192.168.1.0/24和192.168.2.0/24只允许学到汇总和1.0 2.r7上有两个还回172.16.1.0/24和172.16.2.0/24要求全部宣告&#xff0c;但是只有2.0可以通过 3.全网可达 实验思路 配置IP地址 BGP配置 实验步骤 配置IP地址 BGP配置 在…

驱动开发-系统移植

一、Linux系统移植概念 需要移植三部分东西&#xff0c;Uboot ,内核 &#xff0c;根文件系统 &#xff08;rootfs&#xff09; &#xff0c;这三个构成了一个完整的Linux系统。 把这三部分学明白&#xff0c;系统移植就懂点了。 二、Uboot uboot就是引导程序下载的一段代…

力扣hot100 轮转数组 一题多解 翻转数组

Problem: 189. 轮转数组 文章目录 思路复杂度Code 思路 &#x1f468;‍&#x1f3eb; 参考 复杂度 时间复杂度: O ( n ) O(n) O(n) 空间复杂度: O ( 1 ) O(1) O(1) Code class Solution {public void rotate(int[] nums, int k){int n nums.length;k k % n;reverse(…

[C++开发 02_3/5 _ C++中函数(95)]

知识点3&#xff1a;函数提高 3.1函数默认参数 3.2函数占位参数 3.3函数重载 3.1.1函数重载概述 3.1.2函数重载注意事项 引用作为重载条件 函数重载碰到默认参数

一个新的springboot-vue项目如何启动起来

一个新的springboot-vue项目如何启动起来 1.导入mysql 打开yml文件修改数据库密码 名称 用户名 2.打开pom.xml配置maven依赖 尽量换成自己使用过的版本号&#xff0c;或者打开中央仓库搜索相关内容版本号&#xff1a;https://central.sonatype.com/ 注解为黄色 说明工程…

Chrome单独配置代理的方法

Windows Windows上单独对Chrome设置代理&#xff0c;需要在启动时传递参数&#xff0c;具体步骤如下。 在Chrome浏览器的快捷方式上右击&#xff0c;进入属性。在 快捷方式 标签下找到 目标 项目&#xff0c;在最后添加 –proxy-server“socks5://xxx.xxx.xx.xx:xxxx” 如果要…

python爬虫采集下载中国知网《出版来源导航》论文文献下载_PDF文档_数据采集知网爬虫论文Python3

时隔一年&#xff0c;很久没更新博客了。今天给大家带来一个python3采集中国知网 &#xff1a;出版来源导航 这个是网址是中国知网的&#xff0c;以下代码仅限于此URL&#xff08;出版来源导航&#xff09;采集&#xff0c;知网的其他网页路径采集不一定行&#xff0c;大家可以…

部署Filebeat+Kafka+ELK 集群

目录 Kafka 概述 为什么需要消息队列&#xff08;MQ&#xff09; 使用消息队列的好处 消息队列的两种模式 Kafka 定义 Kafka 简介 Kafka 的特性 Kafka 系统架构 在zookeeper集群的基础上部署 kafka 集群 部署zookeeper集群 部署kafka集群 下载安装包 安装 Kafka Ka…

【自动化测试】读写64位操作系统的注册表

自动化测试经常需要修改注册表 很多系统的设置&#xff08;比如&#xff1a;IE的设置&#xff09;都是存在注册表中。 桌面应用程序的设置也是存在注册表中。 所以做自动化测试的时候&#xff0c;经常需要去修改注册表 Windows注册表简介 注册表编辑器在 C:\Windows\regedit…
最新文章