什么是数据驱动?
数据驱动视图是指通过数据来驱动页面的显示和行为,使得页面的展示和交互元素可以根据数据的变化而自动更新。这种开发模式通常使用前端框架或库来实现,例如Vue.js、React等。简单来说,其实就是通过操作原始数据来进行显示更新页面,通过更改原始数据重新渲染页面。而并非使用dom操作。
数据驱动应用场景
数据驱动应用场景非常广泛,比如,在管理系统、电子商务平台、社交网络等复杂应用中,页面上的数据和用户交互元素需要频繁地更新和响应,数据驱动视图的开发模式能够极大地提高开发效率,并且使得页面的维护和拓展更加容易。
数据驱动案例(学生管理系统)
下面,我们来通过学生管理系统深入了解一下数据驱动,这个项目涉及到了增删改的操作,全部用的是数据驱动。
整体效果
我们先来看一下页面总体效果
页面结构:
学生管理系统的整体结构和布局
<div class="box">
<div class="top">
<h1>学生管理系统</h1>
<label for=""><span>*</span>用户名:<input id="username" type="text"></label>
<br />
<span class="validate-user red none">请输入正确的用户名</span>
<br />
<label for=""><span>*</span>密码:<input id="userpassword" type="text"></label>
<br />
<span class="validate-pwd red none">请输入正确的密码</span>
<br />
<label for=""><span>*</span>邮箱:<input id="useremail" type="text"></label>
<br />
<span class="validate-email red none">请输入正确的邮箱</span>
<br />
<button class="add">添加</button>
<button id="reset">重置</button>
</div>
<div class="bottom">
<table>
<thead>
<tr>
<td>编号</td>
<td>姓名</td>
<td>密码</td>
<td>邮箱</td>
<td>操作</td>
</tr>
</thead>
<tbody class="container">
</tbody>
</table>
</div>
<!-- 搭建渲染列表 -->
</div>
页面样式:
通过CSS对页面样式进行设计和美化
<style>
* {
margin: 0;
padding: 0;
}
.box {
width: 900px;
margin: 100px auto;
}
h1 {
text-align: center;
margin-bottom: 30px;
}
.top {
text-align: center;
width: 600px;
margin: auto;
}
.top label {
width: 600px;
color: #c5bebe;
height: 45px;
}
.top label input {
width: 510px;
display: flex;
float: right;
margin-right: 10px;
height: 45px;
border: 1px solid #c5bebe;
}
.top label span {
color: rgb(192, 64, 64);
line-height: 45px;
}
.add,
#reset {
position: relative;
right: 140px;
width: 80px;
height: 45px;
border-radius: 5px;
border: none;
cursor: pointer;
}
.add {
background-color: green;
color: #fff;
}
#reset {
background-color: rgb(15, 89, 201);
color: #fff;
}
.bottom {
width: 700px;
margin-left: 150px;
}
.bottom table {
margin: auto;
width: 800px;
line-height: 50px;
}
.bottom table tr {
width: 600px;
text-align: left;
font-size: 16px;
color: #c5bebe;
}
.edit,
#del {
width: 65px;
height: 35px;
color: #fff;
border: none;
border-radius: 5px;
cursor: pointer;
}
#del {
background-color: #d43d3d;
}
tbody tr td:nth-child(2),
tbody tr td:nth-child(3) {
width: 100px;
}
tbody tr td:nth-child(2) input,
tbody tr td:nth-child(3) input,
tbody tr td:nth-child(4) input {
width: 100px;
}
.validate-user,
.validate-pwd,
.validate-email {
width: 200px;
height: 20px;
line-height: 20px;
float: left;
margin-left: 30px;
}
.red {
font-size: 12px;
color: red;
}
.none {
display: none;
}
.inline {
display: '';
}
</style>
数据渲染:
将数据渲染到页面中,因为采用的是数据驱动的思想,其中还调用了几个自己封装的方法,比如删除,清除input框数据,添加数据,直接操作数据.
function $$(selector, all = false) {
if (typeof selector !== 'string') return
return all ? document.querySelectorAll(selector) : document.querySelector(selector)
}
// 先将页面渲染出来
let list = saveget('list').length ? saveget('list') : [
{ id: 1, name: '张三', password: '666', email: '123qq.com', isedit: false },
{ id: 2, name: '李四', password: '666', email: '123qq.com', isedit: false },
{ id: 3, name: '王五', password: '666', email: '123qq.com', isedit: false },
{ id: 4, name: '赵六', password: '666', email: '123qq.com', isedit: false },
]
// 将数据渲染到列表中
// 先获取要渲染的节点
// 因为渲染后续会用到所以为了提到代码的复用率,所以可以封转一下
function rander() {
let container = document.getElementsByClassName("container")[0]
container.innerHTML = list.map(function (item, index) {
// 编辑数据
// 为了实现点击编辑,添加两个标签,一个用于显示一个用于编辑,这个功能的本质其实就是两个标签之间的切换,
// 在输入框中输入数据,我们要拿到数据,然后切换标签,将源数据更改,最后将源数据重新渲染到页面中即可
return `
<tr>
<td>${item.id}</td>
<td>
<input class="user-name-input" style="display: ${item.isedit ? '' : 'none'};"value="${item.name}"/>
<span style="display: ${item.isedit ? 'none' : ''};">${item.name}</span>
</td>
<td>
<input class="user-pwd-input" style="display: ${item.isedit ? '' : 'none'};"value="${item.password}"/>
<span style="display: ${item.isedit ? 'none' : ''};">${item.password}</span>
</td>
<td>
<input class="user-email-input" style="display: ${item.isedit ? '' : 'none'};"value="${item.email}"/>
<span style="display: ${item.isedit ? 'none' : ''};">${item.email}</span>
</td>
<td>
<button style="background: ${item.isedit ? 'green' : 'blue'};" class="edit" data-id="${item.id}" data-type="${item.isedit ? 'ok' : 'edit'}" data-index="${index}">${item.isedit ? '完成' : '编辑'}</button>
<button id = "del" data-id="${item.id}">删除</button>
</td>
</tr>
`
}).join('')//加.join()是因为return默认是有分隔符的,用join可以将几个数据连接成一个字符串,所以那个分隔符就没有了
// 如果不放在这里一次只能删除一条数据,让在这里让他们同步
del()
useredit()
saveset('list', list) //删除操作执行完后再执行本地存储操作
}
rander()
添加数据与表单验证:
const username = document.getElementById("username")
const userpassword = document.getElementById("userpassword")
const useremail = document.getElementById("useremail")
const emailReg = /^[\w-]+(\.[\w-]+)*@([a-zA-Z0-9-]+\.)+(com|org|net|edu|gov|co|io|info|biz|me|us)$/
// const nameRegex = /^[A-Za-z\s'-]+$/;至少包含一个数字至少包含一个小写字母至少包含一个大写字母至少有 8 个字符长度
const passwordRegex = /^(?=.*\d)(?=.*[a-z])(?=.*[A-Z]).{8,}$/;
const nameRegex = /^[\u4E00-\u9FA5A-Za-z]+$/;
let add = document.getElementsByClassName("add")[0]
add.onclick = function () {
// 获取三个验证提示信息的dom节点
const validateuser = $$('.validate-user')
const validatepwd = $$(".validate-pwd")
const validateemail = $$(".validate-email")
// 创建一个开关
let flag = true
if (!username.value || !nameRegex.test(username.value)) {
validateuser.classList.remove('none')
validateuser.classList.add('inline')
} else {
validateuser.classList.add('none')
validateuser.classList.remove('inline')
}
if (!userpassword.value || !passwordRegex.test(userpassword.value)) {
validatepwd.classList.remove('none')
validatepwd.classList.add('inline')
} else {
validatepwd.classList.add('none')
validatepwd.classList.remove('inline')
}
if (!useremail.value || !emailReg.test(useremail.value)) {
validateemail.classList.remove('none')
validateemail.classList.add('iniline')
} else {
validateemail.classList.add('none')
validateemail.classList.remove('inline')
}
if (!flag) {
return false
}
// 将输入框中的值赋值给渲染函数
list = [...list, {
id: list.length + 1,
name: username.value,
password: userpassword.value,
email: useremail.value
}]
clear()//每次添加数据都要清空渲染列表,要不然会出现一次加好几个得情况
rander()//添加完数据以后因为更改了原始数据,但是页面没有发生变化,所以要更新页面,重新渲染
}
清除input框内容
function clear() {
let reset = document.querySelector('#reset')
username.value = ''
userpassword.value = ''
useremail.value = ''
}
clear()
删除数据:
// 删除数据
// 因为要复用,所以还是将这段代码封装一下
function del() {
$$('#del', true).forEach(el => [
el.addEventListener('click', function () {
if (confirm("此操作将永久删除该条数据,是否继续?")) {
// 操作数据
list = list.filter(item => item.id !== +this.dataset.id)
// 因为操作的是数据,所以删除完一条就要重新渲染一遍
rander()
}
})
])
}
编辑/更改数据
// 编辑
// 获取编辑按钮的节点
function useredit() {
$$(".edit", true).forEach(el => {
el.addEventListener('click', function () {
const id = this.dataset.id
const type = this.dataset.type
const index = this.dataset.index
list.forEach(item => {
if (+id === item.id) {
item.isedit = type !== 'ok'
type === 'ok' && (item.name = $$('.user-name-input', true)[index].value)
type === 'ok' && (item.password = $$('.user-pwd-input', true)[index].value)
type === 'ok' && (item.email = $$('.user-email-input', true)[index].value)
}
})
rander()
})
})
}
本地存储:
页面刷新数据不丢失
function saveset(key, value) {
window.localStorage.setItem(key, JSON.stringify(value))
}
function saveget(key) {
return JSON.parse(window.localStorage.getItem(key)) || []
}
感悟:通过这个项目,我获得了许多宝贵的经验与知识,我对数据驱动的思想有了更深刻的理解.,我掌握了实现响应式页面更新的技巧,当数据发生变化时,数据能够自动更新,提升了用户体验。并且我了解了本地存储技术,通过它实现了数据永久存储,避免了因页面刷新而导致数据丢失的问题,增强用户体验。这些收获让我更专注于业务逻辑处理,避免使用dom操作,提高开发效率。