项目实战:supermall

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档

目录

前言

一、划分目录结构?

二、开发步骤

2.1 引入css

2.2  引入vue.config.js和.editorconfig

2.3 项目的模块划分:tabbar=>路由映射关系

2.4 首页开发

2.5 详情页开发

三、项目细节改进

四、nginx项目在windows下的部署

五、项目总结

5.1 项目描述:

5.2 项目中学到了什么?

5.2.1 项目开发流程及开发方法和思想

5.2.2 插件或第三方库

5.3 项目文件

5.4 项目截图


前言

随着vue的学习,实现supermall。


提示:以下是本篇文章正文内容,下面案例可供参考

一、划分目录结构?

二、开发步骤

2.1 引入css

  •         base.css
  •         normalize.css

2.2  引入vue.config.js和.editorconfig

vue.config.js别名配置

2.3 项目的模块划分:tabbar=>路由映射关系

2.4 首页开发

        2.4.1 navbar的封装

        2.4.2 网络数据的请求

        2.4.3 轮播图

                better-scroll---->2.4.7

        2.4.4 推荐信息

        2.4.5 定位

                position:sticky

                top:44px

        当高度没达到设置高度时,position为sticky,达到后position自动设置为fixed。

        2.4.6 goods:(流行/新款/精选)

        goods:{

                "pop":{ page:0,list:[] },

                "news":{page:0,list:[]},

                "sell":{page:0,list:[]}

}

          2.4.7 Better-Scroll

介绍 | BetterScroll 2.0

侦听position

//默认情况下BScroll是不可以实时的监听滚动位置 
//probe侦测 //0 1:不侦测 
//2:在手指滚动的过程中侦测,手指离开后的惯性滚动过程不中侦测 
//3:只要是滚动都侦测
const bscroll = new BScroll(document.querySelector('.wrapper'), {
    probeType:2,
})
bscroll.on('scroll', (position) => {
    console.log(position);
})

内部可以发生点击事件:

click:true

上拉加载更多:

pullUpLoad:true
bscroll.on('pullingUp',()=>{
    console.log("请求加载更多")
    //发送网络请求,请求更多页的数据
    
    // 等数据请求后,并将新的数据展示出来后
    
    setTimeout(()=>{
        bscroll.finishPullUp()
    },2000)

})

document.querySelector('.wrapper')拿到的元素不准确

解决办法:

ref如果绑定在组件中,通过this.$refs.refname获取到的是一个组件对象

ref如果绑定在普通元素中,那么通过this.$refs.refname获取到的是一个元素对象

高度的计算函数:

height: calc(100% - 93px);

组件的监听:

<back-top @click.native="backClick"></back-top>

返回顶部:

this.$refs.scroll.scroll.scrollTo(0,0,500)

        2.4.8  解决首页中可滚动区域的bug

                better-scroll在决定多少区域可滚动时,是根据scrollHeight属性决定的。

                scrollHeight属性是根据放better-scroll的content的子组件的高度

                但是在我们的首页当中,刚开始计算scrollHeight属性时,是没有将图片计算在内的

                所以,计算出来的值是有错误的

                后来图片加载进来之后有了新的高度,但是scrollHeight属性并没有进行更新

                所以滚动出现了问题

        如何解决这个问题?

                监听每一张图片是否加载完成,只要有一张图片加载完成,执行一次refresh()

                如何监听图片加载完成?

                        原生的js监听图片:img.onload = function(){}

                        vue中监听:@load='方法'

                调用scroll的refresh():

                        因为涉及到非父子组件通信,所以选择事件总线(this.$bus)来解决

//vue实例作为事件总线发射监听事件
Vue.prototype.$bus = new Vue()

imageLoad(){
  this.$bus.$emit('itemImageLoad')
}

this.$bus.$on('itemImageLoad',()=>{
  this.$refs.scroll.refresh()
  // console.log('-----');
})
优化:

        因为this.$refs.scroll.refresh()调用太频繁,

对于refresh非常频繁的问题,进行防抖操作:

        防抖:debounce / 节流:throttle

        防抖函数起作用的过程:

  •                 如果直接执行refresh,则需要执行30次
  •                 所以将refresh函数直接传给debounce函数中,生成一个新的函数
  •                 之后在调用非常频繁的时候,就是用新生成的函数
  •                 而新生成的函数,并不会非常频繁的调用,如果下一次执行来的非常快,那么会将上一次取消掉
