四时宝库

程序员的知识宝库

不忘初心,前端小伙伴选择Webpack的理由是啥?

如今,前端开发领域的变化用日新月异来形容,丝毫不过分,当然也可以换言之:“别更新了,臣妾真心学不动了~”

早在前端库和框架出现之前,“小伙伴们的生活”是如此朴实无华,过去......就只有一个简单的javascript file,而你不得不把它包含到html文件中,然后呢......完事儿!

在CSS中,多了些库和框架可以使用;JavaScript生态系统也是同样......我们已迎来了ES2020,甚至更高版本的标准,你懂的;过去几年,Angular、React、Vue三大框架也竞相角逐。

然后就是所谓的“module bundlers”,捆绑器或构建工具,包括webpack、browserify和gulp等等,进入主题!你是否想过我们的工具是如何变得越来越复杂哒?!以至于不得不使用诸如webpack之类的构建工具?


1 . 回看历史

1-1 混沌之初的万物

在库、框架和Webpack等构建工具不曾存在的那个“年代”,我们只需要在HTML中包含一个脚本即可,问题立马解决~ 如下:

<head> 
   <script src =“ main.js” type =“ text / javascript”> </ script> 
</ head>

后来有了库,这样我们不得不一个一个地以适当的顺序将它们纳入,使它们相互依赖进行工作:

<head>
   <script src="library1.js" type="text/javascript"></script>
   <script src="library2.js" type="text/javascript"></script>
   <script src="script.js" type="text/javascript"></script>
</head>

再有后来......整个生态系统蓄势待发~添加了诸如Ember、Backbone、Meteor、Angular、React......Vue之类的框架,使事情变得有些复杂了,进而添加一些不断推陈出新的JS标准,例如ES6、ES7及更高版本。

那么才到了构建工具上,如browserify、grunt、WebPack......甚至EsBuild,这样我们才混到了今天的一脸懵逼和怅然若失境界......

var webpack = require("webpack");
var path = require("path");
var HtmlWebpackPlugin = require("html-webpack-plugin");
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;

module.exports = {
    entry: {
        bundle: './src/index.js'
    },
    output: {
        path: path.join(__dirname, "../dist"),
        //note: we changed `bundle` name into a variable `[name]` to get the key values in `entry` property instead of declaring the name statically.
        //[chunkhash] - this is a large string of characters that uses hash. If vendor or javascript files were updated, webpack will automatically bundle the contents of the file then generate a different hash.
        filename: "[name].[chunkhash].js"
    },
    mode: "development",
    devtool: "inline-source-map",
    devServer: {
        proxy: {
            '/api': {
                target: "http://localhost:3000",
                secure: false,
                changeOrigin: true
            }
        }
    },
    module: {
        rules: [
            {
                use: {
                    loader: "babel-loader"
                },
                test: /\.js$/,
                exclude: /node_modules/ //excludes node_modules folder from being transpiled by babel. We do this because it's a waste of resources to do so.
            },
            {
                use: ['style-loader', 'css-loader'],
                test: /\.css$/
            }
        ]
    },
    plugins: [
        //`manifest` - Gives the browser a better understanding that tells whether the vendor file has actually got changed.
        // new webpack.optimize.CommonsChunkPlugin({
        //     names: ['vendor', 'manifest']
        // }), //We need to include this plugin so that it never duplicates the libraries that were included in `vendor.js` within `bundle.js` as well
        new HtmlWebpackPlugin({
            template: 'src/index.html'
        }), //this plugin is responsible for injecting the entry scripts of webpack (such as `bundle.js` and `vendor.js`) inside the html file without specifying them manually.        
        new webpack.DefinePlugin({
            'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV) //we will set the correct variable for `process.env.NODE_ENV` variable inside the `scripts` property in `package.json`
        }), //This adds windows-scoped variables that will be defined in bundle.js
        // new BundleAnalyzerPlugin()
    ],
    optimization: {
        splitChunks: {
            chunks: 'async',
            minSize: 30000,
            maxSize: 0,
            minChunks: 1,
            maxAsyncRequests: 5,
            maxInitialRequests: 3,
            automaticNameDelimiter: '~',
            name: true,
            cacheGroups: {
                commons: {
                    test: /[\\/]node_modules[\\/]/,
                    name: 'vendors',
                    chunks: 'all'
                },
                default: {
                    minChunks: 2,
                    priority: -20,
                    reuseExistingChunk: true
                }
            }
        }
    }
};


