前端全栈框架 - Express 安装与使用 - 《手把手带你从零开始学全栈》
Express 是一个精简、灵活的Node.js的Web应用程序开发框架,为Web和移动应用程序提供了一组强大的功能;简单来说,Express 框架本身是对Node.js中的HTTP模块进行的一层抽象,就是这层抽象使得开发者可以无须注意细节,直接上手进行页面和业务逻辑的开发
一个合格的工程从来不是一些简单的文件堆砌,就如同建造一座摩天大楼一样,并不像搭建一个玩具模型的房子,通过简单的拼装就可以完成;建造一座摩天大楼必须拥有坚实的地基和框架,还要有规范和章程才能完成;Express框架从Node.js发布之初就存在,至今已有十多年的历史了;开发者可以使用Express快速地搭建一个具有完整功能的网站,而不是一个简单的网页
Express 的主要功能包括:
- 设置中间件来响应HTTP请求
- 定义路由表执行不同的HTTP请求动作
- 通过向模板传递参数动态渲染HTML页面
1. Express 的安装
要安装Express,首先要具备Node.js环境;安装node.js环境的方法这里不再介绍,请自行查找搭建即可;检查电脑是否安装node的方法,在终端里面输入命令行: node -v
回车,即可
node -v
然后输入命令行:npm install -g express
回车
npm install -g express
npm命令运行完毕,可以运行以下命令查看Express的版本号,如果出现则证明Express已经安装成功
express --version
在安装完Express之后,就可以使用Express命令来创建一个新的项目了
2. 使用Express创建项目
以下以 Mac 系统为例
(1) 桌面新建 demo
文件夹
(2) 打开终端,输入 cd
,然后将demo文件夹拖拽进终端内
(3) 执行创建命令,创建一个名为hello的Express项目
express hello
(4) 创建成功之后会在demo目录下出现一个名叫hello的目录,进入hello目录,然后安装依赖包
npm install
(5) 安装完毕后,执行命令启动应用
npm start
(6) 应用启动后,在浏览器中输入http://localhost:3000/ 网址就可以看到应用了
3. Express 项目结构分析
项目正常启动后,我们使用 vsCode 打开hello项目看一下它的目录结构,如图所示
看起来是不是结构比较简单?相比较其他语言的框架来说很轻量,这也是Node.js快速开发的一个特色
目录结构中的文件及其作用如下表所示
目录名/文件名 | 类型 | 作用 |
---|---|---|
bin | 目录 | 服务器脚本默认目录 |
bin / www | js | 服务器默认脚本,即启动服务脚本 |
node_modules | 目录 | 依赖包安装目录,用于存放依赖包 |
public | 目录 | 静态资源目录,用于存放静态资源 |
routes | 目录 | 路由目录,用于存放路由文件 |
routes / index | js | 首页路由文件 |
routes / users | js | 用户路由文件 |
views | 目录 | 页面目录,用于存放页面文件 |
views / error | jade | 错误页面 |
views / index | jade | 首页 |
views / layout | jade | 页面共用布局 |
app | js | 应用主文件 |
package | json | 项目配置文件 |
package-lock | json | 锁定的项目配置文件 |
下面我们重点给 app.js
添加一些注释分析,因为 app.js
文件相当于项目启动的主入口文件,有一些公共方法和服务器配置等信息
// http 处理模块
var createError = require('http-errors');
// 引入 Express
var express = require('express');
// 引入 path
var path = require('path');
// 引入 cookie 处理对象
var cookieParser = require('cookie-parser');
// 引入日志模块
var logger = require('morgan');
// 引入路由文件中的 index.js 文件
var indexRouter = require('./routes/index');
// 引入路由文件中的 users.js 文件
var usersRouter = require('./routes/users');
// 创建 Express 应用
var app = express();
// 定义页面目录
app.set('views', path.join(__dirname, 'views'));
// 定义页面模板引擎
app.set('view engine', 'jade');
// 定义日志打印级别
app.use(logger('dev'));
// 定义 JSON 格式处理数据
app.use(express.json());
// 定义使用 urlencoded 处理数据及 querystring 模块解析数据
app.use(express.urlencoded({ extended: false }));
// 定义使用 cookie 处理对象
app.use(cookieParser());
// 定义静态资源目录 public
app.use(express.static(path.join(__dirname, 'public')));
// 定义指向 index.js 的路由
app.use('/', indexRouter);
// 定义指向 user.js 的路由
app.use('/users', usersRouter);
// 定义 404 错误处理
app.use(function(req, res, next) {
next(createError(404));
});
// 定义其他错误处理
app.use(function(err, req, res, next) {
// 设置 locals ,只在开发环境生效
res.locals.message = err.message;
res.locals.error = req.app.get('env') === 'development' ? err : {};
// 返回错误 http 状态码
res.status(err.status || 500);
// 渲染错误页面
res.render('error');
});
module.exports = app;
4. Express 路由
接触到一款框架,首先要熟悉的就是它的路由;路由是指应用程序的端点(URI)如何响应客户端请求
通俗来说,就是定义什么路径来访问
4-1. GET 请求路由
在Express中定义路由特别简单。首先来看一下routes
目录中的index.js
路由文件,也就是首页路由文件,它是一个GET请求路由,如下:
// 引入 Express
var express = require('express');
// 引入 Express 路由对象
var router = express.Router();
// 首页路由
router.get('/', function(req, res, next) {
res.render('index', { title: 'Express' });
});
// 导出路由
module.exports = router;
首页路由文件很简单,其中也只定义了一个根目录的路由
将其中的代码:
res.render('index', { title: 'Express' });
更换成如下代码:
res.render('index', { title: Hello });
保存文件,重新运行 npm start
命令,用浏览器打开 http://localhost:3000 查看效果,因为在浏览器地址栏中输入地址发送的请求默认是GET请求
可以看到,之前的Express被换成了Hello
前面的一段代码定义了首页的路由,这个路由定义的是GET请求,请求的是“/”
路径,也就是根路径;接着后面跟着一个方法,当用户发送GET请求根路径的时候,就进入这个方法中,在这个方法中可以进行一些对请求的操作
Express默认的首页方法中使用了res.render()
方法,该方法的作用是渲染页面;其第一个参数就是页面的文件名,这里对应的就是views目录中的index.jade
文件;第二个参数是传入到页面文件的对象,可以将数据渲染到页面上
4-2. 自定义路由
分析过首页路由之后,可以试着自己写一个路由;在index.js
路由文件中添加如下代码:
// 定义一个GET请求“/world”的路由,执行方法
router.get('/world', function (req, res, next) {
// 渲染index页面
res.render('index', { title: 'Hello World' });
});
将项目重新 npm start
启动,打开浏览器请求地址 http://localhost:3000/world
新定义的路由生效了,是不是很简单呢?
但是每次更改过路由文件都要重新启动项目才会生效,是不是觉得太麻烦太影响效率了?
4-3. nodemon 工具使用
在这里,给大家推荐一个工具 nodemon ,其安装方式也是利用npm命令:
npm install -g nodemon
安装完成之后,修改项目根目录中的 package.json
文件,将其中的
"scripts": {
"start": "node ./bin/www"
},
修改成:
"scripts": {
"start": "nodemon ./bin/www"
},
然后再执行 npm start
命令启动项目,这样在路由文件被更改并保存之后,会自动重启项目,并可以立刻在浏览器中看到更改后的运行结果
🔔 nodemon 安装小提示
安装完 nodemon 后运行执行 npm start
命令启动项目如果出现下方错误
请按顺序执行以下代码
npm uninstall nodemon
sudo npm install -g --force nodemon
4-4. 其他请求方式的路由
HTTP请求方式除了GET之外,还有POST、PUT等其他方式;类似于GET请求的路由编写方式,其他请求方式也换成相应的 router
方法,如下:
// POST请求方式
router.post('/world', function (req, res, next) {
res.render('index', { title: 'Hello World' });
});
// PUT请求方式
router.put('/world', function (req, res, next) {
res.render('index', { title: 'Hello World' });
});
// DELETE请求方式
router.delete('/world', function (req, res, next) {
res.render('index', { title: 'Hello World' });
});
大家可以自己尝试一下相应的请求方式
4-5. 路由匹配规则
前面演示的路由匹配都是完整匹配,即定义“/world”
,在浏览器中要输入“/world”
才能匹配到
在Express中,除了完整匹配,还支持模糊匹配,例如:
router.get('/wes?t', function (req, res, next) {
res.render('index', { title: 'Hello West' });
});
会发现当请求 http://localhost:3000/west 和 http://localhost:3000/wet 时都可以成功
同样,模糊匹配还可以按如下处理:
能够匹配 /west
、/weest
、/weeest
等
router.get('/we+st', function (req, res, next) {
res.render('index', { title: 'Hello World' });
})
能够匹配 /west
、/wt
router.get('/w(es)? t', function (req, res, next) {
res.render('index', { title: 'Hello World' });
})
能够匹配 /west
、/we123st
、/wexxxst
等
router.get('/we*st', function (req, res, next) {
res.render('index', { title: 'Hello World' });
})
能够匹配 /west
、/we123st
、/wexxxst
等
router.get('/we*st', function (req, res, next) {
res.render('index', { title: 'Hello World' });
})
同时,Express路由还支持正则表达式,如下:
// 能够匹配路径中包含west的任何内容,如 /west、/aawest、/westee 等
router.get(/west/, function (req, res, next) {
res.render('index', { title: 'Hello World' });
})
4-6. 响应方法
下表中响应对象 res
的方法可以向客户机发送响应,并终止请求/响应循环;如果没有从路由处理程序调用其中任何方法,客户机请求将保持挂起状态
方法 | 描述 |
---|---|
res.download() | 提示将要下载文件 |
res.end() | 结束响应进程 |
res.json() | 发送 JSON 响应 |
res.jsonp() | 在 JSONP 的支持下发送 JSON 响应 |
res.redirect() | 重定向请求 |
res.render() | 呈现视图模板 |
res.send() | 发送各种类型的响应 |
res.sendFile() | 以八位元流形式发送文件 |
res.sendStatus() | 设置响应状态码并以响应主体形式发送其字符串表示 |
5. Express 页面
在前面定义路由的时候,用到的处理方法中有这么一行代码:
res.render('index', { title: 'Hello World' });
这行代码是Express的返回对象 Response
的一个 render()
方法,这个后面会讲
这里先来讲render()
方法的第一个参数,也就是需要渲染的模板文件名,也就是对应views
目录中的文件,即页面文件
在前面分析项目结构的时候已经说过,页面目录中有3个页面文件:index.jade
、error.jade
和layout.jade
;大家可能对 jade
这样的拓展名文件不太熟悉,其实它就是一种模板引擎,为了使用大家熟悉的HTML结构,通常在项目实际开发过程中会将其更换成便于理解的其他模板引擎,比如 art-template
等
5-1. 更换引擎模板
Express默认的模板引擎是 jade
,为了便于新用户上手开发,需要把它替换成更简洁、高效的 art-template
更换模板引擎的方法也很简单,首先需要安装 art-template
依赖包,分别执行以下命令:
npm install -S art-template
npm install -S express-art-template
两个依赖包都安装成功之后,修改项目根目录下的 app.js
文件,将其中的
// 定义页面模板引擎
app.set('view engine', 'jade');
```index
更换成
```js
// 定义页面模板引擎
// app.set('view engine', 'jade');
// 更换模板引擎成 art-template
app.engine('.html', require('express-art-template'));
app.set('view engine', 'html');
接着到 views
目录下新建一个 index.html
文件,如下:
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Kylin Technical Team</title>
</head>
<body>
<h3>《手把手带你从零开始学全栈 - Node. + Express + Vue 企业级开发实战》</h3>
</body>
</html>
然后去浏览器中预览一下效果,模板引擎已经更换成功
5-2. 渲染数据到页面上
目前的大前端,基本所有数据都是动态生成的;在前面的章节中说过,在Express中将数据渲染到页面上的方法是response
对象的 render()
方法的第二个参数,如下:
// 定义一个GET请求“/”的路由
router.get('/', function (req, res, next) {
// 渲染index页面,并传送变量title
res.render('index', { title: 'Hello' });
});
这段代码在渲染的时候向 index.html
页面中传入了一个值为 Hello
的 title
字段,但是在浏览器中预览却没有看到这个 Hello
,这是因为还没有编辑页面文件来接收这个 title
字段
打开 views
目录中的 index.html
文件,在其中的<body></body>
标签中添加以下代码:
<h4>这是设置的 Title 的值 : {{title}}</h4>
在浏览器中预览,效果如图所示
还可以多定义一些参数,让页面来接收,如下:
// 定义一个GET请求“/”的路由
router.get('/', function (req, res, next) {
// 渲染index页面,传入对象
res.render('index', {
title: 'Hello',
team: 'Kylin Technical Team',
name: '黄勇超',
age: 18
});
});
修改对应的页面文件 index.html
:
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Kylin Technical Team</title>
</head>
<body>
<h3>《手把手带你从零开始学全栈 - Node. + Express + Vue 企业级开发实战》</h3>
<h4>这是设置的 Title 的值 : {{title}}</h4>
<h4>大家好,我是 {{team}} 的 {{name}},我{{age}}岁了 </h4>
</body>
</html>
效果图如下
现在已经能够做到将数据渲染到页面上了,但是在实际开发需求中,必不可少地会根据不同的情况展示不同的页面,这个时候就需要用条件渲染了
5-3. 基本条件渲染
还是基于之前的示例,现在需要实现:
如果我的年龄大于等于30岁,就不展示“我今年XX岁了”这些文字,如果小于30岁,则展示文字
将 index.js
的路由代码修改为:
// 定义一个GET请求“/”的路由
router.get('/', function (req, res, next) {
// 渲染index页面,传入对象
res.render('index', {
title: 'Hello',
team: 'Kylin Technical Team',
name: '黄勇超',
age: 25
});
});
将对应的 index.html
页面文件修改为:
<! DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h2>{{title}}</h2>
<! -- 判断年龄小于30-->
{{if age<30 }}
<p>大家好,我是{{name}},我今年{{age}}岁,很高兴认识大家!</p>
{{/if}}
<! -- 判断年龄大于等于30-->
{{if age>=30 }}
<p>大家好,我是{{name}},很高兴认识大家!</p>
{{/if}}
</body>
</html>
页面效果如下
现在还是显示年龄的,将路由代码修改为:
// 定义一个GET请求“/”的路由
router.get('/', function (req, res, next) {
// 渲染index页面,传入对象
res.render('index', {
title: 'Hello',
team: 'Kylin Technical Team',
name: '黄勇超',
age: 32
});
});
再看看效果图
这时就不会显示年龄了,因为页面文件中的那段条件渲染代码生效了
在 art-template
中,如上述示例,if
判断有固定的写法,用{{if CONDITION}}
开头,以{{/if}}
结尾,将需要判断的代码放在其中
5-4. 嵌套条件渲染
除了基本的条件渲染外,很多时候往往存在复杂的情形,需要进行多层判断
art-template
也提供了嵌套条件渲染的方式,在条件判断里继续增加条件判断,即嵌套if判断,将页面文件代码更改如下:
<! DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h2>{{title}}</h2>
<! -- 判断年龄小于30-->
{{if age<30 }} <p>大家好,我是{{name}},我今年{{age}}岁,很高兴认识大家!</p>
{{/if}}
<! -- 判断年龄大于等于30-->
{{if age>=30 }}
<p>大家好,我是{{name}},
<! -- 判断happy字段是否为真 -->
{{if happy }}
<span>很高兴认识大家!</span>
{{/if}}
</p>
{{/if}}
</body>
</html>
路由修改如下
// 定义一个GET请求“/”的路由
router.get('/', function (req, res, next) {
// 渲染index页面,传入对象
res.render('index', {
title: 'Hello',
team: 'Kylin Technical Team',
name: '黄勇超',
age: 42,
happy:false
});
});
这样页面又发生了变化,如图所示
将 happy
的值设为 true
时,如图所示
在页面开发过程中,除了条件渲染,循环渲染也是一种常用的方式,最常见的就是渲染一个列表;在art-template
中,循环渲染也特别简单
5-5. 基本循环渲染
首先在路由中定义一个数组,数组中的每一项都是一个对象,需要将数组渲染到页面上,每一条数据对应数组中的一项;定义路由的代码如下:
// 定义一个GET请求“/”的路由
router.get('/', function(req, res, next) {
// 渲染index页面,传入渲染对象
res.render('index', {
title: 'Hello',
// 定义一个数组,每一项都是一个对象
list: [{
id: 1,
content: '今天天气不错'
}, {
id: 2,
content: '昨天你吃了什么?'
}, {
id: 3,
content: '工作好累'
}]
});
});
修改页面文件代码:
<! DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h2>{{title}}</h2>
<! -- 循环list,每一项为item -->
{{each list as item }}
<p>数据id:{{item.id}},内容:{{item.content}}</p>
{{/each}}
</body>
</html>
在浏览器中查看,效果图如下
特别简单是不是?跟条件渲染一样,在 art-template
中循环渲染也有固定的写法:
以 {{each LIST as ITEM}}
(LIST为数组字段,ITEM为数组中的每一项) 开头
以{{/each}}
结尾,需要循环的内容放在其中
5-6. 循环渲染结合条件渲染
在实际的业务场景中,通常不会简单地渲染列表,而往往需要在循环过程中进行一些判断,根据判断的结果展示不同的页面,也就是在循环渲染中增加条件渲染
下面结合上一节的条件渲染给大家展示一个综合的例子;定义路由代码:
// 定义一个GET请求“/”的路由
router.get('/', function (req, res, next) {
// 渲染index页面,传入渲染对象
res.render('index', {
title: 'Hello',
// 定义一个数组,每一项都是一个对象
list: [{
id: 1,
content: '今天天气不错'
}, {
id: 2,
content: '昨天你吃了什么?'
}, {
id: 3,
content: '工作好累'
}],
// 定义一个变量,表示当前被选中的id
targetId: 2
});
});
修改页面文件代码:
<! DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h2>{{title}}</h2>
<! -- 循环list,每一项为item -->
{{each list as item }}
<! -- 判断list中的每一项item的id属性是否等于targetId字段 -->
{{if item.id === targetId}}
<p style="color: #f00; ">数据id:{{item.id}},内容:{{item.content}}</p>
{{else}}
<p>数据id:{{item.id}},内容:{{item.content}}</p>
{{/if}}
{{/each}}
</body>
</html>
来看看效果
在这个示例中,对 list
这个数组字段进行了循环渲染,同时在循环中又进行了判断,只有当 list
中某一条数据的 id
值等于 targetId
这个字段值的时候,才将这一条数据展示为红色
转载自:https://juejin.cn/post/7155412145868374023