ITPub博客

首页 > 应用开发 > IT综合 > 前端演进思考

前端演进思考

原创 IT综合 作者:java06051515 时间:2019-12-03 20:50:07 0 删除 编辑

0. 从模型说起

图1

原生JS/jQuery 这时候开发者需要手动处理 BindChangeUpdate,jQuery 相比原生 JS,简化了操作,屏蔽了浏览器的兼容性。

三大框架 这时候开发者只需处理 BindChange,框架自动完成 Update。框架使用一套高效的 diff 算法最小化更新 DOM,因此除了简化开发者工作,还带来了前端性能的提升。

1. 组件思想

除此之外,框架另一个突出贡献是引入了 组件 思想,这使我们能将一个复杂的应用拆成多个组件逐个击破,还使我们能用一套基础组件组合出各种各样的应用,以高效的响应业务需求。

图2

但是在大型应用中,这种方式又出现 2个严重的问题

图3

跨组件数据传递 在单向数据流中,Props down & Event up**,跨组件的数据传递非常困难,如上图的,中间2个组件即使不关注数据,也必须写上相关代码让数据传下来。联想到现实中,我们说的组织层级过多导致流程冗杂,也是类似的道理。

图4

意外数据修改 JS 的数据类型和 Java 类似,除了少数几个基础类型是值,其他的都是 引用,因此在某个子组件里的数据修改,很容易影响到其他地方。尽管框架一再警告 props 应该看作只读的,但受制于语言特性,框架无法阻止意外的修改。

从开发者角度,我们也希望能 在一个地方就看到整个应用的状态,而不是深入到各个组件里去查看。

2. 状态管理

对于跨组件数据共享问题,一个自然的想法是:为啥不写一个公共的模块,把共享的变量都放到里面呢,之前写后端也是这样的呀?

前端还真不能这么干!根本原因在于我们要依赖框架的自动 Update 能力(第一张图),React 中 有且仅有两种情况会触发界面更新

  • state 变化( setState
  • context 变化(Redux)

(说 forceUpdate 的,看我眼神……)

于是 Redux 登场,理想情况下,数据流会被规整成下图那样:

图5

但是 Redux 也不是免费的,它带来两个我一直耿耿于怀的问题:

组件不能任意组合 组件连接 Redux 以后,即丧失了封闭性,如果页面上出现这样一个组件的两个实例,那这两个实例的状态就会互相影响。

割裂的逻辑 强行将逻辑拆成 actionreducer,一个功能要改3处代码,怎么看怎么蠢。

3. Maybe hooks?

较早前就听过 hooks,当时并不以为意,想着无非是给函数组件注入状态而已。数据和方法绑定,扒着指头数也就3种方式:

  • 模块
  • 闭包

类就挺直观的呀,干吗要要用闭包这种 tricky 方式去做?直到最近读文档,才发现这可能就是我一直心心念的 既要有清晰的数据流,又不割裂逻辑 的实现方式,抄 一段代码 感受感受:

class FriendStatusWithCounter extends React.Component {
  constructor(props) {
    super(props);
    this.state = { count: 0, isOnline: null };
    this.handleStatusChange = this.handleStatusChange.bind(this);
  }
  componentDidMount() {
    document.title = `You clicked ${this.state.count} times`;
    ChatAPI.subscribeToFriendStatus(
      this.props.friend.id,
      this.handleStatusChange
    );
  }
  componentDidUpdate() {
    document.title = `You clicked ${this.state.count} times`;
  }
  componentWillUnmount() {
    ChatAPI.unsubscribeFromFriendStatus(
      this.props.friend.id,
      this.handleStatusChange
    );
  }
  handleStatusChange(status) {
    this.setState({
      isOnline: status.isOnline
    });
  }
  // ...
function FriendStatusWithCounter(props) {
  const [count, setCount] = useState(0);
  useEffect(() => {
    document.title = `You clicked ${count} times`;
  });
  const [isOnline, setIsOnline] = useState(null);
  useEffect(() => {
    function handleStatusChange(status) {
      setIsOnline(status.isOnline);
    }
    ChatAPI.subscribeToFriendStatus(props.friend.id, handleStatusChange);
    return () => {
      ChatAPI.unsubscribeFromFriendStatus(props.friend.id, handleStatusChange);
    };
  });
  // ...
}

看,hooks 能不受 class 组件生命周期钩子的限制,将逻辑相关的代码集中到一起了!

4. 其他

state 里该放什么数据 由于我们使用 state 的本质是利用框架的自动更新 DOM 的能力,因此 state 里只需放 界面相关 的数据。

Redux 里该放什么数据 Redux 的职责更多是数据管理,开发实践中,随业务迭代接口数据变动会比较频繁,适宜放 Redux 中;另外跨组件共享的数据也要放 Redux 中。总结起来就是 界面相关 && (服务端的 || 共享的) 应该放 Redux 中。

那岂不是和数据全貌相差太大了 只把界面相关的数据放 state 和 redux 里,会不会与业务数据全貌相差太大了呢?我认为不会,在后端“读写分离”的实践中,一般做法只是将查询和写入的服务放到不同的实例上,而更进一步的做法是 查询用的表和写入用的表都是不一样的!state 和 Redux 就是类似这样的读模型;界面无关的数据,完全可以再放到一个公共模块里。

作者:杨少军

来自 “ ITPUB博客 ” ,链接:http://blog.itpub.net/31559758/viewspace-2666763/,如需转载,请注明出处,否则将追究法律责任。

请登录后发表评论 登录
全部评论

注册时间:2018-10-26

  • 博文量
    116
  • 访问量
    89200