文章导航

关于删除掉整个node_modules前的注意事项

这两天在自己配置webpack,来实现本地开发环境和生成生产版本代码。

于是就在翻webpack的文档,安装了一些插件和loader,之后我发现还有一个css分离没有实现,于是打算推翻之前的webpack配置,顺便又把node_modules给完全删除了。

跑完cnpm i,然后npm run build,发现报错:webpack not installed, consider installing it using …

wtf?此时我还不知道错在了哪里,然后把node整个卸载了,把Appdata里的npm相关文件夹全部删掉,之后重装node,依然不行。

就在刚才,我尝试去webpack的github页面那里找有没有类似的issue,然后我就找到答案了,一个已经关闭的issue里说:This appears to happen when the webpack.config.js module require()s a missing module.

意思就是说:在webpack的配置文件里引用的模块有可能没有安装。

我对比package.json和配置文件一看,果然,有些模块是有但不是全局的,在别的文件夹,最关键的就是没有安装到我自己的项目文件夹下面。

所以在删除整个node_modules之前,一定要检查是不是每个依赖项都已经写进了package.json。不然重新下载的时候是不会安装的,到时候webpack就会报这个匪夷所思的错误,我甚至动了重装系统的念头。

Redux教程todo示例的个人解读

最近在学习Redux,今天刚把Redux基础看完,教程提供了一个todo的简单示例,麻雀虽小,五脏俱全。

把示例确确实实地看懂之后,明显地加深了我对Redux的理解。

Redux分三大部分:action,reducer,store。核心思想是store保存全局state且唯一,而修改state的唯一方式是触发(dispatch)action,此时reducer接收action和旧的state并返回一个新的state,此时与redux连接(connect)上的组件就会收到新的state,更新自身。而store则通过createStore(reducer)生成。

然后其中还有一个核心便是:容器组件与展示组件相分离的思想。从我个人的理解来说,就是展示组件是纯函数,仅通过接收props来更新输出,因此展示组件完全可以脱离Redux使用;而容器组件才是负责逻辑控制的抽象,通过容器组件与Redux进行连接和响应,进而改变下属的展示组件,因此容器组件起的是一个承上启下的作用。

在React开发中,通常会使用React-Redux,该库提供的connect函数就是连接Redux与展示组件的高阶函数,该函数会调用2次。第1次调用该函数,就可以生成一个连接到Redux的高阶组件;第2次调用,往这个高阶组件传入对应的展示组件,就可以得到想要的容器组件了。

因此,我觉得在实际开发过程中,至少这3块需要做比较多的工作:action,reducer,container。对应:触发->数据更新->视图更新。

最大的收获就是,一定要静下来去思考去看,这样思维就会很清晰,理解起来会很快。

https://www.redux.org.cn/docs/basics/UsageWithReact.html

TS入门墙裂推荐

Typescript入门文档

总结下我个人目前的学习Web的常用电子文档,ES5-http://wangdoc.com

ES6-http://es6.ruanyifeng.com

实在是非常的全面。上周买的《JavaScript高级程序设计》目前还没开看,之前简单翻了下,感受就是:如果我一开始就从书开看的话,估计我的学习效率会很低。。。因为书上说的更加详细,不太利于我这种初学者去把握JS的总体概念。我个人的学习路线就是先弄个大概,然后利用这点皮毛先实践,实践遇到问题再来钻细节,这个时候理解更快,也更深。

现在ES5我已经掌握大部分,ES6也对新的特性有个60%的大概认知,实际使用也应该有10%,比如常用的箭头函数和解构赋值等。最后就是React和TypeScript,确实,对React有了大概了解和简单的使用(写了状态组件和容器组件)后,就会被React这种追求简约的思想所征服,虽然我一开始是先使用的Vue,但是现在我必须承认我更加偏好React,确实很香,学习React其实更像是在追随一种思想,而那种思想深深地吸引了我。