var webpack = require("webpack");
var path = require("path");
var HtmlWebpackPlugin = require("html-webpack-plugin");
const CompressionPlugin = require("compression-webpack-plugin");

module.exports = {
    entry: {
        bundle: './src/index.js'
    },
    output: {
        path: path.join(__dirname, "../dist"),
        //note: we changed `bundle` name into a variable `[name]` to get the key values in `entry` property instead of declaring the name statically.
        //[chunkhash] - this is a large string of characters that uses hash. If vendor or javascript files were updated, webpack will automatically bundle the contents of the file then generate a different hash.
        filename: "[name].[chunkhash].js"
    },
    mode: "production",
    module: {
        rules: [
            {
                use: {
                    loader: "babel-loader"
                },
                test: /\.js$/,
                exclude: /node_modules/ //excludes node_modules folder from being transpiled by babel. We do this because it's a waste of resources to do so.
            },
            {
                use: ['style-loader', 'css-loader'],
                test: /\.css$/
            }
        ]
    },
    plugins: [
        new HtmlWebpackPlugin({
            template: 'src/index.html'
        }), //this plugin is responsible for injecting the entry scripts of webpack (such as `bundle.js` and `vendor.js`) inside the html file without specifying them manually.
        new webpack.DefinePlugin({
            'process.env.NODE_ENV': JSON.stringify('production') //we will set the correct variable for `process.env.NODE_ENV` variable inside the `scripts` property in `package.json`
        }) //This adds windows-scoped variables that will be defined in bundle.js
    ],
    optimization: {
        minimize: true,
        splitChunks: {
            chunks: 'async',
            minSize: 30000,
            maxSize: 0,
            minChunks: 1,
            maxAsyncRequests: 5,
            maxInitialRequests: 3,
            automaticNameDelimiter: '~',
            name: true,
            cacheGroups: {
                commons: {
                    test: /[\\/]node_modules[\\/]/,
                    name: 'vendors',
                    chunks: 'all'
                },
                default: {
                    minChunks: 2,
                    priority: -20,
                    reuseExistingChunk: true
                }
            }
        }
    }
};

必须承认,当年我及小伙伴们开始了解、阅读这些堆栈时,也是略显胆怯,直到稍微缓缓神,

注意,未完......


2 . 什么是Webpack

首先,根据官方网站,Webpack是用于现代JavaScript应用的静态模块捆绑器,它构建了一个依赖图(dependency graph );无论JavaScript文件相互依赖时,它们各自位于何处,这个图用于将javascript模块捆绑为一体。

简而言之,Webpack是一个捆绑器,负责捆绑所有JavaScript文件以促成它们正常工作。

2-1 Webpack救民于水火

从前面的1-1,你知道了我们是怎么样成功“作茧自缚”的,那么Webpack像超人一样来解救我们了!构建webpack之类的工具可以解决哪些问题?

如下是Webpack试图解决的问题:

2-1-1 自动化

你或许不太喜欢把每个javascript库加入到HTML的headers中,但却喜欢使用npm把要在功能中使用到的这些库纳入进去。

Webpack能够做到这一点,我们只需要从npm安装所需选入的任何库,然后webpack会自动在bundle中包含该库(前提是的确在模块中会使用它们)。

2-1-2 加载速度

如果我们大力创建一个现代的Web应用,那么在网页中加载单个脚本的成本非常高。

仍旧是Webpack!它通过把我们拥有的每个javascript模块捆绑在一起,从而为我们提高加载速度,这是因为从webserver获取脚本时只请求一次;其原理是——与webserver去请求一个大文件相比,一个接一个地获取javascript文件会给Web服务器施加更多压力。

2-1-3 按需加载(必要脚本)

