Code Monkey home page Code Monkey logo

weidian-tech-blog's People

Contributors

hoperyy avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

weidian-tech-blog's Issues

微店技术周刊(总第 28 期) - 本期主编 - 刘远洋

微店技术周刊(总第 28 期) - 本期主编 - 刘远洋

Success isn’t about how your life looks to others. It’s about how it feels to you.

关于

全栈化技术周刊整理每周有趣有用有料的各类内容,为大家呈现多样的技术世界。

周刊编辑:@何会会 / @张尚金 / @杨力 / @赵兴 / @刘远洋

周刊招募投稿,主题不限,向社区发出你的声音吧!

见闻

如何写好函数

@罗炜 - 生产力平台

原文链接: luoway/blog#11

什么是好的函数?

这要从结果上来评价一个函数的好坏。先考虑写完一个函数,它有哪些结果?

  • 可执行

    这是最基本的,函数不能运行那就没有意义。

    保障函数可执行,要从两个方面考虑:函数本身逻辑、函数执行环境。函数本身逻辑可执行不用多说,函数执行环境是容易遗漏并出错的:函数如果接收参数,那么就要考虑参数的数据类型是否符合运行要求;函数如果调用外部变量、函数,就要考虑外部变量是否存在且符合要求,外部函数是否能正常工作。对这些情况的处理能力称为健壮性

    换个角度考虑,如果写的这个函数在程序中没有被调用过,那它就是应当删除的冗余代码,应当减少。如果这个函数被调用一次以上,它就是有价值的代码。如果被多次调用,那它就具备复用性,价值进一步提升。

  • 完成功能

    这是第二个基本,函数没有完成它该有的功能,那它的意义也是值得怀疑的。

    进一步考虑,如果函数没有完成被期望的功能,却干了别的出人意料的事,那它简直是老鼠屎,扰乱了程序的执行逻辑。

    提炼一下:“被期望的功能”意味着函数是有姓名的,在函数名中应当体现出来,这就是语义。函数不应当做出“别的出人意料的事”,这就是副作用,应当避免。

  • 可阅读

    衡量可阅读程度的名词,一般称为可读性。可读性是现代程序语言发展的根本,从二进制,到汇编等低级语言,到今天百家争鸣的高级语言,可读性一路攀升。按理说,高级语言的可读性已经远高于低级语言了,为什么编程时还要注意可读性?

    试想一下反面例子:Web前端如何保护代码资产?

    就目前客户端浏览器“三大件”HTML、CSS、JavaScript而言,保护代码资产是不可能实现的。所有的解决方案归纳为“降低可读性”,让人难以阅读,就一定程度上做到了保护代码资产,不让人理解进而进行修改和维护。

    相反地,提高可读性,就是为了方便自己或他人理解以及进行修改和维护。

由此,一个好的函数,它应当是

  • 可执行的,健壮的,冗余代码越少越好,复用性越高越好。
  • 完成功能,函数名是有语义的,说明了函数完成的功能,且没有副作用。
  • 可阅读的,方便再次理解、修改和维护。

怎样写好函数

本文以JavaScript为例,从健壮性、复用性、语义、副作用、可读性五个方面举例说明。

健壮性

坏的例子

function numberPlusOne(val){
  return val + 1
}

期望是对输入数字,返回数字加1后的结果。但如果输入的不是数字,而是数字字符串,或者是非数字的其他内容呢?

好的例子

function numberPlusOne(val){
  if(typeof val === 'string') {
    val = parseFloat(val)
  }
  if(typeof val === 'number'){
    if(!isNaN(val)) return val + 1
  }
  return NaN
}

如果有大数相加需要,还得进一步考虑JavaScript计算精度问题。

复用性

坏的例子

function formatProductPrice(productInfo){
  if(!productInfo) return productInfo
  if(productInfo.price){
    if(typeof productInfo.price === 'string') {
      productInfo.price = parseFloat(productInfo.price)
    }
    productInfo.price = isNaN(productInfo.price) ? '0.00' : productInfo.price.toFixed(2)
  }
  //复制粘贴得到下一段,并替换price为originalPrice
  if(productInfo.originalPrice){
    if(typeof productInfo.originalPrice === 'string') {
      productInfo.originalPrice = parseFloat(productInfo.originalPrice)
    }
    productInfo.originalPrice = isNaN(productInfo.originalPrice) ? '0.00' : productInfo.originalPrice.toFixed(2)
  }
  return productInfo
}

期望是格式化产品的两个价格字段price、originalPrice,两个字段处理方式一致。

好的例子

function formatProductPrice(productInfo){
  if(!productInfo) return productInfo
  formatPrice(productInfo, 'price')
  formatPrice(productInfo, 'originalPrice')
  return productInfo
}

function formatPrice(obj, key){
  if(!obj[key]) return
  
  let val = obj[key]
  if(typeof val === 'string') val = parseFloat(val)
  obj[key] = val.toFixed && val.toFixed(2) || '0.00'
}

复用性的基本内容就是避免重复代码。但在编程过程中,它应当是值得考虑的优化方案,而不是奉为圭臬的必须方案。提前考虑复用,结果由于各种原因没有被复用到,实际是没有提高复用性,反而可能降低开发效率。

语义

坏的例子

function add(a, b){
  return a + b
}

期望是计算两数相加(add)的结果,即求和(sum)。

好的例子

function sum(a, b){
  return a + b
}

那么add应当如何满足其语义呢?

Number.prototype.add = function(val){
  return this + val
}

let a = 1, b = 2
a.add(b)	//3

add语义是“增加”,sum语义是“合计”,意义是不同的。编程所需的语义,是建立在能够正确理解语言意义基础上的。所以说,程序员是需要学好英语的。
上例说明的是函数名的语义不恰当问题,编程中常见的问题是给常量、变量、字段命名,有时候还会纠结多个相似的值,如何区分命名。

副作用

//对象合并
const obj1 = { a: 1 }
const obj2 = { b: 2 }

function extendWithSideEffect(obj1, obj2){
  Object.assign(obj1, obj2)
  return obj1
}

function extend(obj1, obj2){
  return Object.assign({}, obj1, obj2)
}

期望是“对象合并”,两个函数都实现了对象合并,并返回合并后的对象。extendWithSideEffect的副作用是会改变输入参数obj1对象内容,在当前期望中是副作用,应当避免。

可读性

坏的例子

function oneDayOfWorker(){
  init()	//非常想吐槽的函数名init
}

function init(){
  leaveHome()
}
//假设以下行为均是异步的
function leaveHome(){
  doSomeThing(work)
}
function work(){
  doSomeThing(goHome)
}
function goHome(){
  doSomeThing(sleep)
}

好的例子

function oneDayOfProgramer(){
  leaveHome(()=>{
    work(()=>{
      goHome(sleep)
    })
  })
}

function leaveHome(callback){
  doSomeThing(callback)
}
function work(callback){
  doSomeThing(callback)
}
function goHome(callback){
  doSomeThing(callback)
}

更好的例子

async function oneDayOfProgramer(){
  await leaveHome()
  await work()
  await goHome()
  sleep()
}

function transformPromise(fn){
  return new Promise(resolve=>{
    fn(resolve)
  })
}
function leaveHome(){
  return transformPromise(doSomeThing)
}
function work(){
  return transformPromise(doSomeThing)
}
function goHome(){
  return transformPromise(doSomeThing)
}

这个例子主要说明的可读性问题是,避免“链式”编写函数,而应当以“总-分”的结构去组织函数。

设主函数为main,A、B、C、D是需要有序调用的子函数定义,a、b、c、d是子函数调用。

“链式”编写函数:

main[a], A[b]→B[c]→C[d]→D

描述为主函数中只调用开始的子函数,在子函数定义中去调用其他子函数,形成“链表”结构。代码读者需要逐个子函数地查看以理解主函数main的功能逻辑。

“总-分”结构组织的函数:

main[a→b→c→d], A, B, C, D

描述为主函数中描述了子函数调用顺序,子函数定义各自实现功能。代码读者可以根据主函数main,结合子函数名的语义理解功能逻辑。

上面的问题是一种影响可读性的典型问题。可读性需要注意的问题不止一种,还有些问题可能存在争议需要统一意见,因此有着“代码风格”之说,不同风格有差异也有共同之处,多做了解和比较,整理出自己心目中的最佳实践吧!

结束语

“如何写好函数”是一个偏主观的话题,在编程实践中程序员们积累了大量客观的评价指标,其中有些指标可能是相互制约的,例如复用性、可扩展性、可读性,三者就不容易共同提高。所以这类问题鲜少有“最佳实践”的讨论。

但是,写好函数的重要性是不言而喻的。“编程一时爽,重构火葬场”,坏的函数要么影响程序员上班的心情,要么提前下次重构的计划到来,两者都不是什么好事。何以解忧?唯有换行。嗯,换行是有条提升可读性的代码风格规范。

反观自身,如何评价自己的代码好不好?笔者的建议是,阅读当前编程语言最流行的一些框架、库的源码,阅读过程中去思考如果自己来写,能不能写得更好。本文正是读源码过程中有感而发。

微店技术周刊(总第 31 期)-本期主编-何会会

微店技术周刊(总第 31 期)-本期主编-何会会

祝大家中秋快乐!

关于

微店技术周刊整理每周有趣有用有料的各类内容,为大家呈现多样的技术世界。

周刊编辑:@何会会 / @张尚金 / @杨力 / @赵兴 / @刘远洋

周刊招募内容作者,主题不限,发出你的声音吧!

周刊同步在 微店技术团队 github,欢迎关注:https://github.com/weidian-inc/weidian-tech-blog

技术分享

工具资料

编程之外

一窥两百年后的世界:23世纪概览

一、世界

概述

23世纪世界的主流趋势仍然是和平及商业,人类-安立柯帝国-翡翠文明三方的恒星际贸易联系维持着经济的繁荣,而这种繁荣已经持续了超过一个甲子。 安立柯帝国对地球特产葡萄酒、巧克力和机器人的庞大需求拉动着人类世界六国集团的经济,而六国集团内部的市场需求和太阳系殖**义也拉动着地球剩余国家的自然资源和人力资源的出口。这就是所谓的第一拉动和第二拉动,维持着人类文明的繁荣。

但是,表面的和平与繁荣掩盖不了隐藏的矛盾和冲突。六国集团内各国对殖民地的争夺愈发激烈;被锁死在地球的南方国家对六国集团垄断太阳系殖民和星际贸易日益不满;而在殖民地的新世界,殖民者与匪帮之间围绕据点不断上演旷日持久的拉锯战。发生在全太阳系范围的局部冲突或小型战争层出不穷,而谁也不知道这些矛盾、冲突和零星战火会将23世纪的世界带往何处。

种族和文明

【人类文明】**、美联、欧罗巴、英国、斯拉夫和以色列此六国在人类政治经济生态链中居于高级地位,他们毫无疑问地、强有力地控制着太阳系的每一寸空间,没有此六国的应许,其余国家的企业或公民无法在太阳系随意开展业务和迁移,更遑论通过星门前往安立柯星系。太阳系殖民浪潮的确波澜壮阔,但与六国集团之外的人们无关。

**

在第一次接触后引发的科技浪潮推动下,**率先于21世纪中后期启动了对月球和火星的殖民计划,在22世纪上半页完成了工业基地月球化,并与美欧英斯以一同执行了致力于将火星地球化的“盖亚计划”,而在整个22世纪**企业在太阳系中兴建了大量空间站和太空城市,这些持续一个世纪以上的投资、开拓和基础设施建设都支撑着**人在23世纪成为数量最多的人类地外殖民者。查看**完整词条

北美联邦

在21世纪中叶的北极战争后,美联吸取了美国的教训:**阶层纵容利益集团绑架政府实行帝国主义政策从而导致战线过长和树敌众多,最终导致内部矛盾被海外战争激化而招致社会撕裂和内战。从而,美联的国策在此后一百多年都维持着某种程度的孤立主义,“北美洲是北美洲人的北美洲,至于其他地方?谁在乎?”这种观点一直保留在22世纪美联人的头脑中。不过,在太阳系殖民浪潮开始后,美联人前辈当年拓荒西部的精气神全部得到唤醒,北美人开拓地外殖民地的劲头毫不亚于**人。查看北美联邦完整词条

欧罗巴合众国

欧罗巴通过与俄罗斯的合并将领土范围向东扩张到了太平洋海岸,但不久后就将叶尼塞河以东领土全部卖给了**,并最终将东部边界固定在乌拉尔山脉一带。欧洲大陆紧接着在21世纪40年代爆发了惨绝人寰的七年动乱,基督徒在欧罗巴军政府和天主教世界的支持下大量屠杀和驱逐欧洲的穆斯林,最终将后者全部赶到西撒哈拉。此后几十年,欧洲大陆终于迎来了安宁,高压的提高生育率政策提高了人口基数,统一的市场带来了经济潜力的持续释放,欧罗巴赶上了第一次接触带来的技术爆发,参加了盖亚计划,投入到太阳系殖民浪潮之中,这一切都令西方文明的核心地带在23世纪成为人类最强大国家之一。查看欧罗巴完整词条

