2021-07-28

重新手写一个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/902598.html

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

review:https://www.ikjzd.com/w/2735

贝贝官网:https://www.ikjzd.com/w/1321

promoted:https://www.ikjzd.com/w/971


该版把上一次的数据修改就更新全部页面改为了局部更新,相比于上一版的在数据绑定上不是简单的一个监听set再全部更新,具体见下文。总体流程仍然是根据自己理解来实现的绑定,相较于上一版的数据更新就全部刷新,这次改成了部分页面更改,总体流程大致如图:(字本来就丑,那个笔芯写更丑了,希望能看懂吧)这里就从头介绍下怎样实现整个流程的createApp这里是整个Vue的入口,通过传入options参数会将里面的
阿里巴巴 批发:https://www.ikjzd.com/w/1084
taofenba:https://www.ikjzd.com/w/1725
浙江缙云有个神秘山洞,春秋智人隐居于此,传说曾用五计灭掉吴国_倪翁:http://www.30bags.com/a/219851.html
浙江缙云有一座"仙都观",就在5A级旅游景区内,却几乎没有游客_景点:http://www.30bags.com/a/220436.html
浙江缙云有座"天下第一峰",高度仅170.8米,却是仙都颜值担当_景区:http://www.30bags.com/a/220023.html
浙江缙云有座赵侯祠,传说供奉的神医精通法术,能将清水变成美酒_赵炳:http://www.30bags.com/a/219903.html
昨天被三个猛男弄个半死 女婿的那个很大:http://lady.shaoqun.com/a/283415.html
女刑警被两个黑人挺进 被蹂躏得死去活来粗大:http://lady.shaoqun.com/a/247109.html
2021夏天去哪旅游好玩 夏季出游好去处 :http://www.30bags.com/a/530746.html
2021武汉有什么好玩的地方 武汉好玩的地方有哪些 :http://www.30bags.com/a/530747.html
Tik Tok代运营丨Tik Tok达人计划又是什么?:https://www.ikjzd.com/articles/146994
"我和400多个女孩上过床,但我知道我是一名优秀的归国PUA家庭教师。":http://lady.shaoqun.com/a/439510.html

No comments:

Post a Comment