前端工程\模块化

前端工程\模块化🏭

本篇文章,学习记录于:尚硅谷🎢,紧接前文:邂逅Node.JS的那一夜→博客

无论是前端、后端、甚至非编程领域都有模块化的概念,只是不同的领域叫法不同,不过,新技术的产生一定是有原因的:

模块化的概念🧊

为什么需要模块化技术: 随着 Web 技术的发展,各种交互以及新技术等使网页变得越来越丰富,前端工程师登上了舞台

同时也使得我们前端的代码量急速上涨、复杂性在逐步增高,越来越多的业务逻辑和交互都放在 Web 层实现

代码一多,各种命名冲突、代码冗余、文件间依赖变大等等一系列的问题就出来了,甚至导致后期难以维护;


编程领域的模块化: 在这些问题上, java、 php 等后端语言中早已有了很多实践经验,模块化

编程领域中的模块化,就是遵守固定的规则,把一个大文件拆成独立并互相依赖的多个小模块

因为小的、组织良好的代码远比庞大的代码更加理解和维护,于是前端也开始了模块化历程;

模块的化优点✅

  • 可维护性: 模块与模块之间是独立的,一个优秀的模块会让外面的代码对自己的依赖越少越好,这样自己就可以独立去更新和改进;

  • 防止命名冲突: 模块化设计,将系统分解为独立的模块,每个模块都有自己的命名空间,这有助于避免全局范围内的命名冲突;

  • 提高代码复用性: 将代码划分为独立的模块,每个模块负责特定功能。这样的设计使得模块可以在不同的项目中重复使用;

  • 实现代码按需加载、提高团队协作、代码灵活性、降低代码复杂性… … 等: 模块化使前端更易于管理、扩展和维护;

模块化技术发展📶

前端模块化技术的发展历史经历了多个阶段,从最初的零散脚本到现代化的模块系统: 了解即可:

  • Script标签,最简单粗暴的方式:

    早期前端主要使用 <script>标签引入JavaScript,这种方式:存在全局命名空间的问题,容易引起变量冲突,难以维护;

  • 对象模拟命名空间: 为了解决全局命名空间的问题,将相关的函数和变量封装到一个全局对象中,减少了命名冲突的风险;

  • IIFE 和 闭包: ES5 没有块作用域的概念,通过闭包+IIFE 模拟模块封装的效果;

    闭包 Closure:闭包,打破了“函数作用域”的束缚,外部作用域可以访问函数内部的变量,类似Java的get|set

    IIFE Immediately Invoked Function Expression: 是一种将代码块封装在函数中并立即执行的模式,私有作用域|减少全局污染

    //(function(){ IIFE立即执行代码块 })()
    var module = (function(){
      var _count = 0;
      var fn1 = function (){ /** 代码块 */	}
      var fn2 = function fn2(){ /** 代码块 */	}
      //闭包: 暴漏需要暴漏的变量|函数,外部可以访问函数内部的属性|方法;
      return { 	
          fn1: fn1, 
          fn2: fn2,  }
    })()
    
    module.fn1();	// 对象.Xxx 获取闭包暴漏出的fn1;
    module._count; 	// undefined 对于没有暴漏的属性|函数返回undefined;
    

随着时代发展,ES6之前 JavaScript 一直没有体系的 模块化技术

社区开发者们自行制定了一些模块加载方案,最主要的有 CommonJS 和 AMD 两种,前者用于服务器,后者用于浏览器

  • CommonJS: 使用requiremodule.exports语法来导入和导出模块,主要用于服务器端的模块化技术
  • AMD Asynchronous Module Definition 适用于前端浏览器模块化技术:定义一种异步加载模块的规范,通过RequireJS库实现

模块技术深入人心,官方ECMAScript 2015 ES6的发布: 使用importexport关键字,开发者可以更轻松地组织和导入导出模块;

初体验

经过上述简单的介绍,想必对Node有了一定的了解:内置模块(属于官方领域略…) 此处针对:如何自定义模块——并+导入模块使用 “简单介绍”

Node中的模块概念其本质就是对应一个个Xxx.JS文件,通过模块规范语法进行:属性|函数暴漏 | 模块引入 语法介绍:

  • 暴漏模块数据: module.exports = ???; 用于设置模块中要暴漏的属性|方法,可以暴露任意数据类型;
  • 导入模块: require('模块路径'); 语法和内置模块一样,内容则是要导入模块的相对路径;

自定义模块:moduleOne.js

function fun(){ console.log('通过module.exports 暴漏出函数fun'); }  //声明函数

//model.exports = '设置要暴漏的属性|函数';
module.exports = fun;

自定义模块—之间—导入:mian.js

//通过require('导入自定义模块');
const fun = require('./moduleOne.js');

//调用自定义模块
fun();	

在这里插入图片描述

  • 上述简单介绍: Node中的每一个.js文件都可以理解为模块 ,具有module对象可以通过:module.exports 设置该模块作用域下的属性|函数;
  • 外界的模块想要获取: 则通过 require('自定义模块名路径') 导自定义模块,require(导入模块,返回的值) === 模块 module.exports 值:

module.exports 模块暴漏

上述简单介绍了 moudle: 在每个 .js 自定义模块中都有一个 module 对象,它里面存储了和当前模块有关的信息,.exports 可以暴露任意数据

