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
在这里插入图片描述
在这里插入图片描述

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了。
在这里插入图片描述
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请求快很多。
在这里插入图片描述

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

关于antd的包大小。它确实是按需加载了,看看这里,它已经很努力地只引入了table和button这两个我按需加载的组件,占地方最大的竟然是样式文件。(拳头硬了)
在这里插入图片描述

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。
在这里插入图片描述
在这里插入图片描述

但是报了一个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
增加publicPath,改成下面代码这样就好了。
(后续:在使用时遇到import styles from './css/index.less';这种方式styles模块为空,样式无法获取,解决方法放在文章末尾补充)

      {
        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了整个包。牛逼牛逼。
在这里插入图片描述

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


最后提一个css module导入导出的问题。

在上面的配置过后,发现 import styles from './css/index.less'; 这种导入方式的styles内容为空。无法获取到css。
一开是我以为是mini-css-extract-plugin抽出styles的问题。
看了官网的配置增加了modules的配置,直接报styles为undefined。
然后我又把它注释掉了。

// 此时的相关配置
[...
    {
      loader: MiniCssExtractPlugin.loader,
      options: {
        esModule: false,
        // modules: {
        //   namedExport: true,
        // },
        publicPath: '../',
     },
   },
   'css-loader',
   // ...
]

import from './css/index.less';这种全局直接引入的是可以获取到样式的,但是没有类名编译,看过css modules相关的文章应该清楚没有编译过的类名默认是global,所以我猜测样式是没有经过modules处理?这个处理在哪里打开呢,然后我找到了css-loader里的配置。

之后我把css-loader的配置写成如下,可喜可贺import styles from './css/index.less';终于识别出来了,正确地打印出了样式。
然而又出现了两个bug:
1、类名没有标识,是hash字符串,有点看不懂。
2、编译后显示的antd-css文件也同样被处理了,antd样式全部失效。

  {
     loader: 'css-loader',
     options: {
     modules: true,
     }
  },

利用css-loader中的修改类名属性对node_modules内的文件和我们的文件加以区分。
在modules中加了一个getLocalIdent属性。
然后就好了。
不过看官网提到/\.module\.less$/这种匹配模块,其实umi的config配置里也有这么个匹配,似乎也是能解决问题的。

  • /\.module\.less$/问题参考博客记录:https://blog.csdn.net/u011423258/article/details/106263578
  • css-module问题:
    https://zhuanlan.zhihu.com/p/23602046
    https://blog.csdn.net/qq_38462207/article/details/82108345
      {
        test: /\.less$/,
        use: [
          {
            loader: MiniCssExtractPlugin.loader,
            options: {
              esModule: false,
              // modules: {
              //   namedExport: true,
              // },
              publicPath: '../',
            },
          },
          // 'style-loader',
          // 'css-loader',
          {
            loader: 'css-loader',
            options: {
              modules: {
                getLocalIdent: (context, localIdentName, localName, options) => {
                  if (
                    context.resourcePath.includes('node_modules') ||
                    context.resourcePath.includes('ant.design.pro.less')
                  ) {
                    return localName;
                  }
  
                  const match = context.resourcePath.match(/src(.*)/);
                  if (match && match[1]) {
                    const pathname = match[1].replace('.less', '');
                    const name = pathname.split('\\').join('-');
                    return `ddo-${name}-${localName}`;
                  }
                  return localName;
                },
              },
            },
          },
          'less-loader',
        ],
      },
  • 4
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 7
    评论
### 回答1: 学习 React 的最快方法是通过官方文档和教程来学习。你可以在 React 官网上找到大量的文档和教程。 首先,你可以先阅读 React 入门教程,了解 React 的基本原理和概念。然后,你可以深入学习 React 的核心概念,例如组件、状态和生命周期等。 此外,你还可以尝试完成一些 React 的练习题或者挑战题,以加强对 React 的理解和掌握。 最后,你可以尝试自己编写一个简单的 React 应用来练习所学的知识,或者参与一些开源项目来进一步提高自己的能力。 ### 回答2: 要快速学习React,可以按照以下步骤进行: 1. 先理解React的基本概念:React是一个用于构建用户界面的JavaScript库,支持组件化开发,通过使用虚拟DOM进行高效的渲染。初学者应该了解React的基本概念,如组件、状态、属性、生命周期等。 2. 学习JSX语法:JSX是React的核心语法,它允许我们在JavaScript中直接书写类似HTML的代码,进一步加强了React组件的可读性和可维护性。学习JSX语法后,可以更轻松地编写React组件。 3. 搭建开发环境:在学习React之前,需要安装并配置Node.js和npm(或者使用yarn)作为开发环境的基础。可以使用Create React App或者手动配置Webpack等工具来搭建React应用的开发环境。 4. 学习React生命周期:React组件有不同的生命周期方法,它们在组件的创建、更新和销毁的不同阶段被触发。了解React生命周期可以帮助我们更好地控制组件的行为和优化性能。 5. 组件化开发:React是基于组件化开发的思想,通过将一个复杂的UI划分为多个独立的、可复用的组件,可以更好地进行开发和维护。学习如何设计和使用React组件,以及组件之间的通信,是学习React的关键。 6. 学习React的状态管理:React的状态管理是通过组件的状态进行的。状态是组件中的数据,可以通过setState方法进行更新。学习如何管理组件的状态,以及使用React的Context或Redux等库进行更复杂的状态管理,能够提升React应用的可扩展性。 7. 掌握常用的React Hook:React Hook是React 16.8版本引入的新特性,它允许我们在函数式组件中使用状态和其他React特性,避免使用类组件的繁琐。掌握React Hook可以帮助我们更快速地开发React应用。 8. 实践和项目经验:最后,通过实践和参与真实的React项目,将所学知识应用到实际中,能够加深理解并快速成长。阅读官方文档、查看示例代码和参与开源项目都是不错的学习途径。 总之,快速学习React需要掌握React的基本概念、语法、开发环境,了解组件化开发和状态管理,以及掌握React Hook等新特性。通过实践和项目经验,不断提高实际应用的能力。 ### 回答3: 要快速学习React,可以按照以下步骤进行: 1. 了解基础知识:在开始之前,建议先了解JavaScript的基础知识,因为React是基于JavaScript的库。熟悉HTML和CSS也会对学习React有所帮助。 2. 学习React的核心概念:React有一些核心概念,比如组件、状态、生命周期等,需要理解并掌握它们的使用方法。可以通过阅读官方文档、教程和相关书籍来学习。 3. 上手实践:理论学习之后,通过实践来巩固所学知识。可以尝试搭建简单的React项目,并编写一些基础的组件。在实践过程中,会更加深入理解React的工作原理和使用方法。 4. 参与开源项目:在GitHub上可以找到很多开源的React项目,可以选择一些自己感兴趣的项目进行贡献。通过参与开源项目,可以学习到其他开发者的优秀代码实践,提高自己的编码能力。 5. 继续学习和实践:学习React是一个不断迭代的过程,可以通过阅读最新的React文档、博客和社区讨论来保持学习的动力。同时,不断实践和开发项目,将学到的知识应用到实际情境中。 总之,学习React需要投入时间和精力,理论与实践并重,通过不断学习和实践,才能掌握React的技能,快速成为React开发者。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值