MVC && MVVM
前端框架前端 MV*框架的意义
被误解的MVC和被神化的MVVM
Vue.js新手入门指南
单页应用SPA的路由
单页面应用的路由问题
本文是在自己总结时,看了许多篇文章有了些体会,然后把我认为有意义的摘抄下来,文中很大部分摘录以上参考的文章,再结合一点点自己的观点,总的来说这篇文章是一个汇总文。
MV框架又是为什么兴起的呢?
在前端搞 MV有什么意义?也有人提出这样的疑问:以 AngularJS,Knockout,BackBone 为代表的 MV*框架,它跟 jQuery 这样的框架有什么区别?我 jQuery 用得好好的,有什么必要再引入这种框架?
库和框架是有一些区别的:库是一种工具,我提供了,你可以不用,即使你用了,也没影响你自 己的代码结构。框架则是面向一个领域,提供一套解决方案,如果你用我,就得按照我的方式办事。
jQuery 的思维方式是:以 DOM 操作为中心
MV*框架的思维方式是:以模型为中心,DOM 操作只是附加
由于前端功能的增强、代码的膨胀,导致不得不做“前端的架构”这个事情了。
为什么需要MV*框架
所以回到那个问题上,jQuery 满足了你的业务需要,你还有什么必要引入 MV*框架?
这个是要看产品类型的,如果是页面型产品,多数确实不太需要它,因为页面中的 JavaScript 代码,处理交互的绝对远远超过处理模型的,但是如果是应用软件类产品,这就太需要了。
MV框架的理念是把前端按照职责分层,每一层都相对比较独立,有自己的价值,也有各自发挥的余地。
这种逻辑的好处在于,业务逻辑与用户界面分离之后,后期对于界面的改版以及对于用户交互的处理变化,仅仅需要改动View层即可,不在需要对业务逻辑层进行多大的改动。后期的维护成本会减少很多。
将开发重心从DOM操作,转移到数据操作,将DOM操作与程序逻辑解耦。
期望提升开发效率、单位时间产出、后期代码扩展性,降低维护成本
MVC
Model 数据模型
View 用户界面
Controller 业务逻辑
不同的框架对Model、View的数据同步有不同的处理
MVC开发模式的原则:我们来看看 MVC 这种架构的特点。其实设计模式很多时候是为了 Don't repeat yourself 原则来做的,该原则要求能够复用的代码要尽量复用,来保证重用。在 MVC 这种设计模式中,我们发现 View 和 Model 都是符合这种原则的。在Controller层尽量写不能够复用的。
什么是MVVM
开发人员只要考虑和处理Model(数据模型)的变化即可,不用考虑Model和View之间的数据绑定同步,更不用花精力用大量的代码获取DOM元素改变DOM元素的值来完成界面数据的变化。所有工作交给VM(View-Model)来处理。
MVVM并没有业务逻辑的控制器,它通过数据双向绑定,实时更新View和Model层,当数据模型发生变化的时候,用户界面(DOM)的内容会即时更新。反之如果用户操作导致某些DOM内容变化(如input),ViewModel也会即时的将Model数据模型更新。
数据双向绑定,开发人员不用再把精力放在DOM的修改和更新,只要通过模板引擎将数据模型和用户界面绑定,框架会实时同步双方数据的变化。减轻了开发人员的负担,也减少了DOM操作逻辑导致业务逻辑混乱的可能性。
backbonejs
调用 Backbone.history.start() 开始监控 hashchange 事件并分配路由
View:视图
Model:数据
Router:路由,由于Controller层主要负责了路由,而业务逻辑都在View中写了。
backbone事件:订阅发布者模式
vue.js
什么是vue
Vue.js(读音 /vjuː/, 类似于 view) 是一套构建用户界面的 渐进式框架。与其他重量级框架不同的是,Vue 采用自底向上增量开发的设计。Vue 的核心库只关注视图层,并且非常容易学习,非常容易与其它库或已有项目整合。另一方面,Vue 完全有能力驱动采用单文件组件和 Vue 生态系统支持的库开发的复杂单页应用。
Vue.js 的目标是通过尽可能简单的 API 实现响应的数据绑定和组合的视图组件。
Vue.js就是一个用于搭建类似于网页版知乎这种表单项繁多,且内容需要根据用户的操作进行修改的网页版应用。
vue支持
vue 不支持IE8以下浏览器的原因?因为 Vue.js 使用了 IE8 不能模拟的 ECMAScript 5 特性
Virtual DOM(这个也没有写好)
通过js对象。。。。。。。。。
生命周期
-
beforeCreate
observe Data / init Events
组件实例被创建,组件属性计算之前
应用:beforeCreate 给个loading加载界面
-
created
1.组件实例创建完成,属性已经绑定,但DOM还未生成,$el属性还不存在
2.在实例创建之后同步调用。此时实例已经结束解析选项,这意味着已建立:数据绑定,计算属性,方法,watcher/事件回调。
3.但是还没有开始 DOM 编译,$el 还不存在,但是实例存在,即this.a存在,可打印出来 。
应用:created阶段做一些初始化,实现函数自执行,例如:created撤销loading
el / template
-
beforeMounted
模板编译、挂载之前
replace el
-
mounted
1.模板编译、挂载之后不保证组件已在document中
2.在编译结束后调用。此时所有的指令已生效,因而数据的变化将触发 DOM 更新。但是不担保 $el 已插入文档。
应用:mounted阶段做ajax,或者配合路由钩子做一些事情;此时DOM已经获取到,可以对DOM进行操作
data changed
-
beforeUpdated
1.数据更新时调用,发生在虚拟 DOM 重新渲染和打补丁之前。
2.你可以在这个钩子中进一步地更改状态,这不会触发附加的重渲染过程。
3.该钩子在服务器端渲染期间不被调用。
virtual dom re-render / patch
-
updated
由于数据更改导致的虚拟 DOM 重新渲染和打补丁,在这之后会调用该钩子。当这个钩子被调用时,组件 DOM 已经更新,所以你现在可以执行依赖于 DOM 的操作。
然而在大多数情况下,你应该避免在此期间更改状态,因为这可能会导致更新无限循环。
vm.$destroyed
beforeDestroy
dsetroyed
vue模块化(自己没有还没有写好)
双向绑定的实现
还好发布检查了一次,发现自己把自己乱七八糟说的都写进来了。
这篇文章我觉还不错,跟着自己写一遍,就会比较了解了。所有才有我的乱七八糟之说。自己理解最重要。
1.实例MVVM对象时,会生成观察者observe去观察实例中的data的每一个属性。
2.observe对象会遍历每一个key、value。如果value也是对象,也会通过observe来观察其中的key、value。
3.在每个observe对象中会创建一个订阅者,然后再设置属性的内置get、set函数。
4.通过内置get函数:在里面会给data的属性添加订阅者。
5.通过内置set函数:会将value值更改,并且如果新的value值是object,那么则创建一个observe观察者观察它。最后会通知订阅者。
6.compile主要做的事情是解析模板指令,将模板中的变量替换成数据,然后初始化渲染页面视图,并将每个指令对应的节点绑定更新函数,添加监听数据的订阅者,一旦数据有变动,收到通知,更新视图。
7.观察者Watcher是observe与compile的桥梁,在解析vue的自定义指令时,会为每一个指令创建一个Watcher观察者对象。watcher会将每个指令的当前状态设定,并添加到每个data的订阅者数组中,当data的数值发生改变时,会通知其绑定的订阅者,触发每一个订阅者更新函数。
vue的缺点
第一点:数据绑定使得 Bug 很难被调试。你看到界面异常了,有可能是你 View 的代码有 Bug,也可能是 Model 的代码有问题。数据绑定使得一个位置的 Bug 被快速传递到别的位置,要定位原始出问题的地方就变得不那么容易了。
第二点:对于过大的项目,数据绑定需要花费更多的内存。
vue的计算属性-computed
我们可以将同一函数定义为一个 method 而不是一个计算属性。对于最终的结果,两种方式确实是相同的。然而,不同的是计算属性是基于它们的依赖进行缓存的。计算属性只有在它的相关依赖发生改变时才会重新求值。这就意味着只要 message 还没有发生改变,多次访问 reversedMessage 计算属性会立即返回之前的计算结果,而不必再次执行函数。
watch
仅是观察变量的变化可以不用watch,直接用计算属性
但是,当在数据发生变化时,你希望执行开销较大的操作时,用watch比较有用。
watch可以限制我们执行的频率,在最终结果前,可以设置中间状态。计算属性无法做到。
vuex
state:驱动应用的数据源
view:以声明方式将state映射到视图
action:响应在view上的用户输入导致的状态变化。
当我们的应用遇到多个组件共享状态时,单向数据流的简洁性很容易被破坏:
多个视图依赖于同一状态。
来自不同视图的行为需要变更同一状态。
对于问题一,传参的方法对于多层嵌套的组件将会非常繁琐,并且对于兄弟组件间的状态传递无能为力。对于问题二,我们经常会采用父子组件直接引用或者通过事件来变更和同步状态的多份拷贝。以上的这些模式非常脆弱,通常会导致无法维护的代码。
把组件的共享状态抽取出来,以一个全局单例模式管理。
Vuex 是一个专为Vue.js应用程序开发的状态管理模式。它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。
由于 Vuex 的状态存储是响应式的,从 store 实例中读取状态最简单的方法就是在计算属性中返回某个状态
State:状态
Getters:从State中派生出来的一些状态
Mutations:更改 Vuex 的 store 中的状态的唯一方法是提交 mutation,不能直接调用,需要触发store.commit(type,[payload])。mutation必须时同步函数。异步函数不能捕捉到改变的状态
-
Actions:
Action 提交的是 mutation,而不是直接变更状态
Action 可以包含任意异步操作。
Action 函数接受一个与 store 实例具有相同方法和属性的 context 对象。
Action 通过 store.dispatch 方法触发:store.dispatch('increment')
store.dispatch 可以处理被触发的action的回调函数返回的Promise,并且store.dispatch仍旧返回Promise
-
Modules
由于使用单一状态树,应用的所有状态会集中到一个比较大的对象。当应用变得非常复杂时,store 对象就有可能变得相当臃肿。
为了解决以上问题,Vuex 允许我们将 store 分割成模块(module)。每个模块拥有自己的 state、mutation、action、getter、甚至是嵌套子模块——从上至下进行同样方式的分割
SPA
单页面应用(Single Page Application)简称SPA,使用SPA构建的应用优点有用户体验好、速度快,内容的改变不需要重新加载整个页面,避免了不必要的跳转和重复渲染,从而相对减轻了服务器压力,SPA在WEB移动端应用非常广泛。
单页应用路由
HTML5在History里增加了pushState方法,这个方法会将当前的url添加到历史记录中,然后修改当前url为新url。当然这个方法只会修改地址栏的Url显示,但并不会发出任何请求。因此我们可以利用这个方法结合ajax实现单页面应用SPA,就是PushState+Ajax,人称Pjax。
location.hash更改了,页面也不会变化 hashchange hash值的改变也会加入历史记录中
总的来说,基于Hash的路由,兼容性更好;基于History API的路由,更加直观和正式。但是,有一点很大的区别是,基于Hash的路由不需要对服务器做改动,基于History API的路由需要对服务器做一些改造。
pushstate的使用方法:
history.pushState(state, title, url)
state: 可以放任意你想放的数据,它将附加到新url上,作为该页面信息的一个补充。 该对象可在onpopstate事件中获取,也可在history对象中获取。
title: 顾名思义,就是document.title。
url: 新url,也就是你要显示在地址栏上的url。
history.replaceState(state, title, url)
replaceState方法与pushState大同小异,区别只在于pushState会将当前url添加到历史记录,之后再修改url,而replaceState只是修改url,不添加历史记录。
window.onpopstate
一般来说,每当url变动时,popstate事件都会被触发。因此,我们可以把它用作浏览器的前进后退事件。该事件有一个参数,就是上文pushState方法的第一个参数state。
Pjax能做什么
Pjax是一个优秀的解决方案,它可以做:
可以在页面切换间平滑过渡,增加Loading动画。
可以在各个页面间传递数据,不依赖URL。
可以选择性的保留状态,如音乐网站,切换页面时不会停止播放歌曲。
所有的标签都可以用来跳转,不仅仅是a标签。
避免了公共JS的反复执行,减少了请求体积,节省流量,加快页面响应速度。
对SEO也不会有影响,对于不支持HTML5的浏览器以及搜索引擎爬虫,则可以跳转真实的页面。
支持浏览器前进和后退按钮。
Pjax
ajax请求,通过html5pushState来修改历史记录,如果不支持html5则
重写url
每次修改location的属性(hash除外),页面都会以新URL重新加载
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。