webpack优化之code splitting

作为当前风头正盛的打包工具,webpack风靡前端界。确实作为引领了一个时代的打包工具,很多方面都带来了颠覆性的改进,让我们更加的感受到自动化的快感。不过最为大家诟病的一点就是用起来太难了。

要想愉快的使用,要使用n多的配置项,究其原因在于文档的不够详细、本身默认集成的不足。也不能说这是缺点吧,更多的主动权放给用户就意味着配置工作量的增加,这里就不过多探讨了。当历尽千辛万苦,你的项目跑起来之后,可能会发现有一些不太美好的问题的出现,编译慢、打包文件大等。那么,我们还要花些时间来看看怎么优化相关配置了。 下面一起看下code splitting

code splitting出现的背景

对于前端资源来说,文件体积过大是很影响性能的一项。特别是对于移动端的设备而言简直是灾难。此外对于某些只要特定环境下才需要的代码,一开始就加载进来显然也不那么合理,这就引出了按需加载的概念了。

为了解决这些情况,代码拆分就应运而生了。代码拆分故名思意就是将大的文件按不同粒度拆分,以满足解决生成文件体积过大、按需加载等需求。具体到webpack而言有下面几种方式来达到我们的目的。

webpack实现代码拆分的方式

webpack通过下面三种方式来达到以上目的

  1. Entry Points: 多入口分开打包
  2. Prevent Duplication:去重,抽离公共模块和第三方库
  3. Dynamic Imports:动态加载

    这里不去扒文档上的定义了,我们从一个例子中来逐步体会他们不同的作用。

假设我们有这么个项目,有下面几个文件

代码很简单(示例而已,直接用commonjs的语法来写了):

//a.js
var react = require(‘react‘)
var tool = require(‘./tool‘)
var b = require(‘./b‘)
function load(){
    b()
    tool()
    console.log(‘全部文件都从一个入口打包‘)
}
load()
//b.js
var react = require(‘react‘)
var tool = require(‘./tool‘)
function b(){
    tool()
    console.log(‘这是bjs文件‘)
}
module.exports = b;
//tool.js
var react = require(‘react‘)
function tool(){
    console.log(‘这是tooljs文件‘)
}
module.exports = tool;

配置很简单:

var webpack = require(‘webpack‘);module.exports = {
    entry: ‘./codesplitting/c1/a.js‘,
    output: {
        path: __dirname,
        filename: ‘/dist/index.js‘
    }
    //*****
}

直接打包:可以看到文件大小有2047行,体积也变大了

目前只引入了react,并且业务代码几乎没有的情况下。大家可以猜到实际项目中的情况了。来让我们进行第一优化

Entry Points

如果业务中的项目不是单页面应用,这一步可以忽略了,直接是多入口打包。这里是为了演示效果,强行分一个模块出来打包,假设我们的文件也很大,需要将b.js单独打个包出来:

    entry: {
        index:‘./codesplitting/c1/a.js‘,
        other:‘./codesplitting/c1/a.js‘
    },
    output: {
        path: path.resolve(__dirname, ‘./dist‘),
        filename: ‘[name].js‘
    },
    //***

这里a.js也需要修改,去掉对b的引用。入口文件之间不能相互引用的。不然,问题就大了,到底以谁为主呢,这样就陷入了循环引用的问题。

此时的生成文件如下:

看来文件竟然只小了那么一点了吧?第一步的优化这里就完成了,显然你会认为我在开玩笑。

当然这只是万里长征第一步,看一下dist下的文件不难发现两个文件中都把react这个第三方库和tool.js这个可复用模块打进去了,显然这样重复打包有点没必要。

是不是可以把这些复用性强的模块拿出来单独打包呢?

这样浏览器第一次请求之后就会将该文件缓存起来,从服务端请求的只有体积缩小之后的业务文件了,这样的话加载速度显然会有所提升。

如果你也是这么想的,来一起继续看下去。

