0%

linux下 hexo deploy 显示Permission Denied 的权限不够问题

前面的话

为什么发现了这个问题呢?由于工作的原因,我将所有的工作的机子都变成了ubuntu 16.04,自从用了linux内核真的,腰不酸了腿不疼了,干啥都有活力了,不会像window下各种浪费生命了。

当然首先就是要在linux机子上安装git 这个程序员必备的工具,并且在自己的账户上连接上github 的ssh。

首先就是要将所有的项目在Ubuntu 下跑起来,当然也包括我之前的博客,顺手就apt-get安装了hexo,并通过 hexo项目整体转移的方法,请看链接 更换电脑如何转移hexo

最后发现sudo hexo deploy之后出现了经典的 本地没有ssh 对应github 上面的 ssh的问题。也就是Permission Denied。

思考

为什么会出现permission denied。就是没有ssh 呗。这也是linux权限问题的锅。首先要使用hexo 必须使用sudo获取linux下的最高权限 ,sudo目录就是linux下的root,当你添加ssh的时候都是在自己的账户下添加的,而没有到root去添加,所有sudo hexo deploy当然就会出现ssh不存在的permission denied问题啦。

解决

直接在root下添加ssh不就可以解决了啊。我们来一步一步的解决如何添加ssh到linux root目录下面。

  • sudo ssh-keygen -t rsa -C “xxx@xxx.com”加上sudo在linux的root目录下创建ssh。

  • 然后利用cat 命令 gedit 或者vim打开在root目录下的ssh。sudo cat /var/root/.ssh/id_rsa.pub

  • 在github上面进行ssh的粘贴。方法。github添加ssh

窗口进度条及其高级使用

我们大概实现的效果就像YouTube上面的红色进度条那样。但是YouTube上面那个进度条还是很坑爹的。文章后面再告诉你们为什么。

首先窗口的滚动进度条

窗口的滚动条非常的简单,只要用 window.onscroll 事件的监听,就可以实现。当然我最近在研究Vue.js,所以用vue.js实现了一个。反正都差不多吧,这个没什么好说的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Bar</title>
<style>
#bar{
position: fixed;
height: 5px;
background-color: aqua;
}
</style>
</head>
<body id="app">
<div id="bar" v-bind:style="{width:changeWidth+'%'}"></div>
<div v-for="n in 1000">{{message}}</div>
<script src="./vue.min.js"></script>
<script>
var app = new Vue({
el: '#app',
data: {
message: 'This is a Vue bar!',
changeWidth:0
}
})

window.addEventListener('scroll',() => {
app.changeWidth=(document.body.scrollTop/(document.body.scrollHeight-window.innerHeight))*100
})

</script>
</body>
</html>

效果

优化一下

因为这里的窗口滚动进度条没有过度效果吗(虽然谷歌浏览器她会自动帮你优化一点过度的效果,但是我们还是自己写的和谐一点点),所以就加多一句CSS3的动画

1
transition: width 1s;

到这里我们的滚动进度条就基本上实现了,也可以做一个很不错的水平效果。但是这个滚动还是有很多东西值得我们去研究一下的。

window.onscroll什么时候会触发

这里的window.onscroll当滚动了鼠标的滚轮的时候就会触发对吧,这个无可非议。就算这个界面我们手抽搐的去滚动这个滚轮,他就一直一直的触发
这样我们在我们的触发代码里面输入一个console.log(1)

我这里指滚动了一下,这里就被触发了13次,虽然这个鼠标滚轮的时间开销是不大的,也不用特地去做优化,但是如果是一次滚动我们触发了一次服务器请求会怎么样?这个结果我们不敢想象。我们既然是是去深入的挖掘它,那我们就深入去看看可以怎么优化。

我们做一个延迟的滚动进度条

功能:在鼠标一直滚动的时候是不会改变进度条的长度,直到鼠标的滚动停止后0.5s之后才开始出发轮动条的改变,即你一直在0.5s之内滚动,进度条长度不会改变。
在这里我们主要是学习这个方法吧。如果模拟进度条的改变是请求服务器的话,我们就可以有效的去抑制住那些疯狂请求服务器的动作。

我们要实现的效果大概是这样的:

这样我们主要还是用setTimeout来进行限制,因为setTimeout可以有效的帮我们延迟触发的时间。

