1. 程式人生 > >Webpack抽離第三方類庫以及common解決方案

Webpack抽離第三方類庫以及common解決方案

前端構建場景有兩種,一種是單頁面構建,另一種是多入口構建多頁面應用程式(我視野比較小,目前就知道這兩種),下面我們針對這兩種場景總結了幾種抽離第三方類庫以及公共檔案的解決方案。

如果有哪些地方優化不周到,請指點一二,另外求關注求星星,麼麼噠

單頁面構建:

常規配置

const path = require('path');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const { CleanWebpackPlugin } = require('clean-webpack-plugin')

module.exports = {
    mode: "development",
    entry: {
        app: './app.js'
    },
    output: {
        path: path.resolve(__dirname, './build/'),
        filename: "bundle-[chunkhash:8].js"
    },
    devtool: "source-map",
    module: {
        rules: [
            {
                test: /\.js$/,
                use: {
                    loader: 'babel-loader',
                },
                exclude: /node_modules/
            },
            {
                test: /\.css$/,
                use: [
                    MiniCssExtractPlugin.loader,
                    'css-loader'
                ],
                exclude: /node_modules/
            },
            {
                test: /\.less$/,
                use: [
                    MiniCssExtractPlugin.loader,
                    'css-loader',    // translates CSS into CommonJS
                    'less-loader',     // compiles Less to CSS
                ],
                exclude: /node_modules/
            },
            {
                test: /\.(jpe?g|png|gif|svg)$/i,
                use: [{
                    loader: 'file-loader',
                    options: {
                        limit: 1024,
                    }
                }],
            }
        ]
    },
    plugins: [
        new MiniCssExtractPlugin({
            filename: "[name].[chunkhash:8].css",
            chunkFilename: "[id].[chunkhash:8].css"
        }),
        new HtmlWebpackPlugin({
            title: 'webpack',
            template: './index.html',
            chunks: ['app']
        }),
        new CleanWebpackPlugin()
    ],
}

在指令碼種我們常規寫法是這樣的

require('./main-less.less');
require('./main-css.css');
const ReactDOM = require('react-dom');
const React = require('react');
import Main from './main.js';
// /**
//  *  引入 scope hisiting test
//  */
// import B from './ScopeHisitingTest/b';
ReactDOM.render(
    <Main />,
    document.getElementById('app')
)

我們看下構建輸出結果

 

 

 現在我們看到這個應該思考三個問題

  1.指令碼部分,難道每個頁面都要寫一邊import React&ReactDOM 嗎

  2.構建體積能不能再縮小一點

  3.構建速度能不能在快一點
以上三個問題都會在開發過程中耽誤開發效率,我們開始處理這三個問題

方案1

html全域性引用第三方類庫,比如React,因為React原始碼中將React掛在到了window上,這麼做解決了什麼呢,腳本里面我們不用在每一個頁面中引用第三方類庫了,我們看下程式碼

<!DOCTYPE html>
<html>

<head>
    <meta charset="UTF-8">
</head>

<body>
    <div id='app'></div>
    <script crossorigin src="https://unpkg.com/react@16/umd/react.development.js"></script>
    <script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>
</body>

</html>

好了解決了指令碼部分不用每個頁面都需要import一次了

構建體積怎麼減小呢

這裡我們可以藉助webpack外掛,下面我們上程式碼

 externals: {
        'react': 'react',
        'react-dom': 'react-dom'
    },

我們將兩個第三方類庫打包的時候不依賴進去就可以啦,我們看下打包效果

 

 

 可以明顯的看到,採用這種用法之後會有一個問題就是,我們在腳本里面就不能在引用第三方類庫了,不然打包進去,external會找不到這個第三方導致報錯,直接用就好了,我們畢竟要解決的就是這個問題嘛。

以上就是第一種解決方案。

第二種

第二種方式採用將第三方類庫打包到指定的dll中,通過webpack構建應用時引用

涉及兩個Plugin,分別是DllReferencePlugin,DllPlugin

首先建立一個專門針對dll的webpack配置檔案

