深入浅出 Vue Mixin,2024前端大厂面试经验分享

}

// newBase 就拥有了三种口味。

const newBase = mergeStrategies(base, mixins);

注意一下这个 mergeStrategies

合并策略可以你想要的形式,也就是说你可以自定义自己的策略,这是其一。另外要解决冲突的问题。上面是通过 Object.assign 来实现的,那么 mixins 内的方法会覆盖base 内的内容。如果这不是你期望的结果,可以调换 mixin 和 base 的位置。

组合大于继承 && DRY

想象一下上面的例子用继承如何实现?由于 js 是单继承语言,只能一层层继承。写起来很繁琐。这里就体现了 mixin 的好处。符合组合大于继承的原则。

mixin 内通常是提取了公用功能的代码。而不是每一个地方都写一遍。符合 DRY 原则。

什么是 vue mixin


vue mixin 是针对组件间功能共享来做的。可以对组件的任意部分(生命周期, data等)进行mixin,但不同的 mixin 之后的合并策略不同。在源码分析部分会介绍细节。

组件级 mixin

假设两个功能组件 model 和 tooltip ,他们都有一个显示和关闭的 toggle 动作:

//modal

const Modal = {

template: ‘#modal’,

data() {

return {

isShowing: false

}

},

methods: {

toggleShow() {

this.isShowing = !this.isShowing;

}

}

}

//tooltip

const Tooltip = {

template: ‘#tooltip’,

data() {

return {

isShowing: false

}

},

methods: {

toggleShow() {

this.isShowing = !this.isShowing;

}

}

}

可以用 mixin 这么写:

const toggleMixin = {

data() {

return {

isShowing: false

}

},

methods: {

toggleShow() {

this.isShowing = !this.isShowing;

}

}

}

const Modal = {

template: ‘#modal’,

mixins: [toggleMixin]

};

const Tooltip = {

template: ‘#tooltip’,

mixins: [toggleMixin],

};

全局 mixin

全局 mixin 会作用到每一个 vue 实例上。所以使用的时候要慎重。通常会用 plugin 来显示的声明用到了那些 mixin。

比如 vuex。我们都知道它在每一个实例上扩展了一个  在任意一个组件内可以调用 store。那么他是如何实现的呢?

在 src/mixin.js 内

export default function (Vue) {

const version = Number(Vue.version.split(‘.’)[0])

if (version >= 2) {

Vue.mixin({ beforeCreate: vuexInit })

} else {

// override init and inject vuex init procedure

// for 1.x backwards compatibility.

const _init = Vue.prototype._init

Vue.prototype._init = function (options = {}) {

options.init = options.init

? [vuexInit].concat(options.init)

: vuexInit

_init.call(this, options)

}

}

/**

* Vuex init hook, injected into each instances init hooks list.

*/

function vuexInit () {

const options = this.$options

// store injection

if (options.store) {

this.$store = typeof options.store === ‘function’

? options.store()

: options.store

} else if (options.parent && options.parent.$store) {

this. s t o r e   =   o p t i o n s . p a r e n t . store = options.parent. store = options.parent.store

}

}

}

我们看到 在 Vue 2.0 以上版本,通过 Vue.mixin({ beforeCreate: vuexInit })实现了在每一个实例的 beforeCreate 生命周期调用vuexInit 方法。

而 vuexInit 方法则是:在跟节点我们会直接把store 注入,在其他节点则拿父级节点的 store,这样this.$store 永远是你在根节点注入的那个store。

vue mixin 源码实现


在 Vuex 的例子中,我们通过 Vue.mixin({ beforeCreate: vuexInit }) 实现对实例的 $store 扩展。

全局 mixin 注册

我们先看一下 mixin 是如何挂载到原型上的。

在 src/core/index.js 中:

import Vue from ‘./instance/index’

import { initGlobalAPI } from ‘./global-api/index’

initGlobalAPI(Vue)

export default Vue

我们发现有一个 initGlobalAPI。在 src/global-api/index 中:

/* @flow */

import config from ‘…/config’

import { initUse } from ‘./use’

import { initMixin } from ‘./mixin’

import { initExtend } from ‘./extend’

import { initAssetRegisters } from ‘./assets’

import { set, del } from ‘…/observer/index’

import { ASSET_TYPES } from ‘shared/constants’

import builtInComponents from ‘…/components/index’

import {

warn,

extend,

nextTick,

mergeOptions,

defineReactive

} from ‘…/util/index’

export function initGlobalAPI (Vue: GlobalAPI) {

// config

const configDef = {}

configDef.get = () => config

if (process.env.NODE_ENV !== ‘production’) {

configDef.set = () => {

warn(

‘Do not replace the Vue.config object, set individual fields instead.’

)

}

}

Object.defineProperty(Vue, ‘config’, configDef)

// exposed util methods.

// NOTE: these are not considered part of the public API - avoid relying on

// them unless you are aware of the risk.

Vue.util = {

warn,

extend,

mergeOptions,

defineReactive

}

Vue.set = set

Vue.delete = del

Vue.nextTick = nextTick

Vue.options = Object.create(null)

ASSET_TYPES.forEach(type => {

Vue.options[type + ‘s’] = Object.create(null)

})

// this is used to identify the “base” constructor to extend all plain-object

// components with in Weex’s multi-instance scenarios.

Vue.options._base = Vue

extend(Vue.options.components, builtInComponents)

initUse(Vue)

initMixin(Vue)

initExtend(Vue)

initAssetRegisters(Vue)

}

