首页 文章 精选 留言 我的

精选列表

搜索[前端统一],共10000篇文章
优秀的个人博客,低调大师

我在阿里云做前端代码智能化

作为一个整天以代码为伴的码农,避免不了会接触到各种代码提示工具,但是呢,用久了之后会发现他们都有个共同点,那就是模型巨大,动辄几百兆;并且模型大必然需要更多的计算,同样会导致电脑内存占用高,风扇呼呼的转,时间久了逐渐会发现电脑存储不够用了,电脑变卡了等等问题。 那么,有没有一款轻量化的代码提示插件?或者说,如何实现一款轻量化的代码提示插件呢? 下面我会从 模型选择、模型实现、模型优化三个方面 来介绍我们在代码智能提示方面的一些实践。 模型选择 如何衡量一个模型的效果是好还是不好呢? 我们首先建立了基本的模型衡量体系,见下图。 我们会从推荐的准确性、完整性、连续性、性能、个性化以及智能性六个维度去综合评价一个模型的优劣。之后,我们会进一步完善这个体系,使之能够系统完整的评测模型的效果。 实践中,我们选取了 GPT-2模型和基于markov的n-gram统计模型进行对比。 对比发现,GPT-2模型在准确性和完整性方面表现优异,但是在性能方面,由于模型较大,推荐一次耗时较久,(这里我们试了最少参数的版本,训练之后模型在500M左右,推荐一次大概需要10S),由于暂时没找到模型压缩的方法,只能暂时放弃。 另一个n-gram模型,在测试后发现,它在持续推荐和性能方面表现优异,模型大小仅有40M,但也并非完美,在准确性和完整性方面表现的不是很好。 在实践中,我们发现,推荐耗时在毫秒级别能够使用户顺畅无阻碍的编写下去,多于1s,则会让用户的输入产生停顿。 基于此我们暂时选取n-gram作为我们的推荐模型。 模型实现 下面介绍一下n-gram模型的基本原理以及我们的实现。 首先,n-gram模型基于马尔可夫链的假设,即:当前这个词出现的概率仅仅跟前面几个有限的词相关。 以最简单的n=2为例,即 下一个词出现的概率仅跟之前一个词相关,基于这个思想,我们将大量代码进行切分,这样我们得到了很多的二元组,这里可以使用单词的出现次数代表概率。这样,我们根据一个词就可以得到一个不同概率分布的推荐列表,然后每次都以当前词进行推荐,就可以产生持续不断的推荐了。切分的效果参考这张图 以下是模型训练和产生推荐的大致流程。 模型优化 在真实应用中,仅仅有这些是远远不够的。模型只是其中一部分,更多的应该是在应用过程中,依据实践经验和用户的反馈,对模型进行不断调优: 比如,在实际应用中,基于同一个上下文,我们一般会同时产生基于n=3,n=4等等不通粒度的推荐,我们会给不通粒度赋予不同的权重,然后结合概率进行筛选和排序,并且根据实际效果以及我们的反馈体系,对权重进行断优化,确保产生最佳效果的推荐。 另外,为了让推荐更加个性化,我们的模型需要具备实时训练的能力,即用户的输入在产生推荐的同时,还要参与模型的增量训练,并根据用户实际的选择对模型进行调优,确保模型的准确性和个性化,大致流程图如下 还有,为了得到更加轻量的模型,我们对语料库也进行了精简,比如,在初始模型中,我们对出现概率极低的语料进行了删减,保持模型的轻巧和高效,同时由于具备实时训练的能力,确保这个操作不会影响到推荐的准确率。 结语 当然,目前的这些对于一个轻巧好用的代码提示来说是远远不够的,当前我们正在尝试完善模型的衡量体系,并且在尝试更多轻量的语言模型,希望给用户提供完美的体验。 原文链接 本文为阿里云原创内容,未经允许不得转载。

优秀的个人博客,低调大师

前端阿里面试—— 实现一个深拷贝

