vue2.x 无缝升级到 vue3.x

背景

  • vue3做了很多优化,比如说diff算法(最长上升子序列),数据劫持优化使用了proxy,解决了之前数组和对象添加新属性的更新问题,更小的体积;编译优化:靶向更新,提升(变量提升,静态字符串,公共样式);语法优化:compositionAPI
  • 可以参考:https://juejin.cn/post/6903012926660018183#heading-7
  • 未来 vue2项目需要升级成 vue3版本的,包括nuxt,虽然 Vue2团队尽可能的减少了破坏性的更新,还提供了一份细致的迁移指南,大部分是体力活,有的改起来稍微有点麻烦,比如自定义指令生命周期的更名,以及传入参数的一些细微变化。
  • 思路:vue2 => AST => 经过一个据迁移指南自己根写的插件 =>vue3

    工具:gogocode-plugin-vue

  • 阿里妈妈的新工具 GoGoCode的内置工具,GoGoCode,一个 AST处理工具

    使用

  • 全局安装

    1. npm install gogocode-cli -g
  • 迁移源文件: 进入需要升级的vue目录

  1. gogocode -s ./src(原文件的文件目录) -t gogocode-plugin-vue -o ./src-out(输出的文件目录)
  2. //如果两个文件目录名字相同,转换插件会覆盖你的代码,所以请一定先备份
  • 依赖升级(升级package.json中的vue/vuex/vue-router/Vue编译工具)
  • 如果是老版本 vue-cli生成的项目,需要自行升级vue-cli 确保vue3项目能成功被构建 vue-cli4.5.0及以上

    1. gogocode -s package.json -t gogocode-plugin-vue -o package.json
  • 需要关注的地方:window.$vueApp = Vue.createApp(App) 需要提到最前面

  • 一些依赖于vue2开发的组件库也推出了vue3版本,如果API发生了变化,需要自己手动升级,(这得是个麻烦事),
  • 做好人工对比和测试,以免有没有覆盖的写法
  • 升级依赖很坑!!!!! 升级webpack4后一堆不兼容,5也是。html-webpack-plugin都不兼容,尤其是babel插件,需要用babel-upgrade 升级,babel-plugin-transform-vue-jsx也要升级到4.0.1,因为依赖了@babel/plugin-syntax-jsx: 参考:https://github.com/vuejs/babel-plugin-transform-vue-jsx/blob/master/package.json
  • 各种路由,各种vue找不到,综合管理后台放弃!!!
  • element-plus 也有不向下兼容的,比如说时间控件:https://github.com/element-plus/element-plus/issues/162

Nuxt 3情况

支持 webpack5 和 vite

