是一种只须要将单个页面加载到服务器之中的web应用程序。当浏览器向服务器发出第一个请求时,服务器会返回一个index.html文件,它所需的js,css等会在显示时统一加载,部分页面按需加载。url地址变化时不会向服务器在请求页面,经过路由才实现页面切换。javascript
优势:css
缺点:html
在以往开发程序过程当中,界面布局代码、界面交互逻辑代码、业务逻辑代码三者代码都是混在一块儿的。随着业务需求愈来愈大,代码愈来愈复杂,不只致使开发困难,更是致使维护代码更困难,特别是维护别人的代码。vue
因此就出现MVC模式来解决这个问题,其中M表明Model,专门来处理、存储数据。V表明View,专门来处理页面的展现。C表明Controller专门处理业务逻辑。java
用户操做View,View发送指令到Control,完成业务逻辑处理后,要求Model处理相应的数据,将处理好的数据发送到View,要求View把这些数据展现给用户。node
固然用户也能够直接下发指令到Control,完成对应业务逻辑处理后,要求Model处理相应的数据,将处理好的数据发送到View,要求View把这些数据展现给用户。webpack
也能够经过View直接要求Moder处理数据,将处理好的数据发送到View,要求View把这些数据展现给用户。ios
然而在MVC模式中,Model、Control、View三者相互依赖,修改起来要兼顾其余二者,仍是很是困难。git
因此又出现了MVP模式来解决这个问题,在MVP模式中P表明Presenter替代原来的Control。github
当用户操做View,View发送指令到Presenter,完成业务逻辑处理后,要求Model处理相应的数据,将处理好的数据返回到Presenter中,Presenter将数据发送到View中,要求View把这些数据展现给用户。
MVP模式中,Presenter将View和Model彻底隔离开,Presenter和View相互依赖,Presenter和Model相互依赖,View和Model再也不相互依赖,使代码耦合下降。
由于Presenter和View相互依赖,这样Presenter就没办法单独作单元测试,非得等到View作好之后才行。因此对View分割一部分叫作View接口,Presenter只依赖View接口,这样Presenter不用依赖View就能够测试了,而且也增长了复用性,只要View实现了View接口部分,Presenter就能够大发神威。
然而在MVP模式中,由于让Presenter发送数据到View,让View展现,仍然须要大量的、烦人的代码,这实在是一件不舒服的事情。 那么可不可让View在Model变化时自动更新。
因此出现了MVVM模式来实现这个设想,其中VM表明ViewModel负责视图显示逻辑和监听视图变化,M表明Model变成处理业务逻辑和数据。
当用户操做View时,ViewModel监听到View的变化,会通知Model中对应的方法进行业务逻辑和数据处理,处理完毕后,ViewModel会监听到自动让View作出相应的更新。ViewModel能够对应多个View,具备很强的复用性。
在Vue项目中。new Vue()就是一个ViewModel,View就是template模板。Model就是Vue的选项如data、methods等。在开发过程咱们只关注View怎么展现,Model怎么处理业务逻辑和数据。不要去管处理业务逻辑和数据后怎么让View更新,View上有操做,怎么让Model处理这个操做,这些统统交给ViewModel来实现,大大下降了开发成本。
Object.defineProperty(obj,prop,descriptor)
方法会直接在一个对象上定义一个新属性,或者修改一个对象的现有属性, 并返回这个对象。
function Archiver() {
var temperature = null;
var archive = [];
Object.defineProperty(this, 'temperature', {
get: function() {
console.log('get!');
return temperature;
},
set: function(value) {
temperature = value;
archive.push({ val: temperature });
}
});
this.getArchive = function() { return archive; };
}
var arc = new Archiver();
arc.temperature; // 'get!'
arc.temperature = 11;
arc.temperature = 13;
arc.getArchive(); // [{ val: 11 }, { val: 13 }]
复制代码
通俗来讲是在对目标对象的操做以前提供了拦截,对外界的操做进行过滤和修改某些操做的默认行为,能够不直接操做对象自己,而是经过操做对象的代理对象来间接来操做对象。
let proxy = new Proxy(target, handler)
target
是用 Proxy 包装的目标对象(能够是任何类型的对象,包括原生数组,函数,甚至另外一个代理);handler
一个对象,其属性是当执行一个操做时定义代理的行为的函数,也就是自定义的行为。handle
能够为{}
,可是不能为null
,不然会报错
Proxy 目前提供了 13 种可代理操做,比较经常使用的
let obj = {
a : 1,
b : 2
}
let test = new Proxy(obj,{
get : function (target,property) {
return property in target ? target[property] : 0
},
set : function (target,property,value) {
target[property] = 6;
},
has: function (target,prop){
if(prop == 'b'){
target[prop] = 6;
}
return prop in target;
},
})
console.log(test.a); // 1
console.log(test.c); // 0
test.a = 3;
console.log(test.a) // 6
if('b' in test){
console.log(test) // Proxy {a: 6, b: 6}
}
复制代码
采用的是Mustache的web模板引擎mustache.js
<script type="text/javascript" src="./mustache.js"></script>
<script type="text/javascript">
var data = {
"company": "Apple",
}
var tpl = '<h1>Hello {{company}}</h1>';
var html = Mustache.render(tpl, data);
console.log(html);
</script>
复制代码
Vue.js 的核心是一个容许采用简洁的模板语法来声明式地将数据渲染进 DOM 的系统。
以上是官方原话,从中能够得知Vue的核心是模板语法和数据渲染。
单向数据流是指数据只能从父级向子级传递数据,子级不能改变父级向子级传递的数据。
双向数据流是指数据从父级向子级传递数据,子级能够经过一些手段改变父级向子级传递的数据。
好比用v-model
、.sync
来实现双向数据流。
双向绑定是指数据模型(Module)和视图(View)之间的双向绑定。
其原理是采用数据劫持结合发布者-订阅者模式的方式来实现。
Vue中先遍历data选项中全部的属性(发布者)用Object.defineProperty
劫持这些属性将其转为getter/setter。读取数据时候会触发getter。修改数据时会触发setter。
而后给每一个属性对应new Dep(),Dep是专门收集依赖、删除依赖、向依赖发送消息的。先让每一个依赖设置在Dep.target
上,在Dep中建立一个依赖数组,先判断Dep.target
是否已经在依赖中存在,不存在的话添加到依赖数组中完成依赖收集,随后将Dep.target
置为上一个依赖。
组件在挂载过程当中都会new一个Watcher实例。这个实例就是依赖(订阅者)。Watcher第二参数式一个函数,此函数做用是更新且渲染节点。在首次渲染过程,会自动调用Dep方法来收集依赖,收集完成后组件中每一个数据都绑定上该依赖。当数据变化时就会在seeter中通知对应的依赖进行更新。在更新过程当中要先读取数据,就会触发Wacther的第二个函数参数。一触发就再次再次自动调用Dep方法收集依赖,同时在此函数中运行patch(diff运算)来更新对应的DOM节点,完成了双向绑定。
而后经过编译将模板转成渲染函数render,执行渲染函数render,在其中建立不一样类型的VNode类,最后整合就能够获得一个虚拟DOM(vnode)。
最后经过patch将vnode和oldVnode进行比较后,生成真实DOM。
在初始化的最后,若是检测到选项有el
属性,则调用vm.$mount
方法挂载vm
,挂载的目标就是把模板渲染成最终的DOM
。
vm.$options
有render函数。由于在不一样构建版本上的挂载过程都不同,因此要对Vue原型上的$mount
方法进行函数劫持。
首先建立一个变量mount将Vue原型上的$mount
方法保存到这个变量上。而后Vue原型上的$mount
方法被一个新的方法覆盖。在这个新方法中调用mount这个原始方法。
经过el属性进行获取DOM元素。若是el是字符串,则使用document.querySelector获取DOM元素并赋值给el。若是获取不到,则建立一个空的div元素并赋值给el。若是el不是字符串,默认el是DOM元素,不进行处理。
判断el是否是html元素或body元素,若是是则给出警告退出程序。
由于挂载后续过程当中须要render函数生成vnode,故要判断$options
选项中是否有render
函数这个属性,若是有直接调用原始的$mount方法。
若是没有,则判断template是否存在。若不存在则将el的outerHTML赋值给template。若存在,若是template是字符串且以#开头,经过选择符获取DOM元素获取innerHTML赋值给template,若是template已是DOM元素类型直接获取innerHTML赋值给template。
而后将template编译成代码字符串并将代码字符串转成render函数,并赋值到vm.$options
的render属性上。
最后调用原始的$mount
方法。
$mount
方法,先触发beforeMount
钩子函数,而后建立一个Watcher实例,在第二参数传入一个函数vm._update
。该函数是首次渲染和更新渲染做用,参数为render函数(vnode),若是vm._vnode
不存在则进行首次渲染。
同时vnode中被劫持的数据自动收集依赖。当vnode中被劫持的数据变化时候触发对应的依赖,从而触发vm._update
进行更新渲染。
最后触发mounted
钩子函数。
JSX就是Javascript和XML结合的一种格式。React发明了JSX,利用HTML语法来建立虚拟DOM。当遇到<
,JSX就当HTML解析,遇到{
就当JavaScript解析。