官方文档: [v3.cn.vuejs.org/guide/insta…]
一.Vue3.x的六大亮点
- Preformance:性能比Vue2.x好
- Tree shaking support:按需编译,体积比Vue2.x更小
- Composition API: 组合API(类似React Hooks)
- Better Typescript support:更好的TS支持
- Custom Renderer API:暴露了自定义渲染API
- Fragment,Teleport(Protal),Suspense:更先进的组件
二.Vue3.x是如何变快的
1.diff算法优化
- Vue2中的虚拟dom是进行全量的对比
- Vue3新增了静态标记(PatchFlag):在与上次虚拟节点进行对比的时候,只对比带有patch flag的节点,并且可以通过flag的信息得知当前节点要对比的具体内容
2.静态提升
- Vue2中无论元素是否参加更新,每次都会重新创建,然后在渲染
- Vue3中对不参与更新的元素,会做静态提升,只会被创建一次,在渲染时直接复用即可
3.事件侦听器缓存
- 默认情况下事件被视为动态绑定,所以每次都会追踪它的变化
- 但是因为是同一个函数,所以没有追踪变化,直接缓存起来复用即可
三.安装
1.通过vue-cli脚手架安装vue-three
- 对比vite更稳定一些
- 和安装vue2相同,只不过在选择的时候,选择安装3即可
// 安装vue-cli
npm install -g @vue/cli
cnpm install -g @vue/cli
yarn global add @vue/cli
vue create vue-three
cd vue-three
npm run serve
2.通过vite脚手架安装vue-three
- 对比vue-cli更轻量一些
// npm或者cnpm
npm init vite-app vue-three
cd vue-three
npm install
npm run dev
// yarn
yarn create vite-app vue-three
cd vue-three
yarn
yarn dev
四.composition API
1.set up :组合式API入口
2.reactive: 定义对象的双向绑定
setup () {
const myInform = reactive ({
age: 18, // 定义一个age属性,属性值为18
nextAge:computed (() => myInform.age + 1) // 计算属性,age的值每次加1
})
return {myInform} // 定义的值要返回出去
}
3.ref:定义js基础类型/定义对象的双向绑定
setup () {
const myName = ref('棠梨煎雪1226')
const myInform = ref ({
name: '棠梨煎雪1226',
age: 18
})
return {myName, myInform} // 定义的值要返回出去
}
4.toRefs: 解构对象
// 直接返回这个对象,那么在使用的时候,需要打点访问
<div>{{myInform.name}}</div>
<div>{{myInform.age}}</div>
setup () {
const myInform = reactive ({
name: '棠梨煎雪1226',
age: 18
})
return {myInform} // 定义的值要返回出去
}
- 使用toRefs解构对象
// 直接访问
<div>{{name}}</div>
<div>{{age}}</div>
setup () {
const myInform = reactive ({
name: '棠梨煎雪1226',
age: 18
})
return {...toRefs(myInform)}
}
5.watch: 监听
watch(source,callback,[options])
// source:指定侦听的响应式变量/String,Object,Function,Array
// callback:回调函数
// options: deep,immediate,flush
<template>
<div class="HelloWorld">
<div>{{age}}</div>
<div>{{nextAge}}</div>
</div>
</template>
<script>
import {reactive,computed,onMounted,onUnmounted,toRefs,watch} from "vue";
export default {
name: "HelloWorld",
setup() {
const { age, nextAge } = userCounter();
// 侦听器
watch (age, (newValue,oldValue) =>{
console.log('新值:' + newValue,'旧值:' + oldValue)
})
return {
age,
nextAge,
};
},
};
// 可以把方法提出来
function userCounter() {
const data = reactive({
age: 2,
nextAge: computed(() => data.age + 1),
});
let timer;
onMounted(() => {
timer = setInterval(() => {
data.age++;
}, 1000);
});
onUnmounted(() => {
clearInterval(timer);
});
return toRefs(data);
}
</script>
6.watchEffect
7.computed
五.生命周期钩子
1.beforeCreate,created,setup执行顺序以及vue3.x中的使用
vue3.x是往下兼容vue2.x的,也就是说,虽然vue3.x里面已经将beforeCreate和created变更为了set up,但使用这两个也不会报错
beforeCreate ----- set up
created ----- set up
beforeMount ----- onBeforeMount
mounted ----- onMounted
beforeUpdate ----- onBeforeUpdate
update ----- onUpdate
beforeDestory ----- onBeforeUnmount
destory ----- onUnmounted
activated ---- onActivated
deactivated ---- onDeactivated
errorCaptured ---- onErrorCaptured
vue3.x新加的两个生命周期
onRenderTracked
onRenderTriggered
六.Teleport
1.传送门组件,提供一种简介的方式,指定内容的父元素
2.例子:
- 希望在组件内部显示一个弹窗Dialog,又希望渲染的DOM结构不嵌套在组件的DOM中
- 父组件
<template>
<!-- 模态框 -->
<Dialog></Dialog>
</template>
<script>
import Dialog from "./Dialog.vue";
export default {
name: "HelloWorld",
components: {Dialog},
setup() {
}
};
</script>
- 子组件
<template>
<button class="open" @click="modelShow = true">点击打开模态框</button>
<!-- 用teleport包裹弹窗的内容,to属性指向id属性 -->
<teleport to="#app">
<div v-if="modelShow" class="content">
内容
<button class="open" @click="modelShow = false">关闭打开模态框</button>
</div>
</teleport>
</template>
<script>
import { ref } from "vue";
export default {
name: "dialog_box",
setup() {
const modelShow = ref(false);
return { modelShow };
},
};
</script>
<style scoped>
.content {
position: absolute;
width: 100%;
height: 100%;
background: pink;
top: 30%;
width: 200px;
height: 200px;
left: 40%;
}
</style>
- 效果图
- content和app同级
七.Fragment
1.在vue2.x中,只允许有一个根节点
2.在vue3.x中,允许有多个根节点
八.Emits Component Option
1.vue3.x中组件发送自定义事件需要定义在emits选项中
2.例子:
- 父组件
<template>
<div class="helloWorld">
<!-- 子传父的自定义事件 -->
<Emits @click="emitClick"></Emits>
</div>
</template>
<script>
import Emits from "./Emits.vue";
export default {
name: "HelloWorld",
components: {Emits},
methods: {
emitClick() {
console.log('我被点击了')
}
}
};
</script>
<style scoped>
.helloWorld {
width: 100%;
/* height: 100%; */
}
</style>
- 子组件
<template>
<div @click="$emit('click')">点击我!!!</div>
</template>
<script>
export default {
}
</script>
<style lang="scss" scoped>
</style>
- 效果
- 点击一次,输出两次内容,这里是一个大坑!!!!!!
- 解决
<template>
<div @click="$emit('click')">点击我!!!</div>
</template>
<script>
export default {
emits: ['click'] // 可以更好地解决重复输出的问题
}
</script>
<style lang="scss" scoped>
</style>
3.上述例子,如果不使用emits: ['click']的另一种写法
- 父组件
<template>
<div class="helloWorld">
<!-- 子传父的自定义事件 -->
<Emits @my-click="emitClick"></Emits>
</div>
</template>
<script>
import Emits from "./Emits.vue";
export default {
name: "HelloWorld",
components: {Emits},
methods: {
emitClick() {
console.log('我被点击了')
}
}
};
</script>
<style scoped>
.helloWorld {
width: 100%;
/* height: 100%; */
}
</style>
- 子组件
<template>
//传值的时候,不要直接传递click即可,就不需要在使用emits: ['click']
<div @click="$emit('my-click')">点击我!!!</div>
</template>
九.摇树优化
1.例子:nextTick
// vue2.x
// Vue构造函数直接接触
import Vue from 'vue'
Vue.nextTick(()=>{})
// vue3.x
// 提取独立函数,打包可以看出是否被调用
import {nextTick} from 'vue'
nextTick(()=>{})
十.V-model
1.vue2.x的v-model原理
- 核心是通过object.defineProperty进行get,set拦截
- :value + @input
- 参考链接:https://blog.csdn.net/Dobility/article/details/110147985
2.vue3.x的v-model原理
- 父组件
<template>
<div class="helloWorld">
<!-- v-model数据的双向绑定 -->
<Mymodel v-model:myCounter="myCounter"></Mymodel>
</div>
</template>
<script>
import { ref } from 'vue'
import Mymodel from "./Mymodel.vue";
export default {
name: "HelloWorld",
components: {Mymodel},
setup() {
const myCounter = ref(0)
return {myCounter}
}
};
</script>
<style scoped>
.helloWorld {
width: 100%;
/* height: 100%; */
}
</style>
- 子组件
<template>
<div @click="$emit('update:myCounter',myCounter + 1)">
myCounter:{{myCounter}}
</div>
</template>
<script>
export default {
props: {
myCounter: {
type: Number,
default: 1
}
}
}
</script>