[转]京东单品页前端开发那些不得不说的事儿

京东单品页前端开发那些不得不说的事儿

07,31,2016 10:48 AMjavascript

简介

详情页也叫作单品页,域名以「item.jd.com/skuid.html」为格式的页面。是负责展现京东商品 SKU 的落地页面。主要任务是展现和商品相关的信息,如:价格、促销、库存、推荐,从而引导用户进入购买流程。同时单品页有不少版本。通常分为两类。一类咱们一般看到的「通用类目详情页」—— 全部类目均可以使用,一类是不常常看到的「垂直属性详情页」—— 一些有特殊属性的商品集合css

item version

首先。因为详情页大量(sku上亿)、高并发(日 pv 约 5000 万)等特性,在很长的一段时间里,单品页面都是后端程序生成静态页面使用 CDN 来解决大量、高并发的问题html

其次。单品页涉及的「三方」系统特别多,好比:促销、库存、合约、秒杀、预售、推荐、IM、店铺、评价社区。而单品页的主要任务就是展现这些系统的信息,而且适当的处理他们之间的冲突关系,而这些系统的接口通常都使用 异步 Ajax 来完成,由于 其一 CDN 没法作到页面的动态化,其二 一些系统的信息对实时性要求特别高(价格、秒杀),即便使用后端动态渲染也很难作到无缓存 0 延迟前端

基于上面两个缘由,注定了单品页是一种重多系统业务逻辑展现型页面。重前端页面。我大概汇总了一下页面上异步接口,总共约有 30 个,页首屏的接口特别重要,接口之间几乎都有耦合关系java

item-async-service

前端的发展历程

混沌时期

混沌时期的单品页并无前端开发的概念。核心的功能脚本只有三个:促销价格(promotion.js)、库存地区(iplocation.js)、其它逻辑(pshow.js)。这三个脚本分别是三个不一样团队的同事负责维护,当时我刚进入京东的时候在 UED 部门,负责页面脚本总体的维护工做和 pshow的开发。那时候我本身维护的 pshow.js 脚本压缩后只有 80 kb,全部的代码都是过程式的,没有任何使用模式和代码技巧,JS 最多也只被用来作个判断渲染 DOM。那时候的前端工做内容只在 UI 层面,写样式和一些交互脚本webpack

这个阶段给我最深入的感受是单品页后端模板不多维护(后端架构是最老的 aspx 版本)。大多数的改动都要用 JavaScript 去动态渲染。由于后端页面是一个生成器生成的。若是页面后端模板有改动那么就须要全量的生成一次,过程可能须要几个小时git

初见端倪

当我接手这个项目时恰好有一次大改版,就在这时候老大说页面上的脚本都要放在咱们手里维护。而后就是一大波的重构、重写。基本上 pshow 被重写了大概 80% 其它的由于业务逻辑的问题并无彻底重写,只是作了些代码层面的优化github

有一个模板引擎叫 trimPath,知道这个的估计都算老前端的了。最先的客户端 JavaScript MVC 模式表明做品,只到如今仍是使用。这个阶段像评价这种彻底异步加载的模块特别适合使用模板引擎来减小维护的工做量。这个时候虽然页面上的代码并不都是咱们写的,但基本上前端对页面的 JavaScript 有了控制权,接下来的事情就是寻找机会逐个优化web

这段时间是最痛苦的时候,维护的工做统一到前端。而后后端几乎没有变化,只是在一段时间将后台的架构从 aspx 过渡到了 java。本质上并无什么改变。前端却作了比之前更多的事情,也是在这个时候我接手了大量的维护工做(包含全站公共库的维护)使得我意识到了一些自动化、工程化方面的重要性,后文会主要讲解,顺便说下,那时候前端自动化工具 Grunt 刚面世,可是我本身却用的是 apache ant,不过不久就切换到了 Grunt 来构建项目apache

拨云见日

单品页不只重系统逻辑,也重维护

在这段时间里一方面有正常的维护类需求要作,一方面本身也不断的学习新知识为之后的改版作铺垫。不过就在这时单品页有历史意义的一次技改出现了 —— 单品页动态化技改。关于后端部分的改造细节能够去 开涛的文章 了解

总的来讲此次的改版后不少数据直接从后端读取,再也不从前端异步获取并且咱们也作过一些异步加载的优化,多接口 combo 从统一服务吐出给前端使用。这时前端就不用再为异步接口的加载时苦脑了,只须要专一系统接口的逻辑

