开发DOM更新算法
开发DOM更新算法
- 首先实现重新渲染视图的方法
update(data) {
if (!data || (Array.isArray(data) && data.length === 0))
return this.renderError();
this._data = data;
//调用_generateMarkup()方法生成新的HTML标记字符串
const newMarkup = this._generateMarkup();
//使用 document.createRange().createContextualFragment() 将字符串转换为文档片段(DOM)
const newDOM = document.createRange().createContextualFragment(newMarkup);
//使用 querySelectorAll('*') 获取新 DOM 中的所有元素
const newElements = newDOM.querySelectorAll('*');
console.log(newElements);
}
- 之后我们在更新份数的视图中进行更新一下
const controlServings = function (newServings) {
//更新菜谱份数(在状态上)
model.updateServings(newServings);
//更新菜谱视图
recipeView.update(model.state.recipe);
};
- 之后我们看下控制台的输出,会生成所有的虚拟DOM;
- 之后我们获取一下当前的DOM中的所有元素,并且都给他转换成数组,方便后续操作
const newElements = Array.from(newDOM.querySelectorAll('*'));
const curElements = Array.from(this._parentElement.querySelectorAll('*'));
- 接着就是差异比较和更新一下DOM了
update(data) {
if (!data || (Array.isArray(data) && data.length === 0))
return this.renderError();
this._data = data;
const newMarkup = this._generateMarkup();
const newDOM = document.createRange().createContextualFragment(newMarkup);
const newElements = Array.from(newDOM.querySelectorAll('*'));
const curElements = Array.from(this._parentElement.querySelectorAll('*'));
newElements.forEach((newEl, i) => {
const curEl = curElements[i];
if (
!newEl.isEqualNode(curEl) &&
newEl.firstChild?.nodeValue.trim() !== ''
) {
curEl.textContent = newEl.textContent;
}
});
}
上述代码的知识点回顾
- DocumentFragment: 轻量级的文档对象,不在主 DOM 树中,可以高效地进行 DOM 操作;
- createContextualFragment: 将 HTML 字符串解析为 DOM 片段的方法;
- isEqualNode: 比较两个节点是否相同的 DOM 方法;
- 虚拟 DOM 概念: 类似于 React 的虚拟 DOM 思想,先比较再局部更新;
- querySelectorAll(‘*’): 获取元素下的所有子元素
- 接着我们文本的局部更新应该没有什么问题了
- 接着继续更新属性
//更新改变的属性
if (!newEl.isEqualNode(curEl))
Array.from(newEl.attributes).forEach(attr =>
curEl.setAttribute(attr.name, attr.value)
);
});
}
- 我们可以同样使用上面的方法来实现当我们点击菜谱的时候,菜谱的状态是被选中的状态;
_generateMarkupPreview(result) {
const id = window.location.hash.slice(1);
return `
<li class="preview">
<a class="preview__link ${
result.id === id ? 'preview__link--active' : ''
}" href="#${result.id}">
<figure class="preview__fig">
<img src="${result.image}" alt="${result.title}" />
</figure>
<div class="preview__data">
<h4 class="preview__title">${result.title}</h4>
<p class="preview__publisher">${result.publisher}</p>
</div>
</a>
</li>`;
}
}
- 之后再控制器中调用该方法
//0当选中搜索结果时更新结果视图
resultsView.update(model.getSearchResultsPage());
- 但当我们更新的话,这个条线部分为真,所以会报错找不到食谱,我们去除掉就可以了
下面的文章我们就要来实现书签的功能了;