10分钟看完,保证让你收获颇丰
话不多说了,下面开始吧!
webpack是一个模块打包工具,可以把互相依赖的 HTML、css、javascript 以及图片、字体等资源文件经过一系列的处理,打包成静态的前端项目。
我们在传统的网页开发项目中,需要通过在 HTML 中引入大量的 javascript、CSS 等文件,不仅可能会导致命名冲突,还会使页面体积变大。因为如果是第三方库,需要加载所有的代码。
而在 node.js出现之后,javascript项目支持通过 require 进行模块化开发了,并且支持 npm 。方便的管理依赖借着 node.js 和浏览器js的一致性。前端项目开始在 node.js下开发,完成之后把代码构建成浏览器支持的形式。
对于 React 或 Vue这种组件化的开发方式,因为有很多分散的文件,那么就特别需要这样的构建操作。
Webpack 就是进行这一步构建操作的,把 node.js 模块化的代码转换为浏览器可执行的代码,它提供了 import 和 export 的ES6模块化的语法支持。然后通过分析代码中 import 导入的依赖,把需要的代码加载进来。然后通过分析代码中的 import 导入的依赖,把需要的代码加载进来。在 webpack 中,任何文件都可以通过 import 导入,只要有对应的 loader就可以了。在打包过程中还可以通过插件干预打包过程,例如剔除不必要的代码,形成体积更小的项目。
Webpack 是一个基于 node.js 的框架,所以说在开发之前先确保你的电脑里边已经安装了 node.js 。
接下来我们创建一个 node.js项目,在一个合适的目录下使用 mkdir blog
创建一个blog 项目。这里要展示一个简单的博客列表,利用 webpack 来对它所涉及的一些依赖进行打包。创建完成之后通过cd blog
进入到这个目录里边。
之后初始化 node.js 项目,这里我使用yarn依赖管理工具,你也可以使用 npm 要初始化项目使用 yarn init -y
参数,意思就是生成的 package 的 JSON 文件,所有的配置项都取默认值,它就不会一条一条的去询问了。
创建完成之后,使用yarn添加webpack依赖
yarn add webpack-cli --dev
是把 webpack 相关的依赖按安装到开发者依赖里边。因为 Webpack 只有在开发的时候才会用到它最后打包成的代码里边不需要再使用 Webpack 了。
这里使用的是内测版的 VSCode, 所以这里的命令是 code -insiders .
。如果你使用的是正式版的 ,那么就是 code 命令,然后使用点号打开当前目录。打开之后,我们先新建一个 src 目录,这里边就会存放我们要写的源代码线信件,一个 index.js 文件。这里边我们先编写一些简单的测试代码,例如使用一个 console.log 打印出 hello world 字样。接下来在项目的根目录下创建一个 index.html 文件。在它里边我们会加载 index 的js这里使用快捷命令 HTML :5
,然后按 tab 补全。在 body 里边我们使用 script 来加载 index.js 这里 src 属性设置为"./src/ index.js"
,编写一个测试的文本,例如使用一个
好,现在我们使用 live sever
来运行一下。 live sever是 VSCode 的一个插件,它是用来实时预览 HTML 文件的,这个可以通过插概念市场来安装。我们在文件上面右击选择 open with live sever它会自动帮我们打开浏览器,这里可以看到 hello world 这个字样就打印出来了。我们看一下开发者工具,看一下 console 这个面板,可以看到 hello world 的这个字符打印出来了。
虽然现在我们还没有用到任何的 import
语句。但是我们先熟悉一下 webpack 的执行流程,打开 vs code 命令行,运行 npx webpack
这样就执行了打包命令。
npx
可以让我们直接运行 node modules 下边安装的库的自带的命令行,而不用写 node modules 这一串的相对路径。
打包完成之后,我们可以看到项目里边多了一个 dist 的目录,然后下边它有一个要main.js 我们打开它可以看到它的代码跟咱们的 src/index 下边的是一样的,因为我们没有用到任何 import 来导入其他的依赖,所以说这两个代码才是一样的。不过这里我们就演示了 Webpack 的打包过程。
接下来我们把 index.html文件里边的 index.js 这个引用改成使用"./dist/main.js"
因为后面我们要使用的都是打包后的js文件。
接下来我们试试在有依赖的情况下打包后的代码是什么样的。在 src 目录下再新建一个 data.js文件,里边导出一个函数来返回一串示例的博客列表数据,使用 export function getBlogPosts
来导出 getBlogPost 函数,然后 return 返回一个数组。
数组里边就是示例的博客内容["博客1","博客 2 ","博客 3"]
。 之后在 index 里边把 console.log 改成使用getBlogPost 函数,它调用的返回值。这里 vs code 会自动帮我们把getBlogPost 从 data 这个文件里边导入进来。
再运行一下 npx Webpack 看一下结果。
打开 man.js 可以看到这个代码就简化了,直接 cancel.log 这个博客列表数据了,说明这个 webpack
智能的判断了代码的逻辑,通过 import 把代码拿过来,然后分析代码的执行逻辑。最后得出我们只打印了这个博客列表数据,就生成了这样的一个简化的代码。好我们在浏览器里边试一下,看到这里打印出来了三个博客列表数据。
在配置文件里边可以修改入口的js文件,也就是说从哪个文件里边开始寻找 import 依赖的路径。还可以配置出口,也就是说最后生成的js文件的一些信息。然后能够通过 loader加载不同类型的文件,再通过 plug ins 在打包的过程中对代码进行一些优化或者其他的操作。我们来测试一下,看看把打包后的文件名修改一下。
先新建一个 webpack.config .js文件。然后这里边使用的是 node.js 的模块化语法,使用 module.exports 导出一个空的对象{}。在这个对象里边配置 webpack 的配置项就可以了。首先我们添加一个mode属性,因为我们之前打包的时候命令行提示了,这个 mode 没有设置 Webpack 默认取的是 production 但是我们也可以设置为 development 开发环境,这样的话在开发环境下 webpack 打包的代码会不太一样,方便我们开发者进行调试。
所以说这里我们把 mode 先设置成 development 接下来我们看一下entry
这里我们还是取得它的默认值,但是我们可以把它拿出来看一下是怎么设置的。这里可以直接把咱们的入口文件路径给它写上entry:"./src/index.js"
接下来配置打包后的文件名,我们使用 output 配置项给它传递一个对象属性,它里边我们配置 filename 这个就是打包后的文件名。例如说我们改成叫做 dist.js 然后存放目录也可以改一下。现在是 dist 如果要改成别的话,可以给它配置一个 pass 属性。然后这里我们可以利用 node.js 提供的 pass 库来获取 webpack.config.js所在的目录。
moudule.exports = {
mode:"development",
entry:"./src/index.js"
output:{
filename:"dist.js",
path:_dirname + "/dist"
},
}
基于它再去找到一个新的目录来存放我们打包后的文件。这个 pass 工具需要导入进来。我们这里使用const 的 pass = require pass 这样就可以使用了。
const path = require("Path")
例如说我们还是把它放到 dist 里边,当然你也可以改成其他的文件夹。这里就是演示一下它的用法。我们可以调用 pass.resolve resolve 接收多个参数,可以指定多级目录。例如第一级我们设置为下划线,下划线 DR name 然后第二集设置为 dist 这样它就会放到 dist 目录下边了,你也可以把它改成别的名字。
path:_dirname + "/dist" //改写成
path:path.resolve(_dirname,"dist"),//可以指定多级目录
运行 npx Webpack 打包看一下。结果可以看到 dist 目录下边多了一个 dist.js 文件,这里面的代码多了许多,因为现在我们改成了开发模式。接下来我们把 index.html中的js文件引入,改成使用 dist.js 之前的 main.js 就不需要了给它删除。
接下来我们使用 Webpack loader来加载css文件,我们先把 HTML 中的 h1 删除。然后 index 的js里边的代码我们把它改一下,根据获取出来的博客列表数据生成一组ul 和li元素展示。我们把 getBlogPost 函数返回的结果保存到一个常量blogs = getBlogPost()
,然后创建ul元素`const ul=document.createElement(“ul”)。接下来遍历 blocks 这个数组。
blogs.forEach((blog)=>{
const li = document.createElement("li");
li.innerText = blog;
ul.appendChild(li);
});
document.body.appendChild(ul);
这里代码的意思就是说我们遍历 blojs 对于每一个博客列表数据创建一个 li 元素,然后给 li 元素内部的文本设置为 blog 元素代表的文本值,然后把它添加到 ul 元素中。最后我们使用 document.body.append child ul 把这个 ul 放到 body 元素里边。这样我们就在js里边创建好了 HTML 元素。
测试一下,运行 npx webpack 先打包,然后回到浏览器里边看一下。可以看到这三个博客列表项数据就都显示出来了。下面我们就要加载css,先创建css文件,在 src 下新建一个 style.css 文件。因为它里边的代码不是重点,所以这里我直接把它复制过来,就是一些简单的样式。
*{
padding:0;
margin: 0;
font-family: sans-serif;
}
body{
display:grid;
place-items;center;
list-style:none;
}
li{
padding: 12px;
}
img{
max-width: 500px;
}
调整之后,我们在 index.js里边把它导入进来,这样它就能够充分利用 webpack 的特性了。如果在 index HTML 中导入,那么就和普通导入css的方式没有区别,就不能对css样式进行一些优化或者其他的操作了。那么这里在 index.js里边,我们使用 import 然后指定css的路径,也就是当前目录下的 style.css 这样就把它导入进来了。
import "./style.css";
我们先试试不使用 loader的话打包会出现什么样的效果?运行 npx Webpack 可以看到题是出错了。这里他就告诉我们了,我们可能需要一个合适的 loader来加载这样的文件类型。好要加载css文件,我们需要安装两个 loader使用yarnadd --devstyle-loader css-loader
。
这里几乎所有和 webpack 有关的依赖都需要安装在开发者依赖里边。因为在打包后我们都不需要它了,这里安装一个是叫 style-loader然后另一个是css-loader好安装完成之后,我们需要在 webpack 的配置文件里边去配置它。
对于 loader在 webpack 里边,我们需要先去匹配以什么样的扩展名结尾的文件去应用什么样的 loader那么在这里我们添加一个 module 配置项,然后在里边配置 rules 配置项,它的值是一个数组,数组里边的每一个元素都对应一个 loader的配置。
mudules:{
reles:[{
test:/\.css$/i,
use:["style-loader","css-loader"],
},
],
}
每个 loader的配置都包含匹配扩展名以及使用哪些 loader的相关的选项。例如说我们这里配置cssloader那么就给 rules 传递一个对象,然后配置 test 属性,它的值是一个正则表达式,用于匹配文件名。这里我们匹配所有以点css结尾的文件。那么这里我们先传递一个正则表达式,然后使用反斜杠点来匹配文件名里边的点号。因为点号在正则表达式中有特殊的含义,所以需要反斜杠进行转译,然后写上css这个扩展名,再接着使用一个 $符号来匹配文件名的结尾。后边我们可以加上一个i
来忽略大小写,再给它来传递一个use
属性。
就是说使用哪些 loader这里我们就使用刚才安装好的 style loader和cssloader好,现在我们打包一下再看一下。运行 npx webpack 可以看到这个打包成功了。我们在浏览器上看一下,可以看到相应的样式也都应用上了其他的像是sass 等css预处理文件,也可以通过相应的 loader来这样配置。
对于图片等静态资源的文件, webpack 原生的支持,所以说就不再需要额外的安装 loader了。那么我们这里可以直接在 rules 里边再添加一个配置对象,然后设置 test 的属性,这里匹配所有的图片文件。那么我们使用反斜杠点,然后使用一个小括号,里边我们要匹配多种图片的扩展名,例如 PNG 然后使用竖线表示或 SVG 或 gpg 或 GP EG 或 GIF 然后加上 dollar 匹配扩展名。这个结尾再加上一个 I 表示忽略大小写。然后这里因为它是使用内置的 loader所以我们这里使用 tab 属性,我们使用 asset 斜杠 resource 这样的值,这样配置就可以了。
mudules:{
reles:[{
test:/\.(png|svg|jpg|jpeg|gif|bwm)$/i,
use:"assets/resource",
},
],
}
接下来我们测试一下,先添加一张测试图片,在 src 下新建一个 SS 目录,再在它里边新建一个 images 目录。这里我直接把图片 copy 过来,然后在 indexjs中创建一个 image 元素,把图片展示出来。这里我们先使用 import 把图片导入进来。这里我们先给它一个名字,例如叫做 HeroImageimage 然后 from 点杠 assets 下边的 images 下边的 hyarno.gp EG 这样导入之后, HeroImageimage 表示的就是打包后的图片的真实的路径,可以直接用于 image 元素的 src 属性上边。
接下来我们创建 image 元素const image = document.createElement("img")
然后设置它的 src 属性值为我们导入进来的image.src = HeroImage;
。 最后把它添加到 body 里边,这里我要把它放到 body 的开头。那么我们就调用 document.body.prepend (image);
这个创建好的元素传递进去,这样就可以了。
我们打包一下 npx Webpack 回到浏览器看一下这个图片就正常的加载出来了。我们还可以看到在打包之后,这个图片的名字也变成了随机的字符串。
现在我们的 index HTML 是手写的,非常容易出错,需要同步 src 下边的路径。而 webpack 有一个插件可以自动生成 HTML 文件,这样就不需要我们再手动去编写它的代码了。
这个插件叫做 HTML web peg plug in
我们先安装它一下运行 :
yarnadd html-webpack-plugin --dev
这里忘了我们需要把它添加到开发者依赖中,后面还需要加上--dev
。
打开 webpack.config.js里边,我们使用这个插件,首先先导入进来,把它的导入进来的结果放到这个常量里边。
const HtmlWebpackPlugin = require("html-webpack-plugin");
最后在配置对象里边我们添加一个 plugins 配置项,它的值是一个数组,在里边我们可以加载多种插件。上边 HtmlWebpackPlugin它导出的是一个构造函数,我们使用 new 调用它,这样就加载好了这个插件。
plugins: [new HtmlWebpackPlugin()],
好,我们运行一下 npx Webpack 可以看到 diss 的目录下多了一个 index.html 这个就是它自动帮我们生成的 HTML 代码,我们可以直接使用 live sever运行一下,它可以看到展示的效果是一样的。那么之前的这个 HTML 文档,我们就可以把它关闭了。
不过看一下这个 HTML 的标题是默认的 Webpack App 但是这个 webpack 不过这个 HTML webpack plug in 支持我们传递参数来自定义 HTML 代码的生成。例如说我们这里可以传递一个 title 属性,设置一下网页的标题,例如叫做博客列表,之后再重新打包一下。回到浏览器可以看到这里网页的标题就变成了博客列表。我们在开发前端项目的,有的时候会使用新的 javascript 的特性,但是要兼容低版本的浏览器。那么我们可以利用 babel这个工具来转译咱们的js代码, Webpack 也支持相应的 loader我们看一下来怎么实现。
yarnadd --devbabel-loader @babel/core @babel/preset-env
安装完成之后我们来使用这个 loader在 webpack 配置项里边,我们再新添加一个 loader的配置,这里我们要匹配js结尾的文件,那么使用 test 匹配点杠js这个扩展名。然后使用 exclude 把 node modules 这个目录给去掉,这样它就不会转译 node modules 下面的代码,再使用一个 use 来配置使用哪些 loader这里我们使用对象形式,因为我们需要给 loader传递一些自定义的配置。这里使用 loader配置项指定要使用哪个 loader我们这里使用 babel loader然后 options 来给这个 loader传递一些配置 precise 使用 at babel 斜杠 preset env这样就能够自动转译代码了。为了方便我们看打包后的代码,我们可以在 webpack 的配置项里添加一个 devtool 配置项,把它设置为 inline-source-map 这样方便我们查看打包后的源代码。
devtool: "inline-source-map",
{
test: /\.js$/,
exclude: /node_mudules/,
use: {
loader: "babel-loader",
options: {
presets: ["@babel/preset-env"],
},
},
},
我们运行 npx webpack 打包一下看一下。先注意一下,这里我们 index 的js里使用了这个箭头函数来作为 forEach 的回调函数,但是在 ES6之前是不支持箭头函数的。那么这样在打包后应该它会转换成一个普通的函数。我们看一下是不是这样,现在已经打包完毕了。打开 diss.js 我们搜索一下斜杠 src 斜杠 index.js 看一下它里边的代码。这里可以看到箭头函数就转换成了普通的匿名函数,这种形式说明咱们的 babyloader就生效了。
这样的话可以减少打包后的文件体积。它需要一个叫做 tyarnsor webpack plug in 的插件。我们这里先安装一下 :
yarnadd --dev terser-webpack-plugin
安装完成之后,我们在 Webpack.config.js里边配置它,首先导入进来,把它保存到一个叫做 TerserPlug in 的常量里边。这里使用 require 把咱们的安装好的terser-webpack-plugin 加载进来,然后再导出的配置对象里边添加一个 optimization 配置项 minimize 是否要压缩,把它的值设置为 true 然后 minimizyarn用什么工具来压缩?我们这里使用安装的 tensorplugin 这里直接调用 new tyarnsyarnplug in 这个构造函数就可以了。
const TerserPlugin = require("terser-webpack-plugin");
optimization: { //在module.exports中写入
minimize: true, //是否压缩
minimize: [new TerserPlugin()] //使用什么工具压缩
},
好,我们测试一下,运行 npx webpack 再打开 dist.js 可以看到他把所有的空白都去掉了,并且这些变量什么的都使用了。简化的形是后边还有一串加密的字符。
现在我devsever开发服务器们在开发的时候需要每次在改动js之后都要重新打包。 Webpack 提供了一个 devsever开发服务器,它可以在启动之后,如果我们修改了js代码,它就会自动重新打包并刷新页面。我们来看一看怎么来使用这个工具。首先先安装它:
yarnadd --dev webpack-dev-sever
接下来我们需要指定 devsever要从哪里去加载代码。我们打开webpack.configjs在配置项里边再添加一个 devsever配置项,给它的值设置为一个对象,然后设置 static 给它指定咱们的 dist 目录。
devServer:{
static: "./dist",
},
在接下来为了方便我们运行开发服务器,我们在 package.json里边再新添加一个 script 。例如我们可以使用 yarnstart
来启动开发服务器,那么这里我们添加一个 scripts 配置项,然后在里边配置 start 命令值为 webpack serve – open 这样就能够启动 webpack devsever并自动打开浏览器。
"Scripts":{
"start": "webpack serve --open"
},
好我们运行一下试试在命令行里运行 yarn start 可以看到它自动地帮我们把浏览器打开了,并且展示的效果和以前是一样的。好我们再改一下 indexjs里边的代码,看一下它会不会自动的帮我们重新打包。例如我们在添加完图片之后,再添加一个 h1 展示一些测试的文字const h1 = document. createElement("h1");
然后设置它的 innertext 比如说这里叫做博客列表,然后把它添加到 document.body 里边。好我们回到浏览器里边再看一下,可以看到这个 h1 元素就添加进来了。
h1.innerText = "博客列表";
document.body.prepend(h1);
现在我们打包后的文件这个 dist.js每次都是一样的,但是浏览器会根据这个文件名进行缓存。一般我们为了避免浏览器进行缓存,我们会给文件名加上一串随机的字符,每次更新之后都改为新的字符。
我们看一下怎么来实现打开 webpack.config.js这里我们需要配置 output 这里 filename 我们配置的是写死的 dist.js但是我们也可以把它改成一个带有 hash 也就是不重复的字符串的文件名。这里我们可以把它的值改成方括号 name 点方括号 content hash.js 这里 name 也可以写成写死的。不过写成方括号 name 的话, webpack 会自动把它替换成 main 就是默认的之前的文件名。然后 content hash 会每次根据文件的内容进行 hash 计算,得出一串不重复的字符。这样的话如果打包的时候文件内容发生了变化,那么这个 content hash 也会自动发生变化。
output:{
filename: "[name].[contenthash].js"
},
好我们来测试一下,先停止开发服务,然后运行 npx Webpack 可以看到它生成了这样的带有随机字符串的文件名。我们再改一下 indexjs里边的代码,比如说把这个 h1 这一段给去掉。在运行 npx while pack 在之前我们看一下它这个是 5a94 开头 FF 8e 结尾的,再运行一下,看看它生成什么样的。这里可以看到它生成了新的这个文件,这一串随机的字符串也修改了。好,我们把这些先删除,然后把音带解js的代码还原。这里我们先继续使用之前的 dist.js文件名。
接下来我们看一下如何给导入的路径设置别名。有的时候js文件所在的目录可能会嵌套的比较深,要引入其他的目录,下边的js文件需要使用很多点杠来访问这个相对路径。但是 web pad 可以让我们指定一个路径别名来把这一串相对路径给替换掉,这样就少写一些字符串。
我们来测试一下这个例子,我们就随便测试一下,也不是特别有实际意义的。我们在 SSC 下新建一个 utils 目录,在里边再新建一个 date.js 在里边我们导出一个函数,把 date 对象转换成年份-月份-日子
这样的字符串。这里我们 export 一个 function date to STR 然后接收 date 参数,返回转换之后的字符串。它里边的代码不重要,这里我就直接复制过来了。
之后我们添加一个嵌套层次比较深的目录,在 sic 下新建一个 test 然后在里边再新建一个 date 目录,再在 date 下边我们新建一个 print date.js 文件。这里边它会调用之前我们创建的 date 的js里边的 date to STR 来打印日期。
这里我们使用 console.log 然后调用 dateToStr 传递当前日期 new date 这里可以看到我们导入的时候使用了两极相对路径
import {dateToStr} from "../../utils/date"
console.log(dateToStr(new Date())
接下来我们需要在 indexjs里边再把它导入进来。这样的话 webpack 才会发现这个文件。我们先把其他没用的js文件关闭。在 index 的js中,我们导入 import ./test 下边的 date 下边的 printDate 就js这个文件。
import "./test/date/printDate";
好,我们配置路径别名,打开 Webpack.config.js再添加一个 resolve 配置项,它的值也是一个对象。然后添加 ideas 配置项,它里边的属性名就是路径别名值就是真实的路径。比如说我们把 utils 这个目录它的别名设置为 utils 真实的目录,我们使用 pass.resolve 然后传递下_dirname 第二个参数传递 src 下面的 utils 把这个真实的路径设置为 utils 的这个别名的值。
resolve: {
alias: {
utils: path.resolve(_dirname, "src/utils"),
}
},
下面我们就可以在 printDate.js里边把这两个点杠去掉了(…/…/),直接使用 utils 这个别名,最后它就会转换成真实的地址。我们运行一下 npxy pack 这里只要打包没有错误,就说明咱们这个路径别名就生效了。
有时候我们可能需要分析一下打包后的结果,哪个文件占的体积比较大,再进行下一步的优化。那么 Webpack 也有一个可视化的打包分析工具,叫做 webpack-bundle-analyzer我们先安装一下,看看它怎么使用。
yarn add --dev webpack-bundle-analyzer
安装完成之后,在 webpack.config.js里边把它导入进来来,把它放在一个常量中。const bundle analyzerplugin 然后= require 我们刚才安装的webpack-bundle-analyzer它是一个插件,所以我们把它添加到 plugins 数组里边就可以了。
const BundleAnalyzerPlugin = require("webpack-bundle-analyzer")
pulugins:{
new BundleAnalyzerPlugin.BundleAnalyzerPlugin(),
}
这里调用 new bundle analyzyarnplugin 这里注意的是 bundle analyzyarnplug in 这个导入进来的是一个对象,我们在需要访问它里边的同名的 bundle analyzyarnplug in 这个构造函数才可以,或者我们在导入的时候就可以把它这个直接访问出来,例如再加上个点号,然后访问这个构造函数。这样的话我们可以在 plugin 里边直接调用它就可以了。
这个工具会在我们运行 npx webpack 的时候自动打开,我们现在就运行一下。可以看到它自动打开了这个可视化的工具,可以看到是咱们的 indexjs文件所占的最大。虽然它的体积并不是特别的大,但是在其他的所有的文件里边它占用的算是比较大的,所以说它在这个可视化工具里边占的相当大的一个部分,其他的都是比较小的。
好了,这个视频我们介绍了 webpack 为什么使用它以及使用了这样一个示例,演示了它常用的配置项的用法。随着项目规模的增加,你可能会遇到更多的 webpack 配置项 loader和 plugins 这些 webpack 官网都有详细的介绍。