Recently, vue3+ Element-Plus + Webpack (vite is not used yet) has been enabled in the company’s new project. The biggest change is CompositionAPI. Here is a bit of experience in using it.

Front knowledge

Vue from 2 to 3, the most basic aspect is usually some grammar changes, official documents and online articles are very detailed, this article is no longer tired, this part can refer to the following articles as a pre-study

Vue3 Chinese document

A long swastika will take you to fully master Vue3

[Vue3 official tutorial] 🎄 word notes | synchronous learning video guide

Preparation before development

Here, I use a VUE2 OA system template of the company as the basis for development. There are also many such templates on GitHub, such as very useful ones

Vue-element-admin Base template

From VUe2 to VUe3, I took three steps:

  1. Simply change the syntax of VUe2 to that of VUe3

  2. Use the Position API for code extraction and reuse. Because at the beginning there will be an unfamiliar to familiar process, if the grammar has not written proficients to pull away from the code will not go smoothly, at the same time will give yourself a psychological hint: vue3 this is a broken thing, or vue2 fragrance. First impressions are important for everything

  3. The project fully embraces TypeScript. I don’t know why this is last. Since I’m not familiar with TypeScript, if I had added it in the first place, the project would have gone almost to zero.

Item syntax modification

Let’s start with the first step: modify the syntax of VUe3 to read more documents on the line, if you don’t like the trouble you can also read the two articles recommended above, which probably understand the grammar can be started.

Note in this section that while VUe3 is compatible with vue2’s syntax, there are some incompatible updates

V3 Migration Guide

Also check the latest documentation if previous projects used vuex Vue-Router

vue-router

vuex

Vuex and VUE-Router documents are in English, so you will have no problem with cet 8. But considering there may be lazy cancer sufferers like me, here are some of the more common ones;

import {useStore} from 'vuex'
import {useRoute, useRouter} from 'vue-router'

setup() {
  // store can be used as this.$store in vue2. And then how can we continue with the call method before
  const store = useStore()
  / / such as:
  store.dispatch('xxx')
  store.commit('xxx')
  const gettersData = store.getters.xxx

  // route is equivalent to vue2's this.$route
  const route = useRoute()
  const xxx = route.query.xxx

  // Router is equivalent to vue2's this.$router, which is called when jumping to a route
  const router = useRouter()
  router.push()
}
Copy the code

If you don’t change the page logic and so on, just change the syntax, this part will be familiar in a few days

CompositionAPI

When I follow the steps above to write the page logic, ho, refreshing.

A setup function on a page that doesn’t have too much logic gives you 300 or 400 lines, data, methods, listeners, etc., mixed together, you have me, I have you, the code is sweet, give me a whole head of several large circles. And this is not too much logic, if a more complex page is not thousands of lines of code setup waiting for me to favor? Alexander

Here you can see one benefit of VUE2: a high lower limit. Even if my basic knowledge is not good, I can write methods in methods and watch in watch. These options of vue2 make the code clear to some extent. If I just change the syntax of vue2 into vue3 and do not use CompositionAPI to extract functional logic, I think it’s true and I’d rather use 2, because it’s really messy.

Here are two examples based on my project from both global logic reuse and logic extraction within components to document my approach. Here the code and logic will be presented as simple as possible, and the specific ideas and ideas will be described more.

Component or Position API

Vue3 has a CompositionAPI for reuse, and I used to reuse components in some places. Should I use components or CompositionAPI for reuse?

A simple way to distinguish this is to see if the part of the content you’re dealing with has a UI structure that needs to be reused. If any, the component is preferentially removed. If not, consider the CompositionAPI. (Most of them are considered in this way, and the actual project should be judged according to the actual situation); Some logic needs to be reused, such as ultramans all need to hit monsters in common, so that the CompositionAPI can be removed

This is how we used the CompositionAPI, but there are still problems in the actual project: If I have a page with a lot of logic, and there is really no logic to reuse, using the positionAPI seems unnecessary, but the setup is really long and smelly without doing anything. If it is written at one go, it is ok, because I still have some memory of what I wrote before, but if it takes a little longer, it is really torture to go back and look again. First of all, regardless of whether the logic is complex or not, it is hard to watch such a long function. So in that sense it’s necessary to pull away.

