精选文章 前端路 - Webpack

前端路 - Webpack

作者:Azer.Chen 时间: 2021-02-05 09:43:14
Azer.Chen 2021-02-05 09:43:14
【摘要】概述 

本质 
JavaScript 应用程序的静态模块打包器 
核心 
加载器(Loader)机制 
工作流程 
配置初始化 webpack 会首先读取配置文件,执行默认配置编译前准备 webpack 会实例化 compiler,注册 plugins、resolverFactory、hooks。reslove 前准备 webpack 实例化 compilation、NormalModul...

概述


本质

JavaScript 应用程序的静态模块打包器

核心

加载器(Loader)机制

工作流程

  1. 配置初始化 webpack 会首先读取配置文件,执行默认配置
  2. 编译前准备 webpack 会实例化 compiler,注册 plugins、resolverFactory、hooks。
  3. reslove 前准备 webpack 实例化 compilation、NormalModuleFactory 和 ContextModuleFactory
  4. reslove 流程解析文件的路径信息以及 inline loader 和配置的 loader 合并、排序
  5. 构建 module runLoaders 处理源码,得到一个编译后的字符串或 buffer。将文件解析为 ast,分析 module 间的依赖关系,递归解析依赖文件
  6. 生成 chunk 实例化 chunk 并生成 chunk graph,设置 module id,chunk id,hash 等
  7. 资源构建 使用不同的 template 渲染 chunk 资源
  8. 文件生成 创建目标文件夹及文件并将资源写入,打印构建信息

 

基本使用


 安装

yarn add webpack webpack-cli --dev

配置

// package.json
"scripts": { "build": "webpack"
}

// webpack.config.js
const path = require('path')
const HtmlWebpackPlugin = require('html-webpack-plugin') // 用于访问内置插件

module.exports = { mode: 'development', // 指定打包时的工作模式 entry: './src/main.js', // 指定 webpack 打包入口文件 output: { // 打包后,打包结果的输出路径 filename: 'bundle.js', // 输出文件的名称 path: path.join(__dirname, 'output'), // 输出文件所在的目录,必须是绝对路径 publicPath: 'dist/', // 打包后文件的发布路径,默认为空,即项目的根目录 }, module: { // 指定打包所使用到的 loader rules: [ // 配置文件编译规则 { test: '/\.txt$/', use: 'raw-loader' } ] }, plugins: [ // 指定使用到的插件,需要配合 require 引入 new HtmlWebpackPlugin({template: './src/index.html'}) ]
}

打包

yarn webpack

 

工作模式


production(生产模式)

  • 默认模式
  • 会自动启动优化,对代码进行压缩、编译
  • 打包结果代码会极大的丢失可读性

development(开发模式)

  • 会自动优化打包速度
  • 打包时,会自动添加一些调试过程中需要的辅助到代码中

none 

  • 运行最原始状态的打包,不会对代码做任何额外的处理

 

资源加载


Webpack 默认只能编译 JS 文件,会将所有文件都当成 JS 来解析编译。要编译打包其他非 JS 类型的文件,需要安装引入对应的解析模块,否则便会报错

加载方式

  • 遵循 ES Modules 标准的 import 声明
import tools from './tools.js' 
import icon from './icon.png' 
import './main.css'
  • 遵循 CommonJS 标准的 require 函数
const tools = require('./tools.js').default 
const icon = require('./icon.png') 
require('./main.css')
  • 遵循 AMD 标准的 define 函数和 require 函数
define(['./tools.js', './icon.png', './main.css'], (tools, icon) => {})
require(['./tools.js', './icon.png', './main.css'], (tools, icon) => {})
  • CSS 样式代码中的 @import 指令和 url 函数
  • HTML 代码中图片标签的 src 属性

 

Loader(资源加载器)


作用

  • 负责资源文件从输入到输出的转换
  • 支持链式传递
    • 对于同一个资源可以一次使用多个 loader
    • 多个 loader 按有后往前的顺序执行,即先执行的 loader 应该排在后边
  • 用于对模块源码的转换,因为 webpack 本身只支持 js 处理,loader 描述了 webpack 如何处理非 javascript 模块,并且在 build 中引入这些依赖

常用加载器

  • 编译转换类
    • 将加载的资源模块转化成 JS 代码模块
  • 文件操作类
    • 会将导入的文件拷贝到输出的路径
  • 代码检查类
    • 统一代码风格
    • 一般不会自主修改代码

