1.vue的优点及缺点
Vue最核心的两个特点,响应式和组件化
响应式:
通过MVVM思想实现数据的双向绑定,通过虚拟DOM让我们可以用数据来操作DOM,而不必去操作真实的DOM,提升性能,且让开发者有更多的时间去思考业务逻辑.
组件化:
把一个单页应用中的各个模块拆分到一个个组件中,或者把一些公共的部分抽离出来做成一个可复用的组件,所以组件化带来的好处就是,提高开发效率,方便重复使用,使项目可维护性更强.
虚拟DOM:
缺点:
基于对象配置文件的写法,也就是options写法,并发时不利于对一个属性的查找.
- 不利于seo
- 导航不可用,如果一定要导航需要自行实现前进,后退.(需要自己建立堆栈管理)
- 初次加载耗时多
2.Vue中hash模式和history模式的区别
- 最明显的是在显示上,hash模式的URL中会夹杂着#号,而history没有
- Vue底层对它的实现方式不同,hash模式是依靠onhashchange事件(监听location.hash的改变),而history模式主要是依靠的H5 history中新增的两个方法.pushState()可以改变url地址且不会发送请求,replaceState()可以读取历史记录栈,还可以对浏览器记录进行修改.
- 当真正需要通过URL向后端发送HTTP请求的时候,比如常见的用户手动输入URL后回车,或者是刷新重启浏览器,这时候history模式需要后端的支持.因为history模式下,前端的URL必须和实际向后端发送请求的URL一致.
3.null和undefined的区别
- null表示无的对象,也就是该处不应该有值;而undefined表示未定义
- 在转换为数字时结果不同,Number(null)为0,而Number(undefined)为NaN
使用场景上: null:
- 作为函数的参数,表示该函数的参数不是对象
- 作为对象原型链的终点
undefined:
- 变量被声明了,但没有被赋值,就等于undefined
- 调用函数时,应该提供但参数没有提供,该函数等于undefined
- 对象没有赋值属性,该属性的值为udnefined
- 函数没有返回值时,默认返回undefined
4.冒泡排序
function bubbleSort (arr) {
for (let i = 0; i < arr.length; i++) {
let flag = true;
for (let j = 0; j < arr.length - i - 1; j++) {
flag = false;
let temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
}
}
if (flag) break;
}
return arr;
}
用一个flag来优化,如果某一次循环中没有交换过元素,那么意味着排序已经完成.
5.数组去重
1.Array.from(new Set(arr));
2.[...new Set(arr)];
3.for循环嵌套,利用splice去重.
4.新建数组,利用indexOf或者includes去重;
5.先用sort排序,然后用一个指针从第0位开始,配合while循环去重;
6.描述一下Promise
Promise是一个对象,它代表了一个异步操作的最终完成或者失败.由于它的then方法和catch方法,finally方法会返回一个新的promise所以可以允许我们链式调用.解决了传统的回调地狱.
1.Promise的状态一经改变就不能再改变.
2..then和.catch都会返回一个新的promise
3.catch不管被链接到那里,都能捕获上层未捕获过的错误.
4.在promise中,返回任意一个非promise的值都会被包裹成promise对象.
5.promise的.then,.catch都可以被调用多次,但如果Promise内部的状态一经改变,并且有了一个值,那么后续每次调用,then或者.catch的时候都会直接拿到该值.
6..then或.catch返回的值不能是promise本身,否则会造成死循环.
7..then或者.catch中return一个error对象并不会抛出错误.所以不会被后续的.catch捕获.
8..then或者.catch的参数期望是函数,但传入非函数则会发生值透传.
9..then方法能接收两个参数,第一个是处理成功的函数,第二个是处理失败的函数,在某些时候你可以认为.catch是.then第二个参数的简写.
10..finally方法也是返回一个Promise,它在Promise结束的时候,无论结果为resolved还是rejected,都会执行里面的回调函数.
11.finally方法
11.1.finally()方法不管Promise对象最后的状态如何都会执行.
11.2回调函数不接收任何的参数,也就是说在.finally()函数中是没法知道Promise最终的状态.
11.3它最终返回的默认是上一个Promise对象值,不过如果抛出的是一个异常则返回异常的Promise对象.
- all()和race()
- Promise.all()的作用是接收一组异步任务,然后并行执行异步任务,并且在所有异步操作执行完后才执行回调.
- .race()的作用也是接收一组异步任务,然后并行执行异步任务,只保留取第一个执行完成的异步操作的结果,其它的方法仍在执行,不过执行结果会被抛弃.
- Promise.all().then()结果中数组的顺序和Promise.all()接收到的数组顺序一致.
- all和race传入的数组中如果有会抛出异常的异步任务,那么只有最先抛出的错误会被捕获,并且是被then的第二个参数或者后面的catch捕获;但并不会影响数组中其它的异步任务的执行.
7.type和interface的区别
type类型别名
- 相同点:
1.都可以描述一个对象或函数
2.都允许继承
3.interface extends interface
4.type extends type
5.interface extends type
6.type extends interface
- 不同点:
1.type可以而interface不可以
type可以声明基本类型别名,联和类型,元组等类型.type语句中还可以使用typeof获取实例的类型进行赋值.
type DiffName = string;
// 联和类型
interface Dog {
wang();
}
interface Cat {
miao();
}
type Pet = Dog | Cat;
// 元组类型
type PetList = [Dog, Cat];
// type extends type
type Name = {
name: string;
}
type User = Name & { age: number };
// interface extends type
type Name = {
name: string;
}
interface User extends Name {
age: number;
}
// type extends interface
interface Name {
name: string;
}
type User = Name & {
age: number;
}
// 获取实例类型进行赋值
type B = typeof div
2.interface可以而type不可以
interface能够声明合并
// interface 能够声明合并
interface DiffLx {
name: string;
}
interface DiffLx {
age: number;
}
interface DiffLx {
sex: string;
}
// 合并后
interface DiffLx {
name: string;
age: number;
sex: string;
}
8.any和unknow的区别
any和unknow的最大区别是,unknow是top type(任何类型都是它的subtype),而any既是top type,又是bottom type(它是任何类型的subtype),这导致any基本上就是放弃了任何类型检查.
TypeScript3.0中引入unknow类型也被认为是top type,但它更安全.与any一样,所有类型都可以分配给unknow
let uncertain: unknow = 'Hello';
uncertain = 12;
uncertain = { hello: () => 'Hello' };
可以将unknow类型的变量赋值给any和unknow
使用类型断言缩小未知范围
要对未知类型执行某些操作,首先需要使用类型断言来缩小范围.(强制typescript编译器相信我们知道自己在做什么).
const getDogName = () => {
let x: unknow;
return x;
};
cosnt dogName = getDogName();
console.log((dogName as string).toLowerCase());
它只是一个假设,没有运行时效果,也不能防止我们在不小心的情况下造成错误.
使用类型收缩:
ts编译器会分析我们的代码,并找出一个更窄的类型.使用typeof和instanceof
9.TypeScript高级技巧(待补充)
- 1.keyof
keyof与Object.keys略有相似,只不过keyof取interface的键.
interface Point {
x: number;
y: number;
}
type keys = keyof Point;
假设有一个object,使用ts实现一个get函数来获取它的属性值.使用keyof来加强get函数的类型功能.
const data = {
a: 3,
hello: 'world'
}
function get<T extends object, K extends keyof T>(o: T, name: K): T[K] {
return o[name];
}
- 2.Required&Partial&Pick
type Partial<T> = {
[P in keyof T] ? : T[P];
};
type Required<T> = {
[P in keyof T]-?: T[P];
};
type Pick<T, K extends keyof T> = {
[P in K]: T[P];
};
interface User {
id: number;
age: number;
name: string;
};
type PartialUser = Partial<User>
type PickUser = Pick<User, "id" | "age">
-3.Condition Type 可以用来扩展一些基本类型
T extends U ? X : Y;
type isTrue<T> = T extends true ? true : false;
// 相当于 type t = false;
type t1 = isTrue<false>;
-4.never & Exclude & Omit
10.EventLoop的执行过程
- 一开始整个脚本作为一个宏任务执行.
- 执行过程中同步代码直接执行,宏任务进行入宏任务队列.微任务进入微任务队列.
- 当前宏任务执行完毕,检查微任务列表,直到全部执行完
- 执行浏览器UI线程的渲染工作
- 检查是否有Web Worker任务,有则执行
- 执行完本轮的宏任务,回到2,依此循环,直到宏任务和微任务队列都为空.
11.addEventListener函数的第三个参数
true为捕获,false为冒泡
12.如何写原生自定义事件?
13.冒泡和捕获的具体过程
冒泡是指:当给元素绑定了事件之后,这个事件会依次在它的父级元素中被触发(前提是父级元素也有这个同名称的事件,比如子元素和父元素都绑定了click事件就触发父元素的click)
捕获则是从上层向下层传递,与冒泡相反.
冒泡结果: button > li > ul > document > window
捕获结果: window > document > ul > li > button
14.并不是所有的事件都有冒泡
onblur, onfocus, onmouseenter, onmouseleave
15.webpack中的loader和plugin有什么区别?
loader是一个转换器,只专注于转换文件,完成压缩,打包,语言编译,仅仅是为了打包,并且运行在打包之前.
plugin是一个扩展器,它丰富了webpack本身,为其进行一些其它功能的扩展,它不局限于打包,资源的加载,还有其它的功能.所以它是在整个编译周期都起作用.
16.HTTP和TCP的不同
HTTP的责任是去定义数据,在两台计算机相互传递信息时,HTTP规定了每段数据以什么形式表达才是能
够被另外一台计算机理解.
而TCP所要规定的是数据应该怎么传输才能稳定且高效的传递于计算机之间.
17.TCP和UDP的区别
1.TCP是一个面向链接的,可靠的,基于字节流的传输层协议.
2.UDP是一个面向对象无连接的传输层协议.
TCP的三次握手,保证双方都有接受和发送数据的能力.
字节流服务: 将大块数据分割为以报文段为单位的数据包进行管理.
18.介绍一下虚拟DOM
虚拟DOM本质就是用一个原生的Java Script对象去描述一个DOM节点.是对真实DOM的一层抽象.由于在浏览器中操作DOM是很昂贵的.频繁操作DOM,会产生一定的性能问题,因此我们需要这一层抽象,在patch过程中尽可能地一次性将差异更新到DOM中,这样保证了DOM不会出现性能很差的情况.还有一点很重要,设计初衷,为了更好的跨平台,比如Node.js就没有DOM,如果想实现SSR,那么一个方式就是借助Virtual DOM,因为Virtual DOM本身是JS对象.
19.JSONP的原理及实现
基本原理:
主要是利用script标签的src属性没有跨域限制,通过指向一个需要访问的地址,由服务端返回一个预先定义好的JS函数的调用,并且将服务器数据以该函数参数的形式传递过来,此方法需要前后端配合完成.
执行过程:
- 前端定义一个解析函数(如:jsonpCallback = function (res) {});
- 通过params的形式包装script标签的请求参数,并且声明执行函数(如: cb = jsonpCallback)
- 后端获取到前端声明的执行函数(jsonpCallback),并以带上参数且调用执行函数的方式传递给前端.
- 前端在script标签返回资源的时候就会去执行jsonpCallback并通过回调函数的方式拿到数据了.
缺点:
- 只能进行GET请求. 优点:
- 兼容性好,在一些古老的浏览器中都可以运行.
20.浏览器为什么会禁止跨域?
- 首先,跨域只存在于浏览器,浏览器为web提供了访问入口,并且访问的方式很简单,在地址栏输入要访问的地址或者点击某个链接就可以了,正是这种开放的形态,所以需要对它有所限制
- 同源策略的产生主要是为了保证用户信息的安全,防止恶意网站窃取数据.分为Ajax同源策略和DOM同源策略
Ajax同源策略
主要做了两种限制: 1.不同源页面不能获取cookie; 2.不同源页面不能发起Ajax请求.防止CSRF攻击.cookie主要是为了解决浏览器与服务器会话状态的问题,它本质上是存储在浏览器或本地文件中一个小小的文件,那么它里面一般会存储用户的一些信息,包括隐私信息,如果没有Ajax同源策略,恶意网站只需要一段脚本就可以获取你的cookie,从而冒充你的身份去给其它网站发送恶意请求.
DOM同源策略
它限制了不同源页面不能获取DOM,例如一个假的网站利用iframe嵌套一个银行网站,并把宽高和其它部分调整的和原银行网站一样,仅仅只是地址栏上的域名不同,若是用户没有注意的话就以为这个是真的网站,如果这时候用户在里面输入账号密码,如果没有同源策略,那么这个恶意网站就可以获取到银行网站中的DOM.也就能拿到用户的输入内容以此来达到窃取用户信息的目的.
21.CORS跨域的原理
过程分析:
- 浏览器先根据同源策略对前端页面和后台交互地址做匹配,若同源,则直接发送数据请求;若不同源,则发送跨域请求.
- 服务器收到浏览器跨域请求后,根据自身配置返回对应文件头.若未配置过任何允许跨域,则文件头里不包含Access-Control-Allow-origin字段,若配置过域名,则返回Access-Control-Allow-origin+对应规则里的域名的方法.
- 浏览器根据接收到的响应头里的Access-Control-Allow-origin字段做匹配.若无该字段,说明不允许跨域,从而抛出一个错误,若有该字段,则对字段内容和当前域名做对比,如果同于,则说明可以跨域,浏览器接收该响应;若不同源,则说明该域名不可跨域,浏览器不接收该响应,并抛出一个错误.
- 在CORS中有简单请求和非简单请求,简单请求是不会触发CORS的预检请求.
- 需预检的请求必须使用OPTIONS方法发起一个预检请求到服务器,以获知服务器是否允许该实际请求.可以避免跨域请求对服务器的用户数据产生未预期的影响.
CORS的简单请求
1.使用下列方法之一 GET,HEAD,POST.
2.人为设置以下集合外的请求头 Accept,Accept-Language,Content-Language,Content-Type,DPR,Downlink,Save-Data,Viewport-Width,Width;
3.Content-Type的值仅限于下面的三者之一 text/plain,multipart/form-data,application/x-www-form-urlencoded
4.请求中的任意XMLHttpRequestUpload对象均没有注册任何事件监听器;
5.请求中没有使用ReadableStream对象.
除了上面这些请求外,都是非常简单的请求.
CORS的预检请求具体是怎样的?
在CORS中,可以使用OPTIONS方法发起一个预检请求(一般都是浏览器检测到请求跨域时,会自动发起),以检测实际请求是否可以被服务器所接受.预检请求报文中到Access-Control-Request-Method首部字段告知服务器实际情求所使用的HTTP方法;Access-Control-Request-Headers首部字段告知服务器实际请求所携带的自定义首部字段.服务器基于从预检请求获得的信息来判断,是否接受接下来的实际请求.服务器所返回的Access-Control-Allow-Methods首部字段将所有允许的请求方法告知客户端,该首部字段与Allow类似,但只能用于涉及到CORS的场景中.
MVVM
MVC中的Controller演变成ViewModel.model层代表数据模型,view层代表UI组件,ViewModel是View和Model层的桥梁,数据会绑定到viewModel层并自动将数据渲染到页面中,视图变化的时候会通知viewModel层更新数据.
Vue2.x响应式数据原理
Vue在初始化数据时,会使用Object.defineProperty重新定义data中的所有属性,当页面使用对应属性时,首先会进行依耐收集(收集当前组件的watcher)如果属性发送变化会通知相关依耐进行更新操作(发布订阅)
Vue3.x响应式原理
使用Proxy直接监听对象和数组的变化.
Proxy只会处理对象的第一层,那么Vue3又是如何处理这个问题的?
判断当前Reflect.get的返回值是否为Object,如果是则再通过reactive方法做代理,这样就实现了深度观测.
监测数组的时候可能触发多次get/set,那么如何防止触发多次呢?
可以判断key是否为当前代理对象target自身属性,也可以判断旧值与新值是否相等,只有满足以上连个条件之一时,才有可能执行tigger.
Vue2.x如何监测数组变化?
使用函数劫持的方式,重写了数组的方法,Vue将data中的数组进行了原型链重写,指向了自己定义的数组原型方法.这样当调用数组api时,可以通知依耐更新.如果数组中包含着引用类型,会对数组中的引用类型再次递归遍历进行监控.这样就实现了监测数组变化.
nextTick原理
在下次DOM更新循环结束之后执行延迟回调.nextTick主要使用了宏任务和微任务.根据执行环境分别尝试采用:
- Promise
- MutationObserver
- setImmediate
- 如果以上都不行则采用setTimeout 定义了一个异步方法,多次调用nextTick会将方法存入队列中,通过这个异步方法清空当前队列.
Vue生命周期
- beforeCreate是new Vue()之后触发的第一个钩子,在当前阶段data,methods,computed以及watch上的数据和方法都不能被访问.
- created在实例创建完成后发生,当前阶段已经完成了数据观测,也就是可以使用数据,更改数据,在这里更改数据不会触发updated函数,可以做一些初始数据的获取,在当前阶段无法与DOM进行交互,如果非要想,可以通过vm.$nextTick来访问Dom.
- beforeMount发生在挂载之前,在这之前template模版已导入渲染函数编译.而当前阶段虚拟DOM已经创建完成,即将开始渲染.在此时也可以对数据进行更改,不会触发updated.
- mounted在挂载完成后发生,在当前阶段,真实的DOM挂载完成,数据完成双向绑定,可以访问到DOM节点,使用$refs属性对DOM进行操作.
- beforeUpdated发生在更新之前,也就是响应式数据发生更新,虚拟dom重新渲染之前被触发,可以在当前阶段进行更改数据,不会造成重渲染.
- updated发生在更新完成之后,当前阶段组件dom已完成更新.要注意的是避免在此期间更改数据,因为这可能会导致无限循环的更新.
- beforeDestroy发生在实例销毁之前,在当前阶段实例完全可以被使用,比如清除计时器.
- destoryed发生在实例销毁之后,这个时候只剩下dom空壳,组件已被拆解,数据绑定被卸载,监听被移除,子实例也被销毁.
Computed和Watch
Computed本质是一个具备缓存的watcher,依耐的属性发生变化就会更新视图.适用于计算比较消耗性能的计算场景.当表达式过于复杂时,在模版中放入过多逻辑会让模版难以维护,可以将复杂逻辑放入计算属性中处理.
Watch没有缓存性,更多的是观察的作用,可以监听某些数据执行回调.当我们需要深度监听对象中的属性时,可以打开deep:true选项,这样便会对对象中的每一项进行监听,这样会带来性能问题.优化的话可以使用字符串形式监听.
组件中的data为什么是一个函数?
一个组件被复用多次的话,也就会创建多个实例,本质上,这些实例用的都是同一个构造函数.如果data是对象的话,对象属于引用类型,会影响到所有的实例,所以为了保证组件不同的实例之间data不冲突,data必须是一个函数.
v-model原理
Vue事件绑定原理
原生事件绑定是通过addEventListener绑定给真实元素的的.
组件事件是通过Vue自定义的#on实现的.
Vue模版编译原理
Vue的编译过程就是将template转化为render函数的过程.会经历以下阶段:
- 生成AST树
- 优化
- codegen
首先解析模版,生成AST语法树(一种用js对象的形式来描述整个模版).使用大量的正则表达式对模版进行解析,遇到标签,文本的时候都会执行对应的钩子进行相关处理.
Vue的数据是响应式的,但其实模版中并不是所有的数据都是响应式的,有一些数据首次渲染后就不会再变化,对应的DOM也不会变化.那么优化过程就是深度遍历AST树,按照相关条件对树节点进行标记.这些标记的节点(静态节点)就可以跳过对它们的比对,对运行时的模版起到很大的优化作用.
编译的最后一步是将优化后的AST树转换为可执行的代码.
Vue2.x和Vue3.x的diff算法.
- 同级比较,再比较子节点
- 先判断一方有子节点,一方没有子节点的清空(如果新的children没有子节点,将旧的子节点移除)
- 比较都有子节点的情况(核心diff)
- 递归比较子节点
正常diff两个树的时间复杂度是o(n的三次方),但实际情况下我们很少会进行跨层级但移动DOM,所以Vue将diff进行了优化O(n),只有当新旧children都为多个子节点时才需要用核心的diff算法进行同层级比较.
vue2的核心diff算法采用了双端比较的算法,同时从新旧children的两端开始进行比较,借助key值找到可复用的节点,再进行相关操作.
Vue3.x借鉴了ivi算法和inferno算法 在创建VNode时就确定其类型,以及在mount/patch的过程中采用位运算来判断一个VNode的类型,在这个基础之上再配合核心的Diff算法,使得性能上较Vue2.x有了提升.(该算法中还运用了动态规划的思想求解最长递归子序列)
虚拟DOM以及Key属性的作用
vue2的Virtual DOM借鉴snabbdom的实现. 本质是一个原生JS对象去描述一个DOM节点.是对真实DOM的一层抽象.映射到真实DOM要经历VNode的create,diff,patch等阶段.
key的作用是尽可能的复用DOM元素
新旧children中的节点只有顺序是不同的时候,最佳的操作应该是通过移动元素的位置来达到更新的目的.
需要在新旧children的节点中保存映射关系,以便能够在旧children的节点中找到可复用的节点.key也就是children中节点的唯一标识.
keep-alive
keep-alive可以实现组件缓存,当组件切换时不会对当前组件进行卸载.
常用的两个属性include/exclude,允许有条件的进行缓存.
两个生命周期activated/deactivated,用来得知当前组件是否处于活跃状态.
keep-alive运用LRU算法.
Vue中组件生命周期调用顺序
组件的调用顺序都是先父后子,渲染完成的顺序是先子后父.
组件的销毁操作是先父后子,销毁完成的顺序是先子后父.
加载渲染过程:
父beforeCreate->父created->父beforeMount->子beforeCreate->子created->子beforeMount->子mounted->父mounted;
子组件更新过程
父beforeUpdate->子beforeUpdate->子updated->父updated;
父组件更新过程:
父beforeUpdate->父updated;
销毁过程:
父beforeDestory->子beforeDestory->子destoryed->父destoryed;
vue2.x组件通信方式
- 父子组件通信
获取父子组件实例
children. ref获取实例的方法调用组件的属性或者方法. provide,inject不推荐,但写组件库时常用.
- 兄弟组件通信 Event Bus实现跨组件通信
Vuex
Vue性能优化
编码阶段
- 尽量减少data中的数据,data中的数据都会增加getter和setter,会收集对应的watcher.
- v-if和v-for不能连用.
- 如果需要使用v-for给每项元素绑定事件时使用事件代理.
- SPA页面采用keep-alive缓存组件.
- 在更多的情况下使用v-if代替v-show
- key保证唯一
- 使用路由懒加载,异步组件
- 防抖,节流
- 第三方模块按需引入
- 长列表滚动到可视区域动态加载
- 图片懒加载 SEO优化
- 预渲染
- 服务端渲染SSR 打包优化
- 压缩代码
- Tree Shaking/Scope Hoisting
- 使用cdn加载第三方模块
- 多线程打包happypack
- splitChunks抽离公共文件
- sourceMap优化 用户体验
- 骨架屏
- PWA 还可以使用缓存(客户端缓存,服务端缓存)优化,服务端开启gzip压缩等.
history路由实现原理
history.pushState()和history.replaceState();
webpack原理
1.初始化参数: 从配置文件和shell语句中读取与合并参数,得出最终的参数.
2.开始编译:用上一步得到的参数初始化compiler对象,加载所有配置的插件,执行对象的run方法开始执行编译;
3.确定入口:根据配置中的entry找出所有的入口文件;
4.编译模块:从入口文件出发,调用所有配置的loader对模块进行翻译,再找出该模块依耐的模块,再递归本步骤直到所有入口依耐的文件都经过了步骤的处理;
5.完成模块编译: 在经过第4步使用loader翻译完所有模块后,得到了每个模块被翻译后的最终内容以及他们之间的依耐关系;
6.输出资源: 根据入口和模块之间的依耐关系,组装成一个个包含多个模块的Chunk,再把每个Chunk转换成一个单独的文件加入到输出列表,这步是可以修改输出内容的最后机会;
7.输出完成:在确定好输出内容后,根据配置确定输出的路径和文件名,把文件内容写入到文件系统.
在以上过程中,webpack会在特定的时间点广播出特定的事件,插件在监听到感兴趣的事件后会执行特定的逻辑,并且插件可以调用webpack提供的API改变webpack的运行结果.
babel原理
babel的转译过程分为三个阶段: parsing,transforming,generating.以ES6转译为ES5代码为例,babel转译的具体过程如下:
1.ES6代码输入
2.babylon进行解析得到AST
3.plugin用babel-traverse对AST树进行遍历转译,得到新的AST树.
4.用babel-genenrator通过AST树生成ES5代码.
闭包
能够读取其它函数内部变量的函数 在js中变量的作用域属于函数作用域,在函数执行完毕后,作用域就会被清理,内存也会随之被回收,但是由于闭包函数是建立在函数内部的子函数,由于其可访问上级作用域,即使上级函数执行完毕,作用域也不会随之销毁,这时子函数,便拥有了访问上级作用域中变量的权限,即使上级函数执行完后作用域内的值也不会被销毁.
解决了什么问题? 1.可以读取函数内部的变量
2.让这些变量的值始终保存在内存中.不会在函数调用后被清除.
前端如何处理十万级别的大数据?
worker子线程
const worker = new Worker(aURL, options);
它有两个参数
1.aURL(必须)是一个DOMString表示worker将执行的脚本的URL.它必须遵守同源策略.
2.options(可选)它的一个作用就是指定worker的名称,用来区分多个worker线程.
worker的属性
onerror: 指定error事件的监听函数
onmessage: 指定message事件监听函数,发生过来的数据在Event.data属性中.
onmessageerror:指定messageerror事件的监听函数.发送的数据无法序列化成字符串时会触发这个事件.
worker的方法
postMessage(): 向worker线程发送消息
worker.terminate()立即终止worker线程.
使用worker的注意点
1.同源限制
分配给worker线程运行的脚本文件,必须与主线程的脚本文件同源.
2.DOM限制
worker线程所在的全局对象,与主线程不一样,无法读取主线程所在网页的DOM对象,也无法使用document,window,parent这些对象.但是,worker线程可以navigatpr对象和location对象.
3.通信联系
worker线程和主线程不在同一个上下文环境,它们不能直接通信,必须通过消息完成.
4.脚本限制
worker线程不能执行alert()方法和confirm()方法,但可以使用XMLHttpRequest对象发出AJAX请求.
5.文件限制
worker线程无法读取本地文件,即不能打开本地文件系统(file://),
computed和watch的区别和运用场景
computed: 是计算属性,依耐其它属性值,并且computed的值有缓存,只有它依耐的属性值发生改变,下一次获取computed的值时才会重新计算computed的值;
watch:更多的是观察的作用,类似于某些数据的监听回调,每当监听当数据变化时都会执行回调进行后续操作.
运用场景:
- 当我们需要进行数值计算,并且依耐于其它数据时,应该使用computed,因为可以利用computed的缓存特性,避免每次获取值时,都要重新计算;
- 当我们需要在数据变化时执行异步任务或开销较大当操作时,应该使用watch,使用watch选项允许我们执行异步,限制我们执行该操作当频率,并在我们得到最终结果前,设置中间状态.
描述微信小程序的相关文件类型
微信小程序项目结构主要有四个文件类型.
- WXML是框架设计的一套标签语言,结合基础组件,事件系统,可以构建出页面的结构.内部主要是微信自己定义的一套组件.
- WXSS一套样式语言,用于描述WXML的组件样式.
- js逻辑处理,网络请求.
- json小程序设置,如页面注册,页面标题及tabBar.
主要文件
- app.json 必须要有这个文件,如果没有这个文件,项目无法运行,因为微信框架把这个作为配置文件入口,整个小程序的全局配置.包括页面注册,网络设置,以及小程序的window背景色,配置导航条样式,配置默认标题.
- app.js 必须要有这个文件,没有也是会报错.但是这个文件创建一下就行,什么都不需要写以后我们可以在这个文件中监听并处理小程序的生命周期函数,声明全局变量.
微信小程序原理
微信小程序采用JS,WXML,WXSS三种技术进行开发,本质就是一个单页面应用,所有的页面渲染和事件处理,都在一个页面内进行,但又可以通过微信客户端调用原生的各种微信的架构的接口,是数据驱动的架构模式,它的UI和数据是分离的,所有的页面的更新,都需要通过对数据的更改来实现.
小程序分为两个部分webview和appService.其中webview主要用来展现UI,appService用来处理业务逻辑,数据及接口调用.他们在两个进程中运行,通过系统层JSBridge实现通信,实现UI的渲染,事件的处理.
小程序的双向数据绑定和vue那里不一样?
小程序直接this.data的属性是不可以同步到视图的,必须调用
this.setData({
})
WXSS和CSS类似,不过在CSS的基础上做了一些补充和修改.
- 尺寸单位 rpx
rpx是响应式像素,可以根据屏幕宽度进行自适应.规定屏幕宽为750rpx.
- 使用@import标识符来导入外联样式.@import后跟需要导入的外联样式表的相对路径.
小程序页面间有那些传递数据的方法?
- 使用全局变量实现数据传递
在app.js文件中自定义全局变量globalData,将需要存储的信息存放在那里
// app.js
App({
// 全局变量
globalData: {
userInfo: null
}
})
使用的时候,直接使用getApp()拿到存储的信息.
- 使用wx.navigateTo与wx.redirectTo的时候,可以将部分数据放到url里面,并在新页面onload的时候初始化. wx.navigateTo和wx.redirectTo不允许跳转到tab所包含的页面
- 使用本地缓存Storage相关
小程序的生命周期函数
- onload页面加载时触发.一个页面只会调用一次,可以在onload的参数中获取打开当前页面路径中的参数.
- onShow()页面显示/切入前台时触发
- onReady()页面初次渲染完成时触发.一个页面只会调用一次,代表页面已经准备妥当,可以和视图进行交互.
- onHide()页面隐藏/切入后台时触发.如navigateTo或底部tab切换到其它页面,小程序切入后台等.
- onUnload()页面卸载时触发.如redirectTo或navigateBack到其它页面时.
简述下 wx.navigateTo(), wx.redirectTo(), wx.switchTab(), wx.navigateBack(), wx.reLaunch()的区别
- wx.navigateTo():保留当前页面,跳转到应用内的某个页面.但是不能跳转到tabbar页面.
- wx.redirectTo():关闭当前页面,跳转到应用内的某个页面.但是不允许跳转到tabbar页面.
- wx.switchTab():跳转到tabBar页面,并关闭其它所有非tabBar页面.
- wx.navigateBack():关闭当前页面,返回上一页面或多级页面.可通过getCurrentPages()获取当前的页面栈,决定需要返回几层.
- wx.reLaunch():关闭所有页面,打开到应用内的某个页面.
bindtap和catchtap的区别
相同点: 点击事件函数,点击时触发.
不同点:主要是bindTap是不会阻止冒泡事件的,catchtap是阻止冒泡的.
小程序关联微信公众号如何确定用户的唯一性
同一个微信开放平台账号下的移动应用,网站应用和公众号账号(包括小程序),用户的unionid是唯一的.同一用户,对同一个微信开放平台下的不同应用,unionid是相同.