So to sum up, there are two main considerations to extract positionAPI: logic reuse and easy reading and maintenance

Global logic reuse

Abstracting logic code generally follows the principle of single function, a logical JS is only responsible for one function, while managing their own data, that is, the behavior of changing data is also defined here, when referencing the call to change data behavior, rather than directly modify internal data violence.

Some of the functional logic in the project is used globally and in many places, so the logic can be isolated into the global positionAPI. For these logic files I will create a hooks folder under SRC/along with the global component Components. Which is why the folder is called hooks from last year when I wrote React. On the other hand, CompositionAPI is too long. If use is not clear, the naming problem depends on your own habits.

I’m using global logic from one of my projects as an example to help you understand.

In terms of global public logic, as OA system, there are many functional modules in the project with addition and editing. However, our company uses popboxes to carry this part of form form.

Here you can extract the common logic: 1. Open the frame 2. Close the frame 3. Clear the form data before closing the popup. 4. Open the edit popup and assign the initial value

These functions may not have much code, but because they are common, they can be extracted, because it will be easy to maintain later, and although the code is small, it is really annoying to write them every time.

Here’s an extra note: why doesn’t this cartridge pull apart into components? Because we said that if you have the same UI structure, you can separate it into components.

Here’s the way I think about it: if you split it as a component, you can say that the UI elements of the popbox need to be determined at use — title, width and height, button position and text, style inside the popbox, and so on. When you think about it this way, if I extract a component, it doesn’t look any different than if I used element-Plus directly, so I’ll just extract the logic and reuse it

Here is a detailed explanation

// The convenience of vue3 is here. I can easily import the API I want to use without having to use a vue file.
import { ref, watchEffect } from 'vue'
import _ from 'lodash'

// The whole export is a function, which is usually a single function while maintaining its own internal data
// formData is the data source of the current form binding, editFormData is the initial value of the form obtained during editing, and formRef is the ref object of the form in the dialog, which can be used to empty the form and remove validation results.
export default function useDialogForm({ formData, formRef, editFormData } = {}) {
  // The variables I define here are defined inside the function, and some are defined outside the function. Syntactically speaking, it is ok, but it depends on your own needs and then decide where to define them.
  // I use this function to share data if defined externally. That is, if I have two pop-ups on this page, it's not right if they share whether to display this variable.
  const dialogFlag = ref(false)

  function openDialog() {
    dialogFlag.value = true
  }

  function closeDialog() {
    formRef.value.resetFields()
    dialogFlag.value = false
  }

  function beforeClose(done) {
    done()
    formRef.value.resetFields()
  }

  function openEditDialog() {
    dialogFlag.value = true
    nextTick(() = > {
      formData.value = _.cloneDeep(editFormData.value)
    })
  }

  return {
    dialogFlag,
    openDialog,
    closeDialog,
    beforeClose,
    openEditDialog
  }
}

Copy the code

So that part of the logic is pulled away. If a page needs a popbox, you can use it like this:

<template>
  <el-button type="primary" @click="openDialog">new</el-button>
  <el-button type="text" @click="Edit (Current row data)">The editor</el-button>
  <el-dialog v-model="dialogFlag" title="xxx" width="60%" :before-close="beforeClose">
     <el-form ref="addCityRef" :model="form" label-width="140px">.</el-form>
   </el-dialog>
</template>

<script>
import useDialogForm from '@/hooks/useDialogForm'

setup() {
  const {dialogFlag, openDialog, closeDialog, openEditDialog} = useDialogForm()

  const editData = ref({})
  function edit(data) {
 	editData.value = data
  }

  return {
    dialogFlag,
    openDialog,
    closeDialog,
    beforeClose,
    openEditDialog,
    edit
  }
}
</script>
Copy the code

Logical separation within components

Hooks in components are mostly for extracting different logic for separate management. Although they can be reused, they may not be reused in practice. Take the following very common page as an example, provided that nothing is pulled out of the component

