文章目录
2.1 什么是 Loader?
如果想要打包一个 png 图片,webpack 是不会知道要怎么打包的,因为它默认只知道怎么打包 js 文件,因此就需要在配置文件中的 module
配置项告诉 webpack 怎么打包 png 模块
loader 的执行有顺序,是从后往前执行,所以对css打包时,需把 style-loader 写在 css-loader 前面,使 css-loader 先执行
const path = require('path')
module.exports = {
mode: 'development', // 打包模式,默认是 production
entry: {
main: './src/index.js', // 打包的入口文件,即打包 index.js 文件
},
module: { // 打包的模块配置
rules: [{ // 打包规则
test: /\.png$/, // 如果遇到 .png 结尾的文件
use: { // 使用
loader: 'file-loader' // 使用 file-loader 来打包
}
}]
},
output: {
filename: 'bundle.js', // 打包后的文件名
path: path.resolve(__dirname, 'dist'), // 打包后的文件放在哪个文件夹内,这里必须是绝对路径
}
}
通过以上例子,我们就知道了 loader
其实是一个 webpack 的打包方案,它知道对于某个特定的文件,webpack应该如何打包。
2.2 使用loader打包静态资源文件
2.2.1 打包图片资源
使用 file-loader
打包的 png 图片默认会重新起一个随机的文件名放到配置的出口文件夹下,我们想要不改变文件名打包就需要对 loader 进一步做配置:
module: {
rules: [{
test: /\.(png|jpg|gif)$/,
use: {
loader: 'file-loader',
options: {
// 这种配置语法叫做 placeholder, 占位符
name: '[name].[ext]' // 打包后的文件名以及后缀不变
}
}
}]
}
关于 file-loader 的更多信息,请参考官网
除了 file-loader 外,使用 url-loader
也能完成相同的功能,file-loader能做到的事,url-loader 都能做,对于图片文件来说,file-loader 是将图片打包到出口文件夹下指定文件夹内,通过外部引入的方式使用图片,而 url-loader 是通过将图片转成 base64
字符串,直接使用,不会单独生成一个图片文件。
怎么选择file-loader 和 url-loader 打包图片:
- file-loader 打包图片是引入外部文件,会形成一次 http 请求,当图片资源很大的时候,使用file-loader
- url-loader 打包图片会把图片转成 base64 字符串,虽然不会去进行一次 http 请求,但如果图片很大,js 的加载就会占用很多时间,页面很久才会展示出来
所以:当图片资源很大的时候使用 file-loader,图片资源很小的时候使用 url-loader
我们一般选用url-loader,因为它能设置条件来动态选择是单独打包文件,还是转成base64到出口文件中,配置如下:
module: {
rules: [{
test: /\.(png|jpg|gif)$/,
use: {
loader: 'url-loader',
options: {
// 这种配置语法叫做 placeholder, 占位符
name: '[name].[ext]', // 打包后的文件名以及后缀不变
outputPath: 'images/', // 打包后的图片文件会放到出口文件夹中的 images 文件夹下
limit: 2048 // 当图片大小大于 2048bit,即大于2Kb时,url-loader就不会将图片转成base64字符串,而是单独生成一个文件,打包到outputPath指定文件夹内
}
}
}]
}
2.2.2 打包CSS
打包CSS,需要两个loader,一个是 style-loader
,另一个是 css-loader
,css-loader可以分析出css文件间的关系,例如使用 @import
时几个css间的引用关系,css-loader 会把他们打包成一个css文件;style-loader在得到 css-loader 生成的 css 文件后, 会把这个文件挂载到页面的 <head></head>
部分。
{
test: /\.css$/,
use: ['style-loader', 'css-loader']
}
同理,如果使用 sass
、less
、stylus
写样式时,也要引入相应的 loader
使用sass,需要安装 sass-loader
和 node-sass
{
test: /\.scss$/,
use: ['style-loader', 'css-loader', 'sass-loader']
}
使用less,需安装 less-loader
{
test: /\.less$/,
use: ''less-loader''
}
使用stylus,需安装stylus
和 stylus-loader
{
test: /\.styl$/,
use: ['style-loader', 'css-loader', 'stylus-loader']
}
当我们使用一些 CSS3 的样式时,一般会在样式前面加厂商前缀,这时我们可以使用 postcss-loader
来帮助我们,使webpack打包时自动加上厂商前缀,使用 postcss-loader 需在根目录下创建一个 postcss.config.js
的配置文件,打包时,postcss-loader 会自动的在这个配置文件中读取配置信息,具体配置如下:
我们先安装所需的依赖包:
# postcss-loader
npm i postcss-loader -D
# postcss-loader 所需的插件
npm i autoprefixer -D
postcss.config.js 配置文件:
module.exports = {
plugins: [
require('autoprefixer') // 使用 autoprefixer 插件
]
}
webpack.config.js 配置文件中 module 配置
// 没有在 css 中使用 @import 语法时
module: {
rules: [{
test: /\.css$/,
use: ['style-loader', 'postcss-loader']
}]
}
// 在 css 中使用了 @import 语法时
module: {
rules: [{
test: /\.css$/,
use: [
'style-loader',
{
loader: 'css-loader',
options: {
importLoaders: 1 // 下面有多少个loader,就写多少,这里下面只有一个postcss-loader,所以只写1
}
},
'postcss-loader'
]
}]
}
使用postcss-loader 后,浏览器样式截图:
2.3 模块化CSS
当使用模块化创建项目的时候,我们会遇到这种情况:一个模块里的css会作用到另一个模块,相当于写了一个全局样式,但很多时候我们只想让每个模块里的css作用于本模块,于是有了CSS模块化的概念。
在webpack配置中,只需在css-loader
的 options 中配置 modules: true
,在引入模块的 js 文件中,添加class的时候也做修改
webpack.config.js:
module: {
rules: [{
test: /\.css$/,
use: [
'style-loader',
{
loader: 'css-loader',
options: {
importLoaders: 1,
module: true
}
},
'postcss-loader'
]
}]
}
引入模块的js文件:
import bg from './bg.png';
import style from'./index.css';
import Header from './header.js';
Header();
var img = new Image();
img.src = bg;
img.classList.add(style.bg);
var root = document.getElementById('root');
root.append(img);
P.S.:如果需使用字体文件时,只需要使用 file-loader 即可,不用做太多配置
具体各种文件类型的打包可以查看官网
2.4 插件
plugin 可以在webpack运行到某个时刻,帮助webpack做一些特定的事情,类似vue、react的生命周期函数
2.4.1 html-webpack-plugin
之前我们打包的时候,调用出口文件(我这里是 bundle.js)的 index.html 文件总需要我们自己在出口文件夹(/dist)下创建,手动引入 bundle.js。webpack 的插件(html-webpack-plugin)可以很好的帮助我们免去这一步。
html-webpack-plugin
会在打包结束后,自动生成一个html文件,并把打包生成的js自动引入到这个HTML文件中,但在没做任何配置的时候,它并不会对这个 HTML 文件做任何的操作(不会创建dom元素,即不会创建js中元素需挂载的目标),我们可以对plugins做配置来让它创建这个dom元素,配置如下:
// webpack.config.js
// 首先引入 html-webpack-plugin
const HtmlWebpackPlugin = require('html-webpack-plugin');
// 在配置项中增加
plugins: [new HtmlWebpackPlugin({
template: 'src/index.html' // 以自建的src/index.html为模板创建 html 文件,只需在模板文件中创建dom元素
})]
注意:
一般来说,我们打包后的出口文件夹里的东西并不会被删除,我们做第二次打包时,只会覆盖出口文件夹的内容,如果你修改了配置文件中出口文件 名,那么就会出现两个出口文件(一个是修改前的,一个是修改后的),这时候我们可以借助 clean-webpack-plugin
将我们的出口文件夹清空,再打包
plugins: [
new HtmlWebpackPlugin({
template: 'src/index.html'
}),
new CleanWebpackPlugin() // 在打包前清空出口文件夹
]
2.5 entry 和 output
entry入口文件我们可以将它写成一个字符串
entry: './src/index.js'
如果没有在output中指明出口文件filename
,那么默认的出口文件名会是 main.js
。实际上,入口文件可以是一个对象,出口文件的默认文件名就是这个对象的键,即,上面的字符串方式我们可以写成:
entry: {
main: './src/index.js'
}
如果我们想要将入口文件打包成两个不同文件名的文件,只需在entry中多加一个键值对,再再output中配置一下filename,即可:
entry: {
main: './src/index.js',
sub: './src/index.js'
},
output: {
filename: '[name].js' // 通过占位符 name 打包生成不同的文件名的文件
}
就可以生成两个文件了。
当使用了 html-webpack-plugin
插件,打包生成的出口文件 index.html 中就会自动引入打包好的js文件,如果我们想在这前面加个前缀,可以通过配置 output 的 publicPath
来实现:
output: {
publicPath: 'https://www.cdn.com.cn',
filename: 'bundle.js', // 打包后的文件名
path: path.resolve(__dirname, 'dist'), // 打包后的文件放在哪个文件夹内,这里必须是绝对路径
}
2.6 SourceMap 配置
SourceMap 是一个映射关系,他知道出口文件中打包的代码对应的是源代码文件的哪一行
source-map: 会自动生成一个.map文件,inline: 就会把生成的 .map 文件合并到出口文件中,cheap: 只提示多少行出错,不提示多少列,module: 不知管业务代码,也管 loader 的报错
开发者模式mode: 'development'
下,是默认打开 SourceMap 的,我们可以加个配置项,关闭掉 SourceMap:
module.exports = {
mode: 'development', // 打包模式,默认是 production
devtool: 'none'
}
经实践,想要得到较好的报错提示信息,且打包速度也较快,可以将 SourceMap 设置成 cheap-module-eval-source-map
module.exports = {
mode: 'development', // 打包模式,默认是 production
devtool: 'cheap-module-eval-source-map'
}