<dfn id="hx5t3"><strike id="hx5t3"><em id="hx5t3"></em></strike></dfn>

    <thead id="hx5t3"></thead><nobr id="hx5t3"><font id="hx5t3"><rp id="hx5t3"></rp></font></nobr>

    <listing id="hx5t3"></listing>

    <var id="hx5t3"></var>
    <big id="hx5t3"></big>

      
      

      <output id="hx5t3"><ruby id="hx5t3"></ruby></output>
      <menuitem id="hx5t3"><dfn id="hx5t3"></dfn></menuitem>

      <big id="hx5t3"></big>

        n?i?g?h?t?i?r?e?

        n?i?g?h?t?i?r?e? 查看完整檔案

        上海編輯  |  填寫畢業院校上海巧議網絡科技有限公司  |  軟件開發工程師 編輯 www.very-geek.com 編輯
        編輯

        正在更新 Elixir 語言的系列文章:http://www.very-geek.com

        個人動態

        n?i?g?h?t?i?r?e? 贊了回答 · 2月28日

        解決看到別人寫得很好的代碼,感覺很無力怎么辦?

        記錄在紙上的思想就如同某人留在沙上的腳印,我們也許能看到他走過的路徑;
        但若想知道他在路上看見了什么東西, 就必須用我們自己的眼睛。

        關注 36 回答 13

        n?i?g?h?t?i?r?e? 關注了專欄 · 2020-06-27

        編程時間簡史

        編程考古學,不寫代碼,只談歷史,適合作為程序員們吹逼的談資。

        關注 325

        n?i?g?h?t?i?r?e? 贊了文章 · 2020-06-27

        《編程時間簡史系列》JavaScript 模塊化的歷史進程

        引言

        昨天在思否上閑逛,發現了一個有意思的問題(點此傳送)。

        因為這個問題,我產生了寫一個系列文章的想法,試圖從站在歷史的角度上來看待編程世界中林林總總的問題和解決方案。

        目前中文網絡上充斥著大量互相“轉載”的內容,基本是某一個技術問題的解決方案(what?how?),卻不涉及為什么這么做和歷史緣由(why?when?)。比如你要搜 “JavaScript 有哪些模塊化方案?它們有什么區別?”,能得到一萬個有用的結果;但要想知道 “為什么 JavaScript 有這么多模塊化方案?它們是誰創建的?”,卻幾乎不可能。

        因此,這一系列文章內會盡可能的不涉及具體代碼,只談歷史故事。但會在文末提供包含部分代碼的參考鏈接,以供感興趣的朋友自行閱讀。

        這個系列暫定為十篇文章,內容會涉及前端、后端、編程語言、開發工具、操作系統等等。也給自己立個 Flag,在今年年底之前把整個系列寫完。如果沒完成目標……就當我沒說過這句話(逃

        全系列索引:

        1. 《編程時間簡史系列》JavaScript 模塊化的歷史進程
        2. 《編程時間簡史系列》Web Server 編年史

        正文

        模塊化,是前端繞不過去的話題。

        隨著 Node.js 和三大框架的流行,越來越多的前端開發者們腦海中都會時常浮現一個問題:

        為什么 JavaScript 有這么多模塊化方案?

        自從 1995 年 5 月,Brendan Eich 寫下了第一行 JavaScript 代碼起,JavaScript 已經誕生了 25 年。

        但這門語言早期僅僅作為輕量級的腳本語言,用于在 Web 上與用戶進行少量的交互,并沒有依賴管理的概念。

        隨著 AJAX 技術得以廣泛使用,Web 2.0 時代迅猛發展,瀏覽器承載了愈來愈多的內容與邏輯,JavaScript 代碼越來越復雜,全局變量沖突、依賴管理混亂等問題始終縈繞在前端開發者們的心頭。此時,JavaScript 亟需一種在其他語言中早已得到良好應用的功能 —— 模塊化。

        其實,JavaScript 本身的標準化版本 ECMAScript 6.0 (ES6/ES2015) 中,已經提供了模塊化方案,即 ES Module。但目前在 Node.js 體系下,最常見的方案其實是 CommonJS。再加上大家耳熟能詳的 AMD、CMD、UMD,模塊化的事實標準如此之多。

        那么為什么有如此之多的模塊化方案?它們又是在怎樣的背景下誕生的?為什么沒有一個方案 “千秋萬代,一統江湖”?

        接下來,我會按照時間順序講述模塊化的發展歷程,順帶也就回答了上述幾個問題。

        萌芽初現:從 YUI Library 和 jQuery 說起

        時間回到 2006 年 1 月,當時還是國際互聯網巨頭的 Yahoo(雅虎),開源了其內部使用已久的組件庫 YUI Library。

        YUI Library 采用了類似于 Java 命名空間的方式,來隔離各個模塊之間的變量,避免全局變量造成的沖突。其寫法類似于:

        YUI.util.module.doSomthing();

        這種寫法無論是封裝還是調用時都十分繁瑣,而且當時的 IDE 對于 JavaScript 來說智能感知非常弱,開發者很難知道他需要的某個方法存在于哪個命名空間下,經常需要頻繁地查閱開發手冊,導致開發體驗十分不友好。

        在 YUI 發布之后不久,John Resig 發布了 jQuery。當時年僅 23 歲的他,不會知道自己這一時興起在 BarCamp 會議上寫下的代碼,將占據未來十幾年的 Web 領域。

        jQuery 使用了一種新的組織方式,它利用了 JavaScript 的 IIFE(立即執行函數表達式)和閉包的特性,將所依賴的外部變量傳給一個包裝了自身代碼的匿名函數,在函數內部就可以使用這些依賴,最后在函數的結尾把自身暴露給 window。這種寫法被很多后來的框架所模仿,其寫法類似于:

        (function(root){
            // balabala
            root.jQuery = root.$ = jQuery;
        })(window);

        這種寫法雖然靈活性大大提升,可以很方便地添加擴展,但它并未解決根本問題:所需依賴還是得外部提前提供,還是會增加全局變量。

        從以上的嘗試中,可以歸納出 JavaScript 模塊化需要解決哪些問題:

        1. 如何給模塊一個唯一標識?
        2. 如何在模塊中使用依賴的外部模塊?
        3. 如何安全地(不污染模塊外代碼)包裝一個模塊?
        4. 如何優雅地(不增加全局變量)把模塊暴漏出去?

        圍繞著這些問題,JavaScript 模塊化開始了一段曲折的探索之路。

        探索之路:CommonJS 與 Node.js 的誕生

        讓我們來到 2009 年 1 月,此時距離 ES6 發布尚有 5 年的時間,但前端領域已經迫切地需要一套真正意義上的模塊化方案,以解決全局變量污染和依賴管理混亂等問題。

        Mozilla 旗下的工程師 Kevin Dangoor,在工作之余,與同事們一起制訂了一套 JavaScript 模塊化的標準規范,并取名為 ServerJS。

        ServerJS 最早用于服務端 JavaScript,旨在為配合自動化測試等工作而提供模塊導入功能。

        這里插一句題外話,其實早期 1995 年,Netsacpe(網景)公司就提供了有在服務端執行 JavaScript 能力的產品,名為 Netscape Enterprise Server。但此時服務端能做的 JavaScript 還是基于瀏覽器來實現的,本身沒有脫離其自帶的 API 范圍。直到 2009 年 5 月,Node.js 誕生,賦予了其文件系統、I/O 流、網絡通信等能力,才真正意義上的成為了一門服務端編程語言。

        2009 年年初,Ryan Dahl 產生了創造一個跨平臺編程框架的想法,想要基于 Google(谷歌)的 Chromium V8 引擎來實現。經過幾個月緊張的開發工作,在 5 月中旬,Node.js 首個預覽版本的開發工作已全部結束。同年 8 月,歐洲 JSConf 開發者大會上,Node.js 驚艷亮相。

        但在此刻,Node.js 還沒有一款包管理工具,外部依賴依然要手動下載到項目目錄內再引用。歐洲 JSConf 大會結束后,Isaac Z. Schlueter 注意到了 Ryan DahlNode.js,兩人一拍即合,決定開發一款包管理工具,也就是后來大名鼎鼎的 Node Package Manager(即 npm)。

        在開發之初,擺在二人面前的第一個問題就是,采用何種模塊化方案?。二人將目光鎖定在了幾個月前(2009 年 4 月)在華盛頓特區舉辦的美國 JSConf 大會上公布的 ServerJS。此時的 ServerJS 已經更名為 CommonJS,并重新制訂了標準規范,即Modules/1.0,展現了更大的野心,企圖一統所有編程語言的模塊化方案。

        具體來說,Modules/1.0標準規范包含以下內容:

        1. 模塊的標識應遵循一定的書寫規則。
        2. 定義全局函數 require(dependency),通過傳入模塊標識來引入其他依賴模塊,執行的結果即為別的模塊暴漏出來的 API。
        3. 如果被 require 函數引入的模塊中也包含外部依賴,則依次加載這些依賴。
        4. 如果引入模塊失敗,那么 require 函數應該拋出一個異常。
        5. 模塊通過變量 exports 來向外暴露 API,exports 只能是一個 object 對象,暴漏的 API 須作為該對象的屬性。

        由于這個規范簡單而直接,Node.jsnpm 很快就決定采用這種模塊化的方案。至此,第一個 JavaScript 模塊化方案正式登上了歷史舞臺,成為前端開發中必不可少的一環。

        需要注意的是,CommonJS 是一系列標準規范的統稱,它包含了多個版本,從最早 ServerJS 時的 Modules/0.1,到更名為 CommonJS 后的 Modules/1.0,再到現在成為主流的 Modules/1.1。這些規范有很多具體的實現,且不只局限于 JavaScript 這一種語言,只要遵循了這一規范,都可以稱之為 CommonJS。其中,Node.js 的實現叫做 Common Node Modules。CommonJS 的其他實現,感興趣的朋友可以閱讀本文最下方的參考鏈接。

        值得一提的是,CommonJS 雖然沒有進入 ECMAScript 標準范圍內,但 CommonJS 項目組的很多成員,也都是 TC39(即制訂 ECMAScript 標準的委員會組織)的成員。這也為日后 ES6 引入模塊化特性打下了堅實的基礎。

        分道揚鑣:CommonJS 歷史路口上的抉擇

        在推出 Modules/1.0 規范后,CommonJSNode.js 等環境下取得了很不錯的實踐。

        但此時的 CommonJS 有兩個重要問題沒能得到解決,所以遲遲不能推廣到瀏覽器上:

        1. 由于外層沒有 function 包裹,被導出的變量會暴露在全局中。
        2. 在服務端 require 一個模塊,只會有磁盤 I/O,所以同步加載機制沒什么問題;但如果是瀏覽器加載,一是會產生開銷更大的網絡 I/O,二是天然異步,就會產生時序上的錯誤。

        因此,社區意識到,要想在瀏覽器環境中也能順利使用 CommonJS,勢必重新制訂新的標準規范。但新的規范怎么制訂,成為了激烈爭論的焦點,分歧和沖突由此誕生,逐步形成了三大流派:

        • Modules/1.x 派:這派的觀點是,既然 Modules/1.0 已經在服務器端有了很好的實踐經驗,那么只需要將它移植到瀏覽器端就好。在瀏覽器加載模塊之前,先通過工具將模塊轉換成瀏覽器能運行的代碼了。我們可以理解為他們是“保守派”。
        • Modules/Async 派:這派認為,既然瀏覽器環境于服務器環境差異過大,那么就不應該繼續在 Modules/1.0 的基礎上小修小補,應該遵循瀏覽器本身的特點,放棄 require 方式改為回調,將同步加載模塊變為異步加載模塊,這樣就可以通過 ”下載 -> 回調“ 的方式,避免時序問題。我們可以理解為他們是“激進派”。
        • Modules/2.0 派:這派同樣也認為不應該沿用 Modules/1.0,但也不向激進派一樣過于激進,認為 require 等規范還是有可取之處,不應該隨隨便便放棄,而是要盡可能的保持一致;但激進派的優點也應該吸收,比如 exports 也可以導出其他類型、而不僅局限于 object 對象。我們可以理解為他們是“中間派”。

        其中保守派的思路跟今天通過 babel 等工具,將 JavaScript 高版本代碼轉譯為低版本代碼如出一轍,主要目的就是為了兼容。有了這種想法,這派人馬提出了 Modules/Transport 規范,用于規定模塊如何轉譯。browserify 就是這一觀點下的產物。

        激進派也提出了自己的規范 Modules/AsynchronousDefinition,奈何這一派的觀點并沒有得到 CommonJS 社區的主流認可。

        中間派同樣也有自己的規范 Modules/Wrappings,但這派人馬最后也不了了之,沒能掀起什么風浪。

        激進派、中間派與保守派的理念不和,最終為 CommonJS 社區分裂埋下伏筆。

        百家爭鳴:激進派 —— AMD 的崛起

        激進派的 James Burke 在 2009 年 9 月開發出了 RequireJS 這一模塊加載器,以實踐證明自己的觀點。

        但激進派的想法始終得不到 CommonJS 社區主流認可。雙方的分歧點主要在于執行時機問題,Modules/1.0 是延遲加載、且同一模塊只執行一次,而 Modules/AsynchronousDefinition 卻是提前加載,加之破壞了就近聲明(就近依賴)原則,還引入了 define 等新的全局函數,雙方的分歧越來越大。

        最終,在 James Burke、Karl Westin 等人的帶領下,激進派于同年年底宣布離開 CommonJS 社區,自立門戶。

        激進派在離開社區后,起初專注于 RequireJS 的開發工作,并沒有過多的涉足社區工作,也沒有此草新的標準規范。

        2011 年 2 月,在 RequireJS 的擁躉們的共同努力下,由 Kris Zyp 起草的 Async Module Definition(簡稱 AMD)標準規范正式發布,并在 RequireJS 社區的基礎上建立了 AMD 社區。

        AMD 標準規范主要包含了以下幾個內容:

        1. 模塊的標識遵循 CommonJS Module Identifiers。
        2. 定義全局函數 define(id, dependencies, factory),用于定義模塊。dependencies 為依賴的模塊數組,在 factory 中需傳入形參與之一一對應。
        3. 如果 dependencies 的值中有 require、exportsmodule,則與 CommonJS 中的實現保持一致。
        4. 如果 dependencies 省略不寫,則默認為 ['require', 'exports', 'module'],factory 中也會默認傳入三者。
        5. 如果 factory 為函數,模塊可以通過以下三種方式對外暴漏 API:return 任意類型;exports.XModule = XModule、module.exports = XModule。
        6. 如果 factory 為對象,則該對象即為模塊的導出值。

        其中第三、四兩點,即所謂的 Modules/Wrappings,是因為 AMD 社區對于要寫一堆回調這種做法頗有微辭,最后 RequireJS 團隊妥協,搞出這么個部分兼容支持。

        因為 AMD 符合在瀏覽器端開發的習慣方式,也是第一個支持瀏覽器端的 JavaScript 模塊化解決方案,RequireJS 迅速被廣大開發者所接受。

        但有 CommonJS 珠玉在前,很多開發者對于要寫很多回調的方式頗有微詞。在呼吁高漲聲中,RequireJS 團隊最終妥協,搞出個 Simplified CommonJS wrapping(簡稱 CJS)的兼容方式,即上文的第三、四兩點。但由于背后實際還是 AMD,所以只是寫法上做了兼容,實際上并沒有真正做到 CommonJS 的延遲加載。

        CommonJS 規范有眾多實現不同的是,AMD 只專注于 JavaScript 語言,且實現并不多,目前只有 RequireJSDojo Toolkit,其中后者已經停止維護。

        一波三折:中間派 —— CMD 的衰落

        由于 AMD 的提前加載的問題,被很多開發者擔心會有性能問題而吐槽。

        例如,如果一個模塊依賴了十個其他模塊,那么在本模塊的代碼執行之前,要先把其他十個模塊的代碼都執行一遍,不管這些模塊是不是馬上會被用到。這個性能消耗是不容忽視的。

        為了避免這個問題,上文提到,中間派試圖保留 CommonJS 書寫方式和延遲加載、就近聲明(就近依賴)等特性,并引入異步加載機制,以適配瀏覽器特性。

        其中一位中間派的大佬 Wes Garland,本身是 CommonJS 的主要貢獻者之一,在社區中很受尊重。他在 CommonJS 的基礎之上,起草了 Modules/2.0,并給出了一個名為 BravoJS 的實現。

        另一位中間派大佬 @khs4473 提出了 Modules/Wrappings,并給出了一個名為 FlyScript 的實現。

        Wes Garland 本人是學院派,理論功底十分扎實,但寫出的作品卻既不優雅也不實用。而實戰派的 @khs4473 則在與 James Burke 發生了一些爭論,最后刪除了自己的 GitHub 倉庫并停掉了 FlyScript 官網。

        到此為止,中間一派基本已全軍覆滅,空有理論,沒有實踐。

        讓我們前進到 2011 年 4 月,國內阿里巴巴集團的前端大佬玉伯(本名王保平),在給 RequireJS 不斷提出建議卻被拒絕之后,萌生了自己寫一個模塊加載器的想法。

        在借鑒了 CommonJS、AMD 等模塊化方案后,玉伯寫出了 SeaJS,不過這一實現并沒有嚴格遵守 Modules/Wrappings 的規范,所以嚴格來說并不能稱之為 Modules/2.0。在此基礎上,玉伯提出了 Common Module Definition(簡稱 CMD)這一標準規范。

        CMD 規范的主要內容與 AMD 大致相同,不過保留了 CommonJS 中最重要的延遲加載、就近聲明(就近依賴)特性。

        隨著國內互聯網公司之間的技術交流,SeaJS 在國內得到了廣泛使用。不過在國外,也許是因為語言障礙等原因,并沒有得到非常大范圍的推廣。

        兼容并濟:UMD 的統一

        2014 年 9 月,美籍華裔 Homa Wong 提交了 UMD 第一個版本的代碼。

        UMDUniversal Module Definition 的縮寫,它本質上并不是一個真正的模塊化方案,而是將 CommonJSAMD 相結合。

        UMD 作出了如下內容的規定:

        1. 優先判斷是否存在 exports 方法,如果存在,則采用 CommonJS 方式加載模塊;
        2. 其次判斷是否存在 define 方法,如果存在,則采用 AMD 方式加載模塊;
        3. 最后判斷 global 對象上是否定義了所需依賴,如果存在,則直接使用;反之,則拋出異常。

        這樣一來,模塊開發者就可以使自己的模塊同時支持 CommonJSAMD 的導出方式,而模塊使用者也無需關注自己依賴的模塊使用的是哪種方案。

        姍姍來遲:欽定的 ES6/ES2015

        時間前進到 2016 年 5 月,經過了兩年的討論,ECMAScript 6.0 終于正式通過決議,成為了國際標準。

        在這一標準中,首次引入了 importexport 兩個 JavaScript 關鍵字,并提供了被稱為 ES Module 的模塊化方案。

        在 JavaScript 出生的第 21 個年頭里,JavaScript 終于迎來了屬于自己的模塊化方案。

        但由于歷史上的先行者已經占據了優勢地位,所以 ES Module 遲遲沒有完全替換上文提到的幾種方案,甚至連瀏覽器本身都沒有立即作出支持。

        2017 年 9 月上旬,Chrome 61.0 版本發布,首次在瀏覽器端原生支持了 ES Module。

        2017 年 9 月中旬,Node.js 迅速跟隨,發布了 8.5.0,以支持原生模塊化,這一特性被稱之為 ECMAScript Modules(簡稱 MJS)。不過到目前為止,這一特性還處于試驗性階段。

        不過隨著 babel、Webpack、TypeScript 等工具的興起,前端開發者們已經不再關心以上幾種方式的兼容問題,習慣寫哪種就寫哪種,最后由工具統一轉譯成瀏覽器所支持的方式。

        因此,預計在今后很長的一段時間里,幾種模塊化方案都會在前端開發中共存。


        尾聲

        本文以時間線為基準,從作者、社區、理念等幾個維度談到了 JavaScript 模塊化的幾大方案。

        其實模塊化方案遠不止提到的這些,但其他的都沒有這些流行,這里也就不費筆墨。

        文中并沒有提及各個模塊化方案是如何實現的,也沒有給出相關的代碼示例,感興趣的朋友可以自行閱讀下方的參考閱讀鏈接。

        下面我們再總結梳理一下時間線:

        時間事件
        1995.05Brendan Eich 開發 JavaScript。
        2006.01Yahoo 開源 YUI Library,采用命名空間方式管理模塊。
        2006.01John Resig 開發 jQuery,采用 IIFE + 閉包管理模塊。
        2009.01Kevin Dangoor 起草 ServerJS,并公布第一個版本 Modules/0.1。
        2009.04Kevin Dangoor 在美國 JSConf 公布 CommonJS。
        2009.05Ryan Dahl 開發 Node.js。
        2009.08Ryan Dahl 在歐洲 JSConf 公布 Node.js。
        2009.08Kevin DangoorServerJS 改名為 CommonJS,并起草第二個版本 Modules/1.0。
        2009.09James Burke 開發 RequireJS。
        2010.01Isaac Z. Schlueter 開發 npm,實現了基于 CommonJS 模塊化方案的 Common Node Modules。
        2010.02Kris Zyp 起草 AMD,AMD/RequireJS 社區成立。
        2011.01玉伯開發 SeaJS,起草 CMD,CMD/SeaJS 社區成立。
        2014.08Homa Wong 開發 UMD。
        2015.05ES6 發布,新增特性 ES Module。
        2017.09ChromeNode.js 開始原生支持 ES Module。

        注:文章中的所有人物、事件、時間、地點,均來自于互聯網公開內容,由本人進行搜集整理,其中如有謬誤之處,還請多多指教。


        參考閱讀


        首發于 www.tvxinternet.com,歡迎轉載,轉載請注明來源和作者。

        RHQYZ, Write at 2020.06.24.

        查看原文

        贊 55 收藏 29 評論 5

        n?i?g?h?t?i?r?e? 回答了問題 · 2020-04-14

        plupload 插件在安卓系統里無法拍照上傳, IOS卻可以。 如何解決?

        This is also true for a lot of older mobile browsers who do not support the 'multiple' attribute of the input='file' element. Browsers who do support it sometimes don't fare well with the 'accept' attribute. And indeed that is before the file selection pickers on android. Some are just not capable of selecting multiple files for a given multiple input, while an other might.

        簡言之,有些移動端瀏覽器不支持多選,而有些則支持多選但是 accept 屬性表現不佳。accept 就是你問題中設置的 mime_types。

        這是來自 plupload issues 里的回答,所以至少這個插件是沒什么特別好的辦法的。要么單選,要么不設置 mime_types。

        關注 3 回答 2

        n?i?g?h?t?i?r?e? 贊了文章 · 2020-04-06

        前端學習計算機理論知識的好處

        寫本文的目的,主要是源于和一個老前端的交流。

        我認為學習計算機理論知識很有用,對職業發展幫助非常大。他認為應用開發和底層開發是兩回事,前端屬于應用開發,學理論知識完全浪費時間。具體細節就不說了,最后是不歡而散。

        不過,有一點我們是達成共識的:學習計算機理論知識不能讓你的業務頁面寫得更快、更好,它不是銀彈,不能解決所有開發中的問題。如果你還處于入門級別,學這個也沒多大用,學好前端基礎知識反而更有用(這句話針對非科班出身的前端)。

        我認為學習計算機理論知識對于前端來說有兩點好處:

        1. 知其然,知其所以然
        2. 開拓眼界,多維發展

        1. 知其然,知其所以然

        我們都知道,在 JavaScript 中,有兩種數據類型,分別為基本類型和引用類型。

        基本類型

        let a, b
        a = 1
        b = a
        b = 3
        console.log(a) // 1
        console.log(b) // 3

        引用類型

        let a, b
        a = { msg: 'hello' }
        b = a
        b.msg = 'world'
        console.log(a) // { msg: "world" }
        console.log(b) // { msg: "world" }

        為什么基本類型 b 的值變了,a 不會變?而在引用類型中 b 的值變了,a也跟著變?如果你學習過內存管理以及編譯原理相關知識,就可以理解這個現象了。

        從程序的角度來看,內存被抽象為一個一維數組,a 和 b 在內存都占著一個位置,并且在內存中存儲的是它們各自的值。

        而引用類型則不同,在創建一個引用類型數據時,需要在堆中分配一塊內存,然后將這塊內存的地址返回。即 a = { msg: 'hello' } 這個操作,a 存儲的是一個地址。執行 b = a 后, a 和 b 指向同一個地址。當 b.msg = 'world' 這個操作執行時,改變的是這塊內存中的值,所以就不難理解為什么在引用類型中 b 的值變了,a 也跟著變了。

        以上只是其中一個例子,還有更多例子就不一一列舉了。

        學習計算機理論知識能讓我們不僅僅看到程序的表面,還能看到程序計算的本質。想一想,從你寫下一行代碼開始,經歷詞法分析、語法分析、生成機器碼,最后變成一條條指令在 CPU 中執行,并且數據在 CPU 與內存之間如何流轉你都了然如胸,這種感覺多奇妙。

        2. 開拓眼界,多維發展

        一個好的前端不僅僅是一個前端,不要只盯著眼前的一畝三分地,更要了解前端以外的知識。

        一個項目啟動前,通常會有一個需求討論會。如果你不懂理論知識,當聊到數據庫、服務器、并發等名詞時,你就只能兩眼一摸黑,插不上嘴,安安靜靜的坐在一邊。但如果你學過這方面的知識時,你就能和他們一起指點江山,不再是外人。

        在前端方向,理論知識也有用武之地。例如 babel,就需要用到編譯原理;研究 webgl,還得用上圖形學的知識;學了軟件工程,你就明白測試、團隊規范的重要性和必要性。說白了,懂計算機理論知識的前端和普通前端是站在不同維度上看問題的。

        計算機已經發展幾十年了,中間淘汰的技術數不勝數,前端還是最近幾年才火起來的,說不定哪天這個職業就沒了。如果發生這種情況,你還能干什么?

        技術會過時,理論知識不會過時,只要馮諾依曼體系還在,你學的東西就一直有用。學好計算機理論知識,不干前端,還能干別的。

        結論

        1. 計算機理論知識很有用。
        2. 剛入門先學好前端基礎知識,感覺水平差不多了,再去學習計算機理論知識。

        更多文章,敬請關注

        查看原文

        贊 9 收藏 3 評論 2

        n?i?g?h?t?i?r?e? 評論了文章 · 2019-05-09

        親歷,創業公司如何死掉的

        親歷,創業公司如何死掉的

        雖然目前尚未離職,但期限迫在眉睫。失望過后還得整頓自己再出發,新開始之前老感覺缺點東西,還是回顧一下東家是如何在這條創業路上死掉的,順便記錄下,已警示后來的自己。

        1. 目標宏達,出發點錯誤

        大Boss目標太過宏大,短時間內無法開發,項目周期太長, 項目落地時出發點偏離,導致剛開始就造重復的輪子。

        2. 技術團隊人員分布不合理

        領導在尋求技術團隊領導太過感性(后來才知道談了一個就確定了),技術領導招人的方向出現偏差(以前端全棧為標準),導致出現5個前端沒有后端,迫不得已才招1個后端,技術選型也很不友好(用到的技術實踐的公司很少)。

        3. 缺少專業產品經理和懂市場人員

        后來才知道產品經理是UI零時轉型,產品開發沒有切合市場需求, 想當然。

        4. 領導不懂技術,缺少團隊賦能

        大boss傳統企業出身,不懂互聯網技術,作為創業公司很少和團隊見面,也不去主導產品的方向。

        5. 工資遲發,影響氛圍

        工資長期遲發, 導致員工情緒很大程度受到了影響,人員變動也頻繁。

        ...

        我得到了什么

        1. 學習領導的的一些理念。
        2. 看清楚創業公司不應犯的錯誤。
        3. 主導從0到1的項目過程,嘗試做了小團隊的管理者。
        4. 發現自己對產品經理非常感興趣,除了coding,產品將是我的第二職業,非要排出的第三的話,不想做廚師的coder不是好PM。
        5. 對于如何生活,有了新的認識,當然也是受領導的感染。
        6. 創業不是有錢就能干的,很大可能是一幫志同道合的人沒錢給干成了, 所以初期的創業公司招人這個路數風險很大, 找人才是上策。
        7. 見識了當下融資的困境和面臨的問題, 必須要有的硬核擔當,總的來說,當下的經濟形勢,資本已將對待A輪公司的標準移到了種子輪或者天使輪, 所以創業更需要硬核產品,砸地必須有坑的產品,這也資本才可能青睞。
        8. 責任心和正道必須是創業者必須有的基本素質。
        9. 選擇前要慎重, 選擇后要要極力證明自己選擇的正確性,而不是盲目草率的重新選擇。
        10. 融資,不光融的是錢, 更重要的是資源, 怎么把盤子做大, 所以好多公司不是因為錢不夠,是想做大盤子而已。

        后續
        非常善良的人其實最是合創業, 但是絕對不能單獨帶領團隊,很多情況判斷會出現錯誤, 所以一定要有個合伙人唱白臉,在關鍵時刻來制止錯誤的決定, 我司可能就缺這么一個人。
        領導看過后覺得寫得很好, 欣慰之至,還點贊了, 期望新的開始能讓自己變得更好更強大,也希望能給新東家貢獻自己的力量。
        查看原文

        n?i?g?h?t?i?r?e? 評論了文章 · 2019-05-09

        親歷,創業公司如何死掉的

        親歷,創業公司如何死掉的

        雖然目前尚未離職,但期限迫在眉睫。失望過后還得整頓自己再出發,新開始之前老感覺缺點東西,還是回顧一下東家是如何在這條創業路上死掉的,順便記錄下,已警示后來的自己。

        1. 目標宏達,出發點錯誤

        大Boss目標太過宏大,短時間內無法開發,項目周期太長, 項目落地時出發點偏離,導致剛開始就造重復的輪子。

        2. 技術團隊人員分布不合理

        領導在尋求技術團隊領導太過感性(后來才知道談了一個就確定了),技術領導招人的方向出現偏差(以前端全棧為標準),導致出現5個前端沒有后端,迫不得已才招1個后端,技術選型也很不友好(用到的技術實踐的公司很少)。

        3. 缺少專業產品經理和懂市場人員

        后來才知道產品經理是UI零時轉型,產品開發沒有切合市場需求, 想當然。

        4. 領導不懂技術,缺少團隊賦能

        大boss傳統企業出身,不懂互聯網技術,作為創業公司很少和團隊見面,也不去主導產品的方向。

        5. 工資遲發,影響氛圍

        工資長期遲發, 導致員工情緒很大程度受到了影響,人員變動也頻繁。

        ...

        我得到了什么

        1. 學習領導的的一些理念。
        2. 看清楚創業公司不應犯的錯誤。
        3. 主導從0到1的項目過程,嘗試做了小團隊的管理者。
        4. 發現自己對產品經理非常感興趣,除了coding,產品將是我的第二職業,非要排出的第三的話,不想做廚師的coder不是好PM。
        5. 對于如何生活,有了新的認識,當然也是受領導的感染。
        6. 創業不是有錢就能干的,很大可能是一幫志同道合的人沒錢給干成了, 所以初期的創業公司招人這個路數風險很大, 找人才是上策。
        7. 見識了當下融資的困境和面臨的問題, 必須要有的硬核擔當,總的來說,當下的經濟形勢,資本已將對待A輪公司的標準移到了種子輪或者天使輪, 所以創業更需要硬核產品,砸地必須有坑的產品,這也資本才可能青睞。
        8. 責任心和正道必須是創業者必須有的基本素質。
        9. 選擇前要慎重, 選擇后要要極力證明自己選擇的正確性,而不是盲目草率的重新選擇。
        10. 融資,不光融的是錢, 更重要的是資源, 怎么把盤子做大, 所以好多公司不是因為錢不夠,是想做大盤子而已。

        后續
        非常善良的人其實最是合創業, 但是絕對不能單獨帶領團隊,很多情況判斷會出現錯誤, 所以一定要有個合伙人唱白臉,在關鍵時刻來制止錯誤的決定, 我司可能就缺這么一個人。
        領導看過后覺得寫得很好, 欣慰之至,還點贊了, 期望新的開始能讓自己變得更好更強大,也希望能給新東家貢獻自己的力量。
        查看原文

        n?i?g?h?t?i?r?e? 評論了文章 · 2019-04-29

        如何管理多個 Github 賬號的 ssh key?

        背景

        通常我們有使用 Github、Gitlab,當 Github 或 Gitlab 下有多個賬號,你會發現你的 ssh key 不夠用了,因為站點會給你個提示:

        ssh key already in use github

        這個時候我們可以采用如下做法

        1. 生成多個公鑰/私鑰對
        2. 對不同的域名配置不同的私鑰

        實操

        1. 生成多個公鑰/私鑰對
        ssh-keygen -t rsa -C "你的郵箱"

        用不同的郵箱創建不同的公鑰/私鑰對

        1. 添加私鑰
        ssh-add '私鑰路徑'

        ssh-add 提示并將用戶的使用的私鑰添加到由ssh-agent 維護的列表中. 此后, 當使用公鑰連接到遠程 SSH 或 SCP 主機時,不再提示相關信息

        1. 創建配置文件

        在 .ssh 目錄創建 config 文件,配置如下內容

        # gitlab
            Host git.iboxpay.com
            HostName git.iboxpay.com
            PreferredAuthentications publickey
            IdentityFile ~/.ssh/id_rsa
        
        # github
            Host github2.com
            HostName github.com
            PreferredAuthentications publickey
            IdentityFile ~/.ssh/feygh
        
        # github
        
           Host github.com
           HostName github.com
           PreferredAuthentications publickey
           IdentityFile ~/.ssh/id_rsa

        這個配置文件中 Gitlab 下的 ssh 通信都走 id_rsa 公鑰加密,所以你在 Gitlab 倉庫中要復制 id_rsa 這個公鑰過去;
        值得注意的是,這里有兩個 Github 配置,仔細觀察下 HostName 都是 github.com,但是 Host 不一樣,這就是關鍵之處。

        大家回憶下我們是怎么 clone Github 的?

        git@github.com:username/projectname.git

        分析這個地址 git@github.com 告訴 ssh 要登錄的主機,username 和 projectname 看字面意思就理解,不贅述。

        在這個配置文件中 Host 就是和 github.com 這個對應,HostName 是真實的物理連接 Host。

        如果大家這么 clone Github:

        git@github2.com:username/projectname.git

        那么進行這個 ssh 連接的時候,會命中 ssh config 中 Host 為 github2.com 的配置,反過來亦然。也就說你在 clone Github 另一個項目的時候要手動修改下地址為上述地址即可。

        查看原文

        n?i?g?h?t?i?r?e? 評論了文章 · 2019-01-11

        原生 js 實現移動端 Touch 滑動反彈

        image

        移動端 Touch 滑動反彈

        什么是Touch滑動?就是類似于PC端的滾動事件,但是在移動端是沒有滾動事件的,所以就要用到Touch事件結合js去實現,效果如下:

        效果圖

        1. 準備工作

        什么是移動端的Touch事件?在移動端Touch事件可以細分成三種,分別是:touchstart、touchmovetouchend,并且touch事件必須要用addEventListener去監聽。
        • touchStart當手指觸碰到屏幕的時候觸發
        • touchmove當手指在屏幕上不斷移動的時候觸發
        • touchend當手指離開屏幕的時候觸發

        Touch 事件觸發的 Event 對象:

        // 手指觸碰到屏幕時觸發
        element.addEventListener('touchstart', function (e) {
            // 打印的事件對象
            console.log(e);
        })

        Touch 事件對象

        changedTouches、targetTouches、touches都是偽數組,里面裝的是手指列表

        三種返回對象的區別:

        其實這三種返回的對象,都是表示用戶觸摸事件時的手指信息,之所以是一個偽數組,是因為有可能出現多指同時觸摸,但是在實際工作中一般不去考慮多指的情況。而它們唯一的區別就是在touchstarttouchmove事件的時候,changedTouches、targetTouches、touches都能獲取到手指的信息,但是在touchend事件的時候,targetTouches、touches對象是不能返回離開屏幕時的手指信息,只有changedTouches對象能返回。

        有哪些手指信息?

        我們可以看下上面的圖片,在changedTouche[0]中,有些值:
        clientX:74    // 觸摸點相對于瀏覽器的 viewport 左邊緣的 x 坐標,不會包括左邊的滾動距離。
        clientY:73    // 觸摸點相對于瀏覽器的 viewport 上邊緣的 Y 坐標,不會包括上邊的滾動距離。
        screenX:2202  // 觸摸點相對于屏幕左邊緣的 x 坐標。
        screenY:327   // 觸摸點相對于屏幕上邊緣的 Y 坐標。
        pageX:65      // 觸摸點相對于 document 的左邊緣的 x 坐標,包括左邊的滾動距離
        pageY:18      // 觸摸點相對于 document 的上邊緣的 Y 坐標,包括左邊的滾動距離

        2. 基本結構

        此案例模擬的是移動端的一種滑動菜單效果。

        HTML部分:

        <aside class="main">
          <div class="draw" id="draw">
            <ul>
              <li style="background:orange">列表一</li>
              <li style="background:yellowgreen">列表二</li>
              <li style="background:yellow">列表三</li>
              <li style="background:cyan">列表四</li>
              <li style="background:orangered">列表五</li>
              <li style="background:pink">列表六</li>
              <li style="background:red">列表七</li>
              <li style="background:purple">列表八</li>
              <li style="background:violet">列表九</li>
              <li style="background:brown">列表十</li>
            </ul>
          </div>
        </aside>

        css部分:

        在列表的父盒子上設定一個overflow: hidden屬性,使超出盒子部分的列表暫時隱藏掉,后面會通過js去實現滑動。
        /* 樣式初始化 */
        * {
          margin: 0;
          padding: 0;
        }
        
        html,
        body {
          width: 100%;
        
        }
        
        aside {
          height: 100%;
          width: 100%;
        }
        /* 列表的父盒子,限制寬高 */
        /* 注意設置overflow: hidden;樣式后,超出這個盒子的ul將不會顯示 */
        .draw {
          width: 60px;
          height: 500px;
          border: 2px solid #ccc;
          overflow: hidden;
          position: fixed;
          left: 10px;
          top: 50%;
          transform: translateY(-50%);
        }
        
        /* li 設置了浮動, 所以 ul 要清除浮動 */
        ul:after {
          content: "";
          display: block;
          visibility: hidden;
          height: 0;
          clear: both;
        }
        
        ul {
          zoom: 1;
        }
        
        li {
          list-style: none;
          float: left;
          width: 60px;
          height: 60px;
          line-height: 60px;
          text-align: center;
        }

        效果圖:

        基本結構

        3. 首次滑動

        手指觸摸到列表向下滑動的時候,列表應該跟著向下滑動,當手指離開屏幕的時候,列表應該停在滑動的位置。這里就會用到上面準備階段的知識點了,不明白的可以參考上面的概念。

        實現原理:

        • 1、touchstart的時候,獲取手指觸摸的落點A,通過這個點對象里面的clientY屬性,獲取距離頂部可視區的距離;
        • 2、touchmove的時候,獲取手指的點B,同樣的獲取移動時距離頂部可視區的距離;
        • 3、touchmove的時候,還要做另一件事情,就是獲取兩點的差值(B.clientY-A.clientY),將這個差值動態賦值給ul,ul只需要設置向Y軸方向偏移這個距離,就能實現列表隨手指滑動

        先來張示意圖,怎么通過 js 讓列表滑動起來:

        原理圖

        示例代碼:

        var draw = document.querySelector('#draw');
        var ul = draw.children[0];
        
        // touchstart 時,記錄手指在 Y 軸上的落點距離可視頂部距離
        var startY = 0
        ul.addEventListener('touchstart', function (e) {
          startY = e.changedTouches[0].clientY;
        })
        
        // touchmove 時,記錄此時手指在 Y 軸上的落點距離可視頂部距離
        ul.addEventListener('touchmove', function (e) {
          // 獲取差值
          var dy = e.changedTouches[0].clientY - startY;
          // 設置 ul 在 Y 軸上的偏移
          ul.style.transform = 'translateY(' + dy + 'px)';
        })

        效果圖:

        首次滑動

        4. 再次滑動

        上面的效果圖,細心的朋友可能已經發現了問題,在第一次的時候,得到了我們想要的效果,但是在第二次的時候,我們繼續向下移動了一段距離,但是ul并沒有接著第一次的位置繼續向下,而是瞬間跳了上去。

        問題分析:

        雖然第二次是繼續向下移動了一段距離,但是觸摸結束后,最終是將此時的差值,重新賦值給了ulY軸偏移,所以視覺上“跳了上去”。

        再次滑動

        解決方法:

        每一次滑動結束之后,都應該記錄下此次滑動的距離,與之前的進行累加,待下一次滑動的時候,ulY軸的偏移值應該是之前的距離加上本次滑動的距離。
        • 新增touchend事件,在該事件里同樣的可以獲取到本次滑動的距離,將它與上一次的距離相加,賦值給一個全局變量;
        • touchmove事件里有點小改動,就是在給ul設置偏移值的時候,除了本次滑動的差值還要加上這個上一次的值;

        示意圖:

        再次滑動解決方法

        示例代碼:

        var draw = document.querySelector('#draw');
        var ul = draw.children[0];
        
        var startY = 0 // 剛觸碰到屏幕的時的手指信息
        var centerY = 0 // 用來記錄每次觸摸時上一次的偏移距離
        
        // touchstart 時,記錄手指在 Y 軸上的落點距離可視頂部距離
        ul.addEventListener('touchstart', function (e) {
          startY = e.changedTouches[0].clientY;
        })
        
        // touchmove 時,記錄此時手指在 Y 軸上的落點距離可視頂部距離
        ul.addEventListener('touchmove', function (e) {
          // 獲取差值
          var dy = e.changedTouches[0].clientY - startY;
          // 上次的滑動距離加上本次的滑動距離
          var tempY = centerY + dy;
          // 設置 ul 在 Y 軸上的偏移
          ul.style.transform = 'translateY(' + tempY + 'px)';
        })
        
        // touchend 時,記錄此時手指在 Y 軸上的落點距離可視頂部距離
        ul.addEventListener('touchend', function (e) {
          // 獲取差值
          var dy = e.changedTouches[0].clientY - startY;
          // 記錄移動的距離
          centerY = centerY + dy;
        })

        效果圖:

        再次滑動效果圖

        5. 限制滑動區間

        到上面一步,我們已經可以實現列表的滑動了,但是也存在一個問題,就是向上或者向下的時候沒有限制,上下可以無限的滑動,甚至再用點力,就看不到列表了。為了美觀和實用,這樣肯定不行的,需要給它設定一個區間,設定向上或者向下最多只能留白多少。

        限制滑動區間效果圖

        限制向下滑動最大區間:

        設定向下最大區間的值比較簡單,直接設定一個值,當上一次滑動的距離加上本次滑動的距離大于這個值的時候,就不讓它再繼續往下滑了,讓他直接等于這個設定的值。

        示例代碼:

        var maxDown = 50; // 設定一個最大向下滑動的距離
        
        // touchmove 時,記錄此時手指在 Y 軸上的落點距離可視頂部距離
        ul.addEventListener('touchmove', function (e) {
          // 獲取差值
          var dy = e.changedTouches[0].clientY - startY;
        
          // 上次的滑動距離加上本次的滑動距離
          var tempY = centerY + dy;
        
          // 當上次滑動的距離加上本次滑動的距離 大于 設定的最大向下距離的時候
          if (tempY > maxDown) {
            // 直接讓偏移的值 等于這個設定值
            tempY = maxDown;
          }
        
          // 設置 ul 在 Y 軸上的偏移
          ul.style.transform = 'translateY(' + tempY + 'px)';
        })

        限制向上滑動最大區間:

        向上滑動時,當ul的底部距盒子底部的距離大于設定值的時候,不讓其繼續向上滑動,關鍵是這個值怎么去判斷?

        限制向上滑動最大區間

        求出向上滑動最大值:

        求出向上滑動最大值

        注意:因為ul是向上滑動的,所以求得的距離前面要加上一個負號(-

        示例代碼:

        // 設定一個最大向下滑動的距離
        var maxDown = 50; 
        // 求得一個最大向上滑動的距離
        var maxUp = -(ul.offsetHeight - draw.offsetHeight + maxDown); 
        
        // touchmove 時,記錄此時手指在 Y 軸上的落點距離可視頂部距離
        ul.addEventListener('touchmove', function (e) {
          // 獲取差值
          var dy = e.changedTouches[0].clientY - startY;
        
          // 上次的滑動距離加上本次的滑動距離
          var tempY = centerY + dy;
        
          // 當上次滑動的距離加上本次滑動的距離 大于 設定的最大向下距離的時候
          if (tempY > maxDown) {
            tempY = maxDown;
          }
          // 當上次滑動的距離加上本次滑動的距離 小于 設定的最大向上距離的時候 
          else if (tempY < maxUp) {
            // 直接讓偏移的值 等于這個設定值
            tempY = maxUp;
          }
        
          // 設置 ul 在 Y 軸上的偏移
          ul.style.transform = 'translateY(' + tempY + 'px)';
        })

        效果圖:

        設定最大滑動區間

        認真觀察上圖,雖然成功的設置了最大滑動區間,但是你有沒有發現,一直往一個方向滑動的時候,雖然列表不會繼續往下滑動,但是接著往相反方向滑動的時候,感覺列表滑不動,需要滑一段距離后,列表才會跟著走,這是為什么呢?因為滑動的過程centerY是一直變的,列表雖然視覺上不動了,但是在touchend事件的時候,它的centerY值一直在累加。解決方法請往下看:

        6. 設定反彈區間

        “滑動反彈”,這里的反彈是本篇文章的最后一步,上面說到的問題,就在這里解決。因為每一次觸發touchend事件的時候,centerY值就累加一次,所以需要在touchend事件里做判斷。我們設定一個反彈區間,就是當centerY的值大于或者小于某個值的時候,讓它觸發反彈。

        設定向上反彈值:

        向上的值比較簡單,設置成“0”。為什么是“0”呢?我們限定只要手指離開時,上一次的滑動距離加上本次的距離> 0的時候,就讓它觸發反彈,并且反彈回0點的位置,也就是兩次滑動的距離和= 0。

        向上反彈值

        示例代碼:

        // 向上反彈
        var maxUpBounce = 0;
        
        // touchend 時,記錄此時手指在 Y 軸上的落點距離可視頂部距離
        ul.addEventListener('touchend', function (e) {
          // 獲取差值
          var dy = e.changedTouches[0].clientY - startY;
          // 記錄移動的距離
          centerY = centerY + dy;
        
          // 兩次滑動的距離 大于 設定的 向上 反彈值時
          if (centerY > maxUpBounce) {
            // 讓兩次滑動的距離 等于 設置的值
            centerY = maxUpBounce;
            // 添加過渡
            ul.style.transition = 'transform .5s';
            ul.style.transform = 'translateY(' + centerY + 'px)';
          }
        })

        設定向下反彈值:

        向下的值其實跟之前求滑動區間差不多,我們參考下圖,當列表向上滑動,滑動到列表底部的時候,只要此時再向上滑動,就讓它向下反彈。向下反彈值就是-(ul.offsetHeight - draw.offsetHeight),只要滑動的差值小于這個設定值,就讓它向下反彈,并且反彈回設定值的位置。

        向下反彈值

        示例代碼:

        // 向上反彈值
        var maxUpBounce = 0; 
         // 向下反彈值
        var maxDownBounce = -(ul.offsetHeight - draw.offsetHeight);
        
        // touchend 時,記錄此時手指在 Y 軸上的落點距離可視頂部距離
        ul.addEventListener('touchend', function (e) {
          // 獲取差值
          var dy = e.changedTouches[0].clientY - startY;
          // 記錄移動的距離
          centerY = centerY + dy;
        
          // 兩次滑動的距離 大于 設定的 向上 反彈值時
          if (centerY > maxUpBounce) {
            // 讓兩次滑動的距離 等于 設置的值
            centerY = maxUpBounce;
            // 添加過渡
            ul.style.transition = 'transform .5s';
            ul.style.transform = 'translateY(' + centerY + 'px)';
          }
          // 兩次滑動的距離 小于 設定的 向下 反彈值時
          else if (centerY < maxDownBounce) {
            // 讓兩次滑動的距離 等于 設置的值
            centerY = maxDownBounce;
            // 添加過渡
            ul.style.transition = 'transform .5s';
            ul.style.transform = 'translateY(' + centerY + 'px)';
          }
        })

        注意:touchend事件的時候,給列表添加了transition屬性才會有反彈的效果,但是,下一次滑動的時候,touchmove事件的時候,這個屬性還存在,所以就會出現滑動的時候有頓挫感,所以在touchmove事件的時候,一進來就清一下過渡ul.style.transition = 'none';。

        完成后效果圖:

        效果圖

        7. 完整代碼

        <!DOCTYPE html>
        <html lang="en">
        
        <head>
          <meta charset="UTF-8">
          <meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
          <meta http-equiv="X-UA-Compatible" content="ie=edge">
          <title>移動端 Touch 滑動反彈</title>
          <style>
            /* 樣式初始化 */
            * {
              margin: 0;
              padding: 0;
            }
        
            html,
            body {
              width: 100%;
        
            }
        
            aside {
              height: 100%;
              width: 100%;
            }
        
            /* 列表的父盒子,限制寬高 */
            /* 注意設置overflow: hidden;樣式后,超出這個盒子的ul將不會顯示 */
            .draw {
              width: 60px;
              height: 500px;
              border: 2px solid #ccc;
              overflow: hidden;
              position: fixed;
              left: 10px;
              top: 50%;
              transform: translateY(-50%);
            }
        
            /* li 設置了浮動, 所以 ul 要清除浮動 */
            ul:after {
              content: "";
              display: block;
              visibility: hidden;
              height: 0;
              clear: both;
            }
        
            ul {
              zoom: 1;
            }
        
            li {
              list-style: none;
              float: left;
              width: 60px;
              height: 60px;
              line-height: 60px;
              text-align: center;
            }
          </style>
        </head>
        <aside class="main">
          <div class="draw" id="draw">
            <ul>
              <li style="background:orange">列表一</li>
              <li style="background:yellowgreen">列表二</li>
              <li style="background:yellow">列表三</li>
              <li style="background:cyan">列表四</li>
              <li style="background:orangered">列表五</li>
              <li style="background:pink">列表六</li>
              <li style="background:red">列表七</li>
              <li style="background:purple">列表八</li>
              <li style="background:violet">列表九</li>
              <li style="background:brown">列表十</li>
            </ul>
          </div>
        </aside>
        
        <body>
          <script>
            var draw = document.querySelector('#draw');
            var ul = draw.children[0];
        
            var startY = 0; // 剛觸碰到屏幕的時的手指信息
            var centerY = 0; // 用來記錄每次觸摸時上一次的偏移距離
            var maxDown = 50; // 設定一個最大向下滑動的距離
            var maxUp = -(ul.offsetHeight - draw.offsetHeight + maxDown); // 求得一個最大向上滑動的距離
            var maxUpBounce = 0; // 向上反彈值
            var maxDownBounce = -(ul.offsetHeight - draw.offsetHeight); // 向下反彈值
        
            // touchstart 時,記錄手指在 Y 軸上的落點距離可視頂部距離
            ul.addEventListener('touchstart', function (e) {
              startY = e.changedTouches[0].clientY;
            })
        
            // touchmove 時,記錄此時手指在 Y 軸上的落點距離可視頂部距離
            ul.addEventListener('touchmove', function (e) {
              // 清除過渡
              ul.style.transition = 'none';
              // 獲取差值
              var dy = e.changedTouches[0].clientY - startY;
        
              // 上次的滑動距離加上本次的滑動距離
              var tempY = centerY + dy;
        
              // 當上次滑動的距離加上本次滑動的距離 大于 設定的最大向下距離的時候
              if (tempY > maxDown) {
                tempY = maxDown;
              }
              // 當上次滑動的距離加上本次滑動的距離 小于 設定的最大向上距離的時候 
              else if (tempY < maxUp) {
                tempY = maxUp;
              }
        
              // 設置 ul 在 Y 軸上的偏移
              ul.style.transform = 'translateY(' + tempY + 'px)';
            })
        
            // touchend 時,記錄此時手指在 Y 軸上的落點距離可視頂部距離
            ul.addEventListener('touchend', function (e) {
              // 獲取差值
              var dy = e.changedTouches[0].clientY - startY;
              // 記錄移動的距離
              centerY = centerY + dy;
        
              // 兩次滑動的距離 大于 設定的 向上 反彈值時
              if (centerY > maxUpBounce) {
                // 讓兩次滑動的距離 等于 設置的值
                centerY = maxUpBounce;
                // 添加過渡
                ul.style.transition = 'transform .5s';
                ul.style.transform = 'translateY(' + centerY + 'px)';
              }
              // 兩次滑動的距離 小于 設定的 向下 反彈值時
              else if (centerY < maxDownBounce) {
                // 讓兩次滑動的距離 等于 設置的值
                centerY = maxDownBounce;
                // 添加過渡
                ul.style.transition = 'transform .5s';
                ul.style.transform = 'translateY(' + centerY + 'px)';
              }
            })
          </script>
        </body>
        
        </html>
        查看原文

        n?i?g?h?t?i?r?e? 贊了文章 · 2019-01-11

        原生 js 實現移動端 Touch 滑動反彈

        image

        移動端 Touch 滑動反彈

        什么是Touch滑動?就是類似于PC端的滾動事件,但是在移動端是沒有滾動事件的,所以就要用到Touch事件結合js去實現,效果如下:

        效果圖

        1. 準備工作

        什么是移動端的Touch事件?在移動端Touch事件可以細分成三種,分別是:touchstart、touchmovetouchend,并且touch事件必須要用addEventListener去監聽。
        • touchStart當手指觸碰到屏幕的時候觸發
        • touchmove當手指在屏幕上不斷移動的時候觸發
        • touchend當手指離開屏幕的時候觸發

        Touch 事件觸發的 Event 對象:

        // 手指觸碰到屏幕時觸發
        element.addEventListener('touchstart', function (e) {
            // 打印的事件對象
            console.log(e);
        })

        Touch 事件對象

        changedTouches、targetTouches、touches都是偽數組,里面裝的是手指列表

        三種返回對象的區別:

        其實這三種返回的對象,都是表示用戶觸摸事件時的手指信息,之所以是一個偽數組,是因為有可能出現多指同時觸摸,但是在實際工作中一般不去考慮多指的情況。而它們唯一的區別就是在touchstarttouchmove事件的時候,changedTouches、targetTouches、touches都能獲取到手指的信息,但是在touchend事件的時候,targetTouches、touches對象是不能返回離開屏幕時的手指信息,只有changedTouches對象能返回。

        有哪些手指信息?

        我們可以看下上面的圖片,在changedTouche[0]中,有些值:
        clientX:74    // 觸摸點相對于瀏覽器的 viewport 左邊緣的 x 坐標,不會包括左邊的滾動距離。
        clientY:73    // 觸摸點相對于瀏覽器的 viewport 上邊緣的 Y 坐標,不會包括上邊的滾動距離。
        screenX:2202  // 觸摸點相對于屏幕左邊緣的 x 坐標。
        screenY:327   // 觸摸點相對于屏幕上邊緣的 Y 坐標。
        pageX:65      // 觸摸點相對于 document 的左邊緣的 x 坐標,包括左邊的滾動距離
        pageY:18      // 觸摸點相對于 document 的上邊緣的 Y 坐標,包括左邊的滾動距離

        2. 基本結構

        此案例模擬的是移動端的一種滑動菜單效果。

        HTML部分:

        <aside class="main">
          <div class="draw" id="draw">
            <ul>
              <li style="background:orange">列表一</li>
              <li style="background:yellowgreen">列表二</li>
              <li style="background:yellow">列表三</li>
              <li style="background:cyan">列表四</li>
              <li style="background:orangered">列表五</li>
              <li style="background:pink">列表六</li>
              <li style="background:red">列表七</li>
              <li style="background:purple">列表八</li>
              <li style="background:violet">列表九</li>
              <li style="background:brown">列表十</li>
            </ul>
          </div>
        </aside>

        css部分:

        在列表的父盒子上設定一個overflow: hidden屬性,使超出盒子部分的列表暫時隱藏掉,后面會通過js去實現滑動。
        /* 樣式初始化 */
        * {
          margin: 0;
          padding: 0;
        }
        
        html,
        body {
          width: 100%;
        
        }
        
        aside {
          height: 100%;
          width: 100%;
        }
        /* 列表的父盒子,限制寬高 */
        /* 注意設置overflow: hidden;樣式后,超出這個盒子的ul將不會顯示 */
        .draw {
          width: 60px;
          height: 500px;
          border: 2px solid #ccc;
          overflow: hidden;
          position: fixed;
          left: 10px;
          top: 50%;
          transform: translateY(-50%);
        }
        
        /* li 設置了浮動, 所以 ul 要清除浮動 */
        ul:after {
          content: "";
          display: block;
          visibility: hidden;
          height: 0;
          clear: both;
        }
        
        ul {
          zoom: 1;
        }
        
        li {
          list-style: none;
          float: left;
          width: 60px;
          height: 60px;
          line-height: 60px;
          text-align: center;
        }

        效果圖:

        基本結構

        3. 首次滑動

        手指觸摸到列表向下滑動的時候,列表應該跟著向下滑動,當手指離開屏幕的時候,列表應該停在滑動的位置。這里就會用到上面準備階段的知識點了,不明白的可以參考上面的概念。

        實現原理:

        • 1、touchstart的時候,獲取手指觸摸的落點A,通過這個點對象里面的clientY屬性,獲取距離頂部可視區的距離;
        • 2、touchmove的時候,獲取手指的點B,同樣的獲取移動時距離頂部可視區的距離;
        • 3、touchmove的時候,還要做另一件事情,就是獲取兩點的差值(B.clientY-A.clientY),將這個差值動態賦值給ul,ul只需要設置向Y軸方向偏移這個距離,就能實現列表隨手指滑動

        先來張示意圖,怎么通過 js 讓列表滑動起來:

        原理圖

        示例代碼:

        var draw = document.querySelector('#draw');
        var ul = draw.children[0];
        
        // touchstart 時,記錄手指在 Y 軸上的落點距離可視頂部距離
        var startY = 0
        ul.addEventListener('touchstart', function (e) {
          startY = e.changedTouches[0].clientY;
        })
        
        // touchmove 時,記錄此時手指在 Y 軸上的落點距離可視頂部距離
        ul.addEventListener('touchmove', function (e) {
          // 獲取差值
          var dy = e.changedTouches[0].clientY - startY;
          // 設置 ul 在 Y 軸上的偏移
          ul.style.transform = 'translateY(' + dy + 'px)';
        })

        效果圖:

        首次滑動

        4. 再次滑動

        上面的效果圖,細心的朋友可能已經發現了問題,在第一次的時候,得到了我們想要的效果,但是在第二次的時候,我們繼續向下移動了一段距離,但是ul并沒有接著第一次的位置繼續向下,而是瞬間跳了上去。

        問題分析:

        雖然第二次是繼續向下移動了一段距離,但是觸摸結束后,最終是將此時的差值,重新賦值給了ulY軸偏移,所以視覺上“跳了上去”。

        再次滑動

        解決方法:

        每一次滑動結束之后,都應該記錄下此次滑動的距離,與之前的進行累加,待下一次滑動的時候,ulY軸的偏移值應該是之前的距離加上本次滑動的距離。
        • 新增touchend事件,在該事件里同樣的可以獲取到本次滑動的距離,將它與上一次的距離相加,賦值給一個全局變量;
        • touchmove事件里有點小改動,就是在給ul設置偏移值的時候,除了本次滑動的差值還要加上這個上一次的值;

        示意圖:

        再次滑動解決方法

        示例代碼:

        var draw = document.querySelector('#draw');
        var ul = draw.children[0];
        
        var startY = 0 // 剛觸碰到屏幕的時的手指信息
        var centerY = 0 // 用來記錄每次觸摸時上一次的偏移距離
        
        // touchstart 時,記錄手指在 Y 軸上的落點距離可視頂部距離
        ul.addEventListener('touchstart', function (e) {
          startY = e.changedTouches[0].clientY;
        })
        
        // touchmove 時,記錄此時手指在 Y 軸上的落點距離可視頂部距離
        ul.addEventListener('touchmove', function (e) {
          // 獲取差值
          var dy = e.changedTouches[0].clientY - startY;
          // 上次的滑動距離加上本次的滑動距離
          var tempY = centerY + dy;
          // 設置 ul 在 Y 軸上的偏移
          ul.style.transform = 'translateY(' + tempY + 'px)';
        })
        
        // touchend 時,記錄此時手指在 Y 軸上的落點距離可視頂部距離
        ul.addEventListener('touchend', function (e) {
          // 獲取差值
          var dy = e.changedTouches[0].clientY - startY;
          // 記錄移動的距離
          centerY = centerY + dy;
        })

        效果圖:

        再次滑動效果圖

        5. 限制滑動區間

        到上面一步,我們已經可以實現列表的滑動了,但是也存在一個問題,就是向上或者向下的時候沒有限制,上下可以無限的滑動,甚至再用點力,就看不到列表了。為了美觀和實用,這樣肯定不行的,需要給它設定一個區間,設定向上或者向下最多只能留白多少。

        限制滑動區間效果圖

        限制向下滑動最大區間:

        設定向下最大區間的值比較簡單,直接設定一個值,當上一次滑動的距離加上本次滑動的距離大于這個值的時候,就不讓它再繼續往下滑了,讓他直接等于這個設定的值。

        示例代碼:

        var maxDown = 50; // 設定一個最大向下滑動的距離
        
        // touchmove 時,記錄此時手指在 Y 軸上的落點距離可視頂部距離
        ul.addEventListener('touchmove', function (e) {
          // 獲取差值
          var dy = e.changedTouches[0].clientY - startY;
        
          // 上次的滑動距離加上本次的滑動距離
          var tempY = centerY + dy;
        
          // 當上次滑動的距離加上本次滑動的距離 大于 設定的最大向下距離的時候
          if (tempY > maxDown) {
            // 直接讓偏移的值 等于這個設定值
            tempY = maxDown;
          }
        
          // 設置 ul 在 Y 軸上的偏移
          ul.style.transform = 'translateY(' + tempY + 'px)';
        })

        限制向上滑動最大區間:

        向上滑動時,當ul的底部距盒子底部的距離大于設定值的時候,不讓其繼續向上滑動,關鍵是這個值怎么去判斷?

        限制向上滑動最大區間

        求出向上滑動最大值:

        求出向上滑動最大值

        注意:因為ul是向上滑動的,所以求得的距離前面要加上一個負號(-

        示例代碼:

        // 設定一個最大向下滑動的距離
        var maxDown = 50; 
        // 求得一個最大向上滑動的距離
        var maxUp = -(ul.offsetHeight - draw.offsetHeight + maxDown); 
        
        // touchmove 時,記錄此時手指在 Y 軸上的落點距離可視頂部距離
        ul.addEventListener('touchmove', function (e) {
          // 獲取差值
          var dy = e.changedTouches[0].clientY - startY;
        
          // 上次的滑動距離加上本次的滑動距離
          var tempY = centerY + dy;
        
          // 當上次滑動的距離加上本次滑動的距離 大于 設定的最大向下距離的時候
          if (tempY > maxDown) {
            tempY = maxDown;
          }
          // 當上次滑動的距離加上本次滑動的距離 小于 設定的最大向上距離的時候 
          else if (tempY < maxUp) {
            // 直接讓偏移的值 等于這個設定值
            tempY = maxUp;
          }
        
          // 設置 ul 在 Y 軸上的偏移
          ul.style.transform = 'translateY(' + tempY + 'px)';
        })

        效果圖:

        設定最大滑動區間

        認真觀察上圖,雖然成功的設置了最大滑動區間,但是你有沒有發現,一直往一個方向滑動的時候,雖然列表不會繼續往下滑動,但是接著往相反方向滑動的時候,感覺列表滑不動,需要滑一段距離后,列表才會跟著走,這是為什么呢?因為滑動的過程centerY是一直變的,列表雖然視覺上不動了,但是在touchend事件的時候,它的centerY值一直在累加。解決方法請往下看:

        6. 設定反彈區間

        “滑動反彈”,這里的反彈是本篇文章的最后一步,上面說到的問題,就在這里解決。因為每一次觸發touchend事件的時候,centerY值就累加一次,所以需要在touchend事件里做判斷。我們設定一個反彈區間,就是當centerY的值大于或者小于某個值的時候,讓它觸發反彈。

        設定向上反彈值:

        向上的值比較簡單,設置成“0”。為什么是“0”呢?我們限定只要手指離開時,上一次的滑動距離加上本次的距離> 0的時候,就讓它觸發反彈,并且反彈回0點的位置,也就是兩次滑動的距離和= 0。

        向上反彈值

        示例代碼:

        // 向上反彈
        var maxUpBounce = 0;
        
        // touchend 時,記錄此時手指在 Y 軸上的落點距離可視頂部距離
        ul.addEventListener('touchend', function (e) {
          // 獲取差值
          var dy = e.changedTouches[0].clientY - startY;
          // 記錄移動的距離
          centerY = centerY + dy;
        
          // 兩次滑動的距離 大于 設定的 向上 反彈值時
          if (centerY > maxUpBounce) {
            // 讓兩次滑動的距離 等于 設置的值
            centerY = maxUpBounce;
            // 添加過渡
            ul.style.transition = 'transform .5s';
            ul.style.transform = 'translateY(' + centerY + 'px)';
          }
        })

        設定向下反彈值:

        向下的值其實跟之前求滑動區間差不多,我們參考下圖,當列表向上滑動,滑動到列表底部的時候,只要此時再向上滑動,就讓它向下反彈。向下反彈值就是-(ul.offsetHeight - draw.offsetHeight),只要滑動的差值小于這個設定值,就讓它向下反彈,并且反彈回設定值的位置。

        向下反彈值

        示例代碼:

        // 向上反彈值
        var maxUpBounce = 0; 
         // 向下反彈值
        var maxDownBounce = -(ul.offsetHeight - draw.offsetHeight);
        
        // touchend 時,記錄此時手指在 Y 軸上的落點距離可視頂部距離
        ul.addEventListener('touchend', function (e) {
          // 獲取差值
          var dy = e.changedTouches[0].clientY - startY;
          // 記錄移動的距離
          centerY = centerY + dy;
        
          // 兩次滑動的距離 大于 設定的 向上 反彈值時
          if (centerY > maxUpBounce) {
            // 讓兩次滑動的距離 等于 設置的值
            centerY = maxUpBounce;
            // 添加過渡
            ul.style.transition = 'transform .5s';
            ul.style.transform = 'translateY(' + centerY + 'px)';
          }
          // 兩次滑動的距離 小于 設定的 向下 反彈值時
          else if (centerY < maxDownBounce) {
            // 讓兩次滑動的距離 等于 設置的值
            centerY = maxDownBounce;
            // 添加過渡
            ul.style.transition = 'transform .5s';
            ul.style.transform = 'translateY(' + centerY + 'px)';
          }
        })

        注意:touchend事件的時候,給列表添加了transition屬性才會有反彈的效果,但是,下一次滑動的時候,touchmove事件的時候,這個屬性還存在,所以就會出現滑動的時候有頓挫感,所以在touchmove事件的時候,一進來就清一下過渡ul.style.transition = 'none';。

        完成后效果圖:

        效果圖

        7. 完整代碼

        <!DOCTYPE html>
        <html lang="en">
        
        <head>
          <meta charset="UTF-8">
          <meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
          <meta http-equiv="X-UA-Compatible" content="ie=edge">
          <title>移動端 Touch 滑動反彈</title>
          <style>
            /* 樣式初始化 */
            * {
              margin: 0;
              padding: 0;
            }
        
            html,
            body {
              width: 100%;
        
            }
        
            aside {
              height: 100%;
              width: 100%;
            }
        
            /* 列表的父盒子,限制寬高 */
            /* 注意設置overflow: hidden;樣式后,超出這個盒子的ul將不會顯示 */
            .draw {
              width: 60px;
              height: 500px;
              border: 2px solid #ccc;
              overflow: hidden;
              position: fixed;
              left: 10px;
              top: 50%;
              transform: translateY(-50%);
            }
        
            /* li 設置了浮動, 所以 ul 要清除浮動 */
            ul:after {
              content: "";
              display: block;
              visibility: hidden;
              height: 0;
              clear: both;
            }
        
            ul {
              zoom: 1;
            }
        
            li {
              list-style: none;
              float: left;
              width: 60px;
              height: 60px;
              line-height: 60px;
              text-align: center;
            }
          </style>
        </head>
        <aside class="main">
          <div class="draw" id="draw">
            <ul>
              <li style="background:orange">列表一</li>
              <li style="background:yellowgreen">列表二</li>
              <li style="background:yellow">列表三</li>
              <li style="background:cyan">列表四</li>
              <li style="background:orangered">列表五</li>
              <li style="background:pink">列表六</li>
              <li style="background:red">列表七</li>
              <li style="background:purple">列表八</li>
              <li style="background:violet">列表九</li>
              <li style="background:brown">列表十</li>
            </ul>
          </div>
        </aside>
        
        <body>
          <script>
            var draw = document.querySelector('#draw');
            var ul = draw.children[0];
        
            var startY = 0; // 剛觸碰到屏幕的時的手指信息
            var centerY = 0; // 用來記錄每次觸摸時上一次的偏移距離
            var maxDown = 50; // 設定一個最大向下滑動的距離
            var maxUp = -(ul.offsetHeight - draw.offsetHeight + maxDown); // 求得一個最大向上滑動的距離
            var maxUpBounce = 0; // 向上反彈值
            var maxDownBounce = -(ul.offsetHeight - draw.offsetHeight); // 向下反彈值
        
            // touchstart 時,記錄手指在 Y 軸上的落點距離可視頂部距離
            ul.addEventListener('touchstart', function (e) {
              startY = e.changedTouches[0].clientY;
            })
        
            // touchmove 時,記錄此時手指在 Y 軸上的落點距離可視頂部距離
            ul.addEventListener('touchmove', function (e) {
              // 清除過渡
              ul.style.transition = 'none';
              // 獲取差值
              var dy = e.changedTouches[0].clientY - startY;
        
              // 上次的滑動距離加上本次的滑動距離
              var tempY = centerY + dy;
        
              // 當上次滑動的距離加上本次滑動的距離 大于 設定的最大向下距離的時候
              if (tempY > maxDown) {
                tempY = maxDown;
              }
              // 當上次滑動的距離加上本次滑動的距離 小于 設定的最大向上距離的時候 
              else if (tempY < maxUp) {
                tempY = maxUp;
              }
        
              // 設置 ul 在 Y 軸上的偏移
              ul.style.transform = 'translateY(' + tempY + 'px)';
            })
        
            // touchend 時,記錄此時手指在 Y 軸上的落點距離可視頂部距離
            ul.addEventListener('touchend', function (e) {
              // 獲取差值
              var dy = e.changedTouches[0].clientY - startY;
              // 記錄移動的距離
              centerY = centerY + dy;
        
              // 兩次滑動的距離 大于 設定的 向上 反彈值時
              if (centerY > maxUpBounce) {
                // 讓兩次滑動的距離 等于 設置的值
                centerY = maxUpBounce;
                // 添加過渡
                ul.style.transition = 'transform .5s';
                ul.style.transform = 'translateY(' + centerY + 'px)';
              }
              // 兩次滑動的距離 小于 設定的 向下 反彈值時
              else if (centerY < maxDownBounce) {
                // 讓兩次滑動的距離 等于 設置的值
                centerY = maxDownBounce;
                // 添加過渡
                ul.style.transition = 'transform .5s';
                ul.style.transform = 'translateY(' + centerY + 'px)';
              }
            })
          </script>
        </body>
        
        </html>
        查看原文

        贊 95 收藏 65 評論 12

        認證與成就

        • 獲得 2522 次點贊
        • 獲得 92 枚徽章 獲得 5 枚金徽章, 獲得 39 枚銀徽章, 獲得 48 枚銅徽章

        擅長技能
        編輯

        (??? )
        暫時沒有

        開源項目 & 著作
        編輯

        (??? )
        暫時沒有

        注冊于 2012-10-10
        個人主頁被 26.3k 人瀏覽

        一本到在线是免费观看_亚洲2020天天堂在线观看_国产欧美亚洲精品第一页_最好看的2018中文字幕