认识Webpack

 

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

从早期的自己写脚本,到现在的各种构建工具,前端工程化已经发展到新的阶段了。

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

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

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

初步认识

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

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

那么问题来了: 对于如Grunt、Webpack这种通过配置工作的工具来说,有哪些配置可用,配置的行为、配置的可选值,需要完备的文档才好会用。 幸好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:

#裸数据
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中的样式的。

在配置文件中,配置需要使用的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

更多资料:

认识Webpack的更多相关文章

  1. js学习笔记:webpack基础入门(一)

    之前听说过webpack,今天想正式的接触一下,先跟着webpack的官方用户指南走: 在这里有: 如何安装webpack 如何使用webpack 如何使用loader 如何使用webpack的开发者 ...

  2. webpack之傻瓜式教程

    接触webpack也有挺长一段时间了,公司的项目也是一直用着webpack在打包处理,但前几天在教新人的情况下,遇到了一个问题,那就是:尽管网上的webpack教程满天飞,但是却很难找到一个能让新人快 ...

  3. 细说前端自动化打包工具--webpack

    背景 记得2004年的时候,互联网开发就是做网页,那时也没有前端和后端的区分,有时一个网站就是一些纯静态的html,通过链接组织在一起.用过Dreamweaver的都知道,做网页就像用word编辑文档 ...

  4. Webstorm+Webpack+echarts构建个性化定制的数据可视化图表&&两个echarts详细教程(柱状图,南丁格尔图)

    Webstorm+Webpack+echarts   ECharts 特性介绍 ECharts,一个纯 Javascript 的图表库,可以流畅的运行在 PC 和移动设备上,兼容当前绝大部分浏览器(I ...

  5. 使用webstorm+webpack构建简单入门级“HelloWorld”的应用&&引用jquery来实现alert

    使用webstorm+webpack构建简单入门级"HelloWorld"的应用&&构建使用jquery来实现 1.首先你自己把webstorm安装完成. 请参考这 ...

  6. webpack入门教程之Hello webpack(一)

    webpack入门教程系列为官网Tutorials的个人译文,旨在给予想要学习webpack的小伙伴一个另外的途径.如有不当之处,请大家指出. 看完入门教程系列后,你将会学习到如下内容: 1.如何安装 ...

  7. webpack的使用

    1.webpack是什么? 打包前端项目的工具(为项目提高逼格的东西). 2.webpack的基本命令 webpack#最基本的启动webpack命令 webpack-w #提供watch方法,实时进 ...

  8. Webpack 配置摘要

    open-browser-webpack-plugin 自动打开浏览器 html-webpack-plugin 通过 JS 生成 HTML webpack.optimize.UglifyJsPlugi ...

  9. Vue + Webpack + Vue-loader 系列教程(2)相关配置篇

    原文地址:https://lvyongbo.gitbooks.io/vue-loader/content/ 使用预处理器 在 Webpack 中,所有的预处理器需要和一个相应的加载器一同使用.vue- ...

  10. Vue + Webpack + Vue-loader 系列教程(1)功能介绍篇

    原文地址:https://lvyongbo.gitbooks.io/vue-loader/content/ Vue-loader 是什么? vue-loader 是一个加载器,能把如下格式的 Vue ...

随机推荐

  1. MVVM 框架解析之双向绑定

    更好的阅读体验,点击 原文地址 MVVM 框架 近年来前端一个明显的开发趋势就是架构从传统的 MVC 模式向 MVVM 模式迁移.在传统的 MVC 下,当前前端和后端发生数据交互后会刷新整个页面,从而 ...

  2. CSS基础知识(颜色、伪类、盒子模型)

    6.设置颜色单位 L    普通英文单词 {color : 属性值red;} 此方法简单,便捷.但设置的颜色在不同浏览器中,可能显示的颜色出现差异 * 三原色 - 红.绿.蓝 L   颜色的八进制方式 ...

  3. Jquery_基础(一) 常用方法与选择器

    一.Jquery常用方法: $(function(){ //掌握$() 以及function(){} 结合 $("#a01").click(function(){ alert('h ...

  4. 从浏览器多进程到JS单线程,JS运行机制的一次系统梳理

    前言 见解有限,如有描述不当之处,请帮忙及时指出,如有错误,会及时修正. ----------超长文+多图预警,需要花费不少时间.---------- 如果看完本文后,还对进程线程傻傻分不清,不清楚浏 ...

  5. Linux-vmware tools安装与cdrom挂载(转)

    昨天想直接复制虚拟机centos系统中命令行的内容到主机的txt文档上进行保存,发现不能实现虚拟机与主机之间的直接通讯,后来查资料发现原来是由于我的虚拟机没有安装vwmare tools的缘故. 一个 ...

  6. java小入门的感觉

    工作两三年,.NET与Java都干过,也都是应付差事,用着现有的框架,现有的规范,实现简单的功能,有余力的情况下,看看框架中的代码,欣赏一下前辈们的心血,居然在单位也算有心的了?! 最近的JAVA项目 ...

  7. 应用中Token的作用

    Token 的作用 Token,就是令牌,最大的特点就是随机性,不可预测.一般黑客或软件无法猜测出来. 那么,Token有什么作用?又是什么原理呢? Token一般用在两个地方: 1)防止表单重复提交 ...

  8. 访问网时出现403 Forbidden错误的原因:

    1.你的IP被列入黑名单.2.你在一定时间内过多地访问此网站(一般是用采集程序),被防火墙拒绝访问了.3.网站域名解析到了空间,但空间未绑定此域名.4.你的网页脚本文件在当前目录下没有执行权限.5.在 ...

  9. sudo :apt-get:command not found

    在centos下用yum install xxx yum和apt-get的区别 一般来说著名的linux系统基本上分两大类:  1.RedHat系列:Redhat.Centos.Fedora等  2. ...

  10. 使用wrk进行性能测试

    1 wrk介绍 wrk是一款现代化的HTTP性能测试工具,即使运行在单核CPU上也能产生显著的压力.它融合了一种多线程设计,并使用了一些可扩展事件通知机制,例如epoll and kqueue. 一个 ...