我的码云git管理项目记录

本文详细介绍Vue项目从安装配置到移动端开发的全过程,涵盖多页面应用与单页面应用对比,移动端常见问题解决方案,以及首页、城市页和详情页的详细开发流程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、安装

先是配公钥,然后本地与远程同步...一系列操作

$git status 查看新增加进来的文件以及代码改动

$ git add .    将本地的代码推到git本地缓存中

$ git commit -m 'project initialized'    将本地缓存中的内容提交到本地的空库中

      -m是留一条信息

$git push 推送代码

二、关于多页面应用/多页面应用

1.多页面应用

 (1).每次点击一个不同页面,后台就直接返回回来一整个页面

 (2).seo优化:在搜索引擎中,网页排名,只能识别到html文件中的内容,js中的内容识别不到

 (3)缺点:每次跳转都需要发送一次http请求,所以切换慢

2.单页面应用

(1).一进来加载一个首页,每次点击其他页,是通过js将原来的页清除,用js进行新的渲染

(2).单页面首次需要发一个http请求,同时还需要发一个js请求,两个请求都回来了,才会展示首屏

(3).缺点:在百度,谷歌搜索排名差

vue开发中,服务器端渲染就可解决单页面开发中的问题

三、vue移动端的基本配置

1.reset.css重置样式文件,上网搜就行

2.移动端1px边框问题,各种手机不同  border.css

3.300ms点击延迟现象 fastclick 第三方模块   --save 即不管是开发还是打包上线都要用的模块用--save

四、正式进入项目的开发----首页和城市页

1.首先安装stylus  stylus-loader (对于stylus、scss、less的区别自己先下去补功课)

2.注意移动端用rem,根据reset.css中html的font-size去换算 ,我们这里1rem = 50px;

3.关于图标库的使用方法

4.特别要注意的是每次修改完webpack的配置必须要重启服务器,自己常常因为这个找半天错误也看不出来

3.首页--swiper开发

(1)接下来进行企业级开发,我们在码云上面建一个新的分支,每次开发一个新功能都新建一个新的分支,最后保证这个分支不错误,最后将这个分支合并到总分支master上

     $git pull  将线上的index-swiper分支拉到自己的本地

     $git checkout index-swiper  将本地原来的master分支切换成index-swiper分支

(2)按照github上vue-aswsome-swiper文档进行安装使用Swiper插件

(3)有一点需要注意,那就是加入.wrapper并设置样式是为了在3G网下,图片没有加载出来,无法将这个容器撑开,导致下面的内容置顶,然后等图片加载出来,下面的内容在弹回到原来应该的位置上,会有一次抖动

(4)项目完成后

以下常规操作,将index-swiper分支上的内容推送到index-swiper分支上

$git add .   推送到本地缓存

$git commit -m 'change'  提交本地缓存

$git push  将本地的代码推送到远程

接下来将index-swiper分支上的内容推到master分支上

$git checkout master  切换到本地master分支上

$git merge origin/index-swiper  将swiper分支上的内容推到master分支上

$git push 将合并后的master分支推动到远程仓库

3.首页--icons开发

还是利用swiper插件

需要注意的点是关于过多图标时如何进行分页

//注意这里要对swiper-slide根据页数进行循环,即一个slide一个slide的切换

<swiper :options="swiperOption">
			<swiper-slide v-for="(page,index) of pages" :key="index">
				<div class="icon" 
				     v-for="item of page" 
				     :key="item.id">
					<div class="icon-img">
						<img class="icon-img-content" :src="item.imgUrl">
					</div>		
					<p class="icon-desc">{{item.desc}}</p>
				</div>	
			</swiper-slide>
		</swiper>


//将所有的图片分配到他应该在的页中
computed:{
			pages(){
				const pages = [];
				this.iconList.forEach((item,index) => {
					const page = Math.floor(index / 8 );
					if(!pages[page]){
						pages[page] = []
					}
					pages[page].push(item);
				})
				return pages;
			}
		}

但是要注意计算属性中的pages名字要与返回出来的值的名字相同

将代码与master合并,并push远程

