webpack深入学习,搭建和优化react项目。

搭建和优化webpack的react项目

参考教程: https://www.jianshu.com/p/04e436cf75ba


遇到问题:
1、webpack-dev-server 成功启动无渲染
看看html内dom节点是否挂载JSX,是否引入了打包好的js资源。
使用HtmlWebpackPlugin会自动把生成的bundle.js文件引入html内。

通常来说,每个入口会生成本入口的依赖树图和chunk文件,一个入口对应一个chunk,而一个chunk对应一个Bundle,某个入口打包出来的bundle也只是一个js文件。(当然,也不是说chunk和bundle一一对应,比如开启了sourceMap,就会打包生成一个chunk对应两个Bundle)

webpack的使用目的:将所有非js文件打包成可以使用js表示的文件。

    最后的最后,打包的目的都是为了让浏览器可以识别代码生成页面,所以(如果是打包一个完整项目)必须要把我们打包后生成的bundle.js文件引入html文件内,然后再用某些挂载方式展现在页面上。
    html文件是我们自己先生成的,比如react内的html文件中有个< div id=‘app’>< /div>,就在js内获取这个dom节点生成真实Dom信息。

我以前经常搞不懂,为什么一个入口为什么能生成好几个文件出来,这几个文件又是怎么区分的之类的。

  • bundle和chunk的区别
    暂时先给过去的自己下一个结论吧:(没有使用代码分割等等方式的时候,单纯的打包。)
    chunk是过程,bundle是结果。
    webpack通过入口模块分析依赖图,然后打包依赖模块生成chunk,一个入口生成一个chunk,多入口生成多个chunk,chunk是打包过程中的Modules集合。
    Bundle是打包结果最终输出的一个或者多个打包好的文件。
    chunk在构建完成后便呈现为bundle。

2、vendors和externals
这两个都是可以打包外部拓展依赖包的。比如react、loadash、antd这种第三方通用依赖,可以使用这两个,但是有什么区别呢?
externals:从外部环境中获取,完全不会打包,只留一个引用接口,一般用在组件上,比如这个组件要放在react\antd环境中,就可以使用externals。像现在我们要搭建一个react项目,它本身就是一个完整的项目了,不可能从外部获取依赖,所以不能用externals。

vendors:
详细可见:https://webpack.docschina.org/configuration/optimization/
在webpack的Optimization(优化)属性中会使用到的概念。
比如SplitChunksPlugin,代码分割工具,可以使用它将vendors第三方依赖分割出去,单独生成一个文件。
再比如CommonsChunkPlugin(webpack3.x),提取公共第三方依赖打包。可以生成一个公共vendor。
vendor的体积优化可以使用uglfyJS压缩。
(具体如何使用看下方,哈哈哈我以前用过vendor结果这一次仔细学习发现以前用错了。orz)

        new webpack.optimize.CommonsChunkPlugin({
     
            name: 'vendors', // 将公共模块提取,生成名为`vendors`的chunk
            chunks: ['index','list','about'], //提取哪些模块共有的部分
            minChunks: 3 // 提取至少3个模块共有的部分
        }),

3、优化webpack打包,关于文件过大等问题

优化webpack打包一是减少代码体积,二是加快打包速度。
减少代码体积:主要是将各种非首屏js代码、第三方包、样式文件等等从主文件中分离,主文件尽量只保留首屏加载时最需要的js代码,其他js文件分离出来懒加载,第三方包样式资源等作为静态资源也尽可能分出来,可以静态资源就可以放在CDN缓存、利用浏览器缓存加载,速度快很多。

两个比较有用的插件工具:

npm install -D clean-webpack-plugin webpack-bundle-analyzer
// 自动删除输出目录下的文件
const CleanWebpackPlugin = require(‘clean-webpack-plugin’);
// 可以优化打包体积,在打包结束的时候,会启动启动一个服务在浏览器查看打包的大小和包含的内容等
const BundleAnalyzerPlugin = require(‘webpack-bundle-analyzer’)

现在这里有一个足足有4M的main.js
webpack深入学习,搭建和优化react项目。_第1张图片
webpack深入学习,搭建和优化react项目。_第2张图片

1、路由懒加载,使用异步加载方式。

看看有哪些是不用首屏加载的,就异步加载了。具体原理什么的我另写了一篇懒加载相关的博客记录。
其实直接使用import(’’)就可以达到效果,但是也可以使用别人封装好的懒加载组件:react-loadable或React.lazy()

其实我们并不需要立马把全部模块加载,我们希望我们希望webpack打包的时候只打包我们会立马用到的代码,而不需要立马用到的模块,可以单独打包成一个js文件,当需要用到这个模块的时候才去加载这个模块的代码,这样可以减少我们第一次加载的bundle的文件提交,提高传输速度。
此时用ES6的模块懒加载就能实现我们的期望。

