# vueproject **Repository Path**: sym2020/vueproject ## Basic Information - **Project Name**: vueproject - **Description**: No description available - **Primary Language**: Java - **License**: MulanPSL-2.0 - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2021-04-09 - **Last Updated**: 2021-04-24 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README gulishop-client 20210410 day01安装 架构 1. 安装 安装脚手架 vue create gulishop-client (随意起的名字) publish 此文件是根目录 最终对应的是dist 直接copy过去,不收webpack的限制,浏览器运行的是构建好的dist文件, assets静态资源,各个组件之间公共的部分,受到webpack的管理 package.json包 最重要的是5个name version scripts dependencies依赖 devDependencies开发依赖 2. 启动项目 npm run serve 或者yarn serve 3. 配置vue.config.js文件 禁用语法检查 module.exports={ lintOnSave:false//禁用语法检查 } 4.配置main.js import Vue from "vue" import App from './App' import router from '@/router' Vue.config.productionTip=false new Vue({ render:h=>h(App), router }).$mount('#app') 5.jsconfig.json配置别名@提示 { "compilerOptions": { "baseUrl": "./", "paths": { "@/*": ["src/*"] } }, "exclude": ["node_modules", "dist"] } 6.配置完文件后需要重启项目 npm run serve 或yarn serve 7.git的基本操作 先有本地代码 创建本地库 创建远程库 关联本地和远程 修改本地 修改远程 先有远程代码 直接克隆 git分支扩展 分支创建和合并 本地创建分支 git checkout -b dev 本地推送新分支自动在远程库建立新分支 git push origin dev 合并分支之前如果是多人协作先拉取一下远程master,以防止别人已经做了更改 本地切换到master 然后再合并分支 git merge dev 合并之后再次推送到远程master 分支删除 项目开发完成可以删除分支 git push origin --delete dev 删除远程分支 git branch -d dev 删除本地分支 8.观察页面确定页面主体框架 我们的功能页面是 上中下结构 上和下是不变化的,只有中间在变化 切换页面的时候,页面是不刷新的,证明切换的过程当中请求采用的是ajax请求 这个是单页面应用,中间的部分就是我们的路由组件切换 上和下,我们全部都定义为非路由组件 9. 创建文件夹pages/( Home Search Login Register) 为其分别创建index.vue文件 项目创建的基本结构 publish //放置第三方或者是长久不变的 最终对应dist 直接copy上去,不收webpack管理 css reset.css //css初始化 index.html favicon.icon src assets //静态资源 各个组件之间公共的资源 受webpack的管理 pages //放置路由组件的文件夹 Home index.Vue Search index.vue Login index.vue Register index.vue components //放置非路由组件的文件夹 Header index.vue Footer index.vue router //放路由文件的文件夹 index.js //路由的主文件 routes.js //放路由规则 单独成一个文件 App.vue //外壳文件 main.js //入口文件 babel.config.js package.json jsconfig.js //配置别名 @提示 @代表src 关于路径问题都要写成@注意好管理 vue.config.js //配置webpack的文件隐藏了,在此文件中可以写配置文件自动添加到webpack里 定义页面主体组件组装,切换路径可以组件跳转(非路由组件和路由组件) Header和Footer是固定的所以是非路由组件写入components文件夹 Home Search Login Register 都是点击才会出现所以是路由组件并且是一级的(可能内部还有二级) 10.路由器配置文件 路由组件的使用步骤 1)安装vue路由 npm i vue-router -S(运行依赖 运行项目) npm i less lee-loader@5 -D(开发依赖 打包时用这个) 2).引入并使用 import Vue from 'vue' import VueRouter from 'vue-router' import routes from './routes' Vue.use(VueRouter) //声明使用插件,所有的vue官方插件都要声明使用,第三方插件不需要 // 本质在调用插件内部的install方法,只有调用它,插件的功能才会添加到Vue身上和Vm身上 3) 必须往外暴露一个路由器对象 在router路由器中添加一个 配置项routes const router = new VueRouter({ routes }) export default router 4)必须在Vue配置路由对象当中注册路由器对象 在main.js中写入 import router from '@/router' new Vue({ render: h => h(App), router }).$mount('#app') 5)使用 11.路由组件和非路由组件的区别 使用都是三大步 定义 注册 使用 定义都是一样去定义的,只是定义的文件夹不同 路由组件放文件夹pages里 非路由文件放components里 注册的时候区别:非路由组件是注册在要使用的组件当中(或者全局注册),而路由组件是要在路由配置routes:[path:xxx,component:xxx]当中注册的 使用的时候区别:非路由组件使用注册的组件标签, 而路由组件使用声明式导航(router-link,router-view)和编程式导航来使用(push replace router-view) 非路由组件和路由组件生命周期不同 路由组件在切换的时候,会销毁重建(keep-alive) 而非路由组件不会 12.Header和Footer非路由组件的使用 定义 定义在components 注册 注册在App 使用 在App当中书写组件标签 13.Home Search Login Register路由组件的使用 定义 定义在pages当中 注册: 路由器的相关配置 安装路由 npm i vue-router -S 引入并声明使用路由插件 向外暴露一个路由器的对象 在main当中把路由器注册到Vue的配置对象 注册路由组件(配置路由的时候就已经注册了) 使用: 使用router-link 和 router-view push和replace 和 router-view router-link 切换路径用的 不能自己定义的函数 this.$router.push(路由名) this.$router.replace('/search') 代码区 Header/index.vue

