vue项目记录笔记

HTML特殊字符编码对照表 HTML特殊字符编码对照表(特殊符号及其命名实体,十进制编码),写html代码碰到特殊字符时有用,摘要必须大于50个字!!! 立即下载

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}
  }
})

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Cursor Agent模式下如何优雅切换conda环境运行Python脚本?3种实用方法实测 本文针对Cursor Agent模式下运行Python脚本时环境错配的痛点,实测了三种优雅切换conda环境的实用方法。通过Shell全局配置、项目级.cursorrc文件定制以及对话中直接指令,开发者可以确保AI助手在正确的虚拟环境中执行代码,从而提升开发效率和协作流畅度。 阅读详情

相关推荐

从零到一:手把手教你用STM32和WiFi打造智能温室大棚的物联网核心

本文详细介绍了如何利用STM32单片机和WiFi技术从零构建智能温室大棚的物联网核心系统。内容涵盖硬件选型、传感器集成、低功耗WiFi通信、自动控制逻辑及OLED显示,提供实际部署中的优化技巧和故障排查方案,帮助开发者打造稳定可靠的农业自动化解决方案。

c7d8e的博客 179

vue路由

vue路由的使用:path加 /:参数,如:/update/:id 带参数使用this.$route.params.参数获取

LUOZONGW的博客 403

集成calibre到cadence virtuoso环境中 load: can‘t access file - “/opt/eda/Mentor/lib/calibre.skl“

项目场景: 设想是将Calibre集成到cadence virtuoso中,但网上的解决办法尝试过在本机上实验都有点问题,因此想记录下来我的解决办法仅供参考。 问题描述: 安装好cadence, calibre之后打开virtuoso,CIW窗口显示 *Error* load: can't access file - "/opt/eda/Mentor/lib/calibre.skl" 问题是出在calibre.skl的加载路径出错,尝试网上的在工程目录下修改.cdsinit文件,添加calibre安装

weixin_52714276的博客 1万+

Vue路由,关于地址栏没有问题,router-view是一片空白

首先看源代码 目录结构如下: App.vue <template> <div id="app"> <router-view></router-view> </div> </template> ...

qq_41893686的博客 2105

Transformer在CV里太耗算力?手把手拆解Restormer的MDTA与GDFN模块

本文深度解析了CVPR2022最佳论文提名作品Restormer的创新模块MDTA与GDFN,有效解决了Transformer在计算机视觉领域的高计算复杂度问题。通过通道注意力降维和门控前馈网络设计,Restormer在图像恢复重建任务中显著降低计算量,同时保持高性能,为实时图像处理提供了可行方案。

weixin_30258901的博客 445

基于Python实现的捕鱼达人小游戏源代码+使用说明

摘要: 本文介绍了一款自制的捕鱼达人小游戏,使用Python3和Pygame模块开发。游戏包含完整的资源文件(图片、音效、字体)和源码结构(11个功能模块),实现了大炮移动射击、特殊子弹、炸弹、难度递增等核心玩法。安装需配置Python环境和Pygame模块,运行main.py即可启动游戏。游戏支持键盘控制(方向键移动、空格射击、1键放炸弹),默认时长600秒,包含开始菜单、游戏界面和结束界面。所有代码均有详细注释,项目已开源并提供下载链接。(150字)

2301_76484015的博客 666

vue学习---前端路由

我们通过vue-cil脚手架工具创建的项目是单页面应用,我们把单页面应用称为single page app 简称spa,单页面就是只有一个html代码 Vue.js 路由允许我们通过不同的 URL 访问不同的内容(不同的组件), 实现单页面应用的多页面切换效果。(通过url的改变切换不同的组件) Vue.js 路由需要载入 vue-router 库.vue-router 库是独立vue之外的,可以有也可以没有,若要实现不同的 URL 访问不同的内容(不同的组件)可以用vue-router,如果不用vue

茹嗨瑞的博客 1122

osgearth2.10 demo功能介绍

