vue项目记录笔记

1. 环境准备

  1. node.js  node -v
  2. npm  npm -v
  3. 码云 注册账号. 代码管理
  4. git  git --version
  5. 码云上生成公钥  https://gitee.com/help/articles/4181 
  6. 把项目克隆到本地, 进入本地目录, git clone git@gitee.com:zyzl22/newTravel.git
  7. 安装 vue
# 全局安装 vue-cli 安装过一次之后,就不用安装了
npm install --global vue-cli
# 创建一个高于 webpack 模板的新项目
vue init webpack my-project
# 安装依赖, 走你
cd my-project
npm install
npm run dev
? Target directory exists. Continue? Yes
? Project name mytravel
? Project description A Vue.js project
? Author ziyuzile
? Vue build standalone
? Install vue-router? Yes
? Use ESLint to lint your code? Yes
? Pick an ESLint preset Standard
? Set up unit tests No
? Setup e2e tests with Nightwatch? No
? Should we run `npm install` for you after the project has been created? (recommended) npm
cd my-project
npm run dev #开启服务

  8. 把本地代码提交到 码云

git add . 
git commit -m 'desc'
git push

 


2. 单文件组件与Vue中的路由

  1. 一个文件以.vue结尾的, 叫做你一个单文件组件
  2. 路由就是根据网址的不同, 返回不同的内容给用户

2.1 修改路由, route/index.js. 

  1. 删除原来的components 文件夹
  2. 新建一个pages文件夹,加入 pages/home/Home.vue, 模板如下
    <template>
    	<div>home</div>
    </template>
    
    <script>
    export default{
    	name: 'Home'
    }
    </script>
    
    <style>
    	
    </style>

     

  3. 修改路由, @代表 src这个文件夹.
    import Vue from 'vue'
    import Router from 'vue-router'
    import Home from '@/pages/home/Home'
    
    Vue.use(Router)
    
    export default new Router({
      routes: [
        {
          path: '/',
          name: 'Home',
          component: Home
        }
      ]
    })
    
  4. 如果报错, 可以修改 config/index.js => useEslint: false,

2.2 新加一个路由 /list

  1. 在 router/index.js 修改 , PS: 记得导入!
    import Vue from 'vue'
    import Router from 'vue-router'
    import Home from '@/pages/home/Home'
    import List from '@/pages/list/List'
    
    Vue.use(Router)
    
    export default new Router({
      routes: [
        {
          path: '/',
          name: 'Home',
          component: Home
        },
        {
        	path: '/list',
        	name: 'List',
        	component: List
        }
      ]
    })
    

     

  2. 在  pages 下新建 list/List.vue
    <template>
    	<div>list</div>
    </template>
    
    <script>
    export default{
    	name: 'List'
    }
    </script>
    
    <style>
    	
    </style>

     

  3. 此时, 访问 http://localhost:8080/#/list , 就可以看到 list.

3. 多页应用 VS 单页应用, vue.js是一个单页应用

    3.1 多页引用: 页面跳转->返回html

  1. 优点: 首屏时间快, SEO效果好
  2. 缺点: 页面切换慢

    3.2 单页应用: 页面跳转->JS渲染

  1. 优点: 页面切换快
  2. 缺点: 首屏时间稍慢, SEO差

4. 项目初始化

  1. 修改 index.html ,因为是手机端, 控制缩放, 和关闭双指放大缩小
    <meta name="viewport" content="width=device-width,initial-scale=1.0,minimum-scale=1.0,maximum-scale=1.0,user-scalable=no">

     

  2. 引入重置样式, reset.css, 放入 src/assets/styles/reset.css, 在 src/main.js 中引入
    import './assets/styles/reset.css'
    

     

  3. 解决1px边框的问题, 引入 border.css, 放入 src/assets/styles/border.css, 在 src/main.js 中引入
    import './assets/styles/border.css'
    
    <style lang="stylus" scoped>
    	@import '~assets/common.styl'
    	.border-zx
    		&:before
    			border-color: red
    		&:after
    			border-color: red
    </style>
    

     

  4. 解决 移动端点击有一个300毫秒延迟的bug

# 进入项目目录
npm install fastclick --save

# 安装好之后, 在/package.json 中的 dependencies 就会显示

# 然后再 src/main.js 中, 引入并操作
import fastClick from 'fastclick'
fastClick.attach(document.body)

     5. www.iconfont.cn 图标管理

     6. 上传到git仓库. 


 首页header区域开发