const webpack = require('webpack');
const path = require('path');

module.exports = {
    entry: {
        react: [
            'react',
            'react-dom'
        ]
    },
    output: {
        filename: '[name].dll.js',
        path: path.resolve(__dirname, './distDll/dll/'),
        library: '[name]_dll_[hash]'
    },
    plugins: [
        new webpack.DllPlugin({
            name: '[name]_dll_[hash]',
            context: __dirname,
            path: path.join(__dirname, 'distDll/dll', '[name].manifest.json')
        })
    ]
}

然後執行這個webpack,生成dll以及描述模組執行依賴的manifest.json,我們應用的webpack需要引用這個dll

 new webpack.DllReferencePlugin({
            context: __dirname,
            manifest: require('./distDll/dll/react.manifest.json')
        }),

到這裡就結束了嗎,並不是,我們執行下webpack會發現,React找不到了,我們首先考慮到什麼,難道是external的問題嗎,你會發現跟它一點關係沒有,難道我們每次寫還要匯入第三方類庫嗎,解決方案

ProvidePlugin

   new webpack.ProvidePlugin({
            'React': 'react',
            'ReactDOM': 'react-dom'
        })

這樣就解決了這個問題,我們看下構建效果

 

 

 同樣也達到了我們的目的

方案三

方案三採用optimization分離,其實與多頁面分離第三方與common部分的用法是一樣的,我們就跟多頁面一起具體了。

多頁面解決方案

基本配置

const path = require('path');
const webpack = require('webpack');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const { CleanWebpackPlugin } = require('clean-webpack-plugin')
const HtmlWebpackPlugin = require('html-webpack-plugin');

module.exports = {
    mode: 'development',
    entry: {
        app1: './app1.js',
        app2: './app2.js'
    },
    output: {
        path: path.resolve(__dirname, './build/'),
        filename: "[name]-[chunkhash].js"
    },
    devtool: "source-map",
    module: {
        rules: [
            {
                test: /\.less$/,
                use: [
                    MiniCssExtractPlugin.loader,
                    {
                        loader: 'css-loader',
                    },
                    'less-loader',

                ],
                exclude: /node_modules/
            },
            {
                test: /\.js$/,
                use: [
                    'cache-loader',
                    {
                        loader: 'babel-loader',
                    }
                ],
                exclude: /node_modules/
            }
        ]
    },
    plugins: [
        new MiniCssExtractPlugin({
            filename: '[name].[hash:5].css',
        }),
        new CleanWebpackPlugin(),
        new HtmlWebpackPlugin({
            title: 'index1',
            template: './index1.html',
            filename: 'index1.html',
            chunks: ['app1', 'common'],
            // hash: true
        }),
        new HtmlWebpackPlugin({
            title: 'index2',
            template: './index2.html',
            filename: 'index2.html',
            chunks: ['app2', 'common'],
            // hash: true
        }),
        new webpack.HashedModuleIdsPlugin(),
    ],

}

打包效果

 

 

 問題很明顯,速度慢,體積過大,這裡還有個問題就是,app1的與app2引用共同的檔案導致的體積過大,也就是我們要解決的如何提取common部分的問題,這裡我們就不討論指令碼import 第三方的問題了,上面有解決方案了。

提取common部分

optimization: {
        runtimeChunk: {
            "name": "manifest"
        },
        splitChunks: {
            chunks: 'all',
            cacheGroups: {
                default: false,
                vendors: false,
                common: {
                    test: /\.(s*)js$/,
                    chunks: 'all',
                    minChunks: 2,
                    minSize: 0,
                    name: 'common',
                    enforce: true,
                    priority: -11
                },
                vendors: {
                    test: /[\\/]node_modules[\\/]/,
                    name: "vendors",
                    priority: -10,
                    chunks: 'all',
                    reuseExistingChunk: true,
                    enforce: true
                },
                style: {
                    name: 'style',
                    test: /\.less$/,
                    chunks: 'all',
                    enforce: true
                }
            }
        },
        runtimeChunk:{
            name:'manifest'
        }
    },

