徒步上山看日出

徒步上山看日出 查看完整档案

广州编辑佛山职业技术学院  |  物联网应用技术 编辑XXXXX  |  WEB开发工程师 编辑填写个人主网站
编辑

黑夜給了我黑色的眼睛,我卻用來尋找光明。

个人动态

徒步上山看日出 赞了文章 · 2019-09-20

魔兽世界lua笔记(3、魔兽界面位置修改)

关于插件,以前在nga上看人说,老外大多用单体插件,国内大多用整合插件。

不论是单体还是整合,后者是默认的UI,总是会有自己觉得不舒服的地方,那么就自己来动手微调一下吧。

首先来举一个例子:比如盗贼的能量,默认是在血条的下方,打怪的时候经常要斜着眼睛瞄着那里,很不利于身心健康,所以我们想把它放到中间来

盗贼能量移动代码如下,做成宏就可以了。(说明:这个例子不是我自己写的,原贴来自nga)

/run PlayerFrameManaBar:ClearAllPoints()
/run PlayerFrameManaBar:SetPoint(“CENTER”, UIParent, “CENTER”, 0, -80)
/run PlayerFrameManaBar:SetScale(1.2)
/run PlayerFrameManaBar:SetAlpha(1.0)

不过提供宏不是我们的目的,我们的目的是要知道如何来写类似的宏,所以老胡来解释一下这4行代码的含义:
1、先说每行都有的这个PlayerFrameManaBar,上节课《老胡lua随笔——5)魔兽插件介绍,如何编写一个魔兽插件》里面,我们用到了一个frame,目的是做事件监控,这里的PlayerFrameManaBar也是一个Frame,是一个UI类的Frame,他就代表着玩家的Mana条,对于盗贼来说就是能量条,对于其他职业来说可能是蓝条、怒气等等。

有同学可能要问了,你怎么猜到蓝条是PlayerFrameManaBar的,如果我想搞血条怎么办,有没有一个字典,答案是没有,但是blz提供了一个更好的宏/fstack

在聊天框输入这个命令之后,鼠标移动到任意的界面上,就会提示当前frame的名字(注意,只针对全局frame,局部frame的名字是动态生成的,所以可能是一堆数字之类的,那种就不行了)

2、然后来说第二行的SetPoint这个方法(第一行等下说),这个方法是用来调整UI的位置,这个方法有两种用法,我们来一一介绍:

1)绝对定位法

一般来说,我们会像下面这样来定位一个窗口,指定他的左上角与屏幕左上角的距离,对魔兽来说UIParent代表着屏幕窗体,所以可以这样写。

local xoffset, yoffset = 100,-80(负数代表往下移动,应该是这样,老胡没测试)

/run frame:SetPoint(“TOPLEFT”, UIParent, “TOPLEFT”, xoffset, yoffset)

clipboard.png
2)相对定位(注意,前方高能)
刚才的例子中,frame:SetPoint(“TOPLEFT“, UIParent, “TOPLEFT“, xoffset, yoffset)

left,top大家应该理解了,但是这两个TOPLEFT 是什么鬼?

第一个TOPLEFT指的是当前frame的左上角,第二个TOPLEFT指的是UIParent的左上角,合起来,就是说frame左上角的点,距离UIParent左上角是100,80。想想一下两本书,一个大一个小,先左上角对齐,然后把小的那本,往右100,再往下80。

下面再来说一个例子,请思考,如果我想把一个方块放到另一个方块的右边,怎么做?

clipboard.png
按照刚才的思路,先把两本书的左上角对齐,然后往右移动Frame1宽度的距离就行了,因为是顶部对齐,所以不用往下移动,所以这样就可以了。

local xoffset, yoffset = Frame1:GetWidth(), 0

Frame2:SetPoint(“TOPLEFT”, Frame1, “TOPLEFT”, xoffset, yoffset)

再来一个,这就需要不但往右,还要往下,往下的距离是两个frame的高度差。

clipboard.png
local xoffset, yoffset = Frame1:GetWidth(), Frame1:GetHeight()-Frame2:GetHeight()

Frame2:SetPoint(“TOPLEFT”, Frame1, “TOPLEFT”, xoffset, yoffset)

说明,上面的Frame1:GetHeight()-Frame2:GetHeight()应该是不对的,上面我们说过,向下是负数,所以应该取一个负,但是这里不是我们的关键,请忽略这个,我们只讲思路,正负问题留给你去尝试了,多试才能会啊。

经过上面几个例子,我们应该有点会了,但是老胡告诉你,我们上面的代码实际上是很low的,来我们来看一下这个方法的完全体:

Frame:SetPoint(“point” [, relativeTo [, “relativePoint” [, xOffset [, yOffset]]]])

详细介绍参见api:http://wowprogramming.com/doc...

第一个参数是point,我们上面大多用的TOPLEFT,实际上他还有很多别的取值:CENTER BOTTOM TOP LEFT RIGHT BOTTOMLEFT BOTTOMRIGHT TOPLEFT TOPRIGHT

比如刚才这个例子,我们刚才是说,先左上角对齐,然后Frame2往右移动Frame:GetWidth()的距离,但是我们也可以想象成,我直接用Frame2的左上角,对齐Frame1的右上角,然后不移动,对比一下这两种的写法:

--第一种
local xoffset, yoffset = Frame1:GetWidth(), 0
Frame2:SetPoint("TOPLEFT", Frame1, "TOPLEFT", xoffset, yoffset)

--第二种
local xoffset, yoffset = 0,0
Frame2:SetPoint("TOPLEFT", Frame1, "TOPRIGHT", xoffset, yoffset)

clipboard.png

然后下面这个,就是2的左下,对齐到1的右下。

clipboard.png
2的中间点对齐到1的中间点(想象一下如果都用TOPLEFT,是不是繁琐了不少)

clipboard.png

回顾一下我们开头的例子,能量条的中间点,对齐到UIParent的中间点,然后往右移动0,往下移动80

/run PlayerFrameManaBar:SetPoint(“CENTER”, UIParent, “CENTER”, 0, -80)

好,SetPoint方法就啰嗦到这里。到这里大家应该发现了,我上面提到说有两种用法,绝对定位和相对定位,实际上并没有,只有相对定位这一种(完事无绝对啊),绝对定位,其实就是相对于屏幕,而屏幕本身也是一个frame,叫UIParent,记住了,这个名字很常用。

