webpack 基础使用
安装
npm i -D webpack webpack-cli
配置 script 脚本
"scripts":{
"build":"webpack src/main.js"
}
执行
npm run build
生成了 dist 文件夹,含有 main.js 说明简单的打包成功
修改配置
默认配置文件是 webpack.config.js
// webpack.config.js
const path = require('path')
module.exports = {
mode: 'development', // 开发模式
entry: path.resolve(__dirname, '../src/main.js'), // 入口文件
output: {
filename: 'output.js', // 打包后的文件名称
path: path.resolve(__dirname, '../dist') // 打包后的目录
}
}
更改打包命令
"scripts":{
"build":"webpack --config build/webpack.config.js"
}
配置输出文件名和输出目录位置
module.exports = {
// 省略其他配置
output: {
filename: '[name].[hash:8].js', // 打包后的文件名称
path: path.resolve(__dirname, '../dist') // 打包后的目录
}
}
每次手动引入修改引入的 js 当然是不可容忍的,于是使用插件
npm i -D html-webpack-plugin
然后配置一个 index.html 模版
// webpack.config.js
const path = require('path')
const HtmlWebpackPlugin = require('html-webpack-plugin')
module.exports = {
mode: 'development', // 开发模式
entry: path.resolve(__dirname, '../src/main.js'), // 入口文件
output: {
filename: '[name].[hash:8].js', // 打包后的文件名称
path: path.resolve(__dirname, '../dist') // 打包后的目录
},
plugins: [
new HtmlWebpackPlugin({
template: path.resolve(__dirname, '../public/index.html')
})
]
}
配置多页面入口开发
const path = require('path')
const HtmlWebpackPlugin = require('html-webpack-plugin')
module.exports = {
mode: 'development', // 开发模式
entry: {
main: path.resolve(__dirname, '../src/main.js'),
header: path.resolve(__dirname, '../src/header.js')
},
output: {
filename: '[name].[hash:8].js', // 打包后的文件名称
path: path.resolve(__dirname, '../dist') // 打包后的目录
},
plugins: [
new HtmlWebpackPlugin({
template: path.resolve(__dirname, '../public/index.html'),
filename: 'index.html',
chunks: ['main'] // 与入口文件对应的模块名
}),
new HtmlWebpackPlugin({
template: path.resolve(__dirname, '../public/header.html'),
filename: 'header.html',
chunks: ['header'] // 与入口文件对应的模块名
})
]
}
自动清除上次的打包产物使用 clean-webpack-plugin 插件
const { CleanWebpackPlugin } = require('clean-webpack-plugin')
module.exports = {
// ...省略其他配置
plugins: [new CleanWebpackPlugin()]
}
使用 css
需要加入 loader
npm i -D style-loader css-loader
如果我们用 less 则安装相应的 loader
npm i -D less less-loader
修改配置文件
// webpack.config.js
module.exports = {
// ...省略其他配置
module: {
rules: [
{
test: /\.css$/,
use: ['style-loader', 'css-loader'] // 从右向左解析原则
},
{
test: /\.less$/,
use: ['style-loader', 'css-loader', 'less-loader'] // 从右向左解析原则
}
]
}
}
添加 css 前缀
npm i -D postcss-loader autoprefixer
修改配置
// webpack.config.js
module.exports = {
module: {
rules: [
{
test: /\.less$/,
use: ['style-loader', 'css-loader', 'postcss-loader', 'less-loader'] // 从右向左解析原则
}
]
}
}
引入 autoprefixer
- 第一种使用 postcss.config.js
module.exports = {
plugins: [require('autoprefixer')] // 引用该插件即可了
}
- 第二种直接在 webpack.config.js 中配置
// webpack.config.js
module.exports = {
//...省略其他配置
module: {
rules: [
{
test: /\.less$/,
use: [
'style-loader',
'css-loader',
{
loader: 'postcss-loader',
options: {
plugins: [require('autoprefixer')]
}
},
'less-loader'
] // 从右向左解析原则
}
]
}
}
拆分 css
npm i -D mini-css-extract-plugin
webpack4.0 以前通过extract-text-webapck-plugin
插件
修改配置文件
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
module.exports = {
//...省略其他配置
module: {
rules: [
{
test: /\.less$/,
use: [MiniCssExtractPlugin.loader, 'css-loader', 'less-loader']
}
]
},
plugins: [
new MiniCssExtractPlugin({
filename: '[name].[hash].css',
chunkFilename: '[id].css'
})
]
}
拆分多个 css 上面用到的 mini-css-extract-plugin 会将所有的 css 样式合并为一个 css 样式,而拆分为一个个对应的多个 css 文件需要用到 extract-text-webpack-plugin,而目前 mini-css-extract-plugin 还不支持此功能,安装 extract-text-webpack-plugin
npm i extract-text-webpack-plugin@next
// webpack.config.js
const path = require('path')
const ExtractTextWebpackPlugin = require('extract-text-webpack-plugin')
let indexLess = new ExtractTextWebpackPlugin('index.less')
let indexCss = new ExtractTextWebpackPlugin('index.css')
module.exports = {
module: {
rules: [
{
test: /\.css$/,
use: indexCss.extract({
use: ['css-loader']
})
},
{
test: /\.less$/,
use: indexLess.extract({
use: ['css-loader', 'less-loader']
})
}
]
},
plugins: [indexLess, indexCss]
}
处理文件、图片等 file-loader: 处理文件名解析 url,并将文件移动到输出的目录中 url-loader:一般与 file-loader 搭配使用,功能和 file-loader 类似,但是如果文件小于限制会编码 base64,否则使用 file-loader 移动文件到输出目录
// webpack.config.js
module.exports = {
// 省略其它配置 ...
module: {
rules: [
// ...
{
test: /\.(jpe?g|png|gif)$/i, //图片文件
use: [
{
loader: 'url-loader',
options: {
limit: 10240,
fallback: {
loader: 'file-loader',
options: {
name: 'img/[name].[hash:8].[ext]'
}
}
}
}
]
},
{
test: /\.(mp4|webm|ogg|mp3|wav|flac|aac)(\?.*)?$/, //媒体文件
use: [
{
loader: 'url-loader',
options: {
limit: 10240,
fallback: {
loader: 'file-loader',
options: {
name: 'media/[name].[hash:8].[ext]'
}
}
}
}
]
},
{
test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/i, // 字体
use: [
{
loader: 'url-loader',
options: {
limit: 10240,
fallback: {
loader: 'file-loader',
options: {
name: 'fonts/[name].[hash:8].[ext]'
}
}
}
}
]
}
]
}
}
babel 转义 js 文件
npm i -D babel-laoder @babel/preset-env @babel/core
注意 babel-loader 与 babel-core 版本对应
- babel-loader 8.x 对应 babel-core 7.x
- babel-laoder 7.x 对应 babel-core 6.x
// webpack.config.js
module.exports = {
// 省略其它配置 ...
module: {
rules: [
{
test: /\.js$/,
use: {
loader: 'babel-loader',
options: {
presets: ['@babel/preset-env']
}
},
exclude: /node_modules/
}
]
}
}
上面的 babel-loader 只会将 ES6/7/8 语法转换为 ES5 语法,但是对新 api 并不会转换 例如(promise、Generator、Set、Maps、Proxy 等) 此时我们需要借助 babel-polyfill 来帮助我们转换
npm i @babel/polyfill
// webpack.config.js
const path = require('path')
module.exports = {
entry: ['@babel/polyfill', path.resolve(__dirname, '../src/index.js')] // 入口文件
}
配置 webpack-dev-server 进行热更新
npm i -D webpack-dev-server
const Webpack = require('webpack')
module.exports = {
// ...省略其他配置
devServer: {
port: 3000,
hot: true,
contentBase: '../dist'
},
plugins: [new Webpack.HotModuleReplacementPlugin()]
}
一份完整的 vue 配置
// webpack.config.js
const path = require('path')
const { CleanWebpackPlugin } = require('clean-webpack-plugin')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
const ExtractTextWebpackPlugin = require('extract-text-webpack-plugin')
const vueLoaderPlugin = require('vue-loader/lib/plugin')
const Webpack = require('webpack')
module.exports = {
mode: 'development', // 开发模式
entry: {
main: path.resolve(__dirname, '../src/main.js')
},
output: {
filename: '[name].[hash:8].js', // 打包后的文件名称
path: path.resolve(__dirname, '../dist') // 打包后的目录
},
module: {
rules: [
{
test: /\.vue$/,
use: ['vue-loader']
},
{
test: /\.js$/,
use: {
loader: 'babel-loader',
options: {
presets: [['@babel/preset-env']]
}
}
},
{
test: /\.css$/,
use: [
'vue-style-loader',
'css-loader',
{
loader: 'postcss-loader',
options: {
plugins: [require('autoprefixer')]
}
}
]
},
{
test: /\.less$/,
use: [
'vue-style-loader',
'css-loader',
{
loader: 'postcss-loader',
options: {
plugins: [require('autoprefixer')]
}
},
'less-loader'
]
}
]
},
resolve: {
alias: {
vue$: 'vue/dist/vue.runtime.esm.js',
' @': path.resolve(__dirname, '../src')
},
extensions: ['*', '.js', '.json', '.vue']
},
devServer: {
port: 3000,
hot: true,
contentBase: '../dist'
},
plugins: [
new CleanWebpackPlugin(),
new HtmlWebpackPlugin({
template: path.resolve(__dirname, '../public/index.html'),
filename: 'index.html'
}),
new vueLoaderPlugin(),
new Webpack.HotModuleReplacementPlugin()
]
}
区分开发环境
- webpack.dev.js
- webpack.prod.js
生产环境主要实现的是压缩代码、提取css文件、合理的sourceMap、分割代码
需要安装以下模块:
npm i -D webpack-merge copy-webpack-plugin optimize-css-assets-webpack-plugin uglifyjs-webpack-plugin
- webpack-merge 合并配置
- copy-webpack-plugin 拷贝静态资源
- optimize-css-assets-webpack-plugin 压缩 css
- uglifyjs-webapck-plugin 压缩 js
webpack mode 设置 production 的时候会自动压缩 js 代码。原则上不需要引入 uglifyjs-webpack-plugin 进行重复工作。但是 optimize-css-assets-webpack-plugin 压缩 css 的同时会破坏原有的 js 压缩,所以这里我们引入 uglifyjs 进行压缩
const path = require('path')
const { CleanWebpackPlugin } = require('clean-webpack-plugin')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const vueLoaderPlugin = require('vue-loader/lib/plugin')
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
const devMode = process.argv.indexOf('--mode=production') === -1
module.exports = {
entry: {
main: path.resolve(__dirname, '../src/main.js')
},
output: {
path: path.resolve(__dirname, '../dist'),
filename: 'js/[name].[hash:8].js',
chunkFilename: 'js/[name].[hash:8].js'
},
module: {
rules: [
{
test: /\.js$/,
use: {
loader: 'babel-loader',
options: {
presets: ['@babel/preset-env']
}
},
exclude: /node_modules/
},
{
test: /\.vue$/,
use: [
{
loader: 'vue-loader',
options: {
compilerOptions: {
preserveWhitespace: false
}
}
}
]
},
{
test: /\.css$/,
use: [
{
loader: devMode ? 'vue-style-loader' : MiniCssExtractPlugin.loader,
options: {
publicPath: '../dist/css/',
hmr: devMode
}
},
'css-loader',
{
loader: 'postcss-loader',
options: {
plugins: [require('autoprefixer')]
}
}
]
},
{
test: /\.less$/,
use: [
{
loader: devMode ? 'vue-style-loader' : MiniCssExtractPlugin.loader,
options: {
publicPath: '../dist/css/',
hmr: devMode
}
},
'css-loader',
'less-loader',
{
loader: 'postcss-loader',
options: {
plugins: [require('autoprefixer')]
}
}
]
},
{
test: /\.(jep?g|png|gif)$/,
use: {
loader: 'url-loader',
options: {
limit: 10240,
fallback: {
loader: 'file-loader',
options: {
name: 'img/[name].[hash:8].[ext]'
}
}
}
}
},
{
test: /\.(mp4|webm|ogg|mp3|wav|flac|aac)(\?.*)?$/,
use: {
loader: 'url-loader',
options: {
limit: 10240,
fallback: {
loader: 'file-loader',
options: {
name: 'media/[name].[hash:8].[ext]'
}
}
}
}
},
{
test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/i,
use: {
loader: 'url-loader',
options: {
limit: 10240,
fallback: {
loader: 'file-loader',
options: {
name: 'media/[name].[hash:8].[ext]'
}
}
}
}
}
]
},
resolve: {
alias: {
vue$: 'vue/dist/vue.runtime.esm.js',
' @': path.resolve(__dirname, '../src')
},
extensions: ['*', '.js', '.json', '.vue']
},
plugins: [
new CleanWebpackPlugin(),
new HtmlWebpackPlugin({
template: path.resolve(__dirname, '../public/index.html')
}),
new vueLoaderPlugin(),
new MiniCssExtractPlugin({
filename: devMode ? '[name].css' : '[name].[hash].css',
chunkFilename: devMode ? '[id].css' : '[id].[hash].css'
})
]
}
webpack.dev.js
const Webpack = require('webpack')
const webpackConfig = require('./webpack.config.js')
const WebpackMerge = require('webpack-merge')
module.exports = WebpackMerge(webpackConfig, {
mode: 'development',
devtool: 'cheap-module-eval-source-map',
devServer: {
port: 3000,
hot: true,
contentBase: '../dist'
},
plugins: [new Webpack.HotModuleReplacementPlugin()]
})
webpack.prod.js
const path = require('path')
const webpackConfig = require('./webpack.config.js')
const WebpackMerge = require('webpack-merge')
const CopyWebpackPlugin = require('copy-webpack-plugin')
const OptimizeCssAssetsPlugin = require('optimize-css-assets-webpack-plugin')
const UglifyJsPlugin = require('uglifyjs-webpack-plugin')
module.exports = WebpackMerge(webpackConfig, {
mode: 'production',
devtool: 'cheap-module-source-map',
plugins: [
new CopyWebpackPlugin([
{
from: path.resolve(__dirname, '../public'),
to: path.resolve(__dirname, '../dist')
}
])
],
optimization: {
minimizer: [
new UglifyJsPlugin({
//压缩js
cache: true,
parallel: true,
sourceMap: true
}),
new OptimizeCssAssetsPlugin({})
],
splitChunks: {
chunks: 'all',
cacheGroups: {
libs: {
name: 'chunk-libs',
test: /[\\/]node_modules[\\/]/,
priority: 10,
chunks: 'initial' // 只打包初始时依赖的第三方
}
}
}
}
})