徒手撸极简前后端分离Demo!吃透原生JS动态渲染底层
之前一直觉得前后端分离是个特别高大上的工程化概念,总以为得学一堆框架、接口规范、部署流程才能上手。
直到昨天我没用Vue、没用React,纯靠原生JS+HTML+CSS+json-server,手写了一套最朴素的前后端分离小案例,瞬间把底层逻辑彻底打通了。
👇
完整文章地址:https://juejin.cn/post/7642981046925918208
没有花里胡哨的封装,所有流程都是最原始的写法,反而让我搞懂了框架背后到底在帮我们做什么。今天把我踩坑、试错、顿悟的全过程分享出来。
先说说我想解决的问题
以前写前端页面,所有数据都是写死在HTML里的。
展示一个用户表格,就得手动写一堆<tr>,新增、修改、删除数据都要改页面代码,特别笨拙,完全不符合实际开发逻辑。
我就想实现一个最基础的效果:数据单独存、前端动态拉取、自动渲染页面,真正做到数据和页面结构分离。
不想搭复杂的 Java、Python 后端,偶然发现json-server这个神器,零代码就能搭本地API接口,新手练手前后端分离再合适不过。
5分钟搭建极简模拟后端
说实话,以前总觉得后端搭建很麻烦,这次实操完才知道,本地练手的后端居然这么简单。
全程就三个文件,分工超级清晰,这也是我第一次真切体会到模块化拆分的意义。
1. 初始化项目配置
新建一个 backend 文件夹,执行npm init -y初始化项目,生成package.json。这个文件就是项目的“身份证”,记录所有依赖和启动脚本。
然后安装核心依赖npm i json-server,最后配置启动命令,完整配置如下:
{ "name": "backend", "version": "1.0.0", "description": "", "main": "index.js", "scripts": { "dev":"json-server --watch db.json" // 启动本地模拟接口服务 }, "keywords": [], "author": "", "license": "ISC", "type": "commonjs", "dependencies": { "json-server": "^1.0.0-beta.15" } }2. 搭建模拟数据库
新建db.json,直接写入模拟用户数据,json-server 会自动把这个文件当成数据库,对外提供接口。
{ "users":[ { "id": 1, "name": "张三", "hometown": "杭州", "nikename": "小三" }, { "id": 2, "name": "李四", "hometown": "南昌", "nikename": "小四" }, { "id": 3, "name": "王五", "hometown": "上海", "nikename": "小五" } ] }终端执行npm run dev,直接启动服务,访问http://localhost:3000/users就能拿到所有用户数据。一行后端代码没写,接口直接可用,太适合新手练手了。
用Bootstrap重构页面,改掉Div满天飞的坏习惯
之前写页面,我清一色全用 div,堆得乱七八糟,自己回头看都看不懂结构。这次刻意用了 HTML5 语义化标签 + Bootstrap 栅格布局,彻底改掉了烂习惯。
语义化标签的真香之处
这次页面我用了header、main、aside、footer,表格也严格区分thead和tbody。
以前觉得这些标签没用,和 div 没区别,后来才懂大厂为啥特别看重语义化:
- 结构一目了然,哪个是头部、主体、侧边栏清晰分明
- 搜索引擎抓取页面更友好,利于SEO
- 表格拆分表头和表体,是规范开发的基础,后续DOM操作更精准
Bootstrap栅格布局快速适配页面
手动写CSS居中、自适应布局太麻烦,Bootstrap的栅格系统直接开箱即用。
我用了container做页面居中留白,row定义行,col-md-6 col-md-offset-3实现表格居中展示,几行类名搞定布局,不用写一行自定义CSS。
完整HTML页面代码:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>动态数据表格</title> <!-- 引入Bootstrap样式,快速实现页面布局 --> <link href="https://cdn.bootcdn.net/ajax/libs/twitter-bootstrap/3.0.3/css/bootstrap.min.css" rel="stylesheet"> </head> <body> <header>111</header> <!-- 核心主体区域 --> <main class="container"> <aside>333</aside> <!-- 栅格布局:居中6列表格 --> <div class="row col-md-6 col-md-offset-3"> <table class="table table-striped" id="user-table"> <thead class="container"> <tr> <td>ID</td> <td>姓名</td> <td>家乡</td> <td>昵称</td> </tr> </thead> <tbody> <!-- JS动态渲染数据,这里留白 --> </tbody> </table> </div> <aside>444</aside> </main> <footer>222</footer> <!-- 引入自定义JS逻辑 --> <script src="./common.js"></script> </body> </html>踩坑半小时!终于弄懂JS异步执行逻辑
这是本次学习最大的卡点,也是新手最容易犯的错!
我第一版代码写完,控制台能打印出接口数据,但页面空白、无任何渲染内容,当时直接懵了。
错误写法(千万别这么写)
我当时把数据遍历渲染的代码,写在了 fetch 请求的外面:
let users=[]; // 异步请求数据 fetch('http://localhost:3000/users') .then(data => data.json()) .then(data => { console.log(data); // 控制台能正常打印数据 users=data; }) // 同步遍历渲染(坑死我了!) const oBody = document.querySelector('.table tbody'); for(let user of users){ oBody.innerHTML += ` <tr> <td>${user.id}</td> <td>${user.name}</td> <td>${user.nikename}</td> <td>${user.hometown}</td> </tr> ` }我百思不得其解:数据明明拿到了,为啥渲染不出来?
后来翻了执行逻辑才恍然大悟:JS是单线程,同步代码优先执行,异步代码后置执行。
页面加载后,会先执行外面的 for 循环,这时候 fetch 接口还在请求中,users还是空数组。等接口请求成功、赋值完数据,页面早就渲染结束了。
执行步骤(按时间顺序)
- 定义空数组
users = [] - 发起
fetch请求 →丢到异步队列,不等待,继续走后面代码 - 立刻执行同步的 for 循环→
users是空的,页面啥也不渲染 - 过了一会儿,网络请求成功 → 执行
.then→ 给users赋值 - 但渲染代码早就跑完了!页面永远不会更新
结果
- 控制台能打印数据(赋值成功了)
- 页面无任何数据(渲染时数据还没回来)
正确写法(所有依赖接口数据的操作,放进回调)
let users=[]; const oBody = document.querySelector('.table tbody'); // 异步获取数据 + 动态渲染,全部放在回调内部 fetch('http://localhost:3000/users') .then(data => data.json()) .then(data => { console.log(data); users=data; // 数据回来后,再执行DOM渲染 for(let user of users){ oBody.innerHTML += ` <tr> <td>${user.id}</td> <td>${user.name}</td> <td>${user.nikename}</td> <td>${user.hometown}</td> </tr> ` } })执行步骤
- 定义变量、获取 DOM
- 发起
fetch请求 → 丢到异步队列 - 等待网络请求……(期间不执行任何代码)
- 数据成功返回→ 执行
.then - 给
users赋值 →再执行 for 循环渲染 - 页面正常显示数据
结果
- 控制台打印数据
- 页面正常渲染所有用户信息
一句话总结
- 第一段:不等数据回来,直接渲染→ 白跑一趟,没数据
- 第二段:等数据回来,再渲染→ 逻辑正确,页面生效
这就是 JavaScript 异步编程最基础、最容易踩的坑!所有依赖异步数据的操作(渲染 DOM、计算、判断等),必须写在异步回调(then/async-await)里面。
新手核心避坑点:只要是接口请求、定时器这类异步操作,后续所有依赖返回结果的逻辑,必须写在异步回调里面!
原生DOM编程,吃透框架底层原理
现在的框架都帮我们封装好了DOM渲染,很多人只会用框架,根本不懂底层怎么实现的。这次纯原生手写,我彻底搞懂了动态渲染的本质。
简单说下我的通俗理解:
DOM 就是浏览器把所有HTML标签,转换成JS内存里的一棵树状对象。我们通过querySelector精准选中页面节点,再用innerHTML动态拼接HTML结构,把接口数据批量插入页面。
对比老式的索引for循环,ES6的for...of真的太香了,不用管下标、不用赋值,直接遍历数组元素,代码简洁可读性拉满,这也是现在主流的遍历写法。
最后聊聊模块化开发的感悟
以前写代码,所有结构、样式、逻辑全堆在一个文件里,看着简洁,实则一坨乱麻,后续根本没法维护扩展。
这次我严格做了拆分:
- HTML:只负责页面结构,只管长什么样
- CSS/外部Bootstrap:只负责页面样式,只管好不好看
- 独立JS文件:只负责逻辑、数据请求、DOM渲染
- json文件:只负责存储数据
这就是最基础的模块化思想:每个文件只做一件事,各司其职,解耦分离。所有大厂的工程化项目,底层都是这个逻辑。
收尾:本次学习3个核心收获
折腾完这个小demo,比我看十篇理论文章收获都大,总结三个最实用的知识点:
1.前后端分离的本质超简单:前端专注视图渲染,后端专注提供数据接口,数据和页面彻底解耦,这就是所有前后端项目的核心逻辑。
JS异步执行是重中之重:同步先行、异步后置,所有依赖接口返回值的逻辑,绝对不能写在异步代码外部。
基础远比框架重要:Vue/React只是封装了DOM操作和异步逻辑,吃透原生DOM、异步机制、语义化开发,才算真正懂前端。
最后提一句:json-server 只适合新手练手、本地调试,没有权限校验、数据持久化简陋,千万别用到线上正式项目。
其实前端很多看似难懂的概念,亲手撸一遍demo就彻底通透了。你们初学前端的时候,有没有踩过JS异步、DOM渲染的坑?评论区聊聊~
