可谓分开后我爱你就饿

君,已阅读到文档的结尾了呢~~
扫扫二维码,随身浏览文档
手机或平板扫扫即可继续访问
饿了么网站的运营模式
举报该文档为侵权文档。
举报该文档含有违规或不良信息。
反馈该文档无法正常浏览。
举报该文档为重复文档。
推荐理由:
将文档分享至:
分享完整地址
文档地址:
粘贴到BBS或博客
flash地址:
支持嵌入FLASH地址的网站使用
html代码:
&embed src='http://www.docin.com/DocinViewer-4.swf' width='100%' height='600' type=application/x-shockwave-flash ALLOWFULLSCREEN='true' ALLOWSCRIPTACCESS='always'&&/embed&
450px*300px480px*400px650px*490px
支持嵌入HTML代码的网站使用
您的内容已经提交成功
您所提交的内容需要审核后才能发布,请您等待!
3秒自动关闭窗口|||||||||||||||||
&>>&&&&&正文
饿了么被曝光 你还敢随便“饿了么”?
  福香来菜馆经营者:“牌子怎么没挂啊?没有营业执照。”
  三元饺子馆经营者:“没有营业执照我们。”
  这几家本已被关停的餐馆,可谓“名亡实存”,并且有些店可谓生意兴隆。他们如今在大门上或吧台前都贴上了标志,“本店已加盟饿了么”。
  某店主:“如果走饿么的话,就相对贵一点,每单要提百分之十五点走,羊毛出在羊身上,他提了我的点,我肯定要把价格提高一点。”
  调查采访中,记者还发现了一个更为奇怪的现象。在通州万达广场东南角,有一个没有任何牌匾标识的商铺,店铺办公室角落里有一台电脑,饿了么通州北苑地区配送团队的管理人员,平时就在这里用这台电脑上办公。
  每天上午十点,所有配送员上岗前都要在商铺前开早会,然后开工外出接单。可是,每天散会后,配送团队中有三分之一的人并不离开,而是全都进了这家商铺里,排队等候。
  每到这时,就在同一台电脑上,另外一个工作人员在上面操作着。令记者诧异的是他竟然打开了五个“饿了么”网上店铺的接单客户端。分别是久久香便当、ENJOY、hello咖喱、棒棒美食、台北治愈你。而排队等候的配送员,也接连从一个小窗里口,拿出包装好的餐盒,外出送餐。
  饿了么派单员:“一个人两单,该走就走。”
  记者打开饿了么网站找到了相应的店铺。这五家店登记地址各不相同,却都很模糊,五家店都没有上传实体店铺照片,但五家店铺无一例外都登上了方便客户搜索的首页。配送团队负责人向记者道出了这五家店的秘密。
  饿了么配送员:“他就五个店,五个店合起来。”
  饿了么通州北苑区域配送团队负责人:“五个店的餐都是从这里出。”
  原来在这个店铺办公室的后面,还有一间十平方米左右的房间,里面有两个工作人员正在加工着食品。这五家店网上售出的所有外卖食品竟然全部出自于这间小小的厨房。
  五家店店长:“咱们一天多少单,400多单,一天啊,你以为一个月啊。”
  只有两个人,一天里是如何炒制出几百单形色各异饭菜的?这五家网上店铺里登载的一道道看似新鲜艳丽的美食,又是如何加工出来的呢?
责任编辑:孙钊
48小时点击排行
共青团中央主办 共青团中央网络影视中心承办 版权所有:中国青年网  RT,最近加入减肥大军,看到好多JM谈到这个,然后我找度娘问了一下,说是脂肪主要由葡萄糖变成的,淀粉肉类一起吃才能变成葡萄糖。LZ是学生物化学的,对这方面有一点点的常识啊,淀粉在人体内难道不是先变成麦芽糖再变成葡萄糖吗???跟肉类一不一起吃都一样吧。。。  LZ困惑这个问题很久了,有没有神人给我解答一下?????
楼主发言:12次 发图:0张 | 更多
  扯淡的呗
  没有说不会形成脂肪,是形成的量相对少一些。
  @闪电王麦麦 3楼
11:43:50  没有说不会形成脂肪,是形成的量相对少一些。  -----------------------------  那又是为什么啊?
  LZ现在秉承着吃饭不吃肉吃肉不吃饭的原则,都快疯了。。。LZ是无肉不欢啊
  扯淡的。。。还用说  其实肉和菜分开吃,怎么滴不好下饭都少吃点,就瘦了= =
  @闪电王麦麦
11:43:50  没有说不会形成脂肪,是形成的量相对少一些。  -----------------------------  @蓝翎落 4楼
12:01:43  那又是为什么啊?  -----------------------------  具体的不知道怎么解释,但是淀粉跟蛋白质分开吃确实能减肥,当初我用这个方法创下了一个月瘦20斤的记录,很健康并且从未饿过肚子。我只能说,这是众多网传减肥方法里最靠谱的了,我的方法是根据个人情况在台湾mm减肥法的基础上作了改动的,楼主你可以搜来参考下。
  @闪电王麦麦
11:43:50  没有说不会形成脂肪,是形成的量相对少一些。  -----------------------------  @蓝翎落
12:01:43  那又是为什么啊?  -----------------------------  @闪电王麦麦 7楼
13:15:49  具体的不知道怎么解释,但是淀粉跟蛋白质分开吃确实能减肥,当初我用这个方法创下了一个月瘦20斤的记录,很健康并且从未饿过肚子。我只能说,这是众多网传减肥方法里最靠谱的了,我的方法是根据个人情况在台湾mm减肥法的基础上作了改动的,楼主你可以搜来参考下。  -----------------------------  是从多少J瘦下来的?脸色身体状况呢?我刚看了隔壁的贴,对运动有动心。刚刚搜了下台湾MM的
  简单来说就是让身体从燃烧糖类供能的机器变成燃烧蛋白质和脂肪供能的机器  一般而言身体是优先消耗糖类的,效率优于后者  所以阿特金减肥需要连续吃肉类不吃淀粉,使身体慢慢变成燃烧蛋白质和脂肪的状态才能更好的减肥  减肥嘛,减得就是蛋白质和脂肪哦
  如果肉类和淀粉类一起吃,让身体变成消耗脂肪的状态就不能达到,只要一点点淀粉糖类,身体就不会转变成长期燃脂状态,多余的能量只会变成肥肉越吃越肥……  当然如果你吃的量低于基础代谢率也不会肥的啦,减肥效率会相对低一点而已
  我不是学生物化学的,不知道原因,只知道我朋友这么做了减了20斤
  怎么叫分开吃,是上一顿只吃饭,下一顿只吃肉吗?  
  @酱油梅子 9楼
14:11:07  简单来说就是让身体从燃烧糖类供能的机器变成燃烧蛋白质和脂肪供能的机器  一般而言身体是优先消耗糖类的,效率优于后者  所以阿特金减肥需要连续吃肉类不吃淀粉,使身体慢慢变成燃烧蛋白质和脂肪的状态才能更好的减肥  减肥嘛,减得就是蛋白质和脂肪哦  -----------------------------  楼主,这个是正解哦!我记得原理就是这么说的,但我没法完整写出来,看这里看这里!
  @闪电王麦麦
11:43:50  没有说不会形成脂肪,是形成的量相对少一些。  -----------------------------  @蓝翎落
12:01:43  那又是为什么啊?  -----------------------------  @闪电王麦麦
13:15:49  具体的不知道怎么解释,但是淀粉跟蛋白质分开吃确实能减肥,当初我用这个方法创下了一个月瘦20斤的记录,很健康并且从未饿过肚子。我只能说,这是众多网传减肥方法里最靠谱的了,我的方法是根据个人情况在台湾mm减肥法的基础上作了改动的,楼主你可以搜来参考下。  -----------------------------  @蓝翎落 8楼
14:08:00  是从多少J瘦下来的?脸色身体状况呢?我刚看了隔壁的贴,对运动有动心。刚刚搜了下台湾MM的  -----------------------------  130斤瘦下来的,身高166,现在也还是维持比较健康(可能天涯er会觉得胖)的体重。我试过很多种减肥方法,包括21天、绝食、运动,减肥药也吃过,但最终就这个方法让我成功减掉体重,并且没有反弹。  如果你看了台湾mm减肥法,会发现它还是蛮苛刻的,尤其是后两个星期的做法,我非常不推荐。我根据自己的状况调整,减完以后气色还是很好的,大姨妈也是准时来,所以才说很健康。另外运动减肥操作起来太辛苦啦,而且效果很一般,对体重基数大的减肥人士来说还有可能造成关节损伤。健身教练都会建议先减重再运动,有助于身体塑形。
  @抛开偏见 12楼
14:51:02  怎么叫分开吃,是上一顿只吃饭,下一顿只吃肉吗?  -----------------------------  这一口吃主食(面食、米饭),下一口吃肉类。。。。
  @酱油梅子 10楼
14:13:42  如果肉类和淀粉类一起吃,让身体变成消耗脂肪的状态就不能达到,只要一点点淀粉糖类,身体就不会转变成长期燃脂状态,多余的能量只会变成肥肉越吃越肥……  当然如果你吃的量低于基础代谢率也不会肥的啦,减肥效率会相对低一点而已  -----------------------------  那根本是淀粉和肉类不能一起吃,还是糖类和淀粉不能一起呢?要知道糖类包含的单糖二糖多糖基本存在所有事物中,像水果
  @抛开偏见 12楼
14:51:02  怎么叫分开吃,是上一顿只吃饭,下一顿只吃肉吗?  -----------------------------  最起码以天做单位吧,建议妹子也可以看看台湾MM那个,想尝试一下
  @dnhl416 11楼
14:39:08  我不是学生物化学的,不知道原因,只知道我朋友这么做了减了20斤  -----------------------------  看来是有科学根据的啊,动心了,你朋友用了多长时间?
  牛吃草也长肉,你听他瞎扯  
  不是说光吃肉就可以减肥了么  害我这个肉食动物高兴坏了  结果炒了一盘子肉却吃不下去了  悲了个催的
  @酱油梅子
14:11:07  简单来说就是让身体从燃烧糖类供能的机器变成燃烧蛋白质和脂肪供能的机器  一般而言身体是优先消耗糖类的,效率优于后者  所以阿特金减肥需要连续吃肉类不吃淀粉,使身体慢慢变成燃烧蛋白质和脂肪的状态才能更好的减肥  减肥嘛,减得就是蛋白质和脂肪哦  -----------------------------  @闪电王麦麦 15楼
14:52:33  楼主,这个是正解哦!我记得原理就是这么说的,但我没法完整写出来,看这里看这里!  -----------------------------  恩恩,LZ看到了!妹子你真可爱~~~我决定明早去跑步试试,如果觉得不可行就用你的这个办法了!
  @酱油梅子
14:13:42  如果肉类和淀粉类一起吃,让身体变成消耗脂肪的状态就不能达到,只要一点点淀粉糖类,身体就不会转变成长期燃脂状态,多余的能量只会变成肥肉越吃越肥……  当然如果你吃的量低于基础代谢率也不会肥的啦,减肥效率会相对低一点而已  -----------------------------  @蓝翎落 16楼
15:00:34  那根本是淀粉和肉类不能一起吃,还是糖类和淀粉不能一起呢?要知道糖类包含的单糖二糖多糖基本存在所有事物中,像水果  -----------------------------  严格来说,按照杜坎减肥食谱,速效期的话水果也不能吃;但不追求速度的话,糖分相对低的水果吃一点也可以,比淀粉类影响小,淀粉类供能大大高于水果
  回复第8楼(作者:@蓝翎落 于
14:08)   @闪电王麦麦 3楼
11:43:50   没有说不会形成脂肪,是形成…… ==========楼主你学生化的还跑天涯上来问了,这上没几个比你专业的,你应当是回答别人疑问的角色啊  
  @闪电王麦麦
