Skip to content

框架相关

zhaoluting edited this page Dec 22, 2021 · 7 revisions

模块化开发

什么是模块化开发

  1. 模块就是一个有特定功能的文件,我们可以通过加载这些模块得到特定的功能。
  2. 模块化开发就是js的功能分离,通过需求引入不同的文件。
  3. 模块化开发可以使代码耦合度降低,避免代码多次在页面出现,他最大的作用就是重用。

AMD和CMD

  1. AMD规范也叫异步模块加载规范,在这个规范下模块会异步加载,不影响后面语句的执行。
  2. CMD规范通用模块定义。CMD是按需加载,一个模块就是一个文件。

CommonJS 和 ES6 Module
AMD和CMD主要是用在浏览器端,但是在现在前端借助webpack来实现大前端的情况下,我们在实际开发中还是Commonjs和ES6 Module用的比较多,CommonJS主要是应用在服务端。ES Module旨在统一JavaScript中的所有模块。

  • CommonJS标准规定,一个单独的文件就是一个模块,模块内将需要对外暴露的变量放到exports对象里,可以是任意对象、函数、数组等,未放到exports对象里的都是私有的。用require方法加载模块,即读取模块文件获得exports对象。
    • Nodejs的模块系统就采用CommonJS模式。
    • CommonJS规范加载模块是同步的,也就是说,只有加载完成,才能执行后面的操作。
  • ES6 模块的设计思想是尽量的静态化,使得编译时就能确定模块的依赖关系,以及输入和输出的变量。CommonJS 和 AMD 模块,都只能在运行时确定这些东西。
    • ES6 模块不是对象,而是通过export命令显式指定输出的代码,再通过import命令输入。
    • 不再需要UMD模块格式了,将来服务器和浏览器都会支持 ES6 模块格式。目前,通过各种工具库,其实已经做到了这一点。

总结

  • 在 ES6 之前,浏览器端一般采用AMD,Node.js采用CommonJS。- ES6 在语言标准的层面上,实现了模块功能,而且实现得相当简单,完全可以取代CommonJS和AMD规范,成为浏览器和服务器通用的模块解决方案。
  • AMD和CMD迟早会是过去,CommonJS注重服务端,是同步的。ES6 Module是官方标准,推荐使用。UMD是为了兼容客户端和服务端提出的一种折中方案。

服务器端渲染(SSR)

客户端渲染路线

  1. 客户端发起请求,在接收到HTML后下载js/css文件,完成初始化
  2. 客户端接收到HTML后下载js/css文件,完成初始化
  3. 等待js加载并完成初始化
  4. 客户端向服务端请求页面所需数据
  5. 服务端返回页面数据
  6. 客户端完成局部刷新,生成完整页面

服务端渲染路线

  1. 客户端发起请求
  2. 服务端请求页面数据( 内网请求快 )并完成初始渲染(服务端性能好)
  3. 服务端向客户端返回已经有正确内容的HTML文件
  4. 客户端下载js/css文件,并完成初始化,把剩下一部分渲染完成( 内容小,渲染快 ),生成完整页面
  5. 客户端把剩下一部分渲染完成( 内容小,渲染快 )

服务器端渲染优势

  • 更好的 SEO,由于搜索引擎爬虫抓取工具可以直接查看完全渲染的页面。
  • 更快的内容到达时间(time-to-content),特别是对于缓慢的网络情况或运行缓慢的设备。无需等待所有的 JavaScript 都完成下载并执行,才显示服务器渲染的标记,通常可以产生更好的用户体验。

服务器端渲染缺点:

  • 更多的开发条件限制:例如vue服务端渲染只支持 beforCreate 和 created 两个钩子函数,这会导致一些外部扩展库需要特殊处理,才能在服务端渲染应用程序中运行;并且与可以部署在任何静态文件服务器上的完全静态单页面应用程序 SPA 不同,服务端渲染应用程序,需要处于 Node.js server 运行环境;
  • 更多的服务器负载:在 Node.js 中渲染完整的应用程序,显然会比仅仅提供静态文件的 server 更加大量占用CPU 资源 (CPU-intensive - CPU 密集),因此如果你预料在高流量环境 ( high traffic ) 下使用,请准备相应的服务器负载,并明智地采用缓存策略。

同构

  • 同构就是让一份代码,既可以在服务端运行,也可以在客户端运行,并且执行的效果都是一样的,都是完成这个html的组装,正确的显示页面。
  • 同构条件:需要实现客户端与服务端的路由、模型组件、数据模型的共享。