1. 使用 stylus 管理css 

    1.1 进入项目目录文件夹, npm install stylus@0.54.5 --save

    1.2 在安装一个  npm install stylus-loader@3.0.1 --save

    1.3 使用的时候, 在 style 加入 lang="stylus" 即可.

ps: 上面的 stylus 和 stylus-loader 直接安装会出现问题(2020年12月30日11:30:02), 这里需要指定版本号,集合解决.

 

2. 在 src/pages/home 下新建一个 components 文件夹, 把home里面的组件都放入其中

    2.1 在home/components下, 建一个 Header.vue

    2.2 在 home/Home.vue 中, 引入,再注册,再使用, 如下

<template>
	<home-header></home-header>
</template>

<script>
import HomeHeader from './components/Header'
export default{
	name: 'Home',
	components: {
		HomeHeader
	}
}
</script>

<style>
	
</style>

3. 使用 Iconfont, 下载好之后, 把除了 .html 和 .js 的文件舍弃, 其他5个文件, 拷出来.

    3.1 在 src/assets/styles 新建 iconfont 把 5个文件放入 .eot .svg .ttf .woff .woff2

    3.2 把iconfont.css 拷到 styles 中, 并修改里面的路径

    3.3 在 src/main.js 中引入 import './assets/styles/iconfont.css'

    3.4 使用 <span class="iconfont">&#xe624;</span>

    3.4 使用 css变量, 在src/assets/styles 新建 varibles.styl 文件夹, 加入 $bgColor=#00bcd4

    3.5 在 home/components/Header.vue 中 引用

<style lang="stylus">
	@import '../../../assets/styles/varibles.styl'
	.header
		display: flex
		line-height: .86rem
		background: $bgColor
//...
</style>

    3.6, 上面的 ../../../ 太长了, 之前学过 @ 可以代表src 文件夹, 这里可以换成 @ ,但是在 style 中, 使用 @ 前面需要加一个 ~, 如下

<style lang="stylus">
	@import '~@/assets/styles/varibles.styl'
</style>

    3.7 上述解决了问题, 但是还不够简洁, 因为styles 这个文件夹经常用过, 可以给他一个别名. PS: 修改了配置,需要重启服务.

# 1. 在 bulid -> webpack.base.conf中, 新加一个别名
resolve: {
  extensions: ['.js', '.vue', '.json'],
  alias: {
    'vue$': 'vue/dist/vue.esm.js',
    '@': resolve('src'),
    'styles': resolve('src/assets/styles'),
  }
},

# 使用
<style lang="stylus">
	@import '~styles/varibles.styl'
</style>

4. 首页轮播图

    4.1 新建git分支, 把代码放到分支中, 最后合并到 master

git pull # 把线上新建的分支 index-swiper 拉到本地
git checkout index-swiper 切换到分支

#... 编写代码结束

git add . #加入缓冲区
git commit -m 'description' #提交
git push 

git checkout master # 切换到主分支
git merge index-swiper 合并到主分支
git push # 提交

    4.2 做轮播要用到一个 vue-awesome-swiper 插件, https://github.com/surmon-china/vue-awesome-swiper

    4.3 安装 npm install vue-awesome-swiper@2.6.7 --save  加上一个版本号, 稳定.

    4.4 在 src/main.js 中引入. 正确引入之后就可以使用 swiper了.

import VueAwesomeSwiper from 'vue-awesome-swiper'
import 'swiper/dist/css/swiper.css'

Vue.use(VueAwesomeSwiper, /* { default global options } */)

    4.5 swiper 组件的使用方法, 可以参考githua的地址 PS:这里有一个swiper父级 height=0, 然后用padding-bottom 撑高度, 不错!

<template>
	<div class="wrapper">
		<swiper :options="swiperOption">

			<swiper-slide
				v-for="(item, index) of swiperList"
				:key="item.id"
			>
				<img class="swiper-img" :src="item.imgUrl" alt="">
		    </swiper-slide>
		    
			<div class="swiper-pagination" slot="pagination"></div>
		</swiper>
	</div>

</template>

<script>
export default{
	name: 'HomeSwiper',
	data (){
		return {
			swiperOption: {
				pagination: '.swiper-pagination',
				loop: true
			},
			swiperList: [
				{
					id: '0001',
					imgUrl: 'http://img1.qunarzz.com/piao/fusion/1801/1a/94428c6dea109402.jpg_640x200_2cf590d8.jpg'
				},
				{
					id: '0002',
					imgUrl: 'http://img1.qunarzz.com/piao/fusion/1802/42/7c92b9a381e46402.jpg_640x200_1cdce2a4.jpg'
				}
			]
		}
	}
}
</script>

