重构MVC
上一篇文章简单的介绍了一下关于MVC架构的信息,本篇我们将实际的在我们的项目中实际的应用一下;
- 第一步就是创建文件;
- 接着就按照上篇文章最后的图片来完成就可以了
export const state = {
recipe: {},
};
export const loadRecipe = async function (id) {
const res = await fetch(
`https://forkify-api.herokuapp.com/api/v2/recipes/${id}`
);
const data = await res.json();
if (!res.ok) throw new Error(`${data.message} (${res.status})`);
let { recipe } = data.data;
recipe = {
id: recipe.id,
title: recipe.title,
publisher: recipe.publisher,
sourceUrl: recipe.source_url,
image: recipe.image_url,
servings: recipe.servings,
cookingTime: recipe.cooking_time,
ingredients: recipe.ingredients,
};
console.log(recipe);
};
- 然后我们在控制器中导入model
import * as model from './model.js';
- 之后我们就可以调用了,然后我们把加载食谱的给加上
await model.loadRecipe(id);
- model中缺少了错误处理的代码,我们加上
export const loadRecipe = async function (id) {
try {
const res = await fetch(
`https://forkify-api.herokuapp.com/api/v2/recipes/${id}`
);
const data = await res.json();
if (!res.ok) throw new Error(`${data.message} (${res.status})`);
const { recipe } = data.data;
state.recipe = {
id: recipe.id,
title: recipe.title,
publisher: recipe.publisher,
sourceUrl: recipe.source_url,
image: recipe.image_url,
servings: recipe.servings,
cookingTime: recipe.cooking_time,
ingredients: recipe.ingredients,
};
console.log(state.recipe);
} catch (err) {
alert(err);
}
};
const { recipe } = model.state;先暂时加上运行看看效果
- 接下来我们处理视图,之后导出并导入
class RecipeView {
#parentElement = document.querySelector('.recipe');
}
export default new RecipeView();
import recipeView from './view/recipeView.js';
- 我们首先在控制器将视图渲染出来
//(2)渲染菜谱
recipeView.render(model.state.recipe);
- 然后我们在视图中将渲染的代码写出来
import icons from 'url:../../img/icons.svg';
class RecipeView {
#parentElement = document.querySelector('.recipe');
#data;
render(data) {
this.#data = data;
const markup = this.#generateMarkup();
this.#clear();
this.#parentElement.insertAdjacentHTML('afterbegin', markup);
}
#clear() {
this.#parentElement.innerHTML = '';
}
renderSpinner = function () {
const markup = `
<div class="spinner">
<svg>
<use href="${icons}#icon-loader"></use>
</svg>
</div>`;
this.#parentElement.innerHTML = '';
this.#parentElement.insertAdjacentHTML('afterbegin', markup);
};
#generateMarkup() {
return `
<figure class="recipe__fig">
<img src="${this.#data.image}" alt="Tomato" class="recipe__img" />
<h1 class="recipe__title">
<span>${this.#data.title}</span>
</h1>
</figure>
<div class="recipe__details">
<div class="recipe__info">
<svg class="recipe__info-icon">
<use href="${icons}#icon-clock"></use>
</svg>
<span class="recipe__info-data recipe__info-data--minutes">${
this.#data.cookingTime
}</span>
<span class="recipe__info-text">minutes</span>
</div>
<div class="recipe__info">
<svg class="recipe__info-icon">
<use href="${icons}#icon-users"></use>
</svg>
<span class="recipe__info-data recipe__info-data--people">${
this.#data.servings
}</span>
<span class="recipe__info-text">servings</span>
<div class="recipe__info-buttons">
<button class="btn--tiny btn--increase-servings">
<svg>
<use href="${icons}#icon-minus-circle"></use>
</svg>
</button>
<button class="btn--tiny btn--increase-servings">
<svg>
<use href="${icons}#icon-plus-circle"></use>
</svg>
</button>
</div>
</div>
<div class="recipe__user-generated">
<svg>
<use href="${icons}#icon-user"></use>
</svg>
</div>
<button class="btn--round">
<svg class="">
<use href="${icons}#icon-bookmark-fill"></use>
</svg>
</button>
</div>
<div class="recipe__ingredients">
<h2 class="heading--2">Recipe ingredients</h2>
<ul class="recipe__ingredient-list">
${this.#data.ingredients
.map(ing => {
return `
<li class="recipe__ingredient">
<svg class="recipe__icon">
<use href="${icons}#icon-check"></use>
</svg>
<div class="recipe__quantity">${ing.quantity}</div>
<div class="recipe__description">
<span class="recipe__unit">${ing.unit}</span>
${ing.description}}
</div>
</li>`;
})
.join('')}
</ul>
</div>
<div class="recipe__directions">
<h2 class="heading--2">How to cook it</h2>
<p class="recipe__directions-text">
This recipe was carefully designed and tested by
<span class="recipe__publisher">${
this.#data.publisher
}</span>. Please check out
directions at their website.
</p>
<a
class="btn--small recipe__btn"
href="${this.#data.sourceUrl}"
target="_blank"
>
<span>Directions</span>
<svg class="search__icon">
<use href="${icons}#icon-arrow-right"></use>
</svg>
</a>
</div>`;
}
}
export default new RecipeView();
- 之后我们在处理一下图标的视图
import icons from 'url:../img/icons.svg';
........
renderSpinner = function () {
const markup = `
<div class="spinner">
<svg>
<use href="${icons}#icon-loader"></use>
</svg>
</div>`;
this.#parentElement.innerHTML = '';
this.#parentElement.insertAdjacentHTML('afterbegin', markup);
};
- 接着我们重构一小段代码
#generateMarkupIngredient(ing) {
return `
<li class="recipe__ingredient">
<svg class="recipe__icon">
<use href="${icons}#icon-check"></use>
</svg>
<div class="recipe__quantity">${ing.quantity}</div>
<div class="recipe__description">
<span class="recipe__unit">${ing.unit}</span>
${ing.description}}
</div>
</li>`;
}
}
- 然后调用
<div class="recipe__ingredients">
<h2 class="heading--2">Recipe ingredients</h2>
<ul class="recipe__ingredient-list">
${this.#data.ingredients.map(this.#generateMarkupIngredient).join('')}
</ul>
</div>
- 这样我们本节目的就完成了,视图层的代码如下
import icons from 'url:../../img/icons.svg';
class RecipeView {
#parentElement = document.querySelector('.recipe');
#data;
render(data) {
this.#data = data;
const markup = this.#generateMarkup();
this.#clear();
this.#parentElement.insertAdjacentHTML('afterbegin', markup);
}
#clear() {
this.#parentElement.innerHTML = '';
}
renderSpinner = function () {
const markup = `
<div class="spinner">
<svg>
<use href="${icons}#icon-loader"></use>
</svg>
</div>`;
this.#parentElement.innerHTML = '';
this.#parentElement.insertAdjacentHTML('afterbegin', markup);
};
#generateMarkup() {
return `
<figure class="recipe__fig">
<img src="${this.#data.image}" alt="Tomato" class="recipe__img" />
<h1 class="recipe__title">
<span>${this.#data.title}</span>
</h1>
</figure>
<div class="recipe__details">
<div class="recipe__info">
<svg class="recipe__info-icon">
<use href="${icons}#icon-clock"></use>
</svg>
<span class="recipe__info-data recipe__info-data--minutes">${
this.#data.cookingTime
}</span>
<span class="recipe__info-text">minutes</span>
</div>
<div class="recipe__info">
<svg class="recipe__info-icon">
<use href="${icons}#icon-users"></use>
</svg>
<span class="recipe__info-data recipe__info-data--people">${
this.#data.servings
}</span>
<span class="recipe__info-text">servings</span>
<div class="recipe__info-buttons">
<button class="btn--tiny btn--increase-servings">
<svg>
<use href="${icons}#icon-minus-circle"></use>
</svg>
</button>
<button class="btn--tiny btn--increase-servings">
<svg>
<use href="${icons}#icon-plus-circle"></use>
</svg>
</button>
</div>
</div>
<div class="recipe__user-generated">
<svg>
<use href="${icons}#icon-user"></use>
</svg>
</div>
<button class="btn--round">
<svg class="">
<use href="${icons}#icon-bookmark-fill"></use>
</svg>
</button>
</div>
<div class="recipe__ingredients">
<h2 class="heading--2">Recipe ingredients</h2>
<ul class="recipe__ingredient-list">
${this.#data.ingredients.map(this.#generateMarkupIngredient).join('')}
</ul>
</div>
<div class="recipe__directions">
<h2 class="heading--2">How to cook it</h2>
<p class="recipe__directions-text">
This recipe was carefully designed and tested by
<span class="recipe__publisher">${
this.#data.publisher
}</span>. Please check out
directions at their website.
</p>
<a
class="btn--small recipe__btn"
href="${this.#data.sourceUrl}"
target="_blank"
>
<span>Directions</span>
<svg class="search__icon">
<use href="${icons}#icon-arrow-right"></use>
</svg>
</a>
</div>`;
}
#generateMarkupIngredient(ing) {
return `
<li class="recipe__ingredient">
<svg class="recipe__icon">
<use href="${icons}#icon-check"></use>
</svg>
<div class="recipe__quantity">${ing.quantity}</div>
<div class="recipe__description">
<span class="recipe__unit">${ing.unit}</span>
${ing.description}}
</div>
</li>`;
}
}
export default new RecipeView();