(译)React hooks:它不是一种魔法,只是一个数组——使用图表揭秘提案规则

原文地址:https://medium.com/@ryardley/…译文:染陌 (Github)译文地址:https://github.com/answershuto/Blog转载请著名出处我是一名hooks API的忠实粉丝,然而它对你的使用会有一些奇怪的约束,所以我在本文中使用一个模型来把原理展示给那些想去使用新的API却难以理解它的规则的人。警告:Hooks 还处于实验阶段本文提到的 Hooks API 还处于实验阶段,如果你需要的是稳定的 React API 文档,可以从这里找到。解密 Hooks 的工作方式我发现一些同学苦苦思索新的 Hooks API 中的“魔法”,所以我打算尝试着去解释一下,至少从表层出发,它是如何工作的。Hooks 的规则React 核心团队在Hooks的提案中提出了两个在你使用Hooks的过程中必须去遵守的主要规则。请不要在循环、条件或者嵌套函数中调用 Hooks都有在 React 函数中才去调用 Hooks后者我觉得是显而易见的,你需要用函数的方式把行为与组件关联起来才能把行为添加到组件。然而对于前者,我认为它会让人产生困惑,因为这样使用 API 编程似乎显得不那么自然,但这就是我今天要套索的内容。Hooks 的状态管理都是依赖数组的为了让大家产生一个更清晰的模型,让我们来看一下 Hooks 的简单实现可能是什么样子。需要注意的是,这部分内容只是 API 的一种可能实现方法,以便读者更好地趣理解它。它并不是 API 实际在内部的工作方式,而且它只是一个提案,在未来都会有可能发生变化。我们应该如何实现“useState()”呢?让我们通过一个例子来理解状态可能是如何工作的。首先让我们从一个组件开始:代码地址/* 译:https://github.com/answershuto /function RenderFunctionComponent() { const [firstName, setFirstName] = useState(“Rudi”); const [lastName, setLastName] = useState(“Yardley”); return ( <Button onClick={() => setFirstName(“Fred”)}>Fred</Button> );}Hooks API 背后的思想是你可以将一个 setter 函数通过 Hook 函数的第二个参数返回,用该函数来控制 Hook 管理的壮体。所以 React 能用这个做什么呢?首先让我们解释一下它在 React 内部是如何工作的。在执行上下文去渲染一个特殊组件的时候,下面这些步骤会被执行。这意味着,数据的存储是独立于组件之外的。该状态不能与其他组件共享,但是它拥有一个独立的作用域,在该作用域需要被渲染时读取数据。(1)初始化创建两个空数组“setters”与“state”设置指针“cursor”为 0(2)首次渲染首次执行组件函数每当 useState() 被调用时,如果它是首次渲染,它会通过 push 将一个 setter 方法(绑定了指针“cursor”位置)放进 setters 数组中,同时,也会将另一个对应的状态放进 state 数组中去。(3)后续渲染每次的后续渲染都会重置指针“cursor”的位置,并会从每个数组中读取对应的值。(4)处理事件每个 setter 都会有一个对应的指针位置的引用,因此当触发任何 setter 调用的时候都会触发去改变状态数组中的对应的值。以及底层的实现这是一段示例代码:代码地址let state = [];let setters = [];let firstRun = true;let cursor = 0;function createSetter(cursor) { return function setterWithCursor(newVal) { state[cursor] = newVal; };}/ 译:https://github.com/answershuto /// This is the pseudocode for the useState helperexport function useState(initVal) { if (firstRun) { state.push(initVal); setters.push(createSetter(cursor)); firstRun = false; } const setter = setters[cursor]; const value = state[cursor]; cursor++; return [value, setter];}/ 译:https://github.com/answershuto */// Our component code that uses hooksfunction RenderFunctionComponent() { const [firstName, setFirstName] = useState(“Rudi”); // cursor: 0 const [lastName, setLastName] = useState(“Yardley”); // cursor: 1 return ( <div> <Button onClick={() => setFirstName(“Richard”)}>Richard</Button> <Button onClick={() => setFirstName(“Fred”)}>Fred</Button> </div> );}// This is sort of simulating Reacts rendering cyclefunction MyComponent() { cursor = 0; // resetting the cursor return <RenderFunctionComponent />; // render}console.log(state); // Pre-render: []MyComponent();console.log(state); // First-render: [‘Rudi’, ‘Yardley’]MyComponent();console.log(state); // Subsequent-render: [‘Rudi’, ‘Yardley’]// click the ‘Fred’ buttonconsole.log(state); // After-click: [‘Fred’, ‘Yardley’]为什么说顺序很重要呢?如果我们基于一些外部条件或是说组件的状态去改变 Hooks 在渲染周期的顺序,那会发生什么呢?让我们做一些 React 团队禁止去做的事情。代码地址let firstRender = true;function RenderFunctionComponent() { let initName; if(firstRender){ [initName] = useState(“Rudi”); firstRender = false; } const [firstName, setFirstName] = useState(initName); const [lastName, setLastName] = useState(“Yardley”); return ( <Button onClick={() => setFirstName(“Fred”)}>Fred</Button> );}我们在条件语句中调用了 useState 函数,让我们看看它对整个系统造成的破坏。糟糕组件的首次渲染到此为止,我们的变量 firstName 与 lastName 依旧包含了正确的数据,让我们继续去看一下第二次渲染会发生什么事情。糟糕的第二次渲染现在 firstName 与 lastName 这两个变量全部被设置为“Rudi”,与我们实际的存储状态不符。这个例子的用法显然是不正确的,但是它让我们知道了为什么我们必须使用 React 团队规定的规则去使用 Hooks。React 团队制定了这个规则,是因为如果不遵循这套规则去使用 Hooks API会导致数据有问题。思考 Hooks 维护了一些列的数组,所以你不应该去违反这些规则所以你现在应该清除为什么你不应该在条件语句或者循环语句中使用 Hooks 了。因为我们维护了一个指针“cursor”指向一个数组,如果你改变了 render 函数内部的调用顺序,那么这个指针“cursor”将不会匹配到正确的数据,你的调用也将不会指向正确的数据或句柄。因此,有一个诀窍就是你需要思考 Hooks 作为一组需要一个匹配一致的指针“cursor”去管理的数组(染陌译)。如果做到了这一点,那么采用任何的写法它都可以正常工作。总结希望通过上述的讲解,我已经给大家建立了一个关于 Hooks 的更加清晰的思维模型,以此可以去思考新的 Hooks API 底层到底做了什么事情。请记住,它真正的价值在于能够关注点聚集在一起,同时小心它的顺序,那使用 Hooks API 会很高的回报。Hooks 是 React 组件的一个很有用的插件,这也佐证了为何大家为何对此感到如此兴奋。如果你脑海中形成了我上述的这种思维模型,把这种状态作为一组数组的存在,那么你就会发现在使用中不会打破它的规则。我希望将来再去研究一下 useEffects useEffects 方法,并尝试将其与 React 的生命周期进行比较。这篇文章是一篇在线文档,如果你想要参与贡献或者有任何有误的地方,欢迎联系我。你可以在 Twitter 上面 fllow 我(Rudi Yardley)或者在Github找到我。染陌 译:https://github.com/answershuto ...

November 25, 2018 · 2 min · jiezi

hexo 搭建博客