代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
window.addEventListener('scroll',delay(() => {
console.log(1);
app.changeWidth=(document.body.scrollTop/(document.body.scrollHeight-window.innerHeight))*100
},500))

function valve(func,time){
let timer=null;
const _fun=function(){
clearTimeout(timer);
timer = setTimeout(()=>{func()}, time)
}
return _fun;
}

在这里我单独做了一个函数,我们利用了setTimeout和clearTimeout来成功抑制住我们的进度条长度的改变,这也使得这个进度条在停止的时候才会进行改变。实现的效果还是不错的哈哈!

当然这种延迟的效果的思想应该是更重要的,我曾今做过一个那个输入框提示的一个小功能,当这个input输入框里面有内容在输入的时候不会触发,当这个input输入停止后0.5s,网页会用这个input里面不管输入还是没有输入的内容去请求服务器,看看是否存在这个用户。或者是一个输入的提示效果

例如这样:

那我们为什么不做一个可以时间间隔触发的进度条呢?

效果是这样的:

代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
window.addEventListener('scroll',valve(() => {
app.changeWidth=(document.body.scrollTop/(document.body.scrollHeight-window.innerHeight))*100
},1000))

function valve(func,time){
let timer=null;
const _fun=function(){
if(timer) {

}else{
clearTimeout(timer);
timer =setTimeout(() => {func()}, time);
timer=null;
}
}
return _fun;
}

这个进度条的目的是如果鼠标滚轮一直在滚动,那么他将会做一个阻隔,是隔一秒钟变化一次。这样的话,就有效的减少了进度条频繁的变化,他只是在滚动的途中,隔着一秒钟去变化一次。
这个思想就是阻隔的思想,如果一个事件一直在请求服务器,我就可以限定出它间隔多少秒去请求服务器,有效的阻隔请去服务器的次数。

上面的两个解决方案和思想也是我们这次进度条深究得出的比较有价值的东西

那我们还可以做什么进度条呢?

其实在文章的开头我们就有提到,YouTube上面的红色进度条,这个进度条为什么坑爹呢!因为他是假的。
我们每次打开加载视频的时候,他是不是都会卡一下?见图。

可能大多数人都以为它加载的时候卡了一下吧。其实是假的!假的! 他只是故意营造出一个断断续续加载的效果让大家看的舒服一点而已。

你可以论证一下。你把网断了,你再点视频,发现他还是有这个红色的条子,只是到一般就停了变成了无网络的界面而已。

要是再不信你可以用谷歌浏览器测试下?哈哈!

这里用到就是那个间隔变化的滚动条,可以自己实现一个去模拟Youtube上面的效果哦!

最近在干嘛

老实说我已经有快一个月都没有更新博客了,虽然现在博客浏览量还不是很多啦。但是还是我必要说说最近我在干嘛。

其实最近在一间小的算是创业团队或者公司实习,所以时间比较紧,星期二星期四满课,一三五六都会在办公室打代码,而且回到学校都会被各种作业和部门事情困住,所以呢,就没有很多时间去更新自己的博客或者写一写笔记了。

只不过在那个团队有很牛逼的师兄带着我学习,也接触很多新的技术,例如Vuejs等等,才发现真的其实学习真的是无止境的。

大概这个实习会持续到1月份。这时间里面我可能会一直记录我在学习工作中遇到的问题吧。也希望自己能够进步。

到时候这个项目结束以后,我也会做几期记录,来记下自己学习到了什么,在这几个月做了什么有了什么成果。也给自己一个鼓励吧!

项目中的prop()的attr()的jQuery大坑

说起来惭愧,在现在React横行的年代,自己还在大学的项目里面用jQuery慢慢磨。大一的时候深受《锋利的jQuery》的影响,一直都觉得jQuery是一个特别 Niubility 的框架。当然!这只不过是开个玩笑,受项目制约,我还是得认真研究下这个jQuery。

项目中遇到的程序的Bug

以为这个东西非常简单

首先我的项目要做一个全选复选框的功能:列表项有多个复选框,当列表头的复选框的选择了之后,列表中的所有的复选框不管是否已经选择都必须全部选择。而当列表头的复选框取消了选择,所有的列表复选框也必须取消选择。

看似很简单的功能呢。我们知道在复选框中,只要添加了 checked 属性,就算他里面是” “这样的空值,但是都是选中的。

