前端的全栈开发之旅(启航篇),一分钟实现todo list后台

栏目: 数据库 · 发布时间: 4年前

内容简介:做为一名前端开发工程师,时常纠结于是否要学习后端开发,成为一名真正的全栈,可是node.js好像很难啊,深入浅出node.js这本书成功劝退了许多想学全栈的同学,因为一开始就把后端开发最艰深的一面展示了出来,如模块和内存管理等,虽然对于有更高追求的开发者来说, 这些原理肯定是掌握的越深入越好,但是对于普通的后台业务开发来说,只需要会用能实现业务就可以了, 我们今天就从todo list项目开始,揭开后台开发的神秘面纱,帮你走上全栈开发工程师之路。本教程适合有一定前端基础、轻微了解node.js的开发人员(其

做为一名前端开发工程师,时常纠结于是否要学习后端开发,成为一名真正的全栈,可是node.js好像很难啊,深入浅出node.js这本书成功劝退了许多想学全栈的同学,因为一开始就把后端开发最艰深的一面展示了出来,如模块和内存管理等,虽然对于有更高追求的开发者来说, 这些原理肯定是掌握的越深入越好,但是对于普通的后台业务开发来说,只需要会用能实现业务就可以了, 我们今天就从todo list项目开始,揭开后台开发的神秘面纱,帮你走上全栈开发工程师之路。

本教程适合有一定前端基础、轻微了解node.js的开发人员(其实只需要知道npm安装和引用模块就已足够)

我们打算从零开始,步步为营逐渐完善整个项目代码

  1. 第一步,先实现一个最简化的纯静态版本的todolist
  2. 第二步,建立后台接口,用mock测试数据填充,让页面先能够正常显示出来
  3. 第三步,设计数据库,将测试数据改为真正的接口

其实这也是真实项目的开发流程,现在的前后台开发都是分离的,前后台开发先按照接口约定各自开发代码,前端用静态数据模拟(mock)能够完成99%以上的功能开发,后端都是纯数据,自己单元测试就好了,全部开发完毕之后,前后台对接部署在测试环境。

迈步从头跃,前端纯静态版todolist

为了保持教程的简单,前端代码用jquery实现,先建立一个index.html的空白文件,在页面引入jquery,并添加一个ul元素 做为容器。加一个文本框和添加按钮,html代码如下:

<ul id="list"></ul>
<input type="text" id="title" placeholder="输入待办事项">
<button id="btn_add">添加</button>
复制代码

现在还没有后台数据库,数据只能先用静态的数组来定义,每条记录有id,title,status 三个属性,分别表示事项的编号、标题、是否已完成。 定义一个数组存放用测试数据,然后用forEach循环该数组,对html字符串进行拼接,最后合并html生成dom,该示例用了ES6的模板字符串,方便书写和演示。

代码如下:

function render(){
        var html="";
        var result=[
        {id:1,title:"hello",status:0},
        {id:2,title:"hello",status:0} 
        ];
        result.forEach(function(item){
            var checked=item.status>0?"checked":"";
            var li=`<li itemId="${item.id}">
                <input type="checkbox" ${checked}>
                <input type="textbox" value="${item.title}">
                <a href="javascript:"  class="delete">删除</a>
            </li>`
            html+=li;
        })
        $("#list").html(html);
       
    
}
$(function (){
    render();
})
复制代码

运行 index.html,成功显示出页面。

前端的全栈开发之旅(启航篇),一分钟实现todo list后台

第二次迭代,用mock数据,连接真实的后台接口

好了,静态页面渲染已经写好,要迈出后台开发的第一步了,把静态数据改成调用ajax 接口,对render函数稍做改造即可

function render(){
    $.post("http://localhost/todo/list",{},function (result){
        var html="";
        result.forEach(function(item){
            var checked=item.status>0?"checked":"";
            var li=`<li itemId="${item.id}">
                <input type="checkbox" ${checked}>
                <input type="textbox" value="${item.title}">
                <a href="javascript:"  class="delete">删除</a>
            </li>`
            html+=li;
        })
        $("#list").html(html);
    });
}
复制代码

但是问题是现在还没有后台服务器,而且本地的html文件发送ajax请求会遭遇跨域错误,有在本地做过开发的同学一定知道ajax会产生cors异常,因为本地的文件是用file://协议打开的,如果访问http://协议的页面,会因为同源策略导致跨域错误,同源策略要求:

  1. 协议相同
  2. 域名相同
  3. 端口相同

控制台的报错信息如下图所示:

前端的全栈开发之旅(启航篇),一分钟实现todo list后台

这个问题其实是有办法解决的,只要后台接口实现了cors跨域,就可以畅通无阻的调用了,这样你的代码不用部署到远程服务器,就可以调用远程服务器的接口,非常的方便。那么先来搭建个http服务实现cors吧,少年!我们今天不用express,也不用koa,因为他们都没有mock功能,跨域也要额外编写很多代码。因此我自己封装了一个,名为webcontext,github地址: github.com/windyfancy/…

通过npm可以安装,我们在硬盘上建个目录,例如todo_sample,用于存放后台项目,切换进入该文件夹,运行npm install安装

npm install --save webcontext
复制代码

安装好之后,在项目根目录建一个app.js,用于启动http服务,写两行代码:

app.js

const WebContext = require('webcontext');
const app = new WebContext();
复制代码

运行node app.js,http服务就启动了,然后访问http://localhost/,能够输出hello信息,表示http服务已经搭建成功!

接下来,在项目根目录/service目录中建立一个todo子目录,将在todo目录中建立一个list.ejs空白文件,目录结构如下

|-- service                          
|     |--todo
|         |--list.ejs
复制代码

把之前的静态数据存入list.ejs中,它其实是个ejs模板文件,当然也可以存放json

[
    {id:1,title:"hello",status:0},
    {id:2,title:"hello",status:0}
]
复制代码

现在再来用浏览器直接访问: http://localhost/todo/list ,已经可以直接输了该文件的内容了,但是用本地的index.html调用这个接口仍然是返回跨域错误的,为了安全考虑, webcontext 跨域的配置默认是关闭的,我们打开项目根目录的web.config.json(首次运行时会自动生成),修改cors属性中的allowOrigin字段值为*即可开启跨域

"cors":{
        "allowOrigin":"*"
    }
复制代码

现在再来访问本地的index.html,发现已经可以请求成功了,http 响应头正确的输出了cors 跨域的信息。

前端的全栈开发之旅(启航篇),一分钟实现todo list后台

战前准备工作,设计 mysql 数据库

上一步完成了后台http接口的搭建,用mock静态数据验证了todolist的加载功能正常。终于到了激动人心的数据库开发环节了,想想马上就可以做个高大上的CURD boy了,走上人生巅峰,出任CEO,心情真是有点小激动呢。

简单了解一下吧,顾名思义,数据库是用来存放数据的地方,目前主流的数据库是关系数据库,如mysql、oracle、sql server等,以行列结构存储一张张表的数据,就如同一个excel表格,每一张表是一个独立的sheet,我们把刚才静态的json数据转换成表的形式如下:

id title status
1 hello 0
2 world 0

以mysql为例,来定义一下这张todo_list表的结构,共有3个字段:

  • id:表示待办事项的编号,数值类型,在数据库中以int类型表示,它是每条记录的唯一标识,即主键
  • title:表示待办事项的名称,字符串类型,在数据库中以varchar变长字符串类型表示
  • status:表示待办事项的状态,布尔类型,为了便于扩展我们定义成数据类型,也用int类型表示

我们来建表吧,首先肯定要安装mysql了

上官方网站https://www.mysql.com/downloads/ 下载安装一下,完整安装一下。装 好之后,就 可以用自带的workbench连接数据库了。用你刚才安装时初始设定的密码连接一下:

前端的全栈开发之旅(启航篇),一分钟实现todo list后台

连接上之后,数据库还是空的呢,要先新建一个数据库(schema),名称为todo_db,然后在这个数据库中新建一个表,可以用 工具 栏或菜单中的create new table快速创建一张表:

前端的全栈开发之旅(启航篇),一分钟实现todo list后台

当然也可以用 sql 代码去创建表:

CREATE TABLE `todo_list` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `title` varchar(45) DEFAULT NULL,
  `status` int(11) DEFAULT NULL,
  PRIMARY KEY (`id`)
)
复制代码