Prevent Duplication

webpack去除重复引用是通过CommonsChunkPlugin插件来实现的。该插件的配置项如下:

{
    //被抽离为公共文件的chunk名,例如common,可以是string或者数组
    //显然如果是单个的模块,就是name多个就是names
    name:string,
    names:[],
    //打包之后公共模块的名称模板
    //例如‘[name].js‘
    //如果省略,则和name名称一致
    filename:string,
     //模块被引的最小次数,也就是说至少有几个组件引用了该模块。
    //如果是Infinity,则表明单纯的创建,并不做任何事情
    minChunks:2
}

具体在webpack中去重对于第三方库显示声明vendor,公共模块声明common的方式来处理

entry: {
        index:‘./codesplitting/c1/a.js‘,
        other:‘./codesplitting/c1/b.js‘,
        //第三方库显示声明
        vendor:[‘react‘],
        //公共组件声明为common
        common:[‘./codesplitting/c1/tool‘]
    },
    //***
    plugins: [
        new webpack.optimize.CommonsChunkPlugin({
            names:["common", "vendor"],
            filename: "[name].js"
        })
    ]

打包结果如下:

可以看到index和other两个业务包已经很小了,react被抽离到单独的包中。

这样还有一个问题,对于某些代码可能只有在特定条件下才执行,或者可能就不执行。

我不希望在首屏就去加载它,也就是我们常说的按需加载是要怎么做呢。一起看下去。

Dynamic Imports

webpack建议如下两种方式使用动态加载。

1)、ECMAScript中出于提案状态的import()

2)、webpack 特定的 require.ensure

我们这里就是用第二种来看下效果(毕竟偷懒没用babel...),在ajs中动态引入di.js

    //虽然始终会加载,大家能明白就行
    if(true){
        require.ensure([],function(require){
            var di = require(‘./di‘)
        })
    }
    //新增动态加载的js
    function di(){
        tool()
        console.log(‘这是动态引入的文件‘)
    }
    module.exports = di;

运行之后可以发现多了个2.2.js,打开可以发现就是我们新建的动态引入的di.js

大家可能会问怎么确定就是动态引入的呢,虽然本示例只能看打包之后的例子(就不引入dev server了,毕竟是懒。。。)我们依然可以从代码里看到结果。

首先、查看index.js文件,可以看到下面的代码:

      var react = __webpack_require__(2)
       var tool = __webpack_require__(1)
       /****省略8*****/
      //虽然始终会加载
        if(true){
            __webpack_require__.e/* nsure */(2, function(require){
                var di = __webpack_require__(13)
            })
        }

与直接require的模块不同,require.ensure被转化为了 webpack_require.e方法,来继续看一下该方法有什么用。

    __webpack_require__.e = function requireEnsure(chunkId, callback) {
        // "0" is the signal for "already loaded"
        if(installedChunks[chunkId] === 0)
            return callback.call(null, __webpack_require__);

        // an array means "currently loading".
        if(installedChunks[chunkId] !== undefined) {
            installedChunks[chunkId].push(callback);
        } else {
            // start chunk loading
            installedChunks[chunkId] = [callback];
            var head = document.getElementsByTagName(‘head‘)[0];
            var script = document.createElement(‘script‘);
            script.type = ‘text/javascript‘;
            script.charset = ‘utf-8‘;
            script.async = true;

            script.src = __webpack_require__.p + "" + chunkId + "." + ({"0":"common","1":"index","3":"other"}[chunkId]||chunkId) + ".js";
            head.appendChild(script);
        }
    };

结合注释直接从源码中可以看出来,最后面的条件语句来创建script标签进而实现动态加载的。所谓动态加载本质还是要创建script标签来实现的。

结束语

至此代码分割部分的优化已经完成了,以上是个人关于代码分割的简单理解,抛砖引玉,共同学习进步。更多请移步github查看

原文地址:https://www.cnblogs.com/pqjwyn/p/8064966.html

