Webpack

241 阅读18分钟

一:模块打包工具的由来

  1. ES Modules存在环境兼容性问题(es6)
  2. 模板文件过多,网络请求频繁
  3. 所有的前端资源都需要模块化
  4. 新特性代码编译
  5. 模块化JavaScript打包
  6. 支持不同类型的资源模块

二:模块打包工具概要(Webpack)

  1. 模块打包器(Module bundler),将零散的代码打包到同一个文件
  2. 模块加载器(Loader),有环境兼容的代码通过插件进行编译转换,或者其他转换
  3. 代码拆分(Code Splitting),按模块进行打包
  4. 资源模块(Asset Module),载入任意类型的文件

三:Webpack快速上手

  1. 创建两个js文件(index.js,heading.js)放在src目录和一个在根目录的index.html文件,并且使用ES Module将这些文件引用起来
  2. 在index.html文件中通过模块方式引入(注意需要
  3. yarn init --yes 初始化一个package.json
  4. yarn add webpack webpack-cli --dev  安装webpack
  5. yarn webpack --version 查看版本
  6. yarn webpack 运行webpack(未配置的话,webpack4.0后面的版本会按照默认配置进行打包)

四:Webpack文件配置

  1. 4.0后的版本支持零配置打包,会按照约定进行打包(src/index.js打包成dist/main.js)
  2. 需要自定义的话,需要在项目的根目录下创建一个webpack.config.js文件,这个文件是运行在node环境中的所以需要按照commonJS方法去编写代码(module.export)
  3. l例如把index.js入口路径改为main.js,那么就需要webpack专门的添加配置文件(webpack.config.js)
  4. entry属性表示webpack打包文件入口路径
  5. output属性表示输出文件路径,它的值要求是一个对象,里面有一个filename可以指定输出文件名称,还可以添加一个path属性表示文件输出目录,并且路径必须是一个绝对路径,例如(output:{ filename : bundle.js , path:'目录' })

五:Webpack工作模式

  1. webpack4新增加了一个工作模式用法,大大简化了webpack配置的复杂程度
  2. 在webpack.config.js文件中添加一个mode属性,它可以设置webpack的工作模式
  3. 生产模式,mode:'production'  ||  通过yarn webpack -- mode production开启,这是默认模式,在这个模式下webpack会自动启动一些优化插件
  4. 开发模式,mode:'devalopment' || 通过yarn webpack --mode development开启,这个模式会自动优化打包速度
  5. mode:'none' || 通过 yarn webpack --mode node 开启,运行最原始的打包

六:Webpack资源模块加载

  1. webpack不仅仅是javascript的模块打包工具,webpack还是整个前端工程的模块打包工具,可以在前端项目引入任意类型的文件
  2. 因为webpack内部默认只会处理javascript文件,需要处理其他文件的话需要按照加载器(Loader),r然后在wenpack.config.js文件进行相应的配置
  3. yarn add css-loader --dev (这个插件是处理css文件的)
  4. yarn add style-loader --dev (它的作用就是把css-loader转换的结果通过style的样式追加到页面,如果没有这个插件,页面样式就不能正常显示)
  5. 这只是尝试把入口改为css,一般入口都应该为js,在module属性里面进行配置,然后紫萼如rules属性
  6. loader是webpack的核心特性,借助loader就可以加载任意类型的资源

七:Webpack导入资源加载器

  1. 正确的用法还是将js作为文件的入口
  2. 通过import的方式引入文件,在js将css引入和js文件一起打包
  3. webpack建议我们根据代码需求动态导入资源,因为需要资源的不是应用,而是代码
  4. 逻辑合理,js确实需要这些资源文件,确保上线资源不缺失,都是必要的

八:Webpack文件资源加载器

  1. 例如图片等文件
  2. yarn add file-loader --dev
  3. 配置modele模块:module:{ rules:[ { test: /.jpg$/ , use: 'file-loader' }] }
  4. 如果配置完module就执行yarn webpack打包就会报错,因为打包过后文件引用路径不正确,默认引用了根目录
  5. 解决方案就是在output属性里面配置(资源文件路径-告诉网站打包过后的路径)--publicPath: 'dist/'--文件后面的斜线不能省略

九:Webpack-URL加载器

  1. Date URLs(base方案)与 url-loader
  2. 例如把图片转换成base64,取代file-loader
  3. yarn add url-loader --dev
  4. 这个插件可以配置超出大小不使用该方式打包(使用file-loader,但是必须需要安装这个插件)
  5. 下面表示超出10kb单独提取存放
  6. 小于10kb文件转换为Data URLs嵌入代码中
  7. 如果使用限制大小这种方法必须需要先安装file-loader
  8. 最佳实现方案,小文件使用Data URLs减少请求次数,大文件单独提取存放,提高加载速度

十:Webpack常用加载器分类

  1. 编译转换类-将模块转换为js代码,例如:css-loader
  2. 文件操作类型加载器-将文件加载到的资源模块拷贝到输出的目录,并且把访问路径导出,;例如:file-loader
  3. 代码质量检测-统一代码风格,提高代码质量,例如:esline-loader

十一:Webpack与ES2015

  1. 因为模块打包需要,所以处理import和export,但它并不能处理es2015的新特性,需要配置才可以
  2. yarn add babel-loader @babel/core @babel/preser-env --dev
  3. 使用这个插件,加载器可以用来编译转换代码

十二:Webpack加载资源的方式

  1. 遵循ES Modules标准的import声明
  2. 遵循CommonJS标准的require函数
  3. 遵循AMD标准define函数和require函数
  4. Loader加载的非javascript也会触发资源加载(例如5,6)
  5. css-loader--(import和@import url方式)
  6. html-loader(img的src属性,a标签的href属性)
  7. 因为html-loader加载器不认识a标签的href属性,所以配置如下

十三:Webpack核心工作原理

  1. 根据配置找到入口文件,这个文件一般是js脚本文件
  2. 然后根据入口文件的代码,根据import或者require等解析,推断出来这个文件依赖的资源模块,然后解析对应的依赖
  3. 最后形成一颗依赖树,然后webpack就是遍历这颗依赖树,找到相对应的加载器,去加载这个模块,打包带出口文件
  4. Loader机制是Webpack的核心

十四:Webpack与ES2015

  1. 需求:开发一个markdown-loader,用了这个loader后可以在代码这届带入MD文件
  2. 将MD文件转换为html
  3. 解析为html----yarn add marked --dev
  4. 第二种方法的loader--yarn add html-loader --dev
  5. Loader负责资源文件从输入到输出的转换
  6. 对于同一资源可以依次使用多个Loader

十五:Webpack插件机制介绍(Plugin)

  1. 插件机制是Webpack另一个核心特性
  2. 目的是增强Webpack自动化能力
  3. Loader专注实现资源模块加载
  4. Plugin解决其他自动化工作(例如清除dist目录,拷贝静态文件至输出目录,压缩输出代码)

十六:Webpack自动清除输出目录插件

  1. yarn add clean-webpack-plugin
  2. 在webpack.config.js中添加一个plugin属性,用于配置插件
  3. 安装完成后需要导入

十七:Webpack自动生成HTML插件

  1. Webapck自动生成HTML插件会自动生成使用打包结果的HTML文件,通过Webpack输出HTML文件
  2. yarn add html-webpack-plugin
  3. 配置完成后打包完成后目录会出现问题,因为之在output配置了publicPath,所以需要删除
  4. 在插件配置中可以添加属性设置标题和标签
  5. 还可以使用自定义模板,在src目录创建一个html文件,然后可以通过<%= htmlWebpackPlugin.options.title %>模板语法获取值
  6. 也可以创建多个html,创建多个实例即可

十八:Webpack插件总结

  1. yarn add copy-webpack-plugin --dev
  2. 其他插件具体可以通过关键字去查找使用

十九:Webpack开发一个插件

  1. Plugin通过钩子机制实现
  2. Webpack要求我们的插件是一个函数或者是一个包含apply方法的对象
  3. Webpack通过生命周期的钩子中挂载实现展开

二十:Webpack开发体验问题

  1. 设想理想的开发环境
  2. 以Http Server运行
  3. 自动编译 + 自动刷新
  4. 通过Source Map支持

二十一:Webpack自动编译

  1. 可以通过watch工作模式监视文件变化,自动重新打包
  2. yarn webpack --watch(通过这个命令打包即可监视)
  3. 但是更改完源代码后需要手动刷新浏览器

二十二:Webpack自动刷新浏览器

  1. BrowserSync工具
  2. yarn add browser-sync --dev
  3. 第一步启动yarn webpack --watch
  4. 第二部启动browser-sync dist --files '**/*'(监视dist文件下面的所有文件)
  5. 但是使用这种方法有弊端,使用需要使用watch监视文件变化,还需要使用BrowserSync监视,效率较低(因为需要两次监视)

二十三:Webpack-Dev Server

  1. 这是Webpack提供的一个开发工具,提供用于开发的HTTP Server,集成自动编译和自动刷新浏览器等功能
  2. yarn add Webpack-dev-server --dev
  3. 这里需要注意webpack脚手架版本,如果版本太高的话会报错
  4. 运行命令,yarn webpack-dev-server
  5. 自动打开浏览器命令:yarn webpack-dev-server --open

二十四:Webpack-dev-server静态资源访问

  1. webpack-dev-server默认只会server打包输出文件,主要提供webpack打包输出的文件,都可以直接被访问,但是其他静态资源文件也需要server就需要额外告诉webpack-dev-server
  2. 到webpack.config.js中添加devServer属性,使用contentBase指定额外的资源

二十五:Webpack-dev-server代理API

  1. 开发过程中会产生跨域问题
  2. 可以使用跨域资源共享CORS去解决,使用CORS的前提是API必须支持,但是并不是任何情况下API都应该支持

二十六:Source Map介绍

  1. Source Map 解决了源代码与运行代码不一致所产生的问题,在调式时产生

二十七:Webpack配置Source Map

  1. Webpack支持12中source-map方式,每种方式的效率和效果各不相同
  2. 在webpack.config.js中配置devtool属性

二十八:Webpack-eval模式的Source-Map

  1. 在控制台显示的是打包过后的代码

二十九:Webpack-devtool模式对比

  1. eval---是否使用eval执行模块代码
  2. cheap-source map---是否包含行信息
  3. module--是否能够得到Loader处理之前的源代码

三十:Webpack选择Source Map模式

  1. 开发模式选择:cheap-module-eval-source-map
  2. 原因一:个人代码一般不会超过80个字符
  3. 原因二:代码经过Loader转换过后的差异较大
  4. 原因三:首次打包速度慢无所谓,重新打包相对较快
  5. 生产环境选择:none
  6. 原因一:Source Map会暴露源代码
  7. 原因二:调式是开发阶段的事情
  8. 或者选择nosource-source-map,出现错误就可以看到源代码的对应位置,而不暴露源代码

三十一:Webpack自动刷新的问题

  1. 如果页面在开发的时候,使用了编辑器,你在页面上输入了文字,然后你修改了css样式,保存代码后,webpack-dev-server就会自动刷新浏览器,那么这就会导致之前输入得的文字也消失了
  2. 解决办法就是开启HMR

三十二:Webpack开启HMR

  1. 模块热替换,应用程序运行过程中实时替换某个模块,应用运行状态不受影响
  2. HMR已经集成在webpack-dev-server中,我们可以通过启动webpack0dev-server时开启
  3. 开启命令:yarn webpack-dev-server --hot开启HMR
  4. 不过也可以通过配置的方式开启HMR
  5. 在配置文件中的devServer属性中进行配置(hot : true)
  6. 并且需要导入webpack模块,使用其中的插件

三十三:Webpack使用HMR的疑问

  1. webpack中的HMR并不是可以开箱即用的
  2. webpack中的HMR需要手动处理模块热更替换逻辑
  3. 样式文件自动更新是因为样式文件是经过loader处理的,所以不需要我们手动处理
  4. 样式文件修改完直接替换即可,但是脚本文件是没有规律的,各种导出什么的,或者其他,无法创建一个通用的HMR
  5. 总结:我们需要手动处理js模块更新后的热替换

三十四:Webpack使用HMR API

  1. module.hot
  2. accept方法,接受给定依赖模块的更新,并触发一个回调函数来对这些更新做出响应
  3. 比如监视一个js脚本文件-module.hot.accept('./heading.js', () => {console.log('跟新')})
  4. 更改完这个被监视的脚本文件后就会触发响应的回调
  5. 如果没有使用这个HMR热更新的话,那么会导致页面刷新

三十五:Webpack处理模块热替换

  1. 更改图片路径即可
  2. module.hot.accept('图片路径', () => { img.src = background})

三十六:Webpack-HMR注意事项

  1. 处理HMR的代码报错会导致自动刷新,我们可以将devServer中的hot:true改为hotOnly:true
  2. 没启动的情况下,HMR-API报错(可以加一个判断if)
  3. 这是代码中多了一些与业务无关的代码(打包时会被自动移除,不用担心)

三十七:Webpack生产环境优化

  1. 生产环境注重运行效率
  2. 开发环境注重开发效率
  3. webpack4.0提供了模式(mode),为不同的工作环境创建不同的配置

三十八:Webpack不同环境下的配置

  1. 配置文件根据不同环境做出不同的配置,一个环境对应一个配置文件

  2. env是运行的时候传入的环境名参数,例如yarn webpack --env production

  3. argv表示运行时传入的所有参数

    module.exports = (env, argv) => { 提供if语句做判断修改属性的值 }

三十九:Webpack不同环境的配置文件

  1. 多文件配置,适合大型项目,上一种方式适合小型项目
  2. 我们新建3个文件,一个作为公共的配置文件,一个作为开发环境的,一个作为线上环境的
  3. webpack.common.js
  4. webpack.dev.js
  5. webpack.prod.js
  6. yarn add webpack-merge --dev(这个用于将公共的配置合并至生产配置或者开发配置)
  7. 打包运行时需要指定运行的配置文件,例如yarn webpack --config   webpack.prod.js

四十:Webpack DefinePlugin

  1. 这是一个插件,为代码注入全局成员(process.env.NODE_ENV)

    const webpack = require('webpack')

    module.exports = { plugin:{ new webpack.DefinePlugin({ API_BASE_URL : ' "www.baidu.com" ' }) } }

    //在其他文件使用,打包时就会输出上面的链接 console.log( API_BASE_URL )

四十一:Webpack使用Tree Shaking

  1. 摇掉代码中未引用的部分
  2. Tree Shaking不是指某个配置选项,它是一组功能搭配使用后的优化效果,在生产模式模式下自动开启
  3. optimization属性用来集中配置优化功能
  4. 在里面配置上useExports : true 表示只导出外部使用成员
  5. 在里面配置上minimize  : true 表示压缩掉无用的东西

四十二:Webpack合并模块

  1. 使用这个属性以后的打包结果就不是和以前一样一个模块对应一个函数,而是尽可能的将所有模块合并输出到一个函数中,这即提升可运行效率,又减少了代码的体积

四十三:Webpack-Tree Shaking 与 Babel

  1. 有时候使用Babel后,Tree Shaking不能生效
  2. 是因为Tree Shaking的前提是ES Modules,由webpack打包的代码必须使用ES Modules,为了转换代码中的ECMAScript新特性,很多时候我们会选择babel0loader去处理js,ESM被转换为了CommonJS,不过新的本版已经关闭了这一特性,现在支持ESM了

四十四:Webpack-sideEffects

  1. 它允许我们通过配置的方式去标识代码是否有副作用,从而为tree Shaking提供更大的压缩空间
  2. 副作用:模块执行时除了导出成员以外所作的事情
  3. sideEffects一般用于npm包标记是否有副作用
  4. 在配置文件中-optimization:{ sideEffects : true }
  5. 在package.json标记是否有副作用-“sideEffects” : false
  6. 现在的标记是没有副作用,那么打包的时候这些没有把用到的模块就不会被打包

四十五:Webpack-sideEffects注意

  1. 使用sideEffects的前提就是确定你的代码真的没有副作用
  2. 例如在js文件中为Nunber的原型添加一个扩展方法,然后在其他文件导入这个文件,那么就存在副作用
  3. 解决方法就是可以在package.json中单独标记文件路径

四十六:Webpack代码分割

  1. 代码分包 || 代码分割
  2. 在应用开始工作的时候并不是每个模块在启动的时候都是必须的,所以我们可以分包,按需加载
  3. 可以使用多入口打包或者动态加载(vue 路由的按需加载)

四十七:Webpack多入口打包

  1. 多入口打包一般适用于多页面应用程序,一个页面对应一个打包入口,公共部分单独提取
  2. 我们可以把配置文件的entry属性改为一个对象entry : {}
  3. output输出文件名可以使用占位符【name】的方式
  4. 但是打包完成后会有问题,就是打包结果被html页面全部载入了script,这是因为输出HTML的插件是默认自动注入所有的结果的HTML,如果只要指定的打包结果可以在插件中使用chunks进行配置

四十八:Webpack提取公共部分

  1. 不同入口中肯定会有公共模块
  2. 在optimization属性中进行配置即可

四十九:Webpack动态导入

  1. 按需加载:需要用到某个模块时,在加载这个模块
  2. 动态导入的模块会被自动分包
  3. 动态导入的方式比多入口更为灵活
  4. 实现方式只需要按照 ES Module去动态导入的方式去导入成员的方式就可以了

五十:Webpack魔法注释

  1. 在import的最前面import(/*  名字 */ ‘路劲’)
  2. 使用这种方式那么打包出来的文件bundel就是你的注释的名称
  3. 如果相同的名字那么就会被打包到一起

五十一:Webpack-MiniCssExtractPlugin

  1. 提取CSS到单个文件,通过这个插件就可以实现CSS按需加载
  2. yarn add mini-css-extract-plugin
  3. 按照完成后再webpack.config.js中导入这个插件
  4. 然后添加到plugin对象数组中
  5. 并且去掉之前使用的style-loader
  6. 取而代之的是MiniCssExtractPlugin.loader(不需要额外安装)

五十二:Webpack-OptimizeCssAssetsWebpackPlugin

  1. 压缩输出CSS文件
  2. yarn add optimize-css-asset-webpack-plugin --dev
  3. 安装完成后导入插件
  4. 然后再plugin属性中使用
  5. 但是放在plugin的话,这个插件就会在任何情况下被执行,而配置到optimization中的minimizer的话,只会在minimizer这个特性开启时才会执行(生产环境是自动开启的)
  6. 但是如果这样配置的话JS文件会打包不了,需要安装(yarn add terser-webpack-pulgin --dev)同样如上配置

五十三:Webpack输出文件名Hash

  1. 生产模式下,文件名使用Hash,一般我们在部署项目的时候都会启动静态缓存,使用文件名Hash可以解决缓存时间长的问题,一但我们的资源文件发生改变,那我们的文件名称也会发生改变,对客户端来说全新的文件就是全新的请求,那就解决了缓存问题,那就可以将缓存时间设置很长
  2. output属性的filename和其他属性的filename都支持使用占位符的方式为文件名设置名称和Hash
  3. 第一个占位符name是名称,第二个占位符是Hash
  4. [name]-[hash : 8]--这个Hash是项目级别的就是项目中任意一个文件发生改变,那么全部文件名都会发生改变
  5. [name]-[chunkhash : 8]--这个Hash是修改文件,对应的js和css文件名称都会改变,同一个chunk都会改变
  6. [name]-[chunkhash : 8]--这个Hash是文件级别的hash,是根据输出内容改变的
  7. chunkhash是最适合解决缓存问题的