Writing Difference between Single File Application and Multi-File Application of webpack.config.js Based on vue+webpack

Keywords: Javascript Webpack Vue sass github

1. Preface

In recent days, some people have asked similar questions, that is, how to change the configuration of single-page applications to multi-file applications, or how to change the configuration of multi-file applications to single-file applications when building directories with vue and webpack. This situation, I have dealt with before, the company's colleagues taught me, I will write this article for this situation. If you feel that I did not write well enough and wrong, you are welcome to point out that we are making progress together.

2. description

  1. First of all, the vue and webpack versions I use are 2.x. Please note that the versions I use, especially the webpack versions, 1 and 2 are somewhat different.

  2. Then, I will not say much about the process of project construction. I have written articles before, and there are many good articles worth learning on the Internet. Next, I only focus on webpack.config.js configuration file, because when I do the project, the basic change is here. Although there are also changes in the writing of the project file, I believe that the change will not be difficult for everyone. If I really do not know how to start, I may write articles later.

3. Configuration of single file applications

Since there are so many single file applications written now, I'll start by placing the configuration file for the file application. The code is as follows.

let path = require('path');
let webpack = require('webpack');
/*
 html-webpack-plugin Plug-ins, plug-ins that generate HTML in webpack,
 See https://www.npmjs.com/package/html-webpack-plugin here.
 */
let HtmlWebpackPlugin = require('html-webpack-plugin');
/*
 A node module that retrieves a list of files based on pattern matching.
 Detailed usage of glob can be seen here - https://github.com/isaacs/node-glob
 */
let glob = require('glob');
/*
 webpack Plug-in unit
 */
let CommonsChunkPlugin = webpack.optimize.CommonsChunkPlugin;
let UglifyJsPlugin = webpack.optimize.UglifyJsPlugin;
let publicPath = '/dist/';
//IP address
let serverHost = getIPAdress();
let config = {
    //Entry file
    entry: {
        index: path.resolve(__dirname, 'src/js/page/index.js'),
        vendors: ['vue', 'vue-router','vue-resource','vuex','element-ui','element-ui/lib/theme-default/index.css'] // Documents that need to be packaged separately
    },
    //Export documents
    output: {
        path: path.join(__dirname, 'dist'), //Output directory configuration, template, style, script, image resource path configuration are relative to it
        publicPath: publicPath,                //Paths on server s corresponding to resources such as templates, styles, scripts, images, etc.
        filename: 'js/[name].js',            //The generation configuration of the main js corresponding to each page
        // Chunk Filename:'js/[name]. asyncChunk. js? [chunkhash]'//chunk generated configuration
        chunkFilename: 'js/[name].asyncChunk.js?'+new Date().getTime() //Configuration generated by chunk
    },
    module: {
        //loader
        rules: [
            {
                test: /\.vue$/,
                loader: 'vue-loader',
                options: {
                    loaders: {
                        scss: 'vue-style-loader!css-loader!sass-loader', // <style lang="scss">
                        sass: 'vue-style-loader!css-loader!sass-loader?indentedSyntax' // <style lang="sass">
                    }
                }
            },
            {
                test: /\.html$/,
                loader: "raw-loader"
            },
            {
                test: /\.css$/,
                loader: 'style-loader!css-loader'
            },
            {
                test: /\.js$/,
                exclude: /node_modules/,
                loader: "babel-loader",
                options: {
                    presets: ["es2015","stage-0"],
                    plugins: ['syntax-dynamic-import']
                }
            },
            {
                test: /\.scss$/,
                loader: 'style-loader!css-loader!sass-loader'
            },
            {
                test: /\.(eot|svg|ttf|woff|woff2)(\?\S*)?$/,
                loader: 'file-loader'
            },
            {
                //Picture loader, similar to file-loader, is more suitable for pictures. It can convert smaller pictures to base64 and reduce http requests.
                //With the following configuration, convert images smaller than 8192 byte to base64 code
                test: /\.(png|jpg|gif)$/,
                loader: 'url-loader?limit=8192&name=images/[hash].[ext]'
            }
        ]
    },
    //Plug-in unit
    plugins: [
        //Generate HTML files
        new HtmlWebpackPlugin({
            filename: path.resolve(__dirname, 'dist/html/index.html'), //Generated html storage path, relative to path
            template: path.resolve(__dirname, 'src/html/index.html'), //ejs template path, preferred with loader for processing
            inject: 'body',  //The location where js is inserted, true/'head'/'body'/false
            chunks: ['load', 'vendors', 'vendor1', 'vendor2', 'index'],
            hash: true
        }),
        //Extracting Common Modules
        new CommonsChunkPlugin({
            name: 'vendors', // Extract the common module and generate a chunk named `vendors'.
            //Name: ['vendors','vendor1','vendor2','load'], // extracts the common module and generates a chunk named `vendors'.
            minChunks: 2, //Minimum number of times common modules are used. Configuration 2, that is, the same module will be extracted as a common chunks only if it is referenced by two other pages at the same time.
            // children:true // If true, then all child dependencies of public components will be selected
        }),
        //In async chunk, find the module of multiplexing >= 2 times and extract it separately
        new CommonsChunkPlugin({
            async: 'lazy',
            minChunks: (module, count) => ( //Number of times the count module is reused
                count >= 2
            )
        }),
        new UglifyJsPlugin({ //Compressed code
            compress: {
                warnings: false,
                drop_debugger: true,
                drop_console: true
            },
            except: ['$super', '$', 'exports', 'require', 'define', 'module'] //Exclude keywords
        })
    ],
    //Using webpack-dev-server
    devServer: {
        contentBase: path.join(__dirname, "/"),
        host: serverHost,
        port: 9090, //Default 9090
        inline: true, //js changes can be monitored
        hot: true//Hot start
    },
    resolve: {
        alias: {
            vue: 'vue/dist/vue.js'
        },
        extensions:['.js','.scss','.vue','.json']// You can use import XX from'xx'grammar directly without suffixes
    }
};
module.exports = config;
/**
 * @description Get the local IP address
 * @returns {string|*}
 */