上述暴漏了一个函数,实际开发中通常一个不仅仅一个属性|函数,.exports可以是任何数据类型所以可以是一个:{ 对象 }

且:require(导入模块,返回的值) === 模块 module.exports 值:

moduleStr.Js: 整个JS文件,直接通过module.exports 暴漏,验证:require(返回值是module.exports的value);

module.exports = "每个.JS文件模块都有一个module对象: require('导入模块,返回值的是module.exports的value')";
{ module.exports = "每个.JS文件只有一个module 即使是 { 作用域块共享同.JS文件的module对象 } 且后来者居上原则..."; }

moduleObj.JS: 通过:module.exports = { Xxx } 自定义模块暴漏多个属性|函数数据;

let funshow = function () { console.log(`我叫${name},外号叫${rname}`); }
let { name, rname } = { name: 'wsm', rname: '5400' };

/** module.exports 支持任何数据类型 { 对象类型 } */
module.exports = { name, rname, funshow, Xxx: '自定义属性...' }	//解构赋值

main.js:

/** require导入自定义模块 */
const mObj = require('./moduleObj');
const mStr = require('./moduleStr');

/** 调用模块属性|函数 */
mObj.funshow();	
console.log(mObj);
console.log(mStr);
我叫wsm,外号叫5400
{ name: 'wsm', rname: '5400', funshow: [Function: funshow], Xxx: '自定义属性...' }
每个JS只有一个module { 作用域块共享一个JS文件的module全局对象 } 且遵循后来者居上原则...

exports.Xxx 模块暴漏

对于模块暴漏,Node还提供第二种写法: exports.Xxx = Value;

moduleExp.js: exports .JS文件中的内置对象可将变量、函数或对象暴漏,以便在其他文件中引用;

  • ⚡注意:⚡ exports 不可以直接赋值: exports = ???使用exports 不可以使用 module.exports优先级高⏫ 下面详细介绍
exports.name = 'wsm';
exports.rname = '5400';
exports.funShow = function () { console.log(`我叫${exports.name},外号叫${exports.rname}`); }

main.js:

/** require导入自定义模块 */
const mExp = require('./moduleExp.js');
console.log(mExp);
mExp.funShow();
{ name: 'wsm', rname: '5400', funShow: [Function (anonymous)] }
我叫wsm,外号叫5400

二者模块暴漏区别:

🆗 经过上述简单了解到了:module.exportsexports 它们都可以暴漏数据,那么二者之间的关系呢:

模块内部 module 与 exports 的隐式关系: exports = module.exports = {}require 返回的是目标模块中 module.exports 的值 😵一下子好晕

  • 所以: exports.Xxx 相当于给 {} 对象上赋值,require 返回的是 module.exports 所以可以获取暴漏的属性;
  • 所以: module.exports优先级高,如果修改了指向,则exports设置的值也就失效了…
  • 所以: exports ≠ value; 不能直接赋值,修改了堆空间指向导致无法暴漏属性|函数;

require 注意事项:

在Node模块化中都是使用 require关键字导入模块: 导入内置模块、传入文件路径即可引入自定义文件模块;

// 加载内置模块方法:
const fs = require('fs');
// 加载自定义模块方法:
const moduleDemo = require('./moduleDemo.js');
/** 暂时省略加载第三方模块: */

对于自定义模块,require 还有一些使用注意事项⚡:

  • 对于自己创建的模块,导入时路径建议写相对路径,且不能省略: ./../
  • .js.json 文件导入时可以不用写后缀,对于同名文件:Xxx.JS|Xxx.JSON .JS优先级更高)
  • c/c++编写的 node 扩展文件也可以不写后缀,但是一般用不到,如果导入其他类型的文件,会以 JS文件进行处理;

main.js: 和 其他自定义模块的暴漏代码;
在这里插入图片描述

//导入多个自定义模块;
const module01 = require('./module01'); //建议使用相对路径,更方便加载自定义模块;
const module02 = require('./module02');
const module03 = require('./module03');
// const module04 = require('./module04');
const module05 = require('./module05.abcd');

console.log(module01);  //module01.JS   .js可以省略后缀
console.log(module02);  //module02.JS   同名文件 js|json JS优先级更大
console.log(module03);  //{ title: 'module03.JSON' }    .json可以省略后缀,并直接返回正JSON内容
// console.log(module04);   //报错: Cannot find module './module04' 非规定文件后缀无法省略后缀
console.log(module05);      //对于其他数据类型则默认以JS进行读写——>——>即: 非规范后缀文件仅识别JS的语法

require 文件夹操作:

require(‘文件夹’) 对于文件夹的导入,会有一些特殊规则,了解即可: 有助于后期的包管理工具学习

  • 如果导入的路径是个文件夹,Node则会首先检测该文件夹下 package.json 文件中 main属性 对应的文件 存在则导入,反之报错

  • 如果 main 属性不存在,或者 package.json 不存在,则会尝试导入文件夹下的 index.jsindex.json

在这里插入图片描述

main.js: require导入文件夹模块

const wsm = require('./wsm');
console.log(wsm);
  • Demo测试: 可以通过:删除package.JS文件文件中的 main:xxx.JS 属性;

require 导入模块流程:

require 导入模块流程相对比较复杂: 这里也仅仅是简单介绍:

  1. 对于核心模块: (httpfs等,直接返回模块;
  2. 非核心模块: 获取导入文件路径,相对路径—>—>绝对路径
  3. 缓存检测: 首先从缓存中查找,如果缓存存在,则直接返回缓存模块,
  4. 缓存不存在: 根据获取的绝对路径|文件夹路径根据规则找到对应的文件,使用FS模块加载该文件并通过:arguments.callee.toString() 查看自执行函数,通过:(function(){})()立即执行函数|执行 最后缓存|返回模块值

NVM 版本控制器

NVM 全称:Node Version Manager node 版本管理工具:

顾名思义它是用来管理 node 版本的工具,方便在同一台设备切换不同版本的 Node

实际开发过程中,经常遇到不同的项目所使用的 Node版本不同,导致开发者需要不停的调整Node版本,NVM就是为了解决这个问题!

NVM 下载|安装

NVM 并不是Node,不会影响Node的任何命令,仅是管理多个Node版本的一个工具: 下载地址

windows系统下载nvm-setup.zip安装包,如果电脑上之前已经安装了node,先卸载,然后解压nvm-setup.zip 双击.exe 进行安装:

  • win+r cmd nvm -v 查看NVM版本🎉🎉 安装成功!!

  • 配置NVM node镜像: 打开nvm的安装目录,找到setting.txt文件:

    arch:64
    proxy:none
    node_mirror:npm.taobao.org/mirrors/node/
    npm_mirror:npm.taobao.org/mirrors/npm/
    

安装注意事项:⚡⚡

  • 安装路径: 不能有中文,可以自定义目录,但貌似程序有时候并不会自动创建nodejs目录,该目录存放Node下载公共module)
  • 环境变量: 默认情况程序会自动配置环境变量,如没有则需手动配置:NVM_HOMENVM_SYMLINKPath
  • NVM命令: 建议使用NVM命令时候,以管理员方式运行CMD,否则报错;

NVM常用命令

  • nvm on :开启node.js版本管理;

  • nvm off :关闭node.js版本管理,关闭|开启 同时会影响Node的使用;

  • nvm list: 显示已安装的版本,* 开头的表示当前使用的版本;

  • nvm list available: 显示所有可以下载的 Node.js 版本;

  • nvm uninstall xx.xx.xx: 删除指定的 Node.js版本;

  • nvm install xx.xx.xx: 安装指定的 Node.js版本;

  • nvm install latest: 安装最新版的 Node.js;

  • nvm use xx.xx.xx: 切换指定的Node.js;

注意:为了避免出错,切换Node版本|使用NVM命令,建议使用管理员模式;

NPM 包管理工具

NPM/包

包是什么:

NodeJS 中的 第三方模块又叫做 第三方模块指的是同一个概念,只不过叫法不同;

由于 Node 的内置模块仅提供了一些底层的 API,导致在基于内置模块进行项目开发的时,效率很低

  • 包是基于内置模块封装开发出来的 ,提供了更高级、更方便的 API, 极大的提高了开发效率
  • 包和内置模块之间的关系,类似于速冻食品本质一样,更方便制作加工;

包的来源: 不同于 Node.js 中的内置模块与自定义模块, 包是由第三方个人或团队开发出来的 ,免费供所有人使用;

注意 :Node.js 中的包都是免费且开源的,不需要付费即可免费下载使用,国外npm, Inc公司: 全球最大的包共享平台!!!

NPM 包管理工具是什么:

NPM 全称 Node Package Manager 翻译为中文意思是『Node 的包管理工具』

NPM 是 NodeJS 内置的包管理工具: 用于NodeJS包的发布、传播、依赖控制、管理已经安装的包

NPM 提供了命令行工具,使你可以方便地下载、安装、升级、删除包,也可以让你作为开发者发布并维护包


前端常见的包管理工具有:

  • npm 是Node.js的包管理工具,广泛用于前端开发,允许开发者安装、共享和管理JavaScript代码包;
  • yarn 由Facebook、Google、Exponent和Tilde等公司共同开发,与npm兼容:提供更快的安装速度、依赖关系管理;
  • cnpm 是一个淘宝镜像提供的用于替代 npm 的客户端工具,主要目的是解决在国内使用 npm 安装包时速度较慢的问题;

NPM 基本使用

Node 在安装时会自动安装 npm 可通过CMD 快速查看版本号:

  • npm -v:查看当前npm的版本号
  • node -v:查看当前Node的版本号

NPM 初始化

使用NPM 进行包管理|项目管理,首先需要项目根目录进行初始化:

  • 在项目的根目录中打开终端: 打开命令行终端,进入你希望创建项目的目录;
  • 运行 npm init 命令: 在终端中执行初始化命令:npm init|npm init -y 跳过手动输入默认信息)
  • 生成 package.json 完成所有提示后,npm init 将生成一个 package.json 文件,并将其保存在项目的根目录中

在这里插入图片描述

🆗通过这个过程,we成功地初始化了一个新的 Node 项目,并创建了一个包含项目基本信息的 package.json 文件;

package.json 文件:

package.json 是 Node 项目中非常重要的配置文件:项目信息、依赖项管理、脚本定义、模块入口定义、开源协议...

