ES Module使用-原理-包管理工具npm(一)

本文涉及的产品
云解析 DNS,旗舰版 1个月
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
全局流量管理 GTM,标准版 1个月
简介: ES Module使用-原理-包管理工具npm

ES Module使用-原理-包管理工具npm

(理解)前端使用模块化的方案解析

  • es modules
  1. ECMA2015提出模块化规范
  2. 必须浏览器本身支持es modules才能够使用


  • webpack 模块化打包工具
  1. 如果浏览器不支持的话我们可以通过webpack进行打包,这样就算浏览器不支持也能够进行使用,因为webpack内部已经做出处理了。后续我们也会写webpack内部是怎么实现的


认识 ES Module

  • JavaScript没有模块化一直是它的痛点,所以才会产生我们前面学习的社区规范:CommonJS、AMD、CMD等,所以在ECMA推出自己的模块化系统时,大家也是兴奋异常
  • ES Module和CommonJS的模块化有一些不同之处:
  • 一方面它使用了importexport关键字
  • 另一方面它采用编译期的静态分析,并且也加入了动态引用的方式
  • ES Module模块采用export和import关键字来实现模块化:
  • export负责将模块内的内容导出
  • import负责从其他模块导入内容;
  • 了解:采用ES Module将自动采用严格模式:use strict

(掌握)ESModule的基本的导入导出

案例代码结构组件

  • 这里我在浏览器中演示ES6的模块化开发:
<script src="./modules/foo.js" type="module"></script>
<script src="main.js" type="module"></script>
  • 如果直接在浏览器中运行代码,会报如下错误:(不开启本地服务)

image.png

  • 这个在MDN上面有给出解释:
  • 我这里使用的VSCode插件:Live Server

exports关键字

  • export关键字将一个模块中的变量、函数、类等导出;
  • 我们希望将其他中内容全部导出,它可以有如下的方式:
  • 方式一:在语句声明的前面直接加上export关键字
  • 方式二:将所有需要导出的标识符,放到export后面的 {}中
  • 注意:这里的 {}里面不是ES6的对象字面量的增强写法,{}也不是表示一个对象的
  • 所以: export {name: name},是错误的写法;
  • 方式三:导出时给标识符起一个别名
  • 通过as关键字起别名
//方式2
//定义的时候就直接导出了,这种方式不能起别名,但有时候反而更加方便
export const name = "小余"
export function sayHello(){
    console.log("你好啊")
}
export class Person{
    
}
//方式3
//导出取别名
exports {
    name as fname//要导出本文件的name,取别名为fname
}
//导入
import {fname} from "./xxx"
//为什么这么做?因为导入怕原先变量名跟当前文件的变量名有冲突,而直接修改导入文件的变量名又会突兀又麻烦,所以在导出的时候就采用取别名的方式

import关键字

  • import关键字负责从另外一个模块中导入内容
  • 导入内容的方式也有多种:
  • 方式一:import {标识符列表} from '模块';
    注意:这里的{}也不是一个对象,里面只是存放导入的标识符列表内容
import {} from "./xxx"//常用


  • 方式二:导入时给标识符起别名
    通过as关键字起别名
//刚刚导出的第二种方式无法在导出的时候起别名,那就可以在导入的时候起别名
import {name as myName} from "./xxx"//常用
//这种起别名方式更常用


  • 方式三:通过 * 将模块功能放到一个模块功能对象(a module object)上
//*就全部导入的意思,as foo就是给*起别名
import * as foo from "./foo.js"//特殊情况使用


(掌握)ESModule的导入和导出方式扩展export和import结合使用

  • 补充:export和import可以结合使用
//原写法
import {sum} from "./bar"
export{sum}
//优化写法1,两步省略到一步,随着使用次数的增加,省略的就会越明显,也更简洁(阅读性强),推荐这种
export { sum as barSum} from "./bar.js"//导出来自./bar.js的sum,并起别名为barSum
//优化写法2,导入多个内容直接超级简写(简洁性强)  有总结工具文档的话使用这种
export * form "./bar.js"//导出来自./bar.js的所有内容
  • 为什么要这样做呢?
  • 在开发和封装一个功能库时,通常我们希望将暴露的所有接口放到一个文件中(比如放到index.js里面)
  • 这样方便指定统一的接口规范,也方便阅读
  • 这个时候,我们就可以使用export和import结合使用

而且我们专门拿出一个文件用来存放导入的东西的时候,就不用到处翻来翻去的了,使用的都是统一在index.js里面,甚至文件名都可以直接省略掉,而是填入文件夹名,文件夹名就会自动推导去找自身里面的index.js,非常的科学规范

