2021-05-29

2021-05-29  本文已影响0人  Chin是我啊

记一次实战经历<去哪儿网> vue-cli 2

预备知识

1.Git命令:

git add .  放到缓冲区

git commit -m 把缓冲区代码提交到本地仓库中

git push 推到远程仓库

git checkout branchName 切换到某分支

git merge origin/index-swiper 将分支合并到主分支

2.在style中引入css文件

如果需要在style中引入其他的css 文件,则需要使用@import 'path'

如果要用到@(也就是src),则需要在@前面加~,也就是~@

3.起别名

在build/webpack.base.config.js中resolve的alias修改,修改以后需要重启服务器

4.开发者工具可以模拟3G网络

network -> no throtting

5.宽高比一定

两种办法  1.height:0  padding-bottom: 50%  表示占宽度的50% 标准写法

width:100%  height:50%vw  视口宽度的50%  存在兼容性问题

6.使用axios

【补充】:cdn引入axios <script src="https://cdn.jsdelivr.net/npm/axios@0.12.0/dist/axios.min.js"></script>

import axios from 'axios'

在声明周期钩子函数中调用函数

methods: {

getCityInfo() {

axios.get('/api/city.json')

.then(this.handleGetCityInfoSucc)

   },

handleGetCityInfoSucc(res) {

res=res.data

if(res.ret&&res.data){

constdata=res.data

this.cities=data.cities

this.hotCities=data.hotCities

     }

   }

}

7.监听器watch

watch和data、methods、components是并列的,对应的是一个对象类型,对象中是函数,这些函数用于监听数据的改变。函数名就是变量名

watch:{

question([newValue,oldValue]){

//code

   }

}

vue官网侦听器

8.indexOf()

string.indexOf(searchvalue[,formindex])该方法用来判断字符串中是否含有特定子字符串。第一个参数是要查找的内容,第二个字符串是要开始查找的索引号,如果没有第二个参数默认从0开始。如果没有找到,返回-1

<scripttype="text/javascript">

varstr="Hello world!"

document.write(str.indexOf("Hello")+"<br />")

document.write(str.indexOf("World")+"<br />")

document.write(str.indexOf("world"))

</script>

以上代码的输出:

0

-1

6

js indexOf()用法

9.vuex

需要进一步学习,了解的比较少

state mutations mapState...

10.遍历对象和遍历数组

用v-for遍历数组,第一个参数是元素,第二个参数是索引。

遍历对象中的对象,第一个是对象本身,第二个参数是对象名,用let in 参数是对象名

项目预热

安装node.js -> LTS  长期维护版,稳定

github

安装Vue CLI脚手架,创建项目 vue init webpack my-project

项目概述

项目结构

纠正:static可以存放静态文件,是外界可以直接访问的目录。

重点介绍src目录,我们写的代码都保存在这个目录下assets是一些共用的静态资源,common放的是公共组件,pages放组件,router下面是路由配置文件,store下面是公共数据文件(vuex相关),App.vue是跟实例,main.js是整个项目的入口。

项目初始化

修改meta标签,禁止用户通过手指缩放(不报错表明引用正确)

index.html

<metaname="viewport"content="width=device-width,initial-scale=1.0,minimum-scale=1.0,maximum-scale=1.0,user-scalable=no">

引入reset.css(重置样式表)  normalize修改程度比较小  reset.css对默认样式破坏性比较多main.js

引入border.css  解决在某些手机上border宽度不一的情况

解决某些手机存在300毫秒延迟问题

npm install fastclick --save  //安装

import fastClick from 'fastclick' //导入

fastClick.attach(document.body)  //使用

删除无用代码,提交本地仓库

首页开发

header区域开发

1.安装stylus(可选)

npm install stylus --save

npm install stylus-loader --save

2.创建header组件

3.安装less(好用)

npm install less less-loader --save-dev

修改build/webpack.base.config.js

{

test:/\.less$/i,

loader: [

// compiles Less to CSS

"style-loader",

"css-loader",

"less-loader",

       ],

     }

报错是版本原因,要安装低版本

npm uninstall less-loader  //安装less-loader

npm install less-loader@4.1.0  //指定版本

解决header区域城市自动切换到指定城市

在Home组件中创建一个变量lastCity表示最新当前城市,当路由跳转到当前页面时,让lastCity等于vuex中的city,lastCIty在结构中并没有被用到,他的作用即使监测当前页面被激活时,检查城市是否发生变化,从而决定是否重新发送请求。

