vue2.x源码刨析-new Vue的时候做了什么(手写简易版01)

本篇文章大致的介绍一下new Vue的过程,
首先我们在生成一个Vue实例化对象的时候,一般会这样写:

<div id="app" style="color: red">{{name}} dep {{age}} dep {{name}}</div>
const vm = new Vue({data() {return {// 代理数据name: "zf",age: 20,address: {num: 30,content: "回龙观",},hobby: ["eat", "drink", { a: 1 }],};},created() {// console.log(this.xxx); // 数据来源不明确console.log("created");},el: "#app", // 我们要将数据  解析到el元素上// template: "<div>111</div>",
});
// vue.mixin 混合  可以混入一些公共方法

那么在new Vue的过程中,都做了些什么,是怎么把数据做到响应式的呢?
其实,在实例化Vue的时候,主要执行的是,_init方法,

function Vue(options) {// options就是用户的选项this._init(options);
}

在实例化的时候,会对数据进行初始化,在初始化的过程中,会在Vue原型上面挂载上_init方法,

initMixin(Vue); // 扩展了init方法
initLifeCycle(Vue); // vm._update vm._render
initGlobalAPI(Vue); // 全局 api 的实现
initStateMixin(Vue); // 实现了 nextTick $watch

initMixin_init方法挂载到了原型上面,

export function initMixin(Vue) {Vue.prototype._init = function (options) {// 用于初始化操作const vm = this;// Vue vm.$options 就是获取用户的配置// 我们定义的全局指令和过滤器.... 都会挂载到实例上// this.constructor 不能写成 Vue,可能是子组件vm.$options = mergeOptions(this.constructor.options, options);callHook(vm, "beforeCreate");// 初始化状态 初始化计算属性 watchinitState(vm);callHook(vm, "created");if (options.el) {vm.$mount(options.el); // 实现数据的挂载}};Vue.prototype.$mount = function (el) {const vm = this;el = document.querySelector(el);let ops = vm.$options;if (!ops.render) {// 先进行查找有没有render函数let template; // 没有render看一下是否写了template, 没写template采用外部的templateif (!ops.template && el) {// 没有写模板,但是写了eltemplate = el.outerHTML;} else {// if (el) {//     template = ops.template;// }template = ops.template;}// 写了template就用写了的templateif (template) {// 这里需要对模板进行编译const render = compileToFunction(template);ops.render = render;}}// console.log(ops.render); // 最终就可以获取render方法mountComponent(vm, el); // 组件的挂载};
}

其中,mergeOptions方法主要是用来合并两个对象

const strats = {};
const LIFECYCLE = ["beforeCreate", "created"];
LIFECYCLE.forEach((hook) => {strats[hook] = function (p, c) {// {} {created:function(){}} => {created:[fn]}// {created:[fn]} {created: function(){}} => {created: [fn,fn]}if (c) {// 如果儿子有  父亲有  让父亲和儿子拼在一起if (p) {return p.concat(c);} else {return [c]; // 儿子有父亲没有,则将儿子包装成数组}} else {return p; // 如果儿子没有,则用父亲即可}};
});strats.components = function(parentVal, childVal) {const res = Object.create(parentVal)if(childVal) {for(let key in childVal) {res[key] = childVal[key];  // 返回的是构造的对象  可以拿到父亲原型上的属性,并且将儿子的都拷贝到自己身上}}return res;
}export function mergeOptions(parent, child) {const options = {};for (let key in parent) {// 循环老的 {a:1}mergeField(key);}for (let key in child) {// 循环新的 {}if (!parent.hasOwnProperty(key)) {mergeField(key);}}function mergeField(key) {// 策略模式,用策略模式减少 if/elseif (strats[key]) {options[key] = strats[key](parent[key], child[key]);} else {options[key] = child[key] || parent[key]; // 优先采用儿子,再采用父亲}}return options;
}

在这里首先用child的属性替换掉parent的属性,其次使用策略模式,整合各个生命周期,这里将生命周期整合成一个数组,是因为如果我们使用了mixin方法,可能会存在一个生命周期被调用两次的情况。在这里我们可以看出components其实就是父数据的一个复制版,先使用Object.create实现父数据的继承,然后循环子数据,替换掉父数据的数据。
callhook方法就是调用生命周期

export function callHook(vm, hook) {const handlers = vm.$options[hook];if (handlers) {handlers.forEach((handler) => handler.call(vm));}
}

在前面我们已经知道,生命周期通过mergeOptions方法策略模式,已经是一个数组的形式,所以在这里对数组进行循环,调用数组的方法。
当然这里面主要还有$mount方法,这个方法可以解析模板中的数据,将响应式数据进行渲染,这个方法我在后面会主要进行分析。
_init方法中,其实还有对状态进行初始化(data,computed,watch),这个后面谈到响应式数据的时候,可以再说。
在初始化的时候还有几个初始化的方法,主要还是在Vue的原型上面挂载方法。
initLifeCycle方法,其中涉及到数据的更新引起dom的变化,模板的解析,主要用来处理这些东西

export function initLifeCycle(Vue) {Vue.prototype._update = function (vnode) {const vm = this;const el = vm.$el;const prevVnode = vm._vnode;vm._vnode = vnode; // 把组件第一次产生的虚拟节点保存到 _vnode 上if (prevVnode) {// 之前渲染过了vm.$el = patch(prevVnode, vnode)} else {// patch既有初始化的功能,又有更新的逻辑vm.$el = patch(el, vnode);}};// _c('div', {}, ...children)Vue.prototype._c = function () {return createElementVNode(this, ...arguments);};// _v(text)Vue.prototype._v = function () {return cretaeTextNode(this, ...arguments);};Vue.prototype._s = function (value) {if (typeof value !== "object") return value;return JSON.stringify(value);};Vue.prototype._render = function () {const vm = this;// 让 with 中的this指向vm// 当渲染的时候会去实例中取值,我们就可以将属性和视图绑定在一起return vm.$options.render.call(vm); // 通过ast语法树转义后生成的render方法};
}

initGlobalAPI主要是用来配置一些全局属性

function initGlobalAPI(Vue) {// 静态方法Vue.options = {_base: Vue};Vue.mixin = function (mixin) {// 我们期望将用户的选项和全局的 options 进行合并// {} {created:function(){}} => {created:[fn]}// {created:[fn]} {created: function(){}} => {created: [fn,fn]}this.options = mergeOptions(this.options, mixin);return this;};Vue.extend = function(options) {// 就是实现根据用户的参数 返回一个构造函数而已function Sub(options = {}) { // 最终使用一个组件 就是 new 一个实例this._init(options); // 就是默认对子类进行初始化操作}  Sub.prototype = Object.create(Vue.prototype);  // Sub.prototype.__proto__ === Vue.prototypeSub.prototype.constructor = Sub;  // 组合式继承要重新连接// 希望将用户传递的参数 和全局的 Vue.options 来合并Sub.options = mergeOptions(Vue.options, options); // 保存用户传递的选项;return Sub;}Vue.options.components = {} // 全局的指令  Vue.options.directivesVue.component = function(id, definition) {// 如果 definition 已经是一个函数了,说明用户自己调用了 Vue.extenddefinition = typeof definition === 'function' ? definition : Vue.extend(definition)Vue.options.components[id] = definition;console.log(Vue.options.components);}
}

initStateMixin主要实现$nextTick$watch方法,其中$watch方法极为重要,是整个响应式系统的核心

function initStateMixin(Vue) {Vue.prototype.$nextTick = nextTick;Vue.prototype.$watch = function (exprOrFn, cb) {// firstname 的值变化了,直接执行 cb 函数即可new Watcher(this, exprOrFn, { user: true }, cb);};
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/157937.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【idea】解决idea 执行maven build总下载 Downloading maven-metadata.xml文件

可以看到如下日志中打印了执行的命令行&#xff0c;其中包含 --update-snapshots&#xff0c;是强制更新的意思。 日志内容如下&#xff1a; D:\env\jdk1.8.0_261\bin\java.exe --update-snapshots -s D:\env\apache-maven-3.8.6\conf\settings.xml -Dmaven.repo.localD:\env\…

【开源】基于Vue.js的天然气工程业务管理系统的设计和实现

项目编号&#xff1a; S 021 &#xff0c;文末获取源码。 \color{red}{项目编号&#xff1a;S021&#xff0c;文末获取源码。} 项目编号&#xff1a;S021&#xff0c;文末获取源码。 目录 一、摘要1.1 项目介绍1.2 项目录屏 二、功能模块三、使用角色3.1 施工人员3.2 管理员 四…

tomcat (SCI)ServletContainerInitializer 的加载原理

问题&#xff1a;使用WebScoket的时候发现通过ServerEndpoint方式注册上去的url无法访问&#xff0c;报错404 经过排查发现在WsServerContainer这个类中的addEndpoint方法一直没有触发ServerEndpoint注解的扫描 通过该方法来源于StandardContext.startInternal()方法的调用如下…

CSS特效015:7个小球转圈圈加载效果

CSS常用示例100专栏目录 本专栏记录的是经常使用的CSS示例与技巧&#xff0c;主要包含CSS布局&#xff0c;CSS特效&#xff0c;CSS花边信息三部分内容。其中CSS布局主要是列出一些常用的CSS布局信息点&#xff0c;CSS特效主要是一些动画示例&#xff0c;CSS花边是描述了一些CSS…

ERP对接淘宝/天猫/京东/拼多多商品详情数据API接口

引言 今天&#xff0c;我们时代变化非常快&#xff0c;传统行业做法&#xff0c;已经无法完全适应时代的发展。互联网的发展&#xff0c;造成了一股网购热。京东&#xff0c;天猫&#xff0c;淘宝&#xff0c;易购……网购&#xff0c;给我们生活带来了方便&#xff0c;消费者…

axios封装使用

&#x1f33c;未封装 在项目中往往一个页面存在着要联调好几个接口&#xff0c;此时用下面的写法就会显得代码很冗余 // 向给定ID的用户发起请求 axios.get(/user?ID123456,{ params: { //发送请求的数据ID: 123456 } }) .then(function (response) { // 处理成功情…

人工智能:科技之光,生活之美

在科技飞速发展的今天&#xff0c;人工智能已经深入到我们的生活中&#xff0c;它如同一束璀璨的科技之光&#xff0c;照亮我们生活的每一个角落&#xff0c;使我们的生活更加美好。下面我将从人工智能的领域、应用以及对人工智能的看法三个方面来谈谈它对我们生活的影响。 一、…

最新红盟云卡个人自动发卡开源系统源码+全开源无加密+虚拟商品在线售卖平台

源码简介&#xff1a; 最新红盟云卡个人自动发卡开源系统源码全开源无加密虚拟商品在线售卖平台&#xff0c;支持多个接口的个人免签功能。 红盟云卡系统是一款基于PHP和MySQL开发的虚拟商品在线售卖平台。它具备美观且功能丰富的发卡网站特性&#xff0c;并可与社区进行无缝…

【量化】一个简版单档tick数据回测框架

这是一个简易的模拟实际交易流程的回测框架&#xff0c;所使用的行情数据是单档的tick成交数据。为了实现调用者可以实现自己的交易逻辑&#xff0c;本框架预留了几个函数予以调用者能够继承类后在子类中重写以实现买入卖出信号的生成&#xff08;check_sell()和check_buy()&am…

Docker Swarm总结

1、swarm 理论基础 1.1 简介 Docker Swarm 是由 Docker 公司推出的 Docker 的原生集群管理系统&#xff0c;它将一个 Docker 主机池变成了一个单独的虚拟主机&#xff0c;用户只需通过简单的 API 即可实现与 Docker 集群的通 信。Docker Swarm 使用 GO 语言开发。从 Docker 1.…

inf和nan

在某些编程语法中inf表示无穷大,nan表示不是一个数(not a number) nan表示这个数不确定,而无穷大表示这个数任意大 1/0inf 这里把0当做一个无限接近0,但是非0的数 5-inf-inf 一个数减去无穷大会等于负无穷大 而inf-infnan 因为两个无穷大相减有很多可能,可能等于一个常数,也可能…

Linux find命令

根据时间查找 ## 查找几分钟之内创建的文件 find . -cmin 5

HTML所有功能大汇总

HTML所有的功能&#xff0c;都在下面的表格中呈现清楚了。千万不要死记硬背&#xff0c;但是在遇到困难的时候&#xff0c;可以按照这个表进行查找。 类别功能HTML标签文本样式粗体<b></b> 或 <strong></strong>斜体<i></i>或<em>&…

Ajax基础(应用场景|jquery实现Ajax|注意事项|Ajax发送json数据|Ajax携带文件数据)

文章目录 一、Ajax简介二、基于jquery实现Ajax三、使用Ajax注意的问题1.Ajax不要与form表单同时提交2.后端响应格式问题3、使用了Ajax作为请求后的注意事项 四、前后端数据传输的编码格式(content-Type)1.urlencoded2.formdata3.application/json 五、Ajax携带文件数据六、Ajax…

代码随想录第六十三天 | 单调栈:寻找 左边 / 右边 距离当前元素最近的 更小 元素的 下标(暴力,双指针,单调栈)(84);代码随想录主要题目结束

1、寻找 左边 / 右边 距离当前元素最近的 更小 元素的 下标 1.1 leetcode 84&#xff1a;柱状图中最大的矩形 第一遍代码思路错了&#xff0c;如&#xff1a;输入[2,1,2]&#xff0c;对于2&#xff0c;因为比栈顶元素1大&#xff0c;然后就会直接得出2&#xff08;1&#xff…

掌握Java语言特性的必备题目集锦!

问题&#xff1a;什么是ConcurrentLinkedDeque&#xff1f;它在Java中的使用场景是什么&#xff1f; 回答&#xff1a;ConcurrentLinkedDeque是Java中的一个线程安全的双向链表队列实现。它是Java并发集合框架中的一部分。它扩展了LinkedList类&#xff0c;并实现了Deque&…

etoken是什么意思,有什么作用?

EToken是一种数字货币&#xff0c;它是由以太坊区块链平台发行的智能合约&#xff0c;旨在为以太坊生态系统提供一种安全、可靠、去中心化的交易媒介。EToken具有多种作用&#xff0c;下面将详细介绍。 一、EToken的定义和发行 EToken是由以太坊智能合约创建的数字货币&#xf…

渲染器——快速Diff算法

讨论第三种用于比较新旧两组子节点的方式&#xff1a;快速Diff 算法。正如其名&#xff0c;该算法的实测速度非常快。该算法最早应用于 ivi 和 inferno 这两个框架&#xff0c;Vue.js 3 借鉴并扩展了它。 下图比较了 ivi、inferno 以及 Vue.js 2 的性能&#xff1a; 上图来自…

Redis持久化机制详解

使用缓存的时候&#xff0c;我们经常需要对内存中的数据进行持久化也就是将内存中的数据写入到硬盘中。大部分原因是为了之后重用数据&#xff08;比如重启机器、机器故障之后恢复数据&#xff09;&#xff0c;或者是为了做数据同步&#xff08;比如 Redis 集群的主从节点通过 …

Qt程序的自定义安装卸载方案

前言 NSIS 是一个 Open Source 的 Windows 系统下安装程序制作程序&#xff1b; NSIS-UI-Plugin 是一个开源的NSIS UI插件&#xff1b; 0x0 环境搭建 https://www.cnblogs.com/NSIS/p/16581122.html https://github.com/sway913/NSIS-UI-Plugin 0x1 类图 0x2 二次开发 自定…