说一下对cookie和Storage(localStorage和sessionStorage) 的认识和区别?
将用户信息保存在浏览器中。
早期本地储存采用的是cookie的方式,但是cookie大小有限,并且前端操作不方便,所以H5给了两个新的储存方法
localStorage
:永久性本地存储,只要不手动删除或者代码清除,一直保存在浏览器中。sessionStorage
: 会话级本地存储,只有在当前窗口下才生效,窗口关闭数据清空。
一般来说用于储存后台返回的token、用户信息等常用数据。
本地存储的区别
区别 | cookie | localStorage(优选) | sessionStorage |
---|---|---|---|
数据有效期 | 默认窗口关闭前有效(可以手动设置有效期) | 永久有效 | 窗口关闭前有效 |
存储大小 | 4kb | 至少5mb | 至少5mb |
作用范围 | 同源同窗口 | 同源窗口共享 | 同源同窗口 |
与服务器通信 | 每次都会携带cookie | 不会自动携带 | 不会自动携带 |
如何实现水平、垂直居中?
弹性盒子方法
display: flex;
justify-content: center;
align-items: center;
定位方法
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
margin: auto;
盒子模型(怪异模型)
标准盒模型
上面就是一个标准盒模型:
- content:代表内容区域,存放内容,文本或者里面图片等等
- padding:内边距,盒子内部的空间,内容与边框之间的间距,相当于快递中的泡沫
- border:盒子的边框,四周边框可以分别设置。
- margin:代表外边距,盒子和盒子之间的间距(父子关系,兄弟关系)
如何怪异盒子(IE盒模型)
width和height包含了content和padding和border,目前主流的浏览器默认都是标准盒模型
- 可以通过box-sizing来转换盒模型
- content-box:标准盒模型
- border-box:怪异盒模型
怪异盒子真正的大小的计算:
- 宽度= width(包含了content+padding+border)
- 高度= height(包含了content+padding+border)
怪异盒子所占空间的大小的计算:
- 宽度=width(包含了content+padding+border)+ margin * 2(左右)
- 高度 =height(包含了content+padding+border)+ margin * 2(上下)
实现图文混排
float
如何判断数据类型
1、使用 typeof
let obj={
name:'dawn',
age:21
}
let fn=function() {
console.log('我是 function 类型');
}
console.log(typeof 1); //number
console.log(typeof 'abc'); //string
console.log(typeof true); //boolean
console.log(typeof undefined); //undefined
console.log(typeof fn); //function
console.log(typeof (new Date) ); //object
console.log(typeof null); //object
console.log(typeof [1,2,3]); //object
console.log(typeof obj); //object
由结果可知typeof可以测试出number
、string
、boolean
、undefined
及function
,
2、使用 instanceof
obj instanceof Object ,可以左边放你要判断的内容,右边放类型来进行JS类型判断,只能用来判断复杂数据类型,因为instanceof 是用于检测构造函数(右边)的 prototype
属性是否出现在某个实例对象(左边)的原型链上。
let arr=[1,2,3,4,5,6,7]
let obj={
name:'dawn',
age:21
}
let fn=function() {
console.log('我是 function 类型');
}
console.log(arr instanceof Array); //true
console.log(obj instanceof Object); //true
console.log(fn instanceof Function); //true
console.log((new Date) instanceof Date); //true
3、使用Object.prototype.toString.call
let obj = {
name: 'dawn',
age: 21
}
let fn = function () {
console.log('我是 function 类型');
}
console.log(Object.prototype.toString.call(1)); // [object Number]
console.log(Object.prototype.toString.call('Hello tomorrow')); // [object String ]
console.log(Object.prototype.toString.call(true)); // [object Boolean]
console.log(Object.prototype.toString.call(undefined)); // [object Undefined]
console.log(Object.prototype.toString.call(fn)); // [object Function]
console.log(Object.prototype.toString.call(new Date)); // [object Date]
console.log(Object.prototype.toString.call(null)); // [object Null]
console.log(Object.prototype.toString.call([1, 2, 3])); // [object Array]
console.log(Object.prototype.toString.call(obj)); // [object Object]
在任何值上调用 Object 原生的 toString() 方法,都会返回一个 [object NativeConstructorName] 格式的字符串。每个类在内部都有一个 [[Class]] 属性,这个属性中就指定了上述字符串中的构造函数名。
但是它不能检测非原生构造函数的构造函数名。
4、使用constructor
let arr = [1, 2, 3, 4, 5, 6, 7]
let obj = {
name: 'dawn',
age: 21
}
let fn = function () {
console.log('我是 function 类型');
}
console.log((9).constructor === Number); //true
console.log('hello'.constructor === String); //true
console.log(true.constructor === Boolean); //true
console.log(fn.constructor === Function); //true
console.log((new Date).constructor === Date); //true
console.log(obj.constructor === Object); //true
console.log([1, 2, 3].constructor === Array); //true
constructor不能判断undefined和null,并且使用它是不安全的,因为contructor的指向是可以改变的
在数组上新增一个方法,实现复制。期望:[1,2,3].copy() //输出 [1,2,3,1,2,3]
Array.prototype.copy = function () {
arr = this;//this指的是数组[1, 2, 3]
arr.forEach((value) => {
arr.push(value);
});
return arr
};
console.log([2, 3, 4, 5].copy());
一个页面上有大量的图片(大型电商网站),加载很慢,你有哪些方法优化这些图片的加载,给用户更好的体验。
图片懒加载,在页面上的未可视区域可以添加一个滚动条事件,判断图片位置与浏
览器顶端的距离与页面的距离,如果前者小于后者,优先加载
如果图片为 css 图片,可以使用 CSSsprite(精灵图),SVGsprite(精灵图),
Iconfont(字体图标)(精灵图,小图标…)
如果图片过大,可以使用特殊编码的图片,加载时会先加载一张压缩的特别厉害的
缩略图,以提高用户体验
如果图片展示区域小于图片的真实大小,则应该在服务器端根据业务需要先行进行
图片压缩,图片压缩后大小与展示一致
export和export default的区别
export 和 export default 都可用于导出常量、函数、文件、模块等 ,
你可以在其它文件或模块中通过 import 将其导入,以便能够对其进行使用
在一个文件或模块中,export、import可以有多个,export default仅有一个
通过export方式导出,在导入时要加{ },且不能自定义名字,export default不用加{ },且可以自定义名字
js垃圾回收机制
JS的垃圾回收机制是为了防止内存泄漏(已经不需要的某一块内存还一直存在着),垃圾回收机制就是不停歇的寻找这些不再使用的变量,并且释放掉它所指向的内存。
在JS中,JS的执行环境会负责管理代码执行过程中使用的内存。
垃圾回收机制
标记清除法
核心思想就是将整个垃圾回收操作分为两个阶段:
遍历所有的对象找到活动对象,进行标记的操作
遍历所有的对象,找到那些没有标记的对象进行清除。(注意在第二阶段中也会把第一阶段涉及的标志给抹掉,便于GC下次能够正常的工作)
通过两次的遍历行为把我们当前的垃圾空间进行回收,最终交给我们的空闲列表进行维护。
总结
核心思想:分标记和清除两个阶段:
遍历所有的对象找活动对象做标记
遍历所有的对象清除没有标记的对象
回收相应空间
优点
可以回收循环引用的对象空间。相对于引用计数算法来说:解决对象循环引用的不能回收问题。
缺点
容易产生碎片化空间,浪费空间、不能立即回收垃圾对象。
空间碎片化:所谓空间碎片化就是由于当前所回收的垃圾对象,在地址上面是不连续的,由于这种不连续造成了我们在回收之后分散在各个角落,造成后续使用的问题
引用计数
内部通过引用计数器,来维护当前对象的引用数,从而判断该对象的引用数是否为0,来决定它是否是一个垃圾对象。如果引用数值为0,GC就开始工作,将其所在的内存空间进行回收释放和再使用
引用计数器
当某一个对象的引用关系发生改变时,引用计数器就会自动的修改这个对象所对应的引用数值(比如我们代码中有一个对象空间,一个变量引用了它,那么这个对象空间的引用数值加1) 引用数值为0的时候GC就开始工作,将当前的对象空间回收
优点
可以即时回收垃圾对象、减少程序卡顿时间。
发现垃圾立即回收(因为根据当前的引用数值是否为0判断他是否是一个垃圾,如果是垃圾就回收内存空间,释放内存)
最大限度的减少程序暂停(应用程序在执行的过程中必定会对内存进行消耗,而当前的执行平台内存空间是有上限的,所以内存肯定会有占满的时候。由于引用计数算法时刻监控着那些引用数值为0的对象,当内存爆满的时候会去找那些引用数值为0的对象释放其内存,这个也就保证了当前的内存空间不会有占满的时候)
缺点
无法回收循环引用的对象、资源消耗较大
无法回收循环引用的对象
时间开销大(当前的引用计数需要去维护一个数值的变化,时刻监控当前引用数值是否修改,修改需要时间)
原型、原型链
原型
JavaScript
常被描述为一种基于原型的语言——每个对象拥有一个原型对象
当试图访问一个对象的属性时,它不仅仅在该对象上搜寻,还会搜寻该对象的原型,以及该对象的原型的原型,依次层层向上搜索,直到找到一个名字匹配的属性或到达原型链的末尾
准确地说,这些属性和方法定义在Object的构造器函数(constructor functions)之上的prototype
属性上,而非实例对象本身
原型链
原型对象也可能拥有原型,并从中继承方法和属性,一层一层、以此类推。这种关系常被称为原型链 (prototype chain),它解释了为何一个对象会拥有定义在其他对象中的属性和方法
在对象实例和它的构造器之间建立一个链接(它是__proto__
属性,是从构造函数的prototype
属性派生的),之后通过上溯原型链,在构造器中找到这些属性和方法
js对数组有哪些操作方法(增删改查)及其他最少说7个
增
下面前三种是对原数组产生影响的增添方法,第四种则不会对原数组产生影响
- push()
- unshift()
- splice()
- concat()
删
下面三种都会影响原数组,最后一项不影响原数组:
- pop()
- shift()
- splice()
- slice()
改
即修改原来数组的内容,常用splice
- splice()
查
即查找元素,返回元素坐标或者元素值
- indexOf()
- includes()
- find()
排序方法
- reverse()
- sort()
转换方法
- join()
迭代方法
常用来迭代数组的方法(都不改变原数组)有如下:
- some()
- every()
- forEach()
- filter()
- map()
vue如何实现双向数据绑定?
vue实现数据双向绑定的主要是:
采用数据劫持结合发布者-订阅者模式的方式,通过Object.defineProperty()来劫持各个属性的setter、getter,在数据变动时发布消息给订阅者,触发相应监听回调。
当创建Vue实例时,vue会遍历data选项的属性,利用Objet.defineProperty为属性添加getter和setter对数据的读取进行劫持(getter用来依赖收集,setter用来派发更新),并且在内部追踪依赖,在属性被访问和修改时通知变化。
每个组件实例会有对应的watcher实例,在组件渲染的过程中记录依赖的所有数据属性(进行依赖收集,还有computed watcher,user watcher实例),之后依赖项被该改动时,setter方法会通知依赖与此data的watcher实例重新计算(派发更新),从而使它关联的组件重新渲染。
为什么在Vue3.0采用了Proxy,抛弃了Object.defineProperty
Object.defineProperty本身有一定的监控到数组下标变化的能力,但是在Vue中,从性能/体验的性价比考虑,弃用了这个特性(Vue为什么不能检测数组的变动)。为了解决这个问题,经过Vue内部处理后可以使用以下几种方法来监听数组
push()、pop()、shift()、unshift()、splice()、sort()、reverse()
由于只针对了以上7种方法进行了hack处理,所以其他数组的属性也是检测不到的,还是具有一定的局限性。
Object.defineProperty只能劫持对象的属性,因此需要对每个对象的每个属性进行遍历。Vue 2.x是通过递归+遍历data对象来实现对数据的监听的,如果属性值也是对象那么需要深度遍历,显然如果能劫持一个完整的对象才是更好的选择。Proxy可以劫持整个对象,并返回一个新的对象。Proxy不仅可以代理对象,代理数组。还可以代理动态增加的属性。
Proxy相较于Object.defineProperty的优势
1.直接监听对象而非属性
2.直接监听数组的变化
3.拦截方式较多
4.Proxy返回一个新对象,可以只操作新对象达到目的,而Object.defineProperty只能遍历对象属性直接修改(需要用深拷贝进行修改)
5.Proxy作为新标准将受到浏览器厂商重点持续的性能优化
6.Proxy不能用polyfill来兼容,polyfill主要抚平不同浏览器之间对js实现的差异。
微信小程序获取用户信息
wx.getUserInfo({})
HTTP状态码200/302/401/404/500分别代表什么意思
2xx (成功)
200 – 服务器成功返回网页
3xx (重定向)
302(临时移动) 服务器目前从不同位置的网页响应请求,但请求者应继续使用原有位置来响应以后的请求。
4xx(请求错误)
401(未授权) 请求要求身份验证。对于登录后请求的网页,服务器可能返回此响应。
404 – 请求的网页不存在
5xx(服务器错误)
500(服务器内部错误) 服务器遇到错误,无法完成请求。
定时器 settimeout和setinterval的区别,如果用settimeout实现每隔一定的时间就执行一次,怎么实现
setTimeout() 方法用于在指定的毫秒数后调用函数或计算表达式, 而setInterval()则是在每隔指定的毫秒数循环调用函数或表达式,直到clearInterval把它清除。
也就是说setTimeout()只执行一次,setInterval()可以执行多次。
function timer() {
setTimeout(function () {
console.log(111);
timer()
}, 1000)
}
timer()
如何改变this指向
call
、apply
、bind
作用是改变函数执行时的上下文,简而言之就是改变函数运行时的this
指向
apply
apply
接受两个参数,第一个参数是this
的指向,第二个参数是函数接受的参数,以数组的形式传入
改变this
指向后原函数会立即执行,且此方法只是临时改变this
指向一次
call
call
方法的第一个参数也是this
的指向,后面传入的是一个参数列表
跟apply
一样,改变this
指向后原函数会立即执行,且此方法只是临时改变this
指向一次
bind
bind方法和call很相似,第一参数也是this
的指向,后面传入的也是一个参数列表(但是这个参数列表可以分多次传入)
改变this
指向后不会立即执行,而是返回一个永久改变this
指向的函数
封装时间格式转换函数formatDate(时间,格式(Y-M-D h:m:s))
function formatDate(value = Date.now(), format = "Y-M-D h:m:s") {
//函数传入字符串 给字符串前面加0 截取后两位 例如传入 1 返回 01 传入 11 返回 11
const formatNumber = n => `0${n}`.slice(-2);
//将字符串转为时间对象
const date = new Date(value);
const formatList = ["Y", "M", "D", "h", "m", "s"];
const resultList = [];
//获取年
resultList.push(date.getFullYear().toString());
// 获取月
resultList.push(formatNumber(date.getMonth() + 1));
//获取日
resultList.push(formatNumber(date.getDate()));
//获取小时
resultList.push(formatNumber(date.getHours()));
//获取分钟
resultList.push(formatNumber(date.getMinutes()));
//获取秒
resultList.push(formatNumber(date.getSeconds()));
//将字符串 format 中对应的字母(formatList[i]) 替换为 格式化的时间 resultList[i]
for (let i = 0; i < resultList.length; i++) {
format = format.replace(formatList[i], resultList[i]);
}
//返回时间
return format;
}
浅拷贝与深拷贝有何区别?如何实现?
浅拷贝
浅拷贝,指的是创建新的数据,这个数据有着原始数据属性值的一份精确拷贝
如果属性是基本类型,拷贝的就是基本类型的值。如果属性是引用类型,拷贝的就是内存地址
即浅拷贝是拷贝一层,深层次的引用类型则共享内存地址
在JavaScript
中,存在浅拷贝的现象有:
Object.assign
Array.prototype.slice()
,Array.prototype.concat()
- 使用拓展运算符实现的复制
深拷贝
深拷贝开辟一个新的栈,两个对象属完成相同,但是对应两个不同的地址,修改一个对象的属性,不会改变另一个对象的属性
常见的深拷贝方式有:
- _.cloneDeep()
- jQuery.extend()
- JSON.stringify()
- 手写循环递归
同步和异步的区别,优缺点?
- 同步
- 代码按照编写好的顺序依次执行的过程被称为同步执行,执行的代码被称为同步代码。
- 优点:
- 代码逻辑清晰,数据加载明确。
- 缺点:
- 执行效率低,系统资源利用率不高。
- 异步
- 代码未按照编写顺序依次执行的过程被称为异步执行,被执行的代码被称为异步代码。
- 优点:
- 执行效率高,系统资源利用高,
- 缺点
- 代码执行逻辑不够清晰,数据加载相对不够明确。
- 常见的:
- 定时器
- ajax async属性为true时的请求。
请说出以下结果输出什么?为什么?
for(var i = 0; i < 5; i++) {
setTimeout(function(){
console.log(i)
}, 0)
}
输出结果为: 5,5,5,5,5
原因:
1.var不具有块级作用域,所以i
为全局变量,
2.setTimeout为异步方法,需要等同步方法执行完毕后执行console.log(i)操作,此时i
经过循环++ ,i=5
同步代码执行完成后执行异步方法 console.log(i) 的值为 5
var i = 0
{
setTimeout() //加入任务队列
i++ //1
}
//i<5继续循环
{
setTimeout() //加入任务队列
i++ //2
}
//i<5继续循环
{
setTimeout() //加入任务队列
i++ //3
}
//i<5继续循环
{
setTimeout() //加入任务队列
i++ //4
}
//i<5继续循环
{
setTimeout() //加入任务队列
i++ //5
//不满足条件 跳出循环
}
//同步执行结束 执行异步方法
{
console.log(i) //5
}
{
console.log(i) //5
}
{
console.log(i) //5
}
{
console.log(i) //5
}
{
console.log(i) //5
}
等同步方法执行完毕后执行console.log(i)操作,此时i
经过循环++ ,i=5
同步代码执行完成后执行异步方法 console.log(i) 的值为 5
var i = 0
{
setTimeout() //加入任务队列
i++ //1
}
//i<5继续循环
{
setTimeout() //加入任务队列
i++ //2
}
//i<5继续循环
{
setTimeout() //加入任务队列
i++ //3
}
//i<5继续循环
{
setTimeout() //加入任务队列
i++ //4
}
//i<5继续循环
{
setTimeout() //加入任务队列
i++ //5
//不满足条件 跳出循环
}
//同步执行结束 执行异步方法
{
console.log(i) //5
}
{
console.log(i) //5
}
{
console.log(i) //5
}
{
console.log(i) //5
}
{
console.log(i) //5
}
如何用JS实现圆形区域点击事件。
1、map+area 或者 svg
2、border-radius
3、纯 js 实现 需要求一个点在不在圆上简单算法、获取鼠标坐标等等
Object.is() 与原来的比较操作符“=”、“”的区别?
“==”:不全相等,只⽐较数据,不⽐较类型,如果两边的类型不⼀致,则会
进⾏强制类型转化后再进⾏⽐较
“===”:全等,既要⽐较数据,也⽐较数据类型,如果两边的类型不⼀致时,
不会做强制类型准换,直接返回 false
(1) == 主要存在:强制转换成 number, null==undefined
" "==0 //true
“0”==0 //true
" " !=“0” //true
123==“123” //true
null==undefined //true
(2)Object.js:⼀般情况下和三等号的判断相同,它处理了⼀些特殊的情况,
⽐如 -0 和 +0 不再相等,两个 NaN 是相等的。
主要的区别就是+0 != -0 ⽽ NaN==NaN
(相对⽐=和的改进)
Proxy 可以实现什么功能?
在 Vue3.0 中通过 Proxy 来替换原本的 Object.defineProperty 来实现数据响
应式。
Proxy 是 ES6 中新增的功能,它可以⽤来⾃定义对象中的操作。
let p = new Proxy(target, handler)
target 代表需要添加代理的对象,handler ⽤来⾃定义对象中的操作,⽐如可
以⽤来⾃定义 set 或者 get 函数。
下⾯来通过 Proxy 来实现⼀个数据响应式:
let onWatch = (obj, setBind, getLogger) => {
let handler = {
get(target, property, receiver) {
getLogger(target, property)
return Reflect.get(target, property, receiver)
},
set(target, property, value, receiver) {
setBind(value, property)
return Reflect.set(target, property, value) }
}
return new Proxy(obj, handler)
}
let obj = { a: 1 }
let p = onWatch(
obj,
(v, property) => {
console.log(`监听到属性${property}改变为${v}`)
},
(target, property) => {
console.log(`'${property}' = ${target[property]}`)
}
)
p.a = 2 *//* 监听到属性*a*改变
p.a *// 'a' = 2*
在上述代码中,通过⾃定义 set 和 get 函数的⽅式,在原本的逻辑中插⼊了我
们的函数逻辑,实现了在对对象任何属性进⾏读写时发出通知。
当然这是简单版的响应式实现,如果需要实现⼀个 Vue 中的响应式,需要在
get 中收集依赖,在 set 派发更新,之所以 Vue3.0 要使⽤ Proxy 替换原本的
API 原因在于 Proxy ⽆需⼀层层递归为每个属性添加代理,⼀次即可完成以上
操作,性能上更好,并且原本的实现有⼀些数据更新不能监听到,但是 Proxy
可以完美监听到任何⽅式的数据改变,唯⼀缺陷就是浏览器的兼容性不好。
Vue 的路由实现:hash 模式 和 history 模式
**hash 模式:**在浏览器中符号“#”,#以及#后面的字符称之为 hash,用
window.location.hash 读取;
特点:hash 虽然在 URL 中,但不被包括在 HTTP 请求中;用来指导浏览器动作,对服务
端安全无用,hash 不会重加载页面。
hash 模式下,仅 hash 符号之前的内容会被包含在请求中,如 http://www.xxx.com,
因此对于后端来说,即使没有做到对路由的全覆盖,也不会返回 404 错误。
**history 模式:**history 采用 HTML5 的新特性;且提供了两个新方法:pushState(),
replaceState()可以对浏览器历史记录栈进行修改,以及 popState 事件的监听到状
态变更。
history 模式下,前端的 URL 必须和实际向后端发起请求的 URL 一致,
如 http://www.xxx.com/items/id。后端如果缺少对 /items/id 的路由处理,将返回
404 错误。Vue-Router 官网里如此描述:“不过这种模式要玩好,还需要后台配置支
持……所以呢,你要在服务端增加一个覆盖所有情况的候选资源:如果 URL 匹配不到
任何静态资源,则应该返回同一个 index.html 页面,这个页面就是你 app 依赖的页
面
vuex核心流程
每个Vuex应用的核心就是store,里面又包括:
(1)state(数据):用来存放数据源,就是公共状态
(2)getters(数据加工):有时候需要对数据源进行加工,返回需要的数据
(3)actions(事件):要执行的操作,可以进行异步操作
(4)mutations(执行):操作结束之后,actions通过commit更新state数据源
(5)modules:使用单一状态树,致使应用的全部状态集中到一个很大的对象,所以把每个模块的局部状态分装使每一个模块拥有本身的state,mutation,actions,setters,甚至是嵌套子模块
Vuex的工作流程就是:
(1)通过dispatch去提交一个actions
(2)actions接收到这个事件之后,在actions中可以执行一些异步|同步操作,根据不用的情况去分发给不同的mutations
(3)actions通过commit去触发mutations,
(4)mutations去更新state数据,state更新之后,就会通知vue进行渲染
vue-router 有哪几种导航钩子
1、全局守卫: router.beforeEach
2、全局解析守卫: router.beforeResolve
3、全局后置钩子: router.afterEach
4、路由独享的守卫: beforeEnter
5、组件内的守卫: beforeRouteEnter、beforeRouteUpdate (2.2 新增)、
beforeRouteLeave
导航表示路由正在发生改变,vue-router 提供的导航守卫主要用来:通过跳转或取消的
方式守卫导航。有多种机会植入路由导航过程中:全局的, 单个路由独享的, 或者组
件级的。
注意:参数或查询的改变并不会触发进入/离开的导航守卫。 你可以通过 观察
$route 对象 来应对这些变化,或使用 beforeRouteUpdate 的组件内守卫。
vue-router 实现动态加载路由组件( 懒加载 )
当打包构建应用时,Javascript 包会变得非常大,影响页面加载。如果我们能把不同路由对应
的组件分割成不同的代码块,然后当路由被访问的时候才加载对应组件,这样就更加高效了。
结合 Vue 的异步组件和 Webpack 的代码分割功能,轻松实现路由组件的懒加载。
第一步:定义一个能够被 Webpack 自动代码分割的异步组件。
//在 src/router/index.js 里面引入异步引入组件
const index = () => import('../page/list/index.vue');
第二步:在路由配置中什么都不需要改变,只需要像往常一样使用 index。
const router = new VueRouter({
routes: [
{ path: '/index', component: index,name:"index" }
]})
第三步:在 build/webpack.base.conf.js 下的 output 属性,新增 chunkFilename。
output: {
path: config.build.assetsRoot,
filename: '[name].js',
//新增 chunFilename 属性
chunkFilename: '[name].js',
publicPath: process.env.NODE_ENV === 'production'
? config.build.assetsPublicPath
: config.dev.assetsPublicPath
},
如何实现文字禁止换行,超过部分用省略号隐藏
white-space:nowrap;
overflow:hidden;
text-overflow:ellipsis;
强制不换行,超出部分隐藏且以省略号形式出现
MVC、MVP 与 MVVM 模式
1、MVC:
MVC 是应用最广泛的软件架构之一,一般 MVC 分为:
Model( 模型 )、Controller( 控制器 )、View( 视图 )。
这主要是基于分层的目的,让彼此的职责分开。View 一般通过 Controller 来和 Model 进行联系。Controller 是 Model 和 View 的协调者,View 和 Model不直接联系。基本联系都是单向的。
1、View 传送指令到 Controller
2、Controller 完成业务逻辑后,要求 Model 改变状态
3、Model 将新的数据发送到 View,用户得到反馈
2、MVP:
MVP 模式将 Controller 改名为 Presenter,同时改变了通信方向。
1、各部分之间的通信,都是双向的。
2、View 与 Model 不发生联系,都通过 Presenter 传递。
3、View 非常薄,不部署任何业务逻辑,称为"被动视图"(Passive View),即没有任何主动性,而 Presenter 非常厚,所有逻辑都部署在那里。
3、MVVM
MVVM 是把 MVC 的 Controller 和 MVP 的 Presenter 改成了 ViewModel。
View 的变化会自动更新到 ViewModel,ViewModel 的变化也会自动同步到 View上显示。这种自动同步是因为 ViewModel 中的属性实现了 Observer,当属性变更时都能触发对应的操作。
请描述一次完整的 Redux 数据流
看上面的图,可以看到数据流发生改变的时候,数据是如何流动的。
单向数据流调用 store.dispatch(action) -> reducer(state, action) ->store.getState()
(1)调用 sote.dispath(action)(这里的 action 和上面提到的 action 不是一个概念,
这个是一个纯的对象,上面是一个 action creator)
{ type: 'LIKE_ARTICLE', ID: 1 }
你可以在任何地方调用 store.dispatch(action),比如组件内部,Ajax 回调函数里面等等。
(2)Action 会触发给 Store 指定的 rootreducerrootreducer 会返回一个完整的状态树,state 状态树上的各个值都可以由对应的reducer 来更新。
(3)store 会保存状态树
更新完 state 后,新的 State 会替代旧的 State。然后可以添加监听函数
store.subscribe(listener)
在回调函数里面可以通过
store.getState()
来获取新的 state。这样就能更新整个 UI。只要调用
dispath
,state 就会根据reducer
对应更新,进而触发监
听函数subscribe
,然后触发回调函数渲染 UI。
React的事件和普通的HTML事件有什么不同?
区别:
● 对于事件名称命名⽅式,原⽣事件为全⼩写,react 事件采⽤⼩驼峰;
● 对于事件函数处理语法,原⽣事件为字符串,react 事件为函数;
● react 事件不能采⽤ return false 的⽅式来阻⽌浏览器的默认⾏为,⽽必
须要地明确地调⽤preventDefault()来阻⽌默认⾏为。
合成事件是 react 模拟原⽣ DOM 事件所有能⼒的⼀个事件对象,其优点如
下:
● 兼容所有浏览器,更好的跨平台;
● 将事件统⼀存放在⼀个数组,避免频繁的新增与删除(垃圾回收)。
● ⽅便 react 统⼀管理和事务机制。
事件的执⾏顺序为原⽣事件先执⾏,合成事件后执⾏,合成事件会冒泡绑定到
document 上,所以尽量避免原⽣事件与合成事件混⽤,如果原⽣事件阻⽌冒
泡,可能会导致合成事件不执⾏,因为需要冒泡到document 上合成事件才会
执⾏。
在React中如何避免不必要的render?
React 基于虚拟 DOM 和⾼效 Diff 算法的完美配合,实现了对 DOM 最⼩粒度
的更新。⼤多数情况下,React 对 DOM 的渲染效率⾜以业务⽇常。但在个别
复杂业务场景下,性能问题依然会困扰我们。此时需要采取⼀些措施来提升运
⾏性能,其很重要的⼀个⽅向,就是避免不必要的渲染(Render)。这⾥提
下优化的点:
● shouldComponentUpdate 和 PureComponent
在 React 类组件中,可以利⽤ shouldComponentUpdate或者
PureComponent 来减少因⽗组件更新⽽触发⼦组件的 render,从⽽达到⽬
的。shouldComponentUpdate 来决定是否组件是否重新渲染,如果不希望组
件重新渲染,返回 false 即可。
● 利⽤⾼阶组件
在函数组件中,并没有 shouldComponentUpdate 这个⽣命周期,可以利⽤
⾼阶组件,封装⼀个类似 PureComponet 的功能
● 使⽤ React.memo
React.memo 是 React 16.6 新的⼀个 API,⽤来缓存组件的渲染,避免不必
要的更新,其实也是⼀个⾼阶组件,与 PureComponent ⼗分类似,但不同的
是, React.memo只能⽤于函数组件。
React组件的state和props有什么区别?
(1)props
props是⼀个从外部传进组件的参数,主要作为就是从⽗组件向⼦组件传递数
据,它具有可读性和不变性,只能通过外部组件主动传⼊新的props来重新渲
染⼦组件,否则⼦组件的props以及展现形式不会改变。
(2)state
state的主要作⽤是⽤于组件保存、控制以及修改⾃⼰的状态,它只能在
constructor中初始化,它算是组件的私有属性,不可通过外部访问和修改,
只能通过组件内部的this.setState来修改,修改state属性会导致组件的重新
渲染。
(3)区别
● props 是传递给组件的(类似于函数的形参),⽽state 是在组件内被组
件⾃⼰管理的(类似于在⼀个函数内声明的变量)。
● props 是不可修改的,所有 React 组件都必须像纯函数⼀样保护它们的
props 不被更改。
● state 是在组件中创建的,⼀般在 constructor中初始化 state。state 是
多变的、可以修改,每次setState都异步更新的。
slot是什么?有什么作⽤?原理是什么?
slot⼜名插槽,是Vue的内容分发机制,组件内部的模板引擎使⽤slot元素作
为承载分发内容的出⼝。插槽slot是⼦组件的⼀个模板标签元素,⽽这⼀个标
签元素是否显示,以及怎么显示是由⽗组件决定的。slot⼜分三类,默认插
槽,具名插槽和作⽤域插槽。
默认插槽:⼜名匿名查抄,当slot没有指定name属性值的时候⼀个默认
显示插槽,⼀个组件内只有有⼀个匿名插槽。
具名插槽:带有具体名字的插槽,也就是带有name属性的slot,⼀个组
件可以出现多个具名插槽。
作⽤域插槽:默认插槽、具名插槽的⼀个变体,可以是匿名插槽,也可以
是具名插槽,该插槽的不同点是在⼦组件渲染作⽤域插槽时,可以将⼦组
件内部的数据传递给⽗组件,让⽗组件根据⼦组件的传递过来的数据决定
如何渲染该插槽。
实现原理:当⼦组件vm实例化时,获取到⽗组件传⼊的slot标签的内容,存
放在vm. s l o t 中,默认插槽为 v m . slot中,默认插槽为vm. slot中,默认插槽为vm.slot.default,具名插槽为vm.$slot.xxx,
xxx 为插槽名,当组件执⾏渲染函数时候,遇到slot标签,使⽤$slot中的内
容进⾏替换,此时可以为插槽传递数据,若存在数据,则可称该插槽为作⽤域
插槽。
对 React 和 Vue 的理解,它们的异同
相似之处:
● 都将注意⼒集中保持在核⼼库,⽽将其他功能如路由和全局状态管理交给
相关的库;
● 都有⾃⼰的构建⼯具,能让你得到⼀个根据最佳实践设置的项⽬模板;
● 都使⽤了Virtual DOM(虚拟DOM)提⾼重绘性能;
● 都有props的概念,允许组件间的数据传递;
● 都⿎励组件化应⽤,将应⽤分拆成⼀个个功能明确的模块,提⾼复⽤性。
不同之处 :
1)数据流
Vue默认⽀持数据双向绑定,⽽React⼀直提倡单向数据流
2)虚拟DOM
Vue2.x开始引⼊"Virtual DOM",消除了和React在这⽅⾯的差异,但是在具
体的细节还是有各⾃的特点。
● Vue宣称可以更快地计算出Virtual DOM的差异,这是由于它在渲染过程
中,会跟踪每⼀个组件的依赖关系,不需要重新渲染整个组件树。
● 对于React⽽⾔,每当应⽤的状态被改变时,全部⼦组件都会重新渲染。
当然,这可以通过 PureComponent/shouldComponentUpdate这个⽣命
周期⽅法来进⾏控制,但Vue将此视为默认的优化。
3)组件化
React与Vue最⼤的不同是模板的编写。
● Vue⿎励写近似常规HTML的模板。写起来很接近标准 HTML元素,只是
多了⼀些属性。
● React推荐你所有的模板通⽤JavaScript的语法扩展⸺JSX书写。
具体来讲:React中render函数是⽀持闭包特性的,所以import的组件在
render中可以直接调⽤。但是在Vue中,由于模板中使⽤的数据都必须挂在
this 上进⾏⼀次中转,所以 import ⼀个组件完了之后,还需要在
components 中再声明下。 4)监听数据变化的实现原理不同
● Vue 通过 getter/setter 以及⼀些函数的劫持,能精确知道数据变化,不需
要特别的优化就能达到很好的性能● React 默认是通过⽐较引⽤的⽅式进⾏的,如果不优化
(PureComponent/shouldComponentUpdate)可能导致⼤量不必要的
vDOM的重新渲染。这是因为 Vue 使⽤的是可变数据,⽽React更强调数
据的不可变。
5)⾼阶组件
react可以通过⾼阶组件(HOC)来扩展,⽽Vue需要通过mixins来扩展。
⾼阶组件就是⾼阶函数,⽽React的组件本身就是纯粹的函数,所以⾼阶函数
对React来说易如反掌。相反Vue.js使⽤HTML模板创建视图组件,这时模板
⽆法有效的编译,因此Vue不能采⽤HOC来实现。
6)构建⼯具
两者都有⾃⼰的构建⼯具:
● React ==> Create React APP
● Vue ==> vue-cli
7)跨平台
● React ==> React Native
● Vue ==> Weex
delete和Vue.delete删除数组的区别
1。 delete:只是被删除数组成员变为 empty / undefined,其他元素键
值不变
2。 Vue.delete:直接删了数组成员,并改变了数组的键值(对象是响
应式的,确保删除能触发更新视图,这个⽅法主要⽤于避开 Vue 不能检
测到属性被删除的限制)
什么是单向数据流
概念:数据流指的是组件之间的数据流动。单向数据流指⽗级数据修改
流向⼦级,⼦级不能修改⽗级的数据,在Vue中,⽗组件可以通过Prop向⼦
组件进⾏传值,当⽗组件的数据发⽣改变时,⽗级的更新会向下流动到⼦级,
⼦组件中的数据会同步更新。但是,反过来则不⾏,也就是说⼦组件中不能通
过修改 Prop 来更新⽗级的数据
React的⽣命周期有哪些?
渲染过程调用到的生命周期函数,主要几个要知道;
* constructor
* getInitialState
* getDefaultProps
* componentWillMount
* render
* componentDidMount
更新过程
* componentWillReceiveProps
* shouldComponentUpdate
* componentWillUpdate
* render
* componentDidUpdate
卸载过程
componentWillUnmount
Component, Element, Instance 之间有什么区别和联系?
React.createClass和extends Component的bai区别主要在于:
(
1)语法区别
● createClass本质上是⼀个⼯⼚函数,extends的⽅式更加接近最新的
ES6规范的class写法。两种⽅式在语法上的差别主要体现在⽅法的定义
和静态属性的声明上。
● createClass⽅式的⽅法定义使⽤逗号,隔开,因为creatClass本质上是
⼀个函数,传递给它的是⼀个Object;⽽class的⽅式定义⽅法时务必谨
记不要使⽤逗号隔开,这是ES6 class的语法规范。
(2)propType 和 getDefaultProps
● React.createClass:通过proTypes对象和getDefaultProps()⽅法来设置
和获取props.
● React.Component:通过设置两个属性propTypes和defaultProps
(3)状态的区别
● React.createClass:通过getInitialState()⽅法返回⼀个包含初始值的对
象
● React.Component:通过constructor设置初始状态
(4)this区别
● React.createClass:会正确绑定this
● React.Component:由于使⽤了 ES6,这⾥会有些微不同,属性并不会⾃
动绑定到 React 类的实例上。
(5)Mixins● React.createClass:使⽤ React.createClass 的话,可以在创建组件时添
加⼀个叫做 mixins 的属性,并将可供混合的类的集合以数组的形式赋给
mixins。
● 如果使⽤ ES6 的⽅式来创建组件,那么 React mixins 的特性将不能被使
式的⽅法定义使⽤逗号,隔开,因为creatClass本质上是
⼀个函数,传递给它的是⼀个Object;⽽class的⽅式定义⽅法时务必谨
记不要使⽤逗号隔开,这是ES6 class的语法规范。
(2)propType 和 getDefaultProps
● React.createClass:通过proTypes对象和getDefaultProps()⽅法来设置
和获取props.
● React.Component:通过设置两个属性propTypes和defaultProps
(3)状态的区别
● React.createClass:通过getInitialState()⽅法返回⼀个包含初始值的对
象
● React.Component:通过constructor设置初始状态
(4)this区别
● React.createClass:会正确绑定this
● React.Component:由于使⽤了 ES6,这⾥会有些微不同,属性并不会⾃
动绑定到 React 类的实例上。
(5)Mixins● React.createClass:使⽤ React.createClass 的话,可以在创建组件时添
加⼀个叫做 mixins 的属性,并将可供混合的类的集合以数组的形式赋给
mixins。
● 如果使⽤ ES6 的⽅式来创建组件,那么 React mixins 的特性将不能被使
⽤了。