英国

英国人在21世纪末重新整合陈旧的摇摇欲坠的英联邦体系,将这个共主邦联组织彻底改革成一个邦联制国家,用英镑统一了成员国的货币,将来自各个成员国的代表选入伦敦的议会和政府,英军重新部署到这些成员国并作为防务主力,白厅成为成员国的外交、国防、贸易和教育政策制定者。英国随后在以色列出售可控核聚变能源技术给其余几个大国的过程中继续发挥协调人的角色,在22世纪的盖亚计划和太阳系航运竞赛中,英国人愈发焕发出数百年前的活力并成功取得一些成就,这直接推动英国人将其帝国时期的海洋战略放在太空中实施,他们倾举国之力建起了舰队和运输舰队,到了23世纪初这些英国太空船逐渐成为了太阳系内的一支强大力量并掌控了一些关键航道。查看英国完整词条

斯拉夫共和国

在一小撮政治野心家和几名民族主义资本家的领导和支持下,在欧俄合并后诞生的俄罗斯复国主义武装入侵格陵兰并成功在此建立了“新俄罗斯”。在21世纪50年代的北极战争后,新俄罗斯的领袖决定担起泛斯拉夫主义的旗帜,号召全球——主要是东欧和南欧的欧罗巴加盟邦中的斯拉夫人和对新政权心怀不满的俄罗斯精英来到格陵兰参与建立一个泛斯拉夫主义国家“斯拉夫共和国”(Slavyanskaya Respublika)。此后一百多年间,斯拉夫通过援助马尔代夫建立人工陆地的方式,获得了在赤道的重要航天发射场,并参与了第一次接触和盖亚计划,在22世纪后凭借航天领域的积累和政策支持,与英国人展开了太阳系航运竞赛,为南方国家提供防务服务,并在23世纪跻身六国集团之列。查看斯拉夫完整词条

以色列国

21世纪20年代被伊斯兰联军击败并丢失国土的以色列国内各种矛盾爆发,经过多年混战后军政府上台收拾局面。军政府在部分美国犹太集团和欧洲犹太人的支持下稳定了以色列政局,并策划了将持续五十年的“救赎计划”,计划除了做出重建国家的总体规划外,还明确要求哈瑞迪派必须世俗化并为以色列重建提供合格劳动力否则将被集体流放到非洲,同时计划也明确了军政府为以色列国的政体。 此后以色列逐渐恢复活力,在21世纪中叶与中美欧英斯一同获取了第一次接触的科技成果,也作为参与者投入了盖亚计划,并在21世纪末成功开发出可控核聚变中的关键约束技术。以色列将这个能源革命的钥匙卖给列强后获得巨额资金,军政府顺势将救赎计划再延期一百年,以便集**力对小行星带的谷神星地球化改造。在22世纪中叶,谷神星被改造成了“迦南行星”,以色列人开始陆续从马达加斯加迁移到这个全新的星球,直至23世纪初已有85%以上的以色列公民生活在迦南行星。查看以色列完整词条

其余有一定影响力的区域大国还包括:伊斯兰世界联盟、太平洋共和国、墨萨克斯共和国、日本、巴西、西非联邦。此外,一些防务巨头和国际组织也是23世纪人类世界的主要玩家,包括:维索卡、联合地球、地球儿女等。

**【安立柯文明】**数千年来,神权政治下的安立柯帝国皇帝将土地分封给七个最大的家族,这些家族又各自控制着数量庞大的地方贵族领主,让他们在自己的领地里建立军队和**。人类在安立柯帝国境内各行省的贸易和探索活动不断增多,通过取悦掌控行省的大家族和贿赂领主,人类商队和探险队在帝国境内的活动为人类的葡萄酒、巧克力和机器人出口创造着源源不断的需求。

皇帝依靠庞大的地方贵族领主,让他们在自己的领地里建立军队和**,全帝国范围内一共有约1200个领主,而他们绝大部分从属于七个最大的家族,皇室从而以一种低成本的方式建立起了金字塔般的**基础。

安立柯帝国的核心美仑奇斯行星距离地球约两千光年,作为一颗气态巨行星的卫星,这个行星的体积是地球的三倍,南北半球拥有辽阔的大陆,赤道附近是连绵不绝的浅海——安立柯人就是从浅海中的一种鱼类进化而来,而他们的绝大部分生活区域至今也是在浅海和大陆的海岸地区。

**【翡翠文明】**较之人类和安立柯,翡翠是一个十分古老的文明,自从人类在21世纪中期历史性地与翡翠文明进行第一次接触后,这个古老文明在21世纪末作为中间人安排了人类与安立柯两大文明的交往。翡翠作为比人类和安立柯更高级的文明,掌控着连通太阳系与安立柯星系的星门,但却与这两个文明保持着若即若离的关系。

以下是根据“涅槃计划”中的材料翻译得出的关于翡翠文明的部分概况,注意这只是人类翻译的版本,是否与事实相符则未得到翡翠文明的明确答复。

翡翠文明原本只是庞大翡翠星系中的一个行星文明,在五万年前崛起并逐步对星系中的其他文明发动**,一个不断扩张的帝国也随之建成。伴随着历史长河中的无数次分裂和战争,翡翠帝国的生产力已经提高到整个星系最高的水平,其人口也位居星系之最,其他文明种族要不是处于奴役状态就是已经消亡。大约在两万年前,翡翠文明成为了该星系占据绝对主导地位的种族并发展到生产力超高的阶段,物质达到极大的丰富程度,翡翠人无需付出劳动即可获得无限供给的物质,翡翠社会的共识是深度开发每个人的精神世界。翡翠文明在23世纪的两大使命分别是发展大功率恒星级探测器来探索整个银河系的文明分布状况、以及展开进阶哲学研究。

从来没有人类见到过翡翠人的真实面目,因为他们在每次与人类接触之时都会将其身体和外貌展现为该人类所在种族的样子,他们的说法是这样能最大程度上尊重双方的自然基因。目前的主流观点是翡翠文明对人类没有恶意,且比较热心地向人类分享其高超的科学技术,动机可能是为了提高人类在整个银河系中的文明程度,进而达成翡翠文明的某种使命。

科学和技术

23世纪人类文明的工业和社会生产主要依靠超级核聚变和空间太阳能提供能源动力。此时,人类普遍从安立柯帝国进口一种称为“精炼源”的矿物质来提高超级托卡马克核聚变过程中约束等离子磁场的稳定性。从而实现持久可控的核聚变并继而以此作为大型机械和城市的主要发电方式。太空大国普遍以核聚变能源作为其主力能源。另一方面,几个大国及其企业在环绕地球的轨道上设置大量太阳能板接收太阳能,在空间站转换为电能后用无线输电技术发回到地面上的电能接收站,再通过电网送到各个使用终端。空间太阳能的使用范围主要在地球与月球殖民地。太空城、地球上的小国和资源匮乏国家是空间太阳能的主要服务对象,太空大国为其在轨道上建立电站然后将电能传输到这些国家的城市。而出于地缘政治考量,大国本身不会过多使用空间太阳能。

人类从22世纪开始进入了太空殖民时代,太阳系、南门二、安立柯星系、翡翠星系是人类足迹已到的几个恒星系。不过,后两个星系由于是外星文明的势力范围,因此人类仍无法对其殖民和开发,而只是拥有航道通行权。人类的殖民地和空间开发主要仍在太阳系和南门二。其中以月球和火星的开发最为成熟,这两个地方已经遍布人类殖民点和城市,环木星轨道、土卫六、小行星带和柯伊伯带也渐渐吸引了大量人类殖民者前往开发。值得一提的是,以色列人更是把谷神星直接地球化后改造成了迦南行星。而南门二则只有潘朵拉行星上有殖民据点。除了行星,还有大量空间站和太空城市充斥着太阳系,其中最大的是柯伊伯带星门附近的边缘港,人口已达两百万以上。

23世纪人类飞船的主流推进方式主要是核聚变、光压、磁压这三种推进系统。由于恒星际间的旅行依靠星门作为枢纽,因此飞船并不需要装备支持超长时间航行以及近光速级别的推进系统。

机器人三定律在23世纪发挥了重要作用。不过由于对安立柯出口机器人的利润更高,这反而导致人类的机器人产能大部分配置在生产对安出口型机器人上,留给人类世界消费的机器人供应并不十分充裕。

出于对AI和硅基文明的潜在崛起的担心,人类各主要国家都除了对AI的使用实行分级机制外,还立法限制机器人公司或相关运营机构私自提高AI及机器人的能力,而且也不允许AI擅自联网。

货币和金融

人类文明的货币系统是商品本位占绝对主导,世界各国政府都基于黄金和克里普(KREEP)的混合物质(比例为1比1)作为货币的价值,货币形式是电子货币形式,由各国**银行管理的大规模分布式服务器阵列作为电子货币的发行基础。而与之相反,安立柯帝国的货币系统是信用货币星元,星元的发行并没有真实的商品作为基础,它在人类、翡翠文明、安立柯帝国三边贸易中被确立为基础货币的无锚定信用凭证。

由世界各国入股的国际货币基金组织(IMF)在全人类范围内发行信用点(Credit Point, 简称CP),CP以一揽子货币为基础,包括:人民币、欧元、联邦美元(Federal Dollar,简称FEDA)、第纳尔、英镑和高级谢克尔(Senior Sheqel)。人类文明范围内的贸易以及与IMF的结算都以CP为基础货币。

商业和社会

23世纪的人类商业组织主要分为三种:作坊、行会、超级企业。

作坊是23世纪最普遍的商业组织形态,某个公民或家庭依靠3D打印设备、机器人和互联网便能成立作坊。作坊可以为市场提供各种商品或服务,但受限于竞争力太弱和精力有限,作坊往往无法涉足除本地社区外的更大市场。因此,作坊往往会选择加入某个行会,由后者负责其产品的批量生产、统一销售和大规模市场营销,作坊本身只负责创意和生产。

行会是作坊的联合体,它代表某个地区或者行业众多作坊的利益。大型行会的形态更类似于某种卡特尔,它们往往有一定实力在商业层面上与超级企业抗衡,但受制于没有私人武装和政治影响力,行会无法在更高层面上与超企对抗。行会往往被视作二十世纪大企业的翻版,但二者仍有很大不同:一,行会不直接从事产品制造和服务业务;二,行会没有庞大的员工福利压力需要承担;三,行会没有股东和董事会,只有一个附属的共同基金和合伙人团队,而后者也分为普通合伙人和有限合伙人。行会一般有如下特点:一,往往以行业和地区注册;二,作坊有随时加入和退出的权利;三,行会议会有强大权力随时干涉合伙人的决策。

超级企业类似于近代的康采恩,它们往往面向庞大的海外殖民市场,拥有私人武装和强大的政治影响力。超企的业务主要集中于需要庞大资本和劳动力,作坊和行会无力涉足的领域,例如矿业、重型制造业、金融业和航运业等。超企内部的组织结构与二十世纪的组织结构比较接近,呈金字塔形且等级分明,不同之处在于机器人和AI系统在超企中被广泛应用。超企有以下特点:一,资产庞大;二,业务覆盖广阔的市场;三,拥有私人武装;四,在殖民地普遍拥有政治权力(例如制定法律、收税和司法仲裁等);五,政府一般拥有其比例不等的股权。查看人类超级企业榜

从21世纪末开始,六大国就在太阳系积极奉行领土扩张政策,地球化成功的几个外星球(尤其是火星)都纷纷成为这些大国的开拓处女地,这构成了太阳系殖**义的核心。有几个原因支撑着太阳系殖**义:一,领土是国家利益的基本保障,外星球上的金属、矿产和水源等都是重要战略资源;二,领土扩张需要大量人口进行开拓,这能创造大量就业机会,尤其是提供给社会中下层人口;三,领土扩张后能容纳更多人口移民,间接扩大了民族发展空间,也有利于提高生育率。为了激励社会中下层成为拓荒者前往殖民地开拓空间,六大国都出台了力度不一的鼓励政策,最常用的就是拓荒者可以获得土地或获得大部分的土地开发收益,拓荒者也可以将已经开拓到一定规模的土地卖给后来的殖民者或企业从而实现套现。

>查看原文

微店技术周刊(总第 33 期)-本期主编-刘远洋

微店技术周刊(总第 33 期)-本期主编-刘远洋

