一. 前言
Node.js
对前端来说无疑具有里程碑意义,与其越来越流行的今天,掌握 Node.js 技术已经不仅仅是加分项,而是前端攻城师们必须要掌握的一项技能。而 Express 基于 Node.js 平台,快速、开放、极简的 Web 开发框架,成为 Node.js 最流行的框架,所以使用 Express 进行 web 服务端的开发是个不错且可信赖的选择。但是 Express 初始化后,并不马上就是一个开箱即用,各种功能完善的 web 服务端项目,例如:MySQL 连接、jwt-token 认证、md5 加密、中间件路由模块、异常错误处理、跨域配置、自动重启
等一系列常见的功能,需要开发者自己手动配置安装插件和工具来完善功能,如果你对 web 服务端开发或者 Express 框架不熟悉,那将是一项耗费巨大资源的工作。
本文作者根据项目实战经验已将底层服务架构搭建完成,并且本项目已在 github 开源,供大家学习参考使用(如有不足,还请批评指正),希望能减轻大家的工作量,更高效的完成工作,有更多时间提升自己的能力。
后端 API 接口源码地址 👉:https://github.com/jackchen0120/todo-nodejs-api
前端界面源码地址 👉:https://github.com/jackchen0120/todo-vue-admin
如果觉得本文还不错,记得点个 👍 或者给个 ❤️,你们的赞和 star 是作者编写更多更精彩文章的动力!
分享之前我们先来认识一下Node.js、Express
都是什么东东。
Node.js
简单的说 Node.js 就是运行在服务端的 JavaScript。
Node.js 是一个基于 Chrome JavaScript 运行时建立的一个平台。
Node.js 是一个事件驱动 I/O 服务端 JavaScript 环境,基于 Google 的 V8 引擎,V8 引擎执行 Javascript 的速度非常快,性能非常好。
Express
Express 是一个简洁而灵活的 Node.js Web 应用框架,提供了一系列强大特性帮助你创建各种 Web 应用,和丰富的 HTTP 工具。使用 Express 可以快速地搭建一个完整功能的网站。
Express 框架核心特性:
- 可以设置中间件来响应 HTTP 请求。
- 定义了路由表用于执行不同的 HTTP 请求动作。
- 可以通过向模板传递参数来动态渲染 HTML 页面。
二. 前后端分离
前端项目采用的技术栈是基于Vue + iView
,用 vue-cli 构建前端界面,后端项目采用的技术栈是基于Node.js + Express + MySQL
,用 Express 搭建的后端服务器。
Web3研习社 👉:[https://54web3.cc/)
部分效果截图
三. 前端部分
3.1 基础环境
开发前准备工作,相关运行环境配置如下:
工具名称 | 版本号 |
---|---|
node.js | 10.16.2 |
npm | 6.9.0 |
运行项目
1> 下载安装依赖
git clone https://github.com/jackchen0120/todo-vue-admin.git
cd todo-vue-admin
npm install 或 yarn
2> 开发模式
npm run serve
运行之后,访问地址:http://localhost:8082
3> 生产环境打包
npm run build
3.2 目录结构
│ package.json // npm包管理所需模块及配置信息
│ vue.config.js // webpack配置
├─public
│ favicon.ico // 图标
│ index.html // 入口html文件
└─src
│ App.vue // 根组件
│ main.js // 程序入口文件
├─assets // 存放公共图片文件夹
├─components
│ Footer.vue // 页面底部公用组件
│ Header.vue // 页面头部公用组件
├─router
│ index.js // 单页面路由注册组件
├─store
│ │ index.js // 状态管理仓库入口文件
│ └─modules
│ userInfo.js // 用户模块状态管理文件
├─styles
│ base.scss // 基础样式文件
├─utils
│ api.js // 统一封装API接口调用方法
│ network.js // axios封装与拦截器配置
│ url.js // 自动部署服务器环境
│ valid.js // 统一封装公用模块方法
└─views
Home.vue // 首页界面
Login.vue // 登录界面
3.3 技术栈
- vue2.6
- vue-router
- vuex
- axios
- webpack
- ES6/7
- flex
- iViewUI
3.4 功能模块
- 登录(登出)
- 注册
- 记住密码
- 忘记密码(修改密码)
- todoList 增删改查
- 点亮红星标记
- 查询条件筛选
3.5 代码实现
3.5.1 全局安装 vue-cli4
npm install -g @vue/cli
#安装指定版本
npm install -g @vue/[email protected]
#OR
yarn global add @vue/cli
3.5.2 vue-cli4 创建项目及运行
vue create todo-vue-admin
cd todo-vue-admin
npm run serve
3.5.3 开发配置
在项目根目录新增vue.config.js
文件,配置信息如图所示:
3.5.4 其他事项
按照上面的步骤完成脚手架搭建后,把需要的 axios、vue-router、view-design、sass-loader、node-sass 等依赖库安装配置好,准备开始上膛。
3.5.5 实现前端登录注册功能
首先在 views 文件夹下面新建login.vue
组件,编写一个静态的登录注册页面。登录成功后将登录返回的 token 保存到浏览器端并跳转到主页。views 文件夹下面新建home.vue
组件,显示登录成功后的页面,并获取用户基本信息,主页右上角显示用户头像、修改密码、退出登录等功能。代码如下:
<template>
<div class="login-container">
<div class="pageHeader">
<img src="../assets/logo.png" alt="logo">
<span>TODOList区块链管理平台</span>
</div>
<div class="login-box">
<div class="login-text" v-if="typeView != 2">
<a href="javascript:;" :class="typeView == 0 ? 'active' : ''" @click="handleTab(0)">登录</a>
<b>·</b>
<a href="javascript:;" :class="typeView == 1 ? 'active' : ''" @click="handleTab(1)">注册</a>
</div>
<!-- 登录模块 -->
<div class="right-content" v-show="typeView == 0">
<div class="input-box">
<input
autocomplete="off"
type="text"
class="input"
v-model="formLogin.userName"
placeholder="请输入登录邮箱/手机号"
/>
<input
autocomplete="off"
type="password"
class="input"
v-model="formLogin.userPwd"
maxlength="20"
@keyup.enter="login"
placeholder="请输入登录密码"
/>
</div>
<Button
class="loginBtn"
type="primary"
:disabled="isDisabled"
:loading="isLoading"
@click.stop="login">
立即登录
</Button>
<div class="option">
<Checkbox class="remember" v-model="checked" @on-change="checkChange">
<span class="checked">记住我</span>
</Checkbox>
<span class="forget-pwd" @click.stop="forgetPwd">忘记密码?</span>
</div>
</div>
<!-- 注册模块 -->
<div class="right_content" v-show="typeView == 1">
<div class="input-box">
<input
autocomplete="off"
type="text"
class="input"
v-model="formRegister.userName"
placeholder="请输入注册邮箱/手机号"
/>
<input
autocomplete="off"
type="password"
class="input"
v-model="formRegister.userPwd"
maxlength="20"
@keyup.enter="register"
placeholder="请输入密码"
/>
<input
autocomplete="off"
type="password"
class="input"
v-model="formRegister.userPwd2"
maxlength="20"
@keyup.enter="register"
placeholder="请再次确认密码"
/>
</div>
<Button
class="loginBtn"
type="primary"
:disabled="isRegAble"
:loading="isLoading"
@click.stop="register">
立即注册
</Button>
</div>
</div>
</div>
</template>
<style lang="scss" scoped>
.login-container {
background-image: url('../assets/bg.png');
background-position: center;
background-size: cover;
position: relative;
width: 100%;
height: 100%;
.pageHeader {
padding-top: 30px;
padding-left: 40px;
img {
vertical-align: middle;
display: inline-block;
margin-right: 15px;
}
span {
font-size: 18px;
display: inline-block;
vertical-align: -4px;
color: rgba(255, 255, 255, 1);
}
}
.login-box {
position: absolute;
left: 64vw;
top: 50%;
-webkit-transform: translateY(-50%);
transform: translateY(-50%);
box-sizing: border-box;
text-align: center;
box-shadow: 0 1px 11px rgba(0, 0, 0, 0.3);
border-radius: 2px;
width: 420px;
background: #fff;
padding: 45px 35px;
.option {
text-align: left;
margin-top: 18px;
.checked {
padding-left: 5px;
}
.forget-pwd, .goback {
float: right;
font-size: 14px;
font-weight: 400;
color: #4981f2;
line-height: 20px;
cursor: pointer;
}
.protocol {
color: #4981f2;
cursor: pointer;
}
}
.login-text {
width: 100%;
text-align: center;
padding: 0 0 30px;
font-size: 24px;
letter-spacing: 1px;
a {
padding: 10px;
color: #969696;
&.active {
font-weight: 600;
color: rgba(73, 129, 242, 1);
border-bottom: 2px solid rgba(73, 129, 242, 1);
}
&:hover {
border-bottom: 2px solid rgba(73, 129, 242, 1);
}
}
b {
padding: 10px;
}
}
.title {
font-weight: 600;
padding: 0 0 30px;
font-size: 24px;
letter-spacing: 1px;
color: rgba(73, 129, 242, 1);
}
.input-box {
.input {
&:nth-child(1) {
/*margin-top: 10px;*/
}
&:nth-child(2),
&:nth-child(3) {
margin-top: 20px;
}
}
}
.loginBtn {
width: 100%;
height: 45px;
margin-top: 40px;
font-size: 15px;
}
.input {
padding: 10px 0px;
font-size: 15px;
width: 350px;
color: #2c2e33;
outline: none; // 去除选中状态边框
border: 1px solid #fff;
border-bottom-color: #e7e7e7;
background-color: rgba(0, 0, 0, 0); // 透明背景
}
input:focus {
border-bottom-color: #0f52e0;
outline: none;
}
.button {
line-height: 45px;
cursor: pointer;
margin-top: 50px;
border: none;
outline: none;
height: 45px;
width: 350px;
background: rgba(216, 216, 216, 1);
border-radius: 2px;
color: white;
font-size: 15px;
}
}
// 滚动条样式
::-webkit-scrollbar {
width: 10px;
}
::-webkit-scrollbar-track {
-webkit-box-shadow: inset006pxrgba(0,