各端平台开发模式

  • Native App:传统的原生App开发模式,有iOS和Android两大系统,需要各自语言开发各自App。性能和体验都是最好的,但开发和发布成本高。
  • Web App:运行在移动端浏览器上的网站应用,即H5应用,泛指 SPA(Single Page Application)模式开发出的网站,与MPA(Multi-page Application)对应。开发和发布成本最低,但性能和体验会受到浏览器处理能力的限制。
  • Hybrid App:由Native App结合Web App所形成的模式,称之为混合APP。由Native通过JSBridge等方法提供统一的API,JS调用API,最终页面在Webview中显示。Hybrid兼具Native App良好交互体验的优势和Web App跨平台开发的优势,开发和发布效率介于Native App、Web App之间,但严重受限于WebView的解析渲染效率,且必须要原生配合。
  • PWA:全称为Progressive Web App,是谷歌公司在2015年提出的渐进式网页开发技术。PWA使用Service Worker(离线缓存)、Manifest等多种技术来增强Web App的功能,最终可以让网页应用获得媲美原生应用的体验。但由于苹果对PWA的支持力度不够等等原因一直不温不火。
  • React Native:Facebook发现Hybrid App存在很多缺陷,于是开源了一套新App开发方案。使用JSX语言编写,通过JSBridge调用原生API渲染UI交互通信。效率体验接近Native App,发布和开发成本低于Native App,支持Android、IOS两端。
  • Weex:阿里研习RN重新设计出的一套开发模式,也是原生渲染,并在v2.0版本官方支持Vue.js,与RN分庭抗礼。单页开发模式效率极高,热更新发包体积小,并且跨平台性更强,支持Android、IOS、Web三端,但社区没有RN活跃,功能没有RN健全。
  • Rax:由于阿里团队普遍使用react,故研发出了react版的Weex。Rax 是一套基于 React 写法的 Weex 上层 DSL。Rax 基于 React 的标准,支持在 Web、Weex、Node、小程序等容器中渲染。
  • Flutter:谷歌2018年发布的跨平台移动UI框架,编程语言是Drat,使用Flutter Engine引擎,原生渲染。Flutter可以直接与平台通信,不需要JS引擎的桥接,原生编码,唯一要求系统提供的是canvas以实现UI的绘制,跨平台性超强。

微信小程序

相关文件类型

  • WXML(WeiXin Markup Language)是框架设计的一套标签语言,结合基础组件、事件系统,可以构建出页面的结构。内部主要是微信自己定义的一套组件
  • WXSS (WeiXin Style Sheets)是一套样式语言,用于描述 WXML 的组件样式,尺寸单位使用rpx (响应式像素)。
  • js 逻辑处理,网络请求
  • json 小程序设置,如页面注册,页面标题及tabBar

优点

  1. 基于微信(支付宝、手机百度),媲美原生
  2. 类web开发体验,跨平台运⾏,减少开发⼯作量
  3. 拥有离线能力,动态更新发版周期简单
  4. 极强的传播能力,短时间类能够获得大量的用户

原理

传统的HTML5的运行环境是浏览器,而微信小程序的运行环境并非完整的浏览器,是微信开发团队基于浏览器内核完全重构的一个内置解析器,针对小程序专门做了优化,配合自己定义的开发语言标准,提升了小程序的性能。

网页开发渲染线程和脚本线程是互斥的,而在小程序中,逻辑层和渲染层是分开的,逻辑层运行在 JSCore 中,并没有一个完整浏览器对象,因而缺少相关的DOM API和BOM API。这一区别导致了前端开发非常熟悉的一些库,例如 jQuery、 Zepto 等,在小程序中是无法运行的。同时 JSCore 的环境同 NodeJS 环境也是不尽相同,所以一些 NPM 的包在小程序中也是无法运行的。

  • 本质就是一个单页面应用,所有的页面渲染和事件处理,都在一个页面内进行,但又可以通过微信客户端调用原生的各种接口。
  • 是数据驱动的架构模式,它的 UI 和数据是分离的,所有的页面更新,都需要通过对数据的更改来实现。
  • 小程序分为两个部分 webviewappService 。其中 webview 主要用来展现 UI appService 有来处理业务逻辑、数据及接口调用。它们在两个进程中运行,通过系统层 JSBridge 实现通信,实现 UI 的渲染、事件的处理。

