zhuSass / blog

不定期更新
0 stars 0 forks source link

React Native入门基础篇 #3

Open zhuSass opened 4 years ago

zhuSass commented 4 years ago

导读

前端发展从早期的切图仔标签到现在的工程化,发展之迅猛的同时也面临着与C/S程序一样的问题,也就是系统化、模块化、规范化,经历过前端百家齐鸣的盛世,js前端在市场的影响力也越来越大,云端化催动着大批C/SB/S的趋势,虚拟domnode生态js工程师可以应用到桌面程序、移动原生应用、小程序、各种物流网嵌入式软件中,大有能用js编写的终会用js,连当时全球风靡的热播剧西部世界里头机器人的源码也有js,场景是这样的:剧中host(机器人)老鸨梅芙,突然发现了自己的身份,于是胁迫人类作业员,并招募其它host,计划逃出这个虚拟世界,却发现自己所有的行动仍然是代码编写出来的,于是就有了下面的剧照


我大js一统江湖啊,连人工智能也靠它写

好吧,以上有些吹水了。玩笑归玩笑,但是我还是想说一句话不管什么语言都要靠生态去推动,reactNative与wexx众多js跨端框架和Flutter这种究竟选哪个好,我个人观点比较看好js .逃)

应用程序的复杂程度越来越大,那么我们也有一个刚需需要面对如果对代码进行复用,在react和vue有一个Mixin(混入)这个术语,这篇我们来聊聊react中的mixin

mixin

先让我们看看在react中是如何使用的

var SetIntervalMixin = {
  componentWillMount: function() {
    this.intervals = [];
  },
  setInterval: function() {
    this.intervals.push(setInterval.apply(null, arguments));
  },
  componentWillUnmount: function() {
    this.intervals.forEach(clearInterval);
  }
};

var createReactClass = require('create-react-class');

var TickTock = createReactClass({
  mixins: [SetIntervalMixin], // 使用 mixin
  getInitialState: function() {
    return {seconds: 0};
  },
  componentDidMount: function() {
    this.setInterval(this.tick, 1000); // 调用 mixin 上的方法
  },
  tick: function() {
    this.setState({seconds: this.state.seconds + 1});
  },
  render: function() {
    return (
      <p>
        React has been running for {this.state.seconds} seconds.
      </p>
    );
  }
});

ReactDOM.render(
  <TickTock />,
  document.getElementById('example')
);

从上面我们就可以看出该react是对mixins这个参数名对象进行了封装,用当前组件可以使用SetIntervalMixin对象里的参数对象,看起来好像只是两个对象拼装,我们来简单的实现下。

首先要定义一个名叫mixins的函数,它是用来做混入的,接收一个参数list.

// 混入函数
function mixin(mixins: Function[]) {
    return (target: any) => {
        // 判断混入list是否为空
        if (!mixins.length) {
            console.warn(`混入对象是个空的,它必须是需要有值的`, );
            return;
        }
        mixins.forEach((mixinsItem: Function) => {
            // 获取对象里元素描述符
            let descriptors = handleGetOwnPropertyDescriptor(mixinsItem);
            // 将混入集合注入到目标对象的原型链里
            Object.keys(descriptors).forEach((key:string) => {
                defineProperty(target.prototype, key, descriptors[key]);
            })
        })
    }
}

然后我们就可以愉快的使用了。

// 主函数
@mixin([
    timeTools,
    weatherTool,
])
class Main {
    run: Function;
    handle: Function;
    title: string;
}

完整代码如下:

const {
    defineProperty,
    getOwnPropertyDescriptor,
} = Object;

// 混入函数
function mixin(mixins: Function[]) {
    return (target: any) => {
        // 判断混入list是否为空
        if (!mixins.length) {
            console.warn(`混入对象是个空的,它必须是需要有值的`, );
            return;
        }
        mixins.forEach((mixinsItem: Function) => {
            // 获取对象里元素描述符
            let descriptors = handleGetOwnPropertyDescriptor(mixinsItem);
            // 将混入集合注入到目标对象的原型链里
            Object.keys(descriptors).forEach((key:string) => {
                defineProperty(target.prototype, key, descriptors[key]);
            })
        })
    }
}
// 时间
const timeTools:any = {
    title: '时间',
    run() {
        console.log(`${this.title}:进行输出`)
    }
}
// 天气
const weatherTool:any = {
    name: '天气',
    handle() {
        console.log(`${this.name}:进行输出`)
    }
}
// 主函数
@mixin([
    timeTools,
    weatherTool,
])
class Main {
    run: Function;
    handle: Function;
    title: string;
}