那就用jQuery的attr()来进行实现吧,将checkbox的 checked 属性变成 checked值。首先我们来实现全选的功能

1
2
3
if ($(".contentRightListGroupBanderListInput").attr("checked")) {
$("input[name=items]:checkbox").attr('checked', true);
}

结果

结果发现:根本没有反应诶亲!而且如果用了FireBug进行调试,发现if的语句内部根本就进不去啊!

利用Firefox进行调试,发现checked属性的确是true,但是都是进不去if语句。

并且我还发现一个更加严重的问题,怎么做全不选的功能,利用hasAttr()来判断是否有checked属性吗?这样如果没有选中的话,就会返回一个undefined,这个恐怖的东西是我们不想见到的。

翻阅一切资料找到的prop()方法

在jQuery 1.6之后,就开始推荐利用prop()方法来部分替代attr()方法了。利用prop()方法的好处,特别是在处理单选复选框的时候,利用prop()代替attr()会更好。

因为prop()在判断checked只会返回true和false,有这个checked属性返回true,没有就返回false,这样有利于我们判断。但attr()返回都是我们自己定义属性值,例如在checked属性中,虽然只要有这属性,选框都是选中,但是他返回的是值,也就不管你里面的值是checked true 还是空值都会返回给你,如果没有checked属性还会返回undefined,简直乱麻。

利用prop方法来判断是否选中的简单代码:

1
2
3
4
5
6
if ($(".contentRightListGroupBanderListInput").prop("checked") == true) {
$("input[name=items]:checkbox").prop('checked', true);
}
if ($(".contentRightListGroupBanderListInput").prop("checked") == false) {
$("input[name=items]:checkbox").prop('checked', false);
}

这样单纯的利用truefalse来判断就可以轻松解决用attr()属性解决不了的问题。

什么时候应该用prop()什么时候用attr()呢

其实如果说prop()只是在checked中有大作用那就错了

1.添加属性名称该属性就会生效应该使用prop();
2.是有true,false两个属性使用prop();

prop()就是专门为这两个来使用的,例如disabled checked selected

那么在其他情况之下就使用attr就可以完成几乎所有功能了。特别是在自定义属性中,还是一定要用attr()的,因为你定义的属性值可能连你自己都不知道你在定义什么,哈哈,开玩笑。总之,自己定义的属性一定要用attr()。

当SetTimeout遇到了字符串

今天闲来没事的时候,去逛逛segementFault,看了看别人提的问题。说到setTimeout和window.onload冲突。一开始我是挺疑惑他表他什么意思的,因为setTimeout和window.onload应该不会有明显的冲突吧。带着疑惑去追问。后来贴出代码的时候我就明白了,来看看他的代码中的疑惑吧。

1
2
3
window.onload=function(){
setTimeout("D.style.background='#990033'",2000);
}

他觉得这个代码运行的时候,setTimeout会报错,不能正确的运行指定的代码,就觉得在window.onload和setTimeout有冲突。

我对这个问题非常感兴趣。也自己试了一下,发现确实不能运行字符串里面的代码。但是还是很怀疑window.onload和setTimeout怎么可能会有冲突呢?

解决问题

为了测试方便,我就稍微改动了一下代码。将setTimeout的调用改成调用函数。

1
2
3
4
5
6
window.onload = function() {
function myFun(i) {
alert(1);
}
setTimeout('myFun(1)', 2000);
}

在这里发现的确不能运行,但是这里的问题就很明显了,我将函数的调用变成了 'myFun(1)'字符串调用,所以会出现不能调用 myFun()的问题。

为了深入理解我们在W3school查询一下setTimeout的用法

那么就是setTimeout只能接受函数或者是表达式的计算。那么现在答案很明显了,既是setTimeout不支持第一个参数为字符串的调用。

但是按照提问者的用法,这种 "D.style.background='#990033'"CSS的调用也必须准守这种形式那么怎么办?

那么就服从setTimeout的规则,说干就干,将他装换成函数就好了。见以下代码。

1
2
3
4
5
window.onload=function(){
setTimeout(function(){
D.style.background='#990033'
},2000);
}

这里的2秒钟之后就会执行setTimeout里面的匿名函数,即准守了setTimeout的原则,也可以利用setTimeout来调用类似字符串的形式的功能代码。

回到提问并且深入挖掘

