webpack说难很难,说简单也简单,难主要是loader
和plugin
模块
本期文章主要介绍webpack的使用和作用,以后再来聊原理
webpack是目前用的最多的构建工具,它不是个框架,webpack就是个打包工具,给项目做压缩处理,此前我介绍过vite
和cli
,它们也是构建工具或者打包工具。webpack是个老牌的工具,
常见的构建工具有这些:
vite:很新,vue作者打造的
gulp:国内也用的非常多
rollup:国外用得非常多
webpack是众多构建工具最出色的,功能最强大,用户基数也是最大的,其实cli
就是webpack
构建的,其实你学一个最新的vite
和一个强大的webpack
就够了,其他构建工具要用的时候你也一定从容不迫,都是一类型的东西
为什么项目需要打包?
假设我们项目写完了,我们现在需要把他部署到服务器上去,部署服务器上的目的就是别人也可以访问,就是部署到公网上去,我们连接的wifi
是局域网,如果都是同一个wifi
那都可以访问你的资源,前提是你允许访问,服务器一定是连接了公网的
假设我们的项目完成后有个60M的大小,其实非常大,所以我们希望对代码进行一个删繁就简,去除掉伪代码,比如用于调试的console.log
,就算去掉这些东西,也不会节省夺少空间,我们这么做的原因是服务器的资源非常的昂贵。企业稍微做大一点都是自己做一个服务器,比如内网穿透啥的,让它可以连接到外网,运维来管。初创型的公司一般都是租第三方的服务器,一年动辄几十万,上百万。
所以我们需要进行压缩打包处理,这里的打包和360打包是两码事,后者只是换了个文件格式,前者是去除掉伪代码,去掉代码的换行等等,密密麻麻。尽可能地减少服务器资源的浪费。
当我们使用npm
包管理工具进行打包npm run build
的时候,你会发现你的项目目录下多了个dist
文件夹
其实这个项目结构最终就是被转成了html
,js
,css
。assets
里面都是些js
和css
文件,我们随便看一个js
文件,代码密密麻麻
这个项目我其实并没有用到这么多代码,最后这么多的原因是因为我用了vantUI
框架,引入了它的源码,这些代码都是给机器看的
如果没有跨域等的问题,理论上我们是可以直接运行这个index.html
文件的,实际上css
等资源必须当成服务资源进行返回
体验webpack带来的效果
现在给到一个情景:我们在项目根目录下新建一个public
文件夹,里面放一个index.html
,然后在根目录下新建一个文件夹src
,src
下新建一个tools
文件夹,写一个add.js
,add.js
通过ESModule
语法引入到src
下的main.js
,然后在main.js
中调用add.js
函数,去打印值,最后引入到index.html
模块化引入这里就不过多展示,语法我在node那期文章都讲了:初识node | 模块化 | yrm换源 | nvm切换node - 掘金 (juejin.cn)感兴趣可以看看两种语法的区别,都非常重要
按道理这么做是没问题的,但是浏览器其实是读不懂es6的模块化语法的,以前是这样的,现在更新了,只要在引入的时候加个属性type
即可
xml
<script type="module" src="../src/main.js"></script>
现在是这样的,浏览器可以读懂模块化语法,我现在就要回到以前,浏览器就是读不懂模块化语法。
这里说浏览器读不懂es6的模块化语法仅仅是为了假设,现在浏览器基本上都可以读懂es6了,但是
es7,es8,es9
它还是会读不懂
浏览器更新很慢,跟不上es的版本,所以我们有没有一种方法,让浏览器可以读懂新版本的语法
现在我们还是把
type="module"
给删掉,模拟下就是让浏览器读不懂。
另外我们也把main.js
的引入给删掉,因为webpack
是可以帮我们背后做一个引入的,用过cli
的就清楚,不需要手动引入js
。不过vite
要引入
这就是webpack
的作用了,它可以把es6,es7,es8
等高版本js
给降级成低版本的语法。当然不全是webpack
的作用,它降不了的可以请帮手
配置webpack
先初始化项目
csharp
npm init -y
生成package.json
,有这个就可以安装其他内容
首先安装webpack
,webpack
既然是个构建工具,所以我们在生产环境用不上他,我们只在开发环境用,所以是-D
css
npm i webpack webpack-cli -D
这个时候项目目录下就会出现node_modules
,这个东西就是放你安装的东西的源码的,会比较大。还会有个package-lock.json
文件
手动在项目根目录下创建一个webpack.config.js
文件。这里是手动配置webpack
其实以后工作中大概率是不会让你去手动配置一个webpack
的。那些脚手架已经可以弄得非常完美了。
但是面试官可能上来会问你一句:vue-cli
的执行过程是什么?
我们来到webpack的中文文档下,看下webpack的主要内容模块:概念 | webpack 中文文档 | webpack中文文档 | webpack中文网 (webpackjs.com)
这几个模块会了,你就算是熟悉webpack
了。loader
和plugin
会比较难一点
webpack
由于是node
写的,所以你可以在里面使用node
的内置模块,其实vite
也是node
写的
webpack.config.js
我们来到项目根目录下新建一个webpack.config.js
文件,开始配置该文件
先引入path
模块,因为要用上path身上的方法
ini
const path = require('path')
然后再抛出一个对象,这里面东西比较多,这里的配置都是给webpack
看的
css
module.exports = {
mode: 'development',
entry: {
main: './src/main.js',
},
output: {
path: path.resolve(__dirname, './dist'),
filename: 'js/chunk-[contenthash].js',
clean: true,
},
}
先看下mode
这里的值就两个,一个development
代表代码在生产环境下生效,webpack
库考虑的东西会比后者多;另一个production
代表开发环境。
再看下entry
,告诉webpack
这里的文件是入口文件
有入口就有出口output
,webpack
会从入口文件读到后传出去,这个过程会对文件做出一系列操作,path.resolve()
是将绝对路径进行拼接。我的文章node常用内置模块总结 - 掘金 (juejin.cn)详细讲了这些模块,最后生成的文件就放到这个路径下
filename
就是生成的文件放在js文件夹目录内,文件名为chunk后接一串哈希值,[contenthash
]是根据文件内容自动生成的值,如果文件内容变了,那么该值也一定会变
clean
是自动清理上一次打包的结果,默认值为true
,如果你代码改变了再打包一遍,就会生成一个新的文件,因为文件名hash
值变了,clean
为false
就会保留下来原来的
哈希值的意义涉及到cdn的缓存问题。一个情景带你理解,之前讲hash路由原理的时候也提过一嘴,就是大厂的服务器一般会在各个省份都有个分服务器,然后为了保证分服务器与总部的服务器保持同步,分服务器就需要看文件名chunk后面的哈希值是否变更,变了就去往总部那里拿一份最新的过来,哈希值的目的就是为了防止这个缓存,让你拿到的文件是旧版本的,而不是最新的
然后输入指令webpack
运行
这个时候webpack
就读到入口文件了,然后帮你打包按照你的规则放入了dist
中
这个地方如果你报错,就说明还没有全局安装一个webpack
less
npm i webpack webpack-cli -D -G // -D开发环境 -G全局 安装两个:一个webpack webpack-cli
这个打包后的js文件,如果你细心,你会发现里面还用到了欺骗词法eval
,把不属于这里的代码搬到了这里
我们目前做的这些目的都是为了使用webpack
可以把main.js放到index.html
中生效
但是webpack
是有短板的,它只能打包js
代码,因此对于index.html
它无动于衷。然而,webpack
的精髓就是loader
和plugin
。
这两个东西使得webpack
异常的强大。
我们来看下loader
loader
可以让webpack
理解除了js
以外的文件
我们再来看下plugin
plugin
的能力比loader
强,它不仅可以识别除了js之外的语言,它还可以在这个过程中做其他事情,loader
能干的plugin
也能干
使用这些东西都需要另外安装,比如这里我们让他读懂html
,安装一个plugin
。这里因为不仅仅是要读到html
,还需要将js
插入进去,所以要其他功能,就用插件
css
npm i html-webpack-plugin -D
不清楚指令就去百度,什么文件对应什么指令
webpack官方有很多插件,但是并不能百分百满足我们的日常开发需求,还有一些野路子插件,我们可以去node仓库
和npm
网站中搜,如果实在是搜不到你的需求,你就只能自己手搓一个了
安装完了我们现在要去使用,先引入,再用进来,这里用法是new
一个,具体插件的用法需要自行查看用法,打造成了函数就得调用它,打造成了构造函数你就得new
它
css
const path = require('path')
module.exports = {
mode: 'development',
entry: {
main: './src/main.js',
},
output: {
path: path.resolve(__dirname, './dist'),
filename: 'js/chunk-[contenthash].js',
clean: true,
},
plugins: [
new HtmlWebpackPlugin({
template: './public/index.html',
filename: 'index.html',
inject: 'body'
})
]
}
filename
还是一样,最后生成的html
文件名,然后js需要插入到html
里面的body
中
有些js需要植入到head
中,所以inject就是提供了两个选项,一个head
一个body
好了,现在已经可以让webpack
识别html
并且把js
植入到html
中了,已经实现了webpack
的作用了,可以去用liver server
运行下index.html
看控制台是否有打印3
这里的index.html也已经按照你的规则去生成了,并且js
插入到了body
中
如果你打包运行习惯了npm run build
而不是原始的webpack
我们这里可以来到package.json
中去配置一下
在scripts
中新加一行"build": "webpack",
这样你运行npm run build
就是webpack
了,其实老版本的webpack这里的值写的是全称webpack --config webpack.config.js
意思就是webpack运行config的这个配置文件,现在新版本只需要写个webpack
即可
webpack
并不能把所有的es6-es13的代码全给你读懂,不过webpack可以帮你找帮手,让它帮你解析
读懂css
css也是需要安装的,假设我现在src下新建了一个style文件夹,里面放了个index.css
样式文件,并且导入到main.js
中
这里用css不需要做其他事情,刚刚的html用插件是因为还需要插入操作,因此这里用loader
去解决css
我们去到官网找css的loader
,里面还有less
,sass
样式,这里我们导入css
和style-loader
,一般用css-loader
还会带上style-loader
style就是让css以style标签的形式
css
npm i css-loader style-loader -D
安装完后依旧去webpack.config.js
使用它
css
module.exports = {
mode: 'development',
entry: {
main: './src/main.js',
},
output: {
path: path.resolve(__dirname, './dist'),
filename: 'js/chunk-[contenthash].js',
},
plugins: [
new HtmlWebpackPlugin({
template: './public/index.html',
filename: 'index.html',
inject: 'body',
})
],
module: {
rules: [
{
test: /\.css$/i, // $表示后缀
use: ['style-loader', 'css-loader'] // 顺序不能乱,从右往左读
}
]
}
}
这里没有写生成到那里,就会默认生成到js中,所以为了模块化,需要单独打包到一个文件中
那就需要再下载一个插件,因为读到css还需要干其他的事情,就是用插件
css
npm i mini-css-extract-plugin -D
安装后后引入,再使用
用法看下npm官网mini-css-extract-plugin - npm (npmjs.com),用了正则
javascript
const path = require('path')
const HtmlWebpackPlugin = require('html-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
module.exports = {
mode: 'development',
entry: {
main: './src/main.js',
},
output: {
path: path.resolve(__dirname, './dist'),
filename: 'js/chunk-[contenthash].js',
},
plugins: [
new HtmlWebpackPlugin({
template: './public/index.html',
filename: 'index.html',
inject: 'body',
}),
new MiniCssExtractPlugin({
filename: 'styles/chunk-[contenthash].css',
})
],
module: {
rules: [
{
test: /\.css$/i, // $表示后缀
use: [MiniCssExtractPlugin.loader, 'css-loader'] // 顺序不能乱,从右往左读
}
]
}
}
图片
其实是可以读懂图片的,没有配规则的时候,图片资源会默认放到dist文件夹中,命名很乱,也没有一个文件夹去装,如图效果
来到module中配置一个规则,图片的格式就那么几个,这里全部写上
yaml
module: {
rules: [
{
test: /\.(png|jpe?g|gif|svg)$/,
type: 'asset',
// parser: { // base64
// dataUrlCondition: {
// maxSize: 5 * 1024 * 1024, // 1bit 1kb 1Mb
// },
// },
generator: {
filename: 'images/[contenthash][ext][query]'
}
}
]
}
注释掉的地方的效果是给你转成base64的格式,并且放到了js文件中
babel
babel就是webpack找的帮手,babel可以读懂最新的es版本并且编译成低版本的es
假设浏览器读不懂箭头函数的语法,那么babel就可以把箭头函数降级成普通函数
安装babel
bash
npm i @babel/core babel-loader @babel/preset-env @babel/plugin-transform-runtime -D
babel/core
是babel
的核心代码,babel-loader
就是让webpack
可以用这个babel
,preset-env
是个线程转换规则,plugin-transform-runtime
是转换async await
的插件
然后去到rule
再添加一个规则,exclude是把依赖排除在外,这个一定要写
javascript
module: {
rules: [
{
test: /\.js$/,
exclude: /node_modules/,
use: ['babel-loader']
}
]
}
这还不够,因为你需要向babel指名从es几降到es几,来到根目录配置一个babel.config.js
文件
java
module.exports = {
presets: [
'@babel/preset-env' // 默认的规则
],
plugins: ['@babel/plugin-transform-runtime'], // babel运行的时候进行转换
}
好了,这个时候,es6就被转成es5了,我之前的add.js写的是箭头函数,去打包后的js中搜不到const add
了,倒是变成了function add
有了这个东西,es多新你就用多新,不用担心浏览器读不懂
读懂vue
不用想,webpack
肯定也是读不懂vue的
先安装个vue
css
npm i vue
现在我用vue的写法去写main.js
src下再新建一个App.vue
,随便写点内容
xml
<template>
<div class="wrapper">
hello world
</div>
</template>
<script>
export default {
}
</script>
<style lang="css" scoped>
.wrapper {
width: 300px;
height: 300px;
background-color: black;
color: white;
font-size: 20px;
}
</style>
如何让webpack
读懂vue呢,先装东西
css
npm i vue-template-compiler vue-loader @vue/babel-preset-jsx -D
vue也会涉及到es新语法,所以要用上babel
然后在webpack.config.js
中引入并使用它,碰到vue后缀的文件就用vue-loader去读懂它
javascript
const { VueLoaderPlugin } = require('vue-loader');
module.exports = {
plugins: [
new VueLoaderPlugin(),
],
module: {
rules: [
{
test: /\.vue$/,
use: 'vue-loader'
}
]
}
}
读到之后插件会进行解析,最后在babel.config.js中再配置下,碰到vue时用另一个规则去解析vue中的js代码
java
module.exports = {
presets: [
'@babel/preset-env', // 默认的规则
'@vue/babel-preset-jsx'
],
plugins: ['@babel/plugin-transform-runtime'], // babel运行的时候进行转换
}
以上就是完整的vue-cli如何被完整打造出来的,其实就是这样配置的
最后
面试中,webpack
会被问到的核心问题主要就是这个三个:
webpack
的打包过程,原理是什么- 有自己开发过
webpack
的插件吗 - 有自己开发过
webpack
的loader
吗
这三个问题都很难,第一个问题就需要我们自己手搓一个webpack
,也就是用node搭建一个webpack
你才能理解它的原理,以后再来详谈这些
本期文章主要带大家认识了下webpack,以及基本的配置,清楚他能给我们带来什么样的作用,其实就是一个打包作用,并且同时可以在帮手babel的作用下给js语法降级,让浏览器可以读懂它
另外有不懂之处欢迎在评论区留言,如果觉得文章对你学习有所帮助,还请"点赞+评论+收藏"一键三连,感谢支持!
本次学习代码已上传至本人GitHub学习仓库:github.com/DolphinFeng...