前端高频面试题总结(看完这些面试轻轻松松)

cookies、sessionStorage和localStorage区别


分析:考察求职者对于本地存储的方式是否熟悉,因为初学者会把 cookies 和 sessionStorage 与localStorage 搞混

解答:

cookies:它是网站用来标记用户身份的一段数据,通常情况下是一段加密的字符串,并且默认情况之下只会在同源的 HTTP 请求中携带

sessionStorage:它是浏览器本地存储的一种方式,以键值对的形式进行存储,并且存储的数据会在浏览器关闭之后自动删除

localStorage:它也是浏览器本地存储的一种方式,和 sessionStorage 一样,也是以键值对的形式存在的,不过它存储的是一个持久化的数据,一般不主动删除,数据会一直存在

display:none;与visibility:hidden;opacity:0;


分析:这是一个比较容易出现错误的 CSS 问题,没有大量实际开发经验的开发者,很难了解它们之间的区别

解答:

共同点:它们都可以让元素不可见

不点:

display:none; 会让元素完全从染树中消失,不染的时候不占据任何空间。而 visibility.hidden; 不会让元素从染树消失,不染时元素继续占据空间,只是内容不可见display: none;是非继承属性,子孙节点消失是因为元素从渲染树消失造成,通过修改子孙节点属性无法显示;而visibility: hidden;是继承属性,子孙节点消失由于继承了 hidden,通过设置visibility: visible;可以让子孙节点显式

opacity:0,该元素隐藏起来了,但不会改变页面布局,

并且,如果该元素已经绑定一些事件,如click事件,

那么点击该区域,也能触发点击事件的

visibility:hidden,该元素隐藏起来了,

但不会改变页面布局,但是不会触发该元素已经绑定的事件

display=none,把元素隐藏起来,并且会改变页面布局,

可以理解成在页面中把该元素删除掉一样

为什么要初始化CSS样式


问题: 什么是 postcss,以及 postcss 有什么作用?

分析:有很多已经工作了两年、三年的开发者依然无法解释清楚 postcss 是什么,以及它的作用。

解答:

·首先明确 postcss 是一个平台

·基于这个平台,可以使用一些插件,来优化 css 的代码。比如说: autoprefixer 插件,他就需要基于 postcss 使用,作用是可以帮助我们为 css 增加上不同的浏览器前

闭包


分析:闭包的问题一直是在前端方面非常被关注的问题,这个问题作为面试题经久不衰

解答:

什么是闭包?

闭包就是能够读取其他函数内部变量的一一个通常情况下,我们会在一个函数中,去创建另外一个函数然后通过新创建的这个函数来访问上层函数的局部变量,被访问到的局部变量会始终保存在内存之中

原型与原型链


null、undefined


KeepAlive


同步和异步


路由VueRouter两种模式hash和history


vue中传递参数


vue双向绑定原理


vue生命周期


typeof与instanceof


bind、call、apply


event loop(事件循环)


promise


webpack


this对象


js数据类型


  1. 基本数据类型

  1. Number、String、Boolean、Null、Undefined、Symbol、bigInt

  1. 引用数据类型

  1. object、Array、Date、Function、RegExp

== 和 ===的区别


  1. ==是非严格意义上的相等

  1. 值相等就相等

  1. ===是严格意义上的相等,会比较两边的数据类型和值大小

  1. 值和引用地址都相等才相等

js数组和对象的遍历方式


  1. for in

  1. for

  1. forEach

  1. for-of

map与forEach的区别


  1. forEach 方法,是最基本的方法,就是遍历与循环,默认有 3 个传参:分别是遍历的数组内

容 item、数组索引 index、和当前遍历数组 Array

  1. map 方法,基本用法与 forEach 一致,但是不同的,它会返回一个新的数组,所以 callback

需要有 return 值,如果没有,会返回 undefined

箭头函数与普通函数的区别?


  1. 函数体内的 this 对象,就是定义时所在的对象,而不是使用时所在的对象

  1. 不可以当作构造函数,也就是说,不可以使用 new 命令,否则会抛出一个错误

  1. 不可以使用 arguments 对象,该对象在函数体内不存在。如果要用,可以用 Rest 参数代替

  1. 不可以使用 yield 命令,因此箭头函数不能用作 Generator 函数

同源策略


同源指的是域名、协议、端口号相同

如何解决跨域


  1. jsonp跨域

  1. document.domain + iframe 跨域

  1. nodejs中间件代理跨域

  1. 后端在头部信息里面设置安全域名

attribute 和 property 的区别是什么?


  1. attribute 是 dom 元素在文档中作为 html 标签拥有的属性

  1. property 就是 dom 元素在 js 中作为对象拥有的属性。

  1. 对于 html 的标准属性来说,attribute 和 property 是同步的,是会自动更新的

  1. 但是对于自定义的属性来说,他们是不同步的

script 引入方式?


  1. html 静态 <script> 引入

  1. js 动态插入 <script>

  1. <script defer> : 异步加载,元素解析完成后执行

  1. <script async> : 异步加载,但执行时会阻塞元素渲染

数组(array)方法


  1. map : 遍历数组,返回回调返回值组成的新数组

  1. forEach : 无法 break ,可以用 try/catch 中 throw new Error 来停止

  1. filter : 过滤

  1. some : 有一项返回 true ,则整体为 true

  1. every : 有一项返回 false ,则整体为 false

  1. join : 通过指定连接符生成字符串

  1. push / pop : 末尾推入和弹出,改变原数组, 返回推入/弹出项

  1. unshift / shift : 头部推入和弹出,改变原数组,返回操作项

  1. sort(fn) / reverse : 排序与反转,改变原数组

  1. concat : 连接数组,不影响原数组, 浅拷贝

  1. slice(start, end) : 返回截断后的新数组,不改变原数组

  1. splice(start,number,value…): 返回删除元素组成的数组,value 为插入项,改变原数组

  1. indexOf / lastIndexOf(value, fromIndex) : 查找数组项,返回对应的下标

  1. reduce / reduceRight(fn(prev, cur) ,defaultPrev) : 两两执行,

  1. prev 为上次化简函数的return 值,cur 为当前值(从第二项开始)

JavaScript 深浅拷贝?


  1. 浅拷贝

  1. Object.assign

  1. 深拷贝

  1. 可以通过 JSON.parse(JSON.stringify(object)) 来解决

说说面向对象编程思想?


  1. 基本思想是使用对象,类,继承,封装等基本概念来进行程序设计

  1. 优点

  1. 易维护

  1. 易扩展

  1. 开发工作的重用性、继承性高,降低重复工作量。

  1. 缩短了开发周期

项目性能优化


  1. 减少 HTTP 请求数

  1. 减少 DNS 查询

  1. 使用 CDN

  1. 避免重定向

  1. 图片懒加载

  1. 减少 DOM 元素数量

  1. 减少 DOM 操作

  1. 使用外部 JavaScript 和 CSS

  1. 压缩 JavaScript、CSS、字体、图片等

  1. 优化 CSS Sprite

  1. 使用 iconfont

  1. 多域名分发划分内容到不同域名

  1. 尽量减少 iframe 使用

  1. 避免图片 src 为空

  1. 把样式表放在 link 中

  1. 把 JavaScript 放在页面底部

什么是单线程,和异步的关系?


  1. 单线程 :只有一个线程,只能做一件事

  1. 原因 : 避免 DOM 渲染的冲突

  1. 浏览器需要渲染 DOM

  1. JS 可以修改 DOM 结构

  1. JS 执行的时候,浏览器 DOM 渲染会暂停

  1. 两段 JS 也不能同时执行(都修改 DOM 就冲突了)

  1. webworker 支持多线程,但是不能访问 DOM

  1. 解决方案 :异步

说说负载均衡?


  1. 单台服务器共同协作,不让其中某一台或几台超额工作,发挥服务器的最大作用

  1. http 重定向负载均衡:调度者根据策略选择服务器以 302 响应请求,

  1. 缺点只有第一次有效果,后续操作维持在该服务器 dns 负载均衡:解析域名时,

  1. 访问多个 ip 服务器中的一个(可监控性较弱)原因 - 避免 DOM 渲染的冲突

  1. 反向代理负载均衡:访问统一的服务器,由服务器进行调度访问实际的某个服务器,

  1. 对统一的服务器要求大,性能受到 服务器群的数量

作用域链?


  1. 作用域链可以理解为一组对象列表,包含 父级和自身的变量对象,

  1. 因此我们便能通过作用域链访问到父级里声明的变量或者函数

逐进增强和优雅降级


一. 逐进增强

针对低版本浏览器进行构建页面,保证最基本的功能,然后再针对高版本浏览器进行效果、交互等改进和追加功能达到更好的用户体验。

二. 优雅降级

一开始就构建完整的功能,然后再针对低版本浏览器进行兼容

vue优点


  1. 轻量级

  1. 速度快

  1. 简单易学

  1. 低耦合

  1. 可重用性

  1. 独立开发

  1. 文档齐全,且文档为中文文档

v-show和v-if指令的共同点和不同点


  1. 相同点:都可以控制dom元素的显示和隐藏

  1. 不同点:v-show只是改变display属性,dom元素并未消失,切换时不需要重新渲染页面

  1. v-if直接将dom元素从页面删除,再次切换需要重新渲染页面

  1. v-if 是动态的向 DOM 树内添加或者删除 DOM 元素

  1. v-show 是通过设置 DOM 元素的 display 样式属性控制显隐

  1. v-if 切换有一个局部编译/卸载的过程,切换过程中合适地销毁和重建内部的事件监听和子组件

  1. v-show 只是简单的基于 css 切换

性能消耗

  1. v-if 有更高的切换消耗

  1. v-show 有更高的初始渲染消耗

使用场景

  1. v-if 适合运营条件不大可能改变

  1. v-show 适合频繁切换

如何让CSS只在当前组件中起作用


  1. scoped

如何获取dom


  1. 给dom元素加ref=‘refname’,然后通过this.$refs.refname进行获取dom元素

说出几种vue当中的指令和它的用法


  1. v-model

  1. v-on

  1. v-html

  1. v-text

  1. v-once

  1. v-if

  1. v-show

vue-loader是什么?它的用途是什么?


  1. vue文件的一个加载器,将template/js/style转换为js模块

  1. 用途:js可以写es6、style样式

为什么用key


  1. 给每个dom元素加上key作为唯一标识 ,diff算法可以正确的识别这个节点,使页面渲染更加迅速。

v-model的使用


  1. v-model用于表单的双向绑定,可以实时修改数据

请说出vue.cli项目中src目录每个文件夹和文件的用法


  1. components存放组件

  1. app.vue主页面入口

  1. index.js主文件入口

  1. ass存放静态资源文件

分别简述computed和watch的使用场景


  1. 用官网的一句话来说,所有需要用到计算的都应该使用计算属性。

  1. 多条数据影响一条数据时使用计算属性,使用场景购物车。

  1. 如果是一条数据更改,影响多条数据时,使用watch,使用场景搜索框。

computed 计算属性 : 依赖其它属性值,并且 computed 的值有缓存,

只有它依赖的 属性值发生改变,下一次获取 computed 的值时才会重新计算 computed 的值。

watch 侦听器 : 更多的是观察的作用,无缓存性,

类似于某些数据的监听回调,每 当监听的数据变化时都会执行回调进行后续操作。

运用场景:

当我们需要进行数值计算,并且依赖于其它数据时,

应该使用 computed,因为可以利用 computed的缓存特性,

避免每次获取值时,都要重新计算。

当我们需要在数据变化时执行异步或开销较大的操作时,

应该使用 watch,使用 watch 选项允许我们执行异步操作 ( 访问一个 API ),

限制我们执行该操作的频率, 并在我们得到最终结果前,

设置中间状态。这些都是计算属性无法做到的。

多个因素影响一个显示,用Computed;

一个因素的变化影响多个其他因素、显示,用Watch;

Computed 和 Methods 的区别

computed: 计算属性是基于它们的依赖进行缓存的,

只有在它的相关依赖发生改变时才会重新求值对于 method ,只要发生重新渲染,

method 调用总会执行该函数

v-on可以监听多个方法吗?


  1. 可以,比如 v-on=“onclick,onbure”

$nextTick的使用


  1. 在data()中的修改后,页面中无法获取data修改后的数据,

  1. 使用$nextTick时,当data中的数据修改后,可以实时的渲染页面

vue组件中data为什么必须是一个函数?


  1. 因为javaScript的特性所导致,在component中,

  1. data必须以函数的形式存在,不可以是对象。

  1. 组件中的data写成一个函数,数据以函数返回值的形式定义,

  1. 这样每次复用组件的时候,都会返回一份新的data,

  1. 相当于每个组件实例都有自己私有的数据空间,

  1. 他们值负责各自维护数据,不会造成混乱。

  1. 而单纯的写成对象形式,就是所有组件实例共用了一个data,

  1. 这样改一个全部都会修改。

渐进式框架的理解


  1. 主张最少

  1. 可以根据不同的需求选择不同的层级

vue在双向数据绑定是如何实现的?


  1. vue双向数据绑定是通过数据劫持、组合、发布订阅模式的方式来实现的,

  1. 也就是说数据和视图同步,数据发生变化,视图跟着变化,

  1. 视图变化,数据也随之发生改变

  1. 核心:关于vue双向数据绑定,其核心是Object.defineProperty()方法

单页面应用和多页面应用区别及缺点


  1. 单页面应用(SPA),通俗的说就是指只有一个主页面的应用,

  1. 浏览器一开始就加载所有的js、html、css。

  1. 所有的页面内容都包含在这个主页面中。但在写的时候,还是分开写,

  1. 然后再加护的时候有路由程序动态载入,单页面的页面跳转,

  1. 仅刷新局部资源。多用于pc端。

  1. 多页面(MPA),就是一个应用中有多个页面,页面跳转时是整页刷新

  1. 单页面的优点:用户体验好,快,内容的改变不需要重新加载整个页面,

  1. 基于这一点spa对服务器压力较小;前后端分离,页面效果会比较酷炫

  1. 单页面缺点:不利于seo;导航不可用,如果一定要导航需要自行实现前进、

  1. 后退。初次加载时耗时多;页面复杂度提高很多。

Vue 项目中为什么要在列表组件中写 key,其作用是什么?


  1. key是给每一个vnode的唯一id,可以依靠key,更准确,

  1. 更快的拿到oldVnode中对应的vnode节点。

  1. 更准确

  1. 因为带key就不是就地复用了,

  1. 在sameNode函数 a.key === b.key对比中可以避免就地复用的情况。

  1. 所以会更加准确。

  1. 更快

  1. 利用key的唯一性生成map对象来获取对应节点,比遍历方式更快。

父组件和子组件生命周期钩子执行顺序是什么?


  1. 加载渲染过程

父 beforeCreate -> 父 created -> 父 beforeMount -> 子 beforeCreate -> 子 created -> 子 beforeMount -> 子 mounted -> 父 mounted

子组件更新过程

父 beforeUpdate -> 子 beforeUpdate -> 子 updated -> 父 updated

父组件更新过程

父 beforeUpdate -> 父 updated

销毁过程

父 beforeDestroy -> 子 beforeDestroy -> 子 destroyed -> 父 destroyed

谈一谈你对 nextTick 的理解?


  1. 当你修改了data的值然后马上获取这个dom元素的值,

  1. 是不能获取到更新后的值,你需要使用$nextTick这个回调,

  1. 让修改后的data值渲染更新到dom元素之后在获取,才能成功。

vue组件中data为什么必须是一个函数?


  1. 因为JavaScript的特性所导致,在component中,

  1. data必须以函数的形式存在,不可以是对象。

  1. 组建中的data写成一个函数,数据以函数返回值的形式定义,

  1. 这样每次复用组件的时候,都会返回一份新的data,

  1. 相当于每个组件实例都有自己私有的数据空间,

  1. 它们只负责各自维护的数据,不会造成混乱。

  1. 而单纯的写成对象形式,就是所有的组件实例共用了一个data,

  1. 这样改一个全都改了。

vue和jQuery的区别


jQuery是使用选择器($)选取DOM对象,对其进行赋值、取值、事件绑定等操作,

其实和原生的HTML的区别只在于可以更方便的选取和操作DOM对象,

而数据和界面是在一起的。

比如需要获取label标签的内容:$("lable").val();,

它还是依赖DOM元素的值。

Vue则是通过Vue对象将数据和View完全分离开来了。

对数据进行操作不再需要引用相应的DOM对象,可以说数据和View是分离的,

他们通过Vue对象这个vm实现相互的绑定。这就是传说中的MVVM。

delete和Vue.delete删除数组的区别


  1. delete只是被删除的元素变成了 empty/undefined 其他的元素的键值还是不变。

  1. Vue.delete 直接删除了数组 改变了数组的键值。

SPA首屏加载慢如何解决


  1. 安装动态懒加载所需插件;使用CDN资源。

vue项目是打包了一个js文件,一个css文件,还是有多个文件?


  1. 根据vue-cli脚手架规范,一个js文件,一个CSS文件。

vue更新数组时触发视图更新的方法


  1. push();

  1. pop();

  1. shift();

  1. unshift();

  1. splice();

  1. sort();

  1. reverse()

什么是 vue 生命周期?有什么作用?


  1. 每个 Vue 实例在被创建时都要经过一系列的初始化过程——例如,

  1. 需要设置数据监听、编译模板、将实例挂载到 DOM 并在数据变化时更新 DOM 等。

  1. 同时在这个过程中也会运行一些叫做 生命周期钩子 的函数,

  1. 这给了用户在不同阶段添加自己的代码的机会。

第一次页面加载会触发哪几个钩子?


  1. beforeCreate, created, beforeMount, mounted

vue获取数据在一般在哪个周期函数


  1. created

  1. beforeMount

  1. mounted

created和mounted的区别


  1. created:在模板渲染成html前调用,

  1. 即通常初始化某些属性值,然后再渲染成视图。

  1. mounted:在模板渲染成html后调用,通常是初始化页面完成后,

  1. 再对html的dom节点进行一些需要的操作。

vue生命周期的理解


  1. 总共分为8个阶段创建前/后,载入前/后,更新前/后,销毁前/后。

  1. 创建前/后: 在beforeCreated阶段,vue实例的挂载元素$el和数据对象data都为undefined,还未初始化。

  1. 在created阶段,vue实例的数据对象data有了,$el还没有。

  1. 载入前/后:在beforeMount阶段,vue实例的$el和data都初始化了,

但还是挂载之前为虚拟的dom节点,data.message还未替换。

  1. 在mounted阶段,vue实例挂载完成,data.message成功渲染。

  1. 更新前/后:当data变化时,会触发beforeUpdate和updated方法。

  1. 销毁前/后:在执行destroy方法后,对data的改变不会再触发周期函数,

说明此时vue实例已经解除了事件监听以及和dom的绑定,但是dom结构依然存在。

vuex是什么?


  1. vue框架中状态管理。

vuex有哪几种属性?


有五种,State、 Getter、Mutation 、Action、 Module

state: 基本数据(数据源存放地)

getters: 从基本数据派生出来的数据

mutations : 提交更改数据的方法,同步!

actions : 像一个装饰器,包裹mutations,使之可以异步。

modules : 模块化Vuex

vue全家桶


  1. vue-cli、vuex、vueRouter、Axios

vue-cli 工程常用的 npm 命令有哪些?


  1. npm install 下载 node_modules 资源包的命令

  1. npm run dev 启动 vue-cli 开发环境的 npm 命令

  1. npm run build vue-cli 生成 生产环境部署资源 的 npm 命令

  1. npm run build–report 用于查看 vue-cli 生产环境部署资源文件大小的 npm 命令