{
  "name": "npm01",      //包名  
  "version": "1.0.0",   //包版本
  "description": "",    //包描述
  "main": "index.js",   //包入口文件: 指定项目作为模块时的入口文件;
  "scripts": {          //自动脚本定义: 定义一些命令行脚本,方便执行常见的任务: `npm run Xxx` 执行;
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "",         //包作者
  "license": "ISC",    	//开源协议(比较复杂暂时不介绍...
   dependencies: { 		//dependencies 专门用来记录您使用 npm i 安装的包
   },
}
初始化过程注意事项⚡:
  • package.json 支持手动创建与修改

  • version 版本号要求 x.x.x 的形式定义:x 必须是数字,默认值是 1.0.0

  • 可以使用 npm init -y 或者 npm init --yes 极速创建 package.json

  • 包名不能使用中文、大写,默认值是:文件夹的名称 ,所以文件夹名称也不建议使用:中文和大写

NPM 搜索包

项目开发过程中我们为了快速完成某个任务,可以通过NPM提供的命令来寻找包,快速开发:

  • npm s 包类型npm serach 包类型 通过指定包类型,到NPM官网库中寻找匹配的包来进行开发

在这里插入图片描述

上述,通过控制命令查找包太不方便,实际开发过程中更多的是通过官网进行查找: npmjs.com

NPM 下载安装包

🆗,确认需要安装的包,开始进行下载使用了,NPM 常用下载命令: npm i 包名npm install 包名

运行之后文件夹下会增加两个资源: node_modules 文件夹 存放下载的包、package-lock.json 包的锁文件 用来锁定包的版本
在这里插入图片描述

使用uniq 数组工具包:

第三方包: 可以快速的满足程序的开发,uniq可以帮助我们快速操作数组:快速去重一组数组的重复元素;

index.js: 导入第三方uniq包实现快速数组去重开发效果;

//导入uniq第三方包:包下载之后和内置模块一样直接导入;
const uniq = require('uniq');
//使用uniq第三方包:快速实现数组去重;
let arr = [1,2,3,3,4,4,5,6,5,7,8,8];
let arr2 = uniq(arr);
console.log(arr2);                  //[1,2,3,4,5,6,7,8]

require 导入三方包基本流程: 向上原则

  • 在当前文件夹下 node_modules 中寻找同名的文件夹,

  • 没有继续:向上 node_modules 中寻找同名的文件夹,直至找到磁盘根目录,

  • 向上原则是为了建立清晰的层次结构、提高软件系统的模块化程度,有助于创造稳定、灵活、可维护和可扩展的软件设计;

NPM 安装包的依赖

生产|开发依赖包:

实际开发过程中为了方便稳定开发: 程序环境分为开发环境|生产环境

  • 开发环境: 是程序员专门用来写代码的环境,一般是指程序员的电脑,开发环境一般由开发者自己访问;
  • 生产环境: 是项目代码正式运行的环境,一般是指正式的服务器电脑,生产环境的项目每个客户都可以访问;

所以: 在开发过程中对依赖包也有分类,有些包仅在开发过程中使用,如果发布一起打包则会占用服务器性能效率;

我们可以在安装时设置选项来区分依赖的类型 ,目前分为两类:

  • 生产依赖安装(默认): npm i -S 包名npm i --save 包名 包信息保存在 package.json 中 dependencies 属性;
  • 开发依赖安装: npm i -D 包名npm i --save-dev 包名 包信息保存在 package.json 中 devDependencies 属性;
全局依赖包:

另外NPM除了安装开发依赖包: 还支持将包安装到全局环境中,可以在任何项目中使用,通常用于安装命令行工具;

不是所有的包都适合全局安装 , 只有全局类的工具才适合,可以通过查看包的官方文档来确定安装方式 ,这里先不必太纠结

  • npm install -g 包名 通过以下命令可以全局安装包,-g 选项表示全局安装;

示例: 以安装 nodemon(一个用于监视文件变化并自动重启 Node.js 应用程序的工具)为例:

在这里插入图片描述

  • 使用全局包nodemon 监听|启动node项目:项目文件更新自动重新启动!热部署!

注意事项:

  • 全局安装的包通常会被安装在系统的全局目录中:node安装目录下的 node_modules
  • 在某些系统上,可能需要使用管理员权限执行全局安装的命令(使用 sudo 或以管理员身份运行命令)
安装包依赖:

在项目协作中有一个常用的命令就是 npm i

通过该命令可以依据 package.jsonpackage-lock.json 的依赖声明安装项目依赖

  • 因为: 在多人协作开发过程中,项目可能会用到很多很多的包 而: 导致项目体积过大,不方便团队成员之间共享项目源代码;

  • 所以: 在实际开发过程中,不建议将node_modules文件夹 进行git管理,建议添加 .gitignore 忽略文件;

  • npm 提供了一个快捷命令:快速安装 package.json 管理的所有依赖包:npm install 或 npm i

  • 当我们拿到一个 剔除了 node_modules 的项目之后: 最长使用的命令

指定包版本:

项目中可能会遇到版本不匹配的情况,有时就需要安装指定版本的包,可以使用下面的命令的:

  • 命令格式: npm i 包名@版本号 实际情况可以先去官网确认版本存在,避免麻烦;
  • 命令示例: npm i jquery@1.11.2 下载安装指定的1.11.2版本的jquery包;
卸载依赖包:

项目中可能需要删除某些不需要的包,可以使用下面的命令

  • 局部删除: npm uninstall 包名npm remove 包名npm r 包名
  • 全局删除: npm uninstall -g 全局依赖包名npm remove -g 全局依赖包名

⚡注意⚡: 项目中执行删除命令,会把卸载的包自动从 package.jsondependencies 中移除掉 团队开发需协商操作;

定义执行脚本:

package.json:package.json 文件中,你可以通过 scripts 字段定义一些自定义脚本,以便在项目中执行特定的任务

{
  //... ...
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "server": "node ./index.js",
    "start": "node ./index.js"
  },
 //... ...
}

