2021-07-29

重新手写一个Vue

该版把上一次的数据修改就更新全部页面改为了局部更新,相比于上一版的在数据绑定上不是简单的一个监听set再全部更新,具体见下文。

总体流程

仍然是根据自己理解来实现的绑定,相较于上一版的数据更新就全部刷新,这次改成了部分页面更改,总体流程大致如图:(字本来就丑,那个笔芯写更丑了,希望能看懂吧)

QQ图片20210728143535.jpg

这里就从头介绍下怎样实现整个流程的

createApp

这里是整个Vue的入口,通过传入options参数会将里面的data,methods等挂载到Vue实例上,再通过代理,让对vm的属性访问转换为对vm.$data中属性的访问:

static createApp(options) {  //? 将data代理到vm上  const vm = new Proxy(new Vue(), {   get(target, p) {    if (Reflect.get(target, p)) {     return Reflect.get(target, p);    } else {     return target.$data[p]._isref ? target.$data[p].value : target.$data[p];    }   },   set(target, p, value) {    if (target[p]) {     Reflect.set(target, p, value);    } else if (target.$data[p]?._isref) {     Reflect.set(target.$data[p], "value", value);    } else {     Reflect.set(target.$data, p, value);    }    return true;   },  });  options.onBeforCreate?.call(vm);  vm.$data = options.data.call(vm);  new Observer(vm).observeData(); //! 将data的数据转为响应式  for (const key in options.methouds) {   vm.$methouds[key] = options.methouds[key].bind(vm);  }  options.onCreated?.call(vm);  return vm; }

将data中的数据转换为响应式

这个步骤通过Observer实例中的observeData来进行,我这里通过Proxy来实现(Vue2.x中使用Object.defineProperty)。

import Dep from "./dep.js";const dep = new Dep();export default class Observer { constructor(vm) {  this.vm = vm; } observeData() {  const data = this.vm.$data;  for (const key in data) {   data[key] = this.ref(data[key]);  } } // *===============↓ 将数据转换为响应式数据的方法 ↓===============* // reactive(data) {  //? 如果对象里还有对象,递归实现响应式  for (const key in data) {   if (typeof data[key] === "object") {    data[key] = this.reactive(data[key]);   }  }  return new Proxy(data, {   get(target, p) {    window.target && dep.add(window.target);    window.target = null; //? 将watch实例保存后删除    return Reflect.get(target, p);   },   //todo 修改对象属性后修改Vnode   set(target, p, value) {    target._isref     ? Reflect.set(target, "value", value)     : Reflect.set(target, p, value);    dep.notify();    return true;   },  }); } ref(data) {  //? 基本数据类型会被包装为对象再进行代理  if (typeof data != "object") {   data = {    value: data,    _isref: true,    toSting() {     return this.value;    },   };  }  return this.reactive(data); }}

这里在get上设置了dep.add,在第一次渲染页面的时候会读取到对应的$data中的属性,在这个时候将这个属性的位置和一个用来更新视图的回调函数打包进Watcher的实例再放入dep中储存起来,在以后数据更新时会触发set,通知dep调用储存的所有watcher实例上的update方法,update方法会比较储存的旧值来决定是否触发回调函数来更新视图。

Dep:

import { nextTick } from "./util.js";export default class Dep { constructor() {  this.watchers = [];  this.lock = true; } add(watcher) {  this.watchers.push(watcher); } notify() {  //? 放入微任务队列,只要触发一次notify就不再触发,在微任务里更新视图,这样所有数据都更新后再触发更新  if (this.lock) {   this.lock = false;   nextTick(() => {    this.watchers.forEach((watcher) => {     watcher.update(); //? 用watcher实例的update更新视图    });    this.lock = true;   });  } }}

Watcher:

import { getByPath } from "./util.js";export default class Watcher { constructor(vm, key, cb) {  this.vm = vm;  this.key = key; //? 代表该数据在$data哪里的字符串  this.cb = cb; //? 更新页面的回调函数  window.target = this;  //! 获得旧数据,同时触发vm[key]的get把上面一行设置watcher实例push进dep 见observer.js  this.oldValue = getByPath(vm, key); } //? dep调用notify来调用所有的update更新视图 update() {  let newValue = getByPath(this.vm, this.key);  if (newValue === this.oldValue) return;  this.oldValue = newValue;  this.cb(newValue); }}

为了使用方便,这里把Watcher的实例化过程挂载到vm上,实例化Watcher并推入dep的过程全由vm.$watche完成:

class Vue { constructor() {  this.$watch = function (key, cb) {   new Watcher(this, key, cb);  };  }}

页面渲染

通过修改原来的第一版渲染函数,这里改为了挨个读取节点来转换,通过读取每个节点的字符串形式来把数据替换或把方法挂载:

export default function render($el, vm) { const nodes = $el.children; Array.prototype.forEach.call(nodes, (el) => {  if (el.children.length > 0) {   render(el, vm); //? 递归渲染子......

原文转载:http://www.shaoqun.com/a/892299.html

跨境电商:https://www.ikjzd.com/

亿恩网:https://www.ikjzd.com/w/1461

友家速递:https://www.ikjzd.com/w/1341

prime:https://www.ikjzd.com/w/129


该版把上一次的数据修改就更新全部页面改为了局部更新,相比于上一版的在数据绑定上不是简单的一个监听set再全部更新,具体见下文。总体流程仍然是根据自己理解来实现的绑定,相较于上一版的数据更新就全部刷新,这次改成了部分页面更改,总体流程大致如图:(字本来就丑,那个笔芯写更丑了,希望能看懂吧)这里就从头介绍下怎样实现整个流程的createApp这里是整个Vue的入口,通过传入options参数会将里面的
四川雅安哀悼日是什么时候开始?:http://www.30bags.com/a/402469.html
四川雅安碧峰峡什么时候去最好?:http://www.30bags.com/a/419947.html
四川雅安成都等地今天仍有暴雨 局地大暴雨谨防泥石流:http://www.30bags.com/a/425299.html
四川雅安地震了,还能去九寨沟吗?:http://www.30bags.com/a/402504.html
口述:我和小姨子有了性关系:http://lady.shaoqun.com/a/77111.html
老公洗澡老婆被上王总 当着老公被他上司干:http://lady.shaoqun.com/a/274803.html
你太大了岳你太紧疼了 岳两腿中间流水了:http://www.30bags.com/m/a/249859.html
大狼狗大干秦丽娟 狗狗巨大用力挺入她的体内 凌淑娟被一群野狗上:http://www.30bags.com/m/a/249678.html
深圳宝安科技馆8月展览汇总(持续更新):http://www.30bags.com/a/517601.html
2021时尚深圳展蝶讯馆展览好看吗:http://www.30bags.com/a/517602.html
2021时尚深圳蝶讯馆观展攻略:http://www.30bags.com/a/517603.html
深圳欢乐谷夏浪音乐节有朱星杰吗:http://www.30bags.com/a/517604.html

No comments:

Post a Comment