Vuejs项目练习

练习一:点击更改颜色

作业需求:点击列表中的哪一项,那么该项文字变为红色(结合v-for和v-bind来实现)
实现效果:
在这里插入图片描述
实现代码:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>作业</title>
  <style>
    .active{
        color: red;
    }
  </style>
</head>
<body>
<div id="app">
  <ul>
    <li @click="btnClick(index)" :class="{active:currentIndex === index}" v-for="(item,index) in movies">{{item}}</li>
  </ul>
</div>
<script src="../js/vue.js"></script>
<script>
  const app = new Vue({
    el: '#app',
    data: {
      message: '你好啊',
      movies:['哆啦A梦I','哆啦A梦II','哆啦A梦III','哆啦A梦IV'],
      currentIndex:-1
    },
    methods:{
      btnClick(index){
        this.currentIndex = index
      }
    }
  })
</script>
</body>
</html>

练习二:TarBar案例的实现

作业需求:
在这里插入图片描述
实现代码:

1、实现逻辑

  • npm init webpack tabbar,创建项目,目录结构如下
    在这里插入图片描述

  • 在base.css中设置基础样式,即清除浏览器自带样式
    在这里插入图片描述

  • TabBarItem.vue 设置插槽并设置样式并导出

    <template>
      <div class="tab-bar-item">
        <slot name="item-icon"></slot>
        <slot name="item-text"></slot>
    <!--    <img src="../../assets/img/tabbar/shouye.svg" alt="">
        <div>首页</div>-->
      </div>
    </template>
    
    <script>
    export default {
      name: "TabBarItem"
    }
    </script>
    
    <style scoped>
      .tab-bar-item {
        flex: 1;
        text-align: center;
        height: 49px;
        font-size: 14px;
      }
      .tab-bar-item img{
        width: 24px;
        height: 24px;
        margin-top: 3px;
        /*将图片的默认3px去掉*/
        vertical-align: middle;
      }
    </style>
    
  • TabBar.vue 中设置插槽并导出

    <template>
      <div id="tab-bar">
    <!--    设置插槽-->
        <slot></slot>
      </div>
    </template>
    
    <script>
    export default {
      name: "TabBar"
    }
    </script>
    
    <style scoped>
      #tab-bar {
        display: flex;
        background-color: #f6f6f6;
    
        position: fixed;
        left: 0;
        right: 0;
        bottom: 0;
        /*设置阴影:x方向,y方向,阴影宽度,阴影颜色*/
        box-shadow: 0 -1px 1px rgba(100,100,100,.1);
      }
    
    </style>
    
  • App.vue 中导入、注册、使用组件

    <template>
      <div id="app">
    <!--    3、使用组件-->
        <tab-bar>
          <tab-bar-item>
            <img slot="item-icon" src="./assets/img/tabbar/shouye.svg" alt="">
            <div slot="item-text">首页</div>
          </tab-bar-item>
          <tab-bar-item>
            <img slot="item-icon" src="./assets/img/tabbar/profile.svg" alt="">
            <div slot="item-text">分类</div>
          </tab-bar-item>
          <tab-bar-item>
            <img slot="item-icon" src="./assets/img/tabbar/gouwuche.svg" alt="">
            <div slot="item-text">购物车</div>
          </tab-bar-item>
          <tab-bar-item>
            <img slot="item-icon" src="./assets/img/tabbar/wode.svg" alt="">
            <div slot="item-text">我的</div>
          </tab-bar-item>
        </tab-bar>
      </div>
    </template>
    
    <script>
      /*1、导入组件*/
      import TabBar from "./components/tabbar/TabBar";
      import TabBarItem from "./components/tabbar/TabBarItem";
      export default {
        name: 'App',
        components:{
          /*2、注册组件*/
          TabBar,
          TabBarItem
        }
      }
    </script>
    
    
    <style>
      /*导入基本样式*/
      @import "./assets/css/base.css";
    </style>
    

