使用了vue,vue-route, vuex, vue-awesome-swiper, babel-polyfill, fastclick, iconfont,等最新的前端技术进行开发制作。
webpack进行编译
说明都在文档中文档结构为md语法,网页无法显示目录结构,项目源码就不放了。
项目说明文档
开发之前安装的东西
node.js运行环境
官网:https://nodejs.org/zh-cn/,下载安装长期支持版,直接下一步就行,安装完成之后使用 node -v命令和 npm -v命令参看版本检查是否可用。
码云和git
目前用不到,跳过,可以同步代码,保持线上和线下相同
vue-cli
vue官方提供的脚手架工具
官方地址:https://cli.vuejs.org/
安装1:npm install -g @vue/cli
初始化项目:vue init webpack Travel,如果文件夹已经存在,选择Y,根据提示进行下一步
项目文件介绍
项目文件夹根目录
README.md 项目的说明文件
package.json 项目的依赖包,项目开发所依赖的第三方开发包都包含在里面
package-lock.json package.json的锁文件,可以确定已经安装第三方包的版本
LICENSE 开源协议的说明
index.html 项目默认的首页文件
static 存放静态的资源,比如图片,或者模拟json数据
node_modules 依赖包
src目录
src 整个项目的源代码
src/main.js 整个项目的入口文件
src/App.vue 项目最原始的根组件
src/rounter/index.js 项目所有的路由
src/components/ 存放项目需要用到的组件
src/assets/ 项目需要用到的图片资源
config目录
config/ 存放项目的配置文件
config/index.js 存放基础的配置信息
config/dev.env.js 开发环境的配置信息
config/prod.env.js 线上环境的配置信息
build目录
build/ 存放webpack打包的内容。vue-cli自动构建,一般不用修改
build/webpack.base.conf.js 基础的配置项
build/webpack.dev.conf.js 开发环境的配置项
build/webpack.prod.conf.js 线上环境的配置项
单文件组件与vue中的路由
单文件组件
当一个文件以vue结尾的时候叫做单文件组件,里面放的是一个vue的组件
路由
根据网址的不同,返回不同的数据给用户,这就是路由的功能
项目初始化
设置屏幕大小禁止缩放
<meta name="viewport" content="width=device-width,initial-scale=1.0,minimum-scale=1.0,maximum-scale=1.0,user-scalable=no">
引入两个css初始效果的文件
reset.css 设置每个设备显示的效果是一样的
border.css 解决设置边框多倍屏显示不正常的问题,比如设置了1px的边框,在二倍屏显示为2px
300ms延迟问题
在某些设备使用click事件会出现click延迟300ms执行,
在项目文件夹下使用npm install fastclick --save
进行安装fastclick,安装完成之后在main.js中进行配置
fastClick.attach(document.body)
iconfont字体图标
主要是用到了阿里巴巴的iconfont网站,在网站中新建一个项目
样式辅助工具Stylus
npm install stylus --save
npm install stylus-loader --save
项目安装依赖
<style lang="stylus" scoped>
</style>
在组件中说明使用stylus进行开发
在css中引入stylus全部变量
<style lang="stylus" scoped>
@import '../../../assets/styles/varibles.styl'
</style>
路径太长可以通过@符号引入,js可以直接使用@,css中需要添加~@
<style lang="stylus" scoped>
@import '~@/assets/styles/varibles.styl'
</style>
头部区域开发
vue-awesome-swiper轮播图
来源可以在github上搜索vue-awesome-swiper
npm install [email protected] --save
安装vue-awesome-swiper
在js数组中配置图片静态路径的时候需要require
需求:如何components里面的index.vue怎样能把assets里面的图片拿出来。
1.在img标签里面直接写上路径:
<img src="../assets/a1.png" class="" width="100%"/>
2.利用数组保存再循环输出:
<el-carousel-item v-for="item in carouselData" :key="item.id">
<img :src="item.url" class="carouselImg"/>
<span class="carouselSpan">{{ item.title }}</span>
</el-carousel-item>
data: () => ({
carouselData:[
{url:require('../assets/a1.png'),title:'你看我叼吗1',id:1},
{url:require('../assets/a3.png'),title:'你看我叼吗2',id:2},
{url:require('../assets/a4.png'),title:'你看我叼吗3',id:3}
]
}),
问题解决Unable to preventDefault inside passive event listener
具体解决链接:[解决地址](https://segmentfault.com/a/1190000008512184)
两个方案:
1、注册处理函数时,用如下方式,明确声明为不是被动的
window.addEventListener('touchmove', func, { passive: false })
2、应用 CSS 属性 touch-action: none; 这样任何触摸事件都不会产生默认行为,但是 touch 事件照样触发。
touch-action 还有很多选项,详细请参考touch-action
[注]未来可能所有的元素的 touchstart touchmove 事件处理函数都会默认为 passive: true
轮播图问题
点击拖动无法继续轮播api同swiper,后期查询修复
ajax请求首页数据
axios的使用
安装axios,npm install axios --save
由于多个组件需要使用ajax发送数据,将发送放在home.vue文件中发送
methods: {
getHomeInfo () {
axios.get('./api/index.json')
.then(this.getHomeInfoSucc)
},
getHomeInfoSucc (res) {
console.log(res)
}
},
mounted () {
this.getHomeInfo()
}
请求的代理
在没有后端的情况下,请求的文件放在本地,发布上线不建议修改。
需要webpack下的dev-server提供的请求代理功能
在项目目录下的config目录下面的index.js文件中proxyTable配置代理
比如请求的地址是
axios.get('./api/index.json')
配置文件中填写
proxyTable: {
'./api':{
target: 'http://localhost:8080',
pathRewrite: {
'^/api': './static/mock'
}
}
},
图片的显示
由于之前的图片放在asset目录下,在外部请求json文件中的图片地址无法显示,因为无法访问
vue-cli 目录中,只有static 目录放置的静态文件可以被外部访问。如果想在JSON(static/mock/index.json) 中引入图片地址,必须把图片放在static 目录下。然后JSON 中的图片地址,是你的vue 组件引入图片的地址。我是在src/component/home/home.vue中引入 static/img/1.jpg 图片,图片地址为:"../../../static/img/1.jpg"。 在JSON 中的地址也要这样写。如果配置JSON 时不确定图片地址,可以在你对应的VUE组件中 直接 引入一张图片测试下地址,然后再放在JSON 中。
问题待解决
在使用webpack-dev-servser的过程中,配置了本地代理,发现并不是很好用,
时不时会报504的错误,需要多次刷新才可以正确访问到代理的位置,判断是webpack代理的性能问题
![控制台报错截图](说明_files/1.jpg)
ajax获取轮播图默认显示最后一张
在显示的滑块加上判断,v-if判断数组是否有内容,如果还没请求是不会创建滑块的。因为list
创建时默认为空,v-if为false不会渲染
<swiper :options="swiperOption" v-if="list.length">
<!-- slides -->
<swiper-slide v-for="item of list" :key="item.id">
<img class="swiper-img" :src="item.imgUrl" />
</swiper-slide>
<!-- Optional controls -->
<div class="swiper-pagination" slot="pagination"></div>
</swiper>
建议:使用计算属性来决定是否显示轮播图
<swiper :options="swiperOption" v-if="showSwiper">
<!-- slides -->
<swiper-slide v-for="item of list" :key="item.id">
<img class="swiper-img" :src="item.imgUrl" />
</swiper-slide>
<!-- Optional controls -->
<div class="swiper-pagination" slot="pagination"></div>
</swiper>
computed: {
showSwiper () {
return this.list.length
}
}
选择地址页面的开发
滑动使用better-scroll
具体使用方法等待后期学习,作用是在浏览器中模拟手机的滑动给人更好的体验效果
兄弟组件之间的联动
通过绑定触摸事件来出发触摸事件,计算高度的方式返回触摸的字母,传递给父组件,
父组件传递给list组件进行页面更新
触摸函数优化
在执行触摸函数的时候函数执行的程度非常频繁,设置定时器减少函数的执行次数提高性能
阻止默认行为
在选择地址的时候滑动字母页面会跟着滚动,在touchstart后面加上prevent阻止默认行为
@touchstart.prevent="handleTouchStart"
选择地址搜索功能的开发
主要技术
使用侦听器,侦听输入数据的改变,触发方法计算父组件传递过来的城市列表
展示相应的数据
首页和选择地址界面的数据共享
采用vuex进行非父子组件的传值
实际开发中一般不采用bus总线的形式进行非父子组件的传值
采用vue官方提供的vuex进行非父子组件中的传值,
vuex介绍地址 [https://vuex.vuejs.org/zh/](https://vuex.vuejs.org/zh/)
state相当与数据创库,组件通过调用api去改变仓库里面的值,
使用仓库数据的组件数据自然也就更新了
介绍图片![](说明_files/1.png)
Vuex的使用
在src目录下新建一个store文件夹,新建index.js文件
创建仓库导出公用变量
在main.js创建根实例的时候传递store,vuex会派发到每个组件里面
子组件通过插值表达式{{ this.$store.state.city }}使用仓库里面的数据
修改仓库里面的数据
给按钮绑定点击事件,点击按钮触发this.$store.dispatch('changeCity', city)
在index.js中actions中定义changeCity方法,参数一: 上下文对象 参数二:传递过来的城市的值
使用上下文对象触发commit方法,调用mutations里面的ChangeCity方法改变store里面的值
city: '北京'
},
actions: {
changeCity (ctx, city) {
ctx.commit('changeCity', city)
}
},
mutations: {
changeCity (state, city) {
state.city = city
}
}
如果没有异步操作,操作非常简单,组件可以直接调用commit方法改变store里面的数据
修改过后存储到本地
使用localStorage,需要解决用户关闭了本地存储,或者隐身的bug,否则项目无法正常运行
localStorage.city || '北京'
优先从本地取,取不到使用默认,
处理关闭本地存储功能
localStorage.city = city
} catch (e) {
}
city: localStorage.city || '北京'
},
mutations: {
changeCity (state, city) {
state.city = city
localStorage.city = city
}
}
### 拆分store里面的组件
方便后期维护,对state里面的代码进行拆分
拆分成为多个js文件
### 使用mapState进行数据映射
页面引入
import { mapState } from 'vuex'
在计算属性中...mapState(['city'])进行映射
映射完成 this.$store.state.city 可以改为 this.city
### vuex中的getter
类似于vue中的计算属性,理解为过滤器,可以对数据进行处理,使用映射记得引入
### vuex中的Module
由于使用单一状态树,应用的所有状态会集中到一个比较大的对象。
当应用变得非常复杂时,store 对象就有可能变得相当臃肿。
为了解决以上问题,Vuex 允许我们将 store 分割成模块(module)。
每个模块拥有自己的 state、mutation、action、getter、
甚至是嵌套子模块——从上至下进行同样方式的分割:
## keep-alive优化网页性能
### 解决路由切换页面的时候ajax重复请求的问题
在app.vue中使用keep-alive标签包裹路由标签,作用是缓存ajax请求的json文件
下次页面切换的时候不需要重新请求服务器,减少消耗
### 切换城市首页数据重新加载
使用了keep-alive组件会多出一个生命周期函数activated
选择地址过后activated函数会执行,在这个函数判断选择的城市是否和上一次的城市是否
相同,不同重新请求数据
## 推荐详情页面的开发
### router-link的使用
为了防止a标签文字颜色发生改变,可以将ui中的li标签直接写成router-link标签
tag指明渲染成什么标签,依然有点击跳转效果,并且在后面携带了参数
<router-link
tag="li"
class="item"
border-bottom
v-for="item of list"
:key="item.id"
:to="'/detail/' + item.id"
>
<img class="item-img" :src="item.imgUrl" />
<div class="item-info">
<p class="item-title">{{ item.title }}</p>
<p class="item-desc">{{ item.desc}}</p>
<button class="item-button">查看详情</button>
</div>
</router-link>
### 递归逐渐实现详情页列表
递归组件就是自己调用自己,定义组件的名字就是用来自己调用自己
### 动态获取数据根据id请求对应的文件
在路由后面配置了每项的id,在发ajax请求的时候可以接收作为参数
axios.get('./static/mock/detail.json', {
params: {
id: this.$route.params.id
}
}).then(this.handleGetDataSucc)
### keep-alive另一种解决
有些页面需要重新请求ajax,比如详情页面,除了借助actived生命钩子还可以在keep-alive标签上
使用exclude加上组件名字排除不使用keep-alive进行缓存
<keep-alive exclude="Detail">
<router-view/>
</keep-alive>
## 全局组件的定义
### 全局画廊组件的定义和webpack配置
在src目录下新建common文件夹新建gallary文件夹,里面存放为gallary的组件
在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')
}
}
### 画廊组件点击显示
默认为隐藏点击显示的时候会出现一点问题,
需要在轮播属性中加上observeParents和observer属性控制
具体作用可以查询官网api
[https://3.swiper.com.cn/plus/search.php?kwtype=0&q=obser](https://3.swiper.com.cn/plus/search.php?kwtype=0&q=obser)
swiperOptions: {
pagination: '.swiper-pagination',
paginationType: 'fraction',
observeParents: true,
observer: true
}
### header区域的渐隐渐显效果
## 全局事件的解绑
### 推荐详情页绑定了window事件deactivated事件中进行解绑
推荐详情页绑定了window事件,如果不进行解绑,会影响其他组件
比如回到了首页,方法还是会执行原因在于事件绑定在文档对象中
使用了keep-alive的组件在生命周期会有activated方法页面更新触发
对应还有deactivated生命钩子函数
activated () {
window.addEventListener('scroll', this.handleScroll)
}
解绑操作
activated () {
window.addEventListener('scroll', this.handleScroll)
},
deactivated () {
window.removeEventListener('scroll', this.handleScroll)
}
## 组件名的作用
### 组件名的作用
一:可以用来使用递归组件
二:可以用来取消页面缓存
三:开发者工具可以显示各个组件的名字
## 动画效果
### 新建Fade.vue文件模板
模板使用transition标签内部包含slot标签
vue会自动向这个模板中在特定的时候插入不同的类
## vue项目真机调试
### 局域网调试
webpack自带的开发服务器默认不支持ip地址访问项目,在局域网下的项目只允许localhost的方式进行访问
找到项目文件夹下的package.json配置文件修改
script里面的
"dev": "webpack-dev-server --host 0.0.0.0 --inline --progress --config build/webpack.dev.conf.js",
在webpack-dev-server 后面加上--host 0.0.0.0
这样局域网下面的设备就可以通过ip进行访问了
## 真机测试bug解决
### 低版本安卓机器白屏问题
某些低版本的安卓手机可能不支持es6的一些特性,需要安装
npm install babel-polyfill --save
在main.js文件中引入import 'babel-polyfill'
## vue项目打包上线
### 打包上线
项目文件夹运行npm run build
会在项目文件夹下生成一个dist目录需要放到http服务器进行根目录即可运行
如果需要修改运行路径,比如放在服务器的app目录下,需要修改打包配置重新打包,
不能直接放到app目录下,文件的引用会出现问题
在config目录下的index.js文件中最底部修改build内容的代码
assetsPublicPath: '/',改为
assetsPublicPath: '/app',
重新打包编译,项目就会运行在app目录下了
### 组件的异步加载
适合组件页面比较复杂的情况
打包出来的js文件实际上是所有页面的js,集合,当我们不需要所有文件的时候就可以按需加载
主要是app.js文件
找到src/router/index.js
修改组件的引用方式,采用箭头函数的方式引入
component: () => import('@/pages/home/Home.vue')
component: () => import('@/pages/city/City.vue')
### 打包压缩注意事项
可以把node_modules文件夹删除,需要进行开发的时候,在项目文件夹中运行npm install就会自动安装需要的依赖
配置路径需要完整!!!
assetsPublicPath: '/app/VueTravel/',
后面不能少了/
否则出现大问题!
## 遗留bug的解决
### better-scroll滑动安卓无法点击
项目真机调试发现安卓无法点击,查询问题因为一开始点击事件被禁用了,在使用better-scroll的组件
初始化的时候加上可以点击
this.scroll = new Bscroll(this.$refs.search, {click: true})
### 首页无法滑动问题
这个问题比较新,分析原因在于使用了fastclick插件,当上一次事件未完成,下一次点击开始会阻止
一开始在全局使用了css属性touch-action:none
导致页面无法滚动,暂时去除这个属性,等待后续解决