Webpack知识整理,再也不怕面试问我了

663 阅读10分钟

随着Node.js的不断发展,前端项目的不断提升,我们的项目打包难免会出现庞大,打包慢的情况。趁着工作之外的时间,通读了关于webpack的数据,也在此总结一下相关的知识。

关注公众号《前端小时》回复webpack获取高清思维导图

目录

  • 入门
  • 配置
  • 优化
  • 原理

入门

前端发展

模块化

commonjs

commonjs的核心思想就是通过require的方式同步引入依赖包,它是一种JavaScript规范

commonjs也分为两个版本,一个是commonjs1,只能用exports.XX = XX的方式进行模块的导出;而commonjs2新添加了module.exports = XX的方式导出。

优点

  • 代码可复用
  • 能够在`node环境运行
  • 第三方模块都使用这种方式

缺点

  • 无法直接运行在浏览器环境
  • ES6代码需要转成ES5

AMD

AMD模块化方式也是一种JavaScript规范,它采用异步加载依赖,主要解决浏览器环境模块化问题。

优点

  • 不转换代码直接运行在浏览器
  • 异步加载
  • 并行加载多个依赖
  • 运行在浏览器和Node环境

缺点

  • JavaScript运行环境没有原生支持AMD
  • 需先引入AMD库才能使用

ES6模块化

它是异种骨ECMA的JavaScript模块化规范,支持浏览器和Node环境,但是缺点就是需要ES6转成ES5。

样式文件模块化(mixin)

还有一种是样式文件的模块化,现在CSS也可以像其他变成语言一样可以有编程的思想在里面。可以定义变量,公共样式的引入等。

新框架

  • Vue
  • React
  • Angular

新语言

ES6

  • 模块化
  • Class语法
  • let声明
  • 箭头函数
  • async函数
  • Set和Map结构

TypeScript

TypeScript是JavaScript的一种超集,提静态类型检查,避免我们在项目里面出现一些低级错误,一般大型项目都建议引入。但是很多不喜欢就是因为代码啰嗦,它也无法在浏览器/Node环境运行,需要转换。

Flow

  • JavaScript的超集
  • 提供静态类型检查

SCSS 一种css预处理器,常见的还有less,stylus等。

优点

  • 方便管理代码
  • 抽离公共部分
  • 逻辑灵活

缺点

  • 代码需要转化

常见构建工具


  • npm script
  • grunt
  • gulp
  • fis3
  • webpack
  • rollup

webpack的优势

  • 专注模块化项目
  • Plugin扩展
  • 场景不限于web开发
  • 社区庞大
  • 开发体验良好,但只能开发模块化项目

核心概念

  • Entry:第一步将从Entry开始,抽象输入
  • Module:一切皆模块,从Entry开始递归遍历所有依赖的模块
  • Chunk:代码块,一个Chunk由多个模块组成,用于代码分割与合并
  • Loader:模块转化器,将原内容转成新内容
  • Plugin:扩展插件,监听广播事件,回调

构建流程

  • 代码转换
  • 文件优化
  • 代码分割
  • 模块合并
  • 自动刷新
  • 代码校验
  • 自动发布

webpack实时预览原理

webpack向JavaScript代码注入一个代理客户端,代理客户端用于控制网页的刷新。网页和devServer之间使用的是websocket协议,当webpack监听到代码改变时就会让webpack通知客户端进行页面的刷新。

配置

Entry

  • context:寻找相对目录会以context为跟目录
  • Entry类型:string array object
  • chunk名称:类型是string/array ,是main、如是object ,是object中的键名
  • 动态配置Entry:设置成函数动态返回

Output

  • filename:配置输出文件的名称,多个chunk可设置成 filename : [name].js
  • chunkFilename:无入口的chunk在输出时的文件名,用于指定在运行过程中生成的chunk
  • path:输出文件存放的本地目录
  • publicPath:配置发布到线上的资源URL前缀
  • crossOriginLoading:配置代码块异步加载的方式(JSONP),anonymous- 不会带上cookie,use-credentials-带上cookie
  • libraryTarget:配置以何种方式导出库,var commonjs commonjs2 this window global
  • library:配置导出库的名称
  • libraryExport:配置导出的模块中哪些子模块需要导出

Module

  • noParse:忽略部分没采用模块化文件的递归解析
  • parser:更细粒度配置哪些模块语法被解析

Resolve

  • alias:通过别名映射新路径
  • mainFields:配置采用哪一份代码,数组['brower' , 'main']
  • extensions:配置不带后缀时寻找文件的方式,['.ts' , '.js' , '.json']
  • modules:配置去哪些目录下寻找第三方模块,默认:node_modules
  • descriptionFiles:配置第三方描述文件的名称,默认package.json,descriptionFiles: [ 'package.json' ]
  • ecforceExtension:配置导入语句是否强制带后缀
  • enforceModuleExtension:【node_modules】配置导入语句是否强制带后缀

DevServer

  • hot:在不刷新整个页面的情况下通过新模块来替换旧模块,实现实时预览
  • inline:配置是否将代理客户端注入到代码块里面来控制网页,默认true
  • historyApiFallback:配置命中路由时返回的html文件
  • contentBase:配置DevServer HTTP服务器的文件根目录,只能用来配置暴露本地文件的规则
  • headers:可以在HTTP响应时注入一些HTTP响应头
  • host:配置DevServer服务的监听地址
  • port:服务监听的端口
  • allowedHost:配置白名单列表,只有HTTP请求的host在列表才能正确返回
  • disableHostCheck:是否关闭用于DNS重新绑定的HTTP请求的HOST检查,DevServer默认只接受来自本地的请求
  • https:DevServer默认使用HTTP方式,是否切换成HTTPS服务
  • clientLogLevel:配置客户端日志等级
  • compress:是否开启Gzip压缩
  • open:是否默认打开浏览器

其他配置

  • Target:构建针对不同环境的代码
  • DevTool:配置如何生成Source Map
  • Watch/WatchOprions:Watch,文件监听;WatchOprions:配置哪些文件需要监听,变化后多久才会变化
  • Externals:webpack打包忽略的模块
  • ResolveLoader:告诉webpack如何寻找Loader

优化

缩小文件搜索范围使用DllPlugin使用HappyPack使用ParallelUglifyPlugin使用自动刷新开启模块热替换区分环境代码压缩CDN加速使用Tree Shaking提取公共代码分割代码以按需加载使用Prepack开启Scope Hoisting输出分析

缩小文件搜索范围

优化Loader配置

  • 尽可能少文件被Loader处理
  • 尽可能让Loader快速命中需处理的文件

优化resolve.modules配置

  • 指明第三方文件的存放位置,减少搜索步骤

优化resolve.mainFields配置

  • 指明当前环境所需的代码,减少搜索步骤

优化resolve.alias配置

  • 尽可能明确别名路径
  • 整体性较强使用此优化
  • loadash不适合使用,会输出很多无用代码

优化resolve.extensions配置

  • 后缀尝试列表尽可能少
  • 频率出现高的文件后缀放前面
  • 导入语句尽可能带上后缀

优化module.noParse配置

  • 忽略部分没有采用模块化的文件递归解析处理

使用DllPlugin

  • 将网页依赖的基础模块抽离出来,打包到一个个单独的动态链接库中
  • 导入模块存在于某个动态链接库,模块不会再次打包,从库里取
  • 页面依赖的所有动态链接库都需要被加载

使用HappyPack

定义

  • 将任务分解多个子进程并发执行
  • 子进程执行完将结果发给主进程

原理

  • 实例化一个HappyPack,告诉核心调度器如何通过Loader转化一类文件
  • 核心调度器会把任务分给子进程,结果发给主进程

使用ParallelUglifyPlugin

  • 多进程并行代码压缩

使用自动刷新

文件监听

原理
  • 定时获取文件的最后最后修改时间
  • 当前获取和最后一次的编辑时间不一致则文件发生变化
  • watchOptions中的poll属性可以设置每秒轮询多少次
  • watchOptions中的aggregateTimeout属性可以设置收集时间,然后告诉监听者
优化
  • 默认从Entry递归遍历,加到监听列表里面
  • 忽略第三方模块的文件监听
  • 加大收集时间,减少一秒轮询次数

自动刷新浏览器

方法
  • 借助浏览器提供的接口刷新
  • 向开发网页注入代理客户端
  • 网页装进一个iframe中,刷新iframe
优化
  • 关闭inline注入模式
  • 通过执行命令来完成注入一个代理客户端

开启模块热替换

原理

  • 源码发生变化后只编译发生变化的模块
  • 用新模块去替换旧模块

优势

  • 预览反应快,等待时间短
  • 不刷新浏览器,保留网页运行状态

优化

  • 使用内置的NamedModulesPlugin输出具体文件
  • 监听更少的文件和忽略node_modules下面的文件
  • 不可关闭inline注入,每个chunk都需要包含客户端代码

区分环境

  • 线上环境代码压缩
  • 测试环境专注开发
  • 通过process.env.NODE_ENV

代码压缩

压缩JavaScript

  • UglifyJsPlugin
  • ParallelUglifyPlugin 多进程处理压缩

压缩ES6

  • 需要单独安装uglify-webpack-plugin

压缩CSS

  • cssnano,css-loader内置

CDN加速

  • 针对html文件不开启缓存
  • 文件名使用hash值
  • 把不同类型的文件放到不同的CDN服务器上减少并发请求时间
  • 可能会增加域名解析时间,可开启DNS预解析

使用Tree Shaking

  • 剔除无用代码
  • 必须使用ES6语法
  • 第三方库可能不生效,原因是使用commonJS规范

提取公共代码

  • 资源重复加载,浪费流量/服务器成本
  • 首屏加载慢,影响用户体验

分割代码以按需加载

使用Prepack

  • 编译代码时提前将结果放进编译后的代码中
  • Babel将JavaScript源码抽象成语法树AST
  • 内置js解释器,执行源码
  • 不能识别DOM API和部分NodeJS API
  • 优化后性能可能更差
  • 优化后文件尺寸增加

开启Scope Hoisting

作用域提升

  • 代码体积小
  • 创建函数作用域变少,内存开销小
  • 必须使用ES6模块化语句

使用

  • webpack内置
  • 第三方模块会使用commomJs规范,需要转换
  • 开启降级处理(--display-optimization-bailout)

输出分析

  • webpack-bundle-analyzer

原理

工作原理概括

基本概念

  • Entry:第一步将从Entry开始,抽象输入
  • Module:一切皆模块,从Entry开始递归遍历所有依赖的模块
  • Chunk:代码块,一个Chunk由多个模块组成,用于代码分割与合并
  • Loader:模块转化器,将原内容转成新内容
  • Plugin:扩展插件,监听广播事件,回调

流程概括

  • 开始编译:用参数初始化Compiler对象,加载所有配置的插件,执行run方法
  • 确定入口:根据Entry找出所有的入口文件
  • 编译模块:Loader递归对模块进行翻译
  • 完成编译:得到模块之前的依赖关系以及内容
  • 输出资源:根据关系,输出Chunk,再将每个Chunk转成单独文件输出到文件列表中
  • 输出完成:根据配置输出文件路径和文件名,写到文件系统中

流程细节

  • 初始化:启动构建,读取与配置参数,加载Plugin,实例化Compiler
  • 编译:从Entry出发,递归遍历文件使用Loader处理
  • 输出:将每个Chunk输出单个文件到文件系统中

Loader职责

  • 职责单一,一次只能完成一次转换
  • 多种转换需要用多个Loader顺序处理

加载本地Loader

  • Npm link:在package.json配置,npm link注册到全局,npm link loader-name 连接到node_modules
  • ResolveLoader:到哪里去寻找Loader,默认node_modules,可配置自己的Loader目录

Plugin编写

定义

  • webpack在生命周期广播事件,plugin监听这些事件

原理

  • webpack启动会生成BasicPlugin实例子
  • 传入compiler对象
  • compiler.plugin监听广播的事件

compiler/compilation

  • Plugin和webpack之间的桥梁
  • 前者包含了webpack环境的所有配置信息
  • 后者包含当前的模块资源,编译生成的资源,变化的文件等
  • Compiler代表webpack的整个生命周期
  • Compilation只代表一次编译

image.png