安裝webpack
首先安裝webpack、webpack-cli、webpack-dev-server。javascript
項目初始化
這裏使用vue項目作演示,大家能夠用別的,不影響webpack的構建。創建webpack.config.js文件css
引入一系列的依賴和loader
vue-loader和vue-template-compiler是vue必須的,官方地址:https://vue-loader.vuejs.org/zh/。html
node-sass、less、css-loader、vue-style-loader、less-loader和sass-loader讓咱們項目支持css、less和sass。vue
babel-loader、@babel/core、@babel/preset-env讓咱們項目支持ES6。java
還有圖片文件和字體文件的加載器,url-loader依賴於file-loader因此都要安裝 。node
關於devServer
如其名稱,devServer是用於啓動開發環境的服務的,配置項能夠上官網看,比較好理解。這裏就提兩個重要的配置項,一是hot爲true時,能夠啓動HMR。二是proxy,在先後端分離的項目,可使用這個配置來代理後臺的服務地址。webpack
//webpack.config.js const path = require('path'); const VueLoaderPlugin = require('vue-loader/lib/plugin'); module.exports ={ entry:'./src/main.js', // 項目的入口文件 output:{ filename: 'bundle.js', // 打包後的文件名 path: path.join(__dirname, 'dist'), // 項目的打包文件路徑 publicPath: 'dist/' // 輸出解析文件的目錄,指定資源文件引用的目錄 }, module: { rules: [ { test: /\.js$/, loader: 'babel-loader', exclude: file => ( /node_modules/.test(file) && !/\.vue\.js/.test(file) ) }, { test: /\.vue$/, loader: 'vue-loader' // vue官網介紹,使用這個loader的同時,還須要VueLoaderPlugin }, { test: /\.css$/, //匹配後綴名爲css的文件,而後分別用css-loader,vue-style-loader去解析 use: [ //解析器的執行順序是從下往上(先css-loader再vue-style-loader) 'vue-style-loader', 'css-loader' ], }, { test: /\.scss$/, use: [ 'vue-style-loader', 'css-loader', 'sass-loader' ], }, { test: /\.less$/, use: [ 'vue-style-loader', 'css-loader', 'less-loader' ] }, { test:/\.(png|jpe?j|gif|svg)(\?.*)?$/, loader:'url-loader', options:{ limit: 10 * 1024, // 10 KB //圖片文件大小小於limit的數值,就會被改寫成base64直接填入url裏面 } }, ] }, plugins: [ new VueLoaderPlugin() // 官方使用vue-loader時,必要的plugin ], devServer: { // 開發服務器工具 contentBase: './public', //基於的根目錄 open: true, // 自動打開瀏覽器 overlay: true // 將錯誤顯示在html之上 } }
爲了之後方便啓動,把config文件配置好後,再把package.json的scripts也配置一下web
"scripts": { "serve": "webpack-dev-server", //webpack5.0和webpack-cli4.0不支持webpack-dev-server,使用webpack serve "build": "webpack" }
這時啓動項目,就能夠看到編譯後的項目了。json
引入經常使用的插件
clean-webpack-plugin能夠在每次打包前把上一次打包的文件刪除再進行打包。後端
html-webpack-plugin能夠將html文件打包後自動引入打包後的js文件,能夠經過設置相關屬性來指定html,若是想要生成多個html,能夠屢次new這個插件。引入這個插件後,就不須要設置output.publicPath和devServer.contentBase屬性了。
copy-webpack-plugin將須要拷貝的文件放入打包後的文件,通常不在開發階段使用。
HotModuleReplacementPlugin熱更新插件(也稱HMR),這個插件是webpack自己提供的,不須要再單獨去下載依賴。在devServer中配置{hot:true}時啓動,css自動生效,而js等須要手動添加(使用module.hot.accept(dep,callback))
//webpack.config.js const webpack = require('webpack') const path = require('path'); const VueLoaderPlugin = require('vue-loader/lib/plugin'); const { CleanWebpackPlugin } = require('clean-webpack-plugin'); const HtmlWebpackPlugin = require('html-webpack-plugin'); const CopyWebpackPlugin = require('copy-webpack-plugin') module.exports ={ entry:'./src/main.js', // 項目的入口文件 output:{...}, module: {...}, plugins: [ new VueLoaderPlugin(), // 官方使用vue-loader時,必要的plugin new CleanWebpackPlugin(), new HtmlWebpackPlugin({ title: 'Webpack Vue Sample', // 設置title meta: { // 設置meta viewport: 'width=device-width' }, template: './public/index.html' // 指定模板 }), new webpack.HotModuleReplacementPlugin(), // // 開發階段最好不要使用這個插件 // new CopyWebpackPlugin(['public']) ], devServer: { // 開發服務器工具 hot: true, // 啓動HMR ,若是要手動添加一些文件的HMR,能夠用hotOnly來調試 open: true, // 自動打開瀏覽器 overlay: true // 將錯誤顯示在html之上 } }
不一樣工做環境的配置
建立不一樣環境配置的方式主要有兩種方法,一是在配置文件中添加相應的條件,根據不一樣的條件來導出相應的配置;二是根據不一樣的環境添加對應的配置文件。在不一樣的環境能夠有不一樣配置項,像mode、devtool等,還有上面用到的clean-webpack-plugin、copy-webpack-plugin等插件均可以區分到所對應的環境中。
根據不一樣的條件來導出相應的配置
這個方法主要經過cli配置的環境名參數,而後再module.exports裏接收這個參數來進行判斷,啓動production的方法能夠是在命令行輸入‘webpack --env production’,也能夠配置在scripts中。
module.exports = (env, argv) => { const config = { mode: 'development', entry: './src/main.js', output: {...}, devtool: 'cheap-eval-module-source-map', devServer: {...}, module: {...}, plugins: [...] }; if (env === 'production') { config.mode = 'production'; config.devtool = false; config.plugins = [ ...config.plugins, new CleanWebpackPlugin(), new CopyWebpackPlugin(['public']) ]; } return config; }
根據不一樣的環境添加對應的配置文件
這個方法如其名,是要創建不一樣環境的配置文件來進行匹配。通常須要創建一個webpack.common.js一個公共配置,而後再按開發、生產的等環境在創建對應環境特有的配置文件,最後在啓動項目時經過'webpack --config webpack.prod.js'來啓動不一樣的項目便可。
// webpack.common.js const path = require('path'); const VueLoaderPlugin = require('vue-loader/lib/plugin'); const HtmlWebpackPlugin = require('html-webpack-plugin'); module.exports ={ entry:'./src/main.js', // 項目的入口文件 output:{...}, module: {...}, plugins: [ new VueLoaderPlugin(), // 官方使用vue-loader時,必要的plugin new HtmlWebpackPlugin({ title: 'Webpack Vue Sample', // 設置title meta: { // 設置meta viewport: 'width=device-width' }, template: './public/index.html' // 指定模板 }), ], }
//webpack.dev.js const webpack = require('webpack'); const {merge} = require('webpack-merge'); const common = require('./webpack.common'); module.exports = merge(common,{ mode: 'development', devtool: 'cheap-eval-module-source-map', plugins: [ new webpack.HotModuleReplacementPlugin(), ], devServer: { // 開發服務器工具 hot: true, // 啓動HMR open: true, // 自動打開瀏覽器 overlay: true // 將錯誤顯示在html之上 } });
// webpack.prod.js const webpack = require('webpack') const { CleanWebpackPlugin } = require('clean-webpack-plugin'); const CopyWebpackPlugin = require('copy-webpack-plugin') const {merge} = require('webpack-merge'); const common = require('./webpack.common'); module.exports = merge(common,{ mode:'production', plugins: [ new CleanWebpackPlugin(), new CopyWebpackPlugin( ["public"]) ], });
配置全局常量
經過對開發模式和生產模式配置不一樣的常量,從而能夠在構建時容許不一樣的行爲。 可使用webpack自帶的DefinePlugin來進行配置。
new webpack.DefinePlugin({ BASE_URL:JSON.stringify('./') }),
打包過程的優化
Tree Shaking
Tree Shaking在production模式下會自動開啓,咱們想要在其餘模式下也使用的話,須要本身來配置。
optimization: { usedExports: true, // 模塊只導出被使用的成員 concatenateModules: true, // 儘量合併每個模塊到一個函數中 minimize: true, // 壓縮輸出結果 },
sideEffects
sideEffects在production模式下也會自動開啓,它主要的做用是對所標識的沒有反作用文件進行識別,將沒有用的到的代碼不進行打包。想在除production模式下也啓用這個功能,能夠對webpack配置文件和package.json文件兩個文件進行配置。
// webpack配置文件 optimization: { sideEffects: true, // 開啓sideEffects 功能 },
// package.json "sideEffects": [ "*.css" //標識有反作用的文件 ]
Code Splitting
Code Splitting主要做用就是將打包後的代碼進行分包,具體實現有兩種方法:1.多入口打包;2.動態導入;
多入口打包
多入口打包的功能主要經過webpack的配置來實現
// 多入口打包 module.exports = { entry: { // 入口改成多文件導入 index: './src/index.js', app: './src/app.js' }, output: { filename: '[name].bundle.js' // 導出的文件名動態生成 }, module: {...}, optimization: { splitChunks: { chunks: 'all' // 防止重複引用,自動提取全部公共模塊到單獨 bundle } }, plugins: [ new HtmlWebpackPlugin({ title: 'index Page', template: './src/index.html', filename: 'index.html', chunks: ['index'] //設置html須要導入的js文件 }), new HtmlWebpackPlugin({ title: 'app Page', template: './src/app.html', filename: 'app.html', chunks: ['app'] }) ] }
動態導入
動態導入指的是在應用運行到須要某個模塊的時候,再來導入這個模塊。webpack自動支持這種方式而且會單獨進行分包,其主要應用場景是路由切換時。打包後的文件還能夠經過魔法註釋(/* webpackChunkName: 'name' */)來定義文件的名稱
const render = () => { const hash = window.location.hash || '#home' const mainElement = document.querySelector('.main') mainElement.innerHTML = '' if (hash === '#home') { import(/* webpackChunkName: 'homeCom' */'./home/home').then(({ default: home}) => { mainElement.appendChild(home()) }) } else if (hash === '#index') { import(/* webpackChunkName: 'indexCom' */'./index/index').then(({ default: index}) => { mainElement.appendChild(index()) }) } } render() window.addEventListener('hashchange', render)
css文件提取到單個文件
當css文件比較大想要單獨提取出來時,可使用‘mini-css-extract-plugin’插件,在使用這個插件時,還要同時使用‘optimize-css-assets-webpack-plugin’、'terser-webpack-plugin'這兩個插件,‘optimize-css-assets-webpack-plugin’是用來壓縮css文件的,'terser-webpack-plugin'是用來壓縮js文件的。由於在配置minimizer後,webpack會取消默認的js壓縮plugin,多以須要本身再把js的plugin給添加進來。
const MiniCssExtractPlugin = require('mini-css-extract-plugin') const OptimizeCssAssetsWebpackPlugin = require('optimize-css-assets-webpack-plugin') const TerserWebpackPlugin = require('terser-webpack-plugin') module.exports = { entry: './src/index.js', output: {...}, optimization: { minimizer: [ // webpack官方建議把壓縮性質的文件用minimize來統一管理 new TerserWebpackPlugin(), // js文件壓縮 new OptimizeCssAssetsWebpackPlugin() // css文件壓縮 ] }, module: { rules: [ ..., { test: /\.css$/, use: [ // 'style-loader', // 將樣式經過 style 標籤注入 MiniCssExtractPlugin.loader, // 替換掉'style-loader' 'css-loader' ] } ] }, plugins: [ ..., new MiniCssExtractPlugin() // 提取css文件到單個文件,若是單個css文件體積不大,不必單獨提取到一個文件 ] }
substitution
webpack提供了一個substitution(可替換的模版字符串)的方式,這個方式將根據資源內容建立出惟一 hash 。主要有三個模板:1.'hash',2.'chunckhash',3.'contenthash',具體何時用,就不作多介紹了。
new MiniCssExtractPlugin({ filename: 'static/css/[name].[contenthash:4].css', // hash默認20位長度,能夠用':'接數字,指定長度 chunkFilename: 'static/css/[name].[contenthash:4].css', }),
ESlint
使用eslint-loader做爲pre-loader運用,能夠在開發過程當中每次保存的時候就會自動進行代碼校驗。
module.exports = { module: { rules: [ { enforce: 'pre', test: /\.(js|vue)$/, loader: 'eslint-loader', exclude: /node_modules/ } ] } }