webpack 基础
Webpack:
配置文件:
var path = require('path')
module.exports = {
entry: './a.js', // 要打包的入口文件
output: {
path: path.resolve(__dirname,''), // 输出路径
filename: 'out.js' // 输出文件名称
},
mode: 'none' // 模式 默认 production 给生产环境打包,会压缩,none 不会
}
Webpack loader :
Loader 是 Webpack 生态的一个重要组成,一般称为预处理器
Webpack 在进行打包时,对所有引入的资源文件,都当做模块,但是它本身只支持对 js 文件处理(现支持 json),如果引入 css 或 图片文件,那么在处理该模块是会报错。
`Module parse failed…You may need an appropriate loader to handle this file type.
`
此时 loader 就起作用了
css-loader 作用为 解析 css 文件 包括 @import 等 css 语法,作用也仅是解析 css 文件,在解析完成之后,以字符串形式将 css 插入 js 文件中,此时 css 并未生效,需要插入 html
style-loader 作用为将 js 中的 css 样式插入 html 中,原理很简单,即动态的生成 style 标签 插入 html 中的 head 标签中
// 配置
const path = require('path') // 导入 path 模块 为了获取文件路径
module.exports = { // 模块输出
entry: './a.js', // 要打包的项目入口文件
output: { // 输出信息
path: path.resolve(__dirname,''), // 输出路径
filename: 'bundle.js' // 输出文件名
},
module: { // 模块内容
rules: [{
test: /\.css$/ , // 正则匹配 css 文件
use: ['style-loader','css-loader']
// 使用 style-loader 和 css-loader 解析 css 文件 从后向前执行
}]
},
mode: 'none' // 使用 none 模式 不压缩内容,否则默认压缩
}
Webpack 入口与出口:
Webpack 会将所有依赖的文件打包到一个 .js 文件中,但是这通常不是希望打包出来的资源,如果要拆分 js 、css 图片等资源的话,可以在构建的时候,通过预处理器 loader 和 插件 plugin 进行干预,将 .js 文件转换为 js、css 、图片资源
模块化 import 与 require 的区别:
Webpack 是模块打包工具,将一切文件视为模块,本身支持非常多的模块化方法。
import 有 impot '' 和 impot() 两种方法:
- import '' 同步
- import() 异步
Webpack 在打包时,碰到 import() 引入的模块不会立即将模块内容导入,而是会动态生成 js 即在运行时 生成 script 标签,import() 导入模块后是 promise 对象,可通过 impot().then() 处理后续异步工作
而 require 是 Commonjs 的内容
主要在 node.js 中使用,主要通过 module.exports 到处模块 ,require('') 导入模块
Webpack 入口 entry:
Webpack 配置内容:
var path = require('path')
module.exports = {
entry: './index.js',
output: {
path: path.resolve(__dirname,''),
filename: 'bundle.js'
},
mode: 'none'
}
上述配置为 根据当前目录的 index.js 打包 输出为 当前目录的 bundle.js
entry 就是 资源入口文件 ,是一个相对路径
Webpack 基础目录 context
上面配置省略了一个配置参数 context,Webpack 官方称之为 基础目录 (base directory)
context 在 Webpack 中表示 资源入口文件 entry 是从那个目录为起点的,context 值为字符串,表示绝对路径
var path = require('path')
module.exports = {
context: path.resolve(__dirname,'./src'),
entry: './js/a.js',
output: {
path: path.resolve(__dirname,''),
filename: 'bundle.js'
},
mode: 'none'
}
上述配置为 从工程根目录下 src 文件夹的 a.js 开始打包
一般不会设置 context ,不设置 context 时,它是当前工程根目录
Webpack 资源入口 entry
Webpack 资源入口 entry 代表路径为相对路径,可以是字符串、数组、对象、函数形式
字符串形式:
`entry: 'index.js'
`数组形式:
` entry: ['core-js/stable','regenerator-runtime/runtime','./index.js']
`
表示数组最后一项为 资源入口文件 数组其余文件会预先构建到入口文件
// 上面配置 与 下面配置 是等价的
// index.js
import 'core-js/stable'
import 'regenerator-runtime/runtime'
// webpack.config.js
module.exports = {
entry: './index.js'
}
对象形式:
var path = require('path') module.exports = { entry: { entry: { app: ['core-js/stable', 'regenerator-runtime/runtime', './a.js'], vendor: './vendor' }, output: { path: path.resolve(__dirname, ''), filename: '[name].js' }, mode: 'none' };
分别从两个入口文件打包,每个入口文件各自寻找自己依赖的文件模块打包成一个 js ,最终得到两个 js 文件
函数形式:
Webpack 取 函数返回值作为入口配置,返回值是以上三种之一即可
函数形式的 entry 用于做逻辑处理,但少用
Webpack 出口 output
var path = require('path');
module.exports = {
entry: './a.js',
output: {
path: path.resolve(__dirname, ''),
filename: 'bundle.js'
},
mode: 'none'
};
output 就是 资源出口配置项,是一个对象,有属性 filename、path、publicPath、chunkFilename 等重要属性
- Webpack 的 output.filename
打包生成的资源名称,可根据需要修改,除文件名称外,
也可使相对路径。最终的输出的是 path + filename 的拼接
支持类似变量的方式动态生成文件名,如`[hash]
方括号表示特定的动态值 <br> 即 hash (哈希) 值,生成的文件名回事打包时的 hash 值 <br> 特定动态值除 ``
[hash]` 外, 还有
[name]``
,表示的为 chunk 的名称,
简单理解为,每一个资源入口,异步模块资源都 代表一个 chunk
对于 字符串和数组形式的 entry`[name]
值都为 main <br> 对于 entry 为对象形式的多入口配置,``
[name]` 为属性名,对应入口文件 <br> 特定动态值还有
[hash]、[chunkhash]、[contenthash]``
等 - Webpack 的 output.path
表示资源打包的输出位置,要是绝对路径,Webpack4 默认为 dist 目录 - Webpack 的 output.publicPath
表示资源访问路径,指的是 output 的,资源输出位置表示打包完成后,资源存放的磁盘位置
浏览器通过 资源访问路径 访问资源
表现形式有两个,相对路径 和 绝对路径 - 相对路径,又分为两种情况
- 相对于当前浏览的 html 页面路径取值 ,以 ./ 或 ../ 开头
- 相对于当前页面的服务器地址路径取值 , 以 / 开头
- 绝对路径,output.publicPath 以 HTTP 协议名称开始,一般在使用了 CDN 时使用
因 CDN 域名与服务器域名会不一样,协议名称有 http 和 https ,或使用相对协议
即 以 // 开头,省略前缀 https: 或 http:,
使用相对协议时,浏览器会以当前页面使用协议与相对协议拼接 - Webpack 的 output.chunkFilename
chunkFilename 也是用于表示 打包生成的文件名
与 filename 区别在于 表示的是在打包过程中 非入口文件的 chunk 名称
通常在使用 异步模块时 会生成非入口文件的 chunk
hash 、chunkhash 、contenthash 区别:
当浏览器访问一个 html 页面时,html 页面会加载 js css 等外部资源,需要花费一定下载时间
如果页面上有一些外部资源是长期不变的,如 JQuery 或 商标图片等 可以将这部分资源存储在本地,这就是缓存
浏览器获得资源后,只有同名的资源在缓存有效期内,就会把该资源一直缓存在本地,于是下次访问之后,对于相同的资源,不会再请求服务器,而是直接用本地资源。
可在服务器上设置缓存有效期为几天,几月,几年,使得资源长期缓存在本地。
如果资源内容变化了,但是名称不变,那么浏览器不会重新下载资源,所以要给资源一个独特的命名,如
jquery-2df3ad34.js 这种,只要内容不变,名称不变,内容一变,名称也随之变,
于是浏览器找不到资源,就会重新请求服务器
而要保证类似 2df3ad34 这种名字是唯一的,就需要依靠 hash
在使用 Webpack 构建时,Webpack 会根据所有文件内容计算 hash,只要文件内容变动,就会重新计算
一般取 生成的 hash 前八位作文件名一部分
在 Webpack 中 通过以 `[hash:8]
` 方式表示取 hash 值前八位
如: `filename: 'jquery-[hash:8].js'
`
在 Webpack 中 hash 、 chunkhash 、 contenthash 都是根据文件内容计算 hash
hash 是根据打包中所有 文件 计算出的 hash,在一次打包中,所有出口文件的 filename 获得的 hash 都是一样的
chunkhash 是根据打包过程中当前 chunk 计算的 hash,如果为 Webpack 配置的是 多入口配置,通常会生成对个 chunk,每个 chunk 对应的 出口 filename 获得的 `[chunkhash]
` 是不一样的
contenthash 类似 chunkhash,是根据 打包时 css 内容计算的 hash,一般使用提取 css 的插件使用
Webpack 预处理器 loader:
预处理器 loader 本质上是一个函数,接受资源模块,将其处理成 Webpack 核心能使用的形式
loader 配置:
const path = require('path');
module.exports = {
entry: './a.js', // a.js里引入了CSS文件
output: {
path: path.resolve(__dirname, ''),
filename: 'bundle.js'
},
module: {
rules: [{
test: /\.css$/,
use: ['style-loader', 'css-loader']
}]
},
mode: 'none'
};
配置 loader 是在 Webpack 中的 module 配置项进行的,用 module 是因为这个配置项是解析和处理模块的。
module 的配置项中 最重要的一个 配置子项 就是 rules。它定义了 loader 的处理规则
rules 是一个数组,数组每一项都是 js 对象,对象有两个关键属性 test 和 use
test 是匹配模块文件名的正则 如 `/\.css$/
` ,与正则匹配的模块会被 use 的 loader 处理
use 可以使 字符串 、对象 、数组,表示使用的 loader,如果是单一 loader 那么可以取字符串,
如 `use: 'babel-loader'
`
如果有额外配置参数,取对象,参数放在 options(部分 loader 放 query)中
如 `use: {loader: 'babel-loader',options: {...}}
`
如果使用多个 loader 进行链式处理,取数组,数组每一项都可以是 字符串或对象
除 test 、 use 外, rules 还有 exclude 、 include 、resource 、 issure 、 enforce 等参数
exclude 与 include
如果有文件不想被正则匹配的 loader 处理,可配置 exclude 项,exclude 中文意为排除
如 `exclude: /node_modules/
`
而 include 与 exclude 相反,它表示只对匹配的文件处理
如 `include: /src/
`
enforce
对同一类后缀名类型的文件,可使用多个 loader 处理,loader 处理顺序为 从后向前
如果想强制某个 loader 最先处理或最后处理,使用 enforce
Webpack 推荐的 enforce 项有两个,pre 和 post
pre 表示 在所有的 loader 之前执行
post 表示在 所有的 loader 之后执行
rules: [{
test: /\.js$/,
use: ['eslint-loader'],
enforce: 'pre',
exclude: /node_modules/,
}]
// 表示在 所有 处理 js 的 loader 之前执行 eslint-loader,进行代码规范检查校验
resource 和 issuer
resource 意为资源,issuer意为发行人
在 Webpack 中 被加载的模块称为 resource,实施加载的模块称为 issuer
而 test 和 exclude 是使用了默认 resource 的
rules: [{
test: /\.css$/,
use: ['style-loader', 'css-loader'],
exclude: /node_modules/
}]
// 两个配置是等价的
rules: [{
use: ['style-loader', 'css-loader'],
resource: {
test: /\.css$/,
exclude: /node_modules/
}
}]
如果要指定只有 src 目录下引用的 css 可以被相应的 loader 处理,可配置 issuer
rules: [{
test: /\.css$/,
use: ['style-loader', 'css-loader'],
exclude: /node_modules/,
issuer: {
test: /\.js$/,
include: /src/
}
}]
// 两个配置是等价的
rules: [{
use: ['style-loader', 'css-loader'],
resource: {
test: /\.css$/,
exclude: /node_modules/
},
issuer: {
test: /\.js$/,
include: /src/
}
}]
babel-loader
主要用于在 Webpack 打包时,用 Babel 将 ES6 转换为 ES5
同时还有预设 `@babel/preset-env
` ,如果不使用 babel-loader 那么依旧可以使用 Webpack 进行打包,但是打包后的 ES6 代码不会转换为 ES5
const path = require('path');
module.exports = {
entry: './a.js',
output: {
path: path.resolve(__dirname, ''),
filename: 'bundle.js'
},
module: {
rules: [
{
test: /\.js$/,
exclude: /node_modules/,
use: {
loader: 'babel-loader',
options: {
presets: ['@babel/preset-env']
}
}
}
]
},
mode: 'none'
};
除使用 babel-loader ,还增加了配置项 options ,该配置与 babel 配置文件基本一致,这里使用 @babel/preset-env
babel-loader 配置项 options 除了正常的 babel 配置文件配置项,还可以开启缓存,通过 `cacheDirrctory:true
` 开启缓存,在初次打包后,如果再次打包,文件没有发生变化,可直接使用初次打包后的缓存文件。
babel 配置如果过于复杂,可单独建立配置文件,如 babel.config.js 。babel-loader 会自动读取使用默认 配置文件配置
file-loader
是一个通用文件处理 loader,在 Webpack 中作用为 处理文件导入地址并替换成其访问地址,并把文件输出到响应位置。
可以处理 js 通过 import 引入的图片
// webpack.config.js
const path = require('path');
module.exports = {
entry: './a.js',
output: {
path: path.resolve(__dirname, ''),
filename: 'bundle.js'
},
module: {
rules: [{
test: /\.jpg$/,
use: 'file-loader'
}]
},
mode: 'none'
};
可以处理 css 导入的 图片
// webpack.config.js
const path = require('path');
module.exports = {
entry: './a.js',
output: {
path: path.resolve(__dirname, ''),
filename: 'bundle.js'
},
module: {
rules: [{
test: /\.css$/,
use: ['style-loader', 'css-loader']
},{
test: /\.jpg$/,
use: 'file-loader'
}]
},
mode: 'none'
url-loader
是 file-loader 的增强版,支持所有 file-loader 功能,还有一个特殊功能
即,可以计算出文件的 base64 编码,在文件体积小于指定值时(单位 byte )可以返回一个 base64 编码的 DataURL 代替访问地址
使用 base64 的好处就是可以减少一次网络请求,提升页面加载速度
// webpack.config.js
const path = require('path');
module.exports = {
entry: './a.js',
output: {
path: path.resolve(__dirname, ''),
filename: 'bundle.js'
},
module: {
rules: [{
test: /\.(jpg|png)$/,
use: {
loader: 'url-loader',
options: {
limit: 1024 * 8, // 图片体积小于 8 kb 的转 base64 编码 URL 直接写入文件
}
}
}]
},
mode: 'none'
};
file-loader 生成文件默认名称为 `[contenthash].[ext]
,``
[contenthash]` 是资源内容 hash 值,
[ext]``
是 文件扩展名
还有 hash 和 name,hash 是根据内容计算 hash,name 是 原始名称
file-loader 默认使用 output.publicPath 作资源访问路径,也可以在 file-loader 的配置项 options 中配置 publicPath 参数,它会覆盖 output.publicPath
// webpack.config.js
const path = require('path');
module.exports = {
entry: './a.js',
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'bundle.js'
},
module: {
rules: [{
test: /\.(jpg|png)$/,
use: {
loader: 'url-loader',
options: {
limit: 1024 * 8,
name: '[name]-[contenthash:8].[ext]',
publicPath: './dist/'
}
}
}]
},
mode: 'none'
};
Webpack 插件 plugin
用于扩展 Webpack 功能,虽然称为插件,但却是 Webpack 的骨干,Webpack 自身也是建立于插件系统上的
Webpack 使用插件只需要在配置项中增加一个 plugins 项即可。plugins 是一个数组,每一个数组元素是一个插件。
const path = require('path');
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
module.exports = {
entry: './a.js',
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'bundle1.js'
},
plugins:[
new CleanWebpackPlugin()
],
mode: 'none'
};
通常 plugins 数组每一个元素都是插件构造函数 New 出来的实例,根据每一个插件的特点,可能会需要向其参数里传递各种配置参数。现在的插件基本都有默认配置,可以免去配置,只有在需要特殊处理时,进行手动配置参数。
clean-webpack-plugin
是一个清除文件的插件,每次打包后,磁盘空间会存有打包后的资源,在此打包时,需要先将本地已有的打包资源清除,减少对磁盘的占用。
使用插件时,首先要安装模块,然后通过 `require()
` 函数导入进配置文件,才能在 plugins 数组中 进行 new 方法。
var path = require('path');
var { CleanWebpackPlugin } = require('clean-webpack-plugin');
module.exports = {
entry: './a.js',
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'bundle.js',
// filename: 'bundle2.js',
},
plugins:[
new CleanWebpackPlugin()
],
mode: 'none'
};
copy-webpack-plugin
用于复制文件,有时,有些本地资源在打包时并没有任何模块使用到,但是却想要把它们放在打包后的资源输出目录,预处理器不适合这么做,插件可以。
var path = require('path');
var CopyPlugin = require("copy-webpack-plugin");
module.exports = {
entry: './a.js',
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'bundle.js'
},
plugins:[
new CopyPlugin({
patterns: [
{ from: path.resolve(__dirname, 'src/img/'), to: path.resolve(__dirname, 'dist/image/') },
],
}),
],
mode: 'none'
};
在使用 copy-webpack-plugin 时,需要传入参数,改参数是一个对象,参数对象的 patterns 属性就是设置从哪复制及复制到哪的,该属性是数组,数组每一项是对象,对象的 from 属性就是 从哪复制,to 属性是复制到哪
Webpack 开发环境配置
开启监听模式
webpack --watch
使用 webpack-dev-server ( DevServer )
webpack-dev-server 安装后可以通过在 Webpack 配置文件里增加与 entry output 同级的 devServer 配置项开启
DevServer 除了支持文件监听和浏览器自动刷新外,还可以做到模块热替换。
DevServer 开启了一个本地 HTTP 服务器,可以请求处理和转发,不需要使用本地文件预览,还支持 Source Map,方便调试
默认使用工程目录下的 index.html 作为首页
Source Map
在进行打包之后,打包出口文件中的代码不再是原始文件中的代码了,并不利于开发调试
如果要在浏览器里直接看到打包前的代码,就需要 source map。
只需要在 Webpack 配置文件中增加一行配置即可
devtool: 'source-map'
在开发环境,可以对 devtool 取值为 eval-cheap-module-source-map,该配置值能保留loader处理前的原始代码信息,而打包速度也不错,是一个较佳的选择。