h5 录音 自动生成proto Js语句 UglifyJS-- 对你的js做了什么 【原码笔记】-- protobuf.js 与 Long.js 【微信开发】-- 发送模板消息 能编程与会编...

得益于前辈的分享,做了一个h5录音的demo。效果图如下:

h5 录音 自动生成proto Js语句 UglifyJS-- 对你的js做了什么 【原码笔记】-- protobuf.js 与 Long.js 【微信开发】-- 发送模板消息 能编程与会编..._第1张图片

点击开始录音会先弹出确认框:

h5 录音 自动生成proto Js语句 UglifyJS-- 对你的js做了什么 【原码笔记】-- protobuf.js 与 Long.js 【微信开发】-- 发送模板消息 能编程与会编..._第2张图片

首次确认允许后,再次录音不需要再确认,但如果用户点击禁止,则无法录音:

点击发送 将录音内容发送到对话框中。点击即可播放。点击获取录音即可下载最后一次的音频:

播放下载都是围绕blob文件。播放就是让隐藏的audio标签的地址指向内存中的blob:

        this.play = function (audio,blob) {
            blob=blob||this.getBlob().blob;
            audio.src = URL.createObjectURL(blob);  
        };  
createObjectURL 我们在用base64显示图片的时候也可以用到。
 img.src = URL.createObjectURL(blob);

这样比一长串的字符串好看很多。同理如果你想销毁该地址对应的数据而节省内存可以这样:

 URL.revokeObjectURL(img.src);

扯远了点。下载就是模拟a标签的点击。

复制代码
   function downloadRecord(record){
              var save_link = document.createElementNS('http://www.w3.org/1999/xhtml', 'a')
                save_link.href = URL.createObjectURL(record);
                var now=new Date;
                save_link.download = now.Format("yyyyMMddhhmmss");
                fake_click(save_link);
            }

       
            function fake_click(obj) {
            var ev = document.createEvent('MouseEvents');
            ev.initMouseEvent('click', true, false, window, 0, 0, 0, 0, 0, false, false, false, false, 0, null);
            obj.dispatchEvent(ev);
            }
复制代码

每次发送 ,其实是讲音频数据缓存下来,标记下id。下次点击的时候根据id获取缓存的数据,然后叫给audio元素播放:

复制代码
var msg={};
            //发送音频片段
            var msgId=1;
            function send(){
                if(!recorder){
                    showError("请先录音");
                    return;
                }

               var data=recorder.getBlob();
               if(data.duration==0){
                     showError("请先录音");
                    return;
               }
                msg[msgId]=data;
                recorder.clear();
                console.log(data);
                var dur=data.duration/10;
                 var str="
"+dur+"s
" $(".messages").append(str); msgId++; } $(document).on("click",".voiceItem",function(){ var id=$(this)[0].id; var data=msg[id]; playRecord(data.blob); })
复制代码

内部是基于AudioContext实现:兼容性如下,基本上只能在谷歌和火狐浏览器里面玩。很可惜微信和ios目前不支持的。如果电脑没有音频驱动或者没有麦都会报错提示。

h5 录音 自动生成proto Js语句 UglifyJS-- 对你的js做了什么 【原码笔记】-- protobuf.js 与 Long.js 【微信开发】-- 发送模板消息 能编程与会编..._第3张图片 

 有兴趣的朋友可以玩玩。未来移动端支持就更好了。

源码:http://files.cnblogs.com/files/stoneniqiu/Voice.zip

参考博客:

http://blog.csdn.net/bzhou0125/article/details/46444201
http://www.zhangxinxu.com/wordpress/2017/06/html5-web-audio-api-js-ux-voice/
https://developer.mozilla.org/en-US/docs/Web/API/AudioContext
 

自动生成proto Js语句

2017-07-28 20:02 by stoneniqiu, 789 阅读, 0 评论, 收藏, 编辑

在与后端的WebSocket通信时,前端要带一个proto文件是一个累赘的事情。首先是明显的曝光了协议实体对象,再一个浏览器客户端很容易会缓存该文件,新的协议更新可能导致客户端不能使用,另外在cdn服务器上还需要配置.proto类型客户端才能下载过去。真是遗毒不浅,自己使用的时候会注意这些,但给别人使用的时候就很不乐观了,所以这次全部将proto文件转成JavaScript对象,省去协议文件和加载的步骤。

先看代码:

复制代码
 function createProto(name) {
        var args = [].slice.call(arguments, 1);
        var obj = new protobuf.Type(name);
        for (var i = 0; i < args.length; i++) {
            var p = args[i];
            var key = i + 1;
            obj.add(new protobuf.Field(p[0], key, p[1] || "string"));
        }
        return obj;
    }

    function createEnum(name,list) {
        var obj = new protobuf.Enum(name);
        for (var i = 0; i < list.length; i++) {
            obj.add(list[i],i);
        }
        return obj;
    }

    function loadProto(callback) {
         if (typeof protobuf == "undefined") return;//说明浏览器加载失败            root = new protobuf.Root().define("IMEntity");
            root.add(createProto("Token", ["UserID"], ["Token"], ["Device"], ["Version", "int32"], ["Appkey"]));
            root.add(createProto("Feedback", ["ResultCode", "int32"], ["ResultData"], ["Seq", "int32"], ["MsgID"]));
            root.add(createEnum("ReceiptType", ["Receive", "Read"]));
       //...
util.triggerCallback(callback); };
复制代码

proto 主要有两种类型,Type和Enum。Type对应协议中的message,相当于是类。Enum就是枚举类型

复制代码
var Root  = protobuf.Root,
    Type  = protobuf.Type,
    Field = protobuf.Field;

var AwesomeMessage = new Type("AwesomeMessage").add(new Field("awesomeField", 1, "string"));

var root = new Root().define("awesomepackage").add(AwesomeMessage);
复制代码

枚举的创建不要需要Field。只需要add 字段名即可。那么接下来的问题是,手写root.add 也很烦,因为要一个一个对照属性,不断的复制粘贴,很容易出错。所以又做了个自动生成代码的页面:

复制代码
 
    
复制代码

然后页面上会得到:

h5 录音 自动生成proto Js语句 UglifyJS-- 对你的js做了什么 【原码笔记】-- protobuf.js 与 Long.js 【微信开发】-- 发送模板消息 能编程与会编..._第4张图片

红色部分复制到工程里面就可以用了。当然要带上createProto和createEnum两个方法。proto的格式要规范,毕竟start里面是以空格split的。相对于protobuf.load("xx.proto",callback)的方式要好很多。load对位置要求比较死板,一定要在根目录。而且有类型不存在就会报错,终止程序。add方法不存在找不到类型的错误。另外速度也快了很多。 

 

 

UglifyJS-- 对你的js做了什么

2017-07-27 08:38 by stoneniqiu, 1118 阅读, 4 评论, 收藏, 编辑

也不是闲着没事去看压缩代码,但今天调试自己代码的时候发现有点意思。因为是自己写的,虽然压缩了,格式化之后还是很好辨认。当然作为min的首要准则不是可读性,而是精简。那么它会尽量的缩短代码,尽量的保持一行,最大化的减少的空白。我们常用的分号都会被替换成了逗号,短句变成了连贯的长句。

1.立即执行函数

h5 录音 自动生成proto Js语句 UglifyJS-- 对你的js做了什么 【原码笔记】-- protobuf.js 与 Long.js 【微信开发】-- 发送模板消息 能编程与会编..._第5张图片
我本是第二种写法,uglify给我替换成了第一种(当然更短一点)。其实括号和!号的作用都是将funtion的部分转成一个表达式,而不再是申明。这样就能立即执行,同理~   +都可以做到。

2.变量名替换

这个是自然的,函数名、参数名、变量名都替换成了单个字母。甚至是‘_’

3.函数置顶

function foo (){} 这种形式的代码都会被放到模块的最顶端。当然这是一种规范,后来发现还有另外一个作用就是方便后面的代码合并。 比如 我们这样定义:
复制代码
var self=this;
function a(){}
self.a=a;
function b(){}
self.b=b;
return self;
复制代码

会替换成:

function a(){}
function b(){}
var s={}
return  s.a={},s.b={},s

注意到最后的s 不能漏了,return会以最后一个表达式的结果为准。

复制代码
  function rt(n) {
    return n;
   }
  function xx() {
      return rt(1), rt(2);
  }
复制代码

执行xx()得到的是2,如果 rt(2)后面还有个不返回值的函数执行,那么xx()会得到undefined。

4.bool值替换

false-->!1  true-->!0

5.if 

if语句是压缩最多的地方。

1) return 前置:
复制代码
 function load() {
            if (t) {
                x = false;
                log("error");
                return;
            }
            console.log("22")
        }
复制代码

比如我的原函数大概是这样。压缩后成了这样:

  if (t) return x =!1,void log("error")
return提前了,末尾多了一个void。 这是为什么呢。 没有大括号,if的四段代码变成了一句话。void的在这里的作用是抹掉函数的返回值。因为本来的这个if 是没有返回值的 。如果这个时候log方法带有返回值。那么调用load就会拿到这个返回值。这会产生干扰,违背了原函数的本意。所以用void抹掉了。 
2) 短路
复制代码
     function foo() {
            if (!x) {
                return;
            }
            console.log("doA");
            console.log("doB");
  }
复制代码

压缩后:

    function f() {
            x || console.log("doA"), console.log("doB");
      }

这样蛮不错的。同理:

复制代码
if(x&&y){
doa();
dob();
}
doc();
--> x&&y&&(doa(),dob()),doc()
复制代码