文件资源加载器

// 安装
yarn add file-loader --dev

// 导入文件
import icon from './icon.png'
const img = new Image()
img.src = icon
document.body.append(img)

// 配置 webpack.config.js
module: { rules: [ { test: /.png$/, use: 'file-loader' } ]
}

URL 资源加载器

  • Data URLs
    • 特殊的 URL 协议,可以用来直接表示一个文件的内容
    • 引用时不会发起 http 请求
  • url-loader
    • 可以将(图片)文件转化成 Data URL,从而直接在 JS 中导入
    • 可针对小文件(小于10K)使用,减少请求的次数
    • 大文件(大于10K)则应单独提取存放,提高加载速度
data:text/html;charset=UTF-8,

html content

data:image/png;base64,iVBodfasfjl...jfoasfe // 安装 url-loader yarn add url-loader --dev // 配置 webpack.config.js module: { rules: [ { test: /.png$/, use: { loader: 'url-loader', options: { limit: 10 * 1024 // 10KB,即只将小于 10KB 的文件进行转化 } } } ] }

 

Plugin(插件)


本质

一个函数或者一个包含 apply 方法的对象。

工作机制

Plugin 通过在生命周期的钩子中挂载函数实现扩展

作用

增强 Webpack 自动化能力,解决其他自动化工作,如:清除 dist 目录、拷贝静态文件至输出目录、压缩输出代码等。

常用插件

  • 自动清除输出目录

// 安装
yarn add clean-webpack-plugin --dev

// 配置 webpack.config.js
const { CleanWebpackPlugin } = require('clean-webpack-plugin')
module.exports = { plugins: [ new CleanWebpackPlugin() ]
}
  • 自动生成使用 bundle.js 的 HTML

// 安装
yarn add html-webpack-plugin

