webpack4項目中的實踐

學習webpack4的配置更改

webpack做爲一個模塊打包器,主要用於前端工程中的依賴梳理和模塊打包,將咱們開發的具備高可讀性和可維護性的代碼文件打包成瀏覽器能夠識別並正常運行的壓縮代碼,主要包括樣式文件處理成css,各類新式的JavaScript轉換成瀏覽器認識的寫法等,也是前端工程師進階的不二法門。javascript

webpack.config.js配置項簡介

  1. Entry:入口文件配置,Webpack 執行構建的第一步將從 Entry 開始,完成整個工程的打包。
  2. Module:模塊,在Webpack裏一切皆模塊,Webpack會從配置的Entry開始遞歸找出全部依賴的模塊,最經常使用的是rules配置項,功能是匹配對應的後綴,從而針對代碼文件完成格式轉換和壓縮合並等指定的操做。
  3. Loader:模塊轉換器,用於把模塊原內容按照需求轉換成新內容,這個是配合Module模塊中的rules中的配置項來使用。
  4. Plugins:擴展插件,在Webpack構建流程中的特定時機注入擴展邏輯來改變構建結果或作你想要的事情。(插件API)
  5. Output:輸出結果,在Webpack通過一系列處理並得出最終想要的代碼後輸出結果,配置項用於指定輸出文件夾,默認是./dist
  6. DevServer:用於配置開發過程當中使用的本機服務器配置,屬於webpack-dev-server這個插件的配置項。

webpack打包流程簡介

  • 根據傳入的參數模式(development | production)來加載對應的默認配置
  • entry裏配置的module開始遞歸解析entry所依賴的全部module
  • 每個module都會根據rules的配置項去尋找用到的loader,接受所配置的loader的處理
  • entry中的配置對象爲分組,每個配置入口和其對應的依賴文件最後組成一個代碼塊文件(chunk)並輸出
  • 整個流程中webpack會在恰當的時機執行plugin的邏輯,來完成自定義的插件邏輯

基本的webpack配置搭建

首先經過如下的腳本命令來創建初始化文件:css

npm init -y
npm i webpack webpack-cli -D // 針對webpack4的安裝
mkdir src && cd src && touch index.html index.js
cd ../ && mkdir dist && mkdir static
touch webpack.config.js
npm i webpack-dev-server --save-dev
複製代碼

修改生成的package.json文件,來引入webpack打包命令:html

"scripts": {
    "build": "webpack --mode production",
    "dev": "webpack-dev-server --open --mode development"
}
複製代碼

webpack.config.js文件加入一些基本配置loader,從而基本的webpack4.x的配置成型(以兩個頁面入口爲例):前端

const path = require('path');
const CopyWebpackPlugin = require('copy-webpack-plugin') // 複製靜態資源的插件
const CleanWebpackPlugin = require('clean-webpack-plugin') // 清空打包目錄的插件
const HtmlWebpackPlugin = require('html-webpack-plugin') // 生成html的插件
const ExtractTextWebapckPlugin = require('extract-text-webpack-plugin') //CSS文件單獨提取出來
const webpack = require('webpack')

