最新公告
  • 欢迎您光临起源地模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • 深入浅出vue.js 第十三章 实例方法与全局API的实现原理

    正文概述 掘金(----影子)   2020-12-11   674

    上一章介绍了Vue.js内部的整体结构,知道了它会向构造函数添加一些属性和方法。本章中,我们将详细介绍它的实例方法和全局API的实现原理

    在Vue.js内部,有这样一段代码

        import {initMixin} from './init'
        import {stateMixin} from './state'
        import {renderMixin} from './render'
        import {eventsMixin} from './events'
        import {lifecycleMixin} from './liftcycle'
        import {warn} from '../util/index'
        function Vue (options){
          if(process.env.NODE_ENV !== 'production' && !(this instanceof Vue)){
            warn('Vue is a constructor and should be called with the `new` keyword)
          }
          this._init(options)
        }
        initMixin(Vue)
        stateMixin(Vue)
        renderMixin(Vue)
        eventsMixin(Vue)
        lifecycleMixin(Vue)
        export default(Vue)
    

    其中定义了Vue构造函数,然后分别调用了initMixin,stateMixin,eventMixin,lifecycleMixin和renderMixin这五个函数,并将Vue构造函数当做参数传给了5个函数。

    这5个函数的作用是向Vue的原型中挂载方法。

    13.1 数据相关的实例方法

    与数据相关的实例方法有3个,分别是vm.watch,vm.watch,vm.watch,vm.set,vm.$delete,它们是在stateMixin中挂载到Vue的原型上的,

    import {set,del} from '../observer/index'
    export function stateMixin(vue){
      Vue.prototype.$set = set
      Vue.prototype.$delete = del
      Vue.Prototype.$watch = function (expOrFn,cb,options){}
    }
    

    当stateMixin被调用时,会向Vue构造函数的prototype属性挂载这个3个实例方法

    13.2 事件相关的实例方法

    与事件相关的实例方法:vm.on,vm.on,vm.on,vm.once,vm.offvm.off和vm.off和vm.emit。当eventMixin被调用时,会添加这个4个实例方法

    13.2.1 vm.$on

    vm.$on(event,callback)
    

    这个和v-on 好像没关系,别搞混了。

    用法 监听当前实例上的自定义事件,事件可以由vm.$emit触发。回调函数会接收所有传入事件所触发的函数的额外参数

    vm.$on('test',function(msg){
      console.log(msg)
    })
    vm.$emit('test','hi')
    // => 'hi'
    

    vm._events 是一个对象,用来存储事件。在代码中,我们使用事件名(event)从vm_events中取出事件列表,如果列表不存在,则使用空数组初始化,然后再将回调函数添加到事件列表中

    这样事件就注册好了,vm._events哪来的?事实上,在执行new Vue()时,Vue会执行this.init 方法进行一系列初始化操作,其中Vue.js的实例上创建了一个_events属性,用来存储事件

    vm.events = Object.create(null)
    

    13.2.2 vm.$off

    vm.$off([event,callback])

    // 移除所有事件监听器
    if(!arguments.length){
        vm._events = Object.create(null)
        return vm
    }
    

    this.offvm.off 和vm.off和vm.off 是同一个方法,vm是this的别名

    13.2.3 vm.$once

    vm.$once(event,callback) 用法:监听一个自定义事件,但是只触发一次,在第一次触发之后移除监听器

    13.2.4 vm.$emit

    vm.$emit(event,[...args]) 触发当前实例上的事件。附加参数都会传递给监听器回调

    所有事件监听器回调函数都会存储在vm.events中,所以触发事件的实现思路是使用事件名从vm.events中取出对应的事件监听器回调函数列表。然后依次执行列表中的监听器回调并将参数传递给监听器回调。

    13.3 生命周期相关的实例方法

    与生命周期相关的实例方法有4个,分别是vm.mount,vm.mount,vm.mount,vm.forceUpdate,vm.nextTickvm.nextTick和vm.nextTick和vm.destroy

    vm.forceUpdatevm.forceUpdate和vm.forceUpdate和vm.destroy是lifecyleMixin挂载的

    vm.$nextTick 方法是从renderMixin挂载的

    vm.$mount 是跨平台代码中挂载到Vue构造函数的prototype属性上的

    vm.$forceUpdate

    作用就是迫使Vue.js实例重新渲染

    Vue.prototype.$forceUpdate = function(){
        const vm = this
        if(vm.watcher){
           vm._watcher.update()
        }
    }
    

    vm.watcher 就是Vue.js实例的watcher,每当组件内依赖的数据发生变化时,都会自动触发Vue.js实例中_watcher 的update方法

    vm.$forceUpdate是手动通知Vue.js实例重新渲染

    vm.$destroy

    Vue.js 实例的$children属性存储了所有子组件

    vm.$nextTick

    nextTick 接收一个回调函数作为参数,它的作用是将回调延迟到下次DOM更新周期之后执行。

    它与全局方法Vue.nextTick一样,不同的是回调的this自动绑定到调用它的实例上。如果没有提供回调且在支持promise的环境中,则返回一个Promise

    new Vue({
      methods:{
        example:function(){
          this.message = 'changed'
          // DOM还没更新
          this.$nextTick(function(){
          //DOM 闲杂更新了
          //this 绑定到当前实例
            this.doSomethingElse()
          })
        }
       }
     })
    

    有个问题: 下次DOM更新周期之后执行,是什么时候呢? 当状态发生变化时,watcher触发渲染的操作不是同步的,而是异步的。

    1. 为什么Vue.js使用异步更新队列

    react的setData()也是这个原理吧

    Vue.js中有个队列,每当需要渲染时,会将watcher推送到这个队列中,在下一次事件循环中再让watcher触发渲染流程

    如果在同一轮事件循环中有两个数据发生了变化,那么组件的watcher会收到两份通知,从而进行两次渲染。事实上,并不需要渲染两次,虚拟DOM会对整个组件进行渲染,所以只需要等所有状态修改完毕后,一次性将整个组件的DOM渲染到最新即可

    要解决这个问题,收到通知watcher实例添加到队列中缓存起来,并判断队列是否已经存在相同的watcher。然后在下一次事件循环中,会让队列watcher触发渲染。这样保证了即便在同一事件循环中有两个状态发生改变,watcher最后也执行一次渲染流程

    2.什么是事件循环

    我们都知道JavaScript是一门单线程且非阻塞的脚本语言。

    微任务:

    • Promise.then
    • MutationObserver
    • Object.observe
    • process.nextTick

    宏任务

    • setTimeout
    • setInterval
    • setImmediate
    • MessageChannel
    • requesAnimationFrame
    • I/O
    • UI 交互事件

    3. 什么是执行栈

    当我们执行了一个方法时,JavaScript会生成一个与这个方法对应的执行环境(context),又叫执行上下文。这个执行环境中有这个方法的私有作用域,上层作用域的指向,方法的参数,私有作用域中定义的变量以及this对象。这个执行环境会被添加到一个栈中,这个栈就是执行栈

    new Vue({
      methods: {
        example:function(){
          // 先使用nextTick 注册回调
          this.$nextTick(function(){
            //DOM 没有更新
          })
          // 然后修改数据
          this.message = 'changed'
        }
      },
    })
    
    new Vue({
      methods: {
        example:function(){
          // 先使用setTimeout向宏任务中注册回调
        setTimeout(_ => {
          // DOM 现在更新了
        },0)
          // 然后修改数据向微任务中注册回调 ---- watcher的更新是微任务,异步更新的
          this.message = 'changed'
        }
      },
    })
    

    微任务优先级太高,也可能出现问题。也可强制使用宏任务的方法

    vm.$mount

    vm.$mount([elementOrSelector])
    

    如果Vue.js实例在实例化时没有收到el选项,则它处于“未挂载”状态,没有关联的DOM元素。我们可以使用vm.$mount手动挂载一个未挂载的实例。

    13.4 全局API的实现原理

    现在我们已经了解了Vue.js实例方法的内部原理,接下来将介绍全局API的内部原理

    13.4.1 Vue.extend

    Vue.extend(options) 用法: 使用基础Vue构造器创建一个"子类",其参数是一个包含”组件选项“的对象。data选项是特例,在Vue.extend()中,它必须是函数:

    var Profile = Vue.extend({
      template:'<p>{{firstName}}{{lastName}} aka {{alias}}',
      data:function(){
        return {
          firstName:'Walter',
          lastName:'Whilte',
          alias:'Heisenberg'
        }
      }
    })
    // 创建Profile实例,并挂载到一个元素上
    new Profile().$mount('#mount-point')
    

    全局API和实例方法不同,后者是在Vue的原型上挂载方法,也就是Vue.prototype上挂载方法,而前者是直接在Vue上挂载方法。

    Vue.extend = function(extendOptions){
    // 做点什么
    }
    

    总体来说,其实就是创建了一个Sub函数并继承了父级。如果直接使用Vue.extend,则Sub继承于Vue构造函数

    13.4.2 Vue.nextTick

    Vue.nextTick([callback,context])

    vm.msg = 'Hello'
    // DOM还没有更新
    Vue.nextTick(function(){
     // DOM 更新了
    })
    
    // 作为一个Promise 使用
    Vue.nextTick().then(function(){ // DOM 更新了})
    

    13.4.3 Vue.set

    Vue.set(target,key,value) Vue.set和vm.$set 的实现原理相同

    是Vue和组件的区别吗 为啥要设置两个呢?

    13.4.4 Vue.delete

    Vue.delete(target,key) 上同

    13.4.5 Vue.directive

    注册或获取全局指令 Vue.directive(id,[definition])

    Vue.directive('my-directive',{
      bind:function(){},
      inserted:function(){},
      update:function(){},
      componentUpdated:function(){},
      unbind:function(){}
    })
    // 注册(指令函数)
    Vue.directive('my-directive',function(){
      // 这里将会被bind和update调用
    })
    // getter 方法,返回已注册的指令
    var myDirective = Vue.directive('my-directive')
    

    虽然代码复用和抽象的主要形式是组件,但是有些情况下,仍需对普通DOM元素进行底层操作,这时就会用到自定义指令

    13.4.6 Vue.filter

    注册或获取全局过滤器 Vue.filter(id,[definition])

    Vue.filter('my-filter',function(value){
     // 返回处理后的值
    })
    
    // getter 方法,返回已注册的过滤器
    var myFilter = Vue.filter('my-filter')
    

    过滤器可以用在两个地方:双花括号插值和v-bind表达式

    13.4.7 Vue.component

    Vue.component(id,[definition]) 注册或获取全局组件。注册组件时,还会自动使用给定的id设置组件的名称。


    起源地下载网 » 深入浅出vue.js 第十三章 实例方法与全局API的实现原理

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    模板不会安装或需要功能定制以及二次开发?
    请QQ联系我们

    发表评论

    还没有评论,快来抢沙发吧!

    如需帝国cms功能定制以及二次开发请联系我们

    联系作者

    请选择支付方式

    ×
    迅虎支付宝
    迅虎微信
    支付宝当面付
    余额支付
    ×
    微信扫码支付 0 元