nyflxp

nyflxp 查看完整档案

北京编辑河南理工大学  |  网络工程 编辑  |  填写所在公司/组织 codehtml.cn 编辑
编辑

前端路上的蜗牛

个人动态

nyflxp 赞了文章 · 2020-08-10

接口文档神器YApi

什么是YApi

github: https://github.com/YMFE/yapi

官网上是这么介绍的:YApi 是一个可本地部署的、打通前后端及QA的、可视化的接口管理平台 https://hellosean1025.github....

可以这么说YApi兼具swagger,rap2,postman的各项优点

why YApi

如果你想要一个好用的接口管理平台,那么你需要YApi或swagger。YApi支持各种数据导入,实现无缝迁移。

如果你想mock接口,那么你需要YApi或rap2。

如果你想做接口自动化测试,那么你需要YApi或postman。

如果你担心数据放在其他平台不安全,那么你也需要YApi。YApi支持本地化部署。

此外,YApi还有很多特性。

这么优秀,难道你还不准备用吗?

YApi有哪些功能特性

  • 可视化接口管理
  • 数据mock
  • 自动化接口测试
  • 数据导入(各种,包括swagger、har、postman、json、命令行)
  • 权限管理
  • 支持本地化部署
  • 支持插件
  • 支持二次开发

内网部署

如果只想先看看,可以跳过此节,直接使用官方demo http://yapi.demo.qunar.com/

这里需要您的环境满足

  • nodejs(7.6+)
  • mongodb(2.6+)
  • git

使用yapi-cli 工具,部署 YApi 平台是非常容易的。执行 yapi server 启动可视化部署程序,输入相应的配置和点击开始部署,就能完成整个网站的部署。部署完成之后,可按照提示信息,执行 node/{网站路径/server/app.js} 启动服务器。在浏览器打开指定url, 点击登录输入您刚才设置的管理员邮箱,默认密码为 ymfe.org 登录系统(默认密码可在个人中心修改)。

不想看字,没关系,直接在命令行输入一下命令,接着按提示一步步就可以了

npm install -g yapi-cli --registry https://registry.npm.taobao.org
yapi server

👇看截图

我们按照提示进入安装目录,后执行以下命令

node vendors/server/app.js

然后打开 http://localhost:3000/, 可以看到跟官方demo一样的界面。

至此,我们已经本地部署好服务里。🍺

可视化的接口管理

接口管理页面包括预览、编辑、运行和高级mock,这样看是不是很清爽😬

数据导入

yapi支持swagger、HAR、postman、json、命令行五种导入

这里以HAR为例: 我们先打开开发者工具,然后打开一个网站:如 美团外卖 https://h5.waimai.meituan.com...

如图:点击下载

接下来,如图

导入成功后,我们就多了几个接口了,是不是很完美 😁

数据mock

yapi的mock包括很多种

mock请求参数和返回参数

如图yapi提供给我们两种mock参数的形式

这里我们把image mock成图片

点击上图链接

这里我们成功mock了一个接口

想想是不是经常遇到后端接口没有ready的情况,现在有了yapi是不是一切都迎刃而解了呢🤔

想了解更多,请查看官方文档 https://hellosean1025.github....

高级mock

点击高级mock,这里我们加了一个字段

现在来试试看吧

是不是很爽?

全局mock脚本

自动化接口测试

这里我们用cnode为例,接口1:/topics 接口2:/topic/:id, 其中接口2中的id由接口1返回

添加一个叫cnode的项目,注意设置好域名

再导入两个cnode接口

运行,没有问题

下面👇是重点

下面开始关联两个接口,即从接口1返回的数据中获取id,并赋值给接口2的参数

点击更新

开始测试或者服务端测试

状态pass,查看测试报告

今天就介绍到这里。

更多文章请关注 https://github.com/abc-club/f...

喜欢请star

喜欢请star

喜欢请star

查看原文

赞 34 收藏 23 评论 0

nyflxp 赞了文章 · 2020-08-04

关于 Git 的 20 个面试题

翻译:疯狂的技术宅

原文:https://www.edureka.co/blog/i...


未经许可,禁止转载!
本文首发微信公众号:前端先锋
欢迎关注,每天都给你推送新鲜的前端技术文章


我在工作中很喜欢 Git 。 Git 在许多开发团队中扮演着重要的角色。

关于 Git 面试的第一个问题必须是:

Q1. Git和SVN有什么区别?

GitSVN
1. Git是一个分布式的版本控制工具1. SVN 是集中版本控制工具
2.它属于第3代版本控制工具2.它属于第2代版本控制工具
3.客户端可以在其本地系统上克隆整个存储库3.版本历史记录存储在服务器端存储库中
4.即使离线也可以提交4.只允许在线提交
5.Push/pull 操作更快5.Push/pull 操作较慢
6.工程可以用 commit 自动共享6.没有任何东西自动共享

Q2. 什么是Git?

我建议你先通过了解 git 的架构再来回答这个问题,如下图所示,试着解释一下这个图:

  • Git 是分布式版本控制系统(DVCS)。它可以跟踪文件的更改,并允许你恢复到任何特定版本的更改。
  • 与 SVN 等其他版本控制系统(VCS)相比,其分布式架构具有许多优势,一个主要优点是它不依赖于中央服务器来存储项目文件的所有版本。
  • 每个开发人员都可以“克隆”我在图中用“Local repository”标注的存储库的副本,并且在他的硬盘驱动器上具有项目的完整历史记录,因此当服务器中断时,你需要的所有恢复数据都在你队友的本地 Git 存储库中。
  • 还有一个中央云存储库,开发人员可以向其提交更改,并与其他团队成员进行共享,如图所示,所有协作者都在提交更改“远程存储库”。

clipboard.png

下一组 Git 面试问题将测试你使用 Git 的体验:

Q3. 在 Git 中提交的命令是什么?

答案非常简单。
用于写入提交的命令是 git commit -a

现在解释一下 -a 标志, 通过在命令行上加 -a 指示 git 提交已修改的所有被跟踪文件的新内容。还要提一下,如果你是第一次需要提交新文件,可以在在 git commit -a 之前先 git add <file>

Q4. 什么是 Git 中的“裸存储库”?

你应该说明 “工作目录” 和 “裸存储库” 之间的区别。

Git 中的 “裸” 存储库只包含版本控制信息而没有工作文件(没有工作树),并且它不包含特殊的 .git 子目录。相反,它直接在主目录本身包含 .git 子目录中的所有内容,其中工作目录包括:

  1. 一个 .git 子目录,其中包含你的仓库所有相关的 Git 修订历史记录。
  2. 工作树,或签出的项目文件的副本。

Q5. Git 是用什么语言编写的?

你需要说明使用它的原因,而不仅仅是说出语言的名称。我建议你这样回答:

Git使用 C 语言编写。 GIT 很快,C 语言通过减少运行时的开销来做到这一点。

Q6. 在Git中,你如何还原已经 push 并公开的提交?

There can be two answers to this question and make sure that you include both because any of the below options can be used depending on the situation: 1
这个问题可以有两个答案,你回答时也要保包含这两个答案,因为根据具体情况可以使用以下选项:

  • 删除或修复新提交中的错误文件,并将其推送到远程存储库。这是修复错误的最自然方式。对文件进行必要的修改后,将其提交到我将使用的远程存储库
git commit -m "commit message"
  • 创建一个新的提交,撤消在错误提交中所做的所有更改。可以使用命令:
git revert <name of bad commit>

Q7. git pull 和 git fetch 有什么区别?

git pull 命令从中央存储库中提取特定分支的新更改或提交,并更新本地存储库中的目标分支。

git fetch 也用于相同的目的,但它的工作方式略有不同。当你执行 git fetch 时,它会从所需的分支中提取所有新提交,并将其存储在本地存储库中的新分支中。如果要在目标分支中反映这些更改,必须在 git fetch 之后执行git merge。只有在对目标分支和获取的分支进行合并后才会更新目标分支。为了方便起见,请记住以下等式:

<center><h5>git pull = git fetch + git merge</h5></center>

Q8. git中的“staging area”或“index”是什么?

For this answer try to explain the below diagram as you can see:
可以通过下图进行解释:

在完成提交之前,可以在称为“staging area”或“index”的中间区域中对其进行格式化和审查。从图中可以看出,每个更改首先在暂存区域中进行验证,我将其称为“stage file”,然后将更改提交到存储库。

clipboard.png

Q9. 什么是 git stash?

首先应该解释 git stash 的必要性。

通常情况下,当你一直在处理项目的某一部分时,如果你想要在某个时候切换分支去处理其他事情,事情会处于混乱的状态。问题是,你不想把完成了一半的工作的提交,以便你以后就可以回到当前的工作。解决这个问题的答案是 git stash。

再解释什么是git stash。

stash 会将你的工作目录,即修改后的跟踪文件和暂存的更改保存在一堆未完成的更改中,你可以随时重新应用这些更改。

Q10. 什么是git stash drop?

通过说明我们使用 git stash drop 的目的来回答这个问题。

git stash drop 命令用于删除隐藏的项目。默认情况下,它将删除最后添加的存储项,如果提供参数的话,它还可以删除特定项。

下面举个例子。

如果要从隐藏项目列表中删除特定的存储项目,可以使用以下命令:

git stash list:它将显示隐藏项目列表,如:

stash@{0}: WIP on master: 049d078 added the index file
stash@{1}: WIP on master: c264051 Revert “added file_size”
stash@{2}: WIP on master: 21d80a5 added number to log

如果要删除名为 stash@{0} 的项目,请使用命令 git stash drop stash@{0}

Q11. 如何找到特定提交中已更改的文件列表?

对于这个问题,不能仅仅是提供命令,还要解释这个命令究竟做了些什么。

要获取特定提交中已更改的列表文件,请使用以下命令:

git diff-tree -r {hash}

给定提交哈希,这将列出在该提交中更改或添加的所有文件。 -r 标志使命令列出单个文件,而不是仅将它们折叠到根目录名称中。

你还可以包括下面提到的内容,虽然它是可选的,但有助于给面试官留下深刻印象。

输出还将包含一些额外信息,可以通过包含两个标志把它们轻松的屏蔽掉:

git diff-tree –no-commit-id –name-only -r {hash}

这里 -no-commit-id 将禁止提交哈希值出现在输出中,而 -name-only 只会打印文件名而不是它们的路径。

Q12. git config 的功能是什么?

首先说明为什么我们需要 git config

git 使用你的用户名将提交与身份相关联。 git config 命令可用来更改你的 git 配置,包括你的用户名。

下面用一个例子来解释。

假设你要提供用户名和电子邮件 ID 用来将提交与身份相关联,以便你可以知道是谁进行了特定提交。为此,我将使用:

git config –global user.name "Your Name": 此命令将添加用户名。

git config –global user.email "Your E-mail Address": 此命令将添加电子邮件ID。

Q13. 提交对象包含什么?

Commit 对象包含以下组件,你应该提到以下这三点:

  • 一组文件,表示给定时间点的项目状态
  • 引用父提交对象
  • SHAI 名称,一个40个字符的字符串,提交对象的唯一标识。

Q14. 如何在Git中创建存储库?

这可能是最常见的问题,答案很简单。

要创建存储库,先为项目创建一个目录(如果该目录不存在),然后运行命令 git init。通过运行此命令,将在项目的目录中创建 .git 目录。

Q15. 怎样将 N 次提交压缩成一次提交?

将N个提交压缩到单个提交中有两种方式:

  • 如果要从头开始编写新的提交消息,请使用以下命令:
git reset –soft HEAD~N &&
git commit
  • 如果你想在新的提交消息中串联现有的提交消息,那么需要提取这些消息并将它们传给 git commit,可以这样:
git reset –soft HEAD~N &&
git commit –edit -m"$(git log –format=%B –reverse .HEAD@{N})"

Q16. 什么是 Git bisect?如何使用它来确定(回归)错误的来源?

我建议你先给出一个Git bisect 的小定义。

Git bisect 用于查找使用二进制搜索引入错误的提交。 Git bisect的命令是

git bisect <subcommand> <options>

既然你已经提到过上面的命令,那就解释一下这个命令会做什么。

此命令用了二进制搜索算法来查找项目历史记录中的哪个提交引入了错误。你可以通过告诉它已知包含该错误的“错误”提交以及在引入错误之前已知的“良好”提交来使用它。然后 git bisect 在这两个端点之间选择一个提交,并询问你所选的提交是“好”还是“坏”。它继续缩小范围,直到找到引入更改的确切提交。

Q17. 如果想要在提交之前运行代码性检查工具,并在测试失败时阻止提交,该怎样配置 Git 存储库?

我建议你先介绍一下完整性检查。

完整性或冒烟测试用来确定继续测试是否可行和合理。

下面解释如何实现这一目标。

这可以通过与存储库的 pre-commit hook 相关的简单脚本来完成。git 会在提交之前触发 pre-commit hook。你可以在这个脚本中运行其他工具,例如 linters,并对提交到存储库中的更改执行完整性检查。

最后举个例子,你可以参考下面的脚本:

#!/bin/sh
files=$(git diff –cached –name-only –diff-filter=ACM | grep ‘.go$’)
if [ -z files ]; then
    exit 0
fi
unfmtd=$(gofmt -l $files)
if [ -z unfmtd ]; then
    exit 0
fi
echo “Some .go files are not fmt’d”
exit 1

这段脚本检查是否需要通过标准 Go 源代码格式化工具 gofmt 传递所有即将提交的 .go 文件。如果脚步以非 0 状态退出,脚本会有效地阻止提交操作。

Q18. 描述一下你所使用的分支策略?

这个问题被要求用Git来测试你的分支经验,告诉他们你在以前的工作中如何使用分支以及它的用途是什么,你可以参考以下提到的要点:

  • 功能分支(Feature branching)

    要素分支模型将特定要素的所有更改保留在分支内。当通过自动化测试对功能进行全面测试和验证时,该分支将合并到主服务器中。

  • 任务分支(Task branching)

    在此模型中,每个任务都在其自己的分支上实现,任务键包含在分支名称中。很容易看出哪个代码实现了哪个任务,只需在分支名称中查找任务键。

  • 发布分支(Release branching)

    一旦开发分支获得了足够的发布功能,你就可以克隆该分支来形成发布分支。创建该分支将会启动下一个发布周期,所以在此之后不能再添加任何新功能,只有错误修复,文档生成和其他面向发布的任务应该包含在此分支中。一旦准备好发布,该版本将合并到主服务器并标记版本号。此外,它还应该再将自发布以来已经取得的进展合并回开发分支。

最后告诉他们分支策略因团队而异,所以我知道基本的分支操作,如删除、合并、检查分支等。

Q19. 如果分支是否已合并为master,你可以通过什么手段知道?

答案很直接。

要知道某个分支是否已合并为master,你可以使用以下命令:

git branch –merged 它列出了已合并到当前分支的分支。

git branch –no-merged 它列出了尚未合并的分支。

Q20. 什么是SubGit?

SubGit 是将 SVN 到 Git迁移的工具。它创建了一个可写的本地或远程 Subversion 存储库的 Git 镜像,并且只要你愿意,可以随意使用 Subversion 和 Git。

这样做有很多优点,比如你可以从 Subversion 快速一次性导入到 Git 或者在 Atlassian Bitbucket Server 中使用SubGit。我们可以用 SubGit 创建现有 Subversion 存储库的双向 Git-SVN 镜像。你可以在方便时 push 到 Git 或提交 Subversion。同步由 SubGit 完成。


本文首发微信公众号:前端先锋

欢迎扫描二维码关注公众号,每天都给你推送新鲜的前端技术文章

欢迎扫描二维码关注公众号,每天都给你推送新鲜的前端技术文章


欢迎继续阅读本专栏其它高赞文章:


查看原文

赞 84 收藏 70 评论 4

nyflxp 赞了文章 · 2020-07-28

github上值得关注的前端项目

http://microjs.com/#

该网站的资源都托管到了githubmicrojs.com是一个可以让你选择微型的js类库的网站,该网站里的js库都是压缩后不大于5KB的,非常实用

图片描述

https://plainjs.com/(10.22更新)

The Vanilla JavaScript Repository,该仓库都是用原生js写的插件和组件,很实用。里面的项目也都托管到了github

图片描述

