# 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 # 路由架构分析 ![20230521140241](https://zx21-img.oss-cn-shenzhen.aliyuncs.com/img/20230521140241.png) 一套代码实现电脑端和移动端。所以路由要做特别的设计。 `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"), }, }, }); ``` # 移动端首页分析 ![20230521150531](https://zx21-img.oss-cn-shenzhen.aliyuncs.com/img/20230521150531.png) 通过判断当前设备是移动端还是 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
  • ``` # 滑块 ![20230522140739](https://zx21-img.oss-cn-shenzhen.aliyuncs.com/img/20230522140739.png) 主要分为两部分 滑块样式 滑动动画 滑块的切换处理逻辑需要具备哪些内容: - 选中的 item 下标:`currentCategoryIndex` - 所有 item 元素:`itemRefs` - ul 的横向滚动偏离位置:`ulScrollLeft` - 最后在 `currentCategoryIndex` 发生改变时,获取 item 下标元素的 left 和 width,计算 sliderStyle 即可。 # 弹出窗口 popup ![20230522140849](https://zx21-img.oss-cn-shenzhen.aliyuncs.com/img/20230522140849.png) - 当 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`:右下角悬浮区域。 ![20230524140005](https://zx21-img.oss-cn-shenzhen.aliyuncs.com/img/20230524140005.png) # PC 端 Header ![20230524140453](https://zx21-img.oss-cn-shenzhen.aliyuncs.com/img/20230524140453.png) 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)