1. 代码的执行流程
代码的执行流程分为同步与异步。
2. 什么样子的是宏任务?
1. setTimeout 和 setInterval 定时器: 没有写时间(传参的),代表下一帧执行,如果没有其他任务1ms后执行。
// 没有写时间(传参的),代表下一帧执行,如果没有其他任务1ms执行。
setTimeout(() => {
})
-------------------------------
setInterval(()=>{
})
2. setImmediate的使用和setTimeout类似的,但是在间隔一段时间后执行,闲时执行。闲时执行:是指cpu不繁忙的时候。
setImmediate(()=>{
})
注意!!!
如果没有大量运行代码时,setImmediate 比 setTimeout 和 setInterval 先执行。
如果运行当中cpu被占用时,setTimeout 和 setInterval 比 setImmediate 先执行。
3. 什么样子的是微任务?
nextTick 等同于Promise.resolve.then(()=>{})。是异步执行。
// 等同于Promise.resolve.then(()=>{})
process.nextTick(()=>{
})
3.1 案例1分析:
console.log("a");//同步
new Promise(function (resolve, reject) {
resolve();
console.log("b");//同步
}).then(function () {//异步
console.log("c");
})
setTimeout(() => {//异步
console.log("d");
})
console.log("e");//同步
为什么执行流程是这样的呢?
因为遇到宏任务将开辟一个新的任务栈,将这个宏任务放在新任务栈的最顶端。遇到微任务,将放在当前任务栈的最下面。
所以执行结果是:a ----> b ----> e ----> c ----> d
3.2 案例2分析:如果遇到微任务与宏任务遇到一起了是怎么执行的?
// 如果遇到微任务与宏任务遇到一起了是怎么执行的?
new Promise(function (resolve, reject) {
resolve();
}).then(function () {//异步微任务
setTimeout(() => {//异步宏任务
console.log("a");
})
})
setTimeout(() => {//异步宏任务
new Promise(function (resolve, reject) {
resolve();
}).then(function () {//异步微任务
console.log("b");
})
})
所以执行结果是: b ----> a
3.3 案例3分析:
async function fn1() {
console.log("a");
await Promise.resolve();//异步微任务
console.log("b");
}
function fn2() {//同步
console.log("c");
}
function init() {//同步
fn1();
fn2();
}
init();
所以执行结果是: a ----> c ----> b
3.4 案例4分析:比较几种写法任务的执行速度
写法一:微任务密集性高,速度快。
new Promise(function (resolve, reject) {
resolve();
}).then(function () {//异步微任务
console.log("a1");
}).then(function () {//异步微任务
console.log("b1");
}).then(function () {//异步微任务
console.log("c1");
}).then(function () {//异步微任务
console.log("d1");
}).then(function () {//异步微任务
console.log("e1");
}).then(function () {//异步微任务
console.log("f1");
}).then(function () {//异步微任务
console.log("g1");
}).then(function () {//异步微任务
console.log("h1");
})
写法二:微任务密集性中等,速度中等。
// 中
new Promise(function (resolve, reject) {
resolve();
}).then(function () {//异步微任务
console.log("a2");
return new Promise(function (resolve, reject) {
resolve();
})
}).then(function () {//异步微任务
console.log("b2");
return new Promise(function (resolve, reject) {
resolve();
})
}).then(function () {//异步微任务
console.log("c2");
return new Promise(function (resolve, reject) {
resolve();
})
}).then(function () {//异步微任务
console.log("d2");
return new Promise(function (resolve, reject) {
resolve();
})
}).then(function () {//异步微任务
console.log("e2");
return new Promise(function (resolve, reject) {
resolve();
})
}).then(function () {//异步微任务
console.log("f2");
return new Promise(function (resolve, reject) {
resolve();
})
}).then(function () {//异步微任务
console.log("g2");
return new Promise(function (resolve, reject) {
resolve();
})
}).then(function () {//异步微任务
console.log("h2");
return new Promise(function (resolve, reject) {
resolve();
})
})
写法三:微任务密集性低,速度低。
new Promise(function (resolve, reject) {
resolve(new Promise(function (resolve1, reject1) {
resolve1();
}))
}).then(function () {
console.log("a3");
})
写法四:微任务密集性低,速度低。等同于写法三。
Promise.resolve().then(function () {
console.log("a4");
Promise.resolve().then(function () {
console.log("b4");
Promise.resolve().then(function () {
console.log("c4");
Promise.resolve().then(function () {
console.log("d4");
Promise.resolve().then(function () {
console.log("e4");
Promise.resolve().then(function () {
console.log("f4");
Promise.resolve().then(function () {
console.log("g4");
Promise.resolve().then(function () {
console.log("h4");
});
});
});
});
});
});
});
});
1. 写法一与写法二作比较:
// 写法一:密集性 快
new Promise(function (resolve, reject) {
resolve();
}).then(function () {//异步微任务
console.log("a1");
}).then(function () {//异步微任务
console.log("b1");
}).then(function () {//异步微任务
console.log("c1");
}).then(function () {//异步微任务
console.log("d1");
}).then(function () {//异步微任务
console.log("e1");
}).then(function () {//异步微任务
console.log("f1");
}).then(function () {//异步微任务
console.log("g1");
}).then(function () {//异步微任务
console.log("h1");
})
// 写法二:中
new Promise(function (resolve, reject) {
resolve();
}).then(function () {//异步微任务
console.log("a2");
return new Promise(function (resolve, reject) {
resolve();
})
}).then(function () {//异步微任务
console.log("b2");
return new Promise(function (resolve, reject) {
resolve();
})
}).then(function () {//异步微任务
console.log("c2");
return new Promise(function (resolve, reject) {
resolve();
})
}).then(function () {//异步微任务
console.log("d2");
return new Promise(function (resolve, reject) {
resolve();
})
}).then(function () {//异步微任务
console.log("e2");
return new Promise(function (resolve, reject) {
resolve();
})
}).then(function () {//异步微任务
console.log("f2");
return new Promise(function (resolve, reject) {
resolve();
})
}).then(function () {//异步微任务
console.log("g2");
return new Promise(function (resolve, reject) {
resolve();
})
}).then(function () {//异步微任务
console.log("h2");
return new Promise(function (resolve, reject) {
resolve();
})
})
写法一与写法二作比较:
除了第一次的执行,后面的代码执行,写法一都会比写法二多执行两次。
原因:因为写法二需要接收到上一个then执行的结果才能继续向后执行。
2. 写法一与写法三作比较:
//写法一:快
new Promise(function (resolve, reject) {
resolve();
}).then(function () {//异步微任务
console.log("a1");
}).then(function () {//异步微任务
console.log("b1");
}).then(function () {//异步微任务
console.log("c1");
}).then(function () {//异步微任务
console.log("d1");
}).then(function () {//异步微任务
console.log("e1");
}).then(function () {//异步微任务
console.log("f1");
}).then(function () {//异步微任务
console.log("g1");
}).then(function () {//异步微任务
console.log("h1");
})
//写法三: 慢
new Promise(function (resolve, reject) {
resolve(new Promise(function (resolve1, reject1) {
resolve1();
}))
}).then(function () {
console.log("a3");
})
写法一与写法三作比较:写法一执行速度更快。
写法二与写法三作比较:
// 写法二: 中
new Promise(function (resolve, reject) {
resolve();
}).then(function () {//异步微任务
console.log("a2");
return new Promise(function (resolve, reject) {
resolve();
})
}).then(function () {//异步微任务
console.log("b2");
return new Promise(function (resolve, reject) {
resolve();
})
}).then(function () {//异步微任务
console.log("c2");
return new Promise(function (resolve, reject) {
resolve();
})
}).then(function () {//异步微任务
console.log("d2");
return new Promise(function (resolve, reject) {
resolve();
})
}).then(function () {//异步微任务
console.log("e2");
return new Promise(function (resolve, reject) {
resolve();
})
}).then(function () {//异步微任务
console.log("f2");
return new Promise(function (resolve, reject) {
resolve();
})
}).then(function () {//异步微任务
console.log("g2");
return new Promise(function (resolve, reject) {
resolve();
})
}).then(function () {//异步微任务
console.log("h2");
return new Promise(function (resolve, reject) {
resolve();
})
})
//写法三: 慢
new Promise(function (resolve, reject) {
resolve(new Promise(function (resolve1, reject1) {
resolve1();
}))
}).then(function () {
console.log("a3");
})
写法二与写法三作比较:写法二执行速度快一点。
4. node.js
node的版本:
- 1 是大版本 :代码产生大的变化。
- 2 是小版本 :代码迭代增加新的内容。
- 3 是微版本 :修复了代码中bug。
偶数版本为稳定版 (0.6.x ,0.8.x ,0.10.x)
奇数版本为非稳定版(0.7.x ,0.9.x ,0.11.x)
node.js的问题:
- 以前版本安装的很多全局的工具包需要重新安装。
- 无法回滚到之前的版本。
- 无法在多个版本之间切换(很多时候我们要使用特定版本)。
4.1 node.js的命令
1. node 文件名.js : 执行该文件。
2. node 文件名.js 参数1 参数2: 执行该文件并传参。
3. process.argv 属性的内容如下:
[
'/Users/ericxie/.nvm/versions/node/v18.18.0/bin/node', ---> node程序路径
'/Users/ericxie/xja/鸿蒙13/ex/0903/server.js', ---> 执行的文件路径
'1', --->传入的参数1
'2' --->传入参数2
]
4. commonjs模块化 :是NodejsES5版本的内容。
5. ES6模块化 :ES6版本。
4.2 commonjs模块化的语法
导出默认模块的语法:
默认模块关键字:module.exports
module.exports = function(){
console.log("aaa");
...
}
加载模块的语法:
let 变量名 = require("./导出模块的文件名");
// 不要扩展名
let fn = require("./导出模块的文件名");
//执行加载的模块代码
fn();
导出多个模块的语法:
导出模块关键字 :exports
let a = 1;
let arr = [1, 2, 3];
let obj = { a: 1, b: 2 };
function fn() {
console.log("fn");
}
exports.a = a;
exports.arr = arr;
exports.obj = obj;
exports.fn = fn;
加载多个模块的语法:
let { 导出的变量名, 导出的变量名,.... } = require("./导出模块的文件名");
注意!!!
1. 有重复的变量名,取别名。
取别名的语法 ---> 原来的变量名:新的变量名
2. 同一个文件中不能既导出多个模块又导出默认模块。只能导出一个默认模块,或者只能导出多个模块,两者不能同时导出。
3.导入nodejs的默认API也是使用require();,但不需要加./这样的路径。导入nodejs的变量一般使用const修饰。
4. 下载的包(库),直接通过require()引入,不需要给路径。
5. 在package.json设置了type:module 表示当前Nodejs使用ES6模块化,不能使用ES5模块化的Commonjs。如果下载包是ES5模块化的也不能使用。
// 不要扩展名
let fn = require("./a");
let { a, obj, arr, fn: fn1 } = require("./b");
let fn2 = require("./b");
fn();
console.log(a);
console.log(obj);
console.log(arr);
fn1();
fn2();
----------------------------------
// 导入nodejs的API 不需要加./这样的路径
const http = require("http");
// 下载的包,直接通过require引入,不需要给路径
const lodash = require("lodash");
console.log(lodash.chunk([1, 2, 3, 4, 5], 2));
5. node版本切换工具nvm
- nvm ls :查看所有已经安装的node版本。
- nvm install v版本 :安装指定的node版本。
- nvm uninstall v版本:卸载安装的版本。
- nvm use 版本:使用某个node版本。
6. node的平台的包管理器npm
npm 是 Node JavaScript 平台的包管理器。它将模块放置到位,以便 node 可以找到它们,并智能地管理依赖冲突。它具有极强的可配置性,可以支持各种用例。最常见的是,您使用它来安装、卸载、更新、查看、搜索、发布和开发 node 程序。运行 npm help
以获取可用命令的列表。
npm的背后,是基于couchdb的一个数据库,详细记录了每个包的信息,包括作者、版本、依赖、授权信息等。它的一个很重要的作用就是:将开发者从繁琐的包管理工作(版本、依赖等)中解放出来,更加专注于功能的开发。
常用命令:
- npm init -y : 自动生成默认的包配置文件package.json。如果没有写-y就需要你自己一步一步的填写配置信息。
- npm -v :查看版本。
下载包的命令:
- npm install 包名:下载指定的包。缩写npm i 包名 。
- npm install 包名@latest :下载最新版本的包。缩写npm i 包名@latest。
- npm install 包名@版本 :下载指定版本的包。缩写npm i 包名@版本。
全局安装命令:
- npm i 包名 -g :全局安装 ,命令行插件可以这么安装。
- npm i nrm -g :全局安装 nrm包 ,nrm包的作用是转换npm的下载地址镜像。
- nrm ls :查看所有镜像地址。
- nrm test :镜像地址 ,测试镜像地址的速度。
- nrm use 镜像地址 :使用某个镜像地址。
- npm i nodemon -g :全局安装nodemon包。nodemon包的作用是实时自动执行程序。nodemon 文件名.js :执行该文件。
- npm i http-server -g :全局安装http-server包。http-server创建一个web服务。
- npm i webpack -g :全局安装webpack打包工具。
项目中用到插件主要分两种,一种项目依赖,一种是开发依赖。
项目依赖:比如项目中需要使用vue或者lodash,项目上线后,vue和lodash都需要一起放在线上,否则无法运行。
开发依赖:项目中如果需要打包、测试、创建临时服务器等操作时,下载的webpack等都是本地开发时需要,但是线上运行时不需要,这种包就叫做开发依赖。
安装到项目依赖中3种写法:
- npm i 包名 --save
- npm i 包名 -S
- npm i 包名
安装到开发依赖中2种写法:
npm i 包名 --save-dev
npm i 包名 -D
同时下载多个包的命令:
npm i 包名1 包名2 包名3 -S
node配置文件package.jsonxian详解:
{
"name": "npms", ---> 项目名,
"version": "1.0.0", ---> 版本
"description": "", ---> 项目描述
"main": "a.js", ---> 项目入口文件
"type" : "module" ---> 设置为ES6模块化
"scripts": { ---> 脚本,要执行的命令
"start": "cross-env PORT=4000 nodemon", ---> "脚本名" :"要执行的命令"。
"dev":"node a.js"
注意!!!
使用npm run 脚本名就可以执行命令。脚本名如果使用的是 start ,执行时不用run ,直接执行npm start。脚本名如果使用 install ,执行时不用run ,直接执行npm i 先下载插件再执行脚本内容。
},
"keywords": [], ---> 项目关键词
"author": "", ---> 作者
"license": "ISC", ---> 项目版权
"dependencies": { ---> 项目依赖
"lodash":"^4.17.21",
"vue":"^3.4.38"
},
"devDependencies": { ---> 开发依赖
"lodash":"^4.17.21", ---> ^大版本锁定,锁定4版本,更新小版本和微版本。
"vue":"~3.4.38", ---> ~ 小版本锁定 锁定大版本和小版本,只更新微版本。
"vue":"3.4.38", ---> 全锁定,只下这个版本。
"vue":"*", ---> *下载最新版本,不建议使用。
},
"config": { ---> 项目依赖
"name":"xietian",
"age":30
}
}
node配置文件package.jsonxian中"scripts"脚本的使用详解:
1. 当开发项目的时候,很多命令行工具,安装在全局中 -g 这时候这个项目就只能你自己跑起来。把项目发给别人,别人没有安装对应全局命令,就无法使用项目。把所有的安装包,包括全局安装包,写在package.json中,把全局插件写在开发依赖中 ,npm i 下载了所有包,当把项目发给别人时,需要先删除node_modules,然后发给别人,别人重新npm i 就会得到所有包。这时候全局命令并不能在命令行执行,把命令写在脚本中,执行脚本时,脚本会先查找本地node_modules,如果查找到就调用本地的命令,不会查找全局命令。
2、可以通过config配置参数传入到主程序中。
process.env.npm_package_config_属性名
3、可以通过脚本传参。
"start":"要执行的命令"
例如: "start": "cross-env PORT=4000 nodemon a.js"
当nodemon不写文件名.js它会直接执行入口文件。
注意!!!
这个方法苹果电脑可以,但是Windows电脑不行。
Windows需要通过配置 "devDependencies" 下载"cross-env": "^7.0.3",并在"scripts"脚本中添加 "start": "cross-env "才能使用。
4、可以使用入口文件。
其它npm命令详解:
根据配置文件下载包:
- npm i :根据package.json文件中dependencies和devDependencies下载更新里面的所有内容。
- npm update :作用和npm i 是一样的。
查看命令:
- npm view 包名 versions :查找指定包线上发布所有版本。
- npm view 包名 :查找指定包的信息。
- npm ls :查看所有安装的包列表。
- npm ls 包名 :查看指定包名的依赖。
清除缓存命令:不支持断点续传和多点下载。
- npm cache clean --force
- npm cache verify
卸载包命令·:
npm uninstall 包名
npm uninstall 包名 -g
npm uninstall 包名 -S
npm uninstall 包名 -D
配置下载镜像地址命令:
npm config set registry https://registry.npmjs.org/
查看npm的代理服务命令:代理必须都指向null。
- npm config get proxy
- npm config get https-proxy
设置npm的代理服务命令:如果代理不为null,使用以下命名设置。错误内容为ECONNREFUSED就会一直连接不上。连接不上就是null。
- npm config set proxy null
- npm config set https-proxy null
代码上传步骤:
1. nrm use npm :重新指向npm官网地址
2. 在npm官网中注册
3. npm login
输入用户名 :不会显示输入内容,直接输入。
输入密码 : 不会显示输入内容,直接输入。
输入邮箱 : 不会显示输入内容,直接输入。
4. 查看是否重名。package.json中 "name": "npms",必须要修改,修改的名字在npmjs官网的包名没有重复。不允许使用大写字母 ,尽量不使用短横线、下划线,可以使用@ /。
5. npm publish :上传。
7. npx
npx的作用就是当别人安装不上全局配置,但是是临时使用。直接执行npx的全局命令,不需要安装。npx 不需要下载到全局中,可以直接下载到缓存运行。
npx 全局命令
例如: npx use taobao
8. 案例
8.1 服务端渲染SSR
1. 创建server文件夹。
2. 创建server.js文件,右键选择在在集成终端打开。
3. npm init -y :初始化,得到一个package.json的文件。
4. 编写package.json的文件。
{
"name": "server",
"version": "1.0.0",
"main": "server.js",
"scripts": {
"start": "cross-env PORT=4000 nodemon"
},
"keywords": [],
"author": "",
"license": "ISC",
"dependencies": {},
"devDependencies": {//开发依赖
"nodemon": "^3.1.4",//开启服务
"cross-env": "^7.0.3"
}
}
5. npm i : 下载node_modules。如果该命令无法下载成功。全局下载一个yarn,使用yarn的命令来下载node_modules。yarn的作用和npm的作用是一样的。
yarn有关的命令:
- npm i yarn -g :全局下载一个yarn。
- yarn add 包名 等同于 npm i 包名
- yarn remove 包名 等同于 npm uninstall 包名
- yarn 等同于 npm i
6. npm start :启动程序。
7. 编写server.js文件。
response.end()
方法说明:结束响应,告诉客户端所有消息已经发送。当所有要返回的内容发送完毕时,该函数必须被调用一次。 如果不调用该函数,客户端将永远处于等待状态。
语法:
response.end([data], [encoding])
接收参数:
- data : end()执行完毕后要输出的字符,如果指定了 data 的值,那就意味着在执行完 response.end() 之后,会接着执行一条 response.write(data , encoding);
- encoding: 对应data的字符编码。
// 1.引入http模块
const http = require("http");
// 2.创建一个服务实例
//require:请求, response:响应
let app = http.createServer(function (require, response) {
// 2.1响应头
response.writeHead(200, {
// 响应的内容的类型是文本或者是html页面,编译使用utf-8字符集
"Content-type": "text/html;charset=utf-8"
});
// 2.2服务器渲染一个页面,向页面中写入内容
response.write("<ul>");
for (var i = 0; i < 10; i++) {
response.write("<li>" + i + "</li>");
}
response.write("</ul>");
// 2.3返回响应消息,告诉客户端所有消息已经发送。
response.end();
});
// 3.服务实例侦听来访问服务器的请求,获取到端口号和IP地址
app.listen(process.env.PORT);
8. 访问 localhost:4000
8.2 服务端渲染的通信方法
1. 方法一:获取路由的方法
例如像 localhost:4000/?type=1这样的网址该怎样获取到值
require请求对象有一个属性url可以直接获取到网址中?(包括?)后面的内容和网址标题的图标。
例如:require.url 获取到两部分的内容,分别是/?type=1和/favicon.ico。
根据上述url属性来获取路由修改代码:
// 1.引入http
const http = require("http");
// 2.创建一个服务实例
//require:请求, response:响应
let app = http.createServer(function (require, response) {
// require.url 可以获取到两部分的内容,分别是/?type=1和/favicon.ico
// 2.1当获取到的url是/favicon.ico的部分时,直接告诉客户端所有消息已经发送。
if (require.url === "favicon.ico") {
response.end();
return;
}
// 2.2当获取到的url是/?type=1的部分时,进行拆分获取到?后面的部分
// 调用解析请求url的函数
let obj = getQuery(require);
// 2.3响应头
response.writeHead(200, {
// 响应的内容的类型是文本或者是html页面,编译使用utf-8字符集
"Content-type": "text/html;charset=utf-8",
});
switch (obj.type) {
case 1:
getList(response);
break;
case 2:
addUser(response);
break;
case 3:
removeUser(response);
break;
}
function getList(response) {
response.end("list");
}
function addUser(response) {
response.end("addUser");
}
function removeUser(response) {
response.end("removeUser");
}
// 2.4服务器渲染一个页面,向页面中写入内容
response.write("<ul>");
for (var i = 0; i < 10; i++) {
response.write("<li>" + i + "</li>");
}
response.write("</ul>");
// 2.5返回响应消息,告诉客户端所有消息已经发送。
response.end();
});
// 解析require请求url的函数
function getQuery(require) {
let url = require.url.split("?")[1];
// 截取像这样的?type=1&user=kwj 的网址变成 [type=1,user=kwj];
// value:上一次返回的结果, item:数组中的每一个元素
return url.split("&").reduce((value, item) => {
//[type=1,user=kwj]==> [type,1] [user,kwj]
var arr = item.split("=");
try {
value[arr[0]] = JSON.parse(arr[1]);
} catch (e) {
value[arr[0]] = arr[1];
}
return value;
}, {})
}
// 3.服务实例侦听来访问服务器的请求,获取到端口号和IP地址
app.listen(process.env.PORT);
2. 方法二:使用路劲获取路由
例如像 localhost:4000/list?a=1这样的网址该怎样获取到值
8.4 服务端渲染使用get请求怎么获取到地址上的参数
querystring.parse(request.url.split("?")[1])可以直接获取到url。
//get请求讲解
const http = require("http");
let app = http.createServer(function (request, response) {
if (request.url === "/favicon.ion") {
response.end();
return;
}
// parse可以直接获取到url
let obj = querystring.parse(request.url.split("?")[1]);
console.log(obj);
//服务端渲染,写入响应头
response.writeHead(200, {
"Content-Type": "text/html;charset=utf-8",
"Access-Control-Allow-Origin": "*"
})
response.end("OK");
});
// 端口号
app.listen(process.env.PORT);
8.5 客户端与服务器的交流
1. 创建client文件夹并创建一个getIndex.html。
2.编写getIndex.html。
<!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>
<!-- 默认情况下表单有action,当提交时,会把表单元素中所有name对应表单元素的value,拼装成search,提交给action对应的地址 -->
<form action="http://localhost:4000" method="get">
<label>用户名</label> <input type="text" name="user">
<label>密码</label> <input type="password" name="password">
<label>年龄</label><input type="text" name="age">
<label>性别</label><input type="text" name="sex">
<button type="submit">提交</button>
</form>
</body>
</html>
3. 运行 getIndex.html。