Deng
Deng
ES6之Promise与Class类 | odjBlog
    欢迎来到odjBlog的博客!

ES6之Promise与Class类

web前端学习 odjbin 4年前 (2022-01-13) 58次浏览 0个评论

Promise

Promise 是什么

      //1.认识 Promise: 是异步操作的一种解决方案
      //回调函数
      document.addEventListener(
        "click",
        () => {
          console.log("这里是异步的");
        },
        false
      );
      console.log("这里是同步的");
      //2.什么时候使用 Promise : 一般用来解决层层嵌套的回调函数(回调地狱 callback hell)的问题
      //运动 box 移动右下左上
      const move = (el, { x = 0, y = 0 } = {}, end = () => {}) => {
        el.style.transform = `translate3d(${x}px,${y}px,0)`;
        el.addEventListener(
          "transitionend",
          () => {
            // console.log('end');
            end();
          },
          false
        );
      };
      const boxEl=document.getElementById('box');
      document.addEventListener('click',()=>{
        move(boxEl,{x:150},()=>{
          move(boxEl,{x:150,y:150},()=>{
            move(boxEl,{y:150},()=>{
              move(boxEl,{x:0,y:0})
            })
          });
        })
      },false);

Promise 的基本用法[重点]

      //1.实例化构造函数生成实例对象
      //Promise 解决的不是回调函数,而是回调地狱
      //2.Promise 的状态
      const p = new Promise((resolve, reject) => {
        //[重要]Promise 有 3 种状态,一开始是 pending(未完成),执行 resolve,变成 fulfilled(resolved),已成功
        //执行 reject,变成 rejected,已失败
        //Promise 的状态一旦变化,就不会再改变了
        resolve();//success
      });
      console.log(p);
      //3.then 方法
      p.then(()=>{
        console.log('success');
      },()=>{
        console.log('error');
      })
      //4.resolve 和 reject 函数的参数
      const p1 = new Promise((resolve, reject) => {
        // resolve({username:'alex'});
        // reject('reason');
        reject(new Error('reason'))
      })
      p1.then(
        (data)=>{
          console.log('success',data);
        },(err)=>{
        console.log('error',err);
      }
      )

then()[重点]

      //1.什么时候执行
      //pending->fulfilled 时,执行 then 的第一个回调函数
      //pending->rejected 时,执行 then 的第二个回调函数
      //2.执行后的返回值
      //then 方法执行后返回一个新的 Promise 对象
      const p = new Promise((resolve, reject) => {
        resolve();
      })
      const p2= p.then(
        () => {},//成功
        () => {}
      ).then()
      console.log(p,p2,p===p2);
      //3.then 方法返回的 Promise 对象的状态改变
      const p1 = new Promise((resolve, reject) => {
        //resolve();//success success2
        reject();//error success2
      })
      p1.then(//执行 success 这个回调
        () => {
          //console.log('success')
        },
        () => {
          console.log('error')
          //在 then 的回调函数中,return 后面的东西,会用 Promise 包装一下
          return undefined;
          //等价于
          // return new Promise(resolve=>{
          //   resolve( undefined)
          // })
          //默认返回的永远都是成功状态的 Promise 对象,失败则写完整(resolve, reject)调用 reject
        }
      ).then(//上边那个 then 的返回值决定后面的 then 执行回调
        data => {
          console.log('success2',data)
        },
        err => {
          console.log('error2',err)
        }
      ).then(
        data => {
          console.log('success3',data)
        },
        err => {
          console.log('error3',err)
        }
      )

then()-2

      //4.使用 Promise 解决回调地狱
      const boxEI = document.getElementById("box");
      //(1)纵向发展的,由外往内一层层嵌套的 [详细代码见标题 Promise 是什么]
      const movePromise = (el, point) => {
        return new Promise((resolve) => {
          move(el, point, () => {
            resolve();
          });
        });
      };
      //(2)横向发展的
      document.addEventListener(
        "click",
        () => {
          movePromise(boxEI, { x: 150 })
            .then(() => {
              return movePromise(boxEI, { x: 150, y: 150 });
            })
            .then(() => {
              return movePromise(boxEI, { y: 150 });
            })
            .then(() => {
              return movePromise(boxEI, { x: 0, y: 0 });
            });
        },
        false
      );

