钩子的应用和生命周期的实现

钩子(hook)

钩子的概念源于Windows的消息处理机制,通过设置钩子,应用程序可以对所有的消息事件进行拦截,然后执行钩子函数,对消息进行想要的处理方式。比如截获键盘、鼠标的输入,屏幕取词,日志监视等等,钩子实际上是一个处理消息的程序段,在没有到达目的窗口前,钩子程序就先捕获该消息,亦即钩子函数先得到控制权

钩子机制也叫hook机制,或者你可以把它理解成一种匹配机制,就是我们在代码中设置一些钩子,然后程序执行时自动去匹配这些钩子

在javascript中钩子是将需要执行的函数或者其他一系列动作注册到一个统一的入口,程序通过调用这个钩子来执行这些已经注册的函数,还有就是提供一个可以响应默认流程的机制的时机,像react中生命周期的componentWillMount,componentDidMount等,这些都是钩子函数。

简而言之,钩子函数就是拥有特殊匹配规则的回调函数


为什么要使用钩子?怎样去衡量该不该使用钩子?

当某一个任务有一个默认的响应流程的时候就可以给该任务加入钩子


一个钩子的基本结构


钩子的构成遵循发布订阅的设计模式

1.定义钩子函数

2.绑定钩子函数

3.卸载钩子函数

let hooks = { 

  after_login: {

    type: 'listener',

    mulit: true,

    listener: []

  },

  ...

}

function bindHook(name, listener) {

  if (!name) throw new Error('缺少hookname');

  if (name in hooks === false) {

    throw new Error('不存在的hookname');

  }

  if (hooks[name].mulit === true) {

    hooks[name].listener.push(listener);

  } else {

    hooks[name].listener = listener;

  }

}

function emitHook(name, ...args) {

  if (!hooks[name]) throw new Error('不存在的hook name');

  let hook = hooks[name];

  if (hook.mulit === true && hook.type === 'listener') {

    if (Array.isArray(hook.listener)) {

      let promiseAll = hook.listener.map(item => Promise.resolve(item.call(pluginModule, ...args)));

      return Promise.all(promiseAll);

    }

  } else if (hook.mulit === false && hook.type === 'listener') {

    if (typeof hook.listener === 'function') {

      return Promise.resolve(hook.listener.call(pluginModule, ...args));

    }

  }

}

pluginModule = {

  hooks: hooks,

  bindHook: bindHook,

  emitHook: emitHook

}

钩子的具体使用场景

生命周期(lifecycle):init -> created -> compiled -> ......

生命周期中每个周期再单独定义一个小周期(extend):beforeInit -> init -> inited;  beforeCompile -> compile -> compiled

数据请求: beforeRequest(参数处理,headers处理) -> afterRequested(返回参数处理)

路由切换: router.beforeEach -> router.afterEach

富文本编辑器:例如保存,新建,导入导出

懒加载


Vue里面钩子的应用


一、路由钩子

路由钩子主要是给使用者在路由发生变化时进行一些特殊的处理而定义的

1.全局钩子

const router = new VueRouter({ ... })


router.beforeEach((to, from, next) => {

 // ...

 next()

})

2.路由独享的钩子

const router = new VueRouter({

 routes: [

  {

   path: '/foo',

   component: Foo,

   beforeEnter: (to, from, next) => {

    // ...

   }

  }

 ]

})

3.组件内的钩子

const Foo = {

 template: `...`,

 beforeRouteEnter (to, from, next) {

  // 在渲染该组件的对应路由被 confirm 前调用

  // 不!能!获取组件实例 `this`

  // 因为当守卫执行前,组件实例还没被创建

 },

 beforeRouteUpdate (to, from, next) {

  // 在当前路由改变,但是该组件被复用时调用

  // 举例来说,对于一个带有动态参数的路径 /foo/:id,在 /foo/1 和 /foo/2 之间跳转的时候,

  // 由于会渲染同样的 Foo 组件,因此组件实例会被复用。而这个钩子就会在这个情况下被调用。

  // 可以访问组件实例 `this`

 },

 beforeRouteLeave (to, from, next) {

  // 导航离开该组件的对应路由时调用

  // 可以访问组件实例 `this`

 }

}

二、Vue生命周期hooks


  vue ssr 新加入了asyncData钩子

