# test621
**Repository Path**: zx21/test621
## Basic Information
- **Project Name**: test621
- **Description**: test621
- **Primary Language**: Unknown
- **License**: Not specified
- **Default Branch**: main
- **Homepage**: None
- **GVP Project**: No
## Statistics
- **Stars**: 0
- **Forks**: 0
- **Created**: 2023-06-21
- **Last Updated**: 2023-06-21
## Categories & Tags
**Categories**: Uncategorized
**Tags**: None
## README
# 项目基本组成
```js
src;
App.vue; // 项目根组件,一级路由出口
api; // 接口请求
assets; // 静态资源
icons; // svg icon 图标
images; // image 图片
logo.png; // logo
components; // 通用的业务组件,例如点赞组件在多个页面中使用到
constants; // 常量
directives; // 自定义指令
libs; // 通用组件,不包含业务,可用于构建中台物料库或通用组件库
main.js; // 入口文件
permission.js; // 页面权限控制中心,如未登录不能进入个人中心页面
router; // 路由
index.js; // 路由处理中心
modules; // 路由模块
mobile - routes.js; // 移动端路由
pc - routes.js; // PC 端路由
store; // vuex 全局状态
getters.js; // 全局状态访问处理
index.js; // 全局状态中心
modules; // 状态子模块
styles; // 全局样式
index.scss; // 全局通用的样式处理
utils; // 工具模块
vendor; // 外部供应资源,例如人类行为认证
views; // 页面组件,与 components 区别在于此处组件对应路由表,以页面的形式展示
layout; // 用于 PC 端分割一级路由和二级路由
components; // 该页面组件下的业务组件
index.vue; // layout 组件
tailwind.config.cjs; // tailwind css 配置文件,与 src 平级
vite.config.js; // vite 配置文件,与 src 平级
```
# 创建项目
npm create vite@latest
# 使用 tailwind
https://tailwindcss.com/docs/guides/vite
npm install -D tailwindcss postcss autoprefixer
npx tailwindcss init -p
```js
tailwind.config.js;
export default {
// ---------
content: ["./index.html", "./src/**/*.{js,ts,jsx,tsx,vue}"],
// ----------
theme: {
extend: {},
},
plugins: [],
};
```
```js
src/style.css
@tailwind base;
@tailwind components;
@tailwind utilities;
```
# 添加 vscode 插件
Prettier - Code formatter
```js
.prettierrc
{
"semi": false, // 代码结尾不加分号
"singleQuote": true, // 优先单引号
"trailingComma": "none" // 不添加尾随逗号
}
```
鼠标右键,选择 `使用...格式化文档`,然后选择`配置默认格式化程序`,选择 Prettier - Code formatter 即可。
Tailwind CSS IntelliSense
Volar
# 路由架构分析

一套代码实现电脑端和移动端。所以路由要做特别的设计。
`App.vue`:一级路由出口,用作整页路由切换。
`Main.vue`:二级路由出口,用作局部路由切换,不包含一级路由的头部 Header 区域。
# vue-router
根据设备的不同使用不同端的路由表。
src/router/index.js
# 判断是否移动端
```js
import { computed } from "vue";
import { PC_DEVICE_WIDTH } from "../constants";
/**
* 判断当前是否为移动端设备,根据当前屏幕宽度是否小于一个指定宽度(1280)来判断
* 使用计算属性使其具有响应式,更加灵活
*/
export const isMobileTerminal = computed(
() => document.documentElement.clientWidth < PC_DEVICE_WIDTH
);
```
通过 `vueuse 优化`处理方案
对于 `computed` 计算属性来说,会在其依赖的响应式数据发生变化时重新进行计算,而在上一小节中 isMobileTerminal 计算属性中屏幕宽度 document.documentElement.clientWidth 并`不是响应式数据`,所以 isMobileTerminal 计算属性是不会随着屏幕宽度变化而重新计算的。
```js
import { useWindowSize } from "@vueuse/core";
//useWindowSize返回一个响应式的页面宽度,我们可以利用这个响应式宽度重构我们的 isMobileTerminal,使其具备响应式的能力。
const { width } = useWindowSize();
export const isMobileTerminal = computed(() => width.value < PC_DEVICE_WIDTH);
```
# vueuse
npm i @vueuse/core
# 定义 @ 软链接
```js
vite.config.js;
import { join } from "path";
export default defineConfig({
resolve: {
alias: {
// 软链接
"@": join(__dirname, "/src"),
},
},
});
```
# 移动端首页分析