const app = new Main();
app.run(); // log->时间:进行输出
app.handle(); // log->天气:进行输出

// 获取对象描述符
function handleGetOwnPropertyDescriptor(obj: Function):object {
    let descriptors = {};
    let keys = Object.keys(obj);
    keys.forEach(key => {
        descriptors[key] = getOwnPropertyDescriptor(obj, key);
    });

    return descriptors;
}

react里的mixin如果组件拥有多个 mixins,且这些 mixins 中定义了相同的生命周期方法(例如,当组件被销毁时,几个 mixins 都想要进行一些清理工作),那么这些生命周期方法都会被调用的。使用 mixins 时,mixins 会先按照定义时的顺序执行,最后调用组件上对应的方法。和我们刚刚实现并没有对此处理,会覆盖调,大体实现思路是这样。

mixin的缺点

// 此函数接收一个组件...
function withSubscription(WrappedComponent, selectData) {
  // ...并返回另一个组件...
  return class extends React.Component {
    constructor(props) {
      super(props);
      this.handleChange = this.handleChange.bind(this);
      this.state = {
        data: selectData(DataSource, props)
      };
    }

    componentDidMount() {
      // ...负责订阅相关的操作...
      DataSource.addChangeListener(this.handleChange);
    }

    componentWillUnmount() {
      DataSource.removeChangeListener(this.handleChange);
    }

    handleChange() {
      this.setState({
        data: selectData(DataSource, this.props)
      });
    }

    render() {
      // ... 并使用新数据渲染被包装的组件!
      // 请注意,我们可能还会传递其他属性
      return <WrappedComponent data={this.state.data} {...this.props} />;
    }
  };
}

HOC的出现可以解决这些问题:

HOC缺陷:

Hooks

Hook 是 React 16.8 的新增特性。它可以让你在不编写 class 的情况下使用 state 以及其他的 React 特性。一个简单例子看看它:

import React, { useState } from 'react';

function Example() {
  // 声明一个叫 “count” 的 state 变量。
  const [count, setCount] = useState(0);

  return (
    <div>
      <p>You clicked {count} times</p>
      <button onClick={() => setCount(count + 1)}>
        Click me
      </button>
    </div>
  );
}
zhuSass commented 3 years ago
<template>
    <div class="global-tableoperaction-wrap">
      <div class="tableoperaction-item">
        <slot v-for="item of slotNames.one"
              :name="item"
        ></slot>
      </div>
      <div v-if="(slotNames.two).length" class="tableoperaction-select">
        <el-dropdown>
          <div class="dropdown-main">‧‧‧</div>
          <el-dropdown-menu slot="dropdown" class="global-tableoperaction-wrap-dropdown">
            <el-dropdown-item v-for="(item, i) of slotNames.two" :key="i">
              <slot
                :name="item"
              ></slot>
            </el-dropdown-item>
          </el-dropdown-menu>
        </el-dropdown>
      </div>
    </div>
</template>

<script>
    export default {
        name: "V-tabel-operation",
        data() {
          return {
          }
        },
        computed: {
          slotNames: {
            cache:false,
            get: function () {
              let slotNames = Object.keys(this.$slots) || [];
              let oneNum = 3;
              let towNum = 3;
              if (slotNames.length == 4) {
                oneNum = 4;
                towNum = 4;
              }
              return {
                one: slotNames.slice(0, oneNum),
                two: slotNames.slice(towNum),
              };
            }
          },
        },
    }
</script>

<style lang="scss">
  .global-tableoperaction-wrap-dropdown {
    .el-dropdown-menu__item {
      padding: 0;
      &>span {
        display: block;
        padding: 0 20px;
      }
    }
  }
  .global-tableoperaction-wrap {
    display: flex;
    flex-wrap: wrap;
    .tableoperaction-item {
      display: flex;
      flex-wrap: wrap;
      &>span  {
        cursor: pointer;
        margin-right: 15px;
        color: #007EEF;
      }
    }
    .tableoperaction-select {
      .dropdown-main {
        color: #007EEF;
        cursor: pointer;
      }
    }
  }
</style>
<style scope>
  .el-dropdown-menu__item:focus, .el-dropdown-menu__item:not(.is-disabled):hover {
    color: #007EEF;
  }
</style>