├── .gitignore ├── CSS ├── BFC │ └── README.md ├── 模块化css:BEM学习总结 │ └── readme.md └── 盒模型常见问题 │ └── readme.md ├── LICENSE ├── README.md ├── WEB类库插件使用案例 ├── H5移动端调试神器 - vconsole.js │ ├── README.md │ └── images │ │ └── vconsole-demo.png ├── betterScroll.js封装下拉刷新、上拉加载组件踩坑 │ ├── README.md │ ├── image │ │ └── betterScroll.png │ └── package │ │ ├── index.scss │ │ └── index.tsx ├── qrcode.js生成二维码并提供下载(案例) │ ├── README.md │ ├── demo │ │ ├── demo_qrcode.html │ │ ├── demo_qrcode_jquery.html │ │ └── js │ │ │ ├── jquery.min.js │ │ │ ├── qrcode │ │ │ ├── qrcode.js │ │ │ └── qrcode.min.js │ │ │ └── qrcode_jquery │ │ │ └── jquery.qrcode.min.js │ └── img-1.jpg └── 基于cropper.js封装vue在线图片裁剪组件 │ ├── README.md │ ├── cropper-demo.jpg │ └── demo │ └── imgCropper.vue ├── WEB需求问题解决 ├── BingMap在VUE项目中的使用 │ ├── README.md │ ├── demo │ │ ├── bingMap.vue │ │ └── initMap.js │ └── img │ │ ├── Pushpin_1.png │ │ ├── Pushpin_2.png │ │ ├── Pushpin_3.png │ │ └── Pushpin_4.jpeg └── VUE缓存:动态keep-alive │ └── README.md ├── node.js └── 基于node.js构建文件服务器 │ ├── README.md │ └── nodeFileServer │ ├── file │ └── img │ │ ├── 0.jpg │ │ ├── 1.jpg │ │ ├── 2.jpg │ │ ├── 3.jpg │ │ ├── 4.jpg │ │ ├── 5.jpg │ │ ├── 6.jpg │ │ └── 7.jpg │ ├── js │ └── suffix.js │ └── server.js ├── react ├── articles │ ├── learning react day 1 │ │ ├── ES6 │ │ │ ├── arrow.js │ │ │ ├── class.js │ │ │ └── templateLiteral.js │ │ └── README.md │ └── learning react day 2 │ │ └── README.md ├── questions │ └── README.md └── react-demo │ └── demo │ ├── .gitignore │ ├── README.md │ ├── package.json │ ├── public │ ├── favicon.ico │ ├── index.html │ └── manifest.json │ ├── src │ ├── App.css │ ├── App.js │ ├── App.test.js │ ├── index.css │ ├── index.js │ ├── logo.svg │ └── registerServiceWorker.js │ └── yarn.lock ├── web奇技淫巧 ├── JSON数据处理-链型数据与树型数据互转 │ ├── demo │ │ ├── demo_1.js │ │ └── demo_2.js │ ├── img │ │ └── tree.png │ └── readme.md ├── Native JS processing time requirements │ ├── README.md │ └── demo │ │ ├── demo1.js │ │ └── demo2.js ├── js中的slice非常规用法 │ ├── demo.js │ └── readme.md └── 选项卡点击更换背景图 │ ├── icons │ ├── item-0-blue.png │ ├── item-0-red.png │ ├── item-1-blue.png │ ├── item-1-red.png │ ├── item-2-blue.png │ └── item-2-red.png │ ├── images │ └── demo.png │ ├── index.html │ ├── js │ ├── click.js │ └── jquery.min.js │ └── readme.md ├── web概念 ├── ES6、ES7中async、await函数详解 │ └── readme.md └── 事件委托 │ ├── README.md │ └── demo │ └── demo.html ├── 前端常用概念 └── 移动端触摸事件(touchstart、touchmove、touchend) │ ├── README.md │ └── demo │ └── index.html ├── 小程序 ├── 小程序开发-mpvue中使用图表库 │ ├── img │ │ ├── demo-1.png │ │ └── show.gif │ └── readme.md ├── 小程序开发-mpvue构建小程序项目-1-踩坑 │ └── readme.md ├── 小程序开发-利用canvas实现保存二维码海报到本机 │ ├── README.md │ ├── demo │ │ ├── index.vue │ │ ├── readme.md │ │ └── wx.js │ └── img │ │ └── demo.jpg ├── 小程序开发-常用api汇总 │ └── readme.md └── 小程序开发-简明文档-Part 1 │ └── README.md ├── 数据库 ├── learning mongoDB day 1 │ └── README.md ├── learning mongoDB day 2 │ └── nodeServer │ │ ├── package-lock.json │ │ └── server.js └── mac下安装使用mongodb │ └── README.md ├── 框架相关 └── react │ └── react_antd │ └── AntdPro-学习使用-001-安装使用及结构布局.md └── 随笔 └── one day in alibaba └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | npm-debug.log -------------------------------------------------------------------------------- /CSS/BFC/README.md: -------------------------------------------------------------------------------- 1 | 2 | ### 什么是BFC 3 | 4 | > BFC全称Block Formatting Context, 即块格式化上下文。 5 | 6 | ### 视觉格式化模型 7 | 8 | > 视觉格式化模型(visual formatting model)是用来处理文档并将其显示在视觉媒体上的机制。 9 | 10 | 视觉格式化模型定义了盒(Box)的生成 11 | - 块盒 12 | - 行内盒 13 | - 匿名盒(没有名字不能被选择器选中的盒) 14 | 15 | **盒的类型由==display属性==决定** 16 | 17 | ### 盒分类 18 | 19 | #### 1、块盒(block box) 20 | 21 | - 当display为blcok、list-item、table时,称之为块级元素(block-level) 22 | - 视觉上呈现为块,竖直排列 23 | - 默认参与块格式化上下文 24 | - 每个块级元素至少生成一个块级盒,称之为主要块级盒(principal block-level box) 25 | - 一些元素(如 \)会生成额外的盒来放置项目符号,不过多数元素只生成一个主要块级盒 26 | 27 | #### 2、行内盒(inline box) 28 | 29 | - 当display为inline、inline-block、inline-table时,称之为行内级元素 30 | - 视觉上它将内容与其它行内元素排列为多行,典型的如段落内容,有文本(可以有多种格式譬如着重),或图片,都是行内级元素 31 | - 行内元素生成行内级盒(inline-level box),参与行内格式化上下文。(参与行内格式化上下文的行内级盒称为行内盒,displlay:inline的元素都是行内盒) 32 | - 不参与生成行内格式化上下文的行内级盒成为原子行内级盒,这些盒由可替换行内元素,或display为inline-block、inline-table的元素生成,不能拆分成多个盒 33 | 34 | #### 3、匿名盒(anonymous box) 35 | 36 | - 匿名盒分==匿名块盒== 与 ==匿名行内盒==,由于没有名字,所以无法用选择器选择,他们所有属性都是inherit或初始默认值 37 | 38 | ##### 匿名盒例子 39 | 40 | ```html 41 |
42 | some inline text 43 |

followed by a paragraph

44 | followed by more inline text. 45 |
46 | ``` 47 | 48 | ![匿名盒演示](https://user-gold-cdn.xitu.io/2017/9/12/7a736c1dc28c124ace4ef98b555e8ed1?imageView2/0/w/1280/h/960/format/webp/ignore-error/1) 49 | 50 | ### 三种定位方案 51 | 52 | > 在定位的时候,浏览器就会根据元素的盒类型和上下文对这些元素进行定位,可以说盒就是定位的基本单位。定位时,有三种定位方案,分别是==常规流==,==浮动==, 以及==绝对定位==。 53 | 54 | #### 1、常规流(normal flow) 55 | 56 | - 在常规流中,盒一个接一个排列 57 | - 块级格式化上下文:竖着排列 58 | - 行内格式化上下文:横着排列 59 | - position为static或relative,且float为none时,触发常规流 60 | - 对于静态定位(static positioning) 61 | - position:static 62 | - 盒的位置是常规流中的位置 63 | - 对于相对定位(relative positioning) 64 | - position:relative 65 | - 盒偏移位置由 top、bottom、left、right属性定义 66 | - 即使有偏移,仍保留原有的未知,其他常规流不能占用这些位置 67 | 68 | #### 2、浮动(float) 69 | 70 | - 盒称为浮动盒(floating boxes) 71 | - 位于当前行的开头或结尾 72 | - 会导致常规流环绕在它周边 73 | - 除非设置clear属性 74 | 75 | #### 3、绝对定位(absolute positioning) 76 | 77 | - 绝对定位方案,脱离文档流 78 | - 盒从常规流中被移除,不影响常规流的布局 79 | - 其定位相对于包含它的块 80 | - top、bottom、left、right 81 | - 若元素属性为absolute或fixed 82 | - position:absolute 83 | - position:fixed 84 | - 元素为绝对定位元素 85 | - position:absolute的元素的定位 86 | - 相对于最近的一个relative、fixed或absolute的父元素 87 | - 没有以上的元素,则相对于body 88 | 89 | ### 块格式化上下文 90 | 91 | > **==块格式化上下文是页面css视觉渲染的一部分,用于决定块盒子的布局及浮动相互影响范围的一个区域==** 92 | 93 | #### BFC的创建方法 94 | 95 | 根元素或其他包含它的元素 96 | 97 | - 浮动 98 | - float不为none 99 | - 绝对定位 100 | - position:absolute 101 | - position:fixed 102 | - 行内块 103 | - display:inline-block 104 | - 表格单元格 105 | - display:table-cell 106 | - html表格单元格默认属性 107 | - overflow不为visible 108 | - 弹性盒flex boxes 109 | - display:flex 110 | - display:inline-flex 111 | 112 | **最常见的==overflow:hidden==、==float:left/right==、==position:absolute==,看到这些,说明该元素创建了一个BFC** 113 | 114 | #### BFC的范围 115 | 116 | > 一个BFC包含创建该上下文元素的所有子元素 117 | 118 | > 但不包括创建了新BFC的子元素的内部元素 119 | 120 | ```html 121 |
122 |
123 |
124 |
125 |
126 |
127 |
128 |
129 |
130 |
131 | ``` 132 | 133 | - BFC_1 134 | - 创建元素为#div_1 135 | - 范围包含:#div_2、#div_3、#div_4、#div_5 136 | - BFC_2 137 | - 创建元素为#div_5 138 | - 范围包含:#div_6、#div_7 139 | 140 | > 一个元素不能同时存在于两个BFC中 141 | 142 | > BFC的一个最重要的效果是,让处于BFC内部的元素与外部的元素相互隔离,使内外元素的定位不会相互影响。这是利用BFC清除浮动所利用的特性。 143 | 144 | #### BFC的一些特性 145 | 146 | - 内部的盒会在垂直方向一个接一个排列(可以看作BFC中有一个的常规流) 147 | - 处于同一个BFC中的元素相互影响,可能会发生margin collapse 148 | - 每个元素的margin box的左边,与容器块border box的左边相接触(对于从左往右的格式化,否则相反)。即使存在浮动也是如此 149 | - BFC就是页面上的一个隔离的独立容器,容器里面的子元素不会影响到外面的元素,反之亦然 150 | - 计算BFC的高度时,考虑BFC所包含的所有元素,连浮动元素也参与计算 151 | - 浮动盒区域不叠加到BFC上 152 | 153 | ### 实例 154 | 155 | #### 实例1 156 | 157 | ```html 158 | 176 |
177 |
178 |
179 |
180 | ``` 181 | 182 | 效果 183 | 184 | ![实例1效果图](https://user-gold-cdn.xitu.io/2017/9/12/79ca79e60ce01f6521edd5f4827a0e05?imageView2/0/w/1280/h/960/format/webp/ignore-error/1) 185 | 186 | - 绿色#left向左浮动float:left创建了新的BFC 187 | - 绿色#left脱离文档流 188 | - 粉色#right就被定义到父元素的左上角 189 | - 最终绿色#left与粉色#right发生了重叠 190 | - 父元素并未创建BFC,所以高度不会考虑绿色#right,发生了高度坍塌 191 | - BFC特性6:浮动区域不叠加到BFC区域上 192 | 193 | #### 实例2 194 | 195 | ```html 196 | .BFC{ 197 | overflow: hidden; 198 | } 199 | 200 |
201 |
202 |
203 |
204 | ``` 205 | 206 | 效果 207 | 208 | ![实例2效果图](https://user-gold-cdn.xitu.io/2017/9/12/e87b60f9f1502616b1f3bf511523ab70?imageView2/0/w/1280/h/960/format/webp/ignore-error/1) 209 | 210 | - 通过overflow:hidden对父元素创建了BFC 211 | - 计算高度的时候会将浮动的#right计算进去 212 | - BFC特性5:计算BFC高度时,浮动元素也参与计算 213 | 214 | #### 实例3 215 | 216 | ```html 217 | 226 | 227 |
228 |
229 |
230 |
231 |
232 |
233 |
234 |
235 | ``` 236 | 237 | 效果 238 | 239 | ![实例3效果图](https://user-gold-cdn.xitu.io/2017/9/12/de6e7e8c21f62befc82db4cbcd3909bd?imageView2/0/w/1280/h/960/format/webp/ignore-error/1) 240 | 241 | - 由于粉色#left并没有创建BFC,所以内部元素会受到外部元素样式的影响,被#right挤压到了右边 242 | 243 | 244 | #### 实例4 245 | 246 | ```html 247 |
248 |
249 |
250 |
251 |
252 |
253 |
254 |
255 | ``` 256 | 257 | 效果图 258 | 259 | ![实例4效果图](https://user-gold-cdn.xitu.io/2017/9/12/6321ac6a5114233f0fd5a3e1955b270b?imageView2/0/w/1280/h/960/format/webp/ignore-error/1) 260 | 261 | - 粉色#left创建了BFC 262 | - 粉色#left不与绿色#right发生重叠,内部白色块处于隔离的空间 263 | - BFC特性4:BFC就是页面上一个隔离的独立容器 264 | - 白色块就不会受到外部绿色#right的影响 265 | 266 | > 案例参考及原文连接: 267 | 268 | > [学习BFC-考拉海购前端团队](https://juejin.im/post/59b73d5bf265da064618731d) 269 | -------------------------------------------------------------------------------- /CSS/模块化css:BEM学习总结/readme.md: -------------------------------------------------------------------------------- 1 | # 模块化css: BEM学习总结 2 | 3 | > 参考文章: [编写模块化CSS:BEM--大漠](https://www.w3cplus.com/css/css-architecture-1.html) 4 | 5 | ### BEM规范 6 | 7 | **使用‘block’、‘element’、‘modifier’来修饰元素** 8 | 9 | ```css 10 | .block { /* styles */ } 11 | .block__element { /* styles */ } 12 | .block--modifier { /* styles */ } 13 | ``` 14 | 15 | ### 一个例子 16 | 17 | ```html 18 |
19 |
20 | 21 | 22 |
23 |
24 | ``` 25 | 26 | ```css 27 | .button { 28 | padding: 10px 0; 29 | background-color: red; 30 | } 31 | .button--secondary { 32 | padding: 10px 0; 33 | background-color: blue; 34 | } 35 | ``` 36 | 37 | - **'.form'** 是一个块 -- **block** 块 38 | - **'.form__action'** 中的 **'__action'** 可以看出是 **'.form'** 的一个子元素 -- **element** 元素 39 | - **'.button--secondary'** 中的 **'--secondary'** 可以看出是 **'.button'** 的一个样式的修改 -- **modifier** 修饰符 40 | 41 | ### BEM做了什么 42 | 43 | - 让class的数量尽可能少 44 | - 用开发人员能够立即知道class的样式应用在了哪里,且做出修改不会影响其他内容 -------------------------------------------------------------------------------- /CSS/盒模型常见问题/readme.md: -------------------------------------------------------------------------------- 1 | # 盒模型常见问题 part 1 2 | 3 | ### margin-top 影响父元素样式 4 | 5 | - 原因 6 | 7 | > In this specification, the expression collapsing margins means that adjoining margins (no non-empty content, padding or border areas or clearance separate them) of two or more boxes (which may be next to one another or nested) combine to form a single margin. 所有毗邻的两个或更多盒元素的margin将会合并为一个margin共享之。毗邻的定义为:同级或者嵌套的盒元素,并且它们之间没有非空内容、Padding或Border分隔。 8 | 9 | - 解决办法 10 | - 父级或子元素使用浮动或者绝对定位absolute(浮动或绝对定位不参与)margin的折叠 11 | - 父级overflow:hidden; 12 | - 父级设置padding(破坏非空白的折叠条件) 13 | - 父级设置border 14 | - 参考 15 | - [子元素margin-top为何会影响父元素?--CSDN](https://blog.csdn.net/sinat_27088253/article/details/52954688) 16 | - [w3c - css2.1 盒模型规范](https://www.w3.org/TR/CSS21/box.html#collapsing-margins) -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 LucaLJX 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # jianshu_demo 2 | 简书上发表的文章的一些小demo。 3 | -------------------------------------------------------------------------------- /WEB类库插件使用案例/H5移动端调试神器 - vconsole.js/README.md: -------------------------------------------------------------------------------- 1 | ## H5移动端调试神器 - vconsole.js 2 | 3 | > vconsole.js git地址:[https://github.com/Tencent/vConsole](https://github.com/Tencent/vConsole) 4 | 5 | ### 引入方式 6 | 7 | vconsoleUtil.js 8 | 9 | ```javascript 10 | import VConsole from 'vconsole' 11 | var vConsole = new VConsole(); 12 | export default vConsole 13 | ``` 14 | 15 | ### 调用方式 16 | 17 | 以vue项目为例 18 | 19 | ```javascript 20 | // 在main.js中引入 21 | import './utils/vconsoleUtil.js' 22 | ``` 23 | 24 | ### 效果 25 | 26 | ![vconsole.js使用效果](./images/vconsole-demo.png) 27 | 28 | BY-Luca_LJX([git地址](https://github.com/LucaLJX/jianshu_demo/blob/master/WEB%E7%B1%BB%E5%BA%93%E6%8F%92%E4%BB%B6%E4%BD%BF%E7%94%A8%E6%A1%88%E4%BE%8B/H5%E7%A7%BB%E5%8A%A8%E7%AB%AF%E8%B0%83%E8%AF%95%E7%A5%9E%E5%99%A8%20-%20vconsole.js/images/vconsole-demo.png)) -------------------------------------------------------------------------------- /WEB类库插件使用案例/H5移动端调试神器 - vconsole.js/images/vconsole-demo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LucaLJX/jianshu_demo/9fa279e2cd47560a0d23ada47104e6aeee4c558d/WEB类库插件使用案例/H5移动端调试神器 - vconsole.js/images/vconsole-demo.png -------------------------------------------------------------------------------- /WEB类库插件使用案例/betterScroll.js封装下拉刷新、上拉加载组件踩坑/README.md: -------------------------------------------------------------------------------- 1 | ## betterScroll.js 封装下拉刷新、上拉加载组件(踩坑) 2 | 3 | ### betterScroll.js 原理 4 | 5 | > betterScroll.js 滚动原理。 -- [ betterScroll官网 ](https://better-scroll.github.io/docs/zh-CN/guide/#%E6%BB%9A%E5%8A%A8%E5%8E%9F%E7%90%86) 6 | 7 | ![示意图](./image/betterScroll.png) 8 | 9 | 此示例图展示了betterScroll.js的滚动原理,基本上理解这张图就可以解决此类库开发使用中碰到的绝大多数问题。 10 | 11 | ```html 12 | 13 |
14 |
15 |
    16 |
  • word word word word
  • 17 |
  • word word word word
  • 18 | ... 19 |
  • word word word word
  • 20 |
  • word word word word
  • 21 |
22 |
23 |
24 | ``` 25 | 26 | 如上示例代码,betterScroll.js只有当内容区域 - **‘content’** 部分的高度超过容器区域 - **‘wrapper’** 的高度,才会触发滚动,而只有触发betterScroll.js的滚动,才可以使用对用的下拉、上拉功能。 27 | 28 | ### betterScroll.js 封装类库中碰到的问题及解决方案 29 | 30 | #### 1.betterScroll初始化之后无法触发滚动的问题 31 | 32 | 此问题如上文 **原理** 中已经作出了解释,在开发的过程中,务必初始化 **‘wrapper’** 容器,且保证子内容 **‘content’** 的高度超过父级容器。 33 | 34 | - 若内容为空,如何触发滚动下拉刷新 35 | 36 | 实际开发中很多列表为空,此时 **‘content’** 无法通过其子内容自动撑开到超过父容器的程度,则此时需要利用css的计算属性对其高度进行赋值 37 | 38 | **设置为比父容器的 100% 多 1px 的高度即可** 39 | 40 | ```css 41 | .content { 42 | min-height: calc(100% + 1px); 43 | } 44 | ``` 45 | 46 | - **‘content’** 已设置为比父容器高1px的情况,还是无法触发下拉(滚动) 47 | 48 | 这种情况往往是因为草率地设置了父级 **‘wrapper’** 的高度为100%,而 **‘wrapper’** 的父级并没有设置高度从而其无法继承父级高度,需要子级撑开导致的 49 | 50 | **实际开发中建议手动利用css属性设置父级wrapper的高度,一般开发中都可以设置为全屏,即 100vh** 51 | 52 | ```css 53 | .wrapper { 54 | height: 100vh; 55 | } 56 | ``` 57 | 58 | ### 基于 betterScroll.js 设计封装下拉刷新、上拉加载组件的小tips 59 | 60 | ```html 61 | 62 |
63 | 64 |
65 | 下拉刷新。。。 66 | ... 67 |
68 |
69 |
    70 |
  • word word word word
  • 71 |
  • word word word word
  • 72 | ... 73 |
  • word word word word
  • 74 |
  • word word word word
  • 75 |
76 | 77 |
78 | 上拉加载。。。 79 | ... 80 |
81 |
82 |
83 | ``` 84 | 85 | #### 1. 文字提示 86 | 87 | - 建议将下拉刷新的文字提示与 **‘content’** 平级且绝对定位到顶部,设置层级低于 **‘content’** 88 | 89 | - 建议将上拉加载的文字提示至于内容的底部,切勿脱离文档流 90 | 91 | #### 2. 下拉刷新状态 92 | 93 | 建议计算手指下拉的距离,以用于提示:“释放刷新数据” 94 | 95 | 建议下拉刷新分为四个状态,如下: 96 | 97 | ```javascript 98 | before: '下拉刷新', // 手指下拉,放开前,超出触发刷新距离前 99 | pulling: '释放刷新数据', // 手指下拉,放开前,超出触发刷新距离后 100 | loading: '加载中...', // 手指放开,ajax请求数据完成前 101 | finished: '加载完成' // 手指放开,ajax请求数据完成后 102 | ``` 103 | 104 | #### 3. 暴露betterScroll.js 的refresh方法,以主动触发 105 | 106 | > betterScroll.js refresh()方法。 -- [ refresh() ](https://better-scroll.github.io/docs/zh-CN/guide/base-scroll-api.html#%E6%96%B9%E6%B3%95) 107 | 108 | > refresh(): 重新计算 BetterScroll,当 DOM 结构发生变化的时候务必要调用确保滚动的效果正常 109 | 110 | - 使用场景 111 | 112 | **非下拉加载触发页面的数据更新(dom更新),如果不调用refresh方法重新计算,则‘content’的高度会保持dom更新之前的状态,最终导致滚动错乱** 113 | 114 | 115 | ### 核心代码 116 | 117 | ```tsx 118 | import { Component, Prop, Vue } from 'vue-property-decorator' 119 | // bscroll 120 | import BScroll from '@better-scroll/core' 121 | // @ts-ignore 122 | import PullDown from '@better-scroll/pull-down' 123 | // @ts-ignore 124 | import Pullup from '@better-scroll/pull-up' 125 | import _ from 'lodash' 126 | import './index.scss' 127 | 128 | BScroll.use(PullDown) 129 | BScroll.use(Pullup) 130 | 131 | const TIME_BOUNCE = 500 132 | const TIME_STOP = 500 133 | const PULLDOWN_THRESHOLD = 50 134 | const PULLUP_THRESHOLD = 10 135 | const STOP = 50 136 | const PULLDOWN_TIPS = { 137 | before: '下拉刷新', 138 | pulling: '释放刷新数据', 139 | loading: '加载中...', 140 | finished: '加载完成' 141 | } 142 | const PULLUP_TIPS = { 143 | before: '上拉加载更多', 144 | loading: '加载中...' 145 | } 146 | 147 | export interface PullConfig { 148 | threshold: number // 配置顶部下拉的距离来决定刷新时机 149 | stop?: number // 回弹停留的距离 150 | } 151 | 152 | export interface CoordinateVo { 153 | x: number 154 | y: number 155 | } 156 | 157 | export interface TipsConfigVo { 158 | before: string 159 | pulling?: string 160 | loading: string 161 | finished?: string 162 | } 163 | 164 | @Component({ 165 | name: 'PullRefresh' 166 | }) 167 | export default class PullRefresh extends Vue { 168 | 169 | // 是否支持下拉刷新 170 | @Prop({ default: true }) 171 | private pullDown!: boolean 172 | 173 | // 是否支持上拉加载 174 | @Prop({ default: true }) 175 | private pullUp!: boolean 176 | 177 | // z-index 配置 178 | @Prop({ default: 100 }) 179 | private zIndex!: number 180 | 181 | // pullDown 组件配置 182 | @Prop({ default: () => { 183 | return { 184 | threshold: PULLDOWN_THRESHOLD, // 配置顶部下拉的距离来决定刷新时机 185 | stop: STOP // 回弹停留的距离 186 | } 187 | } }) 188 | private pullDownConfig!: PullConfig 189 | 190 | // pullUp 组件配置 191 | @Prop({ default: () => { 192 | return { 193 | threshold: PULLUP_THRESHOLD // 配置顶部下拉的距离来决定刷新时机 194 | } 195 | } }) 196 | private pullUpConfig!: PullConfig 197 | 198 | // 是否无更多数据 - 只有开启Pullup的情况下传 199 | @Prop({ default: false }) 200 | private noMore!: boolean 201 | 202 | @Prop({ default: '无更多数据' }) 203 | private noMoreTips!: string 204 | 205 | // 下拉、上拉操作结束后的停留时间 206 | @Prop({ default: TIME_STOP }) 207 | private stayTime!: number 208 | 209 | @Prop({ default: () => PULLDOWN_TIPS }) 210 | private pullDownTips!: TipsConfigVo 211 | 212 | @Prop({ default: () => PULLUP_TIPS }) 213 | private pullUpTips!: TipsConfigVo 214 | 215 | @Prop({ default: true }) 216 | private empty!: boolean 217 | 218 | // 状态 下拉之前 219 | private beforePullDown: boolean = true 220 | // 状态 下拉过程中 221 | private isPullingDown: boolean = false 222 | private bscroll: any = _.noop 223 | private pullDownY: number = 0 224 | // 状态 上拉之前 225 | private isPullUpLoad: boolean = false 226 | // 状态 上拉过程中 227 | private pullUpLoading: boolean = false 228 | 229 | private get tipsStyle () { 230 | return { 231 | 'z-index': this.zIndex 232 | } 233 | } 234 | 235 | private get contentStyle () { 236 | return { 237 | 'z-index': this.zIndex + 1, 238 | 'padding-bottom': this.noMore || this.pullUp ? '50px' : 0 239 | } 240 | } 241 | 242 | private get showNoMoreTips () { 243 | return this.noMore && this.pullUp && !this.empty 244 | } 245 | 246 | private get pullingDownOut () { 247 | return this.pullDownY > this.pullDownConfig.threshold 248 | } 249 | 250 | /** 251 | * 更新better scroll的dom 252 | */ 253 | public refreshDom () { 254 | return new Promise((resolve, reject) => { 255 | this.bscroll.refresh() 256 | resolve() 257 | }) 258 | } 259 | 260 | private mounted () { 261 | this.$nextTick(() => { 262 | this.initBscroll() 263 | }) 264 | } 265 | 266 | /** 267 | * 初始化 268 | */ 269 | private initBscroll () { 270 | const config = { 271 | scrollY: true, 272 | bounce: { 273 | top: this.pullDown ? true : false, 274 | bottom: this.pullUp ? true : false 275 | }, 276 | bounceTime: TIME_BOUNCE, 277 | pullDownRefresh: this.pullDown ? this.pullDownConfig : false, 278 | pullUpLoad: this.pullUp ? this.pullUpConfig : false 279 | } 280 | this.bscroll = new BScroll(this.$refs.PullRefreshWrapper as HTMLElement, config) 281 | if (this.pullDown) { 282 | this.bscroll.on('pullingDown', this.pullingDownHandler) 283 | } 284 | if (this.pullUp) { 285 | this.bscroll.on('pullingUp', this.pullingUpHandler) 286 | } 287 | this.bscroll.on('scroll', this.scrollHandler) 288 | } 289 | 290 | /** 291 | * 下拉滚动持续触发 292 | * @param pos 滚动过程中的坐标 293 | */ 294 | private scrollHandler (pos: CoordinateVo) { 295 | this.pullDownY = pos.y 296 | } 297 | 298 | /** 299 | * 下拉滚动结束触发 300 | */ 301 | private async pullingDownHandler () { 302 | this.beforePullDown = false 303 | this.isPullingDown = true 304 | // 触发刷新操作 305 | await this.refeashData() 306 | // 下拉过程结束,进入回弹状态 307 | this.isPullingDown = false 308 | this.finishPullDown() 309 | } 310 | 311 | /** 312 | * 下拉触发pullDown事件 313 | */ 314 | private async refeashData () { 315 | return new Promise((resolve, reject) => { 316 | this.$emit('pullDown', resolve) 317 | }) 318 | } 319 | 320 | /** 321 | * 上拉触发pullUp事件 322 | */ 323 | private getMore () { 324 | return new Promise((resolve, reject) => { 325 | this.$emit('pullUp', resolve) 326 | }) 327 | } 328 | 329 | /** 330 | * 下拉结束后调用的方法,用于处理停留时间等 331 | */ 332 | private async finishPullDown () { 333 | const stopTime = this.stayTime 334 | await new Promise(resolve => { 335 | setTimeout(() => { 336 | this.bscroll.finishPullDown() 337 | resolve() 338 | }, stopTime) 339 | }) 340 | setTimeout(() => { 341 | this.beforePullDown = true 342 | this.bscroll.refresh() 343 | }, TIME_BOUNCE) 344 | } 345 | 346 | /** 347 | * 上拉触发的事件 348 | */ 349 | private async pullingUpHandler () { 350 | if (this.noMore || this.empty) return false 351 | this.isPullUpLoad = true 352 | this.pullUpLoading = true 353 | await this.getMore() 354 | this.pullUpLoading = false 355 | this.bscroll.finishPullUp() 356 | this.bscroll.refresh() 357 | this.isPullUpLoad = false 358 | } 359 | 360 | /** 361 | * 上拉提示文字 362 | */ 363 | private upTips () { 364 | if (!this.pullUp || this.empty) return null 365 | const upTips = this.pullUpTips 366 | let tips = '' 367 | if (this.noMore) { 368 | tips = this.noMoreTips 369 | } else { 370 | tips = !this.isPullUpLoad && !this.pullUpLoading ? upTips.before : upTips.loading 371 | } 372 | return

