企业🤖AI智能体构建引擎,智能编排和调试,一键部署,支持私有化部署方案 广告
把Webpack放在历史情景下能更好的理解它。这能让你知道它实现了多么强大的功能。过去,把一些脚本组合到一起就足够了。如今时代变了,现在分发你的JavaScript代码是一个复杂的过程。 这个问题随着单页应用的增加而升级。他们依靠很多沉重的库。最后你想做的是一次性加载它们。这是一个好主意,Webpack能与这些解决方案一起工作。 随着Node.js和[npm](https://www.npmjs.com/)的流行,Node.js的包管理工具,提供了更多的上下文。在npm之前,管理依赖是很困难的。现在npm在前端开发很流行,管理依赖的解决方案已经改变。现在我有许多棒棒哒的方式来管理我们项目的依赖。 历史告诉我们有许多的构建系统,[Make](https://en.wikipedia.org/wiki/Make_%28software%29)也许是最出名的一个,而且任然是一个可行方案。在前端的世界[Grunt](http://gruntjs.com/)和[Gulp](http://gulpjs.com/)已经特别的流行了。通过npm实现的插件机制使它们都很强大。 [Browserify](http://browserify.org/)领先了一步。它基于npm打包提供了非常强大的功能。你可以用很多小工具补充它。这是与Webpack的实现是一个很好的对比. [JSPM](http://jspm.io/)走在了前面,它推动了包管理直接到了浏览器。它建立在[System.js](https://github.com/systemjs/systemjs)之上,System.js是一个动态的模块加载器。不像Browserify和Webpack,它跳过了在开发阶段把代码打包到一起的步骤。然而你也可以产生一个生产环境的包来使用它。Glen Maddern在它的[关于JSPM的视频](https://www.youtube.com/watch?t=33&v=iukBMY4apvI)里有详细讲述。 ##Make 你可以说Make让你回到了童年时代。它首次发布时1977年。尽管它是一个老工具,但却很有意义。Make允许你写一些单独的任务来达到不同的目的。在下面的例子中,你将有几个单独的任务来创建一个生产环境构建,压缩你的JavaScript和运行测试。你可以在许多其他工具里看到同样的主意。 虽然Make通常在C项目里使用,但它没有被束缚在里面。James Coglan有关于[如何在JavaScript使用Make](https://blog.jcoglan.com/2014/02/05/building-javascript-projects-with-make/)的详细讲解.思考一些下面由James提供的简短代码。 ``` PATH := node_modules/.bin:$(PATH) SHELL := /bin/bash source_files := $(wildcard lib/*.coffee) build_files := $(source_files:%.coffee=build/%.js) app_bundle := build/app.js spec_coffee := $(wildcard spec/*.coffee) spec_js := $(spec_coffee:%.coffee=build/%.js) libraries := vendor/jquery.js .PHONY: all clean test all: $(app_bundle) build/%.js: %.coffee coffee -co $(dir $@) $< $(app_bundle): $(libraries) $(build_files) uglifyjs -cmo $@ $^ test: $(app_bundle) $(spec_js) phantomjs phantom.js clean: rm -rf build ``` 使用Make来需要使用Make的特殊语法和终端命令来构建任务。这可方式可以很容易和Webpack一起工作。 ##Grunt ![document/2015-09-08/55eee2f3d0563](https://box.kancloud.cn/document_2015-09-08_55eee2f3d0563.png) Grunt在Gulp之前是主流构建工具。它的插件构架促使了它占领的市场。同时这也是它致命的弱点。从经验来看,你不希望维护一个300多行的`Gruntfile`。下面是一个[Grunt文档](http://gruntjs.com/sample-gruntfile)里的一个例子。 ``` module.exports = function(grunt) { grunt.initConfig({ jshint: { files: ['Gruntfile.js', 'src/**/*.js', 'test/**/*.js'], options: { globals: { jQuery: true } } }, watch: { files: ['<%= jshint.files %>'], tasks: ['jshint'] } }); grunt.loadNpmTasks('grunt-contrib-jshint'); grunt.loadNpmTasks('grunt-contrib-watch'); grunt.registerTask('default', ['jshint']); }; ``` 在这个列子中,我们定义了两个和*jshint*有关的任务。jshint是一个代码规范工具,帮助你发现源代码里可能存在的语法和使用不规范问题。我们有一个独立运行jshint的任务,同时又有一个监视任务。当我们运行Grunt时,在我们编辑和保存源代码时就能实时看到警告信息。 在实践中,你可能为了各种各样的目的需要很多小的任务。比如打包一个项目。这个列子向我们展示了任务是如何构建的。Grunt非常重要的一点是为我们隐藏了很多细节。当走的太远时会是一个问题。很难明白这些配置下到底发生了什么。 *提醒:grunt-webpack插件允许你在Grunt环境中使用Webpack。你可以把这个重任交给Webpack。* ##Gulp ![document/2015-09-09/55f044a92debc](https://box.kancloud.cn/document_2015-09-09_55f044a92debc.png) Gulp使用了不同实现方式。用真正的代码替代了对每个插件的配置。Gulp构建在可靠和管道之上。如果你熟悉Unix,这是相同的思想。你会很熟悉数据源,过滤器,水槽(sinks)。 文件就是数据源,过滤器在数据源上执行操作(比如转换JavaScript),最后结果被送到水槽(比如,你的发布目录)。这里有一个`Gulpfile`让你对这种实现更多认识,这个文件来着这个项目的README。下面删减了部分。 ``` var gulp = require('gulp'); var coffee = require('gulp-coffee'); var concat = require('gulp-concat'); var uglify = require('gulp-uglify'); var sourcemaps = require('gulp-sourcemaps'); var del = require('del'); var paths = { scripts: ['client/js/**/*.coffee', '!client/external/**/*.coffee'] }; // Not all tasks need to use streams // A gulpfile is just another node program and you can use all packages available on npm gulp.task('clean', function(cb) { // You can use multiple globbing patterns as you would with `gulp.src` del(['build'], cb); }); gulp.task('scripts', ['clean'], function() { // Minify and copy all JavaScript (except vendor scripts) // with sourcemaps all the way down return gulp.src(paths.scripts) .pipe(sourcemaps.init()) .pipe(coffee()) .pipe(uglify()) .pipe(concat('all.min.js')) .pipe(sourcemaps.write()) .pipe(gulp.dest('build/js')); }); // Rerun the task when a file changes gulp.task('watch', function() { gulp.watch(paths.scripts, ['scripts']); }); // The default task (called when you run `gulp` from cli) gulp.task('default', ['watch', 'scripts']); ``` 配置文件全变成代码了,当你遇到问题时,你可以马上修改(hack)它。你可以包装现成的Node.js模块作为Gulp插件。和Grunt对比,你比较明白这到底发生了什么。不过你任然需要为临时的任务写很多样板代码,这也就是为什么新的工具会出现。 [gulp-webpack](https://www.npmjs.com/package/gulp-webpack)能让你在Gulp环境中使用Webpack ##Browserify ![document/2015-09-09/55f04f2109a04](https://box.kancloud.cn/document_2015-09-09_55f04f2109a04.png) 处理JavaScript的模块一直以来都是一个问题。知道ES6,这门语言都没有模块的概览。因此在90年代进入浏览器环境时,我们就卡主了。很多解决方案被提出来,包括[AMD](http://requirejs.org/docs/whyamd.html) 在实践中,使用CommonJS是很实用的。它是Node.js的格式,采用同步加载的方式。它的优点时可以利用npm而不必重复造轮子。 [Browserify](http://browserify.org/)解决了这个问题,它提供了把CommonJS打包到一起的方法。你可以把它和Gulp集成。这里有一些小的转换工具能让你超越基本的使用,比如[watchify](https://www.npmjs.com/package/watchify)提供了一个文件监听器,能够在你开发期间进行打包。毫无疑问,这回节省一些努力,并在某种程度上这是一个不错的方案。 Browserify的生态系统由许许多多的小模块组成。在这方面,它始终坚持Unix的哲学。Browserify比Webpack更易用,选择使用它也是非常好的。 ##Webpack ![document/2015-09-10/55f18fb766e03](https://box.kancloud.cn/document_2015-09-10_55f18fb766e03.png) 你可能会说Webpack(或者webpack)比起Browerify实现的更集成一些。你获得了更多的开箱即用。Webpack扩展了`require`,允许你使用加载器自定义它的行为。你可以通过这个机制加载任意的内容。也适用于css文件(`@import`)。Webpack为任务也提供了插件,比如压缩,本地化,热加载等等。 这一切都基于配置。这里有一个例子,改编自[Webpack的官方文档](http://webpack.github.io/docs/tutorials/getting-started/)。 ###webpack.config.js ``` var webpack = require('webpack'); module.exports = { entry: './entry.js', output: { path: __dirname, filename: 'bundle.js' }, module: { loaders: [ { test: /\.css$/, loaders: ['style', 'css'] } ] }, plugins: [ new webpack.optimize.UglifyJsPlugin() ] }; ``` 上面的配置是使用JavaScript写的,且具有很好的扩展性。只要是JavaScript,Webpack就能很好的和它在一起。 有时,配置文件会使Webpack看起来有一些不透明。很难理解其中发生了什么。在复杂的项目中尤其明显。我和Christian Alfoni 完成了[Webpack手册](https://christianalfoni.github.io/react-webpack-cookbook/),里面对具体的问题有更详细的描述。 #JSPM ![document/2015-09-22/560156c498d2c](https://box.kancloud.cn/document_2015-09-22_560156c498d2c.png) 使用JSPM会与前面介绍的工具不同。它自带了一些命令行工具来安装新包到项目里,和创建线上压缩包等。它支持[System plugins](https://github.com/systemjs/systemjs#plugins),能允许在你的项目里加载各种格式的js包。 鉴于JSPM是一个年轻的项目,它肯定有很多坑现在。如果你是一个冒险者,值得去试一试。就像你现在知道的那样,在前端开发里工具更新的很快JSPM就是一个有价值的竞争者。 #为什么使用Webpack? 为什么你会使用Webpack代替Gulp或者Grunt?这不是非此即彼的问题。Webpack不仅处理打包的困难问题,还处理其他很多问题。我选择Webpack是因为它支持模块热替换(HMR)。这是react-hot-loader使用的特性。我将在后续像你展示。 你或许已经对LiveReload或者Browsersync很熟悉。当你做出改变时这些工具自动刷新浏览器。HMR把这个事情更进了一步。在React的案例中,它允许应用保存自己的状态。这听起来简单,但是在实践中却大不一样。 抛开HMR这个特性。Webpack的打包能力是很广泛的。它允许你使用各种方法分开打包。你可以在你的应用执行时动态加载。这个延时加载很方便,特别是面对大型程序时。你可以在你需要时才加载依赖。 使用Webpack你可以很容易给包的名字加上hash。这能让你在客服端改变时将起失效。在理想情况下包分割能让客服端仅仅重新加载很小一部分代码。 用其他工具也能完成这些任务,问题是肯定需要做很多工作。在Webpack里主要的就是配置。记住你可以通过livereactload使用HMR,所以这个特性不是只有Webpack才有。 通过集成这些小的特性。Webpack能让你惊讶并完成很多事情。如果你觉得什么事情缺失了,加载器和插件可以帮助你找到他们。Webpack有一个意味深长的学习曲线,尽管如此,它仍是一个值得学习的的工具,能节省很多时间和精力,在长期看来。 获得更多的Webpack和其他工具的比较,请翻阅[官方对比](https://webpack.github.io/docs/comparison.html)。 #Webpack支持的模块格式 Webpack允许你使用不同的模块格式,不过在帽子(hood)下它们以同样方式工作。 ##CommonJS 如果你使用Node.js,那么你应该很熟悉CommonJS了。下面是简短的例子: ``` var MyModule = require('./MyModule'); // export at module root module.exports = function() { ... }; // alternatively, export individual functions exports.hello = function() {...}; ``` ##ES6 ES6这个格式我们从1995年就开始等待了。正如你看到那样,它很像CommonJS并且更简单。 ``` import MyModule from './MyModule.js'; // export at module root export default function () { ... }; // or export as module function, // you can have multiple of these per module export function hello() {...}; ``` ##AMD AMD是异步模块定义格式。作为一种变通方案被发明,它引进了一个`define`包装器: ``` define(['./MyModule.js'], function (MyModule) { // export at module root return function() {}; }); // or define(['./MyModule.js'], function (MyModule) { // export as module function return { hello: function() {...} }; }); ``` 顺便提一下,它可能和`require`一起使用,想下面这个样子: ``` define(['require'], function (require) { var MyModule = require('./MyModule.js'); return function() {...}; }); ``` 这些实现方法确实减少了一些混乱。你最终还是要使用一些多余的代码。现在有了ES6,你应该不会再有理由使用AMD了,除非你真的需要它。 ##UMD UMD,同意模块定义。需要更上一层楼。这是一个怪物格式,它的目标是让上面的格式互相兼容。只是让你看一看,千万不要写在代码里,把这个工作留给工具吧。如果你没有被吓到。你可以试一试[官方文档](https://github.com/umdjs/umd) Webpack能生成UMD包装器给你(output.libraryTarget: 'umd')。这对于库的作者特别有用。当讨论npm和库作者是时我们会回来讨论关于这个的细节。 #总结 我希望这个章节能帮助你明白为什么Webpack是一个值得学习的工具。它解决了一个常见的web开发问题。当你使用好它时,它可以节省大量的处理时间。在接下来的章节里我们会更深入的考察Webpack。你将学习开一个简单的开发配置。我们会用它来开始我们的Kanban应用。 你可以,也许是应该,使用Webpack时和其他一些工具一起使用。它不能解决所有的问题。它能解决打包中困难的问题。它减少了开发中的一些负担。仅仅使用`package.json, scripts`,和Webpack会带你走很远,你马上就能看到。