从接下来的一段时间里,Mg要进行阅读源码的工作。再阅读源码前,梳理一下准备工作。

7项重要储备

  1. Flow 基本语法
  2. 发布/订阅模式
  3. ES6+ 语法
  4. 原型链、闭包
  5. 函数柯里化
  6. event loop

1.flow

1.1 什么是flow

没有类型的静态检查是 JavaScript 语言的先天缺失,所有很多变量类型相关的问题只能在运行阶段暴露出来。为了使 JavaScript 语言的类型更加安全,业界的实践有 TypeScript;这些都需要你重新学习一套语言语法,然后由编译器把相应的代码编译成原生的 JavaScript 代码;在一个现有的系统中把当前代码改成 TypeScript 成本比较高,需要重写所有逻辑。

Facebook 推出的 Flow 是另一个思路。Flow 是一个静态类型检测工具;在现有项目中加上类型标注后,可以在代码阶段就检测出对变量的不恰当使用。Flow 弥补了 JavaScript 天生的类型系统缺陷。利用 Flow 进行类型检查,可以使你的项目代码更加健壮,确保项目的其他参与者也可以写出规范的代码;而 Flow 的使用更是方便渐进式的给项目加上严格的类型检测。那么这么好的 Flow,要怎么开始使用呢?

1.2 基础类型检查

Flow 支持原始数据类型,有如下几种:

boolean
number
string
null
void( 对应 undefined )

如下使用:

let str:string = 'str';
// 重新赋值
str = 3 // 报错

1.3 更多详情有如下资料

官方文档:flow.org/en/

Flow 的使用入门:zhuanlan.zhihu.com/p/26204569

欲知详情,可进一步阅读

2.发布/订阅模式

Vue 的双向绑定机制采用数据劫持结合发布/订阅模式实现的: 通过 Object.defineProperty() 来劫持各个属性的 setter,getter,在数据变动时发布消息给订阅者,触发相应的监听回调。

而在Vue3.0中,使用proxy代替原来的Object.defineProperty()以实现发布订阅模式。

从Vue的源码来看,Vue的双向绑定主要做了2件事

  1. 数据劫持
  2. 添加观察者
// 老版本通过 Object.defineProperty 递归可以实现
// src/core/observer/index.js
Object.defineProperty(obj, key, {
enumerable: true,
configurable: true,
get: function reactiveGetter () {
const value = getter ? getter.call(obj) : val
if (Dep.target) {
dep.depend()
if (childOb) {
childOb.dep.depend()
}
if (Array.isArray(value)) {
dependArray(value)
}
}
return value
},
set: function reactiveSetter (newVal) {
const value = getter ? getter.call(obj) : val
if (newVal === value || (newVal !== newVal && value !== value)) {
return
}
if (setter) {
setter.call(obj, newVal)
} else {
val = newVal
}
childOb = !shallow && observe(newVal)
dep.notify()
}
})

这里就是劫持了对象的get和set方法。在所代理的属性的get方法中,当dep.Target存在的时候会调用 dep.depend().

划重点:2行代码

  1. Object.defineProperty
  2. dep.depend()
// 最新版可以通过 Proxy 实现
Proxy(data, {
get(target, key) {
return target[key];
},
set(target, key, value) {
let val = Reflect.set(target, key, value);
_that.$dep[key].forEach(item => item.update());
return val;
}
})

从上面的代码看出,无非就劫持了对象的get和set方法,在数据劫持之外最重要的部分就是 Dep 和 Watcher,这其实是一个观察者模式。用最简单的代码实现以下 Vue 的观察者模式。

观察者模式实现:(源码精简)

    // 观察者
class Dep {
constructor() {
this.subs = []
} addSub(sub) {
this.subs.push(sub)
} depend() {
if (Dep.target) {
Dep.target.addDep(this);
}
} notify() {
this.subs.forEach(sub => sub.update())
}
} // 被观察者
class Watcher {
constructor(vm, expOrFn) {
this.vm = vm;
this.getter = expOrFn;
this.value;
} get() {
Dep.target = this; var vm = this.vm;
var value = this.getter.call(vm, vm);
return value;
} evaluate() {
this.value = this.get();
} addDep(dep) {
dep.addSub(this);
} update() {
console.log('更新, value:', this.value)
}
} // 观察者实例
var dep = new Dep(); // 被观察者实例
var watcher = new Watcher({x: 1}, (val) => val);
watcher.evaluate(); // 观察者监听被观察对象
dep.depend() dep.notify()

划重点:3件事

  1. 通过 watcher.evaluate() 将自身实例赋值给 Dep.target
  2. 调用 dep.depend() 将dep实例将 watcher 实例 push 到 dep.subs中
  3. 通过数据劫持,在调用被劫持的对象的 set 方法时,调用 dep.subs 中所有的 watcher.update()

