无法让webpack完全忽略不必要的JSX组件

时间:2017-03-14 16:07:41

标签: sass webpack extract-text-plugin

花了几年时间锁定了一个项目的古老技术堆栈,我终于有机会探索更多当前的框架,并在React和Webpack中使用。到目前为止,它在很大程度上是一次令人耳目一新的愉快体验,但我在Webpack遇到了一些困难,我希望蜂巢能有助于解决问题。

我一直在仔细阅读Webpack 2.0文档并且搜索得非常详尽,并且做得很短(我只是提出了this问题,这个问题很接近,但我和#39 ;我不确定是否适用)。缺乏信息使我认为我正在考虑两种情况之一:

  1. 我想做的事情是疯了。
  2. 我想做的事情是基本的,以至于它应该是一个明智的选择。
  3. ......然而,我在这里。

    我正在寻找的简短版本是基于环境变量排除某些JSX组件被包含在Webpack构建/包中的方法。目前,无论依赖树根据条目js forward中的imported是什么样子应该是什么样的,Webpack似乎想要捆绑项目文件夹中的所有内容。

    我认为这是默认行为,因为它有一定意义 - 应用程序可能需要处于初始状态以外的状态的组件。但在这种情况下,我们的应用程序'是完全无国籍的。

    为了给出一些背景知识,这里有一些粗略的项目要求:

    • 项目包含一大堆组件,可以组装到页面中,并根据配置文件给出主题。
    • 这些组件中的每一个都包含自己的模块化CSS,用SASS编写。
    • 每个导出的页面都是静态的,实际上捆绑包将从导出目录中删除。 (是的,如果项目结束于简单地渲染一系列单个静态页面,则React有点过分。项目的未来状态将包括一个顶部的UI,React应该是一个非常合适的,但是 - 现在最好用JSX编写这些组件。)
    • 使用extract-text-webpack-plugin从包中提取CSS - 在最终导出中的元素上没有直接内联样式,这是可以更改的要求。
    • 作为页面主题信息的一部分,设置SASS变量,然后由SASS用于每个JSX组件。
    • 只编译给定页面配置文件中引用的JSX组件的SASS变量,并通过sass-loader传递,以便在编译CSS时使用。

    事情发生在这里:

    我们说我有5个JSX组件,方便地标题为component_1component_2component_3,依此类推。其中每个文件都有一个与之关联的匹配.scss文件,包含在以下方式中:

    import React from 'react';
    import styles from './styles.scss';
    
    module.exports = React.createClass({
    
      propTypes: {
        foo: React.PropTypes.string.isRequired,
      },
    
      render: function () {
    
        return (
          <section className={`myClass`}>
            <Stuff {...props} />
          </section>
        );
      },
    });
    

    现在让我们说我们有两页:

    • page_1包含component_1component_2
    • page_2包含component_3component_4component_5

    这些页面都是使用通用的layout组件构建的,它以这样的方式决定使用哪些块:

    return (
      <html lang='en'>
        <body>
          {this.props.components.map((object, i) => {
            const Block = component_templates[object.component_name];
            return <Block key={i}{...PAGE_DATA.components[i]} />;
          })}
        </body>
      </html>
    );
    

    上面遍历一个包含我所需的JSX组件的对象(对于给定的页面),现在以下列方式创建:

    load_components() {
      let component_templates = {};
      let get_component = function(component_name) {
        return require(`../path/to/components/${component_name}/template.jsx`);
      }
      for (let i = 0; i < this.included_components.length; i++) {
        let component = this.included_components[i];
        component_templates[component] = get_component(component);
      }
      return component_templates;
    }
    

    所以一般的流程是:

    1. 从页面配置中收集包含的组件列表。
    2. 创建一个对每个此类组件执行require的对象,并使用组件名称作为键存储结果。
    3. 单步执行此对象,并将每个JSX组件包含到layout
    4. 所以,如果我只是遵循依赖树,这应该都可以正常工作。但是,无论layout实际加载是什么,Webpack都会尝试包含所有组件。由于我只在页面上为实际使用的组件加载SASS变量,这导致Webpack在undefined variable时抛出sass-loader错误尝试处理与未使用的模块关联的SASS文件。

      这里需要注意的一点:静态页面呈现得很好,甚至可以在Webpack的开发服务器中运行......我只是遇到了大量错误而且Webpack引起了人们的注意。

      我最初的想法是,这个解决方案可以在我用于JSX文件的加载器的配置中找到,也许我只需要告诉加载器不是加载,如果Webpack试图加载所有内容。我正在使用`babel-loader:

      { test: /\.jsx?$/,
        loader: 'babel-loader',
        exclude: [
          './path/to/components/component_1/',
        ],
        query: { presets: ['es2015', 'react'] } 
      },
      

      exclude条目有新内容,似乎有效。

      这就是一种中篇小说,但我想在太多信息方面犯错,而不是缺乏信息。我错过了一些简单的东西,还是想做一些疯狂的事情?既?

      如何让未使用的组件被Webpack处理而不是

1 个答案:

答案 0 :(得分:0)

TL; DR

不要在require中使用表达式,并使用多个入口点,每页一个。

详细答案

  

Webpack似乎想要捆绑项目文件夹中的所有内容。

情况并非如此,webpack仅包含您导入的内容,这是静态确定的。事实上,许多对webpack不熟悉的人最初都感到困惑,因为webpack并不仅仅包含整个项目。但在你的情况下,你遇到了一种边缘情况。问题在于您使用require的方式,特别是此功能:

let get_component = function(component_name) {
  return require(`../path/to/components/${component_name}/template.jsx`);
}

您正在尝试根据函数的参数导入组件。 webpack如何在编译时知道应该包含哪些组件?好吧,webpack不进行任何程序流分析,因此唯一的可能性就是包含所有可能与表达式匹配的组件。

您很幸运,webpack允许您首先执行此操作,因为仅将变量传递给require将失败。例如:

function requireExpression(component) {
  return require(component);
}

requireExpression('./components/a/template.jsx');

在编译时会给你这个警告,即使很容易看到应该需要哪个组件(后来在运行时失败):

Critical dependency: the request of a dependency is an expression

但是因为webpack没有进行程序流分析,所以它没有看到它,即使它确实如此,你甚至可以在任何地方使用该功能,即使用户输入也是如此原因。

有关详细信息,请参阅官方文档的require with expression

  

Webpack正试图包含我们所有的组件,无论布局实际加载的是什么。

既然你知道为什么webpack需要所有组件,那么理解为什么你的想法不会成功,坦率地说,过于复杂也很重要。

如果我理解正确,你有一个多页面应用程序,每个页面应该有一个单独的包,它只包含必要的组件。但是你真正做的是在运行时只使用所需的组件,所以从技术上讲,你有一个包含所有页面的包,但你只使用其中一个,这对于单页应用程序(SPA)。不知何故,你希望webpack知道你正在使用哪一个,它只能通过运行才能知道,所以它在编译时肯定不知道。

问题的根源在于您在运行时(在执行程序时)做出决定,而这应该在编译时完成。解决方案是使用多个入口点,如Entry Points - Multi Page Application中所示。您需要做的就是单独创建每个页面并导入您实际需要的页面,而不是花哨的动态导入。因此,您需要配置webpack,每个入口点都是一个独立的包/页面,它将生成它们的相关名称(另请参阅output.filename):

entry: {
  pageOne: './src/pageOne.jsx',
  pageTwo: './src/pageTwo.jsx',
  // ...
},
output: {
  filename: '[name].bundle.js'
}

这很简单,它甚至可以简化构建过程,因为它会自动生成pageOne.bundle.jspageTwo.bundle.js等等。

作为最后的评论:尽量不要过于聪明地使用动态导入(几乎完全避免在导入中使用表达式),但如果您决定制作单页应用程序并且只想加载必要的内容对于当前页面,您应该阅读Code Splitting - Using import()Code Splitting - Using require.ensure,并使用react-router之类的内容。