catch()[标红]

 <style>
      * {
        margin: 0;
        padding: 0;
      }
      #box {
        width: 300px;
        height: 300px;
        background-color: red;
        transition: all 0.5s;
      }
    </style>
  </head>
  <body>
    <div id="box"></div>
    <script>
      //then(data=>{});用的多
      //catch 专门用来处理 rejected 状态,本质上是 then 的特例
      //2.基本用法
      new Promise((resolve, reject) => {
        // resolve(123);
        reject("reason");
      })
        .then((data) => {
          console.log(data);
        })
        // .then(null,err=>{
        //     console.log(err);
        // });等价于↓
        .catch((err) => {
          console.log(err);
          //return undefined;
          throw new Error("reason");
        })
        .then((data) => {
          console.log(data); //undefined
        })
        .catch((err) => {
          console.log(err);
        });
      //catch()可以捕获它前面的错误
      //一般总是建议,Promise 对象后面要跟 catch 方法,
      // 这样可以处理 Promise 内部发生的错误

finally() 了解,工作不常用

      //1.什么时候执行
      //当 Promise 状态发生变化时,不论如何变化都会执行,不变化不执行
      new Promise((resolve, reject) => {
        // resolve(123);undefined
        reject("reason"); //undefined
      })
        .finally((data) => {
          console.log(data);
        })
        .catch((err) => {});
      //2.本质
      //finally()本质上是 then()的特例
      //上面等同于
      new Promise((resolve, reject) => {
        // resolve(123);//成功
        reject("reason"); //失败
      })
        .then(//本质上是这样
          (result) => {
            return result;
          },
          (err) => {
            return new Promise((resolve, reject) => {
              reject(err);
            });
          }
        ) 
        .then((data) => {
          console.log(data);
        })
        .catch((err) => {
          console.log(err);
        });

Promise 的构造函数方法

Promise.resolve() 和 Promise.reject()

      //1.Promise.resolve(): 是成功状态 Promise 的一种简写形式
      new Promise((resolve) => resolve("foo"));
      //简写: Promise.resolve('foo');
      //参数
      //一般参数 重点关注
      Promise.resolve("foo").then((data) => {
        console.log(data); //foo
      });
      /*了解就行,不用深究
      //特殊参数,Promise 对象
      // 当 Promise.resolve()接收的是 Promise 对象时,直接返回这个 Promise 对象,什么都不做
      const p = new Promise((resolve) => {
        setTimeout(resolve, 1000, "我执行了");
        //相当于
        // setTimeout(()=>{
        //   resolve('我执行了')
        // },1000)
      });
      Promise.resolve(p).then((data) => {
        console.log(data); //我执行了
      });
      //等价于
      p.then((data) => {
        console.log(data);
      });
      console.log(Promise.resolve(p) === p1); //true

      //当 resolve 函数接收的是 Promise 对象时,后面的 then 会根据传递的 Promise 对象的状态变化决定执行哪一个回调
      new Promise((resolve) => resolve(p)).then((data) => {
        console.log(p);
      });

      //(2)具有 then 方法的对象,作为参数
      function func(obj) {
        obj.then(1, 2);
      }
      func({
        then(resolve, reject) {
          console.log(a, b);
        },
      });
      const thenable = {
        then(resolve, reject) {
          console.log("then");
          resolve("data");
        },
      };
      Promise.resolve(thenable).then(
        (data) => console.log(data),
        (err) => console.log(err)
      );
      console.log(Promise.resolve(thenable));
      */

      //2.Promise.reject()
      //失败状态 Promise 的一种简写形式
      Promise.reject("reason");
      //参数 : 不管什么参数,都会原封不动地向后传递,作为后续方法的参数
      const p = new Promise((resolve) => {
        setTimeout(resolve, 1000, "我执行了");
      });
      Promise.reject(p).catch((err) =>console.log(err));

      new Promise((resolve,reject) => {
        resolve(123)
      }).then(data=>{
        // return data
        //return Promise.resolve(data)
        return Promise.reject('reason')
      }).then(data=>{
        console.log(data);
      }).catch(err=>console.log(err))

