自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

程序員必知的前端演進(jìn)史

開(kāi)發(fā) 后端 前端
對(duì)于復(fù)雜的應(yīng)用來(lái)說(shuō),其架構(gòu)肯定不是只有純MVP或者純MVVM這么簡(jiǎn)單的。如果一個(gè)應(yīng)用混合了MVVM、MVP和MVC,那么他也變成了MVC——因?yàn)樗苯釉L問(wèn)了Model層。但是如果細(xì)分來(lái)看,只有訪問(wèn)了Model層的那一部分才是MVC模式。

細(xì)細(xì)整理了過(guò)去接觸過(guò)的那些前端技術(shù),發(fā)現(xiàn)前端演進(jìn)是段特別有意思的歷史。人們總是在過(guò)去就做出未來(lái)需要的框架,而現(xiàn)在流行的是過(guò)去發(fā)明過(guò)的。如,響應(yīng)式設(shè)計(jì)不得不提到的一個(gè)缺點(diǎn)是:它只是將原本在模板層做的事,放到了樣式(CSS)層來(lái)完成。

復(fù)雜度同力一樣不會(huì)消失,也不會(huì)憑空產(chǎn)生,它總是從一個(gè)物體轉(zhuǎn)移到另一個(gè)物體或一種形式轉(zhuǎn)為另一種形式。

如果六、七年前的移動(dòng)網(wǎng)絡(luò)速度和今天一樣快,那么直接上的技術(shù)就是響應(yīng)式設(shè)計(jì),APP、SPA就不會(huì)流行得這么快。盡管我們可以預(yù)見(jiàn)未來(lái)這些領(lǐng)域會(huì)變得更好,但是更需要的是改變現(xiàn)狀。改變現(xiàn)狀的同時(shí)也需要預(yù)見(jiàn)未來(lái)的需求。

[[158148]]

(題圖來(lái)自:cuelogic.com)

什么是前端?

維基百科是這樣說(shuō)的:前端(front-end)和后端(back-end)是描述進(jìn)程開(kāi)始和結(jié)束的通用詞匯。前端作用于采集輸入信息,后端進(jìn)行處理。計(jì)算機(jī)程序的界面樣式,視覺(jué)呈現(xiàn)屬于前端。

這種說(shuō)法給人一種很模糊的感覺(jué),但是他說(shuō)得又很對(duì),它負(fù)責(zé)視覺(jué)展示。在MVC結(jié)構(gòu)或者M(jìn)VP中,負(fù)責(zé)視覺(jué)顯示的部分只有view層,而今天大多數(shù)所謂的View層已經(jīng)超越了View層。前端是一個(gè)很神奇的概念,但是而今的前端已經(jīng)發(fā)生了很大的變化。

你引入了Backbone、Angluar,你的架構(gòu)變成了MVP、MVVM。盡管發(fā)生了一些架構(gòu)上的變化,但是項(xiàng)目的開(kāi)發(fā)并沒(méi)有因此而發(fā)生變化。這其中涉及到了一些職責(zé)的問(wèn)題,如果某一個(gè)層級(jí)中有太多的職責(zé),那么它是不是加重了一些人的負(fù)擔(dān)?

前端演進(jìn)史

過(guò)去一直想整理一篇文章來(lái)說(shuō)說(shuō)前端發(fā)展的歷史,但是想著這些歷史已經(jīng)被人們所熟知。后來(lái)發(fā)現(xiàn)并非如此,大抵是幸存者偏見(jiàn)——關(guān)注到的都知道這些歷史。

數(shù)據(jù)-模板-樣式混合

在有限的前端經(jīng)驗(yàn)里,我還是經(jīng)歷了那段用Table來(lái)作樣式的年代。大學(xué)期間曾經(jīng)有償幫一些公司或者個(gè)人開(kāi)發(fā)、維護(hù)一些CMS,而Table是當(dāng)時(shí) 幫某個(gè)網(wǎng)站更新樣式接觸到的——ASP.Net(maybe)。當(dāng)時(shí),我們啟動(dòng)這個(gè)CMS用的是一個(gè)名為aspweb.exe的程序。于是,在我的移動(dòng)硬 盤里找到了下面的代碼。

  1. <TABLE cellSpacing=0 cellPadding=0 width=910 align=center border=0
  2.   <TBODY> 
  3.   <TR> 
  4.     <TD vAlign=top width=188><TABLE cellSpacing=0 cellPadding=0 width=184 align=center border=0
  5.         <TBODY> 
  6.         <TR> 
  7.           <TD>[站外圖片上傳中……(9)]</TD></TR> 
  8.         <TR> 
  9.           <TD> 
  10.             <TABLE cellSpacing=0 cellPadding=0 width=184 align=center 
  11.             background=Images/xxx.gif border=0

