普通input:input
中的v-model
,最终通过target.addEventListener
处理成在节点上监听input
事件function($event){msg=$event.target.value}}
的形式,当input
值变化时msg
也跟着改变。
// main.js
const inputBox = {
template: `<input @input="$emit('input', $event.target.value)">`,
};
new Vue({
el: "#app",
template: `<div>
<input-box v-model="msg"></input-box>
<p>{{msg}}</p>
</div>`,
components: {
inputBox
},
data() {
return {
msg: 'hello world!'
};
},
});
组件:v-model
在组件中则通过给点击事件绑定原生事件,当触发到$emit
的时候,再进行回调函数ƒunction input($$v) {msg=$$v}
的执行,进而达到子组件修改父组件中数据msg
的目的。
二十四、v-slot
v-slot
产生的主要目的是,在组件的使用过程中可以让父组件有修改子组件内容的能力,就像在子组件里面放了个插槽,让父组件往插槽内塞入父组件中的楔子;并且,父组件在子组件中嵌入的楔子也可以访问子组件中的数据。v-slot
的产生让组件的应用更加灵活。
1、具名插槽
let baseLayout = {
template: `<div class="container">
<header>
<slot name="header"></slot>
</header>
<main>
<slot></slot>
</main>
<footer>
<slot name="footer"></slot>
</footer>
</div>`,
data() {
return {
url: ""
};
}
};
new Vue({
el: "#app",
template: `<base-layout>
<template v-slot:header>
<h1>title-txt</h1>
</template>
<p>paragraph-1-txt</p>
<p>paragraph-2-txt</p>
<template v-slot:footer>
<p>foot-txt</p>
</template>
</base-layout>`,
components: {
baseLayout
}
});
引入的组件baseLayout
中的template
被添加了属性v-slot:header
和v-slot:footer
,子组件中定义了对应的插槽被添加了属性name="header"
和name="footer"
,未被进行插槽标识的内容被插入到了匿名的<slot></slot>
中。
2、作用域插槽
let currentUser = {
template: `<span>
<slot name="user" v-bind:userData="childData">{{childData.firstName}}</slot>
</span>`,
data() {
return {
childData: {
firstName: "first",
lastName: "last"
}
};
}
};
new Vue({
el: "#app",
template: `<current-user>
<template v-slot:user="slotProps">{{slotProps.userData.lastName}}</template>
</current-user>`,
components: {
currentUser
}
});
当前例子中作用域插槽通过v-bind:userData="childData"
的方式,将childData
作为参数,父组件中通过v-slot:user="slotProps"
的方式进行接收,为父组件使用子组件中的数据提供了可能。
v-slot
的底层实现请参考vue中的v-slot(源码分析)
二十五、Vue.filters
filters
类似于管道流可以将上一个过滤函数的结果作为下一个过滤函数的第一个参数,又可以在其中传递参数让过滤器更灵活。
// main.js文件
import Vue from "vue";
Vue.filter("filterEmpty", function(val) {
return val || "";
});
Vue.filter("filterA", function(val) {
return val + "平时周末的";
});
Vue.filter("filterB", function(val, info, fn) {
return val + info + fn;
});
new Vue({
el: "#app",
template: `<div>{{msg | filterEmpty | filterA | filterB('爱好是', transformHobby('chess'))}}</div>`,
data() {
return {
msg: "张三"
};
},
methods: {
transformHobby(type) {
const map = {
bike: "骑行",
chess: "象棋",
game: "游戏",
swimming: "游泳"
};
return map[type] || "未知";
}
}
});
其中我们对msg
通过filterEmpty
、filterA
和filterB('爱好是', transformHobby('chess'))}
进行三层过滤。
Vue.filters
的底层实现请查看vue中的filters(源码分析)
二十六、Vue.use
- 作用:
Vue.use
被用来安装Vue.js插件,例如vue-router
、vuex
、element-ui
。 install
方法:如果插件是一个对象,必须提供install
方法。如果插件是一个函数,它会被作为install
方法。install
方法调用时,会将Vue
作为参数传入。- 调用时机:该方法需要在调用
new Vue()
之前被调用。 - 特点:当 install 方法被同一个插件多次调用,插件将只会被安装一次。
二十七、Vue.extend
和选项extends
1、Vue.extend
Vue.extend
使用基础Vue
构造器创建一个“子类”,参数是一个包含组件选项的对象,实例化的过程中可以修改其中的选项,为实现功能的继承提供了思路。
new Vue({
el: "#app",
template: `<div><div id="person1"></div><div id="person2"></div></div>`,
mounted() {
// 定义子类构造函数
var Profile = Vue.extend({
template: '<p @click="showInfo">{{name}} 喜欢 {{fruit}}</p>',
data: function () {
return {
name: '张三',
fruit: '苹果'
}
},
methods: {
showInfo() {
console.log(`${this.name}喜欢${this.fruit}`)
}
}
})
// 实例化1,挂载到`#person1`上
new Profile().$mount('#person1')
// 实例化2,并修改其`data`选项,挂载到`#person2`上
new Profile({
data: function () {
return {
name: '李四',
fruit: '香蕉'
}
},
}).$mount('#person2')
},
});
在当前例子中,通过Vue.extend
构建了子类构造函数Profile
,可以通过new Profile
的方式实例化无数个vm
实例。我们定义初始的template
、data
和methods
供vm
进行使用,如果有变化,在实例的过程中传入新的选项参数即可,比如例子中实例化第二个vm
的时候就对data
进行了调整。
2、选项extends
extends
允许声明扩展另一个组件 (可以是一个简单的选项对象或构造函数),而无需使用 Vue.extend
。这主要是为了便于扩展单文件组件,以实现组件继承的目的。
const common = {
template: `<div>{{name}}</div>`,
data() {
return {
name: '表单'
}
}
}
const create = {
extends: common,
data() {
return {
name: '新增表单'
}
}
}
const edit = {
extends: common,
data() {
return {
name: '编辑表单'
}
}
}
new Vue({
el: "#app",
template: `<div>
<create></create>
<edit></edit>
</div>`,
components: {
create,
edit,
}
});
当前极简demo中定义了公共的表单common
,然后又在新增表单组件create
和编辑表单组件edit
中扩展了common
。
二十八、Vue.mixin
和选项mixins
全局混入和局部混入视情况而定,主要区别在全局混入是通过Vue.mixin
的方式将选项混入到了Vue.options
中,在所有获取子组件构建函数的时候都将其进行了合并,是一种影响全部组件的混入策略。
而局部混入是将选项通过配置mixins
选项的方式合并到当前的子组件中,只有配置了mixins
选项的组件才会受到混入影响,是一种局部的混入策略。
二十九、Vue.directive
和directives
1、使用场景
主要用于对于DOM的操作,比如:文本框聚焦,节点位置控制、防抖节流、权限管理、复制操作等功能
2、钩子函数
bind
:只调用一次,指令第一次绑定到元素时调用。在这里可以进行一次性的初始化设置。inserted
:被绑定元素插入父节点时调用 (仅保证父节点存在,但不一定已被插入文档中)。update
:所在组件的 VNode 更新时调用,但是可能发生在其子 VNode 更新之前。指令的值可能发生了改变,也可能没有。但是你可以通过比较更新前后的值来忽略不必要的模板更新。componentUpdated
:指令所在组件的 VNode 及其子 VNode 全部更新后调用。unbind
:只调用一次,指令与元素解绑时调用。
3、钩子函数参数
el
:指令所绑定的元素,可以用来直接操作 DOM。binding
:一个对象,包含以下 property:name
:指令名,不包括v-
前缀。value
:指令的绑定值,例如:v-my-directive="1 + 1"
中,绑定值为2
。oldValue
:指令绑定的前一个值,仅在update
和componentUpdated
钩子中可用。无论值是否改变都可用。expression
:字符串形式的指令表达式。例如v-my-directive="1 + 1"
中,表达式为"1 + 1"
。arg
:传给指令的参数,可选。例如v-my-directive:foo
中,参数为"foo"
。modifiers
:一个包含修饰符的对象。例如:v-my-directive.foo.bar
中,修饰符对象为{ foo: true, bar: true }
。
vnode
:Vue 编译生成的虚拟节点。oldVnode
:上一个虚拟节点,仅在update
和componentUpdated
钩子中可用。
4、动态指令参数
指令的参数可以是动态的。例如,在 v-mydirective:[argument]="value"
中,argument
参数可以根据组件实例数据进行更新!这使得自定义指令可以在应用中被灵活使用。
三十、vue
中的原生事件
vue
中可以通过@
或者v-on
的方式绑定事件,也可为其添加修饰符。
new Vue({
el: '#app',
template: `<div @click='divClick'><a @clickt='aClick' href=''>点击</a></div>`,
methods: {
divClick() {
console.log('divClick')
},
aClick() {
console.log('aClick')
},
}
})
以上例子如果点击a
会触发其默认行为,如果href
不为空还会进行跳转。除此之外,点击还会继续触发div
上绑定的点击事件。
如果通过@click.stop.prevent='aClick'
的方式为a
标签的点击事件添加修饰符stop
和prevent
,那么就不会触发其a
的默认行为,即使href
不为空也不会进行跳转,同时,div
上的点击事件也不会进行触发。
模板的渲染一般分为编译生成render
函数、render
函数执行生成vNode
和patch
进行渲染。下面按照这步骤进行简单分析。
1、render
通过编译生成的render
函数:
with(this) {
return _c('div', {
on: {
"click": divClick
}
}, [_c('a', {
attrs: {
"href": "http://www.baidu.com"
},
on: {
"click": function ($event) {
$event.stopPropagation();
$event.preventDefault();
return aClick($event)
}
}
}, [_v("点击")])])
}
其中div
的on
作为div
事件描述。a
标签的attrs
作为属性描述,on
作为事件描述,在描述中.stop
被编译成了$event.stopPropagation()
来阻止事件冒泡,.prevent
被编译成了$event.preventDefault()
用来阻止a
标签的默认行为。
2、vNode
通过执行Vue.prototype._render
将render
函数转换成vNode
。
3、patch
patch
的过程中,当完成$el
节点的渲染后会执行invokeCreateHooks(vnode, insertedVnodeQueue)
逻辑,其中,针对attrs
会将其设置为$el
的真实属性,当前例子中会为a
标签设置herf
属性。针对on
会通过target.addEventListener
的方式将其处理过的事件绑定到$el
上,当前例子中会分别对div
和a
中的click
进行处理,再通过addEventListener
的方式进行绑定。
小结
vue
中的事件,从编译生成render
再通过Vue.prototype._render
函数执行render
到生成vNode
,主要是通过on
作为描述。在patch
渲染阶段,将on
描述的事件进行处理再通过addEventListener
的方式绑定到$el
上。
三十一、常用修饰符
1、表单修饰符
(1).lazy
在默认情况下,v-model
在每次 input
事件触发后将输入框的值与数据进行同步 ,可以添加 lazy
修饰符,从而转为在 change
事件之后进行同步:
<input v-model.lazy="msg">
(2).number
如果想自动将用户的输入值转为数值类型,可以给 v-model
添加 number
修饰符:
<input v-model.number="age" type="number">
(3).trim
如果要自动过滤用户输入的首尾空白字符,可以给 v-model
添加 trim
修饰符:
<input v-model.trim="msg">
2、事件修饰符
(1).stop
阻止单击事件继续传播。
<!--这里只会触发a-->
<div @click="divClick"><a v-on:click.stop="aClick">点击</a></div>
(2).prevent
阻止标签的默认行为。
<a href="http://www.baidu.com" v-on:click.prevent="aClick">点击</a>
(3).capture
事件先在有.capture
修饰符的节点上触发,然后在其包裹的内部节点中触发。
<!--这里先执行divClick事件,然后再执行aClick事件-->
<div @click="divClick"><a v-on:click="aClick">点击</a></div>
(4).self
只当在 event.target 是当前元素自身时触发处理函数,即事件不是从内部元素触发的。
<!--在a标签上点击时只会触发aClick事件,只有点击phrase的时候才会触发divClick事件-->
<div @click.self="divClick">phrase<a v-on:click="aClick">点击</a></div>
(5).once
不像其它只能对原生的 DOM 事件起作用的修饰符,.once
修饰符还能被用到自定义的组件事件上,表示当前事件只触发一次。
<a v-on:click.once="aClick">点击</a>
(6).passive
.passive
修饰符尤其能够提升移动端的性能
<!-- 滚动事件的默认行为 (即滚动行为) 将会立即触发 -->
<!-- 而不会等待 `onScroll` 完成 -->
<!-- 这其中包含 `event.preventDefault()` 的情况 -->
<div v-on:scroll.passive="onScroll">...</div>
3、其他修饰符
除了表单和事件的修饰符,Vue
还提供了很多其他修饰符,在使用的时候可以查阅文档。
小结
Vue
中提供了很多好用的功能和api
,那么修饰符的出现就为功能和api
提供了更为丰富的扩展属性和更大的灵活度。
三十二、vue-router
vue
路由是单页面中视图切换的方案,有三种mode
:
- hash,#后的仅仅作为参数,不属于url部分
- history,路径作为请求url请求资源链接,如果找不到会出现404错误
- abstract,服务端渲染场景 hash场景下,会出现
url
链接,再修改其view-router中对应的值。
了解vue-router
的底层实现请参考vue2视图切换:vue-router
三十三、vuex
vuex
是状态管理仓库,一般使用的场景为:多个视图依赖于同一状态,来自不同视图的行为需要变更同一状态。其管理的状态是响应式的,修改也只能显式提交mutation
的方式修改。vuex
有state
、getter
、mutation
、action
和module
五个核心,并且通过module
实现了vuex
树的管理。
了解vuex
的底层实现请参考vue2状态管理:vuex
三十四、eventBus
使用场景:兄弟组件传参
const eventBus = new Vue();
const A = {
template: `<div @click="send">component-a</div>`,
methods: {
send() {
eventBus.$emit('sendData', 'data from A')
}
},
}
const B = {
template: `<div>component-b</div>`,
created() {
eventBus.$on('sendData', (args) => {
console.log(args)
})
},
}
new Vue({
el: '#app',
components: {
A,
B,
},
template: `<div><A></A><B></B></div>`,
})
在当前例子中,A
组件和B
组件称为兄弟组件,A
组件通过事件总线eventBus
中的$emit
分发事件,B
组件则通过$on
来监听事件。
实现原理:eventsMixin
export function eventsMixin (Vue: Class<Component>) {
const hookRE = /^hook:/
Vue.prototype.$on = function (event: string | Array<string>, fn: Function): Component {
const vm: Component = this
if (Array.isArray(event)) {
for (let i = 0, l = event.length; i < l; i++) {
vm.$on(event[i], fn)
}
} else {
(vm._events[event] || (vm._events[event] = [])).push(fn)
// optimize hook:event cost by using a boolean flag marked at registration
// instead of a hash lookup
if (hookRE.test(event)) {
vm._hasHookEvent = true
}
}
return vm
}
Vue.prototype.$once = function (event: string, fn: Function): Component {
const vm: Component = this
function on () {
vm.$off(event, on)
fn.apply(vm, arguments)
}
on.fn = fn
vm.$on(event, on)
return vm
}
Vue.prototype.$off = function (event?: string | Array<string>, fn?: Function): Component {
const vm: Component = this
// all
if (!arguments.length) {
vm._events = Object.create(null)
return vm
}
// array of events
if (Array.isArray(event)) {
for (let i = 0, l = event.length; i < l; i++) {
vm.$off(event[i], fn)
}
return vm
}
// specific event
const cbs = vm._events[event]
if (!cbs) {
return vm
}
if (!fn) {
vm._events[event] = null
return vm
}
// specific handler
let cb
let i = cbs.length
while (i--) {
cb = cbs[i]
if (cb === fn || cb.fn === fn) {
cbs.splice(i, 1)
break
}
}
return vm
}
Vue.prototype.$emit = function (event: string): Component {
const vm: Component = this
if (process.env.NODE_ENV !== 'production') {
const lowerCaseEvent = event.toLowerCase()
if (lowerCaseEvent !== event && vm._events[lowerCaseEvent]) {
tip(
`Event "${lowerCaseEvent}" is emitted in component ` +
`${formatComponentName(vm)} but the handler is registered for "${event}". ` +
`Note that HTML attributes are case-insensitive and you cannot use ` +
`v-on to listen to camelCase events when using in-DOM templates. ` +
`You should probably use "${hyphenate(event)}" instead of "${event}".`
)
}
}
let cbs = vm._events[event]
if (cbs) {
cbs = cbs.length > 1 ? toArray(cbs) : cbs
const args = toArray(arguments, 1)
const info = `event handler for "${event}"`
for (let i = 0, l = cbs.length; i < l; i++) {
invokeWithErrorHandling(cbs[i], vm, args, vm, info)
}
}
return vm
}
}
在Vue
构造函数定义完执行的eventsMixin
函数中,在Vue.prototype
上分别定义了$on
、$emit
、$off
和$once
的方法易实现对事件的绑定、分发、取消和只执行一次的方法。eventBus
就是利用了当new Vue
实例化后实例上的$on
、$emit
、$off
和$once
进行数据传递。
三十五、ref
使用场景: 父组件获取子组件数据或者执行子组件方法
const A = {
template: `<div>{{childData.age}}</div>`,
data() {
return {
childData: {
name: 'qb',
age: 30
},
}
},
methods: {
increaseAge() {
this.childData.age++;
}
}
}
new Vue({
el: '#app',
components: {
A,
},
template: `<A ref='childRef' @click.native='changeChildData'></A>`,
methods: {
changeChildData() {
// 执行子组件的方法
this.$refs.childRef.increaseAge()
// 获取子组件的数据
console.log(this.$refs.childRef.childData);
},
}
})
在当前例子中,通过ref='childRef'
的方式在当前组件中定义一个ref
,可以通过this.$refs.childRef
的方式获取到子组件A
。可以通过this.$refs.childRef.increaseAge()
的方式执行子组件中age
增加的方法,也可以通过this.$refs.childRef.childData
的方式获取到子组件中的数据。
三十六、props
使用场景: 父子传参
const A = {
template: `<div @click='emitData'>{{childData}}</div>`,
props: ['childData'],
methods: {
emitData() {
this.$emit('emitChildData', 'data from child')
}
},
}
new Vue({
el: '#app',
components: {
A
},
template: `<A :childData='parentData' @emitChildData='getChildData'></A>`,
data() {
return {
parentData: 'data from parent'
}
},
methods: {
getChildData(v) {
console.log(v);
}
}
})
从当前例子中可以看出,数据父传子是通过:childData='parentData'
的方式,数据子传父是通过this.$emit('emitChildData', 'data from child')
的方式,然后,父组件通过@emitChildData='getChildData'
的方式进行获取。
1、父组件render
函数
new Vue
中传入的模板template
经过遍历生成的render
函数如下:
with(this) {
return _c('A', {
attrs: {
"childData": parentData
},
on: {
"emitChildData": getChildData
}
})
}
其中data
部分有attrs
和on
来描述属性和方法。
在通过createComponent
创建组件vnode
的过程中,会通过const propsData = extractPropsFromVNodeData(data, Ctor, tag)
的方式获取props
,通过const listeners = data.on
的方式获取listeners
,最后将其作为参数通过new VNode(options)
的方式实例化组件vnode
。
2、子组件渲染
在通过const child = vnode.componentInstance = createComponentInstanceForVnode( vnode, activeInstance )
创建组件实例的过程中,会执行到组件继承自Vue
的._init
方法,通过initEvents
将事件处理后存储到vm._events
中,通过initProps
将childData
赋值到子组件A
的vm
实例上,并进行响应式处理,让其可以通过vm.childData
的方式访问,并且数据发生变化时视图也可以发生改变。
组件模板编译后对应的render
函数是:
with(this) {
return _c('div', {
on: {
"click": emitData
}
}, [_v(_s(childData))])
}
在createElm
完成节点的创建后,在invokeCreateHooks(vnode, insertedVnodeQueue)
阶段,给DOM
原生节点节点绑定emitData
。
3、this.$emit
在点击执行this.$emit
时,会通过var cbs = vm._events[event]
取出_events
中的事件进行执行。
至此,父组件中的传递的数据就在子组件中可以通过this.xxx
的方式获得,也可以通过this.$emit
的方式将子组件中的数据传递给父组件。
prop
数据发生改变引起视图变化的底层逻辑请参考vue2从数据变化到视图变化:props引起视图变化详解
三十七、$attrs
和$listeners
使用场景: 父子组件非props
属性和非native
方法传递
// main.js文件
import Vue from "vue";
const B = {
template: `<div @click="emitData">{{ formParentData }}</div>`,
data() {
return {
formParentData: ''
}
},
inheritAttrs: false,
created() {
this.formParentData = this.$attrs;
console.log(this.$attrs, '--------------a-component-$attrs')
console.log(this.$listeners, '--------------b-component-$listeners')
},
methods: {
emitData() {
this.$emit('onFun', 'form B component')
}
},
}
const A = {
template: `<B v-bind='$attrs' v-on='$listeners'></B>`,
components: {
B,
},
props: ['propData'],
inheritAttrs: false,
created() {
console.log(this.$attrs, '--------------b-component-$attrs')
console.log(this.$listeners, '--------------b-component-$listeners')
}
}
new Vue({
el: '#app',
components: {
A,
},
template: `<A :attrData='parentData' :propData='parentData' @click.native="nativeFun" @onFun="onFun"></A>`,
data() {
return {
parentData: 'msg'
}
},
methods: {
nativeFun() {
console.log('方法A');
},
onFun(v) {
console.log('方法B', v);
},
}
})
当前例子中,new Vue
的template
模板中有attrData
、propData
、click.native
和onFun
在进行传递。实际运行后,在A
组件中this.$attrs
为{attrData: 'msg'}
,this.$listeners
为{onFun:f(...)}
。在A
组件中通过v-bind='$attrs'
和v-on='$listeners'
的方式继续进行属性和方法的传递,在B
组件中就可以获取到A
组件中传入的$attrs
和$listeners
。
当前例子中完成了非props
属性和非native
方法的传递,并且通过v-bind='$attrs'
和v-on='$listeners'
的方式实现了属性和方法的跨层级传递。
同时通过this.$emit
的方法触发了根节点中onFun
事件。
关于例子中的inheritAttrs: false
,默认情况下父作用域的不被认作props
的attribute
绑定将会“回退”且作为普通的HTML
属性应用在子组件的根元素上。当撰写包裹一个目标元素或另一个组件的组件时,这可能不会总是符合预期行为。通过设置inheritAttrs
到false
,这些默认行为将会被去掉。
三十八、$parent
和$children
使用场景: 利用父子关系进行数据的获取或者方法的调用
const A = {
template: `<div @click="changeParentData">{{childRandom}}</div>`,
data() {
return {
childRandom: Math.random()
}
},
mounted() {
console.log(this.$parent.parentCount, '--child-created--'); // 获取父组件中的parentCount
},
methods: {
changeParentData() {
console.log(this.$parent); // 打印当前实例的$parent
this.$parent.changeParentData(); // 调用当前父级中的方法`changeParentData`
},
changeChildData() {
this.childRandom = Math.random();
}
}
}
const B = {
template: `<div>b-component</div>`,
}
new Vue({
el: '#app',
components: {
A,
B,
},
template: `<div><A></A><B></B><p>{{parentCount}}</p><button @click="changeChildrenData">修改子组件数据</button></div>`,
data() {
return {
parentCount: 1
}
},
mounted() {
console.log(this.$children[0].childRandom, '--parent-created--'); // 获取第一个子组件中的childRandom
},
methods: {
changeParentData() {
this.parentCount++;
},
changeChildrenData() {
console.log(this.$children); // 此时有两个子组件
this.$children[0].changeChildData(); // 调起第一个子组件中的'changeChildData'方法
}
}
})
在当前例子中,父组件可以通过this.$children
获取所有的子组件,这里有A
组件和B
组件,可以通过this.$children[0].childRandom
的方式获取子组件A
中的数据,也可以通过this.$children[0].changeChildData()
的方式调起子组件A
中的方法。
子组件可以通过this.$parent
的方式获取父组件,可以通过this.$parent.parentCount
获取父组件中的数据,也可以通过this.$parent.changeParentData()
的方式修改父组件中的数据。
Vue
中$parent
和$children
父子关系的底层构建请参考杂谈:������/parent/children的底层逻辑
三十九、inject
和provide
使用场景:嵌套组件多层级传参
const B = {
template: `<div>{{parentData1}}{{parentData2}}</div>`,
inject: ['parentData1', 'parentData2'],
}
const A = {
template: `<B></B>`,
components: {
B,
},
}
new Vue({
el: '#app',
components: {
A,
},
template: `<A></A>`,
provide: {
parentData1: {
name: 'name-2',
age: 30
},
parentData2: {
name: 'name-2',
age: 29
},
}
})
例子中在new Vue
的时候通过provide
提供了两个数据来源parentData1
和parentData2
,然后跨了一个A
组件在B
组件中通过inject
注入了这两个数据。
1、initProvide
在执行组件内部的this._init
初始化方法时,会执行到initProvide
逻辑:
export function initProvide (vm: Component) {
const provide = vm.$options.provide
if (provide) {
vm._provided = typeof provide === 'function'
? provide.call(vm)
: provide
}
}
如果在当前vm.$options
中存在provide
,会将其执行结果赋值给vm._provided
。
2、initInjections
function initInjections (vm: Component) {
const result = resolveInject(vm.$options.inject, vm)
if (result) {
toggleObserving(false)
Object.keys(result).forEach(key => {
/* istanbul ignore else */
if (process.env.NODE_ENV !== 'production') {
defineReactive(vm, key, result[key], () => {
warn(
`Avoid mutating an injected value directly since the changes will be ` +
`overwritten whenever the provided component re-renders. ` +
`injection being mutated: "${key}"`,
vm
)
})
} else {
defineReactive(vm, key, result[key])
}
})
toggleObserving(true)
}
}
function resolveInject (inject: any, vm: Component): ?Object {
if (inject) {
// inject is :any because flow is not smart enough to figure out cached
const result = Object.create(null)
const keys = hasSymbol
? Reflect.ownKeys(inject)
: Object.keys(inject)
for (let i = 0; i < keys.length; i++) {
const key = keys[i]
// #6574 in case the inject object is observed...
if (key === '__ob__') continue
const provideKey = inject[key].from
let source = vm
while (source) {
if (source._provided && hasOwn(source._provided, provideKey)) {
result[key] = source._provided[provideKey]
break
}
source = source.$parent
}
if (!source) {
if ('default' in inject[key]) {
const provideDefault = inject[key].default
result[key] = typeof provideDefault === 'function'
#### 专业技能
一般来说,面试官会根据你的简历内容去提问,但是技术基础还有需要自己去准备分类,形成自己的知识体系的。简单列一下我自己遇到的一些题
* HTML+CSS
* JavaScript
* 前端框架
* 前端性能优化
* 前端监控
* 模块化+项目构建
* 代码管理
* 信息安全
* 网络协议
* 浏览器
* 算法与数据结构
* 团队管理
最近得空把之前遇到的面试题做了一个整理,包括我本人自己去面试遇到的,还有其他人员去面试遇到的,还有网上刷到的,我都统一的整理了一下,希望对大家有用。
**其中包含HTML、CSS、JavaScript、服务端与网络、Vue、浏览器等等**
**由于文章篇幅有限,仅展示部分内容**
![](https://img-blog.csdnimg.cn/img_convert/ac0b1c2376da47d727e0dc8a77e76478.png)