当前位置 博文首页 > 毛小星的博客:Webpack的简单使用
Webpack 是一个模块化的打包工具,它可以将我们的模块化代码都打包到一个 js 文件中。而对于我们在不同环境中有兼容性问题的代码,我们就可以通过模块加载器(loader)去解决。而且 Webpack 具备代码拆分的能力(Code Splitting),它可以将应用中的代码根据我们的要求去打包,我们可以将应用初次加载的代码打包到一起,其他模块的代码打包到一起,实现渐进式加载。这样我们就可以避免将所有代码都打包到一起,而引起打包文件过大,导致页面加载速度过慢的问题。Webpack 还支持将其他类型的文件打包到 js 文件中。Webpack 是目前最主流的打包工具,它的功能非常强大,覆盖了绝大部分的现代 web 应用开发过程,下面我们来看看如何使用它来构建我们自己的项目。
我们想在项目使用 Webpack,首先我们需要在项目中安装它,我们要初始化一个 package.json,然后将 webpack 和 webpack-cli 作为我们的开发依赖引入
yarn init -y // 或者 npm init -y
yarn add webpack webpack-cli --dev
你可以使用 npm 去下载依赖,下面我都使用 yarn 去安装依赖,如果你想了解 yarn,可以去yarn 官网看一下。
安装之后,我们可以使用下面的命令查看 Webpack 的版本
yarn webpack --version
如果能够正常输出版本,那就证明我们安装成功了。
接下来我们就可以使用 webpack 去打包我们的文件。webpack 默认会从 src 下面的 index.js 开始打包,将所有的 js 文件导打包,之后我们所有的 js 文件就会被打包到 dist 目录下的 main.js 文件中。
Webpack4 之后的版本支持零配置的方式打包,Webpack 默认约定将 “src/index.js” 作为默认入口文件打包到 “dist/mian.js” 中,这种零配置的方式虽然好,但是有时候我们想自定义这个入口文件和输出文件。Webpack 中提供了一种方式,我们可以在项目的根目录下创建一个 webpack.config.js 的文件,这个文件是一个运行在 node.js 中的文件,我们需要按照 commonjs 的规范去编写我们的代码。例如我们想将 src 目录下的 main.js 作为入口文件,将 dist 目录作为输出目录,将 bundle.js 作为输出文件,我们可以做如下配置
const path = require('path')
module.exports = {
entry: './src/main.js', // 入口文件
output: {
filename: 'bundle.js', // 输出文件
path: path.join(__dirname, 'dist') // 输出目录
}
}
接下来我们运行打包命令,就可以将我们的项目打包到 dist 目录下的 bundle.js 文件中去
yarn webpack
webpack 的打包会默认开启生产模式,它会自动帮助我们加载很多插件,去压缩我们的代码,但是这样肯定是不太利于我们去开发的
我们可以通过命令行去指定以什么模式去打包
yarn webpack --mode development
这样我们就以开发模式去打包我们的代码
可以看到开发模式的代码多了很多注释,还会添加一些调试过程中的辅助,当然我们也可以在配置文件中去配置这个模式
const path = require('path')
module.exports = {
// 这个属性有三种取值,分别是 production、development 和 none。
// 1. 生产模式下,Webpack 会自动优化打包结果;
// 2. 开发模式下,Webpack 会自动优化打包速度,添加一些调试过程中的辅助;
// 3. None 模式下,Webpack 就是运行最原始的打包,不做任何额外处理;
mode: 'development',
entry: './src/main.js',
output: {
filename: 'bundle.js',
path: path.join(__dirname, 'dist')
}
}
loader 在 Webpack 中扮演着至关重要的角色,下面我们来看看 Webpack 官方文档对 loader 解释
loader 用于对模块的源代码进行转换。
loader 可以使你在 import 或"加载"模块时预处理文件。因此,loader 类似于其他构建工具中“任务(task)”,并提供了处理前端构建步骤的强大方法。
loader 可以将文件从不同的语言(如 TypeScript)转换为 JavaScript,或将内联图像转换为 data URL。
loader 甚至允许你直接在 JavaScript 模块中 import CSS文件!
Webpack 的 loader 非常强大,你可以通过 loader 在你的项目中去加载任何资源!
其实 Webpack 内部默认是有 js loader 的,但是它只能处理 js 文件,所以当我们去在模块中引入 css 文件的时候,Webpack 就会报出错误,这个时候 css-loader 就会体现它的作用,它会帮助我们去处理 css 文件,并将这些 css 文件生成一个 js 模块打包到输出的 js 文件中,然后 style-loader 会将这个模块通过 style 标签的形式引入到页面中去
// 安装 css-loader
yarn add css-loader style-loader --dev
安装好 css-loader 和 style-loader 之后我们要在 Webpack 的配置文件中做出如下配置
const path = require('path')
module.exports = {
entry: './src/main.js', // 入口文件
output: {
filename: 'bundle.js', // 输出文件
path: path.join(__dirname, 'dist') // 输出目录
},
module: {
rules: [
{
test: /.css$/,
use: [
'style-loader',
'css-loader',
]
},
]
}
}
需要注意的是,loader 的加载默认是从后向前加载,所以 css-loader 要放在后面,因为我们要先将 css 转换成 js 模块,才能正常打包
我们在实际开发中,不光要写一些逻辑代码,往往还要引入一些文件去帮助我们去优化项目,例如我们经常会在项目中引入一些图片文件。Webpack 为我们提供了 file-loader 去帮助我们处理这些文件
// 安装 file-loader
yarn add file-loader --dev
安装好 file-loader 之后,我们就可以在配置文件中进行一些配置
module: {
rules: [
{
test: /\.(png|jpe?g|gif)$/,
use: "file-loader"
},
]
}
配置好之后,我们再去打包,dist 目录下就会多出打包后的图片,但是如果你这时候打开项目地址的话,你会发现图片加载不进来,这是因为 Webpack 会默认加载项目的根目录,而你的图片是在 dist 目录下的,所以这时候需要你做出一些配置去让它默认加载 dist 目录下的图片,做法就是在输出文件的时候指定根目录
const path = require('path')
module.exports = {
mode: 'none',
entry: './src/main.js',
output: {
filename: 'bundle.js',
path: path.join(__dirname, 'dist'),
publicPath: 'dist/' // 指定 dist 为根目录
},
module: {
rules: [
{
test: /.png$/,
use: 'file-loader'
}
]
}
}
除了像 file-loader 这种通过拷贝物理文件方式去处理文件资源外,还有像通过 Data URLs 这种方式去表述文件,Data URLs 是一种特殊的 url 协议,它可以通过 url 去表述文件,正常我们都是通过一个 url 去服务器访问这个资源文件,而 Data URLs 本身就会包含这个文件的所有内容,从而不用去服务器访问这个资源,减少了 http 请求。
例如下面就是一个 Data URLs 表示的文件类型
data:text/html;chartset=UTF-8,<h1>html content</h1>
上面这个 url 就表示这是一个 html 类型的文件,编码是 utf-8,文件内容是一个 h1 标签
当我们的资源是图片的时候,就会被解析成 base64 编码,浏览器依旧能根据这个编码解析出这个图片的内容
这样我们就可以通过 url-loader 去加载任何我们想要加载的文件
// 安装 url-loader
yarn add url-loader --dev
然后我们就可以不用使用 file-loader 去处理我们的文件了,而去使用 url-loader 去加载我们的文件
module: {
rules: [
{
test: /.png$/,
use: {
loader: 'url-loader',
options: {
limit: 10 * 1024 // 超出 10 KB 使用 file-loader
}
}
}
]
}
使用 url-loader 的方式去处理我们的文件当然会显得很方便,但是只是适合比较小的文件,如果文件过大的话,会影响我们的打包速度,所以我们可以为文件设置一个最大限度,如果文件超过这个临界值的话,还是使用传统的 file-loader 去通过物理拷贝的方式求处理文件。值的注意的是,如果我们通过限制文件大小的方式去处理文件的话,即使你的配置中没有 file-loader,你还是需要下载 file-loader,这样 Webpack 才会正常工作。
由于 Webpack 默认就能处理我们代码中的 import 和 export,所以很多人可能会认为 Webpack 会自动编译 es6 的代码。实则不然,因为模块打包需要,所以 Webpack 才会处理 import 和 export,而其他的 es6 代码,Webpack并不能去处理它们,babel-loader 能够帮助我们去处理这些 es6 的代码。
// 安装 babel-loader
yarn add babel-loader @babel/core @babel-preset-env --dev
我们在安装 babel-loader 的时候还需要安装 @babel/core @babel-preset-env 这两个插件。
@babel/core是babel的核心库,所有的核心Api都在这个库里,这些Api供babel-loader调用。
@babel-preset-env 是用来转换 es6 中的具体特性的一个插件集合
下面我们来看看怎么配置 babel-loader
module: {
rules: [
{
test: /.js$/,
use: {
loader: 'babel-loader',
options: {
presets: ['@babel/preset-env']
}
}
},
]
}
下面我们来来自己开发一个 loader,然后通过这个过程我们再来深入了解 loader 的工作原理。下面我们来开发一个 markdown-loader,这样我们就能将 markdown 转换为一个字符串然后渲染到浏览器中去。
首先我们创建一个 markdown-loader.js 的文件,用来开发我们的 loader。每一个 Webpack loader 都需要导出一个函数,这个函数就是对整个资源的处理过程,这个函数中有一个参数,这个参数是一个入口,就是我们需要处理的资源,出口就是我们需要返回的内容,值得注意的是,这个返回内容必须是一段 javaScript 代码,否则 Webpack 就会报错。
module.exports = source => { // source 就是我们需要处理的资源
return 'console.log("hello loader")'
}
接下来我们就可以在配置文件中引入这个 loader
module: {
rules: [
{
test: /.md$/,
use: [
'./markdown-loader'
]
}
]
}
下面我们需要引入一个去解析 markdown 文件的插件 marked
yarn add marked --dev
下面我们在 loader 中去处理 markdown 资源
const marked = require('marked')
module.exports = source => {
const html = marked(source)
return `export default ${JSON.stringify(html)}`
}
这样我们就完成了一个处理 markdown 类型文件的 loader,当然除了上面这种处理方式,我们还有其他的处理方式。因为 loader 支持链式调用,所以我们可以直接返回一个 html 字符串,然后将这个 html 字符串交给 html-loader 去处理
// 下载 html-loader
yarn add html-loader --dev
我们修改一下 loader 中对 markdown 资源的处理
const marked = require('marked')
module.exports = source => {
const html = marked(source)
// 返回 html 字符串交给下一个 loader 处理
return html
}
之后我们在配置文件中引入 html-loader 去处理 markdown-loader 返回的内容,值得注意的是 loader 的执行顺序,use 数组后面的先执行,所以我们要将 html-loader 放在数组的前面。
module: {
rules: [
{
test: /.md$/,
use: [
'html-loader', // 处理 markdown-loader 返回的内容
'./markdown-loader'
]
}
]
}
插件机制是 Webpack 的另外一个核心机制,目的是增强 Webpack 的自动化能力。loader 专注于实现各种资源的加载,plugin 是为了解决其他的自动化工作,例如 plugin 能够帮助我们去清除上一个打包的打包文件或者能够帮助我们压缩我们的代码,plugin 几乎能够帮助我们去实现大多数的前端工程化工作,下面我们来了解一些 Webpack 中比较常用的插件。
如果你自己创建一个新项目的话,你打包过一个文件目录,当你去修改了一些配置而再去打包的话,你的打包目录下就会有之前遗留的文件,这显然不是我们想要的结果,而 clean-webpack-plugin 就能够帮助在打包之前清除上一次的打包文件。
yarn add clean-webpack-plugin --dev
下面我们来配置一下这个插件
const path = require('path')
const { CleanWebpackPlugin } = require('clean-webpack-plugin')
module.exports = {
mode: 'none',
entry: './src/main.js',
output: {
filename: 'bundle.js',
path: path.join(__dirname, 'dist'),
publicPath: 'dist/'
},
plugins: [
new CleanWebpackPlugin()
]
}
上面的配置文件中,我们引入了 clean-webpack-plugin,然后结构出 CleanWebpackPlugin 这个插件,之后我们在 plugins 中创建一个数组,然后在数组成员中执行这个插件,这样下次我们打包代码之前就会自动清除 dist 目录下的文件。
我们在开发的时候,需要将打包生成的 js 文件引入到 src 目录下的 index.html,这种硬编码的方式显然是不好的,因为如果你使用的一些 hash-chunk 这样的插件的话,打包出来的文件名称每一次都不一样,你就需要每一次都手动去引入这个文件,这种做法显然会显得比较"笨",html-webpack-plugin 就能帮助我们去解决这个问题。
yarn add html-webpack-plugin --dev
下面我们来看看它的基础配置
const path = require('path')
const HtmlWebpackPlugin = require('html-webpack-plugin')
module.exports = {
mode: 'none',
entry: './src/main.js',
output: {
filename: 'bundle.js',
path: path.join(__dirname, 'dist'),
// publicPath: 'dist/'
},
plugins: [
new HtmlWebpackPlugin()