Anderson_liu

Anderson_liu 查看完整档案

北京编辑  |  填写毕业院校奇虎360  |  研发工程师 编辑 findx.design 编辑
编辑

Lucky life, thank you.

个人动态

Anderson_liu 分享了头条 · 2018-08-14

值得学习使用的酷炫的前端设计和动画。Github地址:https://github.com/Anderson-L...

赞 0 收藏 1 评论 1

Anderson_liu 发布了文章 · 2018-08-14

基于Electron快速开发MacOS Menubar app

基于electron开发MacOS Menubar app主要涉及的技术其实就是Electron的 Tray API。

此外也有人将这个API做了简单的封装:menubar(github)

其基本原理就是将Electron的窗口挪到menubar对应app按钮的下面,也就是点击menu bar按钮时,在获取按钮的位置,然后在按钮的下方显示窗口。

比如在这里有一个将传统Electron改造为MacOS Menubar application的例子:

// 检测是否MacOS darwin
if (platform === 'darwin' || tray) {
    const iconPath = join(__dirname, 'static/IconTrayMac.png');
    const trayIcon = new Tray(iconPath);
    trayIcon.setToolTip(`${app.getName()}`);
    // 点击时显示窗口,并修改窗口的显示位置
    trayIcon.on('click', () => {
      const {screen} = electron;
      const {width, height} = screen.getPrimaryDisplay().workAreaSize;
      const [defaultWidth, defaultHeight] = [width, height].map(x => Math.round((x * 3) / 4));
      const WINDOW_WIDTH = defaultWidth - 350;
      const WINDOW_HEIGHT = defaultHeight;
      const HORIZ_PADDING = 15;
      const VERT_PADDING = 15;

      const cursorPosition = screen.getCursorScreenPoint();
      const primarySize = screen.getPrimaryDisplay().workAreaSize;
      const trayPositionVert = cursorPosition.y >= primarySize.height / 2 ? 'bottom' : 'top';
      const trayPositionHoriz = cursorPosition.x >= primarySize.width / 2 ? 'right' : 'left';
      win.setPosition(getTrayPosX(), getTrayPosY());
      if (win.isVisible()) {
        win.hide();
      } else {
        win.show();
      }
      // 计算位置
      function getTrayPosX() {
        const horizBounds = {
          left: cursorPosition.x - (WINDOW_WIDTH / 2),
          right: cursorPosition.x + (WINDOW_WIDTH / 2)
        };
        if (trayPositionHoriz === 'left') {
          return horizBounds.left <= HORIZ_PADDING ? HORIZ_PADDING : horizBounds.left;
        }
        return horizBounds.right >= primarySize.width ? primarySize.width - HORIZ_PADDING - WINDOW_WIDTH : horizBounds.right - WINDOW_WIDTH;
      }
      function getTrayPosY() {
        return trayPositionVert === 'bottom' ? cursorPosition.y - WINDOW_HEIGHT - VERT_PADDING : cursorPosition.y + VERT_PADDING;
      }
    });
    return;
  }

这个时候就有了效果:

image.png | left | 747x467

接下来还有一个问题,就是怎么实现 点击其他地方时,该窗口自动隐藏
这里要用到的是Electron的 Blur 事件(文档):

aoWindow.on('blur', () => {
    if (platform === 'darwin') {
      aoWindow.hide();
    }
  }); 

在MacOS系统中,检测到Blur事件,也就是未聚焦于窗口时,调用 hide 把窗口隐藏掉。

这样,就实现了一个MacOS的Menubar application!

接下来就用 electron-packager 将该App打包成MacOS的dmg包就行了!

Tips: 使用electron-packager打包过程有个坑,就是使用cnpm安装依赖的话,会导致打包时间极为漫长,用npm重新安装就好了。
Tips: 用于MacOS中Tray的icon,有个经验是将icon大小设置为14X14。

最后给出一个我Fork后修改的Github开源Microsoft To-Do desktop app应用的地址:https://github.com/Anderson-L...
原repo地址:https://github.com/klauscfhq/ao

