chenhuiYj / chenhuiYj/note

vue3 - 选项式API和组合式API

Open
#47 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
HTML
Stars
6
Forks
0
PR merge metrics
No merged PRs in 30d

Description

Options API

试想下,如果页面(App.vue)有两个功能,一个是 todoList,一个是计数器。如果按照选项式 API 方式显示,代码大致是下面这样

<template>
  <div id="app">
    <!--todoList-->
    <input type="text" v-model="val" @keyup.enter="addTodo">
    <ul>
      <li v-for="todo in todos" :key="todo.id">{{todo.title}}</li>
    </ul>
    <!--计数器-->
    <div>count:{{count}}</div>
    <div>double:{{double}}</div>
    <div><button @click="addCount">加 1</button></div>
  </div>
</template>

<script>
export default {
  data(){
    return{
            //todoList 的 data
      val:'',
      todos:[ 
        {id:0, title:'吃饭', done:false},
        {id:1, title:'睡觉', done:false},
        {id:2, title:'学习', done:false},
      ],
	  //计数器的 data
      count:1
    }
  },
 methods:{
	//todoList 的方法
    addTodo(){
      this.todos.push({
        id:this.todos.length,
        title:this.val,
        done:false
      })
      this.val = ''
},
//计数器的方法
    addCount(){
      this.count++
    }
  },
//计数器的计算属性
  computed: {
    double() {
      return this.count * 2
    }
  }
}
</script>

渲染后的页面如下

pEF0DxS.png

在 input 输入内容,按下回车,就会加一个 todo。点击一次“加 1”按钮,count 就会加1。

想必大家也看出来了,选项式 API 的一个特点就是,每个功能都会有 data,computed,methods 等配置项。大致呈现下面的模式(一个颜色对应一个功能)

图片来自:https://juejin.cn/post/6891640356543627278

图片来自:https://juejin.cn/post/6891640356543627278

选项式 API 写法的优势,可以直观的看到 data,computed,methods 等选项的代码。写法也很简单。

选项式 API 写法的劣势也比较明显。从上面代码看到一个功能的代码会交叉放在每个功能都会有 data,computed,methods 等配置项上。代码看着没什么问题,但是实际开发中,代码的数量和复杂度远远超过上面的例子。当其中一个功能需要修改的时候,就要在 data,computed,methods 等配置项里面找到那个功能打代码进行修改。上面例子代码少,不复杂修改还是很简单的,如果文件代码多,逻辑复杂时候,修改的成本就要高很多了。

要解决这个问题,可以使用 mixin。

比如抽离一个 count.js

export default {
  data() {
    return {
      count:1
    }
  },
  computed: {
    double() {
      return this.count * 2
    }
  },
  methods:{
    addCount(){
      this.count++
    }
  }
}

App.vue

<template>
  <div id="app">
    <!--todoList-->
    <input type="text" v-model="val" @keyup.enter="addTodo">
    <ul>
      <li v-for="todo in todos" :key="todo.id">{{todo.title}}</li>
    </ul>
    <!--计数器-->
    <div>count:{{count}}</div>
    <div>double:{{double}}</div>
    <div><button @click="addCount">加 1</button></div>
  </div>
</template>

<script>
import Count from './count'
export default {
  //通过 mixins 引入 Count
  mixins:[Count],
  data(){
    return{
      val:'',
      todos:[ 
        {id:0, title:'吃饭', done:false},
        {id:1, title:'睡觉', done:false},
        {id:2, title:'学习', done:false},
      ]
    }
  },
  methods:{
    addTodo(){
      this.todos.push({
        id:this.todos.length,
        title:this.val,
        done:false
      })
      this.val = ''
    }
  }
}
</script>

这样写,代码确实拆分了,但是 mixin 问题也暴露出来了,App.vue 里面的 template 上,count,double,addCount 这些属性完全不知道从哪来的,不知道是 mixin,还是全局 install,还是 Vue.prototype.count 设置的,数据来源完全模糊,调试非常麻烦。

而且有的时候,mixin 带来的 count,double,addCount 这些属性,App.vue 如果也要使用,到时候又不知道 this 的 count,double,addCount 这些属性从哪里来的。

mixin 的问题还不止这个,还有可能造成命名冲突,比如 count.js 里面的 count 改成 val,就会和 App.vue 的 val 产生冲突了。mixin 的特性,可能压根不知道是哪两个变量冲突了,有会给调试带来麻烦。

如果想复用选项式 API 写法,就得先把上面的问题避免。

Composition API

同样的页面,下面使用 Composition API 方式实现一次。

<template>
  <div id="app">
    <input type="text" v-model="val" @keyup.enter="addTodo">
    <ul>
      <li v-for="todo in todos" :key="todo.id">{{todo.title}}</li>
    </ul>
    <div>count:{{count}}</div>
    <div>double:{{double}}</div>
    <div><button @click="add">加1</button></div>
  </div>
</template>

<script setup>
import {reactive, ref, computed} from 'vue'
//todoList
let val = ref('')
let todos = reactive([ 
    {id:0, title:'吃饭', done:false},
    {id:1, title:'睡觉', done:false},
    {id:2, title:'lsp', done:false},
])
function addTodo(){
  todos.push({
    id:todos.length,
    title:val.value,
    done:false
  })
  val.value = ''
}
//计数器
let count = ref(1)
function add(){
    count.value++
}
let double = computed(()=>count.value*2)
</script>

可以看到,todoList 的代码(data,methods)和计数器的代码(data,methods,computed)按照功能分开一块一块了。这样一来,如果需要修改其中一个功能,就不需要交叉的看其他功能的代码了,代码会比 options API 方式更清晰。