osgearth-2.10 demo: 说明: 1、运行环境win7 x64 旗舰版,CPU i5-6400 8核,8G内存; 2、括号内是对应demo的测试参数(.earth); 3、配置OSG_FILE_PATH; 4、参数命令行可以查看各个demo需要的输入参数; 5、前缀说明:gui界面程序、cmd控制台程序、non功能未知; 6、程序后括号里的non表示不需要通过命令行输入参数; 示例: 1、gui:osgearth_3pv(simple.earth):相机与地球的投影关系,Range表示相机

rach09的专栏 2421

很轻松的vue路由

Vue路由 Vue Router 是Vue.js官方的路由管理器。它和 Vue.js 的核心深度集成,让构建单页面应用变得易如反掌。路由实际上就是可以理解为指向,就是我在页面上点击一个按钮需要跳转到对应的页面,这就是路由跳转; router-link 来引入路由 <router-link to="/about"> router-view 来获取路由 <router-view class="view has-tabs"/> router/index.js..

weixin_67226535的博客 340

复现《水化硅铝酸钙微结构和性能的分子动力学模拟研究》的分子动力学模拟

本文详细复现了杨军博士论文中关于水化硅铝酸钙(C-A-S-H)凝胶的分子动力学模拟方法。研究内容包括:1)基于11Å Tobermorite构建C-A-S-H分子模型,通过缺陷引入和Al掺杂调节Ca/Si比;2)使用ReaxFF力场模拟交联C-A-S-H的结构与力学性能;3)采用ClayFF力场研究纳米孔道中水和离子传输行为。研究提供了完整的LAMMPS输入脚本、Python分析代码和力场参数设置,为水泥基材料纳米尺度模拟提供可复现的技术方案。

huanghm88的专栏 105

传感器应用案例:压力传感器在工业自动化中的应用_(13).压力传感器的数据采集与处理

数据采集系统(Data Acquisition System, DAS)是连接传感器和控制系统的重要桥梁。它包括硬件(如数据采集卡、信号调理器)和软件(如数据采集软件、数据处理算法)两部分。信号调理:将传感器输出的模拟信号转换为适合AD转换的信号。AD转换:将模拟信号转换为数字信号。数据传输:将数字信号传输到控制系统或上位机。存储:将采集的数据存储在本地或远程数据库中。实时性:保证数据的实时性和准确性。压力传感器的数据采集与处理是工业自动化中不可或缺的一部分。

2401_87715305的博客 1620

记录Vue3的项目搭建笔记

/ 在main.js中引用ElementPlus。3.安装element-plus。

qq_44549422的博客 144

12、控件使用之录入英文、阿拉伯数字删除增加功能及二维码显示

控件使用之录入英文、阿拉伯数字删除增加功能及二维码显示1、用到的控件2、添加控件2.1、添加【文本录入】控件2.1、添加【文本显示】控件2.1、添加【QR 二维码】控件3、保存工程及生成文件4、下载文件 1、用到的控件   需要用的显示控件为【文本显示】控件和【QR二维码】控件,以及触控控件【文本录入】控件。 2、添加控件 2.1、添加【文本录入】控件   【文本录入】控件也叫【ASCI 文本录入】,是用来录入ASCI 字符,打开 DGUS 软件,点击【触控控件】—【文本录入】,用鼠标框选一个区域,即可在右

嵌入式羊圈 625

笔记记录分享网站 Vue.js+SpringBoot

这是一个基于SpringBoot+Vue的笔记分享网站项目,分为用户端和管理端。用户端提供笔记查看、商品购买、个人中心等功能;管理端包含用户管理、笔记审核、商品管理等后台功能。项目采用RESTful API进行前后端交互,开发环境为JDK1.8+Maven3.6.0+MySQL5.7。该项目虽然在实际工作中较少应用,但适合作为编程入门学习案例,包含完整的源码和截图,可供初学者参考学习SpringBoot和Vue的基础开发流程。

wanglindda的博客 350

计算机控制技术及应用

《计算机控制技术及应用》通过总结作者的科研成果和吸收国内外的先进理论、方法和技术,以计算机控制系统的网络化、开放化、智能化和集成化发展趋势为主线索,主要介绍计算机控制系统的基本概念和计算机控制系统的典型形式,计算机控制系统的数学描述与基本分析方法,以及数字PID控制器、最少拍控制系统、达林算法、状态变量设计法等设计方法及其各种改进算法。重点突出计算机控制系统输入输出通道接口的设计方法和典型控制系统的设计案例与实现方法等。

