博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
React Mixins入门指南
阅读量:7107 次
发布时间:2019-06-28

本文共 6667 字,大约阅读时间需要 22 分钟。

  对于很多初级的前端工程师对mixins的概念并不是很了解,也没有在React中尝试使用过Mixins,这边文章基本会按照Mixins的作用、用途、原理等多个方面介绍React中Mixins的使用。

  首先解释一下什么是Mixins,在一些大型项目中经常会存在多个组件需要使用相同的功能的情况,如果在每个组件中都重复性的加入相同的代码,那么代码的维护性将会变的非常差,Mixins的出现就是为了解决这个问题。可以将通用共享的方法包装成Mixins方法,然后注入各个组件实现,我们首先给出一个Mixins简单的例子:

const mixin = function(obj, mixins) {  const newObj = obj;  newObj.prototype = Object.create(obj.prototype);  for (let prop in mixins) {    if (mixins.hasOwnProperty(prop)) {      newObj.prototype[prop] = mixins[prop];    }  }  return newObj;}const manMixins = {  speak: function (){    console.log("I'm "+this.name);  }};const Man = function() {  this.name = 'wang';};const manCanSpeak = mixin(Man,manMixins);const man = new manCanSpeak(); man.speak(); //'I'm wang'复制代码

  上述代码就实现了一个简单的mixin函数,其实质就是将mixins中的方法遍历赋值给newObj.prototype,从而实现mixin返回的函数创建的对象都有mixins中的方法。在我们大致明白了mixin作用后,让我们来看看如何在React使用mixin

React.createClass

  假设我们所有的React组件的props中都含有一个默认的displayName,在使用React.createClass时,我们必须给每个组件中都加入

getDefaultProps: function () {    return {
displayName: "component"};}复制代码

  当然我们,我们通过实现一个mixin函数,就可以实现这个功能,并且在createClass方法使用mixin非常简单:

var mixinDefaultProps = {    getDefaultProps: function(){        return {
displayName: 'component'} }}var ExampleComponent = React.createClass({ mixins: [mixinDefaultProps], render: function(){ return
{this.props.displayName}
}});复制代码

  这样我们就实现了一个最简单的mixin函数,通过给每一个组件配置mixin,我们就实现了不同组件之间共享相同的方法。需要注意的是:

mixin中有相同的函数

  1. 组件中含有多个mixin,不同的mixin中含有相同名字的非生命周期函数,React会抛出异常(不是后面的函数覆盖前面的函数)。
  2. 组件中含有多个mixin,不同的mixin中含有相同名字的生命周期函数,不会抛出异常,mixin中的相同的生命周期函数(除render方法)会按照createClass中传入的mixins数组顺序依次调用,全部调用结束后再调用组件内部的相同的声明周期函数。

mixin中设置props或state

  1. 组件中含有多个mixin,不同的mixin中的默认props或初始state中不存在相同的key值时,则默认props和初始state都会被合并。
  2. 组件中含有多个mixin,不同的mixin中默认props或初始state中存在相同的key值时,React会抛出异常。

JSX

  目前几乎很少有人会使用React.createClass的方式使用React,JSX + ES6成了标配,但是JavaScript在ES6之前是原生不支持的mixin的,ES7引入了decorator,首先介绍一下decorator到底是什么?

Decorator

  ES7的Decorator语法类似于Python中的Decorator,在ES7中也仅仅只是一个语法糖,@decorator主要有两种,一种是面向于类(class)的@decorator,另一种是面向于方法(function)的@decorator。并且@decorator实质是利用了ES5中的Object.defineProperty

Object.defineProperty  

  关于Object.defineProperty不是很了解的同学其实非常推荐看一下《JavaScript高级程序设计》的第六章第一节,大概总结一下:在ES5中对象的属性其实分为两种: 数据属性访问器属性

数据属性

数据属性有四个特性:

  1. configurable: 属性是否可删除、重新定义
  2. enumerable: 属性是否可枚举
  3. writable: 属性值是否可修改
  4. value: 属性值
    访问器属性
  5. configurable: 属性是否可删除、重新定义
  6. enumerable: 属性是否可枚举
  7. get: 读取属性调用
  8. set: 设置属性调用

  Object.defineProperty(obj, prop, descriptor)的三个参数是定义属性的对象、属性名和描述符,描述符本身也是Object,其中的属性就是数据属性或者访问器属性规定的参数,举个栗子:

var person = {};Object.defineProperty(person,'name',{    configurable: true,    enumerable: true,    writable: true,    value: 'wang'});console.log(person.name);//wang复制代码

了解了Object.defineProperty,我们分别看下面向于类(class)的@decorator和面向于方法(function)的@decorator。

面向方法的@decorator

  class语法其实仅仅只是ES6的一个语法糖而已,class其实质是function。并且class中的内部方法会通过Object.defineProperty定义到function.prototype,例如:

class Person {  speak () {    console.log('I am Person!')   }}复制代码

会被Babel转成:

function Person(){}Object.defineProperty(Person.prototype,'speak',{  value: function () { 'I am Person!' },  enumerable: false,  configurable: true,  writable: true})复制代码

  Decorator函数接受的参数与Object.defineProperty类似,与对类(class)的方法使用@decorator,接受到的方法分别是类的prototype,内部方法名和描述符,@decorator会在调用Object.defineProperty前劫持,先调用Decorator函数,将返回的descriptor定义到类的prototype上。

  例如:

function readonly(target, key, descriptor) {  //可以通过修改descriptor参数实现各种功能  descriptor.writable = false  return descriptor}class Person {  @readonly  speak () {    return 'I am Person!'  }}const person = new Person();person.speak = ()=>{    console.log('I am human')}复制代码

面向类的@decorator

  当我们对一个class使用@decorator时,接受到的参数target是类本身。例如:   

function name (target) {  target.name = 'wang'}@nameclass Person {}console.log(Dog.name)//'wang'复制代码

  讲完了@decorator,现在让我们回到JSX中,和两个库都提供了mixin函数可用。大致让我们看一下core-decorators库中mixin的大致代码:

function handleClass(target, mixins) {  if (!mixins.length) {    throw new SyntaxError(`@mixin() class ${target.name} requires at least one mixin as an argument`);  }  for (let i = 0, l = mixins.length; i < l; i++) {    const descs = getOwnPropertyDescriptors(mixins[i]);    const keys = getOwnKeys(descs);    for (let j = 0, k = keys.length; j < k; j++) {      const key = keys[j];      if (!(hasProperty(key, target.prototype))) {        defineProperty(target.prototype, key, descs[key]);      }    }  }}export default function mixin(...mixins) {  if (typeof mixins[0] === 'function') {    return handleClass(mixins[0], []);  } else {    return target => {      return handleClass(target, mixins);    };  }}复制代码

@mixin使用如下:

import { mixin } from 'core-decorators';const SingerMixin = {  sing(sound) {    alert(sound);  }};const FlyMixin = {  fly() {},  land() {}};@mixin(SingerMixin, FlyMixin)class Bird {  singMatingCall() {    this.sing('tweet tweet');  }}var bird = new Bird();bird.singMatingCall();复制代码

  我们可以看到mixin函数相当于采用Currying的方式接受mixins数组,返回

return target => {      return handleClass(target, mixins);};复制代码

handleClass函数的大致作用就是采用defineProperty将mixins数组中的函数定义在target.prototype上,这样就实现了mixin的要求。

Mixin在React中的作用

  讲了这么多Mixin的东西,那么Mixin在React中有什么作用呢?Mixin的作用无非就是在多个组件中共享相同的方法,实现复用,React中的Mixin也是相同的。比如你的组件中可能有共同的工具方法,为了避免在每个组件中都有相同的定义,你就可以采用Mixin。下面依旧举一个现实的例子。

  React的性能优化一个非常常见的方法就是减少组件不必要的render,一般我们可以在生命周期shouldComponentUpdate(nextProps, nextState)中进行判断,通过判断nextPropsnextStatethis.prosthis.state是否完全相同(浅比较),如果相同则返回false,表示不重新渲染,如果不相同,则返回true,使得组件重新渲染(当然你也可以不使用mixin,而使用React.PureComponent也可以达到相同的效果)。并且现在有非常多的现成的库提供如上的功能,例如react-addons-pure-render-mixin中提供了PureRenderMixin方法,首先我们可以在项目下运行:

npm install --save react-addons-pure-render-mixin;复制代码

然后在代码中可以如下使用

import PureRenderMixin from 'react-addons-pure-render-mixin';import {decorate as mixin} from 'react-mixin'@mixin(PureRenderMixin)class FooComponent extends React.Component {  constructor(props) {    super(props);  }  render() {    return 
foo
; }}复制代码

当然你也可以这样写:

var PureRenderMixin = require('react-addons-pure-render-mixin');React.createClass({  mixins: [PureRenderMixin],  render: function() {    return 
foo
; }});复制代码

甚至这样写:

import PureRenderMixin from 'react-addons-pure-render-mixin';class FooComponent extends React.Component {  constructor(props) {    super(props);    this.shouldComponentUpdate = PureRenderMixin.shouldComponentUpdate.bind(this);  }  render() {    return 
foo
; }}复制代码

  因为@decorator是ES7的用法,所以必须使用Babel才能使用,所以我们需要在.babelrc文件中设置:

{  "presets": ["es2015", "stage-1"],  "plugins": [    "babel-plugin-transform-decorators-legacy"  ]}复制代码

并安装插件:

npm i babel-cli babel-preset-es2015 babel-preset-stage-1 babel-plugin-transform-decorators复制代码

  之后我们就可以尽情体验ES7的decorator了!

转载地址:http://uxvhl.baihongyu.com/

你可能感兴趣的文章
标准W3C盒子模型和IE盒子模型CSS布局经典盒子模型(转)
查看>>
Ubuntu上hadoop集群安装[转]
查看>>
汇编实现点亮Led灯(2440)
查看>>
halcon算子翻译——fit_surface_second_order
查看>>
JDBC获取ResultSet的MetaData
查看>>
nginx重启命令
查看>>
901学科类非正版软件下载与安装
查看>>
3-4: 一元多项式的乘法与加法运算
查看>>
Lerning Entity Framework 6 ------ Defining Relationships
查看>>
linux服务器-客户端的最小模型
查看>>
记录一次bug。asp.net 编译后 页面一刷新就报错,在刷新就正常。 (vs2005)
查看>>
Java环境准备
查看>>
Swift3.0P1 语法指南——函数
查看>>
Swift3.0P1 语法指南——下标
查看>>
关于java如何传参的试验
查看>>
Linux下修改Mysql的用户(root)的密码
查看>>
Jenkins+Git+Maven搭建自动化构建平台
查看>>
更新服务
查看>>
Python随笔
查看>>
Python新建/删除文件夹
查看>>