Less/No Code 让业务跑的更快

关于

微店技术周刊整理每周有趣有用有料的各类内容,为大家呈现多样的技术世界。

周刊编辑:@何会会 / @张尚金 / @杨力 / @赵兴 / @刘远洋

周刊招募内容作者,主题不限,发出你的声音吧!

周刊同步在 微店技术团队 github,欢迎关注:https://github.com/weidian-inc/weidian-tech-blog

分享

终端性能优化杂谈

@生产力平台技术部 - 刘远洋

原文链接:hoperyy/blog#147

性能优化的理论方法

常用工具

  • Chrome 自带的 Dev Tools
  • Google 推出的 PageSpeed Insights
  • Lighthouse

Browser Processing Model

image

术语

  • redirect: timing.fetchStart - timing.navigationStart
  • dns: timing.domainLookupEnd - timing.domainLookupStart
  • connect: timing.connectEnd - timing.connectStart
  • network: timing.connectEnd - timing.navigationStart
  • load: timing.loadEventEnd - timing.navigationStart
  • domReady: timing.domContentLoadedEventStart - timing.navigationStart
  • interactive: timing.domInteractive - timing.navigationStart
  • ttf: timing.fetchStart - timing.navigationStart
  • ttr: timing.requestStart - timing.navigationStart
  • ttdns: timing.domainLookupStart - timing.navigationStart
  • ttconnect: timing.connectStart - timing.navigationStart
  • ttfb: timing.responseStart - timing.navigationStart
  • firstPaint: timing.msFirstPaint - timing.navigationStart

核心指标

  • 非视觉指标(Non-Visual Metrics)

    • 首字节时间(用于衡量网络链路和服务器响应性能)
    • 白屏时间(firstPaint)
    • 可交互时间(interactive)
    • 完全加载时间(load)
  • 感官指标(Visual Metrics)

    • First Paint Time (First Non-Blank Paint Time)

      文档中任一元素首次渲染的时间

    • FCP: First Contentful Paint Time

      代表文档中内容元素(文本、图像、Canvas,或者 SVG)首次渲染的时间。

      它通常情况下是无意义的渲染,比如头部和导航条。

    • FMP: First Meaningful Paint Time

      首次有意义的渲染时间(它的统计在重大的布局变化之后,往往代表了用户所关心的首次渲染时间)。

    • First Interactive Time

      首次可交互时间

    • Consistently Interactive Time

      持续可交互时间

    • Fisrt Visual Change

      首次视觉发生变化的时间点

    • Last Visual Change

      最后一次视觉发生变化的时间点

    • Speed Index (SI)

      视觉速度:Mean Pixel-Histogram Difference 算法。

      算法如下,它代表了我们页面在加载过程中视觉上的变化速度,其值越小代表感官性能越好:

    • PSI: Perceptual Speed Index(PSI)

      视觉速度:Structural Similarity Image Metric 算法。

      更贴近用户的真实感受。

重点指标

  • 非视觉指标(Non-Visual Metrics)

    • 首字节时间(用于衡量网络链路和服务器响应性能)
    • 白屏时间(firstPaint)
    • 可交互时间(interactive)
    • 完全加载时间(load)
  • 感官指标(Visual Metrics)

    • FCP
    • FMP
    • PSI

落地工具

  • 开发阶段

    • Chrome 自带的 Dev Tools
    • Lighthouse
    • 同时借助非视觉指标(Non-Visual Metrics)和视觉指标(Visual Metrics)进行分析
  • 生产状态

    • 用研:眼动仪、用户沟通、用户反馈、调研问卷、专家评估,缺点是无法量化
    • 自研:跨平台对标分析

优化策略

  • 纯前端离线化(在浏览器中通过纯前端的手段进行资源文件的离线化)
  • 客户端离线化(在客户端容器内通过离线包的方式实现资源文件及页面的离线化)
  • 页面组件化并按需加载(通过组件化方式对页面细粒度拆分并按需加载)
  • 预渲染提升感官性能(在框架启动之前,通过预渲染的方式确保页面框架最快呈现)

lighthouse 分析

常用性能监控方案

  • 全量
  • 抽样

参考资料

微店技术周刊(总第 27 期)-本期主编-张尚金

微店技术周刊(总第 27 期)-本期主编-张尚金

别人的看法都是狗屁,你是谁只有你自己说了才算 --《哪吒之魔童降世》

关于

微店技术周刊整理每周有趣有用有料的各类内容,为大家呈现多样的技术世界。

周刊编辑:@何会会 / @张尚金 / @杨力 / @赵兴 / @刘远洋

周刊招募内容作者,主题不限,发出你的声音吧!

周刊同步在 微店技术团队 github,欢迎关注:https://github.com/weidian-inc/weidian-tech-blog

分享

编程之外

  • 程序员工作法(推荐者: @张尚金)

    掌握主动权,忙到点子上,8 条程序员工作法助你职场上的我们

  • 应急响应实战笔记(推荐者: @张尚金)

    面对各种各样的安全事件,我们该怎么处理?这是一个关于安全事件应急响应的项目,从系统入侵到事件处理,收集和整理了一些案例进行分析。

  • 提升网站体验的一些设计思考(推荐者: @张尚金)

    良好的用户体验能够留下更多的用户,所以我们应该为追求好的体验而不断努力改进

  • 一些能够提供设计效率的 sketch 插件汇总(推荐者: @张尚金)

    好的工具真的能大大的提升工作效率,这里汇总了一些很有用的 sketch 插件,比如可以快速的从 Unsplash 里面选取照片用于 UI 设计等

投稿

【思考】微店前端工程化的迭代史.md

@刘远洋 - 生产力平台

原文链接:hoperyy/blog#145

正文部分

微店前端工程化起步于一个内部产品 vbuilder,对外我们有一个开源版本 bio-cli

去年我们也写过一篇文章介绍该产品: bio: 一站式前端开发工具

这么长时间过去了,我们在前端工程化方面有了哪些变化、遇到了哪些问题、用怎样的方案解决这些问题等等,值得为大家再分享。

V0.0

这里也就是介绍下背景,为什么我们会开发 vbuilder。

总体思路就是:将重复性工作集成化。

当时,团队面临几个问题:

  • 重复:每个项目要新开一个脚手架(webpack / gulp 之类)
  • 混杂:工程目录既包含脚手架文件,也包含业务文件
  • 混杂packge.json 中的依赖既有脚手架的依赖,也有业务依赖,难以区分
  • 难更新:脚手架一旦确定,几乎不再更新,如 webpack 1.0 的项目极有可能会一直维持在 webpack 1.0 状态
  • 协作:团队协作中,项目的技术栈纷杂,不同人员维护同一个项目成本高昂,如:需重新理解对应工程配置等

总结为下图:

image

基于以上问题,我们开始了 vbuilder 的研发。

最终产品以命令行的形式发布。

此时的 vbuilder 为 V0.0 状态。

V1.0

vbuilder V1.0 提供了以下能力:

  • 默认命令集:内置一套命令集,用于常见功能开发,包括 mock / update / help
  • 静默更新:用户安装一次命令即无需关注更新,其更新自行静默完成
  • 收敛脚手架:将工程内的脚手架配置隐藏,并统一管理,开发者可快速聚焦业务逻辑
  • 开放接入脚手架:不限制技术栈类型(vue / react / angular / weex 等),开放接入不同技术栈
  • 插件化:除内置命令集外,插件化扩展命令集,供团队同学实现订制逻辑

vbuilder 的不断推进下,我们欣喜地看到,团队发生了一些变化:

  • 便捷:新项目一个命令即创建,直接开始业务开发
  • 纯粹:工程目录只保留了业务文件,脚手架等工程配置被隐藏
  • 更新:脚手架被收敛为统一管理,统一更新,尽可能应用最新的技术栈
  • 协作:绝大部分项目协作的成本范围收敛到 “业务逻辑”,剔除了 “工程配置逻辑”,协作成本大大降低
  • 开放:在收敛脚手架配置的同时,开放性接入各类技术栈脚手架,如 weex / vms / 后台管理 / serverside project
  • 协作:团队统一性的技术更新得以快速进行,不会再遇到因工程配置不同不断适配的问题

总结为下图:

image

V1.0 出现后,推进的很顺利,在推进过程中秉持如下原则:

  • 提效:帮助业务开发者节省时间
  • 共担:开发者参与生态建设(脚手架开发维护、插件开发),至少在绩效上会得以加分
  • 好用:使用方式简单好用,才让人有用的欲望

V1.0 基本解决了以下角色的痛点:

  • 后端同学:内部系统开发场景被 100% 覆盖
  • 前端同学:绝大部分业务场景被覆盖
  • 脚手架开发者:强大的脚手架被开发好后,得以快速推广
  • 插件开发者:自定义命令,满足个性化开发

V1.0 的问题

  • 封闭性

    高度定制化的工程配置需求实现难度增大

    脚手架配置的主题被隐藏,虽然仍然开放给开发者一些配置性文件,对于高度定制化的配置需求而言依然杯水车薪。

    此时,就必须新开一个脚手架,重新接入 vbuilder 体系。

    在 “开放性” 来说,打了折扣。

  • 插件开发的冲突

    由于 vbuilder 是基于命令行开发,插件开发者扩展自定义命令式,依然是自定义命令行,团队规模不断扩大的状态下,很容易出现不同插件使用同一个命令,被同时安装的状态下,重复执行该命令。

V2.0

V2.0 至少要解决 V1.0 存在的问题,同时需要有更明确的发展方向。

不过,V2.0 依然基于命令行。

V2.0 如何解决封闭性问题

V1.0 的思路是 “闭合”,虽然有一定的开放性,但仍然不够。

V2.0 新增 “开放” 的能力,脚手架配置可以被隐藏,也可以随时在需要的时候暴露在工程配置中,进行定制化开发。

当然,会遇到脚手架难以统一管理的问题,这一点仍然有办法可以解决。

因为被暴露的工程配置是 vbuilder 提供的,vbuilder 得以方便地统计哪些项目使用了自定义的脚手架,将通用型工具包下发给该工程。

V2.0 如何解决插件开发的冲突

  • 问题 1:插件间的冲突

    举个例子,有两个插件中,都有一个命令 run。如果用户安装了这两个插件,在执行 run 命令的时候,两个插件的逻辑均会触发。

    在某些情况下,这不是用户希望看到的场景,可能 TA 希望的只是运行插件 A 的命令 run

  • 问题 2:插件命令集与内置命令的冲突

    例如,内置命令集中有命令 init,而某个插件也有 init

    那么在用户执行 init 命令时,依然会执行两遍逻辑。

  • 怎样解决?

    我们组合使用了以下方案:

    • vbuilder 检测是否有重复命令,如有,提示用户是都运行、还是选择运行某一个插件中的命令

    • 为命令圈定生效条件

      vbuilder 的命令行基于 commander。我们基于 commander 扩展了一些方法。

      假如我们希望,插件中的命令 show 只在工程目录中 xx.show 文件存在的情况下生效,那么代码如下:

      commander
      .command('show [param]')
      .effect(cwd  => fs.existsSync(path.join(cwd, 'xx.show'))) ---- 这是我们扩展的命令
      .description('我的自定义命令')
      .action((param, options) => {
      	console.log('my show');
      });
    • 为内置命令集声明其为“内置命令”,插件命令可以阻止内置命令执行
      假如插件中有个命令 init,而 vbuilder 内置命令中也有 init,我们希望插件中的 init 命令生效,内置命令不生效,该怎么做呢?

      我们扩展了 commander 的 2 个方法:declareDefault 声明内置命令、preventDefault 阻止内置命令执行。

      定义内置命令时,代码如下:

      commander
      .command('init [param]')
      .declareDefault() --- 声明内置命令
      .description('内置的 init 命令')
      .action((param, options) => {
      	console.log('init inside');
      });

      开发插件命令时,代码如下:

      commander
      .command('init [param]')
      .preventDefault() --- 阻止内置命令执行
      .description('内置的 init 命令')
      .action((param, options) => {
      	console.log('init inside');
      });

      Commander 的源码只有 1000 行左右,逻辑还是很清晰的,扩展起来非常方便,这里不再列举实现。

V2.0 的新功能

在命令行这个场景下,我们把 vbuilder 定义为公司内部开发的一个“水电煤”性质的基础设施。

通过 vbuilder,我们新增了以下场景:

  • 支持 chrome 插件 es6/7 化开发
  • 支持组件库快速开发
  • 支持 js 工具库快速开发
  • 支持快速打开文档库等等

得益于插件化,通过充分调动开发者积极性,我们可以将其能力无限延展。

V3.0

