# C_T7.1 code review ## Code review by 阿傑 ### function 不 pure 的問題 原本寫的 ```javascript // taskStore.js async function postTask(task) { // 關掉add task表單 const globalState = useGlobalStateStore() globalState.isAddingTask = !globalState.isAddingTask const payload = { records: [ { fields: task, }, ], } await airtable.POST('/tblBEw21vjxZX7CxR', payload) fetchTasks() } ``` 改把邏輯放在頁面檔案裡面 ```javascript // MyTask.vue function postTasks(tasks) { globalState.isAddingTask = !globalState.isAddingTask taskStore.postTask(tasks) } ``` 另一個 原本寫的 ```javascript async function fetchTasks() { const fetchedData = await airtable.GET('/tblBEw21vjxZX7CxR') originalTasks.value = fetchedData.records tasks.value = formattedTasks.value sortTasks() } // 裡面還有賦值,嚴重的副作用 // sort還會動到formattedTasks function sortTasks(tasks) { tasks.value = formattedTasks.value.sort((taskA, taskB) => taskB.starred - taskA.starred) tasks.value = formattedTasks.value.sort((taskA, taskB) => taskA.completed - taskB.completed) } ``` 改成 ```javascript async function fetchTasks() { const fetchedData = await airtable.GET('/tblBEw21vjxZX7CxR') originalTasks.value = fetchedData.records tasks.value = sortTasks(formattedTasks.value) } function sortTasks(tasks) { return [...tasks] .sort((taskA, taskB) => taskB.starred - taskA.starred) .sort((taskA, taskB) => taskA.completed - taskB.completed) } ``` ### 過多的判斷邏輯 原本寫的 ```html <component v-if="props.status === 'in-progress'" v-for="task in taskStore.inProgressTasks" // 這行的邏輯 :is="task.editing ? TaskForm : TaskAbstract" :key="task.id" :task="task" @submit="taskStore.updateTask($event)" @change:status="taskStore.updateTask($event)" /> ``` 改成把 TaskForm 和 TaskAbstract 再包成一個 component ```html <TaskItem v-if="props.status === 'my-task'" v-for="task in taskStore.tasks" :task="task" :isEditing="task.editing" :key="task.id" @submit="taskStore.updateTask($event)" @change:status="taskStore.updateTask($event)" /> ``` 這樣還可以把 TaskForm 和 TaskAbstract 拆開寫 ```html // TaskItem.vue <template> <TaskForm v-if="isEditing" :task="props.task" @submit="emits('submit', $event)"></TaskForm> <TaskAbstract v-else :task="props.task" @change:status="emits('change:status', $event)" ></TaskAbstract> </template> ``` ### vite.config.js 引入 scss 檔案 可以新增一個`index.scss`用`@forward`方式引入所有abstracts ![image](https://hackmd.io/_uploads/HJ1VloEKkl.png) 在`vite.config.js`直接`@use`這個資料夾,並拿掉namespace ![image](https://hackmd.io/_uploads/HylYeoEFJe.png) 元件內就可以直接使用變數了 ```css .input { background-color: $primary_white; border: 2px solid $primary_gray; border-radius: 5px; } ``` ### 邏輯再再再簡化 原本依照status來判斷要渲染哪一個元件 ```html // 當status等於all <TaskItem v-if="props.status === 'all'" v-for="task in taskStore.tasks" :task="element" @submit="taskStore.updateTask($event)" @change:status="taskStore.updateTask($event)" /> // 當status等於in-progress <TaskItem v-if="props.status === 'in-progress'" v-for="task in taskStore.inProgressTasks" :task="element" @submit="taskStore.updateTask($event)" @change:status="taskStore.updateTask($event)" /> // 當status等於completed <TaskItem v-if="props.status === 'completed'" v-for="task in taskStore.completedTasks" :task="element" @submit="taskStore.updateTask($event)" @change:status="taskStore.updateTask($event)" /> ``` 改成直接在v-for中渲染相對應的Array ```html <TaskItem v-for="task in taskItems" :task="element" @submit="taskStore.updateTask($event)" @change:status="taskStore.updateTask($event)" /> ``` ```javascript // <script setup> // early return的話盡量不使用else if的寫法 const taskItems = computed(() => { if (props.status === 'in-progress') { return taskStore.inProgressTasks } if (props.status === 'completed') { return taskStore.completedTasks } }) // 甚至可以改成多層的三元運算子 const taskItems = computed(() => props.status === 'in-progress' ? taskStore.inProgressTasks : props.status === 'completed' ? taskStore.completedTasks : taskStore.tasks ) ``` ### defineModel 超難懂QQ 可以不用一開始就寫出來,但如果同事這樣寫,至少要看得懂 原本傳入了task,然後emits兩個events: `submit`和`change:status` ```html // MyTaskView.vue <TaskItem v-for="task in taskStore.tasks" :task="task" @submit="taskStore.updateTask($event)" @change:status="taskStore.updateTask($event)" /> ``` ```html // TaskItem.vue <script setup> const props = defineProps({ task: { type: Object, required: true, }, }) const emits = defineEmits(['submit', 'change:status']) </script> <template> <TaskForm :task="props.task" @submit="emits('submit', $event)" @change:status="emits('change:status', $event)" ></TaskForm> </template> ``` 改成用defineModel的方式,把`task`作為一個像是v-model的modelValue來傳遞和更新 ```html // MyTaskView.vue // 使用task這個名稱傳入內容 // 使用update:前綴加上task名稱作為event名字 <TaskItem v-for="task in taskStore.tasks" :task="task" @update:task="taskStore.updateTask($event)" /> ``` 可以把`task`直接當成ref的感覺來取用或賦值 ```html // TaskItem.vue <script setup> // 不用再傳props和emits // 定義名稱為task const task = defineModel('task') </script> <template> <div> <TaskForm v-if="task.editing" :task="task" @submit="task = $event" @change:status="task = $event" ></TaskForm> </div> </template> ``` ### Lazy Loading Routes 阿傑說都會用動態引入頁面檔 ```javascript routes: [ { path: '/', name: 'my-task', component: () => import('../views/MyTask.vue'), // 動態引入 props: (route) => ({ status: route.query.status || 'all' }), }, ], ```