[TOC]
Vue 与 React 框架的对比
- Vue:使用响应式,数据初始化时,Watcher 监听数据的每个属性,数据修改后可以做针对性修改相应的 DOM。但每个数据都存在一个 watcher 会影响性能。所以引入虚拟 DOM 来解决响应式数据过多的问题。
对于 Vue 2 来说,组件之间的变化,可以通过响应式来通知更新。组件内部的数据变化,则通过虚拟 DOM 去更新页面。这样就把响应式的监听器,控制在了组件级别,而虚拟 DOM 的量级,也控制在了组件的大小。
- React:使用虚拟 DOM 和 Diff 计算逻辑,虚拟 DOM 树过大时,计算时间会较长。为解决性能问题,引入 Fiber 架构,将虚拟 DOM 树微观化,变成链表,使用浏览器的空闲时间计算 Diff。
把树形结构改造成了链表,遍历严格地按照子元素 -> 兄弟元素 -> 父元素的逻辑,随时可以中断和恢复 Diff 的计算过程。
Vue3 新特性
响应式系统
原本使用的 defineProperty 是拦截具体的某个属性,对于不存在的属性无法拦截(涉及数组长度修改等情况)。
proxy 是真正的代理,可以拦截到所有的属性。
自定义渲染器
原本的 Vue2 内部所有模块糅合在一起,为了解决扩展性问题,Vue3 使用 monorepo 来管理(拆包),将响应式、编译和运行时独立出来,渲染的逻辑也拆成平台无关渲染逻辑和浏览器渲染 API 两部分。
使用 TS 重构
Composition API 组合语法
<div id="app">
<h1 @click="add">{{state.count}} * 2 = {{double}}</h1>
</div>
<script src="https://unpkg.com/vue@next"></script>
<script>
const {reactive,computed} = Vue
let App = {
setup(){
const state = reactive({
count:1
})
function add(){
state.count++
}
const double = computed(()=>state.count*2)
return {state,add,double}
}
}
Vue.createApp(App).mount('#app')
</script>
- 代码复用:使 methods、data 可以封装到一个单独的函数中
所有 API 使用 import 引入,对于 Tree-shaking 友好。Vue2 中所有数据都挂载在 this 之上
工程化工具 Vite
Webpack:根据 import 依赖逻辑形成依赖图,调用对应的处理工具,将整个项目打包后,放在内存里再启动调试(存在预打包耗时问题)
- Vite:基于现代浏览器默认支持 ES6 的 import 语法,调试环境下,无需预打包,直接通过网络请求去获取依赖文件,做到复杂项目的秒级调试和热更新
Composition API +
function useTodos() {
let title = ref("");
let todos = ref([{ title: "学习Vue", done: false }]);
function addTodo() {
todos.value.push({
title: title.value,
done: false,
});
title.value = "";
}
function clear() {
todos.value = todos.value.filter((v) => !v.done);
}
let active = computed(() => {
return todos.value.filter((v) => !v.done).length;
});
let all = computed(() => todos.value.length);
let allDone = computed({
get: function () {
return active.value === 0;
},
set: function (value) {
todos.value.forEach((todo) => {
todo.done = value;
});
},
});
return { title, todos, addTodo, clear, active, all, allDone };
}
<script setup>
import { ref, computed } from "vue";
let count = ref(1)
function add(){
count.value++
}
let { title, todos, addTodo, clear, active, all, allDone } = useTodos();
</script>
Vue3 的响应式
let getDouble = n=>n*2
let obj = {}
let count = 1
let double = getDouble(count)
Object.defineProperty(obj,'count',{
get(){
return count
},
set(val){
count = val
double = getDouble(val)
}
})
console.log(double) // 打印2
obj.count = 2
console.log(double) // 打印4 有种自动变化的感觉
let proxy = new Proxy(obj,{
get : function (target,prop) {
return target[prop]
},
set : function (target,prop,value) {
target[prop] = value;
if(prop==='count'){
double = getDouble(value)
}
},
deleteProperty(target,prop){
delete target[prop]
if(prop==='count'){
double = NaN
}
}
})
console.log(obj.count,double)
proxy.count = 2
console.log(obj.count,double)
delete proxy.count
// 删除属性后,我们打印log时,输出的结果就会是 undefined NaN
console.log(obj.count,double)
let getDouble = n => n * 2
let _value = 1
double = getDouble(_value)
let count = {
get value() {
return _value
},
set value(val) {
_value = val
double = getDouble(_value)
}
}
console.log(count.value,double)
count.value = 2
console.log(count.value,double)
迷你 Vuex
- 使用 provide/inject 来做数据共享
state 使用 reactive 来实现响应式
import { inject, reactive } from 'vue' const STORE_KEY = '__store__' function useStore() { return inject(STORE_KEY) } function createStore(options) { return new Store(options) } class Store { constructor(options) { this.$options = options this._state = reactive({ data: options.state }) this._mutations = options.mutations } get state() { return this._state.data } commit = (type, payload) => { const entry = this._mutations[type] entry && entry(this.state, payload) } // main.js入口处app.use(store)的时候,会执行这个函数 install(app) { app.provide(STORE_KEY, this) } } export { createStore, useStore }