成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

用原生js寫一個(gè)"多動(dòng)癥"的簡(jiǎn)歷

Y3G / 3223人閱讀

摘要:用原生寫一個(gè)多動(dòng)癥的簡(jiǎn)歷預(yù)覽地址源碼地址最近在知乎上看到方應(yīng)杭用寫了一個(gè)會(huì)動(dòng)的簡(jiǎn)歷,覺(jué)得挺好玩的,研究一下其實(shí)現(xiàn)思路,決定試試用原生來(lái)實(shí)現(xiàn)。

用原生js寫一個(gè)"多動(dòng)癥"的簡(jiǎn)歷

預(yù)覽地址
源碼地址

最近在知乎上看到@方應(yīng)杭用vue寫了一個(gè)會(huì)動(dòng)的簡(jiǎn)歷,覺(jué)得挺好玩的,研究一下其實(shí)現(xiàn)思路,決定試試用原生js來(lái)實(shí)現(xiàn)。

會(huì)動(dòng)的簡(jiǎn)歷實(shí)現(xiàn)思路

這張會(huì)動(dòng)的簡(jiǎn)歷,就好像一個(gè)打字員在不斷地錄入文字,頁(yè)面呈現(xiàn)動(dòng)態(tài)效果。又好像一個(gè)早已經(jīng)錄制好影片,而我們只是坐在放映機(jī)前觀看。

原理分兩個(gè)部分

頁(yè)面能看見(jiàn)的不斷跳動(dòng)著的增加的文字,由innerHTML控制

頁(yè)面的布局效果由藏在"背后的"style標(biāo)簽完成

想象一下你要往一張網(wǎng)頁(yè)每間隔0.1秒增加一個(gè)字,是不是開個(gè)定時(shí)器,間斷地往body里面塞,就可以?。](méi)錯(cuò),做到這一步就完成了原理的第一部分

再想象一下,在往頁(yè)面里面塞的時(shí)候,我還想改變啊字的字體顏色以及網(wǎng)頁(yè)背景顏色,那應(yīng)該怎么做呢,是不是執(zhí)行下面的代碼就可以呢,沒(méi)錯(cuò),只不過(guò)更改字體和背景色不是突然改變的,而是也是開個(gè)定時(shí)器,間斷地往style標(biāo)簽中塞入以下代碼,這樣就完成了原理的第二步,是不是好簡(jiǎn)單 ???, 接下來(lái)讓我們一步步完成它

.xxx{
  color: blue;
  background: red; 
}
項(xiàng)目搭建

在這個(gè)項(xiàng)目中我們

使用webpack2來(lái)完成項(xiàng)目的構(gòu)建

使用yarn來(lái)處理依賴包的管理

使用es6的寫法

使用部分原生dom操作api

standard.js(代碼風(fēng)格約束利器)

目錄結(jié)構(gòu)如下

最重要的幾個(gè)模塊分別是resumeEditor(簡(jiǎn)歷編輯模塊)stylesEditor(簡(jiǎn)歷樣式編輯模塊) 、 以及vQuery(封裝的dom操作模塊)
最后app.js(入口模塊)再將幾個(gè)模塊的功能結(jié)合起來(lái)完成整個(gè)項(xiàng)目。

vQuery(封裝的dom操作模塊)

因?yàn)楹竺娴膸讉€(gè)模塊都要依賴這個(gè)小模塊,所以我們先簡(jiǎn)單的看下。

class Vquery {
  constructor (selector, context) {
    this.elements = getEles(selector, context)
  }

  optimizeCb (callback) {
    ...
  }

  get (index) {
    ...
  }

  html (sHtml) {
    ...
  }

  addClass (iClass) {
    ...
  }

  css (styles) {
    ...
  }

  height (h) {
    ...
  }

  scrollTop (top) {
    ...
  }
}

export default (selector, context) => {
  return new Vquery(selector, context)
}

可以看出它做的事就是封裝一個(gè)構(gòu)造函數(shù)Vquery,它的實(shí)例會(huì)有一些簡(jiǎn)單的dom操作方法,最后為了能夠像jQuery那樣使用$().funcName的形式去使用,我們導(dǎo)出了一個(gè)匿名函數(shù),在匿名函數(shù)中去new Vquery

stylesEditor(簡(jiǎn)歷樣式編輯模塊)

簡(jiǎn)歷所展現(xiàn)的布局效果都是由這個(gè)模塊完成的,核心方法是showStyles。

