什么是webpack?
webpack可看作是模块打包机,他做的事情是分析你的项目结构,找到JavaScript模块以及其他的一些浏览器不能直接运行的一些扩展语言(Scss,TypeScript)等,并将其打包为合适的格式以供浏览器使用,它是一个用于现代 JavaScript 应用程序的静态模块打包工具。当 webpack 处理应用程序时,webpack 从命令行或配置文件中定义的一个模块列表开始,处理你的应用程序,它会递归地在内部构建一个依赖关系图,其中包含该应用程序需要的每个模块,然后将所有这些模块打包成一个或多个 bundle。
在 webpack 看来,前端所有的资源文件(js、json、css、img、less…)都会作为模块处理。它将根据模块的依赖关系进行静态分析,打包生成对应的静态资源(bundle)。webpack 本身只能理解 JavaScript 和 JSON 文件,这是 webpack 开箱可用的自带能力,万物皆模块。
webpack的六个核心概念
- 1、Entry:入口,Webpack执行构建的第一步将从Entry开始,可抽象成输入。
- 2、Module:模块,在Webpack中一切皆为模块,一个模块对应着一个文件,Webpack会从配置的Entry开始递归找出所有依赖的模块
- 3、Chunk:代码块,一个Chunk由多个模块组合而成,用于代码合并与分割
- 4、Loader:模块转换器,用于把模块原内容按照需求转换成新内容
- 5、Plugin:扩展插件,在Webpack构建流程中的特定时机注入扩展逻辑来改变构建结果或做你想要的事情
- 6、Output:输出结果,在Webpack经过一系列处理并得出最终想要的代码后输出结果
webpack主要功能
- 1、代码转换:TypeScript转化成JavaScript,SCSS编译成CSS等
- 2、文件优化:压缩JavaScript,CSS,HTML代码,压缩合并图片等
- 3、代码分割:提取多个页面的公共代码,提取首屏不需要执行部分的代码让其异步加载
- 4、模块合并:在采用模块化的项目里会有很多个模块和文件,需要构建功能把模块分类合并成一个文件
- 5、自动刷新:监听本地文件的变化,自动重新构建,刷新浏览器
- 6、代码校验:在代码被提交到仓库前需要校验代码是否符合规范,以及单元测试是否通过
- 7、自动发布:更新完代码后,自动构建出线上发布代码并传输给发布系统
webpack主要工作流程
Webpack启动后会从Entry里配置的Module开始递归解析Entry的所有Module,每找到一个Module,就会根据配置的Loader去找出对应的转换规则,对 Module转换后,再解析出当前的Module依赖的Module,这些模块会以Entry为单位进行分组,一个Entry和其所有依赖的Module被分到一个组,也就是一个Chunk,最后Webpack会把所有Chunk转换成文件输出。在整个流程中Webpack会在恰当的时机执行Plugin里定义的逻辑
首先我们初始化一个webpack 项目,先安装webpack的相关npm包吧:
npm install webpack webpack-cli -D
安装好了之后,我们可以在项目的node_modules目录的.bin文件夹下面发现关于webpack的脚本文件:
所以我们在package.json文件中配置的脚本,其实就是执行的这里面的脚本文件。
webpack三种模式
- 1、production
- 2、development
- 3、none
接下来我们来搭建一个webpakc的开发环境
首先安装webpack-dev-server
npm i webpack-dev-server -D
然后在package.json中配置启动命令
接下来,我们去在项目的根目录去创建一个webpack的配置文件 => webpack.config.js
搭建一个开发服务器,我们需要用到devServer这个配置项
const path = require("path");
const HtmlWebpackPlugin = require("html-webpack-plugin");
const CleanWebpackPlugin = require("clean-webpack-plugin").CleanWebpackPlugin;
module.exports = {
mode: "development",
entry: "./index.js",
output: {
filename: "index.js",
path: path.resolve(__dirname, "dist"), //打包出来的文件的路径
},
devtool: "source-map",
devServer: {
//开发服务器配置
port: 3000, //设置服务的端口号
progress: false, //将运行进度输出到控制台。
host: "127.0.0.1",
contentBase: "./dist", //设置开发服务器静态资源的基本目录
compress: false, //是否压缩文件
open: true, //是否自动打开浏览器
},
plugins: [
new CleanWebpackPlugin(), //打包自动清空之前打包的文件目录
new HtmlWebpackPlugin({
filename: "index.html",
template: "./public/index.html",//指定一个html模板
hash: true, // 为静态资源生成hash值
inject: "body",//向template或者templateContent中注入所有静态资源,不同的配置值注入的位置不经相同。
xhtml: true, //true 或者 false, 默认false;是否渲染link为自闭合的标签,true则为自闭合标签
minify: {
removeComments: true,
},
}),
],
};
此处我们已经搭建了一个基本的webpack开发服务,但是我们们打开浏览器的控制台会发现有报错,原来是我们在项目中使用了es6的语法import
index.js
import { fn, test } from "src/App.js";
报错信息:
于是我们就需要去配置webpack的module,在这个配置项中去增加可以处理es6语法的loader => babel
module: {
rules: [
{
test: /\.js$/,
use: [
{
//使用babel-loader将es6转化为es5
loader: "babel-loader",
options: {
presets: ["@babel/preset-env"],
},
},
],
},
]
}
但是我们发现项目在编译的时候,由于有scss代码,所以我们也要去将scss规则配置上
报错信息
我们完整的配置如下:
const path = require("path");
const HtmlWebpackPlugin = require("html-webpack-plugin");
const CleanWebpackPlugin = require("clean-webpack-plugin").CleanWebpackPlugin;
module.exports = {
mode: "development",
entry: "./index.js",
output: {
filename: "[name].[hash:8].js", //当有多个入口配置时,使用占位符,并配置hash值(主要是为了避免缓存)
path: path.resolve(__dirname, "dist"), //打包出来的文件的路径
},
resolve: {
//配置扩展名
extensions: [".js"],
//配置别名,可以通过 'src/xxx' 直接访问src的目录
alias: {
src: path.resolve(__dirname, "src"),
},
},
devtool: "source-map",
devServer: {
//开发服务器配置
port: 3000, //设置服务的端口号
progress: false, //将运行进度输出到控制台。
host: "127.0.0.1",
contentBase: "./dist", //指定服务的默认目录
compress: false, //是否压缩文件
open: true, //是否自动打开浏览器
},
plugins: [
new CleanWebpackPlugin(), //打包自动清空之前打包的文件目录
new HtmlWebpackPlugin({
filename: "index.html",
template: "./public/index.html", //指定一个html模板
hash: true, // 为静态资源生成hash值(避免缓存)
inject: "body", //向template或者templateContent中注入所有静态资源,不同的配置值注入的位置不经相同。
xhtml: true, //true 或者 false, 默认false;是否渲染link为自闭合的标签,true则为自闭合标签
minify: {
removeComments: true,
},
}),
],
module: {
rules: [
{
test: /\.js$/,
use: [
{
//使用babel-loader将es6转化为es5
loader: "babel-loader",
options: {
presets: ["@babel/preset-env"],
},
},
],
},
{
test: /\.scss$/,
//从右向左执行对应的loader
use: ["style-loader", "css-loader", "sass-loader"],
},
{
test: /\.css$/,
use: ["style-loader", "css-loader"],
},
{
test: /\.(png|jpg|gif)$/,
use: [
{
//url-loader内置了file-loader,会把没有超过设置的大小的静态资源图片,转成base64字符串嵌入到html中去,超过的用file-loader单独打包处理
loader: "url-loader",
options: {
limit: 20000,
},
},
],
},
],
},
};
注意一下几个点:
1、单入口(不指定filename):
打包出来的文件名默认为man.[hash].js
2、支持加载css文件
如果不设置css-loader,那么解析的时候会报错,因为webpack默认解析一个文件的时候是当成了js文件
3、contentBase
设置开发服务器静态资源的基本目录,如果不加的话就会默认显示整个项目根目录下面的文件
4、使用了devServer
如果你使用了devServer,那么所有的文件都会写入到内存中,而不会写入到硬盘上面,为了速度
5、devtool:eval
会把打包的文件与之前的文件映射起来,这样子才行方便调试代码
6、htmlWebpackPlugin
如果没有指定chunks参数,在有多个文件入口的时候,那么html中就会插入多个script标签去引入对应的js
7、分离css
因为CSS的下载和JS可以并行,当一个HTML文件很大时,我们可以把CSS单独提取出来加载
使用mini-css-extract-plugin
style-loader只会创建一个style标签,并且把打包好的css文件插入到这个标签中,这样子会减慢页面的加载和渲染,使用miniCssExtractPlugin插件就可以避免这个问题,miniCssExtractPlugin插件会在head标签中创建一个link标签,并把单独打包的css文件引入进来实现预加载。
使用
替换style-loader:
打包出来的结果