花了几年时间锁定了一个项目的古老技术堆栈,我终于有机会探索更多当前的框架,并在React和Webpack中使用。到目前为止,它在很大程度上是一次令人耳目一新的愉快体验,但我在Webpack遇到了一些困难,我希望蜂巢能有助于解决问题。
我一直在仔细阅读Webpack 2.0文档并且搜索得非常详尽,并且做得很短(我只是提出了this问题,这个问题很接近,但我和#39 ;我不确定是否适用)。缺乏信息使我认为我正在考虑两种情况之一:
......然而,我在这里。
我正在寻找的简短版本是基于环境变量排除某些JSX组件被包含在Webpack构建/包中的方法。目前,无论依赖树根据条目js forward中的imported
是什么样子应该是什么样的,Webpack似乎想要捆绑项目文件夹中的所有内容。
我认为这是默认行为,因为它有一定意义 - 应用程序可能需要处于初始状态以外的状态的组件。但在这种情况下,我们的应用程序'是完全无国籍的。
为了给出一些背景知识,这里有一些粗略的项目要求:
extract-text-webpack-plugin
从包中提取CSS - 在最终导出中的元素上没有直接内联样式,这是不可以更改的要求。sass-loader
传递,以便在编译CSS时使用。事情发生在这里:
我们说我有5个JSX组件,方便地标题为component_1
,component_2
,component_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_1
和component_2
page_2
包含component_3
,component_4
和component_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;
}
所以一般的流程是:
require
的对象,并使用组件名称作为键存储结果。layout
。所以,如果我只是遵循依赖树,这应该都可以正常工作。但是,无论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处理而不是?
答案 0 :(得分:0)
不要在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.js
,pageTwo.bundle.js
等等。
作为最后的评论:尽量不要过于聪明地使用动态导入(几乎完全避免在导入中使用表达式),但如果您决定制作单页应用程序并且只想加载必要的内容对于当前页面,您应该阅读Code Splitting - Using import()和Code Splitting - Using require.ensure,并使用react-router之类的内容。