浅谈组件增强

2017-12-07 08:00:30来源:作者:人点击

分享

浅谈组件增强

11月的上海,褪去了一丝温暖,夹带着丝丝寒意。独自一人走在街上,望着路边的男男女女,不禁让我想起了那个前任的她。因为有她,我才学会了做饭,学会照顾人,学会怎么谦让,不过在生命的进程中,她不过是一个过客罢了,她的出现和离开,对我都没有本质的改变。一阵寒风吹过,我再次抬头,是啊,刚才的故事蛮好的,是时候来知乎分享刚编的故事了,显然,程序员并不会有前任。

细细想一想,其实生活中存在很多起修饰作用的东西,万物本通,在编程界,也有许多的起修饰作用的东东,而且基于这个概念还发展出了许许多多的名称。

装饰函数

来首先看第一个装饰函数,函数可以接受参数并且返回值。如果接受的值和返回的一样呢?如果我们有一个构造函数:

function Person(name) {  this.name =  name}Person.prototype.greet = function() {  console.log('hello:' + this.name)}

现在由于 Person 函数是被封装的,不可以改变其源代码,而又想在先增加一个输出时间的方法,再调用原输出。就可以简单采用如下方式:

const cloudic = new Person('cloudic')cloudic.greet() // => hello:cloudic// 函数直接包装function sayDate(f, ...args) { console.log('show date') typeOf f === 'funciton' && f(...args)}cloudic.dateGreet = sayDate(cloudic.greet)cloudic.dateGreet() // => show date hello:cloudic// 采用原型继承包装function DateDecorator(man) {  const newMan = Object.create(man)  newMan.greet = function() {  console.log('show date')  man.greet()  }  return newMan}const newCloudic = DateDecorator(cloudic)newCloudic.greet() // => show date hello:cloudic

这样子类的更改并不会影响其他子类,当然第一种看起来更加简单点,如果希望所做的改变能同步影响到子类,可以覆盖原型对象上同名的方法,当然也可以用 decorator 语法糖。

为什么想动态增加个功能这样繁琐呢,JS 是动态语言,在运行的时候,可以方便的修改对象的属性,但是由于函数存在作用域的限制,想要动态修改函数内部的变量很难实现。所以才绕着弯子,采用包装的方式来实现。换个角度想下,如果一个对象或者函数内部提供了一个方法,该方法可以动态的修改函数内部的变量,那我们只需要调用该方法即可,这样的模式暂且叫中间件模式吧,比如 redux 的中间件。

高阶组件

说回组件层面,如果把上面说的应用到组件上,那么就是传入一个组件返回另外一个组件。在 React 中,我们比较常用的叫 HOC 模式,常见的实现方式如下:

function HOC(WrappedComponent) {  return class Wrap extends React.Component { render() {return <WrappedComponent {...this.props}/> }  }}

在使用的时候,只需要传入需要包装的组件,调用函数后会返回一个包装过的组件。这样的好处是通过避免直接调用原组件,采用类似代理的方式来间接调用,从而可以方便后期替换原组件,可以做适配器,同时相比于 mixin 的实现方式,高阶组件更容易被调试,由于 mixin 是混入模式,导致在组件之间共享的代码很隐晦,如果方法比较多的话,还可能会覆盖现有的组件,所以 React 官方废弃了 mixin 的模式,推崇高阶组件来代替组件之间共享代码。

抽象组件

在 Vue 中,如果想对一个组件或者 DOM 进行相关功能的增强,我们可以用下面几种方式:

其一我们可以想到是采用指令的形式:

Vue.directive('name', {  inserted: function (el) { // do something...  }})

指令可以方便的增加功能和复用,但是指令不能使用回调函数,不能传 props,没有事件,导致使用起来的灵活度不高。指令更多的是做 DOM 层面的工作,封装一些方法来修饰元素或操作元素属性。

其二我们可以用函数式组件:

Vue.component('name', {  functional: true,  render: function (createElement, context) { // ...do something  },  // Props 可选  props: { // ...  }})

函数式组件不会被实例化,也就是没有 data 和 this 上下文,也不能使用事件回调。可以简单的认为就是一个函数而已。不过函数式组件的优点也在于此,并不会有额外的性能开销,从而可以提高程序的性能。

其三就是采用抽象组件:

Vue.component('name', {  abstract: true,  render: function (createElement) { return this.$slots.default[0]  }})

抽象组件在 Vue 官网并没有文档介绍,因为这是一个内部组件定义的功能,不是很稳定,随时可能会更改,并且不会通知到你。Vue 内置的组件比如 keep-alive, transition, component, slot 都是抽象组件,抽象组件没有自己的 DOM 元素,只是简单增加功能然后返回子元素。和纯函数组件相比,它有自己的生命周期,会被实例化,内部有this,是一个真正的组件,所以可以用 emit 发放事件。

总结

在组件增强的各个方法中,每个都有优劣,至于到底选择那个,还是需要使用者根据业务场景来决定。

来自:https://zhuanlan.zhihu.com/p/30818429

前端Vue.js开发

微信扫一扫

第七城市微信公众平台