const showStyles = (num, callback) => {
  let style = styles[num]
  let length
  let prevLength

  if (!style) {
    return
  }

  length = styles.filter((item, i) => { // 計(jì)算數(shù)組styles前n個(gè)元素的長(zhǎng)度
    return i <= num
  }).reduce((result, item) => {
    result += item.length
    return result
  }, 0)

  prevLength = length - style.length

  clearInterval(timer)
  timer = setInterval(() => {
    let start = currentStyle.length - prevLength
    let char = style.substring(start, start + 1) || ""
    currentStyle += char
    if (currentStyle.length === length) { // 數(shù)組styles前n個(gè)元素已經(jīng)全部塞入,則關(guān)閉定時(shí)器,并且執(zhí)行外面?zhèn)鬟M(jìn)來(lái)的回調(diào),進(jìn)而執(zhí)行下一步操作
      clearInterval(timer)
      callback && callback()
    } else {
      let top = $stylePre.height() - MAX_HEIGHT
      if (top > 0) { // 當(dāng)塞入的內(nèi)容已經(jīng)超過(guò)了容器的高度,我們需要設(shè)置一下滾動(dòng)距離才方便演示接下來(lái)的內(nèi)容
        goBottom(top)
      }
      $style.html(currentStyle)
      $stylePre.html(Prism.highlight(currentStyle, Prism.languages.css))
    }
  }, delay)
}

stylesEditor(簡(jiǎn)歷樣式編輯模塊)

簡(jiǎn)歷編輯模塊用來(lái)展示簡(jiǎn)歷內(nèi)容,主要會(huì)經(jīng)歷由markdown格式往html頁(yè)面形式的轉(zhuǎn)換。

const markdownToHtml = (callback) => {
  $resumeMarkdown.css({
    display: "none"
  })
  $resumeWrap.addClass(iClass)
  $resumetag.html(marked(resumeMarkdown)) // 借助marked工具將markdown轉(zhuǎn)化為html
  callback && callback() // 執(zhí)行后續(xù)的回調(diào)
}

const showResume = (callback) => { // 原理基本上同stylesEditor, 不斷地往簡(jiǎn)歷編輯的容器中塞入事先準(zhǔn)備好的簡(jiǎn)歷內(nèi)容,當(dāng)全部塞入的時(shí)候再關(guān)閉定時(shí)器,并執(zhí)行后續(xù)的回調(diào)操作
  clearInterval(timer)
  timer = setInterval(() => {
    currentMarkdown += resumeMarkdown.substring(start, start + 1)
    if (currentMarkdown.length === length) {
      clearInterval(timer)
      callback && callback()
    } else {
      $resumeMarkdown.html(currentMarkdown)
      start++
    }
  }, delay)
}
app(入口模塊)

最后由app入口模塊將以上幾個(gè)模塊整合完成項(xiàng)目的功能,我們找出其中的核心代碼來(lái), ?,你沒(méi)看錯(cuò),傳說(shuō)中的回調(diào)地獄,亮瞎了我的狗眼啊。想必大家和我一樣都是不愿意看到這坨惡心的代碼的,但對(duì)于處理異步問(wèn)題,回調(diào)又的確是一直以來(lái)的解決方案之一。

因?yàn)槎〞r(shí)器的操作是異步行為,而我們的簡(jiǎn)歷生成過(guò)程會(huì)涉及到多個(gè)異步操作,所以為了看到如首頁(yè)預(yù)覽鏈接的效果,必須等前一個(gè)步驟完成之后,才能執(zhí)行下一步步驟,這里首先使用的回調(diào)函數(shù)的解決方案,大家可以從github上拉取代碼,分別切換以下幾個(gè)分支來(lái)查看不同的解決方案

master(使用回調(diào)函數(shù)處理)

promise(使用promise處理)

generator-thunk(使用generator + thunk函數(shù)處理)

generator-promise(使用generator + promise處理)

async(使用async處理)

showStyles(0, () => {
  showResume(() => {
    showStyles(1, () => {
      markdownToHtml(() => {
        showStyles(2)
      })
    })
  })
})

解決回調(diào)地獄之promise

回調(diào)方式能夠解決異步操作問(wèn)題,但是代碼寫起來(lái)非常的不美觀,可讀性差,代碼呈橫向發(fā)展趨勢(shì)...偉大的程序員們開疆?dāng)U土發(fā)明了promise的解決方案。我們來(lái)看一下promise分支中app模塊最終的寫法