请说出 vue-cli 工程中每个文件夹和文件的用处?


  1. build 文件夹是保存一些 webpack 的初始化配置。

  1. config 文件夹保存一些项目初始化的配置

  1. node_modules 是 npm 加载的项目依赖的模块

  1. src 目录是我们要开发的目录:

  1. assets 用来放置图片

  1. components 用来放组件文件

  1. app.vue 是项目入口文件

  1. main.js 项目的核心文件

v-for 与 v-if 的优先级?


  1. v-for 和 v-if 同时使用,有一个先后运行的优先级,

  1. v-for 比 v-if 优先级更高,

  1. 这就说明在v-for 每次的循环赋值中每一次调用 v-if 的判断,

  1. 所以不推荐 v-if 和 v-for 在同一个标签中同时使用。

vue 常用的修饰符?


事件修饰符

  1. .stop 阻止事件继续传播

  1. .prevent 阻止标签默认行为

  1. .capture 使用事件捕获模式,即元素自身触发的事件先在此处处理,然后才交由内部元素进行处理

  1. .self 只当在 event.target 是当前元素自身时触发处理函数

  1. .once 事件只会触发一次

  1. .passive 告诉浏览器你不想阻止事件的默认行为

  1. v-model 的修饰符

  1. .lazy 通过这个修饰符,转变为在 change 事件再同步

  1. .number 自动将用户输入值转化为数值类型

  1. .trim 自动过滤用户输入的收尾空格

键盘事件修饰符

  1. .enter

  1. .tab

  1. .delete (捕获“删除”和“退格”键)

  1. .esc

  1. .space

  1. .up

  1. .down

  1. .left

  1. .right

系统修饰符

  1. .ctrl

  1. .alt

  1. .shift

  1. .meta

鼠标按钮修饰符

  1. .left

  1. .right

  1. .middle

vue 事件中如何使用 event 对象?


  1. 获取事件对象,方法参数传递 $event 。注意在事件中要使用 $ 符号

  1. <button @click="Event($event)">事件对象</button>

vue组件传值方式有哪些


  1. 父传子:子组件通过props[‘xx’] 来接收父组件传递的属性 xx 的值

  1. 子传父:子组件通过 this.$emit(‘fnName’,value) 来传递,

  1. 父组件通过接收 fnName 事件方法来接收回调

  1. 其他方式:通过创建一个bus,进行传值

  1. 使用Vuex

Vue.js 2.0 采用数据劫持(Proxy 模式)

结合发布者-订阅者模式(PubSub 模式)的方式,

通过 Object.defineProperty()来劫持各个属性的 setter,getter,

在数据变动时发布消息给订阅者,触发相应的监听回调。

每个组件实例都有相应的watcher程序实例,

它会在组件渲染的过程中把属性记录为依赖,

之后当依赖项的setter被调用时,会通知watcher重新计算,

从而致使它关联的组件得以更新。

Vue.js 3.0, 放弃了Object.defineProperty ,

使用更快的ES6原生 Proxy (访问对象拦截器, 也称代理器)

vue 中子组件调用父组件的方法?


  1. 直接在子组件中通过 this.$parent.event 来调用父组件的方法。

在子组件里用$emit()向父组件触发一个事件,父组件监听这个事件就行了。

父组件把方法传入子组件中,在子组件里直接调用这个方法。

46、 如何让 CSS 只在当前组件中起作用?

在组件中的 style 前面加上 scoped

如何获取 dom?


  1. ref="domName" 用法:this.$refs.domName

vue路由跳转


(一)声明式导航router-link

不带参数:

// 注意:router-link中链接如果是'/'开始就是从根路由开始,

如果开始不带'/',则从当前路由开始。

<router-link :to="{name:'home'}">

<router-link :to="{path:'/home'}"> //name,path都行, 建议用name

带参数:

<router-link :to="{name:'home', params: {id:1}}">

<router-link :to="{name:'home', query: {id:1}}">

<router-link :to="/home/:id">

//传递对象

<router-link

:to="{

name:'detail',

query: {item:JSON.stringify(obj)}

}"></router-link>

(二)this.$router.push()

不带参数:

this.$router.push('/home')

this.$router.push({name:'home'})

this.$router.push({path:'/home'})

query传参

1.路由配置:

name: 'home',

path: '/home'

2.跳转:

this.$router.push({name:'home',query: {id:'1'}})

this.$router.push({path:'/home',query: {id:'1'}})

3.获取参数

html取参: $route.query.id

script取参: this.$route.query.id

params传参

1.路由配置:

name: 'home',

path: '/home/:id'(或者path: '/home:id')

2.跳转:

this.$router.push({name:'home',params: {id:'1'}})

注意:

// 只能用 name匹配路由不能用path

// params传参数(类似post) 路由配置 path: "/home/:id"

或者 path: "/home:id"否则刷新参数消失

3.获取参数

html取参:$route.params.id

script取参:this.$route.params.id

直接通过path传参

1.路由配置:

name: 'home',

path: '/home/:id'

2.跳转:

this.$router.push({path:'/home/123'})

或者:

this.$router.push('/home/123')

3.获取参数:

this.$route.params.id

params和query的区别

query类似 get,跳转之后页面 url后面会拼接参数,类似?id=1。

非重要性的可以这样传,密码之类还是用params,刷新页面id还在。

params类似 post,跳转之后页面 url后面不会拼接参数。

(三)this.$router.replace()

用法同上

(四)this.$router.go(n)

前或者向后跳转n个页面,n可为正整数或负整数

区别:

this.$router.push

跳转到指定url路径,并在history栈中添加一个记录,

点击后退会返回到上一个页面

this.$router.replace

跳转到指定url路径,但是history栈中不会有记录,

点击返回会跳转到上上个页面 (就是直接替换了当前页面)

this.$router.go(n)

向前或者向后跳转n个页面,n可为正整数或负整数

