上一篇讲到SPA的缺点,Vue是用JvaScript载入後台的数据,并且动态产生元件,SEO只能抓取HTML内容,导致无法抓到该有的数据,因此有团队开发出Nuxt.js这套前端框架,框架或工具都是因应某个需要被解决的问题而生的。
Nuxt.js是以Vue为基础所建构的框架,非Vue官方所开发的,能做到SPA的开发模式,另一方面又可以接换到SSR模式,改善SEO无法爬虫的缺点。
当使用者第一次载入到网页时,nuxt server
会先解析pages.vue档或是compontents.vue档,之後回传给nuxt server,整理出一份HTML档案到client端让画面显示出来,这个部分以前都是使用SSR
模式在跑流程,之後开始转用SPA
模式,点击nuxt-link,都不会让页面跳页,除非点击单纯的a连结,点击a连结将会在种跑一次SSR模式,大大改善SEO的缺点,这样的好处是一开始就有html结构,让搜寻爬虫能找到资料,之後再开始切换SPA,使用者体验有了,SEO问题也解决了,但目前Vue3版本还无法支援
。
以前的观念,就是把所有程序都往HTML页面塞,这种程序码称为义大利面式程序码
(Spaghetti code),随着专案的需求越来越大,让阅读与开发变得非常困难,在开发领域里,有个关注点分离的设计原则(Separation of Concerns),意思就是程序需要拆解成不同区块,各自分工合作,在撰写程序码时,要同时思考後续的维护性。
有了以上的原则後,就可以将模型检视控制器(MVC)拆解成三个区块:
JavaScript:
let productData = []
document.getElementById('addProduct').addEventListener('click', (e) => {
const timeStamp = Math.floor(Date.now());
if (document.getElementById('title').value.trim() !== '') {
productData.push({
id: timeStamp,
title: document.getElementById('title').value.trim(),
origin_price: parseInt(document.getElementById('origin_price').value) || 0,
price: parseInt(document.getElementById('price').value) || 0,
is_enabled: false,
})
let str = '';
productData.forEach((item) => {
str += `
<tr>
<td>${item.title}</td>
<td width="120">
${item.origin_price}
</td>
<td width="120">
${item.price}
</td>
<td width="100">
<div class="form-check form-switch">
<input class="form-check-input" type="checkbox" id="is_enabled" ${item.is_enabled? 'checked': ''} data-action="complete" data-id="${item.id}">
<label class="form-check-label" for="is_enabled">${item.is_enabled? '启用' : '未启用'}</label>
</div>
</td>
<td width="120">
<button type="button" class="btn btn-sm btn-danger move" data-action="remove" data-id="${item.id}"> 删除 </button>
</td>
</tr>`;
})
document.getElementById('productList').innerHTML = str;
document.getElementById('productCount').textContent = productData.length;
document.getElementById('title').value = '';
document.getElementById('origin_price').value = '';
document.getElementById('price').value = '';
}
});
document.getElementById('clearAll').addEventListener('click', (e) => {
e.preventDefault();
productData = [];
let str = '';
productData.forEach((item) => {
str += `
<tr>
<td>${item.title}</td>
<td width="120">
${item.origin_price}
</td>
<td width="120">
${item.price}
</td>
<td width="100">
<div class="form-check form-switch">
<input class="form-check-input" type="checkbox" id="is_enabled" ${item.is_enabled? 'checked': ''} data-action="complete" data-id="${item.id}">
<label class="form-check-label" for="is_enabled">${item.is_enabled? '启用' : '未启用'}</label>
</div>
</td>
<td width="120">
<button type="button" class="btn btn-sm btn-danger move" data-action="remove" data-id="${item.id}"> 删除 </button>
</td>
</tr>`;
})
document.getElementById('productList').innerHTML = str;
document.getElementById('productCount').textContent = productData.length;
});
document.getElementById('productList').addEventListener('click', (e) => {
const action = e.target.dataset.action;
const id = e.target.dataset.id;
if (action === 'remove') {
let newIndex = 0;
productData.forEach((item, key) => {
if (id == item.id) {
newIndex = key;
}
})
productData.splice(newIndex, 1);
} else if (action === 'complete') {
productData.forEach((item) => {
if (id == item.id) {
item.is_enabled = !item.is_enabled;
}
})
}
let str = '';
productData.forEach((item) => {
str += `
<tr>
<td>${item.title}</td>
<td width="120">
${item.origin_price}
</td>
<td width="120">
${item.price}
</td>
<td width="100">
<div class="form-check form-switch">
<input class="form-check-input" type="checkbox" id="is_enabled" ${item.is_enabled? 'checked': ''} data-action="complete" data-id="${item.id}">
<label class="form-check-label" for="is_enabled">${item.is_enabled? '启用' : '未启用'}</label>
</div>
</td>
<td width="120">
<button type="button" class="btn btn-sm btn-danger move" data-action="remove" data-id="${item.id}"> 删除 </button>
</td>
</tr>`;
})
document.getElementById('productList').innerHTML = str;
document.getElementById('productCount').textContent = productData.length;
});
let str = '';
productData.forEach((item) => {
str += `
<tr>
<td>${item.title}</td>
<td width="120">
${item.origin_price}
</td>
<td width="120">
${item.price}
</td>
<td width="100">
<div class="form-check form-switch">
<input class="form-check-input" type="checkbox" id="is_enabled" ${item.is_enabled? 'checked': ''} data-action="complete" data-id="${item.id}">
<label class="form-check-label" for="is_enabled">${item.is_enabled? '启用' : '未启用'}</label>
</div>
</td>
<td width="120">
<button type="button" class="btn btn-sm btn-danger move" data-action="remove" data-id="${item.id}"> 删除 </button>
</td>
</tr>`;
})
document.getElementById('productList').innerHTML = str;
document.getElementById('productCount').textContent = productData.length;
function renderPage(data) {
}
let productData = []
let addBtn = document.getElementById('addProduct');
let clearall = document.getElementById('clearAll');
let clearindex = document.getElementById('productList');
let productTitle = document.getElementById('title');
let productPrice = document.getElementById('origin_price');
let Price = document.getElementById('price');
let Count = document.getElementById('productCount');
//资料处理
function addProduct(){
const timeStamp = Math.floor(Date.now());
if (document.getElementById('title').value.trim() !== '') {
productData.push({
id: timeStamp,
title: document.getElementById('title').value.trim(),
origin_price: parseInt(document.getElementById('origin_price').value) || 0,
price: parseInt(document.getElementById('price').value) || 0,
is_enabled: false,
});
renderPage(productData);
//空字串清空资料
productTitle.value = '';
productPrice.value = '';
Price.value = '';
}
};
addBtn.addEventListener('click', addProduct);
// 资料全部删除
function clearAll(e){
e.preventDefault();
productData = [];
renderPage(productData);
};
clearall.addEventListener('click', clearAll);
function productList(e){
const action = e.target.dataset.action;
const id = e.target.dataset.id;
if (action === 'remove') {
let newIndex = 0;
productData.forEach((item, key) => {
if (id == item.id) {
newIndex = key;
}
})
productData.splice(newIndex, 1);
} else if (action === 'complete') {
productData.forEach((item) => {
if (id == item.id) {
item.is_enabled = !item.is_enabled;
}
})
}
renderPage(productData);
}
clearindex.addEventListener('click' ,productList)
// 渲染画面
function renderPage(data){
let str = '';
productData.forEach((item) => {
str += `
<tr>
<td>${item.title}</td>
<td width="120">
${item.origin_price}
</td>
<td width="120">
${item.price}
</td>
<td width="100">
<div class="form-check form-switch">
<input class="form-check-input" type="checkbox" id="is_enabled" ${item.is_enabled? 'checked': ''} data-action="complete" data-id="${item.id}">
<label class="form-check-label" for="is_enabled">${item.is_enabled? '启用' : '未启用'}</label>
</div>
</td>
<td width="120">
<button type="button" class="btn btn-sm btn-danger move" data-action="remove" data-id="${item.id}"> 删除 </button>
</td>
</tr>`;
})
clearindex.innerHTML = str;
Count.textContent = data.length;
}
renderPage(productData);
关注点分离的版本,相当简洁,整体下来约缩减约五十几行的程序码。
参考资料:
HiSKIO 程序语言线上教学
Kuro
Microsoft MVC
架构原则
ALPHA Camp
<<: [Day 13] Leetcode 49. Group Anagrams (C++)
JavaScript 在运作时会建立执行环境,分为 Global Execution Context...
1. 比较和交换(compare and swap,简称CAS)跟交换(swap)有什麽不同? 比较...
reversing kr 是一个很不错的练习逆向的地方。 reversing .kr 介绍 This...
这边推荐你一款 FonePaw iOS 资料备份及还原 软件,能够一键备份 iPhone Faceb...
前言 在安装过後,今天我们来测试 VM 的相关效能。 CPU 效能测试 我们采用一套有天梯的效能测试...