Memoization思想及近期感想

Memoization,看着觉得好像很高大上,实际上是一个较为简单却很有效的一种思想,提高了代码运行的效率。

作为纯函数,输入不变的情况下,无论输入相同的参数多少次,输出依旧不变。然而对于某些计算较复杂的纯函数,如果在实际程序中有多次调用但输入都是相同的,此时的计算完全可以视为浪费资源,就好比你写数学题辛辛苦苦算出来答案却没有记住,当老师问你最终结果的时候你居然还要重新计算一次一样,很傻。

所以目的很明确,我们需要函数“记住”之前的输入和输出结果,这样,当同样的输入再出现时,我们可以直接返回之前计算出的结果,而不需要再去计算了。实现也比较简单,通过闭包即可。

function memoize(fn) {
  var cachedArg;
  var cachedResult;
  return function(arg) {
    if (cachedArg === arg) {
      return cachedResult;
    }
    cachedArg = arg;
    cachedResult = fn(arg);
    return cachedResult;
  };
}

此例引自https://github.com/react-guide/react-basic,这个例子可以说是最简单的实现,了解了这种强大的思想,就可以创造出各种记忆能力更强大,更贴合实际环境的闭包。

然后是我的个人近期感想。

因为之前的头条笔试被秒杀,以及蘑菇街一面被刷,我意识到了我能力的不足,虽然我因为我的表达能力不好,没有向面试官展现出我实际的能力导致被低估而被刷是一方面,但是最大的原因还是因为,我在前端的学习还是太少,经验太少,即使有货也只是杯水车薪。

然后我最近就开始各种学习,干React组件,开发自己的组件库呀,学TypeScript呀,看ES6呀,依然觉得心里没底,空落落的。为什么?因为这些都不是我真正想要的,我现在纯粹是在为找工作而学习。我对JS和Web开发的确是有兴趣的,但是我现在想要的不是因为工作需要,我才去学webpack,因为别人需要会TS,我才去学TypeScript。我应该跳出为了工作这个圈子去学习,这样才有效率,才有热情,才能真正开发出优秀的,包含我的思想和期待的东西。

程序员最终是为了改变而生的,不管你梦想的大或小。你开发出React和第一次打造出来令自己满意的项目,应该是同样的感觉,是那种创造的满足感和自豪感,以及孜孜不倦的上进心。

react transition group

最近开始学习使用react,想把react学好学精,目前短期是打算通过react来重构我的小站。也就是之前给自己定的目标,因为后来和同校的学生做给校内学生提供查分等服务的小程序,结果等到今天才开始,哈哈。

我现在希望尽可能把每个曾经的功能模块更细化,做成可复用的组件,相当于弄个自己的UI组件库。然后其中有一个是蒙层。为了不那么突兀,所以我希望在卸载蒙层的时候看起来是淡出的,所以我需要和Vue类似的transition。

然后问题就来了,找到react官方提供的react-transition-group并没有花我很多时间,然而接下来因为文档是全英文的,读起来比Vue官方的中文文档肯定会费力很多,但是我还是懂了个大概,这中间花了多少时间我不说。。。总之相比读完Vue的相关文档之后立马上手立马奏效,还是很让我抓鸡。

最后我是用的CSSTransition。它关键的参数主要是in,timeout,以及classNames,还有一个unmountOnExit(之前我没有看每个参数的作用,结果靠多定义一个定时器手工实现的,后来才发现)。in是一个布尔变量,实质就是状态切换,比如从true => false,就会触发exit,对应的类名就会依次变为exit,exit-active,exit-done,通过css为这些类名设置style,搭配css 的transition属性就可以实现你想要的过渡效果。timeout顾名思义,就是状态变化的时长,比如从exit触发到exit-done中经历的时间,所以这个地方最好你css里transition也设置了同样的时间,它的单位为ms。classNames也比较好理解,比如你设置classNames为’fade’,那么触发状态变化时,对应的类名就是fade-exit,fade-exit-active。最后unmountOnExit,官方参数说明中已经讲得很清楚,

