- 版本:
"webpack": "^4.31.0","webpack-cli": "^3.3.2","webpack-dev-server": "^3.3.1"
webpack环境搭建
配置⽂文件名称
- webpack 默认配置⽂文件:
webpack.config.js - 可以通过
webpack --config 指定配置⽂文件 - 当我们执行
webpack --config {文件名}命令的时候,具体做了哪些?
- 寻找
node_modules文件下的.bin文件 .bin在指向真实的配置文件包- 配置文件会作为webpack(config):
config(便是module.export暴露出的对象)参数对象传递给webpack;
// webpack.config.js 文件
// 会将module.exports暴露的模块传递给webpack
const path = require('path')
module.exports = {
mode: 'none',
entry: {
main: './src/a.js',
},
output: {
filename: 'a.[chunkhash].js',
path: path.join(__dirname, './dist'),
}
}
node 执行webpack配置文件
// node.js文件
const path = require('path')
const webpack = require('webpack')
webpack({
mode: 'none',
entry: {
main: './src/a.js',
},
output: {
filename: 'a.[chunkhash].js',
path: path.join(__dirname, './dist'),
}
},(err) => {
console.log(err)
})
通过npm srcript 运行 webpack
- 碰到的问题, 在当前文件夹下
终端执行webpack --config weback.conig.js一直报错,报错片段如下!
TypeError: compiler.plugin is not a function
at CommonsChunkPlugin.apply (/Users/even/Even/continue/packagingTools/webpack-study/webpack-CommonsChunkPlugin/CommonsChunkPlugin01/node_modules/webpack/lib/optimize/CommonsChunkPlugin.js:88:12)
查看当前环境版本
webpack -v
webpack: 5.66.0
webpack-cli: 4.9.1
webpack-dev-server 3.11.2
当前文件夹下
package.json
"devDependencies": {
"webpack": "^3.12.0"
}
配置文件webpack.conig.js
// webpack.conig.js文件
const path = require("path");
const webpack = require("webpack");
const packagejson = require("./package.json");
const config = {
entry: {
first: './src/first.js',
second: './src/second.js',
vendor: Object.keys(packagejson.dependencies)//获取生产环境依赖的库
},
output: {
path: path.resolve(__dirname,'./dist'),
filename: '[name].js'
},
plugins: [
new webpack.optimize.CommonsChunkPlugin({
name: 'vendor',
filename: '[name].js'
}),
]
}
module.exports = config;
仔细看第二行 指向了webpack5全局的包,所以报错高版本并不兼容3,切换全局webpack成本有点大。
- 突然想到如果在
package.json里面执行命令,是否会指向当前文件的webpack3版本呢? 果然真的成功了。。。。。
"scripts": {
"build": "webpack --config webpack.config.js"
},
webpack文件监听
- 文件监听是在发现源码发⽣生变化时,⾃动重新构建出新的输出⽂文件。
- webpack 开启监听模式,有两种⽅方式: 唯⼀一缺陷:
每次需要⼿手动刷新浏览器器
//启动 webpack 命令时,带上 --watch 参数
//在配置 webpack.config.js 中设置 watch: true
- ⽂文件监听的原理理分析
//轮询判断⽂文件的最后编辑时间是否变化 某个⽂文件发⽣生了了变化,并不不会⽴立刻告诉监听者,⽽而是先缓存起来,等 aggregateTimeout
module.export = {
//默认 false,也就是不不开启
watch: true, //只有开启监听模式时,watchOptions才有意义
wathcOptions: {
//默认为空,不监听的文件或者文件夹,支持正则匹配
ignored: /node_modules/,
//监听到变化发生后会等300ms再去执行,默认300ms
aggregateTimeout: 300, //判断文件是否发生变化是通过不停询问系统指定文件有没有变化实现的,默认每秒问1000次
poll: 1000
}
}
文件指纹
- 文件指纹: 打包后输出的⽂文件名的后缀,文件指纹如何生成?
// Hash:和整个项⽬目的构建相关,只要项⽬目⽂文件有修改,整个项⽬目构建的 hash 值就会更更改,
// Chunkhash:和 webpack 打包的 chunk 有关,不不同的 entry 会⽣生成不不同的 chunkhash 值
// Contenthash:根据⽂文件内容来定义 hash ,⽂文件内容不不变,则 contenthash 不不变
使用webpack内置的stats
- stats: 构建的统计信息
// package.json 中使用 stats
"build:stats": "webpack --json > stats.json"
多进程/多实例构建
-
使用 HappyPack 解析资源
-
使用 thread-loader
webpack常规配置
Entry的用法
- 单入口:entry是一个字符串
module.exports = {
entry: './path/to/my/entry/file.js'
};
- 多入口:entry是一个对象
module.exports = {
entry: {
app: './src/app.js',
adminApp: './src/adminApp.js'
}
};
Output 的⽤用法
- 单⼊入⼝口配置
module.exports = {
entry: './path/to/my/entry/file.js'
output: {
filename: 'bundle.js'
path: __dirname + '/dist'
}
};
- 多⼊入⼝口配置
module.exports = {
entry: {
app: './src/app.js',
search: './src/search.js' },
output: {
filename: '[name].js', path: __dirname + '/dist'
}
};
核⼼心概念之 Loaders
- loaders的用法
const path = require('path');
module.exports = {
output: {
filename: 'bundle.js' },
module: {
rules: [
{
test: /\.txt$/, // test 指定匹配规则
use: 'raw-loader' // use 指定使⽤用的 loader 名称
}
]
}
};
核⼼心概念之 Plugins
- 插件⽤用于 bundle ⽂文件的优化,资源管理理和环境变量量注⼊入
const path = require('path');
module.exports = {
output: {filename: 'bundle.js' },
plugins: [
new HtmlWebpackPlugin({template: './src/index.html'}) // 放到 plugins 数组⾥里里
]
};
核⼼心概念之 Mode
- Mode⽤用来指定当前的构建环境是:production、development 还是 none
- source map : 通过 source map 定位到源代码
mode production 的情况下默认开启开发环境开启,
// eval: 使⽤用eval包裹模块代码
// source map: 产⽣生.map⽂文件
//cheap: 不不包含列列信息
//nline: 将.map作为DataURI嵌⼊入,不不单独⽣生成.map⽂文件
//module:包含loader的sourcemap
devtool: 'source-map'
- tree shaking(摇树优化) : 只抽离bundle中有用的代码
mode production 的情况下默认开启开发环境开启,
- scope hoisting : 解决大量闭包代码
mode production 的情况下默认开启开发环境开启, 原理理:将所有模块的代码按照引⽤用顺序放在⼀一个函数作⽤用域⾥里里,然后适当的重命名⼀一 些变量量以防⽌止变量量名冲突
webpack打包库
- library : 指定的全局变量
- libraryTarget : 暴露方式,是commonjs、commonjs2、umd还是this、var等

