admin 发布的文章

Components are one of the most powerful features of Vue.js, and the scope of component instances is independent of each other, which means that data between different components cannot be referenced to each other. Generally speaking, components can have the following relationships:
Image
As shown in the above figure, A and B, B and C, B and D are all father son relationships, C and D are sibling relationships, and A and C are intergenerational relationships (possibly multiple generations apart).
How to choose effective communication methods for different usage scenarios? This is the theme we are going to explore. This article summarizes several ways of communication between Vue components, such as props, $emits/$on, Vuex, $parent/$children, $attrs/$listeners, and provide/object. It provides easy to understand examples to explain the differences and usage scenarios, hoping to be helpful to our friends.
Please poke at Github's blog for the code in this article. If you get it on paper, you'll end up feeling shallow. Let's start typing the code more!
Method 1: props/$exit
Parent component A is passed to child component B through props, and B to A is implemented through $exit in component B and v-on in component A.

  1. Parent component passes values to child components
    Next, we will use an example to illustrate how the parent component passes values to the child component: how to obtain data users from the parent component App. vue in the child component Users. vue: ["Henry", "Bucky", "Emily"]
    //App. vue Parent Component

    Summary: The parent component passes data down to the child components through props. Note: There are three forms of data in the component: data, props, and computed

  2. Sub component passes values to parent component (through event form)
    Next, we will use an example to illustrate how a child component passes values to its parent component: when we click on "Vue. js Demo", the child component passes values to the parent component, and the text changes from "passing a value" to "passing a value from the child to the parent component", achieving the transfer of values from the child component to the parent component.
    Before child components pass values to parent components
    // 子组件

    // 父组件

    总结:子组件通过events给父组件发送消息,实际上就是子组件把自己的数据发送到父组件。

方法二、$emit/$on
这种方法通过一个空的Vue实例作为中央事件总线(事件中心),用它来触发事件和监听事件,巧妙而轻量地实现了任何组件间的通信,包括父子、兄弟、跨级。当我们的项目比较大时,可以选择更好的状态管理解决方案vuex。

1.具体实现方式:

var Event=new Vue();
Event.$emit(事件名,数据);
Event.$on(事件名,data => {});

2.举个例子
假设兄弟组件有三个,分别是A、B、C组件,C组件如何获取A或者B组件的数据



image
$on 监听了自定义事件 data-a和data-b,因为有时不确定何时会触发事件,一般会在 mounted 或 created 钩子中来监听。

方法三、vuex
image

1.简要介绍Vuex原理
Vuex实现了一个单向数据流,在全局拥有一个State存放数据,当组件要更改State中的数据时,必须通过Mutation进行,Mutation同时提供了订阅者模式供外部插件调用获取State数据的更新。而当所有异步操作(常见于调用后端接口异步获取更新数据)或批量的同步操作需要走Action,但Action也是无法直接修改State的,还是需要通过Mutation来修改State的数据。最后,根据State的变化,渲染到视图上。

2.简要介绍各模块在流程中的功能:
Vue Components:Vue组件。HTML页面上,负责接收用户操作等交互行为,执行dispatch方法触发对应action进行回应。
dispatch:操作行为触发方法,是唯一能执行action的方法。
actions:操作行为处理模块,由组件中的$store.dispatch('action 名称', data1)来触发。然后由commit()来触发mutation的调用 , 间接更新 state。负责处理Vue Components接收到的所有交互行为。包含同步/异步操作,支持多个同名方法,按照注册的顺序依次触发。向后台API请求的操作就在这个模块中进行,包括触发其他action以及提交mutation的操作。该模块提供了Promise的封装,以支持action的链式触发。
commit:状态改变提交操作方法。对mutation进行提交,是唯一能执行mutation的方法。
mutations:状态改变操作方法,由actions中的commit('mutation 名称')来触发。是Vuex修改state的唯一推荐方法。该方法只能进行同步操作,且方法名只能全局唯一。操作之中会有一些hook暴露出来,以进行state的监控等。
state:页面状态管理容器对象。集中存储Vue components中data对象的零散数据,全局唯一,以进行统一的状态管理。页面显示所需的数据从该对象中进行读取,利用Vue的细粒度数据响应机制来进行高效的状态更新。
getters:state对象读取方法。图中没有单独列出该模块,应该被包含在了render中,Vue Components通过该方法读取全局state对象。
3.Vuex与localStorage
vuex 是 vue 的状态管理器,存储的数据是响应式的。但是并不会保存起来,刷新之后就回到了初始状态,具体做法应该在vuex里数据改变的时候把数据拷贝一份保存到localStorage里面,刷新之后,如果localStorage里有保存的数据,取出来再替换store里的state。

let defaultCity = "上海"
try { // 用户关闭了本地存储功能,此时在外层加个try...catch
if (!defaultCity){

defaultCity = JSON.parse(window.localStorage.getItem('defaultCity'))

}
}catch(e){}
export default new Vuex.Store({
state: {

city: defaultCity

},
mutations: {

changeCity(state, city) {
  state.city = city
  try {
  window.localStorage.setItem('defaultCity', JSON.stringify(state.city));
  // 数据改变的时候把数据拷贝一份保存到localStorage里面
  } catch (e) {}
}

}
})
这里需要注意的是:由于vuex里,我们保存的状态,都是数组,而localStorage只支持字符串,所以需要用JSON转换:

JSON.stringify(state.subscribeList); // array -> string
JSON.parse(window.localStorage.getItem("subscribeList")); // string -> array
方法四、$attrs/$listeners
1.简介
多级组件嵌套需要传递数据时,通常使用的方法是通过vuex。但如果仅仅是传递数据,而不做中间处理,使用 vuex 处理,未免有点大材小用。为此Vue2.4 版本提供了另一种方法----$attrs/$listeners

$attrs:包含了父作用域中不被 prop 所识别 (且获取) 的特性绑定 (class 和 style 除外)。当一个组件没有声明任何 prop 时,这里会包含所有父作用域的绑定 (class 和 style 除外),并且可以通过 v-bind="$attrs" 传入内部组件。通常配合 interitAttrs 选项一起使用。
$listeners:包含了父作用域中的 (不含 .native 修饰器的) v-on 事件监听器。它可以通过 v-on="$listeners" 传入内部组件
接下来我们看个跨级通信的例子:

// index.vue

// childCom1.vue

// childCom2.vue

// childCom3.vue

image
如上图所示$attrs表示没有继承数据的对象,格式为{属性名:属性值}。Vue2.4提供了$attrs , $listeners 来传递数据与事件,跨级组件之间的通讯变得更简单。

简单来说:$attrs与$listeners 是两个对象,$attrs 里存放的是父组件中绑定的非 Props 属性,$listeners里存放的是父组件中绑定的非原生事件。

方法五、provide/inject
1.简介
Vue2.2.0新增API,这对选项需要一起使用,以允许一个祖先组件向其所有子孙后代注入一个依赖,不论组件层次有多深,并在起上下游关系成立的时间里始终生效。一言而蔽之:祖先组件中通过provider来提供变量,然后在子孙组件中通过inject来注入变量。
provide / inject API 主要解决了跨级组件间的通信问题,不过它的使用场景,主要是子组件获取上级组件的状态,跨级组件间建立了一种主动提供与依赖注入的关系。

2.举个例子
假设有两个组件: A.vue 和 B.vue,B 是 A 的子组件

// A.vue
export default {
provide: {

name: '浪里行舟'

}
}
// B.vue
export default {
inject: ['name'],
mounted () {

console.log(this.name);  // 浪里行舟

}
}
可以看到,在 A.vue 里,我们设置了一个 provide: name,值为 浪里行舟,它的作用就是将 name 这个变量提供给它的所有子组件。而在 B.vue 中,通过 inject 注入了从 A 组件中提供的 name 变量,那么在组件 B 中,就可以直接通过 this.name 访问这个变量了,它的值也是 浪里行舟。这就是 provide / inject API 最核心的用法。

需要注意的是:provide 和 inject 绑定并不是可响应的。这是刻意为之的。然而,如果你传入了一个可监听的对象,那么其对象的属性还是可响应的----vue官方文档
所以,上面 A.vue 的 name 如果改变了,B.vue 的 this.name 是不会改变的,仍然是 浪里行舟。

3.provide与inject 怎么实现数据响应式
一般来说,有两种办法:

provide祖先组件的实例,然后在子孙组件中注入依赖,这样就可以在子孙组件中直接修改祖先组件的实例的属性,不过这种方法有个缺点就是这个实例上挂载很多没有必要的东西比如props,methods
使用2.6最新API Vue.observable 优化响应式 provide(推荐)
我们来看个例子:孙组件D、E和F获取A组件传递过来的color值,并能实现数据响应式变化,即A组件的color变化后,组件D、E、F会跟着变(核心代码如下:)

image

// A 组件

A 组件

......
data() {

return {
  color: "blue"
};

},
// provide() {
// return {
// theme: {
// color: this.color //这种方式绑定的数据并不是可响应的
// } // 即A组件的color变化后,组件D、E、F不会跟着变
// };
// },
provide() {

return {
  theme: this//方法一:提供祖先组件的实例
};

},
methods: {

changeColor(color) {
  if (color) {
    this.color = color;
  } else {
    this.color = this.color === "blue" ? "red" : "blue";
  }
}

}
// 方法二:使用2.6最新API Vue.observable 优化响应式 provide
// provide() {
// this.theme = Vue.observable({
// color: "blue"
// });
// return {
// theme: this.theme
// };
// },
// methods: {
// changeColor(color) {
// if (color) {
// this.theme.color = color;
// } else {
// this.theme.color = this.theme.color === "blue" ? "red" : "blue";
// }
// }
// }
// F 组件

虽说provide 和 inject 主要为高阶插件/组件库提供用例,但如果你能在业务中熟练运用,可以达到事半功倍的效果!

方法六、$parent / $children与 ref
ref:如果在普通的 DOM 元素上使用,引用指向的就是 DOM 元素;如果用在子组件上,引用就指向组件实例
$parent / $children:访问父 / 子实例
需要注意的是:这两种都是直接得到组件实例,使用后可以直接调用组件的方法或访问数据。我们先来看个用 ref来访问组件的例子:

// component-a 子组件
export default {
data () {

return {
  title: 'Vue.js'
}

},
methods: {

sayHello () {
  window.alert('Hello');
}

}
}
// 父组件

不过,这两种方法的弊端是,无法在跨级或兄弟间通信。

// parent.vue



我们想在 component-a 中,访问到引用它的页面中(这里就是 parent.vue)的两个 component-b 组件,那这种情况下,就得配置额外的插件或工具了,比如 Vuex 和 Bus 的解决方案。

总结
常见使用场景可以分为三类:

父子通信:
父向子传递数据是通过 props,子向父是通过 events($emit);通过父链 / 子链也可以通信($parent / $children);ref 也可以访问组件实例;provide / inject API;$attrs/$listeners

兄弟通信:
Bus;Vuex

跨级通信:

Tips: This article is the most complete introduction to the asynchronous promise and Async/Await blogs, which are original and self transforming. Welcome to communicate with the front-end experts and point out any issues
1、 Why is there Async/Await?
We all know that Promise's solution already exists, why should ES7 propose a new Async/Await standard?
The answer is actually obvious: Although Promise breaks out of the strange circle of asynchronous nesting and is expressed more clearly in chain form, we also found that when there are a large number of asynchronous requests and the process is complex, we will find them filled with screens, which looks very difficult. The emergence of Async/Await in ES7 is to solve this complex situation.
Firstly, we must understand Promise.
2、 Promise Introduction
2.1 Promise Example
What is Promise, and many people should know the basic concepts? Take a look directly at the following code (the examples in the entire article are based on the setDelaySecond and setDelay functions, please remember):
Const setDelay=(millisecond)=>{
Return new Promise (resolve, reject)=>{
If (typeof millisecond!='number ') reject (new Error ('parameter must be of type number');
SetTimeout()=>{
Resolve (output after I delayed ${millisecond} milliseconds)
}Millisecond
})
}
We encapsulate a Promise in a function and return a Promise, which is more standardized.
You can see that the defined Promise has two parameters, resolve and reject.
Resolve: Transforms asynchronous execution from pending (request) to resolving (successful return), which is a function execution return.
Reject: As the name suggests, 'reject' means changing from a request to a 'failure'. It is a function that can execute and return a result, but we recommend returning an error, 'new Error()'.
For the above example, you can reject ('return a string ') and return it as you please, but we still recommend returning an Error object to make it clearer that it is' failed' and more standardized.
2.2 Promise then and catch
We obtain our return value through Promise's prototype method then:
SetDelay (3000)
. then (result)=>{
Console. log (result)//Output "I output after a delay of 2000 milliseconds"
})
Output the following value: 'I output after a delay of 2000 milliseconds'.
What if something goes wrong? Then use catch to capture:
SetDelay ('I am a string ')
. then (result)=>{
Console. log (result)//No entry
})
. catch ((err)=>{
Console. log (err)//Output error: "Parameter must be of type number"
})
Isn't it very simple? Okay, now I'll increase the difficulty a bit. What would happen if multiple Promises were executed?
2.3 Promise Interdependence
We are writing a promise:
Const setDelaySecond=(seconds)=>{
Return new Promise (resolve, reject)=>{
If (typeof seconds!='number '| | seconds>10) reject (new Error (' parameter must be of type number and less than or equal to 10 ');
SetTimeout()=>{
Console. log (` First, the setDelaySeconds function output was delayed by ${seconds} seconds, resulting in a total delay of ${seconds+2} seconds')
Resolve (setDelay (2000))//This depends on the previous promise
}, seconds * 1000)
})
}
What happens when the next dependent resolve returns another Promise? Let's execute:

setDelaySecond(3).then((result)=>{
console.log(result)
}).catch((err)=>{
console.log(err);
})
Tips: This article is the most complete introduction to the asynchronous promise and Async/Await blogs, which are original and self transforming. Welcome to communicate with the front-end experts and point out any issues
1、 Why is there Async/Await?
We all know that Promise's solution already exists, why should ES7 propose a new Async/Await standard?
The answer is actually obvious: Although Promise breaks out of the strange circle of asynchronous nesting and is expressed more clearly in chain form, we also found that when there are a large number of asynchronous requests and the process is complex, we will find them filled with screens, which looks very difficult. The emergence of Async/Await in ES7 is to solve this complex situation.
Firstly, we must understand Promise.
2、 Promise Introduction
2.1 Promise Example
What is Promise, and many people should know the basic concepts? Take a look directly at the following code (the examples in the entire article are based on the setDelaySecond and setDelay functions, please remember):
Const setDelay=(millisecond)=>{
Return new Promise (resolve, reject)=>{
If (typeof millisecond!='number ') reject (new Error ('parameter must be of type number');
SetTimeout()=>{
Resolve (output after I delayed ${millisecond} milliseconds)
}Millisecond
})
}
We encapsulate a Promise in a function and return a Promise, which is more standardized.
You can see that the defined Promise has two parameters, resolve and reject.
Resolve: Transforms asynchronous execution from pending (request) to resolving (successful return), which is a function execution return.
Reject: As the name suggests, 'reject' means changing from a request to a 'failure'. It is a function that can execute and return a result, but we recommend returning an error, 'new Error()'.
For the above example, you can reject ('return a string ') and return it as you please, but we still recommend returning an Error object to make it clearer that it is' failed' and more standardized.
2.2 Promise then and catch
We obtain our return value through Promise's prototype method then:
SetDelay (3000)
. then (result)=>{
Console. log (result)//Output "I output after a delay of 2000 milliseconds"
})
Output the following value: 'I output after a delay of 2000 milliseconds'.
What if something goes wrong? Then use catch to capture:
SetDelay ('I am a string ')
. then (result)=>{
Console. log (result)//No entry
})
. catch ((err)=>{
Console. log (err)//Output error: "Parameter must be of type number"
})
Isn't it very simple? Okay, now I'll increase the difficulty a bit. What would happen if multiple Promises were executed?
2.3 Promise Interdependence
We are writing a promise:
Const setDelaySecond=(seconds)=>{
Return new Promise (resolve, reject)=>{
If (typeof seconds!='number '| | seconds>10) reject (new Error (' parameter must be of type number and less than or equal to 10 ');
SetTimeout()=>{
Console. log (` First, the setDelaySeconds function output was delayed by ${seconds} seconds, resulting in a total delay of ${seconds+2} seconds')
Resolve (setDelay (2000))//This depends on the previous promise
}, seconds * 1000)
})
}
What happens when the next dependent resolve returns another Promise? Let's execute:

setDelay('2000')
.then((result)=>{
console.log('第一步完成了');
console.log(result)
return setDelaySecond(3)
})
.catch((err)=>{ // 这里移到第一个链式去,发现上面的不执行了,下面的继续执行
console.log(err);
})
.then((result)=>{
console.log('第二步完成了');
console.log(result);
})
惊喜的发现,链式继续走下去了!!输出如下(undefined是因为上一个then没有返回一个Promise):

clipboard.png

重点来了!敲黑板!!链式中的catch并不是终点!!catch完如果还有then还会继续往下走!不信的话可以把第一个catch在最后面的那个例子后面再加几个then,你会发现并不会跳出链式执行。

如果顺序执行setDelay,setDelay1,setDelaySecond,按照上述的逻辑,流程图可以概括如下:

clipboard.png

catch只是捕获错误的一个链式表达,并不是break!

所以,catch放的位置也很有讲究,一般放在一些重要的、必须catch的程序的最后。**这些重要的程序中间一旦出现错误,会马上跳过其他后续程序的操作直接执行到最近的catch代码块,但不影响catch后续的操作!!!!

到这就不得不体一个ES2018标准新引入的Promise的finally,表示在catch后必须肯定会默认执行的的操作。这里不多展开,细节可以参考:Promise的finally

2.5 Promise链式中间想返回自定义的值
其实很简单,用Promise的原型方法resolve即可:

setDelay(2000).then((result)=>{
console.log('第一步完成了');
console.log(result);
let message = '这是我自己想处理的值';
return Promise.resolve(message) // 这里返回我想在下一阶段处理的值
})
.then((result)=>{
console.log('第二步完成了');
console.log(result); // 这里拿到上一阶段的返回值
//return Promise.resolve('这里可以继续返回')
})
.catch((err)=>{
console.log(err);
})
2.7 如何跳出或停止Promise链式
不同于一般的function的break的方式,如果你是这样的操作:func().then().then().then().catch()的方式,你想在第一个then就跳出链式,后面的不想执行了,不同于一般的break;return null;return false等操作,可以说,如何停止Promise链,是一大难点,是整个Promise最复杂的地方。

1.用链式的思维想,我们拒绝掉某一链,那么不就是相当于直接跳到了catch模块吗?

我们是不是可以直接“拒绝“掉达到停止的目的?

setDelay(2000)
.then((result)=>{
console.log(result)
console.log('我进行到第一步的');
return setDelaySecond(1)
})
.then((result)=>{
console.log('我进行到第二步的');
console.log(result);
console.log('我主动跳出循环了');
return Promise.reject('跳出循环的信息') // 这里返回一个reject,主动跳出循环了
})
.then((result)=>{
console.log('我不执行');
})
.catch((mes)=>{
console.dir(mes)
console.log('我跳出了');
})
但是很容易看到缺点:有时候你并不确定是因为错误跳出的,还是主动跳出的,所以我们可以加一个标志位:

return Promise.reject({

isNotErrorExpection: true // 返回的地方加一个标志位,判断是否是错误类型,如果不是,那么说明可以是主动跳出循环的

})
或者根据上述的代码判断catch的地方输出的类型是不是属于错误对象的,是的话说明是错误,不是的话说明是主动跳出的,你可以自己选择(这就是为什么要统一错误reject的时候输出new Error('错误信息')的原因,规范!)

当然你也可以直接抛出一个错误跳出:

throw new Error('错误信息') // 直接跳出,那就不能用判断是否为错误对象的方法进行判断了
2.那有时候我们有这个需求:catch是放在中间(不是末尾),而同时我们又不想执行catch后面的代码,也就是链式的绝对中止,应该怎么办?

我们看这段代码:

setDelay(2000)
.then((result)=>{
console.log(result)
console.log('我进行到第一步的');
return setDelaySecond(1)
})
.then((result)=>{
console.log('我进行到第二步的');
console.log(result);
console.log('我主动跳出循环了');
return Promise.reject('跳出循环的信息') // 这里直接调用Promise原型方法返回一个reject,主动跳出循环了
})
.then((result)=>{
console.log('我不执行');
})
.catch((mes)=>{
console.dir(mes)
console.log('我跳出了');
})
.then((res)=>{

console.log('我不想执行,但是却执行了'); // 问题在这,上述的终止方法治标不治本。

})
这时候最后一步then还是执行了,整条链都其实没有本质上的跳出,那应该怎么办呢?

敲黑板!!重点来了!我们看Promise/A+规范可以知道:

A promise must be in one of three states: pending, fulfilled, or rejected.
Promise其实是有三种状态的:pending,resolve,rejected,那么我们一直在讨论resolve和rejected这2个状态,是不是忽视了pending这个状态呢?pending状态顾名思义就是请求中的状态,成功请求就是resolve,失败就是reject,其实他就是个中间过渡状态。

而我们上面讨论过了,then的下一层级其实得到的是上一层级返回的Promise对象,也就是说原Promise对象与新对象状态保持一致。那么重点来了,如果你想在这一层级进行终止,是不是直接让它永远都pending下去,那么后续的操作不就没了吗?是不是就达到这个目的了??觉得有疑问的可以参考Promise/A+规范。

我们直接看代码:

setDelay(2000)
.then((result)=>{
console.log(result)
console.log('我进行到第一步的');
return setDelaySecond(1)
})
.then((result)=>{
console.log(result);
console.log('我主动跳出循环了');
// return Promise.reject('跳出循环的信息')
// 重点在这
return new Promise(()=>{console.log('后续的不会执行')}) // 这里返回的一个新的Promise,没有resolve和reject,那么会一直处于pending状态,因为没返回啊,那么这种状态就一直保持着,中断了这个Promise
})
.then((result)=>{
console.log('我不执行');
})
.catch((mes)=>{
console.dir(mes)
console.log('我跳出了');
})
.then((res)=>{
console.log('我也不会执行')
})
这样就解决了上述,错误跳出而导致无法完全终止Promise链的问题。

