官网
https://v3.cn.vuejs.org/guide/introduction.html
Vue3的特点
由原来的Object.defineProperty 的getter 和 setter,改变成为了ES2015 Proxy 作为其观察机制。
Proxy的优势:消除了以前存在的警告,使速度加倍,并节省了一半的内存开销。
总体来说:1. 更快 2. 更小 3. 更容易维护 4. 更加友好 5. 更容易使用
安装
cnpm i vue @vue/cli -g
初始化项目
vue create myapp
组合式API
用组件的选项 (data、computed、methods、watch) 组织逻辑在大多数情况下都有效。然而,当我们的组件变得更大时,逻辑关注点的列表也会增长。这可能会导致组件难以阅读和理解,尤其是对于那些一开始就没有编写这些组件的人来说。
如果我们能够将与同一个逻辑关注点相关的代码配置在一起,这样会更好。而这正是组合式 API 使我们能够做到的。
setup()
为了开始使用组合式 API,我们首先需要一个可以实际使用它的地方。在 Vue 组件中,我们将此位置称为 setup。
defineComponent ()
要让 TypeScript 正确推断 Vue 组件选项中的类型,需要使用 defineComponent 全局方法定义组件:
import { defineComponent } from 'vue'
const Component = defineComponent({
// 已启用类型推断
})
ref: 定义响应数据的初始值
一次只能定义一个,要用return返回
模板
<template>
<div class="wrapper">
home
<div>{{num}}</div>
<div>{{age}}</div>
<div>{{arr}}</div>
<div>{{obj}}</div>
</div>
</template>
js
<script>
import {defineComponent,ref} from 'vue'
export default defineComponent({
name: 'home',
props: {},
components: {},
setup(props,ctx){
//ref只能一次定义一个数据,可以直接return ,模板中直接使用 {{num}}
let num = ref(0)
let age = ref(18)
let arr = ref(['a','b'])
let obj = ref({
age: 1,
name: 'jack'
})
return {
num,
age,
arr,
obj
}
}
});
</script>
reactive: 一次定义多个响应式数据
结合 toRefs()进行返回
<template>
<div class="wrapper">
home
<div>{{num}}</div>
<div>{{age}}</div>
<div>{{arr}}</div>
<div>{{obj}}</div>
</div>
</template>
<script>
import {defineComponent,ref,reactive,toRefs} from 'vue'
export default defineComponent({
name: 'home',
props: {},
components: {},
setup(props,ctx){
//reactive可以定义多个数据
let data = reactive({
num: 1,
age: 100,
arr: [1,2],
obj: {
name: 'jack',
age: 18
}
})
return {
// ...data
...toRefs(data)
}
}
});
</script>
<style lang="scss" scoped>
</style>
定义方法,更新响应式数据
方法要return出去
更新ref定义的数据: 数据.value=新值
更新reactive定义的数据 : 对象.属性=新值
<template>
<div class="wrapper">
home
<hr>
<div @click="clickNum(100)">num--{{num}}</div>
<div @click="clickAge">age----{{age}}</div>
<div @click="clickArr">{{arr}}</div>
<div>{{obj}}</div>
</div>
</template>
<script>
import {defineComponent,ref,reactive,toRefs} from 'vue'
export default defineComponent({
name: 'home',
props: {},
components: {},
setup(props,ctx){
let num = ref(1)
let data = reactive({
age: 100,
arr: [1,2],
obj: {
name: 'jack',
age: 18
}
})
//定义的方法也要return才能使用
let clickNum = (val)=>{
console.log('num新值',val);
// ref定义的数据,访问时要加上value,因为数据是封装后的数据
console.log(num.value);
}
let clickAge = ()=>{
// reactive定义的数据,直接用data访问属性名即可
console.log(data.age)
}
return {
...data,
num,
clickNum,
clickAge
// ...toRefs(data)
}
}
});
</script>
<style lang="scss" scoped>
</style>
计算属性的使用
<template>
<div class="wrapper">
home
<hr />
<div>num1---{{ num1 }}</div>
<div>num2---{{ num2 }}</div>
<div>result---{{ result }}</div>
<div><button @click="add">add</button></div>
</div>
</template>
<script>
import { defineComponent, ref,computed } from "vue";
export default defineComponent({
name: "home",
props: {},
components: {},
setup(props, ctx) {
let num1 = ref(20);
let num2 = ref(30);
// 计算属性API computed()
let result = computed(()=>{
return num1.value + num2.value
})
//num1,num2的值发生变化,result会重新计算
let add = ()=>{
num1.value++
num2.value++
}
return {
num1,
num2,
result,
add
};
},
});
</script>
<style lang="scss" scoped>
</style>
watch监听
watch有2个参数, 第一个参数是监听的数据对象, 可以是单个变量、数组、函数;
第二个参数是数据改变时的回调函数, 有2个参数, 第一个是改变后的数据, 第二个是改变前的数据;
引入watch
import { ref, defineComponent, watch } from "vue";
在setup里面使用
/*监听props*/
watch(props,(newProps, oldProps) => {
showModal.value = newProps.isOpened;
editData.value = newProps.editData as IAdminUser;
});
使用Vuex
vuex的五个属性和vue2中的定义相同
引入组件中的方式发生变化
<template>
<div class="wrapper">
home
<hr />
<!-- 不能像vue2一样直接用store.state.list访问数据 ,vue3没有this -->
<!-- 但如果在setup中return出来store,是可以访问的 -->
<!-- <div>store中的list---{{ store.state.list }}</div> -->
<!-- <div>store中的list---{{ store.state.list }}</div> -->
<div>store中的list---{{ list }}</div>
</div>
</template>
<script>
// vuex中定义state还和以前一样
// 使用方式和以前不同
import { defineComponent, ref,computed} from "vue";
//引入useStore方法
import {useStore} from 'vuex'
export default defineComponent({
name: "home",
props: {},
components: {},
setup(props, ctx) {
//获取vuex中的store对象
let store = useStore()
//---------------------------------------------------------
// 一般用计算属性获取store中的数据
let list = computed(()=>{
return store.state.list
})
return {
// store
list
};
},
});
</script>
<style lang="scss" scoped>
</style>
路由跳转和路由传参
路由配置和vue2相同
在组件中引入方式不同
<template>
<div class="wrapper">
home
<hr />
<button @click="goto">跳转至about</button>
</div>
</template>
<script>
import { defineComponent, ref,computed} from "vue";
import {useRouter} from 'vue-router'
export default defineComponent({
name: "home",
props: {},
components: {},
setup(props, ctx) {
//获取全局路由对象
let router = useRouter()
// console.log(router);
// console.log(router.options)
const goto = ()=>{
// router.push("/about")
// router.push({
// path: '/about'
// })
let obj = {
age: 18,
name: 'xx'
}
//路由传参------query
// 可以和path选项 或 路由的name属性结合 ,刷新后参数还在
// router.push({
// path: '/about',
// query: {
// num: 10,
// color: 'red',
// //下方传递的方法,接收到是字符串 "[object object]"
// // obj: {
// // age: 18,
// // name: 'xx'
// // }
// obj: JSON.stringify(obj)
// }
// })
// 路由传参------params (不能用path+params,只能用路由name+params)
// 刷新后参数不在
router.push({
name: 'About',
params: {
num: 10000
}
})
}
return {
goto
};
},
});
</script>
<style lang="scss" scoped>
</style>
接收路由参数
<template>
<div class="about">
<h1>This is an about page</h1>
</div>
</template>
<script>
import { defineComponent} from "vue";
import { useRoute} from "vue-router";
export default defineComponent({
name: 'about1',
setup(props, ctx) {
//获取当前路由信息
let route = useRoute()
console.log(route);
// -----------------------query接收的全是字符串
// console.log(route.query.num);
// console.log(route.query.obj);
// console.log(typeof route.query.obj);
// --------------------------接收params的值
console.log(route.params.num);
return {
}
},
});
</script>
vue3中的生命周期
<template>
<div class="wrapper">
home
<hr />
<button @click="goto">跳转至about</button>
</div>
</template>
<script>
import { defineComponent, onMounted , onUnmounted} from "vue";
export default defineComponent({
name: "home",
setup(props, ctx) {
// 常用的生命周期函数
// 1. 初始化阶段 setup
// 2. 挂载之后 onMounted()
onMounted(()=>{
console.log('ajax请求');
console.log('dom操作');
console.log('接收路由参数');
})
onUnmounted(()=>{
console.log('清除定时器');
console.log('清除闭包函数');
})
return {
}
},
});
</script>
<style lang="scss" scoped>
</style>
子组件向父组件传值
<template>
<div class="wrapper">
Child
<div>接收父组件值:{{msg}}</div>
<div><button @click="send">向父组件传值</button></div>
</div>
</template>
<script>
import { defineComponent} from "vue";
export default defineComponent({
name: "home",
//------------------------特别注意
// props的值不需要在setup中return,直接在模板中使用即可
// 如果在setup中使用,需要用props.属性名来访问
props: {
msg: {
type: String,
required: true
}
},
setup(props, ctx) {
let send = ()=>{
//用setup第二个参数上下文,来调用emit分发事件send
ctx.emit("send",{
num: 1,
color: 'red'
})
}
return {
send
}
},
});
</script>
<style lang="scss" scoped>
</style>
父组件接收子组件的值
<template>
<div class="wrapper">
Home
<hr />
<Child :msg="msg" @send="getData"></Child>
</div>
</template>
<script>
import { defineComponent, ref } from "vue";
import Child from "./Child";
export default defineComponent({
name: "home",
components: {
Child,
},
setup(props, ctx) {
let msg = ref("父组件的值1");
let getData = (data)=>{
console.log('父组件接收到数据 ',data);
}
return {
msg,
getData
};
},
});
</script>
<style lang="scss" scoped>
</style>