雖然,我也已經(jīng)在HEAD里找到了現(xiàn)代的雛形——DIV + CSS,然而這仍然是一個(gè)Table的年代。

<LINK href="img/xxx.css" type=text/css rel=stylesheet>

人們一直在說(shuō)前端很難,問(wèn)題是你學(xué)過(guò)么???

人們一直在說(shuō)前端很難,問(wèn)題是你學(xué)過(guò)么???

人們一直在說(shuō)前端很難,問(wèn)題是你學(xué)過(guò)么???

也許,你也一直在說(shuō)CSS不好寫,但是CSS真的不好寫么?人們總在說(shuō)JS很難用,但是你學(xué)過(guò)么?只在需要的時(shí)候才去學(xué),那肯定很難。你不曾花時(shí)間去學(xué)習(xí)一門語(yǔ)言,但是卻能直接寫出可以work的代碼,說(shuō)明他們?nèi)菀咨鲜?/strong>。如果你看過(guò)一些有經(jīng)驗(yàn)的Ruby、Scala、Emacs Lisp開(kāi)發(fā)者寫出來(lái)的代碼,我想會(huì)得到相同的結(jié)論。有一些語(yǔ)言可以讓寫程序的人Happy,但是看的人可能就不Happy了。做事的方法不止一種,但是不是所有的人都要用那種方法去做。

過(guò)去的那些程序員都是真正的全棧程序員,這些程序員不僅僅做了前端的活,還做了數(shù)據(jù)庫(kù)的工作。

  1. Set rs = Server.CreateObject("ADODB.Recordset"
  2. sql = "select id,title,username,email,qq,adddate,content,Re_content,home,face,sex from Fl_Book where ispassed=1 order by id desc" 
  3. rs.open sql, Conn, 11 
  4. fl.SqlQueryNum = fl.SqlQueryNum + 1 

在這個(gè)ASP文件里,它從數(shù)據(jù)庫(kù)里查找出了數(shù)據(jù),然后Render出HTML。如果可以看到歷史版本,那么我想我會(huì)看到有一個(gè)作者將style=”"的代碼一個(gè)個(gè)放到css文件中。

在這里的代碼里也免不了有動(dòng)態(tài)生成JavaScript代碼的方法:

show_other = "<SCRIPT language=javascript>"
show_other = show_other & "function checkform()"
show_other = show_other & "{"
show_other = show_other & "if (document.add.title.value=='')"
show_other = show_other & "{"

請(qǐng)盡情嘲笑,然后再看一段代碼:

 

  1. import React from "react"
  2. import { getData } from "../../common/request"
  3. import styles from "./style.css"
  4.  
  5. export default class HomePage extends React.Component { 
  6.   componentWillMount() { 
  7.     console.log("[HomePage] will mount with server response: "this.props.data.home); 
  8.   } 
  9.  
  10.   render() { 
  11.     let { title } = this.props.data.home; 
  12.  
  13.     return ( 
  14.       <div className={styles.content}> 
  15.         <h1>{title}</h1> 
  16.         <p className={styles.welcomeText}>Thanks for joining!</p> 
  17.       </div> 
  18.     ); 
  19.   } 
  20.  
  21.   static fetchData = function(params) { 
  22.     return getData("/home"); 
  23.   } 

10年前和10年后的代碼,似乎沒(méi)有太多的變化。有所不同的是數(shù)據(jù)層已經(jīng)被獨(dú)立出去了,如果你的component也混合了數(shù)據(jù)層,即直接查詢數(shù)據(jù) 庫(kù)而不是調(diào)用數(shù)據(jù)層接口,那么你就需要好好思考下這個(gè)問(wèn)題。你只是在追隨潮流,還是在改變。用一個(gè)View層更換一個(gè)View層,用一個(gè)Router換一 個(gè)Router的意義在哪?

Model-View-Controller

人們?cè)诓粩嗟胤此歼@其中復(fù)雜的過(guò)程,整理了一些好的架構(gòu)模式,其中不得不提到的是我司Martin Folwer的《企業(yè)應(yīng)用架構(gòu)模式》。該書(shū)中文譯版出版的時(shí)候是2004年,那時(shí)對(duì)于系統(tǒng)的分層是

層次

職責(zé)

表現(xiàn)層

提供服務(wù)、顯示信息、用戶請(qǐng)求、HTTP請(qǐng)求和命令行調(diào)用。

領(lǐng)域?qū)?/p>

