本文主要是多入口配置,但願能在無框架開發網頁時提升開發效率,對代碼進行打包優化。本文有什麼須要改善的地方,還望各位多多指教。css
本文關鍵詞:html
github 源碼node
目錄結構大概以下:jquery
|-build
|-create.js
|-utils.js
|-webpack.base.js
|-webpack.dev.js
|-webpack.prod.js
|-dist
|-src
|-.babelrc
|-.eslintrc.js
|-package.json
複製代碼
// webpack.base.js
const webpack = require('webpack')
const PurgecssPlugin = require('purgecss-webpack-plugin')
const rules = require('./webpack.rules.js')
const utils = require('./utils.js')
module.exports = {
entry: {},
resolve: {},
module: {},
externals: {},
plugins: []
}
// webpack.prod.js
const webpack = require('webpack')
const merge = require('webpack-merge')
const { CleanWebpackPlugin } = require('clean-webpack-plugin')
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
const CompressionPlugin = require('compression-webpack-plugin')
const configBase = require('./webpack.base.js')
const utils = require('./utils.js')
const configProd = {
mode: 'production',
devtool: 'none',
output: {},
optimization: {},
plugins: []
}
module.exports = merge(configBase, configProd)
// webpack.dev.js
const webpack = require('webpack')
const merge = require('webpack-merge')
const utils = require('./utils.js')
const configBase = require('./webpack.base.js')
const configDev = {
mode: 'development'
output: {},
devServer: {},
plugins: [],
module: {}
}
module.exports = merge(configBase, configDev)
// webpack.rules.js
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
const devMode = process.env.NODE_ENV !== 'production'
const rules = []
module.exports = rules
複製代碼
後文省略 module.exports
等代碼,再也不贅述。webpack
入口告訴 webapck 從哪一個模塊開始,根據依賴關係打包git
entry: './src/index.js'
複製代碼
entry: {
index: './src/index/index.js'
}
複製代碼
對於多入口配置,能夠用 glob 庫來動態獲取入口文件,以下:github
// utils.js
const path = require('path')
const htmlWebpackPlugin = require('html-webpack-plugin')
const glob = require('glob') // 遍歷目錄
const devMode = process.env.NODE_ENV !== 'production'
/** * 返回文件的絕對路徑 * @param {string} dir 文件路徑 * __dirname 得到當前執行文件所在目錄的完整目錄名(這裏指的是 build 目錄) */
function resolve(dir) {
return path.resolve(__dirname, dir)
}
//動態添加入口
function getEntry(globPath) {
var dirname, name
return glob.sync(globPath).reduce((acc, entry) => {
// name ./src/pages/index/index.js
// dirname ./src/pages/index
// basename index.js
dirname = path.dirname(entry)
name = dirname.slice(dirname.lastIndexOf('/') + 1)
acc[name] = entry
return acc
}, {})
}
function htmlPlugins() {}
module.exports = {
resolve,
getEntry,
htmlPlugins
}
// webpack.base.js
entry: utils.getEntry('./src/pages/*/index.js'),
複製代碼
在配置 output 以前配置這個插件是爲,每次打包前能夠刪除 dist 目錄,保證沒有冗餘文件。web
// webpack.prod.js
const cleanWebpackPlugin = require('clean-webpack-plugin')
plugins: [
// 刪除 dist 目錄
new CleanWebpackPlugin({
// verbose Write logs to console.
verbose: false, //開啓在控制檯輸出信息
// dry Use boolean "true" to test/emulate delete. (will not remove files).
// Default: false - remove files
dry: false
}),
]
複製代碼
自定義輸出文件的位置和名稱json
// webpack.dev.js
output: {
path: utils.resolve('../dist'),
// 包名稱
filename: 'js/[name].js'
},
// webpack.prod.js
output: {
path: utils.resolve('../dist'),
// 包名稱
filename: 'js/[name].[chunkhash:8].js',
// 塊名,公共塊名(非入口)
chunkFilename: 'js/[name].[chunkhash:8].js',
// 打包生成的 index.html 文件裏面引用資源的前綴
// 也爲發佈到線上資源的 URL 前綴
// 使用的是相對路徑,默認爲 ''
publicPath: '.'
},
複製代碼
文件名加入 hash,是爲了更好的利用瀏覽器對靜態文件的緩存。api
即便文件內容沒有改變,每次構建都產生一個新的哈希值,這顯然不是咱們想看到的。能夠用在開發環境,但不建議用於生產環境。
每一個入口都有對應的哈希值,當入口依賴關係中有文件內容發生變化,該入口的哈希值纔會發生變化。適用於生產環境。
根據包內容計算出哈希值,只要包內容不變,哈希值不變。適用於生產環境。
關於這三者的區別,網上也有相關文章,例如我查到的一篇 《webpack 中的 hash、chunkhash、contenthash 區別》 能夠參考。
none、development、production,默認爲 production
// webpack.prod.js
mode: 'production'
// webpack.dev.js
mode: 'development'
複製代碼
webpack4 針對不一樣模式,調用內置的優化策略,能夠減小不少配置。參考 webpack 模式
// webpack.base.js
resolve: {
// import 導入時別名,減小耗時的遞歸解析操做
alias: {
'@': resolve('../src'),
'assets': utils.resolve('../src/assets')
},
extensions: [
'.js',
'.json'
]
}
複製代碼
給項目中不一樣的文件類型,配置相應的規則
// webpack.base.js
module: {
// 忽略大型的 library 能夠提升構建性能
noParse: /jquery|lodash/,
rules: []
}
複製代碼
// webpack.rules.js
rules: [
{
test: /\.js$/,
use: ['babel-loader'],
// 不檢查 node_modules 下的 js 文件
exclude: '/node_modules/'
}
]
// .babelrc
{
"presets": [
[
"@babel/preset-env",
{
"useBuiltIns": "usage",
"corejs": 3,
"modules": false
}
]
]
}
// pages/index/index.js
import 'core-js/stable'
import 'regenerator-runtime/runtime'
複製代碼
根據官網 Usage Guide 配置如上,這裏採用的是 core-js@3 來實現 polyfill。由於 babel7 已經廢棄 @babel/polyfill 和 core-js@2,再也不更新。新的特性只會添加到 core-js@3,爲了不後續再改動,直接用 3。只是打出來的包大了點,這個本身平衡,若是以爲不爽,就仍是用 @babel/polyfill。
關於這個 core-js@3 有篇文章 講的挺清晰,能夠參考。
// webpack.rules.js
rules: [
{
test: /\.s[ac]ss$/i,
use: [
devMode
? 'style-loader'
: {
loader: MiniCssExtractPlugin.loader,
options: {
// you can specify a publicPath here
// by default it use publicPath in webpackOptions.output
publicPath: '../'
}
},
'css-loader',
'postcss-loader',
'sass-loader'
]
}
]
// webpack.prod.js
plugins: [
new MiniCssExtractPlugin({
filename: 'css/[name].[contenthash:8].css',
chunkFilename: 'css/[name].[contenthash:8].css'
}),
]
複製代碼
// webpack.base.js
plugins: [...utils.htmlPlugins('./src/pages/*/index.html')]
// utils.js
function htmlPlugins(globPath) {
var dirname, name
return glob.sync(globPath).reduce((acc, entry) => {
dirname = path.dirname(entry)
name = dirname.slice(dirname.lastIndexOf('/') + 1)
acc.push(new htmlWebpackPlugin(htmlConfig(name, name)))
return acc
}, [])
}
function htmlConfig(name, chunks) {
return {
template: `./src/pages/${name}/index.html`,
filename: `${name}.html`,
// favicon: './favicon.ico',
// title: title,
inject: true,
chunks: [chunks],
minify: devMode
? false
: {
removeComments: true,
collapseWhitespace: true
}
}
}
複製代碼
// webpack.rules.js
rules: [
{
test: /\.(png|jpe?g|gif)(\?.*)?$/,
use: [
{
loader: 'url-loader',
options: {
esModule: false,
limit: 4 * 1024,
name: 'img/[name].[hash:8].[ext]'
}
},
{
loader: 'img-loader',
options: {
plugins: [
require('imagemin-pngquant')({
speed: 2 // 1-11
}),
require('imagemin-mozjpeg')({
quality: 80 // 1-100
}),
require('imagemin-gifsicle')({
optimizationLevel: 1 // 1,2,3
})
]
}
}
]
},
{
test: /\.(svg)(\?.*)?$/,
use: [
{
loader: 'url-loader',
options: {
name: 'img/[name].[hash:8].[ext]'
}
}
]
},
]
複製代碼
用法:
background: url(~assets/index/icons/ic-star-16px.png);
複製代碼
import wukong from 'assets/index/wukong.jpg'
複製代碼
<img src="~assets/index/wukong.jpg" alt="wukong" />
複製代碼
這裏有有幾個點要注意:
esModule: false
纔不會出錯。// webpack.rules.js
rules: [
{
test: /\.(mp4|webm|ogg|mp3|wav|flac|aac)(\?.*)?$/,
loader: 'url-loader',
options: {
limit: 4 * 1024,
name: '[name].[hash:8].[ext]',
outputPath: 'media'
}
},
{
test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/,
loader: 'url-loader',
options: {
limit: 4 * 1024,
name: '[name].[hash:8].[ext]',
outputPath: 'font'
}
}
]
複製代碼
// webpack.dev.js
devServer: {
contentBase: utils.resolve('../src'), // 告訴服務器從哪一個目錄中提供內容
publicPath: '/', // 此路徑下的打包文件可在瀏覽器中訪問
port: '8090',
overlay: true, // 瀏覽器頁面上顯示錯誤
open: true, // 自動打開瀏覽器
// stats: "errors-only", //stats: "errors-only"表示只打印錯誤:
historyApiFallback: false, // 404 會被替代爲 index.html
inline: true, // 內聯模式,實時刷新
hot: true, // 開啓熱更新
proxy: {
'/api': {
target: 'https://example.com/',
changeOrigin: true,
pathRewrite: {}
}
}
},
plugins: [
//熱更新
new webpack.HotModuleReplacementPlugin()
],
複製代碼
// webpack.dev.js
devtool: 'cheap-eval-source-map',
// webpack.prod.js
devtool: 'none',
複製代碼
此選項控制是否生成,以及如何生成 source map。不一樣選項之間,官網 有更詳細解釋和對比。
// webpack.prod.js
optimization: {
runtimeChunk: {
name: 'manifest'
},
splitChunks: {
cacheGroups: {
vendors: {
name: 'vendors',
test: /[\\\/]node_modules[\\\/]/,
priority: -10,
chunks: 'initial' // 只對入口文件處理
},
vendors: {
name: 'chunk-common',
minChunks: 2,
priority: -20,
chunks: 'initial',
reuseExistingChunk: true
}
}
}
},
複製代碼
runtimeChunk 和 splitChunks 主要優化的點在於瀏覽器緩存,若是不考慮,也能夠不加這個配置。
// webpack.base.js
externals: {
'jquery': 'window.jquery'
},
複製代碼
做用:防止將某些 import 的包 (package) 打包到 bundle 中,而是在運行時 (runtime) 再去從外部獲取這些擴展依賴。 沒加 externals 配置,jq 經過 cdn 加載,直接在本地使用 $('#id')
打包沒什麼問題。可是,若是你在本地使用了模塊化的 jq 插件,就加上面這個 externals 配置了。緣由以下:
;(function(window, factory) {
if (typeof exports === 'object') {
module.exports = factory(require('jQuery'))
} else if (typeof define === 'function' && define.amd) {
define(['jQuery'], factory)
} else {
factory()
}
})(window, function($) {
$.fn.green = function() {
$(this).each(function() {
$(this).css('color', 'green')
})
}
})
複製代碼
上面的代碼是一個簡單 jq 插件,採用了 UMD 模塊化方案。if (typeof exports === 'object')
這行代碼會被 webpack 解析爲 if (true)
,也就是說,webpack 編譯後的代碼,會執行 require('jquery')
,而本地並無安裝 jq,因此會報錯,沒法打包成功。
plugins: [
// 自動加載模塊,無需 import 或 require
new webpack.ProvidePlugin({
$: 'jquery',
jQuery: 'jquery',
'window.jQuery': 'jquery'
}),
]
複製代碼