原本四行变成了一行代码。

3).为了合并一行,这也行:

    console.log("doA");
    console.log("doB");
     if (x>0) {
         console.log("true");
      }

合并成这样:

 if (console.log("doA"), console.log("doB"), x > 0) console.log("true");

平时这么写可能不太友好,重点是在if语句中,最后一句才是判断句。结合之前的return。想必对逗号语句有了深刻的认识。

4)throw也不放过

 if (errMsg) {
       util.triggerCallback(fail, "模型验证错误");
       throw Error(errMsg);
  }

压缩后:

 if (a)  throw x.triggerCallback(o, "模型验证错误"), Error(a)

调换了语句的顺序,把throw看成return 就明白了。

5) if else

这个会替换成三元表达式  a?b:c 。

6.数字处理

整百整千的会处理成科学计数 1000 -->1e3 。

7. while

复制代码
var offset = 0;
            while (true) {
                if (offset >= bufferLength) {
                    break;
                }
}
复制代码

会替换成这样:

  for (var n = 0; ; ) {
                if (n >= K)
                    break
 }

确实不错,节省了一行代码。

以上只是独自对比自己的代码发现的一些东西,有的可以在平时的编码中用起来,当然不是追求所有代码都写成一行,这样可读性比较差,另外可能你下次看压缩代码就不那么费劲了。欢迎补充。

 

protobuf.js的结构和webpack的加载之后的结构很相似。这样的模块化组合是个不错的结构方式。1个是适应了不同的加载方式,2个模块直接很独立。webpack的功能更全一点。但如果自己封装js库这样够用了。而且模块对外统一接口 module.exports。这和node很像。

复制代码
(function(global, undefined) {
    "use strict";
    (function prelude(modules, cache, entries) {
        function $require(name) {
            var $module = cache[name];
            //没有就去加载
            if (!$module)
                modules[name][0].call($module = cache[name] = { exports: {} }, $require, $module, $module.exports);

            return $module.exports;
        }

        //曝光成全局
        var proto = global.proto = $require(entries[0]);

        // AMD
        if (typeof define === "function" && define.amd) {
            define(["long"], function(Long) {
                if (Long && Long.isLong) {
                    proto.util.Long = Long;
                    proto.configure();
                }
            });
            return proto;
        }

        //CommonJS
        if (typeof module === "object" && module && module.exports)
            module.exports = proto;


    })
    //传参
    ({
        1: [function (require, module, exports) {

            function first() {
                console.log("first");
            }

            module.exports = first;

        }, {}],
        2: [function(require, module, exports) {
            function second() {
                console.log("second");
            }
            module.exports = second;
        }],
        3: [function (require, module, exports) {

            var proto = {};
            proto.first = require(1);
            proto.second = require(2);
            proto.build = "full";

            module.exports = proto;
        }]
      }, {}, [3]);


})(typeof window==="object"&&window||typeof self==="object"&&self||this)
复制代码

在处理超过16位的整形就得使用Long.js了。 主要是fromString和toString。

复制代码
  function fromString(str, unsigned, radix) {
        if (str.length === 0)
            throw Error('empty string');
        if (str === "NaN" || str === "Infinity" || str === "+Infinity" || str === "-Infinity")
            return ZERO;
        if (typeof unsigned === 'number') {
            // For goog.math.long compatibility
            radix = unsigned,
            unsigned = false;
        } else {
            unsigned = !!unsigned;
        }
        radix = radix || 10;
        if (radix < 2 || 36 < radix)
            throw RangeError('radix');

        var p;
        if ((p = str.indexOf('-')) > 0)
            throw Error('interior hyphen');
        else if (p === 0) {
            return fromString(str.substring(1), unsigned, radix).neg();
        }

        // Do several (8) digits each time through the loop, so as to
        // minimize the calls to the very expensive emulated div.
        var radixToPower = fromNumber(pow_dbl(radix, 8));

        var result = ZERO;
        for (var i = 0; i < str.length; i += 8) {
            var size = Math.min(8, str.length - i),
                value = parseInt(str.substring(i, i + size), radix);
            if (size < 8) {
                var power = fromNumber(pow_dbl(radix, size));
                result = result.mul(power).add(fromNumber(value));
            } else {
                result = result.mul(radixToPower);
                result = result.add(fromNumber(value));
            }
        }
        result.unsigned = unsigned;
        return result;
    }
复制代码

fromstring的思路是把字符串8位一个截取。然后转成Long型(高位,地位,符号位) 加起来。最后是一个Long型。 4294967296 是2的32次方。每次操作之前都会有一个基数的操作 mul(radixToPower)或者mul(power)这两者都是保证result的位数是正确的。

比如{low:123} 和{low:1} 相加之前,先要让{low:123}乘以10,得到{low:1230}再与{low:1}进行位操作。因为第一个是高位,不能直接相加。

