一、框架+库相关
1. vue:一个采用 MVVM 模式, 通过数据驱动视图的形式来构建用户界面的渐进式框架.
vue是一个构建用户界面的渐进式框架,代码简洁,逻辑清晰;帮我们省略了dom操作,代码量减少很多;vue专注于数据:用户只需要关注dom元素值对应绑定的数据,修改dom只需要修改数据就行了。
总结:
- 构建用户界面的渐进式框架,代码简洁,逻辑清晰;
- 使用vue可以避免操作dom数,直接操作数据就行了,MVVM数据驱动视图
- 最大的优势:组件化+数据驱动,把很多元素封装成组件,功能的开发变成了组件的拼装
2. vue全家桶有哪些内容
vue全家桶 =vue+ vue-router (路由)+vuex(状态管理)+vue-resource+axios +构建工具 vue-cli(脚手架)+UI(Element UI)
概括起来就是:、1.项目构建工具、2.路由、3.状态管理、4.http请求工具。
vue-router:vuex主要由五部分组成:state action、mutation、getters、mudle组成。
vuex:
vue-cli:是一个脚手架,能帮助开发快速创建一个vue项目。
axios:axios是一个前端异步执行http请求的ajax框架。
Element-ui
3. Bootstrap
一个前端技术的开源工具包。虽然bootstrap提供了js库,但是很多项目更多的主要是使用了它的css样式库。
4. jQuery:js库
- javascript、JQuery、AJAX、JSON这四个之间的关系?
(1)JavaScript 为页面提供更多功能,是页面交互功能的基础语言。此外它的语言规范和引擎还被用于其他领域,比如 Node 等。
(2)jQuery 屏蔽了浏览器之间的兼容性问题,针对常用功能封装了大量的 API,并支持插件机制,让你写 JS 的效率很高,质量很好。
(3)Ajax:无刷新的异步请求。 技术提供了一种新的前后端数据交互方式,不需要刷新页面,而且不阻塞页面执行流程,异步的去请求去获取、交互数据。
(4)JSON 用来描述前后端数据交互的内容格式,有了 JSON 这样的一套统一的描述规则,前后端解析数据的成本变低,使用非常简单。JSON 属于 JavaScript 的一个子集。
二、HTML+CSS+JS
1. Webpack
- webpack是JavaScript应用程序的静态模块打包器(module bundler)。它做的事情是,分析你的项目结构,找到JavaScript模块以及其它的一些浏览器不能直接运行的拓展语言(Scss,TypeScript等),并将其转换和打包为合适的格式供浏览器使用。
2. Node.js
- 运行在服务端的JavaScript。是一个基于Chrome V8 JavaScript引擎的JavaScript运行环境。在node.js出现之前,JavaScript是只能在浏览器中运行的,Node.js的出现可以让开发像写python一样在命令行写JavaScript。
3.Git
- 合作管理文档
4.npm
- 包管理工具
三、技术相关
1.SPA的理解,优缺点
页面初始化时加载css+html+js,一旦页面加载完成,SPA不会因为用户操作重新加载,随后路由机制接管内容变化+UI与用户交互
**优点:**快,避免不必要的跳转和重复渲染;服务器压力下,前后端职责分开,前只管交互逻辑,后管数据处理
**缺点:**初次加载耗时多;前进后退路由管理不行;SEO难度大
2.v-show与v-if区别
v-if真正的条件渲染,初始渲染时条件为假不渲染
**v-show:**不管初始是什么,元素都会被渲染,简单的基于css的display属性切换(none/block)
区别:v-if 需要更高的切换开销,适用于运行时很少改变条件,不频繁切换条件时用;v-show 需要更高的初始渲染开销,适用于需要经常切换条件的场景
3.闭包是什么?
- 目的是外部函数可以访问内部函数的作用域(局部作用域)
4.Vue生命周期的理解
总共分为8个阶段创建前/后,载入前/后,更新前/后,销毁前/后。
创建前/后: 在beforeCreate阶段,vue实例的挂载元素el和数据对象data都为undefined,还未初始化。在created阶段,vue实例的数据对象data有了,el还没有。
载入前/后:在beforeMount阶段,vue实例的$el和data都初始化了,但还是挂载之前为虚拟的dom节点,data.message还未替换。在mounted阶段,vue实例挂载完成,data.message成功渲染。
更新前/后:当data变化时,会触发beforeUpdate和updated方法。
销毁前/后:在执行destroy方法后,对data的改变不会再触发周期函数,说明此时vue实例已经解除了事件监听以及和dom的绑定,但是dom结构依然存在
(1)、什么是vue生命周期
答: Vue 实例从创建到销毁的过程,就是生命周期。也就是从开始创建、初始化数据、编译模板、挂载Dom→渲染、更新→渲染、卸载等一系列过程,我们称这是 Vue 的生命周期。
(2)、vue生命周期的作用是什么
答:它的生命周期中有多个事件钩子,让我们在控制整个Vue实例的过程时更容易形成好的逻辑。
(3)、vue生命周期总共有几个阶段
答:可以总共分为8个阶段:创建前/后, 载入前/后,更新前/后,销毁前/销毁后
(4)、第一次页面加载会触发哪几个钩子
答:第一次页面加载时会触发 beforeCreate, created, beforeMount, mounted 这几个钩子
(5)、DOM 渲染在 哪个周期中就已经完成
答:DOM 渲染在 mounted 中就已经完成了。
(6)、简单描述每个周期具体适合哪些场景
答:生命周期钩子的一些使用方法:
beforecreate : 可以在这加个loading事件,在加载实例时触发
created : 初始化完成时的事件写在这里,如在这结束loading事件,异步请求也适宜在这里调用
mounted : 挂载元素,获取到DOM节点
updated : 如果对数据统一处理,在这里写上相应函数
beforeDestroy : 可以做一个确认停止事件的确认框
nextTick : 更新数据后立即操作dom
组件创建:
- BeforeCreate:刚初始化了一个实例对象,data和methods都还没有初始化
- Create:data和methods已经被初始化了
挂载:
- BeforeMount:相关的render函数首次被调用,模板已经在内存中编译好,但没有挂载到页面中
- Mounted:整个vue实例一家初始化完毕
更新:
- beforeUpdate:dom树改了还不是最新的数据
- updated:最新的数据,页面和数据一致
销毁:
- BeforeDestroy:此时所有的deta和methods还可以用
- Destroyed:数据方法,指令,过滤器都不能用了
组件激活:
- keep-alive专属,组件被激活时使用
- keep-alive专属,组件被销毁时调用
5.Vue数据双向绑定原理
利用数据劫持和发布者订阅者模式,Vue 内部通过 Object.defineProperty方法属性拦截的方式,把 data 对象里每个数据的读写转化成 getter/setter,当数据变化时通知视图更新。
具体是4个步骤:
-
实现一个监听器 Observer:对数据对象进行遍历,包括子属性对象的属性,利用数据劫持Object.defineProperty() 对属性都加上 setter 和 getter。这样的话,给这个对象的某个值赋值,就会触发 setter,那么就能监听到了数据变化。
-
实现一个解析器 Compile:解析 Vue 模板指令,将模板中的变量都替换成数据,然后初始化渲染页面视图,并将每个指令对应的节点绑定更新函数,添加监听数据的订阅者,一旦数据有变动,收到通知,调用更新函数进行数据更新。
-
实现一个订阅者 Watcher:Watcher 订阅者是 Observer 和 Compile 之间通信的桥梁 ,主要的任务是订阅 Observer 中的属性值变化的消息,当收到属性值变化的消息时,触发解析器 Compile 中对应的更新函数。
-
实现一个订阅器 Dep:订阅器采用 发布-订阅 设计模式,用来收集订阅者 Watcher,对监听器 Observer 和 订阅者 Watcher 进行统一管理。
6.vue框架实现对对象和数组的监听
Object.defineProperty() 只能对属性进行数据劫持,不能对整个对象进行劫持,同理无法对数组进行劫持; Vue 框架是通过遍历数组 和递归遍历对象,从而达到利用 Object.defineProperty() 也能对对象和数组(部分方法的操作)进行监听。
7.proxy和Object.defineProperty()
数据劫持的方式由Object.defineProperty更改为Proxy代理,其他代码不变;
- Object.defineProperty缺点:
(1)无法监控到数组下标的变化,导致直接通过数组的下标给数组设置值,不能实时响应。
(2)只能劫持对象的属性,因此我们需要对每个对象的每个属性进行遍历,不能直接劫持对象和数组 - Proxy优点:
(1) 可以劫持整个对象,并返回一个新对象
(2) 有13种劫持操作,defineProperty没有
8.vm.$set()解决了什么问题
只有在data中已经存在的属性才会被Observe为响应式数据,直接this.xxx[xxx] = xxx,在data里添加一个数据,vue是不能侦测到的,解决办法就是vm.$set();
- 实现原理:目标是数组:使用splice方法触发相应式;目标是对象,先判断属性是否存在,对象是否是相应式–>调用defineReactive方法对属性进行响应式处理-----efineReactive 方法就是 Vue 在初始化对象时,给对象属性采用 Object.defineProperty 动态添加 getter 和 setter 的功能所调用的方法
9.虚拟DOM是什么+优缺点+实现原理
Visual DOM :普通的JS对象,包含tag,props,children;
优点:抽象了原本的渲染过程,实现了跨平台的能力,而不仅仅局限于浏览器的 DOM;
diff 算法,减少 JavaScript 操作真实 DOM 的带来的性能消耗
- 用js对象模拟真实DOM树,对真实DOM进行抽象
- diff算法 比较两颗DOM树的差异
- pach算法 将两个虚拟DOM对象的差异应用到真正的DOM树,从而呈现对应视图
10.有用过什么调试工具
- FunDebug:
安装fundebug-javascript 与 fundebug-vue
npm install fundebug-javascript fundebug-vue --save
配置:
import * as fundebug from "fundebug-javascript";
import fundebugVue from "fundebug-vue";
fundebug.init({
apikey: "API-KEY"
})
fundebugVue(fundebug, Vue);
11.Vue响应式原理
将预先配置好的data通过observer进行数据拦截或数据代理(proxy),获得getter和setter的能力,data中的每一个属性都有一个依赖dep,每一个依赖都有若干个wachter进行监视,当data中的属性改变时,通知给dep,dep再通知给watcher,watcher就从对应的data中拿到值后渲染到页面上
12.watch和computed , computed 和 watch 有什么区别及运用场景?
- Watcher :观察者对象,实例分为渲染(render)watcher,计算属性(computed)watcher,侦听器(user)watcher
- 区别:computed 计算属性 : 依赖其它属性值,并且 computed 的值有缓存,只有它依赖的属性值发生改变,下一次获取 computed 的值时才会重新计算 computed 的值。watch 侦听器 : 更多的是「观察」的作用,无缓存性,类似于某些数据的监听回调,每当监听的数据变化时都会执行回调进行后续操作。
- 运用场景:当我们需要进行数值计算,并且依赖于其它数据时,应该使用 computed,因为可以利用 computed 的缓存特性,避免每次获取值时,都要重新计算。当我们需要在数据变化时执行异步或开销较大的操作时,应该使用 watch,使用 watch 选项允许我们执行异步操作 ( 访问一个 API ),限制我们执行该操作的频率,并在我们得到最终结果前,设置中间状态。这些都是计算属性无法做到的。
13.Vue 组件 data 为什么必须是函数,而new Vue()实例中data可以直接是一个对象
- 因为组件是可以复用的,JS 里对象是引用关系,如果组件 data 是一个对象,那么子组件中的 data 属性值会互相污染,产生副作用。而实例化对象中是不会被复用的,音系不存在上面问题
14.Vue的渲染过程
- 调用 compile 函数,生成 render 函数字符串 ,编译过程如下:
(1)parse 函数解析 template,生成 ast(抽象语法树);(2)optimize 函数优化静态节点 (标记不需要每次都更新的内容,diff 算法会直接跳过静态节点,从而减少比较的过程,优化了 patch 的性能);(3)generate 函数生成 render 函数字符串 - 调用 new Watcher 函数,监听数据的变化,当数据发生变化时,Render 函数执行生成 vnode 对象;
- 调用 patch 方法,对比新旧 vnode 对象,通过 DOM diff 算法,添加、修改、删除真正的 DOM 元素
15.Vuex的工作原理
- vuex 有哪几种属性:有 5 种,分别是 state、getter、mutation、action、module
- vuex 中的 store 本质就是没有 template 的隐藏着的 vue 组件;
16.axios 是请求后台资源的模块
17.组件通信的方式,父子是哪些,兄弟间哪些,跨级有哪些
- props/$emit
- ‘$children/$parent
- $attrs/$listeners
- vuex
- provide/inject
- ref
- eventBus
18.v-if和v-show异同
-
相同点:v-if与v-show都可以动态控制dom元素显示隐藏
-
不同点:v-if显示隐藏是将dom元素整个添加或删除,而v-show隐藏则是为该元素添加css–display:none,dom元素还在。
-
适应:
-
v-if有更高的切换消耗;v-show有更高的初始渲染消耗;
-
v-if适合运营条件不大可能改变;v-show适合频繁切换。
19.webpack打包模块的源码 执行顺序
- 1:把所有模块的代码放入到函数中,用一个数组保存起来
- 2:根据require时传入的数组索引,能知道需要哪一段代码
- 3:从数组中,根据索引取出包含我们代码的函数
- 4:执行该函数,传入一个对象 module.exports
- 5:我们的代码,按照约定,正好是用module.exports = ‘xxxx’ 进行赋值
- 6:调用函数结束后,module.exports从原来的空对象,就有值了
- 7:最终return module.exports; 作为require函数的返回值
20. 列表循环中v-for为什么加key?
- vue组件高度复用增加Key可以标识组件的唯一性,为了更好地区别各个组件 key的作用主要是为了高效的更新虚拟DOM
21.vue 指令系统
- v-on 指令添加一个事件监听器 简写@
- v-bind 动态地绑定一个或多个 attribute,或一个组件 prop 到表达式。
- v-for v-for 指令基于一个数组来渲染一个列表,遍历数据
- v-model 双向数据绑定 只能作用在form表单上
22.keep-alive
vue的内置组件,能在组件切换过程中将状态保留在内存中,防止重复渲染DOM。
23.webpack中的css-loader 和style-loader
-
webpack打包的时候遇到css文件,会用loaders解决
-
遇到后缀为.css的文件,webpack先用css-loader加载器去解析这个文件
-
最后计算完的css,将会使用style-loader生成一个内容为最终解析完的css代码的style标签,放到head标签里。
-
webpack在打包过程中,遇到后缀为css的文件,就会使用style-loader和css-loader去加载这个文件。
而webpack4不能用loaders,改用rules
module:{/声明模板 包含各个loader/
loaders:[
{
test:/.css$/,
loader:‘style-loader!css-loader’
}
]
},
24. webpack中对文件的处理,得需要url-loader和file-loader
简易,对于图片的大小小于limit设置的大小的图片,使用base64编码,可以减少一次图片的网络请求;那么对于比较大的图片,使用base64就不适合了,编码会和html混在一起,一方面可读性差,另一方面加大了html页面的大小,反而加大了下载页面的大小,得不偿失了呢,因此设置一个合理的limit是非常有必要的。
25.webpack的热加载 组件的懒加载
- 路由懒加载:结合 Vue 的异步组件和 Webpack 的代码分割功能,轻松实现路由组件的懒加载。
有三种异步方式
- 1.resolve => require([URL],resolve)
- 2.()=>system.import(URL),webpack官网不推荐使用
- 3.()=>import(URL),属于es7范畴,要配合babel的syntax-dynamic-import插件使用。
优点: - 懒加载的元素将会延迟加载,这里的延迟指的是会出现滞后于其他同步元素的出现,甚至会出现页面闪一下的过程
- 如果一个页面中有许多嵌套的懒加载组件,那么网页中的元素会分先后渲染,层次不齐
建议: - 对于路由页面,最好进行懒加载;
- 对于路由页面中的各个组件实行按需进行懒加载,
vue懒加载的流程:
根页面提供router注入方法–>调用根页面注入方法–>store中存储routers–>取出routers进行动态组件匹配
JavaScript面试题
1.写一个少于 80 字符的函数,判断一个字符串是不是回文字符串
function isPalindrome(str) {
str = str.replace(/\W/g, '').toLowerCase();
return (str == str.split('').reverse().join(''));
}
var a = isPalindrome("aBgdsajdo");
console.log(a);
var b = isPalindrome("ismeemsi");
console.log(b);