行使 Proxy + Promise 达成 依赖收集

从互连网发展进度看 —— 实时通信和网络交叉融合所推动的更动

但事实上,始建于上世纪 60
时代的网络本身并非为“实时”所设计,受限于当时的应用场景和技能,再加上差别国家、运维商之间人为创立的烟幕弹,通讯技术在实时传输、品质担保等各方面都可谓壮志未酬。

也正因如此,从网络诞生之日起,一代又一时半刻的技术人便在对通讯技术拓展不断地翻新升高。1986年,还在南美洲粒子钻探中央(CE索罗德N)的
Tim Berners-Lee 研制出了三项突破性的数字通信技术:可用以排列文本文件的
HTML 语言、连接文件的 HTTP 系统以及用于对格外节点消息举办一定的
ULANDL。那三项创新改变了整整通讯系统,使得消息能够更便于地通过微型总括机互连网。而在壹玖玖叁年,Berners-Lee
更是建立起万维网结盟(World Wide Web Consortium,简称 W3C),负责 Web
相关规范的制订。浏览器的普及和 W3C 的推进,使得 Web
上得以访问的能源逐日拉长起来,可是此时 Web
的显要通讯依旧浏览器向服务器请求静态 HTML 音信。

 

图片 1

 

可是,同在 1994 年,CGI(Common Gateway
Interface,通用网关接口)的产出推动了 Web 上动态音讯服务的勃勃兴起。CGI
定义了 Web 服务器与表面应用程序之间的通讯接口标准,Web 服务器能够经过
CGI 执行外部程序,让外部程序依照 Web 请求内容变更动态的始末。

 

图片 2

 

到了 壹玖玖肆 年,NetScape
公司统一筹划的 JavaScript 被当作浏览器上运维脚本语言为网页扩大动态性,不仅能够做出丰盛酷的页面动态效果,还足以减小与劳动器端的通讯支出,而十年后,也正是二零零七 年,当 谷歌(Google) 的隆起掀开了 Web 2.0 的大幕,应运而生的 AJAX
更使得 javascript 再度大放异彩。

咱俩精通,在 Web 应用中,用户提交表单时就向 Web
服务器发送一个请求,服务器进行接收处理,并赶回二个新的网页,前后五个页面中的当先六分之三HTML 代码往往是同一的,由此也就导致了回到时带宽能源的荒废。而 AJAX
应用仅向服务器发送并赶回须求的数额,且在客户端选取 JavaScript
处理来自服务器的响应,更新页面包车型地铁一对消息。那样不但让浏览器和服务器的数据交流大大减弱,且客户端也得以更飞速地响应用户操作。

 

图片 3

 

而到了运动网络时代,通信技术条件也就改成了大功告成的自然现象。在《苹果终于进入
WebEvoqueTC,新一代移动 Web
应用产生路上还有哪些坑?
》一文中,大家曾谈到的
Web中华VTC 标准正是卓绝案例之一。

在 二〇一二年在此从前,浏览器之间要想达成实时通讯,须求个人技术,在这之中山大学部分都以通过插件和客户端来设置使用。对于众多用户而言,插件的下载、安装和创新是二个繁杂、繁琐和不难出错的操作。而对此开发人士来说,插件的调节和测试、测试、布署、错误修复和珍视同样困难重重,且不提还关系到某些受版权拥戴的技能,整合一定复杂。再者,很多时候,服务提供商很难说服用户去安装插件。

但这一多头步履蹒跚还不讨好的范畴就这么被 谷歌(Google) 将 WebXC60TC
项目开源所打破。二〇一一 年,WebEnclaveTC 基于 BSD 磋商开源,同年,W3C 运转Web路虎极光TC 布置,让 Web奥德赛TC
成为了 HTML5 标准的一部分(方今,该标准还在开发中)。

一派,在活动互连网创业余大学潮涌动之时,不少创业者精选从移动 SDK
切入,将实时通讯工具化,开发者及团伙无需兼顾实时通讯背后繁琐的技艺原理与逻辑落成,只需在运用开发中融合为一相应的
SDK 即可轻松达成实时通讯成效。那上头的代表性集团可知声网
Agora.io,其提供了二个极简 SDK,让开发者接入 SD-CR-VTN™
实时虚拟通讯网,在别的 App
和网站完毕高质量的音频通话、录像通话、全互动直播。

