webpack4 一點通

webpack 是一個現代 JavaScript 應用程序的靜態模塊打包器(module bundler)。當 webpack 處理應用程序時,它會遞歸地構建一個依賴關係圖(dependency graph),其中包含應用程序須要的每一個模塊,而後將全部這些模塊打包成一個或多個 bundle。

安裝

webpack 最新版本 v4.26.0javascript

github star:45.292k css

須要同時安裝 webpackwebpack-cliwebpack-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

實用版

package.json

在實際使用中建議分開配置,生產環境和開發環境分別對應一個配置文件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

把公共配置提取到一個公用文件 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

提取開發環境和取生產環境的相同部分到基礎配置文件 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
                }
            }]
        })
    ],
}

webpack.dev.js

開發環境配置,主要是在本地啓動一個服務

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',
})

webpack.prod.js

生產環境配置

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 出提示
    }
})

webpack.dll.js

提取依賴的包,這樣每次 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。啓用 NamedChunksPluginNamedModulesPlugin
production 會將 process.env.NODE_ENV 的值設爲 production。啓用 FlagDependencyUsagePlugin, FlagIncludedChunksPlugin, ModuleConcatenationPlugin, NoEmitOnErrorsPlugin, OccurrenceOrderPlugin, SideEffectsFlagPluginUglifyJsPlugin

NamedModulesPlugin 當開啓 HMR 的時候使用該插件會顯示模塊的相對路徑,建議用於開發環境

ModuleConcatenationPlugin 預編譯全部模塊到一個閉包中,提高你的代碼在瀏覽器中的執行速度

NoEmitOnErrorsPlugin 在編譯出現錯誤時,直接退出

OccurrenceOrderPlugin 爲組件分配ID,經過這個插件webpack能夠分析和優先考慮使用最多的模塊,併爲它們分配最小的ID

2. loader

解析轉換源代碼,從右到左執行,鏈式傳遞

include/exclude 手動添加必須處理的文件(文件夾)或屏蔽不須要處理的文件(文件夾)

css-loader 使你可以使用相似 @importurl(...) 的方法實現 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'] // 默認配置

對比 webpack3

1. 增長 mode 配置

  • 默認生產環境開起了不少代碼優化(minify, splite)
  • 開發時開啓注視和驗證,並加上了evel devtool
  • 生產環境不支持watching,開發環境優化了打包的速度
  • 自動設置process.env.NODE_EVN到不一樣環境,也就是不使用DefinePlugin了
  • 若是mode設置none,全部默認設置都會去掉

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

相關文章
相關標籤/搜索