Vue.js 源码分析(一) 代码结构
关于Vue
vue是一个兴起的前端js库,是一个精简的MVVM。MVVM模式是由经典的软件架构MVC衍生来的,当View(视图层)变化时,会自动更新到ViewModel(视图模型),反之亦然,View和ViewModel之间通过双向绑定(data-binding)建立联系。
前言
花了一个月时间撸了vue2.0的源码,最近空了一点,今天开始记录一下学习心得,按照自己理解的代码的结构,分为三部分来讲
基础篇 ;比如全局配置、模板渲染、data、method、computed、watch、生命周期、ref、组件原理、组件-props、组件自定义事件
指令篇 ;比如v-bind、v-on、v-if、v-else、v-else-if、v-for、v-html、v-text、v-once、v-pre、v-model、v-show
高级篇 ;过滤器、自定义指令、插槽、作用域插槽、异步组件、transition内置组件、transition-group内置组件、Keep-Alive内置组件
每一个知识点都会讲得比较详细,理解源码需要扎实的js基础。如果有遗漏的,最后再做一下补充,如果有问题,欢迎留言指出,非常感谢!
从源码的角度看,Vue和jQuery看是差不多,都只是一个前端框架, 区别是一个是mvc模式,一个是mvvc模式。Vue是把ES5的defineProperty函数用到了极致,而jQuery为了兼容低版本IE是没有封装这个API的,就像搭积木(对应的浏览器接口和ECMASCRIPT接口),Vue用到了几个新的积木,而jQuery没有用到而已。从功能上,Vue应该和React、Angular来对比,它们和jQuery可以配合使用的。
vue实例化时会把模板(挂载到vue上的el元素对应的DOM节点,或者template属性)转换成一个虚拟的VNode,并在更新DOM时采用diff算法对DOM树进行检测,只更新需要更新的DOM节点,对于一些静态节点则不进行变更,以达到最快的速度。
采用的是v2.5.16这个版本的vue.js,本节先讲解Vue源码的大致结构,
代码结构
vue.js源码就是一个立即执行匿名函数表达式,内部定义了一个vue函数对象,组后返回挂载到window的vue属性上,先复习一下立即执行匿名函数表达式,如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
<script>
(function(global,msg){
global.alert(msg) //运行时会弹出一个窗口,内容为:Hello World
})(this,'Hello World')
</script>
</body>
</html>
知道了匿名函数后,我们来看看在Vue内部的样式,如下:
(function (global, factory) { //在浏览器环境下global等于全局window
typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() : typeof define === 'function' && define.amd ? define(factory) : (global.Vue = factory()); //执行factory()函数,将返回值Vue传递给window.Vue
}(this, (function () {
function Vue(options) { //内部定义的Vue函数
if ("development" !== 'production' && !(this instanceof Vue)) {
warn('Vue is a constructor and should be called with the `new` keyword');
}
this._init(options);
}
return Vue; //返回Vue这个函数对象
})));
这样执行完这个匿名函数后就可以通过window.Vue访问到匿名函数内的Vue函数对象了。
writer by:大沙漠 QQ:22969969
这个匿名函数内部会在Vue的原型对象上挂载很多方法和属性以供我们使用,大致的主线流程如下(为了更好理解,去掉了所有分支)
(function (global, factory) {
typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() : typeof define === 'function' && define.amd ? define(factory) : (global.Vue = factory()); //执行factory()函数,将返回值Vue传递给window.Vue
}(this, (function () {
'use strict';
function Vue(options) { //VUE的构造函数
if ("development" !== 'production' && !(this instanceof Vue)) {
warn('Vue is a constructor and should be called with the `new` keyword');
}
this._init(options); //.实例化时,先执行到这里进行初始化
}
function initMixin(Vue) {
Vue.prototype._init = function (options) { //.再执行到这里
/**/
if (vm.$options.el) { //如果有传入了挂载点的el节点
vm.$mount(vm.$options.el); //通过$mount函数把组件挂载到DOM上面
}
}
}
function lifecycleMixin(Vue) {
Vue.prototype._update = function (vnode, hydrating) { //.更新操作,把Vnode渲染成真实DOM
/**/
if (!prevVnode) { //如果prevVnode为空,即初始化时
vm.$el = vm.__patch__(vm.$el, vnode, hydrating, false, vm.$options._parentElm, vm.$options._refElm); //执行vm.__patch__方法渲染成真实的DOM节点
vm.$options._parentElm = vm.$options._refElm = null;
} else { //如果prevVnode不为空,即更新操作时
vm.$el = vm.__patch__(prevVnode, vnode); //调用vm.__patch__进行更新操作
}
}
}
function renderMixin(Vue) {
/**/
Vue.prototype._render = function () { //.把rener渲染成Vnode
}
}
function mountComponent(vm, el, hydrating) { //.挂载组件 vm:Vue实例 el:真实的DOM节点对象
/**/
var updateComponent;
if ("development" !== 'production' && config.performance && mark) {
/**/
} else {
updateComponent = function () {vm._update(vm._render(), hydrating);}; //渲染成Vnode并转换为真实DOM
}
new Watcher(vm, updateComponent, noop, null, true); //Watcher实例创建后会通过get()方法执行updateComponent()方法的
/**/
}
Vue.prototype.__patch__ = inBrowser ? patch : noop;
Vue.prototype.$mount = function (el, hydrating) { //.原型上的挂载(runtime-only版本)
el = el && inBrowser ? query(el) : undefined; //进行一些修正,因为runtime-only版本是从这里开始执行的
return mountComponent(this, el, hydrating) ////再调用mountComponent方法
};
var mount = Vue.prototype.$mount; //保存原型上的$mount函数
Vue.prototype.$mount = function (el, hydrating) { //.解析源码(Runtime+Compiler版本用的)
/*中间进行模板的解析*/
return mount.call(this, el, hydrating) //最后执行mount
};
return Vue; //最后返回Vue函数对象,会作为一个Vue属性挂载到window上
})));
后面源码讲解时会仔细讲每个细节的
Vue.js 源码分析(一) 代码结构的更多相关文章
- Vue.js 源码分析(十七) 指令篇 v-if、v-else-if和v-else 指令详解
v-if 指令用于条件性地渲染一块内容.这块内容只会在指令的表达式返回true值的时候被渲染. v-else-if,顾名思义,充当 v-if 的“else-if 块”,可以连续使用: 也可以使用 v- ...
- Vue.js 源码分析(三) 基础篇 模板渲染 el、emplate、render属性详解
Vue有三个属性和模板有关,官网上是这样解释的: el ;提供一个在页面上已存在的 DOM 元素作为 Vue 实例的挂载目标 template ;一个字符串模板作为 Vue 实例的标识使用.模板将会 ...
- Vue.js 源码分析(二) 基础篇 全局配置
Vue.config是一个对象,包含Vue的全局配置,可以在启动应用之前修改下列属性,如下: ptionMergeStrategies ;自定义合并策略的选项silent ...
- Vue.js 源码分析(八) 基础篇 依赖注入 provide/inject组合详解
先来看看官网的介绍: 简单的说,当组件的引入层次过多,我们的子孙组件想要获取祖先组件的资源,那么怎么办呢,总不能一直取父级往上吧,而且这样代码结构容易混乱.这个就是这对选项要干的事情 provide和 ...
- Vue.js 源码分析(二十九) 高级应用 transition-group组件 详解
对于过度动画如果要同时渲染整个列表时,可以使用transition-group组件. transition-group组件的props和transition组件类似,不同点是transition-gr ...
- Vue.js 源码分析(二十八) 高级应用 transition组件 详解
transition组件可以给任何元素和组件添加进入/离开过渡,但只能给单个组件实行过渡效果(多个元素可以用transition-group组件,下一节再讲),调用该内置组件时,可以传入如下特性: n ...
- Vue.js 源码分析(二十七) 高级应用 异步组件 详解
当我们的项目足够大,使用的组件就会很多,此时如果一次性加载所有的组件是比较花费时间的.一开始就把所有的组件都加载是没必要的一笔开销,此时可以用异步组件来优化一下. 异步组件简单的说就是只有等到在页面里 ...
- Vue.js 源码分析(二十六) 高级应用 作用域插槽 详解
普通的插槽里面的数据是在父组件里定义的,而作用域插槽里的数据是在子组件定义的. 有时候作用域插槽很有用,比如使用Element-ui表格自定义模板时就用到了作用域插槽,Element-ui定义了每个单 ...
- Vue.js 源码分析(二十三) 指令篇 v-show指令详解
v-show的作用是将表达式值转换为布尔值,根据该布尔值的真假来显示/隐藏切换元素,它是通过切换元素的display这个css属性值来实现的,例如: <!DOCTYPE html> < ...
随机推荐
- url中拼接中文参数,后台接收为乱码的问题
遇到在URL中拼接中文的参数,后台拿到的数据为乱码的问题,这里来说一下问题出现的原因与解决方法. 大家比较关心的应该是解决的方法,因此先说解决方法. 解决方法 解决的方法是在客户端对这个中文参数进行编 ...
- javascript解决在safari浏览器中使用history.back()返回上一页后页面不会刷新的问题
我们知道,在JavaScript中提供了一个window.history.back()方法用于返回上一页,另外也可以使用window.history.go(-1)返回上一页(跳转). 在其他的主流浏览 ...
- 创建WPF项目
参考: https://www.cnblogs.com/yhcao/p/6237611.html https://mahapps.com/guides/reporting-issues.html ht ...
- AspNet Katana中Authentication有关的业务逻辑
我将从CookieAuthenticationMiddleware中间件的使用,来讲述cookie认证是如何实现的 1.系统是如何调用CookieAuthenticationMiddleware的 在 ...
- java基础(8):Eclipse开发工具
1. Eclipse开发工具 Eclipse是功能强大Java集成开发工具.它可以极大地提升我们的开发效率.可以自动编译,检查错误.在公司中,使用的就是Eclipse进行开发. 1.1 Eclipse ...
- 新手教程丨利用Python制作一款截图识别软件!
进入正文前给大家推荐一个微软开发的工具:Snipaste. 这是一款截图软件,把截出的图片放置到窗口上,可以随意移动,使用非常方便,并且支持各类电脑系统. 先简单介绍一下它的用法,F1截图,Ctrl+ ...
- 使用JS通过Web API执行批量操作,多个操作是一个事务!
关注本人微信和易信公众号: 微软动态CRM专家罗勇 ,回复235或者20161105可方便获取本文,同时可以在第一间得到我发布的最新的博文信息,follow me!我的网站是 www.luoyong. ...
- DIY客户端框架
C/S类型的客户端做过好多轮了,在架构上每次都调整优化一部分,慢慢的形成了DIY的框架性东西. 可是最近这一看呢,已经不像MVC了,然后有一天看到了MVP概念,咦!很像.再一看,嗯,就该是MVP. M ...
- Python用openpyxl读写Excel
openpyxl是一个用于读写Excel 2010 xlsx文件的python库.openpyxl官方文档:https://openpyxl.readthedocs.io/en/stable/ 一.安 ...
- zabbix4.0搭建1
server端:负责接受到客户端发送过来的数据,并且保存到自己的数据库当中 端口:10051 agent端:负责每隔一定时间进行客户端的数据采集,并且发送给server端 端口:10050 proxy ...