debounce(func, delay) {
  let timer = null
  return function (...args) {
    if (timer) clearTimeout(timer)
    timer = setTimeout(() => {
      func.apply(this, args)
    }, delay)
  }
}

        2.4.9  上拉加载更多

       子组件侦听页面监听滚到底部将函数传出到父组件

        父组件根据侦听结果加载更多数据

        但better-scroll内部只会请求一次页面加载更多,故在每次请求完数据后都要执行:

//完成上拉加载更多
this.$refs.scroll.finishPullUp()

        2.4.10 吸顶效果

        必须知道滚动距离达到多少时呈现吸顶效果

        offsetTop

    //组件没有offsetheight属性     
    //所有的组件都有一个$el属性:用于获取组件中的元素     
    this.tabOffsetTop = this.$refs.tabControl.$el.offsetTop     console.log(this.tabOffsetTop)

        但是,如果直接在mounted中获取offsetTop该值是不正确的,

如何获取正确的值?

        监听组件中img的加载完成        

        加载完成后,发出事件,在相应父组件中获取正确的值

  • 问题:动态的改变tabControl的样式时, 会出现两个问题:

    • 问题一: 下面的商品内容, 会突然上移

    • 问题二: tabControl虽然设置了fixed, 但是也随着Better-Scroll一起滚出去了.

  • 其他方案来解决停留问题.

    • 在最上面, 多复制了一份PlaceHolderTabControl组件对象, 利用它来实现停留效果.

    • 当用户滚动到一定位置时, PlaceHolderTabControl显示出来.

    • 当用户滚动没有达到一定位置时, PlaceHolderTabControl隐藏起来

        2.4.11 保持原来的状态:

                让home不要随意销毁:keep-alive

                让home中的内容保持原来的位置:

                        离开时, 保存一个位置信息saveY.

                        进来时, 将位置设置为原来保存的位置saveY信息即可.

                                  注意: 最好回来时, 进行一次refresh()

activated() {
  this.$refs.scroll.scrollTo(0,this.saveY,0)
  this.$refs.scroll.refresh()
  // console.log('activate');
},
deactivated() {
  // console.log('deactivated');
  this.saveY = this.$refs.scroll.getScrollY()
  // console.log(this.saveY)
},

2.5 详情页开发

监听整个goodsItem的点击事件进入详情页

排除某一个组件的keep-alive

<keep-alive exclude="Detail"> 
        <router-view/> 
</keep-alive>
//数据进行整合的手段,采用Object完成
export class Goods {
  constructor(itemInfo, columns, services) {
    this.title = itemInfo.title
    this.desc = itemInfo.desc
    this.newPrice = itemInfo.price
    this.oldPrice = itemInfo.oldPrice
    this.discount = itemInfo.discountDesc
    this.columns = columns
    this.services = services
    this.realPrice = itemInfo.lowNowPrice
  }
}
//监听某一个属性的变化
watch: {
 detailInfo() {
   // 获取图片的个数
   this.imagesLength = this.detailInfo.detailImage[0].list.length
 }
}

时间戳:以linux时间元年为起点,返回对应的时间戳,1535329958(时间戳)

        如何将时间戳转成时间格式化字符串

        时间戳:

        1.将时间戳转成Date对象

        const date = new Date(1535329958*1000)

        2.将date进行格式化,转成对应的字符串

                date.getYear() | date.getMonth()-1

                datw--->FormatString(太常用)

                fmt.formate(data,'yyyy-MM-dd hh:mm')

正则表达式:做字符串匹配的利器

两个对象(组件之间)之间有重复代码:采用混入的方法解决

        混入 (mixin) 提供了一种非常灵活的方式,来分发 Vue 组件中的可复用功能。一个混入对象可以包含任意组件选项。当组件使用混入对象时,所有混入对象的选项将被“混合”进入该组件本身的选项。

混入 — Vue.js

混入 — Vue.js

import {debounce} from "./utils";

export const itemListenerMixin = {
  mounted() {
    //图片加载完成的事件监听
    let refresh = debounce(this.$refs.scroll.refresh, 200)
    //保存函数
    this.itemImgListener = () => {
      refresh()
      // this.$refs.scroll.refresh()
    }
    //监听item中图片加载完成
    this.$bus.$on('itemImageLoad', this.itemImgListener)
  }
}
//组件
import {itemListenerMixin} from "../../common/mixin";
mixins: [itemListenerMixin]

类之间的继承:extends关键字

class Animal {
  run() {

  }
}

class Person extends Animal {

}

class Dog extends Animal {
  
}

标题和内容的联动效果:

        1. 点击标题滚动到对应内容(利用防抖操作获取相应内容的高度)