我们目前还没有进入 3.0 的开发,但有一些方向是我们可以尝试的:

  • cloudIDE(内部已有该类平台)
  • vscode 定制化 IDE,该类场景在超大型团队比较适合,IDE 定制化开发有更多的应用场景,更快的开发效率
  • 云化(其实不算新了,很多公司已经实现了云化)

这是目前我们在微店前端工程化领域的一些实践和思考,希望对大家有帮助。

微店技术周刊(总第 30 期)-本期主编-张尚金

微店技术周刊(总第 30 期)-本期主编-张尚金

关于

微店技术周刊整理每周有趣有用有料的各类内容,为大家呈现多样的技术世界。

周刊编辑:@何会会 / @张尚金 / @杨力 / @赵兴 / @刘远洋

周刊招募内容作者,主题不限,发出你的声音吧!

周刊同步在 微店技术团队 github,欢迎关注:https://github.com/weidian-inc/weidian-tech-blog

技术分享

工具资料

编程之外

23世纪问答(001期)

@viva是我问:23世纪的能源主要靠什么?人类对宇宙的开发大致到什么地步了?

答:23世纪人类文明的工业和社会生产主要依靠超级核聚变和空间太阳能提供能源动力。此时,人类普遍从安立柯帝国进口一种称为“精炼源”的矿物质来提高超级托卡马克核聚变过程中约束等离子磁场的稳定性。从而实现持久可控的核聚变并继而以此作为大型机械和城市的主要发电方式。太空大国普遍以核聚变能源作为其主力能源。另一方面,几个大国及其企业在环绕地球的轨道上设置大量太阳能板接收太阳能,在空间站转换为电能后用无线输电技术发回到地面上的电能接收站,再通过电网送到各个使用终端。空间太阳能的使用范围主要在地球与月球殖民地。太空城、地球上的小国和资源匮乏国家是空间太阳能的主要服务对象,太空大国为其在轨道上建立电站然后将电能传输到这些国家的城市。而出于地缘政治考量,大国本身不会过多使用空间太阳能。

人类从22世纪开始进入了太空殖民时代,太阳系、南门二、安立柯星系、翡翠星系是人类足迹已到的几个恒星系。不过,后两个星系由于是外星文明的势力范围,因此人类仍无法对其殖民和开发,而只是拥有航道通行权。人类的殖民地和空间开发主要仍在太阳系和南门二。其中以月球和火星的开发最为成熟,这两个地方已经遍布人类殖民点和城市,环木星轨道、土卫六、小行星带和柯伊伯带也渐渐吸引了大量人类殖民者前往开发。值得一提的是,以色列人更是把谷神星直接地球化后改造成了迦南行星。而南门二则只有潘朵拉行星上有殖民据点。除了行星,还有大量空间站和太空城市充斥着太阳系,其中最大的是柯伊伯带星门附近的边缘港,人口已达两百万以上。

@低熵的高熵体问:行星际与恒星际飞船的推进方式是怎么样的?六大国的在轨打击能力如何?机器人有机器人三定律的限制吗?地球气候如何?大都会新闻目前有多少成员?

答:23世纪人类飞船的主流推进方式主要是核聚变、光压、磁压这三种推进系统。由于恒星际间的旅行依靠星门(一种由翡翠文明发明并维护的能压缩空间的人造黑洞)作为枢纽,因此飞船并不需要装备支持超长时间航行以及近光速级别的推进系统。

六大国在23世纪都有完整的太空舰队和太空战斗能力,其中英国、以色列和斯拉夫的太空运输能力最高,他们的战舰也最多,他们的舰队承运了超过一半的恒星际货物运输,所以被称为“人类马车夫”。不过,**、美联和欧罗巴由于人口众多、工业结构完整以及殖民地广阔,因此拥有最全面和综合度最高的太空打击力量。有其擅长夸空间和行星表面的立体作战。

机器人三定律在23世纪发挥了重要作用。不过,出于对AI和硅基文明的潜在崛起的担心,人类各主要国家都立法限制机器人公司或相关运营机构私自提高AI及机器人的能力,而且也不允许AI擅自联网。

23世纪的地球环境十分优美,这是因为:

一,大部分工业都已经转移到月球和太空城;

二,化石能源不再是能源主流;

三,核聚变和太阳能不会产生环境破坏;

四,社会的环保意识很强(甚至是矫枉过正)。

大都会新闻目前只有一个成员。

@左舷炮手寞寞寂问:翡翠文明对地球的影响体现在哪?

答:翡翠文明作为一个超越人类数万年的先进文明,对人类文明在21世纪第一次接触后的走向产生了深刻影响。

主要表现在几个方面:

1,引起了人类社会的宗教回归思潮。由于第一次接触的强大震撼感,也由于地球几个主要宗教修正了其教义,把外星文明纳入宗教体系中,很多原本已经疏离了宗教的人以及无神论证开始重新找回宗教信仰。与此同时,也催生了大量小众宗教。宗教回潮运动导致的直接后果是很多国家和社会的宗教团体和势力重新取得了强势地位。

2,向人类引荐了安立柯文明。翡翠文明是人类和安立柯文明接触、交流以至发生贸易关系的中间人,这直接引发了人类历史上最长的经济繁荣周期,大大加速了人类开发太阳系和探索其他恒星系的速度。

3,向人类传授了大量科学知识。翡翠文明在接触后不久就为人类制定了“涅槃计划”,通过学校、企业、政府、NGO和特使等各种渠道向人类有系统、有步骤地传授其掌握

的科学知识,此举直接推动了人类文明的发展。

@樊元树问:货币体系是什么样子?

答:23世纪人类文明的货币系统是商品本位占绝对主导,世界各国政府都基于黄金和克里普(KREEP)作为货币的价值,货币形式是电子货币形式,由各国**银行管理的大规模分布式服务器阵列作为电子货币的发行基础。而与之相反,安立柯帝国的货币系统是信用货币星元,星元的发行并没有真实的商品作为基础,它在人类、翡翠文明、安立柯帝国三边贸易中被确立为基础货币的无锚定信用凭证。国际货币基金组织(IMF)在全人类范围内发行信用点(Credit Point, 简称CP),CP以一揽子货币为基础,包括人民币、欧元、联邦美元(Federal Dollar,简称FEDA)、卢布、英镑和高级谢克尔(Senior Sheqel)等。人类文明范围内的贸易以及与IMF的结算都以CP为基础货币。每个国家可将与安立柯帝国贸易所收到的星元与IMF兑换为CP。

@正-魔之左手问:日本交出了什么样的代价才成为**的“盟国”呢?

答:日本的地缘环境和民族性格注定了他们不是倒向**就是倒向美联,自从21世纪美军撤出亚太地区以及紧跟着的美国解体后,日本就意识到倒向**已是必然,尤其是与朝鲜在一场惨烈的本土战争后,日本人成为亚太一极的愿望也最终落空了,因此便与朝鲜,还有独立的琉球一同成为大中华经济圈的重要一员。

@lightman-韬问:之前说过好像要推出游戏,是什么类型的,什么时候可以运营?

答:23世纪游戏预计会在2015年开放公测,是战略类型多人在线网页游戏,形态有点儿类似于travian和文明的结合,不过会有很多创新的玩法在其中。游戏会与大都会新闻的主线直接挂钩,玩家在游戏中的行为与主线间会产生相互影响。

我知道有部分粉丝的问题还没回答,这是因为一来问题比较敏感,二来我也没有想好,所以留待以后再回答。此外,我会在2015年推出一本详细说明23世纪人类文明情况的书,有兴趣的读者届时可以留意大都会新闻的动态。

>查看原文

微店技术周刊(总第 36 期) - 本期主编 - 胡彬

微店技术周刊(总第 36 期) - 本期主编 - 胡彬

祝大家万圣节快乐,Happy Halloween!

关于

微店技术周刊整理每周有趣有用有料的各类内容,为大家呈现多样的技术世界。

周刊编辑:@何会会 / @张尚金 / @杨力 / @刘远洋 / @胡彬

周刊招募内容作者,主题不限,发出你的声音吧!

周刊同步在 微店技术团队 github,欢迎关注:https://github.com/weidian-inc/weidian-tech-blog

分享

投稿

我们说一个人靠谱,到底指的是这个人具有什么特质

来自:技术部 - IT徐胖子

1 靠谱

在工作中,我们肯定希望合作伙伴做事情靠谱,不知大家有没有想过,靠谱这个词究竟表达的是什么意思呢?

有人说靠谱是指工作能力强,有人说是指言出必行,也有人说是指保质保量完成任务。我认为这些说法都有一定的道理。

如果让我谈谈靠谱的定义,我的答案是:符合预期并且状态稳定。这个定义中有两个关键词:符合预期,状态稳定。

2 符合预期

如果小明是初级工程师,那么你对小明的预期就应该是初级工程师所达到的水平。如果他可以把初级工程师的事情做完做好,这就叫达到预期。

当然如果小明在工作中不仅完成了本职工作,而且给你带了惊喜,达到了更高层级的水平,那么小明升职加薪自然也不会遥远。遇到这样的小明固然是惊喜,没有遇到也属于正常。

大多数的小明们可能在工作中只能达到初级工程师的水平,这是正常的。所以在合作中,你一定要调整自己的预期,不能不切实际,幻想着小明有着高级工程师的水平,再反过来埋怨小明没有把事情做好,那就是你的不对了。

做到超过层级的事情叫惊喜,做到符合层级的事情叫符合预期。

下面我将会用比较多的篇幅来讨论另一个词:状态稳定。

3 麦当劳

每个城市都有很多家麦当劳的连锁店,你在A连锁店吃到的薯条的味道,应该不会比B连锁店差。你在C店吃到汉堡包的尺寸,应该也不会比D店小。每当进入麦当劳,你可以保持稳定的预期值去点餐。

这是因为麦当劳有一套严格的操作流程,摘录一段罗振宇老师关于麦当劳工作流程的描述:可口可乐的温度必须保持在4摄氏度,牛肉饼的成分必须是83%的牛肩肉和17%的五花肉,其中脂肪含量不能超过19%,最终做出来的牛肉饼,必须直径是95.8毫米,厚度5.65毫米,重量47.32克。

大家可能会觉得有点过了,可乐5摄氏度和4摄氏度有什么区别呢?牛肉饼重量48克又会有什么影响呢?

其实麦当劳用这种近乎苛刻的方法,在保证一件事情:让客人在全世界所有麦当劳,吃到的可乐和牛肉饼都是同一个味道。

这种工作方式称为标准化,这种产品的质量称为匀质。我们其实经常忽视一点:匀质也是优质的一种。

4 大数定律

下面我们会用到一些数学知识,先介绍一个定律:大数定律。我们以两个班级同学的身高分析为例。

现在有A班和B班两个班级,经过统计调查,两个班级的身高分布图如下:插图

从图中不难分析,在两个班级中,个子高和个子矮的同学都属于少数,个子中等的同学属于大多数。这就是正态分布。其中170cm的同学人数最多。

此时我们就要引出大数定律的定义:当样本足够多时,实验结果的平均值会无限接近于一个数值,我们把这个数值称之为期望值。170cm就是这两个班级身高期望值。

5 标准差

仔细观察两张图还会发现一个细节:A班身高分布较广,图像显得比较矮胖。B班身高分布较窄,图像显得比较瘦高。这说明了什么问题?

这说明在B班,身高偏离期望值170cm的同学比较少。而在A班,身高偏离期望值170cm的同学比较多,分布比较广。

这就引入了另一个概念:标准差。标准差反映了一个数据集的离散程度,即数据偏离期望值的程度。期望值相同的两组数据,标准差未必相同。在本例中,A班的标准差大,B班的标准差小。

假设你是一名新来的校长,你要从A班和B班中,挑选一个班级的全体同学参加仪仗队,仪仗队需要队伍中每个同学的身高都差不多。如果你对两个班级的了解只有上述的身高分布图,你会怎么选择?

你肯定会选择B班。因为标准差小,数据离散程度低,偏离期望值样本少,才更有可能符合你的预期。

标准差代表风险,标准差越小,风险越小。

6 状态稳定

介绍了这些概念,终于回到我们对靠谱的定义:符合预期并且状态稳定。我认为状态稳定就是标准差小,不会偏离期望值太多。

在篮球赛场上,可以被称为球星的,一般都是每场比赛都可以稳定贡献20分以上的运动员,即使有几场发挥不佳,但总体可以提供稳定的预期,这样的运动员才可以被称为球队的核心。

在足球比赛中,怎么样衡量一位门将是否优秀呢?A门将经常有扑出点球的神勇发挥,但是也常有扑球失误。B门将扑点球没有那么擅长,但是不失误,稳如泰山。那么可以坐稳主力位置的一定是B门将。

7 风险对冲

