CodingVue中全局使用Lodash的Debounce防抖函数、Throttle节流函数及其他

之前的项目中,虽然从没有用过Lodash,但也是久仰大名。现在的项目中,希望能够进行实时查询,所以Debounce防抖和Throttle节流函数就必不可少了。

之前并没有用过防抖和节流,但是之前看过掘金的一些文章,而且由于之前有游戏开发经验,所以对于这种逻辑还是非常熟悉的,只是JS中的我们是使用闭包来解决这个问题。代码其实很简单,这里不再贴出代码。这篇文章我们主要还是解决Vue中使用Lodash的问题。

如果是JS代码的话,解决的方法比较多,但是TS的话,会有一些定义问题。

一、使用VuePlugin方式

Vue的插件写起来是很容易,使用Lodash的大部分函数也很方便,但是有一个问题,就是无法使用_.debounce_.throttle函数,因为我们使用的时候使用的是this._这种方式,但是在methods中,我们无法直接使用this的方法来进行赋值,代码如下:

// Vue插件代码
import _Vue from "vue";
import * as lodash from "lodash";
function install(Vue: typeof _Vue, options?: any): void {
    Vue._ = lodash;
    Vue.prototype._ = lodash;
}
export default install;

// 在created中定义的方法,可以用作event,JS中可直接使用,TS中也可以使用,但由于无法对this进行定义,所以会有波浪线
created() {
    this.test = this._.debounce(function(){},1000);
}

// 在这种methods方式下可以用使用lodash的一些方法
methods: {
    test() {
        let _d = this._.debounce(function(){},1000);
    }
}
// global.d.ts
import Vue from "vue";
import * as lodash from "lodash";

declare module "vue/types/vue" {
    // Vue.prototype._
    interface Vue {
        _: lodash.LoDashStatic;
    }
    // Vue._
    interface VueConstructor<V extends Vue = Vue> {
        _: typeof lodash.LoDashStatic;
    }
}

二、使用WebpackProvidePlugin来进行

ProvidePluginWebpack用来自动加载模块的方法,之所以会研究全局使用方式的问题,就是因为我懒得每个vue组件中都import lodash

// webpack的配置,在VUE-CLI 3中,我们写在vue.config.js中
plugins: [
    new webpack.ProvidePlugin({
        _: "lodash"
    })
]
// vue组件中
methods: {
    delay_search: _.debounce(function(this: any) {
        this.other_function();
    }, 500),
}
// global.d.ts
import * as lodash from "lodash";
declare global {
    const _: lodash.LoDashStatic;
}

CodingJavascript中的数字类型转换:parseInt和ToNumber

前言

今天面试一位前端的时候,问到他一个问题:一个数字和一个布尔型用==比较时,类型会如何转换。开始他答的:数字会转换成布尔型,之后改成了:布尔会转换成数字型。我一个迷糊,自己也想不起来了,说是数字会转成布尔。完了之后,我查了下ECMA5.1文档,里面说到是布尔转数字。

于是由这个问题,又引出了我另一个想问但由于自己记不清所以没敢问的问题,字符串和布尔进行比较,类型如何转换?

在文档中确认了一下,是布尔先转成数字,之后字符串也转换成数字进行比较

问题

我记得之前的记忆中,一个字符串转换成数字,是从第一位开始检查的,将可以转换成数字的位置转完,舍弃之后无法转换的。例如:

123abc -> 123

abc123 -> NaN

于是我很自信的认为,console.log('1true' == true);的结果是true,但我运行代码后,发现竟然是false。于是我整个人就懵逼了,难道1true的结果不是1

解决

查看文档后,发现所有数字的隐式类型转换都是用的ToNumber,而我之前记得从第一位开始转的是parseInt

当使用Number创建一个数字时,会对参数进行ToNumber转换,于是我使用console.log(Number('1true'));查看输出,结果是NaN,而parseInt1,而console.log(parseInt('1true') == true);结果也是true

