认识Webpack

认识Webpack

 

网上已经有很多Webpack教程入门教程了。 本文记录了我以个人方式方法、思路认识了解Webpack。从官方的Tutorial入手,不断提出问题、解决,一步一步认识Webpack。javascript

从早期的本身写脚本,到如今的各类构建工具,前端工程化已经发展到新的阶段了。css

早先在百度地图的时候,地图代码用PHP进行简单粗放的处理。这个阶段算是最原始的本身写脚本处理。后来我用Ruby写了一套集合了开发、动态合并、mock数据、一键build的工具。这算是更进了一步。html

如今基于Nodejs的任务管理工具Grunt、Glup都提供了代码合并、压缩、各类JS Transpiler、CSS预处理、各类前端模板的处理。前端

在Grunt、Gulp中是经过第三方库进行编译的。 在Webpack中也是相似的,只不过是增长了Loader的概念。经过一系列“Loader”完成处理。 处理以后统一输出为JS代码。java

初步认识

在深刻以前,你须要先照着官方教程实践一下。有个感性的认识。 完成教程的getting started部分后,你能够初步得出如下结论:node

  1. Webpack是一个用来打包js工程的工具。官方定义为Module bundler
  2. Webpack命令行参数能够配置到名为webpack.config.js(或webpackfile)的配置文件中
  3. Webpack提供一个能够检测文件变化并编译而后刷新浏览器的webpack-dev-server

那么问题来了: 对于如Grunt、Webpack这种经过配置工做的工具来讲,有哪些配置可用,配置的行为、配置的可选值,须要完备的文档才好会用。 幸亏Webpack官网提供了详尽的文档react

下面是主要配置项的简要说明webpack

context 工程目录,必须是绝对路径
entry 打包生成的bundle。能够是多个
output 生成的文件配置选项
    output.filename 生成的文件名模板,好比 "[name].bundle.js" output.path 生成的文件目录,绝对路径 output.publicPath 线上静态资源目录 output.chunkFilename 代码块文件名模板 output.sourceMapFilename source-map文件名模板。默认是[file].map output.devtoolModuleFilenameTemplate output.devtoolFallbackModuleFilenameTemplate output.devtoolLineToLine output.hotUpdateChunkFilename output.hotUpdateMainFilename output.jsonpFunction JSONP异步加载代码块(chunk)时JSONP函数名,默认是webpackJsonp output.hotUpdateFunction JSONP异步热更新代码块时JSONP函数名,默认是webpackHotUpdate output.pathinfo 是否以注释形式在require中增长模块path信息 output.library bundle做为库输出,值为库名 output.libraryTarget 输出库的格式。好比可选amd,umd,commonjs等 output.umdNamedDefine output.sourcePrefix output.crossOriginLoading module module.loaders Loader配置 module.preLoaders, module.postLoaders preLoader和postLoader配置 module.noParse 不须要loader编译的文件 resolve 模块决议配置 resolve.alias 模块别名 resolve.root 模块根目录,绝对路径 resolve.modulesDirectories 模块目录,工做方式相似node_modules。默认值是["web_modules", "node_modules"] resolve.fallback 若是在root和modulesDirectories都找不到,会在这里搜索 resolve.extensions 用于模块查找的扩展名。 resolve.packageMains resolve.packageAlias resolve.unsafeCache resolveLoader 与resolve相似,不过是给loader模块决议使用的配置 resolveLoader.moduleTemplates externals target 目标环境,代码是用于web仍是node仍是electron环境等等 bail profile 每一个模块的时间打点信息 cache 是否开启编译缓存以提升性能。watch模式默认开启 debug 设置loaders为debug模式 devtool 用于方便调试的开发工具选项。好比source-map方便调试混淆后的代码 devServer 传给webpack-dev-server的参数 node 传递给node做为polyfills和mocks的参数 amd require.mad和define.amd对应的值。好比{jQuery:true} loader 提供给loader的额外信息 recordsPath, recordsInputPath, recordsOutputPath plugins 插件配置 

Loaders

咱们最关心的是有哪些loader能够用呢? 经过在 https://github.com/webpack 搜索项目名中包含-loader。我找到了这些官方提供的loader:git

#裸数据 raw-loader #脚本代码 coffee-loader script-loader #样式相关 css-loader style-loader less-loader #html相关 html-loader jade-loader #json相关 json-loader json5-loader #其余 worker-loader-loader imports-loader exports-loader source-map-loader coffee-redux-loader multi-loader react-proxy-loader expose-loader url-loader node-loader bundle-loader val-loader transform-loader jshint-loader null-loader coverjs-loader 