邏輯處理,系統(tǒng)中真正的核心。

數(shù)據(jù)層

與數(shù)據(jù)庫(kù)、消息系統(tǒng)、事物管理器和其他軟件包通訊。

化身于當(dāng)時(shí)***的Spring,就是MVC。人們有了iBatis這樣的數(shù)據(jù)持久層框架,即ORM,對(duì)象關(guān)系映射。于是,你的package就會(huì)有這樣的幾個(gè)文件夾:

|____mappers
|____model
|____service
|____utils
|____controller

在mappers這一層,我們所做的莫過(guò)于如下所示的數(shù)據(jù)庫(kù)相關(guān)查詢:

  1. @Insert
  2.         "INSERT INTO users(username, password, enabled) " + 
  3.                 "VALUES (#{userName}, #{passwordHash}, #{enabled})" 
  4. @Options(keyProperty = "id", keyColumn = "id", useGeneratedKeys = true
  5. void insert(User user); 

model文件夾和mappers文件夾都是數(shù)據(jù)層的一部分,只是兩者間的職責(zé)不同,如:

  1. public String getUserName() { 
  2.     return userName; 
  3.  
  4. public void setUserName(String userName) { 
  5.     this.userName = userName; 

而他們***都需要在Controller,又或者稱為ModelAndView中處理:

  1. @RequestMapping(value = {"/disableUser"}, method = RequestMethod.POST) 
  2. public ModelAndView processUserDisable(HttpServletRequest request, ModelMap model) { 
  3.     String userName = request.getParameter("userName"); 
  4.     User user = userService.getByUsername(userName); 
  5.     userService.disable(user); 
  6.     Map<String,User> map = new HashMap<String,User>(); 
  7.     Map <User,String> usersWithRoles= userService.getAllUsersWithRole(); 
  8.     model.put("usersWithRoles",usersWithRoles); 
  9.     return new ModelAndView("redirect:users",map); 

在多數(shù)時(shí)候,Controller不應(yīng)該直接與數(shù)據(jù)層的一部分,而將業(yè)務(wù)邏輯放在Controller層又是一種錯(cuò)誤,這時(shí)就有了Service層,如下圖:

程序員必知之前端演進(jìn)史
Service MVC

然而對(duì)于Domain相關(guān)的Service應(yīng)該放在哪一層,總會(huì)有不同的意見(jiàn):

程序員必知之前端演進(jìn)史
MVC Player

程序員必知之前端演進(jìn)史
MS MVC

Domain(業(yè)務(wù))是一個(gè)相當(dāng)復(fù)雜的層級(jí),這里是業(yè)務(wù)的核心。一個(gè)合理的Controller只應(yīng)該做自己應(yīng)該做的事,它不應(yīng)該處理業(yè)務(wù)相關(guān)的代碼:

 

  1. if (isNewnameEmpty == false && newuser == null){ 
  2.     user.setUserName(newUsername); 
  3.     List<Post> myPosts = postService.findMainPostByAuthorNameSortedByCreateTime(principal.getName()); 
  4.  
  5.     for (int k = 0;k < myPosts.size();k++){ 
  6.         Post post = myPosts.get(k); 
  7.         post.setAuthorName(newUsername); 
  8.         postService.save(post); 
  9.     } 
  10.     userService.update(user); 
  11.     Authentication oldAuthentication = SecurityContextHolder.getContext().getAuthentication(); 
  12.     Authentication authentication = null
  13.     if(oldAuthentication == null){ 
  14.         authentication = new UsernamePasswordAuthenticationToken(newUsername,user.getPasswordHash()); 
  15.     }else
  16.         authentication = new UsernamePasswordAuthenticationToken(newUsername,user.getPasswordHash(),oldAuthentication.getAuthorities()); 
  17.     } 
  18.     SecurityContextHolder.getContext().setAuthentication(authentication); 
  19.     map.clear(); 
  20.     map.put("user",user); 
  21.     model.addAttribute("myPosts", myPosts); 
  22.     model.addAttribute("namesuccess""User Profile updated successfully"); 
  23.     return new ModelAndView("user/profile", map); 

我們?cè)贑ontroller層應(yīng)該做的事是:

  • 處理請(qǐng)求的參數(shù)

  • 渲染和重定向

  • 選擇Model和Service

  • 處理Session和Cookies

業(yè)務(wù)是善變的,昨天我們可能還在和對(duì)手競(jìng)爭(zhēng)誰(shuí)先推出新功能,但是今天可能已經(jīng)合并了。我們很難預(yù)見(jiàn)業(yè)務(wù)變化,但是我們應(yīng)該能預(yù)見(jiàn)Controller是不容易變化的。在一些設(shè)計(jì)里面,這種模式就是Command模式。

View層是一直在變化的層級(jí),人們的品味一直在更新,有時(shí)甚至可能因?yàn)楦?jìng)爭(zhēng)對(duì)手而產(chǎn)生變化。在已經(jīng)取得一定市場(chǎng)的情況下,Model- Service-Controller通常都不太會(huì)變動(dòng),甚至不敢變動(dòng)。企業(yè)意識(shí)到創(chuàng)新的兩面性,要么帶來(lái)死亡,要么占領(lǐng)更大的市場(chǎng)。但是對(duì)手通常都比你 想象中的更聰明一些,所以這時(shí)開(kāi)創(chuàng)新的業(yè)務(wù)是一個(gè)更好的選擇。

高速發(fā)展期的企業(yè)和發(fā)展初期的企業(yè)相比,更需要前端開(kāi)發(fā)人員。在用戶基數(shù)不夠、業(yè)務(wù)待定的情形中,View只要可用并美觀就行了,這時(shí)可能就會(huì)有大量的業(yè)務(wù)代碼放在View層:

  1. <c:choose> 
  2.     <c:when test="${ hasError }"
  3.     <p class="prompt-error"
  4.         ${errors.username} ${errors.password} 
  5.     </p> 
  6.     </c:when> 
  7.     <c:otherwise> 
  8.     <p class="prompt"
  9.         Woohoo, User <span class="username">${user.userName}</span> has been created successfully! 
  10.     </p> 
  11.     </c:otherwise> 
  12. </c:choose> 

不同的情形下,人們都會(huì)對(duì)此有所爭(zhēng)議,但只要符合當(dāng)前的業(yè)務(wù)便是***的選擇。作為一個(gè)前端開(kāi)發(fā)人員,在過(guò)去我需要修改JSP、PHP文件,這期間我需要去了解這些Template:

  1. {foreach $lists as $v} 
  2. <li itemprop="breadcrumb"><span{if(newest($v['addtime'],24))} style="color:red"{/if}>[{fun date('Y-m-d',$v['addtime'])}]</span><a href="{$v['url']}" style="{$v['style']}" target="_blank">{$v['title']}</a></li> 
  3. {/foreach} 

有時(shí)像Django這一類,自稱為Model-Template-View的框架,更容易讓人理解其意圖:

  1. {% for blog_post in blog_posts.object_list %} 
  2. {% block blog_post_list_post_title %} 
  3. <section class="section--center mdl-grid mdl-grid--no-spacing mdl-shadow--2dp mdl-cell--11-col blog-list"
  4. {% editable blog_post.title %} 
  5. <div class="mdl-card__title mdl-card--border mdl-card--expand"
  6.     <h2 class="mdl-card__title-text"
  7.         <a href="{{ blog_post.get_absolute_url }}"  itemprop="headline">{{ blog_post.title }} › </a> 
  8.     </h2> 
  9. </div> 
  10. {% endeditable %} 
  11. {% endblock %} 

作為一個(gè)前端人員,我們真正在接觸的是View層和Template層,但是MVC并沒(méi)有說(shuō)明這些。

從桌面版到移動(dòng)版

Wap出現(xiàn)了,并帶來(lái)了更多的挑戰(zhàn)。隨后,分辨率從1024×768變成了176×208,開(kāi)發(fā)人員不得不面臨這些挑戰(zhàn)。當(dāng)時(shí)所需要做的僅僅是修改View層,而View層隨著iPhone的出現(xiàn)又發(fā)生了變化。

程序員必知之前端演進(jìn)史
WAP 網(wǎng)站

這是一個(gè)短暫的歷史,PO還需要為手機(jī)用戶制作一個(gè)怎樣的網(wǎng)站?于是他們把桌面版的網(wǎng)站搬了過(guò)去變成了移動(dòng)版。由于網(wǎng)絡(luò)的原因,每次都需要重新加載頁(yè)面,這帶來(lái)了不佳的用戶體驗(yàn)。

幸運(yùn)的是,人們很快意識(shí)到了這個(gè)問(wèn)題,于是就有了SPA。如果當(dāng)時(shí)的移動(dòng)網(wǎng)絡(luò)速度可以更快的話,我想很多SPA框架就不存在了。

先說(shuō)說(shuō)jQuery Mobile,在那之前,先讓我們來(lái)看看兩個(gè)不同版本的代碼,下面是一個(gè)手機(jī)版本的blog詳情頁(yè):

  1. <ul data-role="listview" data-inset="true" data-splittheme="a"
  2.     {% for blog_post in blog_posts.object_list %} 
  3.         <li> 
  4.         {% editable blog_post.title blog_post.publish_date %} 
  5.         <h2 class="blog-post-title"><a href="{% url "blog_post_detail" blog_post.slug %}">{{ blog_post.title }}</a></h2> 
  6.         <em class="since">{% blocktrans with sometime=blog_post.publish_date|timesince %}{{ sometime }} ago{% endblocktrans %}</em> 
  7.         {% endeditable %} 
  8.         </li> 
  9.     {% endfor %} 
  10. </ul> 

而下面是桌面版本的片段:

  1. {% for blog_post in blog_posts.object_list %} 
  2. {% block blog_post_list_post_title %} 
  3. {% editable blog_post.title %} 
  4. <h2> 
  5.     <a href="{{ blog_post.get_absolute_url }}">{{ blog_post.title }}</a> 
  6. </h2> 
  7. {% endeditable %} 
  8. {% endblock %} 
  9. {% block blog_post_list_post_metainfo %} 
  10. {% editable blog_post.publish_date %} 
  11. <h6 class="post-meta"
  12.     {% trans "Posted by" %}: 
  13.     {% with blog_post.user as author %} 
  14.     <a href="{% url "blog_post_list_author" author %}">{{ author.get_full_name|default:author.username }}</a> 
  15.     {% endwith %} 
  16.     {% with blog_post.categories.all as categories %} 
  17.     {% if categories %} 
  18.     {% trans "in" %} 
  19.     {% for category in categories %} 
  20.     <a href="{% url "blog_post_list_category" category.slug %}">{{ category }}</a>{% if not forloop.last %}, {% endif %} 
  21.     {% endfor %} 
  22.     {% endif %} 
  23.     {% endwith %} 
  24.     {% blocktrans with sometime=blog_post.publish_date|timesince %}{{ sometime }} ago{% endblocktrans %} 
  25. </h6> 
  26. {% endeditable %} 
  27. {% endblock %} 

人們所做的只是重載View層。這也是一個(gè)有效的SEO策略,上面這些代碼是我博客過(guò)去的代碼。對(duì)于桌面版和移動(dòng)版都是不同的模板和不同的JS、CSS。

程序員必知之前端演進(jìn)史
移動(dòng)版網(wǎng)頁(yè)

在這一時(shí)期,桌面版和移動(dòng)版的代碼可能在同一個(gè)代碼庫(kù)中。他們使用相同的代碼,調(diào)用相同的邏輯,只是View層不同了。但是,每次改動(dòng)我們都要維護(hù)兩份代碼。

隨后,人們發(fā)現(xiàn)了一種更友好的移動(dòng)版應(yīng)用——APP。

APP與過(guò)渡期API

這是一個(gè)艱難的時(shí)刻,過(guò)去我們的很多API都是在原來(lái)的代碼庫(kù)中構(gòu)建的,即桌面版和移動(dòng)版一起。我們已經(jīng)在這個(gè)代碼庫(kù)中開(kāi)發(fā)了越來(lái)越多的功能,系統(tǒng)開(kāi)發(fā)變得臃腫。如《Linux/Unix設(shè)計(jì)思想》中所說(shuō),這是一個(gè)偉大的系統(tǒng),但是它臃腫而又緩慢。

我們是選擇重新開(kāi)發(fā)一個(gè)結(jié)合***和第二系統(tǒng)的***特性的第三個(gè)系統(tǒng),還是繼續(xù)臃腫下去。我想你已經(jīng)有答案了。隨后我們就有了APP API,構(gòu)建出了博客的APP。

程序員必知之前端演進(jìn)史
應(yīng)用

最開(kāi)始,人們?cè)絹?lái)越喜歡用APP,因?yàn)榕c移動(dòng)版網(wǎng)頁(yè)相比,其響應(yīng)速度更快,而且更流暢。對(duì)于服務(wù)器來(lái)說(shuō),也是一件好事,因?yàn)檎?qǐng)求變少了。