创建好数据库表之后,我们尝试用代码连接数据库,webcontext框架已经集成了数据库的连接,只需要配置一下,在app.js启动时就可以自动连接数据库了,不用编写任何额外的代码

修改web.config.json,设置数据库连接参数

"database":{ 
        "host":"127.0.0.1",
        "port":"3306",
        "user":"root",
        "password":"你的密码",
        "database":"todo_db"
    } 
复制代码

注:MySQL8.0以上版本密码认证协议发生了改变,需要用mysql workbench执行如下代码才能使用node.js连接数据库,sql代码如下: ALTER USER 'root'@'localhost' IDENTIFIED WITH mysql_native_password BY '你的密码';

好了,现在重新运行一下node app.js,就可以自动连接数据库了

第三次迭代,编写数据库查询保存接口,实现CURD

数据库连接成功后,我们要测试一下,能否正常查询和写入数据,因为现在数据库还是空的,我们先从插入数据开始。

什么是CURD?

它代表数据库的 创建(Create)、更新(Update)、读取(Retrieve)和删除(Delete) 4个基本操作。对于关系型数据库,可以通过sql (结构化查询语言 Structured Query Language 简称SQL) 来编写代码支持这4个基本操作,分别对应insert,update,select,delete 4条语句。

webcontext已经对insert,update,select,delete进行了封装,因此对于简单的读写操作,不需要编写原始的sql语句了。