PostCss 8 :https://www.postcss.com.cn/

  • 是一个用 JavaScript 工具和插件转换 CSS 代码的工具
  • 自动添加浏览器前缀 : 插件Autoprefixer
  • 帮你将最新的 CSS 语法转换成大多数浏览器都能理解的语法,并根据你的目标浏览器或运行时环境来确定你需要的 polyfills(),插件postcss-cssnext
  • 自动把px转为rem 插件 :postcss-pxtorem
  • CSS 模块 能让你你永远不用担心命名太大众化而造成冲突,只要用最有意义的名字就行了。需要详细学习 ```css / style.css / .className { color: green; }

import styles from “./style.css”; // import { className } from “./style.css”;

element.innerHTML = ‘

‘;

  1. - 通过使用[stylelint](https://stylelint.io/) 强化一致性约束并避免样式表中的错误。stylelint 是一个现代化 CSS 代码检查工具。它支持最新的 CSS 语法,也包括类似 CSS 的语法,例如 SCSS
  2. - 处理流程图如下:参考:[https://www.jianshu.com/p/9a9048bc8978](https://www.jianshu.com/p/9a9048bc8978)
  3. ![](https://cdn.nlark.com/yuque/0/2021/jpeg/225247/1625127912545-3759a436-c90d-40e9-bb3a-cc61e373398d.jpeg)
  4. <a name="FOCjX"></a>
  5. #### ESBuild
  6. <a name="O9jX9"></a>
  7. #### 用ts重写了
  8. <a name="oDEbM"></a>
  9. #### 用vue3渲染
  10. <a name="xWC6C"></a>
  11. #### 模块化工具
  12. <a name="HE6DX"></a>
  13. #### 新的渲染引擎:Nitro 什么鬼??
  14. <a name="G5bWC"></a>
  15. #### 入口文件app.vue
  16. <a name="POB0l"></a>
  17. #### 可选的page文件夹
  18. <a name="OW9fZ"></a>
  19. #### Alpha 版本2021第一季度开源,Beta 版本第二季度开源。。。好像被骗了,并没有找到
  20. 来自:[https://nuxt.slides.com/atinux/nuxt-3-in-action](https://nuxt.slides.com/atinux/nuxt-3-in-action)<br />[
  21. ](https://www.jianshu.com/p/9a9048bc8978)
  22. <a name="t1jwj"></a>
  23. ## reduce用法大全
  24. <a name="GRJ6F"></a>
  25. ### 1、语法
  26. - 接收2个参数,第一个是回调函数(必选),第二个参数是初始值initialValue(可选)
  27. - 回调函数接收4个参数(累计器,当前值,当前索引,源数组)
  28. - ⚠️初始值initialValue 可以是任意类型,如果没有提供initialValue,从索引1开始执行callback;如果提供了initialValue,从索引0开始执行;
  29. - 它的执行就像一个贪吃蛇,蛇每吃一个豆子,豆子将会变成蛇身的一部分,蛇再去吃下一个豆子。![微信图片_20210809114803.png](https://cdn.nlark.com/yuque/0/2021/png/225247/1628480959729-ff2b1259-008b-4d27-bb0e-b9771bf421e8.png#clientId=ufed67d6c-c4c6-4&from=ui&id=uab1ac4a3&margin=%5Bobject%20Object%5D&name=%E5%BE%AE%E4%BF%A1%E5%9B%BE%E7%89%87_20210809114803.png&originHeight=340&originWidth=500&originalType=binary&ratio=1&size=165271&status=done&style=none&taskId=u23ffed75-34c5-4905-9a7b-57f59dffcf7)
  30. ```javascript
  31. //不带初始值
  32. [1,2,3,4].reduce((acc, cur) => {
  33. return acc + cur
  34. })
  35. //带初始值
  36. [1,2,3,4].reduce((acc, cur) => {
  37. return acc + cur
  38. }, 10)

2、重塑

