webpack4自定義腳手架構建優化

在上篇中搭建了vue的基本腳手架,爲了加快腳手架打包項目的構建速度和減小打包代碼的體積,下面在上一篇的基礎上添磚加瓦,完成腳手架的優化。javascript

腳手架目錄

腳手架是在原有的基礎上構建的,因此基本的目錄結構相相似。不過加上了對JS壓縮,CSS代碼壓縮(tree shake),引入了dllPlugin打包不常變更的類庫生成動態連接庫,加入happypack開啓多進程打包,加快構建的速度,下面也會說到運用npm script,經過sftp把打包的文件上傳到部署的目錄,避免手動上傳。css

腳手架地址:https://github.com/Harhao/webpack/tree/devhtml

|-- .gitignore
|-- build
|   |-- webpack.base.conf.js
|   |-- webpack.dev.conf.js (開發環境打包配置)
|   |-- webpack.dll.conf.js (生成dll動態連接庫)
|   `-- webpack.prod.conf.js(生產環境打包配置)
|-- config
|   `-- index.js (打包配置參數)
|-- dist (生產環境打包目錄)
|   |-- index.html
|   |-- precache-manifest.2df00ef5798fdac7218a2cecb8233a17.js(緩存清單)
|   |-- service-worker.js(service-worker文件)
|   `-- static
|       |-- css
|       |   `-- main_de7bb505.css
|       `-- js
|           |-- framework_2f05dfbce1b06dd8.js
|           |-- framework_2f05dfbce1b06dd8.js.gz (開啓gz壓縮打包結構)
|           |-- main_2f05dfbce1b06dd8.js
|           `-- vendors~main_2f05dfbce1b06dd8.js
|-- dll(開啓dllplugin打包的連接庫)
|   |-- axios.dll.js
|   |-- axios.manifest.json
|   |-- framework.dll.js
|   `-- framework.manifest.json
|-- package.json
|-- postcss.config.js
|-- public
|   `-- index.html
`-- src(開發主目錄)
    |-- App.vue
    |-- main.js
    |-- router
    |   `-- index.js
    `-- views
        |-- admin
        |   `-- index.vue
        `-- login
            `-- index.vue
複製代碼

開發環境配置

webpack.dev.conf.js開發環境配置文件中,增長了happypackDllplugin動態連接。happypack主要是開啓多進程打包文件,加快打包構建速度。而Dllplugin主要是對一些不常變更的類庫提取打包,在再次編譯打包過程,能夠免除再次的打包。vue

開發環境配置文件

下面是webpack.dev.conf.js開發環境全瞰,主要增長了DllReferencePluginhappypackadd-asset-html-webpack-plugin插件。下面對各自的做用詳細說明。java

const path = require("path")
const HappyPack = require("happypack")
const VueLoaderPlugin = require("vue-loader/lib/plugin")
const HtmlWebpackPlugin = require("html-webpack-plugin")
const DllReferencePlugin = require('webpack/lib/DllReferencePlugin')
const AddAssetHtmlWebpackPlugin = require("add-asset-html-webpack-plugin")
const HotModuleReplacementPlugin = require("webpack/lib/HotModuleReplacementPlugin")
const config = require("../config/index")

module.exports = {
    mode: "development",
    devtool: "cheap-module-eval-source-map",
    entry: path.resolve(__dirname, "../src/main.js"),
    output: {
        filename: "[name]_[hash:16].js",
        path: path.resolve(__dirname, "../dist"),
        publicPath: '/'
    },
    resolve: {
        modules: ["node_modules"],
        alias: {
            "@": path.resolve(__dirname, '../src')
        },
        extensions: [".js", ".json", ".vue", ".scss"]
    },
    module: {
        rules: [{
            test: /\.js$/,
            use: ["happypack/loader?id=babel"],
            exclude: path.resolve(__dirname, '../node_modules')
        },
        {
            test: /\.scss$/,
            use: ["style-loader", "css-loader", "postcss-loader", "sass-loader"]
        }, {
            test: /\.vue$/,
            use: ["vue-loader"]
        }, {
            test: /\.(png|jpe?g|gif|svg)(\?.*)?$/,
            use: ["happypack/loader?id=url"]
        }]
    },
    plugins: [
        new DllReferencePlugin({
            manifest: require('../dll/framework.manifest.json')
        }),
        new DllReferencePlugin({
            manifest: require('../dll/axios.manifest.json')
        }),
        new HappyPack({
            id: 'babel',
            loaders: ["babel-loader?cacheDirectory"]
        }),
        new HappyPack({
            id: 'url',
            loaders: ["url-loader"]
        }),
        new VueLoaderPlugin(),
        new HtmlWebpackPlugin({
            template: path.resolve(__dirname, "../public/index.html"),
            filename: 'index.html',
            inject: 'body'
        }),
        new AddAssetHtmlWebpackPlugin({
            filepath: path.resolve(__dirname, '../dll/framework.dll.js') // 對應的 dll 文件路徑
        }),
        new AddAssetHtmlWebpackPlugin({
            filepath: path.resolve(__dirname, '../dll/axios.dll.js') // 對應的 dll 文件路徑
        }),
        new HotModuleReplacementPlugin()
    ],
    devServer: {
        ...config.dev.server
    },
    /** * 開啓webpack對文件變更的監聽 */
    watch: true,
    watchOptions: {
        ignored: /node_modules/,
        aggregateTimeout: 300,
        poll: 1000
    }
}
複製代碼