function fromBits(lowBits, highBits, unsigned) {
        return new Long(lowBits, highBits, unsigned);
    }

fromBits 即转为Long对象。value%4294967296 得到低位。/得到高位。结果通过位移合并起来。mul是bit的乘法,add是bit的加法。 原理是讲一个64位的拆成四段。分别16位。this.low左移16位 就得到 low的32-17位是啥。 然后和addend对象的同位相加

最后的合并是通过|运算。位移之后再还原确实很巧妙。一时看上去都不大理解。

复制代码
 LongPrototype.add = function add(addend) {
        if (!isLong(addend))
            addend = fromValue(addend);

        // Divide each number into 4 chunks of 16 bits, and then sum the chunks.

        var a48 = this.high >>> 16;
        var a32 = this.high & 0xFFFF;
        var a16 = this.low >>> 16;
        var a00 = this.low & 0xFFFF;

        var b48 = addend.high >>> 16;
        var b32 = addend.high & 0xFFFF;
        var b16 = addend.low >>> 16;
        var b00 = addend.low & 0xFFFF;

        var c48 = 0, c32 = 0, c16 = 0, c00 = 0;
        c00 += a00 + b00;
        c16 += c00 >>> 16;
        c00 &= 0xFFFF;
        c16 += a16 + b16;
        c32 += c16 >>> 16;
        c16 &= 0xFFFF;
        c32 += a32 + b32;
        c48 += c32 >>> 16;
        c32 &= 0xFFFF;
        c48 += a48 + b48;
        c48 &= 0xFFFF;
        return fromBits((c16 << 16) | c00, (c48 << 16) | c32, this.unsigned);
    };
复制代码

>>>和>>有什么区别??。 

toString

复制代码
LongPrototype.toString = function toString(radix) {
        radix = radix || 10;
        if (radix < 2 || 36 < radix)
            throw RangeError('radix');
        if (this.isZero())
            return '0';
        if (this.isNegative()) { // Unsigned Longs are never negative
            if (this.eq(MIN_VALUE)) {
                // We need to change the Long value before it can be negated, so we remove
                // the bottom-most digit in this base and then recurse to do the rest.
                var radixLong = fromNumber(radix),
                    div = this.div(radixLong),
                    rem1 = div.mul(radixLong).sub(this);
                return div.toString(radix) + rem1.toInt().toString(radix);
            } else
                return '-' + this.neg().toString(radix);
        }

        // Do several (6) digits each time through the loop, so as to
        // minimize the calls to the very expensive emulated div.
        var radixToPower = fromNumber(pow_dbl(radix, 6), this.unsigned),
            rem = this;
        var result = '';
        while (true) {
            var remDiv = rem.div(radixToPower),
                intval = rem.sub(remDiv.mul(radixToPower)).toInt() >>> 0,
                digits = intval.toString(radix);
            rem = remDiv;
            if (rem.isZero())
                return digits + result;
            else {
                while (digits.length < 6)
                    digits = '0' + digits;
                result = '' + digits + result;
            }
        }
    };
复制代码

也是sub之后拼出来的。也就是fromstring的反向操作。

 

【微信开发】-- 发送模板消息

2017-06-28 20:25 by stoneniqiu, 8795 阅读, 7 评论, 收藏, 编辑

我们需要将一些行为的进展消息推送给用户。除了短信,发送微信模板消息也是不错的选择。模板消息免费、精准到达、而且可以引导用户回到网站上来。但它有两个前提条件。1个是认证的服务号,你才能选择模板。2个是被推送的用户必须关注了你的公众号,而且你也拿到了他的openid。

h5 录音 自动生成proto Js语句 UglifyJS-- 对你的js做了什么 【原码笔记】-- protobuf.js 与 Long.js 【微信开发】-- 发送模板消息 能编程与会编..._第6张图片

先在模板库中找到自己的想要的模板,添加到“我的模板”中。

h5 录音 自动生成proto Js语句 UglifyJS-- 对你的js做了什么 【原码笔记】-- protobuf.js 与 Long.js 【微信开发】-- 发送模板消息 能编程与会编..._第7张图片

展开详情,我们可以看到示例。接下来用C#代码发送一次:

从官方文档的示例中我们可以看到除了推送人的openid,还可以设置每个字段的颜色及跳转地址。先可以定义以个TempModel对象:

复制代码
  public class TemplateModel
    {
        public string touser { get; set; }
        public string template_id { get; set; }
        public string url { get; set; }

        public string topcolor { get; set; }

        public TemplateData data { get; set; } 
    
        public TemplateModel(string hello,string state,string reason,string last)
        {
            data=new TemplateData()
            {
                first = new TempItem(hello),
                keyword1 = new TempItem(state),
                keyword2 = new TempItem(reason),
                remark = new TempItem(last)
            };
             
        }
    }

    public class TemplateData
    {
        public TempItem first { get; set; }
        public TempItem keyword1 { get; set; }
        public TempItem keyword2 { get; set; }
        public TempItem remark { get; set; }
    }
    public class TempItem
    {
        public TempItem(string v,string c = "#173177")
        {
            value = v;
            color = c;
        }
        public string value { get; set; }
        public string color { get; set; }
    }
