该文主要以实操为主,关于模块化、webpack特性等介绍可以参考webpack学习这篇,或者查看官网、其他文章,本文基于
webpack 4.26.0
。
什么是webpack
再提一句,webpack是一个给js打包的工具,可以将很多模块打包成静态文件,可以进行代码分割,可以使得项目在加载过程中只加载当前需要的文件,模块可以通过loader来处理各种各样的文件,不管是CommonJs、AMD、ES6都可以进行处理,还可以处理图片、CSS、JSON、CopyScript文件,或者自定义文件,例如.vue、.jsx文件都可以处理。
从零开始
安装并开始一个项目
|
|
npm init
初始化之后将自动生成一个package.json
文件,如下:
然后,通过npm install
对webpack
进行安装
成功安装webpack
之后,目录和package.json
如下:
玩一个简单的打包
打包一个js
比如,我们新建一个main.js文件,如下
接下来,我们直接运行webpack命令
输入如下,它要求我们必须安装webpack cli
webpack命令行工具,ok,我们选yes,将自动执行npm install -D webpack-cli
来安装。
而且,还继续执行了webpack main.js
打包操作,可以看到一些输出信息,但是有个黄字的warning信息,要求我们指定打包模式,可以再命令行中通过--mode=devalopment/production
来指定打包模式,如下
ok,这样就不再警告了。首先,说下输出的信息:
名称 | 描述 |
---|---|
Hash | 本次打包的哈希值 |
Version | webpack版本号 |
Time | 打包用时 |
Built At | 打包时间 |
Asset | 打包生成的静态文件 |
Chunks | 分块编号 |
Chunk Names | 块名称 |
Entrypoint | 入口 |
Size | 静态文件大小 |
表格下面的信息,就是块的说明信息。本次打包在目录下自动生成了dist目录,如下
当我们不指定打包模式的时候,webpack默认使用production
生成环境下打包,那么生成的打包文件是上面的样子,那么我们在生成环境下打包出来的文件是什么样子呢?如下
我们可以很清晰的看到,打包结果是一个立即执行函数表达式,其中包含webpack
需要的内置函数和main.js
的函数。那么,依赖的文件是怎么打包的呢?我们下面新建一个文件hello.js
。
打包依赖js
然后,在main.js
里面使用CommonJS的方式引用它。
再次进行打包,如下
指定打包文件名和打包路径
目前,我们打包后的文件与入口文件同名,那么如果想指定其他名字呢?我们可以使用--output-filename
。
这样在dist
目录下就生成了名称为bundle.js
的文件。一般打包文件都是放在根目录的dist
文件夹下,如果想放在其他目录下,可以使用--output-path
来设置,比如:
再看下目录,已经在更目录的abc/
文件夹下生成打包好的文件了。
打包css
前端工程里面少不了样式文件,接下来,我们新建一个css文件,并引用它看看。
ok,我们继续打包
报错了!webpack
提示我们需要一个适当的loader
去处理css类型的文件,这里就需要css-loader
了,那么我们同样使用npm install
来安装。
安装成功之后,我们继续打包
诶?怎么还是报错。。。,我们虽然安装了css-loader
,但是我们并没有告诉webpack
处理css文件的时候使用css-loader
,那就指定一下呗
然后,我们再打包
这时候我们打包成功了,我们看一下打包后的bundle.js
可以看到,打包后的bundle.js
的下面注入了css-loader
和main.css
相关的代码,并在main.css
处理前使用css-loader
来处理css。
插入css
为了验证一下,我们打包后的文件的有效性,我们新建一个index.html
文件,并在里面引用打包好的js文件。
直接在浏览器打开index.html
文件,看到js文件我们已经引入,但是样式貌似并没有生效,这是为什么呢?虽然我么使用webpack
打包处理了css文件,也在js文件中引入了css文件,但以上做的只是把css文件打包进bundle.js
里面,但是我们的html并没有引入样式,这里就需要提到style-loader
了,同样,我们通过npm install
安装一下,然后指定style-loader
去处理css样式文件。
接着,我们再打包试试
ok,打开打包后的bundle.js
看一下又增加了哪些代码:
然后,我们再次打开index.html
验证一下
ok,我们看到css样式已经生效了,在index.html
文件里面,添加了<style>
标签,并将css样式注入到<style>
标签里面。
css-loader
主要是来处理css样式文件,style-loader
是在css-loader
处理之后新建一个<style>
标签插入到html文件里面,当html引入了bundle.js
文件之后,插入<style>
标签的代码就会被执行。
那每次引入css文件的时候都要去加入一长串的loader吗?其实我们可以通过命令行工具来指定,要使用--module-bind
参数,像这样。
介绍一些命令行参数
- –watch:观察文件系统的变化,这样每次修改以后都不需要再手动执行打包命令,会自动打包
- –progress:打印出编译进度的百分比值,默认false
- –display-modules:在输出中显示所有模块,包括被排除的模块
- –display-chunk:在输出中显示 chunks
- –display-reasons:显示模块包含在输出中的原因
配置文件玩打包
建立项目的配置文件
在上一节,主要写了一个简单的例子走了一下打包的过程,下面我们规范一下工程目录结构,并通过
webpack.config.js
配置文件来让webpack为我们的工程打包。我们新建一个工程目录webpack_test
,目录结构如下。
webpack.config.js
配置如图所示,src
放源码,script
放脚本文件,style
放样式文件
main.js
内容如下:
style.css
内容如下:
index.html
内容如下:
ok,文件都准备好以后,我们直接运行命令webpack
来打包。
从错误信息中,我们得知输出路径必须是一个绝对路径,但是在配置里面手写绝对路径太麻烦了,怎么办呢?我们可以使用Node.js提供的__dirname
,Node.js 中,__dirname
总是指向被执行js文件的绝对路径,相反,./
会返回你执行node命令的路径,例如你的工作路径。有一个特殊情况是在 require()
中使用./
时,这时的路径就会是含有require()
的脚本文件的相对路径。因此,我们修改webpack.config.js
如下
然后,我们再次执行webpack
命令
ok,打包成功了。在根目录dist/js/
下可以看到打包好的js文件bundle.js
。在上面引用的配置文件叫做webpack.config.js
,这个是webpack默认的配置文件名,其实我们也可以使用其他名字,比如webpack.config.dev.js
,然后在命令行打包的时候添加--config
参数,像下面这样:
嗯,这样就可以通过别的配置文件来打包我们的程序了,但看起来还是有点麻烦,我们可以结合npm
的脚本来做到的,打开package.json
文件,添加script
脚本
接着,我们运行npm run webpack
命令来打包
配置打包入口和输出
上面讲的都是单入口的打包,那么多入口怎么配置呢?可以在配置文件中通过数组的形式,例如两个平行的不相依赖的文件,但想打包在一起的情况。
例如,我们新建一个新的a.js
,并在入口配置它。
多入口webpack.config.js
这时候,我们打包
ok,打包成功了,看下打包之后的bundle.js
。
可以看到,main.js
和a.js
都被打包到bundle.js
里面了。
还有一种多入口的方式,是配置入口对象的方式,这种更适合多页面的应用程序的打包,像这样。
然后,我们再次打包看下
打包提示错误,错误中提示我们多个单独的chunk打包到了同一个文件bundle.js
里面了。怎么办呢?在配置文件里面,应该使用占位符(substitutions)来确保每个文件具有唯一的名称。
再打包看看
OK,打包成功了,在dist/js/
目录下生成了main.js
和a.js
入口同名文件,文件中分别包含了webpack内部函数和入口对应的代码。对于filename
的设置,我们出了使用name
占位符,还可以使用hash
、chunkhash
等来设置。
hash
是本次打包的哈希值,chunkhash
是每个chunk生成的哈希值。
自动化生产项目中的html页面
当我们通过占位符来设置输入的文件名的时候,我们又不知道打包好的js文件名称,如何在html引入呢?可以借助webpack的插件来解决这个问题,引入html-webpack-plugin
,同样通过npm install
进行安装。然后在配置文件webpack.config.js
中建立对插件的引用
配置好plugins
之后,再次打包,可以在dist/js
目录中看到新生成的html文件
惊不惊喜!?html-webpack-plugin
插件已经帮我们自动生成一个引用了打包后的js的html文件。但是这样的html还不能满足实际中的复杂结构,自定义的需求更大,而且自动生成的index.html
没有和我们根目录下的index.html
建立任何联系。其实,我们可以给html-webpack-plugin
插件在初始化的时候穿参数。
立即打包看看结果
在自动生成的index.html
里面,出了自动引入的两个js之外,还包含模版里面的js的引用,所以可以将模版里面的js引用干掉了。还有一个问题,目前所有文件的输出都在dist/js/
目录下,这个如何解决呢?去改改输出配置就可以了。
html-webpack-plugin
插件除了可以指定模版,还可以指定文件名,文件名中同输出配置可以使用占位符;甚至可以通过injuct
参数指定脚本引入的标签是放在head
还是body
下面说一下更复杂的需求,要求给模版传参,并引用,比如在index.html
中引用title。
运行一下打包,可以看下生成的’index.html’
上面是使用了模版语法,当然还可以在里面使用for
循环等语句来实现需求,只需要在<%%>
里面书写js语句代码就可以。
再次打包
这下我们模版里面指定的js已经被引入,但是因为之前设置了inject: head
,因此<head>
标签里面还有自动生成的引用,那么我们直接设置inject
为false就可以干掉多余的了,生成如下
解决完上面的问题之后,在实际开发中,上线后引用的脚本地址一般和开发时候不一样怎么办?下面说下output
的publicPath
属性。
对于按需加载(on-demand-load)或加载外部资源(external resources)(如图片、文件等)来说,output.publicPath
是很重要的选项。如果指定了一个错误的值,则在加载这些资源时会收到 404 错误。
此选项指定在浏览器中所引用的「此输出目录对应的公开 URL」。相对 URL(relative URL) 会被相对于 HTML 页面(或
该选项的值是以 runtime(运行时) 或 loader(载入时) 所创建的每个 URL 为前缀。因此,在多数情况下,此选项的值都会以/结束。默认值是一个空字符串 “”。简单规则如下:output.path
中的 URL 以 HTML 页面为基准。
|
|
|
|
另外,还可以通过html-webpack-plugin
插件的minify
属性对index.html
进行压缩。
|
|
经过上面的配置,打包看看
|
|
自动化生产项目中的多html页面
只需要在plugins
中配置多个html-webpack-plugin
就可以。
ok,打包看看生成的是什么
在输出目录里面生成了两个chunk对应的html文件,都是以index.html
为模版,那么如何做到定制化呢,可以通过html-webpack-plugin
传参数chunks
,在模版中定制不同chunk的不同引用等。