4.首页--后台数据获取(ajax   用axios实现)

1.pull拉下来的代码都是从master分支上拉下来的,将不同的分支合并  git merge xxx(分支名字)

2.利用 npm install axios --save 及生命周期钩子mounted()配合 获取后台数据

3.在没有后台数据的情况下,我们进行后台数据模拟,进行配置

   (1).static中的文件是可以在网页中显示的,即在网址栏可以直接搜到,而其他文件不可以查看

   (2).在.gitignore中写入的文件不会提交的到线上,所以讲ststic/mock文件写到这里面

   (3).但只能在本地这么做,要是发布上线的话,获取的数据必须是axios.get('/api/index.json'),所以将这个路径下的api请求都转到对ststic/mock文件夹下

   (4).使用webpack代理配置文件 config/index.js文件夹下

proxyTable: {
        '/api':{
            target:'http://localhost:8081',
        '^/api':'/static/mock'
            }     pathRewrite:{
               
        }
    }

开发过程中的小bug为什么一上来幻灯片事从第四张开始呢,因为在为获取后台数据之前,对应的数据是个空数组,所以直接渲染最后一张即第四张,所以要做一个v-if判断,使数据加载了之后再渲染幻灯片组件

还要注意不要把逻辑代码放在结构中,如需计算一些值课放在属性值计算函数中

5.城市页--路由配置问题

在router/index.js文件夹下进行配置

原理:在需要点击跳转页面的元素上加<router-link to='/city'>,其实是向这个页面进行跳转,然后index.js中的文件import City from '@/pages/city/City'是寻找这个单页面组件渲染到页面上

6.城市页--页面整体样式的开发以及axios获取后端数据(还是按照以前的方法在static文件夹下放上json文件)

    (1)在开发header和search中用到了box-sizing:border-box,假如给他的width设置成100%其实是包括边框和padding占到100%,而box-sizing:content-box实际上是内容区域就占到了100%不包括边框和padding

    (2)一个书写的规范,经常会在一些dom上边在嵌套一个dom,这样做的好处是好调整布局,非常方便

    (3)注意一点清除浮动的问题(高度塌陷)看一下list.vue中的样式代码

    (4)我们这里不用网页自带的滚动条了,用一个第三方库 npm install better-scroll --save,用滑动更加流畅,同时也有了上下拖拽的效果,等等

    (5)注意一下对象的循环和数组的循环,循环的都是什么(item,key)of cities     :key='key'对象的键;

(item,index)of cities     :key='index'数组的下标

5.城市页--关于右边的A-Z点击后左边切换到特定的位置,实现组件的联动  city-components分支上的代码

首先采取的是 子组件---》父组件----》子组件 的方式来实现两个子组件的传参问题

右侧子组件获取到点击的字母通过$emit传给父组件,父组件再将传过来的值给子组件 list

function(e){...}这里的e代表的是事件,e.target代表的是整个dom元素

接下来的内容是我没有接触过得开发,要好好理解一下,即右边的字母表鼠标在上边滑动时,左边也跟着滑动到相应的位置,需要好好理解:

(1)touchstart,touchmove,touchend是HTML5的新特性,触摸事件,即触摸刚开始,触摸一直移动,触摸结束

(2)我们一开始给了一个标识符touchStatus:false,当刚开始触摸时这个值设为true,当这个值为true了下边的touchmove函数才能执行

(3)这里的client是事件发生时,鼠标距离浏览器的可视区域的X、Y轴的位置,不包含滚动条的区域的部分。就算是页面进行了滚动,鼠标的坐标值还是参考可视区域的。即在屏幕中显示的那部分页面的最顶端的距离

offsetLeft和offsetTop事件源元素相对于父节点的偏移的像素值。即距离包裹他的div的值,一般在静止状态下获取

//Alaphbet.vue字母列表组件
handleTouchMove(e){
                //注意这里offset和client的用法
                if(this.touchStatus){
                    const startY = this.$refs['A'][0].offsetTop;
                    const touchY = e.touches[0].clientY
                    -79;//79即上边header和search的高度
                    // 求出当前滑动到的位置的字母下标
                    const index = Math.floor((touchY - startY)/20);
                    if(index >= 0 && index <= this.letters.length){
                        this.$emit('change',this.letters[index]);
                    }
                    
                }
            }

