在上篇 《学习 Webpack5 之路(基础篇)》中介绍了 Webpack 是什么,为何选择 Webpack,Webpack 的基本概念介绍 3 个问题。javascript
本篇将从实践出发,在第一章节《基础配置》中使用 webpack 搭建一个基础的支持模块化开发的项目,在第二章节《进阶配置》中使用 webpack 搭建一个 SASS + TS + React 的项目。css
本文依赖的 webpack 版本信息以下:html
接下来一块儿配置一个基础的 Webpack。java
将支持如下功能:node
想直接看配置的同窗 -> 本文源码地址:webpack Demo0react
新建一个空项目:webpack
// 新建 webpack-demo 文件夹
mkdir webpack-demo
// 进入 webpack-demo 目录
cd ./webpack-demo
// 初始化项目
npm init -y
复制代码
新建 2 个 js 文件,并进行模块化开发:git
// 进入项目目录
cd ./webpack-demo
// 建立 src 文件夹
mkdir src
// 建立 js文件
touch index.js
touch hello.js
复制代码
index.js:es6
// index.js
import './hello.js'
console.log('index')
复制代码
hello.js:github
// hello.js
console.log('hello webpack')
复制代码
项目结构以下:
- src
- index.js
- hello.js
- package.json
- node_modules
复制代码
Node 须要是最新版本,推荐使用 nvm 来管理 Node 版本。
将 Node.js 更新到最新版本,也有助于提升性能。除此以外,将你的 package 管理工具(例如
npm
或者yarn
)更新到最新版本,也有助于提升性能。较新的版本可以创建更高效的模块树以及提升解析速度。
我安装的版本信息以下:
npm install webpack webpack-cli --save-dev
复制代码
development(开发环境) 和 production(生产环境) 这两个环境下的构建目标存在着巨大差别。为代码清晰简明,为每一个环境编写彼此独立的 webpack 配置。
// 进入项目目录
cd ./webpack-demo
// 建立 config 目录
mkdir config
// 进入 config 目录
cd ./config
// 建立通用环境配置文件
touch webpack.common.js
// 建立开发环境配置文件
touch webpack.dev.js
// 建立生产环境配置文件
touch webpack.prod.js
复制代码
使用 webpack-marge 合并通用配置和特定环境配置。
安装 webpack-merge:
npm i webpack-merge -D
复制代码
通用环境配置:
// webpack.common.js
module.exports = {} // 暂不添加配置
复制代码
开发环境配置:
// webpack.dev.js
const { merge } = require('webpack-merge')
const common = require('./webpack.common')
module.exports = merge(common, {}) // 暂不添加配置
复制代码
生产环境配置:
// webpack.prod.js
const { merge } = require('webpack-merge')
const common = require('./webpack.common')
module.exports = merge(common, {}) // 暂不添加配置
复制代码
项目结构以下:
- config
- webpack.common.js
- webpack.dev.js
- webpack.prod.js
- src
- index.js
- hello.js
- package.json
- node_modules
复制代码
入口起点(entry point) 指示 webpack 应该使用哪一个模块,来做为构建其内部 依赖图(dependency graph) 的开始。进入入口起点后,webpack 会找出有哪些模块和库是入口起点(直接和间接)依赖的。
在本例中,使用 src/index.js 做为项目入口,webpack 以 src/index.js 为起点,查找全部依赖的模块。
修改 webpack.commom.js:
module.exports = merge(common, {
// 入口
entry: {
index: './src/index.js',
},
})
复制代码
output 属性告诉 webpack 在哪里输出它所建立的 bundle,以及如何命名这些文件。
生产环境的 output 须要经过 contenthash 值来区分版本和变更,可达到清缓存的效果,而本地环境为了构建效率,则不引人 contenthash。
新增 paths.js,封装路径方法:
const fs = require('fs')
const path = require('path')
const appDirectory = fs.realpathSync(process.cwd());
const resolveApp = relativePath => path.resolve(appDirectory, relativePath);
module.exports = {
resolveApp,
appPublic: resolveApp('public'),
appHtml: resolveApp('public/index.html'),
appSrc: resolveApp('src'),
appDist: resolveApp('dist'),
appTsConfig: resolveApp('tsconfig.json'),
}
复制代码
修改开发环境配置文件 webpack.dev.js:
module.exports = merge(common, {
// 输出
output: {
// bundle 文件名称
filename: '[name].bundle.js',
// bundle 文件路径
path: resolveApp('dist'),
// 编译前清除目录
clean: true
},
})
复制代码
修改生产环境配置文件 webpack.prod.js:
module.exports = merge(common, {
// 输出
output: {
// bundle 文件名称 【只有这里和开发环境不同】
filename: '[name].[contenthash].bundle.js',
// bundle 文件路径
path: resolveApp('dist'),
// 编译前清除目录
clean: true
},
})
复制代码
上述 filename 的占位符解释以下:
[name]
- chunk name(例如 [name].js
-> app.js
)。若是 chunk 没有名称,则会使用其 id 做为名称[contenthash]
- 输出文件内容的 md4-hash(例如 [contenthash].js
-> 4ea6ff1de66c537eb9b2.js
)经过 mode
配置选项,告知 webpack 使用相应模式的内置优化。
选项 | 描述 |
---|---|
development |
会将 DefinePlugin 中 process.env.NODE_ENV 的值设置为 development . 为模块和 chunk 启用有效的名。 |
production |
会将 DefinePlugin 中 process.env.NODE_ENV 的值设置为 production 。为模块和 chunk 启用肯定性的混淆名称,FlagDependencyUsagePlugin ,FlagIncludedChunksPlugin ,ModuleConcatenationPlugin ,NoEmitOnErrorsPlugin 和 TerserPlugin 。 |
修改开发环境配置文件 webpack.dev.js:
module.exports = merge(common, {
// 开发模式
mode: 'development',
})
复制代码
修改生产环境配置文件 webpack.prod.js:
module.exports = merge(common, {
// 生产模式
mode: 'production',
})
复制代码
当 webpack 打包源代码时,可能会很难追踪到 error 和 warning 在源代码中的原始位置。
为了更容易地追踪 error 和 warning,JavaScript 提供了 source maps 功能,能够将编译后的代码映射回原始源代码。
修改开发环境配置文件 webpack.dev.js:
module.exports = merge(common, {
// 开发工具,开启 source map,编译调试
devtool: 'eval-cheap-module-source-map',
})
复制代码
source map 有许多 可用选项。本例选择的是 eval-cheap-module-source-map
注:为加快生产环境打包速度,不为生产环境配置 devtool。
完成上述配置后,能够经过 npx webpack --config config/webpack.prod.js
打包编译。
编译后,会生成这样的目录结构:
npx webpack --config config/webpack.prod.js
后仅生成了 bundle.js,咱们还须要一个 HTML5 文件,用来动态引入打包生成的 bundle 文件。
引入 HtmlWebpackPlugin
插件,生成一个 HTML5 文件, 其中包括使用 script
标签的 body 中的全部 webpack 包。
安装:
npm install --save-dev html-webpack-plugin
复制代码
修改通用环境配置文件 webpack.commom.js:
module.exports = {
plugins: [
// 生成html,自动引入全部bundle
new HtmlWebpackPlugin({
title: 'release_v0',
}),
],
}
复制代码
从新 webpack 编译 npx webpack --config config/webpack.prod.js
,生成的目录结构以下:
新生成了 index.html,动态引入了 bundle.js 文件:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>release_v0</title>
<meta name="viewport" content="width=device-width,initial-scale=1" />
<script defer="defer" src="index.468d741515bfc390d1ac.bundle.js"></script>
</head>
<body></body>
</html>
复制代码
在每次编译代码时,手动运行 npx webpack --config config/webpack.prod.js
会显得很麻烦,webpack-dev-server 帮助咱们在代码发生变化后自动编译代码。
webpack-dev-server
提供了一个基本的 web server,而且具备实时从新加载功能。
webpack-dev-server
默认配置 conpress: true
,为每一个静态文件开启 gzip compression。
安装:
npm install --save-dev webpack-dev-server
复制代码
修改开发环境配置文件 webpack.dev.js:
module.exports = merge(common, {
devServer: {
// 告诉服务器从哪里提供内容,只有在你想要提供静态文件时才须要。
contentBase: './dist',
},
})
复制代码
完成上述配置后,能够经过 npx webpack serve --open --config config/webpack.dev.js
实时编译。
效果如图:
上述配置文件完成后,优化 webpack 的实时编译、打包编译指令。
经过 cross-env 配置环境变量,区分开发环境和生产环境。
安装:
npm install --save-dev cross-env
复制代码
修改 package.json:
{
"scripts": {
"dev": "cross-env NODE_ENV=development webpack serve --open --config config/webpack.dev.js",
"build": "cross-env NODE_ENV=production webpack --config config/webpack.prod.js"
},
}
复制代码
如今能够运行 webpack 指令:
以上咱们完成了一个基于 webpack 编译的支持模块化开发的简单项目。
源码地址:webpack Demo0
本章节将继续完善配置,在上述配置基础上,用 Webpack 搭建一个 SASS + TS + React 的项目。
将支持如下功能:
想直接看配置的同窗 -> 本文源码地址:webpack Demo1
在 webpack 5 中,可使用内置的 Asset Modules,将 images 图像混入咱们的系统中。
修改通用环境配置文件 webpack.commom.js:
const paths = require('./paths');
module.exports = {
module: {
rules: [
{
test: /\.(png|svg|jpg|jpeg|gif)$/i,
include: paths.appSrc,
type: 'asset/resource',
},
],
},
}
复制代码
在实际开发过程当中,推荐将大图片上传至 CDN,提升加载速度。
使用 Asset Modules 接收字体文件。
修改通用环境配置文件 webpack.commom.js:
module.exports = {
module: {
rules: [
{
test: /.(woff|woff2|eot|ttf|otf)$/i,
include: [
resolveApp('src'),
],
type: 'asset/resource',
},
]
}
}
复制代码
在这里我发现,我不新增对 ttf 文件的配置,也可以引入字体不报错,不知道是什么问题,先记录一下,有知道缘由的大佬移步评论区。
在实际开发过程当中,推荐将字体文件压缩上传至 CDN,提升加载速度。如配置字体的文字是固定的,还能够针对固定的文字生成字体文件,能够大幅缩小字体文件体积。
为了在 JavaScript 模块中 import
一个 CSS 文件,须要安装并配置 style-loader 和 css-loader。
style-loader 用于将 CSS 插入到 DOM 中,经过使用多个 <style></style>
自动把 styles 插入到 DOM 中.
css-loader 对 @import
和 url()
进行处理,就像 js 解析 import/require()
同样,让 CSS 也能模块化开发。
安装 CSS 相关依赖:
npm install --save-dev style-loader css-loader
复制代码
修改通用环境配置文件 webpack.commom.js:
const paths = require('./paths');
module.exports = {
module: {
rules: [
{
test: /\.css$/,
include: paths.appSrc,
use: [
// 将 JS 字符串生成为 style 节点
'style-loader',
// 将 CSS 转化成 CommonJS 模块
'css-loader',
],
},
]
}
}
复制代码
Sass 是一款强化 CSS 的辅助工具,它在 CSS 语法的基础上增长了变量、嵌套、混合、导入等高级功能。
sass-loader 加载 Sass/SCSS 文件并将他们编译为 CSS。
安装 SASS 相关依赖:
npm install --save-dev sass-loader sass
复制代码
修改通用环境配置文件 webpack.commom.js:
const paths = require('./paths');
module.exports = {
module: {
rules: [
{
test: /.(scss|sass)$/,
include: paths.appSrc,
use: [
// 将 JS 字符串生成为 style 节点
'style-loader',
// 将 CSS 转化成 CommonJS 模块
'css-loader',
// 将 Sass 编译成 CSS
'sass-loader',
],
},
复制代码
PostCSS 是一个用 JavaScript 工具和插件转换 CSS 代码的工具。
postcss-loader 使用 PostCSS 处理 CSS 的 loader。
安装 PostCSS 相关依赖:
npm install --save-dev postcss-loader postcss postcss-preset-env
复制代码
修改通用环境配置文件 webpack.commom.js:
const paths = require('./paths');
module.exports = {
module: {
rules: [
{
test: /\.module\.(scss|sass)$/,
include: paths.appSrc,
use: [
// 将 JS 字符串生成为 style 节点
'style-loader',
// 将 CSS 转化成 CommonJS 模块
{
loader: 'css-loader',
options: {
// Enable CSS Modules features
modules: true,
importLoaders: 2,
// 0 => no loaders (default);
// 1 => postcss-loader;
// 2 => postcss-loader, sass-loader
},
},
// 将 PostCSS 编译成 CSS
{
loader: 'postcss-loader',
options: {
postcssOptions: {
plugins: [
[
// postcss-preset-env 包含 autoprefixer
'postcss-preset-env',
],
],
},
},
},
// 将 Sass 编译成 CSS
'sass-loader',
],
},
],
},
}
复制代码
为提高构建效率,为 loader 指定 include,经过使用
include
字段,仅将 loader 应用在实际须要将其转换的模块。
为了让项目的配置灵活性更高,不使用 create-reate-app 一键搭建项目,而是手动搭建 React 对应的配置项。
安装 React 相关:
npm i react react-dom @types/react @types/react-dom -D
复制代码
安装 TypeScript 相关:
npm i -D typescript esbuild-loader
复制代码
为提升性能,摒弃了传统的 ts-loader,选择最新的 esbuild-loader。
修改通用环境配置文件 webpack.commom.js:
const paths = require('./paths');
module.exports = {
resolve: {
extensions: ['.tsx', '.ts', '.js'],
},
module: {
rules: [
{
test: /\.(js|ts|jsx|tsx)$/,
include: paths.appSrc,
use: [
{
loader: 'esbuild-loader',
options: {
loader: 'tsx',
target: 'es2015',
},
}
]
},
]
}
}
复制代码
TypeScript 是 JavaScript 的超集,为其增长了类型系统,能够编译为普通 JavaScript 代码。
为兼容 TypeScript 文件,新增 typescript 配置文件 tsconfig.json:
{
"compilerOptions": {
"outDir": "./dist/",
"noImplicitAny": true,
"module": "es6",
"target": "es5",
"jsx": "react",
"allowJs": true,
"moduleResolution": "node",
"allowSyntheticDefaultImports": true,
"esModuleInterop": true,
}
}
复制代码
若是想在 TypeScript 中保留如
import _ from 'lodash';
的语法被让它做为一种默认的导入方式,须要在文件 tsconfig.json 中设置"allowSyntheticDefaultImports" : true
和"esModuleInterop" : true
。
"allowSyntheticDefaultImports": true
配置TypeScript 配置文件 tsconfig.json 须要加 "allowSyntheticDefaultImports": true
配置,不然会提示 can only be default-imported using the 'allowSyntheticDefaultImports' flag
。
{
"compilerOptions": {
"allowSyntheticDefaultImports": true
},
}
复制代码
不加 "allowSyntheticDefaultImports": true
的加报错信息以下:
在 tsx 中引入 jsx 文件报错以下:
以上咱们完成了一个基于 webpack 编译的 SASS + TS + React 项目。
源码地址:webpack Demo1
本文从 Webpack 基础配置、Webpack 进阶配置 2 个角度进行讲述,从 Webpack 实践着手,和你一块儿了解 Webpack。
下一篇《学习 Webpack5 之路(优化篇)》将从继续优化项目配置,尝试搭建一个最优的 Webpack 配置,敬请期待。
本文源码:
但愿能对你有所帮助,感谢阅读~
别忘了点个赞鼓励一下我哦,笔芯❤️