在组件Header.vue中我们直接使用vuex中的city。

在computed中

computed: {

...mapState(['city'])

}

就可以直接在当前页面中使用this.city(vuex中的数据)。

当从其他页面跳转到当前页面时,激发activated()钩子函数,检查lastCity是否和vuex中的city一样,如果不一样就改变,并重新发送网络请求。

activated() {

if(this.lastCity !== this.city){

this.lastCity = this.city

this.getHomeInfo()

}

}

methods: {

getHomeInfo() {

axios.get('./api/index.json')

.then(this.getHomeInfoSucc)

},

getHomeInfoSucc(res) {

res = res.data

const data = res.data

if(res.ret && res.data){

this.swiperList = data.swiperList

this.iconList = data.iconList

this.recommendList = data.recommendList

this.weekendList = data.weekendList

}

}

}

由于本项目只含有北京的数据,我们默认请求数据的时候不传参。

Q:说了这么多?我们在什么时候需要用到上述功能呢?

A:当我们点击城市选择页面城市的时候

@click='handleCityClick(innerItem.name)

methods: {

handleCityClick(city){

this.$store.commit('changeCity',city)

this.$router.push('/')

}

}

触发mutations.js中的changeCity()函数,city作为第二个参数

mutations.js:

localStorage是为了解决关闭页面以后再次打开页面的问题,这样可以直接打开直接的页面。

changeCity(state, city) {

state.city = city;

try {

localStorage.city = city;

} catch (e) {}

}

state.js

let defaultCity = "北京";

try {

if (localStorage.city) {

defaultCity = localStorage.city;

}

} catch (e) {}

export default {

city: defaultCity

};

首页轮播图开发

线上建立分支index-swiper

拉取到本地  git pull

进入分支  git checkout index-swiper

安装swiper npm install vue-awesome-swiper@2.6.7 --save这里使用v2.6.7老版本,可以在GitHub上找到具体使用的代码

轮播图的核心结构就是swiper和swiper-slide

解决图片区域抖动问题

给图片区域高度写死

vue中css穿透

在使用vue-awesome-swiper时,swiper组件并不属于当前组件,而我们又写了scoped,因此需要用到css穿透。

less和sass无法识别>>>,因此要用到/deep/或者::v-deep  stylus可以识别>>>

.swiper /deep/ .swiper-pagination-bullet-active

//.wrapper >>> .swiper-pagination-bullet-active

{

background-color: #fff !important;

}

从百度到CSDN,最后到Vue.js官网。

vue官网深度作用选择器 

不得不说,官方文档

实现文字省略功能

overflow: hidden;

white-space: nowrap;

text-overflow: ellipsis;

实现图标分页效果

我们可以再次借助轮播图,这次关闭自动播放功能。

因为我们是动态渲染从后端请求过来的数据,因此要实现自动分页,要用到二维数组。

computed: {

pages() {

const pages = []

this.list.forEach((item, index) => {

const page = Math.floor(index / 8)

if(!pages[page])

pages[page] = []

pages[page].push(item)  //二维数组,自动分页

})

return pages

}

}

放到计算属性中,计算属性的成员都是函数(返回一个数据),但是在使用的时候直接我们当成属性来使用,因此命名一般是名词。

动态渲染

v-for="item of page" :key="item.id">

:src="item.imgUrl"

/>

{{item.desc}}

城市页面开发

创建分支city-router

在路由文件router/index.js中配置路由

router-link把内容包裹起来,router-link实质就是a标签。  to="path"表示跳转的路径

a标签优先级高,因为浏览器对a标签有默认样式,所以a标签不会继承父类的样式

头部开发

position: absolute;

top: 0;

left: 0;

头部搜索框

input输入框双向绑定keyword来决定是否显示搜索结果,通过搜索结果的长度来决定没有匹配到数据是否显示。计算属性:hasNoData

局部导入Bscroll

import Bscroll from 'better-scroll'

mounted() {

this.scroll = new Bscroll(this.$refs.search)

}

通过ref来绑定DOM

样式

position: absolute;

top: 1.58rem;

left: 0;

right: 0;

bottom: 0;

overflow: hidden;

搜索结果的算法(用到了节流)

