Quantcast
Channel: CNode:Node.js专业中文社区
Viewing all 14821 articles
Browse latest View live

职业迷茫期,求大佬开解。

$
0
0

小弟我今年刚毕业,英语专业的。实习前一年自学cs知识,发现专业知识蛮够用的,好像偏题了。 现在在创业公司做,职位是打杂的,前端后端产品都做,有空就学新知识(ai),刷刷小项目什么的(比如说爬虫sis)。 现在很迷茫,和老板提过想转职做产品,但是老板然拒了。 前端写的比较多吧,前端比较熟vue什么的,其他框架也能直接上手。 后端最近一直在玩python,入门的是nodejs(js大法好),最近的计划是学go。 问题来了。这样打杂式的工作学习方法,每一部分都会,每一部分都不精,我很气。 1、pm,公司主要做的是to B的,旅游方面。将来肯定也会有各种项目要做(老板不让干,只让我兼着做)。 2、前端,公司前端根本不需要很深的东西,会的东西也不能运用,完全不能实战,小项目也只能随便玩玩。 3、后端,其实蛮喜欢nodejs的,后端很棒,写了很多小东西改善重复化的事情。 4、aiPm,新兴职业?前几个礼拜在携程沙龙上听到的有趣的职业,做ai产品的产品经理。

现在很麻烦,我应该选哪一条路笔直往前呢?迷茫了一段时间了,每天也刷cnode,想听听大佬们的意见。


vs Code问题?

$
0
0

image.png

代码格式化的时候 会出现这种换行 我不想这样 不知道哪位大神 知道在什么地方 设置一下

Unknown encoding: gb2312

$
0
0
const fs = require('fs');
var readable = fs.createReadStream("gbk.txt", {
    encoding: 'gb2312',
    fd: null,
});
readable.on('readable', function() {
  var chunk;
  while (null !== (chunk = readable.read(1))) {
    console.log(chunk); // chunk is one char
  }
});

internal/fs.js:20 throw new Error(Unknown encoding: ${encoding}) ^

Error: Unknown encoding: gb2312 at assertEncoding (internal/fs.js:20:11) at getOptions (fs.js:58:5) at new ReadStream (fs.js:1824:24) at Object.fs.createReadStream (fs.js:1813:10) 如果要读gbk编码的文件,还有什么方法

gulp 相关,gulp-connect 为什么对图片路径是不起作用的??

$
0
0

image.pngimage.png补充: 我修改文件夹 中js 文件名,可以自动刷新页面,修改图片名,则不会 image.png gulp-connect 插件里面 应该是 过滤了 文件后缀名为 图片类型的。

cnode的markdown怎么才能解析audio的标签

$
0
0

我想实现下面的功能,我看cnode源码中吧HTML:false标签关闭了,我测试HTML:true后还是渲染不出来,还要做其他什么改动吗?image.png然后就是实现上传图片一样的上传音频的功能,求指点,给些思路。

如何从零构建个人博客系统

$
0
0

简介

这篇文章主要分享博客里涉及的Ruby, Rails,前端CSS,JS,ubuntu系统命令等知识。如果有什么不解的地方可以通过http://liuzhen.me页面下方的二维码扫描加我微信。

  • Ruby是一种纯粹的面向对象编程语言。它由日本的松本行弘(まつもとゆきひろ/Yukihiro Matsumoto)创建于1993年。

  • Ruby on Rails(官方简称为Rails,亦被简称为RoR),是一个使用Ruby语言写的开源Web应用框架,它是严格按照MVC结构开发的。它努力使自身保持简单,来使实际的应用开发时的代码更少,使用最少的配置。指南: https://ruby-china.github.io/rails-guides/

  • CSS 指层叠样式表, 你在页面看到的展示效果都是通过CSS做出来的,页面的布局,字体大小,颜色,边框,菜单等等. 详情可以查看: http://www.runoob.com/css/css-intro.html

  • JS 是属于网络的脚本语言, 能做的事太多了,像我博客里的相册功能,时间线都是JS做出来的效果。

安装Rails环境

你可以通过搜索 Mac/windows/ubuntu install rails来找到相关文档,这里提供ubuntu 16.04版本的安装文档: https://gorails.com/setup/ubuntu/16.04 , Mac的安装文档: https://ruby-china.org/wiki/mac-nginx-passenger-rails

创建一个Rails项目

安装好Rails环境之后,你可以创建一个Rails项目了,如果你从来没用过Rails,可以先用15分钟学习一下 Rails入门, 了解Rails MVC结构。

如果你对Rails有一定的了解,可以按照这个模版 https://github.com/80percent/rails-template提供的操作步骤, 创建一个Rails项目,使用这个模版创建Rails项目的好处是,这个模版相当于一个全家桶,预先添加一个项目经常需要使用的Gem包,发布需要的puma, mina, monit, nginx配置文件,关于这几个东西是什么,有什么用后面会讲到。

启动Rails

$ rails s

访问 localhost:3000就能看到 hello world 页面了。

创建数据模型

我的博客在设计之初只想要文章,相册,简历这几个功能,这三个功能比较相似,都有标题,内容和可有可无的描述。所以我就用了单表继承,建了个base表。

$ rails g model Base title:string content:text subtitle:string type:string

type字段就是用于单表继承。

执行完这条命令之后,你会看到 db/migrate/xxxx_create_bases.rb多了一个这样的文件,里面的内容是:

class CreateBases < ActiveRecord::Migration[5.1]
  def change
    create_table :bases do |t|
      t.string :title
      t.string :subtitle
      t.text :content
      t.string :type
      t.timestamps
    end
  end
end

t.timestamps 是时间戳,系统会自动在这个表里面加上 created_at, updated_at两个字段。

添加完之后,需要把做一下数据迁移,我一开始学rails的时候对 数据迁移这个词很不理解。其实数据迁移的意思就是,我们现在通过命令创建了个数据表的文件,但是这个文件没有被执行,不执行数据库里就还没有这张表,只有在执行了 rails db:migrate之后,rails才在数据库里把这张表给加上,这个操作就叫做 数据迁移。

创建完了Base表,现在就要创建文章表了 Article, 我们需要添加一个 app/models/article.rb文件,写上:

class Article < Base
end

因为Article继承了Base, 所以就拥有了Base的所有字段了。

你可以通过 rails c从控制台输入 Article.new可以看到:

irb(main):006:0* Article.new
=> #<Article id: nil, title: nil, subtitle: nil, content: nil, type: "Article", created_at: nil, updated_at: nil>

type字段自动就是Article, 这是Rails的一个特性,单表继承。

输入Article.all, 看到的sql语句实际是从bases里查询type为Article的所以记录。

irb(main):007:0> Article.all
Article Load (127.0ms)  SELECT  "bases".* FROM "bases" WHERE "bases"."type" IN ('Article') LIMIT $1  [["LIMIT", 11]]

相册表Photo,简历表ResumeArticle的创建方式相同.

表创建好了,我们就可以创建Controller了,Controller需要区分前端和后端,前端就是提供给用户查询的,后台是提供自己添加,更新,删除操作的。另外后台因为是管理的地方所以不能让所有人都访问,所以需要设置成通过用户名和密码登录。这样别人就无法访问你的后台。

后端设计

为了与前台有所区分,所以需要加一下命名空间: 这里设置成 admin. 先在 config/routes.rb里添加路由,

Rails.application.routes.draw do
  namespace :admin do
    root 'dashboard#index', as: 'root'
    resources :articles
    resources :photos
    resource :resume, only: [:edit, :update]
  end
end

root 'dashboard#index', as: 'root'设置后台的root路由。使用 rails routes命令可以查看具体的路由信息。

控制器

controllers目录下添加admin目录,这个目录下用于存放所有的后台文件,后台添加一个 app/controllers/admin/base_controller.rb文件,继承了 ApplicationController, 这么做是为了admin下的所有controller继承 Admin::BaseController后,用户访问后端链接就会先校验当前用户是否登录,如果没有登录就跳转到登录页面。 代码如下:

class Admin::BaseController < ApplicationController
  layout 'admin'
  before_action :authenticate_user
  def authenticate_user
    unless session[:login]
      redirect_to new_session_path
    end
  end
end

因为article, photo, resume这几个功能比较相似,所以我只讲一下article控制器:app/controllers/admin/articles_controller.rb

代码:

class Admin::ArticlesController < Admin::BaseController
  def index
    @articles = Article.all.order(created_at: 'DESC').page(params[:page])
  end
  def new
    @article = Article.new
  end
  def create
    @article = Article.new(article_params)
    if @article.save
      redirect_to admin_articles_path
    else
      render 'new'
    end
  end
  def edit
    @article = Article.find(params[:id])
  end
  def update
    @article = Article.find(params[:id])
    if @article.update(article_params)
      flash[:notice] = '更新成功'
      redirect_to admin_articles_path
    else
      render 'edit'
    end
  end
  def destroy
    @article = Article.find(params[:id])
    if @article.destroy
      flash[:notice] = '删除成功'
    else
      flash[:notice] = "删除失败, 原因: #{@article.errors.messages.to_s}"
    end
  end
  private
  def article_params
    params.require(:article).permit(:title, :subtitle, :content)
  end
end

控制台里面很简单,就是增,删,改,查。需要注意的就是redirect_to, render的区别,什么时候要用render, 什么时候用redirect_to.

render是指直接熏染某个页面.

redirect_to是指告诉浏览器,让浏览器再重新发送一个指定路由的请求操作。

如:create action里写到如果保存成功就 redirect_to admin_articles_path, 如果失败就 render 'new'.

  1. 假如保存成功,就会告诉浏览器, 让浏览器再向服务器发送一个admin/articles路由请求,然后进入index action里,查询所有Action记录,再熏染index.html页面,返回给浏览器。

  2. 假如保存失败,就用用户填写的@article信息熏染new.html页面,并用flash里的信息,告诉用户提交失败的原因,如果失败后用 redirect_to new_admin_articles_path,也能跳转到new页面,但是用户提交的信息就没有了。

view

.row
  .offset-md-2.col-md-8
    = simple_form_for [:admin, @article] do |f|
      = f.error_notification
      = f.input :title
      = f.input :subtitle
      = f.text_area :content, id: 'editor_content', class: 'simditor', autofocus: true
      = f.submit '提交', class: 'btn btn-primary'
      = link_to '取消', admin_articles_path
javascript:
  new Simditor({
    textarea: $('#editor_content'),
    toolbar: ['title', 'bold', 'italic', 'underline', 'strikethrough', 'fontScale', 'color', '|', 'ol', 'ul', '|', 'blockquote', 'code', 'table', 'link', 'image', 'hr', 'indent', 'outdent', 'alignment']
  });

这个有点需要讲的是编辑器使用了simditor插件,具体要加哪些信息可以看一下这个文档: http://simditor.tower.im/, 但是要支持上传图片功能需要在admin下添加一条路由: post '/upload', to: 'photos#upload', 在photos controller里添加一个upload action,把上传的图片保存到数据库。

前端设计

前端的controller继承ApplicationController,前端的因为只设计到查询,所以添加路由的时候加上only, 如: resources :articles, only: [:index, :show], 就只添加两条路由,如果不加only默认会创建 7 条路由。

前端功能主要就涉及到css.

css调试步骤:

  1. 右击选择’检查’,就能打开控制台,在控制台处,通过点击(2) 处的图标,可以选择页面上任意节点,选择后(3)处会显示这个节点所对应的CSS样式。同样在style处可以通过添加和注释css来对页面样式进行调试。

样式这里涉及的东西太多,我不一一讲解,只讲一些我认为值得讲一讲的知识点。如果想学习更多的css样式知识,可以在文章开头处提供的文档查看学习。

  1. 文章的展示对字体,间距,背景,颜色等等都要求很高,如果设计的不好,文章看久了就容易累,而且容易给别人一种不想去看的感觉。如果间距很窄,一大段落全是文字,就给人一种很不舒服的感觉。如果你对这些信息了解不多,不知道把这些值设置成多少比较好,也不要担心,找一个你觉得文字展示效果看起来很舒服的网站,打开他的控制台,看一下这个网站上这些信息设置的值是多少,跟着一样设置就行了。具体的细节可以再另做调整。

  2. 博客的页面底部用的fa字体,在gemfile里添加 font-awesome-sass后,就能展示出这些字体图标。但是目前的字体中没有支付宝的字体图标,你先不要看代码,想一想,如果是你,你要怎么实现一个跟fa字体相同效果的图标,这个图标带有hover效果,当鼠标放上去的时候背景变成了蓝色。

我的实现方法:

一开始我想的是用一个黑白图片代替,弄完之后我发现hover效果无法实现。于是我就用一个背景透明只有一个支字的图片代替,设置border-radius,background-color和字体达成一致效果,当鼠标放上去的时候就改变background-color: #0085A1;

代码:

footer .fa-alipay {
    border-radius: 50%;
    margin-bottom: 4px;
    background-color: #222529;
    width: 41px;
}
footer .fa-alipay:hover {
    background-color: #0085A1;
}

调试页面上关于hover,visited, focus, active效果,可以像图片中勾选来查看相应的样式效果。

时间线

时间线是用的一个js库,https://github.com/RyanFitzgerald/vertical-timeline, 具体可以查看文档。值得说一下的是,一开始看到这个时间线的效果是在一个网站看到的。然后我通过页面控制台,看到里面class名称命名很规范,所以感觉是个js库,直接在google搜索 cd-timeline-block第一个结果就是这个库的信息。除了这种方式,还可以通过控制台的Sources查看assets文件信息,一般都是经常压缩的,但是有些外部库是有注释的,会写上这个是来自哪个库之类的信息。不过最简单快速的办法还是用google搜索来的快一点。如果你搜的class名字没有找到相应的信息,可以换个class名字试试。

另外一个要说的是,这个JS库里的一个js文件main.js, 与turbolink一起加载,没生效,加载的时候就没被执行,然后我就把它用$(document).on 'turbolinks:load', 加载就好了。

相册

博客里我最喜欢的就是这个相册功能了,当初也是看了这个翻书的效果,我才有重写博客的冲动。看到这个js库是在github Trending上, 这上面会推荐github上比较火的项目。这个库的地址: http://www.turnjs.com, 这里面提供了几个demo. 这个turnjs用的 yepnope加载js,这么加载是因为,有些内容需要在其他文件加载之后去执行。但是有个问题是在生产环境这些js文件都是被转译了的。所以直接在yepnope里面写上文件名,在生产环境上就会找不到对应的文件。对于这个我没有想到特别好的处理办法,就用

$('head').append('<%= javascript_include_tag 'turn.min.js' %>')

来加载文件,然后再执行yepnope({complete: loadApp})。如果你有更好的办法可以交流一下。

其他知识点

  • 效果支持手机端页面需要加上: meta width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no

  • 前端和后端的JS, CSS尽量分开,这样加载速度会快一些

  • 使用的外部库最好重新命名成可读名称, 不然时间长了你就不知道这个库是干什么的了. 比如我用了 timeline 的JS库, 里面有个main.js的文件, 我就把它重命名为 timeline-main.js, 这样不需要再加注释来说明这个文件是做什么的了.

发布

发布需要在服务器上安装好rails后,配置nginx.conf,一般放在/etc/nginx/conf.d/xxx.conf文件.

puma.rbdeploy.rb配置文件是mina部署的相关配置信息,具体操作了什么可以通过bundle exec mina deploy -v来查看:

$ bundle exec mina deploy -v
-----> Creating a temporary build path
-----> Server: liuzhen.me
-----> Path: /home/ruby/RBlog
       $ echo "-----> Branch: master"
-----> Branch: master
-----> Using RVM environment "2.3.1"
-----> Quiet sidekiq (stop accepting new work)
-----> Fetching new git commits
       $ (cd "/home/ruby/RBlog/scm" && git fetch "https://github.com/liuzhenangel/RBlog.git" "master:master" --force)
-----> Using git branch 'master'
       $ git clone "/home/ruby/RBlog/scm" . --recursive --branch "master"
       Cloning into '.'...
       done.
-----> Using this git commit
       $ git rev-parse HEAD > .mina_git_revision
       $ git --no-pager log --format="%aN (%h):%n> %s" -n 1
       liuzhenangel (eb06b54):
       > update timeline
       $ rm -rf .git
-----> Symlinking shared paths
-----> Installing gem dependencies using Bundler
       $ bundle install --without development test --path "vendor/bundle" --deployment
-----> DB migrations unchanged; skipping DB migration
-----> Skipping asset precompilation
-----> Cleaning up old releases (keeping 5)
-----> Deploy finished
-----> Building
-----> Moving build to /home/ruby/RBlog/releases/41
-----> Build finished
-----> Launching
-----> Updating the /home/ruby/RBlog/current symlink
-----> Restart Puma -- hard...
-----> Stopping Puma...
-----> Starting Puma...

从这些日志信息可以看出, 首先会根据你配置的服务器的域名和用户名ssh到服务器上,加载ruby环境,从github拉取最新代码,安装gem包,如果css, js, 图片有更新就重新编译压缩js, css, 图片, 执行 db:migrate数据迁移, 首次发布还会执行rails db:create来创建数据库。然后重启puma.

为什么要nginx,puma

nginx相当于一个代理,当你在浏览器输入:http://liuzhen.me的时候,先通过DNS找到这个域名对应的IP,然后通过路由到达IP所在的服务器上,服务器发现用户请求的是 liuzhen.me,然后nginx就根据配置文件里配置的 liuzhen.me找到对应的项目,这个Rails项目是由puma启动的,然后nginx就把这个事交给puma, puma收到后就根据路由去到对应的controller,然后返回对应的页面给到浏览器。

monit 是什么

没有monit也能发布成功,但是有时候你的puma可能异常关掉了,如果有monit的话,就能时刻监听这个进程是不是启动状态,一旦关闭了,就重新启动。

博客地址: http://liuzhen.me

博客代码: https://github.com/liuzhenangel/RBlog

原文来源: http://liuzhen.me/articles/16

中间件-上云产品 资深前端开发工程师/前端专家/高级前端专家

$
0
0

中间件-上云产品 资深前端开发工程师/前端专家/高级前端专家

招募中(急招)(长期招聘) 招聘邮箱 hanxie.wq@alibaba-inc.com

工作地点

  1. 杭州(阿里巴巴西溪园区)
  2. 深圳(深圳阿里中心阿里巴巴大厦)
  3. 北京

部门介绍

中间件技术部是阿里巴巴集团生态系统的技术基石,为集团各大业务群提供可靠、高效、易扩展的技术基础服务。这里有世界一流的中间件产品和场景,这里有世界最大的电商交易业务场景,这里有世界领先的企业互联网架构平台。我们的愿景是打造世界一流的中间件产品,打造世界一流的高可用架构基础设施,打造世界一流的企业级互联网架构平台。

在招职位

中间件-资深前端开发工程师

工作年限

  1. 二年以上

岗位职责

关注用户体验,不断改进服务的易用性; 参与中间件产品 前端部分开发 能提出必要的改进建议 参与前端中台产品建设 改进协作流程,创建技术标准和规范

岗位要求

大学本科学历,2年以上开发经验,能熟练使用常见类库或框架,编写高质量的前端代码; 熟练掌握React、Redux及相关框架和技术,有单页面应用开发经验; 精通ES6,gulp,webpack等规范和技术; 熟悉NodeJS,有NodeJS开发经验,熟悉Express\koa等框架; 善于 Web 性能优化与前端效果的实现; 良好的团队合作精神和积极主动的沟通意识,具有很强的学习能力和对新技术的追求精神,乐于分享; 有大型网站开发经验者优先; 良好的中英文读写能力,英语口语流利者优先。

中间件-前端开发专家

工作年限

  1. 三年以上

岗位职责

通过对业务深刻的理解,改进技术方案,提高团队整体的研发效率和质量; 改进协作流程,创建技术标准和规范; 参与集团前端中台技术建设,并推动其在业务中成功落地; 辅导、带领新人,传道授业解惑,提升团队整体技术水平;

岗位要求

精通各种Web前端技术(HTML/CSS/Javascript等),熟练跨浏览器、跨终端的开发; 精通React、AngularJS、Vue等MVVM框架至少一种,并有在大型项目中使用的成功经验; 熟悉 Node.js Web 应用开发,有大型 Node.js 项目的开发经验; 熟悉 Web 安全相关知识,并能使用相关技术防范安全漏洞; 有大型网站前端架构、前端性能、可访问性、可维护性等方面的实践经验; 良好的项目管理能力,能独当一面负责一个大型项目的研发流程管理; 良好的沟通表达能力,个性乐观开朗,逻辑性强,善于和各种背景的人合作; 至少熟悉一种后端开发语言,有后端开发经验者优先; 良好的中英文读写能力,英语口语流利者优先。

岗位优势

在这里你可以接触到中国乃至世界最牛的 后端开发人员,可以了解到服务于阿里双十一的中间件体系,你可以结合自己的前端知识跟后端碰撞产生优秀的idea,我们提供完备的产品化商业化体系让你的idea能最终落地上云 你有很棒的老板支持你的创新,使你不仅仅局限在前端世界。 我们跟阿里云和国际UED的关系都非常紧密在这里你也可以接触并了解他们的优秀

总结

中间件值得你加入

express异步异常的处理

$
0
0

koa可以设置个全局中间件,try catch然后抛出错误,因为中间件走2次

express现在的办法

app.get('/index', async function (req, res) {
  try {
    //业务逻辑
	await do something
  } catch (err) {
    res.send(e.message)
  }
});

每个路由都要手动写try catch…路由如果用了promise或async,报错的时候监听500是没反应。后端是抛错了,但是浏览器端没反应,所以手动res.send错误信息

而搜了一下,除了try catch还有domain,都是4-5年前的文章如( http://cnodejs.org/topic/516b64596d38277306407936) ,现在还有什么方法吗,每个路由写try-catch有点烦。用的express,暂时不能换koa


node 国内服务器,除了阿里云、腾讯云这些,有么有稍微便宜点的... 自己做项目的话

$
0
0

node 国内服务器,除了阿里云、腾讯云这些,有么有稍微便宜点的… 自己做项目的话

聊聊《柒个我》这部剧

$
0
0

前言

本文首发公众号 【一名打字员】

最近朋友圈都在追一部正在热播的剧,名字叫做《柒个我》,主演是张一山。没错,就是通常我们都叫他“夏雨”的那个张一山(这个梗不知道的出去面壁)。这个剧翻拍自韩剧《Kill Me Heal Me》,老套筒了,不过大家都很期待张一山的演技,那么到底这部热播剧在豆瓣上的反响如何呢,今天是平安夜,在大家都在过圣诞节追剧的同时,打字员用数据的角度来分析分析这部剧。

评分

截止到现在,这部剧在豆瓣上评分为 4.7, 已经有 10140个人对其进行评价。而《Kill Me Heal Me》(杀了我治愈我)韩版在豆瓣上评分 8.8, 有 39788个人评价,其中两者占比为下图所示:

image.pngimage.png

没有对比也没有伤害,难道我“夏雨”的演技就真的这点水平吗?

评论

接下来我们就来看看评论,看看评论中大部分都是一些啥关键字。为了保持公平公正,打字员对豆瓣该剧的前20页评论进行了整理,然后基于这段数据做出分析。

首先,用结巴分词进行中文分词,去掉停用词并进行词频统计,其大部分关键字如图所示:

image.png

Merry Christmas!

image.png

然后评论区点赞前前三的是以下评论:

  1. 853赞 情节照搬我忍了,造型一样也算了,但是你剪辑、分镜、台词都跟原版毫无二致那还翻拍个什么劲???况且演员演技还不如原版好。。。PS:还好没来个翻唱版《幻听》不然我恐怕要吐血。
  2. 541赞 唯一一星给张一山。台词,服装都和原版一样,这就不说了。打光不如韩版,配角演技出戏,甚至连配乐都和原版差不多。男二看着都比男主大好多。唯一在线张一山,不过感觉对于角色掌控力还差点,年龄上也差点火候,演总裁的时候总感觉跟硬挺着一样。整个剧最大诚意就是展现了这是翻拍,剩下就没啥诚意了。
  3. 523赞 池城的演技可以说是演了7个不同精神世界的同一人,但是到张一山这咋就是7个人了呢。

结语

基于以上,仅仅是作为参考评论,不代表本人观点,从评分的角度来说,这部剧在豆瓣上确实不算太火,甚至达到烂剧的水准。但是从评论的角度来说,还是以好评为多的。不过经过打字员的亲身观剧经历来说,这部剧用来下饭还是很不错的。(逃

PS:需要脚本的童鞋在公众号后台回复哟。

非 nodejs 问题,这张图片如何找出哪个位置开始循环的? 我想切个背景图..

$
0
0

首先说声抱歉哈,这个问题或许在这是发帖有点不合适哈。但是刚进社区有点激动,还望谅解。 这个是整个页面的背景图,是循环的,但是我又不知道从哪里开始切,切到哪里?

demo.jpg

egg.js 必须要用模板引擎 才能渲染在view下的html文件吗?

$
0
0

本人刚学习接触egg.js 现在尝试自己写 暂时没找到怎么配置下能够直接访问view下的html文件。配了官方例子中的模板应该可以 但是我现在就是想知道不安装任何模板引擎是否也能访问到view下的html文件@atian25

【深圳|南山】编程猫招聘 Node.js 开发工程师

$
0
0

Hi,我们是编程猫,全球领先的儿童编程教育公司 这是一个有趣有爱的你才能看见的招募书 有趣是我们公司的灵魂,No fun go die是我们的slogan,为在这里工作的每一个人提供成就感和成长空间是我们管理的目标。

招募岗位:Node.js 工作职责: 1、使用 Node.js 语言进行相关后端接口的开发; 2、负责后端业务的开发及维护;

任职资格: 1、熟悉 Javascript(ES6, ES7),Node.js,了解 Javascript 面向对象的编程思想,继承,原型,闭包等; 2、对 Node.js 的异步回调有一定的了解,对async,await有一定的了解; 3、熟悉 express,koa 框架的使用,了解框架的实现原理; 4、熟悉 MySQL,MongoDB 等数据库的使用,有优化经验更佳; 5、熟悉 Redis 等常用缓存技术; 6、熟悉 Restful API,了解 Socket,Web Socket; 7、了解依赖注入原理; 8、了解Docker; 9、学习能力强,善于沟通,具有独立解决问题的能力。

薪资 12K——24K,能力强的可加可加可加+++

简历投递邮箱 hr@codemao.cn

福利 1、作为初创公司的我们,有一大堆国际牛人加入,如果你想成为一个伟大的人,那你就来对了地方。 2、水果、零食、饮料,这也算是创业公司标配; 3、这是一家女童鞋带着打LOL的公司。。。而且我们女生多。。。 4、作为创业公司的我们总是尽最大可能性保证休假时间; 5、定期公款吃吃喝喝,外出旅游团建; 6、有很多出国机会(不是华为那种出国,人家只去发达国家!); 7、图书随意购买; 8、期待是你成为这个初创公司的核心,公司得一枚大将,你少走10年弯路。 9、其他一些,不一一例举了就,sei用sei知道。

搞点图,这是近期的一些活动,感恩节金枪鱼大餐,万圣节cosplay,程序员节吃鸡大赛 合集.jpg还有三只喵星人,一起来撸猫啦 撸猫系列.jpg

编程猫宇宙中心地址:深圳南山区粤海街道中心路3331号中建钢构大厦 再放一次简历投递邮箱:hr@codemao.cn 我们编程猫宇宙中心见啦~~~

关于社区置顶的学习项目有个问题想要请教

$
0
0

新人刚入nodejs半个月,想跟着社区的学习项目学node,但才刚开始做就出bug o(╥﹏╥)o,希望社区里的大神能解答一下 这个是index.js 代码

app.locals.blog = {
  title: pkg.name,
  description: pkg.description
}
app.use(function (req, res, next) {
  res.locals.user = req.session.user
  res.locals.success = req.flash('success').toString()
  res.locals.error = req.flash('error').toString()
  next()
})

这个是header.ejs 的代码

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <title><%= blog.title %></title>
    <link rel="stylesheet" href="//cdn.bootcss.com/semantic-ui/2.1.8/semantic.min.css">
    <link rel="stylesheet" href="/css/style.css">
    <script src="//cdn.bootcss.com/jquery/1.11.3/jquery.min.js"></script>
    <script src="//cdn.bootcss.com/semantic-ui/2.1.8/semantic.min.js"></script>
  </head>
  <body>

明明是照着项目上面敲为什么还是报这个错 image.png希望能解答一下

【Webpack】2.四个核心概念及使用

$
0
0

【Webpack】1.入门及简单使用【Webpack】2.四个核心概念及使用【Webpack】3.多入口设置与 html-webpack-pugin 插件详解

webpack 中的四个核心概念 (Demo2 Source

  • Entry入口
  • Output输出
  • Loaders
  • Plugins插件

  webpack 中默认的配置文件名称是 webpack.config.js,因此我们需要在项目中创建如下文件结构:

.        
├── index.html            // 显示的页面
├── main.js              // webpack 入口 
├── webpack.config.js   //  webpack 中默认的配置文件
└── bundle.js          //  通过 webpack 命令生成的文件,无需创建

entry入口

  入口起点(entry point)指示 webpack应该使用哪个模块,来作为构建其内部依赖图的开始。进入入口起点后。 webpack会找出有哪些模块和库是入口起点(直接和间接)依赖的。

  可以在 webpack.config.js中 配置 entry属性,来指定一个入口或多个起点入口,代码如下:

    moudle.exports = {
        entry: './path/file.js'
    };

output输出

   output属性告诉 webpack在哪里输出它所创建的 bundles,以及如何命名这些文件。你可以通过在配置指定一个 output字段,来配置这些过程:

    const path = require('path');
    
    moudle.exports = {
        entry: './path/file.js',
        output:{
            path: path.resolve(__dirname,'dist'),
            filename: 'my-webpack.bundle.js'
        }
    }

  其中 output.path属性用于指定生成文件的路径,output.filename用于指定生成文件的名称。

Loaders

   Loaderswebpack能够去处理那些非 JavaScript文件(webpack自身只理解 JavaScript)。loader可以将所有类型的文件转换为 webpack能够处理的有效模块,然后可以利用 webpack的打包能力,对它们进行处理。

  本质上,webpack loader将所有类型的文件,转换为应用程序的依赖图可以直接引用模块。在更高层面上,在 webpack的配置中 loader有两个目标:

  1. 识别应该被对应的 loader进行转换的那些文件(使用 test属性)
  2. 转换这些文件,从而使其能够被添加到依赖图中(并且最终添加到 bundle中)(use属性)

  在开始下面的代码之前,我们需要安装 style-loadercss-loader

    $ npm install --save-dev style-loader css-loader

并在项目中创建 style.css样式文件:

    h1{ color: red; }

  然后在 webpack.config.js中输入以下代码:

    const path = require('path');
    
    module.export = {
        entry: './main.js',
        output: {
            path: path.resolve(__dirname,'dist'),
            filename: 'bundle.js'
        },
        module: {
            rules:[
                { 
                    test: /\.css$/, 
                    use: [
                        { loader: 'style-loader' },
                        { loader: 'css-loader' }
                    ]
                }
            ]
        }
    };

Plugins插件

   Loaders被用于转换某些类型的模块,而插件则可以用于执行范围更广的任务。插件的范围包括,从打包优化和压缩,一直到重新定义环境中的变量。

  想要使用一个插件,需要 require()它,然后把它添加到 Plugins数组中,多数插件可以通过选项自定义。也可以在一个配置文件中因为不同目的而多次使用同一个插件,这时需要通过使用 new操作符来创建它的实例。

  在开始下面的代码之前,我们需要安装 html-webpack-plugin插件:

    $ npm install html-webpack-plugin --save-dev

它可以简化HTML文件的创建,为您的webpack包提供服务。

  然后在 webpack.config.js中输入以下代码:

    const HtmlWebpackPlugin = require('html-webpack-plugin');
    const path = require('path');
    
    const config = {
        entry: './main.js',
        output: {
            path: path.resolve(__dirname,'dist'),
            filename: 'bundle.js'
        },
        module: {
            rules:[
                { 
                    test: /\.css$/, 
                    use: [
                        { loader: 'style-loader' },
                        { loader: 'css-loader' }
                    ]
                }
            ]
        },
        plugins: [
            new HtmlWebpackPlugin({ template: './index.html' })
        ]
    };
    
    module.exports = config;

运行与配置

   最后我们可以直接通过 webpack命令编译打包,如果想要在其命令后加入参数,可以通过配置 package.json文件中的 scripts属性:

    {
        scripts: {
            "build": "webpack --config webpack.config.js --progress --display-modules"
        }
    }

当然如果你想要更改默认的配置文件名称,可以将 --config后面的 webpack.config.js配置文件名改为你自定义的名称。

  通过以下命令执行:

    $ npm run build

JavaScript30秒, 从入门到放弃

$
0
0

有意思

最近很火的github上的库30-seconds-of-code,特别有意思,代码也很优雅。

  1. 能学es6
  2. 自己翻译,能学英语
  3. 代码很美,很优雅,美即正义
  4. 函数式表达,享受

arrayGcd

Calculates the greatest common denominator (gcd) of an array of numbers.

Use Array.reduce() and the gcd formula (uses recursion) to calculate the greatest common denominator of an array of numbers.

const arrayGcd = arr =>{
  const gcd = (x, y) => !y ? x : gcd(y, x % y);
  return arr.reduce((a,b) => gcd(a,b));
}
// arrayGcd([1,2,3,4,5]) -> 1
// arrayGcd([4,8,12]) -> 4

计算数组的最大公约数。

使用Array.reduce()gcd公式(使用递归)来计算一个数组的最大公约数。

➜  code cat arrayGcd.js
const arrayGcd = arr => {
    const gcd = (x, y) => !y ? x : gcd(y, x % y);
    return arr.reduce((a, b) => gcd(a, b));
}

console.log(arrayGcd([1, 2, 3, 4, 5]));
console.log(arrayGcd([4, 8, 12]));
➜  code node arrayGcd.js
1
4

gcd即欧几里德算法,具体不表,自查。这里用到了数组的reduce方法,相当简洁,reduce不太了解的话,看下mdn就明白。

arrayLcm

Calculates the lowest common multiple (lcm) of an array of numbers.

Use Array.reduce() and the lcm formula (uses recursion) to calculate the lowest common multiple of an array of numbers.

const arrayLcm = arr =>{
 const gcd = (x, y) => !y ? x : gcd(y, x % y);
 const lcm = (x, y) => (x*y)/gcd(x, y) 
 return arr.reduce((a,b) => lcm(a,b));
}
// arrayLcm([1,2,3,4,5]) -> 60
// arrayLcm([4,8,12]) -> 24

计算一个数组的最小公倍数。

使用Array.reduce()lcm公式(使用递归)来计算一个数组的最大公约数。

➜  code cat arrayLcm.js
const arrayLcm = arr => {
  const gcd = (x, y) => (!y ? x : gcd(y, x % y));
  const lcm = (x, y) => x * y / gcd(x, y);
  return arr.reduce((a, b) => lcm(a, b));
};

console.log(arrayLcm([1, 2, 3, 4, 5]));
console.log(arrayLcm([4, 8, 12]));
➜  code node arrayLcm.js
60
24

lcm算法用到了前面的gcd算法,关键点是两个数的最大公约数和最小公倍数的乘积正好就是这两个数的乘积。

arrayMax

Returns the maximum value in an array.

Use Math.max() combined with the spread operator (...) to get the maximum value in the array.

const arrayMax = arr => Math.max(...arr);
// arrayMax([10, 1, 5]) -> 10

返回数组中最大的值。

使用Math.max()ES6的扩展运算符返回数组中最大的值。

➜  code cat arrayMax.js
const arrayMax = arr => Math.max(...arr);

console.log(arrayMax([10, 1, 5]));
➜  code node arrayMax.js
10

实际上就是Math.max()干的事,没啥可说的了。

arrayMin

Returns the minimum value in an array.

Use Math.min() combined with the spread operator (...) to get the minimum value in the array.

const arrayMin = arr => Math.min(...arr);
// arrayMin([10, 1, 5]) -> 1

返回数组中最小的值。

使用Math.min()ES6的扩展运算符返回数组中最小的值。

➜  code cat arrayMin.js
const arrayMin = arr => Math.min(...arr);

console.log(arrayMin([10, 1, 5]));
➜  code node arrayMin.js
1

实际上就是Math.min()干的事,没啥可说的了。

chunk

Chunks an array into smaller arrays of a specified size.

Use Array.from() to create a new array, that fits the number of chunks that will be produced. Use Array.slice() to map each element of the new array to a chunk the length of size. If the original array can’t be split evenly, the final chunk will contain the remaining elements.

const chunk = (arr, size) =>
 Array.from({length: Math.ceil(arr.length / size)}, (v, i) => arr.slice(i * size, i * size + size));
// chunk([1,2,3,4,5], 2) -> [[1,2],[3,4],[5]]

按照给定的size将一个数组切分成含有size个数的更小数组块的数组。

使用Array.from()生产新的符合定义的数组。使用Array.slice()来截取指定size个元素组成新的数组块。如果原数组长度不能被size整除,最后的剩余的那些元素将归属于最后一个块。

➜  code cat chunk.js
const chunk = (arr, size) =>
  Array.from({ length: Math.ceil(arr.length / size) }, (v, i) =>
    arr.slice(i * size, i * size + size)
  );

console.log(chunk([1, 2, 3, 4, 5], 2));
➜  code node chunk.js
[ [ 1, 2 ], [ 3, 4 ], [ 5 ] ]

Array.from(arrayLike, mapFn, thisArg)这个方法呢,第一个参数是一个类数组或者可迭代的对象,第二个参数是一个应用在每一个数组元素上的方法,第三个参数就是改变this的指向了。通俗说就是指定谁是你的爸爸。

这里用了一个{ length: Math.ceil(arr.length / size) }迭代对象,length指定了迭代次数,即按照size分块后的数组长度,正好就是原数组长度除以size向上取整的值。向上取整就是为了满足不能完全整除的情况。比如5个元素按照2个一组进行分块,分了两组两个元素的,剩最后一个元素成了独立组,总长为3。

(v, i),由于迭代的时候数组在每一个位置上都是以undefined初始化的,所以v一直都是undefined

arr.slice(i * size, i * size + size)迭代过程中每次截取size个数的元素组成新数组。这里的i就是随着迭代变化,比如length是3,i就是0,1,2。

这里的迭代类似python里的range

➜  code python
Python 3.6.4 (default, Dec 23 2017, 10:37:40)
[GCC 4.2.1 Compatible Apple LLVM 9.0.0 (clang-900.0.39.2)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import math
>>> arr = [1,2,3,4,5]
>>> size = 2
>>> for i in range(math.ceil(len(arr) / size)):
...     print('index: ', i)
...
index:  0
index:  1
index:  2

compact

Removes falsey values from an array.

Use Array.filter() to filter out falsey values (false, null, 0, "", undefined, and NaN).

const compact = arr => arr.filter(Boolean);
// compact([0, 1, false, 2, '', 3, 'a', 'e'*23, NaN, 's', 34]) -> [ 1, 2, 3, 'a', 's', 34 ]

移除掉数组里falsey的元素。(这个falsey不太好翻译,不是错误的意思,而是该值布尔运算值为false的意思,我个人常用!!进行判断)。

使用Array.filter()falsenull0""undefinedNaN这些falsey过滤掉。

➜  code cat compact.js
const compact = arr => arr.filter(Boolean);

console.log(compact([0, 1, false, 2, "", 3, "a", "e" * 23, NaN, "s", 34]));
➜  code node compact.js
[ 1, 2, 3, 'a', 's', 34 ]

Array.prototype.filter()干的,没啥好说。

countOccurrences

Counts the occurrences of a value in an array.

Use Array.reduce() to increment a counter each time you encounter the specific value inside the array.

const countOccurrences = (arr, value) => arr.reduce((a, v) => v === value ? a + 1 : a + 0, 0);
// countOccurrences([1,1,2,1,2,3], 1) -> 3

统计一个元素在一个数组中出现的次数。

使用Array.reduce()在遍历过程中如果指定元素在数组中出现,则增加它的次数值,默认次数为0。

➜  code cat countOccurrences.js
const countOccurrences = (arr, value) =>
  arr.reduce((a, v) => (v === value ? a + 1 : a + 0), 0);

console.log(countOccurrences([1, 1, 2, 1, 2, 3], 1));
console.log(countOccurrences([1, 1, 2, 1, 2, 3], 5));
➜  code node countOccurrences.js
3
0

三元运算符(v === value ? a + 1 : a + 0)遍历过程中判断遍历数组值v是否严格等于指定值value,是,次数a+1;否,a+0

最后的一个逗号后面的0,是这个初始值,即a=0,这个懂reduce方法都知道,特别指出是,因为这个函数一定会有返回值,如果指定元素没有在数组中出现一次,返回值是0,所以必须得初始化为0

deepFlatten

Deep flattens an array.

Use recursion. Use Array.concat() with an empty array ([]) and the spread operator (...) to flatten an array. Recursively flatten each element that is an array.

const deepFlatten = arr => [].concat(...arr.map(v => Array.isArray(v) ? deepFlatten(v) : v));
// deepFlatten([1,[2],[[3],4],5]) -> [1,2,3,4,5]

深度摊平一个数组。

使用递归方法。结合Array.concat()、空数组[]ES6的扩展运算符来摊平一个数组,如果摊平的元素还是一个数组,就再递归运用该方法。

➜  code cat deepFlatten.js
const deepFlatten = arr =>
  [].concat(...arr.map(v => (Array.isArray(v) ? deepFlatten(v) : v)));

console.log(deepFlatten([1, [2], [[3], 4], 5]));
➜  code node deepFlatten.js
[ 1, 2, 3, 4, 5 ]

三元运算符(Array.isArray(v) ? deepFlatten(v) : v)判断v是否是一个数组,是,返回递归运用deepFlatten(v)后的值;否,直接返回v

[].concat(...arr.map(fn))用空数组把map运算产生的数组进行扩展运算值拼接成结果数组返回。

该方法是深度摊平方法,在很多时候还有特定的摊平一层的需求,underscore就有。实现的方法就是再加一个标志参数进行处理即可。具体不讲了。

应该会写一个系列,今天先写到这,明天继续。

个人翻译水平有限,欢迎大家在issues上批评指正。JavaScript30秒, 从入门到放弃博客地址:JavaScript30秒, 从入门到放弃微信公众号地址:JavaScript30秒, 从入门到放弃

JavaScript30秒, 从入门到放弃之Array(二)

$
0
0

difference

Returns the difference between two arrays.

Create a Set from b, then use Array.filter() on a to only keep values not contained in b.

const difference = (a, b) => { const s = new Set(b); return a.filter(x => !s.has(x)); };
// difference([1,2,3], [1,2,4]) -> [3]

返回两个数组的不同。

创建一个b数组的集合,然后使用Array.filter()a数组进行过滤,过滤出不存在于数组b的元素。

➜  code cat difference.js
const difference = (a, b) => {
    const s = new Set(b);
    return a.filter(x => !s.has(x));
}

console.log(difference([1, 2, 3], [1, 2, 4]));
➜  code node difference.js
[ 3 ]

关键点是主客体,这里主体应该是第一个数组,也就是a,客体是数组b,返回的是不在主体a里的数组元素。类似于集合的a - b,不同点是ab数组都可以有重复的元素存在,而集合不允许重复元素存在。

这逻辑是很清晰的,先把b数组转成集合存到s中,然后去filter数组a,只要把不存在于s集合中的元素返回即可。记住filter返回的是一个数组。

differenceWith

Filters out all values from an array for which the comparator function does not return true.

Use Array.filter() and Array.find() to find the appropriate values.

const differenceWith = (arr, val, comp) => arr.filter(a => !val.find(b => comp(a, b)))
// differenceWith([1, 1.2, 1.5, 3], [1.9, 3], (a,b) => Math.round(a) == Math.round(b)) -> [1, 1.2]

从一个数组中筛选出所有不满足指定比较方法运算结果为true的元素值的数组。

使用Array.filter()Array.find()来找出适当的值。

➜  code cat differenceWith.js
const differenceWith = (arr, val, comp) => arr.filter(a => !val.find(b => comp(a, b)));

console.log(differenceWith([1, 1.2, 1.5, 3], [1.9, 3], (a,b) => Math.round(a) == Math.round(b)));
➜  code node differenceWith.js
[ 1, 1.2 ]

difference类似,主客体还是第一个数组arr

我们可以先把意思进行拆分。

  1. comp运行结果为true
  2. 数组val.find()去寻找comp(a, b)(a是arr元素,b是val元素)运行结果为true的值
  3. arr不要上面第2点中运行结果为true的值

通俗点就是说去遍历数组arr的所有元素,然后在数组val里寻找comp运算结果不为true的值。因为val.find()方法如果找到就返回该值,否则返回undefined,此时!val.find()就是truearr.filter()正是需要这样运算结果的值。

distinctValuesOfArray

Returns all the distinct values of an array.

Use ES6 Set and the ...rest operator to discard all duplicated values.

const distinctValuesOfArray = arr => [...new Set(arr)];
// distinctValuesOfArray([1,2,2,3,4,4,5]) -> [1,2,3,4,5]

返回数组去重结果。

使用ES6的集合SetES6的扩展运算符把重复的元素排除掉。

➜  code cat distinctValuesOfArray.js
const distinctValuesOfArray = arr => [...new Set(arr)];

console.log(distinctValuesOfArray([1, 2, 2, 3, 4, 4, 5]));
➜  code node distinctValuesOfArray.js
[ 1, 2, 3, 4, 5 ]

实际上ES6的集合Set干的事,没啥可说。

dropElements

Removes elements in an array until the passed function returns true. Returns the remaining elements in the array.

Loop through the array, using Array.slice() to drop the first element of the array until the returned value from the function is true. Returns the remaining elements.

const dropElements = (arr, func) => {
  while (arr.length > 0 && !func(arr[0])) arr = arr.slice(1);
  return arr;
};
// dropElements([1, 2, 3, 4], n => n >= 3) -> [3,4]

剔除掉数组元素直到指定方法运算结果第一次为true为止。

循环一个数组,使用Array.slice每次去删除该数组的第一个元素直到指定方法运算结果为true,返回的是剩余元素组成的数组。

➜  code cat dropElements.js
const dropElements = (arr, func) => {
    while (arr.length > 0 && !func(arr[0])) arr = arr.slice(1);
    return arr;
};

console.log(dropElements([1, 2, 3, 4], n => n >= 3));
➜  code node dropElements.js
[ 3, 4 ]

这里用while进行循环,循环条件是数组的长度大于0并且数组的第一个元素按照指定方法运行结果为false,如果满足条件,使用arr.slice(1)arr第一个元素删除掉。直到while循环退出,返回此时的arr

这里的边界条件是数组长度为0,这时候就不进入while循环,直接返回空数组。

dropRight

Returns a new array with n elements removed from the right.

Use Array.slice() to slice the remove the specified number of elements from the right.

const dropRight = (arr, n = 1) => arr.slice(0, -n);
//dropRight([1,2,3]) -> [1,2]
//dropRight([1,2,3], 2) -> [1]
//dropRight([1,2,3], 42) -> []

返回数组从右边开始剔除掉n个元素后的数组。

使用Array.slice()切掉从右边开始计算的指定数目的元素。

➜  code cat dropRight.js
const dropRight = (arr, n = 1) => arr.slice(0, -n);

console.log(dropRight([1, 2, 3]));
console.log(dropRight([1, 2, 3], 2));
console.log(dropRight([1, 2, 3], 42));
➜  code node dropRight.js
[ 1, 2 ]
[ 1 ]
[]

n的默认值是1,所以不传第二个参数的时候会删掉数组的最后一个元素。

-n不好理解吗?变换一下就好了arr.slice(0, -n)arr.slice(0, arr.length + (-n))是一样的。

slice(m, n)对应就是[m, n),包含下界,不包含上届。

everyNth

Returns every nth element in an array.

Use Array.filter() to create a new array that contains every nth element of a given array.

const everyNth = (arr, nth) => arr.filter((e, i) => i % nth === nth - 1);
// everyNth([1,2,3,4,5,6], 2) -> [ 2, 4, 6 ]

返回一个新的数组,数组包含每nth的元素,即nth倍数的元素。

使用Array.filter()创建一个包含nth倍数元素的新数组。

➜  code cat everyNth.js
const everyNth = (arr, nth) => arr.filter((e, i) => i % nth === nth - 1);

console.log(everyNth([1, 2, 3, 4, 5, 6], 2));
➜  code node everyNth.js
[ 2, 4, 6 ]

判断是否nth倍数只需要知道该元素的索引加1后能不能被nth整除即可。

如果是我的话我会这么写(e, i) => (i + 1) % nth === 0,可能这样比较符合我的思维习惯。

filterNonUnique

Filters out the non-unique values in an array.

Use Array.filter() for an array containing only the unique values.

const filterNonUnique = arr => arr.filter(i => arr.indexOf(i) === arr.lastIndexOf(i));
// filterNonUnique([1,2,2,3,4,4,5]) -> [1,3,5]

过滤掉不唯一元素后返回的数组。

使用Array.filter()去筛选满足数组元素唯一性的元素。

➜  code cat filterNonUnique.js
const filterNonUnique = arr => arr.filter(i => arr.indexOf(i) === arr.lastIndexOf(i));

console.log(filterNonUnique([1, 2, 2, 3, 4, 4, 5]));
➜  code node filterNonUnique.js
[ 1, 3, 5 ]

方法用得很巧,一个数如果出现在一个数组超过一次,那么该数在数组中的左索引indexOf(从左边数第一次出现该数的索引)和右索引lastIndexOf(从右边数第一次出现该数的索引)一定不相等。

反过来说左索引等于右索引,该数在数组中只出现一次,满足唯一性。

这里和distinctValuesOfArray的区别是,distinctValuesOfArray删掉了重复的元素,只留一个;filterNonUnique删掉了所有重复元素,一个不留。

flatten

Flattens an array.

Use a new array and concatenate it with the spread input array causing a shallow denesting of any contained arrays.

const flatten = arr => [ ].concat( ...arr );
// flatten([1,[2],3,4]) -> [1,2,3,4]

摊平一个数组。

Array.concat()、空数组[]ES6的扩展运算符来摊平一个数组。这里是浅度摊平,即只摊平一层。

➜  code cat flatten.js
const flatten = arr => [].concat(...arr);

console.log(flatten([1, [2], 3, 4]));
console.log(flatten([1, [[2], 5], 3, 4]));
➜  code node flatten.js
[ 1, 2, 3, 4 ]
[ 1, [ 2 ], 5, 3, 4 ]

主要是用[]ES6的扩展运算符arr运算结果concat连接起来。

deepFlatten的区别就是flatten只摊平一层,deepFlatten深度摊平。

个人翻译水平有限,欢迎大家在issues上批评指正。JavaScript30秒, 从入门到放弃之Array(二)微信公众号:JavaScript30秒, 从入门到放弃之Array(二)

EJS模板,有办法实现 include 中嵌套 include 吗?

$
0
0

觉得 ejs 比较 接近之前用的 像php 的smarty 这种,比较简单,但是没法嵌套

翻墙:centos7 autossh永久通道

$
0
0

ssh通道的缺点就是每隔一段时间会断开一次,无法永久有效。而autossh正好解决了这一问题。

修改ssh配置

$ sudo vi /etc/ssh/sshd_config

_打开保持tcp连接设置

TCPKeepAlive yes

重启ssh

$sudo systemctl restart sshd.service 

autossh命令

安装autossh

$ sudo yum install autossh

autossh执行命令

autossh -M 5678 -NR 8888:localhost:80 root@huzhifeng.com

-M 5678: 通过5678端口监视连接状态,连接有问题时就会自动重连 -N:不允许执行远程命令,只做端口转发 Do not execute a remote command. This is useful for just forwarding ports (protocol version 2 only). -R 8888:localhost:80: 将 B 主机的80端口和 VPS 的8888端口绑定,相当于远程端口映射

将autossh通道做出开机自启服务

#cd /usr/lib/systemd/system
#touch autosshTest.service
#vi autosshTest.service

在autosshTest.service中新建以下内容

[Unit]
Description=autosshTest.service
After=network.target remote-fs.target nss-lookup.target

[Service]
Type=simple
PIDFile=/run/autosshTest.pid
ExecStart=/usr/bin/autossh -M 5678 -NR 8888:localhost:80 root@huzhifeng.com
Restart=/bin/kill -s QUIT $MAINPID && /usr/bin/autossh -M 5678 -NR 8888:localhost:80 root@huzhifeng.com
ExecReload=
ExecStop=/bin/kill -s QUIT $MAINPID
PrivateTmp=true
RemainAfterExit=yes
ExecStartPre=

[Install]
WantedBy=multi-user.target

service文件配置可以参考systemd.service 中文手册

运行命令使服务开机自启

sudo systemctl enable autosshTest.service

启动服务

systemctl start autosshTest

翻墙

设置浏览器代理端口为你设置的端口(8888)就行,浏览器代理,软件代理方式有很多我就不说了

关于sequelize中include的问题

$
0
0

sequelize使用hasOne和belongsTo来进行外键关联的时候,对于使用复合主键的表,默认使用主表define的第一个属性作为外键关联的,请问要怎么改可以自定义表之间的外键关联。

Viewing all 14821 articles
Browse latest View live