浅谈一个webpack构建速度优化误区

问题描述

项目中使用了一个npm包a。前几天一直用得好好的,突然某次拉了别的分支代码后,就出Bug了。

第一反应是别人把这个包的版本变了。查看了下项目的package.jsonpackage-lock.json文件,该模块和依赖模块的信息并没有改变,node_modules/a中的版本信息也和package.json中的对应。

一下子没了头绪,只好到node_modules中去调试一下。

TL;DR;

拉到最后看总结 XD

node_modules目录结构

项目中node_modules目录如下:

node_modules
│
└───a
│  │  index.js
|  |  ...
│  │
│  └───node_modules
│    │  ...
│    └───c
|      |  index.js
|      |  ...
│
└───c
  │  index.js
  │  ...

从该目录结构中可以发现,模块a的目录下还有一个node_modules目录,这个目录里放的是模块a的依赖。眼尖的同学可能发现了,项目本身的node_modules目录和a模块的node_modules目录中都有安装了模块c。这是为什么呢?

原因有2个:

  1. 项目直接依赖了模块c
  2. 项目没有直接依赖模块c,但是项目直接依赖的模块b中依赖了模块c,并且和a中依赖的模块c版本不兼容。

我们的项目中并没有直接引用模块c,所以是第二种情况。

npm的模块安装机制

本节主要解释为什么项目没有直接依赖模块c,却会把c安装在项目的node_modules目录下。不感兴趣的同学可以直接跳过。

假设项目依赖了模块a和模块b,模块a依赖模块c的1.0.0版本,模块b依赖模块c的2.0.0版本。

npm2

在npm2的时候,使用嵌套的方式来安装模块,c模块分别被安装到a和b模块的node_modules目录中。

这种方式虽然简单,但是却会导致node_modules中存在大量相同的模块。想象一下,如果模块a和模块依赖的模块c都是1.0.0版本,使用这种方式就会产生冗余的模块。

npm3

到npm3的时候,npm2中产生冗余模块的情况得到改善。npm安装模块时会尽量把模块安装到最外层的node_modules目录中,让模块能够尽量被复用。

  1. 安装模块时,如果外层node_modules目录中没有同名模块,就将其安装到最外层ndoe_modules目录中
  2. 如果外层node_modules目录中已经存在了同名模块,并且版本兼容,则不再安装(使用时直接使用外层模块)
  3. 如果外层node_modules目录中已经存在了同名模块,并且版本不兼容,则安装在父模块的node_modules目录中

上述情况的安装模块如图

引用了错误的模块

node_modules/a/node_modules/c/index.js中加了一些log,发现居然没执行!?

到这一步,要么是log的位置没写对,要么是没有引入这个模块。确认了webpack配置中的resolve.mainFields属性和模块c的package.json文件信息后,排除了第一种可能。

Tips: resolve.mainFields属性用来告诉webpack,引入一个npm模块时,如何找到这个模块的入口。

这时候已经有点懵了,引用模块时不是先从当前目录下的node_modules目录中开始一级一级向上查到吗?说到向上查找,那便到上一级的node_modules目录中去试一试。

果然!引用的是最外层node_modules中的模块c!

难道webpack查找模块的方式和Node.js不一样吗?还是因为webpack的某些配置导致的?

使用如下webpack配置来构建,发现并没有存在上述问题。

const path = require('path')
module.exports = {
 entry: './src/index.js',
 output: {
  filename: 'main.js',
  path: path.resolve(__dirname, './dist/js')
 },
};

那接下来只要排查到底是哪些webpack配置影响到模块检索。查看项目中的webpack配置,和模块检索相关的只有resolve属性。

const config = {
 resolve: {
  modules: [
    path.resolve(projectDir, 'src'),
    path.resolve(projectDir, 'node_modules'),
    path.resolve(imtPath, 'node_modules'),
  ],
  // es tree-shaking
  mainFields: ['jsnext:main', 'browser', 'main'],
  alias: {},
  extensions: ['.jsx', '.js'],
 }
}

