[关闭]
@duanyubin 2016-05-17T01:59:55.000000Z 字数 4359 阅读 341

萝卜项目分享


Immutable.js

目的: 提高性能,减少render方法执行次数
使用: 在全局store(state collection)中使用Immutable实例,component之前传递的pros也是Immutable实例。why?
如果component之前传递的是普通的object,PureRenderMixin无法达到预期的效果

  1. const INITIAL_STATE = fromJS({
  2. barrage: {
  3. list: [],
  4. connected: true
  5. },
  6. anchor: {},
  7. video: {},
  8. hot: {
  9. list: [],
  10. loading: false
  11. },
  12. room: { ...params }
  13. })
  14. // HotItem.jsx
  15. <div className="img-wrap">
  16. <img src={data.get('img_url')} />
  17. <span className="status">{data.get('state') === 1 && '重播'}{data.get('state') === 2 && '直播'}</span>
  18. <Count favourCount={data.get('like_num')} userCount={data.get('total_num')} />
  19. </div>

如果component依赖的props为基本类型,则传递非Immutable实例

  1. <Count favourCount={data.get('like_num')} userCount={data.get('total_num')} />
  2. // Count.jsx
  3. render() {
  4. const { favourCount, userCount } = this.props
  5. return <div className="m-count">
  6. <span className="favourcount">{favourCount}</span>
  7. <span className="usercount">{userCount}</span>
  8. </div>
  9. }

Unit Test (单元测试)

如果发现些单元测试无从下手,可能就是代码没有做到单一职责的原则
几个原则:

  1. 纯函数
  2. 职责单一
  3. 低耦合高内聚
  4. 较少依赖
  5. 尽量高的测试覆盖率

在小项目中,可以不对UI层写单元测试,前提是业务逻辑尽量少的存在于UI层。
比较下列两种写法

  1. // 1
  2. componentDidMount() {
  3. if (this.props.a) {
  4. ajaxCall()
  5. } else {
  6. fetchFromLocalStorage()
  7. }
  8. }
  9. // 2
  10. componetDidMount() {
  11. this.props.fetch(this.props.a)
  12. }
  13. // actions.js
  14. export function fetch(type) {
  15. }

如何写:
Full-Stack Redux Tutorial
BDD开发模式,先写测试用例,再写被测函数
具体:

  1. // 1. 安装下列包
  2. $ npm i --savedev mocha chai chai-immutable
  3. // 2. 增加chai对immutable的支持
  4. // test_helper.js
  5. import chaiImmutable from 'chai-immutable';
  6. chai.use(chaiImmutable);
  7. // 3. 增加npm script
  8. // package.json
  9. "scripts": {
  10. ...
  11. "test": "cross-env NODE_ENV=production mocha --compilers js:babel-core/register --require ./test/test_helper.js \"./test/**/*.@(js|jsx)\"",
  12. "test:watch": "cross-env NODE_ENV=production npm run test -- --watch --watch-extensions js,jsx"
  13. ...
  14. }
  15. // 4. 写测试用例
  16. // reduce_spec.js
  17. describe('Test reducer', () => {
  18. it('should handle FETCH_INIT_INFO', () => {
  19. // ...
  20. expect(reducer(initialState, action).get('room')).to.eql(fromJS({
  21. "domain": "localhost",
  22. "port": 9999,
  23. "userId": "temp3c628f37-dd30-43c8-875f-10c13c00efa8"
  24. }))
  25. })
  26. })

注意要点:
1. 测试用例中,不应该copy被测函数的逻辑,而是应该直接写明期望值
2. 如果要对UI层进行测试,需要用的jsdom等模拟库

Function Programing (函数式编程)

优势

  1. 通用性: 不依赖外部变量,不会改变外部变量的值,可移植
  2. 可靠性: 同样的收入肯定得到同样的输出
  3. 可测试性: 非常容易的写单元测试

原文地址

Pure Function(纯函数)

纯函数无副作用,仅仅依赖于函数的输入,并且当输入相同时输出保持一致

  1. // 纯函数
  2. const add10 = (a) => a + 10
  3. // 依赖于外部变量的非纯函数
  4. let x = 10
  5. const addx = (a) => a + x
  6. // 会产生副作用的非纯函数
  7. const setx = (v) => x = v

compose(组合)

  1. const add1 = (a) => a + 1
  2. const times2 = (a) => a * 2
  3. const compose = (a, b) => (c) => a(b(c))
  4. const add1OfTimes2 = compose(add1, times2)
  5. add1OfTimes2(5) // => 11
  1. list.map(inc).map(isZero) // => [true, false, false]
  2. list.map(compose(isZero, inc)) // => [true, false, false]