2、TarBar案例的实现思路

  • 1、封装TarBar组件

    • 自定义TarBar组件,在App中使用
    • 让TarBar处于底部,并且设置相关的样式
  • 2、TarBar中显示的内容由外界决定

    • 定义插槽
    • flex布局平分TarBar
      在这里插入图片描述
  • 3、自定义TabBarItem.vue,可以传入图片和文字

    • 定义TarBarItem,并且定义两个插槽:图片、文字
    • 给两个插槽外层包装 div,用于设置样式
    • 填充插槽,实现底部TarBar的效果
    <template>
    <!--  所有的Item都展示同一个图片,同一个文字-->
      <div class="tab-bar-item" @click="itemClick">
        <div v-if="!isActive"><slot name="item-icon"></slot>
        </div>
        <div v-else><slot name="item-icon-active"></slot>
        </div>
        <div :style="activeStyle">
          <slot name="item-text"></slot>
        </div>
      </div>
    </template>
    <script>
    export default {
      name: "TabBarItem",
      props:{
        path:String,
        activeColor:{
          type:String,
          default:'red'
        }
      },
      data(){
        return {
          // isActive:true
        }
      },
      /*搞一个计算属性*/
      computed:{
        isActive(){
          /*拿到活跃路由的path与当前路径的path作为对比*/
          /*
          * /home -> item1(/home) = true
          * /home -> item2(/category) = false
          * indexOf()方法:寻找,找到的话不为-1*/
          return this.$route.path.indexOf(this.path) !== -1
        },
        activeStyle(){
          return this.isActive ? {color:this.activeColor} :{}
        }
      },
      methods:{
        itemClick(){
          this.$router.replace(this.path);// 该方法允许返回
        }
      }
    }
    </script>
    
    <style scoped>
      .tab-bar-item {
        /*使盒子都有相同的长度*/
        flex: 1;
        text-align: center;
        height: 49px;
        font-size: 14px;
      }
      .tab-bar-item img{
        width: 24px;
        height: 24px;
        margin-top: 3px;
        /*将图片的默认3px去掉*/
        vertical-align: middle;
      }
    /*  !*设置 active 下的状态*!
      .active{
        color: red;
      }*/
    </style>
    
  • 4、App.vue 使用组件

		  <template>
		  <div id="app">
		<!--    3、使用组件-->
		    <tab-bar>
		      <tab-bar-item path="/home" activeColor="deeppink">
		        <img slot="item-icon" src="./assets/img/tabbar/home.svg" alt="">
		        <img slot="item-icon-active" src="./assets/img/tabbar/home_active.svg" alt="">
		        <div slot="item-text">首页</div>
		      </tab-bar-item>
		      <tab-bar-item path="/category" activeColor="deeppink">
		        <img slot="item-icon" src="./assets/img/tabbar/category.svg" alt="">
		        <img slot="item-icon-active" src="./assets/img/tabbar/category_active.svg" alt="">
		        <div slot="item-text">分类</div>
		      </tab-bar-item>
		      <tab-bar-item path="/cart" activeColor="deeppink">
		        <img slot="item-icon" src="./assets/img/tabbar/cart.svg" alt="">
		        <img slot="item-icon-active" src="./assets/img/tabbar/cart_active.svg" alt="">
		        <div slot="item-text">购物车</div>
		      </tab-bar-item>
		      <tab-bar-item path="/profile" activeColor="deeppink">
		        <img slot="item-icon" src="./assets/img/tabbar/profile.svg" alt="">
		        <img slot="item-icon-active" src="./assets/img/tabbar/profile_active.svg" alt="">
		        <div slot="item-text">我的</div>
		      </tab-bar-item>
		    </tab-bar>
		    <router-view></router-view>
		  </div>
		</template>
		
		<script>
		  /*1、导入组件*/
		  import TabBar from "./components/tabbar/TabBar";
		  import TabBarItem from "./components/tabbar/TabBarItem";
		  export default {
		    name: 'App',
		    components:{
		      /*2、注册组件*/
		      TabBar,
		      TabBarItem
		    }
		  }
		</script>
		
		<style>
		  /*导入基本样式*/
		  @import "./assets/css/base.css";
		</style>
		
  • 4、index.js 中设置相关属性
    在这里插入图片描述

3、给 TabBarItem 传入 active 图片

  • 在 TabBarItem 组件中设置插槽用于存放图片以及文字,并设置active下的style属性为color:red
    在这里插入图片描述
  • 在App.vue中设置两种状态下的的图片显示
    在这里插入图片描述

