对前端工程化的理解

对前端工程化的理解

ps:以理解原理为主

1、以前为什么没有前端工程化的问题

在javascript被创造后很长一段时间,js或者说前端东西(js、css、html文件,各种图片资源等),都是依附于后端页面(例如:jsp页面、.net页面),后端页面有一个特点就是,没有前端路由,这意味着一个url对于一个具体的后端页面,这就是以前所谓的多页面应用。
多页面的优点在于一个页面的静态资源都是比较少量的(相对于单页面应用),纯手工就可以实现较方便的控制,这时对多页面应用做工程化控制是不实用的,也没有这方面的需求!

2、前端工程化解决了什么问题

随着网络带宽和个性化审美需求,对前端需要实现的东西和性能要求大大提高,于是出现了前后端分离,继而出现了单页面应用(单页面应用和多页面应用优缺点可以自行谷歌)。
当单页面出现后,前端作为应用单独的组成部分,一个页面需要承载原来几十个或几百个页面的内容,这就要求前端模块化和组件化,这就造成多名前端的开发者在成千上万个相互依赖的文件中开发相关项目代码,为了解决这些问题(模块化、组件化、规范化、自动化),前端工程化孕育而生。

3、什么是前端工程化

我的理解是:前端工程化就是通过一系列的工具、方法、工程化的思维,将分散到多名前端开发者手中,成千上万个模块、组件或其他静态资源进行有序、规范、标准化、可控、可追踪的组织起来,作为一个整体运行,以便提高前端工程的性能、稳定性、可用性、可维护性等。

4、webpack核心流程

1、初始化阶段:
1、初始化参数:从配置文件、 配置对象、Shell 参数中读取,与默认配置结合得出最终的参数
2、创建编译器对象:用上一步得到的参数创建 Compiler 对象
3、初始化编译环境:包括注入内置插件、注册各种模块工厂、初始化 RuleSet 集合、加载配置的插件等
4、开始编译:执行 compiler 对象的 run 方法
5、确定入口:根据配置中的 entry 找出所有的入口文件,调用 compilition.addEntry 将入口文件转换为 dependence 对象
2、构建阶段:
1、编译模块(make):根据 entry 对应的 dependence 创建 module 对象,调用 loader 将模块转译为标准 JS 内容,调用 JS 解释器将内容转换为 AST 对象,从中找出该模块依赖的模块,再 递归 本步骤直到所有入口依赖的文件都经过了本步骤的处理
2、完成模块编译:上一步递归处理所有能触达到的模块后,得到了每个模块被翻译后的内容以及它们之间的
3、依赖关系图生成阶段:
1、输出资源(seal):根据入口和模块之间的依赖关系,组装成一个个包含多个模块的 Chunk,再把每个 Chunk 转换成一个单独的文件加入到输出列表,这步是可以修改输出内容的最后机会
2、写入文件系统(emitAssets):在确定好输出内容后,根据配置确定输出的路径和文件名,把文件内容写入到文件系统

5、webpack单个文件依赖追踪

任何时候,一个文件依赖于另一个文件,webpack 就把此视为文件之间有 依赖关系。这使得 webpack 可以接收非代码资源(non-code asset)(例如 images 或 web fonts),并且可以把它们作为 依赖 提供给你的应用程序。
webpack 从命令行或配置文件中定义的一个模块列表开始,处理你的应用程序。 从这些 入口起点 开始,webpack 递归地构建一个依赖图,这个依赖图包含着应用程序所需的每个模块,然后将所有这些模块打包为少量的 bundle,

6、代码分离

单应用上,可能存在成百上千的模块,若是打包在一起,会大幅增加应用响应的时间,因为很多模块不用在登录页或主页加载,会照成相当的浪费。
故又产生了代码分离和按需加载的概率。
所谓代码分离是指将子模块的代码从主模块(app)中分离出来,把代码分离到不同的 bundle 中,然后可以按需加载或并行加载这些文件。代码分离可以用于获取更小的 bundle,以及控制资源加载优先级,如果使用合理,会极大影响加载时间。

分离方法:

  1. 入口起点:使用 entry 配置手动地分离代码。
  2. 防止重复:使用 Entry dependencies 或者 SplitChunksPlugin 去重和分离 chunk。
  3. 动态导入:通过模块的内联函数调用来分离代码。
在单应用中通常采用动态导入实现代码分离,通过配置路由和相关组件路径实现路由和文件的关联,将相关组件文件打包成一个bundle文件,相当于每一个路由配置项(符合动态导入)都是一个新的入口chunk。
代码分离通常使用SplitChunksPlugin实现,该插件可以将公共的依赖模块提取到已有的入口 chunk(ps: 可以抽出部分公用方法,形成一个新的chunk) 中,或者提取到一个新生成的 chunk。