从此。双向绑定完成。

3 ES6+ 语法

3.1 export default 和 export 的区别

1.export
//a.js
export const str = "神奇元";
//b.js
import { str } from 'a'; // 导入的时候需要花括号 2.export default
//a.js
const str = "神奇元";
export default str;
//b.js
import str from 'a'; // 导入的时候无需花括号

3.2 箭头函数

主要提两点

  1. 箭头函数中的 this 指向是固定不变的,即是在定义函数时的指向
  2. 而普通函数中的 this 指向时变化的,即是在使用函数时的指向

3.3 箭头函数

Class 可以通过 extends 关键字实现继承,这比 ES5 的通过修改原型链实现继承,要清晰和方便很多。

class staff {
constructor() {
this.company = "ABC";
this.test = [1, 2, 3];
}
companyName() {
return this.company;
}
}
class employee extends staff {
constructor(name, profession) {
super();
this.employeeName = name;
this.profession = profession;
}
} // 将父类原型指向子类
let instanceOne = new employee("Andy", "A");
let instanceTwo = new employee("Rose", "B");
instanceOne.test.push(4);
// 测试
console.log(instanceTwo.test); // [1,2,3]
console.log(instanceOne.companyName()); // ABC
// 通过 Object.getPrototypeOf() 方法可以用来从子类上获取父类
console.log(Object.getPrototypeOf(employee) === staff)
// 通过 hasOwnProperty() 方法来确定自身属性与其原型属性
console.log(instanceOne.hasOwnProperty('test')) // true
// 通过 isPrototypeOf() 方法来确定原型和实例的关系
console.log(staff.prototype.isPrototypeOf(instanceOne)); // true

super 关键字,它在这里表示父类的构造函数,用来新建父类的 this 对象。

  • 子类必须在 constructor 方法中调用 super 方法,否则新建实例时会报错。这是因为子类没有自己的this 对象,而是继承父类的 this 对象,然后对其进行加工。
  • 只有调用 super 之后,才可以使用 this 关键字,否则会报错。这是因为子类实例的构建,是基于对父类实例加工,只有 super 方法才能返回父类实例。
`super` 虽然代表了父类 `A` 的构造函数,但是返回的是子类 `B` 的实例,即` super` 内部的 `this ` 指的是 `B`,因此 `super()` 在这里相当于 A.prototype.constructor.call(this)

ES5 和 ES6 实现继承的区别

ES5 的继承,实质是先创造子类的实例对象 this,然后再将父类的方法添加到 this 上面(Parent.apply(this))。

ES6 的继承机制完全不同,实质是先创造父类的实例对象 this (所以必须先调用 super() 方法),然后再用子类的构造函数修改 this

3.4 proxy

在Vue3.0版本的 Vue 中,会使用 proxy 代替 Object.defineProperty 完成数据劫持的工作。

尤大说,这个新的方案会使初始化速度加倍,于此同时内存占用减半。

proxy 对象的用法:

var proxy = new Proxy(target, handler);

new Proxy() 即生成一个 Proxy 实例。target 参数表示所要拦截的目标对象,handler 参数也是一个对象,用来定制拦截行为。

var proxy = new Proxy({}, {
get: function (obj, prop) {
console.log('get 操作')
return obj[prop];
},
set: function (obj, prop, value) {
console.log('set 操作')
obj[prop] = value;
}
}); proxy.num = 2; // 设置 set 操作 console.log(proxy.num); // 设置 get 操作 // 2

除了 get 和 set 之外,proxy 可以拦截多达 13 种操作。

注意,proxy 的最大问题在于浏览器支持度不够,IE 完全不兼容。

......篇幅过长~大家都看累了吧,请转下一集!