Promise.all()[重点]

      //1.有什么用
      //Promise.all()关注多个 Promise 对象的状态变化
      //传入多个 Promise 实例,包装成一个新的 Promise 实例返回
      //2.基本用法
      const delay = (ms) => {
        return new Promise((resolve) => {
          setTimeout(resolve, ms);
        });
      };
      const p1 = delay(1000).then(() => {
        console.log("p1 完成了");
        // return "p1";
        return Promise.reject('reason')//p1 完成了 reason p2 完成了
      }); 
      const p2 = delay(1000).then(() => {
        console.log("p2 完成了");
        return "p2";
      });
      //Promise.all()的状态变化与所有传入的 Promise 实例对象状态有关
      //所有状态都变成 resolved,最终的状态才会变成 resolved
      //只要有一个变成 rejected,最终的状态就变成 rejected
      const p = Promise.all([p1, p2]);
      p.then(
        (data) => {
          console.log(data);
        },
        (err) => {
          console.log(err);
        }
      );

Promise.race() 和 Promise.allSettled()

     const delay = (ms) => {
        return new Promise((resolve) => {
          setTimeout(resolve, ms);
        });
      };
      const p1 = delay(1000).then(() => {
        console.log("p1 完成了");
        return "p1";
      });
      const p2 = delay(2000).then(() => {
        console.log("p2 完成了");
        return "p2";
      });
      //1.Promise.race()
      //Promise.race()的状态取决于第一个完成的 Promise 实例对象,如果第一个完成的成功了,那最终的就成功;如果第一个完成的失败了,那么最终的就失败
      const racePromise = Promise.race([p1, p2]);
      racePromise.then(
        (data) => {
          console.log(data);
        },
        (err) => {
          console.log(err);
        }
      );
      //2.Promise.allSettled()
      //Promise.allSettled()的状态与传入的 Promise 状态无关,永远都是成功的,它只会忠实的记录下各个 Promise 的表现
      const allSettledPromise = Promise.allSettled([p1, p2]);
      allSettledPromise.then((data) => {
        console.log("succ", data);
      });

Promise 的注意事项

      //1.resolve 或 reject 函数执行后的代码
      //推荐在调用 resolve 或 reject 函数的时候加上 return,不再执行它们后面的代码
      new Promise((resolve, reject) => {
        return resolve(123);
        //return reject('reason');
      });
      //2.Promise.all/race/allSettled 的参数问题
      //参数如果不是 Promise 数组,会将不是 Promise 的数组元素转变成 Promise 对象
      Promise.all([1, 2, 3]).then((datas) => {
        console.log(datas); //(3) [1, 2, 3]
      });
      //等价于
      // Promise.all([
      //     Promise.resolve(1),
      //     Promise.resolve(2),
      //     Promise.resolve(3)
      // ]).then(datas=>{
      //     console.log(datas);
      // });
      //不只是数组,任何可遍历的都可以作为参数
      //数组,字符串,Set,Map,NodeList,arguments
      Promise.all(new Set([1, 2, 3])).then((datas) => {
        console.log(datas); //(3) [1, 2, 3]
      });
      //3.Promise.all/race/allSettled 的错误处理
      const delay = (ms) => {
        return new Promise((resolve) => {
          setTimeout(resolve, ms);
        });
      };
      const p1 = delay(1000)
        .then(() => {
          console.log("p1 完成了");
          return "p1"; //成功状态
          // return Promise.reject("reason");
        })
        // .catch((err) => {
        //   console.log("p1", err);
        // }); //p1 reason
      const p2 = delay(2000)
        .then(() => {
          console.log("p2 完成了");
          return "p2"; //成功状态
          // return Promise.reject('reason');
        })
        // .catch((err) => {
        //   console.log("p2", err);
        // });
      const allPromise = Promise.all([p1, p2]);
      allPromise
        .then((datas) => {
          console.log(datas);
        })
        .catch((err) => {
          console.log(err);
        });
      //错误既可以单独处理,也可以统一处理
      //一旦被处理,就不会在其他地方再处理一遍

Promise 的应用[重点]

   <style>
     #img {
        width: 80%;
        padding: 10%;
      }
    </style>
  </head>
  <body>
    <img
      src="https://images.alphacoders.com/116/1162246.jpg"
      alt=""
      id="img"
    />
    <script>
      // 异步加载图片
      const loadImgAsync = (url) => {
        return new Promise((resolve,reject)=>{
          const img=new Image();
          img.onload=()=>{
            resolve(img)
          }
          img.onerror=()=>{
            reject(new Error(`Could not load image at ${url}`))
          }
          img.src=url
        })
      }
      const ImgDOM=document.getElementById('img')
      loadImgAsync('https://images7.alphacoders.com/116/1162253.jpg').then(img=>{
        console.log(img.src);
        setTimeout(()=>{
          ImgDOM.src=img.src
        },2000)
      }).catch(err=>{
        console.log(err);
      })
    </script>