module.exports = {
    entry: {
        index: path.resolve(__dirname, 'src', 'index.js'),
        page: path.resolve(__dirname, 'src', 'page.js'),
        vendor:'lodash' // 多個頁面所需的公共庫文件,防止重複打包帶入
    },
    output:{
        publicPath: '/',  //這裏要放的是靜態資源CDN的地址
        path: path.resolve(__dirname,'dist'),
        filename:'[name].[hash].js'
    },
    resolve:{
        extensions: [".js",".css",".json"],
        alias: {} //配置別名能夠加快webpack查找模塊的速度
    },
    module: {
        // 多個loader是有順序要求的,從右往左寫,由於轉換的時候是從右往左轉換的
        rules:[
            {
                test: /\.css$/,
                use: ExtractTextWebapckPlugin.extract({
                    fallback: 'style-loader',
                    use: ['css-loader', 'postcss-loader'] // 再也不須要style-loader放到html文件內
                }),
                include: path.join(__dirname, 'src'), //限制範圍,提升打包速度
                exclude: /node_modules/
            },
            {
                test:/\.less$/,
                use: ExtractTextWebapckPlugin.extract({
                    fallback: 'style-loader',
                    use: ['css-loader', 'postcss-loader', 'less-loader']
                }),
                include: path.join(__dirname, 'src'),
                exclude: /node_modules/
            },
            {
                test:/\.scss$/,
                use: ExtractTextWebapckPlugin.extract({
                    fallback: 'style-loader',
                    use:['css-loader', 'postcss-loader', 'sass-loader']
                }),
                include: path.join(__dirname, 'src'),
                exclude: /node_modules/
            },
            {
                test: /\.jsx?$/,
                use: {
                    loader: 'babel-loader',
                    query: { //同時能夠把babel配置寫到根目錄下的.babelrc中
                      presets: ['env', 'stage-0'] // env轉換es6 stage-0轉es7
                    }
                }
            },
            { //file-loader 解決css等文件中引入圖片路徑的問題
            // url-loader 當圖片較小的時候會把圖片BASE64編碼,大於limit參數的時候仍是使用file-loader 進行拷貝
                test: /\.(png|jpg|jpeg|gif|svg)/,
                use: {
                  loader: 'url-loader',
                  options: {
                    outputPath: 'images/', // 圖片輸出的路徑
                    limit: 1 * 1024
                  }
                }
            }
        ]
    },
    plugins: [
        // 多入口的html文件用chunks這個參數來區分
        new HtmlWebpackPlugin({
            template: path.resolve(__dirname,'src','index.html'),
            filename:'index.html',
            chunks:['index', 'vendor'],
            hash:true,//防止緩存
            minify:{
                removeAttributeQuotes:true//壓縮 去掉引號
            }
        }),
        new HtmlWebpackPlugin({
            template: path.resolve(__dirname,'src','page.html'),
            filename:'page.html',
            chunks:['page', 'vendor'],
            hash:true,//防止緩存
            minify:{
                removeAttributeQuotes:true//壓縮 去掉引號
            }
        }),
        new webpack.ProvidePlugin({
            _:'lodash' //全部頁面都會引入 _ 這個變量,不用再import引入
        }),
        new ExtractTextWebapckPlugin('css/[name].[hash].css'), // 其實這個特性只用於打包生產環境,測試環境這樣設置會影響HMR
        new CopyWebpackPlugin([
            {
                from: path.resolve(__dirname, 'static'),
                to: path.resolve(__dirname, 'dist/static'),
                ignore: ['.*']
            }
        ]),
        new CleanWebpackPlugin([path.join(__dirname, 'dist')]),
    ],
    devtool: 'eval-source-map', // 指定加source-map的方式
    devServer: {
        contentBase: path.join(__dirname, "dist"), //靜態文件根目錄
        port: 3824, // 端口
        host: 'localhost',
        overlay: true,
        compress: false // 服務器返回瀏覽器的時候是否啓動gzip壓縮
    },
    watch: true, // 開啓監聽文件更改,自動刷新
    watchOptions: {
        ignored: /node_modules/, //忽略不用監聽變動的目錄
        aggregateTimeout: 500, //防止重複保存頻繁從新編譯,500毫米內重複保存不打包
        poll:1000 //每秒詢問的文件變動的次數
    },
}
複製代碼

在命令行下用如下命令安裝loader和依賴的插件,生成徹底的package.json項目依賴樹。java

npm install extract-text-webpack-plugin@next --save-dev
npm i style-loader css-loader postcss-loader --save-dev
npm i less less-loader --save-dev
npm i node-sass sass-loader --save-dev
npm i babel-core babel-loader babel-preset-env babel-preset-stage-0 --save-dev
npm i file-loader url-loader --save-dev

npm i html-webpack-plugin ---save-dev
npm i clean-webpack-plugin --save-dev
npm i copy-webpack-plugin --save-dev

npm run dev
複製代碼

默認打開的頁面是index.html頁面,能夠加上/page.html來打開page頁面看效果。 PS: 關於loader的詳細說明能夠參考webpack3.x的學習介紹,上面配置中須要注意的是多頁面的公共庫的引入採用的是vendor+暴露全局變量的方式,其實這種方式有諸多弊端,而webpack4針對這種狀況設置了新的API,有興趣的話,就繼續看下面的高級配置吧。node

進階的webpack4配置搭建

