因此 React Fiber 将渲染和更新过程进行了拆解,简单来说,就是每次检查虚拟 DOM 的一小部分,在检查间隙会检查“是否还有时间继续执行下一个虚拟 DOM 树上某个分支任务”,同时观察是否有更优先的任务需要响应,如果“没有时间执行下一个虚拟 DOM 树上某个分支任务”,且有更高优先级,React 就会让出主线程,直到主线程“不忙”的时候继续执行任务。
addAt(index, item) {
let current = this.head
// 维护查找时当前节点的索引
let counter = 1
let node = new Node(item)
// 如果在头部插入
if (index === 0) {
this.head.prev = node
node.next = this.head
this.head = node
}
// 非头部插入,需要从头开始,找寻插入位置
else {
while(current) {
current = current.next
if( counter === index) {
node.prev = current.prev
current.prev.next = node
node.next = current
current.prev = node
}
counter++
}
}
}
remove 方法:
remove(item) {
let current = this.head
while (current) {
// 找到了目标节点
if (current.data === item ) {
// 目标链表只有当前目标项,即目标节点即是链表头又是链表尾
if (current == this.head && current == this.tail) {
this.head = null
this.tail = null
}
// 目标节点为链表头
else if (current == this.head ) {
this.head = this.head.next
this.head.prev = null
}
// 目标节点为链表尾部
else if (current == this.tail ) {
this.tail = this.tail.prev;
this.tail.next = null;
}
// 目标节点在链表收尾之间,中部
else {
current.prev.next = current.next;
current.next.prev = current.prev;
}
}
current = current.next
}
}
removeAt 方法:
removeAt(index) {
// 都是从“头”开始遍历
let current = this.head
let counter = 1
// 删除链表头部
if (index === 0 ) {
this.head = this.head.next
this.head.prev = null
}
else {
while(current) {
current = current.next
// 如果目标节点在链表尾
if (current == this.tail) {
this.tail = this.tail.prev
this.tail.next = null
}
else if (counter === index) {
current.prev.next = current.next
current.next.prev = current.prev
break
}
counter++
}
}
}
reverse 方法:
reverse() {
let current = this.head
let prev = null
while (current) {
let next = current.next
// 前后倒置
current.next = prev
current.prev = next
prev = current
current = next
}
this.tail = this.head
this.head = prev
}
swap 方法,交换两个节点数据值:
swap(index1, index2) {
// 使 index1 始终小于 index2,方便后面查找交换
if (index1 > index2) {
return this.swap(index2, index1)
}
let current = this.head
let counter = 0
let firstNode
while(current !== null) {
// 找到第一个节点,先存起来
if (counter === index1 ){
firstNode = current
}
// 找到第二个节点,进行数据交换
else if (counter === index2) {
// ES 提供了更简洁交换数据的方法,这里我们用传统方式实现,更为直观
let temp = current.data
current.data = firstNode.data
firstNode.data = temp
}
current = current.next
counter++
}
return true
}
export const addVertices = function (vert, g, svgId) {
const svg = d3.select(`[id="${svgId}"]`)
const keys = Object.keys(vert)
const styleFromStyleArr = function (styleStr, arr) {
// Create a compound style definition from the style definitions found for the node in the graph definition
for (let i = 0; i < arr.length; i++) {
if (typeof arr[i] !== 'undefined') {
styleStr = styleStr + arr[i] + ';'
}
}
return styleStr
}
// Iterate through each item in the vertex object (containing all the vertices found) in the graph definition
keys.forEach(function (id) {
const vertex = vert[id]
/**
* Variable for storing the classes for the vertex
* @type {string}
*/
let classStr = ''
if (vertex.classes.length > 0) {
classStr = vertex.classes.join(' ')
}
/**
* Variable for storing the extracted style for the vertex
* @type {string}
*/
let style = ''
// Create a compound style definition from the style definitions found for the node in the graph definition
style = styleFromStyleArr(style, vertex.styles)
// Use vertex id as text in the box if no text is provided by the graph definition
let vertexText = vertex.text !== undefined ? vertex.text : vertex.id
// We create a SVG label, either by delegating to addHtmlLabel or manually
let vertexNode
if (conf.htmlLabels) {
// TODO: addHtmlLabel accepts a labelStyle. Do we possibly have that?
const node = { label: vertexText.replace(/fa[lrsb]?:fa-[\w-]+/g, s => `<i class='${s.replace(':', ' ')}'></i>`) }
vertexNode = addHtmlLabel(svg, node).node()
vertexNode.parentNode.removeChild(vertexNode)
} else {
const svgLabel = document.createElementNS('http://www.w3.org/2000/svg', 'text')
const rows = vertexText.split(/<br[/]{0,1}>/)
for (let j = 0; j < rows.length; j++) {
const tspan = document.createElementNS('http://www.w3.org/2000/svg', 'tspan')
tspan.setAttributeNS('http://www.w3.org/XML/1998/namespace', 'xml:space', 'preserve')
tspan.setAttribute('dy', '1em')
tspan.setAttribute('x', '1')
tspan.textContent = rows[j]
svgLabel.appendChild(tspan)
}
vertexNode = svgLabel
}
// If the node has a link, we wrap it in a SVG link
if (vertex.link) {
const link = document.createElementNS('http://www.w3.org/2000/svg', 'a')
link.setAttributeNS('http://www.w3.org/2000/svg', 'href', vertex.link)
link.setAttributeNS('http://www.w3.org/2000/svg', 'rel', 'noopener')
link.appendChild(vertexNode)
vertexNode = link
}
let radious = 0
let _shape = ''
// Set the shape based parameters
switch (vertex.type) {
case 'round':
radious = 5
_shape = 'rect'
break
case 'square':
_shape = 'rect'
break
case 'diamond':
_shape = 'question'
break
case 'odd':
_shape = 'rect_left_inv_arrow'
break
case 'lean_right':
_shape = 'lean_right'
break
case 'lean_left':
_shape = 'lean_left'
break
case 'trapezoid':
_shape = 'trapezoid'
break
case 'inv_trapezoid':
_shape = 'inv_trapezoid'
break
case 'odd_right':
_shape = 'rect_left_inv_arrow'
break
case 'circle':
_shape = 'circle'
break
case 'ellipse':
_shape = 'ellipse'
break
case 'group':
_shape = 'rect'
break
default:
_shape = 'rect'
}
// Add the node
g.setNode(vertex.id, { labelType: 'svg', shape: _shape, label: vertexNode, rx: radious, ry: radious, 'class': classStr, style: style, id: vertex.id })
})
}
/**
* Add edges to graph based on parsed graph defninition
* @param {Object} edges The edges to add to the graph
* @param {Object} g The graph object
*/
export const addEdges = function (edges, g) {
let cnt = 0
let defaultStyle
if (typeof edges.defaultStyle !== 'undefined') {
defaultStyle = edges.defaultStyle.toString().replace(/,/g, ';')
}
edges.forEach(function (edge) {
cnt++
const edgeData = {}
// Set link type for rendering
if (edge.type === 'arrow_open') {
edgeData.arrowhead = 'none'
} else {
edgeData.arrowhead = 'normal'
}
let style = ''
if (typeof edge.style !== 'undefined') {
edge.style.forEach(function (s) {
style = style + s + ';'
})
} else {
switch (edge.stroke) {
case 'normal':
style = 'fill:none'
if (typeof defaultStyle !== 'undefined') {
style = defaultStyle
}
break
case 'dotted':
style = 'stroke: #333; fill:none;stroke-width:2px;stroke-dasharray:3;'
break
case 'thick':
style = 'stroke: #333; stroke-width: 3.5px;fill:none'
break
}
}
edgeData.style = style
if (typeof edge.interpolate !== 'undefined') {
edgeData.curve = interpolateToCurve(edge.interpolate, d3.curveLinear)
} else if (typeof edges.defaultInterpolate !== 'undefined') {
edgeData.curve = interpolateToCurve(edges.defaultInterpolate, d3.curveLinear)
} else {
edgeData.curve = interpolateToCurve(conf.curve, d3.curveLinear)
}
if (typeof edge.text === 'undefined') {
if (typeof edge.style !== 'undefined') {