查看parseInt文档后,描述是:如果第一个字符不能被转换成数字,parseInt返回NaN

而Number文档是:如果参数无法被转换为数字,则返回 NaN

所以,这就是我今天说错一个问题后的提升。

CodingUnity中接入ShareSDK和ShareREC的教程及常见问题

前言

最近在做的一个AR项目中,接入了ShareSDK和ShareREC,在Android平台基本很顺利,除了AndroidManifest.xml文件部分冲突,以及部分文件重复,都可以很轻松的解决。但在iOS平台接入过程中遇到了许多问题,这里对遇到的问题进行总结并分享。我们先看Android平台的问题,之后再解决iOS平台的问题。解决iOS端的问题是,我会假设你已经完全接好了Android平台,并且Android平台已经能够打包且运行了,这样就可以确保我们需要解决的只是iOS打包时产生的问题。


Android平台常见问题

安卓平台接入时遇到的问题:

The namespace `global::’ already contains a definition for `MiniJSON’

这个问题是由于ShareSDK和ShareREC中都包含了MiniJSON.cs这个JSON操作库,删掉其中一个可以解决。

AndroidManifest.xml文件冲突

Error: [Temp\StagingArea\AndroidManifest-main.xml, E:\Workspace\MobSDK\Temp\StagingArea\android-libraries\ShareSDK\AndroidManifest.xml:19] Trying to merge incompatible /manifest/application/activity[@name=com.mob.tools.MobUIShell] element:

UnityEditor.HostView:OnGUI()

这个问题是因为ShareSDK和ShareREC的两份AndroidManifest.xml文件中,都有android:name="com.mob.tools.MobUIShell"的Activity,我们只需要删除掉ShareREC文件夹里AndroidManifest.xml中的这个Activity即可解决。

Jar包重复冲突

Uncaught translation error: java.lang.IllegalArgumentException: already added: Lcom/mob/MobApplication;
Uncaught translation error: java.lang.IllegalArgumentException: already added: Lcom/mob/MobSDK;
Uncaught translation error: java.lang.IllegalArgumentException: already added: Lcom/mob/MobSDK$1;
Uncaught translation error: java.lang.IllegalArgumentException: already added: Lcom/mob/MobSDKLog$1;
Uncaught translation error: java.lang.IllegalArgumentException: already added: Lcom/mob/a;
Uncaught translation error: java.lang.IllegalArgumentException: already added: Lcom/mob/commons/LockAction;
Uncaught translation error: java.lang.IllegalArgumentException: already added: Lcom/mob/commons/MobProduct;
Uncaught translation error: java.lang.IllegalArgumentException: already added: Lcom/mob/commons/MobProductCollector;
Uncaught translation error: java.lang.IllegalArgumentException: already added: Lcom/mob/commons/a;

java.lang.RuntimeException: Translation has been interruptedjava.lang.RuntimeException: Translation has been interrupted
at com.android.dx.command.dexer.Main.processAllFiles(Main.java:613)
at com.android.dx.command.dexer.Main.runMonoDex(Main.java:311)
at com.android.dx.command.dexer.Main.run(Main.java:277)
at com.android.dx.command.dexer.Main.main(Main.java:245)
at com.android.dx.command.Main.main(Main.java:106)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498) at SDKMain.main(SDKMain.java:130)

这个问题也是由于ShareSDK和ShareREC中存在相同的Jar包导致的(ShareREC中自带了一部分的分享SDK),两份Jar包可能版本不一致,我们保留最新版本的即可。所以解决办法是:删除Assets\Plugins\Android\ShareSDK\libs或Assets\Plugins\Android\ShareREC\libs文件夹中版本较旧的Jar包。

打开APP时提示初始化失败,不支持该硬件

Failure to initialize! Your hardware does not support this application, sorry!

遇到这个问题时,我们需要删除掉Assets\Plugins\Android\ShareSDK\libs目录中的部分so库,一般情况下只需要删除mips开头的即可,保留arm和x86开头的。


iOS平台常见问题

从mob官网下载的用于Unity的ShareSDK有2个,一个是:ShareSDKForU3D.unitypackage,一个是ShareSDKForU3DWithEditor.unitypackage,这里我们使用第一个。不是用第二个的原因是,我们之前接入的ShareREC中,有相同的SDK代码(iOSAutoPackage),主要用于打包iOS时自动修改XCode的Info.plist文件。官方文档中有介绍,可以解压SDK.zip文件,来合并需要打包的SDK文件,我尝试后出现了新的错误,所以并没有使用这种方法。我们会单独下载iOS平台的ShareSDK并加入到XCode工程中。

 

Codingaxios使用x-www-form-urlencoded方式传递参数无法接收的问题

之前的项目中,一直使用的jQuery来请求ajax数据。直到使用Vue框架,才开始使用axios这个库,作为一个官方推荐的库,觉得它应该有过人之处。

可是在刚开始使用中就遇到了一个问题:axios提交的数据,后台接收不到。

之前使用jQuery时,并没有问题,也从没有注意过Request Headers里的内容,这次遇到这个问题时,才开始注意到Request Headers里面的内容。

这个问题在于,jQuery请求数据时,传入JSON对象类型的参数,jQuery会自动转成querystring形式的,但axios并不会转成querystring类型,而是序列化成json字符串进行传递,所以axios请求时使用默认方式传递参数的话,服务端不做处理的话取不到数据。

JSON对象转为querystring有三种方法:

1.浏览器中

var params = new URLSearchParams();
params.append('param1', 'value1');
params.append('param2', 'value2');
axios.post('/foo', params);
2.浏览器中
var qs = require('qs');
axios.post('/foo', qs.stringify({ 'bar': 123 }));
3.nodejs中
var querystring = require('querystring');
axios.post('http://something.com/', querystring.stringify({ foo: 'bar' }));

Coding【PHP】将数据导出为可以用Excel打开的一个CSV文件

在一些小型项目中,后台页面或许并不会有太多的功能。但是统计基本是所有客户必不可少的需求之一,虽然我们可以通过各种JS来实现图标效果,但个人觉得导出Excel是更加合适的方式,而最方便导出的格式莫过于CSV了。

CSV是很简单的文本格式,通过逗号 ,换行 \r\n 进行分隔,而且可以通过Excel打开,也可以很方便的转换为Excel格式。

$csv = fopen('user.csv','w');
$text = "昵称,性别,年龄\n";
foreach ($data['user'] as $row) {
    $nt = $row['name'].','.$row['sex'].','.$row['age']."\n";
    $text .= $nt;
}
//$text = utf8_encode($text);
$text = iconv('UTF-8','GBK',$text);
//echo $text;
fwrite($csv,$text);
fclose($csv);

在最开始导出CSV文件时,发现Excel打开后显示乱码,后得知必须转成GBK才能正常显示中文,所以就有了iconv转换编码类型的一行。之后保存在本地后,供用户下载。

Coding【PHP】将Excel数据导入到MySQL数据库中

在一些项目中,经常会需要将Excel文件导入到Mysql或其他数据库中,我们这里使用PHPExcel来进行导入。

PHPExcel是一个使用PHP语言开发的Excel操作类,拥有读取、写入等强大功能。

我们可以读取服务器上的文件进行导入,也可以通过上传进行导入。

首先进行文件上传:

$file = '/tmp/excel.xlsx';
move_uploaded_file($_FILES["file"]["tmp_name"], $file);

之后我们需要进行一些设置,防止导入大文件时超时等:

ini_set("memory_limit","-1");
set_time_limit(1800);

在PHPExcel中,可以设置缓存已节省内存开始,但会影响读取与写入单元格的速度(官方文档中有此说明)。如果你的内存勾搭,获取并不需要进行缓存设置,但对于小内存的服务器,这是很有必要的。PHPExcel有多种缓存机制,我们使用gzip压缩方式来使用:

$cacheMethod = PHPExcel_CachedObjectStorageFactory::cache_in_memory_gzip;
if (!PHPExcel_Settings::setCacheStorageMethod($cacheMethod)) {
    die($cacheMethod . " caching method is not available" . EOL);
}

官方文档中介绍,在脚本执行完成之前,已初始化过的缓存设置将不能被更改,会一直使用到脚本执行结束。

设置好缓存之后,我们就可以对Excel文件进行读取了:

$reader = PHPExcel_IOFactory::createReader('Excel2007');
$reader->setReadDataOnly(true);

我们读取的是Excel2007格式的文件,PHPExcel支持多种文件格式,如果需要查看对应格式的使用方式可以去查看官方文档。

设置 setReadDataOnly 有助于提高读取性能。

加载文件,之后获取当前的sheet进行操作:

$excel = $reader->load($file);
$sheet = $excel->getActiveSheet();

你可以通过读取单元格的方式来进行操作,也可以通过数组的方式进行操作。

数组方式:

$sheet->toArray(null,true,true,true);

单元格方式:

$sheet->getCell('A6')->getFormattedValue();

可以读取单元格数据之后,我们就可以将数据写入数据库了。

但这样远没有结束,如果你的Excel文件过大的话,load操作的时间会特别特别长(超强服务器请忽略此句),所以我们需要对数据进行分块处理。

我们可以通过Filter来进行分块操作,我们这里定义一个ReadFilter的过滤类:

class ReadFilter implements PHPExcel_Reader_IReadFilter
{
    private $_startRow = 0;
    private $_endRow = 0;

    public function setRows($startRow, $chunkSize)
    {
        $this->_startRow = $startRow;
        $this->_endRow = $startRow + $chunkSize;
    }

    public function readCell($column, $row, $worksheetName = '')
    {
        if ($row >= $this->_startRow && $row < $this->_endRow) {
            return true;
        }
        return false;
    }
}

我们读取的时候通过setRows来规定要读取的起始行和结束行,在readCell中对数据进行过滤,return false则为忽略此条数据。

我们通过URL参数来规定此页面需要读取哪些数据,读取完之后刷新,进行下一批操作,这个功能可以自行尝试。

我们对Excel的操作基本就可以完成了,其实这应该是一个很简单的功能,因为在我的本地服务器上只写了一遍代码就通过了,但传到在线服务器后,发现在线服务器有各种各样的限制,而我又不能修改这些限制,于是尝试了多种读取及数据库写入方式,今天面对的最多的一句话就是Mysql has gone away,当我无比心酸,但最后筛查后发现是框架里的数据库操作类有问题。    =_=

这篇博客就到这里了。

CodingUEditor扩展:添加新的支持code标签的按钮

在默认的UEditor中,是不能添加code标签的,而作为一名程序员,文章中经常会有代码段和代码字符。代码段可以使用pre标签,但代码字符使用pre就太不方便了,而且现在很多的CSS框架中都对code标签有了默认的样式定义,所以决定在UEditor中加入一个新的按钮,来给选中文字添加code标签。

首先编辑器肯定是UEditor了,我选择的是编译好的版本而非源码,因为UEditor现在基本完善了,改动也不大了,所以这样改起来方便。

需要修改的文件分别是ueditor.all.jsthemes\default\css\ueditor.css,另外需要一张code图标,你可以网上寻找,挑选自己喜欢的。

ueditor.all.js中,我们注册一个按钮:

UE.registerUI('code', function(editor, uiName) {
    var btn = new UE.ui.Button({
        name: "code",
        title: "设为代码",
        cssRules: 'background-position: center center;',
        onclick: function() {
            editor.execCommand('code');
        }
    });
    editor.addListener('selectionchange', function() {
        var state = editor.queryCommandState(uiName);
        if (state == -1) {
            btn.setDisabled(true);
            btn.setChecked(false);
        } else {
            btn.setDisabled(false);
            btn.setChecked(state);
        }
    });
    return btn;
});

上面的代码可以参考官方文档,基本需要改的就是标签名字、标题和onclick事件调用的命令。

之后我们添加code命令,因为code命令和加粗b斜体i是类似的功能,所以我们在加粗和斜体的地方添加code命令。

你可以通过搜索bolditalicsubscript等来找到下面的代码:

var basestyles = {
    'bold':['strong','b'],
    'italic':['em','i'],
    'subscript':['sub'],
    'superscript':['sup'],
    //这一行就是我们添加的code功能,注意上面一行末尾加上 " , "
    'code':['code']
}

这样我们就完成了添加一个按钮的功能,不过现在按钮显示的图标还是默认样式的,我们需要定义自己的图标,添加下面的代码到css文件:

.edui-for-code .edui-box.edui-icon.edui-default {
    background: url(../images/code.png) no-repeat transparent;
    background-size: 15px 15px;
    background-position: center center;
}

并将url中的图片地址改为你的code图片地址,建议保存在images目录下。

完成以上步骤,我们就完成了添加code按钮的功能,接下来只需要压缩jscss文件即可,你可以选择自己喜欢的工具进行压缩,并覆盖同目录下的min.jsmin.css后缀的文件。

Coding使用Javascript进行图片或视频上传前的预览

最近在做的一个APP转H5微信网站的项目,其中有APP中批量上传图片并预览的功能,之前没有做过类似的,于是研究了一下,并总结分享。

首先推荐使用jQuery File Upload插件来处理上传事件。

首先引入jQuery、jQuery File Upload及其依赖项:

<script src="js/jquery-1.11.2.min.js" type="text/javascript"></script>
<script src="plugins/jquery.ui.widget.js"></script>
<script src="plugins/jquery.iframe-transport.js"></script>
<script src="plugins/jquery.fileupload.js"></script>

HTML内容如下:

<input class="fileinput" name="files[]" id="fileupload" type="file" multiple>
<div class="pics">
    <ul>
        <li id="pic_one" class="pic_one" style="display:none;">
            <img src="images/circle-new/tupian.png" alt="">
        </li>
        <li id="uploadicon" onclick="openFileUpload();">
            <img src="images/circle-new/zhengjia.png" alt="">
        </li>
        <div class="clearboth"></div>
    </ul>
</div>

ul列表中第一项是我们要使用的模板,默认不显示。添加图片后,我们会克隆此节点,修改图片地址,并加入列表。

第二项是我们上传图片的按钮,用来替代input标签的,不过在此示例中,我们直接使用input来上传。

$(function () {
    'use strict';
    // 服务器地址
    var url = 'server/php/';
    $('#fileupload').fileupload({
        url: url,
        dataType: 'json',
        add: function (e, data) {
            var f = data.files[0];
            var src = window.URL.createObjectURL(f);
            var node = $("#pic_one").clone(true).show();
            node.children("img").attr("src",src);
            $("#uploadicon").before(node);
        }
    }).prop('disabled', !$.support.fileInput)
        .parent().addClass($.support.fileInput ? undefined : 'disabled');
});

我们在fileupload事件中进行处理,add为当使用input选择完图片后的操作,我们先获取选择的文件,之后创建对象URL,你会得到一个blob协议的URL地址,之后便可以克隆模板节点并修改图片地址了,再之后加入ul就可以了。

我们不能使用file协议的URL地址来进行图片预览,因为移动端并不支持。

视频方面,和上面的方法是一致的,只不过视频使用的是video标签,并且当选中视频文件后,需要给video标签添加controls属性,来方便对视频进行预览。

add事件会自动识别选中的文件数量并进行遍历,这都是jQuery File Upload插件带来的便利。

上面的代码中我们只对add事件做了处理,你还可以处理上传完成done事件、上传中progress事件等。

CodingSemantic UI中Sidebar边栏禁止自动隐藏

最近学习了Semantic UI,准备使用这套响应式框架改版博客,但研究之后发现改动量过大,而且这套框架的css和js加起来有1M,过于庞大,使用components方式又过于繁琐,很多组件的样式涉及到多个文件,于是最终放弃了。

期间研究了一下Sidebar的使用方法,个人并不喜欢自动隐藏方式,而是点一下出现,再点一下收回,分享一下解决方案。

首先是引入Semantic UI框架文件:

<script src="jQuery.js"></script>
<link rel="stylesheet" href="semantic.css">
<script src="semantic.js"></script>

然后是我们的HTML:

<div class="ui left sidebar inverted vertical visible uncover menu">
    <a class="item"><i class="home icon"></i> Home </a>
    <a class="item"><i class="block layout icon"></i> Topics </a>
    <a class="item"><i class="smile icon"></i> Friends </a>
    <a class="item"><i class="calendar icon"></i> History </a>
</div>
<div class="pusher">
    <button class="ui primary button" onclick="toggle()">
        toggle
    </button>
</div>

之后你应该看到如下的页面:

QQ截图20160718192749.png

现在我们需要在JS中控制它的显示与隐藏了:

function toggle() {
    $('.ui.sidebar').sidebar({
        context: 'body',
        dimPage : false,
        onVisible: function() {
            $('body').click(function(e){
                this.unbind(e);
            });
        },
        onShow: function() {
            $('.ui.sidebar').css("z-index",999);
        },
        onHide: function() {
            $('.ui.sidebar').css("z-index",1);
        }
    }).sidebar('toggle');
}

第一个 sidebar() 中进行了一些设置:

context:需要显示 sidebar 的容器的选择器

dimPage:sidebar 显示后是否对页面进行遮罩处理

onVisible:这里在 sidebar 开始显示后,取消 body 标签的 click 事件绑定,这样点击页面的任何区域后 sidebar 就不会自动隐藏了。

onShow:处理阴影效果的 z-index。

onHide:处理阴影效果的 z-index。

第二个 sidebar() 就是对侧边栏的切换操作了。

现在当点击 toggle 按钮后,点击页面其他区域 sidebar 就不会自动隐藏了。

CodingXamarin.Forms开发中的一些坑及解决办法

最近在学习使用Xamarin进行跨平台应用开发,遇到了不少问题,于是记录下来,方便以后回顾,并希望能够给大家带来帮助。

使用 Xamarin.Forms 首先遇到的问题是:

Unzipping failed. Please download https://dl-ssl.google.com/android/repository/android_m2repository_r22.zip and extract it to the C:\Users\用户名\AppData\Local\Xamarin\Xamarin.Android.Support.v7.MediaRouter\23.0.1.3\content directory.

你可能会看到很多航这样的问题。这个问题要解决其实很简单,首先你的电脑需要科学上网。

在成功科学上网之后,清理解决方案。

之后build,如果成功,那么恭喜你。如果不成功,检查

C:\Users\用户名\AppData\Local\Xamarin\zips

文件夹,看是否存在zip文件,存在的话清空,之后清理解决方案,然后build。一般情况下这样就可以了,如果不可以,那么我暂时也没有解决办法。

之后可能遇到的问题是:

Resource does not contain a definition for Animation

这个问题也很简单,只需要将安卓项目 Resources 文件夹下的 Resource.Designer.cs 文件从项目移除就可以了。如果依然存在问题,那么删除文件,清理解决方案,之后build,文件会自动重新生成。

剩下的问题可能就是:

Xamarin.Android.Support.v4
Xamarin.Android.Support.v7.AppCompat
......

等一系列包的问题,提示你安装包。你需要打开

工具 -> NuGet包管理器 -> 管理解决方案的NuGet程序包 -> 更新

之后更新所有可更新的包即可。

以上的问题都是 Xamarin.Forms 跨平台开发时会遇到的问题,包括PCL包和共享包,而单独开发Android程序,则不会遇到这些问题。当然更新NuGet包是必须的。

需要注意的一点是:当你更新NuGet包后,会提示你重启VS,这个时候你需要保存你的项目才能够让包正确的被更新。