自定义脚本可以通过 npm run script-name 的方式运行: 且自定义脚本也支持向上原则

  • 上述为例: npm run server 就相当于在执行:node ./index.js

  • start别名,使用时可以省略 run npm start 就相当于:node ./index.js

  • 用途:如果一个项目有多种环境配置,每次启动一堆参数: 定义脚本可以更快速方便启动|管理不同环境的项目;

CNPM | YNPM

CNPM

CNPM 全称为 China Node Package Manager

是一个淘宝构建的 npmjs.com 的完整镜像,也称为**『淘宝镜像🪞**

由于一些网络限制和访问速度的问题,国内开发者在使用 npm 安装 Node包时可能会遇到下载速度慢、甚至失败的情况

CNPM 通过在国内搭建了镜像服务,将 npm 的包镜像到国内服务器上,从而提高了包的下载速度,并减轻了对国际网络的依赖;

NPM 使用 CNPM

安装: 通过 npm 来安装使用:cnpm 全局服务工具;

npm install -g cnpm --registry=https://registry.npmmirror.com

从安装命令就可以知道,cnpm 其本质还是npm,所以其使用命令也和npm几乎异,本魔法师也不常用🧙‍♂️🪄 简单介绍:

#cnpm的命令几乎和npm一致多了一个c开头;

#初始化	cnpm init
#安装包	
	cnpm i 包名		# 默认部署
	cnpm i -S 包名	# 生产环境包
	cnpm i -D 包名	# 开发环境包
	cnpm i -g 包名	# 全局安装包
#构建项目依赖 cnpm i
NPM 配置淘宝镜像

对于习惯使用NPM 朋友,npm本身也支持修改包源来使用淘宝镜像:NPM管理镜像源有两种方式:

方式一:直接配置

npm config set registry https://registry.npmmirror.com/

方式二:使用nrm工具,配置管理npm镜像地址

  • 安装nrm: npm i -g nrm

  • 通过nrm 管理切换镜像地址: nrm use <源名>nrm ls 查看当前,可选源 星号代表当前使用源

      npm ---------- https://registry.npmjs.org/
      yarn --------- https://registry.yarnpkg.com/
      tencent ------ https://mirrors.cloud.tencent.com/npm/
      cnpm --------- https://r.cnpmjs.org/
    * taobao ------- https://registry.npmmirror.com/
      npmMirror ---- https://skimdb.npmjs.com/registry/
    
  • 添加源: nrm add <源名> <源地址> 示例: nrm add wsm https://wsm/

  • 删除源: nrm del <源名> 示例: nrm del wsm

YNPM

yarn 是由 Facebook 在 2016 年推出的新的 Javascript 包管理工具,官方地址

  • 超级安全:在执行代码之前,yarn 会通过算法校验每个安装包的完整性
  • 超级可靠:使用详细、简洁的锁文件格式和明确的安装算法,yarn 能够保证在不同系统上无差异的 工作
  • 速度超快:yarn 缓存了每个下载过的包,所以再次使用时无需重复下载,且利用并行下载以最大 化资源利用率,因此安装速度更快

yarn 安装: npm i -g yarn 通过 npm 来安装使用:yarn全局服务工具;

yarn 常用命令: yarn的使用和npm 也相似,实际开发过程中下载速度块也有很多人使用…

# 初始化
	yarn init \ yarn init -y
# 安装包
	yarn add 包名				#生产依赖
	yarn add 包名 --dev		#开发依赖
	yarn global add 全局包名   #node全局依赖
# 删除包
	yarn remove 包名
    yarn global remove 全局包名
# 一键构建项目依赖
	yarn
# 运行命令别名<script>
	yarn <别名>		#和npm run <别名> 不同不需要加 run

yarn 注意事项: yarn全局包安装,并不会默认配置系统的全局变量所以会,产生安装识别的错觉:个人建议全局可以考虑npm

npm 和 yarn 选择

个人项目: 如果是个人项目, 哪个工具都可以 ,可以根据自己的喜好来选择

企业项目: 企业项目要根据项目代码来选择,可以通过锁文件判断项目的包管理工具,切记勿串用导致包管理异常!


ESM 模块化

在 ES6 之前,JavaScript 并没有原生支持模块化,因此开发者们使用一些第三方库或自己实现一些模块化方案:

或多或少存在一些问题: 命名冲突、依赖管理,单个对象导出,多次导出会覆盖之前的结果;

