
社区管理员
注册成为云大使 全职云大使刘凯的上云经验 一年一度的阿里云购物盛典双11终于结束了,在这场大型的活动中相信有很多人“元气大伤”有的人“盆满钵盈”。然而在今年的阿里云云大使双十一拼团赛中第一名的团长大使光是瓜分最后的百万现金就瓜分了近5万的奖金,还有32名团长在组团中获得1111元组团红包超过2次。大使团长最多的在这次活动中佣金累计获得超过11万。随着越来越多大使的加入,云大使已经成为我们这些推广云计算人的代名词,今日小编有幸采访到拼团活动前100名中的一位优秀大使——刘凯,在此次拼团活动中获得第83名的成绩,整理出来了一些大使的经验分享给大家。 刘凯是一位全职的云大使,做云大使已经一年多了。另外平时没事的时候自己还会接一些建站类的业务,平时最大的兴趣就是琢磨研究建站技术,因为他相信只有自己足够了解在给客户介绍的时候才会很专业,客户才会认可。 据刘凯介绍他的获得渠道一般都是:1:以文章,QQ群,百度知道,博客园 CSDN 知乎等大平台推广为主;2:而且有自己的网站(www.568ys.com)在网站上发布活动链;3:线下客户(由于以前在IDC公司上班多年,还是有一定客户关系和群体),是一位典型的流量型大使。 图片来源:刘凯网站截图 成功路上的无限可能 在推广的过程中总是会遇到种种的困难来考验我们的能力。刘凯在推广路上最大的困难就是很多的平台不喜欢软文,广告类的文章。一来二去的刘凯也逐渐的找到了一些小窍门在这里也分享给大家。 就是多发文章,总会有一些没有被发现的文章可以通过。 多账号发布:避免一个账号被停,全部文章都要被删的情况。 多去了解各大平台的发布内容的标准,尽量在规则允许的情况下去推广阿里云的产品和活动。 在采访中,刘凯也和我们分享了一下他推广成功的小案例,希望能给其他的大使一些灵感。 客户首先比较纠结于购买阿里云服务器还是腾讯或者百度的云服务器,通过跟客户微信聊天,通过介绍阿里云的技术,市场占有率,以及双11活动等推荐客户购买阿里云的服务器,同时跟客户介绍了本人自己和其他客户使用阿里云的案例,以及本人自己网站都是采用的阿里云服务器,成功让客户选择阿里云。 其次是购买时间点,通过了解客户需求的配置以及预算,然后介绍双11对应的活动给客户,让客户选择双11期间下单购买。 小窍门:告知客户,注册账号了一定记得领我发的代金券,先关联上,然后再推荐配置、活动和上云时间点。 阿里云云大使这个项目非常好,目前看到其他云厂商也在模仿阿里云发展云大使模式就可以看出阿里云的营销是做的非常好的。同时专职做云大使,比起上班,在时间安排上更加自由,没有了上班族在办公室的规则和时间约束。中国企业上云是必然,一是因为绝大部分企业越来越需要服务器来存放自己的网站、APP或者存放数据,二是传统的IDC物理机房没有云计算的优势,同时传统IDC公司已经倒闭了很多,据本人所知,目前国内一些小的IDC公司已经大批量倒闭或者转行,仅有例如西部数码、景安、江苏E动等以前做的比较知名的IDC公司还在坚持。因此,随着上云需求的增加,而传统IDC公司的减少,企业上云是必然。以上是刘凯谈及云大使时自己的心得,并且希望自己能早入加入百万云大使的行列里,相信凭借刘凯对待云大使的初心和热爱,这个愿望在不久的将来很快就会实现了。 双十二活动开跑啦 随着双十一的圆满结束,双十二的活动也拉开了帷幕,关注云小站,新的阿里云官方分销平台,不仅仅是新客户,老客户也有返利,还等什么,抓住双十二的机会,下一个成功的就是你。 加入云大使 成为云大使欢迎加入云大使官方交流组织扫码加入
直播主题 图数据库GDB——Gremlin介绍及范例 专家介绍 王炎 存储&数据库领域专家。在基础平台技术领域深耕细作10余年。2018年加入阿里之后,从无到有开始数据库GDB的研发工作。 直播内容 第一部分:Gremlin基础与概念第二部分:Gremlin场景与查询范例第三部分:Gremlin最佳实践 观看回放 回放地址:https://yq.aliyun.com/live/1083 ppt精彩内容一览: ppt下载地址:https://yq.aliyun.com/download/3549 欢迎进群交流
分享嘉宾 刘显(泽贤) 阿里巴巴高级技术专家存储&数据库领域专家。在基础技术体系深耕10余年,于2015年加入阿里,目前是阿里云图数据库 GDB架构师,负责GDB的架构演进及技术建设相关工作。 直播介绍 第一部分:图数据库GDB基本介绍第二部分:图数据库GDB场景及案例分享第三部分:图数据库GDB关键能力解析 观看回放 回放地址:https://yq.aliyun.com/live/1005 ppt精彩内容一览: ppt下载:https://yq.aliyun.com/download/3524 欢迎进群交流
欢迎大家加入阿里云图数据库GDB 社区大群! 为满足广大开发者对更多数据库产品的学习需求,我们特别新建了阿里云图数据库GDB社区交流群,邀请多位阿里数据库专家入驻,为广大云用户提供专业的技术分享及回答,定期在国内开展线下技术沙龙,专家技术直播,欢迎大家加入!图数据库(Graph Database,简称GDB)是一种支持Property Graph图模型、用于处理高度连接数据查询与存储的实时、可靠的在线数据库服务。它支持Apache TinkerPop Gremlin查询语言,可以帮您快速构建基于高度连接的数据集的应用程序。GDB非常适合社交网络、欺诈检测、推荐引擎、实时图谱、网络/IT运营这类高度互连数据集的场景。图数据库GDB针对高度互联数据的存储和查询场景进行设计,并在内核层面进行了大量优化,非常适合社交网络、欺诈检测、推荐引擎、知识图谱、网络/IT运营等高密互连数据集的场景。 社交网络图数据库可以轻松应对海量高度互连社交数据的实时存储和高效查询,帮助您快速构建复杂的社交网络系统。例如,在一个典型的社交网络中,常常会存在“谁认识谁,谁上过什么学校,谁常住什么地方,谁喜欢什么餐馆”等查询,传统关系型数据库对于超过3度的查询往往会很低效甚至无法支持,但图数据库从基因层面提供了解决方案,轻松应对社交网络的各种复杂存储和查询。 金融欺诈检测在金融领域,图数据库经常用于欺诈检测。例如,通过贷款、分期消费者的联系人(或者联系人的联系人)信用信息,对用户进行信用评分,如果评分较低,则拒绝贷款或者提升利率;通过申请人的个人信息(包括电话号码、家庭住址),判断申请人信息是否属实。通常,欺诈者是通过“黑市”购买的用户信息然后拼凑出“个人信息”,并且这些信息会被反复使用,使用图数据库,可以快速发现申请人提供的个人信息与现有用户信息的关系。 实时推荐引擎图数据库非常适合实时推荐场景。您可以将用户的购买行为、位置、好友关系、收藏等数据实时存储在图数据库中,然后利用图数据库能对高度互连数据提供高效查询的特点,通过各种维度的快速查询实时进行多维度个性化推荐。例如,在某APP中,通过用户位置及以前的购买行为信息,当某用户A到达某商场B,APP可以向用户实时推荐附近的门店及商品等信息。 知识图谱图数据库可以帮助您快速构建知识图谱。您可以将图谱数据存储在图数据库中,既可以通过外部输入实时更新,也可以对图数据库内部图谱信息进行分析,不断发现并完善图谱数据。例如,基于图数据库,您可以快速实现针对足球明星的知识图谱应用,帮助用户发现感兴趣的信息。 网络/IT运营图数据库非常适合网络/IT运营相关场景。例如,您可以将路由器、交换机、防火墙、服务器等各种网络设备、终端及其拓扑信息存储在图数据库中,当某服务器或终端遭受恶意攻击或者受到感染时,您可以通过图数据库快速分析并找到传播路径,然后进行相关追踪及处理。 欢迎进群进行交流 进群方式一:链接: https://c.tb.cn/F3.ZR4o5x 即可进群进群方式二:扫描下方二维码即可进群
在中国程序媛中,他们的代码有什么样的魅力,Aone联合云栖社区、饿了么、钉钉、阿里云、天猫、口碑发起首届程序媛比码活动——不秀大长腿,秀高智商;不秀美图照,秀代码图,参与晒码互动游戏赢“83行代码”T恤! 我们来说说这群女工程师的第83行代码及代码背后的故事: 我是交通银行的前端女工程师, 我的第83行代码来自一段爬取指定用户评论区的粉丝信息然后分析并画出用户画像的项目,很好玩。 导入所需库 import json import time import numpy as np import pandas as pd import matplotlib.pyplot as plt import matplotlib import squarify import requests from matplotlib.patches import Polygon from mpl_toolkits.basemap import Basemap from matplotlib.collections import PatchCollection 获取每条微博评论的url参数 def get_comment_parameter(): url = 'https://m.weibo.cn/api/container/getIndex?uid=1773294041&luicode=10000011&lfid=100103type%3D1%26q%3D%E7%8E%8B%E8%8F%8A&\ featurecode=20000320&type=uid&value=1773294041&containerid=1076031773294041' c_r = requests.get(url) for i in range(2,11): c_parameter = (json.loads(c_r.text)["data"]["cards"][i]["mblog"]["id"]) comment_parameter.append(c_parameter) return comment_parameter if name == "__main__": comment_parameter = []#用来存放微博url参数 comment_url = []#用来存放微博url user_id = []#用来存放user_id comment = []#用来存放评论 containerid = []#用来存放containerid feature = []#用来存放用户信息 id_lose = []#用来存放访问不成功的user_id get_comment_parameter() #获取每条微博评论url c_url_base = 'https://m.weibo.cn/api/comments/show?id=' for parameter in comment_parameter: for page in range(1,101):#提前知道每条微博只可抓取前100页评论 c_url = c_url_base + str(parameter) + "&page=" + str(page) comment_url.append(c_url) #获取每个url下的user_id以及评论 for url in comment_url: u_c_r = requests.get(url) try: for m in range(0,9):#提前知道每个url会包含9条用户信息 one_id = json.loads(u_c_r.text)["data"]["data"][m]["user"]["id"] user_id.append(one_id) one_comment = json.loads(u_c_r.text)["data"]["data"][m]["text"] comment.append(one_comment) except: pass #获取每个user对应的containerid user_base_url = "https://m.weibo.cn/api/container/getIndex?type=uid&value=" for id in set(user_id):#需要对user_id去重 containerid_url = user_base_url + str(id) try: con_r = requests.get(containerid_url) one_containerid = json.loads(con_r.text)["data"]['tabsInfo']['tabs'][0]["containerid"] containerid.append(one_containerid) except: containerid.append(0) #获取每个user_id对应的基本信息 #这里需要设置cookie和headers模拟请求 user_agent = "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3359.181 Safari/537.36" headers = {"User-Agent":user_agent} m = 1 for num in zip(user_id,containerid): url = "https://m.weibo.cn/api/container/getIndex?uid="+str(num[0])+"&luicode=10000011&lfid=100103type%3D1%26q%3D&featurecode=20000320&type=uid&value="+str(num[0])+"&containerid="+str(num[1]) try: r = requests.get(url,headers = headers,cookies = cookie) feature.append(json.loads(r.text)["data"]["cards"][1]["card_group"][1]["item_content"].split(" ")) print("成功第{}条".format(m)) m = m + 1 time.sleep(1) except: id_lose.append(num[0]) #将featrue建立成DataFrame结构便于后续分析 user_info = pd.DataFrame(feature,columns = ["性别","年龄","星座","国家城市"]) 工作两年,不喜欢化妆,已有男朋友,哈哈~ 与坤0830小姐姐互动,为她打call——>点击进去晒码 更多小姐姐,点击进入查看代码 有被代码耽误的钉钉吃货程序媛,写代码写到忘记吃饭的——采霜 她急需能把她从代码中叫醒去吃饭的小伙伴,赶紧勾搭;http://yq.aliyun.com/roundtable/126499/answer/170319#visit170319 有以代码为乐的饿了么大前端打(bei)杂(guo)工程师——敖天羽http://yq.aliyun.com/roundtable/126499/answer/170299#visit170299 还有全栈美女工程师——前端后端一锅端的——墨瑜女神http://yq.aliyun.com/roundtable/126499/answer/170316#visit170316 还有阿里云活好码赞,像男人一样活着的技术专家小姐姐——清宵http://yq.aliyun.com/roundtable/126499/answer/170296#visit170296 和天猫Java程序媛,happy(被)来(逼)的——采月http://yq.aliyun.com/roundtable/126499/answer/170354#visit170354 阿里口碑 搜索推荐组的JAVA女工程师——妍霏http://yq.aliyun.com/roundtable/126499/answer/214059#visit214059 阿里口碑基础平台技术部的工程师-衔远,一个技术与马甲线并存的mmhttp://yq.aliyun.com/roundtable/126499/answer/248798#visit248798 云栖社区聚能聊专家,具有知性温婉气质的——海洋的云http://yq.aliyun.com/roundtable/126499/answer/170290#visit170290 福州市五佰网络科技有限公司的Java女工程师——江小白太白http://yq.aliyun.com/roundtable/126499/answer/189332#visit189332 在下*的程序媛——cn_suqingnianhttp://yq.aliyun.com/roundtable/126499/answer/170309#visit170309 交通银行的前端女工程师——昆0830http://yq.aliyun.com/roundtable/126499/answer/201979#visit201979
在中国程序媛中,他们的代码有什么样的魅力,Aone联合云栖社区、饿了么、钉钉、阿里云、天猫、口碑发起首届程序媛比码活动——不秀大长腿,秀高智商;不秀美图照,秀代码图,参与晒码互动游戏赢“83行代码”T恤! 我们来说说这群女工程师的第83行代码及代码背后的故事: 在下*的程序媛,平时喜欢自己写点宏文件……最喜欢自己写的啦 照片: 听说要爆照然后: /*by Qingnian Su,Only want to write sonething useful*/ #ifndef suqingnian #define suqingnian template<typename _Element_pow> _Element_pow _pow(_Element_pow _a,_Element_pow _b,_Element_pow _mod) { _Element_pow _res=1; for(;_b;_b >>= 1, _a= _a * _a % _mod) if(_b & 1) _res = _res * _a % _mod; return _res ; } template<typename _Element_gcd> _Element_gcd _gcd(_Element_gcd _m, _Element_gcd _n) { while (_n != 0) { _Element_gcd _t = _m % _n; _m = _n; _n = _t; } return _m; } bool __check[1000010]; int __size; int _num[1000010]; void _prime(int __n) { for(int _i=2;_i<=__n;_i++) { if(!__check[_i]) _num[__size++]=_i; for(int _j=0;_j<__size;_j++) { if(_i*_num[_j]>__n) break; __check[_num[_j]*_i]=1; if(_i%_num[_j]==0) break; } } return ; } bool _isprime(int __n) {return ~__check[__n];} #include<cstdio> template<typename _Element_stack> struct _stack{ _Element_stack __st[100010]; int __size_st; void __push(_Element_stack __x) {__st[++__size_st]=__x;return ;} void __pop() {__size_st--;return ;} _Element_stack __top() {return __st[__size_st];} bool _empty() {return __size_st==0;} }; #endif #endif 与cn_suqingnian小姐姐互动,为她打call——>点击进去晒码 更多小姐姐,点击进入查看代码 有被代码耽误的钉钉吃货程序媛,写代码写到忘记吃饭的——采霜 她急需能把她从代码中叫醒去吃饭的小伙伴,赶紧勾搭;http://yq.aliyun.com/roundtable/126499/answer/170319#visit170319 有以代码为乐的饿了么大前端打(bei)杂(guo)工程师——敖天羽http://yq.aliyun.com/roundtable/126499/answer/170299#visit170299 还有全栈美女工程师——前端后端一锅端的——墨瑜女神http://yq.aliyun.com/roundtable/126499/answer/170316#visit170316 还有阿里云活好码赞,像男人一样活着的技术专家小姐姐——清宵http://yq.aliyun.com/roundtable/126499/answer/170296#visit170296 和天猫Java程序媛,happy(被)来(逼)的——采月http://yq.aliyun.com/roundtable/126499/answer/170354#visit170354 阿里口碑 搜索推荐组的JAVA女工程师——妍霏http://yq.aliyun.com/roundtable/126499/answer/214059#visit214059 阿里口碑基础平台技术部的工程师-衔远,一个技术与马甲线并存的mmhttp://yq.aliyun.com/roundtable/126499/answer/248798#visit248798 云栖社区聚能聊专家,具有知性温婉气质的——海洋的云http://yq.aliyun.com/roundtable/126499/answer/170290#visit170290 福州市五佰网络科技有限公司的Java女工程师——江小白太白http://yq.aliyun.com/roundtable/126499/answer/189332#visit189332 在下*的程序媛——cn_suqingnianhttp://yq.aliyun.com/roundtable/126499/answer/170309#visit170309 交通银行的前端女工程师——昆0830http://yq.aliyun.com/roundtable/126499/answer/201979#visit201979
在中国程序媛中,他们的代码有什么样的魅力,Aone联合云栖社区、饿了么、钉钉、阿里云、天猫、口碑发起首届程序媛比码活动——不秀大长腿,秀高智商;不秀美图照,秀代码图,参与晒码互动游戏赢“83行代码”T恤! 我们来说说这群女工程师的第83行代码及代码背后的故事: 我是福州市五佰网络科技有限公司的Java女工程师江小白 我的第83行代码来自添加员工信息之前在Service中校验是否已经存在相同的手机号的一段代码 与江小白小姐姐互动,为她打call——>点击进去晒码 更多小姐姐,点击进入查看代码 有被代码耽误的钉钉吃货程序媛,写代码写到忘记吃饭的——采霜 她急需能把她从代码中叫醒去吃饭的小伙伴,赶紧勾搭;http://yq.aliyun.com/roundtable/126499/answer/170319#visit170319 有以代码为乐的饿了么大前端打(bei)杂(guo)工程师——敖天羽http://yq.aliyun.com/roundtable/126499/answer/170299#visit170299 还有全栈美女工程师——前端后端一锅端的——墨瑜女神http://yq.aliyun.com/roundtable/126499/answer/170316#visit170316 还有阿里云活好码赞,像男人一样活着的技术专家小姐姐——清宵http://yq.aliyun.com/roundtable/126499/answer/170296#visit170296 和天猫Java程序媛,happy(被)来(逼)的——采月http://yq.aliyun.com/roundtable/126499/answer/170354#visit170354 阿里口碑 搜索推荐组的JAVA女工程师——妍霏http://yq.aliyun.com/roundtable/126499/answer/214059#visit214059 阿里口碑基础平台技术部的工程师-衔远,一个技术与马甲线并存的mmhttp://yq.aliyun.com/roundtable/126499/answer/248798#visit248798 云栖社区聚能聊专家,具有知性温婉气质的——海洋的云http://yq.aliyun.com/roundtable/126499/answer/170290#visit170290 福州市五佰网络科技有限公司的Java女工程师——江小白太白http://yq.aliyun.com/roundtable/126499/answer/189332#visit189332 在下*的程序媛——cn_suqingnianhttp://yq.aliyun.com/roundtable/126499/answer/170309#visit170309 交通银行的前端女工程师——昆0830http://yq.aliyun.com/roundtable/126499/answer/201979#visit201979
在中国程序媛中,他们的代码有什么样的魅力,Aone联合云栖社区、饿了么、钉钉、阿里云、天猫、口碑发起首届程序媛比码活动——不秀大长腿,秀高智商;不秀美图照,秀代码图,参与晒码互动游戏赢“83行代码”T恤! 我们来说说这群女工程师的第83行代码及代码背后的故事: 平时习惯于用Jupyter Notebook写代码,以至于很多代码像是这样: # 分块计算缺失程度(23块) block_list = [1,5,6,20,24,28,32,36,48,52,54,64,72,76,102,107,111,155,161,166,211,254,278,298] for i in tqdm(range(len(block_list)-1)): tmp_df = train_test.iloc[:,block_list[i]-1:block_list[i+1]-1] print(tmp_df.columns) tmp_df_T = tmp_df.T tmp_df['count'] = tmp_df_T.count() train_test['count_f' + str(block_list[i]) + '_f' +str(block_list[i+1])] = tmp_df['count'] train_test['count_label_f' + str(block_list[i]) + '_f' +str(block_list[i+1]-1)]=np.where(train_test['count_f' + str(block_list[i]) + '_f' +str(block_list[i+1])] > 0,0,1) # 计算占比(基于date) for i in tqdm(r_list): df_dratio = pd.DataFrame() for j in date_list: df_tmp = train_test[train_test['date'] == j] f_ratio = pd.DataFrame(df_tmp[i].value_counts()) f_ratio['date'] = j f_ratio[i + '_rcount'] = f_ratio[i] f_ratio[i + '_ratio'] = f_ratio[i]/len(df_tmp) f_ratio[i] = f_ratio.index f_ratio = f_ratio.reset_index(drop = True) df_dratio = pd.concat([df_dratio, f_ratio], axis = 0) df_dratio = df_dratio.reset_index(drop = True) train_test = train_test.merge(df_dratio,on = ['date',i],how = 'left') 比较喜欢的一段代码是这样: import copy def cal_all_altcol_1(col_, labelcol = None, error_corr = False): # if labelcol is not None: # print("pcsing by groups") col = copy.deepcopy(col_) colname = col.columns[0] if(error_corr == True): if labelcol is not None: mean = pd.DataFrame(calmean(col, labelcol)) alt_col = [col, 1 / col, col-mean, np.fabs(col-mean)] alt_col_name = [colname + "_X", colname + "_1/X", colname + "_Xerr", colname + "_Xerr_abs"] else: mean = calmean(col, labelcol) alt_col = [col, 1 / col, np.fabs(col-mean)] alt_col_name = [colname + "_X", colname + "_1/X", colname + "_Xerr_abs"] elif(error_corr == False): alt_col = [col , 1 / col] alt_col_name = [colname + "_X", colname + "_1/X"] alt_list = pd.concat(alt_col, axis = 1) alt_list.columns = alt_col_name return alt_list def cal_all_altcol_1_base(col_, labelcol = None, error_corr = False): # 只取正负列 # if labelcol is not None: # print("pcsing by groups") col = copy.deepcopy(col_) colname = col.columns[0] if(error_corr == True): if labelcol is not None: mean = pd.DataFrame(calmean(col, labelcol)) alt_col = [col, np.fabs(col-mean), col-mean, ] alt_col_name = [colname + "_X", colname + "_Xerr_abs", colname + "_Xerr"] else: mean = pd.DataFrame(calmean(col, labelcol)) print(mean) alt_col = [col, np.fabs(col-mean)] alt_col_name = [colname + "_X", colname + "_Xerr_abs"] elif(error_corr == False): alt_col = [col] alt_col_name = [colname + "_X"] alt_list = pd.concat(alt_col, axis = 1) # print (alt_list) alt_list.columns = alt_col_name return alt_list def cal_all_altcol_2(col_i, col_j, labelcol = None, error_corr = False): # if labelcol is not None: # print("pcsing by groups") coli = copy.deepcopy(col_i) colj = copy.deepcopy(col_j) coliname = coli.columns[0] coljname = colj.columns[0] colbet = coliname + "_" + coljname coli.columns, colj.columns = [colbet], [colbet] # 需要把列名统一不然不能直接减 Plus = coli + colj Minus = coli - colj Multiply = coli * colj Divide_1 = coli / colj Divide_2 = colj / coli everynum_perpart = 5 if(error_corr == True): P_mean = pd.DataFrame(calmean(Plus, labelcol)) Mi_mean = pd.DataFrame(calmean(Minus, labelcol)) Mu_mean = pd.DataFrame(calmean(Multiply, labelcol)) D1_mean = pd.DataFrame(calmean(Divide_1, labelcol)) D2_mean = pd.DataFrame(calmean(Divide_2, labelcol)) if labelcol is not None: alt_col = [Plus, Minus, Multiply, Divide_1, Divide_2, np.fabs(Plus - P_mean), np.fabs(Minus - Mi_mean), np.fabs(Multiply - Mu_mean), np.fabs(Divide_1 - D1_mean), np.fabs(Divide_2 - D2_mean), Plus - P_mean, Minus - Mi_mean, Multiply - Mu_mean,Divide_1 - D1_mean, Divide_2 - D2_mean] alt_col_name = [colbet + "_X+Y", colbet + "_X-Y", colbet + "_X*Y", colbet + "_X/Y", colbet + "_Y/X", colbet + "_X+Yerr_abs", colbet + "_X-Yerr_abs", colbet + "_X*Yerr_abs", colbet + "_X/Yerr_abs", colbet + "_Y/Xerr_abs", colbet + "_X+Yerr", colbet + "_X-Yerr", colbet + "_X*Yerr", colbet + "_X/Yerr", colbet + "_Y/Xerr",] elif labelcol is None: alt_col = [Plus, Minus, Multiply, Divide_1, Divide_2, np.fabs(Plus - P_mean), np.fabs(Minus - Mi_mean), np.fabs(Multiply - Mu_mean), np.fabs(Divide_1 - D1_mean), np.fabs(Divide_2 - D2_mean)] alt_col_name = [colbet + "_X+Y", colbet + "_X-Y", colbet + "_X*Y", colbet + "_X/Y", colbet + "_Y/X", colbet + "_X+Yerr_abs", colbet + "_X-Yerr_abs", colbet + "_X*Yerr_abs", colbet + "_X/Yerr_abs", colbet + "_Y/Xerr_abs"] elif(error_corr == False): alt_col = [Plus, Minus, Multiply, Divide_1, Divide_2] alt_col_name = [colbet + "_X+Y", colbet + "_X-Y", colbet + "_X*Y", colbet + "_X/Y", colbet + "_Y/X"] alt_list = pd.concat(alt_col, axis = 1) alt_list.columns = alt_col_name # print(alt_list) return alt_list # def cal_all_alt2tol(coli, colj, ) # cal_all_altcol_2(test, test2) 与海洋的云小姐姐互动,为她打call——>点击进去晒码 更多小姐姐,点击进入查看代码 有被代码耽误的钉钉吃货程序媛,写代码写到忘记吃饭的——采霜 她急需能把她从代码中叫醒去吃饭的小伙伴,赶紧勾搭;http://yq.aliyun.com/roundtable/126499/answer/170319#visit170319 有以代码为乐的饿了么大前端打(bei)杂(guo)工程师——敖天羽http://yq.aliyun.com/roundtable/126499/answer/170299#visit170299 还有全栈美女工程师——前端后端一锅端的——墨瑜女神http://yq.aliyun.com/roundtable/126499/answer/170316#visit170316 还有阿里云活好码赞,像男人一样活着的技术专家小姐姐——清宵http://yq.aliyun.com/roundtable/126499/answer/170296#visit170296 和天猫Java程序媛,happy(被)来(逼)的——采月http://yq.aliyun.com/roundtable/126499/answer/170354#visit170354 阿里口碑 搜索推荐组的JAVA女工程师——妍霏http://yq.aliyun.com/roundtable/126499/answer/214059#visit214059 阿里口碑基础平台技术部的工程师-衔远,一个技术与马甲线并存的mmhttp://yq.aliyun.com/roundtable/126499/answer/248798#visit248798 云栖社区聚能聊专家,具有知性温婉气质的——海洋的云http://yq.aliyun.com/roundtable/126499/answer/170290#visit170290 福州市五佰网络科技有限公司的Java女工程师——江小白太白http://yq.aliyun.com/roundtable/126499/answer/189332#visit189332 在下*的程序媛——cn_suqingnianhttp://yq.aliyun.com/roundtable/126499/answer/170309#visit170309 交通银行的前端女工程师——昆0830http://yq.aliyun.com/roundtable/126499/answer/201979#visit201979
在中国程序媛中,他们的代码有什么样的魅力,Aone联合云栖社区、饿了么、钉钉、阿里云、天猫、口碑发起首届程序媛比码活动——不秀大长腿,秀高智商;不秀美图照,秀代码图,参与晒码互动游戏赢“83行代码”T恤! 我们来说说这群女工程师的第83行代码及代码背后的故事: 我是 阿里口碑 基础平台技术部的工程师-衔远,坐标上海。 听说还有发照骗,说好的不秀美颜呢!! ps:团队大量招人,欢迎加入~~ enqing.teq@koubei.com 下面是一段商品购买时库存扣减回补的代码,欢迎cr public class PurchaseProcessExecutorImpl implements PurchaseProcessExecutor { /** * 外部库存服务 */ private OutInventoryService outInventoryService; /** * 执行外部扣减流程,同时更新扣减状态 * * @param bizOrder * @return */ @Override public void executeSubtract(InventoryOrder bizOrder, String uid) { List<SubtractProcessDetail> details = bizOrder.getSubtractProcess(); //生成外部预算请求,key是outInvId List<OutInventoryControlRequest> outReqs = convert2OutInvReqs(details); //通过budgetCore进行扣减 Map<String, BudgetControlResult> res = budgetCoreControl(outReqs, uid, ControlType.SUBTRACT); //更新扣减单元状态 updateUnitStatus(res, details); //更新子单扣减状态 updateOrderStatus(details); //更新主单状态 bizOrder.updateSubtractStatus(); } /** * 执行回补流程,同时更新回补状态 * @param subtractOrder * @param rollbackOrder * @param uid */ @Override public void executeRollback(InventoryOrder subtractOrder, InventoryOrder rollbackOrder, String uid) { //生成外部预算请求 List<OutInventoryControlRequest> outReqs = convert2OutInvReqs( rollbackOrder.getRollbackProcess()); //通过budgetCore进行回补 Map<String, BudgetControlResult> res = budgetCoreControl(outReqs, uid, ControlType.ROLLBACK); //更新回补单元状态 updateUnitStatus(res, rollbackOrder.getRollbackProcess()); //更新子单状态 updateOrderStatus(rollbackOrder.getRollbackProcess()); //更新主单状态 rollbackOrder.updateRollbackStatus(); } /** * 更新扣减单元状态 * * @param res * @param details */ private <T extends PurchaseProcessDetail> void updateUnitStatus(Map<String, BudgetControlResult> res, List<T> details) { if (CollectionTool.isEmpty(details)) { return; } //结果解析 details.forEach(detail -> { //根据扣减结果更新状态 detail.getUnits() .forEach(unit -> unit.updateUnitStatus(res.get(unit.getOutInventoryId()))); }); } /** * 更新子单明细状态 * @param details * @param <T> */ private <T extends PurchaseProcessDetail> void updateOrderStatus(List<T> details) { if (CollectionTool.isEmpty(details)) { return; } details.forEach(detail -> detail.updateStatus()); } /** * 通过budgetCore进行回补 or 扣减 * * @param outReqs * @param uid */ private Map<String, BudgetControlResult> budgetCoreControl(List<OutInventoryControlRequest> outReqs, String uid, ControlType controlType) { Map<String, BudgetControlResult> budgetControlResultMap = new HashMap<>(); if (CollectionTool.isEmpty(outReqs)) { return budgetControlResultMap; } //根据类型调用bc进行回补or扣减 if (ControlType.SUBTRACT == controlType) { //扣减 budgetControlResultMap = outInventoryService.batchSubtract(uid, outReqs); } else if (ControlType.ROLLBACK == controlType) { //回补 budgetControlResultMap = outInventoryService.batchRollback(uid, outReqs); } else { InvBizLog.procWarn(InvBizType.INV_SUBTRACT, InvSecErrorCode.BC_NOT_SUPPORT, "bc控制模式不支持", controlType); } if (CollectionTool.isEmpty(budgetControlResultMap)) { InvBizLog.fail(InvBizType.INV_SUBTRACT, InvSecErrorCode.LOAD_BUD_CORE_RESULT_NULL, "budgetCore调用失败"); throw new InvBusinessException(InvSecErrorCode.LOAD_BUD_CORE_RESULT_NULL, true); } InvBizLog.proc(InvBizType.INV_SUBTRACT, "budgetCore调用结束,结果={0}", budgetControlResultMap); return budgetControlResultMap; } } 与衔远小姐姐互动,为她打call——>点击进去衔远晒码 更多小姐姐,点击进入查看代码 有被代码耽误的钉钉吃货程序媛,写代码写到忘记吃饭的——采霜 她急需能把她从代码中叫醒去吃饭的小伙伴,赶紧勾搭;http://yq.aliyun.com/roundtable/126499/answer/170319#visit170319 有以代码为乐的饿了么大前端打(bei)杂(guo)工程师——敖天羽http://yq.aliyun.com/roundtable/126499/answer/170299#visit170299 还有全栈美女工程师——前端后端一锅端的——墨瑜女神http://yq.aliyun.com/roundtable/126499/answer/170316#visit170316 还有阿里云活好码赞,像男人一样活着的技术专家小姐姐——清宵http://yq.aliyun.com/roundtable/126499/answer/170296#visit170296 和天猫Java程序媛,happy(被)来(逼)的——采月http://yq.aliyun.com/roundtable/126499/answer/170354#visit170354 阿里口碑 搜索推荐组的JAVA女工程师——妍霏http://yq.aliyun.com/roundtable/126499/answer/214059#visit214059 阿里口碑基础平台技术部的工程师-衔远,一个技术与马甲线并存的mmhttp://yq.aliyun.com/roundtable/126499/answer/248798#visit248798 云栖社区聚能聊专家,具有知性温婉气质的——海洋的云http://yq.aliyun.com/roundtable/126499/answer/170290#visit170290 福州市五佰网络科技有限公司的Java女工程师——江小白太白http://yq.aliyun.com/roundtable/126499/answer/189332#visit189332 在下*的程序媛——cn_suqingnianhttp://yq.aliyun.com/roundtable/126499/answer/170309#visit170309 交通银行的前端女工程师——昆0830http://yq.aliyun.com/roundtable/126499/answer/201979#visit201979
在中国程序媛中,他们的代码有什么样的魅力,Aone联合云栖社区、饿了么、钉钉、阿里云、天猫、口碑发起首届程序媛比码活动——不秀大长腿,秀高智商;不秀美图照,秀代码图,参与晒码互动游戏赢“83行代码”T恤! 我们来说说这群女工程师的第83行代码及代码背后的故事: 我是 阿里口碑 搜索推荐组的JAVA女工程师,花名妍霏。 不上相所以自拍比较少,好不容易才找到一张没崩的自拍照骗。 决定再发一张和我们口碑团队的另外一位超漂亮精致妹子的合照!口碑的精致女工程师可是非常的多哦~ 我们团队还有大量的坑招人,包括不限于java/c++工程师,算法工程师等等,欢迎大家加入~ fayyen1213@koubei.com 我的第83行代码来自 生成特定搜索场景下所需要查询的门店状态值集合 的一段代码,欢迎大家指正。 /** * 计算单个display_status * * @param status 展示状态列表 * @return 单个计算结果 */ public static int getEngineDisplayStatus(Map<DisplayStatusEnum, Integer> status) { int resNum = 0; for (Map.Entry<DisplayStatusEnum, Integer> entry : status.entrySet()) { resNum = resNum + (int)(Math.pow(10, entry.getKey().getIndex()) * entry.getValue()); } return resNum; } /** * 递归计算所有的display_status * * @param statusList 展示状态列表 * @param boolList 状态开关列表 * @param resList display_status列表 * @param index 起始位置 * @param calStatus 中间结果 */ private static void calDisplayStatus(DisplayStatusEnum[] statusList, List<Boolean> boolList, List<Integer> resList, int index, Map<DisplayStatusEnum, Integer> calStatus) { if (index == statusList.length) { resList.add(getEngineDisplayStatus(calStatus)); return; } DisplayStatusEnum displayStatusEnum = statusList[index]; if (boolList.get(index)) { calStatus.put(displayStatusEnum, displayStatusEnum.getDisplayValue()); calDisplayStatus(statusList, boolList, resList, index + 1, calStatus); if (displayStatusEnum != DisplayStatusEnum.MallDisplay) { calStatus.put(displayStatusEnum, displayStatusEnum.getDefaultValue()); calDisplayStatus(statusList, boolList, resList, index + 1, calStatus); calStatus.put(displayStatusEnum, displayStatusEnum.getDisplayValue()); } } else { calStatus.put(displayStatusEnum, displayStatusEnum.getDefaultValue()); calDisplayStatus(statusList, boolList, resList, index + 1, calStatus); } } /** * 计算指定环境下需要查询的所有店铺display_status值 * * @param status 展示状态列表 * @return display_status最终计算结果 */ public static String getDisplayStatusQuery(List<DisplayStatusEnum> status) { List<Integer> r = new ArrayList<Integer>(); List<Boolean> boolList = new ArrayList<Boolean>(); DisplayStatusEnum[] enumList = DisplayStatusEnum.class.getEnumConstants(); for (DisplayStatusEnum e : enumList) { if (status.contains(e)) { boolList.add(true); } else { boolList.add(false); } } Map<DisplayStatusEnum, Integer> calcuStatus = new HashMap<DisplayStatusEnum, Integer>(); calDisplayStatus(enumList, boolList, r, 0, calcuStatus); return StringUtils.join(r, " OR "); }` 与妍霏小姐姐互动,为她打call——>点击进去妍霏晒码 更多小姐姐,点击进入查看代码 有被代码耽误的钉钉吃货程序媛,写代码写到忘记吃饭的——采霜 她急需能把她从代码中叫醒去吃饭的小伙伴,赶紧勾搭;http://yq.aliyun.com/roundtable/126499/answer/170319#visit170319 有以代码为乐的饿了么大前端打(bei)杂(guo)工程师——敖天羽http://yq.aliyun.com/roundtable/126499/answer/170299#visit170299 还有全栈美女工程师——前端后端一锅端的——墨瑜女神http://yq.aliyun.com/roundtable/126499/answer/170316#visit170316 还有阿里云活好码赞,像男人一样活着的技术专家小姐姐——清宵http://yq.aliyun.com/roundtable/126499/answer/170296#visit170296 和天猫Java程序媛,happy(被)来(逼)的——采月http://yq.aliyun.com/roundtable/126499/answer/170354#visit170354 阿里口碑 搜索推荐组的JAVA女工程师——妍霏http://yq.aliyun.com/roundtable/126499/answer/214059#visit214059 阿里口碑基础平台技术部的工程师-衔远,一个技术与马甲线并存的mmhttp://yq.aliyun.com/roundtable/126499/answer/248798#visit248798 云栖社区聚能聊专家,具有知性温婉气质的——海洋的云http://yq.aliyun.com/roundtable/126499/answer/170290#visit170290 福州市五佰网络科技有限公司的Java女工程师——江小白太白http://yq.aliyun.com/roundtable/126499/answer/189332#visit189332 在下*的程序媛——cn_suqingnianhttp://yq.aliyun.com/roundtable/126499/answer/170309#visit170309 交通银行的前端女工程师——昆0830http://yq.aliyun.com/roundtable/126499/answer/201979#visit201979
在中国程序媛中,他们的代码有什么样的魅力,Aone联合云栖社区、饿了么、钉钉、阿里云、天猫、口碑发起首届程序媛比码活动——不秀大长腿,秀高智商;不秀美图照,秀代码图,参与晒码互动游戏赢“83行代码”T恤! 我们来说说这群女工程师的第83行代码及代码背后的故事: 听说程序媛这个物种十分罕见,我是作为罕见中的一员happy(被)来(逼)的,毕业几年的人生感悟:每个人在浩瀚的宇宙中都有自己独特的坐标,这个坐标会因为你的选择而变的不同,同样这个世界也会因为你坐标的移动而变得不同!SO,COME ON 加入我们,让我们一起改变世界,成就更好的自己!我在tmall一直等你!caiyue@tmall.com package tmall; import lombok.*; /** Created by caiyue on 2018/7/16. */ @AllArgsConstructor class Business { private String buName, salary, awardMoney, mission, colleague; public Person change(Person p) {return p;} public String toString() { return buName + "公司:[工资:" + salary + ",奖金:" + awardMoney + ",同事:" + colleague + ",使命:" + mission + "]"; } } @Data @AllArgsConstructor class Person { private String role, x, y, z; public String toString() {return role + ":[" + x + "、" + y + "、" + z + "]";} } class TmallBu extends Business { public TmallBu(String buName, String salary, String awardMoney, String misson, String colleague) { super(buName, salary, awardMoney, misson, colleague); } public Person change(Person p) { switch (p.getRole()) { case "程序媛": return new Person(p.getRole(), "人美", "码赞", "会生活"); case "程序员": return new Person(p.getRole(), "人帅", "钱多", "疼老婆"); default: return new Person(p.getRole(), "坐标丢失", "坐标丢失", "坐标丢失"); } } public class TransformAll { public static void main(String[] args) { Business tmall = new TmallBu("天猫", "五颗星", "五颗星", "改变自己改变世界", "一群有情有义的人"); Person ordinaryGirl = new Person("程序媛", "定位自我", "找寻理想", "探索生活意义"); Person tmallProgrammerGirl = tmall.change(ordinaryGirl); System.out.println(tmall.toString()); System.out.println(tmallProgrammerGirl.toString()); System.out.println("欢迎来天猫一起成为更好的自己!"); } } 与采月小姐姐互动,为她打call——>点击进去采月晒码 更多小姐姐,点击进入查看代码 有被代码耽误的钉钉吃货程序媛,写代码写到忘记吃饭的——采霜 她急需能把她从代码中叫醒去吃饭的小伙伴,赶紧勾搭;http://yq.aliyun.com/roundtable/126499/answer/170319#visit170319 有以代码为乐的饿了么大前端打(bei)杂(guo)工程师——敖天羽http://yq.aliyun.com/roundtable/126499/answer/170299#visit170299 还有全栈美女工程师——前端后端一锅端的——墨瑜女神http://yq.aliyun.com/roundtable/126499/answer/170316#visit170316 还有阿里云活好码赞,像男人一样活着的技术专家小姐姐——清宵http://yq.aliyun.com/roundtable/126499/answer/170296#visit170296 和天猫Java程序媛,happy(被)来(逼)的——采月http://yq.aliyun.com/roundtable/126499/answer/170354#visit170354 阿里口碑 搜索推荐组的JAVA女工程师——妍霏http://yq.aliyun.com/roundtable/126499/answer/214059#visit214059 阿里口碑基础平台技术部的工程师-衔远,一个技术与马甲线并存的mmhttp://yq.aliyun.com/roundtable/126499/answer/248798#visit248798 云栖社区聚能聊专家,具有知性温婉气质的——海洋的云http://yq.aliyun.com/roundtable/126499/answer/170290#visit170290 福州市五佰网络科技有限公司的Java女工程师——江小白太白http://yq.aliyun.com/roundtable/126499/answer/189332#visit189332 在下*的程序媛——cn_suqingnianhttp://yq.aliyun.com/roundtable/126499/answer/170309#visit170309 交通银行的前端女工程师——昆0830http://yq.aliyun.com/roundtable/126499/answer/201979#visit201979
在中国程序媛中,他们的代码有什么样的魅力,Aone联合云栖社区、饿了么、钉钉、阿里云、天猫、口碑发起首届程序媛比码活动——不秀大长腿,秀高智商;不秀美图照,秀代码图,参与晒码互动游戏赢“83行代码”T恤! 我们来说说这群女工程师的第83行代码及代码背后的故事: 我是云效的全栈开发女工程师,朱默,花名:墨瑜。 据说认真工作的程序员魅力值加10: 我的第83行代码来自云效RDC上某一功能的代码, 猜猜是哪个功能: public class MysqlLockBuilder { private DataSource dataSource; private String mysqlTableName; private LockDataAccessor lockDataAccessor; private LockStateAccessor lockStateAccessor; private String defaultClientId; public void init() throws SQLException { MysqlLockDataAccessor mysqlLockDataAccessor = new MysqlLockDataAccessor(); mysqlLockDataAccessor.setMysqlTableName(mysqlTableName); mysqlLockDataAccessor.setConnection(dataSource.getConnection()); lockDataAccessor = mysqlLockDataAccessor; defaultClientId = IP.getIpAddr(); lockStateAccessor = buildMysqlLockStateAccessor(); } private LockStateAccessor buildMysqlLockStateAccessor() throws SQLException { MysqlLockStateAccessor lockStateAccessor = new MysqlLockStateAccessor(); lockStateAccessor.setMysqlTableName(mysqlTableName); lockStateAccessor.setConnection(dataSource.getConnection()); lockStateAccessor.init(); return lockStateAccessor; } public String getDefaultClientId() { return defaultClientId; } public void setDefaultClientId(String defaultClientId) { this.defaultClientId = defaultClientId; } public TimedLock lockForAWhile(String lockId, int timeout) throws LockAccessException { TimedLock lock = new TimedLock(lockId, defaultClientId, timeout); try { lock.setLockStateAccessor(buildMysqlLockStateAccessor()); lock(lock); return lock; } catch (SQLException e){ lock.close(); throw new LockAccessException(e); } catch (LockAccessException e) { lock.close(); throw e; } } public ScopedLock lockWhenAlive(String lockId) throws LockAccessException { ScopedLock lock = new ScopedLock(lockId, defaultClientId); try { lock.setLockStateAccessor(buildMysqlLockStateAccessor()); lock(lock); return lock; } catch (SQLException e){ lock.close(); throw new LockAccessException(e); } catch (LockAccessException e){ lock.close(); throw e; } } /** * 指定clientId,加时间锁 * @param lockId: 锁名称 * @param timeout: 加锁时长 * @param clientId: 锁的持有者 * @return * @throws LockAccessException */ public TimedLock lockForAWhileByClient(String lockId, int timeout, String clientId) throws LockAccessException { TimedLock lock = new TimedLock(lockId, clientId, timeout); try { LockStateAccessor timeLockStateAccessor = buildMysqlLockStateAccessor(); lock.setLockStateAccessor(timeLockStateAccessor); lock(lock); return lock; } catch (SQLException e){ lock.close(); throw new LockAccessException(e); } catch (LockAccessException e) { lock.close(); throw e; } } /** * 刷新时间锁,延长加锁时长 * @param lockingContext * @return * @throws LockAccessException */ public Boolean refreshTimeLockByContext(LockingContext lockingContext) throws LockAccessException { if(lockStateAccessor == null){ try { lockStateAccessor = buildMysqlLockStateAccessor(); }catch (SQLException e) { throw new LockAccessException(e); } } return lockStateAccessor.refreshTimeLock(lockingContext); } /** * 手动解锁 * @param lockingContext * @return * @throws LockAccessException */ public Boolean unlockTimeLockByContext(LockingContext lockingContext) throws LockAccessException { if(lockStateAccessor == null){ try { lockStateAccessor = buildMysqlLockStateAccessor(); }catch (SQLException e) { throw new LockAccessException(e); } } return lockStateAccessor.timedUnlock(lockingContext); } /** * 根据锁id,获取时间锁信息 * @param lockId * @return * @throws LockAccessException */ public LockingInfo getTimedLockInfo(String lockId) throws LockAccessException{ return lockStateAccessor.getTimedLockingInfo(lockId); } private void lock(Lockable lock) throws LockAccessException { try { lockDataAccessor.createIfNotPresent(lock.id()); boolean locked = lock.lock(); if (!locked) { throw new LockAccessException("Lock has been occupied"); } } catch (Exception e) { throw new LockAccessException(e); } } public String getMysqlTableName() { return mysqlTableName; } public void setMysqlTableName(String mysqlTableName) { this.mysqlTableName = mysqlTableName; } public void setDataSource(DataSource dataSource) { this.dataSource = dataSource; } public DataSource getDataSource() { return dataSource; } } 与墨瑜小姐姐互动,为她打call——>点击进去墨瑜晒码 更多小姐姐,点击进入查看代码 有被代码耽误的钉钉吃货程序媛,写代码写到忘记吃饭的——采霜 她急需能把她从代码中叫醒去吃饭的小伙伴,赶紧勾搭;http://yq.aliyun.com/roundtable/126499/answer/170319#visit170319 有以代码为乐的饿了么大前端打(bei)杂(guo)工程师——敖天羽http://yq.aliyun.com/roundtable/126499/answer/170299#visit170299 还有全栈美女工程师——前端后端一锅端的——墨瑜女神http://yq.aliyun.com/roundtable/126499/answer/170316#visit170316 还有阿里云活好码赞,像男人一样活着的技术专家小姐姐——清宵http://yq.aliyun.com/roundtable/126499/answer/170296#visit170296 和天猫Java程序媛,happy(被)来(逼)的——采月http://yq.aliyun.com/roundtable/126499/answer/170354#visit170354 阿里口碑 搜索推荐组的JAVA女工程师——妍霏http://yq.aliyun.com/roundtable/126499/answer/214059#visit214059 阿里口碑基础平台技术部的工程师-衔远,一个技术与马甲线并存的mmhttp://yq.aliyun.com/roundtable/126499/answer/248798#visit248798 云栖社区聚能聊专家,具有知性温婉气质的——海洋的云http://yq.aliyun.com/roundtable/126499/answer/170290#visit170290 福州市五佰网络科技有限公司的Java女工程师——江小白太白http://yq.aliyun.com/roundtable/126499/answer/189332#visit189332 在下*的程序媛——cn_suqingnianhttp://yq.aliyun.com/roundtable/126499/answer/170309#visit170309 交通银行的前端女工程师——昆0830http://yq.aliyun.com/roundtable/126499/answer/201979#visit201979
在中国程序媛中,他们的代码有什么样的魅力,Aone联合云栖社区、饿了么、钉钉、阿里云、天猫、口碑发起首届程序媛比码活动——不秀大长腿,秀高智商;不秀美图照,秀代码图,参与晒码互动游戏赢“83行代码”T恤! 我们来说说这群女工程师的第83行代码及代码背后的故事: 大家好,我是饿了么大前端的打杂工程师敖天羽。 照片: 别的小姐姐晒得代码都太厉害了......只有我是来搞笑的,前年情人节的单身狗翻译器代码,两块钱你买不了吃亏,两块钱你买不了上当: const pinyin = require('pinyin'); const program = require('commander'); const translate = require('./translator'); program .version('0.0.5') .description('单身狗汪化翻译机,把你的话都变成汪星语') .option('-e --emoj', '使用emoj来替代汉字汪') .option('-i --stdin', '传输标准输入流文本而不是命令行参数') .option('-c --clip', '自动复制到剪贴板(文本过大慎用)') .parse(process.argv); const word = !program.emoj; const clip = program.clip; if (program.stdin) { process.stdin.setEncoding('utf-8'); process.stdin.on('readable', () => { let chunk = process.stdin.read(); if (chunk !== null) { translate(chunk, word, clip); } }); } else { if (program.args.length > 0) { translate(program.args[0], word, clip); process.stdout.write(`\n`); } else program.outputHelp(); } 对了,新晋花名「天汪」,请多多指教。(路过,给各位小姐姐端茶送水) 与天羽小姐姐互动,为她打call——>点击进去天羽晒码 更多小姐姐,点击进入查看代码 有被代码耽误的钉钉吃货程序媛,写代码写到忘记吃饭的——采霜 她急需能把她从代码中叫醒去吃饭的小伙伴,赶紧勾搭;http://yq.aliyun.com/roundtable/126499/answer/170319#visit170319 有以代码为乐的饿了么大前端打(bei)杂(guo)工程师——敖天羽http://yq.aliyun.com/roundtable/126499/answer/170299#visit170299 还有全栈美女工程师——前端后端一锅端的——墨瑜女神http://yq.aliyun.com/roundtable/126499/answer/170316#visit170316 还有阿里云活好码赞,像男人一样活着的技术专家小姐姐——清宵http://yq.aliyun.com/roundtable/126499/answer/170296#visit170296 和天猫Java程序媛,happy(被)来(逼)的——采月http://yq.aliyun.com/roundtable/126499/answer/170354#visit170354 阿里口碑 搜索推荐组的JAVA女工程师——妍霏http://yq.aliyun.com/roundtable/126499/answer/214059#visit214059 阿里口碑基础平台技术部的工程师-衔远,一个技术与马甲线并存的mmhttp://yq.aliyun.com/roundtable/126499/answer/248798#visit248798 云栖社区聚能聊专家,具有知性温婉气质的——海洋的云http://yq.aliyun.com/roundtable/126499/answer/170290#visit170290 福州市五佰网络科技有限公司的Java女工程师——江小白太白http://yq.aliyun.com/roundtable/126499/answer/189332#visit189332 在下*的程序媛——cn_suqingnianhttp://yq.aliyun.com/roundtable/126499/answer/170309#visit170309 交通银行的前端女工程师——昆0830http://yq.aliyun.com/roundtable/126499/answer/201979#visit201979
在中国程序媛中,他们的代码有什么样的魅力,Aone联合云栖社区、饿了么、钉钉、阿里云、天猫、口碑发起首届程序媛比码活动——不秀大长腿,秀高智商;不秀美图照,秀代码图,参与晒码互动游戏赢“83行代码”T恤! 我们来说说这群女工程师的第83行代码及代码背后的故事: # -*- coding:utf-8 -*- import random what_to_eat = ['牛肉拉面', '麻辣烫', '麻辣香锅', '重庆小面', '水饺', '沙拉', '煎饼果子', '米线', '盖浇饭', '凉皮肉夹馍', '粥+煎饺', '寿司', '石锅拌饭'] random.shuffle(what_to_eat) what_to_eat_this_week = random.sample(what_to_eat, 5) for index in range(len(what_to_eat_this_week)): print('周' + str(index + 1) + ':' + what_to_eat_this_week[index]) 知道大家想看的是照骗~ 海报里面的C位就是我,想和我一起工作么,钉钉smart work团队诚招java、ios、安卓、前端以及产品经理。有意者简历到caishuang@alinana-inc.com,有投必回 与采霜小姐姐互动,为她打call——>点击进去采霜晒码 更多小姐姐,点击进入查看代码 有被代码耽误的钉钉吃货程序媛,写代码写到忘记吃饭的——采霜 她急需能把她从代码中叫醒去吃饭的小伙伴,赶紧勾搭;http://yq.aliyun.com/roundtable/126499/answer/170319#visit170319 有以代码为乐的饿了么大前端打(bei)杂(guo)工程师——敖天羽http://yq.aliyun.com/roundtable/126499/answer/170299#visit170299 还有全栈美女工程师——前端后端一锅端的——墨瑜女神http://yq.aliyun.com/roundtable/126499/answer/170316#visit170316 还有阿里云活好码赞,像男人一样活着的技术专家小姐姐——清宵http://yq.aliyun.com/roundtable/126499/answer/170296#visit170296 和天猫Java程序媛,happy(被)来(逼)的——采月http://yq.aliyun.com/roundtable/126499/answer/170354#visit170354 阿里口碑 搜索推荐组的JAVA女工程师——妍霏http://yq.aliyun.com/roundtable/126499/answer/214059#visit214059 阿里口碑基础平台技术部的工程师-衔远,一个技术与马甲线并存的mmhttp://yq.aliyun.com/roundtable/126499/answer/248798#visit248798 云栖社区聚能聊专家,具有知性温婉气质的——海洋的云http://yq.aliyun.com/roundtable/126499/answer/170290#visit170290 福州市五佰网络科技有限公司的Java女工程师——江小白太白http://yq.aliyun.com/roundtable/126499/answer/189332#visit189332 在下*的程序媛——cn_suqingnianhttp://yq.aliyun.com/roundtable/126499/answer/170309#visit170309 交通银行的前端女工程师——昆0830http://yq.aliyun.com/roundtable/126499/answer/201979#visit201979
为什么要重写React React16 以前 React16 以前,对virtural dom的更新和渲染是同步的。就是当一次更新或者一次加载开始以后,diff virtual dom并且渲染的过程是一口气完成的。如果组件层级比较深,相应的堆栈也会很深,长时间占用浏览器主线程,一些类似用户输入、鼠标滚动等操作得不到响应。借Lin的两张图,视频 A Cartoon Intro to Fiber - React Conf 2017。 React16 Fiber Reconciler React16 用了分片的方式解决上面的问题。 就是把一个任务分成很多小片,当分配给这个小片的时间用尽的时候,就检查任务列表中有没有新的、优先级更高的任务,有就做这个新任务,没有就继续做原来的任务。这种方式被叫做异步渲染(Async Rendering)。 Fiber 对开发者有什么影响 目前看有以下: componentWillMount componentWillReceiveProps componentWillUpdate 几个生命周期方法不再安全,由于任务执行过程可以被打断,这几个生命周期可能会执行多次,如果它们包含副作用(比如AJax),会有意想不到的bug。React团队提供了替换的生命周期方法。建议如果使用以上方法,尽量用纯函数,避免以后采坑。 需要关注下react为任务片设置的优先级,特别是页面用动画的情况 如何试用Fiber异步渲染 默认情况下,异步渲染没有打开,如果你想试用,可以: import React from 'react'; import ReactDOM from 'react-dom'; import App from 'components/App'; const AsyncMode = React.unstable_AsyncMode; const createApp = (store) => ( <AsyncMode> <App store={store} /> </AsyncMode> ); export default createApp; 代码将开启严格模式和异步模式,React16不建议试用的API会在控制台有错误提示,比如componentWillMount。Fiber如何工作 一些原理 懂了原理看代码就简单点。 首先,Fiber是什么: A Fiber is work on a Component that needs to be done or was done. There can be more than one per component. Fiber就是通过对象记录组件上需要做或者已经完成的更新,一个组件可以对应多个Fiber。 在render函数中创建的React Element树在第一次渲染的时候会创建一颗结构一模一样的Fiber节点树。不同的React Element类型对应不同的Fiber节点类型。一个React Element的工作就由它对应的Fiber节点来负责。 一个React Element可以对应不止一个Fiber,因为Fiber在update的时候,会从原来的Fiber(我们称为current)clone出一个新的Fiber(我们称为alternate)。两个Fiber diff出的变化(side effect)记录在alternate上。所以一个组件在更新时最多会有两个Fiber与其对应,在更新结束后alternate会取代之前的current的成为新的current节点。 其次,Fiber的基本规则: 更新任务分成两个阶段,Reconciliation Phase和Commit Phase。Reconciliation Phase的任务干的事情是,找出要做的更新工作(Diff Fiber Tree),就是一个计算阶段,计算结果可以被缓存,也就可以被打断;Commmit Phase 需要提交所有更新并渲染,为了防止页面抖动,被设置为不能被打断。 PS: componentWillMount componentWillReceiveProps componentWillUpdate 几个生命周期方法,在Reconciliation Phase被调用,有被打断的可能(时间用尽等情况),所以可能被多次调用。其实 shouldComponentUpdate 也可能被多次调用,只是它只返回true或者false,没有副作用,可以暂时忽略。 一些数据结构 下面这些数据结构,可以在源码中查看。 fiber是个链表,有child和sibing属性,指向第一个子节点和相邻的兄弟节点,从而构成fiber tree。return属性指向其父节点。详见源码。 更新队列,updateQueue,是一个链表,有first和last两个属性,指向第一个和最后一个update对象。详见源码。 每个fiber有一个属性updateQueue指向其对应的更新队列。 每个fiber(当前fiber可以称为current)有一个属性alternate,开始时指向一个自己的clone体,update的变化会先更新到alternate上,当更新完毕,alternate替换current。 fiber tree的结构如下图: 走进源码 敲黑板,本文重点 不要去github看源码,目录结构是真的复杂。可以自己写个React16的demo或者直接clone我的demo,使用webpack develop mode,来debug node_modules中的react.development.js和react-dom.development.js。 更新入口肯定是setState方法,下面是我画的Fiber的调用关系图,比较简化,没有画判断条件。请注意,该图基于 React v16.3.2 ,后面源码可能改动,注意时效性。 用户操作引起setState被调用以后,先调用enqueueSetState方法,该方法可以划分成两个阶段(非官方说法,是我个人观点),第一阶段Data Preparation,是初始化一些数据结构,比如fiber, updateQueue, update。 新的update会通过insertUpdateIntoQueue方法,根据优先级插入到队列的对应位置,ensureUpdateQueues方法初始化两个更新队列,queue1和current.updateQueue对应,queue2和current.alternate.updateQueue对应。 第二阶段,Fiber Reconciler,就开始进行任务分片调度,scheduleWork首先更新每个fiber的优先级,这里并没有updatePriority这个方法,但是干了这件事,我用虚线框表示。当fiber.return === null,找到父节点,把所有diff出的变化(side effect)归结到root上。 requestWork,首先把当前的更新添加到schedule list中(addRootToSchedule),然后根据当前是否为异步渲染(isAsync参数),异步渲染调用。scheduleCallbackWithExpriation 方法,下一步高能 scheduleCallbackWithExpriation这个方法在不同环境,实现不一样,chrome等览器中使用requestIdleCallback API,没有这个API的浏览器中,通过requestAnimationFrame模拟一个requestIdleCallback,来在浏览器空闲时,完成下一个分片的工作,注意,这个函数会传入一个expirationTime,超过这个时间活没干完,就放弃了。 执行到performWorkOnRoot,就是fiber文档中提到的Commit Phase和Reconciliation Phase两阶段(官方说法)。 第一阶段Reconciliation Phase,在workLoop中,通过一个while循环,完成每个分片任务。 performUnitOfWork也可以分成两阶段,蓝色框表示。beginWork是一个入口函数,根据workInProgress的类型去实例化不同的react element class。workInProgress是通过alternate挂载一些新属性获得的。 实例化不同的react element class时候会调用和will有关的生命周期方法。 completeUnitOfWork是进行一些收尾工作,diff完一个节点以后,更新props和调用生命周期方法等。 然后进入Commit Phase阶段,这个阶段不能被打断,不再赘述。 任务如何分片及分片的优先级 任务分片,或者叫工作单元(work unit),是怎么拆分的呢。因为在Reconciliation Phase任务分片可以被打断,如何拆分一个任务就很重要了。React16中按照fiber进行拆分,也就是原来的虚拟dom节点。记不记得,开篇我们说到,初始化时候,一个虚拟dom树对应着一个结构一样的fiber tree,只是两个树的节点带的信息有差异。 那么这些任务分片的优先级如何呢? React v16.0.0的优先级是这样划分的: { NoWork: 0, // No work is pending. SynchronousPriority: 1, // For controlled text inputs. Synchronous side-effects. TaskPriority: 2, // Completes at the end of the current tick. HighPriority: 3, // Interaction that needs to complete pretty soon to feel responsive. LowPriority: 4, // Data fetching, or result from updating stores. OffscreenPriority: 5, // Won't be visible but do the work in case it becomes visible. } 可以把Priority分为同步和异步两个类别,同步优先级的任务会在当前帧完成,包括SynchronousPriority和TaskPriority。异步优先级的任务则可能在接下来的几个帧中被完成,包括HighPriority、LowPriority以及OffscreenPriority。 React v16.3.2的优先级,不再这么划分,分为三类:NoWork、sync、async,前两类可以认为是同步任务,需要在当前tick完成,过期时间为null,最后一类异步任务会计算一个expirationTime,在workLoop中,根据过期时间来判断是否进行下一个分片任务,scheduleWork中更新任务优先级,也就是更新这个expirationTime。至于这个时间怎么计算,可以 查看源码。 我的一个疑问 既然是每完成一个任务分片,就看看剩余时间是否够用,不够用就停止,让出主线程,够用就更新任务分片优先级并继续下一个高优先级任务分片,且任务分片的结果是可以被缓存的,为什么与will有关的三个生命周期函数会被多次执行? 一个任务分片要么就是被完成、要么就是没有被完成,怎么会多次被执行? 从源码看,原因是异步渲染时候,会调用requestIdleCallback API,在回调函数中可以获得当前callback参数(也就是fiber的分片任务)还能执行多久,如果时间不够,分片任务会被打断(使用cancelIdleCallback API),下次就只能空闲时重新执行。可以参考。 源码中,处理这个逻辑的函数scheduleCallbackWithExpiration: // cancelDeferredCallback在chrome等浏览器中就是cancelIdleCallback,没有实现这个API的浏览器,React会用requestAnimationFrame模拟一个该函数 // scheduleDeferredCallback同理,chrome等浏览器中是requestIdleCallback function scheduleCallbackWithExpiration(expirationTime) { if (callbackExpirationTime !== NoWork) { // A callback is already scheduled. Check its expiration time (timeout). if (expirationTime > callbackExpirationTime) { // Existing callback has sufficient timeout. Exit. return; } else { // Existing callback has insufficient timeout. Cancel and schedule a // new one. cancelDeferredCallback(callbackID); } // The request callback timer is already running. Don't start a new one. } else { startRequestCallbackTimer(); } // Compute a timeout for the given expiration time. var currentMs = now() - originalStartTimeMs; var expirationMs = expirationTimeToMs(expirationTime); var timeout = expirationMs - currentMs; callbackExpirationTime = expirationTime; callbackID = scheduleDeferredCallback(performAsyncWork, { timeout: timeout }); } 最后 现在有关React Fiber,在v16.3.2版本下的运行,相关博客比较少,v16.0.0源码与v16.3.2有一些差异。个人能力有限,如果你有新的看法,欢迎评论。 原文发布时间为:2018年07月03日 本文来源:掘金 如需转载请联系原作者
前言 虽然前端面试中很少会考到算法类的题目,但是你去比如像腾讯一样的大厂面试的时候就知道了,对基本算法的掌握对于从事计算机科学技术的我们来说,还是必不可少的,每天花上 10 分钟,轻松了解基本算法概念以及前端的实现方式。 另外,掌握了一些基本的算法实现,对于我们日常开发来说,也是如虎添翼,能让我们的 js 业务逻辑更趋高效和流畅。 特别说明 今天这个算法稍微比较复杂,牵扯到的概念也比较多,需要先了解基础知识。我给每篇文章的定位是 10 分钟内应该要掌握下来,所以我就擅作主张地将堆排序算法讲解分割为上、下两篇文章了,希望能让大家阅读起来更清爽愉快。 文章结构 《堆排序(上)》文章结构: 简单的二叉树 简单的满二叉树 简单的完全二叉树 简单的堆 简单的堆分类 《堆排序(下)》文章结构: 算法介绍 轻松实现大顶堆调整 轻松实现创建大顶堆 轻松实现堆排序 复杂度分析 简单的二叉树 要了解堆,必须先了解一下二叉树,两者关系在下一步阐述。 二叉树(Binary Tree)是每一个节点最多有两个分支的树结构,通常分支被称作「左子树」和「右子树」,分支具有左右次序,不能随意颠倒。 二叉树第 i 层最多拥有 2^(i-1) 个节点,深度为 k 的二叉树最多共有 2^(k+1)-1 个节点,且定义根节点所在的层级 i=0,所在的深度 k=0。注意该定义在全文起作用,后续不继续提及。 简单的满二叉树 假设某个二叉树深度为 k,第 i 层拥有 2^(i-1) 个节点,且总共拥有 2^(k+1)-1 个节点,这样的二叉树称为「满二叉树」。 换句话说,二叉树的每一层都是满的,除了最后一层上的节点,每一个节点都具有左节点和右节点。 简单的完全二叉树 假设某个二叉树深度为 k,共有 n 个节点,当且仅当其中的每一个节点,都可以和同样深度为 k 的满二叉树上的,按层序编号相同的节点,也就是序号为 1 到 n 的节点,均一一对应时,这样的二叉树称为「完全二叉树」。 满二叉树一定是完全二叉树,但是完全二叉树不一定是满二叉树。 如下的就不是完全二叉树,树 1 中 10 号节点缺失,树 2 中 6、7 号节点缺失,树 3 中 10、11 号节点缺失。 简单的堆 堆(Heap),一类特殊的数据结构的统称,通常是一个可以被看做一棵树的数组对象。 堆的实现通过构造二叉堆(binary heap),实为二叉树的一种,由于其应用的普遍性,当不加限定时,均指该数据结构的这种实现。 堆,是完全二叉树。 对于给定的某个节点的下标 idx,可以很容易地计算出这个节点的父节点与孩子节点的下标: // 计算父节点的下标 var getParentPos = function(idx){ return Math.floor(idx / 2); } // 计算左子节点的下标 var getLeftChildPos = function(idx){ return 2*idx; }; // 计算右子节点的下标 var getRightChildPos = function(idx){ return 2*idx + 1; }; 如下图,取下标 idx = 4 的节点,则其父节点的下标就为 Math.floor(4/2) === 2,其左子节点的下标就为 2*4 === 8,其右子节点的下标就为 2*4 + 1 === 9。 但将堆转换为数组时,由于数组的初始化下标始终为 0,所以我们的堆数据结构模型在此时要发生如下改变: 的,以上的算法也需要做一下微调: // 计算父节点的下标 var getParentPos = function(idx){ return Math.floor((idx-1) / 2); } // 计算左子节点的下标 var getLeftChildPos = function(idx){ return 2*idx + 1; }; // 计算右子节点的下标 var getRightChildPos = function(idx){ return 2 * (idx+1); }; 简单的堆分类 二叉堆一般分为两种:「大顶堆」和「小顶堆」。 假设有一个堆,其中每个节点的值都大于或等于其左右孩子节点的值,则该堆称为「大顶堆」。「大顶堆」的最大元素值出现在根节点。 假设有一个堆,其中每个节点的值都小于或等于其左右孩子节点的值,则该堆称为「小顶堆」。「小顶堆」的最小元素值出现在根节点。 原文发布时间为:2018年06月29日 原文作者:程序猿何大叔 本文来源:掘金 如需转载请联系原作者
前言 因为在公司,只有一个前端,在做移动端布局的时候。利用px进行布局,发现,需要进行各种手机的适配。 所以在开发第二版的时候,想着引用lib-flexible库(即手淘的方案),询问了上一家公司的同事,会有一些字体和一些诡异的问题。 在无意中,突然一个网友跟我说,他有一个很好用的适配方案。利用vw+rem。听着他分析了一把,感觉还不错,所以大胆尝试了一把 根据CSS3规范,视口单位主要包括以下4个: vw : 1vw 等于视口宽度的1% vh : 1vh 等于视口高度的1% vmin : 选取 vw 和 vh 中最小的那个 vmax : 选取 vw 和 vh 中最大的那个 用视口单位度量,视口宽度为100vw,高度为100vh(左侧为竖屏情况,右侧为横屏情况) 例如,在桌面端浏览器视口尺寸为650px,那么 1vw = 650 * 1% = 6.5px(这是理论推算的出,如果浏览器不支持0.5px,那么实际渲染结果可能是7px)。 兼容性 利用适口单位适配页面 对于移动端开发来说,最为重要的一点是如何适配页面,实现多终端的兼容,不同的适配方式各有千秋,也各有缺点。 就主流的响应式布局、弹性布局来说,通过Media Queries 实现的布局需要配置多个响应断点,而且带来的体验也对用户十分的不友好:布局在响应断点范围内的分辨率维持不表,而在响应断点切换的瞬间,布局带来断层式的切换变换,如同卡带的唱机般"咔咔咔"地一又一下。 而通过采用rem单位的动态计算的弹性布局,则是需要在头部内嵌一段脚本来进行监听分辨率的变化来动态改变根元素字体大小,使得CSS于JS耦合了在一起。 有没有办法能够解决这样的问题呢? 答案是肯定,通过利用适口单位实现适配的页面,是既能解决响应式断层问题,又能解决脚本依赖的问题的。 用法以iPhone6为基准(750) 第一步一般来说,我都会对移动端进行meta标签设置 <meta name="viewport" content="width=device-width, initial-scale=2.0, maximum-scale=2.0, minimum-scale=2.0, user-scalable=no"> 因为iPhone6以及大多数的dpr为2,为了第二步的方便进行换算 第二步设置body、html的font-size html { font-size: 13.3333333333333vw // 100px } 13.3333333333333vw怎么来? 100 / 750 = 0.133333333333333vw 我们把这个适口当做100px,然后除于750换算得出 1px = 0.133333333333333vw 那么整个适口等于 0.133333333333333 * 100 = 13.3333333333333vw = 100px 最终于得出 100px = 1rem 通过这样子换算我们利用vw把rem转换成了以100px为基准 那么在项目上就很好地可以进行使用了 div { // width: 100px; width: 1rem; } span { // height: 12px height: .12rem } 原文发布时间为:2018年06月20日 原文作者:聪明的竹子爱学习 本文来源:掘金 如需转载请联系原作者
这是什么?我适合阅读吗? ng.ant.design/docs/introd… NG-ZORRO 是由阿里巴巴阿里云和计算平台事业部的小伙伴为 Ant Design 开发的 Angular 版本,是一个适用于构建中后台应用的组件库。该系列文章是为 ng-zorro 的用户提供的教程项目 Today 的配套教程。如果你是新用户,想要了解一下 ng-zorro 的使用姿势,或者你是 Angular 初学者,想要通过一个项目来锻炼自己的开发能力,欢迎阅读。 目录和链接 本系列文章共有以下几篇(更新中): (本篇)项目简介,学习在项目中添加 ng-zorro,编写最简单的 setup(初始设置)界面 你可以点击下面的链接预览: Today 点击下面的链接获取源代码: today-ng 点击下面的链接获取按照行文顺序 commit 的源代码: wendzhue/today-ng-steps 项目介绍 我们的教程项目要编写一个名为 Today 的待办事项应用。 Today 还有一个基于 electron 和 Vue 的版本,完成度比较高,建议你浏览该项目的主页(对于 Mac 用户我们还提供了能直接运行的 dmg,其他平台的用户请自行 build)。 该应用主要包含以下几个界面,并提供如下的功能: 主界面,分为左边的清单列表和右边的待办事项列表。除了最基本的增加、删除、重命名清单和待办事项外,我们还要支持改变待办事项的排序方法,特别的,我们还要提供“建议”帮助用户确定今天(或者说接下来)要做的事情 待办详情界面:在用户点击一个待办事项之后,这个界面会弹出以让用户对改办事项进行更多的修改,包括修改截止时间、计划时间、待办标题、详情、是否需要提醒等等 总结界面,我们帮助用户记录他们每一天完成任务的情况,并且通过日历的形式展示 初始化(setup)界面 设置界面 是不是有点期待了呢?我们现在就开始! 项目构建 想要了解更多关于项目构建的内容,请阅读 ng-zorro 的文档。 我们先构建一个新项目,然后在这个项目里安装 ng-zorro。 $ ng new today-ng $ cd today-ng $ ng add ng-zorro-antd 现在尝试 ng serve,并打开 locahost:4200,你应该可以看到 ng-zorro 的标志了,恭喜你顺利的为你的项目引入了 ng-zorro! 划分模块并设置路由 我们的应用分为好几个页面,为了在代码的层面也有对应的划分,并保持好的拓展性和可维护性,我们将每个大页面都划分为一个独立的模块。这篇文章里,我将会向你展示如何做最简单的一个模块 setup。 我们先创建 setup module 和对应的 component: ng g m pages/setup ng g c pages/setup --module pages/setup 给项目添加路由。创建 app/app-routing.module.ts 文件,然后编写如下内容: import { NgModule } from '@angular/core'; import { RouterModule, Routes } from '@angular/router'; import { SetupComponent } from './pages/setup/setup.component'; const routes: Routes = [ { path: 'setup', component: SetupComponent }, { path: '', redirectTo: '/setup', pathMatch: 'full' } ]; @NgModule({ imports: [ RouterModule.forRoot(routes) ], exports: [ RouterModule ] }) export class AppRoutingModule {} 修改 app.module.ts 来引入 setup 以及路由模块: // ... import { AppRoutingModule } from './app-routing.module'; import { SetupModule } from './pages/setup/setup.module'; registerLocaleData(zh); @NgModule({ declarations: [ AppComponent ], imports: [ BrowserModule, BrowserAnimationsModule, FormsModule, HttpClientModule, NgZorroAntdModule, AppRoutingModule, SetupModule ], providers: [ { provide: NZ_I18N, useValue: zh_CN } ], bootstrap: [ AppComponent ] }) export class AppModule { } 最后再更新 app.component.html 的内容为 <router-outlet></router-outlet>,这样,我们就划分好了第一个模块并处理了部分路由,现在请看浏览器,是不是显示 setup works! 了呢? Setup 模块 在我们开始写界面之前,先简单分析一下。显然,我们需要把用户名和初始化标记存入 local storage,也很显然,我们不应该把 localStorage.set 等代码直接下载组件文件中,一是因为其他 module 和 component 肯定也要访问用户名信息,二是这提高了代码的耦合性——如果我们切换了存储机制,我们需要到处修改——所以我们要在这里引入 local storage service。除此之外,既然多个 module 需要读取同一信息,我们就要对键值 key 做出约定。 想清楚了,我们再开始写代码,在命令行中输入: $ ng g s services/local-storage --module app 然后在 local-storage.service.ts 中编码: import { Injectable } from '@angular/core'; const ls = localStorage; @Injectable() export class LocalStorageService { constructor() { } public get<T>(key: string): any { return JSON.parse(ls.getItem(key)) as T; } public getList<T>(key: string) { const before = ls.getItem(key); return before ? (JSON.parse(before) as T[]) : []; } public set(key: string, value: any): void { if (!value && value === undefined) { return; } const arr = JSON.stringify(value); ls.setItem(key, arr); } } 在 local-storage.service.ts 同目录下创建 local-storage.namespace.ts 文件(为了简化后面的教程,我这里就把后面会用到的键全告诉你了): export const GLOBAL_NAMESPACE = 'today.'; export const APP_INFO_NAMESPACE = GLOBAL_NAMESPACE + 'appInfo.'; export const INIT_FLAG = APP_INFO_NAMESPACE + 'initFlag'; export const START_USING_DATE = APP_INFO_NAMESPACE + 'startUsingDate'; export const USER_INFO_NAMESPACE = GLOBAL_NAMESPACE + 'userInfo.'; export const USERNAME = USER_INFO_NAMESPACE + 'username'; export const AVATAR_CODE = USER_INFO_NAMESPACE + 'avatarCode'; export const TODO_NAMESPACE = GLOBAL_NAMESPACE + 'todo.'; export const TODOS = TODO_NAMESPACE + 'todos'; export const LIST_NAMESPACE = GLOBAL_NAMESPACE + 'list.'; export const LISTS = LIST_NAMESPACE + 'lists'; export const SUMMARY_NAMESPACE = GLOBAL_NAMESPACE + 'summary.'; export const LAST_SUMMARY_DATE = SUMMARY_NAMESPACE + 'lastSummaryDate'; export const SUMMARIES = SUMMARY_NAMESPACE + 'summaries'; 在用 local storage 进行存储的时候使用命名空间是个最佳实践,命名空间可以是应用名加模块名等形式。 这样就搞定了 service 了,接下来写组件,在 setup.module.html 当中编写界面内容: <div class="full-screen page-content"> <div class="wrapper"> <img class="logo-img" src="./assets/img/logo.png" alt=""> <div class="text-wrapper"> <h1 class="title-text"> Today </h1> </div> <input nz-input placeholder="请输入你喜欢的用户名" #usernameInput [(ngModel)]="username"> <button nz-button [nzType]="'primary'" (click)="completeSetup()" [disabled]="!usernameInput.value"> 开始 </button> <div class="copy-right"> Copyright © 2018 Wendell Hu </div> </div> </div> 在 setup.module.ts 当中编写界面逻辑: import { Component, HostBinding, OnInit } from '@angular/core'; import { LocalStorageService } from '../../services/local-storage/local-storage.service'; import { USERNAME, INIT_FLAG, START_USING_DATE } from '../../services/local-storage/local-storage.namespace'; import { getTodayTime } from '../../../utils/time'; @Component({ selector: 'app-setup', templateUrl: './setup.component.html', styleUrls: [ './setup.component.less' ] }) export class SetupComponent implements OnInit { username: string; constructor( private store: LocalStorageService ) { } ngOnInit() { } completeSetup(): void { this.store.set(INIT_FLAG, true); this.store.set(START_USING_DATE, getTodayTime()); this.store.set(USERNAME, this.username); } } 完成(CSS 请自行编写,推荐使用 CSS 的各种预处理器)!你可以在这个界面输入用户名,点击按钮之后,你就能在 local storage 检视面板中看到有三个字段存储了值。 原文发布时间为:2018年06月02日 本文来源:掘金 如需转载请联系原作者
前言 现在的前端面试不管你用的什么框架,总会问你这个框架的双向绑定机制,有的甚至要求你现场实现一个双向绑定出来,那对于没有好好研究过这方面知识的同学来说,当然是很难的,接下来本文用160行代码带你实现一个极简的双向绑定机制。如果喜欢的话可以点波赞/关注,支持一下,希望大家看完本文可以有所收获。 本文是在面试题:你能写一个Vue的双向数据绑定吗?的基础上仔细研究+改动,并添加了详细注释,而成的。 demo地址: codepen:仿Vue极简双向绑定 Github:仿Vue极简双向绑定 了解Object.defineProperty(): 这个API是实现双向绑定的核心,最主要的作用是重写数据的get、set方法。 使用方式: let obj = { singer: "周杰伦" }; let value = "青花瓷"; Object.defineProperty(obj, "music", { // value: '七里香', // 设置属性的值 下面设置了get set函数 所以这里不能设置 configurable: false, // 是否可以删除属性 默认不能删除 // writable: true, // 是否可以修改对象 下面设置了get set函数 所以这里不能设置 enumerable: true, // music是否可以被枚举 默认是不能被枚举(遍历) // get,set设置时不能设置writable和value,要一对一对设置,交叉设置/同时存在 就会报错 get() { // 获取obj.music的时候就会调用get方法 // let value = "强行设置get的返回值"; // 打开注释 读取属性永远都是‘强行设置get的返回值’ return value; }, set(val) { // 将修改的值重新赋给song value = val; } }); console.log(obj.music); // 青花瓷 delete obj.music; // configurable设为false 删除无效 console.log(obj.music); // 青花瓷 obj.music = "听妈妈的话"; console.log(obj.music); // 听妈妈的话 for (let key in obj) { // 默认情况下通过defineProperty定义的属性是不能被枚举(遍历)的 // 需要设置enumerable为true才可以 否则只能拿到singer 属性 console.log(key); // singer, music } 示例demo: 对,这里有个demo。 画一下重点: get,set设置时不能设置writable和value, 他们是一对情侣的存在,交叉设置或同时存在,会报错 通过defineProperty设置的属性,默认不能删除,不能遍历,当然你可以通过设置更改他们。 get、set 是函数,可以做的事情很多。 兼容性:IE 9,Firefox 4, Chorme 5,Opera 11.6,Safari 5.1 更详细的可以看一下MDN 实现思路: mvvm系列的双向绑定,关键步骤: 实现数据监听器Observer,用Object.defineProperty()重写数据的get、set,值更新就在set中通知订阅者更新数据。 实现模板编译Compile,深度遍历dom树,对每个元素节点的指令模板进行替换数据以及订阅数据。 实现Watch用于连接Observer和Compile,能够订阅并收到每个属性变动的通知,执行指令绑定的相应回调函数,从而更新视图。 mvvm入口函数,整合以上三者。 流程图: 这部分讲的很清楚,现在有点懵逼也没关系,看完代码,自己copy下来玩一玩之后,回头再看实现思路,相信会有收获的。 具体代码实现: html结构: <div id="app"> <input type="text" v-model="name"> <h3 v-bind="name"></h3> <input type="text" v-model="testData1"> <h3>{{ testData1 }}</h3> <input type="text" v-model="testData2"> <h3>{{ testData2 }}</h3> </div> 看到这个模板,相信用过Vue的同学都不会陌生。 调用方法: 采用类Vue方式来使用双向绑定: window.onload = function () { var app = new myVue({ el: '#app', // dom data: { // 数据 testData1: '仿Vue', testData2: '极简双向绑定', name: 'OBKoro1' } }) } 创建myVue函数: 实际上这里是我们实现思路中的第四步,用于整合数据监听器this._observer()、指令解析器this._compile()以及连接Observer和Compile的_watcherTpl的watch池。 function myVue(options = {}) { // 防止没传,设一个默认值 this.$options = options; // 配置挂载 this.$el = document.querySelector(options.el); // 获取dom this._data = options.data; // 数据挂载 this._watcherTpl = {}; // watcher池 this._observer(this._data); // 传入数据,执行函数,重写数据的get set this._compile(this.$el); // 传入dom,执行函数,编译模板 发布订阅 }; Watcher函数: 这是实现思路中的第三步,因为下方数据监听器_observer()需要用到Watcher函数,所以这里就先讲了。 像实现思路中所说的,这里起到了连接Observer和Compile的作用: 在模板编译_compile()阶段发布订阅 在赋值操作的时候,更新视图 // new Watcher() 为this._compile()发布订阅+ 在this._observer()中set(赋值)的时候更新视图 function Watcher(el, vm, val, attr) { this.el = el; // 指令对应的DOM元素 this.vm = vm; // myVue实例 this.val = val; // 指令对应的值 this.attr = attr; // dom获取值,如value获取input的值 / innerHTML获取dom的值 this.update(); // 更新视图 } Watcher.prototype.update = function () { this.el[this.attr] = this.vm._data[this.val]; // 获取data的最新值 赋值给dom 更新视图 } 没有看错,代码量就这么多,可能需要把整个代码连接起来,多看几遍才能够理解。 实现数据监听器_observer(): 实现思路中的第一步,用Object.defineProperty()遍历data重写所有属性的get set。 然后在给对象的某个属性赋值的时候,就会触发set。 在set中我们可以监听到数据的变化,然后就可以触发watch更新视图。 myVue.prototype._observer = function (obj) { var _this = this; Object.keys(obj).forEach(key => { // 遍历数据 _this._watcherTpl[key] = { // 每个数据的订阅池() _directives: [] }; var value = obj[key]; // 获取属性值 var watcherTpl = _this._watcherTpl[key]; // 数据的订阅池 Object.defineProperty(_this._data, key, { // 双向绑定最重要的部分 重写数据的set get configurable: true, // 可以删除 enumerable: true, // 可以遍历 get() { console.log(`${key}获取值:${value}`); return value; // 获取值的时候 直接返回 }, set(newVal) { // 改变值的时候 触发set console.log(`${key}更新:${newVal}`); if (value !== newVal) { value = newVal; watcherTpl._directives.forEach((item) => { // 遍历订阅池 item.update(); // 遍历所有订阅的地方(v-model+v-bind+{{}}) 触发this._compile()中发布的订阅Watcher 更新视图 }); } } }) }); } 实现Compile 模板编译 这里是实现思路中的第三步,让我们来总结一下这里做了哪些事情: 首先是深度遍历dom树,遍历每个节点以及子节点。 将模板中的变量替换成数据,初始化渲染页面视图。 把指令绑定的属性添加到对应的订阅池中 一旦数据有变动,收到通知,更新视图。 myVue.prototype._compile = function (el) { var _this = this, nodes = el.children; // 获取app的dom for (var i = 0, len = nodes.length; i < len; i++) { // 遍历dom节点 var node = nodes[i]; if (node.children.length) { _this._compile(node); // 递归深度遍历 dom树 } // 如果有v-model属性,并且元素是INPUT或者TEXTAREA,我们监听它的input事件 if (node.hasAttribute('v-model') && (node.tagName = 'INPUT' || node.tagName == 'TEXTAREA')) { node.addEventListener('input', (function (key) { var attVal = node.getAttribute('v-model'); // 获取v-model绑定的值 _this._watcherTpl[attVal]._directives.push(new Watcher( // 将dom替换成属性的数据并发布订阅 在set的时候更新数据 node, _this, attVal, 'value' )); return function () { _this._data[attVal] = nodes[key].value; // input值改变的时候 将新值赋给数据 触发set=>set触发watch 更新视图 } })(i)); } if (node.hasAttribute('v-bind')) { // v-bind指令 var attrVal = node.getAttribute('v-bind'); // 绑定的data _this._watcherTpl[attrVal]._directives.push(new Watcher( // 将dom替换成属性的数据并发布订阅 在set的时候更新数据 node, _this, attrVal, 'innerHTML' )) } var reg = /\{\{\s*([^}]+\S)\s*\}\}/g, txt = node.textContent; // 正则匹配{{}} if (reg.test(txt)) { node.textContent = txt.replace(reg, (matched, placeholder) => { // matched匹配的文本节点包括{{}}, placeholder 是{{}}中间的属性名 var getName = _this._watcherTpl; // 所有绑定watch的数据 getName = getName[placeholder]; // 获取对应watch 数据的值 if (!getName._directives) { // 没有事件池 创建事件池 getName._directives = []; } getName._directives.push(new Watcher( // 将dom替换成属性的数据并发布订阅 在set的时候更新数据 node, _this, placeholder, 'innerHTML' )); return placeholder.split('.').reduce((val, key) => { return _this._data[key]; // 获取数据的值 触发get 返回当前值 }, _this.$el); }); } } } 完整代码&demo地址 GitHub完整代码 codepen:仿Vue极简双向绑定 Github:仿Vue极简双向绑定 结语 本文只是一个简单的实现双向绑定的方法,主要目的是帮助各位同学理解mvvm框架的双向绑定机制,也并没有很完善,这里还是有很多缺陷,比如:没有实现数据的深度对数据进行get、set等。希望看完本文,大家能有所收获。 原文发布时间为:2018年06月25日 原文作者:OBKoro1 本文来源:掘金 如需转载请联系原作者
-------------------------