前言
先说一下写此系列的初衷–最近公司招聘高级前端岗位 于是面了很多人 这面着面着就有点 痛心疾首 了 因为我发现很多做了五年以上的同学还是停留在业务逻辑层 一般表现为项目经验丰富 做过一些复杂的业务 但是对于框架或者工具的底层源码实现以及 js 的细节掌握并不到位 就比如 Vue 的混入(mixin) 大家基本都知道用它能解决啥问题 但是混入的原理 混入策略 混入顺序等等这些能全部回答出来的就比较少 于是我决定和大家一起手写一遍Vue2.0版本的源码 学习优秀源码的思路(后续会加入 3.0 版本)
适用人群:没时间去看官方源码或者看源码结构看的比较懵而不想去看的同学
注意:
我看源码一直保持 28 策略 所谓百分之 20 的代码实现了百分之 80 的功能 所以此系列咱们只关心核心逻辑以及功能的实现 不包含 打包构建 类型检查 跨平台 报错提醒 边界处理 兼容处理 SSR(服务端渲染)等
建议大家有时间还是把核心代码掌握之后再回头去看一遍官方源码
正文
大家都知道 Vue 的一个核心特点是数据驱动 如果按照以往 Jquery 的思想 咱们数据变化了想要同步到视图就必须要手动操作 dom 更新 但是 Vue 帮我们做到了数据变动自动刷新视图的功能 那在 Vue 内部就一定有一个机制能监听到数据变化然后触发更新机制 本篇主要介绍响应式数据的原理
1.数据初始化
new Vue({
el: "#app",
router,
store,
render: (h) => h(App),
});
这段代码 大家一定非常熟悉 这就是 Vue 实例化的过程 从 new 操作符 咱们可以看出 Vue 其实就是一个构造函数 没啥特别的 传入的参数就是一个对象 我们叫做 options(选项)
// src/index.js
import {
initMixin } from "./init.js";
// Vue就是一个构造函数 通过new关键字进行实例化
function Vue(options) {
// 这里开始进行Vue初始化工作
this._init(options);
}
// _init方法是挂载在Vue原型的方法 通过引入文件的方式进行原型挂载需要传入Vue
// 此做法有利于代码分割
initMixin(Vue);
export default Vue;
因为在 Vue 初始化可能会处理很多事情 比如数据处理 事件处理 生命周期处理等等 所以划分不同文件引入
// src/init.js
import {
initState } from "./state";
export function initMixin(Vue) {
Vue.prototype._init = function (options) {
const vm = this;
// 这里的this代表调用_init方法的对象(实例对象)
// this.$options就是用户new Vue的时候传入的属性
vm.$options = options;
// 初始化状态
initState(vm);
};
}
initMixin 把_init 方法挂载在 Vue 原型 供 Vue 实例调用
// src/state.js
import {
observe } from "./observer/index.js";
// 初始化状态 注意这里的顺序 比如我经常面试会问到 是否能在data里面直接使用prop的值 为什么?
// 这里初始化的顺序依次是 prop>methods>data>computed>watch
export function initState(vm) {
// 获取传入的数据对象