回到提出的问题上来,深入的挖掘,其实并不是setTimeout和window.onload有冲突,在原来的问题中,其实是Javascript作用域在作怪。

在原先的问题中,如果是 setTimeout("D.style.background='#990033'",2000); 的话,由于setTimeout的第一个参数支持的是函数或者是表达式,所以字符串会被自动执行new Function,将这个字符串强制转换成一个函数。

我们知道在Javascript中,函数里面有自己的作用域,和外界的作用域不同,而在函数内部并没有D.style.background这个对象,所以会有报错。在我变化的例子中,也是如此,如果使用了 setTimeout('myFun(1)', 2000),那么这里面的字符串就会被自动执行new Function,所以创建出来的函数不在这个作用域内,当然也就不能调用,会出现无法找到的问题了。

AngularJS显式依赖注入

依赖注入是Angular的招牌功能,使用angular的$injector注入器就可以实例化所有的组件、模块、指令、控制器。这也是angular的核心功能之一。也是理解Angular内部机制的一部分。

而一般来说Angular是不需要显式进行依赖注入的,因为AngualrJS会内部帮你隐式注入。你可以不管内部发生了什么,但是你的组件、模块、指令、控制器就和声明了ng的元素节点绑定在了一起。

只不过为了了解Angular的内部机制,我们还是要学会如果利用angular的$injector进行手工的显式注入。除此之外Angulard的显式注入可以定义一个函数被调用时用到的依赖关系。这样的话,在源代码被压缩、参数名字改变的时候,angular还是可以执行依赖注入。但是隐式的注入就没有这种效果了。

现在我们来看看AngularJS显式注入的代码。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
var aControllerFactory =
function aController($scope, greeter) {
console.log("LOADED controller", greeter);
};

aControllerFactory.$inject = ['$scope', 'greeter'];
var greeterService = function() {
console.log("greeter service");
return {
doTheThing: function methodThatDoesAThing() {}
}//factory返回对象
};

angular.module('myApp', []) //这里的myApp链接到html元素
.controller('MyController', aControllerFactory)
.factory('greeter', greeterService);

var injector = angular.injector(['ng', 'myApp']),
controller = injector.get('$controller'),
rootScope = injector.get('$rootScope'),
newScope = rootScope.$new();

controller('MyController', { $scope: newScope });

这段代码显示的结果是:

显示的依赖注入的参数的顺序是非常重要的,如果$inject数组的顺序和注入的顺序有差别,就会让注入的元素为空。

Angular.js中的compile pre-link post-link选项的个人理解

AngularJS的生命周期

在AngularJS的生命周期中,分为编译链接两个阶段。

  • 在编译阶段中,每一个指令可能有会有另外一个指令,AngularJS遍历他们形成了模板树,之后会返回一个模板函数,而在模板函数返回之前DOM都是没有形成的,所以此时ng-repeat指令就会生效。
    而在编译完成之后,会返回一个编译函数,这个编译函数会返回一个总的将所有子指令模板合并在一起的模板函数,并且交给链接阶段。

  • 在链接阶段中,我们可以将作用域scope和DOM进行链接,并且对每一个函数的模板的实例进行链接或者监听器的注册。

这个阶段中的三个选项 compile pre-link post-link

这个三个指令并不是完全能用得到,特别是compile,这个指令在实践中,并不会频繁的使用。但是理解这个三个指令的工作机制对于我们理解AngluarJS的生命周期和运作过程有非常重要的作用。

首先看看compile

compile意思是我们希望指令和数据在放入DOM前进行对DOM的操作,因为我们从上一节生命周期中知道,在没有链接之前,我们的DOM虽然生成了,但是我们可以修改DOM,例如添加或者删除节点。但是我们并不推荐这样做。

compile指令可以返回一个对象或者函数,这里我们可以利用Angular的DOM操作element来实现

1
2
3
4
5
6
7
8
9
10
11
angular.module('myApp',[])
.directive('myDirective',function(){
return{
compile:function(tEle,tAttrs,transcludeFn){
//这里进行DOM的操作
return function(scope,ele,attrs){
//在这里进行链接函数的链接
}
}
}
});
  • 注意我们只要使用了compile选项就会默认互斥link选项 这样link选项的所有都会被重写。
  • 而且我在compile选项中的函数用了tEle 和tAtts 这是因为这里操作的还没有实例化的template,这里还没有进行链接,所以没有scope和实例。

