webpack 是一個現代 JavaScript 應用程序的靜態模塊打包器(module bundler)。當 webpack 處理應用程序時,它會遞歸地構建一個依賴關係圖(dependency graph),其中包含應用程序須要的每一個模塊,而後將全部這些模塊打包成一個或多個 bundle。
webpack 最新版本 v4.26.0javascript
github star:45.292k css
須要同時安裝 webpack
、webpack-cli
、webpack-dev-server
,建議安裝在每一個獨立項目而不是全局,這樣方便單獨使用 webpack3 或者 webpack4html
yarn add webpack webpack-cli webpack-dev-server -D 或者: cnpm install webpack webpack-cli webpack-dev-server -D
2018年8月25號webpack4正式發佈。再次以後只要使用npm install webpack命令,默認安裝的就是版本4vue
webpack4 會根據環境自動設置一些默認配置,下面是一個最基礎的配置:java
package.jsonnode
// --config webpack.config.js 配置文件的路徑 // --mode=production 用到的模式 // --progress 打印出編譯進度的百分比值 "scripts": { "dev": "webpack-dev-server --progress --mode=development --config webpack.config.js", "build": "webpack --progress --mode=production --config webpack.config.js", },
webpack.config.jswebpack
const webpack = require('webpack') const HtmlWebpackPlugin = require('html-webpack-plugin') const path = require('path') module.exports = { entry: './main.js', output: { path: path.resolve(__dirname, 'dist'), publicPath: '/', // 資源引用路徑先後都有斜槓 filename: '[name].js' }, devServer: { open: true, // 自動打開瀏覽器 host: '0.0.0.0', port: 3003, }, plugins: [ new HtmlWebpackPlugin({ filename: 'index.html', template: path.resolve(__dirname, 'template.html'), }), new webpack.HotModuleReplacementPlugin(), // 開啓webpack熱更新功能 new webpack.NoEmitOnErrorsPlugin(), // webpack編譯出錯跳過報錯階段,在編譯結束後報錯 ], }
瀏覽器輸入 http://0.0.0.0:3003/webpack-dev-server
能夠查看開發環境文件結構ios
在實際使用中建議分開配置,生產環境和開發環境分別對應一個配置文件git
// --config config/webpack.dev.js 配置文件的路徑 // --progress 打印出編譯進度的百分比值 "scripts": { "start": "webpack --progress --config config/webpack.dll.js", "dev": "webpack-dev-server --progress --config config/webpack.dev.js", "build": "webpack --progress --config config/webpack.prod.js" },
把公共配置提取到一個公用文件 base.conf.js 中,後期全部修改都在這個文件,其餘配置文件不動,減少人爲錯誤github
const os = require('os') const getIp = () => { // 獲取本地ip var interfaces = os.networkInterfaces(); for (var devName in interfaces) { var iface = interfaces[devName]; for (var i = 0; i < iface.length; i++) { var alias = iface[i]; if (alias.family === 'IPv4' && alias.address !== '127.0.0.1' && !alias.internal) { return alias.address; } } } } module.exports = { base: { rootPath: '/', fileName: 'dist', filePath: 'dist/static', }, dev: { useEslint: true, host: getIp(), port: 3001, proxy: [ { context: ['/v2', '/xw', '/wap', '/information'], target: 'https://xwm.jindanlicai.com/', changeOrigin: true, cookieDomainRewrite:{ "*":getIp() } }, ] } }
提取開發環境和取生產環境的相同部分到基礎配置文件 webpack.base.js 中
const config = require('./base.conf.js') // 配置文件 const path = require('path') const webpack = require('webpack') const VueLoaderPlugin = require('vue-loader/lib/plugin') const MiniCssExtractPlugin = require('mini-css-extract-plugin') const HappyPack = require('happypack') // 多進程 默認三個 const os = require('os') const happyThreadPool = HappyPack.ThreadPool({ size: os.cpus().length }) // 處理路徑 function resolve (dir) { return path.join(__dirname, '..', dir) } // eslint檢測 const createLintingRule = () => ({ test: /\.(js|vue)$/, loader: 'eslint-loader', enforce: 'pre', exclude: /node_modules/, include: [resolve('src')], options: { formatter: require('eslint-friendly-formatter'), emitWarning: true } }) // 獲取當前環境 const prod = process.env.NODE_ENV === 'production' module.exports = { context: path.resolve(__dirname, '../'), // 做用於entry 和 loader entry: { index: './src/main.js', }, output: { path: resolve(`${config.base.filePath}`), // 輸出到static這個地址 只能是絕對路徑 filename: 'js/[name].js', chunkFilename: 'js/[name]_[chunkhash:6].js' }, resolve: { extensions: ['.css', '.less', '.js', '.vue', '.json'], // 使用的擴展名 alias: { // 'vue$': 'vue/dist/vue.esm.js', // 模塊別名列表 '@': resolve('src'), } }, module: { // 忽略的文件中不該該含有 import, require, define 的調用,或任何其餘導入機制,忽略部分插件能夠提升構建性能 noParse: /^(vue|vue-router|vuex|vuex-router-sync|axios)$/, rules: [ ...(config.dev.useEslint ? [createLintingRule()] : []), { test: /\.vue$/, loader: 'vue-loader', include: resolve('src') }, { test: /\.pug$/, loader: 'pug-plain-loader', include: resolve('src') }, { test: /\.css$/, oneOf: [ // 這裏匹配 `<style module>` { resourceQuery: /module/, use: [ { // 只在生產環境下使用 CSS 提取,便於你在開發環境下進行熱重載 loader: prod ? MiniCssExtractPlugin.loader : 'vue-style-loader', options: { publicPath: '../' /* 複寫css文件中資源路徑 由於css文件中的外鏈是相對與css的, 咱們抽離的css文件在可能會單獨放在css文件夾內 引用其餘如img/a.png會尋址錯誤 這種狀況下因此單獨須要配置../,複寫其中資源的路徑 */ }, }, { loader: 'css-loader', options: { importLoaders: 1, modules: true, // 開啓 css module localIdentName: 'v_[hash:6]' // 自定義生成的類名 } }, 'postcss-loader' // 自動加前綴以兼容其餘瀏覽器 ] }, // 這裏匹配普通的 .css 文件 或 <style> { use: [ { loader: prod ? MiniCssExtractPlugin.loader : 'vue-style-loader', options: { publicPath: '../' }, }, 'css-loader', 'postcss-loader', ] } ] }, { test: /\.less$/, oneOf: [ // 這裏匹配 `<style lang="less" module>` { resourceQuery: /module/, use: [ { loader: prod ? MiniCssExtractPlugin.loader : 'vue-style-loader', options: { publicPath: '../' }, }, { loader: 'css-loader', options: { importLoaders: 2, modules: true, // 開啓 css module localIdentName: 'v_[hash:6]' // 自定義生成的類名 } }, 'postcss-loader', 'less-loader' ] }, // 這裏匹配普通的 .less 文件 或 <style lang="less"> { use: [ { loader: prod ? MiniCssExtractPlugin.loader : 'vue-style-loader', options: { publicPath: '../' }, }, 'css-loader', 'postcss-loader', 'less-loader' ] } ] }, { test: /\.js$/, loader: 'HappyPack/loader?id=js', exclude: file => ( /node_modules/.test(file) && !/\.vue\.js/.test(file) ) }, // url-loader 包含 file-loader,先把小於 4kb 的文件轉換成 base64,而後交給 file-loader 去處理路徑問題 { test: /\.(png|jpe?g|gif|webp|svg)(\?.*)?$/, loader: 'url-loader', options: { name: 'img/[name]_[hash:6].[ext]', limit: 4096, } }, { test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/, loader: 'url-loader', options: { name: 'fonts/[name]_[hash:6].[ext]', limit: 4096, } }, { test: /\.(mp4|webm|ogg|mp3|wav|flac|aac)(\?.*)?$/, loader: 'url-loader', options: { name: 'media/[name]_[hash:6].[ext]', limit: 4096, } }, ] }, plugins: [ new VueLoaderPlugin(), // vue-loader新用法 new HappyPack({ // 提升js編譯速度 id: 'js', loaders: [{ loader: 'babel-loader', options: { cacheDirectory: true } }] }) ], }
開發環境配置,主要是在本地啓動一個服務
process.env.NODE_ENV = 'development' // 設置當前環境爲開發環境 放在最上面 const config = require('./base.conf.js') // 配置文件 const baseWebpackConfig = require('./webpack.base.js') const path = require('path') const webpack = require('webpack') const merge = require('webpack-merge') const HtmlWebpackPlugin = require('html-webpack-plugin') const FriendlyErrorsPlugin= require('friendly-errors-webpack-plugin') const CopyWebpackPlugin = require('copy-webpack-plugin') module.exports = merge(baseWebpackConfig, { mode: 'development', // 啓用開發模式配置 output: { publicPath: '/', // 資源引用路徑先後都有斜槓 }, devServer: { contentBase: path.join(__dirname, '..', `${config.base.fileName}`), // 用來指定index.html所在目錄 clientLogLevel: "warning", // 熱更新時阻止控制檯顯示消息 太多了 沒加eslint none overlay: {warnings: true, errors: true}, // webpack的eslint等錯誤、警告提示顯示在頁面中 全爲true會中止頁面運行 noInfo: true, // 每次啓動和保存,只顯示webpack編譯的錯誤和警告信息 historyApiFallback: true, // 任意的跳轉或404響應能夠指向 index.html 頁面 watchContentBase: true, // 修改沒有被入口文件託管的文件,好比index.html文件,也會自動更新 compress: true, // 一切服務都啓用gzip 壓縮 hot: true, // 啓動webpack熱模塊替換特性 inline: true, // 自動刷新 open: true, // 自動打開瀏覽器 host: config.dev.host, port: config.dev.port, proxy: config.dev.proxy, }, plugins: [ new HtmlWebpackPlugin({ filename: 'index.html', template: path.resolve(__dirname, '../src/assets/template.html'), vendorJsName: 'vendor.dll.js', // 給模板引用 }), new webpack.HotModuleReplacementPlugin(), // 開啓webpack熱更新功能 new webpack.NoEmitOnErrorsPlugin(), // webpack編譯出錯跳過報錯階段,在編譯結束後報錯 new FriendlyErrorsPlugin({ // webapck啓動時在終端顯示信息 compilationSuccessInfo: { messages: [`Your application is running here: http://${config.dev.host}:${config.dev.port}`], } }), new CopyWebpackPlugin( // 本地開發環境 [ { from: path.resolve(__dirname, '../dist/static/js/vendor.dll.js'), to: './static/', } ], { ignore: ['.DS_Store'], copyUnmodified: true, // debug: "debug" // 是否打印複製的詳細信息 } ) ], devtool: 'cheap-module-eval-source-map', })
生產環境配置
process.env.NODE_ENV = 'production' // 設置當前環境爲生產環境 放在最上面 const config = require('./base.conf.js') // 配置文件 const baseWebpackConfig = require('./webpack.base.js') const path = require('path') const webpack = require('webpack') const merge = require('webpack-merge') const HtmlWebpackPlugin = require('html-webpack-plugin') const CleanWebpackPlugin = require('clean-webpack-plugin') const CopyWebpackPlugin = require('copy-webpack-plugin') const MiniCssExtractPlugin = require('mini-css-extract-plugin') const OptimizeCss = require('optimize-css-assets-webpack-plugin') module.exports = merge(baseWebpackConfig, { mode: 'production', // 啓用生產模式配置 output: { publicPath: './static/', // 資源引用路徑先後都有斜槓 }, stats: { // 未定義選項時,stats 選項的備用值(fallback value)(優先級高於 webpack 本地默認值) all: undefined, modules: false, // 添加構建模塊信息 children: false, // 添加 children 信息 colors: true, // `webpack --colors` 等同於 }, plugins: [ new CleanWebpackPlugin( ['dist/*.html', 'dist/static/js', 'dist/static/css', 'dist/static/img', 'dist/static/fonts', 'dist/static/media'], // 刪除匹配的文件 { root: path.resolve(__dirname, '../'), // 重置到根路經 exclude: ['vendor.dll.js', 'vendor.manifest.json'], // 這幾個文件不刪除 verbose: false, // 開啓在控制檯輸出信息 dry: false, // 啓用刪除文件 } ), new CopyWebpackPlugin( // 這部分不會被 webpack loader 處理 [ { from: path.resolve(__dirname, '../src/public/'), to: 'public/', }, ], { ignore: ['.DS_Store'], copyUnmodified: true, // debug: "debug" // 是否打印複製的詳細信息 } ), new webpack.DllReferencePlugin({ manifest: require(`../${config.base.filePath}/js/vendor.manifest.json`), context: path.join(__dirname, '..'), // 和dllplugin裏面的context一致 }), new MiniCssExtractPlugin({ // 提取css filename: 'css/[name]_[contenthash:6].css' }), new OptimizeCss({ // 壓縮提取的css assetNameRegExp: /\.css$/g, cssProcessor: require('cssnano'), cssProcessorOptions: {discardComments: {removeAll: true}}, canPrint: true, }), new HtmlWebpackPlugin({ filename: '../index.html', // 相對於static的路徑 template: path.resolve(__dirname, '../src/assets/template.html'), hash: true, minify: { removeAttributeQuotes: true, // 清除屬性引號 collapseWhitespace: true, // 清除多餘空格 minifyJS: true, // 壓縮javascript }, vendorJsName: 'vendor.dll.js?' + new Date() * 1, // 給模板引用 chunksSortMode: "dependency", }) ], performance: { hints: "warning" // 打包資源超過 250kb 出提示 } })
提取依賴的包,這樣每次 build 的時候就不用處理依賴包了,提升打包速度
const config = require('./base.conf.js') // 配置文件 const package = require('../package.json') const path = require('path') const webpack = require('webpack') const CleanWebpackPlugin = require('clean-webpack-plugin') module.exports = { mode: 'production', // 啓用生產模式配置 entry: { // 若是使用了chrome的vue-devtool,那打包的時候把vue也排除掉,由於壓縮過的vue是不能使用vue-devtool的 vendor: Object.keys(package.dependencies).filter((val) => { return val != 'test' }) }, stats: { // 未定義選項時,stats 選項的備用值(fallback value)(優先級高於 webpack 本地默認值) all: undefined, modules: false, // 添加構建模塊信息 children: false, // 添加 children 信息 colors: true, // `webpack --colors` 等同於 }, output: { path: path.resolve(__dirname, `../${config.base.filePath}`), filename: 'js/[name].dll.js', library: '[name]' // 生成文件的映射關係,與下面DllPlugin中name配置對應 }, plugins: [ new CleanWebpackPlugin( [`${config.base.filePath}`], // 匹配刪除的文件 { root: path.join(__dirname, '../'), // 必須先重置到根路經 verbose: true, // 開啓在控制檯輸出信息 dry: false // 啓用刪除文件 } ), new webpack.DllPlugin({ // 會生成一個json文件,裏面是關於dll.js的一些配置信息 path: path.resolve(__dirname, `../${config.base.filePath}/js/[name].manifest.json`), name: '[name]', // 與上面output中配置對應 context: path.join(__dirname, '..') // 上下文環境路徑(必填,爲了與DllReferencePlugin存在與同一上下文中) }) ] }
1. mode
webpack增長了一個 mode
配置,只有兩種值 development | production
。對不一樣的環境會啓用不一樣的配置:
選項 | 描述 |
---|---|
development |
會將 process.env.NODE_ENV 的值設爲 development 。啓用 NamedChunksPlugin 和 NamedModulesPlugin 。 |
production |
會將 process.env.NODE_ENV 的值設爲 production 。啓用 FlagDependencyUsagePlugin , FlagIncludedChunksPlugin , ModuleConcatenationPlugin , NoEmitOnErrorsPlugin , OccurrenceOrderPlugin , SideEffectsFlagPlugin 和 UglifyJsPlugin 。 |
NamedModulesPlugin
當開啓 HMR 的時候使用該插件會顯示模塊的相對路徑,建議用於開發環境
ModuleConcatenationPlugin
預編譯全部模塊到一個閉包中,提高你的代碼在瀏覽器中的執行速度
NoEmitOnErrorsPlugin
在編譯出現錯誤時,直接退出
OccurrenceOrderPlugin
爲組件分配ID,經過這個插件webpack能夠分析和優先考慮使用最多的模塊,併爲它們分配最小的ID
2. loader
解析轉換源代碼,從右到左執行,鏈式傳遞
include/exclude
手動添加必須處理的文件(文件夾)或屏蔽不須要處理的文件(文件夾)
css-loader
使你可以使用相似 @import
和 url(...)
的方法實現 require()
的功能
style-loader
將全部的計算後的樣式加入頁面中
3. plugins
解決 loader 沒法實現的其餘事
4. Manifest
資源映射文件,解析和加載模塊
5. autoprefixer.browsers
browsers: ['> 0.5%', 'last 3 versions']
// 兼容性最普遍
browsers: ['> 0.5%', 'last 2 versions', 'ie > 8']
// 經常使用
browsers: ['> 0.5%', 'last 2 versions', 'Firefox ESR', 'not dead']
// 默認配置
1. 增長 mode 配置
2. 自帶環境變量
可在頁面代碼中直接獲取當前環境變量 console.log(process.env.NODE_ENV)
3. webpack-cli
webpack 啓動命令行的代碼放入了 webpack-cli 中,只安裝 webpack,那麼它只能在 nodejs 中使用,不能再命令行中使用
4. UglifyJsPlugin
不須要使用這個插件,只須要使用 optimization.minimize
爲 true 就行,production mode 下自動爲 true
5. vue-cli3
vue inspect > output.js