传递数据的方法

  • app.js 文件中定义全局变量 globalData,可以直接使用 getApp() 拿到存储的信息。
  • 使用 wx.navigateTowx.redirectTo 时,可以将部分数据放在 url 里面,并在新页面 onLoad 的时候初始化。
  • 使用本地缓存 Storage 相关。
  • 使用vuex等状态管理框架

生命周期

应用的生命周期:

  • 用户首次打开小程序,触发onLaunch(全局只触发一次)。
  • 小程序初始化完成后,触发onShow方法,监听小程序显示。
  • 小程序从前台进入后台,触发onHide方法。
  • 小程序从后台进入前台显示,触发onShow方法。
  • 小程序后台运行一定时间,或系统资源占用过高,会被销毁。

页面生命周期:

  • 小程序注册完成后,加载页面,触发onLoad方法。
  • 页面载入后触发onShow方法,显示页面。
  • 首次显示页面,会触发onReady方法,渲染页面元素和样式,一个页面只会调用一次。
  • 当小程序切入后台运行或跳转到其他页面时,触发onHide方法。
  • 当小程序由后台进入到前台运行或重新进入页面时,触发onShow方法。
  • 当使用重定向方法wx.redirectTo()或关闭当前页返回上一页wx.navigateBack(),触发onUnload。
  • 同时,应用生命周期会影响到页面生命周期。

账户体系

如果开发者拥有多个移动应用、网站应用、和公众帐号(包括小程序),可通过 unionid 来区分用户的唯一性,因为只要是同一个微信开放平台帐号下的移动应用、网站应用和公众帐号(包括小程序),用户的 unionid 是唯一的。换句话说,同一用户,对同一个微信开放平台下的不同应用,unionid 是相同的。

页面跳转

  • wx.navigateTo:保留当前页面,跳转到应用内的某个页面。但是不能跳到 tabBar 页面
  • wx.redirectTo:关闭当前页面,跳转到应用内的某个页面。但是不允许跳转到 tabBar 页面
  • wx.switchTab:跳转到 tabBar 页面,并关闭其他所有非 tabBar 页面
  • wx.navigateBack:关闭当前页面,返回上一页面或多级页面。可通过 getCurrentPages() 获取当前的页面栈,决定需要返回几层
  • wx.reLaunch:关闭所有页面,打开到应用内的某个页面

Node

Koa Vs Express Vs Egg

  • Express: 基于Connect中间件,功能丰富,随取随用,自身封装了大量便利的功能,如路由、视图处理等,兼容性强,历史悠久,社区大,文档丰富,用户群大,支持 jade 等前端模板语言。
  • Koa: 由 Express 原班人马打造基于co中间件、ES6新特性的开发框架,不绑定任何中间件,大部分功能需要用户自行引入中间件,更小更健壮更自由,但还未完全成熟,有默认的错误处理方式,对 request 和 response 进行了封装。
  • Egg:阿里基于 koa 研发的企业级框架,入门简单,文档通俗易懂,天然支持多进程,不再需要用pm2。

运行机制

nodejs的运行机制跟浏览器运行机制有什么区别?

因为nodejs服务目的和环境不同,导致API与原生js有区别,event loop还要处理一些I/O,所以与浏览器的event loop是不同的,nodejs的event loop是分阶段的:

  • timer:执行 setTimeout 和 setInterval 回调
  • pending callbacks:执行延迟到下一个循环迭代的I/O回调
  • idle、prepare:仅系统内部使用
  • poll:检索新的I/O事件;执行与I/O相关的回调
  • check: 执行setImmediate 回调
  • close callbacks:一些关闭的回调

注意点:

  1. poll队列执行完,如果没有 setImmediate ,但是定时器到期,则会绕回去执行定时器阶段
  2. setTimeout(fn, 0) 在node.js中会被强制改为 setTimeout(fn, 1)
  3. 遇到微任务会先立即执行, process.nextTick 会立即执行,他的优先级会高于 promise.then

Serverless

Serverless 架构由两部分组成:

  • FaaS(Function-as-a-Service)即为函数运行平台,用户无需搭建庞大的服务系统,只需要上传自己的逻辑函数如一些定时任务、数据处理任务等到云函数平台,配置执行条件触发器、路由等等,完成基础函数的注册。
  • BaaS(Backend-as-a-Service)包含了后端服务组件,它是基于 API 的第三方服务,用于实现应用程序中的核心功能,包含常用的数据库、对象存储、消息队列、日志服务等等。

