这是我参与「掘金日新计划 · 4 月更文挑战」的第5天,点击查看活动详情。
大家好,我叫小杜杜,相信大家都知到我们代码有开发模式和打包模式,作为一个React前端,我们一帮用jsx
、tsx
来书写代码,在样式方面可能会用到less
、sass
等,而实际上浏览器识别不了这些文件,那如何让浏览器识别这些文件呢?实际上是依赖于babel
,同时这种工具是 webpack
在之前讲过一篇有关 package.json
的文章,有兴趣的小伙可以看看:通过开水果店,帮你全面了解package.json文件的作用
关于webpack
,对于新手而言可以说是非常的遥远,为什么这么说呢?因为很多人都没有机会参与到项目中的Webpack
配置,更不要说怎么去做优化了(我就是其中一员😅)
作为一个小菜鸟,要想向前一步,webpack
可以说是极其重要的一环,所以今天主要说下有关webpack
的知识,一起努力吧~
webpack 是什么?
webpack:是一种前端资源构建工具,一个静态模块打包器(module bundler)。
-
前端资源构建工具:在文章的一开始说过,浏览器是不认识像
jsx
、tsx
、less
、sass
的,甚至js
的高级语法都不认识,而这些资源要像在浏览器中能正常运行,就需要对这些文件进行编译,而编译的工具就是前端资源构建工具(webpack便是其中之一)
-
静态模块打包器:在
webpack
看来,前端的所有资源文件(js、css、json...),都会作为一个模块处理,构建一个依赖关系图,然后通过这个关系图将所有的静态资源打包成一个或多个bundle
输出
为什么需要webpack
从前面的介绍来看,webpack
最主要的作用就是将各种资源文件进过统一编辑,使浏览器去认识,然而实际上 webpack
的作用远不止于此,举个简单的例子:
css 的单位中有 px
、rem
,1rem = 16px
,在移动端中,我们不能使用px
的单位,需要使用rem
,而我们的设计稿肯定是px
的,难道我们要手动计算兑换为rem
后的数值吗?就算你说用到的px
数值比较少,我可以自己换算,好没问题,哪之后UI再次调动样式,或者之后再次修改,你每次都需要换算吗?那么其代价是否太大了?
那有没有一个比较好的方案,我在开发的时候就用px
并且在开发环境下,看到的也是px
,只有在打包下才看到rem
呢? 当然有,这时便需要通过配置webpack
来实现这个效果。
诸如此类的问题,我们都可以通过webpack
去解决,做个形象的比喻,webpack
就相当于交通工具,没有他只能徒步,有了它,将会给我们提供大大的便利
这里顺便说下,之前用 React 搭建一个了移动端框架,感觉还是不错的,有兴趣的小伙伴可以看看:打造开箱即用的 react 移动端框架
webpack的五大核心概念
- Entry: 入口,指示 webpack 以哪个文件为入口起点开始打包,分析构建内部依赖图。
- Output: 输出,指示 webpack 打包后的资源 bundles 输出到哪里去,以及如何命名。
- Loader: 处理模块, 让 webpack 能够去处理那些非 JS 的文件,比如样式文件、图片文件(webpack 自身只理解JS)
- Plugins: 插件,可以用于执行范围更广的任务。插件的范围包括,从打包优化和压缩,一直到重新定义环境中的变量等。
- Mode: 模式,指示 webpack 使用相应模式的配置。
其中 Mode
分为development
和production
-
development:会将 DefinePlugin 中 process.env.NODE_ENV 的值设置为
development
。对应的是本地环境。 -
production:会将 DefinePlugin 中 process.env.NODE_ENV 的值设置为
production
。对应的是线上环境。
webpack 详细配置
entry
entry: 入口位置,共有三种模式, string
| array
| object
string
: 单入口,打包形成一个chunk
, 输出一个bundle
文件,并且chunk
的名称默认是 mainarray
: 多入口,但所有入口文件只会形成一个chunk
, 输出一个bundle
文件object
: 多入口,有几个入口文件就会形成几个chunk
,输出几个bundle
文件,并且 chunk 的名称是对象的key
值
// string
entry: "./src/index.js"
// array
entry:['./src/index.js', './src/test.js']
// object
entry: {
index: './src/index.js',
test: './src/test.js
}
output
output: 输出文件,模式为:object
- path: 输出文件目录的位置(将来所有资源输出的公共目录)
- filename:文件名称 (指定名称 + 目录)
- publicPath:公共资源引入的前缀(如 ‘/’ => /文件名)
- chunkFilename: 指定非入口chunk的名称
- clean:打包前清空
- library:打包整个库后向外暴露的变量名
- libraryTarget:库暴露的方式(有 'window', 'global', 'commonjs')
其中 library 可以为对象,包含 libraryTarget, 分别为对象
output: {
path: resolve(__dirname, 'build'),
filename: 'js/[name].js',
publicPath: '/',
chunkFilename: 'js/[name]_chunk.js',
clean: true,
//分开写
library: '[name]',
libraryTarget: 'window',
//合并
library:{
name: '[name]',
type: 'window'
}
}
mode
mode:模块:通过 rules
来配置 loader
loader: webpack
只能理解 JavaScript
和 json
文件, loader
的作用就是让 webpack
理解识别其他文件
loader的参数:
test
:单个loaderuse
:多个loaderexclude
: 排除的文件include
:检查的文件enforce
:执行顺序,分pre
(优先执行) 和post
(延后执行)loader
:单个用loaderoneOf
: 只会生效一个配置option
: 指定配置选项
module: {
rules: [
{
test: /\.css$/,
use: ['style-loader', 'css-loader'], // 多个
exclude: /node_modules/,
include: resolve(__dirname, 'src'),
enforce: 'pre',
loader: 'eslint-loader',
oneOf: [],
options: {}
}
]
}
plugins
plugins: 插件,用于配置些功能。
const HtmlWebpackPlugin = require("html-webpack-plugin"); //创建html文件,并自动引入打包输出的bundles文件。支持html压缩。
plugins: [
new HtmlWebpackPlugin({
template:"index.html"
})
]
resolve
resolve:解析模块的规则
alias
:配置别名,引入时,可用别名代替,如 @ 代表根目录extensions
:在引入时省略文件路径的后缀名, 如 import xxx from '@/index' => import xxx from '@/index.js'modules
: 告诉 webpack 解析模块应该去找哪个目录
resolve: {
alias: {
[@]: path.resolve(__dirname, "src")
},
extensions: ['.js', '.json', '.jsx', '.css'],
modules: [resolve(__dirname, '../../node_modules'), 'node_modules']
}
devServer
devServer: 开发环境下的配置
contentBase
: 运行代码所在的目录watchContentBase
:监视contentBase目录下的所有文件,一旦文件变化就会reloadwatchOptions下的ignored
:配置忽略文件compress
:是否gzip压缩port
:端口号host
:域名open
:是否自动打开浏览器hot
:是否开启HMR功能clientLogLevel
: 是否显示启动服务器日志信息quiet
:除了一些基本信息外,其他内容是否要显示overlay
:如果出错了,是否全屏提示proxy
:代理
devServer: {
contentBase: resolve(__dirname, 'build'),
watchContentBase: true,
watchOptions: {
ignored: /node_modules/
},
compress: true,
port: 5000,
host: 'localhost',
open: true,
hot: true,
clientLogLevel: 'none',
quiet: true,
overlay: false,
proxy: {
'/api': {
target: 'http://localhost:3000',
pathRewrite: {
'^/api': ''
}
}
}
}
proxy以上述代码为例:
- target:目标地址,当服务器接收到/api/xxx的请求,就会把请求转发到另外一个服务器3000
- pathRewrite:简写,意思是由
/api/xxx
可以写为/xxx
optimization
optimization:生产环境下的配置
splitChunks
:官方下的配置,自动生效runtimeChunk
:把runtime部分的代码抽离出来单独打包minimizer
: 一些额外的配置
optimization: {
splitChunks: { //默认的
chunks: 'async',
minSize: 30000,
minChunks: 1,
maxAsyncRequests: 5,
maxInitialRequests: 3,
automaticNameDelimiter: '~',
name: true,
cacheGroups: {
vendors: {
test: /[\/]node_modules[\/]/,
priority: -10
},
default: {
minChunks: 2,
priority: -20,
reuseExistingChunk: true
}
}
},
runtimeChunk: {
name: entrypoint => `runtime-${entrypoint.name}`
},
minimizer: {
// 配置生产环境的压缩方案:js/css
new TerserWebpackPlugin({
cache: true, // 开启缓存
parallel: true, // 开启多进程打包
sourceMap: true // 启用sourceMap(否则会被压缩掉)
})
]
}
}
End
致此, 有关Webpack的基础就讲完了,相信看完的小伙伴,应该可以看懂自己项目的配置了,这样就能更好的理解项目了,喜欢的点个赞👍🏻支持下吧(● ̄(エ) ̄●)