<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          什么是前端工程化

          共 5531字,需瀏覽 12分鐘

           ·

          2021-03-31 10:30

          來源:Lucas HC

          https://www.zhihu.com/question/433854153/answer/1713597311

          我認為,「什么是前端工程化」——這是一個很好的問題,但同時也是一個非?!竸?wù)虛」的問題。

          因為前端工程化是一個極度寬泛且宏大的概念,我們很難去下一個定義,也無法給出一個樣例來解釋。我試圖從工程(構(gòu)建)工具對比和一個線上 bug 的處理來側(cè)面說明。


          工具篇

          提到工程化(構(gòu)建)工具,作為經(jīng)驗豐富的前端開發(fā)者,相信你能列舉出不同時代的代表:從 Browserify + Gulp 到 Parcel,從 Webpack 到 Rollup,甚至 @尤雨溪編寫的 Vite,相信你也并不陌生。沒錯,前端發(fā)展到現(xiàn)在,工程化工具琳瑯滿目。但很多工具的實現(xiàn)和設(shè)計非常復雜,甚至出現(xiàn)了「面向 webpack 編程」的調(diào)侃。

          ToolingReport 是由 Chrome core team 核心成員以及業(yè)內(nèi)著名開發(fā)者打造的構(gòu)建工具比對平臺。這個平臺對比了 Webpack v4、Rollup v2、Parcel v2、Browserify + Gulp 在不同維度下的表現(xiàn),如下圖所示:

          測評通過的 test 得分只是一個方面,實際情況也和不同構(gòu)建工具的設(shè)計目標有關(guān)。比如,Webpack 的構(gòu)建主要依賴了插件和 loader,因此它的能力雖然強大,但配置信息較為煩瑣。而 Parcel 的設(shè)計目標之一就是零配置,開箱即用,但是在功能的集成上相對有限。

          但從工程化的角度出發(fā),我們還是從上面的分數(shù)分析,來看看這些分數(shù)評測的維度。這些分數(shù)來自以下 6 個維度的評測:

          • Code Splitting
          • Hashing
          • Importing Modules
          • Non-JavaScript Resources
          • Output Module Formats
          • Transformations

          和工程化主題相關(guān)的是:這 6 個維度到底是什么,為什么它們能作為考量指標被選取為評測參考標準?下面我們逐一進行分析。

          Code Splitting,即代碼分割。這意味著在構(gòu)建打包時,能夠?qū)㈧o態(tài)資源拆分,因此在頁面加載時,實現(xiàn)最合理的按需加載策略。

          實際上,Code Splitting 是一個很深的話題。比如:不同模塊間的代碼分割機制能否支持不同的上下文環(huán)境(Web worker 環(huán)境等特殊上下文情況),如何實現(xiàn)對 Dynamic Import 語法特性的支持,應(yīng)用配置多入口/單入口時是否支持重復模塊的抽取并打包,代碼模塊間是否支持 Living Bindings(如果被依賴的 module 中的值發(fā)生了變化,則會映射到所有依賴該值的模塊中)。

          總之,Code Splitting 直接決定了前端的靜態(tài)資源產(chǎn)出情況,影響著項目應(yīng)用的性能表現(xiàn)。是前端工程化這顆大樹的一個分支。

          Hashing,即對打包資源進行版本信息映射。這個話題背后的重要技術(shù)點是最合理地利用緩存機制。我們知道有效的緩存策略將直接影響頁面加載表現(xiàn),決定用戶體驗。那么對于前端工程化來說,為了實現(xiàn)更合理的 hash 機制,工具就需要分析各種打包資源,導出模塊間依賴關(guān)系,依據(jù)依賴關(guān)系上下文決定產(chǎn)出包的哈希值。因為一個資源的變動,將會引起其依賴下游的關(guān)聯(lián)資源變動,因此工程工具進行打包的前提就是對各個模塊依賴關(guān)系進行分析,并根據(jù)依賴關(guān)系,支持開發(fā)者自行定義哈希策略。比如,Webpack 提供的不同類型 hash 的區(qū)別:hash/chunkhash/contenthash,這三種 hashing 策略你都了解嗎?為什么有這三種策略的設(shè)計呢?具體我就不展開了。

          Output Module Formats,工程輸出的模塊化方式也需要更加靈活,比如開發(fā)者可配置 ESM、CommonJS 等規(guī)范的構(gòu)建內(nèi)容導出。

          Transformations,前端工程化離不開編譯/轉(zhuǎn)義過程。比如對 JavaScript 代碼的壓縮、對無用代碼的刪除(DCE)等。這里需要站在工程化視覺上注意的是,我們在設(shè)計構(gòu)建工具時,對于類似 JSX 的編譯、.vue 文件的編譯,不會內(nèi)置到工具當中,而是利用 Babel 等社區(qū)能力,「無縫融合」到工程化流程里。工程化工具只做分內(nèi)的事情,其他擴展能力通過插件化機制來完成,顯然是一個非常工程化的設(shè)計。

          其他 Importing Modules 以及 Non-JavaScript Resources 我不多說了,雖然這是評測工程化工具的幾個大方向,但每一個都是前端工程化的重要主題。

          線上問題篇

          這一部分,讓我們以一篇文章《報告老板,我們的 H5 頁面在 iOS 11 系統(tǒng)上白屏了!》分析,我先簡單梳理和總結(jié)一下文章表達的內(nèi)容,讀者看我總結(jié)即可:

          • 筆者發(fā)現(xiàn)某些機型上出現(xiàn)頁面白屏情況;
          • 出現(xiàn)在報錯頁面上的信息非常明顯,即當前瀏覽器不支持 ... 擴展運算符;
          • 出錯的代碼(使用了擴展運算符的代碼)屬于某個公共庫代碼,它沒有使用 Babel 插件進行降級處理,因此線上源代碼出現(xiàn)了 ... 擴展運算符。

          現(xiàn)在問題找到了,或許直接將出現(xiàn)問題的公共庫代碼用 Babel 進行編譯降級就可以了。在文中環(huán)境下,需要在 vue.config.js 中加入對問題公共庫 module-name/library-name 的 Babel 編譯流程:

          transpileDependencies: [
            'module-name/library-name' // 出現(xiàn)問題的那個庫
          ],

          vue-cli 對 transpileDependencies 也有如下說明:

          默認情況下 babel-loader 會忽略所有 node_modules 中的文件。如果你想要通過 Babel 顯式轉(zhuǎn)譯一個依賴,可以在這個選項中列出來。

          按照上述操作,卻得到了新的報錯:Uncaught TypeError: Cannot assign to read only property 'exports' of object '#<Object>'。

          究其原因,module-name/library-name 這個庫對外輸出的是 CommonJS 類型源碼,我們對該庫進行編譯后,項目基礎(chǔ)設(shè)施中會通過 babel-transform-runtime 在編譯時增加 helper 代碼,而這些 helper 使用的是 import 引入。最終編譯結(jié)果出現(xiàn)了 ESM 包含 CommonJS 的情況,是不會被 Webpack 處理的。

          我再次分析下出現(xiàn)的新的問題:

          • plugin-transform-runtime 會根據(jù) sourceType 選擇注入 import 或者 require,sourceType 的默認值是 module,就會默認注入 import;
          • Webpack 不會處理包含 import/export 的文件中的 module.exports 導出,所以需要讓 Babel 自動判斷 sourceType,根據(jù)文件內(nèi)是否存在 import/export 來決定注入什么樣的代碼。

          為了適配上述問題,Babel 設(shè)置了 sourceType 屬性,sourceType:unambiguous 表示 Babel 會根據(jù)文件上下文(比如是否含有 import/export)來決定是否按照 ESM 語法處理文件。

          這時候就需要配置 Babel 內(nèi)容了:

          module.exports = {
            ...  // 省略的配置
            sourceType'unambiguous',
            ...  // 省略的配置

          但是這種做法在工程上并不推薦,上述更改方式對所有編譯文件都生效,但也增加了編譯成本(因為設(shè)置 sourceType:unambiguous 后,編譯時需要做的事情更多),還有個潛在問題:

          Unambiguous can be quite useful in contexts where the type is unknown, but it can lead to false matches because it's perfectly valid to have a module file that does not use import/export statements.

          翻譯過來,就是說并不是所有的 ESM 模塊(這里指使用 ESNext 特性的文件)都含有 import/export,因此即便某個待編譯文件屬于 ESM 模塊,也可能被 Babel 錯誤地判斷為 CommonJS 模塊而引發(fā)誤判。

          **基于這一點,一個更合適的做法是:**只對目標第三方庫 'module-name/library-name' 使用 sourceType:unambiguous,這時 Babel overrides 屬性就派上用場了:

          Allows users to provide an array of options that will be merged into the current configuration one at a time. This feature is best used alongside the "test"/"include"/"exclude" options to provide conditions for which an override should apply.

          具體使用方式:

          module.exports = {
           ...  // 省略的配置  
           overrides: [    
            { include'./node_modules/module-name/library-name/name.common.js',  // 使用的第三方庫      
            sourceType'unambiguous'    
            }  
           ], 
            ...  // 省略的配置
          };

          至此,這個“iOS 11 系統(tǒng)白屏”問題就算告一段落了(你有沒有被各種配置和設(shè)計搞得云里霧里?)。

          我整理了解決路線,如下圖所示:

          我們回過頭再來看這個問題,問題其實出現(xiàn)在一個公共庫上,因而前端生態(tài)的混亂和復雜也許是更本質(zhì)的原因,但這都轉(zhuǎn)嫁為前端工程化的難點。

          我們進一步思考:

          • 作為公共庫,我應(yīng)該如何構(gòu)建編譯代碼,讓業(yè)務(wù)方更有保障地使用?
          • 作為使用者,我應(yīng)該如何處理第三方公共庫,是否還需要對其進行額外編譯和處理?

          被動地發(fā)現(xiàn)問題、解決問題只會讓我們被「牽著鼻子走」——這不是我們的目的。感興趣的讀者可以點贊,關(guān)注,我會很快輸出更多關(guān)于「前端工程化」的內(nèi)容。


          最后的話

          對于很多前端工程師來說,你可能配置過 Babel/Webpack,也可能看過一些關(guān)于 Babel/Webpack 插件或原理的文章。但我認為,通過閱讀幾篇 Babel/Webpack 插件編寫甚至 AST 分析的文章并不能讓我們真正掌握前端工程化。這也完全完全不是前端工程化的要義。

          「配置工程師」只是我們的起點。作為前端開發(fā)者,你可能會被繁瑣的配置和工具所困擾,自己的終端脆弱無比,出現(xiàn)各種報錯。此時,你可能花費了一天的時間,通過 Google 找到了最終的配置解法;或者通過:

          rm -rf node_modules + npm install + npm run dev 

          規(guī)避了問題。但是解決之道卻沒搞清楚,得過且過,今后依然被類似的困境襲擾。

          當我們對配置、工具、構(gòu)建流程、架構(gòu)設(shè)計、生產(chǎn)發(fā)布等環(huán)節(jié)的各種挑戰(zhàn)和問題能有系統(tǒng)化的思考時,「前端工程化」自然也不會再是一個困惑。

          其實很抱歉我無法回答題主這個宏大的問題,我自己也受此困擾,僅以兩個小的細節(jié)方面拋磚引玉(閑時我也會持續(xù)輸出更多關(guān)于「前端工程化」的內(nèi)容)。

          總之,前端既收獲著快速發(fā)展,也迎接著批量劣汰;前端技術(shù)有著與生俱來的混亂,也有著與之抗衡的規(guī)范 —— 這都對前端工程化提出了更高的挑戰(zhàn)。

          最后

          歡迎關(guān)注【前端瓶子君】??ヽ(°▽°)ノ?
          回復「算法」,加入前端算法源碼編程群,每日一刷(工作日),每題瓶子君都會很認真的解答喲
          回復「交流」,吹吹水、聊聊技術(shù)、吐吐槽!
          回復「閱讀」,每日刷刷高質(zhì)量好文!
          如果這篇文章對你有幫助,在看」是最大的支持
          》》面試官也在看的算法資料《《
          “在看和轉(zhuǎn)發(fā)”就是最大的支持
          瀏覽 29
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  色视频在线 | 无码免费在线看 | 青青草视频无码 | 最新亚洲国产黄色视频在线观看 | 国产高清无码在线观看视频 |