大话大前端时代(一) —— Vue 与 iOS 的组件化

今年大前端的概念一而再再而三的被说起,那么大前端时代到底是什么呢?大前端这个词最先是由于在阿里内部有不少前端开发人员既写前端又写 Java 的 Velocity 模板而得来,不过如今大前端的范围已经愈来愈大了,包含前端 + 移动端,前端、CDN、Nginx、Node、Hybrid、Weex、React Native、Native App。笔者是一名普通的全职 iOS 开发者,在接触到了前端开发之后,发现了前端有些值得移动端学习的地方,因而便有了这个大前端时代系列的文章,但愿二者能相互借鉴优秀的思想。谈及到大前端,经常被说起的话题有:组件化,路由与解耦,工程化(打包工具,脚手架,包管理工具),MVC 和 MVVM 架构,埋点和性能监控。笔者就先从组件化方面谈起。网上关于前端框架对比的文章也很是多(对比 React,Vue,Angular),不过跨端对比的文章好像很少?笔者就打算之前端和移动端(以 iOS 平台为主)对比为主,看看这两端的不一样作法,并讨论讨论有无相互借鉴学习的地方。javascript

本文前端的部分也许前端大神看了会以为比较基础,若有错误还请各位大神不吝赐教。html


Vue 篇

一. 组件化的需求

为了提升代码复用性,减小重复性的开发,咱们就把相关的代码按照 template、style、script 拆分,封装成一个个的组件。组件能够扩展
HTML 元素,封装可重用的 HTML 代码,咱们能够将组件看做自定义的 HTML 元素。在 Vue 里面,每一个封装好的组件能够当作一个个的 ViewModel。前端

二. 如何封装组件

谈到如何封装的问题,就要先说说怎么去组织组件的问题。vue

若是在简单的 SPA 项目中,能够直接用 Vue.component 去定义一个全局组件,项目一旦复杂之后,就会出现弊端了:java

  1. 全局定义(Global definitions) 强制要求每一个 component 中的命名不得重复
  2. 字符串模板(String templates) 缺少语法高亮,在 HTML 有多行的时候,须要用到丑陋的 \
  3. 不支持 CSS(No CSS support) 意味着当 HTML 和 JavaScript 组件化时,CSS 明显被遗漏
  4. 没有构建步骤(No build step) 限制只能使用 HTML 和 ES5 JavaScript, 而不能使用预处理器,如 Pug (formerly Jade) 和 Babel

并且如今公司级的项目,大多数都会引入工程化的管理,用包管理工具去管理,npm 或者 yarn。因此 Vue 在复杂的项目中用 Vue.component 去定义一个组件的方式就不适合了。这里就须要用到单文件组件,还可使用 Webpack 或 Browserify 等构建工具。好比下面这个Hello.vue组件,整个文件就是一个组件。webpack

在单文件组件中,整个文件都是一个 CommonJS 模块,里面包含了组件对应的 HTML、组件内的处理逻辑 Javascript、组件的样式 CSS。ios

在组件的 script 标签中,须要封装该组件 ViewModel 的行为。git

  • data
    组件的初始化数据,以及私有属性。
  • props
    组件的属性,这里的属性专门用来接收父子组件通讯的数据。(这里能够类比 iOS 里面的 @property )
  • methods
    组件内的处理逻辑函数。
  • watch
    须要额外监听的属性(这里能够类比 iOS 里面的 KVO )
  • computed
    组件的计算属性github

  • components
    所用到的子组件web

  • lifecycle hooks
    生命周期的钩子函数。一个组件也是有生命周期的,有以下这些:beforeCreatecreatedbeforeMountmountedbeforeUpdateupdatedactivateddeactivatedbeforeDestroydestroyed等生命周期。在这些钩子函数里面能够加上咱们预设的处理逻辑。(这里能够类比 iOS 里面的 ViewController 的生命周期 )

如此看来,在 Vue 里面封装一个单文件组件,和在 iOS 里面封装一个 ViewModel 的思路是彻底一致的。接下来的讨论无特殊说明,针对的都是单文件组件。