但是并非所有的人都會(huì)下載APP——有時(shí)只想看看上面有沒(méi)有需要的東西。對(duì)于剛需不強(qiáng)的應(yīng)用,人們并不會(huì)下載,只會(huì)訪問(wèn)網(wǎng)站。

有了APP API之后,我們可以向網(wǎng)頁(yè)提供API,我們就開(kāi)始設(shè)想要有一個(gè)好好的移動(dòng)版。

過(guò)渡期SPA

Backbone誕生于2010年,和響應(yīng)式設(shè)計(jì)出現(xiàn)在同一個(gè)年代里,但他們似乎在同一個(gè)時(shí)代里火了起來(lái)。如果CSS3早點(diǎn)流行開(kāi)來(lái),似乎就沒(méi)有Backbone啥事了。不過(guò)移動(dòng)網(wǎng)絡(luò)還是限制了響應(yīng)式的流行,只是在今天這些都有所變化。

我們用Ajax向后臺(tái)請(qǐng)求API,然后Mustache Render出來(lái)。因?yàn)镴avaScript在模塊化上的缺陷,所以我們就用Require.JS來(lái)進(jìn)行模塊化。

下面的代碼就是我在嘗試對(duì)我的博客進(jìn)行SPA設(shè)計(jì)時(shí)的代碼:

 

  1. define([ 
  2.     'zepto'
  3.     'underscore'
  4.     'mustache'
  5.     'js/ProductsView'
  6.     'json!/configure.json'
  7.     'text!/templates/blog_details.html'
  8.     'js/renderBlog' 
  9. ],function($, _, Mustache, ProductsView, configure, blogDetailsTemplate, GetBlog){ 
  10.  
  11.     var BlogDetailsView = Backbone.View.extend ({ 
  12.         el: $("#content"), 
  13.  
  14.         initialize: function () { 
  15.             this.params = '#content'
  16.         }, 
  17.  
  18.         getBlog: function(slug) { 
  19.             var getblog = new GetBlog(this.params, configure['blogPostUrl'] + slug, blogDetailsTemplate); 
  20.             getblog.renderBlog(); 
  21.         } 
  22.     }); 
  23.  
  24.     return BlogDetailsView; 
  25. }); 