- 如何指对 .min 压缩
npm install terser-webpack-plugin@1.3.0
const TerserPlugin = require('terser-webpack-plugin');
module.exports = {
entry: {
'large-number': './src/index.js',
'large-number.min': './src/index.js'
},
output: {
filename: '[name].js',
library: 'largeNumber',
libraryTarget: 'umd',
libraryExport: 'default'
},
mode: 'none',
optimization: {
minimize: true,
minimizer: [
new TerserPlugin({
include: /\.min\.js$/,
})
]
}
}
路径 借鉴博客
- 官网说:如果服务器下有文件需要设置webpack的publicPath配置,但又是为什么?
- 我们先了解一下路径问题:
绝对路径指文件的完整路径,包括文件传输的协议HTTP、FTP等,一般用于网站的外部链接, 相对路径是指相对于当前文件的路径,它包含了从当前文件指向目的文件的路径,适用于网站的内部链接。 根路径的设置以“/”开头,后面紧跟文件路径,例如:/download/index.html。根路径必须在配置好的
服务器环境中才能使用。
- 如何使用根路径:
<script src="/subVue/b.js"></script>
选择
路径文件夹 利用http-server开服务 代码 完全模拟生产环境服务器下的文件了,直接点击index.html就可以加载到b.js文件。
- 修改webpack的publicPath其实就是修改了
script引入路径变成根路径。代码如下
// 当我们把publickPath设置成subVue时,html-webpack-plugin 插件自动替换根路径
<script src="/subVue/b.js"></script>
output.path
- 官网地址: 参数
绝对路径,将压缩好的代码打包到当前位置。
output.publicPath
- 打包的配置
- 官网地址: 参数
相对路径是相对于build之后的index.html的,打包后得到的html中可以看到。 - 项目中引用css,js,img等资源时候的一个基础路径
静态资源最终访问路径 = output.publicPath + 资源loader或插件等配置路径
devServer.publicPath
- 在开发阶段,我们借用devServer启动一个开发服务器进行开发,这里也会配置一个publicPath,这里的publicPath路径下的打包文件可以在浏览器中访问。
webpack_public_path
- 官网地址:决定了output.publicPath的值,用于来指定应用程序中所有的资源的基本路径.
- 这个变脸可以让我们在js代码中指定 output.publicPath变量;
externals
- externals:防止将某些 import 的包(package)打包到 bundle 中,而是在运行时(runtime)再去从外部获取这些扩展依赖(external dependencies)。
多个webpack配置合并
- 当使用
webpack-merge合并代码时,第二个参数会覆盖第一个。
// webpack-base.js
const path = require('path')
module.exports = {
entry: {
main: './src/a.js'
},
output: {
filename: 'a.[chunckhash].js',
path: path.join(__dirname, './dist')
}
}
// webpack01.js
const path = require('path');
const webpack = require('webpack');
const {merge} = require('webpack-merge');
const base = require('./webpack-base.js')
let webpackConfig = merge(base,{
output: {
filename: 'a.[chunckhash].js',
path: path.join(__dirname, './dist2')
}
})
webpack(webpackConfig,(err) => {
console.log(err);
})
执行命令 node node.js
设置环境变量
cross-env 插件
- 以下命令会将环境变量
APP_ID的值 插入process.env对象中
// 下载插件
npm install cross-env -S
// 使用 在命令行中
cross-env APP_ID=111111 node ./test.js
npm run {命令} --APP_ID=111111
- 也可以通过npm方式插入环境变量-执行命令
npm run dev --APP_ID=111111切记: 一定是要在run后面跟参数;
// package.json文件下的
"scripts": {
"dev": "node ./test.js",
}
--:代表前缀npm_config_APP_ID转移成小写,最后会看到属性名为npm_config_app_id,值为11111会将环境变量插入process.env对象中
- 当然也可以放多个参数如
npm run dev --APP_ID=111111 --APP=22222等
如何删除文件
rimraf
- npm包
rimraf
// node包 代码
const rm = require('rimraf');
const path = require('path')
rm(path.join(__dirname,'./dist/index01.js'),function(err) {
if (err) throw err;
})
clean-webpack-plugin
- webpack插件
clean-webpack-plugin
// webpack插件代码
const {CleanWebpackPlugin} = require('clean-webpack-plugin');
plugins: [
new CleanWebpackPlugin(),
]
script命令删除
rm -rf ./dist && webpack
俩者差距还是很大的,一个是node包好处是可以删除任意文件任意目录。一个是webpack插件只能删除打包的路径并全量删除。
webpack优化
CommonsChunkPlugin - 3之后已废弃
- 文章中大佬写的和很详细,他是关于webapck3的相关配置;
- 分离出第三方库、自定义公共模块、webpack运行文件
- 单独分离出第三方库、自定义公共模块、webpack运行文件
- 抽离第三方库和自定义公共模块
webpack5 - SplitChunks
- chunks选项,决定要提取那些模块。
默认是async:只提取异步加载的模块出来打包到一个文件中。 异步加载的模块:通过import('xxx')或require(['xxx'],() =>{})加载的模块。 initial:提取同步加载和异步加载模块,如果xxx在项目中异步加载了,也同步加载了,那么xxx这个模块会被提取两次,分别打包到不同的文件中。 同步加载的模块:通过 import xxx或require('xxx')加载的模块。 all:不管异步加载还是同步加载的模块都提取出来,打包到一个文件中。
- minSize选项:规定被提取的模块在压缩前的大小最小值,单位为字节,默认为30000,只有超过了30000字节才会被提取。
- maxSize选项:把提取出来的模块打包生成的文件大小不能超过maxSize值,如果超过了,要对其进行分割并打包生成新的文件。单位为字节,默认为0,表示不限制大小。
- minChunks选项:表示要被提取的模块最小被引用次数,引用次数超过或等于minChunks值,才能被提取。
- maxAsyncRequests选项:最大的按需(异步)加载次数,默认为 6。
- maxInitialRequests选项:打包后的入口文件加载时,还能同时加载js文件的数量(包括入口文件),默认为4。
先说一下优先级 maxInitialRequests / maxAsyncRequests <maxSize<minSize。
- automaticNameDelimiter选项:打包生成的js文件名的分割符,默认为~。
- name选项:打包生成js文件的名称。
- cacheGroups选项,核心重点,配置提取模块的方案。里面每一项代表一个提取模块的方案。下面是cacheGroups每项中特有的选项,其余选项和外面一致,若cacheGroups每项中有,就按配置的,没有就使用外面配置的。
- test选项:用来匹配要提取的模块的资源路径或名称。值是正则或函数。
- priority选项:方案的优先级,值越大表示提取模块时优先采用此方案。默认值为0。
- reuseExistingChunk选项:true/false。为true时,如果当前要提取的模块,在已经在打包生成的js文件中存在,则将重用该模块,而不是把当前要提取的模块打包生成新的js文件。
- enforce选项:true/false。为true时,忽略minSize,minChunks,maxAsyncRequests和maxInitialRequests外面选项
多个入口文件合并
- 在a.js 引入b.js, webpack打包的时候自动会合成
// a.js
import a from './b.js'
// 配置文件只有a入口就可
let path = require('path')
module.exports = {
mode: 'node',
entry: './src/a.js',
output: {
filename: 'bunld.js',
path: path.join(__dirname,'./dist'),
}
}
- 配置入口的时候多个入口
let path = require('path')
module.exports = {
mode: 'node',
entry: {
main: ['./src/a.js', './src/b.js']
},
output: {
filename: 'bunld.js',
path: path.join(__dirname,'./dist'),
}
}
多个入口文件分别打包
- 其实分别打包文件就是将
entry变成对象而已,是不是很简单
let path = require('path')
module.exports = {
mode: 'node',
entry: {
a: './src/a.js',
b: './src/b.js',
},
output: {
filename: '[name].[chunkhash].js',
path: path.join(__dirname,'./dist')
}
}
webpack插件
css文件压缩
- npm install optimize-css-assets-webpack-plugin -D
- npm install cssnano -D
module.exports = {
entry: {
app: './src/app.js',
search: './src/search.js'
},
output: {
filename: '[name][chunkhash:8].js', path: __dirname + '/dist'
},
plugins: [
new OptimizeCSSAssetsPlugin({ assetNameRegExp: /\.css$/g, cssProcessor: require('cssnano’)
]
自动清理构建目录
new HtmlWebpackPlugin({
template: path.join(__dirname, 'src/search.html'), // 模板路径
filename: 'search.html', // 文件名称
chunks: ['search'], // 生成的html用哪些chunk
inject: true, // 自动注入
minify: {
html5: true,
collapseWhitespace: true,
preserveLineBreaks: false,
minifyCSS: true,
minifyJS: true,
removeComments: false
}
})
自动清理理构建目录
- npm install aotoprefixer -D
- npm install postcss-loader -D
{
test: /.less$/,
use: [
'css-loader',
'less-loader',
{
loader: 'postcss-loader',
options: {
plugins: () => [
require('autoprefixer')({
browsers: ['last 2 version', '>1%', 'ios 7']
})
]
}
},
]
},
资源内联的意义
- raw-loader 内联 html
注意版本问题npm install raw-loader@0.5.1
<script>${require('raw-loader!babel-loader!. /meta.html')}</script>
- raw-loader 内联 JS
<script>${require('raw-loader!babel-loader!../node_modules/lib-flexible')}</script>
DefinePlugin - 定义环境变量
- webpack.DefinePlugin:编译时将你代码中的变量替换为其他值或表达式
// a.js文件
console.log('a文件')
let a = NAME;
// webpack.config.js
plugins: [
new webpack.DefinePlugin({
'NAME': 'Even',
}),
]
// 打包好后的文件
console.log('a文件')
let a = Even;
- 会搜索打包文件变量
NAME替换成Even变量。此时代码会报错,因为let a = Even;并没有定义。 - 如果想让
Even是字符串请如下配置
// webpack.config.js
plugins: [
new webpack.DefinePlugin({
'NAME': "'Even'",
}),
]
ExtractTextplugin - 文件提取
- 只在weback3的时候才可使用
- ExtractTextplugin
mini-css-extract-plugin - css提取
- webpack4之后开始使用此插件
// 下载
npm install mini-css-extract-plugin -D
// webpack中引用
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
// 使用时候需要在俩处配置
// 1. 规则中
{
test: /\.(less|css)$/,
use: [
{
loader: MiniCssExtractPlugin.loader,
},
'css-loader',
{
loader: 'less-loader',
options: {
lessOptions: {
javascriptEnabled: true,
},
}
}
],
},
// 2. 插件中
new MiniCssExtractPlugin({
filename: 'css/base.css',
chunkFilename: '[id].css',
}),
webpack解析器
webpack-loader
- loader官网会发现loader大概有7个模块,1、文件,2、JSON,3、转换编译,4、模板,5、样式,6、清理和测试,7、框架
- webpack因为只识别
js文件,所以需要这些loader来处理文件转成js,个个大神的开源代码和维护都在社区有明显标注 - 常规选项配置
- test: 要检测的文件
- loader: 要使用的插件
- include: 解析包含的文件
- exclude: 解析要排除的文件
- options: 插件高级配置选项
- use: [] 多个插件配置时使用
vue-loader
- 细心的朋友会发现vue-loader并不在上面webpack-loader里面,
why?. - 个人理解:
.vue是复杂的文件,文档loader都是处理单个文件,比如.json文件里面全是json数据,而.vue里面有三种1.style,2.script,3.html。 - 所以:为了把
vue文件拆分成loader(如:style-loader、html-loader、script-loader)能够解析的文件,才有了vue-loader插件。单独的配置文档
// webpack.config.js
// 所有的vue项目,这个插件是必不可少的
const { VueLoaderPlugin } = require('vue-loader')
plugins: [
new VueLoaderPlugin()
]
完全理解设计者的初衷,一个页面写所有的js、css、html真的很炫酷,不向其他开发者为了显示自己所谓的高端操作,
解耦后的代码真的很难下咽,一个函数能搞定的事情非要给你弄三个文件,6个函数。。。
url-loader
- 看到webpack代码,不是很理解
limit,name的作用,于是去webpack官网找了很久。。。。才发现他是loader的配置。
{
test: /\.(png|jpe?g|gif|svg)(\?.*)?$/,
loader: 'url-loader',
options: {
limit: 10000,
name: path.join("../build", APP_ID, 'img/[name].[hash:7].[ext]')
}
},
- limit:低于指定的限制时,可以返回一个 DataURL。
- name:这个参数让我很困惑,url-loader并没有对此字段做解释,查了(url-loader)[github.com/webpack-con….
css-loader
- 代码如下! 文档
// index.js
import '../css/base.css';
// base.less
#phone {
background-image: url('../assets/actrulebtn.png');
}
- 问题:在入口文件引入css时,css-loader将url自动处理了,并在
publickpath中输出了图片,自信看文档有个url选项,是否修改url配置,当设置url:false时,不会做额外处理。
module.exports = {
module: {
rules: [
{
test: /\.css$/i,
loader: "css-loader",
options: {
url: false,
},
},
],
},
};
规范代码
- 新增
.eslintrc.js文件
module.exports = {
"parser": "babel-eslint", // 使用解析器
"extends": "airbnb", // 继承airbnb
// 当前想启用的环境
"env": {
"browser": true,
"node": true
},
// 定义规则
"rules": {
"indent": ["error", 4]
}
};
- npm 下载
"babel-eslint": "^10.0.1",
"eslint": "^5.16.0",
"eslint-config-airbnb": "^17.1.0",
"eslint-config-airbnb-base": "^13.1.0",
"eslint-loader": "^2.1.2",
"eslint-plugin-import": "^2.17.3",
"eslint-plugin-jsx-a11y": "^6.2.1",
"eslint-plugin-react": "^7.13.0",