1. 虚拟DOM
虚拟DOM( VDOM )是利用了js的Object对象模型来模拟真实DOM,它的结构是一个树形结构,操作虚拟DOM比真实DOM更高效
2. diff算法
diff算法的思维来自于后端,用来比较两个或多个文件,返回值是文件的不同点,diff算法进行的是同级比较
diff算法的比较思维(Vue中),会出现以下四种情况:
1. 此节点是否被移除 ---> 添加新的节点
2. 属性是否被更改 ---> 旧属性改为新属性
3. 文本内容被改变 ---> 旧内容改为新内容
4. 结构完全不同 ---> 节点将被整个移除替换
3. 自己封装过什么组件,描述一下
4. v-if和v-show有什么区别
v-if用于操作Dom是否渲染,而v-if操作的则是display属性,v-if的有更高的切换开销,v-show有更大的初始渲染开销,所以,如果需要频繁切换,使用v-show比较好,而如果渲染条件改变次数少,则v-if更好
5. Vue的双向绑定原理是什么
v-model默认绑定了对象的value,checked等属性,当它初次绑定的时候,就会触发getter,watcher就会触发, watcher通知Vue生成新的VDOM树,再通过render函数进行渲染,生成真实DOM当视图修改时,意味着DOM的value属性值改变,就会触发setter,watcher监听机制就会执行,watcher通知Vue生成新的VDOM树,再通过render函数进行渲染,生成真实DOM
6. 如何在Vue组件之间传递价值通信 【本人理解为如何在组件内通信】
- 父子通信
- 绑定简单数据类型
- 父组件中定义数据, 通过单向数据绑定的形式, 将数据绑定在子组件的属性身上, 属性是自定义属性
- 子组件通过配置项中的props接收数据, props可以是一个数组,数组中放的是自定义属性的名称
- 接受完后,则这个自定义属性可以向data中的数据一样直接在子组件模板中使用
- 父组件中的数据一旦修改, 子组件数据就会修改,但是子组件无法修改数据, 那么这也就是单项数据流
- 子父通信
- 绑定复杂数据类型
- 父组件中的数据是复杂数据类型的数据,那么父组件再给子组件绑定数据时,给子组件的是一个引用地址
- 所以,子组件可以通过这个引用地址,修改这个数据
- 父组件传递一个方法给子组件,达到操作子组件,间接操作父组件数据的效果
- 父组件定义方法, 然后将这个方法通过单向数据绑定的形式传递给子组件,即通过自定义属性
- 子组件通过props属性接收, 然后通过 事件调用:例:@click=“自定义属性自定义属性名”
- 通过自定义事件来实现通信
- 父组件中定义 数据 和 方法(方法时用来操作数据的)
- 在子组件身上绑定自定义事件
- 子组件定义方法, 在这个方法中通过 this.$emit(eventType,实际参数) 来调用自定义事件的事件处理程序
- 非父子组件通信
- 使用ref来绑定组件, 【ref链】
- 在父组件的模板中, 使用ref = refName 绑定在两个兄弟组件身上
- 在任意一个子组件中, 就可以通过 this.
refs.refName 就可以获得另一个子组件了, 同时这个自组件身上的数据和方法同样也得到了
- 通过事件总线(bus)
- 它是通过事件的发布(声明), 以及事件的订阅(触发)来做的
- 首先在js中 创建一个bus对象(vm)
var bus = new Vue()
- 在Count组件中定义数据, 和修改数据的方法
- 在Count组件中 通过 created 钩子 , 进行bus事件的发布
created: {
bus.$on('add',this.addCount)
}
- 在MyButton组件的方法中通过 bus进行事件的订阅
increment () {
bus.$emit( 'add' )
}
7. Vue 生命周期的理解
- Vue 实例有一个完整的生命周期,生命周期也就是指一个实例从开始创建到销毁的这个过程。
- beforeCreated():在实例创建之前执行,数据未加载状态。
- created():在实例创建、数据加载后,能初始化数据,DOM 渲染之前执行。
- beforeMount():虚拟 DOM 已创建完成,在数据渲染前最后一次更改数据。
- mounted():页面、数据渲染完成,真实 DOM 挂载完成。
- mounted():页面、数据渲染完成,真实 DOM 挂载完成。
- beforeUpadate():重新渲染之前触发。
- updated():数据已经更改完成,DOM 也重新 render 完成,更改数据会陷入死循环。
- beforeDestory()
和 destoryed():前者是销毁前执行(实例仍然完全可用),后者则是销毁后执
8. vue对数组的方法,做了哪些封装
9. 路由的种类及路由实现的底层原理
- 种类
- hash: 使用 URL hash 值来作路由。支持所有浏览器,包括不支持 HTML5 History Api 的浏览器。
- history: 依赖 HTML5 History API 和服务器配置。【需要后端支持】
- abstract: 支持所有 JavaScript 运行环境,如 Node.js 服务器端。如果发现没有浏览器的 API,路由会自动强制进入这个模式。
- 底层原理
10. slot用什么可以代替
11. 2.6版本对slot有哪些改变,及如何使用
- 改变:
vue 在 2.6.0 中,具名插槽和作用域插槽引入了一个新的统一的语法 (即 `v-slot` 指令)。它取代了 `slot` 和 `slot-scope` 这两个目前已被废弃但未被移除且仍有用的特性。但是将会在vue 3 中,被废弃的这两个,不会被支持即无效。
- 使用
1. 具名插槽的变化
```javascript
<!--2.6.0以前的写法-->
<template slot='header'>
<p>------------header----------------</p>
<h3>这是header1的内容</h3>
<p>这是header2的内容</p>
</template>
<!--2.6.0之后的写法-->
<template v-slot:header>
<p>------------header----------------</p>
<h3>这是header1的内容</h3>
<p>这是header2的内容</p>
</template>
```
2. 具名插槽的缩写
跟 v-on 和 v-bind 一样,v-slot 也有缩写,即把参数之前的所有内容 (v-slot:) 替换为字符 #。例如 v-slot:header 可以被重写为 #header:
```html
<base-layout>
<template #header>
<h1>Here might be a page title</h1>
</template>
<p>A paragraph for the main content.</p>
<p>And another one.</p>
<template #footer>
<p>Here's some contact info</p>
</template>
</base-layout>
```
3. 作用域插槽的变化
```html
<slotScope :message='msg'>
<!--2.6.0之前的写法-->
<div slot='sayWhat' slot-scope='thing'>说了:{{thing.said}}</div>
<template slot='listbox' slot-scope='value'>
<p>{{value.send.text}}</p>
</template>
<!--2.6.0之前的写法,不能单独用在html标签上-->
<template v-slot:sayWhat='thing'>
<div>说了:{{thing.said}}</div>
</template>
<template v-slot:listbox='value'>
<p>{{value.send.text}}</p>
</template>
</slotScope>
```
4. 动态插槽名:态指令参数也可以用在 v-slot 上,来定义动态的插槽名:
```html
<base-layout>
<template v-slot:[dynamicSlotName]>
...
</template>
</base-layout>
```
12. V-modle可以用什么替代
v-model其实是v-bind和v-on的语法糖
`<input v-model="something">`其实是
`<input v-bind:value="something" v-on:input="something = $event.target.value">`的语法糖
v-on,它其实就是一个事件绑定器。我们仔细阅读一下
`<input v-bind:value="something" v-on:input="something = $event.target.value">`,
发现它由两部分组成:v-bind:value和v-on:input,必须是value属性和input事件,否则也不会等价于v-model,而且input事件里面,正好是something等于当前输入值。
13. Vuex的理解
vuex是一个集中式的存储仓库【状态】,类似于 本地存储、数据库,vuex是vue的状态管理工具,它的功能主要是实现多组件间的状态【数据】共享
vuex的组成部分(其中state,action和mutation是它的核心组成)
- **state: 状态**
- **action: 动作(业务交互)**
- **mutation: 修改state**
- getter: 获取数据的
- store: state的存储者
应用场景
相对比较大型的应用(状态较多)
### 14. 如何封装插件
1. 创建插件目录,使用npm init -y 初始化目录。
2. 定义插件组件内容,并在入口文件index.js中引入插件组件,并使用install方法,并导出
```JavaScript
import Vue from 'vue'
import Loading from './components/Loading.vue'
export default {
install () {
Vue.component('Loading',Loading)
}
}
```
3. 在Vue的入口文件中引入index.js文件并使用Vue.use()使用该组件,则可以在项目中任意位置使用
```JavaScript
import Loading from './zyl_plugins'
Vue.use(Loading)
```
15. 是否研究过源码
16. Vue不能检测哪些变动的数组,官方是怎么解决的?你是怎么解决的?
1. 当你利用索引设置一个项时, 例如: vm.items[indexOfItem] = newValue;
**解决方法**: 使用 Vue.set || this.$set 或splice(indexOfItem, 1, newValue);
2. 当你修改数组的长度时,例如: vm.items.length = newLength;
**解决方法**:使用splice(newLength)
17. 问的是vue中 新增 对数组api的响应 源码大概是怎么个流程
18. 问你在Vue是用Ajax吗
Vue数据请求用的是原生的fetch和封装的第三方库axios
19. vue兼容ie?
vue.js是不兼容ie8及其以下浏览器,因为用到了ES5中的`Object.defineProperty`
20. vue做PC?react做移动?
不区分平台,都可以使用
21. vue做pc端会做IE的兼容
第一步、安装 babel-polyfill
`npm install --save babel-polyfill`
第二步、在VUE项目的src目录下找到main.js,添加代码`import 'babel-polyfill'`
第三步、在webpack.base.conf.js中配置:
`entry: {
app: ['babel-polyfill','./src/main.js']
},`
22. histroy路由如何去掉#
可以用路由的 history 模式,添加 mode: 'history' 之后将使用 HTML5 history 模式,该模式下没有 # 前缀
23. Vue中如何使用防抖节流,
1. 在公共方法中(如 public.js 中),加入函数防抖和节流方法
- 防抖
```JavaScript
export function _debounce(fn, delay) {
var delay = delay || 200;
var timer;
return function () {
var th = this;
var args = arguments;
if (timer) {
clearTimeout(timer);
}
timer = setTimeout(function () {
timer = null;
fn.apply(th, args);
}, delay);
};
}
```
- 节流
```JavaScript
export function _throttle(fn, interval) {
var last;
var timer;
var interval = interval || 200;
return function () {
var th = this;
var args = arguments;
var now = +new Date();
if (last && now - last < interval) {
clearTimeout(timer);
timer = setTimeout(function () {
last = now;
fn.apply(th, args);
}, interval);
} else {
last = now;
fn.apply(th, args);
}
}
}
```
2. 在需要使用的组件引用
`import { _debounce } from "@/utils/public";`
3. 在 methods 中使用
```JavaScript
methods: {
// 改变场数
changefield: _debounce(function(_type, index, item) {
// do something ...
}, 200)
}
```
24. 保持活动组件在Vue中的作用
25. vue项目的技术栈?
(技术栈:某项工作或某个职位需要掌握的一系列技能组合的统称)
1. vue
组件的生命周期、组件的通信、计算属性(computed)、数据更新检测,对象更新、事件处理器(修饰符)、表单控件绑定、异步更新队列、过渡效果、插件、混合(mixins)....
2. vue-router
两种导航方式(router-link声明式导航,编程式导航)、导航(路由)守卫、路由的激活,动画、数据获取,预载,懒加载,缓存、路由的传接参、动态路由...
3. vuex
state、actions、mutations、getters、modules、热重载
26. vue cli 搭建项目?
vue-cli 用于快速创建vue项目,底层配置使用的就是webpack
1. 安装cli
npm install @vue/cli -g cli3的版本
npm install vue-cli -g cli2的版本
(npm install @vue/cli-init -g 装了这个工具,可以随意安装cli2又能随意安装cli3)
2. 创建项目
vue create peoject(项目名) --->cli3
vue init webpack/webpack-simple(简易版本) peoject(项目名) ---->cli2
3. 目录结构说明
* node_modules 项目的依赖包
* public 静态资源文件夹
* src 源代码开发目录
1. assets 当前项目开发的静态资源
2. compomemts 项目的组件
3. main.js webpack中配置的主入口文件
.......
* .browserslistrc 项目不支持 ie8及以下
* .gitignore git上传时,不上传的文件
* package.json 当前项目的依赖包配置文件
* pastcss.cofig.js 给css添加引擎头前缀
* readme.md 当前项目的说明文件
* yarn.lock 当前项目的依赖包的第三方库的详细信息记录
4. 下载依赖包
下载所需要的包,npm/cnpm/yarn
5. 运行项目
npm run dev
27. dev pro test 环境的配置:相对应的如何配置。script配置,mode环境
28. dev-server 开发如何跑起来的,大概加载了那些东西?加载了那些中间件
29. vue 中的observer
(vue.$data.__ob__)
是保存Observer对象的,用来记录是否已经做过数据的“响应式化”,也就是是否已经被Observer处理过,最终是为了用Object.defineProperty进行数据的双向绑定。
30. vue数据响应式,data变成响应式。让其他数据变成响应式,
31. 刷新之后vuex状态之后,重置的问题,如何解决
利用sessionStorage/localStorage,做一个暂时的储存store的模块化结构
主要是针对mutations 和 getters 做一个简单的赋值和取值封装
mutations.js改变state的同时在本地做一个备份
getters.js 取值时默认从state里面取,没有就从本地拿
在页面vue文件直接用mapGetters获取状态值
这样一来就算state被清空了,还可以在本地储存里面获取状态值
32. vue深度监听的方法
watch所监听的对象里添加deep: true
33. vue一面如何实现一个倒计时定时器,回来的时候时间继续
34. hashRouter,history的原理
- hash 模式 —— 使用 URL 的 hash值 来模拟一个完整的 URL,于是当 URL 改变时,页面不会重新加载。支持所有浏览器,包括不支持 HTML5 History Api 的浏览器。
- history 模式——这种模式充分利用 h5 history.pushState API 来完成 URL 跳转而无须重新加载页面。history需要后台配置支持
35. 路由拦截
登录路由拦截,
```javascript
router.beforeEach((to, from, next) => {
if (to.name !== 'login') {
if (!window.sessionStorage.tokenId) {
router.push({name: 'login'})
}
}
next()
```
36. vue的生命周期,以及每个周期的作用
- beforeCreate:表示组件创建前的准备工作, 为事件的发布订阅 和 生命周期的开始做初始化,无法获取data数据和真实dom
- created: 在实例创建、数据加载后,能初始化数据,DOM 渲染之前执行。数据、属性、方法已经初始化完成,还没挂载。在这个钩子中可以进行数据请求,然后可以进行默认数据的修改
- beforMount: 挂载之前,已完成模板的渲染,生成了虚拟dom, 但真实dom依然无法获取,在这个钩子中数据请求, 它也可以进行一次数据修改
- mounted:页面、数据渲染完成,真实 DOM 挂载完成。在这个钩子中DOM操作就可以进行了, 也可以进行第三方库的实例化(dom不改变)
- beforeUpdate: 数据更新后被动执行,发生在虚拟DOM重新渲染前,因为这个钩子主要做的事情是内部进行的, 所以对我们而言没有太多的操作意义
- updated: 在数据更改导致的虚拟DOM重新渲染和打补丁之后调用。调用时,组件DOM已经更新,所以可以执行依赖于DOM的操作,如进行第三方库的实例化( DOM是改变的 )。然而在大多数情况下,应该避免在此期间更改状态,通常最好使用计算属性或 watcher 取而代之。
- beforeDestory: 销毁前执行(实例仍然完全可用),
- beforeDestory: 组件连同数据和方法一起被销毁,在这个钩子中做善后工作 , 手动清除一些计时器, 和一些方法, 还有第三方实例化出来的对象
37. vue数据获取在那个生命周期钩子?为什么不在mounted中
看实际情况,一般在created(或beforeRouteEnter)里面就可以,如果涉及到需要页面加载完成之后的话用mounted。
在created的时候,视图中的html并没有渲染出来,所以此时如果直接去操作html的dom节点,一定找不到相关的元素
而在mounted中,由于此时html已经渲染出来了,所以可以直接操作dom节点,(此时document.getelementById 即可生效了)。
38. 是否进行过组件封装?举例下自己如何进行组件封装的
首先,组件可以提升整个项目的开发效率。能够把页面抽象成多个相对独立的模块,解决了我们传统项目开发:效率低、难维护、复用性等问题。
然后,使用Vue.extend方法创建一个组件,然后使用Vue.component方法注册组件。子组件需要数据,可以在props中接受定义。而子组件修改好数据后,想把数据传递给父组件。可以采用emit方法。
39. 组件通信的各种方法
父子 props/event $parent/$children ref provide/inject
兄弟 bus vuex
跨级 bus vuex provide.inject
40. 如何实现秒杀功能(结合vue的生命周期)
```javascript
HTML:
<div>{{countDownList}}</div>
script:
export default {
data() {
return {
countDownList: '00天00时00分00秒',
actEndTime: '2018-11-19 18:50:00'
};
},
created() {
this.countDown();
},
methods: {
timeFormat(param) {
return param < 10 ? '0' + param : param;
},
countDown(it) {
var interval = setInterval(() => {
// 获取当前时间,同时得到活动结束时间数组
let newTime = new Date().getTime();
// 对结束时间进行处理渲染到页面
let endTime = new Date(this.actEndTime).getTime();
let obj = null;
// 如果活动未结束,对时间进行处理
if (endTime - newTime > 0) {
let time = (endTime - newTime) / 1000;
// 获取天、时、分、秒
let day = parseInt(time / (60 * 60 * 24));
let hou = parseInt(time % (60 * 60 * 24) / 3600);
let min = parseInt(time % (60 * 60 * 24) % 3600 / 60);
let sec = parseInt(time % (60 * 60 * 24) % 3600 % 60);
obj = {
day: this.timeFormat(day),
hou: this.timeFormat(hou),
min: this.timeFormat(min),
sec: this.timeFormat(sec)
};
} else { // 活动已结束,全部设置为'00'
obj = {
day: '00',
hou: '00',
min: '00',
sec: '00'
};
clearInterval(interval);
}
this.countDownList = obj.day + '天' + obj.hou + '时' + obj.min + '分' + obj.sec + '秒';
}, 1000);
}
}
}
```