所幸配置不多,对着webpack文档查一下,很快便找到了问题:resolve.modules中使用了绝对路径。以下为webpack文档原文:

告诉 webpack 解析模块时应该搜索的目录。

绝对路径和相对路径都能使用,但是要知道它们之间有一点差异。

通过查看当前目录以及祖先路径(即 ./node_modules, ../node_modules 等等),相对路径将类似于 Node 查找 'node_modules' 的方式进行查找。

使用绝对路径,将只在给定目录中搜索。

上述webpack配置中,path.resolve(projectDir, 'node_modules')为项目的node_modules目录。这样配置的原因,是因为想要优化模块检索的速度,结果却导致了这么严重的Bug。

根据webpack文档,就是因为这个绝对路径导致了Bug。那么只要把这个绝对路径换成node_modules,Bug便解决了。

总结

npm在安装模块时,会优先将包安装在node_modules目录的最外层,除非有冲突才会安装到父模块下的node_modules中。而webpack配置中的resolve.modules设置成项目node_modules目录的绝对路径时,会导致webpack在查找node_modules目录时,只在最外层目录查找,忽略掉更深层次的同名模块。这与默认的查找策略“优先使用深层模块”相反,导致构建时使用了错误的npm包。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。

(0)

相关推荐

  • 详解组件库的webpack构建速度优化

    背景 在公司的主要工作是组件库(基于vue的ui组件库,类似element-ui)的开发,也已经有两个多月,期间一直觉得项目的开发构建太慢,每次开发打开开发环境需要 40s 左右,简直不能忍.前前后后尝试了各种优化手段,但是都不理想.终于在今天,找到了问题所在,构建速度提升了 50% 以上,现在只需要 17s 左右,整个心情都好了.现在记录一下所用到的各种优化手段,因为是开发环境,所以只考虑构建速度. 各种配置项的优化 主要是对一些loader添加 include exclude之类的小优化,其

  • 浅谈webpack性能榨汁机(打包速度优化)

    最近对项目的本地开发环境进行了打包速度优化,原有项目,网上能搜到的优化方案基本都加了,在16年低配mac pro 上打包时间为25秒多,但我发现细节做一些调整可能大大降低打包时间,最终优化到7秒多 dll 原有项目是线上和本地公用一套dll配置,因为antd这类ui库需要按需加载所以不能放到dll中,这时可以单独写一个dll配置,将所有第三方库添加到dll中. 这时因为.babelrc中添加了babel-plugin-import插件会导致优化不生效,所以需要对开发环境单独配置babel opt

  • 性能优化篇之Webpack构建速度优化的建议

    如何输出Webpack构建分析 输出Webpack构建信息的.json文件:webpack --profile --json > stats.json --profile:记录构建中的耗时信息 --json:以json格式输出构建结果,最后只输出一个json文件(包含所有的构建信息) web可视化查看构建分析:得到了webpack构建信息文件stats.json,如何进行很好的可视化查看? 方案一:通过可视化分析工具Webpack Analyse,是个在线Web应用,上传stats.json文件

  • 浅谈一个webpack构建速度优化误区

    问题描述 项目中使用了一个npm包a.前几天一直用得好好的,突然某次拉了别的分支代码后,就出Bug了. 第一反应是别人把这个包的版本变了.查看了下项目的package.json.package-lock.json文件,该模块和依赖模块的信息并没有改变,node_modules/a中的版本信息也和package.json中的对应. 一下子没了头绪,只好到node_modules中去调试一下. TL;DR; 拉到最后看总结 XD node_modules目录结构 项目中node_modules目录如

  • 浅谈React + Webpack 构建打包优化

    本文介绍了React + Webpack 构建打包优化,分享给大家,具体如下: 使用 babel-react-optimize对 React 代码进行优化 检查没有使用的库,去除 import 引用 按需打包所用的类库,比如 lodash . echart 等 lodash 可以采用babel-plugin-lodash进行优化. 需要注意的是 在 babel-react-optimize 中使用了 babel-plugin-transform-react-remove-prop-types 这

  • 浅谈Linux环境下gcc优化级别

    代码优化可以说是一个非常复杂而又非常重要的问题,以笔者多年的linux c开发经验来说优化通常分为两个方面,一是人为优化,也就是基于编程经验采用更简易的数据结构函数等来降低编译器负担,二是采用系统自带的优化模式,也就是gcc - o系列,下面我将简述一下各级优化的过程以及实现. gcc - o1 首先o1上面还有一个o0,那个是不提供任何优化,项目中几乎不会使用,而o1使用就非常广泛了,o1是最基本的优化,主要对代码的分支,表达式,常量来进行优化,编译器会在较短的时间下将代码变得更加短小,这样体

  • 浅谈DataFrame和SparkSql取值误区

    1.DataFrame返回的不是对象. 2.DataFrame查出来的数据返回的是一个dataframe数据集. 3.DataFrame只有遇见Action的算子才能执行 4.SparkSql查出来的数据返回的是一个dataframe数据集. 原始数据 scala> val parquetDF = sqlContext.read.parquet("hdfs://hadoop14:9000/yuhui/parquet/part-r-00004.gz.parquet") df: or

  • 浅谈用Webpack路径压缩图片上传尺寸获取的问题

    问题的起因是因为的我的图片大小大于url-loader 的尺寸标准,导致webpack自动将图片的路径做了压缩处理,直接导致了我在获取dom的value的时候无法正确的获取到图片的正确路径. 直接上解决的方法. picUpload(e) { let image = new Image(); const reader = new FileReader(); const $img = e.target.files[0]; const formData = new FormData(); formDa

  • 浅谈一个基础的SpringBoot项目该包含哪些

    前言   建立一个全新的项目,或者把旧的庞大的项目,进行拆分成多个项目.在建立新的项目中,经常需要做一些重复的工作,比如说拷贝一下常用的工具类,通用代码等等.所以就可以做一个基础的项目方便使用,在经历新项目的时候,直接在基础项目上进行简单配置就可以开发业务代码了. 基础项目该包含哪些东西. Swagger在线接口文档. CodeGenerator 代码生成器. 统一返回. 通用的分页对象. 常用工具类. 全局异常拦截. 错误枚举. 自定义异常. 多环境配置文件. Maven多环境配置. 日志配置

  • 浅谈webpack 构建性能优化策略小结

    背景 如今前端工程化的概念早已经深入人心,选择一款合适的编译和资源管理工具已经成为了所有前端工程中的标配,而在诸多的构建工具中,webpack以其丰富的功能和灵活的配置而深受业内吹捧,逐步取代了grunt和gulp成为大多数前端工程实践中的首选,React,Vue,Angular等诸多知名项目也都相继选用其作为官方构建工具,极受业内追捧.但是,随者工程开发的复杂程度和代码规模不断地增加,webpack暴露出来的各种性能问题也愈发明显,极大的影响着开发过程中的体验. 问题归纳 历经了多个web项目

  • vue-cli2 构建速度优化的实现方法

    对于使用 vue-cli 脚手架创建的前端项目,编译发布几乎是必需操作,有的编译只需要几秒钟,快如闪电,有的却需要好几分钟,慢如蜗牛.如果是线上进行热修复,那更是分秒必争,网页响应的速度直接影响了用户体验,用户不会那么有耐心长时间等着,让你慢慢编译. 网上流传 vue-cli 一些优化配置,有些在新版本的 vue-cli 和 webpack3 已经不再需要了,有些是针对 webpack4 的.对于新版本的 vue-cli 和 webpack3,以下简单配置优化后,即可大幅提升构建速度. 按需引用

随机推荐