综合/资源


  • front-end-collect 分享自己长期关注的前端开发相关的优秀网站、博客、以及活跃开发者。star:860



  • f2e-hub 包含Animation,UI,dialog,Carousels,color,image,workflow等。star:100



  • fks 前端技能汇总,包含前端知识架构,后端知识,linux,书籍推荐等。star:4000


  • node123node.js中文资料导航。star:1200



    • Front-end-tutorial 最全的资源教程-前端涉及的所有知识体系。(12.25更新)

    样式/UI/css

    • Semantic-UI 让你使用任何HTML标签 来表现UI控件。

    这是一款语义化设计的前端框架,为攻城师而制作的可复用的开源前端框架。star:17500

    图片描述

    • primerCSS风格指南。star:3600


    • glue 一个生成CSS sprites的简单的命令行工具。star:2.5K (7.19更新)


    • postcss 用js插件来对css进行转换,类似Sass的预编译器,但实现了模块化,并且更加强大。star:4.5K(7.31更新)



    • mui 轻量级css框架。star:1.5K(10.15更新)
    • img2css 将图片转为纯css的黑科技。star:1.5K(12.25更新)

    测试/工具

    • mocha 一个简单、灵活有趣的 JavaScript 测试框架,用于 Node.js 和浏览器上的 JavaScript 应用测试。 star:6680

    687474703a2f2f662e636c2e6c792f6974656d732f336c316b306e32413155334d3149314c323130702f53637265656e25323053686f74253230323031322d30322d32342532306174253230322e32312e3433253230504d2e706e67

    • csscss css代码冗余分析仪,用于分析冗余 。star:2800


    • es6-toolses6 工具集,包括Grunt Tasks,Gulp Plugins,Broccoli Plugins,Brunch Plugins,Webpack plugins等等。star:1860


    • async 一个工具模块,提供了直接而强大的 JavaScript 异步功能。虽然是为 Node.js 设计的,但是它也可以直接在浏览器中使用。star:13000


    • simditor 团队协作工具 Tower 使用的富文本编辑器。star:1300


    • HTMLHintHTML 静态代码分析工具,可以集成到IDE环境或编译系统中。star:900


    • jshintjs静态代码分析工具,可以帮你检测js语法错误和潜在的问题。star:5100


    • csslint 分析和优化你的CSS样式表的工具。由[Nicholas C.
      Zakas][30]所写。star:2700


    • protractor 一款端对端的angular apps 测试框架。star:4K


    • casperjs 一个基于PhantomJS的开源导航脚本和测试工具。star:4.8K


    • Karma 自动化完成单元测试,允许你在多个浏览器里执行js代码。让你的TDD变得简单,快速,有趣。star:5.3K


    • jasmine 是一个简易的JS单元测试框架, 用来测试Javascript代码。star:9.1K(6.28更新)


    • chai 一个针对 Node.js 和浏览器的TDD(测试驱动开发)/BDD(行为驱动开发)的断言框架,可与任何 JavaScript 测试框架集成。star:2K(6.29更新)


    • Qunit 一个很容易使用的js单元测试框架,该框架是由jQuery团队的成员所开发,并且是jQuery的官方测试套件。star:3.3K(6.29更新)

    Workflow/构建工具

    • Grunt 基于Node.js的项目构建工具。拥有数量庞大的插件,是一款优秀的前端自动化工具。star:9500


    • yeoman 一个强健的工具,库,及工作流程的组合。star:960


    • gulp 基于node.js流的新一代前端构建系统。star:14000


    • spmCMD 的包管理工具,需要和 Sea.js 配合使用。

    canvas/数据可视化

    • echarts 基于Canvas,纯Javascript图表库,提供直观,生动,可交互,可个性化定制的数据可视化图表。star:6900


    • Chart.js 使用<canvas>标签的简易HTML5图表。star:14600


    • sketch.js 跨平台JavaScript创意编码框架,gzip压缩后仅有2kb。star:1500


    • d3 一个基于数据操作文档的js数据可视化框架,最流行的可视化库之一。star:38000


    • zrender 一个轻量级的Canvas类库,MVC封装,数据驱动,提供类DOM事件模型,让canvas绘图大不同!star:850


    • c3 一个基于 D3.js 的可重用 JavaScript 图表库。几乎零学习曲线。star:4.5K(6.28更新)


    • img2css 将图片转为纯css代码。(11.3更新)

    模块管理/加载器

    • ESL 是一个浏览器端、符合AMD的标准加载器,适合用于现代Web浏览器端应用的入口与模块管理。

    ESL vs RequireJS

    体积更小 (Smaller)
    性能更高 (Higher performance)
    更健壮 (More Robustness)
    不支持在非浏览器端使用 (Browser only)
    依赖模块用时定义 (Lazy define)
    
    • seajs 一个遵循CommonJS规范的JavaScript模块加载器。提供简单、极致的模块化开发体验。star:4100


    • Component 一个模块化的JavaScript框架,同时也是面向前端的包管理器。


    • webpack一个模块打包工具,你可以使用WebPack管理你的模块依赖,并编绎输出模块们所需的静态文件。star:9K

    动画

    • animate.css 一个跨浏览器的CSS动画库。简单易用易上手。star:23000


    • move.js 极小的 JavaScript 库,支持 CSS3 的动画效果,非常简单优雅。star:2600


    • TweenJS 是一个简单但强大的 Javascript 动画库。CreateJS 套件的一部分。star:1500


    • bounce.js 一个用于制作漂亮的 CSS3 关键帧动画的 JavaScript
      库,使用其特有的方式生成的动画效果。star:3600


    • Swipe 号称最精确的Slider触摸库,专为移动设备优化。star:4.7K


    • tween.js 一款可生成平滑动画效果的js动画库。tween.js允许你以平滑的方式修改元素的属性值。它可以通过设置生成各种类似CSS3的动画效果。star:2.5K(7.15更新)


    • parallax.js轻量级的的视差引擎,能对智能设备的方向作出反应。。star:9K (10.17更新)
    • Velocity 是一款和jQuery的$.animate()有相同API的动画引擎。很适合移动端的动画开发,还打包了颜色动画,转换,循环,easing效果,类动画、滚动等功能。star:9.5K(12月25更新)

    插件



    • onepage-scroll 可以轻松建立一个动感的响应式的滚动效果页面,比较适用于单页面的专题站。支持现代浏览器和IE8以上版本。View demostar:7700


    • slick 一款完全响应式的 jQuery 图片滚动插件,能够根据容器自动适应宽度。star:10000view demo


    • superslides 致力于解决网站大部分特效展示问题。网站上常用的“焦点图/幻灯片”“Tab标签切换”“图片滚动”“无缝滚动”等只需要一个SuperSlide即可解决! view demostar:1100



    • slider 一个jquery完全开源的JavaScript代码库,用户可以开发,调试和深度定制自己的滑块。star:850view demo


    • github-hovercard github 鼠标悬停显示用户,仓库等摘要信息。(10.15更新)


    • onepage-scroll 一款带有背景视觉差效果的jQuery整页滚动特效插件。star:8K (10.17更新)view demo


    • justlazy.js 轻量级js图片延迟加载插件。(10.22更新)
  • 框架、库和组件

    • polymerweb组件构建框架。一套以“一切皆组件、最少化代码量、最少框架限制”为设计理念的Web UI框架。 star:9900


    • impress.js 创建令人兴奋的演示。使用CSS3的转换和过渡,这个库允许你创建令人印象深刻的演示文稿。view demostar:24300


    • ionic 先进的HTML5 移动端开发框架。帮助开发者使用HTML5, CSS3和js做出不可思议的hybrid appstar:17000


    • reveal.js 基于CSS3的3D幻灯片工具。能够制作绚丽的演示文稿并生成HTML格式,将它发布到web上。star:21500view demo


    • pure.css 一组很小的,响应式的css组件,你可以在网页的项目上到处使用。star:12000


    • three.jsJavaScript编写的WebGL第三方库。提供了非常多的3D显示功能。star:20000



    • jquery-pjaxajaxpushState的封装,让你可以很方便的使用pushState技术,用以实现页面无刷新加载。star:11500


    • highlight.jsjavascript语法高亮。既可以运行在浏览器端也可以运行在服务端。star:5500



    • togetherjsMozilla打造的一款可以给网站添加实时协作功能的JavaScript库。star:5K



    • MEAN.JS 全栈式javascript,使用`MongoDB, Express,
      AngularJSNode.jsstar:2.2K`


    • wechat.js 微信相关的 js 操作:分享、网络、菜单。star:700



    • progress.js 一个 jsCSS3的库,帮助开发人员为网页上的每个对象创建和管理进度条效果。star:1.6Kview demo


    • foundation 号称世界上最先进的响应式前端框架,也是一款Mobile First的框架。star:21K


    • Sugar 一个JavaScript库。它扩展了现有的JS对象的方法,让你可以用更少的代码做更多的事情。star:2.8K


    • todomvc 帮你挑选一款MV*框架,它使用不同的最流行的js MV*框架实现了一个相同的Todo应用。star:13K


    • yepnope.js 这是一个异步的条件加载框架,速度超快,只为用户加载需要的脚本。使用非常简单,非常有用!star:2.5K


    • Material UI 是一个 CSS 框架和一组实现谷歌 Material Design 设计规范的 React
      组件。star:8.8K(6.28更新)


    • Pikaday 是一个 JavaScript 日期选择器,特点是轻量级、无依赖和模块化的
      CSSstar:2.8K [view demo]104

    图片描述

    • vuejs 用于构建交互式的 Web 界面的库。它提供了 MVVM 数据绑定和一个可组合的组件系统,具有简单、灵活的 API。star:6K(7.12更新)


    • meteor 超简单的,数据库无处不在的,用于自动化和简化实时运行的 Web 应用程序的开发。纯JavaScript的Web框架。star:27K(7.12更新)


    • webuploader
      一个简单的以HTML5为主,FLASH为辅的现代文件上传组件。支持大文件分片并发上传,极大的提高了文件上传效率。star:1.8K


    • fastclick触摸UI上的消除点击延迟js库。star:1W(10.15更新)


    • wangEditor 轻量级web富文本框。 (10.15更新)


    • benchmark.js是强大的基准测试库,几乎适用于所有JavaScript平台。支持high-resolution定时器,并返回重要的统计结果。star:1.7k(10.15更新)


    • headroom.js是一个轻量级、纯 JS 组件,用来隐藏或展现页面上的元素,为你的页面留下更多展示内容的空间。star:7.5K (10.15更新)


    移动端

    • Swipe 加速移动触摸滑块与硬件之间的转换。star:5000


    • hammer.js 一个支持多点触摸的手势库。star:11000


    • amDoc 无线Web解决方案 - 文档规范指南


    • amazeui 移动优先的跨屏前端框架。面向HTML5开发,使用css3做动画和交互。star:4.1K


    • Zepto 一款面向移动端设备、APIjQuery兼容的基础库。


    • mui 最接近原生APP体验的高性能框架 。star:1.1K(6.28更新)


    • Swiperjavascript打造的滑动特效插件,面向手机、平板电脑等移动终端。能实现触屏焦点图、触屏Tab切换、触屏多图切换等常用效果。star:6K(10月18更新)


    • SUI-Mobile由阿里巴巴国际UED前端出品的移动端UI库,轻量精美 star:2K(2016.1.14更新)


    • lib-flexible 淘宝出品的移动端可伸缩布局方案。star:1.3K+(2016.1.14更新)

    Node.js相关

    http://nodeframework.com/ 专门收集node.jsweb框架的网站。其项目同样均托管在github上。
    图片描述

    • nodeclub 使用 Node.jsMongoDB 开发的社区系统。star:3000


    • N-chat 使用 Express + Socket.IO 搭建的多人聊天室 。star:300


    • N-blog 使用 Express + MongoDB 搭建多人博客。star:1800


    • node-inspector 基于Blink开发者工具的Node.js调试器。star:7000



    • nodePPT 使用nodejs写的网络幻灯片。可能是迄今为止最好的网页版PPTstar:1.6Kview demo


    • hexo 一款快捷,简单,强大的博客框架,基于Nodejsstar:5.2K


    • koa 下一代Node.js Web 框架。由 Express 团队设计。star:6.3K



    • connectNode平台的中间件框架。Express就是基于Connect开发的。star:5.6K(6.29更新)


    • n node版本管理,tj大神所写。star:2.7K


    • nvm node版本管理,通过bash脚本来管理。star:7.5K
    • nodemon这个库是专门调试时候使用的,它会自动检测 node.js 代码的改动,然后帮你自动重启应用。star:7K+。(2.24.16)
    • supertestAPI使用流利的API测试node.js http 服务器。3.1K+

    React相关



    • react-native 一个用React构建native apps的框架。star:15000



    • awesome-react 关于react的工具,资源,视频的集合。star:700


    • Flux 是一个Facebook开发的、利用单向数据流实现的应用架构,用于
      ReactFlux应用有三个主要的部分组成:调度程序、存储和视图(React 组件)。star:6.8K(6.28更新)
    • iscroll 高性能,体积小,无外部依赖,跨平台的滚动组件 star:6K(7.19更新)
    • react-tappable Tappable component for React.(2016,1.4更新)
    • react-native-lessonreact-native入门指南.star:1.3K(2016,1.4更新)

    HTML5

    • html5-boilerplate 一套专业的前端模版,主要用于开发快速、健壮、适应性强的app或网站。star:27K (12.25更新)
    • BrowserquestMozilla开发的HTML5多人在线游戏。star:5200
    • video.js 开源的HTML5和Flash视频播放器。支持自定义进度条、按钮以及工具栏的底色。star:9.1K


    • html5shiv 主要解决HTML5提出的新的元素不被IE6-9识别。star:6K(7.15更新)


    • brunch 快速的前端 HTML5 构建工具。star:4.5K(7.19更新)


    • ulkit 一个轻量级的、模块化前端框架,它被用于快速开发强大的web界面。也是一款优秀的响应式HTML5 框架。star:5.3K (8.3更新)

    模板引擎

    • Handlebars.js 一个js语义模板库,能让你轻松高效的编写语义化模板。star:8.6K(6.29 update)


    • artTemplate 性能卓越的 js 模板引擎。star:1.7K


    • jade 一款高性能简洁易懂的模板引擎,JadeHamlJavascript实现。star:8.7K
    • ejs tj大神写的嵌入javascript的模板引擎,主要用于Node

    浏览器兼容方案

    • es6-shim 提供兼容性垫片,使ES6能兼容于传统的JavaScript引擎。star:1.5K(7.15更新)


    • Modernizr 用来检测浏览器功能支持情况的JavaScript库,可以检测18项CSS3功能以及40多项关于HTML5的功能。star:16000


    • normalize.css 一个可定制的 CSS 文件,使浏览器呈现的所有元素,更一致和符合现代标准。支持IE8+。star:17000


    • html5shiv 主要解决HTML5提出的新的元素不被IE6-9识别。star:6K(7.15更新)



    • Babel 是一款为了写下一代js的编译器,无需等待浏览器支持就可以使用新的语法。star:8.3K (7.29更新)

    高产大牛

    • Evan You 前端轻量级框架MVVM框架vue.js作者,前Google工程师。followers:1.6K


    • TJ Holowaychuk Luna 编程语言, Koa, Express, Stylus, Cluster, Mocha, Jade, node-canvas, component 等知名开源项目的创建和贡献者。 followers:14.1K


    • PaulIrish 著名的前端开发工程师,同时他也是Chrome开发者关系团队成员,jQuery团队成员,Modernizr、Yeoman、CSS3 PleaseHTML5 Boilerplatelead developerfollowers:15.7K


    • Mike Bostock 知名可视化库 D3.js的主要作者。followers:8.3K



    • 司徒正美 前端迷你MVVM框架Avalon作者。followers:2.1K

    其他


    • Mars 腾讯移动Web前端知识库。star:1600


    • brackets 一款使用 HTML,CSS,JavaScript 创建的开源的针对 Web 开发的编辑器。star:23000


    • GhostNode.js开发最新博客系统, 简单简洁, 响应式设计, 支持完全自定义, 免费, 专注博客。star:16000


    • io.jsNodeJS里分离出来的一条分支。star:13000



    • ueditor 百度前端团队出品的富本文编辑器。star:1.1K

    优秀开源组织

    PS:文章在github更新,本篇文章停止更新

    查看原文

    赞 377 收藏 1820 评论 26

    nyflxp 赞了文章 · 2020-06-08

    【react面试题】不可错过的react 面试题 「务必收藏」

    下面是一个常用的关于 React 的面试问题列表:

    无论作为一个面试者,或者招聘官,下面这些问题都可以去参考

    务必收藏

    React 的工作原理

    React 会创建一个虚拟 DOM(virtual DOM)。当一个组件中的状态改变时,React 首先会通过 "diffing" 算法来标记虚拟 DOM 中的改变,第二步是调节(reconciliation),会用 diff 的结果来更新 DOM。

    使用 React 有何优点

    • 只需查看 render 函数就会很容易知道一个组件是如何被渲染的
    • JSX 的引入,使得组件的代码更加可读,也更容易看懂组件的布局,或者组件之间是如何互相引用的
    • 支持服务端渲染,这可以改进 SEO 和性能
    • 易于测试
    • React 只关注 View 层,所以可以和其它任何框架(如Backbone.js, Angular.js)一起使用

    展示组件(Presentational component)和容器组件(Container component)之间有何不同

    展示组件关心组件看起来是什么。展示专门通过 props 接受数据和回调,并且几乎不会有自身的状态,但当展示组件拥有自身的状态时,通常也只关心 UI 状态而不是数据的状态。

    容器组件则更关心组件是如何运作的。容器组件会为展示组件或者其它容器组件提供数据和行为(behavior),它们会调用 Flux actions,并将其作为回调提供给展示组件。容器组件经常是有状态的,因为它们是(其它组件的)数据源。

    类组件(Class component)和函数式组件(Functional component)之间有何不同

    • 类组件不仅允许你使用更多额外的功能,如组件自身的状态和生命周期钩子,也能使组件直接访问 store 并维持状态
    • 当组件仅是接收 props,并将组件自身渲染到页面时,该组件就是一个 '无状态组件(stateless component)',可以使用一个纯函数来创建这样的组件。这种组件也被称为哑组件(dumb components)或展示组件

    (组件的)状态(state)和属性(props)之间有何不同

    State 是一种数据结构,用于组件挂载时所需数据的默认值。State 可能会随着时间的推移而发生突变,但多数时候是作为用户事件行为的结果。

    Props(properties 的简写)则是组件的配置。props 由父组件传递给子组件,并且就子组件而言,props 是不可变的(immutable)。组件不能改变自身的 props,但是可以把其子组件的 props 放在一起(统一管理)。Props 也不仅仅是数据--回调函数也可以通过 props 传递。

    指出(组件)生命周期方法的不同

    • componentWillMount -- 多用于根组件中的应用程序配置
    • componentDidMount -- 在这可以完成所有没有 DOM 就不能做的所有配置,并开始获取所有你需要的数据;如果需要设置事件监听,也可以在这完成
    • componentWillReceiveProps -- 这个周期函数作用于特定的 prop 改变导致的 state 转换
    • shouldComponentUpdate -- 如果你担心组件过度渲染,shouldComponentUpdate 是一个改善性能的地方,因为如果组件接收了新的 prop, 它可以阻止(组件)重新渲染。shouldComponentUpdate 应该返回一个布尔值来决定组件是否要重新渲染
    • componentWillUpdate -- 很少使用。它可以用于代替组件的 componentWillReceivePropsshouldComponentUpdate(但不能访问之前的 props)
    • componentDidUpdate -- 常用于更新 DOM,响应 prop 或 state 的改变
    • componentWillUnmount -- 在这你可以取消网络请求,或者移除所有与组件相关的事件监听器

    应该在 React 组件的何处发起 Ajax 请求

    在 React 组件中,应该在 componentDidMount 中发起网络请求。这个方法会在组件第一次“挂载”(被添加到 DOM)时执行,在组件的生命周期中仅会执行一次。更重要的是,你不能保证在组件挂载之前 Ajax 请求已经完成,如果是这样,也就意味着你将尝试在一个未挂载的组件上调用 setState,这将不起作用。在 componentDidMount 中发起网络请求将保证这有一个组件可以更新了。

    何为受控组件(controlled component)

    在 HTML 中,类似 <input>, <textarea><select> 这样的表单元素会维护自身的状态,并基于用户的输入来更新。当用户提交表单时,前面提到的元素的值将随表单一起被发送。但在 React 中会有些不同,包含表单元素的组件将会在 state 中追踪输入的值,并且每次调用回调函数时,如 onChange 会更新 state,重新渲染组件。一个输入表单元素,它的值通过 React 的这种方式来控制,这样的元素就被称为"受控元素"。

    在 React 中,refs 的作用是什么

    Refs 可以用于获取一个 DOM 节点或者 React 组件的引用。何时使用 refs 的好的示例有管理焦点/文本选择,触发命令动画,或者和第三方 DOM 库集成。你应该避免使用 String 类型的 Refs 和内联的 ref 回调。Refs 回调是 React 所推荐的。

    何为高阶组件(higher order component)

    高阶组件是一个以组件为参数并返回一个新组件的函数。HOC 运行你重用代码、逻辑和引导抽象。最常见的可能是 Redux 的 connect 函数。除了简单分享工具库和简单的组合,HOC最好的方式是共享 React 组件之间的行为。如果你发现你在不同的地方写了大量代码来做同一件事时,就应该考虑将代码重构为可重用的 HOC。

    练习


    • 写一个反转其输入的 HOC
    • 写一个从 API 提供数据给传入的组件的 HOC
    • 写一个实现 shouldComponentUpdate 来避免 reconciliation 的 HOC
    • 写一个通过 React.Children.toArray 对传入组件的子组件进行排序的 HOC

    使用箭头函数(arrow functions)的优点是什么

    • 作用域安全:在箭头函数之前,每一个新创建的函数都有定义自身的 this 值(在构造函数中是新对象;在严格模式下,函数调用中的 this 是未定义的;如果函数被称为“对象方法”,则为基础对象等),但箭头函数不会,它会使用封闭执行上下文的 this 值。
    • 简单:箭头函数易于阅读和书写
    • 清晰:当一切都是一个箭头函数,任何常规函数都可以立即用于定义作用域。开发者总是可以查找 next-higher 函数语句,以查看 this 的值

    为什么建议传递给 setState 的参数是一个 callback 而不是一个对象

    因为 this.propsthis.state 的更新可能是异步的,不能依赖它们的值去计算下一个 state。

    除了在构造函数中绑定 this,还有其它方式吗

    你可以使用属性初始值设定项(property initializers)来正确绑定回调,create-react-app 也是默认支持的。在回调中你可以使用箭头函数,但问题是每次组件渲染时都会创建一个新的回调。

    怎么阻止组件的渲染

    在组件的 render 方法中返回 null 并不会影响触发组件的生命周期方法

    当渲染一个列表时,何为 key?设置 key 的目的是什么

    Keys 会有助于 React 识别哪些 items 改变了,被添加了或者被移除了。Keys 应该被赋予数组内的元素以赋予(DOM)元素一个稳定的标识,选择一个 key 的最佳方法是使用一个字符串,该字符串能惟一地标识一个列表项。很多时候你会使用数据中的 IDs 作为 keys,当你没有稳定的 IDs 用于被渲染的 items 时,可以使用项目索引作为渲染项的 key,但这种方式并不推荐,如果 items 可以重新排序,就会导致 re-render 变慢。

    (在构造函数中)调用 super(props) 的目的是什么

    super() 被调用之前,子类是不能使用 this 的,在 ES2015 中,子类必须在 constructor 中调用 super()。传递 propssuper() 的原因则是便于(在子类中)能在 constructor 访问 this.props

    何为 JSX

    JSX 是 JavaScript 语法的一种语法扩展,并拥有 JavaScript 的全部功能。JSX 生产 React "元素",你可以将任何的 JavaScript 表达式封装在花括号里,然后将其嵌入到 JSX 中。在编译完成之后,JSX 表达式就变成了常规的 JavaScript 对象,这意味着你可以在 if 语句和 for 循环内部使用 JSX,将它赋值给变量,接受它作为参数,并从函数中返回它。

    怎么用 React.createElement 重写下面的代码

    Question:

    const element = (
      <h1 className="greeting">
        Hello, rdhub.cn!
      </h1>
    );

    Answer:

    const element = React.createElement(
      'h1',
      {className: 'greeting'},
      'Hello, rdhub.cn!'
    );

    何为 Children

    在JSX表达式中,一个开始标签(比如<a>)和一个关闭标签(比如</a>)之间的内容会作为一个特殊的属性props.children被自动传递给包含着它的组件。

    这个属性有许多可用的方法,包括 React.Children.mapReact.Children.forEachReact.Children.countReact.Children.onlyReact.Children.toArray

    在 React 中,何为 state

    State 和 props 类似,但它是私有的,并且完全由组件自身控制。State 本质上是一个持有数据,并决定组件如何渲染的对象。

    什么原因会促使你脱离 create-react-app 的依赖

    当你想去配置 webpack 或 babel presets。

    何为 redux

    Redux 的基本思想是整个应用的 state 保持在一个单一的 store 中。store 就是一个简单的 javascript 对象,而改变应用 state 的唯一方式是在应用中触发 actions,然后为这些 actions 编写 reducers 来修改 state。整个 state 转化是在 reducers 中完成,并且不应该有任何副作用。

    在 Redux 中,何为 store

    Store 是一个 javascript 对象,它保存了整个应用的 state。与此同时,Store 也承担以下职责:

    • 允许通过 getState() 访问 state
    • 运行通过 dispatch(action) 改变 state
    • 通过 subscribe(listener) 注册 listeners
    • 通过 subscribe(listener) 返回的函数处理 listeners 的注销

    何为 action

    Actions 是一个纯 javascript 对象,它们必须有一个 type 属性表明正在执行的 action 的类型。实质上,action 是将数据从应用程序发送到 store 的有效载荷。

    何为 reducer

    一个 reducer 是一个纯函数,该函数以先前的 state 和一个 action 作为参数,并返回下一个 state。

    Redux Thunk 的作用是什么

    Redux thunk 是一个允许你编写返回一个函数而不是一个 action 的 actions creators 的中间件。如果满足某个条件,thunk 则可以用来延迟 action 的派发(dispatch),这可以处理异步 action 的派发(dispatch)。

    何为纯函数(pure function)

    一个纯函数是一个不依赖于且不改变其作用域之外的变量状态的函数,这也意味着一个纯函数对于同样的参数总是返回同样的结果。

    关于

    image

    查看原文

    赞 200 收藏 167 评论 1

    nyflxp 关注了用户 · 2020-03-27

    此号不收钱 @cihaobushouqian_5ca8bcfa0ecd2

    关注 8

    nyflxp 赞了文章 · 2020-03-27

    阿里前端攻城狮们写了一份前端面试题答案

    话说一周前,我发了这样一条沸点:

    于是我真的就建群收集了题目,和团队的同事一起写答案,我们也不图什么,就是想做一件有意义的事情,现在我整理了下我们的回答,有的不一定就是非常具体的回答,但也提供了思路和参考资料,大家看看是否还有什么补充的,或者面试时遇到的问题,也欢迎补充

    [高德一面]一个 tcp 连接能发几个 http 请求?(LVYOU)

    如果是 HTTP 1.0 版本协议,一般情况下,不支持长连接,因此在每次请求发送完毕之后,TCP 连接即会断开,因此一个 TCP 发送一个 HTTP 请求,但是有一种情况可以将一条 TCP 连接保持在活跃状态,那就是通过 Connection 和 Keep-Alive 首部,在请求头带上 Connection: Keep-Alive,并且可以通过 Keep-Alive 通用首部中指定的,用逗号分隔的选项调节 keep-alive 的行为,如果客户端和服务端都支持,那么其实也可以发送多条,不过此方式也有限制,可以关注《HTTP 权威指南》4.5.5 节对于 Keep-Alive 连接的限制和规则。

    而如果是 HTTP 1.1 版本协议,支持了长连接,因此只要 TCP 连接不断开,便可以一直发送 HTTP 请求,持续不断,没有上限; 同样,如果是 HTTP 2.0 版本协议,支持多用复用,一个 TCP 连接是可以并发多个 HTTP 请求的,同样也是支持长连接,因此只要不断开 TCP 的连接,HTTP 请求数也是可以没有上限地持续发送

    [腾讯一面]Virtual Dom 的优势在哪里?(B_Cornelius)

    「Virtual Dom 的优势」其实这道题目面试官更想听到的答案不是上来就说「直接操作/频繁操作 DOM 的性能差」,如果 DOM 操作的性能如此不堪,那么 jQuery 也不至于活到今天。所以面试官更想听到 VDOM 想解决的问题以及为什么频繁的 DOM 操作会性能差。

    首先我们需要知道:

    DOM 引擎、JS 引擎 相互独立,但又工作在同一线程(主线程) JS 代码调用 DOM API 必须 挂起 JS 引擎、转换传入参数数据、激活 DOM 引擎,DOM 重绘后再转换可能有的返回值,最后激活 JS 引擎并继续执行若有频繁的 DOM API 调用,且浏览器厂商不做“批量处理”优化, 引擎间切换的单位代价将迅速积累若其中有强制重绘的 DOM API 调用,重新计算布局、重新绘制图像会引起更大的性能消耗。

    其次是 VDOM 和真实 DOM 的区别和优化:

    1. 虚拟 DOM 不会立马进行排版与重绘操作
    2. 虚拟 DOM 进行频繁修改,然后一次性比较并修改真实 DOM 中需要改的部分,最后在真实 DOM 中进行排版与重绘,减少过多DOM节点排版与重绘损耗
    3. 虚拟 DOM 有效降低大面积真实 DOM 的重绘与排版,因为最终与真实 DOM 比较差异,可以只渲染局部

    [字节跳动] common.js 和 es6 中模块引入的区别? (霍小叶)

    CommonJS 是一种模块规范,最初被应用于 Nodejs,成为 Nodejs 的模块规范。运行在浏览器端的 JavaScript 由于也缺少类似的规范,在 ES6 出来之前,前端也实现了一套相同的模块规范 (例如: AMD),用来对前端模块进行管理。自 ES6 起,引入了一套新的 ES6 Module 规范,在语言标准的层面上实现了模块功能,而且实现得相当简单,有望成为浏览器和服务器通用的模块解决方案。但目前浏览器对 ES6 Module 兼容还不太好,我们平时在 Webpack 中使用的 export 和 import,会经过 Babel 转换为 CommonJS 规范。在使用上的差别主要有:

    1. CommonJS 模块输出的是一个值的拷贝,ES6 模块输出的是值的引用。
    2. CommonJS 模块是运行时加载,ES6 模块是编译时输出接口。
    3. CommonJs 是单个值导出,ES6 Module可以导出多个
    4. CommonJs 是动态语法可以写在判断里,ES6 Module 静态语法只能写在顶层
    5. CommonJs 的 this 是当前模块,ES6 Module的 this 是 undefined

    [未知] cookie token 和 session 的区别(Roxannej)

    这道题绝对不是你回答的点越多就越好。这道题考察的是你对浏览器缓存知识的理解程度,所以你应该回答的是 Cookie、 Session、Token 的产生背景、原理、有什么问题,在回答这个的基础上把差别讲出来。把这些东西答出本质,再加点装逼的东西,再故意拓展讲到你准备的其他内容才是答好这道题的关键,而要理解好这些东西,其实一两天就够了。关于 Cookie,最近还发生了 Chrome80 屏蔽第三方 Cookie 的事件,如果真的问到这个问题,讲到这件事情妥妥的加分项,前提是你对这件事情也有比较深入的了解。关于 Cookie 和这件事情 我写了篇文章 github.com/mqyqingfeng… 可以看一下。

    [头条]如何选择图片格式,例如 png, webp(B_Cornelius)

    图片格式

    压缩方式

    透明度

    动画

    浏览器兼容

    适应场景

    JPEG

    有损压缩

    不支持

    不支持

    所有

    复杂颜色及形状、尤其是照片

    GIF

    无损压缩

    支持

    支持

    所有

    简单颜色,动画

    PNG

    无损压缩

    支持

    不支持

    所有

    需要透明时

    APNG

    无损压缩

    支持

    支持

    FirefoxSafariiOS Safari

    需要半透明效果的动画

    WebP

    有损压缩

    支持

    支持

    ChromeOperaAndroid ChromeAndroid Browser

    复杂颜色及形状浏览器平台可预知

    SVG

    无损压缩

    支持

    支持

    所有(IE8以上)

    简单图形,需要良好的放缩体验需要动态控制图片特效

    [未知]首屏和白屏时间如何计算?(jhinnn)

    首屏时间的计算,可以由 Native WebView 提供的类似 onload 的方法实现,在 ios 下对应的是 webViewDidFinishLoad,在 android 下对应的是onPageFinished事件。

    白屏的定义有多种。可以认为“没有任何内容”是白屏,可以认为“网络或服务异常”是白屏,可以认为“数据加载中”是白屏,可以认为“图片加载不出来”是白屏。场景不同,白屏的计算方式就不相同。

    方法1:当页面的元素数小于x时,则认为页面白屏。比如“没有任何内容”,可以获取页面的DOM节点数,判断DOM节点数少于某个阈值X,则认为白屏。 方法2:当页面出现业务定义的错误码时,则认为是白屏。比如“网络或服务异常”。 方法3:当页面出现业务定义的特征值时,则认为是白屏。比如“数据加载中”。

    [未知]小程序和 H5 有什么区别?(张慈仁)

    1. 渲染方式与 H5 不同,小程序一般是通过 Native 原生渲染的,但是小程序同时也支持 web 渲染,如果使用 web 渲染的方式,我们需要初始化一个WebView 组件,然后在 WebView 中加载 H5 页面;

    所以当我们开发一个小程序时,通常会使用 hybrid 的方式,即会根据具体情况选择部分功能用小程序原生的代码来开发,部分功能通过 WebView 加载 H5 页面来实现。Native 与 Web 渲染混合使用,以实现项目的最优解;

    这里值得注意的是,小程序下,native 方式通常情况下性能要优于 web 方式。

    1. 小程序特有的双线程设计。 H5 下我们所有资源通常都会打到一个 bundle.js 文件里(不考虑分包加载),而小程序编译后的结果会有两个bundle,index.js封装的是小程序项目的 view 层,以及 index.worker.js 封装的是项目的业务逻辑,在运行时,会有两条线程来分别处理这两个bundle,一个是主渲染线程,它负责加载并渲染 index.js 里的内容,另外一个是 Service Worker线 程,它负责执行 index.worker.js 里封装的业务逻辑,这里面会有很多对底层api调用。

    [未知]如何判断 0.1 + 0.2 与 0.3 相等?(红白)

    作为一道面试题,我觉得重要的是要讲出一点其他人一般不会答出来的深度。像这道题,可以从原理和解决方案两个地方作为答题点,最好在编一个案例。大致讲自己遇到过这个问题,于是很好奇深入研究了一下,发现是浮点数精度导致……原理怎样怎样……然后又看了业界的库的源码,然后怎样怎样解决。

    关于原理,我专门写了一篇文章 github.com/mqyqingfeng… 来解释,实际回答的时候,我觉得答出来

    1. 非是 ECMAScript 独有
    2. IEEE754 标准中 64 位的储存格式,比如 11 位存偏移值
    3. 其中涉及的三次精度丢失

    就已经 OK 了。

    再讲解决方案,这个可以直接搜索到,各种方案都了解一下,比较一下优劣,还可以参考业界的一些库的实现,比如 math.js,不过相关的我并没有看过……

    如果还有精力的话,可以从加法再拓展讲讲超出安全值的数字的计算问题。

    所以我觉得一能回答出底层实现,二能回答出多种解决方案的优劣,三能拓展讲讲 bignum 的处理,就是一个非常不错的回答了。

    [腾讯二面]了解v8引擎吗,一段js代码如何执行的(B_Cornelius)

    在执行一段代码时,JS 引擎会首先创建一个执行栈

    然后JS引擎会创建一个全局执行上下文,并push到执行栈中, 这个过程JS引擎会为这段代码中所有变量分配内存并赋一个初始值(undefined),在创建完成后,JS引擎会进入执行阶段,这个过程JS引擎会逐行的执行代码,即为之前分配好内存的变量逐个赋值(真实值)。

    如果这段代码中存在function的声明和调用,那么JS引擎会创建一个函数执行上下文,并push到执行栈中,其创建和执行过程跟全局执行上下文一样。但有特殊情况,即当函数中存在对其它函数的调用时,JS引擎会在父函数执行的过程中,将子函数的全局执行上下文push到执行栈,这也是为什么子函数能够访问到父函数内所声明的变量。

    还有一种特殊情况是,在子函数执行的过程中,父函数已经return了,这种情况下,JS引擎会将父函数的上下文从执行栈中移除,与此同时,JS引擎会为还在执行的子函数上下文创建一个闭包,这个闭包里保存了父函数内声明的变量及其赋值,子函数仍然能够在其上下文中访问并使用这边变量/常量。当子函数执行完毕,JS引擎才会将子函数的上下文及闭包一并从执行栈中移除。

    最后,JS引擎是单线程的,那么它是如何处理高并发的呢?即当代码中存在异步调用时JS是如何执行的。比如setTimeout或fetch请求都是non-blocking的,当异步调用代码触发时,JS引擎会将需要异步执行的代码移出调用栈,直到等待到返回结果,JS引擎会立即将与之对应的回调函数push进任务队列中等待被调用,当调用(执行)栈中已经没有需要被执行的代码时,JS引擎会立刻将任务队列中的回调函数逐个push进调用栈并执行。这个过程我们也称之为事件循环。

    附言:需要更深入的了解JS引擎,必须理解几个概念,执行上下文,闭包,作用域,作用域链,以及事件循环。建议去网上多看看相关文章,这里推荐一篇非常精彩的博客,对于JS引擎的执行做了图形化的说明,更加便于理解。

    查看原文

    赞 36 收藏 30 评论 2

    nyflxp 关注了用户 · 2020-01-02

    Pines_Cheng @pines_cheng

    不挑食的程序员,关注前端四化建设。

    关注 288

    nyflxp 赞了文章 · 2019-05-17

    微信小程序资源汇总

    awesome-github-wechat-weapp 
    是由OpenDigg整理并维护的微信小程序开源项目库集合。我们会定期同步上的项目到这里,也欢迎各位


    UI组件
    开发框架
    实用库
    开发工具
    服务端
    项目实例
    Demo


    UI组件

    weui-wxss ★1873 - 同微信原生视觉体验一致的基础样式库
    zanui-weapp ★794 - 好用易扩展的小程序 UI 库
    wx-charts ★449 - 微信小程序图表工具
    Wa-UI ★164 - 针对微信小程序整合的一套UI库
    wux ★163 - 微信小程序自定义组件
    wemark ★161 - 微信小程序Markdown渲染库
    wxapp ★131 - 微信小程序组件
    wx-scrollable-tab-view ★116 - 小程序可滑动得tabview
    wxapp-img-loader ★101 - 微信小程序的图片预加载组件
    WeZRender ★96 - 微信小程序Canvas增强组件
    wetoast ★77 - 仿照微信小程序提供的showToast功能
    wx-alphabetical-listview ★71 - 带字母可滑动的列表小程序
    wx-drawer ★70 - 小程序模仿QQ6侧滑菜单
    wxSearch ★70 - 微信小程序优雅的搜索框
    wx_calendar ★65 - 小程序日历
    wxapp-charts ★52 - 微信小程序图表charts组件
    chartjs-wechat-mini-app ★42 - chartjs微信小程序适配
    citySelect ★42 - 微信小程序城市选择器
    WeiXinProject ★36 - 列表的上拉刷新和上拉加载
    wepy-com-charts ★20 - 微信小程序wepyjs图表控件
    WechatLoading ★14 - 加载框布局LoadingView
    wxTabs ★13 - 微信小程序的多tab实现
    wxapp-lock ★12 - 微信小程序手势解锁


    开发框架

    wepy ★1325 - 小程序组件化开发框架
    Labrador ★785 - 微信小程序模块化开发框架
    wxapp-devFrame ★49 - 小程序基本的开发框架

    实用库

    wxParse ★1107 - 微信小程序富文本解析自定义组件
    wechat-weapp-redux ★189 - 微信小程序Redux绑定
    wxapp-socket-io ★123 - 微信小程序的SocketIO客户端
    wafer-client-sdk ★94 - 微信小程序客户端腾讯云增强 SDK
    WxNotificationCenter ★86 - 微信小程序通知广播模式类
    wilddog-weapp ★67 - 野狗微信小程序客户SDK
    wx-query ★62 - 微信小程序仿jQuery插件
    wxapp-google-analytics ★59 - 让微信小程序支持谷歌统计
    wxapp-jsapi ★49 - 百度地图微信小程序
    wxstream ★19 - 微信小程序的响应式编程类库封装
    upyun-wxapp-sdk ★16 - 又拍云微信小程序


    开发工具

    wept ★1097 - 实时微信小程序开发工具
    weapp-quick ★320 - “微信Web开发者”拷贝工具
    Wxapp.vim ★213 - 微信小程序开发 Vim 插件
    wechat_web_devtools ★179 - 微信开发者工具Linux版
    miniapps ★131 - 小程序项目脚手架工具
    Matchmaker ★107 - 专为微信小程序开发的插件
    wecos ★64 - 微信小程序 COS 瘦身解决方案


    服务端

    m-mall-admin ★137 - 创建REST API的样板应用
    NAMI ★79 - 专为小程序而生的服务端开发容器
    weapp-node-server-demo ★72 - Wafer 服务端 Demo
    Wafer 服务端 SDK - Java ★67 - 企业级微信小程序全栈方案
    xpmjs ★67 - 小程序云端增强 SDK
    weapp-php-server-sdk ★42 - 腾讯云微信小程序云端解决方案


    项目实例

    wecqupt ★255 - 在微信内被便捷地获取和传播
    豆瓣电影项目 ★118 - 微信小程序豆瓣电影项目
    miemie ★28 - 微信小程序咩咩单词
    hellspawn-hunter-weapp ★25 - 阴阳师妖怪搜索小程序


    Demo

    weapp-demo ★1086 - 仿豆瓣电影微信小程序
    wechat-weapp-mall ★750 - 微信小程序移动端商城
    wechat-weapp-gank ★604 - Gank微信小程序
    SmallAppForQQ ★561 - 微信小程序高仿QQ应用
    weapp-wechat-zhihu ★518 - 微信中的知乎
    m-mall ★328 - 实现一个移动端小商城
    仿芒果TV ★326 - 微信小程序demo
    weChatApp-Run ★265 - 跑步微信小程序Demo
    wechat-v2ex ★235 - 简单的v2ex微信小程序
    腾讯云微信小程序 ★234 - 一站式解决方案客户端示例
    weapp-weipiao ★234 - 微信小程序-微票
    wechat-weapp-taobao ★227 - 微信小程序demo 仿手机淘宝
    weapp-boilerplate ★220 - 一个为微信小程序开发准备的基础骨架
    wechat_mall_applet ★201 - 巴爷微信商城的简单版本
    wechat-weapp-movie ★182 - 微信小程序 - 电影推荐
    wechat-app-zhihudaily ★173 - 微信小程序-知乎日报
    wechat-app-music ★153 - 微信小程序: 音乐播放器
    fenda-mock ★153 - 使用微信小程序实现分答这款APP的基础功能
    wechat-weapp-mapdemo ★152 - 微信小程序开发demo-地图定位
    Artand ★123 - 微信小程序
    weapp-douban-film ★112 - 微信小程序 - 豆瓣电影
    wepy-wechat-demo ★105 - wepy仿微信聊天界面
    weapp-one ★104 - 仿 「ONE · 一个」 的微信小程序
    wechat-weapp-redux-todos ★102 - 微信小程序集成Redux实现的Todo list
    weapp-zhihulive ★98 - 基于Zhihu Live数据的微信小程序
    BearDiary ★97 - 微信小程序之小熊の日记
    netmusic-app ★95 - 仿网易云音乐APP的微信小程序
    wxflex ★75 - 微信小程序的Flex布局demo
    番茄时钟 ★75 - 番茄时钟微信小程序版
    weapp-node-server-demo ★72 - Wafer 服务端 Demo
    wechat-chat ★71 - 微信小程序版聊天室
    wxapp-sCalc ★66 - 微信小程序版简易计算器,适合入门练手
    weapp-demo-session ★66 - 微信小程序示例一笔到底
    weapp-demo-breadtrip ★62 - 基于面包旅行 API 制作的微信小程序示例
    wechatapp-news-reader ★59 - 新闻阅读器
    wechat-weapp-demo ★58 - 一个简单的微信小程序购物车DEMO
    weapp-newsapp ★57 - 微信小程序-公众号热门文章信息流
    weapp-girls ★56 - 通过Node.js实现的妹子照片爬虫微信小程序
    wechat-app-flexlayout ★55 - 从FlexLayout布局开始学习微信小程序
    wxapp-hiapp ★52 - HiApp 微信小程序版
    weapp-github ★50 - 微信小程序的简单尝试
    bookbox-wxapp ★37 - 集美大学图书馆的便捷工具
    WeChatMeiZhi ★36 - 微信小程序版妹纸图
    weapp-V2ex ★36 - V2ex 微信小程序版
    WXBaiSi ★34 - 微信小程序仿百思不得姐
    wx-audio ★33 - 微信小程序音乐播放器应用
    wxapp-2048 ★32 - 微信小程序2048
    weapp-500px ★32 - 微信小程序
    yiyaowang-wx ★31 - 医药网原生APP的微信小程序DEMO
    wxreading ★28 - 微信小程序跟读
    WxMasonry ★27 - 微信小程序瀑布流布局模式
    weapp ★26 - 小程序 hello world 尝鲜
    WechatApp-BaisiSister ★26 - 微信小程序版百思不得姐
    wechat-app-xiaoyima ★26 - 仿大姨妈的微信小程序
    WeChat_ayibang ★25 - 微信小程序仿阿姨帮
    hotapp-notepad ★25 - 微信小程序HotApp云笔记
    wechatApp-netease_cloudmusic ★22 - 小程序模仿——网易云音乐
    wxapp-mall ★22 - 微信小程序商城demo
    wx-mime ★20 - 微信小程序版的扫雷
    PigRaising ★20 - 专注管理时间的微信小程序
    GankCamp-WechatAPP ★19 - 微信小程序版干货集中营
    weapp-lolgame ★18 - 英雄联盟(LOL)战绩查询
    wxSortPickerView ★17 - 微信小程序首字母排序选择表
    weapp-douban-movie ★17 - 微信小程序版豆瓣电影
    WexinApp_1024 ★16 - 简单的实现了1024的游戏规则
    wechat-app-githubfeed ★14 - 微信小程序试玩
    doule ★14 - 微信小程序逗乐
    wxApp ★14 - 一步步开发微信小程序
    wx-mina-meteor ★14 - 一个 meteor 的 React todo list 例子
    caipu_weixin ★12 - 微信小程序健康菜谱
    jspapa-wx ★12 - jspapa微信小程序版本
    CNodeJs-WXAPP ★12 - 微信小程序版的CNodeJs中文社区
    weapp-LeanCloud ★12 - LeanCloud 的微信小程序用户登陆Demo
    wejoke ★11 - 微笑话微信小程序
    liwushuoapp ★11 - 微信小程序开发的App
    weapp-sportsnews ★8 - 体育新闻微信小程序
    labrador_mobx_example ★4 - 基于Labrador和mobx构建的小程序开发demo

    查看原文

    赞 358 收藏 511 评论 4

    nyflxp 赞了文章 · 2019-05-15

    微信小程序资源汇总

    awesome-github-wechat-weapp 
    是由OpenDigg整理并维护的微信小程序开源项目库集合。我们会定期同步上的项目到这里,也欢迎各位


    UI组件
    开发框架
    实用库
    开发工具
    服务端
    项目实例
    Demo


    UI组件

    weui-wxss ★1873 - 同微信原生视觉体验一致的基础样式库
    zanui-weapp ★794 - 好用易扩展的小程序 UI 库
    wx-charts ★449 - 微信小程序图表工具
    Wa-UI ★164 - 针对微信小程序整合的一套UI库
    wux ★163 - 微信小程序自定义组件
    wemark ★161 - 微信小程序Markdown渲染库
    wxapp ★131 - 微信小程序组件
    wx-scrollable-tab-view ★116 - 小程序可滑动得tabview
    wxapp-img-loader ★101 - 微信小程序的图片预加载组件
    WeZRender ★96 - 微信小程序Canvas增强组件
    wetoast ★77 - 仿照微信小程序提供的showToast功能
    wx-alphabetical-listview ★71 - 带字母可滑动的列表小程序
    wx-drawer ★70 - 小程序模仿QQ6侧滑菜单
    wxSearch ★70 - 微信小程序优雅的搜索框
    wx_calendar ★65 - 小程序日历
    wxapp-charts ★52 - 微信小程序图表charts组件
    chartjs-wechat-mini-app ★42 - chartjs微信小程序适配
    citySelect ★42 - 微信小程序城市选择器
    WeiXinProject ★36 - 列表的上拉刷新和上拉加载
    wepy-com-charts ★20 - 微信小程序wepyjs图表控件
    WechatLoading ★14 - 加载框布局LoadingView
    wxTabs ★13 - 微信小程序的多tab实现
    wxapp-lock ★12 - 微信小程序手势解锁


    开发框架

    wepy ★1325 - 小程序组件化开发框架
    Labrador ★785 - 微信小程序模块化开发框架
    wxapp-devFrame ★49 - 小程序基本的开发框架

    实用库

    wxParse ★1107 - 微信小程序富文本解析自定义组件
    wechat-weapp-redux ★189 - 微信小程序Redux绑定
    wxapp-socket-io ★123 - 微信小程序的SocketIO客户端
    wafer-client-sdk ★94 - 微信小程序客户端腾讯云增强 SDK
    WxNotificationCenter ★86 - 微信小程序通知广播模式类
    wilddog-weapp ★67 - 野狗微信小程序客户SDK
    wx-query ★62 - 微信小程序仿jQuery插件
    wxapp-google-analytics ★59 - 让微信小程序支持谷歌统计
    wxapp-jsapi ★49 - 百度地图微信小程序
    wxstream ★19 - 微信小程序的响应式编程类库封装
    upyun-wxapp-sdk ★16 - 又拍云微信小程序


    开发工具

    wept ★1097 - 实时微信小程序开发工具
    weapp-quick ★320 - “微信Web开发者”拷贝工具
    Wxapp.vim ★213 - 微信小程序开发 Vim 插件
    wechat_web_devtools ★179 - 微信开发者工具Linux版
    miniapps ★131 - 小程序项目脚手架工具
    Matchmaker ★107 - 专为微信小程序开发的插件
    wecos ★64 - 微信小程序 COS 瘦身解决方案


    服务端

    m-mall-admin ★137 - 创建REST API的样板应用
    NAMI ★79 - 专为小程序而生的服务端开发容器
    weapp-node-server-demo ★72 - Wafer 服务端 Demo
    Wafer 服务端 SDK - Java ★67 - 企业级微信小程序全栈方案
    xpmjs ★67 - 小程序云端增强 SDK
    weapp-php-server-sdk ★42 - 腾讯云微信小程序云端解决方案


    项目实例

    wecqupt ★255 - 在微信内被便捷地获取和传播
    豆瓣电影项目 ★118 - 微信小程序豆瓣电影项目
    miemie ★28 - 微信小程序咩咩单词
    hellspawn-hunter-weapp ★25 - 阴阳师妖怪搜索小程序


    Demo

    weapp-demo ★1086 - 仿豆瓣电影微信小程序
    wechat-weapp-mall ★750 - 微信小程序移动端商城
    wechat-weapp-gank ★604 - Gank微信小程序
    SmallAppForQQ ★561 - 微信小程序高仿QQ应用
    weapp-wechat-zhihu ★518 - 微信中的知乎
    m-mall ★328 - 实现一个移动端小商城
    仿芒果TV ★326 - 微信小程序demo
    weChatApp-Run ★265 - 跑步微信小程序Demo
    wechat-v2ex ★235 - 简单的v2ex微信小程序
    腾讯云微信小程序 ★234 - 一站式解决方案客户端示例
    weapp-weipiao ★234 - 微信小程序-微票
    wechat-weapp-taobao ★227 - 微信小程序demo 仿手机淘宝
    weapp-boilerplate ★220 - 一个为微信小程序开发准备的基础骨架
    wechat_mall_applet ★201 - 巴爷微信商城的简单版本
    wechat-weapp-movie ★182 - 微信小程序 - 电影推荐
    wechat-app-zhihudaily ★173 - 微信小程序-知乎日报
    wechat-app-music ★153 - 微信小程序: 音乐播放器
    fenda-mock ★153 - 使用微信小程序实现分答这款APP的基础功能
    wechat-weapp-mapdemo ★152 - 微信小程序开发demo-地图定位
    Artand ★123 - 微信小程序
    weapp-douban-film ★112 - 微信小程序 - 豆瓣电影
    wepy-wechat-demo ★105 - wepy仿微信聊天界面
    weapp-one ★104 - 仿 「ONE · 一个」 的微信小程序
    wechat-weapp-redux-todos ★102 - 微信小程序集成Redux实现的Todo list
    weapp-zhihulive ★98 - 基于Zhihu Live数据的微信小程序
    BearDiary ★97 - 微信小程序之小熊の日记
    netmusic-app ★95 - 仿网易云音乐APP的微信小程序
    wxflex ★75 - 微信小程序的Flex布局demo
    番茄时钟 ★75 - 番茄时钟微信小程序版
    weapp-node-server-demo ★72 - Wafer 服务端 Demo
    wechat-chat ★71 - 微信小程序版聊天室
    wxapp-sCalc ★66 - 微信小程序版简易计算器,适合入门练手
    weapp-demo-session ★66 - 微信小程序示例一笔到底
    weapp-demo-breadtrip ★62 - 基于面包旅行 API 制作的微信小程序示例
    wechatapp-news-reader ★59 - 新闻阅读器
    wechat-weapp-demo ★58 - 一个简单的微信小程序购物车DEMO
    weapp-newsapp ★57 - 微信小程序-公众号热门文章信息流
    weapp-girls ★56 - 通过Node.js实现的妹子照片爬虫微信小程序
    wechat-app-flexlayout ★55 - 从FlexLayout布局开始学习微信小程序
    wxapp-hiapp ★52 - HiApp 微信小程序版
    weapp-github ★50 - 微信小程序的简单尝试
    bookbox-wxapp ★37 - 集美大学图书馆的便捷工具
    WeChatMeiZhi ★36 - 微信小程序版妹纸图
    weapp-V2ex ★36 - V2ex 微信小程序版
    WXBaiSi ★34 - 微信小程序仿百思不得姐
    wx-audio ★33 - 微信小程序音乐播放器应用
    wxapp-2048 ★32 - 微信小程序2048
    weapp-500px ★32 - 微信小程序
    yiyaowang-wx ★31 - 医药网原生APP的微信小程序DEMO
    wxreading ★28 - 微信小程序跟读
    WxMasonry ★27 - 微信小程序瀑布流布局模式
    weapp ★26 - 小程序 hello world 尝鲜
    WechatApp-BaisiSister ★26 - 微信小程序版百思不得姐
    wechat-app-xiaoyima ★26 - 仿大姨妈的微信小程序
    WeChat_ayibang ★25 - 微信小程序仿阿姨帮
    hotapp-notepad ★25 - 微信小程序HotApp云笔记
    wechatApp-netease_cloudmusic ★22 - 小程序模仿——网易云音乐
    wxapp-mall ★22 - 微信小程序商城demo
    wx-mime ★20 - 微信小程序版的扫雷
    PigRaising ★20 - 专注管理时间的微信小程序
    GankCamp-WechatAPP ★19 - 微信小程序版干货集中营
    weapp-lolgame ★18 - 英雄联盟(LOL)战绩查询
    wxSortPickerView ★17 - 微信小程序首字母排序选择表
    weapp-douban-movie ★17 - 微信小程序版豆瓣电影
    WexinApp_1024 ★16 - 简单的实现了1024的游戏规则
    wechat-app-githubfeed ★14 - 微信小程序试玩
    doule ★14 - 微信小程序逗乐
    wxApp ★14 - 一步步开发微信小程序
    wx-mina-meteor ★14 - 一个 meteor 的 React todo list 例子
    caipu_weixin ★12 - 微信小程序健康菜谱
    jspapa-wx ★12 - jspapa微信小程序版本
    CNodeJs-WXAPP ★12 - 微信小程序版的CNodeJs中文社区
    weapp-LeanCloud ★12 - LeanCloud 的微信小程序用户登陆Demo
    wejoke ★11 - 微笑话微信小程序
    liwushuoapp ★11 - 微信小程序开发的App
    weapp-sportsnews ★8 - 体育新闻微信小程序
    labrador_mobx_example ★4 - 基于Labrador和mobx构建的小程序开发demo

    查看原文

    赞 358 收藏 511 评论 4

    nyflxp 关注了标签 · 2019-05-05

    flutter

    clipboard.png

    Flutter 是 Google 用以帮助开发者在 iOS 和 Android 两个平台开发高质量原生 UI 的移动 SDK。

    Flutter is Google’s mobile app SDK for crafting high-quality native interfaces on iOS and Android in record time. Flutter works with existing code, is used by developers and organizations around the world, and is free and open source.

    Flutter 官网:https://flutter.dev/
    Flutter 中文资源:https://flutter-io.cn/
    Flutter Github:https://github.com/flutter/fl...

    关注 991

    nyflxp 收藏了文章 · 2019-03-24

    React-新的生命周期(React16版本)

    组件生命周期的三个阶段

    1. Mounting(加载阶段)
    2. Updating(更新阶段)
    3. Unmounting(卸载阶段)

    旧的生命周期

    图片描述

    Mounting(加载阶段:涉及6个钩子函数)

    constructor()

    加载的时候调用一次,可以初始化state

    getDefaultProps()

    设置默认的props,也可以用dufaultProps设置组件的默认属性。

    getInitialState()

    初始化state,可以直接在constructor中定义this.state

    componentWillMount()

    组件加载时只调用,以后组件更新不调用,整个生命周期只调用一次,此时可以修改state

    render()

    
    react最重要的步骤,创建虚拟dom,进行diff算法,更新dom树都在此进行

    componentDidMount()

    组件渲染之后调用,只调用一次

    Updating(更新阶段:涉及5个钩子函数)

    componentWillReceiveProps(nextProps)

    组件加载时不调用,组件接受新的props时调用

    shouldComponentUpdate(nextProps, nextState)

    组件接收到新的props或者state时调用,return true就会更新dom(使用diff算法更新),return false能阻止更新(不调用render)

    componentWillUpdata(nextProps, nextState)

    组件加载时不调用,只有在组件将要更新时才调用,此时可以修改state

    render()

    
    react最重要的步骤,创建虚拟dom,进行diff算法,更新dom树都在此进行

    componentDidUpdate()

    组件加载时不调用,组件更新完成后调用

    Unmounting(卸载阶段:涉及1个钩子函数)

    componentWillUnmount()

    组件渲染之后调用,只调用一次
    

    组件的基本写法

    import React, { Component } from 'react'
    
    export default class OldReactComponent extends Component {
        constructor(props) {
            super(props)
            // getDefaultProps:接收初始props
            // getInitialState:初始化state
        }
        state = {
    
        }
        componentWillMount() { // 组件挂载前触发
    
        }
        render() {
            return (
                <h2>Old React.Component</h2>
            )
        }
        componentDidMount() { // 组件挂载后触发
    
        }
        componentWillReceiveProps(nextProps) { // 接收到新的props时触发
    
        }
        shouldComponentUpdate(nextProps, nextState) { // 组件Props或者state改变时触发,true:更新,false:不更新
            return true
        }
        componentWillUpdate(nextProps, nextState) { // 组件更新前触发
    
        }
        componentDidUpdate() { // 组件更新后触发
    
        }
        componentWillUnmount() { // 组件卸载时触发
    
        }
    }

    新的生命周期

    Mounting(加载阶段:涉及4个钩子函数)

    constructor()

    加载的时候调用一次,可以初始化state

    static getDerivedStateFromProps(props, state)

    组件每次被rerender的时候,包括在组件构建之后(虚拟dom之后,实际dom挂载之前),每次获取新的props或state之后;每次接收新的props之后都会返回一个对象作为新的state,返回null则说明不需要更新state;配合componentDidUpdate,可以覆盖componentWillReceiveProps的所有用法

    render()

    react最重要的步骤,创建虚拟dom,进行diff算法,更新dom树都在此进行

    componentDidMount()

    组件渲染之后调用,只调用一次

    Updating(更新阶段:涉及5个钩子函数)

    static getDerivedStateFromProps(props, state)

    组件每次被rerender的时候,包括在组件构建之后(虚拟dom之后,实际dom挂载之前),每次获取新的props或state之后;每次接收新的props之后都会返回一个对象作为新的state,返回null则说明不需要更新state;配合componentDidUpdate,可以覆盖componentWillReceiveProps的所有用法

    shouldComponentUpdate(nextProps, nextState)

    组件接收到新的props或者state时调用,return true就会更新dom(使用diff算法更新),return false能阻止更新(不调用render)

    render()

    react最重要的步骤,创建虚拟dom,进行diff算法,更新dom树都在此进行

    getSnapshotBeforeUpdate(prevProps, prevState)

    触发时间: update发生的时候,在render之后,在组件dom渲染之前;返回一个值,作为componentDidUpdate的第三个参数;配合componentDidUpdate, 可以覆盖componentWillUpdate的所有用法

    componentDidUpdate()

    组件加载时不调用,组件更新完成后调用

    Unmounting(卸载阶段:涉及1个钩子函数)

    
    组件渲染之后调用,只调用一次

    Error Handling(错误处理)

    componentDidCatch(error,info)

    任何一处的javascript报错会触发

    组件的基本写法

    import React, { Component } from 'react'
    
    export default class NewReactComponent extends Component {
        constructor(props) {
            super(props)
            // getDefaultProps:接收初始props
            // getInitialState:初始化state
        }
        state = {
    
        }
        static getDerivedStateFromProps(props, state) { // 组件每次被rerender的时候,包括在组件构建之后(虚拟dom之后,实际dom挂载之前),每次获取新的props或state之后;;每次接收新的props之后都会返回一个对象作为新的state,返回null则说明不需要更新state
            return state
        }
        componentDidCatch(error, info) { // 获取到javascript错误
    
        }
        render() {
            return (
                <h2>New React.Component</h2>
            )
        }
        componentDidMount() { // 挂载后
            
        }   
        shouldComponentUpdate(nextProps, nextState) { // 组件Props或者state改变时触发,true:更新,false:不更新
            return true
        }
        getSnapshotBeforeUpdate(prevProps, prevState) { // 组件更新前触发
    
        }
        componentDidUpdate() { // 组件更新后触发
    
        }
        componentWillUnmount() { // 组件卸载时触发
    
        }
    }

    总结

    旧的生命周期
    图片描述

    新的生命周期
    React16.3生命周期
    image.png
    React16.4+生命周期
    image.png

    1. React16新的生命周期弃用了componentWillMount、componentWillReceiveProps,componentWillUpdate
    2. 新增了getDerivedStateFromProps、getSnapshotBeforeUpdate来代替弃用的三个钩子函数(componentWillMount、componentWillReceiveProps,componentWillUpdate)
    3. React16并没有删除这三个钩子函数,但是不能和新增的钩子函数(getDerivedStateFromProps、getSnapshotBeforeUpdate)混用,React17将会删除componentWillMount、componentWillReceiveProps,componentWillUpdate
    4. 新增了对错误的处理(componentDidCatch)

    参考

    React.Component

    查看原文

    nyflxp 赞了文章 · 2018-12-19

    用 Node.js 快速开发出多功能的多人在线的文章分享平台

    no-shadow

    最近在学习使用 Node.js 框架,边学习边使用,花了大概 3周 时间做完这个 Web应用 且在 <time>12月16</time> 凌晨左右上线成功(其实就是把开发环境搬到服务器), 地址: https://a.lishaoy.net

    这个 Web应用 的代码是开源的,如对这个应用感兴趣,想知道代码是如何运行的,可以去我 GitHub 下载或 clone应用源码


    首先,来看看用 3周 时间做出来的应用都有些什么功能,之后再看看选用的 Node.js 框架,最后看看 Node.js 项目如何部署到服务器。

    Web应用功能

    登录、注册验证

    登录功能

    • 输入框没有输入点击登录会提示:用户名、密码不能为空
    • 输入的用户错误或不存在会提示:用户不存在
    • 输入的密码错误会提示:密码错误
    • 登录后会重定向到用户上次访问的地址

    no-shadow

    注册功能

    • 输入框没有输入点击注册会提示:用户名、邮箱、密码不能为空
    • 用户名和邮箱与其他用户相同会提示:用户名、邮箱已存在
    • 密码小于6位数会提示:最小长度是6位
    • 注册成功后会发送验证邮件到用户邮箱,需点击邮箱按钮验证

    no-shadow

    文章列表

    登录进来,会显示文章列表页面,显示内容如下:

    • 文章标题:点击可进入文章详情页
    • 作者头像、作者名称:点击可进入作者信息页
    • 时间:显示创建时间(多久以前方式显示)
    • 阅读次数、点赞次数
    • 文章简要:自动摘取章头文章
    • 缩略图:自动摘取文章第一张图片

    no-shadow

    文章详情

    点击文章标题可进入文章详情页面,内容如下:

    • 文章标题
    • 作者头像、作者名称
    • 发布时间
    • 阅读次数和点赞次数
    • 编辑按钮(仅作者可见)
    • 左侧浮动工具栏(点赞、发送邮件到自己邮箱、返回顶部、分享)
    • 点赞:文章被点赞后,作者可以收到消息通知,且将文章收录到点赞列表(支持匿名点赞,但不会记录通知,只会加点赞数)

    no-shadow

    编辑文章支持 Markdown

    新建文章和修改文章都支持 Markdown 语法,且会每隔6秒钟自动保存

    no-shadow

    个人信息

    个人信息页面显示内容如下

    • 作者的头像、姓名、简介(支持emoji)
    • 信息栏:GitHub 链接、个人网站链接、发布文章数、总阅读次数、总点赞次数
    • 发布文章列表:个人发布的所有文章(有删除和编辑按钮)
    • 已赞文章列表:点过赞的文章会记录在这里
    • 关注者列表:关注你的用户(关注过的用户,关注按钮高亮显示)
    • 已关注列表:你关注的用户(关注过的用户,关注按钮高亮显示)
    • 关注按钮:作者本人不可见,点击可关注,再次点击取消关注,关注后,用户会收到消息通知

    no-shadow

    文章删除编辑快捷入口,如图

    no-shadow

    下面是我用另一个用户登录,进入到个人信息页面就会显示关注按钮,如图

    no-shadow

    文件上传

    点击文件上传小图标可进入文件上传页面,点击 Files 链接可进入文件上传列表,显示内容如图:

    no-shadow

    no-shadow

    文件预览和编辑

    从文件列表页面点击标题可进入文件预览页面,显示内容如下:

    • 如果是图片显示图片,如果是视频显示视频
    • 工具栏:发送邮件到自己邮箱(登录可见)、编辑按钮、删除按钮(登录自己上传可见)
    • 文件名称
    • 下载按钮
    • 上传者头像

    no-shadow

    消息通知

    点击铃铛小图标可进入消息通知页面,内容如下:

    • 点赞消息列表:收到用户点赞通知,最新的未读消息会高亮显示,点击点赞者头像进入个人信息页面,点击文章标题进入你的文章详情页面
    • 关注者列表:收到关注者的通知,最新未读消息会高亮显示,点关注按钮也可关注他,再点击取消关注
    • 系统消息:目前还没有做功能实现

    no-shadow

    工具栏列表

    点击个人头像可展开工具栏列表,内容如下:

    • 写文章:点击可新建文章编辑页面,和 ➕ 小图标是同样功能
    • 上传文件:点击可打开文件上传页面,和上传小图标是同样功能
    • 个人信息: 点击可进入个人信息页面
    • 已赞:点击可查看已赞过得文章
    • 设置:点击可打开个人设置页面
    • 登出:点击退出登录

    no-shadow

    设置

    点击工具栏上的设置按钮可以设置页面,内容如下:

    个人信息设置

    • 头像:头像是使用的 Gravatar 提供的功能,根据邮箱生成头像
    • 用户名
    • 邮箱:已验证通过会显示验证小图标,没有通过的会显示提示
    • GitHub:只需填写有户名
    • 个人简介:支持emoji
    • 个人网站

    no-shadow

    修改密码设置

    需填写原密码,新密码,再次输入密码

    no-shadow

    聊天室

    点击 Chatroom 链接可进入聊天室,当然这个是用的 websocket 做的,内容如下:

    • 状态图标:显示链接状态
    • 活动用户:左侧黑色区域会动态显示活动用户
    • 消息:会显示发送消息,进入、离开房间通知消息(支持匿名发送消息,但不会保存消息)
    • 消息输入:消息输入框可输入消息,CmdEnter 换行(Windows会显示提示Ctrl+Enter),回车发送消息

    no-shadow

    加入房间和离开房间都有消息通知,如图

    no-shadow

    Node.js 框架

    这个应用的开发我选择的是 Adonisjs 框架,他和 PHPLaravel 有些像,Adonisjs 是在操作系统上运行的 Node.jsMVC 框架。

    接下来,来看看 Adonisjs 框架有哪些特性:

    环境安装简单

    不管是开发环境还是生产环境,安装 Adonisjs 运行环境都是非常简单,先来看看开发环境的安装,生产环境后面会提到。

    首先,我们的电脑上需要安装好 Node.js大于 8.00 版本,管理 Node.js 可以使用 nvm

    其次,就可以使用 npm 安装 Adonis CLI 命令行工具(管理 npm 使用源可以使用 nrm

    npm i -g @adonisjs/cli

    这样就可以在全局使用 adonis 命令

    再次,可以是 adonis new 命令创建项目

    adonis new adonis_pro

    cd 进入项目,执行 adonis serve --dev 运行项目

    cd adonis_pro
    adonis serve --dev

    这样您的开发环境就搭建完成。

    RMVC

    RMVC 就是路由、模型、视图、控制器。

    路由

    创建一条路由非常简单,如

    Route.get('liked/:userId/:postId', 'LikedController.liked')

    这条路由就是用来处理上面提到的点赞功能的

    当然,Adonisjs 提供了 资源路由 以便您更方便的创建路由,例如

    Route.resource('posts', 'PostController').middleware(
       new Map([
            [ [ 'create', 'store', 'edit', 'update', 'destroy' ], [ 'auth' ] ],
            [ [ 'update', 'destroy', 'edit' ], [ 'own:post' ] ]
       ])
    ).validator(new Map([
      [['posts.update', 'posts.store'], ['StorePost']]
    ]))

    这个路由是来处理上面应用提到的文章的 增、删、改、查 ,这个可能有些复杂,使用了 中间件 来处理用户登录状态和操作权限,使用了 验证器 来处理表单验证,这里不介绍的太复杂,如想了解这些具体功能,可以需要花点时间了解学习。

    我们可以去掉 中间件验证器 ,如下:

    Route.resource('posts', 'PostController')

    这条资源路由,其实就包含了以下路由:

    Route.get(url, closure)
    Route.post(url, closure)
    Route.put(url, closure)
    Route.patch(url, closure)
    Route.delete(url, closure)

    Adonisjs 还提供了路由组和其他一些功能,路由组如下:

    Route.group(() => {
        Route.get('profile', 'ProfileController.edit').as('profile.edit')
        Route.post('profile', 'ProfileController.update').as('profile.update').validator('UpdateProfile')
        Route.get('password', 'PasswordController.edit').as('password.edit')
        Route.post('password', 'PasswordController.update').as('password.update').validator('UpdatePassword')
    })
        .prefix('settings')
        .middleware([ 'auth' ])

    使用 .prefixRoute.group 来创建路由组,这条路由组是处理 个人信息设置 功能的,这样访问页面是就统一要带上 settings/**

    控制器

    Adonisjs 提供了命令行来创建控制器,如

    adonis make:controller User --type http

    这样就创建了一个 User 控制器,自动生成代码如下:

    'use strict'
    
    class UserController {
    }
    
    module.exports = UserController

    当然,我们还可以使用 --resource 创建资源类型的控制器

    adonis make:controller Post --resource

    自动生成代码,代码如下:

    'use strict'
    
    class PostController {
     /**
       * Show a list of all posts.
       * GET posts
       */
    async index ({ request, response, view }) {}
    
     /**
       * Render a form to be used for creating a new posts.
       * GET posts/create
       */
    async create ({ request, response, view }) {}
    
     /**
       * Create/save a new posts.
       * POST posts
       */
    async store ({ request, response, view }) {}
     /**
       * Display a single posts.
       * GET posts/:id
       */
    async show ({ request, response, view }) {}
    
     /**
       * Render a form to update an existing posts.
       * GET posts/:id/edit
       */
    async edit ({ request, response, view }) {}
    
     /**
       * Update posts details.
       * PUT or PATCH posts/:id
       */
    async update ({ request, response, view}) {}
    
     /**
       * Delete a posts with id.
       * DELETE posts/:id
       */
    async destroy ({ params, request, response }) {}
    }
    
    module.exports = PostController

    和上面的资源路由是对应的,如用 GET 请求访问 posts 就会调用 index 方法(一般用来显示) ,再如:用 DELETE 请求访问 posts/1 就会执行 destroy 方法(一般用来删除)。

    模型

    Adonisjs 提供了两种模式来处理数据,Query builderLUCID

    首先,我们可以通过 adonis make:migration 来创建数据表

    adonis make:migration users

    会自动生成代码,如下:

    'use strict'
    
    const Schema = use('Schema')
    
    class UsersSchema extends Schema {
      up () {
        this.create('users', (table) => {
          table.increments()
          table.timestamps()
        })
      }
    
      down () {
        this.drop('users')
      }
    }
    
    module.exports = UsersSchema

    这是我们只需在其中添加想要的字段就行,如:

    'use strict'
    
    const Schema = use('Schema')
    
    class UsersSchema extends Schema {
      up () {
        this.create('users', (table) => {
          table.increments()
          table.string('username', 80).notNullable().unique()
          table.string('email', 254).notNullable().unique()
          table.string('password', 60).notNullable()
          table.timestamps()
        })
      }
    
      down () {
        this.drop('users')
      }
    }
    
    module.exports = UsersSchema

    在执行 adonis migration:run 命令就可以在数据库生成数据表

    再来看看,如何获取数据,可以使用 Query builderLUCID 两种方式

    先来看看 Query builder

    const Database = use('Database')
    
    class UserController {
    
      async index (request, response) {
        return await Database
          .table('users')
          .where('username', 'admin')
          .first()
      }
    
    }

    查询 usernameadmin 的用户

    Adonisjs 提供了非常多的方法去操作数据,不是特复杂的关系都够用,如果,关系比较复杂,还可以用原生的 sql 操作,如

    'use strict'
    
    const Database = use('Database')
    
      class NotificationController {
      async followNotice ({ auth, view }) {
        const notices = await Database.raw('select users.id as user_id,users.username,users.email,b.title,b.created_at,b.is_read,b.id as post_id from adonis.users , (select posts.id,posts.title, a.user_id,a.created_at,a.is_read from adonis.posts,(SELECT post_user.post_id, post_user.user_id, post_user.created_at, post_user.is_read FROM adonis.post_user where post_user.post_id in (SELECT posts.id FROM adonis.posts where user_id = ?)) as a where posts.id = a.post_id) as b where b.user_id = users.id and b.user_id <> ? order by b.created_at desc limit 50',[ auth.user.id, auth.user.id ])
      }
    }
    
    module.exports = NotificationController

    使用 Database.raw 来运行原生的 sql,以上这条 sql 是用来查询所有用户给自己所有文章点赞的用户信息和文章信息用于消息通知。

    再来看看,LUCID 的模式是如何操作数据的:

    使用 LUCID 模式,我们先需要用命令行工具创建 Models,如:

    adonis make:model User

    自动生成代码如下:

    'use strict'
    
    const Model = use('Model')
    
    class User extends Model {
    }
    
    module.exports = User

    模型和模型之间需要定义一些关系,如:

    const Model = use('Model')
    
    class User extends Model {
      profile () {
        return this.hasOne('App/Models/Profile')
      }
    }
    
    module.exports = User

    意思是 一个用户对应一个用户信息档案,一对一 的关系

    定义好关系之后,就可以方便的获取数据,如:

    const User = use('App/Models/User')
    
    const user = await User.find(1)
    const userProfile = await user.profile().fetch()

    意思是,从用户表和用户个人信息表里获取用户 id1 的用户信息及个人信息,

    其中,关系可以定义为 3一对一、一对多、多对多 ,多对多需要定义中间表

    再来看看,上面的应用中的实际应用,如:

    async update ({ params, request, response, session, auth }) {
      const { title, content, user_id, tags } = request.all()
    
      const post = await Post.findOrFail(params.id)
      post.merge({ title, content})
      await post.save()
    
      await post.tags().sync(tags)
    
      session.flash({
        type: 'primary',
        message: 'Post updated successfully.'
      })
    
      return response.redirect(
        Route.url('PostController.show', {
          id: post.id
        })
      )
    }

    以上,是更新文章的方法,文章标签多对多 的关系,一个标签可以属于多篇文章,一篇文章可以有多个标签,await post.tags().sync(tags) 这句代码就可以通过 Models 里定义的关系自动把标签和文章关联起来保存到 poststags 表里且把关联关系保存到中间表 post_tag

    当然,Adonisjs 提供了很多方便的方法,想了解更多的话需要您花点时间去了解学习。

    视图

    Adonisjs 框架里视图使用了 edge 模板,我们可以使用命令行工具创建视图文件,如:

    adonis make:view post

    我看可以看下简单的例子:

    @loggedIn
      <h2> You are logged in </h2>
    @else
      <p> <a href="/login">Click here</a> to login </p>
    @endloggedIn

    视图模板里可以使用标签来做逻辑判断,视图模板就没什么好说的,基本都是通用的,关于 edge 视图模板更多语法 Edge官方文档

    最后,Adonisjs 框架还提供了很多其它的实用工具,如:Middleware 中间件、Validator 验证器、Error Handling 自定义异常、Events 事件、Mails 邮件、Websocket 等来处理各种问题。

    Node.js项目发布到阿里云服务器

    首先,我们需要用 ssh 连接到阿里云(或者其他服务器供应商)的主机上,安装一些必要的工具。

    工具安装

    安装 epel-release 软件包仓库

    我们需要安装 epel-release 软件包仓库,epel-release 里面有很多最新的软件包,如,之后安装的 git 就会用到

    sudo yum install epel-release - y

    安装 Git 版本控制命令行工具

    sudo yum install git -y

    准备 Node.js 运行环境

    接下来,我们需要安装 Node.js 以便我们的 Node.js 项目能够跑起来,我们可以使用 nvm 安装和管理 Node.js ,使用 nrm 来管理切换安装源。

    安装 nvm

    curl -o- https://raw.githubusercontent.com/creationix/nvm/v0.33.11/install.sh | bash

    安装好之后,我们需要配置下环境变量,以便能够在命令行使用 nvm 命令,用 vi ~/.bash_profile 编辑下配置文件

    vi ~/.bash_profile

    加入以下代码:

    export NVM_DIR="$HOME/.nvm"
    [ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh"
    [ -s "$NVM_DIR/bash_completion" ] && \. "$NVM_DIR/bash_completion"

    然后,在 source ~/.bash_profile 刷新下配置文件,让它生效

    source ~/.bash_profile

    此时,我们就可以使用 nvm 来安装 Node.js

    nvm install node

    安装好后,可以使用 nvm list 来查看有哪些版本可以使用

    nvm list

    结果:

    ->     v10.13.0
            v11.2.0
             system
    default -> v10.13.0
    node -> stable (-> v11.2.0) (default)
    stable -> 11.2 (-> v11.2.0) (default)
    iojs -> N/A (default)
    lts/* -> lts/dubnium (-> v10.13.0)
    lts/argon -> v4.9.1 (-> N/A)
    lts/boron -> v6.14.4 (-> N/A)
    lts/carbon -> v8.13.0 (-> N/A)
    lts/dubnium -> v10.13.0

    我使用的是 v10.13.0 的版本,默认安装的都是比较新的版本,可能是 v11.2.0v11.1.0,所以我们也可以用 nvm install v10.13.0 来安装指定版本。

    nvm install v10.13.0

    然后,就可以使用 nvm use v10.13.0 来使用指定版本

    nvm use nvm v10.13.0

    结果:

    Now using node v10.13.0 (npm v6.4.1)

    安装 nrm 管理安装源

    使用 npm 安装的程序包,默认的来源是 http://registry.npmjs.org,国内的下载速度会有些慢,我们可以是 nrm 来切换到 taobao 的源

    安装 nrm

    npm install nrm --global

    切换到 taobao 源

    nrm use taobao

    准备项目

    以上工作完成之后,我们的服务器就可以正常运行 Node.js 项目,现在我们需要把本地的项目上传到服务器,上传方法有很多,如:

    • 可以使用 git,先把项目传到 GitHub,然后用 git 下载到服务器
    • 可以是 FTP 工具
    • 可以是命令上传 scp -r 本地目录 root@服务器IP:/var/www/

    发项目文件上传到服务器的指定目录下,如:www

    接下来,我们可以是 PM2 来管理 Node 进程,先需要安装 PM2

    安装PM2

    npm install pm2@latest --global

    这些工作作为之后,就可以来测试一下,启动项目,在本地访问服务器 IP:PORT 来测试是否可以访问

    测试项目是否可以运行

    在测试之前,我们需要改下应用的配置文件,adonisjs 框架里是 .env 文件,修改下 HOST 的值:

    HOST=0.0.0.0
    PORT=3333
    ...

    HOST 默认是 127.0.0.1,需要改成 0.0.0.0 这样就可以在自己电脑上用服务器 IP:PORT 来访问应用

    改完后,进入到项目的根目录,运行应用,adonisjs 的启动文件是 server.js,如:

    pm2 start server.js

    如启动成功会提示:

    [PM2] Applying action restartProcessId on app [server](ids: 0)
    [PM2] [server](0) ✓
    [PM2] Process successfully started
    ┌──────────┬────┬─────────┬──────┬──────┬────────┬─────────┬────────┬─────┬──────────┬──────┬──────────┐
    │ App name │ id │ version │ mode │ pid  │ status │ restart │ uptime │ cpu │ mem      │ user │ watching │
    ├──────────┼────┼─────────┼──────┼──────┼────────┼─────────┼────────┼─────┼──────────┼──────┼──────────┤
    │ server   │ 0  │ 4.1.0   │ fork │ 7171 │ online │ 30      │ 0s     │ 0%  │ 3.4 MB   │ root │ disabled │
    └──────────┴────┴─────────┴──────┴──────┴────────┴─────────┴────────┴─────┴──────────┴──────┴──────────┘
     Use `pm2 show <id|name>` to get more details about an app

    然后,在自己电脑上用服务器 IP:PORT 来访问应用。

    Nginx 代理

    为了让服务器更好地处理网络请求,我们需要添加使用 Nginx 反向代理 把请求转发给 Node.js 应用

    安装 Nginx

    sudo yum install nginx -y

    如果你的服务之前安装过可不用安装,我的阿里云服务器运行了 4 个站点之前安装过,之后我只需添加配置就行。

    启动 Nginx

    sudo systemctl start nginx

    配置 Nginx

    一般情况 Nginx 安装好后会有 /etc/nginx/conf.d 目录,进入这个目录,创建一个配置文件为 Node.js 而准备,名字可随意命名,如:adonis.conf

    server {
      listen 80;
      location / {
          proxy_pass http://127.0.0.1:3333;
          proxy_http_version 1.1;
          proxy_set_header Upgrade $http_upgrade;
          proxy_set_header Connection 'upgrade';
          proxy_set_header Host $host;
          proxy_set_header X-Real-IP $remote_addr;
          proxy_set_header X-Forwarded-Proto $scheme;
          proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
          proxy_cache_bypass $http_upgrade;
      }
    }

    然后,在 Nginx 的主配置文件里把刚才新创建的配置文件(/etc/nginx/nginx.confinclude 进去就可以,如:

    include /etc/nginx/conf.d/*.conf;

    因为,我的主机里运行了4个站点,* 的意思就是加载这个目录下的所有配置文件

    然后,记得把刚才项目里的 .env 配置文件改成 127.0.0.1 ,因为我们现在使用了代理,网络请求交给了 Nginx

    再进入到项目的根目录下运行:

    pm2 stop server.js #停止项目
    pm2 start server.js #启动项目

    这时候再用服务器 IP 访问就是用的 Nginx 去处理请求

    域名和SSL

    如果你有域名可以去对应的供应商解析好,如想使用 https 协议,也可以去对应的供应商下载好证书(下载好的证书要放到服务器某个目录里)。

    再修改下刚才创建的配置文件,让它能够支持 https 和 域名 访问:

    server {
      listen 80;
          listen 443 ssl http2; #SSL
      server_name a.lishaoy.net; #域名
      ssl on;
    
      ssl_certificate /etc/letsencrypt/live/a.lishaoy.net/server.pem; #证书目录
      ssl_certificate_key /etc/letsencrypt/live/a.lishaoy.net/server.key; #证书目录
      ssl_protocols TLSv1.1 TLSv1.2;
      ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:HIGH:!aNULL:!MD5:!RC4:!DHE;
      ssl_prefer_server_ciphers on;
      ssl_session_cache shared:SSL:10m;
      ssl_session_timeout 10m;
      if ($ssl_protocol = "") {
        rewrite ^(.*) https://$host$1 permanent;
      }
      error_page 497  https://$host$request_uri;
    
      error_page 404 /404.html;
      error_page 502 /502.html;
    
      location / {
          proxy_pass http://localhost:3333;
          proxy_http_version 1.1;
          proxy_set_header Upgrade $http_upgrade;
          proxy_set_header Connection 'upgrade';
          proxy_set_header Host $host;
          proxy_set_header X-Real-IP $remote_addr;
          proxy_set_header X-Forwarded-Proto $scheme;
          proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
          proxy_cache_bypass $http_upgrade;
      }
    }

    这样再重启 Ningx 服务和项目的服务,就大功告成了。

    查看原文

    赞 48 收藏 33 评论 3

    nyflxp 关注了专栏 · 2018-12-04

    阿里妈妈前端技术周刊

    阿里妈妈为阿里巴巴的广告部门,本周刊主要面向前端,包括新闻和专题两个板块。

    关注 1388

    nyflxp 回答了问题 · 2018-05-22

    setTimeout最小间隔4ms的问题

    setTimeout的第二个参数,不得低于4毫秒,如果低于这个值,就会自动增加,根据浏览器和当前环境不同,最低时间间隔也不一样,浏览器执行的顺序应该和浏览器当前状态有关系,我在firebox执行的结果是0 1 5 4 3 2

    关注 13 回答 8

    nyflxp 发布了文章 · 2018-05-17

    Video的深入使用

    原文阅读:更好的阅读

    HTML <video> 元素 用于在HTML或者XHTML文档中嵌入视频内容

    属性

    • controls

    设置或返回视频是否应该显示控件(比如播放/暂停等)
    <video data-original="./mt-baker_cibsgl.mp4" width="100%" controls></video>

    image.png

    • autoplay

    设置或返回是否在就绪(加载完成)后自动播放视频
    <video data-original="http://codehtml.cn/code-demo/video/mt-baker_cibsgl.mp4" width="100%" controls autoplay></video>
    • nodownload

    设置是否去除去除下载按钮nodownload
    <video data-original="http://codehtml.cn/code-demo/video/mt-baker_cibsgl.mp4" width="100%" controls controlslist="nodownload"></video>
    • nofullscreen

    设置是否去除全屏显示按钮
    <video data-original="http://codehtml.cn/code-demo/video/mt-baker_cibsgl.mp4" width="100%" controls controlslist="nodownload nofullscreen"></video>
    • poster

    设置视频的封面
    <video data-original="http://codehtml.cn/code-demo/video/mt-baker_cibsgl.mp4" width="100%" controls poster="https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1526349212159&di=d6e00c2a2102cac0b50e0e622aa02618&imgtype=0&data-original=http%3A%2F%2Fimg5.duitang.com%2Fuploads%2Fitem%2F201411%2F06%2F20141106104720_WHEe2.jpeg"></video>

    image.png

    • muted

    设置是否静音(注意:移动端非静音模式下无法自动播放
    <video data-original="http://codehtml.cn/code-demo/video/code.mp4" width="100%" controls muted></video>
    • loop

    设置循环播放
    <video data-original="http://codehtml.cn/code-demo/video/mt-baker_cibsgl.mp4" width="100%" controls loop></video>
    • preload

    视频预加载模式
    <video data-original="http://codehtml.cn/code-demo/video/mt-baker_cibsgl.mp4" width="100%" controls preload></video>
    • volume

    音量控制,区间范围在0-1
    <video data-original="http://codehtml.cn/code-demo/video/code.mp4" width="100%" controls id="volume"></video>
    
    var time = document.getElementById("time");
    time.currentTime = 60;// 秒
    • 播放时间控制

    <video data-original="http://codehtml.cn/code-demo/video/code.mp4" width="100%" controls preload id="time"></video>
    <script type="text/javascript">
      var time = document.getElementById("time");
      time.currentTime = 60;// 秒
    </script>
    • 播放地址切换

    <video data-original="http://codehtml.cn/code-demo/video/code.mp4" width="100%" controls preload id="_src"></video>
    <script type="text/javascript">
      var _src = document.getElementById("_src");
      function changeSrc(){
        _src.src = "http://codehtml.cn/code-demo/video/mt-baker_cibsgl.mp4";
      }
    </script>
    • 备用地址切换

    <video width="100%" controls id="_source">
      <source data-original="http://codehtml.cn/code-demo/video/code1.mp4" type="video/mp4"> // 错误地址
      <source data-original="http://codehtml.cn/code-demo/video/mt-baker_cibsgl.mp4" type="video/mp4">
    </video>
    <script type="text/javascript">
      var _source = document.getElementById("_source");
      setTimeout(function () { 
        console.log(_source.currentSrc);// 获取当前url
      },2000)
    </script>
    • 播放速度

    <video data-original="http://codehtml.cn/code-demo/video/code.mp4" width="100%" controls preload id="_speed"></video>
    <script type="text/javascript">
      var _speed = document.getElementById("_speed");
      _speed.playbackRate = 0.5;
    </script>

    事件

    <video data-original="http://codehtml.cn/code-demo/...; width="100%" controls id="vs"></video>
    • loadstart

    ?> 当浏览器开始寻找指定的音频/视频时,会发生 loadstart 事件。即当加载过程开始时

    v.addEventListener('loadstart',function(e){
      console.log("loadstart");
    })
    • durationchange

    音频/视频的时长
    v.addEventListener('durationchange',function(e){
      console.log("时长", v.duration);
    })
    • loadedmetadata

    当浏览器已经加载完成视频
    v.addEventListener('loadedmetadata',function(e){
      console.log("loadedmetadata");
    })
    • loadeddata

    当浏览器已加载视频的当前帧时
    v.addEventListener('loadeddata',function(e){
      console.log("loadeddata");
    })
    • progress

    ?> 当浏览器正在下载视频

    v.addEventListener('progress',function(e){
      console.log("progress");
    })
    • canplay

    判断是否可以播放
    v.addEventListener('canplay',function(){
      console.log('canplay');
    })
    • canplaythrough

    判断是否可以流畅播放
    v.addEventListener('canplaythrough',function(){
      console.log('canplaythrough');
    })
    • play

    视频播放
    v.addEventListener('play',function(){
      console.log('play');
    })
    • pause

    视频暂停
    v.addEventListener('pause',function(){
      console.log('pause');
    })
    • seeking

    当用户开始移动/跳跃到音视频中的新位置时
    v.addEventListener('seeking',function(){
      console.log('seeking');
    })
    • seeked

    当用户已移动/跳跃到视频中的新位置
    v.addEventListener('seeked',function(){
      console.log('seeked');
    })
    • waiting

    当视频由于需要缓冲下一帧而停止,等待
    v.addEventListener('waiting',function(){
      console.log('waiting');
    })
    • playing

    当视频在已因缓冲而暂停或停止后已就绪时
    v.addEventListener('playing',function(){
      console.log('playing');
    })
    • timeupdate

    目前的播放位置已更改时,播放时间更新
    v.addEventListener('timeupdate',function(){
      console.log('timeupdate');
    })
    • ended

    播放结束
    v.addEventListener('ended',function(){
      console.log('ended');
    })
    • error

    播放错误
    v.addEventListener('error',function(e){
      console.log('error', e);
    })
    • volumechange

    当音量已更改时
    v.addEventListener('volumechange',function(){
      console.log('volumechange');
    })
    • stalled

    当浏览器尝试获取媒体数据,但数据不可用时
    v.addEventListener('stalled',function(){
      console.log('stalled');
    })
    • ratechange

    当视频的播放速度已更改时
    v.addEventListener('ratechange',function(){
      console.log('ratechange');
    })

    参考资料:

    MDN: video

    HTML的媒体支持:audio和video元素

    HTML 5 视频/音频参考手册

    原文阅读 ---->

    查看原文

    赞 8 收藏 10 评论 0

    nyflxp 发布了文章 · 2018-05-07

    CSS3之mix-blend-mode

    image

    <!-- more -->

    在线编辑demo

    属性描述了元素的内容应该与元素的直系父元素的内容和元素的背景如何混合。

    mix-blend-mode: normal; //正常

    mix-blend-mode: multiply; //正片叠底

    mix-blend-mode: screen; //滤色

    mix-blend-mode: overlay; //叠加

    mix-blend-mode: darken; //变暗

    mix-blend-mode: lighten; //变亮

    mix-blend-mode: color-dodge; //颜色减淡

    mix-blend-mode: color-burn; //颜色加深

    mix-blend-mode: hard-light; //强光

    mix-blend-mode: soft-light; //柔光

    mix-blend-mode: difference; //差值

    mix-blend-mode: exclusion; //排除

    mix-blend-mode: hue; //色相

    mix-blend-mode: saturation; //饱和度

    mix-blend-mode: color; //颜色

    mix-blend-mode: luminosity; //亮度

    mix-blend-mode: initial; //初始

    mix-blend-mode: inherit; //继承

    mix-blend-mode: unset; //复原

    在线编辑demo

    image.png

    参考资料:

    MDN: mix-blend-mode
    原文地址

    查看原文

    赞 0 收藏 0 评论 0

    nyflxp 发布了文章 · 2018-04-28

    前端面试题收集,持续更新中

    HTML

    在线阅读
    Github地址

    题目列表

    CSS

    在线阅读
    Github地址

    题目列表

    JS

    在线阅读
    Github地址

    题目列表

    综合

    在线阅读
    Github地址

    题目列表

    查看原文

    赞 41 收藏 119 评论 15

    nyflxp 赞了文章 · 2018-04-18

    剖析Vue原理&实现双向绑定MVVM

    本文能帮你做什么?
    1、了解vue的双向数据绑定原理以及核心代码模块
    2、缓解好奇心的同时了解如何实现双向绑定
    为了便于说明原理与实现,本文相关代码主要摘自vue源码, 并进行了简化改造,相对较简陋,并未考虑到数组的处理、数据的循环依赖等,也难免存在一些问题,欢迎大家指正。不过这些并不会影响大家的阅读和理解,相信看完本文后对大家在阅读vue源码的时候会更有帮助<
    本文所有相关代码均在github上面可找到 https://github.com/DMQ/mvvm

    相信大家对mvvm双向绑定应该都不陌生了,一言不合上代码,下面先看一个本文最终实现的效果吧,和vue一样的语法,如果还不了解双向绑定,猛戳Google

    <div id="mvvm-app">
        <input type="text" v-model="word">
        <p>{{word}}</p>
        <button v-on:click="sayHi">change model</button>
    </div>
    
    <script data-original="./js/observer.js"></script>
    <script data-original="./js/watcher.js"></script>
    <script data-original="./js/compile.js"></script>
    <script data-original="./js/mvvm.js"></script>
    <script>
        var vm = new MVVM({
            el: '#mvvm-app',
            data: {
                word: 'Hello World!'
            },
            methods: {
                sayHi: function() {
                    this.word = 'Hi, everybody!';
                }
            }
        });
    </script>

    效果:
    图片描述

    几种实现双向绑定的做法

    目前几种主流的mvc(vm)框架都实现了单向数据绑定,而我所理解的双向数据绑定无非就是在单向绑定的基础上给可输入元素(input、textare等)添加了change(input)事件,来动态修改model和 view,并没有多高深。所以无需太过介怀是实现的单向或双向绑定。

    实现数据绑定的做法有大致如下几种:

    发布者-订阅者模式(backbone.js)

    脏值检查(angular.js)

    数据劫持(vue.js)

    发布者-订阅者模式: 一般通过sub, pub的方式实现数据和视图的绑定监听,更新数据方式通常做法是 vm.set('property', value),这里有篇文章讲的比较详细,有兴趣可点这里

    这种方式现在毕竟太low了,我们更希望通过 vm.property = value 这种方式更新数据,同时自动更新视图,于是有了下面两种方式

    脏值检查: angular.js 是通过脏值检测的方式比对数据是否有变更,来决定是否更新视图,最简单的方式就是通过 setInterval() 定时轮询检测数据变动,当然Google不会这么low,angular只有在指定的事件触发时进入脏值检测,大致如下:

    • DOM事件,譬如用户输入文本,点击按钮等。( ng-click )
    • XHR响应事件 ( $http )
    • 浏览器Location变更事件 ( $location )
    • Timer事件( $timeout , $interval )
    • 执行 $digest() 或 $apply()

    数据劫持: vue.js 则是采用数据劫持结合发布者-订阅者模式的方式,通过Object.defineProperty()来劫持各个属性的settergetter,在数据变动时发布消息给订阅者,触发相应的监听回调。

    思路整理

    已经了解到vue是通过数据劫持的方式来做数据绑定的,其中最核心的方法便是通过Object.defineProperty()来实现对属性的劫持,达到监听数据变动的目的,无疑这个方法是本文中最重要、最基础的内容之一,如果不熟悉defineProperty,猛戳这里
    整理了一下,要实现mvvm的双向绑定,就必须要实现以下几点:
    1、实现一个数据监听器Observer,能够对数据对象的所有属性进行监听,如有变动可拿到最新值并通知订阅者
    2、实现一个指令解析器Compile,对每个元素节点的指令进行扫描和解析,根据指令模板替换数据,以及绑定相应的更新函数
    3、实现一个Watcher,作为连接Observer和Compile的桥梁,能够订阅并收到每个属性变动的通知,执行指令绑定的相应回调函数,从而更新视图
    4、mvvm入口函数,整合以上三者

    上述流程如图所示:
    图片描述

    1、实现Observer

    ok, 思路已经整理完毕,也已经比较明确相关逻辑和模块功能了,let's do it
    我们知道可以利用Obeject.defineProperty()来监听属性变动
    那么将需要observe的数据对象进行递归遍历,包括子属性对象的属性,都加上 settergetter
    这样的话,给这个对象的某个值赋值,就会触发setter,那么就能监听到了数据变化。。相关代码可以是这样:

    var data = {name: 'kindeng'};
    observe(data);
    data.name = 'dmq'; // 哈哈哈,监听到值变化了 kindeng --> dmq
    
    function observe(data) {
        if (!data || typeof data !== 'object') {
            return;
        }
        // 取出所有属性遍历
        Object.keys(data).forEach(function(key) {
            defineReactive(data, key, data[key]);
        });
    };
    
    function defineReactive(data, key, val) {
        observe(val); // 监听子属性
        Object.defineProperty(data, key, {
            enumerable: true, // 可枚举
            configurable: false, // 不能再define
            get: function() {
                return val;
            },
            set: function(newVal) {
                console.log('哈哈哈,监听到值变化了 ', val, ' --> ', newVal);
                val = newVal;
            }
        });
    }
    

    这样我们已经可以监听每个数据的变化了,那么监听到变化之后就是怎么通知订阅者了,所以接下来我们需要实现一个消息订阅器,很简单,维护一个数组,用来收集订阅者,数据变动触发notify,再调用订阅者的update方法,代码改善之后是这样:

    // ... 省略
    function defineReactive(data, key, val) {
        var dep = new Dep();
        observe(val); // 监听子属性
    
        Object.defineProperty(data, key, {
            // ... 省略
            set: function(newVal) {
                if (val === newVal) return;
                console.log('哈哈哈,监听到值变化了 ', val, ' --> ', newVal);
                val = newVal;
                dep.notify(); // 通知所有订阅者
            }
        });
    }
    
    function Dep() {
        this.subs = [];
    }
    Dep.prototype = {
        addSub: function(sub) {
            this.subs.push(sub);
        },
        notify: function() {
            this.subs.forEach(function(sub) {
                sub.update();
            });
        }
    };

    那么问题来了,谁是订阅者?怎么往订阅器添加订阅者?
    没错,上面的思路整理中我们已经明确订阅者应该是Watcher, 而且var dep = new Dep();是在 defineReactive方法内部定义的,所以想通过dep添加订阅者,就必须要在闭包内操作,所以我们可以在 getter里面动手脚:

    // Observer.js
    // ...省略
    Object.defineProperty(data, key, {
        get: function() {
            // 由于需要在闭包内添加watcher,所以通过Dep定义一个全局target属性,暂存watcher, 添加完移除
            Dep.target && dep.addSub(Dep.target);
            return val;
        }
        // ... 省略
    });
    
    // Watcher.js
    Watcher.prototype = {
        get: function(key) {
            Dep.target = this;
            this.value = data[key];    // 这里会触发属性的getter,从而添加订阅者
            Dep.target = null;
        }
    }

    这里已经实现了一个Observer了,已经具备了监听数据和数据变化通知订阅者的功能,完整代码。那么接下来就是实现Compile了

    2、实现Compile

    compile主要做的事情是解析模板指令,将模板中的变量替换成数据,然后初始化渲染页面视图,并将每个指令对应的节点绑定更新函数,添加监听数据的订阅者,一旦数据有变动,收到通知,更新视图,如图所示:
    图片描述

    因为遍历解析的过程有多次操作dom节点,为提高性能和效率,会先将跟节点el转换成文档碎片fragment进行解析编译操作,解析完成,再将fragment添加回原来的真实dom节点中

    function Compile(el) {
        this.$el = this.isElementNode(el) ? el : document.querySelector(el);
        if (this.$el) {
            this.$fragment = this.node2Fragment(this.$el);
            this.init();
            this.$el.appendChild(this.$fragment);
        }
    }
    Compile.prototype = {
        init: function() { this.compileElement(this.$fragment); },
        node2Fragment: function(el) {
            var fragment = document.createDocumentFragment(), child;
            // 将原生节点拷贝到fragment
            while (child = el.firstChild) {
                fragment.appendChild(child);
            }
            return fragment;
        }
    };

    compileElement方法将遍历所有节点及其子节点,进行扫描解析编译,调用对应的指令渲染函数进行数据渲染,并调用对应的指令更新函数进行绑定,详看代码及注释说明:

    Compile.prototype = {
        // ... 省略
        compileElement: function(el) {
            var childNodes = el.childNodes, me = this;
            [].slice.call(childNodes).forEach(function(node) {
                var text = node.textContent;
                var reg = /\{\{(.*)\}\}/;    // 表达式文本
                // 按元素节点方式编译
                if (me.isElementNode(node)) {
                    me.compile(node);
                } else if (me.isTextNode(node) && reg.test(text)) {
                    me.compileText(node, RegExp.$1);
                }
                // 遍历编译子节点
                if (node.childNodes && node.childNodes.length) {
                    me.compileElement(node);
                }
            });
        },
    
        compile: function(node) {
            var nodeAttrs = node.attributes, me = this;
            [].slice.call(nodeAttrs).forEach(function(attr) {
                // 规定:指令以 v-xxx 命名
                // 如 <span v-text="content"></span> 中指令为 v-text
                var attrName = attr.name;    // v-text
                if (me.isDirective(attrName)) {
                    var exp = attr.value; // content
                    var dir = attrName.substring(2);    // text
                    if (me.isEventDirective(dir)) {
                        // 事件指令, 如 v-on:click
                        compileUtil.eventHandler(node, me.$vm, exp, dir);
                    } else {
                        // 普通指令
                        compileUtil[dir] && compileUtil[dir](node, me.$vm, exp);
                    }
                }
            });
        }
    };
    
    // 指令处理集合
    var compileUtil = {
        text: function(node, vm, exp) {
            this.bind(node, vm, exp, 'text');
        },
        // ...省略
        bind: function(node, vm, exp, dir) {
            var updaterFn = updater[dir + 'Updater'];
            // 第一次初始化视图
            updaterFn && updaterFn(node, vm[exp]);
            // 实例化订阅者,此操作会在对应的属性消息订阅器中添加了该订阅者watcher
            new Watcher(vm, exp, function(value, oldValue) {
                // 一旦属性值有变化,会收到通知执行此更新函数,更新视图
                updaterFn && updaterFn(node, value, oldValue);
            });
        }
    };
    
    // 更新函数
    var updater = {
        textUpdater: function(node, value) {
            node.textContent = typeof value == 'undefined' ? '' : value;
        }
        // ...省略
    };

    这里通过递归遍历保证了每个节点及子节点都会解析编译到,包括了{{}}表达式声明的文本节点。指令的声明规定是通过特定前缀的节点属性来标记,如<span v-text="content" other-attrv-text便是指令,而other-attr不是指令,只是普通的属性。
    监听数据、绑定更新函数的处理是在compileUtil.bind()这个方法中,通过new Watcher()添加回调来接收数据变化的通知

    至此,一个简单的Compile就完成了,完整代码。接下来要看看Watcher这个订阅者的具体实现了

    3、实现Watcher

    Watcher订阅者作为Observer和Compile之间通信的桥梁,主要做的事情是:
    1、在自身实例化时往属性订阅器(dep)里面添加自己
    2、自身必须有一个update()方法
    3、待属性变动dep.notice()通知时,能调用自身的update()方法,并触发Compile中绑定的回调,则功成身退。
    如果有点乱,可以回顾下前面的思路整理

    function Watcher(vm, exp, cb) {
        this.cb = cb;
        this.vm = vm;
        this.exp = exp;
        // 此处为了触发属性的getter,从而在dep添加自己,结合Observer更易理解
        this.value = this.get(); 
    }
    Watcher.prototype = {
        update: function() {
            this.run();    // 属性值变化收到通知
        },
        run: function() {
            var value = this.get(); // 取到最新值
            var oldVal = this.value;
            if (value !== oldVal) {
                this.value = value;
                this.cb.call(this.vm, value, oldVal); // 执行Compile中绑定的回调,更新视图
            }
        },
        get: function() {
            Dep.target = this;    // 将当前订阅者指向自己
            var value = this.vm[exp];    // 触发getter,添加自己到属性订阅器中
            Dep.target = null;    // 添加完毕,重置
            return value;
        }
    };
    // 这里再次列出Observer和Dep,方便理解
    Object.defineProperty(data, key, {
        get: function() {
            // 由于需要在闭包内添加watcher,所以可以在Dep定义一个全局target属性,暂存watcher, 添加完移除
            Dep.target && dep.addDep(Dep.target);
            return val;
        }
        // ... 省略
    });
    Dep.prototype = {
        notify: function() {
            this.subs.forEach(function(sub) {
                sub.update(); // 调用订阅者的update方法,通知变化
            });
        }
    };

    实例化Watcher的时候,调用get()方法,通过Dep.target = watcherInstance标记订阅者是当前watcher实例,强行触发属性定义的getter方法,getter方法执行的时候,就会在属性的订阅器dep添加当前watcher实例,从而在属性值有变化的时候,watcherInstance就能收到更新通知。

    ok, Watcher也已经实现了,完整代码
    基本上vue中数据绑定相关比较核心的几个模块也是这几个,猛戳这里 , 在src 目录可找到vue源码。

    最后来讲讲MVVM入口文件的相关逻辑和实现吧,相对就比较简单了~

    4、实现MVVM

    MVVM作为数据绑定的入口,整合Observer、Compile和Watcher三者,通过Observer来监听自己的model数据变化,通过Compile来解析编译模板指令,最终利用Watcher搭起Observer和Compile之间的通信桥梁,达到数据变化 -> 视图更新;视图交互变化(input) -> 数据model变更的双向绑定效果。

    一个简单的MVVM构造器是这样子:

    function MVVM(options) {
        this.$options = options;
        var data = this._data = this.$options.data;
        observe(data, this);
        this.$compile = new Compile(options.el || document.body, this)
    }

    但是这里有个问题,从代码中可看出监听的数据对象是options.data,每次需要更新视图,则必须通过var vm = new MVVM({data:{name: 'kindeng'}}); vm._data.name = 'dmq'; 这样的方式来改变数据。

    显然不符合我们一开始的期望,我们所期望的调用方式应该是这样的:
    var vm = new MVVM({data: {name: 'kindeng'}}); vm.name = 'dmq';

    所以这里需要给MVVM实例添加一个属性代理的方法,使访问vm的属性代理为访问vm._data的属性,改造后的代码如下:

    function MVVM(options) {
        this.$options = options;
        var data = this._data = this.$options.data, me = this;
        // 属性代理,实现 vm.xxx -> vm._data.xxx
        Object.keys(data).forEach(function(key) {
            me._proxy(key);
        });
        observe(data, this);
        this.$compile = new Compile(options.el || document.body, this)
    }
    
    MVVM.prototype = {
        _proxy: function(key) {
            var me = this;
            Object.defineProperty(me, key, {
                configurable: false,
                enumerable: true,
                get: function proxyGetter() {
                    return me._data[key];
                },
                set: function proxySetter(newVal) {
                    me._data[key] = newVal;
                }
            });
        }
    };
    

    这里主要还是利用了Object.defineProperty()这个方法来劫持了vm实例对象的属性的读写权,使读写vm实例的属性转成读写了vm._data的属性值,达到鱼目混珠的效果,哈哈

    至此,全部模块和功能已经完成了,如本文开头所承诺的两点。一个简单的MVVM模块已经实现,其思想和原理大部分来自经过简化改造的vue源码,猛戳这里可以看到本文的所有相关代码。
    由于本文内容偏实践,所以代码量较多,且不宜列出大篇幅代码,所以建议想深入了解的童鞋可以再次结合本文源代码来进行阅读,这样会更加容易理解和掌握。

    总结

    本文主要围绕“几种实现双向绑定的做法”、“实现Observer”、“实现Compile”、“实现Watcher”、“实现MVVM”这几个模块来阐述了双向绑定的原理和实现。并根据思路流程渐进梳理讲解了一些细节思路和比较关键的内容点,以及通过展示部分关键代码讲述了怎样一步步实现一个双向绑定MVVM。文中肯定会有一些不够严谨的思考和错误,欢迎大家指正,有兴趣欢迎一起探讨和改进~

    最后,感谢您的阅读!

    查看原文

    赞 1343 收藏 1805 评论 152

    nyflxp 收藏了文章 · 2018-03-16

    Github装逼指南——Travis CI 和 Codecov

    好久没写博客了,趁着年前空闲的时间来一篇轻松点的东西。
    最近工作中积累了一些Angular组件打算整一整把他们开源了,既然要开源那么代码可靠性就很重要了,单测不能少,为了保证每次提交的代码都能很好的运行,持续集成不能少。之前看到很多开源项目介绍中都有一些单测覆盖率和build结果的图标,就像这样:

    clipboard.png

    觉得挺酷的。打算在自己的开源组件中也整一套。
    经过Google决定使用TravisCI来进行持续集成,Codecov来统计单测覆盖率。

    Travis CI

    Travis CI是国外新兴的开源持续集成构建项目,支持Github项目。使用十分方便。

    1. 使用Github账号登录Travis CI

    2. 登录之后会自动同步Github项目,选择需要使用Travis CI的项目

    3. 在项目的根目录新增.travis.yml文件,内容如下:

    #指定运行环境
    language: node_js
    #指定nodejs版本,可以指定多个
    node_js:
      - 0.12.5
    
    #运行的脚本命令
    script:
      - npm run ci
    
    #指定分支,只有指定的分支提交时才会运行脚本
    branches:
      only:
        - master

    更多语法请看这里。使用起来非常方便,这样当你每次向github push代码的时候,Travis CI就会自动运行.travis.yml里面的script。自动进行编译以及运行单测。
    由于Travis CI每次build之前都会运行npm install安装项目依赖的npm包,所以在提交代码的时候要保证把所有依赖的包都已经在package.json中声明了,否则build就会失败。

    Codecov

    Codecov是一个开源的测试结果展示平台,将测试结果可视化。Github上许多开源项目都使用了Codecov来展示单测结果。
    Codecov跟Travis CI一样都支持Github账号登录,同样会同步Github中的项目。在nodejs环境下使用Codecov需要安装对于的npm包,运行下面这个命令进行安装:

    npm install codecov --save-dev

    这个包的作用是将我们运行单测产生的结果文件上传到Codecov上进行可视化展示。同时codecov支持的结果文件类型为cobertura。所以需要保证单测执行的结果文件的类型为cobertura
    前端项目进行单元测试推进karma + 'jasmine'的组合。这两个具体是什么东西大家Google一下就知道。使用karma可以通过简单的配置来运行单测。下面是我一个项目中的配置文件,供大家参考:

    // Karma configuration
    // Generated on Mon Feb 01 2016 21:34:22 GMT+0800 (中国标准时间)
    
    module.exports = function(config) {
      config.set({
    
        // base path that will be used to resolve all patterns (eg. files, exclude)
        basePath: '',
    
    
        // frameworks to use
        // available frameworks: https://npmjs.org/browse/keyword/karma-adapter
        // 使用的测试框架jasmine, requirejs支持模块化加载
        frameworks: ['jasmine', 'requirejs'],
    
    
        // list of files / patterns to load in the browser
        files: [
            // karma中用到进行requirejs配置的文件
            'test/test-main.js',
            // 测试中需要用到的文件,includeed设为false表示在页面加载的时候不会加载相应的js文件,也就是可以通过requirejs进行异步加载
            {pattern: 'node_modules/jquery/dist/jquery.min.js', included: false},
            {pattern: 'node_modules/angular/angular.min.js', included: false},
            {pattern: 'node_modules/angular-mocks/angular-mocks.js', included: false},
            {pattern: 'src/bg-single-selector.js', included: false},
            {pattern: 'test/selector.spec.js', included: false}
        ],
    
    
        // list of files to exclude
        exclude: [
        ],
    
    
        // preprocess matching files before serving them to the browser
        // available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor
        // 针对bg-single-selector.js生成单测覆盖率结果
        preprocessors: {
            'src/bg-single-selector.js': 'coverage'
        },
    
    
        // test results reporter to use
        // possible values: 'dots', 'progress'
        // available reporters: https://npmjs.org/browse/keyword/karma-reporter
        // 测试结果的几种输出方式
        reporters: ['progress', 'coverage', 'verbose'],
        // 测试结果报告的类型
        coverageReporter:{
            reporters: [{
                type:'text-summary'
            }, {
                type: 'html',
                dir: 'test/coverage'
            }, {
                // 这就是Codecov支持的文件类型
                type: 'cobertura',
                subdir: '.',
                dir: 'test/coverage'
            }]
        },
    
        // web server port
        port: 9876,
    
    
        // enable / disable colors in the output (reporters and logs)
        colors: true,
    
    
        // level of logging
        // possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG
        logLevel: config.LOG_INFO,
    
    
        // enable / disable watching file and executing tests whenever any file changes
        autoWatch: true,
    
        // start these browsers
        // available browser launchers: https://npmjs.org/browse/keyword/karma-launcher
        browsers: ['PhantomJS'],
        // 运行测试依赖的插件
        plugins: [
            'karma-jasmine',
            'karma-coverage',
            'karma-verbose-reporter',
            'karma-phantomjs-launcher',
            'karma-requirejs'
        ],
    
        // Continuous Integration mode
        // if true, Karma captures browsers, runs the tests and exits
        singleRun: true
      })
    }
    

    通过karma进行单元测试,将命令写到.travis.yml中就可以在每次build的时候运行单测,同时运行codecov [cobertura-coverage.xml路径]就会把单测结果上传到Codecov。在本地运行codecov会失败,需要将这个过程加入到Travis CI的build脚本中,才能成功上传。因为在本地运行就会被作为私有项目,对于私有项目在上传结果时需要加上Codecov提供的token。

    在github中加入图标

    到了最后一步,Travis CI和Codecov都提供图标链接来展示结果。我们只需要将图标链接加入到项目的README中就可以看到结果了。
    对于Travis CI来说,点击下图中的图标:

    clipboard.png
    就会弹出图标的地址。
    对于Codecov来说,打开项目的设置列表就会看到,如下:

    clipboard.png

    最后只需要将对应的链接加到README文件中就可以了。下面是最后的效果:

    clipboard.png

    是不是很赞!
    项目地址:BGSingleSelector,欢迎大家试用提意见,同时不要吝啬Star。

    最后的最后,做一个广告。百度告警平台。这是一个智能的告警平台,提供实时精确的告警送达,故障的协作处理能力。再也不需要担心遗漏监控报警,解放运维人力。

    最后,安利下我的个人博客,欢迎访问:http://bin-playground.top

    查看原文

    nyflxp 赞了文章 · 2018-03-14

    FPB 2.0:免费的计算机编程类中文书籍 2.0

    之前我在 github 上整理了来一份:free-programming-books-zh_CN(免费的计算机编程类中文书籍)

    截至目前为止,已经有 90 多人发了 300 多个 Pull Requests 和 200 多个 issues,从最初的几十本书到现在的近 500 本书。

    欢迎大家提 PR: https://github.com/justjavac/...

    语言无关类

    操作系统

    智能系统

    分布式系统

    编译原理

    函数式概念

    计算机图形学

    WEB服务器

    版本控制

    编辑器

    NoSQL

    PostgreSQL

    MySQL

    管理和监控

    项目相关

    设计模式

    Web

    大数据

    编程艺术

    其它

    语言相关类

    Android

    APP

    AWK

    C/C++

    C#

    Clojure

    CSS/HTML

    Dart

    Elixir

    Erlang

    Fortran

    Go

    Groovy

    Haskell

    iOS

    Java

    JavaScript

    LaTeX

    LISP

    Lua

    OCaml

    Perl

    PHP

    Prolog

    Python

    R

    Ruby

    Rust

    Scala

    Shell

    Swift

    读书笔记及其它

    测试相关

    查看原文

    赞 524 收藏 2250 评论 48