在与后端的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 也很烦,由于要一个一个对照属性,不断的复制粘贴,很容易出错。因此又作了个自动生成代码的页面:
<textarea id="content"> //登录Token message Token{ string UserID = 1; //登录接口返回的IMUserID string Token = 2; //登录接口返回的Token string Device = 3; //客户端设备号 int32 Version = 4; //版本号,发布前与服务端约定值 string Appkey = 5; //客户端Appkey } //收到私信 message ReceivePrivateMessage{ string MsgID = 1; //消息id string SenderID = 2; //发送者id string ReceiverID = 3; //接收者id string Content = 4; //消息内容。客户端转换成业务相关的实体后,再作后续处理(客户端使用,服务器不作任何处理,原样下发) bool Ack = 5; //是否须要已读回执 int32 SendDateTime = 6; //消息发送时间 int32 ContentType = 7; //内容类型(客户端使用,服务器不作任何处理,原样下发) } //回执类型 enum ReceiptType{ Receive = 0; //已收回执(收到消息后当即发送给服务器的回执) Read = 1; //已读回执(用户进入消息阅读界面后发送给服务器的回执) } </textarea> <div id="result"></div> <script> function start() { $("#result").html(""); $("#result").append('root = new protobuf.Root().define("IMProtoEntity")<br>'); var reg = /("([^\\\"]*(\\.)?)*")|('([^\\\']*(\\.)?)*')|(\/{2,}.*?(\r|\n))|(\/\*(\n|.)*?\*\/)/g,// 过滤注释 str = $('#content').val(); // 欲处理的文本 // console.log(str.match(reg));// 打印出:匹配子串 var news = str.replace(reg, ""); // console.log(news); // 打印出:原文本 var reg1 = /[message|enum].*?{/mg; var regobj = /{[^}{]*?}/g;//新地址 var names = news.match(reg1); var protos = news.match(regobj); // console.log(names, protos); var root = {}; for (var i = 0; i < names.length; i++) { var rawname = names[i]; var rawObj = protos[i]; //if (~rawname.indexOf("message")) if (!rawObj) continue; var name = rawname.replace("{", '').replace("message ", '').replace("enum ", ''); var obj = { name: name }; if (~rawname.indexOf("enum")) { obj["type"] = "enum"; } rawObj = rawObj.replace("{", '').replace("}", ''); var protolist = rawObj.split(';'); // console.log("protolist", protolist); var plist = []; for (var j = 0; j < protolist.length; j++) { var p = $.trim(protolist[j]); if (p) { var args = []; var list = p.split(' '); // console.log("list", list); list.forEach(function (n) { n && args.push(n); }), // console.log("args", args); plist.push(args); } } obj.list = plist; console.log(obj); toProto(obj); } } start(); function toProto(obj) { var root = "root"; var fun = "createProto"; var enumfun = "createEnum"; var str = root + '.add('; var args; if (!obj.type) {//message args = ''; for (var i = 0; i < obj.list.length; i++) { var item = obj.list[i]; //老协议2.0 if (item[0] == "required" || item[0] == "optional") { item.shift(); } //新协议3.0 if (item[0] != "string") { args += '["' + item[1] + '","' + item[0] + '"]'; } else { args += '["' + item[1] + '"]'; } if (i < obj.list.length - 1) args += ","; } } else {//enum args = '['; for (var i = 0; i < obj.list.length; i++) { var item = obj.list[i]; args += '"' + item[0] + '"'; if (i < obj.list.length - 1) args += ","; } args += ']'; } var all = str + (obj.type ? enumfun : fun) + '("' + obj.name + '",' + args + '));'; // console.log(all); $("#result").append(all + "<br>"); } </script>
而后页面上会获得:
红色部分复制到工程里面就能够用了。固然要带上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.当即执行函数

