做了這么多年前端,為什么你還是不會寫業務代碼?


  我在平時工作中也會負責一些代碼審查的工作,做的多了,就發現了一個問題:大部分程序員習慣把所有的邏輯都寫在vue文件里,所以這就導致一個問題,也就是你項目中的.vue文件的代碼會巨多,相反,你的js/ts文件中的代碼會沒有那么多。如果是一個小型項目當然還好,如果是一個大型的項目,這可能就是維護人員的噩夢了,因為后續的維護成本是巨大的。至於為什么,相信你看完這篇博客就能懂我的意思了。

  今天有時間靜下心可以寫一篇博客,總結了一下日常審過的代碼,我們就用這些代碼當作案例,分析一下我們以后的coding生涯該如何去避免這些問題。

  首先我們想思考一個問題:.vue文件到底是用來干嘛用的?

  我們可以看一下vue官網是如何介紹這款框架的:

image

  這個視圖層也就vm,說白了就是我們寫的頁面。

  那么頁面中應該負責干什么呢?無非就是用htmlcss代碼搭建頁面結構,然后頁面中可以能有一些元素的事件,比如說按鈕的點擊事件,表單的查詢,表格的請求數據的動作等等。

  所以綜上所述我們可以總結一句話: .vue文件中包含一些頁面結構以及頁面元素動作的發起。

  那么,動作的發起者有了,動作的執行者是誰呢?

  我們看一些我們平常寫的項目結構是啥樣的,拿一個ts項目為例:

image

  這個是src下的目錄結構,應該是分的比較詳細的,各個文件夾有不同的作用,這也符合單一職責。

  我們在回過頭來看一些誰能當作動作的執行者呢?api只負責調用接口,他拿到數據后就拋出了,數據這時候已經流向別處了,assets只放靜態資源,這兩個顯然不能。componentpublic_componentinstancemixinviews全是用來搭建頁面的,mixin雖然是ts文件,但是他的定義也是可以分發Vue組件中的可復用功能,這些顯然也是不行的。interfaceutils這些是抽象出來的東西,顯然是不能放業務代碼的。store現在已經不被大家看好了,因為vuex中的通信實在是太繁瑣了,目前我們的項目只用來放一些字典數據用來緩存。還剩下routersmiddlewarestyle這三個顯然不是干這個用的。最后就只剩下domain了。

  domain(也有叫service的)作為業務層,我們給他最初的定義就是寫一些業務代碼,按鈕的點擊事件,頁面的滾動事件顯然不是業務代碼,這些可以負責調用業務代碼,也就是我們上面說的動作的發起者。這時候業務層的作用就來了,就是充當動作的執行者。設想一下,我點擊一個按鈕去請求一個表格數據,那么按鈕上肯定綁定一個click事件,點擊完成之后就應該調接口了,后續的事情就可以交給業務層了,由業務層完成后續的邏輯,比如我拿數據需要調用api層先請求一下后台數據,拿到數據看一下有沒有請求成功,數據是不是我們想要的格式,處理好時候就可以交給頁面層渲染了。

  好,我看一下實際的代碼表現是什么樣的。

  先說一個簡單需求,我們希望一個表格內展示一些用戶數據,看一下一般的程序員是怎么做的:

數據結構:

    // 表格數據
    let tableData = [
        {
            date: "2016-05-02",
            name: "王小虎",
            address: "上海市普陀區金沙江路 1518 弄",
            phone: "13033443344",
            love: [
                '吃飯',
                '睡覺',
                '寫bug'
            ]
        },
         {
            date: "2016-05-02",
            name: "王小虎",
            address: "上海市普陀區金沙江路 1518 弄",
            phone: "13033443344",
            love: [
                '吃飯',
                '睡覺',
                '寫bug'
            ]
        }
        ...
    ]

   // 表頭數據

   let headData = [
        {
            label: "日期",
            prop: "date",
            width: "180"
        },
        {
            label: "姓名",
            prop: "name",
            width: "180"
        }
        ....
   ]

.vue文件