reduce ->map

  • map 接受一个回调函数,参数为(当前项,索引,原数组),返回一个新数组,并且数组长度不变

    1. const testArr = [1,2,3,4]
    2. Array.prototype.reduceMap = function(callback){
    3. //this拿到当前需要操作的数组
    4. return this.reduce((acc,cur,index,array)=>{
    5. const item = callback(cur,index,array)
    6. acc.push(item)
    7. return acc
    8. },[])
    9. }
    10. testArr.reduceMap((item, index) => {
    11. return item + index
    12. })

    reduce -> forEach

  • forEach接收两个函数作为参数,第一个参数回调函数,接受四个参数(当前项,索引,原数组);第二个参数thisArg为当执行回调函数 callback 时,用作 this 的值

  • 当指定了thisArg时,每次callback调用时,this都会指向 thisArg 参数;如果省略或者为null\undefined,this指向全局对象;如果回调函数是箭头函数,则此参数不起作用(箭头函数内部绑定了this值)
  • forEach没有返回值,不可以链式调用
  • forEach不能中止,除非抛出异常

    1. const testArr = [1,2,3,4]
    2. Array.prototype.reduceForEach = function(callback){
    3. this.reduce((acc,cur,index,array)=>{
    4. callback(cur,index,array)
    5. },[])
    6. }
    7. testArr.reduceForEach((item, index, array) => {
    8. console.log(item, index)
    9. })

    reduce -> filter

  • 接收一个回调函数,参数返回true则返回当前项,反之不返回。回调函数参数(当前项,index,array)

    1. const testArr = [1,2,3,4]
    2. Array.prototype.reduceFilter = function(callback){
    3. return this.reduce((acc,cur,index,array)=>{
    4. if(callback(cur,index,array)){
    5. acc.push(cur)
    6. }
    7. return acc
    8. },[])
    9. }
    10. testArr.reduceFilter(item => item % 2 == 0)

    reduce -> find

  • 参数一为callback(当前元素,index,array)

  • 参数二为thisArg,执行回调时用作this 的对象。可选
  • 返回第一个满足callback 的元素的值

    1. const testArr = [1,2,3,4]
    2. const testObj = [{a:1},{a:2},{a:3},{a:4},{a:5}]
    3. Array.prototype.reduceFind = function(callback){
    4. return this.reduce((acc,cur,index,array)=>{
    5. //如果找到了,并且acc.length ===0(代表找到的第一个),此时循环依旧进行,
    6. //只是不再进行操作
    7. if(callback(cur,index,array)){
    8. if(acc instanceof Array && acc.length ===0){
    9. acc = cur
    10. }
    11. }
    12. //如果都循环到最后一项了都没找到(acc.length==0),说明需要返回undefined
    13. if((index === array.length -1) && acc instanceof Array && acc.length == 0){
    14. acc = undefined
    15. }
    16. return acc
    17. },[])
    18. testArr.reduceFind(item => item % 2 == 0) // 2
    19. testObj.reduceFind(item => item.a % 2 == 0) // {a: 2}
    20. testObj.reduceFind(item => item.a % 9 == 0) // undefined
    21. }
    22. //这个方法 ,如果找到了第一个符合条件的,但是循环并没有停止,只是不会处理结果,优化点?

    使用

    二维数组转一维数组

  • 备注:可以使用flat,深度可以自定义,flatMap了解 ```javascript const testArr = [[1,2], [3,4], [5,6]] testArr.reduce((acc, cur) => { return acc.concat(cur) }, [])

  1. <a name="hcoS2"></a>
  2. #### 计算数组中每个元素出现的个数
  3. ```javascript
  4. const testArr = [1, 3, 4, 1, 3, 2, 9, 8, 5, 3, 2, 0, 12, 10]
  5. testArr.reduce((acc, cur) => {
  6. if (!(cur in acc)) {
  7. acc[cur] = 1
  8. } else {
  9. acc[cur] += 1
  10. }
  11. return acc
  12. }, {})

按照属性给数组分类

  1. const bills = [
  2. { type: 'shop', momey: 223 },
  3. { type: 'study', momey: 341 },
  4. { type: 'shop', momey: 821 },
  5. { type: 'transfer', momey: 821 },
  6. { type: 'study', momey: 821 }
  7. ];
  8. bills.reduce((acc, cur) => {
  9. // 如果不存在这个键,则设置它赋值 [] 空数组
  10. if (!acc[cur.type]) {
  11. acc[cur.type] = [];
  12. }
  13. acc[cur.type].push(cur)
  14. return acc
  15. }, {})