最后将这个改变的字母派发出去,传给父组件,父组件接收到,在传递给list.vue,list.vue有一个watch()侦听器,一旦letter变化,便会出发better-scroll的方法滑动到对应的字母列表上

//list.vue
watch:{
	// 一旦点击字母letter就会变化,监听到了他变化就出发事件
	letter(letter){
	    if(this.letter){
		const element = this.$refs[this.letter][0];
		this.scroll.scrollToElement(element);
	    }
        }
},

6.城市页--性能优化

你每滑动一次鼠标,就要触发一次handleTouchMove,很频繁,A距离上边的值一直是固定的,但是你每执行一次函数,你就要执行一次A距离的获取,我们利用一个生命周期钩子函数

data(){
            return{
                touchStatus:false,//设置一个标示位
                startY:0
            }
        },
        updated(){
            this.startY = this.$refs['A'][0].offsetTop;
        }

原理是,实际上,我们在渲染Aplaet.vue页面上时,一开始数据都是空的,只有当ajax获取到数据之后,数据进行了更新,生命周期钩子updated自动执行,再次进行渲染

函数节流-----当我们在右边的A-Z滑动时,函数执行的额频率相当高,即每动一小下下他都执行一次,所以我们规定在16ms内又一次滑动到其他位置那上一次的这个函数就不执行了,将定时器时间设置为16ms,在16ms内在次出发事件就将上一次的定时器清除掉,这样就降低了函数的执行频率,timer:null,在拖动的过程中明显感觉左边轮动的不是刷刷刷了

 handleTouchMove(e){
      //注意这里offset和client的用法
      if(this.touchStatus){
           if(this.timer){
           clearTimeout(this.timer);
      }
      this.timer = setTimeout(() => {
            const touchY = e.touches[0].clientY - 79;//79即上边header和search的高度
            // 求出当前滑动到的位置的字母下标
            const index = Math.floor((touchY - this.startY)/20);
            if(index >= 0 && index <= this.letters.length){
                   this.$emit('change',this.letters[index]);
            }
       },16)//延迟16ms在执行
     }
},

接下来就提交一下代码到git仓库中

7.城市页--搜索逻辑代码开发多看一下这部分的代码

实现的功能是在搜索框输入数据的时候相关的城市被选择出来,利用<input type="text" class="search-input" v-model="keyword" placeholder="输入城市名或拼音" />  v-model双向数据绑定

注意的是,运用好v-show来判断每一个div什么时候显示,什么时候不显示,此处也用了一次函数节流,不是每输入一个字母便搜索一次,而是100ms之内在搜索就清除上次的定时器

watch:{
            keyword(){
                if(this.timer){
                    clearTimeout(this.timer);
                }
                this.timer = setTimeout( () => {
                    const result = [];
                    // 这里循环的i为键名
                    for(let i in this.cities){
                        // cities[i]数组,对数组的每一项(对象)进行循环
                        // value代表数组的每一项
                        this.cities[i].forEach((value) => {
                            if (value.spell.indexOf(this.keyword) > -1 || value.name.indexOf(this.keyword) > -1) {
                                result.push(value)
                            }
                        })
                    }
                    this.list = result;
                },100);
            }
        },

8.城市页--vuex和localStorage的使用多看一下这部分的代码

首先弄明白vuex是怎么工作的(仔细阅读文档)

states----》vue components存储池向组件中传参,vue components---》actions----》mutations组件修改值反馈到存储池中

虚线部分相当于一个公共的存储池

states:静态数据存储

actions异步数据;

mutations:同步的数据;

getters:类似于组件中的computed,利用states的值进行计算,然后进行dom渲染

module:对负载的states、actions、mutations进行拆分

没有异步获取数据的时候actions是可以省略的

//list.vue中的按钮点击时
methods:{
			handleCityClick(city){
				this.$store.dispatch('changeCity',city);
				this.$router.push('/');
			}
		},

