caige

caige 查看完整档案

深圳编辑中南大学  |  软件工程 编辑自主创业兼职接项目养团队  |  cxo 编辑 www.yudianer.com/#/user/2 编辑
编辑
_ | |__ _ _ __ _ | '_ \| | | |/ _` | | |_) | |_| | (_| | |_.__/ \__,_|\__, | |___/ 该用户太懒什么也没留下

个人动态

caige 赞了文章 · 2018-03-27

React进阶——使用高阶组件(Higher-order Components)优化你的代码

什么是高阶组件

Higher-Order Components (HOCs) are JavaScript functions which add functionality to existing component classes.

通过函数向现有组件类添加逻辑,就是高阶组件。

让我们先来看一个可能是史上最无聊的高阶组件:

function noId() {
  return function(Comp) {
    return class NoID extends Component {
      render() {
        const {id, ...others} = this.props;
        return (
          <Comp {...others}/>
        )
      }
    }
  }
}

const WithoutID = noId()(Comp);

这个例子向我们展示了高阶组件的工作方式:通过函数和闭包,改变已有组件的行为——这里是忽略id属性——而完全不需要修改任何代码。

之所以称之为高阶,是因为在React中,这种嵌套关系会反映到组件树上,层层嵌套就好像高阶函数的function in function一样,如图:

HOC-img

从图上也可以看出,组件树虽然嵌套了多层,但是实际渲染的DOM结构并没有改变。
如果你对这点有疑问,不妨自己写写例子试下,加深对React的理解。现在可以先记下结论:我们可以放心的使用多层高阶组件,甚至重复地调用,而不必担心影响输出的DOM结构。

借助函数的逻辑表现力,高阶组件的用途几乎是无穷无尽的:

适配器

有的时候你需要替换一些已有组件,而新组件接收的参数和原组件并不完全一致。

你可以修改所有使用旧组件的代码来保证传入正确的参数——考虑改行吧如果你真这么想

也可以把新组件做一层封装:

class ListAdapter extends Component {
    mapProps(props) {
        return {/* new props */}
    }
    render() {
        return <NewList {...mapProps(this.props)} />
    }
}

如果有十个组件需要适配呢?如果你不想照着上面写十遍,或许高阶组件可以给你答案

function mapProps(mapFn) {
    return function(Comp) {
        return class extends Component {
            render() {
                return <Comp {...mapFn(this.props)}/>
            }
        }
    } 
}

const ListAdapter = mapProps(mapPropsForNewList)(NewList);

借助高阶组件,关注点被分离得更加干净:只需要关注真正重要的部分——属性的mapping。

这个例子有些价值,却仍然不够打动人,如果你也这么想,请往下看:

处理副作用

纯组件易写易测,越多越好,这是常识。然而在实际项目中,往往有许多的状态和副作用需要处理,最常见的情况就是异步了。

假设我们需要异步加载一个用户列表,通常的代码可能是这样的:

class UserList extends Component {
  constructor(props) {
    super();
    this.state = {
      list: []
    }
  }
  componentDidMount() {
    loadUsers()
      .then(data=> 
        this.setState({list: data.userList})
      )
  }
  render() {
    return (
      <List list={this.state.list} />
    )
  }
  /* other bussiness logics */
}

实际情况中,以上代码往往还会和其它一些业务函数混杂在一起——我们创建了一个业务副作用混杂的、有状态的组件。

如果再来一个书单列表呢?再写一个BookList然后把loadUsers改成loadBooks ?
不仅代码重复,大量有状态和副作用的组件,也使得应用更加难以测试。

也许你会考虑使用Flux。它确实能让你的代码更清晰,但是在有些场景下使用Flux就像大炮打蚊子。比如一个异步的下拉选择框,如果要考虑复用的话,传统的Flux/Reflux几乎无法优雅的处理,Redux稍好一些,但仍然很难做优雅。关于flux/redux的缺点不深入,有兴趣的可以参考Cycle.js作者的文章

回到问题的本源:其实我们只想要一个能复用的异步下拉列表而已啊!

高阶函数试试?

import React, { Component } from 'react';