理解vue实例创建过程(vue2.1.9+)


initLifecycle:生命周期变量初始化

initEvents:初始化events,绑定父组件传过来的事件到$listeners

initRender:渲染Vnode

initInjections:将父组件的provider注入当前的实例的injection中(vue2.2加入的新选项)

initState: 状态初始化data/props/methods/computed/watch

initProvide:初始化provide

vue(2.0.0 - 2.1.9)


三、$emit



四、指令directive



React里面的钩子


一、路由

onEnter和onLeave

withRouter中的setRouteLeaveHook

二、生命周期(版本16+)

getDerivedStateFromProps 组件每次被rerender的时候,包括在组件构建之后

getSnapshotBeforeUpdate 组件更新前触发

shouldComponentUpdate 组件Props或者state改变时触发

componentDidMount 挂载后

componentDidUpdate 组件更新后触发

componentDidCatch(error,info) 获取到javascript错误

componentWillUnmount 组件卸载时触发

生命周期


根据百度百科,很好理解:

生:create

老:update

病:error

死:destroy

自己实现一个简单的生命周期


在生命周期中使用钩子需要注意实例的传递

var hooks = {

  init: {

    type: 'listener',

    mulit: false,

    listener: null

  },

  beforeCompile: {

type: 'listener',

mulit: false,

listener: null

  },

  compile: {

type: 'listener',

mulit: false,

listener: null

  },

  compiled: {

type: 'listener',

mulit: false,

listener: null

  },

  created: {

type: 'listener',

mulit: false,

listener: null

  }

}

function bindHook(name, listener) {

  if (!name) throw new Error('缺少hookname');

  if (name in hooks === false) {

    throw new Error('不存在的hookname');

  }

  if (hooks[name].mulit === true) {

    hooks[name].listener.push(listener);

  } else {

    hooks[name].listener = listener;

  }

}

function emitHook(name, ...args) {

  if (!hooks[name]) throw new Error('不存在的hook name');

  let hook = hooks[name];

  if (hook.mulit === true && hook.type === 'listener') {

    if (Array.isArray(hook.listener)) {

      let promiseAll = hook.listener.map(item => Promise.resolve(item.call(hookModule, ...args)));

      return Promise.all(promiseAll);

    }

  } else if (hook.mulit === false && hook.type === 'listener') {

    if (typeof hook.listener === 'function') {

    return Promise.resolve(hook.listener.call(hookModule, ...args));

    }

  }

}

let $instance  = 0

hookModule = {

  hooks,

  $instance,

  bindHook,

  emitHook

}

bindHook('init', function()  {

return new Promise((resolve, reject) => {

setTimeout(() => {

            console.log('init')

this.$instance++

resolve('resolve')

        }, 2000)

})

})

bindHook('beforeCompile', function() {

return new Promise((resolve, reject) => {

setTimeout(() => {

this.$instance++

console.log('beforeCompile')

resolve('beforeComile')

}, 1000)

})

})

bindHook('compile', function() {

return new Promise((resolve, reject) => {

emitHook('beforeCompile')

        setTimeout(() => {

            console.log('compile')

emitHook('compiled')

this.$instance++

resolve('compiled')

        }, 1000)

})

})

bindHook('compiled', function() {

return new Promise((resolve, reject) => {

setTimeout(() => {

this.$instance++

console.log('compiled')

resolve('compiled')

}, 1000)

})

})

bindHook('created', function() {

return new Promise((resolve, reject) => {

setTimeout(() => {

this.$instance++

            console.log('created')

resolve('created')

        }, 1000)

})

})

emitHook('init').then(() => emitHook('compile').then())

// hookModule.$instance === 4

切记不可使用箭头函数,箭头函数绑定了父上下文,实例无法通过this传入函数中

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 220,809评论 6 513
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 94,189评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 167,290评论 0 359
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 59,399评论 1 294
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 68,425评论 6 397
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 52,116评论 1 308
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,710评论 3 420
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,629评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 46,155评论 1 319
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 38,261评论 3 339
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,399评论 1 352
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 36,068评论 5 347
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,758评论 3 332
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,252评论 0 23
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,381评论 1 271
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,747评论 3 375
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,402评论 2 358

推荐阅读更多精彩内容