vue2.x 原理

MVVM

  • 数据驱动视图:view(DOM / 模板)、Model(模型 data) 、viewModel(vue核心、vue实例:监听事件,指令等)
  • view 和 Model 通过 viewModel 实现互动

在这里插入图片描述


响应式数据原理

  • Object.defineproperty 接收3个参数
  • 参数1:源数据
  • 参数2:属性
  • 参数3:配置项
  • writable:可读写、enumerable:可遍历、configurable:可配置/删除、value:默认值、get:读取、set:修改
  • 缺陷1:深度监听要递归到底,计算量大,一次性计算量大
  • 缺陷2:无法监听新增/删除属性( Vue.set Vue.delete )

代码演示:

function updateView() {
  // ... do something
  console.log('视图更新')
}

重新定义数组原型: defineproperty 无法满足数组的监听

const arrayFuncs = ['push', 'pop', 'shift', 'unshift', 'splice'] // 模拟数组所有的原型方法
const oldArrayProperty = Array.prototype
const vueArray = Object.create(oldArrayProperty)
// vueArray 添加实例方法
arrayFuncs.forEach(methodName => {
  vueArray[methodName] = function () {
    updateView() // 先执行视图更新
    // Array.prototype.push.call(this,...arguments)
    oldArrayProperty[methodName].call(this, ...arguments) // 再执行数组原型方法
  }
})

以上代码:oldArrayProperty 保存 Array 的原型,声明新对象 vueArray 且隐式原型指向 Array 的原型,vueArray 重新封装 Array 原有的原型方法并作为自己的实例方法,执行完视图更新操作之后再使用 Array 的原型方法。


重新定义属性 :

function observer(target) {
  // 只监听对象
  if (typeof target !== 'object' || target === null) {
    return target
  }
  // 数组
  if (Array.isArray(target)) {
    target.__proto__ = vueArray
    return target
  }
  for (let key in target) {
    defineReactive(target, key, target[key])
  }
}

递归深度监听:

function defineReactive(target, key, value) {
  observer(value) // 递归深度监听
  Object.defineProperty(target, key, {
    get() {
      return value
    },
    set(newValue) {
      if (newValue !== value) {
        /**
         * 为什么修改后需要继续深度监听
         * 有可能修改的值是一个引用类型,那么引用类型需要继续深度监听
         */
        observer(newValue) // 继续深度监听
        /**
         * 注意,value 一直在闭包中
         * 此处设置完之后,再 get 时也是会获取最新的值
         */
        value = newValue
        updateView() // 触发更新视图
      }
    }
  })
}

准备数据:

const data = {
  name: 'zhangsan',
  age: 20,
  info: {
    address: '北京'
  },
  arr: [10, 20]
}

// 监听数据
observer(data)
data.name = 'lisi'
data.info = {
  id: '001'
}
data.arr.push(30)
console.log(data, '??')

with

常规定义对象引用类型,以下 obj.c 输出结果为undefined

const obj = {
  a: 100,
  b: 200,
}
console.log(obj.a)
console.log(obj.b)
console.log(obj.c) // undefined

使用with能改变{}内自由变量的查找方式(当作obj的属性来查找)、缺陷会破坏作用域规则

with (obj) {
  console.log(a)
  console.log(b)
  console.log(c) // 报错
}

模板编译

vue-template-compile将模板编译成render函数,执行render生成vnode,执行patch函数将虚拟DOM渲染成真实DOM;

以下 vue 源码 缩写函数含义:

function installRenderHelpers (target) {
    target._o = markOnce;
    target._n = toNumber;
    target._s = toString;
    target._l = renderList;
    target._t = renderSlot;
    target._q = looseEqual;
    target._i = looseIndexOf;
    target._m = renderStatic;
    target._f = resolveFilter;
    target._k = checkKeyCodes;
    target._b = bindObjectProps;
    target._v = createTextVNode;
    target._e = createEmptyVNode;
    target._u = resolveScopedSlots;
    target._g = bindObjectListeners;
    target._d = bindDynamicKeys;
    target._p = prependModifier;
}

const compiler = require("vue-template-compiler")
const template = `<p>{{message}}</p>`
const res = compiler.compile(template)

以上编译结果:

with(this){return _c('p',[_v(_s(message))])}

thisconst vm = new Vue()_ccreateElement 类似于h函数,p 为标签,_v 对应createTextVNode:创建text节点子元素,_s对应toString:创建string类型字符串,完整结果为以下代码

with (this) {
  return createElement('p', [createTextVNode(toString(message))])
}

children

const template = `
  <div id="div1" class="container">
      <img :src="imgUrl"/>
  </div>
with (this) {
  return _c(
    "div", // div标签
    { staticClass: "container", attrs: { id: "div1" } }, // 属性
    [
      // 创建IMG标签
      _c("img", { attrs: { src: imgUrl } }),
    ]
  )
}

v-if

const template = `
    <div>
        <p v-if="flag === 'a'">A</p>
        <p v-else>B</p>
    </div>
`
with (this) {
  return _c(
    "div", 
    [flag === "a" ? 
    _c("p", [_v("A")]) : 
    _c("p", [_v("B")])]
  )
}

v-for

const template = `
    <ul>
        <li v-for="item in list" :key="item.id">{{item.title}}</li>
    </ul>
`
with (this) {
  return _c(
    'ul',
    _l(list, function (item) {
      return _c('li', { key: item.id }, [_v(_s(item.title))])
    }),
    0
  )
}

event

const template = `
    <button @click="clickHandler">submit</button>
`
with (this) {
  return _c('button', { on: { click: clickHandler } }, [_v('submit')])
}

定义组件: template 形式

Vue.component('my-component', {
  template: `<div id='box'>{{name}}</div> `,
  data() {
    return {
      name: '张三'
    }
  }
})

定义组件: h = createElement

<body>
	<div id="root">
	  <my-component />
	</div>
	<script>
		Vue.component('my-component', {
		  render(h) {
		    return h(
		      'div',
		      {
		        attrs: {
		          id: 'box'
		        }
		      },
		      '张三'
		    )
		  }
	  })
	  new Vue({
	    el: '#root'
	  })
	</script>
</body>

渲染过程

  • 渲染过程:解析 vue 模板,生成 render 函数 —> 执行 render 函数( h 函数 )生成 vnode —> patch(elem,vnode) 渲染出真实
  • 更新过程:data 改变触发 setter —> 重新执行 render 函数 —> 生成 newVnode —> patch(vnode,newVnode)
  • Vue 是异步渲染的,无论方法中对 data 改变多少次都会汇总成一次
  • Component render function 即模板编译成 render 函数,执行 render 函数渲染真实 dom 结构,同时会 touchdata,即会触发 data 中的 getterwatcher 则会收集起来进行观察,当进行数据的更改时则会触发 setter并通知进行 re-render

在这里插入图片描述

history 路由用的是 api pushState replaceState popState

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值