js相关

let和const

let es6新增了let声明变量的方式,和var类似

let 变量

const用来声明常量

和var相比 就是 不允许变量提升,存在暂时性死区,不允许重复声明,块级作用域

解构赋值

1
2
3
4
5
6
7
8
9
// 数组,使用的时候名字可以任意
const [a,b,c,d] = [1,2,3,4]

// 对象,解构默认定义对象中的key
const {name, id} = {
name: '张三',
id: 19
}

模版字符串

1
2
3
//方便了字符串的拼接,使用反引号语法``,反引号内部可以通过${}插入表达式
const name = '张三'
const msg = `hello${name}`

字符串的 includes

1
2
3
//用来查找一个字符串中是否包含另一个字符串,返回值为布尔
const name = 'hello 张三'
console.log(name.includes('ll'))

字符串的 startsWith endsWith

用来判断某字符串是否以某字符串开头 用来判断某字符串是否以某字符串结尾

函数-尖头函数

1
2
3
4
5
6
7
function fn() {}

const fn = () => {}

//注意:
//尖头函数花括号可以省略,省略后会默认return
//尖头函数中没有绑定this,this指向上下文

函数-形参默认值 函数-形参解构 函数-形参剩余参数

1
2
3
4
5
6
7
8
9
10
11
12
13
 //函数-形参解构
function fn(a = 1,b =2) {
return a + b
}

console.log(fn()) // 3

//函数-形参剩余参数
function fn(a, ...values) {
console.log(values) // [2,3,4]
}

fn(1,2,3,4)

扩展运算符

1
2
3
4
5
const a = [1, 2, 3, 4, 5]

const b = [...a]

// 可以用于对象

对象简写

1
2
3
4
5
const a = 1
const obj = {
a
}

对象方法简写

1
2
3
const obj = {
fn() { }
}

对象key-使用表达式

1
2
3
4
5
6
let name = '张三'
let key = 'name'
const obj = {
[key]: name
}

Object.assign

相当于一个浅拷贝 拷贝的是地址

Object.create

创建一个对象,并将对象的proto指向参数对象

1
2
3
4
5
6
7
const source = { b: 4, c: 5, obj: { a: 1 } };
const obj = Object.create(source)

console.log(obj)

//可以创建一个没有原型的对象
Object.create(null)

Promise

ES6模块化: 规范

commonJs模块

导出:module.exports

导入: require()

其实就是把不同的功能 分配不同js文件

web过渡时期的产品

AMD: AMD加载完模块后,就立马执行该模块 代表(require.js)

CMD:CMD加载完某个模块后没有立即执行而是等到遇到require语句的时再执行(Sea.js)

web现在流行es6

1
2
3
// 单纯的运行某个文件
import './router.js'

默认导出,默认导入

1
2
3
4
5
6
7
8
9
10
11
12
console.log('配置路由')

const obj = { name: '张三', age: 19 }

export default obj


// 默认导入: 名字任意
import obj from './router.js'

Copied!

注意: ⚠默认导出只能使用一次,所以也就是为什么默认引入可以使用任意名字接收

按需导出,按需导入

1
2
3
4
5
6
7
8
9
export const a = 1

export const b = 2

// 名字要和导出的名字一致
import { a, b } from './router.js'

Copied!

注意:⚠导入的名字默认必须要和导出的名字一致,并用花括号括起来

按需导入可以起别名

1
2
3
4
import { a as a1, b } from './router.js'
console.log(a1)
console.log(b)

同时引入默认和按需

1
import user, { b, c } from './router.js'

注意:⚠语法必须为import 默认导入, {}按需导入 from ‘路径’

全部引入

1
import * as moduleRouter from './router.js'

注意:⚠返回值是一个对象,按需导出的变量添加到对象中的key,值会成为对象中的value,默认导出的key固定为default,值为导出的对象

null 和 undefind的区别

简单区分

总的来说 nullundefined 都代表空,主要区别在于 undefined 表示尚未初始化的变量的值,而 null 表示该变量有意缺少对象指向。

  • undefined

    • 这个变量从根本上就没有定义
    • 隐藏式 空值
  • null

  • 这个值虽然定义了,但它并未指向任何内存中的对象

    • 声明式 空值

vue相关

1.Vue的优点? vue的缺点?

优点: 渐进式,组件化,轻量级,虚拟dom,响应式,单页面路由,数据与视图分开
缺点: 单页面不利于seo,不支持IE8以下,首屏加载时间长

2.为什么说Vue是一个渐进式框架?

