【B站 heima】小兔鲜Vue3 项目学习笔记Day02

文章目录

    • Pinia
      • 1.使用
      • 2. pinia-计数器案例
      • 3. getters实现
      • 4. 异步action
      • 5. storeToRefsx 数据解构保持响应式
      • 6. pinia 调试
    • 项目起步
      • 1.项目初始化和git管理
      • 2. 使用ElementPlus
      • 3. ElementPlus 主题色定制
      • 4. axios 基础配置
      • 5. 路由设计
      • 6. 静态资源初始化和 Error lens安装
      • 7.scss自动导入
      • 8. Layout静态模板结构搭建
      • 9. Layout字体图标引入
      • 10.Layout一级导航渲染
      • 11. layout - 吸顶导航
      • 12. layout - Pinia优化重复请求
    • 小结

Pinia

1.使用

vue专属状态管理库,vuex替代

优势:

  • 提供了更简单的API ,去掉了mutation
  • 提供了组合式API
  • 去掉了modules,每个store都是独立的模块
  • 搭配TS一起使用提供可靠的类型判断

Pinia添加到vue项目中:

  • 创建一个新vue项目: create init vue@latest,装依赖,项目跑起来
  • 打开pinia官方文档,是个小菠萝。点击开始,有个安装选项

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  • 我是使用npm安装:npm install pinia

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  • 按照文档使用 pinia
    在这里插入图片描述
  • 在项目中实际应用(记不住看文档使用即可

在这里插入图片描述

2. pinia-计数器案例

看官方文档的基础实例学习如何使用

找和vue3语法相似的语法进行使用

在这里插入图片描述

  • 创建一个 store( state+action )

src添加一个stores文件夹,新建文件counter.js

//counter.js
// 导入一个方法 defineStore
import { defineStore } from 'pinia'
import {ref} form 'vue'// 参数:标识 回调函数
//!!变量名字需保持规范:use+函数名 
//useCounterStore是一个方法,需执行才能得到真是store实例对象
export const useCounterStore = defineStore('counter', () => {//1.定义数据stateconst count = ref(0)// 2.定义修改数据的方法(action 同步+异步)const increment = () => {count.value++}// 3.以对象的方式return供组件使用return {count,increment}
})
  • 组件使用 store
<script setup>
//1.导入use 打头的方法
import { useCounterStore } from './stores/counter';
//2.执行方法获得store实例对象
const counterStore = useCounterStore();
// console.log(counterStore)  打印看看里面是否有count和increment
</script><template><button @click="counterStore.increment">{{ counterStore.count }}</button>
</template>

3. getters实现

pinia中的getters直接使用computed函数进行模拟

//counter.js
// 导入一个方法 defineStore
import { defineStore } from 'pinia'
import { computed, ref } from 'vue'//定义并暴露一个函数useCounterStore 参数:标识 回调函数
export const useCounterStore = defineStore('counter', () => {//1.定义数据stateconst count = ref(0)// 2.定义修改数据的方法(action 同步+异步)const increment = () => {count.value++}// --  --  getters实现  --   --const doubleCount = computed(() => count.value * 2)// 3.以对象的方式return供组件使用return {count,increment,doubleCount}
})

这时useCountStore中就有了doubleCount这个方法了

<!--App.vue-->
<template><button @click="counterStore.increment">{{ counterStore.count }}</button>{{ counterStore.doubleCount }}
</template>

在这里插入图片描述

4. 异步action

action中实现异步和组件中定义数据和方法的风格完全一致

安装axios: npm install axios

举个获取数据列表 的栗子,获取数据接口地址:http://geek.itheima.net/v1_0/channels

//counter.jsconst list = ref([])  //存放列表数据
//异步actionconst getList = async () => {const res = await axios.get('http://geek.itheima.net/v1_0/channels');}//返回,让组件可以拿到return{list,getList}
<script setup>
//1.导入use 打头的方法
import { onMounted } from 'vue';
import { useCounterStore } from './stores/counter';
//2.执行方法获得store实例对象
const counterStore = useCounterStore();
// console.log(counterStore)
onMounted(() => {//获取数据counterStore.getList()
})
</script>

看一下网页的网络

在这里插入图片描述

给list赋值

//异步actionconst getList = async () => {const res = await axios.get('http://geek.itheima.net/v1_0/channels');list.value = res.data.data.channels}

渲染在页面上,使用v-for

<template><button @click="counterStore.increment">{{ counterStore.count }}</button>{{ counterStore.doubleCount }}<ul><li v-for="item in counterStore.list" :key="item.id">{{ item.name }}</li></ul>
</template>

效果:
在这里插入图片描述

5. storeToRefsx 数据解构保持响应式

辅助保持数据(state+getter)的响应式解构

方法可以正常解构赋值哈

const {count,doubleCount} = counterStore

这样解构是不可以的,会造成响应式丢失,也就是数据变化页面不会更新。

我们可以这样写:

const {count,doubleCount} = storeToRefs(counterStore);

6. pinia 调试

使用之前使用的devtools调试工具

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

项目起步

1.项目初始化和git管理

创建并打开,将项目运行起来(按照绿色的来做):
在这里插入图片描述

这样说明成功

在这里插入图片描述

下面我们看一下 小兔鲜 需要哪些基础目录,

我们按照下面的图片在刚创建好的项目中创建文件夹。

componsables组合函数文件夹:存放通用的函数

使用git管理项目,手动初始化

执行命令并完成手动提交

git init
git add .
git commit -m "init"

配置别名路径联想提示

编写代码,一旦输入@/vscode会立刻联想出src所有的子目录和文件,统一文件路径,不容易出错。

步骤:1.根目录新增jsconfig.json文件

​ 2.添加配置项
在这里插入图片描述

2. 使用ElementPlus

我们在这个项目中使用了通用性组件,由ElementPlus提供

步骤:安装 - 按需引入 - 测试组件

看文档

在这里插入图片描述

安装elementPlus:npm install element-plus --save

安装两个插件:npm install -D unplugin-vue-components unplugin-auto-import

安装之后我们来依照文档配置这两个插件

//vite.config.js//按需导入element Plus插件
import AutoImport from 'unplugin-auto-import/vite'
import Components from 'unplugin-vue-components/vite'
import { ElementPlusResolver } from 'unplugin-vue-components/resolvers'// https://vitejs.dev/config/
export default defineConfig({plugins: [  //插件配置文件vue(),//elementPlus插件AutoImport({resolvers: [ElementPlusResolver()],}),Components({resolvers: [ElementPlusResolver()],}),],

配置文件写好后,重启项目

做个测试,看看组件能不能使用

<template><el-button type="primary">elementPlus</el-button>
</template>

在这里插入图片描述

生效就OK

3. ElementPlus 主题色定制

小免鲜主题色和elementPlus默认的主题色存在冲突

通过定制主题让elementPlus的主题色和小兔鲜项目保持一致

步骤

  • 安装sass:npm i sass -D
    在这里插入图片描述

  • 准备定制文件 :styles/element/index.scss

/* 只需要重写你需要的即可 */
@forward 'element-plus/theme-chalk/src/common/var.scss' with ($colors: ('primary': (// 主色'base': #27ba9b,),'success': (// 成功色'base': #1dc779,),'warning': (// 警告色'base': #ffb302,),'danger': (// 危险色'base': #e26237,),'error': (// 错误色'base': #cf4444,),)
)
  • ElementPlus样式进行覆盖:通知Element使用scss语言,自动导入定制的scss文件覆盖。
//vite.config.jsimport { fileURLToPath, URL } from 'node:url'import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'//按需导入element Plus插件
import AutoImport from 'unplugin-auto-import/vite'
import Components from 'unplugin-vue-components/vite'
import { ElementPlusResolver } from 'unplugin-vue-components/resolvers'// https://vitejs.dev/config/
export default defineConfig({plugins: [  //插件配置文件vue(),//elementPlus插件AutoImport({// 1.配置elementPlus采用sass样式配色系统resolvers: [ElementPlusResolver()],}),Components({resolvers: [ElementPlusResolver({ importStyle: 'sass' })],}),],resolve: {alias: {'@': fileURLToPath(new URL('./src', import.meta.url))}},css: {preprocessorOptions: {scss: {//2.自动导入定制化样式文件进行样式覆盖additionalData: `@use "@/styles/element/index.scss" as *;`}}}
})

在这里插入图片描述

4. axios 基础配置

安装:npm i axios

配置基础实例(统一接口实例)

在这里插入图片描述

utils创建一个http.js

//axios基础封装
import axios from "axios";const httpInstance = axios.create({baseURL: 'http://pcapi-xiaotuxian-front-devtest.itheima.net',timeout: '5000'           //5s
})//拦截器,默认先这样写着,后面有需求再配置
// axios请求拦截器
instance.interceptors.request.use(config => {return config
}, e => Promise.reject(e))// axios响应式拦截器
instance.interceptors.response.use(res => res.data, e => {return Promise.reject(e)
})export default httpInstance

扩展:如果项目里面不同的业务模块需要的接口基地址不同,该如何来做?

答:axios.create()方法可以执行多次,每次执行就会生成一个新
的实例

const http1 = axios.create({baseURL:'url1'})
const http1 = axios.create({baseURL:'url2'})

5. 路由设计

  • 设计首页和登录页的路由(一级路由)

    路由设计规则:找内容切换的区域,如果是页面整体切换,则为一级路由

eslintrc.cjs配置,避免命名报错:

/* eslint-env node */
module.exports = {root: true,'extends': ['plugin:vue/vue3-essential','eslint:recommended'],parserOptions: {ecmaVersion: 'latest'},rules: {'vue/multi-word-component-names':0, //不再强制要求组件命名}
}

删除views文件夹下的组件,创建两个新文件夹LoginLayout分别创建一个index.vue文件,写入一些代码。

<template>
<h2>我是注册页/首页</h2>
</template>

打开router文件夹的index.js,删掉默认的代码。导入loginlayout组件,在routes中配置path、component属性

import { createRouter, createWebHistory } from 'vue-router'
import Login from '@/views/Login/index.vue'
import Layout from '@/views/Layout/index.vue'
// createRouter:创建router实例对象
// createWebHistory:创建history模式的路由const router = createRouter({history: createWebHistory(import.meta.env.BASE_URL),routes: [{path: '/',component: Layout},{path: '

App.vue中写入一级路由出口组件

<script setup>
import { RouterLink, RouterView } from 'vue-router'</script><template><!-- 一级路由出口组件 --><RouterView />
</template>

项目运行效果:

在这里插入图片描述

  • 设计分类页和默认Home页路由(二级路由)

路由设计原则:找内容切换的区域,如果是在一级路由页的内部切换,则为二级路由

和上面一样,在views新增两个文件夹,一个Home,一个Category,分别创建一个index.vue,随便写点内容

//router index.js
import { createRouter, createWebHistory } from 'vue-router'
import Login from '@/views/Login/index.vue'
import Layout from '@/views/Layout/index.vue'
import Home from '@/views/Home/index.vue'
import Category from '@/views/Category/index.vue'
// createRouter:创建router实例对象
// createWebHistory:创建history模式的路由const router = createRouter({history: createWebHistory(import.meta.env.BASE_URL),routes: [{path: '/',component: Layout,children: [{path: '',component: Home},{path: 'category',component: Category}]},{path: '/login',component: Login}]
})export default router

这两个二级路由要在Layout组件里给准备路由出口

<!--Layout index.vue-->
<template><h2>我是首页</h2><!-- 二级路由出口 --><RouterView />
</template>

效果:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

6. 静态资源初始化和 Error lens安装

图片资源 - 把images文件夹放到assets目录下

样式资源 - 把common.scss文件放到styles目录下(这个文件在资源里面,自己拿)。

main.js中引入common.scss

//main.js
//引入初始化样式文件
import '@/styles/common.scss'

error lens是一个实时提供错误警告信息的VScode插件,方便开发,在扩展程序里搜索然后安装就可以了。

7.scss自动导入

在项目里一些组件共享的色值会以scss变量的方式统一放到一个名为var.scss 的文件中。

正常组件中使用,需要先导入scss文件,再使用内部的变量,比较繁琐,自动导入可以免去手动导入的步骤,直接使用内部的变量

配置步骤:

  • 新增一个var.scss文件,存入色值变量
$xtxColor: #27ba9b;
$helpColor: #e26237;
$sucColor: #1dc779;
$warnColor: #ffb302;
$priceColor: #cf4444;
  • 通过vite.config.js配置自动导入文件
 css: {preprocessorOptions: {scss: {//2.自动导入定制化样式文件进行样式覆盖additionalData: `@use "@/styles/element/index.scss" as *;@use "@/styles/var.scss" as *;`,}}}

8. Layout静态模板结构搭建

在这里插入图片描述

Layout文件夹创建一个components文件夹,创建LayoutFooter.vue、LayoutHeader.vue、LayoutNav.vue组件。

<!--LayoutNav.vue-->
<script setup></script><template><nav class="app-topnav"><div class="container"><ul><template v-if="true"><li><a href="javascript:;""><i class="iconfont icon-user"></i>周杰伦</a></li><li><el-popconfirm title="确认退出吗?" confirm-button-text="确认" cancel-button-text="取消"><template #reference><a href="javascript:;">退出登录</a></template></el-popconfirm></li><li><a href="javascript:;">我的订单</a></li><li><a href="javascript:;">会员中心</a></li></template><template v-else><li><a href="javascript:;">请先登录</a></li><li><a href="javascript:;">帮助中心</a></li><li><a href="javascript:;">关于我们</a></li></template></ul></div></nav>
</template><style scoped lang="scss">
.app-topnav {background: #333;ul {display: flex;height: 53px;justify-content: flex-end;align-items: center;li {a {padding: 0 15px;color: #cdcdcd;line-height: 1;display: inline-block;i {font-size: 14px;margin-right: 2px;}&:hover {color: $xtxColor;}}~li {a {border-left: 2px solid #666;}}}}
}
</style>
<!--LayoutHeader.vue-->
<script setup></script><template><header class='app-header'><div class="container"><h1 class="logo"><RouterLink to="/">小兔鲜</RouterLink></h1><ul class="app-header-nav"><li class="home"><RouterLink to="/">首页</RouterLink></li><li> <RouterLink to="/">居家</RouterLink> </li><li> <RouterLink to="/">美食</RouterLink> </li><li> <RouterLink to="/">服饰</RouterLink> </li></ul><div class="search"><i class="iconfont icon-search"></i><input type="text" placeholder="搜一搜"></div><!-- 头部购物车 --></div></header>
</template><style scoped lang='scss'>
.app-header {background: #fff;.container {display: flex;align-items: center;}.logo {width: 200px;a {display: block;height: 132px;width: 100%;text-indent: -9999px;background: url('@/assets/images/logo.png') no-repeat center 18px / contain;}}.app-header-nav {width: 820px;display: flex;padding-left: 40px;position: relative;z-index: 998;li {margin-right: 40px;width: 38px;text-align: center;a {font-size: 16px;line-height: 32px;height: 32px;display: inline-block;&:hover {color: $xtxColor;border-bottom: 1px solid $xtxColor;}}.active {color: $xtxColor;border-bottom: 1px solid $xtxColor;}}}.search {width: 170px;height: 32px;position: relative;border-bottom: 1px solid #e7e7e7;line-height: 32px;.icon-search {font-size: 18px;margin-left: 5px;}input {width: 140px;padding-left: 5px;color: #666;}}.cart {width: 50px;.curr {height: 32px;line-height: 32px;text-align: center;position: relative;display: block;.icon-cart {font-size: 22px;}em {font-style: normal;position: absolute;right: 0;top: 0;padding: 1px 6px;line-height: 1;background: $helpColor;color: #fff;font-size: 12px;border-radius: 10px;font-family: Arial;}}}
}
</style>
<!--LayoutFooter.vue-->
<template><footer class="app_footer"><!-- 联系我们 --><div class="contact"><div class="container"><dl><dt>客户服务</dt><dd><i class="iconfont icon-kefu"></i> 在线客服</dd><dd><i class="iconfont icon-question"></i> 问题反馈</dd></dl><dl><dt>关注我们</dt><dd><i class="iconfont icon-weixin"></i> 公众号</dd><dd><i class="iconfont icon-weibo"></i> 微博</dd></dl><dl><dt>下载APP</dt><dd class="qrcode"><img src="@/assets/images/qrcode.jpg" /></dd><dd class="download"><span>扫描二维码</span><span>立马下载APP</span><a href="javascript:;">下载页面</a></dd></dl><dl><dt>服务热线</dt><dd class="hotline">400-0000-000 <small>周一至周日 8:00-18:00</small></dd></dl></div></div><!-- 其它 --><div class="extra"><div class="container"><div class="slogan"><a href="javascript:;"><i class="iconfont icon-footer01"></i><span>价格亲民</span></a><a href="javascript:;"><i class="iconfont icon-footer02"></i><span>物流快捷</span></a><a href="javascript:;"><i class="iconfont icon-footer03"></i><span>品质新鲜</span></a></div><!-- 版权信息 --><div class="copyright"><p><a href="javascript:;">关于我们</a><a href="javascript:;">帮助中心</a><a href="javascript:;">售后服务</a><a href="javascript:;">配送与验收</a><a href="javascript:;">商务合作</a><a href="javascript:;">搜索推荐</a><a href="javascript:;">友情链接</a></p><p>CopyRight © 小兔鲜儿</p></div></div></div></footer>
</template><style scoped lang='scss'>
.app_footer {overflow: hidden;background-color: #f5f5f5;padding-top: 20px;.contact {background: #fff;.container {padding: 60px 0 40px 25px;display: flex;}dl {height: 190px;text-align: center;padding: 0 72px;border-right: 1px solid #f2f2f2;color: #999;&:first-child {padding-left: 0;}&:last-child {border-right: none;padding-right: 0;}}dt {line-height: 1;font-size: 18px;}dd {margin: 36px 12px 0 0;float: left;width: 92px;height: 92px;padding-top: 10px;border: 1px solid #ededed;.iconfont {font-size: 36px;display: block;color: #666;}&:hover {.iconfont {color: $xtxColor;}}&:last-child {margin-right: 0;}}.qrcode {width: 92px;height: 92px;padding: 7px;border: 1px solid #ededed;}.download {padding-top: 5px;font-size: 14px;width: auto;height: auto;border: none;span {display: block;}a {display: block;line-height: 1;padding: 10px 25px;margin-top: 5px;color: #fff;border-radius: 2px;background-color: $xtxColor;}}.hotline {padding-top: 20px;font-size: 22px;color: #666;width: auto;height: auto;border: none;small {display: block;font-size: 15px;color: #999;}}}.extra {background-color: #333;}.slogan {height: 178px;line-height: 58px;padding: 60px 100px;border-bottom: 1px solid #434343;display: flex;justify-content: space-between;a {height: 58px;line-height: 58px;color: #fff;font-size: 28px;i {font-size: 50px;vertical-align: middle;margin-right: 10px;font-weight: 100;}span {vertical-align: middle;text-shadow: 0 0 1px #333;}}}.copyright {height: 170px;padding-top: 40px;text-align: center;color: #999;font-size: 15px;p {line-height: 1;margin-bottom: 20px;}a {color: #999;line-height: 1;padding: 0 10px;border-right: 1px solid #999;&:last-child {border-right: none;}}}
}
</style>

修改一下Layoutindex.vue

<script setup>
import LayoutNav from './components/LayoutNav.vue'
import LayoutHeader from './components/LayoutHeader.vue'
import LayoutFooter from './components/LayoutFooter.vue'
</script><template><LayoutNav /><LayoutHeader /><RouterView /><LayoutFooter />
</template>

效果:

在这里插入图片描述

9. Layout字体图标引入

在这里插入图片描述

这里的图标没有引入,我们使用的是阿里的字体图标库,使用 font-class 引用的方式

在这里插入图片描述

在这里插入图片描述

将这个加入到index.html文件中

  <link rel="stylesheet" href="//at.alicdn.com/t/font_2143783_iq6z4ey5vu.css">

效果:

在这里插入图片描述

看下面这个周杰伦旁边的小人儿

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

它对应的代码如下

在这里插入图片描述

10.Layout一级导航渲染

静态结构已经全部搭建好了,我们要使用后端接口渲染 渲染一级导航路由,也就是这:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

实现步骤:

  • 根据接口文档封装接口函数
  • 发生请求获取数据列表
  • v-for渲染页面

apis文件夹下创建layout.js文件,封装接口

import httpInstance from '@/utils/http.js'//获取目录
export function getCategoryAPI() {return httpInstance({url: '/home/category/head'})
}

来到LayoutHeader组件,引入接口

封装一个函数getCategory,返回的是promise对象,使用async/await

在挂载完成之后(onMounted)调用函数getCategory

打印res看一下请求的数据,定义一个响应式空数组categoryList接收后台传入的数据。

将 请求 封装进 函数 中是因为方便书写请求前后的逻辑

<script setup>
import { getCategoryAPI } from '@/apis/layout'
import { onMounted, ref } from 'vue'const categoryList = ref([])
const getCategory = async () => {const res = await getCategoryAPI()categoryList.value = res.result// console.log(res)
}onMounted(() => getCategory())</script>

在这里插入图片描述

获取数据成功之后,使用v-for渲染数据

<ul class="app-header-nav"><li class="home" v-for="item in categoryList" :key="item.id"><RouterLink to="/">{{ item.name }}</RouterLink></li></ul>

效果:外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

11. layout - 吸顶导航

需求:浏览器上下滚动过程中,如果距离顶部的滚动距离大于78px,吸顶导航显示,小于78px隐藏

步骤:

  • 准备吸顶导航组件
  • 获取滚动距离
  • 滚动距离作判断条件控制组件盒子展示或隐藏

吸顶导航组件

<script setup></script><template><div class="app-header-sticky"><div class="container"><RouterLink class="logo" to="/" /><!-- 导航区域 --><ul class="app-header-nav "><li class="home"><RouterLink to="/">首页</RouterLink></li><li><RouterLink to="/">居家</RouterLink></li><li><RouterLink to="/">美食</RouterLink></li><li><RouterLink to="/">服饰</RouterLink></li><li><RouterLink to="/">母婴</RouterLink></li><li><RouterLink to="/">个护</RouterLink></li><li><RouterLink to="/">严选</RouterLink></li><li><RouterLink to="/">数码</RouterLink></li><li><RouterLink to="/">运动</RouterLink></li><li><RouterLink to="/">杂项</RouterLink></li></ul><div class="right"><RouterLink to="/">品牌</RouterLink><RouterLink to="/">专题</RouterLink></div></div></div>
</template><style scoped lang='scss'>
.app-header-sticky {width: 100%;height: 80px;position: fixed;left: 0;top: 0;z-index: 999;background-color: #fff;border-bottom: 1px solid #e4e4e4;// 此处为关键样式!!!// 状态一:往上平移自身高度 + 完全透明transform: translateY(-100%);opacity: 0;// 状态二:移除平移 + 完全不透明&.show {transition: all 0.3s linear;transform: none;opacity: 1;}.container {display: flex;align-items: center;}.logo {width: 200px;height: 80px;background: url("@/assets/images/logo.png") no-repeat right 2px;background-size: 160px auto;}.right {width: 220px;display: flex;text-align: center;padding-left: 40px;border-left: 2px solid $xtxColor;a {width: 38px;margin-right: 40px;font-size: 16px;line-height: 1;&:hover {color: $xtxColor;}}}
}.app-header-nav {width: 820px;display: flex;padding-left: 40px;position: relative;z-index: 998;li {margin-right: 40px;width: 38px;text-align: center;a {font-size: 16px;line-height: 32px;height: 32px;display: inline-block;&:hover {color: $xtxColor;border-bottom: 1px solid $xtxColor;}}.active {color: $xtxColor;border-bottom: 1px solid $xtxColor;}}
}
</style>

在Layout文件夹下的index.vue中引入这个组件,使用起来

<script setup>
import LayoutNav from './components/LayoutNav.vue'
import LayoutHeader from './components/LayoutHeader.vue'
import LayoutFooter from './components/LayoutFooter.vue'
import LayoutFixed from './LayoutFixed.vue'
</script><template><LayoutNav /><LayoutHeader /><RouterView /><LayoutFooter /><LayoutFixed />
</template>

关键样式(LayoutFixed中):

.app-header-sticky {width: 100%;height: 80px;position: fixed;left: 0;top: 0;   //置顶z-index: 999;background-color: #fff;border-bottom: 1px solid #e4e4e4;// 此处为关键样式!!!// 状态一:往上平移自身高度 + 完全透明transform: translateY(-100%);   //平移出页面opacity: 0;  //透明度为0// 状态二:移除平移 + 完全不透明//想让组件显示出来只需要加上class = "show" 即可&.show {   transition: all 0.3s linear;transform: none;opacity: 1;  //完全不透明}

获取滚动距离,不自己写了,使用一个vueUse插件,安装一下
安装:npm i @vueuse/core

滚动使用的是useScroll,解构的这个y就是垂直方向滚动的距离。

import { useScroll } from '@vueuse/core'
const { y } = useScroll(window)

y>78时,show生效,我们使用 vue 的动态类实现方式

 <div class="app-header-sticky" :class="{ show: y > 78 }">

12. layout - Pinia优化重复请求

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

我们要把 吸顶导航 组件也转化成数据动态获取的,修改完后我们发现请求了两次数据

<!-- LayoutFixed -->
<script setup>
import { useScroll } from '@vueuse/core'
import { getCategoryAPI } from '@/apis/layout'
import { onMounted, ref } from 'vue'const categoryList = ref([])  //目录数据列表
const { y } = useScroll(window)  //获取滚动距离
const getCategory = async () => {const res = await getCategoryAPI()categoryList.value = res.result// console.log(res)
}onMounted(() => getCategory())</script><template><div class="app-header-sticky" :class="{ show: y > 78 }"><div class="container"><RouterLink class="logo" to="/" /><!-- 导航区域 --><ul class="app-header-nav"><li class="home" v-for="item in categoryList" :key="item.id"><RouterLink to="/">{{ item.name }}</RouterLink></li></ul><div class="right"><RouterLink to="/">品牌</RouterLink><RouterLink to="/">专题</RouterLink></div></div></div>
</template><style scoped lang='scss'>
.app-header-sticky {width: 100%;height: 80px;position: fixed;left: 0;top: 0;z-index: 999;background-color: #fff;border-bottom: 1px solid #e4e4e4;// 此处为关键样式!!!// 状态一:往上平移自身高度 + 完全透明transform: translateY(-100%);opacity: 0;// 状态二:移除平移 + 完全不透明&.show {transition: all 0.3s linear;transform: none;opacity: 1;}.container {display: flex;align-items: center;}.logo {width: 200px;height: 80px;background: url("@/assets/images/logo.png") no-repeat right 2px;background-size: 160px auto;}.right {width: 220px;display: flex;text-align: center;padding-left: 40px;border-left: 2px solid $xtxColor;a {width: 38px;margin-right: 40px;font-size: 16px;line-height: 1;&:hover {color: $xtxColor;}}}
}.app-header-nav {width: 820px;display: flex;padding-left: 40px;position: relative;z-index: 998;li {margin-right: 40px;width: 38px;text-align: center;a {font-size: 16px;line-height: 32px;height: 32px;display: inline-block;&:hover {color: $xtxColor;border-bottom: 1px solid $xtxColor;}}.active {color: $xtxColor;border-bottom: 1px solid $xtxColor;}}
}
</style>
<!-- LayoutHeader -->
<script setup>
import { getCategoryAPI } from '@/apis/layout'
import { onMounted, ref } from 'vue'const categoryList = ref([])
const getCategory = async () => {const res = await getCategoryAPI()categoryList.value = res.result// console.log(res)
}onMounted(() => getCategory())</script><template><header class='app-header'><div class="container"><h1 class="logo"><RouterLink to="/">小兔鲜~</RouterLink></h1><ul class="app-header-nav"><li class="home" v-for="item in categoryList" :key="item.id"><RouterLink to="/">{{ item.name }}</RouterLink></li></ul><div class="search"><i class="iconfont icon-search"></i><input type="text" placeholder="搜一搜"></div><!-- 头部购物车 --></div></header>
</template>
<style scoped lang='scss'>
.app-header {background: #fff;.container {display: flex;align-items: center;}.logo {width: 200px;a {display: block;height: 132px;width: 100%;text-indent: -9999px;background: url('@/assets/images/logo.png') no-repeat center 18px / contain;}}.app-header-nav {width: 820px;display: flex;padding-left: 40px;position: relative;z-index: 998;li {margin-right: 40px;width: 38px;text-align: center;a {font-size: 16px;line-height: 32px;height: 32px;display: inline-block;&:hover {color: $xtxColor;border-bottom: 1px solid $xtxColor;}}.active {color: $xtxColor;border-bottom: 1px solid $xtxColor;}}}.search {width: 170px;height: 32px;position: relative;border-bottom: 1px solid #e7e7e7;line-height: 32px;.icon-search {font-size: 18px;margin-left: 5px;}input {width: 140px;padding-left: 5px;color: #666;}}.cart {width: 50px;.curr {height: 32px;line-height: 32px;text-align: center;position: relative;display: block;.icon-cart {font-size: 22px;}em {font-style: normal;position: absolute;right: 0;top: 0;padding: 1px 6px;line-height: 1;background: $helpColor;color: #fff;font-size: 12px;border-radius: 10px;font-family: Arial;}}}
}
</style>

在这里插入图片描述

stores新增category.js

import { ref } from 'vue'
import { defineStore } from 'pinia'
import { getCategoryAPI } from '@/apis/layout'
export const useCategoryStore = defineStore('category', () => {// 导航列表的数据管理// state 导航列表数据const categoryList = ref([])// action 获取导航数据的方法const getCategory = async () => {const res = await getCategoryAPI()categoryList.value = res.result}return {categoryList,getCategory}
})

使用:

Login文件夹的index.vue

<script setup>
//出发获取导航列表的action
import { useCategoryStore } from '@/stores/category.js'
import { onMounted } from 'vue'const categoryStore = useCategoryStore()onMounted(() => categoryStore.getCategory())
</script>

删掉(注释)LoginFixedLoginHeader中相关的代码

<!-- LayoutFixed -->
<script setup>
import { useScroll } from '@vueuse/core'
// import { getCategoryAPI } from '@/apis/layout'
// import { onMounted, ref } from 'vue'// const categoryList = ref([])  //目录数据列表
const { y } = useScroll(window)  //获取滚动距离
// const getCategory = async () => {
//     const res = await getCategoryAPI()
//     categoryList.value = res.result
//     // console.log(res)
// }// onMounted(() => getCategory())// 使用pinia中的数据
import { useCategoryStore } from '@/stores/category.js'const categoryStore = useCategoryStore()</script>
<template><ul class="app-header-nav"><li class="home" v-for="item in categoryStore.categoryList" :key="item.id"><RouterLink to="/">{{ item.name }}</RouterLink></li></ul>
</template>
<script setup>
import { useCategoryStore } from '@/stores/category.js'
// import { getCategoryAPI } from '@/apis/layout'
// import { onMounted, ref } from 'vue'// const categoryList = ref([])
// const getCategory = async () => {
//     const res = await getCategoryAPI()
//     categoryList.value = res.result
//     // console.log(res)
// }// onMounted(() => getCategory())
const categoryStore = useCategoryStore()</script>
<template><ul class="app-header-nav"><li class="home" v-for="item in categoryStore.categoryList" :key="item.id"><RouterLink to="/">{{ item.name }}</RouterLink></li></ul>
</template>

OK,没问题

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

小结

本篇文章,主要学习了Pinia管理数据,以及Layout的相关知识
私密马赛,图片有亿点糊,我是在typra上面写的,截到csdn上就糊掉了呜呜
祝大家学习顺利!!
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/web/15016.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Github 2024-05-24 开源项目日报 Top10

根据Github Trendings的统计,今日(2024-05-24统计)共有10个项目上榜。根据开发语言中项目的数量,汇总情况如下: 开发语言项目数量Python项目3非开发语言项目2TypeScript项目2JavaScript项目1Kotlin项目1C#项目1C++项目1Shell项目1Microsoft PowerToys: 最大化Windows系统生产…

软件设计师备考笔记(十):网络与信息安全基础知识

文章目录 一、网络概述二、网络互连硬件&#xff08;一&#xff09;网络的设备&#xff08;二&#xff09;网络的传输介质&#xff08;三&#xff09;组建网络 三、网络协议与标准&#xff08;一&#xff09;网络的标准与协议&#xff08;二&#xff09;TCP/IP协议簇 四、Inter…

某神,云手机启动?

某神自从上线之后&#xff0c;热度不减&#xff0c;以其丰富的内容和独特的魅力吸引着众多玩家&#xff1b; 但是随着剧情无法跳过&#xff0c;长草期过长等原因&#xff0c;近年脱坑的玩家多之又多&#xff0c;之前米家推出了一款云某神的app&#xff0c;目标是为了减少用户手…

RedisTemplateAPI:String

文章目录 ⛄1 String 介绍⛄2 命令⛄3 对应 RedisTemplate API❄️❄️ 3.1 添加缓存❄️❄️ 3.2 设置过期时间(单独设置)❄️❄️ 3.3 获取缓存值❄️❄️ 3.4 删除key❄️❄️ 3.5 顺序递增❄️❄️ 3.6 顺序递减 ⛄4 以下是一些常用的API⛄5 应用场景 ⛄1 String 介绍 Str…

ue引擎游戏开发笔记(47)——设置状态机解决跳跃问题

1.问题分析&#xff1a; 目前当角色起跳时&#xff0c;只是简单的上下移动&#xff0c;空中仍然保持行走动作&#xff0c;并没有设置跳跃动作&#xff0c;因此&#xff0c;给角色设置新的跳跃动作&#xff0c;并优化新的动作动画。 2.操作实现&#xff1a; 1.实现跳跃不复杂&…

Java中的继承和多态

继承 在现实世界中&#xff0c;狗和猫都是动物&#xff0c;这是因为他们都有动物的一些共有的特征。 在Java中&#xff0c;可以通过继承的方式来让对象拥有相同的属性&#xff0c;并且可以简化很多代码 例如&#xff1a;动物都有的特征&#xff0c;有名字&#xff0c;有年龄…

Mybatis源码剖析---第一讲

Mybatis源码剖析 基础环境搭建 JDK8 Maven3.6.3&#xff08;别的版本也可以…&#xff09; MySQL 8.0.28 --> MySQL 8 Mybatis 3.4.6 准备jar&#xff0c;准备数据库数据 把依赖导入pom.xml中 <properties><project.build.sourceEncoding>UTF-8</p…

Linux学习笔记:线程

Linux中的线程 什么是线程线程的使用原生线程库创建线程线程的id线程退出等待线程join分离线程取消一个线程线程的局部存储在c程序中使用线程使用c自己封装一个简易的线程库 线程互斥(多线程)导致共享数据出错的原因互斥锁关键函数pthread_mutex_t :创建一个锁pthread_mutex_in…

雷电预警监控系统:守护安全的重要防线

TH-LD1在自然界中&#xff0c;雷电是一种常见而强大的自然现象。它既有震撼人心的壮观景象&#xff0c;又潜藏着巨大的安全风险。为了有效应对雷电带来的威胁&#xff0c;雷电预警监控系统应运而生&#xff0c;成为现代社会中不可或缺的安全防护工具。 雷电预警监控系统的基本…

makefile 编写规则

1.概念 1.1 什么是makefile Makefile 是一种文本文件&#xff0c;用于描述软件项目的构建规则和依赖关系&#xff0c;通常用于自动化软件构建过程。它包含了一系列规则和指令&#xff0c;告诉构建系统如何编译和链接源代码文件以生成最终的可执行文件、库文件或者其他目标文件…

Node.js知识点以及案例总结

思考&#xff1a;为什么JavaScript可以在浏览器中被执行 每个浏览器都有JS解析引擎&#xff0c;不同的浏览器使用不同的JavaScript解析引擎&#xff0c;待执行的js代码会在js解析引擎下执行 为什么JavaScript可以操作DOM和BOM 每个浏览器都内置了DOM、BOM这样的API函数&#xf…

开源模型应用落地-食用指南-以最小成本博最大收获

一、背景 时间飞逝&#xff0c;我首次撰写的“开源大语言模型-实际应用落地”专栏已经完成了一半以上的内容。由衷感谢各位朋友的支持,希望这些内容能给正在学习的朋友们带来一些帮助。 在这里&#xff0c;我想分享一下创作这个专栏的初心以及如何有效的&#xff0c;循序渐进的…

STM32F103C8T6 HC-SR04超声波模块——超声波障碍物测距(HAl库)

超声波障碍物测距 一、HC-SR04超声波模块&#xff08;一&#xff09;什么是HC-SR04&#xff1f;&#xff08;二&#xff09;HC-SR04工作原理&#xff08;三&#xff09;如何使用HC-SR04&#xff08;四&#xff09;注意事项 二、程序编写&#xff08;一&#xff09;CubeMX配置1.…

2024全新Langchain大模型AI应用与多智能体实战开发

2024全新Langchain大模型AI应用与多智能体实战开发 LangChain 就是一个 LLM 编程框架&#xff0c;你想开发一个基于 LLM 应用&#xff0c;需要什么组件它都有&#xff0c;直接使用就行&#xff1b;甚至针对常规的应用流程&#xff0c;它利用链(LangChain中Chain的由来)这个概念…

Facebook之魅:数字社交的体验

在当今数字化时代&#xff0c;Facebook作为全球最大的社交平台之一&#xff0c;承载着数十亿用户的社交需求和期待。它不仅仅是一个简单的网站或应用程序&#xff0c;更是一个将世界各地的人们连接在一起的社交网络&#xff0c;为用户提供了丰富多彩、无与伦比的数字社交体验。…

C++实现基础二叉搜索树(并不是AVL和红黑树)

本次实现的二叉搜索树并不是AVL数和红黑树&#xff0c;只是了解流程和细节。 目录 二叉搜索树的概念K模型二叉搜索树的实现二叉搜索树的架构insert插入find 查找中序遍历Inorder删除earse替换法的思路情况一 &#xff1a;假如要删除节点左边是空的。在左边时在右边时 情况二&a…

文心智能体,零代码构建情感表达大师智能体

前言 随着智能体技术的突飞猛进&#xff0c;各行各业正迎来前所未有的变革与机遇。智能体&#xff0c;作为人工智能领域的重要分支&#xff0c;以其自主性、智能性和适应性&#xff0c;正逐步渗透到我们生活的每一个角落&#xff0c;成为推动社会进步和科技发展的新动力。 为了…

visual studio 2022 ssh 主机密钥算法失败问题解决

 Solution - aengusjiang 问题&#xff1a; I follow the document, then check sshd_config, uncomment“HostKey /etc/ssh/ssh_host_ecdsa_key” maybe need add the key algorithms: #HostKeyAlgorithms ssh-ed25519[Redacted][Redacted]rsa-sha2-256,rsa-sha2-512 Ho…

Redis常用命令——String篇

前面我们讲解了一些 Redis 的全局命令&#xff08;Redis常用基本全局命令&#xff09;。所谓全局命令&#xff0c;就是可以匹配任意一个数据结构进行使用。但是不同的数据结构&#xff0c;也有自己的操作命令。本篇文章主要讲解的是 String 的操作命令&#xff0c;希望会对你有…

ClickHouse课件

列式存储数据库&#xff1a;hbase clickhouse 简介 ClickHouse入门 ClickHouse是俄罗斯的Yandex于2016年开源的列式存储数据库&#xff08;DBMS&#xff09;&#xff0c;使用C语言编写&#xff0c;主要用于在线分析处理查询&#xff08;OLAP&#xff09;&#xff0c;能够使用…