function getIPAdress() {
    let interfaces = require('os').networkInterfaces();
    for (let devName in interfaces) {
        let iface = interfaces[devName];
        for (let i = 0; i < iface.length; i++) {
            let alias = iface[i];
            if (alias.family === 'IPv4' && alias.address !== '127.0.0.1' && !alias.internal) {
                return alias.address;
            }
        }
    }
}

4. Configuration of multi-file applications

Many documents are not used very much now, mainly sometimes small projects responsible for some activities of the company will be used, the code is as follows.

let path = require('path');
let webpack = require('webpack');
/*
 html-webpack-plugin Plug-ins, plug-ins that generate HTML in webpack,
 See https://www.npmjs.com/package/html-webpack-plugin here.
 */
let HtmlWebpackPlugin = require('html-webpack-plugin');
/*
 A node module that retrieves a list of files based on pattern matching.
 Detailed usage of glob can be seen here - https://github.com/isaacs/node-glob
 */
let glob = require('glob');
/*
 webpack Plug-in unit
 */
let CommonsChunkPlugin = webpack.optimize.CommonsChunkPlugin;
let UglifyJsPlugin = webpack.optimize.UglifyJsPlugin;
let publicPath = '/dist/';
//Get all js scripts through the getEntry function
let jsEntries = getEntry('./src/js/page/*.js');
//IP address
let IPAddress = getIPAdress();
let serverHost = IPAddress;
let config = {
    //Entry file
    entry: jsEntries,
    // entry: {
    //     index:jsEntries,
    //     vendors: ['vue', 'vue-router','vue-resource'] // Documents that need to be packaged separately
    // },
    //Export documents
    output: {
        path: path.join(__dirname, 'dist'), //Output directory configuration, template, style, script, image resource path configuration are relative to it
        publicPath: publicPath,                //Paths on server s corresponding to resources such as templates, styles, scripts, images, etc.
        filename: 'js/[name].js',            //The generation configuration of the main js corresponding to each page
        chunkFilename: 'js/[id].chunk.js?[chunkhash]'   //chunk Generated configuration
    },
    module: {
        rules: [
            {
                test: /\.vue$/,
                loader: 'vue-loader',
                options: {
                    loaders: {
                        scss: 'vue-style-loader!css-loader!sass-loader', // <style lang="scss">
                        sass: 'vue-style-loader!css-loader!sass-loader?indentedSyntax' // <style lang="sass">
                    }
                }
            },
            {
                test: /\.html$/,
                loader: "raw-loader"
            },
            {
                test: /\.css$/,
                loader: 'style-loader!css-loader'
            },
            {
                test: /\.js$/,
                exclude: /node_modules/,
                loader: "babel-loader",
                options: {
                    presets: ["es2015","stage-0"],
                    plugins: ['syntax-dynamic-import']
                }
            },
            {
                test: /\.scss$/,
                loader: 'style-loader!css-loader!sass-loader'
            },
            {
                test: /\.(eot|svg|ttf|woff|woff2)(\?\S*)?$/,
                loader: 'file-loader'
            },
            {
                //Picture loader, similar to file-loader, is more suitable for pictures. It can convert smaller pictures to base64 and reduce http requests.
                //With the following configuration, convert images smaller than 8192 byte to base64 code
                test: /\.(png|jpg|gif)$/,
                loader: 'url-loader?limit=8192&name=images/[hash].[ext]'
            }
        ]
    },
    plugins: [
        new CommonsChunkPlugin({
            name: 'vendors', // Extract the common module and generate a chunk named `vendors'.
            //name: ['vendors', 'vendor1', 'vendor2', 'load'], // Extract the common module and generate a chunk named `vendors'.
            minChunks: 2, //Minimum number of times common modules are used. Configuration 2, that is, the same module will be extracted as a common chunks only if it is referenced by two other pages at the same time.
            // children:true  //If true, then all child dependencies of public components will be selected
        })
        new UglifyJsPlugin({ //Compressed code
            compress: {
                warnings: false,
                drop_debugger: true,
                drop_console: true
            },
            except: ['$super', '$', 'exports', 'require', 'define', 'module'] //Exclude keywords
        })
    ],
    //Using webpack-dev-server
    devServer: {
        contentBase: path.join(__dirname, "/"),
        host: serverHost,
        port: 9090, //Default 9090
        inline: true, //js changes can be monitored
        hot: true//Hot start
    },
    resolve: {
        alias: {
            vue: 'vue/dist/vue.js'
        },
        extensions:['.js','.scss','.vue','.json']// You can use import XX from'xx'grammar directly without suffixes
    }
};
//Get the names of all. html files in the directory
let tplPages = Object.keys(getEntry('./src/html/*.html'));
tplPages.forEach((pathname)=> {
    let conf = {
        filename: path.resolve(__dirname, 'dist/html/'+ pathname +'.html'), //Generated html storage path, relative to path
        template: path.resolve(__dirname, 'src/html/'+ pathname +'.html'), //ejs Template path, preferred with loader for processing
        inject: 'body',  //js Insert location, true/'head'/'body'/false
        chunks: ['load', 'vendors', 'vendor1', 'vendor2', 'index'],
        hash: true
    };
//If the file name matches the js corresponding to the entry file name (for example, index.html and index.js are matched, insert index.js into index.html; share.html and share.js are matched, insert share.js into share.html)
    if (pathname in config.entry) {
        conf.inject = 'body';
        conf.chunks = ['vendors', pathname];
        conf.hash = true;
    }
//Generate configuration stack
    config.plugins.push(new HtmlWebpackPlugin(conf));
});
module.exports = config;