于是官方在 ES6 引入了 ESModule 模块化规范来解决这些问题:

  • ESModule 模块化规范是一种静态的模块化方案:
  • 它允许开发者将代码分割成小的、独立的模块,每个模块都有自己的作用域,ESModule 规范是基于文件的
  • ESModule 的模块解析规则是基于 URL 解析规则的:import 语句导入模块时,模块加载器会根据指定的路径解析出对应的 URL
  • 浏览器中: URL 解析规则是基于当前页面的 URL 进行解析,并将其作为唯一标识符来加载对应的模块文件;
  • **NodeJs 中:**URL 解析规则是基于当前运行脚本的路径进行解析;

ESM 初体验:

ESM是官方推行原生的模块化规范,很多浏览器皆支持,类似CommonJs的写法,通过设置type=module 用于HTML中,Node也逐渐开始支持; 语法如下:

  • 模块暴漏: exports 关键字,用于设置模块中要暴漏的属性|函数变量,可以暴露任意数据类型;
  • 模块导入: import 关键字,用于导入Xxx.JS文件地址,并获取其中暴漏的属性|函数变量;

moduleOne.JS: 分别暴露:分别在需要暴漏的属性|函数变量前加:export关键字声明;

export const name = 'wsm';
export function sayName() { return `我叫${name}`; }

index.HTML: 前端页面默认通过<scirpt type="module" >块进行接收,需设置类型则浏览器无法判断是模块导入报错;

导入语法: import * as 变量别名 from "./JS文件地址.Js";

  • import:模块导入的关键字、*:表示接收所有的.JS 文件暴漏变量、
  • as 变量别名:将暴漏的变量封装为一个新的变量别名、from "./文件地址.JS" 指定要导入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>
    <!-- 指定scirpt块用于模块导入 -->
    <script type="module">
        // 通用import导入模块,获取|使用模块对象;
        import * as m1 from "./moduleOne.js";
        console.log(m1);
        console.log(m1.name);
        console.log(m1.sayName());
    </script>
</body>
</html>

启动工具: 即可在浏览器,开发者工具中查看console 查看输出内容;🆗 页面模块导入成功!

模块的暴漏:

ESM: 针对多种不同的应用场景,有多种模块暴漏方式:分别暴漏统一暴漏default默认暴漏 可能不同人不同叫法,总体如此;

/** 分别暴漏 */
export const name = 'wsm';
export function sayName() { return `我叫${name}`; } //分别在需要暴漏的属性|函数变量前加:`export`关键字声明;
/** 统一暴漏 */
const name = 'wsm';
function sayName() { return `我叫${name}`; }

export { name, sayName }    //在 暴漏.JS文件末尾通过: export{x,x,...} 批量管理暴漏的变量;
/** 默认暴漏: export default { 键:值形式属性|函数暴漏; } */
export default { name: 'wsm', sayName: function () { return `我叫${this.name}`; } }	//default相当于对象的封装所以需要this引用;

在这里插入图片描述
根据顺序对应三段module 的导入: ⚡⚡ 特殊的是: export defalut{ } 默认暴漏返回的是一个 default 对象;

模块的导入:

  • 常规导入: import * as 别名 from "文件地址.JS"
  • 默认暴漏简: import 别名 from "默认暴漏文件地址.JS"默认暴漏的.JS文件,支持的简介导入写法;
  • 解构赋值导入: import {模块匹配变量名, 模块匹配变量名 as 别名, ...} from "文件地址.JS" 对于多个模块导入可能存在同名变量,as 别名可以解决;
<script type="module">
    /** 常规导入方式: */
    import * as m1 from "./module01.js";
    import * as m2 from "./module02.js";
    import * as m3 from "./module03.js";
    console.log(m1);
    console.log(m2);
    console.log(m3);

    /** 解构赋值导入: 想对于*的全导入解构更具有选择性 */
    import { name, sayName } from "./module01.js";
    import { default as defaultobj } from "./module03.js";
    import { name as name2, sayName as sayName2 } from "./module02.js";
    console.log(sayName());
    console.log(sayName2());
    console.log(defaultobj.sayName());

    /** 默认暴漏(简介形式: 仅默认暴漏支持... */
    import defaultobj2 from "./module03.js";
    console.log(defaultobj2.sayName());
</script>

前端工程化:

ESM 项目结构:

🆗,上述了解了ESM 模块化的使用: 而对于一个项目所需要的模块非常多,实际开发中需要大量的代码来进行模块导入,而为了解决这个问题:

针对项目中大量的模块导入,为了方便管理: 通常配置一个入口.JS 进行批量导入|管理模块;

index.html 页面仅需要导入一个 mapp.JS 即可批量的导入所有的模块引用;

ESM 结合 NPM:

🆗,到这里已经基本掌握ESM模块化的基本流程: 但我们都知道ES6模块化技术,出现晚于很多模块化社区:

实际开发中经常二者结合使用,达到1+1>2 的效果,ESM可以结合NPM 强大的第三方模块社区库,更加方便快速完成代码开发:

解决ES版本兼容问题:
  • 我们都知道早期,互联网大战时代诞生了很多,JS浏览器环境,导致同一个代码,不同的浏览器可能展示效果不同;
  • 而,ES6新增的模块化技术,很多的浏览器版本短时间无法完全适配,导致很多麻烦😵经过一些列调用;
  • 最终解决方案:通过编译工具将ES6语法,翻译成ES5进行页面引用渲染 编译工具: babel
  • 同时ES6 支持 NPM可以更方便在项目中使用 Babel Babel官方🔗