//让存储池获取点击的那个数据
export default {
	// ctx是执行上下文,通过这个ctx拿到接下来去执行的commit方法
	changeCity(ctx,city){
		ctx.commit('changeCity',city);
	}
}
//接着传递
export default {
	changeCity(state,city){
		//state指的是所有公用的数据
		state.city = city;
		//实现下次刷新也能保留上次的城市记录
		try {
			localStorage.city = city;
		}catch(e){}	
	}
}

利用HTML5新特性localStorage保证下一次刷新或者进来还是上次存贮的位置(城市)

路由跳转,类似于js中的location.href   vue中用this.$router.push(路径)

try-catch为了防止有的用户关闭本地存贮,导致页面出错的存在

mapState:将store仓库中的值直接映射到计算属性中,用this.city 代替this.$store.state.city,两种写法

import {mapState} from 'vuex'
computed:{
    ...mapState(['city'])
    //第二种
    ...mapState({currentCity:'city'})
}

8.首页城市页--网页性能优化

在做城市点击的时候我们会发现,没点击一次城市主页就会发送一次ajax请求(index.json)显然这样的性能特别差,我们利用vue内置的标签<keep-alive></keep-alive>包裹在入口文件App.vue的router-link外层,表示已经发送过请求的页面被存在内存中,就不在发ajax请求了

但是我么发现,这样还是有错误,因为每一次点不同的城市应该请求不同的内容加载,axios.get('/api/index.json?city=' + this.city) .then(this.getHomeInfoSucc)      city用store仓库获取,然后在keep-alive使用后会有一个生命周期钩子activated,在里边改变请求的city,用一个变量lastCity保存最后一次请求的页面,如果不同就ajax请求一次

五、详情页开发

上来不用说了,肯定是路由的配置

page/recommend.vue
<router-link
     tag="li" 
     class="item" 
     v-for="item of recommendList"
     :key="item.id"
     :to="'/detail/'+item.id" 
>
//router/index.js
routes: [
        {
            path:'/detail/:id',
            name:'Detail',
            component:Detail
        }
    ],
//detail.vue

getDetailInfo(){
	//从路由跳转那里获得的id被传递到router/index.js的:id中,然后在从:id中提取这个id
	// 第一种写法
	// axios.get('/api/detail.json?id=' + this.$route.params.id)
	// 第二种写法
	axios.get('api/detail.json',{
		params:{
			id:this.$route.params.id
		}
	}).then(this.handleGetDataSucc)
},

1.详情页--关于banner和gallary的开发

(1)由于项目中很多地方都会用到gallary所以我们吧他封装成一个公共组件,这个组件利用的是swiper插件

(2)在分页器哪里,刚引入时不显示,我们这时候查看源码没问题,就要逐级向上查找:

         首先将分页器的位置进行调整 bottom:-1rem

        之后我们发现还是不行,我们逐级向上的时候发现.swiper-container中有over-flow:hidden所以将其修改 .container >>> .swiper-container   overflow:inherit

(3)当刷新进来发现gallary不能正确的显示了,这是因为一开始gallary隐藏,当gallary被显示的时候swiper计算宽度会出现问题,一旦有这种问题就要想到“刷新”这一解决方法,所以在optionSwiper参数中加上observeParents/observer他们的意思是只要元素本元素或者父元素有改动便刷新一次

2.详情页--关于detail-header的显示和隐藏以及动画渐隐渐现

(1)利用状态值showBack/ !showBack来显示和隐藏导航,当二者对立显示时采用这种方法更好,不要用两个变量控制了

(2)利用生命周期钩子activated监听鼠标滑动,一旦页面展示即执行这个钩子,可以看一下里面坚挺的具体代码

(3)动画渐隐渐现,用动态样式<div :style="opacityStyle">来改变这个值实现透明度的渐变

3.详情页--解绑问题(一定要记住解绑,很重要)

这是一个关键点,好多时候都会因为没有解绑带来很多bug,我们在做详情页的导航时,给了一个监听鼠标滚动事件,这个是全局的监听 window.addEventListener('scroll', this.handlScroll);  这回导致其他页面在滚动时也会被监听,所以我们来接触这个监听,deactivated生命周期钩子,是在即将离开页面时执行的,一旦关闭页面,他就执行了,所以给一个移除监听事件window.removeEventListener('scroll', this.handlScroll);

