vue3的特点
更多的API特性
体积更小、速度更快
解决遗留问题(比如对TS不友好、代码组件逻辑复用)
createApp表示创建一个Vue应用,传入的参数表示这个应用最外层的组件应该如何展示。
// createApp 表示创建一个 Vue 应用, 存储到 app 变量中
// 传入的参数表示,这个应用最外层的组件,应该如何展示
// MVVM 设计模式,M -> Model 数据, V -> View 视图, VM -> ViewModel 视图数据连接层
const app = Vue.createApp({
data() {
return {
message: 'hello world'
}
},
template: "<div>{{message}}</div>"
});
// vm 代表的就是 Vue 应用的根组件
const vm = app.mount('#root');
VUE3.0 新特性 Composition API
当一个组件越来越大的时候,会出现如下的代码结构:
<script>
const app = Vue.createApp({
data(){
return{
name:'dell',
age:23
}
},
methods: {
test() {
},
setName(){
},
setAge(){
},
a(){
},
b(){
}
},
computed:{
},
directives:{},
mixin:[],
mounted() {
},
template:`
<div>name:{{name}},age:{{age}}</div>`
});
const vm = app.mount('#root');
</script>
当一个组件变得越来越复杂的时候。会发现一个问题。当我们去看模板(template
)中,name
相关的逻辑的时候,会看到各个地方都涉及到这个变量的修改,维护性会很复杂。
这就有了Composition API。
Composition API涉及到需要了解一个重要的函数setup
。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>lesson 34</title>
<script src="https://unpkg.com/vue@next"></script>
</head>
<body>
<div id="root"></div>
</body>
<script>
// 对数据做校验的插件
const app = Vue.createApp({
template:`<div @click="handClick">name:{{name}},age:{{age}}</div>`
,
// created 实例被完全初始化之前
setup(props, context) {
//props:外部组件传递进来的一些内容
//context:上下文,代码中讲解
return {
//setup最后必须有一个return值
//handleClick:可见 setup中使用的内容会暴露在外部,比如函数可以直接调用,参数可以直接赋值。
name: 'dell',
handleClick: () => {
alert(123)
}
}
}
});
const vm = app.mount('#root');
</script>
</html>
进一步分析。
<script>
// 对数据做校验的插件
const app = Vue.createApp({
template:`<div @click="handClick">name:{{name}},age:{{age}}</div>`
,
method:{
test(){
alert('test')
}
},
// created 实例被完全初始化之前
setup(props, context) {
this.test();
//props:外部组件传递进来的一些内容
//context:上下文,代码中讲解
return {
//setup最后必须有一个return值
//handleClick:可见 setup中使用的内容会暴露在外部,比如函数可以直接调用,参数可以直接赋值。
name: 'dell',
handleClick: () => {
alert(123)
}
}
}
});
const vm = app.mount('#root');
</script>
如上代码是执行报错的。有了setup就不需要用到this这样的语法了,method方法有更好的Composition API写法。
setup没法调用外部的方法、函数、钩子函数等,比如methods、mounted。setup内部也没有this可以使用。
<script>
// 对数据做校验的插件
const app = Vue.createApp({
template:`<div @click="handClick">name:{{name}},age:{{age}}</div>`
,
method:{
test(){
console.log(this.$options.setup())
}
},
mounted(){this.test()},
// created 实例被完全初始化之前
setup(props, context) {
this.test();
//props:外部组件传递进来的一些内容
//context:上下文,代码中讲解
return {
//setup最后必须有一个return值
//handleClick:可见 setup中使用的内容会暴露在外部,比如函数可以直接调用,参数可以直接赋值。
name: 'dell',
handleClick: () => {
alert(123)
}
}
}
});
const vm = app.mount('#root');
</script>
ref, reactive 响应式的引用
如下代码:
<script>
// ref, reactive 响应式的引用
// 原理,通过 proxy 对数据进行封装,当数据变化时,触发模版等内容的更新
// ref 处理基础类型的数据
// reactive 处理非基础类型的数据
const app = Vue.createApp({
template: `
<div>{{name}}</div>
`,
setup(props, context) {
return { name:'dell' }
}
});
const vm = app.mount('#root');
</script>
修改代码:
<script>
const app = Vue.createApp({
template: `
<div>{{name}}</div>
`,
setup(props, context) {
let name = 'dell';
setTimeout(() => {
name = 'lee'
}, 2000)
return {
name: 'dell'
}
}
});
const vm = app.mount('#root');
</script>
发现及时name变了,模板也不会改变。
Composition API中有办法将普通的变量改成响应式的变量。
原理:通过 proxy 对数据进行封装,当数据变化时,触发模版等内容的更新
这时候需要ref,这是vue提供给我们的一个语法。
<script>
const app = Vue.createApp({
template: `
<div>{{name}}</div>
`,
setup(props, context) {
const { ref } = Vue;
// proxy , 'dell' 变成 proxy({value: 'dell'}) 这样的一个响应式引用
let name = ref('dell');
setTimeout(() => {
name.value = 'lee'
}, 2000)
return {
name: 'dell'
}
}
});
const vm = app.mount('#root');
</script>
ref适合处理基础数据类型,像对象{}
、数组[]
,就不方便处理。
除此之外,可以有如下写法,通过reactive
:
<script>
const app = Vue.createApp({
template: `
<div>{{nameObj.name}}</div>
`,
setup(props, context) {
const { reactive} = Vue;
// reactive 处理非基础类型的数据
//proxy , { name: 'dell'} 变成 proxy({ name: 'dell'}) 这样的一个响应式引用
let nameObj = reactive({name:'dell'});
setTimeout(() => {
nameObj.name = 'lee'
}, 2000)
return {
nameObj
}
}
});
const vm = app.mount('#root');
</script>
由以上可见,setup中通过ref, reactive彻底转换了data的作用。
readonly
如果一个参数我们定义为只读状态,vue提供readonly函数。
<script>
const app = Vue.createApp({
template: `
<div>{{nameObj,name}}</div>
`,
setup(props, context) {
const { reactive, readonly } = Vue;
const nameObj = reactive({name: 'dell', age: 28});
const copyNameObj =readonly(nameObj);
setTimeout(() => {
nameObj.name = 'lee'
copyNameObj.name = 'laa'
}, 2000)
return { nameObj ,copyNameObj}
}
});
const vm = app.mount('#root');
</script>
如上代码就会因为readonly
报错。
toRefs
解构,如果只需要传递某一个对象中的一个属性,那么可以如下编写代码:
<script>
const app = Vue.createApp({
template: `
<div>{{name}}</div>
`,
setup(props, context) {
const { reactive, toRefs } = Vue;
const nameObj = reactive({name: 'dell', age: 28});
setTimeout(() => {
nameObj.name = 'lee'
}, 2000)
const {name} = toRefs(nameObj);
return { nameObj ,copyNameObj}
}
});
const vm = app.mount('#root');
</script>
同样可以实现动态更新值。
toRefs的原理:
toRefs proxy({ name: 'dell', age: 28}), {
name: proxy({ value: 'dell'}),
age: proxy({value: 28})
}
toRef
<script>
// toRef, context
const app = Vue.createApp({
template: `<div>{{age}}</div>`,
setup(props, context) {
const { reactive,toRef } = Vue;
const data= reactive({name:'dell'});
//意思是从data这个响应式对象中尝试取age这个响应式数据。
//如果能取到就取到,如果不存在,则默认取到一个空的响应式数据
const name = toRef(data,'age');
setTimeout(()=>{
name.value='lee'
},2000)
return { age }
}
});
const vm = app.mount('#root');
</script>
context参数
<script>
// toRef, context
const app = Vue.createApp({
template: `<child >parent</child>`,
});
app.component('child', {
setup(props, context) {
const { h } = Vue;//h函数
//context中有3个值
//attrs:指的是父组件传递过来的None-props属性
//(就是当子组件不存在props声明时,父组件传递的自定义属性会在这个属性值中)
//slots:父组件传递过来的插槽( 'parent')
//emit:在setup中触发父组件的事件
const { attrs, slots, emit } = context;
return ()=>h('div',{},['123123']);
}
})
const vm = app.mount('#root');
</script>
这个相当于把<div>123123</div>
渲染到页面上。
如果想把插槽内容parent
渲染到页面上:
<script>
// toRef, context
const app = Vue.createApp({
template: `<child >parent</child>`,
});
app.component('child', {
setup(props, context) {
const { h } = Vue;//h函数
const { attrs, slots, emit } = context;
return ()=>h('div',{},slots.default());
}
})
const vm = app.mount('#root');
</script>
emit的使用:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>lesson 36</title>
<script src="https://unpkg.com/vue@next"></script>
</head>
<body>
<div id="root"></div>
</body>
<script>
// toRef, context
const app = Vue.createApp({
methods: {
handleChange() {
alert('change');
}
},
template: `<child @change="handleChange">parent</child>`,
});
app.component('child', {
template: '<div @click="handleClick">123123</div>',
setup(props, context) {
const { h } = Vue;//h函数
//context中有3个值
//attrs:指的是父组件传递过来的None-props属性
//(就是当子组件不存在props声明时,父组件传递的自定义属性会在这个属性值中)
//slots:父组件传递过来的插槽( 'parent')
//emit:在setup中触发父组件的事件
const { attrs, slots, emit } = context;
function handleClick() { emit('change'); }
return { handleClick }
}
})
const vm = app.mount('#root');
</script>
</html>