{ tips }

373 | } 374 | 375 | /** 376 | * 下拉提示文字 377 | */ 378 | private downTips () { 379 | if (!this.pullDown) return null 380 | const downTipsConfig = this.pullDownTips 381 | let tips = '' 382 | if (this.beforePullDown && !this.isPullingDown) { 383 | tips = this.pullingDownOut ? downTipsConfig.pulling! : downTipsConfig.before 384 | } else { 385 | tips = this.isPullingDown ? downTipsConfig.loading : downTipsConfig.finished! 386 | } 387 | return

{ tips }

388 | } 389 | 390 | protected render () { 391 | return
392 |
393 |
394 |
395 | { 396 | this.$slots.default 397 | } 398 |
{ this.noMoreTips }
399 | { 400 | this.upTips() 401 | } 402 |
403 | { 404 | this.downTips() 405 | } 406 |
407 |
408 |
409 | } 410 | } 411 | ``` 412 | 413 | ```scss 414 | .PullRefreshComponent { 415 | height: 100%; 416 | 417 | .PullRefresh { 418 | height: 100%; 419 | 420 | &_wrapper { 421 | position: relative; 422 | height: 100%; 423 | overflow: hidden; 424 | } 425 | 426 | &_downTips { 427 | z-index: 100; 428 | position: absolute; 429 | height: 50px; 430 | line-height: 50px; 431 | top: 0; 432 | width: 100%; 433 | text-align: center; 434 | font-size: 12px; 435 | color: #666; 436 | } 437 | 438 | &_upTips { 439 | height: 50px; 440 | line-height: 50px; 441 | width: 100%; 442 | position: absolute; 443 | bottom: 0; 444 | text-align: center; 445 | font-size: 12px; 446 | color: #666; 447 | } 448 | 449 | &_content { 450 | background-color: #fff; 451 | position: relative; 452 | z-index: 101; 453 | min-height: calc(100% + 1px); 454 | } 455 | 456 | &_noMore { 457 | position: absolute; 458 | height: 50px; 459 | line-height: 50px; 460 | bottom: 0; 461 | text-align: center; 462 | width: 100%; 463 | } 464 | } 465 | } 466 | 467 | ``` 468 | 469 | BY--LucaLJX ([LucaLJX的github](https://github.com/LucaLJX/jianshu_demo)) 470 | -------------------------------------------------------------------------------- /WEB类库插件使用案例/betterScroll.js封装下拉刷新、上拉加载组件踩坑/image/betterScroll.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LucaLJX/jianshu_demo/9fa279e2cd47560a0d23ada47104e6aeee4c558d/WEB类库插件使用案例/betterScroll.js封装下拉刷新、上拉加载组件踩坑/image/betterScroll.png -------------------------------------------------------------------------------- /WEB类库插件使用案例/betterScroll.js封装下拉刷新、上拉加载组件踩坑/package/index.scss: -------------------------------------------------------------------------------- 1 | .PullRefreshComponent { 2 | height: 100%; 3 | 4 | .PullRefresh { 5 | height: 100%; 6 | 7 | &_wrapper { 8 | position: relative; 9 | height: 100%; 10 | overflow: hidden; 11 | } 12 | 13 | &_downTips { 14 | z-index: 100; 15 | position: absolute; 16 | height: 50px; 17 | line-height: 50px; 18 | top: 0; 19 | width: 100%; 20 | text-align: center; 21 | font-size: 12px; 22 | color: #666; 23 | } 24 | 25 | &_upTips { 26 | height: 50px; 27 | line-height: 50px; 28 | width: 100%; 29 | position: absolute; 30 | bottom: 0; 31 | text-align: center; 32 | font-size: 12px; 33 | color: #666; 34 | } 35 | 36 | &_content { 37 | background-color: #fff; 38 | position: relative; 39 | z-index: 101; 40 | min-height: calc(100% + 1px); 41 | } 42 | 43 | &_noMore { 44 | position: absolute; 45 | height: 50px; 46 | line-height: 50px; 47 | bottom: 0; 48 | text-align: center; 49 | width: 100%; 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /WEB类库插件使用案例/betterScroll.js封装下拉刷新、上拉加载组件踩坑/package/index.tsx: -------------------------------------------------------------------------------- 1 | import { Component, Prop, Vue } from 'vue-property-decorator' 2 | // bscroll 3 | import BScroll from '@better-scroll/core' 4 | // @ts-ignore 5 | import PullDown from '@better-scroll/pull-down' 6 | // @ts-ignore 7 | import Pullup from '@better-scroll/pull-up' 8 | import _ from 'lodash' 9 | import './index.scss' 10 | 11 | BScroll.use(PullDown) 12 | BScroll.use(Pullup) 13 | 14 | const TIME_BOUNCE = 500 15 | const TIME_STOP = 500 16 | const PULLDOWN_THRESHOLD = 50 17 | const PULLUP_THRESHOLD = 10 18 | const STOP = 50 19 | const PULLDOWN_TIPS = { 20 | before: '下拉刷新', 21 | pulling: '释放刷新数据', 22 | loading: '加载中...', 23 | finished: '加载完成' 24 | } 25 | const PULLUP_TIPS = { 26 | before: '上拉加载更多', 27 | loading: '加载中...' 28 | } 29 | 30 | export interface PullConfig { 31 | threshold: number // 配置顶部下拉的距离来决定刷新时机 32 | stop?: number // 回弹停留的距离 33 | } 34 | 35 | export interface CoordinateVo { 36 | x: number 37 | y: number 38 | } 39 | 40 | export interface TipsConfigVo { 41 | before: string 42 | pulling?: string 43 | loading: string 44 | finished?: string 45 | } 46 | 47 | @Component({ 48 | name: 'PullRefresh' 49 | }) 50 | export default class PullRefresh extends Vue { 51 | 52 | // 是否支持下拉刷新 53 | @Prop({ default: true }) 54 | private pullDown!: boolean 55 | 56 | // 是否支持上拉加载 57 | @Prop({ default: true }) 58 | private pullUp!: boolean 59 | 60 | // z-index 配置 61 | @Prop({ default: 100 }) 62 | private zIndex!: number 63 | 64 | // pullDown 组件配置 65 | @Prop({ default: () => { 66 | return { 67 | threshold: PULLDOWN_THRESHOLD, // 配置顶部下拉的距离来决定刷新时机 68 | stop: STOP // 回弹停留的距离 69 | } 70 | } }) 71 | private pullDownConfig!: PullConfig 72 | 73 | // pullUp 组件配置 74 | @Prop({ default: () => { 75 | return { 76 | threshold: PULLUP_THRESHOLD // 配置顶部下拉的距离来决定刷新时机 77 | } 78 | } }) 79 | private pullUpConfig!: PullConfig 80 | 81 | // 是否无更多数据 - 只有开启Pullup的情况下传 82 | @Prop({ default: false }) 83 | private noMore!: boolean 84 | 85 | @Prop({ default: '无更多数据' }) 86 | private noMoreTips!: string 87 | 88 | // 下拉、上拉操作结束后的停留时间 89 | @Prop({ default: TIME_STOP }) 90 | private stayTime!: number 91 | 92 | @Prop({ default: () => PULLDOWN_TIPS }) 93 | private pullDownTips!: TipsConfigVo 94 | 95 | @Prop({ default: () => PULLUP_TIPS }) 96 | private pullUpTips!: TipsConfigVo 97 | 98 | @Prop({ default: true }) 99 | private empty!: boolean 100 | 101 | // 状态 下拉之前 102 | private beforePullDown: boolean = true 103 | // 状态 下拉过程中 104 | private isPullingDown: boolean = false 105 | private bscroll: any = _.noop 106 | private pullDownY: number = 0 107 | // 状态 上拉之前 108 | private isPullUpLoad: boolean = false 109 | // 状态 上拉过程中 110 | private pullUpLoading: boolean = false 111 | 112 | private get tipsStyle () { 113 | return { 114 | 'z-index': this.zIndex 115 | } 116 | } 117 | 118 | private get contentStyle () { 119 | return { 120 | 'z-index': this.zIndex + 1, 121 | 'padding-bottom': this.noMore || this.pullUp ? '50px' : 0 122 | } 123 | } 124 | 125 | private get showNoMoreTips () { 126 | return this.noMore && this.pullUp && !this.empty 127 | } 128 | 129 | private get pullingDownOut () { 130 | return this.pullDownY > this.pullDownConfig.threshold 131 | } 132 | 133 | /** 134 | * 更新better scroll的dom 135 | */ 136 | public refreshDom () { 137 | return new Promise((resolve, reject) => { 138 | this.bscroll.refresh() 139 | resolve() 140 | }) 141 | } 142 | 143 | private mounted () { 144 | this.$nextTick(() => { 145 | this.initBscroll() 146 | }) 147 | } 148 | 149 | /** 150 | * 初始化 151 | */ 152 | private initBscroll () { 153 | const config = { 154 | scrollY: true, 155 | bounce: { 156 | top: this.pullDown ? true : false, 157 | bottom: this.pullUp ? true : false 158 | }, 159 | bounceTime: TIME_BOUNCE, 160 | pullDownRefresh: this.pullDown ? this.pullDownConfig : false, 161 | pullUpLoad: this.pullUp ? this.pullUpConfig : false 162 | } 163 | this.bscroll = new BScroll(this.$refs.PullRefreshWrapper as HTMLElement, config) 164 | if (this.pullDown) { 165 | this.bscroll.on('pullingDown', this.pullingDownHandler) 166 | } 167 | if (this.pullUp) { 168 | this.bscroll.on('pullingUp', this.pullingUpHandler) 169 | } 170 | this.bscroll.on('scroll', this.scrollHandler) 171 | } 172 | 173 | /** 174 | * 下拉滚动持续触发 175 | * @param pos 滚动过程中的坐标 176 | */ 177 | private scrollHandler (pos: CoordinateVo) { 178 | this.pullDownY = pos.y 179 | } 180 | 181 | /** 182 | * 下拉滚动结束触发 183 | */ 184 | private async pullingDownHandler () { 185 | this.beforePullDown = false 186 | this.isPullingDown = true 187 | // 触发刷新操作 188 | await this.refeashData() 189 | // 下拉过程结束,进入回弹状态 190 | this.isPullingDown = false 191 | this.finishPullDown() 192 | } 193 | 194 | /** 195 | * 下拉触发pullDown事件 196 | */ 197 | private async refeashData () { 198 | return new Promise((resolve, reject) => { 199 | this.$emit('pullDown', resolve) 200 | }) 201 | } 202 | 203 | /** 204 | * 上拉触发pullUp事件 205 | */ 206 | private getMore () { 207 | return new Promise((resolve, reject) => { 208 | this.$emit('pullUp', resolve) 209 | }) 210 | } 211 | 212 | /** 213 | * 下拉结束后调用的方法,用于处理停留时间等 214 | */ 215 | private async finishPullDown () { 216 | const stopTime = this.stayTime 217 | await new Promise(resolve => { 218 | setTimeout(() => { 219 | this.bscroll.finishPullDown() 220 | resolve() 221 | }, stopTime) 222 | }) 223 | setTimeout(() => { 224 | this.beforePullDown = true 225 | this.bscroll.refresh() 226 | }, TIME_BOUNCE) 227 | } 228 | 229 | /** 230 | * 上拉触发的事件 231 | */ 232 | private async pullingUpHandler () { 233 | if (this.noMore || this.empty) return false 234 | this.isPullUpLoad = true 235 | this.pullUpLoading = true 236 | await this.getMore() 237 | this.pullUpLoading = false 238 | this.bscroll.finishPullUp() 239 | this.bscroll.refresh() 240 | this.isPullUpLoad = false 241 | } 242 | 243 | /** 244 | * 上拉提示文字 245 | */ 246 | private upTips () { 247 | if (!this.pullUp || this.empty) return null 248 | const upTips = this.pullUpTips 249 | let tips = '' 250 | if (this.noMore) { 251 | tips = this.noMoreTips 252 | } else { 253 | tips = !this.isPullUpLoad && !this.pullUpLoading ? upTips.before : upTips.loading 254 | } 255 | return

{ tips }

256 | } 257 | 258 | /** 259 | * 下拉提示文字 260 | */ 261 | private downTips () { 262 | if (!this.pullDown) return null 263 | const downTipsConfig = this.pullDownTips 264 | let tips = '' 265 | if (this.beforePullDown && !this.isPullingDown) { 266 | tips = this.pullingDownOut ? downTipsConfig.pulling! : downTipsConfig.before 267 | } else { 268 | tips = this.isPullingDown ? downTipsConfig.loading : downTipsConfig.finished! 269 | } 270 | return

{ tips }

271 | } 272 | 273 | protected render () { 274 | return
275 |
276 |
277 |
278 | { 279 | this.$slots.default 280 | } 281 |
{ this.noMoreTips }
282 | { 283 | this.upTips() 284 | } 285 |
286 | { 287 | this.downTips() 288 | } 289 |
290 |
291 |
292 | } 293 | } 294 | -------------------------------------------------------------------------------- /WEB类库插件使用案例/qrcode.js生成二维码并提供下载(案例)/README.md: -------------------------------------------------------------------------------- 1 | 2 | ## qrcode.js使用js生成二维码(并下载)的实践 3 | 4 | ### 知识点 5 | 6 | - base64数据形式 7 | 8 | > Base64是一种用64个字符来表示任意二进制数据的方法。 -- [ 廖雪峰官方网站 ](https://www.liaoxuefeng.com/wiki/001374738125095c955c1e6d8bb493182103fac9270762a000/001399413803339f4bbda5c01fc479cbea98b1387390748000) 9 | 10 | > Base64编码有哪些情景需求? -- [ 知乎 ](https://www.zhihu.com/question/36306744/answer/71626823) 11 | 12 | 13 | ### 案例场景 14 | 15 | - 后台生成邀请链接,前端拿到链接之后处理成二维码图片并下载到本地,用于转发。 16 | 17 | ### qrcode.js及jquery-qrcode.js实现生成二维码 18 | 19 | - qrcode.js github地址: [ qrcode ](https://github.com/davidshimjs/qrcodejs) 20 | - jquery-qrcode.js github地址: [ jquery-qrcode](https://github.com/jeromeetienne/jquery-qrcode) 21 | 22 | #### 1.html 代码 23 | 24 | ```html 25 |
26 | 27 | ``` 28 | 29 | #### 2.js 代码 30 | ```javascript 31 | // qrcode.js 32 | var qrcode = new QRCode(document.getElementById("qrcode"), { 33 | text: "i am the first qrcode", 34 | width: 128, //生成的二维码的宽度 35 | height: 128, //生成的二维码的高度 36 | colorDark : "#000000", // 生成的二维码的深色部分 37 | colorLight : "#ffffff", //生成二维码的浅色部分 38 | correctLevel : QRCode.CorrectLevel.H 39 | }); 40 | //jquery-qrcode.js 41 | var qrcode = $('#qrcode').qrcode({ 42 | width: 128, 43 | height: 128, 44 | text: "size doesn't matter" 45 | }); 46 | ``` 47 | 48 | #### 3.qrcode.js 与jquery-qrcode.js区别 49 | 50 | - qrcode.js可以指定生成的二维码深色、浅色区域块的颜色,jquery-qrcode.js只能默认黑白两色 51 | - qrcode.js支持clear()和makeCode()方法,用于便捷清空二维码、更换二维码,jquery-qrcode.js不支持 52 | 53 | #### 4.qrcode.js生成二维码原理 54 | 55 | ![示意图](./img-1.jpg) 56 | 57 | - qrcode.js在用于生成二维码的容器#qrcode内生成一个canvas标签和一个img标签,并且以base64的编码格式描述图片信息。 58 | 59 | ### 点击按钮,生成的二维码图片保存到本地 60 | 61 | #### 实现原理 62 | 63 | - 将base64编码格式的图片转换成canvas画布 64 | - 利用toDataUrl()方法将canvas画布信息转化为可供下载的url信息 [ toDataUrl() ](https://developer.mozilla.org/zh-CN/docs/Web/API/HTMLCanvasElement/toDataURL) 65 | - 构建下载链接并模拟点击,将图片下载到本机 66 | 67 | #### 基于qrcode.js案例demo实现将生成的二维码保存为本地图片 68 | 69 | - 构建一个用于下载的空的a标签 70 | 71 | ```html 72 | 73 | 74 | ``` 75 | 76 | - 将base64图片构建成画布并模拟点击a标签下载 77 | 78 | ```javascript 79 | //js 80 | function downloadClick () { 81 | // 获取base64的图片节点 82 | var img = document.getElementById('qrcode').getElementsByTagName('img')[0]; 83 | // 构建画布 84 | var canvas = document.createElement('canvas'); 85 | canvas.width = img.width; 86 | canvas.height = img.height; 87 | canvas.getContext('2d').drawImage(img, 0, 0); 88 | // 构造url 89 | url = canvas.toDataURL('image/png'); 90 | // 构造a标签并模拟点击 91 | var downloadLink = document.getElementById('downloadLink'); 92 | downloadLink.setAttribute('href', url); 93 | downloadLink.setAttribute('download', '二维码.png'); 94 | downloadLink.click(); 95 | } 96 | //jquery 97 | function downloadClick () { 98 | // 获取base64的图片节点 99 | var img = $('#qrcode img').[0]; 100 | // 构建画布 101 | var canvas = document.createElement('canvas'); 102 | canvas.width = img.width; 103 | canvas.height = img.height; 104 | canvas.getContext('2d').drawImage(img, 0, 0); 105 | // 构造url 106 | url = canvas.toDataURL('image/png'); 107 | // 构造a标签并模拟点击 108 | var downloadLink = $('#downloadLink').attr("href", url).attr("download", "二维码.png"); 109 | downloadLink[0].click(); 110 | } 111 | ``` 112 | 113 | 114 | -------------------------------------------------------------------------------- /WEB类库插件使用案例/qrcode.js生成二维码并提供下载(案例)/demo/demo_qrcode.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Document 8 | 9 | 10 | 17 | 18 |
19 | 20 | 21 | 22 | 23 | 24 | 56 | 57 | -------------------------------------------------------------------------------- /WEB类库插件使用案例/qrcode.js生成二维码并提供下载(案例)/demo/demo_qrcode_jquery.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Document 8 | 9 | 10 | 11 | 18 | 19 |
20 | 27 | 28 | -------------------------------------------------------------------------------- /WEB类库插件使用案例/qrcode.js生成二维码并提供下载(案例)/demo/js/qrcode/qrcode.min.js: -------------------------------------------------------------------------------- 1 | var QRCode;!function(){function a(a){this.mode=c.MODE_8BIT_BYTE,this.data=a,this.parsedData=[];for(var b=[],d=0,e=this.data.length;e>d;d++){var f=this.data.charCodeAt(d);f>65536?(b[0]=240|(1835008&f)>>>18,b[1]=128|(258048&f)>>>12,b[2]=128|(4032&f)>>>6,b[3]=128|63&f):f>2048?(b[0]=224|(61440&f)>>>12,b[1]=128|(4032&f)>>>6,b[2]=128|63&f):f>128?(b[0]=192|(1984&f)>>>6,b[1]=128|63&f):b[0]=f,this.parsedData=this.parsedData.concat(b)}this.parsedData.length!=this.data.length&&(this.parsedData.unshift(191),this.parsedData.unshift(187),this.parsedData.unshift(239))}function b(a,b){this.typeNumber=a,this.errorCorrectLevel=b,this.modules=null,this.moduleCount=0,this.dataCache=null,this.dataList=[]}function i(a,b){if(void 0==a.length)throw new Error(a.length+"/"+b);for(var c=0;c=f;f++){var h=0;switch(b){case d.L:h=l[f][0];break;case d.M:h=l[f][1];break;case d.Q:h=l[f][2];break;case d.H:h=l[f][3]}if(h>=e)break;c++}if(c>l.length)throw new Error("Too long data");return c}function s(a){var b=encodeURI(a).toString().replace(/\%[0-9a-fA-F]{2}/g,"a");return b.length+(b.length!=a?3:0)}a.prototype={getLength:function(){return this.parsedData.length},write:function(a){for(var b=0,c=this.parsedData.length;c>b;b++)a.put(this.parsedData[b],8)}},b.prototype={addData:function(b){var c=new a(b);this.dataList.push(c),this.dataCache=null},isDark:function(a,b){if(0>a||this.moduleCount<=a||0>b||this.moduleCount<=b)throw new Error(a+","+b);return this.modules[a][b]},getModuleCount:function(){return this.moduleCount},make:function(){this.makeImpl(!1,this.getBestMaskPattern())},makeImpl:function(a,c){this.moduleCount=4*this.typeNumber+17,this.modules=new Array(this.moduleCount);for(var d=0;d=7&&this.setupTypeNumber(a),null==this.dataCache&&(this.dataCache=b.createData(this.typeNumber,this.errorCorrectLevel,this.dataList)),this.mapData(this.dataCache,c)},setupPositionProbePattern:function(a,b){for(var c=-1;7>=c;c++)if(!(-1>=a+c||this.moduleCount<=a+c))for(var d=-1;7>=d;d++)-1>=b+d||this.moduleCount<=b+d||(this.modules[a+c][b+d]=c>=0&&6>=c&&(0==d||6==d)||d>=0&&6>=d&&(0==c||6==c)||c>=2&&4>=c&&d>=2&&4>=d?!0:!1)},getBestMaskPattern:function(){for(var a=0,b=0,c=0;8>c;c++){this.makeImpl(!0,c);var d=f.getLostPoint(this);(0==c||a>d)&&(a=d,b=c)}return b},createMovieClip:function(a,b,c){var d=a.createEmptyMovieClip(b,c),e=1;this.make();for(var f=0;f=g;g++)for(var h=-2;2>=h;h++)this.modules[d+g][e+h]=-2==g||2==g||-2==h||2==h||0==g&&0==h?!0:!1}},setupTypeNumber:function(a){for(var b=f.getBCHTypeNumber(this.typeNumber),c=0;18>c;c++){var d=!a&&1==(1&b>>c);this.modules[Math.floor(c/3)][c%3+this.moduleCount-8-3]=d}for(var c=0;18>c;c++){var d=!a&&1==(1&b>>c);this.modules[c%3+this.moduleCount-8-3][Math.floor(c/3)]=d}},setupTypeInfo:function(a,b){for(var c=this.errorCorrectLevel<<3|b,d=f.getBCHTypeInfo(c),e=0;15>e;e++){var g=!a&&1==(1&d>>e);6>e?this.modules[e][8]=g:8>e?this.modules[e+1][8]=g:this.modules[this.moduleCount-15+e][8]=g}for(var e=0;15>e;e++){var g=!a&&1==(1&d>>e);8>e?this.modules[8][this.moduleCount-e-1]=g:9>e?this.modules[8][15-e-1+1]=g:this.modules[8][15-e-1]=g}this.modules[this.moduleCount-8][8]=!a},mapData:function(a,b){for(var c=-1,d=this.moduleCount-1,e=7,g=0,h=this.moduleCount-1;h>0;h-=2)for(6==h&&h--;;){for(var i=0;2>i;i++)if(null==this.modules[d][h-i]){var j=!1;g>>e));var k=f.getMask(b,d,h-i);k&&(j=!j),this.modules[d][h-i]=j,e--,-1==e&&(g++,e=7)}if(d+=c,0>d||this.moduleCount<=d){d-=c,c=-c;break}}}},b.PAD0=236,b.PAD1=17,b.createData=function(a,c,d){for(var e=j.getRSBlocks(a,c),g=new k,h=0;h8*l)throw new Error("code length overflow. ("+g.getLengthInBits()+">"+8*l+")");for(g.getLengthInBits()+4<=8*l&&g.put(0,4);0!=g.getLengthInBits()%8;)g.putBit(!1);for(;;){if(g.getLengthInBits()>=8*l)break;if(g.put(b.PAD0,8),g.getLengthInBits()>=8*l)break;g.put(b.PAD1,8)}return b.createBytes(g,e)},b.createBytes=function(a,b){for(var c=0,d=0,e=0,g=new Array(b.length),h=new Array(b.length),j=0;j=0?p.get(q):0}}for(var r=0,m=0;mm;m++)for(var j=0;jm;m++)for(var j=0;j=0;)b^=f.G15<=0;)b^=f.G18<>>=1;return b},getPatternPosition:function(a){return f.PATTERN_POSITION_TABLE[a-1]},getMask:function(a,b,c){switch(a){case e.PATTERN000:return 0==(b+c)%2;case e.PATTERN001:return 0==b%2;case e.PATTERN010:return 0==c%3;case e.PATTERN011:return 0==(b+c)%3;case e.PATTERN100:return 0==(Math.floor(b/2)+Math.floor(c/3))%2;case e.PATTERN101:return 0==b*c%2+b*c%3;case e.PATTERN110:return 0==(b*c%2+b*c%3)%2;case e.PATTERN111:return 0==(b*c%3+(b+c)%2)%2;default:throw new Error("bad maskPattern:"+a)}},getErrorCorrectPolynomial:function(a){for(var b=new i([1],0),c=0;a>c;c++)b=b.multiply(new i([1,g.gexp(c)],0));return b},getLengthInBits:function(a,b){if(b>=1&&10>b)switch(a){case c.MODE_NUMBER:return 10;case c.MODE_ALPHA_NUM:return 9;case c.MODE_8BIT_BYTE:return 8;case c.MODE_KANJI:return 8;default:throw new Error("mode:"+a)}else if(27>b)switch(a){case c.MODE_NUMBER:return 12;case c.MODE_ALPHA_NUM:return 11;case c.MODE_8BIT_BYTE:return 16;case c.MODE_KANJI:return 10;default:throw new Error("mode:"+a)}else{if(!(41>b))throw new Error("type:"+b);switch(a){case c.MODE_NUMBER:return 14;case c.MODE_ALPHA_NUM:return 13;case c.MODE_8BIT_BYTE:return 16;case c.MODE_KANJI:return 12;default:throw new Error("mode:"+a)}}},getLostPoint:function(a){for(var b=a.getModuleCount(),c=0,d=0;b>d;d++)for(var e=0;b>e;e++){for(var f=0,g=a.isDark(d,e),h=-1;1>=h;h++)if(!(0>d+h||d+h>=b))for(var i=-1;1>=i;i++)0>e+i||e+i>=b||(0!=h||0!=i)&&g==a.isDark(d+h,e+i)&&f++;f>5&&(c+=3+f-5)}for(var d=0;b-1>d;d++)for(var e=0;b-1>e;e++){var j=0;a.isDark(d,e)&&j++,a.isDark(d+1,e)&&j++,a.isDark(d,e+1)&&j++,a.isDark(d+1,e+1)&&j++,(0==j||4==j)&&(c+=3)}for(var d=0;b>d;d++)for(var e=0;b-6>e;e++)a.isDark(d,e)&&!a.isDark(d,e+1)&&a.isDark(d,e+2)&&a.isDark(d,e+3)&&a.isDark(d,e+4)&&!a.isDark(d,e+5)&&a.isDark(d,e+6)&&(c+=40);for(var e=0;b>e;e++)for(var d=0;b-6>d;d++)a.isDark(d,e)&&!a.isDark(d+1,e)&&a.isDark(d+2,e)&&a.isDark(d+3,e)&&a.isDark(d+4,e)&&!a.isDark(d+5,e)&&a.isDark(d+6,e)&&(c+=40);for(var k=0,e=0;b>e;e++)for(var d=0;b>d;d++)a.isDark(d,e)&&k++;var l=Math.abs(100*k/b/b-50)/5;return c+=10*l}},g={glog:function(a){if(1>a)throw new Error("glog("+a+")");return g.LOG_TABLE[a]},gexp:function(a){for(;0>a;)a+=255;for(;a>=256;)a-=255;return g.EXP_TABLE[a]},EXP_TABLE:new Array(256),LOG_TABLE:new Array(256)},h=0;8>h;h++)g.EXP_TABLE[h]=1<h;h++)g.EXP_TABLE[h]=g.EXP_TABLE[h-4]^g.EXP_TABLE[h-5]^g.EXP_TABLE[h-6]^g.EXP_TABLE[h-8];for(var h=0;255>h;h++)g.LOG_TABLE[g.EXP_TABLE[h]]=h;i.prototype={get:function(a){return this.num[a]},getLength:function(){return this.num.length},multiply:function(a){for(var b=new Array(this.getLength()+a.getLength()-1),c=0;cf;f++)for(var g=c[3*f+0],h=c[3*f+1],i=c[3*f+2],k=0;g>k;k++)e.push(new j(h,i));return e},j.getRsBlockTable=function(a,b){switch(b){case d.L:return j.RS_BLOCK_TABLE[4*(a-1)+0];case d.M:return j.RS_BLOCK_TABLE[4*(a-1)+1];case d.Q:return j.RS_BLOCK_TABLE[4*(a-1)+2];case d.H:return j.RS_BLOCK_TABLE[4*(a-1)+3];default:return void 0}},k.prototype={get:function(a){var b=Math.floor(a/8);return 1==(1&this.buffer[b]>>>7-a%8)},put:function(a,b){for(var c=0;b>c;c++)this.putBit(1==(1&a>>>b-c-1))},getLengthInBits:function(){return this.length},putBit:function(a){var b=Math.floor(this.length/8);this.buffer.length<=b&&this.buffer.push(0),a&&(this.buffer[b]|=128>>>this.length%8),this.length++}};var l=[[17,14,11,7],[32,26,20,14],[53,42,32,24],[78,62,46,34],[106,84,60,44],[134,106,74,58],[154,122,86,64],[192,152,108,84],[230,180,130,98],[271,213,151,119],[321,251,177,137],[367,287,203,155],[425,331,241,177],[458,362,258,194],[520,412,292,220],[586,450,322,250],[644,504,364,280],[718,560,394,310],[792,624,442,338],[858,666,482,382],[929,711,509,403],[1003,779,565,439],[1091,857,611,461],[1171,911,661,511],[1273,997,715,535],[1367,1059,751,593],[1465,1125,805,625],[1528,1190,868,658],[1628,1264,908,698],[1732,1370,982,742],[1840,1452,1030,790],[1952,1538,1112,842],[2068,1628,1168,898],[2188,1722,1228,958],[2303,1809,1283,983],[2431,1911,1351,1051],[2563,1989,1423,1093],[2699,2099,1499,1139],[2809,2213,1579,1219],[2953,2331,1663,1273]],o=function(){var a=function(a,b){this._el=a,this._htOption=b};return a.prototype.draw=function(a){function g(a,b){var c=document.createElementNS("http://www.w3.org/2000/svg",a);for(var d in b)b.hasOwnProperty(d)&&c.setAttribute(d,b[d]);return c}var b=this._htOption,c=this._el,d=a.getModuleCount();Math.floor(b.width/d),Math.floor(b.height/d),this.clear();var h=g("svg",{viewBox:"0 0 "+String(d)+" "+String(d),width:"100%",height:"100%",fill:b.colorLight});h.setAttributeNS("http://www.w3.org/2000/xmlns/","xmlns:xlink","http://www.w3.org/1999/xlink"),c.appendChild(h),h.appendChild(g("rect",{fill:b.colorDark,width:"1",height:"1",id:"template"}));for(var i=0;d>i;i++)for(var j=0;d>j;j++)if(a.isDark(i,j)){var k=g("use",{x:String(i),y:String(j)});k.setAttributeNS("http://www.w3.org/1999/xlink","href","#template"),h.appendChild(k)}},a.prototype.clear=function(){for(;this._el.hasChildNodes();)this._el.removeChild(this._el.lastChild)},a}(),p="svg"===document.documentElement.tagName.toLowerCase(),q=p?o:m()?function(){function a(){this._elImage.src=this._elCanvas.toDataURL("image/png"),this._elImage.style.display="block",this._elCanvas.style.display="none"}function d(a,b){var c=this;if(c._fFail=b,c._fSuccess=a,null===c._bSupportDataURI){var d=document.createElement("img"),e=function(){c._bSupportDataURI=!1,c._fFail&&_fFail.call(c)},f=function(){c._bSupportDataURI=!0,c._fSuccess&&c._fSuccess.call(c)};return d.onabort=e,d.onerror=e,d.onload=f,d.src="data:image/gif;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg==",void 0}c._bSupportDataURI===!0&&c._fSuccess?c._fSuccess.call(c):c._bSupportDataURI===!1&&c._fFail&&c._fFail.call(c)}if(this._android&&this._android<=2.1){var b=1/window.devicePixelRatio,c=CanvasRenderingContext2D.prototype.drawImage;CanvasRenderingContext2D.prototype.drawImage=function(a,d,e,f,g,h,i,j){if("nodeName"in a&&/img/i.test(a.nodeName))for(var l=arguments.length-1;l>=1;l--)arguments[l]=arguments[l]*b;else"undefined"==typeof j&&(arguments[1]*=b,arguments[2]*=b,arguments[3]*=b,arguments[4]*=b);c.apply(this,arguments)}}var e=function(a,b){this._bIsPainted=!1,this._android=n(),this._htOption=b,this._elCanvas=document.createElement("canvas"),this._elCanvas.width=b.width,this._elCanvas.height=b.height,a.appendChild(this._elCanvas),this._el=a,this._oContext=this._elCanvas.getContext("2d"),this._bIsPainted=!1,this._elImage=document.createElement("img"),this._elImage.style.display="none",this._el.appendChild(this._elImage),this._bSupportDataURI=null};return e.prototype.draw=function(a){var b=this._elImage,c=this._oContext,d=this._htOption,e=a.getModuleCount(),f=d.width/e,g=d.height/e,h=Math.round(f),i=Math.round(g);b.style.display="none",this.clear();for(var j=0;e>j;j++)for(var k=0;e>k;k++){var l=a.isDark(j,k),m=k*f,n=j*g;c.strokeStyle=l?d.colorDark:d.colorLight,c.lineWidth=1,c.fillStyle=l?d.colorDark:d.colorLight,c.fillRect(m,n,f,g),c.strokeRect(Math.floor(m)+.5,Math.floor(n)+.5,h,i),c.strokeRect(Math.ceil(m)-.5,Math.ceil(n)-.5,h,i)}this._bIsPainted=!0},e.prototype.makeImage=function(){this._bIsPainted&&d.call(this,a)},e.prototype.isPainted=function(){return this._bIsPainted},e.prototype.clear=function(){this._oContext.clearRect(0,0,this._elCanvas.width,this._elCanvas.height),this._bIsPainted=!1},e.prototype.round=function(a){return a?Math.floor(1e3*a)/1e3:a},e}():function(){var a=function(a,b){this._el=a,this._htOption=b};return a.prototype.draw=function(a){for(var b=this._htOption,c=this._el,d=a.getModuleCount(),e=Math.floor(b.width/d),f=Math.floor(b.height/d),g=[''],h=0;d>h;h++){g.push("");for(var i=0;d>i;i++)g.push('');g.push("")}g.push("
"),c.innerHTML=g.join("");var j=c.childNodes[0],k=(b.width-j.offsetWidth)/2,l=(b.height-j.offsetHeight)/2;k>0&&l>0&&(j.style.margin=l+"px "+k+"px")},a.prototype.clear=function(){this._el.innerHTML=""},a}();QRCode=function(a,b){if(this._htOption={width:256,height:256,typeNumber:4,colorDark:"#000000",colorLight:"#ffffff",correctLevel:d.H},"string"==typeof b&&(b={text:b}),b)for(var c in b)this._htOption[c]=b[c];"string"==typeof a&&(a=document.getElementById(a)),this._android=n(),this._el=a,this._oQRCode=null,this._oDrawing=new q(this._el,this._htOption),this._htOption.text&&this.makeCode(this._htOption.text)},QRCode.prototype.makeCode=function(a){this._oQRCode=new b(r(a,this._htOption.correctLevel),this._htOption.correctLevel),this._oQRCode.addData(a),this._oQRCode.make(),this._el.title=a,this._oDrawing.draw(this._oQRCode),this.makeImage()},QRCode.prototype.makeImage=function(){"function"==typeof this._oDrawing.makeImage&&(!this._android||this._android>=3)&&this._oDrawing.makeImage()},QRCode.prototype.clear=function(){this._oDrawing.clear()},QRCode.CorrectLevel=d}(); -------------------------------------------------------------------------------- /WEB类库插件使用案例/qrcode.js生成二维码并提供下载(案例)/demo/js/qrcode_jquery/jquery.qrcode.min.js: -------------------------------------------------------------------------------- 1 | (function(r){r.fn.qrcode=function(h){var s;function u(a){this.mode=s;this.data=a}function o(a,c){this.typeNumber=a;this.errorCorrectLevel=c;this.modules=null;this.moduleCount=0;this.dataCache=null;this.dataList=[]}function q(a,c){if(void 0==a.length)throw Error(a.length+"/"+c);for(var d=0;da||this.moduleCount<=a||0>c||this.moduleCount<=c)throw Error(a+","+c);return this.modules[a][c]},getModuleCount:function(){return this.moduleCount},make:function(){if(1>this.typeNumber){for(var a=1,a=1;40>a;a++){for(var c=p.getRSBlocks(a,this.errorCorrectLevel),d=new t,b=0,e=0;e=d;d++)if(!(-1>=a+d||this.moduleCount<=a+d))for(var b=-1;7>=b;b++)-1>=c+b||this.moduleCount<=c+b||(this.modules[a+d][c+b]= 5 | 0<=d&&6>=d&&(0==b||6==b)||0<=b&&6>=b&&(0==d||6==d)||2<=d&&4>=d&&2<=b&&4>=b?!0:!1)},getBestMaskPattern:function(){for(var a=0,c=0,d=0;8>d;d++){this.makeImpl(!0,d);var b=j.getLostPoint(this);if(0==d||a>b)a=b,c=d}return c},createMovieClip:function(a,c,d){a=a.createEmptyMovieClip(c,d);this.make();for(c=0;c=f;f++)for(var i=-2;2>=i;i++)this.modules[b+f][e+i]=-2==f||2==f||-2==i||2==i||0==f&&0==i?!0:!1}},setupTypeNumber:function(a){for(var c= 7 | j.getBCHTypeNumber(this.typeNumber),d=0;18>d;d++){var b=!a&&1==(c>>d&1);this.modules[Math.floor(d/3)][d%3+this.moduleCount-8-3]=b}for(d=0;18>d;d++)b=!a&&1==(c>>d&1),this.modules[d%3+this.moduleCount-8-3][Math.floor(d/3)]=b},setupTypeInfo:function(a,c){for(var d=j.getBCHTypeInfo(this.errorCorrectLevel<<3|c),b=0;15>b;b++){var e=!a&&1==(d>>b&1);6>b?this.modules[b][8]=e:8>b?this.modules[b+1][8]=e:this.modules[this.moduleCount-15+b][8]=e}for(b=0;15>b;b++)e=!a&&1==(d>>b&1),8>b?this.modules[8][this.moduleCount- 8 | b-1]=e:9>b?this.modules[8][15-b-1+1]=e:this.modules[8][15-b-1]=e;this.modules[this.moduleCount-8][8]=!a},mapData:function(a,c){for(var d=-1,b=this.moduleCount-1,e=7,f=0,i=this.moduleCount-1;0g;g++)if(null==this.modules[b][i-g]){var n=!1;f>>e&1));j.getMask(c,b,i-g)&&(n=!n);this.modules[b][i-g]=n;e--; -1==e&&(f++,e=7)}b+=d;if(0>b||this.moduleCount<=b){b-=d;d=-d;break}}}};o.PAD0=236;o.PAD1=17;o.createData=function(a,c,d){for(var c=p.getRSBlocks(a, 9 | c),b=new t,e=0;e8*a)throw Error("code length overflow. ("+b.getLengthInBits()+">"+8*a+")");for(b.getLengthInBits()+4<=8*a&&b.put(0,4);0!=b.getLengthInBits()%8;)b.putBit(!1);for(;!(b.getLengthInBits()>=8*a);){b.put(o.PAD0,8);if(b.getLengthInBits()>=8*a)break;b.put(o.PAD1,8)}return o.createBytes(b,c)};o.createBytes=function(a,c){for(var d= 10 | 0,b=0,e=0,f=Array(c.length),i=Array(c.length),g=0;g>>=1;return c},getPatternPosition:function(a){return j.PATTERN_POSITION_TABLE[a-1]},getMask:function(a,c,d){switch(a){case 0:return 0==(c+d)%2;case 1:return 0==c%2;case 2:return 0==d%3;case 3:return 0==(c+d)%3;case 4:return 0==(Math.floor(c/2)+Math.floor(d/3))%2;case 5:return 0==c*d%2+c*d%3;case 6:return 0==(c*d%2+c*d%3)%2;case 7:return 0==(c*d%3+(c+d)%2)%2;default:throw Error("bad maskPattern:"+ 14 | a);}},getErrorCorrectPolynomial:function(a){for(var c=new q([1],0),d=0;dc)switch(a){case 1:return 10;case 2:return 9;case s:return 8;case 8:return 8;default:throw Error("mode:"+a);}else if(27>c)switch(a){case 1:return 12;case 2:return 11;case s:return 16;case 8:return 10;default:throw Error("mode:"+a);}else if(41>c)switch(a){case 1:return 14;case 2:return 13;case s:return 16;case 8:return 12;default:throw Error("mode:"+ 15 | a);}else throw Error("type:"+c);},getLostPoint:function(a){for(var c=a.getModuleCount(),d=0,b=0;b=g;g++)if(!(0>b+g||c<=b+g))for(var h=-1;1>=h;h++)0>e+h||c<=e+h||0==g&&0==h||i==a.isDark(b+g,e+h)&&f++;5a)throw Error("glog("+a+")");return l.LOG_TABLE[a]},gexp:function(a){for(;0>a;)a+=255;for(;256<=a;)a-=255;return l.EXP_TABLE[a]},EXP_TABLE:Array(256), 17 | LOG_TABLE:Array(256)},m=0;8>m;m++)l.EXP_TABLE[m]=1<m;m++)l.EXP_TABLE[m]=l.EXP_TABLE[m-4]^l.EXP_TABLE[m-5]^l.EXP_TABLE[m-6]^l.EXP_TABLE[m-8];for(m=0;255>m;m++)l.LOG_TABLE[l.EXP_TABLE[m]]=m;q.prototype={get:function(a){return this.num[a]},getLength:function(){return this.num.length},multiply:function(a){for(var c=Array(this.getLength()+a.getLength()-1),d=0;d 18 | this.getLength()-a.getLength())return this;for(var c=l.glog(this.get(0))-l.glog(a.get(0)),d=Array(this.getLength()),b=0;b>>7-a%8&1)},put:function(a,c){for(var d=0;d>>c-d-1&1))},getLengthInBits:function(){return this.length},putBit:function(a){var c=Math.floor(this.length/8);this.buffer.length<=c&&this.buffer.push(0);a&&(this.buffer[c]|=128>>>this.length%8);this.length++}};"string"===typeof h&&(h={text:h});h=r.extend({},{render:"canvas",width:256,height:256,typeNumber:-1, 26 | correctLevel:2,background:"#ffffff",foreground:"#000000"},h);return this.each(function(){var a;if("canvas"==h.render){a=new o(h.typeNumber,h.correctLevel);a.addData(h.text);a.make();var c=document.createElement("canvas");c.width=h.width;c.height=h.height;for(var d=c.getContext("2d"),b=h.width/a.getModuleCount(),e=h.height/a.getModuleCount(),f=0;f").css("width",h.width+"px").css("height",h.height+"px").css("border","0px").css("border-collapse","collapse").css("background-color",h.background);d=h.width/a.getModuleCount();b=h.height/a.getModuleCount();for(e=0;e").css("height",b+"px").appendTo(c);for(i=0;i").css("width", 28 | d+"px").css("background-color",a.isDark(e,i)?h.foreground:h.background).appendTo(f)}}a=c;jQuery(a).appendTo(this)})}})(jQuery); 29 | -------------------------------------------------------------------------------- /WEB类库插件使用案例/qrcode.js生成二维码并提供下载(案例)/img-1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LucaLJX/jianshu_demo/9fa279e2cd47560a0d23ada47104e6aeee4c558d/WEB类库插件使用案例/qrcode.js生成二维码并提供下载(案例)/img-1.jpg -------------------------------------------------------------------------------- /WEB类库插件使用案例/基于cropper.js封装vue在线图片裁剪组件/README.md: -------------------------------------------------------------------------------- 1 | 2 | ## 基于cropper.js封装vue在线图片裁剪组件 3 | 4 | ### 效果预览 5 | 6 | ![效果图](cropper-demo.jpg) 7 | 8 | [github:demo下载](https://github.com/LucaLJX/jianshu_demo/tree/master/WEB%E7%B1%BB%E5%BA%93%E6%8F%92%E4%BB%B6%E4%BD%BF%E7%94%A8%E6%A1%88%E4%BE%8B/%E5%9F%BA%E4%BA%8Ecropper.js%E5%B0%81%E8%A3%85vue%E5%9C%A8%E7%BA%BF%E5%9B%BE%E7%89%87%E8%A3%81%E5%89%AA%E7%BB%84%E4%BB%B6) 9 | 10 | ### cropper.js 11 | 12 | > [github:cropper.js](https://github.com/fengyuanchen/cropper) 13 | 14 | > [官网(demo)](http://fengyuanchen.github.io/cropper/) 15 | 16 | #### cropper.js 安装 17 | 18 | - npm或bower安装 19 | 20 | ```shell 21 | npm install cropper 22 | # or 23 | bower install cropper 24 | ``` 25 | - clone下载:[下载地址](https://github.com/fengyuanchen/cropper.git) 26 | 27 | ```shell 28 | git clone https://github.com/fengyuanchen/cropper.git 29 | ``` 30 | 31 | #### 引用cropper.js 32 | 33 | - 主要引用cropper.js跟cropper.css两个文件 34 | 35 | ```html 36 | 37 | 38 | 39 | ``` 40 | 41 | **注意:必须先引入jquery文件,才能使用cropper.js插件** 42 | 43 | #### 简单使用 44 | 45 | - 构建截图所要用到的div容器 46 | 47 | ```html 48 | 49 |
50 | 51 |
52 | ``` 53 | 54 | - 添加容器的样式,让img填充满整个容器(很重要) 55 | 56 | ```css 57 | /* Limit image width to avoid overflow the container */ 58 | img { 59 | max-width: 100%; /* This rule is very important, please do not ignore this! */ 60 | } 61 | ``` 62 | 63 | - 调用cropper.js方法,初始化截图控件 64 | 65 | ```javascript 66 | $('#image').cropper({ 67 | aspectRatio: 16 / 9, 68 | crop: function(e) { 69 | // Output the result data for cropping image. 70 | console.log(e.x); 71 | console.log(e.y); 72 | console.log(e.width); 73 | console.log(e.height); 74 | console.log(e.rotate); 75 | console.log(e.scaleX); 76 | console.log(e.scaleY); 77 | } 78 | }); 79 | ``` 80 | 81 | > 其他详细api请参考:[github:cropper.js](https://github.com/fengyuanchen/cropper#options) 82 | 83 | ### 封装成vue组件 84 | 85 | #### 封装成vue组件中需解决的问题 86 | 87 | - cropper.js相关 88 | - 模拟input框点击选择图片并对选择的图片进行格式、大小限制 89 | - 重新选择图片裁剪 90 | - 确认裁剪并获取base64格式的图片信息 91 | - vue相关 92 | - 非父子组件之间的通信问题 93 | 94 | #### 模拟input框点击选择图片并对选择的图片进行格式、大小限制 95 | 96 | - 构建一个隐藏的input标签,然后模拟点击此input,从而达到能选择图片的功能 97 | 98 | ```html 99 | 100 | 101 | ``` 102 | 103 | ```javascript 104 | //模拟点击 105 | document.getElementById('myCropper-input').click(); 106 | ``` 107 | 108 | - 给input绑定一个监听内容变化的方法,拿到上传的文件,并进行格式、大小校验 109 | 110 | ```javascript 111 | // imgCropperData: { 112 | // accept: 'image/gif, image/jpeg, image/png, image/bmp', 113 | // } 114 | handleFile (e) { 115 | let _this = this; 116 | let inputDOM = this.$refs.inputer; 117 | // 通过DOM取文件数据 118 | _this.file = inputDOM.files[0]; 119 | // 判断文件格式 120 | if (_this.imgCropperData.accept.indexOf(_this.file.type) == -1) { 121 | _this.$Modal.error({ 122 | title: '格式错误', 123 | content: '您选择的图片格式不正确!' 124 | }); 125 | return; 126 | } 127 | // 判断文件大小限制 128 | if (_this.file.size > 5242880) { 129 | _this.$Modal.error({ 130 | title: '超出限制', 131 | content: '您选择的图片过大,请选择5MB以内的图片!' 132 | }); 133 | return; 134 | } 135 | var reader = new FileReader(); 136 | // 将图片将转成 base64 格式 137 | reader.readAsDataURL(_this.file); 138 | reader.onload = function () { 139 | _this.imgCropperData.imgSrc = this.result; 140 | _this.initCropper(); 141 | } 142 | } 143 | ``` 144 | > [参考:从0开始做一个的Vue图片/ 文件选择(上传)组件[基础向]](http://blog.csdn.net/lucky_lxg/article/details/67634265) 145 | 146 | #### 重新选择图片裁剪 147 | 148 | - 当第一次选择图片之后,肯定会面临需要重选图片的问题,那么就会面临如何替换掉裁剪框中的图片,上面的步骤选择了图片后通过[FileRender()](https://developer.mozilla.org/zh-CN/docs/Web/API/FileReader)方法拿到了图片的主要信息,现在就需要重新构建裁剪框就可以解决问题了,查看cropper.js给出的[官方demo](https://github.com/fengyuanchen/cropper/blob/master/examples/crop-avatar/index.html),发现官方是使用动态添加裁剪容器的方法,进行操作的,这里我们仿照官方进行实现。 149 | 150 | ```javascript 151 | // 初始化剪切 152 | initCropper () { 153 | let _this = this; 154 | // 初始化裁剪区域 155 | _this.imgObj = $(''); 156 | let $avatarPreview = $('.avatar-preview'); 157 | $('#myCropper-workspace').empty().html(_this.imgObj); 158 | _this.imgObj.cropper({ 159 | aspectRatio: _this.proportionX / _this.proportionY, 160 | preview: $avatarPreview, 161 | crop: function(e) { 162 | 163 | } 164 | }); 165 | } 166 | ``` 167 | 168 | #### 确认裁剪并获取base64格式的图片信息 169 | 170 | ```javascript 171 | let $imgData = _this.imgObj.cropper('getCroppedCanvas') 172 | imgBase64Data = $imgData.toDataURL('image/png'); 173 | ``` 174 | 175 | #### 构造用于上传的数据 176 | 177 | ```javascript 178 | // 构造上传图片的数据 179 | let formData = new FormData(); 180 | // 截取字符串 181 | let photoType = imgBase64Data.substring(imgBase64Data.indexOf(",") + 1); 182 | //进制转换 183 | const b64toBlob = (b64Data, contentType = '', sliceSize = 512) => { 184 | const byteCharacters = atob(b64Data); 185 | const byteArrays = []; 186 | for(let offset = 0; offset < byteCharacters.length; offset += sliceSize) { 187 | const slice = byteCharacters.slice(offset, offset + sliceSize); 188 | const byteNumbers = new Array(slice.length); 189 | for(let i = 0; i < slice.length; i++) { 190 | byteNumbers[i] = slice.charCodeAt(i); 191 | } 192 | const byteArray = new Uint8Array(byteNumbers); 193 | byteArrays.push(byteArray); 194 | } 195 | const blob = new Blob(byteArrays, { 196 | type: contentType 197 | }); 198 | return blob; 199 | } 200 | const contentType = 'image/jepg'; 201 | const b64Data2 = photoType; 202 | const blob = b64toBlob(b64Data2, contentType); 203 | formData.append("file", blob, "client-camera-photo.png") 204 | formData.append("type", _this.imgType) 205 | ``` 206 | 207 | #### **非父子组件之间的通信问题** 208 | 209 | > 在之前的项目中,常用到父子组件之间的通信传参,一般用两种方法 210 | 211 | - 在router里面放置参数,然后通过调用$route.params.xxx或者$route.query.xxx进行获取 212 | - 通过props进行通信 213 | 214 | **这里我们使用eventBus进行组件之间的通信** 215 | 216 | ##### 步骤 217 | 218 | 1.声明一个bus组件用于B组件把参数传递给A组件 219 | 220 | ```javascript 221 | //bus.js 222 | import Vue from 'vue'; 223 | export default new Vue(); 224 | ``` 225 | 226 | 2.在A组件中引用bus组件,并实时监听其参数变化 227 | 228 | ```javascript 229 | // A.vue 230 | import Bus from '../../components/bus/bus.js' 231 | 232 | export default { 233 | components: { Bus }, 234 | data () {}, 235 | created: function () { 236 | Bus.$on('getTarget', imgToken => { 237 | var _this = this; 238 | console.log(imgToken); 239 | ... 240 | }); 241 | } 242 | } 243 | ``` 244 | 245 | 3.B组件中同样引用bus组件,来把参数传给A组件 246 | 247 | ```javascript 248 | // B.vue 249 | // 传参 250 | Bus.$emit('getTarget', imgToken); 251 | ``` 252 | 253 | 参考: 254 | 255 | - [vue-$on](https://cn.vuejs.org/v2/guide/components.html#使用-v-on-绑定自定义事件) 256 | - [vue-$emit](https://cn.vuejs.org/v2/api/#vm-emit) 257 | - [vue.js之路(4)——vue2.0s中eventBus实现兄弟组件通信](http://blog.csdn.net/u013034014/article/details/54574989?locationNum=2&fps=1) 258 | 259 | ### vue选图截图插件完整代码 260 | 261 | ```html 262 | 285 | 286 | 469 | 470 | 541 | 542 | ``` -------------------------------------------------------------------------------- /WEB类库插件使用案例/基于cropper.js封装vue在线图片裁剪组件/cropper-demo.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LucaLJX/jianshu_demo/9fa279e2cd47560a0d23ada47104e6aeee4c558d/WEB类库插件使用案例/基于cropper.js封装vue在线图片裁剪组件/cropper-demo.jpg -------------------------------------------------------------------------------- /WEB类库插件使用案例/基于cropper.js封装vue在线图片裁剪组件/demo/imgCropper.vue: -------------------------------------------------------------------------------- 1 | 24 | 25 | 208 | 209 | 280 | -------------------------------------------------------------------------------- /WEB需求问题解决/BingMap在VUE项目中的使用/README.md: -------------------------------------------------------------------------------- 1 | 2 | # bing Map 在vue项目中的使用 3 | 4 | ## 写在最前面 5 | 6 | 拥有全球数据库国内好像就只有百度地图有,高德、搜狗、腾讯的都不行,但是由于百度地图的数据更新不及时,所以在做相关项目要用到国外数据的时候,最好还是推荐使用bingMap。 7 | 8 | ## bing Map 使用教程(基础) 9 | 10 | > 参考文档: [bing Map 官方教程](https://msdn.microsoft.com/en-us/library/mt712557.aspx) 11 | 12 | ### bing Map 初始化 13 | 14 | - 引入bing map资源 15 | 16 | ```html 17 | 18 | ``` 19 | 20 | - 初始化地图 21 | 22 | ```html 23 |
24 | 25 | 32 | ``` 33 | 34 | - 设置地图控制参数 35 | - 常用控制参数 36 | - branch 37 | - 加载地图sdk的哪个分支:release(默认)、experimental 38 | - callback 39 | - 地图控制脚本加载完成后的回调(默认:GetMap) 40 | - key 41 | - 用户使用的userKey([详情](https://msdn.microsoft.com/en-us/library/mt712553.aspx)) 42 | - setLang 43 | - 指定用于地图标签和导航控件的语言 44 | - 常用:中国大陆(zh-CN)、中国香港(zh-HK)、简体中文(zh-Hans)、中国台湾(zh-TW)、英文-英国(en-GB)、英文-美国(en-US) 45 | - setMkt([详情](https://msdn.microsoft.com/en-us/library/mt712553.aspx)) 46 | - UR([详情](https://msdn.microsoft.com/en-us/library/mt712553.aspx)) 47 | 48 | - 给bing map添加地图事件([参考](https://msdn.microsoft.com/en-us/library/mt736399.aspx)) 49 | 50 | ```javascript 51 | // 核心代码-demo 52 | Microsoft.Maps.Events.addHandler(你的地图名称, 触发地图事件名称, function() { 触发的事件 }); 53 | 54 | // 常用实例 55 | //Add view change events to the map. 56 | // 视图更改事件 57 | Microsoft.Maps.Events.addHandler(map, 'viewchangestart', function () { highlight('mapViewChangeStart'); }); 58 | Microsoft.Maps.Events.addHandler(map, 'viewchange', function () { highlight('mapViewChange'); }); 59 | Microsoft.Maps.Events.addHandler(map, 'viewchangeend', function () { highlight('mapViewChangEnd'); }); 60 | 61 | //Add mouse events to the map. 62 | // 鼠标事件 63 | Microsoft.Maps.Events.addHandler(map, 'click', function () { highlight('mapClick'); }); 64 | Microsoft.Maps.Events.addHandler(map, 'dblclick', function () { highlight('mapDblClick'); }); 65 | Microsoft.Maps.Events.addHandler(map, 'rightclick', function () { highlight('mapRightClick'); }); 66 | Microsoft.Maps.Events.addHandler(map, 'mousedown', function () { highlight('mapMousedown'); }); 67 | Microsoft.Maps.Events.addHandler(map, 'mouseout', function () { highlight('mapMouseout'); }); 68 | Microsoft.Maps.Events.addHandler(map, 'mouseover', function () { highlight('mapMouseover'); }); 69 | Microsoft.Maps.Events.addHandler(map, 'mouseup', function () { highlight('mapMouseup'); }); 70 | Microsoft.Maps.Events.addHandler(map, 'mousewheel', function () { highlight('mapMousewheel'); }); 71 | 72 | //Add addition map event handlers 73 | Microsoft.Maps.Events.addHandler(map, 'maptypechanged', function () { highlight('maptypechanged'); }); 74 | ``` 75 | 76 | ### bing Map 添加图钉([详情](https://msdn.microsoft.com/en-us/library/mt712640.aspx)) 77 | 78 | #### 基本图钉示例 79 | 80 | ```javascript 81 | function GetMap() { 82 | var map = new Microsoft.Maps.Map('#myMap', { 83 | credentials: 'Your Bing Maps Key', 84 | center: new Microsoft.Maps.Location(47.6149, -122.1941) 85 | }); 86 | 87 | var center = map.getCenter(); 88 | 89 | //Create custom Pushpin 90 | // 创建一个图钉 91 | var pin = new Microsoft.Maps.Pushpin(center, { 92 | // demo_1 93 | title: 'Microsoft', // 图钉的标题 94 | subTitle: 'City Center', // 图钉主体文字 95 | text: '1' // 图钉内的文字 96 | // demo_2 97 | color: 'red', // 纯色图钉 98 | }); 99 | 100 | //Add the pushpin to the map 101 | map.entities.push(pin); 102 | } 103 | ``` 104 | 105 | > demo_1 106 | 107 | ![基本图钉示例(文字)](./img/Pushpin_1.png) 108 | 109 | > demo_2 110 | 111 | ![基本图钉示例(颜色)](./img/Pushpin_2.png) 112 | 113 | #### 添加自定义图片图钉([详情](https://msdn.microsoft.com/en-us/library/mt712698.aspx)) 114 | 115 | ```javascript 116 | function GetMap() { 117 | var map = new Microsoft.Maps.Map('#myMap', 118 | { 119 | credentials: 'You Bing Maps Key' 120 | }); 121 | 122 | var center = map.getCenter(); 123 | 124 | //Create custom Pushpin 125 | var pin = new Microsoft.Maps.Pushpin(center, { 126 | icon: 'images/poi_custom.png', // 自定义图片路径 127 | anchor: new Microsoft.Maps.Point(12, 39) 128 | }); 129 | 130 | //Add the pushpin to the map 131 | map.entities.push(pin); 132 | } 133 | ``` 134 | 135 | > 自定义图标的图钉 136 | 137 | ![自定义图标的图钉](./img/Pushpin_3.png) 138 | 139 | #### bing Map 给图钉添加事件 140 | 141 | - 核心代码 142 | 143 | ```javascript 144 | //Create a pushpin. 145 | var pushpin = new Microsoft.Maps.Pushpin(map.getCenter()); 146 | map.entities.push(pushpin); 147 | 148 | //Add mouse events to the pushpin. 149 | // 将自定义方法及鼠标事件添加到图钉上面 150 | Microsoft.Maps.Events.addHandler(pushpin, 'click', function () { highlight('pushpinClick'); }); 151 | Microsoft.Maps.Events.addHandler(pushpin, 'mousedown', function () { highlight('pushpinMousedown'); }); 152 | Microsoft.Maps.Events.addHandler(pushpin, 'mouseout', function () { highlight('pushpinMouseout'); }); 153 | Microsoft.Maps.Events.addHandler(pushpin, 'mouseover', function () { highlight('pushpinMouseover'); }); 154 | Microsoft.Maps.Events.addHandler(pushpin, 'mouseup', function () { highlight('pushpinMouseup'); }); 155 | ``` 156 | 157 | #### bing Map 给图钉添加hover样式 158 | 159 | > 其核心还是给bing Map的图钉添加事件,通过事件修改图钉的样式 160 | 161 | ```javascript 162 | // demo 163 | var defaultColor = 'blue'; 164 | var hoverColor = 'red'; 165 | var mouseDownColor = 'purple'; 166 | 167 | var pin = new Microsoft.Maps.Pushpin(map.getCenter(), { 168 | color: defaultColor 169 | }); 170 | 171 | map.entities.push(pin); 172 | 173 | Microsoft.Maps.Events.addHandler(pin, 'mouseover', function (e) { 174 | e.target.setOptions({ color: hoverColor }); 175 | }); 176 | 177 | Microsoft.Maps.Events.addHandler(pin, 'mousedown', function (e) { 178 | e.target.setOptions({ color: mouseDownColor }); 179 | }); 180 | 181 | Microsoft.Maps.Events.addHandler(pin, 'mouseout', function (e) { 182 | e.target.setOptions({ color: defaultColor }); 183 | }); 184 | ``` 185 | 186 | > 给图钉添加hover样式 187 | 188 | ![给图钉添加hover样式](./img/Pushpin_4.jpeg) 189 | 190 | #### bing Map 固定锚点 191 | 192 | > 开发人员在使用自定义图钉时遇到的最常见问题之一是,当他们缩放地图时,看起来好像他们的图钉正在漂移到或离开它所要锚定的位置。这是由于图钉选项中的锚点值不正确。锚点指定图像的哪个像素坐标相对于图像的左上角应与图钉位置坐标重叠。 193 | 194 | > [常见配置参考](https://msdn.microsoft.com/en-us/library/mt712695.aspx) 195 | 196 | 197 | ## bing Map 在vue中使用 198 | 199 | ### vue引入bing Map可能会遇到的问题 200 | 201 | > 由于vue一般引用第三方插件是用import的方式进行的,所以的在html中使用script标签引入bing Map SDK会出现两种问题 202 | 203 | > 1.在控制台会报错:Mirosorft is not defined 204 | 205 | > 2.vue-cli会报错:Mirosorft is not defined 206 | 207 | **这里的原因是由于异步加载,所以在调用"Mirosorft"的时候可能SDK并没有引用成功** 208 | 209 | ### 解决“Mirosorft is not defined”的错误 210 | 211 | > [文档参考](https://segmentfault.com/a/1190000012815739) 212 | 213 | 解决“Mirosorft is not defined”的错误,只要在项目中保证调用地图之前,能够正确引入相关工具类就行了。 214 | 215 | ```javascript 216 | // bing map init devTools 217 | export default { 218 | init: function (){ 219 | console.log("初始化bing地图脚本..."); 220 | // bing map key 221 | const bingUesrKey = '你的bingMap Key'; 222 | const BingMap_URL = 'http://www.bing.com/api/maps/mapcontrol?callback=GetMap&key=' + bingUesrKey; 223 | return new Promise((resolve, reject) => { 224 | if(typeof Microsoft !== "undefined") { 225 | resolve(Microsoft); 226 | return true; 227 | } 228 | 229 | // 插入script脚本 230 | let scriptNode = document.createElement("script"); 231 | scriptNode.setAttribute("type", "text/javascript"); 232 | scriptNode.setAttribute("src", BingMap_URL); 233 | document.body.appendChild(scriptNode); 234 | 235 | // 等待页面加载完毕回调 236 | let timeout = 0; 237 | let interval = setInterval(() => { 238 | // 超时10秒加载失败 239 | if(timeout >= 20) { 240 | reject(); 241 | clearInterval(interval); 242 | console.error("bing地图脚本初始化失败..."); 243 | } 244 | // 加载成功 245 | if(typeof Microsoft !== "undefined") { 246 | resolve(Microsoft); 247 | clearInterval(interval); 248 | console.log("bing地图脚本初始化成功..."); 249 | } 250 | timeout += 1; 251 | }, 500); 252 | }); 253 | } 254 | } 255 | 256 | // bing map vue 257 | import bingMap from './**/bing-map'; 258 | bingMap.init() 259 | .then((Microsoft) => { 260 | console.log(Microsoft) 261 | console.log("加载成功...") 262 | // 开始地图操作 263 | }) 264 | ``` 265 | 266 | ### 集成bing Map组件到vue中 267 | 268 | #### 需要达到的功能 269 | 270 | - 在vue项目中成功加载bing Map (完成) 271 | - 当点击bing Map的时候,返回点击点的经纬度 (完成) 272 | - 子组件触发事件返回参数到父组件 273 | - 当已有经纬度的时候,加载bingMap自动显示其经纬度所在的位置并设置图钉 (待完成) 274 | 275 | #### 子组件触发事件返回参数到父组件 276 | 277 | - 实现原理 278 | - [vue-$meit](https://cn.vuejs.org/v2/api/#vm-emit) 279 | 280 | - 核心代码 281 | 282 | ```html 283 | // 子组件 284 | 287 | methods:{ 288 | iclick(){ 289 | let data = { 290 | a:'data' 291 | }; 292 | this.$emit('ievent', data1, 'data2Str'); 293 | } 294 | } 295 | // 父组件 296 | 297 | methods:{ 298 | ievent(...data){ 299 | console.log('allData:',data); // data为包含传过来所有数据的数组,第一个元素是对象,第二个元素是字符串 300 | } 301 | } 302 | ``` 303 | 304 | #### 封装bing Map通用组件 305 | ```html 306 | // 核心代码 307 | 312 | 313 | 365 | 366 | 373 | ``` 374 | 375 | #### 在组件中调用bing Map通用组件 376 | ```javascript 377 | // 引入bingMap 378 | import bingMapsLayer from 'bingMap.vue' 379 | 380 | // component中定义 381 | components: { 382 | bingMapsLayer 383 | }, 384 | 385 | // template中使用 386 | 387 | 388 | // 定义触发点击标记返回经纬度的事件函数 389 | getLocationNums (...data) { 390 | let _this = this; 391 | console.log('click'); 392 | console.log(data); 393 | // 这里的data中即子组件bingMap返回的点击获取的经纬度值 394 | }, 395 | ``` 396 | 397 | ## 未完待续 398 | 399 | ### 后续 400 | 401 | - 在bingMap组件中使用自定义图钉 402 | - 在bingMap组件中定义可拖动图钉标记经纬度 403 | - 集成bingMap自定义搜索 404 | - 集成bingMap图钉详情展示 405 | 406 | -------------------------------------------------------------------------------- /WEB需求问题解决/BingMap在VUE项目中的使用/demo/bingMap.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 59 | 60 | 67 | 68 | 69 | -------------------------------------------------------------------------------- /WEB需求问题解决/BingMap在VUE项目中的使用/demo/initMap.js: -------------------------------------------------------------------------------- 1 | export default { 2 | init: function (){ 3 | console.log("初始化bing地图脚本..."); 4 | // bing map key 5 | const bingUesrKey = 'AgzeobkGvmpdZTFuGa7_6gkaHH7CXHKsFiTQlBvi55x-QLZLh1rSjhd1Da9bfPhD'; 6 | const BingMap_URL = 'http://www.bing.com/api/maps/mapcontrol?callback=GetMap&key=' + bingUesrKey; 7 | return new Promise((resolve, reject) => { 8 | if(typeof Microsoft !== "undefined") { 9 | resolve(Microsoft); 10 | return true; 11 | } 12 | 13 | // 插入script脚本 14 | let scriptNode = document.createElement("script"); 15 | scriptNode.setAttribute("type", "text/javascript"); 16 | scriptNode.setAttribute("src", BingMap_URL); 17 | document.body.appendChild(scriptNode); 18 | 19 | // 等待页面加载完毕回调 20 | let timeout = 0; 21 | let interval = setInterval(() => { 22 | // 超时10秒加载失败 23 | if(timeout >= 20) { 24 | reject(); 25 | clearInterval(interval); 26 | console.error("bing地图脚本初始化失败..."); 27 | } 28 | // 加载成功 29 | if(typeof Microsoft !== "undefined") { 30 | resolve(Microsoft); 31 | clearInterval(interval); 32 | console.log("bing地图脚本初始化成功..."); 33 | } 34 | timeout += 1; 35 | }, 500); 36 | }); 37 | } 38 | } -------------------------------------------------------------------------------- /WEB需求问题解决/BingMap在VUE项目中的使用/img/Pushpin_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LucaLJX/jianshu_demo/9fa279e2cd47560a0d23ada47104e6aeee4c558d/WEB需求问题解决/BingMap在VUE项目中的使用/img/Pushpin_1.png -------------------------------------------------------------------------------- /WEB需求问题解决/BingMap在VUE项目中的使用/img/Pushpin_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LucaLJX/jianshu_demo/9fa279e2cd47560a0d23ada47104e6aeee4c558d/WEB需求问题解决/BingMap在VUE项目中的使用/img/Pushpin_2.png -------------------------------------------------------------------------------- /WEB需求问题解决/BingMap在VUE项目中的使用/img/Pushpin_3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LucaLJX/jianshu_demo/9fa279e2cd47560a0d23ada47104e6aeee4c558d/WEB需求问题解决/BingMap在VUE项目中的使用/img/Pushpin_3.png -------------------------------------------------------------------------------- /WEB需求问题解决/BingMap在VUE项目中的使用/img/Pushpin_4.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LucaLJX/jianshu_demo/9fa279e2cd47560a0d23ada47104e6aeee4c558d/WEB需求问题解决/BingMap在VUE项目中的使用/img/Pushpin_4.jpeg -------------------------------------------------------------------------------- /WEB需求问题解决/VUE缓存:动态keep-alive/README.md: -------------------------------------------------------------------------------- 1 | # VUE缓存:动态keep-alive 2 | 3 | > 路总归是有的,就看愿不愿意剑走偏锋了。 4 | 5 | ## 场景 6 | 7 | 在最近的开发中,设计有A、B、C三个页面,试想这样一个场景需求: 8 | 9 | - 离开B页面进入C页面,缓存B页面数据(keepAlive: true) 10 | - 离开B页面进入A页面,不缓存B页面数据(keepAlive: false) 11 | 12 | ## 概念 13 | 14 | - keep-alive 15 | - keep-alive 包裹动态组件时,会缓存不活动的组件实例,而不是销毁它们。和 transition 相似,keep-alive 是一个抽象组件:它自身不会渲染一个 DOM 元素,也不会出现在父组件链中。 16 | - [keep-alive: vue文档](https://cn.vuejs.org/v2/api/#keep-alive) 17 | 18 | - 组件内的守卫 - beforeRouteLeave 19 | - 导航离开该组件的对应路由时调用 20 | - 可以访问组件实例 `this` 21 | - [组件内的守卫: vue-router 文档](https://router.vuejs.org/zh/guide/advanced/navigation-guards.html#%E7%BB%84%E4%BB%B6%E5%86%85%E7%9A%84%E5%AE%88%E5%8D%AB) 22 | 23 | ## 前置背景:keep-alive 组件实现 24 | 25 | - 路由元信息内添加特定字段如:keepAlive 26 | 27 | ```js 28 | const router = new VueRouter({ 29 | routes: [ 30 | { 31 | path: '/foo', 32 | component: Foo, 33 | children: [ 34 | { 35 | path: 'bar', 36 | component: Bar, 37 | // a meta field 38 | meta: { 39 | keepAlive: true 40 | } 41 | } 42 | ] 43 | } 44 | ] 45 | }) 46 | ``` 47 | 48 | - 父组件内根据路由中的keepAlive字段动态使用keep-alive标签 49 | 50 | ```jsx 51 | class Home extends Vue { 52 | 53 | get keepAlive () { 54 | // 获取当前路由的元信息中的keepAlive字段 55 | return this.$route.meta.keepAlive 56 | } 57 | 58 | private render () { 59 | return ( 60 |
61 | { 62 | !this.keepAlive && 63 | } 64 | 65 | { 66 | this.keepAlive && 67 | } 68 | 69 |
70 | ) 71 | } 72 | } 73 | 74 | export default Home 75 | ``` 76 | 77 | ## 思路 78 | 79 | 由于现在组件的keep-alive是动态根据路由元信息中的keepAlive字段进行动态使用的,所以只要动态改变对应路由元信息的keepAlive字段就可以实现动态缓存。 80 | 81 | ## 实现方案 82 | 83 | ### 方案一 84 | 85 | - 利用beforeRouteLeave改变from的keepAlive实现(原思路,网络解决方案之一,有bug) 86 | 87 | ```js 88 | beforeRouteLeave (to: any, from: any, next: any) { 89 | // 导航离开该组件的对应路由时调用 90 | // 判断是否是去往页面 C 91 | if (to.name !== 'C') { 92 | // 不是去 C 页面,不缓存 93 | from.meta.keepAlive = false 94 | } else { 95 | // 是去 C 页面,缓存 96 | from.meta.keepAlive = true 97 | } 98 | next() 99 | } 100 | ``` 101 | 102 | **bug:首次去C页面,再返回B页面,B并没有缓存,第二次再进入C页面,B页面缓存,且进A页面并不能清除B页面的缓存** 103 | 104 | ### 方案二(网络方案) 105 | 106 | - $destroy()销毁 107 | 108 | ```js 109 | beforeRouteLeave (to: any, from: any, next: any) { 110 | // 导航离开该组件的对应路由时调用 111 | // 判断是否是去往页面 C 112 | if (to.name !== 'C') { 113 | // 不是去 C 页面,不缓存 114 | this.$destroy() 115 | } 116 | next() 117 | } 118 | ``` 119 | 120 | **bug:销毁之后永远不会被缓存** 121 | 122 | ### 方案三(网络方案) 123 | 124 | - 根据源码看来缓存的组件都会设置一个cache属性,可以通过代码强行移除掉。缺点就是没有彻底销毁依旧占内存 125 | - [具体实现参考](https://segmentfault.com/a/1190000015845117) 126 | 127 | ### 方案四(最优解) 128 | 129 | - 利用keep-alive的include属性,利用vuex动态控制include达到动态管理缓存 130 | - 这边不做赘述,网上有很多相关示例代码,且原理很简单 131 | 132 | ## 特殊场景以上方案均不可实现下的解决方案 133 | 134 | ### 特殊场景 135 | 136 | - 此次项目为移动端项目,并未用到keep-alive的include属性,且如果要加入,则项目很多配置需要修改,比较麻烦 137 | 138 | ### 解决方案 139 | 140 | - 解决方案与上面的方案一类似 141 | - 区别: 142 | - 不操作beforeRouteLeave中的from对象改变keepAlive 143 | - 直接操作this.$router中对应路由元信息的keepAlive 144 | 145 | ```js 146 | // 操作指定name的路由的元信息 147 | private changeKeepAlive (parentName: string, name: string, keepAlive: boolean) { 148 | // @ts-ignore 149 | this.$router.options.routes.map((item: any) => { 150 | if (item.name === parentName) { 151 | item.children.map((a: any) => { 152 | if (a.name === name) { 153 | a.meta.keepAlive = keepAlive 154 | } 155 | }) 156 | } 157 | }) 158 | } 159 | 160 | beforeRouteLeave (to: any, from: any, next: any) { 161 | // 导航离开该组件的对应路由时调用 162 | // 可以访问组件实例 `this` 163 | if (to.name === 'C') { 164 | this.changeKeepAlive('Home', 'B', true) 165 | } else { 166 | this.changeKeepAlive('Home', 'B', false) 167 | } 168 | next() 169 | } 170 | ``` 171 | 172 | **经测试,这种解决方案就不会出现方案一的bug** 173 | 174 | BY--LucaLJX ([LucaLJX的github](https://github.com/LucaLJX/jianshu_demo)) 175 | -------------------------------------------------------------------------------- /node.js/基于node.js构建文件服务器/README.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LucaLJX/jianshu_demo/9fa279e2cd47560a0d23ada47104e6aeee4c558d/node.js/基于node.js构建文件服务器/README.md -------------------------------------------------------------------------------- /node.js/基于node.js构建文件服务器/nodeFileServer/file/img/0.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LucaLJX/jianshu_demo/9fa279e2cd47560a0d23ada47104e6aeee4c558d/node.js/基于node.js构建文件服务器/nodeFileServer/file/img/0.jpg -------------------------------------------------------------------------------- /node.js/基于node.js构建文件服务器/nodeFileServer/file/img/1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LucaLJX/jianshu_demo/9fa279e2cd47560a0d23ada47104e6aeee4c558d/node.js/基于node.js构建文件服务器/nodeFileServer/file/img/1.jpg -------------------------------------------------------------------------------- /node.js/基于node.js构建文件服务器/nodeFileServer/file/img/2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LucaLJX/jianshu_demo/9fa279e2cd47560a0d23ada47104e6aeee4c558d/node.js/基于node.js构建文件服务器/nodeFileServer/file/img/2.jpg -------------------------------------------------------------------------------- /node.js/基于node.js构建文件服务器/nodeFileServer/file/img/3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LucaLJX/jianshu_demo/9fa279e2cd47560a0d23ada47104e6aeee4c558d/node.js/基于node.js构建文件服务器/nodeFileServer/file/img/3.jpg -------------------------------------------------------------------------------- /node.js/基于node.js构建文件服务器/nodeFileServer/file/img/4.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LucaLJX/jianshu_demo/9fa279e2cd47560a0d23ada47104e6aeee4c558d/node.js/基于node.js构建文件服务器/nodeFileServer/file/img/4.jpg -------------------------------------------------------------------------------- /node.js/基于node.js构建文件服务器/nodeFileServer/file/img/5.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LucaLJX/jianshu_demo/9fa279e2cd47560a0d23ada47104e6aeee4c558d/node.js/基于node.js构建文件服务器/nodeFileServer/file/img/5.jpg -------------------------------------------------------------------------------- /node.js/基于node.js构建文件服务器/nodeFileServer/file/img/6.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LucaLJX/jianshu_demo/9fa279e2cd47560a0d23ada47104e6aeee4c558d/node.js/基于node.js构建文件服务器/nodeFileServer/file/img/6.jpg -------------------------------------------------------------------------------- /node.js/基于node.js构建文件服务器/nodeFileServer/file/img/7.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LucaLJX/jianshu_demo/9fa279e2cd47560a0d23ada47104e6aeee4c558d/node.js/基于node.js构建文件服务器/nodeFileServer/file/img/7.jpg -------------------------------------------------------------------------------- /node.js/基于node.js构建文件服务器/nodeFileServer/js/suffix.js: -------------------------------------------------------------------------------- 1 | const suffix = { 2 | 'html' : 'text/html', 3 | 'css' : 'text/css', 4 | 'js' : 'text/javascript', 5 | 'json' : 'application/json', 6 | 'ico' : 'image/x-icon', 7 | 'gif' : 'image/gif', 8 | 'jpeg' : 'image/jpeg', 9 | 'jpg' : 'image/jpeg', 10 | 'png' : 'image/png', 11 | 'pdf' : 'application/pdf', 12 | 'svg' : 'image/svg+xml', 13 | 'swf' : 'application/x-shockwave-flash', 14 | 'tiff' : 'image/tiff', 15 | 'txt' : 'text/plain', 16 | 'wav' : 'audio/x-wav', 17 | 'wma' : 'audio/x-ms-wma', 18 | 'wmv' : 'video/x-ms-wmv', 19 | 'xml' : 'text/xml' 20 | } -------------------------------------------------------------------------------- /node.js/基于node.js构建文件服务器/nodeFileServer/server.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs'); 2 | const http = require('http'); -------------------------------------------------------------------------------- /react/articles/learning react day 1/ES6/arrow.js: -------------------------------------------------------------------------------- 1 | /* 2 | 箭头函数 + 变量默认值 3 | */ 4 | let fun_1 = (x = 2) => console.log(x) 5 | 6 | fun_1() // 2 7 | fun_1('3') // '3' 8 | 9 | /* 10 | 箭头函数需要传多个参数,用括号代表参数部分 11 | */ 12 | let fun_2 = (num1, num2) => {return num1 + num2;} 13 | let result_2 = fun_2(3, 4) 14 | console.log(result_2) 15 | 16 | /* 17 | 由于大括号被解释为代码块,所以如果箭头函数直接返回一个对象,必须在对象外面加上括号,否则会报错 18 | */ 19 | let fun_3 = (id, name) => ({id: id, name: name}) 20 | let result_3 = fun_3('2', 'liu') 21 | console.log(result_3) -------------------------------------------------------------------------------- /react/articles/learning react day 1/ES6/class.js: -------------------------------------------------------------------------------- 1 | /* 2 | ES5 生成实例 3 | */ 4 | function Point(x, y) { 5 | this.x = x; 6 | this.y = y; 7 | } 8 | 9 | Point.prototype.toString = function () { 10 | return '(' + this.x + ', ' + this.y + ')'; 11 | }; 12 | 13 | var p = new Point(1, 2); 14 | 15 | console.log(p); // Print {x: 1, y: 2} 16 | console.log(p.toString()); // (1, 2) 17 | console.log(typeof(p)); // object 18 | 19 | /* 20 | ES6 生成实例 21 | */ 22 | /* 23 | 注意: 24 | Point类除了构造方法,还定义了一个toString方法。注意,定义“类”的方法的时候,前面不需要加上function这个关键字,直接把函数定义放进去了就可以了。另外,方法之间不需要逗号分隔,加了会报错。 25 | */ 26 | class PrintES6 { 27 | constructor (x, y) { 28 | this.x = x; 29 | this.y = y; 30 | } 31 | // 定义类的方法,不需要加function关键字,且不能用逗号相隔 32 | toString () { 33 | return '(' + this.x + ', ' + this.y + ')'; 34 | } 35 | } 36 | 37 | var pES6 = new PrintES6(5, 6); 38 | console.log(pES6); // PrintES6 {x: 5, y: 6} 39 | console.log(pES6.toString()); // (5, 6) 40 | console.log(typeof(pES6)); // object -------------------------------------------------------------------------------- /react/articles/learning react day 1/ES6/templateLiteral.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LucaLJX/jianshu_demo/9fa279e2cd47560a0d23ada47104e6aeee4c558d/react/articles/learning react day 1/ES6/templateLiteral.js -------------------------------------------------------------------------------- /react/articles/learning react day 1/README.md: -------------------------------------------------------------------------------- 1 | # learning react day 1 2 | 3 | ## 利用react官方工具构建react项目 4 | 5 | ### react-create-app cli 6 | 7 | ```shell 8 | # 安装工具 9 | $ npm / cnpm install -g create-react-app 10 | # 创建react项目 11 | $ create-react-app proName 12 | # 启动 13 | $ cd ./proName/ 14 | $ npm start 15 | ``` 16 | 17 | ### 预制ES6语法熟悉 18 | 19 | #### 箭头函数 20 | 21 | > demo 参见目录下'./ES6/arrow.js' 22 | 23 | > [ES6入门-箭头函数](http://es6.ruanyifeng.com/?search=%E6%A8%A1%E7%89%88%E5%AD%97%E9%9D%A2%E9%87%8F&x=0&y=0#docs/function#箭头函数) 24 | 25 | #### 类 26 | 27 | > demo 参见目录下'./ES6/class.js' 28 | 29 | > [ES6入门-类class](http://es6.ruanyifeng.com/?search=%E6%A8%A1%E7%89%88%E5%AD%97%E9%9D%A2%E9%87%8F&x=0&y=0#docs/class) 30 | 31 | > ES6 提供了更接近传统语言的写法,引入了 Class(类)这个概念,作为对象的模板。通过class关键字,可以定义类。 32 | 33 | > 基本上,ES6 的class可以看作只是一个语法糖,它的绝大部分功能,ES5 都可以做到,新的class写法只是让对象原型的写法更加清晰、更像面向对象编程的语法而已。 34 | 35 | > Point类除了构造方法,还定义了一个toString方法。注意,定义“类”的方法的时候,前面不需要加上function这个关键字,直接把函数定义放进去了就可以了。另外,方法之间不需要逗号分隔,加了会报错。 36 | 37 | ```javascript 38 | class Point { 39 | constructor() { 40 | // ... 41 | } 42 | 43 | toString() { 44 | // ... 45 | } 46 | 47 | toValue() { 48 | // ... 49 | } 50 | } 51 | 52 | // 等同于 53 | 54 | Point.prototype = { 55 | constructor() {}, 56 | toString() {}, 57 | toValue() {}, 58 | }; 59 | ``` 60 | 61 | #### 模版字符串 62 | 63 | > demo 参见目录下'./ES6/templateLiteral.js' 64 | 65 | > [ES6入门-模版字符串](http://es6.ruanyifeng.com/?search=%E6%A8%A1%E7%89%88%E5%AD%97%E9%9D%A2%E9%87%8F&x=0&y=0#docs/string#模板字符串) 66 | 67 | > 模板字符串(template string)是增强版的字符串,用反引号(`)标识。它可以当作普通字符串使用,也可以用来定义多行字符串,或者在字符串中嵌入变量。 -------------------------------------------------------------------------------- /react/articles/learning react day 2/README.md: -------------------------------------------------------------------------------- 1 | # learning react day 2 2 | 3 | ## 状态、生命周期 4 | 5 | ```javascript 6 | class Clock extends React.Component { 7 | constructor(props) { 8 | super(props); 9 | this.state = {date: new Date()}; 10 | } 11 | // 组件初始化 12 | componentDidMount() { 13 | this.timerID = setInterval( 14 | () => this.tick(), 15 | 1000 16 | ); 17 | } 18 | // 组件即将销毁中 19 | componentWillUnmount() { 20 | clearInterval(this.timerID); 21 | } 22 | 23 | tick() { 24 | this.setState({ 25 | date: new Date() 26 | }); 27 | } 28 | 29 | render() { 30 | return ( 31 |
32 |

Hello, world!

33 |

It is {this.state.date.toLocaleTimeString()}.

34 |
35 | ); 36 | } 37 | } 38 | 39 | ReactDOM.render( 40 | , 41 | document.getElementById('root') 42 | ); 43 | ``` 44 | 45 | 注意: 46 | 47 | **1.不要直接修改state(状态)** 48 | **2.state(状态)更新可能是异步的,所以不要依赖state或者props计算下一个状态** 49 | 50 | ## 处理事件 51 | 52 | - React 事件使用驼峰命名,而不是全部小写。 53 | - 通过 JSX , 你传递一个函数作为事件处理程序,而不是一个字符串。 54 | - 在 React 中你不能通过返回 false(愚人码头注:即 return false; 语句) 来阻止默认行为。必须明确调用 preventDefault 。 55 | 56 | 写法区别 57 | ```html 58 | 59 | 60 | 63 | 64 | 67 | ``` 68 | 69 | 阻止默认事件 70 | ```html 71 | 72 | 73 | Click me 74 | 75 | ``` 76 | 77 | ```javascript 78 | function ActionLink() { 79 | function handleClick(e) { 80 | e.preventDefault(); 81 | console.log('The link was clicked.'); 82 | } 83 | 84 | return ( 85 | 86 | Click me 87 | 88 | ); 89 | } 90 | ``` 91 | 92 | ### 关于this的绑定问题 93 | 94 | - 方法1:在构造函数中绑定this 95 | 96 | ```javascript 97 | class Toggle extends React.Component { 98 | constructor(props) { 99 | super(props); 100 | this.state = {isToggleOn: true}; 101 | 102 | // 这个绑定是必要的,使`this`在回调中起作用 103 | this.handleClick = this.handleClick.bind(this); 104 | } 105 | 106 | handleClick() { 107 | this.setState(prevState => ({ 108 | isToggleOn: !prevState.isToggleOn 109 | })); 110 | } 111 | 112 | render() { 113 | return ( 114 | 117 | ); 118 | } 119 | } 120 | 121 | ReactDOM.render( 122 | , 123 | document.getElementById('root') 124 | ); 125 | ``` 126 | 127 | - 方法2:实验性的 属性初始化语法 (不推荐) 128 | 129 | ```javascript 130 | class LoggingButton extends React.Component { 131 | // 这个语法确保 `this` 绑定在 handleClick 中。 132 | // 警告:这是 *实验性的* 语法。 133 | handleClick = () => { 134 | console.log('this is:', this); 135 | } 136 | 137 | render() { 138 | return ( 139 | 142 | ); 143 | } 144 | } 145 | ``` 146 | 147 | - 方法3:在回调中使用一个 箭头函数 148 | 149 | > 问题:这个语法的问题是,每次 LoggingButton 渲染时都创建一个不同的回调。在多数情况下,没什么问题。然而,如果这个回调被作为 prop(属性) 传递给下级组件,这些组件可能需要额外的重复渲染。我们通常建议在构造函数中进行绑定,以避免这类性能问题。 150 | 151 | ```javascript 152 | class LoggingButton extends React.Component { 153 | handleClick() { 154 | console.log('this is:', this); 155 | } 156 | 157 | render() { 158 | // 这个语法确保 `this` 被绑定在 handleClick 中 159 | return ( 160 | 163 | ); 164 | } 165 | } 166 | ``` 167 | 168 | ### 将参数传递给事件处理程序 169 | 170 | ```html 171 | 172 | 173 | ``` 174 | 175 | ## 列表lists 和 键keys 176 | 177 | 基本列表渲染 178 | 179 | ```javascript 180 | function NumberList(props) { 181 | const numbers = props.numbers; 182 | const listItems = numbers.map((number) => 183 |
  • {number}
  • 184 | ); 185 | return ( 186 |
      {listItems}
    187 | ); 188 | } 189 | 190 | const numbers = [1, 2, 3, 4, 5]; 191 | ReactDOM.render( 192 | , 193 | document.getElementById('root') 194 | ); 195 | ``` 196 | 197 | **如果你提取 一个 ListItem 组件,应该把 key 放置在数组处理的 元素中,不能放在 ListItem 组件自身中的
  • 根元素上。** 198 | 199 | ```javascript 200 | function ListItem(props) { 201 | // 正确!这里不需要指定 key : 202 | return
  • {props.value}
  • ; 203 | } 204 | 205 | function NumberList(props) { 206 | const numbers = props.numbers; 207 | const listItems = numbers.map((number) => 208 | // 正确!key 应该在这里被指定 209 | 211 | 212 | ); 213 | return ( 214 |
      215 | {listItems} 216 |
    217 | ); 218 | } 219 | 220 | const numbers = [1, 2, 3, 4, 5]; 221 | ReactDOM.render( 222 | , 223 | document.getElementById('root') 224 | ); 225 | ``` 226 | 227 | ## 状态提升 228 | 229 | **参考本文所在项目文件夹下的react-demo** 230 | 231 | > [react状态提升](http://www.css88.com/react/docs/lifting-state-up.html) -------------------------------------------------------------------------------- /react/questions/README.md: -------------------------------------------------------------------------------- 1 | # questions for react 2 | 3 | ## react 4 | 5 | - react中,state状态管理如何初始化?如何修改state状态? 6 | - this绑定的问题 7 | 8 | ## antd -------------------------------------------------------------------------------- /react/react-demo/demo/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/ignore-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | 6 | # testing 7 | /coverage 8 | 9 | # production 10 | /build 11 | 12 | # misc 13 | .DS_Store 14 | .env.local 15 | .env.development.local 16 | .env.test.local 17 | .env.production.local 18 | 19 | npm-debug.log* 20 | yarn-debug.log* 21 | yarn-error.log* 22 | -------------------------------------------------------------------------------- /react/react-demo/demo/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "demo", 3 | "version": "0.1.0", 4 | "private": true, 5 | "dependencies": { 6 | "react": "^16.4.0", 7 | "react-dom": "^16.4.0", 8 | "react-scripts": "1.1.4" 9 | }, 10 | "scripts": { 11 | "start": "react-scripts start", 12 | "build": "react-scripts build", 13 | "test": "react-scripts test --env=jsdom", 14 | "eject": "react-scripts eject" 15 | } 16 | } -------------------------------------------------------------------------------- /react/react-demo/demo/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LucaLJX/jianshu_demo/9fa279e2cd47560a0d23ada47104e6aeee4c558d/react/react-demo/demo/public/favicon.ico -------------------------------------------------------------------------------- /react/react-demo/demo/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 11 | 12 | 13 | 22 | React App 23 | 24 | 25 | 28 |
    29 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /react/react-demo/demo/public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "React App", 3 | "name": "Create React App Sample", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | } 10 | ], 11 | "start_url": "./index.html", 12 | "display": "standalone", 13 | "theme_color": "#000000", 14 | "background_color": "#ffffff" 15 | } 16 | -------------------------------------------------------------------------------- /react/react-demo/demo/src/App.css: -------------------------------------------------------------------------------- 1 | .App { 2 | text-align: center; 3 | } 4 | 5 | .App-logo { 6 | animation: App-logo-spin infinite 20s linear; 7 | height: 80px; 8 | } 9 | 10 | .App-header { 11 | background-color: #222; 12 | height: 150px; 13 | padding: 20px; 14 | color: white; 15 | } 16 | 17 | .App-title { 18 | font-size: 1.5em; 19 | } 20 | 21 | .App-intro { 22 | font-size: large; 23 | } 24 | 25 | @keyframes App-logo-spin { 26 | from { transform: rotate(0deg); } 27 | to { transform: rotate(360deg); } 28 | } 29 | -------------------------------------------------------------------------------- /react/react-demo/demo/src/App.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | // import logo from './logo.svg'; 3 | import './App.css'; 4 | 5 | // 摄氏度、华氏度名称 6 | const scaleNames = { 7 | c: 'celsius', 8 | f: 'Fahrenheit' 9 | } 10 | 11 | // 华氏度 -> 摄氏度 12 | function toCelsius(fahrenheit) { 13 | return (fahrenheit - 32) * 5 / 9 14 | } 15 | 16 | // 摄氏度 -> 华氏度 17 | function toFahrenheit (celsius) { 18 | return (celsius * 9 / 5) + 32; 19 | } 20 | 21 | // 数据格式化 22 | function tryConvert (temperature, convert) { 23 | const input = parseFloat(temperature); 24 | if (Number.isNaN(input)) { 25 | return ''; 26 | } 27 | const output = convert(input); 28 | const rounded = Math.round(output * 1000) / 1000; 29 | return rounded.toString(); 30 | } 31 | 32 | // 判断是否沸腾,并提示 33 | function BoilingVerdict (props) { 34 | if (props.celsius >= 100) { 35 | return

    The water would boil.

    36 | } 37 | return

    The water would not boil.

    ; 38 | } 39 | 40 | // 声明温度输入组件 41 | class TemperatureInput extends React.Component { 42 | constructor(props) { 43 | super(props); 44 | this.handleChange = this.handleChange.bind(this); 45 | } 46 | 47 | handleChange(e) { 48 | this.props.onTemperatureChange(e.target.value); 49 | } 50 | 51 | render() { 52 | const temperature = this.props.temperature; 53 | const scale = this.props.scale; 54 | return ( 55 |
    56 | Enter temperature in {scaleNames[scale]}: 57 | 59 |
    60 | ); 61 | } 62 | } 63 | 64 | class App extends Component { 65 | constructor (props) { 66 | super(props); 67 | this.state = {date: new Date(), num: 20, temperature: '', scale: 'c'}; 68 | this.clickTheBtn = this.clickTheBtn.bind(this); 69 | // 温度控制 70 | this.handleCelsiusChange = this.handleCelsiusChange.bind(this); 71 | this.handleFahrenheitChange = this.handleFahrenheitChange.bind(this); 72 | } 73 | // 组件第一次初始化 74 | componentDidMount () { 75 | this.timerId = setInterval( 76 | () => this.tick(), 77 | 1000 78 | ); 79 | } 80 | // 组件即将销毁 81 | componentWillUnmount () { 82 | clearInterval(this.timerId); 83 | } 84 | 85 | handleCelsiusChange(temperature) { 86 | this.setState({scale: 'c', temperature}); 87 | } 88 | 89 | handleFahrenheitChange(temperature) { 90 | this.setState({scale: 'f', temperature}); 91 | } 92 | 93 | clickTheBtn (e) { 94 | this.setState(prevState => ({ 95 | num: prevState.num + 10 96 | })); 97 | } 98 | 99 | tick () { 100 | this.setState({ 101 | date: new Date() 102 | }) 103 | } 104 | render() { 105 | const scale = this.state.scale; 106 | const temperature = this.state.temperature; 107 | const celsius = scale === 'f' ? tryConvert(temperature, toCelsius) : temperature; 108 | const fahrenheit = scale === 'c' ? tryConvert(temperature, toFahrenheit) : temperature; 109 | return ( 110 |
    111 |

    hello!

    112 |

    now is {this.state.date.toLocaleTimeString()}

    113 |

    the num is {this.state.num}

    114 | 115 | 119 | 123 | 125 |
    126 | ); 127 | } 128 | } 129 | 130 | export default App; 131 | -------------------------------------------------------------------------------- /react/react-demo/demo/src/App.test.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import App from './App'; 4 | 5 | it('renders without crashing', () => { 6 | const div = document.createElement('div'); 7 | ReactDOM.render(, div); 8 | ReactDOM.unmountComponentAtNode(div); 9 | }); 10 | -------------------------------------------------------------------------------- /react/react-demo/demo/src/index.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | padding: 0; 4 | font-family: sans-serif; 5 | } 6 | -------------------------------------------------------------------------------- /react/react-demo/demo/src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import './index.css'; 4 | import App from './App'; 5 | import registerServiceWorker from './registerServiceWorker'; 6 | 7 | ReactDOM.render(, document.getElementById('root')); 8 | registerServiceWorker(); 9 | -------------------------------------------------------------------------------- /react/react-demo/demo/src/logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /react/react-demo/demo/src/registerServiceWorker.js: -------------------------------------------------------------------------------- 1 | // In production, we register a service worker to serve assets from local cache. 2 | 3 | // This lets the app load faster on subsequent visits in production, and gives 4 | // it offline capabilities. However, it also means that developers (and users) 5 | // will only see deployed updates on the "N+1" visit to a page, since previously 6 | // cached resources are updated in the background. 7 | 8 | // To learn more about the benefits of this model, read https://goo.gl/KwvDNy. 9 | // This link also includes instructions on opting out of this behavior. 10 | 11 | const isLocalhost = Boolean( 12 | window.location.hostname === 'localhost' || 13 | // [::1] is the IPv6 localhost address. 14 | window.location.hostname === '[::1]' || 15 | // 127.0.0.1/8 is considered localhost for IPv4. 16 | window.location.hostname.match( 17 | /^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/ 18 | ) 19 | ); 20 | 21 | export default function register() { 22 | if (process.env.NODE_ENV === 'production' && 'serviceWorker' in navigator) { 23 | // The URL constructor is available in all browsers that support SW. 24 | const publicUrl = new URL(process.env.PUBLIC_URL, window.location); 25 | if (publicUrl.origin !== window.location.origin) { 26 | // Our service worker won't work if PUBLIC_URL is on a different origin 27 | // from what our page is served on. This might happen if a CDN is used to 28 | // serve assets; see https://github.com/facebookincubator/create-react-app/issues/2374 29 | return; 30 | } 31 | 32 | window.addEventListener('load', () => { 33 | const swUrl = `${process.env.PUBLIC_URL}/service-worker.js`; 34 | 35 | if (isLocalhost) { 36 | // This is running on localhost. Lets check if a service worker still exists or not. 37 | checkValidServiceWorker(swUrl); 38 | 39 | // Add some additional logging to localhost, pointing developers to the 40 | // service worker/PWA documentation. 41 | navigator.serviceWorker.ready.then(() => { 42 | console.log( 43 | 'This web app is being served cache-first by a service ' + 44 | 'worker. To learn more, visit https://goo.gl/SC7cgQ' 45 | ); 46 | }); 47 | } else { 48 | // Is not local host. Just register service worker 49 | registerValidSW(swUrl); 50 | } 51 | }); 52 | } 53 | } 54 | 55 | function registerValidSW(swUrl) { 56 | navigator.serviceWorker 57 | .register(swUrl) 58 | .then(registration => { 59 | registration.onupdatefound = () => { 60 | const installingWorker = registration.installing; 61 | installingWorker.onstatechange = () => { 62 | if (installingWorker.state === 'installed') { 63 | if (navigator.serviceWorker.controller) { 64 | // At this point, the old content will have been purged and 65 | // the fresh content will have been added to the cache. 66 | // It's the perfect time to display a "New content is 67 | // available; please refresh." message in your web app. 68 | console.log('New content is available; please refresh.'); 69 | } else { 70 | // At this point, everything has been precached. 71 | // It's the perfect time to display a 72 | // "Content is cached for offline use." message. 73 | console.log('Content is cached for offline use.'); 74 | } 75 | } 76 | }; 77 | }; 78 | }) 79 | .catch(error => { 80 | console.error('Error during service worker registration:', error); 81 | }); 82 | } 83 | 84 | function checkValidServiceWorker(swUrl) { 85 | // Check if the service worker can be found. If it can't reload the page. 86 | fetch(swUrl) 87 | .then(response => { 88 | // Ensure service worker exists, and that we really are getting a JS file. 89 | if ( 90 | response.status === 404 || 91 | response.headers.get('content-type').indexOf('javascript') === -1 92 | ) { 93 | // No service worker found. Probably a different app. Reload the page. 94 | navigator.serviceWorker.ready.then(registration => { 95 | registration.unregister().then(() => { 96 | window.location.reload(); 97 | }); 98 | }); 99 | } else { 100 | // Service worker found. Proceed as normal. 101 | registerValidSW(swUrl); 102 | } 103 | }) 104 | .catch(() => { 105 | console.log( 106 | 'No internet connection found. App is running in offline mode.' 107 | ); 108 | }); 109 | } 110 | 111 | export function unregister() { 112 | if ('serviceWorker' in navigator) { 113 | navigator.serviceWorker.ready.then(registration => { 114 | registration.unregister(); 115 | }); 116 | } 117 | } 118 | -------------------------------------------------------------------------------- /web奇技淫巧/JSON数据处理-链型数据与树型数据互转/demo/demo_1.js: -------------------------------------------------------------------------------- 1 | const lineList = [ 2 | { 3 | name: 'A-1-14', 4 | id: 14, 5 | parentId: 1 6 | }, 7 | { 8 | name: 'A-4', 9 | id: 4, 10 | parentId: null 11 | }, 12 | { 13 | name: 'A-99-46', 14 | id: 9946, 15 | parentId: 99 16 | }, 17 | { 18 | name: 'A-99-23', 19 | id: 9923, 20 | parentId: 99 21 | }, 22 | { 23 | name: 'A-8', 24 | id: 8, 25 | parentId: null 26 | }, 27 | { 28 | name: 'A-1-17', 29 | id: 17, 30 | parentId: 1 31 | }, 32 | { 33 | name: 'A-8-23', 34 | id: 823, 35 | parentId: 8 36 | }, 37 | { 38 | name: 'A-1', 39 | id: 1, 40 | parentId: null 41 | }, 42 | { 43 | name: 'A-4-16', 44 | id: 416, 45 | parentId: 4 46 | }, 47 | { 48 | name: 'A-1-15', 49 | id: 15, 50 | parentId: 1 51 | }, 52 | { 53 | name: 'A-4-25', 54 | id: 425, 55 | parentId: 4 56 | }, 57 | { 58 | name: 'A-1-12', 59 | id: 12, 60 | parentId: 1 61 | }, 62 | { 63 | name: 'A-99-15', 64 | id: 9915, 65 | parentId: 99 66 | }, 67 | { 68 | name: 'A-4-23', 69 | id: 423, 70 | parentId: 4 71 | }, 72 | { 73 | name: 'A-1-16', 74 | id: 16, 75 | parentId: 1 76 | }, 77 | { 78 | name: 'A-8-46', 79 | id: 846, 80 | parentId: 8 81 | }, 82 | { 83 | name: 'A-8-15', 84 | id: 815, 85 | parentId: 8 86 | }, 87 | { 88 | name: 'A-99', 89 | id: 99, 90 | parentId: null 91 | }, 92 | { 93 | name: 'A-416-1', 94 | id: 41699, 95 | parentId: 416 96 | }, 97 | { 98 | name: 'A-416-2', 99 | id: 4169, 100 | parentId: 416 101 | } 102 | ]; 103 | 104 | function fmtLineToTree (list, idStr, parentIdStr, childrenStr) { 105 | let [newArr, newObj, _idStr, _parentIdStr, _childrenStr] = [[], {}, idStr, parentIdStr, childrenStr]; 106 | // newArr - 构建的新的空数组,即目标数组 107 | // newObj - 构建的新的对象,用于辅助处理(对象的引用) 108 | // _id - 主键的字段名 - key 109 | // _parentIdStr - 父层主键的字段名 - key 110 | // _childrenStr - 新数组中子节点的合集字段名 - key 111 | 112 | // step - 1 113 | // 将原来的数组遍历构建为以主键为key的对象合集 114 | for (let i = 0; i < list.length; i++) { 115 | newObj[list[i][_idStr]] = list[i] 116 | } 117 | 118 | // step - 3 119 | // 将原数组中每个对象的parentId作为新对象的key值,在新对象中进行查找 120 | // 如果找到,则说明数组的当前对象为此对象的子集 121 | // 如果找不到,则说明数组的当前对象为根节点 122 | for (let j = 0; j < list.length; j++) { 123 | let node = list[j]; 124 | let findNode = newObj[findNode[_parentIdStr]]; 125 | // 如果能找到 findNode - 说明此node是findNode的子集 126 | if (findNode) { 127 | // 判断是否已经存在children字段 128 | if (!findNode[_childrenStr]) { 129 | findNode[_childrenStr] = []; 130 | findNode[_childrenStr].push(node); 131 | } else { 132 | // 如果不存在 - 则为根节点 133 | newArr.push(node); 134 | } 135 | } 136 | } 137 | 138 | // 返回新数据 139 | return newArr; 140 | } -------------------------------------------------------------------------------- /web奇技淫巧/JSON数据处理-链型数据与树型数据互转/demo/demo_2.js: -------------------------------------------------------------------------------- 1 | const lineList = [ 2 | { 3 | name: 'A-1-14', 4 | id: 14, 5 | parentId: 1 6 | }, 7 | { 8 | name: 'A-4', 9 | id: 4, 10 | parentId: null 11 | }, 12 | { 13 | name: 'A-99-46', 14 | id: 9946, 15 | parentId: 99 16 | }, 17 | { 18 | name: 'A-99-23', 19 | id: 9923, 20 | parentId: 99 21 | }, 22 | { 23 | name: 'A-8', 24 | id: 8, 25 | parentId: null 26 | }, 27 | { 28 | name: 'A-1-17', 29 | id: 17, 30 | parentId: 1 31 | }, 32 | { 33 | name: 'A-8-23', 34 | id: 823, 35 | parentId: 8 36 | }, 37 | { 38 | name: 'A-1', 39 | id: 1, 40 | parentId: null 41 | }, 42 | { 43 | name: 'A-4-16', 44 | id: 416, 45 | parentId: 4 46 | }, 47 | { 48 | name: 'A-1-15', 49 | id: 15, 50 | parentId: 1 51 | }, 52 | { 53 | name: 'A-4-25', 54 | id: 425, 55 | parentId: 4 56 | }, 57 | { 58 | name: 'A-1-12', 59 | id: 12, 60 | parentId: 1 61 | }, 62 | { 63 | name: 'A-99-15', 64 | id: 9915, 65 | parentId: 99 66 | }, 67 | { 68 | name: 'A-4-23', 69 | id: 423, 70 | parentId: 4 71 | }, 72 | { 73 | name: 'A-1-16', 74 | id: 16, 75 | parentId: 1 76 | }, 77 | { 78 | name: 'A-8-46', 79 | id: 846, 80 | parentId: 8 81 | }, 82 | { 83 | name: 'A-8-15', 84 | id: 815, 85 | parentId: 8 86 | }, 87 | { 88 | name: 'A-99', 89 | id: 99, 90 | parentId: null 91 | }, 92 | { 93 | name: 'A-416-1', 94 | id: 41699, 95 | parentId: 416 96 | }, 97 | { 98 | name: 'A-416-2', 99 | id: 4169, 100 | parentId: 416 101 | } 102 | ]; 103 | 104 | function fmtLineToTree (list, idStr, parentIdStr, childrenStr) { 105 | let [newArr, newObj, _idStr, _parentIdStr, _childrenStr] = [[], {}, idStr, parentIdStr, childrenStr]; 106 | console.log(list); 107 | } 108 | 109 | fmtLineToTree(lineList); -------------------------------------------------------------------------------- /web奇技淫巧/JSON数据处理-链型数据与树型数据互转/img/tree.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LucaLJX/jianshu_demo/9fa279e2cd47560a0d23ada47104e6aeee4c558d/web奇技淫巧/JSON数据处理-链型数据与树型数据互转/img/tree.png -------------------------------------------------------------------------------- /web奇技淫巧/JSON数据处理-链型数据与树型数据互转/readme.md: -------------------------------------------------------------------------------- 1 | ## JSON数据处理-链型数据与树型数据互转 2 | 3 | ![iview 树组件示意图](./img/tree.png) 4 | 5 | > 日常工作中,前端在搭建大型后端管理平台的时候,经常会需要渲染树型结构的层级展示,在这个过程中,有时候会碰到前端返回非树型结构的数据,那么我们就需要对齐进行树型结构的数据转换。 6 | 7 | ### 链型数据转树型数据 8 | 9 | 如下json示意,我们需要将以下类型的链型数据转换为树形数据 10 | 11 | ```javascript 12 | 13 | // 原始数据 - 链型 14 | const lineList = [ 15 | { 16 | name: 'A-1-14', 17 | id: 14, 18 | parentId: 1 19 | }, 20 | { 21 | name: 'A-4', 22 | id: 4, 23 | parentId: null 24 | }, 25 | { 26 | name: 'A-99-46', 27 | id: 9946, 28 | parentId: 99 29 | }, 30 | { 31 | name: 'A-99-23', 32 | id: 9923, 33 | parentId: 99 34 | }, 35 | { 36 | name: 'A-8', 37 | id: 8, 38 | parentId: null 39 | }, 40 | { 41 | name: 'A-1-17', 42 | id: 17, 43 | parentId: 1 44 | }, 45 | { 46 | name: 'A-8-23', 47 | id: 823, 48 | parentId: 8 49 | }, 50 | { 51 | name: 'A-1', 52 | id: 1, 53 | parentId: null 54 | }, 55 | { 56 | name: 'A-4-16', 57 | id: 416, 58 | parentId: 4 59 | }, 60 | { 61 | name: 'A-1-15', 62 | id: 15, 63 | parentId: 1 64 | }, 65 | { 66 | name: 'A-4-25', 67 | id: 425, 68 | parentId: 4 69 | }, 70 | { 71 | name: 'A-1-12', 72 | id: 12, 73 | parentId: 1 74 | }, 75 | { 76 | name: 'A-99-15', 77 | id: 9915, 78 | parentId: 99 79 | }, 80 | { 81 | name: 'A-4-23', 82 | id: 423, 83 | parentId: 4 84 | }, 85 | { 86 | name: 'A-1-16', 87 | id: 16, 88 | parentId: 1 89 | }, 90 | { 91 | name: 'A-8-46', 92 | id: 846, 93 | parentId: 8 94 | }, 95 | { 96 | name: 'A-8-15', 97 | id: 815, 98 | parentId: 8 99 | }, 100 | { 101 | name: 'A-99', 102 | id: 99, 103 | parentId: null 104 | }, 105 | { 106 | name: 'A-416-1', 107 | id: 41699, 108 | parentId: 416 109 | }, 110 | { 111 | name: 'A-416-2', 112 | id: 4169, 113 | parentId: 416 114 | } 115 | ]; 116 | 117 | // 目标数据 - 树型 118 | const treeList = [ 119 | { 120 | id: 1, 121 | name: 'A-1', 122 | children: [ 123 | { 124 | id: 11, 125 | name: 'A-1-11', 126 | parentId: 1 127 | }, 128 | { 129 | id: 12, 130 | name: 'A-1-12', 131 | parentId: 1 132 | } 133 | ] 134 | } 135 | ]; 136 | ``` 137 | 138 | #### 方法一:利用对象的引用进行处理 139 | 140 | ```javascript 141 | 142 | // demo_1 143 | function fmtLineToTree (list, idStr, parentIdStr, childrenStr) { 144 | let [newArr, newObj, _idStr, _parentIdStr, _childrenStr] = [[], {}, idStr, parentIdStr, childrenStr]; 145 | // newArr - 构建的新的空数组,即目标数组 146 | // newObj - 构建的新的对象,用于辅助处理(对象的引用) 147 | // _id - 主键的字段名 - key 148 | // _parentIdStr - 父层主键的字段名 - key 149 | // _childrenStr - 新数组中子节点的合集字段名 - key 150 | 151 | // step - 1 152 | // 将原来的数组遍历构建为以主键为key的对象合集 153 | for (let i = 0; i < list.length; i++) { 154 | newObj[list[i][_idStr]] = list[i] 155 | } 156 | 157 | // step - 3 158 | // 将原数组中每个对象的parentId作为新对象的key值,在新对象中进行查找 159 | // 如果找到,则说明数组的当前对象为此对象的子集 160 | // 如果找不到,则说明数组的当前对象为根节点 161 | for (let j = 0; j < list.length; j++) { 162 | let node = list[j]; 163 | let findNode = newObj[findNode[_parentIdStr]]; 164 | // 如果能找到 findNode - 说明此node是findNode的子集 165 | if (findNode) { 166 | // 判断是否已经存在children字段 167 | if (!findNode[_childrenStr]) { 168 | findNode[_childrenStr] = []; 169 | findNode[_childrenStr].push(node); 170 | } else { 171 | // 如果不存在 - 则为根节点 172 | newArr.push(node); 173 | } 174 | } 175 | } 176 | 177 | // 返回新数据 178 | return newArr; 179 | } 180 | 181 | ``` -------------------------------------------------------------------------------- /web奇技淫巧/Native JS processing time requirements/README.md: -------------------------------------------------------------------------------- 1 | # 原生JS获取日期段及时间比较的骚操作(基本操作) 2 | 3 | ## 需求描述 4 | 5 | > 日常开发中,经常会遇到以下对于时间(日期)的操作需求: 6 | 7 | - 格式化时间为 **‘年-月-日’** 或者 **'年-月-日 时:分:秒'** 8 | - 比较两段时间的大小 9 | - 获取两段时间(日期)中间的所有时间段(日期) 10 | 11 | ## 质疑 12 | 13 | ### 来自路人甲大佬的质疑 14 | 15 | > 路人甲大佬: 为啥不用day.js或者moment.js这些现成的库 16 | 17 | ### 来自作者弱弱的回应 18 | 19 | 现在最流行的day.js轻量库以及moment.js都可以实现以上功能,但是moment.js有12kb大小,day.js仅仅2kb大小,为了时间数据的格式化引入day.js完全没有问题,但是后两个功能的实现需要引入moment.js,作者认为还不如自己写一套。 20 | 21 | ## 功能实现 22 | 23 | ### 时间格式化的实现 24 | 25 | > 此功能无非调用原生js的Date对象的 **getFullYear()** 、 **getMonth()** 、 **getDate()** 等方法获取值之后的拼接,在这里不做赘述 26 | 27 | ### 比较两段时间的大小 28 | 29 | - 笨办法 30 | 31 | > 之前作者开发中只碰到了比较两段日期的先后顺序作校验,所以采取了以下本办法 32 | 33 | Demo 1 - 比较两天大小(笨办法) 34 | 35 | ```js 36 | const day1 = '2018-11-12' 37 | const day2 = '2018-10-22' 38 | 39 | function compareDate (day1, day2) { 40 | const day1Num = parseInt((day1.split('-').join('')), 10) 41 | const day2Num = parseInt((day2.split('-').join('')), 10) 42 | const differenceCount = day2Num - day1Num 43 | console.log(differenceCount) // -90 44 | let result = differenceCount === 0 ? 45 | 'the same day' : differenceCount > 0 ? 46 | 'the day1 is earlier than the day2' : 47 | 'the day2 is earlier than the day1' 48 | return result 49 | } 50 | 51 | console.log(compareDate(day1, day2)) // the day2 is earlier than the day1 52 | ``` 53 | 54 | 问题:这种方法虽然达到了比较两个日期的大小,但是其中的差值是需要进一步处理的,不是很严谨,而且涉及要计算小时的差值,则完全没有办法使用 55 | 56 | - 利用js原生Date对象的 **getTime()** 换算处理 57 | 58 | Demo 1 - 比较两天大小(利用换算成距 1970 年 1 月 1 日之间的毫秒数) 59 | 60 | ```js 61 | function newCompareDate (day1, day2) { 62 | const day1Date = new Date(day1) 63 | const day1Time = day1Date.getTime() 64 | const day2Date = new Date(day2) 65 | const day2Time = day2Date.getTime() 66 | const differenceCount = day2Time - day1Time 67 | console.log(differenceCount) // -1814400000 68 | let result = differenceCount === 0 ? 69 | 'the same day' : differenceCount > 0 ? 70 | 'the day1 is earlier than the day2' : 71 | 'the day2 is earlier than the day1' 72 | return result 73 | } 74 | 75 | console.log(newCompareDate(day1, day2)) // the day2 is earlier than the day1 76 | ``` 77 | 78 | **利用js提供的getTime()方法换算成“距 1970 年 1 月 1 日之间的毫秒数”然后进行差值计算,如果要得到小时数或者天数,则进行进一步计算即可** 79 | 80 | ### 获取两段时间(日期)中间的所有时间段(日期) 81 | 82 | - 利用getTime()方法进行递增计算 83 | 84 | demo 2 85 | 86 | ```js 87 | function getAllDateArr (begin, end) { 88 | let arr = [] 89 | let beginArr = begin.split('-') 90 | let endArr = end.split('-') 91 | let beginDate = new Date() 92 | beginDate.setUTCFullYear(parseInt(beginArr[0], 10), parseInt(beginArr[1], 10) - 1, parseInt(beginArr[2], 10)) 93 | let endDate = new Date() 94 | endDate.setUTCFullYear(parseInt(endArr[0], 10), parseInt(endArr[1], 10) - 1, parseInt(endArr[2], 10)) 95 | let beginSec = db.getTime() - 24 * 60 * 60 * 1000 96 | let endSec = de.getTime() 97 | for (let i = beginSec; i < endSec; i++) { 98 | i = i + 24 * 60 * 60 * 1000 99 | // 使用day.js格式化日期 100 | arr.push(dayjs(new Date(i)).format('YYYY-MM-DD')) 101 | } 102 | return arr 103 | } 104 | 105 | getAllDateArr('2018-11-12', '2018-12-12') 106 | ``` 107 | 108 | ## 结语 109 | 110 | > 以上功能除了day.js之外,其他功能如果引入moment.js则差不多需要14kb内存大小,但自己实现不到20行代码则可以实现功能,所以依赖第三方库有时候可以考虑自己手动实现。 111 | 112 | ### 小tips 113 | 114 | > 作者在之前的一个国际项目中碰到一个问题:在国内前端处理好数据发送到后端,后端存储后如果在其他时区获取使用此时间,会出现时间显示的误差,原因是因为前后端时区不统一的问题,当时的解决方案是前端解决,前端只要在存储及显示的时候,获取本地的时区然后进行时间的换算即可。 115 | 116 | BY--LucaLJX ([LucaLJX的github](https://github.com/LucaLJX/jianshu_demo)) -------------------------------------------------------------------------------- /web奇技淫巧/Native JS processing time requirements/demo/demo1.js: -------------------------------------------------------------------------------- 1 | const day1 = '2018-11-12' 2 | const day2 = '2018-10-22' 3 | 4 | function compareDate (day1, day2) { 5 | const day1Num = parseInt((day1.split('-').join('')), 10) 6 | const day2Num = parseInt((day2.split('-').join('')), 10) 7 | const differenceCount = day2Num - day1Num 8 | console.log(differenceCount) // -90 9 | let result = differenceCount === 0 ? 10 | 'the same day' : differenceCount > 0 ? 11 | 'the day1 is earlier than the day2' : 12 | 'the day2 is earlier than the day1' 13 | return result 14 | } 15 | 16 | console.log(compareDate(day1, day2)) // the day2 is earlier than the day1 17 | 18 | function newCompareDate (day1, day2) { 19 | const day1Date = new Date(day1) 20 | const day1Time = day1Date.getTime() 21 | const day2Date = new Date(day2) 22 | const day2Time = day2Date.getTime() 23 | const differenceCount = day2Time - day1Time 24 | console.log(differenceCount) // -1814400000 25 | let result = differenceCount === 0 ? 26 | 'the same day' : differenceCount > 0 ? 27 | 'the day1 is earlier than the day2' : 28 | 'the day2 is earlier than the day1' 29 | return result 30 | } 31 | 32 | console.log(newCompareDate(day1, day2)) // the day2 is earlier than the day1 -------------------------------------------------------------------------------- /web奇技淫巧/Native JS processing time requirements/demo/demo2.js: -------------------------------------------------------------------------------- 1 | function getAllDateArr (begin, end) { 2 | let arr = [] 3 | let beginArr = begin.split('-') 4 | let endArr = end.split('-') 5 | let beginDate = new Date() 6 | beginDate.setUTCFullYear(parseInt(beginArr[0], 10), parseInt(beginArr[1], 10) - 1, parseInt(beginArr[2], 10)) 7 | let endDate = new Date() 8 | endDate.setUTCFullYear(parseInt(endArr[0], 10), parseInt(endArr[1], 10) - 1, parseInt(endArr[2], 10)) 9 | let beginSec = db.getTime() - 24 * 60 * 60 * 1000 10 | let endSec = de.getTime() 11 | for (let i = beginSec; i < endSec; i++) { 12 | i = i + 24 * 60 * 60 * 1000 13 | // 使用day.js格式化日期 14 | arr.push(dayjs(new Date(i)).format('YYYY-MM-DD')) 15 | } 16 | return arr 17 | } 18 | 19 | getAllDateArr('2018-11-12', '2018-12-12') -------------------------------------------------------------------------------- /web奇技淫巧/js中的slice非常规用法/demo.js: -------------------------------------------------------------------------------- 1 | // 深拷贝、 2 | const a = [1, 2, 3] 3 | let b = a 4 | b[1] = 'qwer' 5 | 6 | console.log(a) // [1, 'qwer', 3] 7 | console.log(b) // [1, 'qwer', 3] 8 | 9 | const c = [1, 2, 3] 10 | let d = c.slice(0) 11 | d[1] = 'abcd' 12 | 13 | console.log(c) // [1, 2, 3] 14 | console.log(d) // [1, 'abcd', 3] 15 | 16 | // 类型转换 17 | const e = { 18 | name: 'luck', 19 | age: 22 20 | } 21 | 22 | const f = [1, 2, 3] 23 | 24 | console.log(e.slice(0)) // TypeError: a.slice is not a function 25 | 26 | console.log([].slice.call(e, 0)) // [] 27 | 28 | console.log([].slice.call(f, 0)) // [1, 2, 3] 29 | -------------------------------------------------------------------------------- /web奇技淫巧/js中的slice非常规用法/readme.md: -------------------------------------------------------------------------------- 1 | 2 | ## 数组的splice方法 3 | 4 | ### 用法一 数组的深拷贝 5 | 6 | #### 例子 7 | 8 | ```javascript 9 | const a = [1, 2, 3] 10 | let b = a 11 | b[1] = 'qwer' 12 | 13 | console.log(a) // [1, 'qwer', 3] 14 | console.log(b) // [1, 'qwer', 3] 15 | 16 | const c = [1, 2, 3] 17 | let d = c.slice(0) 18 | d[1] = 'abcd' 19 | 20 | console.log(c) // [1, 2, 3] 21 | console.log(d) // [1, 'abcd', 3] 22 | ``` 23 | 24 | ### 用法二 对于非数组的未知参数进行类型转换,防止调用数组方法的时候报错 25 | 26 | #### 例子 27 | 28 | ```javascript 29 | const a = { 30 | name: 'luck', 31 | age: 22 32 | } 33 | 34 | const b = [1, 2, 3] 35 | 36 | console.log(a.slice(0)) // TypeError: a.slice is not a function 37 | 38 | console.log([].slice.call(a, 0)) // [] 39 | 40 | console.log([].slice.call(b, 0)) // [1, 2, 3] 41 | ``` -------------------------------------------------------------------------------- /web奇技淫巧/选项卡点击更换背景图/icons/item-0-blue.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LucaLJX/jianshu_demo/9fa279e2cd47560a0d23ada47104e6aeee4c558d/web奇技淫巧/选项卡点击更换背景图/icons/item-0-blue.png -------------------------------------------------------------------------------- /web奇技淫巧/选项卡点击更换背景图/icons/item-0-red.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LucaLJX/jianshu_demo/9fa279e2cd47560a0d23ada47104e6aeee4c558d/web奇技淫巧/选项卡点击更换背景图/icons/item-0-red.png -------------------------------------------------------------------------------- /web奇技淫巧/选项卡点击更换背景图/icons/item-1-blue.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LucaLJX/jianshu_demo/9fa279e2cd47560a0d23ada47104e6aeee4c558d/web奇技淫巧/选项卡点击更换背景图/icons/item-1-blue.png -------------------------------------------------------------------------------- /web奇技淫巧/选项卡点击更换背景图/icons/item-1-red.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LucaLJX/jianshu_demo/9fa279e2cd47560a0d23ada47104e6aeee4c558d/web奇技淫巧/选项卡点击更换背景图/icons/item-1-red.png -------------------------------------------------------------------------------- /web奇技淫巧/选项卡点击更换背景图/icons/item-2-blue.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LucaLJX/jianshu_demo/9fa279e2cd47560a0d23ada47104e6aeee4c558d/web奇技淫巧/选项卡点击更换背景图/icons/item-2-blue.png -------------------------------------------------------------------------------- /web奇技淫巧/选项卡点击更换背景图/icons/item-2-red.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LucaLJX/jianshu_demo/9fa279e2cd47560a0d23ada47104e6aeee4c558d/web奇技淫巧/选项卡点击更换背景图/icons/item-2-red.png -------------------------------------------------------------------------------- /web奇技淫巧/选项卡点击更换背景图/images/demo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LucaLJX/jianshu_demo/9fa279e2cd47560a0d23ada47104e6aeee4c558d/web奇技淫巧/选项卡点击更换背景图/images/demo.png -------------------------------------------------------------------------------- /web奇技淫巧/选项卡点击更换背景图/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 选项卡点击更换背景图 8 | 9 | 10 | 29 | 30 | 31 | 38 | 39 | -------------------------------------------------------------------------------- /web奇技淫巧/选项卡点击更换背景图/js/click.js: -------------------------------------------------------------------------------- 1 | $(document).ready(function () { 2 | watch(); 3 | }); 4 | 5 | function watch () { 6 | var length = $('.menu ul li').length; 7 | for (var i = 0; i < length; i++) { 8 | var checkClass = 'item-' + i.toString(); 9 | var hasActive = $('.' + checkClass).hasClass('item-active'); 10 | if (hasActive) { 11 | $('.' + checkClass).css('background-image', 'url("./icons/' + checkClass + '-red.png")'); 12 | } else { 13 | $('.' + checkClass).css('background-image', 'url("./icons/' + checkClass + '-blue.png")') 14 | } 15 | } 16 | } 17 | 18 | function clickItem (index) { 19 | var length = $('.menu ul li').length; 20 | for (var i = 0; i < length; i++) { 21 | var clickClass = 'item-' + i.toString(); 22 | if (index == i) { 23 | $('.' + clickClass).addClass('item-active'); 24 | } else { 25 | $('.' + clickClass).removeClass('item-active'); 26 | } 27 | } 28 | watch (); 29 | } 30 | 31 | -------------------------------------------------------------------------------- /web奇技淫巧/选项卡点击更换背景图/readme.md: -------------------------------------------------------------------------------- 1 | ### 开发环境 2 | 3 | - vue、jquery 4 | 5 | ### 需求 6 | 7 | - 点击某个选项,其选项图标改变,且鼠标移出之后,保持背景图片改变的效果 8 | 9 | ![示意图](./images/demo.png) 10 | 11 | ### 简单分析 12 | 13 | - 选项的图标分为两种状态:未选中、选中状态,有一些还需加上一个hover状态 14 | - hover可以用css实现,但是如果选项过多,则代码过于冗长 15 | 16 | ### 实现方案 17 | 18 | - 选中状态的选项动态加上特殊类名用于标识 19 | - 同一选项的图片命名为aaa-1.png、aaa-2.png的状态 20 | - 利用jquery甄别特殊类名的方法,给拥有特殊类名的选项动态改变背景图片 21 | 22 | ### 主要代码实现 23 | 24 | #### 1.结构代码 25 | 26 | ```vue-html 27 |
      28 |
    • 29 |
    • 30 |
    • 31 |
    32 | ``` 33 | 34 | #### 2.vue-js代码 35 | ```js 36 | export default { 37 | data () { 38 | return { 39 | active: 0 40 | } 41 | }, 42 | methods: { 43 | // 改变active值 44 | changeActive (index) { 45 | this.active = index; 46 | this.watchActive(); 47 | }, 48 | // 判断特殊类名:item-active,增加对应的样式 49 | watchActive () { 50 | for (var i = 0; i < 4; i++) { 51 | var checkClass = 'item-' + i.toString(); 52 | var isActive = $('.' + checkClass).hasClass('item-active'); 53 | if (isActive == false) { 54 | // 未被选中的选项,背景图为 xxx-1.png 55 | $('.' + checkClass).css('background-image', 'url("./image/' + checkClass + '-1.png")'); 56 | } else { 57 | // 被选中的选项,背景图为 xxx-2.png 58 | $('.' + checkClass).css('background-image', 'url("./image/' + checkClass + '-2.png")'); 59 | } 60 | } 61 | }, 62 | } 63 | } 64 | ``` 65 | ------------------------ 分割线 -------------------------------- 66 | 2017.08.19 67 | 68 | ### 开发环境 69 | 70 | - 纯 jquery环境 71 | 72 | ### 简书查看地址 73 | 74 | > 简书:http://www.jianshu.com/p/c6453464ee0a 75 | 76 | ### 主要代码实现 77 | 78 | #### 1.html代码 79 | 80 | ```html 81 | 88 | ``` 89 | 90 | #### 2.js代码 91 | 92 | ```javascript 93 | $(document).ready(function () { 94 | watch(); 95 | }); 96 | 97 | function watch () { 98 | var length = $('.menu ul li').length; 99 | for (var i = 0; i < length; i++) { 100 | var checkClass = 'item-' + i.toString(); 101 | var hasActive = $('.' + checkClass).hasClass('item-active'); 102 | if (hasActive) { 103 | $('.' + checkClass).css('background-image', 'url("./icons/' + checkClass + '-red.png")'); 104 | } else { 105 | $('.' + checkClass).css('background-image', 'url("./icons/' + checkClass + '-blue.png")') 106 | } 107 | } 108 | } 109 | 110 | function clickItem (index) { 111 | var length = $('.menu ul li').length; 112 | for (var i = 0; i < length; i++) { 113 | var clickClass = 'item-' + i.toString(); 114 | if (index == i) { 115 | $('.' + clickClass).addClass('item-active'); 116 | } else { 117 | $('.' + clickClass).removeClass('item-active'); 118 | } 119 | } 120 | watch (); 121 | } 122 | ``` 123 | 124 | > 如果有新的好方法,会持续更新。。。 125 | 126 | BY--LucaLJX -------------------------------------------------------------------------------- /web概念/ES6、ES7中async、await函数详解/readme.md: -------------------------------------------------------------------------------- 1 | # ES6、ES7 async、await函数详解 2 | 3 | ## 概念 4 | 5 | > async 函数就是Generator 函数的语法糖 6 | 7 | ## 解决问题 8 | 9 | - 回调地狱问题 10 | - Generator 函数的执行必须靠执行器的问题 11 | 12 | ## 与Generator 函数的区别 13 | 14 | - 内置执行器 15 | 16 | > Generator 函数的执行必须靠执行器,所以才有了co模块,而async函数自带执行器。也就是说,async函数的执行,与普通函数一模一样,只要一行。 17 | 18 | - 更好的语义 19 | 20 | > async和await,比起星号和yield,语义更清楚了。async表示函数里有异步操作,await表示紧跟在后面的表达式需要等待结果。 21 | 22 | - 更广的适用性 23 | 24 | > co模块约定,yield命令后面只能是 Thunk 函数或 Promise 对象,而async函数的await命令后面,可以是 Promise 对象和原始类型的值(数值、字符串和布尔值,但这时等同于同步操作)。 25 | 26 | - 返回值是 Promise 27 | 28 | > async函数的返回值是 Promise 对象,这比 Generator 函数的返回值是 Iterator 对象方便多了。你可以用then方法指定下一步的操作。 29 | 30 | ## 示例 31 | 32 | ```javascript 33 | // 定义async中需要调用的promise对象函数 34 | function testResult () { 35 | // 必须return一个promis对象,否则会等同于一个同步操作 36 | return new Promise(function (resolve, reject) { 37 | // 内部可以是一个http请求等异步操作 38 | $.post('xxx', function (res, err) { 39 | if (err) { 40 | return reject(err); 41 | } 42 | resolve(res); 43 | }); 44 | }); 45 | } 46 | 47 | // 定义一个async函数 48 | // 函数定义前必须加 async 关键词 49 | async function testFun () { 50 | const result1 = await testResult(); 51 | } 52 | 53 | // async 函数的调用跟普通函数一样 54 | testFun(); 55 | ``` 56 | 57 | > [es 6 入门 - async函数](http://es6.ruanyifeng.com/#docs/async) 58 | 59 | 扩展阅读: 60 | 61 | > [es 6 入门 - generator函数](http://es6.ruanyifeng.com/#docs/generator) 62 | 63 | > [CSDN - asnyc函数详解](https://blog.csdn.net/y491887095/article/details/80807845) 64 | 65 | ## asnyc函数的异常处理 66 | 67 | ```javascript 68 | async function doCheck(){ 69 | let result; 70 | try{ 71 | result = await check(); 72 | } 73 | catch(e){ 74 | console.log('error occurs'); 75 | } 76 | } 77 | ``` -------------------------------------------------------------------------------- /web概念/事件委托/README.md: -------------------------------------------------------------------------------- 1 | > 参考:https://www.cnblogs.com/liugang-vip/p/5616484.html -------------------------------------------------------------------------------- /web概念/事件委托/demo/demo.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Document 8 | 15 | 16 | 17 | 18 |
    19 |
      20 |
    • 12
    • 21 |
    • 22
    • 22 |
    • 32
    • 23 |
    • 33
    • 24 |
    25 |
    26 | 27 | 28 | 29 | 68 | 69 | -------------------------------------------------------------------------------- /前端常用概念/移动端触摸事件(touchstart、touchmove、touchend)/README.md: -------------------------------------------------------------------------------- 1 | ### 移动端touch事件 2 | 3 | #### 触摸事件 4 | 5 | - touchstart: 手指触摸屏幕时触发 6 | - touchmove: 手指在屏幕上连续滑动时触发 7 | - touchend: 手指从屏幕移开时触发 8 | - touchcancel: 当系统停止跟踪触摸时触发 9 | 10 | #### touch对象的属性 11 | 12 | - clientX : 触摸目标在视口中的x坐标 13 | - clientY : 触摸目标在视口中的Y坐标 14 | - pageX : 触摸目标在页面中的X坐标 15 | - pageY : 触摸目标在页面中的Y坐标 16 | - screenX : 触摸目标在屏幕中的X坐标 17 | - screenY : 触摸目标在屏幕中的Y坐标 18 | - identifier : 标识触摸的唯一ID 19 | - target : 触摸的节点目标 -------------------------------------------------------------------------------- /前端常用概念/移动端触摸事件(touchstart、touchmove、touchend)/demo/index.html: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LucaLJX/jianshu_demo/9fa279e2cd47560a0d23ada47104e6aeee4c558d/前端常用概念/移动端触摸事件(touchstart、touchmove、touchend)/demo/index.html -------------------------------------------------------------------------------- /小程序/小程序开发-mpvue中使用图表库/img/demo-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LucaLJX/jianshu_demo/9fa279e2cd47560a0d23ada47104e6aeee4c558d/小程序/小程序开发-mpvue中使用图表库/img/demo-1.png -------------------------------------------------------------------------------- /小程序/小程序开发-mpvue中使用图表库/img/show.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LucaLJX/jianshu_demo/9fa279e2cd47560a0d23ada47104e6aeee4c558d/小程序/小程序开发-mpvue中使用图表库/img/show.gif -------------------------------------------------------------------------------- /小程序/小程序开发-mpvue中使用图表库/readme.md: -------------------------------------------------------------------------------- 1 | # 小程序开发-mpvue中使用图表库 2 | 3 | ![antV-F2](./img/show.gif) 4 | 5 | ## 图表库 6 | 7 | 在开发中使用图表库,推荐百度的 **Echarts**,和阿里出品的 **AntV**家族,其中移动端为 **AntV-F2** 8 | 9 | - Echarts:[Echarts](http://echarts.baidu.com/) 10 | - AntV-F2:[AntV-F2](https://antv.alipay.com/zh-cn/f2/3.x/index.html) 11 | 12 | 这里antV-F2有现成的原生小程序使用教程(wx-f2:[wx-f2](https://github.com/antvis/wx-f2)),就不做赘述,只讲解如何在mpvue框架中使用。 13 | 14 | ## mpvue框架中使用 Echarts 15 | 16 | > 参考文档: [mpvue-echarts](https://github.com/F-loat/mpvue-echarts) 17 | 18 | - 安装 **mpvue-echarts** 及 **echarts** 插件 19 | 20 | ```shell 21 | ## mpvue-echarts 22 | npm i mpvue-echarts --save 23 | ## echarts 24 | npm i echarts --save 25 | ``` 26 | 27 | - vue文件中以组件的形式使用 28 | 29 | ```html 30 | 35 | 36 | 68 | 69 | 75 | ``` 76 | 77 | ## mpvue框架中使用antV-F2 78 | 79 | antV-F2官方给出了小程序原生的使用方式,并无vue相关的依赖插件,所以我们把小程序原生插件放在 ** “static” ** 文件夹中进行使用 80 | 81 | **注意:这里不能放在"src"目录下,防止被webpack工具打包** 82 | 83 | **wx-f2中看 ff-canvas 源码可以看出. vue文件data内部的opts.onInit 是一个 function 不能被传递到组件上, 通过主动调用 ff-canvas组件 的 init 方法, 并且将initChart传入就即正常使用了** 84 | 85 | - 将wx-f2对应的导入项目static文件夹中 86 | 87 | ![static](./img/demo-1.png) 88 | 89 | - 项目src目录下pages.js以小程序的方式引入wx-f2组件配置 90 | 91 | ```javascript 92 | module.exports = [ 93 | { 94 | path: 'pages/testF2/index', // 页面路径,同时是 vue 文件相对于 src 的路径 95 | config: { 96 | // 引入使用wx-f2组件 97 | usingComponents: { 98 | 'ff-canvas': '/static/libs/f2-canvas/f2-canvas' 99 | } 100 | } 101 | }, 102 | { 103 | path: 'packageA/logs', 104 | subPackage: true, 105 | config: { // 页面配置,即 page.json 的内容 106 | navigationBarTitleText: '查看启动日志' 107 | } 108 | } 109 | ] 110 | ``` 111 | 112 | - vue文件中使用 113 | 114 | ** 注意:mpvue中使用必须以懒加载的形式使用,即主动触发渲染,否则会失败 ** 115 | 116 | ```html 117 | 118 | 124 | 125 | 216 | 217 | 219 | ``` 220 | 221 | **注意:** 222 | 223 | 1. 建议 ‘ this.$mp.page.selectComponent('#column').init(initChart) ’ 这行代码写在vue生命周期mounted里面,而不是小程序onLoad里面,因为vue页面加载在小程序后面 224 | 225 | 2. wx-f2中的tooltip失效,并未使用成功,后续待解决(touch事件绑定成功,但对应的图表效果没有) 226 | 227 | > -- LucaLJX: [github:https://github.com/LucaLJX](https://github.com/LucaLJX/jianshu_demo) 228 | -------------------------------------------------------------------------------- /小程序/小程序开发-mpvue构建小程序项目-1-踩坑/readme.md: -------------------------------------------------------------------------------- 1 | 2 | # 小程序开发-mpvue构建小程序项目-1-踩坑 3 | 4 | ## mpvue-entry 插件引入 5 | 6 | - mpvue的坑 7 | - mpvue新增页面或者模块的时候必须重新npm run dev才可以进行更新,不支持热更新 8 | - mpvue所有页面模块.vue文件都需要写main.js,重复工作 9 | 10 | - 用途 11 | - 支持小程序热更新 12 | - 重新定义mpvue小程序页面配置 13 | 14 | > 参考文档: [npm-mpvue-entry](https://www.npmjs.com/package/mpvue-entry?activeTab=readme) 15 | 16 | > 重点参考文档: [https://github.com/F-loat/mpvue-entry](https://github.com/F-loat/mpvue-entry) 17 | 18 | > 参考配置文档: [mpvue-issue-F-loat的回答](https://github.com/Meituan-Dianping/mpvue/issues/590) 19 | 20 | 21 | - npm安装mpvue-entry依赖包 22 | 23 | ```shell 24 | npm i mpvue-entry --save 25 | ``` 26 | 27 | **操作了半天,mpvue-entry的作者为了方便大家使用,开源了基于mpvue-entry的模版,大家可以直接使用这个quickStart进行构建项目** 28 | 29 | > mpvue-entry-quickStart 项目地址: [mpvue-entry-quickStart](https://github.com/F-loat/mpvue-quickstart) 30 | 31 | - mpvue-entry-quickStart 构建项目 32 | 33 | ```shell 34 | $ npm install -g vue-cli 35 | $ vue init F-loat/mpvue-quickstart my-project 36 | $ cd my-project 37 | $ npm install 38 | $ npm run dev 39 | ``` 40 | 41 | ## mpvue-entry-quickStart模版使用方式 42 | 43 | ### 新增页面 44 | 45 | - 在 **src/pages/**目录下新增.vue文件 46 | - src/pages.js 文件新增页面路径 47 | - 支持热更新,无需重启 48 | 49 | > 参考文档: [https://github.com/F-loat/mpvue-entry](https://github.com/F-loat/mpvue-entry) 50 | 51 | ```javascript 52 | // pages.js 53 | module.exports = [ 54 | { 55 | path: 'pages/news/list', // 页面路径,同时是 vue 文件相对于 src 的路径,必填 56 | config: { // 页面配置,即 page.json 的内容,可选 57 | navigationBarTitleText: '文章列表', 58 | enablePullDownRefresh: true 59 | } 60 | } 61 | ] 62 | ``` 63 | 64 | ### mpvue-entry 使用注意事项 65 | 66 | #### 分包与主包的配置 67 | 68 | - 主包的页面必须放在项目默认 src/pages/ 文件夹下面 69 | - 分包的页面配置,必须加上subPackage参数 70 | 71 | ```javascript 72 | module.exports = [ 73 | // 主包 74 | { 75 | path: 'pages/cardList/index', // 页面路径,同时是 vue 文件相对于 src 的路径 76 | }, { 77 | path: 'pages/card/index' 78 | }, 79 | // 分包 80 | { 81 | path: 'packageA/logs', 82 | subPackage: true, 83 | config: { // 页面配置,即 page.json 的内容 84 | navigationBarTitleText: '查看启动日志' 85 | } 86 | } 87 | ] 88 | ``` 89 | 90 | ## 小程序默认tabBar配置 91 | 92 | > 参考文档: [小程序文档-配置](https://developers.weixin.qq.com/miniprogram/dev/framework/config.html) 93 | 94 | ### 用途 95 | 96 | - 小程序提供的默认展示在底部的tab菜单栏 97 | 98 | ### 配置方式 99 | 100 | - src/main.js 文件中添加config 101 | 102 | ```javascript 103 | // 主 main.js 文件 104 | import Vue from 'vue' 105 | import App from '@/App' 106 | import store from '@/store' 107 | 108 | Vue.config.productionTip = false 109 | App.store = store 110 | App.mpType = 'app' 111 | 112 | const app = new Vue(App) 113 | app.$mount() 114 | 115 | export default { 116 | // 这个字段走 app.json 117 | config: { 118 | pages: [], 119 | window: { 120 | backgroundTextStyle: 'light', 121 | navigationBarBackgroundColor: '#fff', 122 | navigationBarTitleText: 'WeChat', 123 | navigationBarTextStyle: 'black' 124 | }, 125 | // tabBar 配置 126 | tabBar: { 127 | backgroundColor: "#fafafa", 128 | borderStyle: "white", 129 | selectedColor: "#b4282d", 130 | color: "#666", 131 | list: [ 132 | { 133 | pagePath: "pages/cardList/index", 134 | iconPath: "static/images/ic_menu_choice_nor.png", 135 | selectedIconPath: "static/images/ic_menu_choice_pressed.png", 136 | text: "cardlist" 137 | }, 138 | { 139 | pagePath: "pages/card/index", 140 | iconPath: "static/images/ic_menu_choice_nor.png", 141 | selectedIconPath: "static/images/ic_menu_choice_pressed.png", 142 | text: "card" 143 | }, 144 | ] 145 | } 146 | } 147 | } 148 | ``` 149 | 150 | BY-Luca_LJX([git地址](https://github.com/LucaLJX/jianshu_demo/tree/master/%E5%B0%8F%E7%A8%8B%E5%BA%8F/%E5%B0%8F%E7%A8%8B%E5%BA%8F%E5%BC%80%E5%8F%91-mpvue%E6%9E%84%E5%BB%BA%E5%B0%8F%E7%A8%8B%E5%BA%8F%E9%A1%B9%E7%9B%AE-1-%E8%B8%A9%E5%9D%91) 151 | 152 | -------------------------------------------------------------------------------- /小程序/小程序开发-利用canvas实现保存二维码海报到本机/README.md: -------------------------------------------------------------------------------- 1 | # 小程序开发-利用canvas实现保存二维码海报到本机 2 | 3 | ![小程序效果](./img/demo.jpg) 4 | 5 | ## 场景及需求 6 | 7 | > 在小程序开发过程中,经常需要实现保存某个页面为带小程序码的二维码海报图片到本地,然后用于分享或者发朋友圈等操作。 8 | 9 | ## 主要技术点及小程序相关api 10 | 11 | ### 技术注意事项 12 | 13 | - 小程序的canvas与H5 canvas使用api大部分一致,但由于小程序中没有DOM节点的概念,所以不能使用很多现成的工具库 14 | - 小程序canvas默认宽度300px、高度225px 15 | - 小程序canvas相关的api中单位为px,并非rpx,所以在业务实现过程中需要处理适配 16 | - 小程序canvas对跨域图片不支持,需要先将图片缓存到本地 17 | 18 | ### 技术点 19 | 20 | - wx.getImageInfo() 21 | - wx.getSystemInfo() 22 | - wx.canvasToTempFilePath() 23 | - wx.saveImageToPhotosAlbum() 24 | - canvas渲染相关api 25 | 26 | ### 整体流程 27 | 28 | 1.先获取所有图片资源,线上图片需要缓存到本地,使用图片的本地路径做渲染 29 | 30 | 2.获取设备信息,根据设备宽度计算出宽度因子x 31 | 32 | 3.绘制canvas 33 | 34 | 4.将canvas转化为图片,将图片保存到本机 35 | 36 | ## 实现 37 | 38 | ### 宽度因子x及元素宽度的尺寸计算 39 | 40 | > rpx(responsive pixel): 可以根据屏幕宽度进行自适应。规定屏幕宽为750rpx。如在 iPhone6 上,屏幕宽度为375px,共有750个物理像素,则750rpx = 375px = 750物理像素,1rpx = 0.5px = 1物理像素。 41 | 42 | > 参考文档: [小程序-WXSS-尺寸单位](https://developers.weixin.qq.com/miniprogram/dev/framework/view/wxss.html) 43 | 44 | 由文档可以看出不同的设备,宽度是不同数值的px,但是均为750rpx,由此可以利用750rpx计算出宽度因子x: 45 | 46 | > 设备宽度 / 750 47 | 48 | 以设计稿宽度750rpx为例,则在不同设备下,设计稿宽度y在不同设备下的px宽度均为: 49 | 50 | > Y = y * x 51 | 52 | > Y = y * (设备宽度 / 750) 53 | 54 | 由此,在实现中,canvas标签宽高需要设置为变量,且使用微信提供的 **wx.getSystemInfo()** 接口获取设备宽度信息后,进行计算 55 | 56 | ```html 57 | 58 | ``` 59 | 60 | ```javascript 61 | // wx.js 62 | // 获取设备基本信息 63 | export function wxGetSystemInfo () { 64 | return new Promise(async function (resolve, reject) { 65 | wx.getSystemInfo({ 66 | success: function (res) { 67 | resolve(res); 68 | }, 69 | fail: function (err) { 70 | reject(err); 71 | } 72 | }) 73 | }); 74 | } 75 | // demo.vue 76 | // 获取手机基本信息 77 | async getPhoneSystemInfo () { 78 | let _this = this; 79 | let systemInfoRes = await wxGetSystemInfo(); 80 | _this.canvas.width = systemInfoRes.screenWidth; 81 | // 设计稿宽高为 750 * 912 82 | _this.canvas.height = 912 / 750 * _this.canvas.width; 83 | _this.storageQrcode(); 84 | }, 85 | ``` 86 | 87 | ### 获取远程图片缓存到本地使用 88 | 89 | - api 90 | - wx.getImageInfo() 91 | - 参考文档: [wx.getImageInfo()](https://developers.weixin.qq.com/miniprogram/dev/api/media-picture.html#wxgetimageinfoobject) 92 | 93 | 注意:**如果是本地图片,即'static'文件夹中的图片,如'/static/logo.png'经过wx.getImageInfo返回的path会省去开头的'/',即'static/logo.png',这会导致拿不到资源,所以本地图片不需要调用wx.getImageInfo()进行本地缓存** 94 | 95 | ```javascript 96 | // wx.js 97 | // 获取图片基本信息 98 | export function wxGetImgInfo (imgUrl) { 99 | return new Promise(async function (resolve, reject) { 100 | wx.getImageInfo({ 101 | src: imgUrl, 102 | success: function (res) { 103 | resolve(res); 104 | }, 105 | fail: function (err) { 106 | reject(err); 107 | } 108 | }) 109 | }); 110 | } 111 | // demo.vue 112 | // 缓存远程图片 113 | async storageQrcode () { 114 | let _this = this; 115 | // 背景图url转path 116 | // let bgRes = await wxGetImgInfo(_this.bgImg); 117 | // _this.imgPath.bgImg = bgRes.path; 118 | // Logo url转path 119 | // let logoRes = await wxGetImgInfo(_this.logo); 120 | // _this.imgPath.logo = logoRes.path; 121 | 122 | // 头像 url转path 123 | // let headerImgRes = await wxGetImgInfo(_this.cardDetail.header); 124 | // _this.imgPath.headerImg = headerImgRes.path; 125 | // 二维码 url转path 126 | let qrCodeRes = await wxGetImgInfo(_this.qrCode); 127 | _this.imgPath.qrCode = qrCodeRes.path; 128 | console.log(_this.imgPath); 129 | // _this.initCanvas(_this.canvas.width); 130 | }, 131 | ``` 132 | 133 | ### 绘制canvas 134 | 135 | canvas绘制主要用到了图片绘制、文字绘制,图片绘制及文字绘制的时候,需要引入上文说到的宽度因子x进行计算 136 | 137 | #### 图片绘制 138 | 139 | - canvasContext.drawImage 140 | - 参考文档: [canvasContext.drawImage](https://developers.weixin.qq.com/miniprogram/dev/api/canvas/draw-image.html) 141 | 142 | 这里的图片绘制之前先计算宽度因子 143 | 144 | ```javascript 145 | let _this = this; 146 | // variableVal即为上文拿到的设备宽度 147 | const variableNum = variableVal / 750; // 根据设备宽度算出一个rpx为多少px 148 | const ctx = wx.createCanvasContext('qrcode-canvas'); 149 | // 清除画布上矩形的内容 150 | ctx.clearRect(0, 0, 0, 0); 151 | // 绘制上部card背景图 152 | const bgImgDesc = { 153 | url: _this.imgPath.bgImg, 154 | left: 0, 155 | top: 0, 156 | width: 750 * variableNum, 157 | height: 912 * variableNum 158 | }; 159 | ctx.drawImage(bgImgDesc.url, bgImgDesc.left, bgImgDesc.top, bgImgDesc.width, bgImgDesc.height); 160 | ctx.draw(); 161 | ``` 162 | 163 | #### 文字的绘制 164 | 165 | - canvasContext.setFillStyle - 设置颜色 166 | - canvasContext.setFontSize - 设置大小 167 | - canvasContext.fillText - 填充文本 168 | 169 | ```javascript 170 | const nameDesc = { 171 | text: _this.cardDetail.name, 172 | fontSize: 36 * variableNum, 173 | color: '#4F5E6F', 174 | left: 102 * variableNum, 175 | top: 200 * variableNum 176 | }; 177 | ctx.setFillStyle(nameDesc.color); 178 | ctx.setFontSize(nameDesc.fontSize); 179 | ctx.fillText(nameDesc.text, nameDesc.left, nameDesc.top); 180 | ctx.draw(); 181 | ``` 182 | 183 | ### canvas转图片并保存到本地 184 | 185 | - wx.canvasToTempFilePath - canvas转图片 186 | - 参考文档: [wx.canvasToTempFilePath](https://developers.weixin.qq.com/miniprogram/dev/api/canvas/temp-file.html) 187 | - wx.saveImageToPhotosAlbum - 保存图片到本地 188 | - 参考文档: [wx.saveImageToPhotosAlbum](https://developers.weixin.qq.com/miniprogram/dev/api/media-picture.html#wxgetimageinfoobject) 189 | 190 | #### 注意点 191 | 192 | **tip: wx.canvasToTempFilePath() 在 draw 回调里调用该方法才能保证图片导出成功。** 193 | 194 | 由于导出图片需要在canvas绘制图片的draw()方法回调中使用才能,所以我们在绘制canvas的时候直接转canvas为图片,然后将路径存下来,点击下载的时候,再直接拿图片路径进行下载操作。 195 | 196 | ```javascript 197 | // wx.js 198 | // canvas画布转图片 199 | export function wxCanvasToTempFilePath (canvasObj) { 200 | return new Promise(async function (resolve, reject) { 201 | wx.canvasToTempFilePath({ 202 | x: canvasObj.x, 203 | y: canvasObj.y, 204 | width: canvasObj.width, 205 | height: canvasObj.height, 206 | destWidth: canvasObj.destWidth, 207 | destHeight: canvasObj.destHeight, 208 | canvasId: canvasObj.canvasId, 209 | fileType: canvasObj.fileType ? canvasObj.fileType : 'png', 210 | success: function (res) { 211 | resolve(res); 212 | }, 213 | fail: function (err) { 214 | reject(err); 215 | } 216 | }) 217 | }); 218 | } 219 | // demo.vue 220 | // 绘制canvas 221 | initCanvas () { 222 | let _this = this; 223 | // 绘制canvas 224 | ...... 225 | ctx.draw(false, function () { 226 | _this.saveImg(); 227 | }); 228 | }, 229 | // 将canvas转为图片 230 | async saveImg () { 231 | let _this = this; 232 | const canvasObj = { 233 | x: 0, 234 | y: 0, 235 | width: _this.canvas.width, 236 | height: _this.canvas.height, 237 | destWidth: _this.canvas.width * 4, 238 | destHeight: _this.canvas.height * 4, 239 | canvasId: 'qrcode-canvas', 240 | fileType: 'png' 241 | }; 242 | let imgRes = await wxCanvasToTempFilePath(canvasObj); 243 | _this.qrCodeImgPath = imgRes.tempFilePath; 244 | }, 245 | ``` 246 | 247 | ### 下载图片到本地 248 | 249 | ```html 250 | 251 | 252 | ``` 253 | 254 | ```javascript 255 | // wx.js 256 | // 保存图片到本地 257 | export function wxSaveImageToPhotosAlbum (filePath) { 258 | return new Promise(async function (resolve, reject) { 259 | wx.saveImageToPhotosAlbum({ 260 | filePath: filePath, 261 | success: function (res) { 262 | 263 | resolve(res); 264 | }, 265 | fail: function (err) { 266 | reject(err); 267 | } 268 | }) 269 | }); 270 | } 271 | // demo.vue 272 | // 保存图片到本机 273 | async downLoadImg () { 274 | let _this = this; 275 | let saveRes = await wxSaveImageToPhotosAlbum(_this.qrCodeImgPath); 276 | if (saveRes.errMsg === 'saveImageToPhotosAlbum:ok') { 277 | wx.showToast({ 278 | duration: 3000, 279 | icon: 'none', 280 | title: '保存图片成功!', 281 | mask: true 282 | }); 283 | } else { 284 | wx.showToast({ 285 | duration: 3000, 286 | icon: 'none', 287 | title: '保存图片失败,请重试!', 288 | mask: true 289 | }); 290 | } 291 | }, 292 | ``` 293 | 294 | > demo代码已经放在 **'./demo'** 文件夹,欢迎交流 295 | 296 | ## 总结 297 | 298 | 在业务实现中,我们只要把业务流程进行分割,然后一步一步去实现,捋明白流程之后各个击破很多第一反应去查找已有的库来实现的功能自己实现起来也很简单。 299 | 300 | **在某种意义上,自己弄明白原理之后去实现反而更加轻松,更加得心应手。** 301 | 302 | 303 | 304 | > -- LucaLJX: [github:https://github.com/LucaLJX](https://github.com/LucaLJX/jianshu_demo) 305 | 306 | -------------------------------------------------------------------------------- /小程序/小程序开发-利用canvas实现保存二维码海报到本机/demo/index.vue: -------------------------------------------------------------------------------- 1 | 10 | 11 | 222 | 223 | 229 | -------------------------------------------------------------------------------- /小程序/小程序开发-利用canvas实现保存二维码海报到本机/demo/readme.md: -------------------------------------------------------------------------------- 1 | 由于技术框架是mpvue,所以demo是基于vue实现的,小程序原生实现可以参考本demo -------------------------------------------------------------------------------- /小程序/小程序开发-利用canvas实现保存二维码海报到本机/demo/wx.js: -------------------------------------------------------------------------------- 1 | // 获取图片基本信息 2 | export function wxGetImgInfo (imgUrl) { 3 | return new Promise(async function (resolve, reject) { 4 | wx.getImageInfo({ 5 | src: imgUrl, 6 | success: function (res) { 7 | resolve(res); 8 | }, 9 | fail: function (err) { 10 | reject(err); 11 | } 12 | }) 13 | }); 14 | } 15 | 16 | // 下载文件 17 | export function wxDownloadFile (fileUrl) { 18 | return new Promise(async function (resolve, reject) { 19 | wx.downloadFile({ 20 | url: fileUrl, 21 | success: function (res) { 22 | resolve(res); 23 | }, 24 | fail: function (err) { 25 | reject(err); 26 | } 27 | }) 28 | }); 29 | } 30 | 31 | // 获取设备基本信息 32 | export function wxGetSystemInfo () { 33 | return new Promise(async function (resolve, reject) { 34 | wx.getSystemInfo({ 35 | success: function (res) { 36 | resolve(res); 37 | }, 38 | fail: function (err) { 39 | reject(err); 40 | } 41 | }) 42 | }); 43 | } 44 | 45 | // canvas画布转图片 46 | export function wxCanvasToTempFilePath (canvasObj) { 47 | return new Promise(async function (resolve, reject) { 48 | wx.canvasToTempFilePath({ 49 | x: canvasObj.x, 50 | y: canvasObj.y, 51 | width: canvasObj.width, 52 | height: canvasObj.height, 53 | destWidth: canvasObj.destWidth, 54 | destHeight: canvasObj.destHeight, 55 | canvasId: canvasObj.canvasId, 56 | fileType: canvasObj.fileType ? canvasObj.fileType : 'png', 57 | success: function (res) { 58 | resolve(res); 59 | }, 60 | fail: function (err) { 61 | reject(err); 62 | } 63 | }) 64 | }); 65 | } 66 | 67 | // 保存图片到本地 68 | export function wxSaveImageToPhotosAlbum (filePath) { 69 | return new Promise(async function (resolve, reject) { 70 | wx.saveImageToPhotosAlbum({ 71 | filePath: filePath, 72 | success: function (res) { 73 | 74 | resolve(res); 75 | }, 76 | fail: function (err) { 77 | reject(err); 78 | } 79 | }) 80 | }); 81 | } -------------------------------------------------------------------------------- /小程序/小程序开发-利用canvas实现保存二维码海报到本机/img/demo.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LucaLJX/jianshu_demo/9fa279e2cd47560a0d23ada47104e6aeee4c558d/小程序/小程序开发-利用canvas实现保存二维码海报到本机/img/demo.jpg -------------------------------------------------------------------------------- /小程序/小程序开发-常用api汇总/readme.md: -------------------------------------------------------------------------------- 1 | # 小程序开发-常用api汇总 2 | 3 | ## 页面跳转api 4 | 5 | - wx.navigateTo() 6 | 7 | > 保留当前页面,跳转到应用内的某个页面,使用wx.navigateBack可以返回到原页面。 8 | 9 | **需要跳转的应用内非 tabBar 的页面的路径 , 路径后可以带参数。参数与路径之间使用?分隔,参数键与参数值用=相连,不同参数用&分隔;如 'path?key=value&key2=value2'** 10 | 11 | **注意:目前页面路径最多只能十层。** 12 | 13 | ```javascript 14 | wx.navigateTo({ 15 | // 跳转的路径 16 | url: '../login/login?id='+e.currentTarget.id, 17 | // 调用成功的回调函数 18 | success: function (e) { 19 | console.log(e) 20 | }, 21 | // 调用失败的回调函数 22 | fail: function (e) {}, 23 | // 调用结束的回调函数(成功、失败都会执行) 24 | complete: function (e) {} 25 | }) 26 | ``` 27 | 28 | - wx.redirectTo(OBJECT) 29 | 30 | > 关闭当前页面,跳转到应用内的某个页面。 31 | 32 | **需要跳转的应用内非 tabBar 的页面的路径,路径后可以带参数。参数与路径之间使用?分隔,参数键与参数值用=相连,不同参数用&分隔;如 'path?key=value&key2=value2'** 33 | 34 | 示例代码同 **wx.navigateTo()** 35 | 36 | - wx.reLaunch(OBJECT) 37 | 38 | > 关闭所有页面,打开到应用内的某个页面。 39 | 40 | **需要跳转的应用内页面路径 , 路径后可以带参数。参数与路径之间使用?分隔,参数键与参数值用=相连,不同参数用&分隔;如 'path?key=value&key2=value2',如果跳转的页面路径是 tabBar 页面则不能带参数** 41 | 42 | 示例代码同 **wx.navigateTo()** 43 | 44 | - **wx.switchTab(OBJECT)** 45 | 46 | > **跳转到 tabBar 页面,并关闭其他所有非 tabBar 页面** 47 | 48 | **需要跳转的 tabBar 页面的路径(需在 app.json 的 tabBar 字段定义的页面),路径后不能带参数** 49 | 50 | - **wx.navigateBack(OBJECT)** 51 | 52 | > 关闭当前页面,返回上一页面或多级页面。可通过 getCurrentPages() 获取当前的页面栈,决定需要返回几层。 53 | 54 | **delta: 返回的页面数,如果 delta 大于现有页面数,则返回到首页。** 55 | 56 | ```javascript 57 | wx.navigateBack({ 58 | delta: num // 返回的页面数,如果 delta 大于现有页面数,则返回到首页 59 | }) 60 | ``` 61 | 62 | ### 页面跳转注意点 63 | 64 | - **wx.navigateTo 和 wx.redirectTo 不允许跳转到 tabbar 页面,只能用 wx.switchTab 跳转到 tabbar 页面** 65 | - **tabbar 页面无法传参,如果需要传参,可以尝试使用全局变量** 66 | - **tabbar 页面跳转,导航栏不会出现返回按钮,使用 wx.navigateTo 返回按钮会自动出现** 67 | 68 | ## 参数传递与获取 69 | 70 | 由于小程序页面跳转的相关限制,在参数传递上,有三种解决方案,根据不同的使用场景选择对应的解决方案。 71 | 72 | 1. 小程序页面路由(原生)传参 73 | 2. 小程序全局变量传参 74 | 3. mpvue (vuex) store传参 - 不做赘述 75 | 76 | - url传参 77 | 78 | ```html 79 | 80 | 81 | 跳转到新页面 82 | ``` 83 | 84 | ```javascript 85 | wx.navigateTo({ 86 | url: 'test?id=1' // 参数: id = 1 87 | }) 88 | ``` 89 | 90 | - 从url中取参 91 | 92 | > [小程序生命周期onload()中取参](https://developers.weixin.qq.com/miniprogram/dev/framework/app-service/page.html#%E7%94%9F%E5%91%BD%E5%91%A8%E6%9C%9F%E5%9B%9E%E8%B0%83%E5%87%BD%E6%95%B0) 93 | 94 | ```javascript 95 | onLoad (query) { 96 | console.log(query); // {id: 1} 97 | }, 98 | ``` 99 | 100 | > mpvue 方法取参 101 | 102 | ```javascript 103 | console.log(this.$root.$mp.query.id); // 1 104 | ``` 105 | 106 | -------------------------------------------------------------------------------- /小程序/小程序开发-简明文档-Part 1/README.md: -------------------------------------------------------------------------------- 1 | # 小程序开发-简明文档-Part 1 2 | 3 | ### hidden 与 wx:if 4 | 5 | 小程序 **hidden** 和 **wx:if** 等同于vue中的 **v-show** 与 **v-if** 6 | 7 | ```html 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | ``` 20 | 21 | ### wx:for 22 | 23 | 小程序 **wx:for** 等同于vue中的 **v-for** 24 | 25 | - items 、 item 26 | - 小程序默认元素的item, index属性,不需要像vue一样指定 27 | - wx:key 28 | - 小程序也需要指定元素的key值,否则会警告 29 | - wx:for-item 、 wx:for-index 30 | - 小程序可以指定元素的变量名 31 | 32 | ```html 33 | 34 | 35 | 36 | {{ index }}: {{ item.name }} 37 | 38 | 39 | 40 | {{ id }}: {{ user.name }} 41 | 42 | ``` 43 | 44 | ### 小程序原生数组的处理 45 | 46 | 小程序中修改数据不可以像普通js文件中一样直接 **key = value** 格式进行赋值,必须调用小程序自带的 **setData()** 函数进行数据更新 47 | 48 | - 数组的赋值 49 | 50 | ```javascript 51 | Page({ 52 | data: { 53 | list: [] 54 | }, 55 | initList: function () { 56 | let newList = [1, 2, 3] 57 | // 直接赋值不奏效 58 | // this.list = [...newList] 59 | // 调用 setData() 函数进行赋值 60 | this.setData({ 61 | list: newList 62 | }) 63 | } 64 | }) 65 | ``` 66 | 67 | - 数组的追加 68 | - 小程序中对数组进行push操作是无效的 69 | 70 | ```javascript 71 | Page({ 72 | data: { 73 | list: [], 74 | list2: [1, 2, 3] 75 | }, 76 | // 构建新数组,进行替换 77 | addList: function () { 78 | let _this = this 79 | let newList = [] 80 | this.data.list2.map(item => { 81 | newList.push(item) 82 | }) 83 | // 直接在list上操作无效 84 | // _this.data.list2.map(item => { 85 | // _this.data.list.push(item) 86 | // }) 87 | // 此方法无效 88 | this.setData({ 89 | list: newList 90 | }) 91 | }, 92 | // 数组拼接构建新数组,然后替换 93 | addList: function () { 94 | let _this = this 95 | this.setData({ 96 | list: this.data.list.concat(this.data.list2) 97 | }) 98 | } 99 | }) 100 | ``` 101 | 102 | - 数组的删除与清空 103 | 104 | 数组删除可以直接在原数组上进行操作 105 | 106 | ```javascript 107 | Page({ 108 | data: { 109 | list: [1, 2, 3] 110 | }, 111 | // 删除最后一位 112 | initList: function () { 113 | this.setData({ 114 | list: this.data.list.splice(0, this.data.list.length - 1) 115 | }) 116 | }, 117 | // 数组清空 118 | resetList: function () { 119 | this.setData({ 120 | list: [] 121 | }) 122 | } 123 | }) 124 | ``` 125 | 126 | - 数组指定项赋值 127 | 128 | ```javascript 129 | Page({ 130 | data: { 131 | list: [ 132 | { 133 | name: '小明', 134 | age: 22 135 | }, 136 | { 137 | name: '小王', 138 | age: 21 139 | } 140 | ] 141 | }, 142 | // 修改小王的年龄 (第二个人的年龄) 143 | initList: function () { 144 | this.setData({ 145 | 'list[1].name': 23 146 | }) 147 | }, 148 | // 修改小王的年龄 (第二个人的年龄) 149 | initListByIndex: function (index) { 150 | this.setData({ 151 | 'list[' + index + '].name': 23 152 | }) 153 | }, 154 | }) 155 | ``` 156 | -------------------------------------------------------------------------------- /数据库/learning mongoDB day 1/README.md: -------------------------------------------------------------------------------- 1 | 2 | # learning mongodb day 1 3 | 4 | > 基础学习来源: [菜鸟教程(RUNOOB.COM)](http://www.runoob.com/mongodb/mongodb-tutorial.html) 5 | 6 | ### 安装与启动 7 | 8 | #### windows 环境 9 | 10 | - 遇到的问题 11 | 12 | - mongod commond not found 13 | - 需要配置windows的全局变量path 14 | - 使用config文件启动的时候,报log文件should be file 15 | - 在指定的log文件夹新建log文件,并在conf文件中指向这个文件 16 | 17 | - 配置文件示例 18 | 19 | >参考: [http://www.cnblogs.com/zlslch/p/6935377.html](http://www.cnblogs.com/zlslch/p/6935377.html) 20 | 21 | **注意:3.2版本之后httpinterface参数废弃** 22 | 23 | mongod.conf 24 | 25 | ```shell 26 | systemLog: 27 | destination: file 28 | path: E:\LucaLJX\data\testDB\log\testDB.log 29 | storage: 30 | dbPath: E:\LucaLJX\data\testDB\db 31 | ``` 32 | 33 | [配置文件编写参考](https://www.cnblogs.com/phpandmysql/p/7763394.html) 34 | 35 | - 启动 36 | 37 | ```shell 38 | mongod --config xxxxxx 39 | ``` 40 | 41 | >操作参考:[https://blog.csdn.net/zhangpengfei104/article/details/52368237](https://blog.csdn.net/zhangpengfei104/article/details/52368237) 42 | 43 | 44 | ### 给数据库创建登录账号密码 45 | 46 | ```shell 47 | show dbs 48 | use admin 49 | db.createUser()... 50 | ``` 51 | 52 | ### 基础使用 53 | 54 | #### 创建数据库 55 | 56 | - 语法 57 | 58 | ```javascript 59 | use DATABASE_NAME 60 | ``` 61 | 62 | - 注意,需要在新建的数据库里面插入数据,才会展示新建的这个数据库 63 | 64 | #### 查看当前所有数据库 65 | 66 | - 语法 67 | 68 | ```shell 69 | show dbs 70 | ``` 71 | 72 | #### 删除数据库 73 | 74 | - 语法 75 | 76 | ```javascript 77 | db.dropDatabase() 78 | ``` 79 | 80 | **删除数据库跟删除集合,都需要切换到要删除的这个数据库或者集合,集合是数据库的子集** 81 | 82 | #### 创建集合 83 | 84 | - 语法 85 | 86 | ```javascript 87 | db.createCollection(name, options) 88 | 89 | // name: 要创建的集合的名称 90 | // option: 可选参数,指定有关内存大小及索引的选项 91 | ``` 92 | 93 | - options 94 | - capped 95 | - boolean 96 | - 可选 97 | - 如果是true,则创建固定集合(即有固定大小的集合,当达到最大值的时候,会自动覆盖最早的文档) 98 | - 如果是true,必须指定**size**参数 99 | - autoIndexId 100 | - boolean 101 | - 可选 102 | - 如果是true,则自动在_id字段创建索引,默认false 103 | - size 104 | - number 105 | - 可选 106 | - 为固定集合指定一个最大值,以字节计 107 | - 如果capped为true,则需要指定此字段 108 | - max 109 | - number 110 | - 可选 111 | -指定固定集合中包含文档的最大数量 112 | 113 | #### 删除集合 114 | 115 | - 语法 116 | 117 | ```javascript 118 | db.collection.drop() 119 | ``` 120 | 121 | > 如果删除成功,则返回true,否则返回false 122 | 123 | demo 124 | 125 | ```javascript 126 | // 选中数据库 127 | use mydb 128 | // 查看哪些集合 129 | show collections 130 | // 删除指定集合 131 | db.xxx.drop() 132 | ``` 133 | 134 | #### 插入文档 135 | 136 | - mongodb使用**insert()** 或者 **save()** 方法向集合中插入文档 137 | - 文档的数据结构和JSON基本一样 138 | 139 | - 语法 140 | 141 | ```javascript 142 | db.xxx.insert(xxx) 143 | // insert()接收参数为要插入的内容,可以是变量,也可以直接在方法中写 144 | ``` 145 | 146 | #### 更新文档 147 | 148 | > mongodb使用 **update()** 和 **save()** 来更新集合中的文档 149 | 150 | - update() 151 | 152 | 用于更新已存在的文档 153 | 154 | 语法 155 | ```javascript 156 | db.collection.update( 157 | , 158 | , 159 | { 160 | upsert: , 161 | multi: , 162 | writeConcern: 163 | } 164 | ) 165 | ``` 166 | 参数说明 167 | 168 | > 1.query : update的查询条件,类似sql update查询内where后面的。 169 | 170 | > 2.update : update的对象和一些更新的操作符(如$,$inc...)等,也可以理解为sql update查询内set后面的 171 | 172 | > 3.upsert : 可选,这个参数的意思是,如果不存在update的记录,是否插入objNew,true为插入,默认是false,不插入。 173 | 174 | > 4.multi : 可选,mongodb 默认是false,只更新找到的第一条记录,如果这个参数为true,就把按条件查出来多条记录全部更新。 175 | 176 | > 5.writeConcern :可选,抛出异常的级别。 177 | 178 | - save() 179 | 180 | save() 方法通过传入的文档来替换已有文档 181 | 182 | 语法 183 | ```javascript 184 | db.collection.save( 185 | , 186 | { 187 | writeConcern: 188 | } 189 | ) 190 | ``` 191 | 参数说明 192 | 193 | > document : 文档数据。 194 | 195 | > writeConcern :可选,抛出异常的级别。 196 | 197 | 更多示例 198 | 199 | ```javascript 200 | // 只更新第一条记录: 201 | 202 | db.col.update( { "count" : { $gt : 1 } } , { $set : { "test2" : "OK"} } ); 203 | 204 | // 全部更新: 205 | 206 | db.col.update( { "count" : { $gt : 3 } } , { $set : { "test2" : "OK"} },false,true ); 207 | 208 | // 只添加第一条: 209 | 210 | db.col.update( { "count" : { $gt : 4 } } , { $set : { "test5" : "OK"} },true,false ); 211 | 212 | // 全部添加加进去: 213 | 214 | db.col.update( { "count" : { $gt : 5 } } , { $set : { "test5" : "OK"} },true,true ); 215 | 216 | // 全部更新: 217 | 218 | db.col.update( { "count" : { $gt : 15 } } , { $inc : { "count" : 1} },false,true ); 219 | 220 | // 只更新第一条记录: 221 | 222 | db.col.update( { "count" : { $gt : 10 } } , { $inc : { "count" : 1} },false,false ); 223 | ``` 224 | 225 | [教程:mongodb更新文档](http://www.runoob.com/mongodb/mongodb-update.html) 226 | 227 | #### 删除文档 228 | 229 | - 语法 230 | 231 | ```javascript 232 | db.collection.remove( 233 | , 234 | 235 | ) 236 | ``` 237 | 参数说明: 238 | 239 | > query :(可选)删除的文档的条件。 240 | 241 | > justOne : (可选)如果设为 true 或 1,则只删除一个文档。 242 | 243 | > writeConcern :(可选)抛出异常的级别。 244 | 245 | **remove()语法必须传query参数,可以是{},不传的话会报错** 246 | 247 | demo 248 | ```javascript 249 | // 删除检索条件为title是remove的文档(全部删除) 250 | db.test.remove({title: 'remove'}) 251 | // 删除检索条件为title是remove的文档(只删除1个) 252 | db.test.remove({title: 'remove'}, 1) 253 | ``` 254 | 255 | - 语法 256 | 257 | ```javascript 258 | db.xxx.update({xxx:xxx}, {$pop: {key: }}); 259 | ``` 260 | 261 | 注意:db.xxx.find()可以查看集合下所有的文档 262 | 263 | #### 查询文档 264 | 265 | > mongodb查询文档使用find() 266 | 267 | 语法 268 | 269 | ```javascript 270 | db.collection.find(query, projection) 271 | 272 | // 也可以用findOne()进行查询 273 | db.collection.findOne(query, projection) 274 | 275 | // 等价于 276 | db.collection.find().limit(1) 277 | ``` 278 | 279 | 参数说明 280 | 281 | > query :可选,使用查询操作符指定查询条件 282 | 283 | > projection :可选,使用投影操作符指定返回的键。查询时返回文档中所有键值, 只需省略该参数即可(默认省略)。 284 | 285 | - and 查询 286 | 287 | 语法 288 | ```javascript 289 | db.col.find({key1:value1, key2:value2}).pretty() 290 | ``` 291 | 292 | demo 293 | ```javascript 294 | // 以下实例通过 by 和 title 键来查询 菜鸟教程 中 MongoDB 教程 的数据 295 | 296 | db.col.find({"by":"菜鸟教程", "title":"MongoDB 教程"}).pretty() 297 | 298 | // 以上实例中类似于 WHERE 语句:WHERE by='菜鸟教程' AND title='MongoDB 教程' 299 | ``` 300 | 301 | - or 查询 302 | 303 | > MongoDB OR 条件语句使用了关键字 $or 304 | 305 | 语法 306 | ```javascript 307 | db.col.find( 308 | { 309 | $or: [ 310 | {key1: value1}, {key2:value2} 311 | ] 312 | } 313 | ).pretty() 314 | ``` 315 | 316 | demo 317 | ```javascript 318 | // 以下实例中,我们演示了查询键 by 值为 菜鸟教程 或键 title 值为 MongoDB 教程 的文档。 319 | 320 | db.col.find({$or:[{"by":"菜鸟教程"},{"title": "MongoDB 教程"}]}).pretty() 321 | 322 | ``` 323 | 324 | - and 和 or 联合使用 325 | 326 | demo 327 | ```javascript 328 | // 以下实例演示了 AND 和 OR 联合使用,类似常规 SQL 语句为: 'where likes>50 AND (by = '菜鸟教程' OR title = 'MongoDB 教程')' 329 | 330 | db.col.find({"likes": {$gt:50}, $or: [{"by": "菜鸟教程"},{"title": "MongoDB 教程"}]}).pretty() 331 | 332 | ``` 333 | 334 | #### 条件操作(大于、小于、大于等于、小于等于) 335 | 336 | - 大于 337 | - $gt 338 | - 小于 339 | - $lt 340 | - 大于等于 341 | - $gte 342 | - 小于等于 343 | - $lte 344 | - 不等于 345 | - $ne 346 | - 等于 347 | - $eq 348 | 349 | demo 350 | ```javascript 351 | // 获取 "col" 集合中 "likes" 大于 100 的数据 352 | 353 | db.col.find({"likes" : {$gt : 100}}) 354 | 355 | // 另外一个例子 356 | // 获取"col"集合中 "likes" 大于100,小于 200 的数据 357 | // 类似SQL语句: Select * from col where likes>100 AND likes<200; 358 | 359 | db.col.find({likes : {$lt :200, $gt : 100}}) 360 | ``` 361 | 362 | #### $type操作符 363 | 364 | > 具体对应表格请查询 [$type查询表](http://www.runoob.com/mongodb/mongodb-operators-type.html) 365 | 366 | #### limit()与skip() 367 | 368 | - limit() 369 | 370 | > limit()方法接受一个数字参数,该参数指定从MongoDB中读取的记录条数。 371 | 372 | 语法 373 | ```javascript 374 | db.COLLECTION_NAME.find().limit(NUMBER) 375 | ``` 376 | 377 | - skip() 378 | 379 | > 使用skip()方法来跳过指定数量的数据,skip方法同样接受一个数字参数作为跳过的记录条数。 380 | 381 | 语法 382 | ```javascript 383 | db.COLLECTION_NAME.find().limit(NUMBER).skip(NUMBER) 384 | ``` 385 | 386 | demo 387 | ```javascript 388 | // 以下实例只会显示第二条文档数据 389 | 390 | db.col.find({},{"title":1,_id:0}).limit(1).skip(1) 391 | ``` 392 | 393 | > limit() 和 skip() 结合即可以实现分页操作 394 | 395 | #### sort() - mongodb排序 396 | 397 | > 在MongoDB中使用使用sort()方法对数据进行排序,sort()方法可以通过参数指定排序的字段,并使用 1 和 -1 来指定排序的方式,其中 1 为升序排列,而-1是用于降序排列。 398 | 399 | 语法 400 | ```javascript 401 | db.COLLECTION_NAME.find().sort({KEY:1}) 402 | ``` 403 | 404 | > skip(), limilt(), sort()三个放在一起执行的时候,执行的顺序是先 sort(), 然后是 skip(),最后是显示的 limit()。 405 | 406 | 407 | BY-[Luca_LJX](https://github.com/LucaLJX/jianshu_demo/tree/master/%E6%95%B0%E6%8D%AE%E5%BA%93/lerning%20mongoDB%20day%201) -------------------------------------------------------------------------------- /数据库/learning mongoDB day 2/nodeServer/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "requires": true, 3 | "lockfileVersion": 1, 4 | "dependencies": { 5 | "bson": { 6 | "version": "1.0.6", 7 | "resolved": "https://registry.npmjs.org/bson/-/bson-1.0.6.tgz", 8 | "integrity": "sha512-D8zmlb46xfuK2gGvKmUjIklQEouN2nQ0LEHHeZ/NoHM2LDiMk2EYzZ5Ntw/Urk+bgMDosOZxaRzXxvhI5TcAVQ==" 9 | }, 10 | "mongodb": { 11 | "version": "3.1.0-beta4", 12 | "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-3.1.0-beta4.tgz", 13 | "integrity": "sha512-nJAK59fFlMWTdEJaTyGp3HeerkIahnupoMNjbszPJVnO63/36aFnlWHqpYrrJwj9GKlmb47YWBIvNo6fdrUL4Q==", 14 | "requires": { 15 | "mongodb-core": "3.1.0-beta4" 16 | } 17 | }, 18 | "mongodb-core": { 19 | "version": "3.1.0-beta4", 20 | "resolved": "https://registry.npmjs.org/mongodb-core/-/mongodb-core-3.1.0-beta4.tgz", 21 | "integrity": "sha512-aiwUKBGmFZwBx4CdC1iK5kjZyk0zhDxQ0edRtpTZUkk1jyWkdzYd6GEA6wMl7O6ZX/dOiFM2ujkrUR7+vkqJPw==", 22 | "requires": { 23 | "bson": "1.0.6", 24 | "require_optional": "1.0.1", 25 | "saslprep": "1.0.0" 26 | } 27 | }, 28 | "require_optional": { 29 | "version": "1.0.1", 30 | "resolved": "https://registry.npmjs.org/require_optional/-/require_optional-1.0.1.tgz", 31 | "integrity": "sha512-qhM/y57enGWHAe3v/NcwML6a3/vfESLe/sGM2dII+gEO0BpKRUkWZow/tyloNqJyN6kXSl3RyyM8Ll5D/sJP8g==", 32 | "requires": { 33 | "resolve-from": "2.0.0", 34 | "semver": "5.5.0" 35 | } 36 | }, 37 | "resolve-from": { 38 | "version": "2.0.0", 39 | "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-2.0.0.tgz", 40 | "integrity": "sha1-lICrIOlP+h2egKgEx+oUdhGWa1c=" 41 | }, 42 | "saslprep": { 43 | "version": "1.0.0", 44 | "resolved": "https://registry.npmjs.org/saslprep/-/saslprep-1.0.0.tgz", 45 | "integrity": "sha512-5lvKUEQ7lAN5/vPl5d3k8FQeDbEamu9kizfATfLLWV5h6Mkh1xcieR1FSsJkcSRUk49lF2tAW8gzXWVwtwZVhw==", 46 | "optional": true 47 | }, 48 | "semver": { 49 | "version": "5.5.0", 50 | "resolved": "https://registry.npmjs.org/semver/-/semver-5.5.0.tgz", 51 | "integrity": "sha512-4SJ3dm0WAwWy/NVeioZh5AntkdJoWKxHxcmyP622fOkgHa4z3R0TdBJICINyaSDE6uNwVc8gZr+ZinwZAH4xIA==" 52 | } 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /数据库/learning mongoDB day 2/nodeServer/server.js: -------------------------------------------------------------------------------- 1 | const MongoClient = require('mongodb').MongoClient; 2 | const url = 'mongodb://localhost:27017'; 3 | 4 | MongoClient.connect(url, function (err, db) { 5 | if (err) { 6 | throw err 7 | } 8 | console.log('已连接'); 9 | 10 | let testDB = db.db('testdb'); 11 | console.log(testDB); 12 | 13 | testDB.collection('ceshi').find().toArray(function (err, res) { 14 | if (err) { 15 | throw err 16 | } 17 | console.log(res); 18 | }); 19 | }); -------------------------------------------------------------------------------- /数据库/mac下安装使用mongodb/README.md: -------------------------------------------------------------------------------- 1 | 2 | ## mac下安装mongodb 3 | 4 | ### brew 5 | 6 | #### 安装brew 7 | 8 | ```shell 9 | 10 | # 安装brew 11 | /usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)" 12 | 13 | ``` 14 | 15 | #### brew 的用法 16 | 17 | - brew update 更新brew; 18 | - brew install {应用名,如git} 安装软件 19 | - brew cask install {应用名,如git} 也是下载安装,与上面的区别,请查看https://www.zhihu.com/question/22624898 20 | - 更多用法请 brew help 21 | 22 | ### 安装mongodb 23 | 24 | ```shell 25 | sudo brew install mongodb 26 | ``` 27 | 28 | #### 安装遇到的问题 29 | 30 | 在安装的时候,会报出以下错误 31 | 32 | ```shell 33 | Running Homebrew as root is extremely dangerous and no longer supported. As Homebrew does not drop... 34 | ``` 35 | 36 | 这种个情况是因为没有根目录的访问权限导致的,这时候需要添加访问权限 37 | 38 | 参考: 39 | - [homebrew无法安装,提示不能在根目录下使用](http://www.caotama.com/77920.html) 40 | - [homebrew无法安装](https://blog.csdn.net/maindek/article/details/76164955) 41 | 42 | ```shell 43 | sudo chown -R $(whoami) /usr/local 44 | ``` 45 | 46 | 这时候也有可能报以下错误 47 | 48 | ```shell 49 | Operation not permitted 50 | ``` 51 | 52 | 解决方案是在恢复模式下进行授权 53 | 54 | > [Mac root Operation not permitted解决方案](https://blog.csdn.net/kaka_caroline/article/details/79955860) 55 | 56 | 57 | ### 启动mongodb 58 | 59 | > 推荐大家使用config进行启动 60 | 61 | 新建mongod.config文件 62 | 63 | ```config 64 | systemLog: 65 | destination: file 66 | path: /Users/luca_ljx/LJX/DB/testdb/log/testDB.log 67 | storage: 68 | dbPath: /Users/luca_ljx/LJX/DB/testdb/db 69 | ``` 70 | 71 | 启动 72 | 73 | ```shell 74 | mongod --config /Users/xxx/DB/testdb/mongod.conf 75 | ``` 76 | 77 | -------------------------------------------------------------------------------- /框架相关/react/react_antd/AntdPro-学习使用-001-安装使用及结构布局.md: -------------------------------------------------------------------------------- 1 | ## AntdPro-学习使用-001-安装使用及结构布局 2 | 3 | - [react中文文档](https://doc.react-china.org/) 4 | - [ant-design中文文档](https://ant.design/index-cn) 5 | - [antd-pro中文文档](https://pro.ant.design/docs/getting-started-cn) 6 | 7 | ### 安装、启动 8 | 9 | ```shell 10 | # 全局安装脚手架 11 | $ npm install ant-design-pro-cli -g 12 | # or 13 | $ cnpm install ant-design-pro-cli -g 14 | 15 | # 初始化项目 16 | $ pro new 17 | 18 | # 安装项目依赖 19 | $ npm install 20 | # or 21 | $ cnpm install 22 | 23 | # 启动 24 | $ npm start 25 | ``` -------------------------------------------------------------------------------- /随笔/one day in alibaba/README.md: -------------------------------------------------------------------------------- 1 | # 阿里巴巴参观记--舍我其谁的程序员精神 2 | 3 | > if not now, when? if not me, who? 4 | 5 | 这句舍我其谁的口号,可能没有伴随阿里巴巴走过1999至2018的这19个年头,但至少在每个屡创新高的双十一,所有的阿里巴巴程序员都一直恪守这句千金难买的程序员精神。 6 | 7 | 写代码之初,一直觉得很多东西好难,惰性跟挫败感扯着后腿,让我在学习的过程中举步维艰。后来随着学习的东西越来越多,举一反三之后越发觉得其实学习是一件很有成就感的事情。于是在生活中,也觉得没有解决不了的问题,没有学不会的技能,这常常让身边的人不能理解:他们觉得这是自讨苦吃。 8 | 9 | 再后来,当了解到阿里的“if not now, when? if not me, who?”这句口号后,豁然开朗并不禁热血沸腾,生活、学习、工作中,都当如此。看看今天的互联网世界,大多数东西不都是从无到有、由零到一创造出来的么?程序员,不就是应该如此:迎难而上,舍我其谁么? --------------------------------------------------------------------------------