Using Vue3 Usage Summary in a new project

Posted by TGWSE_GY on Wed, 09 Feb 2022 20:40:31 +0100

1, Use background

Recently, the company needs to build a new project for its official website. Because as the official website, the first project is not large, there are only a dozen pages in total, and you want a good user experience, so you finally choose Vue as the technology stack.

Although Vue3( Chinese official website )It has just come out, but if Vue2 is used to build the project, it may face the problem of Vue version upgrade in the future. There are many incompatible changes from Vue2 to Vue3. With the development of the times, the use of new technologies will be more maintainable and scalable.

2, Project construction

What I use here is vue cli4 Carry out project construction

3, Installation dependency

Install some common dependencies. It is worth noting that some plug-ins of Vue3 are different from Vue2.

Special note:

  • element-plus : This is a desktop component library based on Vue 3.0.

    How to use element plus globally:

// plugins/element.js
import ElementPlus from 'element-plus'
import 'element-plus/lib/theme-chalk/index.css'
import 'dayjs/locale/zh-cn'
import locale from 'element-plus/lib/locale/lang/zh-cn'

export default (app) => {
    app.use(ElementPlus, { locale })
}

// main.js
import installElementPlus from './plugins/element'

const app = createApp(App)
installElementPlus(app)

// use
<template>
    <el-button>Button</el-button>
</template>
// Method 1: global mount. This method is not suitable for putting the requests of different modules in the project into corresponding files,
// Because we may not be able to get the current instance this when making a request
// plugins/axios.js
export const plugin = {
    install: function (app, options) {
        console.log(options)
        // Add global method
        app.config.globalProperties.axios = _axios;
    }
}

// main.js
import { plugin as axios } from './plugins/axios'
const app = createApp(App)
app.use(axios)

//use:
this.$axios.post('api/Login',{card:111}).then(res => console.log(res))


//Mode 2: direct use
// plugins/axios.js
const _axios = axios.create(config)
export default _axios

// use:
import axios from '@/plugins/axios'

// Get position category
export function getJobCategories() {
    return axios.post('api/Login',{card:111})
}
  • Vue Router : Vue router4 also provides hook hook functions for use.

    Using router in setup
    import { useRouter } from "vue-router"
    const router = useRouter()
    router.push('/')
    

4, What's new in Vue3

1. Combined API

What is a composite API?

To spread vue functions in a file is simply to put them together

This will make our code more clustered. Students who have used react functional components should find that they are very similar.

  • Setup: setup is the entry point of the composite api. All functions can be written in setup. The parameters that need to be used outside setup are exposed through return

    setup receives two parameters: props and context.

    Props accepts the parameters passed from the parent component. Of course, these parameters are also responsive. It is worth noting that when props is used in setup, the variables defined in props can only be received by props in setup. When obtaining the variables of props in setup, the deconstruction method of es6 can not be used, because it will eliminate the responsiveness of props. If it is necessary to deconstruct props and preserve the response formula, the toRefs function in setup function can be used.


context exposes the properties of three components: attrs (Attribute), slots (slot) and emit (trigger event). context is an ordinary JavaScript object, which does not have a response, and can use es6 deconstruction syntax.

be careful ⚠️

Setup is called before parsing other component options, so the current component instance: this cannot be accessed accurately in setup.

To get the current component instance in setup, the official provides a function: getCurrentInstance, but getCurrentInstance can only be used in setup and life hook functions

// getCurrentInstance represents the global context. ctx is equivalent to this of Vue2,
// But pay special attention that ctx instead of this is only applicable to the development stage. When you put it on the server, you will make an error,
// Later, according to the information, proxy must be used to replace ctx in order to run normally in the officially launched version of your project

 const { ctx, proxy } = getCurrentInstance()
  • Life cycle hook
  • Provide / Inject: parent-child components transmit data across levels
// Parent component
setup () {
	// After the readonly package is wrapped, it can be referenced in the component without changing the value.
	// Otherwise, in the component, you can directly use applypositionid Value = * * * change value
	provide('applyPositionId', readonly(applyPositionId))
	
	// Functions can also be passed
	provide('hideApplyModal', hideApplyModal)
}

//Sub component reception
setup() {
	const applyPositionId = inject('applyPositionId')
    const hideApplyModal = inject('hideApplyModal')
    
	// function call
	hideApplyModal()
}

2. Responsiveness API

