简单来说 Webpack 是一个模块打包器。它将根据模块的依赖关系进行静态分析,然后将这些模块按照指定的规则生成对应的静态资源。
webpack让一切变得不再复杂
webpack是一个现代 JavaScript 应用程序的静态模块打包器(module bundler)。当 webpack 处理应用程序时,它会递归地构建一个依赖关系图(dependency graph),其中包含应用程序需要的每个模块,然后将所有这些模块打包成一个或多个bundle。
webpack 本身是基于node.js开发的。
从 webpack v4.0.0 开始,可以不用引入一个配置文件(零配置文件),然而webpack仍然还是高度可配置的。
1. 安装webpack
目前*@vue/cli和create-react-app基本上采用的是webpack 4.0以上版本,所以本次课程以第四代版本为主;第四代版本需要我们安装webpack和webpack-cli*(可执行命令);
为了防止全局安装webpack的版本冲突,我们真实项目开发的时候基本上以安装在本地项目中为主;
$ npm init -y
$ npm install webpack webpack-cli --save-dev
OR
$ yarn add webpack webpack-cli -D
2.webpack的基础使用
-
初步体验(零配置)
/* * 默认会打包SRC目录中的JS文件(入口默认index.js) * 打包完成的目录默认是DIST/MAIN.JS * * npx:http://www.ruanyifeng.com/blog/2019/02/npx.html * 默认执行node_modules/bin/webpack.cmd文件 * webpack默认支持CommonJS和ES6 Module的模块规范,依此进行依赖打包 */ $ npx webpack
-
自定义基础配置
- webpack.config.js OR webpackfile.js
let path = require('path'); module.exports = { //=>打包模式 开发环境development 生产环境production mode: 'production', //=>入口 entry: './src/index.js', //=>输出 output: { //=>输出文件的文件名 filename: 'bundle.js', //=>输出目录的"绝对路径" path: path.resolve(__dirname, 'dist') } }
-
自定义配置文件名
- $ npx webpack --config webpack.config.development.js
- 可在package.json中配置可执行的脚本命令(区分开发环境)
"scripts": { "serve": "webpack --config webpack.config.development.js", "build": "webpack --config webpack.config.production.js" },
3.webpack-dev-server
-
安装:$ yarn add webpack-dev-server -D
-
基础配置
/* webpack.config.js */ //=>配置DEV-SERVER devServer: { //=>端口 port: 3000, //=>显示编译进度 progress: true, //=>指定访问资源目录 contentBase: './dist', //=>自动打开浏览器 open: true }
/* package.json */ "scripts": { "serve": "webpack-dev-server", "build": "webpack" }
- $ npm run serve
- $ npx webpack-dev-server
-
代码更改后,会自动重新编译,然后自动刷新页面
4.html-webpack-plugin
www.webpackjs.com/plugins/htm…
-
安装:$ yarn add html-webpack-plugin -D
-
在webpack.config.js中使用
let HtmlWebpackPlugin = require('html-webpack-plugin'); module.exports = { ..., //=>在webpack中使用插件 plugins: [ new HtmlWebpackPlugin({ //=>指定自己的模板 template: './src/index.html', //=>输出的文件名 filename: 'index.html', //=>给引入的文件设置HASH戳(清除缓存的),也可以在output中设置 filename: 'bundle.[hash].js' 来生成不同的文件 hash: true, //=>控制是否以及以何种方式最小化输出 //=>https://github.com/kangax/html-minifier minify: { collapseWhitespace: true, removeComments: true, removeAttributeQuotes: true, removeEmptyAttributes: true } }) ] }
5.webpack中的加载器loader:处理样式的
-
安装:$ yarn add css-loader style-loader less less-loader autoprefixer postcss-loader ... -D
-
使用
module.exports = { //=>配置模块加载器LOADER module: { //=>模块规则:使用加载器(默认从右向左执行,从下向上) rules: [{ test: /\.(css|less)$/, //=>基于正则表达式匹配哪些模块需要处理 use: [ "style-loader", //=>把CSS插入到HEAD中 "css-loader", //=>编译解析@import/URL()这种语法 "postcss-loader", //=>设置前缀 { loader: "less-loader", options: { //=>加载器额外的配置 } } ] }] } }
postcss.config.js
module.exports = { plugins: [ require('autoprefixer') ] };
package.json
"browserslist": [ "> 1%", "last 2 versions" ]
6.mini-css-extract-plugin 抽离CSS内容
- 安装 $ yarn add mini-css-extract-plugin optimize-css-assets-webpack-plugin uglifyjs-webpack-plugin -D
let MiniCssExtractPlugin = require('mini-css-extract-plugin'),
OptimizeCssAssetsWebpackPlugin = require('optimize-css-assets-webpack-plugin'),
UglifyjsWebpackPlugin = require('uglifyjs-webpack-plugin');
module.exports = {
//=>设置优化项
optimization: {
//=>设置压缩方式
minimizer: [
//=>压缩CSS(但是必须指定JS的压缩方式)
new OptimizeCssAssetsWebpackPlugin(),
//=>压缩JS
new UglifyjsWebpackPlugin({
cache: true, //=>是否使用缓存
parallel: true, //=>是否是并发编译
sourceMap: true, //=>启动源码映射(方便调试)
})
]
},
plugins: [
//=>使用插件
new MiniCssExtractPlugin({
//=>设置编译后的文件名字
filename: 'main.css'
})
],
module: {
rules: [{
test: /\.(css|less)$/,
use: [
// "style-loader",
//=>使用插件中的LOADER代替STYLE方式
MiniCssExtractPlugin.loader,
"css-loader",
"postcss-loader",
"less-loader"
]
}]
}
}
上述JS压缩对于require/import等还存在问题,需要对于ES6中的一些语法进行处理!
7.基于babel实现ES6的转换和ESLint语法检测
- 安装 $ yarn add babel-loader @babel/core @babel/preset-env @babel/plugin-proposal-class-properties @babel/plugin-proposal-decorators @babel/plugin-transform-runtime -D
- 安装 $ yarn add @babel/runtime @babel/polyfill
- 安装 $ yarn add eslint eslint-loader -D
module.exports = {
...,
module: {
rules: [...,{
test: /\.js$/,
use: [{
loader: 'babel-loader',
options: {
//=>转换的语法预设(ES6->ES5)
presets: [
"@babel/preset-env"
],
//=>基于插件处理ES6/ES7中CLASS的特殊语法
plugins: [
["@babel/plugin-proposal-decorators", {
"legacy": true
}],
["@babel/plugin-proposal-class-properties", {
"loose": true
}],
"@babel/plugin-transform-runtime"
]
}
}], //=>, "eslint-loader"
//=>设置编译时忽略的文件和指定编译目录
include: path.resolve(__dirname, 'src'),
exclude: /node_modules/
}]
}
}
参考eslint.org/demo生成 .eslintrc.json
补充知识:在vscode中开启ES7中类的装饰器,项目根目录中设置 jsconfig.json
{
"compilerOptions": {
"experimentalDecorators": true
}
}
@log
class A{
a=1;
}
8.暴露全局loader
- $ yarn add expose-loader -D
- 前置加载器、后置加载器、普通加载器...
//=>内联加载器
import jquery from 'expose-loader?$!jquery';
console.log(window.$);
{
//=>只要引入JQUERY就在全局注入$
test: require.resolve('jquery'),
use: ['expose-loader?$']
}
let webpack = require('webpack');
module.exports = {
plugins: [
//=>在每个模块中都注入$
new webpack.ProvidePlugin({
'$': 'jquery'
})
],
}
//=>页面中
console.log($);
9.webpack中图片的处理和分目录分发
- 在JS中创建IMG
- 在CSS中设置背景图
- 在HTML中写死
- ...
安装 $ yarn add file-loader url-loader html-withimg-loader -D
module.exports = {
...,
module: {
//=>模块规则:使用加载器(默认从右向左执行)
rules: [..., {
test: /\.(png|jpg|gif)$/i,
use: [{
//=>把指定大小内的图片BASE64
loader: 'url-loader',
options: {
limit: 200 * 1024,
outputPath:'/images'
}
}],
include: path.resolve(__dirname, 'src'),
exclude: /node_modules/
}, {
test: /\.html$/,
use: ['html-withimg-loader']
}]
}
}
10. 最后实现文件分目录发布
module.exports = {
output: {
//=>配置引用前缀(所有资源前加这个地址)
publicPath: './'
},
plugins: [
new MiniCssExtractPlugin({
filename: 'css/main.css'
})
],
module: {
//=>模块规则:使用加载器(默认从右向左执行)
rules: [...,{
test: /\.(png|jpg|gif)$/i,
use: [{
options: {
outputPath: 'images'
}
}]
}]
}
}
合体!
/*
较完整的webpack基置
在这个文件中设置我们自定义的打包规则
1. 所有的规则都写在module.exports={}中
*/
let path = require('path');
let HtmlWebpackPlugin = require('html-webpack-plugin');
let MiniCssExtractPlugin = require('mini-css-extract-plugin'), //把css单独分离出来
OptimizeCssAssetsWebpackPlugin = require('optimize-css-assets-webpack-plugin'), //压缩css
UglifyjsWebpackPlugin = require('uglifyjs-webpack-plugin'); //压缩js
let webpack = require('webpack');
//导入进来的插件都是一个类 new HtmlWebpackPlugin({});
module.exports = {
//配置优化规则
optimization: {
//设置压缩方式
minimizer: [
//压缩css (产生问题:JS压缩不在执行自己默认的压缩方式了,也走的是这个插件,从而导致无法压缩,所以必须设置JS的压缩方式)
new OptimizeCssAssetsWebpackPlugin(),
//压缩js
new UglifyjsWebpackPlugin({
cache: true, //是否使用缓存
parallel: true, //是否是兵法编译
sourceMap: true, //启动源码映射(方便调试)
})
]
},
//配置环境 开发环境development 生产环境production(默认)
mode: 'production',
//入口
entry: ['@babel/polyfill', './src/index-my.js'],
//出口
output: {
//输出文件的文件名
//bundle.min.[hash].js 让每一次生成的文件名都带着hash值
filename: 'bundle.min.js',
// filename: 'bundle.min.[hash].js',
//输出的目录必须是绝对路径,__dirname当前目录
path: path.resolve(__dirname, 'dist'),
publicPath: './' //给编译后引入资源地址前面设置的前缀
},
//关于webpack-dev-server的一些配置 执行命令:webpack-dev-server --config xxx.js (特点:服务启动后,默认是不关闭的,当我们修改src中的源文件,他会自动进行编译,然后自动刷新浏览器,类似于vscode中的Live Server插件,实时刷新)
devServer: {
//创建服务指定的端口号
port: 3000,
//显示打包编译进度
progress: true,
//指定当前服务处理资源的目录
contentBase: './dist',
//编译完成后,自动打开浏览器
open: true
},
//使用插件 (数组)
plugins: [
new HtmlWebpackPlugin({
//不指定模版会按照默认模版创建一个html页面,当然真实项目中一般都是把自己写好的html进行编译
template: './src/index.html',
//输出的文件名
filename: 'index.html',
//让我们引入js后面加上hash戳(清除缓存),但是真实项目中我们一般都是每一次编译生成不同的js文件引入
hash: true,
//控制压缩
minify: {
collapseWhitespace: true,
removeComments: true,
removeAttributeQuotes: true,
removeEmptyAttributes: true
}
}),
new MiniCssExtractPlugin({
//指定输出的文件名
filename: 'main.min.css'
}),
//在每个模块中都注入$
new webpack.ProvidePlugin({
'$': 'jquery'
}),
],
//使用加载器loader处理规则
module: {
rules: [{
//基于正则匹配处理哪些文件
test: /\.(css|less)$/,
//使用哪一个加载器,控制使用的loader(有顺序的:从右到左执行)
use: [
// "style-loader", //把编译好的css插入到页面的head中(内嵌式)
MiniCssExtractPlugin.loader, //使用插件中的loader代替style方式
"css-loader", //编译解析@import/URL()这种语法
// "postcss-loader",//设置前缀的加载器
{
loader: "postcss-loader",
options: {
ident: 'postcss',
plugins: [
require('autoprefixer')
]
}
},
{
loader: "less-loader", //编译less
options: {
//加载额外的配置
}
}
]
}, {
test: /\.js$/,
//处理编译JS的loader
use: [{
loader: 'babel-loader',
options: {
//转换的语法预设(ES6->ES5)
presets: [
"@babel/preset-env"
],
//=>基于插件处理ES6/ES7中CLASS的特殊语法
plugins: [
["@babel/plugin-proposal-decorators", {
"legacy": true //处理装饰器
}],
["@babel/plugin-proposal-class-properties", {
"loose": true //处理属性
}],
"@babel/plugin-transform-runtime"
]
}
}],
//设置编译时忽略的文件和指定编译目录
include: path.resolve(__dirname, 'src'), //编译的
exclude: /node_modules/ //忽略的·
}, {
//图片处理
test: /\.(png|jpg|gif|jpeg|ico|webp|bpm)$/i,
use: [{
loader: 'url-loader',
options: {
//只要图片小于200KB,在处理的时候直接base64
limit: 2 * 1024,
//控制打包后图片所在的目录
outputPath: 'images'
}
}]
}, {
//处理HTML文件中导入的img文件
test: /\.(html|htm|xml)$/i,
use: ['html-withimg-loader']
}]
}
}
修改 Vue-cli3 webpack内部配置
- vue-cli3的配置,全部放在了vue.config.js这一个文件中,讲道理,真的清爽。
- 创建vue.config.js文件命令来审查或修改全局的 CLI 配置。
module.exports = {
//=>process.env.NODE_ENV:环境变量中存储的是开发环境还是生产环境
publicPath: process.env.NODE_ENV === 'production' ? 'http://www.baidu.com/' : '/',
//=>outputDir
//=>自定义目录名称,把生成的JS/CSS/图片等静态资源放置到这个目录中
assetsDir: 'assets',
//=>关闭生产环境下的资源映射(生产环境下不在创建xxx.js.map文件)
productionSourceMap: false,
//=>设置一些webpack配置项,用这些配置项和默认的配置项合并
configureWebpack: {
plugins: []
},
//=>直接去修改内置的webpack配置项
chainWebpack: config => {
//=>config:原始配置信息对象
config.module
.rule('images')
.use('url-loader')
.loader('url-loader')
.tap(options => {
options.limit = 200 * 1024;
return options;
});
},
//=>修改webpack-dev-server配置(尤其是跨域代理)
devServer: {
proxy: {
//请求地址 /user/add
//代理地址 http://api.baidu.cn/user/add
"/": {
changeOrigin: true,
target: "http://api.baidu.cn/"
}
}
},
//=>多余1核cpu时:启动并行压缩
parallel: require('os').cpus().length > 1
}
修改 create-react-app webpack内部配置
- react 不同,它是导出所有Webpack配置让开发者自行更改
- yarn eject 命令 => 把所有隐藏在node_modules中的webpack配置项都暴露出来(方便自己根据项目需求,二次更改webpack配置)
- 打开导出的config文件夹就可以增删改查了 比如修改less的处理配置
- $ yarn add less less-loader //安装
打开config/webpack.config.js
const cssRegex = /\.(css|less)$/;
const cssModuleRegex = /\.module\.(css|less)$/;
{
test: cssRegex,
exclude: cssModuleRegex,
use: getStyleLoaders({
importLoaders: 1,
sourceMap: isEnvProduction && shouldUseSourceMap,
}, "less-loader"),
// Don't consider CSS imports dead code even if the
// containing package claims to have no side effects.
// Remove this when webpack adds a warning or an error for this.
// See https://github.com/webpack/webpack/issues/6571
sideEffects: true,
},
// Adds support for CSS Modules (https://github.com/css-modules/css-modules)
// using the extension .module.css
{
test: cssModuleRegex,
use: getStyleLoaders({
importLoaders: 1,
sourceMap: isEnvProduction && shouldUseSourceMap,
modules: true,
getLocalIdent: getCSSModuleLocalIdent,
}, "less-loader"),
},
我真是 [HIGH] 到不行啦!