const DEFAULT_OPTIONS = {
  mapStateToProps: undefined,
  mapLoadingToProps: loading => ({ loading }),
  mapDataToProps: data => ({ data }),
  mapErrorToProps: error => ({ error }),
};
export function connectPromise(options) {
  return (Comp) => {
    const finalOptions = {
      ...DEFAULT_OPTIONS,
      ...options,
    };
    const {
      promiseLoader,
      mapLoadingToProps,
      mapStateToProps,
      mapDataToProps,
      mapErrorToProps,
    } = finalOptions;

    class AsyncComponent extends Component {
      constructor(props) {
        super(props);
        this.state = {
          loading: true,
          data: undefined,
          error: undefined,
        };
      }
      componentDidMount() {
        promiseLoader(this.props)
          .then(
            data => this.setState({ data, loading: false }),
            error => this.setState({ error, loading: false }),
          );
      }
      render() {
        const { data, error, loading } = this.state;

        const dataProps = data ? mapDataToProps(data) : undefined;
        const errorProps = error ? mapErrorToProps(error) : undefined;

        return (
          <Comp
            {...mapLoadingToProps(loading)}
            {...dataProps}
            {...errorProps}
            {...this.props}
          />
        );
      }
    }

    return AsyncComponent;
  };
}


const UserList = connectPromise({
    promiseLoader: loadUsers,
    mapDataToProps: result=> ({list: result.userList})
})(List); //List can be a pure component

const BookList = connectPromise({
    promiseLoader: loadBooks,
    mapDataToProps: result=> ({list: result.bookList})
})(List);

不仅大大减少了重复代码,还把散落各处的异步逻辑装进了可以单独管理和测试的笼子,在业务场景中,只需要纯组件 + 配置 就能实现相同的功能——而无论是纯组件还是配置,都是对单元测试友好的,至少比异步组件友好多了。

使用curry & compose

高阶组件的另一个亮点,就是对函数式编程的友好。你可能已经注意到,目前我写的所有高阶函数,都是形如:

config => {
    return Component=> {
        return HighOrderCompoent
    }
}

表示为config=> Component=> Component

写成嵌套的函数是为了手动curry化,而参数的顺序(为什么不是Component=> config=> Component),则是为了组合方便。关于curry与compose的使用,可以移步我的另一篇blog

举个栗子,前面讲了适配器和异步,我们可以很快就组合出两者的结合体:使用NewList的异步用户列表

UserList = compose(
  connectPromise({
    promiseLoader: loadUsers,
    mapResultToProps: result=> ({list: result.userList})
  }),
  mapProps(mapPropsForNewList)
)(NewList);

总结

在团队内部分享里,我的总结是三个词 Easy, Light-weight & Composable.

其实高阶组件并不是什么新东西,本质上就是Decorator模式在React的一种实现,但在相当一段时间内,这个优秀的模式都被人忽略。在我看来,大部分使用mixinclass extends的地方,高阶组件都是更好的方案——毕竟组合优于继承,而mixin——个人觉得没资格参与讨论。

使用高阶组件还有两个好处:

  1. 适用范围广,它不需要es6或者其它需要编译的特性,有函数的地方,就有HOC。

  2. Debug友好,它能够被React组件树显示,所以可以很清楚地知道有多少层,每层做了什么。相比之下无论是mixin还是继承,都显得非常隐晦。

值得庆幸的是,社区也明显注意到了高阶组件的价值,无论是大家非常熟悉的react-reduxconnect函数,还是redux-form,高阶组件的应用开始随处可见。

下次当你想写mixinclass extends的时候,不妨也考虑下高阶组件。

查看原文

赞 29 收藏 88 评论 18

caige 回答了问题 · 2018-03-23

解决egg 如何后台执行某个任务?

https://github.com/eggjs/egg/...
框架提供的runInBackground做这个是不是很合适?

关注 3 回答 2

caige 关注了问题 · 2017-12-29

jssdk调用chooseImage上传gif动态图片后,得到的localIds放到src下显示的图片不会动怎么办?

wx.chooseImage({
    count: 1, // 默认9
    sizeType: ['original', 'compressed'], // 可以指定是原图还是压缩图,默认二者都有
    sourceType: ['album', 'camera'], // 可以指定来源是相册还是相机,默认二者都有
    success: function (res) {
        var localIds = res.localIds; // 返回选定照片的本地ID列表,localId可以作为img标签的src属性显示图片
        image = {}
        image.url = localIds // 网页标签 <img class="img-pic" :data-original="image.url"> 选择的gif不会动
    }
});