從API獲取數(shù)據(jù),結(jié)合Template來(lái)Render出Page。但是這無(wú)法改變我們需要Client Side Render和Server Side Render的兩種Render方式,除非我們可以像淘寶一樣不需要考慮SEO——因?yàn)樗荒敲匆揽克阉饕鎺?lái)流量。

這時(shí),我們還是基于類MVC模式。只是數(shù)據(jù)的獲取方式變成了Ajax,我們就犯了一個(gè)錯(cuò)誤——將大量的業(yè)務(wù)邏輯放在前端。這時(shí)候我們已經(jīng)不能再?gòu)腣iew層直接訪問(wèn)Model層,從安全的角度來(lái)說(shuō)有點(diǎn)危險(xiǎn)。

如果你的View層還可以直接訪問(wèn)Model層,那么說(shuō)明你的架構(gòu)還是MVC模式。之前我在Github上構(gòu)建一個(gè)Side Project的時(shí)候直接用View層訪問(wèn)了Model層,由于Model層是一個(gè)ElasticSearch的搜索引擎,它提供了JSON API,這使得我要在View層處理數(shù)據(jù)——即業(yè)務(wù)邏輯。將上述的JSON API放入Controller,盡管會(huì)加重這一層的復(fù)雜度,但是業(yè)務(wù)邏輯就不再放置于View層。