(理解)ESModule的导入和导出结合使用

default用法

  • 前面我们学习的导出功能都是有名字的导出(named exports):
  • 在导出export时指定了名字
  • 在导入import时需要知道具体的名字

其实也可以不指定名字的,后续到webpack的时候再来进行讨论

  • 还有一种导出叫做默认导出(default export)
  • 默认导出export时可以不需要指定名字
  • 导入时不需要使用 {},并且可以自己来指定名字
  • 它也方便我们和现有的CommonJS等规范相互操作
  • 注意:在一个模块中,只能有一个默认导出(default export)
//xiaoyu文件
function parseLyric(){
    return ["歌词"]
}
const name = "小余"
//1.默认导出
export default parseLyric
//2.定义标识符直接作为默认导出(一个模块只能有一个默认导出,这里主要是为了记录所以才写在一起)
export default function(){//直接默认导出甚至可以不起函数名字,而是由导出的那方去决定名字
    return ["十万八千梦"]
}
//在另一个文件中导入
import aaaa from "./xiaoyu"//这个aaaa是随便起的名字,由于是默认导出的方式,所以取名是什么在我们导入的时候可以随便起

(掌握)ESModule的默认导出和导入

import函数

模块要放在js最顶层的,不允许在逻辑代码中编写import导入。


而且顶层的import引入的路径是不能够拼接的



  • 因为顶层模块是最先执行的,优于代码的执行,所以当模块开始执行的时候,字符串是还没有拼起来的,所以会报错
//错误做法
import {name,age} from ("./foo"+".js")
  • 通过import加载一个模块,是不可以在其放到逻辑代码中的,比如:
  • 为什么会出现这个情况呢?
  • 这是因为ES Module在被JS引擎解析时,就必须知道它的依赖关系
  • 由于这个时候js代码没有任何的运行,所以无法在进行类似于if判断中根据代码的执行情况
  • 甚至拼接路径的写法也是错误的:因为我们必须到运行时能确定path的值
  • 但是某些情况下,我们确确实实希望动态的来加载某一个模块:
  • 如果根据不懂的条件,动态来选择加载模块的路径(就是逻辑成立,我们才导入某个模块)
  • 这个时候我们需要使用 import() 函数来动态加载

import函数返回一个Promise,可以通过then获取结果

const flag = "作者是xiaoYu"
if(flag){//满足逻辑
    // const importPromise = import("./foo.js")
    // importPromise.then(res => {
    //     //import()返回的是一个Promise,所以我们可以这样来决定处理
    // })
    //简写
    import("xiaoyu").then(res =>{
      console.log("你说对了,作者是:",res);
    })
}

import meta

  • import.meta是一个给JavaScript模块暴露特定上下文的元数据属性的对象。
  • 它包含了这个模块的信息,比如说这个模块的URL
  • 在ES11(ES2020)中新增的特性

(理解)ESModule的解析过程和原理

ES Module的解析流程

  • ES Module是如何被浏览器解析并且让模块之间可以相互引用的呢?
  • ES Module的解析过程可以划分为三个阶段:
  • 阶段一:构建(Construction),根据地址查找js文件,并且下载,将其解析成模块记录(Module Record)
  • 阶段二:实例化(Instantiation),对模块记录进行实例化,并且分配内存空间,解析模块的导入和导出语句,把模块指向对应的内存地址(模块环境记录Module environment record)
  • 阶段三:运行(Evaluation),运行代码,计算值,并且将值填充到内存地址中

阶段一:构建阶段

  1. 首先从服务器下载下来js文件,然后script的src进行fetch(获取),使用type="module"当作模块去解析
  2. 解析成Module Record,然后在这中途这个js文件解析出来的里面引入了counter.js和display.js文件,然后就又从去fetch(获取)下来,然后解析(parse)生成两个Mudule Record
  3. 然后还会继续检查这两个有没有继续引入,有的话就重复上面的操作,没有的话就结束


MODULE MAP:映射关系

阶段二和三:实例化阶段 – 求值阶段

  1. 查看模块Module Record有没有对这个模块进行导出东西,也就是LocalExport
  2. 有导出就会针对导出的东西生成Module Environment Record(模块环境记录),这里面就记录着我导出的东西,比如图中的count或者render
  3. 然后对count或者render赋值具体值5或者是函数
  4. 然后我们这时候再获取这值就有具体的信息了


