Vue3.0 + 后端api 建立一个小型GoBook图书商域
-
前言:
本篇文章讲述了使用Vue3.0+ 后端API和各种前端UI组件,多种框架结合使用,建立的一个小型GoBook图书商域网站。Vue3的使用,让网站可以按照模块化开发。Bootrsap使得该项目同时能够在PC端、Pad端、移动端等多种终端设备运行。JQuery能够最大化地利用封装的函数,同时减少代码的复杂度。Element-plus组件化开发,减少花费在常规的功能组件上的时间。
目录结构
- 前期准备
- 项目结构
- 具体实现
- 性能优化
- 网站部署
- 总结
一、前期准备
1.框架和UI库
- Vue 3.0 前端渐近式框架——Vue.js (vuejs.org)
- Bootstrap 4 前端UI框架——Bootstrap v4 中文文档 · | Bootstrap 中文网 (bootcss.com)
- JQuery 3.6.0 前端框架——jQuery
- Element-plus 组件库——网站快速成型工具 | Element Plus (gitee.io)
- 阿里巴巴矢量图标库 ——iconfont-阿里巴巴矢量图标库
2.选择框架的原因
2.1 选择Vue 3
首先选用vue3是为了学习当下最新的知识,因此小编也没有去过多了解vue2。其实 ,使用其它框架也是一种尝试和练习。Bootstrap结合vue3框架,可以让vue3项目更加灵活。
2.2 选择Bootstrap 4
因为vue项目是不可复用的,如果需要重新开发一套新的项目必需 新建立vue项目,因此,使用了Bootstrap就让vue项目可以同时在多种不同的设备上线运行。当然,我们也可以使用css3的新属性@media , 使用这个属性去支持多端设备,这 可不是一个好选择哦!
2.3 选择jquery 3.6.0
一开始建立网站前,对vue认识的不是很深刻,所以才选择使用 JQuery框架对vue的一种补充。初学vue,没有学到真正的内涵,所以开始只能在使用虚拟DOM后 ,再通过 jquery操作真实DOM节点。后面进行过优化,把jquery的部分事件监听去除,只留下了部分功能,并且不再通过jquery触发点击事件。其实,在目前,已经可以完全移除jquery框架。首先,jquery框架已经不再被目前的前端开发所必要的框架,浏览器渐渐支持原生JS,比如说document.querySelector,并且vue, React, Angular的虚拟Dom也已经取代jquery操作真实DOM节点,对页面的性能是一种极大的提升。
2.4 选择使用 Element-plus
最后,使用Element-plus UI组件库,可以加快项目的开发。对于一些常规化的功能,可以使用更美观、实用的组件来构成页面。因为当前项目已经使用多种框架,所以小编也不再使用其它的UI组件库,否则项目会变得臃肿不堪。当然,Bootstrap组件是不被包括在内的,因为Boostrap组件库也依旧是Bootstrap框架的一部分。使用部分高效的组件也是个不错的选择。其实移动端正火热使用的vant组件也是一个非常好的组件库(Vant - 轻量、可靠的移动端组件库 (gitee.io)),如果说在当前引入这个组件库,那么当前的网站已经太复杂化。小型网站而使用大量的组件库,很容易造成性能的浪费。每次加载网页,总是从各个组件库中找到组件再加载。到最后页面的渲染。如果非必要引入某个组件库,则不建议过多使用。
2.4 选择使用图标库
阿里巴巴矢量图标库,却实是现在火热的图标库。不管是Bootstrap-icons图标库(Bootstrap 图标库 · Bootstrap 官方开源图标(icon)库 (bootcss.com)), 还是使用FontAwesome图标库(Font Awesome),对于建立一个网站来说,图标真的是太少太少了。不管是Bootsrap在不断更新图标库,也还是阻挡不了我们对 阿里巴巴矢量图标库的向往。这里是把项目下载到本地,再去访问图标。这种方法可能会比较繁琐,但却是比较建议使用的。如果网速慢的话,可能会存在页面已经加载完毕,可图标还在缓慢显示。
二、项目结构和运行效果
1.1项目结构
/*
├─assets // 静态资源
│ ├─css // css文件
│ │ │ base.css // 基础css
│ │ │ normalize.css // 常规的css
│ │ │ other.css
│ │ │
│ │ └─iconfont_css // iconfont图标css文件
│ │ demo.css
│ │ demo_index.html
│ │ iconfont.css
│ │ iconfont.js
│ │ iconfont.json
│ │ iconfont.ttf
│ │ iconfont.woff
│ │ iconfont.woff2
│ │
│ └─images // 图片资源
│ │ 404error.png
│ │ collection-fill.png
│ │ collection.png
│ │ gobook-logo.png
│ │ upward.png
│ │
│ ├─img // 头部,页脚栏图片
│ │ chengxin.png
│ │ ectrust.png
│ │ logo-lmonkey.png
│ │
│ └─pic // 其它数据显示为空的时候,显示的图片
│ bg-address.png
│ bg-cart1.png
│ bg-collect.png
│ bg-login.png
│ bg-order.png
│ icon-qq.png
│ loginCode.png
│
├─components // 子组件
│ ├─common
│ │ ├─backtop // 一键滑动到顶部
│ │ │ BackTop.vue
│ │ │
│ │ ├─footerbar
│ │ │ FooterBar.vue
│ │ │
│ │ └─headerbar
│ │ HeaderBar.vue
│ │
│ └─content
│ └─goods //商品子组件
│ GoodsItem.vue
│ GoodsList.vue
│ GoodsListItem.vue
│
├─network // 封装axios网络请求。获取不同页面的数据
│ address.js
│ cart.js
│ category.js
│ collect.js
│ detail.js
│ home.js
│ order.js
│ request.js
│ settings.js
│ user.js
│
├─router // 路由管理
│ index.js
│
├─store // 状态管理
│ actions.js
│ getters.js
│ index.js
│ mutations.js
│
├─utils // 包含全国地址的js文件
│ addressMessage.js
│
└─views // 视图文件
│ 404error.vue
│
├─address
│ Address.vue
│ AddressEdit.vue
│
├─category
│ Category.vue
│
├─detail
│ Detail.vue
│
├─home
│ │ Home.vue
│ │
│ └─childcomps // 主页的子组件
│ HomeSwiper.vue
│ RecommendView.vue
│
├─order
│ CreateOrder.vue
│ Order.vue
│ OrderDetail.vue
│
├─profile
│ Collection.vue
│ Login.vue
│ Profile.vue
│ Register.vue
│ Settings.vue
│
├─search
│ Search.vue
│
└─shopcart
ShopCart.vue
*/
总共分为8板块,16个页面。
1.2运行结果
因为当前项目是多端适应,所以只展示部分的效果图。
三、具体实现
1.前端页面实现
1.1组件分析
因为当前网站涉及的页面比较多,这里展示比较重要的部分
(1.home.vue首页
使用的是bootstrap组件,所以为了适应多端的设备,需要对一些比较特殊的组件进行特殊处理。
其中首页的轮播图使用3个轮播图,一种是移动端等小屏幕显示, 二是PC等大屏幕显示。 三是Ipad中屏显示。小屏,中屏采用简易化轮播,大屏采用的是高级轮播图。两种不同风格的轮播图,以适应PC端的宽屏,和Ipad的中屏,移动端的小屏。 不过,这样下来,也会造成不小的性能开销。
<!--轮播图内容-->
<div id="swiper" class="container-fluid">
<div v-if="banners.length===0">
<!--轮播图内容栏,空内容显示-->
<el-carousel :interval="4000" arrow="always" height="200px">
<el-carousel-item v-for="item in 6" :key="item">
<h3 class="medium font-weight-bold" style="font-size:18px; margin-top:-50px;">没数据啦!刷新页面一下哦!</h3>
</el-carousel-item>
</el-carousel>
</div>
<!--轮播图内容栏,大屏显示-->
<div class="d-none d-lg-block " v-if="banners.length>0">
<el-carousel :interval="4000" type="card">
<el-carousel-item v-for="(item,index) in banners.slice(0,6)" :keys="index">
<h3 class="medium" @click.prevent="goD(item.id)">
<el-image :src="item.img_url"></el-image>
</h3>
</el-carousel-item>
</el-carousel>
</div>
<!--轮播图内容栏,中小屏显示-->
<div class="d-block d-lg-none " v-if="banners.length>0">
<el-carousel :interval="4000" arrow="always">
<el-carousel-item v-for="(item,index) in banners.slice(0,6)" :keys="index">
<h3 class="medium" @click.prevent="goD(item.id)">
<el-image :src="item.img_url"></el-image>
</h3>
</el-carousel-item>
</el-carousel>
</div>
</div>
使用过babel-scroll移动端的滚动组件,但还是感觉效果不太好。 对PC端的支持性还是不行,流畅性有待提升。也找过其它组件,最后考虑到引入了jquery,所以就尝试使用jquery的窗口事件监听。因为vue和jquery两种不同风格的框架,所以在开发中有比较多的问题。后来经过优化后,才让jquery适应了vue的开发模式。
注意:
第一:如果把监听事件放在login.vue的其它script 中,vue不允许会出错。 一个vue页面中只允许一个script脚本,出现两个script标签就会出错。
第二:如果把监听窗口改变事件放在export default {}同级位置也不行。这虽然不报错,语法也正确。首次运行正常,刷新页面后,不再有效果。
**第三:即使把监听事件放在export default中的setup函数里面也要注意,使用非window和document的事件。需要等待页面挂载完毕后才能监听单个节点的事件。 相对来说,window和document事件是可以不放在onMounted里面的。因为窗口事件,是一直存在。而其它节点,需要等待页面的挂载,否则监听事件是会出错的。 **
这其中要考虑到,其实不应该在组件中通过jquery或者原生js监听事件的。虽然不建议使用,但箭在弦上,不得不发啊!所以现在也只能使用事件的监听。到目前来看,jquery好像是多余的。其实啊!纵观整个项目,用到jquery最多的就是addClass,removeClass, hasClass, 原生js呢使用的是什么? node.classList.add("abc") node.classList.remove("abc") titles[index].classList.toggle("abc") node..className. 现在看来,使用原生js也不会很复杂啊。唉!失算了。
vue渐近式开发不同于传统开发,传统开发是多页面,多文件,多脚本来渲染页面。而vue只是单页面程序,它不会因用户的点击进行页面的跳转,取而代之的是使用路由机制实现html页面内容的更新。
到这里就可以理解了,vue加载完数据后,不再运行子组件(相对于APP.vue根组件,其它的都是子组件)的script内容。就好像window.onload一样只加载一次。只有在组件中export default {}才会继续运行。因此,如果说子组件需要传递信息,一般需要通过$emit向父级派发一个自定义事件,由父组件处理。但在setup中建立的函数与变量,其实包括监听事件,子组件是可以单独处理的。所以啊,把windows监听事件放在setup函数中就没问题了。
// 监听窗口滚动事件
$(window).scroll(function () {
if (Math.ceil($(document).scrollTop()) >= $(document).height() - $(window).height()) {
isArriveBottom.value = isShowBackTop.value = true;
getMoreMessage(); // 调用函数
} else {
isArriveBottom.value = false;
if ($(document).scrollTop() < $(window).height()) {
isShowBackTop.value = false;
} else {
isShowBackTop.value = true; // 在滑动超过一定高度的时候,必需显示
}
}
});
(2.login.vue登录页
登录页有帐号登录和扫码登录,一般来说,移动端和ipad端是没有扫码登录的,所以,这里还是需要监听窗口的变化。如果是移动端,会立即切换成帐号登录模式。并且,因为需要在vue监听事件,所以可以把监听窗口变化放在setup(){..} 里面。
<script>
import $ from 'jquery'; // 引入$符号
....
export default {
name: "Login",
setup() {
....
// 监听窗口的改变, 只要是小屏幕,则会更改样式,使扫码功能隐藏,只显示帐号登录。
// 可能说ipad端没考虑周全,但也允许中屏出现扫码登录。
$(window).resize(function(){
if($(window).width()>760 && $(window).width()<780){
$('._tab-login >div:eq(0)').addClass('active').siblings('div').removeClass('active');
$('._message-login>div:eq(0)').addClass('active').siblings('div').removeClass('active');
}
});
....
}
}
</script>
**(3. register.vue注册页 **
在用户输入注册首先会在前端页检测手机号,如果说手机号不存在,也不会使用axios向后端发起异步请求。并且,注册服务为了保证是正常用户注册,所以使用了h5的canvas画布写数字验证码。
其实在这里,没有对验证码进行过多的修改,以前做的canvas验证码现在直接拿来用。所以当时开发的时候也就是把正确的验证码动态地放在html页面上。因为学习过python爬虫,即使的在网页上没有正确的验证码,感觉破解这个验证码也不难啊!并且答案都在网页上,好像反爬的效果不大啊。只是说现在不再去更新优化,否则这也可以进行优化,甚至把jquery从项目中移除也行。
// 创建自定义的验证码
const initVerifyCode = ()=>{
var canvas = document.getElementById("myCanvas"); // 拿到画布
var _hiddentNode = document.getElementById("_hiddenTxt"); // 隐藏的验证码 //其实这种方法很容易受到攻击
if (canvas.getContext) { //检查支持性
var ctx = canvas.getContext("2d"); // 拿到画笔,获得绘制上下文的绘画功能
drawCodeValid();
canvas.onclick = drawCodeValid;
//获取随机颜色
function randomColor(min, max) {
return "rgba(" + randomRange(min, max) + ',' + randomRange(min, max) + ',' + randomRange(min, max) + ",1)";
}
// 获取随机数
function randomRange(min, max) { // 5 - 8
return Math.floor(Math.random() * (max - min + 1) + min); // 0- 3 + 5 ===> 5-8
} // 0 - 10 ===> 0 - 11
// -45 - 45 ===> 0 - 91 -45
function drawCodeValid() {
var number = 4;
var txt = "";
var cHeight = canvas.height;
var cWidth = canvas.width;
// 画布绘制
ctx.save();
ctx.fillStyle = randomColor(180, 220);
ctx.beginPath();
ctx.fillRect(0, 0, cWidth, cHeight);
ctx.restore();
// 数字绘制
ctx.Baseline = "bottom"; // 文字底部对齐
for (var i = 0; i < number; i++) {
var deg = randomRange(-45, 45);
var x = (cWidth - 10) / number * i + 10;
var y = randomRange(cHeight / 2, cHeight - 10);
var color = randomColor(40, 120);
var currentNum = randomRange(0, 10).toString()[0];
txt += currentNum;
ctx.save();
ctx.fillStyle = color; //颜色必需比背景颜色更深
ctx.font = randomRange(25, 40) + "px SimHei";
ctx.translate(x, y);
ctx.rotate(deg * Math.PI / 180); // 移动,旋转画布
ctx.beginPath();
ctx.fillText(currentNum, 0, 0);
ctx.restore();
}
// 小黑点绘制
for (var j = 0; j < number * 30; j++) {
var xCircle = randomRange(0, cWidth);
var yCircle = randomRange(0, cHeight);
ctx.save();
ctx.fillStyle = randomColor(40, 120); // 颜色偏深
ctx.beginPath();
ctx.arc(xCircle, yCircle, 1, 0, 360 * Math.PI / 180); // 绘制小圆
ctx.closePath();
ctx.fill(); // 自动闭合路径
ctx.restore();
}
// 划线绘制
for (var k = 0; k < number * 2; k++) {
var xLineStart = randomRange(0, cWidth);
var yLineStart = randomRange(0, cHeight);
var xLineEnd = randomRange(0, cWidth);
var yLineEnd = randomRange(0, cHeight);
ctx.save();
ctx.strokeStyle = randomColor(80, 140);
ctx.beginPath();
ctx.moveTo(xLineStart, yLineStart); // 抬起画笔
ctx.lineTo(xLineEnd, yLineEnd); // 绘制划线
ctx.stroke(); // 绘制路径
ctx.restore();
}
_hiddentNode.innerHTML = txt; // 隐藏的验证码
}
} else {
alert('当前Javascript不支持画布元素');
}
除此之外,使用的element-plus组件中,步骤条el-step出现了异常,所以对elemnt-plus组件进行修改,只要在子组件在css部分中不能加上scoped属性,加上就不会作用到组件内。 因为每个组件进都存在作用域,如果添加scoped属性,element-plus组件就不能再使用局部的css属性
如果子组件的css不添加scoped属性,子组件的样式可以共享给其它组件使用。一般来说,其实并不建议这样做,因为会出现样式重叠。但因为使用sass预处理器,可以很大程度上避免样式混乱的问题,并且有bootstrap响应式的预定义类名的使用,也减少了这种情况的发生。
(4. search.vue搜索页
因为在许多的页面上都使用了面包屑导航栏,并且都需要显示上一个页面的名称。所以单独地使用router 是无法获取到上一级的页面标题名称,获取路径也没什么作用。所以这里使用localStorage去存储上一页的信息。同时,为了显示正确的信息,还要使用decodeURIComponent() 把乱码的中文字符改成正常的中文。 在进入页面前,通过路由独享的守卫获取上一个页面的信息。
beforeEnter: (to, from) => { // 路由独享的守卫的from不会来自本身,只能是undefined
if (from.meta.alias !== undefined && from.fullPath !== '/') { // 把数据放在浏览器中
window.localStorage.setItem("lastPage", from.meta.alias);
window.localStorage.setItem("lastPagePath", from.path);
if (Object.keys(from.query).length > 0) {
window.localStorage.setItem("lastPageQuery", from.fullPath);
} else {
window.localStorage.setItem("lastPageQuery", '');
}
} else if (from.fullPath === '/' || from.fullPath === '/home') {
window.localStorage.setItem("lastPage", '');
window.localStorage.setItem("lastPagePath", '');
window.localStorage.setItem("lastPageQuery", '');
}
},
使用路由懒加载,在搜索页重新输入信息后,页面将不会更新。同时,搜索页也获取不到路由跳转信息。所以只能通过watch监听路由的变化,再根据新的搜索数据重新加载页面数据。
// 监听路由的变化,在当前页面搜索时,后出现数据不能及时刷新的情况
watch(() => route.query.SearchText, (newA) => { // 通过watch监听数据的改变
if (newA !== undefined) { // 当搜索页面跳转其它页面时,也会触发这个监听器
// 但其它页面跳转到当前搜索页面时,却不一定会触发
// 清空会有干扰的数据
var new_text = newA;
if(new_text.startsWith('%') === true){
new_text = decodeURIComponent(new_text);
}
window.localStorage.setItem("lastSearchWords", new_text);
searchText.value = new_text;
console.log("路由在变化",newA);
currentAssociateCategories.value = [];
init_ok.init_goods = false;
isTotalMesaage.value = false;
currentOrder.value = 'sales';
tSearchText.value = '';
goods.sales.page = 1;
goods.sales.list = [];
goods.price.page = 1;
goods.price.list = [];
goods.comments_count.page = 1;
goods.comments_count.list = [];
isShowBackTop.value = false;
isArriveBottom.value = false;
isTotal.value = false;
dealSearchText(); // 处理当前的字符串
}
});
(5.Order.vue订单页
因为多处使用到了订单信息,所以在订单页的商品,订单详情,创建订单等都是使用同一个子组件,复用子组件以提高代码的复用率。不过,不同的页面,即使内容大体相同,但还是有些不一样。所以,不同的部分可以通过插槽来实现。
注意:插槽不仅仅可以在父组件使用子组件的时候向子组件添加数据,并且子组件还可以再次回传给父组件。并且在插槽内还可以添加事件,插槽虽然是把数据插入到子组件中,但子组件还是要被添加到父组件中。因此,在插槽内可以直接在父组件内定义父组件的事件。
<template v-slot:插槽名称 ="subdata">
。。。
</template>
因此子组件收到父组件传递的props数据,还可以通过插槽回传给父组件。
<!--父组件-->
<goods-item :allList = "allList" :path="$route.fullPath">
<template v-slot:numSlot="subdata">
<span class="_myNum txt-body px-5 ">x{{subdata.item.num}}</span>
</template>
<template v-slot:bottom="subdata">
.....
<div class="px-1 text-black " style="font-weight:bold">
实付款
<small>¥</small>
<span class="txt-subbody">{{(subdata.item.goods.price) * subdata.item.num}} </span>
<small>.00</small>
</div>
.....
</template>
</goods-item>
(6.用户登录状态管理
用户的信息和地址信息等其它必要信息都是需要放在state中进行管理,当从登录成功的那一个,所以全局需要使用的用户信息,默认地址信息,购物车数据信息都要获取。最后通过commit把信息提交到状态管理容器,如果不想要在当前页面立即获取,也可以通过dispatch分发一个异步请求,后台在异步请求完毕后,再主动提交commit修改状态信息。
状态管理有几个比较重要的部分,getters属性可以获取状态信息。mutations可以执行同步信息,actions可以进行异步处理,所以在一些简单的异步请求中,可以放在actions上进行。
// 主动提交修改的状态信息
store.commit('setAddressUserName',item.name);
// 分发一个异步请求
store.dispatch('updateCart');
// actions.js
const actions = {
// 异步更新购物车数据
updateCart({commit}){
getCart().then(res=>{
//console.log('updateCart当前的数量是',res.data);
commit('addCart',{count:res.data.length || 0})
}).catch(err=>{
//ElMessage.error('当前获取购物车数据失败');
console.log('当前获取购物车数据失败');
})
},
}
// mutaions.js
const mutations = {
// 设置登录状态
setIsLogin(state,payload){
state.user.islogin = payload;
},
}
1.2结构分析
(1. router/index.js路由文件
路由是通过懒加载的方式,并且路由借助了浏览器的History APl来实现的,这样可以使得页面跳转而不刷新,页面的状态就被维持在浏览器中。而vue-router默认使用的是hash模式。
在每个页面访问前,都会全局的路由守卫进行检测是否需要登录,是否有些组件需要卸载,给新加载的页面添加标题。如果需要登录,则会跳转到登录页。
const Home = () => import('../views/home/Home.vue');
const Category = () => import('../views/category/Category.vue');
const Search = () => import('../views/search/Search.vue');
....
const routes = [
{
path: '/',
name: 'DefaultHome', // 默认的路由
component: Home,
meta: {
title: 'GoBook——主页',
keepSearchAlive: true
}
},
....
}
const router = createRouter({ // 使用浏览器的history API, 而vue-router默认使用的是hash模式
history: createWebHistory(process.env.BASE_URL),
routes
});
router.beforeEach((to, from, next) => {
if (to.meta.title === undefined) { // 自定义 重定向页面
return next('/404error');
}
// getters属性中的方法,可以实时监听数据的改变
if (to.meta.isAuthRequired === true
&& (store.getters.getLoginState === false || store.getters.getLoginState === undefined)
&& to.meta.isMaster === undefined) {
ElMessage.error('您还没有登录,请先登录');
return next('/login');
} else {
next();
}
document.title = to.meta.title;
});
2.前端数据获取
当前的vue项目是使用后端API。 前端通过 axios发送异步请求给服务器,服务器因为添加了跨域请求,所以在浏览器中可以通过axios获取后台API数据。因此后台的API就是数据来源。
在vue开发的阶段,对axios封装可以增强对网站的可维护性。不同页面的请求分开放置在不同文件上,并且需要对axios添加拦截器,以便于在请求时添加相应的tokens。
import axios from 'axios';
import {ElMessage} from 'element-plus';
import router from '../router';
export function request(config) {
const instance = axios.create({
baseURL: 'https://api.shop.eduwork.cn',
timeout: 5000,
});
// 请求拦截
instance.interceptors.request.use(config => {
// 如果有一个接口需要认证才可以访问,就在这统一设置
const token = window.localStorage.getItem('token');
if (token) {
config.headers.Authorization = 'Bearer' + token;
}
// 直接放行
return config;
}, err => {
// 获取错误信息 ,只提示首要错误
ElMessage.error(err.response.data.errors[Object.keys(err.response.data.errors)[0]][0]);
});
// 响应拦截器
instance.interceptors.response.use(res => {
return res.data ? res.data : res;
}, err => {
// 如果有需要授权才可以访问的接口,统一去login授权
console.log("request error"+err);
if (err.response && err.response.status === 401) {
ElMessage.error('当前没有登录,请登录!');
window.localStorage.setItem('token', ''); // 清除token
store.commit('setIsLogin', false); // 清除登录状态信息
setTimeout(() => {
router.push({path: '/login'});
}, 500);
}
// 获取错误信息 ,只提示首要错误
ElMessage.error(err.response.data.errors[Object.keys(err.response.data.errors)[0]][0]);
});
return instance(config)
}
四、性能优化
1.路由懒加载
在页面加载时,加载首屏会把所有相关的文件都加载完毕。如果页面数量太多或者文件太大,网络延迟都会降低用户体验,所以在使用vue的建立网站的时候,官方是建议使用懒加载的方式加载路由所指向的vue文件。懒加载方式也会在打包后生成多个chunk文件。
import Home from '../views/Home.vue'
const routes = [
{
path: '/', // 路径
name: 'Home', // 名称
component: Home // 组件 引入方式一
},
{
path: '/about',
name: 'About',
component: () => import('../views/About.vue') // 引入方式二,懒加载方式,打包后会生成多个chunk文件
}
];
2.使用vuex
合理使用vuex保存用户登录状态以及使用localStorage,可以有效地减少对后端数据的访问。因为在相关页面需要维持用户信息和一些默认信息的状态,所以在vuex中没有数据的时候,需要再次向后端请求数据。
以下几点也可以减少api的请求:
- 1.在向后台发送信息前,进行表单验证
- 2.编辑地址时,数据没有修改,则不会向服务器发起保存请求
- 3.使用vue x全局变量,只要vuex中有用户的信息,也不向服务器发起请求
- 4.购物车结算功能,只有在用户点击结算时,才会对服务器中保存的信息进行修改。
3.使用sass预处理器
Sass 是一种css预处理器,CSS 预处理器定义了一种新的语言,其基本思想是,用一种专门的编程语言,为 CSS 增加了一些编程的特性,将 CSS 作为目标生成文件,然后开发者就只要使用这种语言进行编码工作。css预处理器:sass - 简书 (jianshu.com)
4.去除多余的控制台输出
在控制台输出,确实性能不是消耗很大。但是网络的有延迟,对页面的渲染多少有影响。在开发阶段的测试是为了验证数据的正确性。如果需要上线运行,必需要去除无用的输出语句,以减少性能的损耗。但目前该项目不是真正上线运行,还保留部分输出代码。
五、网站部署
在这里,小编不建议购买服务器。如果只是做前端项目的话,可以采取另一种方法------托管网站000webhost。 我们只是需要看到上线的效果,并非真正上线,所以这里可以提供另一种方法。使用fastmock去模拟后端接口,使用FastMock数据模拟平台 。同时,也可以写下接口文档ShowDoc API文档工具。 我们可以先获取后端写出的API接口,再写前端页面。 当然,我们也可以先自己写出模拟数据,写出接口文档,让后端根据模拟的接口文档再去开发真实的api。这里模拟的api也不能随便写,需要自己考虑部分逻辑。
一般开发应用,都是前后端一起开发,并非是谁先谁后。只是说,我们可以通过模拟数据来作为数据源。如果前端页面全部都是使用静态数据,那前后端结合的时候,工作量会比较大。
上线的效果:
六、总结
vuex框架给我们的开发带来了极大的便利,渐近式开发。所谓的渐近式开发就是从中心视图层向外扩展到构建工具层。以中心视图层App.vue作为根页面加载,其它的页面都作为它的子组件。其中会经过组件机制,将不同的组件挂载到页面上。路由机制接管了默认的浏览器页面的跳转。状态管理为我们提供全局变量的功能,在项目运行的时候,维持部分变量的状态。构造工具中,运行时是 用来创建Vue实例、渲染并处理虚拟DOM等的代码。
组件化开发提高了代码的复用性,为开发节省不少时间。对于性能优化方面,除了以上几个方式外,还可以继续优化项目,以达到最佳性能。标准化开发项目,也会对今后的维护做好了基石。因为各种原因,开发该项目已经结束(并非是一个彻底的商域网站,还有部分功能未开发)。只要功夫深,铁杵磨成针。加油!最后感谢学习猿地 - IT培训 (lmonkey.com) 提供的后端API的支持。
这里写上该项目的链接:gobook: 这是一个vue的图书商城项目。采用vue3 + bootstrap4 + jquery 多种框架集合和前端开源库。 (gitee.com)