zoukankan      html  css  js  c++  java
  • 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);                                    //1.实例化时,先执行到这里进行初始化
        }
        function initMixin(Vue) {      
            Vue.prototype._init = function (options) {              //2.再执行到这里 
                /**/
                if (vm.$options.el) {                               //如果有传入了挂载点的el节点
                    vm.$mount(vm.$options.el);                          //通过$mount函数把组件挂载到DOM上面
                }
            }
        }
        function lifecycleMixin(Vue) {  
            Vue.prototype._update = function (vnode, hydrating) {   //7.更新操作,把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 () {                   //6.把rener渲染成Vnode
            }
        }
        function mountComponent(vm, el, hydrating) {                //5.挂载组件 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) {           //4.原型上的挂载(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) {           //3.解析源码(Runtime+Compiler版本用的)
            /*中间进行模板的解析*/
            return mount.call(this, el, hydrating)                      //最后执行mount
        };
        return Vue;                             //最后返回Vue函数对象,会作为一个Vue属性挂载到window上
    })));

    后面源码讲解时会仔细讲每个细节的

  • 相关阅读:
    C#单例模式的多种写法
    数据库优化
    Web Service和Web Api的区别和应用场景
    浅谈堆和栈、值类型和引用类型。
    浅谈隐式类型var和动态类型dynamic以及MVC特性ViewBag
    C# using的三种用法
    sql将表中的某个字段进行排序
    c#重载和重写及运用
    Asp.net Mvc5的认识
    Log4net记录日志存库并存本地
  • 原文地址:https://www.cnblogs.com/greatdesert/p/11010659.html
Copyright © 2011-2022 走看看