webpack4自定義腳手架優(yōu)化

在上篇中搭建了vue的基本腳手架,為了加快腳手架打包項(xiàng)目的構(gòu)建速度和減少打包代碼的體積,下面在上一篇的基礎(chǔ)上添磚加瓦,完成腳手架的優(yōu)化。

腳手架目錄

腳手架是在原有的基礎(chǔ)上構(gòu)建的,所以基本的目錄結(jié)構(gòu)相類似。不過加上了對JS壓縮,CSS代碼壓縮(tree shake),引入了dllPlugin打包不常變動的類庫生成動態(tài)鏈接庫,加入happypack開啟多進(jìn)程打包,加快構(gòu)建的速度,下面也會說到運(yùn)用npm script,通過sftp把打包的文件上傳到部署的目錄,避免手動上傳。

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

|-- .gitignore
|-- build
|   |-- webpack.base.conf.js
|   |-- webpack.dev.conf.js (開發(fā)環(huán)境打包配置)
|   |-- webpack.dll.conf.js (生成dll動態(tài)鏈接庫)
|   `-- webpack.prod.conf.js(生產(chǎn)環(huán)境打包配置)
|-- config
|   `-- index.js (打包配置參數(shù))
|-- dist (生產(chǎn)環(huán)境打包目錄)
|   |-- 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壓縮打包結(jié)構(gòu))
|           |-- 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(開發(fā)主目錄)
    |-- App.vue
    |-- main.js
    |-- router
    |   `-- index.js
    `-- views
        |-- admin
        |   `-- index.vue
        `-- login
            `-- index.vue

開發(fā)環(huán)境配置

webpack.dev.conf.js開發(fā)環(huán)境配置文件中,增加了happypack、Dllplugin動態(tài)鏈接。happypack主要是開啟多進(jìn)程打包文件,加快打包構(gòu)建速度。而Dllplugin主要是對一些不常變動的類庫提取打包,在再次編譯打包過程,可以免除再次的打包。

開發(fā)環(huán)境配置文件

下面是webpack.dev.conf.js開發(fā)環(huán)境全瞰,主要增加了DllReferencePlugin、happypack、add-asset-html-webpack-plugin插件。下面對各自的作用詳細(xì)說明。

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') // 對應(yīng)的 dll 文件路徑
        }),
        new AddAssetHtmlWebpackPlugin({
            filepath: path.resolve(__dirname, '../dll/axios.dll.js') // 對應(yīng)的 dll 文件路徑
        }),
        new HotModuleReplacementPlugin()
    ],
    devServer: {
        ...config.dev.server
    },
    /**
     * 開啟webpack對文件變動的監(jiān)聽
     */
    watch: true,
    watchOptions: {
        ignored: /node_modules/,
        aggregateTimeout: 300,
        poll: 1000
    }
}

DllPlugin動態(tài)鏈接庫

.dll 為后綴的文件,這些文件稱為動態(tài)鏈接庫。提供為其他模塊使用的函數(shù)和數(shù)據(jù)。在一些不常變動的類庫vuejs、vue-routervuex,我們可以打包成動態(tài)鏈接庫,動態(tài)鏈接庫只需要編譯一次,在之后的構(gòu)建過程中被動態(tài)鏈接庫包含的模塊將不會在重新編譯。加快我們構(gòu)建的速度。配置dll動態(tài)鏈接庫,需要另外一個配置文件webpack.dll.conf.js。

Dllplugin 插件專門用于單獨(dú)的webpack配置中,以創(chuàng)建僅限dll的捆綁包。它創(chuàng)建了一個manifest.json文件,用于[DllReferencePlugin]映射依賴項(xiàng)。下面webpack.dll.conf.js通過配置生成dll動態(tài)鏈接庫。

1

通過配置npm script生成動態(tài)鏈接庫文件,在package.json定義了npm run build:dll打包生成dll動態(tài)鏈接庫。

2

生成下面的dll動態(tài)鏈接庫,在運(yùn)行開發(fā)環(huán)境前,需要先打包生成dll動態(tài)鏈接庫,這樣開發(fā)環(huán)境打包時會自動引用動態(tài)鏈接庫。其中frameworkaxios都是我們自定義命名,menifest是類庫的映射文件。

3

有動態(tài)鏈接庫,還需要在webpack.dev.conf.js文件中對鏈接庫的引入。DllReferencePlugin 會去 manifest.json 文件讀取 name字段的值, 把值的內(nèi)容作為在從全局變量中獲取動態(tài)鏈接庫中內(nèi)容時的全局變量名。而AddAssetHtmlWebpackPlugin是將JavaScript或CSS資源添加到生成的HTML中,這里使用該插件將dll動態(tài)鏈接庫添加到index.html中。

4

生成index.html源碼已經(jīng)加入framework.dll.jsaxios.dll.js,源碼結(jié)構(gòu)如下所示:

5

happypack多進(jìn)程打包

HappyPack通過并行轉(zhuǎn)換文件使得初始webpack構(gòu)建更快。happypack通過開啟多個子進(jìn)程并行打包文件,使文件構(gòu)建速度明顯加快。在使用happypack過程中,發(fā)現(xiàn)happypackscss支持度不高。所以在webpack.dev.conf.js沒有對scss文件進(jìn)行處理。在happypack的官網(wǎng)案例中,happypackless的支持度比較高。happypack使用比較簡單,如下所示,下面對以js為后綴的文件用happypack進(jìn)行打包。happypack默認(rèn)開啟的進(jìn)程是3個,可以自定義配置,詳細(xì)參數(shù)可以參照官方文檔說明。

6

開啟happypack成功打包的構(gòu)建流程圖

7

生產(chǎn)環(huán)境配置

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

生產(chǎn)環(huán)境配置文件

webpack.prod.conf.js生產(chǎn)配置環(huán)境文件,沒有對不常更改的類庫(vuejs)生成動態(tài)鏈接庫。利用webpack4內(nèi)部提取公共代碼,進(jìn)行了處理。總瞰如下所示:

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

漸進(jìn)式Web應(yīng)用程序(或PWA)使我們開發(fā)的應(yīng)用在離線后,依然可以訪問。主要依靠了Service Workers的Web技術(shù)實(shí)現(xiàn)。在webpack社區(qū)提供了workbox-webpack-plugin方便實(shí)現(xiàn)PWA功能。

8

wenbpack.prod.conf.js配置好插件workbox-webpack-plugin后,需要在項(xiàng)目的入口文件里注冊serviceWorker。這里展示的vuejs的入口文件main.js

9

運(yùn)行生產(chǎn)環(huán)境打包腳本命令,有2個額外的文件正在生成; service-worker.jsprecache-manifest.18d731d6b692ffdc910ac5548db2f8c0.jsservice-worker.jsService Worker文件,precache-manifest.18d731d6b692ffdc910ac5548db2f8c0.js是一個service-worker.js需要運(yùn)行的文件。您自己生成的文件可能會有所不同; 但你應(yīng)該有一個service-worker.js文件。

10

接下來要離線環(huán)境進(jìn)行測試,查看service-worker離線緩存的效果。webpack官網(wǎng)提供了一個http-serverservice-worker測試。

11

service worker已經(jīng)生效,斷開http-server服務(wù),刷新網(wǎng)頁,依舊可以正確顯示到需要的資源。

12

optimize-css-assets-webpack-plugin

optimize-css-assets-webpack-plugin主要是對打包的css文件進(jìn)行壓縮處理。在webpack4版本中,設(shè)置mode模式為production,會自動對js代碼進(jìn)行壓縮處理,其實(shí)底下是用uglifyjs-webpack-plugin進(jìn)行壓縮。但是css文件還是需要手動進(jìn)行壓縮。而optimize-css-assets-webpack-plugin起的就是壓縮css作用。

打包壓縮的文件如下所示,可見css樣式是壓縮狀態(tài)的。

.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 工具和插件轉(zhuǎn)換 CSS代碼的工具。postcss加上autoprefixer幫助我們在新的css3屬性自動添加廠商前綴。

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

 "browserslist": [
    "last 5 version",
    ">1%",
    "ie>=8"
  ]

在項(xiàng)目的根目錄下,創(chuàng)建postcss.config.js,內(nèi)容如下:

module.exports = {
  plugins: {
    autoprefixer: {
    }
  }
};

JS tree shake

webpack4中使用tree shake剔除沒有使用的JS代碼比較簡單,直接在optimization中開啟usedExports,不過JS tree shake只對ES6語法有效,所以要度量使用。關(guān)于對于csstree shake,有朋友建議是purecss對沒有使用的css代碼剔除,不過實(shí)驗(yàn)了一下,感覺沒有效果,就沒有做推薦。

13

splitChunks 提取公用代碼

webpack.dev.conf.js開發(fā)環(huán)境中使用了dllPlugin的動態(tài)鏈接庫方法提取公用的類庫,加快構(gòu)建速度。在生產(chǎn)環(huán)境中,采用的是webpack4內(nèi)置功能提取公用代碼。在webpack3中采取CommonChunkPlugin提取公共模塊和第三方庫。在webpack4已經(jīng)不建議使用該插件,而是使用內(nèi)置的方法。

14

compression-webpack-plugin

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

15

總結(jié)

對常見的優(yōu)化方案做了一次測試和實(shí)踐,如果有其他好的優(yōu)化方法,可以留言分享。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容