Decorator 简介及实战
hflk6888
7年前
<h3>前言</h3> <p>用过Java的朋友都知道,装饰器(Decorator)是一种十分非常方便改变类运行是行为的一种方式,例如在 Spring 框架中我们用到比较多的注解(@Autowired), 通过它可以自动实例化对象,从而可以精简代码。装饰器是典型的 AOP(Aspect-Oriented-Programming) 编程的应用,类似的 CSS 对于 HTML 的样式也是类似于这种思想,即从切面来改变和影响主对象的行为。</p> <h3>Decorator 简介</h3> <p>Decorator 是一种通过注解表达式就可以扩展类或者方法的函数。Decorator 可以应用到任何一个 class 或者property 上。列如:</p> <pre> @myDecorator class A {} // 作用class @myDecorator doSomething() {} // 作用表达式</pre> <p>Javascript Decorator 目前任然是ES7提案状态,更多该特性的进度可以访问 <a href="/misc/goto?guid=4959755587781708082" rel="nofollow,noindex">proposal-decorators</a> 查看。</p> <h3>Decorator原理</h3> <p>说到更改对象的属性或者方法,大家肯定会想到 Object.defineProperty(obj, prop, descriptor) 方法,通过该方法,我们可以轻易的修改或者重写对象的行为或者属性,之前 Vue 中提到的双向绑定,即是通过重写 set 和 get 方法来实现的。所以在我们还未正式用上 Decorator 前,都是通过 Object.defineProperty 方法来实现。先来简单的认识下这个方法:</p> <p>/**</p> <ul> <li>obj : 需要修改属性的对象</li> <li>prop : 需要修改对象的属性名称</li> <li>descriptor: 用来定义属性具体行为的描述对象<br> **/<br> Object.defineProperty(obj, prop, descriptor)</li> </ul> <p>descriptor 属性说明</p> <ul> <li> <p>configurable : 定义属性对象是否可以被配置,即如果为 false ,定义修改的描述操作(writeable, get 等等)都无效</p> </li> <li> <p>enumerable : 是否可以通过 for-in 来遍历,或者 Object.keys 列举</p> </li> <li> <p>value : 定义对象 value 属性的值,value 可以是 number, object, function 等等</p> </li> <li> <p>writable: 定义 value 值是否可以被重写</p> </li> <li> <p>get: 一个访问 value 属性时会触发的 function 对象</p> </li> <li> <p>set: 一个设置 value 属性时会触发的 function 对象</p> </li> </ul> <p>修改一个属性为只读(readonly)</p> <p>了解 Object.defineProperty 的基本语法后,我通过它先简单实现一个 readonly 实例。具体代码如下:</p> <h3>Decorator 的基本语法与使用</h3> <pre> ``javascript # 定义 function myDecoration(target, name, descriptor) {} # 对property使用 class A { @myDecorator test() {} } # 对class使用 @myDecorator class A {} # 带参数 function myDescorator(a) { return function (target, name, descriptor) { console.llog('params:', a) } } @myDescorator(a) class A {} # 时使用多个装饰器(Decorator) @myDecorator1 @myDecorator2 class A {}</pre> <p>利用 Decorator 语法糖修改一个属性为只读(readonly)</p> <h3>利用 Decorator 给 React 组件封装 PureRender</h3> <p>我们都知道,在 React 生命周期里有一个 shouldComponentUpdate 方法,该方法通过返回 ture 或者 false 来确定组件是否重新 render 组件。也就是说,通过该方法我们可以过滤掉些无效的数据渲染事件,从而提升性能。例如我们针对 props 传递过来的数据对象进行对比,如果 props 对象的属性以及值并未变更的情况下,则无需执行render方法。</p> <p>显然通过对比 props 下数据对象的属性与值是否变更,这种逻辑是可以复用的,而不是在单独的在每个组件中去在重复的写 shouldComponentUpdate 方法。说到改变组件对象的方法行为,这里我们显然就可以使用</p> <p>Decorator 来这个特性来做了,即我们对应用 Decorator 对象的 shouldComponentUpdate 进行重写。通过遍历 props 对象的属性和值,并与老 props 的属性与值进行对比,从而确定是否需要重新渲染。具体代码如下:</p> <pre> function isEqual(a, b) { for (const key in a) { if ({}.hasOwnProperty.call(a, key) && (!{}.hasOwnProperty.call(b, key) || a[key] !== b[key])) { return false; } } for (const key in b) { if ({}.hasOwnProperty.call(b, key) && !{}.hasOwnProperty.call(a, key)) { return false; } } return true; } export default function pureRender(targetComponent) { targetComponent.prototype.shouldComponentUpdate = function (props, state) { return !isEqual(this.state, state) || !isEqual(this.props, props) } } // 使用 @pureRender class ComponentA extends React.Component {}</pre> <h3>通过 Babel 使用 Decorator</h3> <p>由于 Decorator 是ES7中的草案,所以现在需要通过 Bable 才能使用。使用方法如下:</p> <p>安装</p> <p>npm install --save-dev babel-plugin-transform-decorators</p> <p>使用</p> <p><strong>方法一、 通过配置.babelrc</strong></p> <pre> { "plugins": ["transform-decorators"] }</pre> <p><strong>方法二、通过CLI</strong></p> <p>babel --plugins transform-decorators script.js</p> <p><strong>方法三、通过Node API</strong></p> <pre> require("babel-core").transform("code", { plugins: ["transform-decorators"] });</pre> <h3>总结</h3> <p>通过 Decorator 这种不需要直接在对象或者方法中编写额外逻辑的方式,就可以轻易的扩展对象或者方法的能力,既满足了功能需求,也精简了代码,保证了代码的可维护性,例如我们已经常见的@log, @test, @mixin等等工具类。所以,以后的工作中可以多多尝试。</p> <p> </p> <h3>参考</h3> <ul> <li><a href="/misc/goto?guid=4959755587866803329" rel="nofollow,noindex">细说ES7 JavaScript Decorators</a></li> <li><a href="/misc/goto?guid=4959755587957282273" rel="nofollow,noindex">Decorator specification</a></li> <li><a href="/misc/goto?guid=4959755588041648146" rel="nofollow,noindex">Exploring EcmaScript Decorators</a></li> <li><a href="/misc/goto?guid=4958876160612172748" rel="nofollow,noindex">Object.defineProperty</a></li> <li><a href="/misc/goto?guid=4959755588152569939" rel="nofollow,noindex">Babel Legacy Decorator plugin</a></li> <li><a href="/misc/goto?guid=4959755588237421033" rel="nofollow,noindex">core-decorators</a></li> </ul> <p> </p> <p>来自:http://div.io/topic/2063</p> <p> </p>