一张图带你了解webpack基础
前言
什么是webpack?
概念:本质上,webpack 是一个现代 JavaScript 应用程序的静态模块打包器 。当 webpack 处理应用程序时,它会递归地构建一个依赖关系图 ,其中包含应用程序需要的每个模块,然后将所有这些模块打包成一个或多个 bundle。 它包括三个模式:
- 生产模式 production
- 开发(测试)环境 development
- none
从处理资源来说,webpack就是一个bundler(打包工具)。
很多人可能会问,打包和上述图片有什么关系呢?这就得介绍webpack中另外一个重要的东西 ---loader,相信了解过webpack官网的小伙伴都知道,loader是用于对模块的源代码进行转换。简单来说就是在开发中我们不仅仅有基本的js代码处理,我们也需要加载css、图片,也包括 一些高级的将ES6
转成ES5
代码,将TypeScript
转成ES5
代码,将scss
、less
转成css
,将.jsx
、.vue
文件转成js
文件等等。而webpack本身是不支持对这些模块直接进行转化的,这时候就需要用到扩展的loader就可以完成转化了。在不同的模块中,webpack-loader也是不同的,它使用了单一设计模式,一个loader就做一件事。以下是常见的loader:
- babel-loader:对js进行转译,把es6转换成es5
- css-loader:加载css,支持模块化、压缩、文件导入等
- file-loader:把文件输入到一个文件夹里,在代码中通过相对Url去引入输出的文件
- url-loader:加载远程资源,以base64的方式把文件内容注入代码中
- awesome-typescript-loader:将ts转化为js,再通过babel转化为es5
- image-loader:加载并压缩图片文件
- 想要了解更多关于loader的可以去看看babel官方文档
webpack基本概念
- context 上下文 基础目录,绝对路径,用于从配置中解析入口起点(entry point)和 loader 示例:
// 当前目录的物理路径
const basePath = __dirname;
context: path.join(basePath,"src")
- resolve 解析 这些选项能设置模块如何被解析 示例:
resolve: {
// extensions 检测以这些后缀的文件,处理文件
extensions: [".js", ".ts", "tsx"...等]
}
- entry 入口 入口起点指示 webpack 应该使用哪个模块,来作为构建其内部依赖图的开始的。 示例:
// 入口文件
entry: ["./index.html"...等]
// 入口文件可能不止一个
entry: {
// app vender 打包文件后取别名
app: ["./index.js"],
vender: ["./index.ts"]...等
}
- output 输出
配置
output
选项可以控制 webpack 如何向硬盘写入编译文件。 示例:
// 出口,打包文件到xxx目录下
const path = ruquire("path");
output: {
path: path.join(basePath, "dist"),
filename: "bundle.js"
}
- module 模块 在模块化编程中,开发者将程序分解成离散功能块(discrete chunks of functionality),并称之为模块。 示例:
module: {
// rules给resolve里的后缀添加规则
rules: [
// 使用正则, \.表示转译
test: /\.js$/,
// 转译哪种文件使用相对应的loader
loader: "babel-loader"
]
}
- plugins 插件 插件是 webpack 的支柱功能。webpack 自身也是构建于,你在 webpack 配置中用到的相同的插件系统之上! 插件目的在于解决 loader 无法实现的其他事。 示例:
const HtmlWebpackPlugin = ruquire("html-webpack-plugin");
plugins: [
new HtmlWebpackPlugin({
filename: "index.html",
template: "index.html"
})
]
看完这些基础概念之后,相信小伙伴们都对webpack有很大的兴趣,想要手撸一个试试手了。下面是我写的一个简单例子,感兴趣的小伙伴可以去尝试一下。
一个小demo
新建一个webpack-demo文件夹 文件初始化
- npm init -y
在webpack-demo根目录下新建src文件夹,添加三个文件,代码如下:
// index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Webpack基础</title>
</head>
<body>
<div class="container">
<div class="root"></div>
</div>
</body>
</html>
// index.jsx
import * as React from "react";
import * as ReactDOM from "react-dom";
import Hello from "./hello";
ReactDOM.render(
<Hello />,
document.getElementById('root')
)
// hello.jsx
import * as react from 'react';
import * as ReactDOM from 'react-dom'
const Hello = () => {
return (
<div>
Hello webpack!
</div>
)
}
export default Hello
接下来的操作相信大家都猜到了,没错,就是配置webpack。 首先,我们在webpack-demo根目录下新建两个文件。
- .babelrc
- webpack.config.js
很多小伙伴可能会疑惑,配置webpack为什么要.babelrc文件啊?在这里就需要解释一下babel是一种转译的配置工具,分为三个阶段。
- parsing
- transfroming
- generating
举个简单的小栗子: 以es6转译为es5代码为例
// .babelrc
{
"presets": [
"@babel/preset-env",
"@babel/preset-react"
]
}
下面就是配置webpack的主要内容了,在项目根目录下新建webpack.config.js文件。具体代码如下:
const path = require("path");
const HtmlWebpackPlugin = require('html-webpack-plugin')
const basePath = __dirname; // 当前项目的物理路径
// console.log(path.join(basePath),'......');
module.exports = {
// 上下文
context: path.join(basePath, "src"),
// 解析
resolve: {
// extensions 检测以.js为后缀的文件,处理文件
extensions: [".js", ".jsx"]
},
// 入口
entry: ["./index.jsx"],
// 出口,打包文件到dist目录下
output: {
path: path.join(basePath, "dist")
},
// 模块,找到规则
module: {
// 给resolve里的后缀添加规则
rules: [
{
// 正则 \. 表示转义,括号里面表示两者都行
test: /\.(js|jsx)$/,
// 需要使用哪种loader
loader: "babel-loader"
}
]
},
// 插件
plugins: [
new HtmlWebpackPlugin({
filename: "index.html",
template: "index.html"
})
]
}
配置完webpack.config.js文件之后,很多小伙伴发现在终端下面运行会报一系列的错误。那是因为我们的package.json文件还没有配置完成。具体的配置代码如下:
{
"name": "webpack-demo",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"start": "webpack-dev-server --mode=development --hot --open",
"build": "webpack --mode=development"
},
"keywords": [],
"author": "",
"license": "ISC",
"dependencies": {
"@babel/cli": "^7.17.6",
"@babel/core": "^7.17.9",
"@babel/preset-env": "^7.16.11",
"@babel/preset-react": "^7.16.7",
"babel-loader": "^8.2.4",
"html-webpack-plugin": "^5.5.0",
"react": "^17.0.2",
"react-dom": "^17.0.2",
"webpack": "^5.72.0",
"webpack-cli": "^4.9.2",
"webpack-dev-server": "^4.8.1",
"clean-webpack-plugin": "^4.0.0"
}
}
这里主要是修改了package.json中的script脚本以及需要安装的一些依赖包。
- scripts
"start": "webpack-dev-server --mode=development --hot --open"含义:start用于项目的初始化,是接下来一切工作的依赖起始端;webpack-dev-server 是为静态文件提供服务;--mode=development表示生产环境;--hot --open表示自动刷新和热替换。"build": "webpack --mode=development"表示打包时需要的脚本配置。
- dependencies
@babel/cli,@babel/core,@babel/preset-env等表示该本次测试需要的一些依赖包,后面对应的是其安装的版本。我们可以把这些需要安装的依赖包直接粘贴,再直接在终端中npm i(yarn也是一样)下载依赖包,或者在终端中直接一个一个安装一下这些依赖包(有些版本更新或会有些许配置不同)
配置完这些文件,我们在终端中执行
npm run start
就可以在页面上看到hello.jsx上的内容渲染到了index.html页面上。效果如下:
还可以在终端中执行
npm run build
将整个项目打包到dist目录下面。这一整套流程下来,一个基本的webpack项目配置就完成了。
优化
相信细心的小伙伴在执行 npm run build
的时候会发现,运行完后除了生成一个dist目录外,在终端中还会有compiled successfully in xxx ms
这里表示的是使用webpack打包完成后需要多少时间。
看起来只是0.2s左右,如果是一个超大型的项目要打包,时间方面是不是的让我们难以接受?
这也是webpack配置中需要优化的知识点了。由于本demo是打算构建react项目,在webpack.config.js文件中我们可以稍加修改。
- entry
entry: {
app: ['./index.jsx'], // app 取别名
vender: [
'react',
'react-dom'
]
},
把react中的一些公有依赖包打包在同一个文件,之后更新会缓存,再一次打包不会改变,不会出现304情况。
- output
output: {
path: path.join(basePath, "dist"),
filename:"[name].[chunkhash].js"
}
[name]表示变量名占位;[chunkhash] 哈希值,每次打包名字都不一样 生成每个不同的文件,所以请求不止一次,会缓存,如果代码没有更新,下次不用再次发送请求。
- module
module: {
rules: [
{
test: /\.js$/,
// 排除 node_modules 优化
exclude: /node_modules/,
loader: 'babel-loader'
}
]
},
在rules中添加exclude,排除 node-modules不打包,减少不必要代码
- plugins
plugins: [
new CleanWebpackPlugin(),
new HtmlWebpackPlugin({
filename: "index.html",
template: "index.html"
})
]
添加clean-webpack-plugin依赖包,打包时会把上一次的目录删除,清理每次打包下没有使用的目录
添加完这些之后,我们在来看一下效果。
比之前打包速度快了0.05s左右,当然,由于本demo代码量,并且每位小伙伴电脑设备配置不一,可能在调试上面结果会稍有误差,这都不是重点!重点是要了解如何优化。优化后代码如下:
const path = require("path");
const HtmlWebpackPlugin = require('html-webpack-plugin');
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
const basePath = __dirname; // 当前项目的物理路径
// console.log(path.join(basePath),'......');
module.exports = {
// 上下文
context: path.join(basePath, "src"),
// 解析
resolve: {
// extensions 检测以.js为后缀的文件,处理文件
extensions: [".js", ".jsx"]
},
// 入口
entry: {
app: ['./index.jsx'], // app 取别名
vender: [
'react',
'react-dom'
]
},
// 出口,打包文件到dist目录下
output: {
path: path.join(basePath, "dist"),
filename: "[name].[chunkhash].js"
},
// 模块,找到规则
module: {
// 给resolve里的后缀添加规则
rules: [
{
// 正则 \. 表示转义,括号里面表示两者都行
test: /\.(js|jsx)$/,
// 需要使用哪种loader
loader: "babel-loader",
// 排除node_modules 不打包
exclude: /node_modules/,
}
]
},
// 插件
plugins: [
new CleanWebpackPlugin(),
new HtmlWebpackPlugin({
filename: "index.html",
template: "index.html"
})
]
}
总结
希望本文对各位小伙伴能有所帮助。如果有不足之处希望批评指正。喜欢这篇文章的小伙伴可以点赞+收藏哦。参考文献:
转载自:https://juejin.cn/post/7088970247184580638