activated(){
            // 相当于js中的window.onscroll
            //这里是一个全局的滚动,别的页面也会应用行,所以要解决这个问题,让他只在header滚动的时候生效,别的组件不生效
            window.addEventListener('scroll', this.handlScroll);
        },
        //当页面即将隐藏时(结束时)这个生命周期钩子执行
        deactivated(){
            window.removeEventListener('scroll', this.handlScroll);
        }

4.详情页--列表部分的递归组件

可以利用组件的名字,自己调用自己,所以上一次的自己就成了一个父组件,所以在递归的时候二级标题传进去的是  :list="list",像父组件向子组件传参那样使用

5.详情页--使用ajax获取数据

axios的两种写法,还有这里的获取路由中的id值

问题:用<keep-alive>标签,我们每次加载的页面都被缓存了下来,但是,我们的详情页有不同的id,如果这样做,查看不同id的详情页的时候就不能再发送ajax了,而是直接调缓存了所以解决办法:

   (1)像之前的跳转不同城市按钮主页那样,在activated这个生命周期钩子中,没切换一次id发一个ajax请求

   (2)App.vue中标签<keep-alive exclude="Detail"> exclude中为组件名字,表示这个组件不会缓存在内存中,所以每点一次发一次请求

6.详情页--关于vue-router的滚动问题

开发文档中有定义,每一次进入页面,页面时从哪个位置开始展示,router/index.js中设置

6.详情页--封装一个公共的动画插件---fadeAnimation

六、测试与上线

1.前后端联调

npm run start 找到 localhost:8080/#/   实际上找到的是前端服务器

启动自己的后端服务器Wamp,将之前项目static/mock/json数据放进 后端服务器根目录/api/文件夹下

配置文件config/index.js

//原来
proxyTable: {
        '/api':{
            target:'http://localhost:8080',
            pathRewrite:{
                '^/api':'/static/mock'
            }
        }
    }

//上线后改为后端服务器,然后直接寻找后端服务器的api文件即可
proxyTable: {
        '/api':{
            target:'http://localhost:80'
            //可省略成'target:'http://localhost'用http协议,用80端口,可以省略80端口
        }
    }

webpack配置项改变重新启动服务器(前端服务器)网页依旧能能请求到正确的json文件

优点:vue项目的前后端联调就不在需要fiddler这种抓包工具了

2.真机调试---用手机

在cmd中找到自己本机的ip地址

并且修改webpack配置 package.json中的内容,添加 --host 0.0.0.0

    "dev": "webpack-dev-server --host 0.0.0.0 --inline --progress --config build/webpack.dev.conf.js",

便可通过ip地址访问网页了http://10.255.118.100:8080/#/city

在同一局域网下,手机变可以访问该网页了,便会发现以下问题:

(1)在手机中我们发现一个问题,右侧字母列表不能拖动了,所以解决这个bug:Alphabet.vue文件中,给touchStart一个阻止默认行为      @touchstart.prevent="handleTouchStart"

(2)若手机浏览器不支持promise就会造成白屏的情况所以安装一个第三方包

npm install babel-polyfill --save 然后main.js中引入

3.打包上线

运行 npm run build 然后就将源代码进行打包编译,生成一个dist文件(里面有static和index.html两个文件),该dist文件夹下的代码就是要上线的代码,将这两个文件放在后端服务器根路径上就可以了

若要放到后端特定的文件夹下,例如放在根路径的project文件夹下,就要修改配置项config/index.js

build: {
    // Template for index.html
    index: path.resolve(__dirname, '../dist/index.html'),

    // Paths
    assetsRoot: path.resolve(__dirname, '../dist'),
    assetsSubDirectory: 'static',
    //修改这里
    assetsPublicPath: '/project'

在重新npm run build 然后将那两个文件放在根路径的project即可,访问后端服务器localhost/project/#/ 即可正确显示网页了

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值