<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Frontend Architecture | Mobabel</title>
	<atom:link href="http://www.mobabel.net/category/frontend-architecture/feed/?lang=en_uspage-7page-15page-7page-3page-14" rel="self" type="application/rss+xml" />
	<link>http://www.mobabel.net</link>
	<description>Just One Pure ITer</description>
	<lastBuildDate>Sun, 21 Apr 2024 08:14:04 +0000</lastBuildDate>
	<language>en-US</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>https://wordpress.org/?v=6.6.2</generator>
	<item>
		<title>[汇总]Webpack系列</title>
		<link>http://www.mobabel.net/%e6%b1%87%e6%80%bbwebpack%e7%b3%bb%e5%88%97/</link>
					<comments>http://www.mobabel.net/%e6%b1%87%e6%80%bbwebpack%e7%b3%bb%e5%88%97/#respond</comments>
		
		<dc:creator><![CDATA[leelight]]></dc:creator>
		<pubDate>Sat, 07 Mar 2020 16:54:05 +0000</pubDate>
				<category><![CDATA[Theory & Solution]]></category>
		<category><![CDATA[Frontend]]></category>
		<guid isPermaLink="false">http://www.mobabel.net/?p=6193</guid>

					<description><![CDATA[<p>一步步从零开始用 webpack 搭建一个大型项目 深入理解 webpack 文件打包机制 Webpack 的 Bundle Split 和 Code Split 区别和应用 如何更好地优化打包资源 Webpack 打包优化之速度篇 &#160; &#160;</p>
The post <a href="http://www.mobabel.net/%e6%b1%87%e6%80%bbwebpack%e7%b3%bb%e5%88%97/">[汇总]Webpack系列</a> first appeared on <a href="http://www.mobabel.net">Mobabel</a>.]]></description>
										<content:encoded><![CDATA[<p id="activity-name" class="rich_media_title"><a href="https://mp.weixin.qq.com/s/a0QFhcbwkhXStBfMOe7exA">一步步从零开始用 webpack 搭建一个大型项目</a></p>
<p id="activity-name" class="rich_media_title"><a href="https://mp.weixin.qq.com/s/BVYgLeOEIJp5xY55Rcrszg">深入理解 webpack 文件打包机制</a></p>
<p><span id="more-6193"></span></p>
<p id="activity-name" class="rich_media_title"><a href="https://mp.weixin.qq.com/s?__biz=MzAxODE2MjM1MA==&amp;mid=2651555883&amp;idx=1&amp;sn=ad1a42c721377aa3a7faa1e603596d12&amp;chksm=80255feab752d6fcd8ae5dae99357f80428d78a18603c26860d0afb06fc04a9464e98887695b&amp;mpshare=1&amp;scene=24&amp;srcid=#rd">Webpack 的 Bundle Split 和 Code Split 区别和应用</a></p>
<p id="activity-name" class="rich_media_title"><a href="https://mp.weixin.qq.com/s/H-w4LtY3qVNAQLiyGFsEaA">如何更好地优化打包资源</a></p>
<p id="activity-name" class="rich_media_title"><a href="https://mp.weixin.qq.com/s/Av5YMa7u5A7kEuuUhC_Oaw">Webpack 打包优化之速度篇</a></p>
<p>&nbsp;</p>
<p>&nbsp;</p>The post <a href="http://www.mobabel.net/%e6%b1%87%e6%80%bbwebpack%e7%b3%bb%e5%88%97/">[汇总]Webpack系列</a> first appeared on <a href="http://www.mobabel.net">Mobabel</a>.]]></content:encoded>
					
					<wfw:commentRss>http://www.mobabel.net/%e6%b1%87%e6%80%bbwebpack%e7%b3%bb%e5%88%97/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>[汇总]Frontend经验</title>
		<link>http://www.mobabel.net/%e6%80%bb%e7%bb%93frontend%e7%bb%8f%e9%aa%8c%e6%80%bb%e7%bb%93/</link>
					<comments>http://www.mobabel.net/%e6%80%bb%e7%bb%93frontend%e7%bb%8f%e9%aa%8c%e6%80%bb%e7%bb%93/#respond</comments>
		
		<dc:creator><![CDATA[leelight]]></dc:creator>
		<pubDate>Thu, 03 Jan 2019 23:14:11 +0000</pubDate>
				<category><![CDATA[Theory & Solution]]></category>
		<category><![CDATA[Frontend]]></category>
		<guid isPermaLink="false">http://www.mobabel.net/?p=4102</guid>

					<description><![CDATA[<p>仅仅几个月时间,我的 JavaScript 程序就打不开了 前端布局神器display:flex 页面可视化搭建工具技术要点 2018 年度前端开发领域的重大事件 HTML5绘制上海地铁线路图 腾讯新闻前端团队:深入理解前端性能监控 亚马逊首席软件工程师:我们在网站测试上面临的最大挑战 前端工程化:保姆级教学 Jenkins 部署前端项目 爱奇艺电视端Web页面性能优化实践 浏览器跨标签页通信的方式都有什么? &#160; &#160;</p>
The post <a href="http://www.mobabel.net/%e6%80%bb%e7%bb%93frontend%e7%bb%8f%e9%aa%8c%e6%80%bb%e7%bb%93/">[汇总]Frontend经验</a> first appeared on <a href="http://www.mobabel.net">Mobabel</a>.]]></description>
										<content:encoded><![CDATA[<p id="activity-name" class="rich_media_title "><a href="https://mp.weixin.qq.com/s/HBe-nGTtmx5P3V1n8fQVCA">仅仅几个月时间,我的 JavaScript 程序就打不开了</a></p>
<p id="activity-name" class="rich_media_title"><a href="https://mp.weixin.qq.com/s?__biz=MjM5MDA2MTI1MA==&amp;mid=2649089840&amp;idx=1&amp;sn=0929f7ef1d1fe957f6ef1d77db2599c7&amp;chksm=be5bc09d892c498b3905fc10b36ae8ef2af42eda294f5ca5e3fb2cd15ec5579eeec0e94383d7&amp;mpshare=1&amp;scene=24&amp;srcid=#rd">前端布局神器display</a>:flex</p>
<p id="activity-name" class="rich_media_title"><a href="https://mp.weixin.qq.com/s/7jvmZmmsSHHvUBm5cMmMQA">页面可视化搭建工具技术要点</a></p>
<p><a href="https://mp.weixin.qq.com/s?__biz=MjM5MjAwODM4MA==&amp;mid=2650711654&amp;idx=1&amp;sn=3f9f839f8a42a3226c14288e4c314ee3&amp;chksm=bea6d7b589d15ea3b49bf3ee6e2788bbf0707f62306b5f9e942bf5faff20541d39d92e6c56a6&amp;mpshare=1&amp;scene=24&amp;srcid=#rd">2018 年度前端开发领域的重大事件</a></p>
<p id="activity-name" class="rich_media_title"><a href="https://mp.weixin.qq.com/s?__biz=MjM5MDA2MTI1MA==&amp;mid=2649090004&amp;idx=1&amp;sn=bb27caa9c85713bd58ba8183e1cb0522&amp;chksm=be5bc079892c496f293b3f777afbe8c4949375ef3958a741927e5128a04c6ef10522eeff7302&amp;mpshare=1&amp;scene=24&amp;srcid=#rd">HTML5绘制上海地铁线路图</a></p>
<p id="activity-name" class="rich_media_title"><a href="https://mp.weixin.qq.com/s/YI-96IbtIjTDzS-3N-9FAg">腾讯新闻前端团队:深入理解前端性能监控</a></p>
<p id="activity-name" class="rich_media_title"><a href="https://mp.weixin.qq.com/s/AXgLidQekWz5raZWSYpE6g">亚马逊首席软件工程师:我们在网站测试上面临的最大挑战</a></p>
<p id="activity-name" class="rich_media_title "><a href="https://mp.weixin.qq.com/s/9igqPCquyu136swhzUvuoQ">前端工程化:保姆级教学 Jenkins 部署前端项目</a></p>
<p id="activity-name" class="rich_media_title "><a href="https://mp.weixin.qq.com/s/RBVf2zWivQCT5YRK82VB_g">爱奇艺电视端Web页面性能优化实践</a></p>
<p id="activity-name" class="rich_media_title "><a href="https://mp.weixin.qq.com/s/K5oHXhVrG5UaGmSN1HK7bg">浏览器跨标签页通信的方式都有什么?</a></p>
<p>&nbsp;</p>
<p>&nbsp;</p>The post <a href="http://www.mobabel.net/%e6%80%bb%e7%bb%93frontend%e7%bb%8f%e9%aa%8c%e6%80%bb%e7%bb%93/">[汇总]Frontend经验</a> first appeared on <a href="http://www.mobabel.net">Mobabel</a>.]]></content:encoded>
					
					<wfw:commentRss>http://www.mobabel.net/%e6%80%bb%e7%bb%93frontend%e7%bb%8f%e9%aa%8c%e6%80%bb%e7%bb%93/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>[汇总]JS经验</title>
		<link>http://www.mobabel.net/%e6%b1%87%e6%80%bbjs%e7%bb%8f%e9%aa%8c/</link>
					<comments>http://www.mobabel.net/%e6%b1%87%e6%80%bbjs%e7%bb%8f%e9%aa%8c/#respond</comments>
		
		<dc:creator><![CDATA[leelight]]></dc:creator>
		<pubDate>Mon, 17 Dec 2018 09:56:49 +0000</pubDate>
				<category><![CDATA[JavaScript]]></category>
		<category><![CDATA[Javascript]]></category>
		<guid isPermaLink="false">http://www.mobabel.net/?p=3522</guid>

					<description><![CDATA[<p>为什么Facebook的API以一个循环作为开头? 如果你有在浏览器中查看过发给大公司 API 的请求,你可能会注意到,JSON 前面会有一些奇怪的 JavaScript 2018 年,JavaScript 都经历了什么? React vs Angular,到底谁更牛? 对比 12,000 个 Vue.js 开源项目发现最实用的 TOP45! 100 道 JavaScript 面试题及答案(上) &#160; &#160; &#160;</p>
The post <a href="http://www.mobabel.net/%e6%b1%87%e6%80%bbjs%e7%bb%8f%e9%aa%8c/">[汇总]JS经验</a> first appeared on <a href="http://www.mobabel.net">Mobabel</a>.]]></description>
										<content:encoded><![CDATA[<p id="activity-name" class="rich_media_title"><a href="https://mp.weixin.qq.com/s?__biz=MjM5MDE0Mjc4MA==&amp;mid=2651011413&amp;idx=1&amp;sn=baae160b43ee6fa8b2bbd713080a96d3&amp;chksm=bdbec1068ac94810b48457d77ed7d8c5a479a7fdd0ff2c8d26df5781c06098ae2ec8ba627ac0&amp;mpshare=1&amp;scene=24&amp;srcid=#rd">为什么Facebook的API以一个循环作为开头?</a><br />
如果你有在浏览器中查看过发给大公司 API 的请求,你可能会注意到,JSON 前面会有一些奇怪的 JavaScript</p>
<p id="activity-name" class="rich_media_title"><a href="https://mp.weixin.qq.com/s?__biz=MjM5MjAwODM4MA==&amp;mid=2650711234&amp;idx=1&amp;sn=704de16e165d5129a85f076099b08be0&amp;chksm=bea6d51189d15c0723274028dd13f4eee366e29843f86559eb1435d173bddb1b9c5df0235940&amp;mpshare=1&amp;scene=24&amp;srcid=#rd">2018 年,JavaScript 都经历了什么?</a></p>
<p id="activity-name" class="rich_media_title"><a href="https://mp.weixin.qq.com/s?__biz=MjM5ODI5Njc2MA==&amp;mid=2655822782&amp;idx=1&amp;sn=b1329f4eccc88a70d344f54179917491&amp;chksm=bd74ee698a03677f775a6821193f675224a32224d5c3f2a20ddfaf622dc61aa785a98b38e92d&amp;mpshare=1&amp;scene=24&amp;srcid=#rd">React vs Angular,到底谁更牛?</a></p>
<p id="activity-name" class="rich_media_title"><a href="https://mp.weixin.qq.com/s?__biz=MjM5MjAwODM4MA==&amp;mid=2650715249&amp;idx=2&amp;sn=fbd2acb020ca0f6abd9d101eedd5ee54&amp;chksm=bea6c5a289d14cb4f12a302a8f268e0021af2ed95d34392e8ab897ba878c48e8434c83d360f3&amp;mpshare=1&amp;scene=24&amp;srcid=#rd">对比 12,000 个 Vue.js 开源项目发现最实用的 TOP45!</a></p>
<p id="activity-name" class="rich_media_title "><a href="https://mp.weixin.qq.com/s?__biz=MjM5MDA2MTI1MA==&amp;mid=2649135868&amp;idx=1&amp;sn=d1caa8f7befa04b5d6003c41ddfa2c57&amp;chksm=be58bd51892f3447e0581639c8ef2110015edd5c1c26c726e623d2b54877c0cab12baebeeb55">100 道 JavaScript 面试题及答案(上)</a></p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>The post <a href="http://www.mobabel.net/%e6%b1%87%e6%80%bbjs%e7%bb%8f%e9%aa%8c/">[汇总]JS经验</a> first appeared on <a href="http://www.mobabel.net">Mobabel</a>.]]></content:encoded>
					
					<wfw:commentRss>http://www.mobabel.net/%e6%b1%87%e6%80%bbjs%e7%bb%8f%e9%aa%8c/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>[转]微前端 &#8211; 将微服务理念延伸到前端开发中</title>
		<link>http://www.mobabel.net/%e8%bd%ac%e5%be%ae%e5%89%8d%e7%ab%af-%e5%b0%86%e5%be%ae%e6%9c%8d%e5%8a%a1%e7%90%86%e5%bf%b5%e5%bb%b6%e4%bc%b8%e5%88%b0%e5%89%8d%e7%ab%af%e5%bc%80%e5%8f%91%e4%b8%ad/</link>
					<comments>http://www.mobabel.net/%e8%bd%ac%e5%be%ae%e5%89%8d%e7%ab%af-%e5%b0%86%e5%be%ae%e6%9c%8d%e5%8a%a1%e7%90%86%e5%bf%b5%e5%bb%b6%e4%bc%b8%e5%88%b0%e5%89%8d%e7%ab%af%e5%bc%80%e5%8f%91%e4%b8%ad/#respond</comments>
		
		<dc:creator><![CDATA[leelight]]></dc:creator>
		<pubDate>Sat, 08 Dec 2018 14:38:41 +0000</pubDate>
				<category><![CDATA[Theory & Solution]]></category>
		<category><![CDATA[micro-frontends]]></category>
		<guid isPermaLink="false">http://www.mobabel.net/?p=3010</guid>

					<description><![CDATA[<p>本文描述了采用不同 JavaScript 技术框架的多个团队中协同构建一个现代化前端 Web 应用所需要的技术、策略和方法。 1. 什么是微前端? 微前端这个术语最初来自 2016 年的 ThoughtWorks 技术雷达[ https://www.thoughtworks.com/radar/techniques/micro-frontends ],它将微服务的概念扩展到了前端领域。目前的趋势是构建一个功能丰富且强大的前端应用,即单页面应用(SPA),其本身一般都是建立在一个微服务架构之上。前端层通常由一个单独的团队开发,随着时间的推移,会变得越来越庞大而难以维护。这就是传说中的前端巨无霸(Frontend Monolith) [ https://www.youtube.com/watch?v=pU1gXA0rfwc ]。 微前端背后的理念是将一个网站或者 Web App 当成特性的组合体,每个特性都由一个独立的团队负责。每个团队都有擅长的特定业务领域或是它关心的任务。这里,一个团队是跨职能的,它可以端到端,从数据库到用户界面完整的开发它所负责的功能。 然而,这个概念并不新鲜,过去它叫针对垂直系统的前端一体化或独立系统。不过微前端显然是一个更加友好并且不那么笨重的术语。 一体化的前端 垂直化组织方式 2. 什么是现代化前端应用 在介绍中我使用了措辞“构建一个现代化前端应用”,让我们先给出一些这个术语有关的设定。 从一个更广泛的角度来看,Aral Balkan 曾写过一个相关的博客,他把这个概念叫做文档-应用连续统一体。他提出了一个滑动比例尺的概念,在比例尺的最左边是一个网站,由静态文档构成,通过链接相互连接;最右边是一个纯行为驱动的,几乎没内容的应用程序,比如在线图片编辑器。 如果你把你的项目定位在这个范围的左侧,那在 Web 服务器级别的集成会比较合适。在这个模型中,服务器会收集页面中各个组件的内容并将其 HTML 字符串连接起来返回给用户。内容更新则采用从服务端重新加载的方式或者通过 ajax 进行部分替换。Gustaf Nilsson&#46;&#46;&#46;</p>
The post <a href="http://www.mobabel.net/%e8%bd%ac%e5%be%ae%e5%89%8d%e7%ab%af-%e5%b0%86%e5%be%ae%e6%9c%8d%e5%8a%a1%e7%90%86%e5%bf%b5%e5%bb%b6%e4%bc%b8%e5%88%b0%e5%89%8d%e7%ab%af%e5%bc%80%e5%8f%91%e4%b8%ad/">[转]微前端 – 将微服务理念延伸到前端开发中</a> first appeared on <a href="http://www.mobabel.net">Mobabel</a>.]]></description>
										<content:encoded><![CDATA[<p>本文描述了采用不同 JavaScript 技术框架的多个团队中协同构建一个现代化前端 Web 应用所需要的技术、策略和方法。</p>
<h2 id='1-什么是微前端'  id="boomdevs_1" >1. <strong>什么是微前端?</strong></h2>
<p><strong>微前端</strong>这个术语最初来自 2016 年的 ThoughtWorks 技术雷达[ https://www.thoughtworks.com/radar/techniques/micro-frontends ],它将微服务的概念扩展到了前端领域。目前的趋势是构建一个功能丰富且强大的前端应用,即单页面应用(SPA),其本身一般都是建立在一个微服务架构之上。前端层通常由一个单独的团队开发,随着时间的推移,会变得越来越庞大而难以维护。这就是传说中的前端巨无霸(Frontend Monolith) [ https://www.youtube.com/watch?v=pU1gXA0rfwc ]。<span id="more-3010"></span></p>
<p>微前端背后的理念是将一个网站或者 Web App 当成<strong>特性的组合体</strong>,每个特性都由一个<strong>独立的团队</strong>负责。每个团队都有擅长的<strong>特定业务领域</strong>或是它关心的<strong>任务</strong>。这里,一个团队是<strong>跨职能的</strong>,它可以端到端,从数据库到用户界面完整的开发它所负责的功能。</p>
<p>然而,这个概念并不新鲜,过去它叫<strong>针对垂直系统的前端一体化</strong>或<strong>独立系统</strong>。不过微前端显然是一个更加友好并且不那么笨重的术语。</p>
<p><strong>一体化的前端</strong></p>
<p><img fetchpriority="high" decoding="async" class="alignnone size-full wp-image-3011" src="http://www.mobabel.net/wp-content/uploads/2018/12/08/4ffce04d92a4d6cb21c1494cdfcd6dc1_143847.jpg" width="1080" height="422" srcset="http://www.mobabel.net/wp-content/uploads/2018/12/08/4ffce04d92a4d6cb21c1494cdfcd6dc1_143847.jpg 1080w, http://www.mobabel.net/wp-content/uploads/2018/12/08/4ffce04d92a4d6cb21c1494cdfcd6dc1_143847-300x117.jpg 300w, http://www.mobabel.net/wp-content/uploads/2018/12/08/4ffce04d92a4d6cb21c1494cdfcd6dc1_143847-768x300.jpg 768w, http://www.mobabel.net/wp-content/uploads/2018/12/08/4ffce04d92a4d6cb21c1494cdfcd6dc1_143847-1024x400.jpg 1024w" sizes="(max-width: 1080px) 100vw, 1080px" /></p>
<p><strong>垂直化组织方式</strong></p>
<p><img decoding="async" class="alignnone size-full wp-image-3012" src="http://www.mobabel.net/wp-content/uploads/2018/12/08/4ffce04d92a4d6cb21c1494cdfcd6dc1_143855.png" width="920" height="509" srcset="http://www.mobabel.net/wp-content/uploads/2018/12/08/4ffce04d92a4d6cb21c1494cdfcd6dc1_143855.png 920w, http://www.mobabel.net/wp-content/uploads/2018/12/08/4ffce04d92a4d6cb21c1494cdfcd6dc1_143855-300x166.png 300w, http://www.mobabel.net/wp-content/uploads/2018/12/08/4ffce04d92a4d6cb21c1494cdfcd6dc1_143855-768x425.png 768w" sizes="(max-width: 920px) 100vw, 920px" /></p>
<h2 id='2-什么是现代化前端应用'  id="boomdevs_2" >2. <strong>什么是现代化前端应用</strong></h2>
<p>在介绍中我使用了措辞“构建一个现代化前端应用”,让我们先给出一些这个术语有关的设定。</p>
<p>从一个更广泛的角度来看,Aral Balkan 曾写过一个相关的博客,他把这个概念叫做文档-应用连续统一体。他提出了一个滑动比例尺的概念,在比例尺的最左边是一个网站,由静态文档构成,通过链接相互连接;最右边是一个纯行为驱动的,几乎没内容的应用程序,比如在线图片编辑器。</p>
<p>如果你把你的项目定位在这个范围的左侧,那在 Web 服务器级别的集成会比较合适。在这个模型中,服务器会收集页面中各个组件的内容并将其 HTML 字符串连接起来返回给用户。内容更新则采用从服务端重新加载的方式或者通过 ajax 进行部分替换。Gustaf Nilsson Kotte 针对这个主题写过一篇综合性的文章。</p>
<p>当用户界面需要提供及时反馈时,即使采用不可靠连接,一个纯粹的服务端渲染网站也不够用。为了实现 <strong>Optimistic UI</strong> 或 <strong>Skeleton Screens</strong> 这样的技术你需要在设备本身对 UI 进行更新。Google 提出的 PWA 巧妙的描述了这种兼顾各方的做法(渐进增强),同时提供 App 一样的性能体验。这种类型的应用在上面的比例尺中位于文档-应用连续统一体中间的某个地方。在这里纯粹的服务端方案已经不再够用,我们必须将主要逻辑放到浏览器中,这正是本文会重点描述的。</p>
<h2 id='3-微前端背后的核心理念'  id="boomdevs_3" >3. <strong>微前端背后的核心理念</strong></h2>
<ul class="list-paddingleft-2">
<li>技术无关每一个团队在选择和升级他们的技术栈时应该能够做到不需要和其他团队进行对接。Custom Elements 是一个隐藏实现细节的非常好的方法,同时能够对外提供一个统一接口。</li>
<li>隔离团队代码即使所有的团队都使用同样的框架,也不要共享一个运行时。构建独立的应用,不要依赖于共享状态或全局变量。</li>
<li> 建立各团队的前缀当隔离已经不可能时要商定一个命名规范。对 CSS、Events、Local Storage 和 Cookie 建立命名空间来避免碰撞并声明所有权。</li>
<li>本地浏览器特性优先于自定义 API采用浏览器事件进行数据沟通而不是构建一个全局的发布者-订阅者系统。如果你确实需要构建一个跨团队的 API,那就确保它越简单越好。</li>
<li>构建自适应网站即使 JavaScript 执行失败或是根本没有执行,你的特性也应该是能够使用的。采用通用渲染或渐进式增强来提高可感知的性能。</li>
</ul>
<hr />
<h2 id='4-dom-就是-api'  id="boomdevs_4" >4. <strong>DOM 就是 API</strong></h2>
<p><strong>自定义元素 Custom Elements</strong> 面向 Web 组件规范中互操作方面,在浏览器中是一个适用于功能集成的基本元素。每个团队采用自己选择的 Web 技术构建他们的组件,并将它们封装到一个 <strong>自定义元素</strong> 中(比如 &lt;order-minicart&gt;&lt;/order-minicart&gt; &lt;order-minicart\&gt;&lt;/order-minicart\&gt;)。这个特定元素的 DOM 声明(标签名、属性和事件)对于其他团队来说体现为一个协定或者叫公共 API。这样做的好处是其他人可以使用这个组件及其功能而不需要知道实现细节,他们只需要能够和 DOM 交互即可。</p>
<p>但仅仅<strong>自定义元素</strong>是不能满足解决方案的所有需求的。为了处理渐进增强、通用渲染或路由我们还需要软件的其他部分。</p>
<p>本文分为两部分。首先我们会介绍页面组合(<strong>Page Composition</strong>) —— 如何使用不同团队提供的组件组合成一个页面。然后我们会给出一些示例展示客户端页面转化(<strong>Page Transition</strong>)的实现。</p>
<h2 id='5-页面组合'  id="boomdevs_5" >5. <strong>页面组合</strong></h2>
<p>除了采用<strong>不同框架</strong>编写的客户端或服务端代码集成,还有很多副主题需要讨论:<strong>隔离 js</strong>的机制、<strong>规避 CSS 冲突</strong>、按需<strong>加载资源</strong>、不同团队<strong>共享公共资源</strong>、处理<strong>数据获取</strong>和思考提供给用户的<strong>加载状态</strong>。我们将会依次讨论这些主题。</p>
<h3 id='5-1-基本原型'  id="boomdevs_6" >5-1. <strong>基本原型</strong></h3>
<p>如下的拖拉机模型商店的产品页面将会作为后续示例的基础。</p>
<p>这个页面主要功能是通过一个变量选择器在三个不同拖拉机模型之间进行选择转换,变量改变时产品图片、名称、价格和推荐都会更新。还有一个<strong>购买按钮</strong>,点击后会将选中的模型添加到购物车中,同时顶部的迷你购物车也会相应更新。</p>
<p><img decoding="async" class="alignnone size-full wp-image-3013" src="http://www.mobabel.net/wp-content/uploads/2018/12/08/4ffce04d92a4d6cb21c1494cdfcd6dc1_143915.gif" width="640" height="378" /></p>
<p>所有的 HTML 页面都通过<strong>纯 JavaScript</strong>和 ES6 模板字符串在客户端生成,<strong>没有任何依赖</strong>。代码使用一个简单的状态/标记分离方式,一旦有变化整个 HTML 页面都会重新渲染 —— 没有炫酷的 DOM 对比功能,也暂时没有<strong>通用渲染</strong>。当然也没有<strong>团队分离</strong> —— 所有代码都在一个 js/css 文件中。</p>
<h3 id='5-2-客户端集成'  id="boomdevs_7" >5-2. <strong>客户端集成</strong></h3>
<p>在如下示例中,这个页面被分隔成不同的组件和片段,分别被三个不同的团队负责。<strong>交易组</strong>(蓝色)负责所有跟付账流程有关的事情 —— 也就是<strong>购买按钮</strong>和<strong>迷你购物车</strong>。<strong>推荐组</strong>(绿色)负责页面中的产品推荐部分。页面本身则由<strong>产品组</strong>(红色)负责。</p>
<p><img loading="lazy" decoding="async" class="alignnone size-full wp-image-3014" src="http://www.mobabel.net/wp-content/uploads/2018/12/08/4ffce04d92a4d6cb21c1494cdfcd6dc1_143923.jpg" width="1000" height="722" srcset="http://www.mobabel.net/wp-content/uploads/2018/12/08/4ffce04d92a4d6cb21c1494cdfcd6dc1_143923.jpg 1000w, http://www.mobabel.net/wp-content/uploads/2018/12/08/4ffce04d92a4d6cb21c1494cdfcd6dc1_143923-300x217.jpg 300w, http://www.mobabel.net/wp-content/uploads/2018/12/08/4ffce04d92a4d6cb21c1494cdfcd6dc1_143923-768x554.jpg 768w" sizes="(max-width: 1000px) 100vw, 1000px" /></p>
<p><strong>产品组</strong>决定哪个功能点被采用以及该功能在页面布局的位置。页面包含的信息可以由产品组自身提供,比如产品名称、图片和可采用的参数,但还可以包括其他团队提供的片段(自定义元素)。</p>
<h3 id='5-3-如何创建一个自定义元素'  id="boomdevs_8" >5-3. <strong>如何创建一个自定义元素</strong></h3>
<p>让我们把<strong>购买按钮</strong>作为一个示例。产品组简单的将 <code class=""><span class="">&lt;blue-buysku="t_porsche"&gt;&lt;/blue-buy&gt;</span></code> 加入到页面中期望的位置就可以使用这个按钮了。要让这个按钮起作用,交易组还需要在页面中注册元素。</p><pre class="crayon-plain-tag">class BlueBuy extends HTMLElement {
   constructor() {
       super();
       this.innerHTML = `&lt;button type="button"&gt;buy for 66,00 €&lt;/button&gt;`;
   }
   disconnectedCallback() { ... }
}
window.customElements.define('blue-buy', BlueBuy);</pre><p>现在每当浏览器遇到一个新的 <code class=""><span class="">blue-buy</span></code> 标签时,都会调用这个构造器。其中, <code class=""><span class="">this</span></code> 是这个自定义元素 DOM 根节点的引用。所有标准 DOM 元素的属性和方法都可以使用,比如 <code class=""><span class="">innerHTML</span></code> 或 <code class=""><span class="">getAttribute()</span></code>。</p>
<p><img loading="lazy" decoding="async" class="alignnone size-full wp-image-3015" src="http://www.mobabel.net/wp-content/uploads/2018/12/08/4ffce04d92a4d6cb21c1494cdfcd6dc1_143933.gif" width="480" height="178" /></p>
<p>根据标准文档的定义,当命名自定义元素时唯一的需求是名称中必须<strong>包含一个破折号 &#8211;</strong> 以确保和未来新的 HTML 标签进行兼容。在后面的示例中则使用了 <code class=""><span class="">[team_color]-[feature]</span></code> 命名规范。团队命名空间预防了碰撞,这种方法让一个功能点的权责变得更分明:只要看看 DOM 就知道了。</p>
<h3 id='5-4-父子元素通信-dom-修改'  id="boomdevs_9" >5-4. <strong>父子元素通信 / DOM 修改</strong></h3>
<p>当用户在变量选择器中选择了另外一个拖拉机时,<strong>购买按钮</strong>必须相应的进行<strong>更新</strong>。要达到这种效果,产品组只需要从 DOM 中<strong>移除</strong>相应元素,并<strong>插入</strong>一个新的。</p><pre class="crayon-plain-tag">container.innerHTML;
// =&gt; &lt;blue-buy sku="t_porsche"&gt;...&lt;/blue-buy&gt;
container.innerHTML = '&lt;blue-buy sku="t_fendt"&gt;&lt;/blue-buy&gt;';</pre><p>老元素的 <code class=""><span class="">disconnectedCallback</span></code> 方法会被同步调用进行一些清理资源的操作比如移除事件监听器。然后新创建的 <code class=""><span class="">t_fendt</span></code> 元素的 <code class=""><span class="">constructor</span></code> 会被调用。</p>
<p>另外一个性能更好的选择是仅仅更新现有元素的 <code class=""><span class="">sku</span></code> 属性。</p><pre class="crayon-plain-tag">document.querySelector('blue-buy').setAttribute('sku', 't_fendt');</pre><p>如果产品组使用了以 DOM 对比为特色的模板引擎,比如 React,那它的算法就会自动完成上述功能。</p>
<p><img loading="lazy" decoding="async" class="alignnone size-full wp-image-3016" src="http://www.mobabel.net/wp-content/uploads/2018/12/08/4ffce04d92a4d6cb21c1494cdfcd6dc1_143939.gif" width="480" height="57" /></p>
<p>要支持这种效果,自定义元素可以实现 <code class=""><span class="">attributeChangedCallback</span></code> 并指定一个 <code class=""><span class="">observedAttributes</span></code> 列表来触发这个回调。</p><pre class="crayon-plain-tag">const prices = {
   t_porsche: '66,00 €',
   t_fendt: '54,00 €',
   t_eicher: '58,00 €',
};
class BlueBuy extends HTMLElement {
   static get observedAttributes() {
       return ['sku'];
   }
   constructor() {
       super();
       this.render();
   }
   render() {
       const sku = this.getAttribute('sku');
       const price = prices[sku];
       this.innerHTML = `&lt;button type="button"&gt;buy for ${price}&lt;/button&gt;`;
       }
   attributeChangedCallback(attr, oldValue, newValue) {
       this.render();
   }
   disconnectedCallback() {...}
}
window.customElements.define('blue-buy', BlueBuy);</pre><p>为避免重复,引入一个 <code class=""><span class="">render()</span></code> 方法并在 <code class=""><span class="">constructor</span></code> 和 <code class=""><span class="">attributeChangedCallback</span></code> 中调用。这个方法收集需要的数据,并填充新标签的 <code class=""><span class="">innerHTML</span></code> 属性。当决定在自定义元素中采用一个更加成熟的模板引擎或框架时,这里便是初始化代码所呆的地方。</p>
<h3 id='5-5-浏览器支持'  id="boomdevs_10" >5-5. <strong>浏览器支持</strong></h3>
<p>上例采用了 Custom Element 规范 V1 版,目前已经在 Chrome, Safari 和 Opera 中得到支持。但是通过 document-register-element 这个轻量级且经过大量测试的 polyfill 可以让该特性在所有浏览器中运行。在底层,它使用了广泛支持的 Mutation Observer API,所以并没有在背后使用 DOM 树监听这种侵入式的 hack 方法。</p>
<h3 id='5-6-框架兼容性'  id="boomdevs_11" >5-6. <strong>框架兼容性</strong></h3>
<p>因为自定义元素 Custom Element 是一个 Web 标准,所有的主流 JavaScript 框架都支持,比如 Angular、React、Preact、Vue 或 Hyperapp。但深入到细节时,就会发现有些框架依然存在实现上的问题。可以访问 Custom Elements Everywhere 这个兼容性测试套件,Rob Dodson 把没有解决的问题都高亮显示了。</p>
<h3 id='5-7-子父元素或兄弟元素通信-dom-事件'  id="boomdevs_12" >5-7. <strong>子父元素或兄弟元素通信 / DOM 事件</strong></h3>
<p>然而,对于所有的交互来说从上至下传递属性是不够的。在我们的示例中,当用户对购买按钮执行一次点击事件时,迷你购物车应该刷新。</p>
<p>上面这两个片段都由交易组(蓝色)维护的,所以为了达到迷你购物车和按钮通信的效果他们可以构建一种内建的 JavaScript API 进行通信。但这样就需要组件实例之间相互了解,同时也违背了隔离的原则。</p>
<p>一种更加干净的方法是采用发布者订阅者机制:一个组件可以发布信息,其他组件则订阅指定的主题(topic)。幸运的是浏览器内建了这个特性,这也正是 <code class=""><span class="">click</span></code>、 <code class=""><span class="">select</span></code>、 <code class=""><span class="">mouseover</span></code> 等浏览器事件的工作机制。除了这些本地事件,还有一种可能性是通过 <code class=""><span class="">newCustomEvent(...)</span></code> 来创建更加高级别的事件。事件总是绑定到它们创建或者分配的 DOM 节点上,大部分本地事件也支持冒泡的特性,这让监听 DOM 中特定子树节点的所有事件成为可能。如果你想要监听页面上的所有事件,将事件监听器附加到 window 元素上就 OK 了。如下是本示例中 <code class=""><span class="">blue:basket:changed</span></code> 事件创建的大概样子:</p><pre class="crayon-plain-tag">class BlueBuy extends HTMLElement {
 [...]
 connectedCallback() {
   [...]
   this.render();
   this.firstChild.addEventListener('click', this.addToCart);
 }
 addToCart() {
   // maybe talk to an api
   this.dispatchEvent(new CustomEvent('blue:basket:changed', {
     bubbles: true,
   }));
 }
 render() {
   this.innerHTML = `&lt;button type="button"&gt;buy&lt;/button&gt;`;
 }
 disconnectedCallback() {
   this.firstChild.removeEventListener('click', this.addToCart);
 }
}</pre><p>&nbsp;</p>
<p>现在迷你购物车可以在 <code class=""><span class="">window</span></code> 对象上订阅这个事件了,在需要刷新数据时它就会得到通知。</p><pre class="crayon-plain-tag">class BlueBasket extends HTMLElement {
 connectedCallback() {
   [...]
   window.addEventListener('blue:basket:changed', this.refresh);
 }
 refresh() {
   // fetch new data and render it
 }
 disconnectedCallback() {
   window.removeEventListener('blue:basket:changed', this.refresh);
 }
}</pre><p>采用这种方法实现时,迷你购物车片段增加了一个不在它范围之内(window)的 DOM 元素监听器。对于大部分应用来说,这个做法没有什么问题,但是如果你不太满意这种做法,还可以让页面自身(产品组)去监听这个事件,并通过调用 DOM 元素的 <code class=""><span class="">refresh()</span></code> 方法来通知迷你购物车。</p><pre class="crayon-plain-tag">// page.js
const $ = document.getElementsByTagName;
$('blue-buy')[0].addEventListener('blue:basket:changed', function() {
 $('blue-basket')[0].refresh();
});</pre><p>命令式调用 DOM 方法其实相当罕见,但比如在 video 元素 API 中就有这种做法。如果可能的话,还是应该推荐这种命令式的方法(属性更改)。</p>
<h2 id='6-服务端渲染-通用渲染'  id="boomdevs_13" >6. <strong>服务端渲染 / 通用渲染</strong></h2>
<p>在浏览器中采用自定义元素 Custom Elements 来集成组件是个绝好的做法。但实际在构建一个 Web 中可访问的站点时,很可能是初次加载性能才是关键点,在所有的 JS 框架全部加载并执行之前用户只会看到白屏。另外,还有一个值得思考的是如果 JavaScript 执行失败或者被阻塞时网站会发生什么。Jeremy Keith 在他的 ebook/播客 Resilient Web Design 中解释了这个问题的重要性。所以能够在服务端渲染核心内容才是关键。不幸的是 Web 组件规范根本没有讨论服务端渲染。JavaScript 没有,Custom Elements 也没有:(</p>
<h3 id='6-1-自定义元素-+-服务端包含-includes-=-&#x2764;'  id="boomdevs_14" >6-1. <strong>自定义元素 + 服务端包含(Includes) = <img src="https://s.w.org/images/core/emoji/15.0.3/72x72/2764.png" alt="❤" class="wp-smiley" style="height: 1em; max-height: 1em;" /></strong></h3>
<p>为了引入服务端渲染,前面的示例进行了重构。每个团队都有他们自己的 express 服务器,自定义元素的 <code class=""><span class="">render()</span></code> 方法也都通过 url 来进行访问。</p><pre class="crayon-plain-tag">$ curl http://127.0.0.1:3000/blue-buy?sku=t_porsche
&lt;button type="button"&gt;buy for 66,00 €&lt;/button&gt;</pre><p>自定义元素的标签名被用作路径名,属性名成为了查询参数。这样为每个组件用服务端渲染内容的方法就有了。再配合上 <code class=""><span class="">&lt;blue-buy&gt;</span></code> 自定义元素,一种非常接近于<strong>通用 Web 组件</strong>的东西就出来了:</p><pre class="crayon-plain-tag">&lt;blue-buy sku="t_porsche"&gt;
   &lt;!--#include virtual="/blue-buy?sku=t_porsche" --&gt;
&lt;/blue-buy&gt;</pre><p><code class=""><span class="">#include</span></code> 注释是服务端包含 Server Side Includes 的一部分,这个功能在大部分 Web 服务器中都支持。没错,这个就是很早以前我们在网站中嵌入当前日期所采用的同样技术。也有几个其他可选技术比如 ESI、nodesi、compoxure 和 tailor,但是对于我们的项目 SSI 已经被证明是一个简单同时也相当稳定的解决方案。</p>
<p>在 Web 服务器将完整的页面发送到浏览器之前 <code class=""><span class="">#include</span></code> 注释被替换为 <code class=""><span class="">/blue-buy?sku=t_porsche</span></code> 的返回值。在 Nginx 中配置如下:</p><pre class="crayon-plain-tag">upstream team_blue {
 server team_blue:3001;
}
upstream team_green {
 server team_green:3002;
}
upstream team_red {
 server team_red:3003;
}
server {
 listen 3000;
 ssi on;
 location /blue {
   proxy_pass  http://team_blue;
 }
 location /green {
   proxy_pass  http://team_green;
 }
 location /red {
   proxy_pass  http://team_red;
 }
 location / {
   proxy_pass  http://team_red;
 }
}</pre><p>指令 <code class=""><span class="">ssi:on;</span></code> 用来开启 SSI 功能, <code class=""><span class="">upstream</span></code> 和 <code class=""><span class="">location</span></code> 块用来确保每个团队的 url 都会被正确分配到对应的服务,比如以 <code class=""><span class="">/blue</span></code> 开头的 url 会被路由到相应的应用服务( <code class=""><span class="">team_blue:3001</span></code>)。另外, <code class=""><span class="">/</span></code> 路由被映射到负责首页和产品页的产品组(红色)。</p>
<p>下面的动画演示了在一个 <strong>JavaScript 被禁用</strong>的浏览器中拖拉机商店使用情况。</p>
<p><img loading="lazy" decoding="async" class="alignnone size-full wp-image-3017" src="http://www.mobabel.net/wp-content/uploads/2018/12/08/4ffce04d92a4d6cb21c1494cdfcd6dc1_143945.gif" width="640" height="421" /></p>
<p>变量选择按钮现在是一个真实的链接了,每一次点击都会让整个页面重新加载。右边的终端展示了一个请求如何被路由到产品组的流程,产品组则控制整个产品页,里面的标记则由推荐组和交易组的内容片段来提供。</p>
<p>当打开启用 JavaScript 的开关后,在服务端日志消息中只有第一条请求才会显示。所有后续的拖拉机变化逻辑都在客户端处理了,就和前面第一个示例一样。在后面的示例中,产品数据将会从 JavaScript 代码中被抽离出来,并在需要的时候通过一个 REST API 进行加载。</p>
<p>你可以在本机运行这个代码。只需要安装 Docker Compose[ <strong><em>https://docs.docker.com/compose/install/</em></strong> ]。</p><pre class="crayon-plain-tag">git clone https://github.com/neuland/micro-frontends.git
cd micro-frontends/2-composition-universal
docker-compose up --build</pre><p>Docker 会在 3000 端口启动 Nginx,并为每个团队构建 node.js 镜像。当你在浏览器中打开 <code class=""><span class="">http://127.0.0.1:3000/</span></code> 时应该会看到一个红色的拖拉机。通过 <code class=""><span class="">docker-compose</span></code> 给出的组合日志可以很轻松的看到网络中发生了什么。不好的是目前还不能控制输出信息的颜色,所以你不得不接受一个事实,那就是蓝色的交易组可能被高亮成绿色 <img src="https://s.w.org/images/core/emoji/15.0.3/72x72/1f642.png" alt="🙂" class="wp-smiley" style="height: 1em; max-height: 1em;" /></p>
<p><code class=""><span class="">src</span></code> 中的文件会被映射到独立的容器中,当你进行代码更改后 node 应用会重启。修改 <code class=""><span class="">nginx.conf</span></code> 需要重启 <code class=""><span class="">docker-compose</span></code> 才能生效。然后你就尽情瞎搞并提供反馈吧。</p>
<h3 id='6-2-数据获取-加载状态'  id="boomdevs_15" >6-2. <strong>数据获取 &amp; 加载状态</strong></h3>
<p>待续&#8230;</p>
<p>关注 Github Repo[ <em><strong>https://github.com/neuland/micro-frontends</strong></em> ] 来获取通知</p>
<h2 id='7-参考资源'  id="boomdevs_16" >7. <strong>参考资源</strong></h2>
<blockquote>
<ul class="list-paddingleft-2">
<li>[ https://speakerdeck.com/naltatis/micro-frontends-building-a-modern-webapp-with-multiple-teams ]</li>
<li>[ https://medium.com/@tomsoderlund/micro-frontends-a-microservice-approach-to-front-end-web-development-f325ebdadc16 ]</li>
<li>[ https://custom-elements-everywhere.com/ ]</li>
<li> [ https://www.manufactum.com/ ]</li>
</ul>
</blockquote>
<p>&nbsp;</p>
<p>[转]<a href="https://mp.weixin.qq.com/s?__biz=MzAxODE2MjM1MA==&amp;mid=2651555193&amp;idx=1&amp;sn=9961957377d10d7361e1ed7d127fa036&amp;chksm=802552b8b752dbae3cbcf440c813274652fda873f591f4313375715530f703748c499e027514&amp;mpshare=1&amp;scene=24&amp;srcid=#rd">微前端 &#8211; 将微服务理念延伸到前端开发中</a></p>
<p><a href="http://zzfe.org/#/detail/59dc6e1f91d3e35ba880fd0d">http://zzfe.org/#/detail/59dc6e1f91d3e35ba880fd0d</a></p>The post <a href="http://www.mobabel.net/%e8%bd%ac%e5%be%ae%e5%89%8d%e7%ab%af-%e5%b0%86%e5%be%ae%e6%9c%8d%e5%8a%a1%e7%90%86%e5%bf%b5%e5%bb%b6%e4%bc%b8%e5%88%b0%e5%89%8d%e7%ab%af%e5%bc%80%e5%8f%91%e4%b8%ad/">[转]微前端 – 将微服务理念延伸到前端开发中</a> first appeared on <a href="http://www.mobabel.net">Mobabel</a>.]]></content:encoded>
					
					<wfw:commentRss>http://www.mobabel.net/%e8%bd%ac%e5%be%ae%e5%89%8d%e7%ab%af-%e5%b0%86%e5%be%ae%e6%9c%8d%e5%8a%a1%e7%90%86%e5%bf%b5%e5%bb%b6%e4%bc%b8%e5%88%b0%e5%89%8d%e7%ab%af%e5%bc%80%e5%8f%91%e4%b8%ad/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>[转]从 0 到 1 再到 100, 搭建、编写、构建一个前端项目</title>
		<link>http://www.mobabel.net/%e8%bd%ac%e4%bb%8e-0-%e5%88%b0-1-%e5%86%8d%e5%88%b0-100-%e6%90%ad%e5%bb%ba%e3%80%81%e7%bc%96%e5%86%99%e3%80%81%e6%9e%84%e5%bb%ba%e4%b8%80%e4%b8%aa%e5%89%8d%e7%ab%af%e9%a1%b9%e7%9b%ae/</link>
					<comments>http://www.mobabel.net/%e8%bd%ac%e4%bb%8e-0-%e5%88%b0-1-%e5%86%8d%e5%88%b0-100-%e6%90%ad%e5%bb%ba%e3%80%81%e7%bc%96%e5%86%99%e3%80%81%e6%9e%84%e5%bb%ba%e4%b8%80%e4%b8%aa%e5%89%8d%e7%ab%af%e9%a1%b9%e7%9b%ae/#respond</comments>
		
		<dc:creator><![CDATA[leelight]]></dc:creator>
		<pubDate>Thu, 06 Dec 2018 23:07:05 +0000</pubDate>
				<category><![CDATA[Theory & Solution]]></category>
		<category><![CDATA[Frontend]]></category>
		<guid isPermaLink="false">http://www.mobabel.net/?p=2931</guid>

					<description><![CDATA[<p>1. 1. 选择现成的项目模板还是自己搭建项目骨架 搭建一个前端项目的方式有两种:选择现成的项目模板、自己搭建项目骨架。 选择一个现成项目模板是搭建一个项目最快的方式,模板已经把基本的骨架都搭建好了,你只需要向里面填充具体的业务代码,就可以通过内置的工具与命令构建代码、部署到服务器等。 一般来说,一个现成的项目模板会预定义一定的目录结构、书写方式,在编写项目代码时需要遵循相应的规范;也会内置必要的工具,比如 .editorconfig、eslint、stylelint、prettier、husky、lint-staged 等;也会内置必要的命令(package.json &#124; scripts),比如 本地开发:npm run dev、本地预览:npm run start、构建:npm run build、部署:npm run deploy等。 社区比较好的项目模板: react-boilerplate ant-design-pro vue-element-admin react-starter-kit create-react-app create-lila-app(我自己用的,哈哈) 这些模板的使用又分为两种:使用 git 直接克隆到本地、使用命令行创建。 (使用现有模板构建的项目,可以跳过第 2 ~ 7 步) 1-1. 1.1 使用 git 直接克隆到本地 这是一种真正意义上的模板,可以直接到模板项目的 github 主页,就能看到整个骨架,比如 react-boilerplate、ant-design-pro、vue-element-admin、react-starter-kit。 以 react-boilerplate 为例:&#46;&#46;&#46;</p>
The post <a href="http://www.mobabel.net/%e8%bd%ac%e4%bb%8e-0-%e5%88%b0-1-%e5%86%8d%e5%88%b0-100-%e6%90%ad%e5%bb%ba%e3%80%81%e7%bc%96%e5%86%99%e3%80%81%e6%9e%84%e5%bb%ba%e4%b8%80%e4%b8%aa%e5%89%8d%e7%ab%af%e9%a1%b9%e7%9b%ae/">[转]从 0 到 1 再到 100, 搭建、编写、构建一个前端项目</a> first appeared on <a href="http://www.mobabel.net">Mobabel</a>.]]></description>
										<content:encoded><![CDATA[<h2 id='1-1-选择现成的项目模板还是自己搭建项目骨架'  id="boomdevs_1" id="articleHeader1" >1. 1. 选择现成的项目模板还是自己搭建项目骨架</h2>
<p>搭建一个前端项目的方式有两种:选择现成的项目模板、自己搭建项目骨架。</p>
<p>选择一个现成项目模板是搭建一个项目最快的方式,模板已经把基本的骨架都搭建好了,你只需要向里面填充具体的业务代码,就可以通过内置的工具与命令构建代码、部署到服务器等。<span id="more-2931"></span></p>
<p>一般来说,一个现成的项目模板会预定义一定的目录结构、书写方式,在编写项目代码时需要遵循相应的规范;也会内置必要的工具,比如 <a href="http://editorconfig.org/" target="_blank" rel="nofollow noopener noreferrer">.editorconfig</a>、<a href="https://github.com/eslint/eslint" target="_blank" rel="nofollow noopener noreferrer">eslint</a>、<a href="https://github.com/stylelint/stylelint" target="_blank" rel="nofollow noopener noreferrer">stylelint</a>、<a href="https://github.com/prettier/prettier" target="_blank" rel="nofollow noopener noreferrer">prettier</a>、<a href="https://github.com/typicode/husky" target="_blank" rel="nofollow noopener noreferrer">husky</a>、<a href="https://github.com/okonet/lint-staged" target="_blank" rel="nofollow noopener noreferrer">lint-staged</a> 等;也会内置必要的命令(<code>package.json | scripts</code>),比如 <code>本地开发:npm run dev</code>、<code>本地预览:npm run start</code>、<code>构建:npm run build</code>、<code>部署:npm run deploy</code>等。</p>
<p>社区比较好的项目模板:</p>
<ul>
<li><a href="https://github.com/react-boilerplate/react-boilerplate" target="_blank" rel="nofollow noopener noreferrer">react-boilerplate</a></li>
<li><a href="https://github.com/ant-design/ant-design-pro" target="_blank" rel="nofollow noopener noreferrer">ant-design-pro</a></li>
<li><a href="https://github.com/PanJiaChen/vue-element-admin" target="_blank" rel="nofollow noopener noreferrer">vue-element-admin</a></li>
<li><a href="https://github.com/kriasoft/react-starter-kit" target="_blank" rel="nofollow noopener noreferrer">react-starter-kit</a></li>
<li><a href="https://github.com/facebook/create-react-app" target="_blank" rel="nofollow noopener noreferrer">create-react-app</a></li>
<li><a href="https://github.com/senntyou/lila/tree/master/packages/create-lila-app" target="_blank" rel="nofollow noopener noreferrer">create-lila-app</a>(我自己用的,哈哈)</li>
</ul>
<p>这些模板的使用又分为两种:使用 <code>git</code> 直接克隆到本地、使用命令行创建。</p>
<p>(使用现有模板构建的项目,可以跳过第 2 ~ 7 步)</p>
<h3 id='1-1-1-1-使用-git-直接克隆到本地'  id="boomdevs_2" id="articleHeader2" >1-1. 1.1 使用 <code>git</code> 直接克隆到本地</h3>
<p>这是一种真正意义上的模板,可以直接到模板项目的 <code>github</code> 主页,就能看到整个骨架,比如 <a href="https://github.com/react-boilerplate/react-boilerplate" target="_blank" rel="nofollow noopener noreferrer">react-boilerplate</a>、<a href="https://github.com/ant-design/ant-design-pro" target="_blank" rel="nofollow noopener noreferrer">ant-design-pro</a>、<a href="https://github.com/PanJiaChen/vue-element-admin" target="_blank" rel="nofollow noopener noreferrer">vue-element-admin</a>、<a href="https://github.com/kriasoft/react-starter-kit" target="_blank" rel="nofollow noopener noreferrer">react-starter-kit</a>。</p>
<p>以 <code>react-boilerplate</code> 为例:</p>
<p>克隆到本地:</p><pre class="crayon-plain-tag">git clone --depth=&lt;span class=&quot;hljs-number&quot;&gt;1&lt;/span&gt;&nbsp;&lt;span class=&quot;hljs-string&quot;&gt;https:&lt;/span&gt;&lt;span class=&quot;hljs-comment&quot;&gt;//github.com/react-boilerplate/react-boilerplate.git &amp;lt;你的项目名字&amp;gt;&lt;/span&gt;</pre><p>切换到目录下:</p><pre class="crayon-plain-tag">cd&nbsp;&lt;span class=&quot;hljs-tag&quot;&gt;&amp;lt;&lt;span class=&quot;hljs-name&quot;&gt;你的项目名字&lt;/span&gt;&amp;gt;&lt;/span&gt;</pre><p>一般来说,接下来运行 <code>npm run install</code> 安装项目的依赖后,就可以运行;有些模板可能有内置的初始化命令,比如 <code>react-boilerplate</code>:</p><pre class="crayon-plain-tag">&lt;span class=&quot;hljs-attribute&quot;&gt;npm&lt;/span&gt;&nbsp;run setup</pre><p>启动应用:</p><pre class="crayon-plain-tag">npm&nbsp;&lt;span class=&quot;hljs-keyword&quot;&gt;start&lt;/span&gt;</pre><p>这时,就可以在浏览器中预览应用了。</p>
<h3 id='1-2-1-2-使用命令行创建'  id="boomdevs_3" id="articleHeader3" >1-2. 1.2 使用命令行创建</h3>
<p>这种方式需要安装相应的命令,然后由命令来创建项目。</p>
<p>以 <code>create-react-app</code> 为例:</p>
<p>安装命令:</p><pre class="crayon-plain-tag">npm&nbsp;&lt;span class=&quot;hljs-keyword&quot;&gt;install&lt;/span&gt;&nbsp;-g&nbsp;&lt;span class=&quot;hljs-keyword&quot;&gt;create&lt;/span&gt;-react-app</pre><p>创建项目:</p><pre class="crayon-plain-tag">&lt;span class=&quot;hljs-keyword&quot;&gt;create&lt;/span&gt;-react-app my-app</pre><p>运行应用:</p><pre class="crayon-plain-tag">&lt;span class=&quot;hljs-attr&quot;&gt;cd&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;my-app&lt;/span&gt;
&lt;span class=&quot;hljs-attr&quot;&gt;npm&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;start&lt;/span&gt;</pre><p></p>
<h3 id='1-3-1-3-自己搭建项目骨架'  id="boomdevs_4" id="articleHeader4" >1-3. 1.3 自己搭建项目骨架</h3>
<p>如果你需要定制化,可以选择自己搭建项目的骨架,但这需要开发者对构建工具如 <code>webpack</code>、<code>npm</code>、<code>node</code> 及其生态等有相当的了解与应用,才能完美的把控整个项目。</p>
<p>下面将会一步一步的说明如何搭建一个定制化的项目骨架。</p>
<h2 id='2-2-选择合适的规范来写代码'  id="boomdevs_5" id="articleHeader5" >2. 2. 选择合适的规范来写代码</h2>
<p><code>js</code> 模块化的发展大致有这样一个过程 <code>iife =&gt; commonjs/amd =&gt; es6</code>,而在这几个规范中:</p>
<ul>
<li><code>iife</code>: <code>js</code> 原生支持,但一般不会直接使用这种规范写代码</li>
<li><code>amd</code>: <a href="https://github.com/requirejs/requirejs" target="_blank" rel="nofollow noopener noreferrer">requirejs</a> 定义的加载规范,但随着构建工具的出现,便一般不会用这种规范写代码</li>
<li><code>commonjs</code>: <code>node</code> 的模块加载规范,一般会用这种规范写 <code>node</code> 程序</li>
<li><code>es6</code>: <code>ECMAScript2015</code> 定义的模块加载规范,需要转码后浏览器才能运行</li>
</ul>
<p>这里推荐使用 <code>es6</code> 的模块化规范来写代码,然后用工具转换成 <code>es5</code> 的代码,并且 <code>es6</code> 的代码可以使用 <a href="https://en.wikipedia.org/wiki/Tree_shaking" target="_blank" rel="nofollow noopener noreferrer">Tree shaking</a> 功能。</p>
<p>参考:</p>
<ul>
<li><a href="https://en.wikipedia.org/wiki/Immediately-invoked_function_expression" target="_blank" rel="nofollow noopener noreferrer">IIFE(Immediately-invoked function expression)</a></li>
<li><a href="https://en.wikipedia.org/wiki/Tree_shaking" target="_blank" rel="nofollow noopener noreferrer">Tree shaking</a></li>
<li><a href="https://webpack.js.org/guides/tree-shaking/" target="_blank" rel="nofollow noopener noreferrer">webpack &#8211; tree-shaking</a></li>
<li><a href="https://blog.csdn.net/haodawang/article/details/77199980" target="_blank" rel="nofollow noopener noreferrer">webpack 如何优雅的使用tree-shaking(摇树优化)</a></li>
</ul>
<h2 id='3-3-选择合适的构建工具'  id="boomdevs_6" id="articleHeader6" >3. 3. 选择合适的构建工具</h2>
<p>对于前端项目来说,构建工具一般都选用 <a href="https://github.com/webpack/webpack" target="_blank" rel="nofollow noopener noreferrer">webpack</a>,<code>webpack</code> 提供了强大的功能和配置化运行。如果你不喜欢复杂的配置,可以尝试 <a href="https://github.com/parcel-bundler/parcel" target="_blank" rel="nofollow noopener noreferrer">parcel</a>。</p>
<p>参考:</p>
<ul>
<li><a href="https://segmentfault.com/a/1190000016409719">webpack 之外的另一种选择:parcel</a></li>
</ul>
<h2 id='4-4-确定是单页面应用-spa-还是多页面应用'  id="boomdevs_7" id="articleHeader7" >4. 4. 确定是单页面应用(SPA)还是多页面应用</h2>
<p>因为单页面应用与多页面应用在构建的方式上有很大的不同,所以需要从项目一开始就确定,使用哪种模式来构建项目。</p>
<h3 id='4-1-4-1-多页面应用'  id="boomdevs_8" id="articleHeader8" >4-1. 4.1 多页面应用</h3>
<p>传统多页面是由后端控制一个 <code>url</code> 对应一个 <code>html</code> 文件,页面之间的跳转需要根据后端给出的 <code>url</code> 跳转到新的 <code>html</code> 上。比如:</p><pre class="crayon-plain-tag">&lt;span class=&quot;hljs-string&quot;&gt;http:&lt;/span&gt;&lt;span class=&quot;hljs-comment&quot;&gt;//www.example.com/page1 -&amp;gt; path/to/page1.html&lt;/span&gt;
&lt;span class=&quot;hljs-string&quot;&gt;http:&lt;/span&gt;&lt;span class=&quot;hljs-comment&quot;&gt;//www.example.com/page2 -&amp;gt; path/to/page2.html&lt;/span&gt;
&lt;span class=&quot;hljs-string&quot;&gt;http:&lt;/span&gt;&lt;span class=&quot;hljs-comment&quot;&gt;//www.example.com/page3 -&amp;gt; path/to/page3.html&lt;/span&gt;</pre><p>这种方式的应用,项目里会有多个入口文件,搭建项目的时候就需要对这种多入口模式进行封装。另外,也可以选择一些封装的多入口构建工具,如 <a href="https://github.com/senntyou/lila" target="_blank" rel="nofollow noopener noreferrer">lila</a>。</p>
<h3 id='4-2-4-2-单页面应用'  id="boomdevs_9" id="articleHeader9" >4-2. 4.2 单页面应用</h3>
<p><a href="https://en.wikipedia.org/wiki/Single-page_application" target="_blank" rel="nofollow noopener noreferrer">单页面应用(single page application)</a>,就是只有一个页面的应用,页面的刷新和内部子页面的跳转完全由 <code>js</code> 来控制。</p>
<p>一般单页面应用都有以下几个特点:</p>
<ul>
<li>本地路由,由 <code>js</code> 定义路由、根据路由渲染页面、控制页面的跳转</li>
<li>所有文件只会加载一次,最大限度重用文件,并且极大提升加载速度</li>
<li>按需加载,只有真正使用到页面的时候,才加载相应的文件</li>
</ul>
<p>这种方式的应用,项目里只有一个入口文件,便无需封装。</p>
<p>参考:</p>
<ul>
<li><a href="https://segmentfault.com/a/1190000015297908">单页面应用(SPA)、按需加载</a></li>
</ul>
<h2 id='5-5-选择合适的前端框架与-ui-库'  id="boomdevs_10" id="articleHeader10" >5. 5. 选择合适的前端框架与 UI 库</h2>
<p>一般在搭建项目的时候就需要定下前端框架与 UI 库,因为如果后期想更换前端框架和 UI 库,代价是很大的。</p>
<p>比较现代化的前端框架:</p>
<ul>
<li><a href="https://github.com/facebook/react" target="_blank" rel="nofollow noopener noreferrer">react</a></li>
<li><a href="https://github.com/vuejs/vue" target="_blank" rel="nofollow noopener noreferrer">vue</a></li>
<li><a href="https://github.com/angular/angular" target="_blank" rel="nofollow noopener noreferrer">angular</a></li>
</ul>
<p>一些不错的组合:</p>
<ul>
<li><a href="https://github.com/jquery/jquery" target="_blank" rel="nofollow noopener noreferrer">jquery</a> + <a href="https://github.com/twbs/bootstrap" target="_blank" rel="nofollow noopener noreferrer">bootstrap</a>:比较经典的</li>
<li><a href="https://github.com/facebook/react" target="_blank" rel="nofollow noopener noreferrer">react</a> + <a href="https://github.com/ant-design/ant-design" target="_blank" rel="nofollow noopener noreferrer">ant-design</a>、<a href="https://github.com/mui-org/material-ui" target="_blank" rel="nofollow noopener noreferrer">material-ui</a>、<a href="https://github.com/Semantic-Org/Semantic-UI" target="_blank" rel="nofollow noopener noreferrer">Semantic-UI</a>:<code>react</code> 的组合</li>
<li><a href="https://github.com/vuejs/vue" target="_blank" rel="nofollow noopener noreferrer">vue</a> + <a href="https://github.com/ElemeFE/element" target="_blank" rel="nofollow noopener noreferrer">element</a>、<a href="https://github.com/iview/iview" target="_blank" rel="nofollow noopener noreferrer">iview</a>、<a href="https://github.com/airyland/vux" target="_blank" rel="nofollow noopener noreferrer">vux</a>、<a href="https://github.com/ElemeFE/mint-ui" target="_blank" rel="nofollow noopener noreferrer">mint-ui</a>:<code>vue</code> 的组合</li>
</ul>
<p>参考:</p>
<ul>
<li><a href="https://segmentfault.com/a/1190000016334838">前端最受欢迎的 UI 框架</a></li>
</ul>
<h2 id='6-6-定好目录结构'  id="boomdevs_11" id="articleHeader11" >6. 6. 定好目录结构</h2>
<p>一个好的目录结构对一个好的项目而言是非常必要的。</p>
<p>一个好的目录结构应当具有以下的一些特点:</p>
<ol>
<li>解耦:代码尽量去耦合,这样代码逻辑清晰,也容易扩展</li>
<li>分块:按照功能对代码进行分块、分组,并能快捷的添加分块、分组</li>
<li>编辑器友好:需要更新功能时,可以很快的定位到相关文件,并且这些文件应该是很靠近的,而不至于到处找文件</li>
</ol>
<p>比较推荐的目录结构:</p>
<p><strong><em>多页面应用</em></strong></p><pre class="crayon-plain-tag">|&lt;span class=&quot;hljs-comment&quot;&gt;-- src/ 源代码目录&lt;/span&gt;

    |&lt;span class=&quot;hljs-comment&quot;&gt;-- page1/ page1 页面的工作空间(与这个页面相关的文件都放在这个目录下)&lt;/span&gt;
        |&lt;span class=&quot;hljs-comment&quot;&gt;-- index.html html 入口文件&lt;/span&gt;
        |&lt;span class=&quot;hljs-comment&quot;&gt;-- index.js js 入口文件&lt;/span&gt;
        |&lt;span class=&quot;hljs-comment&quot;&gt;-- index.(css|less|scss) 样式入口文件&lt;/span&gt;
        |&lt;span class=&quot;hljs-comment&quot;&gt;-- html/ html 片段目录&lt;/span&gt;
        |&lt;span class=&quot;hljs-comment&quot;&gt;-- (css|less|scss)/ 样式文件目录&lt;/span&gt;
        |&lt;span class=&quot;hljs-comment&quot;&gt;-- mock/ 本地 json 数据模拟&lt;/span&gt;
        |&lt;span class=&quot;hljs-comment&quot;&gt;-- images/ 图片文件目录&lt;/span&gt;
        |&lt;span class=&quot;hljs-comment&quot;&gt;-- components/ 组件目录(如果基于 react, vue 等组件化框架)&lt;/span&gt;
        |&lt;span class=&quot;hljs-comment&quot;&gt;-- ...&lt;/span&gt;
        
    |&lt;span class=&quot;hljs-comment&quot;&gt;-- sub-dir/ 子目录&lt;/span&gt;
        |&lt;span class=&quot;hljs-comment&quot;&gt;-- page2/ page2 页面的工作空间(内部结构参考 page1)&lt;/span&gt;
            |&lt;span class=&quot;hljs-comment&quot;&gt;-- ...&lt;/span&gt;
        
    |&lt;span class=&quot;hljs-comment&quot;&gt;-- ...&lt;/span&gt;
    
|&lt;span class=&quot;hljs-comment&quot;&gt;-- html/ 公共 html 片段&lt;/span&gt;
|&lt;span class=&quot;hljs-comment&quot;&gt;-- less/ 公共 less 目录&lt;/span&gt;
|&lt;span class=&quot;hljs-comment&quot;&gt;-- components/ 公共组件目录&lt;/span&gt;
|&lt;span class=&quot;hljs-comment&quot;&gt;-- images/ 公共图片目录&lt;/span&gt;
|&lt;span class=&quot;hljs-comment&quot;&gt;-- mock/ 公共 api-mock 文件目录&lt;/span&gt;
|&lt;span class=&quot;hljs-comment&quot;&gt;-- ...&lt;/span&gt;</pre><p><strong><em>单页面应用</em></strong></p><pre class="crayon-plain-tag">|&lt;span class=&quot;hljs-comment&quot;&gt;-- src/ 源代码目录&lt;/span&gt;
    |&lt;span class=&quot;hljs-comment&quot;&gt;-- page1/ page1 页面的工作空间&lt;/span&gt;
        |&lt;span class=&quot;hljs-comment&quot;&gt;-- index.js 入口文件&lt;/span&gt;
        |&lt;span class=&quot;hljs-comment&quot;&gt;-- services/ service 目录&lt;/span&gt;
        |&lt;span class=&quot;hljs-comment&quot;&gt;-- models/ model 目录&lt;/span&gt;
        |&lt;span class=&quot;hljs-comment&quot;&gt;-- mock/ 本地 json 数据模拟&lt;/span&gt;
        |&lt;span class=&quot;hljs-comment&quot;&gt;-- images/ 图片文件目录&lt;/span&gt;
        |&lt;span class=&quot;hljs-comment&quot;&gt;-- components/ 组件目录(如果基于 react, vue 等组件化框架)&lt;/span&gt;
        |&lt;span class=&quot;hljs-comment&quot;&gt;-- ...&lt;/span&gt;
        
    |&lt;span class=&quot;hljs-comment&quot;&gt;-- module1/ 子目录&lt;/span&gt;
        |&lt;span class=&quot;hljs-comment&quot;&gt;-- page2/ page2 页面的工作空间(内部结构参考 page1)&lt;/span&gt;
        
    |&lt;span class=&quot;hljs-comment&quot;&gt;-- ...&lt;/span&gt;
    
|&lt;span class=&quot;hljs-comment&quot;&gt;-- images/ 公共图片目录&lt;/span&gt;
|&lt;span class=&quot;hljs-comment&quot;&gt;-- mock/ 公共 api-mock 文件目录&lt;/span&gt;
|&lt;span class=&quot;hljs-comment&quot;&gt;-- components/ 公共组件目录   &lt;/span&gt;
|&lt;span class=&quot;hljs-comment&quot;&gt;-- ... &lt;/span&gt;</pre><p>参考:</p>
<ul>
<li><a href="https://segmentfault.com/a/1190000015816534" target="_blank" rel="noopener">目录结构优化</a></li>
</ul>
<h2 id='7-7-搭建一个好的脚手架'  id="boomdevs_12" id="articleHeader12" >7. 7. 搭建一个好的脚手架</h2>
<p>搭建一个好的脚手架,能够更好的编写代码、构建项目等。</p>
<p>可以查看 <a href="https://segmentfault.com/a/1190000016481132">搭建自己的前端脚手架</a> 了解一些基本的脚手架文件与工具。</p>
<p>比如:</p><pre class="crayon-plain-tag">|&lt;span class=&quot;hljs-comment&quot;&gt;-- /                              项目根目录&lt;/span&gt;
    |&lt;span class=&quot;hljs-comment&quot;&gt;-- src/                       源代码目录&lt;/span&gt;
    |&lt;span class=&quot;hljs-comment&quot;&gt;-- package.json               npm 项目文件&lt;/span&gt;
    |&lt;span class=&quot;hljs-comment&quot;&gt;-- README.md                  项目说明文件&lt;/span&gt;
    |&lt;span class=&quot;hljs-comment&quot;&gt;-- CHANGELOG.md               版本更新记录&lt;/span&gt;
    |&lt;span class=&quot;hljs-comment&quot;&gt;-- .gitignore                 git 忽略配置文件&lt;/span&gt;
    |&lt;span class=&quot;hljs-comment&quot;&gt;-- .editorconfig              编辑器配置文件&lt;/span&gt;
    |&lt;span class=&quot;hljs-comment&quot;&gt;-- .npmrc                     npm 配置文件&lt;/span&gt;
    |&lt;span class=&quot;hljs-comment&quot;&gt;-- .npmignore                 npm 忽略配置文件&lt;/span&gt;
    |&lt;span class=&quot;hljs-comment&quot;&gt;-- .eslintrc                  eslint 配置文件&lt;/span&gt;
    |&lt;span class=&quot;hljs-comment&quot;&gt;-- .eslintignore              eslint 忽略配置文件&lt;/span&gt;
    |&lt;span class=&quot;hljs-comment&quot;&gt;-- .stylelintrc               stylelint 配置文件&lt;/span&gt;
    |&lt;span class=&quot;hljs-comment&quot;&gt;-- .stylelintignore           stylelint 忽略配置文件&lt;/span&gt;
    |&lt;span class=&quot;hljs-comment&quot;&gt;-- .prettierrc                prettier 配置文件&lt;/span&gt;
    |&lt;span class=&quot;hljs-comment&quot;&gt;-- .prettierignore            prettier 忽略配置文件&lt;/span&gt;
    
    |&lt;span class=&quot;hljs-comment&quot;&gt;-- .babelrc                   babel 配置文件&lt;/span&gt;
    |&lt;span class=&quot;hljs-comment&quot;&gt;-- webpack.config.js          webpack 配置文件&lt;/span&gt;
    |&lt;span class=&quot;hljs-comment&quot;&gt;-- rollup.config.js           rollup 配置文件&lt;/span&gt;
    |&lt;span class=&quot;hljs-comment&quot;&gt;-- gulpfile.js                gulp 配置文件&lt;/span&gt;
    
    |&lt;span class=&quot;hljs-comment&quot;&gt;-- test/                      测试目录&lt;/span&gt;
    |&lt;span class=&quot;hljs-comment&quot;&gt;-- docs/                      文档目录&lt;/span&gt;
    |&lt;span class=&quot;hljs-comment&quot;&gt;-- jest.config.js             jest 配置文件&lt;/span&gt;
    |&lt;span class=&quot;hljs-comment&quot;&gt;-- .gitattributes             git 属性配置&lt;/span&gt;</pre><p></p>
<ul>
<li><code>.editorconfig</code>: 用这个文件来统一不同编辑器的一些配置,比如 <code>tab</code> 转 2 个空格、自动插入空尾行、去掉行尾的空格等,<a href="http://editorconfig.org/" target="_blank" rel="nofollow noopener noreferrer">http://editorconfig.org</a></li>
<li><a href="https://github.com/eslint/eslint" target="_blank" rel="nofollow noopener noreferrer">eslint</a>、<a href="https://github.com/stylelint/stylelint" target="_blank" rel="nofollow noopener noreferrer">stylelint</a>、<a href="https://github.com/prettier/prettier" target="_blank" rel="nofollow noopener noreferrer">prettier</a>: 规范化代码风格、优化代码格式等</li>
<li><a href="https://github.com/typicode/husky" target="_blank" rel="nofollow noopener noreferrer">husky</a>、<a href="https://github.com/okonet/lint-staged" target="_blank" rel="nofollow noopener noreferrer">lint-staged</a>: 在 <code>git</code> 提交之前对代码进行审查,否则不予提交</li>
<li><code>.gitlab-ci.yml</code>: <a href="https://docs.gitlab.com/ee/ci/" target="_blank" rel="nofollow noopener noreferrer">gitlab ci</a> 持续集成服务</li>
</ul>
<p>参考:</p>
<ul>
<li><a href="https://segmentfault.com/a/1190000016481132">搭建自己的前端脚手架</a></li>
<li><a href="https://segmentfault.com/a/1190000015858432" target="_blank" rel="noopener">怎样提升代码质量</a></li>
<li><a href="https://segmentfault.com/a/1190000015403363">CSS 模块化</a></li>
<li><a href="https://segmentfault.com/a/1190000016422897" target="_blank" rel="noopener">css 的弱化与 js 的强化</a></li>
<li><a href="https://segmentfault.com/a/1190000015297352">本地化接口模拟、前后端并行开发</a></li>
</ul>
<p>=================================================</p>
<p>到这里为止,一个基本的项目骨架就算搭建好了。</p>
<h2 id='8-8-使用版本控制系统管理源代码-git'  id="boomdevs_13" id="articleHeader13" >8. 8. 使用版本控制系统管理源代码(git)</h2>
<p>项目搭建好后,需要一个版本控制系统来管理源代码。</p>
<p>比较常用的版本管理工具有 <a href="https://git-scm.com/" target="_blank" rel="nofollow noopener noreferrer">git</a>、<a href="http://subversion.apache.org/" target="_blank" rel="nofollow noopener noreferrer">svn</a>,现在一般都用 <code>git</code>。</p>
<p>一般开源的项目可以托管到 <a href="http://github.com/" target="_blank" rel="nofollow noopener noreferrer">http://github.com</a>,私人的项目可以托管到 <a href="https://gitee.com/" target="_blank" rel="nofollow noopener noreferrer">https://gitee.com</a>、<a href="https://coding.net/" target="_blank" rel="nofollow noopener noreferrer">https://coding.net/</a>,而企业的项目则需要自建版本控制系统了。</p>
<p>自建版本控制系统主要有 <a href="https://gitlab.com/" target="_blank" rel="nofollow noopener noreferrer">gitlab</a>、<a href="https://github.com/gogs/gogs" target="_blank" rel="nofollow noopener noreferrer">gogs</a>、<a href="https://github.com/go-gitea/gitea" target="_blank" rel="nofollow noopener noreferrer">gitea</a>:<code>gitlab</code> 是由商业驱动的,比较稳定,社区版是免费的,一般建议选用这个;<code>gogs, gitea</code> 是开源的项目,还不太稳定,期待进一步的更新。</p>
<p>所以,<code>git</code> + <code>gitlab</code> 是不错的配合。</p>
<h2 id='9-9-编写代码'  id="boomdevs_14" id="articleHeader14" >9. 9. 编写代码</h2>
<p>编写代码时,<code>js</code> 选用 <code>es6</code> 的模块化规范来写(如果喜欢用 <a href="https://github.com/Microsoft/TypeScript" target="_blank" rel="nofollow noopener noreferrer">TypeScript</a>,需要加上 <a href="https://github.com/TypeStrong/ts-loader" target="_blank" rel="nofollow noopener noreferrer">ts-loader</a>),样式可以用 <a href="http://lesscss.org/" target="_blank" rel="nofollow noopener noreferrer">less</a>、<a href="http://sass-lang.com/" target="_blank" rel="nofollow noopener noreferrer">scss</a>、<code>css</code> 来写。</p>
<p>写 <code>js</code> 模块文件时,注释可以使用 <a href="http://usejsdoc.org/" target="_blank" rel="nofollow noopener noreferrer">jsdoc</a> 的规范来写,如果配置相应的工具,可以将这些注释导出接口文档。</p>
<p>因为脚手架里有 <a href="https://github.com/typicode/husky" target="_blank" rel="nofollow noopener noreferrer">husky</a>、<a href="https://github.com/okonet/lint-staged" target="_blank" rel="nofollow noopener noreferrer">lint-staged</a> 的配合,所以每次提交的代码都会进行代码审查与格式优化,如果不符合规范,则需要把不规范的代码进行修改,然后才能提交到代码仓库中。</p>
<p>比如 <code>console.log(haha.hehe);</code> 这段代码就会遇到错误,不予提交:</p>
<p><span class="img-wrap"><img loading="lazy" decoding="async" class="alignnone size-full wp-image-2932" title="图片描述" src="http://www.mobabel.net/wp-content/uploads/2018/12/07/e74c27a8bca14013f1cce2ba68e4f112_230709.png" alt="图片描述" width="800" height="490" srcset="http://www.mobabel.net/wp-content/uploads/2018/12/07/e74c27a8bca14013f1cce2ba68e4f112_230709.png 800w, http://www.mobabel.net/wp-content/uploads/2018/12/07/e74c27a8bca14013f1cce2ba68e4f112_230709-300x184.png 300w, http://www.mobabel.net/wp-content/uploads/2018/12/07/e74c27a8bca14013f1cce2ba68e4f112_230709-768x470.png 768w" sizes="(max-width: 800px) 100vw, 800px" /></span></p>
<p>这个功能定义在 <code>package.json</code> 中:</p><pre class="crayon-plain-tag">{
  &lt;span class=&quot;hljs-string&quot;&gt;&quot;devDependencies&quot;&lt;/span&gt;: {             工具依赖
    &lt;span class=&quot;hljs-string&quot;&gt;&quot;babel-eslint&quot;&lt;/span&gt;: &lt;span class=&quot;hljs-string&quot;&gt;&quot;^8.2.6&quot;&lt;/span&gt;,
    &lt;span class=&quot;hljs-string&quot;&gt;&quot;eslint&quot;&lt;/span&gt;: &lt;span class=&quot;hljs-string&quot;&gt;&quot;^4.19.1&quot;&lt;/span&gt;,
    &lt;span class=&quot;hljs-string&quot;&gt;&quot;husky&quot;&lt;/span&gt;: &lt;span class=&quot;hljs-string&quot;&gt;&quot;^0.14.3&quot;&lt;/span&gt;,
    &lt;span class=&quot;hljs-string&quot;&gt;&quot;lint-staged&quot;&lt;/span&gt;: &lt;span class=&quot;hljs-string&quot;&gt;&quot;^7.2.0&quot;&lt;/span&gt;,
    &lt;span class=&quot;hljs-string&quot;&gt;&quot;prettier&quot;&lt;/span&gt;: &lt;span class=&quot;hljs-string&quot;&gt;&quot;^1.14.0&quot;&lt;/span&gt;,
    &lt;span class=&quot;hljs-string&quot;&gt;&quot;stylelint&quot;&lt;/span&gt;: &lt;span class=&quot;hljs-string&quot;&gt;&quot;^9.3.0&quot;&lt;/span&gt;,
    &lt;span class=&quot;hljs-string&quot;&gt;&quot;eslint-config-airbnb&quot;&lt;/span&gt;: &lt;span class=&quot;hljs-string&quot;&gt;&quot;^17.0.0&quot;&lt;/span&gt;,
    &lt;span class=&quot;hljs-string&quot;&gt;&quot;eslint-config-prettier&quot;&lt;/span&gt;: &lt;span class=&quot;hljs-string&quot;&gt;&quot;^2.9.0&quot;&lt;/span&gt;,
    &lt;span class=&quot;hljs-string&quot;&gt;&quot;eslint-plugin-babel&quot;&lt;/span&gt;: &lt;span class=&quot;hljs-string&quot;&gt;&quot;^5.1.0&quot;&lt;/span&gt;,
    &lt;span class=&quot;hljs-string&quot;&gt;&quot;eslint-plugin-import&quot;&lt;/span&gt;: &lt;span class=&quot;hljs-string&quot;&gt;&quot;^2.13.0&quot;&lt;/span&gt;,
    &lt;span class=&quot;hljs-string&quot;&gt;&quot;eslint-plugin-jsx-a11y&quot;&lt;/span&gt;: &lt;span class=&quot;hljs-string&quot;&gt;&quot;^6.1.0&quot;&lt;/span&gt;,
    &lt;span class=&quot;hljs-string&quot;&gt;&quot;eslint-plugin-prettier&quot;&lt;/span&gt;: &lt;span class=&quot;hljs-string&quot;&gt;&quot;^2.6.2&quot;&lt;/span&gt;,
    &lt;span class=&quot;hljs-string&quot;&gt;&quot;eslint-plugin-react&quot;&lt;/span&gt;: &lt;span class=&quot;hljs-string&quot;&gt;&quot;^7.10.0&quot;&lt;/span&gt;,
    &lt;span class=&quot;hljs-string&quot;&gt;&quot;stylelint-config-prettier&quot;&lt;/span&gt;: &lt;span class=&quot;hljs-string&quot;&gt;&quot;^3.3.0&quot;&lt;/span&gt;,
    &lt;span class=&quot;hljs-string&quot;&gt;&quot;stylelint-config-standard&quot;&lt;/span&gt;: &lt;span class=&quot;hljs-string&quot;&gt;&quot;^18.2.0&quot;&lt;/span&gt;
  },
  &lt;span class=&quot;hljs-string&quot;&gt;&quot;scripts&quot;&lt;/span&gt;: {                     可以添加更多命令
    &lt;span class=&quot;hljs-string&quot;&gt;&quot;precommit&quot;&lt;/span&gt;: &lt;span class=&quot;hljs-string&quot;&gt;&quot;npm run lint-staged&quot;&lt;/span&gt;,
    &lt;span class=&quot;hljs-string&quot;&gt;&quot;prettier&quot;&lt;/span&gt;: &lt;span class=&quot;hljs-string&quot;&gt;&quot;prettier --write \&quot;./**/*.{js,jsx,css,less,sass,scss,md,json}\&quot;&quot;&lt;/span&gt;,
    &lt;span class=&quot;hljs-string&quot;&gt;&quot;eslint&quot;&lt;/span&gt;: &lt;span class=&quot;hljs-string&quot;&gt;&quot;eslint .&quot;&lt;/span&gt;,
    &lt;span class=&quot;hljs-string&quot;&gt;&quot;eslint:fix&quot;&lt;/span&gt;: &lt;span class=&quot;hljs-string&quot;&gt;&quot;eslint . --fix&quot;&lt;/span&gt;,
    &lt;span class=&quot;hljs-string&quot;&gt;&quot;stylelint&quot;&lt;/span&gt;: &lt;span class=&quot;hljs-string&quot;&gt;&quot;stylelint \&quot;./**/*.{css,less,sass,scss}\&quot;&quot;&lt;/span&gt;,
    &lt;span class=&quot;hljs-string&quot;&gt;&quot;stylelint:fix&quot;&lt;/span&gt;: &lt;span class=&quot;hljs-string&quot;&gt;&quot;stylelint \&quot;./**/*.{css,less,sass,scss}\&quot; --fix&quot;&lt;/span&gt;,
    &lt;span class=&quot;hljs-string&quot;&gt;&quot;lint-staged&quot;&lt;/span&gt;: &lt;span class=&quot;hljs-string&quot;&gt;&quot;lint-staged&quot;&lt;/span&gt;
  },
  &lt;span class=&quot;hljs-string&quot;&gt;&quot;lint-staged&quot;&lt;/span&gt;: {                 对提交的代码进行检查与矫正
    &lt;span class=&quot;hljs-string&quot;&gt;&quot;**/*.{js,jsx}&quot;&lt;/span&gt;: [
      &lt;span class=&quot;hljs-string&quot;&gt;&quot;eslint --fix&quot;&lt;/span&gt;,
      &lt;span class=&quot;hljs-string&quot;&gt;&quot;prettier --write&quot;&lt;/span&gt;,
      &lt;span class=&quot;hljs-string&quot;&gt;&quot;git add&quot;&lt;/span&gt;
    ],
    &lt;span class=&quot;hljs-string&quot;&gt;&quot;**/*.{css,less,sass,scss}&quot;&lt;/span&gt;: [
      &lt;span class=&quot;hljs-string&quot;&gt;&quot;stylelint --fix&quot;&lt;/span&gt;,
      &lt;span class=&quot;hljs-string&quot;&gt;&quot;prettier --write&quot;&lt;/span&gt;,
      &lt;span class=&quot;hljs-string&quot;&gt;&quot;git add&quot;&lt;/span&gt;
    ],
    &lt;span class=&quot;hljs-string&quot;&gt;&quot;**/*.{md,json}&quot;&lt;/span&gt;: [
      &lt;span class=&quot;hljs-string&quot;&gt;&quot;prettier --write&quot;&lt;/span&gt;,
      &lt;span class=&quot;hljs-string&quot;&gt;&quot;git add&quot;&lt;/span&gt;
    ]
  }
}</pre><p></p>
<ul>
<li>如果你想禁用这个功能,可以把 <code>scripts</code> 中 <code>"precommit"</code> 改成 <code>"//precommit"</code></li>
<li>如果你想自定 <code>eslint</code> 检查代码的规范,可以修改 <code>.eslintrc, .eslintrc.js</code> 等配置文件</li>
<li>如果你想自定 <code>stylelint</code> 检查代码的规范,可以修改 <code>.stylelintrc, .stylelintrc.js</code> 等配置文件</li>
<li>如果你想忽略某些文件不进行代码检查,可以修改 <code>.eslintignore, .stylelintignore</code> 配置文件</li>
</ul>
<p>参考:</p>
<ul>
<li><a href="https://segmentfault.com/a/1190000016481132">搭建自己的前端脚手架</a></li>
</ul>
<h2 id='10-10-组件化'  id="boomdevs_15" id="articleHeader15" >10. 10. 组件化</h2>
<p>当项目拥有了一定量的代码之后,就会发现,有些代码是很多页面共用的,于是把这些代码提取出来,封装成一个组件,供各个地方使用。</p>
<p>当拥有多个项目的时候,有些组件需要跨项目使用,一种方式是复制代码到其他项目中,但这种方式会导致组件代码很难维护,所以,一般是用另一种方式:组件化。</p>
<p>组件化就是将组件独立成一个项目,然后在其他项目中安装这个组件,才能使用。</p>
<p>一般组件化会配合私有 npm 仓库一起用。</p><pre class="crayon-plain-tag">|&lt;span class=&quot;hljs-comment&quot;&gt;-- project1/ 项目1&lt;/span&gt;
    |&lt;span class=&quot;hljs-comment&quot;&gt;-- package.json&lt;/span&gt;
    
|&lt;span class=&quot;hljs-comment&quot;&gt;-- project2/ 项目2&lt;/span&gt;
    |&lt;span class=&quot;hljs-comment&quot;&gt;-- package.json    &lt;/span&gt;

|&lt;span class=&quot;hljs-comment&quot;&gt;-- component1/ 组件1&lt;/span&gt;
    |&lt;span class=&quot;hljs-comment&quot;&gt;-- package.json&lt;/span&gt;

|&lt;span class=&quot;hljs-comment&quot;&gt;-- component2/ 组件2&lt;/span&gt;
    |&lt;span class=&quot;hljs-comment&quot;&gt;-- package.json&lt;/span&gt;</pre><p>在 <code>project1</code> 中安装 <code>component1, component2</code> 组件:</p><pre class="crayon-plain-tag">&lt;span class=&quot;hljs-meta&quot;&gt;# package.json&lt;/span&gt;
{
  &lt;span class=&quot;hljs-string&quot;&gt;&quot;dependencies&quot;&lt;/span&gt;: {
    &lt;span class=&quot;hljs-string&quot;&gt;&quot;component1&quot;&lt;/span&gt;: &lt;span class=&quot;hljs-string&quot;&gt;&quot;^0.0.1&quot;&lt;/span&gt;,
    &lt;span class=&quot;hljs-string&quot;&gt;&quot;component2&quot;&lt;/span&gt;: &lt;span class=&quot;hljs-string&quot;&gt;&quot;^0.0.1&quot;&lt;/span&gt;
  }
}</pre><p></p><pre class="crayon-plain-tag">&lt;span class=&quot;hljs-keyword&quot;&gt;import&lt;/span&gt; compoennt1 &lt;span class=&quot;hljs-keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;'compoennt1'&lt;/span&gt;;
&lt;span class=&quot;hljs-keyword&quot;&gt;import&lt;/span&gt; compoennt2 &lt;span class=&quot;hljs-keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;'compoennt2'&lt;/span&gt;;</pre><p>如果想要了解怎样写好一个组件(<code>npm package</code>),可以参考 <a href="https://segmentfault.com/a/1190000017064659" target="_blank" rel="noopener">从 1 到完美,写一个 js 库、node 库、前端组件库</a>。</p>
<p>参考:</p>
<ul>
<li><a href="https://segmentfault.com/a/1190000015297823">组件化</a></li>
<li><a href="https://segmentfault.com/a/1190000015297864" target="_blank" rel="noopener">私有 npm 仓库</a></li>
<li><a href="https://segmentfault.com/a/1190000017064659">从 1 到完美,写一个 js 库、node 库、前端组件库</a></li>
</ul>
<h2 id='11-11-测试'  id="boomdevs_16" id="articleHeader16" >11. 11. 测试</h2>
<p>测试的目的在于能以最少的人力和时间发现潜在的各种错误和缺陷,这在项目更新、重构等的过程中尤其重要,因为每当更改一些代码后,你并不知道这些代码有没有问题、会不会影响其他的模块。如果有了测试,运行一遍测试用例,就知道更改的代码有没有问题、会不会产生影响。</p>
<p>一般前端测试分以下几种:</p>
<ol>
<li>单元测试:模块单元、函数单元、组件单元等的单元块的测试</li>
<li>集成测试:接口依赖(ajax)、I/O 依赖、环境依赖(localStorage、IndexedDB)等的上下文的集成测试</li>
<li>样式测试:对样式的测试</li>
<li>E2E 测试:端到端测试,也就是在实际生产环境测试整个应用</li>
</ol>
<p>一般会用到下面的一些工具:</p>
<ul>
<li><a href="https://github.com/facebook/jest" target="_blank" rel="nofollow noopener noreferrer">jest</a></li>
<li><a href="https://github.com/airbnb/enzyme" target="_blank" rel="nofollow noopener noreferrer">enzyme</a></li>
<li><a href="https://github.com/cypress-io/cypress" target="_blank" rel="nofollow noopener noreferrer">cypress</a></li>
<li><a href="https://github.com/SeleniumHQ/selenium" target="_blank" rel="nofollow noopener noreferrer">selenium</a></li>
<li><a href="https://github.com/GoogleChrome/puppeteer" target="_blank" rel="nofollow noopener noreferrer">puppeteer</a></li>
</ul>
<p>另外,可以参考 <a href="https://www.jianshu.com/p/1b99af371e66" target="_blank" rel="nofollow noopener noreferrer">聊聊前端开发的测试</a>。</p>
<h2 id='12-12-构建'  id="boomdevs_17" id="articleHeader17" >12. 12. 构建</h2>
<p>一般单页面应用的构建会有 <code>npm run build</code> 的命令来构建项目,然后会输出一个 <code>html</code> 文件,一些 <code>js/css/images ...</code> 文件,然后把这些文件部署到服务器就可以了。</p>
<p>多页面应用的构建要复杂一些,因为是多入口的,所以一般会封装构建工具,然后通过参数传入多个入口:</p><pre class="crayon-plain-tag">npm run build&nbsp;&lt;span class=&quot;hljs-comment&quot;&gt;-- page1 page2 dir1/* dir2/all --env test/prod&lt;/span&gt;</pre><p></p>
<ul>
<li><code>page1, page2</code> 确定构建哪些页面;<code>dir1/*, dir2/all</code> 某个目录下所有的页面;<code>all, *</code> 整个项目所有的页面</li>
<li>有时候可能还会针对不同的服务器环境(比如测试机、正式机)做出不同的构建,可以在后面加参数</li>
<li><code>--</code> 用来分割 <code>npm</code> 本身的参数与脚本参数,参考 <a href="https://docs.npmjs.com/cli/run-script.html" target="_blank" rel="nofollow noopener noreferrer">npm &#8211; run-script</a> 了解详情</li>
</ul>
<p>多页面应用会导出多个 <code>html</code> 文件,需要注意这些导出的 <code>html</code> 不要相冲突了。</p>
<p>当然,也可以用一些已经封装好的工具,如 <a href="https://github.com/senntyou/lila" target="_blank" rel="nofollow noopener noreferrer">lila</a>。</p>
<h2 id='13-13-部署'  id="boomdevs_18" id="articleHeader18" >13. 13. 部署</h2>
<p>在构建好项目之后,就可以部署到服务器了。</p>
<p>传统的方式,可以用 <code>ftp, sftp</code> 等工具,手动传到服务器,但这种方式比较笨拙,不够自动化。</p>
<p>自动化的,可以用一些工具部署到服务器,如 <a href="https://github.com/gulpjs/gulp" target="_blank" rel="nofollow noopener noreferrer">gulp</a>、<a href="https://github.com/teambition/gulp-ssh" target="_blank" rel="nofollow noopener noreferrer">gulp-ssh</a>,当然也可以用一些封装的工具,如 <a href="https://github.com/senntyou/md-sync" target="_blank" rel="nofollow noopener noreferrer">md-sync</a>、<a href="https://github.com/senntyou/lila" target="_blank" rel="nofollow noopener noreferrer">lila</a> 等</p>
<p>以 <code>md-sync</code> 为例:</p><pre class="crayon-plain-tag">npm&nbsp;&lt;span class=&quot;hljs-keyword&quot;&gt;install&lt;/span&gt;&nbsp;md-&lt;span class=&quot;hljs-keyword&quot;&gt;sync&lt;/span&gt;&nbsp;&lt;span class=&quot;hljs-comment&quot;&gt;--save-dev&lt;/span&gt;</pre><p><code>md-sync.config.js</code> 配置文件:</p><pre class="crayon-plain-tag">&lt;span class=&quot;hljs-meta&quot;&gt;module.exports&lt;/span&gt; = &lt;span class=&quot;hljs-string&quot;&gt;[&lt;/span&gt;
  &lt;span class=&quot;hljs-attr&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;hljs-attr&quot;&gt;src&lt;/span&gt;: &lt;span class=&quot;hljs-string&quot;&gt;'./build/**/*',&lt;/span&gt;
    &lt;span class=&quot;hljs-attr&quot;&gt;remotePath&lt;/span&gt;: &lt;span class=&quot;hljs-string&quot;&gt;'remotePath',&lt;/span&gt;
    &lt;span class=&quot;hljs-attr&quot;&gt;server&lt;/span&gt;: &lt;span class=&quot;hljs-string&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;hljs-attr&quot;&gt;ignoreErrors&lt;/span&gt;: &lt;span class=&quot;hljs-string&quot;&gt;true,&lt;/span&gt;
      &lt;span class=&quot;hljs-attr&quot;&gt;sshConfig&lt;/span&gt;: &lt;span class=&quot;hljs-string&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;hljs-attr&quot;&gt;host&lt;/span&gt;: &lt;span class=&quot;hljs-string&quot;&gt;'host',&lt;/span&gt;
        &lt;span class=&quot;hljs-attr&quot;&gt;username&lt;/span&gt;: &lt;span class=&quot;hljs-string&quot;&gt;'username',&lt;/span&gt;
        &lt;span class=&quot;hljs-attr&quot;&gt;password&lt;/span&gt;: &lt;span class=&quot;hljs-string&quot;&gt;'password'&lt;/span&gt;
      &lt;span class=&quot;hljs-attr&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;hljs-attr&quot;&gt;},&lt;/span&gt;
  &lt;span class=&quot;hljs-attr&quot;&gt;},&lt;/span&gt;
  &lt;span class=&quot;hljs-attr&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;hljs-attr&quot;&gt;src&lt;/span&gt;: &lt;span class=&quot;hljs-string&quot;&gt;'./build/**/*.html',&lt;/span&gt;
    &lt;span class=&quot;hljs-attr&quot;&gt;remotePath&lt;/span&gt;: &lt;span class=&quot;hljs-string&quot;&gt;'remotePath2',&lt;/span&gt;
    &lt;span class=&quot;hljs-attr&quot;&gt;server&lt;/span&gt;: &lt;span class=&quot;hljs-string&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;hljs-attr&quot;&gt;ignoreErrors&lt;/span&gt;: &lt;span class=&quot;hljs-string&quot;&gt;true,&lt;/span&gt;
      &lt;span class=&quot;hljs-attr&quot;&gt;sshConfig&lt;/span&gt;: &lt;span class=&quot;hljs-string&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;hljs-attr&quot;&gt;host&lt;/span&gt;: &lt;span class=&quot;hljs-string&quot;&gt;'host',&lt;/span&gt;
        &lt;span class=&quot;hljs-attr&quot;&gt;username&lt;/span&gt;: &lt;span class=&quot;hljs-string&quot;&gt;'username',&lt;/span&gt;
        &lt;span class=&quot;hljs-attr&quot;&gt;password&lt;/span&gt;: &lt;span class=&quot;hljs-string&quot;&gt;'password'&lt;/span&gt;
      &lt;span class=&quot;hljs-attr&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;hljs-attr&quot;&gt;},&lt;/span&gt;
  &lt;span class=&quot;hljs-attr&quot;&gt;},&lt;/span&gt;
&lt;span class=&quot;hljs-attr&quot;&gt;];&lt;/span&gt;</pre><p>在 <code>package.json</code> 的 <code>scripts</code> 配置好命令:</p><pre class="crayon-plain-tag">&lt;span class=&quot;hljs-string&quot;&gt;&quot;scripts&quot;&lt;/span&gt;: {
  &lt;span class=&quot;hljs-string&quot;&gt;&quot;deploy&quot;&lt;/span&gt;: &lt;span class=&quot;hljs-string&quot;&gt;&quot;md-sync&quot;&lt;/span&gt;
}</pre><p></p><pre class="crayon-plain-tag">&lt;span class=&quot;hljs-attribute&quot;&gt;npm&lt;/span&gt;&nbsp;run deploy</pre><p>另外,一般大型项目会使用持续集成 + <code>shell</code> 命令(如 <code>rsync</code>)部署。</p>
<h2 id='14-14-持续集成测试-构建-部署'  id="boomdevs_19" id="articleHeader19" >14. 14. 持续集成测试、构建、部署</h2>
<p>一般大型工程的的构建与测试都会花很长的时间,在本地做这些事情的话就不太实际,这就需要做持续集成测试、构建、部署了。</p>
<p>持续集成工具用的比较多的:</p>
<ul>
<li><a href="https://jenkins.io/" target="_blank" rel="nofollow noopener noreferrer">jenkins</a></li>
<li><a href="https://docs.gitlab.com/ee/ci/" target="_blank" rel="nofollow noopener noreferrer">gitlab ci</a></li>
</ul>
<p><code>jenkins</code> 是通用型的工具,可以与 <a href="https://github.com/" target="_blank" rel="nofollow noopener noreferrer">github</a>、<a href="https://bitbucket.org/" target="_blank" rel="nofollow noopener noreferrer">bitbucket</a>、<a href="https://about.gitlab.com/" target="_blank" rel="nofollow noopener noreferrer">gitlab</a> 等代码托管服务配合使用,优点是功能强大、插件多、社区活跃,但缺点是配置复杂、使用难度较高。</p>
<p><code>gitlab ci</code> 是 <a href="https://about.gitlab.com/" target="_blank" rel="nofollow noopener noreferrer">gitlab</a> 内部自带的持续集成功能,优点是使用简单、配置简单,但缺点是不及 <code>jenkins</code> 功能强大、绑定 <code>gitlab</code> 才能使用。</p>
<p>以 <code>gitlab</code> 为例(任务定义在 <code>.gitlab-ci.yml</code> 中):</p><pre class="crayon-plain-tag">stages:
  - &lt;span class=&quot;hljs-keyword&quot;&gt;install&lt;/span&gt;
  - &lt;span class=&quot;hljs-keyword&quot;&gt;test&lt;/span&gt;
  - &lt;span class=&quot;hljs-keyword&quot;&gt;build&lt;/span&gt;
  - deploy

&lt;span class=&quot;hljs-comment&quot;&gt;# 安装依赖&lt;/span&gt;
&lt;span class=&quot;hljs-keyword&quot;&gt;install&lt;/span&gt;:
  stage: &lt;span class=&quot;hljs-keyword&quot;&gt;install&lt;/span&gt;
  &lt;span class=&quot;hljs-keyword&quot;&gt;only&lt;/span&gt;:
    - dev
    - &lt;span class=&quot;hljs-keyword&quot;&gt;master&lt;/span&gt;
  script:
    - npm &lt;span class=&quot;hljs-keyword&quot;&gt;install&lt;/span&gt;

&lt;span class=&quot;hljs-comment&quot;&gt;# 运行测试用例&lt;/span&gt;
&lt;span class=&quot;hljs-keyword&quot;&gt;test&lt;/span&gt;:
  stage: &lt;span class=&quot;hljs-keyword&quot;&gt;test&lt;/span&gt;
  &lt;span class=&quot;hljs-keyword&quot;&gt;only&lt;/span&gt;:
    - dev
    - &lt;span class=&quot;hljs-keyword&quot;&gt;master&lt;/span&gt;
  script:
    - npm run &lt;span class=&quot;hljs-keyword&quot;&gt;test&lt;/span&gt;

&lt;span class=&quot;hljs-comment&quot;&gt;# 编译&lt;/span&gt;
&lt;span class=&quot;hljs-keyword&quot;&gt;build&lt;/span&gt;:
  stage: &lt;span class=&quot;hljs-keyword&quot;&gt;build&lt;/span&gt;
  &lt;span class=&quot;hljs-keyword&quot;&gt;only&lt;/span&gt;:
    - dev
    - &lt;span class=&quot;hljs-keyword&quot;&gt;master&lt;/span&gt;
  script:
    - npm run clean
    - npm run &lt;span class=&quot;hljs-keyword&quot;&gt;build&lt;/span&gt;

&lt;span class=&quot;hljs-comment&quot;&gt;# 部署服务器&lt;/span&gt;
deploy:
  stage: deploy
  &lt;span class=&quot;hljs-keyword&quot;&gt;only&lt;/span&gt;:
    - dev
    - &lt;span class=&quot;hljs-keyword&quot;&gt;master&lt;/span&gt;
  script:
    - npm run deploy</pre><p>以上配置表示只要在 <code>dev</code> 或 <code>master</code> 分支有代码推送,就会进行持续集成,依次运行:</p>
<ul>
<li><code>npm install</code></li>
<li><code>npm run test</code></li>
<li><code>npm run clean</code></li>
<li><code>npm run build</code></li>
<li><code>npm run deploy</code></li>
</ul>
<p>最终完成部署。如果中间某个命令失败了,将停止接下的命令的运行,并将错误报告给你。</p>
<p>这些操作都在远程机器上完成。</p>
<p>=================================================</p>
<p>到这里为止,基本上完成了一个项目的搭建、编写、构建。</p>
<h2 id='15-15-清理服务器上过期文件'  id="boomdevs_20" id="articleHeader20" >15. 15. 清理服务器上过期文件</h2>
<p>现在前端的项目基本上都会用 <code>webpack</code> 打包代码,并且文件名(<code>html</code> 文件除外)都是 <code>hash</code> 化的,如果需要清除过期的文件而又不想把服务器上文件全部删掉然后重新构建、部署,可以使用 <a href="https://github.com/senntyou/sclean" target="_blank" rel="nofollow noopener noreferrer">sclean</a> 来清除过期文件。</p>
<h2 id='16-16-收集前端错误反馈'  id="boomdevs_21" id="articleHeader21" >16. 16. 收集前端错误反馈</h2>
<p>当用户在用线上的程序时,怎么知道有没有出 bug;如果出 bug 了,报的是什么错;如果是 js 报错,怎么知道是那一行运行出了错?</p>
<p>所以,在程序运行时捕捉 <code>js</code> 脚本错误,并上报到服务器,是非常有必要的。</p>
<p>这里就要用到 <code>window.onerror</code> 了:</p><pre class="crayon-plain-tag">&lt;span class=&quot;hljs-built_in&quot;&gt;window&lt;/span&gt;.onerror = &lt;span class=&quot;hljs-function&quot;&gt;(&lt;span class=&quot;hljs-params&quot;&gt;errorMessage, scriptURI, lineNumber, columnNumber, errorObj&lt;/span&gt;) =&amp;gt;&lt;/span&gt; {
  &lt;span class=&quot;hljs-keyword&quot;&gt;const&lt;/span&gt; data = {
    title: &lt;span class=&quot;hljs-built_in&quot;&gt;document&lt;/span&gt;.getElementsByTagName(&lt;span class=&quot;hljs-string&quot;&gt;'title'&lt;/span&gt;)[&lt;span class=&quot;hljs-number&quot;&gt;0&lt;/span&gt;].innerText,
    errorMessage,
    scriptURI,
    lineNumber,
    columnNumber,
    detailMessage: (errorObj &amp;amp;&amp;amp; errorObj.message) || &lt;span class=&quot;hljs-string&quot;&gt;''&lt;/span&gt;,
    stack: (errorObj &amp;amp;&amp;amp; errorObj.stack) || &lt;span class=&quot;hljs-string&quot;&gt;''&lt;/span&gt;,
    userAgent: &lt;span class=&quot;hljs-built_in&quot;&gt;window&lt;/span&gt;.navigator.userAgent,
    locationHref: &lt;span class=&quot;hljs-built_in&quot;&gt;window&lt;/span&gt;.location.href,
    cookie: &lt;span class=&quot;hljs-built_in&quot;&gt;window&lt;/span&gt;.document.cookie,
  };

  post(&lt;span class=&quot;hljs-string&quot;&gt;'url'&lt;/span&gt;, data); &lt;span class=&quot;hljs-comment&quot;&gt;// 上报到服务器&lt;/span&gt;
};</pre><p>线上的 <code>js</code> 脚本都是压缩过的,需要用 <code>sourcemap</code> 文件与 <a href="https://github.com/mozilla/source-map" target="_blank" rel="nofollow noopener noreferrer">source-map</a> 查看原始的报错堆栈信息,可以参考 <a href="https://segmentfault.com/a/1190000016987829">细说 js 压缩、sourcemap、通过 sourcemap 查找原始报错信息</a> 了解详细信息。</p>
<p>参考:</p>
<ul>
<li><a href="https://segmentfault.com/a/1190000016852780" target="_blank" rel="noopener">前端如何高效的与后端协作开发</a></li>
<li><a href="https://segmentfault.com/a/1190000016987829">细说 js 压缩、sourcemap、通过 sourcemap 查找原始报错信息</a></li>
</ul>
<p>&nbsp;</p>
<p>[resource]<a href="https://segmentfault.com/a/1190000017158444">从 0 到 1 再到 100, 搭建、编写、构建一个前端项目</a></p>The post <a href="http://www.mobabel.net/%e8%bd%ac%e4%bb%8e-0-%e5%88%b0-1-%e5%86%8d%e5%88%b0-100-%e6%90%ad%e5%bb%ba%e3%80%81%e7%bc%96%e5%86%99%e3%80%81%e6%9e%84%e5%bb%ba%e4%b8%80%e4%b8%aa%e5%89%8d%e7%ab%af%e9%a1%b9%e7%9b%ae/">[转]从 0 到 1 再到 100, 搭建、编写、构建一个前端项目</a> first appeared on <a href="http://www.mobabel.net">Mobabel</a>.]]></content:encoded>
					
					<wfw:commentRss>http://www.mobabel.net/%e8%bd%ac%e4%bb%8e-0-%e5%88%b0-1-%e5%86%8d%e5%88%b0-100-%e6%90%ad%e5%bb%ba%e3%80%81%e7%bc%96%e5%86%99%e3%80%81%e6%9e%84%e5%bb%ba%e4%b8%80%e4%b8%aa%e5%89%8d%e7%ab%af%e9%a1%b9%e7%9b%ae/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>[转]使用 JavaScript 的代价!(2018 版)</title>
		<link>http://www.mobabel.net/%e8%bd%ac%e4%bd%bf%e7%94%a8-javascript-%e7%9a%84%e4%bb%a3%e4%bb%b7%ef%bc%81%ef%bc%882018-%e7%89%88%ef%bc%89/</link>
					<comments>http://www.mobabel.net/%e8%bd%ac%e4%bd%bf%e7%94%a8-javascript-%e7%9a%84%e4%bb%a3%e4%bb%b7%ef%bc%81%ef%bc%882018-%e7%89%88%ef%bc%89/#respond</comments>
		
		<dc:creator><![CDATA[leelight]]></dc:creator>
		<pubDate>Tue, 04 Dec 2018 23:58:00 +0000</pubDate>
				<category><![CDATA[JavaScript]]></category>
		<category><![CDATA[Javascript]]></category>
		<guid isPermaLink="false">http://www.mobabel.net/?p=2860</guid>

					<description><![CDATA[<p>建立交互式网站包括向用户发送 JavaScript 。通常,太多了。你是否经历过在一个手机页面上,它看起来已经加载好了,但是点击一个链接或者试图滚动页面的时候,什么也没发生? 一字节又一字节,JavaScript 仍然是我们发送给手机的代价最大的资源,因为它会很大程度上延迟交互。 [resource]使用 JavaScript 的代价!(2018 版) www.oschina.net/translate/the-cost-of-javascript-in-2018</p>
The post <a href="http://www.mobabel.net/%e8%bd%ac%e4%bd%bf%e7%94%a8-javascript-%e7%9a%84%e4%bb%a3%e4%bb%b7%ef%bc%81%ef%bc%882018-%e7%89%88%ef%bc%89/">[转]使用 JavaScript 的代价!(2018 版)</a> first appeared on <a href="http://www.mobabel.net">Mobabel</a>.]]></description>
										<content:encoded><![CDATA[<p>建立交互式网站包括向用户发送 JavaScript 。通常,太多了。你是否经历过在一个手机页面上,它看起来已经加载好了,但是点击一个链接或者试图滚动页面的时候,什么也没发生?</p>
<p>一字节又一字节,JavaScript 仍然是我们发送给手机的代价最大的资源,因为它会很大程度上延迟交互。</p>
<p>[resource]<a href="https://mp.weixin.qq.com/s/0RMTdFa82kTjXdJ_x6vYWg">使用 JavaScript 的代价!(2018 版)</a></p>
<p><a href="http://www.oschina.net/translate/the-cost-of-javascript-in-2018">www.oschina.net/translate/the-cost-of-javascript-in-2018</a></p>The post <a href="http://www.mobabel.net/%e8%bd%ac%e4%bd%bf%e7%94%a8-javascript-%e7%9a%84%e4%bb%a3%e4%bb%b7%ef%bc%81%ef%bc%882018-%e7%89%88%ef%bc%89/">[转]使用 JavaScript 的代价!(2018 版)</a> first appeared on <a href="http://www.mobabel.net">Mobabel</a>.]]></content:encoded>
					
					<wfw:commentRss>http://www.mobabel.net/%e8%bd%ac%e4%bd%bf%e7%94%a8-javascript-%e7%9a%84%e4%bb%a3%e4%bb%b7%ef%bc%81%ef%bc%882018-%e7%89%88%ef%bc%89/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>[转]前端构建:3类13种热门工具的选型参考</title>
		<link>http://www.mobabel.net/%e8%bd%ac%e5%89%8d%e7%ab%af%e6%9e%84%e5%bb%ba%ef%bc%9a3%e7%b1%bb13%e7%a7%8d%e7%83%ad%e9%97%a8%e5%b7%a5%e5%85%b7%e7%9a%84%e9%80%89%e5%9e%8b%e5%8f%82%e8%80%83/</link>
					<comments>http://www.mobabel.net/%e8%bd%ac%e5%89%8d%e7%ab%af%e6%9e%84%e5%bb%ba%ef%bc%9a3%e7%b1%bb13%e7%a7%8d%e7%83%ad%e9%97%a8%e5%b7%a5%e5%85%b7%e7%9a%84%e9%80%89%e5%9e%8b%e5%8f%82%e8%80%83/#respond</comments>
		
		<dc:creator><![CDATA[leelight]]></dc:creator>
		<pubDate>Tue, 04 Dec 2018 22:35:58 +0000</pubDate>
				<category><![CDATA[Theory & Solution]]></category>
		<category><![CDATA[Frontend]]></category>
		<guid isPermaLink="false">http://www.mobabel.net/?p=2823</guid>

					<description><![CDATA[<p>1. 前言 在前端项目的规模和复杂性不断提升的情况下,各类构建思想和相应工具层出不穷。本文竭己所能对比了当下13个构建工具,包括Browserify、Webpack、Rollup、Grunt、Gulp和Yeoman6个广为流行的工具,FIS、Athena、WeFlow和Cooking等4个国产工具,以及三大框架:React,Vue和Angular的官方脚手架。希望能在项目初期的构建工具选型上为大家提供些参考。 2. 全览 构建工具可以分为三类:模块化打包类、任务流构建类和集合型工具类(脚手架)。其中最为突出的,当属用于模块化打包的Webpack和用于任务流构建的Gulp。下面是截至2018年11月28日某时某刻,GitHub上各个工具的Star数目(听说Star数目可以造假?好生无聊的家伙们!)。 前端的构建一般包括JS转码(使用Babel转ES6或TypeScript自转等)、CSS转码(Less或Sass转Css)、代码或资源的合并与压缩,基础检查和各类测试等等。这些虽与本文关系密切,但都不在讨论的范围之内。原因有二:一是实现这些功能的都是某些插件,不是工具本身,各类构建工具都是直接或间接(调用以自己的模式封装后的插件)使用它们的;二是本文介绍的是,构建方向上的类别和各类别里不同工具间的差异,与具体的操作无关。 3. 模块化打包类 现在的前端项目基本是模块化的,原因就不在这多说。而模块化意味着分散,无法直接用于呈现,因此需要进行相应的打包形成一个整体。有些执行环境(Node)能自动打包各个模块,而有些(浏览器)则因为技术或其它考虑需要自行操作。模块化打包工具就是为模块化项目在浏览器上的优化呈现而服务的。 模块化打包的核心是:找出依赖,打包成体。各类工具的基本运行思路便是根据已有配置,从某个文件开始,递归的找出所有与其相关的依赖模块,打包成某种类型的可直接在浏览器中运行的一个或多个新文件。这之中还可以优化输出,以实现代码分离、异步加载和长效缓存等高级功能。 3-1. Browserify 官网 &#124; GitHub 正如其官网介绍的,Browserify会递归的分析,项目中所有使用require引入的JS模块(包括Node内置模块)并打包,使得Node类项目能在浏览器上运行。不过对于与项目有关的其它资源,比如Css和图片等,依然需要手动管理。虽然网上已有人编写了支持此些功能的插件,但这不仅违背了设计初衷,也使配置变得杂乱。而且对于要求越来越高的单页面应用来说,它能提供的助力着实已显疲惫。 3-2. Webpack 官网 &#124; 中文 &#124; GitHub 稳定版已到v4.26.0,本文以此版本为据。另附加官方的对比文档。 Webpack的设计思想新颖实用,社区活跃,功能强大全面,已经是针对前端各类资源的、目前最优秀的模块化管理和打包工具。它入门简单,基本的常用功能能很快上手,并用于实际开发。但精通不易,毕竟打包已是web开发中最重要的挑战之一,必然要耗费些许精力。学习尚且不易,介绍就更为困难,得要有一本书的厚度。所幸此节不是详细介绍,只是亮点阐述,善哉善哉。 3-2-1. 入门已趋简单 掌握了构建的基本思路,任意工具的入门都是较为简单的(读者批:废话)。之所以强调Webpack入门简单,是为了减轻有意者学习之前的顾虑。一方面是它刚被推出时,由于自身的概念新颖而且文档不全面,使开发者处于懵懵懂懂的状态,总感觉离真谛还差些距离。另一方面是它的体系着实庞大,仔细想想都不免胆怯。笔者初次接触时便是这些个感受。 但现在不一样。吃土的日子已经远去,啃草的梦想还会远吗?大家准备好镰刀! Webpack第四版在入门上的方便性体现在三方面。一是基础功能高度集成和约定优于配置思想:安装好Webpack及其CLI后便可直接打包JS和JSON文件,与Browserify一样简单。二是官方文档详细(而且有基本同步的中文版),无论是概念的解析、实际运用的示例还是接口的展示都十分完备。三是现在使用和介绍Webpack的人已经很多了,因此网上的各路资料和相应问题的解决方案都十分丰富。你还在犹豫? 3-2-2. 一切皆模块 如从官网上截取的图片所示,在Webapck眼中一切文件(.js、.css、.jpg、.woff、.csv和.ts等除了某些用于下载的静态大文件外)都是模块,都能通过与JS相似的方式被打包,并安置于合适浏览器渲染的位置。真是十分优秀的立足点。以此思想便可囊括前端会使用到的几乎所有资源,可以十分方便的统一管理和安置,更为便捷和高效。 而且此思想就是为单页面应用而生的。在Webpack的另一层意境中,一个asset(各类资源)是一个模块,一个component是一个模块,一个module也是一个模块。而单页面应用的特点,不就是应用的更新不是整个页面的更新,而是某个module或component或asset的更新吗?十分的契合。 有人说Webpack的缺点在服务端渲染(或说多页面应用)上。喂喂,一来别人的目标本就不在此,二是多页面应用也不需要如此复杂的支持体系。 3-2-3. 高效的构建性能 单页面应用或说需要构建才能展示的应用,相比多页面应用,从每次修改到重新呈现要多经历一个构建的阶段。实际操作中,如果项目庞大而构建性能不够优化,一个小小的修改(打印某值)都会消耗5秒以上的时间,对开发者来说真是个地狱!而优化的方法不外乎两点,一是开发者优化项目的构建思路,二是构建工具优化自身的构建性能。 Webpack拥有较理想的构建性能。在开发阶段,当开启了Webpack的模块热替换之后(使用webpack-dev-server会自动开启),一旦检测到文件被修改,会在应用程序运行过程中通过冒泡捕获的方式最小化替换、添加或删除模块,而无需重新加载整个页面。类似Dom渲染中的回流:如果子元素发生的大小变化,会影响兄弟元素但不影响父元素,那么父元素及其它是无需重新绘制的。而且即便完全重新构建,也会保留先前的应用程序状态,减少等待时间。&#46;&#46;&#46;</p>
The post <a href="http://www.mobabel.net/%e8%bd%ac%e5%89%8d%e7%ab%af%e6%9e%84%e5%bb%ba%ef%bc%9a3%e7%b1%bb13%e7%a7%8d%e7%83%ad%e9%97%a8%e5%b7%a5%e5%85%b7%e7%9a%84%e9%80%89%e5%9e%8b%e5%8f%82%e8%80%83/">[转]前端构建:3类13种热门工具的选型参考</a> first appeared on <a href="http://www.mobabel.net">Mobabel</a>.]]></description>
										<content:encoded><![CDATA[<h2 id='1-前言'  id="boomdevs_1" id="articleHeader0" >1. 前言</h2>
<p>在前端项目的规模和复杂性不断提升的情况下,各类构建思想和相应工具层出不穷。本文竭己所能对比了当下13个构建工具,包括<code>Browserify</code>、<code>Webpack</code>、<code>Rollup</code>、<code>Grunt</code>、<code>Gulp</code>和<code>Yeoman</code>6个广为流行的工具,<code>FIS</code>、<code>Athena</code>、<code>WeFlow</code>和<code>Cooking</code>等4个国产工具,以及三大框架:<code>React</code>,<code>Vue</code>和<code>Angular</code>的官方脚手架。希望能在项目初期的构建工具选型上为大家提供些参考。<span id="more-2823"></span></p>
<h2 id='2-全览'  id="boomdevs_2" id="articleHeader1" >2. 全览</h2>
<p>构建工具可以分为三类:模块化打包类、任务流构建类和集合型工具类(脚手架)。其中最为突出的,当属用于模块化打包的<code>Webpack</code>和用于任务流构建的<code>Gulp</code>。下面是截至2018年11月28日某时某刻,<code>GitHub</code>上各个工具的<code>Star</code>数目(听说<code>Star</code>数目可以造假?好生无聊的家伙们!)。</p>
<p><img loading="lazy" decoding="async" class="alignnone size-full wp-image-2824" src="http://www.mobabel.net/wp-content/uploads/2018/12/04/4093743712-5bfebba74cba0_articlex_223559.png" width="800" height="302" srcset="http://www.mobabel.net/wp-content/uploads/2018/12/04/4093743712-5bfebba74cba0_articlex_223559.png 800w, http://www.mobabel.net/wp-content/uploads/2018/12/04/4093743712-5bfebba74cba0_articlex_223559-300x113.png 300w, http://www.mobabel.net/wp-content/uploads/2018/12/04/4093743712-5bfebba74cba0_articlex_223559-768x290.png 768w" sizes="(max-width: 800px) 100vw, 800px" /></p>
<p>前端的构建一般包括JS转码(使用<code>Babel</code>转<code>ES6</code>或<code>TypeScript</code>自转等)、CSS转码(<code>Less</code>或<code>Sass</code>转<code>Css</code>)、代码或资源的合并与压缩,基础检查和各类测试等等。这些虽与本文关系密切,但都不在讨论的范围之内。原因有二:一是实现这些功能的都是某些插件,不是工具本身,各类构建工具都是直接或间接(调用以自己的模式封装后的插件)使用它们的;二是本文介绍的是,构建方向上的类别和各类别里不同工具间的差异,与具体的操作无关。</p>
<h2 id='3-模块化打包类'  id="boomdevs_3" id="articleHeader2" >3. 模块化打包类</h2>
<p>现在的前端项目基本是模块化的,原因就不在这多说。而模块化意味着分散,无法直接用于呈现,因此需要进行相应的打包形成一个整体。有些执行环境(<code>Node</code>)能自动打包各个模块,而有些(浏览器)则因为技术或其它考虑需要自行操作。模块化打包工具就是为模块化项目在浏览器上的优化呈现而服务的。</p>
<p>模块化打包的核心是:找出依赖,打包成体。各类工具的基本运行思路便是根据已有配置,从某个文件开始,递归的找出所有与其相关的依赖模块,打包成某种类型的可直接在浏览器中运行的一个或多个新文件。这之中还可以优化输出,以实现代码分离、异步加载和长效缓存等高级功能。</p>
<h3 id='3-1-browserify'  id="boomdevs_4" id="articleHeader3" >3-1. Browserify</h3>
<p><a href="http://browserify.org/" target="_blank" rel="nofollow noopener noreferrer">官网</a> | <a href="https://github.com/browserify/browserify" target="_blank" rel="nofollow noopener noreferrer">GitHub</a></p>
<p>正如其官网介绍的,<code>Browserify</code>会递归的分析,项目中所有使用<code>require</code>引入的JS模块(包括<code>Node</code>内置模块)并打包,使得<code>Node</code>类项目能在浏览器上运行。不过对于与项目有关的其它资源,比如<code>Css</code>和图片等,依然需要手动管理。虽然网上已有人编写了支持此些功能的插件,但这不仅违背了设计初衷,也使配置变得杂乱。而且对于要求越来越高的单页面应用来说,它能提供的助力着实已显疲惫。</p>
<h3 id='3-2-webpack'  id="boomdevs_5" id="articleHeader4" >3-2. Webpack</h3>
<p><a href="https://webpack.js.org/" target="_blank" rel="nofollow noopener noreferrer">官网</a> | <a href="https://webpack.docschina.org/" target="_blank" rel="nofollow noopener noreferrer">中文</a> | <a href="https://github.com/webpack/webpack" target="_blank" rel="nofollow noopener noreferrer">GitHub</a></p>
<p>稳定版已到<code>v4.26.0</code>,本文以此版本为据。另附加官方的对比<a href="https://webpack.docschina.org/comparison" target="_blank" rel="nofollow noopener noreferrer">文档</a>。</p>
<p><img loading="lazy" decoding="async" class="alignnone size-full wp-image-2825" src="http://www.mobabel.net/wp-content/uploads/2018/12/04/584065867-5bfeb7ada43ae_articlex_223607.png" width="800" height="356" srcset="http://www.mobabel.net/wp-content/uploads/2018/12/04/584065867-5bfeb7ada43ae_articlex_223607.png 800w, http://www.mobabel.net/wp-content/uploads/2018/12/04/584065867-5bfeb7ada43ae_articlex_223607-300x134.png 300w, http://www.mobabel.net/wp-content/uploads/2018/12/04/584065867-5bfeb7ada43ae_articlex_223607-768x342.png 768w" sizes="(max-width: 800px) 100vw, 800px" /></p>
<p><code>Webpack</code>的设计思想新颖实用,社区活跃,功能强大全面,已经是针对前端各类资源的、目前最优秀的模块化管理和打包工具。它入门简单,基本的常用功能能很快上手,并用于实际开发。但精通不易,毕竟打包已是<code>web</code>开发中最重要的挑战之一,必然要耗费些许精力。学习尚且不易,介绍就更为困难,得要有一本书的厚度。所幸此节不是详细介绍,只是亮点阐述,善哉善哉。</p>
<h4 id='3-2-1-入门已趋简单'  id="boomdevs_6" >3-2-1. 入门已趋简单</h4>
<p>掌握了构建的基本思路,任意工具的入门都是较为简单的(读者批:废话)。之所以强调<code>Webpack</code>入门简单,是为了减轻有意者学习之前的顾虑。一方面是它刚被推出时,由于自身的概念新颖而且文档不全面,使开发者处于懵懵懂懂的状态,总感觉离真谛还差些距离。另一方面是它的体系着实庞大,仔细想想都不免胆怯。笔者初次接触时便是这些个感受。</p>
<p>但现在不一样。吃土的日子已经远去,啃草的梦想还会远吗?大家准备好镰刀!</p>
<p><code>Webpack</code>第四版在入门上的方便性体现在三方面。一是基础功能高度集成和约定优于配置思想:安装好<code>Webpack</code>及其<code>CLI</code>后便可直接打包<code>JS</code>和<code>JSON</code>文件,与<code>Browserify</code>一样简单。二是官方文档详细(而且有基本同步的中文版),无论是概念的解析、实际运用的示例还是接口的展示都十分完备。三是现在使用和介绍<code>Webpack</code>的人已经很多了,因此网上的各路资料和相应问题的解决方案都十分丰富。你还在犹豫?</p>
<h4 id='3-2-2-一切皆模块'  id="boomdevs_7" >3-2-2. 一切皆模块</h4>
<p>如从官网上截取的图片所示,在<code>Webapck</code>眼中一切文件(<code>.js</code>、<code>.css</code>、<code>.jpg</code>、<code>.woff</code>、<code>.csv</code>和<code>.ts</code>等除了某些用于下载的静态大文件外)都是模块,都能通过与<code>JS</code>相似的方式被打包,并安置于合适浏览器渲染的位置。真是十分优秀的立足点。以此思想便可囊括前端会使用到的几乎所有资源,可以十分方便的统一管理和安置,更为便捷和高效。</p>
<p>而且此思想就是为单页面应用而生的。在<code>Webpack</code>的另一层意境中,一个<code>asset</code>(各类资源)是一个模块,一个<code>component</code>是一个模块,一个<code>module</code>也是一个模块。而单页面应用的特点,不就是应用的更新不是整个页面的更新,而是某个<code>module</code>或<code>component</code>或<code>asset</code>的更新吗?十分的契合。</p>
<p>有人说<code>Webpack</code>的缺点在服务端渲染(或说多页面应用)上。喂喂,一来别人的目标本就不在此,二是多页面应用也不需要如此复杂的支持体系。</p>
<h4 id='3-2-3-高效的构建性能'  id="boomdevs_8" >3-2-3. 高效的构建性能</h4>
<p>单页面应用或说需要构建才能展示的应用,相比多页面应用,从每次修改到重新呈现要多经历一个构建的阶段。实际操作中,如果项目庞大而构建性能不够优化,一个小小的修改(打印某值)都会消耗5秒以上的时间,对开发者来说真是个地狱!而优化的方法不外乎两点,一是开发者优化项目的构建思路,二是构建工具优化自身的构建性能。</p>
<p><code>Webpack</code>拥有较理想的构建性能。在开发阶段,当开启了<code>Webpack</code>的模块热替换之后(使用<code>webpack-dev-server</code>会自动开启),一旦检测到文件被修改,会在应用程序运行过程中通过冒泡捕获的方式最小化替换、添加或删除模块,而无需重新加载整个页面。类似<code>Dom</code>渲染中的回流:如果子元素发生的大小变化,会影响兄弟元素但不影响父元素,那么父元素及其它是无需重新绘制的。而且即便完全重新构建,也会保留先前的应用程序状态,减少等待时间。</p>
<h4 id='3-2-4-活跃的社区'  id="boomdevs_9" >3-2-4. 活跃的社区</h4>
<p>活跃的社区可以提升系统的丰富度,降低学习与使用的成本。</p>
<p><code>Webapck</code>社区十分活跃,应用于各种需求的插件都被一一封装而可直接使用(官方也统一展示和说明了一些常用的优秀的<code>Loader</code>和<code>Plugin</code>)。不单单是其它工具的高度协调,开发中的各个阶段:搭建本地服务器、集成测试等,以及与任务流工具(<code>Gulp</code>、<code>Grunt</code>)的集成等等方面的解决或最优方案,都是丰富和全面的。基本上可以想到的需求,在这个社区中,都能直接借鉴他人已有的成果。</p>
<h3 id='3-3-rollup'  id="boomdevs_10" id="articleHeader5" >3-3. Rollup</h3>
<p><a href="https://www.rollupjs.com/guide/en" target="_blank" rel="nofollow noopener noreferrer">官网</a> | <a href="https://www.rollupjs.com/guide/zh" target="_blank" rel="nofollow noopener noreferrer">中文</a> | <a href="https://github.com/rollup/rollup" target="_blank" rel="nofollow noopener noreferrer">GitHub</a></p>
<p><code>Rollup</code>定位为一个<code>JS</code>模块打包器(明指<code>JS</code>),主要用来构建<code>JS</code>库,也可服务于一些无需代码拆分和动态导入的小型应用程序。能在<code>Webpack</code>已稳居打包之首的情况下杀出一条血路,得到<code>Vue</code>、<code>D3</code>、<code>Three</code>和<code>React</code>等著名库的青睐,想必其着手点和性能有过人之处。</p>
<p><code>Rollup</code>本身结构简单,需要的配置项也不多,再加文档全面,所以很容易上手并全部掌握。它使用<code>ES6</code>本身的<code>Module</code>语法作为自己的标准,而不是诸如<code>CommonJS</code>和<code>AMD</code>等以前的解决方案。这意味着按照<code>Module</code>标准编成的代码,不仅现在可以借助<code>Rollup</code>打包运行,未来更能在实现此标准的浏览器上直接运行。</p>
<p>通过<code>Module</code>的特性,<code>Rollup</code>开创了<code>Tree-shaking</code>功能——清除没有在项目中使用到的代码。它基于显式的<code>import</code>和<code>export</code>语句的方式,通过静态分析,排除了任何未在实际中使用的代码,能极大的减少构建于已有模块的项目体积。再加上其构建基本不添加自身的控制代码,使打包后的文件真正的达到纯净二字。想想还有点痒痒,我挠挠裆部。</p>
<h4 id='3-3-1-与-webpack-对比'  id="boomdevs_11" >3-3-1. 与 Webpack 对比</h4>
<p><code>Rollup</code>和<code>Webpack</code>因其定位和专注点是可以共同存在并相互支持的。</p>
<p>正如<code>Rollup</code>官网所说的,<code>Rollup</code>更适合构建独立的<code>JS</code>库,而<code>Webpack</code>为资源丰富的应用程序。虽然<code>Webpack</code>也增加了自己的<code>Tree-shaking</code>功能,但在编译后的输出代码中,简单地运行自动<code>minifier</code>检测未使用的变量,得到的结果是不如原生的静态分析。更何况<code>Webpack</code>生成的代码一定是经过自己包装后的代码——将每个模块封装在一个函数中,再置于一个包中,通过浏览器能使用的<code>require</code>方式逐一执行这些模块。</p>
<h2 id='4-任务流构建类'  id="boomdevs_12" id="articleHeader6" >4. 任务流构建类</h2>
<p>基于任务的构建行为,是不在乎操作对象是否为模块化的。</p>
<p>这类工具的目标是通过配置来解放日常需要重复的工作——转化、合并压缩和单元测试等等。有人说:这些操作<code>Webpack</code>和<code>Rollup</code>不是也能做?是的,基本能做。实际上,在用模块化构建工具的开发中,很少会用到任务流构建工具。但这绝不是说任务流工具会被取代,也不会被取代,至少多页面应用需要。再说任务流工具是十分纯粹的自动化行为,与模块化打包工具立足点就不一样,何谈取代一说。</p>
<h3 id='4-1-grunt'  id="boomdevs_13" id="articleHeader7" >4-1. Grunt</h3>
<p><a href="https://gruntjs.com/" target="_blank" rel="nofollow noopener noreferrer">官网</a> | <a href="https://www.gruntjs.net/" target="_blank" rel="nofollow noopener noreferrer">中文</a> | <a href="https://github.com/gruntjs/grunt" target="_blank" rel="nofollow noopener noreferrer">GitHub</a></p>
<p><code>Grunt</code>虽是老牌构建工具,但依然被许多知名项目如<code>WordPress</code>、<code>Twitter</code>和<code>Jquery</code>等使用,也拥有持续更新的完整生态圈和中文文档。它是通过配置驱动——通过获取到的<code>JSON</code>配置执行操作,来流水线式执行相应任务。虽然在学习成本和执行效率上不出众,但如果项目原本就是通过它自动化构建的,是没有必要迁移到其它工具的。</p><pre class="crayon-plain-tag">&lt;span class=&quot;hljs-comment&quot;&gt;// Grunt 的配置驱动示例&lt;/span&gt;
&lt;span class=&quot;hljs-built_in&quot;&gt;module&lt;/span&gt;.exports = &lt;span class=&quot;hljs-function&quot;&gt;&lt;span class=&quot;hljs-keyword&quot;&gt;function&lt;/span&gt;(&lt;span class=&quot;hljs-params&quot;&gt;grunt&lt;/span&gt;) &lt;/span&gt;{
  grunt.initConfig({
    &lt;span class=&quot;hljs-attr&quot;&gt;jshint&lt;/span&gt;: {
      &lt;span class=&quot;hljs-attr&quot;&gt;files&lt;/span&gt;: [&lt;span class=&quot;hljs-string&quot;&gt;'Gruntfile.js'&lt;/span&gt;, &lt;span class=&quot;hljs-string&quot;&gt;'src/**/*.js'&lt;/span&gt;, &lt;span class=&quot;hljs-string&quot;&gt;'test/**/*.js'&lt;/span&gt;],
      &lt;span class=&quot;hljs-attr&quot;&gt;options&lt;/span&gt;: {
        &lt;span class=&quot;hljs-attr&quot;&gt;globals&lt;/span&gt;: {
          &lt;span class=&quot;hljs-attr&quot;&gt;jQuery&lt;/span&gt;: &lt;span class=&quot;hljs-literal&quot;&gt;true&lt;/span&gt;
        }
      }
    },
    &lt;span class=&quot;hljs-attr&quot;&gt;watch&lt;/span&gt;: {
      &lt;span class=&quot;hljs-attr&quot;&gt;files&lt;/span&gt;: [&lt;span class=&quot;hljs-string&quot;&gt;'&amp;lt;%= jshint.files %&amp;gt;'&lt;/span&gt;],
      &lt;span class=&quot;hljs-attr&quot;&gt;tasks&lt;/span&gt;: [&lt;span class=&quot;hljs-string&quot;&gt;'jshint'&lt;/span&gt;]
    }
  });

  grunt.loadNpmTasks(&lt;span class=&quot;hljs-string&quot;&gt;'grunt-contrib-jshint'&lt;/span&gt;);
  grunt.loadNpmTasks(&lt;span class=&quot;hljs-string&quot;&gt;'grunt-contrib-watch'&lt;/span&gt;);

  grunt.registerTask(&lt;span class=&quot;hljs-string&quot;&gt;'default'&lt;/span&gt;, [&lt;span class=&quot;hljs-string&quot;&gt;'jshint'&lt;/span&gt;]);

};</pre><p></p>
<h3 id='4-2-gulp'  id="boomdevs_14" id="articleHeader8" >4-2. Gulp</h3>
<p><a href="https://gulpjs.com/" target="_blank" rel="nofollow noopener noreferrer">官网</a> | <a href="https://www.gulpjs.com.cn/" target="_blank" rel="nofollow noopener noreferrer">中文</a> | <a href="https://github.com/gulpjs/gulp" target="_blank" rel="nofollow noopener noreferrer">GitHub</a></p>
<p><code>Gulp</code>是新型的构建工具,虽与<code>Grunt</code>的功能相同,但其构建过程却有三大优势。</p>
<h4 id='4-2-1-代码驱动'  id="boomdevs_15" >4-2-1. 代码驱动</h4>
<p>代码驱动即通过执行实际代码驱动程序执行,与常见的配置驱动不同(<code>Webpack</code>、<code>Rollup</code>和<code>Grunt</code>等都是配置驱动)。从任务流构建的角度上看,代码驱动相比配置驱动有三点好处:一是高度的灵活;二是没有过多的配置项,减少学习成本;三是更方便错误的断定和异常情况的调试。</p><pre class="crayon-plain-tag">&lt;span class=&quot;hljs-comment&quot;&gt;// Gulp 的代码驱动示例&lt;/span&gt;
gulp.src(&lt;span class=&quot;hljs-string&quot;&gt;'./client/templates/*.jade'&lt;/span&gt;)
  .pipe(jade())
  .pipe(gulp.dest(&lt;span class=&quot;hljs-string&quot;&gt;'./build/templates'&lt;/span&gt;))
  .pipe(minify())
  .pipe(gulp.dest(&lt;span class=&quot;hljs-string&quot;&gt;'./build/minified_templates'&lt;/span&gt;));</pre><p></p>
<h4 id='4-2-2-node流'  id="boomdevs_16" >4-2-2. Node流</h4>
<p><code>Gulp</code>作为后来者,充分利用<code>NodeJS</code>流的思想进行<code>IO</code>操作,极大增加了大型项目的构建速度。比方说转化<code>Scss</code>成<code>Css</code>,<code>Grunt</code>的操作流程是:读取<code>Scss</code>文件、转化成<code>Css</code>、存储到磁盘,读取<code>Css</code>、压缩处理最后存储到磁盘;而<code>Gulp</code>得操作是:读取<code>Scss</code>文件、转化成<code>Css</code>、压缩处理最后存储到磁盘。一步到位,无需多次的<code>IO</code>操作。</p>
<h4 id='4-2-3-简单明了'  id="boomdevs_17" >4-2-3. 简单明了</h4>
<p><code>Gulp</code>有十分精简的<code>API</code>。你能想到各种类型的任务,基本是通过仅有的五个可链式操作的方法实现的吗?不仅仅是学习和使用方便,编写后的功能也是一目了然。虽然代码驱动相比配置驱动,需要自己写的代码增加,但一是没增加难度只是函数名的多次重写,二是相对代码驱动的好处来说可以忽略。</p>
<h2 id='5-集合型工具类'  id="boomdevs_18" id="articleHeader9" >5. 集合型工具类</h2>
<p>集合型工具类便是常说的脚手架,也可以看作是以模块化或任务流工具为主体的,各类常用工具的高度封装。它是一个开箱即可用的集合体,类似前后端同构时代的后端框架。它会根据你的选择,生成一个完整的、已配置好各类工具的、具有某些特定代码约定的项目框架。这些配置几乎包揽前端开发的整个流程,甚至可以集成自动化部署等后端接口。</p>
<h3 id='5-1-官方框架'  id="boomdevs_19" id="articleHeader10" >5-1. 官方框架</h3>
<p><a href="https://facebook.github.io/create-react-app/" target="_blank" rel="nofollow noopener noreferrer">React CLI</a> | <a href="https://cli.vuejs.org/" target="_blank" rel="nofollow noopener noreferrer">Vue CLI</a> | <a href="https://cli.angular.io/" target="_blank" rel="nofollow noopener noreferrer">Angular CLI</a></p>
<p>集合型工具一般为单页面应用服务,而单页面应用需要使用某个前端框架。无论你是用<code>React</code>、<code>Vue</code>或<code>Angular</code>,还是其它框架,首先得想到它是否有官方脚手架。比如<code>Vue</code>有<code>Vue CLI</code>。一般推荐有官方脚手架的直接使用官方的。因为现代前端框架一般不单独运行,需结合官方提供的其它工具,比如路由、状态管理等。而且各个框架及配件更新不断,每次更新都可能导致与其它插件的兼容问题,新的功能可能需要某些特定插件才能发挥作用。这是一项工程,仅靠个人或某些团体很难照顾周全的。而各个框架又都有意识的通过官方脚手架来充分展示新的特性,降低学习和使用的成本。我们何乐而不为呢?</p>
<h3 id='5-2-yeoman'  id="boomdevs_20" id="articleHeader11" >5-2. Yeoman</h3>
<p><a href="http://yeoman.io/" target="_blank" rel="nofollow noopener noreferrer">官网</a> | <a href="https://github.com/yeoman/yeoman" target="_blank" rel="nofollow noopener noreferrer">GitHub</a></p>
<p><code>Yeoman</code>是一个专为现代前端而生的、灵活通用的脚手架工具。</p>
<p>它的运作方式和其它脚手架不同。在安装好<code>CLI</code>后,需要找到一个符合要求的<code>Generator</code>(一个<code>npm</code>包,相当于脚手架),使用<code>Yeoman</code>运行安装,生成初始化的项目。你也可以自行配置,使用<code>Yeoman</code>封装成符合特定需求的<code>Generator</code>,并发布出去。等到下次,其他人或你自己,需要生成符合此要求的项目时,便可以直接安装并使用<code>Yeoman</code>生成。</p>
<p>这样有明显的两点好处:一是节省体力。在开始一个有特定需求的新项目时,如果有老项目可借鉴,一般会直接复制相关文件。但这样的复制文件可能不纯粹,即增加体积又带来安全隐患。二是在社区的支持下,很多有特殊要求的脚手架,早已有人解决并发布成<code>Generator</code>,是没必要自己动手的。</p>
<h3 id='5-3-国内其它'  id="boomdevs_21" id="articleHeader12" >5-3. 国内其它</h3>
<p>百度 &#8211; FIS &#8211; <a href="http://fis.baidu.com/" target="_blank" rel="nofollow noopener noreferrer">官网</a> | <a href="https://github.com/fex-team/fis3" target="_blank" rel="nofollow noopener noreferrer">GitHub</a><br />
微信 &#8211; WeFlow &#8211; <a href="https://weflow.io/" target="_blank" rel="nofollow noopener noreferrer">官网</a> | <a href="https://github.com/Tencent/WeFlow/" target="_blank" rel="nofollow noopener noreferrer">GitHub</a><br />
京东 &#8211; Athena &#8211; <a href="https://athena.aotu.io/" target="_blank" rel="nofollow noopener noreferrer">官网</a> | <a href="https://github.com/o2team/athena" target="_blank" rel="nofollow noopener noreferrer">GitHub</a><br />
饿了么 &#8211; Cooking(名字与公司的性质相得益彰) &#8211; <a href="http://elemefe.github.io/cooking/" target="_blank" rel="nofollow noopener noreferrer">官网</a> | <a href="https://github.com/ElemeFE/cooking" target="_blank" rel="nofollow noopener noreferrer">GitHub</a></p>
<p>作为程序员或至各行各业,在与年龄增长速度相当的压力下,工资的高低自然成为日常性的评定标准。但在同行老友的酒桌上或某个太阳异常温煦下的小道上,能使自己为自己而不是其他事骄傲的,也肯定是“老子之前做过些什么”之类的实际付出而不是物质方面的获得。因此能够成为被公司支持的、被众多人使用的、开源框架维护团队中的程序员,多少是更为幸福的一类。</p>
<p>这些由国内各个前端团队开发的集合型脚手架,都是基于自用在实践中得到的最为符合本身需求的产品。里面的包含内容十分丰富,不仅仅是这以上提到的前端本职工作,还有与后端的集成方案或自动化部署配置等。且流程简化,开箱即可使用。不过这些笔者都没用过,也没有打算用。不是打趣,原因很现实,有识之士可以在文章下留言。不用却依然写出的原因倒是简单:宣传,宣传即赞许和期盼;凑数,凑到13种好立个多少浮夸的标题。</p>
<h2 id='6-总结'  id="boomdevs_22" id="articleHeader13" >6. 总结</h2>
<p>个人观点,不喜请喷,但要和蔼可亲。</p>
<p>如果是使用某个前端框架开发应用程序,推荐框架官方的脚手架。如果是自己头脑发热想开源个<code>JS</code>库,推荐<code>Rollup</code>打包。如果不是模块化项目,又需要自动化处理一些事情,推荐<code>Gulp</code>作为构建工具。如果项目有特殊要求或作为核心的部件比较稀有,可以先查看<code>Yeoman</code>上是否有符合要求的<code>Generator</code>,没有就只能自食其力。最后如果你处在已有自己脚手架的公司(比如饿了么),可能要按规章制度使用<code>Cooking</code>为自己的仕途烹煮些吃食。肚子真饿,这种宣传饿了么会返优惠券吗?</p>
<p>最后,如果是自食其力的搭建前人没有的脚手架,推荐使用<code>Yeoman</code>发布,方便你我他。</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>[resource]<a href="https://segmentfault.com/a/1190000017183743">前端构建:3类13种热门工具的选型参考</a></p>The post <a href="http://www.mobabel.net/%e8%bd%ac%e5%89%8d%e7%ab%af%e6%9e%84%e5%bb%ba%ef%bc%9a3%e7%b1%bb13%e7%a7%8d%e7%83%ad%e9%97%a8%e5%b7%a5%e5%85%b7%e7%9a%84%e9%80%89%e5%9e%8b%e5%8f%82%e8%80%83/">[转]前端构建:3类13种热门工具的选型参考</a> first appeared on <a href="http://www.mobabel.net">Mobabel</a>.]]></content:encoded>
					
					<wfw:commentRss>http://www.mobabel.net/%e8%bd%ac%e5%89%8d%e7%ab%af%e6%9e%84%e5%bb%ba%ef%bc%9a3%e7%b1%bb13%e7%a7%8d%e7%83%ad%e9%97%a8%e5%b7%a5%e5%85%b7%e7%9a%84%e9%80%89%e5%9e%8b%e5%8f%82%e8%80%83/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>骨架屏</title>
		<link>http://www.mobabel.net/%e9%aa%a8%e6%9e%b6%e5%b1%8f/</link>
					<comments>http://www.mobabel.net/%e9%aa%a8%e6%9e%b6%e5%b1%8f/#respond</comments>
		
		<dc:creator><![CDATA[leelight]]></dc:creator>
		<pubDate>Sat, 24 Nov 2018 12:31:55 +0000</pubDate>
				<category><![CDATA[Theory & Solution]]></category>
		<category><![CDATA[fpr]]></category>
		<category><![CDATA[skeleton]]></category>
		<guid isPermaLink="false">http://www.mobabel.net/?p=2582</guid>

					<description><![CDATA[<p>如何让你的网页“看起来”展现地更快 —— 骨架屏二三事 Vue项目骨架屏注入实践 小程序构建骨架屏的探索</p>
The post <a href="http://www.mobabel.net/%e9%aa%a8%e6%9e%b6%e5%b1%8f/">骨架屏</a> first appeared on <a href="http://www.mobabel.net">Mobabel</a>.]]></description>
										<content:encoded><![CDATA[<p><img loading="lazy" decoding="async" class="alignnone size-full wp-image-2583" src="http://www.mobabel.net/wp-content/uploads/2018/11/24/c79d9749f82cd11702535a34bac3e72e_123233.gif" width="830" height="411" /></p>
<p class="Post-Title"><a href="https://zhuanlan.zhihu.com/p/48601348">如何让你的网页“看起来”展现地更快 —— 骨架屏二三事</a></p>
<p class="article-title"><a href="https://juejin.im/post/5b79a2786fb9a01a18267362">Vue项目骨架屏注入实践</a></p>
<p><span id="more-2582"></span></p>
<p id="activity-name" class="rich_media_title"><a href="https://mp.weixin.qq.com/s?__biz=MjM5MDA2MTI1MA==&amp;mid=2649089588&amp;idx=2&amp;sn=c1f88d3c9710f6a4d1bdd06496a3ed8b&amp;chksm=be5bc199892c488f213a3e23ae176aef41f801b8aeec07194348590ff91f95e38e6c10bba5f5&amp;mpshare=1&amp;scene=24&amp;srcid=#rd">小程序构建骨架屏的探索</a></p>The post <a href="http://www.mobabel.net/%e9%aa%a8%e6%9e%b6%e5%b1%8f/">骨架屏</a> first appeared on <a href="http://www.mobabel.net">Mobabel</a>.]]></content:encoded>
					
					<wfw:commentRss>http://www.mobabel.net/%e9%aa%a8%e6%9e%b6%e5%b1%8f/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>[转]前端如何高效的与后端协作开发</title>
		<link>http://www.mobabel.net/%e8%bd%ac%e5%89%8d%e7%ab%af%e5%a6%82%e4%bd%95%e9%ab%98%e6%95%88%e7%9a%84%e4%b8%8e%e5%90%8e%e7%ab%af%e5%8d%8f%e4%bd%9c%e5%bc%80%e5%8f%91/</link>
					<comments>http://www.mobabel.net/%e8%bd%ac%e5%89%8d%e7%ab%af%e5%a6%82%e4%bd%95%e9%ab%98%e6%95%88%e7%9a%84%e4%b8%8e%e5%90%8e%e7%ab%af%e5%8d%8f%e4%bd%9c%e5%bc%80%e5%8f%91/#respond</comments>
		
		<dc:creator><![CDATA[leelight]]></dc:creator>
		<pubDate>Wed, 07 Nov 2018 12:57:19 +0000</pubDate>
				<category><![CDATA[Theory & Solution]]></category>
		<category><![CDATA[Frontend]]></category>
		<guid isPermaLink="false">http://www.mobabel.net/?p=2377</guid>

					<description><![CDATA[<p>1. 1. 前后端分离 前端与后端的分离,能使前端的开发脱离后端的开发模式,拥有更大的自由度,以此便可做前端工程化、组件化、单页面应用等。 可以参考:前后端分离、web与static服务器分离(https://segmentfault.com/a/1190000015297319)。 2. 2. 尽量避免后端模板渲染 web 应用的渲染方式分为服务器端渲染和客户端渲染,当下比较推荐的方式是客户端渲染,数据使用全 ajax 的方式进行交互。 除非在一些不得不使用服务器端渲染的情况下(如门户、电商等),应当尽量使用客户端渲染,因为客户端渲染更能使前后端分离(项目分离、代码解耦、协作分离、职责分离等),也能更好的做本地接口模拟开发,提升开发效率。 即使用服务器端渲染,在技术支持的条件下,可以使用 node 中间层(由前端人员开发),代替传统的后端模板渲染,这样可以使后端与前端完全解耦,后端与前端只有数据上的往来。 可以参考:细说后端模板渲染、客户端渲染、node 中间层、服务器端渲染(ssr)(https://segmentfault.com/a/1190000016704384)。 3. 3. 尽量避免大量的线上调试 做好本地接口模拟开发(包括后端模板渲染),避免大量的线上调试,因为线上调试很不方便,也很费事,并且每次更新代码,都需要重新构建,然后同步到服务器。 所以做好本地接口模拟开发,只要程序在本地运行是没问题的,一般线上就不会有太大的问题,这样就能大幅降低调试工作量,提升开发效率。 4. 4. 本地接口模拟开发 本地接口模拟就是在本地模拟一个与服务器差不多的环境,能够提供数据所需的接口,进行错误模拟处理等等。 本地接口模拟开发的意义就在于能够在本地完成几乎所有的开发与调试,尽量减少线上的调试,提高开发效率。 一些常用库: browser-sync(https://github.com/BrowserSync/browser-sync):能让浏览器实时、快速响应文件更改( html、 js、css、 sass、 less 等)并自动刷新页面,并且可以同时在PC、平板、手机等设备下进行调试。 webpack-dev-middleware(https://github.com/webpack/webpack-dev-middleware):A development middleware for&#46;&#46;&#46;</p>
The post <a href="http://www.mobabel.net/%e8%bd%ac%e5%89%8d%e7%ab%af%e5%a6%82%e4%bd%95%e9%ab%98%e6%95%88%e7%9a%84%e4%b8%8e%e5%90%8e%e7%ab%af%e5%8d%8f%e4%bd%9c%e5%bc%80%e5%8f%91/">[转]前端如何高效的与后端协作开发</a> first appeared on <a href="http://www.mobabel.net">Mobabel</a>.]]></description>
										<content:encoded><![CDATA[<h4 id='1-1-前后端分离'  id="boomdevs_1" >1. 1. 前后端分离</h4>
<p>前端与后端的分离,能使前端的开发脱离后端的开发模式,拥有更大的自由度,以此便可做前端工程化、组件化、单页面应用等。</p>
<p>可以参考:前后端分离、web与static服务器分离(<a href="https://segmentfault.com/a/1190000015297319">https://segmentfault.com/a/1190000015297319</a>)。<span id="more-2377"></span></p>
<h4 id='2-2-尽量避免后端模板渲染'  id="boomdevs_2" >2. 2. 尽量避免后端模板渲染</h4>
<p>web 应用的渲染方式分为服务器端渲染和客户端渲染,当下比较推荐的方式是客户端渲染,数据使用全 ajax 的方式进行交互。</p>
<p>除非在一些不得不使用服务器端渲染的情况下(如门户、电商等),应当尽量使用客户端渲染,因为客户端渲染更能使前后端分离(项目分离、代码解耦、协作分离、职责分离等),也能更好的做本地接口模拟开发,提升开发效率。</p>
<p>即使用服务器端渲染,在技术支持的条件下,可以使用 node 中间层(由前端人员开发),代替传统的后端模板渲染,这样可以使后端与前端完全解耦,后端与前端只有数据上的往来。</p>
<p>可以参考:细说后端模板渲染、客户端渲染、node 中间层、服务器端渲染(ssr)(<a href="https://segmentfault.com/a/1190000016704384">https://segmentfault.com/a/1190000016704384</a>)。</p>
<h4 id='3-3-尽量避免大量的线上调试'  id="boomdevs_3" >3. 3. 尽量避免大量的线上调试</h4>
<p>做好本地接口模拟开发(包括后端模板渲染),避免大量的线上调试,因为线上调试很不方便,也很费事,并且每次更新代码,都需要重新构建,然后同步到服务器。</p>
<p>所以做好本地接口模拟开发,只要程序在本地运行是没问题的,一般线上就不会有太大的问题,这样就能大幅降低调试工作量,提升开发效率。</p>
<h4 id='4-4-本地接口模拟开发'  id="boomdevs_4" >4. 4. 本地接口模拟开发</h4>
<p>本地接口模拟就是在本地模拟一个与服务器差不多的环境,能够提供数据所需的接口,进行错误模拟处理等等。</p>
<p>本地接口模拟开发的意义就在于能够在本地完成几乎所有的开发与调试,尽量减少线上的调试,提高开发效率。</p>
<p>一些常用库:</p>
<ul class="list-paddingleft-2">
<li>browser-sync(<a href="https://github.com/BrowserSync/browser-sync">https://github.com/BrowserSync/browser-sync</a>):能让浏览器实时、快速响应文件更改( <code class=""><span class="">html</span></code>、 <code class=""><span class="">js</span></code>、<code class=""><span class="">css</span></code>、 <code class=""><span class="">sass</span></code>、 <code class=""><span class="">less</span></code> 等)并自动刷新页面,并且可以同时在PC、平板、手机等设备下进行调试。</li>
<li>webpack-dev-middleware(<a href="https://github.com/webpack/webpack-dev-middleware">https://github.com/webpack/webpack-dev-middleware</a>):A development middleware for webpack。</li>
<li>webpack-hot-middleware(<a href="https://github.com/webpack-contrib/webpack-hot-middleware">https://github.com/webpack-contrib/webpack-hot-middleware</a>):热更新本地开发浏览器服务。</li>
</ul>
<p>另外,本地接口模拟开发需要后端开发人员有规范的接口文档。</p>
<p>可以参考:本地化接口模拟、前后端并行开发(https://segmentfault.com/a/1190000015297352)。</p>
<h4 id='5-5-规范的接口文档'  id="boomdevs_5" >5. 5. 规范的接口文档</h4>
<p>前端与后端协作提升开发效率的一个很重要的方法就是减少沟通:能够形成纸质的文档就不要口头沟通、能够把接口文档写清楚也不要口头沟通(参数、数据结构、字段含义等),特别是线上协作的时候,面对面交流是很困难的。</p>
<p>一个良好的接口文档应当有以下的几点要求与信息:</p>
<ol class="list-paddingleft-2">
<li>格式简洁清晰:推荐用 API Blueprint(https://apiblueprint.org/)</li>
<li>分组:当接口很多的时候,分组就很必要了</li>
<li>接口名、接口描述、接口地址</li>
<li>http 方法、参数、headers、是否序列化</li>
<li>http 状态码、响应数据</li>
</ol>
<p>接口文档可以用一些文档服务(如 leanote(https://github.com/leanote/leanote))来管理文档,也可以用 <code class="">git</code> 来管理;书写方式可以用 <code class="">markdown</code>,也可以 <code class="">YAML</code>、 <code class="">JSON</code> 等。</p>
<p>推荐使用 <code class="">markdown</code> 方式写文档,用 <code class="">git</code> 管理文档。</p>
<p>可以参考:</p>
<ul class="list-paddingleft-2">
<li>本地化接口模拟、前后端并行开发(<a href="https://segmentfault.com/a/1190000015297352">https://segmentfault.com/a/1190000015297352</a>)</li>
<li>API Blueprint(<a href="https://apiblueprint.org/">https://apiblueprint.org/</a>)</li>
</ul>
<h4 id='6-6-去缓存'  id="boomdevs_6" >6. 6. 去缓存</h4>
<p>前端需要做好去客户端缓存的功能,保证用户始终都是使用的最新资源,不会因为因为缓存的问题而出现 bug。</p>
<p>传统的去缓存是在静态资源 url 上加上版本号或者时间戳,不过因为构建工具的出现以及一些浏览器已经不支持这种方式了的缘故,这种方式已经是过去时了。</p>
<p>现在去缓存是将文件 hash 化命名,只要文件变动,文件名就会不一样,以此才能彻底的去缓存。如果使用 webpack 进行打包,会自动将所有文件进行 hash 化命名。</p>
<p>可以参考:webpack output-filename(<a href="https://webpack.js.org/configuration/output/#output-filename">https://webpack.js.org/configuration/output/#output-filename</a>)。</p>
<h4 id='7-7-做好错误处理'  id="boomdevs_7" >7. 7. 做好错误处理</h4>
<p>前端与后端都需要各自做好错误处理,以便发生错误能够有友好的提示,也能在用户反馈时快速准确定位错误来源和原因。</p>
<p>一般前端的错误分为:</p>
<ul class="list-paddingleft-2">
<li>脚本运行错误: <code class=""><span class="">js</span></code> 脚本错误,找到堆栈信息,然后解决</li>
<li>接口错误:服务器报错、数据返回不对、没有响应数据、超时等</li>
</ul>
<p>而接口错误分为:</p>
<ul class="list-paddingleft-2">
<li>状态码错误(状态码非 <code class=""><span class="">2XX</span></code>):服务器报错、超时等</li>
<li>数据错误:没有响应数据、数据格式不对、数据内容不对</li>
</ul>
<p>可以参考:HTTP状态码(<a href="https://baike.baidu.com/item/HTTP%E7%8A%B6%E6%80%81%E7%A0%81/5053660?fr=aladdin">https://baike.baidu.com/item/HTTP%E7%8A%B6%E6%80%81%E7%A0%81/5053660?fr=aladdin</a>)。</p>
<h4 id='8-8-运行时捕捉-js-脚本错误'  id="boomdevs_8" >8. 8. 运行时捕捉 <code class="">js</code> 脚本错误</h4>
<p>当用户在用线上的程序时,怎么知道有没有出 bug;如果出 bug 了,报的是什么错;如果是 js 报错,怎么知道是那一行运行出了错?</p>
<p>所以,在程序运行时捕捉 <code class="">js</code> 脚本错误,并上报到服务器,是非常有必要的。</p>
<p>这里就要用到 <code class="">window.onerror</code> 了:</p><pre class="crayon-plain-tag">window.onerror = (errorMessage, scriptURI, lineNumber, columnNumber, errorObj) =&gt; {

  
const
 data = {

    title: document.getElementsByTagName('title')[0].innerText,

    errorMessage,

    scriptURI,

    lineNumber,

    columnNumber,

    detailMessage: (errorObj &amp;&amp; errorObj.message) || '',

    stack: (errorObj &amp;&amp; errorObj.stack) || '',

    userAgent: window.navigator.userAgent,

    locationHref: window.location.href,

    cookie: window.document.cookie,

  };


  post('url', data); // 上报到服务器

};</pre><p>&nbsp;</p>
<p>线上的 <code class="">js</code> 脚本都是压缩过的,需要用 <code class="">sourcemap</code> 文件与 source-map(https://github.com/mozilla/source-map) 查看原始的报错堆栈信息。</p>
<p>可以参考:</p>
<p><code></code></p>
<ul class="list-paddingleft-2">
<li>webpack &#8211; devtool(<a href="https://webpack.js.org/configuration/devtool/">https://webpack.js.org/configuration/devtool/</a>)</li>
<li>source-map(<a href="https://github.com/mozilla/source-map">https://github.com/mozilla/source-map</a>)</li>
</ul>
<h4 id='9-9-移动端远程调试-vconsole-tbs-studio'  id="boomdevs_9" >9. 9. 移动端远程调试、vConsole、TBS Studio</h4>
<p>因为移动端的开发无法像 pc 端开发一样使用 Chrome 的开发者调试工具,所以调试移动端需要一些额外的技巧。</p>
<p>移动端应用一般都运行在微信浏览器中、 <code class="">webview</code> 中、手机浏览器中。</p>
<h5 id='9-1-远程调试-remote-debugging'  id="boomdevs_10" >9-1. 远程调试(Remote Debugging)</h5>
<p>远程调试就是通过 USB 连接、端口转发、搭建代理等方式,将一个设备的 <code class="">web</code> 页面映射到另一个设备上,比如将手机的 <code class="">webview</code> 映射到 pc 上,达到调试的目的。</p>
<p>移动端 <code class="">web</code> 应用调试难题从一开始就有,不过后来浏览器厂商基本都推出自己的远程调试工具来解决这个问题,包括 <code class="">OperaMobile</code>、 <code class="">iOSSafari</code>、 <code class="">ChromeforAndroid</code>、UC 浏览器等,另外还有一些第三方开发的远程调试工具,比如 weinre(<a href="http://people.apache.org/~pmuellr/weinre/docs/1.x/1.5.0/">http://people.apache.org/~pmuellr/weinre/docs/1.x/1.5.0/</a>) 等。</p>
<p>以 Android 为例,可以将 <code class="">webview</code>、 <code class="">ChromeforAndroid</code> 中的页面映射到 pc 端的 <code class="">ChromeDevTools</code>,然后就可以在 pc 端调试移动端的页面了。</p>
<p>可以参考:移动端Web开发调试之Chrome远程调试(Remote Debugging)(<a href="https://blog.csdn.net/freshlover/article/details/42528643/">https://blog.csdn.net/freshlover/article/details/42528643/</a>)。</p>
<h5 id='9-2-vconsole'  id="boomdevs_11" >9-2. vConsole</h5>
<p>一个轻量、可拓展、针对手机网页的前端开发者调试面板( <code class="">chrome</code> 开发者工具的便利实现)。</p>
<p>这个是内嵌的页面当中的便捷调试器,基本上能够满足一般的需要远程调试的页面。</p>
<ul class="list-paddingleft-2">
<li><code class=""><span class="">github</span></code>:<a href="https://github.com/Tencent/vConsole">https://github.com/Tencent/vConsole</a></li>
<li><code class=""><span class="">demo</span></code>:<a href="https://wechatfe.github.io/vconsole/demo.html">https://wechatfe.github.io/vconsole/demo.html</a></li>
</ul>
<p><img loading="lazy" decoding="async" class="alignnone size-full wp-image-2378" src="http://www.mobabel.net/wp-content/uploads/2018/11/07/4ffce04d92a4d6cb21c1494cdfcd6dc1_125720.jpg" width="320" height="568" srcset="http://www.mobabel.net/wp-content/uploads/2018/11/07/4ffce04d92a4d6cb21c1494cdfcd6dc1_125720.jpg 320w, http://www.mobabel.net/wp-content/uploads/2018/11/07/4ffce04d92a4d6cb21c1494cdfcd6dc1_125720-169x300.jpg 169w" sizes="(max-width: 320px) 100vw, 320px" /></p>
<h5 id='9-3-tbs-studio'  id="boomdevs_12" >9-3. TBS Studio</h5>
<p>因为微信浏览器是定制的浏览器,一般的远程调试方式都不可用,需要配合特定的工具,如微信开发者工具。</p>
<p>TBS Studio(https://x5.tencent.com/tbs/guide.html) 是另一个可以像 Chrome 一样调试远程微信浏览器页面的强大工具。</p>
<p>可以参考:</p>
<ul class="list-paddingleft-2">
<li>tbs studio &#8211; 腾讯浏览服务-调试工具(<a href="https://x5.tencent.com/tbs/guide/debug/season1.html">https://x5.tencent.com/tbs/guide/debug/season1.html</a>)</li>
<li>TBS Studio(<a href="https://x5.tencent.com/tbs/guide.html">https://x5.tencent.com/tbs/guide.html</a>)</li>
</ul>
<h4 id='10-10-前端后并行开发'  id="boomdevs_13" >10. 10. 前端后并行开发</h4>
<p>正常情况下,前端的开发在完成 UI 或者组件开发之后,就需要等后端给出接口文档才能继续进行,如果能做到前后端并行开发,也能提升开发效率。</p>
<p>前后端并行开发,就是说前端的开发不需要等后端给出接口文档就可以进行开发,等后端给出接口之后,再对接好后就基本上可以上线了。</p>
<p>在本地化接口模拟的实现下,就可以做到前后端并行开发,只是在代码层面需要对 <code class="">ajax</code> 进行封装。</p>
<p>可以参考:本地化接口模拟、前后端并行开发(https://segmentfault.com/a/1190000015297352)。</p>
<h4 id='11-11-友好的沟通'  id="boomdevs_14" >11. 11. 友好的沟通</h4>
<p>不管工具多么厉害,很多时候都免不了要当面沟通,友好、心平气和的沟通也是很重要的哩!</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>作者:深予之 (@senntyou)</p>
<p>[resource]<a href="http://segmentfault.com/a/1190000016852780">前端如何高效的与后端协作开发</a></p>The post <a href="http://www.mobabel.net/%e8%bd%ac%e5%89%8d%e7%ab%af%e5%a6%82%e4%bd%95%e9%ab%98%e6%95%88%e7%9a%84%e4%b8%8e%e5%90%8e%e7%ab%af%e5%8d%8f%e4%bd%9c%e5%bc%80%e5%8f%91/">[转]前端如何高效的与后端协作开发</a> first appeared on <a href="http://www.mobabel.net">Mobabel</a>.]]></content:encoded>
					
					<wfw:commentRss>http://www.mobabel.net/%e8%bd%ac%e5%89%8d%e7%ab%af%e5%a6%82%e4%bd%95%e9%ab%98%e6%95%88%e7%9a%84%e4%b8%8e%e5%90%8e%e7%ab%af%e5%8d%8f%e4%bd%9c%e5%bc%80%e5%8f%91/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>[汇总]Frontend资源</title>
		<link>http://www.mobabel.net/%e6%b1%87%e6%80%bbfrontend%e8%b5%84%e6%ba%90/</link>
					<comments>http://www.mobabel.net/%e6%b1%87%e6%80%bbfrontend%e8%b5%84%e6%ba%90/#respond</comments>
		
		<dc:creator><![CDATA[leelight]]></dc:creator>
		<pubDate>Wed, 07 Nov 2018 12:47:09 +0000</pubDate>
				<category><![CDATA[Theory & Solution]]></category>
		<category><![CDATA[Frontend]]></category>
		<guid isPermaLink="false">http://www.mobabel.net/?p=2374</guid>

					<description><![CDATA[<p>2018年最佳JavaScript数据可视化和图表库 &#160; 原来可视化能做得这么酷炫!Uber开源地理空间工具包kepler.gl &#160; 使用Rekit开发可扩展的前端应用 在面临大型、复杂项目的开发时,如何利用各种框架和工具来提高前端的开发质量和效率是大家关注的重点。 JS/CSS体积减少了67%,我们是如何做到的? Three.js实现一个360度全景插件 前端请装上这个 Chrome 插件ajax-interceptor &#160; &#160; &#160;</p>
The post <a href="http://www.mobabel.net/%e6%b1%87%e6%80%bbfrontend%e8%b5%84%e6%ba%90/">[汇总]Frontend资源</a> first appeared on <a href="http://www.mobabel.net">Mobabel</a>.]]></description>
										<content:encoded><![CDATA[<p id="activity-name" class="rich_media_title"><a href="https://mp.weixin.qq.com/s/mY6IhbGFOCd-veFP6_eD1Q">2018年最佳JavaScript数据可视化和图表库</a></p>
<p>&nbsp;</p>
<p id="activity-name" class="rich_media_title"><a href="https://mp.weixin.qq.com/s/ndzLKJC2Tvg2gKrr6jUNTw">原来可视化能做得这么酷炫!Uber开源地理空间工具包kepler.gl</a></p>
<p>&nbsp;</p>
<p id="activity-name" class="rich_media_title"><a href="https://mp.weixin.qq.com/s?__biz=MjM5MDE0Mjc4MA==&amp;mid=2651011714&amp;idx=3&amp;sn=7780db2e85cf8dd3942ecd05153e96e5&amp;chksm=bdbec2d18ac94bc736ce990d8f0726bd3544d99ff2fc914fbe72353a46ad3def7f383de0797b&amp;mpshare=1&amp;scene=24&amp;srcid=#rd">使用Rekit开发可扩展的前端应用</a><br />
在面临大型、复杂项目的开发时,如何利用各种框架和工具来提高前端的开发质量和效率是大家关注的重点。</p>
<p id="activity-name" class="rich_media_title"><a href="https://mp.weixin.qq.com/s?__biz=MjM5MDE0Mjc4MA==&amp;mid=2651011650&amp;idx=2&amp;sn=0b0013e3263977573b48414b6d9211df&amp;chksm=bdbec2118ac94b07b0d1dd0422ec9d93c64e25d8cbaddef5b7723fd7ea20d2d617b506d04afe&amp;mpshare=1&amp;scene=24&amp;srcid=#rd">JS/CSS体积减少了67%,我们是如何做到的?</a></p>
<p id="activity-name" class="rich_media_title"><a href="https://mp.weixin.qq.com/s/pKJBH4Ce68nfnmYbQlaSKw">Three.js实现一个360度全景插件</a></p>
<p id="activity-name" class="rich_media_title "><a href="https://mp.weixin.qq.com/s/S3kLeJEX46CcY3RlwAUZaw">前端请装上这个 Chrome 插件ajax-interceptor</a></p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>The post <a href="http://www.mobabel.net/%e6%b1%87%e6%80%bbfrontend%e8%b5%84%e6%ba%90/">[汇总]Frontend资源</a> first appeared on <a href="http://www.mobabel.net">Mobabel</a>.]]></content:encoded>
					
					<wfw:commentRss>http://www.mobabel.net/%e6%b1%87%e6%80%bbfrontend%e8%b5%84%e6%ba%90/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
	</channel>
</rss>

<!--
Performance optimized by W3 Total Cache. Learn more: https://www.boldgrid.com/w3-total-cache/

Page Caching using Disk: Enhanced (Requested URI contains query) 

Served from: www.mobabel.net @ 2024-12-04 01:06:49 by W3 Total Cache
-->