这篇文章是一年前写的,当时还用着Webpack1.x,而如今Webpack已经是3.x版本了,如果放着不更新难免会误人子弟。感谢一些网友的正面反馈,最近总算也抽出点时间来更新该文章。
0. 说点废话
从文章最初发布到如今已经整整过去一年了,这一年里面好事没做多少,坏事倒是做了一大堆(包括问候一些产品经理)。在这一年里我接触过前后端分离项目,也接触过一些基于Ruby On Rails的全栈式项目。然而这一年来我却并没有掌握多少流行的前端框架,唯一能够确定的是
- 写页面的时候能够有意识地用上HTML5的标签。
- 熟悉基本的CSS,能够根据设计图,写出酷炫(或者不酷炫)的页面。
- 对JavaScript的了解更为深入(不只是停留在Hello World的层面)。
或许我还不能算是一只合格的前端狗,我没有熟练任何框架(如果JQuery也算的话),也没有精力去折腾各种各样的框架,我只想安安静静写样式以及交互罢了。
1. 前言
今天的主题是Webpack,我似乎有点跑题了。关于如何用Webpack去搭建前端项目,Webpack官方文档已经有较为详细的说明,我们只需要自己着跟着文档搭建,掌握了套路之后便可根据自己的需求去搜索相关的插件来使用。
我这个Webpack脚手架并不是什么最佳实践,它只是我能想到的最简单的实践,平时可以用来开发静态页面。如果只是用来开发静态页面那我觉得它应该具有如下功能
- 简单易懂的目录结构。
- 能够自动编译Sass,CoffeeScript等静态资源文件。
- 修改HTML,CSS,JavaScript之后能够动态刷新页面,不需要手动进行刷新。
下面我们逐步来实现这些功能,或者你可以直接从Github(这个不是master分支)上获取相关代码。
2. Webpack构建简单的前端脚手架
从零开始搭建Webpack服务没有比官方文档更详细的了,我这里就顺着文档简单讲讲构建流程。
1) 初始化项目,安装Webpack
mkdir static-page && cd static-page # 创建并移动到项目目录
npm init -y # 生成项目信息,并把信息存放到 package.json文件中
npm install --save-dev webpack # 安装webpack,由于只在开发环境下会用到,这里用了`--save-dev`标识
完成之后我们目录下会有这些文件
> ls
node_modules package.json
- node_modules 用来存放我们通过
npm
命令安装的软件。 - package.json 文件记录该项目的元信息,以及一些依赖包信息。
2) 创建页面文件,以及js入口文件
创建一个index.html
文件用于存放我们的页面内容,我把它放在dist/
目录里面,以及一个名为index.js
的webpack入口文件,我把它放在src/
目录下面。
目录结构大概如下
├── dist
│ └── index.html
├── node_modules
├── package.json
└── src
└── index.js
PS: 所谓入口文件就是从这个文件中引入的资源都会被Webpack统一打包处理,无论它是图片资源,样式资源,还是JS资源。Webpack会根据配置对不同类型的资源文件进行不同方式的处理。
Ruby
// src/index.js
document.body.style.background = 'red'
简单起见,这里的js只做了改变背景色的操作。大家都能看出来,目前两个文件是还没有关联的,接下来我想办法让他们关联起来。这个时候就需要先配置Webpack,这里我采用了官方的配置。文件放在根目录下的 webpack.config.js
文件中
// webpack.config.js
const path = require('path');
module.exports = {
entry: './src/index.js',
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, 'dist')
}
};
配置文件都用js来写。写过js的人应该很容易就可以理解,它所代表的任务是解析入口文件./src/index.js
然后对资源进行处理,最后把处理好的资源统一输出到./dist/bundle.js
文件中。
配置好之后运行命令
> ./node_modules/webpack/bin/webpack.js # 运行我们一开始安装的Webpack
Hash: 15871b00d29beee17361
Version: webpack 3.8.1
Time: 63ms
Asset Size Chunks Chunk Names
bundle.js 2.51 kB 0 [emitted] main
[0] ./src/index.js 33 bytes {0} [built]
它会自动寻找当前目录下的webpack.config.js
配置文件,然后编译并打包相应的文件,现在我们的目录结构大概会是这样了
.
├── dist
│ ├── bundle.js
│ └── index.html
├── node_modules
├── package.json
├── src
│ └── index.js
└── webpack.config.js
期望的出口文件已经生成,接下来只需要在html里面引入这个入口文件便可, 加上一行
...
直接在浏览器打开index.html
页面就能看到如下效果。
这样第一点就已经完成了,它有良好的目录结构,不过现在看来这种简单的目录结构我们自己也懂得怎么构建,用Webpack的必要性似乎不大。接下来我们看看如何用Webpack方便地管理Sass等静态资源。
3) 编译并打包Sass文件
现代的前端领域,为了方便样式文件的管理与开发,我们一般会在项目引入Sass,或者Less这些技术,然而,浏览器却无法直接解析这类型的样式文件,也就是说如果要用到生产环境中,我们必须把这些资源进行预编译。
那么Webpack如何管理Sass资源呢?很简单,我们只需要在入口文件src/index.js
里面引入Sass的文件便可。我们首先创建一个src/example.scss
的文件,并像下面这般引入。
// src/example.scss /
body {
h1 {
color: #fff;
font-size: 40px;
font-family: fantasy;
}
}
// src/example.scss
import './example.scss';
document.body.style.background = 'red'
接下来在根目录下运行webpack的命令
./node_modules/webpack/bin/webpack.js
发现会有报错信息
ERROR in ./src/example.scss
Module parse failed: Unexpected token (1:5)
You may need an appropriate loader to handle this file type.
| body {
| h1 {
| color: #fff;
@ ./src/index.js 1:0-24
Webpack还是非常人性化的,它告诉我们缺少了合适的Loader用来加载对应的Sass文件,如果没有对应的加载器则无法加载对应的资源文件。而Webpack对应的Sass加载器在这里。
只需要按照文档的说法,安装缺少的加载器与编译工具便可
npm install style-loader sass-loader node-sass css-loader --save-dev
具体每一个包有什么作用,文档会提到,这里不详细说。接下来只需要把加载器的配置放到webpack.config.js
文件里面便可。
// webpack.config.js
module.exports = {
...
module: {
rules: [{
test: /\.scss$/,
use: [{
loader: "style-loader" // creates style nodes from JS strings
}, {
loader: "css-loader" // translates CSS into CommonJS
}, {
loader: "sass-loader" // compiles Sass to CSS
}]
}]
}
};
然后再次运行Webpack的命令, 便可以编译通过了。
如果我们不想要每次修改资源都运行一次Webpack的命令,而是想让Webpack监控我们的文件改动,并自动打包,我们可以在命令后面加上--watch
标识
> ./node_modules/webpack/bin/webpack.js --watch
Webpack is watching the files…
Hash: 691c616891a1a6c230b3
Version: webpack 3.8.1
Time: 698ms
Asset Size Chunks Chunk Names
bundle.js 19.1 kB 0 [emitted] main
[0] ./src/index.js 65 bytes {0} [built]
[1] ./src/example.scss 1.15 kB {0} [built]
[2] ./node_modules/css-loader!./node_modules/sass-loader/lib/loader.js!./src/example.scss 237 bytes {0} [built]
+ 3 hidden modules
这样每次我们改动资源文件,Webpack就会自动重新编译打包,我们只管刷新页面便可。下面是我把字体颜色改成绿色之后直接刷新页面的结果。
由于我们的Webpack服务开启了--watch
模式,我们不需要再次手动去运行Webpack命令重新打包资源。
4) 页面自动刷新
如果每次改了样式文件之后还要手动刷新的话,还是有点麻烦,效率还不够高。有没有办法修改前端文件之后自动刷新页面?这个时候请出神器webpack-dev-server.
npm install --save-dev webpack-dev-server
简单配置一下webpack.config.js
文件,加上下面配置
module.exports = {
.....
devServer: {
contentBase: './dist'
},
};
接下来运行命令
./node_modules/webpack-dev-server/bin/webpack-dev-server.js
Project is running at http://localhost:8081/
webpack output is served from /
便可启动服务,我们只需要在浏览器访问http://localhost:8081/
这个地址便可。
此时简单改变一下我们的js文件的内容,改变背景色
import './example.scss';
document.body.style.background = 'blue'
我们不再需要手动刷新页面了,webpack会自动被重新打包,并使得页面自动刷新。
但这里有一个问题,如果我们去修改没有被入口文件托管的文件,比如index.html
文件,就算我们修改html文件的内容,页面也不会自动更新。
可见修改index.html
文件后需要要手动刷新才行,要怎么使得当HTML文件被修改后自动刷新页面呢?很简单
修改webpack.config.js
配置文件,在devServer
上面加上watchContentBase: true
这个配置便可。
.....
module.exports = {
....
devServer: {
contentBase: './dist',
watchContentBase: true
},
....
};
结果如下
可见,当我们修改index.html
文件的时候网页会自动刷新。
5) 还有点小问题
这个问题是我后来才发现的,我们在通过webpack
命令生成了一个叫bundle.js
的文件,但是很奇怪的是,当我们删除了这个文件之后运行webpack-dev-server
这个服务的话,它并不会重新生成这个文件。
> ls dist/
index.html
上面是我删除了dist/bundle.js
并运行webpack-dev-server
之后的结果,dist/
目录下并不会重新生成bundle.js
文件。但是我们的服务并不会提示我们说找不到bundle.js
这个文件。我们的一切服务依然正常运行。
这是因为webpack-dev-server
会把打包好的数据存在内存中,并不会做文件IO操作,所以我们看不到对应文件的生成。
因此,如果我们使用webpack-dev-server
来辅助开发的话其实是不会生成对应出口文件的,从内存中加载数据往往效率更高。
3. 尾声
有经验的webpack使用者来说或许不屑于像我这样做,况且如今不同的框架官方都对webpack配置进行了深度定制。在项目初始化的时候我们大可以站在巨人的肩膀上直接构建项目脚手架。使用如create-react-app,vue-cli等脚手架工具来初始化我们的项目。大家可以根据自己的技术栈来选择合适的工具。
前端技术着实让人眼花缭乱,技术本身也参差不齐,各种技术都有他们的优缺点,我以为能够结合场景解决实际问题的方案才能算是最佳实践,我不排斥折腾,但我并不推荐瞎折腾。当如今前端技术让你难以选择,并感到焦虑的时候,我建议可以先考虑从基础入手,就是下面这三位
流行前后端分离的今天我们就一定要把我们的项目前后端分离吗?流行使用Vue,React这些框架的今天我们就必须抛弃JQuery吗?个人不敢苟同,前后端分离的项目固然有它的优势。如果你正在使用前后端分离的方式去构建一个简单的博客系统,或许你该静下心来想想,这种选择是否只是在增加开发难度?
当你手上有锤子的时候,看什么都像钉子。
很感谢看到这里。