界面很有设计感,经过改造为Menubar app后效率更是大幅提升!

查看原文

赞 1 收藏 0 评论 1

Anderson_liu 发布了文章 · 2018-03-23

使用GDB等验证Golang的“编译器会为某些场合进行专门优化,避免字符串转换时的额外分配和复制操作”

编译器会为某些场合进行专门优化,避免字符串转换时的额外分配和复制操作:

  • 将[]byte转换为string key,去map[string]查询的时候。
  • 将string转换为[]byte,进行for range迭代时,直接取字节赋值给局部变量。

Example Code

package main

func main() {
    m := map[string]int {
        "abc" : 123,
    }

    key := []byte("abc")
    x, ok := m[string(key)]

    println(x, ok)
}
➜  go build -gcflags "-N -l" example.go 

GDB go1.7.6 验证成功

  • 地址都是0xc420047ee0
➜  gdb example
GNU gdb (GDB) 8.0.1
Copyright (C) 2017 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.  Type "show copying"
and "show warranty" for details.
This GDB was configured as "x86_64-apple-darwin17.0.0".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
<http://www.gnu.org/software/gdb/documentation/>.
For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from example...done.
(gdb) b 9
Breakpoint 1 at 0x2164: file /Users/anderson/Code/lang-experiments/golang/src/compiler-avoid-copy-when-transform-string/example.go, line 9.
(gdb) c
The program is not being run.
(gdb) r
Starting program: /Users/anderson/Code/lang-experiments/golang/src/compiler-avoid-copy-when-transform-string/example 
[New Thread 0x2703 of process 90436]
warning: unhandled dyld version (15)
[New Thread 0x1907 of process 90436]
[New Thread 0x1a03 of process 90436]
[New Thread 0x2503 of process 90436]
[New Thread 0x2603 of process 90436]

Thread 2 hit Breakpoint 1, main.main ()
    at /Users/anderson/Code/lang-experiments/golang/src/compiler-avoid-copy-when-transform-string/example.go:9
9        x, ok := m[string(key)]
(gdb) p key
$1 = {array = 0xc420047ee0 "abc", len = 3, cap = 32}
(gdb) b runtime.mapaccess2_faststr
Breakpoint 2 at 0x94b0: file /Users/anderson/.gvm/gos/go1.7.6/src/runtime/hashmap_fast.go, line 297.
(gdb) c
Continuing.

Thread 2 hit Breakpoint 2, runtime.mapaccess2_faststr (t=0x5a2c0 <type.*+50144>, h=0xc42007c000, ky=..., ~r3=0x3, ~r4=32)
    at /Users/anderson/.gvm/gos/go1.7.6/src/runtime/hashmap_fast.go:297
297    func mapaccess2_faststr(t *maptype, h *hmap, ky string) (unsafe.Pointer, bool) {
(gdb) p ky
$2 = 0xc420047ee0 "abc"

delve go1.7.6 验证失败

  • 地址分别是:

    • 0xc420047f20
    • 0xc420047ea8
➜  dlv exec example
Type 'help' for list of commands.
(dlv) b example.go:9
Breakpoint 1 set at 0x2164 for main.main() ./example.go:9
(dlv) c
> main.main() ./example.go:9 (hits goroutine(1):1 total:1) (PC: 0x2164)
     4:        m := map[string]int {
     5:            "abc" : 123,
     6:        }
     7:    
     8:        key := []byte("abc")
=>   9:        x, ok := m[string(key)]
    10:    
    11:        println(x, ok)
    12:    }
(dlv) p key
[]uint8 len: 3, cap: 32, [97,98,99]
(dlv) p &key
(*[]uint8)(0xc420047f20)                                # 地址是0xc420047f20
(dlv) b runtime.mapaccess2_faststr
Breakpoint 2 set at 0x94c3 for runtime.mapaccess2_faststr() /Users/anderson/.gvm/gos/go1.7.6/src/runtime/hashmap_fast.go:297
(dlv) c
> runtime.mapaccess2_faststr() /Users/anderson/.gvm/gos/go1.7.6/src/runtime/hashmap_fast.go:297 (hits goroutine(1):1 total:1) (PC: 0x94c3)
   292:                return unsafe.Pointer(&zeroVal[0])
   293:            }
   294:        }
   295:    }
   296:    