前言 深拷贝这个功能在开发中经常使用到,特别在对引用类型的数据进行操作时,一般会先深拷贝一份赋值给一个变量,然后在对其操作,防止影响到其它使用该数据的地方。 如何实现一个深拷贝,在面试中出现频率一直居高不下。因为在实现一个深拷贝过程中,可以看出应聘者很多方面的能力。 本专栏将从青铜到王者来介绍怎么实现一个深拷贝,以及每个段位对应的能力。 青铜段位 JSON.parse(JSON.stringify(data)) 复制代码 这种写法非常简单,而且可以应对大部分的应用场景,但是它有很大缺陷的。如果你不知道它有那些缺陷,而且这种实现方法体现不出你任何能力,所以这种实现方法处于青铜段位。 如果对象中存在循环引用的情况也无法正确实现深拷贝。 const a = { b: 1, } a.c = a; JSON.parse(JSON.stringify(a)); 如果data里面有时间对象,则JSON.stringify后再JSON.parse的结果,时间将只是字符串的形式。而不是时间对象。 const a = { b: new Date(1536627600000), } console.log(JSON.parse(JSON.stringify(a))) 如果data里有RegExp、Error对象,则序列化的结果将只得到空对象; const a = { b: new RegExp(/\d/), c: new Error('错误') } console.log(JSON.parse(JSON.stringify(a))) 如果data里有函数,undefined,则序列化的结果会把函数置为undefined或丢失; const a = { b: function (){ console.log(1) }, c:1, d:undefined } console.log(JSON.parse(JSON.stringify(a))) 如果data里有NaN、Infinity和-Infinity,则序列化的结果会变成null const a = { b: NaN, c: 1.7976931348623157E+10308, d: -1.7976931348623157E+10308, } console.log(JSON.parse(JSON.stringify(a))) 白银段位 深拷贝的核心就是对引用类型的数据的拷贝处理。 function deepClone(target){ if(target !== null && typeof target === 'object'){ let result = {} for (let k in target){ if (target.hasOwnProperty(k)) { result[k] = deepClone(target[k]) } } return result; }else{ return target; } } 以上代码中,deepClone函数的参数target是要深拷贝的数据。 执行target !== null && typeof target === 'object'判断target是不是引用类型。 若不是,直接返回target。 若是,创建一个变量result作为深拷贝的结果,遍历target,执行deepClone(target[k])把target每个属性的值深拷贝后赋值到深拷贝的结果对应的属性result[k]上,遍历完毕后返回result。 在执行deepClone(target[k])中,又会对target[k]进行类型判断,重复上述流程,形成了一个递归调用deepClone函数的过程。就可以层层遍历要拷贝的数据,不管要拷贝的数据有多少子属性,只要子属性的值的类型是引用类型,就会调用deepClone函数将其深拷贝后赋值到深拷贝的结果对应的属性上。 另外使用for...in循环遍历对象的属性时,其原型链上的所有属性都将被访问,如果只要只遍历对象自身的属性,而不遍历继承于原型链上的属性,要使用hasOwnProperty方法过滤一下。 在这里可以向面试官展示你的三个编程能力。 对原始类型和引用类型数据的判断能力。 对递归思维的应用的能力。 深入理解for...in的用法。 黄金段位 白银段位的代码中只考虑到了引用类型的数据是对象的情况,漏了对引用类型的数据是数组的情况。 function deepClone(target){ if(target !== null && typeof target === 'object'){ let result = Object.prototype.toString.call(target) === "[object Array]" ? [] : {}; for (let k in target){ if (target.hasOwnProperty(k)) { result[k] = deepClone(target[k]) } } return result; }else{ return target; } } 以上代码中,只是额外增加对参数target是否是数组的判断。执行Object.prototype.toString.call(target) === "[object Array]"判断target是不是数组,若是数组,变量result为[],若不是数组,变量result为{}。 在这里可以向面试官展示你的两个编程能力。 正确理解引用类型概念的能力。 精确判断数据类型的能力。 铂金段位 假设要深拷贝以下数据data let data = { a: 1 }; data.f=data 执行deepClone(data),会发现控制台报错,错误信息如下所示。 这是因为递归进入死循环导致栈内存溢出了。根本原因是data数据存在循环引用,即对象的属性间接或直接的引用了自身。 function deepClone(target) { function clone(target, map) { if (target !== null && typeof target === 'object') { let result = Object.prototype.toString.call(target) === "[object Array]" ? [] : {}; if (map[target]) { return map[target]; } map[target] = result; for (let k in target) { if (target.hasOwnProperty(k)) { result[k] = deepClone(target[k]) } } return result; } else { return target; } } let map = {} const result = clone(target, map); map = null; return result } 以上代码中利用额外的变量map来存储当前对象和拷贝对象的对应关系,当需要拷贝当前对象时,先去map中找,有没有拷贝过这个对象,如果有的话直接返回,如果没有的话继续拷贝,这样就巧妙化解的循环引用的问题。最后需要把变量map置为null,释放内存,防止内存泄露。 在这里可以向面试官展示你的两个编程能力。 对循环引用的理解,如何解决循环引用引起的问题的能力。 对内存泄露的认识和避免泄露的能力。 砖石段位 该段位要考虑性能问题了。在上面的代码中,我们遍历数组和对象都使用了for...in这种方式,实际上for...in在遍历时效率是非常低的,故用效率比较高的while来遍历。 function deepClone(target) { /** * 遍历数据处理函数 * @array 要处理的数据 * @callback 回调函数,接收两个参数 value 每一项的值 index 每一项的下标或者key。 */ function handleWhile(array, callback) { const length = array.length; let index = -1; while (++index < length) { callback(array[index], index) } } function clone(target, map) { if (target !== null && typeof target === 'object') { let result = Object.prototype.toString.call(target) === "[object Array]" ? [] : {}; if (map[target]) { return map[target]; } map[target] = result; const keys = Object.prototype.toString.call(target) === "[object Array]" ? undefined : Object.keys( target); function callback(value, key) { if (keys) { // 如果keys存在则说明value是一个对象的key,不存在则说明key就是数组的下标。 key = value; } result[key] = clone(target[key], map) } handleWhile(keys || target, callback) return result; } else { return target; } } let map = {} const result = clone(target, map); map = null; return result } 用while遍历的深拷贝记为deepClone,把用for ... in遍历的深拷贝记为deepClone1。利用console.time()和console.timeEnd()来计算执行时间。 let arr = []; for (let i = 0; i < 1000000; i++) { arr.push(i) } let data = { a: arr }; console.time(); const result = deepClone(data); console.timeEnd(); console.time(); const result1 = deepClone1(data); console.timeEnd(); 从上图明显可以看到用while遍历的深拷贝的性能远优于用for ... in遍历的深拷贝。 在这里可以向面试官展示你的四个编程能力。 具有优化代码运行性能的能力。 了解遍历的效率的能力。 了解++i和i++的区别。 代码抽象的能力。 星耀段位 在这个阶段应该考虑代码逻辑的严谨性。在上面段位的代码虽然已经满足平时开发的需求,但是还是有几处逻辑不严谨的地方。 判断数据不是引用类型时就直接返回target,但是原始类型中还有 Symbol 这一特殊类型的数据,因为其每个 Symbol 都是独一无二,需要额外拷贝处理,不能直接返回。 判断数据是不是引用类型时不严谨,漏了typeof target === function'的判断。 只考虑了 Array、Object 两种引用类型数据的处理,引用类型的数据还有Function 函数、Date 日期、RegExp 正则、Map 数据结构、Set 数据机构,其中 Map 、Set 属于 ES6 的。 废话不多说,直接贴上全部代码,代码中有注释。 function deepClone(target) { // 获取数据类型 function getType(target) { return Object.prototype.toString.call(target) } //判断数据是不是引用类型 function isObject(target) { return target !== null && (typeof target === 'object' || typeof target === 'function'); } //处理不需要遍历的应引用类型数据 function handleOherData(target) { const type = getType(target); switch (type) { case "[object Date]": return new Date(target) case "[object RegExp]": return cloneReg(target) case "[object Function]": return cloneFunction(target) } } //拷贝Symbol类型数据 function cloneSymbol(targe) { const a = String(targe); //把Symbol字符串化 const b = a.substring(7, a.length - 1); //取出Symbol()的参数 return Symbol(b); //用原先的Symbol()的参数创建一个新的Symbol } //拷贝正则类型数据 function cloneReg(target) { const reFlags = /\w*$/; const result = new target.constructor(target.source, reFlags.exec(target)); result.lastIndex = target.lastIndex; return result; } //拷贝函数 function cloneFunction(targe) { //匹配函数体的正则 const bodyReg = /(?<={)(.|\n)+(?=})/m; //匹配函数参数的正则 const paramReg = /(?<=\().+(?=\)\s+{)/; const targeString = targe.toString(); //利用prototype来区分下箭头函数和普通函数,箭头函数是没有prototype的 if (targe.prototype) { //普通函数 const param = paramReg.exec(targeString); const body = bodyReg.exec(targeString); if (body) { if (param) { const paramArr = param[0].split(','); //使用 new Function 重新构造一个新的函数 return new Function(...paramArr, body[0]); } else { return new Function(body[0]); } } else { return null; } } else { //箭头函数 //eval和函数字符串来重新生成一个箭头函数 return eval(targeString); } } /** * 遍历数据处理函数 * @array 要处理的数据 * @callback 回调函数,接收两个参数 value 每一项的值 index 每一项的下标或者key。 */ function handleWhile(array, callback) { let index = -1; const length = array.length; while (++index < length) { callback(array[index], index); } } function clone(target, map) { if (isObject(target)) { let result = null; if (getType(target) === "[object Array]") { result = [] } else if (getType(target) === "[object Object]") { result = {} } else if (getType(target) === "[object Map]") { result = new Map(); } else if (getType(target) === "[object Set]") { result = new Set(); } //解决循环引用 if (map[target]) { return map[target]; } map[target] = result; if (getType(target) === "[object Map]") { target.forEach((value, key) => { result.set(key, clone(value, map)); }); return result; } else if (getType(target) === "[object Set]") { target.forEach(value => { result.add(clone(value, map)); }); return result; } else if (getType(target) === "[object Object]" || getType(target) === "[object Array]") { const keys = getType(target) === "[object Array]" ? undefined : Object.keys(target); function callback(value, key) { if (keys) { // 如果keys存在则说明value是一个对象的key,不存在则说明key就是数组的下标。 key = value } result[key] = clone(target[key], map) } handleWhile(keys || target, callback) } else { result = handleOherData(target) } return result; } else { if (getType(target) === "[object Symbol]") { return cloneSymbol(target) } else { return target; } } } let map = {} const result = clone(target, map); map = null; return result } 在这里可以向面试官展示你的六个编程能力。 代码逻辑的严谨性。 深入了解数据类型的能力。 JS Api 的熟练使用的能力。 了解箭头函数和普通函数的区别。 熟练使用正则表达式的能力。 模块化开发的能力 王者段位 以上代码中还有很多数据类型的拷贝,没有实现,有兴趣的话可以在评论中实现一下,王者属于你哦! 总结 综上所述,面试官叫你实现一个深拷贝,其实是要考察你各方面的能力。例如 白银段位 对原始类型和引用类型数据的判断能力。 对递归思维的应用的能力。 黄金段位 正确理解引用类型概念的能力。 精确判断数据类型的能力。 铂金段位 对循环引用的理解,如何解决循环引用引起的问题的能力。 对内存泄露的认识和避免泄露的能力。 砖石段位 具有优化代码运行性能的能力。 了解遍历的效率的能力。 了解++i和i++的区别。 代码抽象的能力。 星耀段位 代码逻辑的严谨性。 深入了解数据类型的能力。 JS Api 的熟练使用的能力。 了解箭头函数和普通函数的区别。 熟练使用正则表达式的能力。 模块化开发的能力 所以不要去死记硬背一些手写代码的面试题,最好自己动手写一下,看看自己达到那个段位了。 最后 对于大厂面试,我最后想要强调的一点就是心态真的很重要,是决定你在面试过程中发挥的关键,若不能正常发挥,很可能就因为一个小失误与offer失之交臂,所以一定要重视起来。另外提醒一点,充分复习,是消除你紧张的心理状态的关键,但你复习充分了,自然面试过程中就要有底气得多。 我平时一直有整理面试题的习惯,有随时跳出舒适圈的准备,不知不觉整理了229页了,在这里分享给大家,有需要的点击这里免费领取题目+解析PDF 篇幅有限,仅展示部分内容 如果你需要这份完整版的面试题+解析,【点击我】就可以了。 希望大家明年的金三银四面试顺利,拿下自己心仪的offer!

优秀的个人博客,低调大师

Fuchsia 编程语言策略:前端用 Dart,Go 要退出

谷歌新一代开源操作系统Fuchsia 的开发者网站上公布了一份Fuchsia 编辑语言策略,文档描述了 C、C++、Dart、Rust 与 Go 的优劣势,并明确指定了其中哪些语言将会在 Fuchsia 开发生态中得到怎样程度的支持。 该策略文档描述了 Fuchsia 项目在 Fuchsia Platform Source Tree(Fuchsia 平台源码树)和供终端开发者在这之外构建 Fuchsia 开发目标设备软件时使用和支持的编程语言。 Fuchsia Platform Source Tree 是 fuchsia.googlesource.com 上托管的 Fuchsia 源码,终端开发者是指在 Fuchsia Platform Source Tree 之外为 Fuchsia 编写软件的人。与终端开发者相比,Fuchsia Platform Source Tree 可以汇聚对 Fuchsia 系统及其基础技术更大的更改,因为可以执行仅影响 Fuchsia Platform Source Tree 的更改,而无需与其他人员进行协调。相反,影响终端开发者的更改需要更多的协调,并且需要更长的执行时间。 谷歌介绍,对终端开发者的支持意味着 Fuchsia SDK 需要包含可帮助他们为 Fuchsia 开发软件的工具和库,包括 FIDL(FuchsiaInterface definition language,Fuchsia接口定义语言)特定语言后端与支持库等。对异步编程的强大支持则意味着可以使用直线型代码,例如使用 Rust/Dart 等语言的 async/await 编写异步程序。 文档接下来分别罗列了几种编程语言,分析了它们的优缺点之后,得出如下结论: C C 被支持用于终端开发者。 在 Fuchsia Platform Source Tree 中,不鼓励使用 C 的新用法,并且仅批准用于以下领域: 用于底层系统编程,包括内核中 用于定义到共享库和其它系统组件的 ABI 稳定接口 C++ C++ 被支持用于终端开发者。 批准在整个 Fuchsia Platform Source Tree 中使用 C++。 Dart Dart 被支持用于面向非驱动程序的终端开发者。 Dart 被批准在 Fuchsia Platform Source Tree 中用于用户界面和无限期运行的程序。 Rust 终端开发者不支持使用 Rust。 除了 Zircon 内核,Rust 被批准在整个 Fuchsia Platform Source Tree 中使用。 Go Go 仅被批准在网络技术栈中使用,因为网络栈的迁移需要大量成本。时间准许的话,应该将网络栈迁移到批准的语言。 必须将 Fuchsia 中用于目标设备上生产软件的所有其它Go 代码迁移到批准的语言。 总结一下,Go 在 Fuchsia 生态中可能会有些失落,值得注意的是文档中关于其缺点的介绍: 使用垃圾回收来管理内存,这比其它用于管理内存的技术占用更多的资源 运行时环境庞大 Fuchsia Platform Source Tree 在使用 Go 方面具有负面的实施经验。Fuchsia 项目在 Go 中构建的系统组件消耗的内存和内核资源相比使用 C++ 或 Rust 构建的同类组件更多 工具链产生大型二进制文件 值得关注的是,文档中没有涉及 Fuchsia已经确认支持的 Python。不过关于Python 的支持细节,支持语言页面也没有详细内容,只有一栏栏TODO,应该是还没有做好准备。 而另一方面,此前关于支持 JavaScript的传言,看来也无法得证。

优秀的个人博客,低调大师

React新Context API在前端状态管理的实践

11/10号文档资料已全面更新!;《【阿里P7】Android高级教程+BAT面试题》,可复制链接后用石墨文档 App 或小程序打开:【阿里P7】移动互联网架构师进阶高级教程+BAT面试题众所周知,React的单向数据流模式导致状态只能一级一级的由父组件传递到子组件,在大中型应用中较为繁琐不好管理,通常我们需要使用Redux来帮助我们进行管理,然而随着React 16.3的发布,新context api成为了新的选择。 一、Redux的简介以及缺陷 Redux来源于Flux并借鉴了Elm的思想,主要原理如下图所示: 可以看到,Redux的数据流其实非常简单,外部事件通过actionCreator函数调用dipsatch发布action到reducers中,然后各自的reducer根据action的类型(action.type) 来按需更新整个应用的state。 redux设计有以下几个要点: 1. state是单例模式且不可变的,单例模式避免了不同store之间的数据交换的复杂性,而不可变数据提供了十分快捷的撤销重做、“时光旅行”等功能。 2. state只能通过reducer来更新,不可以直接修改。 3. reducer必须是纯函数,形如(state,action) => newState redux本身是个非常纯粹的状态管理库,需要通过react-redux这个库的帮助来管理react的状态。react-redux主要包含两个部分。 1. Provider组件:可以将store注入到子组件的cotext中,所以一般放在应用的最顶层。 2. connect函数: 返回一个高阶函数,把context中由Provider注入的store取出来然后通过props传递到子组件中,这样子组件就能顺利获取到store了。 虽然redux在React项目中得到了普遍的认可与使用率,然而在现实项目中redux还是存在着很多缺点: 1.样板代码过多:增加一个action往往需要同时定义相应的actionType然后再写N个相关的reducer。例如当添加一个异步加载事件时,需要同时定义加载中、加载失败以及加载完成三个actionType,需要一个相对应的reducer通过switch分支来处理对应的actionType,冗余代码过多。 2.更新效率问题:由于使用不可变数据模式,每次更新state都需要拷贝一份完整的state造成了内存的浪费以及性能的损耗。 3.数据传递效率问题:由于react-redux采用的旧版context API,context的传递存在着效率问题。 其中,第一个问题目前已经存在着非常多的解决方案,诸如dva、rematch以及mirror等等,笔者也造过一个类似的轮子restated这里不做过多阐述。 第二个问题首先redux以及react-redux中已经做了非常详尽的优化了,其次擅用shouldComponentUpdate方法也可以避免很多不必要的更新,最后,也可以使用一些不可变数据结构如immutable、Immr等来从根本上解决拷贝开销问题。 第三个问题属于React自身API的局限,从第三方库的角度上来说,能做的很有限。 二、Context API context API主要用来解决跨组件传参泛滥的问题(prop drilling),旧的context API的语法形式如下: 可以看到,使用context api可以把DeliverComponent中的参数color直接跨越MidComponent传递到ReceiverComponent中,不需要冗余的使用props参数传递,特别是ReceiverComponent层级特别深的时候,使用context api能够很大程度上节省重复代码避免bug。 旧Context API的缺陷 旧的context api主要存在如下的缺陷: 1.代码冗余:提供context的组件要定义childContextTypes与getChildContext才能把context传下去。同时接收context的也要先定义contextTypes才能正确拿到数据。 2.传递效率:虽然功能上context可以跨层级传递,但是本质上context也是同props一样一层一层的往下传递的,当层级过深的时候还是会出现效率问题。 3.shouldComponentUpdate:由于context的传递也是一层一层传递,因此它也会受到shouldComponent的阻断。换句话说,当传递组件的context变化时,如果其下面某一个中间组件的shouldComponentUpdate方法返回false,那么之后的接收组件将不会受到任何context变化。 为了解决旧版本的shouldComponentUpdate问题,保证所有的组件都能收到store的变化,react-redux只能传递一个getState方法给各个组件用于获取最新的state(直接传递state可能会被阻断,后面的组件将接收不到state的变化),然后每个connect组件都需要直接或间接监听state的变化,当state发生改变时,通过内部notifyNestedSubs方法从上往下依次触发各个子组件通过getState方法获取最新的state更新视图。这种方式效率较低而且比较hack。 三、新Context API React自16.3开始提供了一个新的context api,彻底解决了旧Context API存在的种种问题。 下面是新context api(右)与使用旧context api的react-redux(左)数据流的比较: 可以看到,新的context api可以直接将context数据传递到传递到子组件中而不需要像旧context api那样级联传递。因此也可以突破shouldComponentUpdate的限制。新版的context api的定义如下: 下面是一个比较简单的应用示例: 可以看到新的context api主要包含一个Provider和Consumer对,在Provider输入的数据可以在Consumer中获得。 新context api的要点如下: 1. Provider和 Consumer必须来自同一次 React.createContext调用。也就是说 NameContext.Provider和 AgeContext.Consumer是无法搭配使用的。 2. React.createContext方法接收一个默认值作为参数。当 Consumer外层没有对应的 Provider时就会使用该默认值。 3. Provider 组件的 valueprop 值发生变更时,其内部组件树中对应的 Consumer组件会接收到新值并重新执行 children函数。此过程不受 shouldComponentUpdete 方法的影响。 4. Provider组件利用 Object.is 检测 value prop 的值是否有更新。注意 Object.is和 === 的行为不完全相同。 5. Consumer组件接收一个函数作为 children prop 并利用该函数的返回值生成组件树的模式被称为 Render Props 模式。 四、新Context API的应用 新的Context API大大简化了react状态传递的问题,也出现了一些基于它的状态管理库,诸如:unstated、react-waterfall等等。下面我们主要尝试使用新context api来造一个react-redux的轮子。 1. Provider 由于新的context api传递过程中不会被shouldComponentUpdate阻断,所以我们只需要在Provider里面监听store变化即可: 2. connect 相比较于react-redux,connect中的高阶组件逻辑就简单的多,不需要监听store变化,直接获得Provider传入的state然后再传递给子组件即可: 好了,至此整个React-redux的接口和功能都已经基本cover了,下面继续介绍一些比较重要的性能优化。 3. 性能优化 - 减少重复渲染 性能优化最大的一部分就是要减少无意义的重复渲染,当WrappedComponent的参数值没有变化时我们应该阻止其重新渲染。可以通过手写shouldComponentUpdate方法实现,也可以直接通过PureComponent组件来达到我们的目标: 这里需要注意的是,本示例的mapDispatchToProps未支持ownProps参数,因此可以把它的返回值看成是不变的,否则每次调用它返回的action函数都是新创建的,从而导致Prevent接收到的参数始终是不同的,达不到预期效果。更为复杂的情况请参考react-redux源码中selector相关的部分。 4. 性能优化 - 减少层级嵌套 性能优化另一个要点就是减少组件的层级嵌套,新context api在获取context值的时候需要嵌套一层Consumer组件,这也是其比旧context api劣势的地方。除此之外,我们应该尽量减少层级的嵌套。因此在前一个性能优化中我们不应该再次嵌套一个PureComponent,取而代之的是,我们可以直接在Cunsumer中实现一个memory机制,实现代码如下: 下面是前后chrome开发人员工具中组件层级的对比,可以看到嵌套层级成功减少了一层,两层嵌套是新context api的局限,如果要保持react-redux的接口模式则无法再精简了。 原文链接https://www.toutiao.com/i6615135777976222216/ 最后 我们今年整理了一份阿里P7级别的Android架构师全套学习资料,特别适合有3-5年以上经验的小伙伴深入学习提升。 主要包括腾讯,以及字节跳动,华为,小米,等一线互联网公司主流架构技术。如果你有需要,尽管拿走好了。 以下为我整理的资料免费分享;【阿里P7】Android高级教程+BAT面试题 1.Android高级技术脑图 2.P7级Android高级架构视频教程 3.Android核心高级技术PDF文档+BAT大厂面试真题解析 4.Android思维脑图(技能树) 1.Android高级技术脑图; 查漏补缺,体系化深入学习提升 2.【Android高级架构视频教程】; 全套部分展示; java与Android内核进阶专题视频与源码 阿里P7级全套高级学习视频; 3.Android核心高级技术PDF文档,BAT大厂面试真题解析 4.Android思维脑图(技能树) 免费分享 【阿里P7】移动互联网架构师进阶高级教程+BAT面试题 为什么免费分享? 我的目的是让更多需要的Android开发朋友能够提升自己的技术水平无论是Android,还是qq,微信,360等,想在互联网上最大程度推广,就必须免费!如果我的学习资料对你有帮助,点个赞,谢谢!

资源下载

更多资源
腾讯云软件源

腾讯云软件源

为解决软件依赖安装时官方源访问速度慢的问题,腾讯云为一些软件搭建了缓存服务。您可以通过使用腾讯云软件源站来提升依赖包的安装速度。为了方便用户自由搭建服务架构,目前腾讯云软件源站支持公网访问和内网访问。

Nacos

Nacos

Nacos /nɑ:kəʊs/ 是 Dynamic Naming and Configuration Service 的首字母简称,一个易于构建 AI Agent 应用的动态服务发现、配置管理和AI智能体管理平台。Nacos 致力于帮助您发现、配置和管理微服务及AI智能体应用。Nacos 提供了一组简单易用的特性集,帮助您快速实现动态服务发现、服务配置、服务元数据、流量管理。Nacos 帮助您更敏捷和容易地构建、交付和管理微服务平台。

Spring

Spring

Spring框架(Spring Framework)是由Rod Johnson于2002年提出的开源Java企业级应用框架,旨在通过使用JavaBean替代传统EJB实现方式降低企业级编程开发的复杂性。该框架基于简单性、可测试性和松耦合性设计理念,提供核心容器、应用上下文、数据访问集成等模块,支持整合Hibernate、Struts等第三方框架,其适用范围不仅限于服务器端开发,绝大多数Java应用均可从中受益。

WebStorm

WebStorm

WebStorm 是jetbrains公司旗下一款JavaScript 开发工具。目前已经被广大中国JS开发者誉为“Web前端开发神器”、“最强大的HTML5编辑器”、“最智能的JavaScript IDE”等。与IntelliJ IDEA同源,继承了IntelliJ IDEA强大的JS部分的功能。

用户登录
用户注册