<template>
    <div class="preivew-page">
        <el-table :data="tableData" style="width: 80%">
            <el-table-column v-for="(item, index) in headList" 
                        :key=index 
                        :prop="item.prop" 
                        :label="item.label" 
                        :width="item.width">
                <template slot-scope="scope" >
                    <span v-if="item.prop=='job'">{{scope.row.jobInfo.job}}</span>
                    <span v-else-if="item.prop=='love'">{{scope.row.love.join('-')}}</span>
                    <span v-else>{{scope.row[item.prop]}}</span>
                </template>
            </el-table-column>
        </el-table>
    </div>
</template>
<script lang='ts'>
import { Vue, Component } from "vue-property-decorator";
import { TableList } from "@/service/index";


@Component
export default class PreviewPage extends Vue {
    // 定義表格數據
    private tableData: any = [];
    // 定義表頭數據
    private headList: any = [];
    async mounted () {
        // 請求表格數據
        let tableList = new TableList();
        this.tableData = await tableList.searchTableData()
        this.headList = await tableList.searchHeadData()
    }
}
</script>

service業務層

import { getTableData, getHeadData } from "@/api/index"

export class TableList {
    public async searchTableData () {
        let data = await getTableData();
        return data;
    }

    public async searchHeadData () {
        let data = await getHeadData();
        return data;
    }
}

最終效果

image

  我們可以看出這里的代碼大部分寫在了.vue文件中,里邊包含了一些判斷和數據處理,而且業務層文件基本沒發揮它的作用,只是簡單的調用了接口而已,我們知道一個類最基本的就是要構造他,才能體現它的多態,這里只是一個頁面有表格業務操作,如果我們有很多個表格呢,是不是就會有很多個業務文件?

  如果我們按照我的說法,把業務代碼不寫在頁面而是寫在service中,看看是什么效果:

.vue文件

<template>
    <div class="preivew-page">
        <el-table :data="tableList.tableList" style="width: 80%">
            <el-table-column v-for="(item, index) in tableList.headList" 
                        :key=index 
                        :prop="item.prop" 
                        :label="item.label" 
                        :width="item.width">
            </el-table-column>
        </el-table>
    </div>
</template>
<script lang='ts'>
import { Vue, Component } from "vue-property-decorator";
import { TableList } from "@/service/index_two";
@Component
export default class PreviewPage extends Vue {
    // 表格業務
    private tableList: any = new TableList();
    async mounted () {
        // 請求表格數據
        await this.tableList.searchTableData()
        await this.tableList.searchHeadData()
    }
}
</script>

service業務層

import { getTableData, getHeadData } from "@/api/index"

export class TableList {
    // 表格數據
    public tableList: any;
    // 表頭數據
    public headList: any;

    constructor (tableList =[],headList=[]) {
        this.tableList = tableList;
        this.headList = headList;
    }

    public async searchTableData () {
        let data: any = await getTableData();
        this.tableList = data.map(el => {
            Reflect.set(el, 'job', el.jobInfo.job);
            Reflect.set(el, 'love', el.love.join('-'));
            return el
        })
    }

    public async searchHeadData () {
        this.headList = await getHeadData();
    }
}

  我們可以看出,業務類中定義了表格數據和表頭,從后端獲取到數據后直接掛在了業務類中,頁面那些數據處理也在放在了業務類中,在頁面中只需要把這個業務類存在vue實例中,在這個頁面中只要是需要就可以拿到這個業務中的屬性和方法。不僅如此,以后不管有多少個表格,只要是業務類似,都可以用這個業務類。是不是很方便?而且頁面中的代碼減少了很多。個人認為這種方式是比第一種要好很多的。

  不僅如此,有沒有想過我們寫的form表單也是只接寫成業務的?把每個表單中的字段用類構造,這樣頁面中只接綁定類中的屬性。因為類是引用類型,所以頁面中表單有變化,業務類中是能響應的,這樣表單的查詢只接在類中取值就行了,而且表單構造相應變得簡單了很多。我們看一下這個例子:

service中的表單業務

export class Form {
    public job: string;
    public love: string[];
    public address: string;
    constructor ( job='', love=[], address='' ) {
        this.job = job;
        this.love = love;
        this.address = address
    }
}

