文章目录
Vue2与Vue3的区别?
创建应用
Vue3中的 Vue不再是一个构造器 而是一个对象
创建应用使用createApp函数创建Vue应用
单文件中可以使用script setup语法糖 代替setup函数 作为组合式api入口(不需要手动暴露大量状态的方法给模板)
模块化开发组件可以不用注册直接使用 (仅限于setup语法糖(一个vue文件只有一个语法糖,可有多个script标签)中)
template选项中根标签可以有多个
API
vue3: 组合式API。 setup函数作为组合式api入口 使得代码可读性更强
vue2: 选项卡API。 data methods computed …
Vue3:
<script>
const { createApp, ref, reactive } = Vue;//js中使用到的
createApp({
setup() {
let obj = reactive({ name: 'terry' });
let arr = ref([1,2,3,4]);
return { obj, arr, }//div中使用到的
}
}).mount('#app');//绑定的div
</script>
Vue2:
<script>
new Vue({
el:'#app',
data:{ },//存放数据,变量,响应式依赖
created(){ },
methods:{},
computed:{},
watch:{}
})
</script>
生命周期
(仅名字变了,功能没变)
- setup代替beforeCreate、created。 this不继续指向组件实例 不使用this
- onBeforeMount(挂载前)
- onMounted(挂载)
- onBeforeUpdate(更新前)
- onUpdate(更新)
- onBeforeUnmount(解绑前)替代beforeDestory (销毁前)
- onUnmounted (解绑)替代destoryed(销毁)
- onErrorCaptured(错误调用)
- onActivated(激活)
- onDeactivated(未激活)
- onRenderTracked(不常用)
- onRenderTriggered(不常用)
- onServerPrefetch(不常用)
v-if和v-for优先级
v2: v-for优先级高于v-if优先级
v3: v-if优先级高于v-for优先级
不建议vif和vfor同时使用 优先级不明显 (别的还好,但如果if中正好用到for的数据,就渲染不出来)
nextTick
vueDom更新机制 异步更新 数据发生改变 此时dom不会立即同步更新 而是在下一次回调中更新dom
状态多次发生改变 dom只更新一次
为了数据更新dom同步更新 此时nextTick
- vue2使用方法:
- Vue.nextTick().then(()=>{ 获取更新后dom })
- Vue.nextTick(()=>{ 获取更新后dom })
- this.$nextTick(()=>{})
- vue3 以后nextTick小钩子函数
- await nextTick();
- nextTick(()=>{})
ref、reactive创建响应式基础 、区别**************
ref 可以接收任意数据类型的值 并自动追踪为响应式依赖,声明得响应式依赖会被包裹在带有.value属性得ref对象中(即访问属性不能直接obj,而是obj.value才能访问得到)
reactive 只能接收引用数据类型 并自动追踪为响应式依赖
reactive和 ref(主要使用)区别?
- ref接收任意数据类型值, 具有更深层次响应式系统,嵌套数据结构更深层次,ref也会检测到变化
reactive只能接收对象类型的值(对象、数组、集合、set)- toRefs和toRef仅对reactive声明得响应式对象生效
使用ref函数创建响应式对象通过toRef和toRefs无法得到ref- 使用reactive声明响应式对象不能完整替换对象,对象中属性不能解构,都会丢失响应性
如果用reactive声明的是数组或者集合,遇到异步,可以使用.value进行赋值,模板中也使用.value来渲染就可以显示结果,(否则按同步执行异步请求还没获取到就输出了,自然什么都显示不出来)
toref、torefs 便捷访问 与 isref()、unref()类型判断
访问数据{{obj.books.name}}这样很冗余
let nameRef = toRef (obj.books, ‘name’); 然后{{nameRef}}这样就简便很多
注:仅对reactive声明得有效,ref声明得无效
- toRef是基于响应式对象的一个属性创建一个ref 并自动追踪为响应式依赖
- toRefs是基于响应式对象的多个属性创建多个ref 并自动追踪为响应式依赖(相当于多个toRef)
isRef()、unref()类型判断
isRef() 检测值是否是ref 只对于ref创建得函数返回true
unref() 参数是ref 返回内部属性 。如果不是 返回参数
模板引用
< div ref=‘box’ />
v2:this. $ refs.box
v3:通过模板引用获取dom元素, 声明和模板引用同名ref
const box = ref(null);
onMounted(()=>{ console.log(box.value) })
自定义Hook函数
自定义一个函数,其中可能用到vue3中的一些API,或者逻辑完全取决于业务
组件
局部注册
- setup语法糖中(组件不用注册,直接导入使用)
< script setup>
import Header from './pages/Header.vue'
import Footer from './pages/Footer.vue'
</ script>
- setup函数中(类似vue2里的局部注册组件)
<script>
import Header from './pages/Header.vue'
import Footer from './pages/Footer.vue'
export default {
// 使用components选项进行组件局部注册
components: { Header,Footer},
setup() { }
}
</script>
全局注册(在main.js文件中)
import './assets/main.css'
import { createApp } from 'vue'
import App from './App.vue'
import Header from './pages/Header.vue'
import Footer from './pages/Footer.vue'
// 组件全局注册 component(组件名字,组件所对应vue页面/组件配置项)
let app = createApp(App);
app.component('Header',Header);
app.component('Footer',Footer);
app.mount('#app')
组件传值
-
父给子传:
- prop
1.父组件将数据写入到子组件标签上
2.子组件接收数据 setup函数接收
2.子组件接收数据 script setup语法糖接收props:['msg','title'], setup(props){ // props--->此时props(第一个参数)就是父组件传递的数据 被包裹在对象中 // 如果解构父组件传递的数据会丢失响应性 需要使用toRef或者toRefs创建具有响应式ref const {msg,title} = props;//会丢失响应式 const {msg,title} = toRefs(props); return { props } }
使用defineProps宏接收父组件传递的数据 const props = defineProps(['msg','title']); const {msg,title} = toRefs(props);//同上响应性
- mitt
- 依赖注入
- V-model
- prop
-
子给父传:
-
emit
1.setup函数中$emit(‘自定义事件’,数据)
1.或者script setup语法糖
使用defineEmits宏声明自定义事件 得到发射自定义事件方法emit(event,args)
const emit = defineEmits([‘my-event’,‘submit’]);
emit(‘自定义事件’,数据);2.父组件在父组件中的模板中(子组件标签上)声明自定义事件
<child @自定义事件名称=‘事件处理程序’>
function handler(a,b){ } -
defineExpose
子组件使用defineExpose宏暴露数据和方法
defineExpose({ subMsg, a, increment })
父组件使用模板引用获取子组件实例中属性和方法
const child = ref(null);
onMounted(()=>{
child.value
child.value.subMsg/a increment()
})
< child ref=‘child’></ child>
-
-
兄弟传值(取消了事件总线,而是用mitt插值(父子、兄弟、祖先后代都可以传值))
- mitt
- 定义一个js文件
import mitt from ‘mitt’
const emitter = mitt();
export default emitter - 兄弟组件引入emitter对象
import emitter
emitter.emit(‘自定义事件’,传递数据) - 另一个兄弟组件引入emitter对象
import emitter
emitter.on(‘自定义事件’,()=>{ })
- 定义一个js文件
- localstorage
- 传递数据:localStorage.setItem(‘msgH’,JSON.stringify(obj))
- 获取数据:let res = localStorage.getItem(‘msgH’);
console.log(JSON.parse(res),‘获取header数据’);
- mitt
依赖注入(祖先后代传值)
let msg=123;
- 祖先组件进行数据传输:provide(‘注入名’,‘注入值’)
- provide(‘message’,msg);
- provide(‘state’,{ref,方法})
- 后代组件进行数据注入:const 同上注入名=inject([‘注入名’])
- const message = inject([‘message’]);
- const {ref,方法} = inject([‘state’]);//解构
watch侦听器
//watch(监听数据源,()=>{},{})
监听ref
watch(page, //侦听数据源
(n, o) => { console.log(n, o)}, //回调函数
{ immediate: true }); //表示不需要等待状态改变立即触发一次
监听getter函数 ()=>obj
watch(() => x.value + y.value,
(result) => { console.log(result, '监听getter函数')});
监听多个数据源租组成数组
watch([x, () => y.value],
([newX, newY]) => {console.log(`x is ${newX} and y is ${newY}`)});
- 不可以直接监听对象中得属性(v2可以) 需要使用getter函数监听
// watch(state.count,(res)=>{
// console.log(res,'无法直接监听响应式对象属性')
// });
watch(()=>state.count,(res)=>{
console.log(res,'使用getter函数形式监听响应式对象属性')
});
- 监听响应式对象默认给创建一个深层侦听器 不需要设置deep:true(v2需要)
watch(params,(res)=>{
console.log(res,'监听响应式对象变化');
})
- 监听响应式对象中的对象 getter函数也无法监听到变化 要么整个替换对象中的对象(即重新给这个对象赋其他值) 要么强制转为深层侦听器
watch(()=>obj.clazz,(res)=>{
console.log(res,'8888888888888888');
}); //出不来
setTimeout(()=>{
obj.clazz = {no:'b1002'}; // 完全替换对象中的对象
arr1.push(5);
},2000); //输出888888888
或者
watch(()=>obj.clazz,(res)=>{
console.log(res,'8888888888888888');},
{deep:true}); //使用deep:true 强制转为深层侦听器
watchEffect
自动收集依赖并进行监听 回调函数会立即执行一次 状态发生改变 回调函数继续执行
深层嵌套的 数据结构 或者 数组 或者是 直接对象 无法直接进行监听
调用watcheffect后会产生一个unwatch函数,通过手动调用实现停止侦听器
// 自动收集响应式状态并监听watchEffect( ( )=>{ } )
const unwatch = watchEffect(()=>{
console.log('count发生改变',count.value);
});
// 手动停止侦听器
unwatch();
watch和watchEffect区别?
1.watch只侦听明确数据源(写在watch数据),回调函数在响应式状态发生改变才会触发
2.watchEffect自动收集所有响应式依赖并进行监听,回调函数会立即执行一次,后续状态发生改变继续执行回函数
3.watch侦听数据源广泛,数组或者对象都可以直接监听,watchEffect无法直接监听
透传 attributes(类似继承)
父组件模板提供了class 、id 、style 、v-on这类属性会透传给子组件模板
组件上的V-model
Parent组件:
<script setup>
import Child from './Child.vue'
import {ref} from 'vue'
const searchText =ref('我是父组件数据')
</script>
<template>
<div>
//组件v-model 组件上
{{ searchText }}
Parent组件--<Child v-model="searchText"></Child>
// v-model参数 v-model:title
//Parent组件--<Child v-model:title="searchText"></Child>
</div>
</template>
Child组件:
<script setup>
import {defineProps,defineEmits} from 'vue'
defineProps(['modelValue']); //接收父组件传递的数据
defineEmits(['update:modelValue']); //发射自定义事件,名字与接收数据名一样
//defineProps(['title']);
//defineEmits(['update:title']);
</script>
<template>
<div>
<input :value="modelValue" @input="emit('update:modelValue', $event.target.value)"/>
//<input :value="title" @input="emit('update:title', $event.target.value)"/>
</div>
</template>
Pinia
组合式API
defineStore( id仓库唯一标识,()={} )
在main.js中:
import { createApp } from 'vue'
import { createPinia } from 'pinia'
import App from './App.vue'
const app = createApp(App)
app.use(createPinia())
app.mount('#app')
在状态机文件中:
import { ref, computed } from 'vue'
import { defineStore } from 'pinia'
import axios from 'axios';
// 相当于创建了useCounterStore仓库 defineStore(id仓库的唯一标识,()=>{})
export const useCounterStore = defineStore('counter', () => {
// 类似于state 相当于声明状态
const count = ref(0);
const list = ref([]);
// 类似于getters
const doubleCount = computed(() => count.value * 2)
// 同步方法 同步actions
function increment() {
count.value++
}
// 异步actions
async function getEmpData(参){
let res = await axios.get("http://121.199.0.35:7001/dashboard/queryDeviceOnlineNumber",{参});
console.log(res,'获取响应');
// 修改状态
list.value = res.data.data;
}
return { count, doubleCount, increment,list,getEmpData}
})
在组件中使用pinia
import {useCounterStore} from 'xxx';
const store = useCounterStore();
store.count 或者 store.increment 或者 store.doubleCount
直接解构状态机中state 、 getters、 actions会丢失响应性
应该用storeToRefs() 和 prop的toRefs()一样
// const {count,doubleCount,increment} = useCounterStore(); 会丢失响应式
import {storeToRefs} from 'pinia'
const {count,doubleCount} = storeToRefs(useCounterStore());