包含如下幾個方面:webpack

  1. 針對CSSJSTreeShaking來減小無用代碼,針對JS須要對已有的uglifyjs進行一些自定義的配置(生產環境配置)
  2. 新的公共代碼抽取工具(optimization.SplitChunksPlugin)提取重用代碼,減少打包文件。(代替commonchunkplugin,生產和開發環境都須要)
  3. 使用HappyPack進行javascript的多進程打包操做,提高打包速度,並增長打包時間顯示。(生產和開發環境都須要)
  4. 建立一個webpack.dll.config.js文件打包經常使用類庫到dll中,使得開發過程當中基礎模塊不會重複打包,而是去動態鏈接庫裏獲取,代替上一節使用的vendor。(注意這個是在開發環境使用,生產環境打包對時間要求並不高,後者每每是項目持續集成的一部分)
  5. 模塊熱替換,還須要在項目中增長一些配置,不過大型框架把這塊都封裝好了。(開發環境配置)
  6. webpack3新增的做用域提高會默認在production模式下啓用,不用特別配置,但只有在使用ES6模塊才能生效。

關於第四點,須要在package.json中的script中增長腳本: "build:dll": "webpack --config webpack.dll.config.js --mode development",git

補充安裝插件的命令行:es6

npm i purify-css purifycss-webpack -D // 用於css的tree-shaking
npm i webpack-parallel-uglify-plugin -D // 用於js的tree-shaking
npm i happypack@next -D //用於多進程打包js
npm i progress-bar-webpack-plugin -D //用於顯示打包時間和進程
npm i webpack-merge -D //優化配置代碼的工具
npm i optimize-css-assets-webpack-plugin -D //壓縮CSS
npm i chalk -D
npm install css-hot-loader -D // css熱更新
npm i mini-css-extract-plugin -D
npm i cross-env -D
複製代碼

TreeShaking須要增長的配置代碼,這一塊參考webpack文檔,須要三方面因素,分別是:github

  • 使用ES6模塊(import/export)
  • package.json文件中聲明sideEffects指定能夠treeShaking的模塊
  • 啓用UglifyJSPlugin,多入口下用WebpackParallelUglifyPlugin(這是下面的配置代碼作的事情)
/*最上面要增長的聲明變量*/
const glob = require('glob')
const PurifyCSSPlugin = require('purifycss-webpack')
const WebpackParallelUglifyPlugin = require('webpack-parallel-uglify-plugin')

/*在`plugins`配置項中須要增長的兩個插件設置*/
new PurifyCSSPlugin({
    paths: glob.sync(path.join(__dirname, 'src/*.html'))
}),
new WebpackParallelUglifyPlugin({
    uglifyJS: {
        output: {
            beautify: false, //不須要格式化
            comments: false //不保留註釋
        },
        compress: {
            warnings: false, // 在UglifyJs刪除沒有用到的代碼時不輸出警告
            drop_console: true, // 刪除全部的 `console` 語句,能夠兼容ie瀏覽器
            collapse_vars: true, // 內嵌定義了可是隻用到一次的變量
            reduce_vars: true // 提取出出現屢次可是沒有定義成變量去引用的靜態值
        }
    }
    // 有興趣能夠探究一下使用uglifyES
}),
複製代碼

關於ES6模塊這個事情,上文的第六點也提到了只有ES6模塊寫法才能用上最新的做用域提高的特性,首先webpack4.x並不須要額外修改babelrc的配置來實現去除無用代碼,這是從webpack2.x升級後支持的,改用sideEffect聲明來實現。但做用域提高仍然須要把babel配置中的module轉換去掉,修改後的.babelrc代碼以下:

{
  "presets": [["env", {"loose": true, "modules": false}], "stage-0"]
}
複製代碼

但這個時候會發現import引入樣式文件就被去掉了……只能使用require來改寫了。

打包DLL第三方類庫的配置項,用於開發環境:

  1. webpack.dll.config.js配置文件具體內容:
const path = require('path')
const webpack = require('webpack')
const pkg = require('../package.json')
/** * 儘可能減少搜索範圍 * target: '_dll_[name]' 指定導出變量名字 */
module.exports = {
    context: path.resolve(__dirname, '../'),
    entry: {
        vendor: Object.keys(pkg.dependencies)
    },
    output: {
        path: path.join(__dirname, 'dist'),
        filename: '[name].dll.js',
        library: '_dll_[name]' // 全局變量名,其餘模塊會今後變量上獲取裏面模塊
    },
    // manifest是描述文件
    plugins: [
        new webpack.DllPlugin({
            name: '_dll_[name]',
            path: path.join(__dirname, 'dist', 'manifest.json'),
            context: path.resolve(__dirname, '../')
        })
    ]
}
複製代碼
  1. webpack.config.js中增長的配置項:
/*找到上一步生成的`manifest.json`文件配置到`plugins`裏面*/
new webpack.DllReferencePlugin({
    manifest: require(path.join(__dirname, '..', 'dist', 'manifest.json')),
}),
複製代碼

多文件入口的公用代碼提取插件配置:

/*webpack4.x的最新優化配置項,用於提取公共代碼,跟`entry`是同一層級*/
optimization: {
    splitChunks: {
        cacheGroups: {
            commons: {
                chunks: "initial",
                name: "common",
                minChunks: 2,
                maxInitialRequests: 5,
                minSize: 0
            }
        }
    }
}

/*針對生成HTML的插件,需增長common,也去掉上一節加的vendor*/
new HtmlWebpackPlugin({
    template: path.resolve(__dirname,'src','index.html'),
    filename:'index.html',
    chunks:['index', 'common'],
    vendor: './vendor.dll.js', //與dll配置文件中output.fileName對齊
    hash:true,//防止緩存
    minify:{
        removeAttributeQuotes:true//壓縮 去掉引號
    }
}),
new HtmlWebpackPlugin({
    template: path.resolve(__dirname,'src','page.html'),
    filename:'page.html',
    chunks:['page', 'common'],
    vendor: './vendor.dll.js', //與dll配置文件中output.fileName對齊
    hash:true,//防止緩存
    minify:{
        removeAttributeQuotes:true//壓縮 去掉引號
    }
}),
複製代碼

PS: 這一塊要多注意,對應入口的HTML文件也要處理,關鍵是自定義的vendor項,在開發環境中引入到html

HappyPack的多進程打包處理:

/*最上面要增長的聲明變量*/
const HappyPack = require('happypack')
const os = require('os') //獲取電腦的處理器有幾個核心,做爲配置傳入
const happyThreadPool = HappyPack.ThreadPool({ size: os.cpus().length })
const ProgressBarPlugin = require('progress-bar-webpack-plugin')

/*在`module.rules`配置項中須要更改的`loader`設置*/
{
    test: /\.jsx?$/,
    loader: 'happypack/loader?id=happy-babel-js',
    include: [path.resolve('src')],
    exclude: /node_modules/,
},

/*在`plugins`配置項中須要增長的插件設置*/
new HappyPack({ //開啓多線程打包
    id: 'happy-babel-js',
    loaders: ['babel-loader?cacheDirectory=true'],
    threadPool: happyThreadPool
}),
new ProgressBarPlugin({
    format: ' build [:bar] ' + chalk.green.bold(':percent') + ' (:elapsed seconds)'
})
複製代碼

PS:要記住這種使用方法下必定要在根目錄下加.babelrc文件來設置babel的打包配置。

開發環境的代碼熱更新: 其實針對熱刷新,還有兩個方面要說起,一個是html文件裏面寫代碼的熱跟新(這個對於框架不須要,若是要實現,建議使用glup,後面有代碼),一個是寫的樣式代碼的熱更新,這兩部分也要加進去。讓咱們一塊兒看看熱更新須要增長的配置代碼:

/*在`devServer`配置項中需增長的設置*/
hot:true

/*在`plugins`配置項中須要增長的插件設置*/
new webpack.HotModuleReplacementPlugin(), //模塊熱更新
new webpack.NamedModulesPlugin(), //模塊熱更新
複製代碼

在業務代碼中要作一些改動,一個比較low的例子爲:

if(module.hot) { //設置消息監聽,從新執行函數
    module.hot.accept('./hello.js', function() {
        div.innerHTML = hello()
    })
}
複製代碼

