面试题记录AL

1使用过的koa2中间件

1.什么是koa2中间件?
https://www.cnblogs.com/crbluesky/p/7931946.html
koa对网络请求采用了中间件的形式处理,中间件可以介入请求和相应的处理,是一个轻量级的模块,每个中间负责完成某个特定的功能。中间件的通过next函数联系,执行next()后会将控制权交给下一个中间件,如果没有有中间件没有执行next后将会沿路折返,将控制权交换给前一个中间件。

image

image

当执行app.listen方法开启服务器时,实际上是在内部,使用http模块,启动了http服务器,并将自身的callback函数传入

二、常用的五个中间件

  • 1koa:面向node.js的表达式HTTP中间件框架,使Web应用程序和API更加令人愉快地编写。Koa的中间件堆栈以类似堆栈的方式流动,允许您执行下游操作,然后过滤和处理上游的响应。
var Koa = require('koa');
var app = new Koa();
 
// response
app.use(ctx => {
  ctx.body = 'Hello World';
});
 
app.listen(3000);
  • 2koa-router:Router middleware for koa(koa的路由中间件)
const Koa = require('koa');
const Router=require('koa-router');

const app = new Koa();
const router=new Router();


router.get('/',function(ctx,next){
    ctx.body='hello hello';
})

app.use(router.routes());

app.listen(3000);
  • 3koa-views:Template rendering middleware for koa.(koa的模板渲染中间件)

ctx:为网络处理上下文,next指向下个中间件

const Koa=require('koa');
const views=require('koa-views');

const app=new Koa();
//配置模板解析器
app.use(views(__dirname+'/views',{
    map:{
        html:'swig'
    }
}));

app.use(async function(ctx,next){
    await ctx.render('layout');
})

app.listen(3000);
  • koa-bodyparser:用来解析body的中间件,比方说你通过post来传递表单,json数据,或者上传文件,在koa中是不容易获取的,通过koa-bodyparser解析之后,在koa中this.body就能直接获取到数据。ps:xml数据没办法通过koa-bodyparser解析,有另一个中间件koa-xml-body。
var Koa = require('koa');
var bodyParser = require('koa-bodyparser');
 
var app = new Koa();

app.use(bodyparser({
  enableTypes:['json', 'form', 'text']
}));
 
app.use(async ctx => {
  
  ctx.body = ctx.request.body;
});
app.listen(3000);
  • 5.promise-mysql:Promise-mysql是mysqljs / mysql的一个包装函数,它包含Bluebird承诺的函数调用。通常这会用Bluebird的.promisifyAll()方法完成,但是mysqljs / mysql的脚本与Bluebird所期望的不同。

对于重复的代码我们将它封装在一起,方便使用,提高代码的重用性,DbUtil.js

var mysql=require('promise-mysql');
var Promise=require('bluebird');

pool = mysql.createPool({
  host: '192.168.22.2',
  user: 'root',
  password: 'root',
  database: 'music',
  connectionLimit: 2
});//此处可根据实际情况更改

function getSqlConnection() {
  return pool.getConnection().disposer(function(connection) {
    pool.releaseConnection(connection);
  });
}

async function execSql(sql){
    let result=null;
    await Promise.using(getSqlConnection(), function(connection) {
        return connection.query(sql)
        .then(function(rows) {
            result=rows;
        }).catch(function(error) {
            console.log(error);
        });
    })
    return result;
}
module.exports = execSql;
var execSql=require('./DbUtil.js');

