2021年前端面试(2023年前端面试题汇总)
一:JavaScript
1 、闭包是什么?利弊?如何解决弊端?
闭包是什么:JS中内层函数可以访问外层函数的变量 ,外层函数无法操作内存函数的变量的特性 。我们把这个特性称作闭包 。
闭包的好处:
隔离作用域 ,保护私有变量;有了闭包才有局部变量 ,要不然都是全局变量了 。 让我们可以使用回调 ,操作其他函数内部; 变量长期驻扎在内存中 ,不会被内存回收机制回收 ,即延长变量的生命周期;闭包的弊端:内层函数引用外层函数变量 ,内层函数占用内存 。如果不释放内存 ,过多时 ,易引起内存泄露 。
解决办法:无法自动销户 ,就及时手动回收 ,使用后将函数的引用赋null 。
2 、深度拷贝
1 、深拷贝与浅拷贝的区别?
拷贝的层级不同 ,深拷贝是指每一层数据的改动都不会影响原对象和新对象,浅拷贝只有第一层的属性变动不互相影响 ,深层的数据变动还会互相影响 。
浅拷贝:Object.assign 深拷贝:JSON.stringify和JSON.parse2 、JSON的stringify和parse处理的缺点?
如果对象中有属性是function或者undefined ,处理后会被过滤掉; 如果属性值是对象,且由构造函数生成的实例对象 ,会丢弃对象的constructor;3 、$.extend()
使用jquey的extend方法不仅能实现深度拷贝 ,还能实现深度合并 。具体用法
深度拷贝:$.extend({},targetObject) // targetObject是需要复制的对象
深度合并:$.extend(true,{},targetObject1,targetObject2) // 可以将两个对象深度合并后再返回出一个新对象
3 、如何判断空对象?如何区分数据类型?
判断空对象
1 、用JSON的stringify和parse转成字符串后 ,跟{}对比; 2 、用ES6 ,判断Object.keys(targetObject)返回值数组的长度是否为0; 3 、用ES5 ,判断Object.getOwnPropertyNames(targetObject)返回的数组长度是否为0;区分数据类型
let a = [1,2] Object.prototype.toString.call(a) // [object Array]4 、如何改变this指向?区别?
call/apply let a = { name: sunq, fn:function(action){ console.log(this.name + love + action); } } let b = {name:sunLi} // 正常的this指向 a.fn(basketball); // sunq love basketball // 改变this指向 ,并体现call与apply的区别 a.fn.apply(b,[football]); // sunLi love football a.fn.call(b,football); // sunLi love football // call 和 apply 区别: call 和 apply 都是可以改变this 指向的问题, call 方法中传递参数要求一 // 个 一个传递参数 。 但是apply 方法要求传递参数是一个数组形式 。 bind // 还是上面的示例 ,bind也可以实现call和apply的效果 。 // bind的不同之处在于bind会返回一个新的函数 。如果需要传参 ,需要再调用该函数并传参 a.fn.bind(b)(piano); // sunLi love piano5 、沙箱隔离怎么做?
使用iframe可以实现 ,变量隔离
6 、浏览器存储 ,他们的区别?
localStorage:永久保存 ,以键值对保存 ,存储空间5M sessionStorage:关闭页签/浏览器时清空 cookie:随着请求发送,通过设置过期时间删除 session:保存在服务端localStorage/sessionStorage是window的属性 ,cookie是document的方法
7、常用的数组方法有哪些?
改变原数组:push 、pop 、shift、unshift 、sort 、splice 、reverse 不改变原属组:concat 、join 、map 、forEach 、filter 、sliceslice和splice的区别?
slice切片的意思 ,根据传入的起始和终止下标,获取该范围数组。 splice可根据传入参数个数不同实现删除 、插入操作 ,直接操作原数组 。第1个参数为起始下标 ,第2个为删除个数 ,第3个为要增加的数据 。数组如何滤重?
8 、Dom事件流的顺序?什么是事件委托?
当页面上的一个元素被点击时 ,先从document向下一层层捕获到该元素。然后再向上冒泡 ,一层层触发 。
事件委托是将事件写在父级元素上 ,e.target是事件捕获时那个最小的元素 ,即选中的元素 。所以可以根据e.target操作选中的元素 。这样不需要给每个子元素绑定事件 ,代码更加简约 。
9 、对原型链的认识?
js通过原型链模拟实现面向对象 ,比如通过实例化一个构造函数可以给每个对象挂载自己专属的属性 ,通过给类的prototype上赋方法是所有对象所共有的方法 。每次实例化不再赋值原型链上的方法 。
10 、防抖/节流的区别?
区别:防抖只会在最后一次事件后执行触发函数 ,节流不管事件多么的频繁 ,都会保证在规定时间段内触发事件函数 。
防抖:原理是维护一个定时器,将很多个相同的操作合并成一个 。规定在delay后触发函数 ,如果在此之前触发函数 ,则取消之前的计时重新计时,只有最后一次操作能被触发 。例如:实时搜索的input ,一直输入就不发送 。
let input = document.querySelector("input"); let time = null;//time用来控制事件的触发 input.addEventListener(input,function(){ //防抖语句 ,把以前的定时删除 ,只执行最后一次 if(time !== null){ clearTimeout(time); } time = setTimeout(() => { console.log(this.value);//业务实现语句 ,这里的this指向的是input },500) }) 节流:原理是判断是否达到一定的时间来触发事件 。某个时间段内只能触发一次函数 。例如:在指定的时间内多次触发无效
//节流 function throttle(fn, time) {//连续触发事件 规定的时间 let flag = false; return function () { //使用标识判断是否在规定的时间内重复触发了函数 ,没有就触发 ,有就不触发 if (!flag) {//不为假时 执行以下 fn();//触发事件 flag = true;//为真 setTimeout(() => {//超时调用(在规定的时间内只执行一次) flag = false; }, time); } } } mybtn.onclick = throttle(btn, 3000);//单击事件 节流(btn,3s时间)二:Html
1、重绘和重排(回流/重构/重载)是什么?如何优化?
样式的调整会引起重绘 ,比如字体颜色 、背景色调整等 Dom的变动会引起重排 ,比如定位改动 、元素宽高调整避免循环插入dom ,比如table的行。可以js循环生成多个dom后 ,一次性插入 。
2、html5有哪些新特性?
本地存储 ,比如localStorage 、sessionStorage 语义化标签 ,如header 、footer 、nav等,使代码结构清晰 ,利于seo canvas svg web worker ,在主线程外再创建一个线程,可与主线程交互 拖放功能三:CSS
1 、如何实现一个宽度不固定的上下左右居中的弹框?
方法一: .pop{ width: 300px; height: 300px; position: fixed; left: 0; right: 0; top: 0; bottom: 0; margin: auto; border: 1px solid red; } 方法二: .chartLengend { // 父元素 width: 60px; height: 40px; position: relative; .line { // 子元素 width: 100%; height: 3px; background-color: #DEA182; position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); border-radius: 2px; } }2 、伪类和伪元素区别?
伪类本质上用于弥补常规css选择器的不足 ,因为如果没有我们可能需要多写一个class ,所以叫伪类 .class:last-child{} .class:first-child{} a:link {color:green;} a:visited {color:green;} a:hover {color:red;} a:active {color:yellow;} 伪元素本质上是创建了一个有内容的虚拟元素 ,如::before ::after 。因为相当于多了一个元素/节点 ,所以叫为元素 // :before用于在某个元素之前插入某些内容。 // :after用于在某个元素之后插入某些内容 。 css p:before{ content:"Read this: "; } html: <p>I live in Ducksburg</p> 页面展示: Read this: I live in Ducksburg F12看dom中: before Read this: I live in Ducksburg四:Vue
1 、单页面应用是什么?优缺点?如何弥补缺点
单页面对一个入口DOM通过路由去更改内容 ,整个应用只有一个html页面
SPA优点:用户体验好 ,没有页面切换就没有白屏情况;
SPA缺点:首屏加载慢 ,不利于SEO
SPA弥补:通过压缩 、路由懒加载缓解首屏慢;通过SSR 服务器端渲染解决SEO问题;
2 、组件及通信方式有哪些?
2.1 、什么是组件?
组件是可复用的 Vue 实例 ,且带有一个名字:在这个例子中是 。我们可以在一个通过 new Vue 创建的 Vue 根实例中 ,把这个组件作为自定义元素来使用:
声明组件
// 定义一个名为 button-counter 的新组件 Vue.component(button-counter, { data: function () { return { count: 0 } }, template: <button v-on:click="count++">You clicked me {{ count }} times.</button> })使用组件(把组件当作自定义元素)
<div id="components-demo"> <button-counter></button-counter> </div>引入组件
new Vue({ el: #components-demo })2.2 、父向子传值
Prop 是你可以在组件上注册的一些自定义 attribute 。当一个值传递给一个 prop attribute 的时候 ,它就变成了那个组件实例的一个 property 。为了给博文组件传递一个标题 ,我们可以用一个 props 选项将其包含在该组件可接受的 prop 列表中:
组件内部声明prop
Vue.component(blog-post, { props: [title], template: <h3>{{ title }}</h3> })父组件里调用 ,并给prop赋值,传递到组件内部
<blog-post title="My journey with Vue"></blog-post>2.3 、父组件监听子组件事件
其实就是通过在父组件声明方法 ,并绑定在子组件上 。以子组件内部触发方法的形式 ,向父组件传参,实现子向父传值的效果 。如下
父组件中声明方法 ,并绑定在子组件上
<template> <lineChart v-on:getQuotaVal="getQuotaVal"></lineChart> </template> <script> methods: { // 本事件用来监听折线图子组件 ,从子组件拿到指标数据 getQuotaVal:function(obj){ this.lineDateType = obj.lineDateType; // 这样父组件就拿到了 ,子组件的obj数据 } }, </script>子组件触发方法
that.val = {}; that.$emit(getQuotaVal,that.val); // 将子组件的数据发送过去;2.4 、兄弟组件间交互
使用EventBus(事件总线) ,vue.$bus.on和emit方法 。
初始化——全局定义 ,可以将eventBus绑定到vue实例的原型上,也可以直接绑定到window对象上.
//main.js Vue.prototype.$EventBus = new Vue();触发事件
this.$EventBus.$emit(eventName, param1,param2,...)监听事件
this.$EventBus.$on(eventName, (param1,param2,...)=>{ //需要执行的代码 })移除监听事件
为了避免在监听时 ,事件被反复触发 ,通常需要在页面销毁时移除事件监听 。或者在开发过程中 ,由于热更新 ,事件可能会被多次绑定监听 ,这时也需要移除事件监听 。
this.$EventBus.$off(eventName);3、v-if和v-show区别?
v-if控制Dom是否存在 ,v-show控制样式
4 、vuex是什么?使用步骤大概说下
vuex是一个状态管理工具 ,集中式的管理所有组件的状态数据 。统一的去管理组件,将组件的状态抽象为一个store文件 ,通过commit方法触发mutation里的函数来改变组件属性 。
组件中可以使用computed属性监听数据的变化控制组件显隐等 。如下举个loading组件的栗子
loading组件中根据Loading数据 ,控制DOM显隐
<template> <div class="cover" v-show="Loading"> <div>加载中</div> </div> </template> <script> import Store from ../../store export default { name: "Loading", computed:{ Loading(){ return Store.state.Loading; } } } </script>vuex集中管理状态,创建一个叫store的js文件
import Vuex from vuex; Vue.use(Vuex); export default new Vuex.Store({ state: { // Loading组件 Loading:false, }, mutations: { // Loading组件 ChangeLoading:function (State,Value) { State.Loading = Value; } }, });使用loading的组件中 ,这样操作
import Store from ../../store Store.commit("changeFooter",true);vuex中 mutation和action的区别和使用?
5 、vue watch和computed区别?
computed
计算结果并返回 ,只有当被计算的属性发生改变时才会触发(即:计算属性的结果会被缓存 ,除非依赖的响应属性变化才会重新及孙)。
如上loading组件也有使用到computed属性 。
watch
监听某一个值 ,当被监听的值发生变化时 ,执行相关操作 。
与computed的区别是 ,watch更加适用于监听某一个值得变化 ,并做对应操作 ,比如请求后台接口等。而computed适用于计算已有的值并返回结果 。 监听简单数据类型:
data(){ return{ first:2 } }, watch:{ first(){ console.log(this.first) } },6、Vue的虚拟Dom是什么?谈一谈对vue diff算法的认识?key的作用?
7 、谈谈对vue的双向绑定原理的理解?
双向绑定主要指修改数据时 ,无须操作DOM ,视图会自动刷新 。操作视图时绑定的数据也会跟随变动 。
数据 => 视图
vue在初始化实例时 ,会用Object.defineProperty方法 ,给所有的数据添加setter函数,实现对数据变更的监听 。当数据被修改时 ,生成新的虚拟DOM树 ,跟老的虚拟DOM对比,根据对比结果找出需要更新的节点进行更新 。
视图 => 数据
从视图到数据较为简单 ,视图变化后触发监听如oninput等 ,在绑定的方法中修改数据 。
8 、vue首屏优化怎么做?
使用较轻量的组件 ,比如echart对应有vue-chart vue-cli开启打包压缩 和后台配合 gzip访问; 路由懒加载 ,分包; 打包时配置删掉log日志 资源过大可以使用cdn模式引入 ,不再打包到本地9 、vue2的缺陷是什么?如何解决vue2.0数组中某一项改变 ,页面不改变的情况?
缺陷:数据如果为对象直接新增属性 ,如果为数组通过下标操作数组项 ,页面无法触发更新 。
原因: Vue 会在初始化实例时对 property 执行 getter/setter 转化 ,所以 property 必须在 data 对象上存在才能让 Vue 将它转换为响应式的 。关于数组作者通过重写push/pop/shift/unshift/splice/reverse/sort这些方法来实现数据的相应绑定 ,其余的操作无法触发页面更新;
对策:关于对象可以通过Vue.$set(obj,key,value) ,组件中通过this.$set(obj,key,value)实现新增 ,修改属性vue可以相应更新视图 。关于数组也可以通过Vue.$set(obj,key,value),或者作者重写的那些方法来操作;
10 、异步操作放在created还是mouted?
如果有些数据需要在初始化时就渲染的 ,比如select下拉框的下拉内容 ,在mouted中请求 。好处如下
页面初始化速度更快,减少用户等待时间 放在 created 中有助于一致性 ,因为ssr 不支持 beforeMount 、mounted 钩子函数11 、vue-router的钩子函数有哪些?
组件内部钩子:beforeRouterEnter() 、beforeRouterLeave 、beforeRouterUpdate
12 、页面如何跳转?如何跨页面传参数?
router-link标签跳转 路由如下跳转 ,顺便把参数传了 。如下 this.$router.push({ path: /url, query: { par:parid } })接受参数
var parid = this.$route.query.par;13 、vue子组件的生命周期?子元素在什么时候挂载?
父:beforeCreate 首先初始化父原素 父:created 父原素挂载数据 父:beforeMounte 父原素开始挂载dom ,tpl里遇到子组件 子:beforeCeate 子组件开始挂载数据 子:created 子元素数据挂载成功 子:beforeMount 子元素开始挂载dom 子:mounted 子元素dom挂载结束 父:mounted 父原素dom挂载结束 父:beforeUpdate 下面开始类似于dom事件流 子:beforeUpdate 子:updated 父:updated 父:beforeDestory 子:beforeDestory 子:destroyed 父:destoryed子元素在父元素挂载dom时 ,开始加载 。子元素一直到加载完毕dom后 ,父原素结束dom挂载。后面就类似于dom事件流了 。
14 、vue的import和node的require区别?
JS支持两种模块化方式 ,commonjs和ES6 。
commonjs用于nodejs ,同步加载模块。ES6的import为了不卡顿 ,异步加载模块 。
新版Nodejs也支持使用import ,但是需要修改文件后缀名为.mjs ,或者在package.json中 ,制定type字段为module 。
五:ES6
1 、箭头函数与es5函数区别?
箭头函数的this指向是固定的 ,普通的this指向是可变的 let a = { name: sunq, fn:function(action){ console.log(this.name + love + action); } } let b = {name:sunLi} // 正常的this指向调用他的对象 a.fn(basketball); // sunq love basketball // 改变this指向 a.fn.apply(b,[football]); // sunLi love football // 如果将a对象的fn函数改成箭头函数,this.name会是undefined // 箭头函数的this指向不会改变 ,且总是指向函数定义生效时所在的对象 。 不可以当作构造函数 ,不可以对箭头函数使用new命令,否则会抛出一个错误 。 var Person = function(name){ this.name = name; } let sunq = new Person(sq); // {name: sq} var Person = (name) => { this.name = name; } let sunq = new Person(sq); // 报错 Person is not a constructor 无arguments对象 不可以使用yield命令 ,因此箭头函数不能用作 Generator 函数 。2、ES6提供的解决异步交互的新方法?区别?
Promise 、Genarate 、async\await
3、宏任务和微任务有哪些?执行顺序?
4 、先并行请求2个接口后 ,再请求第3个接口 ,如何处理?
使用Promise.all()方法 ,将两个promise传入all方法 ,拿到异步结果再请求第三个
明明知道的语法 ,面试官一问我偏偏就是跟实际场景联系不到一块 ,
5 、js的数据类型
string number null undefined boolean object bigInt symbol
6 、说几个ES6新增的数组的方法 详情
map 实例方法 ,类似于forEach ,但是返回新数组
find和findIndex 实例方法 ,传入一个匿名函数 ,ruturn出符合条件的项或下标
... 拓展运算符 基本功能是将一个数组转为用逗号分隔的参数序列
7 、for in和for of的区别
for in适合遍历对象 ,遍历数组拿到的是下标 for of适合遍历数组,遍历数组直接拿到数组的项 。for of只能遍历具备iterator接口的类型 。8 、多个数据请求 ,如何顺序执行?
使用promise的then方法 ,或者写多个promise,async中使用await顺序执行 。
9 、proxy的理解 ,与defineProperty的区别?
proxy直接给所有属性设置拦截 ,defineProperty只给指定的设置 proxy拦截后要用Proxy实例调用 ,defineProperty可以直接使用原对象 // es6的proxy let obj = {name:1,sex:2} let p = new Proxy(obj,{ get:(value,key)=>{ return sq的+obj[key] } }); p.name // sq的1 p.sex // sq的2 // es5的代理 let newObj = {name:1,sex:2} Object.defineProperty(newObj,name,{ get:function(val){ //defineProperty中get函数没有入参 return sq的 + val } }) newObj.name // sq的undefined newObj.sex // 210 、谈谈promise的原理?
六:ElementUI
1 、如果需要修改样式怎么做?
再写一个样式表引入 ,!important覆盖 样式穿透2 、如何通过继承扩展 element-ui 组件的功能?
通过继承扩展 element-ui 组件的功能_elementui扩展组件_在厕所喝茶的博客-CSDN博客
七:Jquery
1 、如何获取同一个cl下 ,最后一个li?
$("#id").children().eq(3).remove(); // 获取多个class中的某一个 $(".className").eq(n).attr("class") // 可行 $(".className")[n].attr("class") // 不可行2 、window.load和$(document).ready()的区别?执行先后顺序?
window.onload必须等到页面内包括图片的所有元素加载完毕后才能执行 。 $(document).ready()是DOM结构绘制完毕后就执行 ,不必等到加载完毕 。通常简写为$()总结:ready事件在load事件加载之前完成 。
3 、如何绑定一个点击事件?
// 方式一 $("#id").on(click,function(){}); // 方式二 $("#id").click(function(){}); // 方式三 $("#id").bind("click",function(){});4、Jquery常用动画?
八:Git的使用
1 、常用哪些语句?
pull 、commit、push 、reset 、merge 、log 、branch 、stash
stash如何使用?
git stash -m xxx git stash pop2 、版本回退语句?soft和hard区别?
git reset --soft 版本号
git reset --hard 版本号
soft会退后 ,代码改动在本地还保存的有 。hard会删除本地改动 ,彻底抹去该版本的痕迹。详情
3 、合并分支注意事项?
将自己分支合到目标分支前 ,最好先将目标分支合到自己分支上处理完冲突 ,再将自己的分支合回目标分支 。
4 、如何进行分支管理?
九:敏捷开发
1 、什么是敏捷开发?
个人理解 ,敏捷开发就是把一个大需求拆为多个独立的小需求 。每个小需求可独立开发 、测试 、上线 ,循序渐进的完成整个系统。每个版本的周期可能比较短,比如2周 ,或者4周 。
比如某公司需要开发维护一个巨大的平台 ,可能把平台外包给多个公司干 。如果用如上方法,并行开发 ,可显著缩减工期 。
如果想要保证每个版本又快又顺利的上线 ,需要有完善的角色支持和流程规范 。
2 、敏捷开发的好处?
个人理解 ,当团队稍大 ,工期很紧时 ,如何有条不紊的保证版本质量就需要一套有效的流程了 。
比如一个团队同时收到3个需求 ,每个需求分发给多个前后端开发 。作为版本负责人或者项目负责人 ,如何把控每个人的代码质量、完成进度 、过程留痕 、风险规避 ,其实是有难度的 。
一个需求如果经过 ,需求澄清、方案设计 、设计评审 、需求传递 、版本排期/评审 、开发划分 、版本开发 、测试用例评审 、内部测试 、代码评审 、灰度试用&测试 、版本发布 、业务验收等完整的流程 ,可以有效地降低犯错的几率 。也方便后期的查找责任人 ,总结各环节的问题 ,提升团队的工作效率,使交付越来越平稳 。
十:开源项目
部分公司面试要求中有写 ,维护有开源项目且具有50个star者优先 。
我平时有维护一个具备管理端/用户端/服务端的个人网站:sunqs blog
面试过程中无话可说时 ,可以拿出来聊聊,缓解尴尬 。
博客的代码全部开源:源码Github地址
十一:网络相关
1 、http和https的区别?
HTTP 明文传输 ,数据都是未加密的 ,安全性较差 ,HTTPS(SSL+HTTP) 数据传输过程是加密的 ,安全性较好 。 使用 HTTPS 协议需要到 CA(Certificate Authority ,数字证书认证机构) 申请证书 ,一般免费证书较少 ,因而需要一定费用。证书颁发机构如:Symantec、Comodo 、GoDaddy 和 GlobalSign 等 。 HTTP 页面响应速度比 HTTPS 快 ,主要是因为 HTTP 使用 TCP 三次握手建立连接 ,客户端和服务器需要交换 3 个包 ,而 HTTPS除了 TCP 的三个包 ,还要加上 ssl 握手需要的 9 个包 ,所以一共是 12 个包 。 http 和 https 使用的是完全不同的连接方式,用的端口也不一样 ,前者是 80 ,后者是 443。 HTTPS 其实就是建构在 SSL/TLS 之上的 HTTP 协议,所以 ,要比较 HTTPS 比 HTTP 要更耗费服务器资源 。2 、常见状态码
200:成功返回 304:网页未更改有缓存 ,未重新请求 404:请求资源不在 500:服务器内部错误 503:服务器超时十二:webpack
1、dependencies和devDependencies区别
安装时 --save -dev会放在devDependencies中 ,--save放在dependencies
devDependencies中安装的是开发环境使用的包 ,比如eslint 、vue-cli-serve;
dependencies中安装的是生产和开发环境下都需要使用的包 ,比如vue 、element 、vant等
devDependencies中的依赖模块 ,在生产环境下不会被打入包内
十三:页面优化
1 、某个页面加载较慢 ,从哪些方向分析 、解决问题?
传统页面首先判断是接口慢 ,还是页面慢 。如果接口慢 ,后端优化 。
如果前端页面加载慢 ,看是否是因为图片等资源过大 ,尝试替换不同格式体积的图片 。定位是否是某些数据处理的函数 ,比较耗时 。或者是否循环操作DOM,js生成dom后再批量插入 。
如果页面直接卡死 ,就需要分析是否内存泄漏 。比如大屏展示的定时刷新卡死 ,排查思路可如下:
使用chrome的任务管理器,操作页面观察的内存占用的变化 。定位到是哪些操作 ,哪块代码导致内存占用飙升 。
因为js并没有直接释放缓存的语法 ,只有靠浏览器的垃圾回收机制自动清理 。我们需要做的是及时给不需要的变量赋空 。
特别注意大数据的循环实例化后 ,变量是否及时赋空 。定时器等闭包方法中是否存在内存泄漏 ,循环渲染地图时 ,是否先将之前地图数据清空等。
单页面应用单页面一般不会某个页面加载慢 ,一般都集中在首屏加载时白屏较久 。处理方法可参考上文中 。
2 、使用缓存
有些接口没必要每次打开页面都请求 ,可用cookie计时。某个时间段内不重新获取 。 某些数据初始化时加载一次即可 ,可通过cookie计时 ,某个时间段内不用请求 。 某些数据可用localstorage存储 ,默认填充input ,更新时才重写缓存 。这个只是用户体验好些十四:Node.js
1 、用过哪些插件(express中间件)?
cors 解决express的跨域问题 body-parser 解析post数据 mongoClient mongodb官方提供的Node.js驱动 crypto 用来加密 ,添加签名 nodemailer 用来发邮件2 、mongodb和mysql的区别?
mongodb是文档型非关系型数据库,mysql是关系型数据库 mongodb以json形式存储数据 ,字段可以随意添加 ,mysql的字段需要提前确定好 mysql多表之间只能通过外键建立关系,mysql可以多层嵌套也可拆表并关联创心域SEO版权声明:以上内容作者已申请原创保护,未经允许不得转载,侵权必究!授权事宜、对本内容有异议或投诉,敬请联系网站管理员,我们将尽快回复您,谢谢合作!