美文网首页
vue学习2

vue学习2

作者: 百里哈哈 | 来源:发表于2019-02-02 18:55 被阅读0次

    观察订阅模式

    关于cleanupDeps函数

    Watcher.prototype.cleanupDeps = function cleanupDeps () {
        var i = this.deps.length;
        while (i--) {
          var dep = this.deps[i];
          if (!this.newDepIds.has(dep.id)) {
            dep.removeSub(this);
          }
        }
        var tmp = this.depIds;
        this.depIds = this.newDepIds;
        this.newDepIds = tmp;
        this.newDepIds.clear();
        // 上面4行代码旧数据的删除     
        tmp = this.deps;
        this.deps = this.newDeps;
        this.newDeps = tmp;
        this.newDeps.length = 0;
        // 新数据的更新   
      };
    

    该方法用于进行依赖dep的更新, 添加新的订阅移除旧的不需要的订阅。
    比如 v-if 已不需要的模板依赖的数据发生变化时就不会通知watcher去 update, 当v-if模板不需要渲染时,它其中涉及到的数据不需要进行该watcher的订阅。
    a.Watcher依赖收集的观察者
    b.Dep依赖收集器 订阅者
    c.Observe 类主要给响应式对象的属性添加 getter/setter 用于依赖收集与派发更新

    生成虚拟DOM的render相关函数

    function installRenderHelpers (target) {
        target._o = markOnce;
        target._n = toNumber;
        target._s = toString;
        target._l = renderList;
        target._t = renderSlot;
        target._q = looseEqual;
        target._i = looseIndexOf;
        target._m = renderStatic;
        target._f = resolveFilter;
        target._k = checkKeyCodes;
        target._b = bindObjectProps;
        target._v = createTextVNode;
        target._e = createEmptyVNode;
        target._u = resolveScopedSlots;
        target._g = bindObjectListeners;
        target._d = bindDynamicKeys;
        target._p = prependModifier;
      }
    

    createFunctionalComponent函数式组件

    a.组件的functional设置为true
    b.组件提供一个render函数
    c. render函数的返回结构类似 h(ele, attr, [children])

    示例

    Vue.component('my-transition', {
        functional:true,
        render:function (h, ctx) {
          var data = {
            props:{
              tag:'ul',
              css:false
            },
            on:{
              beforeEnter:function (el) {
                el.style.opacity = 0
                el.style.height = 0
              },
              enter:function (el, done) {
                var delay = el.dataset.index * 150
                setTimeout(function () {
                  Velocity(el, {opacity:1, height:'1.6em'},{complete:done})
                }, delay)
              },
              leave:function (el, done) {
                var delay = el.dataset.index * 150
                setTimeout(function () {
                  Velocity(el, {opacity:0, height:0}, {complete:done})
                }, delay)
              }
            }
          }
          return h('transition-group', data, ctx.children)
        },
        props:['query', 'list']
      })
    

    component初始化钩子函数

    // inline hooks to be invoked on component VNodes during patch
      var componentVNodeHooks = { 
        init: function init (node, hydrating) {},
        prepunch: function prepunch (oldVnode, Vnode) {},
        insert: function instert(vnode) {},
        destroy: function destroy(vnode) {}
    }
    

    初始化merge相关钩子函数
    代码结构如下

    installComponentHooks
    

    组件创建的过程

    createComponent方法的相关代码

    …
    // 继承自Vue
        if (isObject(Ctor)) {
              Ctor = baseCtor.extend(Ctor);
            }
    …
        // 如果是异步组件
        asyncFactory = Ctor;
           Ctor = resolveAsyncComponent(asyncFactory, baseCtor, context);
    …
        // resolve constructor options in case global mixins are applied after
           // component constructor creation
            resolveConstructorOptions(Ctor);
    …
        // extract props
        var propsData = extractPropsFromVNodeData(data, Ctor, tag);
    …
        // 如果是functional component
              return createFunctionalComponent(Ctor, propsData, data, context, children)
    …
        // install component management hooks onto the placeholder node
            installComponentHooks(data);
    …
        var vnode = new VNode(
          ("vue-component-" + (Ctor.cid) + (name ? ("-" + name) : '')),
          data, undefined, undefined, undefined, context,
          { Ctor: Ctor, propsData: propsData, listeners: listeners, tag: tag, children: children },
          asyncFactory
        );
    
        return vnode
    

    v-model的实现

    // transform component v-model info (value and callback) into
      // prop and event handler respectively.
      function transformModel (options, data) {
        var prop = (options.model && options.model.prop) || 'value';
        var event = (options.model && options.model.event) || 'input'
        ;(data.props || (data.props = {}))[prop] = data.model.value;
        var on = data.on || (data.on = {});
        var existing = on[event];
        var callback = data.model.callback;
        if (isDef(existing)) {
          if (
            Array.isArray(existing)
              ? existing.indexOf(callback) === -1
              : existing !== callback
          ) {
            on[event] = [callback].concat(existing);
          }
        } else {
          on[event] = callback;
        }
      }
    

    有上面代码可知v-model会转换成prop 和event事件
    示例1

    <input v-model=“message” >
    <input :value=“message” @input=“message=$event.target.value” >
    
    

    可以通过设置model的属性 prop自定义变量 event来自定义属性
    示例2

    <my-msg v-model="testmsg"></my-msg>
    Vue.component('my-msg', {
        template: `
          <div>
            <p>this is the value {{msg}}</p>
            <span @click="clickMe"> to click</span>
          </div>
        `,
        props: {
          msg: ''
        },
        model: {
          event: 'coustomEvent',
          prop: 'msg'
        },
        methods: {
          clickMe () {
            this.$emit('coustomEvent', this.msg + 'sss ')
          }
        }
      })
    

    _createElement
    _createElement构造虚拟DOM
    _render函数
    _render函数返回一个vnode
    initMixin函数
    initMixin函数包括Vue.prototype._init
    其中_init执行Vue整个过程中涉及到的函数,实例化Vue的入口函数,其主要代码如下

    // a flag to avoid this being observed
          vm._isVue = true;
          // merge options
          if (options && options._isComponent) {
            // optimize internal component instantiation
            // since dynamic options merging is pretty slow, and none of the
            // internal component options needs special treatment.
            initInternalComponent(vm, options);
          } else {
            vm.$options = mergeOptions(
              resolveConstructorOptions(vm.constructor),
              options || {},
              vm
            );
          }
          /* istanbul ignore else */
          {
            initProxy(vm);
          }
          // expose real self
          vm._self = vm;
          initLifecycle(vm);
          initEvents(vm);
          initRender(vm);
          callHook(vm, 'beforeCreate');
          initInjections(vm); // resolve injections before data/props
          initState(vm);
          initProvide(vm); // resolve provide after data/props
          callHook(vm, 'created');
    
          /* istanbul ignore if */
          if (config.performance && mark) {
            vm._name = formatComponentName(vm, false);
            mark(endTag);
            measure(("vue " + (vm._name) + " init"), startTag, endTag);
          }
    
          if (vm.$options.el) {
            vm.$mount(vm.$options.el);
          }
    

    resolveConstructorOptions
    resolveConstructorOptions处理options的相关属性

    vue插件原理

    initUse
    initUse主要为vue挂载静态方法use, 其代码结构如下

    function initUse (Vue) {
        Vue.use = function (plugin) {
          var installedPlugins = (this._installedPlugins || (this._installedPlugins = []));
          if (installedPlugins.indexOf(plugin) > -1) {
            return this
          }
    
          // additional parameters
          var args = toArray(arguments, 1);
          // 将Vue对象注入       
          args.unshift(this);
          if (typeof plugin.install === 'function') {
            plugin.install.apply(plugin, args);
          } else if (typeof plugin === 'function') {
            plugin.apply(null, args);
          }
          installedPlugins.push(plugin);
          return this
        };
      }
    

    开发插件的示例代码

    MyPlugin.install = function (Vue, options) {
      // 1. 添加全局方法或属性
      Vue.myGlobalMethod = function () {
        // 逻辑...
      }
    
      // 2. 添加全局资源
      Vue.directive('my-directive', {
        bind (el, binding, vnode, oldVnode) {
          // 逻辑...
        }
        ...
      })
    
      // 3. 注入组件
      Vue.mixin({
        created: function () {
          // 逻辑...
        }
        ...
      })
    
      // 4. 添加实例方法
      Vue.prototype.$myMethod = function (methodOptions) {
        // 逻辑...
      }
    }
    

    mixin

    mixin的代码如下所示

    function initMixin$1 (Vue) {
        Vue.mixin = function (mixin) {
          this.options = mergeOptions(this.options, mixin);
          return this
        };
      }
    

    由代码可知mixin将一些方法或属性挂载到 vue的静态options属性上
    例如

    Vue.mixin({
        created: function () {
          console.log('mixin created')
        }
      }) 
    

    Vue继承

    initExtend实现Vue的继承, 在组件初始化时创建一个继承自Vue的子对象
    主要代码结构如下

    var Sub = function VueComponent (options) {
            this._init(options);
          };
          Sub.prototype = Object.create(Super.prototype);
          Sub.prototype.constructor = Sub;
          Sub.cid = cid++;
          Sub.options = mergeOptions(
            Super.options,
            extendOptions
          );
          Sub['super'] = Super;
    
          // For props and computed properties, we define the proxy getters on
          // the Vue instances at extension time, on the extended prototype. This
          // avoids Object.defineProperty calls for each instance created.
          if (Sub.options.props) {
            initProps$1(Sub);
          }
          if (Sub.options.computed) {
            initComputed$1(Sub);
          }
    
          // allow further extension/mixin/plugin usage
          Sub.extend = Super.extend;
          Sub.mixin = Super.mixin;
          Sub.use = Super.use;
    
          // create asset registers, so extended classes
          // can have their private assets too.
          ASSET_TYPES.forEach(function (type) {
            Sub[type] = Super[type];
          });
          // enable recursive self-lookup
          if (name) {
            Sub.options.components[name] = Sub;
          }
    
          // keep a reference to the super options at extension time.
          // later at instantiation we can check if Super's options have
          // been updated.
          Sub.superOptions = Super.options;
          Sub.extendOptions = extendOptions;
          Sub.sealedOptions = extend({}, Sub.options);
    
          // cache constructor
          cachedCtors[SuperId] = Sub;
          return Sub
    }
    

    pruneCache
    删除keepAlive组件的缓存
    其中keepAlive的生命周期函数还包括如下

    var KeepAlive = { 
    …
    created (){},
    destroyed () {},
    mounted() {},
    render() {}
    
    }
    

    initGlobalAPI
    initGlobalAPI注册Vue的全局函数

    renderClass
    renderClass用来生成元素的class, 其中concat代码如下

    function concat (a, b) {
        return a ? b ? (a + ' ' + b) : a : (b || '')
      }
    

    其中涉及字符串化基本方法主要
    stringifyClass、stringifyArray、stringifyObject


    registerRef方法
    registerRef方法用过注册设置ref属性的组件

    自定义指令

    自定义指令主要方法updateDirectives, 而该函数其实是对_update函数的调用
    _update主要用来触发指令创建及变更指令中所涉及到的一些方法, 其代码结构如下

     function _update (oldVnode, vnode) {
        var isCreate = oldVnode === emptyNode;
        var isDestroy = vnode === emptyNode;
        var oldDirs = normalizeDirectives$1(oldVnode.data.directives, oldVnode.context);
        var newDirs = normalizeDirectives$1(vnode.data.directives, vnode.context);
    
        var dirsWithInsert = [];
        var dirsWithPostpatch = [];
    
        var key, oldDir, dir;
        for (key in newDirs) {
          oldDir = oldDirs[key];
          dir = newDirs[key];
          if (!oldDir) {
            // new directive, bind
            callHook$1(dir, 'bind', vnode, oldVnode);
            if (dir.def && dir.def.inserted) {
              dirsWithInsert.push(dir);
            }
          } else {
            // existing directive, update
            dir.oldValue = oldDir.value;
            dir.oldArg = oldDir.arg;
            callHook$1(dir, 'update', vnode, oldVnode);
            if (dir.def && dir.def.componentUpdated) {
              dirsWithPostpatch.push(dir);
            }
          }
        }
    
        if (dirsWithInsert.length) {
          var callInsert = function () {
            for (var i = 0; i < dirsWithInsert.length; i++) {
              callHook$1(dirsWithInsert[i], 'inserted', vnode, oldVnode);
            }
          };
          if (isCreate) {
            mergeVNodeHook(vnode, 'insert', callInsert);
          } else {
            callInsert();
          }
        }
    
        if (dirsWithPostpatch.length) {
          mergeVNodeHook(vnode, 'postpatch', function () {
            for (var i = 0; i < dirsWithPostpatch.length; i++) {
              callHook$1(dirsWithPostpatch[i], 'componentUpdated', vnode, oldVnode);
            }
          });
        }
    
        if (!isCreate) {
          for (key in oldDirs) {
            if (!newDirs[key]) {
              // no longer present, unbind
              callHook$1(oldDirs[key], 'unbind', oldVnode, oldVnode, isDestroy);
            }
          }
        }
      }
    
    

    一个简单的例子如下

    Vue.directive('mydir', {
        // 当被绑定的元素插入到 DOM 中时……
        inserted: function (el) {
          // 聚焦元素
          console.log('look my direct')
        }
      })
    

    注意在模板编译的过程中会将指令名称转为小写,在创建指令时需采用小写方式

    过滤器

    parseFilters
    parseFilters解析过滤器表达式
    举个例子如下

    Vue.filter('capitalize', function (value) {
        if (!value) return ''
        value = value.toString()
        return value.charAt(0).toUpperCase() + value.slice(1)
      })
    
    {{message | capitalize}}
    

    parseFilters返回的结果为"_f("capitalize")(message)"

    编译过程

    编译过程简单介绍
    a.parser 将template模板生成抽象语法树
    b.optimizer 对语法树进行优化处理,用来提升性能
    c.codegen用来生成render函数

    示例如下

    <div>
      <header>
        <h1>I'm a template!</h1>
      </header>
      <p v-if="message">
        {{ message }}
      </p>
      <p v-else>
        No message.
      </p>
    

    function anonymous() { with(this){return _c('div',[_m(0),(message)?_c('p',[_v(_s(message))]):_c('p',[_v("No message.")])])} }

    vue动画过渡

    组件或元素进入或离开时执行的动画过渡效果
    a.组件为transition
    b.条件渲染使用 v-if
    c.条件展示 v-show
    过渡钩子函数
    1.v-enter过渡开始状态,元素被插入之前生效,在元素被插入的下一帧移除
    2.v-enter-active定义过渡生效时的状态,在元素被插入之前生效动画完成之后移除
    3.v-enter-to元素插入之后下一帧生效
    4.v-leave离开时过渡开始时的状态
    5.v-leave-active离开时过渡生效的状态,在过渡被触发是立即生效
    6.v-leave-to离开过渡结束状态
    元素插入时的钩子函数 为function enter (vnode, toggleDisplay)
    定义v-show指令时,在bind, 和 update中会涉及 enter函数的调用, 可用v-show进行相关代码的调试
    补充触发该动画时会进行_isMounted的判断, bind方法发生在mountComponent之前,如果设置默认变量为ture切不进行变更的情况下则不会触发动画函数
    其中涉及到的动画进入代码代码主要如下

    // start enter transition
        beforeEnterHook && beforeEnterHook(el);
        if (expectsCSS) {
          addTransitionClass(el, startClass);
          addTransitionClass(el, activeClass);
          nextFrame(function () {
            removeTransitionClass(el, startClass);
            if (!cb.cancelled) {
              addTransitionClass(el, toClass);
              if (!userWantsControl) {
                if (isValidDuration(explicitEnterDuration)) {
                  setTimeout(cb, explicitEnterDuration);
                } else {
                  whenTransitionEnds(el, type, cb);
                }
              }
            }
          });
        }
    

    示例如下

    <transition name="fade">
            <span v-show="isanimate" style="background: green; color: #fff">the animate</span>
     </transition>
    
    .fade-enter-active, .fade-leave-active {
          transition: opacity 3s ease-out
        }
        .fade-enter, .fade-leave-to {
          opacity: 0;
        }
    
    mounted () {
            console.log('to mounted')
            setTimeout(() => {
              this.isanimate = true
            }, 30)
          }
    

    resolveTransition方法

    macro task 和 micro task

    其代码的执行顺序按照下列伪代码进行说明

    for (macroTask of macroTaskQueue) {
       handleMacroTask()
       for (microTask of microTaskQueue) {
         handleMicroTask(microTask)
       }
     }
    

    在浏览器中,常见的macro task任务有setTimeout MessageChannel postMessage setImmetiate
    常见的micro task 有 MutationObserver 和Promise.then

    相关文章

      网友评论

          本文标题:vue学习2

          本文链接:https://www.haomeiwen.com/subject/apyssqtx.html