2.变量名替换
3.函数置顶
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语句是压缩最多的地方。
function load() { if (t) { x = false; log("error"); return; } console.log("22") }
好比个人原函数大概是这样。压缩后成了这样:
if (t) return x =!1,void log("error")
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.数字处理
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。
先在模板库中找到本身的想要的模板,添加到“个人模板”中。
展开详情,咱们能够看到示例。接下来用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<OpenApiResult>(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就是下图详情的跳转地址。 只能是注册域名下面的地址,不能跳到别的域名去。
但若是你只是拿到了用户的openid,但该用户没有关注公众号,发送时会抛出下面的错误:
相关部分代码: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”。解决方法在你眼前你可能都看不到。就像一我的不想作事就找借口,这些借口在他本身看来都是很合理的。要带有这种情绪,那不如先别开始,要么自我消化,要么拿更好的方案去说服别人。
只要有一种解释是对本身有利的,咱们便不想去推敲和反驳,再漏洞百出的事情看上去也不无可能,并且只要一种解释是可能的,咱们就认定是必定的,强大的情绪大脑会阻止理性大脑往深刻的想。而对于本身不利的解释,咱们或忽略,或者异常仔细的去推敲,抓住一个漏洞则相信本身推翻了全部的解释。----《暗时间》
因此一件事情在本身着手作的时候,先整理好相似的情绪。不少时候摆在咱们面前的方案不少,若是是你熟悉的领域固然好选择,利弊清晰,能快速判断。但不熟悉的时候,就要注意到每一个方案所适用的环境和条件,要归类和总结,本身所遇到的问题自己已经提供了一些前提条件。用这些条件先过滤到一部分。而不是瞎猫抓死耗子同样一个个去碰。
不要等待
编程工做中咱们常常遇到需求不完整,接口文档没有,接口返回值错,误诸如此类 别人的因素,而后致使项目进度缓慢或者停滞。其实我想说,编程简直过轻松愉快了,由于还有人给你提供需求,提供设计稿,你实现就好了。实际生活中不少事情根本没有人会告诉你需求,好比本身装修,你须要定风格,而后选材料,按照流程喊施工队伍,考虑风格和成本,考虑材料合不合适,喜不喜欢,甲醛高不高等等。建材市场鱼龙混杂,装修师傅可能阳奉阴违。即便你监工他都敢用料不够,总有一款让你交点学费。 可能很忙的时候,父母又生病了..... 生活的事情更考验的人的协做和沟通能力、承压能力。没有谁告诉你截止日期和责任,可是你没作好,就是没有作好,不要说什么装修的人坑了你。工做的事情责任范围清晰,不属于你的锅你均可以甩的远远的。但若是团队内部都是这样的氛围,那又能成什么事。雪崩的时候,没有一片雪花以为本身有责任,应该积极主动起来,由于等待耗费的也是本身的时间。
作完了仍是作好了
按照需求和效果图,你可能很快就实现了全部功能和效果。可是本身测试了吗?可用性高吗?稳定吗?兼容性如何?功能作完每每只到了一个阶段,后面还须要本身作检验性工做。确保交出去的东西是ok的,达到预期的。有信心对别人说我作好了,让别人用的放心。而不是说一句我作完了,暗藏的坑让别人先踩。有时间还应该思考一下,代码是否能够更简洁?接口设计是否能够更简明?多个相似方案是否能够统一成产品?思考的价值总会为你省下将来的时间,特别是重复劳动的时间、遇到问题沟通的时间。
知识体系完善
你选择的方案,都是你所知道的方案。也就是说,你的知识范围,决定了你的处理能力范围。甚至于,有的知识你会,但关键的时候你未必想的出来。这个过程就像解题,本身苦思冥想不得,看到答案恍然大悟。为何呢?你的大脑就像一个图书馆,知识的碎片就如书架上的书,你想用的时候,发现找不到地址了。
知识分两种,一种是咱们一般所谓的知识,即领域知识。二是关于咱们大脑吸取知识的机制的知识,后者不妨成为元知识。虽然说这也是领域知识,但跟其余的领域知识不一样的是,它指导咱们学习其它全部的领域知识。
除了完善咱们的领域知识,也许须要补充一下其余领域的,譬如心理学和思惟方面的,固然最好是到生活中去解决问题。
小结:会编程是基于现有经验办事的能力,而能编程是对整个事情的解决能力。在学习一门技术的成本差很少的状况下,差异就来自于编程以外的能力。
学习了Vue全家桶和一些UI基本够用了,可是用元素的方式使用组件仍是不够灵活,好比咱们须要经过js代码直接调用组件,而不是每次在页面上经过属性去控制组件的表现。下面讲一下如何定义动态组件。
Vue.extend
思路就是拿到组件的构造函数,这样咱们就能够new了。而Vue.extend能够作到:https://cn.vuejs.org/v2/api/#Vue-extend
// 建立构造器 var Profile = Vue.extend({ template: '<p>{{firstName}} {{lastName}} aka {{alias}}</p>', data: function () { return { firstName: 'Walter', lastName: 'White', alias: 'Heisenberg' } } }) // 建立 Profile 实例,并挂载到一个元素上。 new Profile().$mount('#mount-point')
官方提供了这个示例,咱们进行一下改造,作一个简单的消息提示框。
动态组件实现
建立一个vue文件。widgets/alert/src/main.vue
<template> <transition name="el-message-fade"> <div v-show="visible" class="my-msg">{{message}}</div> </transition> </template> <script > export default{ data(){ return{ message:'', visible:true } }, methods:{ close(){ setTimeout(()=>{ this.visible = false; },2000) }, }, mounted() { this.close(); } } </script>
这是咱们组件的构成。若是是第一节中,咱们能够把他放到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。
能够在构造方法中传入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;
而后在页面上测试一下:
<el-button type="primary" @click='test'>主要按钮</el-button> //..
methods:{
test(){
this.$mymsg("hello vue");
}
}
这样就实现了基本的传参。最好是在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
低版本的安卓上传图片是个问题,能出现选择图片,但点击图片后没有反应,转成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 = "上传失败!"}); }
这样安卓是能上传了,可是也没有了进度条。