showStylesWrap(0)
  .then(showResumeWrap)
  .then(showStylesWrap.bind(null, 1))
  .then(markdownToHtmlWrap)
  .then(showStylesWrap.bind(null, 2))

可以看到,代碼清爽了很多,縱向發(fā)展,應(yīng)用第一步第二步第三步...一眼就能夠看出來(lái),當(dāng)然實(shí)現(xiàn)的邏輯是將原來(lái)的相關(guān)的模塊用Promise包裝起來(lái),并且在原來(lái)回調(diào)函數(shù)執(zhí)行的地方resolve即可,詳細(xì)實(shí)現(xiàn),歡迎查看項(xiàng)目源碼

解決回調(diào)地獄之generator-thunk,generator-promise

兩種方式比較類似,都要用到es6中的generator。關(guān)于什么是generator,thunk函數(shù),可以查看軟大神關(guān)于ECMAScript 6 入門,這里簡(jiǎn)要地講述一下,其如何處理異步操作問(wèn)題使得可以將異步行為寫起來(lái)如同步般爽。

function timeOut1 () {
  setTimeout(() => {
    console.log(1111)
  }, 1000)
}

function timeOut2 () {
  setTimeout(() => {
    console.log(2222)
  }, 200)
}

function * gen () {
  yield timeOut1()
  yield timeOut2()
}

let g = gen()
g.next()
g.next()

上面的代碼在過(guò)了200毫秒會(huì)log出2222,過(guò)了1秒鐘之后log出1111

這,要?了,你不是說(shuō)generator寫起來(lái)同步可以解決異步問(wèn)題嗎,為毛這里timeOut2沒(méi)有在timeOut1之后執(zhí)行呢,畢竟gen函數(shù)中看起來(lái)是希望這樣的嘛。

其實(shí)不然,timeOut2啥時(shí)候執(zhí)行取決于

g.next()
g.next()

試想兩個(gè)函數(shù)幾乎同時(shí)執(zhí)行,那在定時(shí)器中當(dāng)然是200毫秒后的timeOut2先打印出2222來(lái),但是有沒(méi)有辦法,讓timeOut2在timeOut1后執(zhí)行呢?答案是有的

function timeOut1 () {
  setTimeout(() => {
    console.log(1111)
    g.next()
  }, 1000)
}

function timeOut2 () {
  setTimeout(() => {
    console.log(2222)
  }, 200)
}

function * gen () {
  yield timeOut1()
  yield timeOut2()
}

let g = gen()
g.next()

可以看到我們?cè)趖imeOut1執(zhí)行完成之后,再將指針指向下一個(gè)位置,即timeOut2再去執(zhí)行,這樣的結(jié)果就和gen函數(shù)中兩個(gè)yield的寫起來(lái)同步感覺(jué)一樣了。但是含有一個(gè)問(wèn)題,如果涉及到很多個(gè)異步操作,我們是很難通過(guò)上面的方式將異步流程管理起來(lái)的。于是我們需要做下面一件事

function co (fn) {
  var gen = fn();

  function next(err, data) {
    var result = gen.next(data);
    if (result.done) return;
    result.value(next); // thunk和promise不同地方之一在這里, promise是result.value.then(next)
  }

  next();
}

內(nèi)部的next函數(shù)就是 thunk 的回調(diào)函數(shù)。next函數(shù)先將指針移到 generator 函數(shù)的下一步(gen.next方法),然后判斷 generator 函數(shù)是否結(jié)束(result.done屬性),如果沒(méi)結(jié)束,就將next函數(shù)再傳入 thunk 函數(shù)(result.value屬性),否則就直接退出。

最后我們?cè)诳匆幌峦ㄟ^(guò)co函數(shù)的寫法完成上面的例子

function timeOut1() {
  return (callback) => {
    setTimeout(() => {
      console.log(1111)
      callback()
    }, 1000)
  }

}

function timeOut2() {
  return (callback) => {
    setTimeout(() => {
      console.log(2222)
      callback()
    }, 200)
  }
}

function co(fn) {
  var gen = fn();

  function next(err, data) {
    var result = gen.next(data);
    if (result.done) return;
    result.value(next); // thunk和promise不同地方之一在這里, promise是result.value.then(next)
  }

  next();
}

co(function * () {
  yield timeOut1()
  yield timeOut2()
})

解決回調(diào)地獄之a(chǎn)sync