复制代码

还有一个返回结果对象:

复制代码
 public class OpenApiResult
    {
        public int error_code { get; set; }
        public string error_msg { get; set; }

        public string msg_id { get; set; }
    }
复制代码

然后定义一个发送方法:

复制代码
using SendHelp= Senparc.Weixin.CommonAPIs.CommonJsonSend;

public OpenApiResult SendTemplateMessage(string token,TemplateModel model)
        {
            var url = string.Format("https://api.weixin.qq.com/cgi-bin/message/template/send?access_token={0}", token);
            try
            {
                var res = SendHelp.Send(token, url, model);
                return res;
            }
            catch (Exception e)
            {
                
                return new OpenApiResult(){error_code = -1,error_msg = e.Message};
            }
          
        } 
复制代码

SendHelp是基于Senparac.Weixin 最后就可以调用了:

复制代码
        public ActionResult SendMessage()
        {
            var token = getToken();
            var toUserId = "oBSBmwQjqwjfzQlKsFNjxFLSiIQM";
            var data = new TemplateModel("你好,stoneniqiu","审核通过","资料完整","祝你生活幸福!");
            data.touser = toUserId;
            data.template_id = "gXmkeL7Kc-KUy2EQzKqjPKY-kzFyatTztiYFKCaUPO4";
            data.url = "http://www.xxx.com/xx/xx";
            data.topcolor = "#FF0000";
            var res=wxDeviceService.SendTemplateMessage(token, data);
            return View(res);
        }
复制代码

token即通过AppID和APPSECRET获取。发送之后,手机上马上收到消息。这里的url就是下图详情的跳转地址。 只能是注册域名下面的地址,不能跳到别的域名去。

h5 录音 自动生成proto Js语句 UglifyJS-- 对你的js做了什么 【原码笔记】-- protobuf.js 与 Long.js 【微信开发】-- 发送模板消息 能编程与会编..._第8张图片

但如果你只是拿到了用户的openid,但该用户没有关注公众号,发送时会抛出下面的错误:

其他信息: 微信Post请求发生错误!错误代码:43004,说明:require subscribe hint: [Q2OfvA0092ge21]

相关部分代码:http://files.cnblogs.com/files/stoneniqiu/wx-template.zip

官方文档:https://mp.weixin.qq.com/advanced/tmplmsg?action=faq&token=1798469214&lang=zh_CN

全部错误类型:http://www.szdyhd.com/news/view/webdesign/2016/0614/519.html

 

    几个月前因为一个事情被diao了。起因是临近上线的时候项目后端统一了消息协议(.proto),然后要我前端也支持。我研究了一天,没走通,要么依赖项太多,要么一直报错,而且需要使用的对象兼容性有问题。当时心里有些急,也有几份抵触这种方案,于是在会上说出了我的想法:能不能友好的发发json,兼容性好也不需要什么第三方解析。结果自然是被否决了,理由是大厂出品的,怎么可能不能用呢,用屁股想想就知道?你为啥遇到问题就想着退缩呢。我无语凝噎。重要是给我强调了能编程与会编程是不一样的

    开完会情绪有点低落,回到座位上打开github 继续找方案,不一会儿居然找到了!啪啪打脸的感觉好酸爽。然后开始思索...

是否抵触

     一件事带有抵触情绪之后,那基本是做不好的。在找方案的时候,眼中只看到“NO”,而自动忽略了“YES”。解决方法在你眼前你可能都看不到。就像一个人不想做事就找借口,这些借口在他自己看来都是很合理的。要带有这种情绪,那不如先别开始,要么自我消化,要么拿更好的方案去说服别人。

只要有一种解释是对自己有利的,我们便不想去推敲和反驳,再漏洞百出的事情看上去也不无可能,而且只要一种解释是可能的,我们就认定是一定的,强大的情绪大脑会阻止理性大脑往深入的想。而对于自己不利的解释,我们或忽略,或者异常仔细的去推敲,抓住一个漏洞则相信自己推翻了所有的解释。----《暗时间》

所以一件事情在自己着手做的时候,先整理好类似的情绪。很多时候摆在我们面前的方案很多,如果是你熟悉的领域当然好选择,利弊清晰,能快速判断。但不熟悉的时候,就要注意到每个方案所适用的环境和条件,要归类和总结,自己所遇到的问题本身已经提供了一些前提条件。用这些条件先过滤到一部分。而不是瞎猫抓死耗子一样一个个去碰。

