Decorator 简介及实战

hflk6888 6年前
   <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>