为什么要写vue3
Vue代码库随着时间的推移而暴露出来的设计和体系架构问题
生命周期的变化
vue 2 vue3
BeforeMount onBeforeMount mounted onMounted beforeUpdate onBeforeUpdate updated onUpdated beforeDestroy onBeforeUnmount destroy onUnmounted errorCaptured onErrorCaptured
vue3的亮点
性能优化
1、diff算法优化
vue2中的虚拟DOM是进行全量对比的,数据有没有更新都会进行比较
vue3新增了静态标记(PatchFlag),只会比较带有静态标记的节点,并通过静态标记可以得出要比较的具体内容,固定不变的节点不会带有静态标记
<p>{{message}}<p> //比较的是动态文本
<p :class="{ active: isActive }">我是一只小毛驴</p> // 比较的是动态class
2、hoistStatic(静态提升)
vue2中无论元素是否参与更新,每次都会重新创建,然后渲染
vue3中对于不参与更新的元素,会做静态提升,只会被创建一次,在渲染时直接复用。
3、cacheHandlers(事件侦听器缓存)
默认情况下,事件均会被动态绑定带有静态标记,所以每次都会去追踪他的变化,但函数还是那个函数,不会变,所以不需要追踪变化,直接缓存起来复用即可
composition Api
组合Api又可称为注入Api,等效于组合Api中的数据可以注入到vue2中的data函数中,组合Api中的方法注入到vue2中的methods中。vue2中数据与业务逻辑分离,不便于阅读和后期维护,组合Api允许用户像编写函数一样自由地表达、组合和重用有状态的组件逻辑
typescript支持
Typescript强类型语言,支持类型声明,可进行类型检查,编写稳定性高的代码,有利于后期维护和代码的阅读
使用proxy代替defineProperty
1、vue2版本双向数据绑定是采用数据劫持结合发布者-订阅者模式的方式,通过Object.defineProperty()来劫持各个属性的setter,getter,在数据变动时发布消息给订阅者,触发相应的监听回调。(defineProperty只能绑定首次渲染时候的属性,后加的属性是不会被绑定上,也就不会触发更新渲染)
2、Vue3是用ES6的语法 Proxy对象来实现的(Proxy绑定的是整体,不关心里面有什么属性,而且Proxy的配置项有13种,可以做更细致的事情)
Proxy优势:
(1)defineProperty只能监听某个属性,不能对全对象监听
(2)可以省去for in、闭包等内容来提升效率(直接绑定整个对象即可)
(3)可以监听数组,不用再去单独的对数组做特异性操作
(4) vue3.x可以检测到数组内部数据的变化
(5)defineProperty无法兼容IE8,所以vue2只能兼容到IE8,其他浏览器也会存在轻微兼容问题;proxy除了IE,其他浏览器都兼容
按需编译,打包体积小
vue3快速上手
安装
1、安装vite
vite是一个类似于webpack的打包工具,他的实现原理是利用ES6中的import会发送请求去加载文件的特性,拦截这些请求,做一些预编译,省去了webpack冗长的打包时间
npm install -g create-vite-app
2、利用vite创建vue3项目
create-vite-app projectName
3、安装依赖运行项目
cd projectName
npm install
npm run serve
vue2实现一个todolist
<template> <div class="toDoList"> <label>id:</label><input v-model="student.id" /> <label>姓名:</label><input v-model="student.name" /> <label>年龄:</label><input v-model="student.age" /> <button @click="add">提交</button> <p v-for="(item, index) in studentLists" :key="item.id" @click="sub(index)"> {{ item.name }}--{{ item.age }} </p> </div></template><script>export default { data() { return { studentLists: [ { id: 1, name: "张三", age: "20" }, { id: 2, name: "李四", age: "30" }, { id: 3, name: "王五", age: "40" }, ], student: { id: null, name: "", age: "", }, }; }, methods: { add() { this.studentLists.push(this.student); this.student = {}; }, sub(index) { this.studentLists = this.studentLists.filter((item, index2) => { return index2 != index; }); }, },};</script>
vue3实现一个todolist
1、小试牛刀
<template> <div class="toDoList"> <span @click="add">{{ count }}</span> </div></template><script>import { ref } from "vue";export default { // setup函数是组合Api的入口函数 setup() { //定义一个count变量,初始值为0,这个变量变化时,自动更新ui 等同于 let count = 0 // ref只能监听简单数据类型的变化,对复杂数据类型无用 const count = ref(0); // 在组合Api中,可以直接定义方法 function add() { count.value++; console.log(count); } // 在组合Api中定义的变量、方法,要想在外界使用,必须return出去 return { count, add }; },};</script>
2、实现todolist(常规版)
<template> <div class="toDoList"> <label>id:</label><input v-model="state.student.id" /> <label>姓名:</label><input v-model="state.student.name" /> <label>年龄:</label><input v-model="state.student.age" /> <button @click="add">提交</button> <p v-for="(item, index) in state.studentLists" :key="item.id" @click="sub(index)" > {{ item.name }}--{{ item.age }} </p> </div></template><script>import { reactive } from "vue";export default { // setup函数是组合Api的入口函数 setup() { // reactive可以监听复杂数据类型 const state = reactive({ studentLists: [ { id: 1, name: "张三", age: "20" }, { id: 2, name: "李四", age: "30" }, { id: 3, name: "王五", age: "40" }, ], student: { id: null, name: "", age: "", }, }); // 在组合Api中,可以直接定义方法,也可以写在setup函数外 function add() { state.studentLists.push(state.student); state.student = {}; } function sub(index) { state.studentLists = state.studentLists.filter((item, index2) => { return index2 != index; }); } // 在组合Api中定义的变量、方法,要想在外界使用,必须return出去 return { state, add, sub }; },};</script>
3、实现todolist(封装版)
<template> <div class="toDoList"> <label>id:</label><input v-model="state2.student.id" /> <label>姓名:</label><input v-model="state2.student.name" /> <label>年龄:</label><input v-model="state2.student.age" /> <button @click="add">提交</button> <p v-for="(item, index) in state.studentLists" :key="item.id" @click="sub(index)" > {{ item.name }}--{{ item.age }} </p> </div></template><script>import { reactive } from "vue";function subStudent() { const state = reactive({ studentLists: [ { id: 1, name: "张三", age: "20" }, { id: 2, name: "李四", age: "30" }, { id: 3, name: "王五", age: "40" }, ], }); function sub(index) { state.studentLists = state.studentLists.filter((item, index2) => { return index2 != index; }); } // 必须return return { state, sub };}
function addStudent(state) { const state2 = reactive({ student: { id: null, name: "", age: "", }, }); function add() { state.studentLists.push(state2.student); state2.student = {}; } // 必须return return { state2, add };}
export default { setup() { const { state, sub } = subStudent(); const { state2, add } = addStudent(state); return { state, sub, state2, add }; },};</script>
4、实现todolist(引入版)
//student.js
import { reactive } from "vue";function subStudent() { const state = reactive({ studentLists: [ { id: 1, name: "张三", age: "20" }, { id: 2, name: "李四", age: "30" }, { id: 3, name: "王五", age: "40" }, ], }); function sub(index) { state.studentLists = state.studentLists.filter((item, index2) => { return index2 != index; }); } // 必须return return { state, sub };}function addStudent(state) { const state2 = reactive({ student: { id: null, name: "", age: "", }, }); function add() { state.studentLists.push(state2.student); state2.student = {}; } // 必须return return { state2, add };}export { subStudent, addStudent };
//student.html
<template> <div class="toDoList"> <label>id:</label><input v-model="state2.student.id" /> <label>姓名:</label><input v-model="state2.student.name" /> <label>年龄:</label><input v-model="state2.student.age" /> <button @click="add">提交</button> <p v-for="(item, index) in state.studentLists" :key="item.id" @click="sub(index)" > {{ item.name }}--{{ item.age }} </p> </div></template><script>import { subStudent, addStudent } from "./student";export default { setup() { const { state, sub } = subStudent(); const { state2, add } = addStudent(state); return { state, sub, state2, add }; },};</script>
注意点
setup
(1)setup函数是在beforeCreated之前执行的
(2)在setup函数中无法使用data和methods
(3)在setup函数中使用this,this是undefined
(4)setup函数只能是同步,不能是异步,如果需要异步,要在setup前加async
reactive
(1)reactive函数的参数必须是对象(json/arr)
(2)reactive函数的参数如果不是对象,数据将不具有响应式
(3)reactive函数的参数如果是其他对象(比如 new Date()),直接在原有数据的基础上进行修改,页面不会自动更新;如果想更新,可以通过直接赋值的方式
(3)reactive函数的本质就是将数据包装成一个proxy对象
//直接设置无效
setup(){
const state = reactive({
time = new Date()
);
function addTime(){
state.time.setDate(state.time.getDate() + 1)
}
}
//重新赋值有效
setup(){
const state = reactive({
time = new Date()
);
function addTime(){
const newTime = new Date(state.time.getTime());
nweTime.setDate(state.time.getDate() + 1);
state.time = newTime;
}
}
ref
(1)ref的本质还是reactive,系统会自动将const count = ref(0)转为 const count = reactive({ value : 0 }),只是ref实现对简单类型的监听
(2)在html中使用ref的值(count)可以直接使用count,在js中必须使用count.value才行
总结
waiting for update......