温度PID控制PLC程序

温度PID稳定控制程序,适用于欧姆龙PLC,对其他PLC也具有很大参考价值

SpringBoot+Vue 笔记记录分享网站平台完整项目源码+SQL脚本+接口文档【Java Web毕设】

直接拿走,意外获得200多套代码,需要的滴我SpringBoot+Vue 笔记记录分享网站平台完整项目源码+SQL脚本+接口文档【Java Web毕设】(可提供说明文档(通过*AIGC*)

yunque__的博客 644

迈瑞BS系列全自动生化分析仪LIS协议接口手册(内含HL7和ASTM)

迈瑞BS系列全自动生化分析仪LIS协议接口手册,内含HL7和ASTM)

vue笔记记录(一) —— 搭建项目

利用webpack脚手架搭建vue项目 npm install -g vue-cli vue init webpack my-project cd my-project npm install npm run dev 至此,一个简单的vue项目就搭建好了。 大致目录结构如下: . ├── build/ # webpack config fi...

杨阳洋的博客 303

华为的C语言编程规范.pdf

华为技术有限公司内部技术规范。本规范制定了编写C语言程序的基本原则、规则和建议。从代码的清晰、简洁、可测试、安全、程序效率、可移植各个方面对C语言编程作出了具体指导。

Vue3企业级项目开发实战课-笔记记录

实践 Vue 3 自研组件库开发、Node.js 服务端开发、Vue 3 结合 Node.js 的全栈项目开发以及全栈项目的单元测试等大厂常见的项目技术场景

tuku0801的专栏 1988

使用node爬虫做了一个vue小项目记录使用笔记

vue-backend-system github地址 一直想把以前所学的相关知识进行总结归纳,方便以后查看复习,正好最近最近抽时间研究点东西,打算是做前后台的,对后台这块不熟悉,本来是打算用node加mongodb实现,数据自己弄的不是很清楚,就干脆爬下自己的博客,主要是研究下前后台一起结合着来做的整个流程。 步骤 下载项目 git c...

weixin_34306676的博客 476

Django-Vue3-Admin 学习笔记(一) 项目后端部署记录

笔者偶然听说 Django-Vue3-Admin 是个不错的选择,所以选择这个框架进行开发。然而由于官方文档更新不及时,存在版本不同问题,所以笔者部署时遇到了不少的问题,在这里我将把我遇到的问题详细列出来,同时给予正确的答案,帮助大家更好地学习使用这个新框架。本节为Django后端部分。

qq_39838545的博客 4188

今天尝试启动别人的vue项目,做个笔记记录问题及解决方法

学习vue的笔记

qq_63170044的博客 285

Vue的初始化项目文件搭建笔记 ----每一步都记录下来了

vue 的初始化文件操作 这个项目可能用到的技术: 用的技术: vue, vue-router, axios, token, localStorage, ElementUI, echarts, 富文本编辑器 项目初始化的目标: 创建项目(vue-cli) 调整项目目录(遵循企业开发) git 代码托管 (github, gitee) 引入第三方库 ElementUI 封装请求模块 让我们开始使用vue-cli创建项目 创建项目命令 :vue create 项目名 接下来会弹出来3个选项

weixin_48383564的博客 1251

【Vue项目复习笔记】Home离开时记录状态和位置

这就意味着回到首页,首页还需要重新被创建,但是我们不希望它被重新创建,所以我们就让其不要随意销毁。我们会发现,它并没有记住home离开时的位置,我们在home里的新建destroryed()在scroll.vue中包装一个getScroll Y()但是结果仍然与我们预期有差别,它并没有保留下来这个状态。在我们的App.vue中加入keep-alive。我们发现一旦离开首页,首页就会被销毁。在data()里面默认一个。在Home.vue中。...

qq_40992225的博客 305

nginx 配置多个vue项目(nginx下多个项目 根据/path 找到对应项目)笔记记录

nginx根据/path 配置多个项目

weixin_38069018的博客 453
上一篇: git添加到码云
下一篇: tp5杂技
自娱自乐22
博客等级 码龄18年 241粉丝 135原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值