11:43:50  没有说不会形成脂肪,是形成的量相对少一些。  -----------------------------  @蓝翎落
12:01:43  那又是为什么啊?  -----------------------------  @闪电王麦麦
13:15:49  具体的不知道怎么解释,但是淀粉跟蛋白质分开吃确实能减肥,当初我用这个方法创下了一个月瘦20斤的记录,很健康并且从未饿过肚子。我只能说,这是众多网传减肥方法里最靠谱的了,我的方法是根据个人情况在台湾mm减肥法的基础上作了改动的,楼主你可以搜来参考下。  -----------------------------  @蓝翎落 8楼
14:08:00  是从多少J瘦下来的?脸色身体状况呢?我刚看了隔壁的贴,对运动有动心。刚刚搜了下台湾MM的  -----------------------------  我以前减肥是只吃蔬菜和鱼类和过水的鸡肉,早上喝点麦片粥,饿了就喝牛奶,2个月从108收到88
  @抛开偏见 12楼
14:51:02  怎么叫分开吃,是上一顿只吃饭,下一顿只吃肉吗?  -----------------------------  只要同一餐不要饭和肉一起吃就可以,意思是要么吃饭吃蔬菜,要么吃肉吃蔬菜
  @这是一只公马
16:02:18  楼主,这不是小广告  http://blog.sina.com.cn/s/blog_18op0.html  看完收益很大的  -----------------------  多谢!
  节食减肥下来的人表示,真有这么好减肥的方法吗  
  说的我有点动心想尝试这种方法了
  听说长期会引起身体酸中毒?反正影响代谢的。  
  回复第18楼,@葱头团子饭  牛吃草也长肉,你听他瞎扯   --------------------------  牛全身都是瘦肉呀  
  学生化的还要问这种问题吗?无语了,,,,,,,,,,,,,,,,,,,
  @-Tequila- 31楼
18:53:20  学生化的还要问这种问题吗?无语了,,,,,,,,,,,,,,,,,,,  -----------------------------  大一生,理解下。对这个说法一直不解
  好想试试
  可是如果只吃肉的话,对身体很不好的,你可以百度一下,心脏病神马的啊,而且是在青壮年的时候出现的症状  
  @抛开偏见
14:51:02  怎么叫分开吃,是上一顿只吃饭,下一顿只吃肉吗?  -----------------------------  @吅尐吅 25楼
16:05:36  只要同一餐不要饭和肉一起吃就可以,意思是要么吃饭吃蔬菜,要么吃肉吃蔬菜  -----------------------------  不对啊,最起码要以一天为单位
  @这是一只公马 24楼
16:02:18  楼主,这不是小广告  http://blog.sina.com.cn/s/blog_18op0.html  看完收益很大的  -----------------------------  看完了,王老汉是条好汉,谢谢啦!
  @ATPsynthase 22楼
