下载webpack
-
全局下载webpack 和 webpack-cli
cnpm install webpack webpack-cli -g // 使用webpack -v 查看版本号
-
项目内部下载webpack(为了解决webpack版本兼容)
打开项目文件夹使用命令
cnpm i webpack webpack-cli -D
因为在项目内部使用使用webpack -v 查看版本号会默认去全局目录查找,所以要使用npx webpck -v查看本地版本号
npx webpck -v
注意:
-
npx命令会帮助我们去node_moudles去查找文件
-
webpack-cli可以帮助我们正确在命令行里运行webpack命令
拓展:
- 【npm uninstall xxx】删除xxx模块;
- 【npm uninstall -g xxx】删除全局模块xxx;
-
- 项目内部下载固定版本号的webpack
- 首先用npm info webpack 可以查看到webpack存在的版本
- cnpm i webpack@4.16.5 webpack-cli -D
简单的webpck.config.js
webpck.config.js
as we konw webpack打包需要一个配置文件 来告诉它需要如何完成打包 这个文件就是webpck.config.js
const path = require('path')
module.exports = {
// mode设置为production会自动压缩文件 设置为 development则不会压缩
mode: 'production',
entry: './src/index.js',
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, 'dist')
}
}
运行命令开始打包
npx webpack
简化npx webpack命令
-
使用npm run script来简化npx webpack命令
-
在packjson.js中修改script字段
"script": { // 会默认去本地查找webpack依赖 "bundle": "webpack" }
-
以后运行 npx webpack就可以直接使用
npm run bundle
loader是什么
因为webpack不能识别非js结尾的文件,所以就需要借助loader来处理
loader就是一个打包方案,告诉你打包什么样的文件做什么样的处理
添加图片打包功能
需要下载file-loader
cnpm i file-loader -D
const path = require('path')
module.exports = {
// mode设置为production会自动压缩文件 设置为 development则不会压缩
mode: 'production',
entry: './src/index.js',
module: {
// 配置图片打包
rules: [{
test: /\.jpg$/,
use: {
loader: 'file-loader'
}
}]
}
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, 'dist')
}
}
使用loader打包静态资源(图片篇)
placeholder 占位符
module: {
// 配置图片打包
rules: [{
test: /\.(jpg|png|gif)$/,
use: {
loader: 'file-loader'
option: {
// placeholder 占位符
name: '[name]_[hash].[ext]',
// 打包目录
outputPath: 'images/'
}
}
}]
}
url-loader
url-loader是通过把图片改为base64放在里JS文件里
url-loader相比file-loader多了一个limit配置项
**注意:**如果图片过大则会影响JS加载时间,所以需要对图片的大小做限制
module: {
// 配置图片打包
rules: [{
test: /\.(jpg|png|gif)$/,
use: {
loader: 'url-loader'
option: {
// placeholder 占位符
name: '[name]_[hash].[ext]',
// 打包目录
outputPath: 'images/',
// 大小限制
limit: 10240
}
}
}]
}
使用loader打包静态资源(样式篇)
打包普通css文件
简单使用
module: {
// 配置图片打包
rules: [{
test: /\.(jpg|png|gif)$/,
use: {
loader: 'url-loader'
option: {
// placeholder 占位符
name: '[name]_[hash].[ext]',
// 打包目录
outputPath: 'images/',
// 大小限制
limit: 10240
}
}
}, {
test: /\.css$/,
use: ['style-loader', 'css-loader']
}]
}
- css-loader会把css文件整合到一个css文件
- style-loader会把css文件插入到style标签里去
处理scss文件
module: {
// 配置图片打包
rules: [{
test: /\.(jpg|png|gif)$/,
use: {
loader: 'url-loader'
option: {
// placeholder 占位符
name: '[name]_[hash].[ext]',
// 打包目录
outputPath: 'images/',
// 大小限制
limit: 10240
}
}
}, {
test: /\.scss$/,
use: ['style-loader', 'css-loader', 'scss-loader']
}]
}
**注意:**loader的执行顺序是从下到上,从右往左执行的
自动添加css前缀
-
使用postcss-loader
module: { // 配置图片打包 rules: [{ test: /\.(jpg|png|gif)$/, use: { loader: 'url-loader' option: { // placeholder 占位符 name: '[name]_[hash].[ext]', // 打包目录 outputPath: 'images/', // 大小限制 limit: 10240 } } }, { test: /\.scss$/, use: ['style-loader', 'css-loader', 'scss-loader', 'postcss-loader'] }] }
-
创建postcss.config.js文件
module.exports = { plugins: [ require('autoprefixer') ] }
css-loader常用配置项
importLoaders,modules
module: {
{
test: /\.scss$/,
use: [
'style-loader',
// css-loader常用配置项
{
loader: 'css-loader',
options: {
// 通过@import引入的css也要去走前两个loader(scss-loader和postcss-loader)
importLoaders: 2,
// 模块化引入css 对应的js文件不引入css则不应用css样式
modules: true
}
},
'scss-loader',
'postcss-loader'
]
}]
}
- importLoaders: 2 // 过@import引入的css也要去走前两个loader(scss-loader和postcss-loader)
- modules:true // 模块化引入css 对应的js文件不引入css则不应用css样式
打包字体文件
同样使用file-loader
{
test: /\.(eot|ttf|svg)$/,
use: {
loader: 'file-loader'
}
}
使用plugins让打包更便捷
plugins可以在webpack运行到某个时刻的时候,帮你做一些事情
htmlWebpackPlugin
htmlWebpackPlugin会在打包结束后,自动生成一个html文件,并把打包生成的js自动引入到这个html文件中
添加template会按照模板来生成html
cnpm i html-webpack-plugin -D
const path = require('path')
const HtmlWebpackPlugin = require('html-webpack-plugin')
module.exports = {
// mode设置为production会自动压缩文件 设置为 development则不会压缩
mode: 'production',
entry: './src/index.js',
module: {
// 配置图片打包
rules: [{
test: /\.jpg$/,
use: {
loader: 'file-loader'
}
}]
},
plugins: [
new HtmlWebpackPlugin({
template: 'src/index.htlm'
})
]
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, 'dist')
}
}
cleanWebpackPlugin
每次打包会清空dist目录下的文件
cnpm i clean-webpack-plugin -D
const path = require('path')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const CleanWebpackPlugin = require('clean-webpack-plugin')
module.exports = {
mode: 'production',
entry: './src/index.js',
module: {
rules: [{
test: /\.jpg$/,
use: {
loader: 'file-loader'
}
}]
},
plugins: [
// 配置html模板
new HtmlWebpackPlugin({
template: 'src/index.htlm'
}),
// 每次打包会清空dist目录下的文件
new CleanWebpackPlugin(['dist'])
]
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, 'dist')
}
}
Entry与Output的基础配置
多入口配置
在配置Entry的时候可以配置多入口,与之相应的Output也需要修改可以输出多个js文件
此时可以使用**[name]**占位符
const path = require('path')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const CleanWebpackPlugin = require('clean-webpack-plugin')
module.exports = {
mode: 'production',
entry: {
main: './src/index.js',
sub: './src/index.js'
},
module: {
rules: [{
test: /\.jpg$/,
use: {
loader: 'file-loader'
}
}]
},
plugins: [
new HtmlWebpackPlugin({
template: 'src/index.htlm'
}),
new CleanWebpackPlugin(['dist'])
]
output: {
// 使用**[name]**占位符 对应entry输出的文件名
filename: '[name].js',
path: path.resolve(__dirname, 'dist')
}
}
公共前缀publicPath
可以自动添加cdn前缀
const path = require('path')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const CleanWebpackPlugin = require('clean-webpack-plugin')
module.exports = {
mode: 'production',
entry: {
main: './src/index.js',
sub: './src/index.js'
},
module: {
rules: [{
test: /\.jpg$/,
use: {
loader: 'file-loader'
}
}]
},
plugins: [
new HtmlWebpackPlugin({
template: 'src/index.htlm'
}),
new CleanWebpackPlugin(['dist'])
]
output: {
// 使用publicPath 添加公共前缀
publicPath: 'http://cdn.com.cn'
// 使用**[name]**占位符 对应entry输出的文件名
filename: '[name].js',
path: path.resolve(__dirname, 'dist')
}
}
SourceMap配置
sourceMap它是一个映射关系,它知道打包后的文件下有语法错误,通过SourceMap的映射,能够对应到开发文件下的具体出错位置
**注意:当mode: 'production'**时devtoll会自动开启
选择一种 source map 格式来增强调试过程。不同的值会明显影响到构建(build)和重新构建(rebuild)的速度。
devtool | 构建速度 | 重新构建速度 | 生产环境 | 品质(quality) |
---|---|---|---|---|
(none) | +++ | +++ | yes | 打包后的代码 |
eval | +++ | +++ | no | 生成后的代码 |
cheap-eval-source-map | + | ++ | no | 转换过的代码(仅限行) |
cheap-module-eval-source-map | o | ++ | no | 原始源代码(仅限行) |
eval-source-map | -- | + | no | 原始源代码 |
cheap-source-map | + | o | no | 转换过的代码(仅限行) |
cheap-module-source-map | o | - | no | 原始源代码(仅限行) |
inline-cheap-source-map | + | o | no | 转换过的代码(仅限行) |
inline-cheap-module-source-map | o | - | no | 原始源代码(仅限行) |
source-map | -- | -- | yes | 原始源代码 |
inline-source-map | -- | -- | no | 原始源代码 |
hidden-source-map | -- | -- | yes | 原始源代码 |
nosources-source-map | -- | -- | yes | 无源代码内容 |
实例
const path = require('path')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const CleanWebpackPlugin = require('clean-webpack-plugin')
module.exports = {
mode: 'development',
// 使用SourceMap
// development devtool: 'cheap-module-eval-source-map'
// production devtool: 'cheap-module-source-map'
devtool: 'cheap-module-eval-source-map',
entry: {
main: './src/index.js',
sub: './src/index.js'
},
module: {
rules: [{
test: /\.jpg$/,
use: {
loader: 'file-loader'
}
}]
},
plugins: [
new HtmlWebpackPlugin({
template: 'src/index.htlm'
}),
new CleanWebpackPlugin(['dist'])
]
output: {
publicPath: 'http://cdn.com.cn'
filename: '[name].js',
path: path.resolve(__dirname, 'dist')
}
}
注意:
// development devtool: 'cheap-module-eval-source-map'
// production devtool: 'cheap-module-source-map'
使用WebpackDevServer提升开发效率
使用watch
使用watch监听文件变化自动重新打包
"script": {
// 使用watch监听文件变化自动重新打包
"watch": "webpack --watch"
}
使用WebpackDevServer
-
下载webpack-dev-server
cnpm i webpack-dev-server -D
-
对webpck.config.js进行修改
const path = require('path') const HtmlWebpackPlugin = require('html-webpack-plugin') const CleanWebpackPlugin = require('clean-webpack-plugin') module.exports = { mode: 'development', devtool: 'cheap-module-eval-source-map', entry: { main: './src/index.js', sub: './src/index.js' }, devServer: { // contentBase 自动去dist目录寻找文件 contentBase: './dist', // open 自动打开里篮球 open: true, // proxy 会配置跨越 proxy: { '/api': 'http://localhost:3000' } // port 设置打开的端口号 port: 8080 }, module: { rules: [{ test: /\.jpg$/, use: { loader: 'file-loader' } }] }, plugins: [ new HtmlWebpackPlugin({ template: 'src/index.htlm' }), new CleanWebpackPlugin(['dist']) ] output: { publicPath: 'http://cdn.com.cn' filename: '[name].js', path: path.resolve(__dirname, 'dist') } }
-
改script命令
"script": { // 使用watch监听文件变化自动重新打包 "start": "webpack-dev-server " }
使用middleware自己搭建devServer服务器
-
更改script命令
"script": { // 使用middleware监听文件变化自动重新打包 "middleware": "node server.js" }
-
下载插件
cnpm install express webpack-dev-middleware -D
-
创建server.js
const express = require('express') const webpack = require('webpack') const webpackDevMiddleware = require('webpack-dev-middleware') // 引入配置文件 const config = require('./webpack.config.js') // webpack编译器 const complier = webpack(config) const app = express() // 使用webpackDevMiddleware中间件 app.use(webpackDevMiddleware(complier, { publicePath: config.output.publicePath }) app.listen(3000, () => { console.log('server is running') })
Hot Module Replacement(热模块更新)
模块热替换(HMR - hot module replacement)功能会在应用程序运行过程中,替换、添加或删除 模块,而无需重新加载整个页面。主要是通过以下几种方式,来显著加快开发速度:
- 保留在完全重新加载页面期间丢失的应用程序状态。
- 只更新变更内容,以节省宝贵的开发时间。
- 在源代码中对 CSS/JS 进行修改,会立刻在浏览器中进行更新,这几乎相当于在浏览器 devtools 直接更改样式。
-
启用 webpack 的 模块热替换 功能:
const webpack = require('webpack') module.exports = { //... devServer: { hot: true, // 在构建失败时不需要刷新页面作为回退。 hotOnly: true }, //... plugins: [ new webpack.HotModuleReplacementPlugin() ] };
**注意:**对于css文件cssloader已经默认为我们编写下面一段代码了会进行模块热替换 但是对应JS文件,需要手动去添加代码
import number from './number'
if (module.hot) {
module.hot.accept('./number', () => {
number()
})
}
但是在vue与react中也帮我们完成了这些代码
使用Babel处理ES6语法
@babel/polyfill(常用于写业务代码)
-
下载
cnpm i babel-loader @babel/core @babel/preset-env @babel/polyfill -D
注意:
-
@babel/core是babel-loader的核心包
-
@babel/polyfill里面放的是把es6转到es5的方法 需要引入到对应的JS文件但是使用 **useBuiltIns: 'usage'**后会自动导入@babel/polyfill,不需要手动引入
-
@babel/preset-env是配置插件
-
-
增加规则
webpack.config.js
const webpack = require('webpack') module.exports = { //... module: { rules: [ { test: /\.js$/, // 排除node_modules文件夹 exclude: /node_modules/, loader: "babel-loader", // options配置参数 options: { // presets: ["@babel/preset-env"] presets: [["@babel/preset-env", { // 打包之后会运行在chrome67版本以上的环境下 targets: { chrome: "67", }, // 按需引入@babel/polyfill把es6转到es5的方法 useBuiltIns: 'usage' }]] } } ] } };
@babel/plugin-transform-runtime(常用于写库代码)
注入时以闭包的形式,不污染全局变量
-
下载
npm install --save-dev @babel/plugin-transform-runtime npm install --save @babel/runtime npm install --save @babel/runtime-corejs2
corejs
optionInstall command false
npm install --save @babel/runtime
2
npm install --save @babel/runtime-corejs2
3
npm install --save @babel/runtime-corejs3
-
使用配置
const webpack = require('webpack') module.exports = { //... module: { rules: [ { test: /\.js$/, // 排除node_modules文件夹 exclude: /node_modules/, loader: "babel-loader", // options配置参数 options: { "plugins": [ [ "@babel/plugin-transform-runtime", { "absoluteRuntime": false, "corejs": 2, "helpers": true, "regenerator": true, "useESModules": false, "version": "7.0.0-beta.0" } ] ] } } ] } };
把options配置参数提取到.babelrc文件下
创建.babelrc文件
把options配置参数剪切到.babelrc文件
{
"plugins": [
[
"@babel/plugin-transform-runtime",
{
"absoluteRuntime": false,
"corejs": 2,
"helpers": true,
"regenerator": true,
"useESModules": false,
"version": "7.0.0-beta.0"
}
]
]
}
实现对React框架代码打包
-
下载
npm install --save-dev @babel/preset-react
-
配置babel-loader(在.babelrc文件下配置)
{ "presets": [ ["@babel/preset-env", { target: { chrom: "67", }, useBuiltIns: "usage" }], [ "@babel/preset-react", { "pragma": "dom", // default pragma is React.createElement (only in classic runtime) "pragmaFrag": "DomFrag", // default is React.Fragment (only in classic runtime) "throwIfNamespace": false, // defaults to true "runtime": "classic" // defaults to classic // "importSource": "custom-jsx-library" // defaults to react (only in automatic runtime) } ] ] }
Tree Shaking (ES模块按需引入)
mode为development时默认没有Tree Shaking
-
在webpack.config.js中修改
const webpack = require('webpack') module.exports = { mode: "development", //... optimization: { usedExports: true } };
-
在package.json中修改
添加sideEffects字段可以把一些不需要按需引入的模块排除
{ //... "sideEffects": ["@babel/polyfill"] }
如果没有需要特殊处理的模块,可以把sideEffects设置为false
Code Splitting(代码分割)
同步代码分割
main.js被拆成loadsh.js(1MB),main.js(1MB)
当业务逻辑发生变化时,只要加载main.js即可(1MB)
webpack.config.js
{
//...
// 只需再optimization里面做配置即可
optimization: {
splitChunks: {
chunks: 'all'
}
}
}
异步代码自动分割(import)
当代码内部有异步加载的时候,无需做任何配置,webpack会自动帮你分割到另一个文件
SplitChunksPlugin规则
做代码分割时,默认会有一个SplitChunksPlugin 配置及规则
SplitChunksPlugin 默认配置及规则
module.exports = {
//...
optimization: {
splitChunks: {
chunks: 'async',
minSize: 30000,
maxSize: 0,
minChunks: 1,
maxAsyncRequests: 5,
maxInitialRequests: 3,
automaticNameDelimiter: '~',
name: true,
cacheGroups: {
vendors: {
test: /[\\/]node_modules[\\/]/,
priority: -10
},
default: {
minChunks: 2,
priority: -20,
reuseExistingChunk: true
}
}
}
}
};
- 新代码块可以被共享引用,或这些模块都是来自node_modules
- 新产出的vendor-chunk的大小得大于30kb
- 按需加载的代码块(vendor-chunk)并行请求的数量小于或等于5个
- 初始加载的代码块,并行请求的数量小于或者等于3个
CSS文件代码分割(MiniCssExtractPlugin)
默认情况下webpack会把引入的css文件编译成JS打包,如果我们想把css分离出来单独打包我们就需要借助插件MiniCssExtractPlugin来实现(不支持模块热更新)一般在线上环境中使用
-
下载
npm install --save-dev mini-css-extract-plugin
-
修改webpack.config.js
const MiniCssExtractPlugin = require("mini-css-extract-plugin"); module.exports = { plugins: [ new MiniCssExtractPlugin({ // Options similar to the same options in webpackOptions.output // both options are optional filename: "[name].css", chunkFilename: "[id].css" }) ], module: { rules: [ { test: /\.css$/, use: [ { loader: MiniCssExtractPlugin.loader, options: { // you can specify a publicPath here // by default it use publicPath in webpackOptions.output publicPath: '../' } }, "css-loader" ] } ] } }
-
可以会与Tree Shaking冲突,导致未使用的css文件不参与打包,需在package.json中排除CSS文件的模块化按需加载
package.json
{ //... "sideEffects": ["*.css"] }
Css代码压缩
-
下载
npm i optimize-css-assets-webpack-plugin -D
-
使用
const MiniCssExtractPlugin = require("mini-css-extract-plugin"); const OptimizeCSSAssetsPlugin = require("optimize-css-assets-webpack-plugin"); module.exports = { optimization: { minimizer: [ new OptimizeCSSAssetsPlugin({}) ] }, plugins: [ new MiniCssExtractPlugin({ filename: "[name].css", chunkFilename: "[id].css" }) ], module: { rules: [ { test: /\.css$/, use: [ MiniCssExtractPlugin.loader, "css-loader" ] } ] } }
Dev和prod区别打包
-
创建两个webpack.config.js文件webpack.dev.js与webpack.prod.js
-
修改script命令
"script": { "dev": "webpack-dev-server --config webpack.dev.js", "build": "webpack --config webpack.prod.js" }
-
可以把dev和prod共用的代码提取出来到webpack.common.js中去(使用webpack-merge进行合并)
webpack.common.js
const path = require('path'); const HtmlWebpackPlugin = require('html-webpack-plugin'); const CleanWebpackPlugin = require('clean-webpack-plugin'); module.exports = { entry: { main: './src/index.js', }, module: { rules: [{ test: /\.js$/, exclude: /node_modules/, use: [{ loader: 'babel-loader' }] }, { test: /\.(jpg|png|gif)$/, use: { loader: 'url-loader', options: { name: '[name]_[hash].[ext]', outputPath: 'images/', limit: 10240 } } }, { test: /\.(eot|ttf|svg)$/, use: { loader: 'file-loader' } }] }, plugins: [ new HtmlWebpackPlugin({ template: 'src/index.html' }), new CleanWebpackPlugin(['dist'], { root: path.resolve(__dirname, '../') }) ], optimization: { runtimeChunk: { name: 'runtime' }, usedExports: true, splitChunks: { chunks: 'all', cacheGroups: { vendors: { test: /[\\/]node_modules[\\/]/, priority: -10, name: 'vendors', } } } }, performance: false, output: { path: path.resolve(__dirname, '../dist') } }
webpack.dev.js
const webpack = require('webpack'); const merge = require('webpack-merge'); const commonConfig = require('./webpack.common.js'); const devConfig = { mode: 'development', devtool: 'cheap-module-eval-source-map', devServer: { contentBase: './dist', open: true, port: 8080, hot: true }, module: { rules: [{ test: /\.scss$/, use: [ 'style-loader', { loader: 'css-loader', options: { importLoaders: 2 } }, 'sass-loader', 'postcss-loader' ] }, { test: /\.css$/, use: [ 'style-loader', 'css-loader', 'postcss-loader' ] }] }, plugins: [ new webpack.HotModuleReplacementPlugin() ], output: { filename: '[name].js', chunkFilename: '[name].js', } } module.exports = merge(commonConfig, devConfig);
webpack.prod.js
const MiniCssExtractPlugin = require("mini-css-extract-plugin"); const OptimizeCSSAssetsPlugin = require("optimize-css-assets-webpack-plugin"); const WorkboxPlugin = require('workbox-webpack-plugin'); const merge = require('webpack-merge'); const commonConfig = require('./webpack.common.js'); const prodConfig = { mode: 'production', devtool: 'cheap-module-source-map', module: { rules:[{ test: /\.scss$/, use: [ MiniCssExtractPlugin.loader, { loader: 'css-loader', options: { importLoaders: 2 } }, 'sass-loader', 'postcss-loader' ] }, { test: /\.css$/, use: [ MiniCssExtractPlugin.loader, 'css-loader', 'postcss-loader' ] }] }, optimization: { minimizer: [new OptimizeCSSAssetsPlugin({})] }, plugins: [ new MiniCssExtractPlugin({ filename: '[name].css', chunkFilename: '[name].chunk.css' }), new WorkboxPlugin.GenerateSW({ clientsClaim: true, skipWaiting: true }) ], output: { filename: '[name].[contenthash].js', chunkFilename: '[name].[contenthash].js' } } module.exports = merge(commonConfig, prodConfig);