coderwhy老师讲的细,内容多,更有利于我们的学习职业发展,而不是单纯整一个流水线开发程序员出来。
commonjs只在node中可以实现,在浏览器中是不行的,比如让同一个页面的不同区域模块化。
浏览器如何支持esmodule?
方法一就是这个浏览器支持esmodule
方法二就是使用webpack打包工具,使用了webpack后我们甚至可以commomjs和esmodule混用,因为最后webpack会把这些我们写的代码解析整合为普通的js代码(就是没有使用模块化),这样不管浏览器支持与否,都能使用了。
认识ESModule
注意esmodule中的导出export是没有s的,commomjs中的导出module.exports有s
esmodule会自动采用代码严格模式use strict
方法一ESModule的基本使用
foo.js
const name = "why"
const age = 18
function sayHello() {
console.log("sayHello")
}
// 导出 export
export {
name,
age,
sayHello
}
在commomjs中是module.exports={}也就是等于一个对象来导出
但是esmodule中这里并不是一个对象,而是特殊的语法,你把导出的变量标识符写到export{}里面就好了。
main.js
// 导入 import
// 注意事项一: 在浏览器中直接使用esmodule时, 必须在文件后加上后缀名.js
//不像commonjs不加后缀有查找机制,但是后续用webpack了也不需要加后缀名会补全
import { name, age, sayHello } from "./foo.js"
// const name = "main"
console.log(name)
console.log(age)
sayHello()
index.html
这里为什么有html?因为esmodule是执行在浏览器中的,原先的commomjs是执行在node中的
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<!-- 注意事项二: 在我们打开对应的html时, 如果html中有使用模块化的代码, 那么必须开启一个服务来打开 -->
<script src="./foo.js" type="module"></script>
<script src="./main.js" type="module"></script>
</body>
</html>
这里引入js的时候必须写type=“module”只有这样浏览器才知道它是一个模块化的js,有自己独立的作用域,否则会变量冲突(如foo.js和main.js定义同一个名字变量)。
注意
打开index.html必须开启一个服务,也就是open with live server(vscode里面的live sever插件),不能用open in default browser
因为模块化的js导入的时候,存在一个cors跨域问题,不支持本地的file请求,必须开启一个服务用http等协议。
“Cors的英文全称是Cross-origin Resource Sharing,意思是跨域资源共享
其他导出方式
// 3.导出方式三:
export const name = "why"
export const age = 18
export function sayHello() {
console.log("sayHello")
}
export class Person {}
// console.log(name)
// 1.导出方式一:
// export {
// name,
// age,
// sayHello
// }
// 2.导出方式二: 导出时给标识符起一个别名
// export {
// name as fname,
// age,
// sayHello
// }
当mainjs从foojs中导入了一个name变量,mainjs也需要自己定义一个name变量怎么办呢?就可以用到导出方式二,把foojs中的name起别名为fnam
我们也可以导出方式三,在变量,函数等在定义的时候就export导出,但是这样就不能起别名。但是可以在导入的时候起别名
import { name as fname, age, sayHello } from "./foo.js"
问题
import { name, age, sayHello } from "./foo.js"
每次导入这样写一大堆变量,万一需要导入的很多,不难写吗?
所以我们可以直接写导入*代表全部的意思,然后给它一个别名,就可以用别名来使用
// 3.导入时可以给整个模块起别名
import * as foo from "./foo.js"
const name = "main"
console.log(name)
console.log(foo.name)
console.log(foo.age)
foo.sayHello()
export和import的结合使用
开发中用的不多,但是一些优秀项目或者框架的源码经常这样写
作用:不需要在main.js中导入多个js了,只需要导入一个indexjs
mainjs
// import { formatCount, formatDate } from "./utils/format.js"
// import { parseLyric } from "./utils/parse.js"
import {
formatCount,
formatDate,
parseLyric
} from './utils/index.js'
console.log(formatCount())
console.log(formatDate())
console.log(parseLyric())
indexjs
import { formatCount, formatDate } from './format.js'
import { parseLyric } from './parse.js'
export {
formatCount,
formatDate,
parseLyric
}
// 优化一:
// export { formatCount, formatDate } from './format.js'
// export { parseLyric } from './parse.js'
// 优化二:
// export * from './format.js'
// export * from './parse.js'
default用法
一个模块(也就是一个js文件)只能一个默认导出,导入的时候不需要写导入的变量或函数的名字
import parseLyric from "./parse_lyric.js"
console.log(parseLyric())
这里的parseLyric名字乱下都可以,因为是默认导出,比如写为import demo from “./parse_lyric.js”那么使用的时候就是demo()
其实我感觉是多此一举,又要理解一个东西,只要了解一下有人才这样写我们也知道是什么东西
//导出
// 2.定义标识符直接作为默认导出
export default function() {
return ["新歌词"]
}
import的一些注意
tips:路径是不允许去+拼接的
1-导入语句只能写在代码顶层,不能放在任意的代码语句内,如if条件成立再import导入
因为js引擎解析执行的时候设定的就是先扫描有没有import,如果有就导入后再执行后续代码,你在后续代码里面写个import会直接报错的
如果我就是要语句成立再导入呢?不然我没用到也导入不是浪费性能?
那我们可以使用import函数,以前的写法是import声明。
import meta
不怎么用,也就是说哪个js中import了其他模块,这个js中就有一个import.meta属性,可以打印出来,里面记录的引入的模块变量来自哪个url地址路径
ESModule的解析过程
http://hacks.mozilla.org/2018/03/es-modules-a-cartoon-deep-dive/
了解即可,coderwhy的2022系统课面向25k,内容确实细。
npm的介绍和基本使用
比如尤雨溪开发了vue框架
1-如果我们开发者要使用,可以通过vue官网或者github尤雨溪的主页去下载vue.js等文件,然后放到我们项目的lib中,然后再script引入,这样太麻烦了,如果我们要更换版本还得重新下载引入。
2-尤雨溪把vue.js放在了npm registry ,那么我们只需要通过npm就可以快速下载引入。
npm包管理工具
官网npmjs.org
tips:“.org”域名是互联网的通用顶级域之一,英文全称“organization”,意思为“组织”,适用于各类型组织机构,包括非盈利团体,是组织机构的首选。
npm就是node package manager,刚开始是主要管理node包的,现在在前端项目中也都可以使用。
当我们在vscode控制台,进入指定目录,输入命令如node install vue ,那么对应目录就会自动生成一个node_modules文件夹,vue的源码就会被下载到这里。
同时在当前目录还会生成package.json和package-lock.json
package配置文件
json的几种格式https://blog.csdn.net/weixin_48185778/article/details/109822965
一个完整的项目,通常会有项目名称,版本号,以及依赖那些库等等信息保存在package.json
如何生成的?
1-手动创建,缺点:写的太麻烦
2-在控制台输入npm init 命令自动生成只需要我们自己填写一些信息。如果所以信息默认可以输入命令
npm init-y
3-通过脚手架CLI快速创建项目,会自动生成package.json
常见的属性
在很多项目中,我们通过require(day)引入后,其中day的目录中是没有index.js的,所以commomjs的自动寻找机制就找不到,main属性就可以设置这个入口,让require去找mian属性值的文件。
scripts
当我们要经常运行一段命令行,可以直接把它写入scripts字段内,然后通过npm run的方式运行。
dependencies字段
1-当我们的同事需要在我们项目的基础上写代码什么的,我们传给同事项目一般会把node_modules删掉再压缩发给他们,因为node_modules里面通常有很多包入axios,vue等等等非常大。
2-那么同事拿到我们的代码,要想跑项目,必须再重新下载以上依赖包,dependencies就能告诉他以及node,这个项目需要哪些包,他可以自己一个一个下载,也可以在node终端执行node install命令会自动下载当前项目所需所有包。
devDependencies字段
1-也就是说在开发的时候需要用到,生产product的时候不需要用到的包,放到devDependencies字段中,如webpack就是一个在开发的时候打包的,后续在浏览器等生成页面的时候不需要用到它。
2-安装webpack的时候我们要写npm install webpack –sava -dev命令或者这段命令的缩写npm install webpack -D,就可以自动添加devDependencies字段中
peerDependencies
当一个库必须依赖另一个库才能使用跑起来,就需要把另一个库写入到peerDependencies字段
如上图的element-plus这个库,它是基于vue3的,你想使用element-plus,必须当前环境中也有vue3
版本号
如vue3无法再写vue2的一些语法,不兼容了就是主版本号
只是新加了一个功能,就是次版本号
修复了一些bug,优化一下,就是z修订号。
了解
npm install原理
1-npm和node和java等等在cmd命令行的命令,都需要配置环境变量,当我们输入了命令,会通过环境变量去找到对应的程序执行,也就是说你也可以配置一个qq的环境变量,然后在cmd中输入qq自动启动。
1-那些我们所有项目都要用到的工具包,比如webpack就全局安装,那些只在当前项目使用的包如day.js就局部安装即可(安装到当前安装路径下)
2-并不是你全局安装了包,比如全局安装了dayjs和axios,就能每个项目都能使用了,这种包必须局部安装到项目文件夹内。
1-上面说过了开发依赖就是我们程序员写程序的时候需要用到的包,浏览器等平台渲染的时候用不到,如webpack
2-生产开发依赖就是,开发的时候和浏览器渲染的时候都要用到这个包,比如vue
缓存共用开发包
1、使用cnpm install时候,并不会生成 package-lock.json 文件,也不会根据 package-lock.json 来安装依赖包,还是会使用 package.json 来安装。
2、package-lock.json 是在 npm(^5.x.x.x)后才有package-lock.json 它会在 npm 更改 node_modules 目录树 或者 package.json 时自动生成的 ,它准确的描述了当前项目npm包的依赖树,并且在随后的安装中会根据 package-lock.json 来安装,保证是相同的一个依赖树,不考虑这个过程中是否有某个依赖有小版本的更新。官方是这样解释的
它的产生就是来对整个依赖树进行版本固定(锁死)。
当我们在一个项目中npm install时候,会自动生成一个package-lock.json文件,和package.json在同一级目录下。
package-lock.json记录了项目的一些信息和所依赖的模块。这样在每次安装都会出现相同的结果.。不管你在什么机器上面或什么时候安装。
当我们下次再npm install时候,npm 发现如果项目中有 package-lock.json 文件,会根据 package-lock.json 里的内容来处理和安装依赖而不再根据 package.json。
也就是说package.json里面的依赖,npm install通过这个去下载,只会保证大版本一样,后面的小版本是可能改变的(自动下载最新),如果是package-lock.json就指定什么版本就是什么版本,不会改变。
如果多个项目,都用axios而且版本一样,那么npm就不会去请求仓库地址,而是先看本地有没有这个包,如果有就直接安装,没有再去请求下载。
resolved字段代表了当前包从哪个url下载下来的
requires字段和dependencies是一样的作用,指当前包依赖哪些包才能正常运行使用
integrity是当前包的一个标识,缓存包就是通过它来识别的。
1-也就是说如果这个项目有lock,比如下载那么执行npm install就会通过lock去找需要的包,然后再判断一下现在这个包的所需依赖和以前是不是一样,如axios以前只依赖redirect,现在又加了form-data,一样的话就去缓存查找有没有这个axios的缓存,然后安装,没有缓存的话就要去仓库下载。
2-如果这个项目我们拿到没有lock的话,就只能去仓库下载,然后会自动构建一个lock包,供后续人的使用。
npm的其他命令
yarn
以前npm不好用,各大厂如谷歌脸书就一起推出了一个js包管理工具yarn,后续npm5改进了很多小问题,它们两个的用户就分庭抗礼了。
想安装yarn,用npm下载即可,因为工具包一般每个项目都要使用,所有得-g全局
npm install yarn -g
yarn的命令与作用和npm基本上一样的,就是把npm改成yarn
cnpm
cnpm其实就是npm的中国版,完全一样的东西,就是命令把npm改成cnpm
作用:
npm里面可能有的包服务器在国外,你不科学魔法上网上不去,这个时候有三个解决办法
1-科学上网
2-改npm的下载镜像,但是镜像中的包比较不是官方的,我只想在包下载不了的时候用镜像,就得改来改去
3-安装cnpm,把cnmp的镜像改成国内的镜像站地址如taobao的,平时用npm,如果有包下载不了再用cnpm