通过判断当前设备是移动端还是 PC 端来使用不同的 navigation 组件。
src/views/main/components/navigation/index.vue
创建导航栏组件,这个组件自动显示 pc 或手机的导航栏
# api 调用
npm i axios -S
src/utils/request.js
- 创建 axios 对象
- 配置请求头
src/api/category.js
- 导入 axios 对象去请求
# vite 处理代理服务器
```js
vite.config.js;
export default defineConfig({
server: {
proxy: {
// 代理配置
"/api": {
// 代理所有 /api 请求
target: "https://api.imooc-front.lgdsunday.club/", // 代理后的请求地址
changeOrigin: true, // 开启跨域
},
},
},
});
```
# vite 处理环境变量
src/utils/request.js
npm run dev 命令会进入开发模式,加载 .env.developmen
npm run build 命令会进入生产模式,加载 .env.production
scripts 命令加上 --mode 参数来指定模式,如 "dev": "vite --mode production" 可以让 npm run dev 进入 production 模式
# 适配移动端
动态 rem 基准
src/utils/flexible.js
rem 使用屏幕宽度/10
修正 tailwind
tailwind.config.js
修改字体的默认大小
# 封装 svg
src/libs/svg-icon/index.vue
npm i vite-plugin-svg-icons@2.0.1 -D
```js
vite.config.js;
在vite中使用createSvgIconsPlugin方法, 才能使用svg;
src / main.js;
// 注册 svg-icons,导入的路径是固定的,这是 vite-plugin-svg-icons 生成的虚拟地址
import "virtual:svg-icons-register";
```
# 创建组件库
导入 svg 组件,在 install 方法里注册
```js
src / libs / index.js;
import svgIcon from "./svg-icon/index.vue";
export default {
install(app) {
app.component("m-svg-icon", svgIcon);
},
};
```
在 main 中注册
```js
src / main.js;
import mLibs from "./libs";
createApp(App).use(router).use(mLibs).mount("#app");
```
使用 m-svg-icon 组件
```js
```
# 滑块

主要分为两部分
滑块样式
滑动动画
滑块的切换处理逻辑需要具备哪些内容:
- 选中的 item 下标:`currentCategoryIndex`
- 所有 item 元素:`itemRefs`
- ul 的横向滚动偏离位置:`ulScrollLeft`
- 最后在 `currentCategoryIndex` 发生改变时,获取 item 下标元素的 left 和 width,计算 sliderStyle 即可。
# 弹出窗口 popup

