# vue-pro-0822 **Repository Path**: zbbyoo/vue-pro-0822 ## Basic Information - **Project Name**: vue-pro-0822 - **Description**: vue-pro-0822vue-pro-0822vue-pro-0822vue-pro-0822vue-pro-0822vue-pro-0822 - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 2 - **Created**: 2023-02-06 - **Last Updated**: 2023-02-22 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README ## PC项目 ### 01. 项目基础配置 1. 下载一个脚手架,配置gitee 2. 配置自动打开浏览器 * 在`vue.config.js`中进行配置,这个文件主要是给脚手架的webpack进行补充配置 * 代码如下: ```js const { defineConfig } = require("@vue/cli-service"); module.exports = defineConfig({ //默认情况下 babel-loader 会忽略所有 node_modules 中的文件。你可以启用本选项,以避免构建后的代码中出现未转译的第三方依赖。 transpileDependencies: false, //配置devServe devServer: { //自动打开浏览器配置 // open: true, //配置主机地址 host: "192.168.14.245", //配置端口号 port: 8888, //配置是否开启服务器压缩 compress: true, }, }); ``` 3. 配置eslint * 我们可以在`vue.config.js`中直接配置`lintOnSave:false`关闭所有的eslint检测(不推荐) * 我们推荐的是在`eslintrc.js`中针对性的关闭某个不需要的检测 * 当前也可以通过`//eslint-disable-next-lint`或者`//eslint-disable`关闭某个或者某段代码的eslint检测 4. 路径别名配置 * 首先Vue脚手架内部已经配置好了'@'这个别名,代表"src/"目录 * 我们可以在`vue.config.js`中通过`configureWebpack`配置项配置其他别名 ```js configureWebpack: { resolve: { alias: { "@comp": "@/components", }, }, }, ``` * 虽然别名配置了,但是vscode并没提示,所以我们需要在`jsconfig.json`中进行配置 ```js "paths": { "@/*": [ "src/*" ], "@comp/*": [ "src/components/*" ] }, ``` 5. less的配置 * 默认脚手架中已经有了`less`的包了 * 我们需要安装`less-loader`的包,`npm i less-loader -D` * 我们在style标签中书写`lang="less"`属性即可使用less 6. 公共组件Header和Footer * 在components文件夹中创建两个公共组件Footer和Header * 直接去静态中拿到静态结构和样式(注意图片需要引入) * 在public目录中引入`reset.css` ### 02. 路由基础配置 1. 配置路由 * 配置Login/Register/Home/Search四个组件的路由 * 配置默认路由(/) * 配置任意路由(/*),任意路由必须配置在路由表的最后边 * 目标:在地址栏输入对应的路由地址,可以实现跳转 2. 对刚才的路由添加声明式导航 3. Footer组件的条件渲染 * Footer组件可能在某些路由下不展示,所以需要进行条件渲染: - 解决方式1:直接书写v-if条件渲染,接受当前路由的命名不等于XXX和XXX和XXX..... - 解决方式2:把插值语法的复杂逻辑直接书写成计算属性 - 解决方式3:因为Footer是否展示和路由相关,所以我们可以让路由携带元信息代表当前是否展示 4. 完成HOME组件的静态 ### 03.axios的配置 1. 复习axios及拦截器 * 使用axios的时候,一般都会使用axios.create()方法创建一个axios实例 * 我们会给创建的axios实例添加请求和响应拦截器 * 请求拦截器补充: - 请求拦截器可以设置多个,并且会按照书写倒序依次执行 - 请求拦截器的失败处理函数主要是用来处理上一次执行的请求拦截器中的失败信息 * 响应拦截器补充: - 请求拦截器一直失败,则不再发送请求,直接进入响应拦截器的失败处理函数 * 为什么拦截器的成功是返回一个值,而失败是返回一个失败的promise实例??? - 因为axios的拦截器函数其实都是作为axios的then方法中执行的 - then中如果直接返回一个值,则默认是成功的promise,值为返回的值 - then中想要返回一个失败的promise,只能在then的回调函数中return一个失败的promise 2. 什么是跨域 * 浏览器最安全核心的功能就是同源策略,所为同源指的是协议,端口号,域名保持一致 * 浏览器在请求的时候,违背了同源策略(请求的url协议,域名,端口号有任意一个和当前页面的url不同)即为跨域 3. 解决跨域的方式1--JSONP * 虽然同源策略限制了ajax的跨域请求,但是并没有限制HTML的资源请求(img,link,script等) * 我们可以通过script标签的src属性进行跨域请求,并携带一个本地定义的回调函数的名称 * 服务器返回一个回调函数调用的字符串,后端可以把前端请求的数据封装在回调函数调用字符串的参数内部 * 因为是script发送的请求,所以script接受到响应之后,就会执行这段代码,则前端的回调函数就会被调用,并接受到参数 4. 解决跨域的方式2--CORS * CORS:跨域资源共享 * CORS解决跨域主要是由浏览器和服务端共同完成(不需要开发人员) * 当浏览器发送ajax请求的时候,如果发现是跨域请求,就会在请求头中携带一个Origin字段(Origin字段主要是为了说明当前请求的来历) * 服务器获取Origin的值,判断当前源在不在允许请求的白名单中 - 如果在,则直接响应请求,并携带一个Access-Control-Allow-Origin响应头,值为当前的Origin - 如果不再不会响应头中就不会携带Access-Control-Allow-Origin头 * 客户端会根据响应头中的Access-Control-Allow-Origin进行判断是否放行 * 如果我们想要连带Cookie一起发送,则需要前端的开发人员配合 5. 解决跨域方式3-反向代理 * 在开发环境下:无论是脚手架中devServer的的proxy还是vite脚手架中的proxy,其实底层都是用的是一个node库`node-http-proxy`,我们可以进行proxy配置 * 在生产环境下:我们一般都会在我们的服务器搭建nginx代理服务器解决跨域问题 5. 代理(代理服务器) * 代理服务器(Proxy Server)的功能是代理网络用户去取得网络信息,形象地说,它是网络信息的中转站,是个人网络和Internet服务商之间的中间代理机构 * 代理服务器分为正向代理服务器和反向代理服务器 * 正向代理服务器:代理的是客户端,对客户端负责 - 突破自身ip访问限制 - 开启加速器 - 缓存数据 - 隐藏访问者 * 反向代理服务器:代理的服务端,对服务端负责 - 负载均衡 - 搭建防火墙(隐藏真实ip) - 缓存静态文件 - 开发人员解决跨域 6. 在vue脚手架中配置代理 * 在`vue.config.js`的`devServer`配置中配置`proxy`配置项 * 我们可能会配置多个代理,每一个代理都起一个名字作为这个配置对象的key,未来通过把这个名字作为请求前缀来区分使用哪一个代理 * ```json proxy: { "/dev-api1": { //目标服务器地址 target: "http://gmall-h5-api.atguigu.cn/", //是否开启WebSocket协议 // ws: true, //是否在跨域的时候,把请求的源改为目标地址(一般写为true),这样目标地址就会放行 changeOrigin: true, //如果需要去请求代理,则把请求地址的的前缀替换(前缀的作用只是为了找到对应的代理) pathRewrite: { "^/dev-api1": "", }, }, }, ``` * 在请求的时候,不需要在写目标地址,直接默认请求自己的服务器即可,如果自己的服务器没有资源,则默认会走代理去请求资源 * 为了区分将来走哪一个代理,所以在我们所有的请求前都添加一个对应代理的字符串前缀 7. 环境变量的简单配置 * 我们在很多地方在开发环境和生产环境的值是不一致的(比如代理前缀开发环境是'/dev-api',生产环境是'/prod-api'),我们希望区分开发环境和生产环境 * `process.env`可以得到环境变量对象,内部有一个`NODE_ENV`属性可以得到当前是开发环境还是生产环境,所以我们可以如下配置:`baseURL: process.env.NODE_ENV === "development" ? "/dev-api1" : "/prod-api1",` 8. 环境变量文件的配置 * 我们可以在项目中配置`.env`,`.env.development`,`.env.production`三个文件 * 在文件中可以使用`VUE_APP_`作为前缀给`process.env`对象扩展属性 * 其中`.env`文件无论在什么模式下都会执行 * `.env.development`文件只有在开发环境下才能执行 * `.env.production`文件只有在生产环境下才能执行 * 我们可以在`.env.development`和`.env.production`文件中配置一个变量`VUE_APP_API`代表前缀,这样就可以在项目中直接使用`process.env.VUE_APP_API`得到对应环境的值 9. 拦截器中的重要配置 * 请求拦截器目前不需要配置 * 响应拦截器:只要能进入当前响应拦截器,就说明请求是成功的,但是不一定是我们想要的信息,我们还要拿到返回的数据再次判断是否是我们想要的信息 * 响应拦截器中如果不是我们想要的信息,则直接抛出错误信息,如果是我们想要的信息,则直接返回res.data.data * ```js if (response.data.code !== 200 && response.data.code !== 20000) { //此时虽然请求成功,但是不是我们想要的,我们可以把错误直接展示 alert(response.data.message); //因为error对象固定有一个message属性,所以我们在抛出自己的错误的时候,我们可以把错误封装到一个对象的message属性上,方便后续处理 return Promise.reject({ message: response.data.message }); } else { return response.data.data; } ``` 10. 进度条的配置 * 如果想要在每次请求都要进度条,则在拦截器中配置 * 如果想要路由切换的时候才有进度条,则在路由守卫中配置 * ```js //使用全局前置守卫配置开启进度条 router.beforeEach((to, from, next) => { NProgress.start(); next(); }); //使用全局后置钩子配置关闭进度条 router.afterEach(() => { NProgress.done(); }); ``` ### 04.三级分类逻辑 1. vuex的基础配置 * 配置基础的模块化的vuex,以category模块作为案例 * 目标是可以在所有的组件内部看到$store 2. postman测试接口 * 测试get和post接口 * 保存当前的测试,方便下一次使用 3. 接口文档阅读和书写 4. 封装三级分类的api请求函数 * 在`src/api`中封装三级分类的3个请求函数,并暴露出来 5. 在vuex中封装获取一级分类列表的操作 * vuex中action书写异步请求一级分类列表 * 在mutations中把得到的请求结果赋值给state中的category1List * 在组件中调用actions测试,并查看netWork确认请求是否ok 6. TypeNav组件中一级分类的渲染 * 在TypeNav组件中使用辅助函数获取actions函数和state的数据 * 在mounted初始化的时候派发actions函数 * 在模板中渲染state数据 7. 根据一级分类请求二级分类列表 * 在actions中封装一个获取二级分类的函数 * 函数接受一个category1Id作为参数,得到结果,并保存在对应的一级分类对象的children属性中 * 随便在组件中派发这个action并随意传入一个category1Id,查看结果 * 发现没有响应式 8. 请求二级分类添加响应式渲染 * 上边操作没有响应式的原因是我们一级分类对象的children属性是新增的 * Vue的响应式原理中 新增属性是没有响应式的,需要使用Vue.set方法 * 在设置一级分类的mutations函数中,给每一个category1List的对象使用Vue.set方法扩展一个children属性,值为空,方便后边响应式的赋值 * 封装一个设置二级分类的mutations函数,当actions中请求到二级分类之后,提交到当前的mutations给对应的一级分类对象添加二级分类数据children 9. 三级分类列表的动态样式渲染 * 目标:鼠标移入每一个列表,给一个背景颜色,和展示详情右侧区域 * 设置一个动态类active,拥有active的元素就可以拥有特殊样式了 * 使用一个变量保存当前鼠标移入元素的下标,给每一个元素上设置一个动态类active,判断条件是当前元素的index和鼠标移入的index相等 10. 三级分类列表移入后发送二级分类请求 * 三级分类列表移入后,直接派发二级分类的actions函数,并携带当前的id作为参数即可 * 在模板中响应式的渲染每一个一级分类的二级分类列表 11. 对三级分类鼠标移动进行节流操作 * 使用lodash进行节流操作 * 想要列表节流,首先必须所有的元素的事件都是同一个事件函数 * 我们使用ladash方法把事件函数进行处理,得到一个真正的节流函数,赋值给每一个元素的mouseenter事件即可 * ladash方法接受最后一个配置对象,有两个配置 - leading: false, // 指定调用在节流开始前是否执行一次函数 - trailing: true, //指定在调用节流后是否执行一次函数(如果为true,鼠标移出后时间到了还会再执行一次函数;如果为false,鼠标在节流时间没到的时候移入某个元素上,函数就会一直不再触发) * 我们为了解决trailing导致的bug,可以设置一个变量保存当前鼠标是否在三级分类区域 * 然后在事件函数中判断,如果已经不在了,则直接不再执行函数内后边的代码 12. 根据二级列表请求对应的3级列表 13. 三级分类点击时候的路由导航 14. 使用事件委托解决路由跳转并传递动态路由参数 15. search的静态及全局公共组件TypeNav的设置 16. typeNav组件中Nav在不同组件中的动态样式 17. 搜索按钮跳转并合并params和query参数 ### 05.轮播图 01. 使用mock拦截banner和floor的请求 * 安装mock:`npm i mockjs` * 配置mock拦截ajax请求: ```js import Mock from "mockjs"; import banner from "./data/banner"; import floor from "./data/floor"; Mock.mock("/mock-api/banner", "get", () => { return { code: 200, message: "成功", data: banner, ok: true, }; }); Mock.mock("/mock-api/floor", "get", () => { return { code: 200, message: "成功", data: floor, ok: true, }; }); ``` * 在入口文件引入mock.js * 重新配置一个新的axios实例,公共路径不携带跨域前缀 * 在对应的组件中发送请求 02. 动态渲染banner和floor的数据 03. 在List组件中完成轮播图 * 下载swiper@5 * 使用swiper,注意把在实例化swiper的时候不要使用类选择,而是使用ref获取到元素选择,否则可能会造成多个swiper冲突 * ```js new Swiper(this.$refs.Swiper, { direction: "horizontal", // 垂直切换选项 loop: true, // 循环模式选项 // 如果需要分页器 pagination: { el: ".swiper-pagination", }, // 如果需要前进后退按钮 navigation: { nextEl: ".swiper-button-next", prevEl: ".swiper-button-prev", }, }); ``` * 把swiper实例化代码放在this.$nextTick中,并把整体放在watch监听数据请求回来之后的逻辑中(需要等到数据回来,并且DOM全部加载完成) 04. 在list组件中完成轮播图 * list中不需要使用$nextTick和watch,因为数据是现成的直接传递进来的 * 可以直接使用swiper即可 05. 封装Swiper公共组件 * 把floor组件的swiper改写成list的样子,并都添加立即调用 * 然后封装公共组件,全局注册 * 把对应的数据通过props传递进入即可 06. 搜索页监听动态路由参数的变化的三种方法 * 使用watch监听$route,一旦动态路由发生改变则可以触发 * 使用计算数据得到$route中的值,可以监听$route变化得到最新的值 * 直接使用路由表中的props传递进来,props的值都是最新的值 07. 初始化搜索参数并且发送搜索请求 * 根据接口,在数据中初始化post请求的搜索参数 * 封装api请求函数,并发送请求 08. 搜索页数据的渲染 09. 解决非第一次搜索失效的问题 * 需要使用watch监听$route的变化,然后重新发送请求 10. 每次点击完三级分类后,关闭三级分类的窗口 * 在typeNav组件中,发现三级分类的打开和关闭由计算属性navIsShow控制 * navIsShow由isMouseInNav控制 * 所以可以监听$route的改变,切换isMouseInNav的值 11. 搜索条件的展示 12. 关闭三级分类的搜索条件 * 搜索条件是从props来的,props是从路由动态参数过来的 * 关闭搜索条件,只要重现路由跳转不携带query参数即可 13. 关闭关键字的搜索条件 * 搜索条件是从props来的,props是从路由动态参数过来的 * 关闭搜索条件,只要重现路由跳转不携带params参数即可 * 使用事件总线的方式进行兄弟组件通信,通知header组件清空输入框内容 14. 选择品牌并搜索 * 在子组件内部进行点击品牌,并通过自定义事件子传父的方式传递给父组件 * 父组件收到以后修改searchParams的值,并重新发送请求 15. 品牌的标识的设置和删除 * 设置品牌标识条件渲染 * 点击删除的时候,只药删除searchParams中对应的值并重新渲染即可 16. 平台属性的选择 * 在子组件内部进行点击品牌,并通过自定义事件子传父的方式传递给父组件 * 父组件收到数据以后向searchParams中的props数组添加数据,并判断是否重复 * 重新发送请求 17. 添加平台属性标识及清除某个平台属性 * 可以根据props数组的值遍历生成平台属性标识 * 可以设置点击以后,删除对应数组的值,并重新发送请求 18. 排序按钮的动态样式渲染 * 首先给按钮设置iconfont图标 * 设置按钮的动态类class * 设置按钮的iconfont图标的动态类 * 设置iconfont图标的动态渲染 19. 排序按钮的逻辑实现 * 当点击的是当前排序逻辑的时候,则切换排序方向 * 当点击的是新的排序逻辑的时候,默认选择降序 * 修改searchParams的值,并重新请求 20. 页码逻辑 * 封装一个页码的公共组件 * 根据当前页、总页数、连续页长度计算连续页的开头和结尾的值 * 在结构中遍历渲染按钮 * 把页码组件需要的数据用props传递进来 * 给页码组件绑定自定义事件,切换页码传递给父组件 21. 解决v-if和v-for不能一起使用的问题 * 使用计算属性解决 ### 06.detail详情页 01. detail静态及跳转并解决路由跳转后滚动条的问题 * 滚动条的问题需要在路由的配置中配置 02. detail详情页请求数据 * 要在跳转detail的时候使用动态路由参数携带skuId * 然后发送请求 03. detail详情页数据渲染及解决假报错的问题 * 在使用数据的时候,因为有的时候数据还没有请求回来,如果我们使用数据的深层次属性,会出现报错,但是数据请求回来以后,就解决了,我们把控制台的报错称作为假报错 * 假报错一般解决方式: - 使用初始默认值的方式 - 使用可选链操作符的方式 04. 完成原生放大镜效果 05. 详情页缩略图的轮播 06. 详情页缩略图点击交互效果 07. 销售属性的排他 08. 商品数量的加减 09. 添加购物车 10. 加入购物车成功页逻辑 11. 购物车页面的静态 12. 请求购物车的数据 13. 用户临时id的设置 14. 购物车数据渲染 15. 计算购物车商品的总数量和总价格 16. 封装购物车的几个api请求函数 17. 修改商品的单个选中状态 18. 修改或者获取商品的全选状态 19. 删除单个商品和已选中商品 20. 购物车商品数量的累加累减 21. 购物车商品数量累加累减的节流操作 22. 购物车商品数量输入数量 ### 07.登录注册 01. 登录注册的静态和收集注册数据 02. 获取验证码的逻辑及计时逻辑 03. 注册逻辑 04. 登录的基础流程 05. 前端token校验代码实现 06. 首页个人信息的条件渲染