如果你在你的View層和Model層總有一層接口,那么你采用的就是MVP模式——MVC模式的衍生(PS:為了區(qū)別別的事情,總會(huì)有人取個(gè)表意的名稱)。

一夜之前,我們又回到了過(guò)去。我們離開(kāi)了JSP,將View層變成了Template與Controller。而原有的Services層并不是只承擔(dān)其原來(lái)的責(zé)任,這些Services開(kāi)始向ViewModel改變。

一些團(tuán)隊(duì)便將Services抽成多個(gè)Services,美其名為微服務(wù)。傳統(tǒng)架構(gòu)下的API從下圖

程序員必知之前端演進(jìn)史
API Gateway

變成了直接調(diào)用的微服務(wù):

程序員必知之前端演進(jìn)史
Micro Services

對(duì)于后臺(tái)開(kāi)發(fā)者來(lái)說(shuō),這是一件大快人心的大好事,但是對(duì)于應(yīng)用端/前端來(lái)說(shuō)并非如此。調(diào)用的服務(wù)變多了,在應(yīng)用程序端進(jìn)行功能測(cè)試變得更復(fù)雜,需要Mock的API變多了。

Hybird與ViewModel

這時(shí)候遇到問(wèn)題的不僅僅只在前端,而在App端,小的團(tuán)隊(duì)已經(jīng)無(wú)法承受開(kāi)發(fā)成本。人們更多的注意力放到了Hybird應(yīng)用上。Hybird應(yīng)用解決了一些小團(tuán)隊(duì)在開(kāi)發(fā)初期遇到的問(wèn)題,這部分應(yīng)用便交給了前端開(kāi)發(fā)者。