link指的是我们在compile执行完之后将作用域和DOM进行链接,一般我们指的link都是Postlink,因为如果你直接写Link指令就是默认变成postlink。

postlink的用法

postlink是我们最经常用的一个选项,当然我们默认只会写上link,就是指postlink,他可以为我们链接作用域和编写业务和逻辑代码。

prelink的用法

prelink应该是我们理解Angular中最难的一个选项,prelink会在Postlink之前执行,他在compile之后执行。在prelink中写的是我们在compile DOM操作之后但是又是在postlink执行的业务代码。

实例代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
angular.module('myApp',[])
.directive('myDirective',function(){
return{
compile:function(tEle,tAttrs,transcludeFn){
//这里进行DOM的操作
return {
pre: function(scope, iElem, iAttrs){
console.log(name + ': pre link => ' + iElem.html());
},
post: function(scope, iElem, iAttrs){
console.log(name + ': post link => ' + iElem.html());
}
}
}
}
});

compile pre-link post-link 执行的顺序

首先应该是compile和pre-link是以此的执行的,他们执行完之后post-link才会执行。如果有多个嵌套的指令元素,那么compile 和pre-link会依次执行,而它们执行完后postlink才会执行。
且,pre-link会反向执行,从内到外的执行来保证执行顺序。

这里引用jingxian的文章

传入compile pre-link post-link 的参数解析

当compile 运行的时候,我们的DOM还会修改所以这里的参数都是模板 例如tEle tAttrs
当进入链接时pre-link传入的参数这时是还是tEle tAttrs 因为这里还是链接之前
当然pros-link传入的作用域和元素都是实例我们用,iElement iAttrs scope 如果存在require引入新的作用域的话,我们会多一个controller的参数指向引入的控制器。

ES6函数扩展和解构复制理清

先从阮老师ES6教程的一个事例说起

1
2
3
4
5
6
7
8
9
// 写法一
function m1({x = 0, y = 0} = {}) {
return [x, y];
}

// 写法二
function m2({x, y} = { x: 0, y: 0 }) {
return [x, y];
}

请问这两个的写的有什么区别?一开始看到这个我是蒙的?因为这里将函数的默认值的ES6写法和ES6写法的解构赋值放在了一起。

解释与理清

在这里,函数的参数都变成了对象,而ES6的解构复制就是将利用两个对象的对比来进行赋值进而简化代码

而阮老师给出的答案是

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// 函数没有参数的情况
m1() // [0, 0]
m2() // [0, 0]

// x和y都有值的情况
m1({x: 3, y: 8}) // [3, 8]
m2({x: 3, y: 8}) // [3, 8]

// x有值,y无值的情况
m1({x: 3}) // [3, 0]
m2({x: 3}) // [3, undefined]

// x和y都无值的情况
m1({}) // [0, 0];
m2({}) // [undefined, undefined]

m1({z: 3}) // [0, 0]
m2({z: 3}) // [undefined, undefined]

解决问题的思路

我们解决问题都是从大到小,从简单到复杂,在函数的参数传值比较复杂的情况下也是利用从外到里的看

首先我们看写法一 {x = 0, y = 0} = {}

  • 我们从外到里的看,首先是 调用 传入参数,接着才是判断 是否有参数 如果 ,那么默认值 即最右边 = 右边的 {} 不会生效 进入下一步 如果 没有 那么就默认值 即最右边 = 右边的 {} 就会成为函数参数的默认值

  • 如果外层的默认值不生效 接着看传入的参数对象内部 { } 是否 符合参数对象 内部默认的 x y两个参数 如果不符合 就会启用内层x y 的默认值 ,要是符合,就利用传入的原值

调用 m1() ,首先看外层,没有参数,所以启用默认值 即最右边 = 右边的 {},那么再看内层,这里面没有符合 x y的参数 所以就会启用内层x y的默认值 所以结果 为[0,0]

调用 m1({x: 3, y: 8}) ,内层外层都不会启用默认值所以 为[3,8]

调用 m1({x: 3}),外层有 {}的对象不启用外层默认值,但是内层没有参数y 所以y启用内层默认值结果为[3,0]

调用 m1({}) 外层有 {}的对象不启用外层默认值,内层无x y,所以启用x y默认值 结果为 [0,0]