随着此次技改,前端的代码也迎来了模块化的时代。咱们把全部的前端代码都进行了模块化而后基于 SeaJS 重写,配合 Nginx concat 功能实现了本地模块化开发,线上服务端合并

单品页前端模块的结构与划分

概览

first-screen-normal-module

上图能够看出,基本上最核心的模块都在首屏。每一个模块都有单独的一/多个脚本。代码行数(LOC)由 230+ ~ 1200+ 不等。一般来讲代码行数越多代码复杂性就越高,逻辑越复杂。很难想象「购买方式」这种只有一行属性选择功能的代码行数却 高达 1200 多行。其主要缘由就在于购买方式所在的系统和其它首屏核心系统(库存、促销、地址选择、白条)都有逻辑上的耦合

看着不错,然而在一个前端工程师眼里至少应该是这样的(我只取了一些典型的模块,并非所有):

first-screen-in-fe-eye

这就能够解释为何有的时候只是加一个很小的东西咱们都为考虑再三而后经过 AB 测试提取相关数据,最后后再进行决策。单品页的首屏能够说是寸土寸金

按什么维度划分模块

起初我按模块的属性划分,好比:核心、公共脚本、模块脚本。但用了一段时候之后发现这样划分在单品这种大型系统中并不科学,由于这样划分出来的代码只有划分的人知道是什么规则,其它人接手代码很难快速掌握代码架构,并且尤为在模块比较多的时候不方便维护

后来我尝试彻底以功能模块在页面上出现的位置维度划分。这样以来维护起来方便多了,须要修改某个模块代码只须要对照着图里面标识的模块信息就能轻易找到代码

总体核心模块

咱们按页面上的模块结构首屏划分出来这几个核心模块:

  • curmb - 面包屑
  • concat - 联系咨询相关店铺信息
  • prom - 价格促销信息
  • address - 地区库存选择,配送服务
  • color - 颜色尺码
  • buytype - 合约机购买方式
  • suits - 套装购买
  • jdservice - 增值服务
  • baitiao - 白条支付
  • buybtn - 购买按钮
  • info - 地区提示信息

项目的总体树形结构是这样的:

project-structure

模块内部结构

好比下面这个大图预览的功能,我所有放在一个文件夹里面维护,可是逻辑上的 JavaScript 模块是分离的,只是说文件夹(preview)就表明页面上的某一部分功能集合

module-structure

注意文件夹的命名有必定的规则:

  • 模块脚本与样式名必须同样
  • 须要制做 sprite 的图片统一放在 module/i 目录下面,生成的 sprite 图片也在其中
  • 生成的 mixin 在模块根目录下,便于其它样式文件调用

咱们再来看下自动生成生成的 __sprite.scss 是什么内容:

1
2 3 4 5 6 7 8 9 10 11 12 13 
/* __sprite.scss 自动生成 */ @mixin sprite-arrow-next {  width: 22px;  height: 32px;  background-image: url(i/__sprite.png);  background-position: -0px -30px; }  /* preview.scss 手动添加 */ @import "./__sprite"; .sprite-arrow-next {  @include sprite-arrow-next; } 

注意引用的 mixin 名称和咱们须要手动添加的样式类名一致。固然也能够直接生成一个类名对应的样式,可是灵活性很差。好比 hover 的时候是另一张图片就无法自动生成了

前端技能树

HTML

DOM 节点数

与重业务逻辑的页面不一样,重展现的页面通常具备很高的 DOM 节点数。好比京东首页,正常状况加载完页面一共有 3500 多个 DOM 节点,基本上所有用于展现商品信息、广告图和内容布局,页面上的三方异步服务也比较少。尤为像频道页基本上没有什么业务上的逻辑,所有是静态页面。这种页面的特色是更新换代频率高,一年两三次改版很正常,CMS 作模块化后两天换个皮肤都是没问题的。可是这种思路并不适合单品页。单品页更重业务逻辑,同时展现层 UI 逻辑也有不少关系

我本身的经验是:页面上的 DOM 节点数绝对不能超过 5000 个,不然页面滚动的时候就会出现卡顿的状况,尤为是移动端

同步渲染仍是异步加载

理论状况下最好作法是后端同步动态渲染页面,可是因为 Web 应用中不少功能都是用户行为驱动的。同步加载不可避免的消耗了后端服务资源。好比:非首屏模块(公共头尾、评价)、点击事件触发的 DOM 内容(异步 tab)

