精华内容
下载资源
问答
  • 如何写好一个页面 一、前期准备素材 学成在线PSD源文件 开发工具 = PS(切图)+ submit(编辑器【代码】)+ Chrome(浏览器【测试用】) 二、前期准备工作 创建study目录文件夹(用于存放我们页面的相关内容) ...
    如何写好一个页面

    一、前期准备素材

    • 学成在线PSD源文件
    • 开发工具 = PS(切图)+ submit(编辑器【代码】)+ Chrome(浏览器【测试用】)

    二、前期准备工作

    1. 创建study目录文件夹(用于存放我们页面的相关内容)
    2. study目录内新建images文件夹(用于保存图片)
    3. 新建index.html首页html文件(网站的首页统一规定为index.html)
    4. 新建style.css样式文件。
    5. 将样式引入到我们HTML页面文件中。(如何引入css样式?)
    6. 样式表写入 清除内外边距样式(检测样式表是否引入成功)。

    三、CSS属性书写顺序

    1. 布局定位属性:display / position / float / clear / visibility / overflow 等(建议display第一个写,毕竟关系到模式)
    2. 自身属性:width / height / margin / padding / border / background等
    3. 文本属性:color / font / text-decoration / text-alian / vertical-align / white-space / break-word等
    4. 其他属性:(CSS3):content / curser / border-radius / box-shadow / text-shadow / background:linear-gradient等
    .lizi{
                display: block;/* 转换为块级元素 */
                position: relative;/* 绝对定位 */
                float: left;/* 浮动 */
                width: 100px;/* 宽 */
                height: 100px;/* 高 */
                margin: 0 10px;/* 外边距 */
                padding: 20px 0 ;/* 内间距 */
                font-family: Arial, 'Helvetica Neue',Helvetica, sans-serif;/* 设置字体 */
               color:#333;/* 颜色 */
               background-color:rgba(0,0,0,0.5);/* 背景颜色 */
               -webkit-border-radius: 10px;/* 兼容旧的谷歌浏览器 */
               -moz-webkit-border-radius: 10px;/* 兼容旧的火狐浏览器 */
               -ms-webkit-border-radius: 10px;/* 兼容旧的ie浏览器 */
               -o-webkit-border-radius: 10px;/* 兼容旧的欧鹏浏览器 */
               border-radius: 10px;
    }
    

    四、布局流程

    1. 必须确定页面的版心(可视区)(这个可以自己测量)
    2. 分析页面中的行模块,以及每个行模块中的列模块。(页面布局,就是一行行罗列而成)
    3. 制作HTML结构(遵循先有结构,后有样式的原则。结构永远最重要。)
    4. 最后运用盒子模型的原理,通过div+css布局来控制网页的各个模块
    展开全文
  • 如何写好一个前端页面

    千次阅读 2018-10-08 10:43:57
    个人前端总结,望君收益,不喜勿喷!...不要只修当前页面,所牵扯的页面也要完善,不能改了这,错了那个。 5.控制的用户体验,例如:增加点击范围,加入的过渡~ 6.不要遗漏上拉加载,下拉刷...

    个人前端总结,望君收益,不喜勿喷!

     

    1.审查页面,提取公共部分

    2.提前引入所需的组件

    3.定义好所需字段,注意生命周期的前后顺序,不要出现页面还没加载过程中,先出现无数据的界面

    4.不要遗漏任何地方的事件,并且注意事件的完整性。不要只修当前页面,所牵扯的页面也要完善,不能改了这个,错了那个。

    5.控制好的用户体验,例如:增加点击范围,加入好的过渡~

    6.不要遗漏上拉加载,下拉刷新,置顶 等等小的功能

    7.给适当的地方,加上适当时间的提示框

    8.减少for循环,如果有for循环,把arr.length定义在外部,减少循环工作量

    9.注意清除页面滚动

    10.页面完成后,换手机测试,保证安卓和ios 显示都是正常的。

    11.页面完成后删除所有console和alert

    12.打包的时候,注意修改index和build里面的相关内容,提高打包效率。

    13.打包后,多点点,看看页面有没有打包后的问题,注意优化。

    展开全文
  • 界面可以说是每位移动应用开发者的基本功,也是位合格移动应用开发者绕不过去的坎。但就如不是每位开发者都能够成为合格的开发者一样,本人在不同的团队中发现,甚少有人能够编写出合格的UI代码;而非常奇怪的...

    写界面可以说是每位移动应用开发者的基本功,也是一位合格移动应用开发者绕不过去的坎。但就如不是每一位开发者都能够成为合格的开发者一样,本人在不同的团队中发现,甚少有人能够编写出合格的UI代码;而非常奇怪的是,在很多的开发者论坛上看到我们移动开发者更多关注于某个控件或者是动画,但却很少看到深入剖析UI机制,指导UI开发的文章。


    由于界面涉及到的方面实在过于广泛,本文不可能事无巨细,一一道来,所以本文先立足于点,深入剖析iOS UI系统中不被重视却非常重要的机制,帮助本文读者对ios的UI系统有整体了解;进而以点带面,拓展到UI逻辑设计和架构设计模式的讨论;最后读文而有所思有所得,设计开发出高效、易用、流畅的UI模块。


    本文章节如下:


    • 基础与本质:说明普遍意义上的UI系统的三大模块,让读者从整体上对UI系统有清楚的认识。


    • View:深入View的内部机制,View与Layer之间的关系,以及Offscreen Render;


    • ViewController:讲解ViewController在UI系统中所扮演的角色,以及UI架构设计中ViewController运用和实践;


    • MVC、MVP、MVVM:简单分析三种主流的架构设计模式及其异同,并简单提出了一些做架构设计意见和想法;


    • 总结。


    各章节间没有必然的联系,读者可以选择感兴趣章节阅读。


    1. 基础与本质


    终端App开发区别于后端开发最大的不同,就是终端开发很大部分的逻辑是为用户提供界面以供人机交互,即所谓的UI(User Interface)。所以所有的UI架构主要关注三大模块:界面布局管理,渲染及动画、事件响应;


    1.1 布局管理


    即在规定的坐标系统上,按照一定的层级顺序位置大小排布在容器内。一个UI系统必然有个基于坐标的布局管理系统,不管是Windows、Sysbian,还是Andorid、iOS。好的布局管理机制直接影响界面逻辑实现的难易程度;


    我们现在日常接触到的App的UI坐标系统都是二维的,我们现在玩的3D游戏,受限于二维的展示屏幕,所以实质上只是三维在二维上的映射投影。我们一直在往更高的维度发展:全息影像、Hololens等等。在此可以设想下,未来我们构建界面的布局管理很可能就是基于真实三维坐标。


    1.2 动画及渲染


    UI之所以叫User Interface,就是因为UI通过视觉上的展示,为用户提供信息。这些信息的展示需要通过一系列复杂的计算,最后操作液晶体展示在显示屏上,这一系列过程就是渲染和动画;


    下图就是应用界面渲染到展示的流程:



    引自WWDC2014 #419 Advanced Graphics and Animations for iOS Apps


    这里不展开来讲,推荐没看过的同学都认真观看,能够很好的理解渲染流程和界面优化;


    推荐资料:


    WWDC2014 #419 Advanced Graphics and Animations for iOS Apps

    Objc.io的文章:#Objc Issue 3:Views – Getting Pixels onto the Screen


    1.3 事件响应


    UI除了展示信息之外,还需要接收并响应用户的点击、手势、摇晃等事件,经过一系列操作后更新展示信息,展示给用户;正确及时地响应用户的操作并给予反馈,是良好用户体验的保证。为何Android设备普遍给人的感觉比iOS设备要卡,其中一个主要的原因是iOS系统将响应用户事件放在主线程的最高优先级。


    1.4 UI系统架构


    从整体理解了上述三个方面,你会对UI架构有系统认识。iOS中的UI系统架构如下:


    引自WWDC2014 #419 Advanced Graphics and Animations for iOS Apps


    2. View


    UIView是UIKit中最基本控件,就如同NSObject基本上是Cocoa库内所有类的基类一样,UIView也是UIKit中所有界面控件的基类。只要你愿意,你甚至只用UIView就可以搭建你的App(不过iOS9做了约束,必须设置keyWindow的rootViewControler)。


    一般来说,熟练掌握常用的UIView子类控件(如UIButton, UIImageView, UILabel等)就足以应付90%的界面编码需要。但想要编写出高效、优美的界面代码,还需要更深入的了解。既然要深入,本文假设你对UIView已经有了初步的了解,至少使用写过几个完整的页面;基于此设定下,本文讨论聚焦于以下几点:


    1) UIView 与 CALayer:讨论UIView背后的CALayer,了解CALayer与UIView的关系及渲染流程;


    2) Offscreen Render:阐述什么是Offscreen Render(离屏渲染),以及一些避免离屏渲染的方法;


    3) UIResponser:讨论UIView和UIViewController的父类UIResponser,分析iOS设备上的事件响应链;


    4) 设计与实践:结合本人开发实践经验,说明在UIView应用中好的设计实践规则;


    参考:View Programming Guide for iOS


    2.1 UIView 与 CALayer


    我们应该都知道每个UIView都包含了一个CALayer,就算你没直接看过CALayer,应该也使用过。比如给一个View切个圆角:view.layer.cornerRadius = 5.0f;;加个边框:view.layer.borderWidth = 1.0f; view.layer.borderColor = [UIColor darkGrayColor].CGColor;,这里使用的layer就是CALayer。


    CALayer是QuartzCore库内的类,是iOS上最基本的绘制单元;而UIView只是CALyer之上的封装,更准确的来说,UIView是CALyer的简版封装,加上事件处理的集合类。事件处理我们下一节再讨论,这里的简版封装如何理解,为什么不直接使用CALayer?


    首先,如上一段所述,CALayer是最基本的绘制单元,每一个UIView都有一个CALayer的变量(public var layer: CALayer { get }),UIView的渲染实质就是这个layer的渲染。我们可以看看的类定义,里面有很多属性(变量)及方法在中可以找到几乎一模一样的对应;如属性变量frame、hidden,方法public func convertPoint(p: CGPoint, fromLayer l: CALayer?) -> CGPoint等;但也有更多的属性方法是UIView所没有的,这里就一一列举了。我们可以看到UIView其实是把常用的接口(属性和方法)暴露出来了,让UIView更为易用。


    其次,我们知道iOS平台的Cocoa Touch 是源于OS X平台的Cocoa),是在Cocoa的基础上添加了适用于移动手机设备的手势识别、动画等特性;但从底层实现上来说,Cocoa Touch与Cocoa共用一套底层的库,其中就包括了QuartCore.framework;但QuartCore.framework一开始就是为OS X设计的,所以其中有部分特性是不适合做移动设备开发的,比如最重要的坐标系统。因此,我们也就不难理解为何UIView/NSView在CALayer上做了一层封装。


    以上,是UIView于CALayer的主要的关系。


    2.2 Offscreen Render


    当你尚在懵懂未知的开发初期,在写UIScrollView及其子类(UITableView、UICollectionView)时,一定会遇到滚动不流畅,经常卡顿的情况;你认真研究代码,发现你逻辑代码都放到了异步线程,主线程做的都是渲染界面的活,为什么会卡顿?然后你想老手寻求帮助,老手会让你去掉圆角、半透明和阴影之类,App又重回丝般顺滑;你不知道为什么,问老手,他可能会很详细跟你解释一通,然后你一知半解地点点头,脑中一片茫然;较好的情况,也许你依稀记得这么一个词:离屏渲染(Offscreen Render)。那到底什么是Offscreen Render?为什么Offscreen Render会导致卡顿?


    在第一章的1.2节中有提到渲染的流程图,我们再更深入点,先看看最基本的渲染通道流程:


    引自WWDC2014 #419 Advanced Graphics and Animations for iOS Apps

    注:iOS的GPU渲染机制是Tile-Based的,而Tile-Based GPU也是现在移动设备的主流;


    我们再来看看需要Offscreen Render的渲染通道流程:


    引自WWDC2014 #419 Advanced Graphics and Animations for iOS Apps


    一般情况下,OpenGL会将应用提交到Render Server的动画直接渲染显示(基本的Tile-Based渲染流程),但对于一些复杂的图像动画的渲染并不能直接渲染叠加显示,而是需要根据Command Buffer分通道进行渲染之后再组合,这一组合过程中,就有些渲染通道是不会直接显示的;对比基本渲染通道流程和Masking渲染通道流程图,我们可以看到到Masking渲染需要更多渲染通道和合并的步骤;而这些没有直接显示在屏幕的上的通道(如上图的 Pass 1 和 Pass 2)就是Offscreen Rendering Pass。


    Offscreen Render为什么卡顿,从上图我们就可以知道,Offscreen Render需要更多的渲染通道,而且不同的渲染通道间切换需要耗费一定的时间,这个时间内GPU会闲置,当通道达到一定数量,对性能也会有较大的影响;


    那哪些情况会Offscreen Render呢?


    drawRect

    layer.shouldRasterize = true;


    有mask或者是阴影(layer.masksToBounds, layer.shadow*);


    Text(UILabel, CATextLayer, Core Text, etc)


    注:layer.cornerRadius,layer.borderWidth,layer.borderColor并不会Offscreen Render,因为这些不需要加入Mask。


    还有更多与Offscreen Render以及动画图形优化相关的知识,请认真观看WWDC。


    参考:

    WWDC2011 #121 Understanding UIKit Rendering

    WWDC2014 #419 Advanced Graphics and Animations for iOS Apps


    2.3 设计与实践


    以上几节,对View在开发过程中经常遇到,但并不容易深入理解的概念进行了讨论。接下来,我想脱离View的具体概念,谈谈本人在View设计和开发中的一些实践经验;


    2.3.1 精简扁平的View层次结构


    复杂的View层次结果不仅会影响渲染效率,而且也会造成代码的臃肿,会造成不可预料的问题并且难以定位;怎么样维护一个精简扁平的View层次结构呢?原则如下:


    1) 尽量使用系统原生的控件;


    如实现一个icon跟title上下布局的按钮,很多人习惯是使用一个view包含了一个UIButton和一个UILabel。实际上更为推荐的方式是调整UIButon的contentInset/titleInset/imageInset三个参数来达到这个效果,非常简单,并且title有UIButton上的展示方式和特性,如可以设置高亮颜色等;


    又比如一个有着复杂一点布局结构的滚动界面,有些开发者会觉得使用UITableView/UICollectionView实现会比较复杂,有些效果可能没办法达到,就用他们的基类UIScrollView来实现,自己造了一大套的轮子,代码可能也变得非常复杂;实际上根据我的经验,通过重写或者是内部属性的调整是完全可以使用UITableView/UICollectionView来达到这个效果,毕竟UITableView/UICollectionView是UIScrollView的子类,功能不会减少,而会更加强大,并且我们还能利用已有的data source和delegate机制,实现设计上的解耦。


    其他常见的还有UINavigationBar、UITabBar、UIToolBar等等;


    2) 合理添加/删除动态View;


    有些View是动态的,就是偶尔显示,偶尔隐藏。这类View有两种处理方式:增删,或者显示/隐藏。没有标准的答案,个人更推荐增删的处理方式,即在有需要的时候添加到对应的ContainerView上,在不需要的时候将其删除。这样即可以与懒加载结合在一起,而且也能避免两个动态View的相互影响,比如TableFooterView,或者是错误加载View。但这并不是唯一的方式,假如这个动态View所在的View层级比较简单,并且需要动画进行动态展示,则使用显示/隐藏也是不错的处理方式。


    2.3.2 通用控件


    每一个程序员都可以建立自己的代码库,同理,每一位移动开发程序员都可以建立自己的通用控件代码库。这个库内的控件,可以是你自己写的,也可以是优秀的第三方开源控件。建立控件库,除了能够避免重新造轮子,大大提高我们的开发效率,还有更为重要的一点:在运用、改造、重构中掌握接口设计解耦,甚至是架构的知识和经验。


    每个App的UI设计、交互、布局和配色往往千差万别,但总脱离不出移动App这一范畴,也就决定了在某些通用的控件交互上会保持一致性,以让用户依据自己在移动应用上的使用经验就能轻松快速上手使用,这就是App的移动性。所以通用控件的适用场景往往是很“通用”的。比如下拉刷新、加载更多、Tab Bar、提示Tips、加载错误重新加载等等。在新的App或者功能模块上运用这些控件时,你就会思考怎么让控件更加通用,即不影响旧的逻辑,又能够适用新的需求,这对于做界面的架构设计是非常好的锻炼。


    2.3.3 合理运用VC在替代View组合复杂界面


    在界面开发过程中,我们常常会遇到复杂的界面,比如多页界面、多种布局方式展示多业务的首页等,但由于很大部分开发者已经对“一屏就是一个VC”这一初学者的习惯奉为教条,写出一个庞然大View,再加上复杂的逻辑代码,这一块的代码很可能就演变成了谁都不敢动的禁区。一个VC可以管理多个VC,所以合理的使用VC来替代View进行复杂界面组合,不仅能够将复杂界面切分成更小的粒度,逻辑代码也同步合理划分,便于维护和重构;而依托VC的机制,还能View和数据的动态加载管理。


    下一章中关于轻VC的讨论是这一节知识的拓展。


    3. ViewController


    上一节关于View的章节已讨论了iOS界面机制,这一节则主要是来谈谈在写界面过程中的设计问题和基本规范;


    ViewController在iOS只是一个非常重要的概念,它是我们在开发界面时最常打交道的模块,其在一个App中所扮演的角色,View Controller Programming Guide for iOS 中有清晰准确的描述:


    1) View Management:管理View;


    2) Data Marshalling:管理数据;


    3) User Interactions:响应用户交互;


    4) Resource Management:管理资源;


    5) Adaptivity:适配不同的屏幕尺寸空间的变化;


    可以看到,ViewController有太多的事情要做,这也就导致了ViewController非常容易变得代码膨胀、逻辑混乱等问题;依照个人经验,一个ViewController类的有效代码超过500行,这个ViewController就会变得难以维护,但实际上在开发过程中,往往会遇到上1K行,甚至2~3K行的ViewController类;当一个ViewController类达到2~3K行,就意味着其他开发者接手这个模块来修改东西,已经无法通过滚动来定位代码,只能通过搜索;


    所以,在进行界面开发时,ViewController需要特别注意模块设计,将不同的模块按照逻辑进行一定的拆分,即解耦,又防止ViewController模块的代码膨胀。这就是轻VC的理念;


    3.1 轻VC


    轻VC是前两年非常火的名词,现在似乎已经成为了一种业界规范或者是惯例。同上所述,一个VC的类,如果有效代码超过了500行,则表示这个类看是变得臃肿而难以维护;到达800行,只能通过搜索来定位代码时,重构已势在必行;


    关于轻VC,objc.io的开篇第一章#Issue 1 : Lighter View Controllers,足见这一理念的重要性。掌握轻VC的理念基本上是一个iOS开发者从初级迈向高级必备技能。#Issue 1 : Lighter View Controllers 文中介绍了构建轻VC几种常见的方式:


    1) 将数据源等复杂接口从VC中剥离;


    2) 把业务逻辑代码抽象到Model层;


    3) 将复杂View抽象成独立的类;


    4) 使用VC的Containment的特点,将一个VC中逻辑分离的界面模块剥离成为多个子VC;


    想要设计出合理而易于理解和维护的轻VC结构,需要掌握轻VC的知识并有一定实践经验。在以下情况下,可以考虑将一个VC设计或者重构成更多模块更多类的轻VC结构:


    1) 如上所述,代码超过500行时;


    2) VC内的View的数据源来自多个不同的地方;


    3) VC内有多个复杂的View,需要展示数据实体类较为复杂;


    总之,当你感觉你的VC已经变得臃肿,那么就可尝试轻VC的实践,实践才有收获。


    3.2 VC的设计


    相对于View关注于布局和展示,VC更关注设计和管理。本节以一个实例,来简单介绍在一个完整App中的VC设计。


    先来看一个常见的UI结构设计例子:


    这个图应该非常容易理解:最底部是一个侧滑抽屉控件,该抽屉包含了App内容展示的TabBarController和设置的VC;TabBarController的子Item VC包含了相应业务的List VC,点击List VC进入到详情View内;有些详情VC是使用WebViewController来进行内容的展示。非常简单,不是么?接下来说明该设计的洞见:


    1) Root ViewController,是整个App内Window的根VC,这是一个生命周期与App相同的VC,即Window的RootViewController是唯一且一直存在的,需要切换场景则使用这个Root VC控制子VC切换来实现(常见于场景:需要进行强登录,即登录之后才能使用的App,登录成功后从登录界面切换到主界面,则登录VC和主界面VC都应该是Root VC的子VC,受Root VC的控制来进行切换)。这个RootViewController建议是一个UINavigationController,以此保证足够扩展性,并提供更为丰富的界面交互选择。这个Root VC的生命周期与App一致,这样一些突发的灵活分支界面可以很好的展示在Root VC上,如全局的Loading提示、OpenURL的分支调整等;


    2) Main ViewController:主界面,是主要业务展示界面的根界面。该VC与RootVC功能上会很容易重合在一起,但需要注意的是,该VC并非一直存在,但切换到一些特定分支时,该VC会从Root VC上remove掉,比如前面所说的强登录App,登录界面与主界面就会需要进行切换。另外,该VC隔离了主要业务展示界面的VC与Root VC,便于App整体界面风格的改版和重构。比如现在上图展示的是一个侧滑抽屉+TabBar的组合,那到下个版本改版把侧滑抽屉去掉,那么只需要使用TabBar替换DrawerMenu VC在Main VC中的位置即可,而不会影响到RootVC中其他分支展示出来的界面(如Push等)。


    3) TabBarItem ViewController:作为TabBar Controller的子Item VC,通常会设计为NavigationController,用以管理各TabBarItem内的VC栈。

    注:如果需要在Push进入二级界面(Detail VC)时隐藏TabBar,只需要设置二级VC的hidesBottomBarWhenPushed = true即可,如果想更加灵活的控制TabBar,例如进到三级页面的时候显示出TabBar(这个场景应该很少见),或者你的TabBar是自定义的,可以参考我写的一个开源控件MZNavTab;


    本节所示例的UI结构是一个非常通用的UI结构,市面上除游戏外60%以上的App都是类似的UI交互(统计来源于个人手机),假如你的UI交互与此类似而你的UI结构很混乱的话,不如尝试下这个UI结构设计。


    4. MVC、MVP、MVVM


    MVC:

    MVP:

    MVVM:


    图注:虚线箭头:表示两者之间是非强依赖关系。如MVC图,View与Model一般没有直接联系。


    虚线矩形:表示该模块在对应架构设计中的隐性存在。即一般性架构中并没有这个角色,但立足于iOS这个平台,这又是不可或缺的一部分;


    本文并不打算将MVC、MVP、MVVM这个几个通用架构设计模式的概念统统在这里叙述一遍,上面三个图基本上能够很明白地对比出三者之间的差异。也许与你在网上看到的不尽相同,这是因为以上三图更立足于iOS平台。


    4.1 MVC


    我们最初看到的MVC设计模式图可能是这样的:



    引自[MSDN#ASP.NET - Single-Page Applications: Build Modern, Responsive Web Apps with ASP.net(https://msdn.microsoft.com/en-us/magazine/dn463786.aspx)


    而苹果官方给的MVC的设计模式图却是这样的:


    到底哪一副图才是真正的MVC?我的答案只能是:都是。


    MVC从施乐帕克实验室提出至今,已经应用到各种应用开发领域中:Web App可以用MVC,iOS/android/Windows客户端应用也用MVC,Web前端也在用MVC,等等;这些几乎涵盖了我们常见的开发领域,所以MVC其实已经超越了他原本最初的设计,基于所有涉及展示的应用都能套上MVC,只不过不同的平台在设计上略有差别。而MVP和MVVM,也不过是MVC的衍生变种,除这两者之外,还有我们没怎么见过的HMVC、MVA等。


    4.2 Model Layer


    在讨论MVP和MVVM之前,我想先明确一个经常被误解的概念:Model。由于Model这个词太通用化,如数据Model,数据库Model,这就导致了Model这一概念理解差异化,简单的说,就是被玩坏。抛开其他,我们来看看常见的定义:


    Wikipedia的定义:


    The central component of MVC, the model, captures the behavior of the application in terms of its problem domain, independent of the user interface.[11] The model directly manages the data, logic and rules of the application.


    MSDN(https://msdn.microsoft.com/en-us/library/ff649643.aspx)中的定义:


    Model. The model manages the behavior and data of the application domain, responds to requests for information about its state (usually from the view), and responds to instructions to change state (usually from the controller).


    上面两个定义基本一致:Model,管理应用的行为和数据。


    再来看看Apple官方文档Model-View-Controller的定义:


    Model Objects

    Model objects encapsulate the data specific to an application and define the logic and computation that manipulate and process that data. For example, a model object might represent a character in a game or a contact in an address book. A model object can have to-one and to-many relationships with other model objects, and so sometimes the model layer of an application effectively is one or more object graphs. Much of the data that is part of the persistent state of the application (whether that persistent state is stored in files or databases) should reside in the model objects after the data is loaded into the application. Because model objects represent knowledge and expertise related to a specific problem domain, they can be reused in similar problem domains. Ideally, a model object should have no explicit connection to the view objects that present its data and allow users to edit that data—it should not be concerned with user-interface and presentation issues.

    Communication: User actions in the view layer that create or modify data are communicated through a controller object and result in the creation or updating of a model object. When a model object changes (for example, new data is received over a network connection), it notifies a controller object, which updates the appropriate view objects.


    虽然Apple的官方文档是定义Model Objects,但它的含义还是封装数据以及管理数据相关的逻辑计算;


    所以这里需要明确的一个概念是:在MVC的设计模式中,Model是一个Layer,而不只是一个数据模型(Data Model)类。总体来说,Model Layer 包含了数据模型,以及管理这些数据相关的逻辑计算,如本地数据变化、数据缓存、从网络请求数据等业务逻辑。关于这个问题,还可以参考这篇文章:《iOS应用架构谈 view层的组织和调用方案》。但有一点需要说明:该文章更倾向于从Model Object上思考Model的定义,因为里面的关于Model的示例是从数据模型中扩展出业务接口;而本人则更倾向于从Model Layer来思考Model,即Model并不限于数据模型,可以是数据管理类(各种Manager)、请求队列管理等等。


    4.3 MVP VS MVVM


    上一节关于Model Layer中推荐的文章《iOS应用架构谈 view层的组织和调用方案》对MVC和MVVM都做了非常详细的讨论,是一篇非常不错的文章,推荐各位阅读,那么本节就来说说MVP,以及我为什么更倾向于选择MVP作为App架构设计中的设计框架。


    回顾下在本章一开始祭出的MVP以及MVVM两张图,两者之间有什么不同?


    MVVM的VM(View Model)到V(View),比MVP的P(Presenter)到V(View),多了数据绑定。也就是


    MVP:是MVC的变种,其中Model和View的定义与MVC的一致,不同点在于:MVC的Controller是管理一组Model与View之间交互逻辑,是一个管理者;而Presenter(展示者)则是Model于View之间的连接者,针对特定模块的View提供对应的格式化的Model数据,将View中的行为反馈到Model中。所以MVC中的Controller一般会管理一个或多个Model和一个或多个View,而Presenter则是 M-P-V 一对一,有更细的粒度和更好的解耦。


    从MVP的定义,你会发现MVP与MVVM极其相似,Presenter与View Model扮演的角色基本没有差别,除了前面所说到绑定机制。但绑定机制既有很明显的强大优点——自动连接View和Model,也有很明显的缺点——更高的耦合度,更复杂的代码逻辑;但让人感叹命运无常的是:MVVM随着ReativeCocoa而在iOS平台炙手可热,而iOS平台上甚少有人提及的MVP,在Android平台却几乎成了标准(Android5.0引入了数据绑定支持,MVVM会在Android平台有新的发展)。


    我为什么倾向于MVP?不过是相比于MVVM双向绑定的便利,我更希望我的App设计中有更强的灵活性和扩展性。没有完美的架构设计模式,只有适用于你的App业务场景和团队的设计模式。比如数据逻辑并不复杂、更注重视觉展示的应用,原始的MVC往往是最优解。所有的MVC衍生出的变种,无非是为了Solve The Problem。


    4.4 架构设计模式应用


    无论MVC、MVP还是MVVM,都是指导我们进行架构设计的模式,并非可以生搬硬套的;而且在实际的应用中,对于这些设计模式总会有不同的理解,并且需要根据项目需求进行必要的调整;更为重要的是在我们App的架构设计中,处理好Model-View-Controller之间的关系只是基础,最主要的挑战来自于复杂的业务逻辑和场景,这才是体现一个架构师能力所在。


    唐巧前不久写的一篇文章《被误解的MVC和被神化的MVVM》对MVC和MVVM的实践的讨论应该是体现了现在移动端主流架构思想,其中对网络请求层、ViewModel 层、Service 层、Storage 层等其它类的提取设计,才决定了一个App架构设计的优劣。


    对于架构设计,我准备在下一篇文章,结合本人在iOS/Android两端的设计经验,做个深入的讨论,并给出自己的设计范例,供各位讨论参考。这里先抛出几个在架构设计中最常思考的点,作为下一篇文章的引子:


    1) 架构是为了解耦,越松的耦合就代表越多的份层,但人的思维总是更愿意接受直线思维,怎么解决这个矛盾?


    2) 在一个App中,统一(一致)的架构设计能够让逻辑代码更健壮,更有利于团队成员间的沟通和项目维护,但如何解决其和灵活性之间的矛盾?


    3) 架构设计是否只包含逻辑分层?需要设计数据流和多线程么?


    4) 设计模式中的几大原则;


    5 总结


    以上四个章节,先从UI整体出发,到剖析UIView几点重要机制,接着讨论怎么用好VC这个UI中重要的管理角色,最后则漫谈了MVC/MVVM/MVP几个架构设计模式的异同和实践应用,想通过以点带面,让我们在关注了具体实现之后,能够脱离出来,从俯视下我们App开发更为整体核心的部分。

    展开全文
  • // 使用 CancelToken 退出一个Axios事件 var CancelToken = Axios.CancelToken var source = CancelToken.source() if (state.loading && state.songList.length > 0) { console.log('cancel') source.cancel...

    看了vuex,看到了这样的应用,贴在这里,以后常看

    import Vue from 'vue'
    import Vuex from 'vuex'
    import Axios from 'axios'
    import api from '../api'
    Vue.use(Vuex)
    
    const store = new Vuex.Store({
      state: {
        audio: {
          'id': 0,
          'name': '歌曲名称',
          'singer': '演唱者',
          'albumPic': '/static/placeholder_disk_play_program.png',
          'location': '',
          'album': ''
        },
        lyric: '',
        currentIndex: 0, // 当前播放的歌曲位置
        playing: false, // 是否正在播放
        loading: false, // 是否正在加载中
        showDetail: false,
        songList: [],    // 播放列表
        currentTime: 0,
        tmpCurrentTime: 0,
        durationTime: 0,
        bufferedTime: 0,
        change: false   // 判断是更改的时间还是播放的时间
      },
      getters: {
        audio: state => state.audio,
        playing: state => state.playing,
        loading: state => state.loading,
        showDetail: state => state.showDetail,
        durationTime: state => state.durationTime,
        currentIndex: state => state.currentIndex,
        bufferedTime: state => state.bufferedTime,
        tmpCurrentTime: state => state.tmpCurrentTime,
        songList: state => state.songList,
        change: state => state.change,
        currentTime: state => state.currentTime,
        prCurrentTime: state => {
          return state.currentTime / state.durationTime * 100
        },
        prBufferedTime: state => {
          return state.bufferedTime / state.durationTime * 100
        }
      },
      mutations: {
        play (state) {
          state.playing = true
        },
        pause (state) {
          state.playing = false
        },
        toggleDetail (state) {
          state.showDetail = !state.showDetail
        },
        setAudio (state) {
          state.audio = state.songList[state.currentIndex - 1]
        },
        setAudioIndex (state, index) {
          state.audio = state.songList[index]
          state.currentIndex = index + 1
        },
        removeAudio (state, index) {
          state.songList.splice(index, 1)
          if (index === state.songList.length) {
            index--
          }
          state.audio = state.songList[index]
          state.currentIndex = index + 1
          if (state.songList.length === 0) {
            state.audio = {
              'id': 0,
              'name': '歌曲名称',
              'singer': '演唱者',
              'albumPic': '/static/player-bar.png',
              'location': '',
              'album': ''
            }
            state.playing = false
          }
        },
        setChange (state, flag) {
          state.change = flag
        },
        setLocation (state, location) {
          state.audio.location = location
        },
        updateCurrentTime (state, time) {
          state.currentTime = time
        },
        updateDurationTime (state, time) {
          state.durationTime = time
        },
        updateBufferedTime (state, time) {
          state.bufferedTime = time
        },
        changeTime (state, time) {
          state.tmpCurrentTime = time
        },
        openLoading (state) {
          state.loading = true
        },
        closeLoading (state) {
          state.loading = false
        },
        resetAudio (state) {
          state.currentTime = 0
        },
        playNext (state) { // 播放下一曲
          state.currentIndex++
          if (state.currentIndex > state.songList.length) {
            state.currentIndex = 1
          }
          state.audio = state.songList[state.currentIndex - 1]
        },
        playPrev (state) { // 播放上一曲
          state.currentIndex--
          if (state.currentIndex < 1) {
            state.currentIndex = state.songList.length
          }
          state.audio = state.songList[state.currentIndex - 1]
        },
        addToList (state, songs) {
          var items = Array.prototype.concat.call(songs)
          items.forEach(item => {
            var flag = false
            state.songList.forEach(function (element, index) { // 检测歌曲重复
              if (element.id === item.id) {
                flag = true
                state.currentIndex = index + 1
              }
            })
            if (!flag) {
              state.songList.push(item)
              state.currentIndex = state.songList.length
            }
          })
        },
        setLrc (state, lrc) {
          state.lyric = lrc
        }
      },
      // 异步的数据操作
      actions: {
        getSong ({commit, state}, id) {
          // 使用 CancelToken 退出一个Axios事件
          var CancelToken = Axios.CancelToken
          var source = CancelToken.source()
          if (state.loading && state.songList.length > 0) {
            console.log('cancel')
            source.cancel()
          }
          commit('openLoading')
          Axios.get(api.getSong(id)).then(res => {
            // 统一数据模型,方便后台接口的改变
            var url = res.data.data[0].url
            commit('setAudio')
            commit('setLocation', url)
          })
          .catch((error) => {     // 错误处理
            console.log(error)
            window.alert('获取歌曲信息出错!')
          })
        }
      }
    })
    export default store
    

    文件引用

    import { mapGetters } from 'vuex'
    
    computed: {
          ...mapGetters([
            'songList',
            'showDetail'
          ])
        }

     

    展开全文
  • 一个好的前端页面当然需要一个好的公共部分,提取了简单的css和js,记录日常   一个小小的base.scss @mixin fz($font-size) { font-size: $font-size; [data-dpr="2"] &amp; { font-size: $...
  • “这次我们来看他一个页面渲染器。” “页面渲染器?你是说像谷歌、火狐浏览器那样,将html文件从网上抓取下来,然后把页面展现给用户的那种渲染器吗?” “没错,当时老马只做了文本渲染和图片渲
  • 本章节,将示例如何快速开发一个简单好看的控制页面。[1、了解机智云开源框架]在开发一个简单好看的APP控制页面前,先来了解一下机智云开源框架包里面都有些什么?首先,下载一个Andriod版本的机智云开源框架包,...
  • 控制器A 跳转 控制器B 已经写好 , 即A-&gt;B 现在需要在中间加一页C,改成A-&gt;C-&gt;B 但是A.m里面有大量的方法,代码,属性传值,将这些直接复制到C里很不方便   比如原来A页面里buttonA的点击...
  • 安装上述几软件后 总结下:五步 1、创建新的工程 2、创建建applicatiom.yml 3、创建entity层 4、创建respository层 5、创建Controller层 1、创建新的工程 给定工程名以及包名 2、选中web,jdbc,...
  • 编辑推荐:本文来自于简书,概要如何对页面渲染进行任务划分?要如何执行才能实现最优效率? 实现在每张图片下载完成...”“这次我们来看他一个页面渲染器。”“页面渲染器?你是说像谷歌、火狐浏览器那样,将htm...
  • Java 的一个博客项目 多级评论怎么 关键是如何页面上显示 最好是能给我一个小例子看下
  • 在eclipse中做好登录页面之后,所有的controller什么的都已经写好了,现在想要重新将登录页面在首页显示,做成弹出框的形式,但是又不想再改java代码,这时可以用$.load()来使登录页面在首页中弹出。 首页jsp代码中...
  • //github.com/wangkaiwd/simple-vue-routervue-router是开发vue项目中必不可少的依赖,为了能更的理解其实现原理,而源码阅读起来又过于复杂和枯燥,笔者这里实现一个简易版本的vue-rouer,帮助自己来更的理解...
  • 毫无疑问,app描述页面不仅仅是描述,它更多是一个销售页面,开发者不能面对面向用户营销自己的产品,因此app描述页面的重要性就凸显出来了。如果你的应用是免费的,用户可能匆匆一眼扫过你的描述,因为是免费的,...
  • 写一个简单的登录页面!!!(html)

    千次阅读 2020-12-01 22:07:45
    写一个简单的登录页面!!! 大家,我是伍柒. 今天我又双叒叕发现了一个好东西. 那就是用html做一个登录页面 那,该如何做呢? 首先
  • 我们这学期的系统分析与设计课程的大作业要求做一个电影购票系统,组内成员都没有完整的Web网站开发经验,所以大家都是边做边学的状态。而我由于在之前分布式的课程上被Spring配置弄得死去活来,所以无奈只能利用...
  • 事先用安卓写好一个应用,现在想在appcan中显示事先写好的安卓应用的页面,该如何在appcan中调用并且显示出安卓应用的页面
  • 有助于目录的生成如何改变文本的样式插入链接与图片如何插入一段漂亮的代码片生成一个适合你的列表创建一个表格设定内容居中、居左、居右SmartyPants创建一个自定义列表如何创建一个注脚注释也是必不可少的KaTeX数学...
  • 这期我们要在一个FrameLayout中写一个TabLayout页面 在下面代码中FrameLayout一共有三个页面(fragment1 fragment2 fragment3) 我们要在FrameLayout的fragment1页面中写一个拥有三个fragment...第一步 我们要先写好...
  • 但是,如果您需要构建是一个中大型单页应用,您很可能会考虑如何地在组件外部管理状态,Vuex 将会成为自然而然的选择。 核心概念主要有这些 State Vuex 使用单一状态树——是的,用一个对象就包含了全
  • 下面,我将介绍如何快速地建一个简单漂亮的liferay theme. 工具:liferay IDE 3.0, liferay SDK 6.2,liferay portal 6.2(至于怎么在哪里下载,请参考前面教程) 以上工具准备了以后,就可以在网上随便找一个...
  • 我在这里告诉大家一个小窍门,是我的神技,那就是在电脑上写好移动端页面如何在手机上快速查看呢?多余的话不说,直接说技能点。 1,这里首先找到自己的ip地址然后把自己的ip地址复制到这里,就是自己 在电脑上...
  • 这里yarn webpack后 找到入口文件main.js 根据import的导入,根据rules中配置的/md$/ 加载到md文件 然后进行自定义处理,这时候在控制台上打印的文件输入源处理后返回; 注意这里报了错了 因为loader管道需要...
  • 我们这学期的系统分析与设计课程的大作业要求做一个电影购票系统,组内成员都没有完整的Web网站开发经验,所以大家都是边做边学的状态。而我由于在之前分布式的课程上被Spring配置弄得死去活来,所以无奈只能利用...
  • 我觉得页面控制器的优点还是很明显的,感觉非常适合 php,想尝试着点,但因为一直以来使用的都是前端控制器,所以思路上转不过来 希望有大神能指点下,最好是有代码,例子 php 和 java 都可以 或者有哪个...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 1,869
精华内容 747
关键字:

如何写好一个页面