登录 免费注册

文件App.vue 14.把静态页面的东西复制到对应组件 html 直接复制静态页面当中的html css(less) 安装less和less-loader的包@7,否则less不认识 images 在对应的组件目录下,创建images,把对应的图片给捞到这个文件夹下(注意路径要对应好) 把内容复制过程当中,莫急莫慌 css初始化放入public,reset.css需要引入到index.html当中 复制完成之后,需要把router-link用上,对应的a标签得修改过来, 按钮点击跳转需要用到编程式导航 重定向路由,访问根路径,默认访问的就是home首页 routes:[ { path:'/', redirect:'/home' } ] 15.登录注册不需要Footer,通过路由meta配置解决 如果不写meta 表示空对象 从route当中可以获取到path判断可以解决但是麻烦 通过路由配置的时候路由对象当中配置meta设置来做 routes:[ { path: '/login', component: Login, meta:{ isHidden:true } }, ] 在App.vue中写入 16.路由的传参 1)跳转路由的2种基本方式 声明式 编程式 this.$router.push()/replace() 编程式导航比声明式导航 更加灵活,(内部可以加入自己的逻辑,某些请求效率会更好) 2.路由携带参数的2种方式 params参数 属于路径的一部分,匹配的时候路由的path中需要占位: routes:[ path:'/search/:id' component:Search ] query参数,不属于路径的一部分,匹配的时候路由的path 不需要写 无论是params参数还是query参数最终匹配完成都会解析到当前这个路由对象当中params和query属性当中 $route.params.xxx 比如$route.params.id $route.query.xxx 显示路由组件的时候,会把当前这个路由对象,传递给组件当中的this.$route 所以this.$route就可以获取到之前传递的参数 3.跳转路由的3种写法 1)字符串写法 第一步:在Header组件中 this.$router.push('/search/'+this.keyword+"?keword1="+this.keyword.toUpperCase()) 第二步:在路由器中写占位: routes:[ { path:'/search/:keyword', //写占位符 component:Search } ] 第三步:在Search组件中 需要做2件事.声明接收参数 在模板里展示接收到参数 第二步:全局注册组件 在main.js中 import Pagination from '@/components/Pagination' Vue.component('Pagination',Pagination) 第三步:在search组件适当的位置使用 Pagination标签即可 第四步:写逻辑了 1)) search组件传参给子组件Pagination 通过props goodsListInfo.total传递的有可能是undefined因为goodsListInfo初始化的时候是{} 所以接收的时候需要注意了 要限定了 continueNum连续数 是奇数,连续数最少是3 一般是5 2))在Pagination/index.vue中接收参数 props 需要用对象的复杂写法了 props: { //当前页 currentPageNum: { type: Number, required: true }, //总条数 total: { type: Number, default: 0 }, //每页多少条 pageSize: { type: Number, required: true }, //连续数 continueNum: { type: Number, default: 5 } 3))计算总页数和计算开始数和结束数 computed: { //计算总页数 totalPage() { return Math.ceil(this.total / this.pageSize) }, //根据当前页的不同计算连续也的起始位置和结束位置 startEnd() { let {currentPageNum, total, continueNum, pageSize, totalPage} = this let start = 0, end = 0 if (continueNum > currentPageNum) { start = 1, end = continueNum } else { //先考虑正常情况下 start = currentPageNum - Math.floor(continueNum / 2) end = currentPageNum + Math.floor(continueNum / 2) //大于最大值 小于最小值 if (start < 1) { start = 1, end = continueNum } if (end > totalPage) { start = totalPage - continueNum + 1, end = totalPage } } return {start, end} } } 4))使用在模板位置使用 5))在search组件中写自定义事件 分页器pagination与search父组件用到了自定义事件 绑定自定义事件changeNum methods:{ //分页器 changeNum(page){ this.searchParams.pageNo=page this.getSearchInfo() } } 46.detail组件 详情页 从老师给的静态组件复制到pages中 注意路由里不要带空格 这个bug很难找 控制台不会报错 也不出效果 配置路由 router/routes.js 文件中 第一步:引入 Detail组件 import Detail '@/pages/Detail' 第二步:在routes中增加一项路由 这个需要携带参数 商品的ID 只有知道了id才能向服务器发请求 返回来详情是什么 { path:'/detail/:skuId', component:Detail }, 第三步:router/index.js中增加一项 vue官网看 路由router 增加滚动行为 复制代码过来 scrollBehavior 是为了让右边的滚动条在最上方 有时候点击过来的时候在中间的某个位置,体验效果不好 export default new VueRouter({ // mode:'history',//不带# routes, scrollBehavior (to, from, savedPosition) { return { x: 0, y: 0 } } }) 第四步:在模板适当的位置用需要携带参数 可以写成字符串形式 第五步:检查下是否从search页可以跳转到detail详情页 47.解决bug 用户在search点击最上方的后退按钮跳转到home首页的问题 1))无论在search也怎么点击 只要从search跳转到search页都没有历史记录 用replace 在需要跳转路由的两个地方 this.$router.replace({name:'search',params:this.$route.params}) this.$router.replace({name:'search',query:this.$route.query}) 2))header页在home页和search都有 需要判断一下 然后再跳转路由 如果不是home页是search页 replace跳转到search页 不留历史记录 如果相反 则用push 有历史记录的 3))TypeNav组件的push 也需要判断一下 看是否留历史记录 2))和3))写的代码一样 if (this.$route.path === '/home') { this.$router.replace(location) } else { this.$router.push(location) } 4))当用户的点击分页器时 重新发了请求,需要默认到第一页 在修改参数需要发请求的地方 都需要添加一下 this.searchParams.pageNo=1 48.详情页detail静态页面已经写好 需要初始化数据 动态渲染数据 第一步.写api接口函数 看接口文档 //请求地址:api/item/{skuId} 请求方式 GET请求 需要携带参数 params参数 export const reqDetailListInfo=(skuId)=>{ return Ajax({ url:`/item/${skuId}`, method:'get' }) } 第二步写store store/detail.js中写三连环 在store/index.js中引入 模块合并 第三步:拧开关 在Detail/index.vue中 第四步 从vuex中捞数据 第五步 动态的展示数据 第六步 动态交互 1))销售属性高亮 (排它) 2))轮播小图点击时变成红色小框 3))轮播图 4))放大镜 5))购买数量 6))加入购物车 1))销售属性高亮 (排它) ischecked 1高亮 0不高亮 所有数据遍历 都为0 当前为1 第一步绑定事件dd 需要传参
{{ saleAttrValue.saleAttrValueName }}
第二步 //点击销售按钮切换点中状态(排它) changeChecked(saleAttrValue, spuSaleAttrValueList) { spuSaleAttrValueList.forEach(item => { return item.isChecked = "0" }) saleAttrValue.isChecked = "1" }, 2))轮播小图点击时变成红色小框 在imagelist组件中 第一步 绑定事件 第二步methods:{ changeImg(index){ //点击图片切换图片的橘色框 this.defaultIndex=index //通知兄弟组件zoom切换图片 this.$bus.$emit('changeDefaultIndex',index) }, 第三步 zoom组件中 data() { return { defaultIndex: 0//设置的参考值 } }, props: ['imageList'], mounted() { this.$bus.$on('changeDefaultIndex', (index) => { this.defaultIndex = index }) }, 3))轮播图 在imageList组件中 需要引入js 其中css已经全局引用了(main.js写了) 从轮播图组件复制一份watch里面的数据,把监视对象改成imageList id即ref改成imageSwiper 从轮播图swiper官网找api文档--sblidesgid(网格分布) 找到slidesPerView和slidesPerGroup复制过来添加到watch中 watch: { imageList: { immediate: true, handler(newVal, oldVal) { this.$nextTick(() => { new Swiper(this.$refs.imageSwiper, { slidesPerView:3,//每一屏需要3张 slidesPerGroup:3, //切换几张 // 如果需要前进后退按钮 navigation: { nextEl: '.swiper-button-next', prevEl: '.swiper-button-prev', }, }) }) } } } 4))放大镜 思想 //鼠标动 蒙版动 //蒙版动 大图动 //算鼠标的位置 代码区: methods:{ move(event){ //第一步 鼠标动 蒙版动 //计算蒙版的位置 let mask=this.$refs.mask //获取蒙版元素 在蒙版div处写ref 然后收集到$refs let bigImg=this.$refs.bigImg //获取大图元素 在大图div处写ref 然后收集到$refs //第二步 算鼠标的位置 let mouseX=event.offsetX //获取鼠标x轴坐标 let mouseY=event.offsetY//获取鼠标y轴坐标 let maskX=mouseX-mask.offsetWidth/2 //鼠标移动算下蒙版的位置x轴=鼠标x轴的坐标-蒙版宽度的一半 let maskY=mouseY-mask.offsetHeight/2 //鼠标移动算下蒙版的位置Y轴=鼠标Y轴的坐标-蒙版高度的一半 //在赋值之前计算一下边界 if (maskX<0){ maskX=0 }else if (maskX>mask.offsetWidth){ maskX=mask.offsetWidth } if (maskY<0){ maskY=0 }else if (maskY>mask.offsetHeight){ maskY=mask.offsetHeight } mask.style.left=maskX+'px'//赋值给蒙版绝对定位后的值 mask.style.top=maskY+'px' //第三步 蒙版动 大图动 bigImg.style.left=-2*maskX+'px' //大图默认是蒙版的2倍, bigImg.style.top=-2*maskY+'px' } } 5))购买数量 input框用到双向绑定v-model 可以和@change事件合用 这个在底层的源码分析中就@input事件和@change事件实现双向绑定 需要考虑的情况 点击+ 递增1 需要考虑从字符串转数字 点击- 递减 最小值是1 只能递减到1 + - 6))加入购物车 第一步:复制静态组件添加购物车成功 放在pages/AddCartSuccess下 第二步:写api 看接口文档 请求地址:/api/cart/addToCart/{ skuId }/{ skuNum } 请求方式post 写接口函数的时候 需要携带参数,params参数 export const reqShopCart=(skuId,skuNum)=>{ return Ajax({ url:`/cart/addToCart/${ skuId }/${ skuNum }`, method:'POST' }) } //也可以省略此步骤 此时可以测试下 请求函数是否成功 调用一下函数和在main.js中引入 第三步 写store 增加store/shopcart.js 写三连环 然后再store/index.js中引入和模块合并 49.补充的知识点 promise session cookie localStorage sessionStorage 按下F12在application中能到