因此个人经验是:能放到后端作判断渲染的 DOM 就尽可能放在后端(尤为是首屏)。这样作的好处有四点好处

  1. 后端渲染页面相对稳定,不像前端 JavaScript 动态渲染 DOM,可能由于脚本报错或者不可用形成模块都没法展现
  2. 可访问性、SEO 及用户体验也比较好。不会产生脚本的渲染抖动问题
  3. 必定程度上减小了前端渲染页面的复杂性,减小前端代码复杂度
  4. 逻辑统一到一个地方维护起来也方便,并且后端应该为业务逻辑负责,前端应该为展现UI 交互负责

对于异步渲染的模块来讲,后端一般须要判断 「页面有什么元素」,以及元素之间的依赖对应关系;而前端须要专一于 「元素应该怎么展现」,UI 层面的交互以及模块与模块以前的逻辑关系

其实更多的时候 异步是一种没有办法的办法,也就是说异步是其它方案都解决不了的状况下才考虑的

外链静态资源

尽可能使用外链 CSS 和 JavaScript 资源,一方面便于缓存,减小服务同步输出的资源浪费。IE 6 里面会有一些可怪的 bug,好比有内联样式 style 标签的页面 A 若是在另一个页面 B 中的 link 标签中引用,那么这段 style 会在 B 页面也起做用

使用双协议的 URL

使用 // 来代替http: 和 https: 浏览器会自动适应两种协议的资源访问,兼容性较好。注意 IE 8 下使用脚本更新 src 为双协议时会出现 bug,建议使用 location.protocol 来判断而后作兼容处理

删除元素默认属性

好比 script 标签默认的 type 就是 text/javascript,若是 script 里面的内容是 JavaScript 时能够不用写 type。另外若是要在页面里面插入一段不须要浏览器解析的 HTML 片断时能够将 type 写成text/x-template(任意不存在的 type) 用于放置模板文件,一般用来在脚本中获取其 innerHTML 而无任何负做用

给脚本控制元素加上类钩子

在脚本中取页面元素使用 J- 前缀类名,与普通样式类分离。这样作会生成不少冗余的类名,但却很好的下降了样式和脚本的耦合,而且在重构和脚本职位分开团队里会是一条最佳实践

CSS

样式分类

全部页面只共享一个 sass Mixin,里面包含了基础的 sass 语法糖、经常使用类(清浮动、页面总体颜色字体等)

模块级的样式分为两类:

  1. 与脚本无关的公共样式,单独在模块文件夹中组织。好比:按钮、标签页。所有放在 common 模块中维护
  2. 与脚本相关的模块级样式,与对应模块脚本放在一块儿,能够引用 common 中的公共样式,但不能够被其它模块引用

雪碧图

关于雪碧图 我经验是:永远不要想把全部的图标拼合在一块儿。按模块而不是按页面去拼 sprite 更合理,更方便维护,而后配合构建工具自动接合生成样式文件才是最好的解决方案。固然若是你的页面比较简单,那这条规则并不适用。说到这个问题我就得把珍藏多年的图片拿出来 show 一把,用事实来讲明为何把全部图片都拼在一张图上就必定是对的

早期因为年轻笃信将全部的 icon 拼在一张图上才是完美的(图 1)

first-sprite

后来维护起来实在不方便,就把按钮所有单独接合起来。注意,当时的按钮都是图片,设计方面要求的很严格。加入购物车按钮作的也很是漂亮(图 2)

button-sprite

而后这些都不是最典型的,下面这个 promise icon 才是 (图 3)

promise-sprite

从图里面能够看到,这个功能在第一个版本的时候只有 7 个 icon,后来不断增长,最多的时候达到 77 个。以致于当时每周都会添加两个的频率

同时这个 icon 当时接合的时候技术上也有问题:不该该把文字也切到图片里面,主要缘由是早期 icon 比较少加上外边框样式对齐的问题综合选择了直接使用图片

后来我就以为这样是不对的。而后经过和产品的沟通,说明个人考虑以及新的解决方案后获得了认同。结果就是对图片不进行拼合,后台上传通过审核的不带文字 icon,文字由接口输出,而后在产品上作了约定:icon 最多不能超过 4 个,代码里也作了相应限制。这样就能保证页面上的请求数不会太多同时方便系统维护,问题获得了解决

适当使用 DataURI

这个在一些小图片场景方面特别适合,好比 1*1 的占位图、loading 图等,不过 IE 6 并不支持这种写法,须要的时候能够加上一些兼容写法:

1
2 3 4 
.ELazy-loading {  background: url(data:image/gif;base64,R0lGODlhKwAeAJEAAP///93d3Xq9VAAAACH/C05FVFNDQVBFMi4wAwEAAAAh+QQFFAAAACwDAA0AJQADAAACEpSPAhDtHxacqcr5Lm416f1hBQAh+QQJFAAAACwDAA0AJQADAAACFIyPAcLtDKKcMtn1Mt3RJpw53FYAACH5BAkUAAAALAMADQAlAAMAAAIUjI8BkL0CoxQtrYrenPjcrgDbVAAAOw==) center center no-repeat;  *background-image: url(//misc.360buyimg.com/lib/skin/e/i/loading-jd.gif); } 

关于兼容性

兼容性能够说是前端工程师在日常开发中花费很大量无心义工做的地方。关于兼容性我想说的是若是你不肯意去说服周围的人放弃或者让他们意识到兼容性是个不可能彻底解决的问题,那么你就得为那些低级浏览器给你带来的痛苦埋单

其实更好的办法是你和设计、产品沟通而后给出一种分级支持的方案。把每种浏览器定义一个级别。而后在开发功能的时候以「渐进加强」的方式。一般来说咱们的解决方案是在低级浏览器里面保证流程正常进行、模块可使用,但忽略一些可有可无的错位、不透明等问题,在高级浏览器里面须要对设计稿进行精确还原,适当的加上一些井上添花在细节。好比微小的动画、逻辑细节上的处理等

举个例子吧,下面这个进度条表示预定的人数,它是接口异步加载完才展现的。若是加载完就当即设置进度条宽度会显得生硬无趣,可是若是加上一点动画效果的话就好多了。然而问题又来了,若是加上动画那么逻辑上这个进度条应该是一点点的增长,对应的人数也应该是逐个增长。因而我就作了个优化,让人数在这段时间内均匀的增长。这个细节并非很容易被人发现,可是这种设计会让用户感受很用心并且有意思

pingou

JavaScript

javascript-exec-sequence

单品页的脚本加载/执行顺序:

  1. 等待页面准备就绪(DOM Ready)
  2. 准备就绪后加载入口脚本(main.js),脚本负责其它功能模块的调度,动态接合模块经过 seajs 的 require.async 方法异步调用
  3. 公共模块(common.js)负责加初始化全局变量并挂载到 pageConfig 命名空间
  4. 动态模块数组,这个是后端经过程序判断处理生成的一个模块名列表。通常只包含首屏须要加载的模块
  5. 后加载模块(lazyinit.js)初始化,这个脚本只作一些页面滚动才加载的模块事件绑定。当模块出如今视口内再使用 require.async 异步加载模块的资源及初始化

入口脚本

大体代码以下

1
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 
/** * 模块入口(1. 公共脚本 2. 首屏模块资源 3. 非首屏「后加载模块」) */ var entries = [];  // 页面公共脚本样式 entries.push('common'); // 页面使用到的首屏模块(后端开发根据页面不一样配置须要调用的模块) entries = entries.concat(config.modules); // 非首屏「后加载模块」 entries.push('lazyinit');  for (var i = 0; i < entries.length; i++) {  entries[i] = 'MOD_ROOT/' + entries[i] + '/' + entries[i]; }  if (/debug=show_modules/.test(location.href)) console.log(entries);  require.async(entries, function() {  var modules = Array.prototype.slice.call(arguments);  var len = modules.length;   for (var i = 0; i < len; i++) {  var module = modules[i];   if (module && typeof module.init === 'function') {  module.init(config);  } else {  console.warn('Module[%s] must be exports a init function.', entries[i]);  }  } }); 

注意模块路径中的 MOD_ROOT 是提早在页面定义好的一个 seajs path。目的是为了把前端版本号更新的控制权释放给后端,从而解决了先后端依赖上线不一样步形成的缓存延迟问题,配置脚本中只有几个定义好的路径:

1
2 3 4 5 6 7 8 9 
seajs.config({  paths: {  'MISC' : '//misc.360buyimg.com',  'MOD_ROOT' : '//static.360buyimg.com/item/default/1.0.12/components',  'PLG_ROOT' : '//static.360buyimg.com/item/default/1.0.12/components/common/plugins',  'JDF_UI' : '//misc.360buyimg.com/jdf/1.0.0/ui',  'JDF_UNIT' : '//misc.360buyimg.com/jdf/1.0.0/unit'  } }); 

还有一点,在测试环境的页面中版本号(上面代码中的 1.0.12 是一个全量的版本号)是后端从 URL 上动态读取的(使用参数访问就能够命中对应版本 item.jd.com/sku.html?version=1.0.12)。这样以来测试环境上就能够并行测试不一样版本的需求,并且互不影响。固然若是不一样版本的后端代码也有修改的话这样是不行的,由于后端代码也须要有个对应的版本号

不过咱们已经解决了这个问题。后端会在测试环境里 动态加载后端模板 而且能够作到版本号与前端一致。这样以来配合 git 方便的分支策略就能够同时并行开发测试多个需求,不用单独配多个测试环境。什么?你还在使用 SVN!哦。那当我没说过

事件处理模型

客户端的 JavaScript 代码基本上都是事件驱动的,代码的加载解析依赖于浏览器提供的 DOM 事件。好比 onload, mouseover, scroll 等

事件驱动的的模型特别适用于异步编程,而 JavaScript 天生就是异步,全部的异步操做行为都最终会在一个回调函数(callback)中触发

好比单品页中价格接口,加载完成后须要更新 DOM 元素来展现实时价格;地区选择接口加载完成后会更新配送信息、库存/商品状态等,伪代码以下:

1
2 3 4 5 6 7 8 9 10 11 12 13 14 
/* onPriceReady 和 onAreaChange 能够认为都是一个 Ajax 异步函数调用  * code 1 和 code 2 执行到的时间是不肯定前后顺序的  */ /* prom.js */ onPriceReady(function(price) {  // code 1  $('#price').html(price); });  /* address.js */ onAreaChange(function(area) {  // code 2  $('#stock').html(area.stockInfo); }); 

上面的两段代码分别在两个脚本中维护,由于他们的逻辑相对独立。早期并无关联关系。后来需求有变,他们之间须要共享一些对方的数据(切换地区后须要从新获取价格数据并展现)。可是物理上又不能放在一块儿经过使用全局变量的方式共享,并且它们都是异步加载接口后才取到数据的,并很差肯定谁先谁后(非要作到那就只能用全局变量双向判断)。因此这样并不能很好的解决问题,并且代码的耦合度会成倍增长

这时候咱们引入了一种设计模式来解决这种问题 —— 发布者/订阅者,咱们把这种模式抽象成了自定义事件代码来解决这一问题。这段代码是由 YUI 核心开发者 Nicholas C. Zakas 实现的。代码很简单,事件对象主要有两个方法 addListener(type, listener) 和 fire(event)

因而咱们重构了上面的伪代码:

1
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 
/* prom.js */ // 在代码中注册一个地区变化事件,获取变化后的地区 id // 而后从新请求价格接口并展现 Event.addListener('onAreaChange', function(data) {  getAreaPrice(data.areaIds) });  onPriceReady(function(price) {  $('#price').html(price);   Event.fire({  type: 'onPriceReady',  data: 'Any data you want'  }) });  /* address.js */ onAreaChange(function(area) {  $('#stock').html(area.stockInfo);   // 在地区变化后除了作本身该作的事情之外  // 触发一个名为 onAreaChange 的事件,用来  // 通知其它订阅者事件完成,并传递地区相关参数  // 这个时候在 onAreaChange Ajax 回调函数  // 中就只须要关心本身的逻辑,其它模块的耦合关系  // 交给它们本身经过订阅事件来处理  Event.fire({  type: 'onAreaChange',  data: area.ids  }) }); 

须要注意的一点是,必须确保事件先注册后触发执行,也就是说先 addListener, 再 fire

一些典型的性能优化点

基本上客户端的 JavaScript 性能问题都来自于 DOM 查找和遍历,在用于的时候必定要当心,可能不经意的一个操做就会损失不少性能,尤为在低端浏览器中。顺便多说一点,现代的 JavaScript 解释器自己是很快的,语言层面的性能问题不多遇到。DOM 查找慢是由于 浏览器给 JavaScript 访问页面提供的一套 DOM API 自己慢

  1. 缓存 DOM 查找,同时 DOM 查找不要超过 2000 个,低级浏览器会卡顿
  2. 不要使用链式调用 find,如:find('li').find('a') 而是 find('li a')
  3. 在切换元素显示状态的时候,若是元素不少。优先使用 show()/hide() 方法,而不是 css('display', 'block/none') 前者有缓存,后者会强制触发 reflow
  4. 给节点添加 data-xx 属性在存放一些数据,经过使用 jQuery 的 data('xx') 方法取更高效,减小 DOM 属性访问
  5. 高密度事件(scroll, mousemove)触发场景请使用节流方法
  6. 使用事件代理,而不是直接绑定。若是不肯定代码被调用次数,能够先解除绑定再绑定具备命名空间的事件处理函数
  7. 尽可能少用 DOM 动画,使用 CSS 3 动画代替

前端工程化

起因

前端工程化其实并非最近两年才有的概念。大约在 2013 年的时候 Grunt 问世的时候就已经有所涉及。这类打包工具主要的目的是自动化一些开发流程,我最先使用 Grunt 来构建代码的时候只解决了三个问题:

  1. 合并压缩优化样式脚本
  2. 上线完自动备份
  3. 单个文件打包到多目录(历史缘由一个文件线上的路径有两种,须要传两个目录)

当时我还在组内作过一个分享,有兴趣的能够去围观一下 Best Workflow With Grunt

其实这些工具出现的缘由是:当时前端领域的各类基础设施很缺少,而前端的工做内容又相对零散。工做时须要开不少的软件。再加上 JavaScript 语言自己也很弱,就连包管理这种基础的东西也没有内置,以致于模块化要经过一些第三方类库来实现,好比:RequireJS, SesJS

工具的重要性能够在我以前的一个分享中找到 前端开发工具系列

现状

现在前端工程的生态环境因为 NodeJS 的出现已经变得很好了。你能够根据本身的需求选一个合适的直接用到项目里面。像 Grunt, Gulp, browserify, webpack 等。不过要明白这些工具的出现从另外一方面证实了前端开发天生存在不少的问题:

  • HTML 从诞生到 HTML 5 以前几乎没有任何变化,DOM 性能天生缺失。因此才有了 Virtual DOM 这种东西
  • CSS 只是一门描述型的语言,没有变量、逻辑控制、语句。因此才出现了 Sass, Less 这种预编译工具
  • JavaScript 号称「高阶的(high-level)、动态的(dynamic)、弱类型的(untyped)解释型(interpreted)编程语言,适合面向对象(oop)和函数式的(functional)编程风格」的编程语言,可是语言自己有不少问题(ES 6 以前)。不适合大型项目的开发、没有一些高级特性的支持、同时被其它语言诟病的 callback 风格、单线程执行等。因此才出现了像 TypeScript, Babel 这种编译成 JavaScript 代码的语言

这些问题几乎都是历史性的缘由和兼容性因素形成的。做为一名好的前端工程师要看清楚现状,而后按本身项目的需求去定制一些前端工程化的方案,而不是随波逐流。

选择

其实如今本身开发一套前端工程化/自动化流程的成本已经很低了,你只须要学习一些 NodeJS 的知识,配合 NPM 包管理机制,随手就搞出一个构建工具出来。由于并不须要你去实现什么东西,全部的东西都有现成的包。脚本压缩有 UglifyJS,CSS 优化有 CSS-min,图片压缩优化有 PNG-quant 等等。你只须要想清楚本身要达到什么目的,解决什么问题就能够抄家伙本身写一套工做流出来

我本身的经历也从 Grunt, GulpJS 到如今自造轮子。本身根据需求开发出来一套集成的打包工具,有兴趣的能够去围观一下 Wooo

固然你也能够不用任何打包工具,本身写一些 NPM Script 来彻底定制化项目开发/测试/打包流程。我猜这也是为何如今相似 Grunt 再也不那么火,Gulp 迟迟没有发布 4.0 版本的缘由。写一个构建工具的成本过低了,并且这种集成的工具很难知足差别的开发需求。君不知已有人意识到了这一点么why-i-left-gulp-and-grunt-for-npm-scripts

程序、设计、产品

我始终认为程序、设计是为了产品服务的。好的产品是要重视设计的,好的(前端)工程师是要有一些审美素养

其实不少时候技术解决方案都是要根据产品的定位来设计的,了解产品需求之后才能定制出真正合适的高效的解决方案。比如前面讲到的那个 sprite 案例,若是一开始就和产品讨论好方案后来也不可能有那种失控的状况发生。在产品造成/上线前期能发现问题比上线后发现问题更容易解决

这部份内容和代码无关,就很少说了。然而早年我还有一次分享关于前端、改变

总结

关于单品页的前端开发本篇文章只是冰山一角,还有不少没有说起,每一个小东西均可以单独写一篇文章来分享。随后但愿能够有更多的总结和分享

相关文章
相关标签/搜索