但仍是不能實如今html修改後自動刷新頁面,這裏有個概念是熱更新不是針對頁面級別的修改,這個問題有一些解決方法,但目前都不是很完美,能夠參考這裏,如今針對CSS的熱重載有一套解決方案以下,須要放棄使用上文提到的ExtractTextWebapckPlugin,引入mini-css-extract-pluginhot-css-loader來實現,前者在webpack4.x上與hot-css-loader有報錯,讓咱們改造一番:

/*最上面要增長的聲明變量*/
const MiniCssExtractPlugin = require('mini-css-extract-plugin')

/*在樣式的`loader`配置項中需增長的設置,實現css熱更新,以css爲例,其餘能夠參照個人倉庫來寫*/
{
    test: /\.css$/,
    use: ['css-hot-loader', MiniCssExtractPlugin.loader, 'css-loader', 'postcss-loader'],
    include: [resolve('src')], //限制範圍,提升打包速度
    exclude: /node_modules/
}

/*在`plugins`配置項中須要增長的插件設置,注意這裏不能寫[hash],不然沒法實現熱跟新,若是有hash須要,能夠開發環境和生產環境分開配置*/
new MiniCssExtractPlugin({
    filename: "[name].css",
    chunkFilename: "[id].css"
})
複製代碼

用於生產環境壓縮css的插件,看官方文檔說明,樣式文件壓縮沒有內置的,因此暫時引用第三方插件來作,如下是配置示例。

/*要增長的聲明變量*/
const OptimizeCSSPlugin = require('optimize-css-assets-webpack-plugin')

/*在`plugins`配置項中須要增長的插件設置*/
new OptimizeCSSPlugin({
    cssProcessorOptions: {safe: true}
})
複製代碼

最終成果

  在進階部分咱們對webpack配置文件根據開發環境和生產環境的不一樣作了分別的配置,所以有必要分紅兩個文件,而後發現重複的配置代碼不少,做爲有代碼潔癖的人不能忍,果斷引入webpack-merge,來把相同的配置抽出來,放到build/webpack.base.js中,然後在build/webpack.dev.config.js(開發環境)和build/webpack.prod.config.js(生產環境)中分別引用,在這個過程當中也要更改以前文件的路徑設置,以避免打包或者找文件的路徑出錯,同時將package.json中的腳本命令修改成:

"scripts": {
    "build": "cross-env NODE_ENV='production' webpack --config build/webpack.prod.config.js --mode production",
    "dev": "cross-env NODE_ENV='development' webpack-dev-server --open --config build/webpack.dev.config.js --mode development",
    "dll": "webpack --config build/webpack.dll.config.js --mode production",
    "start": "npm run dll && npm run dev",
    "prod": "npm run dll && npm run build"
}
複製代碼

接下來就是代碼的重構過程,這個過程其實我建議你們本身動手作一作,就能對webpack配置文件結構更加清晰。

build文件夾下的webpack.base.js文件:

'use strict'
const path = require('path');
const chalk = require('chalk');
const ProgressBarPlugin = require('progress-bar-webpack-plugin')
const HappyPack = require('happypack')
const os = require('os')
const happyThreadPool = HappyPack.ThreadPool({ size: os.cpus().length })
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
function resolve (dir) {
  return path.join(__dirname, '..', dir)
}

function assetsPath(_path_) {
  let assetsSubDirectory;
  if (process.env.NODE_ENV === 'production') { // 這裏須要用cross-env來注入Node變量
    assetsSubDirectory = 'static' //可根據實際狀況修改
  } else {
    assetsSubDirectory = 'static'
  }
  return path.posix.join(assetsSubDirectory, _path_)
}