万维钢老师在《对冲风险的数学原理》这篇文章中有一个精彩案例:

A公司是户外用品商店,天气好时盈利百分之40,天气不好时亏损百分之20,假设天气好和天气不好的时间各占一半,那么A公司利润数学期望是:0.5x0.4-0.5x0.2=0.1,即百分之10的利润。

B公司是室内用品商店,盈利情况正好和A公司相反。天气不好时盈利百分之40,天气好时亏损百分之20,那么B公司利润数学期望是:0.5x0.4-0.5x0.2=0.1,也是百分之10的利润。

假设你有100元钱,想购买A公司或B公司的股票,你盘算着,百分之10的利润还不错,但是有可能一下子亏掉百分之20,风险有点高。此时你应该怎么办呢?

答案是你应该50元买A公司的股票,50元买B公司的股票。

假设天气好,A公司盈利50x0.4=20,B公司亏损50x0.2=10,总利润是20-10=10元。

假设天气不好,B公司盈利50x0.4=20,A公司亏损50x0.2=10,总利润还是20-10=10元。

你的利润依然是百分之10,但是标准差即风险变成了0。这就是大名鼎鼎的对冲投资,即投资的领域的运动形态最好为负相关,这样可以抹平风险。

8 文章总结

再回到我对靠谱的定义:符合预期并且状态稳定。符合预期告诉我们要摆正心态,不要做不切实际的幻想。状态稳定告诉我们要管控风险,不要过多偏离期望值。愿我们都成为靠谱的人,并可以遇到靠谱的伙伴。

微店技术周刊(总第 26 期)-本期主编-何会会

微店技术周刊(总第 26 期)-本期主编-何会会

关于

微店技术周刊整理每周有趣有用有料的各类内容,为大家呈现多样的技术世界。

周刊编辑:@何会会 / @张尚金 / @杨力 / @赵兴 / @刘远洋

周刊招募内容作者,主题不限,发出你的声音吧!

分享

投稿

谈谈「数据模型」是如何助力前端开发的

@张尚金 - 生产力平台

原文链接:https://juejin.im/post/5d410e85f265da03dd3d4ee8

正文部分

一、定义

数据模型是数据特征的抽象,用来抽象定义一个业务对象。假如现在有一个用户模型,如果要抽象的描述这个用户对象,可以按照如下来定义:

const UserModel = {
    name:{
        type:String,
        property:'name',
        value:'zhangshang'
    },
    age:{
        type:Number,
        property:'age',
        value:26
    }
}

其中,type声明数据的类型,property指明映射路径,value是默认值。这里先有个大概的概念就行,后面我会通过实例来详细展开。

二、动机

前面介绍了数据模型的定义,那和前端开发又有什么关系呢?前端又不需要和数据库打交道,前端开发主要就是拿到数据显示就完了,那为什么需要数据模型呢?它是怎么助力前端开发的呢?我们先来看一下以下几个场景。

场景一

我们在前端开发中,通过ajax请求拿到服务端数据,然后将数据显示在视图上,经常会写如下代码:

如示例,假如我们要显示用户头像,通过取到headUrl的值绑定在src属性上即可。因为是异步加载获取的数据,在最终获取到headUrl的值之前,我们需要先判断cardData.buyerExperienceInfo的存在性,然后才能取值,否则在视图初次渲染之前会报如下错误:

在这种场景下,我们在开发中就不得不写一些防御性的代码,久而久之,项目中类似代码会越来越多,碰到层级深的,防御性代码就会写的越来越恶心。另外还有的就是,如果服务端在这中间某个字段删掉了,那就又得特殊处理了,否则会有一些未知的非空错误报错,这种编码方式会导致前端严重依赖服务端定义的数据结构,非常不利于后期维护。

场景二

平时开发中,我们拿到了服务端返回的数据,有些不是标准格式的,是无法直接在视图上直接使用的,是需要而外格式化处理的,比如我司服务端返回的的价格字段单位统一是,跟时间相关的字段统一是毫秒值,这个时候我们在组件的生命周期内,就不得不而外增加一些对数据处理的逻辑,还有就是这部分处理在很多组件都是公用的,我们就不得不频繁编写类似的代码,数据处理逻辑没有得到复用

场景三

在用户做了一些交互后,需要将一些数据存储到服务端,这个时候我们拿到的数据往往也是非标准的,就比如你要提交个表单,其中有个价格字段,你拿到价格单位可能是百位的,而服务端需要的单位必须是分位的,这个时候在提交数据之前,你又得对这部分数据进行处理,还有就是有些接口的参数是json字符串形式的,可能是多级嵌套的,你还要需要特意构造这样的参数数据格式,导致开发中编写了太多与业务无关的逻辑,随着项目逐渐扩大或者维护人员更迭,项目会越来越不好维护。

三、数据模型

在碰到这么多痛点之后,我就在想如何解决,回顾以上场景,总结下来存在以下几个问题:

  1. 前后端数据结构没有解耦,前端在应对不定的服务端数据结构前提下,需要编写过多的保护性代码,不利于维护的同时,代码健壮性也不高。
  2. 基础数据逻辑处理没有和UI视图解耦,容易阻塞视图渲染,同时,在视图组件上存在太多的基础数据逻辑处理,没有有效复用。

所以,这里我引入了数据模型的概念,那通过数据模型如何解决这类问题呢?下面我将通过两个实际案例来进一步呈现上述场景,以及引入了数据模型之后是如何解决的。

四、案列功能

......

查看全部文章内容

微店技术周刊(总第 34 期) - 本期主编 - 张尚金

微店技术周刊(总第 34 期)-本期主编-张尚金

【物体表面跟踪特效合成高级工具 Lockdown v1.0.0 Win】可以直跟踪运动物体扭曲不平的表面

视频地址

关于

微店技术周刊整理每周有趣有用有料的各类内容,为大家呈现多样的技术世界。

周刊编辑:@何会会 / @张尚金 / @杨力 / @赵兴 / @刘远洋

周刊招募内容作者,主题不限,发出你的声音吧!

周刊同步在 微店技术团队 github,欢迎关注:https://github.com/weidian-inc/weidian-tech-blog

分享

投稿

本期暂无投稿

谈谈「数据模型」是如何助力前端开发的 - 张尚金

一、定义

数据模型是数据特征的抽象,用来抽象定义一个业务对象。假如现在有一个用户模型,如果要抽象的描述这个用户对象,可以按照如下来定义:

const UserModel = {
    name:{
        type:String,
        property:'name',
        value:'zhangshang'
    },
    age:{
        type:Number,
        property:'age',
        value:26
    }
}

其中,type声明数据的类型,property指明映射路径,value是默认值。这里先有个大概的概念就行,后面我会通过实例来详细展开。

二、动机

前面介绍了数据模型的定义,那和前端开发又有什么关系呢?前端又不需要和数据库打交道,前端开发主要就是拿到数据显示就完了,那为什么需要数据模型呢?它是怎么助力前端开发的呢?我们先来看一下以下几个场景。

场景一

我们在前端开发中,通过ajax请求拿到服务端数据,然后将数据显示在视图上,经常会写如下代码:

如示例,假如我们要显示用户头像,通过取到headUrl的值绑定在src属性上即可。因为是异步加载获取的数据,在最终获取到headUrl的值之前,我们需要先判断cardData.buyerExperienceInfo的存在性,然后才能取值,否则在视图初次渲染之前会报如下错误:

在这种场景下,我们在开发中就不得不写一些防御性的代码,久而久之,项目中类似代码会越来越多,碰到层级深的,防御性代码就会写的越来越恶心。另外还有的就是,如果服务端在这中间某个字段删掉了,那就又得特殊处理了,否则会有一些未知的非空错误报错,这种编码方式会导致前端严重依赖服务端定义的数据结构,非常不利于后期维护。

场景二

平时开发中,我们拿到了服务端返回的数据,有些不是标准格式的,是无法直接在视图上直接使用的,是需要而外格式化处理的,比如我司服务端返回的的价格字段单位统一是,跟时间相关的字段统一是毫秒值,这个时候我们在组件的生命周期内,就不得不而外增加一些对数据处理的逻辑,还有就是这部分处理在很多组件都是公用的,我们就不得不频繁编写类似的代码,数据处理逻辑没有得到复用

场景三

在用户做了一些交互后,需要将一些数据存储到服务端,这个时候我们拿到的数据往往也是非标准的,就比如你要提交个表单,其中有个价格字段,你拿到价格单位可能是百位的,而服务端需要的单位必须是分位的,这个时候在提交数据之前,你又得对这部分数据进行处理,还有就是有些接口的参数是json字符串形式的,可能是多级嵌套的,你还要需要特意构造这样的参数数据格式,导致开发中编写了太多与业务无关的逻辑,随着项目逐渐扩大或者维护人员更迭,项目会越来越不好维护。

三、数据模型

在碰到这么多痛点之后,我就在想如何解决,回顾以上场景,总结下来存在以下几个问题:

  1. 前后端数据结构没有解耦,前端在应对不定的服务端数据结构前提下,需要编写过多的保护性代码,不利于维护的同时,代码健壮性也不高。
  2. 基础数据逻辑处理没有和UI视图解耦,容易阻塞视图渲染,同时,在视图组件上存在太多的基础数据逻辑处理,没有有效复用。

所以,这里我引入了数据模型的概念,那通过数据模型如何解决这类问题呢?下面我将通过两个实际案例来进一步呈现上述场景,以及引入了数据模型之后是如何解决的。

四、案列功能

这个案例使用Vue开发,功能很简单,就是通过ajax请求从服务端拿到数据,然后通过vue视图进行展现,效果如下:

五、常规实现

代码只展示主要功能代码,非完整实现

1.请求数据

created生命周期内,向服务端请求数据。

2.数据处理

获取到数据之后,因为拿到的数据和最终UI上显示的格式不一致,需要转化一下数据格式。

3.渲染数据

给当前Vue实例赋值,然后在template里通过模板语法进行渲染

可以看到常规写法,模板语法里面的写法特别不优雅,各种保护性代码(条件判断)

六、通过数据模型方式处理

1.定义数据模型

首先,我们可以专门建一个名叫model的文件夹,专门用来存放模型,然后定义卡片模型cardModel,其中数据定义格式如下:

  1. type 必填,用来描述该字段的类型,支持String、Number、Date等类型
  2. property 必填,数据路径,对应服务端数据结构的取值路径
  3. value 选填,数据默认值,可不填

通过new Model()进行初始化,后续只需要通过model.parse(data)或者model.traverse(data)这个两个方法就可以完成正向映射和反向映射的过程。

具体的使用方式可以查看API

2.请求数据

通过axios请求接口,在拿到数据之后,调用parse方法解析数据,在解析的过程中会去做赋值操作以及数据格式化。

3.数据渲染

拿到数据,赋值给vue组件实例后,在template模板里面直接使用我们事先定义好的数据字段,不需要再去写类似a&a.b&a.b.c这样的代码,且不管服务端数据字段如何变化,视图渲染都不受影响,从而实现和服务端数据结构进行解耦。

与此同时,针对类似价格、时间等需要格式化的数据,我们可以直接使用,不需要再去写对应的格式化处理逻辑,从而专注于视图组件渲染处理。

通过引入数据模型,我们可以看到在模板里面引入变量的时候不需要进行各种判断,写法非常优雅,而且健壮性很强,即使服务端某个字段没有返回,我们这里也不会因此存在报错的可能性。且在脚本里面没有了数据格式化处理代码,从而不会因为数据处理逻辑代码可能存在的错误,打断UI的渲染。从而带来的更大好处是,随着项目的不断迭代,数据和视图有着清晰的划分,前端和后端进行了解耦,项目的可维护性得到保证。

4.反向映射

在库里面,还提供了traverse方法,和parse方法类似,区别是traverse是反向数据生成以及格式还原。

七、模型库的原理

最后,我来讲讲这个数据模型库(ducker-model)的实现原理,源码总共不到200行,还是简单的,可以通过这里下载查看,主要实现逻辑如下:

  1. 声明一个名叫Model的类。
  2. 通过new Model(options),传入模型结构,初始化数据模型属性,对外主要使用的是parsetraverse方法,
  3. parse方法的实现过程就是遍历模型数据结构,拿到每个属性的数据路径,然后根据这个路径去取传入的的数据里面的数据,最后给事先定义好的属性赋值,在赋值的过程中,可以根据type格式化一些类似时间、价格类型的数据。
  4. traverse方法刚好和parse相反,同样是遍历数据模型结构,拿到每个属性的数据路径,然后根据这个数据路径去设置一个新对象的值,这期间,反向格式化数据类型,最后返回这个新对象。

八、更进一步