所有全局的方法都在这里注册。我们关注 initMixin 方法,定义在 src/core/global-api/mixin.js:

import { mergeOptions } from ‘…/util/index’

export function initMixin (Vue: GlobalAPI) {

Vue.mixin = function (mixin: Object) {

this.options = mergeOptions(this.options, mixin)

return this

}

}

至此我们发现了 Vue 如何挂载全局 mixin。

mixin 合并策略

vuex 通过 beforeCreate Hook 实现为所有 vm 添加 $store 实例。让我们先把 hook 的事情放一边。看一看 beforeCreate 如何实现。

在 src/core/instance/init.js 中:

export function initMixin (Vue: Class) {

Vue.prototype._init = function (options?: Object) {

// remove unrelated code

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’)

// remove unrelated code

if (vm.$options.el) {

vm. m o u n t ( v m . mount(vm. mount(vm.options.el)

}

}

}

我们可以看到在 initRender 完成后,会调用 callHook(vm, 'beforeCreate')。而 init 实在 vue 实例化会执行的。

在 src/core/instance/lifecycle.js 中:

export function callHook (vm: Component, hook: string) {

// #7573 disable dep collection when invoking lifecycle hooks

pushTarget()

const handlers = vm.$options[hook]

if (handlers) {

for (let i = 0, j = handlers.length; i < j; i++) {

try {

handlers[i].call(vm)

} catch (e) {

handleError(e, vm, ${hook} hook)

}

}

}

if (vm._hasHookEvent) {

vm.$emit(‘hook:’ + hook)

}

popTarget()

}

在对 beforeCreate 执行 callHook 过程中,会先从 vue 实例的 options 中取出所有挂载的 handlers。然后循环调用 call 方法执行所有的 hook:

handlers[i].call(vm)

由此我们可以了解到全局的 hook mixin 会和要 mixin 的组件合并 hook,最后生成一个数组。

回头再看:

import { mergeOptions } from ‘…/util/index’

export function initMixin (Vue: GlobalAPI) {

Vue.mixin = function (mixin: Object) {

this.options = mergeOptions(this.options, mixin)

return this

}

}

this.options 默认是 vue 内置的一些 option:

image

mixin 就是你要混入的对象。我们来看一看 mergeOptions。定义在 src/core/util/options.js:

export function mergeOptions (

parent: Object,

child: Object,

vm?: Component

): Object {

if (process.env.NODE_ENV !== ‘production’) {

checkComponents(child)

}

if (typeof child === ‘function’) {

child = child.options

}

normalizeProps(child, vm)

normalizeInject(child, vm)

normalizeDirectives(child)

const extendsFrom = child.extends

if (extendsFrom) {

parent = mergeOptions(parent, extendsFrom, vm)

}

if (child.mixins) {

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数前端工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年Web前端开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
img
img
img
img
img
img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上前端开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新

如果你觉得这些内容对你有帮助,可以添加V获取:vip1024c (备注前端)
img

总结

我在成长过程中也是一路摸爬滚打,没有任何人的指点,所以走的很艰难。例如在大三的时候,如果有个学长可以阶段性的指点一二,如果有已经工作的师兄可以告诉我工作上需要什么,我应该前面的三年可以缩短一半;后来去面试bat,失败了有5、6次,每次也不知道具体是什么原因,都是靠面试回忆去猜测可能是哪方面的问题,回来学习和完善,当你真正去招人的时候,你就会知道面试记录是多么重要,面试官可以从面试记录里看到你的成长,总是去面试,总是没有成长,就会被定义为缺乏潜力。

CodeChina开源项目:【大厂前端面试题解析+核心总结学习笔记+真实项目实战+最新讲解视频】

image
12036249394)]
[外链图片转存中…(img-VGlZq30i-1712036249394)]
[外链图片转存中…(img-rBHIp6s7-1712036249395)]
[外链图片转存中…(img-BKUmtuLX-1712036249395)]

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上前端开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新

如果你觉得这些内容对你有帮助,可以添加V获取:vip1024c (备注前端)
[外链图片转存中…(img-844HMBge-1712036249396)]

总结

我在成长过程中也是一路摸爬滚打,没有任何人的指点,所以走的很艰难。例如在大三的时候,如果有个学长可以阶段性的指点一二,如果有已经工作的师兄可以告诉我工作上需要什么,我应该前面的三年可以缩短一半;后来去面试bat,失败了有5、6次,每次也不知道具体是什么原因,都是靠面试回忆去猜测可能是哪方面的问题,回来学习和完善,当你真正去招人的时候,你就会知道面试记录是多么重要,面试官可以从面试记录里看到你的成长,总是去面试,总是没有成长,就会被定义为缺乏潜力。

CodeChina开源项目:【大厂前端面试题解析+核心总结学习笔记+真实项目实战+最新讲解视频】

[外链图片转存中…(img-uClAVs2n-1712036249397)]
image

  • 28
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值