再者,随着互联网基础设备形成、硬件配件发展成熟,以及 4G、Wi-Fi
的普及,用户开始对更拉长的成效、场景有了越来越多的须求。譬如在当前事在人为智能隆重之时,诸多智能设备都集成了实时音录像的功力,前文提到的红米AI 音箱就是当中之一。

对此,声网 Agora.io 经理 赵斌那样总计道:

中原网络发展高速,基础云服务、开源技术、html5、移动
SDK 等技巧,让中华夏族民共和国的开发者能最急忙地付出移动和网页
App,与世界伤官。下三个风口,一定会是融合了实时通讯技术的运用。

  

2017
已过大半,从年终盛起的《王者荣耀》、《狼人杀》却照旧是最激烈的娱乐产品,其一同特点都在于集成了实时语音成效,前者左手走位右手技能,语音自然也就改成了那几个供给的品质,而后者更毫不说,本便是纯粹依靠实时语音进行下去的游戏。

⑤ 、Promise 3次立异

 

大家改一下 onDepUpdated 函数

  

"type": {

computer(target){
                return target.health > 4000 ? '坦克' : '脆皮';
},
onDepUpdated(val){
            new Promise(a=>a()).then(a=> {console.log(`我的类型是:${val}`);});
              }

},

 

 

咱俩来跑一下:

console.log(`英雄初始类型:${heroxy.type}`) 
heroxy.health = 5000
heroxy.health = 100

 

 

英雄初始类型:脆皮
-> 我的health属性从3000 变为 5000
-> 我的health属性从5000 变为 100
-> 我的类型是:坦克
-> 我的类型是:脆皮

 

 

确实,更新操作时最终执行了,可是照旧履行了四回,我们能够操纵,只进行最后一次,能够那样改:

日增全局变量

const Dep = {
         target: null,
         UpdateIndex:0
}

 

 

创新方法新增二个参数,并且操作从前做判断,

     

onDepUpdated(val,updateIndex){
           new Promise(a=>a()).then(a=> {
                   if(updateIndex == Dep.UpdateIndex){
                         console.log(`我的类型是:${val}`);
                           Dep.UpdateIndex = 0 ;//记住操作完要把全局变量更新回去
                               }
                     } );
}

 

修改get方法:

 

get (target, key, receiver) {
          const _com = computerDict[key];
          if(_com){

                Dep.target = (updateIndex)=> { _com.onDepUpdated(_com.computer(heroxy),Dep.UpdateIndex); 
        };//传入参数
       const _val = _com.computer(heroxy);
      Dep.target = null;
      // _com.onDepUpdated(_val);
      return _val ;

           }
        const _pro = proDict[key];
      if(_pro){
       if (Dep.target && _pro.indexOf(Dep.target) === -1) {
               _pro.push(Dep.target)
            }

         }
return Reflect.get(target, key, receiver);
}

 

 

 

修改set 方法:

 1 set (target, key, value, receiver) {
 2                 const _pro = proDict[key];
 3                if(_pro){
 4  
 5                           console.log(`我的${key}属性从${target[key]} 变为 ${value}`);
 6                      Reflect.set(target, key, value, receiver);
 7                       _pro.forEach((dep) =>{
 8                              Dep.UpdateIndex ++ ;//新增标记
 9                               dep(Dep.UpdateIndex);
10                    });
11                    return true ;
12                  }
13                  const _com = computerDict[key];
14                    if(_com){
15                           console.error('计算属性无法被赋值!')
16                       }
17                     return Reflect.set(target, key, value, receiver);
18 }
19  

 

重复履行:

1 英雄初始类型:脆皮
2 我的health属性从3000 变为 5000
3 我的health属性从5000 变为 100
4 我的类型是:脆皮

 

 

功勋卓著告成,这么些便是大家要的结果。

 