三. 如何划分组件

通常划分组件分能够按照如下标准去划分:

  1. 页面区域:
    header、footer、sidebar……
  2. 功能模块:
    select、pagenation……

这里举个例子来讲明一块儿前端是如何划分组件的。

1. 页面区域

仍是以 objc中国 的首页页面为例

咱们能够把上面的页面按照布局,先抽象图片中间的样子,而后接着按照页面的区域划分组件,最后能够获得最右边的组件树。

在 Vue 实例的根组件,加载 layout。

import Vue from 'vue';
import store from './store';
import router from './router';
import Layout from './components/layout';

new Vue({
  el: '#app',
  router,
  store,
  template: '<Layout/>',
  components: {
    Layout
  }
});复制代码

根据抽象出来的组件树,能够进一步的向下细分各个小组件。

layout 下一层的组件是 header、footer、content,这三部分就组成了 layout.vue 单文件组件的所有部分。

上图就是咱们的 layout.vue 的所有实现。在这个单文件组件中里面引用了三个子组件,navigationBar、footerView、content。因为 content 里面是又各个路由页面组成,因此这里声明成 router-view。

至于各个子组件的具体实现这里就不在赘述了,具体代码能够看这里navigationBar.vuefooterViewlayout.vue

2. 功能模块

通常项目里面详情页的内容最多,咱们就以以 objc中国 的详情页面为例

上图左边是详情页,右图是按照功能区分的图,咱们把整个页面划分为6个子组件。

从上往下依次展开,见上图。

通过功能上的划分之后,整个详情页面的代码变的异常清爽,整个页面就是6个单文件的子组件,每一个子组件的逻辑封装在各自的组件里面,详情页面就是把他们都组装在了一块儿,代码可读性高,后期维护也很是方便。

详情页面具体的代码在这里github.com/halfrost/vu…

6个子组件的代码在这里github.com/halfrost/vu…,具体的代码见连接,这里就不在赘述了。

综上能够看出,前端 SPA 页面抽象出来就是一个大的组件树。

四. 组件化原理

举个例子:

<!DOCTYPE html>
<html>
    <body>
        <div id="app">
            <parent-component>
            </parent-component>
        </div>
    </body>
    <script src="js/vue.js"></script>
    <script>

        var Child = Vue.extend({
            template: '<p>This is a child component !</p>'
        })

        var Parent = Vue.extend({
            // 在Parent组件内使用<child-component>标签
            template :'<p>This is a Parent component !</p><child-component></child-component>',
            components: {
                // 局部注册Child组件,该组件只能在Parent组件内使用
                'child-component': Child
            }
        })

        // 全局注册Parent组件
        Vue.component('parent-component', Parent)

        new Vue({
            el: '#app'
        })

    </script>
</html>复制代码

在上面的例子中,在 <parent-component> 父组件里面声明了一个 <child-component>,最终渲染出来的结果是:

This is a Parent component !
This is a child component !复制代码

上述代码的执行顺序以下:

  1. 子组件先在父组件中的 components 中进行注册。
  2. 父组件利用 Vue.component 注册到全局。
  3. 当渲染父组件的时候,渲染到 <child-component> ,会把子组件也渲染出来。

值得说明的一点是,Vue 进行模板解析的时候会遵循如下 html 常见的限制:

  • a 不能包含其它的交互元素(如按钮,连接)
  • ul 和 ol 只能直接包含 li
  • select 只能包含 option 和 optgroup
  • table 只能直接包含 thead, tbody, tfoot, tr, caption, col, colgroup
  • tr 只能直接包含 th 和 td

五. 组件分类

组件的种类可分为如下4种:

  1. 普通组件
  2. 动态组件
  3. 异步组件
  4. 递归组件

1. 普通组件

以前讲的都是普通的组件,这里就不在赘述了。

2. 动态组件

动态组件利用的是 is 的特性,能够设置多个组件可使用同一个挂载点,并动态切换。

var vm = new Vue({
  el: '#example',
  data: {
    currentView: 'home'
  },
  components: {
    home: { /* ... */ },
    posts: { /* ... */ },
    archive: { /* ... */ }
  }
})


<component v-bind:is="currentView">
  <!-- 组件在 vm.currentview 变化时改变! --> </component>复制代码

如今 <component> 组件的具体类型用 currentView 来表示了,咱们就能够经过更改 currentView 的值,来动态加载各个组件。上述例子中,能够不断的更改 data 里面的 currentView ,来达到动态加载 home、posts、archive 三个不一样组件的目的。

3. 异步组件

Vue容许将组件定义为一个工厂函数,在组件须要渲染时触发工厂函数动态地解析组件,而且将结果缓存起来:

Vue.component("async-component", function(resolve, reject){
    // async operation
    setTimeout(function() {
        resolve({
            template: '<div>something async</div>'
        });
    },1000);
});复制代码

动态组件可配合 webpack 实现代码分割,webpack 能够将代码分割成块,在须要此块时再使用 ajax 的方式下载:

Vue.component('async-webpack-example', function(resolve) {
  // 这个特殊的 require 语法告诉 webpack
  // 自动将编译后的代码分割成不一样的块,
  // 这些块将经过 ajax 请求自动下载。
  require(['./my-async-component'], resolve)
});复制代码

4. 递归组件

若是一个组件设置了 name 属性,那么它就能够变成递归组件了。

递归组件能够利用模板里面的 name 不断的递归调用本身。

name: 'recursion-component',
template: '<div><recursion-component></recursion-component></div>'复制代码

上面这段代码是一个错误代码,这样写模板的话就会致使递归死循环,最终报错 “max stack size exceeded”。解决办法须要打破死循环,好比 v-if 返回 false。

六. 组件间的消息传递和状态管理

在 Vue 中,组件消息传递的方式主要分为3种:

  1. 父子组件之间的消息传递
  2. Event Bus
  3. Vuex 单向数据流

1. 父子组件之间的消息传递

父子组件的传递方式比较单一,在 Vue 2.0 之后,父子组件的关系能够总结为 props down, events up 。父组件经过 props 向下传递数据给子组件,子组件经过 events 给父组件发送消息。

父向子传递

举个例子:

Vue.component('child', {
  // 声明 props
  props: ['msg'],
  // prop 能够用在模板内
  // 能够用 `this.msg` 设置
  template: '<span>{{ msg }}</span>'
})

<child msg="hello!"></child>复制代码

在 child 组件的 props 中声明了一个 msg 属性,在父组件中利用这个属性把值传给子组件。

这里有一点须要注意的是,在非字符串模板中, camelCased (驼峰式) 命名的 prop 须要转换为相对应的 kebab-case (短横线隔开式) 命名。

上面这个例子是静态的绑定,Vue 也支持动态绑定,这里也支持 v-bind 指令进行动态的绑定 props 。

父向子传递是一个单向数据流的过程,prop 是单向绑定的:当父组件的属性变化时,将传导给子组件,可是不会反过来。这是为了防止子组件无心修改了父组件的状态——这会让应用的数据流难以理解。

另外,每次父组件更新时,子组件的全部 prop 都会更新为最新值。这意味着你不该该在子组件内部改变 prop。Vue 建议子组件的 props 是 immutable 的。

这里就会牵涉到2类问题:

  1. 因为单向数据流的缘由,会致使子组件的数据或者状态和父组件的不一致,为了同步,在子组件里面反数据流的去修改父组件的数据或者数据。

  2. 子组件接收到了 props 的值之后,有2种缘由想要改变它,第一种缘由是,prop 做为初始值传入后,子组件想把它看成局部数据来用;第二种缘由是,prop 做为初始值传入,由子组件处理成其它数据输出。

这两类问题,开发者强行更改,也都是能够实现的,可是会致使不使人满意的 “后果” 。第一个问题强行手动修改父组件的数据或者状态之后,致使数据流混乱不堪。只看父组件,很难理解父组件的状态。由于它可能被任意子组件修改!理想状况下,只有组件本身能修改它的状态。第二个问题强行手动修改子组件的 props 之后,Vue 会在控制台给出警告。

若是优雅的解决这2种问题呢?一个个的来讲:

(1)第一个问题,换成双向绑定就能够解决。

在 Vue 2.3.0+ 之后的版本,双向绑定有2种方式

第一种方式:

利用 .sync 修饰符,在 Vue 2.3.0+ 之后做为一个编译时的语法糖存在。它会被扩展为一个自动更新父组件属性的 v-on 侦听器。

// 声明一个双向绑定
<comp :foo.sync="bar"></comp>


// 上面一行代码会被会被扩展为下面这一行:
<comp :foo="bar" @update:foo="val => bar = val"></comp>

// 当子组件须要更新 foo 的值时,它会显式地触发一个更新事件:
this.$emit('update:foo', newValue)复制代码

第二种方式:

自定义事件能够用来建立自定义的表单输入组件,使用 v-model 来进行数据双向绑定。

<input :value="value" @input="updateValue($event.target.value)" >复制代码

在这种方式下进行的双向绑定必须知足2个条件:

  • 接受一个 value 属性
  • 在有新的值时触发 input 事件

官方推荐的2种双向绑定的方式就是上述2种方法。不过还有一些隐性的双向绑定,可能无心间就会形成bug的产生。

pros 是单向数据传递,父组件把数据传递给子组件,须要尤为注意的是,传递的数据若是是引用类型(好比数组和对象),那么默认就是双向数据绑定,子组件的更改都会影响到父组件里面。在这种状况下,若是人为不知情,就会出现一些莫名其妙的bug,因此须要注意引用类型的数据传递。

(2)第二个问题,有两种作法:

  • 第一种作法是:定义一个局部变量,并用 prop 的值初始化它:
props: ['initialCounter'],
data: function () {
  return { counter: this.initialCounter }
}复制代码
  • 第二种作法是:定义一个计算属性,处理 prop 的值并返回。
props: ['size'],
computed: {
  normalizedSize: function () {
    return this.size.trim().toLowerCase()
  }
}复制代码

父向子传递还能够传递模板,使用 slot 分发内容。

slot 是 Vue 的一个内置的自定义元素指令。slot 在 bind 回调函数中,根据 name 获取将要替换插槽的元素,若是上下文环境中有所需替换的内容,则调用父元素的 replaceChild 方法,用替换元素讲 slot 元素替换;不然直接删除将要替换的元素。若是替换插槽元素中有一个顶级元素,且顶级元素的第一子节点为 DOM 元素,且该节点有 v-if 指令,且 slot 元素中有内容,则替换模板将增长 v-else 模板放入插槽中的内容。若是 v-if 指令为 false,则渲染 else 模板内容。

子向父传递

子组件要把数据传递回父组件,方式很单一,那利用自定义事件!

父组件使用 $on(eventName) 监听事件
子组件使用 $emit(eventName) 触发事件

举个简单的例子:

// 在子组件里面有一个 button
<button @click="emitMyEvent">emit</button>

emitMyEvent() {
  this.$emit('my-event', this.hello);
}


// 在父组件里面监听子组件的自定义事件
<child @my-event="getMyEvent"></child>

getMyEvent() {
    console.log(' i got child event ');
}复制代码

这里也能够经过父子之间的关系进行传递数据(直接修改数据),可是不推荐这种方法,例如 this.$parent 或者 this.$children 直接调用父或者子组件的方法,这里类比iOS里面的ViewControllers方法,在这个数组里面能够直接拿到全部 VC ,而后就能够调用他们暴露在.h里面的方法了。可是这种方式相互直接耦合性太大了。

2. Event Bus

Event Bus 这个概念对移动端的同窗来讲也比较熟悉,由于在安卓开发中就有这个概念。在 iOS 开发中,能够类比消息总线。具体实现能够是通知 Notification 或者 ReactiveCocoa 中的信号传递。

Event Bus 的实现仍是借助 Vue 的实例。新建一个新的 Vue,专门用来作消息总线。

var eventBus = new Vue()

// 在 A 组件中引入 eventBus
eventBus.$emit('myEvent', 1)

