18.npm、yarn、cnpm包管理工作
响应式-正确收集依赖的过程
// 监听对象的属性变量:Proxy(vue3)/0bject.defineProperty(vue2)
const objProxy = new Proxy(obj, {
get: function(target, key, receiver) {
// 根据target.key获取对应的depend
const depend = getDepend(target, key)
// 给depend对象中添加响应函数
depend.addDepend(activeReactiveFn)
return Reflect.get(target, key, receiver)
},
set: function(target, key, newValue, receiver) {
Reflect.set(target, key, newValue, receiver)
// depend.notify()
const depend = getDepend(target, key)
depend.notify()
}
})
【对Depend类重构】
00:44:00
01:00:00
响应式函数模拟Vue3示例代码:
// 保存当前需要收集的响应式函数
let activeReactiveFn = null
/**
* Depend优化:
* 1> depend方法
* 2> 使用Set来保存依赖函数, 而不是数组[]
*/
class Depend {
constructor() {
this.reactiveFns = new Set()
}
// addDepend(reactiveFn) {
// this.reactiveFns.add(reactiveFn)
// }
depend() {
if (activeReactiveFn) {
this.reactiveFns.add(activeReactiveFn)
}
}
notify() {
this.reactiveFns.forEach(fn => {
fn()
})
}
}
// 封装一个响应式的函数
function watchFn(fn) {
activeReactiveFn = fn
fn()
activeReactiveFn = null
}
// 封装一个获取depend函数
const targetMap = new WeakMap()
function getDepend(target, key) {
// 根据target对象获取map的过程
let map = targetMap.get(target)
if (!map) {
map = new Map()
targetMap.set(target, map)
}
// 根据key获取depend对象
let depend = map.get(key)
if (!depend) {
depend = new Depend()
map.set(key, depend)
}
return depend
}
function reactive(obj) {
return new Proxy(obj, {
get: function(target, key, receiver) {
// 根据target.key获取对应的depend
const depend = getDepend(target, key)
// 给depend对象中添加响应函数
// depend.addDepend(activeReactiveFn)
depend.depend()
return Reflect.get(target, key, receiver)
},
set: function(target, key, newValue, receiver) {
Reflect.set(target, key, newValue, receiver)
// depend.notify()
const depend = getDepend(target, key)
depend.notify()
}
})
}
// 监听对象的属性变量: Proxy(vue3)/Object.defineProperty(vue2)
const objProxy = reactive({
name: "why", // depend对象
age: 18 // depend对象
})
const infoProxy = reactive({
address: "广州市",
height: 1.88
})
watchFn(() => {
console.log(infoProxy.address)
})
infoProxy.address = "北京市"
const foo = reactive({
name: "foo"
})
watchFn(() => {
console.log(foo.name)
})
foo.name = "bar"
控制台输出:
广州市
北京市
foo
bar
01:11:00
Promise使用详解
【任何一个新技术的出现,都是为了解决原有技术的某一个痛点而产生的。】
异步任务的处理
在ES6出来之后,有很多关于Promise的讲解、文章,也有很多经典的书籍讲解Promise
· 虽然等你学会Promise之后,会觉得Promise不过如此,但是在初次接触的时候都会觉得这个东西不好理解;
那么这里我从一个实际的例子来作为切入点:
· 我们调用一个函数,这个函数中发送网络请求(我们可以用定时器来模拟);
· 如果发送网络请求成功了,那么告知调用者发送成功,并且将相关数据返回过去;
· 如果发送网络请求失败了,那么告知调用者发送失败,并且告知错误信息;
示例讲解“承诺Promise”缘由:
/**
* 这种回调的方式有很多的弊端:
* 1> 如果是我们自己封装的requestData,那么我们在封装的时候必须要自己设计好callback名称, 并且使用好
* 2> 如果我们使用的是别人封装的requestData或者一些第三方库, 那么我们必须去看别人的源码或者文档, 才知道它这个函数需要怎么去获取到结果
*/
// request.js
function requestData(url, successCallback, failtureCallback) {
// 模拟网络请求
setTimeout(() => {
// 拿到请求的结果
// url传入的是coderwhy, 请求成功
if (url === "coderwhy") {
// 成功
let names = ["abc", "cba", "nba"]
successCallback(names)
} else { // 否则请求失败
// 失败
let errMessage = "请求失败, url错误"
failtureCallback(errMessage)
}
}, 3000);
}
// main.js
requestData("kobe", (res) => {
console.log(res)
}, (err) => {
console.log(err)
})
// 更规范/更好的方案 Promise承诺(规范好了所有的代码编写逻辑)
function requestData2() {
return "承诺"
}
const chengnuo = requestData2()
01:41:00
什么是Promise呢?
在上面的解决方案中,我们确确实实可以解决请求函数得到结果之后,获取到对应的回调,但是它存在两个主要的
问题:
· 第一,我们需要自己来设计回调函数、回调函数的名称、回调函数的使用等;
· 第二,对于不同的人、不同的框架设计出来的方案是不同的,那么我们必须耐心去看别人的源码或者文档,以便可以理解它这个函数到底怎么用;
我们来看一下Promise的API是怎么样的:
Promise是一个类,可以翻译成承诺、许诺、期约;
· 当我们需要给予调用者一个承诺:待会儿我会给你回调数据时,就可以创建一个Promise的对象;
· 在通过new创建Promise对象时,我们需要传入一个回调函数,我们称之为executor
√这个回调函数会被立即执行,并且给传入另外两个回调函数resolve、reject ;
√当我们调用resolve回调函数时,会执行Promise对象的then方法传入的回调函数;
√当我们调用reject回调函数时,会执行Promise对象的catch方法传入的回调函数;
function Person(name, age) { }
const p = new Person("why",18)
//类似上面的,new创建Promise返回的是一个“promise承诺”,也是可以传入参数的(参数是:回调函数,并且传入进去就会立即执行)
const promise = new Promise(() =>{ })
//上面的Promise形式也可以用 class 类来类似说明:
class Person {
constructor(callback) {
callback()
}
}
调用时类似:
const p = new Person(() =>{ })
上面中“传入参数【回调函数】”传入的这个函数,被称之为executor,executor在执行的时候会传入一些其他的参数。继续举例:
class Person {
constructor(callback) {
let foo = function() { }
let bar = function() { }
callback(foo, bar)
}
}
const p = new Person((foo, bar) =>{
foo()
bar()
})
对应Promise调用类似【其中:resolve 和 reject 也都是回调函数】:
const promise = new Promise((resolve, reject) =>{
resolve()
})
promise.then(() =>{ })
规定:
在成功时,回调resolve函数;
在失败时,回调reject函数。
在拿到 promise 后,就能执行:
promise.then(() =>{ })
即:在你调用 resolve() 之后,外面的 then方法中的回调函数就会自动执行!
同样地,
即:在你调用 reject() 之后,外面的 catch方法中的回调函数就会自动执行!
promise.catch(() =>{ })
上面就是Promise最基本的创建过程。
这样,有利于在设计API时,只要知道你这个函数返回的是Promise,就可以在拿到结果后,直接写个then(…) 跟 catch(…) 。
知道,成功之后肯定执行then,失败肯定执行catch
还一种写法,直接在then(…) 中传入两个回调函数,第一个是成功的,第二个是失败的。
01:58:00
结合Promise,现在改一下上面的异步函数示例:
function requestData(……) 改成用 Promise 来写。
function requestData(url,) {
// 异步请求的代码会被放入到executor中
return new Promise((resolve, reject) => {
// 模拟网络请求
setTimeout(() => {
// 拿到请求的结果
// url传入的是coderwhy, 请求成功
if (url === "coderwhy") {
// 成功
let names = ["abc", "cba", "nba"]
resolve(names)
} else { // 否则请求失败
// 失败
let errMessage = "请求失败, url错误"
reject(errMessage)
}
}, 3000);
})
}
// main.js
const promise = requestData("coderwhy")
promise.then((res) => {
console.log("请求成功:", res)
}, (err) => {
console.log("请求失败:", err)
})
Promise就是为了解决类似于异步处理函数的各种方案,它不希望我们自己来设计、它给我们一种比较固定、规范的方法;
在两个文件或函数之间沟通的时候,返回一个Promise,我们在then中传入回调。
示例:
function foo() {
// Promise
return new Promise((resolve, reject) => {
resolve("success message")
// reject("failture message")
})
}
// main.js
const fooPromise = foo()
// then方法传入的回调函数两个回调函数:
// > 第一个回调函数, 会在Promise执行resolve函数时, 被回调
// > 第二个回调函数, 会在Promise执行reject函数时, 被回调
fooPromise.then((res) => {
console.log(res)
}, (err) => {
console.log(err)
})
【回调函数,也叫Hook函数(勾子函数)】
// 完全等价于下面的代码
// 【注意】: Promise状态一旦确定下来, 那么就是不可更改的(锁定)
new Promise((resolve, reject) => {
// pending状态: 待定/悬而未决的
console.log("--------")
reject() // 处于rejected状态(已拒绝状态)
resolve() // 处于fulfilled状态(已敲定/兑现状态)
console.log("++++++++++++")
}).then(res => {
console.log("res:", res)
}, err => {
console.log("err:", err)
})
输出:
--------
++++++++++++
err: undefined
Promise划分状态表现图
上面Promise使用过程,我们可以将它划分成三个状态:
待定(pending):初始状态,既没有被兑现,也没有被拒绝;
√当执行executor中的代码时,处于该状态;
已兑现(fulfilled或resolved):意味着操作成功完成;
√执行了resolve时,处于该状态;
已拒绝(rejected):意味着操作失败;
√执行了reject时,处于该状态;
/**
* resolve(参数)
* 1> 普通的值或者对象 pending -> fulfilled
* 2> 传入一个Promise
* 那么当前的Promise的状态会由传入的Promise来决定
* 相当于状态进行了移交
* 3> 传入一个对象, 并且这个对象有实现then方法(并且这个对象是实现了thenable接口)
* 那么也会执行该then方法, 并且又该then方法决定后续状态
*/
// 示例1.传入Promise的特殊情况
const newPromise = new Promise((resolve, reject) => {
// resolve("aaaaaa")
reject("err message")
})
new Promise((resolve, reject) => {
// pending -> fulfilled
resolve(newPromise)
}).then(res => {
console.log("res:", res)
}, err => {
console.log("err:", err)
})
输出:
err: err message
// 示例2.传入一个对象, 这个兑现有then方法
const newPromise = new Promise((resolve, reject) => {
// resolve("aaaaaa")
reject("err message")
})
new Promise((resolve, reject) => {
// pending -> fulfilled
const obj = {
then: function(resolve, reject) {
// resolve("resolve message")
reject("reject message")
}
}
resolve(obj)
}).then(res => {
console.log("res:", res)
}, err => {
console.log("err:", err)
})
输出:
err: reject message
【报错】 Uncaught (in promise) err message
// 示例3:针对示例2报错,需要相应加上catch
const newPromise = new Promise((resolve, reject) => {
// resolve("aaaaaa")
reject("err message")
}).catch(err => {
console.log("era:", err)
})
new Promise((resolve, reject) => {
// pending -> fulfilled
const obj = {
then: function(resolve, reject) {
// resolve("resolve message")
reject("reject message")
}
}
resolve(obj)
}).then(res => {
console.log("res:", res)
}, err => {
console.log("err:", err)
})
输出:
era: err message
err: reject message