vue3新特性
- 更快的渲染:Vue 3使用了新的响应式系统,将Proxy作为其核心,以取代Vue 2.x中使用的Object.defineProperty。这样可以更快地追踪变化并更新视图。
- 更小的包体积:Vue 3改进了其打包工具,可以生成更小、更高效的代码。同时,移除了一些不常用的API,减少了库的大小。
- 更好的Tree-Shaking支持:Vue 3通过使用ES模块来实现更好的Tree-Shaking支持,从而减小了最终打包文件的大小。
// 只导入用到的,不用的不打包进去,减少包的大小
import { xx } from 'vue'
- 静态提升:Vue 3采用了基于编译时的静态提升技术,将组件模板编译为渲染函数,并通过hoist静态节点,以减少不必要的重复渲染和创建。
- 更好的TypeScript支持:Vue 3使用TypeScript重新编写了核心代码,提供了更好的类型支持和开发体验。
- 更好的代码结构和组织:Vue 3重构了其内部代码结构,以更好地组织和管理代码。
这些改进和优化使Vue 3比Vue 2.x更快、更小、更易于开发和维护
var data = { name:'修改前的数据' }
// VUE 框架需要知道属性的改变 + 订阅发布模式 => 属性变更 更新对应DOM
// Vue2
Object.defineProperty(data,'name'{
get(){
console.log('获得数据');
return '测试'
}
set(newValue){
console.log('设置数据')
}
})
// 设置
data.name = '修改后的数据'
// 获取
console.log(data.name)
// 新增属性
data.abc = '123' // 没有触发
Vue.set(data , 'abv','123') this.$set
// 删除属性
delete data.abc
// 数组,key是数字,不能呗框架查询,push splice也无法察觉
// vue2是重写了数组8个方法,slice pop unshift shift等,让框架知道数据改变了
// 性能也比proxy低
// vue3
let p = new Proxy(data,{
set(target,proxyKey,value){
console.log('增加和修改',target,proxyKey,receiver)
},
get(target,proxyKey,receiver){
console.log('获取',target,proxyKey,receiver)
},
deleteProperty(target,proxyKey,receiver){
console.log('删除',target,proxyKey,receiver)
}
})
//代理:装饰目标,最终操作代理
// 增加
p.abc = 'xxx'
// 修改
p.abc = 'yyy'
// 获取
console.log(p.abc)
// 删除
delete p.abc
// vue3
let p = new Proxy(data,{
set(target,proxyKey,value){
console.log('增加和修改',target,proxyKey,value);
return Reflect.set();
},
get(target,proxyKey,value){
console.log('获取',target,proxyKey,value)
},
deleteProperty(target,proxyKey,value){
console.log('删除',target,proxyKey,value)
}
})
//代理:装饰目标,最终操作代理
// 增加
p.abc = 'xxx'
// 修改
p.abc = 'yyy'
// 获取
console.log(p.abc)
// 删除
delete p.abc
VUE3 新增13种拦截方法
- get(target, propKey, receiver):拦截对象属性的读取,比如proxy.foo和proxy['foo']。
- set(target, propKey, value, receiver):拦截对象属性的设置,比如proxy.foo = v或proxy['foo'] = v,返回一个布尔值。
- has(target, propKey):拦截propKey in proxy的操作,返回一个布尔值。
- deleteProperty(target, propKey):拦截delete proxy[propKey]的操作,返回一个布尔值。
- ownKeys(target):拦截Object.getOwnPropertyNames(proxy)、Object.getOwnPropertySymbols(proxy)、Object.keys(proxy)、for...in循环,返回一个数组。该方法返回目标对象所有自身的属性的属性名,而Object.keys()的返回结果仅包括目标对象自身的可遍历属性。
- getOwnPropertyDescriptor(target, propKey):拦截Object.getOwnPropertyDescriptor(proxy, propKey),返回属性的描述对象。
- defineProperty(target, propKey, propDesc):拦截Object.defineProperty(proxy, propKey, propDesc)、Object.defineProperties(proxy, propDescs),返回一个布尔值。
- preventExtensions(target):拦截Object.preventExtensions(proxy),返回一个布尔值。
- getPrototypeOf(target):拦截Object.getPrototypeOf(proxy),返回一个对象。
- isExtensible(target):拦截Object.isExtensible(proxy),返回一个布尔值。
- setPrototypeOf(target, proto):拦截Object.setPrototypeOf(proxy, proto),返回一个布尔值。如果目标对象是函数,那么还有两种额外操作可以拦截。
- apply(target, object, args):拦截Proxy 实例作为函数调用的操作,比如proxy(...args)、proxy.call(object, ...args)、proxy.apply(...)。
- construct(target, args):拦截 Proxy 实例作为构造函数调用的操作,比如new proxy(...args)。
VUE3项目的创建
npm install -g @vue/cli
// 查看版本,要求vue-cli版本在4.5以上,可以创建vue3项目
vue -V
vue create 项目名称
根据自己需求,简单配置
启动项目
进入目录下: yarn serve / npm run serve
关于 vue.config.js 配置
Vue vue.config.js 的详解与配置_蓝胖子的多啦A梦的博客-CSDN博客
createApp
在Vue 3中,改变全局Vue行为的API现在被移动到了由新的createApp方法所创建的应用实例上。vue 3. 0中使用createApp来创建vue实例
import { createApp } from 'vue'
import App from './App.vue'
import router from './router'
// const app = createApp(App) ; app.use(router); app.mount('#app');
// 合并之后
createApp(App).use(router).mount('#app')
setup函数
setup函数是vue 3中专门为组件提供的新属性。
创建组件实例,然后初始化props,紧接着就调用setup函数,会在beforeCreate钩子之前被调用。
模板中使用:如果setup返回一个对象,则对象的属性将会被合并到组件模板的渲染上下文。
<script>
// @ is an alias to /src
import HelloWorld from '@/components/HelloWorld.vue'
import { defineComponent } from 'vue';
export default defineComponent({
name: 'Home',
components: {
HelloWorld
},
setup(props,context){
console.log(context); // 类似VUE2版本中组件内的this
return {
num:999
}
}
})
</script>
注意:在setup()函数中无法访问到this,但可以使用context
reactive函数
reactive()函数接收一个普通对象,返回一个响应式的数据对象
ref的使用
ref函数用来根据给定的基本数据类型创建一个响应式的数据对象,ref()函数调用的返回值是一个对象,这个对象上只包含一个value属性
function ref<T>(value: T):Ref<UnwrapRef<T>>
interface Ref<T>
{
value:T
}
reactive的用法与ref的用法相似,也是将数据变成响应式数据,当数据发生变化时UI也会自动更新。不同的是ref用于基本数据类型,而reactive是用于复杂数据类型
// ref 与 reactive 的比较
<template>
<div class="home">
{{ data.name }}
{{ num }}
</div>
</template>
<script>
import { defineComponent, reactive, ref } from 'vue';
export default defineComponent({
name: 'Home',
setup(props) {
//基本数据类型响应式
let num = ref(100);
// 引用数据类型响应式 new Proxy(对象,{set(target,propKey){}})
const data = reactive({
name: '小太阳'
})
return {
data,
num
}
}
})
</script>
toRefs
【解构部分属性专用】的使用
将一个响应式对象转换为一个普通对象,这个普通对象的每个属性都是指向源对象相应属性的ref。每个单独的ref都是使用toRef()创建的。
<template>
<div class="home">
{{ name }}
</div>
</template>
<script>
import { defineComponent, reactive, ref ,toRefs} from 'vue';
export default defineComponent({
name: 'Home',
setup(props) {
const dataRef = reactive({
name: '小太阳Ref'
})
// 取出属性,并且响应式
const { name } = toRefs(dataRef);
return {
name
}
}
})
</script>
computed计算属性
// 只读
function computed<T>(
getter:() => T,
debuggerOptions?:debuggerOptions
):Readonly<Ref<Readonly<T>>>
// 可写的
function computed<T>(
options:{
get:() =>T
set:(value:T) => void
},
debuggerOptions?:DebuggerOptions
):Ref<T>
简单语法:
<template>
<div class="home">
{{ all }}
</div>
</template>
<script>
import { defineComponent, reactive, computed} from 'vue';
export default defineComponent({
name: 'Home',
setup(props) {
let state = reactive({
id:10
})
let all = computed(()=>{
return state.id+1;
})
return {
all,
}
}
})
</script>
watch的使用
watch()函数用来监视某些数据项的变化,从而触发某些特定的操作
// 侦听多个来源
function watch<T>(
sources: WatchSource<T>[],
callback: WatchCallback<T[]>,
options?: WatchOptions
): StopHandle
// 侦听单个来源
function watch<T>(
source: WatchSource<T>,
callback: WatchCallback<T>,
options?: WatchOptions
): StopHandle
type WatchCallback<T> = (
value: T,
oldValue: T,
onCleanup: (cleanupFn: () => void) => void
) => void
type WatchSource<T> =
| Ref<T> // ref
| (() => T) // getter
| T extends object
? T
: never // 响应式对象
interface WatchOptions extends WatchEffectOptions {
immediate?: boolean
// 默认:false 在侦听器创建时立即触发回调。第一次调用时旧值undefined
deep?: boolean
// 默认:false 如果源是对象,强制深度遍历,以便在深层级变更时触发回调
flush?: 'pre' | 'post' | 'sync'
// 默认:'pre' 调整回调函数的刷新时机
// pre:组件渲染前
// post:侦听器延迟到组件渲染后
// sync:响应依赖改变时触发
onTrack?: (event: DebuggerEvent) => void
// 调试侦听器的依赖
onTrigger?: (event: DebuggerEvent) => void
}
简单语法
const count = ref(0)
watch(count, (count, prevCount) =>{
/* . . . */
})
举个例子:奇偶数判断
<template>
<div class="home">
{{ id +'-'+type }}
<button @click="change()">click</button>
</div>
</template>
<script>
import { defineComponent, reactive, toRefs, watch } from 'vue';
export default defineComponent({
name: 'Home',
setup(props) {
let state = reactive({
id: 10,
type:'偶数'
})
let stop = watch(()=>state.id,(cur,old) => {
if(cur % 2 == 0){
state.type = '偶数'
}else{
state.type = '奇数'
}
})
function change(){
state.id +=1;
}
return {
...toRefs(state),
change
}
}
})
</script>
provide和inject
父组件把值传给所有子组件,谁用谁拿
import { provide,inject } from 'vue';
//父组件
const state = reactive({ k: 1 })
const numState = ref(0);
provide('k1' , state);
// 整个state传递
provide('k1' , numState);
//子组件
let val = inject('k1');
let numState = inject('k2');
console.log(numState.value)
获取slots\attrs\emit
- useAttrs 方法 获取 attrs 属性
- useSlots 方法获取 slots 插槽
- defineEmits 方法获取 emit自定义事件
import { useSlots,useAttrs } from 'vue'
const { attrs, slots, emit } = context
// attrs 获取组件传递过来的属性值,
// slots 组件内的插槽
// emit 自定义事件 子组件
const slots = useSlots()
// 插槽
const attrs = useAttrs()
// 非props属性
const emits = defineEmits(['getChild']);
生命周期
新版的生命周期函数,可以按需导入到组件中,且只能在setup()函数中使用
import { defineComponent, onMounted, onUnmounted, onUpdated } from 'vue';
export default defineComponent({
setup(props) {
onMounted(() => {
console.log('mounted!')
})
onUpdated(() => {
console.log('updated!')
})
onUnmounted(() => {
console.log('unmounted!')
})
return
},
})
vue2/vue3
- beforeCreate->use setup()
- created->use setup()
- beforeMount->onBeforeMount
- mounted->onMounted
- beforeUpdate->onBeforeUpdate
- updated->onUpdated
- beforeDestroy->onBeforeUnmount
- destroyed->onUnmounted
- errorCaptured->onErrorCaptured