例如:

{ 
  path: "/toReview", 
  name: "toReview", 
  component: function () { 
    return Promise.all( // 请求改路由所需要的模块代码
      // 抽出的公用代码js1
      [t.e("AllSearch~AlreadyCheck~AlreadyReview~ApproveDelayAll~ApproveDelayMy~ConsensusCheck~ConsensusChecking~ae8b8a1e"),
      // 抽出的公用代码js2 
      t.e("AlreadyCheck~AlreadyReview~ApproveDelayAll~ApproveDelayMy~ConsensusCheck~ConsensusChecking~Consensus~e4428f7b"), 
      // 抽出的公用代码css1
      t.e("ApproveDelayAll~ApproveDelayMy~ConsensusCheck~ConsensusChecking~ConsensusDelayAll~ConsensusDelayMy~D~3a6f683b"), 
      // 抽出的公用代码css2
      t.e("ApproveDelayAll~ApproveDelayMy~ConsensusCheck~ConsensusChecking~ConsensusDelayAll~ConsensusDelayMy~D~4ef97ccb"), 
      // 该路由对应的配置组件
      t.e("ToReview")])
      // 绑定相关模块,87714to为toReview的构建的id(猜测绑定后自动执行)
      .then(t.bind(null, "87714")
    ) 
  }, 
  meta: { 
    title: "待审阅", 
    key: "toReview" 
  } 
}, 
{ 
  path: "/Reexamination", 
  name: "Reexamin。ation", 
  component: function () { 
    return Promise.all([
      t.e("AllSearch~AlreadyCheck~AlreadyReview~ApproveDelayAll~ApproveDelayMy~ConsensusCheck~ConsensusChecking~ae8b8a1e"), 
      t.e("AlreadyCheck~AlreadyReview~ApproveDelayAll~ApproveDelayMy~ConsensusCheck~ConsensusChecking~Consensus~e4428f7b"), 
      t.e("ApproveDelayAll~ApproveDelayMy~ConsensusCheck~ConsensusChecking~ConsensusDelayAll~ConsensusDelayMy~D~3a6f683b"), 
      t.e("ApproveDelayAll~ApproveDelayMy~ConsensusCheck~ConsensusChecking~ConsensusDelayAll~ConsensusDelayMy~D~4ef97ccb"), 
      t.e("Reexamination")
    ]).then(t.bind(null, "58a9")) 
  }, 
  meta: { 
    title: "复审复核", 
    key: "Reexamination" 
  } 
},

在vue文件打包后的文件https://blog.csdn.net/cjvalue/article/details/113185945可以看出AllSearch~Phone模块中,依赖的相关模块也被打包到该文件里面,而相关图片依赖则要根据相关配置,小图片被解析成base64格式存放在js中,作为一个小模块存在(不是单独的chunk,称为变量更合适)通过a(“978d”)调用,大图片择保存原格式,存放于dist/img的下,通过相对路径调用。

7、按需加载或懒加载(动态导入)

webpack 提供了两个类似的技术。第一种,也是推荐选择的方式是,使用符合 ECMAScript 提案 的 import() 语法 来实现动态导入。第二种,则是 webpack 的遗留功能,使用 webpack 特定的 require.ensure。推荐使用import()。
既将每一个路由配置项(符合动态导入)都是一个新的入口chunk,生成对应的bundle,当进入该路由时,使用Promise.all遍历获取相关的模块代码,并绑定相关模块自动执行。

8、优化打包速度

优化打包方案可以参考https://blog.csdn.net/cjvalue/article/details/107861965

方式有
1、缓存
  • DllPlugin 配置比较繁琐,需要单独配置,生成相应的json,配合DllReferencePlugin过滤使用,(原理:提取第三方公用模块代码,提前打包生成相关代码文件vendor.dll.js,生成相关json文件;在后续构建的时候,直接使用打包好的vendor.dll.js,用DllReferencePlugin过滤掉已经打包的第三方模块,可以加快后续的构建)
  • hard-source-webpack-plugin该插件较为简单粗暴,将第三方依赖打包缓存到硬盘上,后续直接使用(ps:也会缓存相关的环境变量,慎用)
2. 多进程多实例构建,资源并行解析
  • thread-loader (推荐使用这个)
  • HappyPack
  • parallel-webpack

你可能感兴趣的:(webapck,vue,webpack,前端,vue.js)