Vue-Mixins

Mixins

  • 类型Array

  • 详细

    mixins 选项接收一个混入对象的数组。这些混入对象可以像正常的实例对象一样包含实例选项,这些选项将会被合并到最终的选项中,使用的是和 Vue.extend() 一样的选项合并逻辑。也就是说,如果你的混入包含一个 created 钩子,而创建组件本身也有一个,那么两个函数都会被调用。

    Mixin 钩子按照传入顺序依次调用,并在调用组件自身的钩子之前被调用。

  • 示例

    <body>
        <div id="box">
            <button @click='getName'>click</button>
        </div>
    </body>
    
    // 定义一个混入对象
    let myMixin = {
        methods:{
            getName(){
                console.log(1)
            }
        }
    }
    new Vue({
        el:'#box',
        mixins:[myMixin]
    })
    
  • 参考混入

Vue-计算属性

计算属性

模板内的表达式非常便利,但是设计它们的初衷是用于简单运算的。在模板中放入太多的逻辑会让模板过重且难以维护。例如:

<div id="example">
  {{ message.split('').reverse().join('') }}
</div>

在这个地方,模板不再是简单的声明式逻辑。你必须看一段时间才能意识到,这里是想要显示变量 message 的翻转字符串。当你想要在模板中多次引用此处的翻转字符串时,就会更加难以处理。

所以,对于任何复杂逻辑,你都应当使用计算属性

实例

<div id="box">
    {{myname.substring(0,1).toUpperCase() + myname.substring(1)}}

    <p>计算属性:{{getName}}</p>
    <p>计算属性:{{getName}}</p>

    <p>方法:{{getNameMethod()}}</p>
    <p>方法:{{getNameMethod()}}</p>
</div>    
var vm =  new Vue({
    el:"#box",
    data:{
        myname:"retr0"
    },
    methods: {
        getNameMethod(){
            console.log("getNameMethod")
            return this.myname.substring(0,1).toUpperCase() + this.myname.substring(1)
        }
    },
    computed: {
        getName(){
            console.log("getName-computed")
            return this.myname.substring(0,1).toUpperCase() + this.myname.substring(1)
        }
    },
})

计算缓存 VS methods

  1. 计算属性是基于它们的依赖进行缓存的
  2. 计算属性只有在它的相关依赖发生改变时才会重新求值

get() set()

<div id='box'>
    <input type='checkbox' v-model='isAllChecked'>
</div>
new Vue({
    el:'#box',
    data:{
        check:false
    },
    computed:{
        isAllChecked:{
            get(){
                //...doSomeThings
                return some //必须要return
            },
            set(newvalue){
                //doSomeThing(newValue)
            }
        }
    }
})

Vue-表单控件绑定&双向数据绑定

表单控件绑定&双向数据绑定

v-model

基本用法

多选

<div id='box'>
    <input type='checkbox' v-model='check'>
</div>
new Vue({
    el:'#box',
    data:{
        check:true
    }
})

通过v-model绑定了一个数组checkgroup,这样实现每个复选框被点击时,Vue会将该复选框的value值按顺序push到checkgroup中

<div id='box'>
    <input type="checkbox" v-model="checkgroup" value="vue"/>vue
    <input type="checkbox" v-model="checkgroup" value="react"/>react
    <input type="checkbox" v-model="checkgroup" value="jquery"/>jquery
</div>
new Vue({
    el:'#box',
    data:{
        checkgroup:[];
    }
})

单选

<div id='box'>
    <input type="radio" v-model="picked" value="vue" name="aaa"/>vue
    <input type="radio" v-model="picked" value="react"     name="aaa"/>react
    <input type="radio" v-model="picked" value="jquery" name="aaa"/>jquery
</div>
new Vue({
    el:'#box',
    data:{
        picked:""
    }
})

修饰符

.lazy

取代 input 监听 change 事件

<div id='box'>
    <input v-model.lazy='mytext'>
    <!-- 这里的mytext不会实时修改而是在input失去焦点的时候才会修改-->
    {{mytext}} 
</div>
new Vue({
    el:'#box',
    data:{
        mytext:''
    }
})

.number

输入字符串转为有效的数字

