本文概述
- 配置别名和相对路径
- 代码分割
- 将样式提取到单独的捆绑包中
- 处理第三方库和插件
- 配置外部依赖关系
- 支持库的多个实例
- 专注于开发, 而不是Webpack配置
在Node.js(当今最流行的JavaScript框架之一)中, 模块捆绑程序允许在Web浏览器中加载NPM模块, 而面向组件的库(如React)则鼓励并促进JavaScript代码的模块化。
Webpack是可用的模块捆绑器之一, 可将JavaScript代码以及所有静态资产(例如样式表, 图像和字体)处理到捆绑的文件中。处理可以包括用于管理和优化代码依存关系的所有必要任务, 例如编译, 串联, 最小化和压缩。
文章图片
但是, 配置Webpack及其依赖项可能会很麻烦, 而且并非总是一个简单的过程, 特别是对于初学者而言。
这篇博客文章提供了有关如何为不同场景配置Webpack的指南和示例, 并指出了与使用Webpack捆绑项目依赖项有关的最常见陷阱。
该博客文章的第一部分说明了如何简化项目中依赖项的定义。接下来, 我们讨论并演示用于多页和单页应用程序的代码拆分的配置。最后, 如果要在项目中包含第三方库, 我们将讨论如何配置Webpack。
配置别名和相对路径 相对路径与依赖关系不直接相关, 但是在定义依赖关系时会使用它们。如果项目文件结构复杂, 则可能很难解析相关的模块路径。 Webpack配置的最基本好处之一是, 它有助于简化项目中相对路径的定义。
假设我们具有以下项目结构:
- Project
- node_modules
- bower_modules
- src
- script
- components
- Modal.js
- Navigation.js
- containers
- Home.js
- Admin.js
我们可以通过所需文件的相对路径来引用依赖关系, 如果我们想将组件导入源代码中的容器中, 则如下所示:
Home.js
Import Modal from ‘../components/Modal’;
Import Navigation from ‘../components/Navigation’;
Modal.js
import {datepicker} from '../../../../bower_modules/datepicker/dist/js/datepicker';
每次我们要导入脚本或模块时, 都需要知道当前目录的位置, 并找到要导入的内容的相对路径。我们可以想象, 如果我们有一个带有嵌套文件结构的大项目, 或者想要重构复杂项目结构的某些部分, 那么这个问题将如何变得复杂。
我们可以使用Webpack的resolve.alias选项轻松解决此问题。我们可以声明所谓的别名-目录或模块的名称及其位置, 并且我们不依赖项目源代码中的相对路径。
webpack.config.js
resolve: {
alias: {
'node_modules': path.join(__dirname, 'node_modules'), 'bower_modules': path.join(__dirname, 'bower_modules'), }
}
现在, 在Modal.js文件中, 我们可以更简单地导入datepicker:
import {datepicker} from 'bower_modules/datepicker/dist/js/datepicker';
代码分割 在某些情况下, 我们需要将脚本附加到最终捆绑包中, 或者拆分最终捆绑包, 或者我们希望按需加载单独的捆绑包。针对这些场景设置我们的项目和Webpack配置可能并不容易。
在Webpack配置中, Entry选项告诉Webpack起点是最终捆绑包的位置。入口点可以具有三种不同的数据类型:字符串, 数组或对象。
如果我们只有一个起点, 则可以使用这些格式中的任何一种并获得相同的结果。
如果我们要追加多个文件, 并且彼此之间不依赖, 则可以使用数组格式。例如, 我们可以将analytics.js附加到bundle.js的末尾:
webpack.config.js
module.exports = {
// creates a bundle out of index.js and then append analytics.js
entry: ['./src/script/index.jsx', './src/script/analytics.js'], output: {
path: './build', filename: bundle.js '
}
};
管理多个入口点
假设我们有一个包含多个HTML文件的多页应用程序, 例如index.html和admin.html。我们可以通过使用入口点作为对象类型来生成多个包。以下配置生成两个JavaScript捆绑包:
webpack.config.js
module.exports = {
entry: {
index: './src/script/index.jsx', admin: './src/script/admin.jsx'
}, output: {
path: './build', filename: '[name].js' // template based on keys in entry above (index.js &
admin.js)
}
};
index.html
<
script src="http://www.srcmini.com/build/index.js">
<
/script>
admin.html
<
script src="http://www.srcmini.com/build/admin.js">
<
/script>
这两个JavaScript包都可以共享通用的库和组件。为此, 我们可以使用CommonsChunkPlugin, 它查找出现在多个条目块中的模块, 并创建可以在多个页面之间缓存的共享包。
webpack.config.js
var commonsPlugin = new webpack.optimize.CommonsChunkPlugin('common.js');
module.exports = {
entry: {
index: './src/script/index.jsx', admin: './src/script/admin.jsx'
}, output: {
path: './build', filename: '[name].js' // template based on keys in entry above (index.js &
admin.js)
}, plugins: [commonsPlugin]
};
现在, 我们不要忘记在捆绑的脚本之前添加< script src =http://www.srcmini.com/” build / common.js” > < / script> 。
启用延迟加载
Webpack可以将静态资产拆分为较小的块, 并且此方法比标准串联更灵活。如果我们有一个大型的单页应用程序(SPA), 将简单的串联连接到一个包中不是一个好方法, 因为加载一个庞大的包可能很慢, 并且用户通常不需要每个视图上的所有依赖项。
前面我们已经解释了如何将应用程序拆分为多个捆绑包, 连接常见的依赖关系以及如何从浏览器缓存行为中受益。这种方法对于多页应用程序非常有效, 但不适用于单页应用程序。
对于SPA, 我们应仅提供呈现当前视图所需的那些静态资产。 SPA架构中的客户端路由器是处理代码拆分的理想场所。当用户输入路线时, 我们只能加载结果视图所需的那些依赖项。另外, 我们可以在用户向下滚动页面时加载依赖项。
为此, 我们可以使用Webpack可以静态检测到的require.ensure或System.import函数。 Webpack可以基于此拆分点生成单独的捆绑包, 并根据需要调用它。
在此示例中, 我们有两个React容器;管理员视图和仪表板视图。
admin.jsx
import React, {Component} from 'react';
export default class Admin extends Component {
render() {
return <
div >
Admin <
/div>
;
}
}
dashboard.jsx
import React, {Component} from 'react';
export default class Dashboard extends Component {
render() {
return <
div >
Dashboard <
/div>
;
}
}
如果用户输入/ dashboard或/ admin URL, 则仅加载相应的必需JavaScript捆绑包。下面我们可以看到带有和不带有客户端路由器的示例。
index.jsx
if (window.location.pathname === '/dashboard') {
require.ensure([], function() {
require('./containers/dashboard').default;
});
} else if (window.location.pathname === '/admin') {
require.ensure([], function() {
require('./containers/admin').default;
});
}
index.jsx
ReactDOM.render(
<
Router>
<
Route path="/" component={props =>
<
div>
{props.children}<
/div>
}>
<
IndexRoute component={Home} />
<
Route path="dashboard" getComponent={(nextState, cb) =>
{
require.ensure([], function (require) {
cb(null, require('./containers/dashboard').default)
}, "dashboard")}}
/>
<
Route path="admin" getComponent={(nextState, cb) =>
{
require.ensure([], function (require) {
cb(null, require('./containers/admin').default)
}, "admin")}}
/>
<
/Route>
<
/Router>
, document.getElementById('content')
);
将样式提取到单独的捆绑包中 在Webpack中, 诸如样式加载器和css-loader之类的加载器会对样式表进行预处理, 并将其嵌入到输出JavaScript包中, 但是在某些情况下, 它们会导致未样式化内容(FOUC)的泛滥。
我们可以使用ExtractTextWebpackPlugin避免FOUC, 该方法允许将所有样式生成为单独的CSS包, 而不是将它们嵌入最终的JavaScript包中。
webpack.config.js
var ExtractTextPlugin = require('extract-text-webpack-plugin');
module.exports = {
module: {
loaders: [{
test: /\.css/, loader: ExtractTextPlugin.extract('style', 'css’)'
}], }, plugins: [
// output extracted CSS to a file
new ExtractTextPlugin('[name].[chunkhash].css')
]
}
处理第三方库和插件 很多时候, 我们需要使用第三方库, 各种插件或其他脚本, 因为我们不想花时间从头开始开发相同的组件。有许多可用的旧版库和插件未得到积极维护, 无法理解JavaScript模块, 并假定全局以预定义名称存在依赖项。
以下是jQuery插件的一些示例, 并说明了如何正确配置Webpack以便能够生成最终捆绑包。
ProvidePlugin
大多数第三方插件都依赖于特定的全局依赖项。对于jQuery, 插件依赖于定义的$或jQuery变量, 我们可以通过在代码中调用$(‘ div.content’ )。pluginFunc()来使用jQuery插件。
我们可以使用Webpack插件ProvidePlugin在每次遇到全局$标识符时在var $ = require(” jquery” )之前添加前缀。
webpack.config.js
webpack.ProvidePlugin({
‘$’: ‘jquery’, })
当Webpack处理代码时, 它将查找状态$, 并提供对全局依赖项的引用, 而无需导入require函数指定的模块。
进口装载机
一些jQuery插件在全局命名空间中采用$或依靠$作为窗口对象。为此, 我们可以使用imports-loader将全局变量注入模块。
example.js
$(‘div.content’).pluginFunc();
然后, 我们可以通过配置imports-loader将$变量注入模块:
require("imports?$=jquery!./example.js");
这只是为var $ = require(” jquery” ); 到example.js。
在第二个用例中:
webpack.config.js
module: {
loaders: [{
test: /jquery-plugin/, loader: 'imports?jQuery=jquery, $=jquery, this=>
window'
}]
}
通过使用=> 符号(不要与ES6 Arrow函数混淆), 我们可以设置任意变量。最后一个值重新定义此全局变量以指向窗口对象。这与使用(function(){… })。call(window); 包装文件的整个内容相同。并以window作为参数调用此函数。
我们还可以要求使用CommonJS或AMD模块格式的库:
// CommonJS
var $ = require("jquery");
// jquery is available// AMD
define([‘jquery’], function($) {
// jquery is available
});
一些库和模块可以支持不同的模块格式。
在下一个示例中, 我们有一个jQuery插件, 该插件使用AMD和CommonJS模块格式并具有jQuery依赖性:
jquery-plugin.js
(function(factory) {
if (typeof define === 'function' &
&
define.amd) {
// AMD format is used
define(['jquery'], factory);
} else if (typeof exports === 'object') {
// CommonJS format is used
module.exports = factory(require('jquery'));
} else {
// Neither AMD nor CommonJS used. Use global variables.
}
});
webpack.config.js
module: {
loaders: [{
test: /jquery-plugin/, loader: "imports?define=>
false, exports=>
false"
}]
}
我们可以选择特定库要使用的模块格式。如果我们声明define等于false, 则Webpack不会以AMD模块格式解析模块;如果我们将变量export声明为等于false, 则Webpack不会以CommonJS模块格式解析模块。
曝光装载机
如果需要将模块公开给全局上下文, 则可以使用Exposure-loader。例如, 如果我们拥有的外部脚本不是Webpack配置的一部分, 并且依赖于全局命名空间中的符号, 或者我们使用需要在浏览器控制台中访问符号的浏览器插件, 这可能会有所帮助。
webpack.config.js
module: {
loaders: [
test: require.resolve('jquery'), loader: 'expose-loader?jQuery!expose-loader?$'
]
}
jQuery库现在在全局名称空间中可用于网页上的其他脚本。
window.$
window.jQuery
配置外部依赖关系 如果要包括来自外部托管脚本的模块, 则需要在配置中定义它们。否则, Webpack无法生成最终捆绑包。
我们可以使用Webpack配置中的externals选项来配置外部脚本。例如, 我们可以通过单独的< script> 标记使用CDN中的库, 同时仍将其明确声明为项目中的模块依赖项。
webpack.config.js
externals: {
react: 'React', 'react-dom': 'ReactDOM'
}
支持库的多个实例 最好在前端开发中使用NPM软件包管理器来管理第三方库和依赖项。但是, 有时我们可以在同一个库中有多个实例, 它们具有不同的版本, 并且它们在一个环境中不能很好地协同工作。
例如, 这可能发生在React库中, 我们可以在该库中从NPM安装React, 之后可以通过一些附加的软件包或插件使用不同版本的React。我们的项目结构如下所示:
project
|
|-- node_modules
|
|-- react
|-- react-plugin
|
|--node_modules
|
|--react
来自react-plugin的组件与项目中的其余组件具有不同的React实例。现在我们有两个独立的React副本, 它们可以是不同的版本。在我们的应用程序中, 这种情况可能会使我们的全局可变DOM混乱, 并且我们可以在Web控制台日志中看到错误消息。解决此问题的方法是在整个项目中使用相同版本的React。我们可以通过Webpack别名解决它。
webpack.config.js
module.exports = {
resolve: {
alias: {
'react': path.join(__dirname, './node_modules/react'), 'react/addons': path.join(__dirname, '/node_modules/react/addons'), }
}
}
当react-plugin尝试要求React时, 它将使用项目的node_modules中的版本。如果我们想找出我们使用的React版本, 可以在源代码中添加console.log(React.version)。
专注于开发, 而不是Webpack配置 这篇文章只是对Webpack功能和实用工具的了解。
还有许多其他的Webpack加载程序和插件可以帮助你优化和简化JavaScript捆绑。
即使你是初学者, 本指南也为你开始使用Webpack奠定了坚实的基础, 这将使你可以将更多的精力放在开发上, 而不必考虑捆绑配置。
【管理Webpack依赖关系指南】相关:维护控制权:Pt Webpack and React指南。 1个
推荐阅读
- 如何为所有模块,java,android应用程序和android库配置sourceCompatibility和compilerArgs()
- 不要低估品牌故事的力量
- 打造转化-着陆页设计最佳做法
- 要做的工作(将客户需求转化为产品解决方案)
- 图标的可用性和设计最佳实践
- 你需要了解的有关UX素描的一切
- 以人为本的设计在产品设计中的重要性
- 第一印象– UX入门指南
- 迷你教程–字体组合指南