1. NPM 初始化前端项目

2. NPM 安装需要的服务包工具: babel-cli 命令行工具babel-preset-env 预设翻译ECMA包browserify 前端打包工具

  • 使用:babel-cli+babel-preset-env 将原生翻译成了CommonJS模块化语法,浏览器不识别)、所以需用Browserify再次编译;
#切换项目根目录,npm 初始化
npm init -y

#NPM 安装翻译|打包工具:
#babel-cli 命令行工具、
#babel-preset-env 预设翻译ECMA包、browserify|webpack 浏览器打包工具本次使用browserify
npm i babel-cli babel-preset-env browserify -D  #NPM进行批量安装 -D 开发依赖; 下载的比较慢耐心等待;

#npm 使用babel+babel-preset-env进行翻译
#npx babel 源目录 -d 新目录 --presets=babel-preset-env
npx babel resource/JS -d dist/JS --presets=babel-preset-env
#resource/JS -d dist/JS 将原resource/JS目录ES6语法——转换——为dist/JS目录ES5语法;

#babel生成的仅是CommonJS模块化语法,并不支持浏览器直接使用,还需要browserify进行二次打包;
npx browserify dist/JS/mapp.js -o dist/main.js
# npx browserify 源主文件.JS -o 新主文件.JS

3. 前端页面引入:最终browserify 包管理工具生成的: main.js 主文件模块入口

<!-- 指定scirpt块用于模块导入 -->
<!-- <script src="./resource/JS/mapp.js" type="module"></script> -->
<script src="./dist/main.js" type="module"></script>    <!-- 页面引入翻译打包后的原生JS,避免版本导致渲染异常! -->
ESM 引入NPM包:

Demo: 使用 NPM 安装 Jquery包,并使用Jquery修改页面背景颜色;
在这里插入图片描述

resource/JS/mapp.js: 麻烦:前端工程化之后,代码更新也需要随之进行更新,才能看到实施效果… 后期框架解决了这个问题;

NPM 安装完包,在原生ES6中可以直接通过:import 别名 from "模块名"; 进行导入模块,参与项目快速开发;

//使用Jquery 修改页面背景颜色
import $ from 'jquery';     //相当于node中的 const $ = requrie('query');
$('body').css('background','pink');

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

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

相关文章

【WPF.NET开发】优化性能:图形呈现层

本文内容 图形硬件呈现层定义其他资源 呈现层为运行 WPF 应用程序的设备定义图形硬件功能和性能级别。 1、图形硬件 对呈现层级别影响最大的图形硬件功能包括&#xff1a; 视频 RAM - 图形硬件中的视频内存量决定了可用于合成图形的缓冲区大小和数量。 像素着色器 - 像素着…

【升级openssl1.1.1t报错libssl.so.1.1: cannot open shared object file】

升级openssl报错&#xff1a; openssl vesion openssl: error while loading shared libraries: libssl.so.1.1: cannot open shared object file: No such file or directory 编译安装openssl1.1.1t当执行openssl version的时候&#xff0c;报上述错误&#xff0c;将编译到的…

OCP NVME SSD规范解读-8.SMART日志要求-4

SMART-21&#xff1a;这段描述解释了一个与设备内部I/O操作非对齐相关的计数器功能。该计数器记录的是由NVMe SSD执行的、起始地址未按照设备内部间接寻址单元&#xff08;IU&#xff0c;Indirection Unit&#xff09;大小进行对齐的写入I/O操作数量。 “Alignment”指的是每次…

2014年苏州大学837复试机试C/C++

2014年苏州大学复试机试 要求 要求用C/C编程&#xff1b;对程序中必要的地方进行注释。上机规则 请在电脑桌面上新建一个文件夹文件夹名为考试姓名&#xff08;中文&#xff09;&#xff1b;考试完毕后&#xff0c;将所编写的文件放在上述文件中。 第一题&#xff08;20分&…

使用ffmpeg madiamtx制作一个rtsp源

有很多人在跑rtsp解码的demo的时候, 苦于找不到一个可以拉流的源, 这里说一个简单的方法. 使用mediamtx, 加ffmpeg加mp4文件方式, 模拟一个rtsp的源. 基本架构就是这样. 在PC上, 这里说的PC可以是远程的服务器, 也可以是你的开发用的windows, 都行. 把mediamtx, 在pc上跑起来 …

如何有效避免市场恐慌性抛售?

布雷特斯坦伯格是一位备受尊敬的交易心理导师&#xff0c;曾担任华尔街多家顶级培训机构的心理导师&#xff0c;指导交易员们如何应对心理挑战。作为一名心理学教授和资深交易员&#xff0c;他对交易心理的理解远超常人。人们普遍认为&#xff0c;要想在交易领域取得成功&#…

BUUCTF-Real-[PHP]XXE

目录 1、原理 2、XXE漏洞产生的原因 3、开始复现 paylaod 复现 4、flag 1、原理 XML数据在传输过程中&#xff0c;攻击者强制XML解析器去访问攻击者指定的资源内容&#xff08;本地/远程&#xff09;&#xff0c;外部实体声明关键字SYSTEM会令XML解析器读取数据&#xf…

基于SpringBoot的高校社团管理系统