数组去重

  • 可以使用Set 和 Array.from()

    1. const testArr = [1,2,2,3,4,4,5,5,5,6,7]
    2. testArr.reduce((acc, cur) => {
    3. if (!(acc.includes(cur))) {
    4. acc.push(cur)
    5. }
    6. return acc
    7. }, [])

    求最大值或者最小值

    1. const testArr = [
    2. { age: 20 },
    3. { age: 21 },
    4. { age: 22 }
    5. ]
    6. testArr.reduce((acc, cur) => {
    7. if (!acc) {
    8. acc = cur
    9. return acc
    10. }
    11. if (acc.age < cur.age) {
    12. acc = cur
    13. return acc
    14. }
    15. return acc
    16. }, 0)

    按照顺序运行Promise(异步函数的串行)

  • 可以运用在下一个异步需要依赖上一个异步的返回值的 情况下

  • 没有依赖的情况下用Promise.all ```javascript function runPromiseInSequence(arr,input){ return arr.reduce((promiseChain,currentFunction)=>{ return promiseChain.then(currentFunction) //将下一个Promise作为上一个的then回调 },Promise.resolve(input)) //Promise.resolve(input) ,将初始值input转为Promise } function p1(a) { return new Promise((resolve, reject) => { resolve(a 5); }); } function p2(a) { return new Promise((resolve, reject) => { resolve(a 2); }); } function f3(a) { return a 3; } function p4(a) { return new Promise((resolve, reject) => { resolve(a 4); }); } const promiseArr = [p1, p2, f3, p4]; runPromiseInSequence(promiseArr, 10) .then(console.log); // 1200

//then回调的处理

  1. <a name="IHGMz"></a>
  2. #### 功能型函数管道
  3. ```javascript
  4. const double = x => x + x;
  5. const triple = x => 3 * x;
  6. const quadruple = x => 4 * x;
  7. // Function composition enabling pipe functionality
  8. const pipe = (...functions) => input => functions.reduce(
  9. (acc, fn) => fn(acc),
  10. input
  11. );
  12. // Composed functions for multiplication of specific values
  13. const multiply6 = pipe(double, triple);
  14. const multiply9 = pipe(triple, triple);
  15. const multiply16 = pipe(quadruple, quadruple);
  16. const multiply24 = pipe(double, triple, quadruple);
  17. // Usage
  18. multiply6(6); // 36
  19. multiply9(9); // 81
  20. multiply16(16); // 256
  21. multiply24(10); // 240

chrome调试不完全指南

  • 待补充,跟性能优化有关系,比如说内存占用啥的,以及浏览器原理,v8引擎等
  • object 的 delete的坑,了解对象在v8中的存储方式,transition tree ,快属性和慢属性,如果你对一个对象的属性进行 delete 操作,就会导致对象的存储方式退化到字典模式(慢属性模式)。相对于之前的快属性模式,这种存储方式更加消耗内存

参考:https://mp.weixin.qq.com/s/teWRsE0_eOF0-gXl5vCHiw
https://mp.weixin.qq.com/s/oPDb_VuwkA5XLaHKef7uZA
https://mp.weixin.qq.com/s/Ng-NCPOfOHTVnF0OvARzEw

