1.初始化样式
.ellipsis { white-space: nowrap; text-overflow: ellipsis; overflow: hidden; } // 文本超出一行显示...
.ellipsis-2 { word-break: break-all; text-overflow: ellipsis; display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 2; overflow: hidden; } // 文本超出两行显示...
.clearfix:after { content: "."; display: block; visibility: hidden; height: 0; line-height: 0; clear: both; } // 单伪元素清除浮动
我们在配置玩less 文件 全局都要用 需要在main.js 导入
2.组件路由的使用
1.在 app.vue里设置挂载点 2.在首页里设置二级路由挂载点 3.把组件写好 4.在router/index.js里配置路由规则
import { createRouter, createWebHashHistory } from 'vue-router'
const Layout = () => import('@/views/Layout')
const Home = () => import('@/views/Home/index')
const routes = [ { path: '/', component: Layout, children: [ { path: '/', component: Home } // 如果二级路由的path设置为 '' 他会作为二级路由的默认显示 ] } ]
const router = createRouter({ history: createWebHashHistory(), routes })
export default router
3.less自动化的导入
1.例如手动准备变量文件
// 主题 @xtxColor:#27BA9B;
// 辅助 @helpColor:#E26237;
// 成功 @sucColor:#1DC779;
// 警告 @warnColor:#FFB302;
// 价格 @priceColor:#CF4444;
2.在app.vue 里进行测试
<template>
<!-- 一级路由出口 -->
<router-view />
<div class="test">我是测试文字</div>
</template>
<style lang="less" scoped>
// 引入我们定义了less变化的文件
// ~线不能丢
@import "~@/styles/var.less";
.test {
color: @xtxColor;
}
</style>
4.自动引入less方案
- vue add style-resources-loader 执行这个命令添加vue-cli的插件
- 在安装的选项里选择我们要用的语法
- 安装完毕后会在vue.config.js中添加一下配置
module.exports = {
pluginOptions: {
'style-resources-loader': {
preProcessor: 'less',
patterns: []
}
}
}
4. 把需要注入的文件写在里面
const path = require('path')
module.exports = {
pluginOptions: {
'style-resources-loader': {
preProcessor: 'less',
patterns: [
// 配置哪些文件需要自动导入
path.join(__dirname, './src/styles/var.less')
]
}
}
}
5.顶部通栏的布局
1)在 public/index.html 引入字体图标文件
<link rel="icon" href="<%= BASE_URL %>favicon.ico">
<link rel="stylesheet" href="//at.alicdn.com/t/font_2143783_iq6z4ey5vu.css">
<title><%= htmlWebpackPlugin.options.title %></title>
2).然后,新建头部导航组件
3)在 src/views/Layout.vue 中导入使用
头部布局
1)新建header头部组件
Layout/components/top-header.vue
2)在 src/views/Layout.vue 中导入使用
3)抽离header-nav组件
因为在后面的吸顶交互里,我们需要复用导航部分,所以这里我们先直接把他抽离成一个单独的组件
Layout/compoennts/header-nav.vue
底部 一样
6. 接口数据渲染导航
1.在 api 里定义接口数据
import request from '@/utils/request'
// 拉取导航数据
export function fetchCategory () {
return request('/home/category/head', 'GET')
}
在组件里渲染出来
<li class="home"><RouterLink to="/">首页</RouterLink></li>
<li v-for="item in categoryList" :key="item.id">
<RouterLink :to="`/category/${item.id}`">{{item.name}}</RouterLink>
</li>
</ul>
<script>
import { ref } from 'vue'
import { fetchCategory } from '@/api/home'
export default {
setup () {
const categoryList = ref([])
async function loadCategory () {
const res = await fetchCategory()
categoryList.value = res.data.result
}
loadCategory()
return { categoryList }
}
}
</script>
7. 吸顶头部交互实现
电商网站的首页一般都比较长,需要滚动查看内容,在查看的过程中用户可能会随着想去其他页面,此时需要一个吸顶的交互来让用户可以方便跳转
- 滚动距离大于等于78个px的时候,组件固定在视口顶部跟随页面移动
- 滚动距离小于78个px的时候,组件消失
实现思路
基于滚动事件,在滚动事件的回调函数中 不断的拿到当前距离顶部的像素值 如果像素值大于78 组件显示
如果小于78 组件隐藏
- 准备一个吸顶组件,准备一个类名,控制样式让其固定在顶部
- 监听页面滚动,判断滚动距离,距离大于78px添加类名,小于78px移除类名
1)安装@vueuse/core 包,它封装了常见的一些交互逻辑
yarn add @vueuse/core
2)在吸顶导航中使用
src/components/header-sticky.vue
<div class="app-header-sticky" :class="{show:y >= 78}">
<div class="container">
<RouterLink class="logo" to="/" />
<HeaderNav />
<div class="left">
<RouterLink to="/" >品牌</RouterLink>
<RouterLink to="/" >专题</RouterLink>
</div>
</div>
</div>
</template>
<script>
import HeaderNav from './header-nav'
import { useWindowScroll } from '@vueuse/core'
export default {
name: 'AppHeaderSticky',
components: { HeaderNav },
setup () {
// y表示具体顶部的滚动距离 会动态更新
const { y } = useWindowScroll()
return { y }
}
}
</script>
<style scoped lang='less'>
.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;
}
}
}
}
</style>
8.配置 jsconfig.json文件
"compilerOptions": {
"target": "ES6",
"module": "commonjs",
"allowSyntheticDefaultImports": true,
"baseUrl": "./",
"paths": {
"@/*": ["src/*"]
}
},
"exclude": [
"node_modules"
]
}
volar 插件 可以让vue3代码高亮 可以把模板 脚本分屏
9. Home- 分类和轮播图
1)拆分左侧分类组件
home/components/home-category.vue
<div class="home-category">
分类组件
</div>
</template>
<script>
export default {
name: 'HomeCategory'
}
</script>
2)拆分banner组件
home/components/home-banner.vue
<div class="home-banner">
banner
</div>
</template>
<script>
export default {
name: 'HomeBanner'
}
</script>
<style scoped lang='less'>
.home-banner {
width: 1240px;
height: 500px;
position: absolute;
left: 0;
top: 0;
z-index: 98;
}
</style>
3)home组件中引入使用
<div class="page-home">
<div class="home-entry">
<div class="container">
<!-- 左侧分类 -->
<HomeCategory />
<!-- banner轮播图 -->
<HomeBanner/>
</div>
</div>
</div>
</template>
<script>
import HomeCategory from './components/home-category'
import HomeBanner from './components/home-banner'
export default {
name: 'PageHome',
components: { HomeCategory, HomeBanner }
}
</script>
10.左侧导航栏的数据渲染
发送请求 渲染标签
11.把列表数据存入vuex 把三个页面的请求封装
1.在store/modules里定义文件
const category = {
namespaced: true, // 命名空间
state: {
categoryList: [] // 导航列表
},
// state的计算属性 基于categoryList做一定的数据处理 children前俩项
getters: {
},
mutations: {
setCategoryList (state, list) {
state.categoryList = list
}
},
actions: {
async asynGetCategory (ctx) {
// 1. 发送接口请求
const res = await categoryApi()
// 2. 把数据拿到之后通过触发mutation存入state
ctx.commit('setCategoryList', res.data.result)
// 我们找一个合适的时机 把这个action函数调用一下?
// 去哪里触发?
}
}
}
export default category
// 1.如何在setup中使用store
// import {useStore} from 'vuex'
// setup(){ const store = useStore() }
// 2.如何在setup中使用state中的数据
// computed(()=> store.state.模块名.属性名)
// 3.如何在setup中使用计算属性
// computed(()=> store.getters['模块名/getter名'])
2.在layout/index 里触发
import { useStore } from 'vuex'
export default {
name: 'XtxLayout',
components: {
topNavVue,
topHeaderVue,
layoutFooterVue,
headerStickyVue
},
setup () {
// 触发获取分类数据的action this.$store.dispatch
const store = useStore() // this.$store等价
store.dispatch('category/asynGetCategory')
}
}
</script>
3.在使用界面使用计算属性
import { useStore } from 'vuex'
export default {
setup () {
// 在组件里使用store里面存好的数据
// 如何在setup中使用store中的数据?
// 方法:借助计算属性做一个数据映射
const store = useStore()
// 当前组件的cagetoryData就和store中的categoryList完成了映射
// 他俩始终保持响应式的同步
const cagetoryData = computed(() => store.state.category.categoryList)
return {
cagetoryData
}
}
}
12.通过数组的裁切属性把多余的数据隐藏
第一种方法
第二种方法
- 在 store/category 里定义计算属性
getters: {
formatList (state) {
return state.categoryList.map(item => {
return {
id: item.id,
name: item.name,
children: item.children.slice(0, 2),
goods: item.goods // 用来显示右侧弹框中的列表
}
})
}
},
2)在组件里获取store 里的计算属性
import { useStore } from 'vuex'
export default {
setup () {
// 在组件里使用store里面存好的数据
// 如何在setup中使用store中的数据?
// 方法:借助计算属性做一个数据映射
const store = useStore()
// 当前组件的cagetoryData就和store中的categoryList完成了映射
// 他俩始终保持响应式的同步
const cagetoryData = computed(() => store.state.category.categoryList)
return {
cagetoryData
}
}
}
</script>
13.注册全局组件
1.把需要注册的全局组件放到src/components文件夹下
2.在main.js 里引入 并且注册使用
import { createApp } from 'vue'
import App from './App.vue'
import router from './router'
import store from './store'
// 初始化样式文件
import '@/styles/common.less'
// 注册全局组件
import componentsPlugin from '@/components'
import directivePlugin from '@/directives'
// use: 方法,用来注册插件 (Vue.use) 支持串联调用
// 以app根组件为参数生成一个实例对象然后注册store插件注册router插件 最后完成挂载
createApp(App).use(store).use(router).use(componentsPlugin).use(directivePlugin).mount('#app')
14.轮播图的展示
1.查看轮播图组件的名字 2.在需要的banner的位置使用组件 传入组件需要的参数 3.掉接口拿到图片数据地址渲染数据
15.鼠标移入layer让它显示隐藏的盒子
- 设置样式 让本来处于display:none 的盒子 display:black
2.在store里吧需要的数据goods加上
getters: {
formatList (state) {
return state.categoryList.map(item => {
return {
id: item.id,
name: item.name,
children: item.children.slice(0, 2),
goods: item.goods // 用来显示右侧弹框中的列表
}
})
}
},
3.在需要的组件中调用
import { useStore } from 'vuex'
export default {
setup () {
const store = useStore()
// 当前组件的cagetoryData就和store中的categoryList完成了映射
// 他俩始终保持响应式的同步
// 如何在这里获取store中的getters
// store.getters['模块名/getter名字']
const cagetoryData = computed(() => store.getters['category/formatList'])
return {
cagetoryData
}
}
}