15:27:34  回复第8楼(作者:
@闪电王麦麦
11:43:50   没有说不会形成脂肪,是形成…… ==========楼主你学生化的还跑天涯上来问了,这上没几个比你专业的,你应当是回答别人疑问的角色啊  -----------------------------  LZ大一生一枚,才学了一年,有点粗浅认识。。
  @酱油梅子
14:11:07  简单来说就是让身体从燃烧糖类供能的机器变成燃烧蛋白质和脂肪供能的机器  一般而言身体是优先消耗糖类的,效率优于后者  所以阿特金减肥需要连续吃肉类不吃淀粉,使身体慢慢变成燃烧蛋白质和脂肪的状态才能更好的减肥  减肥嘛,减得就是蛋白质和脂肪哦  -----------------------------  @闪电王麦麦 14楼
14:52:33  楼主,这个是正解哦!我记得原理就是这么说的,但我没法完整写出来,看这里看这里!  -----------------------------  减得是脂肪,没有蛋白质,等到你减蛋白质的时候身体已经不好了,减蛋白质减太多基本离死不远了~~~
其他的正确~~~~
临床医学的飘过~~~
  @闪电王麦麦 2楼
11:43:50  没有说不会形成脂肪,是形成的量相对少一些。  -----------------------------  能不能说一下你减肥的时都怎么吃的?我现在减肥每天吃水煮青菜,可是一斤都没瘦
  同不理解运动45分钟以后才减肥 能量守恒你在哪儿  
  回复第18楼,
@葱头团子饭  牛吃草也长肉,你听他瞎扯  --------------------------  @落落天使 30楼
18:48:49  牛全身都是瘦肉呀  -----------------------------  那牛油是哪来的?
  @沉红拂碧
09:46:44  同不理解运动45分钟以后才减肥 能量守恒你在哪儿   -----------------------------  这个我也不理解  难不成前四十五分钟消耗的都是水分?  等高手解答
  回复第2楼(作者:@闪电王麦麦 于
11:43)  没有说不会形成脂肪,是形成的量相对少一些。  ==========  求楼主方法,谢谢  
  曾三个月不吃淀粉。。大姨妈一直不来,从此再也不敢不吃米面了。。。粗粮不算!  
  好像是蛋白质代谢时需要一个淀粉代谢的中间产物参加才能继续,不吃淀粉的话其实蛋白质的代谢也不完全,影响吸收了吧,长期以蛋白质为主食不吃淀粉会中毒,所以这些方法都不是很健康的。饮食讲求搭配是有道理的。
  妈蛋,干过这事,难熬啊,我不吃肉的,到了第二周只吃蛋白质不给吃淀粉差点没把我搞死。后来抵抗力下降,尿路感染。。。好了后恢复正常饮食了。介个法子不适合我啊啊啊
  头晕晕  
  LZ这种方法叫做阿特金斯减肥法,你可以百度一下。。。  我记得我大学刚毕业,刚参加工作的时候知道了这种方法,就是叫吃肉减肥法,试了一下,确实很有效,才几个月不光体重减轻了,而且人也看上去苗条了好多,而且还不用节食,我是无肉不欢的肉食主义者,哈哈哈。。。当时觉得发明这方法的人简直神了。。。  不过后来没继续了,据说此法还是有副作用的,就是光吃肉,不能跟米面一起吃的,时间长了会脱发,还会大量流失体内的钙质。。。好吧,后来停止了就。。  据说阿特金斯减肥法的创始人最后死的时候两百多斤,额,大家还是谨慎点吧。。。
  我觉得不靠谱吧  除非周期为天,一天吃淀粉一天吃肉  坐等靠这个方法减肥的高手'
  我也不大懂这个。但是好多减肥法都是这个原理。
  其实是真的,吃货的减肥方法,因为我有一个同事中午就是不吃饭,菜吃光,菜很多的,绝对不会饿肚子的,瘦了10斤以上
  坚持不下来
  @-Tequila-
18:53:20  学生化的还要问这种问题吗?无语了,,,,,,,,,,,,,,,,,,,  -----------------------------  @蓝翎落 32楼
09:14:56  大一生,理解下。对这个说法一直不解  -----------------------------  把生化书好好看看啊,汗s了。生化第二部分就是讲三大代谢及其调节啊,(我不知道你这生化专业是都学什么的,大一上了哪些课。)或者说作为传统生化来讲,基本上快一半的内容就是研究物质代谢及其调节的。书上印的以及老师讲的,可以说就是目前在这个地球上,作为人类来说,对于此方面最最最最最最最最最最权威的认知。换句话说就是科学,就是真理。(当下来讲)如果这些都解决不了你的问题,问这些普罗大众和以讹传讹的那些神棍们,更是得不到任何“知识”的。
  求具体方法。  
  回复第8楼,@酱油梅子  简单来说就是让身体从燃烧糖类供能的机器变成燃烧蛋白质和脂肪供能的机器   一般而言身体是优先消耗糖类的,效率优于后者   所以阿特金减肥需要连续吃肉类不吃淀粉,使身体慢慢变成燃烧蛋白质和脂肪的状态才能更好的减肥   减肥嘛,减得就是蛋白质和脂肪哦  --------------------------  蛋白质都减了人都快死了吧?
我记得生物书说人体能量先消耗糖,再是脂肪最后才是蛋白质,真到了消耗蛋白质的时候就完了吧  
  我家现在是蒸米时在大米糙米一起蒸
  回复第3楼(作者:@蓝翎落 于
12:01)  @闪电王麦麦 3楼
11:43:50  没有说不会形成脂肪,是形成的量相……  ==========  这是真的。  
  我搅得是酱紫地  本人吃菜都是喜欢有白饭送的人  比如吃最爱的红烧肉 啊啊  能吃七八块麻将大的五花 送两碗白饭 哦吃撑了 后悔中  但是 光让我吃红烧肉 也许吃第五块就腻 了 就是稀饭白饭吸了猪油和肉一起在口里咀嚼的敢叫 那叫一个香   同样干吭白饭一碗就顶住了  所以分开来吃 实际上是控制住吃的量了 这个才是关键
  好多人问我减肥的方法,我在这一次过回了吧。不过先说明,这个方法是根据我自己的体质、饮食习惯作了一定调整的,不一定对大家都有效,仅供参考吧。  有两类人别看:1、管不住自己嘴的 2、爸爸妈妈爷爷奶奶男友看到减肥心疼的   前提:戒掉一切零食(周黑鸭这类也算)、冷饮(外边奶茶店卖的饮料必戒)。有时候可以适当奖励自己,比如喝一杯自己调的水果牛奶等。  早餐:有多饱吃多饱,只要不撑死自己就成,但拒绝过分油腻的早餐。有条件可以在早餐前喝一杯生姜黑糖水,促进血液循环和消化。  午餐:两个选择,还是跟早餐一样,有条件餐前一杯生姜黑糖水。  A餐:淀粉+蔬菜  B餐:肉+蔬菜  晚餐:根据中午的A餐、B餐做调整。  A餐(对应中午):烫菜(最好自己煮,菇类可以多一些,层主不喜欢菜,所以当时是以菇类跟瓜果类为主,很饱肚)+水果(苹果最佳,糖分高的水果减量)+酸奶  B餐(对应中午):水煮土豆+水果(苹果最佳,糖分高的水果减量)+酸奶  晚餐里的B餐会比A餐饱腹一些,毕竟淀粉能直接提供能量,大家选用A餐的时候,如果肚子饿,稍微忍一忍,因为到了第二天早上又可以吃得饱饱的啦。  附加运动:  晚餐后散步一个小时。  *不建议大家在减体重的时候做大量运动,收效甚微的同时还会对胖子的关节造成损害,尤其是跑步。但如果本身喜欢运动的,还是可以维持一定的运动量。不喜欢的就别勉强自己了,能做到每晚坚持散步一小时已经很不错,循序渐进吧。希望每位按这个方法减肥的筒子都能月瘦10斤~~~  @十分纠结ING @下雨不下雪吗 @掌心的触觉
@骚动的朱砂痣
  扯的,消化肉和淀粉的消化酶都不同,完全2个过程,不知道怎么得出不同时吃就不胖的结论的  
  吃的少是王道  
  @酱油梅子
14:13:42  如果肉类和淀粉类一起吃,让身体变成消耗脂肪的状态就不能达到,只要一点点淀粉糖类,身体就不会转变成长期燃脂状态,多余的能量只会变成肥肉越吃越肥……  当然如果你吃的量低于基础代谢率也不会肥的啦,减肥效率会相对低一点而已  -----------------------------  @蓝翎落 15楼
15:00:34  那根本是淀粉和肉类不能一起吃,还是糖类和淀粉不能一起呢?要知道糖类包含的单糖二糖多糖基本存在所有事物中,像水果  -----------------------------  所以吃蛋白质类食物的时候理论上除了蔬菜跟蛋白质不能吃别的,水果跟奶制品也不能吃,因为有果糖跟乳糖……
  回复第8楼,
@酱油梅子  简单来说就是让身体从燃烧糖类供能的机器变成燃烧蛋白质和脂肪供能的机器  一般而言身体是优先消耗糖类的,效率优于后者  所以阿特金减肥需要连续吃肉类不吃淀粉,使身体慢慢变成燃烧蛋白质和脂肪的状态才能更好的减肥  减肥嘛,减得就是蛋白质和脂肪哦  --------------------------  @才不说我叫什么呢 55楼
22:09:17  蛋白质都减了人都快死了吧?
我记得生物书说人体能量先消耗糖,再是脂肪最后才是蛋白质,真到了消耗蛋白质的时候就完了吧  -----------------------------  怎么老是有人说这个,拜托,减肥的目的是减脂肪没错啊,可是如果运动不够肌肉会有一定程度萎缩好吗,也是常说的减肌肉。而且蛋白质也是正正经经的供能物啊,水解产生能量好么,你以为减肥减得只剩90斤的人消耗的全是水和脂肪别开玩笑了!
  。。。
  留名  
  回复第5楼(作者:@黑历史 于
12:05)  扯淡的。。。还用说  其实肉和菜分开吃,怎么滴不好下饭都少吃点,就瘦了= =  ==========  我觉得这才是正确答案!!  
  楼主、我才学了一门动物生化都不信那些东西、健康饮食多运动吧、瘦了10斤的飘过、、
  回复第18楼,
@葱头团子饭  牛吃草也长肉,你听他瞎扯  --------------------------  @落落天使
18:48:49  牛全身都是瘦肉呀  -----------------------------  @sofia023 40楼
10:19:48  那牛油是哪来的?  -----------------------------  牛奶里提炼出来的
  @沉红拂碧
09:46:44  同不理解运动45分钟以后才减肥 能量守恒你在哪儿  -----------------------------  @jjjinggg 42楼
10:25:02  这个我也不理解  难不成前四十五分钟消耗的都是水分?  等高手解答  -----------------------------  这个中学生物书上好像提过  在有氧运动无氧运动还是肌糖原那一段  说一开始是消耗糖分产生乳酸,要过一段时间消耗的才是脂肪
  大家快八~俺要科学减肥~深深觉得遇到瓶颈了
  误区:碳水(它笼统的称为淀粉)和蛋白质不能同吃,它有两个说法。 第一个是胃蛋白酶和淀粉酶会相互干扰,我想说你太小看人体的消化能力了。 第二个是这两个东西碰一块会生成脂肪,跪了,脂肪最后贮存的是你的剩余能量。 来自http://blog.sina.cn/dpool/blog/v4new/article.php?nid=1bnnm
  @jjjinggg
10:25:02  @沉红拂碧
09:46:44   同不理解运动45分钟以后才减肥 能量守恒你在哪儿   -----------------------------   这个我也不理解   难不成前四十五  -----------------------------  这个书上有说是先耗糖再动员脂肪分解 那个多少分钟应该是因人而异 书上很保守的说 长距离奔跑两个小时以上 机体储存糖份早已消耗尽 此时需动员脂肪分解供能...大概是这么说的 上次看bbc一个片说 运动第二天效果会持续 因为要补充失去的糖还是什么 其实慢跑一小时才消300大卡 蛮少的 如果只动个十分钟减肥效果真的比较不明显吧饿了么前端饿了么前端只是一群热爱技术又爱玩的人在写好用的文章关注专栏更多最新文章{&debug&:false,&apiRoot&:&&,&paySDK&:&https:\u002F\u002Fpay.zhihu.com\u002Fapi\u002Fjs&,&wechatConfigAPI&:&\u002Fapi\u002Fwechat\u002Fjssdkconfig&,&name&:&production&,&instance&:&column&,&tokens&:{&X-XSRF-TOKEN&:null,&X-UDID&:null,&Authorization&:&oauth c3cef7c66aa9e6a1e3160e20&}}{&database&:{&Post&:{&&:{&title&:&vue 状态管理的一点思考&,&author&:&ywwhack&,&content&:&\u003Cp\u003E最近在写项目的时候碰到一个场景:一个地图应用,有个侧边栏,侧边栏里面放着很多选项,选项改变的时候,应用会根据侧边栏的条件请求数据,然后在地图上放 markers 。页面可以简单抽象成如下结构:\u003C\u002Fp\u003E\u003Ccode lang=\&js\&\u003E\u002F\u002F App.vue\n\n&div&\n
&sidebar&&\u002Fsidebar&\n
&map&&\u002Fmap&\n&\u002Fdiv&\u003C\u002Fcode\u003E\u003Cp\u003E有很多种方式可以来完成上面的需求,我先介绍 2 种常见的做法,最后再给出一个比较另类但很有趣的方案。\u003C\u002Fp\u003E\u003Ch2\u003E\u003Cb\u003E1.直接上 vuex\u003C\u002Fb\u003E\u003C\u002Fh2\u003E\u003Cp\u003E通常来讲,有多个组件共享状态的时候,把共享的状态丢给 vuex 来处理是个不错的方案。但是在处理上面那个场景的时候,会显示的有点「笨重」,因为侧边栏实际上是一个表单,\u003Cb\u003E如果使用 vuex 的话,就需要为每个选项定义一套 mutation,失去了直接使用 v-model 的便利\u003C\u002Fb\u003E。 \u003C\u002Fp\u003E\u003Cp\u003E直接使用组件状态绑定一个选项\u003C\u002Fp\u003E\u003Ccode lang=\&js\&\u003E\u002F\u002F sidebar.vue\n\n&input v-model=\&message\&&\n\u002F\u002F ...\ndata () {\n
return {\n
message: ''\n
}\n} \u003C\u002Fcode\u003E\u003Cp\u003E当使用 vuex 绑定一个选项时,多了不少「模板」代码\u003C\u002Fp\u003E\u003Ccode lang=\&js\&\u003E\u002F\u002F 定义 state, mutation\n\nstate: {\n
message: ''\n},\nmutations: {\n
updateMessage (state, message) {\n
state.message = message\n
}\n}\n\n\u002F\u002F sidebar.vue\n\n&input :value=\&message\& @input=\&updateMessage\&&\n\u002F\u002F ...\ncomputed: {\n
...mapState({\n
message: state =& state.message\n
})\n},\nmethods: {\n
updateMessage (e) {\n
this.$store.commit('updateMessage', e.target.value)\n
}\n}\u003C\u002Fcode\u003E\u003Ch2\u003E\u003Cb\u003E2.将状态放到父组件上\u003C\u002Fb\u003E\u003C\u002Fh2\u003E\u003Cp\u003E如果 sidebar 和 map 有一个共同的父级,使用这种方式处理起来会比上面的简单很多。但是当我们的应用越来越大的时候,往往会把 sidebar 和 map 拆成颗粒度更小的组件,那么通过 props 一层层传给子组件也会变的非常麻烦。\u003C\u002Fp\u003E\u003Ch2\u003E\u003Cb\u003E3.将组件状态「共享」出来\u003C\u002Fb\u003E\u003C\u002Fh2\u003E\u003Cp\u003E我们通常把组件内的状态写成下面这种形式:\u003C\u002Fp\u003E\u003Ccode lang=\&js\&\u003E\u002F\u002F ...\ndata () {\n
return {\n
message: ''\n
}\n}\u003C\u002Fcode\u003E\u003Cp\u003E实际上,我们可以把 data () {} 中返回的对象单独提取到外面,作为一个变量,像下面这种写法:\u003C\u002Fp\u003E\u003Ccode lang=\&js\&\u003Econst state = { message: '' }\n\u002F\u002F ...\ndata () {\n
return state\n}\u003C\u002Fcode\u003E\u003Cp\u003E\u003Cb\u003E那么在这个组件初始化过程中,state\u003C\u002Fb\u003E \u003Cb\u003E对象会被 vue 「响应式化」,这会引出一个有趣的事情:任何组件,只要模版中使用了\u003C\u002Fb\u003E \u003Cb\u003Estate.message\u003C\u002Fb\u003E \u003Cb\u003E,当\u003C\u002Fb\u003E \u003Cb\u003Estate.message\u003C\u002Fb\u003E \u003Cb\u003E改变时,页面都会被同步更新。\u003C\u002Fb\u003E\u003C\u002Fp\u003E\u003Cp\u003E知道了这个之后,我们就可以将侧边栏的状态写成一个独立的文件,作为一个模块引入其他组件中,结构如下:\u003C\u002Fp\u003E\u003Ccode lang=\&js\&\u003E\u002F\u002F state.js\nexport default { message: '' }\n\n\n\u002F\u002F sidebar.vue\n&input v-model=\&state.message\& \u002F&\n\nimport state from 'path\u002Fto\u002Fstate.js'\n\u002F\u002F ...\ndata () {\n
return { state }\n}\n\n\n\u002F\u002F map.vue\n\u002F\u002F 在模版中使用侧边栏的状态\n&div&{{ state.message }}&\u002Fdiv&\n\nimport state from 'path\u002Fto\u002Fstate.js'\n\u002F\u002F ...\ndata () {\n
return { state }\n},\ncreated () {\n
\u002F\u002F 将侧边栏的状态作为参数去请求数据\n
axios.get('\u002Fxxxx', { params: state })\n}\u003C\u002Fcode\u003E\u003Cp\u003E这样做的一个好处是,既可以在 sidebar.vue 中把 state 当作「内部」状态,愉快的使用 v-model 绑定数据,map.vue 中也可以方便的拿到 state 做为参数请求,同时还可以直接在 map.vue 的模版中使用 `state.message`。\u003C\u002Fp\u003E\u003Cp\u003E\u003Cb\u003E更进一步\u003C\u002Fb\u003E\u003C\u002Fp\u003E\u003Cp\u003E上面的方案中,state「响应式化」是发生在子组件初始化的过程中,我希望能在应用开始的时候,主动在某个时刻完成这一步。这个时候就可以借助 Vue 构造函数,像下面这样:\u003C\u002Fp\u003E\u003Ccode lang=\&js\&\u003E\u002F\u002F store.js\n\nimport state from 'path\u002Fto\u002Fstate.js'\n\nnew Vue({\n
}\n})\u003C\u002Fcode\u003E\u003Cp\u003E然后在主文件 main.js 中引入 store.js,state 就会被「响应式化」。\u003C\u002Fp\u003E\u003Ch2\u003E\u003Cb\u003E总结\u003C\u002Fb\u003E\u003C\u002Fh2\u003E\u003Cp\u003E本文阐述了在这个特定场景下,对于如何管理侧边栏状态的一些思考,希望大家看完后有所收获 :)\u003C\u002Fp\u003E&,&updated&:new Date(&T08:04:10.000Z&),&canComment&:false,&commentPermission&:&anyone&,&commentCount&:34,&likeCount&:83,&state&:&published&,&isLiked&:false,&slug&:&&,&isTitleImageFullScreen&:true,&rating&:&none&,&sourceUrl&:&&,&publishedTime&:&T16:04:10+08:00&,&links&:{&comments&:&\u002Fapi\u002Fposts\u002F2Fcomments&},&url&:&\u002Fp\u002F&,&titleImage&:&https:\u002F\u002Fpic1.zhimg.com\u002Fv2-114f1bd155d47a3fbfe59dd2eed33551_r.jpg&,&summary&:&&,&href&:&\u002Fapi\u002Fposts\u002F&,&meta&:{&previous&:null,&next&:null},&snapshotUrl&:&&,&commentsCount&:34,&likesCount&:83},&&:{&title&:&聊聊 Git 「改变历史」&,&author&:&ransixi&,&content&:&\u003Cblockquote\u003E非常感谢你为 \u003Ca href=\&https:\u002F\u002Fgithub.com\u002FElemeFE\u002Fmint-ui\&\u003Emint-ui\u003C\u002Fa\u003E 修复了这个 issue。不过你的 commit 信息能修改成如下格式吗?「issue 666: Any message about this issue」。\u003Cbr\u003E当我兴高采烈向 \u003Ca href=\&https:\u002F\u002Fgithub.com\u002FElemeFE\u002Felement\&\u003EElement\u003C\u002Fa\u003E 提交 PR 的时候,维护者告诉我你能把你多个 commits 合并成一个 commit 吗?我们需要保持提交历史清晰明了。\u003Cbr\u003E修复了一个线上 master 分支的Bug,发现这个 Bug 在当前 dev 分支也是存在的,怎么将master分支上的 bugfix 的 commit 移植到 dev 分支呢?\u003C\u002Fblockquote\u003E\u003Cp\u003E其实上面的问题会经常出现在我们的开发过程中,或者是在向一些开源项目提交 PR 的时候。在本篇文章中,我将重现以上问题,聊聊 Git 怎么\u003Cb\u003E改变历史记录\u003C\u002Fb\u003E。\u003C\u002Fp\u003E\u003Ch2\u003E\u003Cb\u003E重写最后一次提交\u003C\u002Fb\u003E\u003C\u002Fh2\u003E\u003Cp\u003E在我们开发的过程中,我们经常会遇到这样的问题,当我们进行了一次「冲动」的 Git 提交后。发现我们的 commit 信息有误,或者我们把不应该这次提交的文件添加到了此次提交中,或者有的文件忘记提交了,怎么办?这些问题都可以通过如下命令来进行弥补。\u003C\u002Fp\u003E\u003Cblockquote\u003Egit commit --amend \u003C\u002Fblockquote\u003E\u003Cp\u003E举个例子,在一个刚初始化的 Git 仓库中,有如下两个文件:\u003C\u002Fp\u003E\u003Ccode lang=\&text\&\u003Etotal 0\n-rw-r--r--
9 19 16:35 should-commit.js\n-rw-r--r--
9 19 16:35 should-not-commit.js\u003C\u002Fcode\u003E\u003Cp\u003E其中 should-commit.js 文件应该被提交,而 should-not-commit.js 不应该被提交,但是由于「冲动」,我把 should-not-commit.js 文件提交了。\u003C\u002Fp\u003E\u003Ccode lang=\&bash\&\u003E# 其实应该添加 should-commit.js 文件\ngit add should-not-commit.js\n# 啊哈,由于笔误,我把 commit 写成了 commmit\ngit commit -m 'commmit 1'\u003C\u002Fcode\u003E\u003Cp\u003E通过 git log 命令打印下当前的历史提交记录:\u003C\u002Fp\u003E\u003Ccode lang=\&text\&\u003Ecommit fbaf325cc0bfcec4c599c (HEAD -& master)\nAuthor: ran.luo03 &ran.luo03@ele.me&\nDate:
Tue Sep 19 16:49:57 \n\n
commmit 1\u003C\u002Fcode\u003E\u003Cp\u003E这样的错误的提交一定不能够给别人看到!是时候该祭出 git commit --amend 了。\u003C\u002Fp\u003E\u003Ccode lang=\&bash\&\u003E# 首先,需要将 should-commit.js 文件添加到暂存区\ngit add should-commit.js\n# 其次,将 should-not-commit.js 文件从已暂存状态转为未暂存状态,不会删除 should-not-commit.js 文件。\ngit rm --cache should-not-commit.js\n# 最后,通过git commit --amend 修改提交信息\ngit commit--amend\u003C\u002Fcode\u003E\u003Cp\u003E当键入 git commit —amend 命令后,会打开 Git 默认编辑器,内容包括了上次错误提交的信息,我们只需将 commmit 1 改为 commit 1 就行了,然后保存退出编辑器。这样我们就完成了错误提交的修改,让我们再通过 git log 来查看一下历史提交记录:\u003C\u002Fp\u003E\u003Ccode lang=\&text\&\u003Ecommit 2adadaff9b98f823b9f239da055637d (HEAD -& master)\nAuthor: ran.luo03 &ran.luo03@ele.me&\nDate:
Tue Sep 19 16:49:57 \n\n
commit 1\u003C\u002Fcode\u003E\u003Cp\u003E啊哈,整个历史记录中只有我最新修改后的历史提交,你完全找不到上一次的提交踪迹了。是不是很酷呢?\u003C\u002Fp\u003E\u003Cp\u003E\u003Cb\u003E思考1:\u003C\u002Fb\u003E怎么使用 git reset 命令修改最后一次提交记录?\u003C\u002Fp\u003E\u003Ch2\u003E\u003Cb\u003E提交的删除、排序、合并操作\u003C\u002Fb\u003E\u003C\u002Fh2\u003E\u003Cp\u003E在一个大型项目中,为了保持提交历史的简洁和可逆,往往一个功能点或者一个 bug fix 对应一个提交,但是在我们实际开发的过程中,我们并不是完成整个功能才进行一次提交的,往往是开发了功能点的一部分,就需要给小伙伴们进行 code review,小的 commit 保证了 code review 的效率和准确性,想象一下如果一次给小伙伴 review 上千行代码,几十个文件,他一定会疯掉的。同时 code review 后的反馈,我们可能需要修改代码,然后再次提交。但是这些提交之间的反复修改不应该体现在最终的 PR 上面,因此, 我们需要根据功能点的前后对 commit 进行排序,对相同功能的commits 进行合并,并删除一些不需要的 commit,根据最终的提交历史提 PR。\u003C\u002Fp\u003E\u003Cp\u003E举个例子,将 王之涣 的 \u003Cb\u003E登鹳雀楼 \u003C\u002Fb\u003E摘抄到我的读书笔记中。\u003C\u002Fp\u003E\u003Cp\u003E首先创建 poem 文件,将「黄河入海流」这句诗添加到了文件中,创建第一个 commit 如下:\u003C\u002Fp\u003E\u003Cp\u003E通过 git log --oneline 命令来看看提交记录。\u003C\u002Fp\u003E\u003Ccode lang=\&text\&\u003Eda5ee49 (HEAD -& master) add 黄河入海流\u003C\u002Fcode\u003E\u003Cp\u003E后来觉得,摘抄一句有些单调,不如将其前面一句也摘抄到笔记中吧,于是又出现了第二个 commit 如下:\u003C\u002Fp\u003E\u003Ccode lang=\&text\&\u003E622c3c8 (HEAD -& master) add 白日依山尽\nda5ee49 add 黄河入海流\u003C\u002Fcode\u003E\u003Cp\u003E...\u003C\u002Fp\u003E\u003Cp\u003E觉得自己太随性,摘抄一首诗竟然添加了如此之多的 commits,commits 如下:\u003C\u002Fp\u003E\u003Ccode lang=\&text\&\u003E953aabb (HEAD -& master) add 文章出处\n7fad941 add 摘抄时间\n731d00b add 作者:王焕之\n9a22044 add 标题:登鹳雀楼\n4fee22a add 更上一层楼\nd1293c5 add 欲穷千里目\n622c3c8 add 白日依山尽\nda5ee49 add 黄河入海流\u003C\u002Fcode\u003E\u003Cp\u003E再看看上面的提交历史,觉得如此多的 commits 确实有些冗余了,commits 的顺序似乎也有些问题,因为 commits 的顺序并不是按照正常摘抄一首诗的顺序来组织的。而且觉得添加摘抄时间有些多余了,因为 git 的历史提交记录就已经帮我记录了添加时间。\u003C\u002Fp\u003E\u003Cp\u003E让我们来一步一步通过「重写历史」来解决上面的问题。\u003C\u002Fp\u003E\u003Cp\u003E这次我使用的命令是 git rebase -i 或者 git rebase - -interactive, Git 官方文档对其如下解释:\u003C\u002Fp\u003E\u003Cblockquote\u003EMake a list of the commits which are about to be rebased. Let the user edit that list before rebasing. This mode can also be used to split commits \u003C\u002Fblockquote\u003E\u003Cp\u003E可以看出,该命令罗列了将要 rebase 的提交记录,打开 Git 设置的编辑器,让用户有更多的选择,可以进行 commit 合并,对 commits 重新排序,删除 commit 等。\u003C\u002Fp\u003E\u003Cp\u003E\u003Cb\u003E第一步:删除「add 摘抄时间」commit\u003C\u002Fb\u003E\u003C\u002Fp\u003E\u003Cp\u003E运行命令\u003C\u002Fp\u003E\u003Cblockquote\u003Egit rebase -i HEAD~2\u003C\u002Fblockquote\u003E\u003Cp\u003EGit 打开默认编辑器,出来如下对话信息:\u003C\u002Fp\u003E\u003Ccode lang=\&bash\&\u003Epick 7fad941 add 摘抄时间\npick 953aabb add 文章出处\n\n# Rebase 731d00b..953aabb onto 731d00b (2 commands)\n#\n# Commands:\n# p, pick = use commit\n# r, reword = use commit, but edit the commit message\n# e, edit = use commit, but stop for amending\n# s, squash = use commit, but meld into previous commit\n# f, fixup = like \&squash\&, but discard this commit's log message\n# x, exec = run command (the rest of the line) using shell\n# d, drop = remove commit\u003C\u002Fcode\u003E\u003Cp\u003E上面的对话信息中包含七条可选命令,很明显最后一条 d,drop 正是我需要的,因为我正打算删除 commit。于是我把第一行中的 pick 命令改为了 drop 命令。\u003C\u002Fp\u003E\u003Ccode lang=\&text\&\u003Edrop 7fad941 add 摘抄时间\npick 953aabb add 文章出处\u003C\u002Fcode\u003E\u003Cp\u003E保存并退出编辑器。\u003C\u002Fp\u003E\u003Ccode lang=\&text\&\u003EAuto-merging poem\nCONFLICT (content): Merge conflict in poem\nerror: could not apply 953aabb... add 文章出处\n\nWhen you have resolved this problem, run \&git rebase --continue\&.\nIf you prefer to skip this patch, run \&git rebase --skip\& instead.\nTo check out the original branch and stop rebasing, run \&git rebase --abort\&.\n\nCould not apply 953aabb... add 文章出处\u003C\u002Fcode\u003E\u003Cp\u003EOMG! 竟然竟然提示 poem 文件中有冲突!打开 poem 文件,手动删除不需要的内容及冲突的标记符号,按照上面的提示,运行 git rebase --continue 命令。心想,这下总该好了吧!\u003C\u002Fp\u003E\u003Ccode lang=\&text\&\u003Epoem: needs merge\nYou must edit all merge conflicts and then\nmark them as resolved using git add\u003C\u002Fcode\u003E\u003Cp\u003Erebase 依然没有成功,原来忘记将解决冲突的修改添加到暂存区了,通过运行 git add 命令后,再次执行 git rebase —continue。\u003C\u002Fp\u003E\u003Cp\u003E出来一个对话框,提示我可以修改 commit 信息,我没有修改,直接保存退出。来看看此时的提交历史记录。\u003C\u002Fp\u003E\u003Ccode lang=\&bash\&\u003Eb8f0233 (HEAD -& master) add 文章出处\n731d00b add 作者:王焕之\n9a22044 add 标题:登鹳雀楼\n4fee22a add 更上一层楼\nd1293c5 add 欲穷千里目\n622c3c8 add 白日依山尽\nda5ee49 add 黄河入海流\u003C\u002Fcode\u003E\u003Cp\u003E和之前的 commits log 信息进行对比,发现 「7fad941 add 摘抄时间」 提交,已经被我成功地删除了,虽然期间有些波折。同时我还注意到了,「add 文章出处」的 SHA1的 hash 值也从 953aabb 变成了 b8f0233。说明,该 commit 是新创建的 commit。\u003C\u002Fp\u003E\u003Cp\u003E\u003Cb\u003E第二步:调整 commits 顺序\u003C\u002Fb\u003E\u003C\u002Fp\u003E\u003Cp\u003E看着上面提交历史记录总会有些别扭,因为不是按照诗本身的顺序来进行提交的,现在我需要修改提交的顺序。好吧,又该是 git rebase -i 命令大显身手的时候到了。\u003C\u002Fp\u003E\u003Cp\u003E但是现在有个问题,git rebase -i 命令并不能够编辑第一个提交。不巧的是,我正需要改变第一个 commit 的顺序,这儿需要一点小技巧,用到
--root 选项,通过该选项,我们就能够编辑初始化的提交了。运行命令如下:\u003C\u002Fp\u003E\u003Cblockquote\u003Egit rebase -i —root\u003C\u002Fblockquote\u003E\u003Cp\u003EGit 再次打开编辑器,提示如下对话信息:\u003C\u002Fp\u003E\u003Ccode lang=\&bash\&\u003Epick da5ee49 add 黄河入海流\npick 622c3c8 add 白日依山尽\npick d1293c5 add 欲穷千里目\npick 4fee22a add 更上一层楼\npick 9a22044 add 标题:登鹳雀楼\npick 731d00b add 作者:王焕之\npick b8f0233 add 文章出处\n\n# Rebase b8f0233 onto a69da76 (7 commands)\n#\n# Commands:\n# p, pick = use commit\n# r, reword = use commit, but edit the commit message\n# e, edit = use commit, but stop for amending\n# s, squash = use commit, but meld into previous commit\n# f, fixup = like \&squash\&, but discard this commit's log message\n# x, exec = run command (the rest of the line) using shell\n# d, drop = remove commit\n# ...\u003C\u002Fcode\u003E\u003Cp\u003E修改上面的提交顺序如下:\u003C\u002Fp\u003E\u003Ccode lang=\&text\&\u003Epick 622c3c8 add 白日依山尽\npick da5ee49 add 黄河入海流\npick d1293c5 add 欲穷千里目\npick 4fee22a add 更上一层楼\npick 9a22044 add 标题:登鹳雀楼\npick 731d00b add 作者:王焕之\npick b8f0233 add 文章出处\n...\u003C\u002Fcode\u003E\u003Cp\u003E然后保存并退出编辑器。\u003C\u002Fp\u003E\u003Cp\u003EOMG, 依然存在冲突,通过第一步的方法,解决冲突,运行 git add . 和 git rebase —continue。最后来看看现在的历史提交记录:\u003C\u002Fp\u003E\u003Ccode lang=\&text\&\u003Eddb6576 (HEAD -& master) add 文章出处\na6e40b3 add 作者:王焕之\nce83346 add 标题:登鹳雀楼\ncae4916 add 更上一层楼\nf79b9ac add 欲穷千里目\nfb65570 add 黄河入海流\n8e25185 add 白日依山尽\u003C\u002Fcode\u003E\u003Cp\u003E\u003Cb\u003E第三步:合并 commits\u003C\u002Fb\u003E\u003C\u002Fp\u003E\u003Cp\u003E添加标题和添加作者貌似应该放到一个 commit 里面,也就是说,我需要将「a6e40b3 add 作者:王焕之」 和 「ce83346 add 标题:登鹳雀楼」 合并成一个提交。这样显得提交更加简洁明晰。\u003C\u002Fp\u003E\u003Cp\u003E依然使用 git rebase -i 命令 :\u003C\u002Fp\u003E\u003Cblockquote\u003Egit rebase -i HEAD~3\u003C\u002Fblockquote\u003E\u003Cp\u003EGit 大概如下对话框:\u003C\u002Fp\u003E\u003Ccode lang=\&bash\&\u003Epick ce83346 add 标题:登鹳雀楼\npick a6e40b3 add 作者:王焕之\npick ddb6576 add 文章出处\n\n# Rebase cae4916..ddb6576 onto cae4916 (3 commands)\n#\n# Commands:\n# p, pick = use commit\n# r, reword = use commit, but edit the commit message\n# e, edit = use commit, but stop for amending\n# s, squash = use commit, but meld into previous commit\n# f, fixup = like \&squash\&, but discard this commit's log message\n# x, exec = run command (the rest of the line) using shell\n# d, drop = remove commit\n# ...\u003C\u002Fcode\u003E\u003Cp\u003E这次我使用的命令是 s, squash。该命令用于合并两个或多个 commits,会将选择的 commit 合并到前一个 commt 中。修改上面对话第二行如下:\u003C\u002Fp\u003E\u003Ccode lang=\&bash\&\u003Epick ce83346 add 标题:登鹳雀楼\nsquash a6e40b3 add 作者:王焕之\npick ddb6576 add 文章出处\u003C\u002Fcode\u003E\u003Cp\u003E然后保存并退出编辑器,啊哈,Git 似乎有点疑惑,它并不知道选择哪个 commit 信息作为合并的最终 commit 信息,于是 Git 打开了新的对话框,让我自己输入新的合并提交信息。\u003C\u002Fp\u003E\u003Ccode lang=\&bash\&\u003E# This is a combination of 2 commits.\n# This is the 1st commit message:\n\nadd 标题:登鹳雀楼\n\n# This is the commit message #2:\n\nadd 作者:王焕之\n\n# ...\u003C\u002Fcode\u003E\u003Cp\u003E修改如下:\u003C\u002Fp\u003E\u003Ccode lang=\&bash\&\u003E# This is a combination of 2 commits.\n# This is the 1st commit message:\n\nadd 标题:登鹳雀楼 作者:王焕之\n\n# This is the commit message #2:\n\n# add 作者:王焕之\n\n# ...\u003C\u002Fcode\u003E\u003Cp\u003E保存上面的修改,并退出编辑器。\u003C\u002Fp\u003E\u003Cp\u003E再来看看最后的历史提交记录。\u003C\u002Fp\u003E\u003Ccode lang=\&text\&\u003E* b907e51 - (2 hours ago) add 文章出处 - ran.luo (HEAD -& master)\n* bd0bfed - (3 hours ago) add 标题:登鹳雀楼 作者:王焕之 - ran.luo\n* cae4916 - (3 hours ago) add 更上一层楼 - ran.luo\n* f79b9ac - (3 hours ago) add 欲穷千里目 - ran.luo\n* fb65570 - (3 hours ago) add 黄河入海流 - ran.luo\n* 8e25185 - (3 hours ago) add 白日依山尽 - ran.luo\u003C\u002Fcode\u003E\u003Cp\u003E啊哈,该历史提交记录终于是我想要的了。\u003C\u002Fp\u003E\u003Cp\u003E\u003Cb\u003E思考2:\u003C\u002Fb\u003E假如通过 rebase 合并了多个 commits 后,发现并不是我们想要的结果,怎么使用 git reset 将其恢复到合并前状态?\u003C\u002Fp\u003E\u003Cp\u003E\u003Cb\u003E思考3:\u003C\u002Fb\u003E 在上面的例子中,由于 git rebase -i 不能够直接编辑第一个提交记录,因而使用了 --root 选项,那么有没有什么\u003Ca href=\&https:\u002F\u002Fstackoverflow.com\u002Fquestions\u002F2Finsert-a-commit-before-the-root-commit-in-git\&\u003E方法\u003C\u002Fa\u003E可以在最初的 commit 之前添加一个 root commit 呢?这样 git rebase -i 就可以直接使用了。\u003C\u002Fp\u003E\u003Cp\u003E\u003Cb\u003E思考4:\u003C\u002Fb\u003E在上面的例子中是分步骤进行提交的删除、排序和合并的,那么可不可以在一次 git\u003C\u002Fp\u003E\u003Cp\u003Erebase -i 的操作中全部完成呢?当然你可能面临着更多的冲突需要解决。\u003C\u002Fp\u003E\u003Ch2\u003E\u003Cb\u003E将其他分支的某个提交附加到当前分支\u003C\u002Fb\u003E\u003C\u002Fh2\u003E\u003Cp\u003E还记得文章开头提及的那个问题吗?修复了一个 master 分支上的线上 Bug,完成了项目的测试发布后,发现当前开发分支 dev 也存在同样的问题,怎么办?是把修复 Bug 的代码从 master 分支上线复制一遍到 dev 分支上,这显然效率不高,而且容易复制错误。还是以一个最小的例子来分析 Git 怎么帮我们解决这个问题。\u003C\u002Fp\u003E\u003Cp\u003E当前版本库有两个分支,master 分支和 dev 分支,master 分支包含一个文件 file1,已经发布到线上,dev 分支是从 master 分支上分离出来的一个新的分支,并且已经完成了新功能的开发,添加了另外一个文件 file2。当前的提交图如下:\u003C\u002Fp\u003E\u003Ccode lang=\&text\&\u003E* 132cabb - (4 minutes ago) dev add file2 - ran.luo (dev)\n* daaae54 - (4 minutes ago) add file1 - ran.luo (HEAD -& master)\u003C\u002Fcode\u003E\u003Cp\u003E上面代码可以看出,当前 HEAD 指向 master 分支,并且发现一个线上 bug,需要紧急修复,我对 file1文件内容进行修改,修复了该 bug。并提交一个新的 commit。当前的提交图如下:\u003C\u002Fp\u003E\u003Ccode lang=\&text\&\u003E* c6607dc - (4 seconds ago) master fix bug - ran.luo (HEAD -& master)\n| * 132cabb - (6 minutes ago) dev add file2 - ran.luo (dev)\n|\u002F\n* daaae54 - (7 minutes ago) add file1 - ran.luo\u003C\u002Fcode\u003E\u003Cp\u003E因为 dev 分支是从 master 分支上分离出来的新分支,因此先前 master 分支上面的 bug 在 dev 分支上也存在,但是又有谁想再次手写代码修复一遍 bug 呢?这时候我们就需要用到 git cherry-pick 命令。Git 官方文档对其解释如下:\u003C\u002Fp\u003E\u003Cblockquote\u003Egit-cherry-pick - Apply the changes introduced by some existing commits\u003C\u002Fblockquote\u003E\u003Cp\u003E由官网文档可知,git-cherry-pick 命令常用于将版本库的一个分支上的特定提交引入到另一个分支上,也就是说,其可以将其他分支带来的改变直接作用到当前分支,这不就是本例所需要的吗?\u003C\u002Fp\u003E\u003Cp\u003E首先需要切换到 dev 分支,由于我们需要的是版本库中 master 分支上面的最新的一个关于 bug fix 的提交,将其附加到 dev 分支后面,使用如下命令:\u003C\u002Fp\u003E\u003Cblockquote\u003Egit cherry-pick master\u003C\u002Fblockquote\u003E\u003Cp\u003E执行完毕后,我们切回 master 分支,再来看看当前的提交图:\u003C\u002Fp\u003E\u003Ccode lang=\&text\&\u003E* 439cb35 - (14 minutes ago) master fix bug - ran.luo (dev)\n* 132cabb - (20 minutes ago) dev add file2 - ran.luo\n| * c6607dc - (14 minutes ago) master fix bug - ran.luo (HEAD -& master)\n|\u002F\n* daaae54 - (21 minutes ago) add file1 - ran.luo\u003C\u002Fcode\u003E\u003Cp\u003E啊哈,成功地将 master 分支的最新提交附加到了 dev 分支上面,又双叒叕一次改变了历史,心中的自豪感悠然而生。\u003C\u002Fp\u003E\u003Cp\u003E\u003Cb\u003E思考5:\u003C\u002Fb\u003E既然 git cherry-pick 可以将某一分支上面的制定提交附加到当前分支上线,那么这样是否可能通过不同的操作顺序来对将要附加的提交进行排序呢?\u003C\u002Fp\u003E\u003Cp\u003E\u003Cb\u003E思考6:\u003C\u002Fb\u003E有时候可能一次需要将版本库中某一分支上面的多个连续的提交一次性地附加到当前分支上面,git cherry-pick (git cherry-pick X..Y)命令是否也能够满足我们的需求呢?\u003C\u002Fp\u003E\u003Ch2\u003E\u003Cb\u003E写在最后\u003C\u002Fb\u003E\u003C\u002Fh2\u003E\u003Cp\u003E当我还沉浸在改变历史的成就中难以自拔的时候,身边大佬的一句话让我清醒过来:「历史(记录)没有因你而变,而只是改变了历史(记录)的呈现方式」。当我查阅了.git\u002Fobjects中的关于记录 commit 的文件后,才发现我还是too young too simple。我并没有改变或删除这些记录 commit 的文件,而只是生成了一些新的 commit 文件,尽然以为我改变了历史记录,可笑!这也是我们为什么能够恢复到改变历史记录前状态的原因,关于Git 中 hash、commit、history 的实质,请参考 \u003Ca href=\&https:\u002F\u002Fzhuanlan.zhihu.com\u002Fp\u002F\&\u003Egit inside --simplified --part '1'\u003C\u002Fa\u003E。\u003C\u002Fp\u003E\u003Cp\u003E\u003Cb\u003EWarning\u003C\u002Fb\u003E\u003C\u002Fp\u003E\u003Cp\u003E改变历史提交记录并非完美,你需要遵循如下准则,只要没有其他开发人员获取到你版本库的副本,或者没有共享你的提交记录,那么你就可以尽情的完善你的提交记录,可以修改提交信息,合并或者拆分多个提交,对多个提交进行排序等等。不过,记住一点,如果你的版本库已经公开,并且其他开发人员已经共享了你的提交记录,那么你就不应该重写、修改该版本库中的任意部分。否则,你的合作者会埋怨你,你的家人和朋友也会嘲笑你、抛弃你。\u003C\u002Fp\u003E&,&updated&:new Date(&T06:24:49.000Z&),&canComment&:false,&commentPermission&:&anyone&,&commentCount&:16,&likeCount&:84,&state&:&published&,&isLiked&:false,&slug&:&&,&isTitleImageFullScreen&:false,&rating&:&none&,&sourceUrl&:&&,&publishedTime&:&T14:24:49+08:00&,&links&:{&comments&:&\u002Fapi\u002Fposts\u002F2Fcomments&},&url&:&\u002Fp\u002F&,&titleImage&:&https:\u002F\u002Fpic3.zhimg.com\u002Fv2-74ad148e4e6fc5b8baae280_r.jpg&,&summary&:&&,&href&:&\u002Fapi\u002Fposts\u002F&,&meta&:{&previous&:null,&next&:null},&snapshotUrl&:&&,&commentsCount&:16,&likesCount&:84},&&:{&title&:&你的网站可以一键变色吗?&,&author&:&lujjjh&,&content&:&\u003Cp\u003E若干年前写过一个叫「网站换色精灵」的小工具,原理是调整网站所有图片的色相、饱和度和亮度。然而并没有什么人用……或许是因为做得不好,又或许这本身就是一种伪需求。\u003C\u002Fp\u003E\u003Cp\u003E得益于 Web 标准的发展和设计风格的变化,前端开发者从通过切图还原设计逐渐变为通过代码还原设计。CSS 预处理器也在一定程度上弥补了 CSS 本身表达能力的不足,许多 UI 框架(比如 \u003Ca href=\&https:\u002F\u002Fgithub.com\u002FElemeFE\u002Felement\u002Fblob\u002Fec3895fdbab2a85e0cfe839dFpackages\u002Ftheme-default\u002Fsrc\u002Fcommon\u002Fvar.css\&\u003EElement\u003C\u002Fa\u003E)将基础的颜色值作为配置项供使用者定制,其余的颜色则在它们的基础上调整亮度\u002F饱和度,或者与其他颜色混合而成。虽说做不到一键变色,但是通过重新构建来改变整个网站的配色是没有问题的。\u003C\u002Fp\u003E\u003Cp\u003E除了可定制,这样做还可以让代码变得更容易维护。相比较充斥着各种颜色值的 CSS 代码,甚至可以表达出一些配色思路(或者让不善于设计的我写出至少配色上还过得去的 UI)。\u003C\u002Fp\u003E\u003Ch2\u003E配色思路?\u003C\u002Fh2\u003E\u003Cp\u003E下面这段样式是从七牛的管理控制台中摘抄的:\u003C\u002Fp\u003E\u003Ccode lang=\&css\&\u003E.btn.btn-primary {\n
color: #1989\n
background-color: rgba(25,137,250,.04);\n
border-color: rgba(25,137,250,.4)\n}\u003C\u002Fcode\u003E\u003Cp\u003E心算一下(Shift + 左击),就能发现上面的三个颜色是同一种颜色,只是透明度不同。\u003C\u002Fp\u003E\u003Cimg src=\&v2-e82ef2a913ac0f79c0ed8da8c4cf4046.gif\& data-rawwidth=\&744\& data-rawheight=\&228\& data-thumbnail=\&https:\u002F\u002Fpic3.zhimg.com\u002Fv2-e82ef2a913ac0f79c0ed8da8c4cf4046_b.jpg\&\u003E\u003Cp\u003E由于页面的背景是纯白的,因此调整颜色透明度可以看成是在调整颜色的亮度。按钮虽然只用了「一种颜色」,但是看起来还是比较和谐的。\u003C\u002Fp\u003E\u003Cp\u003E从中可以看出对主按钮常规状态的设计思路是:\u003C\u002Fp\u003E\u003Col\u003E\u003Cli\u003E使用 #1989fa 作为基础颜色;\u003C\u002Fli\u003E\u003Cli\u003E文字颜色使用基础颜色;\u003C\u002Fli\u003E\u003Cli\u003E将基础颜色调亮 96% 作为背景色;\u003C\u002Fli\u003E\u003Cli\u003E将基础颜色调亮 60% 作为边框的颜色。\u003C\u002Fli\u003E\u003C\u002Fol\u003E\u003Ch2\u003E预处理器?\u003C\u002Fh2\u003E\u003Cp\u003E使用像 Sass 这样的预处理器很容易实现上面的需求:\u003C\u002Fp\u003E\u003Ccode lang=\&css\&\u003E$primary-color: #1989\n\n$text-color: $primary-\n$background-color: scale-color($primary-color, $lightness: 96%);\n$border-color: scale-color($primary-color, $lightness: 60%);\n\n.btn-primary {\n
color: $text-\n
background-color: $background-\n
border-color: $border-\n}\u003C\u002Fcode\u003E\u003Cp\u003ESass 会在编译期间计算出表达式的值,生成这样的 CSS 代码:\u003C\u002Fp\u003E\u003Ccode lang=\&css\&\u003E.btn-primary {\n
color: #1989\n
background-color: #f6\n
border-color: #a3d0\n}\u003C\u002Fcode\u003E\u003Cp\u003E不过,使用预编译器就意味着需要构建——总有一些人不喜欢「构建」过程,或者倾向于使用更「原生」的解决方案。\u003C\u002Fp\u003E\u003Cp\u003E那么,使用纯 CSS 可以在一定程度上实现这样的效果吗?答案是肯定的,七牛管理控制台的例子中就用了透明度来实现提升亮度的效果。问题在于,其中的颜色值出现了多次,可维护性还是不高。\u003C\u002Fp\u003E\u003Ch2\u003ECSS 变量\u003C\u002Fh2\u003E\u003Cp\u003E\u003Ca href=\&https:\u002F\u002Fdeveloper.mozilla.org\u002Fen-US\u002Fdocs\u002FWeb\u002FCSS\u002FUsing_CSS_variables\&\u003ECSS 变量\u003C\u002Fa\u003E是一项实验中的技术,不过现代浏览器大多都已经支持了,所以如果你的网站面向的用户使用的基本都是现代浏览器,可以考虑使用这项技术。后文尝试使用这项技术来描述 UI 的配色,编写更容易维护的纯 CSS。\u003C\u002Fp\u003E\u003Cp\u003E我不打算详细介绍 CSS 变量,如有兴趣可以查阅 MDN 和相关规范。不过不必担心,即便对 CSS 变量了解不多也没关系,后文在用到 CSS 变量时会有一些简单的解释。\u003C\u002Fp\u003E\u003Cp\u003E我打算写一个页面作为例子。\u003C\u002Fp\u003E\u003Ch2\u003E配色\u003C\u002Fh2\u003E\u003Cp\u003E好吧,作为一个不会设计的前端工程师,我准备找一个现成的颜色主题。在 Adobe Color CC 上最受欢迎的颜色主题里挑了个\u003Ca href=\&https:\u002F\u002Fcolor.adobe.com\u002Fzh\u002FQualit%C3%A4t-color-theme-2F?showPublished=true\&\u003E顺眼的\u003C\u002Fa\u003E,就可以开始配色了。有了颜色主题,配色会容易一些,只需要选 3 ~ 4 种颜色,就可以配出一个不错的 UI 了。\u003C\u002Fp\u003E\u003Cimg src=\&v2-109f09dbe0.jpg\& data-rawwidth=\&2076\& data-rawheight=\&546\&\u003E\u003Ch2\u003E背景色和文字颜色\u003C\u002Fh2\u003E\u003Cp\u003E为了确保可读性,只要选出反差和亮度差最大的两种颜色即可。在这个颜色主题里,自然是前两个偏黑白的 #323a40 和 #e5eef4 了。我想做一个暗色的配色,因此选择前者为背景色,后者为文字颜色。\u003C\u002Fp\u003E\u003Ccode lang=\&css\&\u003E:root {\n
--background-color: #323a40;\n
--text-color: #e5eef4;\n}\u003C\u002Fcode\u003E\u003Cp\u003ECSS 变量以两个连字符开头,定义 CSS 变量与设置属性类似。上面这段代码定义了 --background-color 和 --text-color 这两个 CSS 变量。:root 选择器会选择根节点(也就是 &html&),与 html 的区别在于优先级更高,适合用于定义全局 CSS 变量。\u003C\u002Fp\u003E\u003Ccode lang=\&css\&\u003Ehtml {\n
background: var(--background-color);\n
color: var(--text-color);\n}\u003C\u002Fcode\u003E\u003Cp\u003E要引用定义的 CSS 变量也很简单,只需要使用 var 函数即可。这样,页面的背景色和文字颜色就设置好了。\u003C\u002Fp\u003E\u003Cimg src=\&v2-e165f895cfc4f977f13c5.jpg\& data-rawwidth=\&780\& data-rawheight=\&280\&\u003E\u003Cp\u003E\u003Ca href=\&https:\u002F\u002Fjsfiddle.net\u002Flujjjh\u002FLt66mq0x\u002F\&\u003E在 JSFiddle 上 DIY\u003C\u002Fa\u003E\u003C\u002Fp\u003E\u003Ch2\u003E主色\u003C\u002Fh2\u003E\u003Cp\u003E\u003Cb\u003E然后,选择一个主色。\u003C\u002Fb\u003E主色通常被用在超链接、主按钮、logo 上。为了它们更突出,应该选择一个与背景色和文字颜色都有一定反差的颜色。这里,我选择颜色主题中的第三个颜色 #37b0c0。\u003C\u002Fp\u003E\u003Ccode lang=\&css\&\u003E:root {\n
--background-color: #323a40;\n
--text-color: #e5eef4;\n
--primary-color: #37b0c0;\n\n
--input-size: 30\n
--input-padding-horizontal: 10\n
--button-border-radius: 4\n}\n\nbutton {\n
background:\n
border: 1px solid var(--primary-color);\n
border-radius: var(--button-border-radius);\n
color: var(--primary-color);\n
height: var(--input-size);\n
padding-left: var(--input-padding-horizontal);\n
padding-right: var(--input-padding-horizontal);\n
transition: all .15\n}\n\nbutton:hover {\n
background: var(--primary-color);\n
color: var(--background-color);\n
cursor:\n}\u003C\u002Fcode\u003E\u003Cp\u003ECSS 变量不仅可以定义颜色值,上面的代码还用 CSS 变量定义了按钮的大小、内边距和边框的半径。\u003C\u002Fp\u003E\u003Cimg src=\&v2-f3dace0de7c1cbc6b009e.jpg\& data-rawwidth=\&676\& data-rawheight=\&280\&\u003E\u003Cp\u003E\u003Ca href=\&https:\u002F\u002Fjsfiddle.net\u002Flujjjh\u002FLt66mq0x\u002F1\u002F\&\u003E在 JSFiddle 上 DIY\u003C\u002Fa\u003E\u003C\u002Fp\u003E\u003Ch2\u003E透明度\u003C\u002Fh2\u003E\u003Cp\u003ECSS 里并没有像 Sass 里 darken、lighten 那样的颜色函数,可以考虑使用透明度在一定程度上实现加深或者减淡的效果。不幸的是,CSS 里同样也没有操作颜色透明度的函数。我们只能把颜色的三个分量拆开定义:\u003C\u002Fp\u003E\u003Ccode lang=\&css\&\u003E:root {\n
--background-color-r: 51;\n
--background-color-g: 59;\n
--background-color-b: 64;\n
--background-color: rgb(\n
var(--background-color-r),\n
var(--background-color-g),\n
var(--background-color-b)\n
--text-color-r: 229;\n
--text-color-g: 238;\n
--text-color-b: 244;\n
--text-color: rgb(\n
var(--text-color-r),\n
var(--text-color-g),\n
var(--text-color-b)\n
--primary-color-r: 62;\n
--primary-color-g: 176;\n
--primary-color-b: 190;\n
--primary-color: rgb(\n
var(--primary-color-r),\n
var(--primary-color-g),\n
var(--primary-color-b)\n
--input-size: 30\n
--input-padding-horizontal: 10\n
--button-border-radius: 4\n}\u003C\u002Fcode\u003E\u003Cp\u003E是的,这么定义很麻烦。不过,每个颜色值还是只会出现一次。\u003C\u002Fp\u003E\u003Ccode lang=\&css\&\u003Einput, button {\n
--border-color: rgba(\n
var(--text-color-r),\n
var(--text-color-g),\n
var(--text-color-b),\n
var(--border-color-alpha, .3)\n
border: 1px solid var(--border-color);\n}\u003C\u002Fcode\u003E\u003Cp\u003E其中,var(--border-color-alpha, .3) 表示引用 --border-color-alpha 变量的值,如果变量没有定义或者无效,则回退到 .3。这样一来,input 和 button 的边框颜色会变成背景色混合 30% 的文本颜色。\u003C\u002Fp\u003E\u003Ccode lang=\&css\&\u003Einput:focus {\n
--border-color-alpha: .6;\n}\u003C\u002Fcode\u003E\u003Cp\u003E当焦点在 input 上时,--border-color-alpha 的值将变为 .6,此时边框颜色会变成背景色混合 60% 的文本颜色。\u003C\u002Fp\u003E\u003Cp\u003E我使用同样的方法写了一个友好的 header。\u003C\u002Fp\u003E\u003Cimg src=\&v2-927f6c74a776bc6dd0c88a.jpg\& data-rawwidth=\&756\& data-rawheight=\&462\&\u003E\u003Cp\u003E\u003Ca href=\&https:\u002F\u002Fjsfiddle.net\u002Flujjjh\u002FLt66mq0x\u002F4\u002F\&\u003E在 JSFiddle 上 DIY\u003C\u002Fa\u003E\u003C\u002Fp\u003E\u003Ch2\u003E白天主题\u003C\u002Fh2\u003E\u003Cp\u003E产品经理找到我说,大多数程序员都觉得我做的页面很友好,但是少数非夜猫子程序员觉得这个主题在白天太刺眼了,希望能有一个「白天主题」。\u003C\u002Fp\u003E\u003Cp\u003E好在 JavaScript 可以设置 CSS 变量的值,而白天主题只需要把背景颜色和文字颜色互换就可以了。\u003C\u002Fp\u003E\u003Ccode lang=\&js\&\u003Econst themes = [\n
name: 'dark',\n
scheme: {\n
'--background-color-r': 51,\n
'--background-color-g': 59,\n
'--background-color-b': 64, \n
'--text-color-r': 229,\n
'--text-color-g': 238,\n
'--text-color-b': 244\n
name: 'light',\n
scheme: {\n
'--background-color-r': 229,\n
'--background-color-g': 238,\n
'--background-color-b': 244, \n
'--text-color-r': 51,\n
'--text-color-g': 59,\n
'--text-color-b': 64\n
}\n];\n\nlet currentTheme = 0;\nwindow.nextTheme = function () {\n
currentTheme = (currentTheme + 1) % themes.\n
const theme = themes[currentTheme];\n
Object.keys(theme.scheme).forEach(name =& {\n
const value = theme.scheme[name];\n
document.documentElement.style.setProperty(name, value);\n
});\n}\u003C\u002Fcode\u003E\u003Cimg src=\&v2-fe423ce1a86c9ff9f2a9.gif\& data-rawwidth=\&600\& data-rawheight=\&352\& data-thumbnail=\&https:\u002F\u002Fpic2.zhimg.com\u002Fv2-fe423ce1a86c9ff9f2a9_b.jpg\&\u003E\u003Cp\u003E\u003Ca href=\&https:\u002F\u002Fjsfiddle.net\u002Flujjjh\u002FLt66mq0x\u002F5\u002F\&\u003E在 JSFiddle 上 DIY\u003C\u002Fa\u003E\u003C\u002Fp\u003E\u003Ch2\u003E颜色混合\u003C\u002Fh2\u003E\u003Cp\u003E透明度不能解决所有问题,如果需要和另一种颜色混合(单纯与黑白混合可以考虑使用 HSL 模型),或者需要渐变,就只能使用一些「黑科技」了。\u003C\u002Fp\u003E\u003Cp\u003E比如说,想把背景颜色设置为 50% 文字颜色 + 50% 主色:\u003C\u002Fp\u003E\u003Ccode lang=\&css\&\u003E... {\n
--base-color: var(--text-color);\n
--mix-color: rgba(\n
var(--primary-color-r),\n
var(--primary-color-g),\n
var(--primary-color-b),\n
background-color: var(--base-color);\n
background-image: linear-gradient(\n
to bottom,\n
var(--mix-color),\n
var(--mix-color)\n
);\n}\u003C\u002Fcode\u003E\u003Ch2\u003E已知问题\u003C\u002Fh2\u003E\u003Cp\u003E除了用起来不如 CSS 预处理器方便之外,Safari 在某些情况下无法工作。比如说\u003C\u002Fp\u003E\u003Ccode lang=\&css\&\u003E:root {\n
--r: 255;\n
--b: 0;\n}\n\n.foo {\n
border: 10px solid\n
rgba(var(--r), var(--g), var(--b), .5);\n}\u003C\u002Fcode\u003E\u003Cp\u003E在 Safari 下边框会被渲染为 currentColor 而不是半透明的红色。\u003C\u002Fp\u003E\u003Cp\u003E解决方法很简单,在内部多定义一个 CSS 变量即可。\u003C\u002Fp\u003E\u003Cp\u003E\u003Ca href=\&https:\u002F\u002Fjsfiddle.net\u002Flujjjh\u002Fwyme15mc\u002F\&\u003E在 JSFiddle 上 DIY\u003C\u002Fa\u003E(请对比在 Chrome 中和 Safari 中的表现)\u003C\u002Fp\u003E&,&updated&:new Date(&T05:30:30.000Z&),&canComment&:false,&commentPermission&:&anyone&,&commentCount&:25,&likeCount&:169,&state&:&published&,&isLiked&:false,&slug&:&&,&isTitleImageFullScreen&:false,&rating&:&none&,&sourceUrl&:&&,&publishedTime&:&T13:30:30+08:00&,&links&:{&comments&:&\u002Fapi\u002Fposts\u002F2Fcomments&},&url&:&\u002Fp\u002F&,&titleImage&:&https:\u002F\u002Fpic1.zhimg.com\u002Fv2-fe423ce1a86c9ff9f2a9_r.jpg&,&summary&:&&,&href&:&\u002Fapi\u002Fposts\u002F&,&meta&:{&previous&:null,&next&:null},&snapshotUrl&:&&,&commentsCount&:25,&likesCount&:169},&&:{&title&:&Mongo 代理程序实现-代码实战篇&,&author&:&geemo-66&,&content&:&\u003Cp\u003E延续上一篇文章 \u003Ca href=\&https:\u002F\u002Fzhuanlan.zhihu.com\u002Fp\u002F\&\u003EMongo 代理程序实现-复制集搭建及抓包篇\u003C\u002Fa\u003E,接下来,就正式开始我们的代码实战。\u003C\u002Fp\u003E\u003Cp\u003E根据一贯的风格,我们先来梳理下项目目录结构,结构如下:\u003C\u002Fp\u003E\u003Ccode lang=\&text\&\u003E.\n|__ bin\u002F
# 用于存放编译后生成的二进制文件\n|__ config\u002F
# 用于存放配置文件\n|__ connection\u002F
# 存放连接相关的文件\n|
|__ proxy.go
# 代理组件\n|
|__ pool.go
# 连接池组件\n|
|__ repl_set.go
# 复制集组件\n|
|__ conn.go
# 连接对象组件\n|__ internal\u002F
# 存放 mongo 内部协议相关文件\n|
|__ auth.go
# 握手鉴权组件\n|
|__ protocol.go
# 协议解析组件\n|
|__ request.go
# 请求重写组件\n|
|__ response.go
# 响应重写组件\n|__ statistics\u002F
# 存放指标统计上报组件\n|__ test\u002F
# 存放各种语言驱动测试代码的文件夹\n|__ utils\u002F
# 工具函数文件夹\n|__ glide.yaml
# 依赖包配置文件\n|__ main.go
# 入口文件\u003C\u002Fcode\u003E\u003Cp\u003E限于篇幅的原因,不可能把上面的细节一一讲个遍,我只挑选 \u003Cb\u003Eproxy、pool\u003C\u002Fb\u003E 两个组件来讲...想了解更多实现细节的童鞋,可以私信我。\u003C\u002Fp\u003E\u003Ch2\u003E\u003Cb\u003Eproxy 实现\u003C\u002Fb\u003E\u003C\u002Fh2\u003E\u003Cp\u003E最简单的 proxy 实现套路就像下面这样:\u003C\u002Fp\u003E\u003Ccode lang=\&go\&\u003E\u002F\u002F main.go\nfunc main() {\n
\u002F\u002F 传入配置参数,实例化一个代理对象\n
p := NewProxy(conf)\n
\u002F\u002F 卡住,循环接受客户端请求\n
p.LoopAccept()\n}\u003C\u002Fcode\u003E\u003Cp\u003E接着来实现 NewProxy、LoopAccept 方法:\u003C\u002Fp\u003E\u003Ccode lang=\&go\&\u003E\u002F\u002F connection\u002Fproxy.go\ntype Proxy struct {\n
sync.RWMutex\n\n
net.Listener\n
writePool, readPool *pool\n}\n\nfunc NewProxy(conf config.UserConf) *Proxy {\n
\u002F\u002F 开始监听本地端口\n
listener, err := net.Listen(\&tcp\&, \&:\&+conf.GetString(\&port\&))\n
if err != nil {\n
log.Fatalln(err)\n
p := &Proxy{\n
listener: listener,\n
\u002F\u002F 实例化连接池\n
p.readPool, p.writePool, err = newPool(p)\n
if err != nil {\n
panic(err)\n
return p \n}\n\nfunc (p *Proxy) LoopAccept() {\n
client, err := p.listener.Accept()\n\n
go func(c net.Conn) {\n
defer c.Close()\n\n
\u002F\u002F 一个连接在多次 messageHandler 中共用一个 Reader 对象\n
cr := bufio.NewReader(c)\n
\u002F\u002F 因为一个连接可能会进行多次读或写操作\n
\u002F\u002F 将客户端请求代理给服务端,服务端响应代理回客户端\n
\u002F\u002F 同时中间对请求或响应进行重写操作\n
err := p.messageHandler(cr, c)\n\n
if err != nil {\n
\u002F\u002F 只要出现错误,就执行到上面的 defer c.Close() 来关闭连接\n
}(client)\n
}\n}\u003C\u002Fcode\u003E\u003Cp\u003E接着来实现核心逻辑 messageHandler:\u003C\u002Fp\u003E\u003Ccode lang=\&go\&\u003E\u002F\u002F connection\u002Fproxy.go\nfunc (p *Proxy) messageHandler(cr *bufio.Reader, c net.Conn) error {\n
\u002F\u002F 对请求报文进行解析操作\n
req, err := internal.Decode(clientReader)\n
if err != nil {\n
return errors.New(\&decode error\&)\n
\u002F\u002F 将客户端请求发送给数据库服务器\n
res, err := p.clientToServer(req)\n
if err != nil {\n
return errors.New(\&request error\&)\n
\u002F\u002F 将数据库服务器响应返回给客户端\n
return res.WriteTo(c)\n}\n\nfunc (p *Proxy) clientToServer(req *internal.Message) (*internal.Message, error) {\n
var server net.Conn\n
\u002F\u002F 如果是读操作,就从读池中取出连接\n
if req.IsReadOp() {\n
host := req.GetHost()\n
\u002F\u002F 某些读操作需要发送到指定的读库上,所以需要传 host,来获取指定读库连接\n
server = p.readPool.Acquire(host)\n
\u002F\u002F 反之,写操作从写池中取出连接\n
} else {\n
\u002F\u002F 由于写库只有一个,所以不用传 host 参数了\n
server = p.writePool.Acquire()\n
\u002F\u002F 将客户端请求发送给数据库服务器\n
err := req.WriteTo(server)\n
if err != nil {\n
return nil, err\n
\u002F\u002F 获取解析数据库服务器响应\n
res, err := internal.Decode(bufio.NewReader(server))\n
return res, err\n}\u003C\u002Fcode\u003E\u003Cp\u003E大致逻辑就是,客户端通过代理把请求发给服务端,服务端响应也通过代理响应回客户端。\u003C\u002Fp\u003E\u003Ccode lang=\&text\&\u003E------------
-----------
------------\n|
| --------& |
| --------& |
| repl_set |\n|
| &-------- |
| &-------- |
|\n------------
response -----------
response ------------\u003C\u002Fcode\u003E\u003Cp\u003E呐~,当然还有非常多的细节,由于篇幅原因不得不省略...\u003C\u002Fp\u003E\u003Ch2\u003E\u003Cb\u003Epool 实现\u003C\u002Fb\u003E\u003C\u002Fh2\u003E\u003Cp\u003E由 proxy 的代码逻辑来看,我们取读或写库连接是通过读或写池的 Acquire 方法来取的:\u003C\u002Fp\u003E\u003Ccode lang=\&go\&\u003E\u002F\u002F connection\u002Fpool.go\ntype pool struct {\n
sync.RWMutex\n\n
chan net.Conn\n
func(string) (net.Conn, error)\n
freeConn func(net.Conn) error\n}\n\nfunc (p *pool) Acquire(opts ...interface{}) (net.Conn, error) {\n
host := \&\&\n
if len(opts) & 0 {\n
host, _ = (opts[0]).(string)\n
chLen := len(p.connCh)\n
\u002F\u002F 从 channel 中遍历剩余数量的 conn\n
for i := 0; i & chL i++ {\n
select {\n
case conn, ok := &- ch:\n
if len(host) & 0 {\n
if conn.RemoteAddr().String() == host {\n
return conn, nil\n
\u002F\u002F 没有找到对应 host 的 conn,则把 conn 重新放回 channel\n
\u002F\u002F 你可以简单理解为只是执行了 p.connCh &- conn 操作\n
p.freeConn(conn)\n
} else {\n
return conn, nil\n
\u002F\u002F 避免数量不足而导致 channel 阻塞等待\n
default:\n
\u002F\u002F 若还没有从 channel 中取到 conn,则立马 new 一个\n
conn, err := p.newConn(host)\n
if err != nil {\n
return nil, err\n
return conn, nil\n}\u003C\u002Fcode\u003E\u003Cp\u003E池的实现大致就是实现了一个循环队列,连接从池中取,取出的连接在使用完后,可以放回池中。\u003C\u002Fp\u003E\u003Ch2\u003E\u003Cb\u003E总结\u003C\u002Fb\u003E\u003C\u002Fh2\u003E\u003Cp\u003E聪明的童鞋可能已经看出,我在定义各种 struct 的时候,基本没有添加什么状态量,因为在并发场景下,对状态量的把控不好会导致一些很严重的问题,读者可以自由发挥设计功底,使用 atomic 或 go 1.9 提供的 sync.Map 等无锁操作来解决这些问题。 \u003C\u002Fp\u003E\u003Ch2\u003E\u003Cb\u003E结束语\u003C\u002Fb\u003E\u003C\u002Fh2\u003E\u003Cp\u003E一溜写下来,看过抓包篇的童鞋可能会说,mmp 你根本就没讲如何实现自动主备切换的逻辑。我表示确实是立了个大 flag (老脸一红...\u003C\u002Fp\u003E\u003Cp\u003E但我要真的一字一句写下来,恐怕很多人看都不想看,文章篇幅就是要简短明了,才有看下去的勇气。当然你真想知道细节,可以私信我,我一定知而\u003Cb\u003E不\u003C\u002Fb\u003E答(233。\u003C\u002Fp\u003E&,&updated&:new Date(&T03:01:20.000Z&),&canComment&:false,&commentPermission&:&anyone&,&commentCount&:2,&likeCount&:11,&state&:&published&,&isLiked&:false,&slug&:&&,&isTitleImageFullScreen&:true,&rating&:&none&,&sourceUrl&:&&,&publishedTime&:&T11:01:20+08:00&,&links&:{&comments&:&\u002Fapi\u002Fposts\u002F2Fcomments&},&url&:&\u002Fp\u002F&,&titleImage&:&https:\u002F\u002Fpic4.zhimg.com\u002Fv2-0ac6cbc4d45d29eb5e15_r.jpg&,&summary&:&&,&href&:&\u002Fapi\u002Fposts\u002F&,&meta&:{&previous&:null,&next&:null},&snapshotUrl&:&&,&commentsCount&:2,&likesCount&:11},&&:{&title&:&HTTP 缓存机制一二三&,&author&:&a-mie-60&,&content&:&\u003Cp\u003EWeb 缓存大致可以分为:数据库缓存、服务器端缓存(代理服务器缓存、CDN 缓存)、浏览器缓存。\u003C\u002Fp\u003E\u003Cp\u003E浏览器缓存也包含很多内容: HTTP 缓存、indexDB、cookie、localstorage 等等。这里我们只讨论 HTTP 缓存相关内容。\u003C\u002Fp\u003E\u003Cp\u003E\u003Cbr\u003E\u003C\u002Fp\u003E\u003Cp\u003E在具体了解 HTTP 缓存之前先来明确几个术语:\u003C\u002Fp\u003E\u003Cul\u003E\u003Cli\u003E缓存命中率:从缓存中得到数据的请求数与所有请求数的比率。理想状态是越高越好。\u003C\u002Fli\u003E\u003Cli\u003E过期内容:超过设置的有效时间,被标记为“陈旧”的内容。通常过期内容不能用于回复客户端的请求,必须重新向源服务器请求新的内容或者验证缓存的内容是否仍然准备。\u003C\u002Fli\u003E\u003Cli\u003E验证:验证缓存中的过期内容是否仍然有效,验证通过的话刷新过期时间。\u003C\u002Fli\u003E\u003Cli\u003E失效:失效就是把内容从缓存中移除。当内容发生改变时就必须移除失效的内容。\u003C\u002Fli\u003E\u003C\u002Ful\u003E\u003Cp\u003E\u003Cbr\u003E\u003C\u002Fp\u003E\u003Cp\u003E浏览器缓存主要是 HTTP 协议定义的缓存机制。HTML meta
标签,例如\u003C\u002Fp\u003E\u003Ccode lang=\&js\&\u003E&META HTTP-EQUIV=\&Pragma\& CONTENT=\&no-store\&&\u003C\u002Fcode\u003E\u003Cp\u003E含义是让浏览器不缓存当前页面。但是代理服务器不解析 HTML 内容,一般应用广泛的是用 HTTP 头信息控制缓存。\u003C\u002Fp\u003E\u003Cp\u003E\u003Cbr\u003E\u003C\u002Fp\u003E\u003Ch2\u003E\u003Cb\u003EHTTP 头信息控制缓存\u003C\u002Fb\u003E\u003C\u002Fh2\u003E\u003Cp\u003E大致分为两种:强缓存和协商缓存。强缓存如果命中缓存不需要和服务器端发生交互,而协商缓存不管是否命中都要和服务器端发生交互,强制缓存的优先级高于协商缓存。具体内容下文介绍。\u003C\u002Fp\u003E\u003Cp\u003E\u003Cbr\u003E\u003C\u002Fp\u003E\u003Cp\u003E匹配流程(已有缓存的情况下):\u003C\u002Fp\u003E\u003Cimg src=\&v2-d322ec21b46f5cc709d61a.jpg\& data-caption=\&\& data-rawwidth=\&401\& data-rawheight=\&861\&\u003E\u003Cp\u003E\u003Cbr\u003E\u003C\u002Fp\u003E\u003Ch2\u003E\u003Cb\u003E强缓存\u003C\u002Fb\u003E\u003C\u002Fh2\u003E\u003Cp\u003E可以理解为无须验证的缓存策略。对强缓存来说,响应头中有两个字段 Expires\u002FCache-Control 来表明规则。\u003C\u002Fp\u003E\u003Cp\u003E\u003Cbr\u003E\u003C\u002Fp\u003E\u003Cp\u003E\u003Cb\u003EExpires\u003C\u002Fb\u003E\u003C\u002Fp\u003E\u003Cp\u003EExpires 指缓存过期的时间,超过了这个时间点就代表资源

我要回帖

更多关于 我饿 的文章

 

随机推荐