module.exports = {
  context: path.resolve(__dirname, '../'),
  entry: {
    index: './src/index.js',
    page: './src/page.js'
  },
  output:{
    path: resolve('dist'),
    filename:'[name].[hash].js'
  },
  resolve: {
    extensions: [".js",".css",".json"],
    alias: {} //配置別名能夠加快webpack查找模塊的速度
  },
  module: {
    // 多個loader是有順序要求的,從右往左寫,由於轉換的時候是從右往左轉換的
    rules:[
      {
        test: /\.css$/,
        use: ['css-hot-loader', MiniCssExtractPlugin.loader, 'css-loader', 'postcss-loader'],
        include: [resolve('src')], //限制範圍,提升打包速度
        exclude: /node_modules/
      },
      {
        test:/\.less$/,
        use: ['css-hot-loader', MiniCssExtractPlugin.loader, 'css-loader', 'postcss-loader', 'less-loader'],
        include: [resolve('src')],
        exclude: /node_modules/
      },
      {
        test:/\.scss$/,
        use: ['css-hot-loader', MiniCssExtractPlugin.loader, 'css-loader', 'postcss-loader', 'sass-loader'],
        include: [resolve('src')],
        exclude: /node_modules/
      },
      {
          test: /\.jsx?$/,
          loader: 'happypack/loader?id=happy-babel-js',
          include: [resolve('src')],
          exclude: /node_modules/,
      },
      { //file-loader 解決css等文件中引入圖片路徑的問題
      // url-loader 當圖片較小的時候會把圖片BASE64編碼,大於limit參數的時候仍是使用file-loader 進行拷貝
        test: /\.(png|jpg|jpeg|gif|svg)/,
        use: {
          loader: 'url-loader',
          options: {
            name: assetsPath('images/[name].[hash:7].[ext]'), // 圖片輸出的路徑
            limit: 1 * 1024
          }
        }
      },
      {
        test: /\.(mp4|webm|ogg|mp3|wav|flac|aac)(\?.*)?$/,
        loader: 'url-loader',
        options: {
          limit: 10000,
          name: assetsPath('media/[name].[hash:7].[ext]')
        }
      },
      {
        test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/,
        loader: 'url-loader',
        options: {
          limit: 10000,
          name: assetsPath('fonts/[name].[hash:7].[ext]')
        }
      }
    ]
  },
  optimization: { //webpack4.x的最新優化配置項,用於提取公共代碼
    splitChunks: {
      cacheGroups: {
        commons: {
          chunks: "initial",
          name: "common",
          minChunks: 2,
          maxInitialRequests: 5, // The default limit is too small to showcase the effect
          minSize: 0 // This is example is too small to create commons chunks
        }
      }
    }
  },
  plugins: [
    new HappyPack({
      id: 'happy-babel-js',
      loaders: ['babel-loader?cacheDirectory=true'],
      threadPool: happyThreadPool
    }),
    new MiniCssExtractPlugin({
      filename: "[name].css",
      chunkFilename: "[id].css"
    }),
    new ProgressBarPlugin({
      format: ' build [:bar] ' + chalk.green.bold(':percent') + ' (:elapsed seconds)'
    }),
  ]
}
複製代碼

webpack.dev.config.js文件內容:

const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin') // 生成html的插件
const webpack = require('webpack')
const baseConfig = require('./webpack.base')
const merge = require('webpack-merge')

const devWebpackConfig = merge(baseConfig, {
  output:{
    publicPath: '/'
  },
  devtool: 'eval-source-map', // 指定加source-map的方式
  devServer: {
    inline:true,//打包後加入一個websocket客戶端
    hot:true,//熱加載
    contentBase: path.join(__dirname, "..", "dist"), //靜態文件根目錄
    port: 3824, // 端口
    host: 'localhost',
    overlay: true,
    compress: false // 服務器返回瀏覽器的時候是否啓動gzip壓縮
  },
  watchOptions: {
      ignored: /node_modules/, //忽略不用監聽變動的目錄
      aggregateTimeout: 500, //防止重複保存頻繁從新編譯,500毫米內重複保存不打包
      poll:1000 //每秒詢問的文件變動的次數
  },
  plugins: [
    // 多入口的html文件用chunks這個參數來區分
    new HtmlWebpackPlugin({
      template: path.resolve(__dirname, '..', 'src','index.html'),
      filename:'index.html',
      chunks:['index', 'common'],
      vendor: './vendor.dll.js', //與dll配置文件中output.fileName對齊
      hash:true,//防止緩存
      minify:{
          removeAttributeQuotes:true//壓縮 去掉引號
      }
    }),
    new HtmlWebpackPlugin({
      template: path.resolve(__dirname, '..', 'src','page.html'),
      filename:'page.html',
      chunks:['page', 'common'],
      vendor: './vendor.dll.js', //與dll配置文件中output.fileName對齊
      hash:true,//防止緩存
      minify:{
          removeAttributeQuotes:true//壓縮 去掉引號
      }
    }),
    new webpack.DllReferencePlugin({
      manifest: path.resolve(__dirname, '..', 'dist', 'manifest.json')
    }),
    new webpack.HotModuleReplacementPlugin(), //HMR
    new webpack.NamedModulesPlugin() // HMR
  ]
})

