目录结构
project
- css
- bootstrap.min.css
- jb.css
- fonts
- 一些bootstrap的字体
- images
- 一些项目用到的图片
- js
- bootstrap.min.js
- jquery.min.js
- jb.js
- index.html
- favicon.ico
项目背景
这个是公司要做的一个官方网站。由于项目比较简单,要求是单页的,没有页面跳转,所以只有一个 .html 文件。项目用了比较常规的 bootstrap + jquery 的开发,这个也没啥好说的。考虑到 CDN 的可控性,所以把所有 bootstrap 的资源都下载到了本地进行引用。项目开始时是用了常规的 js 和 css 引用(css 放前面,js 放后面),在开发完成后,发现有时间多余,就考虑用 webpack 对他进行处理一下,以巩固和学习一下 webpack 所用到的知识
初始化工作
1. 初始化
npm init
先初始化一个 package.json 文件来管理我们 webpack 所依赖的文件包。一路无脑回车即可。
2. 安装 webpack
想要用 webpack ,那么你首先肯定要安装 webpack 才可以啊。用以下命令:
npm install webpack --save
3. webpack.config.js
在根目录下新建一个 webpack.config.js 文件,用来对 webpack 进行配置。
当有这个文件后,我们系可以在命令行中用以下命令来启动配置好的 webpack 了。
webpack --config webpack.config.js
4. 修改 webpack 打包命令
以上虽然也可以启动配置好的 webpack。但是每次要输这么一串命令好像有点太长了(懒啊)。所以在 package.json 中修改这样一项:
"script": {
+ "start": "webpack --config webpack.config.js",
"test": "echo \"Error: no test specified\" && exit 1"
}
这样的话我们就可以在命令行中少敲几个键盘了。直接用以下命令,就等同于上面的命令了:
npm start
好了,到这里,初始化工作就做完了,那么开始我们的 webpack 配置吧
webpack.config.js
1. 哪里来的入口文件?
什么事入口文件
webpack 创建应用程序所有依赖的关系图(dependency graph)。图的起点被称之为入口起点(entry point)。入口起点告诉 webpack 从哪里开始,并根据依赖关系图确定需要打包的内容。可以将应用程序的入口起点认为是根上下文(contextual root) 或 app 第一个启动文件
跟其他的 spa 应用不一样,这样的普通应用,其所有依赖都来自于 index.html 文件。如果说要有入口文件的话,怎么也应该是 index.html 他本身吧。但是 webpack
基本都是用 .js 文件作为入口文件,用 .html 作为入口文件的...(反正我是没有见到过)。至于这里有啥原因的话,大家就参考一下这篇文章吧。(其实我也不懂)
所以说,不管怎么样,我们都需要有个入口文件。
那就不管怎么样,我们经常看到的 webpack 的配置都是这样的
module.exports = {
entry: './index.js'
}
那么我们不管三七二十一,先在根目录下新建一个 index.js,然后让他作为我们的入口文件。
index.js
要知道,对于我们原来的项目而言,我们根本就是不需要这么一个 index.js 文件的(没有他,我们可以活得更好)。但是我们又不得不创建了这样一个文件。那么问题来了,这么创建出来的文件,里面又该放什么内容呢?我们总不该救这么创建一个空文件就算了吧。
在考虑这个问题的时候,我们可以先去看下 webpack 官方的那个很经典的图(我很懒,就不放图了,大家自己去网上找吧)。webpack 把左边乱七八糟的 .js .css .png .jpg .sass。。。等等文件全部打包成了静态资源。也就是说,webpack 打包的是除 html 外的所有资源,那么我们是不是只要把这些资源都放到入口文件中那么就可以让 webpack 帮我们打包了呢?
但是等等。其他的都没有问题,什么 css 啊,什么 js 啊,都好说,因为用来也不过这么几个,但是图片呢,字体呢?我在项目中用了那么多图片,要全部再在 index.js 里面再写一遍!天呐!这是要命的啊!那么我能不能偷懒下,就只写 css 和 js 呢,其他乱七八糟的我先不管?那就先这么来吧。修改我们的 index.js 文件,添加以下内容。
require('./css/bootstrap.min.css')
require('./css/jubang.css')
require('./js/jquery.min.js')
require('./js/bootstrap.min.js')
require('./js/jb.js')
先这样把他当作我们的入口文件吧
2. 配置出口文件
出口文件就很好配置了,将他打包到根目录下的 dist 目录中。嗯 ~ 这很常见!
var path = require('path');
module.exports = {
entry: './index.js',
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, 'dist')
}
}
3. 配置 loader
这个不知道怎么配置,就先看这篇文章吧。
所以说,到这里我们的 webpack.config.js 就是这样的:
var path = require('path');
module.exports = {
entry: './index.js',
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, 'dist')
},
module: {
rules: [{
test: /\.css$/,
use: [
'style.loader',
'css-loader'
]
}, {
test: /\.(png|jpg|svg|git)$/,
use: [
'file-loader'
]
}, {
test: /\.(woff|woff2|eot|ttf|otf)$/,
use: [
'file-loader'
]
}]
}
}
这里我们用到了三个 loader,需要先安装下
npm install css-loader style-loader file-loader --save
这三个 loader 的作用大家还是自己去网上查找吧
4. 第一次打包
配置到这里,我们可以先来打包一下,有问题再改嘛!
命令行切换到项目目录下,执行以下命令:
npm start
打包结束后,项目的目录结构
project
- css
- dist
- fonts
- images
- js
- node-modules
- favicon.ico
- index.html
- index.js
- package.json
- webpack.config.js
我们可以看到,项目根目录下面多出来一个 dist 的目录。没错,这个就是我们 webpack 打包后文件生成的目录,至于为什么会是 dist 目录,那是因为你在 webpack.config.js 的 output 中设置的 path。
现在我们来查看下 webpack 打包出了什么东西
- dist
- xxxx.jpg
- xxxx.woff2
- xxxx.jpg
- xxxx.svg
- bundle.js
- xxxx.ttf
- xxxx.eot
- xxxx.woff
注:xxxx代表一串数字和字母的组合,为了表示方便就这么写了
打包文件中生成了一个 .js 文件,两个 .jpg 文件,四个字体文件(.woff2、.ttf、.eot、.woff),和一个 .svg 文件
我们来看下这是个类型的文件都来自哪里吧
1. bundle.js
bundle.js 是我们根据我们入口文件,将我们在入口文件中所有的依赖资源都打包进去生成的。这个也是我们最主要要关注的文件。
2. .jpg
两个 .jpg 文件是从哪里来的呢?查看了两张图片之后,其实我们会知道,这两个图片都是在我们自己写的 jb.css
中用来作为 background-image
引入的。我们说了,webpack 会根据入口文作为起点,并根据依赖关系图来进行打包的。换句话说,我们在入口文件中依赖了 jb.css
,而 jb.css
依赖了两张 .jpg 图片,所以 webpack 根据依赖分析,将这两张图片也都一起打包进来了。
3. 字体文件 + svg
四个字体文件的来源就需要我们对 bootstrap 有一定了解了。如果熟悉 bootstrap 的同学肯定会知道,在 bootstrap 的依赖里面,他正是依赖了这些字体,也就是说这些字体文件是从 bootstrap.min.css 文件中被打包进来的。其实我在做这个项目的时候,把 bootstrap 的源码弄到本地的时候,会发现里面有一个 fonts 的文件夹,也就是我们项目根目录下的 fonts 文件夹,这里会有四个同样文件结尾的字体文件和一个 .svg 结尾的 svg 文件。而这五个文件不就是我们这里多出来的五个文件嘛
如果还不放心的话,我们可以再做个验证。修改 webpack.config.js 文件
{
test: /\.(png|jpg|svg|git)$/,
use: [
- 'file-loader'
+ 'file-loader?name=[hash:8].[name].[ext]'
]
}, {
test: /\.(woff|woff2|eot|ttf|otf)$/,
use: [
- 'file-loader'
+ 'file-loader?name=[hash:8].[name].[ext]'
]
}
我们把图片和字体文件,在 file-loader 处理后让他的名字就变成 ’8位hash值-原文件名-后缀名‘的格式,那么我们就可以比对这几个文件的来源了。
重新打包后(你得先删除原来的 dist 文件夹),我们就可以发现我们的猜测是正确的!
4. 遗留问题
虽然我们的第一次打包成功了,但是还是留下了几个问题没有解决:第一,我的 js、css、字体、图片等资源都被打包进了 dist 目录,但是作为我们的项目最主要的 index.html 文件呢?没有这个文件,我们打包出来的东西还有什么意义呢!。第二,我的 images 文件夹里有那么多图片,你这个 webpack 打包后为什么就只有两张图片了,其他的呢?
那么接下来让我们急需解决。
5. html-webpack-plugin
要解决第一个问题,我们需要用到 html-webpack-plugin 插件。这个插件的具体说明可以查看这里。这个插件的作用是可以将 html 文件打包,并自动添加对打包后的 output 文件的引用。具体如何使用,请先安装:
npm install html-webpack-plugin --save
修改配置文件:
var path = require('path');
+ var HtmlWebpackPlugin = require('html-webpack-plugin');
在 module 后面加
plugins: [
new HtmlWebpackPlugin({
template: './index.html'
})
]
我们在配置文件中引入了一个插件,并向 HtmlWebpackPlugin 构造函数传递了一个对象参数,在这个对象参数中,我们指明了一个 template 字段,表面我们要打包的 html 的文件源。然后我们重新打包,再查看我们的目录就可以发现 dist 目录下多出了一个 index.html。由于这个项目本身就是一个简单的不依赖任何环境的项目,所以如果正常的话我们直接打开 index.html 页面就能在浏览器里正常显示了。虽然不知道会怎么样,但是我们还是打开来试试吧。
当我们打开 index.html 在浏览器中显示的时候,我们发现浏览器中好多图片都不见了。细想我们的项目代码,发现除了在 jb.css 中的背景图被正确显示以外,其他的定义在 img 标签中的图片没有一张是显示出来的。
所以虽然这个文件配置还有点问题没解决,那么我们先来解决 html 中的 img 问题吧,也就是我们上面提到的第二个问题。
6. html-withimg-loader
要解决第二个问题(也就是 html 中的 img 问题),我们需要用到 html-whithimg-loader,具体关于这个怎么用可以查看这里。
修改配置文件,直接在 rules 中再添加一条配置规则
{
test: /\.(html|htm)$/,
use: [
'html-withimg-loader'
]
}
这条配置规则表明,所有要处理的 html 文件首先会经过 html-withimg-loader 这个 loader 处理。就是这么简单能将我们第二个问题解决吗?试试看吧,事件是检验真理的唯一标准。
修改资源目录
重新打包,再查看 dist 目录,这一查看不要紧,发现 dist 目录下多了好多图片文件,密密麻麻,乱七八糟的,这些该不会就是我们 html 中的图片吧。按住自己的强迫症,先找到 index.html (我们最关心的还是他嘛),打开后再浏览器查看效果。发现果然,我们的图片都已经在了,而且样式也差不多对了。但是这么乱七八糟的 dist 目录,怎么会是我们这种强迫症患者所想要的结果呢!我们想要的是图片都放在图片文件夹下,字体都放在字体文件夹下,其他的比较少的就先让他在外面呆着吧。说干就干,我们来调整一下我们的配置文件
{
test: /\.(png|jpg|svg|git)$/,
use: [
- 'file-loader?name=[hash:8].[name].[ext]'
+ 'file-loader?name=images/[hash:8].[name].[ext]'
]
},
{
test: /\.(woff|woff2|eot|ttf|otf)$/,
use: [
- 'file-loader?name=[hash:8].[name].[ext]'
+ 'file-loader?name=fonts/[hash:8].[name].[ext]'
]
}
自动删除 dist
还有一个问题我已经忍了很久了,每次打包前,我们都需要手动先去删除上次打包留下来的 dist 目录,这个就烦了,虽然只是一个 delete 的事情,但是做多了也烦啊!我们想能不能让 webpack 自动帮我们做了这件事,让我们不需要手动去删除。还好 webpack 够智能,总能满足你提出来的各种无理取闹。不过我们先要装一个插件:
npm install clean-webpack-plugin --save
然后再配置文件中添加对这插件的引用
var HtmlWebpackPlugin = require('html-webpack-plugin');
+ var CleanWebpackPlugin = require('clean-webpack-plugin');
在 plugins 中添加对这个插件的使用
new CleanWebpackPlugin(['dist'])
这样我们就能不用手动删除 dist 文件夹了。
重新打包下试试吧!
打包完后我们在查看 dist 目录就瞬间感觉清爽多了有木有!
- dist
- fonts
- 一些字体文件
- images
- 一些图片文件
- bundle.js
- index.html
修改 index.html
将打包后的项目再次在浏览器中查看,并查看控制台会发现,控制台报了一堆错误。其中有几个是对一些 css、js 文件引用的错误。因为我们把需要用的 css、js 都打包进了 bundle.js 中了。而我们原来的项目是通过静态资源引用的方式一个个导入 html 文件中的。所以,当我们 webpack 打包成功后,就不需要对这些资源进行引用了,我们只需要对 bundle.js(我们打包后的文件)进行引用就可以了,所幸的是,打包后的文件 webpack 已经自动帮我们引用了。所以直接在原来项目中的 index.html 中干掉那些 css、js 就可以了。然后重新打包后就没有这些资源找不到的乱七八糟的错误了。
但是,有一个小问题就是关于我们的 .ico 文件。这是我们网站的图标文件。他的引用错误该如何解决呢?我们可以在生成 html 的时候,将这个问题先给解决了。修改配置文件
new HtmlWebpackPlugin({
template: './index.html',
+ favicon: path.resolve(__dirname, './favicon.ico')
})
这样的话我们的图标文件也就有了。
7. 关于 jquery
虽然一些乱七八糟的引用错误解决了,但是控制台留下了一个让我们非常头疼的问题:jquery 的引用问题:
Uncaught Error: Bootstrap's JavaScript requires jQuery
这里我们需要用到 expose-loader 这个东西,关于他,可以查看这里,废话不多说:
npm install expose-loader --save
在 module 的 rules 中再添加一个 loader
{
test: require.resolve('./js/jquery.min.js'), // 引入 jquery
use: [{
loader: 'expose-loader',
options: '$'
}, {
loader: 'expose-loader',
options: 'jQuery'
}]
}
当然,网上或许有其他方法关于引入 jquery 的,这里只是说一种。然后再打包我们的页面就没有问题了:各种资源都有了,js 写的效果也出现了。
8. 提取 css
虽然网站看上去没啥问题了,但是细心的同学肯定会发现:当我们打开网站的时候,他会先出现一个没有样式的页面,然后一闪而逝,最后才出现我们预期的样子。这是为什么呢?
原因很好理解,因为我们把 css 和 js 都打包进了同一个 bundle.js 里面了。但是,这个 bundle.js 是在页面最后面才加载进来的。也就是说,我们的样式被放在了页面的底部被加载。这完全不符合我们的预期啊。我们希望的是样式在 head 中加载,而 js 脚本才放在页面底部加载。所以我们就不能把 css 和 js 一起打包进 bundle.js 中了。
extract-text-webpack-plugin
详细资料看这里
npm install extract-text-webpack-plugin --save
增加 require
var ExtractTextWebpackPlugin = require('extract-text-webpack-plugin');
修改 css rules
{
test: /\.css$/,
use: ExtractTextWebpackPlugin.extract[{
fallback: 'style-loader',
use: 'css-loader'
}]
}
增加 plugin
new ExtractTextWebpackPlugin('style.css')
打包,然后我们会发现 dist 中多了一个 style.css,然后再 index.html 的 head 中的多了对这个 css 的引用
9. 其他
1. 压缩 js
增加 plugin
new webpack.optimize.UglifyJsPlugin({
compress: {
warnings: false
}
})
2. 压缩 html
new HtmlWebpackPlugin({
template: './index.html',
favicon: path.resolve(__dirname, './favicon.ico'),
minify: {
removeAttributeQuotes: true,
removeComments: true,
removeEmptyAttribute: true,
collapseWhitespace: true
}
}),
3. 优化图片
{
test: /\.(jpg|png|gif|svg)$/,
- use: 'file-loader?name=images/[hash:8].[name].[ext]'
+ use: 'url-loader?limit=8192&name=images/[hash:8].[name].[ext]'
}
总结
暂时就先那么多吧,没时间写了,以后再说。第一次发文,求轻虐。