前端開(kāi)發(fā)人員先熟悉了單純的JS + CSS + HTML,又熟悉了Router + PageView + API的結(jié)構(gòu),現(xiàn)在他們又需要做手機(jī)APP。這時(shí)候只好用熟悉的jQuer Mobile + Cordova。

隨后,人們先從Cordova + jQuery Mobile,變成了Cordova + Angular的 Ionic。在那之前,一些團(tuán)隊(duì)可能已經(jīng)用Angular代換了Backbone。他們需要更好的交互,需要data binding。

接著,我們可以直接將我們的Angular代碼從前端移到APP,比如下面這種博客APP的代碼:

 

  1. .controller('BlogCtrl', function ($scope, Blog) { 
  2.   $scope.blogs = null
  3.   $scope.blogOffset = 0
  4.   // 
  5.   $scope.doRefresh = function () { 
  6.     Blog.async('https://www.phodal.com/api/v1/app/?format=json').then(function (results) { 
  7.       $scope.blogs = results.objects; 
  8.     }); 
  9.     $scope.$broadcast('scroll.refreshComplete'); 
  10.     $scope.$apply() 
  11.   }; 
  12.  
  13.   Blog.async('https://www.phodal.com/api/v1/app/?format=json').then(function (results) { 
  14.     $scope.blogs = results.objects; 
  15.   }); 
  16.  
  17.   $scope.loadMore = function() { 
  18.     $scope.blogOffset = $scope.blogOffset + 1
  19.     Blog.async('https://www.phodal.com/api/v1/app/?limit=10&offset='+ $scope.blogOffset * 20 +  '&format=json').then(function (results) { 
  20.       Array.prototype.push.apply($scope.blogs, results.objects); 
  21.       $scope.$broadcast('scroll.infiniteScrollComplete'); 
  22.     }) 
  23.   }; 
  24. }) 

結(jié)果時(shí)間軸又錯(cuò)了,人們總是超前一個(gè)時(shí)期做錯(cuò)了一個(gè)在未來(lái)是正確的決定。人們遇到了網(wǎng)頁(yè)版的用戶授權(quán)問(wèn)題,于是發(fā)明了JWT——Json Web Token。

然而,由于WebView在一些早期的Android手機(jī)上出現(xiàn)了性能問(wèn)題,人們開(kāi)始考慮替換方案。接著出現(xiàn)了兩個(gè)不同的解決方案:

  1. React Native

  2. 新的WebView——Crosswalk

開(kāi)發(fā)人員開(kāi)始?xì)g呼React Native這樣的框架。但是,他們并沒(méi)有預(yù)見(jiàn)到人們正在厭惡APP,APP在我們的迭代里更新著,可能是一星期,可能是兩星期,又或者是一個(gè)月。誰(shuí)說(shuō)APP內(nèi)自更新不是一件壞事,但是APP的提醒無(wú)時(shí)無(wú)刻不在干擾著人們的生活,噪聲越來(lái)越多。不要和用戶爭(zhēng)奪他們手機(jī)的使用權(quán)

一次構(gòu)建,跨平臺(tái)運(yùn)行

在我們需要學(xué)習(xí)C語(yǔ)言的時(shí)候,GCC就有了這樣的跨平臺(tái)編譯。

在我們開(kāi)發(fā)桌面應(yīng)用的時(shí)候,QT有就這樣的跨平臺(tái)能力。

在我們構(gòu)建Web應(yīng)用的時(shí)候,Java有這樣的跨平臺(tái)能力。

在我們需要開(kāi)發(fā)跨平臺(tái)應(yīng)用的時(shí)候,Cordova有這樣的跨平臺(tái)能力。

現(xiàn)在,React這樣的跨平臺(tái)框架又出現(xiàn)了,而響應(yīng)式設(shè)計(jì)也是跨平臺(tái)式的設(shè)計(jì)。

響應(yīng)式設(shè)計(jì)不得不提到的一個(gè)缺點(diǎn)是:他只是將原本在模板層做的事,放到了樣式(CSS)層。你還是在針對(duì)著不同的設(shè)備進(jìn)行設(shè)計(jì),兩種沒(méi)有什么多大的不同。復(fù)雜度不會(huì)消失,也不會(huì)憑空產(chǎn)生,它只會(huì)從一個(gè)物體轉(zhuǎn)移到另一個(gè)物體或一種形式轉(zhuǎn)為另一種形式。

React,將一小部分復(fù)雜度交由人來(lái)消化,將另外一部分交給了React自己來(lái)消化。在用Spring MVC之前,也許我們還在用CGI編程,而Spring降低了這部分復(fù)雜度,但是這和React一樣降低的只是新手的復(fù)雜度。在我們不能以某種語(yǔ)言的方式 寫某相關(guān)的代碼時(shí),這會(huì)帶來(lái)諸多麻煩。

