dob 使用篇,思考数据流的价值, 固定数据流方案难以覆盖所有场景,数据流同时作为项目中的一环十分重要,但是作为管理数据流向相对没有那么重要,Redux 还是占主流方向。
- 使用 redux 时分不清要不要将结构化数据拍平,然后再分别订阅,或者分不清订阅后数据处理放组件上还是全局,从组件类型来看,可以分别普通组件和业务组件,普通组件不使用数据流,业务组件绑定全局数据流
-
数据流本身有分形功能,数据流可用可不用,所谓具有分形功能的数据流,是贴着 react 分形功能,包装成具有分形能力的组件:
import {combineStores, observable, inject, observe} from 'dob' import {Connect} from 'dob-react' @observable class Store {name = 123} class Action { @inject(Store) store: Store changeName = () => {this.store.name = 456} } const stores = combineStore({Store,Action}) @Connect(stores) class App extends React.component<typeof stores, any> { render() { return <div onClick={this.props.Action.changeName}>{this.props.Store.name}</div> } } ReactDOM.render(<App />, document.getElementById('react-dom'))
- 数据流+ 组件具备完全分形能力,但如果这个组件对 props 有响应式要求,还是需要对该数据流框架有隐形依赖,props 数据 react 的通用连接桥梁,此组件只应该依赖普通对象的 props,内部在进行 observable 化,使得具备可迁移能力
-
数据驱动视图,可以将业务组件和数据的连接放到顶层管理,一般页面通过顶层包裹 Provider 实现
import {combineStores, observable, inject, observe} from 'dob' import {Connect} from 'dob-react' @observable class Store = {name = 123} class Action = { @inject(Store) store: Store changeName = () => {this.store.name = 456} } const stores = combineStores({Store, Action}) ReactDOM.render( <Provider {...store}> <APP /> </Provider> , document.getElementById('react-dom'))
-
注册了全局 Provider 后,会默认透传到 Connect 中去,与分形相反,组件无法在其他项目单独使用,分形组件对结构强依赖,只需要给定的 props 就可以完成功能,全局数据流组件几乎完全不依赖结构,所有 props 从全局 store 获取
-
组件可以分为业务耦合和非业务耦合,业务耦合组件依赖全局数据流,非业务耦合组件保持分形能力
- 对于 MV*思想的库,Connect 概念不仅在于注入数据,还会监听数据变化触发 rerender
- 没有数据流的组件不需要 Connect,业务组件保持未来不确定性,所以可以保持每个业务最贱的 Connect
- store 扁平化原因是 js 对不可变数据流的支持力度不够,催生 immutable.js 出现,不过对于可变数据和不可变数据都有使用场景,扁平化本质是要解决数据格式规范问题, 但是正确的数据格式化才是追求的核心重点
-
使用 dob 时,异步后复制需要非常小心
@Action async getUserInfo() { const UserInfo = await fetchUser() this.store.user.data = userInfo // 严格模式下报错,因为脱离了Action作用域 }
-
这里的 await 只是假装用同步写异步,await 开始时,当前函数栈已经退出,后续代码不是在一个 Action 中,。一般解法用显示申明 Action
@Action async getUserInfo() { const userInfo = await fetchUser() Action(() => { this.store.user.data = userInfo }) }
-
-
只要涉及数据流变化的操作都应该是同步的
-
响应式框架可以自动触发请求触发操作等等,希望请求参数改变可以自动触发请求执行
componentWillMount() { this.fetch({url: this,props.url, userName: this.props.userName}) } componentWillReceiveProps(nextProps) { if(nextProps.url !== this.props.url || nextProps.userName !== this.props.userName) { this.fetch({url: nextProps.url, userName: nextProps.userName}) } }
- observe 有点像自动化的 addEventListener
- 优化 typeScript 下 redux 类型的推导
- 使用依赖注入
-
编写类级别的装饰器,统一捕获 Action 的异常并抛出
const errorCatch = (errorHandler?: (error ?: Error) => void) => (target: any) => { Object.getOwnPropertyNames(target.prototype).forEach(key => { const func = target.prototype[key] target.prototype[key] = async(...args: any[]) => { try { await func.apply(this, args) } catch(error) { errorHandler && errorHandler(error) } } }) return target }
- 任意步骤触发异常,await 之后的代码将停止执行,并将异常上报到前端监控平台
- 准确区分业务和非业务组件,写代码前得先设计数据流的依赖关系,异步时注意分离, 可以解决大部分业务场景的问题,遇到特殊情况可以使用 observe 监听数据变化,从而拓展出自动触发重发等功能,维护好数据流有利于保持整个项目的可维护性。