上一篇主要說了webpack處理ES Module 模塊引入方式的打包
其實webpack也支持其他的規(guī)范打包如CommonJs,CMD,AMD...
npx 會自動查找當(dāng)前依賴包中的可執(zhí)行文件为障,如果找不到部服,就會去 PATH 里找拗慨。如果依然找不到,就會幫你安裝声功。
上一篇說到的宠叼,npx webpack index.js
意思就是npx幫你找到可執(zhí)行的webpack然后去打包index.js冒冬,當(dāng)然,我們也可以通過簡單的配置webpack.config.js
通過配置script命令去執(zhí)行你的打包命令剂邮,配置好后挥萌,只要執(zhí)行npm run build
就可以了枉侧,代碼如下
webpack.config.js
const path = require('path');
module.exports = {
entry: './src/index.js',
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, 'dist')
}
}
package.json
{
"name": "webpack-demo",
"version": "1.0.0",
"description": "",
"main": "index.js",
"dependencies": {
"webpack-cli": "^3.3.12",
"webpack": "^4.44.0"
},
"devDependencies": {},
"scripts": {
"build": "webpack"
},
"author": "",
"license": "ISC"
}
webpack-cli 是webpack提供的一個node工具鏈榨馁,從而是我們可以直接執(zhí)行webpack npx 等翼虫,當(dāng)你不寫配置文件的時候,webpack 會執(zhí)行他的內(nèi)部打包默認配置蛙讥。webpack.config.js是默認寫法锯蛀。假如你把配置文件寫成config.js,那么你在執(zhí)行打包命令的時候次慢,就需要這么寫
webpack --config config.js
告訴webpack他的打包配置文件是config.js
WARNING in configuration
The 'mode' option has not been set, webpack will fallback to 'production' for this value. Set 'mode' option to 'development' or 'production' to enable defaults for each environment.
You can also set it to 'none' to disable any default behavior. Learn more: https://webpack.js.org/configuration/mode/
你會發(fā)現(xiàn)打包的時候有這樣的警告旁涤,大概意思是,讓你配置mode(環(huán)境) development(代碼沒有被壓縮方便調(diào)試) 或者 production(代碼被壓縮)迫像,接下來我們配置一下
const path = require('path');
module.exports = {
mode: 'development',
entry: {
main: './src/index.js'
},
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, 'dist')
}
}
執(zhí)行webpack 打包的時候輸出如下
Hash: 84bea18a4a1fab7a5786 本次打包的唯一hash
Version: webpack 4.44.0 使用webpack的版本
Time: 73ms 本次打包總共耗時
Built at: 2020/07/28 下午4:24:30 編譯完日期
Asset Size Chunks Chunk Names 輸出的一下表格目錄結(jié)構(gòu) 編譯出的文件、大小闻妓、文件的id菌羽、編譯的文件名
bundle.js 6.08 KiB main [emitted] main
Entrypoint main = bundle.js
[./src/content.js] 179 bytes {main} [built]
[./src/footer.js] 176 bytes {main} [built]
[./src/header.js] 176 bytes {main} [built]
[./src/index.js] 135 bytes {main} [built]
webpack 默認只認識js 文件,當(dāng)引入其他文件的時候該怎么打包呢由缆,舉個例子注祖,打包圖片,首先安裝
npm install file-loader -D
然后配置module就可以打包圖片了猾蒂,配置如下
const path = require('path');
module.exports = {
mode: 'development',
entry: {
main: './src/index.js'
},
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, 'dist')
},
module: {
rules: [
{
test: /\.(png|jpe?g|gif)$/i, // i 正則,代表結(jié)尾是晨, 以.png .jpg等結(jié)尾的文件肚菠,同樣可以配置 txt excel 等所有文件
use: 'file-loader',
options: {
name: '[name]_[hash].[ext]', // 圖片重命名
outputPath: 'images/' // 輸入文件目錄
}
}
]
}
}
url-loader也可以實現(xiàn)圖片打包,可以替代file-loader
const path = require('path');
module.exports = {
mode: 'development',
entry: {
main: './src/index.js'
},
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, 'dist')
},
module: {
rules: [
{
test: /\.(png|jpe?g|gif)$/i,
use: {
loader: 'url-loader',
options: {
name: '[name]_[hash].[ext]',
outputPath: 'images/',
limit: 2048 // `url-loader` 功能類似于 [`file-loader`](https://github.com/webpack-contrib/file-loader)罩缴,但是在文件大形梅辍(單位 byte)低于指定的限制時,可以返回一個 DataURL箫章,小于轉(zhuǎn)成base64打包到j(luò)s里反之輸出文件
}
}
}
]
}
}
打包css樣式
const path = require('path');
module.exports = {
mode: 'development',
entry: {
main: './src/index.js'
},
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, 'dist')
},
module: {
rules: [
{
test: /\.(png|jpe?g|gif)$/i,
use: {
loader: 'url-loader',
options: {
name: '[name]_[hash].[ext]',
outputPath: 'images/',
limit: 2048
}
}
},
{
test: /\.css$/i,
use: [ 'style-loader', 'css-loader' ] // css-loader會分析用到了幾個css把所有的css合并成一個css烙荷, 'style-loader',會把合并好的css掛載到head標(biāo)簽里,執(zhí)行順序檬寂,從下到上终抽,右到左
}
]
}
}
scss的使用
npm install sass-loader node-sass webpack --save-dev
const path = require('path');
module.exports = {
mode: 'development',
entry: {
main: './src/index.js'
},
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, 'dist')
},
module: {
rules: [
{
test: /\.(png|jpe?g|gif)$/i,
use: {
loader: 'url-loader',
options: {
name: '[name]_[hash].[ext]',
outputPath: 'images/',
limit: 2048
}
}
},
{
test: /\.s[ac]ss$/i,
use: [ 'style-loader', 'css-loader', 'sass-loader' ]
}
]
}
}
做css瀏覽器兼容,這時候就得需要postcss-loader了
npm i -D postcss-loader
,npm i autoprefixer -D
postcss-loader插件實現(xiàn)自動配置私有前綴焰薄,
const path = require('path');
module.exports = {
mode: 'development',
entry: {
main: './src/index.js'
},
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, 'dist')
},
module: {
rules: [
{
test: /\.(png|jpe?g|gif)$/i,
use: {
loader: 'url-loader',
options: {
name: '[name]_[hash].[ext]',
outputPath: 'images/',
limit: 2048
}
}
},
{
test: /\.scss$/i,
use: [
'style-loader',
{
loader: 'css-loader',
options: {
importLoaders: 2,
modules: true // 開啟模塊化,這樣樣式就用作用域扒袖,就可以在代碼中怎么引入`import style from './index.scss';`
}
},
'sass-loader',
'postcss-loader'
]
}
]
}
}
然后創(chuàng)建postcss.config.js引用loader插件塞茅,postcss.config.js查找規(guī)則由當(dāng)前文件向外層文件查找,找到停止季率,所以可以在不同文件中配置不用的配置
module.exports = {
plugins: [
require('autoprefixer')
]
}
最后結(jié)合package.json中的browserslist 做樣式兼容
{
"name": "webpack-demo",
"version": "1.0.0",
"description": "",
"main": "index.js",
"dependencies": {
"webpack-cli": "^3.3.12"
},
"devDependencies": {
"autoprefixer": "^9.8.5",
"css-loader": "^4.0.0",
"file-loader": "^6.0.0",
"node-sass": "^4.14.1",
"postcss-loader": "^3.0.0",
"sass-loader": "^9.0.2",
"style-loader": "^1.2.1",
"url-loader": "^4.1.0",
"webpack": "^4.44.0"
},
"scripts": {
"build": "webpack"
},
"author": "",
"license": "ISC",
"browserslist": [
"last 2 versions",
"> 1%",
"iOS 7",
"last 3 iOS versions"
]
}
字體文件也可以通過file-loader配置
const path = require('path');
module.exports = {
mode: 'development',
entry: {
main: './src/index.js'
},
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, 'dist')
},
module: {
rules: [
{
test: /\.(png|jpe?g|gif)$/i,
use: {
loader: 'url-loader',
options: {
name: '[name]_[hash].[ext]',
outputPath: 'images/',
limit: 2048
}
}
},
{
test: /\.scss$/i,
use: [
'style-loader',
{
loader: 'css-loader',
options: {
importLoaders: 2,
// modules: true
}
},
'sass-loader',
'postcss-loader'
]
},
{
test: /\.(eot|woff2|woff|ttf|svg)$/,
use: [
'file-loader',
]
}
]
}
}
plugin的簡單使用野瘦,如:
html-webpack-plugin 會在打包結(jié)束后,自動生成一個html文件飒泻,并把打包生成的js自動引入到這個html中
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
mode: 'development',
entry: {
main: './src/index.js'
},
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, 'dist')
},
plugins: [new HtmlWebpackPlugin()],
module: {
rules: [
{
test: /\.(png|jpe?g|gif)$/i,
use: {
loader: 'url-loader',
options: {
name: '[name]_[hash].[ext]',
outputPath: 'images/',
limit: 2048
}
}
},
{
test: /\.scss$/i,
use: [
'style-loader',
{
loader: 'css-loader',
options: {
importLoaders: 2,
// modules: true
}
},
'sass-loader',
'postcss-loader'
]
},
{
test: /\.(eot|woff2|woff|ttf|svg)$/,
use: [
'file-loader',
]
}
]
}
}
loader是什么鞭光,webpack不能識別非js結(jié)尾的文件,所以就需要loader告訴webpack怎么去打包編譯,而plugin泞遗,更像是一個生命周期函數(shù)惰许,他可以在webpack運行到某個時刻的時候,幫你做一些事情
clean-webpack-plugin使用
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
module.exports = {
mode: 'development',
entry: {
main: './src/index.js'
},
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, 'dist')
},
plugins: [
new HtmlWebpackPlugin({
template: 'src/index.html'
}),
new CleanWebpackPlugin() // 默認清理output.path中的文件
],
module: {
rules: [
{
test: /\.(png|jpe?g|gif)$/i,
use: {
loader: 'url-loader',
options: {
name: '[name]_[hash].[ext]',
outputPath: 'images/',
limit: 2048
}
}
},
{
test: /\.scss$/i,
use: [
'style-loader',
{
loader: 'css-loader',
options: {
importLoaders: 2,
// modules: true
}
},
'sass-loader',
'postcss-loader'
]
},
{
test: /\.(eot|woff2|woff|ttf|svg)$/,
use: [
'file-loader',
]
}
]
}
}
多個打包入口的配置史辙,配置多個打包入口的時候輸出文件的名字就不可以寫死了汹买,可以使用動態(tài)占位符,[name] [hash]等等聊倔,html-webpack-plugin也會自動幫你引入打包出來的所有js晦毙,同時你也可以給output配置參數(shù),如publicPath輸出文件的目錄耙蔑,域名等
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
module.exports = {
mode: 'development',
entry: {
main: './src/index.js',
sub: './src/index.js'
},
output: {
publicPath: 'http://www.cdn.cn',
filename: '[name].js',
path: path.resolve(__dirname, 'dist')
},
plugins: [
new HtmlWebpackPlugin({
template: 'src/index.html'
}),
new CleanWebpackPlugin()
],
module: {
rules: [
{
test: /\.(png|jpe?g|gif)$/i,
use: {
loader: 'url-loader',
options: {
name: '[name]_[hash].[ext]',
outputPath: 'images/',
limit: 2048
}
}
},
{
test: /\.scss$/i,
use: [
'style-loader',
{
loader: 'css-loader',
options: {
importLoaders: 2,
// modules: true
}
},
'sass-loader',
'postcss-loader'
]
},
{
test: /\.(eot|woff2|woff|ttf|svg)$/,
use: [
'file-loader',
]
}
]
}
}
sourceMap的配置
sourceMap,會在當(dāng)你打包出來的文件報錯的時候见妒,幫你找到對應(yīng)代碼的映射關(guān)系,從而準(zhǔn)確的定位到源代碼里面的錯誤在哪里
開發(fā)環(huán)境推薦配置 devtool: 'cheap-module-eval-source-map' 這種打包速度快甸陌,同時錯誤提示比較全
生產(chǎn)環(huán)境推薦配置 devtool: 'cheap-module-source-map', 便于調(diào)試须揣,當(dāng)然你也可以配置成none 關(guān)閉映射盐股。
cheap 只提示多好行出錯了,不提示多少列返敬。module除了業(yè)務(wù)代碼遂庄,一些loader里面的錯誤也報一下。 source-map自動生成.map文件劲赠。inline映射關(guān)系打包在代碼里 涛目。eval把對應(yīng)的代碼和source-map一起執(zhí)行,提高打包效率
參考文檔
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', // 參打開代碼映射凛澎,參數(shù)none 關(guān)閉
entry: {
main: './src/index.js',
sub: './src/index.js'
},
output: {
publicPath: './',
filename: '[name].js',
path: path.resolve(__dirname, 'dist')
},
plugins: [
new HtmlWebpackPlugin({
template: 'src/index.html'
}),
new CleanWebpackPlugin()
],
module: {
rules: [
{
test: /\.(png|jpe?g|gif)$/i,
use: {
loader: 'url-loader',
options: {
name: '[name]_[hash].[ext]',
outputPath: 'images/',
limit: 2048
}
}
},
{
test: /\.scss$/i,
use: [
'style-loader',
{
loader: 'css-loader',
options: {
importLoaders: 2,
// modules: true
}
},
'sass-loader',
'postcss-loader'
]
},
{
test: /\.(eot|woff2|woff|ttf|svg)$/,
use: [
'file-loader',
]
}
]
}
}
output.library和output.libraryTarget屬性可能大家都會比較陌生霹肝,因為一般如果只在項目中使用 webpack 不需要關(guān)注這兩個屬性,但是如果是開發(fā)類庫塑煎,那么這兩個屬性就是必須了解的沫换。
umd
(function webpackUniversalModuleDefinition(..){..})(..)
amd
define(['demo'], function(demo) {
demo();
});
commonjs
const demo = require('demo');
demo();
es module
import demo from 'demo';
demo();
大家思考一下,為什么這個類庫能支持不同方式的引入最铁?如何實現(xiàn)的讯赏?這就是 webpack 配置output.library和output.libraryTarget提供的功能。
熱更新
webpack --watch
最簡單的
webpack-dev-server
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', // 參打開代碼映射冷尉,參數(shù)none 關(guān)閉
entry: {
main: './src/index.js',
sub: './src/index.js'
},
devServer: {
contentBase: './dist',
open: true
},
plugins: [
new CleanWebpackPlugin({ cleanStaleWebpackAssets: false }),
new HtmlWebpackPlugin({
title: 'Development',
template: 'src/index.html'
})
],
output: {
filename: '[name].js',
path: path.resolve(__dirname, 'dist')
},
module: {
rules: [
{
test: /\.(png|jpe?g|gif)$/i,
use: {
loader: 'url-loader',
options: {
name: '[name]_[hash].[ext]',
outputPath: 'images/',
limit: 2048
}
}
},
{
test: /\.scss$/i,
use: [
'style-loader',
{
loader: 'css-loader',
options: {
importLoaders: 2,
// modules: true
}
},
'sass-loader',
'postcss-loader'
]
},
{
test: /\.(eot|woff2|woff|ttf|svg)$/,
use: [
'file-loader',
]
}
]
}
}
運行 "start": "webpack-dev-server ",
使用webpack-dev-middleware
實現(xiàn)一個webpack-dev-server漱挎, 在node中使用webpack
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', // 參打開代碼映射,參數(shù)none 關(guān)閉
entry: {
main: './src/index.js',
sub: './src/index.js'
},
devServer: {
contentBase: './dist',
open: true
},
plugins: [
new CleanWebpackPlugin({ cleanStaleWebpackAssets: false }),
new HtmlWebpackPlugin({
title: 'Development',
template: 'src/index.html'
})
],
output: {
publicPath: '/', // 指向跟目錄雀哨。對應(yīng)server.js 一個node服務(wù)
filename: '[name].js',
path: path.resolve(__dirname, 'dist')
},
module: {
rules: [
{
test: /\.(png|jpe?g|gif)$/i,
use: {
loader: 'url-loader',
options: {
name: '[name]_[hash].[ext]',
outputPath: 'images/',
limit: 2048
}
}
},
{
test: /\.scss$/i,
use: [
'style-loader',
{
loader: 'css-loader',
options: {
importLoaders: 2,
// modules: true
}
},
'sass-loader',
'postcss-loader'
]
},
{
test: /\.(eot|woff2|woff|ttf|svg)$/,
use: [
'file-loader',
]
}
]
}
}
server.js
const express = require('express');
const webpack = require('webpack');
const webpackDevMiddleware = require('webpack-dev-middleware');
const config = require('./webpack.config');
const complier = webpack(config); // 返回一個編譯器
const app = express();
app.use(webpackDevMiddleware(complier, {
publicPath: config.output.publicPath
}))
app.listen(3000, () => {
console.log('server in running!!!');
})
運行 "middleware": "node server.js"
Hot Module Replacement 熱模塊替換HMR磕谅,比如我更改了頁面的樣式,webpack檢測到代碼變化雾棺,會給我們重新打包編譯膊夹,導(dǎo)致,頁面重新加載捌浩,然而這并不是我們想要的放刨,我們就可以通過HMR進行熱模塊替換,dom不變只改變樣式尸饺,不觸發(fā)所有文件的重新打包宏榕。
當(dāng)你使用了HMR的時候,樣式改變不會重新加載整個html侵佃。只會替換css麻昼。當(dāng)多個js模塊引用,其中一個js模塊發(fā)生改變的時候馋辈,這個這么寫抚芦,從而只更新這一個模塊。比如在寫vue項目中會有這樣類似的功能但是我們代碼中并沒有寫類似module.hot.accept
這樣的代碼,這是因為vue-loader已經(jīng)替我們做了這一步
if(module.hot) {
module.hot.accept('監(jiān)聽變化的模塊地址', () => {
代碼發(fā)生改變時所執(zhí)行的事
})
}
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
const webpack = require('webpack');
module.exports = {
mode: 'development',
devtool: 'cheap-module-eval-source-map', // 參打開代碼映射叉抡,參數(shù)none 關(guān)閉
entry: {
main: './src/index.js',
sub: './src/index.js'
},
devServer: {
contentBase: './dist',
open: true,
hot: true,
hotOnly: true
},
plugins: [
new CleanWebpackPlugin({ cleanStaleWebpackAssets: false }),
new HtmlWebpackPlugin({
title: 'Development',
template: 'src/index.html'
}),
new webpack.HotModuleReplacementPlugin()
],
output: {
publicPath: '/', // 指向跟目錄尔崔。對應(yīng)server.js 一個node服務(wù)
filename: '[name].js',
path: path.resolve(__dirname, 'dist')
},
module: {
rules: [
{
test: /\.(png|jpe?g|gif)$/i,
use: {
loader: 'url-loader',
options: {
name: '[name]_[hash].[ext]',
outputPath: 'images/',
limit: 2048
}
}
},
{
test: /\.scss$/i,
use: [
'style-loader',
{
loader: 'css-loader',
options: {
importLoaders: 2,
// modules: true
}
},
'sass-loader',
'postcss-loader'
]
},
{
test: /\.(eot|woff2|woff|ttf|svg)$/,
use: [
'file-loader',
]
}
]
}
}
使用babel處理ES6+語法
npm install --save-dev babel-loader @babel/core
babel-loade把babel和webpack建立連接,通信褥民,@babel/core是babel的核心包
npm install @babel/preset-env --save-dev
@babel/preset-env是轉(zhuǎn)化為es5代碼季春,里面包含了所有es6+轉(zhuǎn)換成es5的翻譯規(guī)則。
npm install --save @babel/polyfill
對低版本瀏覽器的語法兼容消返,全局引入import "@babel/polyfill";
然后在webpack中配置按需加載
npm install --save-dev @babel/plugin-transform-runtime
npm install --save @babel/runtime
npm install --save @babel/runtime-corejs2
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
const webpack = require('webpack');
module.exports = {
mode: 'development',
devtool: 'cheap-module-eval-source-map', // 參打開代碼映射载弄,參數(shù)none 關(guān)閉
entry: {
main: './src/index.js',
sub: './src/index.js'
},
devServer: {
contentBase: './dist',
open: true,
hot: true,
hotOnly: true
},
plugins: [
new CleanWebpackPlugin({ cleanStaleWebpackAssets: false }),
new HtmlWebpackPlugin({
title: 'Development',
template: 'src/index.html'
}),
new webpack.HotModuleReplacementPlugin()
],
output: {
publicPath: '/', // 指向跟目錄。對應(yīng)server.js 一個node服務(wù)
filename: '[name].js',
path: path.resolve(__dirname, 'dist')
},
module: {
rules: [
{
test: /\.(png|jpe?g|gif)$/i,
use: {
loader: 'url-loader',
options: {
name: '[name]_[hash].[ext]',
outputPath: 'images/',
limit: 2048
}
}
},
{
test: /\.scss$/i,
use: [
'style-loader',
{
loader: 'css-loader',
options: {
importLoaders: 2,
// modules: true
}
},
'sass-loader',
'postcss-loader'
]
},
{
test: /\.(eot|woff2|woff|ttf|svg)$/,
use: [
'file-loader',
]
},
{ test: /\.js$/,
exclude: /node_modules/,
loader: "babel-loader",
options: { // options里面的內(nèi)容也可以都提出去撵颊,提到根目錄下的.babelrc,就不需要在這里寫了
// presets: [["@babel/preset-env", {
// targets: {
// chrome: "58", // 如果chrome 58版本支持宇攻,就不去轉(zhuǎn)換了
// ie: "11"
// },
// useBuiltIns: "usage", // 會幫你自動引入 @babel/polyfill
// "corejs": "3"
// }]]
plugins: [["@babel/plugin-transform-runtime", { // 在寫類庫的時候可以這么配置,避免全局引入@babel/polyfill從而造成數(shù)據(jù)污染倡勇,此方式會以閉包等注入有作用域逞刷,不會污染到其他地兒
"absoluteRuntime": false,
"corejs": 2, // 當(dāng)頁面需要轉(zhuǎn)換語法的時候幫你裝換
"helpers": true,
"regenerator": true,
"useESModules": false,
"version": "7.0.0-beta.0"
}]]
}
}
]
}
}
安裝react打包需要的環(huán)境
npm i react react-dom --save
npm install --save-dev @babel/preset-react
react 代碼
import React, { Component } from 'react';
import ReactDom from 'react-dom';
class App extends Component {
render() {
return <div>hello react</div>
}
}
ReactDom.render(<App/>, document.getElementById('app'));
npm install --save-dev @babel/preset-react
Tree Shaking
Tree Shaking 只支持ES Module的引入方式(底層靜態(tài)引入,而CommonJS引入屬于動態(tài)引入妻熊,tree shaking只支持靜態(tài)引入)夸浅,他會把,你項目中沒有用到的模塊剔除掉扔役,mode: 'production'此環(huán)境自動就配置了
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
const webpack = require('webpack');
module.exports = {
mode: 'development',
devtool: 'cheap-module-eval-source-map', // 參打開代碼映射帆喇,參數(shù)none 關(guān)閉
entry: {
main: './src/index.js',
sub: './src/index.js'
},
devServer: {
contentBase: './dist',
open: true,
hot: true,
hotOnly: true // 如果沒有寫HMR 熱模塊相關(guān)的代碼,可以把此配置去掉厅目,自動刷新瀏覽器
},
plugins: [
new CleanWebpackPlugin(['dist'],{
root: path.resolve(__dirname, '/), // 設(shè)置要刪除文件的根目錄
cleanStaleWebpackAssets: false
}),
new HtmlWebpackPlugin({
title: 'Development',
template: 'src/index.html'
}),
new webpack.HotModuleReplacementPlugin() // HMR 熱模塊替換
],
// 在 package.json中配置 "sideEffects": [ '*.css', '@babel/polyfill' ], // 避免那種直接import引入的文件被忽略番枚,如 import '@babel/polyfill'
optimization: { // tree shaking 支持法严,production 模式中只不需要配置這塊损敷,只需要package.json中配置 "sideEffects"
usedExports: true
},
output: {
publicPath: '/', // 指向跟目錄。對應(yīng)server.js 一個node服務(wù)
filename: '[name].js',
path: path.resolve(__dirname, 'dist')
},
module: {
rules: [
{
test: /\.(png|jpe?g|gif)$/i,
use: {
loader: 'url-loader',
options: {
name: '[name]_[hash].[ext]',
outputPath: 'images/',
limit: 2048
}
}
},
{
test: /\.scss$/i,
use: [
'style-loader',
{
loader: 'css-loader',
options: {
importLoaders: 2,
// modules: true
}
},
'sass-loader',
'postcss-loader'
]
},
{
test: /\.(eot|woff2|woff|ttf|svg)$/,
use: [
'file-loader',
]
},
{ test: /\.js$/,
exclude: /node_modules/,
loader: "babel-loader",
// options: { // options里面的內(nèi)容也可以都提出去深啤,提到根目錄下的.babelrc,就不需要在這里寫了
// // presets: [["@babel/preset-env", {
// // targets: {
// // chrome: "58", // 如果chrome 58版本支持拗馒,就不去轉(zhuǎn)換了
// // ie: "11"
// // },
// // useBuiltIns: "usage",
// // "corejs": "3"
// // }]]
// plugins: [["@babel/plugin-transform-runtime", { // 在寫類庫的時候可以這么配置,避免全局引入@babel/polyfill從而造成數(shù)據(jù)污染溯街,此方式會以閉包等注入有作用域诱桂,不會污染到其他地兒
// "absoluteRuntime": false,
// "corejs": 2,
// "helpers": true,
// "regenerator": true,
// "useESModules": false,
// "version": "7.0.0-beta.0"
// }]]
// }
}
]
}
}
development 和 production 模式的區(qū)分打包
可以創(chuàng)建 webpack.dev.js 和 webpack.prod.js 兩個配置文件來區(qū)分開發(fā)和生產(chǎn),通過package.json里面配置命令 --config webpack.dev.js 等等
將一些公用的代碼呈昔,提取到webpack.common.js文件中挥等,避免冗余
npm install webpack-merge -D
通過這個插件,將webpack.common.js中的代碼堤尾,分別和dev和prod中的代碼進行合并
const merge = require('webpack-merge)
const commonConfig = require('./webpack.common.js')
...
module.exports = merge(commonConfig, devConfig)
webpack 和 Code Splitting 代碼分割
如果代碼不分割肝劲,打包的體積會很大,加載速度變慢,比如一些插件庫 loadsh 等是不需要被時刻打包的辞槐,手動代碼分割掷漱,可以自entry里面做多個打包入口,這樣就可以打包成多個文件
自動配置的話榄檬,可以在之前的optimization里面做一些配置
代碼分割和webpack沒有任何關(guān)系卜范,只是webpack 可以更優(yōu)雅的實現(xiàn)代碼分割
optimization: {
splitChunks: {
chunks: 'all'
}
}
異步導(dǎo)入 實驗性語法,需要用babel去解析鹿榜,安裝npm install babel-plugin-dynamic-import-webpack -D
.babelrc 配置
{
"presets": [
[
"@babel/preset-env",
{
"targets": {
"chrome": "58",
"ie": "11"
},
"useBuiltIns": "usage",
"corejs": "3"
}
],
"@babel/preset-react"
],
plugins: ['dynamic-import-webpack']
}
/* webpackPrefetch: true */ 表示網(wǎng)絡(luò)空閑時開始加載海雪,首屏渲染優(yōu)化點,這樣點擊事件觸發(fā)的時候,不會有體驗問題犬缨,應(yīng)用場景喳魏,首頁有登錄彈窗的時候,可以把怀薛,登錄框采用異步加載刺彩,優(yōu)化首屏渲染速度
function getComponent() {
return import(/* webpackPrefetch: true */'loadsh').then(({ default: _ }) => {
var element = document.createElement('div');
element.innerHTML = _.join([1,2,3], '-')
})
}
document.addEventListener('click',() => {
getComponent().then(element => {
document.body.appendChild(element)
})
})
webpack 會對同步和異步載入的代碼,都會進行分割(默認參數(shù)async 只對異步代碼進行分割枝恋,因為webpack更多希望你在代碼書寫的時候就采用異步创倔,優(yōu)雅的代碼)。代碼分割焚碌,和webpack無關(guān)畦攘,webpack實現(xiàn)代碼分割的方式有兩種,1同步代碼的分割十电,只需要在webpack配置文件中配置知押,optimization的配置,2異步代碼鹃骂,無需任何配置台盯,會自動進行代碼的分割
splitChunksPlugin 配置參數(shù)的詳解
使用官方提供的異步導(dǎo)入兼容插件
npm install --save--dev @babel/plugin-syntax-dynamic-import
更改chunk名字,可以使用注釋的寫法 /* webpackChunkName: 'loadsh' */
默認配置
module.exports = {
/...
// todo 待后續(xù)仔細研究
optimization: { // tree shaking 支持畏线,production 模式中只不需要配置這塊静盅,只需要package.json中配置 "sideEffects"
usedExports: true,
splitChunks: {
chunks: 'async', // 只對異步代碼生效 參數(shù)all 同步異步都會分割
minSize: 20000, // 大于 20000 字節(jié)才去做分割
minRemainingSize: 0,
maxSize: 9000000, // 如果被分割的代碼大于9000000 字節(jié),會對這些文件嘗試進行二次分割
minChunks: 1, // 代碼被使用多少次之后寝殴,才進行代碼分割
maxAsyncRequests: 30, // 同時加載的模塊最多是30個蒿叠,超過30個就不會做代碼分割了
maxInitialRequests: 30, // 入口文件,最多可以分割30個蚣常,多了就不在分割
automaticNameDelimiter: '~', // 打包出文件市咽,組合文件名的連接符
enforceSizeThreshold: 50000,
cacheGroups: { // 同步代碼會走這里,如果是node-modules下的會分割在defaultVendors組中, cacheGroups和 chunks配合使用
defaultVendors: { // cacheGroups 緩存組抵蚊,打包多個文件的時候施绎,先放在緩存組里面曼验,最后再合并輸出
test: /[\\/]node_modules[\\/]/,
priority: -10, // 多個組之間的匹配規(guī)則,值越大優(yōu)先級越高
filename: 'vendors.js'
},
default: {
minChunks: 2,
priority: -20,
reuseExistingChunk: true, // 如果之前這個模塊被打包過粘姜,那么再次打包的時候就會忽略這個模塊使用之前打包好的
filename: 'common.js'
}
}
}
},
Lazy Loading 懶加載鬓照,Chunk是什么
打包分析,Preloading Prefetching
webpack.github.com/analyse
將打包的描述文件放在stats.json中
"build": "webpack --profile --json > stats.json",
將輸出的文件上傳到 webpack.github.com/analyse
中會查看打包的一些分析依賴
首屏渲染優(yōu)化
盡量去寫孤紧,異步加載的代碼豺裆,提高代碼的利用率,覆蓋率 commd + shif + p 搜索 show cover 進行瀏覽器覆蓋率調(diào)試
/* webpackPrefetch: true */ 表示網(wǎng)絡(luò)空閑時開始加載号显,首屏渲染優(yōu)化點
webpackPrefetch webpackPreLoad 的區(qū)別 webpackPrefetch會等待頁面加載完之后頁面空閑的時候再加載你的代碼臭猜,webpackPreLoad不會等待會一起加載,所以還是webpackPrefetch比較合適(注意瀏覽器的兼容)
Css文件的代碼分割
Css文件的代碼分割 MiniCssExtractPlugin替換style-loader押蚤, 在使用此插件做css 代碼分割的時候蔑歌,一定要主要的是tree shaking 中要排除 *.css 一面,import引入的css 被忽略
Webpack與瀏覽器緩存(Caching)
contenthash 如果打包出的文件沒有變化此值不變揽碘,有變化才會變
output: {
filename: '[name].[contenthash].js',
chunkFilename: '[name].[contenthash].js'
}
// 兼容低版本次屠,防止contenthash不改變內(nèi)容也變
// 低版本中對于各個js之間的依賴關(guān)系(manifest)有可能會變,所以配置如下雳刺,將存在與各個js之間的依賴關(guān)系提取出來劫灶,這樣打包會多生成一個runtime.js的文件
optimization: {
runtimeChunk: {
name: 'runtime'
}
}
Shimming 墊片
假如你引用的一個npm包依賴另一個文件,而猶豫webpack的模塊化打包掖桦,你在外層引入這個依賴文件是不能被npm包所找到本昏,這時候就需要用到,墊片 webpack 提供的一個插件webpack.ProvidePlugin
// 當(dāng)你的模塊中使用到$這個變量的時候枪汪,這個插件會在編譯的過程中自動幫你引入jquery涌穆,給你自動添加類似 `import $ from 'jquery'` 這樣的代碼
new webpack.ProvidePlugin({
$: 'jquery',
_join: ['loadsh', 'join'] // 當(dāng)你使用_join的時候webpack會自動幫你吧 loadsh中的join模塊引入進去賦值給_join
})
一個模塊中的this始終指向模塊自身,其上下文雀久,如果我想讓一個模塊中的this指向window呢該怎么做可以借助插件 npm install imports-loader --save-dev
module: {
rules: [{
test: /\.js$/,
exclude: /node_modules/,
use: [
{
loader: 'babel-loader'
},
{
loader: 'imports-loader?this=>window' // 改變了webpack的一些默認行為
}
]
}]
}
環(huán)境變量的使用
module.exports = (env) => {
if(env && env.production) { // 生產(chǎn)
return merge(commonConfig, prodConfig);
}else { // 開發(fā)
return merge(commonConfig, devConfig);
}
}
"build": "webpack --env.production --config ./build/webpack.common.js" // 傳遞一個env.production全局變量