webpack 对我来说曾经是一个怪物般存在一样,因为它有太多太多的配置项,相反,使用像create-react-App脚手架可以很轻松创建项目,所以有一段时间内,我会尽量避免使用 webpack,因为它看起来既复杂又望而却步
如果你们不习惯从头开始设置 webpack 来使用Babel、TypeScript、Sass、React或Vue,或者不知道为什么要使用 webpack,那么这篇文章是你的最佳选择。就像所有的事情一样,一旦你深入学习,你会发现它并不是那么可怕,只有几个主要的概念需要学习掌握。
如果你是从 webpack 4 升级到 webpack 5,这里有一些注意事项:
现在,大多数网站不再只是单单的由原生JS+纯html编写的,还涉及一些浏览器无法理解的语言,如果项目大,文件多,对应的体积就大。所以要压缩文件和翻译成所有浏览器都能理解的东西,这就是webpack的用武之地。
webpack 可以看做是模块打包器:它做的事情是,分析你的项目结构,找到JAVAScript模块以及其它的一些浏览器不能直接运行的拓展语言(Scss,TypeScript等),并将其打包为合适的格式以供浏览器使用。
对于开发,webpack 还提供了一个开发服务器,它可以在我们保存时动态地更新模块和样式。vue create和create-response-app本质上都依赖于 webpack。
webpac k可以做很多事情,本文只是帮助大家熟悉一些主要概念并进行一些手动的配置。
首先,创建一个目录webpack-tutorial,相关命令如下:
mkdir webpack-tutorial
cd webpack-tutorial
npm init -y // 创建默认的 package.json
安装webpack和webpack-cli:
npm i -D webpack webpack-cli
接着,创建目录 src,并在其里面创建 index.js,内容如下:
console.log('Interesting!')
在项目的根目录中创建一个webpack.config.js。
entry是配置模块的入口,可抽象成输入,Webpack 执行构建的第一步将从入口开始搜寻及递归解析出所有入口依赖的模块。
entry 配置是必填的,若不填则将导致 Webpack 报错退出。这里,我们将src/index.js做为入口点。
const path = require('path')
module.exports = {
entry: {
main: path.resolve(__dirname, './src/index.js'),
},
}
配置 output 选项可以控制 webpack 如何向硬盘写入编译文件。注意,即使可以存在多个入口起点,但只指定一个输出配置。这里指定输出的路径为 'dist':
module.exports = {
/* ... */
output: {
path: path.resolve(__dirname, './dist'),
filename: '[name].bundle.js',
},
}
现在,我们具有构建捆绑包所需的最低配置。在package.json中,我们可以创建一个运行webpack命令的构建脚本。
"scripts": {
"build": "webpack"
}
现在可以运行它了:
npm run build
现在在 dist 目录会生成一个 main.bundle.js 文件
webpack有一个插件接口,这使得它更加灵活。内部webpack代码和第三方扩展使用插件,有一些主要的方法几乎每个webpack项目都会用到。
目前,我们有一个随机的bundle文件,但它对我们还不是很有用。如果需要使用main.bundle.js,就要借助 HTML页面来加载这个 JS 包作为脚本。我们希望HTML文件自动引入这个生成 js 文件,所以我们将使用html-webpack-plugin创建一个HTML模板。
安装一下:
npm i -D html-webpack-plugin
在src文件夹中创建一个template.html文件,这里,我们自定义一个title,内容如下:
src/template.html
<!DOCTYPE html>
<html lang="en">
<head>
<title><%= htmlWebpackPlugin.options.title %></title>
</head>
<body>
<div id="root"></div>
</body>
</html>
创建配置的plugins属性,然后将插件,文件名添加到输出(index.html),并链接到将基于该模板的模板文件。
const path = require('path')
const HtmlWebpackPlugin = require('html-webpack-plugin')
module.exports = {
/* ... */
plugins: [
new HtmlWebpackPlugin({
title: 'webpack Boilerplate',
template: path.resolve(__dirname, './src/template.html'), // template file
filename: 'index.html', // output file
}),
],
}
现在再次运行构建,会看到dist文件夹现在包含一个index.html,里面也自动引入了我们打包好的 js 文件。用浏览器打开 index.html,会在控制台看到 Interesting!。
接着,在index.js中我们动态插入一些 dom 元素到页面中,内容如下:
// Create heading node
const heading = document.createElement('h1')
heading.textContent = 'Interesting!'
// Append heading node to the DOM
const app = document.querySelector('#root')
app.append(heading)
重新构建,在进入 dist 目录下面,用 http-server 运行 html 文件。
http-server
可以在页面上看到,我们注入的 "Interesting!",还会注意到捆绑文件已缩小。
注意:在安装HtmlWebpackPlugin后,你会看到一个DeprecationWarning,因为插件在升级到webpack 5后还没有完全摆脱deprecation警告。
我们还需要设置clean-webpack-plugin,在每次构建后清除dist文件夹中的所有内容。这对于确保不遗留任何旧数据很重要。
clean-webpack-plugin-删除/清理构建文件夹
const path = require('path')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const {CleanWebpackPlugin} = require('clean-webpack-plugin')
module.exports = {
/* ... */
plugins: [
/* ... */
new CleanWebpackPlugin(),
],
}
webpack 使用 loader 预处理一些加载的文件,如 js 文件、静态资源(如图像和CSS样式)和编译器(如TypeScript和Babel)。webpack 5也有一些内置的资产加载器。
在我们的项目中,有一个HTML文件,该文件可以加载并引入一些 JS ,但实际上并没有执行任何操作。那么这个webpack配置要做的主要事情是什么?
Babel是一个工具,可让使用最新的 JS 语法。
建立一个规则来检查项目中(node_modules之外)的任何.js文件,并使用babel-loader进行转换。Babel 还有一些其他的依赖项:
npm i -D babel-loader @babel/core @babel/preset-env @babel/preset-env @babel/plugin-proposal-class-properties
webpack.config.js
module.exports = {
/* ... */
module: {
rules: [
// JavaScript
{
test: /.js$/,
exclude: /node_modules/,
use: ['babel-loader'],
},
],
},
}
如果是 TypeScript 项目,使用的是typescript-loader而不是babel-loader。
现在Babel已经设置好了,但是我们的Babel插件还没有。可以在index.js中添加一些新的语法来证明它还不能正常工作。
// 创建没有构造函数的类属性
class Game {
name = 'Violin Charades'
}
const myGame = new Game()
// 创建 p 节点
const p = document.createElement('p')
p.textContent = `I like ${myGame.name}.`
const heading = document.createElement('h1')
heading.textContent = 'Interesting!'
const app = document.querySelector('#root')
app.append(heading, p)
要解决这个问题,只需在项目的根目录中创建一个.babelrc文件。可以使用preset-env和plugin-proposal-class-properties添加更多默认值。
{
"presets": ["@babel/preset-env"],
"plugins": ["@babel/plugin-proposal-class-properties"]
}
现在运行npm run build 一切准备就绪。
假设我们需要引用一张图片并直接导入到 JS 文件中,这样是无法正常工作的。为了演示,创建 src/ images 并向其中添加图像,然后尝试将其导入到index.js文件中。
src/index.js
import example from './images/example.png'
/* ... */
运行构建时,再次看到错误:
webpack有一些内置的asset modules ,可用于静态资源。对于图像类型,我们将使用asset/resource,注意,这里是一个type,而不是loader。
webpack.config.js
module.exports = {
/* ... */
module: {
rules: [
// Images
{
test: /.(?:ico|gif|png|jpg|jpeg)$/i,
type: 'asset/resource',
},
],
},
}
构建后,可以在dist文件夹查看。
webpack 还有一个asset module ,可以使用asset/inline内联某些数据,例如svgs和字体。
src/index.js
import example from './images/example.svg'
/* ... */
webpack.config.js
module.exports = {
/* ... */
module: {
rules: [
// Fonts and SVGs
{
test: /.(woff(2)?|eot|ttf|otf|svg|)$/,
type: 'asset/inline',
},
],
},
}
同样的需要使用 style loader才能在脚本中执行类似import 'file.css'的操作。
现在很多人都在使用CSS-in-JS、styled-components和其他工具来将样式引入到他们的 JS 应用程序中。
当网站只有一个 CSS 文件,仅能够加载一个CSS文件就足够了。但如果想使用PostCSS,为了能在任何浏览器中使用所有最新的CSS特性。或者想使用Sass, CSS预处理器,那就需要使用其它的 loader 处理。
我想使用这三种方法——在Sass中编写,在PostCSS中处理,以及编译到CSS。这需要引入一些加载器和依赖项。
npm i -D sass-loader postcss-loader css-loader style-loader postcss-preset-env node-sass
就像Babel一样,PostCSS 也需要一个配置文件 postcss.config.js,在根目录中创建它,并输入以下内容:
postcss.config.js
module.exports = {
plugins: {
'postcss-preset-env': {
browsers: 'last 2 versions',
},
},
}
为了测试 Sass 和 PostCSS 是否正常工作,创建src/styles/main.scss,并输入以下内容:
src/styles/main.scss
$font-size: 1rem;
$font-color: lch(53 105 40);
html {
font-size: $font-size;
color: $font-color;
}
现在,将文件导入index.js并添加四个 loader 。它们从最后编译到第一个,因此列表中最后一个是sass-loader,因为需要编译,然后是PostCSS,然后是CSS,最后是style-loader,它将CSS注入到DOM 中。
src/index.js
import './styles/main.scss'
/* ... */
webpack.config.js
module.exports = {
/* ... */
module: {
rules: [
// CSS, PostCSS, and Sass
{
test: /.(scss|css)$/,
use: ['style-loader', 'css-loader', 'postcss-loader', 'sass-loader'],
},
],
},
}
现在,重新构建时,项目中已经应用了Sass和PostCSS。
每次进行更新时都要运行npm run build,站点越大,构建所需的时间就越长,这样就十分的烦琐。为此可以为 webpack 设置两种配置:
开发模式下是在内存中运行所有内容,而不是构建一个dist文件,需要安装 webpack-dev-server
npm i -D webpack-dev-server
出于演示目的,我们可以仅将开发配置添加到正在构建的当前webpack.config.js文件中并对其进行测试。但是,我们开发一般要创建两个配置文件:一个生产环境用的 mode: production,一个开发环境用的mode: development。
const webpack = require('webpack')
module.exports = {
/* ... */
mode: 'development',
devServer: {
historyApiFallback: true,
contentBase: path.resolve(__dirname, './dist'),
open: true,
compress: true,
hot: true,
port: 8080,
},
plugins: [
/* ... */
// Only update what has changed on hot reload
new webpack.HotModuleReplacementPlugin(),
],
})
我们添加mode: development,并创建devServer属性,其中,默认端口将为8080,自动打开浏览器窗口,并使用hot-module-placement,这需要webpack.HotModuleReplacementPlugin插件。这样模块执行更新而无需完全重新加载页面-因此,如果你更新某些样式,则这些样式将发生变化,并且不用重新加载整个 JS ,大大加快了开发速度。
现在,可以使用webpack serve命令来启动项目。
package.json
"scripts": {
"start": "webpack serve"
}
npm start
运行此命令时,将在浏览器中自动弹出一个指向localhost:8080的链接。现在,您可以更新Sass和JavaScript,并观看其动态更新。
我用 Babel,Sass,PostCSS,生产优化和开发服务器创建了可用于生产的webpack 5样板,其中包含本文的所有内容,但会涉及更多细节。从这里,可以轻松设置React,Vue,Typescript或其他任何您想要的东西。
项目地址:webpack 5 boilerplate
看看它,摆弄它,享受它!
作者:Tania Rascia 译者:前端小智 来源:taniarascia
原文:https://www.taniarascia.com/how-to-use-webpack/