212017.12

把react组件转换为web component没有现成的知名库,所以自己写。

https://zhenyong.github.io/react/docs/webcomponents.html 这里介绍了如何自己写代码来把react转换为web component,但是有一个问题是,这样的web component一旦实例化之后就不能从外部改了。所以,还要想办法可以在修改web component的props的时候,重新去渲染内部的react组件,还好有 https://reactjs.org/blog/2015/10/01/react-render-and-top-level-api.html 这篇文章,告诉我们ReactDOM.render其实可以做到。所以,剩下的,就是如何监听web component的props的改变了。

17:30:13 已有0条回复
202017.12

从 webpack 到全面拥抱 Parcel #1 探索 Parcel

https://juejin.im/post/5a38e100f265da4324809297

这个parcel很吊啊,比脚手架还吊,它用到了文件束,效率比webpack快很多。我一直觉得webpack这种东西,浏览器默认支持import,或者网速达到一定快,或者http2全面支持,那它都没什么用了,真正有用的,是babel这类转译器。连gulp都比webpack强,gulp起码还是一个工程化工具,webpack呢?年轻都parcel啊,你涉世未深,马上就要挂了。

component和virtual dom的互动

没有手撸过virtual dom的人,肯定以为component就是virtual dom了,但实际上不是,virtual dom只是个依赖,生命周期这种东西只是component自己臆想的,跟virtual dom没半毛关系。我自己写hst-virtual-dom之后,大致梳理清楚了生命周期的问题。我们来看下使用hst-virtual-dom的代码:

let vdom = new HSTVirtualDOM({ template, state })
...
vdom.create()
...
vdom.mount('#app')
...
vdom.update(newState)
...
vdom.destroy()

看到来吧,这就是在完全没有component情况下,怎么使用一个virtual dom的过程。它提供来四个方法来进行操作,create是生成vtree,这个时候只有一个和DOMtree对应的js对象,这个对象就是vtree,完全按照DOMtree的结构生成,只不过它是一个js对象。mount动作通过createElement,利用vtree创建来真正的DOM结构,并挂载到传入的selector上。create和mount这两个动作其实可以自动处理,如果实例化的时候传入来selector,就自动调用这两个方法。

update和destroy是在外部调用的,不可以自动处理。update是传入新的state,和老的state merge之后更新界面。它的处理过程是,先通过create创建一个新的vtree,通过diff算法得到新老vtree之间的差异,然后把这些差异patch到真实的DOM节点上。destroy则是把创建的真实DOM,以及它们绑定的事件监听销毁。

一个component干了什么事呢?它其实是对virtual dom的封装,完全暴露给外面的,不是virtual dom的方法,反而是一些围绕virtual dom的辅助方法:

let component = new Component() 
->
· this.getDefaultProps()
· this.getInitialState() => state
· this.componentWillMount()
->
· this.render() => template,或者说render本身已经自建了vtree
this.vdom = new HSTVirtualDOM({ template, state })
->
· this.beforeCreate() // vue
this.vdom.create()
· this.created() // vue
->
this.beforeMount() // vue
this.vdom.mount(selector)
this.mounted() // vue
this.componentDidMount()
// 下面进入存在期
this.componentWillReceiveProps()
this.shouldComponentUpdate()
this.componentWillUpdate()
this.render() => newTemplate => this.vdom.template = newTemplate
this.beforeUpdate() // vue
this.vdom.update(newState)
this.componentDidUpdate()
this.updated() // vue
// 下面进入销毁期
this.componentWillUnmount()
this.beforeDestroy() // vue
this.vdom.destroy()
this.destroyed() // vue
// 这个时候对于hst-virtual-dom来说,vdom.vtree还在,如果想要彻底销毁,只需要this.vdom = null

这样你就可以看到,一个component是多么的不起眼,只不过在调用vdom的方法前不断干一些看上去是生命周期,实际上是一些在执行vdom操作前后的普通调用而已的函数。而对于一些初学者而言,拼命的研究生命周期函数,以为即掌握了react的精髓。

10:28:31 已有0条回复
182017.12

腾讯 Alloyteam 面向亿万级用户的Web同构直出

对datamanager.js的思想的解释

https://segmentfault.com/a/1190000012471745