渐进式: 就是,你想用啥你就用啥,咱也不强求你。你想用vuex就用,不用也行,你想用component就用,不用也可以,或者你可以选择不用,或者只选几样去用

3. Vue跟React的异同点?

相同点:

  • 都使用了虚拟dom
  • 组件化开发
  • 都是单向数据流(父子组件之间,不建议子修改父传下来的数据) 4.都支持服务端渲染 不同点:
  • React的JSx, Vue的template
  • 数据变化,React手动(setState),Vue自动(初始化已响应式处理,Object.defineProperty)
  • React单向绑定,Vue双向绑定·4.React的Redux,Vue的Vuex

4.MVVM是什么?和MVC有何区别呢?

Mvc

  • Model(模型):负责从数据库中取数据.
  • View(视图):负责展示数据的地方
  • Controller(控制器):用户交互的地方,例如点击事件等等·思想:Controller将Model的数据展示在View 上

MVVM

VM: 也就是View-Model,数据的双向绑定将【模型】转化成【视图】,即后端传递的数据转化成所看到的页面。实现的方式是:数据绑定。二是将【视图】转化成【模型】,即将所看到的页面转化成后端的数据。实现的方式是:DOM事件监听。

区别

整体看来,MVVM 比MVC精简很多,不仅简化了业务与界面的依赖,还解决了数据频繁更新的问题,不用再用选择器操作DOM元素。因为在MVVM 中,View不知道 Model的存在,Model和ViewModel也观察不到View,这种低耦合模式提高代码的可重用性 Vue是不是MVVM框架? Vue是MVVM框架,但是不是严格符合MVVM,因为MVVM规定Model和View不能直接通信,而Vue的ref可以做到这点

5. Vue和JQuery的区别在哪?为什么放弃JQuery用Vue?

  • jQuery是直接操作DOM,Vue不直接操作DOM,Vue的数据与视图是分开的,Vue只需要操作数据就行
  • jQuery的操作DOM行为是频繁的,而Vue利用虚拟DOM的技术,大大提高了更新DOM时的性能
  • Vue中不倡导直接操作DOM,开发者只需要把大部分精力放在数据层面上
  • Vue集成了一些库,大大提高开发效率,例如Route、Vuex等等

. 为什么data是一个函数 而不是对象

  • 之所以data是一个函数,是因为一个组件可能会多处调用,而每一次调用就会执行data函数并返回新的数据对象,这样,可以避免多处调用之间的数据污染。
  • 简单理解就是 防止组件被多个页面使用时,造成的变量互相污染

Vue的生命周期,有哪些?

  • beforeCreate:实例话Vue,未初始化和响应式数据
  • created:已初始化和响应式数据,可访问数据
  • beforeMount:render调用,虚拟DOM生成,未转真实DOM
  • mounted:真实DOM挂载完成
  • beforeUpdate:数据更新,新虚拟DOM生成
  • updated:新旧虚拟DOM进行对比,打补丁,然后进行真实DOM更新
  • beforeDestroy:实例销毁前,仍可访问数据
  • destroy:实例销毁,子实例销毁,指令解绑,解绑本实例的事件
  • activated:keep-alive所缓存组件激活时调用
  • deactivated:keep-alive所缓存组件停用时调用
  • errorCaptured:子孙组件的错误捕获,此函数可返回false阻止继续向上传播

. 使用过哪些Vue的修饰符呢?

  • .once:事件只触发一次
  • .number:将v-medol绑定的值转数字
  • .trim:讲v-model绑定的值首位空格给去掉
  • .stop:阻止事件冒泡
  • .capture:事件的捕获
  • .self:点击事件绑定本身才触发
  • .lazy:输入框失焦时才会更新v-model的值
  • .prevent:阻止默认事件
  • .native:绑定事件在自定义组件上时,确保能执行
  • .left、.middle、.right:鼠标左中右键的触发
  • passive:相当于给移动端滚动事件加一个.lazy
  • camel:确保变量名会被识别成驼峰命名
  • .sync:简化子修改父值的步骤

.使用过哪些Vue的内部指令呢?

  • v-text:元素的textContent
  • v-html:元素的innerHTML
  • v-show:通过样式display改变显隐,控制的css
  • v-if:通过操作DOM改变显隐
  • v-else:配合v-if
  • v-else-id:配合v-else
  • v-for:循环渲染
  • v-on:绑定事件,缩写@
  • v-bind:绑定变量,缩写:
  • v-model:双向绑定
  • v-slot:插槽
  • v-once:只渲染一次
  • v-pre:跳过元素编译
  • v-cloak:隐藏双括号,有值显示

