美文网首页
使用 Vue 开发业务的套路(模式)

使用 Vue 开发业务的套路(模式)

作者: 云峰yf | 来源:发表于2018-03-22 21:26 被阅读0次

前言

最近一直在忙公司的业务开发活动,从中经历了和产品、Web 后端、底层、测试同学的爱恨情仇,最后想把自己的一点经验总结一下,顺便拿最坑的那个部分做个例子。

Vue 的 MVVM 和 MVC

我们常说 Vue 是 MVVM 的库,又说它不是框架,只是前端的 View 层,MVVM 不就包括 View 吗?为什么又说 Vue 或 React 只是 View 层面上的库呢?

先祭出图:


vue 业务套路.png

我们常说的 MVVM,实际上是指:

  1. 视图层,也就是 HTML+CSS 组成的页面
  2. 模型层,通常指前端组件需要的数据
  3. 视图模型层,它会在视图和模型之间进行通信。通俗点讲,就是通过指令,将视图中触发的事件传达给模型层,将模型层的数据编译成视图。

实际开发中后端的数据和前端所需的数据结构往往不匹配,所以这时我们在前端还是需要一个 MVC:

  1. 视图层,这块是纯前端内容
  2. 模型层,这块是纯后端内容,只通过接口暴露数据,我们前端通常通过 ajax 获取这些数据
  3. 控制器层,这块是视图和数据进行适配的地方,如果后端数据不符合前端需求,应该由它来转化。

如果业务简单,或者故意不分层,那么前端就会把 MVC 的代码都写在一坨,而我们分离它们,自然是为了以后应对后端和产品的不定时更改。

一个小例子

有一天,我接到一个需求,做一个表单提交页,这没什么,但它有十颗树。

但最悲催的是,后端给了我一个这样的数据结构:

{
    data: {
        api: [{
                prop: [{
                        name: 'length',
                        state: 1
                    },
                    {
                        name: 'prototype',
                        state: 1
                    }
                ],
                static_method: [{
                        name: 'from',
                        state: 0
                    },
                    {
                        name: 'isArray',
                        state: 0
                    },
                    {
                        name: 'of',
                        state: 1
                    }
                ],
                proto_method: [{
                        name: 'toString',
                        state: 1
                    },
                    {
                        name: 'toLocalString',
                        state: 1
                    },
                    {
                        name: 'pop',
                        state: 1
                    },
                    {
                        name: 'push',
                        state: 1
                    },
                    {
                        name: 'shift',
                        state: 1
                    },
                    {
                        name: 'unshift',
                        state: 1
                    },
                    {
                        name: 'find',
                        state: 1
                    },
                    {
                        name: 'findIndex',
                        state: 1
                    },
                    {
                        name: 'includes',
                        state: 1
                    },
                    {
                        name: 'indexOf',
                        state: 1
                    },
                    {
                        name: 'lastIndexOf',
                        state: 1
                    },
                    {
                        name: 'join',
                        state: 1
                    },
                    {
                        name: 'slice',
                        state: 1
                    },
                    {
                        name: 'concat',
                        state: 1
                    },
                    {
                        name: 'splice',
                        state: 1
                    },
                    {
                        name: 'copyWithin',
                        state: 1
                    },
                    {
                        name: 'keys',
                        state: 1
                    },
                    {
                        name: 'enenties',
                        state: 1
                    },
                    {
                        name: 'values',
                        state: 1
                    },
                    {
                        name: 'every',
                        state: 1
                    },
                    {
                        name: 'some',
                        state: 1
                    },
                    {
                        name: 'sort',
                        state: 1
                    },
                    {
                        name: 'reverse',
                        state: 1
                    },
                    {
                        name: 'forEach',
                        state: 1
                    },
                    {
                        name: 'map',
                        state: 1
                    },
                    {
                        name: 'filter',
                        state: 1
                    },
                    {
                        name: 'reduce',
                        state: 1
                    },
                    {
                        name: 'reduceRight',
                        state: 1
                    }
                ],
                type: 'Array'
            },
            {
                prop: [{
                    name: 'prototype',
                    state: 1
                }],
                static_method: [{
                        name: 'assign',
                        state: 1
                    },
                    {
                        name: 'create',
                        state: 1
                    },
                    {
                        name: 'defineProperties',
                        state: 1
                    },
                    {
                        name: 'defineProperty',
                        state: 1
                    },
                    {
                        name: 'getOwnPropertyDescriptor',
                        state: 1
                    },
                    {
                        name: 'getOwnPropertyDescriptors',
                        state: 1
                    },
                    {
                        name: 'getOwnPropertyNames',
                        state: 1
                    },
                    {
                        name: 'getOwnPropertySymbols',
                        state: 1
                    },
                    {
                        name: 'getPrototypeOf',
                        state: 1
                    },
                    {
                        name: 'is',
                        state: 1
                    },
                    {
                        name: 'isExtensible',
                        state: 1
                    },
                    {
                        name: 'isFrozen',
                        state: 1
                    },
                    {
                        name: 'isSealed',
                        state: 1
                    },
                    {
                        name: 'keys',
                        state: 1
                    },
                    {
                        name: 'preventExtensions',
                        state: 1
                    },
                    {
                        name: 'seal',
                        state: 1
                    },
                    {
                        name: 'freeze',
                        state: 1
                    },
                    {
                        name: 'values',
                        state: 1
                    },
                    {
                        name: 'setPrototypeOf',
                        state: 1
                    }
                ],
                proto_method: [{
                        name: 'hasOwnProperty',
                        state: 1
                    },
                    {
                        name: 'isPrototypeOf',
                        state: 1
                    },
                    {
                        name: 'propertyIsEnumerable',
                        state: 1
                    },
                    {
                        name: 'toString',
                        state: 1
                    },
                    {
                        name: 'valueOf',
                        state: 1
                    }
                ],
                type: 'Object'
            },
            {
                prop: [{
                        name: 'prototype',
                        state: 1
                    },
                    {
                        name: 'length',
                        state: 1
                    }
                ],
                static_method: [{
                        name: 'fromCharCode',
                        state: 1
                    },
                    {
                        name: 'fromCodePoint',
                        state: 1
                    }
                ],
                proto_method: [{
                        name: 'charAt',
                        state: 1
                    },
                    {
                        name: 'indexOf',
                        state: 1
                    }
                ],
                type: 'String'
            },
            {
                prop: [{
                    name: 'prototype',
                    state: 1
                }],
                static_method: [{
                        name: 'UTC',
                        state: 1
                    },
                    {
                        name: 'now',
                        state: 1
                    }
                ],
                proto_method: [{
                        name: 'getDate',
                        state: 1
                    },
                    {
                        name: 'getTime',
                        state: 1
                    }
                ],
                type: 'Date'
            }
        ],
        api_type: [{
                name: 'Array',
                state: 1
            },
            {
                name: 'Object',
                state: 1
            },
            {
                name: 'String',
                state: 1
            },
            {
                name: 'Date',
                state: 1
            }
        ]
    }
}

我知道你肯定会没耐心地滋溜的滑下来,让我来解释一下这个数组:
每个元素的type键作为父节点,其他一个键共用一棵树。比如 prop 对应属性树、proto_method 对应原型方法树、static_method 对应静态方法树。此数组有四个元素,故四个元素共用三棵树,父节点由 type 键决定。

这个数据结构是肯定有缺陷的,比如:

  1. 每个元素是如何识别的?
  2. 以后树超过两层该怎么办?

按理来说,后端应该给每个元素赋予一个 id 或 index 来区分元素的不同,以便用户在前端选择某个具体的节点时知道那个节点在树中的位置。但是后端表示你爱用不用,于是我只能这样解决这两个问题了:

  1. 将元素的 name 属性当做 id,如果是子元素,那就是 type-name 拼接的值当 id
  2. 以后再出现树必须是扁平结构,类似:
{
  name:'toString',
  id:1,
  pid:0,
  state:1
}