module.exports = devWebpackConfig
複製代碼

webpack.dev.config.js文件內容:

'use strict'
const path = require('path');
const CopyWebpackPlugin = require('copy-webpack-plugin') // 複製靜態資源的插件
const CleanWebpackPlugin = require('clean-webpack-plugin') // 清空打包目錄的插件
const HtmlWebpackPlugin = require('html-webpack-plugin') // 生成html的插件
const webpack = require('webpack')
const baseConfig = require('./webpack.base')
const merge = require('webpack-merge')

const glob = require('glob')
const PurifyCSSPlugin = require('purifycss-webpack')
const WebpackParallelUglifyPlugin = require('webpack-parallel-uglify-plugin')
const OptimizeCSSPlugin = require('optimize-css-assets-webpack-plugin')

module.exports = merge(baseConfig, {
    output:{
        publicPath: './' //這裏要放的是靜態資源CDN的地址(只在生產環境下配置)
    },
    plugins: [
        new HtmlWebpackPlugin({
            template: path.resolve(__dirname, '..', 'src', 'index.html'),
            filename:'index.html',
            chunks:['index', 'common'],
            hash:true,//防止緩存
            minify:{
                removeAttributeQuotes:true//壓縮 去掉引號
            }
        }),
        new HtmlWebpackPlugin({
            template: path.resolve(__dirname, '..', 'src', 'page.html'),
            filename:'page.html',
            chunks:['page', 'common'],
            hash:true,//防止緩存
            minify:{
                removeAttributeQuotes:true//壓縮 去掉引號
            }
        }),
        new CopyWebpackPlugin([
            {
                from: path.join(__dirname, '..', 'static'),
                to: path.join(__dirname,  '..', 'dist', 'static'),
                ignore: ['.*']
            }
        ]),
        new CleanWebpackPlugin(['dist'], {
            root: path.join(__dirname, '..'),
            verbose: true,
            dry:  false
        }),
        new OptimizeCSSPlugin({
            cssProcessorOptions: {safe: true}
        }),
        new PurifyCSSPlugin({
            paths: glob.sync(path.join(__dirname, '../src/*.html'))
        }),
        new WebpackParallelUglifyPlugin({
            uglifyJS: {
                output: {
                    beautify: false, //不須要格式化
                    comments: false //不保留註釋
                },
                compress: {
                    warnings: false, // 在UglifyJs刪除沒有用到的代碼時不輸出警告
                    drop_console: true, // 刪除全部的 `console` 語句,能夠兼容ie瀏覽器
                    collapse_vars: true, // 內嵌定義了可是隻用到一次的變量
                    reduce_vars: true // 提取出出現屢次可是沒有定義成變量去引用的靜態值
                }
            }
        }),
    ]
})
複製代碼

多說一句,就是實現JS打包的treeShaking還有一種方法是編譯期分析依賴,利用uglifyjs來完成,這種狀況須要保留ES6模塊才能實現,所以在使用這一特性的倉庫中,.babelrc文件的配置爲:"presets": [["env", { "modules": false }], "stage-0"],就是打包的時候不要轉換模塊引入方式的含義。

接下來就能夠運行npm start,看一下進階配置後的成果啦,吼吼,以後只要不進行build打包操做,經過npm run dev啓動,不用重複打包vendor啦。生產環境打包使用的是npm run build

以上就是對webpack4.x配置的踩坑過程,期間參考了大量谷歌英文資料,但願能幫助你們更好地掌握wepback最新版本的配置,以上內容親測跑通,有問題的話,歡迎加我微信(kashao3824)討論,到github地址issue也可,歡迎fork/star

最新更改:

  • 修復了common會重複打包已有dll庫的問題
  • 如今的dll庫會自動根據package.json中的配置項生成
  • dll如今是生產環境打包模式,而且vendor.dll.js如今在生產環境下也會注入HTML模板中
  • 生產環境打包使用命令npm run prod
  • 修復了process.env.NODE_ENV在打包過程當中取不到的問題 issue2
相關文章
相關標籤/搜索