优点:

  • 低运维成本、安全可靠、弹性扩容、按需付费:用户在使用对应的服务时,不需要关心或较少关心服务器的硬件资源、软件资源、稳定性等等,这些通常已经由云计算厂商提供设施、服务和 SLA 保障,完全托管给云计算厂商,用户只需要专注自己应用代码本身,上传执行函数到相应云计算平台,按照函数运行的时长按量付费即可。
  • 事件驱动:当一个任务被触发时,比如 HTTP 请求,API Gateway 接受请求、解析和认证,传递对应参数给云函数平台,平台中执行对应回调函数,配合 DB、MQ 等 BaaS 服务在特定容器中完成计算,最终将结果返回给用户。函数执行完成后,一般会被 FaaS 平台销毁,释放对应容器,等待下一个函数运行。

缺点:

  • 云厂商强绑定:使用公有云的 Serverless 产品时,常常会和厂商的其他云产品相绑定,如对象存储、消息等等,意味着需要同时开通其他服务,将导致迁移成本剧增。
  • 不适合长时间任务:云函数平台会限制函数执行时间,如阿里云 Function Compute 最大执行时长为 10 min,如果你的任务时间超长,那么你需要拆分编排你的函数执行流程,并在一个函数执行结束时唤起另一个函数执行。这将增加编码的复杂度,而且花费上可能高于购买一个长时间运行的实例。
  • 冷启动时间:函数运行时,执行容器和环境需要一个准备的时间,尤其是第一次启动时时间可能会较长。对一个 HTTP 请求来讲,可能会带来响应时延的增加,产生性能毛刺。
  • 调试与测试 由于本地环境和平台运行环境的差异性,开发者需要不断调整代码,打印日志,并提交到函数平台运行测试,会带来一些开发成本和产生一些费用。

应用场景:

  • 定时任务:通过时间触发对应的函数任务,完成开发者业务逻辑的处理。
  • 数据加工:通过事驱动件机制,在特定的条件下触发,对系统的日志进行整合,或者对多媒体文件进行加工等等。
  • 低频请求:可以按照频次付费,无需构建一个应用来应对这些必要的但是量小的请求。
  • IoT:物联网场景下,大部分是用户对设备的操控,用户对时延的容忍度较高,也是典型的事件触发且低频场景。
  • 认知计算:适用于某些 AI 场景,如聊天机器人。

TypeScript

  • 参考:官网文档bootcssTypeScript 入门教程
  • TypeScript具有类型系统,且是JavaScript的超集。 它可以编译成普通的JavaScript代码。 TypeScript支持任意浏览器,任意环境,任意系统并且是开源的。
  • 基础类型
    • 简单的数据单元:例如let count: number = 10
    • Null 和 Undefined:用处不是很大,let n: null = null;
    • 数组元素let list: number[] = [1, 2, 3];let list: Array<number> = [1, 2, 3];
    • 元组Tuple:允许表示一个已知元素数量和类型的数组,let x: [string, number];x = ['hello', 10];
    • 枚举类型:可以定义一些带名字的常量,为一组数值赋予友好的名字,enum Color {Red, Green, Blue}; let c: Color = Color.Green;
    • 任意值Any:为那些在编程阶段还不清楚类型的变量指定一个类型,但它会无法使用ts提供的大量保护机制,let list: any[] = [1, true, "free"];
    • unknown:ts3.0引入的新类型,是any类型对应的安全类型。虽然和any一样存储了任意类型的值,但是具体使用的时候需要显式确定,由使用者进行指定将unknown转换成某一确定类型。
    • 空值void:表示没有任何类型。当一个函数没有返回值时,function warnUser(): void { alert("This is my warning message");};声明一个void类型的变量时,只能为它赋予undefined和null,let unusable: void = undefined;
    • never类型:表示永不存在的值的类型。例如,总是会抛出异常或根本就不会有返回值的函数表达式或箭头函数表达式的返回值类型。
  • 泛型:在定义函数、接口或类的时候,不预先指定具体的类型,而在使用的时候再指定类型的一种特性。
    • 是一种创建可复用代码组件的工具。这种组件不止能被一种类型使用,而是能被多种类型复用。
    • 支持创建泛型的方式:泛型接口、泛型类、泛型数组、泛型工具类型
    • 泛型变量:T(type,表示一个ts类型)、K(key,表示对象中的键类型)、V(value,表示对象中的值类型)、E(element,表示元素类型)
    • function identity<T>(arg: T): T { return arg; }
    • 泛型约束:使用一个类型和extends对泛型进行约束。
  • 类(class):当在声明class Point时,除了会创建一个名为Point的类之外,同时也创建了一个名为Point的类型(实例的类型)。
  • 接口(interface):ts核心原则之一是对值所具有的结构进行类型检查,ts接口的作用就是为这些类型命名和为代码或第三方代码定义契约。
  • 函数重载:允许一个函数接受不同数量或类型的参数时,作出不同的处理。重复定义多次函数,前几次都是函数定义,最后一次是函数实现,ts会优先从最前面的函数定义开始匹配。