<style lang="stylus" scoped>
	@import '~styles/varibles.styl'
	.wrapper >>> .swiper-pagination-bullet-active
		background: #fff 
	.wrapper
		width: 100%
		height: 0
		overflow: hidden
		padding-bottom: 31.25%
		background: #eee
		.swiper-img
			width: 100%
</style>

5 图标区域页面布局

    5.1 可以安装一个 vue.js devtools 插件, google插件, 可以很好地辅助vue.js 查看数据.

6 AJAX获取首页数据

ps: php后台解决跨域: header('Access-Control-Allow-Origin:*');

    6.1 使用 axios  模块实现ajax , 安装 npm install axios --save 

    6.2 思想: 在home这个大组件中, 用ajax获取数据, 然后赋给每个小组件, 只请求一次(感觉可以根据情况来定!)

    6.3 先引入, 然后再 mounted 钩子中使用.

    6.4 为了实现模拟ajax, 可以再static文件夹中放入json, 只有这个目录可以被外部访问到. 在static建 static/mock/index.json

    6.5 这里不需要将数据提交到 git, 在根目录 .gitignore 加入 static/mock .

    6.6 ajax 的时候, 写 'api/index.json' 可以用转发机制, 转发到 'static/mock/index.json', 在 config/index.js中

module.exports = {
  dev: {

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

    6.7 代码如下

<template>
	<div>
		<home-header></home-header>
		<home-swiper></home-swiper>
		<home-icons></home-icons>
		<home-recommend></home-recommend>
		<home-weekend></home-weekend>
	</div>
</template>

<script>
import HomeHeader from './components/Header'
import HomeSwiper from './components/Swiper'
import HomeIcons from './components/Icons'
import HomeRecommend from './components/Recommend'
import HomeWeekend from './components/Weekend'
import axios from 'axios'

export default{
	name: 'Home',
	components: {
		HomeHeader,
		HomeSwiper,
		HomeIcons,
		HomeRecommend,
		HomeWeekend
	},
	methods: {
		getHomeInfo (){
			axios.get('/api/index.json')
				.then(this.getHomeInfoSucc)
		},
		getHomeInfoSucc (res){
			console.log(res)
		}
	},
	mounted (){
		this.getHomeInfo()
	}
}
</script>

<style>
	
</style>

    6.8 父组件ajax获取的数据,传递给子组件, 第一步, ajax中赋值, 然后data中建立1个数据与之对应, 在赋值给子组件,子组件中, 用props注册下, 然后使用

data (){
	return {
		city: '',
		SwiperList : []
	}
},
methods: {
	getHomeInfo (){
		axios.get('/api/index.json')
			.then(this.getHomeInfoSucc)
	},
	getHomeInfoSucc (res){
		res = res.data
		if(res.ret && res.data){
			const data = res.data
			this.city = data.city
			this.swiperList = data.swiperList
		}
	}
},

<home-header
	:city="city"
></home-header>
<home-swiper
	:list="swiperList"
></home-swiper>

//子组件
<script>
export default{
	name: 'HomeSwiper',
	props: {
		list: Array
	},
	data (){
		return {
			swiperOption: {
				pagination: '.swiper-pagination',
				loop: true
			},
		}
	}
}
</script>

    6.9 这里有一个问题, swiper的图, 总是从最后一张开始, 这里需要在swiper加入 <swiper :options="swiperOption" v-if="list.length">. 

    6.10 在html中尽量不要使用 运算, 我们修改下, 用 computed计算属性.

<script>
export default{
	name: 'HomeSwiper',
	props: {
		list: Array
	},
	data (){
		return {
			swiperOption: {
				pagination: '.swiper-pagination',
				loop: true
			}
		}
	},
	computed :{
		showSwiper (){
			return this.list.length
		}
	}
}
</script>


<swiper :options="swiperOption" v-if="showSwiper">

总结:

  1. ajax获取值, 然后赋值
  2. 在data中建立获取到的值, 设置为空
  3. 给子组件绑定赋值
  4. 子组件 props 获取值,再使用.

    6.11 swiper组件的设置

<swiper :options="swiperOption">

data (){
	return {
		swiperOption: {
			autoplay: false
		}
	}
},

项目选择页

1. 配置路由 在 src/router/index.js 中, 新增路由

import Vue from 'vue'
import Router from 'vue-router'
import Home from '@/pages/home/Home'
import City from '@/pages/city/City'

Vue.use(Router)

export default new Router({
  routes: [
    {
      path: '/',
      name: 'Home',
      component: Home
    },
    {
      path: '/city',
      name: 'City',
      component: City
    }
  ]
})

2. 在src/pages下新建 city/City.vue, 然后再建一个 city/components 用来放 子模板.

3. 链接跳转, router-link 标签, tag="div" 表明这是一个div, to是链接.

<router-link tag="div" to="/city" class="header-right">
	{{city}}
	<span class="iconfont arrow-icon">&#xe64a;</span>
</router-link>

4. better-scroll 的使用 https://github.com/ustbhuangyi/better-scroll

  1. 进入项目目录, 安装 npm install better-scroll --save 
<template>
	<div class="list" ref="wrapper">
		<div>
			//...
		</div>
	</div>
</template>

<script>
import Bscroll from 'better-scroll'
export default{
	name: 'CityList',
	mounted (){
		this.scroll = new Bscroll(this.$refs.wrapper)
	}
}
</script>

2. 循环一个对象 <div class="area"v-for="(item, key) of cities":key="key"> 

(item, key) 这个第二个参数是key值. 这里是和数组不同的地方.  如果下面还有循环, 如下.

<div class="area"
	v-for="(item, key) of cities"
	:key="key"
>
	<div class="title border-topbottom">{{key}}</div>
	<div class="item-list">

		<div class="item border-bottom"
			v-for="(innerItem, index) of item"
			:key="innerItem.id"
		> {{innerItem.name}} </div> 
		
	</div>
</div>

5. 兄弟组件之间的联动

1. 子组件传值给父组件, 父组件再传值给第二个子组件

2. better-scroll 滑动, element 必须是一个dom结点, 所以加一个 [0].

if(this.letter){
	const element = this.$refs[this.letter][0]
	this.scroll.scrollToElement(element)
}

3. 节流

data (){
	return {
		touchStatus: false,
		startY: 0,
		timer: null
	}
},
---------------------------------------

if(this.timer){
	clearTimeout(this.timer)
}
this.timer = setTimeout( ()=>{
	const touchY = e.touches[0].clientY - 79
	const index = Math.floor((touchY - this.startY) / 20)
	if( index >= 0 && index < this.letters.length){
		this.$emit('change', this.letters[index])
	}
}, 200)

4. 滑动事件 

<template>
	<ul class="list">

		<li class="item"
			v-for="(item, index) of letters"
			:key="index"
			:ref="item"
			@touchstart="handleTouchStart"
			@touchmove="handleTouchMove"
			@touchend="handleTouchEnd"
			@click="handleLetterClick"
		>{{item}}</li>

	</ul>
</template>

<script>
export default{
	name: 'CityAlphabet',
	props: {
		cities: Object
	},
	data (){
		return {
			touchStatus: false,
			startY: 0,
			timer: null
		}
	},
	updated (){
		this.startY = this.$refs['A'][0].offsetTop
	},
	computed: {
		letters (){
			const letters = []
			for (let i in this.cities){
				letters.push(i)
			}
			return letters
		}
	},
	methods: {
		handleLetterClick (e){
			this.$emit('change', e.target.innerText)
			// console.log(e.target.innerText)
		},
		handleTouchStart (){
			this.touchStatus = true
		},
		handleTouchMove (e){
			if(this.touchStatus){

				if(this.timer){
					clearTimeout(this.timer)
				}
				this.timer = setTimeout( ()=>{
					const touchY = e.touches[0].clientY - 79
					const index = Math.floor((touchY - this.startY) / 20)
					if( index >= 0 && index < this.letters.length){
						this.$emit('change', this.letters[index])
					}
				}, 200)

				
			}
		},
		handleTouchEnd (){
			this.touchStatus = false
		}
	}
}
</script>

<style lang="stylus" scoped>
	@import '~styles/varibles.styl'
	.list
		display: flex
		flex-direction: column
		justify-content: center
		position: absolute
		right: 0
		top: 1.58rem
		bottom: 0
		width: .4rem 
		.item
			line-height: .4rem
			text-align: center
			color: $bgColor
 </style>

使用Vuex实现数据共享

1. 地址 https://vuex.vuejs.org/zh/

2. 安装 npm install vuex --save

3. 在 src 目录下, 新建 store/index.js ,按照说明使用

import Vue from 'vue'
import Vuex from 'vuex'

Vue.use(Vuex)

export default new Vuex.Store({
	state: {
		city: '北京'
	}
})

4. 然后再main.js中, 引入 import store from './store'  PS: index.js可以省略

5. 在子组件中使用  {{this.$store.state.city}} 直接使用,不用其他引用, 因为在 main.js 中, 已经引入过了

6. 改变 store 中city的值.

methods: {
	handleCityClick (city){
		this.$store.dispatch('changeCity', city)
	}
},

dispatch 中的 changeCity 对应 index.js 下的 actions . actions下的changeCity, 触发 commit, 对应 mutations下面的 changCity, 以此来改变数据. 

import Vue from 'vue'
import Vuex from 'vuex'

Vue.use(Vuex)

export default new Vuex.Store({
	state: {
		city: '上海'
	},
	actions: {
		changeCity (ctx, city){
			ctx.commit('changCity', city)
		}
	},
	mutations: {
		changCity (state, city){
			state.city = city
		}
	}
})

7. 以上的例子, 用不到 actions(异步), 可以直接用 mutations, 即是直接只用 commit 即可.

8. 点击回到首页, 利用 Vue Router https://router.vuejs.org/zh/guide/essentials/navigation.html

this.$router.push('/')

Vuex的高级用法及localStorage

1. localStorage 是h5出的新功能, 可以实现本地存储 localStorage.city = xxx, 使用的话直接用 localStorage.city.

localStorage 如果客户关闭本地存储或者开启隐身模式, 是有问题的. 所以要用 try catch, (ps: 还不如用 cookie )

import Vue from 'vue'
import Vuex from 'vuex'

Vue.use(Vuex)

let defaultCity = '上海'

try {
	if(localStorage.city){
		defaultCity = localStorage.city
	}
}catch (e){}

export default new Vuex.Store({
	state: {
		city: defaultCity
	},
	mutations: {
		changeCity (state, city){
			state.city = cityte
			try {
				localStorage.city = city 
			}catch (e){}
		}
	}
})

2. store/index.js 的代码已经越变越多, 可以拆分一下, 拆分成2个js. 然后倒入

import Vue from 'vue'
import Vuex from 'vuex'
import state from './state'
import mutations from './mutations'

Vue.use(Vuex)

export default new Vuex.Store({
	state,
	mutations
})
export default {
	changeCity(state, city) {
		state.city = city
		try {
			localStorage.city = city
		} catch (e) {}
	}
}
let defaultCity = '上海'

try {
	if (localStorage.city) {
		defaultCity = localStorage.city
	}
} catch (e) {}

export default {
	city: defaultCity
}

vuex的优化写法

1. 之前写的 vuex 是 {{this.$store.state.city}} 这样调用的. 还有一种简化写法, 先导入, 然后 映射到 计算属性中. 就可以用 {{this.city}}调用了.

<script>
import { mapState } from 'vuex'
export default{
	name: 'HomeHeader',
	computed: {
		...mapState(['city'])
	}
}
</script>


-----------------------------------

{{this.city}}

mapState, 也可以是一个对象

<script>
import { mapState } from 'vuex'
export default{
	name: 'CityList',

	computed: {
		...mapState({
			currentCity: 'city'
		})
	},

</script>
---------------------------------------
{{this.currentCity}}

2. 当改变vuex中数据的时候, 我们之前用到的是 this.$store.commit('changeCity', city) 这样的方法, vuex中也提供了一个方法, 可以直接让我们操作.

import { mapState, mapMutations  } from 'vuex'
export default{
	name: 'CityList',
	
	methods: {
		handleCityClick (city){
			// this.$store.commit('changeCity', city)
			this.changeCity(city)
			this.$router.push('/')
		},
		...mapMutations(['changeCity'])
	},
	
}

3. 当需要对原始数据进行操作的时候, 用到 vuex 中的 getters ,先在 store/index.js 中定义

import Vue from 'vue'
import Vuex from 'vuex'
import state from './state'
import mutations from './mutations'

Vue.use(Vuex)

export default new Vuex.Store({
	state,
	mutations,
	getters: {
		doubleCity (state){
			return state.city + ' ' + state.city
		}
	}
})

4. 在文件中导入

<script>
import { mapState, mapGetters } from 'vuex'
export default{
	name: 'HomeHeader',
	computed: {
		...mapState(['city']),
		...mapGetters(['doubleCity'])
	}
}
</script>
--------------------------------------------
{{this.doubleCity}}

使用 keep-alive 优化网页性能

1. 每次切换页面的时候, ajax都会重新发送, 但如果有些页面不需要第二次发送的话, 就不需要每次切换的时候, 都重新发送ajax.

2. 在 main.js中, 加入 keep-alive 标签, 页面只会在第一次打开的时候发送 ajax, 后面就算切换回来也不会发送. 

<template>
  <div id="app">
  	<keep-alive>
	    <router-view/>
  	</keep-alive>
  </div>
</template>

<script>
export default {
  name: 'App'
}
</script>

<style>
</style>

3. 但是有些页面还是需要每次都发送ajax请求的. 我们一旦用了 keep-alive来缓存我们的页面, 页面就会多了一个生命周期钩子. activated (){}

export default{
	name: 'Home',
	components: {
		HomeHeader,
		HomeSwiper,
		HomeIcons,
		HomeRecommend, 
		HomeWeekend
	},
	data (){
		return {
			lastCity: '',
			swiperList : [],
			iconList: [],
			recommendList: [],
			weekendList: []
		}
	},
	computed: {
		...mapState(['city'])
	},
	methods: {
		getHomeInfo (){
			axios.get('/api/index.json?city=' + this.city)
				.then(this.getHomeInfoSucc)
		},
		getHomeInfoSucc (res){
			res = res.data
			if(res.ret && res.data){
				const data = res.data
				this.swiperList = data.swiperList
				this.iconList = data.iconList
				this.recommendList = data.recommendList
				this.weekendList = data.weekendList
			}
		}
	},
	mounted (){
		this.lastCity = this.city
		this.getHomeInfo()
	},
	activated (){
		if(this.lastCity !== this.city){
			this.lastCity = this.city
			this.getHomeInfo()
		}
	}
}

4. 还有一个方法, 就是不用生命周期钩子, 在 App.vue中, 标明哪个不需要缓存

<template>
  <div id="app">
  	<keep-alive exclude="Detail">
	    <router-view/>
  	</keep-alive>
  </div>
</template>

 


列表跳转到内容页, 带上参数, 用 :to 动态绑定

<router-link tag="li" class="item border-bottom"
	v-for="(item, index) of recommendList"
	:key="item.id"
	:to="'/detail/' + item.id"
>

动态路由 /detail/:id 表示detail后面跟的参数是一个 id

import Vue from 'vue'
import Router from 'vue-router'
import Home from '@/pages/home/Home'
import City from '@/pages/city/City'
import Detail from '@/pages/detail/Detail'

Vue.use(Router)

export default new Router({
  routes: [
    {
      path: '/',
      name: 'Home',
      component: Home
    },
    {
      path: '/city',
      name: 'City',
      component: City
    },
    {
      path: '/detail/:id',
      name: 'Detail',
      component: Detail
    }
  ]
})

公用图片画廊组件拆分

1. 在 src下新建 common/gallary/Gallray.vue 把公共组件放在这里

2. 在 build 下的 webpack.base.conf.js 中增加一个 路径引用

resolve: {
  extensions: ['.js', '.vue', '.json'],
  alias: {
    'vue$': 'vue/dist/vue.esm.js',
    '@': resolve('src'),
    'styles': resolve('src/assets/styles'),
    'common': resolve('src/common'),
  }
}, 

3. 在需要的组件中, 引入

<script>
import CommonGallary from 'common/gallary/Gallary'
export default{
	name: 'DetailBanner',
	components: {
		CommonGallary 
	}
} 
</script>

4. 完整代码

<template>
	<div class="container" @click="handleGallaryClick">
		<div class="wrapper">
			<swiper :options="swiperOptions">

				<swiper-slide
					v-for="(item, index) of imgs"
					:key="index"
				>
					<img class="gallary-img" :src="item" alt="">
			    </swiper-slide>
			    
				<div class="swiper-pagination" slot="pagination"></div>
			</swiper>
		</div>
	</div>
</template>

<script>
export default{
	name: 'CommonGallary',
	props: {
		imgs: {
			type: Array,
			default (){
				return []
			}
		}
	},
	data (){
		return {
			swiperOptions: {
				pagination: '.swiper-pagination',
				paginationType: 'fraction',
				observeParents: true,
				observer:true
			}
		}
	},
	methods: {
		handleGallaryClick (){
			this.$emit('close')
		}
	}
}
</script>

<style lang="stylus" scoped>
	.container >>> .swiper-container
		overflow: inherit
	.container
		display: flex
		flex-direction: column
		justify-content: center
		z-index: 99
		position: fixed
		left: 0
		right: 0
		top: 0
		bottom: 0
		background: #000
		.wrapper
			height: 0
			width: 100%
			padding-bottom: 100%
			.gallary-img
				width: 100%
			.swiper-pagination
				color: #fff
				bottom: -1rem
</style>
<template>
	<div>
		<div class="banner" @click="handleBannerClick">
			<img class="banner-img" src="http://img1.qunarzz.com/sight/p0/201404/23/04b92c99462687fa1ba45c1b5ba4ad77.jpg_600x330_bf9c4904.jpg" alt="">
			<div class="banner-info">
				<div class="banner-title">sdf的算法是大水电费</div>
				<div class="banner-number"><span class="iconfont banner-icon">&#xe632;</span>35</div>
			</div>
		</div>
		<common-gallary :imgs="imgs" @close="handleGallaryClose" v-show="showGallary"></common-gallary>
	</div>
</template>

<script>
import CommonGallary from 'common/gallary/Gallary'
export default{
	name: 'DetailBanner',
	data (){
		return {
			showGallary: false,
			imgs: ['http://img1.qunarzz.com/sight/p0/201404/23/04b92c99462687fa1ba45c1b5ba4ad77.jpg_800x800_70debc93.jpg', 'http://img1.qunarzz.com/sight/p0/1709/76/7691528bc7d7ad3ca3.img.png_800x800_9ef05ee7.png']
		}
	},
	methods: {
		handleBannerClick (){
			this.showGallary = true
		},
		handleGallaryClose (){
			this.showGallary = false
		}
	},
	components: {
		CommonGallary 
	}
} 
</script>

<style lang="stylus" scoped>
	.banner
		position: relative
		overflow: hidden
		height: 0
		padding-bottom: 55%
		.banner-img
			width: 100%
		.banner-info
			display: flex
			position: absolute
			left: 0
			right: 0
			bottom: 0
			color: #fff
			line-height: .6rem
			background-image: linear-gradient(top, rgba(0,0,0,0), rgba(0,0,0, 0.8))
			.banner-title
				font-size: .32rem
				padding:0 .2rem
				flex: 1
			.banner-number
				margin-top: .2rem
				padding: 0 .4rem
				line-height: .32rem
				height: .32rem
				border-radius: .2rem
				background: rgba(0,0,0,.8)
				font-size: .24rem
				.banner-icon
					font-size: .24rem
				
</style>

对全局事件的解绑

在使用了 keep-alive 对页面进行缓存的时候, 页面的生命周期会多出来2个钩子, activated, deactivated

我们之前做的 内容页顶部忽隐忽现的时候, 用到了一个 全局 window绑定的函数, 这个时候是有问题的, 因为这样写了, 各个页面都有了这个绑定时间, 这个时候, 要在 deactivated 中给他去掉. 代码如下

activated (){
	window.addEventListener('scroll', this.handleScroll)
},
deactivated (){
	window.removeEventListener('scroll', this.handleScroll)
}

使用递归组件实现详情页列表,  详细看代码 detail/List.vue

<template>
	<div>
		<div class="item"
			v-for="(item, index) of list"
			:key="index"
		>
			<div class="item-title border-bottom">
				<span class="item-title-icon"></span>
				{{item.title}}
			</div>
			<div v-if="item.children" class="item-children">
				<detail-list :list="item.children"></detail-list>
			</div>
		</div>
	</div>
</template>

内容页请求ajax需要标注是哪一条信息, 之前也用到了动态路由, 这里要这样写, 如下

methods: {
	getDetailInfo (){
		axios.get('/api/detail.json?id=' + this.$route.params.id)
	}
},

可以优化一下

methods: {
	getDetailInfo (){
		axios.get('/api/detail.json', {
			params: {
				id: this.$route.params.id
			}
		})
	}
},

PS: 拖动一个页面,到其他页面, 这个拖动的效果还在. 需要解决这bug. 在 路由文件中, 加入

import Vue from 'vue'
import Router from 'vue-router'
import Home from '@/pages/home/Home'
import City from '@/pages/city/City'
import Detail from '@/pages/detail/Detail'

Vue.use(Router)

export default new Router({
  routes: [
    {
      path: '/',
      name: 'Home',
      component: Home
    },
    {
      path: '/city',
      name: 'City',
      component: City
    },
    {
      path: '/detail/:id',
      name: 'Detail',
      component: Detail
    }
  ],
  scrollBehavior (to, from, savedPosition){
    return { x : 0, y : 0}
  }
})

增加一个动画效果, 放在公共组件中, 原理是写一个, 然后用插槽的形式来实现

1. src/common 新建 fade/FadeAnimation.vue

<template>
	<transition>
		<slot></slot>
	</transition>
</template>

<script>
export default{
	name: 'FadeAnimation'
}
</script>

<style lang="stylus" scoped>
	.v-enter, .v-leave-to
		opacity: 0
	.v-enter-active, .v-leave-active
		transition: opacity .5s
</style>

2. 在需要的组件中 引用, 先 import , 然后再 components中注册, 再使用

<fade-animation>
	<common-gallary 
		:imgs="gallaryImgs" 
		@close="handleGallaryClose" 
		v-show="showGallary"
	></common-gallary>
</fade-animation>

vue项目的接口联调

1. 在项目前后端都做好的情况下, 我们之前前端用的是测试数据,现在要修改为后端的数据, 这就是联调. 先删除我们之前用来测试的 static/mock 文件夹

2. 到 config/index.js 中,  这个跨域很重要, 不用提交不出去.

module.exports = {
  dev: {

    // Paths
    assetsSubDirectory: 'static',
    assetsPublicPath: '/',
    proxyTable: {
        '/api' :{
            target: 'http://bd.del.com',
            changeOrigin: true,  //是否跨域
            pathRewrite: {
                '^/api' : '/api'
            }
        }
    },
}

真机联调

1. 先查看自己的ip地址, ifconfig, windows ipconfig /all

2. vue.js默认是不允许ip来访问的, 我们来修改一下 根目录下的 package.json , 加上 --host 0.0.0.0

"scripts": {
"dev": "webpack-dev-server --host 0.0.0.0 --inline --progress --config build/webpack.dev.conf.js",
"start": "npm run dev",
"lint": "eslint --ext .js,.vue src",
"build": "node build/build.js"
},

3. 这样, 手机保持和代码所在电脑, 一个局域网中, 就可以用ip来访问了 http://192.168.2.1:8080/#

4. 这里有一个bug, 城市选择页面, 往下滑的时候, 会带着顶部往下滑, 这里用一个 .prevent 来阻止默认的行为

<template>
	<ul class="list">

		<li class="item"
			v-for="(item, index) of letters"
			:key="index"
			:ref="item"
			@touchstart.prevent="handleTouchStart"
			@touchmove="handleTouchMove"
			@touchend="handleTouchEnd"
			@click="handleLetterClick"
		>{{item}}</li>

	</ul>
</template>

5. 安卓某些机型可能会白屏, 解决如下

安装一个包  npm install babel-polyfill --save

进入 main.js 入口文件 import 'babel-polyfill' 导入即可.


Vue项目的打包上线

1. 先进入项目目录 运行 npm run build, 完成之后, 会多出来一个 dist 文件夹目录.

2. 把里面的文件放到线上的根目录中即可.

3. 如果想把文件放在一个具体的目录下运行, 则需要修改. config/index.js, 写上我们的地址. 然后重新打包.

注意: /xx/ 最后还有一个 /


Vue中异步组件的使用

我们打包之后, 加载首页的时候, 会加载3个app, 其中最主要的是 app开头的js, 加载首页的时候, 会加载这个js, 这个js中放着所有页面的数据, 是没必要的. 页面小的时候, 是没有什么问题的, 但是随着项目越来越大, 这个 app开头的js会越来越大, 就会出现问题. 如果 app开头的js达到1m以上, 则需要我们用异步组件来优化下.

我们修改在 路由的写法.  写成箭头函数的形式, 这样就实现了异步加载, 需要谁就加载谁, 而不是一次性加载.

import Vue from 'vue'
import Router from 'vue-router'

Vue.use(Router)

export default new Router({
  routes: [
    {
      path: '/',
      name: 'Home',
      component: () => import('@/pages/home/Home')
    },
    {
      path: '/city',
      name: 'City',
      component:  () => import('@/pages/city/City')
    },
    {
      path: '/detail/:id',
      name: 'Detail',
      component:  () => import('@/pages/detail/Detail')
    }
  ],
  scrollBehavior (to, from, savedPosition){
    return { x : 0, y : 0}
  }
})

PS:如果 app.js 很小, 就不建议用异步, 因为用异步,每次打开新的页面都会发送一次http请求, 也是比较消耗. 下面是原来一次性的代码.

import Vue from 'vue'
import Router from 'vue-router'
import Home from '@/pages/home/Home'
import City from '@/pages/city/City'
import Detail from '@/pages/detail/Detail'

Vue.use(Router)

export default new Router({
  routes: [
    {
      path: '/',
      name: 'Home',
      component: Home
    },
    {
      path: '/city',
      name: 'City',
      component: City
    },
    {
      path: '/detail/:id',
      name: 'Detail',
      component: Detail
    }
  ],
  scrollBehavior (to, from, savedPosition){
    return { x : 0, y : 0}
  }
})

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值