如果想看某个Frame的Point,可以用xxFrame:GetPoint()方法哦。

3、再回到最初的例子,来说最后几个,这几个就简单了。

/run PlayerFrameManaBar:ClearAllPoints()
/run PlayerFrameManaBar:SetPoint(“CENTER”, UIParent, “CENTER”, 0, -80)
/run PlayerFrameManaBar:SetScale(1.2)
/run PlayerFrameManaBar:SetAlpha(1.0)

第一行,ClearAllPoints,跟SetPoint刚好相反,是用来删掉frame之前设置的Point,因为后设置的Point并不会直接覆盖前面的,也就是说可以设置多个Point(多个Point的用法老胡也没用过),一般来说,我们设置新的Point之前,需要把旧的删掉,所以无脑加一下就行了。

第三行SetScale是设置frame的尺寸,1是原本的大小,0.9就代表只有原来的0.9倍大小,1.2就代表放大到1.2倍

第四行SetAlpha,是设置透明度,1是完全不透明,0.1~0.9是半透明,越小越透明,0就看不见了。

好啦,今天就写到这里,SetPoint部分比较啰嗦,还望大家耐心。有问题可以找我讨论哦。

查看原文

赞 3 收藏 1 评论 0

徒步上山看日出 赞了文章 · 2019-09-20

魔兽世界lua笔记(2、编写一个打断喊话插件)

前面我们介绍了lua的一些基本用法

然而一切的一切,还是为了能够搞魔兽插件,今天终于步入正题,来搞一个简单的魔兽插件

一、插件的结构

先来看看插件的结构吧,首先,插件是在World of WarcraftInterfaceAddOns目录下的,相信你已经知道了。

目录下面有很多文件夹,我截图了一部分。

clipboard.png
这里面每一个文件夹都是一个插件,这些插件有的是完成一个独立的工作,有的只是一个子模块。一般来说,你实现的功能不太复杂,那用一个文件夹就可以了,如果你搞了一个巨复杂的插件,例如DBM,通常来说你就要规划你的模块。

魔兽的插件可以做很多事情,blz本身就是插件的最大用户,魔兽的登录界面、服务器选择、人物选择界面、游戏里面的UI等等,都是用插件来实现的(默认alt+z快捷键可以隐藏的部分,都是插件实现的),当然blz的有些插件有一些并没在Addons里面,而是隐藏到了客户端里面。

上图里面,Blizzard开头的是blz公司自己的插件,dbm开头的是dbm的插件,其他的是一些单体插件。

下面我们从这里面找了一个文件结构最简单的BuffMaster(增益大师)来说:

clipboard.png

这里面只有两个文件BuffMaster.toc和BuffMaster.lua,下面我们来介绍一下

二、toc文件简介

核心是BuffMaster.toc文件,这是插件的声明文件,每个插件都必须有这么一个文件,他的文件名跟插件的目录名一样,作用是用来告诉wow客户端,你的插件的文件都放在哪里,当然也还有一些别的作用,这个也不用太深究,随着功力的提升慢慢了解就好。

下图就是toc文件的内容,我大概介绍一下:

clipboard.png

  • 第一行是插件的适用版本,70100代表这个插件适用于7.1.x版本,老胡写这篇文章的额时候已经是7.2.5,所以如果运行这个插件就会提示已过期,改为70200就ok了。——很多人问插件过期了怎么改,就是这么简单
  • 后面好多行,都是插件的名字,英文的,简体中文的,繁体中文的,等等
  • Dependencies,依赖哪个插件,这里写的是依赖BigFoot,所以如果玩家勾选了这个插件,但是没勾选BigFoot,就会提示“依赖没有启用”(忘了是不是这么提示的了),导致你的插件不能运行。但实际上这个插件跟大脚没有任何关系,即不是大脚开发的,也不需要依赖大脚,就是大脚的一个流氓行为,所以完全可以删了这一行。很多人问怎么把一些插件分离成单体——这里已经告诉你答案了。但是,有些确实需要依赖的,如果要分离就要改代码了。
  • LoadOnDemand,意思是需要的时候才加载,目的是用不到这个插件的时候就不用占用内存了
  • 最后一行,是关键,这里指定了我们插件中文件的路径BuffMaster.lua,这个文件名不需要跟插件名一致。

我们再来看一下DBM-Core的

clipboard.png

看看toc文件,上面大同小异,下面是这个插件引用的文件资源

clipboard.png

三、编写一个魔兽插件

好了,我们来动手搞一个简单的插件,插件的目的是打断的时候说一句话,这样你就不用再做打断喊话宏啦。

首先我们照猫画虎在Addons下面新建一个文件夹,我起名叫_InterruptWarning,前面有个下划线是为了排序的时候在前面,避免混到一堆插件中找不到,是不是好机智,哈哈。

clipboard.png

然后分别编写两个文件的内容,先来toc的,这个就不重复介绍了

clipboard.png

再来lua的

clipboard.png

这个得好好介绍一下了,因为这个插件虽然代码不多,但是涉及的知识还不少。请看好左边的行号,老胡下面一行一行介绍:

  • line1: 定义一个变量interuptFrame,同时给他赋值,用魔兽的CreateFrame方法创建了一个frame,后面我们将为这个frame加特技
  • line2: 加第一个特技,注册一个事件,COMBAT_LOG_EVENT_UNFILTERED,也就是当战斗日志产生之后触发,点击链接可以查看详细介绍
  • 跳到line11: 家第二个特技,这里定义了档事件触发的时候,通知interruptEvent这个方法,也就是3-10行这个方法
  • line4: 判断是不是战斗日志事件
  • line5: 获取事件触发的参数,我们需要的是eventType和sourceName这两个,含义分别是时间类型和事件来源
  • line6: 判断,如果事件类型是技能被打断,并且事件来源的名字是玩家的名字,这说明当前玩家成功打断了一个技能
  • line7: 说一句话:interrupt >> [xx技能]
    一个简单的事件触发机制。

有点编程基础的人可能懂了,没基础的小白一般还懵逼着,因为老胡这个例子本身就有难度。因为老胡也不想搞个print(1)之类的,然后就告诉你插件做好了。。