Normal page logic I will directly create a new file under the current folder and then import, here if it is not clear, you can create a hook folder under the current folder.

This is to illustrate how the hooks in our component should be considered to extract different logic. So just to make it easier to accept the following, remember the premise: everything here is not separated into components.

The first impression you get when you see the red box is that it’s all one thing — showing the data. And then it’s gone.

It seems that there is no decomposition, in fact, if you think about it carefully, you can find that there is a difference between them: tables capture and display data, and pages describe data range.

A simple way to think about it is: If I take out a part of it, will the rest work? According to the above analysis, the two are indeed separable. Tables can display and retrieve data by themselves without pagination; Capturing a range of data does not have to be presented in a table.

Now let’s look at the code and see how to do the extraction, pseudo code.

Form part of theUseGetTableList. Js:

export default function useGetTableList() {
  const loading = ref(false)

  // tableData is a tableData source, so why did I use ref as a complex type
  const tableData = ref([])

  // PageParams is the paging parameter, and searchData is the other parameter needed to call the interface, such as the search item at the top of the table that you filled in
  async function getList({pageParams = {}, searchData = {}} = {}) {
  
  // data is the argument that is actually needed to call the interface
  constdata = { ... pageParams, ... searchData }const res = await xxx(data)
  loading.value = false
  if(! res || res.data)return
  Reactive (ref = xx) reactive (ref = xx) reactive (ref = xx)
  Tabledata. name = xx, tabledata. age = xx, tabledata. age = xx, tabledata. name = xx, tabledata. age = xx.
  tableData.value = res.data
   ...
  }
  return {
    loading,
    getList,
    tableData
  }
}
    
// If there is anything else that can be extracted from the above code
Copy the code

pagingusePageParams.js

export default function usePageParams() {
  const currentPage = ref(1)
  const currentSize = ref(10)
  const pageParams = computed(() = > ({
    page: current.value,
    size: current.value
  }))

  // The number of entries has changed
  function sizeChange(val) {
    currentSize.value = val
	// This may be confusing: I have changed the pagination and generally need to call the interface, how to call here, don't worry, call the interface is not written here, continue to read.
  }

  // The current page number has changed
  function currentChange(val) {
    currentPage.value = val
  }
    
  return {
    sizeChange,
    currentChange,
    currentPage,
    currentSize
  }
}
Copy the code

Specific Function pageindex.vue

<template>
 <el-table
    v-loading="loading"
    :data="tableData">.</el-table>

 <el-pagination
   :current-page="currentPage"
   :page-sizes="[5, 10, 20, 50]"
   :page-size="currentSize"
   layout="total, sizes, prev, pager, next, jumper"
   :total="total"
   @size-change="handleSizeChange"
   @current-change="handleCurrentChange"
 />
</template>

<script>
import useGetTableList from './useGetTableList'
import usePageParams from './usePageParams'
export default {
  setup() {
    / / get loading, getList tableData
    const {loading,getList,tableData} = useGetTableList()
    // Get sizeChange, currentChange, currentSize, currentPage, pageParams
    const { 
        sizeChange, 
        currentChange, 
        currentSize, 
        currentPage, 
        pageParams 
    } = usePageParams()

    // The number of entries has changed
    function handleSizeChange(val) {
      // Call sizeChange in usePageParams to change currentSize
      sizeChange(val)
        
      // Call the useGetTableList call interface method and pass in the page number argument after the page change
      getList({ pageParams: pageParams.value })
    }
      
    // The current page number has changed
    function handleCurrentChange(val) {
      currentChange(val)
      getList({ pageParams: pageParams.value })
    }
      
    return{... I'm not going to write it here. }}}</script>
Copy the code

Unfinished TypeScript

I’m sorry I haven’t joined this section yet, so feel free to leave a comment if you have a good source on vue3+typescript

conclusion

This is the end of vue3 CompositionAPI I think is still very useful and has the potential, I hope you can have some harvest. Sharing this summary is also hoped to inspire others, if you have good ideas and suggestions, please feel free to comment