赞
踩
目录
三、组合式API(Composition API)和选项式API(options API)
vue目前是前端框架中比较火热的,有了vue2为什么还要学习vue3呢,就像打游戏一样,为什么要升级呢,虽然公司大部分使用的还是vue2,还是有少部分公司使用vue3,提前了解使用vue3 也不是坏处
1.性能提升,首次渲染更快,diff算法更快,内存占用更少,打包体积更小,
2.更好的Typescript支持(在vue下写TS更方便了)
3.提供新的写代码方式:Composition API (需要成本学习)
vue3.0对于vue2.0的版本大部分语法都是可以兼容使用的,但是有些破坏性语法的更新
1.移除了$on方法(eventBus现有模式不再支持,可以使用第三方插件代替)
2.移除过滤器选项(插值表达式里不能在使用过滤器filter,可以使用methods替代)
3.移除.sync语法(v-bind不能在使用.sync修饰符了,现在和v-model语法合并了)
vue create vue3-learn
创建vue3项目中可能会出现 Cannot find module ‘vue-loader-v16/package.json
解决办法 :
① npm install npm@latest -g
(更新 npm)
② npm i vue-loader-v16
package.json 在dependencies配置项中显示,我们当前使用的版本为3.1.4
1.main.js中
核心代码:createApp(App).mount('#app') = createApp(根组件).mount('public/index.html中的div容器')
1.vue2.0中是直接创建了一个vue实例
2.vue3.0中按需导出了一个createApp (ceateApp做了什么)
也就是说 在vue2.0中必须要有一个根元素,在vue3中没这个要求
- <template>
- <img alt="Vue logo" src="./assets/logo.png">
- <HelloWorld msg="Welcome to Your Vue.js App"/>
- </template>
vue3 组合式api(Composition API)的出现是用来区别于vue2选项式api(options API)
下面是来区别vue2和vue3的区别代码 需求 实现盒子显示与隐藏
options API(Vue2) | Composition API(Vue3) | |
优点 | 方便理解,逻辑清楚(数据是放在data里,方法是放在methods里) | 维护性强,数据、方法放在一起 |
缺点 | 代码横跳,不好复用 | 代码量大可以逻辑拆分 |
拆分setup 适用于代码量较多的情况 优点 分散定义,组合使用
- <script>
- function useShow() {
- let show = ref(true);
- const isHide = () => (show.value = false)
- const isShow = () => (show.value = true)
- return {show,isHide,isShow}
- }
- import { ref } from "vue";
- export default {
- setup() {
- const {show,isHide,isShow} = useShow()
- return { show, isShow, isHide };
- },
- };
- </script>
① 一个组件选项,在创建组件之前执行,一旦 props
被解析,并作为组合式 API 的入口点
② setup中不能使用不能使用this、this执行undefined
③ setup函数在beforecreated钩子函数前执行
④ setup函数只会在初始化的时候执行一次
- beforeCreate() {
- console.log("beforeCreate");
- },
- setup() {
- console.log("setup");
- },
此时setup中的this还不是组件实例,此时this是undefined
- setup(props, context) {
-
- }
① 第1个参数为props。props为一个对象,内部包含了父组件传递过来的所有prop数据
② 第2个参数为一个对象context。context对象包含了attrs,slots, emit属性,
③ 如果在data()中也定义了同名的数据,则以setup()中为准。
后面做父子双向绑定时细说参数的用法
选项式 API | Hook inside setup 组合式API |
---|---|
beforeCreate | Not needed* |
created | Not needed* |
beforeMount | onBeforeMount |
mounted | onMounted |
beforeUpdate | onBeforeUpdate |
updated | onUpdated |
beforeUnmount | onBeforeUnmount |
unmounted | onUnmounted |
errorCaptured | onErrorCaptured |
renderTracked | onRenderTracked |
renderTriggered | onRenderTriggered |
activated | onActivated |
deactivated | onDeactivated |
这些生命周期钩子注册函数只能在 setup() 期间同步使用,因为它们依赖于内部全局状态来定位当前活动实例 (此时正在调用其 setup()
的组件实例)。在没有当前活动实例的情况下调用它们将导致错误。
组件实例上下文也是在生命周期钩子的同步执行期间设置的,因此在生命周期钩子内同步创建的侦听器和计算属性也会在组件卸载时自动删除。
因为 setup
是围绕 beforeCreate
和 created
生命周期钩子运行的,所以不需要显式地定义它们。换句话说,在这些钩子中编写的任何代码都应该直接在 setup
函数中编写。
可以通过在生命周期钩子前面加上 “on” 来访问组件的生命周期钩子。前提需要引入
- export default {
- setup() {
- // mounted
- onMounted(() => {
- console.log('Component is mounted!')
- })
- }
- }
- <template>
- <div>{{ num }}</div>
- <button @click="addNum">加1</button>
- </template>
-
- <script>
- export default {
- setup() {
- let num=1,
- addNum=()=>{
- num++
- console.log(num);
- }
- return {num,addNum}
- },
- };
- </script>
点击加1后发现 视图上并没有发生改变 也就是说setup函数中返回的数据并不是响应式的
简单数据类型
- <script>
- import {ref} from 'vue'
- export default {
- setup() {
- let num=ref(1),
- addNum=()=>{
- num.value++
- console.log(num);
- }
- return {num,addNum}
- },
- };
- </script>
复杂数据类型
- <template>
- <div>
- {{ obj.a }}
- <button @click="addNum">加1</button>
- </div>
- </template>
-
- <script>
- import {ref} from 'vue'
- export default {
- setup() {
- const obj = ref({a:1})
- const addNum = ()=>obj.value.a++
- return {obj,addNum}
- },
- };
- </script>
使用步骤
① 从vue框架中导入ref函数
② 使用ref函数并传入数据(简单数据类型或复杂数据类型)
③ 在setup函数中把ref函数调用完毕的返回值以对象的形式返回出去
ref
会返回一个可变的响应式对象,该对象作为一个响应式的引用维护着它内部的值,这就是 ref
名称的来源。该对象只包含一个名为 value
的 property 注意:在setup函数中需要加.value,在template模板中不需要加value
在vue2中获取 DOM元素和 子组件的引用直接绑定ref 通过$refs.ref绑定属性名获取引用
在vue3中 直接绑定ref 是获取不到的
- <template>
- <div ref="box"></div>
- </template>
-
- <script>
- import { ref, onMounted } from "vue";
- export default {
- setup() {
- const box = ref(null);
- onMounted(() => {
- console.log(box.value);
- });
- return { box };
- },
- };
- </script>
注意获取dom要在onMounted钩子函数中 获取,或者添加一个点击事件
我们使用ref函数进行定义响应式数据每次使用都要加.value,reactive就是用来省去加.value,但是只能定义复杂数据类型的数据
- <template>
- <div>
- {{ obj.a }}
- <button @click="addNum">加1</button>
- </div>
- </template>
-
- <script>
- import {reactive} from 'vue'
- export default {
- setup() {
- const obj = reactive({a:1})
- const addNum = ()=>obj.a++
- return {obj,addNum}
- },
- };
- </script>
定义完响应式数据后,我们每次都要在模板中 通过对象.的形式进行渲染数据,但是如果为了解决这种麻烦 可以通过toRefs函数进行解构,模板中就可以直接使用a了 toRefs与reactive配套使用。
- <template>
- <div>
- {{ a }}
- <button @click="addNum">加1</button>
- </div>
- </template>
-
- <script>
- import {reactive, toRefs} from 'vue'
- export default {
- setup() {
- const obj = reactive({a:1})
- let {a} = toRefs(obj)
- const addNum = ()=>obj.a++
- return {a,addNum}
- },
- };
- </script>
从上面看就是给a 进行解构 但是为什么不直接...obj呢
- setup() {
- const obj = reactive({a:1})
- // let {a} = toRefs(obj)
- const addNum = ()=>{obj.a++, console.log(obj.a);}
- return {...obj,addNum}
- },
答案是不能的 原因就是使用...a后 数据并没有保留响应式的特点
官网:当从组合式函数返回响应式对象时,toRefs
非常有用,这样消费组件就可以在不丢失响应性的情况下对返回的对象进行分解/扩散
我们现在有两种定义响应式数据的方式到底该用哪个呢,
① 优先使用ref函数 因为ref 函数 可以处理简单数据类型 也可以处理复杂数据类型 ,常用于简单数据类型定义响应式数据
ref特点: 在代码中获取或修改值时,需要补上.value 但是 template模板中不需要
② reactive 常用与定义复杂数据类型作为响应式数据
reactive 特点 在代码中获取或修改值是不需要加.value , 如果明确知道对象中有什么属性就用reactive
语法:
- import { computed } from 'vue'
-
- const 计算属性名 = computed(() => {
- return 响应式数据相关计算
- })
- <template>
- <div>姓名:{{name}} 月薪:{{salary}}年薪:{{total}}</div>
- </template>
-
- <script>
- import { computed, ref } from "vue";
- export default {
- setup() {
- const name = ref("奥特曼");
- let salary = ref(10000)
- const total = computed(()=>salary.value * 12)
- return { name,salary,total };
- },
- };
- </script>
- <template>
- <div>
- 姓名:{{ name }} 月薪:{{ salary }}年薪:{{ total }}
- <input type="text" v-model="total" />
- </div>
- <button @click="double">月薪double</button>
- </template>
-
- <script>
- import { computed, ref } from "vue";
- export default {
- setup() {
- const name = ref("奥特曼");
- let salary = ref(10000);
- const total = computed({
- set(val){
- return salary.value=val/12
- },
- get(){
- return salary.value * 12
- }
- });
- const double = () => salary.value *= 2;
- return { name, salary, total, double };
- },
- };
- </script>
计算属性也可以绑定v-model
语法:
watch(数据|数组|get函数,(新值,旧值)=>{回调处理逻辑}, {immediate:true|false, deep: true|false})
- <template>
- <div>{{ num }}</div>
- <button @click="addNum">加1</button>
- </template>
-
- <script>
- import { ref, watch } from "vue";
- export default {
- setup() {
- let num = ref(1),
- addNum = () => {
- num.value++;
- };
- watch(num, (newValue) => {
- console.log(newValue);
- });
- return { num, addNum };
- },
- };
- </script>
- <template>
- <div>{{ num }}||| {{num2}} </div>
- <button @click="addNum">加1</button>
- <button @click="addNum2">加1</button>
- </template>
-
- <script>
- import {ref,watch} from 'vue'
- export default {
- setup() {
- let num=ref(1)
- let num2=ref(2)
- const addNum=()=>num.value++
- const addNum2=()=>num2.value++
- watch([num,num2],(newValue)=>{
- console.log(newValue);
- })
- return {num,num2,addNum,addNum2}
- },
- };
- </script>
如果监听 复杂数据类型 在第三个参数对象中的deep:true
- <template>
- 姓名:{{ stu.name }},公司:{{ stu.company }}
- 月薪 {{ stu.money.salary }}
- <button @click="stu.company ='CSDN'">跳槽到CSDN</button>
- </template>
-
- <script>
- import { reactive, watch } from 'vue'
- export default {
- setup() {
- const stu = reactive({
- name: '小王',
- company:'JD',
- money: {
- salary: 1800
- }
- })
- watch(stu, () => {
- // 数据变化之后的回调函数
- console.log('stu发生了变化')
- })
- return { stu }
- }
- }
- </script>
写watch的时候避免使用deep引起性能的问题,第一个参数可以使用回调的方式
- <template>
- 姓名:{{ stu.name }},公司:{{ stu.company }}
- 月薪 {{ stu.money.salary }}
- <button @click="stu.money.salary =18000">月薪上升</button>
- </template>
-
- <script>
- import { reactive, watch } from 'vue'
- export default {
- setup() {
- const stu = reactive({
- name: '小王',
- company:'CSDN',
- money: {
- salary: 1800
- }
- })
- watch(()=>{
- return stu.money.salary
- }, () => {
- // 数据变化之后的回调函数
- console.log('薪资发生变化了')
- })
- return { stu }
- }
- }
- </script>
首次监听是immediate 深度监听 deep
- <template>
- 姓名:{{ stu.name }},公司:{{ stu.company }}
- 月薪 {{ stu.money.salary }}
- <button @click="stu.money.salary*=2">工资double</button>
- <button @click="stu.money.bonus*=2">奖金double</button>
- </template>
-
- <script>
- import { reactive, watch } from 'vue'
- export default {
- setup() {
- const stu = reactive({
- name: '小王',
- company:'JD',
- money: {
- salary: 18000,
- bonus:15000
- }
- })
- watch(() => {
- return stu.money
- }, () => {
- console.log('stu.money发生了变化')
- }, {
- deep: true // 只有为true,才能监听money下所有属性的变化
- })
- return {
- stu
- }
- }
- }
- </script>
在setup函数中有两个参数 第一个参数为props,第二个参数为context
props为一个对象,props接收后,内部包含了所有传递过来的prop数据,context包含了attrs,slots,emit属性,其中emit方法可以完成子传父
父
- <template>
- <div>
- <Son :msg="msg"></Son>
- </div>
- </template>
-
- <script>
- import { ref } from 'vue';
- import Son from "./son.vue";
- export default {
- components: {
- Son,
- },
- setup() {
- const msg = ref('奥特曼')
- return {msg}
- },
- };
- </script>
-
- <style scoped>
- </style>
子
- <template>
- <div>子组件 {{msg}} </div>
- </template>
-
- <script>
- export default {
- props:{
- msg:{
- type:String,
- default:''
- }
- },
- setup(props,context){
- console.log(props,context);
- }
- };
- </script>
-
- <style scoped>
- </style>
注意vue2中的this.$emit 不在生效 setup函数中没有this ,而在setup第二个参数中有emit方法
子
- setup(props,context){
- context.emit('name','奥特曼')
- }
-
- //结构写法
- // setup(props,{emit}){
- // emit('name','奥特曼')
- // }
父
- <template>
- <Son @name="name" ></Son>
- </template>
-
- setup() {
- const name = (name)=>console.log(name);
- return {name}
- },
父传子:在setup中使用props数据 setup(props){ props就是父组件数据 }
子传父:触发自定义事件的时候emit来自 setup(props,{emit}){ emit 就是触发事件函数
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。