17:40:26 已有0条回复
142017.12

在经过将近2个月的讨论之后,至今公司的一个方案实现还未落地,还停留在思想和少量实现代码上,具体的一些细节仍然是搁置状态。这让我再思考,对于开发者而言,如何评价能力?我觉得很多伟大的东西开发出来时,也不见得多么高大上,但是最重要的是,它以完整可用,且对于其它想了解它如何使用的开发者而言还比较容易入门,这就会让它得到更多关注。这也许是衡量一个成熟稳重的开发者的重要指标,如果有一个idea,能否用现有的,自己最熟练的技术,把它快速实现,并且可以正常使用不出大bug,同时,对于其它的开发者而言,可以很容易看懂怎么用它。

虽然有些思想在开发领域非常前卫,但并不代表它是可靠的,因为现在的开发生命周期越来越依赖社区,有人懂,有人用,才会有人继续贡献新代码。如果有一个想法,非常了不起,但是拖拖拉拉几年也没有实现,那么注定是失败的。而假如实现了,别人看不懂,也是失败的。这或许给我们一些启示,不要做那些看上去奇淫巧技的东西,真正能够解决问题的,是那些最容易上手的产品。

18:15:02 已有0条回复
302017.11

模拟代码帮助理解reselect的createSelector函数

reselect提供的createSelector API具有记忆功能,但是它的用法奇怪,让入门者比较难理解,我也废了不少力才理解到这块。现在通过模拟脚本,来协助理解。先来看下我们怎么创建一个selector函数:

let selector = createSelector([fun1, fun2], fun3)

在使用selector的时候,把它当作一个函数,传入state和props作为参数。接下来我们通过selector的反推来理解:

let someState = selector(state, props)
 <- 在mapStateToProps中这样用推导出 =>
let someState = (function(state, props) {
  let state1 = fun1(state, props)
  let state2 = fun2(state, props)
  return fun3(state1, state2)
})(state, props)

这样就可以非常容易的理解,createSelector传入的参数中的函数,各自在什么时候执行,执行的结果拿来干什么。

createSelector的第一个参数是个数组,数组的个数和第二个参数函数(fun3)的参数个数相同,这个怎么做到呢?其实很简单,用扩展运算符...或者用.apply都可以做到。

最后的问题是,如何做到记忆功能呢?

reselect的记忆功能的规则是,fun3的实参如果不变,那么说明它的运算结果也不变,可以直接返回缓存起来的结果。

所以,要使记忆功能生效,你必须保证fun3的实参不变,说白了,就是fun1, fun2的计算结果不变,因此fun1, fun2必须是返回固定值的函数。这种函数比pure function还要硬性,即使参数不同,也要永远返回一个值。当然,我们是不可能做到这样的,如果fun1依赖的state发生来变化,那么它的结果自然就会变,这个时候,fun3就不再返回缓存,而是重新计算结果,同时缓存新的结果,下次就可以用这个缓存了。这样,就做到selector的响应式。

最后的问题是,如果fun1, fun2的结果会随着props的不同而返回不同的结果呢?这种情况普遍存在,一个react组件可能在一个页面里面被多次使用,每次使用的时候props可能不同。这就会导致reselect的记忆功能失效。

解决的办法还是要从记忆功能的原理中去寻找。

每一个计算结果的缓存,与传入fun3的参数是一一对应的,fun3可以说是一个pure function,参数相同的情况下,得到的结果永远相同。有两种解决的想法:

  1. 为每一个组件设置单独的映射,这个可以通过react-redux的connect来实现,当mapStateToProps返回的是一个函数时,那么这个函数的运算结果仅对组件的当前实例生效,也就是说,在我们写mapStateToProps函数时,不能直接返回映射关系,而是返回一个函数,这个函数里面去做一些处理后再返回映射关系。下面有例子。
  2. 既然fun3的计算结果是根据参数来缓存的,那么我们可以尝试对参数做hash,固定的参数对应固定的fun3函数体,不同的参数对应不同的fun3函数体,当在不同的参数之间切换时,如果发现这个hash有存在的fun3函数体,那么就立即用它的缓存。下面也有例子。

想法1的例子:

const makeMapStateToProps = () => {
 const getSelector = makeSelector() // 下一段代码看makeSelector是怎么写的
 const mapStateToProps = (state, props) => {
  return {
   todos: getSelector(state, props)
  }
 }
 return mapStateToProps
}
export default connect(makeMapStateToProps)(MyComponent)
import { createSelector } from 'reselect'
function getSelector(state, props) {
  return state[props.id]
}

export function makeSelector() {
  return createSelector(
    [ getSelector ],
    (user) => {
      user.total_books = user.books.length
      return user
    },
  )
}

通过结合文章开头的推导代码,你会发现,每个组件的实例的props.id是一定的,因此对应的user也是一定的,那么每次都可以使用缓存起来的user。当然,如果props.id改变来,那么缓存就失效了。

想法2,对makeSelector做深度改造:

let selectors = {}
function makeSelector(uid) {
  if (selectors[uid]) return selectors[uid]
  let selector = createSelector(...)
}
function deleteSelector(uid) {
  delete selectors[uid]
}

在connect的时候,直接在makeSelector的时候传入props.id作为标记,mapStateToProps不再返回函数作为结果。当不再对uid对应的用户进行操作之后,要即时删除这个selector。

14:06:50 已有0条回复
282017.11

为什么需要react-redux提供的connect函数呢?redux的store作为一个可以全局可引用的对象,完全可以不依赖react-redux啊,只需要把store独立出来,在需要的地方直接import进来即可。

在经过一段时间的思考之后,终于明白一个道理,react组件是一个纯粹自我封闭的组件,数据流是单向的,这一点非常重要,如何决定数据流单向呢?完全依靠props的传递,也就是说如果在组件内引用store,通过getState来获取数据,就会造成数据来源不清晰的状况。因此,为了避免这种数据流混乱的局面,有必要在组件实例化时,将store中的数据绑定到组件的某个props上。connect函数就是做这样一件事,它把store中的state转化为组件的props,传递给组件,对于组件开发者而言,如果不考虑自己已经理解这套机理,那么只要遵循一个原则:但凡组件本身无法直接获取的数据,都通过props获取。另外一位开发者则可以在拿到这个组件之后,用connect函数对它进行包装,保证这些props是有值的。

由此可见,在react中,把握组件开发的核心思想非常重要。

10:03:00 已有0条回复
162017.11

在重写slickgrid.js的时候,我一直有一种疑惑,究竟应该沿用jquery还是采用react作为视图层面的引擎?说到这里,很多人可能完全还没体会到,jquery和react是同一层面的东西,它们都是用来操作dom,和view层打交道的工具。很多人会立即跳起来,react高端多了,它有virtual dom,不直接和dom打交道。可是说了这么多,真的在开发某个第三方库的时候考虑过这个问题吗?对于撰写一个第三方的组件,它对于使用的开发者而言,无所谓技术框架,它有自己完整的api,因此对于使用者而言,不用深入学习它背后依赖的是jquery还是react。

在选择视图层库时,还有其它的选择,比如一些模板引擎,甚至是框架,但是对我而言,仅考虑jquery和react。它们对比到底有哪些区别呢?

Jquery React
界面构建 $().append() Jsx+data
操作界面方式 $().doSomething() setState
编程方式 随机调用 生命周期
获取子节点 $().find() -
事件响应 $().on() 生命周期+原生事件响应
维护方式 单文件维护 父子组件查找

在构建界面这一点上,react胜出一大截,利用jsx+data的方式构建界面快且理解容易,描述直接。但是在操作界面细节的时候,react则反过来,通过操作state来操作界面,虽然抽象来很多,却在理解直接性上差了很多。

综合之后,我觉得,jquery依然是更好的选择,除了在构建界面上比react差很多以外,其它方面都是比较令人满意的。特别是当项目大到一定复杂程度的时候,react的结构,会让项目维护难度加大。而jquery不存在这种情况,无论你项目多大,jquery都能一针扎在你要操作的那个DOM节点上。

有没有一种方案,可以优化jquery的界面构建过程?例如先通过数据抽象出virtual dom,然后通过virtual dom构建界面,构建过程还是通过jquery,后续的所有维护都通过jquery?

12:48:38 已有0条回复
152017.11

别再拿奇技淫巧搬砖了