webpack从入门及实战(二)webpack核心概念

it2022-12-28  76

Loaders

说到webpack,自然离不开他的一大堆loaders,没有loaders的webpack只能打包js文件,得益于众多的loaders,我们可以打包各种类型的文件。 loaders实际上可以理解为一种打包方案,webpack本身碰到除了js文件之外的其他类型文件时,就不知道该如何打包了,然后打包就会失败,而loaders则是对应某种文件类型的文件一种单独的打包方案,通过config配置了loaders之后,webpack就可以打包该类型的文件了 打包图片 图片可以使用file-loader来进行打包,在项目目录执行npm i file-loader -D之后进行一些简单的配置就可以了

const path = require("path"); module.exports = { entry: "./src/index.js", output: { filename: "bundle.js", path: path.resolve(__dirname, "myDist"), }, module: { rules: [ { // 要打包的文件类型 test: /\.(png|jpg|gif)$/, use: [ { // 使用file-loader打包 loader: "file-loader", options: { // 生成的文件名是 原文件名_哈希值.原文件扩展名 name:'[name]_[hash].[ext]', // 输出目录是在打包目录下的images文件夹下 outputPath:'images/' }, }, ], }, ], }, };

也可以使用url-loader打包,url-loader会多一个limit配置项,低于该大小的文件会直接被转换成base64,不会再被打包

const path = require("path"); module.exports = { entry: "./src/index.js", output: { filename: "bundle.js", path: path.resolve(__dirname, "myDist"), }, module: { rules: [ { // 要打包的文件类型 test: /\.(png|jpg|gif)$/, use: [ { // 使用file-loader打包 loader: "file-loader", options: { // 生成的文件名是 原文件名_哈希值.原文件扩展名 name:'[name]_[hash].[ext]', // 输出目录是在打包目录下的images文件夹下 outputPath:'images/'// 单位是字节,这里代表大于10kb的文件正常打包,小于10kb的文件转成base64 limit:10240 }, }, ], }, ], }, };

引入2张图片作为测试

// index.js import sayHi from './sayHi' // 小的图片大小为5kb import smallImg from './images/small.png' // 大的图片大小为15kb import bigImg from './images/big.png' // 将图片挂载至页面 const root = document.getElementById('root'); const smallImage = new Image() smallImage.src = smallImg const bigImage = new Image() bigImage.src = bigImg root.append(smallImage) root.append(bigImage) sayHi()

可以看到打包后的images文件夹下只有1张大的图片 但是页面上2张图片均可以正常显示 打包样式 普通的css需要通过style-loader和css-loader来打包

const path = require("path"); module.exports = { entry: "./src/index.js", output: { filename: "bundle.js", path: path.resolve(__dirname, "myDist"), }, module: { rules: [ { test:/\.css$/, use:[ // style-loader将样式挂载至dom节点 'style-loader', // css-loader将css文件整理为字符串 'css-loader' ] } ], }, };

css预编译语言需要额外使用对应语言的loader来多做一次解析,这里以less为示例

const path = require("path"); module.exports = { entry: "./src/index.js", output: { filename: "bundle.js", path: path.resolve(__dirname, "myDist"), }, module: { rules: [ { test:/\.less$/, use:[ 'style-loader', { loader:'css-loader', options:{ // 如果我们引入的less文件里通过@import引入了其他的less文件,那引入的文件默认会直接进入css-loader导致出错 // 这里是配置在进入css-loader之前要经过几个loader,这里前面只有1个less-loader,故配置为1 importLoaders: 1, } }, 'less-loader' ] } ], }, };

webpack打包的样式默认是全局通用的,这也就导致会出现样式穿透的问题,我们可以通过modules配置来解决

const path = require("path"); module.exports = { entry: "./src/index.js", output: { filename: "bundle.js", path: path.resolve(__dirname, "myDist"), }, module: { rules: [ { test:/\.less$/, use:[ 'style-loader', { loader:'css-loader', options:{ importLoaders: 1, // 开启模块化样式 modules:true } }, 'less-loader' ] } ], }, };

引入样式时候就需要发生一些改变

// 非模块化引用 import './index.less' // 模块化引用 import style from './index.less' // 使用模块化之后的样式 el.className = style.xxx

还有诸多其他各种类型的loader,可以让我们完成各种各样的打包需求 附上文档地址 在需要打包不同的文件时,查阅文档即可

Plugins

plugins(插件)可以在webpack运行到某些时刻时候,帮你做一些事情,比如clean-webpack-plugin可以在打包开始前帮我们清理掉上一次打包后的文件,而html-webpack-plugin则可以在打包后自动生成一个引用了打包完成的js文件的html文件,并且支持指定模板

const path = require("path"); // 引入插件 const { CleanWebpackPlugin } = require("clean-webpack-plugin"); const HtmlWebpackPlugin = require("html-webpack-plugin"); module.exports = { entry: "./src/index.js", output: { filename: "bundle.js", path: path.resolve(__dirname, "myDist"), }, module: { ... }, // 使用插件 plugins: [ new CleanWebpackPlugin(), new HtmlWebpackPlugin({ // 自定义title title:'html模板', // 自定义文件名 filename:'index.html', // 自定义使用模板的路径 template:'src/public/index.html' }), ], };

devServer

webpack-dev-server 提供了一个简单的 web 服务器,并且能够实时重新加载,安装后在webpack.config中做简单的配置即可使用

const path = require("path"); const { CleanWebpackPlugin } = require("clean-webpack-plugin"); const HtmlWebpackPlugin = require("html-webpack-plugin"); module.exports = { entry: "./src/index.js", devServer:{ // 告诉devServer在哪里查找文件 contentBase: './myDist', // 启动后自动浏览器打开 open:true, // 端口 port:4396 }, output: { filename: "bundle.js", path: path.resolve(__dirname, "myDist"), }, ... };

但是这里有一个坑,我用的是webpack5.x版本,运行后报错了 查阅了文档的各种配置依旧没有搞清楚问题所在,遂求助百度 原来是版本的问题 webpack、webpack-cli、webpack-dev-server是需要版本匹配的 找到了一个可以运行的版本 “webpack”: “^4.43.0”, “webpack-cli”: “^3.3.12”, “webpack-dev-server”: “^3.11.0” 安装代码

npm i webpack@4.43.0 webpack-cli@3.3.12 webpack-dev-server@3.11.0 webpack-dev-server -D

更新版本之后,dev-server就可以正常使用了 但不得不说webpack的文档实在是难以恭维…

模块热替换(HMR)

模块热替换(Hot Module Replacement 或 HMR)是 webpack 提供的最有用的功能之一,它允许在运行时更新各种模块,而无需进行完全刷新。 现在我们的dev-server在运行的时候,更改代码虽然效果会更新,但是这个更新是直接刷新页面来更新的,而使用了HMR之后,就可以做到不刷新页面直接更新了 功能的启用很简单,插件也已经内置在webpack内部无需额外安装

const path = require("path"); const { CleanWebpackPlugin } = require("clean-webpack-plugin"); const HtmlWebpackPlugin = require("html-webpack-plugin"); // 引入webpack const webpack = require("webpack") module.exports = { entry: "./src/index.js", devServer:{ contentBase: './myDist', open:true, port:4396, // 启用模块热更新 hot:true }, output: { filename: "bundle.js", path: path.resolve(__dirname, "myDist"), }, plugins: [ new CleanWebpackPlugin(), new HtmlWebpackPlugin({ title:'html模板', filename:'index.html', template:'src/public/index.html' }), // 使用插件 new webpack.HotModuleReplacementPlugin() ], };

这样就可以实现HMR了,接下来修改样式部分的代码,页面可以做到不刷新直接更新,但js部分的代码还是会刷新的,如果想做到js代码不刷新,则需要通过HMR的api accept来手动配置相应的逻辑

首先要在配置文件里加多一个hotOnly配置

const path = require("path"); const { CleanWebpackPlugin } = require("clean-webpack-plugin"); const HtmlWebpackPlugin = require("html-webpack-plugin"); // 引入webpack const webpack = require("webpack") module.exports = { entry: "./src/index.js", devServer:{ contentBase: './myDist', open:true, port:4396, // 启用模块热更新 hot:true, // 只使用热更新来更新页面,不会自动刷新页面来更新 hotOnly:true }, output: { filename: "bundle.js", path: path.resolve(__dirname, "myDist"), }, plugins: [ new CleanWebpackPlugin(), new HtmlWebpackPlugin({ title:'html模板', filename:'index.html', template:'src/public/index.html' }), // 使用插件 new webpack.HotModuleReplacementPlugin() ], };

然后在引用了其他模块的代码里加上对应处理的逻辑 这里sayHi添加了一个id为hi的dom节点

// index.js import sayHi from "./sayHi"; if (module.hot) { module.hot.accept( "./sayHi", // 监听具体哪个模块的改变 function () { //手动的删除原来saiHi添加的dom节点,然后重新执行saiHi方法 console.log('这样就可以不刷新页面重新加载js文件了') document.getElementById('root').removeChild(document.getElementById("hi")); sayHi(); } // 用于在模块更新后触发的函数 ); } sayHi();

但是如果引入的模块比较复杂,这里的配置也会随之复杂很多,所有具体的情况还是要看业务的需求,刷新一下页面其实也问题不大…

Babel

通常我们会配置babel来帮我们将es6的语法转换成es5的语法,来避免一些奇奇怪怪的问题(ie说的就是你) 首先需要安装babel以及它的loader

npm install --save-dev babel-loader @babel/core @babel/preset-env

在index.js里引入babel/polyfill

// 注入低版本浏览器不支持的一些es6的新方法,诸如promise,map等 import "@babel/polyfill"

然后在loader里面进行简单的配置即可

module:{ rules:[ { test: /\.js$/, exclude: /node_modules/, loader: "babel-loader", options: { presets: ["@babel/preset-env"], }, }, ] }

这时候再进行打包,就可以看到打包后代码里es6的语法已经转换成es5的语法了 也可以做更多的一些适配

module:{ rules:[ { test: /\.js$/, exclude: /node_modules/, loader: "babel-loader", options: { presets: [["@babel/preset-env",{ // 只将用到的es6语法整合进去,没有用到的不整合,减少打包后的大小 useBuiltIns:'usage' }]], }, }, ] }

这样配置之后就可以把引入的babel/polyfill去掉了,他会在需要的时候被自动引入 更多的配置可以参考babel的文档

SourceMap

SourceMap是一种映射关系,可以帮助我们在代码出现错误的时候定位到错误的位置,他的配置对应的是config中的devtool项

先将它设置为none

const path = require("path"); const { CleanWebpackPlugin } = require("clean-webpack-plugin"); const HtmlWebpackPlugin = require("html-webpack-plugin"); module.exports = { entry: "./src/index.js", devtool:false, output: { filename: "bundle.js", path: path.resolve(__dirname, "myDist"), }, ... }; // index.js // 在代码里添加一句错误的语句 console.llog('错啦')

打包是可以正常执行的,但是我们打开页面后控制台会报错 点击查看source后会发现对应的地方是打包之后的代码位置,而不是我们源代码的位置,这对于我们定位错误没有太大帮助,而想要让他定位到我们源代码的位置,就需要配置SourceMap

const path = require("path"); const { CleanWebpackPlugin } = require("clean-webpack-plugin"); const HtmlWebpackPlugin = require("html-webpack-plugin"); module.exports = { entry: "./src/index.js", devtool:'source-map', output: { filename: "bundle.js", path: path.resolve(__dirname, "myDist"), }, ... };

更改devtool配置再次打包后,点击查看source就会帮我们定位到源码的位置了,打包的文件也会多一个map文件 官方的source-map有很多的配置 inline代表不生成map文件,将map文件以base64格式打包至js文件内 cheap代表是否定位错误出现在代码的第几行和第几个字符,带上cheap代表不定位出现在第几个字符,只定位第几行 module代表是否定位一些模块的错误 eval代表以js的eval形式来执行代码,不过这种方式在代码量较大的环境下可能无法准确定位错误 开启SourceMap会造成性能方面的影响,所以通常我们的做法是在开发环境下开启source-map,正式上线时候关闭 我个人会在开发环境下使用’cheap-module-eval-source-map’这种方式提示的错误比较全面,同时打包的速度也比较快 线上环境通常是直接设置为false不开启,如果出了问题需要迅速定位错误的话,使用’cheap-module-source-map’可以帮助我们能准确定位错误

关于webpack一些核心的理念基本已经介绍完毕,后续的博文会介绍webpack进阶的一些高级概念

最新回复(0)