那小白怎么才能懂呢,一句话,猛练自然强,练习题来了,请参照老胡的例子实现一个场景,萨墓里面主母这个boss,中了[吞噬之饥]这个debuff之后,会持续掉血,但是又不明显,所以很多人看不到,每次都要团长提醒,作为团长,现在请你实现一个功能,中了debuff的时候,就说三声“我中了[吞噬之饥],等下踩[雷霆震击]消debuff”,做好了可以qq老胡交流376665005。

有自信的可以做一个更好的例子。不但自己中了提醒,团里的其他人中了,还可以私密提醒他。

最后,上面例子的源码,声明一下,老胡并未测试,如果不能用,纯属正常。

怎么样,这个技能你get了吗?

查看原文

赞 3 收藏 2 评论 0

徒步上山看日出 赞了文章 · 2018-06-15

关于input的一些问题解决方法分享

前言

input是我们接受来自用户的数据常用标签,在前端开发中,相信每个人都会用到这个标签,所以在开发过程中也时候也会遇到一些问题,本文的内容是我在跟input相爱相杀过程中产生的,在此记录分享一下。如果喜欢的话可以点波赞/关注,支持一下,希望大家看完本文可以有所收获。

个人博客了解一下:obkoro1.com

本文内容包括:

  1. 移动端底部input被弹出的键盘遮挡。
  2. 控制input显/隐密码。
  3. 在input中输入emoji表情导致请求失败。
  4. input多行输入显示换行。
  5. 输入框首尾清除空格-trim()
  6. 在input中监听键盘事件

移动端底部input被弹出的键盘遮挡

input输入框是通过position:fixed一直放在页面底部,当点击input进行输入的时候,就会出现如下图片情况(有的机型会遮挡一些)。

当时这个问题是去年在ios中遇到的,在最新版的ios系统中,貌似解决了这个bug,但是为了向下兼容以及防止其他其他机型也出现这个问题,大家可以稍微记一下这个解决方法。

在解决这个问题的时候,有试过下面这种方法:

在input的focus事件中,开启一个定时器,然后每隔300毫秒进行一次document.body.scrollTop=document.body.scrollHeight的调整,运行3次即可。

当时还以为解决了,但是当你底部评论区还有很多内容,你每次点击input,想要输入的时候,整个页面通过scrollTop就会不断的向下滚动,这个体验不用说自己也知道是相当失败的,然后就再去找解决方法,结果就有了下面这个。

Element.scrollIntoView()

Element.scrollIntoView():方法让当前的元素滚动到浏览器窗口的可视区域内

    document.querySelector('#inputId').scrollIntoView();
    //只要在input的点击事件,或者获取焦点的事件中,加入这个api就好了

这个api还可以设置对齐方法,选择将input放在屏幕的上方/下方,类似的api还有:Element.scrollIntoViewIfNeeded(),这两个是解决同一个问题的,选择一个用就可以了。


控制input显/隐密码

这个就很简单了,只需更改input的type属性值就可以了。可以看一下codepen的demo

    //点击函数,获取dom,判断更改属性。
    show(){
        let input=document.getElementById("inputId");  
        if(input.type=="password"){ 
          input.type='text';
        }else{
          input.type='password';
        } 
    }

在input中输入emoji表情导致请求失败

现在用户输入emoji简直已经成为了习惯,如果前后端没有对emoji表情进行处理,那么用户在上传的时候,就会请求失败。

通常这个问题是后端那边处理比较合适的,前端是做不了这件事的,或者说很难做这件事。

之前看过一篇文章,这个文章里面讲了怎么在上传和拿数据下来的时候不会报错,但是不能在显示的时候转换为表情。

ps:之前拿微信用户名的时候,有些人可能在微信昵称上面就会包含表情,如果后端没对表情处理转换,那么普通请求也会出错

之所以说这个,当表单请求错误的时候各位如果实在找不到问题可以往这方面考虑一下,我真的被坑过的o(╥﹏╥)o。


textarea多行回车换行,显示的时候换行设置:

在使用textarea 标签输入多行文本的时候,如果没有对多行文本显示处理,会导致没有换行的情况,就比如下面这种情况,用户在textarea是有换行的。

Css属性:white-space

white-space 属性用于设置如何处理元素内的空白,其中包括空白符和换行符。

只要在显示内容的地方将该属性设置为white-space: pre-line或者white-space:pre-wrap,多行文本就可以换行了

设置之后,显示效果:


输入框首尾清除空格-trim()

输入框清除首尾空格是input较为常见的需求,通常在上传的时候将首尾空格去除掉。一般使用:字符串的原生方法trim() 从一个字符串的两端删除空白字符。

trim() 方法并不影响原字符串本身,它返回的是一个新的字符串。

原生清除方法:

    //原生方法获取值,清除首尾空格上传str2 
    var str2 = document.getElementById("inputId").trim();

Vue清除方法:

Vue提供了修饰符删除首尾空格, 加了修饰符.trim会自动过滤用户输入的首尾空白字符

    <input v-model.trim="msg">

貌似angular也提供了类似过滤的方法,感兴趣的可以自己去查一下。


在input中监听键盘事件

在用户登录或者搜索框的时候,一般都会监听键盘事件绑定回车按键,来执行登录/搜索 等操作。

原生绑定:

 <input onkeydown="keydownMsg(event)" type="text" />
 function keydownMsg(key) {
        keyCode = key.keyCode; //获取按键代码
        if (keyCode == 13) {  //判断按下的是否为回车键
            // 在input上监听到回车 do something
        }
    }

Vue按键修饰符

Vue为监听键盘事件,提供了按键修饰符,并且为常用的按键提供了别名,使用方法如下:当回车按键在input中被按下的时候,会触发里面的函数。

    <input @keyup.enter="enterActive">

结语

上述内容就是我遇到的一些input问题的解决方式以及跟input相关的一些东西,如果有什么错误,欢迎指正!希望大家看完可以有所收获,喜欢的话,赶紧点波订阅关注/喜欢,方便以后查找复制粘贴,233。

希望看完的朋友可以点个喜欢/关注,您的支持是对我最大的鼓励。

个人blog and 掘金个人主页,如需转载,请放上原文链接并署名。码字不易,感谢支持!本人写文章本着交流记录的心态,写的不好之处,不撕逼,但是欢迎指点。

如果喜欢本文的话,可以关注一下我刚开的订阅号,漫漫技术路,期待未来共同学习成长。

以上2018.5.12

参考资料:

Element.scrollIntoView()

移动前端手机输入法自带emoji表情字符处理

white-space

String.prototype.trim()

Vue按键修饰符

keyCode键码值表

查看原文

赞 80 收藏 225 评论 3

徒步上山看日出 关注了专栏 · 2018-01-28

每天一点canvas动画

编码需要乐趣

关注 1015

徒步上山看日出 发布了文章 · 2018-01-25

css动画 --- transition

前言

页面效果是很重要的一环,好的页面效果,可以让用户感觉很舒服,进而能吸引更多的用户。
既然是页面效果,那么动画肯定不可或缺,那么这篇先说下 transition 这个 css3 属性。

初探 transition

transitions 提供了一种在更改 CSS 属性时控制动画速度的方法。其可以让属性变化成为一个持续一段时间的过程,而不是立即生效 的。(摘自MDN

用法

  • 其写法(为了兼容,要加厂商前缀,这里不写咯):

    transition: <property> <duration> <timing-function> <delay>
  • 也可以用逗号隔开,多写几个作用于不用的 property(属性)

    transition: <property> <duration> <timing-function> <delay>, <property> <duration> <timing-function> <delay>

各项作用

@默认值作用
propertynone / all / propertyall作用于需要过渡的属性
durationtime0过渡所需时间
timing-functionlinear / ease / ease-in / ease-out / ease-in-out / cubic-bezier(n,n,n,n)ease规定过渡效果的速度曲线
delaytime (同duration)0延迟多久才开始过渡
  1. property:可过渡的 CSS 属性,有些属性则不行。
  2. time:以秒(如1s)或毫秒(如1000)计。
  3. cubic-bezier(x1,y1,x2,y2)三阶贝塞尔曲线linearease 等等都是由它计算出来的。x轴 的取值范围:[0,1] 区间内, y轴 可以任意值。可在线自定义

动手使用下

基本操作

// css
#box {
  width: 100px;
  height: 100px;
  background-color: aqua;
  transition: all 1s;
  -webkit-transition: all 1s;
  -moz-transition: all 1s;
  -ms-transition: all 1s;
  -o-transition: all 1s;
}

#box:hover {
  width: 500px;
  margin-top: 100px;
}

// html
<div id="box"></div>

图片描述

  • #boxstyle 里有transition,要触发它,则需要改变上面所说的 property
  • 当鼠标移到 div 时,widthmarin-top 发生了变化,触发了 transition
  • all : 所有可过渡的属性都可以的意思。
  • 1s : 在第二项,即 duration 。过渡所需时间。

简单的类似图片无缝滑动

// css
* {
  margin: 0;
  padding: 0;
}

#box {
  margin: 0 auto;
  width: 100px;
  height: 100px;
  overflow: hidden;
  
}

#img_container {
  position: relative;
  width: 400px;
  transition: all .7s linear;
  -webkit-transition: all .7s linear;
  -moz-transition: all .7s linear;
  -ms-transition: all .7s linear;
  -o-transition: all .7s linear;
}

#img_container span {
  display: inline-block;
  width: 100px;
  height: 100px;
  text-align: center;
  line-height: 100px;
  background-color: yellow;
}

#img_container span:nth-of-type(even) {
  background-color: red;
}

ul {
  text-align: center;
}

li {
  padding: 10px;
  cursor: pointer;
  list-style: none;
  display: inline-block;
}

// html
<div id="box">
  <section id="img_container">
     <span>1</span><span>2</span><span>3</span><span>4</span>
  </section>
</div>
<ul>
  <li>图一</li>
  <li>图二</li>
  <li>图三</li>
  <li>图四</li>
</ul>

// js
~function () {
    let lis    = document.getElementsByTagName('li');
    let imgBox = document.getElementById('img_container');
    let width  = +document.querySelector('span').clientWidth;
    
    for (let j = 0;j < lis.length;j++) {
      
      lis[j].addEventListener('click',function() {
        imgBox.style.marginLeft = `-${ width * j }px`;
      });

    }

}()

图片描述

  • 这个 demo 是用 js 改变了 property ,从而触发 transition

滚动到某个位置,动画才出现

// css
* {
  margin: 0;
  padding: 0;
}

body {
  height: 1500px;
}


#box {
  height: 1200px;
  text-align: center;
}

#animation {
  width: 100px;
  height: 100px;
  background-color: red;
  transition: transform 1s cubic-bezier(.1,1.92,.71,.53);
  -webkit-transition: transform 1s cubic-bezier(.1,1.92,.71,.53);
  -moz-transition: transform 1s cubic-bezier(.1,1.92,.71,.53);
  -ms-transition: transform 1s cubic-bezier(.1,1.92,.71,.53);
  -o-transition: transform 1s cubic-bezier(.1,1.92,.71,.53);
}

// html
<div id="box">
  请往下滚动到有动画的地方,或者点击 <a href="#here">这里</a>
</div>
<span id="here"></span>
<div id="animation"></div>

// js 
let animation = function () {
    let animationBox = document.getElementById('animation');

    function show () {
      animationBox.style.transform = 'translateX(600px) scale(3,3) rotate(360deg)';
    }

    function init () {
      animationBox.style.transform = 'translateX(0) scale(1,1) rotate(-360deg)';
    }

    return { show, init };
  }()

  window.onscroll = function () {
    let scrollTop = +window.scrollY;

    if (scrollTop > 650) {
      animation.show();
    } else {
      animation.init();
    }
  }

图片描述

  • 因为主要演示动画,所以没写滚动的节流或防抖了 。
  • 当滚动或点击到动画的位置,动画才开始。
  • css 代码可以看到,transition 要通过 transform 的变化来触发。同时我也用到了 cubic-bezier

最后

这里不深入讲解 cubic-bezier ,因为我看原理也是头晕。。。。
transition 还可以实现很多功能,淡出淡入手风琴效果 等等。等着你们去发现。
其实动画不是特别难的,只要你有颗 骚动(好奇)的心。

查看原文

赞 1 收藏 4 评论 0

徒步上山看日出 关注了用户 · 2018-01-12

司徒正美 @situzhengmei

穿梭于二次元与二进制间的魔法师( ̄(工) ̄) 凸ส้้้้้้้้้้้้้้้้้้้้้้้้้้้้้้้้้้้้้

关注 2145

徒步上山看日出 赞了文章 · 2018-01-12

自动 Import 工具,前端打字员的自我救赎

自动 import 工具

先推荐两个干货,关于正则的
regexrregexper,前者验证正则是否和预期一样,后者以图的形式表达正则,有助于理解天书般的别人写的正则

作为一个前端打字员,一个经常遇到的场景就是在路由文件中引入模块,比如这样

Screenshot_2018-01-11 01.02.52_b1KSEv

router/index.js 中写入

import Vue from 'vue'
import Router from 'vue-router'

const About = () => import('../pages/About.vue')
const Home = () => import('../pages/Home.vue')

Vue.use(Router)
...

如果修改了模块的名字,增加了模块或者删除了模块,就需要重新修改这个路由文件

总是做这么机械的事情无异于消耗我这个前端打字员的寿命

不能忍,遂写个工具

整理思路如下

Screenshot_2018-01-11 01.01.36_00Cube

其中,监视目录下文件的变动依靠的是 node API 中fs.watch(filename[, options][, listener])

替换目标文件中引入模块的部分,则是通过正则来实现

在这里五星推荐一个验证正则是否正确的网站,regexr

代码实现

监视包含模块的目录

fs.watch(dir, {
  recursive: true // 目录下子目录也被监视
}, (event, filename) => { 
// event 是文件变动的类型,添加文件、删除文件和修改文件名都是'rename' 事件
// filename 是变化的文件或目录
  if(event === 'rename'){ // 判断文件变动类型
    
  }
})

当发生rename事件后,需要重新获得目录下(from)所有的模块,包括模块名moduleName,模块文件相对于引用模块文件(to)的相对路径modulePath,将它们存入变量modules

实际项目中,模块通常都是.vue文件,或者.jsx文件,因此只将这些作为模块,在路由文件中引用

另外有些模块文件因为各种原因,希望人工引入,而不被watch,这样的文件存入excludeArr

const _ = require('lodash')
let excludeArr = [...]
let modules = []
let extname = '.vue'
let from = './src/pages'
let to = './src/router/index.js"'

const mapDir = d => {
    // 获得当前文件夹下的所有的文件夹和文件
    const [dirs, files] = _(fs.readdirSync(d)).partition(p =>
        fs.statSync(path.join(d, p)).isDirectory()
    )

    // 映射文件夹
    dirs.forEach(dir => {
        modules.concat(mapDir(path.join(d, dir)))
    })

    // 映射文件
    files.forEach(file => {
        // 文件后缀名
        let filename = path.join(d, file)
        if (path.extname(file) === extname) {
            if (!excludeArr.includes(path.resolve(__dirname, filename))) {
                let moduleName = path.basename(file, extname)
                // 若存在 -
                if (moduleName.match('-')) {
                    moduleName = moduleName.replace(
                        /(-)(.{1})/,
                        (match, p1, p2, offset, string) => p2.toUpperCase()
                    )
                }
                modules.push({
                    moduleName,
                    modulePath: path.relative(path.dirname(to), filename)
                })
            }
        }
    })
}

生成好新的待引入的模块后,接下来就是在路由文件中,将对应的内容替换掉

所以需要读写文件以及正则替换

                const regex = /\/\*\sautoImport(.*\n)*\/\*\sautoImport\s\*\//g
                let importStr = ''
                modules.forEach((m, index) => {
                    importStr =
                        importStr +
                        fillTemplate(template, m.moduleName, m.modulePath) +
                        (cache.length - 1 === index ? '' : '\n')
                })
                fs.readFile(to, 'utf8', (err, data) => {
                    if (err) return console.log(err)
                    let result = ''
                    if (data.match(regex)) {
                        result = data.replace(
                            regex,
                            `/* autoImport */
${importStr}
/* autoImport */`
                        )
                    } else {
                        /* 首次插入在文件最后的import插入 */
                        result = data.replace(
                            /(.*import.*)(\n)([^(import)])/,
                            (match, p1, p2, p3, offset, string) => {
                                return `${p1}
/* autoImport */
${importStr}
/* autoImport */
${p3}`
                            }
                        )
                    }

                    fs.writeFile(to, result, 'utf8', err => {
                        if (err) return console.log(err)
                    })
                })

其中/\/\*\sautoImport(.*\n)*\/\*\sautoImport\s\*\//g是用于匹配两段注释/* autoImport */及其中间的内容

import Vue from 'vue'
import Router from 'vue-router'
/* autoImport */
const About = () => import('../pages/About.vue')
const Home = () => import('../pages/Home.vue')
/* autoImport */

Vue.use(Router)

当第一次使用,没有/* autoImport */时,就需要在最后一个import后面,插入引入的模块

data.replace(
                            /(.*import.*)(\n)([^(import)])/,
                            (match, p1, p2, p3, offset, string) => {
                                return `${p1}
/* autoImport */
${importStr}
/* autoImport */
${p3}`

在这里还可以自定义了引入模块的方式,例如懒加载,"const moduleName = () => import(modulePath)"

const template = "const moduleName = () => import(modulePath)"
const fillTemplate = (template, moduleName, modulePath) =>
    template
        .replace('moduleName', moduleName)
        .replace('modulePath', `'${modulePath}'`)

为了工具的灵活性,把可配置项写成json文件的形式

{
    "extname": ".vue",
    "from": "src/pages",
    "to": "src/router/index.js",
    "template": "const moduleName = () => import(modulePath)",
    "exclude": [
        "./src/pages/login.vue",
        "./src/pages/404.vue",
        "./src/pages/overall/**",
        "./src/pages/account-result/**"
    ]
}

然后通过以下的方式来获得

const config = fs.readFileSync('./autoImport.json')
const { extname, from, to, template, exclude } = JSON.parse(config)

后记

下一步准备把这个工具写成webpack的插件,名字我都起好了,AutoImportPlugin,先在github上占了个坑,顺手给颗星,不用改Bug

同时准备用更加成熟的模块chokidar来代替原生的watch

工具有问题提issue啊

查看原文

赞 8 收藏 13 评论 6

徒步上山看日出 赞了回答 · 2018-01-10

解决vue中使用iview的table表格让显示的数据部分显示为星号?158*****496怎么设置

使用Vue的过滤器(filters)
我给你直接写了一个例子,你直接复制粘贴到一个HTML文件中打开就能看到效果,希望能帮助到你:

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>例子</title>
    <script data-original="https://cdn.jsdelivr.net/npm/vue"></script>
</head>

<body>
    <div id="app">
        {{ message | protect }}
    </div>
    <script>
        var app = new Vue({
            el: '#app',
            data: {
                message: '15761695277'
            },
            filters: {
                protect: function(value) {
                    if (!value) {
                        return ''
                    }
                    //对字符串进行截取
                    //substr(0, 3)表示从0开始取三位
                    //substr(-3)表示从后面数第三位开始取到最后
                    // *号随便加 多长都行
                    return value.substr(0, 3) + "*" + value.substr(-3);
                }
            }
        })
    </script>
</body>

</html>

关注 5 回答 3

徒步上山看日出 赞了文章 · 2018-01-10

你真的会在async/await中捕获异常吗?

原文链接:Catching without Awaiting

当执行一项需要等待一段时间才能返回的任务时,如果使用async/await,就显得比较麻烦了。如果async方法还没有得到返回值,我们就捕获不到其中的异常。

在我的上一篇文章Learn to Throw Again中写到,当使用async/await时,如何同时捕获到回调函数和throw抛出的错误。在这篇文章中,我们将讨论如何在“后台”中执行异步操作并捕获异常(这里使用双引号,因为在单线程平台上没有真正的后台操作)

从回调函数的模式开始,思考下列代码:

function email(user, message, callback) {
  if (!user) {
    // 抛出异常
    throw new Error('Invlid user');
  }
  if (!user.address) {
    // 回调函数,可能抛出异常
    return callback();
  }
  // 异步的
  return mailer.send(user.address, message, callback);
}

上述代码遵循典型的throw-on-bad-input / callback-asynchronous-errors模式(一旦程序接收到错误的输入,异步抛出异常),如果我们想要发出一封邮件,我们这样调用:

email(user, message, () => {});

对于非法的输入,调用这个函数依旧可能抛出异常。但是,如果电子邮件在传输中产生错误,这个函数调用时会忽略异步抛出的错误。

我们把它改为Promise的版本:

function email(user, message) {
  if (!user) {
    throw new Error('Invlid user');
  }
  if (!user.address) {
    return Promise.resolve();
  }
  return mailer.send(user.address, message); // 函数返回一个Promise
}

这样,对于非法的输入,依旧可以捕获到异常。而对于mailer.send()操作则会返回一个Promise,我们能够轻松地通过Promise.catch()捕获到异常:

email(user, message).catch(() => {});

不管是回调函数还是Promise,他们都是异步的,我们的应用程序都不会因为email发送而被阻塞。

对于async/await的模式,如果在try...catch语句中不使用await关键字,那么try...catch子句不会真正工作。来看下面的async版本:

function email(user, message) {
  if (!user) {
    throw new Error('Invlid user');
  }
  if (!user.address) {
    return;
  }
  return mailer.send(user.address, message); // async function
}

如果我们像这样去调用:

try {
  email(user, message);
} catch (err) {
  Bounce.rethrow(err, 'system');
}

对于非法的输入错误,仍然会正常地抛出异常,这没问题。但是对于任何异步返回的异常,例如在mailer.send()抛出的异常,则会被忽略掉。不管这种错误我们想不想捕获到,反正都是捕获不到的。为了修补这个bug,则要使用await关键字。但是问题来了,这将会导致整个“后台操作”的阻塞。

有一种方案是混用async/awaitPromise

email(user, message).catch(() => {});

但这样的问题在于,对于没有address的用户,这个方法返回的返回值类型并不是Promise,因而其也不会有catch()方法,因此程序会出现TypeError: Cannot read property ‘catch’ of undefined这样的错误。

你可能会尝试直接把email()函数声明为async函数, 并使得它一定会返回一个Promise,但是这并不是一个很好的解决方案,因为async / await其实也只是Promise对象的一层包装。如果不使用await关键字,把一个函数声明为async函数是完全没有必要的。因为async函数总是要通过返回一个Promise,通过next-tick拿到结果,这样会浪费Promise包装和next-tick事件循环机制所造成的性能损耗。

此外,如果要在循环中使用async函数,并且这个循环中执行了很多任务,但是其实很多任务并不是真正意义上异步的,那就没有必要使用async / await,可以参考hapi.js中的checking if you really need to await下列代码判断是否真的需要使用await,这样或许能获得一些性能的提升:

var response = (typeof func === 'function' ? func(this) : this._invoke(func));
if (response && typeof response.then === 'function') { // Skip await if no reason to
  response = await response;
}

判断是否真的需要await,其实就是判断其是否存在then方法,并且then方法是一个函数。因为await的作用其实就是取得一个异步操作的返回结果。

如果你能够保证email方法总是返回一个Promise,我们可以通过更改我们的email()函数来达到这一点,但这样就显得急功近利了!代码显得十分不简洁,而且使用了很不必要的异步操作。在一个完整的async/await函数调用栈中,不需要我们手动构建Promise。对于这个例子来说还好,更重要的是,我们不可能总通过改变email()方法来实现,因为这只是一个例子,在实际运用中,可能email()方法是通过模块引入的。

其中一种解决方案是通过await关键字来调用async函数。通常情况下,在一个函数中使用阻塞操作,如果不等待这个函数执行完成,它不会抛出异常,但是我们可以通过try...catch来包裹:

async function backgroundEmail(user, message) {
  try {
    await email(user, message);
  } catch (err) {
    Bounce.rethrow(err, 'system');
  }
}

然后不通过await调用backgroundEmail

backgroundEmail(user, message);

这样我们不但能够捕获到应用程序的异常,还能够捕获到异步抛出的异常。

为了让异常捕获更加简单,我们使用Bounce模块,它提供了一个background()方法。

Bounce.background(() => email(user, message));

如果我们使用Node.jsAssertionError原型,这样就能够使得Bounce抛出输入异常的错误了。

async/await函数去除了一些同步函数(() => {})的功能,为了达到和普通函数相同的效果,我们不得不写一些额外的代码来实现。但是使用新的工具库,可以很简便地突破这一限制。

查看原文

赞 21 收藏 27 评论 0

徒步上山看日出 赞了文章 · 2018-01-10

2018前端值得关注的技术

1.前言

2017悄然过去,2018已经来到。人在进步,技术在发展。2018年前端有哪些领域,技术值得关注,哪些技术会兴起,哪些技术会没落。下面就我个人的判断进行一个预测判断,希望能对大家起到一个参考作用!下面提及的技术,只是建议大家关注,也不是建议大家全部的都要学,而是建议大家按需学,自己觉得哪些需要学,对哪些有兴趣就学哪些!如果大家有什么工具,框架,库觉得可以推荐的,欢迎在评论区提点,让大家相互进步,学习!

2.PWA

PWA(Progressive Web Apps)由谷歌提出,用前沿的技术开发,让网页使用如同App般的体验的一系列方案。明确的一点就是:PWA就是一个网页, 可以通过前沿的技术开发出一个网页应用。

自从谷歌提出PWA后,就持续的获得了业界的关注,热度可见一斑。就在今年,谷歌也宣布:PWA将获得与安卓原生应用同等的待遇与权限。这就意味着以后的网页基本和APP将越发将近,那么关注度将会进一步的上升。

资料参考:
PWA 入门: 写个非常简单的 PWA 页面
【转载】你的首个 Progressive Web App
【转载】下一代Web应用模型:Progressive Web App

3.typeScript

TypeScript由微软开发。它是JavaScript的一个超集,自由和开源的编程语言。在这个语言中,添加了可选的静态类型和基于类的面向对象编程。由下图说明typeScript和JavaScript的关系!

clipboard.png

(图片来源牧之--从 JavaScript 到 TypeScript

angular已经开始使用typeScript进行开发,react和vue也进一步加深对typeScript的支持。不难发现,typeScript的火热程度!

资料参考:
TypeScript官方文档
TypeScript 资源集
从 JavaScript 到 TypeScript 1 - 什么是 TypeScript(看了第一篇,别落下这个系列的几篇文章)

4.parcel能给webpack带来多大的威胁

webpack大家都知道是JavaScript模块打包工具,简单的来说就是把各个模块就行分析,编译,打包等,使产出的文件可以在浏览器中运行。

clipboard.png

(图片来源于菜鸟教程-Webpack入门教程

webpack的工作虽然是模块打包工具,但也能代替类似gulp等自动构建工具的部分功能!经过2017的发展,webpack的火热程度也是有目共睹。

但是,但是。在2017末就出现了一个黑马:parcel。parcel出乎了大多数人的意料,也算是2017的最大惊喜之一。说到parcel的最大优势,貌似就是webpack的最大劣势:配置和性能!parcel号称零配置,多核打包,并且使用文件缓存,在时间上比webpack快了将近10倍!

clipboard.png

(图片来源于neal的文章-宣布 Parcel:一个快速,零配置的 Web 应用打包工具

从star上面而言,Parcel的关注度似乎超过了当时了webpack,热度仍在持续。

webpack难用之处,我觉得就是配置繁琐,且文档不完善,看着也懵逼。至于打包时间方面,只能说没有对比就没有伤害吧。如果Parcel能做好这几点,说不准能从webpack里面分到不少肉。

宣布 Parcel:一个快速,零配置的 Web 应用打包工具
Parcel Vs Webpack

5.WebAssembly

由谷歌, 微软, Mozilla,苹果等公司合作的一个面向Web的通用二进制和文本格式的项目。

引用腾讯IVWEB团队的说法:WebAssembly是一种新的字节码格式。它的缩写是".wasm",.wasm 为文件名后缀,是一种新的底层安全的二进制语法。。它被定义为“精简、加载时间短的格式和执行模型”,并且被设计为Web 多编程语言目标文件格式。这意味着浏览器端的性能会得到极大提升,它也使得我们能够实现一个底层构建模块的集合,例如,强类型和块级作用域。

WebAssembly刚出来的时候,甚至有开发者猜想,以后会不会是WebAssembly代替JavaScript。在这里,我的感觉就是JavaScript不会被WebAssembly代替,等待没落,而是和WebAssembly共存的关系!2017年,chrome,火狐,IE,Safari四个浏览器统一通过了WebAssembly的方案,这是很少见的情况,我所了解的是第一次出现这样的统一情况,可见四个浏览器厂商对WebAssembly的重视。至于2018年,WebAssembly会有如何的发展,这个难说,初步预测应该还是普及推广,但是还没有到普及开发使用的阶段。但是无论如果,这个都值得关注!

来谈谈 WebAssembly 是个啥?为何说它会影响每一个 Web 开发者?
WebAssembly 实践:如何写代码

6.react,angular,vue三驾马车

2017年,react发展的迅猛,vue更是扮演框架黑马的角色,而angular虽然关注度不如以前,但是不容忽视!在2017的调查报告里面可以看到,趋势基本上是react已经占据主流,不使用框架位居第二,angular1,angular2分列三四。

clipboard.png

中国的情况就是,react第一,vue第二

clipboard.png

vue在2017年很火,但在2018年vue的潜力不容小觑如下图(有1.2W人想使用vue)。虽然超过react的可能性不是很大,但是位置依然会提升

clipboard.png

参考资料
2017JavaScript调查报告
前端领域2017年有哪些变化,2018年又有怎样的期待?
2017 前端大事件和趋势回顾,2018 何去何从?
2017 JavaScript 现状报告:询问了23000名开发者,他们给出了这样的答案
2018 年最值得关注的 JavaScript 趋势

无论如何,框架这个我觉得没有最好,只有最适合。三驾马车也没有说一定要全部都要会!一下就是,如果js基础好,学习框架会比较容易上手。如果极端得不学js,直接上手框架,会很吃力,很容易懵。

7.人工智能和大数据

人工智能和大数据,不是一门技术,而是一个领域,最近两年都很火,也不止于前端。我也觉得是互联网下一波的红利。非常值得关注与学习!这也是一个流行的趋势,因此一些数据可视化的工具(echart,D3等)和人工智能的库都得以收到关注!

8.yarn VS npm

相信接触到前端工程化,模块化的开发者都不可避免的使用npm进行功能包的安装依赖。尤其是在node.js的初期,npm就是工程化的一个标配。但是2017年,npm的地位显然是收到了yarn的威胁!今年的调查结果,yarn还超越了npm。yarn的优势在于:快,安全,和一些感人细节!如果照着情况下去,差距会逐渐变大!

clipboard.png

参考资料
yarn, 不是又一个 npm 第三方客户端
Yarn vs npm: 你需要知道的一切

9.css in js依然备受争议?

前端领域,一向是推荐结构层(html),表现层(css),行为层(javascript)分离。但是在react出来之后,这个准则就貌似被推翻了!因为react的组件结构,要求把html,css,javascript写在一起。很多开发者对css in js不适应甚至反对。那么在新的2018年,是否还是继续的争议下去?

参考资料
CSS in JS 简介
精读《请停止 css-in-js 的行为》
大家对CSS in JS怎么看?
不要再在JavaScript中写 CSS了

10.flex和grid布局更加流行

以前前端页面布局的时候,inline-block,float,postion布局等。但是有了flex和grid,布局变得更加的简单。

首先flex基本已经被所有的浏览器支持的,其方便的特性也受到了很多开发者的热捧!

clipboard.png

(图片来源于阮一峰的网络日记--Flex 布局教程:语法篇

而grid,网格布局号称是下一代的布局方式,但是浏览器兼容方面就没有flex那么好。但是这个也是有必要了解的。毕竟已经被W3C纳入标准了。兼容性也是进一步增强。

clipboard.png

参考资料

Flex 布局教程:语法篇
Flex 布局教程:实例篇
CSS Grid布局:什么是网格布局(看完这篇,建议连着看下面的几篇,都是大漠写的一个系列的文章,质量非常高)
CSS Grid VS Flexbox:实例对比

11.rxjs

rxjs我有稍微了解一下,但是还没用上,可能是大家对这个也是比较陌生,但是我在这里提出来,是因为觉得rxjs还是值得推荐的。引用官网的说法:RxJS 是使用 Observables 的响应式编程的库,它使编写异步或基于回调的代码更容易。这个项目是 Reactive-Extensions/RxJS(RxJS 4) 的重写,具有更好的性能、更好的模块性、更好的可调试调用堆栈,同时保持大部分向后兼容,只有一些破坏性的变更(breaking changes)是为了减少外层的 API 。

参考资料
rxjs中文文档
rxjs简单入门
通俗的方式理解RxJS

12.其它方面

受限于篇幅,还有几个我也认为是可以关注的简单说下。如下

小程序

首先微信小程序,自打微信小程序一出来,很多前端就吐槽。跟别人群聊的时候,很多人也提到:小程序一出来,我就认为是没前途的玩意。或者就是:灭绝APP不可能,小程序只是一个阉割版的APP,竞争厂商也不会开发小程序。还有就是:学习这个还不如关注了PWA,那个比这个更加有意义。谈到的话语大概就是这个意思,2017年,我也是比较介意开发小程序,以至于2017我压根就学过小程序。但是就在17年第四季度还是年末,微信先后宣布可以内嵌html5页面,也可以开发小游戏。让我看过了小程序又有了刚发布的热度。在18年,小程序发展如何,我是否会接触和学习开发小程序,走着瞧。但是无论都值得关注。

其次是支付宝的小程序,虽然感觉没什么新闻,但是毕竟是大厂的玩意。关注是值得关注的。至于学不学,开发不开发,另一回事!

Electron

号称开发桌面应用的一大神器。也尝试了几个官方的实例,运行很流畅,只是因为暂时不开发桌面应用,所以没怎么关注,也没学习过。2018年依然关注,甚至会学习开发!

WebVR 与 WebAR

看了所谓的相关实例,那些VR和AR的效果并不能引起我的兴趣,虽然那些实例,我也没了解过时怎么开发的。但是,对于这一块,关注还是值得的。毕竟这也算是WebVR和WebAR刚起步而已。

13.哪些技术会没落/下滑

angular

前面还说到angular在前端框架里面还有很大的使用率。谷歌方面也是出到了5.x,居然在这里会出现?这里只是一个小小预测而已。2017讨论angular的情况已经是比较少了,在2018年里面angular的使用率觉得会继续下滑,但不会没落,并且在前端框架里面依然有很大的一个地位。18年,angular还是会和react和vue齐名的三大框架,只是使用者不如react和vue那样多。

jquery

jquery在2018年也不会没落,只是使用率还是会进一步的减少。说到jquery,还真是一个时代的转变,在我刚接触前端的时候,jquery打天下。那个时候相对于其他的库和框架,jquery就是一个巨无霸,使用率遥遥领先。就连微软是在.net平台上支持了jquery。在刚工作的时候,潜规则就是:不会jquery,没人承认你是前端。可见当时的jquery的地位。也相信很多人对有jquery情怀。只是技术不讲情怀!

es5以下版本语法

本段内容提及的es6代表es6以上的语法,包括es7,es8。es5代表es5以下的语法,包括es3

es6经过两年的发展,方便和实用性得到了众多开发者的欢迎。2017应该是es6语法的使用比率首次超es5,在2018年,es6语法使用比率会继续的升高。而es5等语法的使用比率会继续的下滑。即使就目前开发而言,还是要使用babel把es6的语法编译成es5。

clipboard.png

clipboard.png

clipboard.png

grunt

在gulp发布时,grunt的地位就已经很受影响了,现在又有打包工具代替了自动构建工具的部分工具,就显得自动构建工具的作用不如以前了,更别说市场有一个更好的构建工具了--gulp。

sea.js

sea.js由国人开发,当时使用的时候还满心欢喜,终于有国人的东西登上舞台了。sea.js凭借简单,轻量等优势火极一时。但是有了es6的模块化之后,就连sea.js的作者玉伯也在微博发言:应该给 Sea.js 和 KISSY 也树一块墓碑了。

14.小结

好了,关于2018的的个人对于前端这一方面的预测和一些推荐关注的就写到这里了!虽然写了这么多,但是我并不是说所有都要学,要会,而是建议大家关注,学习哪些技术看自己所需,看自己兴趣。文章提及的也只是我平常关注的,有些只是我也是只是了解,但并没有深入学或者使用。也觉得在前端这一方面,没有什么最好的工具,框架,库等,只有最适合自己的。

最后,如果大家有什么工具,框架,库或者其它的技术,欢迎在评论区内指点提出。让大家相互学习,相互进步!

-------------------------华丽的分割线--------------------
想了解更多,关注关注我的微信公众号:守候书阁

clipboard.png

查看原文

赞 86 收藏 293 评论 3

认证与成就

  • 获得 121 次点赞
  • 获得 6 枚徽章 获得 0 枚金徽章, 获得 0 枚银徽章, 获得 6 枚铜徽章

擅长技能
编辑

开源项目 & 著作
编辑

(゚∀゚ )
暂时没有

注册于 2017-05-18
个人主页被 1.2k 人浏览