<input type="number" v-model.number="mynumber"/>
{{mynumber}}

.trim

输入首尾空格过滤

<input type="text" v-model.trim="myusername"/>
|{{myusername}}|  

Vue-事件处理

事件处理

监听事件

可以用 v-on 指令监听 DOM 事件,并在触发时运行一些 JavaScript 代码。

实例:

<div id="box">
  <button v-on:click="counter++">Add 1</button>
  <p>The button above has been clicked {{ counter }} times.</p>
</div>
var example1 = new Vue({
    el: '#box',
    data: {
        counter: 0
    }
})

事件处理方法

然而许多事件处理逻辑会更为复杂,所以直接把 JavaScript 代码写在 v-on 指令中是不可行的。因此 v-on 还可以接收一个需要调用的方法名称。

实例:

<div id='box'>
    <button @click='fn'>
        click
    </button>
</div>
let vm = new Vue({
    el:'#box',
    data:{
        name:'retr0'
    }
    method:{
    //当@click='fn'不加括号时,可以直接使用形参eve
    //eve是原生 DOM 事件
    //当@click='fn($event)'加括号时,需要传入实参$event
        fn(eve){
            alert(`hello ${this.name}`);
            if(eve){
                alert(eve.target.tagName);
            }
        }
    }
})

内联处理器中的方法

除了直接绑定到一个方法,也可以在内联 JavaScript 语句中调用方法:

<div id="example-3">
  <button v-on:click="say('hi')">Say hi</button>
  <button v-on:click="say('what')">Say what</button>
</div>
new Vue({
    el: '#example-3',
    methods: {
        say: function (message) {
            alert(message)
        }
    }
})

有时也需要在内联语句处理器中访问原始的 DOM 事件。可以用特殊变量 $event 把它传入方法:

<button v-on:click="warn('Form cannot be submitted yet.', $event)">
    Submit
</button>
// ...
methods: {
    warn: function (message, event) {
        // 现在我们可以访问原生事件对象
        if (event) {
            event.preventDefault()
        }
        alert(message)
    }
}

事件修饰符

在事件处理程序中调用 event.preventDefault()event.stopPropagation() 是非常常见的需求。尽管我们可以在方法中轻松实现这点,但更好的方式是:方法只有纯粹的数据逻辑,而不是去处理 DOM 事件细节。

为了解决这个问题,Vue.js 为 v-on 提供了事件修饰符。之前提过,修饰符是由点开头的指令后缀来表示的。

  • .stop
  • .prevent
  • .capture
  • .self
  • .once
  • .passive

.stop

<!-- 阻止事件冒泡 -->
<a v-on:click.stop="doThis"></a>
<!-- 阻止事件冒泡,没有表达式 -->
<a v-on:click.stop></a>

.prevent

<!-- 阻止浏览器默认行为 -->
<div :click.prevent='fn()'></div>
<!-- 阻止默认行为,没有表达式 -->
<div :click.prevent></div>

.self

<!-- 只当事件是从侦听器绑定的元素本身触发时才触发回调 -->
<ul @click.self='fn2()'>
    <li v-for='data in datalist' @click='fn()'>{{data}}</li>
</ul>

.once

<!-- 只触发一次回调 -->
<div @click.once='fn'></div>

如何不用once做到解绑事件:

<!-- 通过状态isActive进行短路 -->
<!-- 需要注意,这里的函数表达式必须加() -->
<div @click='isActive && fn()'>解绑测试</div>

.passive

每次事件产生,浏览器都会去查询一下是否有preventDefault阻止该次事件的默认动作。我们加上passive就是为了告诉浏览器,不用查询了,我们没用 preventDefault阻止默认动作。这里一般用在滚动监听,@scroll,@touchmove 。因为滚动监听过程中,移动每个像素都会产生一次事件,每次都使用内核线程查询prevent会使滑动卡顿。我们通过passive将内核线程查询跳过,可以大大提升滑动的流畅度

<!-- 以 { passive: true } 模式添加侦听器 -->
<div @click.passive='fn'></div>

按键修饰符

<!-- 只有在按下回车键的时才会触发该事件 -->
<input @keyup.enter='fn()'>
<!-- 支持连缀 -->
<input @keyup.enter.ctrl='fn()'>
<!-- 支持keyCode -->
<input @keyup.13='fn()'>