时间: 2024-10-08 22:19:52

webpack优化之code splitting的相关文章

[转] react-router4 + webpack Code Splitting

项目升级为react-router4后,就尝试着根据官方文档进行代码分割.https://reacttraining.com/react-router/web/guides/code-splitting 在实际项目中,js,css文件默认通过webpack打包的话会很大,动不动就好几兆. 在实际场景中,我们需要缩短首屏的时间展现时间,需要将 首屏没有 涉及到 其他页面的 业务和组件 进行代码分离,按需加载. 通过按需加载,如果只是修改了某个页面的逻辑,也不用整个项目文件加载,增加了浏览器缓存的利

webpack 和 code splitting

Code Splitting指的是代码分割,那么什么是代码分割,webpack和code splitting又有什么样的联系呢? 使用npm run dev:"webpack-dev-server --config ./build/webpack.dev.js.",会看不到打包生成的dist目录. 所以我们使用一个新的,不要启用dev-server服务.使用npm run dev-build:"webpack --config ./build/webpack.dev.js&q

[Webpack 2] Maintain sane file sizes with webpack code splitting

As a Single Page Application grows in size, the size of the payload can become a real problem for performance. In this lesson, learn how to leverage code splitting to easily implement lazy loading for your application to load only the code necessary

webpack - code splitting

Code splitting is one of the most compelling features of webpack. This feature allows you to split your code into various bundles which can then be loaded on demand or in parallel. It can be used to achieve smaller bundles and control resource load p

浅谈webpack优化

webpack优化方案 1. 优化开发体验 1-1. 加快构建速度 ① 缩小文件搜索范围 由于 Loader 对文件的转换操作很耗时,需要让尽可能少的文件被 Loader 处理,用include和exclude去缩小: resolve.modules用于配置 Webpack 去哪些目录下寻找第三方模块:[path.resolve(__dirname, 'node_modules')](根目录下): resolve.mainFields用于配置第三方模块使用哪个入口文件:['mian']: res

webpack优化 -- happypack

webpack优化 -- happypack 前言:happypack是一个可以开启多线程转换loader的插件,可以在开发环境下提高编译速度,下面用vue-cli 2.x配合happypack优化一下vue项目.?? 步骤 下载安装happypack 在本文中,vue-cli是2.x版本的(不是3.0版本),webpack是3.6.0,happypack是最新的5.0.1. //安装 npm install happypack -D 改造webpack 找到build/webpack.base

vuejs code splitting with webpack 3种模式

我们知道一个web app如果太大会严重影响用户的体验,如何能够最快速度地让用户看到完整页面是优化web应用需要做的重要工作. 这其中使用code split实现lazy加载,只让用户初次访问时只加载必须的html,css,javascrip是一个比较好的思路.那么到底什么情况下应该使用code split呢? 在vuejs app结合webpack工具链的开发中,至少有以下三种模式可能比较适合使用code split功能,实现lazy load. per page, below fold by

webpack 打包文件体积过大解决方案(code splitting)

优化对比 : 未优化前:index.html引入一个main.js文件,体积2M以上. 优化后入:index.html引入main.js.commons.js.charts.js.other.js.以达到将main.js平分目的.每个文件控制300k以内.(如果高兴100k也没问题) 用到的一堆库及工具: vue.webpack.babel.highcharts.echarts.jquery.html2canvas******此去省略若干m代码 问题: 开发环境用webpack后发现单个js文件

优化EF Code First第一次请求速度

由于EF Code First模式没有模型文件,所以很多一次请求的时候速度比较慢,EF需要将对应的数据库映射关系加载到内存里面,往后请求就比较快.可以通过在程序初始化的时候增加一段代码来优化EF第一次执行的速度,具体代码如下 //EF暖机操作 using (CompanyContext dbContext = new CompanyContext()) { var objectContext =((IObjectContextAdapter)dbContext).ObjectContext; v