Class 类

Class 是什么

      //1.认识 Class: 人类:类
      //具体的人:实例,对象
      //类可以看做是对象的模板,用一个类可以创建出许多不同的对象
      //2.Class 的基本用法: 类名一般首字母大写
      // class Person() {} 错的
      // class Person {}; 错的
      class Person {
        //实例化时执行构造方法,所以必须有构造方法
        constructor(name, age) {
          //建议还是写出来
          console.log("实例化时执行构造方法");
          this.name = name;
          this.age = age;
          // 一般在构造方法中定义属性,方法不在构造方法中定义
          // this.speak = () => {};
        }
        //各实例共享的方法[所有方法写在这就可以了]
        speak() {
          console.log("speak");
        }
      }
      const zs=new Person('ZS',18);
      console.log(zs.name);
      zs.speak();//speak
      console.log(zs.speak===ls.speak);//true
      //3.Class 与构造函数
      console.log(typeof Person); //function
      console.log(Person.prototype.speak); //ƒ speak() {console.log('speak');}
      //(2)构造函数
      function Person(name, age) {
          this.name = name;
          this.age = age;
      }
      Person.prototype.speak=function (){};//方法

Class 的两种定义形式

     //1.声明形式[重点关注]
      class Person{
          constructor() {
          }
          speak(){}
      }
      //2.表达式形式[知道了解就行]
      const Person=function (){};
      const Person=class {
          constructor() {
              console.log('constructor');
          }
          speak(){}
      };
      new Person();
      (function (){
          console.log('func');
      })();
      //立即执行的类
      new (class {
        constructor() {
          console.log("constructor");
        }
      })();

实例属性、静态方法和静态属性

      //1.实例属性[重点关注]会用会写就可以了
      //方法就是值为函数的特殊属性
      class Person {
        age = 0; //默认值
        sex = "male";
        getSex = function () {
          return this.sex;
        };
        constructor(name, sex) {
          this.name = name;
          this.sex = sex;
          // this.age=18;↑
        }
      }
      const p1 = new Person("slf");
      console.log(p1.name);
      console.log(p1.age);
      //2.静态方法[重点关注]
      //类的方法
      class Person {
        constructor(name) {
          this.name = name;
        }
        speak() {
          console.log("speak");
          console.log(this);
        }
        //静态方法
        static speak() {
          console.log("人类可以说话");
          //this 指向类本身
          console.log(this); //Person
        }
      }
      const p2 = new Person("ald");
      p2.speak(); //speak
      Person.speak(); //人类可以说话
      //3.静态属性 [简单了解]知道怎么回事就行了
      //类的属性
      class Person {
        constructor(name) {
          this.name = name;
        }
        //不能这么写,目前只是提案,有兼容性问题
        // static version='1.0';
        static getVersion() {
          return "1.0";
        }
      }
      console.log(Person.getVersion());

私有属性和方法

      //1.为什么需要私有属性和方法
      //一般情况下,类的属性和方法都是公开的
      //公有的属性和方法可以被外界修改,造成意想不到的错误
      class Person {
          constructor(name) {
              this.name = name;
          }
          speak() {
              console.log('speak');
          }
          getName(){
              return this.name;
          }
      }
      const p1 = new Person('Alex');
      console.log(p.name);
      p.speak();
      //2.模拟私有属性和方法
      //2.1 '_' [下划线]开头表示私有
      class Person {
          constructor(name) {
              this._name = name;
          }
          speak() {
              console.log('speak');
          }
          getName(){
              return this._name;
          }
      }
      const p2=new Person('LDde');
      console.log(p.getName());//LDde
      //2.2 将私有属性和方法移出类,优点:彻底杜绝了外部访问的可能性
      (function () {
        let name = "";
        class Person {
          constructor(username) {
            //this.name = name;
            name = username;
          }
          speak() {
            console.log("speak");
          }
          getName() {
            return name;
          }
        }
        window.Person = Person;
      })();
      (function () {
        const p = new Person("alex");
        console.log(p.getName());
      })();