.vue頁面

<template>
    <div class="preivew-page">
        ...
        <el-drawer title="表單"
                    :visible.sync="drawer"
                    direction="rtl">
            <el-form ref="form" :model="form" :inline="true" label-width="100px" size="mini">
                <el-form-item label="職位:">
                    <el-input v-model="form.job"></el-input>
                </el-form-item>
                <el-form-item label="愛好:">
                    <el-select v-model="form.love" multiple  placeholder="請選擇愛好">
                    <el-option label="吃飯" value="吃飯"></el-option>
                    <el-option label="睡覺" value="睡覺"></el-option>
                    <el-option label="打豆豆" value="打豆豆"></el-option>
                    <el-option label="寫bug" value="寫bug"></el-option>
                    </el-select>
                </el-form-item>
                <el-form-item label="地址:">
                    <el-input v-model="form.address"></el-input>
                </el-form-item>
            </el-form>
        </el-drawer>
    </div>
</template>
<script lang='ts'>
import { Vue, Component } from "vue-property-decorator";
import { Form } from "@/service/form"
@Component
export default class PreviewPage extends Vue {
    ...
    // 表單
    private form = new Form();

    openDialog () {
        this.drawer = true
    }

    // 點擊表格中的編輯按鈕
    handleClick ( row: any ) {
        let { job,love,address } = row
        this.form = new Form(job,love,address);
        this.openDialog()
    }
}
</script>

  新增表單需要初始化數據,就是類構造的過程,點擊編輯按鈕需要加載表格中的行數據,賦值到表單中,這時候需要拿到行數據再次構造一下業務類,是不是比賦值要方便很多,省去很多個等號?如果是個查詢的表單,那么查詢的業務,重置的業務都可以在類中取完成。

  我們再深入研究一下,這個頁面中用到了側滑組件,想想我們在一個項目中是不是有很多個側滑,彈窗等等,那么每次用都要寫一堆的打開關閉這些事件嗎?其實這些簡單的邏輯不是我們重點關注的,如果我把這些流程寫成一個業務呢?看看下邊的代碼:

.vue文件

<template>
    <div class="preivew-page">
        <el-drawer title="表單"
                    :visible.sync="drawer.visible"
                    direction="rtl">
            ...
        </el-drawer>

        <el-button @click="drawer.open()">打開</el-button>
        <el-button @click="drawer.close()">關閉</el-button>

    </div>
</template>
<script lang='ts'>
import { Vue, Component } from "vue-property-decorator";
import { Form } from "@/service/form";
import { Drawer } from "@/service/drawer"
@Component
export default class PreviewPage extends Vue {
    // 彈層控制
    private drawer = new Drawer()
    // 表單
    private form = new Form();
    handleClick ( row: any ) {
        let {job,love,address} = row
        this.form = new Form(job,love,address);
        this.drawer.open()
    }
}
</script>

service側滑業務

export class Drawer {
    public visible: boolean;

    constructor ( visible = false ) {
        this.visible = visible;
    }

    open () {
        this.visible = true;
    }

    close () {
        this.visible = false;
    }
}

  這樣每個側滑組件我們都可以用這個業務了;甚至像彈窗這種類似的組件也能用這個業務。

  其實看到這里你應該就明白了,我們應該如何去寫業務代碼了。

  我們作為程序員,從剛入行開始寫的就是業務代碼,做的多了,應該學會自己把自己寫的代碼變得更加漂亮了。老滿足於當下,不如搞點事情,寫一些優雅的代碼。

  業務代碼其實是最好寫的代碼,只有業務代碼寫的多了,我們才能明白如何將業務下沉為公共服務,公共服務再下沉為基礎服務,等到這時候,就不是寫業務代碼這么簡單了,他更考驗我們的抽象能力。

  本博客耗時三天,就是希望大家不要老寫一些面條代碼,面條代碼是容易理解,但是那對我們的能力和技術一點不會有提升,只有善於封裝,善於優化才能寫出更加漂亮的代碼。

關注我的個人博客: 地址

我的開源項目: 地址

一天一句毒雞湯小程序

image


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM