美文网首页常用技术收集iOS技术资料实用工具
教你如何从0到1实现组件化架构

教你如何从0到1实现组件化架构

作者: 袁峥 | 来源:发表于2017-02-22 17:05 被阅读12914次

    前言

    本篇主要讲解组件化架构思想,从零教你如何组件化一个项目。

    如果喜欢我的文章,可以关注我微博:袁峥Seemygo

    为什么要组件化

    • 随着项目的不断迭代,各个模块会越来越复杂,各个模块相互依赖,而且每个模块可能会有共同的业务逻辑,导致整个项目维护起来比较麻烦。
    • 可以采用组件化,把每个业务逻辑和模块分离,单独管理,这样比较方便维护,各个开发人员只需要关心好自己的模块就好了。

    组件化好处

    • 分工更加明确,提高开发效率
    • 复用性更好,能迅速的组成更多的App

    组件化思想

    • 就好比封装控件,复杂的控件一般都会封装,组件化只不过是把每个模块单独抽出来,作为一个小工程,然后在组成一个一个完整的项目。

    如何组件化

    • 使用cocoapods管理组件化开发
      • podspec:描述自己组件工程的代码目录和资源目录在哪,还有自己组件工程所依赖其他框架,到时候就会根据podspec的指引去引入自己的仓库代码.

        • 命令:pod spec create spec文件名
            // 创建pod索引库,固定写法,并且定义索引库的名字为s,后续通过s,就能拿到索引库
            Pod::Spec.new do |s|
            
              // 设置名称
              s.name         = "HttpManager"
            
              // 设置版本号
              s.version      = "0.0.1"
            
              // 设置摘要
              s.summary      = "A short description of HttpManager."
            
              // 设置详情
              s.description  = "Good"
            
              // 设置仓库主页
              s.homepage     = "http://EXAMPLE/HttpManager"
            
              // 设置许可证
              s.license      = "MIT"
            
              // 设置作者
              s.author             = { "iThinkerYZ" => "690423479@qq.com" }
            
              // 设置仓库源,表示在哪可以找到组件工程
              s.source       = { :git => "", :tag => "#{s.version}" }
            
              // 设置 源文件路径 => 不是整个工程的文件,而是自己封装的代码,以后别的工程引入,就会引入这里的代码。
              s.source_files  = "HttpManager/Classes/**/*.{h,m}"
            
             // s.dependency = '' 组件工程依赖哪些第三方框架
             // s.frameworks = 'UIKit', 'MapKit' 组件工程依赖哪些原生框架
             // s.resource_bundles = {} 组件工程图片资源
             
            end
        
        
        • podspec文件注意点:s.source_files仅仅描述组件代码就好,不要描述整个工程名文件,会把所有文件集成上去,错误写法:s.source_files = "HttpManager"

        • **:表示所有文件:因为*表示通配符,可有可无.

          • s.source_files = "HttpManager/Classes/**/*.{h,m}",表示组件代码在podspec目录下HttpManager/Classes中的所有文件,默认会自动追踪到到podspec文件的目录路径下,因为当前处于podspec文件中,处于哪个文件,就自动追踪哪个文件。
          • HttpManager/Classes/a.h 匹配到的应该是HttpManager/Classes/**,表示HttpManager/Classes/a.h后没有东西,就不会在找,直接匹配到。
        • podspec文件注意点:s.description:不能为空

        • podspec文件注意点:s.license:不能乱填,必须是有这样的协议,比如(MIT)

      • podFile文件:指定主工程加载哪些组件库,里面描述好组件库对应的podspec文件在哪,就知道去哪加载组件代码。

        • 创建命令:pod init
        • cocoapods可以加载远程仓库也可以加载本地仓库,一般加载远程仓库.
        • 如何加载本地仓库代码?
          1. 本地仓库代码搞一个podspec文件描述去哪加载组件代码

          2. 谁需要引入本地仓库代码,就创建Podfile

          3. Podfile:指定podspec文件在哪

            pod 'HttpManager' , :path => '../HttpManager'
            
            • ../HttpManager:回到Podfile上一级目录,进入HttpManager就能找到podspec
            • Podfile在哪,就自动跟随在哪级目录
    Podfile文件.png podspec文件.png
    • 创建组件工程
      • 方式一: 直接创建工程,把组件代码放在Class中,只要spec描述好,就会自动加载Class中组件代码
        • 生成spec描述文件,指定加载组件工程的组件代码在哪。
      • 方式二: 使用cocoapods命令:pod lib create 组件代码名称
        • 这个命令会自动生成一套组件代码工程测试代码,并且有Git管理


          Snip20170223_16.png
        • 还会生成podspec,描述好组件代码在哪就好.
        • 如何使用自动生成的组件工程代码?
          1. 需要把自己的组件代码放在Class中对应文件,还不够,发现根本没法引入组件代码.h文件.

          2. 需要重新pod install,因为不重新pod install,Example工程根本不知道Pod更新了,pod install的作用:重新让pod库与所依赖的工程文件产生关联。

    Cocopods原理

    1. 根据Podfile描述,找到对应代码库的描述文件podspec
    2. podspec中描述,去哪(s.source)才能找到代码库,并且找到之后,需要拷贝哪些文件(s.source_files)到自己的工程中。

    如何组件化(公共远程仓库)

    • 问题:以上的代码仓库都在本地,没有远程管理,也就意味着只能自己使用,别人不能使用,如果想把自己的代码仓库开源,让所有的开发人员都可以弄,应该怎么做?
    • 把自己的仓库代码推送到cocoapods中索引库中就好了。
      • 原理:我们之前安装cocoapods时,有一步骤pod setup,这一步其实就是去下载cocoapods中公共的所有代码仓库索引,保存到本地
      • 通过pod repo 指令就能查看cocoapods的索引库
    pod path.png
    • 如何把自己代码仓库上传到cocoapods上
      1. 给自己代码仓库,创建podspec,其实本质是把自己代码仓库的podspec索引文件上传上去就好了,可以直接使用pod lib create命令

      2. 自己仓库代码远程托管,并且公开,这样别人才能获取到你的仓库代码

        • 注意:远程仓库不需要创建gitignore文件,因为pod lib创建了
        • 提交自己仓库代码到远程仓库
        • git status : 查看状态,如果有不想要的文件,可以用gitignore忽略掉
        • 提交到本地缓存区 `git add .``
        • 提交到本地仓库 git commit -m ''
        • 查看远程仓库地址 git remote(查看有没有远程地址)
        • 绑定远程地址 git remote add origin 远程仓库地址
        • 推送自己代码到远程仓库 git push origin master
      3. 给自己仓库绑定Tag,因为cocoapods是根据代码仓库的Tag,去下载对应Tag的远程代码库的。

        • 给自己当前仓库版本添加Tag标签,git tag -a 0.0.1 -m '0.0.1',仅仅是本地
        • 把本地标签推送到远程服务器,git push --tags
      4. 注册trunk

        • 注册trunk,不是任何人都能推送,因为cocoapods依赖trunk服务器管理,所以需要通过trunk推送自己的podspec(cocoapods官网
        • 命令:pod trunk register EMAIL [NAME]
        • pod trunk register 58999050@qq.com yuanzheng
        • 验证成功后,点击邮箱就好了,打开会有点慢.
      5. 推送自己的podspec到cocoapods的索引库

        • pod trunk push HttpManager.podspec --allow-warnings
        • 注意:必须cd 进入到podspec目录下,才能执行这个代码
        • 注意:podspec文件中的s.version版本号要跟最新Tag一致
        • 注意:podspec文件中的s.source仓库地址也不能写错
      6. 测试能否索引到

        • pod search 自己仓库
        • 发现索引不到,其实已经上传到cocoapods上了,只不过需要重新更新索引文件
        • 怎么更新pod索引文件?
          • 原理:pod setup成功后会生成~/Library/Caches/CocoaPods/search_index.json文件
          • search_index.json文件文件删除,重新执行pod search,就会重新更新索引.

    如何组件化(私有远程仓库)

    • 问题:有些公司核心的代码不想开源,就不能放在cocoapods公共的索引库中,也不能放在本地,因为以后需要多人开发,cocoapods支持创建自己的私有索引库,只需要把自己的代码仓库放在自己的私有索引库就好了.
    • 如何创建私有远程仓库索引库
      1. 搞个私有远程索引库托管平台

      2. 本地添加私有远程索引库: pod repo add XMGSpec https://git.coding.net/iThinkerYZ520/XMGSpec.git,后面是索引库远程地址

      3. 把自己私有库的索引添加到自己私有库种: pod repo push XMGSpec XMGLib.podspec --allow-warnings,本地索引库就会有自己的私有库,并且远程也会有,pod repo push会帮我们推送到远程索引库.

    • 怎么使用自己的私有索引仓库
      • pod search 搜索自己库描述

      • pod install,发现找不到,因为默认是去共有的索引库查找

      • 需要在Podfile文件顶部添加一个源,表示去哪个地方查找。

        • source 'https://git.coding.net/iThinkerYZ520/XMGSpec.git'
      • 但是有问题,如果以后要添加公有的索引库,比如AFN,就找不到了

        • 因此还需要在添加一个公有索引库源
        • source 'https://github.com/CocoaPods/Specs.git'
        # 表示先去找私有,在找公有
        source 'https://git.coding.net/iThinkerYZ520/XMGSpec.git'
        source 'https://github.com/CocoaPods/Specs.git'
        
        target '测试私有索引库' do
        
           pod 'XMGLib'
           pod 'AFNetworking', '~> 3.1.0'
        
        end
        

    组件化升级

    • 以后各个组件要不断更新完善,怎么管理。
      • 只需要把最新的版本代码绑定tag,更新Podspec文件,重新上传到版本索引库就好了
        • pod repo push XMGSpec XMGLib.podspec --allow-warnings
      • 工程文件在使用的时候,使用pod update 就能加载最新版本组件代码.
        • pod update --no-repo-update

    组件化资源

    • 组件工程资源放哪?
      • 使用pod lib create创建的组件工程,有个Assets文件夹,把图片放这
      • 然后podspec指定资源文件路径s.resource_bundles
      • 仓库代码重新pod install就好了,会自动生成bundle文件,图片就保存到这。
    Snip20170213_2.png Snip20170213_4.png
    • 因为podspec指定的图片资源就是这个路径,就会自动查找这个路径.
    • 如何使用组件代码的资源?
      • 之前获取图片都是在主bundle中:NSBundle mainBundle,但是组件资源代码,不是在主bundle中,是在自己框架的bundle中
      • 获取自己bundle,[NSBundle bundleForClass:self],self:表示当前类,当前方法获取当前类所在的bundle
        • 因为当前方法在自己框架中,就能获取自己的bundle


          Snip20170213_5.png
        ```
         NSBundle *selfBundle = [NSBundle bundleForClass:self];
    
        NSLog(@"%@",selfBundle);
        
        // 获取bundle还不够,图片在bundle的XMGLib.bundle文件中
        // 注意图片要全名
        NSString *path = [selfBundle pathForResource:@"XMGLib.bundle/图片@2x.png" ofType:nil];
        
        UIImage *image = [UIImage imageWithContentsOfFile:path];
        
        ```
    
    • 注意:可能项目会遇见如图bug
    Snip20170223_19.png
    • 原因:导入的组件代码有xib,但是找不到xib运行,因为xib属于资源,不能当成代码导入,也就是需要用resource_bundles导入。


      Snip20170223_20.png
    • 运行:找不到xib,因为xib被存放到资源的bundle中,因此需要添加前缀
        NSBundle *bundle = [NSBundle bundleForClass:[self class]];
    // 获取当前bundle名称
        NSString *bundleName = bundle.infoDictionary[@"CFBundleName"];
        bundleName = [NSString stringWithFormat:@"%@.bundle",bundleName];
    // xib名称需要拼接Bundle名称,否则找不到xib
        NSString *nibName = [NSString stringWithFormat:@"%@/XMGHomeRecommendCell",bundleName];
        [self.tableView registerNib:[UINib nibWithNibName:nibName bundle:[NSBundle bundleForClass:[self class]]] forCellReuseIdentifier:ID];
    
    • 友情提醒:如果组件中使用了资源,一定要把组件生成framework,不能生成.a,否则资源拿不到.
    • podfile文件中添加描述use_frameworks!
    Snip20170223_21.png
    • 使用use_frameworks和不使用use_frameworks的区别
    Snip20170223_22.png Snip20170223_23.png

    框架依赖

    • podspec文件中描述:s.dependency 'AFNetworking'
      • 在使用组件的时候就会自动导入第三方库

    如何组件化(划分子组件)

    • 随着组件不断扩大,业务也会越来越多,如果不划分子组件,可能我们的工程有时候并不需要导入那么多的业务,也会一起导入到自己工程,造成自己工程不必要的代码太多,所以在大公司一般都会为自己的工程瘦身,就是减少不必要的代码。
    • 一个好的组件,要划分好子组件,别人在加载你的组件的时候,就可以根据自己的需求,加载对应的组件代码
    • 以SDWebImage为例,内部有四个子组件,有一个组件专门用来加载gif图片.
    Snip20170213_7.png
    • SDWebImage的podspec文件,描述了子组件,格式固定
      • 注意:自己子组件如果依赖其他子组件,一定要写依赖子组件,否则子组件不能用,下面就依赖Core子组件
        •  注意:GIF 和 gif不能同名,ruby语法,GIF是外界搜索子组件名称,gif是变量,用于后面访问
          
        •  如果使用了subspec,别人引入你的框架,代码也会按照subspec划分文件夹结构,否则所有代码都在一起,不管你之前是否划分好,比如SDWebImage就会有GIF文件夹
          
        • 还有如果使用subspec,就不需要描述整个文件夹路径,会造成subspec划分的文件夹没有代码,用#注释掉之前的描述。
    Snip20170224_28.png
    s.subspec 'GIF' do |gif|
        gif.ios.deployment_target = '7.0'
        gif.source_files = 'SDWebImage/FLAnimatedImage/*.{h,m}'
        // 设置依赖,依赖自己组件的子组件Core
        gif.dependency 'SDWebImage/Core'
        gif.dependency 'FLAnimatedImage', '~> 1.0'
        gif.xcconfig = {
          'USER_HEADER_SEARCH_PATHS' => '$(inherited) $(SRCROOT)/FLAnimatedImage/FLAnimatedImage'
        }
      end
    
    
    • 使用subspec和不使用subpec区别
    Snip20170223_18.png
    • 如何加载子组件

      • podfile文件描述
      // 只会引入GIF组件
      pod 'SDWebImage/GIF'
      

    相关文章

      网友评论

      • LD_左岸:我现在在基础组件中的Category里给MBProgressHUD添加了分类 分类的.h @class MBProgressHUD;
        @interface MBProgressHUD (MJ)
        + (void)showSuccess:(NSString *)success toView:(UIView *)view;
        .m
        #import "MBProgressHUD.h"
        #import "MBProgressHUD+MJ.h"

        @Implementation MBProgressHUD (MJ)
        #pragma mark 显示信息
        + (void)show:(NSString *)text icon:(NSString *)icon view:(UIView *)view

        spec文件
        s.subspec 'Category' do |c|
        c.source_files = 'LDBase/Classes/Category/**/*'
        c.dependency 'MBProgressHUD', '~> 1.1.0'
        end
        现在有个问题是验证spec时一直报错 后来传到本地的私有索引库了
        cd Example里 pod inistall
        安装 编译 没报错
        但是我在用这个分类时 报错依旧了
        @interface MBProgressHUD (MJ) Cannot define category for undefined class 'MBProgressHUD'
        老师 如何👀
      • 雨子颜:收益良多,希望多出精品
      • Ko_Neko:我的一直报错 - ERROR | file patterns: The `source_files` pattern did not match any file.
        工程结构:创建了一个xcode项目叫MyTools,然后里面新建了一个叫JXTools的文件里面放的我的代码文件。podspec文件描述 : s.source_files = "MyTools/JXTools/*" 但是一直pod lib lint不成功 可以帮看看是哪里出错了吗?谢谢
      • zheNgHH:有个坑 找了一周了
        本地私有库 和 上传到pod中的开放库 集成到项目中后 能在pod中看到库里的文件 但是引用不到 已经设置了search header 还是不行
        zheNgHH:git 地址是 https://github.com/zhenghaonagehao/RMSwiftSideControllerPodSpec.git
        zheNgHH:烦劳帮忙看一下 而且用的是swift
      • Onegeng:组件化是一五年低一六年初很热的话题,今年关于iOS 11好像没什么热门的话题了,连一些高产的人都改写<如何戒掉王者荣耀>了,是不是ios不行了老师
      • 差一点不是帅哥:我太菜了,看不懂。 求大神帮忙
      • 小荣袁:二进制化的时候,framework中的图片,在pod install后,丢失了,只有 Headers 和 PrivateHeaders两个文件夹,里面只有.h文件,难道是二进制化后图片资源加载不了吗?
      • MR_詹:袁峥老师你有开视频直播或其他的吗?想跟你学习组件化和RN技术
      • 君赏:我写了一篇组件话思路http://www.jianshu.com/p/351ac4ce5a59
      • csqingyang:自己摸索一遍之后,发现难点在这里差不多都讲了,,,
      • 新地球说着一口陌生腔调:我想问一下 你这个 pod spec create spec文件名 需要在某个工程目录下执行吗?
        csqingyang:cd 到某一个项目路径下,创建这个项目对应的 podspec. 不过推荐你使用 pod lib create xx. 这个生成的 podspec 模板文件对新手更友好一点.
      • 亦泉心然01:貌似真做不出来.按照你的思路,
        csqingyang:前提你得知道自己想拆什么, 有按照 三层结构拆的, 有按照 MVC 拆的, 看自己的需求,先想清楚拆什么就好
      • Cstars:cocoapods官网有教程,简单几步教你创建私有spec repo,作者可以直接贴链接,然后总结一下其他的难点
        袁峥:@Cstars 嗯嗯
      • Roader:还是不知道怎么做组件化开发啊
      • 39Coding:袁兄,坑啊~~~~~pod注释不能用// 我测试了一下午才发现,大家注意啦
        39Coding:我知道了,各位 git仓库一定要建2个,一个是作为Spec管理的,一个是专门写组件的!repo push的时候会push到Spec仓库
        39Coding:袁哥,我不知道为什么使用私有库,第一次上传pod install没有问题,但是第二次上传后,
        An unexpected version directory `Assets` was encountered for the `/Users/GuJunjie/.cocoapods/repos/AiyouSpec/AiyouSpec` Pod in the `AiyouSpec` repository.
        袁峥:@SamAtropos 用#
      • 阿泽V5:学习了
      • 陈小翰:获益匪浅
        我的大名叫小爱: @keepTMD 看了别的在看这个 这个是总结的 比一般操作的更精华
        陈小翰:@keepTMD ?
        d63def1678be:你根据他这个文章能做出来,看都没看就受益
      • akkkk47:组件化之后还有一些问题,怎么解耦,怎么组件间通讯。 这些如果再出一期详细的,就赞了!
      • 阿拉斯加的狗:精品!受益!
        baiwulong:真的很不错,我挺喜欢峥哥写的博文的,特别自己按照其他的博文糊里糊涂的上传到cocoapod之后,再看这个有种醍醐灌顶的感觉,过分了,哈哈,不过确实扫盲很有用
        Vine_Finer: @keepTMD 有问题说嘛。
        d63def1678be:你根据他这个文章能做出来吗,看都没看就精品
      • 好迪:怎么像是一篇 添加cocopods私有库的文章
        静花寒:我也这么觉得,哈哈,不过文章写得很不错
      • 29f1dcdf0613:加油加油↖(^ω^)↗
      • 温特儿:学习了,希望再继续讲点后面的
      • lxyz22zp:不错:+1:
      • HunterDude:写得好。
      • 邓小帅:精品!受益!
      • 热干面一元五:前排出租:smile:

      本文标题:教你如何从0到1实现组件化架构

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