In Vue2, the data defined in data will be used by object Defineproperty () hijacks data and becomes responsive data through observer mode.
In Vue3, the way to realize the response is changed to the new syntax proxy in es6, and the response is realized through object interception, which solves some leakage areas in vue2.
There is a lot of relevant knowledge about these two points. I will write a new article to explain them accordingly. Here we mainly introduce the use of some new features of Vue3.

  • reactive

    reactive is used to create a responsive state for an object, which is equivalent to Vue 2 Vue in X observable(). After creating a responsive state for an object, there will be no problem of modifying the attributes of an object that are not defined when data is defined in Vue2, and there will be no problem of responsive state.

    setup() {
    	const obj = { count: 0}
    	const state = reactive(obj)
    	console.log(state.count)  // 0
    }
    

    There is a sentence on the official website:

    Responsive transformation is "deep" - it affects all nested properties. What does that mean?

    When we use proxy (those who don't know proxy can read: Ruan Yifeng es6 beginner level )When doing object interception, he will hijack all property properties of this object.

    vue2 uses Vue Observable () enables an object to respond, and the incoming object will be directly Vue Observable is changed to responsive. reactive is to create a responsive copy of the incoming object without changing the original object. When we directly change the original object, the original object will not be responsive.

  • readonly

    readonly gets an object (reactive or pure) or ref and returns the read-only proxy of the original proxy

     const state = reactive({ count: 0 })
     const copy = readonly(state)
     
     //error
     copy.count++
    
  • ref

    This ref is not that Ref. both Vue2 and react have the concept of Ref. When its scope is HTML file, ref is the interface provided by React/Vue to manipulate the instance of React/Vue component or DOM element.

    In the responsiveness API, ref is used to create a responsive and variable ref object, which has a pointer to the variable it creates Value attribute. When return ed in setup, its value will be bound automatically.

    setup() {
    	const count = ref(0)
    	console.log(count.value)  // 0
    
    	return {
    		count
    	}
    }
    mounted() {
    	console.log(count)  // 0
    }
    

    There is a sentence on the official website:

    After reading it for a long time, I don't particularly understand what it means. After using ref and reactive, it is found that they are not much different except in the way they are used. Highly responsive thoughts are still not realized. Through the source code, I can see that when using ref to create responsive data for an object, the reactive method is also called internally.


    If you have any understanding, you can comment and let me know. 🙏🙏

  • computed

    The use of computed is very simple. In essence, it is not much different from vue2

  • watch

    This is equivalent to vue2$ Watch, which can listen to one or more sources. Execute when the dependent value changes.

    	// Listen for a getter
        const state = reactive({ count: 0 })
        watch(
          () => state.count,
          (newcount, prevCount) => {
            console.log('New value:', newcount)
            console.log('Old value:', prevCount)
          }
        )
    
        // Listen directly to a ref
        const count = ref(0)
        watch(count, (newcount, prevCount) => {
            console.log('New value:', newcount)
            console.log('Old value:', prevCount)
        })
    
        // Listen to multiple sources, using array form
        const fooRef = ref(0)
        const barRef = ref(1)
        watch([fooRef, barRef], ([newFoo, newBar], [prevFoo, prevBar]) => {
            console.log('new Foo value:', newFoo)
            console.log('used Foo value:', prevFoo)
    
            console.log('new Bar value:', newBar)
            console.log('used Bar value:', prevBar)
    
        })
    
  • watchEffect

    watchEffect runs a function immediately when it responsively tracks its dependencies and reruns it when the dependencies are changed: it means that the page will be executed once at initialization and again when the dependencies are changed. Students who are familiar with React Hook will be very familiar with it. It has the same function as useEffect. The only difference is that useEffect requires us to manually pass in dependencies, while watchEffect will automatically collect dependencies.

    const count = ref(0)
    
    watchEffect(() => console.log('value:', count.value))
    // value:0
    
    setTimeout(() => {
      count.value++
      // value:1
    }, 100)
    
    

4, Incompatible changes

Vue3 there are many incompatible changes on Vue2. Here are some common attributes.

  • v-model

    The v-model in Vue2 is equivalent to the syntax sugar of: value + input.
    The v-model in Vue3 is equivalent to: modelValue + update:modelValue.

    // Vue2
    <ChildComponent v-model="pageTitle" />
    
    <!-- Equivalent to: -->
    <ChildComponent :value="pageTitle" @input="pageTitle = $event" />
    
    
    // Vue3
    <ChildComponent v-model="pageTitle" />
    
    <!-- Equivalent to: -->
    <ChildComponent :modelValue="pageTitle" @update:modelValue="pageTitle = $event" />
    

    In Vue2, if we want to change the value passed down from v-model, we need the model option to change it.

    // parentComponent
    <ChildComponent v-model="pageTitle" />
    
    // ChildComponent
    export default {
      model: {
        prop: 'title',
        event: 'change'
      },
      props: {
        // This will allow the 'value' attribute to be used for other purposes
        value: String,
        // Use 'title' instead of 'value' as the prop of the model
        title: {
          type: String,
          default: 'Default title'
        }
      }
    }
    
    // perhaps
    <ChildComponent :title="pageTitle" @update:title="pageTitle = $event" />
    // perhaps
    <ChildComponent :title.sync="pageTitle" />
    
    // Subcomponents
    this.$emit('update:title', 'new title')
    

    In Vue3, V - bind is removed Sync modifier. The above functions are abbreviated as:

    <ChildComponent v-model:title="pageTitle" />
    // Equivalent to
    <ChildComponent :title="pageTitle" @update:title="pageTitle = $event" />
    
    Sub components:
    	setup(props, context) {
            const { emit } = context || {}
    
            const onClose =() => {
                emit('update:pageTitle', 'new title')
            }
    
            return {
                onClose,
            }
        }
    
  • v-if and v-for

    In Vue2, when v-if and v-for act on the same DOM node at the same time, v-for takes precedence over v-if.
    In Vue3, when v-if and v-for act on the same DOM node at the same time, the priority of v-if is higher than that of v-for.
    And the key attribute will continue to be used in the branches of v-if/v-else/v-else-if, because when no key is provided for the conditional branch, a unique key will be automatically generated.

  • filters

    filters in Vue3 have been completely removed. It is recommended to use computed instead.

  • template

    In Vue2, only one root node is supported in the template, otherwise an error will be reported.
    In Vue3, the template only supports multiple root nodes.

  • prop

    In Vue2, we need to access the fields in data in prop. You can use this.
    In Vue3, this is no longer supported in prop.

4, Customize Hook

  • useFetch

    import { reactive, toRefs } from 'vue'
    
    export default function useFetch(api, params, transformer) {
        if (typeof api !== 'function') {
            throw new TypeError('api should be type of fuction')
        }
        // Define initial state
        const state = reactive({
            data: null,
            loading: false,
        })
        // Define query function
        const doFetch = (otherParams) => {
            const finalParams = {
                ...(params || {}),
                ...(otherParams || {})
            }
            state.loading = true
            return api(finalParams).then((data) => {
                state.data = typeof transformer === 'function' ? transformer(data) : data
                state.loading = false
                return data
            })
                .catch((err) => {
                    console.log(err && err.message)
                    state.loading = false
                })
        }
        // Return status refs and query function
        return [toRefs(state), doFetch]
    }
    
    // use
    import useListFetch from '@/hooks/useListFetch'
    
    setup() {
    	const getdataFunc = (params) => {
    		const { id } = params || {}
    		return axios.get(`/xxx?id=${id}`)
    	}
    
    	const [
            { data, loading: isLoading},
            getData,
      	] = useListFetch(getdataFunc, {
            id: 10,
        })
    	
    	getData()
    }
    
  • useListFetch

    import { reactive, toRefs } from 'vue'
    
    export default function useListFetch(api, params, transformer) {
        if (typeof api !== 'function') {
            throw new TypeError('api should be type of fuction')
        }
        // Define the initial state of the list
        const state = reactive({
            items: [],
            loading: false,
            isLastPage: false,
        })
        const { pageSize = 10, ...otherParams } = params || {}
        // Define query function
        const doFetch = () => {
            const preSize = state.items.length
            const finalParams = {
                ...otherParams,
                offset: preSize,
                limit: pageSize,
            }
            state.loading = true
            return api(finalParams).then((data) => {
                if (data && Array.isArray(data)) {
                    const newData = typeof transformer === 'function' ?  transformer(data) : data
                    const newItems = [...state.items, ...newData]
                    state.items = newItems
                    if (newItems.length !== preSize + pageSize) {
                        state.isLastPage = true
                    }
                }
                state.loading = false
                return data
            })
                .catch((err) => {
                    console.log(err && err.message)
                    state.loading = false
                })
        }
        // Return status refs and query function
        return [toRefs(state), doFetch]
    }
    
    // 	use
    import useListFetch from '@/hooks/useListFetch'
    
    setup() {
    	const getListFunc = (params) => {
        	const { offset = 0, limit = 10 } = params || {}
        	return axios.get(`/xxx?offset=${offset}&limit=${limit}`)
    	}
    
    	const [
            { items: videoList, loading: isLoading, isLastPage: isLastPage },
            getList,
        ] = useListFetch(getListFunc, {
            pageSize: 10,
        })
    
        getList()
    }
    

5, Project packaging

Reference documents: https://cli.vuejs.org/zh/guide/

These are some important changes when I use Vue3.

Topics: Vue Framework vue-cli4