(模块环境记录):不是由我们创建出来的,而是由浏览器(JS引擎)生成的,这也就是说我们下面这个不是对象


export{//这个括号不是对象,而是导出的特殊语法,是为了告诉JS引擎我们想要导出哪些内容
  xxx
}

包管理工具详解npm、yarn、cnpm、npx、pnpm

(了解)包管理工具-内容概述

  1. npm包管理工具
  2. package配置文件
  3. npm install原理
  4. yarn,cnpm,npx
  5. 发布自己的开发包
  6. pnpm使用和原理

(掌握)包管理工具-代码共享和npm基本操作

代码共享方案

  • 我们已经学习了在JavaScript中可以通过模块化的方式将代码划分成一个个小的结构:
  • 在以后的开发中我们就可以通过模块化的方式来封装自己的代码,并且封装成一个工具
  • 这个工具我们可以让同事通过导入的方式来使用,甚至你可以分享给世界各地的程序员来使用
  • 如果我们分享给世界上所有的程序员使用,有哪些方式呢?
  • 方式一:上传到GitHub上、其他程序员通过GitHub下载我们的代码手动的引用
  • 缺点是大家必须知道你的代码GitHub的地址,并且从GitHub上手动下载
  • 需要在自己的项目中手动的引用,并且管理相关的依赖
  • 不需要使用的时候,需要手动来删除相关的依赖
  • 当遇到版本升级或者切换时,需要重复上面的操作
  • 显然,上面的方式是有效的,但是这种传统的方式非常麻烦,并且容易出错;
  • 方式二:使用一个专业的工具来管理我们的代码
  • 我们通过工具将代码发布到特定的位置(npm (npmjs.com))
  • 其他程序员直接通过工具来安装、升级、删除我们的工具代码
  • 专业的工具就是npm,yarn,cnpm,npx,pnpm这类
  • 显然,通过第二种方式我们可以更好的管理自己的工具包,其他人也可以更好的使用我们的工具包

包管理工具npm

  • 包管理工具npm:
  • Node Package Manager,也就是Node包管理器
  • 但是目前已经不仅仅是Node包管理器了,在前端项目中我们也在使用它来管理依赖的包
  • 比如vue、vue-router、vuex、express、koa、react、react-dom、axios、babel、webpack等等
  • 如何下载和安装npm工具呢?
  • npm属于node的一个管理工具,所以我们需要先安装Node
  • node管理工具:https://nodejs.org/en/,安装Node的过程会自动安装npm工具
  • npm管理的包可以在哪里查看、搜索呢?
  • npm管理的包存放在哪里呢?
  • 我们发布自己的包其实是发布到registry上面的
  • 当我们安装一个包时其实是从registry上面下载的包

3.24小时整集内容

(掌握)项目的配置文件

npm的配置文件(包工具的使用)

  • 那么对于一个项目来说,我们如何使用npm来管理这么多包呢?
  • 事实上,我们每一个项目都会有一个对应的配置文件,无论是前端项目(Vue、React)还是后端项目(Node)
  • 这个配置文件会记录着你项目的名称、版本号、项目描述
  • 也会记录着你项目所依赖的其他库的信息依赖库的版本号
  • 这个配置文件就是package.json
  • 那么这个配置文件如何得到呢?
  • 方式一:手动从零创建项目,npm init –y
  • 方式二:通过脚手架创建项目,脚手架会帮助我们生成package.json,并且里面有相关的配置(都配置好了)

常见的配置文件

  • npm init #创建时填写信息
    npm init -y # 所有信息使用默认的(比较简单)
  • Vue CLI4创建的Vue3项目(Vue的脚手架)

  • create-react-app创建的react17项目(react的脚手架)

(掌握)项目配置文件-基础字段

常见的属性

  • 必须填写的属性:name、version

属性

意思

name

项目的名称

version

当前项目的版本号

description

描述信息,很多时候是作为项目的基本描述

author

作者相关信息(发布时用到)

license

开源协议(发布时用到)

  • private属性:
  • private属性记录当前的项目是否是私有的;
  • 当值为true时,npm是不能发布它的,这是防止私有项目或模块发布出去的方式
  • main属性:
  • 设置程序的入口
    比如我们使用axios模块 const axios = require('axios');
    如果有main属性,实际上是找到对应的main属性查找文件的(就不会默认找到index.js、json、node文件去了)
  • 就引入的时候直接输入main的名字,而不是完整路径了(更加简洁,也是基本的操作)

