youngwind / blog

梁少峰的个人博客
4.66k stars 385 forks source link

交互动画系列之一:解决bundle.js首次加载动画问题 #55

Open youngwind opened 8 years ago

youngwind commented 8 years ago

系列说明

嗯,要想成为一个专业的前端,不懂交互动画可不行。所以借着做项目,顺便好好研究一下这个方向,希望能给团队带来一些提升。

本篇要解决的问题

如果你用react+webpack做过单页面应用,那么你一定遇到过bundle.js太大导致首次加载时白屏时间过长,解决办法有几个方向。

  1. 从较少bundle.js体积大小入手
  2. 从服务端渲染入手
  3. 从加载中动画入手

    首次尝试

一开始我是这么想的:既然bundle.js还没加载完,那么我就把动画的DOM和CSS写在index.html里面就好了。

// index.html
<style>
// 加载中的动画样式
</style>

<div id='root'>
  // 添加一些加载中的动画DOM元素
</div>

结果:

  1. 在bundle.js加载完成之前确实会显示动画。
  2. 但是,一旦bundle.js加载完成,动画会瞬间消失,毫无过渡。

究其原因,无非是react在首次渲染真实DOM元素之前,会清空root里面的DOM元素。

再次尝试

找到原因之后我转换了一下思路:我能不能捕获到react即将渲染真实DOM元素,然后在那之前进行处理呢?比如这样子

// 顶层的container

componentWillMount:function(){
  // 给Index.html里面的动画元素提供淡出动画
}

事实证明此方法不可行。在执行到这儿的时候,我根本找不到让react组件延时挂在到真实DOM的入口,也就是如何在WillMount和Mount之前设置一个setTimeout,我找不到方法。

再接再厉

后来忽然想到一种方法,那就是把动画元素放在root之外,这样react就能自由地控制了

// index.html
<style>
// 加载中的动画样式
</style>

<div id='root'></div>
<div id='loading'>
  // 添加一些加载中的动画DOM元素
  // 这些DOM元素是绝对定位的,覆盖了root
</div>

然后,在componentDidMount之后给loading添加消退动画,这样子就可以实现了。 而且,通过这种方法,你可以精确地控制loading遮罩层的消退时机。实际上我在应用的时候不是在componentDidMount的时候就消退的。因为我里面有数据请求,而这个数据请求至关重要,没有数据展示该页面根本没有意义。所以我索性将消退时机推迟到数据返回成功。这样做的好处有两个。

  1. 用户正式进入应用的时候应用已经是可用的状态了
  2. 不需要再进入应用之后再做数据加载中的动画

最后给个截图 demo

Dbraum commented 8 years ago

请问有没有例子可以给看看

youngwind commented 8 years ago

@kunzhijia 完整的代码demo没有保留下来。按着思路走不能复现吗?你自己写得怎么样?能否给我看看你的demo?

ruyaoyao commented 7 years ago

謝謝分享,最近剛好對於這種lifecycle api有深刻的體會,看了這篇更加確定正確做法。 感激

jun-lu commented 7 years ago

cool

stoneox commented 7 years ago

是需要在componentDidMount中获取到loading DOM吗,这样岂不是就是在react中操作dom?有更好的实现方式吗?

youngwind commented 7 years ago

的确是在 React 中操作了 DOM,但 React 操作的不是 React 渲染出来的 DOM,而是 root 以外的 DOM,所以我觉得问题并不大。 @stoneox

huyansheng3 commented 7 years ago

很赞

Caldis commented 6 years ago

概括一下思路大概是:

  1. 动画写在 HTML 中
  2. React 组件加载完成后将 HTML 中的动画遮罩隐藏
    • 动画不要写在 root 元素中
hejueting commented 6 years ago

6666666666666666

bodyno commented 6 years ago

不错 这种方式确实很好