// 配置 webpack.config.js
const HtmlWebpackPlugin = require('html-webpack-plugin')
module.exports = { plugins: [ // 用于生成 index.html new HtmlWebpackPlugin({ title: 'Webpack Plugin Sample', // 设置导出的 html 的 title meta: { viewport: 'width=device-width' }, template: './src/index.html' // 设置导出的模板文件 }), // 用于生成 about.html new HtmlWebpackPlugin({ filename: 'about.html' // 设置生成的文件的文件名 }) ]
}
  • 复制静态文件

     

     

// 安装
yarn add copy-webpack-plugin --dev

// 配置 webpack.config.js
const CopyWebpackPlugin = require('copy-webpack-plugin')
module.exports = { plugins: [ new CopyWebpackPlugin([ 'pulic' // 需要复制的文件所在的目录 ]) ]
}

自定义插件案例

class MyPlugin { apply (compiler) { console.log('MyPlugin 启动') // 将插件挂载到钩子上 compiler.hooks.emit.tap('MyPlugin', compilation => { // compilation => 可以理解为此次打包的上下文 for (const name in compilation.assets) { if (name.endsWith('.js')) {  // 对 js 文件进行操作 // 获取文件的内容 const contents = compilation.assets[name].source() const withoutComments = contents.replace(/\/\*\*+\*//g, '') // 匹配注释 compilation.assets[name] = { source: () => withoutComments, // 返回处理结果的内容 size: () => withoutComments.length // 返回处理结果的大小,必须 } } } }) }
}

// 配置 webpack.config.js
module.exports = { plugins: [ new MyPlugin() ]
}

 

Dev Server


Webpack Dev Server 是由 Webpack 官方开发的工具,将自动编译和自动刷新的功能集成在一起

安装

yarn add webpack-dev-server --dev

使用

yarn webpack-dev-server
// 自动唤醒浏览器 ===>
yarn webpack-dev-server --open

原理

  • 监听文件变化
  • 当文件变化时,重新执行打包
  • 将打包后的文件暂存再内存中,而不写入磁盘
  • 内部的 http server 直接从内存中读取文件并发送给浏览器,从而减少不必要的磁盘读写操作,大大提高构建效率

配置

// webpack.config.js
module.exports = { devServer: { // 静态资源访问 contentBase: './public' // 指定静态资源目录 // 代理 API proxy: { '/api': { // http://localhost:8080/api/users => https://api.github.com/ target: 'https://api.github.com' pathRewrite: { // 配置代理路径重写规则 '^/api': '' // 将代理路径中的指定字符串替换掉 }, // 不能使用当前主机名(如:localhost:8080)作为请求的主机名 changeOrigin: true } } }
}

 

Source Map


作用

定位代码中错误信息的位置。

因为打包后运行的代码与开发的源代码之间存在较大差异,而调试和报错都是基于运行代码执行的,如此一来就很难定位到错误信息的位置。所以,就需要 Source Map 来描述结果代码和开发源码之间的对应关系。

配置

// webpack.config.js
module.exports = { devtool: 'source-map'
}

模式

  • eval
    • 是否使用 eval 执行模块代码
  • cheap
    • Source Map 是否包含行信息
  • module
    • 是否能够得到 Loader 处理之前的源代码

Source Map 模式对比

devtool

build

rebuild

production

quality (lo: lines only)

(none)

fastest

fastest

yes

bundled code

eval

fastes

fastest

no

generated code

cheap-eval-source-map

fast

faster

no

transformed code (lo)

cheap-module-eval-source-map

slow

faster

no

original source (lo)

eval-source-map

slowest

fast

no

original source

cheap-source-map

fast

slow

yes

transformed code (lo)

cheap-module-source-map

slow

slower

yes

original source (lo)

inline-cheap-source-map

fast

slow

no

transformed code (lo)

inline-cheap-module-source-map

slow

slower

no

original source (lo)

source-map

slowest

slowest

yes

original source

inline-source-map

slowest

slowest

no

original source

hidden-source-map

slowest

slowest

yes

original source

nosources-source-map

slowest

slowest

yes

without source content

 

HMR(模块热更新)


Dev Server 在自动编译后 ,会自动刷新页面。

但是,在页面自动刷新后,会导致原有的页面状态丢失。如,自动刷新前测试输入的内容的丢失。

配置

// 方案一
webpack-dev-server --hot

// 方案二 webpack.config.js
const webpack = require('webpack')
module.exports = { plugins: [ new webpack.HotModuleReplacementPlugin() ]
}

示例

// main.js
const editor = createEditor()
document.body.appendChild(editor)

const img = new Image()
img.src = background
document.body.appendChild(img)

// 使用手动热更新的 API,会替换原有的处理方案,不会再自动刷新
if(module.hot) { // 判断是否已开启 HMR,避免 API 报错 // js 模块热替换 let lastEditor = editor module.hot.accept('./editor', () => { // 手动处理人更新后的处理方式 console.log('editor 模块更新了') const value = lastEditor.innerHTML document.body.removeChild(lastEditor) const newEditor = createEditor() newEditor.innerHTML = value document.body.appendChild(newEditor) lastEditor = newEditor }) // 图片热替换 module.hot.accept('./better.png', () => { img.src = background console.log(background) })
}

注意事项

  • 手动处理的代码中出现错误时,会自动回退使用自动刷新,从而导致无法看到错误信息
  • 没启用 HMR 的情况下,HMR API 报错
  • webpack 打包时会自动去除没有意义的代码

关于样式文件热更新问题

因为样式文件是通过 loader 处理的,在 style loader 中就会自动执行热更新。样式文件更新后只需要把对应的更新替换到原本的文件中。所以,样式文件的热更新是开箱即用的,不需手动处理。

 

生产环境优化


配置方案

  • 配置文件根据环境不同导出不同的配置
// webpack.config.js
module.exports = (env, argv) => { const config = { // 开发环境配置 } if (env === 'production') { config.mode = 'production' config.devtool = false config.plugins = [ ...config.plugins, new CleanWebpackPlugin() new CopyWebpackPlugin(['public']) ] } return config
}
  • 一个环境对应一个配置文件
    • webpack.common.js
    • webpack.dev.js
    • webpack.prod.js

DefinePlugin

通过伪代码注入全局成员

// webpack.config.js
const webpack = require('webpack')
module.exports = { mode: 'none', entry: './src/main.js', output: { filename: 'bundle.js' }, plugins: [ new webpack.DefinePlugin({ API_BASE_URL: '"https://api.example.com"' }) ]
}
// main.js
console.log(API_BASE_URL)

Tree Shaking

// 配置 webpack.config.js
module.exports = { optimization: { // 集中配置 webpack 中的优化功能 useExports: true, // 是否只导出被外部使用的成员 minimize: true, // 是否移除、压缩掉未被使用的成员代码 }
}

Tree Shaking 的作用在于去除代码中未被引用的部分,从而减轻代码的重量。

Tree Shaking 并不特指某一个配置选项,而是在 production 模式下会自动启动的一组具有优化效果的功能的搭配只用。

Tree Shaking 的实现是存在限制性的。实现 tree shaking 的前提是 ESM,即由 Webpack 打包的代码必须使用 ESM 模式。而且,在同时使用 babel-loader 时,由于低版本的 babel 可能会会先将模块转化成 CommonJS,从而导致 Tree Shaking 无法实现

合并模块

// webpack.config.js
module.exports = { optimization: { concatenateModules: true, // 是否将所有模块合并到同一个函数中 }
}

又称为作用域提升(Scope Hoisting)。即将所有的模块尽可能地合并输出到一个函数中,提升运行效率的同时,减小代码的体积。

代码分割

  • 多入口打包

// webpack.config.js
optimization: { splitChunks: { chunks: 'all' // 提取公共模块,组成一个单独的文件 }  
},
plugins: [ new HTMLWebpackPlugin({ title: 'Multi Entry', template: './src/index.html', filename: 'index.html', chunks: ['index'] }), new HTMLWebpackPlugin({ title: 'Multi Entry', template: './src/album.html', filename: 'album.html', chunks: ['album'] })
]
  • 动态导入

// 在引用时才执行导入
const render = () => { const hash = window.location.hash || '#posts' const mainElement = document.querySelector('.main') mainElement.innerHTML = '' if (hash === '#posts') { // 满足条件则导入、使用 /* 魔法注释,通过 webpackChunkName 给分包进行命名,若输出注释同名,则会被打包到一个文件中 */ import(/* webpackChunkName: 'posts' */'./posts/posts').then(({ default: posts }) => { mainElement.appendChild(posts()) }) }
}

MiniCssExtractPlugin

将 CSS 提取打包到单独的文件当中,在通过 link 标签的方式注入到输出结果的 html 中

// 安装
yarn add mini-css-extract-plugin

// 配置 webpack.config.js
const OptimizeCssAssetsWebpackPlugin = require('optimize-css-assets-webpack-plugin')
const TerserWebpackPlugin = require('terser-webpack-plugin')

module.exports = { optimization: { // 一旦配置,则会覆盖默认的压缩模式,需要重新手动配置 minimizer: [ new OptimizeCssAssetsWebpackPlugin(), // 压缩输出的 css 文件 new TerserWebpackPlugin() ] }, module: { rules: [ { test: /\.css$/, use: [ MiniCssExtractPlugin.loader, 'css-loader' ] } ] }, plugins: [ new MiniCssExtractPlugin() ]
}

输出文件名 Hash

添加 hash 名,可以在每次模块内容更改时得到一个新的包含 hash 值的文件名。

生产环境下,当系统识别到新的文件名时,就会重新发送文件请求,能有效避免缓存的问题。

// 项目级
// 项目中任意位置有改动,都会触发整个项目的重置、更新
module.exports = { output: { filename: '[name]-[hash].bundle.js' }  
}

// 目录级
// 目录下的文件有改动,则触发整个文件夹中的文件重置、更新
filename: '[name]-[chunkhash].bundle.js'

// 文件级
// 只更新有改动过的文件
filename: '[name]-[contenthash].bundle.js'

 

勿删,copyright占位
分享文章到微博
分享文章到朋友圈

上一篇:JAVA 集合框架简介

下一篇:什么是十字链表?

CSDN

CSDN

中国开发者社区CSDN (Chinese Software Developer Network) 创立于1999年,致力为中国开发者提供知识传播、在线学习、职业发展等全生命周期服务。

您可能感兴趣 换一换

  • javascript 密码强度规则、打分、验证(给出前端代码,后端代码可根据强度规则翻译)...

    前言: 密码强度是一个很普遍的功能,比较简单,主要是怎么制定这个强度规则。现在需要升级密码强度的验证,以前的验证比较简单,现在已经不能满足需求了,现在需要可灵活变化并有多级别可配置选择的一个密码强度验证,所以就设计了下面这个东东。在设计前也参考了下比较成熟的强度规则,大同小异,不外乎都采取了打分的机制来控制密码强度规则,这样可配置性高,灵活。本来想直接拿来用的,但是发现都比较旧,有些不太适宜...

  • 老男孩之五年软考路

    老男孩之五年软考路 软考就像一盒巧克力,但你可以选择自己的味道~   对于生于82的我,非常羡慕那些一考即过的午饭们,对于我们这样的老男孩,也只能感叹不已。 其实我一直想把自己的的经历写下来,因为还有很多朋友像我一样,已经工作了,没有太多时间去复习,但又想通过软考。   不知不觉,已经出来工作5.5年,5年来,算是完成了自己的目标:通过软考高级。 大学学的是计算机科学与技术,稀里糊涂地过完了...

  • 一步一步教你搭建win7下的Node.js服务(来玩玩服务器端的javascript吧,这可不是前端js插件)...

    什么是Node.js?还服务器端javascript?对于这个概念我在这篇文章不做解释,可以自己去搜索了解下,服务器端js不是新技术,只是最近的node.js的火爆让他爆发了,我会在以后的文章里解释什么是node.js。这里只是纯粹的搭建,连环境都没有,还玩什么服务器端js,一切都成了浮云,让我们先搭建一个环境,输入一个“hello world”的页面。对的,win7下的搭建,你只需一步一步...

  • 前端开发工程师-北京

    岗位职责: - 开发基于AJAX的高性能互联网软件,快速开发建立原型 ; - 参与跨领域的软件产品设计,肩负多样的使命和职责 。任职要求: - 计算机科学或相关专业领域的本科及以上学历,2年以上相关工作经验;  - 具备扎实的计算机编程能力,尤其在数据结构、算法和软件设计方面功力深厚; - 领悟能力强,学习新知识能力强,具有技术创新精神,勇于解决技术难题; - 不管之前是javascri...

  • 前端开发之用工作中的实例来教你切图

    下面就来说说切图这点事儿吧.   一:图片怎么切?   1.切片:   先啰嗦一下几个基础概念.切片:制图软件或网页制作软件中,把图像切成几部分,一片一片晚上传,这样上传的速度比较快.切片工具主要是用来将大图片分解为几张小图片,这个功能用在网页中比较多,因为现在的网页中图文并茂,也正因如此打开一个网页所须的时间就比较长,为了不让浏览网页的人等等时间太长,所以他们将图片切为几个小的来组成。  ...

  • IT草根的江湖路之一:心若在,梦就在—11秒,改变人生

    IT草根的江湖路之一:11秒,改变人生【我和51CTO的故事】   前言:借51CTO六周年之际,献上自己的从最初发展到现在的经历,希望给后来看一点借鉴,也是对自己的一个总结。在这些年中,自己在一些在各大IT社区网站漂泊,慢慢的就固定了几个社区,其中51CTO就是我每天必去的社区之一。在51CTO中,不仅学到了阅读了很多有水准的技术文章,也结识了不少的朋友。感谢51CTO的陪伴,IT路上不再...

  • 草根IT江湖路之三:希望,在坚持之中

    草根IT江湖路之三:希望,在坚持之中      前言:不管是什么行业,都要学会推销自己。那时候,我们迈出了第一步!      系列文章链接: 草根IT的江湖路之一:心若在,梦就在—11秒,改变人生        草根IT江湖路之二:改变,破釜沉舟的斗争       草根IT江湖路之三:希望,在坚持之中     草根IT江湖路之四:羞辱,将眼泪吞下 我和小辉决定亲自上门去公司,争取面试的...

  • 草根IT江湖路之四:羞辱,将眼泪吞下

    草根IT江湖路之四:羞辱,将眼泪吞下 前言:很多事情,都不是一帆风顺的;很多的事情,也不是一蹴而就的,更多的事情,也不是说你想怎么样就怎样的!         系列文章       草根IT的江湖路之一:心若在,梦就在—11秒,改变人生        草根IT江湖路之二:改变,破釜沉舟的斗争       草根IT江湖路之三:希望,在坚持之中     草根IT江湖路之四:羞辱,将眼泪吞下...

华为云40多款云服务产品0元试用活动

免费套餐,马上领取!
前端路 - Webpack介绍:华为云为您免费提供前端路 - Webpack在博客、论坛、帮助中心等栏目的相关文章,同时还可以通过 站内搜索 查询更多前端路 - Webpack的相关内容。| 移动地址: 前端路 - Webpack | 写博客