function SingerDb(){

}
SingerDb.prototype.querySinger=async function(){
    let sql='select * from singer';
    return execSql(sql);
}
SingerDb.prototype.querySingers=async function(){
    let sql='select id,name from singer';
    return execSql(sql);
}
SingerDb.prototype.addSinger=async function(singer){
    let sql=`insert into singer (name,englishname,guoji,chushengdi,jobs,picName) values ('${singer.name}','${singer.englishname}','${singer.guoji}','${singer.chushengdi}','${singer.jobs}','${singer.picName}')`;
    // execSql(sql).then(function(rows){
    //     return rows;
    // });
    return execSql(sql);
}
SingerDb.prototype.delSinger=async function(id){
    let sql=`delete from singer where id=${id}`;
    return execSql(sql);
}
SingerDb.prototype.querySingerById=async function(id){
    let sql=`select * from singer where id=${id}`;
    return execSql(sql);
}
SingerDb.prototype.updateSinger=async function(singer){
    let sql=`update singer set name='${singer.name}' where id=${singer.id}`;
    return execSql(sql);
}
// let singer={
//     name:'张靓颖',
//     englishname:'amanda',
//     guoji:'中国',
//     chushengdi:'中国湖南',
//     jobs:'歌手'
// }

// new SingerDb().addSinger(singer);
// new SingerDb().querySinger();
module.exports=new SingerDb();

2koa-body原理

https://blog.csdn.net/sinat_17775997/article/details/82898312
想要分析 koa-bodyparser 的原理首先需要知道用法和作用,koa-bodyparser 中间件是将我们的 post 请求和表单提交的查询字符串转换成对象,并挂在 ctx.request.body 上,方便我们在其他中间件或接口处取值,使用前需提前安装。

npm install koa koa-bodyparser

koa-bodyparser 具体用法如下:


const Koa = require("koa");
const bodyParser = require("koa-bodyparser");
 
const app = new Koa();
 
// 使用中间件
app.use(bodyParser());
 
app.use(async (ctx, next) => {
    if (ctx.path === "/" && ctx.method === "POST") {
        // 使用中间件后 ctx.request.body 属性自动加上了 post 请求的数据
        console.log(ctx.request.body);
    }
});
 
app.listen(3000);

根据用法我们可以看出 koa-bodyparser 中间件引入的其实是一个函数,我们把它放在了 use 中执行,根据 Koa 的特点,我们推断出 koa-bodyparser 的函数执行后应该给我们返回了一个 async 函数,下面是我们模拟实现的代码。

const querystring = require("querystring");
 
module.exports = function bodyParser() {
    return async (ctx, next) => {
        await new Promise((resolve, reject) => {
            // 存储数据的数组
            let dataArr = [];
 
            // 接收数据
            ctx.req.on("data", data => dataArr.push(data));
 
            // 整合数据并使用 Promise 成功
            ctx.req.on("end", () => {
                // 获取请求数据的类型 json 或表单
                let contentType = ctx.get("Content-Type");
 
                // 获取数据 Buffer 格式
                let data = Buffer.concat(dataArr).toString();
 
                if (contentType === "application/x-www-form-urlencoded") {
                    // 如果是表单提交,则将查询字符串转换成对象赋值给 ctx.request.body
                    ctx.request.body = querystring.parse(data);
                } else if (contentType === "applaction/json") {
                    // 如果是 json,则将字符串格式的对象转换成对象赋值给 ctx.request.body
                    ctx.request.body = JSON.parse(data);
                }
 
                // 执行成功的回调
                resolve();
            });
        });
 
        // 继续向下执行
        await next();
    };
};

在上面代码中由几点是需要我们注意的,即 next 的调用以及为什么通过流接收数据、处理数据和将数据挂在 ctx.request.body 要在 Promise 中进行。

首先是 next 的调用,我们知道 Koa 的 next 执行,其实就是在执行下一个中间件的函数,即下一个 use 中的 async 函数,为了保证后面的异步代码执行完毕后再继续执行当前的代码,所以我们需要使用 await 进行等待,其次就是数据从接收到挂在 ctx.request.body 都在 Promise 中执行,是因为在接收数据的操作是异步的,整个处理数据的过程需要等待异步完成后,再把数据挂在 ctx.request.body 上,可以保证我们在下一个 use 的 async 函数中可以在 ctx.request.body 上拿到数据,所以我们使用 await 等待一个 Promise 成功后再执行 next。