文件路径的引用问题——取别名
在这里插入图片描述

情况二:HTML中的图片路径需要加~
在这里插入图片描述

练习三:返回顶部BackTop

1. 实现逻辑

  1. .native - 对组件的监听事件
  2. 使用@click.native="backClick",执行对原生组件的根元素的监听
  • 在我们需要监听一个组件的原生事件时,必须给对应的事件加上.native修饰符,才能进行监听
  • 例如:拿到组件.scroll内容.scrollTo方法()
    在这里插入图片描述

在这里插入图片描述

2.代码实现

  1. src/components/content/backTop/BackTop.vue
<template>
  <div class="back-top">
    <img src="~assets/img/common/top.png" alt="">
  </div>
</template>

<script>
  export default {
    name: "BackTop"
  }
</script>

<style scoped>
  .back-top {
    position: fixed;
    right: 8px;
    bottom: 55px;
  }

  .back-top img {
    width: 43px;
    height: 43px;
  }
</style>

  1. src/components/common/scroll/Scroll.vue
<template>
  <div class="wrapper" ref="wrapper">
    <div class="content">
      <slot></slot>
    </div>
  </div>
</template>

<script>
  import BScroll from 'better-scroll'

  export default {
    name: "Scroll",
    props: {
      probeType: {
        type: Number,
        default: 0
      },
      pullUpLoad: {
        type: Boolean,
        default: false
      }
    },
    data() {
      return {
        scroll: null,
        message: '哈哈哈'
      }
    },

    mounted() {
      // 1.创建BScroll对象
      this.scroll = new BScroll(this.$refs.wrapper, {
        click: true,
        probeType: this.probeType,
        pullUpLoad: this.pullUpLoad
      })

      // 2.监听滚动的位置
      if (this.probeType === 2 || this.probeType === 3) {
        this.scroll.on('scroll', (position) => {
          // console.log(position);
          this.$emit('scroll', position)
        })
      }

      // 3.监听scroll滚动到底部
      if (this.pullUpLoad) {
        this.scroll.on('pullingUp', () => {
          this.$emit('pullingUp')
        })
      }
    },
    methods: {
      scrollTo(x, y, time=300) {
        this.scroll && this.scroll.scrollTo(x, y, time)
      },
      refresh() {
        this.scroll && this.scroll.refresh()
      },
      finishPullUp() {
        this.scroll && this.scroll.finishPullUp()
      },
      getScrollY() {
        return this.scroll ? this.scroll.y : 0
      }
    }
  }
</script>

<style scoped>

</style>


  1. src/views/home/Home.vue
<template>
  <div id="home" class="wrapper">
    <nav-bar class="home-nav"><div slot="center">购物街</div></nav-bar>
    <tab-control :titles="['流行', '新款', '精选']"
                 @tabClick="tabClick"
                 ref="tabControl1"
                 class="tab-control" v-show="isTabFixed"/>
    <scroll class="content"
            ref="scroll"
            :probe-type="3"
            @scroll="contentScroll"
            :pull-up-load="true"
            @pullingUp="loadMore">
      <home-swiper :banners="banners" @swiperImageLoad="swiperImageLoad"/>
      <recommend-view :recommends="recommends"/>
      <feature-view/>
      <tab-control :titles="['流行', '新款', '精选']"
                   @tabClick="tabClick"
                   ref="tabControl2"/>
      <good-list :goods="showGoods"/>
    </scroll>
    <back-top @click.native="backClick" v-show="isShowBackTop"/>
  </div>
</template>