过滤器 (Filter)


  1. 在Vue中使用filters来过滤(格式化)数据,filters不会修改数据,

  1. 而是过滤(格式化)数据,改变用户看到的输出

  1. (计算属性 computed ,方法 methods 都是通过修改数据来处理数据格式的输出显示。

  1. 使用场景: 比如需要处理时间、数字等的的显示格式;

axios是什么


  1. 易用、简洁且高效的http库, 支持node端和浏览器端,支持Promise,支持拦截器等高级配置。

sass是什么?如何在vue中安装和使用?


  1. sass是一种CSS预编译语言安装和使用步骤如下。

  1. 用npm安装加载程序( sass-loader、 css-loader等加载程序)。

  1. 在 webpack.config.js中配置sass加载程序。

Vue.js页面闪烁


  1. Vue. js提供了一个v-cloak指令,该指令一直保持在元素上,

直到关联实例结束编译。当和CSS一起使用时,

这个指令可以隐藏未编译的标签,直到实例编译结束。用法如下。

[v-cloak]{

display:none;

}

<div v-cloak>{{ title }}</div>

vue常用指令


  1. v-model 多用于表单元素实现双向数据绑定(同angular中的ng-model)

  1. v-bind 动态绑定 作用: 及时对页面的数据进行更改

  1. v-on:click 给标签绑定函数,可以缩写为@,例如绑定一个点击函数 函数必须写在methods里面

  1. v-for 格式: v-for=“字段名 in(of) 数组json” 循环数组或json(同angular中的ng-repeat)

  1. v-show 显示内容 (同angular中的ng-show)

  1. v-hide 隐藏内容(同angular中的ng-hide)

  1. v-if 显示与隐藏 (dom元素的删除添加 同angular中的ng-if 默认值为false)

  1. v-else-if 必须和v-if连用

  1. v-else 必须和v-if连用 不能单独使用 否则报错 模板编译错误

  1. v-text 解析文本

  1. v-html 解析html标签

  1. v-bind:class 三种绑定方法

  1. 对象型 ‘{red:isred}’

  1. 三元型 ‘isred?“red”:“blue”’

  1. 数组型 ‘[{red:“isred”},{blue:“isblue”}]’

  1. v-once 进入页面时 只渲染一次 不在进行渲染

  1. v-cloak 防止闪烁

  1. v-pre 把标签内部的元素原位输出

$route和$router的区别


  1. $route是“路由信息对象”,包括path,params,hash,query,

  1. fullPath,matched,name等路由信息参数。

  1. $router是“路由实例”对象包括了路由的跳转方法,钩子函数等

怎样理解 Vue 的单项数据流


  1. 数据总是从父组件传到子组件,子组件没有权利修改父组件传过来的数据,

  1. 只能请求父组件对原始数据进行修改。这样会防止从子组件意外改变父组件的状态,

  1. 从而导致你的应用的数据流向难以理解。

  1. 注意:在子组件直接用 v-model 绑定父组件传过来的 props 这样是不规范的写法,

  1. 开发环境会报警告。

  1. 如果实在要改变父组件的 props 值可以再data里面定义一个变量,

  1. 并用 prop 的值初始化它,之后用$emit 通知父组件去修改。

虚拟DOM是什么?有什么优缺点?


  1. 由于在浏览器中操作DOM是很昂贵的。频繁操作DOM,

  1. 会产生一定性能问题。这就是虚拟Dom的产生原因。

  1. Vue2的Virtual DOM 借鉴了开源库 snabbdom 的实现。

  1. Virtual DOM本质就是用一个原生的JS对象去描述一个DOM节点,是对真实DOM的一层抽象。

优点:

1、保证性能下限:框架的虚拟DOM需要适配任何上层API可能产生的操作,

他的一些DOM操作的实现必须是普适的,所以它的性能并不是最优的;

但是比起粗暴的DOM操作性能要好很多,

因此框架的虚拟DOM至少可以保证在你不需要手动优化的情况下,

依然可以提供还不错的性能,既保证性能的下限。

2、无需手动操作DOM:我们不需手动去操作DOM,

只需要写好 View-Model的 代码逻辑,框架会根据虚拟DOM和数据双向绑定,

帮我们以可预期的方式更新视图,极大提高我们的开发效率。

3、跨平台:虚拟DOM本质上是JavaScript对象,而DOM与平台强相关,

相比之下虚拟DOM可以进行更方便地跨平台操作,例如服务器端渲染、weex开发等等。

缺点:

  1. 1、无法进行极致优化:虽然虚拟DOM + 合理的优化,

  1. 足以应对大部分应用的性能需要,

  1. 但在一些性能要求极高的应用中虚拟DOM无法进行针对性的极致优化。

  1. 2、首次渲染大量DOM时,由于多了一层DOM计算,会比innerHTML插入慢。

Vuex 页面刷新数据丢失怎么解决?


  1. 需要做 vuex 数据持久化,一般使用本地储存的方案来保存数据,

  1. 可以自己设计存储方案,也可以使用第三方插件。

  1. 推荐使用 vuex-persist (脯肉赛斯特)插件,

  1. 它是为 Vuex 持久化储存而生的一个插件。不需要你手动存取 storage,

  1. 而是直接将状态保存至 cookie 或者 localStorage中。

Vuex 为什么要分模块并且加命名空间?


  1. 模块: 由于使用单一状态树,应用的所有状态会集中到一个比较大的对象。

当应用变得非常复杂时,store 对象就有可能会变得相当臃肿。

为了解决以上问题,Vuex 允许我们将 store 分割成模块(module)。

每个模块拥有自己的 state、mutation、action、getter、甚至是嵌套子模块。

命名空间: 默认情况下,模块内部的 action、mutation、getter

是注册在全局命名空间的 — 这样使得多个模块能够对同一 mutation 或 action 做出响应。

如果希望你的模块具有更高的封装度和复用性,

你可以通过添加 namespaced:true 的方式使其成为带命名的模块。

当模块被注册后,他所有 getter、action、及 mutation 都会自动根据模块注册的路径调整命名。

vue 中使用了哪些设计模式?


工厂模式 - 传入参数即可创建实例

虚拟 DOM 根据参数的不同返回基础标签的 Vnode 和组件 Vnode。

2、单例模式 - 整个程序有且仅有一个实例

vuex 和 vue-router 的插件注册方法 install 判断如果系统存在实例就直接返回掉。

3、发布-订阅模式。(vue 事件机制)

4、观察者模式。(响应式数据原理)

5、装饰器模式(@装饰器的用法)

6、策略模式,策略模式指对象有某个行为,但是在不同的场景中,

该行为有不同的实现方案 - 比如选项的合并策略。

你都做过哪些 Vue 的性能优化?


这里只列举针对 Vue 的性能优化,整个项目的性能优化是一个大工程。

  1. 对象层级不要过深,否则性能就会差。

  1. 不需要响应式的数据不要放在 data 中(可以使用 Object.freeze() 冻结数据)

  1. v-if 和 v-show 区分使用场景

  1. computed 和 watch 区分场景使用

  1. v-for 遍历必须加 key,key最好是id值,且避免同时使用 v-if

  1. 大数据列表和表格性能优化 - 虚拟列表 / 虚拟表格

  1. 防止内部泄露,组件销毁后把全局变量和时间销毁

  1. 图片懒加载

  1. 路由懒加载

  1. 异步路由

  1. 第三方插件的按需加载

  1. 适当采用 keep-alive 缓存组件

  1. 防抖、节流的运用

  1. 服务端渲染 SSR or 预渲染

Vue.set 方法原理


在两种情况下修改 Vue 是不会触发视图更新的。

1、在实例创建之后添加新的属性到实例上(给响应式对象新增属性)

2、直接更改数组下标来修改数组的值。

Vue.set 或者说是 $set 原理如下

因为响应式数据 我们给对象和数组本身新增了__ob__属性,代表的是 Observer 实例。

当给对象新增不存在的属性,首先会把新的属性进行响应式跟踪

然后会触发对象 ob 的dep收集到的 watcher 去更新,

当修改数组索引时我们调用数组本身的 splice 方法去更新数组

函数式组件使用场景和原理


函数式组件与普通组件的区别

1、函数式组件需要在声明组件时指定 functional:true

2、不需要实例化,所以没有this,this通过render函数的第二个参数context代替

3、没有生命周期钩子函数,不能使用计算属性,watch

4、不能通过$emit对外暴露事件,

调用事件只能通过context.listeners.click的方式调用外部传入的事件

5、因为函数组件时没有实例化的,所以在外部通过ref去引用组件时,

实际引用的是HTMLElement

6、函数式组件的props可以不用显示声明,

所以没有在props里面声明的属性都会被自动隐式解析为prop,

而普通的组件所有未声明的属性都解析到$attrs里面,

并自动挂载到组件根元素上(可以通过inheritAttrs属性禁止)

  1. 优点:1.由于函数组件不需要实例化,无状态,没有生命周期,

  1. 所以渲染性要好于普通组件2.函数组件结构比较简单,代码结构更清晰

使用场景:

一个简单的展示组件,作为容器组件使用 比如 router-view 就是一个函数式组件。

“高阶组件”—用于接受一个组件为参数,返回一个被包装过的组件。

相关代码如下:

// 带有functional的属性的就是函数式组件

if (isTrue(Ctor.options.functional)) {

return createFunctionalComponent(

Ctor,

propsData,

data,

context,

children);

}

const listeners = data.on;

data.on = data.nativeOn;

// 安装组件相关钩子 (函数式组件没有调用此方法,从而性能高于普通组件)

installComponentHooks(data);

子组件为何不可以修改父组件传递的 Prop?


  1. 所有的 prop 都使得其父子 prop 之间形成了一个单向下行绑定:

  1. 父级 prop 的更新会向下流动到子组件中,但是反过来则不行。

  1. 这样会防止从子组件意外改变父级组件的状态,从而导致你的应用的数据流向难以理解。

HTML和HTML5有什么区别?


  1. 主要有三个区别:

1、文档声明区别

HTML:超文本标记语言,一种纯文本类型的语言。

HTML5.0:文档声明HTML5方便书写,精简,有利于程序员快速的阅读和开发。

2、结构语义区别

html:没有体现结构语义化的标签,如:<div id="nav"></div>

html5:添加了许多具有语义化的标签,如:<article>、<aside>、<audio>、<bdi>...

3、绘图区别

HTML:指可伸缩矢量图形,用于定义网络的基于矢量的图形。

HTML5:HTML5的canvas元素使用脚本(通常使用JavaScript)在网页上绘制图像,

可以控制画布每一个像素。

什么是盒子模型?


  1. 一个盒子我们会分成几个部分:内容区(content)、内边距(padding)、

  1. 边框(border)、外边距(margin),

  1. 也就是盒模型的组成由margin,padding,boder,content组成

  1. 盒子模型分为标准盒子模型和IE盒子模型

如何理解HTML5语义化?


  1. HTML语义化标签

  1. header – 标题

  1. nav – 导航

  1. article – 文章

  1. section – 节或段

  1. aside – 侧边栏

  1. footer – 页脚

语义化的好处?


  1. 在没有css代码的情况下,也能很好的呈现内容结构、代码结构(让非技术员也能看懂代码)

  1. 提高用户体验,比如:title,alt用于解释名词和图片信息

  1. 利于SEO。语义化能和搜索引擎建立更好的联系,优化搜索

  1. 便于团队开发与维护,语义化更具有可读性

常见的浏览器内核有哪些?


**Trident内核:**代表浏览器是ie浏览器,因此Trident内核又称E内核,

此内核只能用于Windows平台,并且不是开源的。

**Gecko内核:**代表浏览器是Firefox浏览器。Gecko内核是开源的,

最大优势是可以跨平台。

**Webkit内核:代表浏览器是Safari(苹果的浏览器)以及低版本的谷歌浏览器,

是开源的项目。

**Presto内核:**代表浏览器是Opera浏览器(中文译为“欧朋浏览器”),

Presto内核是世界公认最快的渲染速度的引擎,但是在2013年之后,

Open宣布加入谷歌阵营,弃用了该内核。

**Blink内核:**由谷歌和Opera开发,2013年4月发布,现在Chrome内核是Blink。

谈谈你对web标准以及W3C的理解?


  1. eb标准:

  1. web标准主要分为结构、表现、行为3部分

  1. 结构:指我们平时在body里面写的标签,主要是由HTML标签组成

  1. 表现:指更加丰富HTML标签样式,主要由CSS样式组成

  1. 行为:指页面和用户的交互,主要由JS部分组成

  1. W3C对web标准提出了规范化的要求,即代码规范

W3C

  1. 对结构的要求

1、标签字母要小写 2、标签要闭合 3、标签不允许随意嵌套

对表现和行为的要求


建议使用外链CSS和js脚本,实现结构与表现分离、结构与行为分离,能提高页面的渲染效率,更快地显示网页内容

如何实现浏览器响应式布局?


  1. 常用如下

  1. 使用媒体查询(@media)

  1. 使用flex弹性布局

  1. 使用百分比单位

  1. 使用rem单位

  1. 使用VH、HW单位

  1. 推荐一种冷门万能自适应方式:tranfrom:scale(动态比值)

CSS选择器以及优先级的理解?


常用的CSS选择器

ID选择器、类选择器、标签选择器、属性选择器、伪类选择器、后代选择器

权重划分

在同一层级下:

!important > 内联样式 > ID选择器 > 类选择器 > (标签选择器、伪类选择器、属性选择器)

不同层级下:

正常来说权重值越高的优先级越高,但是一直以来没有具体的权重值划分,

所以目前大多数开发中层级越深的优先级越高

谈谈你对回流和重绘的理解?


  1. 什么是回流?

当一个元素自身的宽高,布局,显示或隐藏,或元素内部的文字结构发生变化

,导致需要重新构建页面的时候,就产生了回流

  1. 什么是重绘?

当一个元素自身的宽高,布局,及显示或隐藏没有改变,而只是改变了元素的外观风格的时候,就产生了重绘

  1. 什么时候会进行回流?

(1)添加或者删除可见的 DOM 元素的时候

(2)元素的位置发生改变

(3)元素的尺寸发生改变

(4)内容改变

(5)页面第一次渲染的时候

  1. 什么时候会进行重绘?

列举一些相关的 CSS 样式:color、background、background-size、visibility、box-shadow

css 预处理器


  1. less

  1. sass

<img>的 title 和 alt 有什么区别


  1. 通常当鼠标滑动到元素上的时候显示

  1. alt 是<img> 的特有属性,是图片内容的等价描述,

  1. 用于图片无法加载显示、读屏器阅读图片。可提图片高可访问性,

  1. 除了纯装饰图片外都必须设置有意义的值,搜索引擎会重点分析。

行内元素和块级元素有哪些?img属于什么元素


  1. 块元素

address – 地址

blockquote – 块引用

center – 举中对齐块

dir – 目录列表

div – 常用块级容易,也是CSS layout的主要标签

dl – 定义列表

fieldset – form控制组

form – 交互表单

h1 – 大标题

h2 – 副标题

h3 – 3级标题

h4 – 4级标题

h5 – 5级标题

h6 – 6级标题

hr – 水平分隔线

isindex – input prompt

menu – 菜单列表

noframes – frames可选内容,(对于不支持frame的浏览器显示此区块内容

noscript – 可选脚本内容(对于不支持script的浏览器显示此内容)

ol – 有序表单

p – 段落

pre – 格式化文本

table – 表格

ul – 无序列表

内联元素

a – 锚点

abbr – 缩写

acronym – 首字

b – 粗体(不推荐)

bdo – bidi override

big – 大字体

br – 换行

cite – 引用

code – 计算机代码(在引用源码的时候需要)

dfn – 定义字段

em – 强调

font – 字体设定(不推荐)

i – 斜体

img – 图片

input – 输入框

kbd – 定义键盘文本

label – 表格标签

q – 短引用

s – 中划线(不推荐)

samp – 定义范例计算机代码

select – 项目选择

small – 小字体文本

span – 常用内联容器,定义文本内区块

strike – 中划线

strong – 粗体强调

sub – 下标

sup – 上标

textarea – 多行文本输入框

tt – 电传文本

u – 下划线

img属于行内替换元素,效果与块元素一致

表单中readonly和disabled的区别?


  1. 共同点:能够使用户不能改变表单中的内容

  1. 不同点:

1、readonly只对input和textarea有效,但是disabled对所有的表单元素都是有效的,

包括radio、checkbox

2、readonly可以获取到焦点,只是不能修改。

disabled设置的文本框无法获取焦点

3、如果表单的字段是disabled,则该字段不会发送(表单传值)和序列化

浏览器


  1. 浏览器中输入url到网页显示,整个过程发生了什么

  1. 域名解析

  1. 发起tcp三次握手

  1. 建立tcp连接之后发起htttp请求

  1. 服务器端响应http请求,浏览器得到html代码

  1. 浏览器器解析html代码,并请求html代码中的资源

  1. 浏览器对页面进行渲染呈现给用户

cookie的弊端


  1. 每个特定的域名下最多生成的cookie的个数有限制

  1. IE和Opera会清理近期最少使用的cookie,firefox会随机清理cookie

  1. cookie最大为4096字节,为了兼容一般不超过4095字节

  1. 安全性问题,如果cookie被人劫持,就可以获得所有的session信息

主流浏览器及内核


Google chrome:webkit/blink
safari:webkit
IE:trident
firefox:gecko
Opera:presto/webkit/blink

谈谈对bfc规范的理解


bfc是block formatting context即格式化上下文
bfc是页面css视觉渲染的一部分,用于决定块盒子的布局及浮动相互影响范围的一个区域
bfc最重要的一个效果是,让处于bfc内部与外部的元素相互隔离,使内外的元素的定位不会相互影响

请说出至少三种减少页面加载时间的方法


尽量减少页面中重复的http请求
css样式放置在文件头部、js脚本放置在文件末尾
压缩合并js、css代码
服务器开启gzip压缩

如何进行网站性能优化?


  1. content 方面

减少 HTTP 请求:合并文件、CSS 精灵、inline Image

减少 DNS 查询:DNS 缓存、将资源分布到恰当数量的主机名

减少 DOM 元素数量

Server 方面

使用 CDN

配置 Etag

对组件使用 Gzip 压缩

Cookie 方面

减少cookie大小

css 方面

将样式表放到页面顶部

不使用 CSS 表达式

使用 <link> 不使用 @import

Javascript 方面

将脚本放到页面底部

将 javascript 和 css 从外部引入

压缩 javascript 和 css

删除不需要的脚本

减少 DOM 访问

图片方面

优化图片:根据实际颜色需要选择色深、压缩

优化 css 精灵

不要在 HTML 中拉伸图片

浏览器存储?


短暂性的时候,我们只需要将数据存在内存中,只在运行时可用

持久性存储,可以分为 浏览器端 与 服务器端

浏览器:

cookie : 通常用于存储用户身份,登录状态等,http 中自动携带, 体积上限为 4K , 可自行设置过期时间

localStorage / sessionStorage : 长久储存/窗口关闭删除, 体积限制为 4~5M

indexDB

服务器

分布式缓存 redis

数据库

get / post?


get : 缓存、请求长度受限、会被历史保存记录

无副作用(不修改资源),幂等(请求次数与资源无关)的场景

post : 安全、大数据、更多编码类型

安全性问题?


XSS 攻击: 注入恶意代码

cookie 设置 httpOnly

转义页面上的输入内容和输出内容

CSRF : 跨站请求伪造,防护:

get 不修改数据

不被第三方网站访问到用户的 cookie

设置白名单,不被第三方网站请求

请求校验

性能优化的几个方面?


  1. 资源压缩合并,减少HTTP请求

  1. 非核心代码异步加载

  1. 利用浏览器缓存

  1. 使用CDN

  1. 预解析DNS

异步加载?


  1. 动态脚本加载

  1. defer

  1. async

加载方式区别?


  1. defer是在html解析完毕才执行,如果有多个则按加载顺序执行

  1. async是加载完毕后立即执行,如果是多个,执行顺序与加载顺序无关

预加载?


  1. 在开发中,可能会遇到这样的情况。有些资源不需要马上用到,

但是希望尽早获取,这时候就可以使用预加载。

预加载其实是声明式的 fetch ,强制浏览器请求资源,

并且不会阻塞 onload 事件,可以使用以下代码开启预加载

<link rel="preload" href="" target="_blank">http://example.com">

预加载可以一定程度上降低首屏的加载时间,

因为可以将一些不影响首屏但重要的文件延后加载,唯一缺点就是兼容性不好。

DNS 预解析?


DNS 解析也是需要时间的,可以通过预解析的方式来预先获得域名所对应的 IP。

<meta http-equiv='x-dns-prefetch-control' content='on'>

<link rel="dns-prefetch" href="//yuchengkai.cn">

在https协议中默认a标签不会开启预解析,因此需要手动设置meta

懒执行?


  1. 懒执行就是将某些逻辑延迟到使用时再计算。

  1. 该技术可以用于首屏优化,对于某些耗时逻辑并不需要在首屏就使用的,

  1. 就可以使用懒执行。懒执行需要唤醒,一般可以通过定时器或者事件的调用来唤醒。

懒加载?


  1. 懒加载就是将不关键的资源延后加载。

懒加载的原理就是只加载自定义区域(通常是可视区域,

但也可以是即将进入可视区域)内需要加载的东西。对于图片来说,

先设置图片标签的 src 属性为一张占位图,

将真实的图片资源放入一个自定义属性中,当进入自定义区域时,

就将自定义属性替换为 src 属性,这样图片就会去下载资源,实现了图片懒加载。

懒加载不仅可以用于图片,也可以使用在别的资源上。比如进入可视区域才开始播放视频等等。

什么时候使用状态管理器?


  1. 从项目的整体看

目用户的使用方式复杂

不同身份的用户有不同的使用方式(比如普通用户和管理员)

多个用户之间可以协作

与服务器大量交互,或者使用了 WebSocket

View 要从多个来源获取数据

从组件角度看

某个组件的状态,需要共享

某个状态需要在任何地方都可以拿到

一个组件需要改变全局状态

一个组件需要改变另一个组件的状态

组件有相当大量的,随时间变化的数据

state 需要有一个单一可靠数据源

所有 state 放在顶层组件已经无法满足需求了

说说 React 有什么特点?


  1. 它使用**虚拟 DOM **而不是真正的 DOM。

  1. 它可以用服务器端渲染。

  1. 它遵循单向数据流或数据绑定

列出 React 的一些主要优点?


  1. 它提高了应用的性能

  1. 可以方便地在客户端和服务器端使用

  1. 由于 JSX,代码的可读性很好

  1. React 很容易与 Meteor,Angular 等其他框架集成

  1. 使用 React,编写 UI 测试用例变得非常容易

什么是 JSX?


  1. 它 JSX 是 J avaScript XML 的简写。是 React 使用的一种文件,

它利用 JavaScript 的表现力和类似 HTML 的模板语法。

这使得 HTML 文件非常容易理解。此文件能使应用非常可靠,并能够提高其性能。

例子

render() {

return(

<div>

<h1> Hello World </h1>

</div>

)

}

说说为什么浏览器无法读取 JSX?


  1. 浏览器只能处理 JavaScript 对象,而不能读取常规 JavaScript 对象中的 JSX。

  1. 所以为了使浏览器能够读取 JSX,首先,

  1. 需要用像 Babel 这样的 JSX 转换器将 JSX 文件转换为

  1. JavaScript 对象,然后再将其传给浏览器

你理解“在 React 中,一切都是组件”这句话?


  1. 组件是 React 应用 UI 的构建块。这些组件将整个 UI 分成小的独立并可重用的部分。

  1. 每个组件彼此独立,而不会影响 UI 的其余部分

React 中 render()的目的?


  1. 每个 React 组件强制要求必须有一个 render()。它返回一个 React 元素,

  1. 是原生 DOM 组件的表示。如果需要渲染多个 HTML 元素,

  1. 则必须将它们组合在一个封闭标记内,例如<form>、<group>、<div> 等。

  1. 此函数必须保持纯净,即必须每次调用时都返回相同的结

什么是 Props?


  1. Props 是 React 中属性的简写。它们是只读组件,必须保持纯,即不可变。

  1. 它们总是在整个应用中从父组件传递到子组件。子组件永远不能将 prop 送回父组件。

  1. 这有助于维护单向数据流,通常用于呈现动态生成的数据

React 中的状态是什么?


  1. 状态是 React 组件的核心,是数据的来源,必须尽可能简单。

  1. 基本上状态是确定组件呈现和行为的对象。与 Props 不同,

  1. 它们是可变的,并创建动态和交互式组件。可以通过this.state() 访问它们。

区分状态和 Props?


条件 State Props

从父组件中接受初始值 Yes Yes

父组件可以改变值 No Yes

在组件中设置默认值 No Yes

在组件的内部变化 Yes No

设置子组件的初始值 Yes Yes

在子组件的内部改变 No Yes

如何更新组件的状态?


使用this.setState()更新组件的状态

React 组件生命周期的阶段是什么?


React 组件的生命周期有三个不同的阶段:

初始渲染阶段:这是组件即将开始其生命之旅并进入 DOM 的阶段。

更新阶段:一旦组件被添加到 DOM,它只有在 prop 或状态发生变化时才可能更新和重新渲染。

这些只发生在这个阶段

卸载阶段:这是组件生命周期的最后阶段,组件被销毁并从 DOM 中删除

你对 React 的 refs 有什么了解?


Refs 是 React 中引用的简写。

它是一个有助于存储对特定的 React 元素或组件的引用的属性,

它将由组件渲染配置函数返回。用于对 render()返回的特定元素或组件的引用。

当需要进行 DOM 测量或向组件添加方法时,它们会派上用场

列出一些应该使用 refs 的情况?

需要管理焦点、选择文本或媒体播放时

触发式动画

与第三方 DOM 库集成

如何模块化 React 中的代码?


可以使用 export 和 import 属性来模块化代码。它们有助于在不同的文件中单独编写组件

什么是高阶组件 HOC?


  1. 高阶组件是重用组件逻辑的高级方法,是一种源于 React 的组件模式。

  1. HOC 是自定义组件,在它之内包含另一个组件。它们可以接受子组件提供的任何动态,

  1. 但不会修改或复制其输入组件中的任何行为。你可以认为 HOC 是“纯(Pure)”组件

你能用 HOC 做什么?


  1. HOC 可用于许多任务:

  1. 用代码重用,逻辑和引导抽象

  1. 渲染劫持

  1. 状态抽象和控制

  1. Props 控制

React 中 key 的重要性是什么?


  1. key 用于识别唯一的 Virtual DOM 元素及其驱动 UI 的相应数据。

  1. 它们通过回收 DOM 中当前所有的元素来帮助 React 优化渲染。

  1. 这些 key 必须是唯一的数字或字符串,React 只是重新排序元素而不是重新渲染它们。

  1. 这可以提高应用程序的性能

MVC 框架的主要问题是什么?


  1. key 用对 DOM 操作的代价非常高

  1. 程序运行缓慢且效率低下

  1. 内存浪费严重

  1. 由于循环依赖性,组件模型需要围绕 models 和 views 进行创建

请你解释一下 Flux?


  1. Flux 是一种强制单向数据流的架构模式。它控制派生数据,

  1. 并使用具有所有数据权限的中心store 实现多个组件之间的通信。

  1. 整个应用中的数据更新必须只能在此处进行。

  1. Flux 为应用提供稳定性并减少运行时的错误。

你对“单一事实来源”有什么理解


  1. Redux 使用 “store” 将程序的整个状态存储在同一个地方。

  1. 因此所有组件的状态都存储在store 中,

  1. 并且它们从 store 本身接收更新。

  1. 单一状态树可以更容易地跟踪随时间的变化,

  1. 并调试或检查程序。

列出 Redux 的组件?


  1. Redux 由以下组件组成:

  1. Action 这是一个用来描述发生了什么事情的对象

  1. Reducer 这是一个确定状态将如何变化的地方

  1. Store 整个程序的状态/对象树保存在 Store 中

  1. View 查只显示 Store 提供的数据

Store 在 Redux 中的意义是什么?


store 是一个 JavaScript 对象,它可以保存程序的状态,

并提供一些方法来访问状态、调度操作和注册侦听器。

应用程序的整个状态/对象树保存在单一存储中。

因此,Redux 非常简单且是可预测的。

我们可以将中间件传递到 store 来处理数据,并记录改变存储状态的各种操作。

所有操作都通过 Reducer 返回一个新状态

Redux 有哪些优点?


  1. 结果的可预测性

  1. 可维护性

  1. 服务器端渲染

  1. 易于测试 -

什么是 React 路由?


  1. React 路由是一个构建在 React 之上的强大的路由库,

  1. 它有助于向应用程序添加新的屏幕和流。这使 URL 与网页上显示的数据保持同步。

  1. 它负责维护标准化的结构和行为,并用于开发单页 Web 应用。 React 路由有一个简单的 API。

说说你对 React 的渲染原理的理解?


  1. 单向数据流。React 是一个 MVVM 框架,

简单来说是在 MVC 的模式下在前端部分拆分出数据层和视图层。

单向数据流指的是只能由数据层的变化去影响视图层的变化,而不能反过来(除非双向绑定)

数据驱动视图。我们无需关注页面的 DOM,只需要关注数据即可

渲染过程,生命周期……

setState()大部分时候是异步执行的,提升性能。

React 中三种构建组件的方式?


  1. React.createClass()、ES6 class 和无状态函数。

说出jQuery中常见的几种函数以及他们的含义是什么?


(1)get()取得所有匹配的DOM元素集合;

(2)get(index)取得其中一个匹配的元素.index表示取得第几个匹配的元素;

(3)append(content)向每个匹配的元素内部追加内容;

(4)after(content)在每个匹配的元素之后插入内容;

(5)html()/html(var)取得或设置匹配元素的html内容;

(6)find(expr)搜索所有与指定表达式匹配的元素;

(7)bind(type,[data],fn)为每个匹配元素的特定事件绑定事件处理函数;

(8)empty()删除匹配的元素集合中所有的子节点;

(9)hover(over,out)一个模仿悬停事件(鼠标移动到一个对象上面及移出这个对象)的方法;

(10)attr(name)取得第一个匹配元素的属性值。

jQuery 能做什么?


获取页面的元素;修改页面的外观;改变页面大的内容;响应用户的页面操作;为页面添加动

态效果;无需刷新页面,即可以从服务器获取信息;简化常见的javascript任务。

如何将一个HTML元素添加到DOM树中的?

可以通过appendTo()方法在指定的DOM元素末尾添加一个现存的元素或者一个新的HTML元素。

什么是jQuery? jQuery能做什么?


  1. jQuery是一套JavaScript的库,它简化了使用Javascript进行网页特效开发的一些复杂性,

  1. 提供了对常见任务的自动化和复杂任务的简化

jQuery的作用


  1. 快速获取文档元素

  1. 提供漂亮的页面动态效果

  1. 创建Ajax无刷新网页

  1. 提供对JavaScript语言的增强

  1. 增强的事件处理

  1. 更改网页内容

  1. JQuery可以修改网页中的内容,比如更改网页的文本、

  1. 插入或转网页图像, jQuery简化了原本使用JavaScript代码需要处理的方式。

哪些标签都存在为元素


为元素可以使用js操作吗


Html5 的网页为什么只需要写<!DOCTYOE HTML>


px、em、rem这三个长度单位的区别


CSS3新增伪类有哪些


position几个属性值作用


怎么让一个不定高的div,垂直水平居中


方案一:transform

方案二:flex弹性布局

方案三:绝对定位

清除浮动有哪些常用的方式


让两个块级元素在一行显示有哪些做法


如何设置一个元素在垂直方向居中


css3新增了哪些新特性


怎么判断两个对象相等


js函数有哪些定义方式


js中函数有哪些调用形式


js中的常用内置对象有哪些?并列举该对象的常用方法


列举和数组操作相关的方法


列举和字符串操作相关的方法


document.write 和 innerHTML 的区别


分别阐述split()、slice()、splice()、join()


例举3中强制类型转换和2中隐式类型转换


什么是构造函数


js中实现继承的方式


什么是内存泄漏,哪些操作会引起内存泄漏


什么是预解析


caller和callee的区别是什么


new操作符具体干了什么呢


列举DOM元素增删改查的API


BOM中有哪些常用对象


列举几个常见的浏览器兼容问题


什么是事件委托


事件委托的原理是什么


javascript中有几种定时器,有什么区别


如何实现多个标签页的通信


H5都新增了哪些新特性


常见HTTP状态码有哪些


你知道的HTTP请求方式有几种


MKCOL


(允许用户创建资源)

Extension-mothed


(在不改动协议的前提下,可增加另外的方法)

ajax的工作原理


一个页面从输入URL到页面加载显示完成,这个过程都发生了什么


网站从http协议切换到https协议需要对代码做哪些处理


什么是CDN加速


什么是SEO


为什么利用多个域名来存储网站静态资源会更有效


移动端点击事件会有多少秒的延迟?什么原因造成的?如何解决?


你了解到的网站攻击方式有哪些


谈谈js中的垃圾回收机制


js是单线程还是多线程


js是如何实现异步操作的


分别介绍一下mvc、mvvm、mvp这三种设计模式


es6新增了哪些数据类型


es6新增了哪些特性


使用let声明和var的变量有什么区别


谈谈async/await的使用方式和场景


箭头函数有什么作用及实际应用场景


class如何实现继承


谈谈Promise的理解


vue如何封装一个组件


谈谈vue中插槽的理解


vue路由使用步骤


vue组件库有哪些


谈谈对于mvvm的理解


vue创建组件的时候,data为什么要使用匿名函数 return一个对象


vue路由守卫是什么以及应用场景


vue如何自定义一个过滤器


vue如何自定义一个 vue指令


怎么定义vue-router的动态路由?怎么获取通过路由传过来的参数


vue中v-for指令循环遍历中key属性的作用


vue和react有哪些不同的地方


vue如何实现通过锚点值的改变切换组件


vue如何实现给样式添加作用域?说明其实现原理


vue如何动态添加一个路由规则


vue如何优化页面加载效率


什么是路由懒加载?路由懒加载有什么好处?如何实现


vue中如何触发一个自定义事件


vue中如何监听自定义事件


vue中如何移除自定义事件


vm.$mount(selector)方法的作用是什么


vue中有哪些内置的组件?


vue实例中有哪些属性?


Vue.use(plugin)的作用是什么,使用时需要注意什么?


vm.$nextTick(fn)的作用是什么?


vue中的混入(mixin)有什么作用?


如何开发一个vue插件?


vue中如何动态设置页面的title标题


vue中,当切换到新路由时,想要页面滚到顶部,或者页面后退时,想保持原先滚动位置,如何实现?


什么是ssr?如何实现ssr?


什么是SPA?


使用react、vue、angular开发的SPA单页面应用有什么优缺点?


react中什么是虚拟DOM、使用虚拟DOM有什么优势?


简单介绍一下react中的diff算法?


什么是Redux?


react有哪些常用的组件库?


react如何操作DOM?


什么是高阶组件(HOC)?


react中调用setState后发生了什么?


react中状态state和属性props有什么不同?


react中有几种创建组件的方式?


react中的组件按照职责不同、可以分为几种类型?


类组件函数式组件之间有什么不同?


说说react的生命周期?


react性能优化可以使用哪个生命周期函数?


应该在react组件的何处发起ajax请求?


react如何实现服务端渲染?


自定义的react组件首字母为什么必须大写?


setState什么时候是同步,什么时候是异步?


react事件机制跟原生事件有什么区别?


react事件中为什么要绑定this或者要使用箭头函数?


react事件机制?


谈谈jsx的原理?


jsx在书写的时候和html有什么不同?


jsx中如果非要创建多个平行标签,该如何处理?


angular?


前端构建工具?


webpack和gulp在进行代码合并的时候还有什么不同?


列举几个gulp中常用的插件?


webpack有哪些常见的配置?


微信小程序目前有几种主流的开发模式?


微信小程序开发过程?


微信小程序tabbar导航配置?


微信小程序页面跳转?


微信小程序生命周期?


微信小程序请求数据接口?


微信小程序如何优化代码包的体积?


微信小程序组件库?


微信小程序如何实现页面后退时重新刷新页面?


typescript中数据类型?


typescript和javascript?


git中有哪些常用的命令?


git中和svn有什么区别?


git有哪些常用的图形界面客户端工具?


git在线代码托管平台?


nodejs中有哪些常用内置模块?


常用第三方模块?


express框架如何快速创建一个服务器?


express中路由模块使用步骤?


使用http模块快速开启一个web服务器?


目前流行的app开发方式?


什么是lonic和cordova?


react-native是什么?


什么是flutter?


什么是weex?


你所了解到的产品原型和设计图的共享平台有哪些?


商城的购物车是怎么实现的?


前后端分离的项目如何实现登录状态的保持?


前后端分离开发的后台管理系统一般如何实现权限管理?


如何通过最原始的方式实现一个多语言版的网站?


如何实现从列表页跳入详情页,并从详情页面返回列表页时,滚动条还在当时位置,并保持数据不刷新?


js异步的方法(promise,generator,async)


解决的问题:回调地狱 Promise规范: promise有三种状态,等待(pending)、已完成(fulfilled/resolved)、已拒绝(rejected)

Promise的状态只能从“等待”转到“完成”或者“拒绝”,不能逆向转换,同时“完成”和“拒绝”也不能相互转换. 当返回成功状态时,执行then方法

当返回失败状态时,执行catch方法

promise 可进行链式操作

promise.all()可以将多个Promise实例包装成一个新的Promise实例。

        同时,成功和失败的返回值是不同的,成功的时候返回的是一个结果数组,而失败的时候则返回最先被reject失败状态的值。

promise.race()

promise.then(resolve, reject),resolve 和 reject都是可选参数。如果 resolve 或reject 不是函数,其必须被忽略. then 方法必须返回一个 promise 对象.

  1. 使用:

实例化promise对象需要传入函数(包含两个参数),resolve和reject,内部确定状态.resolve和reject函数可以传入参数在回调函数中使用. resolve和reject都是函数,传入的参数在then的回调函数中接收.

var promise = new Promise(function (resolve, re) { }); promise.then(function (val) { console.log(val); }) function pro(val) { var promise = new Promise(function (resolve, reject) { if (val) { resolve("我是成功状态"); } else { resolve("我是失败状态"); } }) return promise; } //返回promise对象 var op = pro(0); //then接收两个函数,分别对应resolve和reject状态的回调,函数中接收实例化时传入的参数. op.then((val) => { console.log(val); }, (val) => { console.log(val); })

  • Promise回调链:

promise能够在回调函数里面使用 return 和 throw,

所以在then中可以return出一个promise对象或其他值,

也可以throw出一个错误对象,但如果没有return,将默认返回 undefined,那么后面的then中的回调参数接收到的将是undefined.

function p1(val){ return new Promise((resolve,reject)=>{ val==1?resolve(1):reject() }) }; function p2(val){ return new Promise((resolve,reject)=>{ val==2?resolve(2):reject(); }) }; let promimse = new Promise(function(resolve,reject){ resolve(1) }) .then(function(data1) { return p1(data1)//如果去掉return,则返回undefined而不是p1的返回值,会导致报错 }) .then(function(data2){ return p2(data2+1) }) .then(res=>console.log(res))

promise.all()

可以将多个Promise实例包装成一个新的Promise实例。

同时,成功和失败的返回值是不同的,成功的时候返回的是一个结果数组,而失败的时候则返回最先被reject失败状态的值。

Promise.all([]).then().catch()获得的成功结果的数组里面的数据顺序和Promise.all接收到的数组顺序是一致的

只有所有的promise函数都返回成功状态,才会执行then方法

若有一个promise函数返回失败状态,则执行catch方法

使用场景:

  比如说一个页面上需要等两个或多个ajax的数据回来以后才正常显示,在此之前只显示loading图标。

  在前端开发请求数据的过程中,偶尔会遇到发送多个请求并根据请求顺序获取和使用数据的场景,使用Promise.all毫无疑问可以解决这个问题。

let p1 = new Promise((resolve, reject) => { resolve('成功了') }) let p2 = new Promise((resolve, reject) => { resolve('success') }) let p3 = Promse.reject('失败') Promise.all([p1, p2]).then((result) => { console.log(result) //['成功了', 'success'] }).catch((error) => { console.log(error) }) Promise.all([p1,p3,p2]).then((result) => { console.log(result) }).catch((error) => { console.log(error) // 失败了,打出 '失败' })

Promise.race的使用

Promse.race就是赛跑的意思,

意思就是说,Promise.race([p1, p2, p3])里面哪个结果获得的快,就返回那个结果,不管结果本身是成功状态还是失败状态。

let p1 = new Promise((resolve, reject) => { setTimeout(() => { resolve('success') },1000) }) let p2 = new Promise((resolve, reject) => { setTimeout(() => { reject('failed') }, 500) }) Promise.race([p1, p2]).then((result) => { console.log(result) }).catch((error) => { console.log(error) // 打开的是 'failed' })

Generator-------生成器函数,解决异步:

generator函数使用: 1、分段执行,可以暂停 2、可以控制阶段和每个阶段的返回值 3、可以知道是否执行到结尾

generator和异步控制:

利用关键字yield,可以把异步操作写在yield语句里面,

每调用一次next方法,则执行一次yield语句

如果有return,return完成之后就退出了生成器函数,后面如果还有yield操作,则不再执行

所以,Generator函数的一个重要实际意义就是用来处理异步操作,改写回调函数。

function* g() { var o = 1; yield o++; yield o++; } var gen = g(); console.log(gen.next()); // Object {value: 1, done: false} var xxx = g(); console.log(gen.next()); // Object {value: 2, done: false} console.log(xxx.next()); // Object {value: 1, done: false} console.log(gen.next()); // Object {value: undefined, done: true}

async和异步:-------------通过await解决异步

用法: async 表示这是一个async函数,await只能用在这个函数里面。 await 表示在这里等待异步操作返回结果,再继续执行。 await 后一般是一个promise对象 示例:async用于定义一个异步函数,该函数返回一个Promise。 如果async函数返回的是一个同步的值,这个值将被包装成一个理解resolve的Promise,等同于return Promise.resolve(value)。 await用于一个异步操作之前,表示要“等待”这个异步操作的返回值。await也可以用于一个同步的值。

let timer = async function timer(){ return new Promise((resolve,reject) => { setTimeout(() => { resolve('500'); },500); }); } timer().then(result => { console.log(result); //500 }).catch(err => { console.log(err.message); });

------------------------------------------------------------------------------------------------------- //返回一个同步的值 let sayHi = async function sayHi(){ let hi = await 'hello world'; return hi; //等同于return Promise.resolve(hi); } sayHi().then(result => { console.log(result);//hello word });

Jsonp的原理。怎么去读取一个script里面的数据?


首先动态添加一个script标签,script标签里的src是没有跨域限制的

在客户端脚本中注册一个回调函数,然后把回调函数的名字传递给服务器,服务端得到请求的数据后,用回调函数把输出返回的内容包起来,这样,服务器生成的json数据就能被客户端正确接受

然后以js语法的方式,生成一个function,function的名字就是传递上来的参数callback方法的名字

最后将json数据直接以入参的方式,放置到function中,这样就生成了一段js语法的文档,返回给客户端

function jsonpCallback(result) {

alert(result.msg);

}

其中jsonCallback是客户端注册的,获取跨域服务器上的JSON数据后,回调的函数。http://crossdomain.com/jsonServerResponse?jsonp=jsonpCallback 这个url是跨域服务器取JSON数据的接口,参数为回调函数的名字,返回的格式为: jsonpCallback({msg:'this is jsondata'}) 。如此就生成了一段js语法的文档, 传回客户端就可以调用jsonpCallBack函数了.

JSONP的原理与实现


同源策略

前端跨域是每个前端人绕不过的坎,也是必须了解的一个知识点。我记得第一次遇到前端跨域这个坑的时候,真是无语到极点,对于一个移动端出身的人来说,觉得这个玩意无可理喻。但是后来慢慢了解,觉得前端的同源策略是非常有必要的。同源策略就是浏览器默认让www.baidu.com不能加载来自www.google.com的数据。对于现在来说,所有数据都是同源的可能性基本上很小,比如我们公司静态资源www.image.com和前端资源www.htmlcss.com的CDN路径都不一样,前端获取后台数据www.apidata.com又是另一个地址。如何解决这个坑呢?我们公司通过两种方式来避开。具体就是通过设置Access-Control-Allow-Origin来做POST请求,用JSONP来实现GET请求,因为JSONP只能实现GET请求。

通过Access-Control-Allow-Origin支持跨域

有些人肯定就纳闷了,我就喜欢跨域,我就不关注安全,难道就没有办法了吗?当然是否定的。你需要做的,只是让服务器在返回的header里面加上Access-Control-Allow-Origin这个域就可以了。这样浏览器在接收到服务器返回的数据,就不会因为违反同源策略限制你拿到数据了。下面就用抓包来具体看一下:

当我打开这里点开h5链接这个链接的时候。会去https//m.ctrip.com通过POST请求数据,这里就用到了跨域。

:method: POST :authority: m.ctrip.com :scheme: https :path: /restapi/xyz content-length: 290 pragma: no-cache cache-control: no-cache accept: application/json origin: https://pages.ctrip.com user-agent: Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.84 Mobile Safari/537.36 content-type: application/json;charset=UTF-8 referer: https://pages.ctrip.com/ztrip accept-encoding: gzip, deflate, br accept-language: zh-CN,zh;q=0.9,zh-TW;q=0.8,en;q=0.7 {请求体,post请求的参数}

服务器返回的响应头如下:

:status: 200 server: Tengine/2.1.2 date: Thu, 28 Dec 2017 11:01:29 GMT content-type: application/json;charset=utf-8 access-control-allow-origin: * access-control-expose-headers: RootMessageId cache-control: private vary: Accept-Encoding clogging_trace_id: 8196881814119217567 rootmessageid: 921812-0a0e0de1-420683-219524 x-powered-by: CTrip/SOA2.0 Win32NT/.NET soa20-response-status: Success x-aspnet-version: 4.0.30319 x-powered-by: ASP.NET x-gate: ctrip-gate x-gate-instance: unknown x-originating-url: http://m.ctrip.com/xyz x-gate-remote-call-cost: 9 content-encoding: gzip slb-http-protocol-version: HTTP/2.0 access-control-expose-headers: slb-http-protocol-version {服务器返回的有用数据}

我们可以看到,这里有access-control-allow-origin这个响应域就解决了问题。这个方法是最简单的,而且前端POST请求最常见的方法(不确定还有其他好的解决方案)。这种方式最好就是通过他获取服务数据,不要加载js脚本。小心被别人注入攻击。

JSONP的基本原理

讲JSONP之前,我先亮出一段常见的代码。下面这个方法主要就是动态的创建一个script标签,然后设置src属性。并且添加到document的第一个script标签之前。也就是说动态去加载一个javscript脚本。

function loadJs(src, attrs = {}) { return new Promise((resolve, reject) => { const ref = document.getElementsByTagName('script')[0] //创建一个scrpt标签 const script = document.createElement('script') //设置script标签的资源路径 script.src = src script.async = true //设置属性 for (let key in attrs) { script.setAttribute(key, attrs[key]) } //script标签加入document中 ref.parentNode.insertBefore(script, ref) script.onload = resolve script.onerror = reject }) }

最有意思的是script标签的src不受跨域限制。也就是说wwww.baidu.com的文件可以通过上面这个方法无限制的加载www.google.com的js文件。这个就是JSONP的实现的最基本原理。每一个JSONP请求就是动态的创建script元素,然后通过src属性去加载数据,而且一般是通过callback这个回调方法来返回服务器数据,然后再把script标签移除。如此周而复始的循环,想想都累啊。下面看一个JSON的标准格式,服务器会获取到callback这个回调方法。然后通过方法调用的方式把数据返回来,也就是执行callbackFun方法。serverdata就是服务器给客户端的数据。至于callback这个名字,可以自己定义,有客户端和服务器商量决定。

function callbackFun(serverdata){ console.log(serverdata) } <script src="" target="_blank">http://wwww.baidu.com/jsonp.js?callback=callbackFun"></script>

JSONP的实现

下面我会对JSONP做一个最基本的实现。使用Vue和node.js分别实现客户端和服务端,代码地址

首先我们先看客户端的实现:

//获取header的第一个子元素 let container = document.getElementsByTagName("head")[0]; /** * 生成随机字符串 */ function makeid() { var text = ""; var possible = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; for (var i = 0; i < 5; i++) text += possible.charAt(Math.floor(Math.random() * possible.length)); return text; } /** * jsonp请求的实现。返回一个promise对象对应请求成功和请求失败。 * @param {*请求的url} url * @param {*请求的参数} options */ function jsonpRequest(url, options) { return new Promise((resolve, reject) => { try { if (!url) { reject({ err: new Error("url不能为空"), result: null }); } if (!document || !global) { reject({ err: new Error("系统环境有问题"), result: null }); } //创建一个script元素 let scriptNode = document.createElement("script"); //请求参数 let data = options || {}; //回调函数的具体值,服务器和客户端就根据这个方法名来确定请求与返回数据之间的对应。 let fnName = "jsonp" + makeid(); // 把callback加入请求参数中 data["callback"] = fnName; // 拼接url var params = []; //参数的拼接与处理 for (let [key, value] of Object.entries(data)) { params.push(encodeURIComponent(key) + "=" + encodeURIComponent(data[key])); } url = (url.indexOf("?")) > 0 ? (url + "&") : (url + "?"); url += params.join("&"); //把处理好的url赋值给script元素的src属性。 scriptNode.src = url; // 把回调函数暴露为全局方法。script加载回来以后,会执行fnName对应的这个方法。 global[fnName] = function(ret) { resolve({ err: null, result: ret }) //请求完成。删除script元素 container.removeChild(scriptNode); //全局对象中删除已经请求完成的回调方法 delete global[fnName]; } // script元素遇到错误 scriptNode.onerror = function(err) { reject({ err: err, result: null }) //删除script元素和全局回调方法 container.removeChild(scriptNode); global[fnName] && delete global[fnName]; } //指定元素类型 scriptNode.type = "text/javascript"; //把script元素添加到header元素中。到这里script元素就会自动加载src。也就是我们的请求发出去了。 container.appendChild(scriptNode) } catch (error) { //异常处理捕获 reject({ err: error, result: null }); } }); } export default jsonpRequest;

这段代码主要做了如下几件事:

  • 创建一个script标签元素,并且添加到header元素里面。

  • 拼接script元素的src属性,其中必然好汉callback这个参数,服务端根据这个参数的值回调。

  • 回调以后需要手动把script标签元素移除,并且删除全局的回调函数名。

客户端的使用如下,是不是感觉简洁明了,比ES5的回调爽多了:

import jsonpRequest from "../lib/jsonpRequest.js"; async sendJSONPRequest() { //参数 let params = { name: "老黄", site: "www.huangchengdu.com" }; this.showLoading(); //发送请求 let { err, result } = await jsonpRequest( "https://www.huangchengdu.com/jsonp/jsonpRequest", params ); //处理返回的数据 this.hiddenLoading(); if (err) { alert(err.message || "请求出错了"); this.serverData.err = JSON.stringify(err); } else { this.serverData = result; } }

服务端的实现如下。

let express = require('express'); let router = express.Router(); //JSONP请求 router.get('/jsonpRequest', function(req, res, next) { //console.log("=====================" + JSON.stringify(req.query)); //获取name和site参数的值 let name = req.query.name; let site = req.query.site; //拼接回调值 let serverres = { serverReceive:{ name:name, site:site }, serverSend:"hello," + name + ".your site is https://" + site } //返回值。其实就是callback....()种种类型javascript字符串 res.end(req.query.callback + "(" + JSON.stringify(serverres) + ")") }); module.exports = router;

服务端代码说明如下:

  • res.end是express表示对http请求返回。具体返回的数据类似于callback随机数(服务端数据)这种类型。

  • 客户端在收到callback随机数(服务端数据)这个数据以后,会自动按照javascript脚本解析执行。具体就是一个全局方法调用,方法名是callback随机数,参数是服务端数据。这样就实现了服务端数据的回调。

  • 客户端在global对象下面注册了callback随机数这个方法。具体代码是上面global[fnName] = function(ret) {这一行。

  • callback随机数是服务端和客户端商量,具体可以自己决定,真实的时候类似于callbacksuijishu这种类型。

JSONP请求报文

JSONP本质上就是一个普通的GET请求。无非就是这个请求是通过script标签来发送的。而且请求参数里面必定会有一个callback参数。

下面我们具体抓包看一下我们的请求报文:

GET /jsonp/jsonpRequest?name=%E8%80%81%E9%BB%84&site=www.huangchengdu.com&callback=jsonpiFuL4 HTTP/1.1 Host: www.huangchengdu.com Accept: */* Connection: keep-alive Cookie: session=s%3Anot8KTW5FiTLY0VNgrrKksXY96AE2kWT.hrQeyL%2BVjt8ICJjfFqoFdV8JV3lx0IsDntx%2B%2Bc%2FEM98 User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_2) AppleWebKit/604.4.7 (KHTML, like Gecko) Version/11.0.2 Safari/604.4.7 Accept-Language: zh-cn Referer: http://localhost:8081/ Accept-Encoding: br, gzip, deflate

返回报文:

HTTP/1.1 200 OK Server: nginx/1.6.2 Date: Fri, 29 Dec 2017 03:26:31 GMT X-Powered-By: Express Transfer-Encoding: chunked Connection: Keep-alive jsonpiFuL4({"serverReceive":{"name":"��","site":"www.huangchengdu.com"},"serverSend":"hello,��.your site is https://www.huangchengdu.com"})

从上面的报文我们可以返现。请求的callback参数的值和返回的响应体的名称是一样的。响应提就是一个普通的函数。服务器返回的数据作为函数的参数。

XSS攻击


XSS的全称是Cross-site scripting,翻译过来就是跨站脚本。script可以跨域加载脚本这个特性,合理利用比如JSONP。如果不合理利用,比如某个坏人通过某种方式,让你的浏览器去加载恶意的javascrpt脚本,必然就会导致敏感信息被盗或者财务损失。最常见的就是XSS攻击,其实就是注入恶意脚本。真是凡事都有利有弊,就看如何使用了。常用的XSS攻击手段和目的有如下几种:

  • 盗用cookie,获取敏感信息。

  • 利用植入Flash,通过crossdomain权限设置进一步获取更高权限;或者利用Java等得到类似的操作。

  • 利用iframe、frame、XMLHttpRequest或上述Flash等方式,以(被攻击)用户的身份执行一些管理动作,或执行一些一般的如发微博、加好友、- 发私信等操作。

  • 利用可被攻击的域受到其他域信任的特点,以受信任来源的身份请求一些平时不允许的操作,如进行不当的投票活动。

  • 在访问量极大的一些页面上的XSS可以攻击一些小型网站,实现DDoS攻击的效果。

如果某一个字符串里面有var a = 1;<script>alert('我是你大爷')</script>;var b = 2;这种类型的字符串。而且我们刚好要通过script标签加载。那么他就会弹出一个我是你大爷。避免的方式就是把存在这种可能性的地方都处理过,如果包含类似<script>这种字符的脚本就处理掉或者干脆返回错误。目前最常见的预防操作有如下几种:

  • 将重要的cookie标记为http only,这样的话Javascript 中的document.cookie语句就不能获取到cookie了。

  • 只允许用户输入我们期望的数据。例如:年龄的textbox中,只允许用户输入数字。 而数字之外的字符都过滤掉。

  • 对数据进行Html Encode处理。

  • 过滤或移除特殊的Html标签,例如:<script>,<iframe>,<for<,>for>,"for。

  • 过滤JavaScript事件的标签。例如"οnclick=","onfocus"等等。

3 CSRF攻击

这玩意我了解不多,也无法做出模拟操作。跨站请求伪造(英语:Cross-site request forgery),也被称为one-click attack或者session riding,通常缩写为 CSRF 或者 XSRF, 是一种挟制用户在当前已登录的Web应用程序上执行非本意的操作的攻击方法。[1] 跟跨网站脚本(XSS)相比,XSS 利用的是用户对指定网站的信任,CSRF 利用的是网站对用户网页浏览器的信任。

我的理解就是,比如你刚去淘宝买了东西,并且浏览器有你的session护着cookie之类的信息。然后你马上又进入一个不该去的网站,并且点击了里面的一个淘宝链接,然后在你不知情的情况下做一些违法操作。这样阿里后台是不知道的,因为你刚刚通过合法手段买了东西,从而达到在你不知情的情况下,而且淘宝也信任你的情况下,畏畏缩缩偷偷摸摸的干坏事。

3.1 SCRF预防

检查Referer字段,通过这个字段来判断用户是从那个地址跳转到当前地址的。HTTP头中有一个Referer字段,这个字段用以标明请求来源于哪个地址。在处理敏感数据请求时,通常来说,Referer字段应和请求的地址位于同一域名下。以上文银行操作为例,Referer字段地址通常应该是转账按钮所在的网页地址,应该也位于www.examplebank.com之下。而如果是CSRF攻击传来的请求,Referer字段会是包含恶意网址的地址,不会位于www.examplebank.com之下,这时候服务器就能识别出恶意的访问。这种办法简单易行,工作量低,仅需要在关键访问处增加一步校验。但这种办法也有其局限性,因其完全依赖浏览器发送正确的Referer字段。虽然http协议对此字段的内容有明确的规定,但并无法保证来访的浏览器的具体实现,亦无法保证浏览器没有安全漏洞影响到此字段。并且也存在攻击者攻击某些浏览器,篡改其Referer字段的可能。

添加校验token,这个就最常见了,现在那个前端网站还不加一个验证码啊。不管你如何千变万化,你验证码中是用户数据的吧,而且现在好像越来越流行手机号码验证了。CSRF的本质在于攻击者欺骗用户去访问自己设置的地址,所以如果要求在访问敏感数据请求时,要求用户浏览器提供不保存在cookie中,并且攻击者无法伪造的数据作为校验,那么攻击者就无法再执行CSRF攻击。这种数据通常是表单中的一个数据项。服务器将其生成并附加在表单中,其内容是一个伪乱数。当客户端通过表单提交请求时,这个伪乱数也一并提交上去以供校验。正常的访问时,客户端浏览器能够正确得到并传回这个伪乱数,而通过CSRF传来的欺骗性攻击中,攻击者无从事先得知这个伪乱数的值,服务器端就会因为校验token的值为空或者错误,拒绝这个可疑请求。

事件代理js实现


nodejs的架构、优缺点、回调


1、Nodejs语法完全是js语法,只要你懂js基础就可以学会Nodejs后端开发

Node打破了过去JavaScript只能在浏览器中运行的局面。前后端编程环境统一,可以大大降低开发成本。

2、NodeJs超强的高并发能力

NodeJs的首要目标是提供一种简单的、用于创建高性能服务器及可在该服务器中运行的各种应用程序的开发工具。

首先让我们来看一下现在的服务器端语言中存在着什么问题。在Java、PHP或者.NET等服务器语言中,会为每一个客户端连接创建一个新的线程。而每个线程需要耗费大约2MB内存。也就是说,理论上,一个8GB内存的服务器可以同时连接的最大用户数为4000个左右。要让web应用程序支持更多的用户,就需要增加服务器的数量,而web应用程序的硬件成本当然就上升了。

NodeJs不为每个客户连接创建一个新的线程,而仅仅使用一个线程。当有用户连接了,就触发一个内部事件,通过非阻塞I/O、事件驱动机制,让Node.js程序宏观上也是并行的。使用Node.js,一个8GB内存的服务器,可以同时处理超过4万用户的连接。

3、实现高性能服务器

严格地说,Node.js是一个用于开发各种web服务器的开发工具。在Node.js服务器中,运行的是高性能V8 JavaScript脚本语言,该语言是一种可以运行在服务器端的脚本语言。

那么,什么是V8 JavaScript脚本语言呢?该语言是一种被V8 JavaScript引擎所解析并执行的脚本语言。V8 JavaScript引擎是由Google公司使用C++语言开发的一种高性能JavaScript引擎,该引擎并不局限于在浏览器中运行。Node.js将其转用在了服务器中,并且为其提供了许多附加的具有各种不同用途的API。例如,在一个服务器中,经常需要处理各种二进制数据。在JavaScript脚本语言中,只具有非常有限的对二进制数据的处理能力,而Node.js所提供的Buffer类则提供了丰富的对二进制数据的处理能力。

另外,在V8 JavaScript引擎内部使用一种全新的编译技术。这意味着开发者编写的高端的 JavaScript 脚本代码与开发者编写的低端的C语言具有非常相近的执行效率,这也是Node.js服务器可以提供的一个重要特性。

4、开发周期短、开发成本低、学习成本低

Node.js自身哲学,是话最小的硬件成本,追求更高的并发,更高的处理性能。

  1. 它是一个Javascript运行环境

  1. 依赖于Chrome V8引擎进行代码解释

  1. 异步事件驱动

  1. 非阻塞I/O

  1. 轻量、可伸缩,适于实时数据交互应用

  1. 单进程,单线程(这里指主线程)

  1. 性能出众

link和@import引入CSS的区别?


相同点:两者都可以引入外部样式

不同点:

1、从属关系不同

link标签是HTML中标签,不仅可以加载样式,还可以定义一些属性ref

@import是css提供语法规则,其作用只有一个引入外部样式

2、加载顺序不同

link标签与页面同时加载

@import引入的css样式,是在页面加载完毕后样式才被加载

3、兼容性不同

link属于HTML中的标签,无兼容问题

@import是css2.1才有的语法,兼容IE5+

4、DOM操作不同

link是一个标签,可以通过DOM操作这个标签,引入样式

@import是css样式层的东西,没有办法进行DOM操作

AMD和CMD,commonJS的区别


commonJS

  • 一个单独的文件就是一个模块,有自己的作用域。在一个文件里定义的变量、函数、类都是私有的,对其他文件不可见。

  • 加载模块使用 require 方法,该方法读取一个文件并执行,最后返回该文件内部的 exports 对象。

  • 模块加载的顺序,按照其在代码中出现的顺序。加载模块是同步的,所以只有加载完才能执行后面的操作。

  • 模块可以多次加载,但是只会在第一次加载时运行一次,然后运行结果就被缓存,以后再加载,就会直接读取缓存结果。要想让模块再次运行,必须清除缓存。

像 Node.js 主要用于服务器的编程,加载的模块文件一般都已经存在本地硬盘,所以加载起来比较快,不用考虑异步加载的方式,所以 CommonJS 规范比较适用。但如果是浏览器环境,要从服务器加载模块,这是就必须采用异步模式。所以就有了 AMD 和 CMD 解决方案。

AMD

  • 异步加载模块规范,这样模块和模块的依赖可以被异步加载。这和浏览器的异步加载模块的环境刚好匹配。

  • 本规范只定义了一个函数 define,它是全局变量。

  • define.amd 是遵从 AMD 规范的一个特殊标识符,它的值为一个对象

CMD

  • 本规范只定义了一个函数 define,它是全局变量。

  • define.cmd 是遵从 CMD 规范的一个特殊标识符,它的值为一个对象

复制代码

UMD

UMD 是希望解决跨平台的解决方案。

  • UMD 是 AMD 和 Common JS 的糅合。

  • AMD 以浏览器第一的原则发展,异步加载模块。

  • Common JS 以服务器第一原则发展,选择同步加载,它的模块无需包装。

  • UMD 先判断是否支持 Node.js 的模块( exports ) 是否存在,存在则使用 Node.js 模块模式。然后再判断是否支持 AMD ( define 是否存在 ),存在则使用 AMD 方式加载模块。

用promise手写ajax


创建proimise

使用promise对象

解释平衡二叉树,以及在数据结构中的应用(红黑树)


一,什么是“平衡二叉查找树”

1,定义:二叉树中任意一个节点的左右子树的高度相差不能大于1。

所以:完全二叉树,满二叉树都是平衡二叉树,非完全二叉树也有可能是平衡二叉树。

2,平衡二叉查找树不仅满足上面平衡二叉树的定义,还满足二叉查找树的特点。

3,发明平衡二叉查找树这类数据结构的初衷是解决普通二叉查找树在频繁的插入,删除等动态更新的情况下,出现时间复杂度退化的问题。

所以,平衡二叉查找树中“平衡”的意思,其实就是让整棵树左右看起来比较“对称”,比较“平衡”,不要出现左子树很高,右子树很矮的情况。这样就能让整颗树的高度相对低一些,相应的插入,删除,查找等操作的效率高一些。

4,若设计一个新的平衡二叉查找树,只要树的高度不比log2n大很多(如树的高度仍然是对数量级的),尽管它不符合严格的平衡二叉查找树的定义,但它仍然可以被认为是一个合格的平衡二叉查找树。

二,如何定义一棵“红黑树”

1,平衡二叉查找树有很多,如:Splay Tree(伸展树),Treap(树堆)等,但是我们提到平衡二叉查找树,听到的基本都是红黑树。他的出境率甚至要高于“平衡二叉查找树”这几个字,甚至在有些时刻,默认平衡二叉查找树就是红黑树

2,红黑树:英文“Red-Black-Tree”,简称R-B Tree,有如下特性:

它是一种不严格的平衡二叉查找树。

红黑树中的节点,一类别标记为黑色,一类被标记为红色。

根节点是黑色的;

每个叶子节点都是黑色的空节点(NIL),也就是说,叶子节点不存储数据;

任何相邻的节点都不能同时为红色,即红色节点都是被黑色节点隔开的;

每个节点,从该节点到达其可达叶子节点的所有路径,都包含相同数目的黑色节点;

3,二叉查找树很多操作的性能都跟树的高度成正比,一课极其平衡的二叉树(满二叉树或完全二叉树)的高度大约是log2n,所以要证明红黑树是近似平衡的,我们只需要分析,红黑树的高度是否比较稳定地趋近log2n就好

4,红黑树的高度分析

①:首先,若将红色节点从红黑树中去除,那单纯包含黑色节点的红黑树的高度比包含相同节点个数的完全二叉树的高度要小。所以去掉红色节点的“黑树”的高度也不会超过log2n。

②:在红黑树中,红色节点不能相邻,即有一个红色节点就要至少有一个黑色节点,将它更其他红色节点隔开。

红黑树中包含最多黑色节点的路径不会超过log2n,所以加入红色节点之后,最长路径不会超过2log2n,即,红黑树的高度近似2log2n

③:红黑树的高度只比高度平衡的AVL树的高度(log2n)仅仅大了一倍,在性能上下降的并不多。

三:工程中大家都喜欢用红黑树这种平衡二叉查找树的原因:

①:Treap,Splay Tree,绝大部分情况下,它们操作的效率都很高,但是也无法避免极端情况下时间复杂度的退化。尽管这种情况出现概率不大,但是对于单次操作时间非常敏感的场景来讲,它们不适用。

②:AVL树是一种高度平衡的二叉树,所以查找的效率非常高,但是,有利有弊,AVL树为了维持这种高度平衡,要付出更多代价,每次插入,删除都要做调整,就比较复杂,耗时。所以有频繁的插入,删除操作的数据集合,使用AVL树的代价就有点高了。

③:红黑树只是做到了近似平衡,并不是严格的平衡,所以维护平衡的成本上,要比AVL树低。

所以,红黑树的插入,删除,查找各种操作性能都比较稳定。对于工程应用来说,结果状态可控可预期。

四、理解红黑树源码

使用递推思想

暂时无法理解

N、问题

动态数据结构支持动态的数据插入、删除、查找操作,除了红黑树,我们前面还学习过哪些呢?能对比一下各自的优势、劣势,以及应用场景吗?

散列表:插入删除查找都是O(1), 是最常用的,但其缺点是不能顺序遍历以及扩容缩容的性能损耗。适用于那些不需要顺序遍历,数据更新不那么频繁的。

跳表:插入删除查找都是O(logn), 并且能顺序遍历。缺点是空间复杂度O(n)。适用于不那么在意内存空间的,其顺序遍历和区间查找非常方便。

红黑树:插入删除查找都是O(logn), 中序遍历即是顺序遍历,稳定。缺点是难以实现,去查找不方便。其实跳表更佳,但红黑树已经用于很多地方了。

手写jsonp的实现


function jsonp({ url, params, callbackName }) {

//拼接url地址

function getUrl() {

let paramsStr = ''

for (let item in params) {

if (Object.prototype.hasOwnProperty.call(params, item)) {

paramsStr += `${item}=${params[item]}&`

}

}

return `${url}?${paramsStr}callback=${callbackName}`

}

return new Promise((resolve, reject) => {

//创建一个script标签

let scriptEle = document.createElement('script')

//给script标签的src属性赋值。

scriptEle.src = getUrl()

//将其插入文档流中

document.body.appendChild(scriptEle)

//监听回调函数

window[callbackName] = (data) => {

resolve(data)

document.body.removeChild(scriptEle)

}

})

}

jsonp({ url: 'http://127.0.0.1:3000', params: {}, callbackName: 'func' }).then(res => {

console.log(res)

})

http请求头,请求体,cookie在哪个里面?url在哪里面?


每个HTTP请求和响应都会带有相应的头部信息。默认情况下,在发送XHR请求的同时,还会发送下列头部信息:

  • Accept:浏览器能够处理的内容类型

  • Accept-Charset:浏览器能够显示的字符集

  • Accept-Encoding:浏览器能够处理的压缩编码

  • Accept-Language:浏览器当前设置的语言

  • Connection:浏览器与服务器之间连接的类型

  • Cookie:当前页面设置的任何Cookie

  • Host:发出请求的页面所在的域

  • Referer:发出请求的页面的URL

  • User-Agent:浏览器的用户代理字符串

虽然不同浏览器实际发送的头部信息会有所不同,但以上列出的基本上是浏览器都会发送的,用setRequestHeader()方法也可以设置请求头信息,该函数接受两个参数:头部字段的名称和头部字段的值。要成功发送请求头部信息,需在调用open方法之后且调用send方法之前调用。

HTTP响应头部信息:

Date:表示消息发送的时间,时间的描述格式由rfc822定义

server:服务器名字。

Connection:浏览器与服务器之间连接的类型

content-type:表示后面的文档属于什么MIME类型

Cache-Control:控制HTTP缓存

OSI模型,HTTP,TCP,UDP分别在哪些层


物理层

物理层是 OSI 的第一层或最低层,定义了电气和机械规格,如电缆、连接器和介质的信号。

数据链路层

数据链路层位于OSI 模型的第二层,这一层的数据单元称为“”。

数据链路层包括两个子层:

  • 第一层称为逻辑链路控制层,负责将数据包传输到接收网络层,从报头中识别网络层协议地址,并提供流量控制。

  • 第二层称为媒体访问控制层,是网络的逻辑链路控制层和物理层之间的连接层,该层用于通过网络传输数据包。

网络层

网络层位于OSI 模型的第三层,网络层有两个主要功能:

  • 一种是将分段分解为网络数据包,然后在接收端重新组装数据包。

  • 另一种是通过发现跨物理网络的最佳路径来路由数据包。

网络层使用IP地址将数据包路由到目标节点。

传输层

传输层位于OSI 模型的第四层,负责传送、错误检查、流量控制和数据包排序,传输层的数据单元是数据段

两种经典的传输层协议:

  • 传输控制协议 (TCP):面向连接的可靠协议

  • 用户数据报协议 (UDP):无连接且不可靠的协议

会话层

会话层位于OSI 模型的第五层,允许不同平台的用户在他们之间建立一个活跃的通信会话,在两个不同应用程序之间的对话中提供同步。

这个相当于你买了一件大商品,被拆分寄送,没送到一个快递点的时候都要去确认拆分的商品都已到位,一直确认,直到全部收到。

表示层

表示层位于OSI 模型的第六层,以易于理解的形式将数据呈现给用户,表示层负责语法,因为发送方和接收方使用的通信模式可能不同,你可以将表示层看作是翻译。

应用层

应用层位于OSI 模型的第七层,应用层是唯一直接与用户数据交互的 OSI 层,该层提供了一个协议,允许软件发送和接收信息以及向用户呈现有意义的数据。

OSI每层的数据单元

  • APDU:应用协议数据单元

  • PPDU:表示协议数据单元

  • SPDU:会话协议数据单元

  • TPDU:传输协议数据单元(段)

  • 数据包:网络层数据单元

  • :数据链路层数据单元

  • :物理层数据单元

实现两个数组的排序合并


方法1:直接合并后排序时间复杂度为O((m+n)*log(m+n))

function mergeOfSort(nums1, m, nums2, n) {

nums1.splice(m, nums1.length - m, ...nums2);

nums1.sort((a, b)=> a - b);

}

let nums1 = [1,2,3,0,0,0], m = 3;

let nums2 = [2,5,6], n = 3;

// mergeOfSort(nums1, m, nums2, n)

// mergeOfDoubleKey(nums1, m, nums2, n)

mergeOfLastDoubleKey(nums1, m, nums2, n)

console.log(nums1);

方法2. 双指针

时间复杂度为O(m+n) 指针移动单调递增,最多移动m+n次。空间复杂度:O(m+n)。需要建立长度为m+n的中间数组sorted。

function mergeOfDoubleKey(nums1, m, nums2, n) {

let p1 = 0, p2 = 0; // 分别的nums1和nums2的指针

const sorted = new Array(m + n).fill(0);

let cur;

while (p1 < m || p2 < n) {

if (p1 === m) {

cur = nums2[p2++];

}else if (p2 === n) {

cur = nums1[p1++]

} else if (nums1[p1] < nums2[p2]) {

cur = nums1[p1++]

} else {

cur = nums2[p2++]

}

sorted[p1+p2-1] = cur;

}

for (let i = 0; i !== m+n ; i++) {

nums1[i] = sorted[i]

}

}

方法3. 逆向双指针(最优)

时间复杂度:O(m+n) 空间复杂度:O(1)直接对数组nums1原地修改,不需要额外空间。

function mergeOfLastDoubleKey(nums1, m, nums2, n) {

let p1 = m - 1, p2 = n - 1;

let tail = m + n - 1; // 表示最长数组的当前位置

let cur;

while (p1 >= 0 || p2 >= 0) {

if (p1 === -1) {

cur = nums2[p2--];

} else if (p2 === -1) {

cur = nums1[p1--];

} else if (nums1[p1] > nums2[p2]) {

cur = nums1[p1--]

} else {

cur = nums2[p2--]

}

nums1[tail--] = cur; // 从最后一位开始赋值

}

}

手写实现jquery里面的insertAfter


在每个 <p> 元素后插入一个 <span> 元素:

$("button").click(function(){ $("<span>Hello world!</span>").insertAfter("p"); });

手写实现requireJS模块实现


先回顾一下RequireJS的两个重要api

定义模块

define(id?, dependencies?, factory)

加载模块

require([module], callback)

require和define函数的关系

(1)require和define函数接收同样的参数,define和require在依赖处理和回调执行上都是一样的

(2)define的回调函数需要有return语句返回模块对象,这样define定义的模块才能被其他模块引用,所以用它来定义模块(建议在一个文件中使用一次);require的回调函数不需要return语句。其实在我看来,require函数可以看做是特殊的define函数,它用来定义一个顶层匿名模块,用来加载和使用模块,这个模块不需要被其他模块加载。

RequireJS中的执行流程

(1)RequireJS首先找到data-main属性,然后根据属性值(通过新建一个script标签)加载并且解析入口文件

(2)先调用 require 函数和define函数,将所有的依赖转化为script 节点插入到dom 中,然后每一个 节点的onload事件中,将该模块作为实体保存起来,并检查所有模块是否加载完成,如果加载完成,递归执行所有回调

2.实现一个简单的RequireJS

首先这里先说明一点,下面我们主要了解实现的整体流程,会粘出一些主要代码,里面可能会包含一些辅助函数,具体可以查看我github上的源码

(1)定义一个全局变量

首先,需要定义几个全局变量,用来保存已经加载的模块,尚未加载的模块,所有模块等全局信息

var context={

topModule:"", //顶层模块

waitings:[], // 尚未加载的模块

loadeds:[], // 已经加载的模块

baseUrl:"",

/**

* 每一个模块都有下面的几个属性:

* moduleNmae 模块名称

* deps 模块依赖

* factory 模块工厂函数 .

* args 该模块的依赖模块的返回值

* returnValue 该模块工厂函数的返回值

*/

modules:[] // 模块集合

} ;

(2)加载 require 顶层模块

在require.js 里面都是用 data-main 属性来指定入口文件,所以先寻找 data-main 属性,并插入到 head中 。这里将 data-main 作为的路径作为 baseUrl

/**

  • 查找data-main属性的script标签,

  • 根据属性值(通过新建一个script标签)加载并且解析入口文件
    */
    if (isBrowser) {
    var scripts=document.getElementsByTagName('script');
    var head,src,subPath,mainScript;
    eachList(scripts,function(script){
    var dataMain = script.getAttribute('data-main');
    if (dataMain) {
    if (!head) {
    head = script.parentNode;
    }
    if (!context.baseUrl) {
    src = dataMain.split('/');
    mainScript = src.pop();
    subPath = src.length ? src.join('/') + '/' : './';
    context.baseUrl = subPath;
    }
    // 创建顶层节点
    var dataMainNode = document.createElement('script');
    dataMainNode.async = true;
    head.appendChild(dataMainNode);
    dataMainNode.src = dataMain+ ".js";
    dataMainNode.onload = function() {
    // 将顶层模块 从waitings里面除去,并添加到loadeds数组中
    removeByEle(context.waitings, context.topModule)
    context.loadeds.push(context.topModule);
    }
    return true;
    }
    });
    (3)定义 require 方法
    require 方法用来使用模块,也就是定义一个顶层模块,这个模块不需要被其他模块加载

/**

  • require方法,加载一个模块

  • @param {[type]} deps [依赖数组]

  • @param {Function} callback [工厂函数]

  • @return {[type]}
    */
    requireJs.require=function(deps,callback){

if (typeof name !== 'string') {

callback = deps;

deps = name;

name = null;

}

if (!isArray(deps)) {

callback = deps;

deps = [];

}

// 生成随机模块名,方法

let moduleName = getUnqName();

context.topModule = moduleName;

context.waitings.push(moduleName);

// 生成一个模块配置

context.modules[moduleName] = {

moduleName: moduleName,

deps: deps,

factory: callback,

args: [],

returnValue: ""

}

deps.forEach(function(dep) {

var scriptNode = document.createElement("script");

scriptNode.setAttribute("data-module-name", dep);

scriptNode.async = true;

scriptNode.src = context.baseUrl + dep + ".js";

document.querySelector("head").appendChild(scriptNode);

scriptNode.onload = scriptOnload;

context.waitings.push(dep);

});

}

这里需要注意一个函数scriptOnload,在script 节点加载完成后触发。将对应模块从waitings 里面删除,同时往loadeds里面添加该模块,如果发现 waitings为空,那么就开始递归执行工厂函数 。

/**

  • [每一个脚本插入head中,都会执行这个事件 。这个函数完成两件事:

  1. 如果是一个匿名模块加载,那么取得这个匿名模块,并完成模块命名,

  1. 当节点加载完毕,判断context.waitings是否为空,如果不为空,返回,如果为空,说明已经全部加载完毕,现在就可以执行所有的工厂函数]

  • @param {[object]} e [事件对象]

  • @return {[type]}
    */
    function scriptOnload(e) {
    e = e || window.event;
    let node = e.target;
    let moduleName = node.getAttribute('data-module-name');
    tempModule.moduleName = moduleName;
    context.modules[moduleName] = tempModule;
    removeByEle(context.waitings, moduleName);
    context.loadeds.push(moduleName);
    if (!context.waitings.length) {
    console.log(context.modules);
    exec(context.topModule);
    }
    }
    (4)定义 define 方法
    其实define函数和上面的require函数做了差不多相同的事,差别在于require自动生成了一个模块名。并且require中设置了context.topModule

/**

  • [define和 require 做的工作几乎相同]

  • @param {[array]} deps [依赖数组]

  • @param {[function]} callback [工厂函数]

  • @return {[type]}
    */
    requireJs.define=function(name,deps,callback){
    if (typeof name !== 'string') {
    callback = deps;
    deps = name;
    name = null;
    }
    if (!isArray(deps)) {
    callback = deps;
    deps = [];
    }

//生成一个模块配置

tempModule = {

deps: deps,

factory: callback,

args: [],

returnValue: ""

}

// 递归遍历所有依赖,添加到 `head` 中,并设置 这个节点的一个属性`data-module-name`标识模块名

deps.forEach(function(dep) {

var scriptNode = document.createElement("script");

scriptNode.setAttribute("data-module-name", dep);

scriptNode.async = true;

scriptNode.src = context.baseUrl + dep + ".js";

document.querySelector("head").appendChild(scriptNode);

scriptNode.onload = scriptOnload;

context.waitings.push(dep);

});

}

(5)执行回调

我们再回到scriptOnload 函数,每个模块加载完成,就会在 waitings 里面去掉,然后检查waitings 数组,如果为空,说明全部加载完,就可以执行 exec函数,在这里函数中,递归执行所有的回调 。

/**

  • 所有模块加载完毕,递归执行工程函数 , 核心方法

  • @param {[string]} moduleName [模块名]

  • @return {[type]}
    */
    function exec(moduleName) {
    let module = context.modules[moduleName];
    let deps = module.deps;
    let args = [];
    if(deps){
    deps.forEach(function(dep) {
    exec(dep);
    args.push(context.modules[dep].returnValue);
    });
    module.args = args;
    module.returnValue = context.modules[moduleName].factory.apply(context.modules[moduleName], args);
    }
    }
    3.测试
    //user.js
    define([], function () {
    return {
    checkLogin: function (name,pwd) {
    return name==="xxx"&&pwd==="yyy"
    }
    }
    })

//math.js

define(function () {

return {

add: function (a,b){

return a+b;

},

sub:function(a,b){

return a-b;

}

}

})

//main.js

require(["math","user"], function(math,user) {

if(user.checkLogin("xxx","yyz")){

console.log("12+21=" + math.add(12,21));

}else{

console.log('please sign in or register first');

}

})

//test.html

作者:gavin_zhuo

链接:https://www.jianshu.com/p/a0a5e19d3385

来源:简书

著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

jquery绑定click的方法有几种


$("ul li").bind("click",function () {

19 alert(this.innerHTML);

20 });

21

22 $("ul").on("click","li",function () {

23 alert("事件委托");

24 });

25 $("input").click(function () {

26 $("ul").append("<li>666</li>")

移动端适配问题


new operator实际上做了什么?


AJAX Request


// 1. 创建一个 new XMLHttpRequest 对象

let xhr = newXMLHttpRequest();

// 2. 配置它:从 URL /article/.../load GET-request

xhr.open('GET', '/article/xmlhttprequest/example/load');

// 3. 通过网络发送请求

xhr.send();

// 4. 当接收到响应后,将调用此函数

xhr.onload = function() { if (xhr.status != 200) {

// 分析响应的 HTTP 状态

alert(`Error ${xhr.status}: ${xhr.statusText}`);

// 例如 404: Not Found

} else {

// 显示结果

alert(`Done, got ${xhr.response.length} bytes`);

// response 是服务器响应

} };

xhr.onprogress = function(event) {

if (event.lengthComputable) {

alert(`Received ${event.loaded} of ${event.total} bytes`);

} else {

alert(`Received ${event.loaded} bytes`);

// 没有 Content-Length

} };

xhr.onerror = function() {

alert("Request failed");

};

作者:山山呀

链接:https://juejin.cn/post/6895032996315791368

来源:稀土掘金

著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

Cross-domain access(有关跨域)


var options = {

url:ipp+"/web/agentController/authentication",

jsonp: "callbacks",

contentType:false,

xhrFields:{withCredentials:true}, // 设置本地XHR对象的“名-值”映射。例如,可以在需要时设置“withCredentials”为真而执行跨域名请求

success: function(data) {

console.log(data);

$("#fade1").hide();

$("#MyDiv1").hide();

$("#wsm").hide();

$("#divTb").hide();

$("#bankk").show();

$("#weishenhe").show();

}

};

css 盒模型


计算宽度和高度的不同。

标准盒模型:盒子总宽度/高度 = width/height + padding + border + margin。( 即 width/height 只是内容高度,不包含 padding 和 border 值 )

IE盒子模型:盒子总宽度/高度 = width/height + margin = (内容区宽度/高度 + padding + border) + margin。( 即 width/height 包含了 padding 和 border 值 )

标准:box-sizing: content-box; ( 浏览器默认设置 )

IE: box-sizing: border-box;

实现事件代理(事件委托)


事件委托呢,又叫事件代理,事件委托就是利用事件冒泡,只指定一个事件处理程序,就可以管理某一类型的所有事件。

事件委托,通俗地来讲,就是把一个元素响应事件(click、keydown…)的函数委托到另一个元素;

一般来讲,会把一个或者一组元素的事件委托到它的父层或者更外层元素上,真正绑定事件的是外层元素,当事件响应到需要绑定的元素上时,会通过事件冒泡机制从而触发它的外层元素的绑定事件上,然后在外层元素上去执行函数。

委托的优点

减少内存消耗

试想一下,若果我们有一个列表,列表之中有大量的列表项,我们需要在点击列表项的时候响应一个事件

如果给每个列表项一一都绑定一个函数,那对于内存消耗是非常大的,效率上需要消耗很多性能;

因此,比较好的方法就是把这个点击事件绑定到他的父层,也就是 ul 上,然后在执行事件的时候再去匹配判断目标元素;

所以事件委托可以减少大量的内存消耗,节约效率。

动态绑定事件

比如上述的例子中列表项就几个,我们给每个列表项都绑定了事件;

在很多时候,我们需要通过 AJAX 或者用户操作动态的增加或者去除列表项元素,那么在每一次改变的时候都需要重新给新增的元素绑定事件,给即将删去的元素解绑事件;

如果用了事件委托就没有这种麻烦了,因为事件是绑定在父层的,和目标元素的增减是没有关系的,执行到目标元素是在真正响应执行事件函数的过程中去匹配的;

所以使用事件在动态绑定事件的情况下是可以减少很多重复工作的。

继承的两种方法


临时属性方式、call()、apply();

原型链继承

call/apply:优:1、可以实现多重继承 2、可以初始化继承自父类的参数;

缺:浪费内存资源,所有的实例都会拥有一份成员方法的副本;

原型链继承:用子类构造函数的参数去初始化父类属性是无法实现的。

less和sass掌握程度


实现方式不同。less是基于JavaScript运行,所以less是在客户端处理。而sass的安装需要Ruby,是在服务端处理的。

变量。less是以@开头定义的变量,如:@mainColor:#339; 而sass是以$开头定义的变量,如:$mainColor:#339;

输出设置。less没有输出设置,而sass提供了4种输出设置。nested,compact, compressed和expanded。默认为nested。

条件语句。less不支持条件语句,而sass可以使用if{}else{},for{}循环等等。

Animation还有哪些其他属性


1. animation-name(动画名称)

animation-name属性是必须存在的,因为animation-name的值默认是none,没有动画。

2. animation-duration(动画执行一次所需时间)

animation-duration属性也是必须存在的,因为animation-duration的值默认是0,没有动画。

3. animation-delay(动画在开始前的延迟时间)

animation-delay的值可以是秒(s)或者是毫秒(ms),默认值是0,没有延迟。

4. animation-timing-function(动画以何种运行轨迹完成一个周期)

animation-timing-function的值是贝塞尔曲线,默认值是ease,表示动画以低速开始,然后加速,最后在结束前变慢。 最常用的值有以下几个:

(1)linear:表示动画从头到尾的速度都是相同的。

(2)ease-in:表示动画以低速开始。

(3)ease-out:表示动画以低速结束。

(4)ease-in-out:表示动画以低速开始和结束。

如果没有想用的值,也可以直接使用三次贝塞尔函数,使用网站是http://cubic-bezier.com,可直接调试出自己想要的值;也可以在浏览器中直接调试,现在浏览器调试工具中都支持贝塞尔函数可视化调试。

5. animation-iteration-count(动画播放次数)

animation-iteration-count属性值有两种:

(1)直接写数字,自定义想要播放动画的次数。

(2)infinite:设置动画无线循环播放。

6. animation-fill-mode(定义元素动画结束以后或者未开始的元素样式)

默认值为none,标示,不会在动画结束或者未开始时给元素 添加样式

常用属性值为:

(1)forwards:表示动画结束后,元素直接接使用当前样式。

(2)backwards:表示在动画延迟时间元素使用关键帧中from的属性值或者to属性值(当animation-direction为reverse或者alternate-reverse时)

7. animation-direction(是否轮流反向播放动画)

默认值是normal,动画正常播放。如果动画只播放一次,则该属性无效。

常用的属性值为:

(1)reverse:表示动画反向播放。

(2)alternate:表示动画在奇数次播放时为正向播放,为偶数次播放时为反向播放。

(3)alternate-reverse: :表示动画在奇数次播放时为反向播放,为偶数次播放时为正向播放。

animation属性在书写通常合并在一起,除非需要单独设置的属性值,animation属性的简写形式为:animation:code 2s 2s linear infinite alternate forwards;

tcp/udp区别


1、TCP面向连接,UDP面向无连接;

2、TCP传输基于流,UDP传输基于报文;

3、TCP协议传输具有可靠性,UDP传输则不保证可靠数据交付

4、TCP占用系统资源多,效率低,速度慢,UDP占用系统资源较少,效率高,速度相对TCP快

1、为什么说TCP是面向连接的?

因为TCP协议传输数据是端到端的,即传输数据的前提需要搭建好客户端和服务端连接的管道(socket管道),而这些都是也都保证了TCP传输的可靠安全。TCP建立连接管道的前提需要经历"三次握手",三次握手的目的也是为客户端和服务端建立可靠的通信管道,结束数据传输后,客户端和服务端断开连接也需要四次挥手的过程,保证数据完整传输到客户端。而UDP传输数据不止是一对一的,还有一对多,多对多式的传输数据,UDP把数据包装给IP层,通过定位目标IP从而完成传输。

2、TCP基于流,而UDP基于报文传输

TCP基于流传输以字节为传输单位,应用层和传输层之间的数据交互在TCP看来是没有边界的,看成是一连串的字节流,而TCP收到这些数据流会根据数据的大小会进行拆分和储存操作,先把数据放到缓存区,如果数据比较大会先把数据进行分片即进行数据拆分放到TCP缓存区,在合适的时间发送这些数据包,如果数据太小则会先放到TCP缓存区,等到数据包足够大,则会在合适的时间传输这包数据。

这也看出TCP对于数据的处理是没有边界的,可以对数据包进行操作,而应用层和UDP交互的数据,UDP不会对数据进行任何操作,应用层发给UDP一坨数据,他就会发一坨,并不会拖延和放进缓存区,看出UDP保留了报文边界,其实也不用担心应用程序给UDP的数据会超级大,应用层在给UDP数据前会先拆分成合适的数据包给UDP,即便UDP收到的数据包还是会比较大,UDP把数据封装给IP的时候IP也会帮他拆分,从而完成数据传输。

3、TCP数据传输具有可靠性;

这也得益于TCP具有滑动窗口机制,今天下午吃饭时候猛然看到某大牛的在字节的面经,其中一面中面试官的提问就让解释滑动窗口的机制和流量控制,这些专业名词都没听说过。解释一下滑动窗口吧:

滑动窗口原理:

接受端和发送端建立通道后双方都准备完毕,并规定发送数据的大小及发送数据分片的数量

(保证数据传输的安全性),此时窗口建立,发送端的叫做发送窗口,接收端的叫接收窗口。

此时的状态为发送端已经把数据传输给接收端,接收端还未给发送端传达ACK确认 ,接收窗口和滑动窗口都固定在这,发送端会设置一个定时器,如果发送的数据包因为某种原因丢包了接收端没收到,或者接收端给发送端发出的ACK确认因为某种原因丢了,发送端没收到,此时当发送端设置的定时器时间超时,则会重新发送没有收到确认的数据包(如图,比如数据包为0、1、2 接收端收到了0号数据包也成功发送的确认消息,发送端也收到了0号数据包的确认收到消息,发送端的0号会变成红色已确认状态,而1、2号数据包则会根据定时器判断是否超时重新发送)。接收端0号数据包发送确认消息后会移动窗口,即新的窗口为1、2、3,发送端在接收到确认消息后也会移动窗口新的窗口为1、2、3。

此时发送端也会根据定时器判断是否重发1、2号数据包。

接收端发送确认收到消息后会移动窗口(窗口新位置:3、4、5),发送端在接收到确认收到的消息后也会移动窗口,注意接收端会先移动窗口,因为他在发送ACK确认后就立即移动,而确认消息传输到发送端需要时间,接收窗口必须有空闲位置才能接收数据,如果没有空闲位置则会丢弃(接收端接收数据还是比较严格的)这其中也有失序重排和重复数据的处理。

UDP传输数据具有不可靠性:

原因是TCP所执行的一系列操作UDP都没有,包括TCP的滑动窗口机制和流量控制(流量控制后期补充),且传输数据的方式也不是一对一的,UDP只管数据的发送,并不管你接收端数据是否接收到。目前网络通信发展迅速,UDP也不是那么不安全了,传输速度完全取决于网速的快慢。

4、TCP占用系统资源多,效率低,速度慢。

由于TCP要保证数据传输的安全性,会调用滑动窗口机制等一系列操作,需要调用的系统资源较多,所以他的效率就会低,速度也就变慢,而UDP没有TCP保证数据安全的操作,占用系统的资源就少,效率高,传输速度也就快了。

tcp三次握手过程


第一次握手: 客户端将标志位SYN置为1,随机产生一个值序列号seq=x,并将该数据包发送给服务端,客户端 进入SYN_SEND状态,等待服务端确认。

第二次握手: 服务端收到数据包后由标志位SYN=1知道客户端请求建立连接,服务端将标志位SYN和 ACK都置为1,然后ack=x+1,随机产生一个值seq=y,并将该数据包发送给客户端以确认连接请求,即SYN+ACK包,此时服务器进入SYN_RECV状态。

第三次握手: 客户端收到确认后检查,如果正确则将标志位ACK为1,ack=y+1,并将该数据包发送给服务端,此包发送完毕,服务端进行检查如果正确则连接建立成功,客户端和服务器进入ESTABLISHED状态,完成三次握手,随后客户端和服务端之间可以开始传输 数据了。

握手过程中传送的包里不包含数据,三次握手完毕后,客户端与服务器才正式开始传送数据,理想状态下,TCP连接一旦建立,在通信双方中的任何一方主动关闭连接之前,TCP 连接都将被一直保持下去。

tcp四次挥手过程


第一次挥手:客户端(主动关闭方)发送一个FIN,用来关闭客户端到服务端的数据传送,客户端进入FIN_WAIT_1状态。

第二次挥手:服务端收到FIN后,发送一个ACK给客户端,确认序号为收到序号+1,服务端进入Close_WAIT状态。此时TCP连接处于半关闭状态,即客户端已经没有要发送的数据了,但服务端若发送数据,则客户端仍要接收。

第三次挥手:服务端发送一个FIN,用来关闭服务端到客户端的数据传送,服务端进入LAST_ACK状态。

第四次挥手:客户端收到FIN后,客户端进入Time_WAIT状态,接着发送一个ACK给服务端,确认后,服务端进入Closed状态,完成四次挥手。

xss与csrf的原理与怎么防范


XSS攻击和CSRF攻击是网络安全领域比较常见的攻击方式,而且这两种攻击方式从名字上来看,同为跨站攻击:XSS攻击为跨站脚本攻击、CSRF攻击为跨站请求伪造,那么XSS攻击和CSRF攻击有什么区别?以下是详细的内容介绍。

CSRF攻击基本概念及防范方法

一、基本概念

CSRF,英文全称Cross-site request forgery,跨站请求伪造。也被称为“One Click Attack”或者“Session Riding”,通常缩写为CSRF或者XSRF,是一种对网站的恶意利用。

二、防范方法

方法1:Token验证,用的比较多:①服务器发送给客户端一个token;②客户端提交的表单中带着这个token;③如果这个token不合法,那么服务器拒绝这个请求。

方法2:隐藏令牌:将token隐藏在http的head头中,方法二和方法一有点像,本质上没有太大区别,只是使用方式上有区别。

方法3:Referer验证:Referer指的是网页请求来源,意思是,只接受本站的请求,服务器才做响应;如果不是,就拦截。

XSS攻击的基本概念及防范方法

一、基本概念

XSS,全称Cross Site Scripting,跨站脚本攻击。XSS攻击的核心原理是:不需要你做任何的登录认证,它会通过合法的操作,向你的页面注入脚本。

最后导致的结果可能是:盗用cookie破坏页面的正常结构,插入广告等恶意内容D-doss攻击。

二、防范方法

方法1:编码:对用户的输入进行HTML Entity编码。

方法2:过滤:移除用户输入的和事件相关的属性。如onerror可以自动触发攻击,还有onclick等。(总而言之,过滤掉一些不安全的内容)移除用户输入的style节点、script节点、Iframe节点。

方法3:校正:避免直接对HTML Entity进行解码。使用DOM parse转换,校正不配对的DOM标签。

DOM parse,这个概念,它的作用是把文本解析成DOM结构。

比较常用的做法是,通过第一步的编码转成文本,然后第三步转成DOM对象,然后经过第二步的过滤。

还有一种简洁的答案:首先是encode,如果是富文本,就白名单。

XSS攻击和CSRF攻击有什么区别?

区别一:CSRF需要用户先登录网站A,获取cookie,XSS不需要登录。

区别二:CSRF是利用网站A本身的漏洞,去请求网站A的api,XSS是向网站A注入js代码,然后执行js里的代码,篡改网站A的内容。

mysql与 MongoDB的区别


w3c事件与IE事件的区别


w3c事件流:

从根文档(html)开始遍历所有子节点,如果目标事件的父节点设置为捕获时触发,则执行该事件,直到目标被执行,然后再事件冒泡(设置为捕获时触发的事件不再被执行)。

ie事件流:

从目标事件被执行,然后再冒泡父节点的事件,直到根文档

gulp与webpack区别


1.gulp是工具链、构建工具。可以配合各种插件做js压缩,css压缩,less编译等,可以替代手工实现自动化工作。

而webpack是文件打包工具,可以把项目的各种js文件、css文件等打包合并成一个或多个文件,主要用于模块化方案,预编译模块的方案。

2.在定义和使用类比中两者都有各的用途,同时webpack为初级编译程序,gulp为高级编译程序,在功能上要比webpack应用程序中多。

3.webpack可以很方便使用node_module、es6或者样式注入等功能,作为最初级的功能定位性价比最高,webpack输入输出都以js为主,对html兼顾较少,可用组件不多很难达到可用的程度。

gulp在编程方面较为复杂,但是可用的组件也会更多,手动编译的情况下耗时较长,同时此软件不适合初级入门者使用。

webpack底层实现原理


1.新建一个空文件夹demo1

在该路径下cmd窗口打开,输入npm init初始化一个项目

此时文件夹中会生成一个package.json文件,备注:此时项目中只有这一个文件。

2.安装依赖npm install loadash --save

备注:--save是为了项目帮助记住依赖信息,从npm 5以上的版本会默认加上这个参数,所以不写--save也没关系。

npm install jquery --save-dev

备注:dependencies一般对应生产环境的依赖,devDependencies是开发环境的依赖,一般用于装一些开发辅助(如eslint)依赖。不添加任何参数(npm install)这两种包都会安装到node_modules中,npm install --only=dev/npm install --only=prod:只安装开发环境/生产环境的包,加快安装的速度。

3.cmd窗口执行webpack执行打包,若报错,可能是webpack-cli没装,执行下npm install webpack-cli -g

备注:webpack打包的默认打包入口就是src文件下的index.js,默认出口文件就是dist mian.js

4.自定义配置

新建一个webpack.config.js,自定义打包的入口文件为app.js,出口文件为dist文件夹下面的bundle.js

entry:工程资源的入口,它可以是一个文件也可以是多个文件

output:打包结果的配置,path有一个要求(必须是绝对路径)

5.安装webpack-dev-server

webpack-dev-server打包的特殊之处,它不会生成一个打包后的文件,但页面又可以正常访问,就好像是把文件放在了一个内存当中。

额外拓展loader

loader是文件加载器

webpack项目可以在js文件中import引入css或png文件,原因是因为loader的存在,loader的使用需要先安装,npm install css-loader --save-dev,loader的引入在每一个webpack版本中都不太一样。为了使样式生效也要安装:npm install style-loader --save-dev,必须是css-loader在style-loader之后。

gulp底层实现原理


gulp是基于Nodejs的自动任务运行器。

她能自动化地完成 javascript、sass、less、html/image、css 等文件的测试、检查、合并、压缩、格式化、浏览器自动刷新、部署文件生成,并监听文件在改动后重复指定的这些步骤。

她借鉴了Unix操作系统的管道(pipe)思想,前一级的输出,直接变成后一级的输入,使得在操作上非常简单。

gulp主要用到node中的两个模块,fileSystem和stream,文件系统可以对文件进行操作,比如读取文件、写入文件、创建文件、修改文件名、删除文件等;而数据流主要分为读取流、写入流和双工流;这里还有一个管道pipe的概念,管道可以使文件对象从一个流安全的流入到另一个流。

gulp就是先创建一个读取流、一个写入流,在创建若干个双工流,对双工流进行数据的处理,比如去空字符,然后通过管道将读取流、不同功能的双工流、写入流依次连接起来,从而实现对文件的操作处理。

清除浮动有哪几种方式,分别说说


(1)使用clear:both清除浮动

在代码中在放一个空的div标签,然后给这个标签设置clear:both来清除浮动对页面的影响。它的优点是简单,方便兼容性好,但是一般情况下不建议使用该方法,因为会造成结构混乱,不利于后期维护

<divstyle="clear: both"></div>

(2)利用伪元素clearfix来清除浮动

给父级元素添加了一个:after伪元素,通过清除伪元素的浮动,达到撑起父元素高度的目的

.clearfix:after{

content:"";

display:block;

visibility:hidden;

clear:both;

}

(3)overflow方法的使用

当给父元素设置了overflow样式,不管是overflow:hidden或overflow:auto都可以清除浮动只要它的值不为visible就可以了,它的本质就是建构了一个BFC,这样使得达到撑起父元素高度的效果

.box{border:1px solid #ccc;background:#eff2f4;overflow: auto}

(4)双伪元素方法的使用

通过给父元素设置双伪元素来达到清除浮动的效果

.clearfix:before,.clearfix:after {

content: "";

display: block;

clear: both;

}

h5有个api能定位你知道是哪个吗?


H5中有一个API可以直接获取我们的位置

navigator.geolocation

<script>

navigator.geolocation.getCurrentPosition(position=>{

console.log(position);

})

</script>

mvc与mvvc


MVC(Model-View-Controller)

M-模型-数据处理-其实就是数据

V-视图-其实就是用户界面

C-控制器-其实就是用户逻辑

MVVC(Model-View-ViewModel):实现的方式是DOM的事件监听

模型M 指的是后端传递的数据

视图V 指的是所有看到的页面

视图模型 mvvc模式的核心,它是连接view和model的桥梁

window.onload和$(document).ready()的区别,浏览器加载转圈结束时哪个时间点?


1、执行时间上的区别:window.onload必须等到页面内(包括图片的)所有元素加载到浏览器中后才能执行。而$(document).ready(function(){})是DOM结构加载完毕后就会执行。

2、编写个数不同:window.onload不能同时写多个,如果有多个window.onload,则只有最后一个会执行,它会把前面的都覆盖掉。$(document).ready(function(){})则不同,它可以编写多个,并且每一个都会执行。

3、简写方法:window.onload没有简写的方法,$(document).ready(function(){})可以简写为$(function(){})。

另外:由于在$(document).ready()方法内注册的事件,只要DOM就绪就会被执行,因此可能此时元素的关联文件未下载完,例如与图片有关的HTML下载完毕,并且已经解析为DOM树了,但很有可能图片还未加载完毕,所以例如图片的高度和宽度这样的属性此时不一定有效。

要解决这个问题,可以使用JQuery中另一个关于页面加载的方法---load()方法。load()方法会在元素的onload事件中绑定一个处理函数。如果处理函数绑定在元素上,则会在元素的内容加载完毕后触发。如:$(window).load(function(){})=====window.onload = function(){}...

setTimeout和setInterval区别,如何互相实现?


setTimeout() 定义和用法:

定义:

setTimeout()方法用于在指定毫秒数后再调用函数或者计算表达式(以毫秒为单位)

语法:

setTimeout(code,millisec)

code:必需,要调用的函数后要执行的 JavaScript 代码串;millisec:必需,在执行代码前需等待的毫秒数。

setTimeout() 只执行函数一次,如果需要多次调用可以使用 setInterval(),或者在函数体内再次调用setTimeout()

示例代码:延迟1秒弹出 Hello

// 延迟1秒弹出 Hello setTimeout(function(){ alert("Hello"); }, 1000);

setInterval() 定义和用法:

定义:

setInterval() 方法用于按照指定的周期(以毫秒计)来循环调用函数或计算表达式,直到 clearInterval() 被调用或窗口关闭,由 setInterval() 返回的 ID 值可用作 clearInterval() 方法的参数。

语法:

setInterval(code,millisec[,"lang"])

code:必需,要调用的函数或要执行的JavaScript 代码串;millisec:必须,周期性执行或调用 code 之间的时间间隔,以毫秒计。

setInterval() 会不停的调用函数,直到clearInterval() 被调用或者窗口被关闭,由 setInterval() 返回的ID值可用作 clearInterval() 方法的参数。

示例代码:一直显示当前时间,点击停止不继续

<p>显示当前时间:</p> <p id="demo"></p> <button οnclick="myStopFunction()">停止时间</button> <!-- 一直显示当前时间,点击停止不继续 --> <script> var myVar = setInterval(function(){ myTimer() }, 1000); function myTimer() { var d = new Date(); var t = d.toLocaleTimeString(); document.getElementById("demo").innerHTML = t; } function myStopFunction() { clearInterval(myVar); } </script>

setTimeout() 方法只运行一次,也就是说当达到设定的时间后就开始运行指定的代码,运行完后就结束了,次数是一次。

setInterval() 是循环执行的,即每达到指定的时间间隔就执行相应的函数或者表达式,只要窗口不关闭或 clearInterval() 调用就会无限循环下去。

伪类和伪元素区别


伪元素其实相当于伪造了一个元素,例如before,first-letter达到的效果就是伪造了一个元素,然后添加了其相应的效果而已;而伪类没有伪造元素,例如first-child只是给子元素添加样式而已。

伪元素和伪类之所以这么容易混淆,是因为他们的效果类似而且写法相仿,但实际上 css3 为了区分两者,已经明确规定了伪类用一个冒号来表示,而伪元素则用两个冒号来表示。

数组去重


1.第一种方式就是最简单的set去重(o(n))

var arr = [1,2,2,4,3,4,1,3,2,7,5,6,1]

var newArr = new Set(arr)

2.第二种方式就是用indexOf来去重(o(n^3))

判断新数组中某个数字是否存在

function fn(arr){ let newArr = [] arr.forEach((val)=>{ if(newArr.indexOf(val) == -1){ newArr.push(val) } }) return newArr }

function fn(arr) {

  return arr.filter((item, index, arr) => arr.indexOf(item) === index)

}

3.第三种方式普通去重(o(n^3))

for(var i=0;i<arr.length;i++){ for(var j=i+1;j<arr.length;j++){ if(arr[i]==arr[j]){ arr.splice(j,1) } } }

4.键值去重(o(n^3))

根据键值是唯一的来去重

function fn(arr){

let arr1 = [],

arr2 = []

arr.forEach((val)=>{

arr1[val]=val

})

//arr1数组会存在空的情况

//所以返回arr2

arr1.forEach((val)=>{

if(!!val){

arr2.push(val)

}

})

return arr2

}

5.sort排序后去重(o(n^2))

只比set复杂度低

function fn(arr){ let newArr = [] arr.sort((a,b)=>{ return a-b }) arr.forEach((val,index)=>{ if(val != arr[index+1]){ newArr.push(val) } }) return newArr }

vue2和vue3区别


vue2 的双向数据绑定是利用ES5 的一个 API Object.definePropert()对数据进行劫持 结合 发布订阅模式的方式来实现的。

vue3 中使用了 es6 的 ProxyAPI 对数据代理。

相比于vue2.x,使用proxy的优势如下

defineProperty只能监听某个属性,不能对全对象监听

可以省去for in、闭包等内容来提升效率(直接绑定整个对象即可)

可以监听数组,不用再去单独的对数组做特异性操作 vue3.x可以检测到数组内部数据的变化

二. Vue3支持碎片(Fragments)

就是说在组件可以拥有多个根节点。

vue2

<template>

<div class='form-element'>

<h2> {{ title }} </h2>

</div>

</template>

vue3

<template>

<div class='form-element'>

</div>

<h2> {{ title }} </h2>

</template>

三. Composition API

Vue2与Vue3 最大的区别 — Vue2使用选项类型API(Options API)对比Vue3合成型API(Composition API)

旧的选项型API在代码里分割了不同的属性: data,computed属性,methods,等等。新的合成型API能让我们用方法(function)来分割,相比于旧的API使用属性来分组,这样代码会更加简便和整洁。

vue2

export default {

props: {

title: String

},

data () {

return {

username: '',

password: ''

}

},

methods: {

login () {

// 登陆方法

}

},

components:{

"buttonComponent":btnComponent

},

computed:{

fullName(){

return this.firstName+" "+this.lastName;

}

}

}

vue3

export default {

props: {

title: String

},

setup () {

const state = reactive({ //数据

username: '',

password: '',

lowerCaseUsername: computed(() => state.username.toLowerCase()) //计算属性

})

//方法

const login = () => {

// 登陆方法

}

return {

login,

state

}

}

}

四. 建立数据 data

Vue2 - 这里把数据放入data属性中

export default {

props: {

title: String

},

data () {

return {

username: '',

password: ''

}

}

}

在Vue3.0,我们就需要使用一个新的setup()方法,此方法在组件初始化构造的时候触发。

使用以下三步来建立反应性数据:

从vue引入reactive

使用reactive()方法来声名我们的数据为响应性数据

使用setup()方法来返回我们的响应性数据,从而我们的template可以获取这些响应性数据

import { reactive } from 'vue'

export default {

props: {

title: String

},

setup () {

const state = reactive({

username: '',

password: ''

})

return { state }

}

}

template使用,可以通过state.username和state.password获得数据的值。

<template>

<div>

<h2> {{ state.username }} </h2>

</div>

</template>

五. 生命周期钩子 — Lifecyle Hooks

Vue2--------------vue3

beforeCreate -> setup()

created -> setup()

beforeMount -> onBeforeMount

mounted -> onMounted

beforeUpdate -> onBeforeUpdate

updated -> onUpdated

beforeDestroy -> onBeforeUnmount

destroyed -> onUnmounted

activated -> onActivated

deactivated -> onDeactivated

setup() :开始创建组件之前,在beforeCreate和created之前执行。创建的是data和method

onBeforeMount() : 组件挂载到节点上之前执行的函数。

onMounted() : 组件挂载完成后执行的函数。

onBeforeUpdate(): 组件更新之前执行的函数。

onUpdated(): 组件更新完成之后执行的函数。

onBeforeUnmount(): 组件卸载之前执行的函数。

onUnmounted(): 组件卸载完成后执行的函数

若组件被<keep-alive>包含,则多出下面两个钩子函数。

onActivated(): 被包含在中的组件,会多出两个生命周期钩子函数。被激活时执行 。

onDeactivated(): 比如从 A组件,切换到 B 组件,A 组件消失时执行。

六.父子传参不同,setup() 函数特性

总结:

1、setup 函数时,它将接受两个参数:(props、context(包含attrs、slots、emit))

2、setup函数是处于 生命周期函数 beforeCreate 和 Created 两个钩子函数之前的函数

3、执行 setup 时,组件实例尚未被创建(在 setup() 内部,this 不会是该活跃实例的引用,即不指向vue实例,Vue 为了避免我们错误的使用,直接将 setup函数中的this修改成了 undefined)

4、与模板一起使用:需要返回一个对象 (在setup函数中定义的变量和方法最后都是需要 return 出去的 不然无法再模板中使用)

5、使用渲染函数:可以返回一个渲染函数,该函数可以直接使用在同一作用域中声明的响应式状态

注意事项:

1、setup函数中不能使用this。Vue 为了避免我们错误的使用,直接将 setup函数中的this修改成了 undefined)

2、setup 函数中的 props 是响应式的,当传入新的 prop 时,它将被更新。但是,因为 props 是响应式的,你不能使用 ES6 解构,因为它会消除 prop 的响应性。

如果需要解构 prop,可以通过使用 setup 函数中的toRefs 来完成此操作:

父传子,props

import { toRefs } from 'vue'

setup(props) {

const { title } = toRefs(props)

console.log(title.value)

onMounted(() => {

console.log('title: ' + props.title)

})

}

子传父,事件 - Emitting Events

举例,现在我们想在点击提交按钮时触发一个login的事件。

在 Vue2 中我们会调用到this.$emit然后传入事件名和参数对象。

login () {

this.$emit('login', {

username: this.username,

password: this.password

})

}

在setup()中的第二个参数content对象中就有emit,这个是和this.$emit是一样的。那么我们只要在setup()接收第二个参数中使用分解对象法取出emit就可以在setup方法中随意使用了。

然后我们在login方法中编写登陆事件

另外:context 是一个普通的 JavaScript 对象,也就是说,它不是响应式的,这意味着你可以安全地对 context 使用 ES6 解构

setup (props, { attrs, slots, emit }) {

// ...

const login = () => {

emit('login', {

username: state.username,

password: state.password

})

}

// ...

}

3、 setup()内使用响应式数据时,需要通过.value获取

import { ref } from 'vue'

const count = ref(0)

console.log(count.value) // 0

4、从 setup() 中返回的对象上的 property 返回并可以在模板中被访问时,它将自动展开为内部值。不需要在模板中追加 .value

5、setup函数只能是同步的不能是异步的

七. vue3 Teleport瞬移组件

Teleport一般被翻译成瞬间移动组件,实际上是不好理解的.我把他理解成"独立组件",

他可以那你写的组件挂载到任何你想挂载的DOM上,所以是很自由很独立的

以一个例子来看:编写一个弹窗组件

<template>

<teleport to="#modal">

<div id="center" v-if="isOpen">

<h2><slot>this is a modal</slot></h2>

<button @click="buttonClick">Close</button>

</div>

</teleport>

</template>

<script lang="ts">

export default {

props: {

isOpen: Boolean,

},

emits: {

'close-modal': null

},

setup(props, context) {

const buttonClick = () => {

context.emit('close-modal')

}

return {

buttonClick

}

}

}

</script>

<style>

#center {

width: 200px;

height: 200px;

border: 2px solid black;

background: white;

position: fixed;

left: 50%;

top: 50%;

margin-left: -100px;

margin-top: -100px;

}

</style>

在app.vue中使用的时候跟普通组件调用是一样的

<template>

<div id="app">

<img alt="Vue logo" src="./assets/logo.png">

<HelloWorld msg="Welcome to Your Vue.js App"/>

<HooksDemo></HooksDemo>

<button @click="openModal">Open Modal</button><br/>

<modal :isOpen="modalIsOpen" @close-modal="onModalClose"> My Modal !!!!</modal>

</div>

</template>

<script>

import HelloWorld from './components/HelloWorld.vue'

import HooksDemo from './components/HooksDemo.vue'

import Modal from './components/Modal.vue'

import{ref} from 'vue'

export default {

name: 'App',

components: {

HelloWorld,

HooksDemo,

Modal

},

setup() {

const modalIsOpen = ref(false)

const openModal = () => {

modalIsOpen.value = true

}

const onModalClose = () => {

modalIsOpen.value = false

}

return {

modalIsOpen,

openModal,

onModalClose

}

}

}

</script>

<style>

#app {

font-family: Avenir, Helvetica, Arial, sans-serif;

-webkit-font-smoothing: antialiased;

-moz-osx-font-smoothing: grayscale;

text-align: center;

color: #2c3e50;

margin-top: 60px;

}

</style>

要是在app.vue文件中使用的时候,modal是在app的 DOM节点之下的,父节点的dom结构和css都会给modal产生影响

于是产生的问题

modal被包裹在其它组件之中,容易被干扰

样式也在其它组件中,容易变得非常混乱

Teleport 可以把modal组件渲染到任意你想渲染的外部Dom上,不必嵌套在#app中,这样就可以互不干扰了,可以把Teleport看成一个传送门,把你的组件传送到任何地方

使用的时候 to属性可以确定想要挂载的DOM节点下面

<template>

<teleport to="#modal">

<div id="center">

<h2>柏特better</h2>

</div>

</teleport>

</template>

在public文件夹下的index.html中增加一个节点

<!DOCTYPE html>

<html lang="en">

<head>

<meta charset="utf-8">

<meta http-equiv="X-UA-Compatible" content="IE=edge">

<meta name="viewport" content="width=device-width,initial-scale=1.0">

<link rel="icon" href="<%= BASE_URL %>favicon.ico">

<title><%= htmlWebpackPlugin.options.title %></title>

</head>

<body>

<noscript>

<strong>We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>

</noscript>

<div id="app"></div>

<div id="modal"></div>

<!-- built files will be auto injected -->

</body>

</html>

mvvm和mvp和mvc


mvc:经典模式

MVC全名为Model View Controller ,是模型(Model)- 视图(View)- 控制器(Controller)的缩写。他是1970年代被引入到软件设计大众的。MVC模式致力于关注点的切分,这意味着model和controller的逻辑是不与用户界面(View)挂钩的。因此,维护和测试程序变得更加简单容易。

[图片上传失败...(image-98e8bd-1630660921001)]

  • Model层:模型(用于封装业务逻辑相关的数据以及对数据的操纵)

  • View层:视图(渲染图形化界面,也就是所谓的UI界面)

  • Controller层:控制器(M和V之间的连接器,主要处理业务逻辑,包括显示数据,界面跳转,管理页面生命周期等)

**标准MVC工作模式: **当有用户的行为触发操作时,控制器(Controller)更新模型,并通知视图(V)和模型(M)更新,这时视图(V)就会向模型(M)请求新的数据,这就是标准MVC模式下Model,View 和 Controller 之间的协作方式。

MVC优点:

  1. 耦合性低,视图层和业务层分离,这样就允许更改视图层代码而不用重新编译模型和控制器代码;

  1. 重用性高;

  1. 生命周期成本低;

  1. MVC使开发和维护用户接口的技术含量降低;

  1. 可维护性高,分离视图层和业务逻辑层也使得WEB应用更易于维护和修改;

  1. 部署快。

MVC缺点:

  1. 不适合小型,中等规模的应用程序,花费大量时间将MVC应用到规模并不是很大的应用程序通常会得不偿失。

  1. 视图与控制器间过于紧密连接,视图与控制器是相互分离,但却是联系紧密的部件,视图没有控制器的存在,其应用是很有限的,反之亦然,这样就妨碍了他们的独立重用。

  1. 视图对模型数据的低效率访问,依据模型操作接口的不同,视图可能需要多次调用才能获得足够的显示数据。对未变化数据的不必要的频繁访问,也将损害操作性能。

MVP:MVP 模式将 Controller 改名为 Presenter,同时改变了通信方向。

MVP全名为Model View Presenter ,是由MVC演变而来,它和MVC的相同之处在于:Controller / Presente都是负责业务逻辑,Model管理数据,View负责显示。不过在MVP中View并不直接与Model交互,它们之间的通信是通过Presenter (MVC中的Controller)来进行的,即使用 Presenter 对视图和模型进行了解耦,让它们彼此都对对方一无所知,沟通都通过 Presenter 进行。

[图片上传失败...(image-ef95e0-1630660921001)]

  • Model层:模型(用于封装业务逻辑相关的数据以及对数据的操纵)

  • View层:视图(渲染图形化界面,也就是所谓的UI界面)

  • Presenter层:控制器(M和V之间的连接器,主要处理业务逻辑,包括显示数据,界面跳转,管理页面生命周期等)

**标准MVP工作模式: **在 MVP 中,Presenter 可以理解为松散的控制器,其中包含了视图的 UI 业务逻辑,所有从视图发出的事件,都会通过代理给 Presenter 进行处理;同时,Presenter 也通过视图暴露的接口与其进行通信。

MVP特点:

  1. M、V、P之间双向通信。

  1. View 与 Model 不通信,都通过 Presenter 传递。Presenter完全把Model和View进行了分离,主要的程序逻辑在Presenter里实现。

  1. View 非常薄,不部署任何业务逻辑,称为”被动视图”(Passive View),即没有任何主动性,而 Presenter非常厚,所有逻辑都部署在那里。

  1. Presenter与具体的View是没有直接关联的,而是通过定义好的接口进行交互,从而使得在变更View时候可以保持Presenter的不变,这样就可以重用。不仅如此,还可以编写测试用的View,模拟用户的各种操作,从而实现对Presenter的测试–从而不需要使用自动化的测试工具。

MVP优点:

  1. 模型与视图完全分离,我们可以修改视图而不影响模型;

  1. 可以更高效地使用模型,因为所有的交互都发生在一个地方——Presenter内部;

  1. 我们可以将一个Presenter用于多个视图,而不需要改变Presenter的逻辑。这个特性非常的有用,因为视图的变化总是比模型的变化频繁;

  1. 如果我们把逻辑放在Presenter中,那么我们就可以脱离用户接口来测试这些逻辑(单元测试)。

MVP缺点:视图和Presenter的交互会过于频繁,使得他们的联系过于紧密。也就是说,一旦视图变更了,presenter也要变更。

MVVM

MVVM全名为Model View ViewModel。这个模式提供对View和View Model的双向数据绑定。这使得View Model的状态改变可以自动传递给View。典型的情况是,View Model通过使用obsever模式(观察者模式)来将View Model的变化通知给model。

[图片上传失败...(image-dc3ca7-1630660921001)]

  • Model层:Model层代表了描述业务逻辑和数据的一系列类的集合。它也定义了数据修改和操作的业务规则。

  • View层:View代表了UI组件,像CSS,JQuery,html等。他只负责展示从Presenter接收到的数据。也就是把模型转化成UI。

  • View Model层:View Model负责暴漏方法,命令,其他属性来操作VIew的状态,组装model作为View动作的结果,并且触发view自己的事件。

MVVM模式关键点:

  1. 用户和View交互。

  1. View和ViewModel是多对一关系。意味着一个ViewModel只映射多个View。

  1. View持有ViewModel的引用,但是ViewModel没有任何View的信息。

  1. View 和ViewModel之间有双向数据绑定关系。

MVVM优点:

  1. 低耦合,视图(View)可以独立于Model变化和修改,一个ViewModel可以绑定到不同的”View”上,当View变化的时候Model可以不变,当Model变化的时候View也可以不变。

  1. 可重用性,可以把一些视图逻辑放在一个ViewModel里面,让很多view重用这段视图逻辑。

  1. 独立开发,开发人员可以专注于业务逻辑和数据的开发(ViewModel),设计人员可以专注于页面设计,使用Expression Blend可以很容易设计界面并生成xml代码。

  1. 可测试,界面向来是比较难于测试的,而现在测试可以针对ViewModel来写。

三种模式的区别

MVC 是世界上最低级、最原始的 UI 模式;MVC 就是在 V 上绑定 M,然后 C 负责处理界面整个提交请求,并且一遍遍地刷新整个 V。这种机制。所以 MVC 的标志是“初级、单向绑定、一遍遍刷新UI”。

MVP 则是深入到程序的“骨髓”,UI设计模板与 MVP 事件定义绑定,让程序员可以捕获这么一个组件的丰富的事件,然后在事件处理过程中又去从控件树上去直接访问其它所有控件,直接修改其属性。开发的精力很大程度上用在学习各种控件的内部机制上,学习曲线陡峭。所以MVP的标志是“复杂、事件驱动、精细到每一个控件层次”。

MVVM 则是在 MVP 上的改进,它隔离了控件操作层,UI 模板上各种控件直接跟 VM 层的属性绑定,使得 VM 属性改变时自动更新 UI 控件,反之 UI 控件的值改变时又自动更新 VM 属性。这样编程的方式就不是去一大堆控件事件处理,而是写少量的 VM 属性更改行为代码。开发精力绝大部分都放在业务与UI的绑定上,而并不需要研究控件内部机制。

三种设计模式的应用

MVC模式。

React使用的是MVC模式。所有MVC框架都是单向数据流的。

特色:

  • 使用Virtual DOM

  • 提供了响应式(Reactive) 和组件化 (Composable) 的视图组件。

  • 将注意力集中保持在核心库,而将其他功能如路由和全局状态管理交给相关的库。

注:react 实际上是一个“伪MVC”,它其实是 MVP 的,但是它深知 MVP 模式的弊病,它明明是基于组件并且绑定了组件的 change 事件的,但是它有使用虚拟DOM的方式来一遍遍刷新UI控件(并且为了解决性能问题,有各种负责和诡异的避免全局刷新UI树的反模式操作)。所以虽然 React 自称为 MVC模式,但是实际上它是 MVP 的变种。

MVP模式。

JQuery 是非常经典的 MVP 编程模式

MVVM模式。

Knockout、AngularJS、Vue 等可以看作是 MVVM 模式

Angular使用的MVVM模式。当触发UI事件,ajax请求或者 timeout 延迟,会触发脏检查。这时会调用

watch函数,最后把所有的变化全部更新,调用apply()方法把新的数据渲染到页面上。

优点:一次检测会收集所有的数据变化,然后统一更新UI,大大减少了操作 DOM 的次数。

缺点:只要有ui或ajax或settimeout操作时就会进行检查,且当watcher之间相互影响的时候,更会触发多次$digest循环,这样watcher一多,就会很影响性能。

注:AngularJS 其实在 MVVM 上做的不是很好,倾向于 MVP。只有 Knockout 是实现了经典的 MVVM 设计模式,而且有几个性能相关的特性(例如自动延迟 UI 刷新、自动抽稀无用的 UI 刷新操作)可以将性能提高(相对于其它许多 web 前端框架)至少几十倍。

Vue一定意义上算是React和Angular的集大成者。它吸取了MVVM的数据管理思想,同时应用了React的virtual Dom算法。它使用了双向数据绑定来满足开发的便捷,但是它不同组件之间又使用单向数据流,来保证数据的可控性。它使用了很多Angular的指令语法,但是它革新了Angular的脏数据检查机制,使用数据劫持的方法来触法数据检查机制。它借鉴了React的组件化思想,大大增加了前端工程的结构规范化。

注:Vue 内部使用了 Object.defineProperty() 来实现双向绑定,通过这个函数可以监听到 set 和 get 的事件。

总结

MVC 只是把控件跟 M 绑定,一遍遍刷新 UI。而 MVP 则是把控件跟事件单向绑定,它的假设是程序员最爱写低级的代码来操作控件。而 MVVM 则是把控件跟 VM 双向绑定,它的假设是交互界面设计时最爱写高层次一些的声明来操作用户业务行为上的变化。

es6和html5和css3


1、更加语义化的元素。 article、footer、header、nav、section

2、本地化储存。 localStorage 和 sessionStorage (强缓存与协商缓存)

3、离线应用,离线缓存。 manifest

4、拖曳以及释放的api。 Drag and drop

5、媒体播放。 video 和 audio

6、增强表单控件。 calendar、date、time、email、url、search

7、地理位置。 Geolocation

8、多任务。 webworker

普通的 Worker 可以在需要大量计算的时候使用,创建新的线程可以降低主线程的计算压力,不会导致 UI 卡顿。SharedWorker 主要是为不同的 window、iframes 之间共享数据提供了另外一个解决方案。ServiceWorker 可以缓存资源,提供离线服务或者是网络优化,加快 Web 应用的开启速度,更多是优化体验方面的。

9、全双工通信协议。 websocket

在 WebSocket API 中,浏览器和服务器只需要完成一次握手,两者之间就直接可以创建持久性的连接,并进行双向数据传输。

10、历史管理 history

11、跨域资源共享(CORS) Access-Control-Allow-Origin

12/页面可见性改变事件 visibilitychange

13、跨窗口通信 PostMessage

14、Form Data 对象

15、绘画:canvas

2.css3

1、盒子模型

在标准的盒模型中一个盒子的宽度是:margin(左右外边距)+padding(左右内边距)+border(左右边框)+内容的(width).

IE盒子:内容的(width)+margin(左右)(这里的内容width包含了padding(左右内边距)+border(左右边框))

两种盒子模型转换通过box-sizing

2、选择器

属性选择器

input[type=“password”] {

border: 1px solid red;

}

结构伪类选择器

input:first-child {

border: 1px solid green;

}

伪类选择器

::before ::after

3、其他属性

阴影:text-shadow box-shadow

图片模糊:filter:blur(0px)括号的值越大越模糊

背景图属性background-size

透明:opacity transparent

渐变:background: linear-gradient(to top right,red 0%, yellow 30px, green 100%);

4、变形transform(2D,3D)

transform-origin:x% y%;

transform:translateX,translateY,translateZ,rotateX,rotateY,scaleX,scaleY,scaleZ,skewX,skewY

5、动画

过渡动画:transition: all 1s ease 10ms;

帧动画:animation: move 2s linear infinite normal forwards 1s;

6、媒体查询

@media (min-width: 721px) and (max-width: 1440px) {

/ min-width相当于>=,max-width相当于<= /

.mediaBox {

background: green;

}

}

3、ES6

1、var let const

var let const声明方式 区别块级作用域和变量提升

2、扩展运算符…

3、super关键字

4、set map

1.Map是键值对,Set是值得集合,当然键和值可以是任何的值;

2.Map可以通过get方法获取值,而set不能因为它只有值;

3.都能通过迭代器进行for…of遍历;

4.Set的值是唯一的可以做数组去重,Map由于没有格式限制,可以做数据存储;

5、模板字符串

6、promise

let、const、var


1.var是ES5提出的,let和const是ES6提出的。

2.const声明的是常量,必须赋值

1)一旦声明必须赋值,不能使用null占位。

2)声明后不能再修改

3)如果声明的是复合类型数据,可以修改其属性

3.let和var声明的是变量,声明之后可以更改,声明时可以不赋值

4.var允许重复声明变量,后一个变量会覆盖前一个变量。let和const在同一作用域不允许重复声明变量,会报错。

5.var声明的变量存在变量提升(将变量提升到当前作用域的顶部)。即变量可以在声明之前调用,值为undefined。

let和const不存在变量提升。即它们所声明的变量一定要在声明后使用,否则报ReferenceError错。

6.var不存在块级作用域。let和const存在块级作用域。

ES5中作用域有:全局作用域、函数作用域。没有块作用域的概念。

ES6(简称ES6)中新增了块级作用域。块作用域由 { } 包括,if语句和for语句里面的{ }也属于块作用域。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

李宏伟~

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值