3介绍pm2

简介

PM2是node进程管理工具,可以利用它来简化很多node应用管理的繁琐任务,如性能监控、自动重启、负载均衡等,而且使用非常简单。引用

全局安装

sudo npm install pm2@latest -g

用法

  • 最简单的启用一个应用: pm2 start app.js
  • 停止:pm2 stop app_name|app_id
  • 删除:pm2 delete app_name|app_id
  • 重启:pm2 restart app_name|app_id
  • 停止所有:pm2 stop all
  • 查看所有的进程:pm2 list
  • 查看所有的进程状态:pm2 status
  • 查看某一个进程的信息:pm2 describe app_name|app_id

参数说明

  • --watch:监听应用目录源码的变化,一旦发生变化,自动重启。如果要精确监听、不见听的目录,最好通过配置文件
  • -i --instances:启用多少个实例,可用于负载均衡。如果-i 0或者-i max,则根据当前机器核数确定实例数目,可以弥补node.js缺陷
  • --ignore-watch:排除监听的目录/文件,可以是特定的文件名,也可以是正则。比如--ignore-watch="test node_modules "some scripts"
  • -n --name:应用的名称。查看应用信息的时候可以用到
  • -o --output <path>:标准输出日志文件的路径,有默认路径
  • -e --error <path>:错误输出日志文件的路径,有默认路径
  • --interpreter <interpreter>:the interpreter pm2 should use for executing app (bash, python...)。比如你用的coffee script来编写应用

完整参数命令: pm2 start index.js --watch -i 2

配置文件

  • 配置文件里的设置项,跟命令行参数基本是一一对应的
  • 配置文件的格式可以为json/yaml
  • json格式的配置文件,pm2当作普通的js文件来处理,所以可以在里面添加注释或者编写代码,这对于动态调整配置很有好处
  • 如果启动的时候指定了配置文件,那么命令行参数会被忽略(个别参数除外,比如--env)

完整参数单个app配置:

{
  "name"             : "node-app", //启动app名称
  "cwd"              : "/srv/node-app/current", 
  "args"             : ["--toto=heya coco", "-d", "1"],
  "script"           : "bin/app.js",
  "node_args"        : ["--harmony", " --max-stack-size=102400000"],
  "log_date_format"  : "YYYY-MM-DD HH:mm Z",
  "error_file"       : "/var/log/node-app/node-app.stderr.log",
  "out_file"         : "log/node-app.stdout.log",
  "pid_file"         : "pids/node-geo-api.pid",
  "instances"        : 6, //or 0 => 'max'
  "min_uptime"       : "200s", // 200 seconds, defaults to 1000
  "max_restarts"     : 10, // defaults to 15
  "max_memory_restart": "1M", // 1 megabytes, e.g.: "2G", "10M", "100K", 1024 the default unit is byte.
  "cron_restart"     : "1 0 * * *",
  "watch"            : false,
  "ignore_watch"      : ["[\\/\\\\]\\./", "node_modules"],
  "merge_logs"       : true,
  "exec_interpreter" : "node",
  "exec_mode"        : "fork",
  "autorestart"      : false, // enable/disable automatic restart when an app crashes or exits
  "vizion"           : false, // enable/disable vizion features (versioning control)
  // Default environment variables that will be injected in any environment and at any start
  "env": {
    "NODE_ENV": "production",
    "AWESOME_SERVICE_API_TOKEN": "xxx"
  }
  "env_*" : {
    "SPECIFIC_ENV" : true
  }
}

完整配置文件写法:

{
  "apps" : [{
    // Application #1
    "name"        : "worker-app",
    "script"      : "worker.js",
    "args"        : ["--toto=heya coco", "-d", "1"],
    "watch"       : true,
    "node_args"   : "--harmony",
    "merge_logs"  : true,
    "cwd"         : "/this/is/a/path/to/start/script",
    "env": {
      "NODE_ENV": "development",
      "AWESOME_SERVICE_API_TOKEN": "xxx"
    },
    "env_production" : {
       "NODE_ENV": "production"
    },
    "env_staging" : {
       "NODE_ENV" : "staging",
       "TEST"     : true
    }
  },{
    // Application #2
    "name"       : "api-app",
    "script"     : "api.js",
    "instances"  : 4,
    "exec_mode"  : "cluster_mode",
    "error_file" : "./examples/child-err.log",
    "out_file"   : "./examples/child-out.log",
    "pid_file"   : "./examples/child.pid"
  }]
}

通过yaml管理多个应用

process.yml:

apps:
  - script   : app.js
    instances: 4
    exec_mode: cluster
  - script : worker.js
    watch  : true
    env    :
      NODE_ENV: development
    env_production:
      NODE_ENV: production

启动:pm2 start process.yml

环境切换

正式开发中分为不同的环境(开发环境、测试环境、生产环境),我们需要根据不同的情景来切换各种环境
pm2通过在配置文件中通过env_xx来声明不同环境的配置,然后在启动应用时,通过--env参数指定运行的环境

环境配置定义,在应用中,可以通过process.env.REMOTE_ADDR等来读取配置中生命的变量:

  "env": {
    "NODE_ENV": "production",
    "REMOTE_ADDR": "http://www.example.com/"
  },
  "env_dev": {
    "NODE_ENV": "development",
    "REMOTE_ADDR": "http://wdev.example.com/"
  },
  "env_test": {
    "NODE_ENV": "test",
    "REMOTE_ADDR": "http://wtest.example.com/"
  }

启动指定的环境:pm2 start app.js --env development

负载均衡

  pm2 start app.js -i 3 # 开启三个进程
  pm2 start app.js -i max # 根据机器CPU核数,开启对应数目的进程 

开机自动启动

  1. 通过pm2 save保存当前进程状态。
  2. 通过pm2 startup [platform]生成开机自启动的命令。例如:pm2 startup centeros
  3. 将步骤2生成的命令,粘贴到控制台进行,搞定。

更新

安装最新的:npm install pm2@latest -g
然后在内存中更新:pm2 update

参考

4React生命周期

初始化
1、getDefaultProps()
设置默认的props,也可以用dufaultProps设置组件的默认属性.

2、getInitialState()
在使用es6的class语法时是没有这个钩子函数的,可以直接在constructor中定义this.state。此时可以访问this.props

3、componentWillMount()组建将要挂载
此时组件还未渲染完成,dom还未渲染
组件初始化时只调用,以后组件更新不调用,整个生命周期只调用一次,此时可以修改state。

4、 render()
react最重要的步骤,创建虚拟dom,进行diff算法,更新dom树都在此进行。此时就不能更改state了。

5、componentDidMount()
组件渲染完成,此时dom节点已经生成,可以在这里调用ajax请求
组件渲染之后调用,只调用一次。

更新
6、componentWillReceiveProps(nextProps)
在接受父组件改变后的props需要重新渲染组件时需要用到这个函数
组件初始化时不调用,组件接受新的props时调用。

7、shouldComponentUpdate(nextProps, nextState)
setState以后,state发生变化,组件会进入重新渲染的流程,return false可以阻止组件的更新

react性能优化非常重要的一环。组件接受新的state或者props时调用,我们可以设置在此对比前后两个props和state是否相同,如果相同则返回false阻止更新,因为相同的属性状态一定会生成相同的dom树,这样就不需要创造新的dom树和旧的dom树进行diff算法对比,节省大量性能,尤其是在dom结构复杂的时候

8、componentWillUpdata(nextProps, nextState)
组件初始化时不调用,只有在组件将要更新时才调用,此时可以修改state