RePractise

如果你是一只辛勤的蜜蜂,那么我想你應(yīng)該都玩過(guò)上面那些技術(shù)。你是在練習(xí)前端的技術(shù),還是在RePractise?如果你不花點(diǎn)時(shí)間整理一下過(guò)去,順便預(yù)測(cè)一下未來(lái),那么你就是在白搭。

前端的演進(jìn)在這一年特別快,Ruby On Rails也在一個(gè)合適的年代里出現(xiàn),在那個(gè)年代里也流行得特別快。RoR開(kāi)發(fā)效率高的優(yōu)勢(shì)已然不再突顯,語(yǔ)法靈活性的副作用就是運(yùn)行效率降低,同時(shí)后期維護(hù)難——每個(gè)人元編程了自己。

如果不能把Controller、Model Mapper變成ViewModel,又或者是Micro Services來(lái)解耦,那么ES6 + React只是在現(xiàn)在帶來(lái)更高的開(kāi)發(fā)效率。而所謂的高效率,只是相比較而意淫出來(lái)的,因?yàn)樗皇且粚覸iew層。將Model和Controller再加 回View層,以后再拆分出來(lái)?

現(xiàn)有的結(jié)構(gòu)只是將View層做了View層應(yīng)該做的事。

首先,你應(yīng)該考慮的是一種可以讓View層解耦于Domain或者Service層。今天,桌面、平板、手機(jī)并不是唯一用戶設(shè)備,雖然你可能在明年 統(tǒng)一了這三個(gè)平臺(tái),現(xiàn)在新的設(shè)備的出現(xiàn)又將設(shè)備分成兩種類型——桌面版和手機(jī)版。一開(kāi)始桌面版和手機(jī)版是不同的版本,后來(lái)你又需要合并這兩個(gè)設(shè)備。

其次,你可以考慮用混合Micro Services優(yōu)勢(shì)的Monolithic Service來(lái)分解業(yè)務(wù)。如果可以舉一個(gè)成功的例子,那么就是Linux,一個(gè)混合內(nèi)核的“Service”。

***,Keep Learning。我們總需要在適當(dāng)?shù)臅r(shí)候做出改變,盡管我們覺(jué)得一個(gè)Web應(yīng)用代碼庫(kù)中含桌面版和移動(dòng)版代碼會(huì)很不錯(cuò),但是在那個(gè)時(shí)候需要做出改變。

對(duì)于復(fù)雜的應(yīng)用來(lái)說(shuō),其架構(gòu)肯定不是只有純MVP或者純MVVM這么簡(jiǎn)單的。如果一個(gè)應(yīng)用混合了MVVM、MVP和MVC,那么他也變成了MVC——因?yàn)樗苯釉L問(wèn)了Model層。但是如果細(xì)分來(lái)看,只有訪問(wèn)了Model層的那一部分才是MVC模式。

模式,是人們對(duì)于某個(gè)解決方案的描述。在一段代碼中可能有各種各樣的設(shè)計(jì)模式,更何況是架構(gòu)。

責(zé)任編輯:王雪燕 來(lái)源: github
相關(guān)推薦

2015-11-30 11:01:34

前端程序員歷史

2019-01-30 14:14:16

LinuxUNIX操作系統(tǒng)

2023-09-12 11:25:15

2023-10-26 18:05:37

Git命令差異

2017-08-03 14:25:13

Python陷阱與缺陷

2012-06-28 14:01:30

Java程序員排序

2018-07-11 14:04:53

Python陷阱缺陷

2020-10-26 15:20:05

架構(gòu)運(yùn)維技術(shù)

2020-05-13 11:20:57

MySQL規(guī)范數(shù)據(jù)庫(kù)

2015-05-19 14:34:17

程序員編程語(yǔ)言

2014-02-09 10:30:17

Python程序員工具

2021-01-07 09:57:46

軟件架構(gòu)服務(wù)器

2024-12-04 10:12:06

2018-09-18 10:56:52

Android開(kāi)發(fā)http

2020-11-25 10:40:58

程序員技能開(kāi)發(fā)者

2011-02-23 10:39:56

Java程序員Java開(kāi)發(fā)

2020-03-31 08:42:14

程序員操作系統(tǒng) Linux

2023-05-11 08:01:08

Code開(kāi)發(fā)保護(hù)機(jī)制

2019-07-23 17:20:02

Java微服務(wù)軟件架構(gòu)模式

2013-06-24 09:18:05

點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)