Webpack

再来一打Webpack面试题

基础

  • entry:入口起点(entry point) 指示 webpack 应该使用哪个模块,来作为构建其内部依赖图的开始。进入入口起点后,webpack 会找出有哪些模块和库是入口起点(直接和间接)依赖的,每个依赖项随即被处理,最后输出到称之为 bundles 的文件中。默认值是./src/index.js
  • output:output属性告诉 webpack 在哪里输出它所创建的 bundles,以及如何命名这些文件,默认值为./dist。基本上,整个应用程序结构,都会被编译到你指定的输出路径的文件夹中。你可以通过在配置中指定一个output字段,来配置这些处理过程。以下是重要option:
    • filename:对应于 entry 里面的输入文件,经过webpack 打包后输出文件的文件名
    • chunkFilename:未被列在 entry 中,却又需要被打包出来的 chunk 文件的名称。一般来说指的是按需加载单独抽离、要懒加载的文件。
    • path:目标输出目录 path 的绝对路径
    • publicPath(公共路径):为项目中的所有资源指定一个基础路径。静态资源最终访问路径 = output.publicPath + 资源loader或插件等配置路径。
    • library:使用require时的模块名。
    • libraryTarget:配置如何暴露library的方式。通常library不是开发库在项目中不需要配置。
  • mode:production、development、none
  • devtool:常用的配置是source-map(大而全)、cheap-module-eval-source-map(dev推荐使用)、cheap-module-source-map(一般生产环境不配source-map,但可以用这个捕捉线上报错)
  • babel:是一个工具链,主要用于将 ECMAScript 2015+ 版本的代码转换为向后兼容的 JavaScript 语法,以便能够运行在当前和旧版本的浏览器或其他环境中。
  • loaders:一种处理多种文件格式的机制, 可以把 loader 理解为是一个转换器,负责把某种文件格式的内容转换成 webpack 可以支持打包的模块。
  • plugins:loader 用于转换某些类型的模块,而插件则可以用于执行范围更广的任务。包括:打包优化,资源管理,注入环境变量。
    • HotModuleReplacementPlugin:模块热更新插件,依赖于webpack-dev-server(在打包文件改变时更新打包文件或者 reload 刷新整个页面),HRM是只更新修改的部分。
    • HtmlWebpackPlugin:生成 html 文件。将 webpack 中entry配置的相关入口 chunk 和 extract-text-webpack-plugin抽取的 css 样式 插入到该插件提供的template或者templateContent配置项指定的内容基础上生成一个 html 文件,具体插入方式是将样式link插入到head元素中,script插入到head或者body中。
    • clean-webpack-plugin:在打包前清理上一次项目生成的 bundle 文件,会根据output.path自动清理文件夹;这个插件在生产环境用的频率非常高,因为生产环境经常会通过 hash 生成很多 bundle 文件,如果不进行清理的话会导致文件夹非常庞大。
    • mini-css-extract-plugin:将 CSS 提取为独立的文件的插件,对每个包含 css 的 js 文件都会创建一个 CSS 文件,支持按需加载 css 和 sourceMap。这个插件应该只用在生产环境配置,并且在 loaders 链中不使用 style-loader, 而且这个插件暂时不支持 HMR。
    • optimize-css-assets-webpack-plugin:减小 css 打包后的体积(进行压缩)。
  • loader和plugins的区别:loader是一个转换器,将A文件进行编译成B文件,单纯的文件转换过程。plugin是一个扩展器,它丰富了webpack本身,针对是loader结束后,webpack打包的整个过程,它并不直接操作文件,而是基于事件机制工作,会监听webpack打包过程中的某些节点,执行广泛的任务。
  • 模块
    • 简单来说,module,chunk 和 bundle 其实就是同一份逻辑代码在不同转换场景下的取了三个名字,我们直接写出来的是module,webpack根据文件引用关系生成的是chunk,最后生成浏览器可以直接运行的是bundle。
  • 文件指纹策略:打包后输出的文件名后缀,用于做版本管理。
    • hash:和整个项目的构建相关,只要项目文件有修改,整个项目构建的hash值就会更改。
    • chunkhash:和webpack打包的chunk有关,不同的entry会生成不同的chunkhash值。
    • contenthash:根据文件内容来定义hash,文件内容不变,contenthash不变。
  • webpack原理
    • 识别入口文件
    • 通过逐层识别模块依赖(Commonjs、amd或者es6的import,webpack都会对其进行分析,来获取代码的依赖)
    • webpack做的就是分析代码,转换代码,编译代码,输出代码
    • 最终形成打包后的代码
  • 提取公共代码实现方式:optimization.splitChunks(v.4新特性)、CommonsChunkPlugin插件(适用于v.4版本以下,基于父子关系,只能统一抽取到父chunk,造成父chunk过大,不可避免的存在重复引入)
  • 模块异步加载的两种方式:webpack的require.ensure()、es6的import(),
    • webpack中异步加载会被替换成__webpack_require__.e()函数,在该函数中,主要通过jsonp的方式使用script去加载异步chunk文件,同时返回promise
    • 异步加载的代码,会被保存在一个全局的jsonpFunction中
    • jsonpFuntion数组由chunk名和该chunk的module对象组成
  • webpack5.x新特性
    • 通过持久缓存提高构建性能.
    • 使用更好的算法和默认值来改善长期缓存.
    • 通过更好的树摇和代码生成来改善捆绑包大小.
    • 清除处于怪异状态的内部结构,同时在 v4 中实现功能而不引入任何重大更改.
    • 通过引入重大更改来为将来的功能做准备,以使我们能够尽可能长时间地使用 v5.