目前这个库还很基础,只支持了一些常规的功能,能做的事情还很多,比如:

  1. 目前每次拿到请求一次数据之后都需要解析一次,那是否有个缓存机制,在数据没有变动的时候,直接从缓存取数据呢,或者可以直接watch这个解析之后的数据,做到数据变动,视图变动呢?
  2. 现有支持的类型还不够多,可以根据具体业务情况增加一些类型,以应对更多场景,提供可扩展的机制。
  3. 插件机制,比如表单处理,我们是否可以在数据模型定义的时候就定义好字段格式,在提交的时候就可以直接进行格式检测,抛出提醒呢?
  4. 目前的操作方式还比较适合纯粹的渲染式组件,如何和复杂的携带业务交互的组件融合也是需要考虑的。
  5. ... ...

文章末尾会提供模型库下载地址,有需要的可以在此基础上进行扩展,欢迎一起完善这个库,另外,案例demo的地址也提供了,欢迎下载学习理解。

九、源码下载

案例:ducker-model-demo

模型库:ducker-model

微店技术周刊(总第 32 期)-本期主编-刘远洋

微店技术周刊(总第 32 期)-本期主编-刘远洋

没毛病

关于

微店技术周刊整理每周有趣有用有料的各类内容,为大家呈现多样的技术世界。

周刊编辑:@何会会 / @张尚金 / @杨力 / @赵兴 / @刘远洋

周刊招募内容作者,主题不限,发出你的声音吧!

周刊同步在 微店技术团队 github,欢迎关注:https://github.com/weidian-inc/weidian-tech-blog

技术分享

编程之外

投稿

本期暂无投稿

如何自动获取首屏时间 - 刘远洋

如何自动获取首屏时间

本文发表在 微店前端团队 blog

背景

在前端性能数据的获取方法上,现在业内大多使用手动埋点的方式,即在代码中,人工判断首屏完成的位置,并在该处添加首屏记录的代码,类似:firstscreen.report() 这样。

这样做的简单省事,但缺点也很明显:

  • 和业务代码混用

    通用的监控需求混入了业务代码中

  • 覆盖不完整

    需要页面开发者自觉手动添加埋点代码,在业务中埋点覆盖率不一定能达到 100%

  • 准确性不一定高

    由于需要开发者自行判断统计脚本放置的位置,就会存在一些不准确的情况,因为每个人对首屏的理解不同

基于上面的分析,我们近期尝试了一些方案,试图将首屏时间计算自动化,节省人力、并提高准确性。

定义

对首屏时间的定义,每个公司可能会有所不同,在本文中,首屏时间指的是:

  • 如果页面首屏有图片

    首屏时间 = 首屏图片全部加载完毕的时刻 - window.performance.timing.navigationStart
    
  • 如果页面首屏没有图片

    首屏时间 = 页面处于稳定状态前最后一次 dom 变化的时刻 - window.performance.timing.navigationStart
    

实现原理

总体思路为:

  • 从页面加载开始,按照一定的间隔打点,不断记录各个时刻下页面首屏图片列表和其他信息

    问题:按照怎样的间隔打点?

  • 找出页面首屏处于稳定状态的时刻 T1(到这个时刻为止,页面首屏可能已经稳定了一段时间)

    问题:如何找出这个 T1?

  • 以 T1 时刻的首屏图片数量为准,向前倒推,找到所有打点中最后一次和 T1 时刻首屏图片一致的打点时刻 T2

  • 统计 T2 时刻的所有图片加载完成时间 T3

  • T3 即为首屏完成的时刻,进行上报

下面,一个个解决上文中提到的问题:

  • 问题:如何找出首屏处于稳定状态的时刻 T1?

    我们将页面从加载到渲染分为两大阶段:1. 获取数据;2. 数据获取完毕,渲染页面。

    这个逻辑符合绝大部分的页面逻辑:先获取数据,再渲染页面。

    解决方案:

    1. 通过 AOP 切面方式监听 XHR 的 send 对象,抓取页面中的第一个 XHR 请求,以第一个 XHR 请求发出的时刻为起点,统计在 1000ms 以内所有发出的请求到数组 Request 中。

      我们认为可能影响首屏的请求在 [第一个 xhr 请求发出的时刻,第一个 xhr 请求发出的时刻 + 1000ms] 的时间段内均已发出。

    2. 针对串联型的请求(即下一个请求依赖上一个请求的返回数据),同时统计每个请求返回后,500ms 以内新发出的请求到数组 Request 中。

      有些页面的数据请求方式是串行的,可能经过两个串联的请求后首屏的数据才能加载。

      影响首屏的请求可能也会以这样的形式发出。

    3. 数组 Request 中统计到的请求,基本包含了所有影响首屏的数据请求,同时也包含了部分不影响首屏的数据请求。

    4. 针对上述统计到的请求,找到所有数据返回的时刻 T1,然后,T1 = T1 + 300ms,保证页面接收数据后渲染完毕(300ms 用于一次渲染足够了)。

    5. 此时的 T1 时刻,页面首屏被认为处于稳定状态。

  • 问题:按照怎样的间隔打点?

    • MutationObserver

      大家都知道 MutationObserver 对象用于捕捉页面 dom 变化,因此在脚本中,我们使用了 MutationObserver 监听 dom 变化,并在每次 dom 变化时触发一次打点(统计该时刻首屏图片信息)

    • setInterval

      setInterval 也能实现定时打点

    • MutationObserver 和 setInterval 组合

      但 MutationObserver 回调函数的触发时机开发者并不可控,有几种情况:

      • 两次回调之间可能距离几百毫秒甚至 1秒多,导致统计误差较大
      • 某些情况下,dom 不再变化,但页面元素中,imgsrc 发生了变化或元素的 background-image 发生了变化,并不会触发在 MutationObserver 的回调,导致统计失误

      因此,我们现在的方案是结合 MutationObserver 和 setInterval,在 MutationObserver 回调的间歇,启动 setInterval,保证页面加载过程中打点间隔不会过长,提高统计准确率。

统计误差

即使使用了上述复杂的打点与判断,误差仍然存在,那么,误差到底在哪里?

如下图所示:

不稳定状态(1 images)   稳定状态2(2 images)      稳定状态1(2 images)
    |                        |                       |
    |________________________|_______________________|
    t1                       t2                      t3

按照上面的理论,我们会取 t2 时刻为可以统计首屏的时刻,两张图片加载完成的时刻即为首屏完成的时刻。

t2t1 时刻差了 1 张图片。

按照我们的理论,首屏完成时间一定在 t2 之后的某个时刻 t2.n

而实际相差的那张图片,什么时候加载完成的,我们不得而知,可能在 t2 前已经加载完毕了,也可能已经发出请求,但还没加载完毕。

误差就在这里,它总会存在。

但我们需要统计的是在误差可以接受范围内的首屏数据,根据在公司业务实践的反馈来看,数据可靠性很高。

Talk is cheap, show me the code

我们也开源了这个小工具:

github: https://github.com/hoperyy/auto-compute-first-screen-time

npm: https://www.npmjs.com/package/auto-compute-first-screen-time

欢迎小伙伴们使用,吐槽,改进。

作者:刘远洋
公司:微店 - 前端团队
日期:2018-03-05

微店技术周刊(总第 35 期) - 本期主编 - 刘远洋

微店技术周刊(总第 35 期) - 本期主编 - 刘远洋

艺术家 Etienne Jacob 用代码作的动图

Twitter

关于

微店技术周刊整理每周有趣有用有料的各类内容,为大家呈现多样的技术世界。

周刊编辑:@何会会 / @张尚金 / @杨力 / @赵兴 / @刘远洋

周刊招募内容作者,主题不限,发出你的声音吧!

周刊同步在 微店技术团队 github,欢迎关注:https://github.com/weidian-inc/weidian-tech-blog

分享

  • 这个仓库收集汇总了 Web 安全相关的一些资源

  • 康奈尔笔记法: 让你的学习更高效

    康奈尔笔记系统把一页纸分成了三部分:

    1. 右上最大的空间是我们平时做笔记的地方,你按照平时的习惯记录就行了。
    2. 左边那竖着的一条空间叫做“线索栏”,是用来归纳右边的内容的,写一些提纲挈领的东西,这个工作不要在做笔记的时候做,而是在上完课之后马上回顾,然后把要点都写到左边,这样一方面马上复习了内容,另一方面理清了头绪。
    3. 下面那横着的一栏是用来做总结的,就是用一两句话总结你这页记录的内容,这个工作可以延后一点儿做,起到促进你思考消化的作用,另外也是笔记内容的极度浓缩和升华。

    image

  • uber 开源的一个 数据可视化控件库:预览

  • Node.js 终端的实时 ASCII 三维渲染器。Github

    image

  • Chrome 正在实现一个原生文件系统 API。资料

  • 用 JavaScript 实现的 13kb 以内的游戏:链接

  • 前端: Object.assign 和 Object Spread 之争, 用谁?(推荐者:@张川)

Babel 简介与编译原理

投稿人:生产力平台技术部 - 刘远洋

Babel 简介

Babel 是 JavaScript 编译器(来自官网描述)。它将高版本 ECMAScript 语法编译为浏览器都支持的 ES5 语法。

Babel 毫无疑问是目前前端极其重要的基础设施之一了,在介绍 Babel 之前,我们简要梳理下 JavaScript 发展史。

JavaScript 发展简史

  • 1990 年底,万维网(WWW)诞生,可以在命令行查看网页。但通过命令行看网页,也是不太方便的。

  • 1992 年底,美国国家超级电脑应用中心(NCSA)开始开发一个独立的浏览器,叫做 Mosaic。这是人类历史上第一个浏览器,从此网页可以在图形界面的窗口浏览。

  • 1994 年 10 月,Mosaic 通信公司成立,不久后改名为 Netscape,其主要开发面向普通用户的新一代浏览器 Netscape Navigator。

  • 1994 年 12 月,Netscape Navigator 发布了 1.0 版。该版本很受欢迎,但缺乏一种脚本语言,用于控制浏览器的行为。

  • 1995 年,程序员 Brendan Eich 受雇于 Netscape 公司,只用了 10 天就开发出 JavaScript 1.0 版本,当时命名为 Mocha,1995 年 9 月改名为 LiveScript。

  • 1995 年 12 月,Netscape 公司与 Sun 公司(Java语言的发明者和所有者)达成协议,后者允许将 LiveScript 叫做 JavaScript。对于两个公司而言都有益处:NetScape 公司可以借助 Java 的声势,而 Sun 公司则将自己的影响力扩展到了浏览器。

  • 1996 年 3 月,Navigator 2.0 浏览器正式内置了 JavaScript 脚本语言。

  • 1996 年 8 月,微软模仿 JavaScript 开发了一种相近的语言,取名为 JScript,首先内置于 IE3.0。

  • 1996 年 11 月,Netscape 公司决定将 JavaScript 提交给国际标准化组织 ECMA(European Computer Manufacturers Association),希望 JavaScript 能够成为国际标准,以此抵抗微软。

  • 1997 年 7 月,ECMAScript 1.0 版发布。

    ECMA 组织发布 262 号标准文件(ECMA-262)的第一版,规定了浏览器脚本语言的标准,并将这种语言称为 ECMAScript。

    由于 ECMA 的开放和中立性,ECMAScript 和 JavaScript 的关系是,前者是后者的规格,后者是前者的实现。

    ECMA-262 标准后来也被另一个国际标准化组织 ISO(International Organization for Standardization)批准,标准号是 ISO-16262。

  • 1998 年 6 月,ECMAScript 2.0 版发布。

  • 1999 年 12 月,ECMAScript 3.0 版发布,成为 JavaScript 的通行标准,得到了广泛支持。

  • 2007 年 10 月,ECMAScript 4.0 版草案发布,但该版本过于激进,分歧很大。

  • 2008 年,Google 公司为 Chrome 浏览器而开发的 V8 编译器诞生。

  • 2009 年,Node.js 诞生。Node.js 的出现促进了前端工程化的快速发展,前端由石器时代快速进入了工业时代。

  • 2009 年 12 月,ECMAScript 5.0 版正式发布。截止 2012 年底,已得到绝大部分浏览器支持

  • 2010 年,NPM、BackboneJS 和 RequireJS 的出现,标志着 JavaScript 进入模块化开发时代。

  • 2013 年 5 月,Facebook 发布 UI 框架库 React,引入了新的 JSX 语法,使得 UI 层可以用组件开发。

  • 2015 年 6 月,ECMAScript 6.0 版正式发布,并更名为: ECMAScript 2015 标准。

  • 2016 年 6 月,ECMAScript 2016 标准发布。

  • 2017 年 6 月,ECMAScript 2017 标准发布,正式引入了 async 函数,使得异步操作的写法出现了根本的变化。

