花了几天时间撸了一个个人博客

撸了一个自己的个人博客一直是想写一个自己的博客,最近有点时间,花费了几天就撸了一个,雏形已经有了,后续完善内容,优化功能,有很多地方还没来的及做处理,后续继续优化。自己能力有限,有些地方处理的不好,希望大家能够给予指正,以后自己的博客也会同步到这个网站上,下面进入正题。 github地址网站地址 ## 前端页面的文档 页面的话,直接使用vue-cli生成项目,安装相应依赖包,运行项目,该项目中安装的依赖包包含如下: vuex(数据管理)element-ui(ui库)axios(接口请求)sass(css预处理器)使用步骤// 克隆项目git clone git@github.com:dragonnahs/new_websit_blog.git// 进入前端页面目录cd baozi_blog// 安装依赖npm install // 运行项目npm run dev// 打包项目npm run build这里没有一步步讲实现,主要的地方大致说下,怎么实现的,挺简单的,可以先clone下来运行一遍试下,依赖于后台接口,所以尽量吧后台先跑起来。下面主要记录一些写代码过程中一些技术点。 markdown格式解析和高亮需要用到的库marked和highlight.js这两个,在博客详情页面使用, // tempalte(v-highlight时后续加的自定义指令,下面会说)<div class="markdown" v-html="compiledMarkdown" v-highlight></div>// script// 引入和初始化let marked = require('marked')let hljs = require('highlight.js')import 'highlight.js/styles/default.css'marked.setOptions({ renderer: new marked.Renderer(), gfm: true, tables: true, breaks: false, pedantic: false, sanitize: false, smartLists: true, smartypants: false, highlight: function (code, lang) { if (lang && hljs.getLanguage(lang)) { return hljs.highlight(lang, code, true).value; } else { return hljs.highlightAuto(code).value; } }})export default{ data () { return { blogInfo: {} } }, computed: { // 解析代码 compiledMarkdown(){ return marked(this.blogInfo.content || '', { sanitize: true }) } }}// 到这一步已经能够在页面看到解析完成之后的内容了,但是没有高亮样式,原因是vue-router在切换路由的时候会移除hight的事件,解决办法是自定义一个指令重新给代码加上对应的类名,实现代码高亮,具体代码如下// main.jslet hljs = require('highlight.js')// 自定义代码高亮指令Vue.directive('highlight',function (el) { let blocks = el.querySelectorAll('pre code'); blocks.forEach((block)=>{ hljs.highlightBlock(block) })})// 到此已经完美的解决了markdown的解析以及代码的高亮问题。登录验证既然涉及到博客管理后台,就肯定有登录验证的问题,使用的是token去验证权限。前端页面前端的路由守护,需要使用router.beforeEach方法,对每一个路由去进行判断,验证该路由是否需要登录权限,如果需要登录权限的话,就通过存储在本地的token去获取用户信息,得到用户信息,继续当前的路由跳转,不能的话就跳转到登录页去登录。这里的token是登录的时候后台给返回的,后台设置过期时间,登录后保存到本地session中 ...

July 12, 2019 · 5 min · jiezi

Fastfiy基础指南

什么是 Fastify?Fastify 是一个高度专注于以最少开销和强大的插件架构,为开发人员提供最佳体验的 Web 框架。 它受到了 Hapi 和 Express 的启发,是目前最快的 Node 框架之一。 Fastify 独特的将 JSON Schema 应用到请求时的 validation 和响应时的 serialization, 作者写的 fast-json-stringify 包更是达到了2x faster than JSON.stringify的神奇效果。 为什么要使用 Fastify100% 异步:框架的核心都是用异步代码实现的高性能:每秒可以提供34000个请求可扩展:Fastify 通过其钩子,插件和装饰器完全可扩展基于模式:即使不是强制性的,我们建议使用 JSON Schema 来验证路由并序列化输出日志记录:日志非常重要,但成本高昂,我们选择了最好的记录器 Pino对开发者友好:该框架构建非常有表现力,不会牺牲性能和安全性如何安装fastify?使用 npm 安装:npm i fastify --save使用 yarn 安装:yarn add fastify脚手架安装全局安装npm i fastify-cli -g进入目录cd [myproject]初始化Fastify脚手架fastify generate运行npm start如何创建一个简单的 Fastify 应用?声明一个监听客户端http://127.0.0.1:3000/的「GET」请求 Fastify返回 { hello: 'world' }。 // 加载框架并新建实例const fastify = require('fastify')({ // 开始日志记录 logger: true})// 声明路由fastify.get('/', function(request, reply) { reply.send({ hello: 'world' })})// 启动服务!fastify.listen(3000, function(err, address) { if (err) { fastify.log.error(err) process.exit(1) } fastify.log.info(`server listening on ${address}`)})-n- ...

July 12, 2019 · 1 min · jiezi

关于Nodejs的事件订阅发布

一、Node的事件订阅发布1.EventEmitterNode中很多模块都能够使用EventEmitter,有了EventEmitter才能方便的进行事件的监听。下面看一下Node.js中的EventEmitter如何使用。 (1) 基本使用EventEmitter是对事件触发和事件监听功能的封装,在node.js中的event模块中,event模块只有一个对象就是EventEmitter,下面是一个最基本的使用方法: var EventEmitter = require('events').EventEmitter; var event = new EventEmitter(); event.on('some_event', function() { console.log('some_event 事件触发'); }); setTimeout(function() { event.emit('some_event'); }, 1000); 上面的代码中首先实例化了一个EventEimitter对象,然后就可以进行事件的监听以及发布。通过on方法对特定的事件进行监听,通过emit方法对事件进行发布。在1s后发布一个"some_event"事件,这个时候就会自动被event对象通过on进行监听,并触发对应的回调方法。 (2)EventEmitter支持的方法 EventEmitter实例对象支持的方法列表如下:emitter.on(name, f) //对事件name指定监听函数femitter.once(name, f) //与on方法类似,但是监听函数f是一次性的,使用后自动移除emitter.listeners(name) //返回一个数组,成员是事件name所有监听函数emitter.removeListener(name, f) //移除事件name的监听函数femitter.removeAllListeners(name) //移除事件name的所有监听函数......同时,事件的发布emit方法可以传入多个参数,第一个参数是定义的事件,后面其他参数回作为参数传递到监听器的回调函数中。 事件的监听以及发布

July 11, 2019 · 1 min · jiezi

echarts-游客来源

//originLines.js let locatePoint = [ { name: "北京",position: [116.407526, 39.904030]}, { name: "辽宁",position: [123.429440, 41.835441]}, { name: "上海",position: [121.473701, 31.230416]}, { name: "河北",position: [114.468664, 38.037057]}, { name: "浙江",position: [120.152791, 30.267446]}, { name: "广东",position: [113.266530, 23.132191]}, { name: "山西",position: [112.562398, 37.873531]}, { name: "天津",position: [117.200983, 39.084158]}, { name: "吉林",position: [125.325990, 43.896536]}, { name: "陕西",position: [108.954239, 34.265472]}, { name: "丽江",position: [100.233026,26.872108]},] let option = { color:['#14f7fe'], geo:{ map:'china'}, series:[ { type:"lines", coordinateSystem:"geo", effect:{ period: 6, show: true, symbolSize: 3, trailLength: 0.7, }, lineStyle:{ normal:{ color:"#14f2fb", curveness: 0.2, opacity: 0.6, width: 0 } }, data:[]},{ type:"lines", coordinateSystem:"geo", effect:{ period: 6, show: true, symbolSize: 15, trailLength: 0, symbol: "path://M1705.06,1318.313v-89.254l-319.9-221.799l0.073-208.063c0.521-84.662-26.629-121.796-63.961-121.491c-37.332-0.305-64.482,36.829-63.961,121.491l0.073,208.063l-319.9,221.799v89.254l330.343-157.288l12.238,241.308l-134.449,92.931l0.531,42.034l175.125-42.917l175.125,42.917l0.531-42.034l-134.449-92.931l12.238-241.308L1705.06,1318.313z", }, lineStyle:{ normal:{ color:"#14f2fb", curveness: 0.2, opacity: 0.6, width: 1 } }, data:[]},{ type:"effectScatter", coordinateSystem:"geo", symbolSize:6, label:{ normal:{ formatter:"{b}", show:true, position:"right" } }, itemStyle:{ color:"#14f2fb" }, data:[]},]} ...

July 11, 2019 · 2 min · jiezi

构建一个用于创建组件库的项目脚手架工具类-Vuecli3

缘起最近公司内部想搭建一个私有的 npm 仓库,用于将平时用到次数相当频繁的工具或者组件独立出来,方便单独管理,随着项目的规模变大,数量变多,单纯的复制粘粘无疑在优雅以及实用性上都无法满足我们的需求,所以进一步模块化是必然的。 但是一个组件库的建立其实是一个非常麻烦的过程,基础 webpack 的配置不用多说,接着你还要配合增加一些 es-lint 之类的工具来规范化团队成员的代码。在开发过程中,你自然需要一个目录来承载使用示例,方便 dev 这个组件,随后呢,你还得建立一个打包规范,发布到私有 npm 仓库中。 如此一来,必然大大降低我们的积极性,所以不如创建一个用于建立模块包的脚手架工具,方便我们项目的初始化。 tips:最终成品在底部 私有 NPM这里简单提及一下 私有 npm 的搭建。 npm i verdaccio -gpm2 start verdaccio推荐配合 nrm 使用 快速切换仓库地址 verdaccio github 还整个意大利名,属实洋气。 工具在进入正题之前,我先介绍一些要点和工具,有了这写关键点,写起来其实就相当简单了。 npm bin大家有没有想过一些全局安装的工具,他是如何做到在命令行里面自由调用的呢? 事实上这个东西是 npm 提供的链接功能 // package.json{ "name": "lucky-for-you", "bin": { "lucky": "bin/lucky" }}当这样的一个模块被发布之后,一旦有人使用 -g 参数全局安装 sudo npm i luck-for-you -g/usr/local/bin/lucky -> /usr/local/lib/node_modules/luckytiger-package-cli/bin/lucky # npm 帮你进行链接 npm 事实上会帮你进行一次链接,链接到你操作系统的 Path 之中,从而但你敲出 Lucky 这个命令的时候,能从 path 中成功找到对应的程序 ...

July 11, 2019 · 5 min · jiezi

es6-7个比较有用的技巧

数组去重var arr = [1, 2, 3, 3, 4];console.log(...new Set(arr))>> [1, 2, 3, 4]数组和布尔有时我们需要过滤数组中值为 false 的值. 例如(0, undefined, null, false), 你可能不知道这样的技巧 var myArray = [1, 0 , undefined, null, false];myArray.filter(Boolean);> > [1]//是不是很简单, 只需要传入一个 Boolean 函数即可.创建一个空对象有时我们需要创建一个纯净的对象, 不包含什么原型链等等. 一般创建空对象最直接方式通过字面量 {}, 但这个对象中依然存在 proto 属性来指向 Object.prototype 等等. let dict = Object.create(null); dict.__proto__ === "undefined" 合并对象在JavaScript中合并多个对象的需求一直存在, 比如在传参时需要把表单参数和分页参数进行合并后在传递给后端 const page = { current: 1, pageSize: 10} const form = { name: "", sex: ""} const params = {...form, ...page}; /* { name: "", sex: "", current: 1, pageSize: 10 }*利用ES6提供的扩展运算符让对象合并变得很简单. ...

July 11, 2019 · 1 min · jiezi

从零到一实现前后端分离项目的gitlabci流程

?通过一周的尝试, 终于从0到1把gitlab-ci弄好了, 彻底抛弃travis-ci, 最大的坑还是墙外的东西太慢了, 总是timeout 整个过程分为如下几步: 如何在一个1核2G的云服务器上搭建gitlab: 十分钟搭建Gitlab使用gitlab-runner, 并选择正确的executor如何构建前端镜像如何构建后端镜像编写gitlab-ci.yml, 实现一个完整的前端后分离项目的构建部署1. 使用gitlab-runnergitlab-runner跟gitlab-ci是连体婴, 主要为gitlab-ci打工, 使用镜像的安装方式如下: docker run -d --name gitlab-runner --restart always \ -v /srv/gitlab-runner/config:/etc/gitlab-runner \ -v /var/run/docker.sock:/var/run/docker.sock \ gitlab/gitlab-runner:latest其中挂载卷/srv/gitlab-runner/config/config.toml包含了所有runner的配置信息. 通过挂载/var/run/docker.sock:/var/run/docker.sock,使得容器中的进程可以通过它与Docker守护进程通信 1.1 选择Docker作为runner的executor在启动了gitlab-runner容器后, 执行如下命令进入容器, 注册runner docker exec -it gitlab-runner /bin/bashroot@492ce6ab72f9:/# gitlab-runner register接下来需要填写的信息如下: Please enter the gitlab-ci coordinator URL:你的Gitlab地址: http(s)://gitlab.xxx.comPlease enter the gitlab-ci token for this runner:你的Gitlab admin/runners页面中的tokenPlease enter the gitlab-ci description for this runner:填写描述, 无关紧要Please enter the gitlab-ci tags for this runner (comma separated):填写标签, 没有标签谁都可以用, 是shared-runner, 有标签需要声明才可用, 回车就对了Please enter the executor: docker-ssh, ssh, docker+machine, kubernetes, docker-ssh+machine, docker, parallels, shell, virtualbox:选择你的executor: Docker应该是我观察到最常用的吧Please enter the default Docker image (e.g. ruby:2.6):选择一个默认镜像: 例如 docker:stable-alpine不出意外, 就能在gitlab中看到了 ...

July 11, 2019 · 3 min · jiezi

Nodejs-入门你需要知道的-10-个问题

本文为您分享「Node.js 入门你需要知道的 10 个问题」这些问题可能也是面试中会被问到的,当然问题不仅仅是这 10 道,因此,最近开源了一个新项目 Nodejs-Interview-Questions 专注于 Node.js 面试题的分享,提供了中英文版本,您也可以在线预览: https://interview.nodejs.red/ Q1: 什么是 Node.js?Node.js 是一个基于 Chrome V8 引擎的 JavaScript 运行环境。它是一个开源和跨平台的服务端应用程序。任何人都可以编写 JavaScript 代码来开发 Node.js 应用程序。它可以运行于 Microsoft Windows、Linux、 或 OS 系统。 Node.js 不是一个新的语言,也不仅仅是一个基于 JavaScript 的框架,它基于 Chrome 的 JavaScript 运行时,因此代码的编写和执行与浏览器非常相似。 Node.js 功能 以下是 Node.js 的一些重要功能 高度可扩展Node.js 使用的单线程模型且采用了事件循环架构,使得编写可扩展性高的服务器变得既容易又安全。一些传统的服务端语言会创建多线程来处理请求,通常创建线程都是有系统资源开销的,因此也会有一些限制,而 Node.js 只创建一个线程来处理更多的请求。 事件驱动和异步Node.js 的所有 API 都是异步的。这意味着下一个请求来临时可以直接处理而不用等待上一次的请求结果先返回。 No BufferingNode.js 从不缓冲任何任何数据,参见What is No-Buffering feature of Node.js 我们许多人可能会对 Node.js 感到困惑。它不是像 Apache 这样的 Web 服务器。Node.js 提供了一种新方法来执行我们的代码。它是 JavaScript 的运行时。Node.js 提供了创建 HTTP 服务器的方法,我们可以在这之上托管我们的应用程序。 ...

July 11, 2019 · 3 min · jiezi

webpack踩坑系列之基础篇

必备基础工欲善其事,必先利其器,学习webpack之前,要先了解一些关于node以及npm的基础知识。安装nodejs安装nodejs只需要去nodejs官网(https://nodejs.org/en/)下载.msi安装文件,根据提示点击下一步即可完成安装。安装完成后,打开命令行,执行node -v,若出现版本号,说明安装成功。 利用npm初始化项目npm为nodejs包管理器,安装nodejs是自动安装,因此无需额外安装1.新建文件夹test-npm,进入test-npm文件夹(以下简称根目录),打开命令行执行npm init -y即完成了项目的初始化,执行完成后在test-npm文件夹下会生成package.json文件;package.json定义了test-npm`这个项目所需要的各种模块,以及项目的配置信息,详细内容如下: { "name": "test-npm", // 项目名称 "version": "1.0.0", // 版本号 "description": "", // 项目描述 "main": "index.js", // 入口文件,当执行require('test-npm')时会执行index.js的代码 "scripts": { // 当执行`npm run xxx`时会执行此处的命令,后面详细介绍 "test": "echo \"Error: no test specified\" && exit 1" }, "keywords": [], // 关键字 "author": "", // 作者 "license": "ISC" // 项目许可证 }在根目录下新建index.js,在index.js中输入console.log(111)。修改package.json文件,在scripts属性末尾添加"dev": "node index.js",修改后package.json文件的内容如下: { "name": "test-npm", "version": "1.0.0", "description": "", "main": "index.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1", + "dev": "node index.js" }, "keywords": [], "author": "", "license": "ISC" } 打开命令行,执行npm run dev,此时控制台会打印111。安装webpack打开控制台,在根目录下执行npm install webpack webpack-cli -D,npm install xxx后面可以跟--save(简写-S)或者--save-dev(简写-D),其中-S代表在线上环境会用到的模块,-D代表本地开发需要依赖的模块,不需要在线上运行。因为webpack只需要在本地运行,所以执行-S。同理,当我们需要的模块要在线上运行,那么需要执行-S,例如:执行npm install express这里的express需要在线上运行,所以执行-S;等待安装完成,package.json文件内容如下: ...

July 10, 2019 · 1 min · jiezi

华为鸿蒙基于旗鱼OS开发-我还认识一个旗鱼云梯

据了解近期关于华为鸿蒙操作系统的消息越来越多,而且细节也越来越丰富。不过外媒的一则消息却表示华为鸿蒙操作系统并不是从零开始的。俄罗斯一家媒体表示,华为鸿蒙操作系统是基于旗鱼 OS进行开发的,之所以能够兼容安卓,也正是这个原因。 旗鱼 OS是诺基亚放弃MeeGo的继承者,基于MeeGo开发出旗鱼 OS。在华为正式公布鸿蒙操作系统的细节之前,还无法确定鸿蒙操作系统是完全自研还是基于已有成果之上。不过从前段时间的信息来看,鸿蒙完全自研的可能性是比较大的。另一个我知道的旗鱼云梯是建站平台,可以再linux系统上一键建站,帮助企业和站长实现一键建站能力.旗鱼云梯是批量化linux服务器集群管理平台,可以快速搭建web环境,让用户最快的建立网站.都是旗鱼os系统,旗鱼云梯你咋这么优秀呢.作为linux面板建站来说.

July 10, 2019 · 1 min · jiezi

事件订阅广播组件

export default class { // 事件栈 eventStacks = [{ eventType: '', handlers: [] }]; /** * 获取事件对应栈的索引 * * @param {string} eventType 事件类型 * @return {number} stackIndex 对应栈的索引 不存在为-1 */ indexOf(eventType) { const eventStacks = this.eventStacks; // 已有事件类型处理栈 let stackIndex = -1; for (let i = 0; i < eventStacks.length; i++) { const eventStack = eventStacks[i]; if (eventStack.eventType === eventType) { stackIndex = i; break; } } return stackIndex; }; /** * 监听事件 * * @param {string} eventType 自定义事件类型 * @param {Function} handler 事件处理函数 */ on(eventType, handler) { const index = this.indexOf(eventType); if (index >= 0) { // 已存在事件类型处理 直接把相应的处理函数入栈 this.eventStacks[index].handlers.push(handler); } else { // 不存在事件,把对应的事件处理入栈 const newEventStack = { eventType, handlers: [handler] }; this.eventStacks.push(newEventStack); } }; /** * 触发对应的事件 * * @param {string} eventType 自定义事件类型 * @param {Object} params 参数对象 */ emit(eventType, params = {}) { this.execEvent(eventType, params); }; /** * 执行对应的事件 * * @param {string} eventType 自定义事件类型 * @param {Object} params 参数对象 */ execEvent(eventType, params = {}) { const index = this.indexOf(eventType); if (index < 0) { return; } const handlers = this.eventStacks[index].handlers; for (let i = 0; i < handlers.length; i++) { const currentHandler = handlers[i]; if (currentHandler && typeof currentHandler === 'function') { currentHandler(params); } } }; /** * 解除对应的事件 * * @param {string} eventType 事件类型 * @param {Function} handler 事件处理器 必须是引用传进来 使用对象引用相等判断 */ offHandler(eventType, handler) { const index = this.indexOf(eventType); if (index >= 0 && this.eventStacks[index].handlers.length) { // 存在,并且已经入栈 const handlers = this.eventStacks[index].handlers; this.eventStacks[index].handlers = handlers.filter(currentHandler => { return currentHandler !== handler }); } }}

July 10, 2019 · 2 min · jiezi

大文件上传

大文件上传的实现准备阶段:大文件上传的过程中需要用到发布订阅模式监听上传的进度 发布订阅模式的实现 export default class { // 事件栈 eventStacks = [{ eventType: '', handlers: [] }]; /** * 获取事件对应栈的索引 * * @param {string} eventType 事件类型 * @return {number} stackIndex 对应栈的索引 不存在为-1 */ indexOf(eventType) { const eventStacks = this.eventStacks; // 已有事件类型处理栈 let stackIndex = -1; for (let i = 0; i < eventStacks.length; i++) { const eventStack = eventStacks[i]; if (eventStack.eventType === eventType) { stackIndex = i; break; } } return stackIndex; }; /** * 监听事件 * * @param {string} eventType 自定义事件类型 * @param {Function} handler 事件处理函数 */ on(eventType, handler) { const index = this.indexOf(eventType); if (index >= 0) { // 已存在事件类型处理 直接把相应的处理函数入栈 this.eventStacks[index].handlers.push(handler); } else { // 不存在事件,把对应的事件处理入栈 const newEventStack = { eventType, handlers: [handler] }; this.eventStacks.push(newEventStack); } }; /** * 触发对应的事件 * * @param {string} eventType 自定义事件类型 * @param {Object} params 参数对象 */ emit(eventType, params = {}) { this.execEvent(eventType, params); }; /** * 执行对应的事件 * * @param {string} eventType 自定义事件类型 * @param {Object} params 参数对象 */ execEvent(eventType, params = {}) { const index = this.indexOf(eventType); if (index < 0) { return; } const handlers = this.eventStacks[index].handlers; for (let i = 0; i < handlers.length; i++) { const currentHandler = handlers[i]; if (currentHandler && typeof currentHandler === 'function') { currentHandler(params); } } }; /** * 解除对应的事件 * * @param {string} eventType 事件类型 * @param {Function} handler 事件处理器 必须是引用传进来 使用对象引用相等判断 */ offHandler(eventType, handler) { const index = this.indexOf(eventType); if (index >= 0 && this.eventStacks[index].handlers.length) { // 存在,并且已经入栈 const handlers = this.eventStacks[index].handlers; this.eventStacks[index].handlers = handlers.filter(currentHandler => { return currentHandler !== handler }); } }}文件是基于Blob BlobMDN文档Blob 类型可以使用slice截取一段 基于这点 我们就可以吧大文件拆分成很多小的文件块上传 前端实现如下: ...

July 10, 2019 · 3 min · jiezi

webpack4详细教程从无到有搭建react脚手架四

相关链接 webpack4详细教程,从无到有搭建react脚手架(一)webpack4详细教程,从无到有搭建react脚手架(二)webpack4详细教程,从无到有搭建react脚手架(三)管理打包后目录结构打包结构如下 build/ js/ xxx.js css/ xxx.css images/ xxx.jpg index.html修改配置config/webpack.common.js function webpackCommonConfigCreator(options){ ... return { ... output: { - filename: "bundle.js", + filename: "js/bundle.js", path: path.resolve(__dirname, "../build"), }, module: { rules: [ ... { test: /\.(jpg|png|svg|gif)$/, use: [ { loader: 'url-loader', options: { limit: 10240, - name: '[hash].[ext]', + name: 'images/[hash].[ext]', } }, ] }, ] }, plugins: [ ... new ExtractTextPlugin({ - filename: "[name][hash].css" + filename: "css/[name][hash].css" }), ] } }通过相对output目录对资源命名前加上目录名 ...

July 9, 2019 · 2 min · jiezi

webpack4详细教程从无到有搭建react脚手架三

Css安装loaderyarn add style-loader css-loader -D配置config/webpack.common.js ... function webpackCommonConfigCreator(options){ ... return { ... module: { rules: [ ... + { + test: /\.css$/, + include: path.resolve(__dirname, '../src'), + use: ["style-loader", "css-loader"] + } ] }, ... } }创建src/app.css src/app.css .text{ font-size: 20px; color: brown; }src/App.js + import './app.css'; function App(){ return ( - <div> + <div className="text"> hello react </div> ) } ...yarn start, 效果: Scss安装loader yarn add sass-loader node-sass -D配置loaderconfig/webpack.common.js ... function webpackCommonConfigCreator(options){ ... return { ... module: { rules: [ ... { - test: /\.css/, + test: /\.(css|scss)$/, include: path.resolve(__dirname, '../src'), - use: ["style-loader", "css-loader"] + use: ["style-loader", "css-loader", "sass-loader"] } ] }, ... } }创建src/app.scss ...

July 9, 2019 · 3 min · jiezi

webpack4详细教程从无到有搭建react脚手架二

相关链接 webpack4详细教程,从无到有搭建react脚手架(一)配置插件 clean-webpack-plugin、 html-webpack-plugin, 这两个插件基本上是必配的了介绍clean-webpack-plugin - 每次打包时清理上次打包生成的目录 官网指南关于这个插件部分内容已经过时没有更新,按照官网配置会出错,所以参考github上这个插件文档来配置, 文档地址: https://github.com/johnagan/c... html-webpack-plugin - 生成打包文件中的 index.html安装yarn add clean-webpack-plugin html-webpack-plugin -D这两个插件在两种模式下都要用到,所以配置在common.jsconfig/webpack.common.js ... + const HtmlWebpackPlugin = require('html-webpack-plugin'); + const { CleanWebpackPlugin } = require('clean-webpack-plugin'); function webpackCommonConfigCreator(options){ ... return { ... plugins: [ + new HtmlWebpackPlugin(), + new CleanWebpackPlugin({ + cleanOnceBeforeBuildPatterns: [path.resolve(process.cwd(), "build/"), path.resolve(process.cwd(), "dist/")] + }), ] } } ...更改开发代码,在页面上插入一个元素src/index.js var ele = document.createElement('div'); ele.innerHTML = "hello webpack"; document.body.appendChild(ele);效果yarn start, 效果: ...

July 9, 2019 · 2 min · jiezi

webpack4详细教程从无到有搭建react脚手架一

webpack 是一个现代 JavaScript 应用程序的静态模块打包器,前端模块化的基础。作为一个前端工程师(切图仔),非常有必要学习。 webpack官网的文档非常的棒,中文文档也非常给力,可以媲美vue的文档。建议先看概念篇章,再看指南,然后看API和配置总览。看完指南教程后,需要自主动手练习才能更加有影响,这里记录下我搭建react开发环境的过程 准备工作安装webpackyarn add webpack webpack-cli -D创建配置目录结构config webpack.common.js webpack.dev.js webpack.prod.jsscripts build.js // 构建模式脚本 start.js // 开发模式脚本src index.jspackage.json先尝试一个简单配置配置启动脚本命令package.json..."license": "MIT",+ "scripts": {+ "start": "node ./scripts/start.js",+ "build": "node ./scripts/build.js"+ },"devDependencies": { "webpack": "^4.35.2", "webpack-cli": "^3.3.5"}...编写webpack配置, 以 src/index.js 作为主入口,以 build为打包后的目录config/webpack.common.jsoutput path字段这里配置的绝对路径 const path = require('path'); module.exports = { entry: "./src/index.js", output: { path: path.resolve(__dirname, "../build"), filename: "bundle.js" } }编写开发模式运行脚本scripts/build.js const webpack = require('webpack'); const webpackConfig = require('../config/webpack.common.js'); webpack(webpackConfig, (err, stats) => { if(err || stats.hasErrors()){ console.log("编译失败"); } });webpack node接口文档: https://www.webpackjs.com/api... ...

July 9, 2019 · 2 min · jiezi

Express框架中间件bodyparser处理FormData的POST表单数据reqbody接收不到数据

问题在使用node的过程中,express框架是必不可少的。之前表单提交数据使用的是submit按钮,使用express的中间件body-parser来处理,在req.body中可以拿到表单传来的值。 但是今天在使用ajax发送数据时,使用了XMLHttpRequest 2.0 提供的FormData来提交表单数据,出现了req.body一直是个空对象的情况,具体代码如下: 前端JS代码: $('.addBtn').click(function() { let fd = new FormData($('.form')[0]); $.ajax({ url : '/add', type : 'POST', contentType : false, processData :false, data : fd, success: function(res) { console.log(res); } })});contentType: false这一项必须设置为false,否则jQuery会默认将contentType设为 application/x-www-form-urlencoded,导致后端拿不到数据,因为 FormData 默认的数据类型是 multipart/form-data,我在网上查这个问题的相关资料时,有些文档竟然写着将 contentType 的类型改为 application/x-www-form-urlencoded 可以解决问题(白眼) processData: false这一项也是,不需要jQuery帮我们做数据处理,否则也会导致后端拿不到数据 后端代码: router.post('/add', (req, res) => { console.log(req.body); //{}});原因后来通过查阅资料才知道 body-parser 并不支持 contentType: multipart/form-data 的格式类型,也就是不支持formData格式 使用 connect-multiparty 第三方模块解决具体步骤如下: 安装 npm i connect-multiparty -S 引用 ...

July 9, 2019 · 1 min · jiezi

Nodejs性能分析工具alinode的安装和使用简介

啰嗦alinode是基本上是免费开源的,不只是可以运行在ECS里面,理论上可以应用在任何地方(只要面子上过得去)。本文将赘述一下alinode的使用方法。 本文首先啰嗦一下 Node.js 平台的性能问题分析所包含的一些内容,第二part再叙述下 alinode 是如何使用的 性能问题分析要定位 Node.js 的性能问题,一般要对Node进程进行性能方面的进程分析。 进程分析一般包括 内存、CPU、EventLoop、ActiveHandlers 等的分析。对于开发人员来说,这里面包括3个主要任务: 第一,压测应用。通常我们希望分析的都是应用在实际生产运行时的一个状况,而这个状况可以通过自己压测来模拟。第二,获取数据。也就是获取到我们的 CPU状况、内存状况等信息。这个一般通过运行时或运行时的底层提供的一些API来获取。例如Node的语言执行引擎V8就提供了cpu profile的能力。第三,分析数据。拿到数据后,我们需要对数据进行分析以让我们快速定位到问题。一般拿到的数据可读性比较差,因此这一步通常是对数据可视化,例如cpu profile可以通过工具转换为火焰图。或者通过其他第三方工具如v8-analysis工具库提供分析。压测应用这个有几个比较常用的工具,例如 Apache ab、wrk 以及 Node.js 编写的 autocannon 这里就不具体讲用法了,通过压测工具,我们可以量化我们站点的性能指标,在性能问题排查过程中为我们提供依据。 一般我们的性能指标需要达到一定的吞吐量 req/sec,且单个请求的耗时要在预期范围之内。否则耗时过高影响用户体验、吞吐太低则会造成某些用户访问失败。 数据获取CPUcpu一般要观察一些时间来拿到他的函数执行堆栈情况,比如 alinode 进行dump时也是要等3分钟。 方法1:直接使用v8底层对Node暴露的api。我们只需在启动node应用的时候,传入 node --prof app.jsnode.js 从 4.4.0 版本开始内置了 profiler, --prof 命令选项运行应用会在当前目录生成性能日志文件 以上命令会在项目根目录会生成 `isolate-xxxxxxx-v8.log` 格式的文件,这个log文件还不能直接进行分析,一般可视化工具都是要一个json文件。因此,log需要进行预处理,我们可以这样预处理这个log:node --prof-process --preprocess isolate-xxxxxxxxxx-v8.log > v8.json这样就可以把它转为一个json字符串文件。 方法2:使用npm包 v8-profile 使用示例如下: profiler.startProfiling('', true); setTimeout(function() { var profile = profiler.stopProfiling(''); profile.export() .pipe(fs.createWriteStream(`cpuprofile-${Date.now()}.cpuprofile`)) .on('finish', () => profile.delete()) }, 1000);内存方法1: heapdump 这个模块可以用来进行内存数据的采集。这个包需要侵入代码,在代码中引用后,可以通过其API进行dump。或者在外部给进程发送 kill -USR2 <pid> 的信号。方法2:v8-profile。这也是一个需要侵入node代码的模块,通过该模块可以获取 heapdump 和 cpu profile数据分析分析的话,无非是用一些易于查看的数据展示工具或定位工具来帮助我们更好的展示数据。比如能按函数热度去展示cpu profile文件。 ...

July 8, 2019 · 2 min · jiezi

nodejs使用aes128ecb加密如何在c中解密

最近需要在nodejs上加密jwt,C#端解密jwt得到用户信息 class JwtService extends Service { encrypt(content) { const secretkey = this.app.config.jwt.key // 唯一(公共)秘钥 const cipher = crypto.createCipher('aes-128-ecb', secretkey) // 使用aes128加密 let enc = cipher.update(content, 'utf8', 'hex') // 编码方式从utf-8转为hex; enc += cipher.final('hex')// 编码方式转为hex; return enc }}却发现C#端怎么也解密不了,一直报错,改了一整天,后来终于发现,nodejs端加密用的key其实在使用之前已经使用md5加密了一次,服务端如果需要使用这个key解密,则需要也同样使用MD5加密 public static string AesDecrypt(string content, string key) { // nodejs aes加密默认的key使用了md5加密,所以C#解密的key也要默认使用md5 MD5 md5 = new MD5CryptoServiceProvider(); byte[] output = Encoding.UTF8.GetBytes(key); byte[] keyArray = md5.ComputeHash(output); byte[] toEncryptArray = HexStringToBinary(content); RijndaelManaged des = new RijndaelManaged(); des.Key = keyArray; des.Mode = CipherMode.ECB; des.Padding = PaddingMode.PKCS7; ICryptoTransform cTransform = des.CreateDecryptor(); byte[] resultArray = cTransform.TransformFinalBlock(toEncryptArray, 0, toEncryptArray.Length); return Encoding.UTF8.GetString(resultArray); }代码使用了一个函数把16进制转成字节数组 ...

July 8, 2019 · 1 min · jiezi

ngnixvuenodejsredis实现前后端分离的环境配置

一、 windows1. 安装配置nginx1.1. 下载安装1.2. 常用命令# 开启nginx start nginx nginx.exe# 停止nginx nginx.exe -s stop nginx.exe -s quit# 重启nginx nginx.exe -s reload1.3 配置`nginx`部分代码```# 反向代理前端vue和后端的node # 这是前端映射的端口 location / { proxy_pass http://localhost:8080; } # 后端node提供的api接口 location /api/ { proxy_pass http://localhost:8000; proxy_set_header Host $host; }```2. 安装配置redis1. 下载安装2. 常用命令运行打开一个 cmd 窗口 使用 cd 命令切换下载文件的根目录如: C:redis 运行:redis-server.exe redis.windows.conf这时候另启一个 cmd 窗口,原来的不要关闭,不然就无法访问服务端了。切换到 redis 目录下运行:redis-cli.exe -h 127.0.0.1 -p 6379[待修改]

July 8, 2019 · 1 min · jiezi

在不同的项目下切换不同版本的-Nodejs

作者:Brian De Sousa翻译:疯狂的技术宅 原文:https://blog.logrocket.com/sw... 未经允许严禁转载 有时候几乎每周都会发布新版本的 Node.js —— 每隔几周发布一次小版本,每隔几个月发布一次主要版本。如果你是一个需要在不同程序和项目之间切换的码农,可能会发现需要运行不同版本的 Node。 幸运的是,有几种不错的方法可以安装多个版本的 Node 并根据需要进行切换。本文将讨论和比较在 Linux/Mac 环境下的两个流行的 Node 版本管理器:NVM for Windows 和 n Node version manager。 提示:Windows 和 Linux/Mac 有不同的 NVM 实现;但是 n 的 npm 包仅在 Linux/Mac 上得到支持。为了进行比较,让我们假设你正在处理两个程序。程序 1 是在 Node 6.17.1上运行的 Angular 5 程序。程序 2 是在 Node 8.16.0 上运行的 Angular 7 程序。以下是你需要完成的任务: 修复程序 1 上的 bug x将程序 2 升级到 Angular 8实际上,你需要三个版本的 Node 才能完成任务,因为你需要将程序 2 升级到 Node 10.9或更高版本才能支持 Angular 8 的升级。 ...

July 8, 2019 · 3 min · jiezi

译保持Nodejs的速度创建高性能Nodejs-Servers的工具技术和提示

pre-tips本文翻译自: Keeping Node.js Fast: Tools, Techniques, And Tips For Making High-Performance Node.js Servers 原文地址:https://www.smashingmagazine.... 中文标题:保持Node.js的速度-创建高性能Node.js Servers的工具、技术和提示 快速摘要Node 是一个非常多彩的平台,而创建network服务就是其非常重要的能力之一。在本文我们将关注最主流的: HTTP Web servers. 引子如果你已经使用Node.js足够长的时间,那么毫无疑问你会碰到比较痛苦的速度问题。JavaScript是一种事件驱动的、异步的语言。这很明显使得对性能的推理变得棘手。Node.js的迅速普及使得我们必须寻找适合这种server-side javacscript的工具、技术。 当我们碰到性能问题,在浏览器端的经验将无法适用于服务器端。所以我们如何确保一个Node.js代码是快速的且能达到我们的要求呢?让我们来动手看一些实例 工具我们需要一个工具来压测我们的server从而测量性能。比如,我们使用 autocannon npm install -g autocannon // 或使用淘宝源cnpm, 腾讯源tnpm其他的Http benchmarking tools 包括 Apache Bench(ab) 和 wrk2, 但AutoCannon是用Node写的,对前端来说会更加方便并易于安装,它可以非常方便的安装在 Windows、Linux 和Mac OS X. 当我们安装了基准性能测试工具,我们需要用一些方法去诊断我们的程序。一个很不错的诊断性能问题的工具便是 Node Clinic 。它也可以用npm安装: npm install -g clinic这实际上会安装一系列套件,我们将使用 Clinic Doctor和 Clinic Flame (一个 ox 的封装) 译者注: ox是一个自动剖析cpu并生成node进程火焰图的工具; 而clinic Flame就是基于ox的封装。另一方面, clinic工具本身其实是一系列套件的组合,它不同的子命令分别会调用到不同的子模块,例如:医生诊断功能。The doctor functionality is provided by Clinic.js Doctor.气泡诊断功能。The bubbleprof functionality is provided by Clinic.js Bubbleprof.火焰图功能。 The flame functionality is provided by Clinic.js Flame.)tips: 对于本文实例,需要 Node 8.11.2 或更高版本 ...

July 8, 2019 · 5 min · jiezi

React的移动端和PC端生态圈的使用汇总

对于一项技术,我们不能停留在五分钟状态,特别喜欢一句话,用什么方式绘制UI界面一点不重要,重要的是底层的思维,解决问题和优化的思路。由于React的生态极为庞大,本文内容部分来自一些别人的汇总,至于原文只要还是能找到的,我都会贴上地址,谢谢前期贡献的作者,如果有没有被汇总到的,欢迎在下面补充。生态圈:React官方推荐超大型项目使用的TypeScript为什么要把TypeScript放在第一位,因为TypeScript在构建超大型应用时,多人协作可以极大的加快工作效率,特别是前后端交互特别多,业务情况特别复杂的状况下(比如IM),它的优势就凸显出来了。但是在一些中小型项目中,优势并不是那么的明显。(比如做完项目跑路后期不迭代这种) TypeScript并不是一个新语言,可以简单的认为 TS= js + Type.它只是一个javascript的超集,目前更新速度也是非常快, 个人建议,在Node.js开发和React native以及大型React中使用TypeScript在下载官方的react脚手架中,包含了一个第三方的ts创建脚手架的命令 在 Create React App 中使用 TypeScriptCreate React App 内置了对 ·TypeScript` 的支持。需要创建一个使用 TypeScript 的新项目,在终端运行:npx create-react-app my-app --typescript interface IState { collapsed?: boolean, } interface IProps { props?: string | Function } constructor(props: IState) { super(props) } flag :number = 123 componentDidMount() { const result = this.FunctionTest() } FunctionTest():Promise<number|string|object>{ return Promise.resolve(false) } TypeScript写起来代码量会多一些,但是对于参数类型,返回类型,一眼明了,拥有静态类型检查,如果有问题,在编写代码时候就可以知道。补充一点,现在TS的生态已经足够适应开发,像一般的webpack插件都有了typescript的文件支持,当然,并不是所有的第三包都支持ts.在技术选型的时候就要考虑清楚这点,否则就会多做很多事情。状态统一集中管理,redux,mbox,redux-sage,dva等开源库先看看原始的react数据管理 组件间数据的传递,依靠props,状态数据提升等完成,但是对于跨层级的组件间数据传递,就不那么友好了,尤其是大型项目后期的迭代维护再说说被人吐槽,但是它的单向数据流思想不得不肯定的redux. Redux 状态及页面逻辑从 <App/>里面抽取出来, 成为独立的 store, 页面逻辑就是 reducer <TodoList/> 及<AddTodoBtn/>都是 Pure Component, 通过 connect 方法可以很方便地给它俩加一层 wrapper 从而建立起与 store 的联系: 可以通过 dispatch 向 store 注入 action, 促使 store 的状态进行变化, 同时又订阅了 store 的状态变化, 一旦状态有变, 被 connect 的组件也随之刷新使用 dispatch 往 store 发送 action 的这个过程是可以被拦截的, 自然而然地就可以在这里增加各种 Middleware, 实现各种自定义功能, eg: logging这样一来, 各个部分各司其职, 耦合度更低, 复用度更高, 扩展性更好在面试的时候,我觉得如果可以手写一个redux库,并且说清楚单向数据流的思维,是一个加分项。最终推荐使用dva,感谢前辈的开源,解放了我们 ...

July 7, 2019 · 4 min · jiezi

保护-Nodejs-项目的源代码

SaaS(Software as a Service,软件即服务),是一种通过互联网提供软件服务的模式。服务提供商会全权负责软件服务的搭建、维护和管理,使得他们的客户从这些繁琐的工作中解放出来。对于许多中小型企业而言,SaaS 是采用先进技术的最好途径。 然而,对于大型企业而言,情况有所不同。出于产品定制、功能稳定以及掌握自身数据资产等方面的考虑,即使成本增加,他们也更乐意把相关服务部署在企业自己的硬件设备上,也就是常说的私有化部署。 在私有化部署的过程中,服务提供商首先要确保自己的源代码不被泄露,否则产品就可以随意复制和更改,得不偿失。传统的后端运行环境,如 Java、.NET,其源代码是经过编译才部署到服务器上运行的,不存在泄露的风险。而对于应用越来越广泛的 Node.js 而言,运行的则是源代码。即使经过压缩混淆,也可以很大程度地还原。 本文介绍一种可用于 Node.js 端的代码保护方案,使得 Node.js 项目也可以放心地进行私有化部署。 原理当 V8 编译 JavaScript 代码时,解析器将生成一个抽象语法树,进一步生成字节码。Node.js 有一个叫做 vm 的内置模块,创建 vm.Script 的实例时,只要在构造函数中传入 produceCachedData 属性,并设为 true,就可以获取对应代码的字节码。例如: const vm = require('vm');const CODE = 'console.log("Hello world");'; // 源代码const script = new vm.Script(CODE, { produceCachedData: true});const bytecodeBuffer = script.cachedData; // 字节码并且,这段字节码可以脱离源代码运行: const anotherScript = new vm.Script(' '.repeat(CODE.length), { cachedData: bytecodeBuffer});anotherScript.runInThisContext(); // 'Hello world'这段代码看起来不那么容易理解,主要体现在创建 vm.Script 实例时传入的第一个参数: 既然源代码的字节码已经在 bytecodeBuffer 中,为何还要传入第一个参数?为何传入与源代码长度相同的空格?首先,创建 vm.Script 实例时,V8 会检查字节码(cachedData)是否与源代码(第一个参数传入的代码)匹配,所以第一个参数不能省略。其次,这个检查非常简单,它只会对比代码长度是否一致,所以只要使用与源代码长度相同的空格,就可以“欺骗”这个检查。 ...

July 7, 2019 · 3 min · jiezi

基于-Nodejs-的轻量级云函数功能实现

导语在万物皆可云的时代,你的应用甚至不需要服务器。云函数功能在各大云服务中均有提供,那么,如何用“无所不能”的 node.js 实现呢? 一、什么是云函数?云函数是诞生于云服务的一个新名词,顾名思义,云函数就是在云端(即服务端)执行的函数。各个云函数相互独立,简单且目的单一,执行环境相互隔离。使用云函数时,开发者只需要关注业务代码本身,其它的诸如环境变量、计算资源等,均由云服务提供。 二、为什么需要云函数?程序员说不想买服务器,于是便有了云服务;程序员又说连 server 都不想写了,于是便有了云函数。 Serverless 架构通常我们的应用,都会有一个后台程序,它负责处理各种请求和业务逻辑,一般都需要跟网络、数据库等 I/O 打交道。而所谓的无服务器架构,就是把除了业务代码外的所有事情,都交给执行环境处理,开发者不需要知道 server 怎么跑起来,数据库的 api 怎么调用——一切交给外部,在“温室”里写代码即可。 FaaS而云函数,正是 serverless 架构得以实现的途径。我们的应用,将是一个个独立的函数组成,每一个函数里,是一个小粒度的业务逻辑单元。没有服务器,没有 server 程序,“函数即服务”(Functions as a Service)。 三、如何实现?由于本实现是应用在一个 CLI 工具里面的,函数声明在开发者的项目文件里,因而大致过程如下: 1、函数声明与存储声明我们的目标是让云函数的声明和一般的 js 函数没什么两样: module.exports = async function (ctx) { return 'hahha' }};由于云函数的执行通常伴随着接口的调用,所以应该要能支持声明 http 方法: module.exports = { method: 'POST', handler: async function (ctx) { return 'hahha' }};存储由于有 method 等配置,因此编译的时候,需要把上述声明文件 require 进来,此时,handler 字段是一个 Function 类型的对象。可以调用其 toString 方法,得到字符串类型的函数体: const f = require('./func.js');const method = f.method;const body = f.handler.toString();// async function (ctx) {// return 'hahha'// }有了字符串的函数体,存储就很简单了,直接存在数据库 string 类型的字段里即可。 ...

July 7, 2019 · 2 min · jiezi

Node-项目调试

前言 作为一个程序员,感觉我们其实打代码的时间不多,大部分时间都在在调试上,所以如何调高我们调试的效率,这就成了我们的一个大问题了,经过在网上的搜索与自己的探索,就推荐这两种好用的调试方法吧 1 Chrome 调试因为 V8 检查器集成允许使用 Chrome 调试协议将 Chrome DevTools 附加到 Node.js 实例以进行调试。所以我们可以直接用 Chrome 进行调试。这个就最简单的的方法,直接在运行代码的地方加个前缀 --inspect,如下: node --inspect /bin/www这个时候我们在控制台会看到多出这么几个提示 Debugger listening on ws://127.0.0.1:9229/f54d4cb8-56bc-46c4-9b8e-fd7f2163c73cFor help, see: https://nodejs.org/en/docs/inspectorDebugger attached.看到这个我们打开 Chrome,f12 打开控制台,点击上面的 Node 图标 我们就可以打开一个新的调试 Node 的浏览器窗口,我们在代码中打的 debugger 和 console.log 都会在那个窗口显示和输出出来。 2 VS Code 大法在 vscode 中调试步骤也是比较简单,1、 点击蜘蛛图标,打开调试控制台 2、选择一下调试环境 这里我们就直接选 Node的了的啦 3、配置调试模式我们选择好环境之后,他会弹出一个 launch.json 的配置文件,这里面我们可以vscode 有很多调试模式,我们可以通过 add configurations 添加调试模式。等到我们要调试的时候就在 debug 菜单那边选模式,然后点击绿色三角开始调试。下面我们着重介绍一下几个常见的模式吧 3 VS Code Node 调试模式3.1 attach 模式这个先拉出来说,因为后面几个模式都是通过 VS Code 启动项目的,而这个模式是你项目已经启动的情况下启用的,并且那项目还是开启了 Chrome 调试的 ...

July 7, 2019 · 1 min · jiezi

16进制转rgba

function hexToRgba(hex, opacity) { return "rgba(" + parseInt("0x" + hex.slice(1, 3)) + "," + parseInt("0x" + hex.slice(3, 5)) + "," + parseInt("0x" + hex.slice(5, 7)) + "," + opacity + ")";}使用方法hexToRgba('#00c154', 0.2)

July 6, 2019 · 1 min · jiezi

一次项目中的验签加密的一些坑主要是数据格式问题

最近接手了一个项目是一个比较完整的项目了 我需要进行改造 是基于vuecli3.0+webpack开发的u其中有一个功能就是我们每个请求都要进行一次验签 就是取一些字符串进行加密 并将验签加入请求头中 后台也用相同的算法进行加密 然后比对这个功能本身是写好的 但是有些问题我们其中的加密字段是穿的参数按照字母排序 然后取第一个参数的前三位和最后一个参数的后三位这都很简单用sort()方法排序一下就行第一个问题就是axios直接传值的话服务端会认为是字符串所以截取的时候会将符号也截取一般就成了{"+排序第一的参数的第一个字母和排序最后一个参数的最后一个字母 + "} 这样明显是有问题的 不过这个解决也简单 直接用qs这个插件处理一下就ok 这里我做了一个处理 如果requestData是类型是字符串 用 requestData.split('&').sort().join('').replace(/([^=><])=([^=><])/g,'$1$2');进行排序如果是对象 用 for (var i in requestData) { dataString += `${i}${requestData[i]}&` } dataString = dataString.split('&').sort().join('')进行排序这样就解决了大部分的问题 但是昨天突然有一个模块说验签又不过了 我就很奇怪 就让后端小哥把没加密的串返回给我对比了一下首先还是我上面说的截取到了符号 这个简单 qs处理一下然后这里的问题是用qs处理过后 参数中如果带有中文 后端接收到的其实是中文 但是qs自己会对中文进行encodeURIComponent转码 这个处理也很简单 使用decodeURIComponent() 函数进行解码就可以

July 6, 2019 · 1 min · jiezi

使用-requestId-标记全链路日志

标记全链路日志有助于更好的解决 bug 和分析接口性能,本篇文章使用 node 来作为示例 代码示例本文地址github当一个请求到来时,会产生哪些日志本次请求报文本次请求涉及到的数据库操作本次请求涉及到的缓存操作本次请求涉及到的服务请求本次请求所遭遇的异常本次请求执行的关键函数本次请求所对应的响应体如何查询本次从请求到响应全链路的所有日志使用 requestId 唯一标识每个请求,有时它又被称为 sessionId 或者 transactionId。 使用 requestId 标记每次请求全链路日志,所要标记的日志种类如上所示通过把 X-Request-Id (X-Session-Id) 标记在请求头中,在整个链路进行传递async function context (ctx: KoaContext, next: any) { const requestId = ctx.header['x-request-id'] || uuid() ctx.res.setHeader('requestId', requestId) ctx.requestId = requestId await next()}app.use('/todos/:id', (ctx) => { User.findByPk(ctx.body.id, { logging () { // log ctx.requestId } })})如何以侵入性更小的方式来标记每次请求如上,在每次数据库查询时手动对 requestId 进行标记过于繁琐。可以统一设计 logger 函数进行标记 具体代码可见我一个脚手架中的 logger.ts 这里使用了流行的日志库 winston (13582 Star) import winston, { format } from 'winston'const requestId = format((info) => { info.requestId = session.get('requestId') return info})const logger = winston.createLogger({ format: format.combine( format.timestamp(), requestId(), format.json() )})如何在 logger.ts 中绑定 requestId或者说如何在 logger.ts 如何获得整个请求响应生命周期中的 requestId ...

July 6, 2019 · 1 min · jiezi

vscode调试vue-node

1.vscode 调试vue项目引用文字launch.json{ "type": "chrome", "request": "launch", "name": "Launch Chrome against localhost", "url": "http://localhost:8090", "webRoot": "${workspaceFolder}/src", "sourceMaps": true, "sourceMapPathOverrides": { "webpack:///./*": "${webRoot}/*", "webpack:///src/*": "${webRoot}/*", "webpack:///*": "*", "webpack:///./~/*": "${webRoot}/node_modules/*", "meteor://????app/*": "${webRoot}/*" } }vue.config.js vscode 调试node项目launch.json直接F5 或者Fn + F5 运行即可进行调试{ "type": "node", "request": "launch", "name": "Launch Program", "program": "${workspaceFolder}/bin/www", // window 路径需要\\ "runtimeExecutable": "nodemon", // 需要安装nodemon 全局安装 "restart": true, "console": "integratedTerminal", "skipFiles": [ "${workspaceRoot}/node_modules/**/*.js", "<node_internals>/**/*.js" ] }

July 5, 2019 · 1 min · jiezi

VueCli30中集成MockApi

VueCli3.0中集成MockApi一:使用场景哎哟,好烦啊,这个需求还么结束就来下一个需求,程序员不要排期的吗? 没办法啊,资本主义的XX嘴脸啊 来吧,技术评审我俩把接口格式对一把,你先开发,我这边结束了我跟上,再联调 MMP,那又增加了我的工作量啊,每次我都要自己先把数据放在一个配置文件中,引入使用,然后对接的时候还得删除无用代码,好气 你自己Mock接口啊,就向我们后端经常用PostMan一样模拟请求啊 Mock??我去查查看 二:Mock的概念1:Mock的描述 Mock接口其实就是模拟真实接口提供一个在开发环境的假数据,甚至是真实数据,在开发时,经常出现接口内容不能够及时的跟进,导致开发过程中添加一些额外的工作量。接下来的例子全部围绕着Vue为主体介绍前后端提前确定好通信的JSON格式之后,我们在不依赖后端进度的同时,能提供一套好的开发体验。 2:Mock能解决的问题 减少额外工作,在没有Mock接口的时候我们模拟数据的方式很烦躁,比如list列表,需要在data中声明list,去调试内容,或者引入一个mock文件,这样做导致在联调调用接口的部分代码没有写,联调成功的时候要删除很多无用代码 ---> 通过Mock只需在联调的时候把Mock接口的地址换成真实地址即可 import { mockList2 } from 'mock/list.js';export default { data () { return { mockList: [ { "name": 'tx', "age": 12 } ], mockList2 } }}如果采用上述的方式去模拟数据,缺少真正缺口所具备的状态,比如删除接口,有成功和失败的区分,这个模拟就很恶心了 ----> 通过Mock,可以直接通过实在的query或者其他的操作来达到同样的目的3:Mock的几种方式以及对应的优缺点 Mock的方式优缺点本地Mock接口优点:可以更加细粒度的控制mock的内容。缺点:需要增加本地的代码量,以及需要配置webapckMock.js实现ajax拦截优点:数据通过mock.js会更丰富。缺点:增加一些本地配置,拦截ajax后端Controller的静态JSON优点:接口联调不需要修改任何东西。缺点:修改Mock内容沟通成本高,跟后端扯皮利用FastMock去模拟Mock优点:可控内容以及实现动态Restful api。缺点:如果项目包装axios等请求库之后需要针对接口转发做不同处理4:本地Mock接口 该篇文章针对本地Mock接口进行操作,其他的方式会简要介绍并给出对应的链接,如果有需要,自行去查阅。 三:本地Mock周边知识本地Mock的思想就是利用Node + express完成Restful Api。结合webpack配置项devServer同时利用Vue-cli3.0的暴露的配置利用本地express完成mock接口的添加 Node+Express的相关知识点,用node+express写过Restful Api的就应该知道接下来Mock怎么处理了,这里我先简要介绍一下我们需要用到的技术吧(Express的路由以及node的fs模块) Express路由相关,具体的见文档,这里不区分请求方法,直接app.use const express = require('express');const app = express();// 这样一个简单的路由就完成了,请求到/ajax-get-info的请求就能拿到对应的JSON数据app.use('/ajax-get-info', (req, res) => { res.send({ "success": true, "code": 0, "data": {} }) });针对不同的请求生成动态的内容,我们可以通过req.query和req.params等来生成动态内容,在express中,我们传入的body内容,在req.body中并获取不到,需要添加中间件body-parser,需要注意的是这个中间件不能在app全局路由使用,不然会影响到代码到测服的接口,利用http-proxy-middleware转发的接口,所以我们需要单独的设置一个Mock路由,针对路由级别的使用中间件,代码如下 ...

July 5, 2019 · 1 min · jiezi

Parse-Server-快速实现-Serverless-的利器

原文地址 近年来NODEJS发展迅速,很多工程师尤其是前端工程师,用NODEJS来开发一些后端应用。同时,研发效率和研发成本成为开发者关注的重点,对于一些基础常用功能,如何避免重复开发,成为大家关注的重点,而 Parse Server 就是抽象了常用功能的NODEJS开源项目。 首先,从整体上看看 Parse Server 提供了哪些基础功能: 用户的登录注册用户身份的认证数据存储 && 灵活查询文件存储实时查询消息推送缓存服务与云平台很好的对接自定义业务逻辑与Hook机制服务快速搭建默认情况下,Parse Server 使用的默认数据库是 MonogDB,所以需要提前安装该数据库。关于数据库的安装与使用不是本文的重点,暂且跳过。 const config = require('./config');const app = express();var api = new ParseServer({ databaseURI: config.databaseURI, cloud: './cloud/main.js', appId: config.appId, masterKey: config.masterKey, // push: { ... }, // See the Push wiki page // filesAdapter: ..., // 对应不同云厂商的 FilesAdapter // javascriptKey: config.javascriptKey, // js客户端认证 liveQuery: { // 建立websocket链接,实现实时通信 classNames: ['Sdtuent'] }});var dashboard = new ParseDashboard({ "apps": [ { "serverURL": "http://localhost:1337/parse", "appId": config.appId, "masterKey": config.masterKey, "appName": "test" }, ]});// Serve the Parse API at /parse URL prefixapp.use('/parse', api);app.use('/dashboard', dashboard);const port = 1337;const httpServer = http.createServer(app);httpServer.listen(port, function() { console.log('parse-server-example running on port ' + port + '.');});var parseLiveQueryServer = ParseServer.createLiveQueryServer(httpServer);通过上述少量的代码,快速完成服务的搭建,/parse 是API的前缀,/dashboard 是后台的页面路由前缀,这样就可以快速使用 Parse Server 提供的各种功能。 ...

July 5, 2019 · 3 min · jiezi

报错Unexpected-token-import

发生这个错误的原因是node支持ES6的部分语法,而不是支持全部语法目前 NodeJs 只支持部分 ES6 的语法,有些 ES6 的语法还不支持,而 import 语法就是其中之一,web browser 也是一样,只支持部分语法。而要 NodeJs 全部支持 ES6 的语法,可能需要在今后新版本的 NodeJs 才可以。 解决办法就是采用require的方式

July 4, 2019 · 1 min · jiezi

初学Vue三-前后端数据交互

推荐学了node.js、vue.js入门或了解普通js与node传输数据的观看在vue实例中用vue的方式将数据传递到后台完整html代码完整js代码利用插件vue-resource在vue中并没有方法让我们进行前后端数据交互,但是有个人写了个插件叫vue-resource,这个插件可以让我们正常的进行前后端数据交互vue-resource下载用cnpm/npm/bower 都可以下载这个插件用cmd 进入想要安装的目录底下后下载下载代码: cnpm/npm/bower install vue-resource$http 方法下载完之后就可以设定一个事件让登陆注册触发首先要使用当然是要先将插件的代码链入 <script src="vue-resource.js"></script> 利用双向绑定将前端数据在vue 实例中绑定html部分:用户名:<input type="text" v-model="user"><br/>密码:<input :type="passwordBtn" v-model="password"><br/><input type="button" value="submit" @click="login">js部分:new Vue({ el:'body', data:{ user:'', password:'', }, methods:{ login:function(){ console.log(this.$http); } }})可以尝试去掉vue-resource插件的链入,那么console.log(this.$http);就会失效,控制台显示undefined,因为$http方法是由这个插件赋予的vue-resource插件的用法这个插件主要就是提供了个方法可以传递前后端数据,而这个方法也就是$http方法后面接传输方式get/post第一个参数放入node 写的地址第二个参数以json 的形式放入数据名与数据示范代码接上文,默认在方法内开始写: login():function{ this.$http.get('http://localhost:****',{ user:this.user, pass:this.pass });}在方法后面还能用then方法加入一个报错判断,方法内传入两个处理函数第一个处理函数有个形参,形参是后台定义的传入信息,自定义的,根据你想要的可以更改判断信息为任何条件,假设我们从后台传入的信息为一个json{'ok':1}第一个函数的形参是后端发送给前端的一个数据包,里面包含了所有后端发送过来的数据,有一个data属性是我们所需要的,是我们自定义send过来的,ok则是我们自定义的内容第二个处理函数则是如果信息无法传入给后端的话怎么处理login():function{ this.$http.get('http://localhost:****',{ user:this.user, pass:this.pass }).then(function(dat){ if(dat.data.ok==1){ alert('登陆成功'); }else{ alert('登陆失败'); } },function(){ console.log('传输失败'); });}第二个函数也可以不要,因为这是一个错误处理函数,我们也可以用then方法的catch方法,效果一样login():function{ this.$http.get('http://localhost:****',{ user:this.user, pass:this.pass }).then(function(dat){ if(dat.data.ok==1){ alert('登陆成功'); }else{ alert('登陆失败'); } }).catch(functino(){ console.log('传输失败'); });}下面是后台node 部分的代码书写要导入的模块有: express,body-parser当然,要先下载才能导入使用,下载方法也是用 cnpm/npm/bower 这几个包管理器var express=require('express');var bodyParser=require('body-parser');var server = express();导入完毕后设定一个接口号,前端$http方法内的地址用到server.listen(****); 再通过express() 的use 设定bodyparser 模块解析方式为urlencoded()server.use(bodyParser.urlencoded()); ...

July 4, 2019 · 1 min · jiezi

利用verdaccio创建npm私有库

利用verdaccio创建npm私有库文章说明本文记录在语雀中,可以查看 利用verdaccio创建npm私有库。

July 4, 2019 · 1 min · jiezi

使用node简单实现记录浏览量不依赖数据库

技术栈1.node的框架express,对于我们做前端的也比较友好。2.不依赖数据库,就采用文件系统简单代替。 使用场景由于家里要卖土特产,就临时决定做个H5来推广。但是又不知道有没有来访问,就决定简单实现一个浏览量访问记录。安装依赖npm initnpm install express --save代码实现// app.js let express = require('express'); //引入expresslet fs = require('fs'); // 引入文件系统let port = 9000; // 端口号var app = express(); app.get('/',function(req,res){ let result=fs.readFileSync('./record.txt','utf8') // 读取文件 record.txt, record.txt记得和app.js放同一级 fs.writeFileSync("./record.txt", parseInt(result) + 1) // 写入record.txt,并把数字加1. res.send(result); // 最后得到的浏览数,返回浏览器 });var server = app.listen(port ,function(){ var host = server.address().address; var port = server.address().port; console.log('example app listening at http://%s:%s',host,port);})运行代码node app.js // 浏览数可以在 record.txt里面看

July 4, 2019 · 1 min · jiezi

在Windows下搭建React-Native-Android开发环境搭建项目

说明:Windows下搭建React Native Android开发环境和基本的React Native项目搭建环境变量添加控制面板 -> 系统和安全 -> 系统 -> 高级系统设置 -> 高级 -> 环境变量 -> 新建项目搭建调试方式采用真机调试,如需使用 Android 模拟器参考文档 https://reactnative.cn/docs/g...一、环境搭建1.安装的java jdkJava JDK官网安装就是下一步下一步下一步 1.1修改环境变量,新增JAVA_HOME的系统环境变量,值为C:\Program Files (x86)\Java\jdk1.8.0_112,也就是安装JDK的根目录 1.2修改系统环境变量Path,在Path之后新增%JAVA_HOME%\bin;%JAVA_HOME%\jre\bin; 1.3新建系统环境变量CLASSPATH,值为.;%JAVA_HOME%\lib\dt.jar;%JAVA_HOME%\lib\tools.jar; 1.4保存所有的系统环境变量,同时退出系统环境变量配置窗口,然后运行cmd命令行工具,输入javac,如果能出现javac的命令选项,就表示配置成功! 2安装Node.js环境node.js中文网安装就是下一步下一步下一步 2.1注意:需要安装最新的长期稳定版本,不要实验版本;安装完毕之后的node.js会自动配置到全局系统环境变量中安装完毕后,可以输入node -v查看node版本号; 3.安装C++环境3.1大多数情况下操作系统自带C++环境,不需要手动安装C++环境;如果运行报错,则需要手动安装visual studio中的C++环境; 4.安装Git环境(一个可选的操作)Git官网 4.1Git安装完毕后,会自动配置到系统环境变量中;可以通过运行git --version来检查是否正确安装和配置了Git的环境变量; 5.安装Python环境这里选择2.7版本python官网 5.1注意:安装Python时候,只能安装2.×的版本,注意勾选安装界面上的Add Python to path,这样才能自动将Python安装到系统环境变量中; 5.2安装完毕之后,注意应该以管理员身份在命令行中运行python,检查是否成功安装了python。 6.配置安卓环境Android Studio下载安装就是下一步下一步 6.1安装完成点击"Configure",然后点击"SDK Manager" 6.2在 SDK Manager 中选择"SDK Platforms"选项卡,然后在右下角勾选"Show Package Details"。展开Android 6 勾选图中标注的2选项,展开Android 9 (Pie)勾选图中标注的3选项 6.3在 SDK Manager 中选择"SDK Tools"选项卡,勾选图中标注的1的选项 6.4在 SDK Manager 中选择"SDK Tools"选项卡,然后在右下角勾选"Show Package Details",选中图2所需包勾选完成点击安装 ...

July 3, 2019 · 1 min · jiezi

腾讯-Tars-Web-管理端用户体系对接

背景这段时间一直在基于 Tars 作开发。最近的文章也多是针对 Tars 的一些学习笔记。前面我们搭建了 Tars 基础框架,打开了 Tars web 管理界面进行服务的运维操作。不过读者肯定很快就会发现:这好像不用登录啊,那怎么保证只有有权限的用户才能更改服务呢? 显然 Tars web 是支持用户鉴权的。官方文档在这里。本文记录一下我的用户体系对接实验中的一些笔记,便于其他 Tars 的用户参阅。(特别是像我这样对 Node.js 不熟悉的小白……) 本系列文章: 腾讯 Tars 基础框架手动搭建——填掉官方 Guide 的坑腾讯 Tars-Go 服务 Hello World——从 HTTP 开始腾讯 Tars-Go 服务 Hello World——RPC 通信腾讯 Tars-Go 服务获取自定义模版(配置)值腾讯 Tars Web 管理端用户体系对接(本文)本文地址:https://segmentfault.com/a/1190000019657656 Tars 用户鉴权流程准备如果要启用 Tars web 的用户功能,那么首先开发者需要设计一个自己的用户登录服务。该服务是 http 服务,有独立的用户登录、登出功能。Tars Web 本身实现了一个简单的用户功能,不过本文我们重新设计一个。为便于说明,我们假设这个 Tars web 和用户服务 web 环境如下: Tars Web URL:https://tars.amc.com用户 Web URL:https://user.amc.com基本流程从用户通过浏览器访问 Tars web 管理平台开始,如果启用了用户功能,那么基本流程如下: 一言以蔽之:每当浏览器向 Tars web 发起一个请求时,Tars web 均向用户服务器发起请求,判断用户是否有权限;如果鉴权通过,则正常操作 Tars;如果没有,则重定向至用户登录页面。 ...

July 3, 2019 · 2 min · jiezi

你可能不知道的-npm-实用技巧

作者: LeanCloud weakish 分享一些 npm 包管理工具的实用小窍门,希望能够略微提高下前端、Node.js 开发者的生活质量。绝大多数前端和 Node.js 开发者每天的日常工作都离不开 npm,不知道你对 npm 的观感如何?如果你觉得 npm 很棒,那么不妨看下这篇文章,说不定其中有你之前没留意过的小窍门,可以让你 npm 用得更顺手。如果你觉得 npm 很糟糕,那也可以看下这篇文章,也许会发现用上一些小技巧,npm 会变得稍微不那么糟糕。 npm ci别被它的名字骗了。npm ci 并不仅仅适用于持续集成系统,在日常开发中,npm ci 非常实用。和 npm install 不同,npm ci 根据 package-lock.json 安装依赖,这可以保证整个开发团队都使用版本完全一致的依赖,避免把时间浪费在排查因为依赖不一致而导致的各种奇怪问题上。不仅如此,npm ci 还有一个很好的副作用,加快 node 模块安装速度。因为 npm ci 直接根据 package-lock.json 中指定的版本安装,无需计算求解依赖满足问题,在大多数情况下都可以大大加速 node 模块安装过程。如果你曾经因为嫌 npm install 太慢而换用兼容性不那么好的 yarn 以及兼容性更不好的 pnpm,那么不妨试下 npm ci,也许你会发现,其实 npm 也可以不那么慢。 另外,如果 package-lock.json 过时(和 package.json 冲突),那么 npm ci 会很贴心地报错,避免项目依赖陷入过时状态。 有了 npm ci,基本上我只在引入新依赖时才使用 npm install。 ...

July 3, 2019 · 2 min · jiezi

Nodejs-实现远程桌面监控

描述最近使用node实现了一个远程桌面监控的应用,分为服务端和客户端,客户端可以实时监控服务端的桌面,并且可以通过鼠标和键盘来控制服务端的桌面。 这里因为我是用的同一台电脑,所以监控画面是这样的,当然使用两台电脑一个跑客户端,一个跑服务端才有意义。 原理其实这个应用的功能主要分为两部分,一是实现监控,即在客户端可以看到服务端的桌面,这部分功能是通过定时截图来实现的,比如服务端一秒截几次图,然后通过socketio发送到客户端,客户端通过改变img的src来实现一帧帧的显示最新的图片,这样就能看到动态的桌面了。监控就是这样实现的。 另一个功能是控制,即客户端对监控画面的操作,包括鼠标和键盘的操作都可以在服务端的桌面真正的生效,这部分功能的实现是在electron的应用中监听了所有的鼠标和键盘事件,比如keydown、keyup、keypress,mousedown、mouseup、mousemove、click等,然后通过socketio把事件传递到服务端,服务端通过 robot-js来执行不同的事件,这样就能使得客户端的事件在服务端触发了。 实现原理讲完,我们来具体实现一下(源码链接在这)。 实现socket通信首先,服务端和客户端分别引入socket.io和socket.io-client, 分别初始化 服务端: const app = new Koa();const server = http.createServer(app.callback());createSocketIO(server);app.use((ctx): void => { ctx.body = 'please connect use socket';});server.listen(port, (): void => { console.log('server started at http://localhost:' + port);});//createSocketIOconst io = socketIO(server, { pingInterval: 10000, pingTimeout: 5000, cookie: false });io.on('connect', (socket): void => { socket.emit('msg', 'connected');}客户端: var socket = this.socket = io('http://' + this.ip + ':3000')socket.on('msg', (msg) => { console.log(msg)})socket.on('error', (err) => { alert('出错了' + err)})这样,服务端和客户端就通过socketio建立了链接。 ...

July 3, 2019 · 3 min · jiezi

前端历史演变

在选择学习Webpack之前,我们先了解一下前端整个发展历程。 2014年初,我加入互联网开发行业,随没经历前端刀工火种的时态,5年的时间,前端技术的百家齐放很是眼花缭乱。我也从套页面后端工程师、jquery写效果到现在的小程序、node、vue转变成一个纯前端。现在回顾一下前端到底发生了哪些历史变化。 静态页面 1990~2005互联网发展早期,前端只负责写静态页面,纯粹的展示功能,JavaScript的作用也只是增加一些特殊效果。这种静态页面不能读取数据库,为了使Web更加充满活力,以PHP、JSP、ASP.NET为主的动态语言相继诞生。这使页面能够获取数据并不断更新,是前后端混合开发模式开端,所有的前端代码和前端数据都是后端生成的,随着后端代码的庞大和逻辑越来越复杂,相继的MVC发展起来。这时后端大多采用MVC模式开发,前端只是后端MVC中的V(视图);从web的诞生到2005,一直处在_后端重前端轻_的状态。 AJAX阶段 20052004年AJAX技术的诞生改变了前端的发展历史。以Gmail和Google地图这样革命性的产品出现,使得开发者发现,前端的作用不仅仅是展示页面,可以管理数据和用户互动。解决一些糟糕的用户体验,前端页面要想获取后台数据需要刷新整个页面。依稀记得前几年,依托强大的Jquery,一页面的javascript代码使用ajax发送请求渲染DOM的情景。前端开始慢慢向后端靠拢。 NODEJS 的爆发 20092009年Ryan Dahl利用Chrome的V8引擎打造了基于事件循环的异步I/O框架。NODE的诞生,使javascript在服务端的无限可能,更重要的是它构建了一个庞大的生态系统。 2010年1月,NPM作为node的包管理系统首次发布。开发人员可以依照规范编写nodejs模块,发布到npm上,供其他开发人员下载使用。截止目前2019年6月8日,NPM包数量有1,003,262,是世界上最大的包模块管理系统。 Node.js 给开发人员带来了无穷的想象,JavaScript 大有一统天下的趋势。 前端MV**架构阶段 2010随着 HTML5 小程序 的流行,前端再也不是人们眼中的小玩意了,应用功能开发逐步迁移到了前端,前端的代码逻辑逐渐变得复杂起来。2010年10月Backbone MVP架构发布。2010年10月Angular MVC->MVVM2013年05月React开源 MVVM2014年07月Vue MVVM 随着这些 MV* 框架的出现,网页逐渐由 Web Site 演变成了 Web App,最终导致了复杂的单页应用( Single Page Application)的出现。随着 SPA 的兴起,2010年后,前端工程师从开发页面(切模板),逐渐变成了开发“前端应用”(跑在浏览器里面的应用程序)。 javascript 开发App随着 iOS 和 Android 等智能手机的广泛使用,移动浏览器也逐步加强了对 HTML5 特性的支持力度。 Web APP,即移动端的网站。一般泛指 SPA(Single Page Application)模式开发出的网站。将页面部署在服务器上,然后用户使用各大浏览器访问,不是独立APP,无法安装和发布。 Hybrid App,即混合开发,也就是半原生半Web的开发模式,有跨平台效果,实质最终发布的仍然是独立的原生APP。 React Native App,Facebook发起的开源的一套新的APP开发方案,使用JS+部分原生语法来实现功能。 May 7, 2019谷歌发布 Flutter for web,正式宣布 Flutter 成为全平台框架,支持手机、Web、桌面电脑和嵌入式设备。现在学跨平台应用开发,第一个要看的可能不是 React Native,而是 Flutter。 ...

July 3, 2019 · 1 min · jiezi

深入浅出nodejs学习笔记Buffer对象

Buffer在node运行时自动加载,挂载到global对象上,所以使用不需要requireBuffer是一个类似Array的对象,但它主要操作字节。Buffer所占内存不通过V8分配,属于堆外内存。Buffer类似于数组格式,内部元素是16进制的两位数,即0~255Utf-8情况下,汉字占3位,符号和字母占一位 可通过下角标访问元素和赋值。 let buffer = new Buffer(100)buffer[10] = -100 // 156 如果赋值比0小,则加上256buffer[20] = 300 // 44 如果赋值比255大,则减去256buffer[30] = 3.1415 // 3 小数则舍弃小数部分,保留整数console.log(buffer[1]) //会打印一个0到255的随机值Buffer内存分配不通过v8,是因为Buffer的性能相关部分是通过C++实现的,所以它的内存是通过c++层面实现的。Buffer内存分配时,以8kb为界限 小于8kb的为小Buffer:对于小Buffer对象,采用slab分配机制,c++先申请8kb的slab内存,如果该slab剩余空间足够,则在slab空间内分配内存给新的Buffer,如果不足,则申请新的slab空间。大于8kb的为大Buffer:大Buffer对象,将会直接分配一个SlowBuffer对象作为slab空间,该slab空间被大Buffer对象独占。Buffer可以与字符串直接进行转换,支持以下格式 UTF-8ACSIIUTF-16LE/UCS-2Base64HexBinaryBuffer对象尽量不要使用+运算符进行拼接,如下代码 const fs = require(‘fs’)let rs = fs.createReadStream(’test.txt’)let data = ‘'rs.on(‘data’, function(chunk) { data += chunk})rs.on(‘end’, function() { console.log(data)})data += chunk这句话蕴含了toString()转换,等价于data = data.toString() + chunk.toString()chunk为Buffer对象,如何内部含义宽字节编码,toString()就会导致乱码。

July 2, 2019 · 1 min · jiezi

如何把一个项目从Git本地仓库上传到github上

1、在桌面上创建一个文件夹(或者mkdir一个文件),将代码拷贝到该文件夹。 2、打开该文件,鼠标右击进入Git Bash Here 3、通过命令git init把这个文件夹变成Git可管理的仓库 4、通过git add把项目添加到仓库(或git add .把该目录下的所有文件添加到仓库,注意点是用空格隔开的)在这个过程中你其实可以一直使用git status来查看你当前的状态。 5、用git commit -m "日志" 把项目提交到仓库。 6、git remote add origin (仓库地址) 可能出现错误:! [rejected] master -> master (fetch first) error: failed to push some refs to 'git@gitee.co解决: 1、先输入git pull --rebase origin master2、在输入git push origin master如果还不行,出现push declined due to email privacy restrictions则

July 2, 2019 · 1 min · jiezi

koa初探

const router = require('koa-router')()router.get('/', async (ctx, next) => { await ctx.render('index', { title: 'Hello Koa 2!' })})router.get('/string', async (ctx, next) => { ctx.body = 'koa2 string'})router.get('/json', async (ctx, next) => { // http://localhost:3000/json?name=lius&age=26&sex=true ctx.body = { url: ctx.url, ctx_query: ctx.query, ctx_querystring: ctx.querystring } // { // "url": "/json?name=lius&age=26&sex=true", // "ctx_query": { // "name": "lius", // "age": "26", // "sex": "true" // }, // "ctx_querystring": "name=lius&age=26&sex=true" // }})router.post('/json',async (ctx,next)=>{ await ctx.cookies.set('name',ctx.request.body.name) console.log('name',ctx.cookies.get('name')) ctx.body = ctx.request.body})module.exports = router

July 2, 2019 · 1 min · jiezi

将路由器温度通过MQTT协议加入Homeassistant及Homekit

前言最近入手了一台斐讯K3路由器,可是博通的芯片发热太严重,想要随时了解路由器的温度,于是自己动手实现了一组程序来将路由器温度通过MQTT加入Homeassistant及Homekit. 准备已经获取root权限的K3路由器部署好HomeAssistant的内网服务器部署好Node.js的内网服务器前置知识基本的Shell编程基本的HTTP及web服务器知识基本的MQTT原理基础JavaScript编程软件结构最早的想法是直接在路由器上通过Python获取数据及通过MQTT发送至HASS服务器,但是一个是opkg的源太慢了,另一个是不想给路由器增加额外负担(万一加了测温软件温度涨几度就尴尬了),最终采用了如下的软件结构 路由器---> Node.js Server ---> Homeassistant其中路由器到Node.js通过简单的HTTP,将温度数据通过JSON发送过来.Node.js通过Express.js实现web服务器用于接受路由器数据,mqtt包实现mqtt通讯,发送给Homeassistant. 服务器端Shell程序温度获取首先第一步是获取路由器的温度数据: CPUcat /proc/dmu/temperature #CPU温度wl -i eth1 phy_tempsense #2.4GHz无线芯片温度wl -i eth2 phy_tempsense #5GHz无线芯片温度运行结果如下: 需要注意的是这里有一个坑,CPU温度的文件中又不符合UTF-8的字符,直接用curl发的话会导致错误,这里我们直接用cut进行处理,这里暂时不考虑温度为或3位的情况(事实上不太可能发生)cat /proc/dmu/temperature|cut -c19-20同理我们对另外两项数据也进行处理 wl -i eth1 phy_tempsense|cut -c0-2wl -i eth2 phy_tempsense|cut -c0-2发送温度数据HTTP数据约定GET Method数据位于data参数下,内容封装于JSON中,格式如下{"type":"data","CPU":61,"W24G":51,"W5G":65} curl实现这里我们通过curl来发送数据,这里是用GET方法,为了以后增加控制信息方便,数据包格式如下 {"type":"data","CPU":61,"W24G":51,"W5G":65}经过url encode的结果为 %7B%22type%22:%22data%22,%22CPU%22:61,%22W24G%22:51,%22W5G%22:65%7D我的Node.js服务器地址是192.168.2.103,端口3000所以我们的url是 url="http://192.168.2.103:3000/?data=%7B%22type%22:%22data%22,%22CPU%22:$cpu,%22W24G%22:$w24,%22W5G%22:$w5%7D"增加无限循环后的完整程序#!/bin/shecho " run.. "while truedo cpu=$(cat /proc/dmu/temperature|cut -c19-20) w24=$(wl -i eth1 phy_tempsense|cut -c0-2) w5=$(wl -i eth2 phy_tempsense|cut -c0-2) url="http://192.168.2.103:3000/?data=%7B%22type%22:%22data%22,%22CPU%22:$cpu,%22W24G%22:$w24,%22W5G%22:$w5%7D" curl $url sleep 60 #设置1分钟的查询周期 continuedoneecho " end.. "为了让程序不断运行,推荐用screen来管理Node端程序HTTP服务器这里我们基本使用了模版提供的功能,由于我们这里只对数据进行透明传输,所以JSON不需要反序列化 router.get('/', function(req, res, next) { var data = req.query.data res.render('index', { title: 'Express' });});MQTT客户端MQTT数据约定我们的数据分别放在三个Topic下 ...

July 2, 2019 · 1 min · jiezi

nodejs-seiqelizeauto-pg-pghstore-postgres-解决无法下载表结构问题

答案来源: https://stackoverflow.com/que... npm install -g sequelize-autonpm install -g pg pg-hstore降低版本 解决问题 npm install -g pg@6.4.2

July 2, 2019 · 1 min · jiezi

基于Nodejs和nodexlsx开发一个Excel组件

简述需求我们在用Excel表格的时候经常需要合并数据,比如统计整个年级的数据的时候,需要合并每个班成绩的时候,这就需要一个快速将很多张数据类型相同表合并的组件 需要的配置现在Node.js能够很好进行文件的读写操作,只要能读写就能合并社区已经有了封装好的一些插件比如node-xlsx,cnpm install就能用引入Node本来的文件读写工具const xlsx = require('node-xlsx')const fs = require('fs')获取文件决定路径,定义合并数组我们将文件放到excle里,而合并的结果放到result里 const _file = `${__dirname}/excel/`const _output = `${__dirname}/result/`let dataList = [ { name: '提交成绩',//你sheet的名字 data: [] }]读出数据fs.readdir(_file, function(err, files) {}里可以将所有_file读出来放到files数组里 解析数据let excelData = xlsx.parse(`${_file}${item}`)//拼接的绝对路径console.log(excelData)console.log(excelData[0].data) 提取数据进行拼接files.forEach((item, index) => { try { console.log(`开始合并:${item}`) let excelData = xlsx.parse(`${_file}${item}`) if (excelData) { if (dataList[0].data.length > 0) { excelData[0].data.splice(0, 1) } dataList[0].data = dataList[0].data.concat(excelData[0].data) } } catch (e) { console.log('excel表格内部字段不一致,请检查后再合并。') } })写入新的文件 var buffer = xlsx.build(dataList) fs.writeFile(`${_output}resut.${new Date().getTime()}.xlsx`, buffer, function (err) { if (err) { throw err } console.log('\x1B[33m%s\x1b[0m', `完成合并:${_output}resut.${new Date().getTime()}.xlsx`) }) ...

July 1, 2019 · 1 min · jiezi

puppeteer爬虫

利用空闲时间,学习了下puppeteer爬虫,我也想爬取下网上的资源1.部分apipuppeteer.launch(options)参数名称参数类型参数说明ignoreHTTPSErrorsboolean在请求的过程中是否忽略 Https 报错信息,默认为 falseheadlessboolean是否以“无头”的模式运行chrome,也就是不显示UI,默认为trueexecutablePathstring可执行文件的路径,Puppeteer 默认是使用它自带的 chrome webdriver, 如果你想指定一个自己的 webdriver 路径,可以通过这个参数设置slowMonumber使 Puppeteer 操作减速,单位是毫秒。如果你想看看 Puppeteer 的整个工作过程,这个参数将非常有用argsArray(String)传递给 chrome 实例的其他参数,比如你可以设置浏览器窗口大小具体参数timeoutnumber等待chrome实例启动的最长时间,默认是3000ms,如果传入0,则不限制时间dumpioboolean是否将浏览器锦程stdout和stderr导入到process.stdout和process.stderr中,默认为falseuserDataDirstring设置用户数据目录,默认linux是在~/.config目录,window 默认在 C:Users{USER}AppDataLocalGoogleChromeUser Data, 其中 {USER} 代表当前登录的用户名envObject指定对chromium可见的环境变量,默认为process.envdevtoolsboolean是否为每个选项卡自动打开DevTools面板,这个选项只有当headless设置为false的时候有效browser对象api方法名说明browser.close()返回一个promise对象,用于关闭浏览器browser.newPage()返回一个promise对象,创建一个page实例page对象方法名说明page.goto(url[, options])返回一个promise对象,url是目标链接page.waitForSelector()等待某个选择器的元素加载之后,这个元素可以是异步加载的page.evaluate(pageFunction[,args])返回一个可序列化的普通对象,pageFunction 表示要在页面执行的函数, args 表示传入给 pageFunction 的参数2.爬取电影网站const puppeteer = require('puppeteer');/* 爬虫的目标链接地址: 豆瓣电影 */const url = `https://movie.douban.com/tag/#/?sort=R&range=0,10&tags=`;const sleep = time => new Promise(resolve => { setTimeout(resolve, time);});(async () => { console.log('crawler start to visit the target address'); /* dumpio 是否将浏览器进程stdout和stderr导入到process.stdout和process.stderr中 */ const browser = await puppeteer.launch({ args: ['--no-sandbox'], dumpio: false }); const page = await browser.newPage(); await page.goto(url, { waitUntil: 'networkidle2' }); await sleep(3000); await page.waitForSelector('.more'); for(let i = 0; i < 1; i++) { await sleep(3000); await page.click('.more'); } const result = await page.evaluate(() => { let $ = window.$; let nodeItems = $('.list-wp a'); let links = []; /* 获取对应的元素节点 */ if(nodeItems.length >= 1) { nodeItems.each((index, item) => { let elem = $(item); let movieId = elem.find('div').data('id'); let title = elem.find('.title').text(); let rate = Number(elem.find('.rate').text()); let poster = elem.find('img').attr('src').replace('s_ratio_poster','l_ratio_poster'); links.push({ movieId, title, rate, poster, }) }) } return links; }); browser.close(); console.log(result)})();3.爬取网站内容生成pdf文件const puppeteer = require('puppeteer');const url = 'https://cn.vuejs.org/v2/guide/';(async () => { const browser = await puppeteer.launch(); const page = await browser.newPage(); await page.goto(url, { waitUntil: 'networkidle0' }); /* 选择你要输出的那个PDF文件路径,把爬取到的内容输出到PDF中,必须是存在的PDF,可以是空内容,如果不是空的内容PDF,那么会覆盖内容 */ let pdfFilePath = './index.pdf'; /* 根据你的配置选项,我们这里选择A4纸的规格输出PDF,方便打印 */ await page.pdf({ path: pdfFilePath, format: 'A4', scale: 1, printBackground: true, landscape: false, displayHeaderFooter: false }); browser.close();})()正在努力学习中,若对你的学习有帮助,留下你的印记呗(点个赞咯^_^)往期好文推荐: ...

July 1, 2019 · 2 min · jiezi

前端nodejsvueexpress部署到阿里云

vue+express部署到阿里云使用的阿里云服务器CentOS 7.3一,登录对应的服务器 二,在服务器进行对应的初始化yum install -y nginxyum install -y nodejsyum install -y mariadbnpm install pm2 -gnpm install express -g1.初始化数据库 mysql_secure_installation #直接执行初始化命令,会弹出交互配置信息Enter current password for root (enter for none):#初次进入密码为空,直接回车New password: #输入要为root用户设置的数据库密码。Re-enter new password: #重复再输入一次密码。Remove anonymous users? [Y/n] y #删除匿名帐号Disallow root login remotely? [Y/n] n #是否禁止root用户从远程登录Remove test database and access to it? [Y/n] y #是否删除test数据库,想留着也随意Reload privilege tables now? [Y/n] y #刷新授权表,让初始化后的设定立即生效2.登录数据库mysql -u root -p 创建对应的数据库,执行对应的sql文件 navicat连接阿里云数据库 阿里云开放mysql的3306端口 1.登入mysql2.GRANT ALL PRIVILEGES ON . TO 'root'@'%' IDENTIFIED BY '您的数据库密码' WITH GRANT OPTION;3.flush privileges; ...

July 1, 2019 · 1 min · jiezi

OpenKruise-云原生应用自动化引擎正式开源

在本次 KubeCon 上,阿里云将为全球用户分享阿里巴巴超大规模云原生落地实践、云原生前沿技术与应用包括OpenKruise 开源项目、开放云原生应用中心(Cloud Native App Hub),同时将重磅发布边缘容器、云原生应用管理与交付体系等产品和服务。 OpenKruise Github 地址: https://github.com/openkruise/kruise “云原生应用自动化引擎”加持下的阿里“云原生”随着云原生概念的兴起,越来越多的应用开始尝试在云原生的土壤上耕耘。那么什么是云原生,简而言之,云原生就是一套能够充分利用“云”的能力,高效构建与交付应用的方法论集合,使得应用容器化的用户可以充分的利用云的弹性、“不可变基础设施”等优势专注于自身核心业务价值。 当前,阿里巴巴基础设施的云原生演进与升级也正在如火如荼的进行。而在阿里巴巴上云的过程中,阿里内部在超大规模的互联网场景中,已经开始进行大量的云原生的理念落地实践,比如轻量级容器化,阿里巴巴经济体正在大规模推进应用的轻量级容器化,从而达成利用容器的敏捷、一致等特性快速构建符合云原生理念的电商站点交付的能力,适应类似“双十一”大促的严苛技术需求;再比如说云原生应用管理, 阿里巴巴经济体正在将 Kubernetes 等项目的应用编排与自动化能力,穿透到上层运维框架当中,驱动电商应用按照云原生的技术理念进行编排、交付和运行。 在阿里巴巴经济体的整体云原生化过程当中,阿里的技术团队逐渐沉淀出了一套紧贴上游社区标准、适应互联网规模化场景的技术理念与最佳实践。这其中,最重要的无疑是如何对应用进行自动化的发布、运行和管理。 OpenKruise:来自阿里经济体云原生化历程的宝贵经验与最佳实践在 KubeCon 上海,阿里云容器平台团队正式宣布了重量级项目 - OpenKruise(以下简称Kruise)的开源。 Kruise 是 cruise的谐音,'k' for Kubernetes. 字面意义巡航,豪华游艇。寓意Kubernetes上应用的自动巡航,满载阿里巴巴多年应用部署管理经验。 Kruise 的目标是automate everything on Kubernetes ! Kruise 项目源自于阿里巴巴经济体应用过去多年的大规模应用部署、发布与管理的最佳实践,源于容器平台团队对集团应用规模化运维,规模化建站的能力,源于阿里云Kubernetes服务数千客户的需求沉淀。Kruise 借力于云原生社区,集成阿里巴巴云原生实践之精华,反哺社区,指引业界云原生化最佳实践,少走弯路。 Kruise 核心在于自动化,我们将从不同维度解决 Kubernetes之上应用的自动化,包括,部署,升级,弹性扩缩容,Qos调节,健康检查,迁移修复等等。此次Kruise开源的内容主要在应用部署,升级方面,即一套增强版controller组件用于应用的部署和级和运维。后续,Kruise会依次开源智能化的弹性扩缩容组件,以及应用Qos自调节能力的组件等。 Kruise Controllers:将 Kubernetes 的“控制器模式”进行到底以下内容主要介绍 Kruise Controllers - 一套用于 Kubernetes 之上应用自动化部署管理的 controller 组件。众所周知,Kubernetes 项目的核心原理,就是“控制器模式”。目前,Kubernetes 项目默认已经提供了一套 Controller 组件,例如 Deployment, Statefulset, DaemonSet 等,这些 Controller 提供了比较丰富的应用部署和管理功能。但是,随着 Kubernetes 的使用范围越来越广,真实的企业与规模性场景中的业务诉求与上游 Controller 功能不匹配的情况也越来越常见。以阿里巴巴为例:阿里巴巴内部的 Kubernetes 集群需要服务涵盖50几个 BU,上万种应用。这个体量非常庞大,对规模性和高可用性带来了巨大的挑战。与此同时,阿里云上的 Kubernetes 服务也接入了上千家企业客户,收集并支撑了各种各样的客户需求。这些诉求与最后阿里经济体的实践经验,最终促成了 Kruise 开源项目的诞生。 ...

July 1, 2019 · 1 min · jiezi

Web-安全漏洞之文件上传

文件上传漏洞及危害文件上传漏洞是指网络攻击者上传了一个可执行的文件到服务器上,当开发者没有对该文件进行合理的校验及处理的时候,很有可能让程序执行这个上传文件导致安全漏洞。大部分网站都会有文件上传的功能,例如头像、图片、视频等,这块的逻辑如果处理不当,很容易触发服务器漏洞。这种漏洞在以文件名为 URL 特征的程序中比较多见。嗯,是的说的就是世界上最好的语言 PHP。例如用户上传了一个 PHP 文件,拿到对应文件的地址之后就可以执行它了,其中的危害自然不言而喻。那在 Node.js 中就没有文件上传漏洞了么?答案肯定是否的。除了可执行文件外,还有以下几个潜在的问题。<!--more--> 文件名用户上传的文件里有两个东西经常会被程序使用,一个是文件本身,还有一个就是文件名了。如果文件名被用来读取或者存储内容,那么你就要小心了。攻击者很有可能会构造一个类似 ../../../attack.jpg 的文件名,如果程序没有注意直接使用的话很有可能就把服务器的关键文件覆盖导致程序崩溃,甚至更有可能直接将 /etc/passwd 覆盖写上攻击者指定的密码从而攻破服务器。 有些同学可能会说了,/ 等字符是文件名非法字符,用户是定义不了这种名字的。你说的没错,但是我们要知道我们并不是直接和用户的文件进行交互的,而是通过 HTTP 请求拿到用户的文件。在 HTTP 表单上传请求中,文件名是作为字符串存储的。只要是合法的 HTTP 请求格式,攻击者可以构造请求中的任何内容用于提交给服务器。 POST /upload HTTP/1.1Host: test.comConnection: keep-aliveContent-Length: 4237161Accept: */*Origin: http://test.comUser-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.100 Safari/537.36Content-Type: multipart/form-data; boundary=----WebKitFormBoundary9pQqgBGwpDfftP8lReferer: http://test.comAccept-Encoding: gzip, deflateAccept-Language: en,zh-CN;q=0.9,zh;q=0.8,zh-TW;q=0.7,da;q=0.6------WebKitFormBoundary9pQqgBGwpDfftP8lContent-Disposition: form-data; name="file"; filename="../../attack.jpg"Content-Type: image/jpeg------WebKitFormBoundary9pQqgBGwpDfftP8l--HTML 和 SVG虽然说 Node.js 在文件上传服务端可执行程序的漏洞没有 PHP 那么高,但是除了服务端可执行之外我们还有客户端可执行问题,所以还是要做好防备。假设用户可以上传任意格式的文件,而如果攻击者上传了 HTML 文件后可以配合 CSRF 攻击进一步制造 XSS 攻击。 如果你是一个图片上传的接口,如果你仅限制 HTML 格式的话也存在问题,因为图片中有一种特别的存在是 SVG 格式。SVG 是一种矢量图形格式,它使用 XML 来描述图片,在其内部我们是可以插入 <html>, <style>, <script> 等 DOM 标签的。如果不对 SVG 中的文件内容进行过滤的话,也会发生意想不到的效果。 ...

July 1, 2019 · 1 min · jiezi

你不能不知道的Koa实现原理

前言什么?这是一篇源码解读文章 ? 那一定很枯燥!不看。我把 Koa 的核心实现剥离成了 7 个小节,循序渐进,一步一步带你走进 Koa 的内心世界,不会干巴巴的很难啃~ 我没用过 Koa,会不会看不懂 ?每个核心实现我都做了实践和 demo~,非常简单易懂(看完不懂打我)~ Koa 是什么Koa 是一个新的 web 框架,由 Express 幕后的原班人马打造, 致力于成为 web 应用和 API 开发领域中的一个更小、更富有表现力、更健壮的基石。 通过利用 async 函数,Koa 帮你丢弃回调函数,并有力地增强错误处理。 Koa 并没有捆绑任何中间件, 而是提供了一套优雅的方法,帮助您快速而愉快地编写服务端应用程序。Koa 组成Koa 源码非常的精简,只有 4 个文件 application.js (koa 入口,koa 中间件管理,请求处理,本文重点讲解.)context.js (koa 上下文对象,代理 request 与 response 对象的一些方法和属性)request.js (koa 请求对象,基于 node http 模块请求信息进行二次封装,并定义一些属性和方法,引用一张图,可以很清楚的看到 request 做了什么)response.js (koa 响应对象,基于 node http 模块响应信息进行二次封装,并定义一些属性和方法,引用一张图,可以很清楚的看到 response 做了什么)遇见 koa1 . Koa 之 EventEmitter 2 . Koa 之 Http 模块 ...

July 1, 2019 · 1 min · jiezi

eggjs框架开发环境搭建

1.创建工程目录1.1 创建一个文件夹叫 my-egg-application$ mkdir my-egg-application不熟悉命令行的同学也可以手动创建文件夹 2.初始化项目模板2.1 进入工程目录$ cd my-egg-application2.2 下载项目模板$ npm init egg --type=simple这里的type参数是需要下载的模板名字,具体的值可以参考egg在github上的模板egg-boilerplate-simple。在egg的项目主页中找类似格式的项目名字,如egg-boilerplate-[命令行中type的值] 3.安装项目依赖$ npm install --registry=https://registry.npm.taobao.org在这里下载比较慢的同学可以使用淘宝的cnpm,具体的cnpm安装方法请自己网上搜索,很多类似的教程。我为了方便直接就指定了淘宝仓库地址 https://registry.npm.taobao.org 4.运行服务运行项目就比较简单了,运行npm的命令 $ npm run dev在浏览器中打开地址:http://localhost:7001,就能看到服务已经启动完成。是不是特别简单!!! 5.其它npm run [命令]这是npm相关的使用方法,可以打开项目根目录下的package.json文件 // 这是一个最简单的package.json{ "name": "my-egg-application", "version": "1.0.0", "description": "", "scripts": { "dev": "egg-bin dev", }, "author": "mufeng", "license": "ISC"}在script下面看到了dev命令,它其实运行了egg-bin dev这个命令,egg-bin这个模块以后我会再写一篇文章介绍一下

June 30, 2019 · 1 min · jiezi

nodejs的Web开发框架的选择

node.js的Web开发框架的选择?这个问题貌似在其它的后端开发领域不存在。没错,我说的就是隔壁的Java。我要是写java的应用,可以毫不犹豫的选择Spring。但是node可选择的余地多的多。 现有node服务端框架1. Express、Koaexpress框架肯定不用说了,写node服务这块的同学肯定是非常熟悉的框架了。我早期的时候也是express的粉丝。 优点:express的框架结构非常的简单。经过短暂的学习就可以用来开发一个项目。非常适合作为node新手的入门框架。 缺点:开发阶段:Express的缺点也很明显,由于结构简单,自由度高。每个人会有不同的文件编排方式。前期设计阶段需要人工的把项目约定做好。但是团队来新人了,又要重新学习项目约定,无形中增加了学习成本。说到底还是缺乏项目的工程化约束。在项目的开发初期需要自己手工的搭建一些通用的脚手架代码,来方便的之后的开发工作。开发流程会拖的比较长。 运维阶段:由于node单进程,js主线程运行的机制。如果在js主线程中没有做好错误的处理。会导致进程意外退出的问题。这在项目运行阶段是不可接受的问题。需要进程守护的机制来保证程序的健壮性。Express和Koa需要依赖第三方的工具来实现。如PM2。讲道理这些功能应该是一个web开发框架应该具备的基础功能。 总结:不管是Express还是Koa框架。还是处于比较简单的基于http模块的封装。在Reuest和Response这两个对象基础上进行扩展开发。我们业务开发团队需要的是稳定、快速的开发框架。实际开发中往往需要在Express和Koa的基础上封装大量的代码,来适应不同的业务场景,这对追求快速开发的互联网行业是不受欢迎的。 2. Egg.js我在2018年3月份开始接触egg框架。发现这是一个具备较完善功能的web开发框架。 优点:方便、好用、少写很多的脚手架级别的代码。专注于业务逻辑的开发。内建插件机制,兼容koa插件。约定大于配置。内置多进程管理。阿里巴巴开源。文档是中文的。估计没点自虐倾向的同学一般都会选择母语版本的文档来看吧。 缺点:由于目前的使用层面还不够深入。除了对应用配置方式的不太满意外,没有发现大的开发痛点。项目开发实践下来,开发效率杠杠的。 总结:估计写到这里,应该能看出我对egg框架的喜爱程度了。那么下面学习一下egg入门。

June 30, 2019 · 1 min · jiezi

学习nodejs服务开发这一篇就够了持续更新中

开头要说的话接触node.js后端开发也有几年时间了。经过几个项目的实践。不可否认,在后端服务领域。node.js还是有一定的用武之地的。当然在平时的实践中也发现了node体系的一些问题和不足。所以写篇文章分享一下我探索node服务的经历。 目录由于工作繁忙,不定时的会更新的。。。 0. JavaScript语言的学习1. 框架的选择node.js的Web开发框架的选择?Egg.js官网了解一下2. 快速入门3. web综合开发4. 模板引擎Nunjuck的使用5. redis的使用6. 持久层框架的使用---Sequelize7. 持久层框架的使用---Mongoose8. 如何优雅的使用Sequelize9. 如何优雅的开发Router10. egg-security插件的使用11. 定时任务12. 多进程模式开发13. 日志开发的实践14. 全局同一异常处理实践15. 中间件(过滤器)开发实践16. 框架的启动自定义实践17. Service层开发实践18. 一些方便开发的小技巧19. 从Java那边借鉴过来的优秀实践20. 服务的监控21. 容器化部署22. 使用TypeScript开发服务

June 30, 2019 · 1 min · jiezi

nodejs无限级分类

最近做一个商城,商城有个分类,后端是node,前端vue,数据库mysql,然后想用nodejs输出无限级分类。一开始想的是嵌套递归查询,发现会有一个先后顺序的问题,也想过async和await,感觉也不靠谱。 想做分类,然后又想可以扩展。数据库查询出来后,把格式转换了一下。 var arr = [ {"id":1,"name":"手机数码","pid":0}, {"id":2,"name":"家用电器","pid":0}, {"id":3,"name":"美妆护肤","pid":0}, {"id":4,"name":"钟表珠宝","pid":0}, {"id":16,"name":"小米","pid":1}, {"id":17,"name":"华为","pid":1}]完整代码: router.get('/list',function (req,res) { let sql = 'select id,pid,name,img from table_category' var arr = [] pool.query(sql,(err,result)=>{ result.forEach((item,index)=>{ arr[index]={id:item['id'],name:item['name'],pid:item['pid'],img:config.static_img_url+item['img']} }) res.send(tree(arr)) function tree(data) { var map = {}; data.forEach(function (item) { map[item.id] = item; //这里的ID根据数据库的字段 }); //console.log(map) var val = []; data.forEach(function (item) { var parent = map[item.pid]; //这里是父级ID---pid if (parent) { (parent.children || (parent.children = [])).push(item); } else { val.push(item); } }); return val; } })})输出JSON ...

June 30, 2019 · 1 min · jiezi

基于阿里云ECS-Centos7-搭建个人网站服务器在大陆

远程连接服务器 1. 通过Xshell 6(推荐) 填主机,然后点击用户身份验证,选择账号密码登陆就好了,当然也可以选择ssh的方式登陆。用这个软件的好处就是方便上传文件,命令为rz -E,如果显示not command,则先安装,命令为```yum install lrzsz```2. 通过cmd连接ssh root@xx.xx.xx.xx 如果提示“”,则用ssh -o StrictHostKeyChecking=no root@xx.xx.xx.xx修改yum源因为大多数安装包都在国外,所以大多数包都无法直接安装。因此,第一次远程连接主机,先更新yum源 首先备份系统自带yum源配置文件/etc/yum.repos.d/CentOS-Base.repomv /etc/yum.repos.d/CentOS-Base.repo /etc/yum.repos.d/CentOS-Base.repo.backup 2. 进入yum源配置文件所在的文件夹 cd /etc/yum.repos.d/3. 下载163的yum源配置文件到上面那个文件夹内wget http://mirrors.163.com/.help/CentOS7-Base-163.repo4. 清除缓存更新yum clean allyum makecacheyum update安装Nginx 1. 添加Nginx yum源sudo rpm -Uvh http://nginx.org/packages/centos/7/noarch/RPMS/nginx-release-centos-7-0.el7.ngx.noarch.rpm2. 安装yum install -y nginx3.启动和设置自启动sudo systemctl start nginx.servicesudo systemctl enable nginx.service4.nginx相关信息 静态文件目录:/usr/share/nginx/html 网站默认站点配置: /etc/nginx/conf.d/default.conf 自定义Nginx站点配置文件存放目录: /etc/nginx/conf.d/ Nginx全局配置: /etc/nginx/nginx.conf 5.测试 服务器中运行curl http://127.0.0.1 //输出相关网页内容http://<ip> //输出Nginx 默认网页安装node.js环境curl --silent --location https://rpm.nodesource.com/setup_8.x | sudo bash -sudo yum -y install nodejs但该方法只能安装6.x版本的node.js,不知道为啥。 ...

June 29, 2019 · 1 min · jiezi

从零开始搭建一个vuessr下

从零开始搭建一个vue-ssr前言上次我们已经实现了从零开始,搭建一个简单的vue-ssr的demo:从零开始搭建一个vue-ssr(上)。那么这次呢,我们基于vue-cli,进行webpack的改造,实现vue-cli版本的vue-ssr。 开始改建补充安装依赖与上一次不同,这次我们基于vue-cli进行改建,已经有了很多依赖库了,但我们任需要补充一个核心: npm install vue-server-renderer修改客户端的webpack配置修改webpack.dev.conf.js文件,添加插件 const vueSSRClientPlugin = require("vue-server-renderer/client-plugin");const devWebpackConfig = merge(baseWebpackConfig,{ plugins:[ new vueSSRClientPlugin() ] });添加了这个配置以后,重新启动项目通过地址就可以访问到vue-ssr-client-manifest.json(http://localhost:8082/vue-ssr-client-manifest.json),页面中出现的内容就是所需要的client-bundle。 修改vue的相关文件此步骤跟从零开始搭建一个vue-ssr(上)大有相似,不重复描述,直接上代码:修改 router/index.js import vueRouter from "vue-router";import Vue from "vue";import HelloWorld from "@/components/HelloWorld";import About from "@/components/About";Vue.use(vueRouter);export default () => { return new vueRouter({ mode:"history", routes:[ { path:"/", component:HelloWorld, name:"HelloWorld" }, { path:"/about", component:About, name:"About" } ] })}修改app.js import Vue from "vue";import createRouter from "./router";import App from "./App.vue";export default (context) => { const router = createRouter(); const app = new Vue({ router, components: { App }, template: '<App/>' }); return { app, router }}修改entry-server.js ...

June 29, 2019 · 2 min · jiezi

从零开始搭建一个vuessr

从零开始搭建一个vue-ssr背景What?SSR是什么?SSR全拼是Server-Side Rendering,服务端渲染。 所谓服务端渲染,指的是把vue组件在服务器端渲染为组装好的HTML字符串,然后将它们直接发送到浏览器,最后需要将这些静态标记混合在客户端上完全可交互的应用程序。 Why?为什么选择SSR?①满足seo需求,传统的spa数据都是异步加载的,爬虫引擎无法加载,需要利用ssr将数据直出渲染在页面源代码中。 ②更宽的内容达到时间(首屏加载更快),当请求页面的时候,服务端渲染完数据之后,把渲染好的页面直接发送给浏览器,并进行渲染。浏览器只需要解析html不需要去解析js。 How?SSR的原理借用下面的一张图,我们来简单阐述一下vue-ssr的原理。 我们可以看到,左侧Source部分就是我们所编写的源代码,所有代码有一个公共入口,就是app.js,紧接着就是服务端的入口(entry-server.js)和客户端的入口(entry-client.js)。当完成所有源代码的编写之后,我们通过webpack的构建,打包出两个bundle,分别是server bundle和client bundle;当用户进行页面访问的时候,先是经过服务端的入口,将vue组建组装为html字符串,并混入客户端所访问的html模板中,最终就完成了整个ssr渲染的过程。 开始搭建创建一个空白目录并初始化在终端输入以下命令 mkdir ssr-democd ssr-demonpm init由于我们这个只是一个demo项目,可以直接一路按回车键,直接忽略配置。完成之后我们可以看到文件夹里面有一个package.json的文件,这就是配置表。 安装依赖该项目需要四个依赖,依次安装 npm install expressnpm install vuenpm install vue-routernpm install vue-server-renderer其中express使我们node端的框架,vue用于创建vue实例,vue-router则用于实现路由控制,最后vue-server-renderer尤为关键,我们实现的vue-ssr依靠于这个库提供的API。在安装依赖完毕之后,我们看到package.json中已经把四个依赖都写上了。 "express": "^4.17.1","vue": "^2.6.10","vue-router": "^3.0.6","vue-server-renderer": "^2.6.10"创建一个node服务在根目录下我们新建一个server.js,用户搭建node服务 const express = require("express");const app = express();app.get('*', (request, response) => { response.end('hello, ssr');})app.listen(3001, () => { console.log('服务已开启')})接着为了后续开发的便利,我们在package.json中添加一个启动命令: "scripts": { "test": "echo \"Error: no test specified\" && exit 1", "server": "node index.js" },接着我们在终端输入 npm run server,然后再浏览器输入localhost:3001,便可以看到页面中的文字被成功渲染。 渲染html页面在上一步我们已经能成功渲染出一个文字,但是ssr并不是主要为了渲染文字,而是渲染一个html模板。 那么,接下来,我们得告知浏览器,我们需要渲染的是html,而不只是text,因此我们需要修改响应头。 同时,引入vue-server-renderer中的createRenderer对象,有一个renderToString的方法,可以将vue实例转成html的形式。(renderToString这个方法接受的第一个参数是vue的实例,第二个参数是一个回调函数,如果不想使用回调函数的话,这个方法也返回了一个Promise对象,当方法执行成功之后,会在then函数里面返回html结构。) 修改server.js如下: ...

June 29, 2019 · 3 min · jiezi

全国站与子站的开发

关于如何合理的进行子站的开发参考 1,vuepress --- 主题定制 2,ant design landing 3,DataV 数据可视化 4,ElementUI 作为依赖 5,jd.com 京东设计思路(模块化) read.jd.com www.jd.com mall.jd.com product.jd.com sales.jd.com 同一套用户体系 6,模块化开发 7,可插拔 8,环境变量 9,分支 git 版本 10,组件化 11, ant design

June 29, 2019 · 1 min · jiezi

比-man-更强悍的命令行工具-cheat

经常使用命令行,比如 curl 测试接口响应时间 for i in {1..10};do curl -o /dev/null -s -w "$i | time_namelookup: %{time_namelookup} | time_connect: %{time_connect} | time_starttransfer: %{time_starttransfer} | time_total: %{time_total}\n" "http://httpbin.org/ip";done1 | time_namelookup: 0.016000 | time_connect: 0.016000 | time_starttransfer: 0.125000 | time_total: 0.1410002 | time_namelookup: 0.016000 | time_connect: 0.016000 | time_starttransfer: 0.094000 | time_total: 0.1090003 | time_namelookup: 0.016000 | time_connect: 0.031000 | time_starttransfer: 0.109000 | time_total: 0.1090004 | time_namelookup: 0.015000 | time_connect: 0.031000 | time_starttransfer: 0.109000 | time_total: 0.1090005 | time_namelookup: 0.031000 | time_connect: 0.031000 | time_starttransfer: 0.109000 | time_total: 0.1090006 | time_namelookup: 0.016000 | time_connect: 0.016000 | time_starttransfer: 0.094000 | time_total: 0.1090007 | time_namelookup: 0.016000 | time_connect: 0.016000 | time_starttransfer: 0.125000 | time_total: 0.1250008 | time_namelookup: 0.000001 | time_connect: 0.016000 | time_starttransfer: 0.141000 | time_total: 0.1410009 | time_namelookup: 0.015000 | time_connect: 0.015000 | time_starttransfer: 0.093000 | time_total: 0.10900010 | time_namelookup: 0.000001 | time_connect: 0.015000 | time_starttransfer: 0.109000 | time_total: 0.125000奈何命令行参数太多,记不住怎么办?这时候你需要个男人,它就是 man ...

June 29, 2019 · 6 min · jiezi

三道-google-风格-git-面试题及其解答

第一题: 把配置文件推送到了远程仓库,怎样删除远程仓库的该配置文件,本地还要用到这个文件。这种操作失误,比较常见。一般这样解决: git rm --cached filenameecho filename >> .gitignore先解释第二步,本地需要,远程仓库不需要,肯定是要把那个文件写入 .gitignore 文件里面。否则以后还要删除。 第一步则是把该文件从 git 的暂存区域中删除。暂存区域,就是 index 区域。 见一下亲人: git 三个区,git rm filename, 会把文件从工作区 Working Directory 和暂存区域 Staging Area 中删除。本地还要用,就不能这么搞。 git rm --cached filename, 则把文件从暂存区域 Staging Area 删除,保留工作区的,我们一般编辑见到的。 这种情况就是已经 commit 了,生成快照,文件进了版本库 Commit History,然后 push, 远程库与本地库同步一下。 这个时候,直接 push 到远程,无效。因为没有新的快照,也就是没有新的 commit id. 本地与远程的历史 log 是一致的。修改文件,add 再 commit, push 提交过去,就会生效。 第二题: git 如何解决代码冲突解决冲突三连git stash git pull git stash pop 操作就是把自己修改的代码隐藏,然后把远程仓库的代码拉下来,然后把自己隐藏的修改的代码释放出来,让 git 自动合并。接着找 <<<<<<<, 哪里冲突哪里改。 ...

June 28, 2019 · 2 min · jiezi

前端设计数据转换层

前言在工作中,经常会遇到,接口的数据格式与页面布局/交互不匹配的情况,需要前端进行处理。 心想:“数据处理与业务无关,单独抽离一个模块来写吧。” 于是,转换层就此诞生。 第一版设计当我设计模块时, 第一步 会明确模块的职责。 转换层——顾名思义,把接口数据格式 转换 成页面所需要格式。 第二步 制定与其他模块对接规则。 因为它是从页面模块抽离出来的,所以只有页面模块才能引用它。 而且逻辑单一(把输入数据处理后输出),所以它只能引用工具模块。 第三步 划分子模块。 模块主要是处理数据的问题,所以根据数据类型的维度划分子模块。 第一版设计大功告成// 消息通知信息的转换方法// transform/notice.jsexport default{ show(data) { .... return ret; }}// 面板页使用// page/dashboardimport noticeModel from 'model/notice.js'; //发送消息通知请求类import noticeTransform from 'transform/notice.js';//转换成页面所需要接口格式const data = await noticeModel.show().then(res => noticeTransform.show(res));缺陷!!! 第一版设计中,我们很难看出某个转换方法,被那一个或几个页面使用。 随着项目复杂度不断增大,以后接手的小伙伴根本就不敢改/删转换层里的代码。 第二版设计在第一版设计中,遇到转换方法与使用页面对应不明确的问题。 思考后,决定调整划分子模块方式,改为根据页面维度划分。 第二版成品// 面板页里的消息通知信息转换方法// transform/dashboard.jsexport default{ noticeShow(data) { .... return ret; }}// 面板页使用// page/dashboardimport noticeModel from 'model/notice.js'; import dashboardTransform from 'transform/dashboard.js';const data = await noticeModel.show().then(res => dashboardTransform.noticeShow(res));缺陷 Again!!! 虽然能清晰识别页面具有那些转换方法, 但是,如果A与B、C...页面,需要对相同的数据转成相同格式。 这样的模块划分,对相似代码抽离是不友好的。 ...

June 28, 2019 · 1 min · jiezi

express基本原理

了解 express 原理之前,你需要先掌握 express 的基本用法。 关于 express 的介绍请看 express 官网。 基本结构先回顾一下 express 使用的的过程,首先是把模块倒入,然后当做方法执行,在返回值中调用 use 处理路由,调用 listen 监听端口。 const express = require('express')const app = express()app.use('/home', (req, res) => { res.end('home')})app.listen(8080, () => { console.log('port created successfully')})根据上面的使用,我们开始构建代码。我们需要写一个 express 方法,返回一个 app 对象,有 use 和 listen 方法。 const http = require('http')const url = require('url')function express() { const app = {} const routes = []; app.use = function (path, action) { routes.push([path, action]) } function handle(req, res) { let pathname = url.parse(req.url).pathname; for (let i = 0; i < routes.length; i++) { var route = routes[i]; if (pathname === route[0]) { let action = route[1]; action(req, res); return; } } handle404(req, res); } function handle404(req, res) { res.end('404') } app.listen = function (...args) { const server = http.createServer((req, res) => { handle(req, res) }) server.listen(...args) } return app}module.exports = express上面代码中的 use 方法的作用是把请求路径跟对应的处理函数存放在一个数组中,当请求到来的时候遍历数组,根据路径找到对应的方法执行。 ...

June 28, 2019 · 3 min · jiezi

深入理解JavaScript的类型转换

前言JavaScript作为一门弱类型语言,我们在每天的编写代码过程中,无时无刻不在应用着值类型转换,但是很多时候我们只是在单纯的写,并不曾停下脚步去探寻过值类型转换的内部转换规则,最近通过阅读你不知道的JavaScript中篇,对js的值类型转换进行了更加深入的学习,在此分享给大家参考学习。概念将值从一种类型转换为另一种类型通常称为类型转换,主要发生在静态语言的编译阶段;强制类型转换则发生在动态语言的运行阶段;JavaScript作为一门典型的动态弱类型语言自然而然采用的是强制类型转换(即隐式强制类型转换和显式强制类型转换);在js的强制类型转换总是返回标量基本类型值,如字符串、布尔、数字,不会返回对象和函数 var a = 42;var b = a + '';//隐式强制类型转换var c = String(a);//显式强制类型转化前情提要在阅读后面的内容之前,我们首先要明白下面几个概念,以方便对后续内容的理解 封装对象 :eg:var a = new String('abc'),a被叫做封装了基本类型的封装对象,还原一个封装对象的值,可以调用valueOf方法;基本类型的几乎所有方法并非来自本身,而是来自于封装对象的原型对象,例如下面例子 const a = 1.2;console.log(a.toFixed(0));//1基本类型数字并不存在toFixed方法,只是在访问该方法时候,js自动封装基本类型为对应的封装对象,再去访问该封装对象的原型上对应的方法,等同于下面例子 const a = 1.2;console.log(new Number(a).__proto__.toFixed());//0ToPrimitive抽象操作:该操作主要是将对象类型转换为基本类型,首先检查某个对象是否有valueOf属性,如果有则返回该对象的valueOf的值,否则调用该对象的toString属性并返回值(如果valueOf返回的不是基本类型则调用toString方法,例如数组的valueOf返回的还是数组,所有ToPrimitive会默认调用toString方法);抽象值操作ToString负责处理非字符串到字符串的强制类型转换,规则如下:1.null转换为'null',undefined转换为'undefined',其他基本类型都调用基本类型的包装对象属性toString()并返回值。const a = 123;const _a = new Number(123);console.log(String(a), _a.toString());//'123' '123'const b = true;const _b = new Boolean(true);console.log(String(b), _b.toString());//'true' 'true'2.数字的字符串化遵循通用规则,但是极小极大数字使用指数形式const a = 1.07*1000*1000*1000*1000*1000*1000*1000;console.log(String(a));//'1.07e+21'3.对于普通对象来说,除非自行定义,否则toString()返回Object.prototype.toString()的值,其他对象有自己的toString()方法则调用自己的该方法const b = {};console.log(String(b));//[object object]4.数组的默认toString()方法进行了重新定义,将所有单元字符串化以后再用‘,’连接起来const a = [1, 2, 3];console.log(String(a));//'1,2,3'5.JSON字符串化 5-1.JSON字符串化和toString的效果基本相同,只不过序列化的结果总是字符串5-2.JSON对于不安全的值(undefined,function(){},symbol)直接忽略,数组中则以null填充5-3.对象循环引用直接报错,正则表达式序列化为{}ToNumber负责处理非数字到数字的强制类型转换,规则如下:1.true转换为1,false转换为0,undefined转换为NaN,null转换为0console.log(Number(null));//0console.log(Number(undefined));//NaNconsole.log(Number(true));//1console.log(Number(false));//02.对字符串的处理遵循数字常量的相关规定/语法,处理失败时返回NaNconsole.log(Number('123'));//123console.log(Number('0b111'));//7console.log(Number('0o123'));//83console.log(Number('0x123'));//291console.log(Number('123a'));//NaNconsole.log(Number('a123'));//NaN3.对象(包括数组)会首先按照ToPrimitive抽象操作被转换为相应的基本类型值,再按照前两条规则处理;如果某个对象即不存在valueOf方法也不存在toString方法,则会产生TypeError错误(例如Object.create(null)不存在以上两种方法)const arr = [1, 2, 3];console.log(Number(arr));//NaNconsole.log(Number(arr.toString()));//NaNconst num = new Number(123);console.log(Number(num));//123console.log(Number(num.valueOf()));//123const bool = new Boolean(true);console.log(bool.valueOf());//trueconsole.log(Number(bool));//1console.log(Number(bool.valueOf()));//1const obj1 = { toString:()=>"21"}const obj2 = { valueOf:()=>"42", toString:()=>"21"}const obj3 = { a:1}console.log(Number(obj1));//21console.log(Number(obj2));//42console.log(obj3.toString());//[object Object]console.log(Number(obj3));//NaNconst obj = Object.create(null);console.log(Number(obj));//TypeError上述obj1,obj2分别调用toString和valueOf方法,obj3调用原型上的toString()方法 ...

June 28, 2019 · 3 min · jiezi

JS使用模块化实现用户名密码检测密码强弱验证验证码生成

html 页面用户名:<input type="text" id="username" name=""><br>密码:&nbsp;&nbsp;<input type="password" id="userpwd" name=""><br>密码强度:<span id="mm"></span><br>验证码:<input type="text" id="yzm" name=""> <span id="testSpan"></span><br><input type="button" id="login" value="登录" name=""/>JS代码块var module = (function() { var testSpan,msg,Slength,userpwd,userpwd,yzm; function print(msg) { alert(msg); } ;//弹出消息框 //简单的验证码 function createyzm(testSpan,Slength){ var str = "0123456789qwertyuioplkjhgfdsazxcvbnmQWERTYUIOPLKJHGFDSAZXCVBNM"; var list = str.split(""); for (i = 0; i < Slength; i++) { var index = Math.floor(Math.random() * list.length); testSpan.style.backgroundColor="#000";//验证码背景颜色 testSpan.style.color="#fff";//验证码颜色 testSpan.style.display="block"; testSpan.style.textAlign ="center"; testSpan.style.width="70px";//长度 testSpan.innerHTML += list[index]; } return testSpan.innerHTML;};function checkUserpwd(userpwd,errSpan) { var numCount = (/[0-9]/g).test(userpwd) ? 1 : 0; var lowCount = (/[a-z]/g).test(userpwd) ? 1 : 0; var uppCount = (/[A-Z]/g).test(userpwd) ? 1 : 0;//二元运算符 switch (numCount + lowCount + uppCount) { case 3: errSpan.innerHTML = "强"; break; case 2: errSpan.innerHTML = "中"; break; default: errSpan.innerHTML = "弱"; break; return true; }}; function check(username,userpwd){ var uremeber = /^[a-zA-Z0-9_-]{4,16}$/; var username=username.replace(" ",""); var userpwd=userpwd.replace(" ","");//去空格防止sql注入 if(username==""||userpwd=="") { print("用户名密码不能为空"); return false; } if(!uremeber.test(username)||!uremeber.test(userpwd)){ print("用户名密码为为4-16位但不限于(数字,字母,下划线)"); history.go(0);//刷新页面 return false; }else { //... return true; } };return { checkUserpwd: checkUserpwd,//两个参数userpwd,errSpan errspan为提示密码强弱提示性文字,接受类型是js dom对象 注:jq对象用test方法, // userpwd 为用户密码, check: check,//用户名,密码,验证码 createyzm: createyzm,//testSpan,Slength 两个参数testSpan是验证码span标签dom类型对象,Slength是验证码长度 print: print//alert弹窗 };})();var yzm=module.createyzm(document.getElementById("testSpan"),4);document.getElementById("userpwd").onblur = function(){ module.checkUserpwd(this.value,document.getElementById('mm'));}document.getElementById("login").onclick=function(){ var a= module.check(document.getElementById("username").value,document.getElementById("userpwd").value);if(a==true ){ if(document.getElementById("yzm").value==yzm){ alert("登录成功!"); }else{ alert("验证码错误,请重新输入!"); history.go(0);//刷新页面 } }}

June 28, 2019 · 1 min · jiezi

前端开发应知网站

作为一名前端开发者(所有程序员)最起码遇到bug就算不会解决也应该会搜解决问题的答案跟大家分享一下我个人积累的网站:MDN开发者文档:https://developer.mozilla.org...菜鸟教程:https://www.runoob.com/W3CSchool:http://www.w3school.com.cn/JavaScript教程网:https://zh.javascript.info/一行代码搞定bug监控:https://www.fundebug.com/?tds...阿里巴巴图标库:https://www.iconfont.cn/UI颜色布料:https://www.materialui.co/colorslogo在线制作,有很多:http://www.logofree.cn/在线开发工具:https://tool.lu/c/developer渐进式web应用程序核对表:https://developers.google.cn/...在线存放图片的地址:https://sm.ms/MintUI:https://mint-ui.github.io/#!/...ElementUI:https://element.eleme.cn/#/zh-CNiViewUI:https://www.iviewui.com/Layui:https://www.layui.com/cubeUI:https://didi.github.io/cube-u...Antd:https://ant.design/index-cnMUI:http://dev.dcloud.net.cn/mui/Animate.CSS:https://daneden.github.io/ani...ECharts:https://www.echartsjs.com/ind...HighCharts:https://www.highcharts.com.cn/蚂蚁数据可视化:http://antv.alipay.com/zh-cn/...热力图插件:https://www.patrick-wied.at/s...百度地图开放平台:https://passport.baidu.com视频监控直播的插件VLC:https://www.videolan.org/力扣JS题库:https://leetcode-cn.com/ES6入门(阮一峰大佬的):http://es6.ruanyifeng.com/babel(将ES6代码转为ES5代码):https://babeljs.io/处理时间与日期的JS库:http://momentjs.cn/Lodash:https://www.lodashjs.com/你可能不需要jQuery:http://youmightnotneedjquery....云小蜜智能机器人API:https://help.aliyun.com/produ...易万维源接口:https://www.showapi.com/api/a...短信验证码接口:https://www.mysubmail.com/sms...草料二维码生成器:https://cli.im/RAP2假数据接口:http://rap2.taobao.org/易文档接口:https://easydoc.xyz - - 感谢旅行呱大佬帮助我们丰富资源,他的CSDN主页为:https://me.csdn.net/atsoar免费接口:http://www.bejson.com/knownjs...假数据接口(测试用):http://jsonplaceholder.typico...jQuery插件库:http://www.jq22.com/search轮播图插件:https://www.swiper.com.cn/放大镜插件:http://www.elevateweb.co.uk/i...响应式瀑布流插件:http://www.jq22.com/jquery-in...小表情的emoji:https://emojipedia.org/Postman测试前端请求后端接口:https://www.getpostman.com/coding代码托管平台:https://coding.net/码云代码托管平台:https://gitee.com/github代码托管平台:https://github.com/SVN代码托管平台:https://svnbucket.com/?ADTAG=...SVN代码托管中心:http://www.svnchina.com/禅道:https://www.zentao.net/git官网:https://git-scm.com/npm官网:https://www.npmjs.com/掘金网:https://juejin.im/思否:https://segmentfault.com/知乎:https://www.zhihu.com阿里云:https://www.aliyun.com腾讯云:https://cloud.tencent.com/GitBook:https://legacy.gitbook.com/@l...妙味课堂:https://miaov.com/慕课网:https://www.imooc.com/html中文网:https://www.html.cn/hCoder:http://www.hcoder.net/扣丁学堂:http://www.codingke.com/React官网:https://reactjs.org/Redux官网:https://react-redux.js.org/React Router:https://reacttraining.com/rea...Vue官网:https://cn.vuejs.org/NodeJS官网:http://nodejs.cn/uni-app:https://uniapp.dcloud.io/微信开发者平台:https://developers.weixin.qq....DvaJS:https://dvajs.com/jQuery官网:https://jquery.com/jQuery API中文文档:http://jquery.cuishifeng.cn/ZeptoJS官网:https://zeptojs.com/cheerioJS安装地址:https://www.npmjs.com/package...RequireJS官网:https://requirejs.org/Sass官网:https://sass-lang.com/Less官网:http://lesscss.org/Bootstrap官网:https://www.bootcss.com/VScode快捷键介绍:https://www.cnblogs.com/bindo...Socket通信:https://socket.io/JSONPlaceholder:http://jsonplaceholder.typico...web开发互助网:http://hz.uyi2.com/

June 28, 2019 · 1 min · jiezi

深入浅出nodejs学习笔记node内存管理及GC机制

在node.js中,内存主要分为两个部分,堆内存和栈内存 堆内存(heap):存放对象和闭包上下文,v8使用垃圾回收机制管理堆内存栈内存(stack):存放局部变量,栈内存的分配比较简单,当程序离开某作用域后,其栈指针下移(回退),整个作用域的局部变量都会出栈被回收。可通过process.memoryUsage()查看内存使用情况,单位为字节 rss是分配的整体物理内存,包括堆、栈、代码段heapTotal是整体堆内存heapUsed是被使用的堆内存external: 代表v8管理的绑定到javascript的c++对象的内存通常情况下,v8对堆内存的分配为,64位系统下1.4GB,32位系统下0.7GB node将堆内存分为新生代和老生代 新生代:存放存活时间较短的对象老生代:存放存活时间较长的对象在分代的基础上,对于新生代使用Scavenge算法,老生代使用Mark-Sweep和Mark-Compact 对象已经经历过一次复制翻转操作。To空间使用比例超过25%,设置25%的比例是因为,To空间将变成From空间,如果使用比例过高,可能影响后续的内存分配。Scavenge算法Scavenge算法主要通过Cheney算法实现,Cheney算法将新生代分为两部分,叫做SemiSpace空间,该两个SemiSpace空间分别叫做From空间和To空间。From空间为使用空间,To空间为闲置空间。分配新的内存空间时会分配到From空间。开始进行垃圾回收时,会检查From空间内的对象,将存活对象复制至To空间,释放From空间,然后将From空间和To空间进行翻转。Scavenge算法的优点是速度快,无内存碎片,缺点是,只能使用一般的内存空间,但是由于新生代都是存活时间较短的对象,需要复制的对象属于少数。属于使用空间换时间的典型算法。只适用于新生代垃圾回收策略。 在新生代回收阶段,如果存活对象满足以下两个条件,会将该对象移动至老生代,该阶段叫做晋升 对象已经经历过一次复制翻转操作。To空间使用比例超过25%,设置25%的比例是因为,To空间将变成From空间,如果使用比例过高,可能影响后续的内存分配。Mark-Sweep(标记清除)和Mark-Compact(标记整理)Mark-Sweep分为标记和清除两个阶段,在标记阶段遍历所有老生代对象,对还存活的对象进行标记,在清除阶段释放未被标记的内存。Mark-Sweep最大问题是,经过清除阶段后,内存空间会出现不连续状态,影响以后的内存分配。可能出现一个对象,需要分配内存空间很大,而现有内存碎片不能完成分配,进行提前垃圾回收,该回收是没有必要的。为了解决Mark-Sweep的内存碎片问题,Mark-Compact被提了出来,它主要是在标记之后,将存活的对象往一侧移动,完成移动后,直接释放边界外的内存。 Incremental Marking该三种算法执行时,为了保证javascript应用逻辑与垃圾回收机制保持一致,垃圾回收时都需要将应用逻辑停止,这种情况称之为“全停顿”新生代Scavange算法,由于分配内存空间较小,存活对象较少,全停顿影响较少。但是老生代内存空间较大,存储对象较多,全停顿时间较长。所以V8为了减少停顿空间,从标记阶段开始,改为增量标记(Incremental Marking)。将整体拆分为小“步进”,没执行一个“步进”就让js应用逻辑执行一会儿, 垃圾回收与应用逻辑交替进行。

June 28, 2019 · 1 min · jiezi

liunx服务器永久启动mongoDB和express-generator

踩在巨人的肩膀上,总结如何在关掉shell之后让服务依然保持运行1、永久启动mongod:第一步:找到 mongodb.conf ,用vim编辑,如果为空的话,加入代码: dbpath=/root/mongoDB/data #数据库路径logpath=/root/mongoDB/log/mongodb.log #日志传输路径port=27017 #端口号 fork=true #以守护进程的方式运行,创建服务器进程journal=falsestorageEngine=mmapv1第二步:运行命令 mongod -f ××/××/mongodb.conf(mongodb.conf所在的路径),成功之后就永久启动了2、以 forever 永久启动express generator:第一步:下载forever,npm i forever -g第二步:找到forever安装路径,(默认一般安装在 usr/local/bin/)配置全局环境变量(修改根目录 etc/profile 文件)第三步:找到你express generator生成的目录文件夹,修改package.json文件,找到 "start": "node ./bin/www" ## 将node 改为 forever start 即可第四步:启动forever forever start app.js然后,就算你关闭shell,也可以访问你的项目了最后,附上forever的一些常用命令:(不定时更新)//1. 简单的启动 forever start ×××//2. 停止启动 forever stop ××× //3. 停止所有运行的nodeforever stopall //4. 监听当前文件夹下的所有文件改动 forever start -w ××× //5.显示所有运行的服务 forever list

June 28, 2019 · 1 min · jiezi

ReactNodeExpress搭建一个前后端demo

1.简述demo:以前用过原生JS写计算器,这里我想要react来写,并且用数据库记录每次计算公式和结果,并且可发请求获取后台部分:建立数据库后,通过Node.js的express框架写接口操作数据库前端页面:通过react的router控制路由编写,然后通过axios发送请求给后台2.Express简介和上手express官方链接,具体的通过对官方文档的学习还是比较容易上手的,这里我就简要说明2.1新建express项目webstorm可以直接点击newporject来选择expressApp 2.2项目结构与路由挂载├── app.js # 应用的主入口├── bin # 启动脚本├── node_modules # 依赖的模块├── package.json # node模块的配置文件├── public # 静态资源,如css、js等存放的目录├── routes # 路由规则存放的目录└── views # 模板文件存放的目录 2.3路由路由写到routes下面 var express = require('express');var router = express.Router();router.get('/list', function(req, res, next) {//pool.query是数据库的连接池下面会说到 pool.query('SELECT * FROM counter;', function (err, rows, fields) { if (err) throw err; console.log(rows) res.json( rows ) })});router.post('/add', function(req, res, next) { console.log(req.body) //var mysqltl='INSERT INTO counter(id,counter, time) VALUES(\''+req.body.counter+'\','+'now()'+');' var mysqltl=`INSERT INTO counter(counter, time) VALUES('${req.body.counter}',now());` console.log(mysqltl) pool.query(mysqltl, function (err, rows, fields) { if (err) throw err; console.log(rows) res.json( rows ) })});2.4允许跨域请求设置在app.js里面添加 ...

June 28, 2019 · 3 min · jiezi

Eslint笔记

1:Eslint概念ESLint 是在 ECMAScript/JavaScript 代码中识别和报告模式匹配的工具,它的目标是保证代码的一致性和避免错误。说白了就是你们小组长想让你们按照他写代码的风格去写代码(完全插件话的配置插件和规则),指不定这规则还是从哪个大团队伸手党拿来的(各种比较规范化的一些规则配置) 2:Eslint的常用配置parser:配置解析器,这个解析器是把源代码解析成AST之后去验证规则的,默认的是Espree,解析ECMAScript5特性,当然也可以使用其他的解析器,目前常用的babel-eslint、typescript-eslint-parser等parserOptions:解析的配置,列举常用配置 ecmaVersion: 解析的ECMAScript的版本,默认ECMAScript5sourceType: 制定解析的模块类型。script或者module(ECMAScript模块)env: 配置解析的环境,env的值会为eslint默认的设置一些全局的变量,比如browser,会设置浏览器中的全局变量,jQuery,添加jQuery的全局变量,这些值可以并存,给出链接查看env环境变量globals: 全局变量,源文件中访问未定义变量会no-undef,定义一些全局变量就可以使用globals console.log(globalVarible);// 'globalVarible' is not defined no-undef// globals: {// globalVarible: false;// }// will make it rightplugins: 插件,通过插件去增强eslint的功能,以及可以输出一些额外的配置,可以去npm去搜一下eslint-plugin-*去找一些常用的插件rules: 规则,这里就是常用的设置一些项目中使用的规则,规则设置可以是eslint默认的一些rules也可以是插件中一些rules,比如vue/*,0->off关闭规则 1->warn报出警告 2->error报出错误级别,如果一些规则有其他选项,可以通过数组字面量形式配置 { "rules": { "eqeqeq": "off", "curly": "error", "quotes": ["error", "double"] // 代码中使用双引号,级别为错误 } }root: 当前文件的eslint配置文件会在当前目录寻找,如果不存在,会从父级依次寻找到根目录,为了将eslint限制为该项目,可以在项目的根目录下的eslint配置root:true,会停止继续向上寻找extends: 继承的概念,从前一个配置中继承规则,同时也可以去修改继承到的规则,修改分为两类,只修改错误级别或者整体覆盖规则,比如可以使用eslint:recommended来开启eslint的核心规则,这些规则可以在下面的链接中找到eslint:recommended规则,当然也可以继承一些第三方已有的配置,比如eslint-plugin-vue module.exports = { extends: [ // add more generic rulesets here, such as: // 'eslint:recommended', 'plugin:vue/recommended' ], rules: { // override/add rules settings here, such as: // 'vue/no-unused-vars': 'error' } }以上的配置基本可以满足日常开发的需要了,inline Comments可以在源码文件中帮助我们控制Eslint,比如忽略某行验证,在源码文件中添加全局变量等等 ...

June 28, 2019 · 2 min · jiezi

手写一个-Express初级版

序:因为公司 Node 方面业务都是基于一个小型框架写的,这个框架是公司之前的一位同事根据 Express 的中间件思想写的一个小型 Socket 框架,阅读其源码之后,对 Express 的中间件思想有了更深入的了解,接下来就手写一个 Express 框架 ,以作为学习的产出 。 在阅读了同事的代码与 Express 源码之后,发现其实 Express 的核心就是中间件的思想,其次是封装了更丰富的 API 供我们使用,废话不多说,让我们来一步一步实现一个可用的 Express。 本文的目的在于验证学习的收获,大概细致划分如下: 服务器监听的原理路由的解析与匹配中间件的定义与使用核心 next() 方法错误处理中间件定义与使用内置 API 的封装正文:在手写框架之前,我们有必要去回顾一下 Express 的简单使用,从而对照它给我们提供的 API 去实现其相应的功能: 新建一个 app.js 文件,添加如下代码: // app.jslet express = require('express');let app = express();app.listen(3000, function () { console.log('listen 3000 port ...')})现在,在命令行中执行: node app.js 可以看到,程序已经在我们的后台跑起来了。 当我们为其添加一个路由: let express = require('Express');let app = express();app.get('/hello', function (req, res) { res.setHeader('Content-Type', 'text/html; charset=utf-8') res.end('我是新添加的路由,只有 get 方法才可以访问到我 ~')})app.listen(3000, function () { console.log('listen 3000 port ...')})再次重启:在命令行中执行启动命令:(每次修改代码都需要重新执行脚本)并访问浏览器本地 3000 端口: ...

June 28, 2019 · 5 min · jiezi

大话node真的是单线程吗

老鸟:伸着懒腰,看着窗外明媚的阳光,喝一口清茶,心情大美。一天的好心情莫过于此。老鸟:菜鸟,你这消失了大半个月,忙什么呢?菜鸟:听说node最近很火,这不趁着年轻,多储存点知识储备呢!老鸟:那你说说你对node的理解?菜鸟:node的三大特点:单线程,事件驱动,非阻塞I/O...老鸟:说说你理解的单线程?菜鸟:打个比方:皇上处理朝政时,不需要事必躬亲,要学会放权。所有的朝政大臣向丞相报告,由丞相进行整理,并把最后的方案向皇上报告。由皇上进行定夺。老鸟:理解的不错嘛,可你有没有想过,node既然是单线程,又是如何实现异步和非阻塞I/O呢?菜鸟:node是事件驱动,所有的事件做成一个队列,顺序执行的呀老鸟:你这样说也没错,但node是支持多线程的。当你使用MAC的活动监视器你会发现,实际的进程数为6 const http = require('http'); http.createServer((req, res) => { res.end('hello'); }).listen(8000, () => { console.log('server is listening: ' + 8000); });菜鸟:是因为线程池的原因吗?老鸟:不错嘛,还知道线程池呢?但非也非也。老鸟:首先,线程池是按需创建的,当你加载文件IO时,通过ab模拟访问后,你会发现,线程数发生了变化,这是因为,线程池中默认的默认值为4。菜鸟:竟然不是线程池?老鸟:你忽视了node集成了V8引擎?。Node.js启动后会创建V8实例,而V8实例是多线程的。菜鸟:若有所思中...老鸟:主线程在任何时候,都不会阻塞吗?菜鸟:老鸟,经你这么一分析,我可不敢肯定的回答了老鸟:当然不是的。主线程在特定条件下也是会阻塞的。Node.js的事件循环中有一个阶段是Poll。poll阶段在特定情况下是会阻塞的。老鸟:当然,你也不要羞愧,知识是永无止境的,我只是提醒你,要不断探索,不断质疑,才能不断求知。菜鸟:受教了,给你点个赞,顺便收藏一下 参考资料https://nodejs.org/en/docs/gu...

June 28, 2019 · 1 min · jiezi

Koa源码浅析

Koa源码十分精简,只有不到2k行的代码,总共由4个模块文件组成,非常适合我们来学习。 参考代码: learn-koa2 我们先来看段原生Node实现Server服务器的代码: const http = require('http');const server = http.createServer((req, res) => { res.writeHead(200); res.end('hello world');});server.listen(3000, () => { console.log('server start at 3000');});非常简单的几行代码,就实现了一个服务器Server。createServer方法接收的callback回调函数,可以对每次请求的req res对象进行各种操作,最后返回结果。不过弊端也很明显,callback函数非常容易随着业务逻辑的复杂也变得臃肿,即使把callback函数拆分成各个小函数,也会在繁杂的异步回调中渐渐失去对整个流程的把控。 另外,Node原生提供的一些API,有时也会让开发者疑惑: res.statusCode = 200;res.writeHead(200);修改res的属性或者调用res的方法都可以改变http状态码,这在多人协作的项目中,很容易产生不同的代码风格。 我们再来看段Koa实现Server: const Koa = require('koa');const app = new Koa();app.use(async (ctx, next) => { console.log('1-start'); await next(); console.log('1-end');});app.use(async (ctx, next) => { console.log('2-start'); ctx.status = 200; ctx.body = 'Hello World'; console.log('2-end');});app.listen(3000);// 最后输出内容:// 1-start// 2-start// 2-end// 1-endKoa使用了中间件的概念来完成对一个http请求的处理,同时,Koa采用了async和await的语法使得异步流程可以更好的控制。ctx执行上下文代理了原生的res和req,这让开发者避免接触底层,而是通过代理访问和设置属性。 看完两者的对比后,我们应该会有几个疑惑: ctx.status为什么就可以直接设置状态码了,不是根本没看到res对象吗?中间件中的next到底是啥?为什么执行next就进入了下一个中间件?所有中间件执行完成后,为什么可以再次返回原来的中间件(洋葱模型)?现在让我们带着疑惑,进行源码解读,同时自己实现一个简易版的Koa吧! ...

June 28, 2019 · 7 min · jiezi

Nodejs中的ES模块现状

作者:Tobias Nießen翻译:疯狂的技术宅 原文:https://jaxenter.com/es-modul... 未经允许严禁转载 几乎每种编程语言都能将组成程序的代码拆分为多个文件。 在 C 和 C++ 中 #include 指令就用于这个目的,而 Java 和 Python 有 import 关键字。 JavaScript 是迄今为止为数不多的例外之一,但新的 JavaScript 标准(ECMAScript 6)通过引入所谓的 ECMAScript 模块来改变这一点。所有主流浏览器都支持这个新标准 —— 只有 Node.js 似乎落后了。这是为什么?新的 ECMAScript(ES)模块与以前的语言版本不完全兼容,因此使用的 JavaScript 引擎需要知道每一个文件是“旧” JavaScript 代码还是“新”模块。 例如在 ECMAScript 5 中引入的许多程序员首选的严格模式曾经是可选的,必须明确启用才行,同时它在 ES 模块中始终处于活动状态。因此,以下代码段在语法上可以解释为传统的 JavaScript 代码和 ES 模块: a = 5;作为经典的 Node.js 模块,这相当于 global.a = 5,因为未声明变量 a 并且未明确激活严格模式,因此 a 被视为全局变量。如果你尝试加载与 ES 模块相同的文件,则会收到错误 “ReferenceError:a is not defined”,因为未声明的变量可能无法在严格模式下使用。 浏览器通过<script> 标记的扩展解决了区别问题:没有 type 属性或带有 type="text/javascript" 属性的脚本仍然在传统模式下运行,而当脚本使用 type ="module" 属性时则作为模块处理。由于这种简单的分离,现在所有流行的浏览器都支持新的模块。 Node.js 中的实现要困难得多:2009年发明的 JavaScript 应用程序框架使用 CommonJS 标准模块,该标准基于 require 函数。此函数可以随时根据其相对于当前运行模块的路径加载另一个模块。新的 ES 模块也是由它们的路径定义的,但是 Node.js 是如何知道正在加载的模块是遗留的 CommonJS 还是 ES 模块的呢?仅仅基于语法是不够的,因为即使不使用新关键字的 ES 模块也不兼容CommonJS模块。 ...

June 28, 2019 · 3 min · jiezi

分享一个Nodejs-Koa2-MySQL-Vuejs-实战开发一套完整个人博客项目网站

这是个什么的项目?使用 Node.js + Koa2 + MySQL + Vue.js 实战开发一套完整个人博客项目网站。 博客线上地址:www.boblog.comGithub地址:https://github.com/liangfengbo/nodejs-koa-blog解决了什么问题?服务端:使用 Node.js 的 Koa2 框架二次开发 Restful API。前端:Vue.js 打造了前端网站和后台管理系统。项目包含什么功能? Koa2服务端 管理员与权限控制文章文章分类评论文章前端博客网站 Vue.js后台管理系统 Vue.js项目的特点Koa 与 Koa 二次开发API多 koa-router 拆分路由require-directory 自动路由加载异步编程 - async/await异步异常链与全局异常处理Sequelize ORM 管理 MySQLJWT 权限控制中间件参数验证器 Validatornodemon 修改文件自动重启前后端分离使用 Vue.js 搭建前端网站和后台管理系统如何使用和学习?数据库启动项目前一定要在创建好 boblog 数据库。 # 登录数据库$ mysql -uroot -p密码# 创建 wxapp 数据库$ CREATE DATABASE IF NOT EXISTS boblog DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;克隆项目首先使克隆项目,然后进入项目根目录使用命令安装包,最后命令启动项目,代码会根据模型自动创建数据库表的。 根目录都是 Node.js + Koa2 API开发源代码,根目录下的 web 文件夹下都是前端网站项目源代码,根目录下的 admin 文件夹下都是后台管理系统的源代码。 ...

June 27, 2019 · 1 min · jiezi

前后端分离mock-server方案-Moco

虽然前后端分离已经流行很多年了,仍有很多团队不能够充分的利用前后端分离的优势。主要体现在前端过分依赖服务环境, 将高效的约定分工合作模式理解很浅。 在这里推荐一种mock server的解决方案。 mock server的方案有很多: 1. Java API 2. JUnit @Rule 3. Node.js (npm) module 4. Grunt plugin 5. Docker container等这里推荐较为灵活简单的解决方案【Moco】 源项目github地址: https://github.com/dreamhead/... 搭建步骤 1 下载Moco Download Standalone Moco Runnerhttp://central.maven.org/mave...2 启动Java Moco进程 java -jar moco-runner-<version>-standalone.jar http -p 12306 -g main.json3 配置main.json 最佳实践: 配置main.json 引入其他的json [ { "include" : "foo.json" }, { "include" : "mock.json" } ]4 编写配置文件: 1 mock.json [ { "request": { "uri": "/mock" }, "response": { "text": "Hello, MOTO" } } ] 2 foo.json [ { "request": { "uri": "/foo", "queries": { "f": { "match": ".*" // anyNumber anyString } } }, "response": { "json": { // 此处为json body } } } ] 5 修改的配置文件及时生效 ...

June 27, 2019 · 1 min · jiezi

NAPI模块的编写与发布

前言作为nodejs的底层开发语言,c++具有高性能、复用性好等优势,c++编写的chrome v8引擎与libuv、http-parser、zlib等等一起构成了现在大前端技术的基础——nodejs。 nodejs也提供了编写c++扩展以提高性能的方法。但是原生 C++ 模块的开发一直是一个被人冷落的角落,其中一部分原因在于Node程序员有一部分来自于前端,c++的学习成本很高可能没有系统的C++知识,另一部分原因是node的c++扩展机制很复杂,需要理解v8的各种概念,包括隔离示例、虚拟机、上下文、句柄、内存回收(gc)、模板等等。更难过的是,随着版本的更替,一个编译好的插件发布没多久,就会因为版本的不兼容不得不重新编译以适应版本。 关于C++插件开发的发展史,可以参考以下这篇文章 从暴力到 NAN 再到 NAPI——Node.js 原生模块开发方式变迁N-API简介N-API是node8.3新增的实验性特性,在node10中正式启用,下面是官网的说明 N-API (pronounced N as in the letter, followed by API) is an API for building native Addons. It is independent from the underlying JavaScript runtime (ex V8) and is maintained as part of Node.js itself. This API will be Application Binary Interface (ABI) stable across versions of Node.js. It is intended to insulate Addons from changes in the underlying JavaScript engine and allow modules compiled for one version to run on later versions of Node.js without recompilation.Addons are built/packaged with the same approach/tools outlined in the section titled C++ Addons. The only difference is the set of APIs that are used by the native code. Instead of using the V8 or Native Abstractions for Node.js APIs, the functions available in the N-API are used. ...

June 27, 2019 · 1 min · jiezi

适合初学者的koa2mongodb初体验

前言     笔者的前端开发已经有些时日了,对于node一直保留着最初的恐惧,倘若一座不可跨越的高山,思前想后终于迈出最后一步,踏入了开拓自己视野的新视界,希望在看这篇文章的你可以一起跟我动手尝试。     如果你是个急性子,我就提供一波传送门 github:https://github.com/tenggouwa/...      你可以先star,再拉代码,慢慢的看这篇文章。 KOAnext generation web framework for node.js面向node.js的下一代web框架。 由Express团队打造,特点:优雅、简洁、灵活、体积小。几乎所有功能都需要通过中间件实现。 环境搭建node node官方下载地址: https://nodejs.org/zh-cn/down...mongodb mac下mongodb安装教程: https://www.jianshu.com/p/724...windows下mongodb安装教程: https://blog.csdn.net/zhongka...robomongo(mongodb数据库可视化--免费): https://robomongo.org/download本地安装nodemon nodemon会监听你的代码,当有变动的时候自动帮你重启项目(好东西)npm: https://www.npmjs.com/package...yarn(选装)---代替npm/cnpmhomebrew(选装)---包版本管理工具Hello World!!!创建目录 mkdir node-app && cd node-appnpm inityarn add koa -Stouch app.js在编辑器中打开app.js并输入以下代码 const Koa = require('koa');const app = new Koa(); app.use(async ctx => { // ctx.body 即服务端响应的数据 await ctx.body = 'Hello world!!!';})// 监听端口、启动程序app.listen(3000, err => { if (err) throw err; console.log('runing at 3000'); })启动app.js node app 或 nodemon app本地访问localhost:3000got it!!!!KoaRouter安装koa-router ...

June 27, 2019 · 2 min · jiezi

windows下Nodejs多版本切换

原文首发于我的博客,欢迎点击查看获得更好的阅读体验~前言因为electron-vue开发实战中所遇到的问题ERROR in Template execution failed: ReferenceError: process is not defined是nodejs版本导致的,而其它项目中需要用到v10.16.0版本的nodejs,所以需要在windows下安装多个版本进行切换。查了一下资料,让我发现了nvm-windows。 正式开始卸载现有node请注意,在安装NVM for Windows之前,您需要卸载任何现有版本的node.js. 还删除可能保留的任何现有nodejs安装目录(例如,C:\ Program Files \ nodejs)。NVM生成的符号链接不会覆盖现有(甚至是空的)安装目录。 卸载现有的npm您还应该删除现有的npm安装位置(例如C:\ Users <user> \ AppData \ Roaming \ npm),以便正确使用nvm安装位置。 安装使用在releases中下载最新版本nvm-setup.zip,解压后,是一个安装文件,直接安装即可。 由于国内在一些情况下有些特殊。Node.js 官方镜像源又在国外,经常通过 nvm 安装 Node.js 时,速度比较慢,或者没有响应。根据这种情况,nvm 允许更改安装的镜像源,我们可以将镜像源切换到国内的淘宝提供的镜像源。 set NVM_NODEJS_ORG_MIRROR=http://npm.taobao.org/mirrors/node安装node// nvm install <version> [arch]nvm install 12.3.1切换版本nvm use 12.3.1 [32|64]卸载nodenvm uninstall 12.3.1其它命令//查看安装的node版本列表nvm list //or ls//启用node.js版本管理nvm on//禁用node.js版本管理nvm off// 设置node镜像。国内可以使用:https://npm.taobao.org/mirrors/node/nvm node_mirror <node_mirror_url>// 设置npm镜像。中国人可以使用https://npm.taobao.org/mirrors/npm/nvm npm_mirror <npm_mirror_url>升级nvm-windows要升级nvm-windows,请运行新安装程序。它将安全地覆盖需要更新的文件,而无需触及node.js安装。确保使用相同的安装和符号链接文件夹。如果您最初安装到默认位置,则只需在每个窗口上单击“下一步”,直到完成为止。 注意请注意,您安装的任何全局npm模块都不会在已安装的各种版本的node.js之间共享。此外,您正在使用的node版本可能不支持某些npm模块,因此请在工作时注意您的环境。

June 27, 2019 · 1 min · jiezi

使用nodered操作数据库

缘起node-red是目前为止我用到的最好的物联网开发工具,它既可以将相关经验沉淀为节点,又可以灵活快速的定制开发,快速的满足用户的奇葩需求。美中不足的是,它是用node写的,在数据处理、库的丰富性上不及Java、Python。这么好的产品,可惜中文资料太少,2019年下半年我会把node-red手册翻译出来,并写一个系列的文章,请大家监督。先写数据库这一篇,就是发现node-red的文档,特别是结点模块的文档太少了,这导致一个简单的东西,摸熟需要大半天,有文档一个小时就能搞定。MySQL、SQLServer、Oracle是最常用到的三种数据库,下面我们就以这三种数据库为例,讲讲怎么用node-red操作数据库。 操作MySQL 与 SQLServer在node-red仓库中,目前操作MySQL 有三种节点, node-red-node-mysql、node-red-contrib-sqldbs、node-red-contrib-odbc。前两种用法差不多,但node-red-contrib-sqldbs支持 Mysql、MSsql、Postgresql、SQLLite四种数据库。安装这个节点可以搞定四种数据库,大家应该都会选它吧。 配置节点创建一个sqldbs节点在左侧的palette中,搜索sql就可以看到相关节点,往右侧一拖,我们就得到了一个sqldbs节点。配置sqldbs节点双击sqldbs节点进入配置界面,先编辑数据库,创建一个数据库连接,里面填写的内容无非是IP、端口、用户名、密码、数据库名。这里需要注意的是在Dialect一行需要选择数据库类型,Mysql、MSsql、Postgresql、SQLLite 的填写内容并没有区别。配置数据库操作类型,这个动作有点多余,从SQL语句可以解析出SQL操作类型,node-red-node-mysql不需要做这个配置。导入SQL语句sqldbs节点会从msg.topic这个变量中拿SQL,但是其它节点的输出通常是放在msq.payload中(比如MQTT节点、Inject节点),从Inject 节点到sqldbs节点,中间需要一个转换节点,我用了一个function节点。 在Inject 节点中,我注入了一个Json串,该串中包含了我要写入的信息,这也能模拟我从其它节点拿到数据的格式。 { "name": "longtt", "age": 21, "class_name": "401"}在function节点中,我把json串的信息组装到了SQL语句中,并替换了msg var insertOneStudent="INSERT INTO student(name, age, class_name) VALUES ('%s', %d, '%s')";var newMsg = {"topic": util.format(insertOneStudent, msg.payload.name, msg.payload.age, msg.payload.class_name)} return newMsg;执行点击注入节点按钮,会触发注入操作,在右侧的调试信息窗口,我们可以看到调试信息。去数据库查询,我们可以看到相关数据记录已经成功创建。 操作OracleOracle 数据库操作有两个难点,一个是安装麻烦,一个是节点文档非常少。 安装Oracle操作节点在node-red节点仓库中,有两个节点 node-red-contrib-oracledb、node-red-contrib-oracledb-mod, node-red-contrib-oracledb已经过时了,节点安装失败。所以我把精力放到节点 node-red-contrib-oracledb-mod 上,但是也遇到了种种问题。 无法定位到Oracle Client 库 配置好OracleDB的连接配置后,OracleDB节点会去建数据库连接,这个过程失败了。 Oracle-server error connection to 119.3.59.75:1521/orcl: DPI-1047: Cannot locate a 64-bit Oracle Client library: "libclntsh.so: cannot open shared object file: No such file or directory". See https://oracle.github.io/odpi/doc/installation.html#linux for helpNode-oracledb installation instructions: https://oracle.github.io/node-oracledb/INSTALL.htmlYou must have 64-bit Oracle client libraries in LD_LIBRARY_PATH, or configured with ldconfig.If you do not have Oracle Database on this computer, then install the Instant Client Basic or Basic Light package from http://www.oracle.com/technetwork/topics/linuxx86-64soft-092277.html这和Oracle的律师有很大关系,我用过的数据库工具都需要单独安装从Oracle官网下载的instantclient,node-red也不例外。 于是我就到Oracle官网下载了一个instantclient(具体版本取决于你的操作系统版本),然后在我的.bashrc中添加了以下几条配置(注意路径是我机器的路径)。重启了一下node-red,之后OracleDB节点连接就成功了。 ...

June 27, 2019 · 1 min · jiezi

云开发数据库又增新技能

开篇彩蛋由于近期小程序·云开发将上线付费功能(付费功能针对非基础资源配额,基础资源配额仍可免费使用)。为了给开发者更充足的时间进行调整,对于截止 2019-06-21 日前通过邮件申请调整的配额(非基础资源配额)的截止日期统一延长至 2019-08-31。 为了方便开发者进行功能开发并提高开发效率,「小程序·云开发」近期进行了一系列的功能优化。开发者可通过下载最新 Nightly Build 版的开发者工具进行功能体验。 云开发的成长,需要你支持为了给开发者提供更优质的服务,我们诚邀大家参加小程序·云开发问卷调查。我们将根据问卷调查中的反馈信息调整后续版本的功能,感谢大家的支持! 欢迎开发者扫一扫二维码或点击以下链接填写小程序·云开发调查问卷。 问卷链接:https://wj.qq.com/s2/3880935/... 新技能大揭秘数据库高级操作:数据库高级操作是小程序·云开发提供的一种十分灵活的数据库操作能力。通过数据库高级操作,开发者可以更加便捷和高效地对数据库中的大量数据进行进行增删查改/CRUD操作。 云函数支持单文件更新:为了优化云函数代码的上传体验,云函数新增单文件更新功能。开发者可在需上传文件右键选择云函数增量上传体验单文件更新能力。 云函数获取当前环境:小程序·云开发提供 getWXContext 方法用于在云函数中获取微信调用上下文(AppID、环境ID、openid 以及 unionid),使开发者可以更灵活的使用多套资源环境。 云调用:云调用是云开发提供的基于云函数使用小程序开放接口的能力,目前覆盖服务端调用、开放数据等场景。进一步降低了小程序的开发门槛。 HTTP API:HTTP API 是云开发提供的一种小程序外访问云开发资源的能力。使用 HTTP API 开发者可在已有服务器上访问云资源,实现与云开发资源的互通。 开发调试Network 面板展示云开发请求信息:从微信开发者工具 1.02.1905302 及基础库 2.7.1 起,在小程序 Network 面板中会显示云开发请求信息,包括调用所实际请求的环境 ID、请求体、JSON 回包、耗时、及调用堆栈。 本地调试:云函数开发可以不再需要频繁上传测试,只需用 IDE 在本地调试完成再上传到现网验证。开发者可在云函数根目录右键选择本地调试来开启本地调试界面。 如果你有关于使用云开发CloudBase相关的技术故事/技术实战经验想要跟大家分享,欢迎留言联系我们哦!比心!

June 27, 2019 · 1 min · jiezi

webpack-vuecli3-压缩图片

webpack vue-cli3 压缩图片yarn add image-webpack-loaderconfig.module.rules.push({ test: /\.(png|jpe?g|gif|svg)(\?.*)?$/, use:[{ loader: 'image-webpack-loader' options: {bypassOnDebug: true} }]})

June 26, 2019 · 1 min · jiezi

Electron选择文件文件夹对话框非原创传播

文章转载自:https://www.jianshu.com/p/e71...,感谢文章作者,成功完成选择文件夹 的功能 1.第一种方法,纯js代码其原理是:利用input标签的file类别,打开选择文件对话框通过input标签的change事件,将选择的文件返回。为了使每次选择的文件都得到更新,在input对象用完后每次都移除出html中,下次使用时再重新创建并添加到html中。代码如下:/**         *按钮事件实现函数         *原理:利用input标签的file类别,打开选择文件对话框         *通过change事件,将选择的文件返回。为了使每次选择的文件都得到更新,         *在input对象用完后每次都移除出html中,下次使用时再重新创建并添加到html中         */        btnClickFun:function(dir){            // 创建input标签            var inputObj=document.createElement('input')                // 设置属性                inputObj.setAttribute('id','_ef');                inputObj.setAttribute('type','file');                inputObj.setAttribute("style",'visibility:hidden');                if(dir){ // 如果要选择路径,则添加以下两个属性                    inputObj.setAttribute('webkitdirectory', "");                    inputObj.setAttribute('directory', "");                }                // 添加到DOM中                document.body.appendChild(inputObj);                // 添加事件监听器                inputObj.addEventListener("change",this.updatePath);                // 模拟点击                inputObj.click();        },        // 打开文件选择器input标签的change事件响应        updatePath:function(){            var inputObj = document.getElementById("_ef");            var files = inputObj.files;            console.log(files)            try{                if(files.length > 1){                    alert('当前仅支持选择一个文件')                }                else{                    switch(this.btntype){                        case 'store':                            // 临时变量的值赋给输出路径                            this.outpath = files[0].path;                            break;                        case 'add':                            // 添加文件操作                            this.filepath = files[0].path;                            if(this.addFile(this.filepath)){                                alert("添加成功")                            }                            break;                            default:                            break;                    }                }                // 移除事件监听器                inputObj.removeEventListener("change",function(){});                // 从DOM中移除input                document.body.removeChild(inputObj);            }catch (error) {                alert(error)            }        },btnClickFun函数中创建并设置了input标签属性及监听器,函数updatePath为change事件监听的回调函数。通过input标签对象的files属性获得选中的文件名。2.第二种方法,electron首先在子进程中引入ipcRenderer模块,import {ipcRenderer} from 'electron'利用该模块向主进程发送“open-directory-dialog”消息,配置参数为“openDirectory”或“openFile”,并且设置主进程返回的消息“selectedItem”的回调函数为getPath,// 按钮点击事件        btnClick:function(type){           this.btntype = type;            // 判断点击事件是哪个按钮发出的            switch(type){                case 'store':                // 选择存贮路径                    //this.btnClickFun(true);                    ipcRenderer.send('open-directory-dialog','openDirectory');                    ipcRenderer.on('selectedItem',this.getPath);                    break;                case 'add':                // 添加文件                    //this.btnClickFun(false);                    ipcRenderer.send('open-directory-dialog','openFile');                    ipcRenderer.on('selectedItem',this.getPath);                    break;                case 'remove':                    this.deleteItem();                    break;                case 'pack':                    break;                    default:                    break;            }        },        getPath:function(e,path){            console.log(path)            if(path == null){                    alert('请选择一个文件/文件夹')            }            else{                switch(this.btntype){                    case 'store':                        // 临时变量的值赋给输出路径                        this.outpath = path;                        break;                    case 'add':                        // 添加文件操作                        this.filepath = path;                        if(this.addFile(this.filepath)){                            alert("添加成功")                        }                        break;                        default:                        break;                }            }        },然后在主进程中设置好子进程的消息监听,并且引入dialog模块import { dialog } from 'electron'// 绑定打开对话框事件ipcMain.on('open-directory-dialog', function (event,p) {  dialog.showOpenDialog({    properties: [p]  },function (files) {      if (files){// 如果有选中        // 发送选择的对象给子进程        event.sender.send('selectedItem', files[0])      }  })});这样就可以完成选择文件/文件夹的操作了。3.第一种方法实现的vue组件纯JS实现的文件选择器,需要操作DOM原理:利用input标签的file类别,打开选择文件对话框通过change事件,将选择的文件返回。为了使每次选择的文件都得到更新,在input对象用完后每次都移除出html中,下次使用时再重新创建并添加到html中默认打开文件夹,如果需要选择文件,则需要在调用处配置属性dir='file'属性caption显示按钮的文本信息成功调用后会向父进程发送一个‘btnSelectItem’消息用于返回选中的文件全路径 ...

June 26, 2019 · 2 min · jiezi

这儿有20道大厂面试题等你查收

今年来,各大公司都缩减了HC,甚至是采取了“裁员”措施,在这样的大环境之下,想要获得一份更好的工作,必然需要付出更多的努力。 本文挑选了20到大厂面试题,大家在阅读时,建议不要先看我的答案,而是自己先思考一番。尽管,本文所有的答案,都是我在翻阅各种资料,思考并验证之后,才给出的。但因水平有限,本人的答案未必是最优的,如果您有更好的答案,欢迎给我留言。 本文篇幅较长,希望小伙伴们能够坚持读完,如果想加入交流群,可以通过文末的公众号添加我为好友。 1. new的实现原理是什么?new 的实现原理: 创建一个空对象,构造函数中的this指向这个空对象这个新对象被执行 [[原型]] 连接执行构造函数方法,属性和方法被添加到this引用的对象中如果构造函数中没有返回其它对象,那么返回this,即创建的这个的新对象,否则,返回构造函数中返回的对象。function _new() { let target = {}; //创建的新对象 //第一个参数是构造函数 let [constructor, ...args] = [...arguments]; //执行[[原型]]连接;target 是 constructor 的实例 target.__proto__ = constructor.prototype; //执行构造函数,将属性或方法添加到创建的空对象上 let result = constructor.apply(target, args); if (result && (typeof (result) == "object" || typeof (result) == "function")) { //如果构造函数执行的结构返回的是一个对象,那么返回这个对象 return result; } //如果构造函数返回的不是一个对象,返回创建的新对象 return target;}2. 如何正确判断this的指向?如果用一句话说明 this 的指向,那么即是: 谁调用它,this 就指向谁。 但是仅通过这句话,我们很多时候并不能准确判断 this 的指向。因此我们需要借助一些规则去帮助自己: this 的指向可以按照以下顺序判断: 全局环境中的 this浏览器环境:无论是否在严格模式下,在全局执行环境中(在任何函数体外部)this 都指向全局对象 window; ...

June 26, 2019 · 11 min · jiezi

阿里开源云原生应用自动化引擎-OpenKruise-直击-KubeCon

阿里妹导读:在近期开展的 KubeCon China 2019 上,阿里云将陆续为全球用户分享阿里巴巴超大规模云原生落地实践、云原生前沿技术与应用包括 OpenKruise 开源项目、开放云原生应用中心(Cloud Native App Hub),同时将重磅发布边缘容器、云原生应用管理与交付体系等产品和服务。接下来的三天,阿里妹将连线会场,为你带来实时报道。 2019年6月24日至26日,由 CNCF 主办的云原生技术大会 KubeCon 在中国上海盛装启幕,阿里云容器平台团队正式宣布开源重量级项目 OpenKruise,将基于阿里巴巴经济体多年大规模应用部署、发布与管理最佳实践沉淀的能力开放给业界。 OpenKruise 是阿里巴巴开源的 Kubernetes 之上云原生应用自动化的引擎。Kruise 项目源自于阿里巴巴经济体应用过去多年的大规模应用部署、发布与管理的最佳实践,源于阿里云Kubernetes服务数千客户的需求沉淀。 “云原生应用自动化引擎”加持下的阿里经济体“全面上云”随着云原生概念的兴起,越来越多的应用开始尝试在云原生的土壤上耕耘。那么什么是云原生?简而言之,云原生就是一套能够充分利用“云”的能力,高效构建与交付应用的方法论集合,使得应用容器化的用户可以充分的利用云的弹性和“不可变基础设施”等优势专注于自身核心业务价值。 当前,阿里巴巴基础设施的云原生演进与升级也正在如火如荼的进行。而在这个阿里巴巴经济体整体云化的过程中,阿里内部在超大规模的互联网场景中,已经开始进行大量的云原生的理念落地实践,比如轻量级容器化。 阿里巴巴经济体正在大规模推进应用的轻量级容器化,从而达成利用容器的敏捷和一致等特性快速构建符合云原生理念的电商站点交付的能力,适应类似“双十一”大促的严苛技术需求。再比如说云原生应用管理,阿里巴巴经济体正在将 Kubernetes 等项目的应用编排与自动化能力,穿透到上层运维框架当中,驱动电商应用按照云原生的技术理念进行编排、交付、运行。 在阿里巴巴经济体的整体云原生化过程当中,阿里的技术团队逐渐沉淀出了一套紧贴上游社区标准,适应互联网规模化场景的技术理念与最佳实践。这其中,最重要的无疑是如何对应用进行自动化的发布、运行和管理。 OpenKruise:来自阿里经济体云原生化历程的宝贵经验与最佳实践在 KubeCon 上海,阿里云容器平台团队正式宣布了重量级项目 OpenKruise(以下简称 Kruise)的开源。 Kruise 是 cruise 的谐音,"k" for Kubernetes。字面意义是巡航或豪华游艇,寓意 Kubernetes 上应用的自动巡航,满载阿里巴巴多年应用部署管理经验。 Kruise 的目标是 automate everything on Kubernetes ! Kruise 项目源自于阿里巴巴经济体应用过去多年的大规模应用部署、发布与管理的最佳实践,源于容器平台团队对集团应用规模化运维,规模化建站的能力,源于阿里云 Kubernetes 服务数千客户的需求沉淀。Kruise 借力于云原生社区,集成阿里巴巴云原生实践之精华,反哺社区,指引业界云原生化最佳实践,少走弯路。 OpenKruise 是阿里巴巴开源的 Kubernetes 之上云原生应用自动化的引擎。Kruise 核心在于自动化,我们将从不同维度解决 Kubernetes 之上应用的自动化,包括,部署、升级、弹性扩缩容、Qos 调节、健康检查、迁移修复等等。此次 Kruise 开源的内容主要在应用部署,升级方面,即一套增强版 controller 组件用于应用的部署、升级、运维。后续,Kruise 会依次开源智能化的弹性扩缩容组件,以及应用 Qos 自调节能力的组件等。 ...

June 26, 2019 · 2 min · jiezi

揭秘Vue中的Virtual-Dom

前言Vue.js 2.0引入Virtual DOM,比Vue.js 1.0的初始渲染速度提升了2-4倍,并大大降低了内存消耗。那么,什么是Virtual DOM?为什么需要Virtual DOM?它是通过什么方式去提升页面渲染效率的呢?这是本文所要探讨的问题。 模板转换成视图的过程在正式介绍 Virtual Dom之前,我们有必要先了解下模板转换成视图的过程整个过程(如下图): Vue.js通过编译将template 模板转换成渲染函数(render ) ,执行渲染函数就可以得到一个虚拟节点树在对 Model 进行操作的时候,会触发对应 Dep 中的 Watcher 对象。Watcher 对象会调用对应的 update 来修改视图。这个过程主要是将新旧虚拟节点进行差异对比,然后根据对比结果进行DOM操作来更新视图。简单点讲,在Vue的底层实现上,Vue将模板编译成虚拟DOM渲染函数。结合Vue自带的响应系统,在状态改变时,Vue能够智能地计算出重新渲染组件的最小代价并应到DOM操作上。 我们先对上图几个概念加以解释: 渲染函数:渲染函数是用来生成Virtual DOM的。Vue推荐使用模板来构建我们的应用界面,在底层实现中Vue会将模板编译成渲染函数,当然我们也可以不写模板,直接写渲染函数,以获得更好的控制。VNode 虚拟节点:它可以代表一个真实的 dom 节点。通过 createElement 方法能将 VNode 渲染成 dom 节点。简单地说,vnode可以理解成节点描述对象,它描述了应该怎样去创建真实的DOM节点。patch(也叫做patching算法):虚拟DOM最核心的部分,它可以将vnode渲染成真实的DOM,这个过程是对比新旧虚拟节点之间有哪些不同,然后根据对比结果找出需要更新的的节点进行更新。这点我们从单词含义就可以看出, patch本身就有补丁、修补的意思,其实际作用是在现有DOM上进行修改来实现更新视图的目的。Vue的Virtual DOM Patching算法是基于Snabbdom的实现,并在些基础上作了很多的调整和改进。Virtual DOM 是什么?Virtual DOM 其实就是一棵以 JavaScript 对象( VNode 节点)作为基础的树,用对象属性来描述节点,实际上它只是一层对真实 DOM 的抽象。最终可以通过一系列操作使这棵树映射到真实环境上。 简单来说,可以把Virtual DOM 理解为一个简单的JS对象,并且最少包含标签名( tag)、属性(attrs)和子元素对象( children)三个属性。不同的框架对这三个属性的命名会有点差别。 对于虚拟DOM,咱们来看一个简单的实例,就是下图所示的这个,详细的阐述了模板 → 渲染函数 → 虚拟DOM树 → 真实DOM的一个过程 Virtual DOM 作用是什么?虚拟DOM的最终目标是将虚拟节点渲染到视图上。但是如果直接使用虚拟节点覆盖旧节点的话,会有很多不必要的DOM操作。例如,一个ul标签下很多个li标签,其中只有一个li有变化,这种情况下如果使用新的ul去替代旧的ul,因为这些不必要的DOM操作而造成了性能上的浪费。 为了避免不必要的DOM操作,虚拟DOM在虚拟节点映射到视图的过程中,将虚拟节点与上一次渲染视图所使用的旧虚拟节点(oldVnode)做对比,找出真正需要更新的节点来进行DOM操作,从而避免操作其他无需改动的DOM。 其实虚拟DOM在Vue.js主要做了两件事: 提供与真实DOM节点所对应的虚拟节点vnode将虚拟节点vnode和旧虚拟节点oldVnode进行对比,然后更新视图为何需要Virtual DOM?具备跨平台的优势由于 Virtual DOM 是以 JavaScript 对象为基础而不依赖真实平台环境,所以使它具有了跨平台的能力,比如说浏览器平台、Weex、Node 等。 ...

June 26, 2019 · 2 min · jiezi

nodejsexport报错SyntaxError-Unexpected-token-export

最近做一个项目,写nodejs,又写vue,然后就有点混乱了。当时想引入一个config文件,然后很自然的啪啪啪的敲了下面几行代码 //错误代码export default const config = { static_img_url: 'http://localhost:7001/images/'} 啪啦啪啦,查了一下,说原因是export default中的default是一种特殊的系统变量,export default的含义是把此命令后面的变量赋值给default这个特殊的系统变量,并把它导出到其他模块中使用。如此一来,export default const...或者export default var...等语句就是非常明显的错误了。 好,改了一下如下: //还是错误代码const config = { static_img_url: 'http://localhost:7001/images/'} export default config还是报错: 为什么呢???发现掉坑了,搞混乱了。正确应该如下: //正确代码const config = { static_img_url: 'http://localhost:7001/images/'}module.exports = config其实就是Node和浏览器端所支持的模块规范不同。 关于exports和module.exports 在一个node执行一个文件时,会给这个文件内生成一个 exports和module对象,而module有一个exports属性。 exports = module.exports = {};关于 export 和export default export与export default均可用于导出常量、函数、文件、模块等 在一个文件或模块中,export、import可以有多个,export default仅有一个 通过export方式导出,在导入时要加{ },export default则不需要 export能直接导出变量表达式,export default不行。参考引用:引用

June 26, 2019 · 1 min · jiezi

Nodejs学习记录-workerthreads工作线程

Node.js由于JS的执行在单一线程,导致CPU密集计算的任务可能会使主线程会处于繁忙的状态,进而影响服务的性能,虽然可以通过child_process模块创建子进程的方式来解决,但是一方面进程之间无法共享内存,另一方面创建进程的开销也不小。所以在10.5.0版本中Node.js提供了worker_threads模块来支持多线程,一直以来被人所诟病的不擅长CPU密集计算有望成为历史。源码 --> https://github.com/nodejs/nod...worker_thread 模块中有 4 个对象和 2 个类。 isMainThread: 是否是主线程,源码中是通过 threadId === 0 进行判断的。MessagePort: 用于线程之间的通信,继承自 EventEmitter。MessageChannel: 用于创建异步、双向通信的通道实例。threadId: 线程 ID。Worker: 用于在主线程中创建子线程。第一个参数为 filename,表示子线程执行的入口。parentPort: 在 worker 线程里是表示父进程的 MessagePort 类型的对象,在主线程里为 nullworkerData: 用于在主进程中向子进程传递数据(data 副本)const assert = require('assert');const { Worker, MessageChannel, MessagePort, isMainThread, parentPort} = require('worker_threads');if (isMainThread) { const worker = new Worker(__filename); const subChannel = new MessageChannel(); worker.postMessage({ hereIsYourPort: subChannel.port1 }, [subChannel.port1]); subChannel.port2.on('message', (value) => { console.log('received:', value); });} else { parentPort.once('message', (value) => { assert(value.hereIsYourPort instanceof MessagePort); value.hereIsYourPort.postMessage('the worker is sending this'); value.hereIsYourPort.close(); });}

June 25, 2019 · 1 min · jiezi

Nodejs-前后端分离开发新思路

从事 Web 开发的程序员,对于前后端分离模式多半不陌生,这也是目前主流的 Web 开发模式,具体关于前后端分离的模式可以参看文章《你不得不了解的前后端分离原理!》,在这里写者不进行说明。 好了,让我们进入主题 —— Node.js 前后端分离开发新思路 在进入新思路之前,我们现需要了解“老思路”是什么?(注意:后面的案例都是以全栈工程师为例,即前后端代码在一起) 前后端分离开发常规思路以一种具体情景为例:小牛是一名全栈工程师,喜欢前端后端全干,前端使用目前主流的 Webpack + React 全家桶(或 Vue 全家桶),后端使用 express(或 Koa),小牛在同时开发前后端过程中,开启两个进程(前后端各一个),同时使用 nodemon 热重启后台服务,使用 Webpack Proxy 转发实现跨域请求,然后哼哧哼哧开发。如例子:一个前后端分离的简单案例 Process 1 Process 2 ___________ ____________________| | Proxy | | || FrontEnd | <----> | Nodemon | BackEnd || | | | (cp 1) |------------- ----------------------如上示意,该模式启动需要启动两个进程(前端 和 Nodemon),其中 BackEnd 程序作为子进程挂载在 Nodemon 进程,而且前端和 Nodemon 进程通过 Proxy 转发实现通信。 乍看一下这样挺美好的,但是这种模式的缺陷也很容易暴露出来 传统思路的缺陷BackEnd 程序复杂度提升后,启动时间也变得不可控,每次热启动后台服务时间过长;需要同时开启两个进程,一定程度提高了开发成本那么对于上述的问题,需要介绍一下我们今天的主角! 前后端分离开发新思路依旧是小牛的例子,大牛同样使用小牛相同的前后端技术栈,但不同的是,大牛不使用 Nodemon 实现后端程序的热重启,而是使用类似 Webpack HMR(Hot Module Replacement) 的思路,热更新 Node.js 中的 module,具体实现使用 hot-module-require。 ...

June 25, 2019 · 1 min · jiezi

我用Nodejs的Koa框架搭建了一个静态站点

缘起我用Node.js的Koa2框架搭建了一个静态站点,当然这个站点只是部署在我自己的电脑上,主要用来做一些测试:比如写个小页面,尝试下新技术。以前我在自己的电脑上搭建过很多类似的静态站点,因为用过一年的php,所以我之前都是用php+nginx来搭建站点,最近两年我一直在做前端,php也懒得碰了。前段时间在看一个公开课时(忘了哪个机构和哪位老师了,真抱歉),这个公开课的主要内容是教你如何实现一个简单的koa框架,我当时听了下,然后照葫芦画瓢自己写了个简陋的"koa框架"。后来我想用这个简陋的框架搭建一个静态站点,折腾了下,基本可行,但我好奇koa的源码,于是去瞅了下,然后觉得自己写的实在不咋地。于是还是打算用koa来搭建个静态站点。 Koa用起来很方便其实Node.js已经为web开发提供了很多好的api接口,koa只是对其中的一些api进行了下简单的封装,使我们开发起来更方便。按我的理解,koa的核心(或者说比较好的地方)就是其提供的中间件机制: const app = new Koa()app.use(async ctx => { // 中间件内容1})app.use(async ctx => { // 中间件内容2})app.listen(port, () => { console.log('启动')})这种方式用起来也比较方便。它的核心实现在于(从公开课中学到的),将所有传入的中间件合并为一个,然后递归调用,这里不展开说了。 静态站点需求其实很简单那么用koa搭建一个静态站点也就比较容易了,静态站点也就是主要展示静态文字内容(html)、图片,另外加上一些简单的样式美化(css)和交互(js),也就是我只需要一个web服务器能够提供html、图片(jpg、jpg等)、js、css的发布功能即可。下面通过一个页面的请求来简单分析下站点的实现:1,用户通过浏览器访问站点中的a.html2,web服务器接收到请求后解析请求的文件名、文件类型3,根据上面拿到的文件名去服务器上找相应的文件4,找到了,则设置响应头:状态码(200)、响应内容类型和响应体;没有找到,则设置错误的状态码(如404)和对应的响应体。当然,还有很多细节需要考虑:比如服务器的页面存放目录、以及目录是否可读等等情况。 代码目录src |- app-koa.js // 核心文件,处理请求及返回响应 |- file-util.js // 读取文件的方法集合 |- mime.js // mime类型映射 |- status-code.js // 状态码映射 |- views/ // 存放html文件 |- static/ // 存放js、css、图片等静态资源 代码下面我先把我写的代码贴在下面:主文件app-koa.js const url = require('url')const path = require('path')const Koa = require('koa')const CONTENT_TYPE = require('./mime')const STATUS_CODE = require('./status-code')const { accessFilePromise, statFilePromise, readFilePromise} = require('./file-util')const ROOT = __dirnameconst app = new Koa()app.use(async ctx => { const reqUrl = ctx.request.url let pathname = url.parse(reqUrl).pathname let filePath = '' let fileformat = '' // 对web根路径的访问做单独处理 if (pathname === '/') { pathname = '/index.html' } const ext = pathname.indexOf('.') !== -1 ? pathname.match(/(\.[^.]+)$/)[0] : '.html' if (ext === '.html') { filePath = path.join(ROOT, `/views${pathname}`) fileformat = 'utf-8' } else { filePath = path.join(ROOT, pathname) fileformat = 'binary' } try { const isAccessed = await accessFilePromise(filePath) let fileData = null if (!isAccessed) { const code = STATUS_CODE.ENOENT // 文件或目录不可访问,直接返回404 ctx.res.writeHead(code, { 'Content-Type': CONTENT_TYPE['.html'] }) fileData = await readFilePromise(path.join(ROOT, `/views/error/${code}.html`), 'utf-8') ctx.body = fileData } else { const isFile = await statFilePromise(filePath) if (isFile === true) { fileData = await readFilePromise(filePath, fileformat) } else { // 尝试读取该目录下的index.html fileData = await readFilePromise(`${filePath}/index.html`, 'utf-8') } ctx.res.setHeader('Content-Type', CONTENT_TYPE[ext]) if (ext !== '.html') { ctx.res.setHeader('Content-Length', Buffer.byteLength(fileData)) } ctx.res.writeHead(STATUS_CODE.SUCCESS) ctx.body = fileData if (ext !== '.html') { ctx.res.write(ctx.body, 'binary') } } } catch (err) { ctx.res.writeHead(STATUS_CODE[err.code], { 'Content-Type': CONTENT_TYPE[ext] }) }})app.listen(3000, () => { console.log('Your application is running at http://localhost:3000')})file-util.js -- 文件处理工具 ...

June 25, 2019 · 3 min · jiezi

深入koa源码二核心库原理

最近读了 koa2 的源码,理清楚了架构设计与用到的第三方库。本系列将分为 3 篇,分别介绍 koa 的架构设计和 3 个核心库,最终会手动实现一个简易的 koa。这是系列第 2 篇,关于 3 个核心库的原理。 本文来自《心谭博客·深入koa源码:核心库原理》所有系列文章都放在了Github。欢迎交流和Star ✿✿ ヽ(°▽°)ノ ✿is-generator-function:判断 generatorkoa2 种推荐使用 async 函数,koa1 推荐的是 generator。koa2 为了兼容,在调用use添加中间件的时候,会判断是否是 generator。如果是,则用covert库转化为 async 函数。 判断是不是 generator 的逻辑写在了 is-generator-function 库中,逻辑非常简单,通过判断Object.prototype.toString.call 的返回结果即可: function* say() {}Object.prototype.toString.call(say); // 输出: [object GeneratorFunction]delegates:属性代理delegates和 koa 一样,这个库都是出自大佬 TJ 之手。它的作用就是属性代理。这个代理库常用的方法有getter,setter,method 和 access。 用法假设准备了一个对象target,为了方便访问其上request属性的内容,对request进行代理: const delegates = require("delegates");const target = { request: { name: "xintan", say: function() { console.log("Hello"); } }};delegates(target, "request") .getter("name") .setter("name") .method("say");代理后,访问request将会更加方便: ...

June 24, 2019 · 2 min · jiezi

三步教你用Node做一个微信脱单神器小白可上手

前言不知道大家最近有没有被python版的《微信每日说》刷屏呢,他可是霸占了github的python热门快两周了。我们前端的小伙伴是不是也看着有点眼馋呢,因为毕竟是不那么熟悉的python语言,学起来和用起来肯定没有那么舒服。想想要是用我们熟悉的js语言做一个属于自己的哄女友神器是不是很开心呢!???? 哄女友我们前端开发人员也是很认真的,自动哄女友(基友)神器我们也可以做! 项目介绍其实《微信每日说》小项目早在三月份都已经做好了,当时也发布了一篇文章《用Node+wechaty写一个爬虫脚本每天定时给女(男)朋友发微信暖心话》,有看过的朋友应该有印象的。由于上次分享的文章比较偏向于技术,被朋友说可能不太适合小白用户使用,在他的建议下呢,我又重新整理了一下现有代码,和制作了教学视频,方便任意人群的使用。 同时呢我也维护了两套项目,一个是本项目《微信每日说》适合入门人群,操作简单,配置方便,上手容易。另一个项目是《微信个人秘书》,功能较多,涵盖自动加群、自动加好友、自动回复、可设置定时提醒等功能。由于涉及到数据库的操作,所以主要面向有编程经验的群体,有兴趣的小伙伴可以参考《使用koa2+wechaty打造个人微信小秘书》。 本文介绍项目是用node和wechaty微信网页接口开发的一款小工具,可以定时给女朋友发每天的天气情况,天气提醒,每日一句。通过配置机器人api后还可以实现微信机器人自动陪女朋友聊天。 项目地址github: https://github.com/gengchen528/wechatBot 看看前端的小伙伴能不能把这个项目送上热门呢 哈哈 效果预览在三步走教学之前,先放上效果看一下 可以看到在指定的时间就会收到发送的消息,包括天气信息,天气提醒,还有你们在一起多少天了。当开启机器人后,女朋友就可以和小助手对话了,不过目前开源机器人的api都不是非常的智能,匹配的语义可能不是那么准确。所以有时候女朋友生气了,千万记得不要开启机器人,不然回答的不对是会被女朋友暴打的????! 此项目前期使用的是图灵机器人,但是最近图灵机器人做了限制,没有认证的用户不允许调取API,认证的用户每天也只有100条,这就很鸡肋了,女朋友还没哄过瘾呢就被限制了,这是很可怕的(手动滑稽)!所以现在更换了一个天行机器人的api接口,这个接口没有太多限制,送的调用次数也足够用,在项目中已经开放给大家用了,不过还是建议大家自己申请一个账号比较好,因为这个机器人可以自定义名称之类的,也可以设置自己需要的回复内容。 视频教程移步:《三步教你用Node做一个微信哄女友神器》 三步走教程一、安装nodenode官网:https://nodejs.org/zh-cn/ 选择系统对应版本node下载安装,win建议.msi包安装,只需一直下一步即可,其他系统同理; windows下安装node步骤详细参考 https://www.cnblogs.com/liuqiyun/p/8133904.htmlMac下安装node详细步骤参考 https://blog.csdn.net/qq_32407233/article/details/83758899 Linux下安装node详细步骤参考 https://www.cnblogs.com/liuqi/p/6483317.html 安装完成后,按住键盘的shift+鼠标右键,选择在此处打开命令窗口。在命令行执行node -v出现版本号说明安装成功 二、下载代码并配置代码地址:https://github.com/gengchen528/wechatBot 访问此地址,直接下载zip包到本地桌面,然后解压; 进到目录中,找到config目录下的index.js文件 选中index.js文件,右击选择打开方式,没有安装代码编辑器的可以用记事本打开。有代码编辑器的直接用代码编辑器打开,建议非开发人员可以下载一个notepad++,下载链接链接:https://pan.baidu.com/s/1mWdEOaTQ1D6kihQveN1JHw 密码:fn9g,开发人员就各自发挥吧,相信每个人都有自己用的比较舒服的编辑器我就不推荐了](https://upload-images.jianshu... 配置文件中需要修改的地方,女朋友的微信备注姓名NAME必须要换一下,不然你发给我就不好了????,微信昵称NICKNAME最好也写一下,你和女朋友的纪念日MEMORIAL_DAY就不用说了,也要改一下。如果要发送天气信息,女朋友所在城市CITY肯定也是必须修改的,地区LOCATION不知道怎么拼写的话,我建议可以查一下墨迹的官网https://tianqi.moji.com/weather/china/在墨迹天气找到对应地区的天气后,查看一下网页地址栏,绿色标记的拼音填入CITY,红色标记的拼音填入LOCATION 每天发送的时间SENDDATE,这里的规则可以参见schedule目录下的index.js文件。这里0 06 8 * * *代表的是每天的早上8点06分0秒,我们通常只需配置前三个就可以了。如果需要开启机器人聊天的话,需要把AUTOREPLY设置为true,这里我放弃了图灵机器人,原因上面也说了,改用了天行机器人,但是不要抱太大希望,它并不是那么智能????。目前由于我自己账号的api次数还比较多,就在项目代码中开放给大家使用了,这里就不放出来,下载代码后只要修改一下AUTOREPLY就可以自动回复了。// 配置文件module.exports = { // 基础定时发送功能配置项(必填项) NAME: 'Leo_chen', //女朋友备注姓名 NICKNAME: 'Leo_chen', //女朋友昵称 MEMORIAL_DAY: '2015/04/18', //你和女朋友的纪念日 CITY: 'shanghai', //女朋友所在城市 LOCATION: "pudong-new-district", //女朋友所在区(可以访问墨迹天气网站后,查询区的英文拼写) SENDDATE: '0 06 8 * * *', //定时发送时间 每天8点0分0秒发送,规则见 /schedule/index.js ONE: 'http://wufazhuce.com/', ////ONE的web版网站 MOJI_HOST: 'https://tianqi.moji.com/weather/china/', //中国墨迹天气url //高级功能配置项(非必填项) AUTOREPLY: true, //自动聊天功能 默认关闭 AIBOTAPI: 'http://api.tianapi.com/txapi/robot/', //天行机器人API 注册地址https://www.tianapi.com/signup.html?source=474284281 APIKEY: '天行机器人apikey', //天行机器人apikey}三、开始运行程序配置完成好文件别忘记保存了,保存好就回到项目的主目录吧。这时候win系统的话就按住键盘的shift+鼠标右键,选择在此处打开命令窗口。 ...

June 24, 2019 · 1 min · jiezi

StepByStep高频面试题深入解析-周刊05

关于【Step-By-Step】Step-By-Step (点击进入项目) 是我于 2019-05-20 开始的一个项目,每个工作日发布一道面试题。每个周末我会仔细阅读大家的答案,整理最一份较优答案出来,因本人水平有限,有误的地方,大家及时指正。 如果想 加群 学习,可以通过文末的公众号,添加我为好友。 __ 本周面试题一览:实现 Promise.race 方法JSONP 原理及简单实现实现一个数组去重的方法清楚浮动的方法有哪些编写一个通用的柯里化函数 currying20. 实现 Promise.race 方法在实现 Promise.race 方法之前,我们首先要知道 Promise.race 的功能和特点,因为在清楚了 Promise.race 功能和特点的情况下,我们才能进一步去写实现。 Promise.race 功能Promise.race(iterable) 返回一个 promise,一旦 iterable 中的一个 promise 状态是 fulfilled / rejected ,那么 Promise.race 返回的 promise 状态是 fulfilled / rejected. let p = Promise.race([p1, p2, p3]);只要p1、p2、p3之中有一个实例率先改变状态,p的状态就跟着改变。那个率先改变的 Promise 实例的返回值,就传递给 p 的回调函数。 Promise.race 的特点Promise.race 的返回值是一个 promise 实例如果传入的参数为空的可迭代对象,那么 Promise.race 返回的 promise 永远是 pending 态如果传入的参数中不包含任何 promise,Promise.race 会返回一个处理中(pending)的 promise如果 iterable 包含一个或多个非 promise 值或已经解决的promise,则 Promise.race 将解析为 iterable 中找到的第一个值。Promise.race 的实现Promise.race = function (promises) { //promises传入的是可迭代对象(省略参数合法性判断) promises = Array.from(promises);//将可迭代对象转换为数组 return new Promise((resolve, reject) => { if (promises.length === 0) { //空的可迭代对象; //用于在pending态 } else { for (let i = 0; i < promises.length; i++) { Promise.resolve(promises[i]).then((data) => { resolve(data); }).catch((reason) => { reject(reason); }) } } });}21. JSONP原理及简单实现尽管浏览器有同源策略,但是 <script> 标签的 src 属性不会被同源策略所约束,可以获取任意服务器上的脚本并执行。jsonp 通过插入 script 标签的方式来实现跨域,参数只能通过 url 传入,仅能支持 get 请求。 ...

June 24, 2019 · 3 min · jiezi

node模块加载机制

node加载模块分为3个步骤1.路径查找2.文件定位3.模块编译 1.路径查找通过module.paths可以看出,文件查找是先查找当前目录的node_modules,然后查找父目录的node_modules,然后逐级向上查找 2.文件定位如果require传参没有文件扩展名,那么node会按.js .json .node 的顺序查找文件如果没有查找到对应文件,node会把对应目录作为包处理,查找目录下pageage.json文件,用JSON.parse()处理,然后查找到main属性,根据main属性进行文件定位,如果main属性没有文件拓展名,依然按照.js .json .node 顺序查询。如果main属性文件指定的文件名错误,或者压根没有pageage.json,node会将index作为默认文件名,按照.js .json .node 依次进行查找,如果还没有定位到,将进入下一个模块路径查找,全部递归后,如果还没有找到,将会抛出查询失败异常。 3.模块编译编译分为三种情况,js模块、c/c++模块、json模块Js模块每个模块都有默认变量,exports、module、require、__filename、__dirname。这些变量的来源是在编译过程,对文件进行收尾处理 function(exports、module、require、__filename、__dirname){ /*文件内容*/ }exports和module.exports的区别exports是module.exports的引用,如果直接对exports进行复制,只是改变了exports的指向,但是对于module.exports是没有任何改变的。所以我们不能直接对exports进行赋值,应该对exports的属性赋值。c/c++模块因为是c/c++编写,所以该模块并不需要编译,而是直接进行加载和执行。通过调用process.dlopen()方法执行。json模块node直接通过fs模块同步读取json文件,然后通过JSON.parse()进行转换,后赋值给module.exports。 模块缓存核心模块编译成功后缓存在NativeModule._cache下文件模块编译成功后缓存在Module._cache下

June 24, 2019 · 1 min · jiezi

Vue组件库开发总结

Vue组件库开发总结由于工作需要,最近在学习怎么开发一个Vue组件库。主要需要实现以下点:1.组件使用npm包引入2.实现按需引入及按需打包项目中许多实现是参考的element-ui,特别是webpack打包部分 组织项目项目生成项目生成是直接用的vue-cli,在根目录下增加了一个index.js,用于组件打包的入口文件,两个webpack打包文件,以及一个组件的json文件,用于之后的按需引入的打包。组件放置在src/cmps中,目录结构如下图: 组件结构由于我的组件把样式都写在了vue里面,所以没有单独的样式文件,就是一个Vue文件和一个js入口文件 组件编写单个组件编写vue组件的编写需要按照官方的vue插件开发规范来。为了实现后续的按需打包,在每一组件的入口文件中,都需要定义install方法,并随组件一同暴露出来 import Button from './index.vue'Button.intall = function (vue) { vue.component(Button.name, Button)}export default Button所有组件输出编写所有组件的输出就是将所有组件暴露出去,并加上一个对所有组件的install方法。其中if(window && window.Vue) install(window.Vue)是用来实现script标签引入的方式的。 import Input from 'src/cmps/input/index.js'import Toast from 'src/cmps/toast/index.js'import Button from 'src/cmps/button/index.js'const cmps = [ Input, Toast, Button]const install = vue => { cmps.map(cmp => { vue.component(cmp.name, cmp) })}if(window && window.Vue) install(window.Vue)export default { install, Input, Toast, Button}组件打包全量加载的打包全量加载的打包首先是把vue-cli生成的webpack文件改一下打包的出入口文件和路径就行了。为了方便之后的按需加载的打包,出口文件我的路径放在了lib目录下。为了实现npm包、script标签等引入形式,libraryTarget选择了umd模式。library是npm包引入时的名称。entry的写法是我为了用dev在本地测试组件是否可用而写的。externals是为了去除在组件库和实际项目中会重复的库,比如vue entry: ENV == 'dev'? path.resolve(__dirname, './src/main.js'): path.resolve(__dirname, './index.js'),output: { path: path.resolve(__dirname, './lib'), publicPath: '/dist/', filename: 'input-ui.js', library: 'input-ui', libraryExport: 'default', libraryTarget: 'umd'},externals: { vue: 'vue'}package.json中需要加入对主入口的说明 ...

June 24, 2019 · 1 min · jiezi

nodezookeeperclient-运行机制

ZooKeeper ( 简称 zk ) 是一个开源的分布式协调服务,其常用做分布式服务的注册中心 本文要讲的是 Node.js 的 zk sdk -- node-zookeeper-client 在这之前,先了解一下 zk 相关的基本知识 sessionId:客户端连接 session,默认 30s 后过期xid:客户端发送消息序号,zk 服务端进行响应时,xid 相同zxid:ZooKeeper Transaction Id,用于保证 zk 分布式一致性Jute:zk 底层的序列化工具packet:由 header 和 payload 组成,序列化后 buffer 结构类似 [总长度, header, payload]使用示例先从使用示例开始 /** * xiedacon created at 2019-06-14 10:07:46 * * Copyright (c) 2019 Souche.com, all rights reserved. */'use strict';const ZK = require('node-zookeeper-client');const util = require('util');const client = ZK.createClient('127.0.0.1:2181');client.getChildren = util.promisify(client.getChildren);(async () => { console.log('Result:', await client.getChildren('/xxx'));})().catch((err) => { console.log(err);});client.connect();上面的代码总共分成 3 步: ...

June 23, 2019 · 3 min · jiezi

node中删除目录

由于删除目录只能删除空目录(如果有子文件或文件夹要先删除)目录结构算是典型的二叉树模型,所以涉及到遍历树结构二叉树遍历(分为深度和广度,以及先序,中序,后序之分)以下以深度先序解决目录删除在node中由于主线程为单线程, 可以采取串行方式和并行方式深度先序(串行 回调方式)const fs = require('fs')const path = require('path')function rmdir(filePath, callback) { // 先判断当前filePath的类型(文件还是文件夹,如果是文件直接删除, 如果是文件夹, 去取当前文件夹下的内容, 拿到每一个递归) fs.stat(filePath, function(err, stat) { if(err) return console.log(err) if(stat.isFile()) { fs.unlink(filePath, callback) }else { fs.readdir(filePath, function(err, data) { if(err) return console.log(err) let dirs = data.map(dir => path.join(filePath, dir)) let index = 0 !(function next() { // 此处递归删除掉所有子文件 后删除当前 文件夹 if(index === dirs.length) { fs.rmdir(filePath, callback) }else { rmdir(dirs[index++],next) } })() }) } })}rmdir('a', function() { console.log('删除成功')})深度先序 (串行 promise写法)const fs = require('fs')const path = require('path')function rmdirPromise(filePath) { return new Promise((resolve, reject) => { fs.stat(filePath, function(err, stat) { if(err) reject(err) if(stat.isFile()) { fs.unlink(filePath, function(err) { if(err) reject(err) resolve() }) }else { fs.readdir(filePath, function(err, dirs) { if(err) reject(err) dirs = dirs.map(dir => path.join(filePath, dir)) // a/b a/c let index = 0; (function next() { if(index === dirs.length) { fs.rmdir(filePath, function(err) { if(err) reject(err) resolve() }) }else { rmdirPromise(dirs[index++]).then(() => { next() }, err => { reject(err) }) } })() }) } }) })}rmdirPromise('a').then(() => { console.log('删除成功')})深度先序 (串行 async await写法)// 在node v10.0.0+版本中fs模块提供 promise 写法 const fs = require('fs').promises// 如果在node 10之前的版本中可以引入第三方模块 mz const fs = require('mz/fs') 用法一致 https://www.npmjs.com/package/mzconst fs = require('fs').promisesconst path = require('path')async function rmdirAsync(filePath) { let stat = await fs.stat(filePath) if (stat.isFile()) { await fs.unlink(filePath) } else { let dirs = await fs.readdir(filePath) dirs = dirs.map(dir => path.join(filePath, dir)) let index = 0; (async function next() { if (index === dirs.length) { await fs.rmdir(filePath) } else { await rmdirAsync(dirs[index++]) await next() } })() }}rmdirAsync('a').then(() => { console.log('删除成功')}, (err) => { console.log('err', err)})

June 23, 2019 · 2 min · jiezi

如何从零开始定义一个类似websocket的即时通讯协议

深南大道镇楼定义一个自己的通讯协议并不难,关键在于这个协议的可用性,可拓展性,复杂业务场景的实用性即时通讯应用中,客户端和服务器端都可以看成一个服务器一起复习一下websocketWebSocket是一种在单个TCP连接上进行全双工通信的协议。WebSocket通信协议于2011年被IETF定为标准RFC 6455,并由RFC7936补充规范。WebSocket API也被W3C定为标准。WebSocket使得客户端和服务器之间的数据交换变得更加简单,允许服务端主动向客户端推送数据,在WebSocket API中,浏览器和服务器只需要完成一次握手,两者之间就直接可以创建持久性的连接,并进行双向数据传输。说说ws协议的优点:说到优点,这里的对比参照物是 HTTP 协议,概括地说就是:支持双向通信,更灵活,更高效,可扩展性更好。支持双向通信,实时性更强。更好的二进制支持。较少的控制开销。连接创建后,ws 客户端、服务端进行数据交换时,协议控制的数据包头部较小。在不* 包含头部的情况下,服务端到客户端的包头只有 2~10 字节(取决于数据包长度),客户端到服务端的的话,需要加上额外的 4 字节的掩码。而 HTTP 协议每次通信都需要携带完整的头部。支持扩展。ws 协议定义了扩展,用户可以扩展协议,或者实现自定义的子协议。(比如支持自定义压缩算法等)我们先看看web socket协议的实现具体过程,再用代码抽象,定义自己的即时通讯协议:连接握手过程 关于WebSocket有一句很常见的话: Websocket复用了HTTP的握手通道, 它具体指的是:客户端通过HTTP请求与WebSocket服务器协商升级协议, 协议升级完成后, 后续的数据交换则遵照WebSocket协议客户端: 申请协议升级首先由客户端换发起协议升级请求, 根据WebSocket协议规范, 请求头必须包含如下的内容 GET / HTTP/1.1 Host: localhost:8080 Origin: http://127.0.0.1:3000 Connection: Upgrade Upgrade: websocket Sec-WebSocket-Version: 13 Sec-WebSocket-Key: w4v7O6xFTi36lq3RNcgctw请求头详解 请求行: 请求方法必须是GET, HTTP版本至少是1.1请求必须含有Host如果请求来自浏览器客户端, 必须包含Origin请求必须含有Connection, 其值必须含有"Upgrade"记号请求必须含有Upgrade, 其值必须含有"websocket"关键字请求必须含有Sec-Websocket-Version, 其值必须是13请求必须含有Sec-Websocket-Key, 用于提供基本的防护, 比如无意的连接1.2 服务器: 响应协议升级服务器返回的响应头必须包含如下的内容 HTTP/1.1 101 Switching ProtocolsConnection:UpgradeUpgrade: websocketSec-WebSocket-Accept: Oy4NRAQ13jhfONC7bP8dTKb4PTU=响应行: HTTP/1.1 101 Switching Protocols响应必须含有Upgrade, 其值为"weboscket"响应必须含有Connection, 其值为"Upgrade"响应必须含有Sec-Websocket-Accept, 根据请求首部的Sec-Websocket-key计算出来Sec-WebSocket-Key/Accept的计算规范提到:Sec-WebSocket-Key值由一个随机生成的16字节的随机数通过base64编码得到的Key可以避免服务器收到非法的WebSocket连接, 比如http请求连接到websocket, 此时服务端可以直接拒绝Key可以用来初步确保服务器认识ws协议, 但也不能排除有的http服务器只处理Sec-WebSocket-Key, 并不实现ws协议Key可以避免反向代理缓存在浏览器中发起ajax请求, Sec-Websocket-Key以及相关header是被禁止的, 这样可以避免客户端发送ajax请求时, 意外请求协议升级最终需要强调的是: Sec-WebSocket-Key/Accept并不是用来保证数据的安全性, 因为其计算/转换公式都是公开的, 而且非常简单, 最主要的作用是预防一些意外的情况WebSocket通信的最小单位是帧, 由一个或多个帧组成一条完整的消息, 交换数据的过程中, 发送端和接收端需要做的事情如下:发送端: 将消息切割成多个帧, 并发送给服务端接收端: 接受消息帧, 并将关联的帧重新组装成完整的消息数据帧格式详解 ...

June 23, 2019 · 7 min · jiezi

微服务一些痛点

微服务的概念什么是微服务?网上有很多文章,看完之后似懂非懂,理论上扯太多,不如实践体会的真实。以我工作中实践的所谓“微服务”,就是把业务进行拆分,模块化,独立成一个个的服务。这样来看并不能体现出“微”来,因为即使进行了拆分,有些服务还是会随着业务的增长逐渐变“大”。我姑且叫它“多服务”吧。 多服务的痛点开发和发布原本微服务的想法是模块化开发,互不干扰。但是由于我们有针对每个端都有一个壳子服务(在我们的架构中是直接对接前端的那个服务,也有人称这个为gateway)。导致每次开发至少会涉及到两个服务,一个是模块化的基础服务,一个是“壳子”服务,甚至是三个。这样就会导致一次业务的开发,可能需要编写两三个服务的代码。发布也需要发布两三个服务。回滚也一样。如果你开发的服务中同时存在同事上线的代码,回滚起来就是一件很蛋疼的事情(当然,这种事情应该做到0发生,但也无法绝对避免)。 线上问题排查由于一个接口的调用链可能达到3层或4层,导致问题的排查低效,定位和修复的速度不够快,影响用户体验。 流量问题有一种情况是,很多服务都依赖于同一个服务,导致该服务的流量非常的大,常常会看到线上有502、socket hang up等,各种之前从没有碰到过的网络问题。虽然暂时无法排查到根本的原因,但一个明显的事实是流量上来之后才出现这些奇怪的问题。 服务的管理和维护我们的微服务是部署在docker集群的,由于对docker的使用和理解不够深入,导致出现问题的时候无法快速解决,从而影响了业务。 总结技术不够,实践微服务一定是会遇到麻烦的问题的。微服务可能最重要的一点是业务的划分吧。微服务需要完善的东西很多,比维护一般的服务要求高很多。

June 23, 2019 · 1 min · jiezi