Vue的那些事情

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(childrenevent,我是子组件传递到父组件的内)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生命周期说明
brforeCreatesetUp组件创建之前
createdsetUp组件创建完成
beforeMountonBeforeMount组件挂载之前
mountedonMounted组件挂载完成
brforeUpdate数据更新虚拟dom打补丁之前
updatedonUpdated数据更新,虚拟DOM渲染完成
beforeDestoryonBeforeUnmount组件销毁之前
destoryonUnmounted组件创建组件销毁之后
2.mixin是什么?为什么说不建议使用?有什么缺点?

官方解释:vue提供一种混合机制–mixins,用来更高效的实现组件内容的复用。

  1. mixin可以定义公用的变量或方法,但是minxin的数据不是共享的,也就是说每个组件中的mixin的事例都是不一样的,都是单独存在的个体,不存在相互影响。
  2. mixin混入对象值为函数的同名函数选项将会进行递归合并为数组,两个函数都会执行,只不过先执行mixin中的同名函数;
  3. mixin混入对象值为对象的同名对象将会进行替换,都有限执行组件内的同名对象,也就是组件内的同名对象将mixin混入对象的同名对象进行覆盖。
为什么不建议使用?
  1. 命名冲突。
  2. 隐含的依赖关系:如果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:

  1. include:支持字符串和正则表达式,只有名称匹配的组件会被缓存。
  2. exclude:支持字符串和正则表达式,任何名称匹配的都不会被缓存。
  3. 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 自动绑定到调用它的实例上。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值