可能是我用的不熟,或者是因为打包的项目内容挺少的,我懒加载的那个测试文件才分出来2k,相比主文件的4M,有什么用啊orz。不过文件多的时候就要考虑这个了哈哈哈。

2、js处理:第三方依赖包过大,使用代码分割拆包。

antd和lodash的包怎么那么大?我明明是做了按需加载的啊,难不成失效了?。。。(后文找到解法了记录下下)

网上查的说法是可能多个文件依赖了相同的包,导致每一个文件都打包了多个重复的文件。
webpack3.x版本使用CommonsChunkPlugin合并包,现在使用代码分割SplitChunksPlugin,把共同引用的依赖合并成一个单独分出来。

1、首先,对于lodash, 原来是babel没有做按需加载处理,要知道我们的按需加载都是需要靠babel在背后含辛茹苦地识别转化的。所以,npm i babel-plugin-lodash -D,加上解析插件。
之后就可以了,我的main.js包减少了700多k,现在只有3.79M了。
webpack深入学习,搭建和优化react项目。_第3张图片
2、代码分割
可以看webpack 的教程怎么使用:https://webpack.docschina.org/plugins/split-chunks-plugin/

optimization优化属性(webpack4.x的代码压缩和拆包都在这里处理,这是和webpack3.x的不同)
使用优化属性里的splitChunks。它的功能是合并打包第三方依赖生成一个额外的vendor,vendor里包含了ndoe_modules里的依赖,然后使用 HtmlWebpackPlugin将生成的vendor文件生成script标签插入html生效。

splitChunks就算什么配置都不做它也是生效的,源于webpack有一个默认配置,这也符合webpack4的开箱即用的特性。
默认配置:

splitChunks: {
     
    // async表示只从异步加载得模块(动态加载import())里面进行拆分
    // initial表示只从入口模块进行拆分
    // all表示以上两者都包括
    chunks: "async",
    minSize: 30000,   // 大于30k会被webpack进行拆包
    minChunks: 1,     // 被引用次数大于等于这个次数进行拆分
    // import()文件本身算一个
    // 只计算js,不算css
    // 如果同时有两个模块满足cacheGroup的规则要进行拆分,但是maxInitialRequests的值只能允许再拆分一个模块,那尺寸更大的模块会被拆分出来
    maxAsyncRequests: 5,  // 最大的按需加载(异步)请求次数
    // 最大的初始化加载请求次数,为了对请求数做限制,不至于拆分出来过多模块
    // 入口文件算一个
    // 如果这个模块有异步加载的不算
    // 只算js,不算css
    // 通过runtimeChunk拆分出来的runtime不算在内
    // 如果同时又两个模块满足cacheGroup的规则要进行拆分,但是maxInitialRequests的值只能允许再拆分一个模块,那尺寸更大的模块会被拆分出来
    maxInitialRequests: 3,
    automaticNameDelimiter: '~', // 打包分隔符
    name:true,
    cacheGroups: {
     
        // 默认的配置
        vendors: {
     
            test: /[\\/]node_modules[\\/]/,
            priority: -10
        },
        // 默认的配置,vendors规则不命中的话,就会命中这里
        default: {
     
            minChunks: 2, // 引用超过两次的模块 -> default
            priority: -20,
            reuseExistingChunk: true
        },
    },
}
拆分策略 chunks: “all” | “async” | “initial”

需要对异步组件和同步组件同时拆分,采用chunks: "all"进行bundle的拆分。这意味着 chunk 可以在异步和非异步 chunk 之间共享。
如果项目中同步加载的组件chunk不大,可以不对同步加载组件进行拆分,使用chunks:async。
如果项目中异步加载的组件chunk不大,可以不对异步加载组件进行拆分,使用chunks:initial。
当然也可以混用,对于缓存组单独设置

我使用了"all"的拆分方式。哈哈哈哈令人吃惊,vendor竟有3.7M,原本的代码才70多Kb。
在这里插入图片描述
然后现在要对这整一个vendors再拆分,因为后面这个包会变得越来越大的。

  • UI组件库(antd)
  • 基础插件(react,react-dom,react-router-dom,mobx,axios等等)
    这些都是更新频率非常低,公用率高,提及大,所以单独抽取。只要这些包不更新,拆包的chunk文件名就不会变。就一直缓存在浏览器
splitChunks:{
     
 // ....
      cacheGroups: {
     
        defaultVendors: {
     
          test: /[\\/]node_modules[\\/]/,
          priority: -10,
          reuseExistingChunk: true,
          name: 'defaultVendors',
        },
        antdui: {
     
          priority: 2,
           name:"antdui",
          test: /[\\/]node_modules[\\/](antd)[\\/]/, //(module) => (/antd/.test(module.context)),
        },
        // 拆分基础插件
        basic: {
     
          priority: 3,
          name:"basic",
          test: /[\\/]node_modules[\\/](moment|react|react-dom|react-router|react-router-dom|mobx|mobx-react|axios)[\\/]/,
        },
        // 默认的配置,vendors规则不命中的话,就会命中这里
        default: {
     
          minChunks: 2,
          priority: -20,
          reuseExistingChunk: true,
        },
      },
  }