ECMAScript 标准目前保持每年一次发布的速度更新,相应的,部分浏览器对标准的支持会显得有些滞后。

于是,基于对高版本语法转译为低版本语法的各种工具被开发了出来。

下一步,我们来了解下 JavaScript 引擎。

JavaScript 引擎

JavaScript 引擎是一个专门处理 JavaScript 脚本的虚拟机,负责解析 Javascript 语言。

对于浏览器而言,其内核包括:渲染引擎(layout engineer 或者 Rendering Engine)、JavaScript 引擎等。

渲染引擎负责取得网页的内容(HTML、XML、图像等等)、整理讯息(例如加入 CSS 等),以及计算网页的显示方式,然后会输出至显示器或打印机。

浏览器的内核的不同对于网页的语法解释会有不同,所以渲染的效果也不相同。所有网页浏览器、电子邮件客户端以及其它需要编辑、显示网络内容的应用程序都需要内核。

下面是一些 JavaScript 引擎:

  • SpiderMonkey,第一款 JavaScript 引擎,早期用于 Netscape Navigator,现时用于 Mozilla Firefox。
  • V8,开放源代码,由 Google 开发,是 Google Chrome 的一部分。
  • JavaScriptCore,开放源代码,用于 Safari。
  • Chakra (JScript 引擎),用于 Internet Explorer。
  • Chakra (JavaScript 引擎),用于 Microsoft Edge。

Babel

在了解了 JavaScript 及其引擎的各项背景后,我们来了解下 Babel。

Babel 是 JavaScript 编译器(来自官网描述)。它将高版本 ECMAScript 语法编译为浏览器都支持的 ES5 语法。

开发者编写的 JavaScript 代码,与浏览器等容器内运行的 JavaScript 通常是不同的,比如为了兼容低版本浏览器,需要将编写时代码转译为运行时代码

举例:

// 这种语法,在 IE7 等低版本浏览器中是无法识别的,会报语法错误
const fn = (a, b) => a + b;

// 经过 Babel 及其插件编译为 ES5 后,IE7 等浏览器可以识别下面的代码
var fn = function(a, b) {
    return a + b;
};
  • Babel 发展史

    • 2014 年,Facebook 的澳大利亚的工程师 Sebastian McKenzie 发布了 6to5 这个库,用于将 ES6 转为 ES5,它使用的 AST 转换引擎 fork 自 acorn。

    • 2015 年 2 月 15 日,6to5Esnext 的团队决定一起开发 6to5,并改名为 Babel,解析引擎改为 Babylon。后来,Babylon 移入到 @babel/parser

    • 2015-03-31,Babel 5.0 发布。

    • 2015-10-30,Babel 6.0 发布。

      Babel 自 6.0 起,就不再对代码进行修改。从这个版本开始,Babel 主要负责 Parse 和 Generate 流程,修改代码的 transform 过程全都交给插件去做。也就是说,Babel 只是一个语法解析器。

    • 2018-08-27,Babel 7.0 发布。

  • Babylon、Babel 的含义**

    • Babylon:巴比伦,意指巴比伦文明
    • Babel:通天塔,意指巴比伦文明中的通天塔

    创世记第 11 章 1-9 句记录了“巴别城”的故事。当时地上的人们都说同一种语言,当人们离开东方之后,他们来到了示拿之地。在那里,人们想方设法烧砖好让他们能够造出一座城和一座高耸入云的塔来传播自己的名声,以免他们分散到世界各地。上帝来到人间后看到了这座城和这座塔,说一群只说一种语言的人以后便没有他们做不成的事了;于是上帝将他们的语言打乱,这样他们就不能听懂对方说什么了,还把他们分散到了世界各地,这座城市也停止了修建。这座城市就被称为“巴别城”。

    圣经是这样描写的:

    4 他们说,“来吧,我们要建造一座城和一座塔,塔顶通天,为了扬我们的名,免得我们被分散到世界各地。”
    5 但是耶和华降临看到了世人所建造的城和塔。
    6 耶和华说,“看哪,他们都是一样的人,说着同一种语言,如今他们既然能做起这事,以后他们想要做的事就没有不成功的了。”
    7 让我们下去,在那里打乱他们的语言,让他们不能知晓别人的意思。
    8 于是耶和华使他们分散到了世界各地,他们也就停止建造那座城。
    9 因为耶和华在那里打乱了天下人的言语,使众人分散到了世界各地,所以那座城名叫巴别。
    ——创世记11:4–9

Babel 编译原理

Babel 本质上就是一个编译器,将一份代码编译为另一份代码。

Babel 的编译流程和大部分编译器的编译流程是相似的,包括三个过程:

  • 第一阶段:解析( Parsing )

    解析 是将最初原始的代码转换为一种更加抽象的表示( AST )。

    它包括:词法解析( Lexical Analysis )和语法解析( Syntactic Analysis )。

  • 第二阶段:转换( Transormation )

    转换阶段会对 AST 进行遍历,在这个过程中对节点进行增删改查。

  • 第三阶段:重新生成代码( Code Generation )

    编译器有很多种,我们先不考虑其他类型的编译器,先详细了解下 Babel 的整个编译过程。

编译过程如下图:

image

Babel 在编译过程用到了一些工具集,如下图:

本文,我们从最简单的案例入手理解编译原理,不去深究 Babel 各个模块的源码。

解析

解析 是将最初原始的代码转换为一种更加抽象的表示( AST )。