调用 m1({z: 3}) 同上

接着我们看写法二 {x, y} = { x: 0, y: 0 }

  • 我们从外到里的看,首先 调用 ,判断 是否有参数 ,没用有就用默认值 { x: 0, y: 0 }

  • 如果外层的默认值不生效 接着看传入的参数对象内部 { } 是否 符合参数对象 内部有 x y两个参数 但是这两个参数没有默认值

调用 m2() ,首先看外层,没有参数,所以启用默认值 即最右边 = 右边的 { x: 0, y: 0 },那么结果为[0,0]

调用 m2({x: 3, y: 8}) ,内层外层都不会启用默认值所以 为[3,8]

调用 m2({x: 3}),外层有 {}的对象不启用外层默认值 内层y没有默认值,所以就结果为[3, undefined]

调用 m2({}) 外层有 {}的对象不启用外层默认值,内层无x y 且x y 无默认值,结果为[undefined, undefined]

调用 m1({z: 3}) 同上

从一个小题目谈谈js函数闭包

先来看一个问题

题目

1
2
3
4
5
var str1 = str2 = "web";
(function () {
var str1 = str2 = "前端";
})();
console.log(str2);

当然这里输出的是结果是:

前端

如果现在将console.log(str2)变成consloe.log(str1),那么输出的结果就会变成

web

解答

其实这题并不是很难,第一句 var str1 = str2 = "web"; 其实是定义了两个全局的变量,在利用function的闭包内用 var 重新定义了 str1 而没有重新定义 str2

我们知道在默认情况下 如果不用 var 定义的变量都会变成全局变量,所以此时在function闭包内的str2就是引用了全局变量,所以赋值操作当然也就能赋予全局变量 str2 所以输出 str2 结果是 “前端”

str1 用了 var 定义,就是在function闭包内的变量,闭包外自然不可以改变,所以输出的结果是 “web”

脑洞大开

其实每一次看到这种形式的代码

1
2
(function(){
})()

都觉得非常的新鲜,觉得这里有很多东西可以专研,所以在这里也总结一下这种形式的闭包。

解释前先看看

首先这种形式的闭包是人为的加上去,并不是说可以有什么神奇的 duangduang 的特效,而是可以避免很多本来是局部变量可以搞定的比较 low 的变量去污染全局的变量

其次在js中,是 没有块作用域 这种说法
首先我们回到C++,如果有一段代码是这样

1
2
3
4
5
int number1=10;
if(true){
int number1 = 5;
}
cout<<number1;

这里的结果还是

10

而在js代码之中

1
2
3
4
5
var str1= "web";
if(true){
var str1="前端";
};
console.log(str1);

这里的结果就是

前端

因为 if{} 没有块作用域,所以内部的str1直接就重定义了外部全局的str1,所以输出的结果就只是”前端”了,而且这也污染了全局变量(那个等于”web”的str1已经不见了踪影)

而大 js 只有 函数作用域

所以我们要利用函数闭包

利用函数闭包能有效的封装局部的变量,而不污染全局作用域

1
2
3
4
5
6
7
str1 = "web";
(function () {
var str1 = "前端";

//str1剩下的功能
)();
console.log(str1);

此时输出的还是

web

所以我们利用了一个匿名函数 function(){} 并且让他自己调用自己执行函数内部的操作 并且 str1 也没有污染到外部的全局作用域


我是何方猿

老实说,我不擅长表达。
开这个个人的Hexo博客也是想自己写点东西,记录自己程序员发展的点点滴滴。

叫我小俊吧,这样比较亲切一点。

我在哪?

我现在是大三,就读于华南理工大学软件学院。
目前从事的是Web前端方向,断断续续有学一年多吧,可以自己写点小东西出来,只不过也是一个初级的不能再初级的前端工程师了。

关于前端我想说

前端发展实在是超级超级快,目前正在狂学react.jsangular.js
也想成为一名全栈工程师,所以也在努力的学习node.js
自己同时也会从事原型设计工作

目标

当然啦。作为一名超级心怀“理想”的猿,还是想去好的公司打码啦。毕竟猿也要成为一名成精的猿嘛。hhh

来这里干嘛

来这里的首要目的就要虚心学习学习,交流交流,知识无尽嘛!
当然来这里BB也是可以的。来者不拒,我是话唠嘿嘿嘿。