文章目录
Webpack面试题
webpack打包原理、基本功能、构建过程
打包原理
将所有的依赖打包成一个bundle.js,通过代码分割成单元片段按需加载
基本功能
- 代码转换:将ts转js,scss转css,
- 文件优化:压缩js、css、html,代码,合并图片
- 代码分割:提取多个页面的公共代码,提取首屏不需要执行的部分代码,让其异步加载
- 模块合并:采用模块化的项目中,有很多模块很文件,需要构建功能吧模块分类合并成为一个文件
- 自动刷新:坚挺本地源代码的变化,自动构建、刷新浏览器
- 代码校验:代码提交到仓库前,检测代码是否符合规范,以及单元检测是否通过
- 自动发布:更新完成代码后,自动构建出线上发布代码传输给发布系统
构建过程
- 从entry里面配置的module开始递归解析entry依赖的所有module
- 没找到一个module,就会根据配置的loader去找对应的规则
- 对module进行转换,解析出当前module依赖的module
- 这鞋模块会以entry为单位分组,一个entry和其所有依赖的module被分到一个组Chunk
- 最后webpack会把所有的Chunk转换成文件输出
⚠️webpack会在恰当的时候执行plugin里的逻辑
常见loader
- file-loader: 把文件输出到文件夹中
- url-loader: 和file-loader类似,能在文件小的情况下以base64的方式把文件注入到代码中去
- source-map-loader:加载额外的source map 文件,方便断掉调试
- image- loader:加载并压缩图片文件
- css-loader:加载css,支持模块化、压缩、文件导入等
- style-loader:把css代码注入到js中,通过DOM操作去加载css
- eslint-loader:通过eslint检查js代码
- babel-loader:将ES5转为ES6
常见plugin
- define-plugin:定义环境变量
- commons-chunk-plugin:提取公共代码
- uglifyjs-webpack-plugin:通过uglifyES压缩ES6代码
- html-webpack-plugin 压缩html
- clean-webpack-plugin 打包清理元目录文件,webpack打包器清理dist目录
什么是bundle,chunk,module?
- bundle webpack打包出来的文件,
- chunk webpack在进行模块的分析时,代码分割出来的代码块
- module 开发中单个的模块
webpack热更新
- 使用本地启动本地服务,当浏览器访问资源时对此作出响应,
- 服务端和客户端使用websocket连接,
- webpack监听原文件的变化,当文件保存时触发webpack的重新编译
- 每次编译都会生成一个hash值,已改动的JSON文件,已改动的模块化代码
- 编译完成后,通过websocket像客户端推送当前编译的hash戳
- 客户端的websocket监听的文件改动推送来的hash戳,会和上一次做对比,
- 一致则缓存
- 不一致则通过ajax或jsonp向服务端获取最新的资源
- 使用内存文件系统去掉患有修改的内容实现局部刷新
VUE面试题
MVC、MVP、MVVM的理解
MVC:model(模型)、view(视图)、controller(控制器);处理逻辑:view触发事件,controller响应并处理逻辑,调用model,model处理完成后发送给view;
MVP:model、view、present(发布层)理想模型,present为核心,从model获取数据,填充到view;
MVVM:model、view、VM(viewModel);view与VM保持同步,view绑定到VM的属性上,如果VM的数据变化,通过数据绑定的方式,view会自动更新视图,VM也会暴露在Model中,VM也就是new的Vue实例;
VUE生命周期函数
1)创建阶段
-
beforCreate:实例创建前,
-
created:实例创建好,data和methods创建好,可做数据请求;
2)挂载阶段
- beforMount:DOM已经存在,
- mounted:模版中的HTML渲染在页面中,可做Ajax请求,mounted只会执行一次;
3)更新
- beforeUpdate:发生在数据更新之前,
- undated:更新后,大多数情况应该避免此期间更改状态,因为这可能导致更新的无限循环。该钩子在服务器端渲染是不会被调用的
4)销毁
-
beforDestory:在此清楚定时器和绑定的事件;
-
destoryed:调用后所有事件监听器会被移除,所有的子实例也会被销毁
VUE路由
vue-router
vue的插件,含有router-link router-view 组件,
-
router-link:
<router-link to="路由地址" tag="渲染为什么标签"></router-link>
-
active-class:router-link组件的属性,直接在路由中配置
linkActiveClass:'active'
,在router-link标签中写入exact<router-link to="" active-class="" exact></router-link>
vue-router的导航钩子
全局的:
- beforEach
.boforEach((to, from, next) => {})
- afterEach
.afterEach((to, from, next)v=>{})
单个路由独享:
- beforEnter
- beforLeave
组件的:
- beforRouterEnter
- beforRouterUpdate
- beforRouterLeave
$router 和 $route区别
-
router为vue-router的实例,相当于一个全局路由器对象,包含很多对象和属性。
-
route相当于正在跳转的路由对象,可以从中获取参数
vue-router 传参
-
params方式传参
// 传参 this.$router.psuh({ name: '', params: { id: id } }); // 获取 this.$route.params.id
-
query方式传参
// 传参
this.$router.psuh({
path: '',
query: {
id: id
}
});
// 获取
this.$route.query.id
⚠️query相当于get,参数会拼接在url上,params相当于post
vue-router的两种模式(hash history)
-
hash模式
原理是
window.onhashchange
事件,hash值就是#后面的值 -
history模式
H5中History Interface 中新增的pushState()和replaceState()方法,需要后端配置支持,没有正确配置访问时就会返回404,丢弃了不好看的#,但是如果服务器没有正确的响应或资源,就会出现404,所以history不怕前进后端,就怕刷新
vue-router实现路由懒加载
为了好的用户体验,首屏组件加载更快,解决白屏问题,将页面划分为需要的时候再加载,减少首页加载压力。
常用的懒加载方法:
-
ES提出的import 方法(最常用)
const 路由组件名 = () => import(需要加载的模块地址)
const Holle = () => import('@...')
-
VUE异步组件实现
component: resolve => (require(['需要加载的路由地址']), resolve)
routes: [ { path: '/', name: 'Holle', component: resolve => (require(["@/component/HelloWord"], resolve)) } ]
VUE 组件传值
-
父组件传值给子组件
使用props
- props为数组,直接传递[data, flag]
- props为对象,直接对象类型或者给一个默认值
-
子组件传值给父组件
-
子组件使用
this.$emit('自定义事件名', 参数)
-
父组件
<parent-component @自定义事件="方法"></parent-component>
,此时方法不能加()。传值为子组件传的值
-
-
兄弟组件传值
- 中央事件总线,eventBus
const bus = new Vue(); bus.$emit(自定义事件名, 传递的数据); // 接收数据的地方 bus.$on(自定义事件名, function (val) {})
- 使用VueX
-
父组件访问子组件中的方法
组件上添加ref属性,给组件设置一个ID,然后通过
this.$ref.id名.方法/值
-
使用依赖注入
父组件通过provide(函数)注入,所有的子组件通过inject(数组)引用,
// 父组件 provide: function () { return { getName: this.getName } } // 子组件 inject: ['getName']
-
p a r e n t , t h i s . parent,this. parent,this.parent 可以直接访问到组件上的data数据和方法
-
sessionStorage传值
vue中computed和watch、method区别
- methods: 定义function的区域
- computed: 一定程度和methods差不多,不过计算属性基于属性的,属性的变化跟着变化,不变化就不会调用computed;具有get 、set两个方法
- watch: 侦听属性。监听某个值的变化,
- 回调handler(newVal, oldVal),
- deep: true ; 深度监听
- Immediate: true;立即执行,场景:组件创建的时候立即获取一次列表数据,同时监听框,每当发生变化的时候重新获取一次筛选后的数据列表
VUE双向数据绑定的原理
Vue 采用数据劫持 + 发布订阅模式。通过Object.defineProperty()方法为组件中每个data添加get、set方法
原理:通过Object.defineProperty()劫持各个属性中的getter、setter,在数据变化时,发布消息给订阅者,触发相应的监听回调来渲染视图
- 需要Observer(观察者)对数据对象进行递归遍历,给每个属性都加上getter、setter
- 模板解析器complie解析模板指令,将模板中每个变量替换为数据,然后初始化渲染页面视图,并给每个指令对应的几点更新函数,添加数据的监听者,一旦数据变化,收到通知,改变视图
- watcher订阅者,在observer和complie之间做中介,
VUEX
集中组件状态管理
- state,基本数据
- getters,从state中派生的数据
- mutation,跟新store唯一途径,
- action,提交mutation以改变store
- module,将store模块化
VUE插槽
<slot></slot>
单向数据流
组件之间的数据传递是具有单向下行绑定的,从父组件流向子组件。是不能在子组件直接改传过来的props的值的。
子组件需要改变传过来的值为自己所用?
- 定义一个局部变量来接收props传来的值
- 使用计算属性,修改父组件传来的数据
子组件修改父组件中的数据?
-
使用async修饰符,他会被扩展为一个自动更新父组件属性的v-on监听器,被移除
// 父组件 <Hello :message.async></Hello> // 子组件 props: ['message'], methods: { change () { this.$emit('update.message', 修改的值) } }
-
将父组件中的数据包装成对象或数组,子组件中直接修改对象的属性。
s e t 、 set、 set、forceUpdate?
data数据为数组或者对象的时候,直接给某个对象添加属性,页面识别不到。
- 使用this.$forceUpdate 强制刷新;
- 使用this.$set 设置对象的值
JS面试题
面向对象
把事物分解成为一个个对象,然后对对象之间的分工合作。分析出对象只能够的属性和方法,按照步骤编程器。
浏览器缓存
-
强缓存
-
expires
请求头设置时间,
-
cache-control
-
-
协商缓存
虚拟列表
前端处理数据,防止页面卡顿,根据显示窗口计算出现实条数,然后头部和底部使用空白(padding-top,padding-bottom)填充滚动区域
事件委托的原理
通过冒泡,e.target来判断是谁触发
判断数组的方法?
-
instanceof,用法
arr instanceof Array
主要是判断某个实例是否属于某个对象
-
constructor, 用法
arr.constoructor === Array
Object的每个实例都有构造函数constructor,用于保存创建当前对象的函数
-
Array.isArray()
-
Array原型链上的isPrototypeOf,
Array.prototype.isPrototypeOf(arr)
-
Object.getPrototypeOf, 用法
Object.getPrototypeOf(arr) === Array.proptotype
-
Object.prototype.toString(),用法
Object.prototype.toString.call(arr) === 'object Array'
判断对象的方法?
-
toString,
Object.prototype.toString.call(obj) === '[object Object]'
-
constructor
obj.constructor === Object
-
instanceof
obj instanceof Object
-
typeof 不是很准确
typeof obj === Object
数组去重
-
使用indexOf,
function unique (arr) { let brr = []; for (let i = 0; i < arr.length; i++) { let item = arr[i]; if (brr.indexOf(item) === -1) { brr.push(item) } } return brr; }
-
set
// 一: let arr = [...new Set(arr)] // 二 function unique (arr) { return Array.from(new Set(arr)) }
-
Map
function unique (arr) { const res = new Map(); return arr.filter((a) => !res.has(a) && res.set(a, 1)) }
事件循环
同步完成后执行异步程序,
顺序:同步——异步——process.nextTick——异步——setImmediate(当前事件循环后执行)
同步代码放入运行栈,异步放入任务队列中,
事件循环就是检查任务队列中是否有任务等待执行,
宏任务、微任务
微任务:promise.then、Object.observe、MutationObser
宏任务:script(整体代码)、setTimeout、setInterval、I/O、UI Rendering、计时器、ajax、读取文件
优先级:process.nextTick > promise.then > setTimeout > setImmediate
顺序:
1、同步程序
2、process.nextTick
3、微任务
4、宏任务
5、setImmediate
process.nextTick
同步执行完成后异步开始之前执行
setImmediate
异步完成之后在执行
函数截流函数防抖
函数截流:一定时间内只执行一次
function throttle (fn, threshold, scope) {
let timer;
return function () {
let context = scope || this, args = angument;
if (!timer) {
timer = setTimeout(function () {
fn.apply(context, args)
}, threshold)
}
}
}
函数防抖:一定的事件操作,只执行最后一次操作
function debounce (fn, delay, scope) {
let timer = null;
return function () {
let context = scope || this, args = arguments;
clearTimeout(timer);
timer = setTimeout(function () {
fn.apply(context, args);
}, delay)
}
}
New 关键字做了什么
创建了一个对象,将构造函数的this指向创建出来的对象
promise、async
promise的原理就是发布订阅模式,通过两个队列缓存的成功回调(onResolve)和失败的回调(onReject)。promise的状态有三种,pending、rejected、fulfilled,状态改变只能是pending --> fulfilled 或者 pending --> rejected。
链式调用
then中创建的promise,。当一个promise的状态被fulfilled的之后,会还行回调函数,回调函数返回的结果会被当作value,返回给下一个Promise(then中的promise),下一个promise也会被改变(执行resolve或reject),然后又执行其回调,以此类推,就形成了链式
前端优化
重绘和回流(重排)
回流: 重新布局当元素的规模尺寸、布局、隐藏等改变而需要重新构建页面。
重绘: 一些属性的更新,影响元素的外观,风格但是不会影响布局。如background-color
回流一定会重绘,重绘不一定回流
解决方案:
- 将需要多次重排的元素,position设置为absolute或者fixed,脱离文档流,不会影响其他元素的布局。
深拷贝浅拷贝
Object.assign()只能实现一维对象的深拷贝
垃圾回收
-
引用计数:声明一个变量时,给他赋值时就引用计数1次,去另一个值时,就计数减去1,为0 的就清除;
注意:遇到循环调用的,会形成引用次数永远为2
-
标记清除:运行时,垃圾回收器给所有的变量加标记,进入环境的时候加标记,出去的时候去掉
跨域问题
同原策略:域名、协议、端口
-
cors资源共享跨域
后台加上相应头来允许域请求
-
Jsonp跨域
-
document.domin,(iframe和cookie跨域)
处理Cookie和iframe,两个二级域名不一致的,通过共享cookie
-
通过websocket进行跨域,同源策略对其不生效
-
图片ping
instanceof 、typeof、constructor区别
- instaceof,检测狗站函数的prototype属性是否出现在某个实例对象的原型上
- typeof,只能检测简单数据类型,不能应用于复杂数据类型
- constructor,每一个js函数都有一个原型对象,原型对象里有一个constructor指向属性的构造器(fn)