1. Vue通信的方法有多少种?
1.1 Vue 2.x的通信方式有
1.第一种可以直接通过prop进行通信。
2.第二种通过$emit 和 $on。
this. e m i t ( ′ c h i l d r e n − e v e n t ′ , ′ 我是子组件传递到父组件的内 容 ′ ) t h i s . emit('children-event','我是子组件传递到父组件的内容') this. emit(′children−event′,′我是子组件传递到父组件的内容′)this.on(‘children-event’,function(e){console.log(e)})
3.通过 project 和 inject。
A父组件----------------- provide(){ return{ for:‘这是父组件的内容’} }
B子组件----------------- inject[‘for’] data(){ return { demo : this.for }}
4.通过vuex。
5.通过slot插槽。
6. $attrs 和 $listeners。
1.2 Vue 3.x新增通信方式
7.通过ref加 p a r e n t , parent, parent,child,$refs进行通信。
this. r e f s . c o m A . m e s s a g e / / 就能获得组件内的变量 t h i s . refs.comA.message // 就能获得组件内的变量 this. refs.comA.message//就能获得组件内的变量this.children.message 其中 c h i l d r e n [ 0 ] 为正数第一个 t h i s . children[0] 为正数第一个 this. children[0]为正数第一个this.parent.message
8.通过mitt进行兄弟组件通信。
project / reject 使用场景
1、provider/inject:简单的来说就是在父组件中通过provider来提供变量,然后在子组件中通过inject来注入变量需要注意的是这里不论子组件有多深,只要调用了inject那么就可以注入provider中的数据。而不是局限于只能从当前父组件的prop属性来获取数据。
2、可以替代dispath , 可以替代 this.$parent.something
2.1: Vue2.x和3.x的区别
1.main.js文件
2.x
new Vue({
h:=>h(app)
}).$mount('#app')
3.x的时候
import {createApp} from 'vue'
import App from './App.vue'
createApp(App).mount(#app)
2.router路由文件
// 可以根据路由模式的不同,后面俩可以只引用一个
import { createRouter, createWebHistory, createWebHashHistory } from 'vue-router'
import Home from '@/views/Home.vue'
// 构建我们的页面路由配置,可以看到,这里和原来的写法并无二致。
const routes = [
{
path: '/',
component: Home
}, {
path: '/about',
component: () => import('@/views/About.vue'),
}
]
const router = createRouter({
// 使用 hash 模式构建路由( url中带 # 号的那种)
history: createWebHashHistory(),
// 使用 history 模式构建路由 ( url 中没有 # 号,但生产环境需要特殊配置)
// history: createWebHistory(),
routes
})
export default router
3.mian.js挂载方面
import { createApp } from 'vue'
import App from './App.vue'
import router from './router'
const app = createApp(App)
app.use(router)
app.mount('#app')
4.setUp函数
使用 setup 函数时,它将接受两个参数:
- props : 和组件传值一样传过来的。
- context : 接收的是一个普通额js对象,它暴露三个组件的 property,它不是响应式的。
setup(props, context) {
// Attribute (非响应式对象)
console.log(context.attrs)
// 插槽 (非响应式对象)
console.log(context.slots)
// 触发事件 (方法)
console.log(context.emit) }
<template>
<router-link to="/about">点这里去关于我们页面</router-link>
<div class="home">
这里是一个计数器 >>> <span class="red">{{count}}</span> <br>
<button @click="countAdd">{{btnText}}</button>
</div>
</template>
<script>
// ref 是 vue 3.0 的一个重大变化,其作用为创建响应式的值
import { ref } from 'vue'
// 导出依然是个对象,不过对象中只有一个 setup 函数
export default {
setup () {
// 定义一个不需要改变的数据
const btnText = '点这个按钮上面的数字会变'
// 定义一个 count 的响应式数据,并赋值为 0
const count = ref(0)
// 定义一个函数,修改 count 的值。
const countAdd = () => {
count.value++
}
// 导出一些内容给上面的模板区域使用
return {
btnText,
count,
countAdd
}
}
}
</script>
<style lang="scss">
.home {
line-height: 2;
.red {
color: red;
}
}
</style>
5.reactive
<template>
<router-link to="/">点这里去首页</router-link>
<hr>
<dl>
<dt>{{state.name}}</dt>
<dd>性别:{{state.sex}}</dd>
<dd>地址:{{state.address}}</dd>
</dl>
<button @click="addressChange">更新地址</button>
</template>
<script>
// reactive 是 vue 3.0 的一个重大变化,其作用为创建响应式的对象或数组
import { reactive } from 'vue'
// 导出依然是个对象,不过对象中只有一个 setup 函数
export default {
setup () {
// 定义一个 state 的响应式对象数据,并赋值
const state = reactive({
name: 'FungLeo',
sex: 'boy',
address: '上海'
})
console.log(state)
// 定义一个函数,修改 state 的值。
const addressChange = () => {
state.address += '浦东'
}
// 导出一些内容给上面的模板区域使用
return {
state,
addressChange
}
}
}
</script>
reactive 和 ref 的区别就是,reactive 是处理对象或者数组的。
6、生命周期
2.0生命周期 | 3.0生命周期 | 说明 |
---|---|---|
brforeCreate | setUp | 组件创建之前 |
created | setUp | 组件创建完成 |
beforeMount | onBeforeMount | 组件挂载之前 |
mounted | onMounted | 组件挂载完成 |
brforeUpdate | 数据更新虚拟dom打补丁之前 | |
updated | onUpdated | 数据更新,虚拟DOM渲染完成 |
beforeDestory | onBeforeUnmount | 组件销毁之前 |
destory | onUnmounted | 组件创建组件销毁之后 |
2.mixin是什么?为什么说不建议使用?有什么缺点?
官方解释:vue提供一种混合机制–mixins,用来更高效的实现组件内容的复用。
- mixin可以定义公用的变量或方法,但是minxin的数据不是共享的,也就是说每个组件中的mixin的事例都是不一样的,都是单独存在的个体,不存在相互影响。
- mixin混入对象值为函数的同名函数选项将会进行递归合并为数组,两个函数都会执行,只不过先执行mixin中的同名函数;
- mixin混入对象值为对象的同名对象将会进行替换,都有限执行组件内的同名对象,也就是组件内的同名对象将mixin混入对象的同名对象进行覆盖。
为什么不建议使用?
- 命名冲突。
- 隐含的依赖关系:如果mixin很大,我们需要重构的时候,就会出现很多问题。
new Vue发生了那些事情?
function Vue(option){
if(progress.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)
export default Vue
根据代码可以知我们在实例化Vue的时候会调用其原型上的_init方法。这里没有采用clss的方法是为了把不同的功能解耦分开维护。然后我们看_init发生了什么把!
init.js
Vue.prototype._init = function(options?:Object) {
const vm: Component = this // a uid vm._uid = uid++
let startTag, endTag /* istanbul ignore if */
if (process.env.NODE_ENV !== 'production' && config.performance && mark){
startTag =`vue-perf-start:${vm._uid}` endTag =`vue-perf-end:${vm._uid}`
mark(startTag)
}
// 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 */
if (process.env.NODE_ENV !== 'production') {
initProxy(vm)
}
else {
vm._renderProxy = 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 (process.env.NODE_ENV !== 'production' && 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)
}
}
可以看出init的方法:初始化了生命周期函数,初始化了事件中心,初始化渲染,调用了beforeCreate生命周期函数,初始化了inject/provide,初始化了state(props、methods、data、computed、watch),调用了create生命周期,最后调用vm.$mount函数。
4、keep-alive的作用和用法
概念:
keep-alive是vue的内置组件,当它包裹动态路由的时候,会缓存不活动的组件实例,而不是销毁他们。和transtion相似,keep-alive是一个抽象组件:它本身不会渲染成一个DOM元素,也不会出现在父组件的链接中。
作用:
在组件切换的过程中将状态保留在内存中,防止重复渲染DOM,减少加载时间及性能消耗,提高用户体验。
原理:
在created函数调用时将需要缓存的VNode节点保存在this.cache中,在render时,如果VNode的name符合缓存条件,则会从this.cache中取出之前缓存的VNode实例进行渲染。
Props:
- include:支持字符串和正则表达式,只有名称匹配的组件会被缓存。
- exclude:支持字符串和正则表达式,任何名称匹配的都不会被缓存。
- max:数字,最多可缓存多少个组件实例。
生命周期函数:
1、activated
在keep-alive组件激活时调用。
2、deactivated
在keep-alive组件停用时调用。
使用规则(缓存所有、条件缓存、路由缓存)
// App.js
<template>
<div id="app">
<keep-alive>
<router-view/>
</keep-alive>
</div>
</template>
<script>
export default {
name: 'App'
}
</script>
// 1. 将缓存 name 为 test 的组件
<keep-alive include='test'>
<router-view/>
</keep-alive>
// 2. 将缓存 name 为 a 或者 b 的组件,结合动态组件使用
<keep-alive include='a,b'>
<router-view/>
</keep-alive>
// 3. 使用正则表达式,需使用 v-bind
<keep-alive :include='/a|b/'>
<router-view/>
</keep-alive>
export default new Router({
mode: 'history',
routes: [
{
path: 'goods',
name: 'goods',
component: Goods,
meta: {
keepAlive: false // 不需要缓存
}
},
{
path: 'ratings',
name: 'ratings',
component: Ratings,
meta: {
keepAlive: true // 需要缓存
}
}
]
})
5、vue-router有哪几种导航钩子?
一共有三种:全局导航钩子,组件内钩子,单独路由独享钩子。
第一种:全局导航钩子,可以写在路由最下面也可以写在main.js
// main.js || router.js
//to:跳转到的路由 from:从哪个路由离开 next:显示函数
router.beforeEach((to,from,next)=>{
if(to.path == '/index' && 1 == 2){
next();
}else{
alert("请先登录");
next('/list');
}
});
第二种:组件内钩子,写在单独组件中。
beforeRouteEnter(to, from, next) {
// do someting
// 在渲染该组件的对应路由被 confirm 前调用
},
beforeRouteUpdate(to, from, next) {
// do someting
// 在当前路由改变,但是依然渲染该组件是调用
},
beforeRouteLeave(to, from ,next) {
// do someting
// 导航离开该组件的对应路由时被调用
}
第三种:路由独享钩子。
cont router = new VueRouter({
routes: [
{
path: '/list',
component: List,
beforeEnter: (to, from ,next) => {
// do someting
}
}
]
});
6、什么是vuex?
vuex是一个专门为vue.js应用程序开发的状态管理模式,它采用集中式存储管理应用的所有组件状态,并以相应的规则保证状态以种可预测的方式发生改变。
vuex的五大特性:
- state:存放数据源。
- getters:可以对state进行 计算操作。
- Mutation:定义方法改变state。
- Action:可以进行异步操作,可以调用Mutation的方法。
- modules:模块化分割。
如何使用vuex在(vue2.x中)
1、在main.js中引入store。
2、通过this.
s
t
o
r
e
.
d
i
s
p
a
t
c
h
触发
a
c
t
i
o
n
里面的方法。
3
、通过
t
h
i
s
.
store.dispatch触发action里面的方法。 3、通过 this.
store.dispatch触发action里面的方法。3、通过this.store.commit触发mutation里面的方法。
如何使用vuex在(vue3.x中)
import {userStore} from 'vuex'
setUp(){
let store = userStore()
store.dispatch('updateInfo', 'hi')
}
7、理解$nextTick()
有一个div,默认用了v-if隐藏,点击按钮之后,改变v-if的值让他显示出来,并且取到div中的值:
<div id=app>
<div id="div" v-if="showDiv">我是显示文本</div>
<button @click="showAndGetText">获取内容</button >
</div>
<script>
var app = new Vue({
el: '#app',
data () {
return {
showDiv : false
},
methods: {
showAndGetText () {
this.showDiv = true
let text = document.getElementById('div').innerHTML
console.log(text)
}
}
}
})
</script>
这段代码并不难理解,但是控制台回抛出一个’innerHTML’ of null的错误,因为此时页面并未完成渲染,它并没有获取到div元素,这里涉及到一个Vue的重要概念:异步更新队列。
Vue在观察到数据变化时并不是直接更新DOM,而是开启一个队列,并缓冲同一事件循环中发生的所有数据改变。
在缓冲时会去除重复的数据,这样避免了不必要的计算和DOM操作。然后,在下一个时间循环Tick中,Vue刷新队列并执行已去重的工作。
所以,如果你用一个for循环来动态改变数据100次,其实它只会应用最后一次改变,如果没有这种机制,DOM就要重绘100次,这是我们不愿意看到的。
知道了Vue异步更新DOM原理之后,上面的现象就不难理解了,事实上在this.showDiv = true时,div仍然是没有被创建出来的,下面的
let text =document.getElementById(‘div’).innerHTML
console.log(text)
此时,读取的仍然是这一次事件循环的DOM,而实际上在这一次事件循环中,DOM并没有更新,所以是读取不到的。
我们需要等下一个Vue事件循环,DOM更新完成后再读取,就可以读取到。
那么这时就是$nextTick闪亮登场的时候:
<div id=app>
<div id="div" v-if="showDiv">我是显示文本</div>
<button @click="showAndGetText">获取内容</button >
</div>
<script>
var app = new Vue({
el: '#app',
data () {
return {
showDiv : false
},
methods: {
showAndGetText () {
this.showDiv = true
this.$nextTick(function () {
let text = document.getElementById('div').innerHTML
console.log(text)
})
}
}
}
})
</script>
理论上,我们不应主动去操作DOM,因为Vue核心思想是:数据驱动DOM,但在很多业务里,我们避免不了会操做某些DOM元素,这时我们就需要用到$nextTick。
Vue.js 通常鼓励开发人员沿着“数据驱动”的方式思考,避免直接接触 DOM。
this.$nextTick()官方介绍: 将回调延迟到下次 DOM 更新循环之后执行。 在修改数据之后立即使用它,然后等待 DOM 更新。它跟全局方法 Vue.nextTick 一样,不同的是:回调的 this 自动绑定到调用它的实例上。