// 在要监听的组件中监听
eventBus.$on('id-selected', () => {
  // ...
})复制代码

3. Vuex 单向数据流

因为本篇文章重点讨论组件化的问题,因此这里 Vuex 只是说明用法,至于原理的东西以后会单独开一篇文章来分析。

这一张图就描述了 Vuex 是什么。Vuex 专为 Vue.js 应用程序开发的状态管理模式。它采用集中式存储管理应用的全部组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。

上图中箭头的指向就描述了数据的流向。数据的流向是单向的,从 Actions 流向 State,State 中的数据改变了从而影响到 View 展现数据的变化。

从简单的 Actions、State、View 三个角色,到如今增长了一个 Mutations。Mutations 如今变成了更改 Vuex 的 store 中的状态的惟一方法是提交 mutation。Vuex 中的 mutations 很是相似于事件:每一个 mutation 都有一个字符串的 事件类型 (type) 和 一个 回调函数 (handler)。

通常在组件中进行 commit 调用 Mutation 方法

this.$store.commit('increment', payload);复制代码

Actions 和 Mutations 的区别在于:

  • Action 提交的是 mutation,而不是直接变动状态。
  • Action 能够包含任意异步操做,而 Mutations 必须是同步函数。

通常在组件中进行 dispatch 调用 Actions 方法

this.$store.dispatch('increment');复制代码

Vuex 官方针对 Vuex 的最佳实践,给出了一个项目模板结构,但愿你们都能按照这种模式去组织咱们的项目。

├── index.html
├── main.js
├── api
│   └── ... # 抽取出API请求
├── components
│   ├── App.vue
│   └── ...
└── store
    ├── index.js          # 咱们组装模块并导出 store 的地方
    ├── actions.js        # 根级别的 action
    ├── mutations.js      # 根级别的 mutation
    └── modules
        ├── cart.js       # 购物车模块
        └── products.js   # 产品模块复制代码

关于这个例子的详细代码在这里

七. 组件注册方式

组件的注册方式主要就分为2种:全局注册和局部注册

1. 全局注册

利用 Vue.component 指令进行全局注册

Vue.component('my-component', {
  // 选项
})复制代码

注册完的组件就能够在父实例中以自定义元素 <my-component></my-component> 的形式使用。

// 注册
Vue.component('my-component', {
  template: '<div>A custom component!</div>'
})
// 建立根实例
new Vue({
  el: '#example'
})

<div id="example">
  <my-component></my-component> </div>复制代码

2. 局部注册

全局注册组件会拖慢一些页面的加载速度,有些组件只须要用的到时候再加载,因此没必要在全局注册每一个组件。因而就有了局部注册的方式。

var Child = {
  template: '<div>A custom component!</div>'
}
new Vue({
  // ...
  components: {
    // <my-component> 将只在父模板可用
    'my-component': Child
  }
})复制代码

iOS 篇

一. 组件化的需求

在 iOS Native app 前期开发的时候,若是参与的开发人员也很少,那么代码大多数都是写在一个工程里面的,这个时候业务发展也不是太快,因此不少时候也能保证开发效率。

可是一旦项目工程庞大之后,开发人员也会逐渐多起来,业务发展日新月异,这个时候单一的工程开发模式就会暴露出弊端了。

  • 项目内代码文件耦合比较严重
  • 容易出现冲突,大公司同时开发一个项目的人多,每次 pull 一下最新代码就会有不少冲突,有时候合并代码须要半个小时左右,这会耽误开发效率。
  • 业务方的开发效率不够高,开发人员一多,每一个人都只想关心本身的组件,可是却要编译整个项目,与其余不相干的代码糅合在一块儿。调试起来也不方便,即便开发一个很小的功能,都要去把整个项目都编译一遍,调试效率低。

为了解决这些问题,iOS 项目就出现了组件化的概念。因此 iOS 的组件化是为了解决上述这些问题的,这里与前端组件化解决的痛点不一样。

iOS 组件化之后能带来以下的好处:

  • 加快编译速度(不用编译主客那一大坨代码了,各个组件都是静态库)
  • 自由选择开发姿式(MVC / MVVM / FRP)
  • 方便 QA 有针对性地测试
  • 提升业务开发效率