(掌握)项目配置文件-项目依赖

  • scripts属性
  • scripts属性用于配置一些脚本命令,以键值对的形式存在;
  • 配置后我们可以通过 npm run 命令的key来执行这个命令
  • npm start和npm run start的区别是什么?
    它们是等价的
    对于常用的 start、 test、stop、restart可以省略掉run直接通过 npm start等方式运行(你自己定义的就不能省略run)
"scripts": {
    "start":"运行文件的路径"
  }
//运行快捷键 npm run start
  • dependencies属性
  • dependencies属性是指定无论开发环境还是生产环境都需要依赖的包
  • 通常是我们项目实际开发用到的一些库模块vue、vuex、vue-router、react、react-dom、axios等等
  • 打包npm run build还有vue代码是因为我们还需要依赖vue来操作DOM
  • 与之对应的是devDependencies
  • devDependencies属性
  • 一些包在生产环境是不需要的,比如webpack、babel等(打包完就不需要了)
  • 这个时候我们会通过 npm install webpack --save-dev,将它安装到devDependencies属性中
  • devDependencies = development Dependencies(开发依赖)

//开发环境依赖
npm install xxx --save-dev//全写
npm install xxx --D//简写


  • peerDependencies属性
  • 还有一种项目依赖关系是对等依赖,也就是你依赖的一个包,它必须是以另外一个宿主包为前提
  • 比如element-plus是依赖于vue3的,ant design是依赖于react、react-dom
//在element-plus中就能看到
"perrDependencies":{
    "vue":"版本"
}//这表示必须有Vue才能够使用


ES Module使用-原理-包管理工具npm(二)https://developer.aliyun.com/article/1470452

目录
相关文章
|
2月前
|
JavaScript 前端开发 Java
npm学习一:npm 包管理工具 学习、使用。
这篇文章介绍了npm的基础知识和常用命令,包括安装包、查看包信息、管理依赖等操作,并提供了如何删除npm安装的镜像以及如何彻底删除node_modules文件夹的具体步骤。
106 2
|
2月前
|
JavaScript 前端开发 Java
npm学习一:npm 包管理工具 学习、使用。
这篇文章是关于npm包管理工具的学习、使用指南,包括npm概述、基础命令和如何安装webpack。
73 3
npm学习一:npm 包管理工具 学习、使用。
|
2月前
|
缓存 JavaScript 前端开发
拿下奇怪的前端报错(三):npm install卡住了一个钟- 从原理搞定安装的全链路问题
本文详细分析了 `npm install` 过程中可能出现的卡顿问题及解决方法,包括网络问题、Node.js 版本不兼容、缓存问题、权限问题、包冲突、过时的 npm 版本、系统资源不足和脚本问题等,并提供了相应的解决策略。同时,还介绍了开启全部日志、使用替代工具和使用 Docker 提供 Node 环境等其他处理方法。
1174 0
|
2月前
|
缓存 资源调度 JavaScript
npx与npm的差异解析,以及包管理器yarn与Node版本管理工具nvm的使用方法详解
npx与npm的差异解析,以及包管理器yarn与Node版本管理工具nvm的使用方法详解
65 0
|
2月前
|
资源调度 前端开发 安全
前端实战:基于Verdaccio搭建私有npm仓库,轻松上传与下载自定义npm插件包
前端实战:基于Verdaccio搭建私有npm仓库,轻松上传与下载自定义npm插件包
130 0
|
3月前
|
数据安全/隐私保护
发布一个npm包
发布一个npm包
123 2
|
5月前
|
运维 Kubernetes Java
阿里云云效操作报错合集之npm包已经发布到了制品仓库,但流水线中拉取依赖时出现404错误,该如何排查
本合集将整理呈现用户在使用过程中遇到的报错及其对应的解决办法,包括但不限于账户权限设置错误、项目配置不正确、代码提交冲突、构建任务执行失败、测试环境异常、需求流转阻塞等问题。阿里云云效是一站式企业级研发协同和DevOps平台,为企业提供从需求规划、开发、测试、发布到运维、运营的全流程端到端服务和工具支撑,致力于提升企业的研发效能和创新能力。
阿里云云效操作报错合集之npm包已经发布到了制品仓库,但流水线中拉取依赖时出现404错误,该如何排查
|
4月前
NPM——删除已发布的包
NPM——删除已发布的包
155 1
|
4月前
|
JavaScript 前端开发 开发者
从零到一:教你如何发布自己的npm插件包
从零到一:教你如何发布自己的npm插件包
|
4月前
|
JavaScript
GitHub——自动发布NPM包
GitHub——自动发布NPM包
38 0