知乎高赞:什么是前端工程化-LMLPHP

作者:Lucas HC

  • 知乎高赞:什么是前端工程化-LMLPHP

    和工程化主题相关的是:这 6 个维度到底是什么,为什么它们能作为考量指标被选取为评测参考标准?下面我们逐一进行分析。

    Code Splitting,即代码分割。这意味着在构建打包时,能够将静态资源拆分,因此在页面加载时,实现最合理的按需加载策略

    实际上,Code Splitting 是一个很深的话题。比如:不同模块间的代码分割机制能否支持不同的上下文环境(Web worker 环境等特殊上下文情况),如何实现对 Dynamic Import 语法特性的支持,应用配置多入口/单入口时是否支持重复模块的抽取并打包,代码模块间是否支持 Living Bindings(如果被依赖的 module 中的值发生了变化,则会映射到所有依赖该值的模块中)。

    总之,Code Splitting 直接决定了前端的静态资源产出情况,影响着项目应用的性能表现。是前端工程化这颗大树的一个分支。

    Hashing,即对打包资源进行版本信息映射。这个话题背后的重要技术点是最合理地利用缓存机制。我们知道有效的缓存策略将直接影响页面加载表现,决定用户体验。那么对于前端工程化来说,为了实现更合理的 hash 机制,工具就需要分析各种打包资源,导出模块间依赖关系,依据依赖关系上下文决定产出包的哈希值。因为一个资源的变动,将会引起其依赖下游的关联资源变动,因此工程工具进行打包的前提就是对各个模块依赖关系进行分析,并根据依赖关系,支持开发者自行定义哈希策略。比如,Webpack 提供的不同类型 hash 的区别:hash/chunkhash/contenthash,这三种 hashing 策略你都了解吗?为什么有这三种策略的设计呢?具体我就不展开了。

    Output Module Formats,工程输出的模块化方式也需要更加灵活,比如开发者可配置 ESM、CommonJS 等规范的构建内容导出。

    Transformations,前端工程化离不开编译/转义过程。比如对 JavaScript 代码的压缩、对无用代码的删除(DCE)等。这里需要站在工程化视觉上注意的是,我们在设计构建工具时,对于类似 JSX 的编译、.vue 文件的编译,不会内置到工具当中,而是利用 Babel 等社区能力,「无缝融合」到工程化流程里。工程化工具只做分内的事情,其他扩展能力通过插件化机制来完成,显然是一个非常工程化的设计。

    其他 Importing Modules 以及 Non-JavaScript Resources 我不多说了,虽然这是评测工程化工具的几个大方向,但每一个都是前端工程化的重要主题。

    线上问题篇

    这一部分,让我们以一篇文章《报告老板,我们的 H5 页面在 iOS 11 系统上白屏了!》分析,我先简单梳理和总结一下文章表达的内容,读者看我总结即可:

    现在问题找到了,或许直接将出现问题的公共库代码用 Babel 进行编译降级就可以了。在文中环境下,需要在 vue.config.js 中加入对问题公共库 module-name/library-name 的 Babel 编译流程:

    transpileDependencies: [
      'module-name/library-name' // 出现问题的那个库
    ],

    vue-cli 对 transpileDependencies 也有如下说明:

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

    究其原因,module-name/library-name 这个库对外输出的是 CommonJS 类型源码,我们对该库进行编译后,项目基础设施中会通过 babel-transform-runtime 在编译时增加 helper 代码,而这些 helper 使用的是 import 引入。最终编译结果出现了 ESM 包含 CommonJS 的情况,是不会被 Webpack 处理的

    我再次分析下出现的新的问题:

    为了适配上述问题,Babel 设置了 sourceType 属性,sourceType:unambiguous 表示 Babel 会根据文件上下文(比如是否含有 import/export)来决定是否按照 ESM 语法处理文件。

    这时候就需要配置 Babel 内容了:

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

    但是这种做法在工程上并不推荐,上述更改方式对所有编译文件都生效,但也增加了编译成本(因为设置 sourceType:unambiguous 后,编译时需要做的事情更多),还有个潜在问题:

    翻译过来,就是说并不是所有的 ESM 模块(这里指使用 ESNext 特性的文件)都含有 import/export,因此即便某个待编译文件属于 ESM 模块,也可能被 Babel 错误地判断为 CommonJS 模块而引发误判。

    **基于这一点,一个更合适的做法是:**只对目标第三方库 'module-name/library-name' 使用 sourceType:unambiguous,这时 Babel overrides 属性就派上用场了:

    具体使用方式:

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

    至此,这个“iOS 11 系统白屏”问题就算告一段落了(你有没有被各种配置和设计搞得云里雾里?)。

    我整理了解决路线,如下图所示:

    知乎高赞:什么是前端工程化-LMLPHP知乎高赞:什么是前端工程化-LMLPHP

    我们回过头再来看这个问题,问题其实出现在一个公共库上,因而前端生态的混乱和复杂也许是更本质的原因,但这都转嫁为前端工程化的难点。

    我们进一步思考:

    被动地发现问题、解决问题只会让我们被「牵着鼻子走」——这不是我们的目的。感兴趣的读者可以点赞,关注,我会很快输出更多关于「前端工程化」的内容。


    最后的话

    对于很多前端工程师来说,你可能配置过 Babel/Webpack,也可能看过一些关于 Babel/Webpack 插件或原理的文章。但我认为,通过阅读几篇 Babel/Webpack 插件编写甚至 AST 分析的文章并不能让我们真正掌握前端工程化。这也完全完全不是前端工程化的要义。

    「配置工程师」只是我们的起点。作为前端开发者,你可能会被繁琐的配置和工具所困扰,自己的终端脆弱无比,出现各种报错。此时,你可能花费了一天的时间,通过 Google 找到了最终的配置解法;或者通过:

    rm -rf node_modules + npm install + npm run dev 

    规避了问题。但是解决之道却没搞清楚,得过且过,今后依然被类似的困境袭扰。

    当我们对配置、工具、构建流程、架构设计、生产发布等环节的各种挑战和问题能有系统化的思考时,「前端工程化」自然也不会再是一个困惑。

    其实很抱歉我无法回答题主这个宏大的问题,我自己也受此困扰,仅以两个小的细节方面抛砖引玉(闲时我也会持续输出更多关于「前端工程化」的内容)。

    总之,前端既收获着快速发展,也迎接着批量劣汰;前端技术有着与生俱来的混乱,也有着与之抗衡的规范 —— 这都对前端工程化提出了更高的挑战。

    
    
                    
                   
                  
                 
                
               
              
             
            
    
    
           
    
    
          
    
    
         
    
    
        
    
    
       
    
    
    
    
    
       

    本文分享自微信公众号 - 前端开发社区(pt1173179243)。
    如有侵权,请联系 support@oschina.cn 删除。
    本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一起分享。

    04-12 00:39