在與後端的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 = "上傳失敗!"}); }
這樣安卓是能上傳了,可是也沒有了進度條。