Vue3.0基础总结
心无杂念,行路也将势如破竹。
2 年开发,99 位贡献者,2600 次提交,628 次 pr,打包体积减小 41%,初次渲染快 55%,更新快 133%,内存使用减少 54%
Vue2.x 的不足
-
全局 API 容易污染全局环境
-
Mixin 缺点:命名冲突、不清楚变量来源、重用到组件的问题
-
响应式:
-
不允许动态添加根级别的响应式属性
-
对象:不能检测对象属性的添加和移除,可以使用 Vue.set(object,propertyName,value)
-
数组:1、不能利用索引直接设置一个数组项、2、不能修改数组的长度
// 可以通过Vue.set和splice()来解决 Vue.set(vm.items, indexOfItem, newValue); // Array.prototype.splice vm.items.splice(indexOfItem, 1, newValue); vm.$set(vm.items, indexOfItem, newValue); vm.items.splice(newLength); // 响应式原理 Object.defineProperty(data, "count", { get() {}, set() {}, }); new Proxy(data, { get(key) {}, set(key, value) {}, });
-
Vue3 常用 API
-
setup():在组件被创建之前,props 被解析之后执行。它是组合式 API 的入口。
-
执行 setup 时,组件实例尚未被创建。因此,你只能访问以下 property:props、attrs、slots、emit,无法访问以下组件选项:data、computed、methods、refs (模板 ref)
-
若要对传递给 setup() 的参数进行类型推断,你需要使用 defineComponent。
import axios from "axios"; import { defineComponent } from "vue"; export default defineComponent({ async setup() { const res = await axios.get("url"); return { data: res.data, }; }, });
-
参数:
-
props:因为 props 是响应式的,你不能使用 ES6 解构,它会消除 prop 的响应性。如果需要解构 prop,可以在 setup 函数中使用 toRefs
import { toRefs } from 'vue' setup(props) { const { title } = toRefs(props) console.log(title.value) } //如果 title 是可选的 prop,props 中可能没有 title 。toRefs 将不会为 title 创建一个 ref 。需要用 toRef 替代它 import { toRef } from 'vue' setup(props) { const title = toRef(props, 'title') console.log(title.value) }
-
-
context:是一个普通的 JavaScript 对象,不是响应式的,可以安全地使用 ES6 解构 attrs, slots, emit, expose
<!-- 获取自定义事件 --> <modal isOpen="ifOpen" @closeModel="closeIt">弹层</modal>
import { defineComponent } from "vue"; export default defineComponent({ setup(props, context) { const buttonClick = () => { // emit派发事件名称 context.emit("closeModel"); }; return { buttonClick, }; // context.attrs是None-Props属性 // context.slots相当于vue2.x中的this.$slots // 通过h渲染,将插槽里的值渲染到div元素里 // return ()=>h('div',{},context.slots.default()) }, });
-
组合式 API 生命周期钩子
- beforeCreate -> 使用 setup()
- created -> 使用 setup()
- beforeMount -> onBeforeMount
- mounted -> onMounted
- beforeUpdate -> onBeforeUpdate
- updated -> onUpdated
- beforeUnmount -> onBeforeUnmount
- unmounted -> onUnmounted
- renderTracked -> onRenderTracked // 每次渲染后重新收集响应式依赖
- renderTriggered -> onRenderTriggered // 每次触发页面重新渲染是自动执行
- activated -> onActivated
- deactivated -> onDeactivated
- errorCaptured -> onErrorCaptured
setup(){ const error = ref(null); errorCaptured(e){ error.value = e return true } }
-
-
ref、reactive、toRefs、toRef 响应式的引用
-
ref:将基本数据类型的值变成响应式的值。ref 对象具有指向内部值的单个 property.value。
const count = ref(0); // proxy({value:0}) console.log(count.value); // 0 count.value++; console.log(count.value); // 1
-
reactive:返回对象的响应式副本
setup(){ const data = reactive({ count: 0, increase: () => data.count++, double: computed(() => data.count * 2), }); // toRefs将proxy({count:0,...})=>{count:proxy({value,0}),...}转换成可以解构的响应式 const refData = toRefs(data); // reactive通过proxy将{name:'bj'}=> proxy({name:'bj'})响应式 const obj = reactive({name:'bj'}); // readOnlyObj只能读取不能修改,可以传给子组件,子组件不能修改父组件,单向数据流 const readOnlyObj = readonly(obj) return { ...refData, obj }; }
-
toRefs:是解构的响应式对象仍具备响应式
将响应式对象转换为普通对象,其中结果对象的每个 property 都是指向原始对象相应 property 的 ref。可以在不丢失响应性的情况下对返回的对象进行解构/展开
-
toRef:对象可选属性的响应式需要用 toRef,如果没有这个属性,会为对象加上这个属性,属性值为空,如果没有这个属性 toRefs 会报错
setup(){ // 如果data中没有age熟悉,就会添加一个默认的data.age.value='' const age = toRef(data, "age"); setTimeout(() => { age.value = 18; }, 500); return { age }; }
-
-
computed:计算属性
const count = ref(1); const plusOne = computed(() => count.value + 1); console.log(plusOne.value); // 2 plusOne.value++; // 错误 // 接受一个具有 get 和 set 函数的对象,用来创建可写的 ref 对象 const count = ref(1); const plusOne = computed({ get: () => count.value + 1, set: (val) => { count.value = val - 1; }, }); plusOne.value = 1; console.log(count.value); // 0
-
watch、watchEffect
[Vue warn]:Invalid watch source: watch source can only be a getter/effect function, a ref, a reactive object, or an array of these type
- watch
- 具有一定的惰性 lazy 第一次不执行
- 参数可以拿到原始值和当前值
- 可以监听多个数据的变化
watch([count, () => data.name], (newVal, oldVal) => { console.log(newVal, oldVal); });
- watchEffect
- 立即执行,没有惰性 immediate
- 不需要传监听内容,自动感知代码依赖,只有一个回调函数,依赖代码改变触发
- 不能获取之前数据的值
const stop = watchEffect(() => { console.log(obj.name); setTimeout(() => { stop(); // 取消watchEffect监听 }, 500); });
- watch
-
Suspense 异步组件
<Suspense> <template #default> <async-show> </template> <template #fallback> <p>Loading...</p> </template> </Suspense>
-
自定义函数 hooks
// 获取鼠标位置 import { ref, onMounted, onUnmounted } from "vue"; const position = () => { const x = ref(0); const y = ref(0); const getPosition = (e) => { x.value = e.pageX; y.value = e.pageY; }; onMounted(() => { document.addEventListener("click", getPosition); }); onUnmounted(() => { document.removeEventListener("click", getPosition); }); }; export default position;
// loading import { ref } from "vue"; import axios from "axios"; function reqLoading(url) { const res = ref(null); const loading = ref(true); const noLoading = ref(false); const error = ref(error); axios .get(url) .then((res) => { loading.value = false; noLoading.value = true; res.value = res.data; }) .catch((e) => { loading.value = false; error.value = e.message; }); return { res, loading, noLoading, error, }; } export default reqLoading;
-
provide 和 inject
- inject 函数有两个参数:
- inject 的 property 的 name
- 默认值 (可选)
// 父组件 setup() { const location = ref('North Pole') const geolocation = reactive({ longitude: 90, latitude: 135 }) const updateLocation = () => { location.value = 'South Pole' } // readonly包裹,单项数据流原则,子组件只能通过父组件的方法来修改 provide('location', readonly(location)) provide('geolocation', readonly(geolocation)) provide('updateLocation', updateLocation) } // 子组件 setup() { const userLocation = inject('location', 'The Universe') const userGeolocation = inject('geolocation') const updateUserLocation = inject('updateLocation') return { userLocation, userGeolocation, updateUserLocation } }
- inject 函数有两个参数:
Demo:todoList
// 列表的属性和方法抽离
const listControl = () => {
const { reactice } = Vue;
const list = reactive([]);
const addItem = (item) => {
list.push(item);
};
return { list, addItem };
};
// 提交的属性和方法抽离
const submit = () => {
const { ref } = Vue;
const inputValue = ref(null);
const inputChange = (e) => {
inputValue.value = e.target.value;
};
return { inputValue, inputChange };
};
const app = Vue.createApp({
// 调度获取需要返回的data
setup() {
const { list, addItem } = listControl();
const { inputValue, inputChange } = submit();
return {
list,
addItem,
inputValue,
inputChange,
};
},
template: `
<div>
<div>
<input :value="inputValue" @input="inputChange"/>
<buttom @click="()=>addItem(inputValue)">提交</buttom>
</div>
<ul>
<li v-for = "(item,idx) in list" :key="idx">{{item}}</li>
</ul>
</div>
`,
});