从接下来的一段时间里,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. 20191310Lee_yellow缓冲区溢出实验

    缓冲区溢出实验 1.什么是缓冲区溢出 ​ 缓冲区溢出是指程序试图向缓冲区写入超出预分配固定长度数据的情况.这一漏洞可以被恶意用户利用来改变程序的流控制,甚至执行代码的任意片段.这一漏洞的出现是由于数据 ...

  2. spark structured-streaming 最全的使用总结

    一.spark structured-streaming  介绍 我们都知道spark streaming  在v2.4.5 之后 就进入了维护阶段,不再有新的大版本出现,而且 spark strea ...

  3. Nginx高级特性实操

    导读 nginx从入门到精通,点我直达 下载nginx与安装 点我直达 安装依赖 yum -y install gcc zlib zlib-devel pcre-devel openssl opens ...

  4. Java 17 新功能介绍(LTS)

    点赞再看,动力无限.Hello world : ) 微信搜「程序猿阿朗 」. 本文 Github.com/niumoo/JavaNotes 和 未读代码博客 已经收录,有很多知识点和系列文章. Jav ...

  5. JavaScript事件捕获冒泡与捕获

    事件流 JavaScript中,事件流指的是DOM事件流. 概念 事件的传播过程即DOM事件流.事件对象在 DOM 中的传播过程,被称为"事件流".举个例子:开电脑这个事,首先你是 ...

  6. 【Python+postman接口自动化测试】(4)HTTP 协议

    前言 HTTP:超文本传输协议,是用于从WWW服务器传输超文本到本地浏览器的传输协议. HTTP协议是一种无状态协议,主要包含请求和相应两大部分. 请求(Request) get请求示范: GET h ...

  7. js分支语句

    一.逻辑分支(选择结构,分支结构) 其实今天的课程才算开始涉及到逻辑 程序的三大结构 顺序结构 - 每天 代码逐行执行,一行一行自上而下执行 分支结构 有选择了,十字路口的选择,只能选择一个,如果.. ...

  8. 经过4次优化我把python代码耗时减少95%

    背景交代 团队做大学英语四六级考试相关服务.业务中有一个care服务,购买了care服务考试不过可以全额退款,不过有一个前提是要完成care服务的任务,比如坚持背单词N天,完成指定的试卷. 在这个背景 ...

  9. 大爽Python入门教程 3-4 实践例题

    大爽Python入门公开课教案 点击查看教程总目录 1. 求和 使用循环,计算列表所有项的和,并输出这个和. 列表示例 lst = [8, 5, 7, 12, 19, 21, 10, 3, 2, 11 ...

  10. 新技能GET!在前端表格中花式使用异步函数的奥义

    背景 60年代时,操作系统中独立运行的单元通常是进程.但随着计算机技术的发展,人们发现在进程运行过程中,创建.撤销与切换都要花费较大的时空开销. 到了80年代为了解决这一问题,出现了更小的独立运行基本 ...