vue3学习的第四天

142 阅读4分钟

1.给窗口监听滚动事件获得距离顶部的像素值

// 封装各种逻辑函数方法
// 1. 先完成函数的架子(参数+返回值)
// 2. 实现核心逻辑
import { ref } from 'vue'
export function useWindowScroll () {
  // 1. y是一个响应式数据
  // 2. 返回的是一个对象
  const y = ref(0)
  // 核心逻辑:在滚动事件中不断拿到距离顶部的像素值
  window.addEventListener('scroll', () => {
    // 获取一下距离顶部的像素值
    const scrollTop = document.documentElement.scrollTop
    // 把像素值 交给我们用来使用的响应式数据y
    y.value = scrollTop
  })
  return {
    y
  }
}

给div 一个属性判断 大于多少的时候为true
  <div class="app-header-sticky" :class="{show: y > 78}">

2. Home-骨架组件优化

1.写两个tempalate标签 通过判断渲染数据的长度,判断显示那个标签

2.通过全局注册的组件来写骨架屏 通过往组件里传入数据来显示骨架屏的宽高,

3.最后渲染数据,循环显示骨架屏

      <!-- 骨架屏模板 用骨架屏小组件 搭建一个类似于真实结构的模板出来-->
      <!--
        加不加冒号都是父传子 都可以传过去
        加冒号 -> 识别类型 响应式数据 (number,布尔值,数组,对象)
        不加冒号 -> 字符串
       -->
      <ul class="menu">
        <li v-for="i in 9" :key="i">
          <XtxSkeleton
            :width="40"
            :height="20"
            style="margin-right: 5px"
            bg="rgba(255,255,255,0.2)"
          />
          <XtxSkeleton
            :width="50"
            :height="20"
            bg="rgba(255,255,255,0.2)"
            style="margin-right: 5px"
          />
          <XtxSkeleton :width="50" :height="20" bg="rgba(255,255,255,0.2)" />
        </li>
      </ul>
    </template>

3.新鲜好物组件的封装和利用

/**
  思路:
    1. 不管其他事儿 先把一个基础的静态结构搭建起来
    2. 找到那些可能由于使用的地方不同 可变的地方
       主标题  副标题  右侧内容  列表主体内容
    3. 把可变的那些部分 转化成组件props或者是插槽
       props -> 数据  主标 + 副标题
       slot -> 模板(html)  右侧 + 列表主体

  组件传参的思想

    基于组件本身 说一下参数传递的事儿
    一个组件 -> 函数
    函数 可以接收参数  返回值(一个函数执行完毕之后 你可以得到什么)
    function get(num,name,cb){
    }

    组件 可以接收参数 props(数据 类型) + slot(模板 html)
         返回值?  最终呈现到浏览器中的UI

    UI = fn(state)  组件 确实就是一个函数
 */

1.通过封装组件来完成布局
1.引入并使用组件 通过插槽技术和组件传值来完成

        <homePannelVue title="人气推荐" sub-title="新鲜出炉 品质靠谱">
          <template #right>
            <XtxMore />
          </template>
          <template #main>
            <!-- 数据列表 -->
            <ul class="goods-list">
              <li v-for="item in hotData" :key="item.id">
                <RouterLink to="/">
                  <!-- 把原来的src去掉 由我们的指令接管 -->
                  <img alt v-img-lazy="item.picture"/>
                  <p class="name">{{ item.title }}</p>
                  <p class="desc">{{ item.alt }}</p>
                </RouterLink>
              </li>
            </ul>
          </template>
        </homePannelVue>

4.图片懒加载

// vue3有什么变化?

// 插件?
// 1. 定义插件 2.注册插件
// 定义插件 俩种方式  对象定义法  函数定义法

/*
// vue2 定义
const plugin = {
  install (Vue) {
    // 插件的逻辑
  }
}
// 如果是函数定义的话 函数本身会被当成 install方法执行
function plugin(Vue){
  // 插件的逻辑
}
// 注册 调用use方法的时候 会自动执行插件的install方法并且会自动把Vue构造函数传入
// install方法中

Vue.use(plugin)
*/

/**
  vue3
  先定义 再注册
  定义的俩种写法也没有变化  对象定义法 + 函数定义法

  const plugin = {
    install(app){
      // 插件逻辑
    }
  }
  // 当执行use方法的时候 应用实例对象app会自动当成实参传入install方法
  createApp().use(plugin)
 */

/**
 * vue2
 Vue.direactive('指令名称',{
   inserted(el, binding){
    // el: 指令挂在哪个元素 就指的是那个dom对象
    // binding.value: 指令等于号后面的表达式的值
   }
 })
 <img v-指令名称="表达式"/>

 vue3
 createApp() -> app
 app.direactive('指令名称',{
   mounted(el, binding){
     // el: 指令挂在哪个元素 就指的是那个dom对象
     // binding.value: 指令等于号后面的表达式的值
   }
 })
 */
import { useIntersectionObserver } from '@vueuse/core'
const plugin = {
  install (app) {
    console.log('插件已经注册好了', app)
    // 定义指令
    app.directive('img-lazy', {
      mounted (el, binding) {
        console.log(el, binding.value)
        // 在这里写核心逻辑 架子已经都搭好了
        // 核心思想:监控图片是否进入到视口区域 如果进来才发送图片请求
        // 技术点:1. 如何知道图片进入到视口了? 2. 如何发送请求?
        //            vueUse                   img.src = 'url'
        const { stop } = useIntersectionObserver(
          el,
          ([{ isIntersecting }], observerElement) => {
            // isIntersecting: 布尔值 true代表监听的元素已经进入了视口
            // false代表监听的元素离开了视口
            // 这个函数的监听很精确 不光可以横向 还有纵向
            // 这个回调在每次进入离开视口都会不断执行
            // 图片一旦请求回来之后 就不需要你这个监听逻辑了?
            console.log(isIntersecting)
            if (isIntersecting === true) {
              // ? 当前图片进入视口了 赋值src正式发送图片请求
              el.src = binding.value
              // 优化 停止监听
              stop()
            }
          },
          // threshold: 0 - 1 值越小 代表进入的视口的面积越小就会触发
          // 反之越大触发 0 img刚刚露头就触发  1 完全暴露在视口中才会触发
          { threshold: 0 }
        )
      }
    })
  }
}

export default plugin

5.完成路由组件和跳转

1.在路由界面配置子路由

  {
    path: '/Login',
    component: Login
  },
  {
    path: '/',
    component: Layout,
    children: [
      {
        path: '', // ? 如果二级路由的path为空串的时候 当前的二级路由将作为默认二级路由显示
        component: Home
      },
      {
        // 添加动态路由参数 用占位符站位 /:id
        path: 'category/:id',
        component: Category
      }

    ]
  }
]

2.在需要做跳转的页面加上

<RouterLink :to="`/category/${item.id}`">{{item.name}}</RouterLink>
  1. 需要传参加模板字符串

query 是 ?传参 params 是 :传参 路径传参

6.通过从路径上拿参数

import { ref } from 'vue'
import { findTopCategory } from '@/api/category'
import { useRoute } from 'vue-router'
export function useCategory () {
  const categoryData = ref({})
  const route = useRoute() // this.$route
  async function loadCategory () {
    // id? 路由上面获取的? this.$route.params.id
    // setup使用路由参数? useRoute
    // 如果想要路由跳转? useRouter  this.$router
    const res = await findTopCategory(route.params.id)
    categoryData.value = res.data.result
  }
  loadCategory()
  return {
    categoryData
  }
}

主页面解构拿参数 const {categoryData} =useCategory ()