/**
 * @description Get the local IP address
 * @returns {string|*}
 */
function getIPAdress() {
    let interfaces = require('os').networkInterfaces();
    for (let devName in interfaces) {
        let iface = interfaces[devName];
        for (let i = 0; i < iface.length; i++) {
            let alias = iface[i];
            if (alias.family === 'IPv4' && alias.address !== '127.0.0.1' && !alias.internal) {
                return alias.address;
            }
        }
    }
}
function getEntry(globPath) {
    //Get all files under the globPath path
    let files = glob.sync(globPath);
    let entries = {},
        entry, dirname, basename, pathname, extname;
    //loop
    for (let i = 0; i < files.length; i++) {
        entry = files[i];
        dirname = path.dirname(entry);//The name of the folder where the return path is located
        extname = path.extname(entry);//Returns the extended name of the specified file name
        /**
         * path.basename(p, [ext])
         * Returns the specified filename, which excludes the [ext] suffix string
         * path.basename('/foo/bar/baz/asdf/quux.html', '.html')=>quux
         */
        basename = path.basename(entry, extname);
        pathname = path.join(dirname, basename);//Path merging
        entries[basename] = entry;
    }
    //Return map=>{fileName: fileUrl}
    return entries;
}

5. Differential summary

One-to-one comparison, the difference is out, but the writing is quite different. The main differences are as follows

1. The difference between entry files is that a single page application entry file is an index.js ('src/js/page/index.js'). The entry file for multi-page applications is let jsEntries = getEntry('./src/js/page/*.js') for all pages that need to be used. (The getEntry method returns the names and paths of all.Js files in a directory. jsEntries is an array of objects containing the names and paths of all.Js files in the. / SRC / JS / page directory.)

2. In the configuration of multi-file applications, the plug-in HtmlWebpackPlugin is extracted and pushed once (config.plugins.push(new HtmlWebpackPlugin(conf)) to the configuration (config.plugins) while traversing getEntry('./src/html/*.html'). Why do you write this way? It should be clear how many entrance files you have to write this plug-in, how many times do you have to write this plug-in, how many times do you have to write new Html Web pack Plugin? If there are only one or two entrance files, two or three are good. If there are 100 entrance files, don't you write new Html Web pack Plugin 100 times in config.plugins, so it's convenient to traverse the title text.

epilogue

Okay. For single-file and multi-file applications, webpack.config.js is much the same and the difference is discussed here. If you think the article is not well written or wrong, you are welcome to point out. At the same time, I hope this article can help you!

Posted by phppssh on Sat, 15 Dec 2018 09:54:04 -0800