async其實(shí)就是generator函數(shù)的語(yǔ)法糖。大家如果把generator弄明白了,使用它一定不再話下,關(guān)于這個(gè)項(xiàng)目的用法,歡迎查看async分支源代碼,這里不再贅述。

尾述

本文中可能存在闡述不當(dāng)?shù)牡胤?,歡迎大家指正。???,最后點(diǎn)個(gè)贊,點(diǎn)個(gè)star好不好呀。

源碼地址

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/82816.html

相關(guān)文章

  • JavaScript系列(四) - 收藏集 - 掘金

    摘要:函數(shù)式編程前端掘金引言面向?qū)ο缶幊桃恢币詠?lái)都是中的主導(dǎo)范式。函數(shù)式編程是一種強(qiáng)調(diào)減少對(duì)程序外部狀態(tài)產(chǎn)生改變的方式。 JavaScript 函數(shù)式編程 - 前端 - 掘金引言 面向?qū)ο缶幊桃恢币詠?lái)都是JavaScript中的主導(dǎo)范式。JavaScript作為一門多范式編程語(yǔ)言,然而,近幾年,函數(shù)式編程越來(lái)越多得受到開發(fā)者的青睞。函數(shù)式編程是一種強(qiáng)調(diào)減少對(duì)程序外部狀態(tài)產(chǎn)生改變的方式。因此,...

    cfanr 評(píng)論0 收藏0
  • 如何&quot;有計(jì)劃,高效率,優(yōu)簡(jiǎn)歷&quot;應(yīng)對(duì)面試

    摘要:雖然有了十全的計(jì)劃,但如何高效率去記住上面那么多東西是一個(gè)大問(wèn)題,看看我是怎么做的。 前言 前一篇文章講述了我在三月份毫無(wú)準(zhǔn)備就去面試的后果,一開始心態(tài)真的爆炸,但是又不服氣,一想到每次回來(lái)后家人朋友問(wèn)我面試結(jié)果的期待臉,越覺(jué)得必須付出的行動(dòng)來(lái)證明自己了。 面經(jīng)傳送門:一個(gè)1年工作經(jīng)驗(yàn)的PHP程序員是如何被面試官虐的? 下面是我花費(fèi)兩個(gè)星期做的準(zhǔn)備,主要分三部分: 有計(jì)劃——計(jì)劃好...

    gyl_coder 評(píng)論0 收藏0
  • &quot;雙非&quot;應(yīng)屆生校招如何獲得大廠青睞?(內(nèi)附前端大廠面經(jīng)+技術(shù)崗超全求職攻略)

    摘要:拿到秋招的同學(xué),如確定入職需與用人單位簽署三方協(xié)議,以保證雙方的利益不受損失。當(dāng)然每個(gè)崗位所要求的側(cè)重點(diǎn)不同,但卻百變不離其宗。方法論要想達(dá)成某個(gè)目標(biāo)都有其特定的方法論,學(xué)習(xí)技術(shù)也不例外,掌握適當(dāng)?shù)膶W(xué)習(xí)方法才能事半功倍。 寫在前面的話 筆者從17年的2月份開始準(zhǔn)備春招,其中遇到不少坑,也意識(shí)到自己走過(guò)的彎路。故寫了這篇文章總結(jié)一番,本文適合主動(dòng)學(xué)習(xí)的,對(duì)自己要學(xué)的課程不明確的,對(duì)面試有...

    jeffrey_up 評(píng)論0 收藏0
  • &quot;雙非&quot;應(yīng)屆生校招如何獲得大廠青睞?(內(nèi)附前端大廠面經(jīng)+技術(shù)崗超全求職攻略)

    摘要:拿到秋招的同學(xué),如確定入職需與用人單位簽署三方協(xié)議,以保證雙方的利益不受損失。當(dāng)然每個(gè)崗位所要求的側(cè)重點(diǎn)不同,但卻百變不離其宗。方法論要想達(dá)成某個(gè)目標(biāo)都有其特定的方法論,學(xué)習(xí)技術(shù)也不例外,掌握適當(dāng)?shù)膶W(xué)習(xí)方法才能事半功倍。 寫在前面的話 筆者從17年的2月份開始準(zhǔn)備春招,其中遇到不少坑,也意識(shí)到自己走過(guò)的彎路。故寫了這篇文章總結(jié)一番,本文適合主動(dòng)學(xué)習(xí)的,對(duì)自己要學(xué)的課程不明確的,對(duì)面試有...

    lindroid 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<