v-if和v-show有何区别?

  • v-if:通过操作DOM来控制显隐,适用于偶尔的情况,因为每一次执行它都要生成和销毁,v-if有着更高的切换消耗
  • v-show:通过改变css样式display属性控制显隐,适用于频繁显隐的情况,不会销毁,v-show在初始渲染消耗更高点
  • 频繁或者大数量显隐使用v-show ,否则使用v-if

为什么v-if和v-for不建议用在同一标签?

v-for优先级高于v-if,每项都通过v-for渲染出来后再去通过v-if判断显隐,过程中会增加无用的dom操作,渲染了无用的节点

.组件之间的传值方式有哪些?

  • 父传子: 组件使用props进行接收
  • 子传父: 子组件使用$emit+事件对父组件进行传值
  • 父子之间通过$parent$chidren获取实例进而通信
  • 使用$refs获取组件实例,进而获取数据。使用vuex进行状态管理
  • 使用eventBus进行跨组件触发事件,进而传递数据
  • 使用浏览器本地缓存,例如localstorage``sessionStorage

路由有哪些模式呢?又有什么不同呢?

  • hash模式:通过#号后面的内容的更改,触发hashchange事件,实现路由切换,而不刷新页面
  • history模式:通过pushState和replaceState切换url,触发popstate事件,实现路由切换,需要后端配合

路由的钩子函数?

  • beforeEach:跳转路由前
    • to:将要跳转进入的路由对象
    • from:将要离开的路由对象
    • next:执行跳转的方法
  • afterEach:路由跳转后
    • to:将要跳转进入的路由对象 组件内路由钩子
  • beforeRouteEnter(to, from, next):跳转到路由渲染组件时触发
  • beforeRouteUpdate(to, from, next):跳转到路由且组件被复用时触发
  • beforeRouteLeave(to, from, next):跳转到路由且离开组件时触发

如何设置动态class,动态style?

  • 动态class对象:<div :class="{ 'is-active': true, 'red': isRed }"></div>
  • 动态class数组:<div :class="['is-active', isRed ? 'red' : '' ]"></div>
  • 动态style对象:<div :style="{ color: textColor, fontSize: '18px' }"></div>

vuex的有哪些属性?用处是什么?

  • state:定义初始状态
  • getter:从store从取数据
  • mutation:更改store中状态,只能同步操作
  • action:用于提交mutation,而不直接更改状态,可异步操作
  • module:store的模块拆分

watch有哪些属性,分别有什么用?

  • immediate:初次加载时立即执行
  • deep:是否进行深度监听
  • handler:监听的回调函数

一般在哪个生命周期请求异步数据

  • 可以啊钩子函数中的 createdbeforeMountmounted 中进行调用,因为在这三个钩子函数中,data 已经创建,可以将服务端端返回的数据进行赋值。
  • 在created中最好
    • 能更快获取到服务端数据,减少页面加载时间,用户体验更好;
    • SSR不支持 beforeMount 、mounted 钩子函数,放在 created 中有助于一致性。

父子组件生命周期顺序?

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

为什么不建议用index做key,为什么不建议用随机数做key?

  • 用index和用随机数都是同理,随机数每次都在变,做不到专一性,也很消耗性能

使用过那些插槽的使用,原理是什么

  • 默认插槽 - 又名匿名查抄,当slot没有指定name属性值的时候一个默认显示插槽,一个组件内只有有一个匿名插槽
  • 具名插槽:带有具体名字的插槽,也就是带有name属性的slot,一个组件可以出现多个具名插槽。
  • 作用域插槽:默认插槽、具名插槽的一个变体,可以是匿名插槽,也可以是具名插槽,该插槽的不同点是在子组件渲染作用域插槽时,可以将子组件内部的数据传递给父组件,让父组件根据子组件的传递过来的数据决定如何渲染该插槽。 实现原理:当子组件vm实例化时,获取到父组件传入的slot标签的内容,存放在vm.$slot中,默认插槽为vm.$slot.default,具名插槽为vm.$slot.xxx,xxx 为插槽名,当组件执行渲染函数时候,遇到slot标签,使用$slot中的内容进行替换,此时可以为插槽传递数据,若存在数据,则可称该插槽为作用域插槽。 。

是否使用过滤器,过滤器的作用,怎么使用

  • 顾名思义,过滤器就是用来过滤的数据的,在Vue中使用filters来过滤数据,filters不会修改数据,而是过滤数据,改变用户看到的输出(计算属性 computed ,方法 methods 都是通过修改数据来处理数据格式的输出显示)。 使用场景:
  • 需要格式化数据的情况,比如需要处理时间、价格等数据格式的输出 / 显示。
  • 比如后端返回一个 年月日的日期字符串,前端需要展示为 多少天前 的数据格式,此时就可以用fliters过滤器来处理数据。

