# 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件事.声明接收参数 在模板里展示接收到参数
Search params参数为:{{$route.params.keyword}}-----query参数为:{{$route.query.keyword1}}
第二步:全局注册组件 在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
1?skuNum=skuNum:skuNum=1">
+
1?skuNum--:skuNum=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中能到