错误版本:
this.$nextTick(()=>{
  // 第二次调动,根据最新的数据,DOM已经渲染出来了
  // 但是图片依然没有加载完毕,所以这里的数据还是错误的
  this.themeTopYs = []
  this.themeTopYs.push(0)
  this.themeTopYs.push(this.$refs.params.$el.offsetTop)
  this.themeTopYs.push(this.$refs.comment.$el.offsetTop)
  this.themeTopYs.push(this.$refs.recommend.$el.offsetTop)
  console.log(this.themeTopYs)
})
正确版本:在图片加载完后获取高度才是正确的
//防抖操作,对某一个操作不要频繁的进行使用
this.getThemeTopY = debounce(() => {
  this.themeTopYs = []
  this.themeTopYs.push(0)
  this.themeTopYs.push(this.$refs.params.$el.offsetTop)
  this.themeTopYs.push(this.$refs.comment.$el.offsetTop)
  this.themeTopYs.push(this.$refs.recommend.$el.offsetTop)
  console.log(this.themeTopYs)
})

//跳转到当前对应的位置
titleClick(index) {
  // console.log(index);
  this.$refs.scroll.scrollTo(0, -this.themeTopYs[index], 200)

}

2.滚动到对应位置标题的高亮发生改变:

普通做法:

if (this.currentIndex !== i && ((i < length - 1 && positionY >= this.themeTopYs[i]
  && positionY < this.themeTopYs[i + 1])
  || (i === length - 1 && positionY >= this.themeTopYs[i]))) {
  // console.log(i)
  this.currentIndex = i
  // console.log(this.currentIndex)
  this.$refs.nav.currentIndex = this.currentIndex
}

条件一:this.currentIndex !== i   防止赋值的过程过于频繁

条件二:(i < length - 1 && positionY >= this.themeTopYs[i] && positionY < this.themeTopYs[i + 1]) || (i === length - 1 && positionY >= this.themeTopYs[i])

        条件1:i < length - 1 && positionY >= this.themeTopYs[i] && positionY < this.themeTopYs[i + 1]) 

                 判断区间:在0和某个数字之间(i<length-1)

        条件2:i === length - 1 && positionY >= this.themeTopYs[i]  

                判断区间:判断大于等于(i===length-1)

hack做法:用空间换时间

this.themeTopYs.push(Number.MAX_VALUE)

if (this.currentIndex !== i && (positionY >= this.themeTopYs[i]
  && positionY < this.themeTopYs[i + 1])) {
  this.currentIndex = i
  // console.log(this.currentIndex);
  this.$refs.nav.currentIndex = this.currentIndex
}        

底部工具栏:

        点击加入购物车

  1. 监听加入购物车按钮的点击,并且获取商品信息
  2.  将数据利用vuex存储在store中:npm install vuex --save
  3. 购物车展示:构建购物车页面,调用store中的数据
    1. 导航栏展示
    2. 购物车商品的展示
    3. 商品的选中和不选中切换
    4. 底部工具栏的汇总

store中的mapGetters:

mapGetters的映射关系:

import {mapGetters} from 'vuex'

...mapGetters(['cartCount'])

   回到顶部:

        详情页回到顶部   :利用混入mixin解决,实现多组件数据共享

mapActions的映射关系:

import {mapActions} from 'vuex'

...mapActions(['addCart'])         或者

...mapActions({ add:'addCart' })

toast的封装:

        普通封装

        插件形式封装

三、项目细节改进

1.fastClick----->在移动端减小点击300毫秒的延时

npm install fastClick --save

import FastClick from 'fastClick'

FastClick.attach(document.body)

2.图片懒加载

什么是图片懒加载?

        图片需要显示在屏幕区域上时再加载。

lazeload:Lazy Load延迟加载插件延迟了长网页中图像的加载。用户滚动到视窗之外的图像之前,不会加载它们。这与图像预加载相反。

1.npm install vue-lazyload --save

2.import VueLazyload from "vue-lazyload";

3.//lazylad插件

        Vue.use(VueLazyload)

4.<img v-lazy="showImage" alt="" @load="imageLoad">

5.占位符(可有可无)

Vue.use(VueLazyload,{
  loading:require('assets/img/common/placeholder.png')
})

3.单位转换插件:postcss-px-to-viewport

1.1.下载插件

npm install postcss-px-to-viewport --save--dev

2.配置postcss.config.js文件

module.exports = {
    plugins: {
        autoprefixer: {},
        "postcss-px-to-viewport": {

             //375*750 iphone6的屏幕
            viewportWidth: 375,//视窗的宽度,对应的是我们设计稿的宽度(retina 高清屏)
            viewportHeight: 667,//视窗的高度,对应的是我们设计稿的高度
            unitPrecision: 5,//指定'px'转换为视窗单位值得小数位数(很多时候无法整除)
            viewportUnit: 'vw',//指定需要转换成的视窗单位,这里使用vw
            selectorBlackList: ['ignore', 'tab-bar', 'tab-bar-item'],//指定不需要转换的类
            minPixelValue: 1,//小于或等于‘1px’不转换为视窗单位
            mediaQuery: false,//允许在媒体查询中转换'px'
            exclude: [/TabBar/, /CartBottomBar/]//不进行单位转换的文件(数组中内容为正则表达式)
        },
    }
}

正则的规则:

        1.^abc:表示匹配的内容,必须以什么内容开头

        2.abc$:表示匹配的内容,必须以什么内容结尾

四、nginx项目在windows下的部署

服务器:一台电脑(没有显示器,主机)24小时开着,为用户提供服务。

公司有没有服务主机?------->没有,租借阿里云/华为云/腾讯云

主机->操作系统->windows/Linux->tomcat/nginx(软件)

第一:将自己的电脑作为服务器->winsow->nginx

第二:远程部署(Mac)

mainline version:主力在做的版本,可以说开发版

stable version:最新稳定版本

legacy version:遗留老版本的稳定版

部署方法:

        1.下载:nginx: download

        2.解压并双击启动:(路径不能有中文)

                localhost会显示

        3.复制打包的dist文件夹到nginx当前文件夹下面的html文件下,同时复制dist中的文件到html文件中,重新启动服务则会访问当前项目

或者

1.在配置文件中修改:

 location / {
            root   dist;
            index  index.html index.htm;
        }

在任务管理器进程中结束nginx进程,重新启动。

五、项目总结

5.1 项目描述:

1、此项目为一个前后端分离的 电商实战项目
2、采用了Vue全家桶+ES6+Webpack 等前端最新技术
3、项目包括商家、商品、用户、等多个功能子模块
4、采用模块化、组件化、工程化的模式开发

5.2 项目中学到了什么?

5.2.1 项目开发流程及开发方法和思想


1、熟悉一个项目开发的流程
2、学会组件化、模块化、工程化的开发模式
3、掌握使用vue-cli脚手架初始化vue.js项目
4、学会ES6+eslint 的开发方法
5、掌握一些项目的优化技巧

5.2.2 插件或第三方库


1、学会使用 vue-router 开发单页面
2、学会使用 axios 的封装和后端数据交互
3、学会使用 vuex 管理应用组件的状态
4、学会是使用 better-scroll 实现页面滑动效果
5、学会使用 vant-ui 组件库构建界面
6、学会使用 vue-lazyload 实现图片懒加载

5.3 项目文件


vue-cli3/4 的项目文件目录
目录/文件                                                                   说明
dist                                                               项目打包后生成的文件
node_modules                                             npm加载的项目依赖模块
public                                               静态资源目录,如图片、字体、icon图标 ,其中里面的                                                                    index.html是模板,当运行npm run build打包的时候,就是以                                                             这里的index.html作为模板打包 输出 到dist 文件夹下生成一个                                                            新的 index.html文件
src                                                      包含了如下几个目录及文件:
                                                            assest: 放置一些图片,如logo等
                                                           components: 这里我一般用来放置一些公共的组件
                                                   App.vue:应用组件,我们写的所有组件都是在这个组件之上运行的
                                                    main.js: 项目的核心文件,入口js文件
                                                    router: 路由文件夹,决定页面路由的跳转规则
                                                    store: vuex的状态管理文件
                                                    network:自己创建的,用来存放项目中发起请求的js文件模块
                                              pluginunit:自己创建的,用来存放项目中所使用到的ui插件的js文件
                                        common: 存放一些公共的js文件 比如, rem.js 用来解决移动端适配问题的
package.json                                       项目的配置文件,以及一些插件依赖包的信息
package-lock.json                                      插件依赖的详细信息
babel.config.js                                           用来配置一些ui插件
README.md                                        项目的说明文档,markdown格式

5.4 项目截图

首页

在这里插入图片描述    在这里插入图片描述

 分类页

在这里插入图片描述  在这里插入图片描述

 详情

在这里插入图片描述  在这里插入图片描述  在这里插入图片描述

 购物车

在这里插入图片描述  在这里插入图片描述

 我的

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值