一般,第二种的性能是第一种的2倍
借助函数组合,我们可以通过将多个小函数结合在一起来构建更复杂的数据变化

  1. const formalGreeting = (name) => `Hello ${name}`
  2. const casualGreeting = (name) => `Sup ${name}`
  3. const male = (name) => `Mr. ${name}`
  4. const female = (name) => `Mrs. ${name}`
  5. const doctor = (name) => `Dr. ${name}`
  6. const phd = (name) => `${name} PhD`
  7. const md = (name) => `${name} M.D.`
  8. formalGreeting(male(phd("Chet")))
  9. const identity = (x) => x
  10. const greet = (name, options) => {
  11. return pipe([
  12. // greeting
  13. options.formal ? formalGreeting :
  14. casualGreeting,
  15. // prefix
  16. options.doctor ? doctor :
  17. options.male ? male :
  18. options.female ? female :
  19. identity,
  20. // suffix
  21. options.phd ? phd :
  22. options.md ?md :
  23. identity
  24. ])(name)
  25. }

Curry(柯里化)

在调用一个函数的时候传入更少的参数,而这个函数会返回另外一个函数并且能够接收其他参数

具体可参考这篇文章
优点:
1. 重复利用小片段代码
2. 函数可配置
2. 只使用函数完成任务

  1. var objects = [{ id: 1 }, { id: 2 }, { id: 3 }]
  2. objects.map(function(o){ return o.id })

MAP over OBJECTS to get IDS

  1. var get = curry(function(property, object){ return object[property] })
  2. objects.map(get('id')) //= [1, 2, 3]

函数可配置

  1. // R -> [Ramda](http://ramdajs.com/0.21.0/index.html)
  2. const add = R.curry((a, b) => a + b)
  3. add(1, 2) // => 3
  4. const add1 = add(1)
  5. add1(2) // => 3
  6. add1(10) // => 11

引用的透明性和不可变

大多数程序中,比较变量是否相等是比较变量的引用所指向的内存地址

  1. {} == {} // false
  2. [] == [] // false
  3. [1,2] == [1,2] // false

但是,在数学概念上,上述三者是相等的,所以在程序中,如果要判断这种相等,就需要深比较,但是存在性能问题。
为了保证纯函数的特性,不能够在没有改变引用的情况下来改变一个变量,但是每次都要深层拷贝赋值,又会造成性能上的损失,所以,需要依靠结构共享,Persistent data structure(持久化数据结构),常见的例子是链表:
1. 比较两个链表时先比较尾部引用,如果相同,则两链表也相等,反之,需要遍历两个链表;
2. 添加某个值时,不需要复制整个表到内存中,而只是增加一个链接,然后记录这个引用O(1);
对这些不可以变数据结构进行计算得到相应hash值,每次比较只需要先比较引用,再比较此值,就能得到两个数据结构是否是值相等,这就是Immutable.js主要做的。 (Hash array mapped trie)[https://en.wikipedia.org/wiki/Hash_array_mapped_trie]

延迟计算

  1. // 一般的计算方式,从内到外
  2. square(3 + 4)
  3. square(7) // 计算最内层的表达式
  4. 7 * 7
  5. 49
  6. // 从外到内的计算方式
  7. square(3 + 4)
  8. (3 + 4) * (3 + 4) // 计算最外层的表达式
  9. 7 * (3 + 4)
  10. 7 * 7
  11. 49
  12. // 优化
  13. square(3 + 4)
  14. (3 + 4) * (3 + 4) // 计算最外面的表达式
  15. 7 * 7 // 由于引用共享的存在,计算此时减少了一步
  16. 49

Lazy.js
Lazy.js
在 ES2015 中,确实为此实现了一个标准,并且称为函数 generator

  1. const results = _.chain(people)
  2. .pluck('lastName')
  3. .filter((name) => name.startsWith('Smith'))
  4. .take(5)
  5. .value()

一般做法:完成这件事最原始的方式就是将所有的名字拣出来,过滤整个数组,然后使用前 5 个。这就是 Underscore.js 以及绝大多数类库的做法。
使用 generator: 我们可以使用延迟计算 每次仅计算一个值,直到我们拿到了以 『Smith』开头的名字
上述代码,虽然最终是需要前五个take(5),但是在lodash里,要遍历5次。如果惰性求值,只需要遍历一次

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注