// vue2的写法
new Vue({
router,
store,
render: h => h(App),
}).$mount('#app')
// vue3的写法
// 引入的不再是Vue构造函数, 而是一个工厂函数createApp
import { createApp } from 'vue'
import App from './App.vue'
// createApp(App) 创建应用实例对象, 类似于Vue2中的new Vue({}), 但更轻
createApp(App).mount('#app')
Vue3组件中的模板结构可以没有根标签
setup是所有 Composition API
表演的舞台。
组件中用到的数据,方法等,均要配置在setup中。
setup不能是一个async函数,因为返回值不再是return的对象,而是Promise,模板看不到return对象中的属性。
setup执行的时机在beforeCreate之前。
setup中的this是undefined。
import {h} from 'vue'
export default {
name: 'App',
// 此处只是测试一下setup, 暂不考虑响应式的问题
setup () {
// 数据
let name = '张三'
let age = 18
// 方法
function sayHello() {
alert(`我叫${name},我${age}岁了,你好啊!`)
}
// 返回对象(常用)
return {
name,
age,
sayHello
}
// 返回函数(渲染函数)
// return () => h('h1','ShangGuiGu')
}
}
const xxx = ref(initialValue)
,创建一个包含响应式数据的引用对象。
接收的数据可以是基本类型,也可以是对象类型。
基本类型的数据,响应式依然是靠Object.defineProperty()的get和set完成的。
对象类型的数据,依靠的是ES6中的Proxy
import {ref} from 'vue'
export default {
name: 'App',
setup () {
// 数据(响应式要使用ref包装, 返回一个Ref对象)
let name = ref('张三')
let age = ref(18)
// 方法(修改响应式数据的值)
function changeData() {
name.value = '李四'
age.value = 20
}
// 返回对象(常用)
return {
name,
age,
changeData
}
}
}
返回一个对象类型的响应式数据,基本类型的数据请使用ref。
ref也可以包装对象/数组类型数据,内部会通过reactive转为代理对象。
ref通过Object.defineProperty()的get和set来实现响应式。
reactive通过Pxoxy实现响应式,使用Reflect操作源对象内部数据。
// 接收一个对象或数组, 返回一个Proxy对象
const 代理对象 = reactive(源对象)
Vue3在处理对象类型响应式的时候,用的是ES6中的Proxy。
对象类型:通过Object.defineProperty()对属性的读取,修改进行拦截。
数组类型:通过重写更新数组的一系列方法来实现拦截。(对数组的变更方法进行了包裹)
存在问题:
对象新增属性,删除属性界面不会更新。
<template>
<div>
<h1>我是Vue2写的效果</h1>
<h2>姓名:{{ person.name }}</h2>
<h2>年龄:{{ person.age }}</h2>
<h2>性别:{{ person.sex }}</h2>
<button @click="addSex">添加sex属性</button>
<button @click="deleteName">删除name属性</button>
</div>
</template>
<script>
export default {
data () {
return {
person: {
name: '张三',
age: 18
}
}
},
methods:{
addSex () {
this.person.sex = '女'
},
deleteName () {
delete this.person.name
}
}
}
</script>
解决办法
addSex () {
// this.person.sex = '女'
// 追加
this.$set(this.person, 'sex', '女')
},
deleteName () {
// delete this.person.name
// 删除
this.$delete(this.person, 'name')
}
直接通过下标修改数组,界面不会自动更新。
updateArray () {
this.person.hobbies[0] = 'caish'
// 可以通过以下两种方式之一进行修改
// this.$set(this.person.hobbies, 0, 'caish')
// this.person.hobbies.splice(0, 1, 'caish')
}
Vue3
通过Proxy(代理)拦截对象中任意属性的变化。
通过Reflect(反射)对源对象的属性进行操作。
模拟Vue2实现响应式
let person = {
name: '张三',
age: 18
}
// 模拟Vue2对person实现响应式
let p = {}
Object.defineProperty(p, 'name', {
configurable: true,
get(){ // 读取时调用
return person.name
},
set(value){ // 修改时调用
console.log('有人修改了name属性, 我要去更新页面')
person.name = value
}
})
Object.defineProperty(p, 'age', {
configurable: true,
get(){
return person.age
},
set(value){
console.log('有人修改了age属性, 我要去更新页面')
person.age = value
}
})
模拟Vue3实现响应式
// 源数据
let person = {
name: '张三',
age: 18
}
// 返回一个 Proxy 对象
// 此后对p的任何修改都会作用到person身上
const p = new Proxy(person, {})
// 如果想要捕获修改, 在修改的时候做一些动作
const p = new Proxy(person, {
// 读取p某个属性时
get (target, propName) {
console.log(`有人读取了p身上的${propName}属性`)
return target[propName]
},
// 修改或新增p某个属性时
set (target, propName, value) {
console.log(`有人修改了p身上的${propName}属性,我要去更新页面`)
target[propName] = value
},
// 删除p某个属性时
deleteProperty (target, propName) {
console.log(`有人删除了p身上的${propName}属性,我要去更新页面`)
return delete target[propName]
}
})
// 对对象的操作还可以使用Reflect
Reflect.get(target, propName)
Reflect.set(target, propName, value)
Reflect.deleteProperty(target, propName)
props: {
msg: {
type: String,
default: ''
}
},
emits: ['hello'],
setup (props, context) {
console.log('-----setup-----', props) // 组件外部传过来的值,且组件内进行了声明接收
console.log('-----setup-----', context) // 上下文对象
console.log('-----setup-----', context.attrs) // 组件外部传过来的值,且组件内没有声明接收(Vue2中的this.$attrs)
console.log('-----setup-----', context.emit) // 触发自定义事件的函数(Vue2中的this.$emit)
console.log('-----setup-----', context.slots) // 收到的插槽内容(Vue2中的this.$slots)
function test () {
context.emit('hello', 666)
}
return {
test
}
}