《Vue2.5开发去哪儿网App 从零基础入门到实战项目》学习笔记
大纲
基础内容,
基础语法,MVVM模式,组件化
生命周期,动画特效
实战项目,
环境搭建,使用git,数据模拟,本地开发
联调,真机测试,上线
辅助:
Axios,Vue Router,Vuex,异步组件,Stylus,递归组件,插件,公用组件
课程安排:
1.课程介绍
2.Vue初探
3-5.基础知识精讲
6-9.Vue项目实战
10.项目测试上线流程及后续学习指南
准备:
.js
ES6
webpack
npm
课程收获:
彻底入门Vue的使用
理解整个Vue项目的开发流程
移动端页面布局技巧
上手中型Vue项目的开发
规范的代码编写
一、课程介绍
二、Vue起步
- 官方文档
不操作dom,都是通过vue操作
MVVM模式:
对比MVP模式:
M model 模型
V view 视图
P presenter 控制器
前端组件化
页面组成部分
将一个页面拆分成多个组件
全局组件
局部组件,组件注册
组件间的传值
父组件 => 子组件:
v-bind v-bind:
子组件的属性='父组件的值'子组件 => 父组件:
$emit('自定义事件名',参数)
三、Vue基础精讲
Vue实例
组件的注册也是默认实例化
Vue 实例还暴露了一些有用的实例 property 与方法。它们都有前缀
$
,以便与用户定义的 property 区分开来。实例生命周期
生命周期函数就是vue实例在某一个时间点会自动执行的函数
模板语法
指令的职责是:当表达式的值改变时,将其产生的连带影响,响应式地作用于DOM
取值
- 插值表达式
{{ name }}
v-text=""
v-html=""
- 插值表达式
绑定
:class
:style
事件
@click
计算属性,方法和侦听器
对于任何复杂逻辑,你都应当使用计算属性。
计算属性:
computed: {}
会缓存旧数据,只有依赖的数据变化才会刷新缓存
方法实现:没有缓存机制
侦听器实现:有缓存机制,但代码多
计算属性的setter和getter
vue中的样式绑定:
:class="{}"
class的对象绑定:class="{css样式:boolean类型的判断参数}"
:class="[]"
class的数组绑定:class="[存储css样式的变量]"
使用
:style
对象或数组
vue中的条件渲染
v-if v-show v-else v-else-if
- 复用性高,不变的元素不会重新渲染,加key使之重新渲染
列表渲染
使用key值可以提高性能,不要用index,使用item中的唯一标识性能会更高
操作数组只有使用vue定义的方法才会重新渲染页面:
1. push 2. pop 3. shift 4. unshift 5. splice 6. sort 7. reverse
或直接改变数组的引用 或
Vue.set()
方法template 模板占位符,不会被渲染到页面上
也可遍历对象,
- 对象通过改变引用来重新渲染页面 - Vue.set()方法
四、深入理解Vue组件
使用组件的细节点
is
间接使用组件 is="组件名" table/select/ul 等中使用isdata
子组件中的data用函数表示,使每个子组件的实例都有独立的数据存储ref
使用dom ref dom或子组件的引用
父子组件的数据传递
- 子组件不要修改父组件传过来的引用类型数据 通过data副本来修改
组件参数校验与非props特性
校验
props: { content: { type: String, required: false, default: 'default value', // 校验器 validator: function (value) { return value.length>5; } } },
非props特性
- 未定义,子组件不能使用
- dom标签中会展示
给组件绑定原生事件
子组件上绑定的事件都是自定义的事件,子组件模板里绑定的才是原生事件
加修饰符
.native
可以绑定原生事件非父子组件之间的传值
- 官方框架 vuex
- 总线机制(Bus/总线/观察者模式/发布订阅模式)
在Vue中使用插槽
父组件向子组件优雅地传递dom结构
具名插槽
作用域插槽
循环遍历时,父组件自定义子组件展示的dom
动态组件和
v-once
指令动态组件
<component :is="组件名"></component>
根据is属性自动变换组件
v-once
只渲染元素和组件一次。随后的重新渲染,元素/组件及其所有的子节点将被视为静态内容并跳过。这可以用于优化更新性能。
五、Vue中的动画特效
css动画原理
动态地往标签中添加或删除class
使用transition标签包裹
使用Animate.css库
同时使用过渡和动画 设置时长问题
js动画与velocity.js的结合
transition标签存在事件
js钩子
velocity.js库
多个组件或元素的过渡
mode属性控制 in-out out-in
列表过渡
transition-group
动画封装
六、Vue项目预热
环境准备
- node.js
- vue-cli
项目结构
- build 构建脚本目录
- build.js 生产环境构建脚本
- check-versions.js 版本检查(node,npm)
- utils.js 构建相关工具方法
- vue-loader.conf.js css加载器配置
- webpack.base.conf.js webpack基础配置
- webpack.dev.conf.js webpack开发环境配置
- webpack.prod.conf.js webpack生产环境配置
- config 项目配置
- dev.env.js 开发环境变量
- index.js 项目配置文件
- prod.env.js 生产环境变量
- test.env.js 测试环境变量
- src 源码目录
- assets 资源目录,这里的资源会被webpack构建
- components 公共组件目录
- router 前端路由
- index.js
- App.vue 根组件
- main.js 入口js文件
- static 纯静态资源,不会被webpack构建
- .gitkeep
- test 测试文件目录
- .babelrc babel编译参数
- .editorconfig 代码格式
- .esIintignore
- .esIintrc.js
- .gitignore git上传需要忽略的文件配置
- .postcssrc.js 转换css的工具
- index.html 入口页面
- package.json npm包配置文件,里面定义了项目的npm脚本、依赖包等星系
- package-lock.json
- README.md 项目介绍
- build 构建脚本目录
单文件组件与Vue中的路由
.vue 单文件组件
组件的模板
<template></template>
组件的逻辑
<script></script>
组件的样式
<style></style>
路由就是根据网址的不同,返回不同的内容给用户
<router-view/>
显示的是当前路由地址多对应的内容- @表示src目录
单页应用vs多页应用
多页面应用:
页面跳转=》返回HTML
- 优点:首屏时间快,SEO效果好 - 缺点:页面切换慢
单页面应用:
- 优点:页面切换快
- 缺点:首屏时间稍慢,SEO差
项目初始化
vue init webpack travel reset.css 解决一像素边框的Border.css #使用fastclick解决300ms点击延迟 cnpm install fastclick --save
七、项目实战 - 旅游网站首页开发
Stylus - 富有表现力的、动态的、健壮的CSS
cnpm install stylus --save cnpm install stylus-loader --save
iconfont的使用及代码优化
使用stylus
css中引用使用@import
可自定义别名
@ src js中的别名 ~@ src style中的别名
首页轮播图
插件awesome-swiper https://github.com/surmon-china/vue-awesome-swiper
stylus中使用>>>进行样式穿透,限制范围的css样式可在全局生效
首页图标区域页面布局
图标区域逻辑实现
使用计算属性组装二维数组
轮播效果
首页热销推荐组件开发
列表
首页开发周末游组件
列表
使用axios发送ajax请求
安装axios
cnpm install axios --save axios.get().then() config/index.js 配置地址转发代理
首页父子组件间传值
父组件 :自组件的属性名="父组件的数据"
子组件:props
八、项目实战 - 旅游网站城市列表页面开发
路由配置
添加城市路由,设置跳转路由
<router-link to="">
搜索框布局
input
列表布局
list
css样式基本都是常用的那些,可以重点突破
display position line-height width height float padding margin border flex flex-direction justify-content overflow rem px ...
BetterScroll的使用和字母表布局
https://better-scroll.github.io/docs/zh-CN/guide/#betterscroll-是什么
cnpm install better-scroll --save
页面的动态数据渲染
axios使用
兄弟组件数据传递
通过父组件
列表性能优化
搜索逻辑实现
Vuex实现数据共享
Vuex的高级使用及localStorage
解决缓存:
1. cookie 2. localStorage
使用keep-alive优化网页性能
<keep-alive></keep-alive>
九、项目实战 - 旅游网站详情页面开发
动态路由和banner布局
颜色渐变
公用图片画廊组件拆分
实现header渐隐渐显效果
对全局事件的解绑
使用递归组件实现详情页列表
掌握递归组件的使用
动态获取详情页面数据
<keep-alive exclude="Detail">
<router-view/>
</keep-alive>
name的作用:
递归
取消缓存
F12组件名字
- 在项目中加入基础动画
十、项目实战-项目的联调、测试与发布上线
项目前后端联调
改config/index.js的代理
真机测试
打包上线
npm run build
打包后的目录dist
异步组件实现按需加载
按需加载业务js
route/index.js
将局部组件转为异步组件
component: () => import('@/pages/home/Home')
课程总结与后续学习指南
封装axios
import axios from 'axios'
import { MessageBox, Message } from 'element-ui'
import store from '@/store'
import { getToken } from '@/utils/auth'
// create an axios instance
const service = axios.create({
baseURL: process.env.VUE_APP_BASE_API, // url = base url + request url
// withCredentials: true, // send cookies when cross-domain requests
timeout: 5000 // request timeout
})
// request interceptor
service.interceptors.request.use(
config => {
// do something before request is sent
if (store.getters.token) {
// let each request carry token
// ['X-Token'] is a custom headers key
// please modify it according to the actual situation
// config.headers['X-Token'] = getToken()
config.headers['token'] = getToken()
}
return config
},
error => {
// do something with request error
console.log(error) // for debug
return Promise.reject(error)
}
)
// response interceptor
service.interceptors.response.use(
/**
* If you want to get http information such as headers or status
* Please return response => response
*/
/**
* Determine the request status by custom code
* Here is just an example
* You can also judge the status by HTTP Status Code
*/
response => {
const res = response.data
// if the custom code is not 20000, it is judged as an error.
if (res.code !== 200) {
Message({
message: res.message || 'Error',
type: 'error',
duration: 5 * 1000
})
// 50008: Illegal token; 50012: Other clients logged in; 50014: Token expired;
if (res.code === 50008 || res.code === 50012 || res.code === 50014) {
// to re-login
MessageBox.confirm('You have been logged out, you can cancel to stay on this page, or log in again', 'Confirm logout', {
confirmButtonText: 'Re-Login',
cancelButtonText: 'Cancel',
type: 'warning'
}).then(() => {
store.dispatch('user/resetToken').then(() => {
location.reload()
})
})
}
return Promise.reject(new Error(res.message || 'Error'))
} else {
return res
}
},
error => {
console.log('err' + error) // for debug
Message({
message: error.message,
type: 'error',
duration: 5 * 1000
})
return Promise.reject(error)
}
)
export default service
import axios from 'axios'
import { MessageBox, Message } from 'element-ui'
import cookie from "js-cookie"
import {toKey} from "core-js/internals/reflect-metadata";
// 创建axios实例
const service = axios.create({
baseURL: 'http://localhost',
timeout: 15000 // 请求超时时间
})
// http request 拦截器
service.interceptors.request.use(
config => {
// token 先不处理,后续使用时在完善
if (cookie.get('token')){
config.headers['token'] = cookie.get('token')
}
return config
},
err => {
return Promise.reject(err)
})
// http response 拦截器
service.interceptors.response.use(
response => {
if (response.data.code == 208) {
loginEvent.$emit('loginDialogEvent')
return
}else if (response.data.code !== 200) {
Message({
message: response.data.message,
type: 'error',
duration: 5 * 1000
})
return Promise.reject(response.data)
} else {
return response.data
}
},
error => {
return Promise.reject(error.response)
})
export default service