- 当 popup 展开时,展开的东西不属于任何一个组件内部,而是直接插入到 `body` 下面;
- popup 包含两部分内容,一部分为`背景蒙版`,一部分为展示内容的`包裹容器`;
- popup 应该通过一个`双向绑定`进行控制`展示`和`隐藏`;
- popup 展示时,屏幕的滚动应该被`锁定`;
- 展示内容区域应该`接收`所有的 attrs,并且应该通过`插槽让调用`方指定其内容。
新建 @/libs/popup/index.vue 弹出组件
@/libs/index.js 注册组件
@/views/main/components/navigation/mobile/index.vue 使用组件
弹出窗口 popup 实现
src/libs/popup/index.vue
使用
```js
我是测试内容
;
// 控制 popup 展示
const isVisable = ref(false);
const onShowPopup = () => (isVisable.value = true);
```
# 使用 vueuse 实现 v-model
```js
//和正常的一样
const props = defineProps({
modelValue: {
type: Boolean,
required: true,
},
});
//const emits = defineEmits(['update:modelValue']) 不需要返回emits了
defineEmits(["update:modelValue"]);
//多了这一步,获取一个变量
const isVisable = useVModel(props);
// 从监听 props.modelValue 变为直接监听 isVisable
// watch(() => props.modelValue, val =>{})
watch(isVisable,val =>{});
```
# vite 通用组件自动化注册
- vite 的 Glob 导入功能:该功能可以帮助我们在文件系统中导入多个模块;
- vue 的 defineAsyncComponent 方法:该方法可以创建一个按需加载的异步组件。
# 处理 PC 端基础架构
`@/views/layout/index.vue` 文件,表示 PC 端的一级路由出口组件。然后分别创建三个业务组件:
```js
@/views/layout/index.vue
src/router/modules/pc-routes.js
export default [{
path: '/',
name: 'main',
component: () => import('@/views/layout/index.vue'),
children: []
}]
```
`@/views/layout/components/header/index.vue`:表示 PC 端头部区域;
`@/views/layout/components/main.vue`:二级路由出口组件;
`@/views/layout/components/floating.vue`:右下角悬浮区域。

# PC 端 Header