包括:词法解析( Lexical Analysis )和语法解析( Syntactic Analysis )。

  • 2.1.1 词法解析( Lexical Analysis )

    词法解析器( Tokenizer ) 在这个阶段将字符串形式的代码转换为 Tokens (令牌),这个过程由词法解析器( TokenizerLexer )完成。

    令牌( Tokens )是扁平化的语法片段数组,每个数组项包含了:代码片段( value )、代码位置( start / end )、类型( type ) 等信息,这些信息有助于后续的语法分析。

    n*n 经过词法解析后,转换成的令牌如下:

    // n*n
    [
    { type: { ... }, value: "n", start: 0, end: 1, loc: { ... } },
    { type: { ... }, value: "*", start: 2, end: 3, loc: { ... } },
    { type: { ... }, value: "n", start: 4, end: 5, loc: { ... } },
    ...
    ]

    每一个 type 有一组属性来描述该令牌:

    {
        type: {
            label: 'name',
            keyword: undefined,
            beforeExpr: false,
            startsExpr: true,
            rightAssociative: false,
            isLoop: false,
            isAssign: false,
            prefix: false,
            postfix: false,
            binop: null,
            updateContext: null   
        }
    }

    词法解析器( Tokenizer )本质上就是一个字符串处理方法,入参是字符串,返回结果是数组。

    function Tokenizer() {
        const tokens = [];
    
        // 字符串解析,并将结果
        // processing
    
        // 返回 Token 数组
        return tokens;
    }
  • 语法解析( Syntactic Analysis )

    语法解析器( Parser ) 会把 Tokens 转换为抽象语法树( Abstract Syntax Tree, AST )。

    AST 就是 JavaScript 中的一个 Object Tree,用于表示代码的语法结构。

    举个例子:

    const a = 1;
    const b = 2;
    
    console.log(a + b);

    会被解析为:

    image.png

    其中,ProgramVariableDeclaratorCallExpression 等表示节点类型,每个节点都是一个语法单元。

    节点类型的属性描述了节点的详细信息。

    JavaScript 中的节点类型非常多,再加上 JSX、Flow 等,我们不需要记忆它们,在需要的时候,ASTExplorer( https://astexplorer.net/ )可以帮助我们快速查看代码的 AST。

    很显然,语法解析器接收 Token 数组,转为 AST,本质上是一个转换方法:

    function parser(tokens) {
        const ast = {
            type: 'Program',
            body: []
        };
    
        // tokens to ast
        // processing
    
        return ast;
    }

转换( Transformation )

这个过程 Babel 会对 AST 进行遍历,并且进行增删改查等转换动作。

Babel 所有插件都是在这个阶段工作, 比如语法转换、代码压缩等。

由于 AST 有众多类型的节点,在遍历 AST 过程中,需要用到 深度遍历Visitor

深度遍历,就是递归遍历 AST 对象;

Visitor,也就是访问者模式,它是一个对象,其 key 即为各个节点类型,值为各个处理方法。

转换过程如下:

// 转换器定义
function traverse(ast, visitor) {
    // 递归遍历
    // dfs(ast, visitor)
}

// 转换器执行
traverse(ast, {
    Program(node, parent) {
        // ...
    },

    CallExpression(node, parent) {
        // ...
    },

    NumberLiteral(node, parent) {
        // ...
    }
});

重新生成代码( Code Generation )

把 AST 转换回字符串形式的 Javascript,同时这个阶段还会生成 Source Map。

转换器接收一个 AST,并将其转为代码字符串。本质上是对象转字符串的方法。

function transformer(ast) {
    let code = '';

    // 遍历 AST,拼接 code 字符串

    return code;
}

总结

了解了这个过程,我们就可以发挥其威力,做一些很酷的事情,比如自动添加埋点代码、代码压缩、自定义语法糖等等。

参考资源

参考资源如下:

微店技术周刊(总第 29 期) - 本期主编 - 杨力

全栈化技术周刊(总第 29 期)-本期主编-杨力

人工智能,重新定义未来

关于

全栈化技术周刊整理每周有趣有用有料的各类内容,为大家呈现多样的技术世界。

周刊编辑:@何会会 / @张尚金 / @杨力 / @赵兴 / @刘远洋

周刊招募内容作者,主题不限,向团队发出你的声音吧!

推荐

投稿

serverless在微店node领域的探索应用

@杨力 - 生产力平台

原文链接:https://www.cnblogs.com/accordion/p/11381033.html

背景

目前微店中台团队为了满足公司大部分产品、运营以及部分后端开发人员的尝鲜和试错的需求,提供了一套基于图形化搭建的服务端接口交付方案,利用该方案及提供的系统可生成一副包含运行时环境定义可立即运行的工程代码,最后,通过 “某种serverless平台” 实现生成后代码的部署、CI、运行、反向代理、进程守、日志上报、进程分组扩容等功能。

这样,产品和运营人员可基于此种方式搭建的接口配合常用的cms系统实现简单查询需求如活动大促的自主“研发”上线,代码的可靠性、稳定性由中台研发侧提供的“某种serverless平台”保障,有效支撑了多个业务快速上线,节省后端开发人员的人力与硬件资源的开销(大多数需求下,nodejs业务对虚拟机的资源开销小于java业务)。

接口搭建系统

此处并不讲解接口搭建系统的原理与实现,只说明其与上文提到的 “某种serverless平台” 的关系。

enter image description here

这是系统的全貌,部分细节由于敏感信息而省略。平台使用方可基于每个功能组件搭建出一套复杂的业务流,在搭建阶段,提供在线debug和日志功能,可用于排错;在部署CI阶段,可集成不同的运行时平台,既可以是自主实现的运行时,也可是第三方云平台;在运行阶段,通过使用agentool工具实时监控当前服务的性能,并可通过traceId一览请求在各系统的全貌。

serverless方案

本节以资源隔离粒度为度量,介绍了我对三种serverless方案的取舍以及最终为何选择了隔离程度更高的kubeless云平台。

基于函数隔离的Parse Server方案

Parse Server提供了基础功能:基于类与对象的权限控制、基于document的nosql存储、简易的用户身份认证、基于hook的自定义逻辑等,但经过笔者的调查与论证,最终并没有采用类似单进程下基于函数隔离的Parse Server及类似方案,这有几点原因:

  1. Parse Server方案很重,额外引入了非常多不需要的功能,如权限控制、认证、存储等
  2. 服务隔离级别低,多个服务在一个进程运行,多个服务会在libuv层互相抢占CPU,互相影响对方的业务处理
  3. 水平扩容难度大,针对单个服务的扩容无法做到
  4. 底层基于express框架,无法满足运行时接口调用链路的trace追踪
  5. 当多个服务同时引入不同的资源如db、es或者服务创建的对象足够多时,会存在Parse Server主进程溢出的风险,毕竟64位机的node堆内存是有1.4GB上限的,尽管这个上限是可配置的
  6. Parser Server发布的接口需通过其client调用,这在公司商用情况下需要做许多额外的配置才能满足

Parse Server官网

基于进程隔离的super-agent方案

为了解决多个服务抢占libuv的问题,我选择了自主研发的 super-agent方案,通过其名称便可知它是一个超级代理,但它不仅是代理,还是一个具有极简功能且可靠的发布系统和运行时容器;它是一个分布式应用,节点间功能划分明确;它同时提供实时调试功能。

super-agent是一个多角色分布式系统,它即可以看做node容器,也可看成serverless的node实现,它以进程为粒度管理服务。它分为“协调者”和“参与者”,协调者实现 应用CI部署、启动、进程维护与管理和反向代理功能,参与者实现 业务请求代理、接受协调者调度

enter image description here
在super-agent架构中,端口是区分服务的唯一标识,端口对客户端而言是透明的,这层端口资源的隔离由super-agent来做掉,因此多个服务可避免在libuv层的互相竞争,提供水平扩容的可能性。

反向代理

super-agent最核心的功能在于反向代理。由于每个服务都被包装成有单独端口的独立HTTP应用,因此当用户请求流量经过前端转发层后进入super-agent,由其根据相关规则做二次转发,目前支持基于 “路径、端口”规则的转发。

部署

后端应用部署需要进行 “优雅降级、流量摘除、健康检查、应用初始化完毕检查、流量导入、所有参与节点的部署状态查询” 等步骤,需要妥善处理;同时,协调者负责协调众多参与节点全部完成部署操作,这是一个分布式事务,需要做好事务出错后的相关业务补偿。

关于流量

上图并未画出节点角色的区别,实际上只有参与者节点才真正接受用户请求。

协调者流量全部来自于内部系统,包括接受 “接口搭建系统”调用或者其他系统实现的dashboard服务;同时其会向参与者发送相关信令信息,如部署、扩容、下线、debug等。

参与者流量来自于内部系统和外部流量,其中大部分来自于外部流量。内部流量则承载协调者的信令信息。

水平扩容

服务的水平扩容重要性不言而喻。super-agent方案中,协调者负责管理服务的扩容与逻辑分组。

此处的服务是通过服务搭建平台通过拖拽生成的nodejs代码,它是一个包含复杂业务逻辑的函数,可以是多文件。具体的,super-agent通过将该服务包装成一个HTTP服务在单独的进程中执行。因此,如果要对服务进行水平扩容,提供多种策略的选择:

  1. 默认每台虚拟机或物理机一个服务进程,总体上N个机器N个服务进程
  2. 扩容时默认每台机器再fork一个服务进程,N机器2*N个服务进程
  3. 为了更充分利用资源,可为每台机器划分为逻辑组,同时选择在某几个组的机器单独扩容

这些策略对下游应用透明,只需选择相关策略即可。

水平扩容的缺点:每台虚拟机或物理机资源有上限,一个正常的node进程最多消耗1.4GB内存,计算资源共享,在一台8C16G的虚拟机上,最多可同时运行16个服务。及时通过分组扩容的方式,每当扩展新的虚拟机或物理机,都需要由super-agent根据分组信息实现进程守护,同时每次服务CI部署也同样如此。运维管理上需要配合非常清晰明了的dashboard后台才能快速定位问题,这点在多服务的问题上尤其突出。

在线调试

super-agent提供消息机制,由搭建平台中组件开发人员使用提供的serverless-toolkit工具debug相关逻辑,最终可在super-agent的协调者后台查看实时debug结果。

总结

super-agent是符合常规的基于业务进程隔离的解决方案,它有效的支撑了微店的几个活动及产品,虽然峰值QPS不高(100左右),但它也论证了super-agent的稳定性及可靠性(线上无事故,服务无重启,平稳升级)。

但是,super-agent仍然存在几个问题,它让我们不得不另觅他法:

  1. 日常运维困难,需要开发一系列后台系统辅助运维,这需要不少人力成本
  2. 这是一个典型的一机多应用场景,当部署super-agent时会对运行其上的服务有所影响(重启),尽管这个影响并不影响用户访问(此时流量已摘除),但仍然是个风险点
  3. 水平扩容实现繁琐,当下掉某几台参与节点时会带来不少影响

基于内核namespace隔离的kubeless方案

基于kubeless的方案则是隔离最为彻底的解决方法,kubeless是建立在K8s之上的serverless框架,因此它可以利用K8s实现一些非常有用的特性:

  1. 敏捷构建 - 能够基于用户提交的源码迅速构建可执行的函数,简化部署流程;
  2. 灵活触发 - 能够方便地基于各类事件触发函数的执行,并能方便快捷地集成新的事件源;
  3. 自动伸缩 - 能够根据业务需求,自动完成扩容缩容,无须人工干预。

其中,自动伸缩则解决了 super-agent 的痛点。

kubeless中与我们紧密相关的有两个概念:“function和runtime” ,function是一个统称,它包括运行时代码、依赖以及其他配置文件;runtime则定义运行时依赖的环境,是一个docker镜像。

若要接入kubeless平台,我们需要解决如下几个问题:

  1. 开发自定义运行时,满足商用需求,如trace、日志分片、上报采集
  2. 自定义构建镜像,实现ts编译
  3. 基于yaml的function创建、更新、删除流程探索,并自动化
  4. function部署,包括流量摘除、流量导入、业务健康检查
  5. 中间件日志、业务日志、trace日志隔离与挂载
  6. function运行时用户权限问题
  7. 水平扩容探索与尝试
  8. 资源申请规范指定与部署规范约定

因此,前进的道路仍然很曲折,而且很多需求需要自己从源码上去寻找解决方法。

一些说明

kubeless实现的serverless体系中,function所在pod中的所有容器共享网络和存储namespace,但是默认外网是不可访问k8s集群的所有pods,因此需要通过一层代理实现请求的转发,这就是“Service”。Service负责服务发现及转发(iptables四层),因此在Kubeless或者K8s中不会直接通过pod IP来访问服务,而是通过Service转发四层流量完成。Service有K8s分配的cluserIp,clusterIp是集群内部虚拟IP,无法被外部寻址,而是通过Kube-Proxy在容器网络之上又抽象了一层虚拟网络,Kube-Proxy负责Service的路由与转发(关于kube-proxy细节,请看参考资料)。

Service后端对应是一个或多个pods,这些pods中的一个容器则运行相同的业务代码。那么流量是如何路由至Service上来呢?这就涉及到Service的“发布”,常用的是Ingress。Ingress包括HTTP代理服务器和ingress controller,代理服务器负责将请求按照规则路由至对应Service,这层需要Kube-Proxy实现虚拟网络的路由;ingress controller负责从K8s API拉取最新的HTTP匹配规则。
enter image description here

问题解决

  1. 自定义镜像:这里的镜像包括两部分:构建镜像和运行时镜像。运行时镜像需要解决宿主代码的鲁棒性,以及提供 livenessProbe、readinessProbe、metric接口实现;构建镜像则负责构建阶段的操作,如编译、依赖安装、环境变量注入等操作。具体编写可参考 implement runtime images。
  2. 构建镜像参考1
  3. 关于function的CRUD操作,笔者先通过命令行走通整个流程后,又切换成基于yaml的配置文件启动,使用yaml启动好处在于:a,可使用kubeless自带的流量导入与摘除策略 b,水平扩容简单 c,命令简单 d,配置文件模板化,自动化
  4. 部署策略由于涉及到业务特点,此处不详细介绍
  5. 日志的挂载是必要的,否则pod一旦重启,容器内的所有日志全部丢失。尽管会存在日志收集的操作,可是日志收集进程大多数都是异步进行,因此会存在丢失日志的情况。因此,必须通过挂载volumn的形式在K8s node上映射文件。但在这过程中会出现权限的问题,这在下一点说明
  6. 权限问题在于kubeless将function的执行权限设置为非root。这是安全且符合常理的设定,但有时function需要root权限,这需要修改K8s的security context配置,需要谨慎处理
  7. 水平扩容基于K8s的HPA组件完成,目前支持基于CPU和QPS等指标进行扩容,目前笔者并未专门测试这项内容,因为它足够可靠
  8. 资源申请的指定需要符合每个公司的实际情况以及业务特点,以node技术栈为例,pod中每个容器设置1C2GB的内存符合实际情况;而至于部署规范,则要兼顾运行时容器的特点,合理配置K8s的node与pod、function的对应关系

总结

运行在kubeless 中的函数和运行在super-agent的代码没有什么不同,可是周边的环境准备可大大不同。为了让kubeless中的function可以接入公司内部中间件服务,笔者费了不少功夫,主要集中在日志及收集部分。好在事在人为,解决的办法总是多于失败的方法。

进度

目前,super-agent方案已承载了10+个线上应用或活动,稳定运行4个月,资源使用率符合预期;

kubeless方案还未正式接入流量,等待进一步做相关异常测试。

参考

kubeless介绍

security-context

kube-proxy

bio: 一站式前端开发工具 - 刘远洋

本文发表在 微店前端团队 blog

bio 是什么

注意:bio 目前只兼容 Mac 平台

前端开发一站式解决方案。

使用 bio,您将只需关注业务逻辑,无需关注脚手架配置信息,即可快速完成前端开发。

额外地,bio 提供了 eslint、styleint 检测、mock 服务。

当前项目为 bio 客户端,bio 核心功能模块地址:https://github.com/weidian-inc/bio-core

github: https://github.com/weidian-inc/bio-cli

npm: https://www.npmjs.com/package/bio-cli

bio 使用前后

安装

快速使用

  • 第 1 步:创建项目目录

    mkdir demo
    
    cd demo
    
  • 第 2 步:初始化各类项目

    • bio init bio-scaffold-vue: 初始化 vue 项目
    • bio init bio-scaffold-react:初始化 react 项目
    • bio init bio-scaffold-pure: 初始化 非 vue / 非 react 项目
  • 第 3 步:调试

    bio run dev-daily
    

命令集

(1)项目开发

  • bio init <脚手架在 npm 源上的名称>

    • 功能

      初始化项目目录。

      该命令会完成以下动作:

      • 在本地安装脚手架,以确保脚手架存在。脚手架安装在 bio 缓存目录(/Users/用户名/.bio/
      • 如果当前目录是空目录(或只有 README.md),该命令会为生成 demo 文件。
      • 执行 npm install
    • 脚手架

      bio 目前内置了三个脚手架:

      bio-scaffold-vue
      bio-scaffold-react
      bio-scaffold-pure
      

      bio 使用 npm 托管脚手架,默认托管在 npm 官方源,您可自行设置托管源,代码地址

    • 脚手架昵称

      bio 为内置的三个脚手架都取了昵称:

      bio-scaffold-vue --> vue
      bio-scaffold-react --> react
      bio-scaffold-pure --> pure
      

      所以所有涉及脚手架名称的命令,均可以用昵称代替。

      您也可以自行添加昵称,代码地址

  • bio run <脚手架支持的任务> [-n, --no-watch]

    • 功能

      启动脚手架任务。

      bio 会启动脚手架,并透传任务名称到脚手架,以完成各类任务。

      所以,任务名称是可变的,只要脚手架支持就可以。

      我们默认提供的三个脚手架都提供了以下 6 种任务:

      dev-daily -- 调试日常环境
      dev-pre  -- 调试预发环境
      dev-prod -- 调试线上环境
      build-daily -- 打包日常环境
      build-pre -- 打包预发环境
      build-prod -- 打包线上环境
      

      详细信息可查看:bio 内置脚手架任务名称

      举例:初始化完 bio-scaffold-vue 项目后,启动它的 dev-daily 任务,命令即为:

      bio run dev-daily
      
    • 选项 -n, --no-watch 介绍:

      bio 默认会 启动 一个文件监听服务,同步当前目录文件到脚手架目录,保证脚手架目录与业务目录始终是父子关系,供脚手架编译。(资料:(为什么要保证父子关系?))

      -n, --no-watch关闭同步当前目录到脚手架目录的文件监听服务。

      举例:

      bio run build-daily -n  打包日常环境,并关闭文件同步监听服务
      

(2)mock

  • bio mock [端口]

    启动本地 mock 服务,默认端口是 7000

    如果希望指定端口号,可以直接指定,如:bio mock 8000

(3)代码质量

  • bio lint init [-t, --type [value]]

    • 功能

      初始化 lint,会自动在 git commit 前挂载 lint 执行钩子

    • 选项 [-t, --type [value]] 介绍

      默认初始化 es6 规则,如果希望在某个目录初始化 es5 功能,可以进入该目录,执行:

      bio lint init -t es5
      

      目前支持两种类型:es5、es6

  • bio lint [--fix] [-w, --watch]

    执行 lint 检查,bio 会为你生成 lint 结果页面进行查看

    • --fix:自动修正源码中的代码格式。
    • -w, --watch:启动文件监听,文件一旦有变化,会触发 lint 检查

(4)脚手架相关

  • bio scaffold show <脚手架在 npm 源上的名称>

    打开脚手架所在的本地目录。

  • bio scaffold create

    创建脚手架,会提示你新的脚手架名称

(5)帮助

  • bio help

    help 信息

bio 的特点

链接

TODO

  • 完善单元测试
  • 持续集成
  • English Docs
  • 完善脚手架项目 demo

开发者

LICENSE

MIT

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.