=> 297:    func mapaccess2_faststr(t *maptype, h *hmap, ky string) (unsafe.Pointer, bool) {
   298:        if raceenabled && h != nil {
   299:            callerpc := getcallerpc(unsafe.Pointer(&t))
   300:            racereadpc(unsafe.Pointer(h), callerpc, funcPC(mapaccess2_faststr))
   301:        }
   302:        if h == nil || h.count == 0 {
(dlv) p &ky
(*string)(0xc420047ea8)                               # 地址是0xc420047ea8

LLDB go1.7.6 验证成功

  • 地址都是: 0x000000c420041ee0
➜  lldb example
(lldb) target create "example"
Current executable set to 'example' (x86_64).
(lldb) b example.go:9
Breakpoint 1: where = example`main.main + 292 at example.go:9, address = 0x0000000000002164
(lldb) r
Process 91197 launched: '/Users/anderson/Code/lang-experiments/golang/src/compiler-avoid-copy-when-transform-string/example' (x86_64)
Process 91197 stopped
* thread #1, stop reason = breakpoint 1.1
    frame #0: 0x0000000000002164 example`main.main at example.go:9
   6           }
   7       
   8           key := []byte("abc")
-> 9           x, ok := m[string(key)]
   10      
   11          println(x, ok)
   12      }
Target 0: (example) stopped.
(lldb) p key
([]uint8) key = (len 3, cap 32) {
  [0] = 97
  [1] = 98
  [2] = 99
}
(lldb) p &key
(*[]uint8)  = 0x000000c420041ee0 (len 0, cap 0)
(lldb) b runtime.mapaccess2_faststr
Breakpoint 2: where = example`runtime.mapaccess2_faststr + 33 at hashmap_fast.go:302, address = 0x00000000000094d1
(lldb) p key
([]uint8) key = (len 3, cap 32) {
  [0] = 97
  [1] = 98
  [2] = 99
}
(lldb) c
Process 91197 resuming
Process 91197 stopped
* thread #1, stop reason = breakpoint 2.1
    frame #0: 0x00000000000094d1 example`runtime.mapaccess2_faststr(t=0x000000000005a2c0, h=0x000000c420076000, ky="abc", ~r3=0x0000000000000003, ~r4=true) at hashmap_fast.go:302
   299             callerpc := getcallerpc(unsafe.Pointer(&t))
   300             racereadpc(unsafe.Pointer(h), callerpc, funcPC(mapaccess2_faststr))
   301         }
