vuejs 设计与实现 - 简单diff算法
DOM 复用与key的作用:
DOM 复用什么时候可复用?
- key 属性就像虚拟节点的“身份证”号,只要两个虚拟节点的 type属性值和 key 属性值都相同,那么我们就认为它们是相同的,即可以进行 DOM 的复用。即 我们通过【移动】来操作dom,而不是删除dom,创建dom。这样会更节省性能。
如下图展示了有key和无key时新旧两组子节点的映射情况:
如上图可知:如果没有 key,我们无法知道新子节点与旧子节点 间的映射关系,也就无法知道应该如何移动节点。有 key 的话情况则 不同,我们根据子节点的 key 属性,能够明确知道新子节点在旧子节 点中的位置,这样就可以进行相应的 DOM 移动操作了。
强调:DOM 可复用并不意味着不需要更新
.如下所示的2个虚拟节点:
const oldVNode = { type: 'p', key: 1, children: 'text 1' }
const newVNode = { type: 'p', key: 1, children: 'text 2' }
这两个虚拟节点拥有相同的 key 值和 vnode.type 属性值。这意 味着, 在更新时可以复用 DOM 元素,即只需要通过移动操作来完成更 新。但仍需要对这两个虚拟节点进行打补丁操作,
因为新的虚拟节点 (newVNode)的文本子节点的内容已经改变了(由’text 1’变成 ‘text 2’)。因此,在讨论如何移动DOM之前,我们需要先完成打补丁操作.
本节以下面的节点为例,进行简单diff算法:
const oldVNode = {type: 'div',children: [{ key: 1, type: 'p', children: '1' },{ key: 2, type: 'p', children: '2' },{ key: 3, type: 'p', children: '3' },]}const newVNode = {type: 'div',children: [{ key: 3, type: 'p', children: '3' },{ key: 2, type: 'p', children: '2' },{ key: 1, type: 'p', children: '1' },]}
每一次寻找可复用的节点时,都会记录该可复用 节点在旧的一组子节点中的位置索引。
找到需要移动的元素
// 1.找到需要移动的元素
function patchChildren(n1, n2) {const oldChildren = n1.childrenconst newChildren = n2.childrenlet lastIndex = 0for (let i = 0; i < newChildren.length; i++) {const newVNode = newChildren[i]for (j = 0; j < oldChildren.length; j++) {const oldVNode = oldChildren[j]if (newVNode.key === oldVNode.key) {// 移动DOM之前,我们需要先完成打补丁操作patch(oldVNode, newVNode, container)if (j < lastIndex) {console.log('需要移动的节点', newVNode, oldVNode, j)} else {lastIndex = j}break;}}}
}
patchChildren(oldVNode, newVNode)
如何移动元素
更新的过程:
第一步:取新的一组子节点中第一个节点 p-3,它的 key 为 3,尝试在旧的一组子节点中找到具有相同 key 值的可复用节点。发现能够找到,并且该节点在旧的一组子节点中的索引为 2。此时变量 lastIndex 的值为 0,索引 2 不小于 0,所以节点 p-3 对应的真实 DOM 不需要移动,但需要更新变量 lastIndex 的值为2。
第二步:取新的一组子节点中第二个节点 p-1,它的 key 为 1,尝试在旧的一组子节点中找到具有相同 key 值的可复用节点。发
现能够找到,并且该节点在旧的一组子节点中的索引为 0。此时变量 lastIndex 的值为 2,索引 0 小于 2,所以节点 p-1 对应的真实 DOM 需要移动。
到了这一步,我们发现,节点 p-1 对应的真实 DOM 需要移动,但应该移动到哪里呢?我们知道, children的顺序其实就是更新后真实DOM节点应有的顺序。所以p-1在新children 中的位置就代表了真实 DOM 更新后的位置。由于节点p-1在新children中排在节点p-3后面,所以我们应该把节点p-1 所对应的真实DOM移到节点p-3所对应的真实DOM后面。
可以看到,这样操作之后,此时真实 DOM 的顺序为 p-2、p-3、p-1。
第三步:取新的一组子节点中第三个节点 p-2,它的 key 为 2。尝试在旧的一组子节点中找到具有相同 key 值的可复用节点。发现能够找到,并且该节点在旧的一组子节点中的索引为 1。此时变量 lastIndex 的值为 2,索引 1 小于 2,所以节点 p-2 对应的真实 DOM 需要移动。
如下图移动节点:
第三步与第二步类似,节点 p-2 对应的真实 DOM 也需要移动。 面后同样,由于节点 p-2 在新 children 中排在节点 p-1 后面,所以我们应该把节点 p-2 对应的真实 DOM 移动到节点 p-1 对应的真实DOM 后面。移动后的结果如图下图所示:
经过这一步移动操作之后,我们发现,真实 DOM 的顺序与新的一组子节点的顺序相同了:p-3、p-1、p-2。至此,更新操作完成。
function patchChildren(n1, n2) {const oldChildren = n1.childrenconst newChildren = n2.childrenlet lastIndex = 0for (let i = 0; i < newChildren.length; i++) {const newVNode = newChildren[i]for (j = 0; j < oldChildren.length; j++) {const oldVNode = oldChildren[j]if (newVNode.key === oldVNode.key) {// 移动DOM之前,我们需要先完成打补丁操作patch(oldVNode, newVNode, container)if (j < lastIndex) {// console.log('需要移动的节点', newVNode, oldVNode, j)// 如何移动元素const prevVNode = newChildren[i - 1]if (prevVNode) {// 2.找到 prevVNode 所对应真实 DOM 的下一个兄 弟节点,并将其作为锚点const anchor = prevVNode?.el?.nextSiblingconsole.log('插入', prevVNode, anchor)}} else {lastIndex = j}break;}}}
}
patchChildren(oldVNode, newVNode)
在上面这段代码中,如果条件j < lastIndex成立,则说明当 前 newVNode 所对应的真实 DOM 需要移动。根据前文的分析可知, 我们需要获取当前 newVNode 节点的前一个虚拟节点,即 newChildren[i - 1],然后使用insert函数完成节点的移动, 其中 insert 函数依赖浏览器原生的 insertBefore 函数。
添加新元素
function patchChildren(n1, n2) {const oldChildren = n1.childrenconst newChildren = n2.childrenlet lastIndex = 0for (let i = 0; i < newChildren.length; i++) {// 在第一层循环中定义变量 find,代表是否在旧的一组子节点中找到可复用的节点let find = falseconst newVNode = newChildren[i]for (j = 0; j < oldChildren.length; j++) {const oldVNode = oldChildren[j]if (newVNode.key === oldVNode.key) {// 一旦找到可复用的节点,则将变量 find 的值设为 truefind = trueif (j < lastIndex) {// console.log('需要移动的节点', newVNode, oldVNode, j)const prevVNode = newChildren[i - 1]if (prevVNode) {// 2.找到 prevVNode 所对应真实 DOM 的下一个兄 弟节点,并将其作为锚点const anchor = prevVNode?.el?.nextSiblingconsole.log('插入', prevVNode, anchor)}} else {lastIndex = j}break;}}// 添加元素// 如果代码运行到这里,find 仍然为 false,说明当前newVNode没有在旧的一组子节点中找到可复用的节点,也就是说,当前newVNode是新增节点,需要挂载if (!find) {// 为了将节点挂载到正确位置,我们需要先获取锚点元素// 首先获取当前 newVNode 的前一个 vnode 节点const prevVNode = newChildren[i - 1] let anchor = nullif (prevVNode) {// 如果有前一个 vnode 节点,则使用它的下一个兄弟节点作为锚点元 anchor = prevVNode.el.nextSibling} else {// 如果没有前一个 vnode 节点,说明即将挂载的新节点是第一个子节// // 这时我们使用容器元素的 firstChild 作为锚点anchor = container.firstChild}// 挂载 newVNodepatch(null, newVNode, container, anchor)}}
}
patchChildren(oldVNode, newVNode)
移除不存在的元素
// 4.移除不存在的元素
function patchChildren(n1, n2) {const oldChildren = n1.childrenconst newChildren = n2.childrenlet lastIndex = 0for (let i = 0; i < newChildren.length; i++) {// 在第一层循环中定义变量 find,代表是否在旧的一组子节点中找到可复用的节点let find = falseconst newVNode = newChildren[i]for (j = 0; j < oldChildren.length; j++) {const oldVNode = oldChildren[j]if (newVNode.key === oldVNode.key) {// 一旦找到可复用的节点,则将变量 find 的值设为 truefind = trueif (j < lastIndex) {// console.log('需要移动的节点', newVNode, oldVNode, j)const prevVNode = newChildren[i - 1]if (prevVNode) {// 2.找到 prevVNode 所对应真实 DOM 的下一个兄 弟节点,并将其作为锚点const anchor = prevVNode?.el?.nextSiblingconsole.log('插入', prevVNode, anchor)}} else {lastIndex = j}break;}}// 如果代码运行到这里,find 仍然为 false,说明当前newVNode没有在旧的一组子节点中找到可复用的节点,也就是说,当前newVNode是新增节点,需要挂载if (!find) {const prevVNode = newChildren[i - 1] }}// 移除不存在的元素for (let i = 0; i < oldChildren.length; i++) {const oldVNode = oldChildren[i]const has = newChildren.find(vnode => vnode.key === oldVNode.key)// 如果没有找到具有相同 key 值的节点,则说明需要删除该节点if (!has) {// 调用 unmount 函数将其卸载unmount(oldVNode)}}
}
patchChildren(oldVNode, newVNode)
相关文章:
![](https://img-blog.csdnimg.cn/d4198030e4924d97b1ccd994bcf90b6b.png)
vuejs 设计与实现 - 简单diff算法
DOM 复用与key的作用: DOM 复用什么时候可复用? key 属性就像虚拟节点的“身份证”号,只要两个虚拟节点的 type属性值和 key 属性值都相同,那么我们就认为它们是相同的,即可以进行 DOM 的复用。即 我们通过【移动】来…...
![](https://img-blog.csdnimg.cn/b167ab2c5af64de9951a98f34b93e256.png)
【前端|Javascript第3篇】探秘JavaScript的作用域与作用域链:小白也能轻松搞懂!
大家好!欢迎来到本篇博客,今天我们将解开JavaScript编程世界中的一道神秘面纱:作用域与作用域链。很多Javascript开发者并不真正理解它们,但这些概念对掌握Javascript至关重要。如果你对这些概念感到困惑,不要担心&…...
![](https://www.ngui.cc/images/no-images.jpg)
【Spring AOP】结合日志面向切面编程 两种写法
概念 这里需要提前了解什么是Spring的AOP(Aspect Oriented Programming)。是在OOP(面向对象)思想的一种拓展思想。简单来说就是将某个代码块嵌入到其它的代码块中。笔者先前学Spring也有学什么IoC啊AOP啊,但实际上没有…...
![](https://img-blog.csdnimg.cn/img_convert/4ad26c1c410b34b18a683c729b564582.jpeg)
C#在自动化领域的应用前景与潜力
人机界面(HMI)开发:使用C#开发人机界面软件,实现与自动化设备的交互和监控。C#的图形界面设计能力和丰富的控件库使得开发人员能够创建直观、易用的界面。 数据采集与处理:C#可以与各种传感器、设备进行数据通信和采集…...
![](https://img-blog.csdnimg.cn/a80fe11e958a41ba8ce1a881ce56e06d.gif#pic_center)
string模拟实现:
string模拟实现: 上一篇博客,我们对String类有了一个基本的认识,本篇博客我们来从0~1去模拟实现一个String类,当然我们实现的都是一些常用的接口。 ❓我们这里定义了一个string类型,然后STL标准库里面也有string&#…...
![](https://www.ngui.cc/images/no-images.jpg)
系统与软件安全研究(八)
FUZZ101入门 Detail gcc,clang,llvm都有啥区别GCC (GNU Compiler Collection), Clang, 和 LLVM 都是用于编译代码的工具链。它们在某些方面有相似之处,但也有一些重要的区别。 GCC (GNU Compiler Collection):GCC 是由 GNU 组织开发的,是一个非常流行的开源编译器集合。它…...
![](https://img-blog.csdnimg.cn/img_convert/741bd520edd94429faa51eb53975efe2.png)
jmeter测试rpc接口-使用dubbo框架调用【杭州多测师_王sir】
1.基于SOAP架构。基于XML规范。基于WebService协议。特点:接口地址?wsdl结尾2.基于RPC架构,基于dubbo协议,thrift协议。SpringCloud微服务。3.基于RestFul架构,基于json规范。基于http协议(我们常用的都是这种,cms平台也是) Rest…...
![](https://img-blog.csdnimg.cn/769d67b7c6f74eab8a2f29a6329d29f3.png#pic_center)
Java8中forEach()里使用return的效果
先总结:使用forEach()处理集合时不能使用break和continue这两个方法,可以使用无返回值的return跳出此次循环,效果同标准for循环的continue。 首先,forEach()先对入参判空,然后使用增强for循环调用action.accept(t)&am…...
![](https://img-blog.csdnimg.cn/fccfcf8be66340cd9e1cdd9f2ee4ee40.png)
MVC配置原理
如果你想保存springboot的mvc配置并且还想自己添加自己的配置就用这个。 视图解析器原理,它会从IOC容器里获取配置好视图解析器的配置类里的视图解析器集合, 然后遍历集合,生成一个一个的视图对象,放入候选 视图里,…...
![](https://www.ngui.cc/images/no-images.jpg)
rabbitmq安装
安装erlang方案二 vi /etc/yum.repos.d/rabbitmq-erlang.repo 文件内容: In /etc/yum.repos.d/rabbitmq-erlang.repo [rabbitmq-erlang] namerabbitmq-erlang baseurlhttps://dl.bintray.com/rabbitmq-erlang/rpm/erlang/22/el/7 gpgcheck1 gpgkeyhttps://dl.bi…...
![](https://img-blog.csdnimg.cn/fb7d0ed7e0834cc8a4e756f01838bcfe.png)
轻松抓取网页内容!API助力开发者,快速数据采集
在如今这个信息爆炸的时代,人们需要从各种渠道获取数据来支持自己的业务需求。而对于开发者们来说,如何快速、准确地从互联网上抓取所需的数据也成为了一项重要的技能。而抓取网页内容 API 则是一种能够帮助开发者轻松实现数据抓取的工具。 一、什么是抓…...
![](https://img-blog.csdnimg.cn/8f73f0aa3a304036b312268084333cc1.png)
CSDN 直播:腾讯云大数据 ES 结合 AI 大模型与向量检索的新一代云端检索分析引擎 8月-8号 19:00-20:30
本次沙龙围绕腾讯云大数据ES产品展开,重点介绍了腾讯云ES自研的存算分离技术,以及能与AI大模型和文本搜索深度结合的高性能向量检索能力。同时,本次沙龙还将为我们全方位介绍腾讯云ES重磅推出的Elasticsearch Serverless服务,期待…...
![](https://www.ngui.cc/images/no-images.jpg)
区块链智能合约代码示例
以下是一个简单的区块链智能合约代码示例: pragma solidity ^0.4.17;contract SimpleContract {uint public myData;function setMyData(uint newData) public {myData newData;} }该合约具有以下功能: 定义了一个名为 SimpleContract 的合约。定义了一…...
![](https://img-blog.csdnimg.cn/img_convert/f99b80613d00525a27cde4cadb9c475e.png)
Spring Boot介绍--快速入门--约定优于配置
文章目录 SpringBoot 基本介绍官方文档Spring Boot 是什么?SpringBoot 快速入门需求/图解说明完成步骤快速入门小结 Spring SpringMVC SpringBoot 的关系总结梳理关系如何理解-约定优于配置 SpringBoot 基本介绍 官方文档 官网: https://spring.io/projects/spring-boot 学习…...
![](https://img-blog.csdnimg.cn/9d5f05e9db0a4733805fd3f6f9f6636b.png)
Scons编译lib库
实例目录结构: include文件夹:test.hsrc文件夹:test.cSConscriptSConstruct 如下图所示: SConstruct: #执行当前目录下的SConscript SConscript(SConscript);SConscript: import os from SCons.Script…...
![](https://img-blog.csdnimg.cn/6d793acb79b445a79572f0870c92eb87.png)
React源码解析18(1)------ React.createElement 和 jsx
1.React.createElement 我们知道在React17版本之前,我们在项目中是一定需要引入react的。 import React from “react” 即便我们有时候没有使用到React,也需要引入。原因是什么呢? 在React项目中,如果我们使用了模板语法JSX&am…...
![](https://img-blog.csdnimg.cn/aca698b317a448deb2cfacb12a23a030.png)
系列3-常见的高可用MySQL解决方案
高可用主要解决两个问题,如何实现数据共享和同步数据、如何处理failover,数据共享的解决方案一般是SAN,数据同步通过rsync和drbd技术来实现。 1、主从复制解决方案 这是MySQL自身的高可用解决方案,数据同步方法采用的是MySQL rep…...
![](https://www.ngui.cc/images/no-images.jpg)
C#登录后携带cookie爬取数据
前一段时间,公司以前的一个数据采集任务突然之间采集下来的数据都是0了,也就是未登录状态能够获取到的数据,于是猜想肯定是网站的服务升级了,升级了数据接口的逻辑,于是便开始解决此问题。 此采集程序是由.net core开…...
![](https://www.ngui.cc/images/no-images.jpg)
自动驾驶国家新一代人工智能开放创新平台产业化应用
【摘要】:当前,全球新一轮科技革命和产业变革正孕育兴起,自动驾驶作为人工智能最重要的应用载体之一,对于加快交通强国、智能汽车强国建设,具有十分突出的战略意义。我国自动驾驶研发应用,面临技术、资金、应用等诸多挑战,为此,需要打造一套符合我国国情的自动驾驶系统…...
![](https://img-blog.csdnimg.cn/69d34eee608f44b4b4deff02fb197b94.png)
Maven分模块-继承-聚合-私服的高级用法
Maven分模块-继承-聚合-私服的高级用法 JavaWeb知识,介绍Maven的高级用法!!! 文章目录 Maven分模块-继承-聚合-私服的高级用法1. 分模块设计与开发1.1 介绍1.2 实践1.2.1 分析1.2.2 实现 1.3 总结 2. 继承与聚合2.1 继承2.1.1 继承…...
![](https://img-blog.csdnimg.cn/ca67b0c9cad549a09401267791d9719b.png)
Spring 是如何解决循环依赖问题的?
项目场景: 提示:这里简述项目相关背景: 例如:项目场景:示例:通过蓝牙芯片(HC-05)与手机 APP 通信,每隔 5s 传输一批传感器数据(不是很大) 问题描述 我们都知道,如果在代码中,将两个…...
![](https://img-blog.csdnimg.cn/img_convert/a2861737434238e8a061f17e8b02b2d8.png)
Spring-2-深入理解Spring 注解依赖注入(DI):简化Java应用程序开发
今日目标 掌握纯注解开发依赖注入(DI)模式 学习使用纯注解进行第三方Bean注入 1 注解开发依赖注入(DI)【重点】 问题导入 思考:如何使用注解方式将Bean对象注入到类中 1.1 使用Autowired注解开启自动装配模式(按类型) Service public class StudentS…...
![](https://www.ngui.cc/images/no-images.jpg)
java 强密码验证策略工具类
java 强密码验证策略工具类 package com.neusoft.caeid.common.utils;import java.util.regex.Matcher; import java.util.regex.Pattern;/*** author dume*/ public class PasswordUtil {public static final String REGEX "^\\S*(?\\S{6,})(?\\S*\\d)(?\\S*[a-zA-Z…...
![](https://img-blog.csdnimg.cn/80d5857bea8c402a8aa65b7404cc40f2.png)
CI/CD—K8S 基本理解与部署
1 K8S 是什么 Kubernetes 是一款容器的编排调度工具,来源于 Google 开源的 Brog 系统。Kubernetes简称K8S,是用8代替8个字符 “ubernete” 而成的缩写,用于管理云平台中多个主机上的容器化的应用,Kubernetes 的目标是让部署容器化…...
![](https://img-blog.csdnimg.cn/5f559b0aef1b41359302ceff92ce47d6.png)
2023网络安全常用工具汇总(附学习资料+工具安装包)
几十年来,攻击方、白帽和安全从业者的工具不断演进,成为网络安全长河中最具技术特色的灯塔,并在一定程度上左右着网络安全产业发展和演进的方向,成为不可或缺的关键要素之一。 话不多说,网络安全10款常用工具如下 1、…...
![](https://img-blog.csdnimg.cn/9b73cc1a99ae4a09a53107475464f67f.png#pic_center)
OpenStack监控工具
OpenStack是一个开源的云计算管理平台项目,是一系列软件开源项目的组合。由NASA和Rackspace合作研发并发起,以Apache许可证(Apache软件基金会发布的一个自由软件许可证)授权。 OpenStack为私有云和公有云提供可扩展的弹性的云计算…...
![](https://www.ngui.cc/images/no-images.jpg)
讲解密码学综合应用
密码学综合应用是指将密码学的理论和技术应用于各种场景中,以保障信息的安全性、完整性和可靠性。密码学的应用范围非常广泛,包括通信安全、网络安全、电子商务、数字签名、认证、密钥管理等。下面将简要介绍一些密码学综合应用的实例: 1. 加…...
![](https://img-blog.csdnimg.cn/3025399ab5894f2f985ce6c074f8d1ee.png)
Flamingo
基于已有的图像模型和文本模型构建多模态模型。输入是图像、视频和文本,输出是文本。 Vision encoder来自预训练的NormalizerFree ResNet (NFNet),之后经过图文对比损失学习。图片经过图像模型的输出是2D grid,视频按1FPS的频率采样后经过图…...
![](https://img-blog.csdnimg.cn/5d3159b673774ea284b709c04b8e8657.png)
Leetcode-每日一题【剑指 Offer 12. 矩阵中的路径】
题目 单词必须按照字母顺序,通过相邻的单元格内的字母构成,其中“相邻”单元格是那些水平相邻或垂直相邻的单元格。同一个单元格内的字母不允许被重复使用。 例如,在下面的 34 的矩阵中包含单词 "ABCCED"(单词中的字母…...
![](https://img-blog.csdnimg.cn/7af21aa95aaf49e8809405e43388dc0b.png)
安全渗透知识总结二
目录 一、html实体编码 1、Unicode字符编码 2、字符的数字表示 3、常见实体编码 4、url 协议 主机 http状态码 http常用的状态码 端口 常见协议端口 查询参数 锚点 url字符 urlcode字符 绝对url和相对url 二、字符编码 Ascll字符集 html字符集 html的url编码 …...
![](https://img-blog.csdnimg.cn/20200709170227163.png)
苏宁易购网站建设的思路/最近三天的新闻大事摘抄
给定一个二叉树, 找到该树中两个指定节点的最近公共祖先。 百度百科中最近公共祖先的定义为:“对于有根树 T 的两个结点 p 、q ,最近公共祖先表示为一个结点 x ,满足 x 是 p、q 的祖先且 x 的深度尽可能大(一个节点也可以是它自己…...
![](https://img-blog.csdnimg.cn/c6403051f3a840c08f1135fe3b265667.png)
东营网站备案代理公司/北京高端网站建设
文章目录rabbitmq 从入门到精通消息队列介绍1.1 介绍1.2 MQ解决什么问题应用解耦流量消峰消息分发异步消息1.3 常见消息队列及比较Rabbitmq安装2.1 服务端原生安装2.2 服务端Docker安装2.3 客户端安装2.4 设置用户和密码基于Queue实现生产者消费者模型基本使用(生产…...
![](/images/no-images.jpg)
百度蜘蛛网站排名/网络营销方案设计范文
我们常用的在a标签中有点击事件: 1. a href"javascript:js_method();" 这是我们平台上常用的方法,但是这种方法在传递this等参数的时候很容易出问题,而且javascript:协议作为a的href属性的时候不仅会导致不必要的触发window.onbef…...
![](https://s1.51cto.com/images/20180423/1524481748835860.jpg?x-oss-process=image/watermark,size_16,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_100,g_se,x_10,y_10,shadow_90,type_ZmFuZ3poZW5naGVpdGk=)
湖南网络科技有限公司/免费seo课程
3月15日,腾讯AI Lab第二届学术论坛在深圳举行,聚焦人工智能在医疗、游戏、多媒体内容、人机交互等四大领域的跨界研究与应用。全球30位顶级AI专家出席,对多项前沿研究成果进行了深入探讨与交流。腾讯AI Lab还宣布了2018三大核心战略ÿ…...
![](/images/no-images.jpg)
企业服务网/vue seo优化
课前作业如果你对 promise 、reflux 还不那么熟悉,请先行了解他们是什么,有什么用。ReFlux细说大白话讲解Promise现状分析首先我们来絮叨絮叨后端同学是怎么写代码的。首先絮叨一下经典问题,MVC用户看到viewview ——————————》 cont…...
![](http://static.oschina.net/uploads/img/201305/17163445_s1Bj.jpg)
私自使用他人图片做网站宣传/推广软文
为什么80%的码农都做不了架构师?>>> 用来保存计算最终结果的 数据库是整个信息系统的重要组成部分, 技术也相对成熟。然而,对于所有数据库而言,除了记录正确的 处理结果之外,也面临着一些挑战:…...