性能优化

  • 性能分析
    • 体积分析:
      • 通过官方提供的stats.json文件分析打包结果。
      • 第三方工具webpack-bundle-analyzer,打包分析神器
    • 速度分析:speed-measure-webpack-plugin插件帮助分析整个打包的总耗时,以及每一个loader 和每一个 plugins 构建所耗费的时间。
  • 优化策略
    • 使用新版本:这是webpack性能优化的万能膏药,升级版本性能提升很明显。如webpack4.0带来的优化有:v8引擎优化、默认使用更快的md4 hash算法、webpack AST可以直接从loader传递给 AST、使用字符串方法替代正则表达式等。
    • 体积优化
      • js压缩:使用4.0默认使用的terser-webpack-plugin(或4.0之前默认使用的uglifyjs-webpack-plugin)插件;
      • 压缩css:使用optimize-css-assets-webpack-plugin 插件压缩,purgecss-webpack-plugin来擦除无用css。
      • 图片压缩:可以通过线上图片压缩工具自己手工压缩,或者通过image-webpack-loader自动压缩。
      • 拆分代码:使用tree-shaking移除JS上下文中的未引用代码,或者使用 splitChunksPlugin 把一个大的文件分割成几个小的文件,这样也可以有效提升webpack的打包速度。
      • 使用动态polyfill服务:会根据浏览器user-Agent的不同自动发放polyfill来兼容不同版本,不会重复打包。
      • 开启Scope Hoisting:将所有模块的代码按照引用顺序放在一个函数作用域里,然后适当的重命名一些变量以防止变量名冲突,可以让打包出来的代码文件更小、运行的更快。但还有很多问题,目前不太好用。
    • 速度优化
      • 分离:区分开发和生产环境两套配置,各司其职。
      • 减少查找过程:合理配置resolve参数告诉webpack怎么去搜索文件,合理使用resolve.extensions、优化resolve.modules、使用resolve.alias减少查找过程。
      • 缩小构建目标:排除不需要解析的模块,借助include和exclude这两个参数规定loader只在哪些模块应用。
      • 利用多线程提升构建速度:官方推出的一个多进程方案thread-loader,每次解析模块都会将它及它的依赖分配给worker线程,从而达到多进程打包的目的。
      • 预先编译资源模块(DllPlugin):在第一次编译打包后就生成一份不变的代码供其他模块引用,比如打包不会变化的第三方模块,这样下一次构建的时候就可以节省开发时编译打包的时间。但是现在已经不需要了,webpack 4性能更好。
      • 缓存Cache:开启相应loader或者plugin的缓存,来提升二次构建的速度,如babel-loader、terser-webpack-plugin。
      • 合理使用sourceMap:打包生成 sourceMap 的时候,信息越详细打包速度就会越慢。