-> 302         if h == nil || h.count == 0 {
   303             return unsafe.Pointer(&zeroVal[0]), false
   304         }
   305         if h.flags&hashWriting != 0 {
Target 0: (example) stopped.
(lldb) p ky
(string) ky = "abc"
(lldb) p &ky
(*string)  = 0x000000c420041ee0 ""

GDB go1.10 无法查看数组地址,无法验证

➜  gdb example_1_10 
GNU gdb (GDB) 8.0.1
Copyright (C) 2017 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.  Type "show copying"
and "show warranty" for details.
This GDB was configured as "x86_64-apple-darwin17.0.0".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
<http://www.gnu.org/software/gdb/documentation/>.
For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from example_1_10...done.
Loading Go Runtime support.
(gdb) b 9
Breakpoint 1 at 0x104d4b4: file /Users/anderson/Code/lang-experiments/golang/src/compiler-avoid-copy-when-transform-string/example.go, line 9.
(gdb) r
Starting program: /Users/anderson/Code/lang-experiments/golang/src/compiler-avoid-copy-when-transform-string/example_1_10 
[New Thread 0x1903 of process 92053]
warning: unhandled dyld version (15)
[New Thread 0x1807 of process 92053]
[New Thread 0x1a03 of process 92053]
[New Thread 0x2503 of process 92053]
[New Thread 0x2603 of process 92053]

Thread 2 hit Breakpoint 1, main.main () at /Users/anderson/Code/lang-experiments/golang/src/compiler-avoid-copy-when-transform-string/example.go:9
9        x, ok := m[string(key)]
(gdb) p key
$1 =  []uint8 = {97 'a', 98 'b', 99 'c'}
(gdb) p &key
$2 =  []uint8 * = {97 'a', 98 'b', 99 'c'}
(gdb) b runtime.mapaccess2_faststr
Breakpoint 2 at 0x1007ee0: file /usr/local/Cellar/go/1.10/libexec/src/runtime/hashmap_fast.go, line 261.
(gdb) c
Continuing.

Thread 2 hit Breakpoint 2, runtime.mapaccess2_faststr (h=0xc420057e78, ky="abc", t=0x105ad60 <type.*+55168>, ~r3=0x3, ~r4=32)
    at /usr/local/Cellar/go/1.10/libexec/src/runtime/hashmap_fast.go:261
261    func mapaccess2_faststr(t *maptype, h *hmap, ky string) (unsafe.Pointer, bool) {
(gdb) p ky
$3 = "abc"
(gdb) p &ky
$4 = "abc"

delve go1.10 验证失败

  • 地址分别是:

    • 0xc420057e60
    • 0xc420057dd0
➜  dlv exec example_1_10
Type 'help' for list of commands.
(dlv) b example.go:9
Breakpoint 1 set at 0x104d4b4 for main.main() ./example.go:9
(dlv) c
> main.main() ./example.go:9 (hits goroutine(1):1 total:1) (PC: 0x104d4b4)
     4:        m := map[string]int {
     5:            "abc" : 123,
     6:        }
     7:    
     8:        key := []byte("abc")
=>   9:        x, ok := m[string(key)]
    10:    
    11:        println(x, ok)
    12:    }
(dlv) p key
[]uint8 len: 3, cap: 32, [97,98,99]
(dlv) p &key
(*[]uint8)(0xc420057e60)
(dlv) b runtime.mapaccess2_faststr
Breakpoint 2 set at 0x1007ef3 for runtime.mapaccess2_faststr() /usr/local/Cellar/go/1.10/libexec/src/runtime/hashmap_fast.go:261
(dlv) c
> runtime.mapaccess2_faststr() /usr/local/Cellar/go/1.10/libexec/src/runtime/hashmap_fast.go:261 (hits goroutine(1):1 total:1) (PC: 0x1007ef3)
Warning: debugging optimized function
   256:            }
   257:        }
   258:        return unsafe.Pointer(&zeroVal[0])
   259:    }
   260:    
=> 261:    func mapaccess2_faststr(t *maptype, h *hmap, ky string) (unsafe.Pointer, bool) {
   262:        if raceenabled && h != nil {
   263:            callerpc := getcallerpc()
   264:            racereadpc(unsafe.Pointer(h), callerpc, funcPC(mapaccess2_faststr))
   265:        }
   266:        if h == nil || h.count == 0 {
(dlv) p ky
"abc"
(dlv) p &ky
(*string)(0xc420057dd0)

LLDB go1.10 验证成功

  • 地址都是:0x000000c420057e10
➜  lldb example_1_10 
(lldb) target create "example_1_10"
Current executable set to 'example_1_10' (x86_64).
(lldb) b example.go:9
Breakpoint 1: where = example_1_10`main.main + 372 at example.go:9, address = 0x000000000104d4b4
(lldb) r
Process 91907 launched: '/Users/anderson/Code/lang-experiments/golang/src/compiler-avoid-copy-when-transform-string/example_1_10' (x86_64)
Process 91907 stopped
* thread #1, stop reason = breakpoint 1.1
    frame #0: 0x000000000104d4b4 example_1_10`main.main at example.go:9
   6           }
   7       
   8           key := []byte("abc")
-> 9           x, ok := m[string(key)]
   10      
   11          println(x, ok)
   12      }
