《去哪网》制作到上线总结
上学期末接触了vue,寒假的时候在慕课网上买了Dell老师的vue课程开发去哪儿网,学习vue到完成项目使用了将近一个月的时间,当解决最后一个跨越问题,觉得自己学了很多又学的很少。昨天阅读了月影老师16年的一篇博客后恍然大悟。自己在大二转专业以来自己可能并不是真的喜欢计算机,但是从接触了前端以后才体会到了计算机的魅力,不同于C语言的黑洞洞的窗口,前端五彩斑斓的世界激发了我的创造欲。我在追求创造的过程中,学习了html,css,js,vue等,但却也在身边追求工作的狂热中慢慢失去了热爱的初心,单纯的追求技术去学习,貌似失去了最初对问题刨根问底的精神。做了很多项目,解决了问题就不再深究,似乎没有最初的干劲和解决bug的成就感。因此写下本文,回忆总结自己在这个项目中学到的知识,也给自己一个警示,耐下心来认真学习。
项目成品:去哪网仿写
下面内容纯属个人理解,有错误的地方欢迎指出,轻喷!!!我也是个菜鸡!!!
1. Vue
课程的最开始不必说,肯定是从vue开始说起,vue的文档很健全,可以在官网上进行查询:vue官方文档。本文仅记录自己学习过程中学到的知识!
1.1 MVVM模式
- M:Model层,数据模型
- V:View层,视图层
- VM:ViewModel层,监听模型数据的改变和控制视图变化
我们都知道在使用js在开发的过程中对DOM的操作十分的繁琐,而是人总是喜欢“偷懒“的。在vue还没有出现的时代很多传统的服务端代码放到了浏览器中,这样就产生了成千上万行的javascript代码,它们连接了各式各样的HTML 和CSS文件,但缺乏正规的组织形式。而且手机移动端的Web不断的兴起,各种复杂的交互变得多了起来:社交、购物、新闻、视频分享、音乐互动等等一系列让前端项目越来越大,项目的可维护性、可拓展性、安全性等成了主要的问题。为了解决兼容的问题,出现了很多的库,最经典的就是我们耳熟能详的就是jQuery。但是他们一就没有实现对业务逻辑的成分,维护性和拓展性极差。vue则很大程度上的解决了这一问题。
vue采用的是MVVM模式,其中每个字母代表的意思已经在上面叙述了,他让我们在前端代码编写的时候减少了对DOM层的关注,将更多的精力放在了业务逻辑上,同时webpack等打包工具的使用使前端项目模块化,相比于原先更加有调理,更易维护和拓展。
1.2 前端组件化
1.2.1 什么是组件?
组件是可复用的的vue实例,所以它们与 new Vue 接收相同的选项,例如 data、computed、watch、methods 以及生命周期钩子等。仅有的例外是像 el 这样根实例特有的选项。
1.2.2 函数data
因为组件的复用,为了防止data的内容在不同组件使用的地方进行了串联,所以不能用对象来表示,而是用函数,这样就可以将不同引用的组件的data的值存储到不同地址
1.2.3 组件之间的组织
vue的组件有两个组件的注册类型:全局注册和局部注册
1.全局注册
全局注册的组件可以通过 Vue.component 全局注册,注意:
(1)全局注册的组件可以用在其被注册之后的任何 (通过 new Vue) 新创建的 Vue 根实例,也包括其组件树中的所有子组件的模板中。
(2)在用脚手架搭建的项目里,我们会在index.js入口文件处注册一个全局的Vue根实例,挂载到html的ID为#app的div上。
2.局部注册
局部注册则是在已有的Vue的实例内部components中进行注册,在项目的过程中业主要是使用局部注册,注意:
(1)局部注册的组件在其子组件中不可用。
(2)有时看到的对象内部只有一个变量名,其实是ES6的语法,变量名和值相同时可以缩写成一个。
3.模块系统
在看了一些webpack后理解到vue之间的模块是通过webpack进行的打包,我们将组件放在单独的vue文件中,并使用export default将其暴露在外部,可供需要调用的进行import引入。注意:
(1)阅读了webpack的一些文章后明白,其实webpack本身并不能知道如何去打包和理解这些vue文件,所以需要下载一些loader辅助他进行解析。
(2)package.json其实是npm包管理的内容,和node的使用息息相关,最近在学习node,理解了其中的一些配置。webpack也是npm包管理的一个对象。
1.2.4 组件之间的传值
1.父组件向子组件传值
(1)父组件在子组件标签中绑定自定义属性;
(2)子组件通过 props 属性进行接收。
//父组件
<script>
import Child from './....'
export default { components:{ Child } }
<Child :name="mychild" />
</script>
//子组件
<script>
export default { props: ["name"]//此处亦可指定数据类型 }
</script>
2.子组件向父组件传值
(1)在父组件在子组件标签中绑定自定义事件;
(2)子组件通过this.$emit()方法触发自定义事件,传值给父组件。
//父组件
<template>
<Child @changeName="changeName" />
</template>
<script>
import Child from './....'
export default {
components:{ Child },
data:{ name:"123" },
methods:{
changeName(value){
this.name = value
}
}
}
</script>
//子组件
<template>
<button @click="changeParentName">改变父组件的name</button>
</template>
<script>
export default {
methods:{
changeParentName:(value)=>{ this.$emit("changeName","456") }
}
}
</script>
3.Vuex
Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。为了解决除了父子组件之间传值,提出把组件的共享状态抽取出来,以一个全局单例模式管理。详细可看官方文档:Vuex文档
(1)state,驱动应用的数据源;
- Vuex 的状态存储是响应式的。当 Vue 组件从 store 中读取状态的时候,若 store 中的状态发生变化,那么相应的组件也会相应地得到高效更新。
- 你不能直接改变 store 中的状态。改变 store 中的状态的唯一途径就是显式地提交 (commit) mutation。
// vuex.js
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
const store = new Vuex.Store({
state: {
count: 0
},
mutations: {
increment (state) {
state.count++
}
}
})
// 使用或者修改state里面的数据
store.commit('increment')
console.log(store.state.count) // -> 1
methods: {
increment() {
this.$store.commit('increment')
console.log(this.$store.state.count)
}
}
// 将其放在vue实例内部,这样就可以通过this.$store来访问数据了
new Vue({
el: '#app',
store: store,
})
(2)view,以声明方式将 state 映射到视图;
(3)actions,响应在 view 上的用户输入导致的状态变化。
1.3 Vue实例以及生命周期
1.4 计算属性,方法和监听器
-
compute:为了减少模板里面的复杂逻辑运算,方便维护
(1)支持缓存,只有依赖数据发生变化时,才会重新进行计算函数;
(2)计算属性内不支持异步操作;
(3)计算属性的函数中都有一个 get(默认具有,获取计算属性)和 set(手动添加,设置计算属性)方法;
(4)计算属性是自动监听依赖值的变化,从而动态返回内容。 -
watch:侦听属性
(1)不支持缓存,只要数据发生变化,就会执行侦听函数;
(2)侦听属性内支持异步操作;
(3)侦听属性的值可以是一个对象,接收 handler 回调,deep,immediate 三个属性;
(4)监听是一个过程,在监听的值变化时,可以触发一个回调,并做一些其他事情。
1.5 Vue中的指令
- v-if v-else-if v-else
- 进行条件判断来渲染
- v-for
- 进行循环
- v-show
- 进行条件判断来决定display是否为null
- 在一些DOM变化比较频繁的地方使用v-show更好,因为v-if是对DOM的操作,性能消耗比较大
- v-bind,缩写:
- 动态地绑定一个或多个 attribute,或一个组件 prop 到表达式。
- v-on,缩写@click
- 绑定事件监听器。事件类型由参数指定。表达式可以是一个方法的名字或一个内联语句,如果没有修饰符也可以省略。
- 修饰符
- .stop- 调用event.stopPropagation()。
- .prevent- 调用event.preventDefault()。
- .capture- 添加事件侦听器时使用 capture 模式。
- .self- 只当事件是从侦听器绑定的元素本身触发时才触发回调。
- .{keyCode | keyAlias}- 只当事件是从特定键触发时才触发回调。
- .native- 监听组件根元素的原生事件。
- .once- 只触发一次回调。
- .left- (2.2.0) 只当点击鼠标左键时触发。
- .right- (2.2.0) 只当点击鼠标右键时触发。
- .middle- (2.2.0) 只当点击鼠标中键时触发。
- .passive- (2.3.0) 以{ passive: true }模式添加侦听器
1.7 路由(Vue Router)
Vue Router 是 Vue.js 官方的路由管理器。
1.8 keep-alive
-
Props:
- include - 字符串或正则表达式。只有名称匹配的组件会被缓存。
- exclude - 字符串或正则表达式。任何名称匹配的组件都不会被缓存。
- max - 数字。最多可以缓存多少组件实例。
-
用法:
- <keep-alive> 包裹动态组件时,会缓存不活动的组件实例,而不是销毁它们。
- <keep-alive> 是一个抽象组件:它自身不会渲染一个 DOM 元素,也不会出现在组件的父组件链中。
- 当组件在<keep-alive> 内被切换,它的 activated 和 deactivated 这两个生命周期钩子函数将会被对应执行。
1.9 数据双向绑定
详细见,我是你的超级英雄博客:0 到 1 掌握:Vue 核心之数据双向绑定
- 视图变化更新数据:通过事件监听的方式,Object.defineProperty方法属性拦截的方式,把data对象里每个数据的读写转化成getter/setter,来通知视图进行更新。
- 数据变化更新视图:
- 实现一个监听器 Observer ,用来劫持并监听所有属性,如果属性发生变化,就通知订阅者;
- 该构造函数主要使用了Object.defineProperty方法来感知数据的变化,并去通知订阅者
- 实现一个订阅器 Dep,用来收集订阅者,对监听器 Observer 和 订阅者 Watcher 进行统一管理;
- 该构造函数完成的功能是在Observer感知到数据变化后统一收集Wather,然后统一更新
- 实现一个订阅者 Watcher,可以收到属性的变化通知并执行相应的方法,从而更新视图;
- 该构造函数主要实现初始化时将自身添加进Dep订阅器中
- 实现一个解析器 Compile,可以解析每个节点的相关指令,对模板数据和订阅器进行初始化。
- 实现一个监听器 Observer ,用来劫持并监听所有属性,如果属性发生变化,就通知订阅者;
2.项目开发问题
2.1 字题图标使用
字体图标主要是上阿里巴巴的矢量图标库,使用方法也很简单,如下:
- 找到自己需要的图标并加入到项目中
- 下载压缩包后将一下几个文件放在自己项目的静态文件中
- 在index入口文件处引入iconfont.css
- 在需要用到的地方直接使用
2.2 防抖和节流
见另一篇博客:防抖和节流
2.3 Nginx部署前端项目
- 在vue项目中使用npm run build生成dist文件
- 将dist文件上传到自己服务器某文件夹处
- 按照nginx配置好信息后重启nginx
2.4 Node.js部署后端接口
- 使用node写好接口
- 上传到服务器
- 使用forever或pm2进行运行
ps:注意后端服务器要确保设置了可跨域