花了很多时间,于研究webpack,结果发现很多项目开始转到webpack2或者rollup了,这叫一个尴尬。
不过学习下webpack1也没什么坏处,至少可以比较平稳的升级webpack2。
webpack配置参数太多了…真心太多了,还有多种配置方法,太灵活,这也导致它非常难,网上有的资料都不匹配,没办法四处借鉴来用。故此,还是自己摸索着来吧。
webpack是什么
我的简单理解就是,将多个分散的文件(模块),打包成为一个或者指定形式的文件。
比如说:有一个文件main.js
,依赖A.js
B.js
,如果要想用main.js
我们就需要对其进行打包(打包为bundle.js
)。通过webpack打包,会自动把A.js
B.js
的代码打包到bundle.js
。
webpack自带有一些插件,也有第三方的组件,所以支持的功能比较多,也比较复杂。我通过一些配置文件,来介绍一些常用的用法写法。
wepack 准备说明
首先要安装webpack。安装有两种方案:
全局安装:npm install -g webpack
项目安装:npm install -save-dev wepack
启动终端/命令行,找到当前项目的路径,进入项目。
如果是全局安装,那么之后可以直接输入webpack ...
这样来进行使用。
如果是项目安装,可以在package.json
中的scripts
字段中定义命令,之后执行npm run 命令
来进行操作。
为了省事,本系列文章是按照全局安装来使用的。
还是那句话,也可以不用全局安装。可以package.json
中的scripts
中配置如下代码,来通过npm run webpack
使用。
1 | { |
所有例子的代码位置
可以访问 github 进行查阅。
DEMO0 - 入门
第一个最简单的打包例子。
在demo0
下,建立src
文件夹
新建一个say.js
,有两个方法,但是仅仅导出了一个。
1 | var fn = function (msg) { |
再新建一个main.js
1 | var say = require('./say.js'); |
例子很简单,公用方法文件say.js
,被main.js
调用。
请注意
引入的文件(模块),如果是npm安装的,那么代码执行过程中,会有路径处理,能找到这个依赖包,例如写require('react')
即可;
但是是自己写的文件(模块)的话,要写成require('./say.js')
(可以省略扩展名),注意当前目录要加入./
。否则找不到文件。下文会有如何建立别名的方法。
原因是这样的
require
默认的工作路径,拿本例来说,不是/webpack-demo/demo0
,而是/webpack-demo/demo0/node_modules
。在这个目录里面,存在结构react/index.js
,或者存在react/package.json
文件,此文件中,定义了main
入口文件。
这样的话,你直接写require('react')
,就是读取 /webpack-demo/demo0/node_modules/react/index.js
(因为没有指定文件,所以读取默认文件index.js
),所以是OK的。
所以,如果是我们自己的模块,并没有在node_modules
下的,直接写require('say.js')
就会找不到。要写成当前目录require('./say.js')
。
了解完路径问题,我们回到例子,接下来进行webpack打包。
webpack打包
启动终端/命令行,首先进入目录:cd demo0
用webpack进行编译:webpack src/main.js build/app.js
,第一个参数是入口文件,第二个参数是打包后文件
之后可以发现,多了一个build
目录,同时也有了我们要的app.js
文件
剩下的事情,就是编写index.html
,并查看效果了。
重要的说明
请仔细查看编译后的app.js
文件。在源文件say.js
中,我故意加入了没有用到的函数otherFn
,在编译后的最终文件内,是存在这个函数的。
通过实验,不管用这种ES5风格,还是ES6风格,只要文件内有的东西,都会被编译到最终文件,不论是否被引用。这个是webpack1的问题,好在webpack2已经加入Tree-shaking技术来解决这个问题了。
DEMO1 - 使用配置文件webpack.config.js
这回,我们不再使用直接敲命令来控制webpack打包了,而是将要打包的东西,放到配置文件里。
我们复制一份src
目录,到demo1
目录下。这样,最终的打包后结果会是相同的。
我们在demo1
下,建立一个webpack.config.js
文件,这个是默认的配置文件名。
除此外,还有其他的一些名字也是默认名。但是webpack.config.js
是最流行的写法。
1 | console.log('当前dirname:', __dirname); |
这里面,我们第一句打印出当前的工作环境目录,这个以后会用得到,可以注意下。
之后,导出了一个配置对象。这个对象里面,有入口文件entry
、打包文件output
和特殊查找处理resolve
。其中,入口文件和打包文件是必须的,否则webpack不知道入口和出口啊。这两个配置太好理解了,我就不描述了。resolve
下文再讲。
这里一样需要注意,当前目录请写上./
。
之后,我们进入工作目录cd demo1
,并执行webpack
即可。
如果你的配置文件不是默认名,那么需要这样执行webpack --config somename.js
,就是指定配置文件。
执行后,会看到当前dirname路径,因为例子比较简单,不会报错。
如果写的比较复杂,报错了,怎么查看详情?请加参数执行打包:webpack --display-error-details
,这样打包一旦遇到错误,会有错误信息。所有的命令参数,都是两条横线开头的。
resolve是什么
它相当于一个变通处理,基本上都是在解决代码中引用部分require
import
文件的问题。
这里,用到了扩展名extentions
参数,我们加入了['js']
处理方案。意思是,针对引用的文件,自动尝试匹配扩展名。
这里的数组值,可以写不带扩展名的
js
,也可以写成带有扩展名的.js
,当然,还可以写.config.js
这样的双重扩展名一般你见到的,都是这样写的:
extensions: ['', '.jsx', '.js']
,会用一个空串开头。我个人认为没什么意义。
加入扩展名参数后,在我们的代码文件里,一旦有require
(或者ES6的import
),默认会优先找当前文件。找不到,会自动加入我们配置的扩展名js
。
比如main.js
中的require('./say.js')
,默认去找当前目录下的say.js
,结果找到了,OK。
如果我们写成require('./say')
,默认去找当前目录下的say
,找不到!只好按配置的extentions
顺序增加扩展名,再去找say.js
say/index.js
等,直到找到为止。
所以,一旦我们在resolve
中配置了extentions
,就可以在代码引用环节省去对应的扩展名了。这个简写方案,非常常见。
DEMO2 - resolve的alias参数 官网API
这里,我们在提一个参数alias
。
接上一个DEMO1,复制一份为DEMO2。
这次项目不一样了,我们要用到很多自己写的模块,这个例子中,我新加入了take.js
watch.js
文件,并和say.js
一起放到了modules
目录下,而且是按照分类和版本号存放。
结构为:
1 | modules/say/1.0/say.js |
在引用的文件main.js
中,我们希望这么写:
1 | var say = require('say'); |
注意,我们希望这里把say
take
watch
定义了为别名,没有路径概念。
这样好处是,就可以写模块的同事自己专心写模块,而写main.js
的同事只需要关心引入对应模块就行了,不需要关心具体的版本和路径。至于别名的处理,只需要有一个人每次负责修改webpack.config.js
就可以了。
配置文件,需要修改成这样:
1 | console.log('当前dirname:', __dirname); |
这样写之后,在main.js
中,当遇到require('say')
的时候,相当于变成了require('./modules/say/1.0/say')
。这就是别名的作用。
DEMO3 - resolve的更多参数(root)官网API
resolve.root
,用的人应该很少。这里仅做介绍。
现在来个特殊的例子。我们在demo2中,写过一些公共方法,也写了一个调用的main.js
。另一个开发小组,知道我们的公共方法了,觉得很好,也想使用,但是他们不希望自己去维护公共方法。怎么办?
让require的默认路径设置为demo2的模块路径呗~
他们的main.js
是这样的:
1 | var say = require('say'); |
webpack.config.js
需要这样配置:
1 | var path = require('path'); |
要说明注意的点
- 开头引入了
path
,这个是node
自带模块。path.resolve
可以将参数拼接,组成绝对路径。 resolve.root
的值,可以是字符串,也可以是数组。数组相当于多个路径,一个一个查找resolve.root
只支持绝对路径,相对路径不行的- 由于我们使用
root
修改了require
处理依赖的工作路径,即不在当前运行的路径了,修改成为了/webpack-demo/demo2/src/modules
,所以在alias
里,也不能是./
开头了,而应该直接写成say/1.0/say
,和上文中的react
例子相同。如果继续使用./
开头会自动在demo3目录下查找
小结
这篇文章,介绍了如何webpack入门,以及最基础的配置。
配置里面,要有 entry
output
。
处理代码中require
的问题,可以配置resolve
来解决。这里面有扩展名extentions
和别名alias
。常用的就这两个,其他方法我很少见别人用。
还提及到了,如果打包出错,想看错误信息,这样执行:webpack --display-error-details
。