Logo 部分;
搜索框,新建 @/views/layout/components/header/`header-search`/index.vue;
主题切换,新建 @/views/layout/components/header/`header-theme`.vue;
个人信息,新建 @/views/layout/components/header/`header-my`.vue。
# search 搜索框
src/libs/search/index.vue
鼠标移上去有 outline 等`展示效果`;
点击搜索框,下方会`弹出`一个`卡片`;
卡片包含最近搜索和热门`精选`;
输入框输入文字后卡片会有`模糊搜索词`提示;
输入内容实现`双向数据绑定`;
鼠标移入与获取焦点时的`动画`;
`一键清空`文本功能;
搜索触发功能;
可控制,可填充的下拉展示区;
监听到以下事件列表:
clear:删除所有文本事件;
input:输入事件;
focus:获取焦点事件;
blur:失去焦点事件;
search:触发搜索(点击或回车)事件。
# button 按钮
src/libs/button/index.vue
可以是`文字按钮`,并提供 loading 效果;
可以是 `icon 按钮`,并可以指定 icon 颜色;
具备开关的`点击动画`;
可以指定各种`风格和大小`;
当预设的风格或大小不符合需求时,需要给开发者错误提示;
处理`点击事件`。
# popover 气泡卡片
鼠标移动到`主题切换`和`个人信息区域`会有一个卡片`弹出来`,这个弹出来的`卡片`就是接下来要封装的气泡卡片 popover 通用组件。
对于这个组件来说核心的功能有两块:
应该具备`两个插槽`,一个是`触发`卡片弹出`的媒介`,如主题切换的图标,另一个就是`弹出的卡片`;
弹出的卡片可以指定其位置,如`左下`还是`右上`。
处理慢速移动时,气泡消失问题
之所以会出现这个问题是因为图标与弹层气泡间有一层间隙,当鼠标进入这个间隙时会触发 mouseleave 事件,导致 `isVisable` 的值变为 false,所以气泡消失。
要想解决这个问题可以利用类似于防抖 debounce 的概念,就是当鼠标刚离开时,不立刻修改 `isVisable` 的值,而是延迟一段时间,如果这段时间内再次触发了 mouseenter 事件,则不再修改 `isVisable`
# vuex
npm i vuex@next vue-router@next -S
@/store/index.js 初始化 vuex
新建 @/store/modules/category.js 模块
@/store/getters.js
@/main.js 使用 vuex
# 改造 nav 获取数据的方式
@/views/main/components/navigation/index.vue
@/views/main/components/navigation/mobile/index.vue
@/views/main/components/menu/index.vue
# 使用缓存优化体验
- 让 categorys 具备一个初始化数据;
- 从服务端获取数据,替换初始化数据;
- 为了防止初始化数据太老,把每次获取到的新数据,都作为下一次的初始化数据。
借助 vuex-persistedstate 这个库来完成。自动缓存 vuex 中的数据到 localStorage 中
npm i --save vuex-persistedstate
# 主题替换原理
```js
/* 浅色主题 */
.light .content {
color: #333;
}
/* 深色主题 */
.dark .content {
color: white;
}
我是一个 div
```
默认情况下,tailwind 没有开启 `DarkMode` 功能,需要手动开启。
在配置文件中设置 `darkMode`: 'media' 可以让深色模式跟随用户操作系统,
设置 `darkMode`: 'class' 则可以手动切换深色模式。
监听主题的切换行为。
根据行为保存当前需要展示的主题到 vuex 中。
根据 vuex 中保存的当前主题,展示 header-theme 下显示的图标。
根据 vuex 中保存的当前主题,修改 html 的 class。
在 vuex 中定义有关主题的模块。
新建 @/store/modules/theme.js
@/store/index.js 引入主题模块
@/store/getters.js
然后实现主题替换功能。
@/views/layout/components/header/header-theme.vue
新建 @/utils/theme.js
@/main.js
要实现`跟随系统`的主题变更,最重要的就是监听到系统的主题变化,可以利用 `Window.matchMedia`() 方法。
该方法接收一个 mediaQueryString 媒体查询解析字符串,这个字符串我们可以传递 prefers-color-scheme,调用 `window.matchMedia('(prefers-color-scheme: dark)')` 后方法会返回一个 MediaQueryList 对象,该对象中存在一系列媒体查询内容,其中我们需要以下两个:
change 事件,可以监听主题发生变更的行为。
matches 属性,true 表示深色主题,false 表示浅色主题。
# 瀑布流
每个 item 应该横向排列,下一行的2 item 应该连接到当前最短的列中。
使用 `absolute` 绝对定位,通过修改元素的 `top` 和 `left` 来手动控制其显示的位置。
希望组件应该怎么使用
```js
```
`主题变更`
跟随系统、极简白、极夜黑
`懒加载`
图片懒加载、数据懒加载、组件懒加载
`第三方`
qq 扫码登录,移动端 qq 吊起登陆,微信扫码登录、分享、支付宝扫码支付,移动端支付宝吊起支付、用户反馈平台
`登录架构`
人类行为验证,脱离组件库的表单校验、登录鉴权
`通用解决方案`
前端架构、防抖、文件下载、指定 DOM 的全屏处理、功能引导、主动介入浏览器堆栈管理、虚拟任务栈、项目部署
`云存储通用解决方案`
图片裁剪、文件上传、阿里云 COS、腾讯云 OSS
`基于 vite 的大厂配置方案`
多套路由表、alias、代理服务器、axios + 环境变量的统一处理、svg 统一注册、通用组件自动化
注册、通用指令自动化注册
`响应式通用处理`
样式的响应式处理、尺寸单位的响应式处理、多功能的响应式处
理、样式处理方案 tailwindcss、vue 常用工具(vueuse)、动画处理 GSAP、vuex 的自动持久化
`组件`标签直接调用
· 按钮(button)
· 瀑布流(waterfall)
· 移动端弹出层(popup)
· 移动端任务栈(transition-router-view)
· 倒计时(count-down)
· 输入框(input)
· 弹窗(dialog)
· 移动端 navbar(navbar)
· 搜索框(search)
· 移动端 tabbar(trigger-menu + trigger-menu-item)
· 上拉加载(infinite-list)
· 弹出框(popover)
· svg 图标(svg-icon)
· Skeleton
· form
· toast
`方法`触发组件渲染
· 确认弹窗(confirm)
· 消息提示(message)