用原生js写一个”多动症”的简历
最近在知乎上看到@方应杭用vue写了一个会动的简历,觉得挺好玩的,研究一下其实现思路,决定试试用原生js来实现。
会动的简历实现思路
这张会
动
的简历,就好像一个打字员在不断地录入文字,页面呈现动态效果。又好像一个早已经录制好影片,而我们只是坐在放映机前观看。
原理分两个部分
- 页面能看见的不断跳动着的增加的文字,由innerHTML控制
- 页面的布局效果由藏在”背后的”
style
标签完成
想象一下你要往一张网页每间隔0.1秒增加一个啊
字,是不是开个定时器,间断地往body里面塞啊
,就可以啊!没错,做到这一步就完成了原理的第一部分
再想象一下,在往页面里面塞啊
的时候,我还想改变啊字的字体颜色以及网页背景颜色,那应该怎么做呢,是不是执行下面的代码就可以呢,没错,只不过更改字体和背景色不是突然改变的,而是也是开个定时器,间断地往style
标签中塞入以下代码,这样就完成了原理的第二步,是不是好简单 😀😀😀, 接下来让我们一步步完成它
|
|
项目搭建
在这个项目中我们
- 使用webpack2来完成项目的构建
- 使用yarn来处理依赖包的管理
- 使用es6的写法
- 使用部分原生dom操作api
- standard.js(代码风格约束利器)
目录结构如下
最重要的几个模块分别是resumeEditor(简历编辑模块)
、 stylesEditor(简历样式编辑模块)
、 以及vQuery(封装的dom操作模块)
最后app.js(入口模块)
再将几个模块的功能结合起来完成整个项目。
vQuery(封装的dom操作模块)
因为后面的几个模块都要依赖这个小模块,所以我们先简单的看下。
|
|
可以看出它做的事就是封装一个构造函数Vquery,它的实例会有一些简单的dom操作方法,最后为了能够像jQuery那样使用$().funcName的形式去使用,我们导出了一个匿名函数,在匿名函数中去new Vquery
stylesEditor(简历样式编辑模块)
简历所展现的布局效果都是由这个模块完成的,核心方法是showStyles。
|
|
stylesEditor(简历样式编辑模块)
简历编辑模块用来展示简历内容,主要会经历由markdown格式往html页面形式的转换。
|
|
app(入口模块)
最后由app入口模块将以上几个模块整合完成项目的功能,我们找出其中的核心代码来, 😀,你没看错,传说中的回调地狱,亮瞎了我的狗眼啊。想必大家和我一样都是不愿意看到这坨恶心的代码的,但对于处理异步问题,回调又的确是一直以来的解决方案之一。
因为定时器的操作是异步行为,而我们的简历生成过程会涉及到多个异步操作,所以为了看到如首页预览链接的效果,必须等前一个步骤完成之后,才能执行下一步步骤,这里首先使用的回调函数的解决方案,大家可以从github上拉取代码,分别切换以下几个分支来查看不同的解决方案
- master(使用回调函数处理)
- promise(使用promise处理)
- generator-thunk(使用generator + thunk函数处理)
- generator-promise(使用generator + promise处理)
- async(使用async处理)
|
|
解决回调地狱之promise
回调方式能够解决异步操作问题,但是代码写起来非常的不美观,可读性差,代码呈横向发展趋势…伟大的程序员们开疆扩土发明了promise的解决方案。我们来看一下promise分支中app模块最终的写法
|
|
可以看到,代码清爽了很多,纵向发展,应用第一步第二步第三步…一眼就能够看出来,当然实现的逻辑是将原来的相关的模块用Promise包装起来,并且在原来回调函数执行的地方resolve即可,详细实现,欢迎查看项目源码
解决回调地狱之generator-thunk,generator-promise
两种方式比较类似,都要用到es6中的generator。关于什么是generator,thunk函数,可以查看软大神关于ECMAScript 6 入门,这里简要地讲述一下,其如何处理异步操作问题使得可以将异步行为写起来如同步般爽。
|
|
上面的代码在过了200毫秒会log出2222,过了1秒钟之后log出1111
这,要😭了,你不是说generator写起来同步可以解决异步问题吗,为毛这里timeOut2没有在timeOut1之后执行呢,毕竟gen函数中看起来是希望这样的嘛。
其实不然,timeOut2啥时候执行取决于
|
|
可以看到我们在timeOut1执行完成之后,再将指针指向下一个位置,即timeOut2再去执行,这样的结果就和gen函数中两个yield的写起来同步感觉一样了。但是含有一个问题,如果涉及到很多个异步操作,我们是很难通过上面的方式将异步流程管理起来的。于是我们需要做下面一件事
|
|
内部的next函数就是 thunk 的回调函数。next函数先将指针移到 generator 函数的下一步(gen.next方法),然后判断 generator 函数是否结束(result.done属性),如果没结束,就将next函数再传入 thunk 函数(result.value属性),否则就直接退出。
最后我们在看一下通过co函数的写法完成上面的例子
|
|
解决回调地狱之async
async其实就是generator函数的语法糖。大家如果把generator弄明白了,使用它一定不再话下,关于这个项目的用法,欢迎查看async分支源代码,这里不再赘述。
尾述
本文中可能存在阐述不当的地方,欢迎大家指正。😀😀😀,最后点个赞,点个star好不好呀。
源码地址