一步步实现一个Promise

  1. 平常用promise的时候, 是通过new关键字来new Promise(), 所以咱们应该用构造函数或者class来实现. 都2021年了, 咱们就用class来实现一下吧.

    1. class MPromise {
    2. constructor() {
    3. }
    4. }
  2. 定义三种状态类型

    1. const PENDING = 'pending';
    2. const FULFILLED = 'fulfilled';
    3. const REJECTED = 'rejected';
  3. 设置初始状态

    1. class MPromise {
    2. constructor() {
    3. // 初始状态为pending
    4. this.status = PENDING;
    5. this.value = null;
    6. this.reason = null;
    7. }
    8. }
  4. resolve 和 reject 方法

    1. 根据刚才的规范, 这两个方法是要更改status的, 从pending改到fulfilled/rejected.
    2. 注意两个函数的入参分别是value 和 reason.
  1. class MPromise {
  2. constructor() {
  3. // 初始状态为pending
  4. this.status = PENDING;
  5. this.value = null;
  6. this.reason = null;
  7. }
  8. resolve(value) {
  9. if (this.status === PENDING) {
  10. this.status = FULFILLED;
  11. this.value = value;
  12. }
  13. }
  14. reject(reason) {
  15. if (this.status === PENDING) {
  16. this.status = REJECTED;
  17. this.reason = reason;
  18. }
  19. }
  20. }
  1. 是不是发现咱们的promise少了入参, 咱们来加一下
    1. 入参是一个函数, 函数接收resolve和reject两个参数.
    2. 注意在初始化promise的时候, 就要执行这个函数, 并且有任何报错都要通过reject抛出去
  1. class MPromise {
  2. constructor(fn) {
  3. // 初始状态为pending
  4. this.status = PENDING;
  5. this.value = null;
  6. this.reason = null;
  7. try {
  8. fn(this.resolve.bind(this), this.reject.bind(this));
  9. } catch (e) {
  10. this.reject(e);
  11. }
  12. }
  13. resolve(value) {
  14. if (this.status === PENDING) {
  15. this.status = FULFILLED;
  16. this.value = value;
  17. }
  18. }
  19. reject(reason) {
  20. if (this.status === PENDING) {
  21. this.status = REJECTED;
  22. this.reason = reason;
  23. }
  24. }
  25. }
  1. 接下来来实现一下关键的then方法
    1. then接收两个参数, onFulfilled 和 onRejected
  1. then(onFulfilled, onRejected) {}
  1. 检查并处理参数, 之前提到的如果不是function, 就忽略. 这个忽略指的是原样返回value或者reason.
  1. isFunction(param) {
  2. return typeof param === 'function';
  3. }
  4. then(onFulfilled, onRejected) {
  5. const fulFilledFn = this.isFunction(onFulfilled) ? onFulfilled : (value) => {
  6. return value;
  7. }
  8. const rejectedFn = this.isFunction(onRejected) ? onRejected : (reason) => {
  9. throw reason
  10. };
  11. }
  1. 根据当前promise的状态, 调用不同的函数
  1. then(onFulfilled, onRejected) {
  2. const fulFilledFn = this.isFunction(onFulfilled) ? onFulfilled : (value) => {
  3. return value;
  4. }
  5. const rejectedFn = this.isFunction(onRejected) ? onRejected : (reason) => {
  6. throw reason;
  7. };
  8. switch (this.status) {
  9. case FULFILLED: {
  10. fulFilledFn(this.value);
  11. break;
  12. }
  13. case REJECTED: {
  14. rejectedFn(this.reason);
  15. break;
  16. }
  17. }
  18. }
  1. 这个时候有的同学要问了, 你这样写, 是在then函数被调用的瞬间就会执行. 那这时候如果status还没变成fulfilled或者rejected怎么办, 很有可能还是pending的.
    1. 那么我们首先要拿到所有的回调, 然后才能在某个时机去执行他. 新建两个数组, 来分别存储成功和失败的回调, 调用then的时候, 如果还是pending就存入数组.
  1. then(onFulfilled, onRejected) {
  2. const fulFilledFn = this.isFunction(onFulfilled) ? onFulfilled : (value) => {
  3. return value;
  4. }
  5. const rejectedFn = this.isFunction(onRejected) ? onRejected : (reason) => {
  6. throw reason;
  7. };
  8. switch (this.status) {
  9. case FULFILLED: {
  10. fulFilledFn(this.value);
  11. break;
  12. }
  13. case REJECTED: {
  14. rejectedFn(this.reason);
  15. break;
  16. }
  17. case PENDING: {
  18. this.FULFILLED_CALLBACK_LIST.push(realOnFulfilled);
  19. this.REJECTED_CALLBACK_LIST.push(realOnRejected);
  20. break;
  21. }
  22. }
  23. }
  1. 2. status发生变化的时候, 就执行所有的回调. 这里咱们用一下es6gettersetter. 这样更符合语义, status改变时, 去做什么事情. (当然也可以顺序执行, 在给status赋值后, 下面再加一行forEach)
  1. get status() {
  2. return this._status;
  3. }
  4. set status(newStatus) {
  5. switch (newStatus) {
  6. case FULFILLED: {
  7. this.FULFILLED_CALLBACK_LIST.forEach(callback => {
  8. callback(this.value);
  9. });
  10. break;
  11. }
  12. case REJECTED: {
  13. this.REJECTED_CALLBACK_LIST.forEach(callback => {
  14. callback(this.reason);
  15. });
  16. break;
  17. }
  18. }
  19. }
  1. then的返回值
    这块内容比较多, 所以单拿出来了
    7.1 如果 onFulfilled 或者 onRejected 抛出一个异常 e ,则 promise2 必须拒绝执行,并返回拒因 e。(这样的话, 我们就需要手动catch代码,遇到报错就reject)
    7.2 如果 onFulfilled 不是函数且 promise1 成功执行, promise2 必须成功执行并返回相同的值
    7.3 如果 onRejected 不是函数且 promise1 拒绝执行, promise2 必须拒绝执行并返回相同的据因。
    需要注意的是,如果promise1的onRejected执行成功了,promise2应该被resolve
    7.4 如果 onFulfilled 或者 onRejected 返回一个值 x ,则运行resolvePromise方法

    1. then(onFulfilled, onRejected) {
    2. const fulFilledFn = this.isFunction(onFulfilled) ? onFulfilled : (value) => {
    3. return value;
    4. }
    5. const rejectedFn = this.isFunction(onRejected) ? onRejected : (reason) => {
    6. throw reason;
    7. };
    8. const fulFilledFnWithCatch = (resolve, reject) => {
    9. try {
    10. fulFilledFn(this.value);
    11. } catch (e) {
    12. reject(e)
    13. }
    14. };
    15. const rejectedFnWithCatch = (resolve, reject) => {
    16. try {
    17. rejectedFn(this.reason);
    18. } catch (e) {
    19. reject(e);
    20. }
    21. }
    22. switch (this.status) {
    23. case FULFILLED: {
    24. return new MPromise(fulFilledFnWithCatch);
    25. }
    26. case REJECTED: {
    27. return new MPromise(rejectedFnWithCatch);
    28. }
    29. case PENDING: {
    30. return new MPromise((resolve, reject) => {
    31. this.FULFILLED_CALLBACK_LIST.push(() => fulFilledFnWithCatch(resolve, reject));
    32. this.REJECTED_CALLBACK_LIST.push(() => rejectedFnWithCatch(resolve, reject));
    33. });
    34. }
    35. }
    36. }
    1. const fulFilledFnWithCatch = (resolve, reject) => {
    2. try {
    3. fulFilledFn(this.value);
    4. resolve(this.value);
    5. } catch (e) {
    6. reject(e)
    7. }
    8. };
    1. const rejectedFnWithCatch = (resolve, reject) => {
    2. try {
    3. rejectedFn(this.reason);
    4. if (this.isFunction(onRejected)) {
    5. resolve();
    6. }
    7. } catch (e) {
    8. reject(e);
    9. }
    10. }
    1. then(onFulfilled, onRejected) {
    2. const fulFilledFn = this.isFunction(onFulfilled) ? onFulfilled : (value) => {
    3. return value;
    4. }
    5. const rejectedFn = this.isFunction(onRejected) ? onRejected : (reason) => {
    6. throw reason;
    7. };
    8. const fulFilledFnWithCatch = (resolve, reject, newPromise) => {
    9. try {
    10. if (!this.isFunction(onFulfilled)) {
    11. resolve(this.value);
    12. } else {
    13. const x = fulFilledFn(this.value);
    14. this.resolvePromise(newPromise, x, resolve, reject);
    15. }
    16. } catch (e) {
    17. reject(e)
    18. }
    19. };
    20. const rejectedFnWithCatch = (resolve, reject, newPromise) => {
    21. try {
    22. if (!this.isFunction(onRejected)) {
    23. reject(this.reason);
    24. } else {
    25. const x = rejectedFn(this.reason);
    26. this.resolvePromise(newPromise, x, resolve, reject);
    27. }
    28. } catch (e) {
    29. reject(e);
    30. }
    31. }
    32. switch (this.status) {
    33. case FULFILLED: {
    34. const newPromise = new MPromise((resolve, reject) => fulFilledFnWithCatch(resolve, reject, newPromise));
    35. return newPromise;
    36. }
    37. case REJECTED: {
    38. const newPromise = new MPromise((resolve, reject) => rejectedFnWithCatch(resolve, reject, newPromise));
    39. return newPromise;
    40. }
    41. case PENDING: {
    42. const newPromise = new MPromise((resolve, reject) => {
    43. this.FULFILLED_CALLBACK_LIST.push(() => fulFilledFnWithCatch(resolve, reject, newPromise));
    44. this.REJECTED_CALLBACK_LIST.push(() => rejectedFnWithCatch(resolve, reject, newPromise));
    45. });
    46. return newPromise;
    47. }
    48. }
    49. }
  2. resolvePromise

    1. resolvePromise(newPromise, x, resolve, reject) {
    2. // 如果 newPromise 和 x 指向同一对象,以 TypeError 为据因拒绝执行 newPromise
    3. // 这是为了防止死循环
    4. if (newPromise === x) {
    5. return reject(new TypeError('The promise and the return value are the same'));
    6. }
    7. if (x instanceof MPromise) {
    8. // 如果 x 为 Promise ,则使 newPromise 接受 x 的状态
    9. // 也就是继续执行x,如果执行的时候拿到一个y,还要继续解析y
    10. // 这个if跟下面判断then然后拿到执行其实重复了,可有可无
    11. x.then((y) => {
    12. resolvePromise(newPromise, y, resolve, reject);
    13. }, reject);
    14. } else if (typeof x === 'object' || this.isFunction(x)) {
    15. // 如果 x 为对象或者函数
    16. // 这个坑是跑测试的时候发现的,如果x是null,应该直接resolve
    17. if (x === null) {
    18. return resolve(x);
    19. }
    20. let then = null;
    21. try {
    22. // 把 x.then 赋值给 then
    23. then = x.then;
    24. } catch (error) {
    25. // 如果取 x.then 的值时抛出错误 e ,则以 e 为据因拒绝 promise
    26. return reject(error);
    27. }
    28. // 如果 then 是函数
    29. if (this.isFunction(then)) {
    30. let called = false;
    31. // 将 x 作为函数的作用域 this 调用之
    32. // 传递两个回调函数作为参数,第一个参数叫做 resolvePromise ,第二个参数叫做 rejectPromise
    33. // 名字重名了,我直接用匿名函数了
    34. try {
    35. then.call(
    36. x,
    37. // 如果 resolvePromise 以值 y 为参数被调用,则运行 resolvePromise
    38. (y) => {
    39. // 如果 resolvePromise 和 rejectPromise 均被调用,
    40. // 或者被同一参数调用了多次,则优先采用首次调用并忽略剩下的调用
    41. // 实现这条需要前面加一个变量called
    42. if (called) return;
    43. called = true;
    44. resolvePromise(promise, y, resolve, reject);
    45. },
    46. // 如果 rejectPromise 以据因 r 为参数被调用,则以据因 r 拒绝 promise
    47. (r) => {
    48. if (called) return;
    49. called = true;
    50. reject(r);
    51. });
    52. } catch (error) {
    53. // 如果调用 then 方法抛出了异常 e:
    54. // 如果 resolvePromise 或 rejectPromise 已经被调用,则忽略之
    55. if (called) return;
    56. // 否则以 e 为据因拒绝 promise
    57. reject(error);
    58. }
    59. } else {
    60. // 如果 then 不是函数,以 x 为参数执行 promise
    61. resolve(x);
    62. }
    63. } else {
    64. // 如果 x 不为对象或者函数,以 x 为参数执行 promise
    65. resolve(x);
    66. }
    67. }
  3. onFulfilled 和 onRejected 是微任务
    咱们可以用queueMicrotask包裹执行函数

    1. then(onFulfilled, onRejected) {
    2. const fulFilledFn = this.isFunction(onFulfilled) ? onFulfilled : (value) => {
    3. return value;
    4. }
    5. const rejectedFn = this.isFunction(onRejected) ? onRejected : (reason) => {
    6. throw reason;
    7. };
    8. const fulFilledFnWithCatch = (resolve, reject, newPromise) => {
    9. queueMicrotask(() => {
    10. try {
    11. if (!this.isFunction(onFulfilled)) {
    12. resolve(this.value);
    13. } else {
    14. const x = fulFilledFn(this.value);
    15. this.resolvePromise(newPromise, x, resolve, reject);
    16. }
    17. } catch (e) {
    18. reject(e)
    19. }
    20. })
    21. };
    22. const rejectedFnWithCatch = (resolve, reject, newPromise) => {
    23. queueMicrotask(() => {
    24. try {
    25. if (!this.isFunction(onRejected)) {
    26. reject(this.reason);
    27. } else {
    28. const x = rejectedFn(this.reason);
    29. this.resolvePromise(newPromise, x, resolve, reject);
    30. }
    31. } catch (e) {
    32. reject(e);
    33. }
    34. })
    35. }
    36. switch (this.status) {
    37. case FULFILLED: {
    38. const newPromise = new MPromise((resolve, reject) => fulFilledFnWithCatch(resolve, reject, newPromise));
    39. return newPromise;
    40. }
    41. case REJECTED: {
    42. const newPromise = new MPromise((resolve, reject) => rejectedFnWithCatch(resolve, reject, newPromise));
    43. return newPromise;
    44. }
    45. case PENDING: {
    46. const newPromise = new MPromise((resolve, reject) => {
    47. this.FULFILLED_CALLBACK_LIST.push(() => fulFilledFnWithCatch(resolve, reject, newPromise));
    48. this.REJECTED_CALLBACK_LIST.push(() => rejectedFnWithCatch(resolve, reject, newPromise));
    49. });
    50. return newPromise;
    51. }
    52. }
    53. }
  4. 简单写点代码测试一下
    这个时候同学们会发现, 为什么我可以调用.then, 不可以调用.catch呢? 因为我们并没有在类里面声明catch方法 ```javascript const test = new MPromise((resolve, reject) => { setTimeout(() => { resolve(111); }, 1000); }).then(console.log);

console.log(test);

setTimeout(() => { console.log(test);

}, 2000)

  1. 11. catch方法
  2. ```javascript
  3. catch (onRejected) {
  4. return this.then(null, onRejected);
  5. }
  1. promise.resolve
    将现有对象转为Promise对象,如果 Promise.resolve 方法的参数,不是具有 then 方法的对象(又称 thenable 对象),则返回一个新的 Promise 对象,且它的状态为fulfilled。

    1. static resolve(param) {
    2. if (param instanceof MyPromise) {
    3. return param;
    4. }
    5. return new MyPromise(function (resolve) {
    6. resolve(param);
    7. });
    8. }
  2. promise.reject
    返回一个新的Promise实例,该实例的状态为rejected。Promise.reject方法的参数reason,会被传递给实例的回调函数。

    1. static reject(reason) {
    2. return new MPromise((resolve, reject) => {
    3. reject(reason);
    4. });
    5. }
  3. promise.race
    const p = Promise.race([p1, p2, p3]);
    该方法是将多个 Promise 实例,包装成一个新的 Promise 实例。
    只要p1、p2、p3之中有一个实例率先改变状态,p的状态就跟着改变。那个率先改变的 Promise 实例的返回值,就传递给p的回调函数。
    写段测试代码
    ```javascript static race(promiseList) { return new MPromise((resolve, reject) => { const length = promiseList.length;

    if (length === 0) {

    1. return resolve();

    } else {

    1. for (let i = 0; i < length; i++) {
    2. MPromise.resolve(promiseList[i]).then(
    3. (value) => {
    4. return resolve(value);
    5. },
    6. (reason) => {
    7. return reject(reason);
    8. });
    9. }

    } });

}

  1. ```javascript
  2. const test = new MPromise((resolve, reject) => {
  3. setTimeout(() => {
  4. resolve(111);
  5. }, 1000);
  6. });
  7. const test2 = new MPromise((resolve, reject) => {
  8. setTimeout(() => {
  9. resolve(222);
  10. }, 2000);
  11. });
  12. const test3 = new MPromise((resolve, reject) => {
  13. setTimeout(() => {
  14. resolve(333);
  15. }, 3000);
  16. });
  17. MPromise.race([test, test2, test3]).then(console.log);

class实现倒计时组件