末尾获取源码作者介绍&#xff1a;大家好&#xff0c;我是何时&#xff0c;本人4年开发经验&#xff0c;专注定制项目开发 更多项目&#xff1a;CSDN主页YAML 我欲乘风归去 又恐琼楼玉宇 高处不胜寒 -苏轼 目录 一、项目简介 二、开发技术与环境配置 2.1 SpringBoot框架 2…

sqlmap的使用

2024.1.31 sqlmap支持五种不同的注入模式&#xff1a; 1、布尔盲注2、时间盲注3、报错注入4、联合注入5、堆叠注入 检测注入 GET请求的基本格式 ​python sqlmap.py -u <测试网址> Ps:不知道为什么我的sqlmap使用时前面要加python&#xff0c;而大部分其他教程没提到…

Maven简述

Maven是用于管理和构建Java项目的工具&#xff0c;提供了一套标准化的项目结构&#xff0c;提供了一套标准化的构建流程&#xff0c;提供了一套依赖管理机制&#xff0c;通过Maven使得所有IDE构建的项目结构完全一样&#xff0c;让项目可以通用。 项目名称下分为src 和 pom.xm…

河南省考后天网上确认,请提前准备证件照哦

✔报名时间&#xff1a;2024年1月18号一1月24号 ✔报名确认和缴费&#xff1a;2024年1月 31号一2月4号 ✔准考证打印&#xff1a;2024年3月12号一3月17号 ✔笔试时间&#xff1a;2024年3月16日-2024年3月17日。 ✔面试时间&#xff1a;面试时间拟安排在2024年5月中旬 报名网址&…

【Pwn | CTF】BUUCTF test_your_nc1

天命&#xff1a;时隔两年&#xff0c;又杀回了pwn这里 拿到题目的提示&#xff0c;测试你的nc工具 这题直接连接就可以了&#xff0c;windows装了nc工具&#xff0c;直接耍 nc node5.buuoj.cn 28930 下面给一点nc命令的解释&#xff0c;文心一言得出来的 nc命令是一个用于网…

CTF-WEB的入门真题讲解

EzLogin 第一眼看到这个题目我想着用SQL注入 但是我们先看看具体的情况 我们随便输入admin和密码发现他提升密码不正确 我们查看源代码 发现有二个不一样的第一个是base64 意思I hava no sql 第二个可以看出来是16进制转化为weak通过发现是个弱口令 canyouaccess 如果…

[349. 两个数组的交集](C语言)(两种解法:双指针+排序,哈希)

✨欢迎来到脑子不好的小菜鸟的文章✨ &#x1f388;创作不易&#xff0c;麻烦点点赞哦&#x1f388; 所属专栏&#xff1a;刷题 我的主页&#xff1a;脑子不好的小菜鸟 文章特点&#xff1a;关键点和步骤讲解放在 代码相应位置 前提&#xff1a; 看本文章之前&#xff0c;建…

iOS开发Xcode中的ld64和-ld_classic是什么意思

在iOS应用程序开发中&#xff0c;Xcode是一款广泛使用的集成开发环境&#xff08;IDE&#xff09;&#xff0c;而链接器是构建应用程序的关键组成部分之一。在Xcode中&#xff0c;我们常常会遇到两个重要的概念&#xff1a;ld64和-ld_classic。它们分别代表了默认链接器和经典链…

Linux文本三剑客---awk经典案例

awk&#xff08;是一种处理文本文件的应用程序&#xff0c;它依次处理文件的每一行&#xff0c;并读取里面的每一个字段。&#xff09; awk 包含几个特殊的内建变量&#xff08;可直接用&#xff09;如下所示&#xff1a; 1、获取根分区剩余大小 #可以使用df -h命令来查看所有…

OceanBase与新加坡南洋理工大学合作,推进机器学习与数据库技术融合

1月31日&#xff0c;OceanBase和新加坡南洋理工大学&#xff08;以下简称“南洋理工大学”&#xff09;签署合作协议&#xff0c;探索数据库智能化的技术创新。合作将以OceanBase 4.0 小鱼&#xff08;Paetica&#xff09;为研究基础&#xff0c;推进机器学习与数据库技术融合。…

力扣hot100 数据流的中位数 大小根堆

Problem: 295. 数据流的中位数 文章目录 思路复杂度&#x1f496; Code 思路 &#x1f468;‍&#x1f3eb; 参考 大根堆维护较小值&#xff08;堆顶即中位数&#xff09;&#xff0c;小根堆维护较大值&#xff08;堆顶可能是中位数之一&#xff09;维护小堆长度较长&#x…

初谈C++:缺省参数函数重载

文章目录 缺省参数概述缺省参数的分类全缺省半缺省参数 注意 函数重载概述重载类型不同参数类型不同参数个数不同参数类型顺序不同 C支持函数重载的原理 缺省参数 概述 缺省参数是声明或定义函数时为函数的参数指定一个缺省值。在调用该函数时&#xff0c;如果没有指定实参则…

TI AM5708工业派

文章目录 一、TI AM5708工业派简介二、主要使用的功能三、J12 扩展接口四、NFS代码实现总结 一、TI AM5708工业派简介 TI AM5708工业派是基于美国德州仪器&#xff08;TI&#xff09;的AM5708处理器所开发的智能硬件工业派&#xff0c;主要面向工业生产、图像处理、智能人机交…