咦,为何有一个css-loader还有一个style-loader?css-loader是用来加载css文件的 style-loader是用来应用已经加载的css中的样式的。github

在配置文件中,配置须要使用的loader。test用来对文件名进行匹配测试,匹配成功的文件会用对应的loader处理。

module: { loaders: [ { test: /\.coffee$/, loader: "coffee-loader" }, { test: /\.js$/, loader: "jsx-loader" } ] }, 

每一个loader都有本身独特的配置,须要参考对应文档。 全部loader均可以配置一下项目:

test 用来对文件名进行匹配测试
exclude 被排除的文件名 include 包含的文件名 loader 叹号分割的loaders loaders loader数组 

好比babel的配置就有query、cacheDirectory配置项。

能够想象,loader要作的工做无非就是拿到源码,根据参数配置进行变换,返回变换后的结果。看一下less-loder的源代码:

/** * 简化后的伪代码 */ var less = require("less"); var loaderUtils = require("loader-utils"); module.exports = function(source){ //解析loader的query string var query = loaderUtils.parseQuery(this.query); //默认less编译配置 var config = { filename: this.resource, compress: !!this.minimize }; //将query中的配置merge到默认配置中 Object.keys(query).forEach(function(attr){ config[attr] = query[attr] }); //编译less var cb = this.callback; less.render(source, config, function(e, result){ cb(null, result.css, result.map); }); }; 

基本上就是从query读取配置,调用less编译器编译源码。

官网编写loader的教程验证了上述想法。同时也指出了编写loader时要注意的一些问题。

参考:官方给出的已有loader列表

Plugins

有哪些plugin呢? 经过在 https://github.com/webpack 搜索项目名中包含-plugin我找到了这些官方提供的plugin:

extract-text-webpack-plugin compression-webpack-plugin i18n-webpack-plugin component-webpack-plugin 

感受不对啊,那个不少教程中常见的UglifyJsPlugin都没有看到啊!那么只有一个可能,这些plugin都是内置的。在源代码中必定能找到。clone下来webpack的代码。打开lib,满眼都是XXXPlugin。在optimize目录下能够找到UglifyJsPlugin。大体看一下这些代码能够发现,每一个Plugin的原型上都有一个apply函数:

/** * 从UglifyJsPlugin.js简化而来的伪代码 */ ... var uglify = require("uglify-js"); ... UglifyJsPlugin.prototype.apply = function(compiler) { ... compiler.plugin("compilation", function (module) { ... var input = asset.source(); var ast = uglify.parse(input); //压缩 if (options.compress !== false) { var compress = uglify.Compressor(options.comrpess); ast = ast.transform(compress); } //混淆 if (options.mangle !== false) { ast.mangle_names(); uglify.mangle_properties(ast); } //从新从ast生成代码 var result = uglify.OutputStream(); ast.print(result); }); }; 

能够想象,webpack会根据配置文件中plugins数组中的插件实例,调用其apply函数。 在apply函数中,插件对感兴趣的事件(官方叫作stage)注册处理函数(plugin)。好比UglifyJsPlugin就是在compilation事件触发时,对源代码进行压缩混淆。

经过官网阅读how-to-write-a-plugin能够验证了上面的想法。

既然有compilation事件,那确定还有其余事件喽。在lib目录下搜索源代码中的compile.plugin调用

$ ack -Q compiler.plugin( | grep plugin|gawk "{print($2)}"|sort|uniq compiler.plugin("additional-pass", compiler.plugin("after-compile", compiler.plugin("after-environment", compiler.plugin("after-resolvers", compiler.plugin("compilation", compiler.plugin("compile", compiler.plugin("context-module-factory", compiler.plugin("done", compiler.plugin("emit", compiler.plugin("entry-option", compiler.plugin("environment", compiler.plugin("invalid", compiler.plugin("make", compiler.plugin("normal-module-factory", compiler.plugin("run", compiler.plugin("should-emit", compiler.plugin("this-compilation", compiler.plugin("watch-run", 

一共有18个事件。官方教程中只介绍了done,compilation,emit三个。 用一样的方法,咱们还能够查出compilation支持的事件:

$ ack -Q compilation.plugin( | grep plugin|gawk "{print($2,$3)}"|sort|uniq compilation.plugin("additional-assets", function(callback) compilation.plugin("additional-chunk-assets", function() compilation.plugin("after-hash", function() compilation.plugin("after-optimize-chunk-assets", function(chunks) compilation.plugin("after-optimize-tree", function(chunks, compilation.plugin("before-module-ids", function(modules) compilation.plugin("build-module", function(module) compilation.plugin("chunk-hash", function(chunk, compilation.plugin("failed-module", moduleDone); compilation.plugin("need-additional-pass", function() compilation.plugin("normal-module-loader", function(context, compilation.plugin("normal-module-loader", function(loaderContext) compilation.plugin("normal-module-loader", function(loaderContext, compilation.plugin("optimize-assets", function(assets, compilation.plugin("optimize-chunk-assets", function(chunks, compilation.plugin("optimize-chunk-ids", function(chunks) compilation.plugin("optimize-chunk-order", function(chunks) compilation.plugin("optimize-chunks-advanced", function(chunks) compilation.plugin("optimize-chunks-basic", function(chunks) compilation.plugin("optimize-module-order", function(modules) compilation.plugin("optimize-modules-advanced", function(modules) compilation.plugin("optimize-tree", function(chunks, compilation.plugin("record", function(compilation, compilation.plugin("record-chunks", function(chunks, compilation.plugin("record-modules", function(modules, compilation.plugin("revive-chunks", function(chunks, compilation.plugin("revive-modules", function(modules, compilation.plugin("seal", function() compilation.plugin("should-generate-chunk-assets", function() compilation.plugin("should-record", function() compilation.plugin("succeed-module", moduleDone); compilation.plugin(["optimize-chunks", "optimize-extracted-chunks"], compilation.plugin(["optimize-chunks-basic", "optimize-extracted-chunks-basic"], 

有了这两个列表,在本身编写插件就能够有的放矢地参考源代码了。

参考:官方给出的已有plugin的列表

Webpack-dev-server

Webpack提供一个小巧的基于express的开发服务器。支持自动刷新、模块热替换。还有代理。具体如何配置在这里

代理(proxy)在开发是仍是颇有用的。你能够将动态请求映射到后端的开发机,方便联调。

总结

如今照着官方教程你已经能够简单地使用Webpack了。下一步要作的是

  • 了解webpack.config.js中如何配置,有哪些要注意的(好比路径)
  • 实践经常使用的Loader和Plugin
  • 实践webpack的众多配置项
  • 实践使用webpack-dev-server进行开发

须要时能够更进一步:

  • 学习如何编写Loader和Plugin
  • 阅读已有Loader和Plugin的源码
  • 在源码中了解上面列出的stages的含义

更新:Webpack、Browserify和Gulp三者之间究竟是怎样的关系?

下面是我在知乎的回答

Task Runner

Gulp、Grunt和Make(常见于c/cpp)、Ant、Maven、Gradle(Java/Android)、Rake、Thor(Ruby)同样,都是是Task Runner。用来将一些繁琐的task自动化并处理任务的依赖关系。 其中有些是基于配置描述的,描述逻辑比较费劲,好比Ant基于xml。还有些就是代码,比较灵活,我的偏好这种。好比Rake、Thor、Gulp、Gradle。对于Gradle来讲也有些蛋疼。由于它自己是Groovy的DSL。若是要深刻使用,你还得学一下Groovy语言。其余就好多了Rake、Thor就是写Ruby;Gulp就是JavaScript。相对门槛低不少。

模块化解决方案

Browserify It provides a way to bundle CommonJS modules together, adheres to the Unix philosophy(小工具协做), is in fact a good alternative to Webpack. Webpack takes a more monolithic(总体解决、大而全) approach than Browserify... is relies on configuration.

上面这些工具在功能上有交集:代码的Minify、Concat;资源预处理等;

其实每一个工具的官网上都有对工具的设计思想、要解决的问题、与其余工具的对比。本身摘抄下来,作个表格对比一下。高亮出每一个工具独特的特性。这样你就知道何时须要用哪一个工具了。 好比,你的工程模块依赖很简单,不须要把js或各类资源打包,只须要简单的合并、压缩,在页面中引用就行了。那就不须要Browserify、Webpack。Gulp就够用了。

反过来,若是你的工程庞大,页面中使用了不少库(SPA很容易出现这种状况),那就能够选择某种模块化方案。至因而用Browserify仍是Webpack就须要根据其余因素来判断了。好比团队已经在使用了某种方案,你们都比较熟悉了。再好比,你喜欢Unix小工具协做的方式,那就Browserify。

充分了解各类工具、方案,选择合适的和本身须要的。没有绝对的好。优势换了场景也会变成缺点。

UPDATE

下面是闲耘™用Makefile管理前端工程任务的例子: https://github.com/hotoo/pinyin/blob/master/Makefile

更多资料:

相关文章
相关标签/搜索