框架前置课
1-node.js基础,node最重要是服务器开发,具体后面学,现在只学基础
2-npm/yarn/cnpm/npx/pnpm
3-模块化的使用
4-webpack,vue和reactCLI都会用到
5-GIT包管理工具
6-express
7-mysql基础知识
Node.js是什么
1-node是一个基于V8引擎(处理js的)的JavaScript运行时环境
2-node.js与浏览器相比,都是基于V8引擎执行处理js,但是各自还有自己的其他应用,比如chorm浏览器肯定还有书签,收藏夹什么的,node.js也有中间层libuv去处理文件读写,网络io,事件循环等。
3-其中libuv(异步事件库)是由c语言编写的,所以node.js的一部分也是由c语言编写。
Node的应用场景
1-包的管理,以前我们引入一个框架库,vue或者react或者lodash等,都是需要去官网下载到本地,然后用script标签引入,但是有了包管理工具如npm,我们就可以直接安装,如npm install day.js
哪个页面需要使用,就import day.js from“”day.js”即可
tip:我们可以自己打包好node包上传到npm上,然后就可以通过npm在线下载了
2-用node.js开发服务器,中间件,代理服务器的开发。
3- 借助node.js完成前后端渲染的同构应用,如ssr服务器渲染。
4-用node运行为项目编写的JavaScript的脚本,而不是python和shell
5-使用electron开发桌面应用程序,也需要node环境才能运行。
Node的安装和管理
1-我们安装的时候最好用LTS,long time support版本,比较稳定。如果是个人学习可以选current最新版本。
2-我们也可以下载多个版本,通过工具管理,实现不同node版本间的切换。
tip:我们下载后,后缀是msi全称就算Microsoft Install
这里有一个add to path 千万不要取消了,这个就是自动为我们node添加环境变量,也就是在cmd命令行中可以直接使用,如果取消了就需要自己像添加java环境那些一样去设置里面七七八八的手动添加环境变量,麻烦的要死,一个字母错了都不行。
最后在cmd命令行里面输入 node -v 和npm -v如果出来了版本号,就代表安装完毕了。
如果我们这里勾选了,就会出现如下页面
这个页面,它是在为我们安装使用一些包可能需要用到的环境,比如需要c++编译,但是事实上这个时候我们node和npm已经安装好了,这里可以直接关闭,以后需要这些环境的时候再安装,也可以等待它全部下载安装完毕(不魔法上网大概率会安装失败)
node的版本管理工具
实现node版本的切换,可以在电脑上安装多个版本node,比如工作就用LTS版本,在家学习用Current版本。
其中n和nvm都是只支持mac,所以github有人发布了nvm-windows
https://github.com/coreybutler/nvm-windows
nvm install latest 是安装当前时间最新的node版本
nvm list是显示出当前电脑,安装了的node版本
nvm use xx.xx.xx 就是切换版本xx是版本号,注意这里需要给cmd管理员权限运行
注意!!!
必须在nvm的配置文件也就是setting.txt中加入下面两行修改镜像源,不然你下一辈子都下不完,科学上网也不行。
tip:当你下载一些东西没反应,第一时间考虑要不要科学上网,如果还不行就换镜像源。
镜像源就是那些大厂什么的帮我们复制了国外的资源,然后我们在国内访问这个镜像源下载就行了。
node_mirror: https://npm.taobao.org/mirrors/node/
npm_mirror: https://npm.taobao.org/mirrors/npm/
JavaScript代码在node中的 执行
命令行运行
1-以前我们的js文件,必须建一个html文件,然后用script引入,现在我们只需要把js写好,然后在命令行cd到当前demo.js文件的目录,然后node demo.js即可
2-移动盘符的命令,什么cd.. tab补全 dir cls 百度学一下就行
3-也可以在vscode的终端打开cmd和powershell都可以(vscode是用electron写的)
Node的输入和输出
输出
很简单直接console.log()打印输出
console.clear()清空控制台
console.trace()输出函数的调用栈,也就是执行路径
输入
输入的话,原先用浏览器执行js,可以用prompt(),现在用node在终端执行怎么办呢?
在执行文件的命令后空格隔开,输入变量赋值即可。
//文件名字为demo.js
// 1.输出的内容
console.log("Hello World")
const num1 = 100
const num2 = 200
console.log(num1 + num2)
// 2.给程序输入内容
// node ./webpack.config.js env=development
const arg1 = process.argv[2]
const arg2 = process.argv[3]
console.log(process.argv)
console.log(arg1, arg2)
setTimeout(() => {
// console.clear()
console.trace()
}, 3000);
命令
node demo.js num1=20 num2=30
在node中js是有一个process.argv的数组,里面保存了一些数据
为什么叫argv?
node的repl
read-eval-print-loop读取,求值,输出,循环
我们在控制台输入node就可以进入,输入.exit或者按两次ctrl+c就可以退出
它和我们在浏览器的console控制台差不多,可以写简单的js代码运行,实际中作用不大
Node的全局对象
1-众所周知在浏览器中有全局对象window,但是在node中是没有window的,但是有global
2-一个是浏览器视口可以看到的所有东西,node根本没有视口何来window
3-在浏览器执行js中我们定义一个var变量会被添加成为window的属性,但是在node中不会被添加为global的属性
3-为了避免把浏览器和node的全局对象记混,统一了一个名字globalThis,在浏览器中指window,在node中指global,我个人认为多此一举。
process进程全局对象
console.log(process)这个对象东西非常多比如原先说的argv[],可以自己去nodejs.org或者api.nodejs.cn去查询文档
tips:org后缀域名代表非盈利性或政府组织。
定时器也是全局对象
interval间隔,immediate立即立刻的也就是说它会立即执行这个回调函数。
process.nextTick()和setImmediate()谁先执行?在事件循环中会讲清楚。
特殊的全局对象
1-它实际上是模块中的变量,每个模块都有,像全局变量
2-在命令行交互中不可以使用。
假设当前文件在 e:\学习\demo.js
__dirname可以拿到当前代码所在的目录结构 //e:\学习\
__filename当前代码文件目录结构 //e:\学习\demo.js
module模块,exports出口传播,require需求,也是特殊的全局对象,后面会学
// 特殊的全局对象
// __dirname当前的文件所在的目录结构(重要) 注意这是两个下划线
console.log(__dirname)
// __filename当前目录+文件名称
console.log(__filename)
前端模块化
什么是模块化开发
1-比如一个项目,不同的人为一个页面书写js,如果不使用模块化开发,那么”东方“写了a.js并定义了一些变量,”亚洲“就无法在b.js中再定义那些变量的名字。如果是一些常用的变量名如name,age,两个人就要去看另一个人的代码,避免变量冲突,这样就太麻烦了。
2-怎么办?我们可以用立即执行函数,因为有函数作用域的限制,所以不会冲突,但问题又来了,当我们需要用到另一个js文件的变量怎么办?因为作用域的限制,想要的时候又拿不到了
3-我们可以在立即执行函数里面书写函数返回值,把立即函数内的所有变量通过对象返回,那么别人在需要使用的时候就可以通过返回值引入了。
const moduleA = (function() {
let name = "why"
let age = 18
let height = 1.88
console.log(name)
return {
name,
age,
height
}
}())
4-其实这样就避免了作用域的冲突,也可以导入导出不同文件的变量和函数等,但是每个人有每个人的习惯,你写的a.js返回的是对象,也许其他人不知道这个方法就不会调用a.js中的所需变量。必须有一个统一的书写方法。
5-所以社区就诞生了很多模块化的规范,其中有CommonJS和AMD和CMD,官方后来推出了ESMoudle使用的最多。
没有模块化的问题
CommonJS(社区模块化方案)和Node
社区指定的约定规范,还有很多人使用。在webpack中也有它的影子
utile.js
const UTIL_NAME = "util_name"
function formatCount() {
return "200万"
}
function formatDate() {
return "2022-10-10"
}
//exports是一个对象,如果你没有导入任何东西就是空的
console.log(exports) // {}
//导出
//这个变量随便命名的,命名为一样使用方便点
exports.UTIL_NAME = UTIL_NAME
exports.formatCount = formatCount
exports.formatDate = formatDate
导入
// 1.直接获取导出的对象, 从对象中获取属性
//这个util是一个对象,你随便命名的,这是命名为文件名字有利于区分
// const util = require("./util.js")
// console.log(util.UTIL_NAME)
// console.log(util.formatCount())
// console.log(util.formatDate())
// 2.导入对象之后, 直接对其进行解构
// const {
// UTIL_NAME,
// formatCount,
// formatDate
// } = require("./util.js")
// console.log(UTIL_NAME)
// console.log(formatCount())
// console.log(formatDate())
// 3.探讨require的本质
const bar = require("./bar.js")
console.log(bar.name) // bar
console.log(bar.nb) // bar
console.log(bar) // bar
// 4s之后重新获取name
// setTimeout(() => {
// console.log(bar.name)
// }, 4000)
// 2s之后通过bar修改了name
setTimeout(() => {
bar.name = "kobe"
}, 2000)
tip:const bar = require(“./bar.js”)是把bar.js里面的exports对象的地址给了bar
所以说在使用中修改一些exports里面的属性变量值,两边都会被改
module.exports导出
const name = "foo"
const age = 18
function sayHello() {
console.log("sayHello")
}
// 1.在开发中使用的很少
// exports.name = name
// exports.age = age
// exports.sayHello = sayHello
// 2.将模块中内容导出
// 结论: Node导出的本质是在导出module.exports对象
module.exports.name = name
module.exports.age = age
module.exports.sayHello = sayHello
如上代码是module.exports导出方法,你这不是犯病吗?效果一样的,偏要多加个单词module
上面代码中module.exports.age和exports.age其实是一个东西
exports===module.exports
const name = "foo"
const age = 18
function sayHello() {
console.log("sayHello")
}
// 1.在开发中使用的很少
// exports.name = name
// exports.age = age
// exports.sayHello = sayHello
// 2.将模块中内容导出
// 结论: Node导出的本质是在导出module.exports对象
// module.exports.name = name
// module.exports.age = age
// module.exports.sayHello = sayHello
// // console.log(exports.name, "----")
// // console.log(exports.age, "----")
// // console.log(exports.sayHello, "----")
// console.log(exports === module.exports)
// 3.开发中常见的写法
module.exports = {
name,
age,
sayHello
}
// exports.name = "哈哈哈哈"
// module.exports.name = "哈哈哈哈"
导入者按理来说是不应该能修改导出者也就是原始版本中的变量的,在ESmodule中就不支持。
所以开发中commonJS一般是给module.exports一个对象值,那么就开辟了一个新地址新内存,另一个js文件通过require方法取到exports对象中的属性变量后,即使修改也不会影响被导出变量的js中的变量值。
问题:exports===module.exports,那我还是没有理由写module呀,开辟新对象,我给exports对象赋值不是一样的?
测试中我发现,你直接给export赋值一个类,开辟新空间,另一个js文件中引不到东西。
require方法的解析
如上图,在index.js导出了一个变量对象,在mian.js中引入的时候,并没有写导入的变量来自具体哪一个文件如utils/index.js,而只写了utils还是可以使用,这是为什么?这就需要知道require的查找机制
require的查找寻找机制
path和http是内置的模块
在实际开发中,比如我们require一个axios
1-axios不是核心库
2-搜索node_modules文件夹(这个文件夹npm install axios等会自动生成),当前路径找不到,就自动搜寻再上一级目录,如果都没有返回not found。
require引入模块的运行过程
foo.js
console.log("foo-----")
console.log("foo+++++")
1-当模块被引入,模块代码会被自动执行一次,按照当前main.js代码顺序执行,遇到模块引入就跳到被引入模块的js文件中执行,然后继续后面代码
2-模块引入执行一次后,再重复引入,不会再执行模块中的代码。
也就是说main引入了aaa和bbb,aaa引入了ccc,ccc引入了ddd,ddd引入了eee
main会先引入完了bbb这条线,再去引入bbb,bbb虽然引入了ccc,但是ccc已经被引入一次了,就不会再被执行。
commonjs的引入是同步的,没有异步。
1-一般commonjs是在服务器中,引入的文件也都在服务器存储了,如上引入aaa,bbb,ccc等等
因为代码在本地都有,所以速度是很快的,即使aaa中调用了查询数据库等需要耗时,但是查询数据库本身是异步的,所以会继续执行下去,总而言之引入的速度会很快。
2-如果把它应用到浏览器中,因为commonjs是同步的,aaa,bbb等文件都需要从服务器去下载下来,一个没下载好,下面的其他引入都无法执行。
AMD和CMD(了解)
现在开发已经不用了