前端用的是vue

关注 2 回答 0

caige 赞了文章 · 2017-12-29

微信公众号:如何一键导出微信所有文章

clipboard.png

一键导出微信所有文章(PDF 、Excel 、 Word)

微信公众号阅读成为许多人每天的日常习惯,相信在每天关注的微信公众号中,总会有一些作者在持续地输出优质内容。

这些内容也许是你所处领域的精华,也许其中的观点启人深思,也许是单纯地喜欢上了某个大V的文字。而微信又是每日最频繁的沟通工具,生活中是否会时常遇到这样的场景:上班路上,一边整酣畅地阅读公众号刚更新的内容,突如其来的一条微信消息不得不让你中断退出,等回复完,刚刚看到的一半内容和公众号又不知道跑哪去了。以至于你的脑中时不时冒出这么一个想法:如果可以将此微信公众号所有的文章都保存下来,做成电子书格式,放到阅读器里阅读,该多么方便。

但当看到众多的历史文章,想一篇一篇地查看,然后发送到电脑浏览器,手工另存为文档,会立即让人望而生畏。

此时,作为一枚程序猿的兴趣被激发了:这些文章既然是保存在微信服务器上的,如果能通过接口批量拿到,转换成需要的pdf格式,岂不是就解决了这个问题?

搜狗搜索的“微信”一栏提供了一个思路突破口,通过HTTP请求,可以抓取搜狗的网站内容信息,包含公众号名称,文章发布时间,标题,内容详情等等。

不过问题来了,微信虽然也集成了搜狗搜索,不过对于公众号的内容,搜狗也只能拿最多最近的10篇文章。

个人的微信可以查看所有历史文章,但是需要手工翻页,而且发出来的文章链接24小时之后就失效了。而且搜狗对爬虫做了反爬限制,程序抓取极不稳定。

直到看了某位大神的帖子,通过中间代理,在客户端和服务器中间建立一个消息处理流程,将拿到的链接一一解析处理,才把整个过程自动抓取给实现出来,不过仍然耗费少量的手工工作量。

导出来的pdf大致如下:

clipboard.png

可查看链接:

http://okuodho27.bkt.clouddn....

在此分享给需要的朋友,需要的朋友可填写下面表单:

http://weixin2pdf.mikecrm.com...

查看原文

赞 1 收藏 4 评论 0

caige 赞了回答 · 2017-12-03

iscroll-probe容器里面设置fixed无效?

使用了iscroll就是这样,这时候窗口不再是window,而是iscroll的主窗口。只能避免使用fixed。

关注 4 回答 3

caige 赞了回答 · 2016-12-19

解决想用rem给width与height进行赋值时,该怎么计算呢?以谁为参照物呢?

其中的rem原理我就不说了,我想说的就是,rem,真的不能用在height和width中, 在height和width中,一般情况下使用百分比就可以了.
在width里面使用rem的话,你会发现, 他计算出来的数值,和你想要的不一样.
举个例子吧:
当你在html里面设置的font-size为10px的话,

html{
    font-size:10px;
}
div{
    font-size:2rem;  //得到的结果是20px 你会微微一笑,艹,你骗我呢. 不急,show u code
}
div{
    height: 2rem;  //这时候,请打开你的控制台,观察div的高度,你会发现是24px;TM!!!怎么不是20px呢
}

原理就是, font-size 对于height 和 width 而言最小值是 12px, 而对于font-size而言, 你想设多少,就设置多少。 这个无可厚非。
咳咳~~
所以综上所述: 在height 里面一般使用 百分比就够了(你整个12px 你自己也不好计算呀,而且如果动态的时候真的给你整个 <12px的,到时候哭都来不及).
在font-size里面,最好就使用rem, 因为这个单位是专门为他开发的.

关注 6 回答 4

caige 回答了问题 · 2016-11-17

微信开发文档第三步:刷新access_token这个有什么用

注意看这一段:

关于网页授权access_token和普通access_token的区别

  1. oauth的access_token:第一次进入你的网站时进行一次授权,获取用户的信息(openid)。

  2. 普通access_token:调用微信的rest接口时,微信服务器都需要进行安全认证。将此access_token带在url参数中。

现在各种语言都有相应的sdk帮忙获取“普通access_token”,并做超时自动重新获取。如果是用来做项目的话,可以不用造轮子了,github上面一大堆。除了access_token的功能外,还封装了各个接口的各种sdk。