把vendors主要分成了3个部分,第一个是default,第二个是antd,第三个是react等等基础包。拆出去之后可以用浏览器缓存,cdn缓存等方式加载静态资源,会比放在一个bundle请求快很多。
webpack深入学习,搭建和优化react项目。_第4张图片

3、代码压缩

1、css使用mini-css-extract-plugin抽取css文件,再使用optimize-css-assets-webpack-plugin进行代码压缩。

关于antd的包大小。它确实是按需加载了,看看这里,它已经很努力地只引入了table和button这两个我按需加载的组件,占地方最大的竟然是样式文件。(拳头硬了)
webpack深入学习,搭建和优化react项目。_第5张图片

yarn add mini-css-extract-plugin optimize-css-assets-webpack-plugin -D

mini-css-extract-plugin
把js中import导入的样式文件,单独打包成一个css文件,结合html-webpack-plugin,以link的形式插入到html文件中。

注:

  1. 此插件不支持HMR,若修改了样式文件,是不能即时在浏览器中显示出来的,需要手动刷新页面。
  2. mini-css-extract-plugin 插件和style-loader冲突,使用之后把style-loader注释掉。
      {
     
        test: /\.css$/,
        use: [
          {
     
            loader: MiniCssExtractPlugin.loader,
          },
          // 'style-loader',
          'css-loader',
        ],
      },
      {
     
        test: /\.less$/,
        use: [
          {
     
            loader: MiniCssExtractPlugin.loader,
          },
          // 'style-loader',
          'css-loader',
          'less-loader',
        ],
      },

plugins:[new MiniCssExtractPlugin(), ......]

  optimization: {
     
    minimize: true,
    minimizer: [
	// ...
      new OptimizeCSSAssetsPlugin({
     
        cssProcessorOptions: {
     
          discardComments: {
      removeAll: true }, // 移除注释
        },
      }),
    ],
}

打包结果:
antdui分离,成功把antd包减小500多K。
webpack深入学习,搭建和优化react项目。_第6张图片
webpack深入学习,搭建和优化react项目。_第7张图片

但是报了一个warning:export ‘default’ (imported as ‘styles’) was not found in ‘./index.less’ (module has no exports)。
妈耶。我引入的styles模块名识别不到?npm run dev的服务器启起来都是空白的。
查了一下是引用文件路径的问题,参考https://blog.csdn.net/qq_37431622/article/details/108045465
改成这样就好了。

      {
     
        test: /\.css$/,
        use: [
          {
     
            loader: MiniCssExtractPlugin.loader,
            options: {
     
              esModule: false,
              publicPath: '../',
            },
          },
          // 'style-loader',
          'css-loader',
        ],
      },
      {
     
        test: /\.less$/,
        use: [
          {
     
            loader: MiniCssExtractPlugin.loader,
            options: {
     
              esModule: false,
              publicPath: '../',
            },
          },
          // 'style-loader',
          'css-loader',
          'less-loader',
        ],
      },
2、js使用Uglifyjs,Uglifyjs还会进行Tree-shaking剔除无用代码。

npm i uglifyjs-webpack-plugin -D

// 在minimizer属性内添加
// 自定义js优化配置,将会覆盖默认配置
new UglifyJsPlugin({
     
    parallel: true,  //使用多进程并行运行来提高构建速度
    sourceMap: false,
    uglifyOptions: {
     
        warnings: false,
        compress: {
     
            unused: true,
            drop_debugger: true,
            drop_console: true, 
        },
        output: {
     
            comments: false // 去掉注释
        }
    }
})

我的天啊orz这个压缩代码的效果好到逆天啊,一下子少了2M,现在只有1M了整个包。牛逼牛逼。
webpack深入学习,搭建和优化react项目。_第8张图片

CDN引入方式

可以看这位博主是怎么做的:https://segmentfault.com/a/1190000038180453。
我还没试过,有兴趣的以后可能会再补充实践一下吧哈哈。


最后总结

搭建:
1、正常的入口、出口、常用loader配置,配置webpack-dev-server、热加载、sourceMap。
2、index.html,使用html-webpack-plugin插件。

优化总共经历的过程大概是:
1、路由懒加载、异步加载代码,减少主bundle代码体积。
2、对于js文件,代码拆分,把第三方依赖vendor单独拆出来。生成静态资源包。
3、压缩代码,抽取css、对css代码压缩,压缩js代码。


相关实验项目git地址,可以看到完整的webpack.config配置:
https://github.com/3sang/Dodo-webpack-practice

你可能感兴趣的:(前端)