-
Notifications
You must be signed in to change notification settings - Fork 0
/
content.json
1 lines (1 loc) · 237 KB
/
content.json
1
{"meta":{"title":"安静的小站","subtitle":"个人博客","description":"技术生活个人博客","author":"你的名字。ß","url":"http://www.mvtime.cn"},"pages":[{"title":"gallery","date":"2017-05-09T00:57:16.000Z","updated":"2017-05-09T00:57:16.000Z","comments":true,"path":"gallery/index.html","permalink":"http://www.mvtime.cn/gallery/index.html","excerpt":"","text":""},{"title":"相册","slug":"photos","date":"2017-02-17T09:33:51.000Z","updated":"2017-02-17T09:33:51.000Z","comments":false,"path":"photos/index.html","permalink":"http://www.mvtime.cn/photos/index.html","excerpt":"","text":"七牛 图片来自七牛,正在加载中… (function() { var loadScript = function(path) { var $script = document.createElement('script') document.getElementsByTagName('body')[0].appendChild($script) $script.setAttribute('src', path) } setTimeout(function() { loadScript('./ins.js') }, 0) })()"},{"title":"","date":"2017-02-16T01:58:21.000Z","updated":"2017-02-15T03:17:08.000Z","comments":true,"path":"photos/ins.css","permalink":"http://www.mvtime.cn/photos/ins.css","excerpt":"","text":"#post-instagram{ padding: 30px; } #post-instagram .article-entry{ padding-right: 0; } .instagram{ position: relative; min-height: 500px; } .instagram img { width: 100%; } .instagram .year { font-size: 16px; } .instagram .open-ins{ padding: 10px 0; color: #cdcdcd; } .instagram .open-ins:hover{ color: #657b83; } .instagram .year{ display: inline; } .instagram .thumb { width: 25%; height: 0; padding-bottom: 25%; position: relative; display: inline-block; text-align: center; background: #ededed; outline: 1px solid #ddd; } .instagram .thumb a { position: relative; } .instagram .album h1 em{ font-style: normal; font-size: 14px; margin-left: 10px; } .instagram .album ul{ display: flex; flex-wrap: wrap; clear: both; width: 100%; text-align: left; } .instagram .album li{ list-style: none; display: inline-block; box-sizing: border-box; padding: 0 5px; margin-bottom: -10px; height: 0; width: 25%; position: relative; padding-bottom: 25%; } .instagram .album li:before{ display: none; } .instagram .album div.img-box{ position: absolute; width: 90%; height: 90%; -webkit-box-shadow: 0 1px 0 rgba(255,255,255,0.4), 0 1px 0 1px rgba(255,255,255,0.1); -moz-box-shadow: 0 1px 0 rgba(255,255,255,0.4), 0 1px 0 1px rgba(255,255,255,0.1); box-shadow: 0 1px 0 rgba(255,255,255,0.4), 0 1px 0 1px rgba(255,255,255,0.1); } .instagram .album div.img-box img{ width: 100%; height: 100%; position: absolute; z-index: 2; } .instagram .album div.img-box .img-bg{ position: absolute; top: 0; left: 0; bottom: 0px; width: 100%; margin: -5px; padding: 5px; -webkit-box-shadow: 0 0 0 1px rgba(0,0,0,.04), 0 1px 5px rgba(0,0,0,0.1); -moz-box-shadow: 0 0 0 1px rgba(0,0,0,.04), 0 1px 5px rgba(0,0,0,0.1); box-shadow: 0 0 0 1px rgba(0,0,0,.04), 0 1px 5px rgba(0,0,0,0.1); -webkit-transition: all 0.15s ease-out 0.1s; -moz-transition: all 0.15s ease-out 0.1s; -o-transition: all 0.15s ease-out 0.1s; transition: all 0.15s ease-out 0.1s; opacity: 0.2; cursor: pointer; display: block; z-index: 3; } .instagram .album div.img-box .icon { font-size: 14px; position: absolute; left: 50%; top: 50%; margin-left: -7px; margin-top: -7px; color: #999; z-index: 1; } .instagram .album div.img-box .img-bg:hover{ opacity: 0; } .photos-btn-wrap { border-bottom: 1px solid #e5e5e5; margin-bottom: 20px; } .photos-btn { font-size: 16px; color: #333; margin-bottom: -4px; padding: 5px 8px 3px; } .photos-btn.active { color: #08c; border: 1px solid #e5e5e5; border-bottom: 5px solid #fff; } @media screen and (max-width:600px) { .instagram .thumb { width: 50%; padding-bottom: 50%; } .instagram .album li { width: 100%; position: relative; padding-bottom: 100%; text-align: center; } .instagram .album div.img-box{ margin: 0; width: 90%; height: 90%; } }"},{"title":"","date":"2017-06-13T09:26:12.000Z","updated":"2017-06-13T09:26:12.000Z","comments":true,"path":"photos/ins.json","permalink":"http://www.mvtime.cn/photos/ins.json","excerpt":"","text":"[\"2015-04-29 173112.jpg\",\"2015-04-29 220053.jpg\",\"2015-04-29 220204.jpg\",\"2015-04-29 220228.jpg\",\"2015-04-29 220319.jpg\",\"2015-04-29 220449.jpg\",\"2015-04-29 220508.jpg\",\"2015-04-29 220512.jpg\",\"2015-04-29 220522.jpg\",\"2015-04-29 220547.jpg\",\"2015-04-29 220610.jpg\",\"2015-04-29 220618.jpg\",\"2015-04-29 220622.jpg\",\"2015-04-29 220653.jpg\",\"2015-04-29 220715.jpg\",\"2015-04-29 220732.jpg\",\"2016-01-22 184120.jpg\",\"2016-01-26 082022.jpg\",\"2016-01-26 191152.jpg\",\"2016-01-26 191156.jpg\",\"2016-01-29 143929.jpg\",\"2016-01-29 143935.jpg\",\"2016-01-29 143941.jpg\",\"2016-01-29 144208.jpg\",\"2016-01-29 144427.jpg\",\"2016-01-29 155644.jpg\",\"2016-01-29 155648.jpg\",\"2016-01-29 155651.jpg\",\"2016-01-29 155655.jpg\",\"2016-01-29 160501.jpg\",\"2016-01-29 162652.jpg\",\"2016-01-31 123954.jpg\",\"2016-01-31 124056.jpg\",\"2016-01-31 124129.jpg\",\"2016-01-31 124150.jpg\",\"2016-02-01 175619.jpg\",\"2016-02-01 175639.jpg\",\"2016-02-02 215427.png\",\"2016-02-03 133311.jpg\",\"2016-02-04 184617.png\",\"2016-02-04 222945.jpg\",\"2016-02-04 223546.jpg\",\"2016-02-04 225820.jpg\",\"2016-02-05 062955.jpg\",\"2016-02-05 171425.jpg\",\"2016-02-09 184940.jpg\",\"2016-02-09 184959.jpg\",\"2016-02-09 185011.jpg\",\"2016-02-09 185015.jpg\",\"2016-02-09 204908.jpg\",\"2016-02-09 204911(1).jpg\",\"2016-02-09 204911.jpg\",\"2016-02-09 204917.jpg\",\"2016-02-09 221356.jpg\",\"2016-02-09 222135.jpg\",\"2016-02-09 231231.jpg\",\"2016-02-10 151218.jpg\",\"2016-02-13 135919.jpg\",\"2016-02-13 135947.jpg\",\"2016-02-13 140032.jpg\",\"2016-02-14 110741.jpg\",\"2016-02-15 220807.jpg\",\"2016-02-15 220829.jpg\",\"2016-02-16 210330.jpg\",\"2016-02-16 210347.jpg\",\"2016-02-19 175310.jpg\",\"2016-02-19 175453.jpg\",\"2016-02-19 224547.jpg\",\"2016-02-19 231028.jpg\",\"2016-02-20 160327.jpg\",\"2016-02-20 205905.jpg\",\"2016-02-21 203837.jpg\",\"2016-02-21 203913.jpg\",\"2016-02-21 204000.jpg\",\"2016-02-26 190052.jpg\",\"2016-03-02 083126.jpg\",\"2016-03-02 083229.jpg\",\"2016-03-03 213417.jpg\",\"2016-03-05 144322.jpg\",\"2016-03-05 144410.jpg\",\"2016-03-05 145055.jpg\",\"2016-03-05 161208.jpg\",\"2016-03-06 120357.jpg\",\"2016-03-06 120720.jpg\",\"2016-03-06 121207.jpg\",\"2016-03-06 131333.jpg\",\"2016-03-06 132549.jpg\",\"2016-03-12 183817.jpg\",\"2016-03-12 183833.jpg\",\"2016-03-12 212027.jpg\",\"2016-03-19 192748.png\",\"2016-03-19 192752.png\",\"2016-03-21 182234.jpg\",\"2016-03-22 180624.png\",\"2016-03-22 180627.png\",\"2016-03-22 180630.png\",\"2016-03-26 193157.jpg\",\"2016-03-26 193209.jpg\",\"2016-03-27 134747.jpg\",\"2016-03-27 143907(1).jpg\",\"2016-03-27 143907.jpg\",\"2016-03-27 143917.jpg\",\"2016-03-27 143956.jpg\",\"2016-03-27 144014.jpg\",\"2016-03-27 144148(1).jpg\",\"2016-03-27 144148.jpg\",\"2016-03-27 144200.jpg\",\"2016-03-27 145943.jpg\",\"2016-03-27 150025.jpg\",\"2016-03-27 150047.jpg\",\"2016-03-27 150155.jpg\",\"2016-03-27 150204.jpg\",\"2016-03-27 150222.jpg\",\"2016-03-27 150235.jpg\",\"2016-03-27 150313.jpg\",\"2016-03-27 150430.jpg\",\"2016-03-27 150432.jpg\",\"2016-03-27 150700.jpg\",\"2016-03-27 150702.jpg\",\"2016-03-27 150703.jpg\",\"2016-03-27 150707.jpg\",\"2016-03-27 150818.jpg\",\"2016-03-27 151324.jpg\",\"2016-03-27 151514.jpg\",\"2016-03-27 151540.jpg\",\"2016-03-27 151554.jpg\",\"2016-03-27 151901.jpg\",\"2016-03-27 151910.jpg\",\"2016-03-27 151939.jpg\",\"2016-03-27 152005.jpg\",\"2016-03-27 152148.jpg\",\"2016-03-27 152237.jpg\",\"2016-03-27 152259.jpg\",\"2016-03-27 152819(1).jpg\",\"2016-03-27 152819.jpg\",\"2016-03-27 152821(1).jpg\",\"2016-03-27 152821.jpg\",\"2016-03-27 153506.jpg\",\"2016-03-27 154141.jpg\",\"2016-03-27 154200.jpg\",\"2016-03-27 155001.jpg\",\"2016-03-27 155019.jpg\",\"2016-03-27 155032.jpg\",\"2016-03-27 155116.jpg\",\"2016-03-27 161246.jpg\",\"2016-03-27 162141(1).jpg\",\"2016-03-27 162141.jpg\",\"2016-03-27 162146.jpg\",\"2016-03-27 162158.jpg\",\"2016-03-27 162808.jpg\",\"2016-03-27 162832.jpg\",\"2016-03-27 163801.jpg\",\"2016-03-27 163813(1).jpg\",\"2016-03-27 163813.jpg\",\"2016-03-27 163818(1).jpg\",\"2016-03-27 163818.jpg\",\"2016-03-27 163848.jpg\",\"2016-03-28 083903.png\",\"2016-03-29 081114.jpg\",\"2016-03-30 151649.png\",\"2016-04-01 175130(1).jpg\",\"2016-04-01 175130.jpg\",\"2016-04-01 175133.jpg\",\"2016-04-01 181043.jpg\",\"2016-04-01 183104.jpg\",\"2016-04-01 213821.png\",\"2016-04-01 213829.png\",\"2016-04-01 222511.jpg\",\"2016-04-02 131707.jpg\",\"2016-04-02 131717.jpg\",\"2016-04-02 131720.jpg\",\"2016-04-02 131723.jpg\",\"2016-04-02 132921.jpg\",\"2016-04-02 132931.jpg\",\"2016-04-02 132943.jpg\",\"2016-04-02 140022.jpg\",\"2016-04-02 140033(1).jpg\",\"2016-04-02 140033.jpg\",\"2016-04-02 140052.jpg\",\"2016-04-02 151842.jpg\",\"2016-04-02 151846.jpg\",\"2016-04-02 151847.jpg\",\"2016-04-02 152043.jpg\",\"2016-04-02 152112.jpg\",\"2016-04-02 152235.jpg\",\"2016-04-02 152428.jpg\",\"2016-04-03 095125.jpg\",\"2016-04-03 095137.jpg\",\"2016-04-03 095138.jpg\",\"2016-04-03 095144.jpg\",\"2016-04-03 101508.jpg\",\"2016-04-03 101535.jpg\",\"2016-04-03 101559.jpg\",\"2016-04-03 101633.jpg\",\"2016-04-03 101639.jpg\",\"2016-04-03 102304.jpg\",\"2016-04-03 102615(1).jpg\",\"2016-04-03 102615.jpg\",\"2016-04-03 102620(1).jpg\",\"2016-04-03 102620.jpg\",\"2016-04-03 102627.jpg\",\"2016-04-03 102655.jpg\",\"2016-04-03 102657.jpg\",\"2016-04-03 102740.jpg\",\"2016-04-03 102752.jpg\",\"2016-04-03 102804.jpg\",\"2016-04-03 102805.jpg\",\"2016-04-03 103817.jpg\",\"2016-04-03 103818.jpg\",\"2016-04-03 103827(1).jpg\",\"2016-04-03 103827.jpg\",\"2016-04-03 103850.jpg\",\"2016-04-03 104018.jpg\",\"2016-04-03 104130.jpg\",\"2016-04-03 104352.jpg\",\"2016-04-03 104423.jpg\",\"2016-04-03 104515.jpg\",\"2016-04-03 104524.jpg\",\"2016-04-03 105732.jpg\",\"2016-04-03 105827.jpg\",\"2016-04-03 105914.jpg\",\"2016-04-03 105924.jpg\",\"2016-04-03 105957(1).jpg\",\"2016-04-03 105957.jpg\",\"2016-04-03 110001.jpg\",\"2016-04-03 110002.jpg\",\"2016-04-03 110233.jpg\",\"2016-04-03 110304.jpg\",\"2016-04-03 110607.jpg\",\"2016-04-03 110613.jpg\",\"2016-04-03 110648.jpg\",\"2016-04-03 110729.jpg\",\"2016-04-03 111013.jpg\",\"2016-04-03 111019.jpg\",\"2016-04-03 111049.jpg\",\"2016-04-03 111628.jpg\",\"2016-04-03 111830.jpg\",\"2016-04-03 111935.jpg\",\"2016-04-03 112202.jpg\",\"2016-04-03 112752.jpg\",\"2016-04-03 112809.jpg\",\"2016-04-03 112832.jpg\",\"2016-04-03 112834(1).jpg\",\"2016-04-03 112834.jpg\",\"2016-04-03 112836.jpg\",\"2016-04-03 113040.jpg\",\"2016-04-03 113713(1).jpg\",\"2016-04-03 113713.jpg\",\"2016-04-03 113757(1).jpg\",\"2016-04-03 113757.jpg\",\"2016-04-03 115043.jpg\",\"2016-04-03 115550.jpg\",\"2016-04-03 115552(1).jpg\",\"2016-04-03 115552.jpg\",\"2016-04-03 115558(1).jpg\",\"2016-04-03 115558.jpg\",\"2016-04-03 115727.jpg\",\"2016-04-03 115729(1).jpg\",\"2016-04-03 115729.jpg\",\"2016-04-03 115734.jpg\",\"2016-04-03 115737.jpg\",\"2016-04-03 115811(1).jpg\",\"2016-04-03 115811.jpg\",\"2016-04-03 120239.jpg\",\"2016-04-03 120352(1).jpg\",\"2016-04-03 120352.jpg\",\"2016-04-03 120750.jpg\",\"2016-04-03 120809.jpg\",\"2016-04-03 120900.jpg\",\"2016-04-03 120906.jpg\",\"2016-04-03 121212.jpg\",\"2016-04-03 121625.jpg\",\"2016-04-03 122354.jpg\",\"2016-04-03 122801.jpg\",\"2016-04-03 125847.jpg\",\"2016-04-03 125852.jpg\",\"2016-04-03 125853.jpg\",\"2016-04-03 125905.jpg\",\"2016-04-03 130722.jpg\",\"2016-04-03 131142.jpg\",\"2016-04-03 131150(1).jpg\",\"2016-04-03 131150.jpg\",\"2016-04-03 144242.png\",\"2016-04-03 144250.png\",\"2016-04-03 145654(1).jpg\",\"2016-04-03 145654.jpg\",\"2016-04-07 192034.jpg\",\"2016-04-11 172450.jpg\",\"2016-04-11 172459.jpg\",\"2016-04-11 172506.jpg\",\"2016-04-11 172508.jpg\",\"2016-04-15 152402.jpg\",\"2016-04-15 202035.jpg\",\"2016-04-15 202036.jpg\",\"2016-04-15 204303.jpg\",\"2016-04-15 204307.jpg\",\"2016-04-15 204312.jpg\",\"2016-04-15 211600.jpg\",\"2016-04-15 211601.jpg\",\"2016-04-15 213022.jpg\",\"2016-04-17 080020.jpg\",\"2016-04-17 080025.jpg\",\"2016-04-17 080033.jpg\",\"2016-04-17 080231.jpg\",\"2016-04-17 080240.jpg\",\"2016-04-17 080538.jpg\",\"2016-04-17 080938.jpg\",\"2016-04-17 081654.jpg\",\"2016-04-17 081701.jpg\",\"2016-04-17 081710.jpg\",\"2016-04-17 081714.jpg\",\"2016-04-17 081717.jpg\",\"2016-04-17 081719.jpg\",\"2016-04-17 082329.jpg\",\"2016-04-17 082336.jpg\",\"2016-04-17 083404.jpg\",\"2016-04-17 083416.jpg\",\"2016-04-17 084412.jpg\",\"2016-04-17 084416.jpg\",\"2016-04-17 091254.jpg\",\"2016-04-17 091303.jpg\",\"2016-04-17 091510.jpg\",\"2016-04-17 093140.jpg\",\"2016-04-17 093142.jpg\",\"2016-04-17 093147.jpg\",\"2016-04-17 093156.jpg\",\"2016-04-17 093201.jpg\",\"2016-04-17 093728.jpg\",\"2016-04-17 093732.jpg\",\"2016-04-17 093748.jpg\",\"2016-04-17 093755.jpg\",\"2016-04-17 093757.jpg\",\"2016-04-17 093830.jpg\",\"2016-04-17 093837.jpg\",\"2016-04-17 094445(1).jpg\",\"2016-04-17 094445.jpg\",\"2016-04-17 094819(1).jpg\",\"2016-04-17 094819.jpg\",\"2016-04-17 094852.jpg\",\"2016-04-17 100005.jpg\",\"2016-04-17 100013.jpg\",\"2016-04-17 100310.jpg\",\"2016-04-17 100506.jpg\",\"2016-04-17 100514.jpg\",\"2016-04-17 100648.jpg\",\"2016-04-17 100834.jpg\",\"2016-04-17 101129(1).jpg\",\"2016-04-17 101129.jpg\",\"2016-04-17 101205(1).jpg\",\"2016-04-17 101205.jpg\",\"2016-04-17 101215(1).jpg\",\"2016-04-17 101548.jpg\",\"2016-04-17 103321.jpg\",\"2016-04-17 103326.jpg\",\"2016-04-17 103508.jpg\",\"2016-04-17 105138.jpg\",\"2016-04-17 105141.jpg\",\"2016-04-17 105152.jpg\",\"2016-04-17 105241.jpg\",\"2016-04-17 105524.jpg\",\"2016-04-17 105536.jpg\",\"2016-04-17 105557.jpg\",\"2016-04-17 105600.jpg\",\"2016-04-17 105610.jpg\",\"2016-04-17 105614.jpg\",\"2016-04-17 105617.jpg\",\"2016-04-17 105755.jpg\",\"2016-04-17 110321.jpg\",\"2016-04-17 110438(1).jpg\",\"2016-04-17 110438.jpg\",\"2016-04-17 110740.jpg\",\"2016-04-17 110751.jpg\",\"2016-04-17 111100.jpg\",\"2016-04-17 111138(1).jpg\",\"2016-04-17 111138.jpg\",\"2016-04-17 111433.jpg\",\"2016-04-17 111440.jpg\",\"2016-04-23 111804.jpg\",\"2016-04-23 111806.jpg\",\"2016-04-23 111820.jpg\",\"2016-04-23 111910.jpg\",\"2016-04-23 111915.jpg\",\"2016-04-23 111917.jpg\",\"2016-04-23 111926.jpg\",\"2016-04-23 111932.jpg\",\"2016-04-23 111958.jpg\",\"2016-04-23 112019.jpg\",\"2016-04-23 112723(1).jpg\",\"2016-04-23 112723.jpg\",\"2016-04-23 112725.jpg\",\"2016-04-23 112726.jpg\",\"2016-04-23 112727.jpg\",\"2016-04-23 112729.jpg\",\"2016-04-23 112733.jpg\",\"2016-04-23 112736.jpg\",\"2016-04-23 112738.jpg\",\"2016-04-23 115558.jpg\",\"2016-04-23 120732.jpg\",\"2016-04-23 121259.jpg\",\"2016-04-23 121301.jpg\",\"2016-04-23 121401.jpg\",\"2016-04-23 121405.jpg\",\"2016-04-23 121412.jpg\",\"2016-04-23 121416.jpg\",\"2016-04-23 121419.jpg\",\"2016-04-23 121420.jpg\",\"2016-04-23 123234.jpg\",\"2016-04-23 123235.jpg\",\"2016-04-23 123252.jpg\",\"2016-04-23 131159.jpg\",\"2016-04-23 131633.jpg\",\"2016-04-23 131945.jpg\",\"2016-04-23 132925.jpg\",\"2016-04-23 140724.jpg\",\"2016-04-23 140730.jpg\",\"2016-04-23 144922.jpg\",\"2016-04-23 144924(1).jpg\",\"2016-04-23 144924.jpg\",\"2016-04-23 145225.jpg\",\"2016-04-26 203324.jpg\",\"2016-04-30 193345.jpg\",\"2016-04-30 193403.jpg\",\"2016-05-05 201023.png\",\"2016-05-05 210509.jpg\",\"2016-05-06 110741.jpg\",\"2016-05-06 110812.jpg\",\"2016-05-07 200314.jpg\",\"2016-05-07 201531.jpg\",\"2016-05-07 202010.jpg\",\"2016-05-08 203507.jpg\",\"2016-05-08 203522.jpg\",\"2016-05-08 203652.jpg\",\"2016-05-08 203654.jpg\",\"2016-05-08 203658.jpg\",\"2016-05-08 203701.jpg\",\"2016-05-08 203734.jpg\",\"2016-05-08 203805.jpg\",\"2016-05-08 203807.jpg\",\"2016-05-08 203849(1).jpg\",\"2016-05-08 203849.jpg\",\"2016-05-10 210535.jpg\",\"2016-05-13 124114.jpg\",\"2016-05-13 193600.jpg\",\"2016-05-13 193606.jpg\",\"2016-05-14 093117.jpg\",\"2016-05-14 103012.png\",\"2016-05-14 103418.jpg\",\"2016-05-16 115827.jpg\",\"2016-05-21 192233.jpg\",\"2016-05-23 191800.jpg\",\"2016-05-23 191830.jpg\",\"2016-05-23 191943.jpg\",\"2016-05-24 210208.jpg\",\"2016-05-25 205517.jpg\",\"2016-05-27 172927.jpg\",\"2016-05-27 173013.jpg\",\"2016-05-28 002326.jpg\",\"2016-05-28 002341.jpg\",\"2016-05-28 002352.jpg\",\"2016-05-28 002357.jpg\",\"2016-05-28 002410.jpg\",\"2016-05-28 002417.jpg\",\"2016-05-28 002428.jpg\",\"2016-05-28 002441.jpg\",\"2016-05-28 002447.jpg\",\"2016-05-30 191852.jpg\",\"2016-05-30 191914.jpg\",\"2016-06-06 110901.jpg\",\"2016-06-10 175603.jpg\",\"2016-06-11 152628.jpg\",\"2016-06-14 203815.jpg\",\"2016-06-14 203837.jpg\",\"2016-06-14 203841.jpg\",\"2016-06-14 203844.jpg\",\"2016-06-14 205021.jpg\",\"2016-06-15 191707.jpg\",\"2016-06-16 172752.jpg\",\"2016-06-16 183943.jpg\",\"2016-06-18 095238.jpg\",\"2016-06-20 161440.jpg\",\"2016-06-20 161810.png\",\"2016-07-01 081125.png\",\"2016-07-03 123722.jpg\",\"2016-07-03 134018(1).jpg\",\"2016-07-03 134018.jpg\",\"2016-07-03 134023.jpg\",\"2016-07-03 155605.jpg\",\"2016-07-03 155727.jpg\",\"2016-07-03 155856.jpg\",\"2016-07-03 155858.jpg\",\"2016-07-04 100805.jpg\",\"2016-07-04 175914.jpg\",\"2016-07-04 175926.jpg\",\"2016-07-14 114258.jpg\",\"2016-07-17 182500.jpg\",\"2016-07-20 141632(1).jpg\",\"2016-07-20 141632.jpg\",\"2016-07-20 141645.jpg\",\"2016-07-25 191800.jpg\",\"2016-07-31 132637.jpg\",\"2016-08-08 130727.jpg\",\"2016-08-09 123636.png\",\"2016-08-09 123954.png\",\"2016-08-17 192702.jpg\",\"2016-08-18 081701.jpg\",\"2016-08-19 175948.jpg\",\"2016-08-19 204856.jpg\",\"2016-08-19 204908.jpg\",\"2016-08-19 204911.jpg\",\"2016-08-29 212941.jpg\",\"2016-08-31 185304.jpg\",\"2016-09-02 074431.jpg\",\"2016-09-02 224202.jpg\",\"2016-09-07 184846.jpg\",\"2016-09-07 184850.jpg\",\"2016-09-07 184854.jpg\",\"2016-09-07 185500.jpg\",\"2016-09-07 190855.jpg\",\"2016-09-07 190908.jpg\",\"2016-09-07 190959.jpg\",\"2016-09-07 191021.jpg\",\"2016-09-07 191028.jpg\",\"2016-09-07 191113.jpg\",\"2016-09-07 191133.jpg\",\"2016-09-07 213936.jpg\",\"2016-09-08 184916.jpg\",\"2016-09-08 184919.jpg\",\"2016-09-08 185012.jpg\",\"2016-09-13 163714.jpg\",\"2016-09-13 173913.jpg\",\"2016-09-16 141208.jpg\",\"2016-09-16 141700.jpg\",\"2016-09-16 144112.jpg\",\"2016-09-16 144515.jpg\",\"2016-09-16 144608.jpg\",\"2016-09-16 145441.jpg\",\"2016-09-16 145556.jpg\",\"2016-09-16 145848.jpg\",\"2016-09-16 150520.jpg\",\"2016-09-16 151448.jpg\",\"2016-09-16 151502.jpg\",\"2016-09-16 152531.jpg\",\"2016-09-16 155011.jpg\",\"2016-09-16 161507.jpg\",\"2016-09-16 163752.jpg\",\"2016-09-16 165815.jpg\",\"2016-09-16 193425.jpg\",\"2016-09-16 213043.jpg\",\"2016-09-16 213045.jpg\",\"2016-09-26 195951.jpg\",\"2016-09-26 204332.jpg\",\"2016-09-27 102023.jpg\",\"2016-09-28 095535.jpg\",\"2016-09-28 191914.jpg\",\"2016-09-28 192103.jpg\",\"2016-10-01 194903.jpg\",\"2016-10-05 111452.jpg\",\"2016-10-05 113806.jpg\",\"2016-10-05 114550.jpg\",\"2016-10-05 114837.jpg\",\"2016-10-05 115014.jpg\",\"2016-10-05 115355.jpg\",\"2016-10-05 115530.jpg\",\"2016-10-05 115539.jpg\",\"2016-10-05 115546.jpg\",\"2016-10-05 115641.jpg\",\"2016-10-05 115646.jpg\",\"2016-10-05 115744.jpg\",\"2016-10-05 115756.jpg\",\"2016-10-05 120556.jpg\",\"2016-10-05 120645.jpg\",\"2016-10-05 120756.jpg\",\"2016-10-05 122246.jpg\",\"2016-10-05 122919.jpg\",\"2016-10-05 123348.jpg\",\"2016-10-05 123407.jpg\",\"2016-10-05 123729.jpg\",\"2016-10-05 124806.jpg\",\"2016-10-05 124906.jpg\",\"2016-10-05 124916.jpg\",\"2016-10-05 125345.jpg\",\"2016-10-05 125356.jpg\",\"2016-10-05 130230.jpg\",\"2016-10-05 130239.jpg\",\"2016-10-05 130301.jpg\",\"2016-10-05 133018.jpg\",\"2016-10-05 133021.jpg\",\"2016-10-05 165604.jpg\",\"2016-10-10 095722.jpg\",\"2016-10-19 215109.jpg\",\"2016-10-19 215114.jpg\",\"2016-10-23 152052.jpg\",\"2016-10-28 192743.jpg\",\"2016-10-29 132229(1).jpg\",\"2016-10-29 132229.jpg\",\"2016-10-29 132301.jpg\",\"2016-10-29 133114.jpg\",\"2016-10-29 133300.jpg\",\"2016-10-29 134936.jpg\",\"2016-10-29 143608.jpg\",\"2016-10-29 143628.jpg\",\"2016-10-29 143820.jpg\",\"2016-10-29 145835(1).jpg\",\"2016-10-29 145835.jpg\",\"2016-10-29 154811.jpg\",\"2016-10-29 160459.jpg\",\"2016-10-29 160517.jpg\",\"2016-10-29 160652.jpg\",\"2016-10-29 161158.jpg\",\"2016-10-29 161223.jpg\",\"2016-10-29 161737.jpg\",\"2016-10-29 161744.jpg\",\"2016-10-29 161759.jpg\",\"2016-10-29 161816.jpg\",\"2016-10-29 161822.jpg\",\"2016-10-29 164224.jpg\",\"2016-10-29 164237.jpg\",\"2016-10-29 164245.jpg\",\"2016-10-29 164249.jpg\",\"2016-10-29 164303.jpg\",\"2016-10-29 164613.jpg\",\"2016-10-29 165546.jpg\",\"2016-10-29 172949.jpg\",\"2016-10-29 180001.jpg\",\"2016-11-04 195753.jpg\",\"2016-11-04 195905.jpg\",\"2016-11-07 123405.jpg\",\"2016-11-07 174342.jpg\",\"2016-11-07 174359.jpg\",\"2016-11-07 174412.jpg\",\"2016-11-09 132613.jpg\",\"2016-11-09 145847.jpg\",\"2016-11-11 185746.jpg\",\"2016-11-11 194840.jpg\",\"2016-11-11 194858.jpg\",\"2016-11-11 195343.jpg\",\"2016-11-11 195353.jpg\",\"2016-11-11 195604.jpg\",\"2016-11-11 200216.jpg\",\"2016-11-11 200238.jpg\",\"2016-11-11 201202.jpg\",\"2016-11-11 201303.jpg\",\"2016-11-11 201721.jpg\",\"2016-11-11 201830.jpg\",\"2016-11-11 202037.jpg\",\"2016-11-11 202048.jpg\",\"2016-11-11 202148.jpg\",\"2016-11-11 202158.jpg\",\"2016-11-17 211824.jpg\",\"IMG20161210100302.jpg\",\"IMG_20161121_205426.jpg\",\"IMG_20161121_205439.jpg\",\"IMG_20161121_205511.jpg\",\"IMG_20161121_210056.jpg\",\"IMG_20161121_210143.jpg\",\"IMG_20161130_195212.jpg\",\"IMG_20161130_213456.jpg\",\"IMG_20161130_213556.jpg\",\"IMG_20161130_214713.jpg\",\"IMG_20161130_215920.jpg\",\"IMG_20161130_220446.jpg\",\"IMG_20161204_120306.jpg\",\"IMG_20161205_160943.jpg\",\"IMG_20161207_133736.jpg\",\"IMG_20161208_173225.jpg\",\"IMG_20161211_145922.jpg\",\"IMG_20161216_224436.jpg\",\"IMG_20161223_180832.jpg\",\"IMG_20161224_151707.jpg\",\"IMG_20161224_152222.jpg\",\"IMG_20161224_162256.jpg\",\"IMG_20161224_165235.jpg\",\"IMG_20161224_170428.jpg\",\"IMG_20161224_170523.jpg\",\"IMG_20161224_170619.jpg\",\"IMG_20161224_170658.jpg\",\"IMG_20161224_170717.jpg\",\"IMG_20161224_170750.jpg\",\"IMG_20161224_170822.jpg\",\"IMG_20161224_170923.jpg\",\"IMG_20161224_171008.jpg\",\"IMG_20161224_171038.jpg\",\"IMG_20161224_171107.jpg\",\"IMG_20161224_171339.jpg\",\"IMG_20161224_171518.jpg\",\"IMG_20161224_171713.jpg\",\"IMG_20161224_171929.jpg\",\"IMG_20161224_172653.jpg\",\"IMG_20161224_172737.jpg\",\"IMG_20161224_173736.jpg\",\"IMG_20161224_173928.jpg\",\"IMG_20161224_173948.jpg\",\"IMG_20161224_174014.jpg\",\"IMG_20161224_174639.jpg\",\"IMG_20161224_174830.jpg\",\"IMG_20170104_155154.jpg\",\"IMG_20170104_155242.jpg\",\"IMG_20170104_155431.jpg\",\"IMG_20170110_084119.jpg\",\"IMG_20170110_084128.jpg\",\"IMG_20170110_084217.jpg\",\"IMG_20170110_084932.jpg\",\"IMG_20170112_152423.jpg\",\"IMG_20170112_190241.jpg\",\"IMG_20170113_084613.jpg\",\"IMG_20170113_084626.jpg\",\"IMG_20170113_174425.jpg\",\"IMG_20170113_221058.jpg\",\"IMG_20170114_161551.jpg\",\"IMG_20170114_164419.jpg\",\"IMG_20170114_164629.jpg\",\"IMG_20170118_113604.jpg\",\"IMG_20170119_083228.jpg\",\"IMG_20170123_165636.jpg\",\"IMG_20170123_165653.jpg\",\"IMG_20170127_100418.jpg\",\"IMG_20170127_190715.jpg\",\"IMG_20170131_192554.jpg\",\"IMG_20170131_192558.jpg\",\"IMG_20170131_192610.jpg\",\"IMG_20170131_192624.jpg\",\"IMG_20170131_192702.jpg\",\"IMG_20170131_192908.jpg\",\"IMG_20170131_192952.jpg\",\"IMG_20170131_193023.jpg\",\"IMG_20170131_193104.jpg\",\"IMG_20170131_193124.jpg\",\"IMG_20170131_193256.jpg\",\"IMG_20170131_193423.jpg\",\"IMG_20170131_193431.jpg\",\"IMG_20170131_195233.jpg\",\"IMG_20170131_195717.jpg\",\"IMG_20170131_195846.jpg\",\"IMG_20170131_200706.jpg\",\"IMG_20170131_200945.jpg\",\"IMG_20170131_201457.jpg\",\"IMG_20170204_215130.jpg\",\"IMG_20170211_183508.jpg\",\"IMG_20170211_183636.jpg\",\"IMG_20170215_135100.JPG\",\"haha.jpg\",\"5835570118d7f.jpg\",\"mmexport1488696575846.jpg\",\"mmexport1488696600603.jpg\",\"mmexport1488696609850.jpg\",\"mmexport1488702760689.jpg\",\"mmexport1488711150359.jpg\",\"mmexport1488711545675.jpg\",\"mmexport1488796776023.jpg\",\"mmexport1488796779479.jpg\",\"mmexport1488796785258.jpg\",\"mmexport1488696575846.jpg\",\"mmexport1488696600603.jpg\",\"mmexport1488696609850.jpg\",\"mmexport1488702760689.jpg\",\"mmexport1488711150359.jpg\",\"mmexport1488711545675.jpg\",\"mmexport1488796776023.jpg\",\"mmexport1488796779479.jpg\",\"mmexport1488796785258.jpg\",\"mmexport1488696575846.jpg\",\"mmexport1488696600603.jpg\",\"mmexport1488696609850.jpg\",\"mmexport1488702760689.jpg\",\"mmexport1488711150359.jpg\",\"mmexport1488711545675.jpg\",\"mmexport1488796776023.jpg\",\"mmexport1488796779479.jpg\",\"mmexport1488796785258.jpg\",\"Screenshot_2017-02-19-08-31-53.png\",\"mmexport1488867615764.jpg\",\"mmexport1488867623607.jpg\",\"mmexport1488867632602.jpg\",\"mmexport1488867679463.jpg\",\"mmexport1488867684585.jpg\",\"mmexport1488867690591.jpg\",\"mmexport1488867700718.jpg\",\"mmexport1488867724787.jpg\",\"IMG_20170307_075341.jpg\",\"IMG_20170308_172128.jpg\",\"IMG_20170308_195237.jpg\",\"IMG_20170308_195916.jpg\",\"IMG_20170309_091722.jpg\",\"IMG_20170309_091725.jpg\",\"IMG_20170309_113152.jpg\",\"IMG_20170309_114112.jpg\",\"IMG_20170309_114704.jpg\",\"IMG_20170312_174653.jpg\",\"IMG_20170312_180351.jpg\",\"IMG_20170312_225505.jpg\",\"IMG_20170312_230031.jpg\",\"IMG_20170313_024005.jpg\",\"IMG_20170313_091447.jpg\",\"IMG_20170313_094155.jpg\",\"IMG_20170313_094619.jpg\",\"IMG_20170313_094713.jpg\",\"IMG_20170313_094745.jpg\",\"IMG_20170313_094756.jpg\",\"IMG_20170313_115307.jpg\",\"IMG_20170313_124055.jpg\",\"IMG_20170313_130127.jpg\",\"IMG_20170313_130149.jpg\",\"IMG_20170313_142830.jpg\",\"IMG_20170313_143037.jpg\",\"IMG_20170313_143046.jpg\",\"IMG_20170313_143052.jpg\",\"IMG_20170313_144222.jpg\",\"IMG_20170313_144224.jpg\",\"IMG_20170313_144227.jpg\",\"IMG_20170313_144228.jpg\",\"IMG_20170313_144245.jpg\",\"IMG_20170313_144246.jpg\",\"IMG_20170313_144248.jpg\",\"IMG_20170313_144250.jpg\",\"IMG_20170313_144251.jpg\",\"IMG_20170313_144255.jpg\",\"IMG_20170313_145243.jpg\",\"IMG_20170313_145246_1.jpg\",\"IMG_20170313_145251.jpg\",\"IMG_20170313_145254.jpg\",\"IMG_20170313_145852.jpg\",\"IMG_20170313_145933.jpg\",\"IMG_20170313_145935.jpg\",\"IMG_20170313_150000.jpg\",\"IMG_20170313_150027.jpg\",\"IMG_20170313_150030.jpg\",\"IMG_20170313_150325.jpg\",\"IMG_20170313_150328.jpg\",\"IMG_20170313_151121.jpg\",\"IMG_20170313_153852.jpg\",\"IMG_20170313_153908.jpg\",\"IMG_20170313_161020.jpg\",\"IMG_20170313_161113.jpg\",\"IMG_20170313_161115.jpg\",\"IMG_20170313_161242.jpg\",\"IMG_20170313_161246.jpg\",\"IMG_20170313_161403.jpg\",\"IMG_20170313_161406.jpg\",\"IMG_20170313_161409.jpg\",\"IMG_20170313_161414.jpg\",\"IMG_20170313_161611.jpg\",\"IMG_20170313_161618.jpg\",\"IMG_20170313_161707.jpg\",\"IMG_20170313_161717.jpg\",\"IMG_20170313_161720.jpg\",\"IMG_20170313_161730.jpg\",\"IMG_20170313_161921.jpg\",\"IMG_20170313_161928.jpg\",\"IMG_20170313_161933.jpg\",\"IMG_20170313_161940.jpg\",\"IMG_20170313_161944.jpg\",\"IMG_20170313_161946.jpg\",\"IMG_20170313_162651.jpg\",\"IMG_20170313_162901.jpg\",\"IMG_20170313_162934.jpg\",\"IMG_20170313_162959.jpg\",\"IMG_20170313_163024.jpg\",\"IMG_20170313_163107.jpg\",\"IMG_20170313_163227.jpg\",\"IMG_20170313_163250.jpg\",\"IMG_20170313_163815.jpg\",\"IMG_20170313_163917.jpg\",\"IMG_20170313_164150.jpg\",\"IMG_20170313_164411.jpg\",\"IMG_20170313_164432.jpg\",\"IMG_20170313_164447.jpg\",\"IMG_20170313_164453.jpg\",\"IMG_20170313_164505.jpg\",\"IMG_20170313_164516.jpg\",\"IMG_20170313_164612.jpg\",\"IMG_20170313_164627.jpg\",\"IMG_20170313_164706.jpg\",\"IMG_20170313_164732.jpg\",\"IMG_20170313_171425.jpg\",\"IMG_20170313_171440.jpg\",\"IMG_20170313_175227.jpg\",\"IMG_20170313_175305.jpg\",\"IMG_20170313_181748.jpg\",\"IMG_20170313_181835.jpg\",\"IMG_20170313_181902.jpg\",\"IMG_20170313_181923.jpg\",\"IMG_20170313_181938.jpg\",\"IMG_20170313_182111.jpg\",\"IMG_20170313_182211.jpg\",\"IMG_20170313_182222.jpg\",\"IMG_20170313_182241.jpg\",\"IMG_20170313_182250.jpg\",\"IMG_20170313_182410.jpg\",\"IMG_20170313_182425.jpg\",\"IMG_20170313_182428.jpg\",\"IMG_20170313_184728.jpg\",\"IMG_20170313_203149.jpg\",\"IMG_20170313_203236.jpg\",\"IMG_20170313_205117.jpg\",\"IMG_20170313_205130.jpg\",\"IMG_20170313_205532.jpg\",\"IMG_20170313_205744.jpg\",\"IMG_20170313_205829.jpg\",\"IMG_20170313_205854.jpg\",\"IMG_20170313_205949.jpg\",\"IMG_20170313_210020.jpg\",\"IMG_20170313_210140.jpg\",\"IMG_20170313_210205.jpg\",\"IMG_20170313_210252.jpg\",\"IMG_20170313_210257.jpg\",\"IMG_20170313_211951.jpg\",\"IMG_20170313_212013.jpg\",\"IMG_20170313_212127.jpg\",\"IMG_20170313_213242.jpg\",\"IMG_20170313_213639.jpg\",\"IMG_20170313_214722.jpg\",\"IMG_20170314_111128.jpg\",\"IMG_20170314_111141.jpg\",\"IMG_20170314_111713.jpg\",\"IMG_20170314_111745.jpg\",\"IMG_20170314_162706.jpg\",\"IMG_20170314_162831.jpg\",\"IMG_20170314_205002.jpg\",\"IMG_20170314_211123.jpg\",\"IMG_20170314_212431.jpg\",\"IMG_20170314_214421.jpg\",\"IMG_20170314_221925.jpg\",\"IMG_20170314_222009.jpg\",\"IMG_20170314_223355.jpg\",\"IMG_20170314_223659.jpg\",\"IMG_20170314_223811.jpg\",\"IMG_20170314_223957.jpg\",\"IMG_20170314_224108.jpg\",\"IMG_20170314_224635.jpg\",\"IMG_20170314_224703.jpg\",\"IMG_20170314_224750.jpg\",\"IMG_20170315_095231.jpg\",\"IMG_20170315_110130.jpg\",\"IMG_20170315_110954.jpg\",\"IMG_20170315_111003.jpg\",\"IMG_20170315_111005.jpg\",\"IMG_20170315_111007.jpg\",\"IMG_20170315_111020.jpg\",\"IMG_20170315_111020_1.jpg\",\"IMG_20170315_111021.jpg\",\"IMG_20170315_111027.jpg\",\"IMG_20170315_111028.jpg\",\"IMG_20170315_111132.jpg\",\"IMG_20170315_111738.jpg\",\"IMG_20170315_111901.jpg\",\"IMG_20170315_111918.jpg\",\"IMG_20170315_111928.jpg\",\"IMG_20170315_111944.jpg\",\"IMG_20170315_112005.jpg\",\"IMG_20170315_112110.jpg\",\"IMG_20170315_112144.jpg\",\"IMG_20170315_112204.jpg\",\"IMG_20170315_112212.jpg\",\"IMG_20170315_112230.jpg\",\"IMG_20170315_112237.jpg\",\"IMG_20170315_112240.jpg\",\"IMG_20170315_112318.jpg\",\"IMG_20170315_112323.jpg\",\"IMG_20170315_112355.jpg\",\"IMG_20170315_112420.jpg\",\"IMG_20170315_112434.jpg\",\"IMG_20170315_112600.jpg\",\"IMG_20170315_112656.jpg\",\"IMG_20170315_112717.jpg\",\"IMG_20170315_112723.jpg\",\"IMG_20170315_112806.jpg\",\"IMG_20170315_113336.jpg\",\"IMG_20170315_113449.jpg\",\"IMG_20170315_113748.jpg\",\"IMG_20170315_113809.jpg\",\"IMG_20170315_113826.jpg\",\"IMG_20170315_113930.jpg\",\"IMG_20170315_114008.jpg\",\"IMG_20170315_114118.jpg\",\"IMG_20170315_114513.jpg\",\"IMG_20170315_114909.jpg\",\"IMG_20170315_115830.jpg\",\"IMG_20170315_120246.jpg\",\"IMG_20170315_120441.jpg\",\"IMG_20170315_120954.jpg\",\"IMG_20170315_121533.jpg\",\"IMG_20170315_121809.jpg\",\"IMG_20170315_121938.jpg\",\"IMG_20170315_122224.jpg\",\"IMG_20170315_123048.jpg\",\"IMG_20170315_123054.jpg\",\"IMG_20170315_123843.jpg\",\"IMG_20170315_123900.jpg\",\"IMG_20170315_123909.jpg\",\"IMG_20170315_123928.jpg\",\"IMG_20170315_123938.jpg\",\"IMG_20170315_123955.jpg\",\"IMG_20170315_124212.jpg\",\"IMG_20170315_130435.jpg\",\"IMG_20170315_131826.jpg\",\"IMG_20170315_133026.jpg\",\"IMG_20170315_150922.jpg\",\"IMG_20170315_154627.jpg\",\"IMG_20170315_154631.jpg\",\"IMG_20170315_163022.jpg\",\"IMG_20170315_194035.jpg\",\"IMG_20170315_194938.jpg\",\"IMG_20170315_195132.jpg\",\"IMG_20170315_195143.jpg\",\"IMG_20170315_195411.jpg\",\"IMG_20170315_195435.jpg\",\"IMG_20170315_195532.jpg\",\"IMG_20170315_195704.jpg\",\"IMG_20170315_195843.jpg\",\"IMG_20170315_200404.jpg\",\"IMG_20170315_201001.jpg\",\"IMG_20170315_201254.jpg\",\"IMG_20170315_201324.jpg\",\"IMG_20170315_201708.jpg\",\"IMG_20170315_201717.jpg\",\"IMG_20170315_201818.jpg\",\"IMG_20170315_202049.jpg\",\"IMG_20170315_202053.jpg\",\"IMG_20170315_202151.jpg\",\"IMG_20170315_202406.jpg\",\"IMG_20170315_203001.jpg\",\"IMG_20170315_203039.jpg\",\"IMG_20170315_203107.jpg\",\"IMG_20170315_203201.jpg\",\"IMG_20170315_205201.jpg\",\"IMG_20170315_211320.jpg\",\"IMG_20170316_085311.jpg\",\"IMG_20170316_090216.jpg\",\"IMG_20170316_093020.jpg\",\"IMG_20170316_093104.jpg\",\"IMG_20170316_093109.jpg\",\"IMG_20170316_093114.jpg\",\"IMG_20170316_115437.jpg\",\"IMG_20170316_122330.jpg\",\"IMG_20170316_122532.jpg\",\"IMG_20170316_122639.jpg\",\"IMG_20170316_124351.jpg\",\"IMG_20170316_124527.jpg\",\"IMG_20170316_132245.jpg\",\"IMG_20170316_132750.jpg\",\"IMG_20170316_133243.jpg\",\"IMG_20170316_134906.jpg\",\"IMG_20170316_134917.jpg\",\"IMG_20170316_134934.jpg\",\"IMG_20170316_135108.jpg\",\"IMG_20170316_135138.jpg\",\"IMG_20170316_135308.jpg\",\"IMG_20170316_135426.jpg\",\"IMG_20170316_135502.jpg\",\"IMG_20170316_135522.jpg\",\"IMG_20170316_135528.jpg\",\"IMG_20170316_135533.jpg\",\"IMG_20170316_135657.jpg\",\"IMG_20170316_135802.jpg\",\"IMG_20170316_135812.jpg\",\"IMG_20170316_135925.jpg\",\"IMG_20170316_135935.jpg\",\"IMG_20170316_140202.jpg\",\"IMG_20170316_140215.jpg\",\"IMG_20170316_140223.jpg\",\"IMG_20170316_140234.jpg\",\"IMG_20170316_140236.jpg\",\"IMG_20170316_140237.jpg\",\"IMG_20170316_140245.jpg\",\"IMG_20170316_140257.jpg\",\"IMG_20170316_140317.jpg\",\"IMG_20170316_140330.jpg\",\"IMG_20170316_140451.jpg\",\"IMG_20170316_140605.jpg\",\"IMG_20170316_140635.jpg\",\"IMG_20170316_140642.jpg\",\"IMG_20170316_140645.jpg\",\"IMG_20170316_140647.jpg\",\"IMG_20170316_140702.jpg\",\"IMG_20170316_141325.jpg\",\"IMG_20170316_141327.jpg\",\"IMG_20170316_141330.jpg\",\"IMG_20170316_141530.jpg\",\"IMG_20170316_141539.jpg\",\"IMG_20170316_141618.jpg\",\"IMG_20170316_143641.jpg\",\"IMG_20170316_143659.jpg\",\"IMG_20170316_145806.jpg\",\"IMG_20170316_145811.jpg\",\"IMG_20170316_145814.jpg\",\"IMG_20170316_145821.jpg\",\"IMG_20170316_145827.jpg\",\"IMG_20170316_145830.jpg\",\"IMG_20170316_145908.jpg\",\"IMG_20170316_150055.jpg\",\"IMG_20170316_150110.jpg\",\"IMG_20170316_152126.jpg\",\"IMG_20170316_152251.jpg\",\"IMG_20170316_152305.jpg\",\"IMG_20170316_152309.jpg\",\"IMG_20170316_152337.jpg\",\"IMG_20170316_152526.jpg\",\"IMG_20170316_152534.jpg\",\"IMG_20170316_152538.jpg\",\"IMG_20170316_152631.jpg\",\"IMG_20170316_152650.jpg\",\"IMG_20170316_152659.jpg\",\"IMG_20170316_152704.jpg\",\"IMG_20170316_152735.jpg\",\"IMG_20170316_152831.jpg\",\"IMG_20170316_153123.jpg\",\"IMG_20170316_153148.jpg\",\"IMG_20170316_153150.jpg\",\"IMG_20170316_153158.jpg\",\"IMG_20170316_153158_1.jpg\",\"IMG_20170316_153218.jpg\",\"IMG_20170316_153416.jpg\",\"IMG_20170316_153459.jpg\",\"IMG_20170316_153502.jpg\",\"IMG_20170316_153514.jpg\",\"IMG_20170316_153734.jpg\",\"IMG_20170316_153741.jpg\",\"IMG_20170316_153758.jpg\",\"IMG_20170316_153933.jpg\",\"IMG_20170316_153949.jpg\",\"IMG_20170316_153952.jpg\",\"IMG_20170316_154023.jpg\",\"IMG_20170316_154031.jpg\",\"IMG_20170316_154034.jpg\",\"IMG_20170316_154133.jpg\",\"IMG_20170316_154232.jpg\",\"IMG_20170316_154402.jpg\",\"IMG_20170316_154405.jpg\",\"IMG_20170316_154408.jpg\",\"IMG_20170316_154423.jpg\",\"IMG_20170316_154441.jpg\",\"IMG_20170316_154456.jpg\",\"IMG_20170316_155012.jpg\",\"IMG_20170316_155308.jpg\",\"IMG_20170316_155311.jpg\",\"IMG_20170316_155454.jpg\",\"IMG_20170316_155502.jpg\",\"IMG_20170316_155700.jpg\",\"IMG_20170316_160127.jpg\",\"IMG_20170316_160201.jpg\",\"IMG_20170316_160205.jpg\",\"IMG_20170316_160224.jpg\",\"IMG_20170316_160227.jpg\",\"IMG_20170316_160510.jpg\",\"IMG_20170316_160523.jpg\",\"IMG_20170316_160531.jpg\",\"IMG_20170316_160548.jpg\",\"IMG_20170316_160555.jpg\",\"IMG_20170316_160606.jpg\",\"IMG_20170316_160717.jpg\",\"IMG_20170316_160728.jpg\",\"IMG_20170316_160753.jpg\",\"IMG_20170316_160836.jpg\",\"IMG_20170316_160939.jpg\",\"IMG_20170316_160959.jpg\",\"IMG_20170316_161011.jpg\",\"IMG_20170316_161055.jpg\",\"IMG_20170316_162228.jpg\",\"IMG_20170316_162235.jpg\",\"IMG_20170316_162249.jpg\",\"IMG_20170316_162307.jpg\",\"IMG_20170316_162309.jpg\",\"IMG_20170316_162312.jpg\",\"IMG_20170316_163801.jpg\",\"IMG_20170316_163813.jpg\",\"IMG_20170316_163820.jpg\",\"IMG_20170316_163848.jpg\",\"IMG_20170316_163901.jpg\",\"IMG_20170316_163907.jpg\",\"IMG_20170316_163924.jpg\",\"IMG_20170316_164204.jpg\",\"IMG_20170316_164221.jpg\",\"IMG_20170316_164240.jpg\",\"IMG_20170316_164303.jpg\",\"IMG_20170316_164311.jpg\",\"IMG_20170316_164332.jpg\",\"IMG_20170316_164343.jpg\",\"IMG_20170316_164353.jpg\",\"IMG_20170316_164913.jpg\",\"IMG_20170316_164921.jpg\",\"IMG_20170316_165037.jpg\",\"IMG_20170316_165122.jpg\",\"IMG_20170316_165129.jpg\",\"IMG_20170316_165131.jpg\",\"IMG_20170316_165146.jpg\",\"IMG_20170316_165149.jpg\",\"IMG_20170316_165152.jpg\",\"IMG_20170316_165154.jpg\",\"IMG_20170316_165157.jpg\",\"IMG_20170316_165200.jpg\",\"IMG_20170316_165205.jpg\",\"IMG_20170316_165209.jpg\",\"IMG_20170316_165213.jpg\",\"IMG_20170316_165215.jpg\",\"IMG_20170316_165224.jpg\",\"IMG_20170316_165229.jpg\",\"IMG_20170316_165254.jpg\",\"IMG_20170316_165304.jpg\",\"IMG_20170316_165308.jpg\",\"IMG_20170316_165313.jpg\",\"IMG_20170316_165317.jpg\",\"IMG_20170316_165733.jpg\",\"IMG_20170317_071806.jpg\",\"IMG_20170317_071814.jpg\",\"IMG_20170317_071837.jpg\",\"IMG_20170317_071901.jpg\",\"Screenshot_2017-03-08-11-03-50.png\",\"IMG_20170421_184636.jpg\",\"IMG_20170423_151825.jpg\",\"IMG_20170423_152410.jpg\",\"IMG_20170423_152521.jpg\",\"IMG_20170423_152717.jpg\",\"IMG_20170423_152850.jpg\",\"IMG_20170423_152911.jpg\",\"IMG_20170423_153635.jpg\",\"IMG_20170423_153657.jpg\",\"IMG_20170423_155930.jpg\",\"IMG_20170423_160012.jpg\",\"IMG_20170423_160048.jpg\",\"IMG_20170423_160506.jpg\",\"IMG_20170423_160528.jpg\",\"IMG_20170423_164419.jpg\",\"IMG_20170423_165159.jpg\",\"IMG_20170423_170052.jpg\",\"IMG_20170423_170843.jpg\",\"IMG_20170423_180129.jpg\",\"IMG_20170424_174241.jpg\",\"IMG_20170424_174905.jpg\",\"IMG_20170424_180225.jpg\",\"IMG_20170424_180348.jpg\",\"IMG_20170424_180922.jpg\",\"IMG_20170429_111249.jpg\",\"IMG_20170429_184810.jpg\",\"IMG_20170429_184942.jpg\",\"IMG_20170502_181837.jpg\",\"IMG_20170504_175130.jpg\",\"Screenshot_2017-04-30-21-57-54.png\",\"Screenshot_2017-04-30-21-58-18.png\",\"Screenshot_2017-04-30-21-58-23.png\",\"Screenshot_2017-04-30-21-58-27.png\",\"Screenshot_2017-04-30-21-58-32.png\",\"Screenshot_2017-04-30-21-58-39.png\",\"Screenshot_2017-04-30-21-58-40.png\",\"Screenshot_2017-04-30-21-59-00.png\",\"Screenshot_2017-04-30-21-59-15.png\",\"Screenshot_2017-04-30-21-59-24.png\",\"Screenshot_2017-04-30-21-59-28.png\"]"},{"title":"关于我","date":"2017-02-19T00:00:13.000Z","updated":"2017-05-10T01:14:22.000Z","comments":true,"path":"about/index.html","permalink":"http://www.mvtime.cn/about/index.html","excerpt":"","text":"三体小小监听员如果看到这条消息请不要回答!不要回答!不要回答!"},{"title":"links","date":"2017-02-17T11:36:10.000Z","updated":"2017-02-17T11:36:10.000Z","comments":true,"path":"links/index.html","permalink":"http://www.mvtime.cn/links/index.html","excerpt":"","text":""},{"title":"","date":"2017-11-23T01:16:16.045Z","updated":"2017-02-17T13:05:20.000Z","comments":true,"path":"photos/ins.js","permalink":"http://www.mvtime.cn/photos/ins.js","excerpt":"","text":"/******/ (function(modules) { // webpackBootstrap /******/ // The module cache /******/ var installedModules = {}; /******/ /******/ // The require function /******/ function __webpack_require__(moduleId) { /******/ /******/ // Check if module is in cache /******/ if (installedModules[moduleId]) /******/ return installedModules[moduleId].exports; /******/ /******/ // Create a new module (and put it into the cache) /******/ var module = installedModules[moduleId] = { /******/ exports: {}, /******/ id: moduleId, /******/ loaded: false /******/ }; /******/ /******/ // Execute the module function /******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); /******/ /******/ // Flag the module as loaded /******/ module.loaded = true; /******/ /******/ // Return the exports of the module /******/ return module.exports; /******/ } /******/ /******/ /******/ // expose the modules object (__webpack_modules__) /******/ __webpack_require__.m = modules; /******/ /******/ // expose the module cache /******/ __webpack_require__.c = installedModules; /******/ /******/ // __webpack_public_path__ /******/ __webpack_require__.p = \"/dist/\"; /******/ /******/ // Load entry module and return exports /******/ return __webpack_require__(0); /******/ }) /************************************************************************/ /******/ ([ /* 0 */ /***/ function(module, exports, __webpack_require__) { 'use strict'; __webpack_require__(1); var _view = __webpack_require__(2); var _view2 = _interopRequireDefault(_view); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } /** * @name impush-client * @description 这个项目让我发家致富… * @date 2016-12-1 */ var _collection = []; var _count = 0; var searchData; function addMask(elem) { var rect = elem.getBoundingClientRect(); var style = getComputedStyle(elem, null); var mask = document.createElement('i'); mask.className = 'icon-film'; mask.style.color = '#fff'; mask.style.fontSize = '26px'; mask.style.position = 'absolute'; mask.style.right = '10px'; mask.style.bottom = '10px'; mask.style.zIndex = 1; elem.parentNode.appendChild(mask); } var createVideoIncon = function createVideoIncon() { var $videoImg = document.querySelectorAll('.thumb a[data-type=\"video\"]'); for (var i = 0, len = $videoImg.length; i < len; i++) { addMask($videoImg[i]); } }; var render = function render(res) { var ulTmpl = \"\"; for (var j = res.length-1; j>=0; j--) { var data = res[j]; var liTmpl = \"\"; var src = 'http://olg4t9xt4.bkt.clouddn.com/' + data; ; var minSrc = src + '?imageView2/1/w/200/h/200/q/100&raw=true'; var target = minSrc; src += '?imageView2/1/w/800/h/800/q/100&raw=true'; var type = \"image\"; ulTmpl += '\\ \\ \\ \\ ' + \"来自七牛\" + '\\ '; } document.querySelector('.instagram').innerHTML = '' + ulTmpl + ''; createVideoIncon(); _view2.default.init(); }; var replacer = function replacer(str) { var arr = str.split(\"/\"); return \"/assets/ins/\" + arr[arr.length - 1]; }; var ctrler = function ctrler(data) { var imgObj = {}; for (var i = 0, len = data.length; i < len; i++) { var y = data[i].y; var m = data[i].m; var src = replacer(data[i].src); var text = data[i].text; var key = y + \"\" + ((m + \"\").length == 1 ? \"0\" + m : m); if (imgObj[key]) { imgObj[key].srclist.push(src); imgObj[key].text.push(text); } else { imgObj[key] = { year: y, month: m, srclist: [src], text: [text] }; } } render(imgObj); }; function loadData(success) { if (!searchData) { var xhr = new XMLHttpRequest(); xhr.open('GET', './ins.json?t=' + +new Date(), true); xhr.onload = function() { if (this.status >= 200 && this.status < 300) { var res = JSON.parse(this.response); searchData = res; success(searchData); } else { console.error(this.statusText); } }; xhr.onerror = function() { console.error(this.statusText); }; xhr.send(); } else { success(searchData); } } var Ins = { init: function init() { loadData(function(data) { render(data); }); } }; Ins.init(); // export default impush; /***/ }, /* 1 */ /***/ function(module, exports, __webpack_require__) { /* WEBPACK VAR INJECTION */ (function(global) { 'use strict'; var inViewport = __webpack_require__(3); var lazyAttrs = ['data-src']; global.lzld = lazyload(); // Provide libs using getAttribute early to get the good src // and not the fake data-src replaceGetAttribute('Image'); replaceGetAttribute('IFrame'); function registerLazyAttr(attr) { if (indexOf.call(lazyAttrs, attr) === -1) { lazyAttrs.push(attr); } } function lazyload(opts) { opts = merge({ 'offset': 333, 'src': 'data-src', 'container': false }, opts || {}); if (typeof opts.src === 'string') { registerLazyAttr(opts.src); } var elts = []; function show(elt) { var src = findRealSrc(elt); if (src) { elt.src = src; } elt.setAttribute('data-lzled', true); elts[indexOf.call(elts, elt)] = null; } function findRealSrc(elt) { if (typeof opts.src === 'function') { return opts.src(elt); } return elt.getAttribute(opts.src); } function register(elt) { elt.onload = null; elt.removeAttribute('onload'); elt.onerror = null; elt.removeAttribute('onerror'); if (indexOf.call(elts, elt) === -1) { inViewport(elt, opts, show); } } return register; } function replaceGetAttribute(elementName) { var fullname = 'HTML' + elementName + 'Element'; if (fullname in global === false) { return; } var original = global[fullname].prototype.getAttribute; global[fullname].prototype.getAttribute = function(name) { if (name === 'src') { var realSrc; for (var i = 0, max = lazyAttrs.length; i < max; i++) { realSrc = original.call(this, lazyAttrs[i]); if (realSrc) { break; } } return realSrc || original.call(this, name); } // our own lazyloader will go through theses lines // because we use getAttribute(opts.src) return original.call(this, name); }; } function merge(defaults, opts) { for (var name in defaults) { if (opts[name] === undefined) { opts[name] = defaults[name]; } } return opts; } // http://webreflection.blogspot.fr/2011/06/partial-polyfills.html function indexOf(value) { for (var i = this.length; i-- && this[i] !== value;) {} return i; } module.exports = lazyload; // export default impush; /* WEBPACK VAR INJECTION */ }.call(exports, (function() { return this; }()))) /***/ }, /* 2 */ /***/ function(module, exports) { 'use strict'; var initPhotoSwipeFromDOM = function initPhotoSwipeFromDOM(gallerySelector) { // parse slide data (url, title, size ...) from DOM elements // (children of gallerySelector) var parseThumbnailElements = function parseThumbnailElements(el) { el = el.parentNode.parentNode; var thumbElements = el.getElementsByClassName('thumb'), numNodes = thumbElements.length, items = [], figureEl, linkEl, size, type, // video or not target, item; for (var i = 0; i < numNodes; i++) { figureEl = thumbElements[i]; // // include only element nodes if (figureEl.nodeType !== 1) { continue; } linkEl = figureEl.children[0]; // size = linkEl.getAttribute('data-size').split('x'); type = linkEl.getAttribute('data-type'); target = linkEl.getAttribute('data-target'); // create slide object item = { src: linkEl.getAttribute('href'), w: parseInt(size[0], 10), h: parseInt(size[1], 10) }; if (figureEl.children.length > 1) { item.title = figureEl.children[1].innerHTML; } if (linkEl.children.length > 0) { item.msrc = linkEl.children[0].getAttribute('src'); item.type = type; item.target = target; item.html = ''; if (type === 'video') { //item.src = null; } } item.el = figureEl; // save link to element for getThumbBoundsFn items.push(item); } return items; }; // find nearest parent element var closest = function closest(el, fn) { return el && (fn(el) ? el : closest(el.parentNode, fn)); }; // triggers when user clicks on thumbnail var onThumbnailsClick = function onThumbnailsClick(e) { e = e || window.event; e.preventDefault ? e.preventDefault() : e.returnValue = false; var eTarget = e.target || e.srcElement; // find root element of slide var clickedListItem = closest(eTarget, function(el) { return el.tagName && el.tagName.toUpperCase() === 'FIGURE'; }); if (!clickedListItem) { return; } // find index of clicked item by looping through all child nodes // alternatively, you may define index via data- attribute var clickedGallery = clickedListItem.parentNode, // childNodes = clickedListItem.parentNode.childNodes, // numChildNodes = childNodes.length, childNodes = document.getElementsByClassName('thumb'), numChildNodes = childNodes.length, nodeIndex = 0, index; for (var i = 0; i < numChildNodes; i++) { if (childNodes[i].nodeType !== 1) { continue; } if (childNodes[i] === clickedListItem) { index = nodeIndex; break; } nodeIndex++; } if (index >= 0) { // open PhotoSwipe if valid index found openPhotoSwipe(index, clickedGallery); } return false; }; // parse picture index and gallery index from URL (#&pid=1&gid=2) var photoswipeParseHash = function photoswipeParseHash() { var hash = window.location.hash.substring(1), params = {}; if (hash.length < 5) { return params; } var vars = hash.split('&'); for (var i = 0; i < vars.length; i++) { if (!vars[i]) { continue; } var pair = vars[i].split('='); if (pair.length < 2) { continue; } params[pair[0]] = pair[1]; } if (params.gid) { params.gid = parseInt(params.gid, 10); } return params; }; var openPhotoSwipe = function openPhotoSwipe(index, galleryElement, disableAnimation, fromURL) { var pswpElement = document.querySelectorAll('.pswp')[0], gallery, options, items; items = parseThumbnailElements(galleryElement); // define options (if needed) options = { // define gallery index (for URL) galleryUID: galleryElement.getAttribute('data-pswp-uid'), getThumbBoundsFn: function getThumbBoundsFn(index) { // See Options -> getThumbBoundsFn section of documentation for more info var thumbnail = items[index].el.getElementsByTagName('img')[0], // find thumbnail pageYScroll = window.pageYOffset || document.documentElement.scrollTop, rect = thumbnail.getBoundingClientRect(); return { x: rect.left, y: rect.top + pageYScroll, w: rect.width }; } }; // PhotoSwipe opened from URL if (fromURL) { if (options.galleryPIDs) { // parse real index when custom PIDs are used // http://photoswipe.com/documentation/faq.html#custom-pid-in-url for (var j = 0; j < items.length; j++) { if (items[j].pid == index) { options.index = j; break; } } } else { // in URL indexes start from 1 options.index = parseInt(index, 10) - 1; } } else { options.index = parseInt(index, 10); } // exit if index not found if (isNaN(options.index)) { return; } if (disableAnimation) { options.showAnimationDuration = 0; } // Pass data to PhotoSwipe and initialize it gallery = new PhotoSwipe(pswpElement, PhotoSwipeUI_Default, items, options); gallery.init(); var $tempVideo; var stopVideoHandle = function stopVideoHandle() { if ($tempVideo) { $tempVideo.remove(); $tempVideo = null; } }; var changeHandle = function changeHandle() { var item = gallery.currItem; stopVideoHandle(); if (item.type === 'video') { var $ctn = item.container; var style = $ctn.getElementsByClassName('pswp__img')[0].style; var $video = document.createElement('video'); $video.setAttribute('autoplay', 'autoplay'); $video.setAttribute('controls', 'controls'); $video.setAttribute('src', item.target); $video.style.width = style.width; $video.style.height = style.height; $video.style.position = 'absolute'; $video.style.zIndex = 2; $tempVideo = $video; $ctn.appendChild($video); } }; gallery.listen('initialZoomIn', changeHandle); gallery.listen('afterChange', changeHandle); gallery.listen('initialZoomOut', stopVideoHandle); }; // loop through all gallery elements and bind events var galleryElements = document.querySelectorAll(gallerySelector); for (var i = 0, l = galleryElements.length; i < l; i++) { galleryElements[i].setAttribute('data-pswp-uid', i + 1); galleryElements[i].onclick = onThumbnailsClick; } // Parse URL and open gallery if it contains #&pid=3&gid=1 var hashData = photoswipeParseHash(); if (hashData.pid && hashData.gid) { openPhotoSwipe(hashData.pid, galleryElements[hashData.gid - 1], true, true); } }; var Viewer = function() { function init() { initPhotoSwipeFromDOM('.photos'); } return { init: init }; }(); module.exports = Viewer; /***/ }, /* 3 */ /***/ function(module, exports) { /* WEBPACK VAR INJECTION */ (function(global) { module.exports = inViewport; var instances = []; var supportsMutationObserver = typeof global.MutationObserver === 'function'; function inViewport(elt, params, cb) { var opts = { container: global.document.body, offset: 0 }; if (params === undefined || typeof params === 'function') { cb = params; params = {}; } var container = opts.container = params.container || opts.container; var offset = opts.offset = params.offset || opts.offset; for (var i = 0; i < instances.length; i++) { if (instances[i].container === container) { return instances[i].isInViewport(elt, offset, cb); } } return instances[ instances.push(createInViewport(container)) - 1 ].isInViewport(elt, offset, cb); } function addEvent(el, type, fn) { if (el.attachEvent) { el.attachEvent('on' + type, fn); } else { el.addEventListener(type, fn, false); } } function debounce(func, wait, immediate) { var timeout; return function() { var context = this, args = arguments; var callNow = immediate && !timeout; clearTimeout(timeout); timeout = setTimeout(later, wait); if (callNow) func.apply(context, args); function later() { timeout = null; if (!immediate) func.apply(context, args); } }; } // https://github.com/jquery/sizzle/blob/3136f48b90e3edc84cbaaa6f6f7734ef03775a07/sizzle.js#L708 var contains = function() { if (!global.document) { return true; } return global.document.documentElement.compareDocumentPosition ? function(a, b) { return !!(a.compareDocumentPosition(b) & 16); } : global.document.documentElement.contains ? function(a, b) { return a !== b && (a.contains ? a.contains(b) : false); } : function(a, b) { while (b = b.parentNode) { if (b === a) { return true; } } return false; }; } function createInViewport(container) { var watches = createWatches(); var scrollContainer = container === global.document.body ? global : container; var debouncedCheck = debounce(watches.checkAll(watchInViewport), 15); addEvent(scrollContainer, 'scroll', debouncedCheck); if (scrollContainer === global) { addEvent(global, 'resize', debouncedCheck); } if (supportsMutationObserver) { observeDOM(watches, container, debouncedCheck); } // failsafe check, every 200ms we check for visible images // usecase: a hidden parent containing eleements // when the parent becomes visible, we have no event that the children // became visible setInterval(debouncedCheck, 150); function isInViewport(elt, offset, cb) { if (!cb) { return isVisible(elt, offset); } var remote = createRemote(elt, offset, cb); remote.watch(); return remote; } function createRemote(elt, offset, cb) { function watch() { watches.add(elt, offset, cb); } function dispose() { watches.remove(elt); } return { watch: watch, dispose: dispose }; } function watchInViewport(elt, offset, cb) { if (isVisible(elt, offset)) { watches.remove(elt); cb(elt); } } function isVisible(elt, offset) { if (!contains(global.document.documentElement, elt) || !contains(global.document.documentElement, container)) { return false; } // Check if the element is visible // https://github.com/jquery/jquery/blob/740e190223d19a114d5373758127285d14d6b71e/src/css/hiddenVisibleSelectors.js if (!elt.offsetWidth || !elt.offsetHeight) { return false; } var eltRect = elt.getBoundingClientRect(); var viewport = {}; if (container === global.document.body) { viewport = { top: -offset, left: -offset, right: global.document.documentElement.clientWidth + offset, bottom: global.document.documentElement.clientHeight + offset }; } else { var containerRect = container.getBoundingClientRect(); viewport = { top: containerRect.top - offset, left: containerRect.left - offset, right: containerRect.right + offset, bottom: containerRect.bottom + offset }; } // The element must overlap with the visible part of the viewport var visible = ( (eltRect.right > viewport.left) && (eltRect.left < viewport.right) && (eltRect.bottom > viewport.top) && (eltRect.top < viewport.bottom) ); return visible; } return { container: container, isInViewport: isInViewport }; } function createWatches() { var watches = []; function add(elt, offset, cb) { if (!isWatched(elt)) { watches.push([elt, offset, cb]); } } function remove(elt) { var pos = indexOf(elt); if (pos !== -1) { watches.splice(pos, 1); } } function indexOf(elt) { for (var i = watches.length - 1; i >= 0; i--) { if (watches[i][0] === elt) { return i; } } return -1; } function isWatched(elt) { return indexOf(elt) !== -1; } function checkAll(cb) { return function() { for (var i = watches.length - 1; i >= 0; i--) { cb.apply(this, watches[i]); } }; } return { add: add, remove: remove, isWatched: isWatched, checkAll: checkAll }; } function observeDOM(watches, container, cb) { var observer = new MutationObserver(watch); var filter = Array.prototype.filter; var concat = Array.prototype.concat; observer.observe(container, { childList: true, subtree: true, // changes like style/width/height/display will be catched attributes: true }); function watch(mutations) { // some new DOM nodes where previously watched // we should check their positions if (mutations.some(knownNodes) === true) { setTimeout(cb, 0); } } function knownNodes(mutation) { var nodes = concat.call([], Array.prototype.slice.call(mutation.addedNodes), mutation.target ); return filter.call(nodes, watches.isWatched).length > 0; } } /* WEBPACK VAR INJECTION */ }.call(exports, (function() { return this; }()))) /***/ } /******/ ]);"},{"title":"tags","date":"2017-02-17T11:21:23.000Z","updated":"2017-02-17T11:21:23.000Z","comments":true,"path":"tags/index.html","permalink":"http://www.mvtime.cn/tags/index.html","excerpt":"","text":""}],"posts":[{"title":"Java面试题(自答)","slug":"Java面试题(自答)","date":"2018-02-27T07:31:16.000Z","updated":"2018-02-27T10:27:13.062Z","comments":true,"path":"2018/02/27/Java面试题(自答)/","link":"","permalink":"http://www.mvtime.cn/2018/02/27/Java面试题(自答)/","excerpt":"","text":"基础篇基本功 面向对象的特征 封装、继承、多态 final, finally, finalize 的区别 final是用来修饰属性、方法、类,属性不可变,方法不可重写,类不可继承 finalize当垃圾收集器将对象从内存清理之前做必要的清理工作 finally是try和catch捕获异常,finally一定会执行,无论try块是否发生异常 int 和 Integer 有什么区别 int是基本数据类型初始值为0,Integer是对象类型初始值为null占用更多的内存Integer会自动拆箱为int 重载和重写的区别 你可以在一个类中重载方法,但是只能在子类中重写方法,一个是编译时活动,一个是运行时活动 抽象类和接口有什么区别 接口是对动作的抽象比如吃饭,抽象类是对类别的抽象比如人。 抽象类要被子类继承,接口要被子类实现 抽象类的方法可以被实现,接口不可以 说说反射的用途及实现 反射机制是在运行状态中,是搭建框架的基础所在,可以对项目进行解耦。可以根据类的名字获取到 说说自定义注解的场景及实现 HTTP 请求的 GET 与 POST 方式的区别 session 与 cookie 区别 session 分布式处理 JDBC 流程 MVC 设计思想 equals 与 == 的区别 集合 List 和 Set 区别 List 和 Map 区别 Arraylist 与 LinkedList 区别 ArrayList 与 Vector 区别 HashMap 和 Hashtable 的区别 HashSet 和 HashMap 区别 HashMap 和 ConcurrentHashMap 的区别 HashMap 的工作原理及代码实现 ConcurrentHashMap 的工作原理及代码实现 线程 创建线程的方式及实现 sleep() 、join()、yield()有什么区别 说说 CountDownLatch 原理 说说 CyclicBarrier 原理 说说 Semaphore 原理 说说 Exchanger 原理 说说 CountDownLatch 与 CyclicBarrier 区别 ThreadLocal 原理分析 讲讲线程池的实现原理 线程池的几种方式 线程的生命周期 锁机制 说说线程安全问题 volatile 实现原理 synchronize 实现原理 synchronized 与 lock 的区别 CAS 乐观锁 ABA 问题 乐观锁的业务场景及实现方式 核心篇数据存储 MySQL 索引使用的注意事项 说说反模式设计 说说分库与分表设计 分库与分表带来的分布式困境与应对之策 说说 SQL 优化之道 MySQL 遇到的死锁问题 存储引擎的 InnoDB 与 MyISAM 数据库索引的原理 为什么要用 B-tree 聚集索引与非聚集索引的区别 limit 20000 加载很慢怎么解决 选择合适的分布式主键方案 选择合适的数据存储方案 ObjectId 规则 聊聊 MongoDB 使用场景 倒排索引 聊聊 ElasticSearch 使用场景 缓存使用 Redis 有哪些类型 Redis 内部结构 聊聊 Redis 使用场景 Redis 持久化机制 Redis 如何实现持久化 Redis 集群方案与实现 Redis 为什么是单线程的 缓存奔溃 缓存降级 使用缓存的合理性问题 消息队列 消息队列的使用场景 消息的重发补偿解决思路 消息的幂等性解决思路 消息的堆积解决思路 自己如何实现消息队列 如何保证消息的有序性 框架篇Spring BeanFactory 和 ApplicationContext 有什么区别 Spring Bean 的生命周期 Spring IOC 如何实现 说说 Spring AOP Spring AOP 实现原理 动态代理(cglib 与 JDK) Spring 事务实现方式 Spring 事务底层原理 如何自定义注解实现功能 Spring MVC 运行流程 Spring MVC 启动流程 Spring 的单例实现原理 Spring 框架中用到了哪些设计模式 Spring 其他产品(Srping Boot、Spring Cloud、Spring Secuirity、Spring Data、Spring AMQP 等) Netty 为什么选择 Netty 说说业务中,Netty 的使用场景 原生的 NIO 在 JDK 1.7 版本存在 epoll bug 什么是TCP 粘包/拆包 TCP粘包/拆包的解决办法 Netty 线程模型 说说 Netty 的零拷贝 Netty 内部执行流程 Netty 重连实现 微服务篇微服务 前后端分离是如何做的 微服务哪些框架 你怎么理解 RPC 框架 说说 RPC 的实现原理 说说 Dubbo 的实现原理 你怎么理解 RESTful 说说如何设计一个良好的 API 如何理解 RESTful API 的幂等性 如何保证接口的幂等性 说说 CAP 定理、 BASE 理论 怎么考虑数据一致性问题 说说最终一致性的实现方案 你怎么看待微服务 微服务与 SOA 的区别 如何拆分服务 微服务如何进行数据库管理 如何应对微服务的链式调用异常 对于快速追踪与定位问题 微服务的安全 分布式 谈谈业务中使用分布式的场景 Session 分布式方案 分布式锁的场景 分布是锁的实现方案 分布式事务 集群与负载均衡的算法与实现 说说分库与分表设计 分库与分表带来的分布式困境与应对之策 安全问题 安全要素与 STRIDE 威胁 防范常见的 Web 攻击 服务端通信安全攻防 HTTPS 原理剖析 HTTPS 降级攻击 授权与认证 基于角色的访问控制 基于数据的访问控制 性能优化 性能指标有哪些 如何发现性能瓶颈 性能调优的常见手段 说说你在项目中如何进行性能调优 工程篇需求分析 你如何对需求原型进行理解和拆分 说说你对功能性需求的理解 说说你对非功能性需求的理解 你针对产品提出哪些交互和改进意见 你如何理解用户痛点 设计能力 说说你在项目中使用过的 UML 图 你如何考虑组件化 你如何考虑服务化 你如何进行领域建模 你如何划分领域边界 说说你项目中的领域建模 说说概要设计 设计模式 你项目中有使用哪些设计模式 说说常用开源框架中设计模式使用分析 说说你对设计原则的理解 23种设计模式的设计理念 设计模式之间的异同,例如策略模式与状态模式的区别 设计模式之间的结合,例如策略模式+简单工厂模式的实践 设计模式的性能,例如单例模式哪种性能更好。 业务工程 你系统中的前后端分离是如何做的 说说你的开发流程 你和团队是如何沟通的 你如何进行代码评审 说说你对技术与业务的理解 说说你在项目中经常遇到的 Exception 说说你在项目中遇到感觉最难Bug,怎么解决的 说说你在项目中遇到印象最深困难,怎么解决的 你觉得你们项目还有哪些不足的地方 你是否遇到过 CPU 100% ,如何排查与解决 你是否遇到过 内存 OOM ,如何排查与解决 说说你对敏捷开发的实践 说说你对开发运维的实践 介绍下工作中的一个对自己最有价值的项目,以及在这个过程中的角色 软实力 说说你的亮点 说说你最近在看什么书 说说你觉得最有意义的技术书籍 工作之余做什么事情 说说个人发展方向方面的思考 说说你认为的服务端开发工程师应该具备哪些能力 说说你认为的架构师是什么样的,架构师主要做什么 说说你所理解的技术专家","categories":[],"tags":[{"name":"技术","slug":"技术","permalink":"http://www.mvtime.cn/tags/技术/"},{"name":"Java","slug":"Java","permalink":"http://www.mvtime.cn/tags/Java/"}]},{"title":"Spring注解","slug":"Spring注解","date":"2018-01-03T06:09:06.000Z","updated":"2018-01-03T06:35:44.722Z","comments":true,"path":"2018/01/03/Spring注解/","link":"","permalink":"http://www.mvtime.cn/2018/01/03/Spring注解/","excerpt":"","text":"lombok 注解 val : 和 scala 中 val 同名, 可以在运行时确定类型; @NonNull : 注解在参数上, 如果该类参数为 null , 就会报出异常, throw new NullPointException(参数名) @Cleanup : 注释在引用变量前, 自动回收资源 默认调用 close() 方法 @Getter/@Setter : 注解在类上, 为类提供读写属性 @Getter(lazy=true) : @ToString : 注解在类上, 为类提供 toString() 方法 @EqualsAndHashCode : 注解在类上, 为类提供 equals() 和 hashCode() 方法 @NoArgsConstructor, @RequiredArgsConstructor, @AllArgsConstructor : 注解在类上, 为类提供无参,有指定必须参数, 全参构造函数 @Data : 注解在类上, 为类提供读写属性, 此外还提供了 equals()、hashCode()、toString() 方法 @Value : @Builder : 注解在类上, 为类提供一个内部的 Builder @SneakThrows : @Synchronized : 注解在方法上, 为方法提供同步锁 @Log : @Log4j : 注解在类上, 为类提供一个属性名为 log 的 log4j 的日志对象 @Slf4j : 注解在类上, 为类提供一个属性名为 log 的 log4j 的日志对象 NotEmpty,@NotNull和@NotBlank的区别1 @NotEmpty :不能为null,且Size>0 2 @NotNull:不能为null,但可以为empty(“”),没有Size的约束 3 @NotBlank:只用于String,不能为null且trim()之后size>0","categories":[],"tags":[{"name":"技术","slug":"技术","permalink":"http://www.mvtime.cn/tags/技术/"},{"name":"Java","slug":"Java","permalink":"http://www.mvtime.cn/tags/Java/"}]},{"title":"iTerm2免密码登录服务器","slug":"iTerm2免密码登录服务器","date":"2017-12-07T02:04:15.000Z","updated":"2017-12-08T03:33:17.747Z","comments":true,"path":"2017/12/07/iTerm2免密码登录服务器/","link":"","permalink":"http://www.mvtime.cn/2017/12/07/iTerm2免密码登录服务器/","excerpt":"","text":"使用公钥私钥完成ssh免密码登录1、 在本地机器创建公钥,一路回车即可 ssh-keygen -t rsa 2、将公钥复制到ssh服务器将上一步生成的id_rsa.pub公钥文件复制到目标服务器对应用户下的~/.ssh/authorized_keys 文件 ,如果服务器上没有authorized_keys 这个文件,直接创建即可,如果有,则把id_rsa.pub 这个文件里的内容copy 追加到后面即可。 也可以利用ssh-copy-id工具复制ssh-copy-id [email protected]如果失败可能是因为ssh密码错误ssh-keygen -R 47.94.171.61删除原先服务器密码。 3、 配置快捷登录在自己的mac上的 ~/.ssh/config配置文件添加你的ssh服务器信息,若没有就新建,有就在后面追回,格式: Host alias #自定义别名 HostName hostname #替换为你的ssh服务器ip或domain Port port #ssh服务器端口,默认为22 User user #ssh服务器用户名 IdentityFile ~/.ssh/id_rsa #第一个步骤生成的公钥文件对应的私钥文件12345 中文注释可能需要删除 保存关闭,ssh alias 就可以登录你要登的服务器了 ##可能会出现的问题 还是需要输入密码,查看tail -f /var/log/secure日志 错误信息Authentication refused: bad ownership or modes for directory /root看起来是root目录权限的问题,设置为700,但是我的还是不行 只有使用vim /etc/ssh/sshd_config设置StrictModes no它的作用是关闭权限的验证,然后重启sshservice sshd restart。","categories":[],"tags":[{"name":"技术","slug":"技术","permalink":"http://www.mvtime.cn/tags/技术/"},{"name":"iTerm2","slug":"iTerm2","permalink":"http://www.mvtime.cn/tags/iTerm2/"}]},{"title":"Java中9个处理Exception的最佳实践","slug":"Java中9个处理Exception的最佳实践","date":"2017-11-25T07:06:56.000Z","updated":"2017-11-25T07:19:03.773Z","comments":true,"path":"2017/11/25/Java中9个处理Exception的最佳实践/","link":"","permalink":"http://www.mvtime.cn/2017/11/25/Java中9个处理Exception的最佳实践/","excerpt":"","text":"在Java中处理异常并不是一个简单的事情。不仅仅初学者很难理解,即使一些有经验的开发者也需要花费很多时间来思考如何处理异常,包括需要处理哪些异常,怎样处理等等。这也是绝大多数开发团队都会制定一些规则来规范对异常的处理的原因。而团队之间的这些规范往往是截然不同的。 本文给出几个被很多团队使用的异常处理最佳实践。 1. 在Finally块中清理资源或者使用try-with-resource语句当使用类似InputStream这种需要使用后关闭的资源时,一个常见的错误就是在try块的最后关闭资源。 public void doNotCloseResourceInTry() { FileInputStream inputStream = null; try { File file = new File(\"./tmp.txt\"); inputStream = new FileInputStream(file); // use the inputStream to read a file // do NOT do this inputStream.close(); } catch (FileNotFoundException e) { log.error(e); } catch (IOException e) { log.error(e); } } 上述代码在没有任何exception的时候运行是没有问题的。但是当try块中的语句抛出异常或者自己实现的代码抛出异常,那么就不会执行最后的关闭语句,从而资源也无法释放。 合理的做法则是将所有清理的代码都放到finally块中或者使用try-with-resource语句。 public void closeResourceInFinally() { FileInputStream inputStream = null; try { File file = new File(\"./tmp.txt\"); inputStream = new FileInputStream(file); // use the inputStream to read a file } catch (FileNotFoundException e) { log.error(e); } finally { if (inputStream != null) { try { inputStream.close(); } catch (IOException e) { log.error(e); } } } } public void automaticallyCloseResource() { File file = new File(\"./tmp.txt\"); try (FileInputStream inputStream = new FileInputStream(file);) { // use the inputStream to read a file } catch (FileNotFoundException e) { log.error(e); } catch (IOException e) { log.error(e); } } 2. 指定具体的异常尽可能的使用最具体的异常来声明方法,这样才能使得代码更容易理解。 public void doNotDoThis() throws Exception { ... } public void doThis() throws NumberFormatException { ... } 如上,NumberFormatException字面上即可以看出是数字格式化错误。 3. 对异常进行文档说明当在方法上声明抛出异常时,也需要进行文档说明。和前面的一点一样,都是为了给调用者提供尽可能多的信息,从而可以更好地避免/处理异常。 在Javadoc中加入throws声明,并且描述抛出异常的场景。 /** * This method does something extremely useful ... * * @param input * @throws MyBusinessException if ... happens */ public void doSomething(String input) throws MyBusinessException { ... } 4. 抛出异常的时候包含描述信息在抛出异常时,需要尽可能精确地描述问题和相关信息,这样无论是打印到日志中还是监控工具中,都能够更容易被人阅读,从而可以更好地定位具体错误信息、错误的严重程度等。 但这里并不是说要对错误信息长篇大论,因为本来Exception的类名就能够反映错误的原因,因此只需要用一到两句话描述即可。 try { new Long(\"xyz\"); } catch (NumberFormatException e) { log.error(e); } NumberFormatException即告诉了这个异常是格式化错误,异常的额外信息只需要提供这个错误字符串即可。当异常的名称不够明显的时候,则需要提供尽可能具体的错误信息。 5. 首先捕获最具体的异常现在很多IDE都能智能提示这个最佳实践,当你试图首先捕获最笼统的异常时,会提示不能达到的代码。 当有多个catch块中,按照捕获顺序只有第一个匹配到的catch块才能执行。因此,如果先捕获IllegalArgumentException,那么则无法运行到对NumberFormatException的捕获。 public void catchMostSpecificExceptionFirst() { try { doSomething(\"A message\"); } catch (NumberFormatException e) { log.error(e); } catch (IllegalArgumentException e) { log.error(e) } } 6. 不要捕获ThrowableThrowable是所有异常和错误的父类。你可以在catch语句中捕获,但是永远不要这么做。 如果catch了throwable,那么不仅仅会捕获所有exception,还会捕获error。而error是表明无法恢复的jvm错误。因此除非绝对肯定能够处理或者被要求处理error,不要捕获throwable。 public void doNotCatchThrowable() { try { // do something } catch (Throwable t) { // don't do this! } } 7. 不要忽略异常很多时候,开发者很有自信不会抛出异常,因此写了一个catch块,但是没有做任何处理或者记录日志。 public void doNotIgnoreExceptions() { try { // do something } catch (NumberFormatException e) { // this will never happen } } 但现实是经常会出现无法预料的异常或者无法确定这里的代码未来是不是会改动(删除了阻止异常抛出的代码),而此时由于异常被捕获,使得无法拿到足够的错误信息来定位问题。 合理的做法是至少要记录异常的信息。 public void logAnException() { try { // do something } catch (NumberFormatException e) { log.error(\"This should never happen: \" + e); } } 8. 不要记录并抛出异常可以发现很多代码甚至类库中都会有捕获异常、记录日志并再次抛出的逻辑。如下: try { new Long(\"xyz\"); } catch (NumberFormatException e) { log.error(e); throw e; } 这个处理逻辑看着是合理的。但这经常会给同一个异常输出多条日志。如下: 17:44:28,945 ERROR TestExceptionHandling:65 - java.lang.NumberFormatException: For input string: "xyz"Exception in thread "main" java.lang.NumberFormatException: For input string: "xyz"at java.lang.NumberFormatException.forInputString(NumberFormatException.java:65)at java.lang.Long.parseLong(Long.java:589)at java.lang.Long.(Long.java:965)at com.stackify.example.TestExceptionHandling.logAndThrowException(TestExceptionHandling.java:63)at com.stackify.example.TestExceptionHandling.main(TestExceptionHandling.java:58) 如上所示,后面的日志也没有附加更有用的信息。如果想要提供更加有用的信息,那么可以将异常包装为自定义异常。 public void wrapException(String input) throws MyBusinessException { try { // do something } catch (NumberFormatException e) { throw new MyBusinessException(\"A message that describes the error.\", e); } } 因此,仅仅当想要处理异常时才去捕获,否则只需要在方法签名中声明让调用者去处理。 9. 包装异常时不要抛弃原始的异常捕获标准异常并包装为自定义异常是一个很常见的做法。这样可以添加更为具体的异常信息并能够做针对的异常处理。 需要注意的是,包装异常时,一定要把原始的异常设置为cause(Exception有构造方法可以传入cause)。否则,丢失了原始的异常信息会让错误的分析变得困难。 public void wrapException(String input) throws MyBusinessException { try { // do something } catch (NumberFormatException e) { throw new MyBusinessException(\"A message that describes the error.\", e); } } 总结综上可知,当抛出或者捕获异常时,有很多不一样的东西需要考虑。其中的许多点都是为了提升代码的可阅读性或者api的可用性。 异常不仅仅是一个错误控制机制,也是一个沟通媒介,因此与你的协作者讨论这些最佳实践并制定一些规范能够让每个人都理解相关的通用概念并且能够按照同样的方式使用它们。","categories":[],"tags":[{"name":"技术","slug":"技术","permalink":"http://www.mvtime.cn/tags/技术/"},{"name":"Java","slug":"Java","permalink":"http://www.mvtime.cn/tags/Java/"}]},{"title":"Java学习记录","slug":"Java学习记录","date":"2017-11-17T02:23:48.000Z","updated":"2017-11-17T02:53:01.736Z","comments":true,"path":"2017/11/17/Java学习记录/","link":"","permalink":"http://www.mvtime.cn/2017/11/17/Java学习记录/","excerpt":"","text":"Java琐碎的学习记录 泛型、Mybatis分页查询、SOA架构2017.11.16学习记录 泛型的使用 class的定义方式public class PageEntity<T>,初始化时需要指定泛型类型new PageEntity(TbItem),这样private T e; 属性e就代表TbItem类型 Mybatis使用generator自动生成,generator配置注意引入classpathdriver驱动。 一种是自己定义一个Page实体,里面包含了页码、当前页数、数组、entity(泛型)等。Controller接受Page和entity封装到Page中,在service查询list添加到Page的list中 使用PageHelper插件,添加Maven依赖5.0.0,mybatis配置拦截器 <plugins> <plugin interceptor=\"com.github.pagehelper.PageInterceptor\"> <!--自动识别使用的数据库--> </plugin> </plugins> SOA架构 当其他jar包添加方法或者类时需要install,maven父依赖版本号更改时需要clean install -e -U强制更新 zookeeper的安装需要jdk1.7的环境(可能是我的版本低),阿里云需要开启对应的端口访问(安全组配置) 当tomcat不打印日志的时候,需要在resource里面添加log4j.properties配置,会打印日志 maven继承一般只针对版本号,依赖把另一个项目打成jar包,soa会把service和web分别打成war包,springmvc在web层配置,spring-dao还有事务在service配置","categories":[],"tags":[{"name":"技术","slug":"技术","permalink":"http://www.mvtime.cn/tags/技术/"},{"name":"Java","slug":"Java","permalink":"http://www.mvtime.cn/tags/Java/"}]},{"title":"IntelliJ IDEA For Mac 快捷键","slug":"IntelliJ-IDEA-For-Mac-快捷键","date":"2017-10-27T07:47:33.000Z","updated":"2017-10-27T08:07:43.907Z","comments":true,"path":"2017/10/27/IntelliJ-IDEA-For-Mac-快捷键/","link":"","permalink":"http://www.mvtime.cn/2017/10/27/IntelliJ-IDEA-For-Mac-快捷键/","excerpt":"","text":"http://www.cnblogs.com/wxd0108/p/5295017.html Mac键盘符号和修饰键说明 ⌘ Command ⇧ Shift ⌥ Option ⌃ Control ↩︎ Return/Enter ⌫ Delete ⌦ 向前删除键(Fn+Delete) ↑ 上箭头 ↓ 下箭头 ← 左箭头 → 右箭头 ⇥ 右制表符(Tab键) ⇤ 左制表符(Shift+Tab) ⎋ Escape (Esc) ⇞ Page Up(Fn+↑) ⇟ Page Down(Fn+↓) Home Fn + ← End Fn + → 首行 Fn + ⌘ + ← 尾行 Fn + ⌘ + → 一、Editing(编辑) ⌃Space 基本的代码补全(补全任何类、方法、变量) ⌃⇧Space 智能代码补全(过滤器方法列表和变量的预期类型) ⌘⇧↩ 自动结束代码,行末自动添加分号 ⌘P 显示方法的参数信息 ⌃J, Mid. button click 快速查看文档 ⇧F1 查看外部文档(在某些代码上会触发打开浏览器显示相关文档) ⌘+鼠标放在代码上 显示代码简要信息 ⌘F1 在错误或警告处显示具体描述信息 ⌘N, ⌃↩, ⌃N 生成代码(getter、setter、构造函数、hashCode/equals,toString) ⌃O 覆盖方法(重写父类方法) ⌃I 实现方法(实现接口中的方法) ⌘⌥T 包围代码(使用if..else, try..catch, for, synchronized等包围选中的代码) ⌘/ 注释/取消注释与行注释 ⌘⌥/ 注释/取消注释与块注释 ⌥↑ 连续选中代码块 ⌥↓ 减少当前选中的代码块 ⌃⇧Q 显示上下文信息 ⌥↩ 显示意向动作和快速修复代码 ⌘⌥L 格式化代码 ⌃⌥O 优化import ⌃⌥I 自动缩进线 ⇥ / ⇧⇥ 缩进代码 / 反缩进代码 ⌘X 剪切当前行或选定的块到剪贴板 ⌘C 复制当前行或选定的块到剪贴板 ⌘V 从剪贴板粘贴 ⌘⇧V 从最近的缓冲区粘贴 ⌘D 复制当前行或选定的块 ⌘⌫ 删除当前行或选定的块的行 ⌃⇧J 智能的将代码拼接成一行 ⌘↩ 智能的拆分拼接的行 ⇧↩ 开始新的一行 ⌘⇧U 大小写切换 ⌘⇧] / ⌘⇧[ 选择直到代码块结束/开始 ⌥⌦ 删除到单词的末尾(⌦键为Fn+Delete) ⌥⌫ 删除到单词的开头 ⌘+ / ⌘- 展开 / 折叠代码块 ⌘⇧+ 展开所以代码块 ⌘⇧- 折叠所有代码块 ⌘W 关闭活动的编辑器选项卡 二、Search/Replace(查询/替换) Double ⇧ 查询任何东西 ⌘F 文件内查找 ⌘G 查找模式下,向下查找 ⌘⇧G 查找模式下,向上查找 ⌘R 文件内替换 ⌘⇧F 全局查找(根据路径) ⌘⇧R 全局替换(根据路径) ⌘⇧S 查询结构(Ultimate Edition 版专用,需要在Keymap中设置) ⌘⇧M 替换结构(Ultimate Edition 版专用,需要在Keymap中设置) 三、Usage Search(使用查询) ⌥F7 / ⌘F7 在文件中查找用法 / 在类中查找用法 ⌘⇧F7 在文件中突出显示的用法 ⌘⌥F7 显示用法 四、Compile and Run(编译和运行) ⌘F9 编译Project ⌘⇧F9 编译选择的文件、包或模块 ⌃⌥R 弹出 Run 的可选择菜单 ⌃⌥D 弹出 Debug 的可选择菜单 ⌃R 运行 ⌃D 调试 ⌃⇧R, ⌃⇧D 从编辑器运行上下文环境配置 五、Debugging(调试) F8 进入下一步,如果当前行断点是一个方法,则不进入当前方法体内 F7 进入下一步,如果当前行断点是一个方法,则进入当前方法体内,如果该方法体还有方法,则不会进入该内嵌的方法中 ⇧F7 智能步入,断点所在行上有多个方法调用,会弹出进入哪个方法 ⇧F8 跳出 ⌥F9 运行到光标处,如果光标前有其他断点会进入到该断点 ⌥F8 计算表达式(可以更改变量值使其生效) ⌘⌥R 恢复程序运行,如果该断点下面代码还有断点则停在下一个断点上 ⌘F8 切换断点(若光标当前行有断点则取消断点,没有则加上断点) ⌘⇧F8 查看断点信息 六、Navigation(导航) ⌘O 查找类文件 ⌘⇧O 查找所有类型文件、打开文件、打开目录,打开目录需要在输入的内容前面或后面加一个反斜杠/ ⌘⌥O 前往指定的变量 / 方法 ⌃← / ⌃→ 左右切换打开的编辑tab页 F12 返回到前一个工具窗口 ⎋ 从工具窗口进入代码文件窗口 ⇧⎋ 隐藏当前或最后一个活动的窗口,且光标进入代码文件窗口 ⌘⇧F4 关闭活动run/messages/find/… tab ⌘L 在当前文件跳转到某一行的指定处 ⌘E 显示最近打开的文件记录列表 ⌘⌥← / ⌘⌥→ 退回 / 前进到上一个操作的地方 ⌘⇧⌫ 跳转到最后一个编辑的地方 ⌥F1 显示当前文件选择目标弹出层,弹出层中有很多目标可以进行选择(如在代码编辑窗口可以选择显示该文件的Finder) ⌘B / ⌘ 鼠标点击 进入光标所在的方法/变量的接口或是定义处 ⌘⌥B 跳转到实现处,在某个调用的方法名上使用会跳到具体的实现处,可以跳过接口 ⌥ Space, ⌘Y 快速打开光标所在方法、类的定义 ⌃⇧B 跳转到类型声明处 ⌘U 前往当前光标所在方法的父类的方法 / 接口定义 ⌃↓ / ⌃↑ 当前光标跳转到当前文件的前一个/后一个方法名位置 ⌘] / ⌘[ 移动光标到当前所在代码的花括号开始/结束位置 ⌘F12 弹出当前文件结构层,可以在弹出的层上直接输入进行筛选(可用于搜索类中的方法) ⌃H 显示当前类的层次结构 ⌘⇧H 显示方法层次结构 ⌃⌥H 显示调用层次结构 F2 / ⇧F2 跳转到下一个/上一个突出错误或警告的位置 F4 / ⌘↓ 编辑/查看代码源 ⌥ Home 显示到当前文件的导航条 F3选中文件/文件夹/代码行,添加/取消书签 ⌥F3 选中文件/文件夹/代码行,使用助记符添加/取消书签 ⌃0...⌃9 定位到对应数值的书签位置 ⌘F3 显示所有书签 七、Refactoring(重构) F5 复制文件到指定目录 F6 移动文件到指定目录 ⌘⌫ 在文件上为安全删除文件,弹出确认框 ⇧F6 重命名文件 ⌘F6 更改签名 ⌘⌥N 一致性 ⌘⌥M 将选中的代码提取为方法 ⌘⌥V 提取变量 ⌘⌥F 提取字段 ⌘⌥C 提取常量 ⌘⌥P 提取参数 八、VCS/Local History(版本控制/本地历史记录) ⌘K 提交代码到版本控制器 ⌘T 从版本控制器更新代码 ⌥⇧C 查看最近的变更记录 ⌃C 快速弹出版本控制器操作面板 九、Live Templates(动态代码模板) ⌘⌥J 弹出模板选择窗口,将选定的代码使用动态模板包住 ⌘J 插入自定义动态代码模板 十、General(通用) ⌘1...⌘9 打开相应编号的工具窗口 ⌘S 保存所有 ⌘⌥Y 同步、刷新 ⌃⌘F 切换全屏模式 ⌘⇧F12 切换最大化编辑器 ⌥⇧F 添加到收藏夹 ⌥⇧I 检查当前文件与当前的配置文件 `§⌃, ⌃`` 快速切换当前的scheme(切换主题、代码样式等) ⌘, 打开IDEA系统设置 ⌘; 打开项目结构对话框 ⇧⌘A 查找动作(可设置相关选项) ⌃⇥ 编辑窗口标签和工具窗口之间切换(如果在切换的过程加按上delete,则是关闭对应选中的窗口) 十一、Other(一些官方文档上没有体现的快捷键) ⌘⇧8 竖编辑模式 导航⌘O 查找类文件 Ctrl + N ⌘⌥O 前往指定的变量 / 方法 Ctrl + Shift + Alt + N ⌃← / ⌃→ 左右切换打开的编辑tab页 Alt← / Alt→ ⎋ 从工具窗口进入代码文件窗口 ESC ⌘L 在当前文件跳转到某一行的指定处 Ctrl + G ⌘E 显示最近打开的文件记录列表 Ctrl + E ⌘⌥← / ⌘⌥→ 退回 / 前进到上一个操作的地方 Ctrl + Alt + ← Ctrl + Alt + → ⌘⇧⌫ 跳转到最后一个编辑的地方 ⌃H 显示当前类的层次结构 Ctrl + H ⌘⇧H 显示方法层次结构 ⌃⌥H 显示调用层次结构 F4 / ⌘↓ 编辑/查看代码源 ⌘⌥U 显示类UML图 ⌃J 查看注释 编辑⌥⌦ 删除到单词的末尾(⌦键为Fn+Delete)⌥⌫ 删除到单词的开头⌘+ / ⌘- 展开 / 折叠代码块⌘F1 在错误或警告处显示具体描述信息⌘⌥L 格式化代码⌃⌥O 优化import⇧↩ 开始新的一行⌘⇧↩ 自动结束代码,行末自动添加分号⌃I 实现方法(实现接口中的方法)⇧F6 重命名文件或者变量⌘N, ⌃↩, ⌃N 生成代码(getter、setter、构造函数、hashCode/equals,toString)⌘P 显示方法的参数信息 查找Double⇧ 查找任何东西⌘⇧F 全局查找(根据路径)⌘F 文件内查找⌘G 查找模式下,向下查找⌘⇧G 查找模式下,向上查找 导航⌘⌥B 跳转到接口的实现⌘U 查看接口定义 ⌘⌥← / ⌘⌥→ 退回 / 前进到上一个操作的地方 ⌘B / ⌘ 鼠标点击 进入光标所在的方法/变量的接口或是定义处⌃⇧B 跳转到类型声明处 ⌥ Space, ⌘Y 快速打开光标所在方法、类的定义 ⌘O 查找类文件⌘⇧O 查找所有类型文件、打开文件、打开目录,打开目录需要在输入的内容前面或后面加一个反斜杠/ F12 返回到前一个工具窗口⎋ 从工具窗口进入代码文件窗口⇧⎋ 隐藏当前或最后一个活动的窗口,且光标进入代码文件窗口 F3选中文件/文件夹/代码行,添加/取消书签⌥F3 选中文件/文件夹/代码行,使用助记符添加/取消书签⌃0…⌃9 定位到对应数值的书签位置⌘F3 显示所有书签 ⌥F1 显示当前文件选择目标弹出层,弹出层中有很多目标可以进行选择(如在代码编辑窗口可以选择显示该文件的Finder) ⌘F12 弹出当前文件结构层,可以在弹出的层上直接输入进行筛选(可用于搜索类中的方法) 通用⌃⌘F 切换全屏模式","categories":[],"tags":[{"name":"IDE, 技术","slug":"IDE,-技术","permalink":"http://www.mvtime.cn/tags/IDE,-技术/"}]},{"title":"Java设计模式","slug":"Java设计模式","date":"2017-10-27T06:36:57.000Z","updated":"2017-10-27T06:46:17.363Z","comments":true,"path":"2017/10/27/Java设计模式/","link":"","permalink":"http://www.mvtime.cn/2017/10/27/Java设计模式/","excerpt":"","text":"设计模式是对大家实际工作中写的各种代码进行高层次抽象的总结,其中最出名的当属 Gang of Four (GoF) 的分类了,他们将设计模式分类为 23 种经典的模式,根据用途我们又可以分为三大类,分别为创建型模式、结构型模式和行为型模式。 有一些重要的设计原则在开篇和大家分享下,这些原则将贯通全文: 面向接口编程,而不是面向实现。这个很重要,也是优雅的、可扩展的代码的第一步,这就不需要多说了吧。 职责单一原则。每个类都应该只有一个单一的功能,并且该功能应该由这个类完全封装起来。 对修改关闭,对扩展开放。对修改关闭是说,我们辛辛苦苦加班写出来的代码,该实现的功能和该修复的 bug 都完成了,别人可不能说改就改;对扩展开放就比较好理解了,也就是说在我们写好的代码基础上,很容易实现扩展。 创建型模式创建型模式的作用就是创建对象,说到创建一个对象,最熟悉的就是 new 一个对象,然后 set 相关属性。但是,在很多场景下,我们需要给客户端提供更加友好的创建对象的方式,尤其是那种我们定义了类,但是需要提供给其他开发者用的时候。 简单工厂模式和名字一样简单,非常简单,直接上代码吧: public class FoodFactory { public static Food makeFood(String name) { if (name.equals("noodle")) { Food noodle = new LanZhouNoodle(); noodle.addSpicy("more"); return noodle; } else if (name.equals("chicken")) { Food chicken = new HuangMenChicken(); chicken.addCondiment("potato"); return chicken; } else { return null; } } } 其中,LanZhouNoodle 和 HuangMenChicken 都继承自 Food。 简单地说,简单工厂模式通常就是这样,一个工厂类 XxxFactory,里面有一个静态方法,根据我们不同的参数,返回不同的派生自同一个父类(或实现同一接口)的实例对象。 我们强调职责单一原则,一个类只提供一种功能,FoodFactory 的功能就是只要负责生产各种 Food。 工厂模式简单工厂模式很简单,如果它能满足我们的需要,我觉得就不要折腾了。之所以需要引入工厂模式,是因为我们往往需要使用两个或两个以上的工厂。 public interface FoodFactory { Food makeFood(String name); } public class ChineseFoodFactory implements FoodFactory { @Override public Food makeFood(String name) { if (name.equals("A")) { return new ChineseFoodA(); } else if (name.equals("B")) { return new ChineseFoodB(); } else { return null; } } } public class AmericanFoodFactory implements FoodFactory { @Override public Food makeFood(String name) { if (name.equals("A")) { return new AmericanFoodA(); } else if (name.equals("B")) { return new AmericanFoodB(); } else { return null; } } } 其中,ChineseFoodA、ChineseFoodB、AmericanFoodA、AmericanFoodB 都派生自 Food。 客户端调用: public class APP { public static void main(String[] args) { // 先选择一个具体的工厂 FoodFactory factory = new ChineseFoodFactory(); // 由第一步的工厂产生具体的对象,不同的工厂造出不一样的对象 Food food = factory.makeFood("A"); } } 虽然都是调用 makeFood(“A”) 制作 A 类食物,但是,不同的工厂生产出来的完全不一样。 第一步,我们需要选取合适的工厂,然后第二步基本上和简单工厂一样。 核心在于,我们需要在第一步选好我们需要的工厂。比如,我们有 LogFactory 接口,实现类有 FileLogFactory 和 KafkaLogFactory,分别对应将日志写入文件和写入 Kafka 中,显然,我们客户端第一步就需要决定到底要实例化 FileLogFactory 还是 KafkaLogFactory,这将决定之后的所有的操作。 虽然简单,不过我也把所有的构件都画到一张图上,这样读者看着比较清晰: 抽象工厂模式当涉及到产品族的时候,就需要引入抽象工厂模式了。 一个经典的例子是造一台电脑。我们先不引入抽象工厂模式,看看怎么实现。 因为电脑是由许多的构件组成的,我们将 CPU 和主板进行抽象,然后 CPU 由 CPUFactory 生产,主板由 MainBoardFactory 生产,然后,我们再将 CPU 和主板搭配起来组合在一起,如下图: 这个时候的客户端调用是这样的: // 得到 Intel 的 CPU CPUFactory cpuFactory = new IntelCPUFactory(); CPU cpu = intelCPUFactory.makeCPU(); // 得到 AMD 的主板 MainBoardFactory mainBoardFactory = new AmdMainBoardFactory(); MainBoard mainBoard = mainBoardFactory.make(); // 组装 CPU 和主板 Computer computer = new Computer(cpu, mainBoard); 单独看 CPU 工厂和主板工厂,它们分别是前面我们说的工厂模式。这种方式也容易扩展,因为要给电脑加硬盘的话,只需要加一个 HardDiskFactory 和相应的实现即可,不需要修改现有的工厂。 但是,这种方式有一个问题,那就是如果 Intel 家产的 CPU 和 AMD 产的主板不能兼容使用,那么这代码就容易出错,因为客户端并不知道它们不兼容,也就会错误地出现随意组合。 下面就是我们要说的产品族的概念,它代表了组成某个产品的一系列附件的集合: 当涉及到这种产品族的问题的时候,就需要抽象工厂模式来支持了。我们不再定义 CPU 工厂、主板工厂、硬盘工厂、显示屏工厂等等,我们直接定义电脑工厂,每个电脑工厂负责生产所有的设备,这样能保证肯定不存在兼容问题。 这个时候,对于客户端来说,不再需要单独挑选 CPU厂商、主板厂商、硬盘厂商等,直接选择一家品牌工厂,品牌工厂会负责生产所有的东西,而且能保证肯定是兼容可用的。 public static void main(String[] args) { // 第一步就要选定一个“大厂” ComputerFactory cf = new AmdFactory(); // 从这个大厂造 CPU CPU cpu = cf.makeCPU(); // 从这个大厂造主板 MainBoard board = cf.makeMainBoard(); // 从这个大厂造硬盘 HardDisk hardDisk = cf.makeHardDisk(); // 将同一个厂子出来的 CPU、主板、硬盘组装在一起 Computer result = new Computer(cpu, board, hardDisk); } 当然,抽象工厂的问题也是显而易见的,比如我们要加个显示器,就需要修改所有的工厂,给所有的工厂都加上制造显示器的方法。这有点违反了对修改关闭,对扩展开放这个设计原则。 单例模式单例模式用得最多,错得最多。 饿汉模式最简单: public class Singleton { // 首先,将 new Singleton() 堵死 private Singleton() {}; // 创建私有静态实例,意味着这个类第一次使用的时候就会进行创建 private static Singleton instance = new Singleton(); public static Singleton getInstance() { return instance; } // 瞎写一个静态方法。这里想说的是,如果我们只是要调用 Singleton.getDate(...), // 本来是不想要生成 Singleton 实例的,不过没办法,已经生成了 public static Date getDate(String mode) {return new Date();} } 很多人都能说出饿汉模式的缺点,可是我觉得生产过程中,很少碰到这种情况:你定义了一个单例的类,不需要其实例,可是你却把一个或几个你会用到的静态方法塞到这个类中。 饱汉模式最容易出错: public class Singleton { // 首先,也是先堵死 new Singleton() 这条路 private Singleton() {} // 和饿汉模式相比,这边不需要先实例化出来,注意这里的 volatile,它是必须的 private static volatile Singleton instance = null; public static Singleton getInstance() { if (instance == null) { // 加锁 synchronized (Singleton.class) { // 这一次判断也是必须的,不然会有并发问题 if (instance == null) { instance = new Singleton(); } } } return instance; } } 双重加锁,volatile 保证了线程间的可见性,synchronized 对可能的并发问题做同步 很多人不知道怎么写,直接就在 getInstance() 方法签名上加上 synchronized,这就不多说了。 嵌套类最经典,以后大家就用它吧: public class Singleton3 { private Singleton3() {} // 主要是使用了 嵌套类可以访问外部类的静态属性和静态方法 的特性 private static class Holder { private static Singleton3 instance = new Singleton3(); } public static Singleton3 getInstance() { return Holder.instance; } } 注意,很多人都会把这个嵌套类说成是静态内部类,严格地说,内部类和嵌套类是不一样的,它们能访问的外部类权限也是不一样的。 最后,一定有人跳出来说用枚举实现单例,是的没错,枚举类很特殊,它在类加载的时候会初始化里面的所有的实例,而且 JVM 保证了它们不会再被实例化,所以它天生就是单例的。不说了,读者自己看着办吧,不建议使用。 建造者模式经常碰见的 XxxBuilder 的类,通常都是建造者模式的产物。建造者模式其实有很多的变种,但是对于客户端来说,我们的使用通常都是一个模式的: Food food = new FoodBuilder().a().b().c().build(); Food food = Food.builder().a().b().c().build(); 套路就是先 new 一个 Builder,然后可以链式地调用一堆方法,最后再调用一次 build() 方法,我们需要的对象就有了。 来一个中规中矩的建造者模式: class User { // 下面是“一堆”的属性 private String name; private String password; private String nickName; private int age; // 构造方法私有化,不然客户端就会直接调用构造方法了 private User(String name, String password, String nickName, int age) { this.name = name; this.password = password; this.nickName = nickName; this.age = age; } // 静态方法,用于生成一个 Builder,这个不一定要有,不过写这个方法是一个很好的习惯, // 有些代码要求别人写 new User.UserBuilder().a()...build() 看上去就没那么好 public static UserBuilder builder() { return new UserBuilder(); } public static class UserBuilder { // 下面是和 User 一模一样的一堆属性 private String name; private String password; private String nickName; private int age; private UserBuilder() { } // 链式调用设置各个属性值,返回 this,即 UserBuilder public UserBuilder name(String name) { this.name = name; return this; } public UserBuilder password(String password) { this.password = password; return this; } public UserBuilder nickName(String nickName) { this.nickName = nickName; return this; } public UserBuilder age(int age) { this.age = age; return this; } // build() 方法负责将 UserBuilder 中设置好的属性“复制”到 User 中。 // 当然,可以在 “复制” 之前做点检验 public User build() { if (name == null || password == null) { throw new RuntimeException(\"用户名和密码必填\"); } if (age <= 0 || age >= 150) { throw new RuntimeException(\"年龄不合法\"); } // 还可以做赋予”默认值“的功能 if (nickName == null) { nickName = name; } return new User(name, password, nickName, age); } } } 核心是:先把所有的属性都设置给 Builder,然后 build() 方法的时候,将这些属性复制给实际产生的对象。 看看客户端的调用: public class APP { public static void main(String[] args) { User d = User.builder() .name("foo") .password("pAss12345") .age(25) .build(); } } 说实话,建造者模式的链式写法很吸引人,但是,多写了很多“无用”的 builder 的代码,感觉这个模式没什么用。不过,当属性很多,而且有些必填,有些选填的时候,这个模式会使代码清晰很多。我们可以在 Builder 的构造方法中强制让调用者提供必填字段,还有,在 build() 方法中校验各个参数比在 User 的构造方法中校验,代码要优雅一些。 题外话,强烈建议读者使用 lombok,用了 lombok 以后,上面的一大堆代码会变成如下这样: @Builder class User { private String name; private String password; private String nickName; private int age; } 怎么样,省下来的时间是不是又可以干点别的了。 当然,如果你只是想要链式写法,不想要建造者模式,有个很简单的办法,User 的 getter 方法不变,所有的 setter 方法都让其 return this 就可以了,然后就可以像下面这样调用: User user = new User().setName("").setPassword("").setAge(20); 原型模式这是我要说的创建型模式的最后一个设计模式了。 原型模式很简单:有一个原型实例,基于这个原型实例产生新的实例,也就是“克隆”了。 Object 类中有一个 clone() 方法,它用于生成一个新的对象,当然,如果我们要调用这个方法,java 要求我们的类必须先实现 Cloneable 接口,此接口没有定义任何方法,但是不这么做的话,在 clone() 的时候,会抛出 CloneNotSupportedException 异常。 protected native Object clone() throws CloneNotSupportedException; java 的克隆是浅克隆,碰到对象引用的时候,克隆出来的对象和原对象中的引用将指向同一个对象。通常实现深克隆的方法是将对象进行序列化,然后再进行反序列化。 原型模式了解到这里我觉得就够了,各种变着法子说这种代码或那种代码是原型模式,没什么意义。 创建型模式总结创建型模式总体上比较简单,它们的作用就是为了产生实例对象,算是各种工作的第一步了,因为我们写的是面向对象的代码,所以我们第一步当然是需要创建一个对象了。 简单工厂模式最简单;工厂模式在简单工厂模式的基础上增加了选择工厂的维度,需要第一步选择合适的工厂;抽象工厂模式有产品族的概念,如果各个产品是存在兼容性问题的,就要用抽象工厂模式。单例模式就不说了,为了保证全局使用的是同一对象,一方面是安全性考虑,一方面是为了节省资源;建造者模式专门对付属性很多的那种类,为了让代码更优美;原型模式用得最少,了解和 Object 类中的 clone() 方法相关的知识即可。 结构型模式前面创建型模式介绍了创建对象的一些设计模式,这节介绍的结构型模式旨在通过改变代码结构来达到解耦的目的,使得我们的代码容易维护和扩展。 代理模式第一个要介绍的代理模式是最常使用的模式之一了,用一个代理来隐藏具体实现类的实现细节,通常还用于在真实的实现的前后添加一部分逻辑。 既然说是代理,那就要对客户端隐藏真实实现,由代理来负责客户端的所有请求。当然,代理只是个代理,它不会完成实际的业务逻辑,而是一层皮而已,但是对于客户端来说,它必须表现得就是客户端需要的真实实现。 理解代理这个词,这个模式其实就简单了。 public interface FoodService { Food makeChicken(); Food makeNoodle(); } public class FoodServiceImpl implements FoodService { public Food makeChicken() { Food f = new Chicken() f.setChicken(\"1kg\"); f.setSpicy(\"1g\"); f.setSalt(\"3g\"); return f; } public Food makeNoodle() { Food f = new Noodle(); f.setNoodle(\"500g\"); f.setSalt(\"5g\"); return f; } } // 代理要表现得“就像是”真实实现类,所以需要实现 FoodService public class FoodServiceProxy implements FoodService { // 内部一定要有一个真实的实现类,当然也可以通过构造方法注入 private FoodService foodService = new FoodServiceImpl(); public Food makeChicken() { System.out.println(\"我们马上要开始制作鸡肉了\"); // 如果我们定义这句为核心代码的话,那么,核心代码是真实实现类做的, // 代理只是在核心代码前后做些“无足轻重”的事情 Food chicken = foodService.makeChicken(); System.out.println(\"鸡肉制作完成啦,加点胡椒粉\"); // 增强 chicken.addCondiment(\"pepper\"); return fruit; } public Food makeNoodle() { System.out.println(\"准备制作拉面~\"); Food noodle = foodService.makeNoodle(); System.out.println(\"制作完成啦\") return fruit; } } 客户端调用,注意,我们要用代理来实例化接口: // 这里用代理类来实例化 FruitService fruitService = new FruitServiceProxy(); fruitService.makeChicken(); 我们发现没有,代理模式说白了就是做 “方法包装” 或做 “方法增强”。在面向切面编程中,算了还是不要吹捧这个名词了,在 AOP 中,其实就是动态代理的过程。比如 Spring 中,我们自己不定义代理类,但是 Spring 会帮我们动态来定义代理,然后把我们定义在 @Before、@After、@Around 中的代码逻辑动态添加到代理中。 说到动态代理,又可以展开说 …… Spring 中实现动态代理有两种,一种是如果我们的类定义了接口,如 UserService 接口和 UserServiceImpl 实现,那么采用 JDK 的动态代理,感兴趣的读者可以去看看 java.lang.reflect.Proxy 类的源码;另一种是我们自己没有定义接口的,Spring 会采用 CGLIB 进行动态代理,它是一个 jar 包,性能还不错。 适配器模式说完代理模式,说适配器模式,是因为它们很相似,这里可以做个比较。 适配器模式做的就是,有一个接口需要实现,但是我们现成的对象都不满足,需要加一层适配器来进行适配。 适配器模式总体来说分三种:默认适配器模式、对象适配器模式、类适配器模式。先不急着分清楚这几个,先看看例子再说。 默认适配器模式首先,我们先看看最简单的适配器模式默认适配器模式(Default Adapter)是怎么样的。 我们用 Appache commons-io 包中的 FileAlterationListener 做例子,此接口定义了很多的方法,用于对文件或文件夹进行监控,一旦发生了对应的操作,就会触发相应的方法。 public interface FileAlterationListener { void onStart(final FileAlterationObserver observer); void onDirectoryCreate(final File directory); void onDirectoryChange(final File directory); void onDirectoryDelete(final File directory); void onFileCreate(final File file); void onFileChange(final File file); void onFileDelete(final File file); void onStop(final FileAlterationObserver observer); } 此接口的一大问题是抽象方法太多了,如果我们要用这个接口,意味着我们要实现每一个抽象方法,如果我们只是想要监控文件夹中的文件创建和文件删除事件,可是我们还是不得不实现所有的方法,很明显,这不是我们想要的。 所以,我们需要下面的一个适配器,它用于实现上面的接口,但是所有的方法都是空方法,这样,我们就可以转而定义自己的类来继承下面这个类即可。 public class FileAlterationListenerAdaptor implements FileAlterationListener { public void onStart(final FileAlterationObserver observer) { } public void onDirectoryCreate(final File directory) { } public void onDirectoryChange(final File directory) { } public void onDirectoryDelete(final File directory) { } public void onFileCreate(final File file) { } public void onFileChange(final File file) { } public void onFileDelete(final File file) { } public void onStop(final FileAlterationObserver observer) { } } 比如我们可以定义以下类,我们仅仅需要实现我们想实现的方法就可以了: public class FileMonitor extends FileAlterationListenerAdaptor { public void onFileCreate(final File file) { // 文件创建 doSomething(); } public void onFileDelete(final File file) { // 文件删除 doSomething(); } } 当然,上面说的只是适配器模式的其中一种,也是最简单的一种,无需多言。下面,再介绍“正统的”适配器模式。 对象适配器模式来看一个《Head First 设计模式》中的一个例子,我稍微修改了一下,看看怎么将鸡适配成鸭,这样鸡也能当鸭来用。因为,现在鸭这个接口,我们没有合适的实现类可以用,所以需要适配器。 public interface Duck { public void quack(); // 鸭的呱呱叫 public void fly(); // 飞 } public interface Cock { public void gobble(); // 鸡的咕咕叫 public void fly(); // 飞 } public class WildCock implements Cock { public void gobble() { System.out.println("咕咕叫"); } public void fly() { System.out.println("鸡也会飞哦"); } } 鸭接口有 fly() 和 quare() 两个方法,鸡 Cock 如果要冒充鸭,fly() 方法是现成的,但是鸡不会鸭的呱呱叫,没有 quack() 方法。这个时候就需要适配了: // 毫无疑问,首先,这个适配器肯定需要 implements Duck,这样才能当做鸭来用 public class CockAdapter implements Duck { Cock cock; // 构造方法中需要一个鸡的实例,此类就是将这只鸡适配成鸭来用 public CockAdapter(Cock cock) { this.cock = cock; } // 实现鸭的呱呱叫方法 @Override public void quack() { // 内部其实是一只鸡的咕咕叫 cock.gobble(); } @Override public void fly() { cock.fly(); } } 客户端调用很简单了: public static void main(String[] args) { // 有一只野鸡 Cock wildCock = new WildCock(); // 成功将野鸡适配成鸭 Duck duck = new CockAdapter(wildCock); ... } 到这里,大家也就知道了适配器模式是怎么回事了。无非是我们需要一只鸭,但是我们只有一只鸡,这个时候就需要定义一个适配器,由这个适配器来充当鸭,但是适配器里面的方法还是由鸡来实现的。 我们用一个图来简单说明下: 上图应该还是很容易理解的,我就不做更多的解释了。下面,我们看看类适配模式怎么样的。 类适配器模式废话少说,直接上图: 看到这个图,大家应该很容易理解的吧,通过继承的方法,适配器自动获得了所需要的大部分方法。这个时候,客户端使用更加简单,直接 Target t = new SomeAdapter(); 就可以了。 适配器模式总结 类适配和对象适配的异同 一个采用继承,一个采用组合; 类适配属于静态实现,对象适配属于组合的动态实现,对象适配需要多实例化一个对象。 总体来说,对象适配用得比较多。 适配器模式和代理模式的异同 比较这两种模式,其实是比较对象适配器模式和代理模式,在代码结构上,它们很相似,都需要一个具体的实现类的实例。但是它们的目的不一样,代理模式做的是增强原方法的活;适配器做的是适配的活,为的是提供“把鸡包装成鸭,然后当做鸭来使用”,而鸡和鸭它们之间原本没有继承关系。 桥梁模式理解桥梁模式,其实就是理解代码抽象和解耦。 我们首先需要一个桥梁,它是一个接口,定义提供的接口方法。 public interface DrawAPI { public void draw(int radius, int x, int y); } 然后是一系列实现类: public class RedPen implements DrawAPI { @Override public void draw(int radius, int x, int y) { System.out.println("用红色笔画图,radius:" + radius + ", x:" + x + ", y:" + y); } } public class GreenPen implements DrawAPI { @Override public void draw(int radius, int x, int y) { System.out.println("用绿色笔画图,radius:" + radius + ", x:" + x + ", y:" + y); } } public class BluePen implements DrawAPI { @Override public void draw(int radius, int x, int y) { System.out.println("用蓝色笔画图,radius:" + radius + ", x:" + x + ", y:" + y); } } 定义一个抽象类,此类的实现类都需要使用 DrawAPI: public abstract class Shape { protected DrawAPI drawAPI; protected Shape(DrawAPI drawAPI){ this.drawAPI = drawAPI; } public abstract void draw(); } 定义抽象类的子类: // 圆形 public class Circle extends Shape { private int radius; public Circle(int radius, DrawAPI drawAPI) { super(drawAPI); this.radius = radius; } public void draw() { drawAPI.draw(radius, 0, 0); } } // 长方形 public class Rectangle extends Shape { private int x; private int y; public Rectangle(int x, int y, DrawAPI drawAPI) { super(drawAPI); this.x = x; this.y = y; } public void draw() { drawAPI.draw(0, x, y); } } 最后,我们来看客户端演示: public static void main(String[] args) { Shape greenCircle = new Circle(10, new GreenPen()); Shape redRectangle = new Rectangle(4, 8, new RedPen()); greenCircle.draw(); redRectangle.draw(); } 可能大家看上面一步步还不是特别清晰,我把所有的东西整合到一张图上: 这回大家应该就知道抽象在哪里,怎么解耦了吧。桥梁模式的优点也是显而易见的,就是非常容易进行扩展。 本节引用了这里的例子,并对其进行了修改。 装饰模式要把装饰模式说清楚明白,不是件容易的事情。也许读者知道 Java IO 中的几个类是典型的装饰模式的应用,但是读者不一定清楚其中的关系,也许看完就忘了,希望看完这节后,读者可以对其有更深的感悟。 首先,我们先看一个简单的图,看这个图的时候,了解下层次结构就可以了: 我们来说说装饰模式的出发点,从图中可以看到,接口 Component 其实已经有了 ConcreteComponentA 和 ConcreteComponentB 两个实现类了,但是,如果我们要增强这两个实现类的话,我们就可以采用装饰模式,用具体的装饰器来装饰实现类,以达到增强的目的。 从名字来简单解释下装饰器。既然说是装饰,那么往往就是添加小功能这种,而且,我们要满足可以添加多个小功能。最简单的,代理模式就可以实现功能的增强,但是代理不容易实现多个功能的增强,当然你可以说用代理包装代理的方式,但是那样的话代码就复杂了。 首先明白一些简单的概念,从图中我们看到,所有的具体装饰者们 ConcreteDecorator 都可以作为 Component 来使用,因为它们都实现了 Component 中的所有接口。它们和 Component 实现类 ConcreteComponent 的区别是,它们只是装饰者,起装饰作用,也就是即使它们看上去牛逼轰轰,但是它们都只是在具体的实现中加了层皮来装饰而已。 注意这段话中混杂在各个名词中的 Component 和 Decorator,别搞混了。 下面来看看一个例子,先把装饰模式弄清楚,然后再介绍下 java io 中的装饰模式的应用。 最近大街上流行起来了“快乐柠檬”,我们把快乐柠檬的饮料分为三类:红茶、绿茶、咖啡,在这三大类的基础上,又增加了许多的口味,什么金桔柠檬红茶、金桔柠檬珍珠绿茶、芒果红茶、芒果绿茶、芒果珍珠红茶、烤珍珠红茶、烤珍珠芒果绿茶、椰香胚芽咖啡、焦糖可可咖啡等等,每家店都有很长的菜单,但是仔细看下,其实原料也没几样,但是可以搭配出很多组合,如果顾客需要,很多没出现在菜单中的饮料他们也是可以做的。 在这个例子中,红茶、绿茶、咖啡是最基础的饮料,其他的像金桔柠檬、芒果、珍珠、椰果、焦糖等都属于装饰用的。当然,在开发中,我们确实可以像门店一样,开发这些类:LemonBlackTea、LemonGreenTea、MangoBlackTea、MangoLemonGreenTea……但是,很快我们就发现,这样子干肯定是不行的,这会导致我们需要组合出所有的可能,而且如果客人需要在红茶中加双份柠檬怎么办?三份柠檬怎么办?万一有个变态要四份柠檬,所以这种做法是给自己找加班的。 不说废话了,上代码。 首先,定义饮料抽象基类: public abstract class Beverage { // 返回描述 public abstract String getDescription(); // 返回价格 public abstract double cost(); } 然后是三个基础饮料实现类,红茶、绿茶和咖啡: public class BlackTea extends Beverage { public String getDescription() { return "红茶"; } public double cost() { return 10; } } public class GreenTea extends Beverage { public String getDescription() { return "绿茶"; } public double cost() { return 11; } } ...// 咖啡省略 定义调料,也就是装饰者的基类,此类必须继承自 Beverage: // 调料 public abstract class Condiment extends Beverage { } 然后我们来定义柠檬、芒果等具体的调料,它们属于装饰者,毫无疑问,这些调料肯定都需要继承 Condiment 类: public class Lemon extends Condiment { private Beverage bevarage; // 这里很关键,需要传入具体的饮料,如需要传入没有被装饰的红茶或绿茶, // 当然也可以传入已经装饰好的芒果绿茶,这样可以做芒果柠檬绿茶 public Lemon(Beverage bevarage) { this.bevarage = bevarage; } public String getDescription() { // 装饰 return bevarage.getDescription() + ", 加柠檬"; } public double cost() { // 装饰 return beverage.cost() + 2; // 加柠檬需要 2 元 } } public class Mango extends Condiment { private Beverage bevarage; public Mango(Beverage bevarage) { this.bevarage = bevarage; } public String getDescription() { return bevarage.getDescription() + ", 加芒果"; } public double cost() { return beverage.cost() + 3; // 加芒果需要 3 元 } } ...// 给每一种调料都加一个类 看客户端调用: public static void main(String[] args) { // 首先,我们需要一个基础饮料,红茶、绿茶或咖啡 Beverage beverage = new GreenTea(); // 开始装饰 beverage = new Lemon(beverage); // 先加一份柠檬 beverage = new Mongo(beverage); // 再加一份芒果 System.out.println(beverage.getDescription() + " 价格:¥" + beverage.cost()); //"绿茶, 加柠檬, 加芒果 价格:¥16" } 如果我们需要芒果珍珠双份柠檬红茶: Beverage beverage = new Mongo(new Pearl(new Lemon(new Lemon(new BlackTea())))); 是不是很变态? 看看下图可能会清晰一些: 到这里,大家应该已经清楚装饰模式了吧。 下面,我们再来说说 java IO 中的装饰模式。看下图 InputStream 派生出来的部分类: 我们知道 InputStream 代表了输入流,具体的输入来源可以是文件(FileInputStream)、管道(PipedInputStream)、数组(ByteArrayInputStream)等,这些就像前面奶茶的例子中的红茶、绿茶,属于基础输入流。 FilterInputStream 承接了装饰模式的关键节点,其实现类是一系列装饰器,比如 BufferedInputStream 代表用缓冲来装饰,也就使得输入流具有了缓冲的功能,LineNumberInputStream 代表用行号来装饰,在操作的时候就可以取得行号了,DataInputStream 的装饰,使得我们可以从输入流转换为 java 中的基本类型值。 当然,在 java IO 中,如果我们使用装饰器的话,就不太适合面向接口编程了,如: InputStream inputStream = new LineNumberInputStream(new BufferedInputStream(new FileInputStream(""))); 这样的结果是,InputStream 还是不具有读取行号的功能,因为读取行号的方法定义在 LineNumberInputStream 类中。 我们应该像下面这样使用: DataInputStream is = new DataInputStream( new BufferedInputStream( new FileInputStream(""))); 所以说嘛,要找到纯的严格符合设计模式的代码还是比较难的。 门面模式门面模式(也叫外观模式,Facade Pattern)在许多源码中有使用,比如 slf4j 就可以理解为是门面模式的应用。这是一个简单的设计模式,我们直接上代码再说吧。 首先,我们定义一个接口: public interface Shape { void draw(); } 定义几个实现类: public class Circle implements Shape { @Override public void draw() { System.out.println("Circle::draw()"); } } public class Rectangle implements Shape { @Override public void draw() { System.out.println("Rectangle::draw()"); } } 客户端调用: public static void main(String[] args) { // 画一个圆形 Shape circle = new Circle(); circle.draw(); // 画一个长方形 Shape rectangle = new Rectangle(); rectangle.draw(); } 以上是我们常写的代码,我们需要画圆就要先实例化圆,画长方形就需要先实例化一个长方形,然后再调用相应的 draw() 方法。 下面,我们看看怎么用门面模式来让客户端调用更加友好一些。 我们先定义一个门面: public class ShapeMaker { private Shape circle; private Shape rectangle; private Shape square; public ShapeMaker() { circle = new Circle(); rectangle = new Rectangle(); square = new Square(); } /** * 下面定义一堆方法,具体应该调用什么方法,由这个门面来决定 */ public void drawCircle(){ circle.draw(); } public void drawRectangle(){ rectangle.draw(); } public void drawSquare(){ square.draw(); } } 看看现在客户端怎么调用: public static void main(String[] args) { ShapeMaker shapeMaker = new ShapeMaker(); // 客户端调用现在更加清晰了 shapeMaker.drawCircle(); shapeMaker.drawRectangle(); shapeMaker.drawSquare(); } 门面模式的优点显而易见,客户端不再需要关注实例化时应该使用哪个实现类,直接调用门面提供的方法就可以了,因为门面类提供的方法的方法名对于客户端来说已经很友好了。 组合模式组合模式用于表示具有层次结构的数据,使得我们对单个对象和组合对象的访问具有一致性。 直接看一个例子吧,每个员工都有姓名、部门、薪水这些属性,同时还有下属员工集合(虽然可能集合为空),而下属员工和自己的结构是一样的,也有姓名、部门这些属性,同时也有他们的下属员工集合。 public class Employee { private String name; private String dept; private int salary; private List<Employee> subordinates; // 下属 public Employee(String name,String dept, int sal) { this.name = name; this.dept = dept; this.salary = sal; subordinates = new ArrayList<Employee>(); } public void add(Employee e) { subordinates.add(e); } public void remove(Employee e) { subordinates.remove(e); } public List<Employee> getSubordinates(){ return subordinates; } public String toString(){ return ("Employee :[ Name : " + name + ", dept : " + dept + ", salary :" + salary+" ]"); } } 通常,这种类需要定义 add(node)、remove(node)、getChildren() 这些方法。 这说的其实就是组合模式,这种简单的模式我就不做过多介绍了,相信各位读者也不喜欢看我写废话。 享元模式英文是 Flyweight Pattern,不知道是谁最先翻译的这个词,感觉这翻译真的不好理解,我们试着强行关联起来吧。Flyweight 是轻量级的意思,享元分开来说就是 共享 元器件,也就是复用已经生成的对象,这种做法当然也就是轻量级的了。 复用对象最简单的方式是,用一个 HashMap 来存放每次新生成的对象。每次需要一个对象的时候,先到 HashMap 中看看有没有,如果没有,再生成新的对象,然后将这个对象放入 HashMap 中。 这种简单的代码我就不演示了。 结构型模式总结前面,我们说了代理模式、适配器模式、桥梁模式、装饰模式、门面模式、组合模式和享元模式。读者是否可以分别把这几个模式说清楚了呢?在说到这些模式的时候,心中是否有一个清晰的图或处理流程在脑海里呢? 代理模式是做方法增强的,适配器模式是把鸡包装成鸭这种用来适配接口的,桥梁模式做到了很好的解耦,装饰模式从名字上就看得出来,适合于装饰类或者说是增强类的场景,门面模式的优点是客户端不需要关心实例化过程,只要调用需要的方法即可,组合模式用于描述具有层次结构的数据,享元模式是为了在特定的场景中缓存已经创建的对象,用于提高性能。 行为型模式行为型模式关注的是各个类之间的相互作用,将职责划分清楚,使得我们的代码更加地清晰。 策略模式策略模式太常用了,所以把它放到最前面进行介绍。它比较简单,我就不废话,直接用代码说事吧。 下面设计的场景是,我们需要画一个图形,可选的策略就是用红色笔来画,还是绿色笔来画,或者蓝色笔来画。 首先,先定义一个策略接口: public interface Strategy { public void draw(int radius, int x, int y); } 然后我们定义具体的几个策略: public class RedPen implements Strategy { @Override public void draw(int radius, int x, int y) { System.out.println("用红色笔画图,radius:" + radius + ", x:" + x + ", y:" + y); } } public class GreenPen implements Strategy { @Override public void draw(int radius, int x, int y) { System.out.println("用绿色笔画图,radius:" + radius + ", x:" + x + ", y:" + y); } } public class BluePen implements Strategy { @Override public void draw(int radius, int x, int y) { System.out.println("用蓝色笔画图,radius:" + radius + ", x:" + x + ", y:" + y); } } 使用策略的类: public class Context { private Strategy strategy; public Context(Strategy strategy){ this.strategy = strategy; } public int executeDraw(int radius, int x, int y){ return strategy.draw(radius, x, y); } } 客户端演示: public static void main(String[] args) { Context context = new Context(new BluePen()); // 使用绿色笔来画 context.executeDraw(10, 0, 0); } 放到一张图上,让大家看得清晰些: 这个时候,大家有没有联想到结构型模式中的桥梁模式,它们其实非常相似,我把桥梁模式的图拿过来大家对比下: 要我说的话,它们非常相似,桥梁模式在左侧加了一层抽象而已。桥梁模式的耦合更低,结构更复杂一些。 观察者模式观察者模式对于我们来说,真是再简单不过了。无外乎两个操作,观察者订阅自己关心的主题和主题有数据变化后通知观察者们。 首先,需要定义主题,每个主题需要持有观察者列表的引用,用于在数据变更的时候通知各个观察者: public class Subject { private List<Observer> observers = new ArrayList<Observer>(); private int state; public int getState() { return state; } public void setState(int state) { this.state = state; // 数据已变更,通知观察者们 notifyAllObservers(); } public void attach(Observer observer){ observers.add(observer); } // 通知观察者们 public void notifyAllObservers(){ for (Observer observer : observers) { observer.update(); } } } 定义观察者接口: public abstract class Observer { protected Subject subject; public abstract void update(); } 其实如果只有一个观察者类的话,接口都不用定义了,不过,通常场景下,既然用到了观察者模式,我们就是希望一个事件出来了,会有多个不同的类需要处理相应的信息。比如,订单修改成功事件,我们希望发短信的类得到通知、发邮件的类得到通知、处理物流信息的类得到通知等。 我们来定义具体的几个观察者类: public class BinaryObserver extends Observer { // 在构造方法中进行订阅主题 public BinaryObserver(Subject subject) { this.subject = subject; // 通常在构造方法中将 this 发布出去的操作一定要小心 this.subject.attach(this); } // 该方法由主题类在数据变更的时候进行调用 @Override public void update() { String result = Integer.toBinaryString(subject.getState()); System.out.println("订阅的数据发生变化,新的数据处理为二进制值为:" + result); } } public class HexaObserver extends Observer { public HexaObserver(Subject subject) { this.subject = subject; this.subject.attach(this); } @Override public void update() { String result = Integer.toHexString(subject.getState()).toUpperCase(); System.out.println("订阅的数据发生变化,新的数据处理为十六进制值为:" + result); } } 客户端使用也非常简单: public static void main(String[] args) { // 先定义一个主题 Subject subject1 = new Subject(); // 定义观察者 new BinaryObserver(subject1); new HexaObserver(subject1); // 模拟数据变更,这个时候,观察者们的 update 方法将会被调用 subject.setState(11); } output: 订阅的数据发生变化,新的数据处理为二进制值为:1011 订阅的数据发生变化,新的数据处理为十六进制值为:B 当然,jdk 也提供了相似的支持,具体的大家可以参考 java.util.Observable 和 java.util.Observer 这两个类。 实际生产过程中,观察者模式往往用消息中间件来实现,如果要实现单机观察者模式,笔者建议读者使用 Guava 中的 EventBus,它有同步实现也有异步实现,本文主要介绍设计模式,就不展开说了。 责任链模式责任链通常需要先建立一个单向链表,然后调用方只需要调用头部节点就可以了,后面会自动流转下去。比如流程审批就是一个很好的例子,只要终端用户提交申请,根据申请的内容信息,自动建立一条责任链,然后就可以开始流转了。 有这么一个场景,用户参加一个活动可以领取奖品,但是活动需要进行很多的规则校验然后才能放行,比如首先需要校验用户是否是新用户、今日参与人数是否有限额、全场参与人数是否有限额等等。设定的规则都通过后,才能让用户领走奖品。 如果产品给你这个需求的话,我想大部分人一开始肯定想的就是,用一个 List 来存放所有的规则,然后 foreach 执行一下每个规则就好了。不过,读者也先别急,看看责任链模式和我们说的这个有什么不一样? 首先,我们要定义流程上节点的基类: public abstract class RuleHandler { // 后继节点 protected RuleHandler successor; public abstract void apply(Context context); public void setSuccessor(RuleHandler successor) { this.successor = successor; } public RuleHandler getSuccessor() { return successor; } } 接下来,我们需要定义具体的每个节点了。 校验用户是否是新用户: public class NewUserRuleHandler extends RuleHandler { public void apply(Context context) { if (context.isNewUser()) { // 如果有后继节点的话,传递下去 if (this.getSuccessor() != null) { this.getSuccessor().apply(context); } } else { throw new RuntimeException("该活动仅限新用户参与"); } } } 校验用户所在地区是否可以参与: public class LocationRuleHandler extends RuleHandler { public void apply(Context context) { boolean allowed = activityService.isSupportedLocation(context.getLocation); if (allowed) { if (this.getSuccessor() != null) { this.getSuccessor().apply(context); } } else { throw new RuntimeException("非常抱歉,您所在的地区无法参与本次活动"); } } } 校验奖品是否已领完: public class LimitRuleHandler extends RuleHandler { public void apply(Context context) { int remainedTimes = activityService.queryRemainedTimes(context); // 查询剩余奖品 if (remainedTimes > 0) { if (this.getSuccessor() != null) { this.getSuccessor().apply(userInfo); } } else { throw new RuntimeException("您来得太晚了,奖品被领完了"); } } } 客户端: public static void main(String[] args) { RuleHandler newUserHandler = new NewUserRuleHandler(); RuleHandler locationHandler = new LocationRuleHandler(); RuleHandler limitHandler = new LimitRuleHandler(); // 假设本次活动仅校验地区和奖品数量,不校验新老用户 locationHandler.setSuccessor(limitHandler); locationHandler.apply(context); } 代码其实很简单,就是先定义好一个链表,然后在通过任意一节点后,如果此节点有后继节点,那么传递下去。 至于它和我们前面说的用一个 List 存放需要执行的规则的做法有什么异同,留给读者自己琢磨吧。 模板方法模式在含有继承结构的代码中,模板方法模式是非常常用的,这也是在开源代码中大量被使用的。 通常会有一个抽象类: public abstract class AbstractTemplate { // 这就是模板方法 public void templateMethod(){ init(); apply(); // 这个是重点 end(); // 可以作为钩子方法 } protected void init() { System.out.println("init 抽象层已经实现,子类也可以选择覆写"); } // 留给子类实现 protected abstract void apply(); protected void end() { } } 模板方法中调用了 3 个方法,其中 apply() 是抽象方法,子类必须实现它,其实模板方法中有几个抽象方法完全是自由的,我们也可以将三个方法都设置为抽象方法,让子类来实现。也就是说,模板方法只负责定义第一步应该要做什么,第二步应该做什么,第三步应该做什么,至于怎么做,由子类来实现。 我们写一个实现类: public class ConcreteTemplate extends AbstractTemplate { public void apply() { System.out.println("子类实现抽象方法 apply"); } public void end() { System.out.println("我们可以把 method3 当做钩子方法来使用,需要的时候覆写就可以了"); } } 客户端调用演示: public static void main(String[] args) { AbstractTemplate t = new ConcreteTemplate(); // 调用模板方法 t.templateMethod(); } 代码其实很简单,基本上看到就懂了,关键是要学会用到自己的代码中。 状态模式update: 2017-10-19 废话我就不说了,我们说一个简单的例子。商品库存中心有个最基本的需求是减库存和补库存,我们看看怎么用状态模式来写。 核心在于,我们的关注点不再是 Context 是该进行哪种操作,而是关注在这个 Context 会有哪些操作。 定义状态接口: public interface State { public void doAction(Context context); } 定义减库存的状态: public class DeductState implements State { public void doAction(Context context) { System.out.println("商品卖出,准备减库存"); context.setState(this); //... 执行减库存的具体操作 } public String toString(){ return "Deduct State"; } } 定义补库存状态: public class RevertState implements State { public void doAction(Context context) { System.out.println("给此商品补库存"); context.setState(this); //... 执行加库存的具体操作 } public String toString() { return "Revert State"; } } 前面用到了 context.setState(this),我们来看看怎么定义 Context 类: public class Context { private State state; private String name; public Context(String name) { this.name = name; } public void setState(State state) { this.state = state; } public void getState() { return this.state; } } 我们来看下客户端调用,大家就一清二楚了: public static void main(String[] args) { // 我们需要操作的是 iPhone X Context context = new Context("iPhone X"); // 看看怎么进行补库存操作 State revertState = new RevertState(); revertState.doAction(context); // 同样的,减库存操作也非常简单 State deductState = new DeductState(); deductState.doAction(context); // 如果需要我们可以获取当前的状态 // context.getState().toString(); } 读者可能会发现,在上面这个例子中,如果我们不关心当前 context 处于什么状态,那么 Context 就可以不用维护 state 属性了,那样代码会简单很多。 不过,商品库存这个例子毕竟只是个例,我们还有很多实例是需要知道当前 context 处于什么状态的。 行为型模式总结行为型模式部分介绍了策略模式、观察者模式、责任链模式、模板方法模式和状态模式,其实,经典的行为型模式还包括备忘录模式、命令模式等,但是它们的使用场景比较有限,而且本文篇幅也挺大了,我就不进行介绍了。 总结学习设计模式的目的是为了让我们的代码更加的优雅、易维护、易扩展。我以前学设计模式的初衷是为了看懂开源代码,也为了写出装B的代码。这次整理这篇文章,让我重新审视了一下各个设计模式,对我自己而言收获还是挺大的。我想,文章的最大收益者一般都是作者本人,为了写一篇文章,需要巩固自己的知识,需要寻找各种资料,而且,自己写过的才最容易记住,也算是我给读者的建议吧。 (全文完)","categories":[],"tags":[{"name":"Java","slug":"Java","permalink":"http://www.mvtime.cn/tags/Java/"},{"name":"设计模式","slug":"设计模式","permalink":"http://www.mvtime.cn/tags/设计模式/"}]},{"title":"Mac安装mycli命令行工具","slug":"Mac安装mycli命令行工具","date":"2017-10-25T05:18:25.000Z","updated":"2017-10-25T06:16:31.270Z","comments":true,"path":"2017/10/25/Mac安装mycli命令行工具/","link":"","permalink":"http://www.mvtime.cn/2017/10/25/Mac安装mycli命令行工具/","excerpt":"","text":"mac安装方法 $ brew update && brew install mycli 使用方法$ mycli --help Usage: mycli [OPTIONS] [DATABASE] Options: -h, --host TEXT Host address of the database. -P, --port INTEGER Port number to use for connection. Honors $MYSQL_TCP_PORT -u, --user TEXT User name to connect to the database. -S, --socket TEXT The socket file to use for connection. -p, --password TEXT Password to connect to the database --pass TEXT Password to connect to the database --ssl-ca PATH CA file in PEM format --ssl-capath TEXT CA directory --ssl-cert PATH X509 cert in PEM format --ssl-key PATH X509 key in PEM format --ssl-cipher TEXT SSL cipher to use --ssl-verify-server-cert Verify server's "Common Name" in its cert against hostname used when connecting. This option is disabled by default -v, --version Version of mycli. -D, --database TEXT Database to use. -R, --prompt TEXT Prompt format (Default: "\\t \\u@\\h:\\d> ") -l, --logfile FILENAME Log every query and its results to a file. --defaults-group-suffix TEXT Read config group with the specified suffix. --defaults-file PATH Only read default options from the given file --myclirc PATH Location of myclirc file. --auto-vertical-output Automatically switch to vertical output mode if the result is wider than the terminal width. -t, --table Display batch output in table format. --csv Display batch output in CSV format. --warn / --no-warn Warn before running a destructive query. --local-infile BOOLEAN Enable/disable LOAD DATA LOCAL INFILE. --login-path TEXT Read this path from the login file. -e, --execute TEXT Execute query to the database. --help Show this message and exit.123456789101112131415161718192021222324252627282930313233343536 连接数据库$ mycli local_database $ mycli -h localhost -u root app_db $ mycli mysql://amjith@localhost:3306/django_poll ##提醒 sudo chown -R $(whoami) /usr/local 把/use/local的owner換成自己,就有write權限了 whoami就是一個命令,會echo當前登錄用戶的名字。當然你知道自己的用戶名,比如jack,就直接 sudo chown -R jack /usr/local 也可以 修改過權限之後,就直接跑 brew install node 不需要再用sudo,因為當前用戶已經有權限寫入/usr/local了","categories":[],"tags":[{"name":"技术","slug":"技术","permalink":"http://www.mvtime.cn/tags/技术/"},{"name":"Mac","slug":"Mac","permalink":"http://www.mvtime.cn/tags/Mac/"}]},{"title":"iOS统一更换字体和大小","slug":"iOS统一更换字体和大小","date":"2017-09-21T02:15:10.000Z","updated":"2017-09-21T02:16:21.000Z","comments":true,"path":"2017/09/21/iOS统一更换字体和大小/","link":"","permalink":"http://www.mvtime.cn/2017/09/21/iOS统一更换字体和大小/","excerpt":"","text":"// // UILabel+FontChange.m // LiquoriceDoctorProject // // Created by HenryCheng on 15/12/7. // Copyright © 2015年 iMac. All rights reserved. // #import \"UILabel+FontChange.h\" #import //#define CustomFontName @\"FZLBJW--GB1-0\" @implementation UILabel (FontChange) + (void)load { //方法交换应该被保证,在程序中只会执行一次 static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ //获得viewController的生命周期方法的selector SEL systemSel = @selector(willMoveToSuperview:); //自己实现的将要被交换的方法的selector SEL swizzSel = @selector(myWillMoveToSuperview:); //两个方法的Method Method systemMethod = class_getInstanceMethod([self class], systemSel); Method swizzMethod = class_getInstanceMethod([self class], swizzSel); //首先动态添加方法,实现是被交换的方法,返回值表示添加成功还是失败 BOOL isAdd = class_addMethod(self, systemSel, method_getImplementation(swizzMethod), method_getTypeEncoding(swizzMethod)); if (isAdd) { //如果成功,说明类中不存在这个方法的实现 //将被交换方法的实现替换到这个并不存在的实现 class_replaceMethod(self, swizzSel, method_getImplementation(systemMethod), method_getTypeEncoding(systemMethod)); } else { //否则,交换两个方法的实现 method_exchangeImplementations(systemMethod, swizzMethod); } }); } - (void)myWillMoveToSuperview:(UIView *)newSuperview { [self myWillMoveToSuperview:newSuperview]; if ([self isKindOfClass:NSClassFromString(@\"UIButtonLabel\")] || [self isKindOfClass:NSClassFromString(@\"UITextFieldLabel\")]) { return; } if (self) { if (self.tag == 10086) { self.font = [UIFont fontWithName:@\"SourceHanSansCN-Light\" size:self.font.pointSize]; } else if (self.tag == 998) { self.font = [UIFont fontWithName:@\"SourceHanSansCN-Regular\" size:self.font.pointSize]; }else { NSString *fontName; fontName = @\"SourceHanSansCN-Light\"; if (SCREEN_WIDTH==414) { self.font = [UIFont fontWithName:fontName size:self.font.pointSize+2]; } else { self.font = [UIFont fontWithName:fontName size:self.font.pointSize]; } } } } @end","categories":[],"tags":[{"name":"技术","slug":"技术","permalink":"http://www.mvtime.cn/tags/技术/"},{"name":"iOS","slug":"iOS","permalink":"http://www.mvtime.cn/tags/iOS/"}]},{"title":"iOS中造成dealloc不调用的原因","slug":"iOS中造成dealloc不调用的原因","date":"2017-08-04T08:12:57.000Z","updated":"2017-08-04T08:14:17.000Z","comments":true,"path":"2017/08/04/iOS中造成dealloc不调用的原因/","link":"","permalink":"http://www.mvtime.cn/2017/08/04/iOS中造成dealloc不调用的原因/","excerpt":"","text":"问题描述最近在一个项目中用到了地图,发现在地图页面和上一个页面间反复切换回出现内存爆增的情况,就像吃了炫迈一样根本停不下来(直到app内存爆表,app闪退收场)。造成这一结果的根本原因是地图的mapView没有释放,导致每次打开地图界面的时候内存中都重新加载了一个地图mapView。于是在网上搜索了一番找到了解决办法,只需要在地图的ViewController中dealloc方法中释放掉mapView就行了。具体代码如下: - (void)dealloc { [_mapView release]; [super dealloc]; } //并且在界面将要显示的时候设置代理,将要消失的时候取消代理 - (void)viewWillAppear:(BOOL)animated { _mapView.delegate = self; } - (void)viewWillDisappear:(BOOL)animated { _mapView.delegate = nil; } 以上给出的方法确实是对的,可以解决反复切换地图页面和地图上一级页面内存暴增造成的闪退问题。但是这里要说的不是这个问题,而是一个新的问题,我在dealloc中打了断点,但是dealloc根本就没有执行,所以mapView也就根本就没有释放,内存还是一样在暴增。为什么ViewController已经被pop了,而ViewController的dealloc方法却没有被调用?(按理说ViewController被pop的时候它的dealloc的方法应该被调用才对)。 解决办法通过Google搜索终于在晚上找到了答案(大家就不要用百度,想要快速准确的找到自己想要的答案推荐大家用google)。造成ViewController不释放的原因可能有很多。遇到dealloc不调用的时候只需要检查您的ViewController中是否存在以下几个问题: ViewController中存在NSTimer 如果你的ViewController中有NSTimer,那么你就要注意了,因为当你调用 [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(updateTime:) userInfo:nil repeats:YES]; 时,这个target:self就增加了ViewController的return count,如果你不将这个timer invalidate,将别想调用dealloc。 ViewController中有关的代理 一个比较隐秘的因素,你去找找与这个类有关的代理,有没有强引用属性?比如一个代理的delegate应该是 assign 的现在是retain,(╯‵□′)╯︵┻━┻,就是这个,它会影响你不让你调用dealloc,不信,就试试吧。(这个我还没有遇到过)。 ViewController中有Block 这个就是我我上面不进入dealloc的真正原因,Block体内使用实例变量也会造成循环引用,使得拥有这个实例的对象不能释放。例如你这个类叫OneViewController,有个属性是NSString *name; 如果你在block体中使用了self.name,那样子的话这个类就没法释放。要解决这个问题,MRC下只需 __block Viewcontroller *weakSelf = self; ARC下将block 换为 weak 目前我所知道的就以上三种情况,如果有什么错误的地方或者还存在的一些情况,欢迎大家来补充。","categories":[],"tags":[{"name":"技术","slug":"技术","permalink":"http://www.mvtime.cn/tags/技术/"},{"name":"iOS","slug":"iOS","permalink":"http://www.mvtime.cn/tags/iOS/"}]},{"title":"iOS设置导航条返回item样式","slug":"iOS设置导航条返回item样式","date":"2017-08-03T03:07:09.000Z","updated":"2017-08-03T03:09:50.000Z","comments":true,"path":"2017/08/03/iOS设置导航条返回item样式/","link":"","permalink":"http://www.mvtime.cn/2017/08/03/iOS设置导航条返回item样式/","excerpt":"","text":"导航条的backitem在导航控制器里面设置会有很多问题,下面介绍一种重写控制器的viewload方法一劳永逸解决方法 #import \"UIViewController+EmptyBackButton.h\" #import @implementation UIViewController (EmptyBackButton) + (void)load { static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ Class class = [self class]; SEL originalSelector = @selector(viewDidLoad); SEL swizzledSelector = @selector(mob_viewDidLoad); Method originalMethod = class_getInstanceMethod(class, originalSelector); Method swizzledMethod = class_getInstanceMethod(class, swizzledSelector); BOOL didAddMethod = class_addMethod(class, originalSelector, method_getImplementation(swizzledMethod), method_getTypeEncoding(swizzledMethod)); if (didAddMethod) { class_replaceMethod(class, swizzledSelector, method_getImplementation(originalMethod), method_getTypeEncoding(originalMethod)); } else { method_exchangeImplementations(originalMethod, swizzledMethod); } }); } #pragma mark - Method Swizzling - (void)mob_viewDidLoad { [self mob_viewDidLoad]; UIBarButtonItem *backButtonItem = [[UIBarButtonItem alloc] initWithTitle:@\"\" style:UIBarButtonItemStylePlain target:nil action:nil]; UIImage* image = [UIImage imageNamed:@\"v2_goback_white\"]; [backButtonItem setBackButtonBackgroundImage:[image resizableImageWithCapInsets:UIEdgeInsetsMake(0, image.size.width, 0, 0)] forState:UIControlStateNormal barMetrics:UIBarMetricsDefault]; [self.navigationItem setBackBarButtonItem:backButtonItem]; } @end Runtime方式设置只有返回图片的backitem","categories":[],"tags":[{"name":"技术","slug":"技术","permalink":"http://www.mvtime.cn/tags/技术/"},{"name":"iOS","slug":"iOS","permalink":"http://www.mvtime.cn/tags/iOS/"}]},{"title":"Django+Vue服务端渲染","slug":"Django-Vue服务端渲染","date":"2017-08-01T06:58:12.000Z","updated":"2017-08-01T07:08:28.000Z","comments":true,"path":"2017/08/01/Django-Vue服务端渲染/","link":"","permalink":"http://www.mvtime.cn/2017/08/01/Django-Vue服务端渲染/","excerpt":"","text":"SPA项目的前后端开发的便利自然不用说,路由也是由前端决定。但是对于SEO不太友好,因为百度引擎不会等待ajax的完成,网上Vue大部分都是node.js的服务端渲染,下面我用了一种取巧的方式。当是搜索引擎访问走Django模板解析,普通用户重定向到Vue的路由 views配置# 电影渲染详情 class MovieDetailView(View): def get(self, request, movie_id): movie = Movie.objects.get(id=int(movie_id)) return render_to_response('detail.html', { 'movie': movie }) urls配置url(r'^detail/(?P<movie_id>\\d+)/index.html$', MovieDetailView.as_view()), Django的模板路径多加了一个index.html Nginx配置主要是nginx的跳转配置 if ($http_user_agent !~* (baiduspider|googlebot|soso|bing|sogou|yahoo|sohu-search|yodao|YoudaoBot|robozilla|msnbot|MJ12bot|NHN|Twiceler|MSIE)) { rewrite ^(.*)/detail/([0-9]+)/index.html http://www.mvtime.cn/detail/$2/ permanent; } location / { uwsgi_pass 0.0.0.0:3031; include /etc/nginx/uwsgi_params; try_files $uri $uri/ /; } location ~* \\.(html|txt)$ { uwsgi_pass 0.0.0.0:3031; include /etc/nginx/uwsgi_params; } 第一个location是Vue的history配置,提交给百度带有index.html当访问者不是爬虫去掉index.html走Vue的路由","categories":[],"tags":[{"name":"Django","slug":"Django","permalink":"http://www.mvtime.cn/tags/Django/"},{"name":"技术","slug":"技术","permalink":"http://www.mvtime.cn/tags/技术/"},{"name":"Vue","slug":"Vue","permalink":"http://www.mvtime.cn/tags/Vue/"}]},{"title":"Python3读取和写入Json","slug":"Python3读取和写入Json","date":"2017-07-19T06:03:28.000Z","updated":"2017-07-19T06:06:13.000Z","comments":true,"path":"2017/07/19/Python3读取和写入Json/","link":"","permalink":"http://www.mvtime.cn/2017/07/19/Python3读取和写入Json/","excerpt":"","text":"def get_city(): with open('city.json') as json_file: data = json.load(json_file) for model in data: model['childrenRegion'].insert(0, {'id': model['id'], 'name': '全部'}) return data def write_city(data): with open('city.json', 'w') as json_file: str = json.dumps(data, ensure_ascii=False, indent=2) # 写入时中文不转Unicode并且格式化 print(str) json_file.write(str) if __name__ == \"__main__\": write_city(get_city())","categories":[],"tags":[{"name":"技术","slug":"技术","permalink":"http://www.mvtime.cn/tags/技术/"},{"name":"Python","slug":"Python","permalink":"http://www.mvtime.cn/tags/Python/"}]},{"title":"xadmin添加富文本编辑器ueditor","slug":"xadmin添加富文本编辑器ueditor","date":"2017-07-17T02:02:54.000Z","updated":"2017-07-17T06:23:40.000Z","comments":true,"path":"2017/07/17/xadmin添加富文本编辑器ueditor/","link":"","permalink":"http://www.mvtime.cn/2017/07/17/xadmin添加富文本编辑器ueditor/","excerpt":"","text":"xadmin的强大不必再说,支持 python 3.5 的 xadmin 安装下载方法,下面说明如何添加富文本编辑器插件 Ueditor安装去https://github.com/twz915/DjangoUeditor3 下载兼容Python3,复制到extra_apps下面 INSTALLED_APPS = [ 'DjangoUeditor' ] 在extra_apps/xadmin/plugins下创建ueditor.py import xadmin from xadmin.views import BaseAdminPlugin, CreateAdminView, ModelFormAdminView, UpdateAdminView from DjangoUeditor.models import UEditorField from DjangoUeditor.widgets import UEditorWidget from django.conf import settings class XadminUEditorWidget(UEditorWidget): def __init__(self, **kwargs): self.ueditor_options=kwargs self.Media.js = None super(XadminUEditorWidget, self).__init__(kwargs) class UeditorPlugin(BaseAdminPlugin): def get_field_style(self, attrs, db_field, style, **kwargs): if style == 'ueditor': if isinstance(db_field, UEditorField): widget = db_field.formfield().widget param = {} param.update(widget.ueditor_settings) param.update(widget.attrs) return {'widget': XadminUEditorWidget(**param)} return attrs def block_extrahead(self, context, nodes): js = '<script type=\"text/javascript\" src=\"%s\"></script>' % (settings.STATIC_URL + \"ueditor/ueditor.config.js\") js += '<script type=\"text/javascript\" src=\"%s\"></script>' % (settings.STATIC_URL + \"ueditor/ueditor.all.min.js\") nodes.append(js) xadmin.site.register_plugin(UeditorPlugin, UpdateAdminView) xadmin.site.register_plugin(UeditorPlugin, CreateAdminView) init.py安装PLUGINS = ('ueditor') Ueditor使用models.py添加from DjangoUeditor.models import UEditorField content = UEditorField(width=800, height=600, default='', verbose_name="电影详情") adminx.py添加style_fields = {"content": "ueditor"} DjangoUeditor配置说明https://github.com/twz915/DjangoUeditor3","categories":[],"tags":[{"name":"Django","slug":"Django","permalink":"http://www.mvtime.cn/tags/Django/"},{"name":"技术","slug":"技术","permalink":"http://www.mvtime.cn/tags/技术/"},{"name":"xadmin","slug":"xadmin","permalink":"http://www.mvtime.cn/tags/xadmin/"}]},{"title":"Django部署问题排查","slug":"Django部署问题排查","date":"2017-07-14T14:43:47.000Z","updated":"2017-07-15T02:18:52.000Z","comments":true,"path":"2017/07/14/Django部署问题排查/","link":"","permalink":"http://www.mvtime.cn/2017/07/14/Django部署问题排查/","excerpt":"","text":"Django问题排查有很多种方案,说一下我现在用的 阿里云安全组配置开启8000端口用于测试 执行python /home/MovieSite/manage.py runserver 0.0.0.0:8000,开启Django自带服务器调试,setting设置DEBUG = True 运行项目,查看控制台错误提示 目前发现的错误 mysqlmysql配置问题导致Got a packet bigger than ‘max_allowed_packet’ bytes” 进入/etc/mysql/mysql.conf.d配置max_allowed_packet = 64M 重启mysqlservice mysql restart","categories":[],"tags":[{"name":"Django","slug":"Django","permalink":"http://www.mvtime.cn/tags/Django/"},{"name":"技术","slug":"技术","permalink":"http://www.mvtime.cn/tags/技术/"},{"name":"服务器","slug":"服务器","permalink":"http://www.mvtime.cn/tags/服务器/"}]},{"title":"Python3实现百度站长主动推送","slug":"Python3实现百度站长主动推送","date":"2017-07-14T13:05:08.000Z","updated":"2017-07-14T13:06:22.000Z","comments":true,"path":"2017/07/14/Python3实现百度站长主动推送/","link":"","permalink":"http://www.mvtime.cn/2017/07/14/Python3实现百度站长主动推送/","excerpt":"","text":"import requests # 百度主动推送 def tuisong(): url = 'http://data.zz.baidu.com/urls?site=www.mvtime.cn&token=token' headers = {'User-Agent': 'curl/7.12.1', 'Host': 'data.zz.baidu.com', 'Content-Type': 'application/x-www-form-urlencoded', 'Content-Length': '83'} url_list = [] for i in range(1): url_list.append('http://www.mvtime.cn') data = '\\n'.join(url_list) print(data) r = requests.post(url, headers=headers, data=data) data = r.json() print(data) if __name__ == \"__main__\": ts = tuisong()","categories":[],"tags":[{"name":"Django","slug":"Django","permalink":"http://www.mvtime.cn/tags/Django/"},{"name":"技术","slug":"技术","permalink":"http://www.mvtime.cn/tags/技术/"},{"name":"Python","slug":"Python","permalink":"http://www.mvtime.cn/tags/Python/"}]},{"title":"阿里云搭建Vue+Django项目(详细篇)","slug":"阿里云搭建Vue-Django项目-详细篇","date":"2017-07-13T05:53:45.000Z","updated":"2017-12-05T09:29:47.657Z","comments":true,"path":"2017/07/13/阿里云搭建Vue-Django项目-详细篇/","link":"","permalink":"http://www.mvtime.cn/2017/07/13/阿里云搭建Vue-Django项目-详细篇/","excerpt":"","text":"使用阿里云搭建Ubuntu环境,nginx + uwsgi socket 的方式来部署 Django+Vue项目,包括但不限于定时任务,Scrapy环境配置,解决跨域,Nginx域名重定向,Vue Router的History模式的配置 参考文档:http://code.ziqiangxuetang.com/django/django-nginx-deploy.html https://sanwen8.cn/p/68bJEgr.html 环境搭建pip安装步骤一: 在终端上使用以下命令,来保证你系统上所有的包都是最新的。 sudo apt-get updatesudo apt-get upgrade 步骤二: 安装Pip 安装python-pip和你所需要的包: apt-get install python-pip 检查你所安装Pip的版本: pip-V Python3安装与默认shell里执行: sudo update-alternatives --install /usr/bin/python python /usr/bin/python2 100 sudo update-alternatives --install /usr/bin/python python /usr/bin/python3 150 如果要切换到Python2,执行: sudo update-alternatives --config python Python第三方库安装1、导出当前项目pip安装的包表 pip freeze > 项目目录/requirements.txt 2、根据导出的pip安装包表安装pip包 pip install -r 项目目录/requirements.txt 这里Scrapy库需要安装依赖 sudo apt-get install python-dev python-pip libxml2-dev libxslt1-dev zlib1g-dev libffi-dev libssl-dev 阿里云配置阿里云配置入口端口,包括3306的配置,这样才能远程连接mysql,mysql远程连接的配置http://www.cnblogs.com/ruofengzhishang/p/5477502.html Vue与Django的搭建Vue的build设置Vue项目build到Django项目根目录下,在config/index.js里面 Django替换模板视图 TemplateViewurls.py配置url(r'^$', TemplateView.as_view(template_name="index.html")), TEMPLATES = [ { 'BACKEND': 'django.template.backends.django.DjangoTemplates', 'DIRS': [os.path.join(BASE_DIR, 'movie')] , 'APP_DIRS': True, 'OPTIONS': { 'context_processors': [ 'django.template.context_processors.debug', 'django.template.context_processors.request', 'django.contrib.auth.context_processors.auth', 'django.contrib.messages.context_processors.messages', ], }, }, ] STATIC_URL = '/static/' MEDIA_URL = '/media/' MEDIA_ROOT = os.path.join(BASE_DIR, 'media') # Add for Vue.js STATICFILES_DIRS = [ os.path.join(BASE_DIR, \"movie/static\"), ] # collectstatic使用 STATIC_ROOT = os.path.join(BASE_DIR, \"static\") Django跨域1.安装django-cors-headers pip install django-cors-headers 2.配置settings.py文件 INSTALLED_APPS = [ ... 'corsheaders', ... ] MIDDLEWARE_CLASSES = ( ... 'corsheaders.middleware.CorsMiddleware', 'django.middleware.common.CommonMiddleware', # 注意顺序 ... ) #跨域增加忽略 CORS_ALLOW_CREDENTIALS = True CORS_ORIGIN_ALLOW_ALL = True CORS_ORIGIN_WHITELIST = ( '*' ) CORS_ALLOW_METHODS = ( 'DELETE', 'GET', 'OPTIONS', 'PATCH', 'POST', 'PUT', 'VIEW', ) CORS_ALLOW_HEADERS = ( 'XMLHttpRequest', 'X_FILENAME', 'accept-encoding', 'authorization', 'content-type', 'dnt', 'origin', 'user-agent', 'x-csrftoken', 'x-requested-with', 'Pragma', ) 运行开发服务器测试上传项目到/home目录下 进入项目目录 python manage.py collectstatic python manage.py runserver 打开网站看是否执行成功,如果执行成功说明项目没有问题 uwsgi部署安装 sudo pip install uwsgi --upgrade 配置文件 [uwsgi] socket = 0.0.0.0:3031 chdir = /home/MovieSite touch-reload = /home/MovieSite py-autoreload = 1 wsgi-file = MovieSite/wsgi.py processes = 4 threads = 2 stats = 0.0.0.0:3032 启动uwsgi uwsgi --ini site.ini Nginx配置安装 nginx 等软件 ubuntu / Linux Mint 等,下面简写为 (ubuntu): sudo apt-getinstallpython-dev nginx nginx配置 server { listen 80; server_name 47.94.171.61 www.mvtime.cn mvtime.cn; if ($host != "mvtime.cn"){ rewrite ^(.*) http://mvtime.cn$1 permanent; } root /home/MovieSite; access_log /var/log/nginx/access_narwhals.log; error_log /var/log/nginx/error_narwhals.log; location / { uwsgi_pass 0.0.0.0:3031; include /etc/nginx/uwsgi_params; set_real_ip_from 0.0.0.0/0; real_ip_header X-Forwarded-For; real_ip_recursive on; try_files $uri $uri/ /; } location /static/ { root /home/MovieSite; access_log off; } location ^~ /movies/ { uwsgi_pass 0.0.0.0:3031; include /etc/nginx/uwsgi_params; } } 其中try_files用来解决Vue的history模式问题 启动ngingx service nginx start 爬虫与定时任务定时任务crontab -l 查看定时任务 0 10 * /bin/bash /home/run.sh >/home/result.log 2>&1 输入log到/home/result.log crontab -e 重写定时任务 sh文件程序必须用绝对路径 #!/bin/bash # 进入爬虫目录 cd /home/MovieSite/movies_scrapy/movies_scrapy && # 运行爬虫脚本 /usr/local/bin/scrapy crawl newmovie && # 运行爬虫脚本 /usr/local/bin/scrapy crawl hotmovie","categories":[],"tags":[{"name":"Django","slug":"Django","permalink":"http://www.mvtime.cn/tags/Django/"},{"name":"技术","slug":"技术","permalink":"http://www.mvtime.cn/tags/技术/"},{"name":"Vue","slug":"Vue","permalink":"http://www.mvtime.cn/tags/Vue/"},{"name":"服务器","slug":"服务器","permalink":"http://www.mvtime.cn/tags/服务器/"}]},{"title":"Django-RestFramework添加自定义字段","slug":"Django-RestFramework添加自定义字段","date":"2017-06-13T02:05:36.000Z","updated":"2017-06-13T02:07:53.000Z","comments":true,"path":"2017/06/13/Django-RestFramework添加自定义字段/","link":"","permalink":"http://www.mvtime.cn/2017/06/13/Django-RestFramework添加自定义字段/","excerpt":"","text":"添加自定义字段有两种方式 Model# Create your models here. class Category(models.Model): type = models.CharField(max_length=50, primary_key=True, unique=True) name = models.CharField(max_length=50) class Meta: verbose_name = \"分类\" verbose_name_plural = verbose_name @property def get_movie_image(self): return Movie.objects.filter(type=self.type)[0].image def __str__(self): return self.name Serializersclass CategoryListSerializer(serializers.ModelSerializer): image = serializers.ReadOnlyField(source='get_movie_image') image2 = serializers.SerializerMethodField() class Meta: model = Category fields = ('type', 'name', 'image', 'image2') def get_image2(self, obj): return Movie.objects.filter(type=obj.type)[0].image 两种方式添加分别是image和image2","categories":[],"tags":[{"name":"Django","slug":"Django","permalink":"http://www.mvtime.cn/tags/Django/"},{"name":"技术","slug":"技术","permalink":"http://www.mvtime.cn/tags/技术/"}]},{"title":"我在桥上看风景","slug":"我在桥上看风景","date":"2017-06-12T07:49:08.000Z","updated":"2017-06-12T07:55:44.000Z","comments":true,"path":"2017/06/12/我在桥上看风景/","link":"","permalink":"http://www.mvtime.cn/2017/06/12/我在桥上看风景/","excerpt":"","text":"空气中还有一些尘埃 伴随风一起刮起来 在霓虹灯亮起的夜晚里寻找臭味相投的气息 有谁带着仅存的牵挂有谁下了班还不想回家 如果这世界是一幅画我们陷在里面不能自拔 我在桥上看风景 却看见拥抱后的孤寂 口中说着继续 转身就已放弃 我在桥上看风景 看穿了相聚和悲喜 看穿了这是一场戏 想散场又不知到哪里去 人来人往的人群相拥 看不出有谁哪里不同 心里做着一个梦 所以 脚步才沉重 有谁受了伤却要内疚 有谁放了手还不肯走 如果我眼里容不下他 会不会就看出他的变化 我在桥上看风景 却看见拥抱后的孤寂 口中说着继续 转身就已放弃 我在桥上看风景 看穿了相聚和悲喜 看穿了这是一场戏 想散场又不知到哪里去 我在桥上看风景 却看见拥抱后的孤寂 口中说着继续 转身就已放弃 我在桥上看风景 看穿了相聚和悲喜 看穿了这是一场戏 想散场又不知到哪里去","categories":[],"tags":[{"name":"音乐","slug":"音乐","permalink":"http://www.mvtime.cn/tags/音乐/"},{"name":"生活","slug":"生活","permalink":"http://www.mvtime.cn/tags/生活/"}]},{"title":"客从何处来","slug":"客从何处来","date":"2017-06-07T02:13:20.000Z","updated":"2017-06-07T02:16:28.000Z","comments":true,"path":"2017/06/07/客从何处来/","link":"","permalink":"http://www.mvtime.cn/2017/06/07/客从何处来/","excerpt":"","text":"作曲 : 燕池 作词 : 蘅初 编曲:李大亮、燕池 风里藏着秘密,遇山散了 花里喃着耳语,迎雾没了 怀里拥着温度,沾露凉了 手里攥着船票,入水化了 散落的繁星,谱给无眠旅人的安眠曲 松软的土地,呼吸青草芳香悦然前行 故乡的过客,远方的归人 无意间我说起的故事,你又是否相信 远方,让我大闹一场悄然离去的故乡 故乡,让我收藏荣耀戏谑人生的远方 不辞而别的朋友,一见如故的路人 轻声问起,客往何处去 风里藏着秘密,遇山散了 花里喃着耳语,迎雾没了 怀里拥着温度,沾露凉了 手里攥着船票,入水化了 我若问自己,客从何处来 被湮灭的理想,被阐释的爱情 我若问自己,客从何处来 被所相信的相信,被所否定的否定 月应有圆缺,日不惧升落 被所崇拜的崇拜,被所怀疑的怀疑 客从何处来,客往何处去 转身即是远方,前路亦是故乡 故乡 远方,让我大闹一场悄然离去的故乡 故乡,让我收藏荣耀戏谑人生的远方 不辞而别的朋友,一见如故的路人 轻声问起,客往何处去 远方,让我大闹一场悄然离去的故乡 故乡,让我收藏荣耀戏谑人生的远方 不辞而别的朋友,一见如故的路人 轻声问起,客往何处去 轻声问起,客往何处去","categories":[],"tags":[{"name":"音乐","slug":"音乐","permalink":"http://www.mvtime.cn/tags/音乐/"},{"name":"生活","slug":"生活","permalink":"http://www.mvtime.cn/tags/生活/"}]},{"title":"Mac 上利用 launchctl 开启Scrapy定时任务","slug":"Mac-上利用-launchctl-开启Scrapy定时任务","date":"2017-05-29T16:45:41.000Z","updated":"2017-05-29T16:57:15.000Z","comments":true,"path":"2017/05/30/Mac-上利用-launchctl-开启Scrapy定时任务/","link":"","permalink":"http://www.mvtime.cn/2017/05/30/Mac-上利用-launchctl-开启Scrapy定时任务/","excerpt":"","text":"前面的话Linux 和 Mac 通用的 crontab 定时任务: crontab 定时任务这个方法是通用的,Mac提供了更加方便和易读的launchctl方法,launchctl通过 plist 属性列表(Property List)配置。 Linux 和 Mac 通用的 crontab 时间间隔可以精确到分钟,而 launchctl 时间间隔可以精确到 每秒 。 创建定时运行的任务脚本run.sh #!/bin/bash # 记录一下开始时间 echo `date` >> $HOME/log && # 进入 虚拟机 目录 cd /Users/MisterDeng/Documents/Python/.virtualenvs/education && # 激活 python 虚拟环境 virtualenv source bin/activate && # 进入爬虫目录 cd /Users/MisterDeng/Documents/Python/movies/movie_scrapy/movie_scrapy && # 运行爬虫脚本 scrapy crawl newmovie -o data.json && # 运行完成 echo 'finish' >> $HOME/log && 连接两条命令,当前一条执行完才会继续下一条命令。 将文件拖入控制台测试shell脚本,发现Permission denied,就是没有权限。解决办法:修改该文件xxx.sh 的权限 :使用命令:chmod 777 xxx.sh 。 创建定时任务文件task.plsit <?xml version=\"1.0\" encoding=\"UTF-8\"?> <!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\"> <plist version=\"1.0\"> <dict> <key>Label</key> <string>xyz.hanks.spider</string> <!-- 要运行的程序, 如果省略这个选项,会把ProgramArguments的第一个 元素作为要运行的程序 --> <key>Program</key> <string>/Users/MisterDeng/Documents/Python/movies/movie_scrapy/run.sh</string> <!-- 每天18:30 --> <key>StartCalendarInterval</key> <dict> <key>Minute</key> <integer>43</integer> <key>Hour</key> <integer>0</integer> </dict> <!-- 运行间隔,用于循环执行,与StartCalenderInterval使用其一,单位为>秒 --> <!-- <key>StartInterval</key> <integer>60</integer> --> <!-- 标准输出文件 --> <key>StandardOutPath</key> <string>/Users/MisterDeng/Documents/Python/movies/movie_scrapy/run-out.log</string> <!-- 标准错误输出文件,错误日志 --> <key>StandardErrorPath</key> <string>/Users/MisterDeng/Documents/Python/movies/movie_scrapy/run-err.log</string> </dict> </plist> 将定时任务加入系统# 加载 task.plist $ launchctl load task.plist 查看是否添加成功 $ launchctl list | grep hanks - 0 xyz.hanks.spider 如果像移除的话 # 移除 xyz.hanks.spider $ launchctl remove xyz.hanks.spider 其他完整的 plist 文件格式参数。 <?xml version=\"1.0\" encoding=\"UTF-8\"?> <!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\"> <plist version=\"1.0\"> <dict> <key>Label</key> <!-- 名称,要全局唯一 --> <string>xyz.hanks.spider</string> <!-- 要运行的程序, 如果省略这个选项,会把ProgramArguments的第一个 元素作为要运行的程序 --> <key>Program</key> <string>/Users/hanks/run.sh</string> <!-- 命令, 第一个为命令,其它为参数--> <key>ProgramArguments</key> <array> <string>/Users/hanks/run.sh</string> </array> <!-- 运行时间 --> <key>StartCalendarInterval</key> <dict> <key>Minute</key> <integer>30</integer> <key>Hour</key> <integer>9</integer> <key>Day</key> <integer>1</integer> <key>Month</key> <integer>5</integer> <!-- 0和7都指星期天 --> <key>Weekday</key> <integer>0</integer> </dict> <!-- 运行间隔,与StartCalenderInterval使用其一,单位为秒 --> <key>StartInterval</key> <integer>30</integer> <!-- 标准输入文件 --> <key>StandardInPath</key> <string>/Users/hanks/run-in.log</string> <!-- 标准输出文件 --> <key>StandardOutPath</key> <string>/Users/hanks/run-out.log</string> <!-- 标准错误输出文件 --> <key>StandardErrorPath</key> <string>/Users/hanks/run-err.log</string> </dict> </plist>","categories":[],"tags":[{"name":"技术","slug":"技术","permalink":"http://www.mvtime.cn/tags/技术/"},{"name":"Scrapy","slug":"Scrapy","permalink":"http://www.mvtime.cn/tags/Scrapy/"}]},{"title":"Django rest_framework使用总结","slug":"Django-rest-framework使用总结","date":"2017-05-25T00:48:01.000Z","updated":"2017-05-25T02:20:29.000Z","comments":true,"path":"2017/05/25/Django-rest-framework使用总结/","link":"","permalink":"http://www.mvtime.cn/2017/05/25/Django-rest-framework使用总结/","excerpt":"","text":"Models数据库表的字段 # 课程Model class Course(models.Model): course_org = models.ForeignKey(CourseOrg, verbose_name='课程机构', null=True, blank=True) name = models.CharField(max_length=52, verbose_name='课程名字') desc = models.CharField(max_length=300, verbose_name='课程描述') teacher = models.ForeignKey(Teacher, verbose_name='讲师', null=True, blank=True) detail = models.TextField(verbose_name='课程详情') degree = models.CharField(choices=(('cj', '初级'), ('zj', '中级'), ('gj', '高级')), max_length=2, verbose_name='难度') learn_times = models.IntegerField(default=0, verbose_name='学习时长(分钟数)') students = models.IntegerField(default=0, verbose_name='学习人数') fav_nums = models.IntegerField(default=0, verbose_name='收藏人数') image = models.ImageField(upload_to='courses/%Y/%m', verbose_name='封面图', max_length=100) click_nums = models.IntegerField(default=0, verbose_name='点击数') is_banner = models.BooleanField(default=False, verbose_name=u'是否是轮播图') category = models.CharField(default='后端', max_length=20, verbose_name='课程类别') tag = models.CharField(default='', verbose_name='课程标签', max_length=10) youneed_konw = models.CharField(default='', max_length=300, verbose_name='课前须知') teacher_tell = models.CharField(default='', max_length=300, verbose_name='老师告诉你能学什么') add_time = models.DateTimeField(default=datetime.now, verbose_name='添加时间') class Meta: verbose_name = '课程' verbose_name_plural = verbose_name # 章节Model class Lesson(models.Model): course = models.ForeignKey(Course, verbose_name='课程', related_name='lessons') name = models.CharField(max_length=100, verbose_name='章节名') add_time = models.DateTimeField(default=datetime.now, verbose_name='添加时间') class Meta: verbose_name = '章节' verbose_name_plural = verbose_name Serializers数据库字段的解析 from .models import Course, Lesson from rest_framework import serializers class LessonSerializers(serializers.ModelSerializer): class Meta: model = Lesson fields = ('id', 'name', 'add_time') class CourseSerializers(serializers.ModelSerializer): lessons = LessonSerializers(many=True, read_only=True) class Meta: model = Course fields = ('id', 'name', 'desc', 'lessons') ViewsApi界面的展示 逻辑的处理 class CourseListApiView(generics.ListCreateAPIView): queryset = Course.objects.all() serializer_class = CourseSerializers class CourseDetailApiView(generics.RetrieveUpdateDestroyAPIView): queryset = Course.objects.all() serializer_class = CourseSerializers 总结嵌套查询Lesson的外键必须设置related_name='lessons'用来标识 CourseSerializers设置lessons = LessonSerializers(many=True, read_only=True) 结果http://localhost:8000/course/list/ [ { \"id\": 1, \"name\": \"Python\", \"desc\": \"算法\", \"lessons\": [ { \"id\": 1, \"name\": \"1\", \"add_time\": \"2017-05-22T13:40:00\" }, { \"id\": 2, \"name\": \"2\", \"add_time\": \"2017-05-22T13:40:00\" }, { \"id\": 3, \"name\": \"3\", \"add_time\": \"2017-05-22T13:40:00\" } ] } ] 深度查询class LessonSerializers(serializers.ModelSerializer): class Meta: model = Lesson depth = 2 fields = ('id', 'name', 'add_time', 'course') 结果http://localhost:8000/course/lesson/list/ [ { \"id\": 1, \"name\": \"1\", \"add_time\": \"2017-05-22T13:40:00\", \"course\": { \"id\": 1, \"name\": \"Python\", \"desc\": \"算法\", \"detail\": \"发多少飞\", \"degree\": \"cj\", \"learn_times\": 0, \"students\": 2, \"fav_nums\": 0, \"image\": \"http://localhost:8000/media/courses/2017/05/%E6%88%91%E7%9A%84%E6%8E%A8%E5%B9%BF%E5%BC%95%E5%AF%BC_rtZYmxW.jpg\", \"click_nums\": 4, \"is_banner\": true, \"category\": \"后端\", \"tag\": \"发多少\", \"youneed_konw\": \"发送\", \"teacher_tell\": \"发送\", \"add_time\": \"2017-05-22T13:40:00\", \"course_org\": { \"id\": 1, \"name\": \"慕课\", \"category\": \"pxjg\", \"desc\": \"发斯蒂芬\", \"tag\": \"全国知名\", \"click_nums\": 12, \"fav_nums\": 12, \"image\": \"http://localhost:8000/media/org/2017/05/%E6%88%91%E7%9A%84%E6%8E%A8%E5%B9%BF%E5%BC%95%E5%AF%BC.jpg\", \"address\": \"范德萨\", \"add_time\": \"2017-05-22T13:39:00\", \"students\": 342, \"city\": 1 }, \"teacher\": { \"id\": 1, \"name\": \"发疯\", \"work_years\": 3, \"work_company\": \"发多少\", \"work_position\": \"发送\", \"points\": \"大师傅\", \"click_nums\": 3, \"fav_nums\": 0, \"age\": 30, \"image\": \"http://localhost:8000/media/teacher/2017/05/%E5%9B%BE%E5%B1%82-113x.png\", \"add_time\": \"2017-05-22T13:39:00\", \"org\": 1 } } } ] 也可以在LessonSerializers中name = serializers.ReadOnlyField(source='course.name') 增加外健的字段 视图重写ListCreateAPIView封装了get的方法可以选择重写 class CourseListApiView(generics.ListCreateAPIView): queryset = Course.objects.all() serializer_class = CourseSerializers def list(self, request, *args, **kwargs): queryset = self.filter_queryset(self.get_queryset()) page = self.paginate_queryset(queryset) if page is not None: serializer = self.get_serializer(page, many=True) return self.get_paginated_response(serializer.data) serializer = self.get_serializer(queryset, many=True) return Response({'data': serializer.data, 'message': '获取成功'}) 分页导入from rest_framework.pagination import PageNumberPagination class LargeResultsSetPagination(PageNumberPagination): page_size = 1 page_size_query_param = 'page_size' max_page_size = 2 CourseListApiView设置pagination_class = LargeResultsSetPagination","categories":[],"tags":[{"name":"Django","slug":"Django","permalink":"http://www.mvtime.cn/tags/Django/"},{"name":"技术","slug":"技术","permalink":"http://www.mvtime.cn/tags/技术/"}]},{"title":"如何优化 Django REST Framework 的性能?","slug":"如何优化-Django-REST-Framework-的性能?","date":"2017-05-24T01:09:32.000Z","updated":"2017-05-24T01:10:19.000Z","comments":true,"path":"2017/05/24/如何优化-Django-REST-Framework-的性能?/","link":"","permalink":"http://www.mvtime.cn/2017/05/24/如何优化-Django-REST-Framework-的性能?/","excerpt":"","text":"现在,Django REST Framework 帮助 Django 开发者给自己的应用开发出简单而强大的标准 REST APIs,而且现在我们已经成功的应用在了一些 Django Web 项目上。 然而,看似简单直观的 Django REST Framework 及其嵌套序列化可能会大大降低你的 API 端的性能。你的服务器的其他部分的响应能力也会被某一个低效的 REST API 拖后腿。 问题的根源就是 「N+1 selects problem」;首先查询数据库一次得到表中的数据(例如,Customers),然后每个用户的其他字段又需要循环不止一次地查询数据库(例如 customer.country.Name)。使用 Django 的 ORM,很容易造成这个问题,而使用 DRF,同样会造成这个问题。 幸运的是,目前有修复 Django REST Framework 性能问题的解决方法,而且不需要对代码进行重大重组。它只是需要使用未充分利用的 select_related 和 prefetch_related 方法来执行所谓的「预加载」。 这种方法产生了很好的效果,在我们最近的项目中,有个重要的 API 调用花费了5-10秒钟才返回结果。运用合适的预加载后,同样的调用时间花费都变的低于1秒,典型的加速20倍以上。 为什么 Django REST Framework 那么容易造成这个问题当你建立一个 DRF 视图时,你经常需要从多个相关表中返回相应的数据。写这样的功能是很简单的,DRF文档中有详细的介绍。不过不幸的是,只要你在序列化中使用嵌套关系,你就在拿你的性能开玩笑,像很多的性能问题一样,它往往只出现有大型数据集的真实生产环境中。 这种情况发生就是因为 Django 的 ORM 是惰性的,它只取出当前查询所需响应最小的数据。它不知道你是否有成百上千的相同或相似的数据也需要取出来。 况且如今,当我们谈到数据库型网站时,一般情况下,最重要的响应指标就是数据库的访问次数。 在 DRF 视图中,我们每次序列化有嵌套关系的数据时都会出现问题,如下面的例子: class CustomerSerializer(serializers.ModelSerializer): # This can kill performance! order_descriptions = serializers.StringRelatedField(many=True) # So can this, same exact problem... orders = OrderSerializer(many=True, read_only=True) # This can kill performance! CustomerSerializer 函数里面是这么运行的: 获取所有的 customers (需要往返到数据库) 对于第一个返回的客户,获取他们的 orders (又需要去往返一趟数据库) 对于第二个返回的客户,获取他们的 orders (又需要去往返一趟数据库) 对于第三个返回的客户,获取他们的 orders (又需要去往返一趟数据库) 对于第四个返回的客户,获取他们的 orders (又需要去往返一趟数据库) 对于第五个返回的客户,获取他们的 orders (又需要去往返一趟数据库) 对于第六个返回的客户,获取他们的 orders (又需要去往返一趟数据库) 。。。。终于意识到,千万不要有更多的用户 而且这个很快就变的更糟,如果你 OrderSerializer 本身又有一个嵌套关系,那你就有了嵌套循环,那么即使数据量很小,你也很快就陷入麻烦之中。这有一个经验,一个拥有适度流量的网站,在进入真正的麻烦前也就可以负担起50次到数据库的访问。 解决 Django 「懒惰」的基本方法现在我们解决这个问题的方法就是「预加载」。从本质上讲,就是你提前警告 Django ORM 你要一遍又一遍的告诉它同样无聊的指令。在上面的例子中,在 DRF 开始获取前很简单地加上这句话就搞定了: queryset = queryset.prefetch_related('orders') 当 DRF 调用上述相同序列化 customers 时,出现的是这种情况: 获取所有 customers(执行两个往返数据库操作,第一个是获取 customers,第二个获取相关 customers 的所有相关的 orders。) 对于第一个返回的 customers,获取其 order(不需要访问数据库,我们已经在上一步中获取了所需要的数据) 对于第二个返回的 customers,获取其 order (不需要访问数据库) 对于第三个返回的 customers,获取其 order (不需要访问数据库) 对于第四个返回的 customers,获取其 order (不需要访问数据库) 对于第五个返回的 customers,获取其 order (不需要访问数据库) 对于第六个返回的 customers,获取其 order (不需要访问数据库) 你又意识到,你可以有了很多 customers,已经不需要再继续等待去数据库。 其实 Django ORM 的「预备」是在第1步进行请求,它在本地高速缓存的数据能够提供步骤2+所要求的数据。与之前往返数据库相比从本地缓存数据中读取数据基本上是瞬时的,所以我们在有很多 customers 时就获得了巨大的性能加速。 解决 Django REST Framework 性能问题的标准化模式我们已经确定了一个优化 Django REST Framework 性能问题的通用模式,那就是每当序列化查询嵌套字段时,我们就添加一个新的 @staticmethod 名叫 setup_eager_loading,像这样: class CustomerSerializer(serializers.ModelSerializer): orders = OrderSerializer(many=True, read_only=True) def setup_eager_loading(cls, queryset): """ Perform necessary eager loading of data. """ queryset = queryset.prefetch_related('orders') return queryset 这样,不管哪里要用到这个序列化,都只需在调用序列化前简单调用 setup_eager_loading ,就像这样: customer_qs = Customers.objects.all() customer_qs = CustomerSerializer.setup_eager_loading(customer_qs) # Set up eager loading to avoid N+1 selects post_data = CustomerSerializer(customer_qs, many=True).data 或者,如果你有一个 APIView 或 ViewSet,你可以在 get_queryset 方法里调用 setup_eager_loading: def get_queryset(self): queryset = Customers.objects.all() # Set up eager loading to avoid N+1 selects queryset = self.get_serializer_class().setup_eager_loading(queryset) return queryset 那么怎样编写 setup_eager_loading想要解决 Django 的性能问题,最困难的部分就是要熟悉 select_related 和它的小伙伴儿们是如何工作的。在这儿,我们将详细介绍它们在 Django ORM 和 Django REST Framework 中怎样使用。 select_related:Django ORM 最简单的预加载工具,对于所有一对一或多对一的数据关系,你都需要从同一个父对象获取数据,如客户的公司名称。这个会被翻译成 SQL 的 join 操作,这样父对象的数据就和子对象的数据一起取回来了。(参见官方文档) prefetch_related:对于更复杂的关系,即每个结果有多行(例如 many=True ),像多对一或多对多的数据关系,比如上述客户的订单,这转化一个二级 SQL 查询,通常有很长的 WHERE ... IN ,从中只选择相关的行。(参见官方文档) Prefetch:用于复杂 prefetch_related 查询,例如过滤子集。它也可以嵌套setup_eager_loading 进行调用。 (参见官方文档) 一个合适的预加载模型在本例中,我们优化一个假想的活动规划网站(巧了,我们正在进行的项目 getfetcher.com 就是这样)的 Django REST Framework 相关的性能问题,我们有一个简单的数据库结构: from django.contrib.auth.models import User class Event: """ A single occasion that has many `attendees` from a number of organizations.""" creator = models.ForeignKey(User) name = models.TextField() event_date = models.DateTimeField() class Attendee: """ A party-goer who (usually) represents an `organization`, who may attend many `events`.""" events = models.ManyToManyField(Event, related_name='attendees') organization = models.ForeignKey(Organization, null=True) class Organization: name = models.TextField() 在这个例子中,为了获取所有的事件,我们的预加载代码长这样: class EventSerializer(serializers.ModelSerializer): creator = serializers.StringRelatedField() attendees = AttendeeSerializer(many=True) unaffiliated_attendees = AttendeeSerializer(many=True) @staticmethod def setup_eager_loading(queryset): """ Perform necessary eager loading of data. """ # select_related for "to-one" relationships queryset = queryset.select_related('creator') # prefetch_related for "to-many" relationships queryset = queryset.prefetch_related( 'attendees', 'attendees__organization') # Prefetch for subsets of relationships queryset = queryset.prefetch_related( Prefetch('unaffiliated_attendees', queryset=Attendee.objects.filter(organization__isnull=True)) ) return queryset 当我们使用 EventSerializer 之前确定调用 setup_eager_loading,这样我们只会有两个大的查询,而不是 N+1 个小的查询,而且我们的表现通常会好很多! 结论预加载是 Django REST Framework 通用的性能优化方式,OneAPM Python Agent 也是国内通用的性能监控工具,而且支持 Django REST Framework ,下面是一个简单 demo 的截图。 任何时候你通过 ORM 查询嵌套关系时,你都应该考虑建立适合的预加载。根据现有经验,这是现在小型和中型 Web 开发中最常见的性能相关问题。 参考 Django REST Framework 文档 Github上的问题来自动执行 prefetch_related:Automatically determine select_related and prefetch_related on ModelSerializer。 DRF 的作者 Tom Christie,在 DRF 性能这篇博客文章中谈到了我们上面处理的问题,Get your ORM lookups right 原文链接:Optimizing slow Django REST Framework performance","categories":[],"tags":[{"name":"Django","slug":"Django","permalink":"http://www.mvtime.cn/tags/Django/"},{"name":"技术","slug":"技术","permalink":"http://www.mvtime.cn/tags/技术/"}]},{"title":"总有你要的编程书单(GitHub)","slug":"总有你要的编程书单(GitHub)","date":"2017-05-22T00:39:33.000Z","updated":"2017-05-22T00:41:45.000Z","comments":true,"path":"2017/05/22/总有你要的编程书单(GitHub)/","link":"","permalink":"http://www.mvtime.cn/2017/05/22/总有你要的编程书单(GitHub)/","excerpt":"","text":"目录IDE IntelliJ IDEA 简体中文专题教程 MySQL 21分钟MySQL入门教程 MySQL索引背后的数据结构及算法原理 NoSQL Disque 使用教程 Neo4j .rb 中文資源 Redis 命令参考 Redis 设计与实现 The Little MongoDB Book The Little Redis Book 带有详细注释的 Redis 2.6 代码 带有详细注释的 Redis 3.0 代码 PostgreSQL PostgreSQL 8.2.3 中文文档 PostgreSQL 9.3.1 中文文档 Web 3 Web Designs in 3 Weeks Chrome 开发者工具中文手册 Chrome扩展开发文档 Growth: 全栈增长工程师指南 Grunt中文文档 Gulp 入门指南 gulp中文文档 HTTP 接口设计指北 HTTP/2.0 中文翻译 http2讲解 JSON风格指南 Wireshark用户手册 一站式学习Wireshark 关于浏览器和网络的 20 项须知 前端代码规范 及 最佳实践 前端开发体系建设日记 前端资源分享(一) 前端资源分享(二) 正则表达式30分钟入门教程 浏览器开发工具的秘密 移动Web前端知识库 移动前端开发收藏夹 WEB服务器 Apache 中文手册 Nginx开发从入门到精通 (淘宝团队出品) Nginx教程从入门到精通 (PDF版本,运维生存时间出品) 其它 OpenWrt智能、自动、透明翻墙路由器教程 SAN 管理入门系列 Sketch 中文手册 深入理解并行编程 函数式概念 傻瓜函数编程 分布式系统 走向分布式 (PDF) 在线教育 51CTO学院 Codecademy CodeSchool Coursera Learn X in Y minutes (数十种语言快速入门教程) shiyanlou TeamTreeHouse Udacity xuetangX 慕课网 (丰富的移动端开发、php开发、web前端、html5教程以及css3视频教程等课程资源) 极客学院 汇智网 计蒜客 大数据 Spark 编程指南简体中文版 大型集群上的快速和通用数据处理架构 大数据/数据挖掘/推荐系统/机器学习相关资源 数据挖掘中经典的算法实现和详细的注释 面向程序员的数据挖掘指南 操作系统 Debian 参考手册 Docker —— 从入门到实践 Docker中文指南 Docker入门实战 FreeBSD 使用手册 FreeRADIUS新手入门 Linux Documentation (中文版) Linux Guide for Complete Beginners Linux 构建指南 Linux 系统高级编程 Linux工具快速教程 Mac 开发配置手册 Operating Systems: Three Easy Pieces The Linux Command Line (中英文版) Ubuntu 参考手册 uCore Lab: Operating System Course in Tsinghua University UNIX TOOLBOX 命令行的艺术 嵌入式 Linux 知识库 (eLinux.org 中文版) 开源世界旅行手册 理解Linux进程 鸟哥的 Linux 私房菜 基础学习篇 鸟哥的 Linux 私房菜 服务器架设篇 数据库 Redis 设计与实现 The Little MongoDB Book 中文版 智能系统 一步步搭建物联网系统 正则表达式 正则表达式30分钟入门教程 版本控制 Git - 简易指南 Git-Cheat-Sheet (感谢 @flyhigher139 翻译了中文版) Git Community Book 中文版 git-flow 备忘清单 Git magic Git Magic Git 参考手册 Github帮助文档 GitHub秘籍 Git教程 (本文由 @廖雪峰 创作,如果觉得本教程对您有帮助,可以去 iTunes 购买) Got GitHub GotGitHub HgInit (中文版) Mercurial 使用教程 Pro Git Pro Git 中文版 (整理在gitbook上) svn 手册 学习 Git 分支 (点击右下角按钮可切换至简体及正体中文) 沉浸式学 Git 猴子都能懂的GIT入门 程序员杂谈 程序员的自我修养 管理和监控 ElasticSearch 权威指南 Elasticsearch 权威指南(中文版) ELKstack 中文指南 Logstash 最佳实践 Mastering Elasticsearch(中文版) Puppet 2.7 Cookbook 中文版 编程艺术 取悦的工序:如何理解游戏 (豆瓣阅读,免费书籍) 每个程序员都应该了解的内存知识(译)【第一部分】 程序员编程艺术 编程入门指南 编译原理 《计算机程序的结构和解释》公开课 翻译项目 编辑器 exvim–vim 改良成IDE项目 Vim中文文档 所需即所获:像 IDE 一样使用 vim 笨方法学Vimscript 中译本 计算机图形学 OpenGL 教程 设计模式 史上最全设计模式导学目录 图说设计模式 软件开发方法 傻瓜函数编程 (《Functional Programming For The Rest of Us》中文版) 硝烟中的 Scrum 和 XP 项目相关 GNU make 指南 Gradle 2 用户指南 Gradle 中文使用文档 Joel谈软件) selenium 中文文档 开源软件架构 持续集成(第二版) (译言网) 約耳談軟體(Joel on Software) 编码规范 让开发自动化系列专栏 追求代码质量 语言相关Android Android Design(中文版) Android Note(开发过程中积累的知识点) Android6.0新特性详解 Android学习之路 Android开发技术前线(android-tech-frontier) Google Android官方培训课程中文版 Google Material Design 正體中文版 (译本一 译本二) Material Design 中文版 Point-of-Android Android 一些重要知识点解析整理 AWK awk中文指南 awk程序设计语言 C C 语言常见问题集 C/C++ 学习教程 Linux C 编程一站式学习 新概念 C 语言教程 C Sharp 精通C#(第6版) C++ 100个gcc小技巧 100个gdb小技巧 C 语言编程透视 C/C++ Primer - @andycai C++ FAQ LITE(中文版) C++ Primer 5th Answers C++ Template 进阶指南 C++ 基础教程 C++ 并发编程(基于C++11) C++ 并发编程指南 CGDB中文手册 Cmake 实践 (PDF) GNU make 指南 Google C++ 风格指南 QT 教程 ZMQ 指南 像计算机科学家一样思考(C++版) (《How To Think Like a Computer Scientist: C++ Version》中文版) 简单易懂的C魔法 跟我一起写Makefile(PDF) (PDF) CoffeeScript CoffeeScript 中文 CoffeeScript 编程风格指南 Dart Dart 语言导览 Elasticsearch Elasticsearch 权威指南 (《Elasticsearch the definitive guide》中文版) ELKstack 中文指南 Mastering Elasticsearch(中文版) Elixir Elixir Getting Started 中文翻译 Elixir 编程语言教程 (Elixir School) Elixir元编程与DSL 中文翻译 Phoenix 框架中文文档 Erlang Erlang 并发编程 (《Concurrent Programming in Erlang (Part I)》中文版) Fortran Fortran77和90/95编程入门 Golang Go Web 编程 Go 入门指南 (《The Way to Go》中文版) Go 官方文档翻译 Go 指南 (《A Tour of Go》中文版) Go 简易教程 (《 The Little Go Book 》中文版) Go 编程基础 Go 语言标准库 Go命令教程 Go实战开发 Go语言博客实践 Java程序员的Golang入门指南 Network programming with Go 中文翻译版本 Revel 框架手册 学习Go语言 神奇的 Go 语言 Groovy 实战 Groovy 系列 Haskell Haskell 趣学指南 Real World Haskell 中文版 HTML / CSS CSS3 Tutorial 《CSS3 教程》 CSS参考手册 Emmet 文档 HTML5 教程 HTML和CSS编码规范 Sass Guidelines 中文 前端代码规范 (腾讯 AlloyTeam 团队) 学习CSS布局 通用 CSS 笔记、建议与指导 iOS Apple Watch开发初探 Google Objective-C Style Guide 中文版 iOS7人机界面指南 iOS开发60分钟入门 iPhone 6 屏幕揭秘 网易斯坦福大学公开课:iOS 7应用开发字幕文件 Java Activiti 5.x 用户指南 Apache MINA 2 用户指南 Apache Shiro 用户指南 Google Java编程风格指南 H2 Database 教程 Java Servlet 3.1 规范 Java 编码规范 Jersey 2.x 用户指南 JSSE 参考指南 MyBatis中文文档 Netty 4.x 用户指南 Netty 实战(精髓) REST 实战 Spring Boot参考指南 (翻译中) Spring Framework 4.x参考文档 用jersey构建REST服务 Javascript Airbnb JavaScript 规范 AngularJS AngularJS中译本 AngularJS入门教程 AngularJS最佳实践和风格指南 在Windows环境下用Yeoman构建AngularJS项目 构建自己的AngularJS backbone.js backbone.js中文文档 backbone.js入门教程 (PDF) Backbone.js入门教程第二版 Developing Backbone.js Applications(中文版) Chrome扩展及应用开发 CoffeeScript CoffeeScript 编码风格指南 D3.js D3.js 入门系列 (还有进阶、高级等系列) 官方API文档 张天旭的D3教程 楚狂人的D3教程 ECMAScript 6 入门 (作者:阮一峰) ExtJS Ext4.1.0 中文文档 Google JavaScript 代码风格指南 Google JSON 风格指南 impress.js impress.js的中文教程 JavaScript Promise迷你书 Javascript 原理 JavaScript 标准参考教程(alpha) 《JavaScript 模式》 “JavaScript patterns”中译本 javascript 的 12 个怪癖 JavaScript 秘密花园 JavaScript核心概念及实践 (PDF) (此书已由人民邮电出版社出版发行,但作者依然免费提供PDF版本,希望开发者们去购买,支持作者) Javascript编程指南 (源码) jQuery How to write jQuery plugin 简单易懂的JQuery魔法 Meteor Discover Meteor Node.js express.js 中文文档 Express框架 koa 中文文档 Learn You The Node.js For Much Win! (中文版) Node debug 三法三例 Node.js Fullstack《從零到一的進撃》 Node.js 包教不包会 Nodejs Wiki Book (繁体中文) nodejs中文文档 Node入门 七天学会NodeJS 使用 Express + MongoDB 搭建多人博客 JavaScript全栈工程师培训材料 React.js Learn React & Webpack by building the Hacker News front page React Native 中文文档(含最新Android内容) React webpack-cookbook React 入门教程 React.js 中文文档 underscore.js Underscore.js中文文档 You-Dont-Know-JS (深入JavaScript语言核心机制的系列图书) Zepto.js Zepto.js 中文文档 命名函数表达式探秘 (注:原文由为之漫笔 翻译,原始地址无法打开,所以此处地址为我博客上的备份) 学用 JavaScript 设计模式 (开源中国) 深入理解JavaScript系列 LaTeX LaTeX 笔记 一份不太简短的 LaTeX2ε 介绍 大家來學 LaTeX (PDF) LISP ANSI Common Lisp 中文翻译版 Common Lisp 高级编程技术 (《On Lisp》中文版) Lua Lua 5.3 参考手册 Markdown Markdown 快速入门 Markdown 简明教程 Markdown 语法说明 献给写作者的 Markdown 新手指南 Node.js Node 入门 The NodeJS 中文文档 (社区翻译) 七天学会NodeJS 阿里出品,很好的入门资料 Perl Master Perl Today 《Modern Perl》中文版 Perl 5 教程 Perl 教程 PHP PHP 之道 PHP5中文手册 PHP扩展开发及内核应用 Symfony2 实例教程 深入理解 PHP 内核 Python Django book 2.0 Python 3 文档(简体中文) 3.2.2 documentation Python 中文学习大本营 深入 Python 3 笨办法学 Python R 153分钟学会 R (PDF) R 导论 (《An Introduction to R》中文版) (PDF) 用 R 构建 Shiny 应用程序 (《Building ‘Shiny’ Applications with R》中文版) 统计学与 R 读书笔记 (PDF) reStructuredText reStructuredText 入门 reStructuredText 简明教程 Ruby Rails 风格指南 Ruby on Rails Tutorial 原书第 2 版 Ruby on Rails 实战圣经 Ruby 风格指南 笨方法学 Ruby Rust Rust 官方教程 Rust 语言学习笔记 RustPrimer 通过例子学习 Rust Scala Effective Scala Scala 初学者指南 (The Neophyte’s Guide to Scala) Scala 课堂 (Twitter的Scala中文教程) Scheme Scheme 入门教程 (《Yet Another Scheme Tutorial》中文版) Shell Shell 编程基础 Shell 脚本编程30分钟入门 The Linux Command Line 中文版 Swift 《The Swift Programming Language》中文版 Vim Vim Manual(中文版) 大家來學 VIM Visual Prolog Visual Prolog 7初学指南 Visual Prolog 7边练边学","categories":[],"tags":[{"name":"技术","slug":"技术","permalink":"http://www.mvtime.cn/tags/技术/"},{"name":"书籍","slug":"书籍","permalink":"http://www.mvtime.cn/tags/书籍/"}]},{"title":"Landing Guy","slug":"Landing-Guy","date":"2017-03-03T01:50:12.000Z","updated":"2017-03-03T01:56:28.000Z","comments":true,"path":"2017/03/03/Landing-Guy/","link":"","permalink":"http://www.mvtime.cn/2017/03/03/Landing-Guy/","excerpt":"","text":"Right I die. My life before my eyes.生命于我眼前支离破碎As I was hang there,I see wonderland.尚存一息我眼见了仙境I don’t really see much of anything.那里几乎空无一物But I see you.That is crazy.却出现了你. 这疯狂至极 Your landing guy is home.你的旅人已经回家Oh,it’s magic.oh,that’s magic.这奇妙无比,光怪迷离 Your landing guy is home.你的旅人已经回家Oh,it’s magic.oh,that’s magic.这奇妙无比,光怪迷离 Night to day, and day to night.白昼更迭里All the sadness,you take my breath away.你悲戚了我的窒息So I cried .for all these time.所以我不曾停止哭泣Then I comes closes,you stare at me.你注目着我的临近 Are we falling in love?我们是否滋生了爱Oh,it’s magic.oh,that’ s magic.这奇妙无比,光怪迷离 Are we falling in love?我们是否滋生了爱Oh,it’s magic.oh,that’s magic.这奇妙无比,光怪迷离 Your landing guy is home.你的旅人已经回家Oh,it’s magic.oh,that’s magic.这奇妙无比,光怪迷离 Your landing guy is home.你的旅人已经回家Oh,it’s magic.oh,that’s magic.这奇妙无比,光怪迷离 Oh,it’s magic.oh,that’s magic.这奇妙无比,光怪迷离 Oh,it’s magic.oh,that’s magic.这奇妙无比,光怪迷离","categories":[],"tags":[{"name":"音乐","slug":"音乐","permalink":"http://www.mvtime.cn/tags/音乐/"},{"name":"生活","slug":"生活","permalink":"http://www.mvtime.cn/tags/生活/"}]},{"title":"前端资源汇总(转载)","slug":"前端资源汇总(转载)","date":"2017-02-22T08:43:21.000Z","updated":"2017-02-22T08:48:12.000Z","comments":true,"path":"2017/02/22/前端资源汇总(转载)/","link":"","permalink":"http://www.mvtime.cn/2017/02/22/前端资源汇总(转载)/","excerpt":"","text":"1.前言 转载自史上最全的前端资源大汇总,收藏之用 2. 目录 首先我们先来看一下文章的分区,以方便我们进行检索。 PS: 内容超长,请注意! 前言 目录 入门类 HTML 5 部分 CSS 3 部分 JQuery Angular JS React Vue Node JS JS Template 移动端 移动端 API 综合 API 其他 API 综合类 工具类 综合效果搜索平台 团队 BLOG 开发中心 ECMAScript D3 RequrieJS SeaJS Less & Sass Markdown 兼容性 UI相关 图表类 正则表达式 前端规范 PHP 各大公司开源项目 常用 算法 JSON Ext, EasyUI, J-UI 及其它各种UI方案 页面 社会化 分享功能 富文本编辑器 前端概述 Gulp Grunt Fis pc图轮 移动端图轮 文件上传 模拟select 取色插件 城市联动 剪贴板 简繁转换 表格 Grid 在线演示 常规优化 优化工具 在线工具 前端架构 推荐作品 简历模板 求职 面试题 iconfont Fiddler Chrome Firebug 移动,微信调试 iOS Simulator Image 浏览器同步 在线PPT制作 前端导航网站 常用CDN Git 各种日期日历 Date library 其它 效果类 弹出层 优秀JavaScript项目 其他一些推荐 3. 入门类 前端入门教程 瘳雪峰的Javascript教程 前端工程师必备的PS技能——切图篇 结合个人经历总结的前端入门方法 作者的简书地址 作者的CSDN地址 HTML 修真录——初识”异界” HTML 修真录——“深渊三君王” HTML 基础入门 HTML 基础提升 CSS 基础入门 CSS 盒模型 CSS 浮动 CSS 定位 4. HTML 5 部分 深入理解HTML5标签 如何写出高效率的HTML HTML meta标签总结与属性使用介绍 戏说HTML5 编写高质量的 HTML 代码 如何解决 img 标签上下出现的间隙 五分钟学会 Canvas 基础(一) 五分钟学会 Canvas 基础(二) 模仿 LED 灯的滚动文字效果 关于 Canvas 的兄弟 SVG 的基础教程 HTML 5 动态效果制作方法整理 Canvas 效果实例 细数前端中的一些黑科技 前端 Meta 用法大汇总 HTML5新特性 5. CSS 3 部分 CSS 语法参考 如何编写可维护的CSS CSS3动画手册 腾讯css3动画制作工具 志爷css小工具集合 css3 js 移动大杂烩 bouncejs 触摸库 animate.css 全局CSS的终结 browserhacks CSS3其他属性 弹性盒模型详解 CSS3动画 2D变形&3D变形 蒙版mask 6. JQuery YOU MIGHT NOT NEED JQUERY jQuery API 中文文档 hemin 在线版 css88 jq api css88 jqui api 学习jquery jquery 源码查找 Web前端资源汇总(jQuery,Js,Css3等) 7. Angular JS Angular.js 的一些学习资源 angularjs中文社区 Angularjs源码学习 Angularjs源码学习 angular对bootstrap的封装 angularjs + nodejs 吕大豹 Angularjs AngularJS 最佳实践 Angular的一些扩展指令 Angular数据绑定原理 一些扩展Angular UI组件 Ember和AngularJS的性能测试 带你走近AngularJS - 基本功能介绍 Angularjs开发指南 Angularjs学习 不要带着jQuery的思维去学习AngularJS angularjs 学习笔记 angularjs 开发指南 angularjs 英文资料 angular bootstrap angular jq mobile angular ui 整合jQuery Mobile+AngularJS经验谈 有jQuery背景,该如何用AngularJS编程思想 AngularJS在线教程 angular学习笔记 8. React react.js中文论坛 react.js官方网址 react.js官方文档 react.jsmaterialUI react.jsTouchstoneJSUI react.jsamazeuiUI React入门实例教程-阮一峰 ReactNative中文版 Webpack和React小书-前端乱炖 Webpack和React小书-gitbook webpack Webpack,101入门体验 webpack入门教程 基于webpack搭建前端工程解决方案探索 9. Vue Vue2.0 Vue Vue Router Vuex Vue-Cli Vue 论坛 Vue 聊天室 Vue 入门指南 Vue 的一些资源索引 awesome-vue vue-syntax-highlight 10. Node JS Node.js 包教不包会 一个nodejs博客 【NodeJS 学习笔记04】新闻发布系统 过年7天乐,学nodejs 也快乐 七天学会NodeJS Nodejs学习笔记(二)— 事件模块 nodejs入门 angularjs nodejs 从零开始nodejs系列文章 理解nodejs nodejs事件轮询 node入门 nodejs cms Node初学者入门,一本全面的NodeJS教程 NodeJS的代码调试和性能调优 11. JS Template template-chooser artTemplate tomdjs 淘宝模板juicer模板 Fxtpl v1.0 繁星前端模板引擎 laytpl mozilla - nunjucks Juicer dustjs etpl 12. 移动端 fastclick no-click-delay 【敏捷开发】Android团队开发规范 Android 开发规范与应用 ionic 13. 移动端 API 99移动端知识集合 移动端前端开发知识库 移动前端的一些坑和解决方法(外观表现) 【原】移动web资源整理 zepto 1.0 中文手册 zepto 1.0 中文手册 zepto 1.1.2 zepto 中文注释 jqmobile 手册 移动浏览器开发集合 移动开发大杂烩 14. 综合 API javascripting 各种流行库搜索 runoob.com-包含各种API集合 开源中国在线API文档合集 devdocs(英文综合API网站) 15. 其他 API HTTP API 设计指南 javascript流行库汇总 验证api underscore 中文手册 underscore源码分析 underscore源码分析-亚里士朱德的博客 underscrejs en api lodash - underscore的代替品 ext4api qwrap手册 缓动函数 svg 中文参考 svg mdn参考 svg 导出 canvas svg 导出 png ai-to-svg localStorage 库 16. 综合类 前端知识体系 前端知识结构 Web前端开发大系概览 Web前端开发大系概览-中文版 Web Front-end Stack v2.2 免费的编程中文书籍索引 前端书籍 前端免费书籍大全 前端知识体系 免费的编程中文书籍索引 智能社 - 精通JavaScript开发 重新介绍 JavaScript(JS 教程) 麻省理工学院公开课:计算机科学及编程导论 JavaScript中的this陷阱的最全收集–没有之一 JS函数式编程指南 JavaScript Promise迷你书(中文版) 腾讯移动Web前端知识库 Front-End-Develop-Guide 前端开发指南 前端开发笔记本 大前端工具集 - 聂微东 前端开发者手册 17. 工具类 前端人的俱乐部 如何优雅地使用Sublime Text 新编码神器Atom使用纪要 css sprite 雪碧图制作 版本控制入门 – 搬进 Github Grunt-beginner前端自动化工具 IntelliJ IDEA 简体中文专题教程 SublimeText Atom visual studio code 18. 综合效果搜索平台 JavaScript 资源大全中文版 100+ 超全的web开发工具和资源 设计师网址导航 快搜 阿里iconfont zoommyapp.com unsplash.com www.pinterest.com New Old Stock 500px.com 效果网 花瓣网 优美图 codepen 摄图网 常用的JavaScript代码片段 19. 团队 BLOG 腾讯ISUX 奇舞周刊 淘宝前端团队(FED) 码农周刊 WEB前端开发 A JS tip per day! 腾讯全端 AlloyTeam 阿里巴巴-U一点 百度WEB前端研发部 携程设计委员会 平安科技移动开发二队技术周报 20. 开发中心 mozilla js参考 chrome开发中心(chrome的内核已转向blink) safari开发中心 microsoft js参考 js秘密花园 js秘密花园 w3help(综合Bug集合网站) 21. ECMAScript Understanding ECMAScript 6 - Nicholas C. Zakas exploring-es6 exploring-es6翻译 exploring-es6翻译后预览 阮一峰 es6 阮一峰 Javascript ECMA-262,第 5 版 es5 22. D3 d3 Tutorials Gallery lofter iteye ruanyifeng 23. RequrieJS Javascript模块化编程(一):模块的写法 Javascript模块化编程(二):AMD规范 Javascript模块化编程(三):require.js的用法 RequireJS入门(一) RequireJS入门(二) RequireJS进阶(三) requrie源码学习 requrie 入门指南 requrieJS 学习笔记 requriejs 其一 require backbone结合 24. SeaJS seajs seajs 中文手册 25. Less & Sass sass sass教程-sass中国 Sass 中文文档 less 26. Markdown Markdown 语法说明 (简体中文版) markdown入门参考 gitbook mdeditor stackedit mditor lepture-editor markdown-editor 作业部落 27. 兼容性 esma 兼容列表 W3C CSS验证服务 caniuse csscreator microsoft 在线测兼容-移动端 emulators 28. UI相关 bootcss ANT DESIGN MetroUICSS semantic Buttons kitecss pintuer amazeui worldhello linuxtoy gitmagic rogerdudler gitref book gogojimmy 29. 图表类 Highcharts 中文API Highcharts 英文API ECharts 百度的图表软件 高德地图 开源的矢量图脚本框架 svg 地图 30. 正则表达式 JS正则表达式元字符 正则表达式30分钟入门教程 MDN-正则表达式 ruanyifeng - RegExp对象 小胡子哥 - 进阶正则表达式 is.js 正则在线测试 31. 前端规范 通过分析github代码库总结出来的工程师代码书写习惯 HTML&CSS编码规范 by @mdo 团队合作的css命名规范-腾讯AlloyTeam前端团队 前端编码规范之js - by yuwenhui 前端编码规范之js - by 李靖 前端开发规范手册 Airbnb JavaScript 编码规范(简体中文版) AMD与CMD规范的区别 AMD与CMD规范的区别 KISSY 源码规范 bt编码规范 规范加强版 前端代码规范 及 最佳实践 百度前端规范 百度前端规范 百度前端规范 ECMAScript6 编码规范–广发证券前端团队 JavaScript 风格指南/编码规范(Airbnb公司版) 网易前端开发规范 css模块 前端规范资源列表 32. PHP 最流行的PHP 代码规范 最流行的PHP 代码规范 33. 各大公司开源项目 Facebook Projects 百度web前端研发部 百度EFE 百度github alloyteam alloyteam-github alloyteam-AlloyGameEngine AlloyDesigner(即时修改,即时保存,设计稿较正,其它开发辅助工具) H5交互页编辑器AEditor介绍 AEditor 值得订阅的weekly 奇舞团开源项目 Qunar UED Scrat 34. 常用 ieBetter.js(让IE6-IE8拥有IE9+,Chrome等浏览器特性) 模拟键盘 拼音 中国个人身份证号验证 35. 算法 数据结构与算法 JavaScript 常见排序算法(JS版) 经典排序 常见排序算法-js版本 JavaScript 算法与数据结构 精华集 面试常考算法题精讲 36. JSON 模拟生成JSON数据 37. Ext, EasyUI, J-UI 及其它各种UI方案 extjs ext4英文api ext4中文api jquery easyui 未压缩源代码 J-UI MUI-最接近原生APP体验的高性能前端框架 Amaze UI(中国首个开源 HTML5 跨屏前端框架) 淘宝 HTML5 前端框架 KISSY - 阿里前端JavaScript库 网易Nej - Nice Easy Javascript Kendo UI MVVM Demo Bootstrap Foundation Smart UI 雅虎UI - CSS UI 38. 页面 社会化 分享功能 (pc端)百度分享 (pc端)JiaThis (移动端)社会化分享组件 (移动端)ShareSDK 轻松实现社会化功能 (移动端)友盟分享 39. 富文本编辑器 功能齐全 tinymce 百度 ueditor 经典的ckeditor 经典的kindeditor wysiwyg 一个有情怀的编辑器。Bach’s Editor tower用的编辑器 summernote 编辑器 html5编辑器 Quilljs编辑器 wangEditor 40. 前端概述 前端工具大全 什么是前端工程化 [译] 前端攻略-从路人甲到英雄无敌 41. Gulp Gulp官网 Gulp中文网 gulp资料收集 Gulp:任务自动管理工具 - ruanyifeng Gulp插件 Gulp探究折腾之路(I) Gulp折腾之路(II) Gulp不完全入门教程 为什么使用gulp? Gulp安装及配合组件构建前端开发一体化 Gulp 入门指南 Gulp 入门指南 - nimojs Gulp in Action Gulp开发教程(翻译) 前端构建工具gulpjs的使用介绍及技巧 42. Grunt gruntjs Grunt中文网 43. Fis fis 官网 fis 44. pc图轮 Vue 的图片轮播组件:vue-slider 左右按钮多图切换 fullpage全屏轮播 45. 移动端图轮 滑屏效果 全屏fullpage 单个图片切换 单个全屏切换 百度的切换库 单个全屏切换 滑屏效果 旋转拖动设置 类似于swipe切换 支持多种形式的触摸滑动 滑屏效果 大话主席pc移动图片轮换 滑屏效果 基于zepto的fullpage WebApp定宽网页设计下,固定宽度布局开发WebApp并实现多终端下WebApp布局自适应 判断微信客户端的那些坑 可以通过javascript直接调用原生分享的工具 JiaThis 分享到微信代码 聊聊移动端跨平台开发的各种技术 前端自动化测试 多种轮换图片 滑动侧边栏 46. 文件上传 百度上传组件 上传 flash 头像上传 图片上传预览 图片裁剪 图片裁剪-shearphoto jQuery图片处理 47. 模拟select 糖饼 select flexselect 双select select2 48. 取色插件 类似 Photoshop 的界面取色插件 jquery color 取色插件集合 farbtastic 圆环+正方形 49. 城市联动 jquery.cityselect.js基于jQuery+JSON的省市或自定义联动效果 50. 剪贴板 剪贴板 clipboard 最新的剪切方案 不是Flash的剪贴板 51. 简繁转换 简繁转换 52. 表格 Grid facebook表格 类似于Excel编辑表格-handsontable bootstrap-table插件 datatables 53. 在线演示 js 在线编辑 - runjs js 在线编辑 - jsbin js 在线编辑 - codepen js 在线编辑 - jsfiddle java 在线编辑 - runjs js 在线编辑 - hcharts js 在线编辑 - jsdm sql 在线编辑 - sqlfiddle mozilla 在线编辑器 54. 常规优化 Javascript高性能动画与页面渲染 移动H5前端性能优化指南 5173首页前端性能优化实践 给网页设计师和前端开发者看的前端性能优化 复杂应用的 CSS 性能分析和优化建议 张鑫旭——前端性能 前端性能监控总结 web前端性能优化进阶路 前端技术:网站性能优化之CSS无图片技术 浏览器的加载与页面性能优化 页面加载中的图片性能优化 Hey——前端性能 YSLOW中文介绍 转一篇Yahoo关于网站性能优化的文章,兼谈本站要做的优化 Yahoo!团队实践分享:网站性能 网站性能优化指南:什么使我们的网站变慢? 网站性能优化实践,减少加载时间,提高用户体验 浅谈网站性能优化 前端篇 前端重构实践之如何对网站性能优化? 前端性能优化:使用媒体查询加载指定大小的背景图片 网站性能系列博文 加载,不只是少一点点 前端性能的测试与优化 分享网页加载速度优化的一些技巧? web前端优化(基于Yslow 网站性能优化工具大全 【高性能前端1】高性能HTML 【高性能前端2】高性能CSS 由12306谈谈网站前端性能和后端性能优化 毫秒必争,前端网页性能最佳实践 网站性能工具Yslow的使用方法 前端工程与性能优化(上):静态资源版本更新与缓存 前端工程与性能优化(下):静态资源管理与模板框架 HTTPS连接的前几毫秒发生了什么 Yslow Essential Web Performance Metrics — A Primer, Part 1 Essential Web Performance Metrics — Part 2 YUISlide,针对移动设备的动画性能优化 Improving Site Performance 让网站提速的最佳前端实践 Why Website Speed is Important Need for Speed – How to Improve your Website Performance 阿里无线前端性能优化指南 (Pt.1 加载期优化 55. 优化工具 JavaScript 性能分析新工具 OneProfile JavaScript 堆内存分析新工具 OneHeap 56. 在线工具 google在线工具 阿里-免费测试服务 阿里-F2etest多浏览器兼容性测试解决方案 js性能测试 57. 前端架构 技术架构 前端架构 如何成为前端架构师 关于前端架构-张克军 百度腾讯offer比较(腾讯游戏VS百度基础架构) 58. 推荐作品 winter代码片段需要翻墙 fgm 岑安作品集 当耐特demo集合 米空格 js作品 myFocus SeaJS组件库 颜海镜作品 脚儿网作品 javascript个人作品 妙味的雷东升游戏作品 javascript作品集 云五笔,灰度产生生成工具 项目主页 个性的作品主页 ucren js demos 集 智能社 实例陈列架 zoye demo 王员外 平凡 jyg 游戏案例 很多jquery插件 不羁虫 - soJs 作品系列 frozenui 黑白棋 fromone pazguille Html5 VideoPlayer Proton 烟花 59. 简历模板 简历 张伦 简历 马斯特 张秋怡 翁天信 动画方式的简历 组件丰富简历 haorooms博客 60. 求职 面试你之前,我希望在简历上看到这些! 61. 面试题 那几个月在找工作(百度,网易游戏) 2014最新面试题 2016校招内推 – 阿里巴巴前端 – 三面面试经历 年后跳槽那点事:乐视+金山+360面试之行 Interviewing a front-end developer 拉勾网js面试题 前端面试 Web开发笔试面试题 大全 前端开发面试题 2014最新前端面试题 百度面试 前端工作面试问题 前端开发面试题 5个经典的前端面试问题 最全前端面试问题及答案总结 如何面试一名前端开发工程师? 史上最全 前端开发面试问题及答案整理 前端实习生面试总结 史上最全 前端开发面试问题及答案整理 BAT及各大互联网公司2014前端笔试面试题:JavaScript篇 前端开发面试题大收集 收集的前端面试题和答案 如何面试前端工程师 前端开发面试题 牛客网-笔试面经 62. iconfont 中文字体 淘宝字库 字体 制作教程 zhangxinxu-icommon icommon 用字体在网页中画ICON图标(推荐教程 字体压缩工具 63. Fiddler Fiddler调式使用知多少(一深入研究) 微信fiddle 微信fiddle 64. Chrome Google Chrome 官方 Chrome - 基础 Chrome - 进阶 Chrome - 性能 Chrome - 性能进阶 Chrome - 移动 Chrome - 使用技巧 Chrome - Console控制台不完全指南 Chrome - Workspace使浏览器变成IDE network面板 chrome开发工具快捷键 chrome调试工具常用功能整理 Chrome 开发工具 Workspace 使用 Chrome神器Vimium快捷键学习记录 sass调试-w3cplus 如何更专业的使用Chrome开发者工具-w3cplus chrome调试canvas chrome profiles1 chrome profiles3 chrome移动版调试 chrome调试 chrome的调试 chrome console 命令详解 查看事件绑定1 查看事件绑定2 神器——Chrome开发者工具(一 奇趣百科性能优化(Chrome DevTools 中的 Timeline Profils 等工具使用介绍 chrome 开发者工具的 15 个小技巧 Chrome开发者工具不完全指南 Chrome 开发者工具使用技巧 65. Firebug firebug视频教程 firefox 模拟器 console.log 命令详解 Firebug入门指南 Firebug控制台详解 66. 移动,微信调试 浏览器端调试安卓 移动端前端开发调试 使用 Chrome 远程调试 Android 设备 mac移动端调试 mac移动端调试 无线调试攻略 无线调试攻略 屌爆了,完美调试 微信webview(x5 微信调试的那些事 远程console 微信调试工具 各种真机远程调试方法汇总 67. iOS Simulator Simulator iOS Simulator的介绍和使用心得 68. Image loading img 智图-图片优化平台 在线png优化 SM.MS(图床工具~简易好用) yutuku:极简图床 Qchan图床 69. 浏览器同步 puer liveReload f5 File Watchers 70. 在线PPT制作 nodePPT Cleaver快速制作网页PPT impress.js PPT reveal bespoke-fx slippy 71. 前端导航网站 界面清爽的前端导航 前端导航 前端网址导航 前端名录 前端导航 前端开发资源 前端开发仓库 - 众多效果的收集地 前端资源导航 F2E 前端导航 72. 常用CDN 新浪CDN 百度静态资源公共库 360网站卫士常用前端公共库CDN服务 Bootstrap中文网开源项目免费 CDN 服务 开放静态文件 CDN - 七牛 CDN加速 - jq22 jQuery CDN 微软CDN 73. Git git-scm 廖雪峰-Git教程 git-for-windows GitHub 添加 SSH keys gogithub git常规命令练习 git的资料整理 我所记录的git命令(非常实用) 企业开发git工作流模式探索部分休整 GitHub 漫游指南 GitHub秘籍 使用git和github进行协同开发流程 动画方式练习git 74. 各种日期日历 经典my97 强大的独立日期选择器 fullcalendar fullcalendar日历控件知识点集合 中文api 农历日历 超酷的仿百度带节日日历老黄历控件 日期格式化 大牛日历控件 我群某管理作品 input按位替换-官网 input按位替换-github bootstrap-daterangepicker 国外30个插件集合 JavaScript datepicker Datepair.js 一个风格多样的日历 弹出层式的全日历 jquery双日历 大气实用jQuery手机移动端日历日期选择插件 jQuery Mobile 移动开发中的日期插件Mobiscroll 75. Date library Datejs sugarjs 76. 其它 Mock.js 是一款模拟数据生成器 特色的HTML框架可以创建精美的iOS应用 淘宝SUI avalonjs Avalon新一代UI库: OniUI avalon.oniui-基于avalon的组件库 生成二维码(草料) 77. 效果类 弹出层 焦点图轮播特效 HTML5 有哪些让你惊艳的 demo? 78. 弹出层 artDialog 最新版 artDialog 文档 google code 下载地址 贤心弹出层 响应式用户交互组件库 sweetalert-有css3动画弹出层 79. 优秀JavaScript项目 Angular和Webpack种子文件 Fis3面向前端的工程构建系统 Fis3 DEMO 前端JQuery系列:源码剖析 avalon框架 Microsoft ChakraCore 微软的Chakra引擎 Quintus HTML游戏引擎 一个用node.js搭建的有趣博客 Web前端助手–FeHelper(Chrome扩展) 百度前端技术学院 Cheerio(node.js中的jQuery) nodejs的一个聊天软件 类似微信 使用html5和node.js构建的网易云音乐 babel ES6转换为ES5 一个JS富文本编辑器 一个JS脑图可视化工具 一个JS写的Flappy Bird Game 一个JS写的GBA模拟器 SegmentFault写的Markdown解析器 基于node.js的Ghost博客 学习react的demos 80. 其他一些推荐 那些所倚靠的利器记载 如何优雅地使用Sublime Text sublime text 下的Markdown写作 新编码神器Atom使用纪要 Win下最爱效率神器:AutoHotKey Mac必备软件渐集之ZSH-终极Shell Win下必备神器之Cmder Vimium~让您的Chrome起飞","categories":[],"tags":[{"name":"技术","slug":"技术","permalink":"http://www.mvtime.cn/tags/技术/"},{"name":"前端","slug":"前端","permalink":"http://www.mvtime.cn/tags/前端/"}]},{"title":"一个人的时候","slug":"一个人的时候","date":"2017-02-19T04:27:56.000Z","updated":"2017-02-20T08:47:03.000Z","comments":true,"path":"2017/02/19/一个人的时候/","link":"","permalink":"http://www.mvtime.cn/2017/02/19/一个人的时候/","excerpt":"","text":"一个人的思考每个人都有一个人的时候,当你一个人独处的时候,你会想什么,做什么。 放一首音乐吧,舒缓你紧张神经 读一本书吧,带你去书中的世界 看一场电影吧,体验惊险刺激 写一篇文章吧,记录下你的生活 思考人生吧 人是一枝有思想的芦苇,对于生命,我们和其它万物又有什么区别,也不知道怎么滴变异,人类才诞生了智慧,如果不思考,机械的上班,学习,玩,猴子🐒也可以做到啊。 一个人的行为君子独处,守正不挠,出自:《汉书·刘向传》有德行的人单居独处,也坚守正道,不肯屈从。 一个人独处的时候,才是你真正释放自己的时候,你可能再也不用伪装,不用约束自己。这些都没有关系,但是当你一个人独处时变成了另外一个人似的,这真的需要你好好反思了。 一个人的总结以上都是扯淡,就是希望自己一个人过周末的时候多读书多看报少吃零食多睡觉 思考一些问题 约束自己的行为 变得勤劳 做些有意思、有意义的事情","categories":[],"tags":[{"name":"生活","slug":"生活","permalink":"http://www.mvtime.cn/tags/生活/"},{"name":"思考","slug":"思考","permalink":"http://www.mvtime.cn/tags/思考/"}]},{"title":"Markdown语法","slug":"Markdown语法","date":"2017-02-19T04:12:44.000Z","updated":"2017-02-20T04:38:32.000Z","comments":true,"path":"2017/02/19/Markdown语法/","link":"","permalink":"http://www.mvtime.cn/2017/02/19/Markdown语法/","excerpt":"","text":"前言原来用的MWeb编辑器,上面有很多快捷键,有图形化按钮,很方便。然后换了个逼格比较高的Typora,漂亮的不像实力派,因为少了按钮,那我必须记下来了,因为觉得MWeb的语法总结很好,所以以下是来自MWeb编辑器的Markdown语法 Markdown 的设计哲学 Markdown 的目標是實現「易讀易寫」。不過最需要強調的便是它的可讀性。一份使用 Markdown 格式撰寫的文件應該可以直接以純文字發佈,並且看起來不會像是由許多標籤或是格式指令所構成。Markdown 的語法有個主要的目的:用來作為一種網路內容的寫作用語言。 本文约定如果有写 效果如下:, 在 MWeb 编辑状态下只有用 CMD + 4 或 CMD + R 预览才可以看效果。 标题Markdown 语法: # 第一级标题 `<h1>` ## 第二级标题 `<h2>` ###### 第六级标题 `<h6>` 效果如下: 第一级标题 <h1>第二级标题 <h2>第六级标题 <h6>强调Markdown 语法: *这些文字会生成`<em>`* _这些文字会生成`<u>`_ **这些文字会生成`<strong>`** __这些文字会生成`<strong>`__ 在 MWeb 中的快捷键为: CMD + U、CMD + I、CMD + B效果如下: 这些文字会生成<em>这些文字会生成<u> 这些文字会生成<strong>这些文字会生成<strong> 换行四个及以上空格加回车。如果不想打这么多空格,只要回车就为换行,请勾选:Preferences - Themes - Translate newlines to <br> tags 列表无序列表Markdown 语法: * 项目一 无序列表 `* + 空格键` * 项目二 * 项目二的子项目一 无序列表 `TAB + * + 空格键` * 项目二的子项目二 在 MWeb 中的快捷键为: Option + U效果如下: 项目一 无序列表 * + 空格键 项目二 项目二的子项目一 无序列表 TAB + * + 空格键 项目二的子项目二 有序列表Markdown 语法: 1. 项目一 有序列表 `数字 + . + 空格键` 2. 项目二 3. 项目三 1. 项目三的子项目一 有序列表 `TAB + 数字 + . + 空格键` 2. 项目三的子项目二 效果如下: 项目一 有序列表 数字 + . + 空格键 项目二 项目三 项目三的子项目一 有序列表 TAB + 数字 + . + 空格键 项目三的子项目二 任务列表(Task lists)Markdown 语法: - [ ] 任务一 未做任务 `- + 空格 + [ ]` - [x] 任务二 已做任务 `- + 空格 + [x]` 效果如下: [ ] 任务一 未做任务 - + 空格 + [ ] [x] 任务二 已做任务 - + 空格 + [x] 图片Markdown 语法: ![GitHub set up](http://zh.mweb.im/asset/img/set-up-git.gif) 格式: ![Alt Text](url) Control + Shift + I 可插入Markdown语法。如果是 MWeb 的文档库中的文档,还可以用拖放图片、CMD + V 粘贴、CMD + Option + I 导入这三种方式来增加图片。效果如下: MWeb 引入的特别的语法来设置图片宽度,方法是在图片描述后加 -w + 图片宽度 即可,比如说要设置上面的图片的宽度为 140,语法如下: 链接Markdown 语法: email <[email protected]> [GitHub](http://github.com) 自动生成连接 <http://www.github.com/> Control + Shift + L 可插入Markdown语法。如果是 MWeb 的文档库中的文档,拖放或CMD + Option + I 导入非图片时,会生成连接。效果如下: Email 连接: example@example.com连接标题Github网站自动生成连接像: http://www.github.com/ 这样 区块引用Markdown 语法: 某某说: 第一行引用第二行费用文字 CMD + Shift + B 可插入Markdown语法。效果如下: 某某说: 第一行引用第二行费用文字 行内代码Markdown 语法: 像这样即可:`<addr>` `code` CMD + K 可插入Markdown语法。效果如下: 像这样即可:<addr> code 多行或者一段代码Markdown 语法: ```js function fancyAlert(arg) { if(arg) { $.facebox({div:'#foo'}) } } ``` CMD + Shift + K 可插入Markdown语法。效果如下: function fancyAlert(arg) { if(arg) { $.facebox({div:'#foo'}) } } 顺序图或流程图Markdown 语法: ```sequence 张三->李四: 嘿,小四儿, 写博客了没? Note right of 李四: 李四愣了一下,说: 李四-->张三: 忙得吐血,哪有时间写。 ``` ```flow st=>start: 开始 e=>end: 结束 op=>operation: 我的操作 cond=>condition: 确认? st->op->cond cond(yes)->e cond(no)->op ``` 效果如下( Preferences - Themes - Enable sequence & flow chart 才会看到效果 ): 张三->李四: 嘿,小四儿, 写博客了没? Note right of 李四: 李四愣了一下,说: 李四-->张三: 忙得吐血,哪有时间写。 st=>start: 开始 e=>end: 结束 op=>operation: 我的操作 cond=>condition: 确认? st->op->cond cond(yes)->e cond(no)->op 更多请参考:http://bramp.github.io/js-sequence-diagrams/, http://adrai.github.io/flowchart.js/ 表格Markdown 语法: 第一格表头 | 第二格表头 --------- | ------------- 内容单元格 第一列第一格 | 内容单元格第二列第一格 内容单元格 第一列第二格 多加文字 | 内容单元格第二列第二格 效果如下: 第一格表头 第二格表头 内容单元格 第一列第一格 内容单元格第二列第一格 内容单元格 第一列第二格 多加文字 内容单元格第二列第二格 删除线Markdown 语法: 加删除线像这样用: 删除这些 效果如下: 加删除线像这样用: 删除这些 分隔线以下三种方式都可以生成分隔线: `` ------- 效果如下: *** ***** - - - ## MathJax Markdown 语法: 块级公式:$$ x = \\dfrac{-b \\pm \\sqrt{b^2 - 4ac}}{2a} $$ \\[ \\frac{1}{\\Bigl(\\sqrt{\\phi \\sqrt{5}}-\\phi\\Bigr) e^{\\frac25 \\pi}} =1+\\frac{e^{-2\\pi}} {1+\\frac{e^{-4\\pi}} {1+\\frac{e^{-6\\pi}}{1+\\frac{e^{-8\\pi}} {1+\\ldots} } } } \\] 行内公式: $\\Gamma(n) = (n-1)!\\quad\\forall n\\in\\mathbb N$ 效果如下(`Preferences` - `Themes` - `Enable MathJax` 才会看到效果): 块级公式: $$ x = \\dfrac{-b \\pm \\sqrt{b^2 - 4ac}}{2a} $$ \\\\[ \\frac{1}{\\Bigl(\\sqrt{\\phi \\sqrt{5}}-\\phi\\Bigr) e^{\\frac25 \\pi}} = 1+\\frac{e^{-2\\pi}} {1+\\frac{e^{-4\\pi}} {1+\\frac{e^{-6\\pi}} {1+\\frac{e^{-8\\pi}} {1+\\ldots} } } } \\\\] 行内公式: $\\Gamma(n) = (n-1)!\\quad\\forall n\\in\\mathbb N$ ------- ## 脚注(Footnote) Markdown 语法: 这是一个脚注:[^sample_footnote] 效果如下: 这是一个脚注:[^sample_footnote] [^sample_footnote]: 这里是脚注信息 ## 注释和阅读更多 <!-- comment --> <!-- more --> Actions->Insert Read More Comment *或者* `Command + .` **注** 阅读更多的功能只用在生成网站或博客时,插入时注意要后空一行。 ## TOC Markdown 语法: [TOC] 效果如下: [TOC]","categories":[],"tags":[{"name":"技术","slug":"技术","permalink":"http://www.mvtime.cn/tags/技术/"},{"name":"Markdown","slug":"Markdown","permalink":"http://www.mvtime.cn/tags/Markdown/"}]},{"title":"Hexo文章添加音乐插件","slug":"Hexo文章添加音乐插件","date":"2017-02-19T03:42:10.000Z","updated":"2017-02-20T07:42:50.000Z","comments":true,"path":"2017/02/19/Hexo文章添加音乐插件/","link":"","permalink":"http://www.mvtime.cn/2017/02/19/Hexo文章添加音乐插件/","excerpt":"","text":"前言Hexo添加音乐插件方法网上已经有一大堆了,此文章是定制化的在文章中插入音乐,比如这样的文章。只需要在文章加入music: xxx就可以设置你想要的音乐了(xxx为网易某个歌曲id) 定制化定制化基于我的主题Material,网易云音乐的外链想必不用说了,我复制下来的是这样的: <iframe frameborder=\"no\" border=\"0\" marginwidth=\"0\" marginheight=\"0\" width=330 height=86 src=\"//music.163.com/outchain/player?type=2&id=29567192&auto=0&height=66\"></iframe> 打开themes/material/layout/_partial/post-content.ejs复制如下代码: <div id=\"post-content\" class=\"markdown-<%= theme.reading.markdown %> mdl-color-text--grey-700 mdl-card__supporting-text fade out\"> <% if(page.music != null) { %> <iframe class=\"music\" frameborder=\"no\" border=\"0\" marginwidth=\"0\" marginheight=\"0\" width=330 height=86></iframe> <% } %> <% if(theme.scheme === 'Paradox') { %> <%- page.content %> <% } %> <% if(theme.scheme === 'Isolation') { %> <div class=\"post-content_wrapper\"> <p class=\"post-title\"> <%- page.title %> </p> <%- page.content %> </div> <% } %> </div> <script> $('.music').attr('src', '//music.163.com/outchain/player?type=2&id=' + <%- page.music %> + '&auto=0&height=66'); </script> 然后在文章添加music就行了: --- title: 关于我 date: 2017-02-19 08:00:13 music: 29567192 --- 结语网易云音乐喜欢了很久,日推很赞,质量很好,最重要的是评论让你觉得不是一个人在听歌,如果想要查看歌曲id,直接在客户端上的歌曲上复制链接地址就ok 这里有个小bug,插件有时会刷新两遍,有很多种可能的原因,算了,先放过它吧 补充:已经解决了问题,看了下大概是因为分享会remove一个class,然后重绘了界面 解决办法在window.onload方法里执行js就ok,这样保证它在重绘后生效","categories":[],"tags":[{"name":"技术","slug":"技术","permalink":"http://www.mvtime.cn/tags/技术/"},{"name":"Hexo","slug":"Hexo","permalink":"http://www.mvtime.cn/tags/Hexo/"}]},{"title":"Hexo设置多说shortname","slug":"Hexo设置多说shortname","date":"2017-02-18T07:40:04.000Z","updated":"2017-02-20T04:38:22.000Z","comments":true,"path":"2017/02/18/Hexo设置多说shortname/","link":"","permalink":"http://www.mvtime.cn/2017/02/18/Hexo设置多说shortname/","excerpt":"","text":"前言本来是很简单的问题,但是浪费我不少时间,所以有必要记录一下,防止下次出错。刚开始配置多说shortname压根没有在意,因为上一个主题的这个是随便填的,而且成功了(黑人问号)。所以这次也是随便写的,怎么都失败,还看了控制台、源代码,后来突然觉得这个shortname这个单词好奇怪,然后百度了一下。 解决比如我的多说二级域名是xxx.duoshuo.com,shortname就是xxx。最后吐槽一下,short不是短的意思吗,短名字?,are you sure?","categories":[],"tags":[{"name":"技术","slug":"技术","permalink":"http://www.mvtime.cn/tags/技术/"},{"name":"Hexo","slug":"Hexo","permalink":"http://www.mvtime.cn/tags/Hexo/"}]},{"title":"Node.js上传图片到七牛","slug":"Node.js上传图片到七牛","date":"2017-02-17T02:28:07.000Z","updated":"2017-02-20T04:38:39.000Z","comments":true,"path":"2017/02/17/Node.js上传图片到七牛/","link":"","permalink":"http://www.mvtime.cn/2017/02/17/Node.js上传图片到七牛/","excerpt":"","text":"前言主题基于 Yilia ,作者使用的是Instagram拉取的相册,捣鼓了半天,翻墙申请账号,最后发现图片爬不下来,后来发现一篇七牛上传的node脚本,因为需要上传的图片太多,然后改成了自己需要的。 if (stats.isFile()) { var suffix = getFilenameSuffix(files[index]); if(!(suffix=='.js'|| suffix == '.DS_Store')){ //要上传文件的本地路径 filePath = path+'/'+files[index]; console.log('抓取到文件: '+files[index]); //上传到七牛后保存的文件名 key = files[index]; //生成上传 Token token = uptoken(bucket, key); // 异步执行 uploadFile(token, key, filePath); arr.push(files[index]); } 这是上传图片的主要代码,主要问题有在photos文件中如果新增了图片,会把以前的图片再次上传一遍,所以做了检查重复不会上传: /** * 判断数组中是否包含某元素 */ function array_contain(array, obj){ for (var i = 0; i < array.length; i++){ if (array[i] == obj)//如果要求数据类型也一致,这里可使用恒等号=== return true; } return false; } 然后愉快的开始执行,因为photos文件里大约有1000多张图片,理所当然的电脑直接死机。因为执行了一千次异步方法,js又是单线程,那就用同步上传的方法,js异步改成同步的话有嵌套或者回调,我用了async库,用法很简单: async.series([ function(callback) { ... }, function(callback) { ... } ]); 要求传入函数数组,而不是对象Object数组,所以我把上传图片方法改成了闭包: //构造上传函数 function uploadFile(uptoken, key, localFile) { var extra = new qiniu.io.PutExtra(); return function(callback) { qiniu.io.putFile(uptoken, key, localFile, extra, function(err, ret) { if(!err) { // 上传成功, 处理返回值 console.log('上传成功 : ',ret.hash, ret.key); callback(); } else { // 上传失败, 处理返回代码 console.log(err); callback(); } }); } } 当读取完图片之后执行上传: if (index == files.length) { // 执行上传 console.log(funArr.length); async.series(funArr, function(err, result) { console.log(\"上传完成\"); fs.writeFile(\"./ins.json\", JSON.stringify(arr, null, \"\\t\")); return; }); } 修改过后的tool.js脚本代码: const fs = require(\"fs\"); const photosPath = \"images/photos\"; const blogPath = \"images/blog\"; var qiniu = require(\"qiniu\"); var async = require(\"async\"); //需要填写你的 Access Key 和 Secret Key qiniu.conf.ACCESS_KEY = ''; qiniu.conf.SECRET_KEY = ''; //构建上传策略函数 function uptoken(bucket, key) { var putPolicy = new qiniu.rs.PutPolicy(bucket+\":\"+key); return putPolicy.token(); } //构造上传函数 function uploadFile(uptoken, key, localFile, arr, filename) { var extra = new qiniu.io.PutExtra(); return function(callback) { qiniu.io.putFile(uptoken, key, localFile, extra, function(err, ret) { if(!err) { // 上传成功, 处理返回值 console.log('上传成功 : ',ret.hash, ret.key); arr.push(filename); callback(); } else { // 上传失败, 处理返回代码 console.log(err); callback(); } }); } } /** * 读取文件后缀名称,并转化成小写 * @param file_name * @returns */ function getFilenameSuffix(file_name) { if(file_name=='.DS_Store'){ return '.DS_Store'; } if (file_name == null || file_name.length == 0) return null; var result = /\\.[^\\.]+/.exec(file_name); return result == null ? null : (result + \"\").toLowerCase(); } /** * 判断数组中是否包含某元素 */ function array_contain(array, obj){ for (var i = 0; i < array.length; i++){ if (array[i] == obj)//如果要求数据类型也一致,这里可使用恒等号=== return true; } return false; } /* 读取photos内的图片 */ function startUploadImg(path, file_name, updload_path) { fs.readdir(path, function (err, files) { if (err) { return; } //要上传的空间 bucket = updload_path; // 读取json文件 try { var data = fs.readFileSync(file_name, 'utf-8'); var arr = JSON.parse(data); } catch (err) { var arr = []; } var funArr = new Array(); (function iterator(index) { if (index == files.length) { // 执行上传 console.log(funArr.length); async.series(funArr, function(err, result) { console.log(\"上传完成\"); fs.writeFile(file_name, JSON.stringify(arr, null, \"\\t\")); return; }); } fs.stat(path + \"/\" + files[index], function (err, stats) { if (err) { return; } if (stats.isFile() && !array_contain(arr, files[index])) { var suffix = getFilenameSuffix(files[index]); if(!(suffix=='.js'|| suffix == '.DS_Store')){ //要上传文件的本地路径 filePath = path+'/'+files[index]; //上传到七牛后保存的文件名 key = files[index]; if (updload_path == 'misterdeng') { if (!array_contain(arr, 'http://od50s29vd.bkt.clouddn.com/'+files[index])) { //生成上传 Token token = uptoken(bucket, key); // 异步执行 funArr.push(uploadFile(token, key, filePath, arr, 'http://od50s29vd.bkt.clouddn.com/'+files[index])); } else { console.log('这个博客图片已经上传过了'); } } else { //生成上传 Token token = uptoken(bucket, key); // 异步执行 funArr.push(uploadFile(token, key, filePath, arr, files[index])); } } } else { console.log('不是文件或者已经上传过了: '+files[index]); } iterator(index + 1); }) }(0)); }); } // 执行方法 startUploadImg(blogPath, 'blog.json', 'misterdeng'); startUploadImg(photosPath, 'source/photos/ins.json', 'hexo'); 刚刚把tool修改成了直接可以上传两个文件夹的图片,一个文章引用,一个是相册,附上一张效果图: ","categories":[],"tags":[{"name":"技术","slug":"技术","permalink":"http://www.mvtime.cn/tags/技术/"},{"name":"Node","slug":"Node","permalink":"http://www.mvtime.cn/tags/Node/"}]},{"title":"秒速五厘米","slug":"秒速五厘米","date":"2017-02-16T05:45:59.000Z","updated":"2017-02-17T04:37:08.000Z","comments":true,"path":"2017/02/16/秒速五厘米/","link":"","permalink":"http://www.mvtime.cn/2017/02/16/秒速五厘米/","excerpt":"","text":"在这几年里我光顾着低头前行只想着得到那无法得到的东西但是又不知道那究竟是什么而这个不知从何而来的想法逐渐地变成一种压迫让我只能靠不停工作来解脱等我惊觉之时逐渐僵硬的心只能感觉到痛苦然后在一天早上我发现曾经那刻骨铭心的感情已然完全失却","categories":[],"tags":[{"name":"生活","slug":"生活","permalink":"http://www.mvtime.cn/tags/生活/"},{"name":"动漫","slug":"动漫","permalink":"http://www.mvtime.cn/tags/动漫/"}]},{"title":"清晨 醒来的时候","slug":"你的名字","date":"2017-02-15T01:54:03.000Z","updated":"2017-02-17T02:55:55.000Z","comments":true,"path":"2017/02/15/你的名字/","link":"","permalink":"http://www.mvtime.cn/2017/02/15/你的名字/","excerpt":"","text":"清晨 醒来的时候不知道自己为什么会哭时常会有这样的情况做过的梦总回想不起只是只是一种有什么要消失的丧失感即使醒来后 也一直存在我一直在寻找 寻找着某个人陷入这种情绪应该是从那天开始的那天 彗星划过天空的那天那就像就像梦幻一般的景色一般那真是 无与伦比美到极致的景色","categories":[],"tags":[{"name":"生活","slug":"生活","permalink":"http://www.mvtime.cn/tags/生活/"},{"name":"动漫","slug":"动漫","permalink":"http://www.mvtime.cn/tags/动漫/"}]}]}