这两天复习备战字节跳动一面,虽然知道自己很没有实力,但是必将是自己吹出去的牛,还是一定要做到
一.css布局
弹性布局
javascript
.container {
display: flex; /* 或者 inline-flex */
}
一旦设置了 display: flex,这个容器(.container)的直接子元素就会自动成为 Flex 项目。
现在,这些 Flex 项目就不再是普通的块级或行内元素了,它们会遵循 Flexbox 的规则进行排列。
Flex 容器的核心属性(控制项目如何排列)
容器的属性主要用来设置主轴的方向、是否换行以及项目在主轴和交叉轴上的对齐方式。
1. flex-direction- 定义主轴方向
决定项目如何排列(是横着排还是竖着排)。
-
row(默认值):主轴为水平方向,起点在左端。 -
row-reverse:主轴为水平方向,起点在右端。 -
column:主轴为垂直方向,起点在上沿。 -
column-reverse:主轴为垂直方向,起点在下沿。
2. justify-content- 项目在主轴上的对齐方式
如何分配项目之间及其周围的空间。
-
flex-start(默认值):向主轴起点对齐。 -
flex-end:向主轴终点对齐。 -
center: 居中对齐。 -
space-between:两端对齐,项目之间的间隔都相等。 -
space-around:每个项目两侧的间隔相等。所以项目之间的间隔比项目与边框的间隔大一倍。 -
space-evenly:所有间隔都完全相等。
3. align-items- 项目在交叉轴上的对齐方式
(单行情况下)如何对齐一行内的项目。
-
stretch(默认值):如果项目未设置高度或设为auto,将占满整个容器的高度。 -
flex-start:向交叉轴的起点对齐。 -
flex-end:向交叉轴的终点对齐。 -
center: 在交叉轴中居中对齐。 -
baseline: 项目的第一行文字的基线对齐。
4. flex-wrap- 定义是否换行
默认情况下,项目都排在一条轴线上。flex-wrap定义当一条轴线排不下时如何换行。
-
nowrap(默认):不换行。 -
wrap:换行,第一行在上方。 -
wrap-reverse:换行,第一行在下方。
Flex 项目的核心属性(控制项目自身)
项目的属性可以用来控制单个项目在容器中的表现。
1. flex-grow- 放大比例
定义项目的放大能力。默认值为 0,即如果存在剩余空间,也不放大。
如果所有项目的 flex-grow属性都为 1,则它们将等分剩余空间。如果一个项目的 flex-grow属性为 2,其他项目都为 1,则前者占据的剩余空间将比其他项多一倍。
2. flex-shrink- 缩小比例
定义项目的缩小能力。默认值为 1,即如果空间不足,该项目将缩小。
如果所有项目的 flex-shrink属性都为 1,当空间不足时,都将等比例缩小。如果一个项目的 flex-shrink属性为 0,其他项目都为 1,则空间不足时,前者不缩小。
3. flex-basis- 项目占据的主轴空间
定义在分配多余空间之前,项目占据的主轴空间。浏览器根据这个属性计算是否有多余空间。它的值可以设为 auto(默认,即项目本来的大小)或一个固定的长度(如 200px)。
4. 简写属性:flex
flex属性是 flex-grow, flex-shrink和 flex-basis的简写。强烈推荐使用这个简写。
一些最常用的值:
-
flex: 1: 等价于flex: 1 1 0%。项目会伸缩以填满空间。 -
flex: auto: 等价于flex: 1 1 auto。 -
flex: none: 等价于flex: 0 0 auto。项目完全不可伸缩。 -
flex: 0 0 200px: 项目基础宽度为 200px,不放大也不缩小(类似固定宽度侧边栏)。
5. align-self- 允许单个项目有不一样的对齐方式
可覆盖容器的 align-items属性。属性值与之相同。
一个经典示例:实现水平垂直居中
在 Flexbox 出现之前,垂直居中是个老大难问题。现在变得非常简单
javascript
.container {
display: flex;
justify-content: center; /* 主轴(水平)居中 */
align-items: center; /* 交叉轴(垂直)居中 */
height: 300px; /* 给容器一个高度 */
border: 1px solid #ccc;
}
.centered-item {
/* 无需任何设置 */
}
网格布局
一、核心概念:它是什么?
CSS 网格布局 是 CSS 中目前最强大的二维布局系统。与 弹性布局(Flexbox) 主要处理一维布局 (行 或 列)不同,网格布局被设计用来同时在行和列两个维度上处理布局。
你可以把网格布局想象成一张网 或一个表格,你可以在上面精确地放置和排列元素。它让你能够将网页划分成一个个由行和列组成的区域,然后将页面元素放入这些预定义或动态生成的区域中。
二、为什么需要它?------ 对比弹性布局
弹性布局(Flexbox)非常出色,但它本质上是"一维"的:
-
它主要处理一条轴线上的布局(要么是水平的主轴,要么是垂直的交叉轴)。
-
虽然可以换行(
flex-wrap: wrap),但下一行的项目与上一行的项目在列方向上没有对齐关系。
网格布局的优势在于"二维"控制:
-
同时定义行和列:你可以先定义好整个页面的网格结构,然后再将元素放置到这个网格的任意位置。
-
精确的布局控制:可以轻松实现报纸杂志式的复杂布局,比如一个元素跨越多行多列。
-
解决等高、精准对齐等难题:网格轨道(行/列)的结构性保证了内容的对齐。
示例:创建一个 3x3 的网格
javascript
.container {
display: grid;
grid-template-columns: 100px 200px 100px; /* 三列:宽100px, 200px, 100px */
grid-template-rows: 80px 120px 80px; /* 三行:高80px, 120px, 80px */
}
常用的单位:
-
固定单位 :
px -
相对单位 :
%,fr(关键单位! ),em,rem -
灵活单位 :
auto,min-content,max-content
fr单位(片段单位):
这是一个为网格布局而生的单位,代表剩余空间的分配比例 。它类似于 Flexbox 中的 flex-grow。
javascript
.container {
display: grid;
grid-template-columns: 1fr 2fr 1fr; /* 总宽度分为4份,第二列占2份,第一、三列各占1份 */
}
用于简化重复模式的书写。
2. 网格间距:gap
用于设置网格线之间的间距,是 row-gap和 column-gap的简写。
javascript
.container {
gap: 20px 10px; /* 行间距20px,列间距10px */
/* 等同于 */
/* row-gap: 20px; */
/* column-gap: 10px; */
}
3. 隐式和显式网格:grid-auto-rows和 grid-auto-columns
当你明确使用 grid-template-rows/columns定义的网格是显式网格 。如果你放置的项目超出了这个显式网格,浏览器会自动创建新的网格轨道,这被称为隐式网格。
grid-auto-rows/columns就是用来控制这些隐式网格轨道大小的。
五、网格项目的核心属性(控制项目放置)
这些属性用在网格项目上,用来指定该项目在网格中的具体位置。
1. 基于网格线的放置
网格线是划分网格的线条,有数字索引(从1开始)也可以自定义名称。一个3列的网格有4条垂直的网格线。
网格线是划分网格的线条,有数字索引(从1开始)也可以自定义名称。一个3列的网格有4条垂直的网格线。
-
grid-column-start:项目从第几条垂直网格线开始。 -
grid-column-end:项目到第几条垂直网格线结束。 -
grid-row-start:项目从第几条水平网格线开始。 -
grid-row-end:项目到第几条水平网格线结束。示例:让一个项目占据多行多列
javascript.item-1 { grid-column: 1 / 3; /* 从第1条垂直线开始,到第3条垂直线结束(占据第1、2列) */ grid-row: 1 / 2; /* 从第1条水平线开始,到第2条水平线结束(占据第1行) */ } .item-2 { grid-column: 2 / 4; /* 占据第2、3列 */ grid-row: 2 / 4; /* 占据第2、3行 */ }
七、总结:什么时候使用网格布局?
-
整体页面布局:这是 Grid 最擅长的领域。定义页头、侧边栏、主内容区、页脚等大块区域。
-
复杂的二维布局:需要元素在行和列两个方向上都保持严格对齐的布局,如图库、仪表盘、卡片网格。
-
重叠内容:可以轻松地将多个网格项目放置到同一个网格单元格中,实现内容重叠。
二.vue Router 4
1. 什么是 Vue Router 4
Vue Router 4 是 Vue 3 的官方路由管理器,专门为 Vue 3 设计,支持 Composition API。
2. 核心特性在代码中的体现
路由创建方式
javascript
// Vue Router 4 的创建方式
const router = createRouter({
history: createWebHistory(import.meta.env.BASE_URL),
routes: [...]
})
历史记录模式
javascript
// 使用 HTML5 History 模式
history: createWebHistory(import.meta.env.BASE_URL)
// 其他可选模式:
// createWebHashHistory() - 哈希模式
// createMemoryHistory() - 内存历史模式
3. 路由配置结构分析
嵌套路由 (Nested Routes)
javascript
{
path: '/',
component: Layout, // 布局组件
children: [ // 嵌套子路由
{
path: '', // 空路径,默认子路由
component: Home
},
{
path: 'category/:id', // 动态路由
component: Category
}
]
}
多级嵌套
javascript
{
path: 'member',
component: Member,
children: [ // 第二级嵌套
{
path: '',
component: UserInfo
},{
path: 'order',
component: UserOrder
}
]
}
4. 路由特性详解
动态路由
javascript
{
path: 'detail/:id', // 动态参数
component: Detail
}
// 访问:/detail/123 → $route.params.id = '123'
命名路由
javascript
// 建议的改进:添加 name 属性
{
path: 'category/:id',
name: 'category',
component: Category
}
// 使用时:router.push({ name: 'category', params: { id: 1 } })
滚动行为
javascript
scrollBehavior() {
return { top: 0 } // 路由切换时滚动到顶部
}
5. 路由模式对比
| 模式 | 优点 | 缺点 |
|---|---|---|
createWebHistory |
干净的URL,SEO友好 | 需要服务器配置 |
createWebHashHistory |
无需服务器配置 | URL中有#,不够美观 |
6. 在实际项目中的使用
路由导航
javascript
// 编程式导航
router.push('/category/1')
router.push({ path: '/detail/123' })
// 声明式导航
<router-link to="/category/1">分类</router-link>
7. 路由结构优势
-
清晰的层次结构:使用 Layout 作为根布局
-
模块化组织:不同功能模块分开管理
-
用户流程完整:从浏览→购物车→结算→支付→会员中心
-
嵌套路由合理:会员中心内嵌用户信息和订单页面
Vue Router 4 通过这些特性提供了强大而灵活的路由管理能力,让你的单页面应用具有多页面应用的用户体验。
三.pinia
1. 什么是 Pinia
Pinia 是 Vue 的官方状态管理库,替代了之前的 Vuex,专门为 Vue 3 和 Composition API 设计。
2. Store 定义分析
Store 创建方式
javascript
export const useCartStore = defineStore('cart', () => {
// State, Getters, Actions 都在这里定义
return {
// 暴露给组件使用的属性和方法
}
}, {
persist: true // 插件配置
})
参数说明:
-
'cart': Store 的唯一 ID -
函数: 使用 Composition API 风格定义 Store
-
配置对象: 插件配置(这里使用了持久化插件)
3. State (状态) 管理
响应式状态定义
const cartList = ref([]) // 购物车列表状态
特点:
-
使用
ref()创建响应式数据 -
状态变化会自动更新依赖的组件
-
通过
.value访问和修改
4. Getters (计算属性)
计算属性定义
javascript
// 购物车总数量
const allCount = computed(() => cartList.value.reduce((a, b) => a + b.count, 0))
// 购物车总价格
const allPrice = computed(() => cartList.value.reduce((a, b) => a + b.count * b.price, 0))
// 是否全选
const isAll = computed(() => cartList.value.every((item) => item.selected))
// 选中商品数量和价格
const selectedCount = computed(() => cartList.value.filter(item => item.selected).reduce((a, b) => a + b.count, 0))
const selectedPrice = computed(() => cartList.value.filter(item => item.selected).reduce((a, b) => a + b.count * b.price, 0))
作用:
-
基于状态派生出新数据
-
自动缓存,依赖的状态变化时才重新计算
-
在模板中像普通属性一样使用
5. Actions (动作)
同步 Actions
javascript
javascript
// 清空购物车
const clearCart = () => {
cartList.value = []
}
// 单个商品选中状态切换
const singleCheck = (skuId, selected) => {
const item = cartList.value.find((item) => skuId === item.skuId)
if (item) {
item.selected = selected
}
}
// 全选/取消全选
const allCheck = (selected) => {
cartList.value.forEach(item => item.selected = selected)
}
异步 Actions
javascript
javascript
// 添加商品到购物车
const addCart = async (goods) => {
if (isLogin.value) { // 已登录 → 调用API
await insertCartAPI(goods.skuId, goods.count)
updateNewCartList()
} else { // 未登录 → 本地操作
const item = cartList.value.find((item) => goods.skuId === item.skuId)
if (item) {
item.count++
} else {
cartList.value.push(goods)
}
}
}
// 删除购物车商品
const delCart = async (skuId) => {
if (isLogin.value) {
await delCartAPI([skuId])
updateNewCartList()
} else {
const idx = cartList.value.findIndex((item) => skuId === item.skuId)
cartList.value.splice(idx, 1)
}
}
// 更新购物车列表
const updateNewCartList = async () => {
const res = await fineNewCartListAPI()
cartList.value = res.result
}
Actions 特点:
-
可以包含异步操作
-
直接修改 state
-
支持
async/await -
可以调用其他 actions
6. Store 间通信
使用其他 Store
const userStore = useUserStore()
const isLogin = computed(() => userStore.userInfo.token)
说明:
-
可以在一个 Store 中导入和使用其他 Store
-
实现 Store 之间的状态依赖和通信
7. 持久化配置
{
persist: true // 启用持久化
}
作用:
-
页面刷新后状态不丢失
-
购物车数据会保存在 localStorage
-
用户体验更好
8. 在组件中的使用方式
基本使用
javascript
vue
<template>
<div>
<!-- 使用 state -->
<div v-for="item in cartStore.cartList" :key="item.skuId">
{{ item.name }} - {{ item.price }}
</div>
<!-- 使用 getters -->
<div>总数量: {{ cartStore.allCount }}</div>
<div>总价格: {{ cartStore.allPrice }}</div>
<div>选中价格: {{ cartStore.selectedPrice }}</div>
<!-- 使用 actions -->
<button @click="cartStore.addCart(product)">加入购物车</button>
<button @click="cartStore.delCart(skuId)">删除</button>
<button @click="cartStore.allCheck(true)">全选</button>
</div>
</template>
<script setup>
import { useCartStore } from '@/stores/cart'
const cartStore = useCartStore()
// 在方法中调用 actions
const handleAddCart = (product) => {
cartStore.addCart({
skuId: product.id,
count: 1,
price: product.price,
selected: true
})
}
</script>
结构化使用(保持响应式)
javascript
vue
<script setup>
import { useCartStore } from '@/stores/cart'
import { storeToRefs } from 'pinia'
const cartStore = useCartStore()
// 使用 storeToRefs 保持响应式
const { cartList, allCount, allPrice, isAll } = storeToRefs(cartStore)
// Actions 直接解构
const { addCart, delCart, allCheck } = cartStore
</script>
9. 购物车 Store 设计优势
业务逻辑封装完善
javascript
javascript
// 1. 登录状态自动处理
if (isLogin.value) {
// 调用后端 API
} else {
// 本地操作
}
// 2. 数据同步机制
await insertCartAPI(goods.skuId, goods.count)
updateNewCartList() // 添加后立即同步最新数据
// 3. 完整的购物车功能
- 增删改查商品
- 选中状态管理
- 价格数量计算
- 本地/云端数据同步
用户体验优化
javascript
// 持久化:刷新页面购物车数据不丢失
persist: true
// 实时计算:价格数量自动更新
computed(() => cartList.value.reduce((a, b) => a + b.count * b.price, 0))
10. Pinia 相比 Vuex 的优势
-
更简单的 API: 没有 mutations,只有 state、getters、actions
-
TypeScript 支持更好: 完整的类型推断
-
Composition API 风格: 与 Vue 3 完美契合
-
模块化设计: 每个 Store 都是独立的
-
DevTools 支持: 良好的开发调试体验
总结
购物车 Store 很好地展示了 Pinia 的核心概念:
-
State :
cartList管理购物车数据 -
Getters: 各种计算属性派生数据
-
Actions: 处理业务逻辑和异步操作
-
Store 通信: 与用户 Store 交互
-
持久化: 数据本地保存
这样的设计让状态管理变得清晰、可维护,并且提供了良好的用户体验!