insert

在项目目录创建一个js文件,/service/todo/add.js,webcontext实现了页面地址自动路由,不用手工编写路由代码,当访问http://localhost/service/todo/add时,会自动调用这个路径(/service/todo/add.js)文件中的onLoad方法,为add.js并编写如下代码:

module.exports= {
    async onLoad() {
        await this.database.insert("todo_list",{  title:"hello",status:0});
        this.render({code:"OK"})
    }
}
复制代码

只有4行代码, 我们来逐行解释一下

  1. module.exports:表示导出这个对象,这样webcontext框架才能自动引用到它,这个对象会自动从Context类继承,你不用编写任何extend或修改prototype的代码 , webcontext框架内部实现了对/service目录下的文件自动添加路由和自动继承的功能。不用任何的require和extend,具体原理也非常简单,可以看下我的这篇文章->: Node.js 实现类似于.php,.jsp的服务器页面技术,自动路由
  2. async onLoad 函数:首先,这是一个异步函数,由于第3行代码访问数据库用了await,所以这里必须要加async关键字,然后onLoad是一个事件,也可以说是一个回调函数,它表示这个函数的代码是在后台接收到http请求后执行。
  3. this.database.insert, 由于当前文件自动继承自Context类,可以通过this获取到请求对象request、响应对象response,以及database对象,database对象封装了基本的curd操作。insert方法第一个参数表示要插入的表名,第二个参数是一个对象,表示要插入的字段名和字段值,为了便于测试,我们先用死数据测试一下 { title:"hello",status:0}
  4. this.render 将传入的字符串或对象输出到http响应。传入object的话会自动stringify。

写好之后,我们来访问http://localhost/service/todo/add,然后使用mysql workbench查看一下数据库,发现数据已经成功入库了。 测试成功后,我们把这行写死的数据改过来吧,this.request.data["title"]表示获取post表单中的title字段。

await this.database.insert("todo_list",{  title:this.request.data["title"],status:0});
复制代码

select

现在数据库里已经有数据了,我们要把请求mock的接口改成读取真实数据。删掉list.ejs(当然不删留着它做活口也是可以的) 在/service/todo目录新建一个list.js文件,书写代码如下:

/service/todo/list.js

module.exports= {
    async onLoad() {
        var result=await this.database.select("todo_list")
        this.render(result); 
    }
}
复制代码

不用过多解释了,套路和上一个页面一样,你唯一要改的就是把insert方法改成select方法,这个例子比较简单,只需要传一个表名就可以了。实际上select方法已经封装的非常强大,支持各种where条件、 排序 、数据库层分页、多表连接等,暂不展开讲述。

update

/service/todo/update.js

module.exports= {
    async onLoad() {
        await this.database.update("todo_list",this.request.data)
        this.render({code:"OK"})
    }
}
复制代码

update和insert写法几乎一样,为了增加点新鲜感,第二个参数直接用this.request.data表单数据了,这样可以节省很多代码,但是如果别人恶意post不合法的数据的话你的代码会报错。

delete

/service/todo/delete.js

module.exports= {
    async onLoad() {
        await this.database.delete("todo_list",{ id:this.request.data["id"]})
        this.render({code:"OK"})
    }
}
复制代码

删除,只需要表名和id参数

第四次迭代,增加添加,保存和删除前端代码