通常情况下,你开发的应用都会去加载所拥有的每个javascript模块和库,不管这些功能是否需要;但是万一在某些条件下,特定功能或模块中根本不需要某些库?

查看控制台中以下图表:

上图中红色表示我们应用中未使用脚本的百分比情况,绿色的表示正在使用该脚本。那么思考一下......假设我们有更多未被使用的脚本,这些脚本在加载期间会不必要地加载,这应该不是你希望看到的~

webpack提供了一种称为“代码拆分”的功能帮助我们解决该问题;它的原理是,我们实际上可以选择仅在应用需要时才“按需”或“异步”加载单个脚本,从而通过以极快的速度来加载,为我们的应用提供了相对的性能优势。

由于“代码拆分”,我们不再需要加载不必要的脚本,这些脚本只会在用户不使用某些特定模块或功能时妨碍性能。

有关代码拆分的更多信息,不赘述,可访问webpack的官方文档:

相关链接:https://webpack.js.org/guides/code-splitting/

2-1-4 依赖问题

Webpack受欢迎的另一个原因是,它解决了构建javascript应用时的常见问题:依赖性。

如前所述,在webpack出现之前,我们通常以正确的顺序排列脚本和库以正确连接依赖项:

<script src="moment.min.js"></script>
<script src="typeahead.min.js"></script>
<script src="jquery.min.js"></script>  
<script src="otherplugins.min.js"></script>
<script src="main.js"></script>

虽然不能说不可以,但想一想,随着你所依赖的库列表的增多,会遇到一些依赖的问题,这是因为没有以正确的顺序导入脚本。当所依赖的库列表越来越广时,会遇到很多类似问题。

但是当我们开始使用webpack时,这迎刃而解!因为你会通过使用ECMAScript模块(ESM)提前知道在构建期间缺少哪些依赖关系,如下所示:

//helper.js
export const sqrt = Math.sqrt;
export function square(x) {
    return x * x;
}
export function diag(x, y) {
    return sqrt(square(x) + square(y));
}

//main.js
import { square, diag } from 'helper';console.log(square(11)); // 121
console.log(diag(4, 3)); // 5

这些导入和导出模块会被webpack自动检测到,并作为指示符,以确定哪些javascript模块将在捆绑包中被包含进去,这有效地消除对“以正确的顺序安排库以及确保正常运行”的焦虑感。


3 . 为什么学Webpack?

下面列举说说学习Webpack对前端开发者来说能够建立哪些工作优势:

3-1 更好地了解现代前端生态系统

你应该注意到,Angular、React和Vue早就依靠webpack来构建boilerplates或现成的应用。

boilerplates要依赖webpack的原因是,它像Angular一样包含许多模块和库,如上所述,Webpack使下载和包含模块的过程实现了自动化,因此它经常被使用在框架/库中。

因此学习Webpack并非仅仅可以使你受益于前面那些好处,而且你还能够了解前端的boilerplates如何在后台运行,从而了解现代的前端生态系统。

3-2 更高效地完成开发

webpack的“热模块更换”功能,让我们节省了时间。具体说,由于页面无需触发完全的重加载就可以将我们的更改表现在javascript代码上,这显然促进了工作效率的提升。

注意,不仅仅适用于javascript,你的CSS代码也可以通过在webpack配置中添加css加载程序,而从中受益。开发变得异常迅速,并减少了调试时页面在完全加载上所花费的时间。

3-3 更好地设置单页应用

当你学习webpack时,你将能够轻松设置单页应用,在React上尤为明显——因为webpack可以依此使用Babel之类的编译器,将JSX语法转换为可读的javascript代码。

3-4 全面控制构建系统

如需要在早期版本中转换ES6 +代码以使javascript代码与旧版浏览器相兼容,必要的话,你可以选择webpack所需的各种构建系统,例如通过webpack加载器使用babel或traceur,。


总结,

如果你已经了解并且会利用Webpack,那么我们已通过此文带你回归了过去,追忆往昔;但是如果你正打算入手,相信全文对你多少能有点帮助吧。

发表评论:

控制面板
您好,欢迎到访网站!
  查看权限
网站分类
最新留言
    友情链接