watch: {

keyword() {

if(this.timer){

clearTimeout(this.timer)

}

if(!this.keyword){

this.list = []

return

} //这里解决的是删除搜索框中的内容以后,使搜索结果消失

this.timer = setTimeout(() => {

const result = []

for (let i in this.cities){

//从A到Z,再逐个遍历

this.cities[i].forEach( value => {

//英文名和汉字都检查,只要有一个符合就加入result

if(value.spell.indexOf(this.keyword) > -1 ||

value.name.indexOf(this.keyword) > -1){

result.push(value)

}

});

}

this.list = result

},100)

}

}

列表布局

分支city-list

最外层容器的样式:top,left,right,bottom都为0  overflow:hidden

better-scroll

npm install better-scroll --save

按照固定的结构来书写

用ref来获取DOM

import Bscroll from 'better-scroll'

this.scroll = new Bscroll(this.$refs.wrapper)

页面可以丝滑地滚动

页面的动态渲染

分支city-ajax

兄弟组件间联动

点击Alphabet组件中的字母,list组件通过事件对象将字母传给父组件city

handleLetterClick(e) {

//e是事件对象

this.$emit('change',e.target.innerText)

}

city再传给list,LIst组件定位到对应的DOM

利用better-scroll的功能自动滚到某个区域中,通过href获取dom

watch: {

letter() {

if (this.letter) {

const element = this.$refs[this.letter][0]  //为什么获取到的是数组?因为ref为该字母的可能有很多

//如果属性名是一个变量,可以这样写obj[variable]

this.scroll.scrollToElement(element);

//定位到该DOM

}

},

}

实现字母侧边栏连续触摸时滚动到对应区域

给字母绑定触摸事件

v-for="item of letters"

:key="item"

:ref="item"

@touchstart.prevent = 'handleTouchStart'

@touchmove = 'handleTouchMove'

@touchend = 'handleTouchEnd'

@click = handleLetterClick>

{{item}}

methods: {

handleLetterClick(e) {

//e是事件对象

this.$emit('change',e.target.innerText)

},

handleTouchStart(){

this.touchStatus = true

},

handleTouchMove(e){

if(this.touchStatus){

if(this.timer){

clearTimeout(this.timer);

}

this.timer = setTimeout(() => {

console.log(e.touches[0].clientY);

const touchY = e.touches[0].clientY - 79;    //touch到的字母距离当前盒子顶部的距离

const index = Math.floor((touchY - this.startY)/20);

if(index >= 0 && index < this.letters.length){

this.$emit('change',this.letters[index]);

}

},16)

}

},

handleTouchEnd(){

this.touchStatus = false

}

}

性能优化

startY放到updated()钩子函数中

当页面数据更新完成

节流

添加定时器

详情页面开发

实现点击banner画廊出现,点击画廊banner出现

画廊作为banner的一个组件,点击banner时showGalleay变为true,点击画廊时触发自定义事件改变showGallary的值。

为banner添加底部阴影

渐变

background-image: linear-gradient(top,rgba(0,0,0,0),rgba(0,0.0,.8));

Header渐变

通过动态绑定style实现渐变

class="header-fixed"

v-show="!showAbs"

:style="opacityStyle">

景点详情

methods: {

handleScroll() {

const top = document.documentElement.scrollTop

if(top > 60){

let opacity = top / 140

opacity = opacity > 1 ? 1 : opacity

this.opacityStyle = {

opacity

}

this.showAbs = false

}

else this.showAbs = true

}

},

// activated() {

//  window.addEventListener('scroll',this.handleScroll)

// },

// deactivated() {

//  window.removeEventListener('scroll',this.handleScroll)

// }

mounted () {

window.addEventListener('scroll', this.handleScroll)

},

unmounted () {

window.removeEventListener('scroll', this.handleScroll)

}

}

开启全局监听以后,同时要开启unmounted,否则会在其他页面一直保持监听。

疑点:为什么activated不行

结语:这个项目是我完整做的第一个Vue项目,做下来真的收获很大,对一个完整的项目有了大概的了解。

项目使用vue + vue-router + vuex全家桶,涉及到逻辑方面的内容也有很多。包括但不限于使用vue,还有vue-cli,Git,其他插件的使用,接触到以前听说过但是不知道是什么的名词(例如节流和防抖),在遇到不懂的地方的时候通过各个社区寻求答案,学会使用专业文档(利于vue官网)。

感谢老师

上一篇下一篇

猜你喜欢

热点阅读