這裡我們要做的是,提供commonjs,合併css(提取common部分也是可以的,畢竟頁面也不可能用全部的css,做法與提取commonjs是一樣的,配置minChunks最小為2就可以了),提供第三方類庫。

我們看下打包效果

 

 

 速度提升了,同時生成了common檔案以及提三方檔案集合verndors檔案,嗯,目前解決了我們要解決的問題,上面單頁面場景同樣適用,瀏覽器快取這個一般不會變得vendors,也達到了提升效率問題,

但是有沒有想過一個問題,就是每一次webpack構建過程,是不是都要打一次這個包呢,浪費時間了吧,於是我們採用什麼方式呢,沒錯~採用DllPlugin與DllReferencePlugin來提取一次第三方,

剩下common部分每一次構建都去重新打一次。

程式碼同樣引用dll

 new webpack.DllReferencePlugin({
            context: __dirname,
            manifest: require('./distDll/dll/react.manifest.json')
        })

構建效果

 

 效果就是大幅度提升構建速度。

最終配置檔案

const path = require('path');
const webpack = require('webpack');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const { CleanWebpackPlugin } = require('clean-webpack-plugin')
const HtmlWebpackPlugin = require('html-webpack-plugin');

module.exports = {
    mode: 'development',
    entry: {
        app1: './app1.js',
        app2: './app2.js'
    },
    output: {
        path: path.resolve(__dirname, './build/'),
        filename: "[name]-[chunkhash].js"
    },
    devtool: "source-map",
    module: {
        rules: [
            {
                test: /\.less$/,
                use: [
                    MiniCssExtractPlugin.loader,
                    {
                        loader: 'css-loader',
                        // options: {
                        //     sourceMap: true,
                        //     modules: true,
                        //     localIdentName: '[name]---[local]---[hash:base64:5]'
                        // }
                    },
                    'less-loader',

                ],
                exclude: /node_modules/
            },
            {
                test: /\.js$/,
                use: [
                    'cache-loader',
                    {
                        loader: 'babel-loader',
                        options: {
                            // cacheDirectory: path.join(__dirname,'./build/', 'babel_cache')
                            // happyPackMode: true,
                            // transpileOnly: true
                        }
                    }
                ],
                exclude: /node_modules/
            }
        ]
    },
    optimization: {
        runtimeChunk: {
            "name": "manifest"
        },
        splitChunks: {
            chunks: 'all',
            cacheGroups: {
                default: false,
                vendors: false,
                common: {
                    test: /\.(s*)js$/,
                    chunks: 'all',
                    minChunks: 2,
                    minSize: 0,
                    name: 'common',
                    enforce: true,
                    priority: -11
                },
                // vendors: {
                //     test: /[\\/]node_modules[\\/]/,
                //     name: "vendors",
                //     priority: -10,
                //     chunks: 'all',
                //     reuseExistingChunk: true,
                //     enforce: true
                // },
                style: {
                    name: 'style',
                    test: /\.less$/,
                    chunks: 'all',
                    enforce: true
                }
            }
        },
        runtimeChunk:{
            name:'manifest'
        }
    },
    plugins: [
        new MiniCssExtractPlugin({
            filename: '[name].[hash:5].css',
        }),
        new CleanWebpackPlugin(),
        new HtmlWebpackPlugin({
            title: 'index1',
            template: './index1.html',
            filename: 'index1.html',
            chunks: ['app1', 'common'],
            // hash: true
        }),
        new HtmlWebpackPlugin({
            title: 'index2',
            template: './index2.html',
            filename: 'index2.html',
            chunks: ['app2', 'common'],
            // hash: true
        }),
        new webpack.HashedModuleIdsPlugin(),
        new webpack.DllReferencePlugin({
            context: __dirname,
            manifest: require('./distDll/dll/react.manifest.json')
        })
    ],

}

以上就是針對webpack構建優化全部總結,涉及第三方抽取,common抽取等問題。

求個關注~謝謝,麼麼噠~

&n