9、render()
组件渲染

10、componentDidUpdate()
组件初始化时不调用,组件更新完成后调用,此时可以获取dom节点。

卸载
11、componentWillUnmount()
组件将要卸载时调用,一些事件监听和定时器需要在此时清除。
clear你在组建中所有的setTimeout,setInterval
移除所有组建中的监听 removeEventListener

5如何配置React-Router

6路由的动态加载模块

https://mp.csdn.net/postedit

7服务端渲染SSR

8介绍Redux数据流的流程

https://www.jianshu.com/p/e984206553c2
Redux三大原则
1 唯一数据源
2 保持只读状态
3 数据改变只能通过纯函数来执行

1、redux中间件
中间件提供第三方插件的模式,自定义拦截 action -> reducer 的过程。变为 action -> middlewares -> reducer 。这种机制可以让我们改变数据流,实现如异步 action ,action 过滤,日志输出,异常报告等功能。
常见的中间件:
redux-logger:提供日志输出
redux-thunk:处理异步操作
redux-promise:处理异步操作,actionCreator的返回值是promise
2、redux有什么缺点
1.一个组件所需要的数据,必须由父组件传过来,而不能像flux中直接从store取。
2.当一个组件相关数据更新时,即使父组件不需要用到这个组件,父组件还是会重新render,可能会有效率影响,或者需要写复杂的shouldComponentUpdate进行判断。

3、react性能优化是哪个周期函数?
shouldComponentUpdate 这个方法用来判断是否需要调用render方法重新描绘dom。因为dom的描绘非常消耗性能,如果我们能在shouldComponentUpdate方法中能够写出更优化的dom diff算法,可以极大的提高性能。
为什么虚拟dom会提高性能?

4、虚拟dom相当于在js和真实dom中间加了一个缓存,利用dom diff算法避免了没有必要的dom操作,从而提高性能。
具体实现步骤如下:
用 JavaScript 对象结构表示 DOM 树的结构;然后用这个树构建一个真正的 DOM 树,插到文档当中
当状态变更的时候,重新构造一棵新的对象树。然后用新的树和旧的树进行比较,记录两棵树差异
把2所记录的差异应用到步骤1所构建的真正的DOM树上,视图就更新了。

5、diff算法?
把树形结构按照层级分解,只比较同级元素。
给列表结构的每个单元添加唯一的key属性,方便比较。
React 只会匹配相同 class 的 component(这里面的class指的是组件的名字)
合并操作,调用 component 的 setState 方法的时候, React 将其标记为 dirty.到每一个事件循环结束, React 检查所有标记 dirty 的 component 重新绘制.
选择性子树渲染。开发人员可以重写shouldComponentUpdate提高diff的性能。

9常见Http请求头

http请求中的常用头(请求头)的含义:
Accept:告诉服务器,客户端支持的数据类型。
Accept-Charset:告诉服务器,客户端采用的编码。
Accept-Encoding:告诉服务器,客户机支持的数据压缩格式。
Accept-Language:告诉服务器,客户机的语言环境。
Host:客户机通过这个头告诉服务器,想访问的主机名。
If-Modified-Since:客户机通过这个头告诉服务器,资源的缓存时间。
Referer:客户机通过这个头告诉服务器,它是从哪个资源来访问服务器的。(一般用于防盗链)
User-Agent:客户机通过这个头告诉服务器,客户机的软件环境。
Cookie:客户机通过这个头告诉服务器,可以向服务器带数据。
Connection:客户机通过这个头告诉服务器,请求完后是关闭还是保持链接。
Date:客户机通过这个头告诉服务器,客户机当前请求时间。

10移动端适配1px的问题

11介绍flex布局

12他css方式设置垂直居中

13使用过webpack里面哪些plugin和loader

14webpack里面的插件是怎么实现的

15dev-server是怎么跑起来

16怎么实现this对象的深拷贝

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值