Webpack5不完全指南-基础篇
金石有声,不拷不鸣
引言
在webpack之类的构建工具诞生之前,前端项目构建一直处于"高耕火种"时代,项目代码压缩混淆、 js/css等依赖的合并都需要手动操作。webpack的出现解决了前端项目构建的很多痛点,可以说是前端工程化中必学必会的一环。
什么是Webpack?
Webpack 是前端项目的构建工具,它可以解析项目的依赖(js/css/图片资源等),对它们进行打包处理。
Webpack主要功能:
代码转译
模块合并
混淆压缩
代码分割
代码刷新
自动刷新
自动部署
webpack环境搭建
webpack基于Node环境,Node请自行下载
Webpack安装
全局安装: npm install webpack webpack-cli -g
局部安装(推荐): npm install webpack webpack-cli -D
搭建Demo
新建basic文件夹并用Vscode打开
局部安装 webpack webpack-cli
npm install webpack webpack-cli -D
Webpack-cli
Webpack-cli之前是再webpack包中的,webpack 4.0以后作为单独的模块进行管理。
执行Webpack
Webpack 通过指令 webpack
来对项目进行打包,但是这个指令只在全局安装的Webpack有用。npm5.2 提供了 npx
指令,npx
可以在项目中直接执行模块的指令。
终端执行npx webpack
webpack执行了,但是依赖和配置有问题,打包的入口、出口文件等都需要配置。
新建默认入口文件
webpack默认入口文件为: 根目录/src/index.js
新建 根目录/src/index.js
// /src/index.js const init = () => { console.log("webpack真好玩") } init() 复制代码
执行
npx webpack
,打包成功生成 /dist/main.js
应用场景
在真实开发中会涉及多个模块依赖的场景
新建 src/a.js
// moduleA console.log("我是模块A"); module.exports = { name: "moduleA" } 复制代码
src/index.js 导入 a.js 并重新构建
const a = require("./a") console.log(a.name); const init = () => { console.log("webpack真好玩") } init() 复制代码
安装vscode插件 - run code ,并执行 main.js
在浏览器端执行
前面的 require
、module.exports
属于 CommonJS模块化规范 ,浏览器默认不支持该语法。但是代码经过webpack打包后,会自动转换模块化规范。
新建 index.html (src/index.html),引入 src/index.js
<!DOCTYPE html> <html> <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>Document</title> </head> <body> <!-- 使用CommonJS模块化规范 --> <script src="./index.js"></script> </body> </html> 复制代码
引入 dist/main.js
<body> <!-- 使用CommonJS模块化规范 --> <!-- <script src="./src/index.js"></script> --> <!-- 引入webpack打包后的js --> <script src="./dist/main.js"></script> </body> 复制代码
Webpack配置
webpack四大核心概念:
入口(entry):程序入口的js
出口(output):打包之后存放的位置
loader:对项目依赖、文件进行转换处理
plugin:可以处理loader做不了的工作
Webpack配置文件
Webpack配置文件名默认为 webpack.config.js,配置文件必须遵守 CommmonJS 模块化规范。
const path = require("path") // webpack配置文件必须遵循CommonJS规范 module.exports = { // 项目打包入口文件 entry: "./src/index.js", // 项目出口配置 output: { // path.resolve("path")/path.join(_dirname,"path"):将相对路径解析为绝对路径 path: path.resolve("./dist/"), // 打包后的文件名 filename: 'bundle.js' }, // 打包模式(默认:production):production || development mode: "development" } 复制代码
webpack指令参数
--config 文件名:
使用自定义配置文件执行打包指令:
npx webpack --config webpack.custom.config.js
可以根据开发 / 上线 不同环境使用不同的配置文件
新增script指令:
{ "scripts": { // 终端输入 npm run build,使用 webpack.custom.config.js进行打包 "build": "npx webpack --config webpack.custom.config.js" } } 复制代码
html插件
webpack每次编译打包不会将index.html打包进来,通过html插件可以打包html文件同时自动引入其他css/js等依赖
html插件作用
1、打包后自动复制一份目标html文件
2、html自动引入打包后的js(注意:不要手动引入js!!!)
安装:
npm i html-webpack-plugin -D
在webpack配置文件中配置插件
// webpack.config.js const HtmlPlugin = require("html-webpack-plugin") module.exports = { mode: "production", // 配置插件字段 plugins: [ // 新建一个HtmlPlugin实例 new HtmlPlugin( { // 打包输出文件名 filename: "index.html", // 被复制的目标html template: "./src/index.html" } ) ] } 复制代码
修改index.html
<body> <!-- 使用CommonJS模块化规范 --> <!-- <script src="./src/index.js"></script> --> <!--新增节点--> <ul> <li>1</li> <li>1</li> <li>1</li> <li>1</li> <li>1</li> <li>1</li> <li>1</li> <li>1</li> <li>1</li> </ul> <!--使用html-webpack-plugin无需引入js,它会自动帮你引入--> <!--<script src="./src/index.js"></script>--> </body> 复制代码
执行指令
npx webpack
进行打包
自动编译工具
每次修改代码后,都要执行npm run build
重新编译,我们可以借助插件或工具让webpack进行自动编译
watch mode
webpack-dev-server(最推荐)
webpack-dev-middleware
watch mode
webpack 开启watch模式,会监视项目文件的变化,当发现有修改的代码时会自动编译打包,输出文件。
方式一:webpack-cli打包指令增加--wtach
参数
1、package.json 新增 watch 脚本
"scripts": { "build": "npx webpack --config webpack.custom.config.js", "watch": "npx webpack --config webpack.config.js --watch" } 复制代码
2、终端执行 npm run watch
,开始监听项目文件
方式二:webpack配置文件开启watch模式
// webpack.config.js module.exports = { // 打包模式(默认:production):production || development mode: "production", // 开启watch模式 watch: true } 复制代码
webpack-dev-server
这个npm包可以在本地开启服务,在内存中生成打包的bundle.js.打包效率高在修改代码后重新构建并刷新页面。注意:使用webpack-dev-server 必须先安装webpack
1、安装 webpack-dev-server :
npm i webpack-dev-server -D
2、 webpack-dev-server 指令:
npx webpack-dev-server --port 3001
--port:指定服务端口号
--hot:开启热更新
--open:是否自动打开浏览器访问页面
3、配置 serve 脚本 执行脚本
"scripts": { "serve": "npx webpack-dev-server --port 3001 --open", } 复制代码
当然也可以通过webpack配置文件的 devServer 字段开启 webpack-dev-server 服务
1、 配置 devServer 字段
// webpack.config.js module.exports = { // 开启watch模式 // watch: false, // 配置 webpack-dev-server devServer: { // 编译完成是否自动打开页面 open: true, // 服务端口号 port: 3000, // 是否启用压缩 compress: true, // 是否启用热更新 hot: true, // 服务的基准路径 contentBase: "./src" } } 复制代码
2、配置 dev 脚本 执行脚本
"scripts": { "build": "npx webpack --config webpack.custom.config.js", "watch": "npx webpack --config webpack.config.js --watch", "serve": "npx webpack-dev-server --port 3001 --open", "dev": "npx webpack serve" } 复制代码
webpack-dev-middleware
webpack-dev-middleware 使用node的中间件,它可以作为一个容器将webpack打包后的资源提供给服务器。webpack-dev-server 也是通过这个实现的
安装 express、webpack-dev-middleware
npm i express webpack-dev-middleware -D
根目录新建 server.js
// 新建服务 并运行 // 导入express框架 const express = require("express") // 导入webpack const webpack = require("webpack") // 导入中间件 const webpackDevMiddleware = require("webpack-dev-middleware") // 导入webpack配置对象 const config = require("./webpack.config.js") // 新建服务 const app = express() const compiler = webpack(config) // app注册中间件 app.use(webpackDevMiddleware(compiler, { publicPath: "/" })) // 监听服务 app.listen(3001, function () { console.log("3000端口服务运行"); }) 复制代码
新增脚本 server, 并执行
"scripts": { "server": "node server.js" } 复制代码
处理css文件
webpack默认无法处理css文件,我们需要借助相关loader进行处理
安装 css-loading
npm i css-loader style-loader -D
配置文件中配置loader(webpack.config.js)
// 配置各种loader module: { rules: [ // 配置用来解析css相关loader { // loader匹配到的文件 test: /\.css$/i, // webpack调用loader的顺序是从右到左 use: ['style-loader', 'css-loader'] } ] }, 复制代码
新建
src/css/index.css
li { line-height: 100px; background-color: red; } 复制代码
入口文件导入css
html引入的资源文件 webpack不会打包,我们需要将css文件引入到 src/index/js 中// src/index.js // 在webpack的入口文件中引入css import style from './css/index.css' 复制代码
执行
npm run serve
处理less/scss文件
css预处理器的文件 webpack也可以打包处理,这里以 less 为例
安装 loader
npm i less-loader -D
新建 src/less/index.less
ul { li:nth-child(1) { background-color: pink; color: green; } } 复制代码
在入口文件中导入less
// 在webpack的入口文件中引入less import './less/index.less' 复制代码
配置less-loader (webpack.config.js)
// 配置各种loader module: { rules: [ { test: /\.css$/i, // webpack调用loader的顺序是从右到左 use: ['style-loader', 'css-loader'] }, // 配置用来解析.less相关loader { test: /\.less$/i, // webpack调用loader的顺序是从右到左 use: ['style-loader', 'css-loader', 'less-loader'] } ] } 复制代码
执行
npm run serve
,查看效果
处理图片字体文件
图片和字体文件也需要loader来处理
webpack4
file-loader:处理图片等文件
url-loader:处理图片等文件,可将图片转为base64格式
注意:url-loader 是对 file-loader 的包装,使用 url-loader 必须安装 file-loader
安装
npm i fild-loader url-loader -D
配置相关loader (webpack.config.js)
module: { rules: [ // 配置用来解析图片等文件相关loader { test: /\.jpg|png|gif|bmp|ttf|eot|svg|woff|wpff2$/i, // 图片大小大于16940自动转为base64格式 use: 'url-loader?limit=16940' // 使用file-loader // use: 'file-loader' }] } 复制代码
webpack5
webpack5 通过内置的 Asset Modules 引入任何其他类型的文件
1、asset/resource 发送一个单独的文件并导出 URL。之前通过使用 file-loader 实现。
2、asset/inline 导出一个资源的 data URI。之前通过使用 url-loader 实现。
3、asset/source 导出资源的源代码。之前通过使用 raw-loader 实现。
4、asset 在导出一个 data URI 和发送一个单独的文件之间自动选择。之前通过使用 url-loader,并且配置资源体积限制实现。
配置文件(webpack.config.js)
module: { rules: [ // 配置用来解析图片等文件相关loader { test: /\.jpg|png|gif|bmp|ttf|eot|svg|woff|wpff2$/i, type: "asset", //解析 parser: { //转base64的条件 dataUrlCondition: { // 大于100kb转为base64格式 maxSize: 100, // 25kb } }, generator: { //输出路径 // name:使用原文件名 // hash:文件名增加hash字符 // ext:使用原文件拓展名 filename: 'img/[name].[hash:6][ext]', //打包后对资源的引入,文件命名已经有/img了 publicPath: './' } } ] }, 复制代码
配置babel
webpack可以通过babel打包解析高级语法,将它们转换成可兼容绝大多数浏览器的代码
安装
npm install babel-loader babel-core babel-preset-env
配置 bable-loader (webpack.custom.config.js)
// 配置各种loader module: { rules: [ // 解析所有.js文件 { test: /\.js$/, // 排除node_modules等文件 exclude: /(node_modules|bower_components)/, use: { // 使用 babel-loader loader: 'babel-loader', options: { // 使用 babel的预设 presets: ['@babel/preset-env'] } } } ] } 复制代码
使用bebal处理generator
js一些最新语法或者处于草案阶段的语法,babel需要借助相应的插件进行处理。
// index.js ...... const zs = new Person({ name: "张三" }) console.log("zs", zs.name); const init = () => { console.log("webpack真好玩123fsd") } init() // 使用generator function* fn() { yield 1 yield 2 return 3 } let newFn = fn() console.log("next1:", newFn.next()) console.log("next2:", newFn.next()) 复制代码
打包后会报错
安装 plugin-transform-runtime
npm i @babel/plugin-transform-runtime -D // @babel/runtime 是 @babel/plugin-transform-runtime的核心依赖 npm install --save @babel/runtime 复制代码
配合打包配置 babel-loader (webpack.custom.config.js)
module: { rules: [ { test: /\.js$/, exclude: /(node_modules|bower_components)/, use: { loader: 'babel-loader', options: { presets: ['@babel/preset-env'], // 注册babel插件 plugins: ["@babel/plugin-transform-runtime"] } } } ] }, 复制代码
npm run build
执行 webpack.custom.config.js
babel配置文件
babel作为一个工具,可能会有大量需要配置的地方,如果在webpack配置babel可能会造成webpack配置文件体量过于庞大、可读性差等问题。这时候可以通过babel的独立配置文件解决这个问题
.babelrc.json
在项目根目录新建 .babelrc.json ,将 babel-loader 的 options 字段移植到 .babelrc.json 中.
// .babelrc.json { "presets": [ "@babel/preset-env" ], "plugins": [ "@babel/plugin-transform-runtime" ] } // webpack.custom.config.js { test: /\.js$/, // 排除node_modules等文件 exclude: /(node_modules|bower_components)/, use: { // 使用 babel-loader loader: 'babel-loader', // 移植到 .babelrc.json中 // options: { // // 使用 babel的预设 // presets: ['@babel/preset-env'], // plugins: ["@babel/plugin-transform-runtime"] // } } } 复制代码
使用bebal处理实例的原型方法
某些实例的原型方法babel不会进行转换成低级代码,可能会出现兼容性问题。例如数组实例的map、includes等方法。
@babel/polyfill
他是 @babel/plugin-transform-runtime的补丁,只需要安装一下在使用实例原型方法的位置导入进行即可
安装
npm i @babel/polyfill -D
导入
// src/index.js // 导入polyfill import '@babel/polyfill' const arr = [1, 2, 3] console.log("arr是否包含 '1'", arr.includes(1)); console.log(arr.map(e => e + 2)); `` 复制代码
sourceMap
Sourcemap
本质上是一个信息文件,里面储存着代码转换前后的对应位置信息。它记录了转换压缩后的代码所对应的转换前的源代码位置。webpack会对源码进行压缩、编译、高版本代码到低版本代码的转换,会导致处理后代码阅读性很差,无法追踪bug错误,Sourcemap就是解决这一问题的利器。Sourcemap可以在生产环境使用但是极不推荐!!会造成源码泄露的风险
devtool
devtool是webpack配置项,用来控制使用sourceMap的方式
[inline-|hiddeb-|eval-][nosources-][cheap-[module-]]source-map
source-map
devtool:source-map
是最基本的用法
特点:
外部单独一个sourcemap文件
可以提供错误代码准确位置,源代码的错误位置
示例
手写一个bug
const a = require("./a") import './css/index.css' import './less/index.less' import img from './img/avatar.jpg' console.log(a.name); class Person { constructor(opt) { this.name = opt.name } } // 手动声明一个错误(在console.log()后加上()) const zs = new Person({ name: "张三" }); console.log("zs", zs.name)(); 复制代码
配置 webpack.config.js
const path = require("path") const HtmlPlugin = require("html-webpack-plugin") module.exports = { entry: "./src/index.js", output: { path: path.resolve(__dirname, "./dist"), filename: 'bundle.js' }, mode: "production", plugins: [ new HtmlPlugin( {filename: "index.html",template: "./src/index.html"} ) ], module: { rules: [ { test: /\.css$/i,use: ['style-loader', 'css-loader']}, { test: /\.less$/i, use: ['style-loader', 'css-loader', 'less-loader']}, { test: /\.jpg|png|gif|bmp|ttf|eot|svg|woff|wpff2$/i, type: "asset", parser: { dataUrlCondition: { maxSize: 100, // 25kb } }, generator: { filename: 'img/[name].[hash:6][ext]', publicPath: './' } }, { test: /\.js$/, exclude: /(node_modules|bower_components)/, use: {loader: 'babel-loader' } } ] }, // 开启source-map模式 devtool: "source-map" } 复制代码
执行
npx webpack
构建项目,dist目录下生成.map文件
inline-source-map
特点:
内联sourcemap,构建速度快。只生成一个sourcemap
可以提供错误代码准确位置,源代码的错误位置
示例:
修改 webpack.config.js,将 devtool 改为 "inline-source-map"
devtool: "inline-source-map" 复制代码
删除dist目录,执行 npx webpack 重新构建项目,没有 .map 文件所有sourcemap数据会以base64的形式内嵌到 bundle.js 中
hidden-source-map
特点:
外部sourcemap文件
可以提示错误代码错误原因,没有错误位置
不能追踪源代码错误位置,只能提示构建后错误代码位置
eval-source-map
特点:
内联sourcemap,每一个文件都有对应的source-map 都在eval
可以提供错误代码信息,源代码的错误位置
nosources-source-map
特点:
外部单独一个sourcemap文件
可以提供错误代码信息,没有任何源代码的信息
cheap-source-map
特点:
外部单独一个sourcemap文件
可以提供错误代码信息和源代码错误位置(只精确到行)
cheap-module-source-map
特点:
外部单独一个sourcemap文件
可以提供错误代码信息和源代码错误位置
开发环境:
开发环境 要求速度快、调试友好 速度:
eval > inline > cheap > ...
eval-cheap-source-map>eval-source-map
调试:
source-map
cheap-module-source-map
cheap-source-map
建议:
eval-source-map(MVVM框架一般用这个) / eval-cheap-module-source-map
生产环境
生产环境要考虑代码是否要隐藏、调试友好,不能使用内联,内联会让代码体积变大
nosources-source-map
hidden-source-map
建议
source-map / cheap-module-source-map
clean-webpack-plugin
webpack每次构建生成新的dist目录,默认不会清空dist下的文件,这对导致有些缓存文件一直存在,clean-webpack-plugin会在构建项目前清空dist
安装
npm i clean-webpack-plugin -D
配置plugin
// webpack.custom.config.js const CleanWebpackPlugin = require("clean-webpack-plugin").CleanWebpackPlugin module.exports = { plugins: [ new HtmlPlugin( { filename: "index.html", template: "./src/index.html" } ), // new插件实例 new CleanWebpackPlugin() ], } 复制代码
copy-webpack-plugin
webpack默认打包src下的所有资源,目录外的资源可以通过 copy-webpack-plugin 打包进来
安装
npm i copy-webpack-plugin -D
配置plugin
// webpack.custom.config.js { plugins:[ new CopyWebpackPlugin({ patterns: [{ // 将项目的assets目录复制一份到dist/assets中 from: path.join(__dirname, "assets"), to: 'assets' }] }) ] } 复制代码
BannerPlugin
这是webpack内部的一个插件,用来为打包js添加注释、版本等信息
在 webpack.custom.config.js 中
// 导入webpack const Webpack = require("webpack") module.exports = { plugins: [ new Webpack.BannerPlugin("这是一段版权信息") ] }
作者:_光光
链接:https://juejin.cn/post/7017718501099962399