Crystal Follow your heart http://cuijing.org/ Wed, 12 Jul 2017 00:05:55 +0800 Wed, 12 Jul 2017 00:05:55 +0800 Jekyll v3.2.1 跨年2017 <p><br /></p> <p>之所以一直坚持着写年终总结,大概是想着至少一年能给自己一个快照,2017来了,我在成都春熙路跨年。</p> <p>在自己慢慢长大的过程中,心态是在不断变化的,但随着成熟后有一个理念没有变,那就是你想让自己更好,一定不是坐以待毙等着别人来改变你,而是要主动达成你想做的事情,这又能引到一个鸡汤思维,“先赚它个一亿”,“我靠,这怎么可能”,----no,你该问怎样能做到。</p> <p><br /></p> <h3>2016</h3> <p><strong>第一个话题,转岗。</strong></p> <p>2016年的春节,舅舅问过我一个问题,如果让你重新选择,你想做什么职业?我说现在就挺好,或者当时研究生如果选择做工业设计也挺好。这些诱惑我的,便是用技术为生活做一些小小的优化,也许都还不能谈得上创新,但是你知道,通过自己的能动性略微改变一点什么,这是件十分惊喜的事情。</p> <p>转岗在当时就已经深埋我的内心,我选择了前端的领域,也希望在这个领域中经历更多的事情,看到自己在这个浪潮中的变化和贡献。</p> <p>最终转岗成功经历了两次流程,有一些波澜也有很多感想,但于我而言,现在最重要的是最快将能力提升,融入这个领域(附:你知道杀人游戏的角色技巧是什么么?就是把自己当作想要扮演的角色,比如你是杀手但想隐藏身份伪装成平民,那么白天说话时就需要按平民角色去思考和表达)。</p> <p>总结一点:</p> <ul> <li>你所经历的,学过的,都在影响着你往前踏出的每一步,或者构建成你的特质,或者影响着你的三观和选择,正向地看待这一切,然后努力地走好每一步。</li> <li>折腾会带来新鲜感也会引起稍许的畏惧,但请不要过于忧虑,把人生当作一场经历,把选择往后看五年十年,你会发现当下这个只不过是去食堂选了今天吃哪个菜而已。</li> <li>把自己当作你最想成为的自己,然后想想这样的自己会怎么做事,做些什么。</li> <li>简单、坦诚</li> </ul> <p><br /></p> <p><strong>第二个话题,恋爱。</strong></p> <p>缘分是一件很奇妙的事情,在我被老妈催促的年龄,本来谈恋爱这事披上了任务性了,但是因为转岗遇到了轻零,一切又好似打破。那段时间他常常来找我聊天,当时正好看到采铜老师的一本书讲到婚恋的拇指法则“生理上有冲动,精神上受鼓舞,沟通上很流畅”,感觉“沟通上很流畅”便是第一个十分重要的点,于是在接下来的一段相处后,状态轻松虽偶有不习惯感,但聊得来,且两个人都比较有自己的想法并愿意通过努力付诸实践,最后我们决定在一起了。一起去做了很多以前没做过或者不敢做的事情,磨合彼此的习惯,调整彼此不同阶段的状态,真不得不承认恋爱让人重返18岁。</p> <p>在以后的日子里,期望我们能一起经历更多,一起成为更好的自己和彼此。</p> <p><br /></p> <p><strong>第三个话题,理财。</strong></p> <p>做得不够,但也有点变化。给自己买了个小house,整个流程算是较快但也十分繁琐,包括看楼盘、户型、谈价格、定金、合同、贷款、还款等等,常有深水区。期间也想买爱车,合计后发现买了生活会拮据,车牌竞价了两次也都比较悲剧的差一点。</p> <p>年底的时候因为正好工作也要转财富线,还有人民币持续贬值的现状,我也浅浅看了下比特币和美股,但还未深入,简单说说自己的看法:</p> <ul> <li>比特币:高风险投资,但它背后去中心化的区块链技术的确十分具有开创性价值,当年读研的时候就知道这东西但没有在意,现在不仅本身价值疯长(2016年涨幅135%),同时也有类似的虚拟货币进入交易市场,也有越来越多的实体商家支持比特币支付,未来它的价值将如何衡量大家也都只是猜测,如果你感兴趣,可以考虑先了解购买和使用流程,它的暴涨很诱人但同时你需要知道这是高风险投资。</li> <li>美股:美股比A股自由度更大,但整个市场调控较成熟一些,对于新手来说,现在的开户和交易流程都十分方便,从自己熟知的国内在美国上市的公司入手,子柳常说的长期持有腾讯股(港股)一定赚,李开复也演讲说人工智能时代买英达尔肯定没错。</li> </ul> <p>2016年的经济已经十分动荡了,房价暴涨后各种限购、人民币持续贬值、美元加息,而且17年美元可能还会多次加息,作为小屁民,感觉也到了必须要觉知理财的时候了。</p> <p><br /></p> <p>其他有一些数据复盘:</p> <ul> <li>读书:豆瓣上mark的竟然只有9本!看的书肯定不止,但是打脸的是我也想不起太多看完醍醐灌顶的东西。</li> <li>写文章:博客有12篇,另ATA还有几篇,公众号有2篇,笔记里没放出来的总结若干篇,明年继续坚持。顺手在读读日报维护了几个专栏,最大订阅数有800啦。</li> <li>电影:豆瓣mark看了30部。</li> <li>旅行:9天的青海湖西北自驾游、舟山东极岛、宁德霞浦、成都</li> </ul> <p><br /></p> <h3>新的一年</h3> <p>有三件很明确的待办事情:</p> <ul> <li>(ing)技能升级,晋升P7。</li> <li>(ing)小house的装修,有更多自己的想法加入进去,但目前还未系统学习。</li> <li>(done)跟轻零哥的新西兰之行,去体验下极限运动。</li> </ul> <p>还有坚持读书、总结、写文章的习惯继续保持。</p> <p><br /></p> <h3>后记</h3> <p>如果说2016年的主题词是“改变”,那么2017便是“积累”,厚积薄发,希望自己在新的一年增强带入感,自信尝试,勇敢担当。</p> Sun, 01 Jan 2017 00:00:00 +0800 http:/cuijing.org/2017/01/go-to-2017/ http:/cuijing.org/2017/01/go-to-2017/ 2017 LIFE 关于单元测试 <p><br /></p> <h2>前言</h2> <p>你来看这篇文章一定是打算做单元测试了,或者说也在考虑单元测试是否能真正帮助到你?</p> <p>Q1: 为什么一定要做单元测试?</p> <blockquote> <p>单元测试是一切测试进行的基石,它针对最小粒度unit的测试,位于测试金字塔的最底层,再往上是Service(比如淘宝体系下的HSF接口测试)-UI(比如基于webdriver的UI自动化测试),在实践的过程中你会发现单元测色是对代码进行测试的最有效工具之一</p> <p>有更快的 <strong>开发-验证</strong> 循环(开发过程被划分为更小的快速迭代过程),无需等待依赖实现,无需特定环境</p> <p>节省调试时间(越复杂的功能,潜在收益越大)</p> <p>帮助应对业务的快速变化</p> <p>便于项目交接或多人协作</p> </blockquote> <p>Q2: 单元测试的粒度要做多细?</p> <blockquote> <p>我的感受是:一不追求数量二不追求覆盖率,设计有效的案例进行单元测试实践。</p> <p>StackOverflow上敏捷开发实践的奠基人Kent Beck也对此问题表达了观点:“我倾向于去对那些有意义的错误做测试,所以,我对一些比较复杂的条件逻辑会异常地小心。”</p> <p>UT的粒度是多少,这个不重要,重要的是你会不会自己思考你的软件应该怎么做,怎么测</p> </blockquote> <p>相关文档阅读: * <a href="http://coolshell.cn/articles/8209.html">单元测试要做多细</a></p> <p><br /></p> <h2>哪些场景需要做单元测试</h2> <p>之前在工作中根据webx工程的分层总结了下单元测试、接口测试、UI测试的场景划分,这是一个较为理想的实践建议: <img src="../../../img/unit-test.png" alt=""></p> <p>为什么说是较为理想的实践建议?看看现实 * 有人说dal层的CRUD我都写了多少年了,项目时间这么紧的情况下还要写这些无趣的增删改查单测 * 翻翻现有的应用代码,偶有零散的单元测试代码可见,但基本数据都是一次性(也就是不可持续跑起来),或者单元测试中无assert(只有System.out.println) * 环境常常跑不起来 * 数据准备麻烦,或者依赖的外部环境太多</p> <p>我只能说,如果你能从中获得收益,就更能辩证地看待这些问题。</p> <p><br /></p> <h2>怎么写单元测试</h2> <blockquote> <p><strong>单元测试的流程:场景(数据)构造-执行-断言(校验)。</strong></p> </blockquote> <h3>1. 涉及的框架选型</h3> <p>主要在于单元测试框架、mock框架、数据准备、断言库。 之前对它们做了些简单的比较:<a href="http://gitlab.alibaba-inc.com/cainiao-quality-of-wms-tms/doc/wikis/unit-test-framework-compare">框架对比调研</a></p> <p><br /></p> <h3>2. 写一个单元测试</h3> <p>基于前面也提到的一些问题,实践中我们常常省略了对dal层的单元测试,而是对一些逻辑复杂的集成单元进行单测(往往是biz中的一些manager实现类),这里用最基础的Junit4+Mockito来演示一下整个流程。</p> <h3>2.1 pom依赖</h3> <div class="highlight"><pre><code class="language-text" data-lang="text">&lt;dependency&gt; &lt;groupId&gt;junit&lt;/groupId&gt; &lt;artifactId&gt;junit&lt;/artifactId&gt; &lt;version&gt;4.11&lt;/version&gt; &lt;scope&gt;test&lt;/scope&gt; &lt;/dependency&gt; &lt;dependency&gt; &lt;groupId&gt;com.taobao.hsf&lt;/groupId&gt; &lt;artifactId&gt;hsfunit&lt;/artifactId&gt; &lt;version&gt;1.0.5-SNAPSHOT&lt;/version&gt; &lt;scope&gt;test&lt;/scope&gt; &lt;/dependency&gt; &lt;dependency&gt; &lt;groupId&gt;org.mockito&lt;/groupId&gt; &lt;artifactId&gt;mockito-all&lt;/artifactId&gt; &lt;version&gt;2.0.2-beta&lt;/version&gt; &lt;/dependency&gt; </code></pre></div> <p><br /></p> <h3>2.2 BaseTestCase</h3> <p>bean的加载、pandora容器启动(hsfunit)</p> <div class="highlight"><pre><code class="language-text" data-lang="text">public class BaseTestCase { protected static ApplicationContext ac = null; private static String[] configXml = { &quot;basecase.xml&quot; }; static { try { HSFEasyStarter.start(&quot;hsfunit&quot;, &quot;1.4.9.6&quot;); ac = new ClassPathXmlApplicationContext(configXml); } catch (Exception e) { System.out.println(e.getMessage()); } } } </code></pre></div> <p><br /></p> <h3>2.3 测试脚本编写</h3> <p>设计好要测试的场景,包括需要的数据准备、待测试接口、校验点。</p> <div class="highlight"><pre><code class="language-text" data-lang="text">public class BomMsgUnitTest extends BaseTestCase{ private BomCore bomCore; private InventoryBomDAO inventoryBomDAO; private static final long WAREHOUSE_ID = 8; private static final long OWNER_ID = 8; private List&lt;InventoryBomDTO&gt; boms = new ArrayList&lt;InventoryBomDTO&gt;(); private static final long BOM_NUM = 10L; @Before public void init(){ bomCore = (BomCore)ac.getBean(&quot;bomCore&quot;); inventoryBomDAO = (InventoryBomDAO)ac.getBean(&quot;inventoryBomDAO&quot;); //数据初始化 createBomAndStart(); } @After public void destroy(){ //省略数据清理 } @Test public void test_N_CacheUpdate(){ int cacheCountOld = bomPartMap.get(WAREHOUSE_ID + &quot;_&quot; + OWNER_ID).size(); if (cacheCountOld &gt; 0){ System.out.println(&quot;初始时的cache列表:&quot;); listCacheBom(WAREHOUSE_ID, OWNER_ID); } for(int i = 0; i &lt; BOM_NUM/2; i++){ bomCore.stopUsing(boms.get(i).getId()); } int bomCount, cacheCount; List&lt;InventoryBomDO&gt; list = inventoryBomDAO.getBomPartList(WAREHOUSE_ID, OWNER_ID); bomCount = list.size(); cacheCount = bomPartMap.get(WAREHOUSE_ID + &quot;_&quot; + OWNER_ID).size(); System.out.println(&quot;bomCount = &quot; + bomCount + &quot;, cacheCount = &quot; + cacheCount); System.out.println(&quot;stop更新后的cache列表:&quot;); listCacheBom(WAREHOUSE_ID, OWNER_ID); Assert.assertEquals(bomCount, cacheCount); Assert.assertEquals(cacheCountOld, cacheCount + BOM_NUM / 2); } } </code></pre></div> <p><br /> </p> <h3>2.4 当你需要mock</h3> <p>在单元测试中我们常常希望关注到核心代码逻辑本身的正确性,而暂时不用去关注所有的外部依赖,这时mock框架可以帮到我们。抽象来看适用两种场景:</p> <ul> <li>直接创建mock对象,并设置对象方法调用时的预期返回值</li> <li>被测bean A中,mock A依赖的bean B的行为动作(在spring框架中做待测类的mock时常用)</li> <li>mock一些异常场景,比如按预期抛异常验证事务性回滚是否正确</li> </ul> <p>具体使用实例参加之前写过的一篇文章:<a href="http://cuijing.org/2016/10/spring-mockito/">在spring中使用mockito</a></p> <p><br /></p> <h3>2.5 如何管理配置文件</h3> <p>常常需要把web子工程下的关于bean的配置文件拷贝到测试目录src/test/resources下,但这会带来一个问题,同一份配置文件在多处做维护,一旦配置出现修改都会造成不一致,最终会使得测试用例运行失败;而且维护需要成本。</p> <blockquote> <p>一个最直接的方式,在测试BaseTestCase中使用配置文件时直接引用web子工程下的是否可行?可以。</p> </blockquote> <p>(假设我们所有的单元测试单独写在utest子工程下)</p> <p><strong>一方面需要在utest的pom文件中,maven-surefire-plugin插件中增加classpath路径,这样就对路径可见</strong>,如下:</p> <div class="highlight"><pre><code class="language-text" data-lang="text">&lt;plugin&gt; &lt;groupId&gt;org.apache.maven.plugins&lt;/groupId&gt; &lt;artifactId&gt;maven-surefire-plugin&lt;/artifactId&gt; &lt;configuration&gt; &lt;includes&gt; &lt;include&gt;**/**Test.java&lt;/include&gt; &lt;/includes&gt; &lt;argLine&gt;-Xmx512m&lt;/argLine&gt; &lt;additionalClasspathElements&gt; &lt;additionalClasspathElement&gt; ${project.parent.basedir}/web/src/main/webapp &lt;/additionalClasspathElement&gt; &lt;/additionalClasspathElements&gt; &lt;/configuration&gt; &lt;/plugin&gt; </code></pre></div> <p>另一方面,一般webx工程的bean配置总入口在webx.xml中,这里的import的配置分很多类,比如表单验证配置等等,但我们关心的是各个模块的bean导入部分,所以为了utest子工程引用这些bean更方便,各个模块的bean配置应放到一起,并<strong>规范一个总的引用入口</strong>。</p> <p>是否这样就可以了?</p> <p>等等,发现web子工程下bean的配置不是纯粹的.xml文件,而是使用了auto-config进行替换的.vm文件,这就麻烦了,因为auto-config是运行期间做替换的,如果使用maven的autoconfig插件,只有在执行mvn package或者mvn install才会激活替换过程。也就是在执行单元测试时是没法做这个动态替换的。</p> <p>综合上述,我们可以规范一下配置使用方式:</p> <ul> <li>web子工程下的各个模块的bean配置放到一起,并规范一个总入口,如上面例子中的basecase.xml;</li> <li>这些bean的配置不使用autoconfig工具做动态替换,只在antx.properties维护相应的版本号信息等</li> <li>utest的pom文件中增加classpath路径;</li> <li>utest中引入总入口的bean配置,并增加antx.properties来做一些版本号信息的替换,即一下这段</li> </ul> <div class="highlight"><pre><code class="language-text" data-lang="text">&lt;!--占位符配置开始 --&gt; &lt;bean id=&quot;propertyConfigurer&quot; class=&quot;org.springframework.beans.factory.config.PropertyPlaceholderConfigurer&quot;&gt; &lt;property name=&quot;location&quot;&gt; &lt;value&gt;antx.properties&lt;/value&gt; &lt;/property&gt; &lt;/bean&gt; </code></pre></div> <p><br /></p> <h2>代码规范</h2> <ul> <li>测试类命名 <ul> <li>Java类路径src/main/java/下com.cainiao.wmpinventory.BomMsg</li> <li>对应的测试类路径src/test/java下com.taobao.wmpinventory.BomMsgUnitTest (单元测试用UTest结尾,接口集成测试用ITest结尾)</li> </ul></li> <li>测试方法命名(N表示正常流,E表示异常流,description一定要用英文) <ul> <li>test_N_description </li> <li>test_E_description</li> </ul></li> <li>脚本代码中一定要写断言!禁止使用System.out.println做断言</li> </ul> <p><br /></p> Thu, 15 Dec 2016 00:00:00 +0800 http:/cuijing.org/2016/12/about-unit-test/ http:/cuijing.org/2016/12/about-unit-test/ 单元测试 测试 在spring中使用mockito <p><br /></p> <h2>背景</h2> <p>最近在做的一个双11的业务项目中,对业务系统中做一些复杂业务层类的单元测试时,一方面,注入bean的会是个很头大的事情,因为bean与bean之间有各种依赖关系,一些bean是通过注解的方式实现,一些依赖外部的bean是通过xml来配置的(如何在webx中维护test下配置文件,可以另外讲了);另一方面,对依赖的服务,常常希望它呈现不同的状态,用mock方式最简便不过了。</p> <p>mock的框架有好多(Mockito, EasyMock, PowerMock等),在支持的特性和语法使用上有些许差别,总的来说mockito最较为常见,语法直观文档较多,常用场景够支持了,这里就用的mockito为例。<a href="http://site.mockito.org/mockito/docs/current/org/mockito/Mockito.html#injectmocks_annotation">Mockito官方文档</a></p> <p>怎么mock有很多层次的理解,我主要举下例子:</p> <ul> <li>直接创建mock对象,并设置对象方法调用时的预期返回值</li> <li><strong>被测bean A中,mock A依赖的bean B的行为动作(在spring框架中做待测类的mock时常用)</strong></li> </ul> <p><br /></p> <h2>使用方式</h2> <h3>1. 依赖包:</h3> <div class="highlight"><pre><code class="language-text" data-lang="text">&lt;dependency&gt; &lt;groupId&gt;org.mockito&lt;/groupId&gt; &lt;artifactId&gt;mockito-all&lt;/artifactId&gt; &lt;version&gt;2.0.2-beta&lt;/version&gt; &lt;/dependency&gt; </code></pre></div> <h3>2. 创建mock对象,并设置对象方法调用时的预期返回值</h3> <div class="highlight"><pre><code class="language-text" data-lang="text">//创建mock对象,参数可以是类,也可以是接口 List&lt;String&gt; list = mock(List.class); //设置方法的预期返回值 when(list.get(0)).thenReturn(&quot;helloworld&quot;); //验证方法调用(是否调用了get(0)) verify(list).get(0); </code></pre></div> <h3>3. 被测bean A中,mock A依赖的bean B的行为动作</h3> <blockquote> <p><strong>结论:对于被mock的bean B用@Mocks标注;对被测类bean A自己,用@InjectMocks标注</strong></p> <p><strong>解释:</strong></p> <ol> <li>bean被标记了@InjectMocks , 在before方法中执行 MockitoAnnotations.initMocks(this)的时候,会将标记了@Mock或@Spy的属性注入到该bean 中。</li> <li>如果是@Mock, 那就是通常的方式,bean里面的provider完全被Mock实例替换,所有的调用都是针对Mock生成类的。</li> <li>如果是bean加载又加@Spy , 那么对于定制了返回值的会调用Mock实例,否则会调用真实注入的属性。 </li> </ol> <p><strong>另外行为模拟上有多种方式:</strong></p> <ol> <li>对于有返回值的方法,采用Mockito.when().thenReturn();</li> <li>没有返回值时,即void方法,我们可以采用Mockito.doNothing().when().XXX();</li> <li>抛异常的场景Mockito.doThrow().when().XXX();</li> </ol> <p><strong>同时,参数的构造提供anyLong()、anyInt()、anyBoolean()、any(XXX.class)。</strong></p> </blockquote> <p><br /></p> <h2>一个实例</h2> <p>bean的依赖图如下,inventoryMsgService中需要mock三个bean的行为,其他表现为真实行为。</p> <p><img src="http://ata2-img.cn-hangzhou.img-pub.aliyun-inc.com/67f0ceb086d71db0ae1ef59cfe299fb6" alt="1"></p> <div class="highlight"><pre><code class="language-text" data-lang="text">public class BaseTestCase { protected static ApplicationContext ac = null; private static String[] configXml = { &quot;basecase.xml&quot; }; static { try { HSFEasyStarter.start(&quot;hsfunit&quot;, &quot;1.4.9.6&quot;); ac = new ClassPathXmlApplicationContext(configXml); } catch (Exception e) { System.out.println(&quot;Error&quot;); } } } public class BomMsgUnitTest extends BaseTestCase{ @InjectMocks private InventoryMsgService inventoryMsgService; @Mock private InventoryMsgOccupyOutCore inventoryMsgOccupyOutCore; @Mock private InventoryMsgReportCore inventoryMsgReportCore; @Mock private InventoryMsgManager inventoryMsgManager; @Before public void init(){ inventoryMsgService = (InventoryMsgService)ac.getBean(&quot;inventoryMsgServiceImpl&quot;); MockitoAnnotations.initMocks(this); } @Test public void testInventoryOcuppy(){ //mock inventoryMsgOccupyOutCore.alloteMsgDetail when(inventoryMsgOccupyOutCore.alloteMsgDetail(any(InventoryMsgDetailDO.class), any(Boolean.class))).thenReturn(true); //mock inventoryMsgReportCore.getOrderReoprtStatus InventoryMsgResp resp = new InventoryMsgResp(); resp.setStatus(InventoryMsgStatus.OCCUPY_SUCCESS.getType()); when(inventoryMsgReportCore.getOrderReoprtStatus(anyLong(), anyLong(), anyInt(), anyBoolean())).thenReturn(resp); //mock inventoryMsgManager.updateMsgStatus(msgDO) Mockito.doNothing().when(inventoryMsgManager).updateMsgStatus( any(InventoryMsgDO.class)); InventoryOrderMsgReq orderMsgReq = new InventoryOrderMsgReq(); //... //入参数据构造 //... boolean testResult = inventoryMsgService.allotInventory(orderMsgReq); verify(inventoryMsgOccupyOutCore.alloteMsgDetail(any(InventoryMsgDetailDO.class), any(Boolean.class))); verify(inventoryMsgReportCore.getOrderReoprtStatus(anyLong(), anyLong(), anyInt(), anyBoolean())); verify(inventoryMsgManager.updateMsgStatus(any(InventoryMsgDO.class))); Assert.assertTrue(testResult); } } </code></pre></div> <p><br /></p> <h2>其他</h2> <p>将mock方式恰当地用在业务逻辑层的单元测试上,是能帮助构造很多异常场景用例的,比如项目组里前段时间发生的事务失效的线上问题,其实也是可以通过单元测试,mock 数据层的操作抛RuntimeException,来验证是否事务生效回滚,下次可以说下这个。</p> <p><br /></p> <h2>相关文章</h2> <p><a href="https://www.tianmaying.com/tutorial/JunitForSpringBoot">SpringBoot与JUnit+Mockito 单元测试</a></p> <p><a href="http://www.vogella.com/tutorials/Mockito/article.html">Unit tests with Mockito - Tutorial</a></p> Fri, 14 Oct 2016 00:00:00 +0800 http:/cuijing.org/2016/10/spring-mockito/ http:/cuijing.org/2016/10/spring-mockito/ mockito 单元测试 测试 h5 canvas的一个例子 <p><br /></p> <h2>前言</h2> <p>因为工作内容上的需要,最近稍学习了一下h5的canvas使用,可以稍总结下。 假设现在需要绘制一个物体在某个带纹理的场景下的运动。</p> <p><br /></p> <h2>解析</h2> <p><br /></p> <h4>step1:纹理的使用</h4> <ul> <li>API: <a href="https://developer.mozilla.org/zh-CN/docs/Web/API/CanvasRenderingContext2D/createPattern">CanvasRenderingContext2D.createPattern()</a></li> <li>使用:返回值CanvasPattern可作为当前的fillStyle,当进行绘制时,会在canvas上绘制出效果。</li> <li>注意点:对纹理的使用是需要Image的onload事件,它的作用是对图片进行预加载处理,即在图片加载完成后才立即除非其后function的代码体。这个是必须的,如果不写的话,画布将会显示黑屏。因为没有等待图片加载完成就填充纹理,导致浏览器找不到图片。</li> </ul> <p><br /></p> <h4>step2:运动的小球</h4> <p>做动画需要一个定时器,设定每间隔多少毫秒做一些改变。后来浏览器厂商提供了requestAnimationFrame()方法,它能从浏览器层面做一些动画渲染的优化。</p> <ul> <li>API: <a href="https://developer.mozilla.org/zh-CN/docs/Web/API/Window/requestAnimationFrame">window.requestAnimationFrame</a></li> <li>使用:虽然浏览器提供了方法,但这个方法并不会为我们做动画,也就是物体如何动是需要自己实现的。</li> <li>为什么要用它,看看这篇:<a href="http://007sair.github.io/web/2016/03/28/js-raf.html">web动画新选择:requestAnimationFrame</a></li> <li>注意点:比如模拟运动的小球,在每间隔时间绘制小球前需要清除画布上一次绘制的内容(不然画布上就是一堆小球),每次要用到ctx.clearRect(),如此我们就需要确保当前画布是可清除的(没有背景、纹理信息等)。</li> </ul> <p><br /></p> <h4>step3:多画布的使用</h4> <ul> <li>我们可以对canvas标签的dom设置它的属性z-index,值越大离用户越近。</li> <li>场景:什么情况下需要使用多画布?从上一步我们可以知道,在做动画时每次绘制前需要清除画布,那如果我们在画布上有一些静态的背景或纹理,如何清除?这时,我们就可以使用多画布,将动静进行拆分。</li> <li>使用:(1)需要注意z-index 仅能在定位元素上奏效(例如 position:absolute;),不然画布无法重叠。(2)画布层数若是太多,需要注意渲染性能。</li> </ul> <p><br /></p> <h2>实例</h2> <div class="highlight"><pre><code class="language-text" data-lang="text">&lt;!DOCTYPE html&gt; &lt;html&gt; &lt;head&gt; &lt;meta charset=&quot;UTF-8&quot;&gt;&lt;title&gt;example&lt;/title&gt; &lt;style&gt; #backgroundCanvas{ z-index:1; position:absolute; } #movingCanvas{ z-index:2; position:absolute; } &lt;/style&gt; &lt;/head&gt; &lt;body&gt; &lt;canvas id=&quot;backgroundCanvas&quot;&gt;&lt;/canvas&gt; &lt;canvas id=&quot;movingCanvas&quot;&gt;&lt;/canvas&gt; &lt;script&gt; window.requestAnimFrame = (function() { return window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || function(callback) { window.setTimeout(callback, 1000 / 60); }; })(); var backgroundCanvas = document.getElementById(&#39;backgroundCanvas&#39;); backgroundCanvas.width = window.innerWidth; backgroundCanvas.height = window.innerHeight; var bctx = backgroundCanvas.getContext(&#39;2d&#39;); var img = new Image(); img.src = &quot;1.jpg&quot;; img.onload = function(){ var woodfill = bctx.createPattern(img, &quot;repeat&quot;); bctx.fillStyle = woodfill; bctx.fillRect(0,0,400,400); (function animloop(){ requestAnimFrame(animloop); render(); })(); }; var m2_x0=0; //横坐标变化位移 var m2_eastward=true; //变量为真,横坐标变大 function render(){ var movingCanvas = document.getElementById(&quot;movingCanvas&quot;); var mctx = movingCanvas.getContext(&quot;2d&quot;); movingCanvas.width = window.innerWidth; movingCanvas.height = window.innerHeight; mctx.clearRect(0,0,movingCanvas.width,movingCanvas.height); mctx.beginPath(); mctx.arc(95 + m2_x0, 50, 10,0,2*Math.PI); mctx.fillStyle=&quot;red&quot;; mctx.fill(); mctx.stroke(); if (m2_eastward) m2_x0++; else m2_x0--; if (m2_x0==200) m2_eastward=false; if (m2_x0==0) m2_eastward=true; } &lt;/script&gt; &lt;/body&gt; &lt;/html&gt; </code></pre></div> <p><br /></p> <p>最后,另外安利一下</p> <ul> <li><a href="http://codepen.io/">codepen</a>,搜canvas,上面有一些很不错的例子。</li> <li><a href="http://canvas.ursb.me/">CANVAS</a>,感觉是个萌萌哒的妹纸写的canvas api教程</li> </ul> Wed, 31 Aug 2016 00:00:00 +0800 http:/cuijing.org/2016/08/an-example-of-h5-canvas/ http:/cuijing.org/2016/08/an-example-of-h5-canvas/ canvas html5 前端 集成json-editor小结 <p><br /></p> <h2>一、需求目的</h2> <p><strong>目的:</strong>将复杂的json编辑可视化,让填写字段值更容易。</p> <p><strong>业务背景:</strong>业务系统中上下游的交互有时候是通过一段报文(比如大宝的网关和PAC的交互),有时候又是通过消息的机制来达成异步化(比如metaQ、精卫),而我想做的是根据场景方便的mock这个报文或者消息,并且这些报文或者消息有一些约定好的结构与字段。</p> <p><strong>工具库:</strong>github上排名非常靠前的<a href="https://github.com/jdorn/json-editor">json-editor</a></p> <p><br /></p> <h2>二、拆分需求</h2> <p>json-editor所做的就是根据你定义的有效的json schema,渲染出html form,而它本身所需要的依赖,none,only浏览器。</p> <p>所以我从源码中剥离了这个例子:<a href="https://rawgit.com/jdorn/json-editor/master/examples/advanced.html">advanced.html</a>,它符合我的需求是因为它这里有多个schema的切换Basic Person-&gt;Complex Person,而我就是有多种消息格式的schema。</p> <p>本来以为一切就这么愉快地结束了。</p> <p>但是用起来的时候发现,new一个editor时,它的startval是给定的:</p> <div class="highlight"><pre><code class="language-text" data-lang="text">// Initialize the editor var editor = new JSONEditor(document.getElementById(&#39;editor_holder&#39;),{ // Enable fetching schemas via ajax ajax: true, ...... // Seed the form with a starting value startval: starting_value, ...... }); </code></pre></div> <p>而我期望的是对于不同的schema有不同的初始化值。</p> <p><br /></p> <h2>三、解决</h2> <p>第一步想到的就是对切换schema的select下拉框增加change事件监听,试了后发现不生效。查看了下原实现中select已绑定了change事件,并且实现中用e.preventDefault()来阻止事件往上冒泡了,所以才对我增加的事件监听不生效。</p> <p><img src="../../../img/jsoneditor-1.png" alt=""></p> <p>于是回到json-editor库本身,通过editor.editors查看对象下所有成员变量与方法,同时结合文档。发现editor提供了一个ready事件( If the ajax property is true and JSON Editor needs to fetch an external url, the api methods won&#39;t be available immediately. Listen for the ready event before calling them.),于是我们可以把对select的监听插入到这个事件中。其中select元素没有分明的name或id,所以取到元素还花了些时间。</p> <div class="highlight"><pre><code class="language-text" data-lang="text"> editor.on(&#39;ready&#39;,function() { var editors = editor.editors; if (!editors) { return false; } var rowHolder = editor.editors[&#39;root&#39;][&#39;row_holder&#39;]; var selectDom = rowHolder.querySelectorAll(&#39;.content &gt; select&#39;); selectDom[0] &amp;&amp; selectDom[0].addEventListener(&#39;change&#39;, function (e) { var _this = e.currentTarget; var option = (function () { var options = _this.childNodes; var aim = null; options.forEach(function (option, index) { if (option.selected) { aim = option; return true; } }) return aim; }()); if(_this.value === &#39;Complex Person&#39;) editor.setValue(starting_value_2); }) }); </code></pre></div> Mon, 22 Aug 2016 00:00:00 +0800 http:/cuijing.org/2016/08/summary-of-json-editor-use/ http:/cuijing.org/2016/08/summary-of-json-editor-use/ json-editor 前端 前后端数据交互的两种方式 <p><br /></p> <h2>前言</h2> <p>平常在项目测试过程中常常需要写一些小工具,方便造数据或者mock上下游的依赖等,这些工具做起来也都比较轻,主要涉及到前后端数据交互、权限控制、数据校验、业务HSF接口、DB操作。今天主要说说前后端数据交互的两种方式,写下来方便组里在平时的工作中快速的写工具。</p> <p><br/></p> <h2>方式一 直接通过变量交互</h2> <p>比如在webx中以.vm模版为桥梁进行交互。Velocity是基于Java的模版引擎,适用于变量赋值、基本数据类型、函数、逻辑运算、模版嵌套等等。</p> <h3>1. 前端-&gt;后端</h3> <p>.vm中通过form表单提交的方式将input等标签的value值传给后端,后端通过 request.getParameter(&quot;xxx&quot;)来获取值。</p> <div class="highlight"><pre><code class="language-text" data-lang="text">&lt;form class=&quot;form-vertical&quot; method=&quot;post&quot; action=&quot;configurationCheck.htm&quot;&gt; ...... &lt;div class=&quot;control-group&quot;&gt; &lt;label class=&quot;control-label&quot;&gt;单号:&lt;/label&gt; &lt;div class=&quot;controls&quot;&gt; &lt;input type=&quot;input-normal&quot; name=&quot;orderNo&quot; value=&quot;$!orderNo&quot; /&gt; &lt;/div&gt; &lt;/div&gt; ...... &lt;/form&gt; </code></pre></div> <hr> <div class="highlight"><pre><code class="language-text" data-lang="text">public void execute(@Param(&quot;orderNo&quot;) String orderNo, Context context) { String orderNo = request.getParameter(&quot;orderNo&quot;); ...... } </code></pre></div> <h3>2. 后端-&gt;前端</h3> <p>后端通过把变量放到上下文中context.put(&quot;xxx&quot;, xxx)来传值给前端,前端在.vm文件中使用velocity语法来取变量值\$!xxx,不同的变量类型有不同的使用方式,可参看velocity语法。</p> <div class="highlight"><pre><code class="language-text" data-lang="text">#foreach($item in $!items) &lt;div class=&quot;label label-info&quot;&gt;$item.itemId - $item.barcode&lt;/div&gt; #end </code></pre></div> <hr> <div class="highlight"><pre><code class="language-text" data-lang="text">context.put(&quot;items&quot;, itemVOs); </code></pre></div> <p><br/></p> <h2>方式二 通过AJAX异步接口</h2> <p>通过\$.ajax()的方式,我们可以任意的前端-&gt;后端、后端-&gt;前端来获取数据。</p> <div class="highlight"><pre><code class="language-text" data-lang="text">&lt;button type=&quot;button&quot; onclick=&quot;invokeUrl(&#39;JingweiLogAjax.do&#39;)&quot;&gt;Send&lt;/button&gt; &lt;input type=&quot;hidden&quot; id=&#39;jingweiMsg&#39; value=&quot;$!jingweiMsg&quot; /&gt; &lt;script&gt; function invokeUrl(url) { var requestStr = document.getElementById(&#39;jingweiMsg&#39;).value $.ajax({ url : url, type:&#39;POST&#39;, data: { request:requestStr, }.done(function(data){ alert(data); } }); }&lt;/script&gt; </code></pre></div> <hr> <div class="highlight"><pre><code class="language-text" data-lang="text">//JingweiLogAjax.java public void execute(Context context) { String response = null; try { String msgJson = request.getParameter(&quot;request&quot;); //处理逻辑 ...... //返回结果写到response PrintWriter writer = response.getWriter(); writer.write(response); writer.flush(); writer.close(); }catch(Throwable e){ response = &quot;异常&quot; + e.getMessage(); } } </code></pre></div> <p><br /> </p> <h2>其他</h2> <p>上述是前后端数据交互最基础的方式。实际在应用中不同场景可能会有一些不同的方式,实质都是将数据的交互方式和样式渲染封装了。比如</p> <h4>1. data-*属性</h4> <p>前端中可以利用data-* 属性来存储和操作数据,这些属性在页面上是不显示的,不会影响到你的页面布局和风格,并且是可读可写的。比如典型的在bootstrap table中的使用方式</p> <div class="highlight"><pre><code class="language-text" data-lang="text">&lt;table data-toggle=&quot;table&quot; data-url=&quot;data1.json&quot;&gt; </code></pre></div> <p><br /></p> <h4>2. 前后端分离</h4> <p>前后端分离时,往往只需要前后端对好异步接口的字段,后端拼JSON返回给前端,前端根据后端数据对页面进行动态渲染。这样.vm文件都可以如此简单:</p> <div class="highlight"><pre><code class="language-text" data-lang="text">&lt;script&gt; BUI.use(&#39;page/data/portal&#39;, function (Portal) { new Portal({ api: &#39;/portal/homePage.do&#39;, }); }); &lt;/script&gt; </code></pre></div> Tue, 16 Aug 2016 00:00:00 +0800 http:/cuijing.org/2016/08/data-interaction-from-frontend-to-backend/ http:/cuijing.org/2016/08/data-interaction-from-frontend-to-backend/ 前后端交互 前端 关于NoClassDefFoundError问题 <p><br /></p> <h2>前言</h2> <p>有一次看到一篇文章,说到平时在工作时有一个习惯让他收获颇多,就是每次遇到问题或者bug按照一个固定格式做记录 --【日期】、【问题】、【原因】、【怎么发现的】、【修复】、【在哪些文件修改了】、【我导致的】、【解决Bug的时间】、【教训】。于是,我也记录了段时间,今天说说一个很基础也很常见的问题NoClassDefFoundError。</p> <p><br /></p> <h2>场景一</h2> <h3>现象: 使用tddl时报错NoClassDefFoundError:</h3> <div class="highlight"><pre><code class="language-text" data-lang="text">错误信息: Could not initialize class com.taobao.tddl.config.ConfigDataMode 配置信息: &lt;bean id=&quot;xxxDataSource&quot; class=&quot;com.taobao.tddl.group.jdbc.TGroupDataSource&quot; init-method=&quot;init&quot;&gt; &lt;property name=&quot;appName&quot; value=&quot;xxx&quot;/&gt; &lt;property name=&quot;dbGroupKey&quot; value=&quot;xxx&quot;/&gt; &lt;/bean&gt; </code></pre></div> <p><br/></p> <h3>分析:</h3> <h4>(1) 初步的排查思路:(1)tddl jar包是否引用进来了,ConfigDataMode类是否确实存在?(2)是不是有包冲突? <b/></h4> <blockquote> <p>之所以会强调一下排查(1),是因为以前犯过一个很低级的错误,在有多个子工程的情况下,eclipse全局搜该jar包发现确实有,但可能是你的主pom里做了该jar包的依赖管理,子pom里未进行依赖所导致的。而排查(2)应该算是在遇到这个错误时的第一反应。</p> </blockquote> <h4>(2) 打出依赖树,检查tddl-config是否有多版本?</h4> <p>没有,这里的版本为tddl-config-5.1.17-3.jar</p> <h4>(3) 进ConfigDataMode.class看看是否有什么特别?</h4> <p>这个类里面是有静态块的,而静态块代码是在类初始化时主动执行的,也就是静态块里的执行异常也可能导致上述的错误。</p> <div class="highlight"><pre><code class="language-text" data-lang="text">static { String m = System.getProperty(CONFIG_MODE, &quot;auto&quot;); mode = Mode.nameOf(m); if (mode == null) { mode = Mode.AUTO; } String cmd = System.getProperty(&quot;sun.java.command&quot;); if (StringUtils.equals(cmd, &quot;com.taobao.tddl.server.TddlLauncher&quot;)) { isServer = true; } String appname = System.getProperty(&quot;appName&quot;); if (StringUtils.startsWith(appname, &quot;tddl&quot;)) { isServer = true; } } </code></pre></div> <h4>(4) 强大的IDEA提示StringUtils.startsWith方法找不到(弱爆的eclipse什么反应都没有)。</h4> <p>于是再确认了下为什么,实际是commons-lang包的版本太低,没有提供,升级到2.6就可以了。</p> <p><br/></p> <h3>原因: 解决下来,其实是NoClassDefFoundError报错的类中包含的静态块代码初始化失败。</h3> <p><br /> <br /></p> <h2>场景二</h2> <h3>现象:工程在本地起服务没问题,分支部署到日常上就失败</h3> <div class="highlight"><pre><code class="language-text" data-lang="text">查看/home/admin/wmpxbao/logs/catalina里的日志 发现又是一个NoClassDefFoundError: Could not initialize class com.taobao.qa.perf.client.PapControllerClientFactory </code></pre></div> <p><br/></p> <h3>分析:</h3> <h4>(1) 有了上一次的经验后,很快检查jar包是否依赖成功?报错的类所在的jar包是否冲突?</h4> <p>没有。值得注意的是这次检查jar包冲突不再是本地工程就够了,更准确的方式是在服务器上war包的lib里检查。</p> <h4>(2) 接着按上面的经验看类中静态变量和静态块</h4> <p>在PapControllerClientFactory中发现整个类就是一堆静态变量、静态块、静态方法。也就是没有一些指导信息的话,排查会比较盲目。</p> <h4>(3) 于是只好从服务器上日志入手,清除干扰的历史日志,对所有新创建的日志进行仔细搜罗</h4> <p>最后在/home/admin/wmpxbao/logs下的wmpxbao.log主日志下捕捉到这么一点信息:</p> <div class="highlight"><pre><code class="language-text" data-lang="text">2016-07-26 ERROR proxy.ProxyService - [ProxyService]: newInstance Class.forName error, classType:com.taobao.wmpxbao.biz.schedule.PapTaskResultProcessor java.lang.NoSuchMethodError: com.caucho.hessian.client.HessianProxyFactory.setChunkedPost(Z)V at com.taobao.qa.perf.client.PapControllerFactory.createPAP(PapControllerClientFactory.java:126) </code></pre></div> <h4>(4) 这么一来就有思路了,确定HessianProxyFactory在war包里的版本</h4> <p>发现有有两个</p> <div class="highlight"><pre><code class="language-text" data-lang="text">hessian-3.0.13.bugfix-20120711.024044-23.jar hessian-3.1.5.jar </code></pre></div> <h4>(5) 去maven仓库下载这两个jar source来确定哪一个包含我们需要的method,然后排除掉另一个。</h4> <p>排包麻烦时可以用:</p> <div class="highlight"><pre><code class="language-text" data-lang="text">&lt;dependency&gt; &lt;groupId&gt;hessian&lt;/groupId&gt; &lt;artifactId&gt;hessian&lt;/artifactId&gt; &lt;version&gt;999-not-exist&lt;/version&gt; &lt;/dependency&gt; </code></pre></div> <p><br/></p> <h3>原因:同样是NoClassDefFoundError报错,也同样是类静态块代码初始化时错误,但是从服务器上的日志去反向寻找异常点切入。</h3> <p><br /></p> <h2>总结</h2> <p><strong>NoClassDefFoundError的问题排查思路:</strong></p> <ul> <li>step1: 该类所使用的子module是否引入了jar包?</li> <li>step2: 该类在该jar包中是否存在?</li> <li>step3: 是否存在该类所在的jar包发生版本冲突?</li> <li>step4: 该类中是否存在静态变量或静态代码块初始化失败?</li> </ul> Sun, 07 Aug 2016 00:00:00 +0800 http:/cuijing.org/2016/08/about-no-class-def-found-error/ http:/cuijing.org/2016/08/about-no-class-def-found-error/ NoClassDefFoundError Java 后端 祝我生日快乐 <p><br /></p> <p>一、 今天是我的生日,不知不觉的想码篇文字。翻了翻上篇博文,又翻了翻朋友圈去年生日发的状态,满满的滋味。这次有点害怕生日的到来,大概也觉得自己老大不小了,但是今天莫总说了句话让我印象深刻:</p> <blockquote> <p>“女人28,这是最好的季节,有想法,具有独立性,有梦想,能赚钱,没有太多家庭压力……再好不过的时光”</p> </blockquote> <p><strong>我抿嘴笑了笑,觉得没错。珍惜眼前。</strong></p> <p><br /></p> <p>二、引用昨天发朋友圈的那句话:</p> <blockquote> <p>“成长最大的感触是活得更有知觉,慢慢地在变化万千的世界里寻找和追随属于自己的,感受阵痛与纠结,去选择和经历。”</p> </blockquote> <p><strong>这是成长最吸引我的地方,当然也是最不容易的地方,往往蜕变包含了很多痛。</strong></p> <p><br /></p> <p>三、时间总是哗哗的流走,若是不回头看看,总有点记不起那些日子的模样,所以总有那句话让你‘不忘初心’。即便以某种方式打开一下,也不一定能够立马区分想要的生活是什么样子的,有时候折腾一下反而能更好理解自己看重的是什么,同时有一点也很重要,那就是自信,<strong>自信自己的判断与决策,勇敢地去为人生负责。</strong></p> <p><br /></p> <p>四、六月份开始,我也加入了改变自己小组,不知不觉20天坚持着写日记,你会发现坚持做一件事情其实很容易,但做得不够的可能是给予一个循环迭代的过程来提高自我认知。回想这半年多的时光中,最深感触的一点,可以用大魔王以前说过的一句话:</p> <blockquote> <p>“不要把努力想得那么沉重,一点一滴的积累而已”</p> </blockquote> <p>的确如此,所以,<strong>多充实自己,给予时间的基数,没有问题的。</strong></p> <p><br /></p> <p>五、马上要转点进入28啦,祝我生日快乐。送什么祝福呢,学会接受不完美的自己,然后打开自己的内心,坚韧而真实地努力着。</p> <blockquote> <p>“勇敢不是不恐惧,而是心怀恐惧,仍依然前行。”</p> </blockquote> <p>来点实际的,让我这个月摇到车牌吧~~~</p> Mon, 20 Jun 2016 00:00:00 +0800 http:/cuijing.org/2016/06/happy-birthday-to-myself/ http:/cuijing.org/2016/06/happy-birthday-to-myself/ 生日 成长 谈谈转岗之事 <p><br /></p> <p>这段故事我跟很多人重复地讲过,这个过程中我的心境也在不断的变化着,最终的结果可能要让所有的看客失望,我依旧选择了留在原地。</p> <blockquote> <p>我于去年8月有初步的换岗念头,9月去日本回来下定决心,于是就开始了工作之余的转岗准备。到今年3月底抓住了转岗新政策出来前的尾巴,面试了阿里云技术体验部的前端开发岗,并获得offer。</p> <p>在准备的过程中,首先是构建自己的前端领域知识图谱,follow各大博客、领域活跃人员文章、资讯等,学习和实践入门基础书籍和知识点,大大小小的例子做了一些实践,后来选择nodejs方向做了一些实践,并读了部分源码写下读书笔记。在此期间了解了集团的各个前端团队和活跃的人。一切为了做好转岗的选择。</p> <p>接着在持续了一个月的转岗流程中,因为自己的无知,处理不恰当,思考不透彻,纠结,患得患失,让自己过得很难受,也带来了很多不必要的麻烦。</p> </blockquote> <h3>总结一点感受:</h3> <div class="highlight"><pre><code class="language-text" data-lang="text">* 也许从一开始想到转菜鸟前端,路就不会如今天这般难堪。偶尔应该有曲线救国的变通。 * 跟hr聊天时不宜什么都说,因为不是所有的话都适合摊到台面上说。 * 学会从不同的角色上去真正理解一件事情。 * 做选择切忌患得患失,不要想得太好,也不要想得太坏。 * 朋友给的建议更多时候是自己想法的镜子,多拎出客观的事实,决定还是应该由自己拿。 * 不要不好意思谈工资,这是一件十分正常的事情。 * 人总会面临迷茫,要学会在困难的时候找到合适的资源或人来帮助自己。 * 打开眼界,主动结识比自己优秀的人,主动了解这个行业的发展规律,多观察做人做事的方式。 * 敢想敢选敢做。 </code></pre></div> <p><strong>在这个过程中,我收获了自己的那份坚持和努力,收获了转岗这件事情过程中心历的成长,收获了跟很多角色聊天的机会(更从侧面认识到自己),我相信我的故事也带给很多人前进的动力,对自己的人生有了一次多方面深刻的思考,唯一就是心里的那份仪式感有点过不去,所以唯有在此更加努力才不负今天所有的纠结。</strong></p> <p><br /></p> <h3>期待以后,在职场事情处理时能更成熟的看待事情、沟通、做决策。</h3> <p>--</p> <h3>「更新于2016.12.16」</h3> <p>今年双十一前后再次着手转岗,稍微介绍下大体情况</p> <ul> <li>选了3个BU去面试,有的找同事推荐,有的自己去联系他们的老大(两种方式本身对内转来说差别不大,找同事推荐的话找个靠谱的)</li> <li>最好还是准备一份简历,面试时也方便从简历上问问题</li> <li>真实地表达自己</li> <li>最终选择了一直想去的蚂蚁体验技术部</li> <li>开放地去跟hrg、主管聊,让他们能够理解,积极去做双方的沟通</li> <li>目前在工作交接中,看到新团队的忙碌,一种前置紧张感</li> <li>相信自己,加油</li> </ul> Tue, 03 May 2016 00:00:00 +0800 http:/cuijing.org/2016/05/talking-about-change-work-title/ http:/cuijing.org/2016/05/talking-about-change-work-title/ 转岗 前端 成长 NodeJS学习小结 <p><br /></p> <blockquote> <p>前段时间跟着慕课网的课程简单学习了NodeJS,主要包括<a href="http://www.imooc.com/learn/348">进击Node.js基础(一)</a>,<a href="http://www.imooc.com/learn/75">node+mongodb 建站攻略(一期)</a>,<a href="http://www.imooc.com/learn/197">node建站攻略(二期)——网站升级</a>。这里稍微做下总结。</p> </blockquote> <h3>因为是上手的第一个项目,所以对整个项目的框架选型(前后端)、项目代码结构的逐次升级和管理、打包构建测试,这些大体的流程上印象十分深刻。用到的框架或工具有:</h3> <ul> <li>express 4.x :基于 Node.js 平台的一个 web 开发框架</li> <li>middleware:从express 4.x版本开始, 除了 express.static, Express 以前内置的中间件现在已经全部单独作为模块安装使用了。请参考<a href="https://github.com/senchalabs/connect#middleware">中间件列表</a>。 <ul> <li>session</li> <li>bodyParser</li> <li>cookieParser </li> <li>morgan</li> </ul></li> <li>mongoose(mongodb):十分简便好用,也了解了mongoose shell怎么玩</li> <li>jade:一个模版引擎,对缩进十分严格</li> <li>mocha:一个测试框架</li> <li>grunt:<a href="http://javascript.ruanyifeng.com/tool/grunt.html">Grunt:任务自动管理工具</a> <ul> <li>grunt-contrib-watch</li> <li>grunt-mocha-test</li> <li>grunt-contrib-less</li> <li>grunt-contrib-uglify</li> <li>grunt-contrib-jshint</li> </ul></li> <li>bower:一个客户端技术的软件包管理器,这个项目中用来管理依赖的页面包资源</li> <li>npm:一个NodeJS包管理和分发工具</li> <li>bcrypt:一个跨平台的文件加密工具</li> </ul> <p><br /></p> <h3>其次是,在写的过程中常常会遇到一些意料外的问题,如何定位及解决也十分重要,同时也积累了经验。写完是十分有成就感的,但总觉得有更多的细节是需要问为什么的。(页面上看起来不显眼的东西,可是在背后的细节还是十分多的,所以编程是个细致活)</h3> <p><br /></p> <h3>再做个例子后,读读源码,希望得到更踏实的收获。</h3> <p><br /></p> <h3>其他阅读</h3> <ul> <li><a href="https://cnodejs.org/topic/55651bf07d4c64752effb4b1">Node.js最新Web技术栈(2015年5月)</a></li> <li><a href="http://nqdeng.github.io/7-days-nodejs/">七天学会nodejs</a></li> <li><a href="http://howtonode.org/">How To Node</a></li> </ul> Sun, 21 Feb 2016 00:00:00 +0800 http:/cuijing.org/2016/02/summary-of-learning-nodejs/ http:/cuijing.org/2016/02/summary-of-learning-nodejs/ NodeJS 前端