extends[重要]

    //1.子类继承父类
    class Person {
      constructor(name, sex) {
        this.name = name;
        this.sex = sex;
        this.say = function () {
          console.log("say");
        };
      }
      speak() {
        console.log("speak");
      }
      static speak() {
        console.log("static speak");
      }
    }
    Person.version = "1.0";
    class Programmer extends Person {
      constructor(name, sex) {
        super(name, sex); //指代父类的 constructor
      }
    }
    const zss = new Programmer("zs", "男");
    zss.say(); //say
    zss.speak(); //speak
    Programmer.speak(); //static speak
    console.log(Programmer.version); //1.0
    //2.改写继承的属性或方法
    class Programmer extends Person {
      constructor(name, sex, feature) {
        super(name, sex); //this 操作不能放在 super 前面
        this.feature = feature; //不能在 super 前面,是父类没有的
      }
      hi() {
        console.log("hi");
      }
      //同名覆盖
      speak() {
        console.log("Programmer speak");
      }
      static speak() {
        console.log("Programmer static speak");
      }
    }
    Programmer.version = "2.0";

super[简单了解]

 //1.作为函数调用
      //代表父类的构造方法,只能用在子类的构造方法中,用在其他地方就会报错
      //super 虽然代表了父类的构造方法,但是内部的 this 指向子类的实例
      class Person {
        constructor(name) {
          this.name = name;
          console.log(this); //指向子类的实例
        }
      }

      class Programmer extends Person {
        constructor(name, sex) {
          super(name, sex); //相当于调用父类的构造方法
        }
      }
      new Programmer();
      //2.作为对象使用
      //2.1 在构造方法中使用或一般方法中使用
      //super 代表父类的原型对象 Person.prototype, 所以定义在父类实例上的方法或属性(this.),是无法通过 super 调用的
      //通过 super 调用父类的方法时,方法内部的 this 指向当前的子类实例
      class Person {
        constructor(name) {
          this.name = name;
          console.log(this);
        }
        speak() {
          console.log("speak");
          // console.log(this);
        }
        static speak() {
          console.log("Person speak");
          console.log(this); //指向子类
        }
      }

      class Programmer extends Person {
        constructor(name, sex) {
          super(name, sex); //相当于调用父类的构造方法

          // console.log(super.name);undefined
          // super.speak(); //speak
        }
        speak() {
          super.speak();
          console.log("Programmer speak");
        }
        //2.2 在静态方法中使用
        //指向父类,而不是父类的原型对象
        //通过 super 调用父类的方法时,方法内部的 this 指向当前的子类,而不是子类的实例
        static speak() {
          super.speak();
          console.log("Programmer speak");
        }
      }
      // new Programmer();
      Programmer.speak();
      //3.注意事项
      //使用 super 的时候,必须显式指定是作为函数还是作为对象使用,否则会报错
      class Person {
        constructor(name) {
          this.name = name;
        }
        speak() {
          console.log("speak");
        }
      }
      class Programmer extends Person {
        constructor(name, sex) {
          super(name, sex);
          console.log(super());
          console.log(super.speak);
        }
      }

Class 的应用 [键盘左右切换的幻灯片]

<link rel="stylesheet" href="./slider.css" />
    <div class="slider-layout">
      <div class="slider">
        <div class="slider-content">
          <div class="slider-item">
            <a href="javascript:;"
              ><img src="./imgs/1.jpg" alt="1" class="slider-img"
            /></a>
          </div>
        </div>
      </div>
    </div>
/* css reset */
* {
  padding: 0;
  margin: 0;
}
a {
  text-decoration: none;
  outline: none;
}
img {
  vertical-align: top;
}

/* layout */
.slider-layout {
  width: 80%;
  height: 420px;
  margin: 0 auto;
}

/* slider */
.slider,
.slider-content,
.slider-item,
.slider-img {
  width: 100%;
  height: 100%;
}
.slider {
  overflow: hidden;
}
.slider-item {
  float: left;
}
.slider-animation {
  transition-property: transform;
  transition-duration: 0ms;
}
  <script src="./base.js"></script>
    <script>
      console.log(BaseSlider);
      class Slider extends BaseSlider {
        constructor(el, options) {
          super(el, options);

          this._bindEvent();
        }

        _bindEvent() {
          document.addEventListener("keyup", (ev) => {
            // console.log(ev.keyCode);
            if (ev.keyCode === 37) {
              // ←
              this.prev();
            } else if (ev.keyCode === 39) {
              // →
              this.next();
            }
          });
        }
      }
      new Slider(document.querySelector(".slider"), {
        initialIndex: 1,
        animation: true,
        speed: 1000,
      });
    </script>
  • base.js
// 默认参数
const DEFAULTS = {
    // 初始索引
    initialIndex: 0,
    // 切换时是否有动画
    animation: true,
    // 切换速度,单位 ms
    speed: 300
};
// base
const ELEMENT_NODE = 1;
const SLIDER_ANIMATION_CLASSNAME = 'slider-animation';

class BaseSlider {
    constructor(el, options) {
        console.log(options)
        if (el.nodeType !== ELEMENT_NODE)
            throw new Error('实例化的时候,请传入 DOM 元素!');

        // 实际参数
        this.options = {
            ...DEFAULTS,
            ...options
        };

        const slider = el;
        const sliderContent = slider.querySelector('.slider-content');
        const sliderItems = sliderContent.querySelectorAll('.slider-item');

        // 添加到 this 上,为了在方法中使用
        this.slider = slider;
        this.sliderContent = sliderContent;
        this.sliderItems = sliderItems;

        this.minIndex = 0;
        this.maxIndex = sliderItems.length - 1;
        this.currIndex = this.getCorrectedIndex(this.options.initialIndex);

        // 每个 slider-item 的宽度(每次移动的距离)
        this.itemWidth = sliderItems[0].offsetWidth;

        this.init();
    }

    // 获取修正后的索引值
    // 随心所欲,不逾矩
    getCorrectedIndex(index) {
        if (index < this.minIndex) return this.maxIndex;
        if (index > this.maxIndex) return this.minIndex;
        return index;
    }

    // 初始化
    init() {
        // 为每个 slider-item 设置宽度
        this.setItemsWidth();

        // 为 slider-content 设置宽度
        this.setContentWidth();

        // 切换到初始索引 initialIndex
        this.move(this.getDistance());

        // 开启动画
        if (this.options.animation) {
            this.openAnimation();
        }
    }

    // 为每个 slider-item 设置宽度
    setItemsWidth() {
        for (const item of this.sliderItems) {
            item.style.width = `${this.itemWidth}px`;
        }
    }

    // 为 slider-content 设置宽度
    setContentWidth() {
        this.sliderContent.style.width = `${
      this.itemWidth * this.sliderItems.length
    }px`;
    }

    // 不带动画的移动
    move(distance) {
        this.sliderContent.style.transform = `translate3d(${distance}px, 0px, 0px)`;
    }

    // 带动画的移动
    moveWithAnimation(distance) {
        this.setAnimationSpeed(this.options.speed);
        this.move(distance);
    }

    // 设置切换动画速度
    setAnimationSpeed(speed) {
        this.sliderContent.style.transitionDuration = `${speed}ms`;
    }

    // 获取要移动的距离
    getDistance(index = this.currIndex) {
        return -this.itemWidth * index;
    }

    // 开启动画
    openAnimation() {
        this.sliderContent.classList.add(SLIDER_ANIMATION_CLASSNAME);
    }

    // 关闭动画
    closeAnimation() {
        this.setAnimationSpeed(0);
    }

    // 切换到 index 索引对应的幻灯片
    to(index) {
        index = this.getCorrectedIndex(index);
        if (this.currIndex === index) return;

        this.currIndex = index;
        const distance = this.getDistance();

        if (this.options.animation) {
            return this.moveWithAnimation(distance);
        } else {
            return this.move(distance);
        }
    }

    // 切换上一张
    prev() {
        this.to(this.currIndex - 1);
    }

    // 切换下一张
    next() {
        this.to(this.currIndex + 1);
    }

    // 获取当前索引
    getCurrIndex() {
        return this.currIndex;
    }
}
喜欢 (0)
[]
分享 (0)
发表我的评论
取消评论
表情 贴图 加粗 删除线 居中 斜体 签到

Hi,您需要填写昵称和邮箱!

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址
已稳定运行:3年255天5小时17分