其实通过以下方法完成的:

<div id='box'>
    <input @keyup='fn1($event)'>
</div>
new Vue({
    el:'#box',
    methods:{
        fn1(eve){
            if(eve.keyCode == 13){
                ......
            }
        }
    }
})

Vue-列表渲染

列表渲染

v-for

  • 预期Array | Object | number | string | Iterable (2.6 新增)

  • 用法

    基于源数据多次渲染元素或模板块。此指令之值,必须使用特定语法 alias in expression ,为当前遍历的元素提供别名:

    <div v-for="item in items">
      {{ item.text }}
    </div>
    

    另外也可以为数组索引指定别名 (或者用于对象的键):

    <div v-for="(item, index) in items"></div>
    <div v-for="(val, key) in object"></div>
    <div v-for="(val, name, index) in object"></div>
    

    v-for 的默认行为会尝试原地修改元素而不是移动它们。要强制其重新排序元素,你需要用特殊属性 key 来提供一个排序提示:

    <div v-for="item in items" :key="item.id">
      {{ item.text }}
    </div>
    

    从 2.6 起,v-for 也可以在实现了可迭代协议的值上使用,包括原生的 MapSet。不过应该注意的是 Vue 2.x 目前并不支持可响应的 MapSet 值,所以无法自动探测变更。

    当和 v-if 一起使用时,v-for 的优先级比 v-if 更高。详见列表渲染教程

    v-for 的详细用法可以通过以下链接查看教程详细说明。

  • 参考

key

  1. 跟踪每个节点的身份,从而重用和重新排序现有元素

  2. 理想的 key 值是每项都有的且唯一的 id。data.id

数组更新检测

  1. 使用以下方法操作数组,可以检测变动:

    • push()
    • pop()
    • shift()
    • unshift()
    • splice()
    • sort()
    • reverse()
  2. 使用以下方法操作数组,无法检测到变动

  • filter()
  • concat()
  • slice()
  • map()
  • 新数组替换旧数组
  1. 不能检测以下变动的数组

    vm.items[indexOfItem] = newValue//是无法渲染页面的
    

    解决方案

    1. Vue.set(example1.items, indexOfItem, newValue)

    2. splice

      vm.items.splice(indexOfItem,1,newValue)
      

Vue-条件渲染

条件渲染

v-if