另外一点,我们在尝试新的事物的时候,总是会遇到各种困难,不同的人在不同的碰壁次数之后会退出。用程序员的话来说,我们都在for循环,区别在于你是什么情况下break;的。有的人退出阈值高,这是能坚持的一类人,有的人退出阈值低。过早退出的原因在于对未来的不确定性,对投资时间最终无法收到回报的恐惧。其实在坚持坚持,再想一想,答案马上就出来了。

不要等待

   编程工作中我们经常遇到需求不完整,接口文档没有,接口返回值错,误诸如此类 别人的因素,然后导致项目进度缓慢或者停滞。其实我想说,编程简直太轻松愉快了,因为还有人给你提供需求,提供设计稿,你实现就行了。实际生活中很多事情根本没有人会告诉你需求,比如自己装修,你需要定风格,然后选材料,按照流程喊施工队伍,考虑风格和成本,考虑材料合不合适,喜不喜欢,甲醛高不高等等。建材市场鱼龙混杂,装修师傅可能阳奉阴违。即使你监工他都敢用料不够,总有一款让你交点学费。 可能很忙的时候,父母又生病了..... 生活的事情更考验的人的协作和沟通能力、承压能力。没有谁告诉你截止日期和责任,但是你没做好,就是没有做好,不要说什么装修的人坑了你。工作的事情责任范围清晰,不属于你的锅你都可以甩的远远的。但如果团队内部都是这样的氛围,那又能成什么事。雪崩的时候,没有一片雪花觉得自己有责任,应该积极主动起来,因为等待耗费的也是自己的时间。    

做完了还是做好了

按照需求和效果图,你可能很快就实现了所有功能和效果。但是自己测试了吗?可用性高吗?稳定吗?兼容性如何?功能做完往往只到了一个阶段,后面还需要自己做检验性工作。确保交出去的东西是ok的,达到预期的。有信心对别人说我做好了,让别人用的放心。而不是说一句我做完了,暗藏的坑让别人先踩。有时间还应该思考一下,代码是否可以更简洁?接口设计是否可以更简明?多个类似方案是否可以统一成产品?思考的价值总会为你省下未来的时间,特别是重复劳动的时间、遇到问题沟通的时间。

知识体系完善

 你选择的方案,都是你所知道的方案。也就是说,你的知识范围,决定了你的处理能力范围。甚至于,有的知识你会,但关键的时候你未必想的出来。这个过程就像解题,自己苦思冥想不得,看到答案恍然大悟。为什么呢?你的大脑就像一个图书馆,知识的碎片就如书架上的书,你想用的时候,发现找不到地址了。

知识分两种,一种是我们通常所谓的知识,即领域知识。二是关于我们大脑吸收知识的机制的知识,后者不妨成为元知识。虽说这也是领域知识,但跟其他的领域知识不同的是,它指导我们学习其它所有的领域知识。

除了完善我们的领域知识,也许需要补充一下其他领域的,譬如心理学和思维方面的,当然最好是到生活中去解决问题。 

小结:会编程是基于现有经验办事的能力,而能编程是对整个事情的解决能力。在学习一门技术的成本差不多的情况下,差别就来自于编程之外的能力。

 

学习了Vue全家桶和一些UI基本够用了,但是用元素的方式使用组件还是不够灵活,比如我们需要通过js代码直接调用组件,而不是每次在页面上通过属性去控制组件的表现。下面讲一下如何定义动态组件。

 Vue.extend

 思路就是拿到组件的构造函数,这样我们就可以new了。而Vue.extend可以做到:https://cn.vuejs.org/v2/api/#Vue-extend

