√
1.vue
长处?
- 轻量级框架:
只关注视图层,是一个构建数据的视图汇合,大小只有几十kb; - 数据绑定:
保留了angular的特点,在数据操作方面更为简略; - 组件化:
保留了react的长处,实现了html的封装和重用,在构建单页面利用方面有着独特的劣势;
视图,数据,构造拆散:
使数据的更改更为简略,不须要进行逻辑代码的批改,只须要操作数据就能实现相干操作; - 虚构DOM:
dom操作是十分消耗性能的, 不再应用原生的dom操作节点,极大解放dom操作,但具体操作的还是dom不过是换了另一种形式;
运行速度更快:
相比拟与react而言,同样是操作虚构dom,就性能而言,vue存在很大的劣势。
2.vue
父组件向子组件传递数据?
父子间通信:父亲提供数据通过属性 props传给儿子;儿子通过 $on 绑父亲的事件,再通过 $emit 触发本人的事件(公布订阅)
利用父子关系 $parent 、$children
父组件提供数据,子组件注入。 provide 、 inject ,插件用得多。
ref 获取组件实例,调用组件的属性、办法
vuex 状态治理实现通信
祖孙节点能够应用: $attrs/$listeners
3.子组件向父组件传递事件?
- $emit办法
自定义子组件
<template>
<div class="container">
<p @click="clickAction">{{titleName}}</p>
<div class="line"></div>
</div>
</template>
export default {
name: "HelloWorld",
props:{
titleName:{
type:string,
default:""
}
},
methods: {
clickAction(){
this.$emit('clickChild',this.titleName);
}
}
};
父组件中调用子组件
<div class="message">
<ActivityHead
:titleName="msgRight"
@clickChild="clickChild">
</ActivityHead>
</div>
import ActivityHead from "./ActivityHead.vue";
export default {
name: "HelloWorld",
components: {
ActivityHead
},
methods: {
clickChild(msg){
console.log(msg);
}
}
};
4.v-if
与v-show
的区别
共同点:
都能管制元素的显示和暗藏;
不同点:
实现实质办法不同,v-show实质就是通过管制css中的display设置为none,管制暗藏,只会编译一次;v-if是动静的向DOM树内增加或者删除DOM元素,若初始值为false,就不会编译了。
而且v-if不停的销毁和创立比拟耗费性能。 总结:如果要频繁切换某节点,应用v-show(切换开销比拟小,初始开销较大)。
如果不须要频繁切换某节点应用v-if(初始渲染开销较小,切换开销比拟大)。
5.v-if
与 v-for
的优先级?
外围答案:
1、v-for优先于v-if被解析
2、如果同时呈现,每次渲染都会先执行循环再判断条件,无论如何循环都不可避免,节约了性能
3、要避免出现这种状况,则在外层嵌套template,在这一层进行v-if判断,而后在外部进行v-for循环
4、如果条件呈现在循环外部,可通过计算属性提前过滤掉那些不须要显示的项
6.vue
组件中data
为什么必须是一个函数?
如果data
是一个函数的话,这样每复用一次组件,就会返回一份新的data
,相似于给每个组件实例创立一个公有的数据空间,让各个组件实例保护各自的数据。
而单纯的写成对象模式,就使得所有组件实例共用了一份data
,就会造成一个变了全都会变的后果。
所以说vue
组件的data
必须是函数。这都是因为js
的个性带来的,跟vue自身设计无关。
js
自身的面向对象编程也是基于原型链和构造函数,应该会留神原型链上增加个别都是一个函数办法而不会去增加一个对象了。
7.VueX
之actions
与mutations
的区别?
actions
1、用于通过提交mutation扭转数据
2、会默认将本身封装为一个Promise
3、能够蕴含任意的异步操作
mutations
1、通过提交commit
扭转数据
2、只是一个单纯的函数
3、不要应用异步操作,异步操作会导致变量不能追踪
2.如何在vuex中应用异步批改?
在调用vuex
中的办法action
的时候,用promise
实现异步批改
const actions = {
asyncLogin({ commit }, n){
return new Promise(resolve => {
setTimeout(() => {
commit(types.UserLogin, n);
resolve();
},3000)
})
}
}
8.Vue
有哪些组件间的通信形式?
外围答案:
Vue 组件间通信只有指以下 3 类通信:
父子组件通信、隔代组件通信、兄弟组件通信,上面咱们别离介绍每种通信形式且会阐明此种办法可实用于哪类组件间通信。
办法一
props/$emit
父组件A
通过props
的形式向子组件B
传递,B to A
通过在 B 组件中 $emit
, A
组件中 v-on
的形式实现。
1.父组件向子组件传值
接下来咱们通过一个例子,阐明父组件如何向子组件传递值:在子组件Users.vue中如何获取父组件App.vue中的数据
userList:["Henry","Bucky","Emily"]
//App.vue父组件
<template>
<div id="app">
<hook-users
:userList="userList"/>
//前者自定义名称便于子组件调用,后者要传递数据名
</div>
</template>
<script>
import AppUsers from "./Components/AppUsers"
export default {
name: 'App',
data(){
return{
userList:["Henry","Bucky","Emily"]
}
},
components:{
"app-users":AppUsers
}
}
//users子组件
<template>
<div class="hello">
<ul>
//遍历传递过去的值,而后出现到页面
<li v-for="user in userList">{{user}}</li>
</ul>
</div>
</template>
<script>
export default {
name: 'AppUsers',
props:{
userList:{
type:Array,
required:true
}
}
}
</script>
总结:
父组件通过props
向下传递数据给子组件。注:组件中的数据共有三种模式:data
、props
、computed
2.子组件向父组件传值(通过事件模式)
// 子组件
<template>
<header>
<h1 @click="changeTitle">{{title}}</h1>//绑定一个点击事件
</header>
</template>
<script>
export default {
name: 'AppHeader',
data() {
return {
title:"Vue.js Demo"
}
},
methods:{
changeTitle() {
this.$emit("titleChanged","子向父组件传值");
//自定义事件 传递值“子向父组件传值”
}
}
}
</script>
// 父组件
<template>
<div id="app">
<app-header v-on:titleChanged="updateTitle" ></app-header>
//与子组件titleChanged自定义事件保持一致
// updateTitle($event)承受传递过去的文字
<h2>{{title}}</h2>
</div>
</template>
<script>
import Header from "./components/Header"
export default {
name: 'App',
data(){
return{
title:"传递的是一个值"
}
},
methods:{
updateTitle(e){ //申明这个函数
this.title = e;
}
},
components:{
"app-header":Header,
}
}
</script>
总结:
子组件通过events
给父组件发送音讯,实际上就是子组件把本人的数据发送到父组件。
办法二、$emit/$on
这种办法通过一个空的Vue
实例作为地方事件总线(事件核心),用它来触发事件和监听事件,奇妙而轻量地实现了任何组件间的通信,包含父子、兄弟、跨级。当咱们的我的项目比拟大时,能够抉择更好的状态治理解决方案vuex
。
1.具体实现形式:
var App=new Vue();
App.$emit(事件名,数据);
App.$on(事件名,data => {});
或者本人实现一个
class MyEventEmitter {
constructor() {
this.event = {};
}
// 监听
on(type, listener) {
if (this.event[type]) {
this.event[type].push(listener);
} else {
this.event[type] = [listener];
}
}
//发送监听
emit(type, ...rest) {
if (this.event[type]) {
this.event[type].map(fn => fn.apply(this, rest));
}
}
//移除监听器
removeListener(type) {
if (this.event[type]) {
delete this.event[type];
console.log(this.event);
}
}
//移除所有的监听器
removeAllListener() {
this.event = {};
}
}
var MyEvent=new MyEventEmitter();
MyEvent.$emit(事件名,数据);
MyEvent.$on(事件名,data => {});
然而这种形式,记得在每次触发监听的时候,记得移除上一个监听器
办法三、Vuex与localStorage
vuex 是 vue 的状态管理器,存储的数据是响应式的。然而并不会保存起来,刷新之后就回到了初始状态,具体做法应该在vuex里数据扭转的时候把数据拷贝一份保留到localStorage外面,刷新之后,如果localStorage里有保留的数据,取出来再替换store里的state。
const jsonToString=(json)=>{
return JSON.stringify(json)
}
const stringToJson=(keyName)=>{
//暂不验证数据格式
return window.localStorage.getItem(keyName)?
JSON.parse(window.localStorage.getItem(keyName))
:{};
}
export default new Vuex.Store({
state: {
selectCity:stringToJson("selectCity")
},
mutations: {
changeCity(state, selectCity) {
state.selectCity = selectCity
try {
window.localStorage.setItem('selectCity',jsonToString(state.selectCity));
// 数据扭转的时候把数据拷贝一份保留到localStorage外面
} catch (e) {}
}
}
})
办法四、$attrs/$listeners
如图:
场景
有些变态需要:比如说A父组件外面导入了B组件,可是B组件外面又导入了C组件,当初须要A父组件传值给C组件,或者是C组件须要传值给父组件,这时候就须要用到$attrs和$listeners了。
$attrs
蕴含了父作用域中不作为 prop 被辨认 (且获取) 的个性绑定 (class 和 style 除外)。当一个组件没有申明任何 prop 时,这里会蕴含所有父作用域的绑定 (class和 style 除外),并且能够通过 v-bind=”$attrs” 传入外部组件——在创立高级别的组件时十分有用。(父传孙专用)
$listener
蕴含了父作用域中的 (不含 .native 润饰器的) v-on 事件监听器。它能够通过 v-on=”$listeners” 传入外部组件——在创立更高层次的组件时十分有用。(孙传父专用)
在父组件当中,最外层组件
<template>
<div>
<Child1
:child1Info="child1"
:child2Info="child2"
v-on:test1="onTest1"
v-on:test2="onTest2">
</Child1>
</div>
</template>
<script>
import Child1 from './child1';
export default {
data() {
return {
child1:"hahha",
child2:"asdsdasd"
};
},
components: { Child1 },
methods: {
onTest1(msg) {
console.log('test1 running...',msg);
},
onTest2(msg) {
console.log('test2 running',msg);
}
}
};
</script>
···
//在子组件中
<template>
<div class="child-1">
<p>在子组件当中:</p>
<p>props-child1Info: {{child1Info}}</p>
<p>$attrs: {{$attrs}}</p>
<hr>
<!-- Child2组件中能间接触发test的起因在于 B组件调用C组件时 应用 v-on 绑定了$listeners 属性 -->
<!-- 通过v-bind 绑定$attrs属性,Child2组件能够间接获取到A组件中传递下来的props(除了child1组件中props申明的) -->
<Child2 v-bind="$attrs" v-on="$listeners"></Child2>
</div>
</template>
<script>
import Child2 from ‘./child2’;
export default {
props: ['child1Info'],
data() {
return {};
},
components: { Child2 },
mounted() {
this.$emit('test1','嘻嘻');
}
};
</script>
//在孙子组件当中:
<template>
<div class="child-2">
<p>在最里层组件当中child2:</p>
<p>props-child2Info: {{child2Info}}</p>
<p> $attrs 的值: {{$attrs}}</p>
<hr>
</div>
</template>
<script>
export default {
props: ['child2Info'],
data() {
return {};
},
mounted() {
this.$emit('test2','哈哈');
}
};
</script>
#### 代码具体阐明:



## 9.`Vue`中双向数据绑定是如何实现的?
1.`vue.js` 则是采纳数据劫持联合发布者-订阅者模式的形式。
2.通过`Object.defineProperty()`来劫持各个属性的`setter`,`getter`.
3.在数据变动时公布音讯给订阅者,触发相应的监听回调。咱们先来看`Object.defineProperty()`这个办法:
var obj = {};
Object.defineProperty(obj, ‘name’, {
get: function() {
console.log('我被获取了')
return val;
},
set: function (newVal) {
console.log('我被设置了')
}
})
obj.name = ‘fei’;
//在给obj设置name属性的时候,触发了set这个办法
var val = obj.name;
//在失去obj的name属性,会触发get办法
## 10.单页面利用和多页面利用区别及优缺点?
单页面利用(`SPA`),艰深一点说就是指只有一个主页面的利用,浏览器一开始要加载所有必须的 `html`, `js`, `css`。所有的页面内容都蕴含在这个所谓的主页面中。但在写的时候,还是会离开写(页面片段),而后在交互的时候由路由程序动静载入,单页面的页面跳转,仅刷新部分资源。多利用于`pc`端。
多页面(`MPA`),就是指一个利用中有多个页面,页面跳转时是整页刷新
#### 单页面的长处:
1,用户体验好,快,内容的扭转不须要从新加载整个页面,基于这一点spa对服务器压力较小
2,前后端拆散
3,页面成果会比拟炫酷(比方切换页面内容时的专场动画)
#### 单页面毛病:
1,不利于`seo`
2,导航不可用,如果肯定要导航须要自行实现后退、后退。(因为是单页面不能用浏览器的后退后退性能,所以须要本人建设堆栈治理)
3,首次加载时耗时多
4,页面复杂度进步很多
## 11.`vue`中`v-if`和`v-for`优先级?
`v-for`和`v-if`不应该一起应用,必要状况下应该替换成`computed`属性。起因:`v-for`比`v-if`优先,如果每一次都须要遍历整个数组,将会影响速度,尤其是当之须要渲染很小一部分的时候。
<li
v-for=”user in users”
v-if=”user.isActive”
:key=”user.id”>
{{ user.name }}
</li>
如上状况,即便100个user中之须要应用一个数据,也会循环整个数组。
omputed: {
activeUsers: function () {
return this.users.filter(function (user) {
return user.isActive
})
}
}
<ul>
<li
v-for=”user in activeUsers”
:key=”user.id”>
{{ user.name }}
</li>
</ul>
## 12.`Vue`事件的修饰符()?
1)``.stop`:等同于`JavaScript`中的`event.stopPropagation()``,避免事件冒泡
2)`.prevent`:等同于`JavaScript`中的`event.preventDefault()`,避免执行预设的行为(如果事件可勾销,则勾销该事件,而不进行事件的进一步流传)
3)`.capture`:与事件冒泡的方向相同,事件捕捉由外到内
4)`.self`:只会触发本人范畴内的事件,不蕴含子元素
5)`.once`:只会触发一次
6)`.passive`:`passive`示意`listener`函数不会调用`preventDefault()`
`passive`次要用在挪动端的`scroll`事件,来进步浏览器响应速度,晋升用户体验。因为`passive=true`等于提前通知了浏览器,`touchstart`和`touchmove`不会阻止默认事件,手刚开始触摸,浏览器就能够立即给与响应;
否则,手触摸屏幕了,但要期待`touchstart`和`touchmove`的后果,多了这一步,响应工夫就长了,用户体验也就差了。
## 13.`Vue`的两个外围是什么?
#### 1、数据驱动:
在vue中,数据的扭转会驱动视图的自动更新。传统的做法是须要手动扭转DOM来使得视图更新,而vue只须要扭转数据。
#### 2、组件
组件化开发,长处很多,能够很好的升高数据之间的耦合度。将罕用的代码封装成组件之后(vue组件封装办法),就能高度的复用,进步代码的可重用性。一个页面/模块能够由多个组件所组成。
### 14、`react`和`vue`的区别
#### 相同点
* 数据驱动页面提供响应式的试图组件
* 都有`virtual DOM`,组件化的开发通过`props`参数进行父子之间组件传递数据都实现了`webComponents`标准
* 数据流动单向都反对服务器的渲染SSR
* 都有反对`native`的办法`react`有`React native vue`有`wexx`
#### 不同点
* 数据绑定`Vue`实现了双向的数据绑定`react`数据流动是单向的
* 数据渲染大规模的数据渲染`react`更快
* 应用场景`React`配合`Redux`架构适宜大规模多人合作简单我的项目Vue适宜小快的我的项目
* 开发格调`react`举荐做法`jsx` + `inline style`把`html`和`css`都写在`js`了
* `vue`是采纳`webpack` +`vue-loader`单文件组件格局`html`, `js`, `css`同一个文件
## 15.`vue3.0`有哪些新个性
#### vue3.0的设计指标
* 更小
* 更快
* 增强TypeScript反对
* 增强API设计一致性
* 进步本身可维护性
* 凋谢更多底层性能
具体能够从以下方面来了解
#### 1,压缩包体积更小
以后最小化并被压缩的 `Vue` 运行时大小约为 20kB(2.6.10 版为 22.8kB)。`Vue 3.0`捆绑包的大小大概会`缩小一半`,即只有`10kB`!
#### 2,Object.defineProperty -> Proxy
`Object.defineProperty`是一个绝对比拟低廉的操作,因为它间接操作对象的属性,颗粒度比拟小。将它替换为`es6`的`Proxy`,在指标对象之上架了一层拦挡,代理的是对象而不是对象的属性。这样能够将本来对对象属性的操作变为对整个对象的操作,颗粒度变大。
`javascript`引擎在解析的时候心愿对象的构造越稳固越好,如果对象始终在变,可优化性升高,`proxy`不须要对原始对象做太多操作。
#### 3,Virtual DOM 重构
vdom的实质是一个形象层,用`javascript`形容界面渲染成什么样子。`react`用`jsx`,没方法检测出能够优化的动静代码,所以做工夫分片,`vue`中足够快的话能够不必工夫分片。
#### 传统vdom的性能瓶颈:
尽管 Vue 可能保障触发更新的组件最小化,但在单个组件外部仍然须要遍历该组件的整个 vdom 树。
传统 vdom 的性能跟模版大小正相干,跟动静节点的数量无关。在一些组件整个模版内只有大量动静节点的状况下,这些遍历都是性能的节约。
`JSX` 和手写的 `render function` 是齐全动静的,适度的灵活性导致运行时能够用于优化的信息有余
那为什么不间接摈弃vdom呢?
高级场景下手写 `render function` 取得更强的表达力
生成的代码更简洁
#### 兼容2.x
`vue`的特点是底层为`Virtual DOM`,下层蕴含有大量动态信息的模版。为了兼容手写 `render function`,最大化利用模版动态信息,`vue3.0`采纳了动静联合的解决方案,将`vdom`的操作颗粒度变小,每次触发更新不再以组件为单位进行遍历,次要更改如下
将模版基于动静节点指令切割为嵌套的区块
每个区块外部的节点构造是固定的
每个区块只须要以一个 Array 追踪本身蕴含的动静节点
vue3.0将 vdom 更新性能由与模版整体大小相干晋升为与动静内容的数量相干
#### 4, 更多编译时优化
Slot 默认编译为函数:父子之间不存在强耦合,晋升性能
Monomorphic vnode factory:参数统一化,给它children信息,
Compiler-generated flags for vnode/children types
#### 5,选用Function_based API
为什么撤销 `Class API` ?
1,更好地反对`TypeScript`
`Props` 和其它须要注入到 `this` 的属性导致类型申明仍然存在问题
`Decorators`提案的重大不稳固使得依赖它的计划具备重大危险
2,除了类型反对以外 `Class API` 并不带来任何新的劣势
3,`vue`中的`UI`组件很少用到继承,个别都是组合,能够用`Function-based API`
## 16.`Vue`性能优化办法
### 1)编码阶段
* 尽量减少data中的数据,data中的数据都会减少getter和setter,会收集对应的watcher;
* 如果须要应用v-for给每项元素绑定事件时应用事件代理;
* SPA 页面采纳keep-alive缓存组件;
* 在更多的状况下,应用v-if代替v-show;
* key保障惟一;
* 应用路由懒加载、异步组件;
* 防抖、节流;
* 第三方模块按需导入;
* 长列表滚动到可视区域动静加载;
* 图片懒加载;
### 2)用户体验:
* 骨架屏;
* PWA;
* 还能够应用缓存(客户端缓存、服务端缓存)优化、服务端开启gzip压缩等。
### 3)SEO优化
* 预渲染;
* 服务端渲染SSR;
### 4)打包优化
* 压缩代码;
* Tree Shaking/Scope Hoisting;
* 应用cdn加载第三方模块;
* 多线程打包happypack;
* splitChunks抽离公共文件;
* sourceMap优化;
## 17.`v-model`的原理
`v-model`实质就是一个语法糖,能够看成是value + input办法的语法糖。能够通过model属性的prop和event属性来进行自定义。
>原生的v-model,会依据标签的不同生成不同的事件和属性。
>v-model 在外部为不同的输出元素应用不同的属性并抛出不同的事件:
1)text 和 textarea 元素应用 value 属性和 input 事件;
2)checkbox 和 radio 应用 checked 属性和 change 事件;
3)select 字段将 value 作为 prop 并将 change 作为事件。
### 例子
model: {
prop: ‘checked’,
event: ‘change’
}
如果想要更改 `checked` 这个 `prop` 能够在 `Vue` 的 `instance` 中用以下这行代码发送 `change` 这个 `event`,并将指标的变动值传给 `checked` 这个 `prop`。
this.$emit(‘change’, $event.target.value);
## 18.`nextTick`的实现原理是什么?
在下次 `DOM` 更新循环完结之后执行提早回调。`nextTick`次要应用了宏工作和微工作。依据执行环境别离尝试采纳
* Promise
* MutationObserver
* setImmediate
* 如果以上都不行则采纳setTimeout
定义了一个异步办法,屡次调用`nextTick`会将办法存入队列中,通过这个异步办法清空以后队列。
#### 19.谈谈`Computed`和`Watch`!
`Computed`实质是一个具备缓存的`watcher`,依赖的属性发生变化就会更新视图。
实用于计算比拟耗费性能的计算场景。当表达式过于简单时,在模板中放入过多逻辑会让模板难以保护,能够将简单的逻辑放入计算属性中解决。
`Watch`没有缓存性,更多的是察看的作用,能够监听某些数据执行回调。
当咱们须要深度监听对象中的属性时,能够关上`deep:true`选项,这样便会对对象中的每一项进行监听。
这样会带来性能问题,优化的话能够应用字符串模式监听,如果没有写到组件中,不要遗记应用`unWatch`手动登记哦。
#### 20.说一下`Vue`的生命周期
`beforeCreate`是`new Vue()`之后触发的第一个钩子,在以后阶段`data`、`methods`、`computed`以及`watch`上的数据和办法都不能被拜访。
`created`在实例创立实现后产生,以后阶段曾经实现了数据观测,也就是能够应用数据,更改数据,在这里更改数据不会触发updated函数。能够做一些初始数据的获取,在以后阶段无奈与Dom进行交互,如果非要想,能够通过`vm.$nextTick`来拜访`Dom`。
`beforeMount`产生在挂载之前,在这之前`template`模板已导入渲染函数编译。而以后阶段虚构`Dom`曾经创立实现,行将开始渲染。在此时也能够对数据进行更改,不会触发`updated`。
`mounted`在挂载实现后产生,在以后阶段,实在的`Dom`挂载结束,数据实现双向绑定,能够拜访到`Dom`节点,应用`$refs`属性对`Dom`进行操作。
`beforeUpdate`产生在更新之前,也就是响应式数据产生更新,虚构`dom`从新渲染之前被触发,你能够在以后阶段进行更改数据,不会造成重渲染。
`updated`产生在更新实现之后,以后阶段组件`Dom`已实现更新。要留神的是防止在此期间更改数据,因为这可能会导致有限循环的更新。
`beforeDestroy`产生在实例销毁之前,在以后阶段实例齐全能够被应用,咱们能够在这时进行善后收尾工作,比方革除计时器。
`destroyed`产生在实例销毁之后,这个时候只剩下了`dom`空壳。组件已被拆解,数据绑定被卸除,监听被移出,子实例也通通被销毁。
## 21.`Vue`模版编译原理晓得吗,能简略说一下?
简略说,`Vue`的编译过程就是将`template`转化为`render`函数的过程。会经验以下阶段:
* 生成AST树
* 优化
* codegen
首先解析模版,生成`AST`语法树(一种用`JavaScript`对象的模式来形容整个模板)。
应用大量的正则表达式对模板进行解析,遇到标签、文本的时候都会执行对应的钩子进行相干解决。
`Vue`的数据是响应式的,但其实模板中并不是所有的数据都是响应式的。有一些数据首次渲染后就不会再变动,对应的`DOM`也不会变动。那么优化过程就是深度遍历AST树,依照相干条件对树节点进行标记。这些被标记的节点(动态节点)咱们就能够跳过对它们的比对,对运行时的模板起到很大的优化作用。
编译的最初一步是将优化后的`AST`树转换为可执行的代码。
## 22.`Vue`的长处及毛病?
首先Vue最外围的两个特点,数据驱动和组件化。
### 响应式:
这也就是vue.js最大的长处,通过MVVM思维实现数据的双向绑定,通过虚构DOM让咱们能够用数据来操作DOM,而不用去操作实在的DOM,晋升了性能。且让开发者有更多的工夫去思考业务逻辑。
### 组件化:
把一个单页利用中的各个模块拆分到一个个组件当中,或者把一些公共的局部抽离进去做成一个可复用的组件。所以组件化带来的益处就是,进步了开发效率,不便重复使用,使我的项目的可维护性更强。
### 虚构DOM:
当然,这个不是vue中独有的。
### 毛病:
基于对象配置文件的写法,也就是options写法,开发时不利于对一个属性的查找。另外一些毛病,在小我的项目中感觉不太出什么,vuex的魔法字符串,对ts的反对。兼容性上存在一些问题。
### 不利于seo:
导航不可用,如果肯定要导航须要自行实现后退、后退。(因为是单页面不能用浏览器的后退后退性能,所以须要本人建设堆栈治理)。
首次加载时耗时多。
## 23.`Vue`中`hash`模式和`history`模式的区别?
最显著的是在显示上,`hash`模式的`URL`中会夹杂着`#`号,而history没有。
`Vue`底层对它们的实现形式不同。hash模式是依附`onhashchange`事件(监听`location.hash`的扭转),而`history`模式是次要是依附的`HTML5 history`中新增的两个办法,`pushState()`能够扭转url地址且不会发送申请,`replaceState()`能够读取历史记录栈,还能够对浏览器记录进行批改。
当真正须要通过`URL`向后端发送HTTP申请的时候,比方常见的用户手动输出`URL`后回车,或者是刷新(重启)浏览器,这时候`history`模式须要后端的反对。
因为`history`模式下,前端的`URL`必须和理论向后端发送申请的URL统一,例如有一个`URL`是带有门路`path`的(例如`www.lindaidai.wang/blogs/id`),如果后端没有对这个门路做解决的话,就会返回`404`谬误。所以须要后端减少一个笼罩所有状况的候选资源,个别会配合前端给出的一个`404`页面。
hash:
window.onhashchange = function(event){
// location.hash获取到的是包含#号的,如”#heading-3″
// 所以能够截取一下
let hash = location.hash.slice(1);
}
## 24.你的接口申请个别放在哪个生命周期中?
接口申请个别放在`mounted`中,但须要留神的是服务端渲染时不反对`mounted`,须要放到`created`中。
1. 如果不须要操作Dom元素或子组件, 那放到created或者mounted中都能够, 如果须要操作Dom元素, 则须要放到mounted中. 并且, 须要留神的是, 即使是放到created, Vue不会期待异步返回的数据再去执行下一步. 从体验的角度思考, 放在mounted中更好.
#### 25.`Vue SSR`渲染原理?
### 长处:
#### 更利于SEO
不同爬虫工作原理相似,只会爬取源码,不会执行网站的任何脚本(Google除外,据说Googlebot能够运行javaScript)。应用了Vue或者其它MVVM框架之后,页面大多数DOM元素都是在客户端依据js动静生成,可供爬虫抓取剖析的内容大大减少。另外,浏览器爬虫不会期待咱们的数据实现之后再去抓取咱们的页面数据。服务端渲染返回给客户端的是曾经获取了异步数据并执行JavaScript脚本的最终HTML,网络爬中就能够抓取到残缺页面的信息。
#### 更利于首屏渲染
首屏的渲染是node发送过去的html字符串,并不依赖于js文件了,这就会使用户更快的看到页面的内容。尤其是针对大型单页利用,打包后文件体积比拟大,一般客户端渲染加载所有所需文件工夫较长,首页就会有一个很长的白屏等待时间。
### 场景:
交互少,数据多,例如新闻,博客,论坛类等
### 原理:
相当于服务端后面加了一层url调配,能够假想为服务端的中间层,
当地址栏url扭转或者间接刷新,其实间接从服务器返回内容,是一个蕴含内容局部的html模板,是服务端渲染
而在交互过程中则是ajax解决操作,部分刷新,首先是在history模式下,通过history. pushState形式进而url扭转,而后申请后盾数据服务,拿到真正的数据,做到部分刷新,这时候接管的是数据而不是模板
### 毛病
#### 服务端压力较大
原本是通过客户端实现渲染,当初对立到服务端node服务去做。尤其是高并发拜访的状况,会大量占用服务端CPU资源;
#### 开发条件受限
在服务端渲染中,created和beforeCreate之外的生命周期钩子不可用,因而我的项目援用的第三方的库也不可用其它生命周期钩子,这对援用库的抉择产生了很大的限度;
#### 平安问题
因为做了node服务,因而平安方面也须要思考DDOS攻打和sql注入
#### 学习老本绝对较高
除了对webpack、Vue要相熟,还须要把握node、Express相干技术。绝对于客户端渲染,我的项目构建、部署过程更加简单。
#### 26.`new Vue()` 产生了什么?
1)`new Vue()`是创立`Vue`实例,它外部执行了根实例的初始化过程。
2)具体包含以下操作:
* 选项合并
* `$children`,`$refs`,`$slots`,`$createElement`等实例属性的办法初始化
* 自定义事件处理
* 数据响应式解决
* 生命周期钩子调用 (`beforecreate created`)
* 可能的挂载
### 总结:
`new Vue()`创立了根实例并筹备好数据和办法,将来执行挂载时,此过程还会递归的利用于它的子组件上,最终造成一个有严密关系的组件实例树。
#### 27.`Vue.use`是干什么的?原理是什么?
>`vue.use` 是用来应用插件的,咱们能够在插件中扩大全局组件、指令、原型办法等。
1、查看插件是否注册,若已注册,则间接跳出;
2、解决入参,将第一个参数之后的参数归集,并在首部塞入 `this` 上下文;
3、执行注册办法,调用定义好的 `install` 办法,传入解决的参数,若没有 `install` 办法并且插件自身为 `function` 则间接进行注册;
1) 插件不能反复的加载
`install` 办法的第一个参数是vue的构造函数,其余参数是Vue.set中除了第一个参数的其余参数; 代码:`args.unshift(this)`
2) 调用插件的install 办法 代码:
typeof plugin.install === ‘function’
3) 插件自身是一个函数,间接让函数执行。 代码:
plugin.apply(null, args)
4) 缓存插件。 代码:
installedPlugins.push(plugin)
#### 28.请说一下响应式数据的了解?
1) 对象外部通过`defineReactive`办法,应用 `Object.defineProperty()` 监听数据属性的 `get` 来进行数据依赖收集,再通过 `set` 来实现数据更新的派发;
2) 数组则通过重写数组办法来实现的。扩大它的 7 个变更⽅法,通过监听这些办法能够做到依赖收集和派发更新;
#### 对应源码
Object.defineProperty(obj, key, {
enumerable: true,
configurable: true,
get: function reactiveGetter () {
const value = getter ? getter.call(obj) : val
if (Dep.target) {
dep.depend() // ** 收集依赖 ** /
if (childOb) {
childOb.dep.depend()
if (Array.isArray(value)) {
dependArray(value)
}
}
}
return value
},
set: function reactiveSetter (newVal) {
const value = getter ? getter.call(obj) : val
if (newVal === value || (newVal !== newVal && value !== value)) {
return
}
if (process.env.NODE_ENV !== 'production' && customSetter) {
customSetter()
}
val = newVal
childOb = !shallow && observe(newVal)
dep.notify() /**告诉相干依赖进行更新**/
}
})
#### 29.`Vue`中是如何检测数组变动?
数组思考性能起因没有用`defineProperty`对数组的每一项进行拦挡,而是抉择重写数组 办法以进行重写。
当数组调用到这 7 个办法的时候,执行 `ob.dep.notify()` 进行派发告诉 `Watcher` 更新;
在Vue中批改数组的索引和长度是无奈监控到的。
须要通过以下7种变异办法批改数组才会触发数组对应的`wacther`进行更新。
数组中如果是对象数据类型也会进行递归劫持。
#### 阐明:那如果想要改索引更新数据怎么办?
能够通过`Vue.set()`来进行解决 =》 外围外部用的是 `splice` 办法。
#### 源码
const arrayProto = Array.prototype
export const arrayMethods = Object.create(arrayProto)
const methodsToPatch = [
‘push’,
‘pop’,
‘shift’,
‘unshift’,
‘splice’,
‘sort’,
‘reverse’
]
methodsToPatch.forEach(function (method) { // 重写原型办法
const original = arrayProto[method] // 调用原数组的办法
def(arrayMethods, method, function mutator (…args) {
const result = original.apply(this, args)
const ob = this.__ob__
let inserted
switch (method) {
case 'push':
case 'unshift':
inserted = args
break
case 'splice':
inserted = args.slice(2)
break
}
if (inserted) ob.observeArray(inserted)
// notify change
ob.dep.notify() // 当调用数组办法后,手动告诉视图更新
return result
})
})
this.observeArray(value) // 进行深度监控
#### 30.`Vue.set` 办法是如何实现的?
### 外围答案
为什么`$set`能够触发更新,咱们给对象和数组自身都减少了`dep`属性,当给对象新增不存在的属性则触发对象依赖的`watcher`去更新,当批改数组索引时咱们调用数组自身的`splice`办法去更新数组。
### 补充答案
1) 如果是数组,调用重写的splice办法 (这样能够更新视图 )
代码:
target.splice(key, 1, val)
2) 如果不是响应式的也不须要将其定义成响应式属性。
3) 如果是对象,将属性定义成响应式的
defineReactive(ob.value, key, val)
### 源码地址
export function set (target: Array<any> | Object, key: any, val: any): any {
if (process.env.NODE_ENV !== ‘production’ &&
(isUndef(target) || isPrimitive(target))
) {
warn(`Cannot set reactive property on undefined, null, or primitive value: ${(target: any)}`)
}
if (Array.isArray(target) && isValidArrayIndex(key)) {
target.length = Math.max(target.length, key)
target.splice(key, 1, val)
return val
}
if (key in target && !(key in Object.prototype)) {
target[key] = val
return val
}
const ob = (target: any).__ob__
if (target._isVue || (ob && ob.vmCount)) {
process.env.NODE_ENV !== 'production' && warn(
'Avoid adding reactive properties to a Vue instance or its root $data ' +
'at runtime - declare it upfront in the data option.'
)
return val
}
if (!ob) {
target[key] = val
return val
}
defineReactive(ob.value, key, val)
ob.dep.notify()
return val
}
#### 31.`Vue`中模板编译原理?
### 外围答案
将`template`转换成`render`函数
### 补充阐明
这里要留神的是咱们在开发时尽量不要应用template.
因为将template转化成render办法须要在运行时进行编译操作会有性能损耗,同时援用带有complier包的vue体积也会变大.
默认.vue文件中的 template解决是通过vue-loader 来进行解决的并不是通过运行时的编译。
### 流程如下
1) 将 template 模板转换成 ast 语法树 - parserHTML
2) 对动态语法做动态标记 - markUp
3) 从新生成代码 - codeGen
### 源码地址
function baseCompile (
template: string,
options: CompilerOptions
) {
const ast = parse(template.trim(), options) // 1.将模板转化成ast语法树
if (options.optimize !== false) { // 2.优化树
optimize(ast, options)
}
const code = generate(ast, options) // 3.生成树
return {
ast,
render: code.render,
staticRenderFns: code.staticRenderFns
}
})
const ncname = [a-zA-Z_][\\-\\.0-9_a-zA-Z]*
;
const qnameCapture = ((?:${ncname}\\:)?${ncname})
;
const startTagOpen = new RegExp(^<${qnameCapture}
); // 标签结尾的正则 捕捉的内容是标签名
const endTag = new RegExp(^<\\/${qnameCapture}[^>]*>
); // 匹配标签结尾的 </div>
const attribute = /^\s(1+)(?:\s(=)\s(?:”(2)”+|'(3*)’+|(4+)))?/; // 匹配属性的
const startTagClose = /^\s*(/?)>/; // 匹配标签完结的 >
let root;
let currentParent;
let stack = []
function createASTElement(tagName,attrs){
return {
tag:tagName,
type:1,
children:[],
attrs,
parent:null
}
}
function start(tagName,attrs){
let element = createASTElement(tagName,attrs);
if(!root){
root = element;
}
currentParent = element;
stack.push(element);
}
function chars(text){
currentParent.children.push({
type:3,
text
})
}
function end(tagName){
const element = stack[stack.length-1];
stack.length --;
currentParent = stack[stack.length-1];
if(currentParent){
element.parent = currentParent;
currentParent.children.push(element)
}
}
function parseHTML(html){
while(html){
let textEnd = html.indexOf('<');
if(textEnd == 0){
const startTagMatch = parseStartTag();
if(startTagMatch){
start(startTagMatch.tagName,startTagMatch.attrs);
continue;
}
const endTagMatch = html.match(endTag);
if(endTagMatch){
advance(endTagMatch[0].length);
end(endTagMatch[1])
}
}
let text;
if(textEnd >=0 ){
text = html.substring(0,textEnd)
}
if(text){
advance(text.length);
chars(text);
}
}
function advance(n) {
html = html.substring(n);
}
function parseStartTag(){
const start = html.match(startTagOpen);
if(start){
const match = {
tagName:start[1],
attrs:[]
}
advance(start[0].length);
let attr,end
while(!(end = html.match(startTagClose)) && (attr=html.match(attribute))){
advance(attr[0].length);
match.attrs.push({name:attr[1],value:attr[3]})
}
if(end){
advance(end[0].length);
return match
}
}
}
}
// 生成语法树
parseHTML(<div id="container"><p>hello<span>zf</span></p></div>
);
function gen(node){
if(node.type == 1){
return generate(node);
}else{
return `_v(${JSON.stringify(node.text)})`
}
}
function genChildren(el){
const children = el.children;
if(el.children){
return `[${children.map(c=>gen(c)).join(',')}]`
}else{
return false;
}
}
function genProps(attrs){
let str = '';
for(let i = 0; i < attrs.length;i++){
let attr = attrs[i];
str+= `${attr.name}:${attr.value},`;
}
return `{attrs:{${str.slice(0,-1)}}}`
}
function generate(el){
let children = genChildren(el);
let code = `_c('${el.tag}'${
el.attrs.length? `,${genProps(el.attrs)}`:''
}${
children? `,${children}`:''
})`;
return code;
}
// 依据语法树生成新的代码
let code = generate(root);
let render = with(this){return ${code}}
;
// 包装成函数
let renderFn = new Function(render);
console.log(renderFn.toString());
#### 32.`Proxy` 与 `Object.defineProperty` 优劣比照?
### Proxy 的劣势如下:
1)`Proxy` 能够间接监听对象而非属性;
2)`Proxy` 能够间接监听数组的变动;
3)`Proxy` 有多达 13 种拦挡办法,不限于 apply、ownKeys、deleteProperty、has 等等是 Object.defineProperty 不具备的;
4)`Proxy` 返回的是一个新对象,咱们能够只操作新的对象达到目标,而 Object.defineProperty 只能遍历对象属性间接批改;
5)Proxy 作为新规范将受到浏览器厂商重点继续的性能优化,也就是传说中的新规范的性能红利;
### Object.defineProperty 的劣势如下:
兼容性好,反对 IE9,而 Proxy 的存在浏览器兼容性问题,而且无奈用 polyfill 磨平,因而 Vue 的作者才申明须要等到下个大版本( 3.0 )能力用 Proxy 重写。
#### 33.`Vue3.x`响应式数据原理?
`Vue3.x`改用`Proxy`代替`Object.defineProperty`。
因为`Proxy`能够间接监听对象和数组的变动,并且有多达13种拦挡办法。并且作为新规范将受到浏览器厂商重点继续的性能优化。
`Proxy`只会代理对象的第一层,那么`Vue3`又是怎么解决这个问题的呢?
判断以后`Reflect.get`的返回值是否为`Object`,如果是则再通过`reactive`办法做代理, 这样就实现了深度观测。
监测数组的时候可能触发屡次`get/set`,那么如何避免触发屡次呢?
咱们能够判断`key`是否为以后被代理对象`target`本身属性,也能够判断旧值与新值是否相等,只有满足以上两个条件之一时,才有可能执行`trigger`。
#### 34.`Vue`的生命周期办法有哪些?
>总共分为8个阶段:创立前/后,载入前/后,更新前/后,销毁前/后。
### 1、创立前/后:
1) `beforeCreate`阶段:`vue`实例的挂载元素`el`和数据对象`data`都为`undefined`,还未初始化。
阐明:在以后阶段data、methods、computed以及watch上的数据和办法都不能被拜访。
2) created阶段:vue实例的数据对象data有了,el还没有。
阐明:能够做一些初始数据的获取,在以后阶段无奈与Dom进行交互,如果非要想,能够通过vm.$nextTick来拜访Dom。
### 2、载入前/后:
1) beforeMount阶段:vue实例的$el和data都初始化了,但还是挂载之前为虚构的dom节点。
阐明:以后阶段虚构Dom曾经创立实现,行将开始渲染。在此时也能够对数据进行更改,不会触发updated。
2) mounted阶段:vue实例挂载实现,data.message胜利渲染。
阐明:在以后阶段,实在的Dom挂载结束,数据实现双向绑定,能够拜访到Dom节点,应用$refs属性对Dom进行操作。
## 3、更新前/后:
1) beforeUpdate阶段:响应式数据更新时调用,产生在虚构DOM打补丁之前,适宜在更新之前拜访现有的DOM,比方手动移除已增加的事件监听器。
阐明:能够在以后阶段进行更改数据,不会造成重渲染。
2) updated阶段:虚构DOM从新渲染和打补丁之后调用,组成新的DOM曾经更新,防止在这个钩子函数中操作数据,避免死循环。
阐明:以后阶段组件Dom已实现更新。要留神的是防止在此期间更改数据,因为这可能会导致有限循环的更新。
## 4、销毁前/后:
1) beforeDestroy阶段:实例销毁前调用,实例还能够用,this能获取到实例,罕用于销毁定时器,解绑事件。
阐明:在以后阶段实例齐全能够被应用,咱们能够在这时进行善后收尾工作,比方革除计时器。
2) destroyed阶段:实例销毁后调用,调用后所有事件监听器会被移除,所有的子实例都会被销毁。
阐明:以后阶段组件已被拆解,数据绑定被卸除,监听被移出,子实例也通通被销毁。
### 补充阐明
第一次页面加载时会触发:beforeCreate, created, beforeMount, mounted。
1) created 实例曾经创立实现,因为它是最早触发的起因能够进行一些数据,资源的申请。(服务器渲染反对created办法)
2) mounted 实例曾经挂载实现,能够进行一些DOM操作。(接口申请)
#### 35.生命周期钩子是如何实现的?
#### 外围答案:
Vue的生命周期钩子就是回调函数而已,当创立组件实例的过程中会调用对应的钩子办法。
#### 补充阐明
外部次要是应用callHook办法来调用对应的办法。外围是一个公布订阅模式,将钩子订阅好(外部采纳数组的形式存储),在对应的阶段进行公布。
#### 36.`Vue` 的父组件和子组件生命周期钩子执行程序?
### 外围答案:
第一次页面加载时会触发 beforeCreate, created, beforeMount, mounted 这几个钩子。
### 渲染过程:
父组件挂载实现肯定是等子组件都挂载实现后,才算是父组件挂载完,所以父组件的mounted在子组件mouted之后
* 父beforeCreate ->
* 父created ->
* 父beforeMount ->
* 子beforeCreate ->
* 子created ->
* 子beforeMount ->
* 子mounted ->
* 父mounted
### 子组件更新过程:
影响到父组件:
* 父beforeUpdate ->
* 子beforeUpdate->
* 子updated ->
* 父updted
不影响父组件:
* 子beforeUpdate ->
* 子updated
### 父组件更新过程:
影响到子组件:
* 父beforeUpdate ->
* 子beforeUpdate->
* 子updated ->
* 父updted
不影响子组件:
* 父beforeUpdate ->
* 父updated
### 销毁过程:
* 父beforeDestroy ->
* 子beforeDestroy ->
* 子destroyed ->
* 父destroyed
>重要:父组件期待子组件实现后,才会执行本人对应实现的钩子。
#### 37.组件中写 `name`选项有哪些益处及作用?
### 外围答案:
1) 能够通过名字找到对应的组件 ( 递归组件 )
2) 能够通过name属性实现缓存性能 (keep-alive)
3) 能够通过name来辨认组件 (跨级组件通信时十分重要)
#### 38.`keep-alive`平时在哪里应用?原理是?
### 外围答案:
keep-alive 次要是组件缓存,采纳的是LRU算法。最近最久未应用法。
罕用的两个属性include/exclude,容许组件有条件的进行缓存。
两个生命周期activated/deactivated,用来得悉以后组件是否处于沉闷状态。
### 源码
abstract: true, // 形象组件
props:{
include: patternTypes, // 要缓存的有哪些
exclude: patternTypes, // 要排除的有哪些
max: [String, Number] //最大缓存数量
}
if(cache[key]) { // 通过key 找到缓存,获取实例
vnode.componentInstance = cache[key].componentInstance
remove(keys, key) //将key删除掉
keys.push(key) // 放到开端
} else {
cache[key] = vnode // 没有缓存过
keys.push(key) //存储key
if(this.max && keys.length > parseInt(this.max)) { // 如果超过最大缓存数
// 删除最早缓存的
pruneCacheEntry(cache, keys[0], keys, this._vnode)
}
}
vnode.data.keepAlive = true // 标记走了缓存
#### 39.`Vue.mixin`的应用场景和原理?
### 外围答案:
Vue.mixin的作用就是抽离公共的业务逻辑,原理相似“对象的继承”,当组件初始化时会调用 mergeOptions办法进行合并,采纳策略模式针对不同的属性进行合并,如果混入的数据和自身组件中的数据抵触,会采纳“就近准则”以组件的数据为准。
### 补充答复:
`mixin`中有很多缺点“命名抵触问题”,“依赖问题”,“数据起源问题”,这里强调一下`mixin`的数据是不会被共享的。
### 注意事项
如果只是提取专用的数据或者通用的办法,并且这些数据或者办法,不须要组件间进行保护,就能够应用`mixins`。(相似于js中封装的一些专用的办法)
#### 40.`mixins`和`vuex`的区别?
`vuex`公共状态治理,在一个组件被引入后,如果该组件扭转了vuex外面的数据状态,其余引入vuex数据的组件也会对应批改,所有的vue组件利用的都是同一份vuex数据。(在js中,有点相似于浅拷贝)
`vue`引入`mixins`数据,`mixins`数据或办法,在每一个组件中都是独立的,互不烦扰的,都属于vue组件本身。(在js中,有点相似于深度拷贝)
#### 41.`mixins`和公共组件的区别?
通用的数据和办法,的确能够提出一个通用的组件,由父子组件传参的模式进行分享专用。
### 公共组件
子组件通过props接管来自父组件(公共组件)的参数或者办法,但vue不倡议,子组件间接批改props接管到的父组件的数据。须要在子组件的data中或者computed中定义一个字段来接管。(有点麻烦)
公共组件最次要的作用还是复用雷同的vue组件(有视图,有办法,有状态
)。
### mixins
如果只是提取专用的数据或者通用的办法,并且这些数据或者办法,不须要组件间进行保护,就能够应用mixins。(相似于js中封装的一些专用的办法)
#### 42.`Vue-router`有几种钩子函数?具体是什么及执行流程是怎么的?
### 外围答案:
路由钩子的执行流程,钩子函数品种有:全局守卫、路由守卫、组件守卫。
### 残缺的导航解析流程
`1.`导航被触发;
`2.`在失活的组件里调用beforeRouteLeave守卫;
`3.`调用全局beforeEach守卫;
`4.`在复用组件里调用beforeRouteUpdate守卫;
`5.`调用路由配置里的beforeEnter守卫;
`6.`解析异步路由组件;
`7.`在被激活的组件里调用beforeRouteEnter守卫;
`8.`调用全局beforeResolve守卫;
`9.`导航被确认;
`10.`调用全局的afterEach钩子;
`11.`DOM更新;
`12.`用创立好的实例调用beforeRouteEnter守卫中传给next的回调函数。
#### 43.`vue-router` 两种模式的区别?
### 外围答案:
`vue-router` 有 3 种路由模式:`hash`、`history`、`abstract`。
1) `hash`模式:hash + hashChange
特点:hash尽管在URL中,但不被包含在HTTP申请中;用来领导浏览器动作,对服务端平安无用,hash不会重加载页面。通过监听 hash(#)的变动来执行js代码 从而实现 页面的扭转。
### 外围代码:
window.addEventListener(‘hashchange‘,function(){
self.urlChange()
})
2) `history`模式:historyApi + popState
`HTML5`推出的history API,由pushState()记录操作历史,监听popstate事件来监听到状态变更;
因为 只有刷新 这个url(www.ff.ff/jjkj/fdfd/fdf/fd)就会申请服务器,然而服务器上基本没有这个资源,所以就会报404,解决方案就 配置一下服务器端。
#### 阐明:
1)`hash`: 应用 URL hash 值来作路由。反对所有浏览器,包含不反对 HTML5 History Api 的浏览器;
2)`history` : 依赖 HTML5 History API 和服务器配置。具体能够查看 HTML5 History 模式;
3)`abstract` : 反对所有 JavaScript 运行环境,如 Node.js 服务器端。如果发现没有浏览器的 API,路由会主动强制进入这个模式.
#### 44.`Vue` 为什么须要虚构DOM? 虚构`DOM`的优劣如何?
## 外围答案:
`Virtual DOM` 就是用js对象来形容实在DOM,是对实在DOM的形象,因为间接操作DOM性能低然而js层的操作效率高,能够将DOM操作转化成对象操作,最终通过diff算法比对差别进行更新DOM (缩小了对实在DOM的操作)。虚构DOM不依赖实在平台环境从而也能够实现跨平台。
## 补充答复:
`虚构DOM`的实现就是一般对象蕴含tag、data、hildren等属性对实在节点的形容。(实质上就是在JS和DOM之间的一个缓存)
`Vue2`的 Virtual DOM 借鉴了开源库snabbdom的实现。
`VirtualDOM`映射到实在DOM要经验VNode的create、diff、patch等阶段。
#### 45.`Vue`中`key`的作用和工作原理,说说你对它的了解
### 外围答案:
#### 例如:
v-for=”(item, index) in tableList” :key=”index”
key的作用次要是为了高效的更新虚构DOM,其原理是vue在patch过程中通过key能够精准判断两个节点是否是同一个,从而防止频繁更新不同元素,使得整个patch过程更加高效,缩小DOM操作量,进步性能。
#### 补充答复:
1) 若不设置key还可能在列表更新时引发一些荫蔽的bug
2) vue中在应用雷同标签名元素的过渡切换时,也会应用到key属性,其目标也是为了让vue能够辨别它们,否则vue只会替换其外部属性而不会触发过渡成果。
#### 46.说说`$nextTick`的应用?
1) `nextTick`的回调是在下次`DOM`更新循环完结之后执行的提早回调。
2) 在批改数据之后立刻应用这个办法,获取更新后的`DOM`。
3) `nextTick`次要应用了宏工作和微工作。
#### 47.`computed` 和 `watch` 的区别和使用的场景?
### 外围答案:
`computed`:
计算属性。依赖其它属性值,并且 computed 的值有缓存,只有它依赖的属性值产生扭转,下一次获取 computed 的值时才会从新计算 computed 的值;
`watch`:
监听数据的变动。更多的是「察看」的作用,相似于某些数据的监听回调 ,每当监听的数据变动时都会执行回调进行后续操作;
### 使用场景:
1)当咱们须要进行数值计算,并且依赖于其它数据时,应该应用 computed,因为能够利用 computed 的缓存个性,防止每次获取值时,都要从新计算;
2)当咱们须要在数据变动时执行异步或开销较大的操作时,应该应用 watch,应用 watch 选项容许咱们执行异步操作 ( 拜访一个 API ),限度咱们执行该操作的频率,并在咱们失去最终后果前,设置中间状态。
这些都是计算属性无奈做到的。
#### 48.如何了解自定义指令?
#### 外围答案:
>指令的实现原理,能够从编译原理 =>代码生成=> 指令钩子实现进行概述
1)在生成 ast 语法树时,遇到指令会给以后元素增加directives属性
2)通过 genDirectives 生成指令代码
3)在patch前将指令的钩子提取到 cbs中,在patch过程中调用对应的钩子。
4)当执行指令对应钩子函数时,调用对应指令定义的办法
#### 49.`$router`和`$route`的区别?
`this.$router`与`this.$route` 两者相差 一个 `r` 然而差距蛮大的。
`Vue Router`是`Vue.js`的路由管理器,在Vue实例外部,能够通过`$router`拜访路由实例
通过`$route`能够拜访以后激活的路由的状态信息,蕴含了以后`URL`解析失去的信息,还有URL匹配到的路由记录,能够将`$router`了解为一个容器去治理了一组`$route`,
而`$route`是进行了以后URL和组件的映射。
#### 50.`$router`对象有哪些属性?
* `$router.app`: 配置了router的Vue根实例。
* `$router.mode`: 路由应用的模式。
* `$router.currentRoute`: 以后路由对应的路由信息对象。
#### 51.`$router`对象有哪些办法?
* $router.beforeEach(to, from, next)
* $router.beforeResolve(to, from, next)
* $router.afterEach(to, from)
* $router.push(location[, onComplete[, onAbort]])
* $router.replace(location[, onComplete[, onAbort]])
* $router.go(n)
* $router.back()$router.back
* $history.forward()
* $router.getMatchedComponents([location])
* $router.resolve(location[, current[, append]])
* $router.addRoutes(route)
* $router.onReady(callback[, errorCallback])
* $router.onError(callback)
### 52.`$route`对象有哪些属性?
* `$route.path`:
返回字符串,对该当前路由的门路,总是解析为绝对路径。
* `$route.params`:
返回一个key-value对象,蕴含了动静片段和全匹配片段,如果没有路由参数,就是一个空对象。
* `$route.query`:
返回一个key-value对象,示意URL查问参数。
* `$route.hash`:
返回以后路由的带#的hash值,如果没有hash值,则为空字符串。
* `$route.fullPath`:
返回实现解析后的URL,蕴含查问参数和hash的残缺门路。
* `$route.matched`:
返回一个数组,蕴含以后路由的所有嵌套门路片段的路由记录,路由记录就是routes配置数组中的对象正本。
* `$route.name`:
如果存在以后路由名称则返回以后路由的名称。
* `$route.redirectedFrom`:
如果存在重定向,即为重定向起源的路由的名字。
#### 53.对`MVC (react)` `MVVM(vue)`的理解?
### 标签 腾讯 阿里 西门子
### 什么是MVC
* `M(modal)`:是应用程序中解决数据逻辑的局部。
* `V (view)` :是应用程序中数据显示的局部。
* `C(controller)`:是应用程序中解决用户交互的中央
### 什么是MVVM
* M(modal):模型,定义数据结构。
* C(controller):实现业务逻辑,数据的增删改查。在MVVM模式中个别把C层算在M层中,(只有在现实的双向绑定模式下,Controller 才会齐全的隐没。这种现实状态个别不存在)。
* VM(viewModal):视图View的模型、映射和显示逻辑(如if for等,非业务逻辑),另外绑定器也在此层。ViewModel是基于视图开发的一套模型,如果你的利用是给盲人用的,那么也能够开发一套基于Audio的模型AudioModel。
* V(view) :将ViewModel通过特定的GUI展现进去,并在GUI控件上绑定视图交互事件,V(iew)个别由MVVM框架主动生成在浏览器中。
### 54.`Vue.js` 如何让`CSS`只在以后组件中起作⽤?
将以后组件的 `<style>` 批改为 `<style scoped>`
#### 55.`Vue` 中的`diff`原理?
### 外围答案:
vue的diff算法是平级比拟,不思考跨级比拟的状况。外部采纳深度递归的形式 + 双指针的形式进行比拟。
### 补充答复:
1) 先比拟是否是雷同节点
2) 雷同节点比拟属性,并复用老节点
3) 比拟儿子节点,思考老节点和新节点儿子的状况
4) 优化比拟:头头、尾尾、头尾、尾头
5) 比对查找进行复用
### Vue2 与 Vue3.x 的diff算法:
Vue2的外围Diff算法采纳了双端比拟的算法,同时从新旧children的两端开始进行比拟,借助key值找到可复用的节点,再进行相干操作。
Vue3.x借鉴了ivi算法和 inferno算法,该算法中还使用了动静布局的思维求解最长递归子序列。(理论的实现能够联合Vue3.x源码看。)
### 56.Vuex的5个外围属性是什么?
别离是 state、getters、mutations、actions、modules 。
### 57.在Vuex中应用mutationvuex要留神什么?
mutation 必须是同步函数
### 58.Vuex中action和mutation有什么相同点?
第二参数都能够接管内部提交时传来的参数。
this.$store.dispatch(‘ACTION_NAME’,data)
和
this.$store.commit(‘SET_NUMBER’,10)
在组件中屡次提交同一个action,怎么写应用更不便。
### 59.那为什么new Vue里data能够是一个对象?
new Vue({
el: ‘#app’,
router,
template: ‘<App/>’,
components: {App}
})
- \s”‘<>/= ↩
- ” ↩
- ‘ ↩
- \s”‘=<>` ↩
发表回复