为了减少读者老爷的头晕目眩感,我在例子中只放了三棵树,最后实现的效果如下图,在线演示地址

效果图

面对这种恶心的数据结构,如果前端写成一坨,非常有可能诞生一个一千行以上的 vue 文件,所以我们分层,是对未来的自己好,也是为以后接手的人好。

我们分层后,vue 的 js 部分(MVVM)只需要写成:

new Vue({
    el: '#app',
    data: {
        treeData: [
            { 'title': '属性', data: [], checkedKeys: [] },
            { 'title': '原型方法', data: [], checkedKeys: [] },
            { 'title': '静态方法', data: [], checkedKeys: [] }
        ],
        apiData: new DataProvider(sourceData.data)
    },
    methods: {
        initTreeData() {
            Object.assign(this.treeData[0], this.apiData.getProp())
            Object.assign(this.treeData[1], this.apiData.getProtoMethod())
            Object.assign(this.treeData[2], this.apiData.getStaticMethod())
        }
    },
    mounted() {
        this.initTreeData()
    }
})

而 MVC 中的 C 里的逻辑,都封装到 DataProvider 里去了。业务部分只需要消费数据就行了。

DataProvider 除了 get* 的方法外,还应该要有 set* 的方法,用于将前端数据转成后端可用的数据结构(对,你没看错,丧心病狂的后端还要我在用户选完树后按那个恶心的结构传回去)。不过分层后一切都好做很多了,就算以后这块的数据结构又改,vue 文件里几乎是不需要改动的。

而 Vue 官方也是推荐我们将 MVC 的 V 层分离出来的,只不过是通过 Vuex 中实现:


vuex

结语

业务开发如搬砖,很多人嫌弃的不要的不要的,但是公司一般就是靠业务赚钱的,不能老让你研究公司感觉没什么卵用的赚不了钱的技术。

无论我们是开发业务项目还是开发技术产品项目,都应该静下心来,仔细琢磨每一处可以设计、构建的部分,荣辱不惊。

代码处处是学问。

相关文章

  • 使用 Vue 开发业务的套路(模式)

    前言 最近一直在忙公司的业务开发活动,从中经历了和产品、Web 后端、底层、测试同学的爱恨情仇,最后想把自己的一点...

  • VUE 2.x中全局组件的封装(三)

    Vue.extend 属于 Vue 的全局 API,在实际业务开发中我们很少使用,因为相比常用的 Vue.comp...

  • web前端开发高级

    前端高效开发框架技术与应用 Vue 基础Vue 框架简介 MVX 模式介绍Vue 框架概述如何使用 Vue.js ...

  • Vue 基本使用

    传统开发模式对比 原生JSJQuery Vue.js之 HelloWorld 基本步骤 Vue 的基本使用步骤:1...

  • 尤雨溪回应:Vue 与 TypeScript 为什么相性特别差?

    近日,有开发者在知乎上提出了一个问题:“TypeScript 不适合在 vue 业务开发中使用吗?”,Vue 的作...

  • Vuex状态管理

    Vuex是一个专门为Vue.js应用程序开发的状态管理模式 使用方法: 使用vuex插件import Vue fr...

  • React和Vue的区别 2020-06-23

    区别: 1、使用习惯和思维模式 vue更符合web开发者思维模式 React 对于拥有函数式编程背景的开发者(非W...

  • 反射与代理设计模式

      代理设计模式是在程序开发中使用最多的设计模式,代理设计模式的核心是有真实业务实现类和代理业务实现类,并且代理类...

  • vuex

    vue - vuex的定义和使用 (自我理解) Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。它...

  • Vue-Cli 自定义指令

    使用Vue框架开发的同胞们在非常愉快的使用vue内置指令的同时,有时候因为业务的原因,难免想动一动DOM的底层,这...

网友评论

      本文标题:使用 Vue 开发业务的套路(模式)

      本文链接:https://www.haomeiwen.com/subject/dsszqftx.html