复制代码
// 创建构造器
var Profile = Vue.extend({
  template: '

{{firstName}} {{lastName}} aka {{alias}}

', data: function () { return { firstName: 'Walter', lastName: 'White', alias: 'Heisenberg' } } }) // 创建 Profile 实例,并挂载到一个元素上。 new Profile().$mount('#mount-point')
复制代码

官方提供了这个示例,我们进行一下改造,做一个简单的消息提示框。

动态组件实现

创建一个vue文件。widgets/alert/src/main.vue

复制代码


复制代码

这是我们组件的构成。如果是第一节中,我们可以把他放到components对象中就可以用了,但是这儿我们要通过构造函数去创建它。再创建一个widgets/alert/src/main.js

复制代码
import Vue from 'vue';
let MyMsgConstructor = Vue.extend(require('./main.vue'));

let instance;

var MyMsg=function(msg){
 instance= new MyMsgConstructor({
     data:{
         message:msg
}})

//如果 Vue 实例在实例化时没有收到 el 选项,则它处于“未挂载”状态,没有关联的 DOM 元素。可以使用 vm.$mount() 手动地挂载一个未挂载的实例。
instance.$mount();
 
 document.body.appendChild(instance.$el)
 return instance;
}


export default MyMsg;
复制代码

require('./main.vue')返回的是一个组件初始对象,对应Vue.extend( options )中的options,这个地方等价于下面的代码:

import alert from './main.vue'
let MyMsgConstructor = Vue.extend(alert);

而MyMsgConstructor如下。

 参考源码中的this._init,会对参数进行合并,再按照生命周期运行:

复制代码
  Vue.prototype._init = function (options) {
    ...// merge options
    if (options && options._isComponent) {
      // optimize internal component instantiation
      // since dynamic options merging is pretty slow, and none of the
      // internal component options needs special treatment.
      initInternalComponent(vm, options);
    } else {
      vm.$options = mergeOptions(
        resolveConstructorOptions(vm.constructor),
        options || {},
        vm
      );
    }
// expose real self
    vm._self = vm;
    initLifecycle(vm);
    initEvents(vm);
    initRender(vm);
    callHook(vm, 'beforeCreate');
    initInjections(vm); // resolve injections before data/props
    initState(vm);
    initProvide(vm); // resolve provide after data/props
    callHook(vm, 'created');

    ...

    if (vm.$options.el) {
      vm.$mount(vm.$options.el);
    }
  };
复制代码

而调用$mount()是为了获得一个挂载实例。这个示例就是instance.$el。

h5 录音 自动生成proto Js语句 UglifyJS-- 对你的js做了什么 【原码笔记】-- protobuf.js 与 Long.js 【微信开发】-- 发送模板消息 能编程与会编..._第9张图片

可以在构造方法中传入el对象(注意上面源码中的mark部分,也是进行了挂载vm.$mount(vm.$options.el),但是如果你没有传入el,new之后不会有$el对象的,就需要手动调用$mount()。这个方法可以直接传入元素id。

 instance= new MessageConstructor({
     el:".leftlist",
     data:{
         message:msg
}})

 这个el不能直接写在vue文件中,会报错。接下来我们可以简单粗暴的将其设置为Vue对象。

调用

在main.js引入我们的组件:

复制代码
//..
import VueResource from 'vue-resource'
import MyMsg from './widgets/alert/src/main.js';
//..
//Vue.component("MyMsg", MyMsg);
Vue.prototype.$mymsg = MyMsg;
复制代码

然后在页面上测试一下:

复制代码
主要按钮
//..

 methods:{
  test(){
  this.$mymsg("hello vue");
  }
 }

复制代码

h5 录音 自动生成proto Js语句 UglifyJS-- 对你的js做了什么 【原码笔记】-- protobuf.js 与 Long.js 【微信开发】-- 发送模板消息 能编程与会编..._第10张图片 

这样就实现了基本的传参。最好是在close方法中移除元素:

复制代码
 close(){
    setTimeout(()=>{
       this.visible = false;
       this.$el.parentNode.removeChild(this.$el);
      },2000)
   },
复制代码

 回调处理

回调和传参大同小异,可以直接在构造函数中传入。先修改下main.vue中的close方法:

复制代码
export default{
        data(){
           return{
               message:'',
               visible:true
           } 
        },
        methods:{
            close(){
                setTimeout(()=>{
                     this.visible = false;
                      this.$el.parentNode.removeChild(this.$el);

                if (typeof this.onClose === 'function') {
                 this.onClose(this);
                }
                },2000)
            },
        },
        mounted() {
        this.close();
      }
    }
复制代码

如果存在onClose方法就执行这个回调。而在初始状态并没有这个方法。然后在main.js中可以传入

复制代码
var MyMsg=function(msg,callback){

 instance= new MyMsgConstructor({
     data:{
         message:msg
    },
    methods:{
        onClose:callback
    } 
})
复制代码

这里的参数和原始参数是合并的关系,而不是覆盖。这个时候再调用的地方修改下,就可以执行回调了。

   test(){
      this.$mymsg("hello vue",()=>{
        console.log("closed..")
      });
    },

你可以直接重写close方法,但这样不推荐,因为可能搞乱之前的逻辑且可能存在重复的编码。现在就灵活多了。

统一管理

 如果随着自定义动态组件的增加,在main.js中逐个添加就显得很繁琐。所以这里我们可以让widgets提供一个统一的出口,日后也方便复用。在widgets下新建一个index.js 

复制代码
import MyMsg from './alert/src/main.js';

const components = [MyMsg];

let install =function(Vue){
  components.map(component => {
    Vue.component(component.name, component);
  });

 Vue.prototype.$mymsg = MyMsg;

}

if (typeof window !== 'undefined' && window.Vue) {
  install(window.Vue);
};

export default {
    install
}
复制代码

在这里将所有自定义的组件通过Vue.component注册。最后export一个install方法就可以了。因为接下来要使用Vue.use。

安装 Vue.js 插件。如果插件是一个对象,必须提供 install 方法。如果插件是一个函数,它会被作为 install 方法。install 方法将被作为 Vue 的参数调用。

也就是把所有的组件当插件提供:在main.js中加入下面的代码即可。

复制代码
...
import VueResource from 'vue-resource'
import Widgets from './Widgets/index.js'

...
Vue.use(Widgets)
复制代码

这样就很简洁了。

小结: 通过Vue.extend和Vue.use的使用,我们自定义的组件更具有灵活性,而且结构很简明,基于此我们可以构建自己的UI库。以上来自于对Element源码的学习。

widgets部分源码:http://files.cnblogs.com/files/stoneniqiu/widgets.zip

vue2入坑随记(一)-- 初始全家桶

 

低版本的安卓上传图片是个问题,能出现选择图片,但点击图片后没有反应,转成base64也无解。于是改为用微信的接口上传。和之前的微信分享功能都是基于微信的jssdk。

步骤比我们平时上传到服务器多一步,他是先调用chooseeImage方法获得用户要上传的图片id。然后上传到微信的服务器,微信的服务器默认只保存三天,所以还要让后台下载到自己的服务器上,然后返回地址,前端显示,这样才算完成。

复制代码
     var time = '@ViewBag.Share.timestamp';
      wx.config({
        debug: false, // 开启调试模式,调用的所有api的返回值会在客户端alert出来,若要查看传入的参数,可以在pc端打开,参数信息会通过log打出,仅在pc端时才会打印。
        appId: '@ViewBag.Share.appId', // 必填,公众号的唯一标识
        timestamp: parseInt(time), // 必填,生成签名的时间戳
        nonceStr: '@ViewBag.Share.nonceStr', // 必填,生成签名的随机串
        signature: '@ViewBag.Share.signature',// 必填,签名,见附录1
          jsApiList: ["chooseImage", "previewImage", "uploadImage", "downloadImage"] // 必填,需要使用的JS接口列表,所有JS接口列表见附录2
      });
        wx.ready(function() {
            $(document).on("click", ".ctcon", function() {
                wx.chooseImage({
                    count: 1, // 默认9
                    sizeType: ['original', 'compressed'], // 可以指定是原图还是压缩图,默认二者都有
                    sourceType: ['album', 'camera'], // 可以指定来源是相册还是相机,默认二者都有
                    success: function (res) {
                        var localIds = res.localIds; // 返回选定照片的本地ID列表,localId可以作为img标签的src属性显示图片
                        uploadimg(localIds[0].toString());

                    }
                });
            });

                
function uploadimg(lid) { wx.uploadImage({ localId: lid, // 需要上传的图片的本地ID,由chooseImage接口获得 isShowProgressTips: 1, // 默认为1,显示进度提示 success: function (res) { var serverId = res.serverId; // 返回图片的服务器端ID //alert(serverId); $.post("/Question/DownWxImage", { serverId: serverId }, function(res) { //alert(res.SaveName); if (res.Success === true) { // 显示图片 } }); }); } });
复制代码

count表示让用户选择图片的张数,然后这里的localIds要tostring。不然上传不了。uploadImage执行完了就可以通知让后台上传:

复制代码
  public ActionResult DownWxImage(string serverId)
        {
            var token = getToken();
             var url = string.Format("http://file.api.weixin.qq.com/cgi-bin/media/get?access_token={0}&media_id={1}",
                 token, serverId);//图片下载地址

             HttpWebRequest req = (HttpWebRequest)HttpWebRequest.Create(url);

             req.Method = "GET";
             using (WebResponse wr = req.GetResponse())
             {
                 HttpWebResponse myResponse = (HttpWebResponse)req.GetResponse();

                var strpath = myResponse.ResponseUri.ToString();
                 WebClient mywebclient = new WebClient();

                 var path = "/Content/UploadFiles/mobile/";
                 var uploadpath = Server.MapPath(path);
                 if (!Directory.Exists(uploadpath))
                 {
                     Directory.CreateDirectory(uploadpath);
                 }
                 string saveName = Encrypt.GenerateOrderNumber() + ".jpg";
                 var savePath = uploadpath + saveName;

                 try
                 {
                     mywebclient.DownloadFile(strpath, savePath);
                     return Json(new { Success = true, SaveName = path + saveName });
                 }
                 catch (Exception ex)
                 {
                     savePath = ex.ToString();
                 }

             }
             return Json(new {Success = false, Message = "上传失败!"});
        }
复制代码

 这样安卓是能上传了,但是也没有了进度条。

转载于:https://www.cnblogs.com/cjm123/p/9135334.html

你可能感兴趣的:(h5 录音 自动生成proto Js语句 UglifyJS-- 对你的js做了什么 【原码笔记】-- protobuf.js 与 Long.js 【微信开发】-- 发送模板消息 能编程与会编...)