富有代码以下,优化就不做了…..

 

 1 const hero = {
 2   health: 3000,
 3   IQ: 150
 4 }
 5 
 6 
 7 const computerDict = {
 8 
 9      "type": {
10 
11            computer(target){
12                 return target.health > 4000 ? '坦克' : '脆皮';
13            },
14            onDepUpdated(val,updateIndex){
15                 new Promise(a=>a()).then(a=> {
16                     if(updateIndex == Dep.UpdateIndex){
17                         Dep.UpdateIndex = 0 ;
18                        console.log(`我的类型是:${val}`);
19                     }
20                     });               
21            }
22 
23       },
24 }
25 
26 
27 const  proDict = {
28     "health":[],
29     "IQ":[]
30 }
31 
32 const Dep = {
33   target: null,
34   UpdateIndex:0 
35 }
36 
37 
38 const heroxy = new Proxy(hero,{
39       set (target, key, value, receiver) {
40         const _pro = proDict[key];
41         if(_pro){
42 
43             console.log(`我的${key}属性从${target[key]} 变为 ${value}`);
44             Reflect.set(target, key, value, receiver);
45              _pro.forEach((dep) =>{
46                 Dep.UpdateIndex ++ ;
47                  dep(Dep.UpdateIndex);
48              });
49              return true ;
50         }
51         const _com = computerDict[key];
52         if(_com){
53             console.error('计算属性无法被赋值!')
54         }       
55          return Reflect.set(target, key, value, receiver);
56       },
57 
58      
59        get (target, key, receiver) {
60            const _com = computerDict[key];
61            if(_com){ 
62             
63             Dep.target = (updateIndex)=> {_com.onDepUpdated(_com.computer(heroxy),Dep.UpdateIndex); };
64             const _val = _com.computer(heroxy); 
65             Dep.target = null;
66            // _com.onDepUpdated(_val);
67            return _val ;
68 
69            }
70            const _pro = proDict[key];
71            if(_pro){
72             if (Dep.target && _pro.indexOf(Dep.target) === -1) {
73                 _pro.push(Dep.target)
74                }
75             
76            }
77            return  Reflect.get(target, key, receiver);   
78        }
79   });

 参考链接:

          (深切浅出Vue基于“信赖收集”的响应式原理)
https://zhuanlan.zhihu.com/p/29318017

        http://es6.ruanyifeng.com/#docs/proxy)

 Aspect Oriented Programming(AOP)
框架https://baike.baidu.com/item/AOP/1332219?fr=aladdin

https://blog.cloudboost.io/reactivity-in-vue-js-2-vs-vue-js-3-dcdd0728dcdf

https://esdiscuss.org/topic/an-update-on-object-observe

EventLoop (http://www.ruanyifeng.com/blog/2014/10/event-loop.html

   

 

那正是说,在风口之上,实时通讯还留存怎样技术难题尚待完全占领?

接下去,我们开始展览具体分析。

  • 互连网传输:现存的互连网作为冷战时代的产物最早其实是为了用于保险United States通讯互连网,其在网络传输方面包车型客车各样局限也直接促成了现在的互连网在大文件传输、实时传输方面包车型大巴窒碍难行。而语/录像通讯、直播连麦对实时性需求丰裕高,供给延迟低至几百阿秒,由此,现存的互连网并无法满意那种新型的实时应用场景。

  • 编解码:观念的编解码算法,也非应用于复杂的互连网实时情况的良选,就导致卡顿、模糊等不可用的景色爆发。

  • 硬件适配:在音摄像通话中,除了延迟,还有三个严重影响用户体验的题材
    ——
    回声。所谓“回声”,便是指本身的声响传到远端再经过远端的Mike风录音传回来。大家必要通过信号处清理计算法来开始展览回声消除,但由于手机的音量控制是非线性的,分歧的手提式有线电话机材料、手提式有线电话机壳会招致声音传导性有差别,设备项目差距导致算法不可能普适。且Android 手提式无线电电话机碎片化严重,也就径直促成了运动端适配工作量庞杂。

  • QoE 品质保持: 来自北欧的实时通讯数据测试集团 Callstats.io
    曾分享过欧洲和美洲市场实时通讯行业现状的调查商讨数据,基于公网的 Web锐界TC
    通话中有 16%
    通话品质不行接受。而实际境况中,类似东南亚、中东这么些基本建设不鼎盛地区会倒霉得多。怎么样保持
    本田UR-VTC 服务的高连通性、高质量,也就改为了 奇骏TC 领域的一大技术难点。

行使 ES6 Proxy
来落到实处响应式监听,其能够简化源代码、易于学习,并且还能够带来更好地品质表现。(https://blog.cloudboost.io/reactivity-in-vue-js-2-vs-vue-js-3-

想要从根源上消除这几个难点,还亟需先对 陆风X8TC 整个技术栈做个精通。

EvoqueTC
从功能流程上来讲,包涵了采访、编码、前后处理、传输、解码、缓冲、渲染等众多环节,下图是一个牧马人TC
通信的总结流程,每三个分叉环节还有更细分的技术模块。比如在上下处理环节,有美颜、滤镜、回声化解、噪声抑制等,采集有话筒阵列等,编解码有
VP八 、VP玖 、H.264 等。

 

图片 4

 

在此处,来自声网的技艺术专科高校家分享了她们的推行:

  • 透过专为内容实时传输而布置的互联网架构 SD-奥迪Q7TN
    消除互联网传输难题。
    在网络上分歧地段的数码基本放置软件组网单元,互相连接相互调度,在存活的公共互连网基础上创设一层新的虚拟网络;
  • 针对互连网信道的实时一对① 、多个人电视发表设计了尤其的个人编解码,以适应互连网丢包、抖动、延迟等难题;

 

图片 5

 

  • 将“免”适配和适配相互协作,依靠线上多少的报告,判断“免”的功力;
  • 依照大数目开发了可供开发者 7*24
    查看的“实时数据监察和控制平台”。开发者可以查阅的各种用户的通话质量情状,包罗网路分布、设备分布、质量分布、通话品质、接通率、通话分钟数等。

值得一提的还有,不少开发者直接将 HavalTC 和 Web凯雷德TC
划上了等号。实际上,Web瑞虎TC 是 谷歌(Google)的1个特地针对网页实时通讯的规范及开源项目,只提供了根基的前端功用达成,包罗编码解码和震动缓冲等,开发者若要基于
Web哈弗TC
开发商用项目,供给活动做服务端达成和布署,信令前后端选型完毕安顿,以及手提式有线电话机适配等一多如牛毛具体做事。除外,还要在可用性和高品质方面展开大批量的校对和打磨,对本身费用力量的门道须要相当高。而3个正式的
本田CR-VTC
技术劳务类别,除了饱含上述的通讯环节外,实际上还要求有化解互连网不安定的专用通讯互连网,以及针对性网络信道的高容忍度的音摄像信号处清理计算法。当然,常规云服务的高可用、服务质量的有限支撑和监理维护理工科人具都只好算是3个正规服务商的着力模块。

    “ Proxy
用于修改某个操作的私下认可行为,等同于在语言层面做出修改,所以属于一种“元编制程序”(meta
programming),即对编程语言实行编制程序。

而从娱乐到直播、在线教育/医疗以及 VR/A昂科威、AI
等网络垂直行业及更新技术,那样的例证还有为数不少。比如转型做直播的陌陌在新式的
8.0
版本中出产了“快聊”、“狼人杀”、“派对”等实时摄像社交玩法;中兴在新揭橥智能喇叭中也集成了实时语音云服务。随着网络服务越来越廉价易得,诸如互联网电话、录像通话、全互动直播等实时气象已然成为用户的广大供给,越来越多的规模化应用基于使用形式及气象集成了实时音录像功用,“实时”简直已是网络最热的标签词之一。

dcdd0728dcdf)。

那么,当实时通讯无处不在之时,我们该怎么做?

当各式智能硬件、移动选择以及 Web App
中的许多模块都更加正视于音录制技术,实时通讯已然成为了具备行业的第一次全国代表大会基础设备,不仅仅是在直播、游戏那些泛娱乐行业,更渗透到在线医疗、教育、金融等世界。在分歧情状下,推动着众人联系互动方式的变更。

唯独,便是那样一个已与各样垂直行业开始展览深度融合须求巨大的技巧领域,却照旧贫乏大旨技术高端人才。大切诺基TC
宗旨技术最急需的是通讯工程相关标准的相貌,而那个专业的应届毕业生在此在此以前就业一般集中于Samsung、爱立信等观念通讯行业厂商,也不有所
普拉多TC
的经验,一般都以办事后一回学习。开发者须要对实时通讯有更深层次的精晓,建立起
途达TC 技术系统,帮忙本身在依次行当开拓立异的或然。


说到底,对于想要进入 昂科威TC 领域的开发者,推荐即将于 9 月 21 -11日在法国首都市万豪旅舍进行的 OdysseyTC 2017
实时互连网大会
,首要有两点:一是在乎集结了实时通讯世界十三分重量级的大牛,比如
Web奔驰M级TC 标准之父、IETF 的加入者 丹尼尔勒 C.
Burnett,还有来自谷歌、声网、Slack、Houseparty、Atlaissian、陌陌、花椒、杜洞尕等营业所的技术专家,能够在实地得到实时通讯最新的一贯接帮衬源,同时也与讲者们开始展览更透彻的沟通沟通。其次,这一议会的分会场安装完全围绕
帕杰罗TC
技术栈来,从尾部到前端,从架构到编解码,从一举手一投足支付到行业技术实施,能够支持全体想要学习
帕杰罗TC 的开发者建立起学习架构种类。

 

http://blog.csdn.net/Byeweiyang/article/details/77164940

壹 、使数据对象变得“可观看”

    大家把原对象变成了可寓指标代办对象 heroxy

     

1 const heroxy = new Proxy(hero,{
2            set (target, key, value, receiver) {
3                    console.log(`我的${key}属性从$(target[key]) 变为 $(value)`);
4                    return Reflect.set(target, key, value, receiver);//同样也是ES6新特性,比Object.defineProperty 更强大,更适合元编程,大家顺便一起学一下
5             }
6         };

 

大家来执行下:

1      heroxy.health = 5000;
2       heroxy.IQ =  200;
3 //->  我的health属性从3000 变为 5000
4 //->  我的200属性从150变为 200

代码确实简洁

 

而Vue.js 2.0 的响应式首要依靠
Object.defineProperty,其具备较好地浏览器包容性,不过其不可能直接监听数组下标格局改变以及动态增加的性质;而
Vue.js 3 中则布置

那是三个王者荣耀里的身先士卒:

属于ES5的特色,而ES6 带来了Proxy特性。那里先介绍一下:

那边顺便提一下,作者以为“信赖收集”的极限方案应该是ES7 的
Object.observe,可是被提倡人团结打消了。

动用场景:

上面通过不难的代码我们用 Proxy
来落到实处“正视收集”的主导原理,为了让大家更好掌握,笔者举了跟Object.defineProperty
相同的例证:

肆 、自动更新的难点…..

 

当大家连年设置

hero.health = 5000
hero.health = 100

 

的时候,health应该要以最终三次设置,也正是100为准,hero.health = 5000

那时不该触发变更事件,视图不该绘制,太浪费质量了。

然则大家又不可能手动调用更新,不然就太不“响应式了”。

小编们又想自动更新,又想把募集到的借助只做2回最后的批量更新,如何是好吧?

答案是
EventLoop 

简易说一下,大家得以把立异操作放到队列之中去,当大家主线程执行完的时候,才再次来到

调用队列之中的形式,ajax callback ,settimeout 就是那样干的,当然 promise
也是。

 

的本心是代理,用在此地球表面示由它来“代理”某个操作,能够译为“代理器”。 “

看来那一个让自家想起了几年前作者看过的影象深远的一句话“
框架是语法的互补”(网上检索了下,居然找不到出处了),看起来, Javascript
未来居然自带Aspect
Oriented

Programming(AOP)
框架了。

② 、总计属性

 

 1 const heroxy = new Proxy(hero,{
 2         set (target, key, value, receiver) {
 3        console.log(`我的${key}属性从$(target[key]) 变为 $(value)`);
 4       return Reflect.set(target, key, value, receiver);
 5         }
 6  
 7       get (target, key, receiver) {
 8  
 9           if(key == "type"){
10  
11             const _val = target.health > 4000 ? '坦克' : '脆皮';
12              console.log(`我的类型是:${val}`);
13             return _val ;
14           }
15            else{
16               Reflect.get(target, key, receiver);
17              }
18           }
19 };

 

 

 

大家来实行下:

1 heroxy.health = 5000
2 heroxy.IQ = 200
3 heroxy.type
4 //-> 我的health属性从3000 变为 5000
5 //-> 我的200属性从150变为 200
6 //-> 坦克
7  

 

由此地点五个例子,基本了解Proxy的用法了,接下去是依赖收集的基本,可是在那几个此前大家先改下代码,首先定义两个全局字典:

 

const computerDict = {

"type": {

       computer(target){
                 return target.health > 4000 ? '坦克' : '脆皮';
       },
     onDepUpdated(val){
              console.log(`我的类型是:${val}`);
       }
        },
}


const proDict = {
         "health":[] , //为什么要定义集合,下面再说
         "IQ":[]
}

 

 

修改Proxy

 1 const heroxy = new Proxy(hero,{
 2             set (target, key, value, receiver) {
 3                       const _pro = proDict[key];
 4                      if(_pro){
 5  
 6                          console.log(`我的${key}属性从$(target[key]) 变
 7                           为 $(value)`);
 8                         
 9                        }
10                      const _com = computerDict[key];
11                       if(_com){
12                         console.error('计算属性无法被赋值!')
13                        }
14                      return Reflect.set(target, key, value, receiver);
15         },
16  
17  
18           get (target, key, receiver) {
19               const _com = computerDict[key];
20               if(_com){
21                          const _val = _com.computer(target);
22                       _com.onDepUpdated(_val);
23                         return _val ;
24  
25                    }
26            else
27           {
28  
29                         return Reflect.get(target, key, receiver);
30            }
31         }
32 });            

 

 

这么我们代码变得特别注解式,更易于扩张

 

        当自家 设置 /变更 hero.health 的值,会触发 hero.type
这些计算属性爆发变化,而在vue里面就是导致视图的换代。

Proxy 达成动态代理类似于middleware(中间件),能够对 对象
的基本操作进行联合处理,很合乎完成 “依赖收集“。

Proxy
能够知道成,在对象对象在此之前架设一层“拦截”,外界对该目的的访问,都必须先通过那层拦截,由此提供了一种机制,能够对外边的拜会举行过滤和改写。Proxy
这些词

   
 (开头Vue基于“正视收集”的响应式原理)
,那篇小说讲的是透过三个通俗易懂例子,介绍了 怎么样用Object.defineProperty
达成的“信赖收集”的法则。Object.defineProperty

 const hero = {
                  health: 3000,
                  IQ: 150
                }

三 、重视收集

 

同样的 ,定义一个依靠收集器

1 /**
2 * 定义一个“依赖收集器”
3 */
4 const Dep = {
5          target: null
6 }

 

当大家先是次调用计量属性的时候,改写get 部分:

 

get (target, key, receiver) {
          const _com = computerDict[key];
         if(_com){

           Dep.target = _com.onDepUpdated ;
           const _val = _com.computer(target);
            Dep.target = null;//临时存放一下而已
            / / _com.onDepUpdated(_val);
            return _val ;

        }
       else
       {
             return Reflect.get(target, key, receiver);
          }
}

 

在它的回调函数中,调用了大胆的health属性,也正是触发了对应的getter函数

再而三修改get部分:

 1 get (target, key, receiver) {
 2         const _com = computerDict[key];
 3        if(_com){
 4  
 5             Dep.target = ()=> {_com.onDepUpdated(_com.computer(heroxy)); };//这里要用heroxy
 6             const _val = _com.computer(target);
 7             Dep.target = null;
 8             // _com.onDepUpdated(_val);
 9               return _val ;
10  
11             }
12           const _pro = proDict[key];
13           if(_pro){
14               if (Dep.target && _pro.indexOf(Dep.target) === -1) {
15             _pro.push(Dep.target);//明白了 proDict[key]定义为数组就是为了存放触发的函数集合
16 
17                    }
18  
19             }
20                    return Reflect.get(target, key, receiver);
21     }

 

最后修改set 部分:

 

 1 set (target, key, value, receiver) {
 2             const _pro = proDict[key];
 3          if(_pro){
 4  
 5                   console.log(`我的${key}属性从${target[key]} 变为 ${value}`);
 6                Reflect.set(target, key, value, receiver);
 7               _pro.forEach((dep) =>{
 8                       dep();
 9                   });
10                   return true ;
11          }
12           const _com = computerDict[key];
13         if(_com){
14                    console.error('计算属性无法被赋值!')
15             }
16          return Reflect.set(target, key, value, receiver);
17 },

 

 

大家来跑一下:

console.log(`英雄初始类型:${hero.type}`) hero.health = 5000
hero.health = 100

->英雄初始类型:脆皮
->我的health属性从100 变为 5000
->我的类型是:坦克
->我的health属性从5000 变为 100
->我的类型是:脆皮

 

 

诚然达到了笔者们预料的机能……

 

相关文章

发表评论

电子邮件地址不会被公开。 必填项已用*标注

*
*
Website