关注 7 回答 6

caige 关注了问题 · 2016-11-17

微信开发文档第三步:刷新access_token这个有什么用

http://mp.weixin.qq.com/wiki/...

微信文档中说的刷新access_token有什么用?要获取到用户的openid不是每次都要先获得code(唯一的吧?),再用code去获取access_token,再获取到openid,那把access_token刷新是什么意思?请微信开发的大神指导下、

关注 7 回答 6

caige 赞了回答 · 2016-11-16

解决微信支付的哪个环节会用到access_token

首先微信支付有四种方式:刷卡支付、公众号支付、扫码支付、APP支付。
https://pay.weixin.qq.com/wiki/doc/api/index.html

由于涉及到access_token,题主应该是公众号支付。

公众号支付后端代码包含两块:请求微信服务器创建订单、微信服务器支付成功回调,这两快的业务都用不到access_token。

再看前端,微信公众号内嵌HTML5前端支付有两套API:
1、https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=7_7&index=6
2、http://mp.weixin.qq.com/wiki/11/74ad127cc054f6b80759c40f77ec03db.html#.E5.8F.91.E8.B5.B7.E4.B8.80.E4.B8.AA.E5.BE.AE.E4.BF.A1.E6.94.AF.E4.BB.98.E8.AF.B7.E6.B1.82

如果你用的是1,那么也用不到access_token。

如果用的是2,那么首先要调用wx.config:
http://mp.weixin.qq.com/wiki/11/74ad127cc054f6b80759c40f77ec03db.html#.E6.AD.A5.E9.AA.A4.E4.B8.89.EF.BC.9A.E9.80.9A.E8.BF.87config.E6.8E.A5.E5.8F.A3.E6.B3.A8.E5.85.A5.E6.9D.83.E9.99.90.E9.AA.8C.E8.AF.81.E9.85.8D.E7.BD.AE

其中有一个参数是signature,再看signature是如何生成的:
http://mp.weixin.qq.com/wiki/11/74ad127cc054f6b80759c40f77ec03db.html#.E9.99.84.E5.BD.951-JS-SDK.E4.BD.BF.E7.94.A8.E6.9D.83.E9.99.90.E7.AD.BE.E5.90.8D.E7.AE.97.E6.B3.95

其中有一项是jsapi_ticket,jsapi_ticket要通过access_token获取。

所以的确有可能用到access_token。

关注 4 回答 2

caige 关注了问题 · 2016-11-15

解决使用vue cli 构建的项目,现在需要使用微信js sdk,如何使用?

在vue组件中
const wx = require('../assets/js/wx')
后直接报出错误,

error in ./src/assets/js/wx.js

  error        space-unary-ops        Unexpected space after unary operator '!'                                                                      
  /Users/Niok/Documents/Work/so/ga-piclike/src/assets/js/wx.js:1:1
  ! function(a, b) { "function" == typeof define && (define.amd || define.cmd) ? define(function() {
   ^

应该是es6的问题,但是现在没有解决思路,所以前来提问

第一次使用vue写正式项目但是在这里卡住了。
还有开发模式时候的请求数据也有问题希望能得到解答。
谢谢

更新直接引入后的结果,在模块中无法找到这个wx
clipboard.png

//更新
引入https://www.npmjs.com/package/weixin-js-sdk
可以在app.js看到jweixin的引入。
import wx from 'weixin-js-sdk'
但是当我在组件下打印
console.log(wx)
控制台报 undefined;

//更新 已经解决
使用
https://www.npmjs.com/package/weixin-js-sdk

//更新些最终解决方案
一:安装 weixin-js-sdk 这个模块 npm install weixin-js-sdk --save-dev
二:开始使用 import wx from 'weixin-js-sdk'

然后就可以使用了。wx.config 然后使用接口了。不过现在我还有问题

关注 22 回答 9

认证与成就

  • 获得 65 次点赞
  • 获得 5 枚徽章 获得 0 枚金徽章, 获得 1 枚银徽章, 获得 4 枚铜徽章

擅长技能
编辑

(゚∀゚ )
暂时没有

开源项目 & 著作
编辑

(゚∀゚ )
暂时没有

注册于 2015-09-29
个人主页被 891 人浏览