Vue3 has been updated to 3.2, and I think vuers should know something about VUe3, especially its composite API. In this article, we will discuss what constitutes best practice for Vue3 composite apis through the author’s recent refactoring practices.
Start with a login screen
I had a previous vuE2 project that used vUE buckets, especially the Vuex. I’m going to take one of the simplest modules from this project and talk about the optimizations and refactorings I’m going to do in the near future, and talk about them with you.
Here is the login screen, I randomly found a web map, simple to say is an input field and a button.
But I probably had this logic in my old project, style wasn’t the main issue, so I ignored it.
// login.vue <template> <div class="flex flex-col justify-center items-center"> <input ref="input" :value="usename" @change="changeUsername" @click="callFocus"> <button ref="btn" @click="loginByUsername">log in</button> </div> </template> <script> import {mapActions} from "vuex" import {Toast} from 'vant' export default { name: "LoginByName", data () { return { username:'' } }, Mounted () {const scrollIntoView = () => setTimeout(() =>{this.$refs.btn.scrollinToView (false)},10) } this.$refs.input.addEventListener('focus',scrollIntoView) this.$once('hook:beforeDestroy',()=>{ this.$refs.input.removeEventListener('focus',scrollIntoView) }) this.trackByAdjust('some_event') this.trackByTa('some_event') }, methods: { ... mapActions(['trackByAdjust','getUserInfo','trackByTa']), ChangeUsername (e) {/ / fore and aft to Spaces to lowercase enclosing the username = e. arget. Value. The replace (/ ^ \ s + | \ s + $/ gm, ' '). The toLowerCase ()}, CallFocus (){// Fix iOS occasionally clicking without focusing this.$refs.input.focus()}, Async loginByUsername(){if(this.username === ""){return Toast('username is required')} / ^ (? ! . *..). (? ! . *. $) [^ \ W] [\ w.] 29 {0} $/. The test (enclosing the username)) {enclosing showWrongName = true return} TrackByTa (' XXX ',{login_click_position:'username'}) await this.getUserInfo(this.username)}} </script>Copy the code
From my logic here, it can be seen that it is roughly divided into two parts: logon logic, and page display input logic. Since the logon data is shared across the SPA, the logon request interface is placed in the VUEX, which is similar to this function.
// api/index.js import axios from 'axios' import md5 from 'crypto-js/md5' import enc_hex from 'crypto-js/enc-hex' export Const getData(username){// Const ts = new Date().gettime () const secret = md5(username+ts).tostring (enc_hex).slice(0,10) return axios({ method:'get', url:'xxx.com/api/login', params:{ts,secret} }) }Copy the code
// store/action.js import {getData} from '@/api' export default { async getUserInfo({state,commit,dispatch},username){ Commit ('resetUser') const res = await getData(username) if(res.data.code === 200){if(state.xxx){commit('resetUser') const res = await getData(username) if(res.data.code === 200){if(state.xxx){ Commit (' XXX ',res.xx)} else {commit('aaa',res.xxx)} return res.data} else {// login failed return false}}}Copy the code
There is also a bunch of logic in mutation, which I will ignore to save space. Even now, it’s really just a login interface with a lot of code, and it’s scattered.
Imagine that the encryption method of the interface has changed, and the return value has a new field, which needs to be displayed on the interface. How do I change it?
- To modify the
/api/index.js
Call axios code to modify the encryption method. - Modify the
/store/action.js
- Modify the
/store/mutation.js
- Modify the
/store/state.js
- May need to be modified
utils/index.js
- Reopening the component
login.vue
, modifyloginByUsername
This function also hastemplate
Display in.
As you add functionality, each change becomes extremely complex, and complexity means error.
Yes, the structure is basically a management template for floral shorts, but for heavy Vuex users, I don’t think the structure is too bad.
Vue3 refactoring scenario
This time I had some time to refactor the whole project with Vue3, so I’ve been thinking about how to use composite API recently. Last night I started to write, and it occurred to me that composite API is all about integrating logic, by function or by separation of concerns.
When I think of the focus as the entire login process, then I can put the entire logic into a function
import axios from 'axios' import md5 from 'crypto-js/md5' import enc_hex from 'crypto-js/enc-hex' import useAppStore from '.. / / / store 'pinia import {useTaTrack} from' @ / composables useNameLogin 'const getData (username) {/ / there will be encrypted interface parameters, Const ts = new Date().gettime () const secret = md5(username+ts).tostring (enc_hex).slice(0,10) return axios({ method:'get', url:'xxx.com/api/login', params:{ts,secret} }) } export default async (username) => { if(username === ''){ return {code:400,msg:'username is Required '}} // Check if(! / ^ (? ! . *..). (? ! .*.$)[^\W][\ W.]{0,29}$/.test(this.username)){this.showname = true return} const res = await If (res.data,code === 200){useTaTrack('login_success') // Returns the value to store for use by other component pages $patch({userInfo:res.data}) const store = useAppStore() store.$patch({userInfo:res.data}) return res.data} return false}Copy the code
So when I use the function above, login.vue looks like this.
// login.vue <template> <div class="flex flex-col justify-center items-center"> <input ref="input" :value="usename" @change="changeUsername(e)" @click="callFocus"> <button ref="btn" @click="handleClick">log in</button> </div> </template> <script> setup> import {ref,onMounted,onBeforeUnmount,unref} from 'vue' import useNameLogin from '@ / composables useNameLogin import {useAdjust, useTaTrack} from' @ / composables useTrack/dot/events const input = ref (null) $refs const BTN = ref(null) const username = ref(") const changeUsername = (e)=>{if(e.troge.value! ==username.value){ username.value = e.target.value.toLowerCase() } } const handleClick = async ()=>{ useTaTrack('click_login_button',{login_click_position:'username'}) const res = await useNameLogin(unref(username)) if(res){ username.value = '' // do sth } } const scrollIntoView = () => { setTimeout(() =>{ btn.value && btn.value.scrollIntoView(false) },100) } onMounted(() => { useAdjust('some_event') useTaTrack('some_event') input.value.addEventListener('focus',scrollIntoView) }) onBeforeUnmount(()=>{ input.value.removeEventListener('focus',scrollIntoView) }) </script>Copy the code
If I were doing this now, the requirement mentioned above: the interface encryption has changed during login, and the return value has a new field that needs to be displayed on the interface.
I just need to
- Modify the
composables/useNameLogin.js
Correlation logic in - Modify the
store/index.js
Here is the store created by Pinia - May need to be modified
utils/index.js
- Modify the
login.vue
To display the associated logic
From this simple example, the amount of code for the component is much reduced and the overall structure is much simpler. Also, the next time I add a button, it’s just a file in the Composables folder, a function, and a bind to the button on the interface.
advantage
- High cohesion, a function from start to finish in a file, reducing the number of changes to the file.
- The new functionality is to create a new function and reduce the amount of logic to operate elsewhere
- Use setup as much as possible on the page, reducing the amount of code and eliminating the need to jump around the page
disadvantage
- The structure is confusing. I used to look for interfaces in the API folder, but now THE API is scattered in various composite apis
- It is not completely decoupled and may have deeper reference levels. For example, one composable refers to another
- Easy to produce spaghetti code
Conclusion: This article is a reflection and review of what I wrote about VUe3 last night. I also want to discuss with you whether it is good for me to use the composite API in this way.