动态创建和删除

  • 用法

    根据表达式的值的 truthiness 来有条件地渲染元素。在切换时元素及它的数据绑定 / 组件被销毁并重建。如果元素是 ` ,将提出它的内容作为条件块。

    当条件变化时该指令触发过渡效果。

    当和 v-if 一起使用时,v-for 的优先级比 v-if 更高。详见列表渲染教程

  • 参考条件渲染 - v-if

<div id="box">
    <div v-if="datalist.length">
        <ul>
            <li v-for='data in datalist'>
                {{data}}
            </li>
        </ul>
    </div>
</div>

v-else

  • 不需要表达式

  • 限制:前一兄弟元素必须有 v-ifv-else-if

  • 用法

    v-if 或者 v-else-if 添加“else 块”。

    可以用于购物车空空如也和有数据的状态切换

    <div v-if="Math.random() > 0.5">
        Now you see me
    </div>
    <div v-else>
        Now you don't
    </div>
    
  • 参考条件渲染 - v-else

v-else-if

2.1.0 新增

  • 类型any

  • 限制:前一兄弟元素必须有 v-ifv-else-if

  • 用法

    表示 v-if 的 “else if 块”。可以链式调用。

    可以用于购物车的状态

    <div v-if="type === 'A'">
      A
    </div>
    <div v-else-if="type === 'B'">
      B
    </div>
    <div v-else-if="type === 'C'">
      C
    </div>
    <div v-else>
      Not A/B/C
    </div>
    
  • 参考条件渲染 - v-else-if

template v-if

在元素上使用 v-if 条件渲染分组

因为 v-if 是一个指令,所以必须将它添加到一个元素上。但是如果想切换多个元素呢?此时可以把一个 元素当做不可见的包裹元素,并在上面使用v-if。最终的渲染结果将不包含 元素。

<template v-if="ok">
    <h1>Title</h1>
    <p>Paragraph 1</p>
    <p>Paragraph 2</p>
</template>

v-show

  • 预期any

  • 用法

    根据表达式之真假值,切换元素的 display CSS 属性。

    当条件变化时该指令触发过渡效果。

  • 参考条件渲染 - v-show

Vue-class与style

class与style

绑定HTML Class

对象语法

<div id='box'>
    <div :class='classObj'>动态绑定ClassName</div>
</div>
new Vue({
    el:'#box',
    data:{
        classObj:{
            aa:1,//true则显示该class名,false则不会
               bb:0,
            cc:1
        }
    }
})

数组语法

<div id='box'>
    <div :class='classArr'>动态绑定ClassName</div>
</div>
new Vue({
    el:'#box',
    data:{
        classArr:['aa','cc']//数组里有的都会显示为className
    }
})

绑定内联样式

对象语法

<div id='box'>
    <div :style='styleObj'>动态修改样式</div>
</div>
new Vue({
    el:'#box',
    data:{
        styleObj:{
            backgroundColor:'#14c145'
        }
        //注意,直接用this.styleObj.属性=val,是无法渲染页面的,必须要使用
        // Vue.set(this.styleObj,属性,val)才可以渲染页面
    }
})

数组语法

<div id='box'>
    <div :class='styleArr'>动态修 改样式</div>
</div>
new Vue({
    el:'#box',
    data:{
        styleArr:[{
            backgroundColor:"#14c145",
            fontSize:'12px'
        }]//注意,往数组中push对象,可以渲染页面
        //this.styleArr.push({color:'red'})
    }
})

#####对象方法
-最简单的绑定(这里的active加不加单引号都可以,以下也一样都能渲染)

:class="{ 'active': isActive }"

1
判断是否绑定一个active

:class="{'active':isActive===-1}"  
或者
:class="{'active':isActive===index}"

绑定并判断多个

第一种(用逗号隔开)
:class="{ 'active': isActive, 'sort': isSort }"
第二种(放在data里面)
//也可以把后面绑定的对象写在一个变量放在data里面,可以变成下面这样
:class="classObject"
data() {
  return {
    classObject:{ active: true, sort:false }
  }
}
第三种(使用computed属性)
:class="classObject"
data() {
  return {
    isActive: true,
    isSort: false
  }
},
computed: {
  classObject: function () {
    return {
      active: this.isActive,
      sort:this.isSort
    }
  }
}

#####数组方法

单纯数组
:class="[isActive,isSort]"
data() {
  return{
    isActive:'active',
    isSort:'sort'
 }
}

数组与三元运算符结合判断选择需要的class
$$
(注意:三元运算符后面的“:”两边的class需要加上单引号,否则不能正确渲染)
:class=”[isActive?’active’:’’]”
或者
:class=”[isActive==1?’active’:’’]”
或者
:class=”[isActive==index?’active’:’’]”
或者
:class=”[isActive==index?’active’:’otherActiveClass’]”
$$
数组对象结合动态判断
//前面这个active在对象里面可以不加单引号,后面这个sort要加单引号

:class="[{ active: isActive }, 'sort']"
或者
:class="[{ active: isActive===1 }, 'sort']"
或者
:class="[{ active: isActive===index }, 'sort']"

Vue-什么是虚拟DOM?

摘要: 什么是虚拟DOM?

Fundebug经授权转载,版权归原作者所有。

前言

Vue.js 2.0引入Virtual DOM,比Vue.js 1.0的初始渲染速度提升了2-4倍,并大大降低了内存消耗。那么,什么是Virtual DOM?为什么需要Virtual DOM?它是通过什么方式去提升页面渲染效率的呢?这是本文所要探讨的问题。

模板转换成视图的过程

在正式介绍 Virtual Dom之前,我们有必要先了解下模板转换成视图的过程整个过程(如下图):

  • Vue.js通过编译将template 模板转换成渲染函数(render ) ,执行渲染函数就可以得到一个虚拟节点树
  • 在对 Model 进行操作的时候,会触发对应 Dep 中的 Watcher 对象。Watcher 对象会调用对应的 update 来修改视图。这个过程主要是将新旧虚拟节点进行差异对比,然后根据对比结果进行DOM操作来更新视图。

简单点讲,在Vue的底层实现上,Vue将模板编译成虚拟DOM渲染函数。结合Vue自带的响应系统,在状态改变时,Vue能够智能地计算出重新渲染组件的最小代价并应到DOM操作上。

img

我们先对上图几个概念加以解释:

  • 渲染函数:渲染函数是用来生成Virtual DOM的。Vue推荐使用模板来构建我们的应用界面,在底层实现中Vue会将模板编译成渲染函数,当然我们也可以不写模板,直接写渲染函数,以获得更好的控制。
  • VNode 虚拟节点:它可以代表一个真实的 dom 节点。通过 createElement 方法能将 VNode 渲染成 dom 节点。简单地说,vnode可以理解成节点描述对象,它描述了应该怎样去创建真实的DOM节点。
  • **patch(也叫做patching算法):虚拟DOM最核心的部分,它可以将vnode渲染成真实的DOM,这个过程是对比新旧虚拟节点之间有哪些不同,然后根据对比结果找出需要更新的的节点进行更新。这点我们从单词含义就可以看出, patch本身就有补丁、修补的意思,其实际作用是在现有DOM上进行修改来实现更新视图的目的。Vue的Virtual DOM Patching算法是基于Snabbdom**的实现,并在些基础上作了很多的调整和改进。

Virtual DOM 是什么?

Virtual DOM 其实就是一棵以 JavaScript 对象( VNode 节点)作为基础的树,用对象属性来描述节点,实际上它只是一层对真实 DOM 的抽象。最终可以通过一系列操作使这棵树映射到真实环境上。

简单来说,可以把Virtual DOM 理解为一个简单的JS对象,并且最少包含标签名( tag)、属性(attrs)和子元素对象( children)三个属性。不同的框架对这三个属性的命名会有点差别。

对于虚拟DOM,咱们来看一个简单的实例,就是下图所示的这个,详细的阐述了模板 → 渲染函数 → 虚拟DOM树 → 真实DOM的一个过程

img

Virtual DOM 作用是什么?

虚拟DOM的最终目标是将虚拟节点渲染到视图上。但是如果直接使用虚拟节点覆盖旧节点的话,会有很多不必要的DOM操作。例如,一个ul标签下很多个li标签,其中只有一个li有变化,这种情况下如果使用新的ul去替代旧的ul,因为这些不必要的DOM操作而造成了性能上的浪费。

为了避免不必要的DOM操作,虚拟DOM在虚拟节点映射到视图的过程中,将虚拟节点与上一次渲染视图所使用的旧虚拟节点(oldVnode)做对比,找出真正需要更新的节点来进行DOM操作,从而避免操作其他无需改动的DOM。

其实虚拟DOM在Vue.js主要做了两件事:

  • 提供与真实DOM节点所对应的虚拟节点vnode
  • 将虚拟节点vnode和旧虚拟节点oldVnode进行对比,然后更新视图

给大家推荐一个好用的BUG监控工具Fundebug,欢迎免费试用!

为何需要Virtual DOM?

  • 具备跨平台的优势

由于 Virtual DOM 是以 JavaScript 对象为基础而不依赖真实平台环境,所以使它具有了跨平台的能力,比如说浏览器平台、Weex、Node 等。

  • 操作 DOM 慢,js运行效率高。我们可以将DOM对比操作放在JS层,提高效率。

因为DOM操作的执行速度远不如Javascript的运算速度快,因此,把大量的DOM操作搬运到Javascript中,运用patching算法来计算出真正需要更新的节点,最大限度地减少DOM操作,从而显著提高性能。

Virtual DOM 本质上就是在 JS 和 DOM 之间做了一个缓存。可以类比 CPU 和硬盘,既然硬盘这么慢,我们就在它们之间加个缓存:既然 DOM 这么慢,我们就在它们 JS 和 DOM 之间加个缓存。CPU(JS)只操作内存(Virtual DOM),最后的时候再把变更写入硬盘(DOM)

  • 提升渲染性能

Virtual DOM的优势不在于单次的操作,而是在大量、频繁的数据更新下,能够对视图进行合理、高效的更新。

为了实现高效的DOM操作,一套高效的虚拟DOM diff算法显得很有必要。我们通过patch 的核心—-diff 算法,找出本次DOM需要更新的节点来更新,其他的不更新。比如修改某个model 100次,从1加到100,那么有了Virtual DOM的缓存之后,只会把最后一次修改patch到view上。那diff 算法的实现过程是怎样的?

diff 算法

img

Vue的diff算法是基于snabbdom改造过来的,仅在同级的vnode间做diff,递归地进行同级vnode的diff,最终实现整个DOM树的更新。因为跨层级的操作是非常少的,忽略不计,这样时间复杂度就从O(n3)变成O(n)。

diff 算法包括几个步骤:

  • 用 JavaScript 对象结构表示 DOM 树的结构;然后用这个树构建一个真正的 DOM 树,插到文档当中
  • 当状态变更的时候,重新构造一棵新的对象树。然后用新的树和旧的树进行比较,记录两棵树差异
  • 把所记录的差异应用到所构建的真正的DOM树上,视图就更新了

img

diff 算法的实现过程

diff 算法本身非常复杂,实现难度很大。本文去繁就简,粗略介绍以下两个核心函数实现流程:

  • patch(container,vnode) :初次渲染的时候,将VDOM渲染成真正的DOM然后插入到容器里面。
  • patch(vnode,newVnode):再次渲染的时候,将新的vnode和旧的vnode相对比,然后之间差异应用到所构建的真正的DOM树上。

1. patch(container,vnode)

通过这个函数可以让VNode渲染成真正的DOM,我们通过以下模拟代码,可以了解大致过程:

function createElement(vnode) {    
var tag = vnode.tag  
var attrs = vnode.attrs || {}    
var children = vnode.children || []    
if (!tag) {       
 return null  
  }    
// 创建真实的 DOM 元素    
var elem = document.createElement(tag)   
 // 属性    
var attrName    
for (attrName in attrs) {    
    if (attrs.hasOwnProperty(attrName)) { 
           // 给 elem 添加属性
           elem.setAttribute(attrName, attrs[attrName])
        }
    }
    // 子元素
    children.forEach(function (childVnode) {
        // 给 elem 添加子元素,如果还有子节点,则递归的生成子节点。
        elem.appendChild(createElement(childVnode))  // 递归
    })    // 返回真实的 DOM 元素   
 return elem
}

2. patch(vnode,newVnode)

这里我们只考虑vnode与newVnode如何对比的情况:

function updateChildren(vnode, newVnode) {
    var children = vnode.children || []
    var newChildren = newVnode.children || []
  // 遍历现有的children
    children.forEach(function (childVnode, index) {
        var newChildVnode = newChildren[index]
  // 两者tag一样
        if (childVnode.tag === newChildVnode.tag) {
            // 深层次对比,递归
            updateChildren(childVnode, newChildVnode)
        } else { 
  // 两者tag不一样
           replaceNode(childVnode, newChildVnode) 
       }
    }
)}

给大家推荐一个好用的BUG监控工具Fundebug,欢迎免费试用!

参考

关于Fundebug

Fundebug专注于JavaScript、微信小程序、微信小游戏、支付宝小程序、React Native、Node.js和Java线上应用实时BUG监控。 自从2016年双十一正式上线,Fundebug累计处理了10亿+错误事件,付费客户有阳光保险、核桃编程、荔枝FM、掌门1对1、微脉、青团社等众多品牌企业。欢迎大家免费试用

Vue-介绍

Vue介绍

官方介绍

Vue (读音 /vjuː/,类似于view) 是一套用于构建用户界面的渐进式框架。与其它大型框架不同的是,Vue 被设计为可以自底向上逐层应用。Vue 的核心库只关注视图层,不仅易于上手,还便于与第三方库或既有项目整合。另一方面,当与现代化的工具链以及各种支持类库结合使用时,Vue 也完全能够为复杂的单页应用提供驱动。

渐进式

框架做分层设计,每层都可选,不同层可以灵活接入其他方案。而当你都想用官方的实现时,会发现也早已准备好,各层之间包括配套工具都能比接入其他方案更便捷地协同工作。一个个放入,放多少就做多少。

MV*模式(MVC/MVP/MVVM)

MVC

model view controller

介绍

用户的对View操作以后,View捕获到这个操作,会把处理的权利交移给Controller(Pass calls);Controller会对来自View数据进行预处理、决定调用哪个Model的接口;然后由Model执行相关的业务逻辑(数据请求);当Model变更了以后,会通过观察者模式(Observer Pattern)通知View;View通过观察者模式收到Model变更的消息以后,会向Model请求最新的数据,然后重新更新界面。

缺点

把业务逻辑和展示逻辑分离,模块化程度高。但由于View是强依赖特定的Model的,所以View无法组件化,无法复用

MVP

model view presenter

介绍

和MVC模式一样,用户对View的操作都会从View交移给Presenter。Presenter会执行相应的应用程序逻辑,并且对Model进行相应的操作;而这时候Model执行完业务逻辑以后,也是通过观察者模式把自己变更的消息传递出去,但是是传给Presenter而不是View。Presenter获取到Model变更的消息以后,通过View提供的接口更新界面

缺点

View不依赖Model,View可以进行组件化。但Model->View的手动同步逻辑麻烦,维护困难

MVVM

介绍

MVVM的调用关系和MVP一样。但是,在ViewModel当中会有一个叫Binder,或者是Data-binding engine的东西。你只需要在View的模版语法当中,指令式地声明View上的显示的内容是和Model的哪一块数据绑定的。当ViewModel对进行Model更新的时候,Binder会自动把数据更新到View上去,当用户对View进行操作(例如表单输入),Binder也会自动把数据更新到Model上去。这种方式称为:Two-way data-binding,双向数据绑定。可以简单而不恰当地理解为一个模版引擎,但是会根据数据变更实时渲染

特点

解决了MVP大量的手动View和Model同步的问题,提供双向绑定机制。提高了代码的可维护性。对于大型的图形应用程序,视图状态较多,ViewModel的构建和维护的成本都会比较高。

引入

script标签引入

<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>

Vue cli

Vue 提供了一个官方的 CLI,为单页面应用 (SPA) 快速搭建繁杂的脚手架

npm install ‐g @vue/cli

数据绑定原理

当你把一个普通的 JavaScript 对象传入 Vue 实例作为 data 选项,Vue 将遍历此对象所有的属性,并使用Object.defineProperty把这些属性全部转为 getter/setter。Object.defineProperty 是 ES5 中一个无法 shim 的特性,这也就是 Vue 不支持 IE8 以及更低版本浏览器的原因。

每个组件实例都对应一个 watcher 实例,它会在组件渲染的过程中把“接触”过的数据属性记录为依赖。之后当依赖项的 setter 触发时,会通知watcher,从而使它关联的组件重新渲染。

Object.defineProperty

Object.defineProperty() 方法会直接在一个对象上定义一个新属性,或者修改一个对象的现有属性, 并返回这个对象。

语法

Object.defineProperty(obj, prop, descriptor)
参数
  • obj

    要在其上定义属性的对象。

  • prop

    要定义或修改的属性的名称。

  • descriptor

    将被定义或修改的属性描述符。

返回值

被传递给函数的对象

在ES6中,由于 Symbol类型的特殊性,用Symbol类型的值来做对象的key与常规的定义或修改不同,而Object.defineProperty 是定义key为Symbol的属性的方法之一。

实例

let obj = {};
    Object.defineProperty(obj,"myname",{
        get(data){
            console.log("被访问" + data);
        },
        set(data){
            console.log("被修改" + data)
        }
    })

注意

Vue3 的 变化Object.defineProperty有以下缺点。

  1. 无法监听es6的Set、Map 变化;
  2. 无法监听Class类型的数据;
  3. 属性的新加或者删除也无法监听;
  4. 数组元素的增加和删除也无法监听。

兼容性

针对Object.defineProperty的缺点,ES6 Proxy都能够完美得解决,它唯一的缺点就是,对IE不友好,所以vue3在检测到如果是使用IE的情况下(没错,IE11都不支持Proxy),会自动降级Object.defineProperty的数据监听系统。