Target 0: (example_1_10) stopped.
(lldb) p key
([]uint8) key = (len 3, cap 32) {
  [0] = 97
  [1] = 98
  [2] = 99
}
(lldb) p &key
(*[]uint8)  = 0x000000c420057e10 (len 0, cap 0)
(lldb) b runtime.mapaccess2_faststr
Breakpoint 2: where = example_1_10`runtime.mapaccess2_faststr + 38 at hashmap_fast.go:266, address = 0x0000000001007f06
(lldb) c
Process 91907 resuming
Process 91907 stopped
* thread #1, stop reason = breakpoint 2.1
    frame #0: 0x0000000001007f06 example_1_10`runtime.mapaccess2_faststr(h=0x000000c420057e78, ky="abc", t=0x000000000105ad60, ~r3=0x0000000000000003, ~r4=true) at hashmap_fast.go:266
   263             callerpc := getcallerpc()
   264             racereadpc(unsafe.Pointer(h), callerpc, funcPC(mapaccess2_faststr))
   265         }
-> 266         if h == nil || h.count == 0 {
   267             return unsafe.Pointer(&zeroVal[0]), false
   268         }
   269         if h.flags&hashWriting != 0 {
Target 0: (example_1_10) stopped.

(lldb) p ky
(string) ky = "abc"
(lldb) p &ky
(*string)  = 0x000000c420057e10 ""
(lldb) 

结论

  • 不知道为什么,切换版本前后delve都验证失败了
  • 切换版本前后,LLDB都验证成功了
  • GDB无法查看GO1.10的数组地址
查看原文

赞 0 收藏 0 评论 0

Anderson_liu 关注了标签 · 2017-09-21

微信小程序

微信小程序开发工具,技巧

关注 915

Anderson_liu 发布了文章 · 2017-09-18

AntDesign结合mockjs随机生成图片

首先在七牛云存储一定数量的mock图片,比如笔者存储了近千张,方便接下来在代码中使用。
图片的来源可以通过爬取诸如https://unsplash.it/400/800/?random等网站实现,在此不赘述。
核心步骤就是将图片以数字(如1-1000)为文件名存储到七牛云,然后就可以在代码中通过随机int访问了。
再结合Ant Design的API,构造upload组件的Mock数据fileList如下:

const Mock = require('mockjs')
let data = Mock.mock({
  'data|4-8': [
    {
      id: '@id',
      title: '@title',
      namespace: 'desctab',
      content: '@paragraph',
      isPublish: '@boolean',
      createTime: '@datetime',
      'fileList|1-10': [{
        name: '@integer(1,960)',
        uid: '-@name',
        url: 'http://xxxxx.xxx.clouddn.com/@name',
        status: 'done',
      }],
    },
  ],
})

效果如下:

clipboard.png

clipboard.png

查看原文

赞 1 收藏 1 评论 0

Anderson_liu 回答了问题 · 2017-09-16

解决开发团队使用git,如何协作,流程是怎样的,能不能举个例子,或有什么好的教程?

推荐你看一下阮一峰的Git工作流程,学习git flow,希望对你有所帮助。

clipboard.png

关注 20 回答 15

Anderson_liu 发布了文章 · 2017-09-15

Ant Design upload 组件快速配置使用七牛云

在使用ant design开发后台业务过程中,遇到了给upload组件配置后台服务器的问题。因为用习惯了七牛云的快速易用,以及喜欢它的自动压缩接口,因此第一反应就是想怎么配置上传到七牛云上面。
不过经过多番搜寻,并没找到好的解决方案。官方推荐参考的jQuery-File-Upload服务端上传接口实现测试了几个nodejs的实现,发现很久没有维护了,存在一些问题。于是只能从阅读ant design源代码来看能不能怎么修改upload的上传方式,从而实现提交图片过去。

首先从ant design的upload组件源码可以看到,它是基于这个upload组件编写的。再看该upload组件,得到具体的request实现:

function getError(option, xhr) {
  const msg = `cannot post ${option.action} ${xhr.status}'`;
  const err = new Error(msg);
  err.status = xhr.status;
  err.method = 'post';
  err.url = option.action;
  return err;
}

function getBody(xhr) {
  const text = xhr.responseText || xhr.response;
  if (!text) {
    return text;
  }

  try {
    return JSON.parse(text);
  } catch (e) {
    return text;
  }
}

// option {
//  onProgress: (event: { percent: number }): void,
//  onError: (event: Error, body?: Object): void,
//  onSuccess: (body: Object): void,
//  data: Object,
//  filename: String,
//  file: File,
//  withCredentials: Boolean,
//  action: String,
//  headers: Object,
// }
export default function upload(option) {
  const xhr = new XMLHttpRequest();

  if (option.onProgress && xhr.upload) {
    xhr.upload.onprogress = function progress(e) {
      if (e.total > 0) {
        e.percent = e.loaded / e.total * 100;
      }
      option.onProgress(e);
    };
  }

  const formData = new FormData();

  if (option.data) {
    Object.keys(option.data).map(key => {
      formData.append(key, option.data[key]);
    });
  }

  formData.append(option.filename, option.file);

  xhr.onerror = function error(e) {
    option.onError(e);
  };

  xhr.onload = function onload() {
    // allow success when 2xx status
    // see https://github.com/react-component/upload/issues/34
    if (xhr.status < 200 || xhr.status >= 300) {
      return option.onError(getError(option, xhr), getBody(xhr));
    }

    option.onSuccess(getBody(xhr), xhr);
  };


  xhr.open('post', option.action, true);

  // Has to be after `.open()`. See https://github.com/enyo/dropzone/issues/179
  if (option.withCredentials && 'withCredentials' in xhr) {
    xhr.withCredentials = true;
  }

  const headers = option.headers || {};

  // when set headers['X-Requested-With'] = null , can close default XHR header
  // see https://github.com/react-component/upload/issues/33
  if (headers['X-Requested-With'] !== null) {
    xhr.setRequestHeader('X-Requested-With', 'XMLHttpRequest');
  }

  for (const h in headers) {
    if (headers.hasOwnProperty(h) && headers[h] !== null) {
      xhr.setRequestHeader(h, headers[h]);
    }
  }
  xhr.send(formData);

  return {
    abort() {
      xhr.abort();
    },
  };
}

至此上传的逻辑一目了然,那么可以怎么改造呢?再翻阅一下七牛云的文档,得到一个利用js上传的实现:

/*
 *   本示例演示七牛云存储表单上传
 *
 *   按照以下的步骤运行示例:
 *
 *   1. 填写token。需要您不知道如何生成token,可以点击右侧的链接生成,然后将结果复制粘贴过来。
 *   2. 填写key。如果您在生成token的过程中指定了key,则将其输入至此。否则留空。
 *   3. 姓名是一个自定义的变量,如果生成token的过程中指定了returnUrl和returnBody,
 *      并且returnBody中指定了期望返回此字段,则七牛会将其返回给returnUrl对应的业务服务器。
 *      callbackBody亦然。
 *   4. 选择任意一张照片,然后点击提交即可
 *
 *   实际开发中,您可以通过后端开发语言动态生成这个表单,将token的hidden属性设置为true并对其进行赋值。
 *
 *  **********************************************************************************
 *  * 贡献代码:
 *  * 1. git clone git@github.com:icattlecoder/jsfiddle
 *  * 2. push代码到您的github库
 *  * 3. 测试效果,访问 http://jsfiddle.net/gh/get/jquery/1.9.1/<Your GitHub Name>/jsfiddle/tree/master/ajaxupload
 *  * 4. 提pr
 *   **********************************************************************************
 */
$(document).ready(function() {
    var Qiniu_UploadUrl = "http://up.qiniu.com";
    var progressbar = $("#progressbar"),
        progressLabel = $(".progress-label");
    progressbar.progressbar({
        value: false,
        change: function() {
            progressLabel.text(progressbar.progressbar("value") + "%");
        },
        complete: function() {
            progressLabel.text("Complete!");
        }
    });
    $("#btn_upload").click(function() {
        //普通上传
        var Qiniu_upload = function(f, token, key) {
            var xhr = new XMLHttpRequest();
            xhr.open('POST', Qiniu_UploadUrl, true);
            var formData, startDate;
            formData = new FormData();
            if (key !== null && key !== undefined) formData.append('key', key);
            formData.append('token', token);
            formData.append('file', f);
            var taking;
            xhr.upload.addEventListener("progress", function(evt) {
                if (evt.lengthComputable) {
                    var nowDate = new Date().getTime();
                    taking = nowDate - startDate;
                    var x = (evt.loaded) / 1024;
                    var y = taking / 1000;
                    var uploadSpeed = (x / y);
                    var formatSpeed;
                    if (uploadSpeed > 1024) {
                        formatSpeed = (uploadSpeed / 1024).toFixed(2) + "Mb\/s";
                    } else {
                        formatSpeed = uploadSpeed.toFixed(2) + "Kb\/s";
                    }
                    var percentComplete = Math.round(evt.loaded * 100 / evt.total);
                    progressbar.progressbar("value", percentComplete);
                    // console && console.log(percentComplete, ",", formatSpeed);
                }
            }, false);

            xhr.onreadystatechange = function(response) {
                if (xhr.readyState == 4 && xhr.status == 200 && xhr.responseText != "") {
                    var blkRet = JSON.parse(xhr.responseText);
                    console && console.log(blkRet);
                    $("#dialog").html(xhr.responseText).dialog();
                } else if (xhr.status != 200 && xhr.responseText) {

                }
            };
            startDate = new Date().getTime();
            $("#progressbar").show();
            xhr.send(formData);
        };
        var token = $("#token").val();
        if ($("#file")[0].files.length > 0 && token != "") {
            Qiniu_upload($("#file")[0].files[0], token, $("#key").val());
        } else {
            console && console.log("form input error");
        }
    })
})

通过审阅其中的核心逻辑可以知道,*它的上传逻辑与antd 的upload组件的核心区别就是formData增加了七牛云上传token:

formData.append('token', token);

而通过upload的request源码又可以知道,可以通过option.data传过来的参数执行formData.append('token', token);,该部分的源码如下:

const formData = new FormData();

  if (option.data) {
    Object.keys(option.data).map(key => {
      formData.append(key, option.data[key]);
    });
  }

由此,再参阅ant design官方文档可以知道,使用Upload组件时,可以通过data API传入自定义的data,那么自此就可以得到一个简洁的办法,通过以下sample code就可以实现Upload组件上传图片到七牛云:

const QINIU_SERVER = 'http://up.qiniu.com'
data = {
    token: 'PUT-YOUR-TOKEN-HERE',
  }
<Upload
    action={QINIU_SERVER}
    listType="picture-card"
    className="upload-list-inline"
    onChange={this.onChange}
    onPreview={this.handlePreview}
    fileList={fileList}
    data={this.data}
    >
        {uploadButton}
</Upload>

最后划重点:

BTW, 如果使用的是七牛云其他地区如华南地区的Bucket,需要替换使用其他地区的上传域名进action,具体可参考这里
Enjoy it!

查看原文

赞 12 收藏 5 评论 13

Anderson_liu 发布了文章 · 2017-09-15

《深入浅出React和Redux》读书笔记一

  • 使用React.createClass是一种过时的方法
  • React 判断一个元素是HTML元素还是React组件的原则就是看第一个字母是否大写
  • 在HTML中直接写onclick一直就是为人诟病的写法,网页应用开发界一直倡导的是用jQuery的方法添加事件处理函数,直接写onclick会带来代码混乱的问题
  • 既然长期以来不倡导在HTML中使用onclick,为什么在React的JSX中我们却要使用onClick这样的方式来添加事件处理函数呢?
  • 以前使用HTML来代表内容、用CSS代表样式、用JavaScript来定义交互行为,这三种语言分在三种不同的文件里面,实际上是把不同的技术分开管理了,而不是逻辑上的"分而治之"
  • 根据做同一件事的代码应该有高耦合性的设计原则,既然我们要实现一个Click-Counter,那为什么不把实现这个功能的所有代码集中在一个文件里呢?
  • 即使现在,我们还是要说在HTML中直接使用onclick很不专业,原因如下:

    • onclick 添加的事件处理函数是在全局环境下执行的,这污染了全局环境,很容易产生意料不到的后果;
    • 给很多DOM元素添加onclick事件,可能会影响网页的性能,毕竟,网页需要的事件处理函数越多,性能就会越低;
    • 对于使用onclick的DOM元素,如果要动态地从DOM树中删除的话,需要把对应的事件处理器注销,如果忘了注销,就可能造成内存泄漏,像这样的Bug很难被发现
    • 上述的这些问题,在JSX中都不存在
  • 因为

    • JSX中一个组件使用了onClick,并没有直接使用onclick的HTML,而是使用了事件委托(event delegation)的方式处理事件,无论有多少个onClick出现,其实最后都只在DOM树上添加了一个事件处理函数,挂在最顶层的DOM节点上;
    • 所有的点击事件都被这个事件处理函数捕获,然后根据具体组件分配给特定函数,使用事件委托的性能当然要比为每个onClick都挂载一个事件处理函数要高;
    • 因为React控制了组件的生命周期,在unmount的时候自然能够清楚相关的所有事件处理函数,内存泄漏也不再是一个问题;
  • 选取一些DOM元素,然后对这些元素做一些操作,这是一种最容易理解的开发模式;jQuery的发明人John Resig就是发现了网页应用开发者的这个编程模式,才创造出了jQuery,其一问世就受到普遍认可,因为这种模式直观易懂。但是,对于庞大的项目,这种模式会造成代码结构复杂,难以维护,每个jQuery的使用者都会有这种体会
  • 用React开发的ClickCounter组件并没有像jQuery那样做"选中一些DOM元素然后做一些事情的动作"

打一个比方,React是一个聪明的建筑工人,而jQuery是一个比较傻的建筑工人,开发者你就是一个建筑的设计师,如果是jQuery这个建筑工人为你工作,你不得不事无巨细地告诉jQuery“如何去做”,要告诉他这面墙要拆掉重建,那面墙上要新开一个窗户,反之,如果是React这个建筑工人为你工作,你所要做的就是告诉这个工人“我想要什么样子”,只要把图纸递给React这个工人,他就会替你搞定一切,当然他不会把整个建筑拆掉重建,而是很聪明地把这次的图纸和上次的图纸做一个对比,发现不同之处,然后只去做适当的修改就完成任务了。
显而易见,React的工作方式把开发者从繁琐的操作中解放出来,开发者只需要着重“我想要显示什么”,而不用操心“怎样去做”。
这种新的思维方式,对于一个简单的例子也要编写不少代码,感觉像是用高射炮打蚊子,但是对于一个大型的项目,这种方式编写的代码会更容易管理,因为整个React应用要做的就是渲染,开发者关注的是渲染成成什么样子,而不用关心如何实现增量渲染。
React的理念,归结为一个公式,就像下面这样:
UI=render(data)
让我们来看看这个公式表达的含义,用户看到的界面(UI),应该是一个函数(在这里叫render)的执行结果,只接受数据(data)作为参数。这个函数是一个纯函数,所谓纯函数,指的是没有任何副作用,输出完全依赖于输入的函数,两次函数调用如果输入相同,得到的结果也绝对相同。如此一来,最终的用户界面,在render函数确定的情况下完全取决于输入数据。

查看原文

赞 1 收藏 3 评论 0

认证与成就

  • 获得 15 次点赞
  • 获得 1 枚徽章 获得 0 枚金徽章, 获得 0 枚银徽章, 获得 1 枚铜徽章

擅长技能
编辑

开源项目 & 著作
编辑

(゚∀゚ )
暂时没有

注册于 2017-04-23
个人主页被 669 人浏览