iOS 组件化的封装性只是其中的一小部分,更加关心的是如何拆分组件,如何解除耦合。前端的组件化可能会更加注重组件的封装性,高可复用性。

二. 如何封装组件

iOS 的组件化手段很是单一,就是利用 Cocoapods 封装成 pod 库,主工程分别引用这些 pod 便可。愈来愈多的第三方库也都在 Cocoapods 上发布本身的最新版本,大公司也在公司内部维护了公司私有的 Cocoapods 仓库。一个封装完美的 Pod 组件,主工程使用起来很是方便。

具体若是用 Cocoapods 打包一个静态库 .a 或者 framework ,网上教程不少,这里给一个连接,详细的操做方法就再也不赘述了。

最终想要达到的理想目标就是主工程就是一个壳工程,其余全部代码都在组件 Pods 里面,主工程的工做就是初始化,加载这些组件的,没有其余任何代码了。

三. 如何划分组件

iOS 划分组件虽然没有一个很明确的标准,由于每一个项目都不一样,划分组件的粗粒度也不一样,可是依旧有一个划分的原则。

App之间能够重用的 Util、Category、网络层和本地存储 storage 等等这些东西抽成了 Pod 库。还有些一些和业务相关的,也是在各个App之间重用的。

原则就是:要在App之间共享的代码就应该抽成 Pod 库,把它们做为一个个组件。不在 App 间共享的业务线,也应该抽成 Pod,解除它与工程其余的文件耦合性。

常见的划分方法都是从底层开始动手,网络库,路由,MVVM框架,数据库存储,加密解密,工具类,地图,基础SDK,APM,风控,埋点……从下往上,到了上层就是各个业务方的组件了,最多见的就相似于购物车,个人钱包,登陆,注册等。

四. 组件化原理

iOS 的组件化原理是基于 Cocoapods 的。关于 Cocoapods 的具体工做原理,能够看这篇文章《CocoaPods 都作了什么?》

这里简单的分析一下 pod进来的库是什么加载到主工程的。

pod 会依据 Podfile 文件里面的依赖库,把这些库的源代码下载下来,并建立好 Pods workspace。当程序编译的时候,会预先执行2个 pod设置进来的脚本。

在上面这个脚本中,会把 Pods 里面的打包好的静态库合并到 libPods-XXX.a 这个静态库里面,这个库是主工程依赖的库。

上图就是给主项目加载 Pods 库的脚本。

Pods 另一个脚本是加载资源的。见下图。

这里加载的资源是 Pods 库里面的一些图片资源,或者是 Boudle 里面的 xib ,storyboard,音乐资源等等。这些资源也会一块儿打到 libPods-XXX.a 这个静态库里面。

上图就是加载资源的脚本。

五. 组件分类

iOS 的组件主要分为2种形式:

  1. 静态库
  2. 动态库

静态库通常是以 .a 和 .framework 结尾的文件,动态库通常是以 .dylib 和 .framework 结尾的文件。

这里能够看到,一个 .framework 结尾的文件仅仅经过文件类型是没法判断出它是一个静态库仍是一个动态库。

静态库和动态库的区别在于:

  1. .a文件确定是静态库,.dylib确定是动态库,.framework多是静态库也多是动态库;

  2. 静态库在连接其余库的状况时,它会被完整的复制到可执行文件中,若是多个App都使用了同一个静态库,那么每一个App都会拷贝一份,缺点是浪费内存。相似于定义一个基本变量,使用该基本变量是是新复制了一份数据,而不是原来定义的;静态库的好处很明显,编译完成以后,库文件实际上就没有做用了。目标程序没有外部依赖,直接就能够运行。固然其缺点也很明显,就是会使用目标程序的体积增大。

  3. 动态库不会被复制,只有一份,程序运行时动态加载到内存中,系统只会加载一次,多个程序共用一份,节约了内存。并且使用动态库,能够不从新编译链接可执行程序的前提下,更新动态库文件达到更新应用程序的目的。