<script>
  import HomeSwiper from './childComps/HomeSwiper'
  import RecommendView from './childComps/RecommendView'
  import FeatureView from './childComps/FeatureView'

  import NavBar from 'components/common/navbar/NavBar'
  import TabControl from 'components/content/tabControl/TabControl'
  import GoodList from 'components/content/goods/GoodsList'
  import Scroll from 'components/common/scroll/Scroll'
  import BackTop from 'components/content/backTop/BackTop'

  import { getHomeMultidata, getHomeGoods } from "network/home"
  import {debounce} from "common/utils";

  export default {
    name: "Home",
    components: {
      HomeSwiper,
      RecommendView,
      FeatureView,
      NavBar,
      TabControl,
      GoodList,
      Scroll,
      BackTop
    },
    data() {
      return {
        banners: [],
        recommends: [],
        goods: {
          'pop': {page: 0, list: []},
          'new': {page: 0, list: []},
          'sell': {page: 0, list: []},
        },
        currentType: 'pop',
        isShowBackTop: false,
        tabOffsetTop: 0,
        isTabFixed: false,
        saveY: 0
      }
    },
    computed: {
      showGoods() {
        return this.goods[this.currentType].list
      }
    },
    destroyed() {
      console.log('home destroyed');
    },
    activated() {
      this.$refs.scroll.scrollTo(0, this.saveY, 0)
      this.$refs.scroll.refresh()
    },
    deactivated() {
      this.saveY = this.$refs.scroll.getScrollY()
    },
    created() {
      // 1.请求多个数据
      this.getHomeMultidata()

      // 2.请求商品数据
      this.getHomeGoods('pop')
      this.getHomeGoods('new')
      this.getHomeGoods('sell')
    },
    mounted() {
      // 1.图片加载完成的事件监听
      const refresh = debounce(this.$refs.scroll.refresh, 50)
      this.$bus.$on('itemImageLoad', () => {
        refresh()
      })
    },
    methods: {
      /**
       * 事件监听相关的方法
       */
      tabClick(index) {
        switch (index) {
          case 0:
            this.currentType = 'pop'
            break
          case 1:
            this.currentType = 'new'
            break
          case 2:
            this.currentType = 'sell'
            break
        }
        this.$refs.tabControl1.currentIndex = index;
        this.$refs.tabControl2.currentIndex = index;
      },
      backClick() {
        /*返回顶部*/
        this.$refs.scroll.scrollTo(0, 0)
      },
      /*该方法是判断滑动的时候是否显示返回顶部的按钮*/
      contentScroll(position) {
        // 1.判断BackTop是否显示,加负号是为了让其转为正数
        this.isShowBackTop = (-position.y) > 1000

        // 2.决定tabControl是否吸顶(position: fixed)
        this.isTabFixed = (-position.y) > this.tabOffsetTop
      },
      loadMore() {
        this.getHomeGoods(this.currentType)
      },
      swiperImageLoad() {
        this.tabOffsetTop = this.$refs.tabControl2.$el.offsetTop;
      },
      /**
       * 网络请求相关的方法
       */
      getHomeMultidata() {
        getHomeMultidata().then(res => {
          this.banners = res.data.banner.list;
          this.recommends = res.data.recommend.list;
        })
      },
      getHomeGoods(type) {
        const page = this.goods[type].page + 1
        getHomeGoods(type, page).then(res => {
          this.goods[type].list.push(...res.data.list)
          this.goods[type].page += 1

          // 完成上拉加载更多
          this.$refs.scroll.finishPullUp()
        })
      }
    }
  }
</script>
<!--scoped 表示局部作用域,代表此处的css只在当前文件中起效果
-->

<style scoped>
  #home {
    /*padding-top: 44px;*/
    height: 100vh; /*vh 表示viewport height视口高度*/
    position: relative;
  }

  .home-nav {
    background-color: var(--color-tint);
    color: #fff;

    /*在使用浏览器原生滚动时, 为了让导航不跟随一起滚动*/
    /*position: fixed;*/
    /*left: 0;*/
    /*right: 0;*/
    /*top: 0;*/
    /*z-index: 9;*/
  }

  .content {
    overflow: hidden;

    position: absolute;
    top: 44px;
    bottom: 49px;
    left: 0;
    right: 0;
  }

  .tab-control {
    position: relative;
    z-index: 9;
  }

  /*.content {*/
    /*height: calc(100% - 93px);*/
    /*overflow: hidden;*/
    /*margin-top: 44px;*/
  /*}*/
</style>

练习四:滑动中的上拉加载更多

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

练习五:Vuejs实现轮播图

点击跳转:https://www.cnblogs.com/zhuhukang.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

博客zhu虎康

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值