本文主要是多入口配置,但願能在無框架開發網頁時提升開發效率,對代碼進行打包優化。本文有什麼須要改善的地方,還望各位多多指教。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' }), ]