好了,现在CURD操作都已经完成了,业务代码只有8行,其实可以更少,因为mysql 支持replace into语句,insert 和 update可以合二为一。即使加上一些参数的合法性校验,代码量也是非常少的。现在前后台分离之后,数据库业务的后台开发 真的要比前端要简单很多,除了一些多表连接和统计的sql语句比较难写之外,其它都是重复度很高的数据操作代码,不过话说回来,自从前端普及了vue,react之后,开发门槛也降低了很多。

现在后台接口都完成了,但是添加、修改、删除的前端代码还没有实现,我们用事件委托的方式添加dom操作和ajax请求的代码

function saveItem(target){
    var li=$(target).parents("li");
        var id=li.attr("itemId")
        var title=li.find("input[type=textbox]").val();
        var status=li.find("input[type=checkbox]").prop("checked")?1:0;
        $.post("http://localhost/todo/update",{id:id,title:title,status:status},function (res){
            if(res.code="OK"){
                render();
            }
        });
}
function deleteItem(target){
    var id=$(target).parents("li").attr("itemId")
    $.post("http://localhost/todo/delete",{id:id},function (res){
        if(res.code="OK"){
            render();
        }
    });
}
$("#list").on("change","input[type=textbox]",function (e){
    saveItem(e.target);
})
$("#list").on("click","input[type=checkbox]",function (e){
    saveItem(e.target)
})
$("#list").on("click",".delete",function (e){
    deleteItem(e.target)
})
$("#btn_add").click("click",function (e){
    $.post("http://localhost/todo/add",{title:$("#title").val()},function (res){
        if(res.code="OK"){
            render();
        }
    });
})
复制代码

清理战场 ,静态文件迁移部署到http服务器

现在所有代码都已经完工,可是html和js文件总不能一直放在本地吧,webcontext已经内置了静态文件服务,只需要把本地的index.html和jquery.js存放在站点根目录下/frontend目录下,再来访问http://localhost/index.html,就可以访问到了。 具体原理请参考: Node.js 实现类似于.php,.jsp的服务器页面技术,自动路由

最后,既然已经都在同一个http路径下了,可以把代码里$.post的绝对路径都改成相对路径。

总结

本文用了8行代码实现了todolist 后台接口,为什么只需要这么少的代码,源于webcontext的设计理念:约定优于配置,默认配置优于手工配置,配置优于编码,将开发者的用户体验放在第一位。

举例来说:

  1. 它实现了服务器页面技术,不再需要添加路由的代码,因为页面地址本身已经包含了路由信息,一个文件处理一个请求,也便于解耦,定义路由本身就属于画蛇添足的行为
  2. 配置了数据库连接字符串,自动连接数据库,一行代码实现CURD,也实现了ORM数据库实体映射功能,实现了不需要写sql就可以操作数据库。
  3. 默认配置文件是自动生成的,大部分情况下都不需要修改这些配置。
  4. 对于表单、上传、json,各个环节都是自动解析为对象的,甚至可以将表单数据直接写入数据库,省去中间转换参数的冗余代码

webcontext的结构设计参考了asp.net的优雅设计,用一个空的context对象容器,引用request,response,session这些http请求处理操作类,即使在ejs模板中,也可以通过this获取到context,轻易进行各种操作,并进行了扩展,增加了数据库访问,日志写入器等对象 主要结构如下:

前端的全栈开发之旅(启航篇),一分钟实现todo list后台

虽然提供了如此多的功能,它的代码却非常精简,只有千行左右,很容易读懂,它虽然强大,却不是阳春白春,并不高深,如果你想了解一个web框架是如何铸成的,不妨来读一下它的源码,如果你认为有用,请不要吝惜你的star,它现在还是一棵小树,需要你的支持。

github地址: github.com/windyfancy/…

本文的全部代码也已经上传github,在webcontext_examples项目中,有需要的同学可以自行查阅。


以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Ruby元编程(第2版)

Ruby元编程(第2版)

[意] Paolo Perrotta / 廖志刚 / 华中科技大学出版社 / 2015-8-1 / 68.80

《Ruby元编程(第2版)》在大量剖析实例代码的基础上循序渐进地介绍Ruby特有的实用编程技巧。通过分析案例、讲解例题、回顾Ruby类库的实现细节,作者不仅向读者展示了元编程的优势及其解决问题的方式,更详细列出33种发挥其优势的编程技巧。本书堪称动态语言设计模式。Ruby之父松本行弘作序推荐。一起来看看 《Ruby元编程(第2版)》 这本书的介绍吧!

HTML 编码/解码
HTML 编码/解码

HTML 编码/解码

正则表达式在线测试
正则表达式在线测试

正则表达式在线测试