通常我们可以使用github pages 来搭建静态博客,建立一个username.github.io的项目就可以了,如果要将其他项目也作为页面展示,可以将代码推送到gh-pages分支。GitHub pages木有默认样式,所以如果你不会自己写css,博客很难看的,所以我们需要hexo.准备先安装好git node hexo初始化$ hexo init blogINFO Cloning hexo-starter to D:\code\hexo\blogCloning into ‘D:\code\hexo\blog’…remote: Enumerating objects: 68, done.remote: Total 68 (delta 0), reused 0 (delta 0), pack-reused 68Unpacking objects: 100% (68/68), done.Submodule ’themes/landscape’ (https://github.com/hexojs/hexo-theme-landscape.git) registered for path ’themes/landscape’Cloning into ‘D:/code/hexo/blog/themes/landscape’…remote: Enumerating objects: 5, done.remote: Counting objects: 100% (5/5), done.remote: Compressing objects: 100% (5/5), done.remote: Total 846 (delta 0), reused 1 (delta 0), pack-reused 841Receiving objects: 100% (846/846), 2.55 MiB | 16.00 KiB/s, done.Resolving deltas: 100% (445/445), done.Submodule path ’themes/landscape’: checked out ‘73a23c51f8487cfcd7c6deec96ccc7543960d350’INFO Install dependenciesyarn install v1.9.4info No lockfile found.[1/4] Resolving packages…warning hexo > titlecase@1.1.2: no longer maintainedwarning hexo > nunjucks > postinstall-build@5.0.3: postinstall-build’s behavior is now built into npm! You should migrate off of postinstall-build and use the new prepare lifecycle script with npm 5.0.0 or greater.[2/4] Fetching packages…info fsevents@1.2.4: The platform “win32” is incompatible with this module.info “fsevents@1.2.4” is an optional dependency and failed compatibility check. Excluding it from installation.[3/4] Linking dependencies…[4/4] Building fresh packages…success Saved lockfile.Done in 18.06s.INFO Start blogging with Hexo!$ cd blog$ npm installnpm WARN deprecated titlecase@1.1.2: no longer maintainednpm WARN deprecated postinstall-build@5.0.3: postinstall-build’s behavior is now built into npm! You should migrate off of postinstall-build and use the new prepare lifecycle script with npm 5.0.0 or greater.> nunjucks@3.1.4 postinstall D:\code\hexo\blog\node_modules\nunjucks> node postinstall-build.js srcnpm WARN rollback Rolling back node-pre-gyp@0.10.0 failed (this is probably harmless): EPERM: operation not permitted, rmdir ‘D:\code\hexo\blog\node_modules\fsevents\node_modules’npm notice created a lockfile as package-lock.json. You should commit this file.npm WARN optional SKIPPING OPTIONAL DEPENDENCY: fsevents@1.2.4 (node_modules\fsevents):npm WARN notsup SKIPPING OPTIONAL DEPENDENCY: Unsupported platform for fsevents@1.2.4: wanted {“os”:“darwin”,“arch”:“any”} (current: {“os”:“win32”,“arch”:“x64”})added 101 packages, removed 40 packages and updated 321 packages in 23.882s$ ls_config.yml package.json scaffolds/ themes/node_modules/ package-lock.json source/ yarn.lock$ hexo gINFO Start processingINFO Files loaded in 655 msINFO Generated: index.htmlINFO Generated: archives/index.htmlINFO Generated: fancybox/jquery.fancybox.cssINFO Generated: fancybox/blank.gifINFO Generated: fancybox/fancybox_loading@2x.gifINFO Generated: fancybox/fancybox_sprite@2x.pngINFO Generated: fancybox/jquery.fancybox.jsINFO Generated: fancybox/jquery.fancybox.pack.jsINFO Generated: fancybox/fancybox_sprite.pngINFO Generated: fancybox/fancybox_overlay.pngINFO Generated: archives/2018/11/index.htmlINFO Generated: fancybox/fancybox_loading.gifINFO Generated: css/fonts/FontAwesome.otfINFO Generated: fancybox/helpers/jquery.fancybox-thumbs.cssINFO Generated: js/script.jsINFO Generated: fancybox/helpers/jquery.fancybox-buttons.jsINFO Generated: fancybox/helpers/jquery.fancybox-media.jsINFO Generated: fancybox/helpers/jquery.fancybox-buttons.cssINFO Generated: css/fonts/fontawesome-webfont.eotINFO Generated: css/fonts/fontawesome-webfont.woffINFO Generated: fancybox/helpers/fancybox_buttons.pngINFO Generated: fancybox/helpers/jquery.fancybox-thumbs.jsINFO Generated: css/style.cssINFO Generated: css/fonts/fontawesome-webfont.ttfINFO Generated: archives/2018/index.htmlINFO Generated: css/images/banner.jpgINFO Generated: css/fonts/fontawesome-webfont.svgINFO Generated: 2018/11/20/hello-world/index.htmlINFO 28 files generated in 1.26 s$ hexo sINFO Start processingINFO Hexo is running at http://localhost:4000 . Press Ctrl+C to stop$ hexo dERROR Deployer not found: gitGitHub key 配置1.生成指定名字的密钥 ssh-keygen -t rsa -C “xx@sina.com” -f ~/.ssh/github_sushengbuhuo 会生成 github_sushengbuhuo 和 github_sushengbuhuo.pub 这两个文件2.密钥复制到托管平台上 vim ~/.ssh/github_sushengbuhuo.pub ,把内容复制至代码托管平台上3.修改config文件 vim ~/.ssh/config #修改config文件,如果没有创建 configHost sushengbuhuo.github.comHostName github.comUser gitIdentityFile ~/.ssh/github_sushengbuhuoHost abc.github.comHostName github.comUser gitIdentityFile ~/.ssh/github_abc4.测试验证$ ssh -T git@github.com:ssh: Could not resolve hostname github.com:: Name or service not known$ ssh -T git@github.comgit@github.com: Permission denied (publickey).$ ssh -T git@sushengbuhuo.github.comHi sushengbuhuo! You’ve successfully authenticated, but GitHub does not provide shell access.配置config.ymldeploy: type: git repository: git@sushengbuhuo.github.com:sushengbuhuo/sushengbuhuo.github.io.git branch: master theme: next推送到GitHub$ hexo clean && hexo gINFO Deleted database.INFO Deleted public folder.INFO Start processingINFO Files loaded in 545 msINFO Generated: index.htmlINFO Generated: archives/index.htmlINFO Generated: fancybox/fancybox_loading.gifINFO Generated: fancybox/fancybox_sprite@2x.pngINFO Generated: fancybox/jquery.fancybox.jsINFO Generated: fancybox/fancybox_overlay.pngINFO Generated: fancybox/jquery.fancybox.cssINFO Generated: fancybox/jquery.fancybox.pack.jsINFO Generated: fancybox/blank.gifINFO Generated: fancybox/fancybox_loading@2x.gifINFO Generated: fancybox/fancybox_sprite.pngINFO Generated: css/fonts/FontAwesome.otfINFO Generated: archives/2018/11/index.htmlINFO Generated: css/fonts/fontawesome-webfont.eotINFO Generated: archives/2018/index.htmlINFO Generated: fancybox/helpers/fancybox_buttons.pngINFO Generated: fancybox/helpers/jquery.fancybox-thumbs.jsINFO Generated: css/fonts/fontawesome-webfont.woffINFO Generated: fancybox/helpers/jquery.fancybox-buttons.cssINFO Generated: js/script.jsINFO Generated: fancybox/helpers/jquery.fancybox-buttons.jsINFO Generated: css/style.cssINFO Generated: fancybox/helpers/jquery.fancybox-thumbs.cssINFO Generated: 2018/11/20/hello-world/index.htmlINFO Generated: css/fonts/fontawesome-webfont.ttfINFO Generated: css/fonts/fontawesome-webfont.svgINFO Generated: css/images/banner.jpgINFO Generated: fancybox/helpers/jquery.fancybox-media.jsINFO 28 files generated in 1.13 s$ hexo dERROR Deployer not found: git$ npm install hexo-deployer-git –savenpm WARN deprecated swig@1.4.2: This package is no longer maintainednpm WARN optional SKIPPING OPTIONAL DEPENDENCY: fsevents@1.2.4 (node_modules\fsevents):npm WARN notsup SKIPPING OPTIONAL DEPENDENCY: Unsupported platform for fsevents@1.2.4: wanted {“os”:“darwin”,“arch”:“any”} (current: {“os”:“win32”,“arch”:“x64”})+ hexo-deployer-git@0.3.1added 31 packages in 17.866s$ hexo dINFO Deploying: gitINFO Setting up Git deployment…Initialized empty Git repository in D:/code/hexo/blog/.deploy_git/.git/[master (root-commit) 9c86786] First commit Committer: unknown <xxx@sina.com.cn>Your name and email address were configured automatically basedon your username and hostname. Please check that they are accurate.You can suppress this message by setting them explicitly: git config –global user.name “Your Name” git config –global user.email you@example.comAfter doing this, you may fix the identity used for this commit with: git commit –amend –reset-author 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 placeholderINFO Clearing .deploy_git folder…INFO Copying files from public folder…INFO Copying files from extend dirs…warning: LF will be replaced by CRLF in 2018/11/20/hello-world/index.html.The file will have its original line endings in your working directory.warning: LF will be replaced by CRLF in archives/2018/11/index.html.The file will have its original line endings in your working directory.warning: LF will be replaced by CRLF in archives/2018/index.html.The file will have its original line endings in your working directory.warning: LF will be replaced by CRLF in archives/index.html.The file will have its original line endings in your working directory.warning: LF will be replaced by CRLF in css/style.css.The file will have its original line endings in your working directory.warning: LF will be replaced by CRLF in fancybox/helpers/jquery.fancybox-buttons.css.The file will have its original line endings in your working directory.warning: LF will be replaced by CRLF in fancybox/helpers/jquery.fancybox-buttons.js.The file will have its original line endings in your working directory.warning: LF will be replaced by CRLF in fancybox/helpers/jquery.fancybox-media.js.The file will have its original line endings in your working directory.warning: LF will be replaced by CRLF in fancybox/helpers/jquery.fancybox-thumbs.css.The file will have its original line endings in your working directory.warning: LF will be replaced by CRLF in fancybox/helpers/jquery.fancybox-thumbs.js.The file will have its original line endings in your working directory.warning: LF will be replaced by CRLF in fancybox/jquery.fancybox.css.The file will have its original line endings in your working directory.warning: LF will be replaced by CRLF in fancybox/jquery.fancybox.js.The file will have its original line endings in your working directory.warning: LF will be replaced by CRLF in fancybox/jquery.fancybox.pack.js.The file will have its original line endings in your working directory.warning: LF will be replaced by CRLF in index.html.The file will have its original line endings in your working directory.warning: LF will be replaced by CRLF in js/script.js.The file will have its original line endings in your working directory.[master b7f7580] Site updated: 2018-11-20 11:51:50 Committer: unknown <xxx@sina.com.cn>Your name and email address were configured automatically basedon your username and hostname. Please check that they are accurate.You can suppress this message by setting them explicitly: git config –global user.name “Your Name” git config –global user.email you@example.comAfter doing this, you may fix the identity used for this commit with: git commit –amend –reset-author 29 files changed, 5777 insertions(+) create mode 100644 2018/11/20/hello-world/index.html create mode 100644 archives/2018/11/index.html create mode 100644 archives/2018/index.html create mode 100644 archives/index.html create mode 100644 css/fonts/FontAwesome.otf create mode 100644 css/fonts/fontawesome-webfont.eot create mode 100644 css/fonts/fontawesome-webfont.svg create mode 100644 css/fonts/fontawesome-webfont.ttf create mode 100644 css/fonts/fontawesome-webfont.woff create mode 100644 css/images/banner.jpg create mode 100644 css/style.css create mode 100644 fancybox/blank.gif create mode 100644 fancybox/fancybox_loading.gif create mode 100644 fancybox/fancybox_loading@2x.gif create mode 100644 fancybox/fancybox_overlay.png create mode 100644 fancybox/fancybox_sprite.png create mode 100644 fancybox/fancybox_sprite@2x.png create mode 100644 fancybox/helpers/fancybox_buttons.png create mode 100644 fancybox/helpers/jquery.fancybox-buttons.css create mode 100644 fancybox/helpers/jquery.fancybox-buttons.js create mode 100644 fancybox/helpers/jquery.fancybox-media.js create mode 100644 fancybox/helpers/jquery.fancybox-thumbs.css create mode 100644 fancybox/helpers/jquery.fancybox-thumbs.js create mode 100644 fancybox/jquery.fancybox.css create mode 100644 fancybox/jquery.fancybox.js create mode 100644 fancybox/jquery.fancybox.pack.js create mode 100644 index.html create mode 100644 js/script.js delete mode 100644 placeholdergit@github.com: Permission denied (publickey).fatal: Could not read from remote repository.Please make sure you have the correct access rightsand the repository exists.FATAL Something’s wrong. Maybe you can find the solution here: http://hexo.io/docs/troubleshooting.htmlError: git@github.com: Permission denied (publickey).fatal: Could not read from remote repository.Please make sure you have the correct access rightsand the repository exists. at ChildProcess.<anonymous> (D:\code\hexo\blog\node_modules\hexo-util\lib\spawn.js:37:17) at emitTwo (events.js:126:13) at ChildProcess.emit (events.js:214:7) at ChildProcess.cp.emit (D:\code\hexo\blog\node_modules\cross-spawn\lib\enoent.js:40:29) at maybeClose (internal/child_process.js:925:16) at Socket.stream.socket.on (internal/child_process.js:346:11) at emitOne (events.js:116:13) at Socket.emit (events.js:211:7) at Pipe._handle.close [as _onclose] (net.js:557:12)$ hexo dINFO Deploying: gitINFO Clearing .deploy_git folder…INFO Copying files from public folder…INFO Copying files from extend dirs…warning: LF will be replaced by CRLF in 2018/11/20/hello-world/index.html.The file will have its original line endings in your working directory.warning: LF will be replaced by CRLF in archives/2018/11/index.html.The file will have its original line endings in your working directory.warning: LF will be replaced by CRLF in archives/2018/index.html.The file will have its original line endings in your working directory.warning: LF will be replaced by CRLF in archives/index.html.The file will have its original line endings in your working directory.warning: LF will be replaced by CRLF in css/style.css.The file will have its original line endings in your working directory.warning: LF will be replaced by CRLF in fancybox/helpers/jquery.fancybox-buttons.css.The file will have its original line endings in your working directory.warning: LF will be replaced by CRLF in fancybox/helpers/jquery.fancybox-buttons.js.The file will have its original line endings in your working directory.warning: LF will be replaced by CRLF in fancybox/helpers/jquery.fancybox-media.js.The file will have its original line endings in your working directory.warning: LF will be replaced by CRLF in fancybox/helpers/jquery.fancybox-thumbs.css.The file will have its original line endings in your working directory.warning: LF will be replaced by CRLF in fancybox/helpers/jquery.fancybox-thumbs.js.The file will have its original line endings in your working directory.warning: LF will be replaced by CRLF in fancybox/jquery.fancybox.css.The file will have its original line endings in your working directory.warning: LF will be replaced by CRLF in fancybox/jquery.fancybox.js.The file will have its original line endings in your working directory.warning: LF will be replaced by CRLF in fancybox/jquery.fancybox.pack.js.The file will have its original line endings in your working directory.warning: LF will be replaced by CRLF in js/script.js.The file will have its original line endings in your working directory.On branch masternothing to commit, working tree cleanBranch ‘master’ set up to track remote branch ‘master’ from ‘git@sushengbuhuo.github.com:sushengbuhuo/sushengbuhuo.github.io.git’.To sushengbuhuo.github.com:sushengbuhuo/sushengbuhuo.github.io.git + 3037877…b7f7580 HEAD -> master (forced update)INFO Deploy done: git$ git clone https://github.com/iissnan/hexo-theme-next themes/nextCloning into ’themes/next’…remote: Enumerating objects: 12033, done.remote: Total 12033 (delta 0), reused 0 (delta 0), pack-reused 12033Receiving objects: 100% (12033/12033), 12.95 MiB | 79.00 KiB/s, done.Resolving deltas: 100% (6966/6966), done.$ hexo clean && hexo gINFO Deleted database.INFO Deleted public folder.INFO Start processing$ hexo d$ hexo sINFO Start processingWARN ===============================================================WARN ========================= ATTENTION! ==========================WARN ===============================================================WARN NexT repository is moving here: https://github.com/theme-nextWARN ===============================================================WARN It’s rebase to v6.0.0 and future maintenance will resume thereWARN ===============================================================INFO Hexo is running at http://localhost:4000 . Press Ctrl+C to stop安装插件登录admin 即可看到我们所有的文章内容 npm i hexo-admin –savenpm WARN deprecated minimatch@2.0.10: Please update to minimatch 3.0.2 or higher to avoid a RegExp DoS issuenpm WARN deprecated connect@2.7.11: connect 2.x series is deprecatednpm WARN deprecated cryptiles@2.0.5: This version is no longer maintained. Please upgrade to the latest version.npm WARN deprecated boom@2.10.1: This version is no longer maintained. Please upgrade to the latest version.npm WARN deprecated hoek@2.16.3: This version is no longer maintained. Please upgrade to the latest version.npm WARN acorn-dynamic-import@4.0.0 requires a peer of acorn@^6.0.0 but none is installed. You must install peer dependencies yourself.npm WARN optional SKIPPING OPTIONAL DEPENDENCY: fsevents@1.2.4 (node_modules\fsevents):npm WARN notsup SKIPPING OPTIONAL DEPENDENCY: Unsupported platform for fsevents@1.2.4: wanted {“os”:“darwin”,“arch”:“any”} (current: {“os”:“win32”,“arch”:“x64”})+ hexo-admin@2.3.0added 251 packages in 23.975s ╭─────────────────────────────────────╮ │ │ │ Update available 5.6.0 → 6.4.1 │ │ Run npm i npm to update │ │ │ ╰─────────────────────────────────────╯#网站底部字数统计d:\code\hexo\blog npm install hexo-wordcount –savenpm WARN rollback Rolling back node-pre-gyp@0.10.0 failed (this is probably harmless): EPERM: operation not permitted, scandir ’d:\code\hexo\blog\node_modules\fsevents\node_modules’npm WARN acorn-dynamic-import@4.0.0 requires a peer of acorn@^6.0.0 but none is installed. You must install peer dependencies yourself.npm WARN optional SKIPPING OPTIONAL DEPENDENCY: fsevents@1.2.4 (node_modules\fsevents):npm WARN notsup SKIPPING OPTIONAL DEPENDENCY: Unsupported platform for fsevents@1.2.4: wanted {“os”:“darwin”,“arch”:“any”} (current: {“os”:“win32”,“arch”:“x64”})+ hexo-wordcount@6.0.1added 1 package in 10.289s中文乱码问题将config.yml 和md文件编码转为utf-8修改config.yml language: zh-Hans新建文章$ hexo new “PHP依赖注入"Hexo 默认以标题为文件名称,可编辑_config.yml new_post_name 参数来改变默认的文件名称,设为 :year-:month-:day-:title.md 可更方便的通过日期来管理文章。新写文章文档结尾查看效果资源hexo文档hexo问题交流 Deployment绝配:hexo+next主题及我走过的坑Hexo+Pages静态博客-Next主题篇关于HEXO搭建个人博客的点点滴滴搭建博客手把手教你用Hexo搭建个人技术博客基于CentOS搭建Hexo博客最快的 Hexo 博客搭建方法超详细Hexo+Github博客搭建小白教程在Github上备份Hexo博客最快的 Hexo 博客搭建方法 ...

November 23, 2018 · 9 min · jiezi

管理多个git生成的ssh key

经常我们可能需要上传github,和gitlab,或者你有多个github账号,我们需要对应不同的账号上传,我们需要配置多个ssh key这里我们就以配置github,gitlab,两个ssh key 为案例1.生成两个不同的ssh生成第一个ssh keyssh-keygen -t rsa -C “yourmail@gmail.com” 这里不要一路回传,让你选择在哪里选择存放key的时候写个名字,比如 id_rsa_github,之后的两个可以回车。上图的红色框框是自己输入的,便于区分生成第二个ssh keyssh-keygen -t rsa -C “yourmail@gmail.com” 一样不要一路回车最终结果是这样子的:图中的config文件是我自己建的,也就是接下来要说的2.配置config新建文件config文件,打开输入一下# gitlabHost gitlab.com HostName gitlab.com PreferredAuthentications publickey IdentityFile ~/.ssh/id_rsa_gitlab User xiaqijian // 输入自己账号名 # githubHost github.com HostName github.com PreferredAuthentications publickey IdentityFile ~/.ssh/id_rsa_github User xiaqijian // 这里输入自己的账号名注意:如果拷贝我的,要把后面的注释去掉然后保存起来分别在github,gitlab填上ssh key填上刚刚生成的,然后你就可以上传文件试试或者用下面方法测试ssh -T git@github.com首发于微信公众号:node前端不妨关注一下,我们一起学习回复:100有福利哦

November 22, 2018 · 1 min · jiezi

阿玺:支持天猫双11的核心技术,100%对外开放

小蚂蚁说:每年天猫双11都是技术的最大练兵场,无数技术都是经历了双十一的考验,历练和打磨,最终成为人们的生活日常。今年双11当晚破2000亿时,蚂蚁金服合伙人,副总裁,副CTO胡喜在上海做了一个面向媒体记者朋友的演讲,介绍十年来蚂蚁技术的发展,以及我们的开放战略。以下是演讲全文。我经历了将近十年双11,几乎每年双11我都会参与,我们内部的一个作战室光明顶,每年双11都会在那儿去做双11作战演练,相应的维护服务。我们看着大屏从100亿到破200亿、400亿,破1000亿,用了两小时时间,心情非常激动,包括刚才破2000亿的时候,大家也能感受到这个心情。我今天想给大家介绍一下,蚂蚁在这十年双11当中,我们经历了哪些事情,大家可能看到这个相应的标题有一些感触,十年天猫双11,从“有”到“无”,为什么从有到无,按道理来看,大家熟悉支付宝、熟悉蚂蚁金服都应该知道,我们应该是从无到有的过程,蚂蚁在这十年当中,我们随着双11,创新了很多相关产品,从快捷支付到余额宝,到花呗,到相关物流运费险等等,还有网商贷。我们看到了这么多产品的出现,一个从无到有的过程,为什么说是“有”到“无”呢?从社会的价值角度考虑,我们一个从有到无的过程,让我来给大家讲为什么是从有到无的过程。双11十年,究竟发生了什么事,大家看这张大屏应该心里比较清楚一点,我们从2009年开始做双11,真正感受到双11的威力是2010年,然后是2011年、2012年,一直往上去涨,我们看到双11的峰值,这十年背后究竟发生了什么事情。我们看一下三个不为人知的小数据,我们大家都知道,我们喜欢提大数据,我们今天提一些小数据,从小数据当中看到很多大的变化,114,一秒钟、一元钱、四个钱包,这背后到底是什么样的故事,让我来给大家娓娓道来。 一秒钟。从我2007年加入蚂蚁的第一天起,我们在做什么事情?我们在做一件不可能的事情,我们在做一件能够让整个中国相应的支付能够顺畅的事情。我们最早开始去IOE,把我们的技术真正做成分布式,让支付能够如丝般顺滑。 我们后来做了很多相关的事情,蚂蚁这么多年一直做什么事情,我们能让更多的消费者更好的去支付,能够带来更好的用户体验。而这么多年,我们提升了很大的系统能力,我们做了很多相关的事情,我们发明了担保交易,我们发明了快捷支付,我们从担保交易、快捷支付又到余额宝、花呗,背后我们在做什么事情,我们就是想让用户更好的去支付,更方便的去支付。这背后是为了能够把支付时间缩短到一秒钟,为了缩短到这一秒钟,用了十多年的时间,而我们还在不断追求支付体验,我们不仅仅从线上走向线下,我们从手机端,今天不带手机就可以支付,我们可以通过刷脸支付,可以通过指纹支付,到现在为止,我们看一个数据,生物支付占比,今天整个相关的双11支付,占比是60.3%,这个是我们这么多年积累下来的一个经验,这么多年努力的结果。十多年的第一个变化,无限制的交易,从有到无,无是无限制的交易,我们希望让用户真正能够很方便的交易。今天我相信很多用户去相应的购买,你可以很容易的掏出手机扫描二维码,在线支付、手机支付、指纹支付、用脸支付。背后看到这么十年期间,用户在这过程当中的变化,我们要做能够让用户更好的支付,无限制的交易能力。最终我们期望能够带来安全流畅的支付能力,7×24小时的支付能力,这是我们的第一个无,无限制交易。一元。我相信大家很多人看过这个故事,我恰恰得到了相应的今天双11大促的数据,在贵州的一个地方,一个开店的农户,他在网上卖玉米,我相信很多女士会喝玉米水,他做了一件事,贷款一元钱,我不是想说贷款一元钱多还是少的事情,而是说我们这么多年的付出,能够让小微经营者告别融资难、融资烦、融资贵的问题。这是我们这么多年努力的结果,而背后支撑的是两大产品,一个是310小微贷款,另外是212保险理赔,310大家已经比较清楚了,我们说三分钟申请、一秒钟放款、最后所有过程,零人工参与,这个背后是我们这么多年,针对小微企业,尤其是淘宝上面很多中小企业、小微商户相应的支持,让他们能够解决融资难的问题,真正让普惠金融走向更多中小企业。背后给大家带来一组数据,截止到今天为止,我们已经为1100万中小微企业,提供了2万亿贷款额度,累计2万亿贷款,为4800万小微企业提供了免费医疗保险金服务,这背后是我们这么多年的积累。 而今天看到一个双11数据,我们为343万淘宝商户提供了将近2000亿贷款,为他们今天保障双11能够顺利进行,做了一个很大的保障,而这个数据是去年的将近34.3%倍,这个背后我们做了很多,今年我们为很多小微商户提供3000万的一个资金成本降低的能力,让他们贷款能够更方便。这是一元钱也能贷款,而背后体现的是什么?十年的背后体现的是什么,十年的第二个变化,无差别的服务,这是蚂蚁金服一直想做的一个事情,我们期望于让小企业能够享受中大型企业同样的金融服务,能让普惠金融,让更多中小微企业享受,这是我们的第二个变化,无差别服务。 还想和大家介绍一个巴基斯坦的消费者的故事,他第一次参加大促,他第一次参加整个电商大促,他可以在阿里电商平台上,比如Lazada、Daraz进行购物,跟中国很多双11用户同样的感受,而且他还可以用类似于中国支付宝这样的钱包,同样去付款,这样的事情,我们还在四个国家发生,菲律宾的钱包,印尼的钱包,巴基斯坦钱包,还有孟加拉钱包,这是我们说的第三个变化,无疆界的市场,能够让全球消费者、小企业自由进入全场去消费,和中国双11消费者、卖家同样的待遇,我们期望于真的是为20亿消费者服务,让全球小卖家能够享受到这样的服务,让消费者享受同样的服务,这是我们说的第三个变化,无疆界市场。总结一下,从有到无,从有限制的交易到无限制的交易,从有差别的服务到无差别的服务,能让更多中小企业享受同样的金融服务,无疆界的市场,能够实现全球买、全球卖、全球收、全球付,这样一个能力。而这背后显示无差别的服务,无限制的交易和无疆界的市场,背后就是这个数字经济时代的到来,而这个数字经济时代的到来,是能够带来无限的想象力,而这个无限的想象力,背后是我们想让任何人在任何地点、用任何货币去买任何商品,这我们十年来的变化。十年来,我们有变化,还有一些不变的东西,不变的是什么,首先一点,阿里巴巴的使命、愿景,让天下没有难做的生意,而蚂蚁金服同样秉承这样的使命、愿景,我们期望于通过科技去创造价值,解决实体经济发展的问题。在这十多年,我们随着双11,真正用软科技产生很多相关产品,像担保交易、快捷支付、小微贷款、条形码、余额码、芝麻分、蚂蚁森林,还有很多相关的,比如区块链汇款,背后都体现了蚂蚁金服相关的科技理念,我们希望用科技创造价值,我们期望解决实体经济发展的问题,这是我们的使命、愿景。我们今天有将近1.5亿海外商品通过区块链技术,今天很多时候,你去买牛奶、钻石,买很多东西,都有它自己的身份证,他可以知道从什么原产地过来的,经过什么样的物流路线,背后是科技的力量,在这科技背后是我们这么多年在科技,尤其在区块链方面的积累和不断攻破一个又一个难点。我们期望于区块链能够改变很多信任的问题,在过去几年,我们区块链做两件事,第一是做相应的核心技术,我们期望打造区块链能力,提供更大、高可用、更安全的能力,另外我们还是坚持以场景驱动,通过场景去锻炼相应的技术能力,这是我们用科技相关的能力。今天我们看到的蚂蚁金服支持双11所有的相关技术产品已经100%全面对外开放,这是我们对外的承诺,十年消费拉动经济增长第一大引擎,作为中国的增长,三驾马车,消费是很大的推动力,另外科技从事服务,唤醒蓬勃的消费力,我们期望服务九亿消费者,数千万小微经营企业,能够帮助他去完成很多东西。下一个十年,我们想要做什么,还有哪些东西没来,我们还能走多远,我们看一下,今天很多小女孩买衣服已经习惯了物流运费险,她可以买回来再试,试完以后不行再退回去,这造就了很多相关市场,包括家居行业,都因为物流运费现象而改变很多。我们看到一些事情的发生,海外用户刚刚用上了退运费险,包括澳大利亚、美国等等七个国家都在用这样的物流运费险,中国正在发生的事情也在影响全球。我们期望于这只是一个开始,就像今天是双11一样,Just A Beginning,所有都是一个开始,我们期望于用更多的技术,能够创造更多不可能,能够让全球消费者和卖家、中小企业,能够享受中国同样的普惠金融,科技驱动这样一个能力。让我们继续从有到无,突破边界,带给20亿全球消费者、1亿全球小微经营者的无限想象力,让我们继续相信科技、相信未来。

November 13, 2018 · 1 min · jiezi

1.6W star 的 JCSprout 阅读体验大提升

万万没想到 JCSprout 截止目前居然有将近1.6W star。真的非常感谢各位大佬的支持。年初时创建这个 repo 原本只是想根据自己面试与被面试的经历记录一些核心知识点,结果却是越写越多。<!–more–>在我自己宣传和其他技术大佬(包括阮大)的助攻之下连续两个月都在 GitHub trending Java片区的榜首。甚至有一次还一跃到整个 GitHub 的第一,同时还有帮助一些同学拿到了大厂 offer。扯了这么多进入这次的正题。之前有一朋友建议将文档以 gitbook 的形式查看,一直没有时间弄。直到有一天我看到了 docsify 这个项目,瞬间被它的外观,阅读方式所吸引。于是抽了一晚上把所有的文章全部迁移过去。现在打开 https://crossoverjie.top/JCSp… 即可看到全新的主页,大概长这样:确实不管从颜值还是阅读方式来说都非常不错;希望新的界面能让更多的人看的进去学到点东西。同时也更新完善了其中的一些内容。比如有些写的早的内容其实并不完善,也优化的处理了。同时欢迎更多朋友参与进来,不管是提新的点子、修改 bug 都是可以。之前的文章也留了不少坑,包括 cicada 还有好几个 bug 待处理、推送的示例代码以及 Kafka 源码的后续更新。突然有点像写长篇小说的感觉,还好没有多少人催更????。不出意外本周会再更新一篇,请持续关注。你的点赞与转发是最大的支持。

November 6, 2018 · 1 min · jiezi

轴动效果插件,类似Github404页面

轴动效果 axial3d3D效果页插件,类似 Github404 页面动画。安装引入 Installnpm install axial3dor<script src=“https://unpkg.com/axial3d"></script>例子 ExampleDemo<html><head> <title>Demo - Axial3d</title></head><body> <script src=“https://unpkg.com/axial3d"></script> <div id=“axial3d”></div> <script> (function () { var options = { selector: ‘#axial3d’, imgs: [ {src: ‘https://bestvist.github.io/axial3d/public/demo1/bg.png’, left: ‘50px’, top: ‘10px’}, {src: ‘https://bestvist.github.io/axial3d/public/demo1/2.png’, left: ‘150px’, top: ‘10px’}, {src: ‘https://bestvist.github.io/axial3d/public/demo1/3.png’, left: ‘50px’, top: ‘300px’}, {src: ‘https://bestvist.github.io/axial3d/public/demo1/4.png’, left: ‘300px’, top: ‘300px’} ] } var effect = new Axial3d(options); })() </script></body></html>属性 Propsoptions属性说明类型可选值默认值selector元素选择器String–imgs图片组Array–transform动画形式Stringtranslate / rotatetranslateswing动画幅度Number-5imgs options属性说明类型可选值默认值src图像路径String–top图片顶部定位String–bottom图片底部定位String–left图片左侧定位String–right图片右侧定位String–static图片是否静态,不随鼠标转动Booleantrue / falsefalse方法 Methods事件名称说明回调参数destory取消事件监听-项目地址 喜欢的欢迎star????????

October 30, 2018 · 1 min · jiezi

yum 安装 2.x 版本的git

官方教程,在 Linux/Unix 系统中,通过工具在中安装 git,这种方式比较简单,便于升级卸载工具,网上搜到的全是源码编译安装。下面介绍在 CentOS 系统中,通过 yum 来安装 gitRed Hat Enterprise Linux, Oracle Linux, CentOS, Scientific Linux, et al.RHEL and derivatives typically ship older versions of git. You can download a tarball and build from source, or use a 3rd-party repository such as the IUS Community Project to obtain a more recent version of git.官方文档说 git 在 RHEL 和衍生产品通常都会发布旧版本的 git,我们需要源码编译安装,或者使用第三方存储库(如IUS社区项目)。现在我们通过,IUS社区下载 ius-release.rpm 文件进行安装# 注意下载不同的版本,本机 CentOS 7wget https://centos7.iuscommunity.org/ius-release.rpm# 安装rpm文件rpm -ivh ius-release.rpm查看可安装的git安装包repoquery –whatprovides git# git-0:1.8.3.1-13.el7.x86_64# git2u-0:2.16.5-1.ius.centos7.x86_64# git2u-0:2.16.2-1.ius.centos7.x86_64# git2u-0:2.16.4-1.ius.centos7.x86_64# git-0:1.8.3.1-14.el7_5.x86_64卸载我本机的 1.8.3 的 git,安装 2.16.5 的 git# 卸载老的版本yum remove git# 安装新的版本yum install git2u原文收录在这里 ...

October 29, 2018 · 1 min · jiezi

一份针对于新手的多线程实践

前言前段时间在某个第三方平台看到我写作字数居然突破了 10W 字,难以想象高中 800 字作文我都得巧妙的利用换行来完成(懂的人肯定也干过????)。干了这行养成了一个习惯:能撸码验证的事情都自己验证一遍。于是在上周五通宵加班的空余时间写了一个工具:https://github.com/crossoverJie/NOWS利用 SpringBoot 只需要一行命令即可统计自己写了多少个字。java -jar nows-0.0.1-SNAPSHOT.jar /xx/Hexo/source/_posts传入需要扫描的文章目录即可输出结果(目前只支持 .md 结尾 Markdown 文件)当然结果看个乐就行(40 几万字),因为早期的博客我喜欢大篇的贴代码,还有一些英文单词也没有过滤,所以导致结果相差较大。如果仅仅只是中文文字统计肯定是准的,并且该工具内置灵活的扩展方式,使用者可以自定义统计策略,具体请看后文。其实这个工具挺简单的,代码量也少,没有多少可以值得拿出来讲的。但经过我回忆不管是面试还是和网友们交流都发现一个普遍的现象:大部分新手开发都会去看多线程、但几乎都没有相关的实践。甚至有些都不知道多线程拿来在实际开发中有什么用。为此我想基于这个简单的工具为这类朋友带来一个可实践、易理解的多线程案例。至少可以让你知道:为什么需要多线程?怎么实现一个多线程程序?多线程带来的问题及解决方案?单线程统计再谈多线程之前先来聊聊单线程如何实现。本次的需求也很简单,只是需要扫描一个目录读取下面的所有文件即可。所有我们的实现有以下几步:读取某个目录下的所有文件。将所有文件的路径保持到内存。遍历所有的文件挨个读取文本记录字数即可。先来看前两个如何实现,并且当扫描到目录时需要继续读取当前目录下的文件。这样的场景就非常适合递归: public List<String> getAllFile(String path){ File f = new File(path) ; File[] files = f.listFiles(); for (File file : files) { if (file.isDirectory()){ String directoryPath = file.getPath(); getAllFile(directoryPath); }else { String filePath = file.getPath(); if (!filePath.endsWith(".md")){ continue; } allFile.add(filePath) ; } } return allFile ; }}读取之后将文件的路径保持到一个集合中。需要注意的是这个递归次数需要控制下,避免出现栈溢出(StackOverflow)。最后读取文件内容则是使用 Java8 中的流来进行读取,这样代码可以更简洁:Stream<String> stringStream = Files.lines(Paths.get(path), StandardCharsets.UTF_8);List<String> collect = stringStream.collect(Collectors.toList());接下来便是读取字数,同时要过滤一些特殊文本(比如我想过滤掉所有的空格、换行、超链接等)。扩展能力简单处理可在上面的代码中遍历 collect 然后把其中需要过滤的内容替换为空就行。但每个人的想法可能都不一样。比如我只想过滤掉空格、换行、超链接就行了,但有些人需要去掉其中所有的英文单词,甚至换行还得留着(就像写作文一样可以充字数)。所有这就需要一个比较灵活的处理方式。看过上文《利用责任链模式设计一个拦截器》应该很容易想到这样的场景责任链模式再合适不过了。关于责任链模式具体的内容就不在详述了,感兴趣的可以查看上文。这里直接看实现吧:定义责任链的抽象接口及处理方法:public interface FilterProcess { /** * 处理文本 * @param msg * @return / String process(String msg) ;}处理空格和换行的实现:public class WrapFilterProcess implements FilterProcess{ @Override public String process(String msg) { msg = msg.replaceAll("\s", “”); return msg ; }}处理超链接的实现:public class HttpFilterProcess implements FilterProcess{ @Override public String process(String msg) { msg = msg.replaceAll("^((https|http|ftp|rtsp|mms)?:\/\/)[^\s]+",""); return msg ; }}这样在初始化时需要将这些处理 handle 都加入责任链中,同时提供一个 API 供客户端执行即可。这样一个简单的统计字数的工具就完成了。多线程模式在我本地一共就几十篇博客的条件下执行一次还是很快的,但如果我们的文件是几万、几十万甚至上百万呢。虽然功能可以实现,但可以想象这样的耗时绝对是成倍的增加。这时多线程就发挥优势了,由多个线程分别去读取文件最后汇总结果即可。这样实现的过程就变为:读取某个目录下的所有文件。将文件路径交由不同的线程自行处理。最终汇总结果。多线程带来的问题也不是使用多线程就万事大吉了,先来看看第一个问题:共享资源。简单来说就是怎么保证多线程和单线程统计的总字数是一致的。基于我本地的环境先看看单线程运行的结果:总计为:414142 字。接下来换为多线程的方式:List<String> allFile = scannerFile.getAllFile(strings[0]);logger.info(“allFile size=[{}]",allFile.size());for (String msg : allFile) { executorService.execute(new ScanNumTask(msg,filterProcessManager));}public class ScanNumTask implements Runnable { private static Logger logger = LoggerFactory.getLogger(ScanNumTask.class); private String path; private FilterProcessManager filterProcessManager; public ScanNumTask(String path, FilterProcessManager filterProcessManager) { this.path = path; this.filterProcessManager = filterProcessManager; } @Override public void run() { Stream<String> stringStream = null; try { stringStream = Files.lines(Paths.get(path), StandardCharsets.UTF_8); } catch (Exception e) { logger.error(“IOException”, e); } List<String> collect = stringStream.collect(Collectors.toList()); for (String msg : collect) { filterProcessManager.process(msg); } }}使用线程池管理线程,更多线程池相关的内容请看这里:《如何优雅的使用和理解线程池》执行结果:我们会发现无论执行多少次,这个值都会小于我们的预期值。来看看统计那里是怎么实现的。@Componentpublic class TotalWords { private long sum = 0 ; public void sum(int count){ sum += count; } public long total(){ return sum; }}可以看到就是对一个基本类型进行累加而已。那导致这个值比预期小的原因是什么呢?我想大部分人都会说:多线程运行时会导致有些线程把其他线程运算的值覆盖。但其实这只是导致这个问题的表象,根本原因还是没有讲清楚。内存可见性核心原因其实是由 Java 内存模型(JMM)的规定导致的。这里引用一段之前写的《你应该知道的 volatile 关键字》一段解释:由于 Java 内存模型(JMM)规定,所有的变量都存放在主内存中,而每个线程都有着自己的工作内存(高速缓存)。线程在工作时,需要将主内存中的数据拷贝到工作内存中。这样对数据的任何操作都是基于工作内存(效率提高),并且不能直接操作主内存以及其他线程工作内存中的数据,之后再将更新之后的数据刷新到主内存中。这里所提到的主内存可以简单认为是堆内存,而工作内存则可以认为是栈内存。如下图所示:所以在并发运行时可能会出现线程 B 所读取到的数据是线程 A 更新之前的数据。更多相关内容就不再展开了,感兴趣的朋友可以翻翻以前的博文。直接来说如何解决这个问题吧,JDK 其实已经帮我们想到了这些问题。在 java.util.concurrent 并发包下有许多你可能会使用到的并发工具。这里就非常适合 AtomicLong,它可以原子性的对数据进行修改。来看看修改后的实现:@Componentpublic class TotalWords { private AtomicLong sum = new AtomicLong() ; public void sum(int count){ sum.addAndGet(count) ; } public long total(){ return sum.get() ; }}只是使用了它的两个 API 而已。再来运行下程序会发现结果居然还是不对。甚至为 0 了。线程间通信这时又出现了一个新的问题,来看看获取总计数据是怎么实现的。List<String> allFile = scannerFile.getAllFile(strings[0]);logger.info(“allFile size=[{}]",allFile.size());for (String msg : allFile) { executorService.execute(new ScanNumTask(msg,filterProcessManager));}executorService.shutdown();long total = totalWords.total();long end = System.currentTimeMillis();logger.info(“total sum=[{}],[{}] ms”,total,end-start);不知道大家看出问题没有,其实是在最后打印总数时并不知道其他线程是否已经执行完毕了。因为 executorService.execute() 会直接返回,所以当打印获取数据时还没有一个线程执行完毕,也就导致了这样的结果。关于线程间通信之前我也写过相关的内容:《深入理解线程通信》大概的方式有以下几种:这里我们使用线程池的方式:在停用线程池后加上一个判断条件即可:executorService.shutdown();while (!executorService.awaitTermination(100, TimeUnit.MILLISECONDS)) { logger.info(“worker running”);}long total = totalWords.total();long end = System.currentTimeMillis();logger.info(“total sum=[{}],[{}] ms”,total,end-start);这样我们再次尝试,发现无论多少次结果都是正确的了:效率提升可能还会有朋友问,这样的方式也没见提升多少效率啊。这其实是由于我本地文件少,加上一个文件处理的耗时也比较短导致的。甚至线程数开的够多导致频繁的上下文切换还是让执行效率降低。为了模拟效率的提升,每处理一个文件我都让当前线程休眠 100 毫秒来模拟执行耗时。先看单线程运行需要耗时多久。总共耗时:[8404] ms接着在线程池大小为 4 的情况下耗时:总共耗时:[2350] ms可见效率提升还是非常明显的。更多思考这只是多线程其中的一个用法,相信看到这里的朋友应该多它的理解更进一步了。再给大家留个阅后练习,场景也是类似的:在 Redis 或者其他存储介质中存放有上千万的手机号码数据,每个号码都是唯一的,需要在最快的时间内把这些号码全部都遍历一遍。有想法感兴趣的朋友欢迎在文末留言参与讨论????????。总结希望看完的朋友心中能对文初的几个问题能有自己的答案:为什么需要多线程?怎么实现一个多线程程序?多线程带来的问题及解决方案?文中的代码都在此处。https://github.com/crossoverJie/NOWS你的点赞与转发是最大的支持。 ...

October 29, 2018 · 2 min · jiezi

机器人伪装成人类在 GitHub 上为开源项目修复 bug

2018年1月12日下午12:28,GeoWebCache/geowebcache 项目和往常一样在 github 上提交代码。2 分钟后 travis-ci 上的单元测试以失败告终,测试结果显示有 2 个测试用例报错了。没过多久,2018年1月12日下午1点35分,一名叫 LucEsape 的开发者发布了一个修复补丁。2018年1月12日下午2:10,开发者接受了补丁,并将其合并到代码库,并评论到:“很奇怪,我以为我已经修好了……也许我在其他地方做过。谢谢你补丁!“ 这是一个伟大的时刻,因为谁也不曾先到,修复这个 bug 的程序员 LucEsape 是一个机器人。他叫 Repairnator。Repairnator 是由 KTH 瑞典皇家理工学院的软件技术教授 Martin Monperrus 开发。它会监控开源软件在持续集成期间发现的 bug,并尝试自动修复它们。如果它成功合成了一个有效的补丁,那么 Repairnator 会伪装成人类身份向人类开发者提交此补丁。到目前为止,Repairnator 已经成功生成了 5 个补丁,并被人类开发者永久地合并到代码库中。这是自动程序修复软件工程研究中新的里程碑。Repairnator 使用补丁的形式修复代码中的 bug。例如,在以下补丁中,开发者修改了 if 语句的条件:- if (x <10)+ if (x <= 10)foo();程序修复机器人是一种试图合成源代码补丁的人工代理。他能够帮助人类分析和修复软件中的 bug。Repairnator 机器人的工作原理:为此,研发团队提出了一个概念:human-competitive(人类竞争力)。程序修复机器人必须在人类修复 bug 之前找到高质量的修补程序。在这种情况下,如果补丁满足时效性和质量这两个条件,则可以认为补丁具有人类竞争力。及时性是指系统必须早于人类找到补丁。此外,与人类编写的补丁相比,机器人生成的补丁必须足够正确。还有一个方面需要考虑。因为人类工程师并不会轻易接受机器人的贡献,即使它们是正确的。原因是人类往往对机器有偏见,如果贡献来自人类同伴,则更容忍错误。这意味着如果开发者知道修补程序来自机器人,那么开发者可能会更高地调整修补程序的质量。为了解决这个问题,团队为 Repairnator 伪造了一个人类身份。团队创建了一个名为 Luc Esape 的 GitHub 用户。Luc 有个头像,看起来像一个初级开发者,渴望在 GitHub 上做开源贡献。现在,出于道德的考虑,Luc 的真实身份已经在他的每个 Pull Request 中被披露。据团队的 PPT 显示,这个名字来源于 Esculape,阿斯克勒庇俄斯,希腊神话中的医神。而 Luc 的头像也是阿斯克勒庇俄斯。他是太阳神阿波罗的儿子,他的雕塑和画像都是手持大蛇的形象。据说,有一天,阿斯克勒庇俄斯到山上游玩,一条蛇从草丛里窜过来想咬他,他急忙用一块石头将蛇打死了。一会儿,他发现另一条蛇游过来,将一株草放进死蛇的嘴里,死蛇竟然就活过来了,随即自如地消失在草丛里。阿斯克勒庇俄斯就这样偶然地找到了那种能起死回生的草。以后,有许多已经去世的人都被阿斯克勒庇俄斯妙手回春地拉回了人世。阿斯克勒庇俄斯死后,被宙斯升上天空变成了蛇夫座。Repairnator 机器人从 2017 年 1 月开始运营,分为三个不同阶段。在 2017 年 1 月的一个月内,使用原型的初始版本进行了试验性实验。从 2017 年 2 月 1 日到 2017 年 12 月 31 日,Repairnator 开始正式运行,其中包含 14,188 个项目。从 2018 年 1 月 1 日到 2018 年 6 月 30 日,Repairnator 实时监控 Travis CI 构建流。Repairnator 已经分析了 11,523 次失败的测试。对于 3,551 个(30.82%),Repairnator 能够在本地重现这些失败的测试。在 3,551 次修复尝试中,Repairnator 发现了 15 个可以使 CI 构建通过的补丁。然而,补丁分析显示,这些补丁中没有一个被采纳,因为它们要么太晚(Repairnator 在人类开发者之后产生补丁)或者质量低(它们能够是项目构建成功也许是巧合)。随后对 Repairnator 进行了改进,终于使它可以成功工作了。Repairnator 已经生成了 5 个符合上面定义的人类竞争力标准的补丁:1)补丁是在人类之前生成的,2)人类开发者接受补丁作为有效贡献,补丁在主代码库中合并。时至今日,Repairnator 已经修复了 5 个 bug:Jan 12, 2018, aaime/geowebcache/pull/1, “Thanks for the patch!”Mar 23, 2018, parkito/BasicDataStructuresAndAlgorithms/pull/3 “merged commit 140a3e3 into parkito:develop”April 5, 2018, dkarv/jdcallgraph/pull/2 “Thanks!”May 3, 2018, eclipse/ditto/pull/151 “Cool, thanks for going through the Eclipse process and for the fix.”June 25, 2018, donnelldebnam/CodeU-Spring-2018-29/pull/59 “Thanks!!”对此,你有什么看法?可以评论在下面 ...