学习Vue源码前的几项必要储备(一)的更多相关文章

  1. 学习Vue源码前的几项必要储备(二)

    7项重要储备 Flow 基本语法 发布/订阅模式 ES6+ 语法 原型链.闭包 函数柯里化 event loop 接上讲 聊到了ES6的几个重要语法,加下来到第四点继续开始. 4.原型链.闭包 原型链 ...

  2. 一起学习vue源码 - Object的变化侦测

    作者:小土豆biubiubiu 博客园:www.cnblogs.com/HouJiao/ 掘金:https://juejin.im/user/58c61b4361ff4b005d9e894d 简书:h ...

  3. 手牵手,从零学习Vue源码 系列二(变化侦测篇)

    系列文章: 手牵手,从零学习Vue源码 系列一(前言-目录篇) 手牵手,从零学习Vue源码 系列二(变化侦测篇) 陆续更新中... 预计八月中旬更新完毕. 1 概述 Vue最大的特点之一就是数据驱动视 ...

  4. 学习 vue 源码 -- 响应式原理

    概述 由于刚开始学习 vue 源码,而且水平有限,有理解或表述的不对的地方,还请不吝指教. vue 主要通过 Watcher.Dep 和 Observer 三个类来实现响应式视图.另外还有一个 sch ...

  5. 手牵手,从零学习Vue源码 系列一(前言-目录篇)

    系列文章: 手牵手,从零学习Vue源码 系列一(前言-目录篇) 手牵手,从零学习Vue源码 系列二(变化侦测篇) 手牵手,从零学习Vue源码 系列三(虚拟DOM篇) 陆续更新中... 预计八月中旬更新 ...

  6. 一起学习vue源码 - Vue2.x的生命周期(初始化阶段)

    作者:小土豆biubiubiu 博客园:https://www.cnblogs.com/HouJiao/ 掘金:https://juejin.im/user/58c61b4361ff4b005d9e8 ...

  7. Vue源码学习(一):调试环境搭建

    最近开始学习Vue源码,第一步就是要把调试环境搭好,这个过程遇到小坑着实费了点功夫,在这里记下来 一.调试环境搭建过程 1.安装node.js,具体不展开 2.下载vue项目源码,git或svn等均可 ...

  8. Vue源码详细解析:transclude,compile,link,依赖,批处理...一网打尽,全解析!

    用了Vue很久了,最近决定系统性的看看Vue的源码,相信看源码的同学不在少数,但是看的时候却发现挺有难度,Vue虽然足够精简,但是怎么说现在也有10k行的代码量了,深入进去逐行查看的时候感觉内容庞杂并 ...

  9. Vue源码解析(一):入口文件

    在学习Vue源码之前,首先要做的一件事情,就是去GitHub上将Vue源码clone下来,目前我这里分析的Vue版本是V2.5.21,下面开始分析: 一.源码的目录结构: Vue的源码都在src目录下 ...

随机推荐

  1. /etc/hosts 详解

    /etc/hosts:主机名查询静态表,是ip地址与域名快速解析的文件.ip地址与主机名之间的映射,包括主机的别名. 通常将常用的域名和ip地址映射加入到hosts文件中,实现快速方便的访问. 如果没 ...

  2. spring security 之自定义表单登录源码跟踪

    ​ 上一节我们跟踪了security的默认登录页的源码,可以参考这里:https://www.cnblogs.com/process-h/p/15522267.html 这节我们来看看如何自定义单表认 ...

  3. go条件语句

    1. if else package main import "fmt" func main(){ a :=123 if a>100{ fmt.prinln("大于 ...

  4. python Max retries exceeded with URL in requests

    使用requests进行重试 import requests from requests.adapters import HTTPAdapter from requests.packages.urll ...

  5. js 开始

    hello world 开始JavaScript 是一种脚本语言,它的解释器被称为 JavaScript 引擎.JavaScript 被发明用于在 HTML 网页上使用,给HTML网页增加动态功能.J ...

  6. vue.js学习与实战笔记(2)

    驼峰式写法时需要注意的问题 学习到组件这一章时,由于没注意到vue中对于camelCased的解释,导致出错了都找不出来,后面发现 在使用驼峰式写法时,在使用模板的时候需要使用kebab-case命名 ...

  7. 【Rancher相关问题】Rancher 2.5.8 及以下版本,提示Alert: Component controller-manager,scheduler is unhealthy.

    问题描述 如图,Rancher2.5.8版本提示 controller-manager,scheduler 不健康,管理的k8s集群版本1.21.1 解决方法 在Master节点执行如下命令: sed ...

  8. [cf559E]Gerald and Path

    将所有线段的端点(即$a_{i}$和$a_{i}\pm l_{i}$)离散,并按照$a_{i}$从小到大排序 定义$f_{i,,j}$表示前$i$条线段在位置$j$之前最多能覆盖的长度(默认覆盖到$j ...

  9. [nowcoder5669H]Harder Gcd Problem

    题目相当于问1-n中最多能选出多少对不互素无交集的二元组,并要求方案 构造:将所有数放入其最小质因子对应的集合,若素数p所对应的集合元素个数为奇数且$p\ne 2$且$2p\le n$,那么就将$2p ...

  10. 【Azure 应用服务】App Service 无法连接到Azure MySQL服务,报错:com.mysql.cj.jdbc.exceptions.CommunicationsException: Communications link failure

    问题描述 App Service使用jdbc连接MySQL服务,出现大量的  Communications link failure: com.mysql.cj.jdbc.exceptions.Com ...