DllPlugin動態連接庫

.dll 爲後綴的文件,這些文件稱爲動態連接庫。提供爲其餘模塊使用的函數和數據。在一些不常變更的類庫vuejsvue-routervuex,咱們能夠打包成動態連接庫,動態連接庫只須要編譯一次,在以後的構建過程當中被動態連接庫包含的模塊將不會在從新編譯。加快咱們構建的速度。配置dll動態連接庫,須要另一個配置文件webpack.dll.conf.jsnode

Dllplugin 插件專門用於單獨的webpack配置中,以建立僅限dll的捆綁包。它建立了一個manifest.json文件,用於[DllReferencePlugin]映射依賴項。下面webpack.dll.conf.js經過配置生成dll動態連接庫。linux

1

經過配置npm script生成動態連接庫文件,在package.json定義了npm run build:dll打包生成dll動態連接庫。webpack

2

生成下面的dll動態連接庫,在運行開發環境前,須要先打包生成dll動態連接庫,這樣開發環境打包時會自動引用動態連接庫。其中frameworkaxios都是咱們自定義命名,menifest是類庫的映射文件。ios

3

有動態連接庫,還須要在webpack.dev.conf.js文件中對連接庫的引入。DllReferencePlugin 會去 manifest.json 文件讀取 name字段的值, 把值的內容做爲在從全局變量中獲取動態連接庫中內容時的全局變量名。而AddAssetHtmlWebpackPlugin是將JavaScript或CSS資源添加到生成的HTML中,這裏使用該插件將dll動態連接庫添加到index.html中。css3

4

生成index.html源碼已經加入framework.dll.jsaxios.dll.js,源碼結構以下所示:

5

happypack多進程打包

HappyPack經過並行轉換文件使得初始webpack構建更快。happypack經過開啓多個子進程並行打包文件,使文件構建速度明顯加快。在使用happypack過程當中,發現happypackscss支持度不高。因此在webpack.dev.conf.js沒有對scss文件進行處理。在happypack的官網案例中,happypackless的支持度比較高。happypack使用比較簡單,以下所示,下面對以js爲後綴的文件用happypack進行打包。happypack默認開啓的進程是3個,能夠自定義配置,詳細參數能夠參照官方文檔說明。

6

開啓happypack成功打包的構建流程圖

7

生產環境配置

webpack.prod.conf.js生產配置文件中,添加了workbox-webpack-plugin漸進行PWA的資源緩存;optimize-css-assets-webpack-plugincss代碼壓縮處理;compression-webpack-plugin對大文件進行gz壓縮;UglifyJsPluginjs文件壓縮處理,UglifyJsPlugin在上篇已作說明。

生產環境配置文件

webpack.prod.conf.js生產配置環境文件,沒有對不常更改的類庫(vuejs)生成動態連接庫。利用webpack4內部提取公共代碼,進行了處理。總瞰以下所示:

const path = require("path")
const VueLoaderPlugin = require("vue-loader/lib/plugin")
const HtmlWebpackPlugin = require("html-webpack-plugin")
const { CleanWebpackPlugin } = require("clean-webpack-plugin")
const UglifyJsPlugin = require("uglifyjs-webpack-plugin")
const MiniCssExtractPlugin = require("mini-css-extract-plugin")
const WorkboxPlugin = require('workbox-webpack-plugin')
const OptimizeCSSAssetsPlugin = require('optimize-css-assets-webpack-plugin')
const CompressionWebpackPlugin = require('compression-webpack-plugin')
module.exports = {
    mode: "production",
    entry: {
        main: path.resolve(__dirname, "../src/main.js"),
        framework: ["vue", "vue-router", "vuex"]
    },
    output: {
        filename: "static/js/[name]_[hash:16].js",
        path: path.resolve(__dirname, "../dist"),
        publicPath: './'
    },
    resolve: {
        modules: ["node_modules"],
        alias: {
            "@": path.resolve(__dirname, '../src')
        },
        extensions: [".js", ".json", ".vue", ".scss"]
    },
    module: {
        rules: [{
            test: /\.js$/,
            use: [{
                loader: "babel-loader",
                options: {
                    presets: ['@babel/preset-env']
                }
            }],
            exclude: /node_modules/
        },
        {
            test: /\.scss$/,
            use: [
                {
                    loader: MiniCssExtractPlugin.loader,

                },
                "css-loader",
                "postcss-loader",
                "sass-loader"
            ],
            exclude: /node_modules/
        },
        {
            test: /\.vue$/,
            use: ["vue-loader"],
            exclude: /node_modules/
        }]
    },
    plugins: [
        new VueLoaderPlugin(),
        new HtmlWebpackPlugin({
            title: '自搭建webpack腳手架',
            template: path.resolve(__dirname, "../public/index.html"),
            filename: 'index.html',
            inject: 'body',
            minify: {
                removeComments: true,//移除註釋
                collapseWhitespace: true,//移除空白字符串
                removeAttributeQuotes: true //移除雙引號
            }
        }),
        new CleanWebpackPlugin(),
        new MiniCssExtractPlugin({
            filename: 'static/css/[name]_[chunkhash:8].css',
            chunkFilename: '[id].css',
        }),
        new WorkboxPlugin.GenerateSW({
            clientsClaim: true,
            skipWaiting: true
        }),
        new CompressionWebpackPlugin({
            filename: '[path].gz[query]',
            algorithm: 'gzip',
            test: /(\.js$|\.css$)/,
            threshold: 10240,
            minRatio: 0.8
        })
    ],
    optimization: {
        usedExports:true,
        minimizer: [
            new UglifyJsPlugin({
                test: /\.js(\?.*)?$/i
            }),
            new OptimizeCSSAssetsPlugin({
                // cssProcessorOptions: true? {map: { inline: false }}:{}
            })
        ],
        splitChunks: {
            chunks: "all",
            minChunks: 1,
            minSize: 0,
            cacheGroups: {
                framework: {
                    test: "framework",
                    name: "framework",
                    enforce: true
                }
            }
        }
    }
}
複製代碼

workbox-webpack-plugin

漸進式Web應用程序(或PWA)使咱們開發的應用在離線後,依然能夠訪問。主要依靠了Service Workers的Web技術實現。在webpack社區提供了workbox-webpack-plugin方便實現PWA功能。

8

wenbpack.prod.conf.js配置好插件workbox-webpack-plugin後,須要在項目的入口文件裏註冊serviceWorker。這裏展現的vuejs的入口文件main.js

9

運行生產環境打包腳本命令,有2個額外的文件正在生成; service-worker.jsprecache-manifest.18d731d6b692ffdc910ac5548db2f8c0.jsservice-worker.jsService Worker文件,precache-manifest.18d731d6b692ffdc910ac5548db2f8c0.js是一個service-worker.js須要運行的文件。您本身生成的文件可能會有所不一樣; 但你應該有一個service-worker.js文件。

10

接下來要離線環境進行測試,查看service-worker離線緩存的效果。webpack官網提供了一個http-serverservice-worker測試。

11

service worker已經生效,斷開http-server服務,刷新網頁,依舊能夠正確顯示到須要的資源。

12

optimize-css-assets-webpack-plugin

optimize-css-assets-webpack-plugin主要是對打包的css文件進行壓縮處理。在webpack4版本中,設置mode模式爲production,會自動對js代碼進行壓縮處理,其實底下是用uglifyjs-webpack-plugin進行壓縮。可是css文件仍是須要手動進行壓縮。而optimize-css-assets-webpack-plugin起的就是壓縮css做用。

打包壓縮的文件以下所示,可見css樣式是壓縮狀態的。