但是!随之而来也有一个问题,那就是可能会导致潜在的内存泄漏,因为我们知道这个一直处于pending状态下的Promise会一直处于被挂起的状态,而我们具体不知道浏览器的机制细节也不清楚,一般的网页没有关系,但大量的复杂的这种pending状态势必会导致内存泄漏,具体的没有测试过,后续可能会跟进测试(nodeJS或webapp里面不推荐这样),而我通过查询也难以找到答案,这篇文章可以推荐看一下:从如何停掉 Promise 链说起。可能对你有帮助在此种情况下如何做。

当然一般情况下是不会存在泄漏,只是有这种风险,无法取消Promise一直是它的痛点。而上述两个奇妙的取消方法要具体情形具体使用。

2.8 Promise.all
其实这几个方法就简单了,就是一个简写串联所有你需要的Promise执行,具体可以参照阮一峰的ES6Promise.all教程。

我这上一个代码例子

Promise.all([setDelay(1000), setDelaySecond(1)]).then(result=>{
console.log(result);
})
.catch(err=>{
console.log(err);
})
// 输出["我延迟了1000毫秒后输出的", "我延迟了1秒后输出的,注意单位是秒"]
输出的是一个数组,相当于把all方法里面的Promise并行执行,注意是并行。
相当于两个Promise同时开始执行,同时返回值,并不是先执行第一个再执行第二个,如果你想串行执行,请参考我后面写的循环Promise循环串行(第4.2小节)。

然后把resolve的值保存在数组中输出。类似的还有Promise.race这里就不多赘述了。

三、Async/await介绍
3.1 基于Promise的Async/await
什么是async/await呢?可以总结为一句话:async/await是一对好基友,缺一不可,他们的出生是为Promise服务的。可以说async/await是Promise的爸爸,进化版。为什么这么说呢?且听我细细道来。

为什么要有async/await存在呢?

前文已经说过了,为了解决大量复杂不易读的Promise异步的问题,才出现的改良版。

这两个基友必须同时出现,缺一不可,那么先说一下Async:

async function process() {
}
上面可以看出,async必须声明的是一个function,不要去声明别的,要是那样await就不理你了(报错)。

这样声明也是错的!

const async demo = function () {} // 错误
必须紧跟着function。接下来说一下它的兄弟await。

上面说到必须是个函数(function),那么await就必须是在这个async声明的函数内部使用,否则就会报错。

就算你这样写,也是错的。

let data = 'data'
demo = async function () {

const test = function () {
    await data
}

}
必须是直系(作用域链不能隔代),这样会报错:Uncaught SyntaxError: await is only valid in async function。

讲完了基本规范,我们接下去说一下他们的本质。

3.2 async的本质
敲黑板!!!很重要!async声明的函数的返回本质上是一个Promise。

什么意思呢?就是说你只要声明了这个函数是async,那么内部不管你怎么处理,它的返回肯定是个Promise。

看下列例子:

(async function () {

return '我是Promise'

})()
// 返回是Promise
//Promise {: "我是Promise"}
你会发现返回是这个:Promise {: "我是Promise"}。

自动解析成Promise.resolve('我是Promise');

等同于:

(async function () {

return Promise.resolve('我是Promise');

})()
所以你想像一般function的返回那样,拿到返回值,原来的思维要改改了!你可以这样拿到返回值:

const demo = async function () {

return Promise.resolve('我是Promise');
// 等同于 return '我是Promise'
// 等同于 return new Promise((resolve,reject)=>{ resolve('我是Promise') })

}
demo.then(result=>{

console.log(result) // 这里拿到返回值

})
上述三种写法都行,要看注释细节都写在里面了!!像对待Promise一样去对待async的返回值!!!

好的接下去我们看await的干嘛用的.

3.3 await的本质与例子
await的本质是可以提供等同于”同步效果“的等待异步返回能力的语法糖。

这一句咋一看很别扭,好的不急,我们从例子开始看:

const demo = async ()=>{

let result = await new Promise((resolve, reject) => {
  setTimeout(()=>{
    resolve('我延迟了一秒')
  }, 1000)
});
console.log('我由于上面的程序还没执行完,先不执行“等待一会”');

}
// demo的返回当做Promise
demo().then(result=>{
console.log('输出',result);
})
await顾名思义就是等待一会,只要await声明的函数还没有返回,那么下面的程序是不会去执行的!!!。这就是字面意义的等待一会(等待返回再去执行)。

那么你到这测试一下,你会发现输出是这个:输出 undefined。这是为什么呢?这也是我想强调的一个地方!!!

你在demo函数里面都没声明返回,哪来的then?所以正确写法是这样:

const demo = async ()=>{

let result = await new Promise((resolve, reject) => {
  setTimeout(()=>{
    resolve('我延迟了一秒')
  }, 1000)
});
console.log('我由于上面的程序还没执行完,先不执行“等待一会”');
return result;

}
// demo的返回当做Promise
demo().then(result=>{
console.log('输出',result); // 输出 我延迟了一秒
})
我推荐的写法是带上then,规范一点,当然你没有返回也是没问题的,demo会照常执行。下面这种写法是不带返回值的写法:

const demo = async ()=>{

let result = await new Promise((resolve, reject) => {
  setTimeout(()=>{
    resolve('我延迟了一秒')
  }, 1000)
});
console.log('我由于上面的程序还没执行完,先不执行“等待一会”');

}
demo();
所以可以发现,只要你用await声明的异步返回,是必须“等待”到有返回值的时候,代码才继续执行下去。

那事实是这样吗?你可以跑一下这段代码:

const demo = async ()=>{

let result = await setTimeout(()=>{
  console.log('我延迟了一秒');
}, 1000)
console.log('我由于上面的程序还没执行完,先不执行“等待一会”');
return result

}
demo().then(result=>{
console.log('输出',result);
})
你会发现,输出是这样的:

我由于上面的程序还没执行完,先不执行“等待一会”
输出 1
我延迟了一秒
奇怪,并没有await啊?setTimeout是异步啊,问题在哪?问题就在于setTimeout这是个异步,但是不是Promise!起不到“等待一会”的作用。

所以更准确的说法应该是用await声明的Promise异步返回,必须“等待”到有返回值的时候,代码才继续执行下去。

请记住await是在等待一个Promise的异步返回
当然这种等待的效果只存在于“异步”的情况,await可以用于声明一般情况下的传值吗?

事实是当然可以:

const demo = async ()=>{

let message = '我是声明值'
let result = await message;
console.log(result); 
console.log('我由于上面的程序还没执行完,先不执行“等待一会”');
return result

}
demo().then(result=>{
console.log('输出',result);
})
Output:
I declare the value
I won't execute 'wait for a moment' because the above program has not been completed yet
Output I am a declared value
Just one thing to note here: then execution is always final.
3.4 Actual combat of async/await advantages
Now let's take a look at the actual combat:
Const setDelay=(millisecond)=>{
Return new Promise (resolve, reject)=>{
If (typeof millisecond!='number ') reject (new Error ('parameter must be of type number');
SetTimeout()=>{
Resolve (output after I delayed ${millisecond} milliseconds)
}Millisecond
})
}
Const setDelaySecond=(seconds)=>{
Return new Promise (resolve, reject)=>{
If (typeof seconds!='number '| | seconds>10) reject (new Error (' parameter must be of type number and less than or equal to 10 ');
SetTimeout()=>{
Resolve (I delayed the output by ${seconds} seconds, please note that the unit is seconds)
}, seconds * 1000)
})
}
For example, the above two delay functions (written above), for example, I want to delay for 1 second first, then delay for 2 seconds, then delay for 1 second, and finally output "complete". This process, if written using then's method, would be like this (nested hell writing without sending when going out and turning right):
SetDelay (1000)
. then (result=>{
Console. log (result);
Return setDelaySecond (2)
})
. then (result=>{
Console. log (result);
Return setDelay (1000)
})
. then (result=>{
Console. log (result);
Console. log ('Done ')
})
. catch (err=>{
Console. log (err);
})
At first glance, isn't it quite complicated? If there is more logic, it may be more tiring to watch. Now let's try async/await
(async()=>{
Const result=await setDelay (1000);
Console. log (result);
Console. log (await setDelaySecond (2));
Console. log (await setDelay (1000));
Console. log ('Completed ');
}) ()
Look! Is there no redundant long chain code, and the semantics are very clear and comfortable? So, when you see this, you must have noticed that the catch above is not implemented in async? Next, let's analyze how async/await handles errors?
3.5 async/await error handling
Because the async function returns a Promise, we can catch errors outside.
Const demo=async()=>{
Const result=await setDelay (1000);
Console. log (result);
Console. log (await setDelaySecond (2));
Console. log (await setDelay (1000));
Console. log ('Completed ');
}
Demo (). catch (err=>{
Console. log (err);
})
Capture errors in the catch of the async function and treat them as a Pormise. If you don't want to use this method, you can use try Catch statement:
(async()=>{
Try{
Const result=await setDelay (1000);
Console. log (result);
Console. log (await setDelaySecond (2));
Console. log (await setDelay (1000));
Console. log ('Completed ');
}Catch (e){
Console. log (e)// Error caught here
}
}) ()
Of course, you don't need to catch outside at this time.
Usually, our try The number of catches will not be too many, but the most. If there are too many, it indicates that your code definitely needs to be refactored, and it must not have been written very well. Another point is try Catch is usually only used when needed, and sometimes it is not necessary to write errors in the catch.
Someone might ask, I try Catch seems to only wrap around code blocks. If I need to split and process them separately, and don't want to crash the entire process due to one error, then should I write a bunch of tries Catch? I'm just uncomfortable, I just don't want to write try What about catch? Here is a great solution for reference only:
We know that the word 'await' must be followed by a promise. Can we write it like this?
(async()=>{
Const result=await setDelay (1000). catch (err=>{
Console. log (err)
});
Console. log (result);
Const result1=await setDelaySecond (12). catch (err=>{
Console. log (err)
})
Console. log (result1);
Console. log (await setDelay (1000));
Console. log ('Completed ');
}) ()
Output in this way:
I delayed the output by 1000 milliseconds
Error: The parameter must be of type number and be less than or equal to 10
At Promise (test4. html: 19)
At new Promise ()
At setDelaySecond (test4. html: 18)
At test4. html: 56
Undefined
I delayed the output by 1000 milliseconds
Completed
Is it great that even if there are errors, they won't affect subsequent operations? Of course not, do you think this code is also very ugly? It's messy and awkward to write, and it follows catch. So we can improve by encapsulating the code function that extracts errors:
//To function
Function to (promise){
Return promise. then (data=>{
Return [null, data];
})
. catch (err=>[err])// Return writing for es6
}
The return is an array, with the first being an error and the second being an asynchronous result, using the following:
(async()=>{
//The writing method of ES6 returns an array (if you find it unfamiliar with the writing method of ES5). The first is the error message, and the second is the asynchronous return data of then. It should be noted that duplicate variable declarations may cause problems (for example, if using let or const, please change the variable name).
[err, result]=await to (setDelay (1000))
//If err exists, there is an error, and if you don't want to continue executing, throw an error
If (err) throw new Error ('An error occurred and I no longer want to execute it ');
Console. log (result);
[err, result1]=await to (setDelaySecond (12))
//If you still want to execute, don't throw an error
If (err) console. log ('An error occurred and I want to continue executing ', err);
Console. log (result1);
Console. log (await setDelay (1000));
Console. log ('Completed ');
}) ()
3.6 Asynchronous/await interrupt (termination program)
Firstly, we need to clarify that Promise itself cannot be aborted. Promise itself is just a state machine that stores three states (pending, resolved, rejected). Once a request is made, it must be closed loop and cannot be cancelled. Previously, being in the pending state was only a state of suspending a request, not canceling. This situation is generally not allowed to occur, but is only used to temporarily suspend the chain process.
The essence of interruption (termination) is just hanging in the chain, not essentially canceling the Promise request, which cannot be done, and Promise does not have a cancel state.
Unlike the chain writing method of Promise, it is easy to interrupt a program in async/await because the semantics are very obvious. In fact, just like the general function writing method, when you want to interrupt, you can simply return a value, null, empty, or false. See example:
Let count=6;
Const demo=async()=>{
Const result=await setDelay (1000);
Console. log (result);
Const result1=await setDelaySecond (count);
Console. log (result1);
If (count>5){
Return 'I have exited and will not proceed with the following';
//Return;
//Return false// These writing methods are all acceptable
//Return null;
}
Console. log (await setDelay (1000));
Console. log ('Completed ');
};
Demo (). then (result=>{
Console. log (result);
})
. catch (err=>{
Console. log (err);
})
The essence is to directly return a Promise, which is equivalent to returning Promise. resolve ('I have exited and will not proceed below '). Of course, you can also return a' reject ': return Promise. reject (new Error ('reject'), which will enter the error message.
The async function essentially returns a Promise!
4、 Points to pay attention to during asynchronous operations in actual combat
We often use the two writing methods mentioned above, and may also mix them. Sometimes, we may encounter some situations. Here is an example to illustrate:
4.1 Promise to Obtain Data (Serial) Then Writing Notes
It goes without saying that parallelism is very simple, simply issuing requests through a loop or using Promise. all. What should we do if we need to serialize a request?
We need to implement a program that delays the output values by 1 second in sequence for a total of 5 seconds. Firstly, the Promise loop is relatively cumbersome:
We often make mistakes! Just not valuing the impact of function names and function execution on the program
Let's not talk about loops, let's give an example of an error. Now we have a delay function
Const setDelay=(millisecond)=>{
Return new Promise (resolve, reject)=>{
If (typeof millisecond!='number ') reject (new Error ('parameter must be of type number');
SetTimeout()=>{
Resolve (output after I delayed ${millisecond} milliseconds)
}Millisecond
})
}
We want to achieve the goal of "looping through the serial execution of the Promise function with a delay of one second", and the expected result should be: outputting the output after I delay for 1000 milliseconds every other second, after a total of 3 cycles
syncPromise(arr).then(result=>{
console.log(result);
console.log('ok');
})
//Or add methods to the Promise class
Promise. syncAll=function syncAll(){
Return syncPromise
}//You can use it directly in the future
Promise. syncAll (arr). then (result=>{
Console. log (result);
Console. log ('Completed ');
})
The Great God also summarized a writing method for reduce, which is actually a process of iterating through arrays:

const p = arr.reduce((total, current)=>{

return total.then((result)=>{
    console.log(result);
    return current()
})

}Promise. resolve ('Program Start ')
p. Then (result)=>{
Console. log ('Finished ', result);
})
Both are feasible in the circular field of Promise.
4.3 async/await loop for obtaining data (serial)
Now let's introduce the practical application of Niubi's async/await. Do you have to look at the above code? Indeed, I also think it's troublesome. So, can there be any improvement if using async/await? This is the meaning of its appearance:
Simulate the loop of the above code:
(async ()=>{

arr = [timeout(2000), timeout(1000), timeout(1000)]
for (var i=0; i < arr.length; i++) {
    result = await arr[i]();
    console.log(result);
}

})()
... Is that all? Yes... That's it, isn't it particularly convenient!!!! Semanticization is also very obvious!! In order to maintain consistency with the above style, no error handling has been added here, so remember to add your try during actual combat Catch statement to catch errors.
4、 Postscript
I have always wanted to summarize Promise and async/await, but I may not have summarized enough in many aspects. I have tried my best to expand the space, and there may be new knowledge points and summary points that will be updated in the future (to be continued). However, the beginner's level is basically sufficient.
We often say that the emergence of async/await eliminated Promise, which can be said to be a big mistake. On the contrary, it is precisely because of Promise that there is an improved version of async/await. From the above analysis, it can be seen that the two are complementary and indispensable.
To learn async/await well, one must first be proficient in Promise. The two are inseparable, and there are different opinions and suggestions for improvement. Welcome to guide!
Front end white, everyone communicates with each other, peace!

Vue 3.x 的Pre-Alpha 版本。后面应该还会有 Alpha、Beta 等版本,预计至少要等到 2020 年第一季度才有可能发布 3.0 正式版;
所以应该趁还没出来加紧打好 Vue2.x 的基础;
Vue基本用法很容易上手,但是有很多优化的写法你就不一定知道了,本文从列举了 36 个 vue 开发技巧;
后续 Vue 3.x 出来后持续更新.

1.require.context()
1.场景:如页面需要导入多个组件,原始写法:

import titleCom from '@/components/home/titleCom'
import bannerCom from '@/components/home/bannerCom'
import cellCom from '@/components/home/cellCom'
components:{titleCom,bannerCom,cellCom}
2.这样就写了大量重复的代码,利用 require.context 可以写成

const path = require('path')
const files = require.context('@/components/home', false, /.vue$/)
const modules = {}
files.keys().forEach(key => {
const name = path.basename(key, '.vue')
modules[name] = files(key).default || files(key)
})
components:modules
这样不管页面引入多少组件,都可以使用这个方法

3.API 方法

实际上是 webpack 的方法,vue 工程一般基于 webpack,所以可以使用
require.context(directory,useSubdirectories,regExp)
接收三个参数:
directory:说明需要检索的目录
useSubdirectories:是否检索子目录
regExp: 匹配文件的正则表达式,一般是文件名
2.watch
2.1 常用用法
1.场景:表格初始进来需要调查询接口 getList(),然后input 改变会重新查询

created(){
this.getList()
},
watch: {
inpVal(){

this.getList()

}
}
2.2 立即执行
2.可以直接利用 watch 的immediate和handler属性简写

watch: {
inpVal:{

handler: 'getList',
  immediate: true

}
}
2.3 深度监听
3.watch 的 deep 属性,深度监听,也就是监听复杂数据类型

watch:{
inpValObj:{

handler(newVal,oldVal){
  console.log(newVal)
  console.log(oldVal)
},
deep:true

}
}
此时发现oldVal和 newVal 值一样;
因为它们索引同一个对象/数组,Vue 不会保留修改之前值的副本;
所以深度监听虽然可以监听到对象的变化,但是无法监听到具体对象里面那个属性的变化

  1. 14种组件通讯
    3.1 props
    这个应该非常属性,就是父传子的属性;
    props 值可以是一个数组或对象;

// 数组:不建议使用
props:[]

// 对象
props:{
inpVal:{
type:Number, //传入值限定类型
// type 值可为String,Number,Boolean,Array,Object,Date,Function,Symbol
// type 还可以是一个自定义的构造函数,并且通过 instanceof 来进行检查确认
required: true, //是否必传
default:200, //默认值,对象或数组默认值必须从一个工厂函数获取如 default:()=>[]
validator:(value) {

// 这个值必须匹配下列字符串中的一个
return ['success', 'warning', 'danger'].indexOf(value) !== -1

}
}
}
3.2 $emit
这个也应该非常常见,触发子组件触发父组件给自己绑定的事件,其实就是子传父的方法

// 父组件

// 子组件
this.$emit('title',[{title:'这是title'}])
3.3 vuex
1.这个也是很常用的,vuex 是一个状态管理器
2.是一个独立的插件,适合数据共享多的项目里面,因为如果只是简单的通讯,使用起来会比较重
3.API

state:定义存贮数据的仓库 ,可通过this.$store.state 或mapState访问
getter:获取 store 值,可认为是 store 的计算属性,可通过this.$store.getter 或

   mapGetters访问

mutation:同步改变 store 值,为什么会设计成同步,因为mutation是直接改变 store 值,

     vue 对操作进行了记录,如果是异步无法追踪改变.可通过mapMutations调用

action:异步调用函数执行mutation,进而改变 store 值,可通过 this.$dispatch或mapActions

   访问

modules:模块,如果状态过多,可以拆分成模块,最后在入口通过...解构引入
3.4 $attrs和$listeners
2.4.0 新增
这两个是不常用属性,但是高级用法很常见;
1.$attrs
场景:如果父传子有很多值,那么在子组件需要定义多个 props
解决:$attrs获取子传父中未在 props 定义的值

// 父组件

// 子组件
mounted() {
console.log(this.$attrs) //{title: "这是标题", width: "80", height: "80", imgUrl: "imgUrl"}
},
相对应的如果子组件定义了 props,打印的值就是剔除定义的属性

props: {
width: {

type: String,
default: ''

}
},
mounted() {
console.log(this.$attrs) //{title: "这是标题", height: "80", imgUrl: "imgUrl"}
},
2.$listeners
场景:子组件需要调用父组件的方法
解决:父组件的方法可以通过 v-on="$listeners" 传入内部组件——在创建更高层次的组件时非常有用

// 父组件

// 子组件
mounted() {
console.log(this.$listeners) //即可拿到 change 事件
}
如果是孙组件要访问父组件的属性和调用方法,直接一级一级传下去就可以

3.inheritAttrs

// 父组件

// 子组件
mounted() {
console.log(this.$attrs) //{title: "这是标题", width: "80", height: "80", imgUrl: "imgUrl"}
},

inheritAttrs默认值为true,true的意思是将父组件中除了props外的属性添加到子组件的根节点上(说明,即使设置为true,子组件仍然可以通过$attr获取到props意外的属性)
将inheritAttrs:false后,属性就不会显示在根节点上了
3.5 provide和inject
2.2.0 新增
描述:
provide 和 inject 主要为高阶插件/组件库提供用例。并不推荐直接用于应用程序代码中;
并且这对选项需要一起使用;
以允许一个祖先组件向其所有子孙后代注入一个依赖,不论组件层次有多深,并在起上下游关系成立的时间里始终生效。

//父组件:
provide: { //provide 是一个对象,提供一个属性或方法
foo: '这是 foo',
fooMethod:()=>{

console.log('父组件 fooMethod 被调用')

}
},

// 子或者孙子组件
inject: ['foo','fooMethod'], //数组或者对象,注入到子组件
mounted() {
this.fooMethod()
console.log(this.foo)
}
//在父组件下面所有的子组件都可以利用inject
provide 和 inject 绑定并不是可响应的。这是官方刻意为之的。
然而,如果你传入了一个可监听的对象,那么其对象的属性还是可响应的,对象是因为是引用类型

//父组件:
provide: {
foo: '这是 foo'
},
mounted(){
this.foo='这是新的 foo'
}

// 子或者孙子组件
inject: ['foo'],
mounted() {
console.log(this.foo) //子组件打印的还是'这是 foo'
}
3.6 $parent和$children
$parent:父实例
$children:子实例

//父组件
mounted(){
console.log(this.$children)
//可以拿到 一级子组件的属性和方法
//所以就可以直接改变 data,或者调用 methods 方法
}

//子组件
mounted(){
console.log(this.$parent) //可以拿到 parent 的属性和方法
}
$children和$parent 并不保证顺序,也不是响应式的
只能拿到一级父组件和子组件

3.7 $refs
// 父组件

mounted(){
console.log(this.$refs.home) //即可拿到子组件的实例,就可以直接操作 data 和 methods
}
3.8 $root
// 父组件
mounted(){
console.log(this.$root) //获取根实例,最后所有组件都是挂载到根实例上
console.log(this.$root.$children[0]) //获取根实例的一级子组件
console.log(this.$root.$children[0].$children[0]) //获取根实例的二级子组件
}
3.9 .sync
在 vue@1.x 的时候曾作为双向绑定功能存在,即子组件可以修改父组件中的值;
在 vue@2.0 的由于违背单项数据流的设计被干掉了;
在 vue@2.3.0+ 以上版本又重新引入了这个 .sync 修饰符;

// 父组件

//编译时会被扩展为

// 子组件
// 所以子组件可以通过$emit 触发 update 方法改变
mounted(){
this.$emit("update:title", '这是新的title')
}
3.10 v-slot
2.6.0 新增
1.slot,slot-cope,scope 在 2.6.0 中都被废弃,但未被移除
2.作用就是将父组件的 template 传入子组件
3.插槽分类:
A.匿名插槽(也叫默认插槽): 没有命名,有且只有一个;

// 父组件

<template v-slot:default>
   任意内容
   <p>我是匿名插槽 </p>
</template>

// 子组件
我是默认值
//v-slot:default写上感觉和具名写法比较统一,容易理解,也可以不用写
B.具名插槽: 相对匿名插槽组件slot标签带name命名的;

// 父组件

<template v-slot:todo>
   任意内容
   <p>我是匿名插槽 </p>
</template>

//子组件
我是默认值
C.作用域插槽: 子组件内数据可以被父页面拿到(解决了数据只能从父页面传递给子组件)

// 父组件



//slotProps 可以随意命名
//slotProps 接取的是子组件标签slot上属性数据的集合所有v-bind:user="user"

// 子组件

{{ user.lastName }}


data() {

return {
  user:{
    lastName:"Zhang",
    firstName:"yue"
  },
  test:[1,2,3,4]
}

},
// {{ user.lastName }}是默认数据 v-slot:todo 当父页面没有(="slotProps")
3.11 EventBus
1.就是声明一个全局Vue实例变量 EventBus , 把所有的通信数据,事件监听都存储到这个变量上;
2.类似于 Vuex。但这种方式只适用于极小的项目
3.原理就是利用$on和$emit 并实例化一个全局 vue 实现数据共享

// 在 main.js
Vue.prototype.$eventBus=new Vue()

// 传值组件
this.$eventBus.$emit('eventTarget','这是eventTarget传过来的值')

// 接收组件
this.$eventBus.$on("eventTarget",v=>{
console.log('eventTarget',v);//这是eventTarget传过来的值
})
4.可以实现平级,嵌套组件传值,但是对应的事件名eventTarget必须是全局唯一的

3.12 broadcast和dispatch
vue 1.x 有这两个方法,事件广播和派发,但是 vue 2.x 删除了
下面是对两个方法进行的封装

function broadcast(componentName, eventName, params) {
this.$children.forEach(child => {

var name = child.$options.componentName;

if (name === componentName) {
  child.$emit.apply(child, [eventName].concat(params));
} else {
  broadcast.apply(child, [componentName, eventName].concat(params));
}

});
}
export default {
methods: {

dispatch(componentName, eventName, params) {
  var parent = this.$parent;
  var name = parent.$options.componentName;
  while (parent && (!name || name !== componentName)) {
    parent = parent.$parent;

    if (parent) {
      name = parent.$options.componentName;
    }
  }
  if (parent) {
    parent.$emit.apply(parent, [eventName].concat(params));
  }
},
broadcast(componentName, eventName, params) {
  broadcast.call(this, componentName, eventName, params);
}

}
}
3.13 路由传参
1.方案一

// 路由定义
{
path: '/describe/:id',
name: 'Describe',
component: Describe
}
// 页面传参
this.$router.push({
path: /describe/${id},
})
// 页面获取
this.$route.params.id
2.方案二

// 路由定义
{
path: '/describe',
name: 'Describe',
omponent: Describe
}
// 页面传参
this.$router.push({
name: 'Describe',
params: {

id: id

}
})
// 页面获取
this.$route.params.id
3.方案三

// 路由定义
{
path: '/describe',
name: 'Describe',
component: Describe
}
// 页面传参
this.$router.push({
path: '/describe',

query: {
  id: id

`}
)
// 页面获取
this.$route.query.id
4.三种方案对比
方案二参数不会拼接在路由后面,页面刷新参数会丢失
方案一和三参数拼接在后面,丑,而且暴露了信息

3.14 Vue.observable
2.6.0 新增
用法:让一个对象可响应。Vue 内部会用它来处理 data 函数返回的对象;

返回的对象可以直接用于渲染函数和计算属性内,并且会在发生改变时触发相应的更新;
也可以作为最小化的跨组件状态存储器,用于简单的场景。
通讯原理实质上是利用Vue.observable实现一个简易的 vuex

// 文件路径 - /store/store.js
import Vue from 'vue'

export const store = Vue.observable({ count: 0 })
export const mutations = {
setCount (count) {

store.count = count

}
}

//使用

4.render 函数
1.场景:有些代码在 template 里面写会重复很多,所以这个时候 render 函数就有作用啦

// 根据 props 生成标签
// 初级

// 优化版,利用 render 函数减小了代码重复率

2.render 和 template 的对比
前者适合复杂逻辑,后者适合逻辑简单;
后者属于声明是渲染,前者属于自定Render函数;
前者的性能较高,后者性能较低。

5.异步组件
场景:项目过大就会导致加载缓慢,所以异步组件实现按需加载就是必须要做的事啦
1.异步注册组件
3种方法

// 工厂函数执行 resolve 回调
Vue.component('async-webpack-example', function (resolve) {
// 这个特殊的 require 语法将会告诉 webpack
// 自动将你的构建代码切割成多个包, 这些包
// 会通过 Ajax 请求加载
require(['./my-async-component'], resolve)
})

// 工厂函数返回 Promise
Vue.component(
'async-webpack-example',
// 这个 import 函数会返回一个 Promise 对象。
() => import('./my-async-component')
)

// 工厂函数返回一个配置化组件对象
const AsyncComponent = () => ({
// 需要加载的组件 (应该是一个 Promise 对象)
component: import('./MyComponent.vue'),
// 异步组件加载时使用的组件
loading: LoadingComponent,
// 加载失败时使用的组件
error: ErrorComponent,
// 展示加载时组件的延时时间。默认值是 200 (毫秒)
delay: 200,
// 如果提供了超时时间且组件加载也超时了,
// 则使用加载失败时使用的组件。默认值是:Infinity
timeout: 3000
})
异步组件的渲染本质上其实就是执行2次或者2次以上的渲染, 先把当前组件渲染为注释节点, 当组件加载成功后, 通过 forceRender 执行重新渲染。或者是渲染为注释节点, 然后再渲染为loading节点, 在渲染为请求完成的组件

2.路由的按需加载

webpack< 2.4 时
{
path:'/',
name:'home',
components:resolve=>require(['@/components/home'],resolve)
}

webpack> 2.4 时
{
path:'/',
name:'home',
components:()=>import('@/components/home')
}

import()方法由es6提出,import()方法是动态加载,返回一个Promise对象,then方法的参数是加载到的模块。类似于Node.js的require方法,主要import()方法是异步加载的。
6.动态组件
场景:做一个 tab 切换时就会涉及到组件动态加载


但是这样每次组件都会重新加载,会消耗大量性能,所以 就起到了作用




这样切换效果没有动画效果,这个也不用着急,可以利用内置的






7.递归组件
场景:如果开发一个 tree 组件,里面层级是根据后台数据决定的,这个时候就需要用到动态组件

// 递归组件: 组件在它的模板内可以递归的调用自己,只要给组件设置name组件就可以了。
// 设置那么House在组件模板内就可以递归使用了,不过需要注意的是,
// 必须给一个条件来限制数量,否则会抛出错误: max stack size exceeded
// 组件递归用来开发一些具体有未知层级关系的独立组件。比如:
// 联级选择器和树形控件

递归组件必须设置name 和结束的阀值

8.函数式组件
定义:无状态,无法实例化,内部没有任何生命周期处理方法
规则:在 2.3.0 之前的版本中,如果一个函数式组件想要接收 prop,则 props 选项是必须的。

在 2.3.0 或以上的版本中,你可以省略 props 选项,所有组件上的特性都会被自动隐式解析为 prop
在 2.5.0 及以上版本中,如果你使用了单文件组件(就是普通的.vue 文件),可以直接在 template 上声明functional
组件需要的一切都是通过 context 参数传递
context 属性有:
1.props:提供所有 prop 的对象
2.children: VNode 子节点的数组
3.slots: 一个函数,返回了包含所有插槽的对象
4.scopedSlots: (2.6.0+) 一个暴露传入的作用域插槽的对象。也以函数形式暴露普通插槽。
5.data:传递给组件的整个数据对象,作为 createElement 的第二个参数传入组件
6.parent:对父组件的引用
7.listeners: (2.3.0+) 一个包含了所有父组件为当前组件注册的事件监听器的对象。这是 data.on 的一个别名。
8.injections: (2.3.0+) 如果使用了 inject 选项,则该对象包含了应当被注入的属性


9.components和 Vue.component
components:局部注册组件

export default{
components:{home}
}
Vue.component:全局注册组件

Vue.component('home',home)
10.Vue.extend
场景:vue 组件中有些需要将一些元素挂载到元素上,这个时候 extend 就起到作用了
是构造一个组件的语法器
写法:

// 创建构造器
var Profile = Vue.extend({
template: '

{{extendData}}
实例传入的数据为:{{propsExtend}}

',//template对应的标签最外层必须只有一个标签
data: function () {

return {
  extendData: '这是extend扩展的数据',
}

},
props:['propsExtend']
})

// 创建的构造器可以挂载到元素上,也可以通过 components 或 Vue.component()注册使用
// 挂载到一个元素上。可以通过propsData传参.
new Profile({propsData:{propsExtend:'我是实例传入的数据'}}).$mount('#app-extend')

// 通过 components 或 Vue.component()注册
Vue.component('Profile',Profile)
11.mixins
场景:有些组件有些重复的 js 逻辑,如校验手机验证码,解析时间等,mixins 就可以实现这种混入
mixins 值是一个数组

const mixin={

created(){
  this.dealTime()
},
methods:{
  dealTime(){
    console.log('这是mixin的dealTime里面的方法');
  }

}
}

export default{
mixins:[mixin]
}
12.extends
extends用法和mixins很相似,只不过接收的参数是简单的选项对象或构造函数,所以extends只能单次扩展一个组件

const extend={

created(){
  this.dealTime()
},
methods:{
  dealTime(){
    console.log('这是mixin的dealTime里面的方法');
  }

}
}

export default{
extends:extend
}
13.Vue.use()
场景:我们使用 element时会先 import,再 Vue.use()一下,实际上就是注册组件,触发 install 方法;
这个在组件调用会经常使用到;
会自动组织多次注册相同的插件.

14.install
场景:在 Vue.use()说到,执行该方法会触发 install
是开发vue的插件,这个方法的第一个参数是 Vue 构造器,第二个参数是一个可选的选项对象(可选)

var MyPlugin = {};
MyPlugin.install = function (Vue, options) {

// 2. 添加全局资源,第二个参数传一个值默认是update对应的值
Vue.directive('click', {
  bind(el, binding, vnode, oldVnode) {
    //做绑定的准备工作,添加时间监听
    console.log('指令my-directive的bind执行啦');
  },
  inserted: function(el){
  //获取绑定的元素
  console.log('指令my-directive的inserted执行啦');
  },
  update: function(){
  //根据获得的新值执行对应的更新
  //对于初始值也会调用一次
  console.log('指令my-directive的update执行啦');
  },
  componentUpdated: function(){
  console.log('指令my-directive的componentUpdated执行啦');
  },
  unbind: function(){
  //做清理操作
  //比如移除bind时绑定的事件监听器
  console.log('指令my-directive的unbind执行啦');
  }
})

// 3. 注入组件
Vue.mixin({
  created: function () {
    console.log('注入组件的created被调用啦');
    console.log('options的值为',options)
  }
})

// 4. 添加实例方法
Vue.prototype.$myMethod = function (methodOptions) {
  console.log('实例方法myMethod被调用啦');
}

}

//调用MyPlugin
Vue.use(MyPlugin,{someOption: true })

//3.挂载
new Vue({

el: '#app'

});
更多请戳 vue中extend,mixins,extends,components,install的几个操作

  1. Vue.nextTick
    2.1.0 新增
    场景:页面加载时需要让文本框获取焦点
    用法:在下次 DOM 更新循环结束之后执行延迟回调。在修改数据之后立即使用这个方法,获取更新后的 DOM

mounted(){ //因为 mounted 阶段 dom 并未渲染完毕,所以需要$nextTick
this.$nextTick(() => {

this.$refs.inputs.focus() //通过 $refs 获取dom 并绑定 focus 方法

})
}
16.Vue.directive
16.1 使用
场景:官方给我们提供了很多指令,但是我们如果想将文字变成指定的颜色定义成指令使用,这个时候就需要用到Vue.directive

// 全局定义
Vue.directive("change-color",function(el,binding,vnode){
el.style["color"]= binding.value;
})

// 使用

16.2 生命周期
1.bind 只调用一次,指令第一次绑定到元素时候调用,用这个钩子可以定义一个绑定时执行一次的初始化动作。
2.inserted:被绑定的元素插入父节点的时候调用(父节点存在即可调用,不必存在document中)
3.update: 被绑定与元素所在模板更新时调用,而且无论绑定值是否有变化,通过比较更新前后的绑定值,忽略不必要的模板更新
4.componentUpdate :被绑定的元素所在模板完成一次更新更新周期的时候调用
5.unbind: 只调用一次,指令月元素解绑的时候调用

  1. Vue.filter
    场景:时间戳转化成年月日这是一个公共方法,所以可以抽离成过滤器使用

// 使用
// 在双花括号中
{{ message | capitalize }}

// 在 v-bind

// 全局注册
Vue.filter('stampToYYMMDD', (value) =>{
// 处理逻辑
})

// 局部注册
filters: {
stampToYYMMDD: (value)=> {

// 处理逻辑

}
}

// 多个过滤器全局注册
// /src/common/filters.js
let dateServer = value => value.replace(/(\d{4})(\d{2})(\d{2})/g, '$1-$2-$3')
export { dateServer }
// /src/main.js
import * as custom from './common/filters/custom'
Object.keys(custom).forEach(key => Vue.filter(key, custom[key]))
18.Vue.compile
场景:在 render 函数中编译模板字符串。只在独立构建时有效

var res = Vue.compile('

{{ msg }}
')

new Vue({
data: {

msg: 'hello'

},
render: res.render,
staticRenderFns: res.staticRenderFns
})
19.Vue.version
场景:有些开发插件需要针对不同 vue 版本做兼容,所以就会用到 Vue.version
用法:Vue.version()可以获取 vue 版本

var version = Number(Vue.version.split('.')[0])

if (version === 2) {
// Vue v2.x.x
} else if (version === 1) {
// Vue v1.x.x
} else {
// Unsupported versions of Vue
}
20.Vue.set()
场景:当你利用索引直接设置一个数组项时或你修改数组的长度时,由于 Object.defineprototype()方法限制,数据不响应式更新

不过vue.3.x 将利用 proxy 这个问题将得到解决
解决方案:

// 利用 set
this.$set(arr,index,item)

// 利用数组 push(),splice()
21.Vue.config.keyCodes
场景:自定义按键修饰符别名

// 将键码为 113 定义为 f2
Vue.config.keyCodes.f2 = 113;

22.Vue.config.performance
场景:监听性能

Vue.config.performance = true
只适用于开发模式和支持 performance.mark API 的浏览器上

23.Vue.config.errorHandler
1.场景:指定组件的渲染和观察期间未捕获错误的处理函数
2.规则:

从 2.2.0 起,这个钩子也会捕获组件生命周期钩子里的错误。同样的,当这个钩子是 undefined 时,被捕获的错误会通过 console.error 输出而避免应用崩溃
从 2.4.0 起,这个钩子也会捕获 Vue 自定义事件处理函数内部的错误了
从 2.6.0 起,这个钩子也会捕获 v-on DOM 监听器内部抛出的错误。另外,如果任何被覆盖的钩子或处理函数返回一个 Promise 链 (例如 async 函数),则来自其 Promise 链的错误也会被处理
3.使用

Vue.config.errorHandler = function (err, vm, info) {
// handle error
// info 是 Vue 特定的错误信息,比如错误所在的生命周期钩子
// 只在 2.2.0+ 可用
}
24.Vue.config.warnHandler
2.4.0 新增
1.场景:为 Vue 的运行时警告赋予一个自定义处理函数,只会在开发者环境下生效
2.用法:

Vue.config.warnHandler = function (msg, vm, trace) {
// trace 是组件的继承关系追踪
}
25.v-pre
场景:vue 是响应式系统,但是有些静态的标签不需要多次编译,这样可以节省性能

{{ this will not be compiled }} 显示的是{{ this will not be compiled }}
{{msg}} 即使data里面定义了msg这里仍然是显示的{{msg}}
26.v-cloak
场景:在网速慢的情况下,在使用vue绑定数据的时候,渲染页面时会出现变量闪烁
用法:这个指令保持在元素上直到关联实例结束编译。和 CSS 规则如 [v-cloak] { display: none } 一起用时,这个指令可以隐藏未编译的 Mustache 标签直到实例准备完毕

// template 中

{{value.name}}

// css 中
[v-cloak] {

display: none;

}
这样就可以解决闪烁,但是会出现白屏,这样可以结合骨架屏使用

27.v-once
场景:有些 template 中的静态 dom 没有改变,这时就只需要渲染一次,可以降低性能开销

这时只需要加载一次的标签
v-once 和 v-pre 的区别:
v-once只渲染一次;v-pre不编译,原样输出

28.事件修饰符
.stop:阻止冒泡
.prevent:阻止默认行为
.self:仅绑定元素自身触发
.once: 2.1.4 新增,只触发一次
.passive: 2.3.0 新增,滚动事件的默认行为 (即滚动行为) 将会立即触发,不能和.prevent 一起使用
29.按键修饰符和按键码
场景:有的时候需要监听键盘的行为,如按下 enter 去查询接口等

// 对应键盘上的关键字
.enter
.tab
.delete (捕获“删除”和“退格”键)
.esc
.space
.up
.down
.left
.right
30.Vue-router
场景:Vue-router 是官方提供的路由插件

30.1 缓存和动画
1.路由是使用官方组件 vue-router,使用方法相信大家非常熟悉;
2.这里我就叙述下路由的缓存和动画;
3.可以用exclude(除了)或者include(包括),2.1.0 新增来坐判断



//或include="a,b" :include="/a|b/",a 和 b 表示组件的 name
//因为有些页面,如试试数据统计,要实时刷新,所以就不需要缓存

<router-view/> //路由标签



// c 表示组件的 name值

注:匹配首先检查组件自身的 name 选项,如果 name 选项不可用,则匹配它的局部注册名称 (父组件 components 选项的键值)。匿名组件不能被匹配
4.用 v-if 做判断,组件会重新渲染,但是不用一一列举组件 name

30.2 全局路由钩子
1.router.beforeEach

router.beforeEach((to, from, next) => {
console.log('全局前置守卫:beforeEach -- next需要调用') //一般登录拦截用这个,也叫导航钩子守卫
if (path === '/login') {

next()
return

}
if (token) {

next();

}
})
2.router.beforeResolve (v 2.5.0+)
和beforeEach类似,区别是在导航被确认之前,同时在所有组件内守卫和异步路由组件被解析之后,解析守卫就被调用
即在 beforeEach之后调用

3.router.afterEach
全局后置钩子
在所有路由跳转结束的时候调用
这些钩子不会接受 next 函数也不会改变导航本身

30.3 组件路由钩子
1.beforeRouteEnter
在渲染该组件的对应路由被确认前调用,用法和参数与router.beforeEach类似,next需要被主动调用
此时组件实例还未被创建,不能访问this
可以通过传一个回调给 next来访问组件实例。在导航被确认的时候执行回调,并且把组件实例作为回调方法的参数

beforeRouteEnter (to, from, next) {
// 这里还无法访问到组件实例,this === undefined
next( vm => {

// 通过 `vm` 访问组件实例

})
}
2.beforeRouteUpdate (v 2.2+)
在当前路由改变,并且该组件被复用时调用,可以通过this访问实例, next需要被主动调用,不能传回调

3.beforeRouteLeave
导航离开该组件的对应路由时调用,可以访问组件实例 this,next需要被主动调用,不能传回调

30.4 路由模式
设置 mode 属性:hash或 history

30.5 Vue.$router
this.$router.push():跳转到不同的url,但这个方法回向history栈添加一个记录,点击后退会返回到上一个页面
this.$router.replace():不会有记录
this.$router.go(n):n可为正数可为负数。正数返回上一个页面,类似 window.history.go(n)
30.6 Vue.$route
表示当前跳转的路由对象,属性有:
name:路由名称
path:路径
query:传参接收值
params:传参接收值
fullPath:完成解析后的 URL,包含查询参数和 hash 的完整路径
matched:路由记录副本
redirectedFrom:如果存在重定向,即为重定向来源的路由的名字

this.$route.params.id:获取通过 params 或/:id传参的参数
this.$route.query.id:获取通过 query 传参的参数
30.7 router-view 的 key
场景:由于 Vue 会复用相同组件, 即 /page/1 => /page/2 或者 /page?id=1 => /page?id=2 这类链接跳转时, 将不在执行created, mounted之类的钩子


这样组件的 created 和 mounted 就都会执行

31.Object.freeze
场景:一个长列表数据,一般不会更改,但是vue会做getter和setter的转换
用法:是ES5新增的特性,可以冻结一个对象,防止对象被修改
支持:vue 1.0.18+对其提供了支持,对于data或vuex里使用freeze冻结了的对象,vue不会做getter和setter的转换
注意:冻结只是冻结里面的单个属性,引用地址还是可以更改

new Vue({

data: {
    // vue不会对list里的object做getter、setter绑定
    list: Object.freeze([
        { value: 1 },
        { value: 2 }
    ])
},
mounted () {
    // 界面不会有响应,因为单个属性被冻结
    this.list[0].value = 100;

    // 下面两种做法,界面都会响应
    this.list = [
        { value: 100 },
        { value: 200 }
    ];
    this.list = Object.freeze([
        { value: 100 },
        { value: 200 }
    ]);
}

})
32.调试 template
场景:在Vue开发过程中, 经常会遇到template模板渲染时JavaScript变量出错的问题, 此时也许你会通过console.log来进行调试
这时可以在开发环境挂载一个 log 函数

// main.js
Vue.prototype.$log = window.console.log;

// 组件内部

{{$log(info)}}

33.vue-loader 小技巧
33.1 preserveWhitespace
场景:开发 vue 代码一般会有空格,这个时候打包压缩如果不去掉空格会加大包的体积
配置preserveWhitespace可以减小包的体积

{
vue: {

preserveWhitespace: false

}
}
33.2 transformToRequire
场景:以前在写 Vue 的时候经常会写到这样的代码:把图片提前 require 传给一个变量再传给组件

// page 代码

现在:通过配置 transformToRequire 后,就可以直接配置,这样vue-loader会把对应的属性自动 require 之后传给组件

// vue-cli 2.x在vue-loader.conf.js 默认配置是
transformToRequire: {

video: ['src', 'poster'],
source: 'src',
img: 'src',
image: 'xlink:href'

}

// 配置文件,如果是vue-cli2.x 在vue-loader.conf.js里面修改
avatar: ['default-src']

// vue-cli 3.x 在vue.config.js
// vue-cli 3.x 将transformToRequire属性换为了transformAssetUrls
module.exports = {
pages,
chainWebpack: config => {

config
  .module
    .rule('vue')
    .use('vue-loader')
    .loader('vue-loader')
    .tap(options => {
  options.transformAssetUrls = {
    avatar: 'img-src',
  }
  return options;
  });

}
}

// page 代码可以简化为

34.为路径设置别名
1.场景:在开发过程中,我们经常需要引入各种文件,如图片、CSS、JS等,为了避免写很长的相对路径(../),我们可以为不同的目录配置一个别名

2.vue-cli 2.x 配置

// 在 webpack.base.config.js中的 resolve 配置项,在其 alias 中增加别名
resolve: {

extensions: ['.js', '.vue', '.json'],
alias: {
  'vue$': 'vue/dist/vue.esm.js',
  '@': resolve('src'),
}

},
3.vue-cli 3.x 配置

// 在根目录下创建vue.config.js
var path = require('path')
function resolve (dir) {
console.log(__dirname)
return path.join(__dirname, dir)
}
module.exports = {
chainWebpack: config => {

config.resolve.alias
  .set(key, value) // key,value自行定义,比如.set('@@', resolve('src/components'))

}
}
35.img 加载失败
场景:有些时候后台返回图片地址不一定能打开,所以这个时候应该加一张默认图片

// page 代码

36.css
36.1 局部样式
1.Vue中style标签的scoped属性表示它的样式只作用于当前模块,是样式私有化.

2.渲染的规则/原理:
给HTML的DOM节点添加一个 不重复的data属性 来表示 唯一性
在对应的 CSS选择器 末尾添加一个当前组件的 data属性选择器来私有化样式,如:.demo[data-v-2311c06a]{}
如果引入 less 或 sass 只会在最后一个元素上设置

// 原始代码

// 浏览器渲染效果

Vue.js scoped


36.2 deep 属性
// 上面样式加一个 /deep/

// 浏览器编译后

Write in front
The documentation on slots in Vue is very short, and the language is concise. In addition, the differences in frequency and order of use between Vue and commonly used options such as methods, data, and computed may cause developers who are new to slots to easily feel like "forget it, learn later, you can already write basic components", so they closed the Vue documentation.
In fact, the concept of a slot is very simple. Let's talk about it in three parts. This section is also written in the order of the Vue documentation.
Before entering the third section, let students who have not yet been exposed to a slot have a simple concept of what a slot is: a slot, also known as a slot, is an HTML template for a component. Whether this template is displayed or not, and how it is displayed is determined by the parent component. In fact, the two core issues of a slot are highlighted here, which are whether to display it and how to display it.
Due to the fact that slots are a template, any component can be divided into two categories from the perspective of template types: non slot templates and slot templates.
Non slot templates refer to HTML templates, which refer to 'div, span, ul, table', etc. The display and hiding of non slot templates and how they are displayed are controlled by the plugin itself; The slot template is a slot, which is an empty shell because its display and hiding, as well as the final HTML template display, are controlled by the parent component. However, the position of the slot display is determined by the child component itself. The slot is written in which block of the component template, and the template passed from the parent component will be displayed in which block in the future.
Single slot | Default slot | Anonymous slot
Firstly, there is a single slot, which is the official name for Vue, but it can also be called the default slot, or as opposed to a named slot, we can call it an anonymous slot. Because it does not need to set the name attribute.
A single slot can be placed anywhere in a component, but like its name, there can only be one such slot in a component. Correspondingly, there can be many named slots, as long as the name (name attribute) is different.
Below is an example to demonstrate.
Parent component:


Sub components::


In this example, because the parent component has written an HTML template in, the anonymous slot template for the child component is as follows. That is to say, the anonymous slot of the sub component has been used, which is used by the template below.是被下面这块模板使用了。

menu1 menu2 menu3 menu4 menu5 menu6

The final rendering result is shown in the figure:

Note: All demos have been styled for easy observation. Among them, the parent component is filled with a gray background, and the child components are filled with light blue.
Named slot
Anonymous slots do not have a name attribute, so they are anonymous slots. Therefore, adding a name attribute to a slot becomes a named slot. Named slots can appear N times in a component. Appear in different locations. The following example is a component with two named slots and a single slot. These three slots are displayed by the parent component in the same CSS style, with slight differences in content.
Parent component:

子组件:


The displayed results are shown in the figure:
As can be seen, the parent component is associated with a named slot through the slot attribute on the HTML template. HTML templates without slot attributes are associated with anonymous slots by default.
Scope slot
Finally, there is our scope slot. This is a bit difficult to understand. Officially, it is called a scope slot. In fact, compared to the previous two slots, we can call it a slot with data. What does it mean? The first two are both written in the template of the component
Anonymous slot

Named slot

However, the scope slot requires binding data on the slot. That is to say, you need to write it like this.


export default {

data: function(){
  return {
    data: ['zhangsan','lisi','wanwu','zhaoliu','tianqi','xiaoba']
  }
},

}
As we mentioned earlier, whether the slot is displayed or not depends on whether the parent component has written a template under the child, as shown below.

html template

Once written, the slot always needs to display something in the browser, which is what HTML should look like. If not written, the slot is just an empty shell, with nothing.
OK, when we say there is an HTML template, that is, when the parent component inserts a template into the child component, what style should be inserted? This is determined by the parent component's HTML+CSS, but what is the content in this style?
Because the scope slot is bound to a set of data, the parent component can be used. Thus, the situation becomes like this: the style of the parent component is the final say, but the content can show the slot binding of the child component.
Let's compare the differences between a scope slot and a single slot and a named slot. Because a single slot and a named slot do not bind data, the template provided by the parent component should include both style and content. In the example above, the text you see, "Menu 1" and "Menu 2", are all provided by the parent component itself; And for the scope slot, the parent component only needs to provide a set of styles (provided that the data bound to the scope slot is confirmed).
In the following example, you can see that the parent component provides three styles (flex, ul, and direct display), but does not provide data. The data is all based on the person name array bound by the child component slot itself.
Parent component:

Sub components:

export default {

data: function(){
  return {
    data: ['zhangsan','lisi','wanwu','zhaoliu','tianqi','xiaoba']
  }
}

}
The results are shown in the figure:
Github
The above three demos are placed on GitHub, and you can go and get them if you need them. It is very convenient to use and is based on the Vue cli construction project.
Address point here
last
If this article is helpful for you to understand slot and scope slot, please don't be stingy with your likes.
Programming is valued in practice, take action now!

Look at the interview questions, just to fill in any gaps and see what aspects you still don't understand. Remember not to think that memorizing interview questions means everything is fine. It's best to understand the underlying principles so that you can have a smooth conversation during the interview. Otherwise, a slightly skilled interviewer can tell at a glance whether they have real talent and knowledge or have just memorized this interview question.
(These are all basic vue interview questions, so there's no need to waste time looking down.)
1、 What is your understanding of MVVM?
MVVM is the abbreviation for Model View View Model.
A Model represents a data model, and business logic for data modification and operation can also be defined in the Model.
View represents the UI component, which is responsible for transforming the data model into a UI for presentation.
ViewModel listens to changes in model data, controls view behavior, and handles user interaction. Simply understood, it is an object that synchronizes View and Model, connecting Model and View.
In the MVVM architecture, there is no direct connection between View and Model, but rather interaction through ViewModel. The interaction between Model and ViewModel is bidirectional, so changes in View data are synchronized to the Model, and changes in Model data are immediately reflected on the View.
ViewModel connects the View and Model layers through bidirectional data binding, and the synchronization between View and Model is completely automatic without human intervention. Therefore, developers only need to focus on business logic, do not need to manually operate DOM, and do not need to pay attention to data state synchronization issues. Complex data state maintenance is completely managed by MVVM.
Bg2015020110.png
2、 Vue's lifecycle
Before creating, data observation and initialization events have not yet started
Created (after creation) completes data observation, property and method operations, initializes events, and the $el attribute has not yet been displayed
Before Mount is called before the start of the mount, and the relevant render function is called for the first time. The instance has completed the following configuration: compile the template and generate HTML from the data and template in the data. Please note that the HTML has not been attached to the page at this time.
Mounted (after loading) is a newly created VM in EL$ El, and mount it to the instance. The instance has completed the following configuration: replace the DOM object pointed to by the el attribute with the compiled HTML content above. Complete the HTML rendering in the template to the HTML page. Perform ajax interaction during this process.
BeforeUpdate is called before the data is updated, which occurs before the virtual DOM is re rendered and patched. You can further change the state in this hook without triggering additional re rendering processes.
Updated is called after the virtual DOM is re rendered and patched due to data changes. When called, the component DOM has been updated, so DOM dependent operations can be performed. However, in most cases, changing the state during this period should be avoided as it may result in an infinite loop of updates. This hook is not called during server-side rendering.
BeforeDestroy is called before the instance is destroyed. The instance is still fully available.
Destroyed is called after the instance is destroyed. After the call, all event listeners will be removed and all child instances will also be destroyed. This hook is not called during server-side rendering.
What is the Vue lifecycle?
Answer: The process from creation to destruction of Vue instances is the lifecycle. The lifecycle of Vue is a series of processes, including creating, initializing data, compiling templates, mounting Doms, rendering, updating, rendering, and destroying.
What is the role of the Vue lifecycle?
Answer: It has multiple event hooks in its lifecycle, making it easier for us to form good logic when controlling the entire Vue instance process.
How many stages are there in the Vue lifecycle?
Answer: It can be divided into a total of 8 stages: before/after creation, before/after loading, before/after updating, and before/after destruction.
Which hooks will trigger the first page load?
Answer: The following beforeCreate, created, beforeMount, and mounted will be triggered.
In which cycle has DOM rendering been completed?
Answer: The DOM rendering is already completed in mounted.
3、 The principle of Vue implementing bidirectional data binding: Object. defineProperty()
The main way to achieve bidirectional data binding in Vue is to use a combination of data hijacking and publisher subscriber mode, using Object. defineProperty() to hijack the setters and getters of various properties, and publish messages to subscribers when data changes, triggering corresponding listening callbacks. When passing a regular Javascript object to a Vue instance as its data option, Vue will traverse its properties and convert them to getters/setters using Object. defineProperty. Users cannot see getters/setters, but internally they allow Vue to track dependencies and notify changes when attributes are accessed and modified.
The bidirectional data binding of Vue uses MVVM as the entry point for data binding, integrating Observer, Compile, and Watcher. Through Observer, it listens for data changes in its own model, parses compilation template instructions through Compile (used in Vue to parse {{}}), and finally uses Watcher to build a communication bridge between Observer and Compile, achieving data changes ->view updates; View interaction change (input) ->bidirectional binding effect of data model change.
Implementing Simple Bidirectional Binding with JavaScript

<div id="app">
<input type="text" id="txt">
<p id="show"></p>


4、 Parameter transfer between Vue components

  1. Parent component and child component passing values
    The parent component passes to the child component: the child component receives data through the props method;
    Child component passing to parent component: $exit method passing parameters
  2. Data transfer between non parent-child components, and value transfer between sibling components
    EventBus is the creation of an event center, equivalent to a transit station, that can be used to transmit and receive events. The project is relatively small, so this is more suitable. (Although many people recommend using VUEX directly, it depends on the requirements. Technology is just a means, achieving the goal is the king's way.)
    5、 Route Implementation of Vue: Hash Mode and History Mode
    Hash mode: In the browser, the symbol '#', followed by the characters' # ', is called a hash, which is read using window. location. hash;
    Feature: Although hash is in the URL, it is not included in HTTP requests; Used to guide browser actions, useless for server security, hash will not reload the page.
    In hash mode, only the content before the hash symbol will be included in the request, such as http://www.xxx.com Therefore, for the backend, even if full coverage of the route is not achieved, a 404 error will not be returned.
    History mode: History adopts the new features of HTML5; And two new methods are provided: pushState (), replaceState () to modify the browser history stack, and to listen for state changes in popState events.
    In the history mode, the front-end URL must be consistent with the actual URL that initiates the request from the backend, such as http://www.xxx.com/items/id . If the backend lacks routing processing for/items/id, it will return a 404 error. In the official website of Vue Router, it is described as follows: "However, to play this mode well, backend configuration support is also required... So, you need to add a candidate resource on the server that covers all situations: if the URL cannot match any static resources, you should return to the same index. html page, which is the page that your app depends on
    6、 What is the difference between Vue, Angular, and React?
    (The versions are constantly updated, and the following differences may not be very accurate. I only use Vue in my work and am not very familiar with Angular and React.)
  3. Differences from AngularJS
    Similarities:
    Both support instructions: built-in instructions and custom instructions; Both support filters: built-in filters and custom filters; Both support bidirectional data binding; None of them support low-end browsers.
    Differences:
    AngularJS has a high learning cost, such as adding the Dependency Injection feature, while Vue.js itself provides a relatively simple and intuitive API; In terms of performance, AngularJS relies on dirty checking of data, so the more Watchers, the slower; Vue.js uses dependency tracking based observation and asynchronous queue updates, and all data is independently triggered.
  4. Differences from React
    Similarities:
    React adopts a special JSX syntax, and Vue. js also advocates writing. Vue special file formats in component development, with some conventions for file content, both of which need to be compiled and used; The central idea is the same: everything is a component, and component instances can be nested; Provide reasonable hook functions that allow developers to customize requirements; They do not have built-in column count AJAX, Route, and other functions in the core package, but are loaded as plugins; Mixins feature is supported in component development.
    Differences:
    The Virtual DOM used by React will perform dirty checks on the rendered results; Vue.js provides instructions, filters, and more in the template, making it very convenient and fast to operate the Virtual DOM.
    7、 Hook function for Vue routing
    The homepage can control navigation jumps, beforeEach, afterEach, etc., and is generally used for modifying page titles. Some redirect functions that require login to adjust the page.
    Before Each mainly has three parameters to, from, and next:
    To: The target routing object that the route is about to enter,
    From: route The current navigation is about to leave the route
    Next: Function must call the method resolve hook. The execution effect depends on the calling parameters of the next method. Can control webpage redirection.
    8、 What is vuex? How to use it? Which functional scenario uses it?
    The states that are only used for reading are concentrated in the store; The way to change the state is to submit mutations, which is a synchronized thing; Asynchronous logic should be encapsulated in actions.
    Introduce the store in main.js and inject it. Created a new directory store Export.
    Scenarios include: in a single page application, the status between components, music playback, login status, and adding to the shopping cart
    Picture description
    State
    Vuex uses a single state tree, meaning that each application will only contain one store instance, but a single state tree and modularity do not conflict. The stored data status cannot be directly modified.
    Mutations
    The methods defined by mutations dynamically modify the state or data in Vuex's store.
    Getters
    Computational attributes similar to Vue are mainly used to filter some data.
    Action
    Actions can be understood as transforming the data processing methods in mutations into asynchronous data processing methods, which in simple terms are asynchronous operations on data. The view layer distributes actions through store.dispath.

const store = new Vuex.Store({ //store

  state: {
     count: 0
         },
  mutations: {                
     increment (state) {
      state.count++
     }
      },
  actions: { 
     increment (context) {
      context.commit('increment')

}
}
})
modules
When the project is particularly complex, each module can have its own state, mutation, action, and getters, making the structure very clear and easy to manage.

const moduleA = {
state: { ... },
mutations: { ... },
actions: { ... },
getters: { ... }
}
const moduleB = {
state: { ... },
mutations: { ... },
actions: { ... }
}

const store = new Vuex.Store({
modules: {

a: moduleA,
b: moduleB

})
9、 How can Vue cli add custom instructions?

  1. Create local instructions

var app = new Vue({

el: '#app',
data: {    
},
// Create instructions (can have multiple)
directives: {
    // Instruction Name
    dir1: {
        inserted(el) {
            // The first parameter in the instruction is the DOM of the currently used instruction
            console.log(el);
            console.log(arguments);
            // Operate on DOM
            el.style.width = '200px';
            el.style.height = '200px';
            el.style.background = '#000';
        }
    }
}

})
2.Global Directives

Vue.directive('dir2', {

inserted(el) {
    console.log(el);
}

})
3.Use of instructions

十、How can Vue customize a filter?
html code:

{{msg| capitalize }}

JS code:

var vm=new Vue({

el:"#app",
data:{
    msg:''
},
filters: {
  capitalize: function (value) {
    if (!value) return ''
    value = value.toString()
    return value.charAt(0).toUpperCase() + value.slice(1)
  }
}

})
Global Definition Filter

Vue.filter('capitalize', function (value) {
if (!value) return ''
value = value.toString()
return value.charAt(0).toUpperCase() + value.slice(1)
})
The filter receives the value of the expression (msg) as the first parameter. The capitalization filter will receive the value of msg as the first parameter.

11、 What is your understanding of keep alive?
Keep alive is a built-in component of Vue that can preserve the state of the included components or avoid re rendering.
After version 2.1.0 of Vue, keep alive added two new attributes: include (cache of included components) and exclude (exclude components are not cached and have a higher priority than include).
Usage method
<keep alive include='include_ Components' exclude='exclude_ Components

<-- Whether this component is cached depends on the include and exclude attributes -->


Parameter Interpretation
Include - String or regular expression, only components with matching names will be cached
Exclude - Strings or regular expressions, any components with matching names will not be cached
The properties of include and exclude allow components to cache conditionally. Both can use "," to separate strings, regular expressions, and arrays. When using regexes or arrays, remember to use v-bind.
Usage examples
<-- Comma separated string, only components a and b are cached. -->
Keep alive include="a, b">


<-- Regular expression (requires the use of v-bind, all matching rules will be cached) -->
Keep alive: include="/a | b/">


<-- Array (requires the use of v-bind, everything contained will be cached) -->
Keep alive: include="['a ','b']">


12、 An interview question that can be answered in just one sentence

  1. CSS only works on the current component
    Answer: Write scoped in the style tag, for example:
  2. Differences between v-if and v-show
    Answer: V-if is rendered according to the conditions, and V-show is the block or none of display;
  3. Differences between $route and $router
    Answer: $route is the "routing information object", including path, params, hash, query, fullPath, matched, name, and other routing information parameters. And $router is the "routing instance" object, which includes the jump method, hook function, and so on of the route.
    What are the two cores of vue.js?
    Answer: Data driven, component system
  4. Several Common Instructions for Vue
    Answer: V-for, V-if, V-bind, V-on, V-show, V-else
  5. What are the commonly used modifiers for Vue?
    Answer:. prevent: Submitting an event will no longer overload the page Stop: Prevent click events from bubbling Self: Triggered when an event occurs on the element itself rather than a child element Capture: Event listening, which will be called when an event occurs
    Can V-on bind multiple methods?
    Answer: Yes
    What is the role of key values in Vue?
    Answer: When Vue.js uses v-for to update the rendered element list, it defaults to using the "in place reuse" strategy. If the order of the data items is changed, Vue will not move DOM elements to match the order of the data items, but will simply reuse each element here and ensure that it displays each element that has been rendered under a specific index. The main function of key is to efficiently update the virtual DOM.
  6. What are the computational properties of Vue?
    Answer: Placing too much logic in a template can make it too heavy and difficult to maintain. In situations where complex data processing is required and may be used multiple times, it is recommended to use the method of calculating attributes as much as possible. Benefits: ① Clear data processing structure; ② Depends on data, updates data, and automatically updates processing results; ③ This points to the VM instance within the calculation attribute; ④ When calling the template, simply write the calculation property name; ⑤ The commonly used method is the getter method, which can obtain data or use the set method to change the data; ⑥ Compared to methods, regardless of whether the dependent data remains unchanged, methods will be recalculated. However, when the dependent data remains unchanged, computed is obtained from the cache and will not be recalculated.
  7. Single page applications such as Vue and their advantages and disadvantages
    Answer: Advantages: Vue's goal is to achieve responsive data binding and composite view components through the simplest possible API, with a responsive data binding system at its core. MVVM, data-driven, componentized, lightweight, concise, efficient, fast, and modular friendly.
    Disadvantage: It does not support lower version browsers, and only supports IE9 at the minimum; Not conducive to SEO optimization (if you want to support SEO, it is recommended to use the server to render components); The first time loading the homepage takes a relatively long time; You cannot use the browser's navigation buttons and need to implement forward and backward actions yourself.
  8. How to define dynamic routing for vue router? How to obtain the value passed in
    Answer: In the index.js file in the router directory, add/: id to the path attribute and use the params. id of the router object to obtain it.
    During Vue interviews, frequently asked interview questions/Vue knowledge points are organized
  9. front-end real factory interview questions
    Learning ES6 Notes - ES6 Grammar Commonly Used in Work