原文出處: AlloyTeam 歡迎分享原創(chuàng)到伯樂(lè)頭條 《=》 ”按模塊劃分“目錄結(jié)構(gòu),,把當(dāng)前模塊下的所有邏輯和資源都放一起了,,這對(duì)于多人獨(dú)自開(kāi)發(fā)和維護(hù)個(gè)人模塊不是很好嗎?當(dāng)然了,那爭(zhēng)論的結(jié)果是我乖乖地改回主流的”按資源劃分“的目錄結(jié)構(gòu),。因?yàn)?,沒(méi)有做到JS模塊化和資源模塊化,僅僅物理位置上的模塊劃分是沒(méi)有意義的,,只會(huì)增加構(gòu)建的成本而已,。 雖然他說(shuō)得好有道理我無(wú)言以對(duì),但是我心不甘,,等待他日前端組件化成熟了,,再來(lái)一戰(zhàn)! 而今天就是我重申正義的日子,!只是當(dāng)年那個(gè)跟你撕逼的人不在,。 模塊化的不足 模塊一般指能夠獨(dú)立拆分且通用的代碼單元。由于JavaScript語(yǔ)言本身沒(méi)有內(nèi)置的模塊機(jī)制(ES6有了?。,。覀円话銜?huì)使用CMD或ADM建立起模塊機(jī)制?,F(xiàn)在大部分稍微大型一點(diǎn)的項(xiàng)目,,都會(huì)使用requirejs或者seajs來(lái)實(shí)現(xiàn)JS的模塊化。多人分工合作開(kāi)發(fā),,其各自定義依賴和暴露接口,,維護(hù)功能模塊間獨(dú)立性,對(duì)于項(xiàng)目的開(kāi)發(fā)效率和項(xiàng)目后期擴(kuò)展和維護(hù),,都是是有很大的幫助作用,。 但,麻煩大家稍微略讀一下下面的代碼 JavaScript
上面是具體某個(gè)頁(yè)面的主js,,已經(jīng)封裝了像Position,,NET,Refresh等功能模塊,,但頁(yè)面的主邏輯依舊是”面向過(guò)程“的代碼結(jié)構(gòu),。所謂面向過(guò)程,是指根據(jù)頁(yè)面的渲染過(guò)程來(lái)編寫(xiě)代碼結(jié)構(gòu),。像:init -> getData -> processData -> bindevent -> report -> xxx ,。 方法之間線性跳轉(zhuǎn),你大概也能感受這樣代碼弊端,。隨著頁(yè)面邏輯越來(lái)越復(fù)雜,,這條”過(guò)程線“也會(huì)越來(lái)越長(zhǎng),并且越來(lái)越繞,。加之缺少規(guī)范約束,,其他項(xiàng)目成員根據(jù)各自需要,,在”過(guò)程線“加插各自邏輯,最終這個(gè)頁(yè)面的邏輯變得難以維護(hù),。 開(kāi)發(fā)需要小心翼翼,,生怕影響“過(guò)程線”后面正常邏輯。并且每一次加插或修改都是bug泛濫,,無(wú)不令產(chǎn)品相關(guān)人員個(gè)個(gè)提心吊膽,。 頁(yè)面結(jié)構(gòu)模塊化 基于上面的面向過(guò)程的問(wèn)題,行業(yè)內(nèi)也有不少解決方案,,而我們團(tuán)隊(duì)也總結(jié)出一套成熟的解決方案:Abstractjs,,頁(yè)面結(jié)構(gòu)模塊化。我們可以把我們的頁(yè)面想象為一個(gè)樂(lè)高機(jī)器人,,需要不同零件組裝,如下圖,,假設(shè)頁(yè)面劃分為tabContainer,,listContainer和imgsContainer三個(gè)模塊。最終把這些模塊add到最終的pageModel里面,,最終使用rock方法讓頁(yè)面啟動(dòng)起來(lái),。
下面是偽代碼的實(shí)現(xiàn) JavaScript
我們把這些常用的請(qǐng)求CGI,處理數(shù)據(jù),,事件綁定,,上報(bào),容錯(cuò)處理等一系列邏輯方法,,以頁(yè)面塊為單位封裝成一個(gè)Model模塊,。 這樣的一個(gè)抽象層Model,我們可以清晰地看到該頁(yè)面塊,,請(qǐng)求的CGI是什么,,綁定了什么事件,做了什么上報(bào),,出錯(cuò)怎么處理,。新增的代碼就應(yīng)該放置在相應(yīng)的模塊上相應(yīng)的狀態(tài)方法(preload,process,,event,,complete…),杜絕了以往的無(wú)規(guī)則亂增代碼的行文,。并且,,根據(jù)不同業(yè)務(wù)邏輯封裝不同類型的Model,如列表滾動(dòng)的ScrollModel,,滑塊功能的SliderModel等等,,可以進(jìn)行高度封裝,集中優(yōu)化。 現(xiàn)在基于Model的頁(yè)面結(jié)構(gòu)開(kāi)發(fā),,已經(jīng)帶有一點(diǎn)”組件化“的味道,。每個(gè)Model都帶有各自的數(shù)據(jù),模板,,邏輯,。已經(jīng)算是一個(gè)完整的功能單元。但距離真正的WebComponent還是有一段距離,,至少滿足不了我的”理想目錄結(jié)構(gòu)“,。 WebComponents 標(biāo)準(zhǔn) 我們回顧一下使用一個(gè)datapicker的jquery的插件,所需要的步奏: 1. 引入插件js 2. 引入插件所需的css(如果有) 3. copy 組件的所需的html片段 4. 添加代碼觸發(fā)組件啟動(dòng) 現(xiàn)階段的“組件”基本上只能達(dá)到是某個(gè)功能單元上的集合,。他的資源都是松散地分散在三種資源文件中,,而且組件作用域暴露在全局作用域下,缺乏內(nèi)聚性很容易就會(huì)跟其他組件產(chǎn)生沖突,,如最簡(jiǎn)單的css命名沖突,。對(duì)于這種“組件”,還不如上面的頁(yè)面結(jié)構(gòu)模塊化,。 于是W3C按耐不住了,,制定一個(gè)WebComponents標(biāo)準(zhǔn),為組件化的未來(lái)指引了明路,。 下面以較為簡(jiǎn)潔的方式介紹這份標(biāo)準(zhǔn),,力求大家能夠快速了解實(shí)現(xiàn)組件化的內(nèi)容。(對(duì)這部分了解的同學(xué),,可以跳過(guò)這一小節(jié)) 1. 模板能力 模板這東西大家最熟悉不過(guò)了,,前些年見(jiàn)的較多的模板性能大戰(zhàn)artTemplate,juicer,,tmpl,,underscoretemplate等等。而現(xiàn)在又有mustachejs無(wú)邏輯模板引擎等新入選手,??墒谴蠹矣袥](méi)有想過(guò),這么基礎(chǔ)的能力,,原生HTML5是不支持的(T_T),。 而今天WebComponent將要提供原生的模板能力 XHTML
template標(biāo)簽內(nèi)定義了myTmpl的模板,需要使用的時(shí)候就要 2. ShadowDom 封裝組件獨(dú)立的內(nèi)部結(jié)構(gòu) ShadowDom可以理解為一份有獨(dú)立作用域的html片段,。這些html片段的CSS環(huán)境和主文檔隔離的,,各自保持內(nèi)部的獨(dú)立性。也正是ShadowDom的獨(dú)立特性,,使得組件化成為了可能,。 JavaScript
在具體dom節(jié)點(diǎn)上使用createShadowRoot方法即可生成其ShadowDom。就像在整份Html的屋子里面,,新建了一個(gè)shadow的房間,。房間外的人都不知道房間內(nèi)有什么,保持shadowDom的獨(dú)立性,。 3. 自定義原生標(biāo)簽 初次接觸Angularjs的directive指令功能,,設(shè)定好組件的邏輯后,一個(gè) JavaScript
Object.create方式繼承HTMLElement.prototype,,得到一個(gè)新的prototype。當(dāng)解析器發(fā)現(xiàn)我們?cè)谖臋n中標(biāo)記它將檢查是否一個(gè)名為createdCallback的方法,。如果找到這個(gè)方法它將立即運(yùn)行它,所以我們把克隆模板的內(nèi)容來(lái)創(chuàng)建的ShadowDom,。 最后,,registerElement的方法傳遞我們的prototype來(lái)注冊(cè)自定義標(biāo)簽。 上面的代碼開(kāi)始略顯復(fù)雜了,,把前面兩個(gè)能力“模板”“shadowDom”結(jié)合,,形成組件的內(nèi)部邏輯。最后通過(guò)registerElement的方式注冊(cè)組件,。之后可以愉快地 4. imports解決組件間的依賴 XHTML
這個(gè)類php最常用的html導(dǎo)入功能,HTML原生也能支持了,。 WebComponents標(biāo)準(zhǔn)內(nèi)容大概到這里,,是的,我這里沒(méi)有什么Demo,,也沒(méi)有實(shí)踐經(jīng)驗(yàn)分享,。由于webComponents新特性,基本上除了高版本的Chrome支持外,,其他瀏覽器的支持度甚少,。雖然有polymer幫忙推動(dòng)webcompoents的庫(kù)存在,但是polymer自身的要求版本也是非常高(IE10+),。所以今天的主角并不是他,。 我們簡(jiǎn)單來(lái)回顧一下WebCompoents的四部分功能: 1 .定義組件的HTML模板能力 2. Shadow Dom封裝組件的內(nèi)部結(jié)構(gòu),,并且保持其獨(dú)立性 3. Custom Element 對(duì)外提供組件的標(biāo)簽,實(shí)現(xiàn)自定義標(biāo)簽 4. import解決組件結(jié)合和依賴加載 組件化實(shí)踐方案 官方的標(biāo)準(zhǔn)看完了,,我們思考一下,。一份真正成熟可靠的組件化方案,需要具備的能力,。 “資源高內(nèi)聚”—— 組件資源內(nèi)部高內(nèi)聚,,組件資源由自身加載控制 “作用域獨(dú)立”—— 內(nèi)部結(jié)構(gòu)密封,不與全局或其他組件產(chǎn)生影響 “自定義標(biāo)簽”—— 定義組件的使用方式 “可相互組合”—— 組件正在強(qiáng)大的地方,,組件間組裝整合 “接口規(guī)范化”—— 組件接口有統(tǒng)一規(guī)范,,或者是生命周期的管理 個(gè)人認(rèn)為,模板能力是基礎(chǔ)能力,,跟是否組件化沒(méi)有強(qiáng)聯(lián)系,,所以沒(méi)有提出一個(gè)大點(diǎn)。 既然是實(shí)踐,,現(xiàn)階段WebComponent的支持度還不成熟,,不能作為方案的手段。而另外一套以高性能虛擬Dom為切入點(diǎn)的組件框架React,,在facebook的造勢(shì)下,,社區(qū)得到了大力發(fā)展。另外一名主角Webpack,,負(fù)責(zé)解決組件資源內(nèi)聚,,同時(shí)跟React極度切合形成互補(bǔ)。 所以【W(wǎng)ebpack】+【React】將會(huì)是這套方案的核心技術(shù),。 不知道你現(xiàn)在是“又是react+webpack”感到失望,,還是“太好了是react+webpack”不用再學(xué)一次新框架的高興。無(wú)論如何下面的內(nèi)容不會(huì)讓你失望的,。 一,,組件生命周期 React天生就是強(qiáng)制性組件化的,所以可以從根本性上解決面向過(guò)程代碼所帶來(lái)的麻煩,。React組件自身有生命周期方法,,能夠滿足“接口規(guī)范化”能力點(diǎn)。并且跟“頁(yè)面結(jié)構(gòu)模塊化”的所封裝抽離的幾個(gè)方法能一一對(duì)應(yīng),。另外react的jsx自帶模板功能,,把html頁(yè)面片直接寫(xiě)在render方法內(nèi),組件內(nèi)聚性更加緊密,。 由于React編寫(xiě)的JSX是會(huì)先生成虛擬Dom的,,需要時(shí)機(jī)才真正插入到Dom樹(shù)。使用React必須要清楚組件的生命周期,,其生命周期三個(gè)狀態(tài): mount這單詞翻譯增加,,嵌入等,。我倒是建議“插入”更好理解。插入,!拔出,!插入!拔出,!默念三次,,懂了沒(méi)?別少看黃段子的力量,, 組件狀態(tài)就是: 插入-> 更新 ->拔出,。 然后每個(gè)組件狀態(tài)會(huì)有兩種處理函數(shù),一前一后,,will函數(shù)和did函數(shù),。 因?yàn)榘纬龊蠡径际琴t者形態(tài)(我說(shuō)的是組件),所以沒(méi)有DidUnmount這個(gè)方法,。 另外React另外一個(gè)核心:數(shù)據(jù)模型props和state,,對(duì)應(yīng)著也有自個(gè)狀態(tài)方法 還有一個(gè)特殊狀態(tài)的處理函數(shù),用于優(yōu)化處理 加上最重要的render方法,,React自身帶的方法剛剛好10個(gè),。對(duì)于初學(xué)者來(lái)說(shuō)是比較難以消化。但其實(shí) 回到組件化的主題。 一個(gè)頁(yè)面結(jié)構(gòu)模塊化的組件,,能獨(dú)立封裝整個(gè)組件的過(guò)程線 我們換算成React生命周期方法: 組件的狀態(tài)方法流中,,有兩點(diǎn)需要特殊說(shuō)明: 1,二次渲染: 由于React的虛擬Dom特性,,組件的render函數(shù)不需自己觸發(fā),,根據(jù)props和state的改變自個(gè)通過(guò)差異算法,得出最優(yōu)的渲染,。 請(qǐng)求CGI一般都是異步,,所以必定帶來(lái)二次渲染。只是空數(shù)據(jù)渲染的時(shí)候,,有可能會(huì)被React優(yōu)化掉,。當(dāng)數(shù)據(jù)回來(lái),,通過(guò)setState,觸發(fā)二次render 2,,componentWiillMount與componentDidMount的差別 和大多數(shù)React的教程文章不一樣,,ajax請(qǐng)求我建議在WillMount的方法內(nèi)執(zhí)行,而不是組件初始化成功之后的DidMount,。這樣能在“空數(shù)據(jù)渲染”階段之前請(qǐng)求數(shù)據(jù),,盡早地減少二次渲染的時(shí)間。 二,JSX很丑,,但是組件內(nèi)聚的關(guān)鍵,! WebComponents的標(biāo)準(zhǔn)之一,需要模板能力,。本是以為是我們熟悉的模板能力,,但React中的JSX這樣的怪胎還是令人議論紛紛。React還沒(méi)有火起來(lái)的時(shí)候,,大家就已經(jīng)在微博上狠狠地吐槽了“JSX寫(xiě)的代碼這TM的丑”,。這其實(shí)只是Demo階段JSX,等到實(shí)戰(zhàn)的大型項(xiàng)目中的JSX,,包含多狀態(tài)多數(shù)據(jù)多事件的時(shí)候,,你會(huì)發(fā)現(xiàn)………….JSX寫(xiě)的代碼還是很丑。 為什么我們會(huì)覺(jué)得丑?因?yàn)槲覀冊(cè)缫呀?jīng)對(duì)“視圖-樣式-邏輯”分離的做法潛移默化,。 基于維護(hù)性和可讀性,,甚至性能,我們都不建議直接在Dom上面綁定事件或者直接寫(xiě)style屬性,。我們會(huì)在JS寫(xiě)事件代理,,在CSS上寫(xiě)上classname,html上的就是清晰的Dom結(jié)構(gòu),。我們很好地維護(hù)著MVC的設(shè)計(jì)模式,,一切安好。直到JSX把他們都糅合在一起,,所守護(hù)的技術(shù)棧受到侵略,,難免有所抵制,。 但是從組件化的目的來(lái)看,這種高內(nèi)聚的做法未嘗不可,。 下面的代碼,,之前的“邏輯視圖分離”模式,我們需要去找相應(yīng)的js文件,,相應(yīng)的event函數(shù)體內(nèi),,找到td-info的class所綁定的事件。 對(duì)比起JSX的高度內(nèi)聚,,所有事件邏輯就是在本身jsx文件內(nèi),,綁定的就是自身的showInfo方法。組件化的特性能立馬體現(xiàn)出來(lái),。 (注意:雖然寫(xiě)法上我們好像是HTML的內(nèi)聯(lián)事件處理器,,但是在React底層并沒(méi)有實(shí)際賦值類似onClick屬性,內(nèi)層還是使用類似事件代理的方式,,高效地維護(hù)著事件處理器) 再來(lái)看一段style的jsx,。其實(shí)jsx沒(méi)有對(duì)樣式有硬性規(guī)定,我們完全可遵循之前的定義class的邏輯,。任何一段樣式都應(yīng)該用class來(lái)定義,。在jsx你也完全可以這樣做。但是出于組件的獨(dú)立性,,我建議一些只有“一次性”的樣式直接使用style賦值更好,。減少冗余的class。 XHTML 1 2 3 className='list' style={{background: '#ddd'}}> {list_html} 或許JSX內(nèi)部有負(fù)責(zé)繁瑣的邏輯樣式,,可JSX的自定義標(biāo)簽?zāi)芰?,組件的黑盒性立馬能體驗(yàn)出來(lái),是不是瞬間美好了很多,。 JavaScript 1 2 3 4 5 6 7 8 render: function(){ return ( div> Menus bannerNums={this.state.list.length}>/Menus> TableList data={this.state.list}>/TableList> /div> ); } 雖然JSX本質(zhì)上是為了虛擬Dom而準(zhǔn)備的,,但這種邏輯和視圖高度合一對(duì)于組件化未嘗不是一件好事。 學(xué)習(xí)完React這個(gè)組件化框架后,,看看組件化能力點(diǎn)的完成情況 “資源高內(nèi)聚”—— (33%) html與js內(nèi)聚 “作用域獨(dú)立”—— (50%) js的作用域獨(dú)立 “自定義標(biāo)簽”—— (100%)jsx “可相互組合”—— (50%) 可組合,,但缺乏有效的加載方式 “接口規(guī)范化”—— (100%)組件生命周期方法 Webpack 資源組件化 對(duì)于組件化的資源獨(dú)立性,,一般的模塊加載工具和構(gòu)建流程視乎變得吃力,。組件化的構(gòu)建工程化,不再是之前我們常見(jiàn)的,,css合二,,js合三,而是體驗(yàn)在組件間的依賴于加載關(guān)系,。webpack正好符合需求點(diǎn),,一方面填補(bǔ)組件化能力點(diǎn),,另一方幫助我們完善組件化的整體構(gòu)建環(huán)境。 首先要申明一點(diǎn)是,,webpack是一個(gè)模塊加載打包工具,,用于管理你的模塊資源依賴打包問(wèn)題。這跟我們熟悉的requirejs模塊加載工具,,和grunt/gulp構(gòu)建工具的概念,,多多少少有些出入又有些雷同。 首先webpak對(duì)于CommonJS與AMD同時(shí)支持,,滿足我們模塊/組件的加載方式,。 JavaScript 1 2 3 4 require('module'); require('../file.js'); exports.doStuff = function() {}; module.exports = someValue; JavaScript 1 2 3 define('mymodule', ['dep1', 'dep2'], function(d1, d2) { return someExportedValue; }); 當(dāng)然最強(qiáng)大的,最突出的,,當(dāng)然是模塊打包功能,。這正是這一功能,補(bǔ)充了組件化資源依賴,,以及整體工程化的能力 根據(jù)webpack的設(shè)計(jì)理念,,所有資源都是“模塊”,webpack內(nèi)部實(shí)現(xiàn)了一套資源加載機(jī)制,,可以把想css,,圖片等資源等有依賴關(guān)系的“模塊”加載。這跟我們使用requirejs這種僅僅處理js大大不同,。而這套加載機(jī)制,,通過(guò)一個(gè)個(gè)loader來(lái)實(shí)現(xiàn)。 JavaScript 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 // webpack.config.js module.exports = { entry: { entry: './index.jsx', }, output: { path: __dirname, filename: '[name].min.js' },, module: { loaders: [ {test: /\.css$/, loader: 'style!css' }, {test: /\.(jsx|js)?$/, loader: 'jsx?harmony', exclude: /node_modules/}, {test: /\.(png|jpg|jpeg)$/, loader: 'url-loader?limit=10240'} ] } }; 上面一份簡(jiǎn)單的webpack配置文件,,留意loaders的配置,數(shù)組內(nèi)一個(gè)object配置為一種模塊資源的加載機(jī)制,。test的正則為匹配文件規(guī)則,,loader的為匹配到文件將由什么加載器處理,多個(gè)處理器之間用 如 jsx文件通過(guò)jsx-loader編譯,‘,?’開(kāi)啟加載參數(shù),,harmony支持ES6的語(yǔ)法。 圖片資源通過(guò)url-loader加載器,配置參數(shù)limit,,控制少于10KB的圖片將會(huì)base64化,。 資源文件如何被require? JavaScript 1 2 3 4 5 6 // 加載組件自身css require('./slider.css'); // 加載組件依賴的模塊 var Clip = require('./clipitem.js'); // 加載圖片資源 var spinnerImg = require('./loading.png'); 在webpack的js文件中我們除了require我們正常的js文件,,css和png等靜態(tài)文件也可以被require進(jìn)來(lái),。我們通過(guò)webpack命令,編譯之后,,看看輸出結(jié)果如何: JavaScript 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 webpackJsonp([0], { /* 0 */ /***/ function(module, exports, __webpack_require__) { // 加載組件自身css __webpack_require__(1); // 加載組件依賴的模塊 var Clip = __webpack_require__(5); // 加載圖片資源 var spinnerImg = __webpack_require__(6); /***/ }, /* 1 */ /***/ function(module, exports, __webpack_require__) { /***/ }, /* 2 */ /***/ function(module, exports, __webpack_require__) { exports = module.exports = __webpack_require__(3)(); exports.push([module.id, '.slider-wrap{\r\n position: relative;\r\n width: 100%;\r\n margin: 50px;\r\n background: #fff;\r\n}\r\n\r\n.slider-wrap li{\r\n text-align: center;\r\n line-height: 20px;\r\n}', '']); /***/ }, /* 3 */ /***/ function(module, exports) { /***/ }, /* 4 */ /***/ function(module, exports, __webpack_require__) { /***/ }, /* 5 */ /***/ function(module, exports) { console.log('hello, here is clipitem.js') ; /***/ }, /* 6 */ /***/ function(module, exports) { module.exports = 'data:image/png;base64,iVBORw0KGg......' /***/ } ]); webpack編譯之后,,輸出文件視乎亂糟糟的,但其實(shí)每一個(gè)資源都被封裝在一個(gè)函數(shù)體內(nèi),,并且以編號(hào)的形式標(biāo)記(注釋),。這些模塊,由webpack的__webpack_require__內(nèi)部方法加載,。入口文件為編號(hào)0的函數(shù)index.js,,可以看到__webpack_require__加載其他編號(hào)的模塊。 css文件在編號(hào)1,,由于使用css-loader和style-loader,,編號(hào)1-4都是處理css。其中編號(hào)2我們可以看我們的css的string體,。最終會(huì)以內(nèi)聯(lián)的方式插入到html中,。 圖片文件在編號(hào)6,可以看出exports出base64化的圖片,。 組件一體輸出 JavaScript 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 // 加載組件自身css require('./slider.css'); // 加載組件依賴的模塊 var React = require('react'); var Clip = require('../ui/clipitem.jsx'); // 加載圖片資源 var spinnerImg = require('./loading.png'); var Slider = React.createClass({ getInitialState: function() { // ... }, componentDidMount: function(){ // ... }, render: function() { return ( div> Clip data={this.props.imgs} /> img className='loading' src={spinnerImg} /> /div> ); } }); module.exports = Slider; 如果說(shuō),,react使到html和js合為一體。 那么加上webpack,,兩者結(jié)合一起的話,。js,css,,png(base64),,html 所有web資源都能合成一個(gè)JS文件。這正是這套方案的核心所在:組件獨(dú)立一體化,。如果要引用一個(gè)組件,,僅僅 加入webpack的模塊加載器之后,,我們組件的加載問(wèn)題,,內(nèi)聚問(wèn)題也都成功地解決掉 “資源高內(nèi)聚”—— (100%) 所有資源可以一js輸出 “可相互組合”—— (100%) 可組合可依賴加載 CSS模塊化實(shí)踐 很高興,你能閱讀到這里,。目前我們的組件完成度非常的高,,資源內(nèi)聚,易于組合,,作用域獨(dú)立互不污染,。。,。,。等等,視乎CSS模塊的完成度有欠缺,。 那么目前組件完成度來(lái)看,,CSS作用域其實(shí)是全局性的,并非組件內(nèi)部獨(dú)立,。下一步,,我們要做得就是如何讓我們組件內(nèi)部的CSS作用域獨(dú)立。 這時(shí)可能有人立馬跳出,,大喊一句“德瑪西亞,!”,哦不,,應(yīng)該是“用sass啊傻逼,!”??墒?strong>項(xiàng)目組件化之后,,組件的內(nèi)部封裝已經(jīng)很好了,其內(nèi)部dom結(jié)構(gòu)和css趨向簡(jiǎn)單,,獨(dú)立,,甚至是破碎的。LESS和SASS的一體式樣式框架的設(shè)計(jì),,他的嵌套,,變量,include,,函數(shù)等豐富的功能對(duì)于整體大型項(xiàng)目的樣式管理非常有效,。但對(duì)于一個(gè)功能單一組件內(nèi)部樣式,視乎就變的有點(diǎn)格格不入,?!安荒転榱丝蚣芏蚣埽线m才是最好的”,。視乎原生的css能力已經(jīng)滿足組件的樣式需求,,唯獨(dú)就是上面的css作用域問(wèn)題。 這里我給出思考的方案: classname隨便寫(xiě),,保持原生的方式,。編譯階段,,根據(jù)組件在項(xiàng)目路徑的唯一性,由【組件classname+組件唯一路徑】打成md5,,生成全局唯一性classname,。正當(dāng)我要寫(xiě)一個(gè)loader實(shí)現(xiàn)我的想法的時(shí)候,發(fā)現(xiàn)歪果仁已經(jīng)早在先走一步了,。,。。,。 這里具體方案參考我之前博客的譯文:http://www./2015/10/8536/ 之前我們討論過(guò)JS的模塊?,F(xiàn)在通過(guò)Webpack被加載的CSS資源叫做“CSS模塊”?我覺(jué)得還是有問(wèn)題的?,F(xiàn)在style-loader插件的實(shí)現(xiàn)本質(zhì)上只是創(chuàng)建link[rel=stylesheet]元素插入到document中,。這種行為和通常引入JS模塊非常不同。引入另一個(gè)JS模塊是調(diào)用它所提供的接口,,但引入一個(gè)CSS卻并不“調(diào)用”CSS,。所以引入CSS本身對(duì)于JS程序來(lái)說(shuō)并不存在“模塊化”意義,純粹只是表達(dá)了一種資源依賴——即該組件所要完成的功能還需要某些asset,。 因此,,那位歪果仁還擴(kuò)展了“CSS模塊化”的概念,除了上面的我們需要局部作用域外,,還有很多功能,,這里不詳述。具體參考原文 http:///articles/css-modules 非常贊的一點(diǎn),,就是cssmodules已經(jīng)被css-loader收納,。所以我們不需要依賴額外的loader,基本的css-loader開(kāi)啟參數(shù)modules即可 JavaScript 1 2 3 4 5 6 7 8 //webpack.config.js ... module: { loaders: [ {test: /\.css$/, loader: 'style!css?modules&localIdentName=[local]__[name]_[hash:base64:5]' }, ] } .... modules參數(shù)代表開(kāi)啟css-modules功能,,loaclIdentName為設(shè)置我們編譯后的css名字,,為了方便debug,我們把classname(local)和組件名字(name)輸出,。當(dāng)然可以在最后輸出的版本為了節(jié)省提交,,僅僅使用hash值即可。另外在react中的用法大概如下,。 JavaScript 1 2 3 4 5 6 7 8 9 10 11 var styles = require('./banner.css'); var Banner = new React.createClass({ ... render: function(){ return ( div> div className={styles.classA}>/div> /div> ) } }); 最后這里關(guān)于出于對(duì)CSS一些思考,, 關(guān)于css-modules的其它功能,我并不打算使用,。在內(nèi)部分享【我們竭盡所能地讓CSS變得復(fù)雜】中提及: 我們項(xiàng)目中大部分的CSS都不會(huì)像boostrap那樣需要變量來(lái)設(shè)置,,身為一線開(kāi)發(fā)者的我們大概能夠感受到:設(shè)計(jì)師們改版UI,絕對(duì)不是簡(jiǎn)單的換個(gè)色或改個(gè)間距,,而是面目全非的全新UI,,這絕對(duì)不是一個(gè)變量所能解決的”維護(hù)性“,。 反而項(xiàng)目實(shí)戰(zhàn)過(guò)程中,真正要解決的是:在版本迭代過(guò)程中那些淘汰掉的過(guò)期CSS,,大量地堆積在項(xiàng)目當(dāng)中,。我們像極了家中的歐巴醬不舍得丟掉沒(méi)用的東西,因?yàn)檫@可是我們使用sass或less編寫(xiě)出具有高度的可維護(hù)性的,,肯定有復(fù)用的一天。 這些堆積的過(guò)期CSS(or sass)之間又有部分依賴,,一部分過(guò)期沒(méi)用了,,一部分又被新的樣式復(fù)用了,導(dǎo)致沒(méi)人敢動(dòng)那些歷史樣式,。結(jié)果現(xiàn)網(wǎng)項(xiàng)目迭代還帶著大量?jī)赡昵皼](méi)用的樣式文件,。 組件化之后,css的格局同樣被革新了,??赡躳ostcss才是你現(xiàn)在手上最適合的工具,而不在是sass,。 到這里,,我們終于把組件化最后一個(gè)問(wèn)題也解決了。 “作用域獨(dú)立”—— (100%) 如同shadowDom作用域獨(dú)立 到這里,,我們可以開(kāi)一瓶82年的雪碧,,好好慶祝一下。不是嗎,? 組件化之路還在繼續(xù) webpack和react還有很多新非常重要的特性和功能,,介于本文僅僅圍繞著組件化的為核心,沒(méi)有一一闡述,。另外,,配搭gulp/grunt補(bǔ)充webpack構(gòu)建能力,webpack的codeSplitting,,react的組件通信問(wèn)題,,開(kāi)發(fā)與生產(chǎn)環(huán)境配置等等,都是整套大型項(xiàng)目方案的所必須的,,限于篇幅問(wèn)題,。可以等等我更新下篇,,或大家可以自行查閱,。 但是,不得不再安利一下react-hotloader神器,。熱加載的開(kāi)發(fā)模式絕對(duì)是下一代前端開(kāi)發(fā)必備,。嚴(yán)格說(shuō),,如果沒(méi)有了熱加載,我會(huì)很果斷地放棄這套方案,,即使這套方案再怎么優(yōu)秀,,我都討厭react需要5~6s的編譯時(shí)間。但是hotloader可以在我不刷新頁(yè)面的情況下,,動(dòng)態(tài)修改代碼,,而且不單單是樣式,連邏輯也是即時(shí)生效,。 如上在form表單內(nèi),。使用熱加載,表單不需要重新填寫(xiě),,修改submit的邏輯立刻生效,。這樣的開(kāi)發(fā)效率真不是提高僅僅一個(gè)檔次。必須安利一下,。 或許你發(fā)現(xiàn),,使用組件化方案之后,整個(gè)技術(shù)棧都被更新了一番,。學(xué)習(xí)成本也不少,,并且可以預(yù)知到,基于組件化的前端還會(huì)很多不足的問(wèn)題,,例如性能優(yōu)化方案需要重新思考,,甚至最基本的組件可復(fù)用性不一定高。后面很長(zhǎng)一段時(shí)間,,需要我們不斷磨練與優(yōu)化,,探求最優(yōu)的前端組件化之道。 至少我們可以想象,,不再擔(dān)心自己寫(xiě)的代碼跟某個(gè)誰(shuí)誰(shuí)沖突,,不再為找某段邏輯在多個(gè)文件和方法間穿梭,不再copy一片片邏輯然后改改,。我們每次編寫(xiě)都是可重用,,可組合,獨(dú)立且內(nèi)聚的組件,。而每個(gè)頁(yè)面將會(huì)由一個(gè)個(gè)嵌套組合的組件,,相互獨(dú)立卻相互作用。 對(duì)于這樣的前端未來(lái),,有所期待,,不是很好嗎 至此,感謝你的閱讀,。 點(diǎn)擊下面 “閱讀原文” 查看詳情,! |
|