.entry[data-v-7ba5bd90]{color:red;-webkit-transform:scale(.8);-ms-transform:scale(.8);transform:scale(.8)}.admin[data-v-2ba5fe30]{color:#333}
複製代碼

optimize-css-assets-webpack-pluginwebpack.prod.conf.js配置使用:

13

postcss

postcss是一個用JavaScript 工具和插件轉換 CSS代碼的工具。postcss加上autoprefixer幫助咱們在新的css3屬性自動添加廠商前綴。

package.json文件添加如下字段:

"browserslist": [
    "last 5 version",
    ">1%",
    "ie>=8"
  ]
複製代碼

在項目的根目錄下,建立postcss.config.js,內容以下:

module.exports = {
  plugins: {
    autoprefixer: {
    }
  }
};
複製代碼

JS tree shake

webpack4中使用tree shake剔除沒有使用的JS代碼比較簡單,直接在optimization中開啓usedExports,不過JS tree shake只對ES6語法有效,因此要度量使用。關於對於csstree shake,有朋友建議是purecss對沒有使用的css代碼剔除,不過實驗了一下,感受沒有效果,就沒有作推薦。

13

webpack-deploy-sftp

在打包生成生產環境的項目中,想自動上傳打包目錄到開發測試環境預覽,能夠利用webpack-deploy-sftp插件集成到webpack.prod.conf.js中,打包完成會自動上傳到linux開發環境中。

17
若是想經過自定義 npm script的方法,能夠本身編寫上傳腳本,下面是本身編寫的一個參考:

const Client = require("ssh2-sftp-client")
const fs = require('fs')
const path = require('path')
//有關帳號密碼端口設置文件
const config = require('./config')
//本地須要上傳的目錄
const filePath = './dist'
//遠程地址
const remotePath = '/home/xx/xx'
const sftp = new Client()
let tmpPath = ''
sftp.connect(config).then(() => {
    console.log("鏈接成功")
    fileTravel(filePath)
})
sftp.on('end', () => {
    console.log('end event')
})
sftp.on('close', () => {
    console.log('close event')
})
function fileTravel(filePath) {
    fs.readdir(filePath, function (err, files) {
        if (err) {
            console.log(err)
        } else {
            files.forEach(function (filename) {
                const filedir = path.join(filePath, filename)
                console.warn(`upload file: ${filedir}`)
                let url = filedir.substr(filedir.indexOf('\\') + 1)
                url = url.replace(/\\/g, '/')
                fs.stat(filedir, function (error, stats) {
                    if (error) {
                        console.error(`獲取文件stats失敗`)
                    } else {
                        const isFile = stats.isFile()
                        const isDir = stats.isDirectory()
                        if (isDir) {
                            tmpPath = `${remotePath}/${url}`
                            sftp.mkdir(tmpPath, true).then(() => {
                                fileTravel(filedir);
                            }).catch(err => {
                                fileTravel(filedir)
                            })
                        }
                        if (isFile) {
                            sftp.put(filedir, `${remotePath}/${url}`)
                        }
                    }
                })
            })
        }
    })
}
複製代碼

定義npm script命令,執行上傳打包的文件夾。

18

splitChunks 提取公用代碼

webpack.dev.conf.js開發環境中使用了dllPlugin的動態連接庫方法提取公用的類庫,加快構建速度。在生產環境中,採用的是webpack4內置功能提取公用代碼。在webpack3中採起CommonChunkPlugin提取公共模塊和第三方庫。在webpack4已經不建議使用該插件,而是使用內置的方法。

14

compression-webpack-plugin

在上面使用splitChunks提取公共模塊framework,可是打包出來的模塊仍是比較大,在瀏覽器下載包模塊時候時間會比較長,因此在對打包出來,體積比較大的模塊使用compression-webpack-plugin打包成gz包。配合後端開啓gz支持,這樣能夠加快資源下載的速度。

15

webpack-merge

webpack.dev.conf.jswebpack.prod.conf.js文件的公用部門提取到webpack.base.conf.js中,經過webpack-merge合併不一樣部分。

webpack.base.conf.js

webpack.base.conf.js公用部份以下:

16

webpack.dev.conf.js

wepack.dev.conf.js剔除公用的部分,其餘配置項以下所示:

17

webpacck.prod.conf.js

wepack.prod.conf.js剔除公用的部分,其餘配置項以下所示:

18

若是喜歡能夠給個贊~或星~喲
GitHub地址: github.com/Harhao/webp…

相關文章
相關標籤/搜索