使用

  • 插值表达式 \{{ }}v-bind* 表达式* 中,然后放在操作符“ | ”后面进行指示。

自定义指令的钩子函数?

  • bind:指令绑定到指定元素时调用,只调用一次
  • inserted:指定元素插入父节点时调用
  • update:所在组件的 VNode 更新时调用
  • componnetUpdated:所在组件以及其子组件 VNode 全部更新后调用
  • unbind:只调用一次,指令与元素解绑时调用

说说nextTick的用处?

修改数据时不能马上得到最新的DOM信息,所以需要使用nextTick,在nectTick回调中可以获取最新DOM信息

为什么nextTick优先是微任务?

优先是Promise.then方法,是个微任务,这样可以避免多一次队列,进而少一次UI渲染,节省性能

Vue的SSR是什么?有什么好处?

SSR全称Server Side Render

  • 有利于SEO:由于是在服务端,将数据填充进HTML之后再推送到浏览器,所以有利于SEO的爬取
  • 首屏渲染快 SSR的缺点:
  • 开发条件会受到限制,服务器端渲染只支持beforeCreate和created两个钩子;
  • 当需要一些外部扩展库时需要特殊处理,服务端渲染应用程序也需要处于Node.js的运行环境;
  • 更多的服务端负载。

Vue响应式是怎么实现的?

  • 1、劫持:通过Object.defineProperty对对象进行递归劫持属性的get、set
  • 2、观察者模式:使用watcher进行观察数据使用的地方
  • 3、发布订阅模式:使用dep收集watcher,数据更改时,通过notify方法通知dep里的watcher去进行相应的更新
  • 4、数组:数组没有使用劫持的模式,而是通过重写数组原型上的方法,来实现数组的响应式

为什么只对对象劫持,而要对数组进行方法重写?

数组的元素大概率是成百上千的,所以对数组下标进行劫持的话会非常消耗性能。Vue通过对数组原型上方法的重写,实现数组的响应式

router.push、router.replace、router.go的区别?

  • router.push:跳转,并向history栈中加一个记录,可以后退到上一个页面
  • router.replace:跳转,不会向history栈中加一个记录,不可以后退到上一个页面
  • router.go:传正数向前跳转,传负数向后跳转

Vue中封装的数组方法有哪些,如何实现页面更新

  • 在Vue中,对响应式处理利用的是Object.defineProperty对数据进行拦截,而这个方法并不能监听到数组内部变化,数组长度变化,数组的截取变化等,所以需要对这些操作进行hack,让Vue能监听到其中的变化
  • push()
  • pop()
  • shift()
  • unshift()
  • solice()
  • sort()
  • reverse()

请问子组件可以直接改变父组件的数据吗?怎么解决

  • 肯定是不可以的,在vue里面为了维护父子组件的单向数据流,每次父级组件发生更新时,子组件中所有的 prop 都将会刷新为最新的值,如果你去修改,在控制台发出警告
  • 可以通过 \$emit* 派发一个自定义事件,父组件接收到后,由父组件去修改。*

vue如何监听对象或者数组某个属性的变化

  • this.$set(你要改变的数组/对象,你要改变的位置/key,你要改成什么value)
  • 调用以下几个数组的方法
1
2
splice()、 push()、pop()、shift()、unshift()、sort()、reverse()
复制代码

Vue有那些性能优化

(1)编码阶段

  • 尽量减少data中的数据,data中的数据都会增加getter和setter,会收集对应的watcher
  • v-if和v-for不能连用
  • 如果需要使用v-for给每项元素绑定事件时使用事件代理
  • SPA 页面采用keep-alive缓存组件
  • 在更多的情况下,使用v-if替代v-show
  • key保证唯一
  • 使用路由懒加载、异步组件
  • 防抖、节流
  • 第三方模块按需导入
  • 长列表滚动到可视区域动态加载
  • 图片懒加载

(2)SEO优化

  • 预渲染
  • 服务端渲染SSR

(3)打包优化

  • 压缩代码
  • Tree Shaking/Scope Hoisting
  • 使用cdn加载第三方模块
  • 多线程打包happypack
  • splitChunks抽离公共文件
  • sourceMap优化

(4)用户体验

  • 骨架屏
  • PWA
  • 还可以使用缓存(客户端缓存、服务端缓存)优化、服务端开启gzip压缩等。