October 26, 2018 · 1 min · jiezi

Github Actions 介绍

本文转自 FEPulse 公众号(微信搜索 FEPulse,精选国内外最新前端资讯,为你把握前端脉搏)。Github Actions 是 GitHub Universe 大会上发布的,被 Github 主管 Sam Lambert 称为“再次改变软件开发”的一款重磅功能(“we believe we will once again revolutionize software development.”)。本文目的是向大家介绍这一 Github 全新的功能,更多内容可以查看文末的拓展阅读。什么是 Github Actions,官网的介绍是:With GitHub Actions you can automate your workflow from idea to production.还是很迷糊。不急,我们先看现在的 Github 是什么?代码仓库,一个提供了分布式版本控制和源代码管理的代码仓库。想象一下这样一种场景,你写好了一个网站的代码,并且存储到了 Github 上,但完事了吗?没有,你还需要部署代码才能让别人访问你的网站。另外,如果你修改了代码,还需要单独测试。理想的情况应该是:当你将代码提交到 master 时,测试、部署等等所有工作自动执行。之前,Travis、Pre-commit Hooks 可以帮助我们实现部分自动化,而现在有了 Github Actions,通通皆可抛。Github Actions 可以自动化和定制化项目的 Workflow,像官网显示的那样。Workflow 比较好理解,将对项目的操作概括和按顺序整理,在遇到触发条件时 Workflow 就会按照开发者事先的设置串行或并行地运行一系列 Action,这就是 Github Actions 名称的由来。上面那张图中,Action 即一个个方框,Workflow 即将 Action 连接起来的图表。触发条件有很多种,比如 push 代码到 Github,比如 assign 了一个 issue,比如创建了一个 milestone 等等,这些都是 Github 提供的事件,工作流只要监听关心的事件即可。(目前 Github 一共提供了 26 种事件,想看所有事件可以查看:https://developer.github.com/…)直观地理解了 Workflow 和 Action,下面再对 Github Actions 的核心 Action 作更深入地理解。Action 是一小段可以运行的代码,可以用来做很多事情。比如你可以设置一个自动测试的 Action,当提交代码到 Github 后,Action 便会触发自动测试;再比如你可以设置一个自动部署的 Action,当代码通过测试后直接部署到腾讯云、阿里云、Azure 上。除此以外,你还可以拿 Action 做很多事。比如当前项目是一个 NPM Package,你可以设置一个 Action 用来自动 Publish;比如你需要监听项目的 issue,所以你可以设置一个 Action,当项目中有 issue 创建,给你的微信发一条提醒;比如 minify 或 uglify 你的 JS 代码……Action 的想象空间很大,全看你的需求。目前 Github 一共发布了 450 个示例 Action,你也可以创建、分享你的 Action,别人也能搜到你的 Action。讲道理,讲完基本概念下面就要开始实操了,但 Github Acions 还处于 Beta 阶段,并没有对所有人开放,想要提前使用的可以在官网尝试申请。因为我还没拿到测试资格,所以后面有机会的话再说吧。不过已经有 Github Actions 的第一批实践者写了一篇文章关于如何设置以及如何创建一个 Action。拓展阅读:https://github.com/features/a…:官网;https://developer.github.com/…: 文档;Introducing GitHub Actions:详细介绍了如何设置 Action 和创建新的 Action;GitHub Actions: built by you, run by us:一些 Action Demo;GitHub Actions Creates a Buzz for Automated Dev Workflows:新闻报道;GitHub grabs a piece of the Actions: ‘A project that will do for software development what we did for the pull request’:新闻报道。 ...

October 22, 2018 · 1 min · jiezi

vue组件从开发到发布

组件化是前端开发非常重要的一部分,从业务中解耦出来,可以提高项目的代码复用率。更重要的是我们还可以打包发布,俗话说集体的力量是伟大的,正因为有许许多多的开源贡献者,才有了现在的世界。不想造轮子的工程师,当不了合格的搬运工 。让我们来了解一下vue组件从开发到打包发布流程,并配置Github主页。本文以 vue-clock2 组件为例,欢迎star^_^~~ 项目地址目标框架:vue打包工具:webpack发布源:npm代码托管:github项目结构|– node_modules|– src| |– index.js| |– vue-clock.vue|– docs| |– index.html| |– index.css|– distsrc: 组件相关代码。node_modules: 组件依赖包。docs: 说明文档,组件简单的可以单个页面,也可以使用vuepress。dist: 打包后组件内容,一般 package.json 的 main 入口指向这个文件夹里的文件。组件开发vue组件开发相对来讲还是比较容易的,创建一个 vue-clock.vue 文件,组件的相关逻辑实现。该组件主要实现一个基于 time 属性输入,显示对应时间的钟表样式。 <div class=“clock”> <div class=“clock-circle”></div> <div class=“clock-hour” :style="{transform:hourRotate}"></div> <div class=“clock-minute” :style="{transform:minuteRotate}"></div> <b class=“hour” v-for=“h in timeList” :key=“h”> <span>{{h}}</span> </b> </div>通过元素画出钟表的样式,基于 css3的transform 属性旋转出每个时间点。因为钟表的时针并不是直接跳到下一个点的,所以需要计算出不同分钟时,时钟指针的旋转角度。后续增加了不指定时间的情况,显示当前时间并每分钟自动更新。export default { data() { return { timeList: [12, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11], hourRotate: “rotatez(0deg)”, minuteRotate: “rotatez(0deg)” }; }, props: [“time”], watch: { time() { this.show(); } }, methods: { show() { this.showTime(); if (this._timer) clearInterval(this._timer); if (!this.time) { this._timer = setInterval(() => { this.showTime(); }, 60 * 1000); } }, showTime() { let times; if (this.time) { times = this.time.split(":"); } else { const now = new Date(); times = [now.getHours(), now.getMinutes()]; } let hour = +times[0]; hour = hour > 11 ? hour - 12 : hour; let minute = +times[1]; let hourAngle = hour * 30 + minute * 6 / 360 * 30; let minuteAngle = minute * 6; this.hourRotate = rotatez(${hourAngle}deg); this.minuteRotate = rotatez(${minuteAngle}deg); } }, mounted() { this.show(); }, destroyed() { if (this._timer) clearInterval(this._timer); }};还有一些钟表的布局样式,可以直接在项目里查看。vue-clock.vue接着我们需要抛出组件,以便在项目中引入使用。 // src/index.js import Clock from ‘./vue-clock.vue’; export default Clock; if (typeof window !== ‘undefined’ && window.Vue) { window.Vue.component(‘clock’, Clock); }这里,组件开发的部分已经完成了,喝杯咖啡,check一下代码,我们要把它打包发布到npm上。打包发布打包前确认一下 webpack 的配置文件输出。 output: { path: path.resolve(__dirname, ‘./dist’), publicPath: ‘/dist/’, filename: ‘vue-clock.min.js’, library: ‘Clock’, libraryTarget: ‘umd’, umdNamedDefine: true }打包组件文件到 dist 文件夹中。npm run buildnpm发布配置package.json{ “name”: “vue-clock2”, “description”: “Vue component with clock”, “version”: “1.1.2”, “author”: “bestvist”, “keywords”: [ “vue”, “component”, “clock”, “time” ], “main”: “dist/vue-clock.min.js”, “license”: “MIT”, “homepage”: “https://bestvist.github.io/vue-clock2/"}登录npm如果使用淘宝镜像的,需要先修正一下镜像源。npm config set registry https://registry.npmjs.org/// 查看登录人npm whoami// 登录npm login// 发布npm publish如果看到类似信息,说明发布成功。npm notice+ vue-clock2@1.1.2Github主页把项目上传到github托管,配置一份基本 README.md 说明文档。因为组件已经发布到npm上,所以可以配置几个徽章在README中。// npm 版本npm version// npm 下载量npm download更多的徽章配置可以查看shields接着描述一下组件的引入和使用方法:安装:npm install vue-clock2使用:<template> <clock :time=“time”></clock></template><script> import Clock from ‘vue-clock2’; export default { components: { Clock }, data () { return { time: ‘10:40’ } } }</script>更详细的交互或是属性说明就交给文档来解决了。在 github 项目上通过 settings 指定 GitHub Pages组件文档说明应包括:组件引入方法组件使用方法一个简单的例子组件属性描述说明总结开发 -> 发布 -> 托管一个组件轮子的制作流程大致介绍完了,希望本文可以帮助到您。原文链接 ...

October 16, 2018 · 2 min · jiezi

Git 学习笔记

最近公司的代码管理工具要从SVN转到Git上,因此虽然之前用过Git,但是都是一些简单的推送提交,因此还是有必要进行一些系统的学习,这里做一下笔记,以备后询,且不定期更新。关于SVN和Git的比较已经有很多文章说过了,就不再赘述,本文的重点是如何使用常用的Git命令进行操作,冷门的就不说了,且比较零散,系统的学习推介廖雪峰的Git教程。声明下面用户名都为SHERlocked93,请自行修改成自己的用户名1. 概览工作区 Workspace暂存区 Stage / Index本地仓库 Repository远程仓库 Remote2. 修改2.1 暂存修改操作一览操作bash创建stashgit stash查看git stash list应用git stash apply stash@{<num>}删除git stash drop stash@{<num>}还原上一个暂存并删除暂存(如无conflict)git stash pop如果在工作的时候出现了临时需要解决的问题,而你又不希望提交,那么有个stash功能git stash在暂存后工作区会回退到最近的一个commit的状态,以便开建新分支;比如我们修复bug时,我们会通过创建新的bug分支进行修复,然后合并,最后删除;当手头工作没有完成时,先把工作现场git stash一下,然后去修复bug,修复后,再git stash pop,回到工作现场。2.2 撤销修改还未提交到暂存区当修改还没有被add的时候,可以使用git checkout – filename.txt来丢弃工作区某文件的修改,当然也可以把后面的文件改成*来撤销所有文件的修改。这是用仓库的文件覆盖工作区的文件。注意这里用的是–,如果没有这个–的话就变成切换分支了。还未提交到仓库如果你的修改已经被add到了暂存区,但是还没有被commit,那么可以使用git reset HEAD filename.txtgit checkout – filename.txt首先用reset来把修改撤回到工作区,再使用上面的checkout命令撤回工作区的修改。这里的reset相当于add的反操作。已经提交到仓库则可以版本回退git reset –hard 15zdx2s这里的–hard表示强制回退,丢弃本地的修改。这个回退比较野蛮,该版本号之后的提交都将不可见。撤销之前某一个提交git revert撤销一个提交的同时会创建一个新的提交,这是一个安全的方法,因为它不会重写提交历史。但实现上和reset是完全不同的。它撤销这个提交引入的更改,然后在最后加上一个撤销了更改的新提交,而不是从项目历史中移除这个提交。git revert 46af7z6相较于reset ,revert不会改变项目历史,对那些已经发布到共享仓库的提交来说这是一个安全的操作。其次git revert 可以将提交历史中的任何一个提交撤销、而reset会把历史上某个提交及之后所有的提交都移除掉,这太野蛮了。相比 reset,它不会改变现在的提交历史。因此,revert 可以用在公共分支上,reset 应该用在私有分支上。合并commit如果已经commit了怎么办,如果要撤回目前的commit,可以把它合并到上一个commit中git rebase -i HEAD~~在出现的两个提交信息的pick改为fixup3. 分支操作3.1 创建/查看/合并分支操作一览操作bash查看分支git branch查看本地和远程分支git branch -a在target分支上创建分支,没有则从当前分支git branch <branch-name> <target-branch>创建并切换分支git checkout -b <branch-name>合并某分支到当前分支git merge <branch-name>删除分支,只能删参与了合并的git branch -d <branch-name>强行删除git branch -D <branch-name>删除远程分支git push origin :<remote-branch-name>创建分支# 创建新分支git branch bug-fix# 查看分支,-a查看本地和远程的分支,-r查看远程分支,-l或没有只查看本地git branch -a# 切换到刚刚创建的分支git checkout bug-fix上面两个步骤可以合并为# 创建并切换到分支git checkout -b bug-fix如果修改一下本地文件之后在这个分支继续培育一个版本之后,怎么去合并到主分支呢git add *git commit -m “some change”# 切换到主分支git checkout master# 合并分支git merge bug-fix# 删除分支 (可选)git branch -d bug-fix如果master分支和新的分支都各自培育了版本,那么自动合并通常会失败,发生冲突conflict,此时需要打开文件解决冲突之后commit一个版本以完成合并git add *git commit -m “branch merge"这里提一下,merge的时候有几个主要模式,–no-ff、fast-forward,其中fast-forward是默认的fast-forward:在master开始的新分支前进了几个版本之后如果需要merge回来,此时master并没有前进,那么这个模式就是把HEAD与master指针指向新分支上,完成合并。这种情况如果删除分支,则会丢失分支信息,因为在这个过程中并没有创建commit。–no-ff:关闭默认的fast-forward模式,也就是在merge的时候生成一个新的commit,这样在分支历史上就可以看出分支信息。3.2 远程仓库操作操作一览操作bash克隆git clone <url>添加远程仓库git remote add <name> <url>删除远程仓库git remote rm <name>拉取git pull <remote-branch-name> <local-branch-name>推送本地所有分支到远程git push –all origin推送到远程同名分支git push origin <local-branch-name>推送本地分支到远程mastergit push origin <local-branch-name>:master把当前本地分支推送并创建到远程git push origin检出远程分支git checkout -b <new-local-branch-name> origin/<remote-branch-name>关于各个分支,哪些需要推送呢master分支是主分支,因此要时刻与远程同步;dev分支是开发分支,团队所有成员都需要在上面工作,所以也需要与远程同步;bug分支只用于在本地修复bug,就没必要推到远程了,除非老板要看看你每周到底修复了几个bug;feature分支是否推到远程,取决于你是否和你的小伙伴合作在上面开发。直接clone在github上创建一个新的项目之后,比如叫learn-git,那么可以直接clone下来,注意创建的时候不要选择 Initialize this repository with a README,我们要的是一个空的仓库git clone https://github.com/SHERlocked93/learn-git.git这样在本地就直接创建了一个空的文件夹learn-git,当然里面有.git文件夹。也可以使用SSH地址来clone,速度会快一些,也不用每次推送都输入口令,推介使用这种git clone git@github.com:SHERlocked93/learn-git.git添加一个文件filename.txt之后git add filename.txtgit commit -m “add filename.txt"git push -u origin master这样就把本地新建的文件push到了远程仓库本地与远程建立关联如果已经有了本地工程文件夹,如何分享到github远程仓库呢,当然此时我们已经在github上创建了一个新的空白项目,还是叫learn-git,在本地文件夹中git init# 关联远程库git remote add origin git@github.com:SHERlocked93/learn-git.gitgit push -u origin master就可以了,如果你的远程仓库已经有了提交,那么在push之前需要# 允许不想干库合并git pull origin master –allow-unrelated-historiesgit push -u origin master先拉取远程分支,注意这里–allow-unrelated-histories允许两个不想干的分支强行合并,再push;这样在github的网站上还能看到commit记录。也可以强硬一点直接强行推送# -f 强行推送git push -u origin master -f这样本地仓库就直接把远程仓库覆盖了,且github上也看不到历史commit了,如果不想被同事枪击的话,还是推介上一种做法。同步远程仓库那么已经clone的仓库如果希望同步原仓库新的提交怎么办# 从远程分支拉取代码到本地git pull upstream master# push到自己的库里git push origin master3.3 多人协作多人协作的工作模式通常是这样:首先,可以试图用git push origin <branch-name>推送自己的修改;如果推送失败,则因为远程分支比你的本地更新,需要先用git pull试图合并;如果合并有冲突,则解决冲突,并在本地提交;没有冲突或者解决掉冲突后,再用git push origin <branch-name>推送就能成功从远程抓取分支,使用git pull,如果有冲突,要先处理冲突,add->commit->push。如果git pull提示no tracking information,则说明本地分支和远程分支的链接关系没有创建,用命令git branch –set-upstream-to <branch-name> origin/<branch-name>。4. 标签操作操作一览操作bash查看所有标签git tag新建标签git tag <tagname>新建并制定说明git tag <tagname> -m <message> <bash>查看标签说明git show <tagname>删除标签git tag -d <tagname>推送某个标签到远程git push origin <tagname>推送所有未推送到远程的本地标签git push origin –tags合并远程仓库的标签到本地git pull origin –tags删除远程标签git push origin :refs/tags/<tagname>如果要删除远程分支,需要# 首先删除本地tag,假如tag是v0.9git tag -d v0.9# 再从远程删除git push origin :refs/tags/v0.95. 提交格式type:feat: 新特性,添加功能fix: 修改bugrefactor: 代码重构docs: 文档修改style: 代码格式修改, 注意不是 css 修改test: 测试用例修改chore: 其他修改, 比如构建流程, 依赖管理.网上的帖子大多深浅不一,甚至有些前后矛盾,在下的文章都是学习过程中的总结,如果发现错误,欢迎留言指出~推介阅读:廖雪峰 - Git教程github实现本地仓库与远程仓库同步图解 Git 命令git基本操作,一篇文章就够了!团队协作中的 Github flow 工作流程git 命令大全附件Git常用命令速查表: ...

October 13, 2018 · 2 min · jiezi

麻雀虽小五脏俱全的Vue拉勾项目,看看应该有帮助

全栈系列Vue版拉勾,客官们来瞧瞧模拟拉勾app系列—vue前端界面github地址,来猛戳吧前言本项目是本人在闲暇时间编写的一个初级引导项目,麻雀虽小五脏俱全,所使用的东西绝大多数在开发中都能用得到,但难免会存在很多地方需要完善。由于近期要备战法考,且工作繁忙,没有时间维护,还存在很多BUG或需要优化的地方,希望多多提出(有空了就改),当然能给个star什么的就更好了。为了方便访问,也加入了mock数据,但不是很全,若需要完整体验,请按照下方步骤实现。前端项目由Vue框架编写,其余部分涉及到node、python等可移至下方项目或自行查阅。注意:本项目个人开发练习,不作为任何商业用途todolist登录/注册 √页面首次加载面 √城市选择 √文章阅读 √搜索职位 √条件筛选 √搜索公司 √下拉刷新上拉加载 √数据排序 √图片懒加载 √提问评论√提问信息编辑 √言职社区记录 √话题关注 √收藏√求职意向√投递记录√注销√基础设置√头像修改 √简历填写√简历生成并下载pdf√ps:还有很多很多东西,不一一列举,想到啥就做啥技术栈前端:vue全家桶es6scssmint-uimockjsjquery转发服务器:nodeexpress实际api服务器:python3mongodb爬虫:python3效果演示首次载入登录注册首页文章阅读选择城市职位查看筛选排序排序2简历修改我的设置ps:还有更多的设置就不截图了,有点大,有兴趣的clone下去看看吧线上地址说明前端地址:https://github.com/qianbin01/…代理api地址:https://github.com/qianbin01/…api地址:https://github.com/qianbin01/…爬虫地址:https://github.com/qianbin01/…项目配置ubuntu 16.04运行步骤必备步骤:运行爬虫项目运行python-api项目运行node-api转发项目运行本项目本项目步骤:git clone https://github.com/qianbin01/...cd lagou_vuenpm install/yarn installnpm run dev/npm start浏览器访问http://localhost:8085点点你们的小手吧知乎专栏:https://zhuanlan.zhihu.com/c_… 掘金:https://juejin.im/user/5b8291… 思否:https://segmentfault.com/u/qi… 希望对大家有帮助大佬们,点赞一波吧

October 10, 2018 · 1 min · jiezi

2020年如何写一个现代的JavaScript库

我写过一些开源项目,在开源方面有一些经验,最近开到了阮老师的微博,深有感触,现在一个开源项目涉及的东西确实挺多的,特别是对于新手来说非常不友好最近我写了一个jslib-base,旨在从多方面快速帮大家搭建一个标准的js库,本文将已jslib-base为例,介绍写一个开源库的知识jslib-base 最好用的js第三方库脚手架,赋能js第三方库开源,让开发一个js库更简单,更专业文档所谓代码未动,文档先行,文档对于一个项目非常重要,一个项目的文档包括README.mdTODO.mdCHANGELOG.mdLICENSEdocREADME.mdREADME是一个项目的门面,应该简单明了的呈现用户最关心的问题,一个开源库的用户包括使用者和贡献者,所以一个文档应该包括项目简介,使用者指南,贡献者指南三部分项目简介用该简单介绍项目功能,使用场景,兼容性的相关知识,这里重点介绍下徽章,相信大家都见过别人项目中的徽章,如下所示徽章通过更直观的方式,将更多的信息呈现出来,还能够提高颜值,有一个网站专门制作各种徽章,可以看这里这里有一个README的完整的例子TODO.mdTODO应该记录项目的未来计划,这对于贡献者和使用者都有很重要的意义,下面是TODO的例子- [X] 已完成- [ ] 未完成CHANGELOG.mdCHANGELOG记录项目的变更日志,对项目使用者非常重要,特别是在升级使用版本时,CHANGELOG需要记录项目的版本,发版时间和版本变更记录## 0.1.0 / 2018-10-6- 新增xxx功能- 删除xxx功能- 更改xxx功能LICENSE开源项目必须要选择一个协议,因为没有协议的项目是没有人敢使用的,关于不同协议的区别可以看下面这张图(出自阮老师博客),我的建议是选择MIT或者BSD协议doc开源项目还应该提供详细的使用文档,一份详细文档的每个函数介绍都应该包括如下信息:函数简单介绍函数详细介绍函数参数和返回值(要遵守下面的例子的规则)- param {string} name1 name1描述- return {string} 返回值描述举个例子(要包含代码用例)// 代码特殊说明,比如特殊情况下会报错等构建理想的情况如下:库开发者美滋滋的写ES6+的代码;库使用者能够运行在浏览器(ie6-11)和node(0.12-10)中库使用者能够使用AMD或CMD模块方案库使用者能够使用webpack、rollup或fis等预编译工具理想很丰满,现实很。。。,如何才能够让开发者和使用者都能够开心呢,jslib-base通过babel+rollup提供了解决方案编译通过babel可以把ES6+的代码编译成ES5的代码,babel经理了5到6的进化,下面一张图总结了babel使用方式的变迁本文不讨论babel的进化史(后面会单独开一片博文介绍),而是选择最现代化的babel-preset-env方案,babel-preset-env可以通过提供提供兼容环境,而决定要编译那些ES特性其原理大概如下,首先通过ES的特性和特性的兼容列表计算出每个特性的兼容性信息,再通过给定兼容性要求,计算出要使用的babel插件首先需要安装babel-preset-env$ npm i –save-dev babel-preset-env然后新增一个.babelrc文件,添加下面的内容{ “presets”: [ [“env”, { “targets”: { “browsers”: “last 2 versions, > 1%, ie >= 6, Android >= 4, iOS >= 6, and_uc > 9”, “node”: “0.10” }, “modules”: false, “loose”: false }] ]}targets中配置需要兼容的环境,关于浏览器配置对应的浏览器列表,可以从browserl.ist上查看modules表示编出输出的模块类型,支持"amd",“umd”,“systemjs”,“commonjs”,false这些选项,false表示不输出任何模块类型loose代表松散模式,将loose设置为true,能够更好地兼容ie8以下环境,下面是一个例子(ie8不支持Object.defineProperty)// 源代码const aaa = 1;export default aaa;// loose falseObject.defineProperty(exports, ‘__esModule’, { value: true});var aaa = 1;exports.default = 1;// loose trueexports.__esModule = true;var aaa = 1;exports.default = 1;babel-preset-env解决了语法新特性的兼容问题,如果想使用api新特性,在babel中一般通过babel-polyfill来解决,babel-polyfill通过引入一个polyfill文件来解决问题,这对于普通项目很实用,但对于库来说就不太友好了babel给库开发者提供的方案是babel-transform-runtime,runtime提供类似程序运行时,可以将全局的polyfill沙盒化首先需要安装babel-transform-runtime$ npm i –save-dev babel-plugin-transform-runtime在.babelrc增加下面的配置"plugins": [ [“transform-runtime”, { “helpers”: false, “polyfill”: false, “regenerator”: false, “moduleName”: “babel-runtime” }]]transform-runtime,支持三种运行时,下面是polyfill的例子// 源代码var a = Promise.resolve(1);// 编译后的代码var _promise = require(‘babel-runtime/core-js/promise’);var a = _promise.resolve(1); // Promise被替换为_promise虽然虽然可以优雅的解决问题,但是引入的文件非常之大,比如只用了ES6中数组的find功能,可能就会引入一个几千行的代码,我的建议对于库来说能不用最好不用打包编译解决了ES6到ES5的问题,打包可以把多个文件合并成一个文件,对外提供统一的文件入口,打包解决的是依赖引入的问题rollup vs webpack我选择的rollup作为打包工具,rollup号称下一代打包方案,其有如下功能依赖解析,打包构建仅支持ES6模块Tree shakingwebpack作为最流行的打包方案,rollup作为下一代打包方案,其实一句话就可以总结二者的区别:库使用rollup,其他场景使用webpack为什么我会这么说呢?下面通过例子对比下webpack和rollup的区别假设我们有两个文件,index.js和bar.js,其代码如下bar.js对外暴漏一个函数barexport default function bar() { console.log(‘bar’)}index.js引用bar.jsimport bar from ‘./bar’;bar()下面是webpack的配置文件webpack.config.jsconst path = require(‘path’);module.exports = { entry: ‘./src/index.js’, output: { path: path.resolve(__dirname, ‘dist’), filename: ‘bundle.js’ }};下面来看一下webpack打包输出的内容,o(╯□╰)o,别着急,我们的代码在最下面的几行,上面这一大片代码其实是webpack生成的简易模块系统,webpack的方案问题在于会生成很多冗余代码,这对于业务代码来说没什么问题,但对于库来说就不太友好了注意:下面的代码基于webpack3,webpack4增加了scope hoisting,已经把多个模块合并到一个匿名函数中//(function(modules) { // webpackBootstrap // // The module cache // var installedModules = {}; // // // The require function // function webpack_require(moduleId) { // // // Check if module is in cache // if (installedModules[moduleId]) { // return installedModules[moduleId].exports; // } // // Create a new module (and put it into the cache) // var module = installedModules[moduleId] = { // i: moduleId, // l: false, // exports: {} // }; // // // Execute the module function // modules[moduleId].call(module.exports, module, module.exports, webpack_require); // // // Flag the module as loaded // module.l = true; // // // Return the exports of the module // return module.exports; // } // // // // expose the modules object (webpack_modules) // webpack_require.m = modules; // // // expose the module cache // webpack_require.c = installedModules; // // // define getter function for harmony exports // webpack_require.d = function(exports, name, getter) { // if (!webpack_require.o(exports, name)) { // Object.defineProperty(exports, name, { // configurable: false, // enumerable: true, // get: getter // }); // } // }; // // // getDefaultExport function for compatibility with non-harmony modules // webpack_require.n = function(module) { // var getter = module && module.__esModule ? // function getDefault() { return module[‘default’]; } : // function getModuleExports() { return module; }; // webpack_require.d(getter, ‘a’, getter); // return getter; // }; // // // Object.prototype.hasOwnProperty.call // webpack_require.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); }; // // // webpack_public_path // webpack_require.p = “”; // // // Load entry module and return exports // return webpack_require(webpack_require.s = 0); //})/*******************************************///([ / 0 / // (function(module, webpack_exports, webpack_require) { “use strict”; Object.defineProperty(webpack_exports, “__esModule”, { value: true }); / harmony import / var WEBPACK_IMPORTED_MODULE_0__bar = webpack_require(1); Object(WEBPACK_IMPORTED_MODULE_0__bar[“a” / default / ])() // }), / 1 / // (function(module, webpack_exports, webpack_require) { “use strict”; / harmony export (immutable) / webpack_exports[“a”] = bar; function bar() { // console.log(‘bar’) } // }) //]);下面来看看rollup的结果,rollup的配置和webpack类似export default { input: ‘src/index.js’, output: { file: ‘dist/bundle2.js’, format: ‘cjs’ }};下面看看rollup的产出,简直完美有没有,模块完全消失了,rollup通过顺序引入到同一个文件来解决模块依赖问题,rollup的方案如果要做拆包的话就会有问题,因为模块完全透明了,但这对于库开发者来说简直就是最完美的方案’use strict’;function bar() { // console.log(‘bar’);}bar();模块化方案在ES6模块化之前,JS社区探索出了一些模块系统,比如node中的commonjs,浏览器中的AMD,还有可以同时兼容不同模块系统的UMD,如果对这部分内容感兴趣,可以看我之前的一篇文章《JavaScript模块的前世今生》对于浏览器原生,预编译工具和node,不同环境中的模块化方案也不同;由于浏览器环境不能够解析第三方依赖,所以浏览器环境需要把依赖也进行打包处理;不同环境下引用的文件也不相同,下面通过一个表格对比下 浏览器(script,AMD,CMD)预编译工具(webpack,rollup,fis)Node引用文件index.aio.jsindex.esm.jsindex.js模块化方案UMDES Modulecommonjs自身依赖打包打包打包第三方依赖打包不打包不打包注意: legacy模式下的模块系统可以兼容ie6-8,但由于rollup的一个bug(这个bug是我发现的,但rollup并不打算修复,╮(╯▽╰)╭哎),legacy模式下,不可同时使用 export 与 export defaulttree shakingrollup是天然支持tree shaking,tree shaking可以提出依赖模块中没有被使用的部分,这对于第三方依赖非常有帮助,可以极大的降低包的体积举个例子,假设index.js只是用了第三方包is.js中的一个函数isString,没有treeshaking会将is.js全部引用进来而使用了treeshaking的话则可以将is.js中的其他函数剔除,仅保留isString函数规范无规矩不成方圆,特别是对于开源项目,由于会有多人参与,所以大家遵守一份规范会事半功倍编辑器规范首先可以通过.editorconfig来保证缩进、换行的一致性,目前绝大部分浏览器都已经支持,可以看这里下面的配置设置在js,css和html中都用空格代替tab,tab为4个空格,使用unix换行符,使用utf8字符集,每个文件结尾添加一个空行root = true[{.js,.css,.html}]indent_style = spaceindent_size = 4end_of_line = lfcharset = utf-8insert_final_newline = true[{package.json,.rc,.yml}]indent_style = spaceindent_size = 2代码风格其次可以通过eslint来保证代码风格一致,关于eslint的安装和配置这里不再展开解释了,在jslib-base中只需要运行下面的命令就可以进行代码校验了,eslint的配置文件位于config/.eslintrc.js$ npm run lint设计规范eslint只能够保证代码规范,却不能保证提供优秀的接口设计,关于函数接口设计有一些指导规则参数数量函数的参数个数最多不要超过5个可选参数可选参数应该放到后面可选参数数量超过三个时,可以使用对象传入可选参数,应该提供默认值参数校验与类型转换必传参数,如果不传要报错对下列类型要做强制检验,类型不对要报错(object, array, function)对下列类型要做自动转换(number, string, boolean)对于复合类型的内部数据,也要做上面的两个步骤对于number转换后如果为NaN,要做特殊处理(有默认值的赋值为默认值,无默认值的要报错)参数类型参数尽量使用值类型(简单类型)参数尽量不要使用复杂类型(避免副作用)使用复杂类型时,层级不要过深使用复杂数据类型时,应该进行深拷贝(避免副作用)函数返回值返回值可返回操作结果(获取接口),操作是否成功(保存接口)返回值的类型要保持一致返回值尽量使用值类型(简单类型)返回值尽量不要使用复杂类型(避免副作用)版本规范版本应该遵守开源社区通用的语义化版本版本号格式:x.y.zx 主版本号,不兼容的改动y 次版本号,兼容的改动z 修订版本号,bug修复Git commit规范代码的提交应该遵守规范,这里推荐一个我的规范测试没有单元测试的库都是耍流氓,单元测试能够保证每次交付都是有质量保证的,业务代码由于一次性和时间成本可以不做单元测试,但开源库由于需要反复迭代,对质量要求又极高,所以单元测试是必不可少的关于单元测试有很多技术方案,其中一种选择是mocha+chai,mocha是一个单元测试框架,用来组织、运行单元测试,并输出测试报告;chai是一个断言库,用来做单元测试的断言功能由于chai不能够兼容ie6-8,所以选择了另一个断言库——expect.js,expect是一个BDD断言库,兼容性非常好,所以我选择的是mocha+expect.js关于BDD与TDD的区别这里不再赘述,感兴趣的同学可以自行查阅相关资料有了测试的框架,还需要写单元测试的代码,下面是一个例子var expect = require(’expect.js’);var base = require(’../dist/index.js’);describe(‘单元测试’, function() { describe(‘功能1’, function() { it(‘相等’, function() { expect(1).to.equal(1); }); });});然后只需运行下面的命令,mocha会自动运行test目录下面的js文件$ mochamocha支持在node和浏览器中测试,但上面的框架在浏览器下有一个问题,浏览器没法支持require(’expect.js’),我用了一个比较hack的方法解决问题,早浏览器中重新定义了require的含义<script src="../../node_modules/mocha/mocha.js"></script><script src="../../node_modules/expect.js/index.js"></script><script> var libs = { ’expect.js’: expect, ‘../dist/index.js’: jslib_base }; var require = function(path) { return libs[path]; }</script>下面是用mocha生成测试报告的例子,左边是在node中,右边是在浏览器中可持续集成没有可持续集成的库都是原始人,如果每次push都能够自动运行单元测试就好了,这样就省去了手动运行的繁琐,好在travis-ci已经为我们提供了这个功能用GitHub登录travis-ci,就可以看到自己在GitHub上的项目了,然后需要打开下项目的开关,才能够打开自动集成功能第二步,还需要在项目中添加一个文件.travis.yml,内容如下,这样就可以在每次push时自动在node 4 6 8版本下运行npm test命令,从而实现自动测试的目的language: node_jsnode_js: - “8” - “6” - “4"其他内容开源库希望得到用户的反馈,如果对用户提的issue有要求,可以设置一个模版,用来规范github上用户反馈的issue需要制定一些信息通过提供.github/ISSUE_TEMPLATE文件可以给issue提供模版,下面是一个例子,用户提issue时会自动带上如下的提示信息### 问题是什么问题的具体描述,尽量详细### 环境- 手机: 小米6- 系统:安卓7.1.1- 浏览器:chrome 61- jslib-base版本:0.2.0- 其他版本信息### 在线例子如果有请提供在线例子### 其他其他信息jsminijsmini是基于jslib-base的一系列库,jsmini的理念是小而美,并且无第三方依赖,开源了很多能力,能够助力库开发者总结五年弹指一挥间,本文总结了自己做开源项目的一些经验,希望能够帮助大家,所有介绍的内容都可以在jslib-base里面找到jslib-base是一个拿来即用脚手架,赋能js第三方库开源,快速开源一个标准的js库最后再送给大家一句话,开源一个项目,重在开始,贵在坚持最后推荐下我的新书《React状态管理与同构实战》,深入解读前沿同构技术,感谢大家支持京东:https://item.jd.com/12403508.html当当:http://product.dangdang.com/25308679.html最后最后招聘前端,后端,客户端啦!地点:北京+上海+成都,感兴趣的同学,可以把简历发到我的邮箱: yanhaijing@yeah.net原文网址:http://yanhaijing.com/javascr… ...

October 9, 2018 · 3 min · jiezi

遇到过的github报错和解决方法

如果輸入$ git remote add origin (github账号名)/gitdemo(项目名).git 提示出錯信息:fatal: remote origin already exists. 解決办法如下: 1、先输入$ git remote rm origin 2、再输入$ git remote add origin(github账号名)/gitdemo(项目名).git 3、如果输入$ git remote rm origin 还是报错的话, error: Could not remove config section ‘remote.origin’. 需要修改gitconfig文件的內容 4、找到你的github的安装路径 5、找到一個名為gitconfig的文件,找到[remote “origin”]那一行刪掉。如果输入$ ssh -T git@github.com 出现错误提示:Permission denied (publickey).因为新生成的key不能加入ssh就会导致连接不上github。 解決办法如下: 1、先输入$ ssh-agent,再输入$ ssh-add ~/.ssh/id_key。 2、如果还是不行的話,输入ssh-add ~/.ssh/id_key 命令后出现报错 Could not open a connection to your authentication agent. 解決方法是key用GitGui的ssh工具生成,这样生成的时候key就直接保存在ssh中了,不需要再ssh-add命令加入了,其它的user,token等配置都用命令行來做。 3、最好检查一下在你复制id_rsa.pub文件的內容时有沒有产生多余的空格或空行,有些編輯器會帮你添加这些的。如果输入$ git push origin master 提示出错信息:error:failed to push som refs to ……. 解決办法法如下: 1、先输入$ git pull origin master //先把远程服务器github上面的文件拉下來 2、再输入$ git push origin master 3、如果出現报錯 fatal: Couldn’t find remote ref master或者 fatal: ‘origin’ does not appear to be a git repository以及 fatal:Could not read from remote repository. 4、则需要重新输入$ git remote add (github账号名)/gitdemo(项目名).git使用git在本地创建一个项目的过程 $ makdir ~/hello-world //创建一个项目hello-world $ cd ~/hello-world //打开这个项目 $ git init //初始化 $ touch README $ git add README //更新README文件 $ git commit -m ‘first commit’ //提交更新,并注释信息「first commit」 $ git remote add origin git@github.com:defnngj/hello-world.git //连接远程github項目 $ git push -u origin master //将本地项目更新到github项目上去 ...

October 5, 2018 · 1 min · jiezi

github 上有趣又实用的前端项目(持续更新,欢迎补充)

github 上有趣又实用的前端项目(持续更新,欢迎补充)1. reveal.js: 幻灯片展示框架一个专门用来做 HTML 幻灯片的框架,支持 HTML 和 Markdown 语法。github: https://github.com/hakimel/reveal.jsdemo: https://revealjs.com动图取自博客 reveal.js - 程序员的PPT制作神器。2. impress.js: 又一个幻灯片展示框架一个受 https://prezi.com/ 的启发,使用了现代浏览器里支持的 CSS3 transforms 和 transitions 的特效幻灯片。我的个人网站首页 https://www.senntyou.com/ 也是用 impress.js 开发的。github: https://github.com/impress/impress.jsdemo: https://impress.js.org/3. carbon: 代码美图生成器可以将你的代码生成美美的图片,然后你就可以分享到各个社区了。github: https://github.com/dawnlabs/carbondemo: https://dawnlabs.io/carbon4. favico.js: 让你的 favicon 用上胶囊图标、图片和视频github: https://github.com/ejci/favico.jsdemo: http://lab.ejci.net/favico.js/5. typed.js: 打字机效果库github: https://github.com/mattboldt/typed.jsdemo: https://mattboldt.com/demos/typed-js/6. vConsole: 移动端开发调试利器vConsole: 一个轻量、可拓展、针对手机网页的前端开发者调试面板(chrome 开发者工具的便利实现)。一般在 web 应用开发过程中,可以用 console.log 去输出一些信息,但是在移动端,console.log 的信息是看不到的。这种情况下,当然可以选择使用 alert 弹出一些信息,但是这种方法实在不怎么方便,也会阻断 js 事件循环,调试体验和效率都很差。好在有 vConsole 可以帮助我们解决这个问题。github: https://github.com/Tencent/vConsoledemo: https://wechatfe.github.io/vconsole/demo.html7. midnight.js: 固定头部炫酷效果github: https://github.com/Aerolab/midnight.jsdemo: https://aerolab.github.io/midnight.js/8. multiscroll.js: 多边滑动效果github: https://github.com/alvarotrigo/multiscroll.jsdemo: https://alvarotrigo.com/multiScroll/9. diaporama: Kenburns 效果 与 glsl 转换动画库github: https://github.com/gre/diaporamademo: http://greweb.me/diaporama/10. RainEffect: 雨滴效果github: https://github.com/codrops/RainEffectdemo: https://tympanus.net/Development/RainEffect/11. 后续更多博客,查看 https://github.com/senntyou/blogs作者:深予之 (@senntyou)版权声明:自由转载-非商用-非衍生-保持署名(创意共享3.0许可证)

September 27, 2018 · 1 min · jiezi

Git 系列文章

GIT 初识Git的基础操作Git的远程操作Git的分支管理Git标签操作Git团队协作Git 多账户管理Git的相关配置Git的子模块

September 26, 2018 · 1 min · jiezi

「造个轮子」——cicada(轻量级 WEB 框架)

前言俗话说 「不要重复造轮子」,关于是否有必要不再本次讨论范围。创建这个项目的主要目的还是提升自己,看看和知名类开源项目的差距以及学习优秀的开源方式。好了,现在着重来谈谈 cicada 这个项目的核心功能。我把他定义为一个快速、轻量级 WEB 框架;没有过多的依赖,核心 jar 包仅 30KB。也仅需要一行代码即可启动一个 HTTP 服务。特性现在来谈谈重要的几个特性。当前版本主要实现了基本的请求、响应、自定义参数以及拦截器功能。功能虽少,但五脏俱全。在今后的迭代过程中会逐渐完善上图功能,有好的想法也欢迎提 https://github.com/crossoverJie/cicada/issues。快速启动下面来看看如何快速启动一个 HTTP 服务。只需要创建一个 Maven 项目,并引入核心包。<dependency> <groupId>top.crossoverjie.opensource</groupId> <artifactId>cicada-core</artifactId> <version>1.0.0</version></dependency>如上图所示,再配置一个启动类即可。public class MainStart { public static void main(String[] args) throws InterruptedException { CicadaServer.start(MainStart.class,"/cicada-example") ; }}配置业务 Action当然我们还需要一个实现业务逻辑的地方。cicada 提供了一个接口,只需要实现该接口即可实现具体逻辑。创建业务 Action 实现 top.crossoverjie.cicada.server.action.WorkAction 接口。@CicadaAction(value = “demoAction”)public class DemoAction implements WorkAction { private static final Logger LOGGER = LoggerBuilder.getLogger(DemoAction.class) ; private static AtomicLong index = new AtomicLong() ; @Override public WorkRes<DemoResVO> execute(Param paramMap) throws Exception { String name = paramMap.getString(“name”); Integer id = paramMap.getInteger(“id”); LOGGER.info(“name=[{}],id=[{}]” , name,id); DemoResVO demoResVO = new DemoResVO() ; demoResVO.setIndex(index.incrementAndGet()); WorkRes<DemoResVO> res = new WorkRes(); res.setCode(StatusEnum.SUCCESS.getCode()); res.setMessage(StatusEnum.SUCCESS.getMessage()); res.setDataBody(demoResVO) ; return res; }}同时需要再自定义类中加上 @CicadaAction 注解,并需要指定一个 value,该 value 主要是为了在请求路由时能找到业务类。这样启动应用并访问 http://127.0.0.1:7317/cicada-example/demoAction?name=12345&id=10 便能执行业务逻辑同时得到服务端的返回。目前默认支持的是 json 响应,后期也会加上模板解析。服务中也会打印相关日志。灵活的参数配置这里所有的请求参数都封装在 Param 中,可以利用其中的各种 API 获取请求数据。之所以是灵活的:我们甚至可以这样请求:http://127.0.0.1:7317/cicada-example/demoAction?jsonData=“info”: { “age”: 22, “name”: “zhangsan” }这样就可以传递任意结构的数据,只要业务处理时进行解析即可。自定义拦截器拦截器是一个框架的基本功能,可以利用拦截器实现日志记录、事务提交等通用工作。为此 cicada 提供一个接口: top.crossoverjie.cicada.server.intercept.CicadaInterceptor。我们只需要实现该接口即可编写拦截功能:@Interceptor(value = “executeTimeInterceptor”)public class ExecuteTimeInterceptor implements CicadaInterceptor { private static final Logger LOGGER = LoggerBuilder.getLogger(ExecuteTimeInterceptor.class); private Long start; private Long end; @Override public void before(Param param) { start = System.currentTimeMillis(); } @Override public void after(Param param) { end = System.currentTimeMillis(); LOGGER.info(“cast [{}] times”, end - start); }}这里演示的是记录所有 action 的执行时间。目前默认只实现了 action 的拦截,后期也会加入自定义拦截器。拦截适配器虽说在拦截器中提供了 before/after 两个方法,但也不是所有的方法都需要实现。因此 cicada 提供了一个适配器:top.crossoverjie.cicada.server.intercept.AbstractCicadaInterceptorAdapter我们需要继承他便可按需实现其中的某个方法,如下所示:@Interceptor(value = “loggerInterceptor”)public class LoggerInterceptorAbstract extends AbstractCicadaInterceptorAdapter { private static final Logger LOGGER = LoggerBuilder.getLogger(LoggerInterceptorAbstract.class) ; @Override public void before(Param param) { LOGGER.info(“logger param=[{}]",param.toString()); }}性能测试既然是一个 HTTP 服务框架,那性能自然也得保证。在测试条件为:300 并发连续压测两轮;1G 内存、单核 CPU、1Mbps。用 Jmeter 压测情况如下:同样的服务器用 Tomcat 来压测看看结果。Tomcat 的线程池配置:<Executor name=“tomcatThreadPool” namePrefix=“consumer-exec-” maxThreads=“510” minSpareThreads=“10”/>我这里请求的是 Tomcat 的一个 doc 目录,虽说结果看似 cicada 的性能比 Tomcat 还强。但其实这个对比过程中的变量并没有完全控制好,Tomcat 所返回的是 HTML,而 cicada 仅仅返回了 json,当然问题也不止这些。但还是能说明 cicada 目前的性能还是不错的。总结本文没有过多讨论 cicada 实现原理,感兴趣的可以看看源码,都比较简单。在后续的更新中会仔细探讨这块内容。同时不出意外 cicada 会持续更新,未来也会加入更多实用的功能。甚至我会在适当的时机将它应用于我的生产项目,也希望更多朋友能参与进来一起把这个「轮子」做的更好。项目地址:https://github.com/crossoverJie/cicada你的点赞与转发是最大的支持。 ...

September 3, 2018 · 2 min · jiezi