By default the child component stays mounted after it reaches the 'exited' state. Set unmountOnExit if you’d prefer to unmount the component after it finishes exiting.

意思就是,常理下你的子组件在达到exited(这里是transition文档里,csstransition继承了transition的参数,但是状态有一点不一样)状态后依旧处于挂载状态,就是你的组件并没有销毁。但如果你需要在组件达到exited状态后自动销毁,就添加这个参数。这正是我想要的,蒙层淡出后就自动销毁,如果不销毁,这个蒙层虽然已经透明,但是因为处于图层的顶层(position:fixed;z-index:10),直接挡住了我整个页面,导致我根本无法交互。

目前虽然可以使用了,但是我还没有把这个文档搞透彻,不说了,继续看。

https://reactcommunity.org/react-transition-group/

尾递归

在看阮一峰老师的ES6教程,看到了函数的扩展中谈到了尾递归。这个概念对我来说还很新,认真看了下。两个来源,一个阮老师的2015年写的博客,一个是ES6入门教程。博客有图。

我个人对尾递归的理解就是,函数的最后一步调用了仅函数本身,不包括任何其他的外层变量,所有的变量通过参数方式传递到内层函数,这样子外层函数就没有存在理由了,因此外层调用帧也滚蛋了,只保留内层函数的调用帧,层层如此,因此始终只有一个调用帧而已,最后满足某些条件,返回一个值,整个过程结束。所以尽量写尾递归的函数,会节约很多内存空间。

http://www.ruanyifeng.com/blog/2015/04/tail-call.html

http://es6.ruanyifeng.com/#docs/function#%E5%B0%BE%E8%B0%83%E7%94%A8%E4%BC%98%E5%8C%96

感觉现在我光看ES6教程和网道,有时候去MDN查查相关的其他知识,已经学到了不少。算是沾了网络发达的光,目前还没有花钱买书看的打算,因为连文档都还没看完弄熟悉,等有自信了,就去买本书深入下更细节的东西。感谢翻译外文教程文档的所有人,如果没有他们翻译文档,很多人可能都会因为这个英文门槛就跟前端或者其他程序员岗位说再见了。等自己有能力了,一定要做一些反哺社区的事情。

超赞的grid+flex布局

实践证明,grid布局虽好,但是微信客户端支持还不够,不仅会导致布局出问题,还会导致所有class全部失效,最重要的是花了我一天时间找自己的问题。。。最后发现是微信自己的问题。

依旧推荐grid布局,因为错误是微信那边支持度较差造成的,之前的效果图是开发者工具里边显示的,是正常的,但是到手机上预览就出问题了。


 

现在只剩我和另一位同学,来完成成信大助手微信小程序的开发,而我负责前端。一个人更好,方便大展拳脚。

今天我在准备做课表页面的时候偶然想起了grid网格布局,然后先在网上看了看grid的基本概念,推荐阮一峰老师才写没多久的grid网格布局

直接上效果。

就这么点代码,我就搞定了整个课表的结构,接下来再上个色就可以用了。而且针对不同的课表信息,内容会自动排版,无需进行任何其他更改,妙就妙在grid的grid-column或grid-row属性上边,这两个属性分别指定当前元素在容器内的纵向与横向位置。真是个好东西。

和之前我没加入的时候后端自己写的前端代码对比下来,简洁了不知道多少倍~ 感受下~

小站更新:利用localStorage实现距离上次访问的时间显示

现在用户可以知道自己上次的访问时间,利用localStorage实现,那么,清缓存的操作就很可能一并清除localStorage,从行为上导致功能被初始化。

一分钟以内提示刚刚,大于一分钟&&一小时以内提示?分钟前,以此类推,直到?天。超过30天也依然按天算,比如59天前。

实现思路:

if (!window.localStorage.getItem(‘last-visited’)) {
this.notice = ‘这是你第一次访问本站’;
window.localStorage[‘last-visited’] = Date.now();
} else {
let last = Number(localStorage.getItem(‘last-visited’));
let now = Date.now();
window.localStorage[‘last-visited’] = now;
let diff = now – last;
if (diff < 60000) {
this.notice += ‘刚刚’;
} else if (diff >= 60000 && diff < 3600000) {
this.notice += Math.floor(diff / 60000) + ‘分钟前’;
} else if (diff >= 3600000 && diff < 24*3600000) {
this.notice += Math.floor(diff / 3600000) + ‘小时前’;
} else {
this.notice += Math.floor(diff / 24*3600000) + ‘天前’;
}
}
localStorage只能存储字符串形式的键值。虽然-号会直接进行隐式类型转换成数字类型,不过依然建议先将键值强制转换成数字类型。
其实本来是看见阮一峰老师在wangdoc上web api里有一个Intl.RelativeFormatTime可以实现相对时间,想用用这个api的。后来试了很久都发现要报错又不知道问题出在哪,后来从chrome控制台(这里点名下Firefox Dev,aurora通道升级的内核已经67了,居然控制台只报错,没说明错在哪?直接控制台用构造函数定义一个,然后发现是一个空对象。。)发现了,在MDN上找到问题,原来这个api有点新,对浏览器版本要求很高,很多人的浏览器访问都不一定能支持这个api,遂放弃。
之后想再在后端实现下记录用户访问次数和ip收集。

TCP三握四挥

今天做京东前端笔试题的时候看到了相关的问题,然后在网上看了一下一些博客的解释。https://blog.csdn.net/qq_38950316/article/details/81087809

说下我的理解。

为什么一定需要三次握手呢?就是因为我们无法保证第二次握手的时候,Customer端能收到来自Server端的确认消息。举个例子,你和一些人打篮球,你运着球,这个时候你发现一个队友站的位置非常好就想传球给他,然后你叫了他一声就马上把球扔过去了,结果你队友根本没听见,然后你就把队友给砸晕了。。。这个例子的目的是想表达二次握手这种思路太理想,根本没有考虑实际的情况。所以二次挥手会有死锁的可能性。

三次握手就避免了上述情况的发生,第二次握手的意思其实是S向C表示已经准备好,但是C是否准备好S并不知道(S不应该默认C准备好),第三次握手的目的就是C告诉S这边已经准备好。当S收到C的确认消息之后,表示CS都准备好了,所以连接就正式建立,开始发收数据。

为什么是四次挥手?举个例子,假设你是好学生,正在写作业,然后你作业还没写完的时候,你妈告诉你等会写完作业要跟她去探亲戚,所以你自然是先回应她一声你知道了,然后等你把作业写完的时候,你才再告诉她你写完作业可以去了。多出来一次挥手就是因为S端不会在接收到断开连接之后就马上停止发送数据,它需要把来自C端的请求处理完才能停。

挥手同样也是需要两边准备好的过程。接上边的例子,你作业写完了,然后你通知你妈,但是你妈不一定听见了,那这个时候很自然的,你猜她肯定没听见,这个时候肯定要再叫她一次(未必你说完了之后就穿着鞋子管都不管就走了?),直到她答应你才代表她听见了(我也不相信你妈听见之后也一言不发低着头穿鞋无视你的存在)。所以说最后两次挥手就是这么来的,很符合现实环境。

上面的例子顺带解释了wait 2个msl的存在理由,wait其实解决的是你妈回答了你,你却没听见的问题——你如果没听见你妈回复你,自然就会认为是她没听见,然后再叫她(妈!!!),然后你妈就又回答你(干什么啊?我知道了呀!)。直到你听见之后,这个循环就停了。这个例子的思路和wait并不完全一致,因为现实中妈妈不会专门去wait,但是大体思路已经能说清wait的存在理由。