六. 组件间的消息传递和状态管理

以前咱们讨论过了,iOS 组件化十分关注解耦性,这算是组件化的一个重要目的。iOS 各个组件之间消息传递是用路由来实现的。关于路由,笔者曾经写过一篇比较详细的文章,感兴趣的能够来看这篇文章《iOS 组件化 —— 路由设计思路分析》

七. 组件注册方式

iOS 组件注册的方式主要有3种:

  1. load方法注册
  2. 读取 plist 文件注册
  3. Annotation注解方式注册

前两种方式都比较简单,容易理解。

第一种方式在 load 方法里面利用 Runtime 把组件名和组件实例的映射关系保存到一个全局的字典里,方便程序启动之后能够随时调用。

第二种方式是把组件名和组件实例的映射关系预先写在 plist 文件中。程序须要的时候直接去读取这个 plist 文件。plist 文件能够从服务器读取过来,这样 App 还能有必定的动态性。

第三种方式比较黑科技。利用的是 Mach-o 的数据结构,在程序编程连接成可执行文件的时候,就把相关注册信息直接写入到最终的可执行文件的 Data 数据段内。程序执行之后,直接去那个段内去读取想要的数据便可。

关于这三种作法的详细实现,能够看笔者以前的一篇文章《BeeHive —— 一个优雅但还在完善中的解耦框架》,在这篇文章里面详细的分析了上述3种注册过程的具体实现。


总结

通过上面的分析,咱们能够看出 Vue 的组件化和 iOS 的组件化区别仍是比较大的。

二者平台上开发方式存在差别

主要体如今单页应用和类多页应用的差别。

如今前端比较火的一种应用就是单页Web应用(single page web application,SPA),顾名思义,就是只有一张Web页面的应用,是加载单个HTML 页面并在用户与应用程序交互时动态更新该页面的Web应用程序。

浏览器从服务器加载初始页面,以及整个应用所需的脚本(框架、库、应用代码)和样式表。当用户定位到其余页面时,不会触发页面刷新。经过 HTML5 History API 更新页面的 URL 。浏览器经过 AJAX 请求检索新页面(一般以 JSON 格式)所需的新数据。而后, SPA 经过 JavaScript 动态更新已经在初始页面加载中已经下载好的新页面。这种模式相似于原生手机应用的工做原理。

可是 iOS 开发更像类 MPA (Multi-Page Application)。

每每一个原生的 App ,页面差很少应该是上图这样。固然,可能有人会说,依旧能够把这么多页面写成一个页面,在一个 VC 里面控制全部的 View,就像前端的 DOM 那样。这种思路虽然理论上是可行的,可是笔者没有见过有人这么作,页面一多起来,100多个页面,上千个 View,都在一个 VC 上控制,这样开发有点蛋疼。

二者解决的需求也存在差别

iOS 的组件化一部分也是解决了代码复用性的问题,可是更多的是解决耦合性大,开发效率合做性低的问题。而 Vue 的组件化更多的是为了解决代码复用性的问题。

二者的组件化的方向也有不一样。

iOS 平台因为有 UIKit 这类苹果已经封装好的 Framework,因此基础控件已经封装完成,不须要咱们本身手动封装了,因此 iOS 的组件着眼于一个大的功能,好比网络库,购物车,个人钱包,整个业务块。前端的页面布局是在 DOM 上进行的,只有最基础的 CSS 的标签,因此控件都须要本身写,Vue 的组件化封装的可复用的单文件组件其实更加相似于 iOS 这边的 ViewModel。

因此从封装性上来说,二者能够相互借鉴的地方并很少。iOS 能从前端借鉴的东西在状态管理这一块,单向数据流的思想。不过这一块思想虽然好,可是如何能在自家公司的app上获得比较好的实践,依旧是仁者见仁智者见智的事了,并非全部的业务都适合单向数据流。


Reference:
Vue.js 官方文档

GitHub Repo:Halfrost-Field

Follow: halfrost · GitHub

Source: halfrost.com/vue_ios_mod…

相关文章
相关标签/搜索