代码大概是下面这样

https://juejin.cn/post/6891640356543627278

图片截自:https://juejin.cn/post/6891640356543627278

至于复用,上面的代码可能一下看不出来效果,下面抽离出 todo.js 和 count.js,复用效果就出来了。

App.vue

<template>
  <div id="app">
    <input type="text" v-model="val" @keyup.enter="addTodo">
    <ul>
      <li v-for="todo in todos" :key="todo.id">{{todo.title}}</li>
    </ul>
    <div>count:{{count}}</div>
    <div>double:{{double}}</div>
    <div><button @click="add">加1</button></div>
  </div>
</template>

<script setup>

import useToDo from './todo'
import useCounter from './count'

let {val, todos, addTodo} = useToDo()


let {val:count,double,add} = useCounter() 
</script>

todo.js

import {reactive, ref} from 'vue'
export default function useToDo(){
  let val = ref('')
  let todos = reactive([ 
      {id:0, title:'吃饭', done:false},
      {id:1, title:'睡觉', done:false},
      {id:2, title:'lsp', done:false},
  ])
  function addTodo(){
    todos.push({
      id:todos.length,
      title:val.value,
      done:false
    })
    val.value = ''
  }
  return {val, todos, addTodo}
}

count.js

import {ref, computed} from 'vue'
export default function useCounter(){
    let val = ref(1)
    function add(){
        val.value++
    }
    let double = computed(()=>val.value*2)
    return {val, double, add}
}

这样一改写,复用就能看出来了,复用的改写也很简单。

想必大家也看到了,实际上 Composition API 就是把代码分成了,很多个 function,再引入

https://juejin.cn/post/6891640356543627278

图片截自:https://juejin.cn/post/6891640356543627278

Composition API 的劣势就是学习难度会稍微高一些,但不算太难。以及 Composition API 是 vue3之后才支持,网上的资源,生态肯定不如 options api,但随着时间发展,这些问题不大。

------- 组合式 API 选项式 API
‌逻辑复用 更好,通过可组合函数实现 较差,通常通过 mixin 实现,但存在作用域不清晰、命名冲突等问题
‌代码组织 更清晰,按逻辑关注点组织代码 直观,但复杂组件中代码可能分散在多个选项中
‌响应式系统 更灵活,通过 ref 和 reactive 控制 相对固定,主要通过 data 选项
‌学习难度 较难,需要理解新的概念和模式 简单,符合传统面向对象编程的思维方式
‌IDE 支持 较好,但依赖于特定插件或配置 良好,多数 IDE 都有良好支持
‌文档和资源 逐渐增加,但相对于选项式 API 较少 丰富,有大量教程和社区支持

Composition API 和 Options API 如何公用?

首先说明,不建议这种写法。在一个组件里面,如果用了 Options API,就继续使用 Options API,如果用了 Composition API 就继续用 Composition API。

如果一个基于 Options API 开发的组件需要和一个基于 Composition API 开发的组件进行整个,就可以在一个选项式 API 的组件中通过 setup() 选项来使用组合式 API。

<script>
import { ref } from 'vue'

export default {
  data(){
    return {
      msgOption:'守候-options'
    }
  },
  setup() {
    const msgSetup = ref('守候-setup')
    return {
      msgSetup
    }
  },
}
</script>

<template>
  <h1>{{ msgOption }}</h1>
  <input v-model="msgOption" />

  <h1>{{ msgSetup }}</h1>
  <input v-model="msgSetup" />
</template>

pEF042T.png

但是需要注意 data 不要重名等。如果重名就会出现问题,如下实例

<script>
import { ref } from 'vue'

export default {
  data(){
    return {
      msg:'守候-options'
    }
  },
  setup() {
    const msg = ref('守候-setup')
    const handleUpdateMsgForSetup=function(){
      msg.value='修改后-setup'
    }
    return {
      msg,
      handleUpdateMsgForSetup
    }
  },

  methods:{
    handleUpdateMsgForOptions(){
      this.msg='修改后-options'
    }
  },

  mounted() {
    console.log(this.msg) // 0
  }
}
</script>

<template>
  <h1>{{ msg }}</h1>
  <input v-model="msg" />

  <p @click="handleUpdateMsgForOptions">handleUpdateMsgForOptions</p>
  <p @click="handleUpdateMsgForSetup">handleUpdateMsgForSetup</p>
</template>

pEF0hGV.png

上面的例子可以看到,在 data 和 setup 同时声明了 msg,但是拿的是 setupData。那是因为,vue3 的写法是,如果 setupData 里面有值,就先拿 setupData 的值。setupData 没有就再从 data 里面取值。

自然而然点击 handleUpdateMsgForSetup,msg 的值就会变成‘修改后-setup’,点击 handleUpdateMsgForOptions,msg 的值就会变成‘修改后-options’

参考链接

https://juejin.cn/post/6892017198450081800
https://cn.vuejs.org/guide/extras/composition-api-faq
https://juejin.cn/post/6894993303486332941

Contributor guide

No contributing guide indexed for this repository

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Review the full issue and the repository’s documentation conventions first; no target file, test, or entry point is named. Define the intended documentation scope and acceptance criteria before editing, then verify the finished Vue 3 Options API and Composition API comparison against the cited references.

Written by the indexing model from the issue text.

Assessment

Tech stack
javascript
Domain
documentation
Issue type
Documentation
Difficulty
4/5
Estimated time
3-5 days
Activity status
Stale
Clarity
Needs clarification
Newbie friendliness
25/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.