0%

前端面试题-vue(补充中...)


vue优点

1
2
3
4
5
6
7
8
9
10
11
12
13
轻量级框架: 只关注视图层,是一个构建数据的视图集合,大小只有几十kb;

简单易学: 国人开发,中文文档,不存在语言障碍 ,易于理解和学习;

双向数据绑定: 保留了angular的特点,在数据操作方面更为简单;

组件化: 保留了react的优点,实现了html的封装和重用,在构建单页面应用方面有着独特的优势;

视图,数据,结构分离: 使数据的更改更为简单,不需要进行逻辑代码的修改,只需要操作数据就能完成相关操作;

虚拟DOM: dom操作是非常耗费性能的, 不再使用原生的dom操作节点,极大解放dom操作,但具体操作的还是dom不过是换了另一种方式;

运行速度更快: 相比较与react而言,同样是操作虚拟dom,就性能而言,vue存在很大的优势。

单页面应用和多页面应用区别及优缺点

1
2
3
4
5
6
单页面应用(SPA),通俗一点说就是指只有一个主页面的应用,浏览器一开始要加载所有必须的 html, js, css。所有的页面内容都包含在这个所谓的主页面中。但在写的时候,还是会分开写(页面片段),然后在交互的时候由路由程序动态载入,单页面的页面跳转,仅刷新局部资源。多应用于pc端。
多页面(MPA),就是指一个应用中有多个页面,页面跳转时是整页刷新
单页面的优点:
用户体验好,快,内容的改变不需要重新加载整个页面,基于这一点spa对服务器压力较小;前后端分离;页面效果会比较炫酷(比如切换页面内容时的专场动画)。
单页面缺点:
不利于seo;导航不可用,如果一定要导航需要自行实现前进、后退。(由于是单页面不能用浏览器的前进后退功能,所以需要自己建立堆栈管理);初次加载时耗时多;页面复杂度提高很多

为什么说vue是一个渐进式的javascript框架,渐进式是什么意思?

1
2
3
4
5

vue允许你将一个页面分割成可复用的组件,每个组件都包含属于自己的html、css、js用来渲染网页中相应的地方。
对于vue的使用可大可小,它都会有相应的方式来整合到你的项目中。所以说它是一个渐进式的框架。

vue是响应式的(reactive)这是vue最独特的特性,也就是说当我们的数据变更时,vue会帮你更新所有网页中用到它的地方。

vue的性能优化

1
路由懒加载、图片懒加载、第三方组件库按需引入、keep-alive缓存页面、使用v-show复用DOM、避免v-if与v-for同时使用

为什么避免v-for和v-if同时使用?

1
v-for比v-if优先级高,使用的话每次v-for都会v-if判断,影响性能

vue的两个核心点

1
2
3
4
数据驱动、组件系统

数据驱动:ViewModel,保证数据和视图的一致性。
组件系统:应用类UI可以看作全部是由组件树构成的

为什么使用虚拟DOM

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
创建真实DOM的代价高,虚拟dom由于本质是一个js对象,因此天生具备跨平台的能力,可以实现在不同平台的准确显示。

(1) 虚拟dom是什么?
vue2.x才有的虚拟dom;本质是js对象;
(2) 虚拟dom在vue中做了什么?
[1].将真实dom转化虚拟dom(js对象);
[2].更新的时候做对比;
(3) 虚拟dom是如何提升vue的渲染效率的?
[1].局部更新(节点更新);
[2].将直接操作dom的地方拿到两个js对象之中去做比较

虚拟 DOM 的实现原理主要包括以下 3 部分:

用 JavaScript 对象模拟真实 DOM 树,对真实 DOM 进行抽象;

diff 算法 — 比较两棵虚拟 DOM 树的差异;

pach 算法 — 将两个虚拟 DOM 对象的差异应用到真正的 DOM 树

vue组件销毁方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
1、使用 v-if 当v-iftrue时就会重新渲染组件 (手动销毁)

<template>
<div>
<test v-if="reCreate"></test>
<button @click="close">点击成功销毁</button>
<button @click="open">点击成功创建</button>
</div>
</template>

<script>
export default {
data:{
reCreate: true,
menuTree: []
},
methods:{
open(){
this.reCreate= true
},
close(){
this.reCreate = false
}
},
//或者监听
watch:{
menuTree(){
this.reFresh= false
this.$nextTick(()=>{
this.reFresh = true
})
}
}
}
</script>

2、绑定key值 改变key值 (手动销毁)

<template>
<div>
<test :key="key"></test>
<button @click="reCreate">点击重新创建</button>
</div>
</template>

<script>
export default {
data:{
key: 0
},
methods:{
reCreate(){
this.key++
}
},
//或者监听
watch:{
$route(){
++this.menuKey
}
}
}
</script>

3、$destroy
$destroy是组件内部销毁自已。和外部销毁(v-if)的区别在于,内部销毁不会移除页面上已有的DOM的。所以一般需要加上移除DOM的代码
destroyElement() {
this.$destroy();
this.$el.parentNode.removeChild(this.$el);
},

MVC和MVVM

1
2
3
MVC包括view视图层、controller控制层、model数据层。各部分之间的通信都是单向的

MVVM包括view视图层、model数据层、viewmodel层。各部分通信都是双向的

v-model的原理/双向数据绑定原理

1
2
3
4
5
6
7
8
9
vue 双向数据绑定是通过 数据劫持 结合 发布订阅模式的方式来实现的, 也就是说数据和视图同步,数据发生变化,视图跟着变化,视图变化,数据也随之发生改变;
核心:关于VUE双向数据绑定,其核心是 Object.defineProperty()方法。

v-model用于表单数据的双向绑定,其实它就是一个语法糖,这个背后就做了两个操作:
v-bind绑定一个value属性;
v-on指令给当前元素绑定input事件


发布订阅模式 定义了一种一对多的依赖关系,让多个订阅者对象同时监听某一个主题对象。这个主题对象在自身状态变化时,会通知所有订阅者对象,使它们能够自动更新自己的状态

vue生命周期

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
什么是vue生命周期?

Vue实例从创建到销毁的过程,就是生命周期

Vue声明周期的作用是什么?
它的生命周期中有多个事件钩子,让我们在控制整个Vue实例的过程时更容易形成好的逻辑

Vue生命周期总共有几个阶段?(创建->加载->更新->销毁)
8个阶段:
创建前 beforeCreate / 创建前后 created,
载入前 beforeMount / 载入后 mounted,
更新前 beforeUpdate / 更新后 updated,
销毁前 beforeDestory/ 销毁后 destroyed。

第一次页面加载会触发那几个钩子?
第一次页面加载时会触发
beforeCreate,created,
beforeMount,mounted 这几个钩子

每个生命周期适合那些场景?
1、beforeCreate(创建前) :数据观测和初始化事件还未开始,此时 data 的响应式追踪、event/watcher 都还没有被设置,也就是说不能访问到data、computed、watch、methods上的方法和数据。
2、created(创建后) :实例创建完成,实例上配置的 options 包括 data、computed、watch、methods 等都配置完成,但是此时渲染得节点还未挂载到 DOM,所以不能访问到 $el 属性。
3、beforeMount(挂载前) :在挂载开始之前被调用,相关的render函数首次被调用。实例已完成以下的配置:编译模板,把data里面的数据和模板生成html。此时还没有挂载html到页面上。
4、mounted(挂载后) :在el被新创建的 vm.$el 替换,并挂载到实例上去之后调用。实例已完成以下的配置:用上面编译好的html内容替换el属性指向的DOM对象。完成模板中的html渲染到html 页面中。此过程中进行ajax交互。
5、beforeUpdate(更新前) :响应式数据更新时调用,此时虽然响应式数据更新了,但是对应的真实 DOM 还没有被渲染。
6、updated(更新后):在由于数据更改导致的虚拟DOM重新渲染和打补丁之后调用。此时 DOM 已经根据响应式数据的变化更新了。调用时,组件 DOM已经更新,所以可以执行依赖于DOM的操作。然而在大多数情况下,应该避免在此期间更改状态,因为这可能会导致更新无限循环。该钩子在服务器端渲染期间不被调用。
7、beforeDestroy(销毁前) :实例销毁之前调用。这一步,实例仍然完全可用,this 仍能获取到实例。
8、destroyed(销毁后) :实例销毁后调用,调用后,Vue 实例指示的所有东西都会解绑定,所有的事件监听器会被移除,所有的子实例也会被销毁。该钩子在服务端渲染期间不被调用。
另外还有 keep-alive 独有的生命周期,分别为 activated 和 deactivated 。用 keep-alive 包裹的组件在切换时不会进行销毁,而是缓存到内存中并执行 deactivated 钩子函数,命中缓存渲染后会执行 activated 钩子函数

第一次页面加载会触发哪几个钩子

1
beforeCreate, created, beforeMount, mounted

vue获取数据在哪个周期函数

1
2
一般 created/beforeMount/mounted 皆可.
比如如果你要操作 DOM , 那肯定 mounted 时候才能操作

Vue的父子组件生命周期钩子函数执行顺序

1
2
3
4
5
6
7
8
9
10
11
<!-- 加载渲染过程 -->
<!-- 父beforeCreate -> 父created -> 父beforeMount -> 子beforeCreate -> 子created -> 子beforeMount -> 子mounted -> 父mounted -->

<!-- 子组件更新过程 -->
<!-- 父beforeUpdate -> 子beforeUpdate -> 子updaed -> 父updated -->

<!-- 父组件跟新过程 -->
<!-- 父beforeUpdate -> 父updated -->
<!-- 销毁过程 -->

<!-- 父beforeDestroy -> 子beforeDestroy -> 子destroyed ->父destroyed -->

Vue组件通信

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
props/$emit、$refs、EventBus、$parent、vuex、provide / inject

1)props、$emit
父传子:
在父组件中,用v-bind动态绑定一个自定义属性,给子组件传递数据。
在子组件中,使用props接收父组件传过来的数据。

子传父:
子组件通过事件的方式,利用$emit给父组件传值。($emit的第一个参数是父组件自定义事件的方法名,后面的“value”是子组件要给父组件传递的数据)。
在父组件中,绑定一个自定义事件,用来接收子组件传来的值。

2)ref
父组件在使用子组件的时候设置ref
父组件通过设置子组件ref来获取数据
父组件
//<Children ref ="foo">
this.$refs.foo // 获取子组件实例,通过子组件实例我们就能拿到对应的数据

$refs 可以用来获取子组件的数据(ref只在dom渲染完后才会有,可以在生命周期mounted(){}钩子中调用,或者在this.$nextTick(()=>{})中调用)。

3)$parent //可以用来从一个子组件访问父组件的实例
通过共同祖辈$parent或者$root搭建通信侨联

兄弟组件
this.$parent.on('add',this.add)
另一个兄弟组件
this.$parent.emit('add')


provide / inject:
在祖先组件定义provide属性,返回传递的值
在后代组件通过inject接收组件传递过来的值
传递的时候:以对象形式传递过去
provide() {
return {
sonDate: '子组件数据',
childDate: '孙组件数据'
}
},
接收的时候和props接收方式一样:
inject:['参数'] // inject:['sonDate']

EventBus //兄弟组件传值
bus.js
import Vue from 'vue'
const bus = new Vue()
export default bus
使用:先引入bus文件
传递:bus.$emit('message','传递的值')
接收:bus.$on('message',(e)=>{e就是获取到的值})
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
Vue 组件间通信只要指以下 3 类通信:父子组件通信、隔代组件通信、兄弟组件通信,下面我们分别介绍每种通信方式且会说明此种方法可适用于哪类组件间通信。
1)props / $emit 适用 父子组件通信

这种方法是 Vue 组件的基础,相信大部分同学耳闻能详,所以此处就不举例展开介绍。
2)ref / $refs

ref:如果在普通的 DOM 元素上使用,引用指向的就是 DOM 元素;如果用在子组件上,引用就指向组件实例
$parent / $children:访问父 / 子实例
3)EventBus ($emit / $on)适用于 父子、隔代、兄弟组件通信

这种方法通过一个空的 Vue 实例作为中央事件总线(事件中心),用它来触发事件和监听事件,从而实现任何组件间的通信,包括父子、隔代、兄弟组件。
4)$attrs/$listeners适用于 隔代组件通信

$attrs:包含了父作用域中不被 prop 所识别 (且获取) 的特性绑定 ( classstyle 除外 )。当一个组件没有声明任何 prop 时,这里会包含所有父作用域的绑定 ( classstyle 除外 ),并且可以通过 v-bind="$attrs" 传入内部组件。通常配合 inheritAttrs 选项一起使用。
$listeners:包含了父作用域中的 (不含 .native 修饰器的) v-on 事件监听器。它可以通过 v-on="$listeners" 传入内部组件
5)provide / inject适用于 隔代组件通信

祖先组件中通过 provider 来提供变量,然后在子孙组件中通过 inject 来注入变量。provide / inject API 主要解决了跨级组件间的通信问题,不过它的使用场景,主要是子组件获取上级组件的状态,跨级组件间建立了一种主动提供与依赖注入的关系。
6)Vuex适用于 父子、隔代、兄弟组件通信

Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。每一个 Vuex 应用的核心就是 store(仓库)。“store” 基本上就是一个容器,它包含着你的应用中大部分的状态 ( state )。
Vuex 的状态存储是响应式的。当 Vue 组件从 store 中读取状态的时候,若 store 中的状态发生变化,那么相应的组件也会相应地得到高效更新。
改变 store 中的状态的唯一途径就是显式地提交 (commit) mutation。这样使得我们可以方便地跟踪每一个状态的变化。

EventBus 事件总线

1
2
3
4
5
介绍
通常作为多个模块间的通信机制,相当于一个事件管理中心,一个模块发送消息,其它模块接受消息,就达到了通信的作用。

原理
本质上是采用了发布-订阅的设计模式,比如多个模块 A、B、C 订阅了一个事件 EventX,然后某一个模块 X 在事件总线发布了这个事件,那么事件总线会负责通知所有订阅者 A、B、C,它们都能收到这个通知消息,同时还可以传递参数

vue单向数据流

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
1. Vue 的单向数据流:指数据从父组件传递给子组件,子组件没有权利直接修改父组件传来的数据,即子组件从 props 中直接获取的数据,只能请求父组件修改数据再传给子组件。父级属性值的更新会下行流动到子组件中。

2. 为什么不能子组件直接修改父组件传来的值呢?父组件的值可能会不断发生变化,那么如果我们子组件对父组件传来的值比如说 props 有一个 number,子组件收到了 number=1,在收到后,子组件直接改变number 的值为 5,去做些事情,但还未做时父组件数据更新了,传过来一个值 3,也就是说子组件刚将其变为 5,父组件又把它变成了 3,可能影响子组件的使用。说的官方一些,就是父组件的值更新时,子组件中 props 的值也会发生更新。

//父组件
<section id="app">
<custom-component :count="count"></custom-component>
</section>
//子组件
Vue.component("custom-component", {
props: ['count'],
template: `
<div>
<h1>一个自定义模版</h1>
<input type="button" @click="changeCount" value="按钮"/>
{{count}}
</div>`,
methods: {
changeCount() {
this.count++ //直接修改是会报错的
}
}
});

new Vue({
el: "#app",
data() {
return {
count: 0
};
}
});

3. 在子组件中直接用 v-model 绑定父组件传过来的数据是不合理的,如果希望修改父组件传给子组件的值:

1)在子组件 data 中创建一个变量获取 props 中的值,再改变这个 data 中的值。
//子组件
Vue.component("custom-component", {
//作为data中局部数据的初始值来过渡,必须以函数形式申明,
//相当于子组件里操作的都是this.count的指针
data() {
return {
initCount: this.count //建一个变量获取 props 中的值
}
},
props: ['count'],
template: `
<div>
<h1>一个自定义模版</h1>
<input type="button" @click="changeCount" value="按钮"/>
<!--该处应该是使用initCount而不是count-->
{{initCount}}
</div>`,
methods: {
changeCount() {
//这里计算的也是initCount
this.initCount++
}
}
});

2)子组件使用 $emit 发出一个事件,让父组件接收去修改这个值
//父组件
<section id="app">
<h3>父组件使用了count</h3>
<p>{{count}}</p>
<!--需要在自定义模版标签上添加一个自定义事件来接收count值-->
<custom-component :count="count" @increment-click="countHandle"></custom-component>
</section>
//父组件方法
methods: {
countHandle() {
//此处的this.count 属于父组件的count
this.count++;
}
}
//子组件
Vue.component("custom-component", {
data() {
return {
initCount: this.count
}
},
//与直接使用data不同的是这里添加选项参数计算属性`computed`
computed: {
initCount2() {
return this.initCount;
}
},
props: ['count'],
template: `
<div>
<h1>一个自定义模版</h1>
<input type="button" @click="changeCount" value="按钮"/>
<!--这里现在使用的是computed里的函数返回结果-->
{{initCount2}}
</div>`,
methods: {
changeCount() {
this.initCount++;
//触发一下"increment-click"事件, 通知父组件
this.$emit("increment-click");
}
}
});

v-if与v-show的区别?使用场景分别是什么?

1
2
3
v-if:显示隐藏是将dom元素整个添加或删除;适用于运行时条件很少改变。

v-show:隐藏则是为该元素添加css–display:none,dom元素依旧还在;适用于非常频繁地切换。(不能用于权限操作)

vue组件中的data()为什么是函数?

1
2
3
每一个组件都有自己的私有作用域,确保各组件数据不会被干扰。

单纯的写成对象形式,就是所有的组件实例共用了一个data,这样改一个全都改了。

router和route的区别

1
2
3
route是当前正在跳转的路由对象,可以从route里面获取hash,name,path,query,mathsr等属性方法(接收参数时使用)

router跳转连接就可以使用

computed 计算属性和watch监听区别?

1
2
3
4
5
6
computed:
    当一个属性受多个属性影响的时候就需要用到computed
    最典型的栗子: 购物车商品结算的时候
watch:
    当一条数据影响多条数据的时候就需要用watch
    栗子:搜索数据

vue实现打印

1
2
3
插件 vue-print-nb 、print.js

导出: vue-json-excel

vue请求(axios)封装

1
2
3
4
在until文件夹下创建 request.js 文件
创建 axios 以及封装
请求拦截(request,在发送请求之前 给每个接口添加token,或者添加公共参数之类的)和
相应拦截(response,根据 后端返回来的状态码判定执行不同业务,token是否过期,登录状态、授权)

封装过后的axios中加了拦截器heder中加token 有的接口需要token 有的接口不需要带token 的问题解决办法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
第一种实现思路:
在axios拦截器中获取当前的api,将不需要添加token的api存储到一个数组中,将其遍历比较
// request拦截器
// 不加token的api
const exceptUrls = ['xxx','xxx','xxx']
service.interceptors.request.use(config => {
if (getToken() && !isToken && exceptUrls.indexOf(config.url) ===-1) {
config.headers['Authorization'] = getToken() // 让每个请求携带自定义token 请根据实际情况自行修改
}
return config
}, error => {
console.log(error)
Promise.reject(error)
})

第二种实现思路:
在api中添加一个参数,参数带着Boolean类型的标识,然后在axios拦截器中获取进行判断
export function xxx(data) {
return request({
url: 'xxx',
method: 'post',
headers: {'isToken': false},
data
})
}

axios拦截器
// request拦截器
service.interceptors.request.use(config => {
// 是否需要设置 token
const isToken = (config.headers || {}).isToken === false
if (getToken() && !isToken ) {
config.headers['Authorization'] = 'Bearer ' + getToken() // 让每个请求携带自定义token 请根据实际情况自行修改
}
return config
}, error => {
console.log(error)
Promise.reject(error)
})


vue路由封装

1
2
3
4
5
6
7
8
9
10
11
12
1. 创建路由文件
新建 src/router/index.js

2. 将路由实例,传入到 vue 实例
在main.js中

3、设置路由守卫,对路由前置守卫进行了封装,实现了以下功能:

判断用户是否已登录,如果未登录则重定向到登录页。
判断用户是否有权限访问该页面,如果没有则重定向到 403 页面。
根据路由 meta 设置标题。
错误处理:根据错误码重定向到对应的页面

vue权限是怎么弄的

1
2
3
初始化的时候先挂载不需要权限控制的路由,比如登录页,404等错误页。如果用户通过URL进行强制访问,则会直接进入404,相当于从源头上做了控制。

登录后,获取用户的权限信息,然后筛选有权限访问的路由,在全局路由守卫里进行调用addRoutes添加路由

怎么重定向页面?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
const router = new VueRouter({
routes:[
{ path: '/a', redirect: '/b' }
]
})

怎么配置404页面?
const router = new VueRouter({
routes:[
{
path: '*', redirect: {path:'/'}
}
]
})

vue-router 有哪几种导航守卫?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
全局守卫、路由独享守卫、路由组件内的守卫

//路由的钩子函数总结有6个

全局的路由钩子函数:
beforeEach 全局前置守卫,进入路由之前
afterEach 全局后置钩子,进入路由之后

单个的路由钩子函数:
beforeEnter

组件内的路由钩子函数:
beforeRouteEnter 进入路由前
beforeRouteLeave 离开当前路由时
beforeRouteUpdate 路由复用同一个组件时

Vue 如何获取dom?

1
ref=“domName” 用法:this.$refs.domName

$ nextTick的使用?

1
2
当你修改了data的值然后马上获取这个dom元素的值,是不能获取到更新后的值,
你需要使用$nextTick这个回调,让修改后的data值渲染更新到dom元素之后在获取,才能成功

created()钩子函数中进行DOM操作时

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
export default {
name: 'HelloWorld',
data () {
return {
msg: 'HelloWorld'
}
},
created(){
let that=this;
that.$refs.domName; //是报错的

//在created()钩子函数执行的时候DOM 其实并未进行任何渲染,而此时进行DOM操作无异于徒劳,所以此处一定要将DOM操作的js代码放进Vue.nextTick()的回调函数中。与之对应的就是mounted钩子函数,因为该钩子函数执行时所有的DOM挂载已完成

//正确写法
that.$nextTick(()=>{ //不使用this.$nextTick()方法会报错

that.$refs.domName
})
},
}

Vue 强制更新数据的方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
法一:
this.$forceUpdate(); //使用forceUpdate强制渲染,更新视图和数据。注:全局强制刷新,性能消耗高

法二:
this.$set(object,index,new);
Vue.set(object,index,new);
// 参数一:要改变的数组或对象
// 参数二:下标,或者元素名称
// 参数三:得到的新的值

this.$set()方法是Vue自带的可对数组和对象进行赋值,并触发监听的方法。注:指向性强制刷新,性能消耗低

例如
下拉框不能实时更新(强制刷新下)
<el-select v-model="form.workZoneIdsArr" placeholder="请选择所属工区" @change="$forceUpdate()">
<el-option v-for="dict in addTheirWorkAreaList" :key="dict.id" :label="dict.name" :value="dict.id" />
</el-select>

vue改变数据DOM不更新的解决方案

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
1、数组
由于 JavaScript 的限制,Vue 不能检测以下变动的数组:
当你利用索引直接设置一个项时,例如:this.items[indexOfItem] = newValue
当你修改数组的长度时,例如:this.items.length = newLength

解决方案:
Vue.set(this.items, indexOfItem, newValue)
this.items.splice(indexOfItem, 1, newValue)
this.$set(this.items, indexOfItem, newValue) (this.$set 实例方法是全局方法 Vue.set 的一个别名)
this.items.splice(newLength)

2、对象
由于 JavaScript 的限制,Vue 不能检测对象属性的添加或删除

var vm = new Vue({
data: {
userInfo: {
name: 'zhangsan'
}
}
})
方案一 利用Vue.set(object, key, value)
Vue.set(vm.userInfo, 'sex', 'man')

方案二 利用 this.$set(this.object, key, value),这只是全局Vue.set的别名
this.$set(this.userInfo, 'sex', 'man')

方案三 利用Object.assign({}, this.obj,{...})
this.userInfo = Object.assign({}, this.userInfo, {
'sex': 'man',
...
})

删除对象属性的方法(前面对象名,后面具体属性名)

this.$delete(this.userInfo, 'name')

delete和Vue.delete删除数组的区别

1
2
3
delete只是被删除的元素变成了 empty/undefined 其他的元素的键值还是不变。

Vue.delete 直接删除了数组 改变了数组的键值

异步请求适合在哪个生命周期调用?

1
2
3
4

可以在钩子函数 created、beforeMount、mounted 中进行调用,因为在这三个钩子函数中,data 已经创建,可以将服务端端返回的数据进行赋值。

我一般在 created 钩子函数中调用异步请求,能更快获取到服务端数据,减少页面 loading 时间;

过滤器的作用?如何实现一个过滤器?使用场景?

1
过滤器是用来过滤数据的,在vue中使用filters来过滤数据;使用场景:例如(时间/日期 格式化)

Vue中key是用来做什么的?为什么不推介使用index作为key

1
2
3
4
5
6
7
8
9
10
11
1、使用key来给每个节点做一个唯一标识,key的作用主要是为了高效的更新虚拟DOM

2、当以数组的下标index作为index值时,其中一个元素(如增删改查)发生了变化就有可能导致所有元素的key值发生变化


vue中key值的作用可以分为两种情况来考虑,话不多说

这第一种情况是在v-if中使用key。由于vue会尽可能高效渲染元素,通常会复用已有元素而不是从头开始渲染。因此当使用v-if来实现元素切换的时候,如果切换前后含有相同类型的元素,那么这个元素就会被复用。如果是相同的input元素,那么切换前后用户输入不会被清除掉,这样是不符合需求的。因此可以通过使用key来唯一的识别这个元素,这个情况下,使用key的元素就会被复用。这个时候key的作用是用来识别一个独立的元素。

第二种情况是v-for中使用key、用v-for更新已渲染过的元素列表时,它默认使用“就地复用”的测略。如果数据项的顺序发生了改变,vue不会移动DOM元素来匹配数据的顺序,而是简单复用此处的每个元素。因此通过每个列表提供一个key值,来以便vue跟踪元素的身份,从而高效的实现复用。这个时候key的作用是为了高效的更新渲染虚拟DOM

vue 常用事件修饰符

1
2
3
4
5
6
7
.stop 阻止点击事件冒泡

.prevent 阻止默认事件

.once 只执行一次

.self 只在元素本身触发

如何让CSS只在当前组件中起作用?

1
在组件中的style前面加上scoped

keep-alive的作用是什么?

1
keep-alive 是 Vue 内置的一个组件,可以使被包含的组件保留状态,或避免重新渲染  (后台管理系统的 面包屑)

keep-alive 是 Vue 内置的一个组件,可以使被包含的组件保留状态,避免重新渲染 ,其有以下特性:

一般结合路由和动态组件一起使用,用于缓存组件;
提供 include 和 exclude 属性,两者都支持字符串或正则表达式, include 表示只有名称匹配的组件会被缓存,exclude 表示任何名称匹配的组件都不会被缓存 ,其中 exclude 的优先级比 include 高;
对应两个钩子函数 activated 和 deactivated ,当组件被激活时,触发钩子函数 activated,当组件被移除时,触发钩子函数 deactivated

说出几种vue当中的指令和它的用法?

1
2
3
4
v-model双向数据绑定;
v-for循环;
v-if v-show 显示与隐藏;
v-on事件;v-once: 只绑定一次。

vue-loader是什么?使用它的用途有哪些?

1
2
3
vue文件的一个加载器,将template/js/style转换成js模块。

用途:js可以写es6、style样式可以scss或less、template可以加jade等

请说出vue.cli项目中src目录每个文件夹和文件的用法?

1
2
3
4
5
assets文件夹是放静态资源;
components是放组件;
router是定义路由相关的配置;
app.vue是一个应用主组件;
main.js是入口文件。

*assets和static的区别

1
2
3
4
5
相同点:assets和static两个都是存放静态资源文件。

不相同点:assets中存放的静态资源文件在项目打包时,会将assets中放置的静态资源文件进行打包上传,而压缩后的静态资源文件最终也都会放置在static文件中跟着index.html一同上传至服务器。static中放置的静态资源文件就不会要走打包压缩格式化等流程,而是直接进入打包好的目录,直接上传至服务器。因为避免了压缩直接进行上传,在打包时会提高一定的效率,但是static中的资源文件由于没有进行压缩等操作,所以文件的体积也就相对于assets中打包后的文件提交较大点。在服务器中就会占据更大的空间。

建议:将项目中template需要的样式文件js文件等都可以放置在assets中,走打包这一流程。减少体积。而项目中引入的第三方的资源文件如iconfoont.css等文件可以放置在static中,因为这些引入的第三方文件已经经过处理,我们不再需要处理,直接上传

vue-router 是什么?它有哪些组件

1
vue用来写路由一个插件。router-link、router-view

vue路由跳转方式有哪些

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
1、用“<router-link :to="{..}">”语句;

1). 不带参数
<router-link :to="{name:'home'}">
<router-link :to="{path:'/home'}"> //name,path都行, 建议用name
// 注意:router-link中链接如果是'/'开始就是从根路由开始,如果开始不带'/',则从当前路由开始。

2).带参数
<router-link :to="{name:'home', params: {id:1}}">
// params传参数 (类似post)
// 路由配置 path: "/home/:id" 或者 path: "/home:id"
// 不配置path ,第一次可请求,刷新页面id会消失
// 配置path,刷新页面id会保留
// html 取参 $route.params.id
// script 取参 this.$route.params.id


<router-link :to="{name:'home', query: {id:1}}">
// query传参数 (类似get,url后面会显示参数)
// 路由可不配置
// html 取参 $route.query.id
// script 取参 this.$route.query.id

2、用“this.$router.push()”(函数里面调用)语句;

1). 不带参数
this.$router.push('/home')
this.$router.push({name:'home'})
this.$router.push({path:'/home'})

2). query传参
this.$router.push({name:'home',query: {id:'1'}})
this.$router.push({path:'/home',query: {id:'1'}})

// html 取参 $route.query.id
// script 取参 this.$route.query.id

3). params传参
this.$router.push({name:'home',params: {id:'1'}}) // 只能用 name

// 路由配置 path: "/home/:id" 或者 path: "/home:id" ,
// 不配置path ,第一次可请求,刷新页面id会消失
// 配置path,刷新页面id会保留
// html 取参 $route.params.id
// script 取参 this.$route.params.id

4). query和params区别

query类似 get, 跳转之后页面 url后面会拼接参数,类似?id=1, 非重要性的可以这样传, 密码之类还是用params刷新页面id还在
params类似 post, 跳转之后页面 url后面不会拼接参数 , 但是刷新页面id 会消失

3、用“this.$router.replace()”(用法同上,push)语句;


4、用“this.$router.go(n)”语句
this.$router.go(n)
向前或者向后跳转n个页面,n可为正整数或负整数


ps : 区别

this.$router.push
跳转到指定url路径,并想history栈中添加一个记录,点击后退会返回到上一个页面

this.$router.replace
跳转到指定url路径,但是history栈中不会有记录,点击返回会跳转到上上个页面 (就是直接替换了当前页面)

this.$router.go(n)
向前或者向后跳转n个页面,n可为正整数或负整数

params和query的区别

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
用法:
query要用path来引入,
params要用name来引入,

接收参数都是类似的,分别是

this.$route.query.name 和
this.$route.params.name。

url地址显示:
query更加类似于我们ajax中get传参,
params则类似于post,说的再简单一点,前者在浏览器地址栏中显示参数,后者则不显示

注意点: query刷新不会丢失query里面的数据
params刷新 会 丢失 params里面的数据

怎么定义 vue-router 的动态路由? 怎么获取传过来的值?

1
在router目录下的index.js文件中,对path属性加上/:id。 使用router对象的params.id

active-class 是哪个组件的属性?

1
2
3
4
5
6
7
8
9
vue-router模块的router-link组件。当routerlink标签被点击时将会应用这个样式

在使用时会有一个Bug
首页的active会一直被应用
解决办法
为了解决上面的问题,还需加入一个属性exact,类似也有两种方式:
在router-link中写入exact

<router-link to='/' active-class="active" exact>首页</router-link>

vue-router的两种模式

1
2
hash模式: 即地址栏 URL 中的 # 符号;
history模式

vue-router实现路由懒加载( 动态加载路由 )

1
2
3
4
5
6
7
三种方式
第一种:vue异步组件技术 ==== 异步加载,vue-router配置路由 , 使用vue的异步组件技术 , 可以实现按需加载 .但是,这种情况下一个组件生成一个js文件。

第二种:路由懒加载(使用import)。

第三种:webpack提供的require.ensure(),vue-router配置路由,使用webpack的require.ensure技术,也可以实现按需加载。这种情况下,多个路由指定相同的chunkName,会合并打包成一个js文件

引进组件的步骤

1
2
3
在template中引入组件;
在script的第一行用import引入路径;
用component中写上组件名称

SPA首屏加载慢如何解决

1
2
3
4
5
6
7
1、路由懒加载
2、组件库局部引用
3、节流防抖
4、提升代码复用,封装自定义组件
5、预处理器,预加载
6、图片不放本地,放服务器存储,后端返回网址访问
7、使用CDN资源

vue slot

1
简单来说,假如父组件需要在子组件内放一些DOM,那么这些DOM是显示、不显示、在哪个地方显示、如何显示,就是slot分发负责的活

你们vue项目是打包了一个js文件,一个css文件,还是有多个文件

1
根据vue-cli脚手架规范,一个js文件,一个CSS文件

Vue里面router-link在电脑上有用,在安卓上没反应怎么解决

1
Vue路由在Android机上有问题,babel问题,安装babel polypill插件解决

Vue2中注册在router-link上事件无效解决方法

1
使用@click.native。原因:router-link会阻止click事件,.native指直接监听一个原生事件

RouterLink在IE和Firefox中不起作用(路由不跳转)的问题

1
2
方法一:只用a标签,不适用button标签;
方法二:使用button标签和Router.navigate方法

请说下封装 vue 组件的过程

1
2
3
4
1. 建立组件的模板,先把架子搭起来,写写样式,考虑好组件的基本逻辑。(os:思考1小时,码码10分钟,程序猿的准则。)
2. 准备好组件的数据输入。即分析好逻辑,定好 props 里面的数据、类型。
3. 准备好组件的数据输出。即根据组件逻辑,做好要暴露出来的方法。
4. 封装完毕了,直接调用即可

vue初始化页面闪动问题

1
2
3
4
5
6
使用vue开发时,在vue初始化之前,由于div是不归vue管的,所以我们写的代码在还没有解析的情况下会容易出现花屏现象,看到类似于{{message}}的字样,虽然一般情况下这个时间很短暂,但是我们还是有必要让解决这个问题的。
首先:

在css里加上[v-cloak] {display: none;}。 //用于 防止闪屏,防止页面出现{{ }}

如果没有彻底解决问题,则在根元素加上style="display: none;" :style="{display: 'block'}"

vue修改打包后静态资源路径的修改

1
2
3
4
5
6
7
8
9
10
11
12
cli2版本:将 config/index.js 里的 assetsPublicPath 的值改为 './'
build: {
...
assetsPublicPath: './',
...
}

cli3版本:在根目录下新建vue.config.js 文件,然后加上以下内容:(如果已经有此文件就直接修改)
module.exports = {
publicPath: '', // 相对于 HTML 页面(目录相同)

}

vue项目中用v-for循环本地图片,图片不显示,解决办法

1
2
使用require动态引入图片,或者将图片放入static文件夹里面
<img v-bind:src='require(item.imgurl())' >

v-on可以监听多个方法吗?

1
2
可以
栗子:<input type="text" v-on="{ input:onInput,focus:onFocus,blur:onBlur, }">

vue中 async与await的使用

1
2
VUE中,我们时常会遇到一种实际应用情况:B处的渲染需要依赖A处的接口返回,但是由于种种原因,不能将B直接放在A接口返回后的代码处理里面。这时候我们就需要使用async/await阻塞进程,告诉B,等A有返回后你再执行

async和await是如何处理异步任务的

简单说,async是通过Promise包装异步任务

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
//比如有如下代码
async function async1() {
await async2()
console.log('async1 end')
}
async function async2() {
console.log('async2 end')
}
async1()

//改为ES5的写法
new Promise((resolve, reject) => {
// console.log('async2 end')
async2()
...
}).then(() => {
// 执行async1()函数await之后的语句
console.log('async1 end')
})

prominse的三种状态分别是什么

1
2
3
4
5
6
7
8
9
  pedding 等待
resolve 成功 成功执行.then回调
reject 失败 失败执行.catch回调

.finall无论成功或者失败都执行回调
两个常用方法 prominse.all() 同时拿到几个请求数据 进行对应操作
prominse.race() 赛跑机制,取得一个最先拿到的数据
[场景](https://blog.csdn.net/web2022050901/article/details/125182909)

父组件可以监听到子组件的生命周期吗?

比如有父组件 Parent 和子组件 Child,如果父组件监听到子组件挂载 mounted 就做一些逻辑处理,可以通过以下写法实现:

1
2
3
4
5
6
7
// Parent.vue
<Child @mounted="doSomething"/>

// Child.vue
mounted() {
this.$emit("mounted");
}

以上需要手动通过 $emit 触发父组件的事件,更简单的方式可以在父组件引用子组件时通过 @hook 来监听即可,如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//  Parent.vue
<Child @hook:mounted="doSomething" ></Child>

doSomething() {
console.log('父组件监听到 mounted 钩子函数 ...');
},

// Child.vue
mounted(){
console.log('子组件触发 mounted 钩子函数 ...');
},

// 以上输出顺序为:
// 子组件触发 mounted 钩子函数 ...
// 父组件监听到 mounted 钩子函数 ...

当然 @hook 方法不仅仅是可以监听 mounted,其它的生命周期事件,例如:created,updated 等都可以监听。

混入 (mixin)

将组件的公共逻辑或者配置提取出来,哪个组件需要用到时,直接将提取的这部分混入到组件内部即可。这样既可以减少代码冗余度,也可以让后期维护起来更加容易。

这里需要注意的是:提取的是逻辑或配置,而不是HTML代码和CSS代码。其实大家也可以换一种想法,mixin就是组件中的组件,Vue组件化让我们的代码复用性更高,那么组件与组件之间还有重复部分,我们使用Mixin在抽离一遍

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
//局部混入

//mixin.js文件
export default {
data(){
return {
name: 'mixin'
}
},
created(){
this.hello()
console.log("我是mixin里面的created!")
},
methods: {
hello: () =>{
console.log('hello from mixin!');
}
}
}

//组件
<template>
<p>这是文字</p>
</template>

<script>
import mixin from '@/views/common/mixin' //导入混入(mixin)
export default {
mixins: [mixin] //使用混入(mixin)
}
</script>


//全局混入
混入也可以进行全局注册。使用时格外小心!一旦使用全局混入,它将影响每一个之后创建的 Vue 实例。使用恰当时,这可以用来为自定义选项注入处理逻辑

在main.js中通过Vue.mixin()引入混入对象即可全局使用(作用于该Vue实例下的所有组件)
import mixin from './mixins';
Vue.mixin(mixin)

Mixin和Vuex的区别

1
2
3
4
5
上面一点说Mixin就是一个抽离公共部分的作用。在Vue中,Vuex状态管理似乎也是做的这一件事,它也是将组件之间可能共享的数据抽离出来。两者看似一样,实则还是有细微的区别,区别如下:

Vuex公共状态管理,如果在一个组件中更改了Vuex中的某个数据,那么其它所有引用了Vuex中该数据的组件也会跟着变化。

Mixin中的数据和方法都是独立的,组件之间使用后是互相不影响的

使用过 Vue SSR 吗?说说 SSR ?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
客户端渲染vs服务端渲染
客户端渲染我们叫做CSR渲染方式,服务端渲染我们叫做SSR渲染

什么是服务器端渲染?
server side render 前端页面的产生是由服务器端生成的,我们就称之为服务端渲染。

什么是客户端渲染?
client side render 服务端只提供json格式的数据,渲染成什么样子由客户端通过js控制

运行架构对比:
CSR执行流程:浏览器加载html文件 -> 浏览器下载js文件 -> 浏览器运行vue代码 -> 渲染页面
SSR执行流程:浏览器加载html文件 -> 服务端装填好内容 -> 返回浏览器渲染

开发模式对比
CSR:前后端通过接口JSON数据进行通信,各自开发互不影响
SSR:前后端分工搭配复杂,前端需要写好html模板交给后端,后端装填模板内容返给浏览器


What?SSR是什么?
SSR全拼是Server-Side Rendering,服务端渲染。
所谓服务端渲染,指的是把vue组件在服务器端渲染为组装好的HTML字符串,然后将它们直接发送到浏览器,最后需要将这些静态标记混合在客户端上完全可交互的应用程序。

Why?为什么选择SSR?
①满足seo需求,传统的spa数据都是异步加载的,爬虫引擎无法加载,需要利用ssr将数据直出渲染在页面源代码中。
②更宽的内容达到时间(首屏加载更快),当请求页面的时候,服务端渲染完数据之后,把渲染好的页面直接发送给浏览器,并进行渲染。浏览器只需要解析html不需要去解析js。

详解:
对SEO有利:其实爬虫爬你的页面是件好事,因为有些页面爬虫不支持执行JavaScript的,这不支持实现JavaScript并不是说SSR我的页面会是空的HTML页面,而有了SSR以后,这些抓取工具就可以得到完整的HTML结构化数据,然后被纳入搜索引擎。
更短的白屏时间:相对于客户端渲染,服务器渲染是在浏览器URL中请求的,之后我们得到了一个HTML文本,浏览器只需要解析HTML,构建...直接DOM只是一棵树。而客户端渲染,需要得到一个空的一个第一个HTML页面,此时页面已经进入白屏,之后需要加载并执行JavaScript、请求后端服务器获取数据、JavaScript渲染页面几次才能看到最后一页。特别是在复杂的应用程序中,由于需要加载JavaScript脚本,应用程序越复杂,需要加载JavaScript的脚本越多,它就越大,这导致应用程序的首屏加载时间很长,并且降低了体验感。

在vue2和vue3里使用ssr有什么区别?
vue2时代做ssr一般使用vue2+vue-server-renderer可以实现SSR功能,但是vue3中会报错;

vue3可以不使用插件就实现SSR,就是原生支持

Vue SSR服务端渲染的使用场景有哪些?
1.SEO需求
SEO(Search Engine Optimization,搜索引擎优化),是一种利用搜索引擎规则,提高网站在搜索引擎内自然排名的技术。通常这需要页面内容在页面加载完成时便已经存在。

前后端分离的纯前端项目,由于这类项目需要页面加载完成后再异步获取数据渲染,因此大部分搜索引擎无法获取到这类项目的内容。Vue SSR正是基于此类需求而给出的一种技术方案。利用nodejs搭建页面渲染服务,在服务端完成之前需要在客户端完成的页面渲染工作,输出给SEO更友好的页面。

2.首屏渲染速度
目前对于首屏渲染速度的提升有许多方案,在ssr之外还有龙骨,墓碑,数据直出。相比于这些方案ssr方案实现是最复杂的,但效果也是最好的。


SSR怎么做:开箱即用的SSR脚手架

目前前端流行的三种技术栈 React, Vue 和 Angula ,已经孵化出对应的服务端渲染框架,开箱即用

React: Next.js

Vue: Nuxt.js

Angula: Nest.js

vue , 微信小程序 , uni-app属性的绑定

1
2
3
vue和uni-app动态绑定一个变量的值为元素的某个属性的时候,会在属性前面加上冒号":";

小程序 绑定某个变量的值为元素属性时,会用两个大括号{{}}括起来,如果不加括号,为被认为是字符串。

vue , 微信小程序 , uni-app的页面生命周期函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
vue:
beforeCreate(创建前)
created(创建后)
beforeMount(载入前,挂载)
mounted(载入后)
beforeUpdate(更新前)
updated(更新后)
beforeDestroy(销毁前)
destroyed(销毁后)
小程序/uni-app:
1. onLoad:首次进入页面加载时触发,可以在 onLoad 的参数中获取打开当前页面路径中的参数。
2. onShow:加载完成后、后台切到前台或重新进入页面时触发
3. onReady:页面首次渲染完成时触发
4. onHide:从前台切到后台或进入其他页面触发
5. onUnload:页面卸载时触发
6. onPullDownRefresh:监听用户下拉动作
7. onReachBottom:页面上拉触底事件的处理函数
8. onShareAppMessage:用户点击右上角转发

vue、小程序、uni-app中的本地数据存储和接收

1
2
3
4
5
6
7
8
9
10
11
12
13
vue:
存储:localstorage.setItem(‘key’,‘value’)
接收:localstorage.getItem(‘key’)

微信小程序:
存储:通过wx.setStorage/wx.setStorageSync写数据到缓存
接收:通过wx.getStorage/wx.getStorageSync读取本地缓存,

uni-app:
存储:uni.setStorage({key:“属性名”,data:“值”}) //异步
uni.setStorageSync(KEY,DATA) //同步
接收:uni.getStorage({key:“属性名”,success(res){res.data}}) //异步
uni.getStorageSync(KEY) //同步

移动端性能优化?

1
2
3
4
5
6
7
8
9
尽量使用css3动画,开启硬件加速
适当使用touch时间代替click时间
避免使用css3渐变阴影效果
可以用transform: translateZ(0) 来开启硬件加速
不滥用float。float在渲染时计算量比较大,尽量减少使用
不滥用web字体。web字体需要下载,解析,重绘当前页面
合理使用requestAnimationFrame动画代替setTimeout
css中的属性(css3 transitions、css3 3D transforms、opacity、webGL、video)会触发GUP渲染,耗电

uni-app开发中遇到的问题

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
1、跳转底部四个tabbar页面,是不能使用uni.navigateTo()的,这样无法跳转,必须使用uni.switchTab()

2、我一开始是游客模式进的主页,然后在触发登录进行登录操作后,再次使用uni.switchTab()跳转到了主页,这个时候,如果我们有些操作是写在created()生命周期里面的,比如网络请求这些,就不会触发,可能会导致一些问题。那如果我想再次进入主页后,还想进行页面的网络数据刷新,该怎么操作呢?这时候就可以使用window.location.href(),window.location.href = 主页地址,这时候就会发出相应的生命周期函数

3、判断手机系统
let osName = plus.os.name.toLowerCase(); // ios或android

4、页面通信
// 跳转页面并发送数据
uni.navigateTo({
url:"/pages/chat/customerService",
success(res) {
res.eventChannel.emit("RoomToCustomerService",{
roomNumber: that.roomAllInfo.roomNumber,
roomId: that.roomID
});
}
});

// 在被跳转的页面 接收传递来的数据
let eventChannel = that.getOpenerEventChannel();
eventChannel.on("RoomToCustomerService",function(res){
that.gameRoomInfo = res;
});


5、调用子组件方法
首先给组件定义一个ref属性(ref=”msgInput”),然后使用 this.$refs.msgInput.function();
<!-- 定义 -->
<chatInput ref="msgInput"></chatInput>

// 使用
this.$refs.msgInput.closeFunction();

6、日期格式问题,Data.parse()时间转化为时间戳出现NaN,但在PC正常
你需要将-替换为/

7、列表页获取数据的时候,页面会闪一下
可能是页面里面存在图片,图片只定义了宽度,没有定义高度
解决:给图片定义高度

8、在页面调用uni.scanCode识别普通二维码再进来小程序该页面,会再执行一次onShow,也就是onShow会执行两次
记得有需要数据回显的页面,请求数据的时候一定不要放在onShow,否则很有可能会导致新更改的数据,被回显的数据覆盖

9、H5与原生app交互遇到的坑
H5采用前后端分离的模式开发,H5调用接口为http协议,而原生app打开H5使用的是https协议,这样的情况,会造成H5无法进行API请求,简单理解就是https协议下,加载http协议资源会被拦截

H5请求接口,始终要注意跨域问题。最简单的处理方式,就是api端使用Access-Control-Allow-Origin:*(或者指定域名),API端开放特定域名使用资源

分享过程中,如果使用到了app的原生微信分享功能,需要特别注意字段的限制,其中最常见的就是图片的限制,二进制大小32k以内



10、uniapp开发安卓和ios一些不一样的地方
//底部距离不一样
比如购物车全选按钮的操作栏,他是跟下面的tabbar挨着的,没有间距。
假设我下面abbar的高度为100rpx,如果我把全选按钮操作栏的固定定位距离底部的距离写成bottom:100rpx,这样的话,在安卓端显示是可以达到我们的预期效果,但是在ios上,全选按钮操作栏就会有部分内容被tabbar遮盖,所以我们遇到这种情况的时候,要判断设备类型,然后加上对应样式

//复制操作
uni.setClipboardData(),uniapp提供的一种复制操作,很方便,但是安卓可以,ios不行,所以最好还是使用vue的v-clipboard指令

//微信JSSDK的自定义页面分享
也是一样的,安卓可以成功,IOS不行

11、App平台IOS端软键盘上方横条去除方案(禁用键盘上的^)

在 pages.json 中配置 style配置

"app-plus": {
"softinputNavBar": "none"
}

12、navigateTo, redirectTo 只能打开非 tabBar 页面,switchTab 只能打开 tabBar 页面

13、页面结构复杂,css样式太多的情况,使用 image 可能导致样式生效较慢,出现 “闪一下” 的情况,此时设置 image{will-change: transform} ,可优化此问题

14、在字体或高度中使用了 rpx ,那么需注意这样的写法意味着随着屏幕变宽,字体会变大、高度会变大。如果你需要固定高度,则应该使用 px

15、H5端页面刷新之后页面栈会消失,此时navigateBack不能返回,如果一定要返回可以使用history.back()导航到浏览器的其他历史记录

16、tabbar 的页面展现过一次后就保留在内存中,再次切换 tabbar 页面,只会触发每个页面的onShow,不会再触发onLoad

17、如需调节checkbox,radio⼤⼩,可通过css的scale⽅法调节,如缩⼩到70%style="transform:scale(0.7)"

18、在小程序端 font-weight:bold ,要写bold,数值苹果不支持,还有就是文字颜色不能写rgba小程序苹果不支持会不显示

19、小程序真机调试包太大,无法上传,分包处理
1 将所有静态资源都存放到远程服务器上
2 采用分包的方式,将主包的体积降下来

20、小程序分享配置
微信小程序的分享不支持API调用,只能用户主动点击触发分享。可通过右上角的胶囊和 button 按钮分享给微信好友。开启右上角胶囊分享功能需在页面中加入 onShareAppMessage 生命周期函数,在onShareAppMessage 中还可监听到是哪种方式触发的分享

onShareAppMessage(res) {
if (res.from === 'button') {// 来自页面内分享按钮
console.log("按钮分享")
} else if (res.from === 'menu') {// 来自页面右上角胶囊
console.log("胶囊分享")
}
}
分享携带的参数可通过以下方式获取
在 App onShow中
在App.vue 中的 onShow 中也能获取到分享携带的参数
无论是以何种方式进入小程序,都会进入 App.vue 中的 onShow,所以在这里是一定可以获取到分享携带的参数的
扫码进入同点击分享链接进入一样,都可在对应的生命周期中获取
onShow: function(option) {
console.log('====App onShow====')
console.log(option)
console.log('====App onShow====')
},
21

Vuex类

vuex是什么?怎么使用?哪种功能场景使用它?

1
2
3
4
5
Vuex 是Vue框架开发的状态管理库,它使得在应用程序中管理共享状态变得更加容易和可维护

在main.js引入store,注入。新建了一个目录store.js,…… export

场景有:单页应用中,组件之间的状态。音乐播放、登录状态、加入购物车

vuex有哪几种属性?

1
2
3
4
5
6
7
8
9
state:基本数据(数据源存放地)
this.$store.state.count
getter:从基本数据派生出来的数据
this.$store.getters.show
mutations:唯一修改state的方法,修改过程是同步的
this.$store.commit('inc', 2)
action:像一个装饰器,包裹mutations,使之可以异步
this.$store.dispatch('',arg)
Module:模块化Vuex,可以让每一个模块拥有自己的state、mutation、action、getters,使得结构非常清晰,方便管理

你有使用过vuex的module吗?主要是在什么场景下使用?

1
把状态全部集中在状态树上,非常难以维护。按模块分成多个module,状态树延伸多个分支,模块的状态内聚,主枝干放全局共享状态

在组件中怎么访问Vuex模块中的getter和state,怎么提交mutation和action?

1
2
3
4
5
6
7
8
通过 this.$store.getters / this.$store.state 来访问模块中的getter和state。

通过(同步操作) this.$store.commit('mutations方法名',值)提交模块中的mutation。

通过(异步操作) this.$store.dispatch('actions方法名,值')提交模块中的action。



vue.js中ajax请求代码应该写在组件的methods中还是vuex的actions中?

1
2
3
如果请求来的数据是不是要被其他组件公用,仅仅在请求的组件内使用,就不需要放入vuex 的state里。

如果被其他地方复用,这个很大几率上是需要的,如果需要,请将请求放入action里,方便复用

Vuex 是通过什么方式提供响应式数据的?

1
2
3
在 Store 构造函数中通过 new Vue({}) 实现的。
利用 Vue 来监听 state 下的数据变化,
给状态(数据)添加 getter、setter可以监听数据改变

Vuex 如何区分 state 是外部直接修改,还是通过 mutation 方法修改的?

1
2
在vuex底层会有一个committing变量,初始值false;当通过mutation方法修改数据时
把committing变量变成true;如果是直接改变的变量则不改变committing变量

页面刷新后vuex的state数据丢失怎么解决

1
2
3
放在localStorage 或者sessionStorage中 ,或者借用辅助插vuex-persistedstate。

vuex-persistedstate的createPersistedState()方法

怎么在组件中批量使用Vuex的state状态?

1
2
3
4
5
6
7
8
//使用mapState辅助函数, 利用对象展开运算符将state混入computed对象中

import {mapState} from 'vuex'
export default{
computed:{
...mapState(['price','number'])
}
}

怎么监听vuex数据的变化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
第一种方案 使用computed和watch监听vuex数据变化,可以在组件中通过组件的 watch方法来做, 因为组件可以将state数据映射到 组件的计算属性上,

然后 监听 映射的计算属性即可 代码如下

// vuex中的state数据
state: {
count: 0
},

// A组件中映射 state数据到计算属性
computed: {
// this.$store.state.count
// mapState 把全局 count 变成 可以直接使用的 数据
...mapState(['count'])
}
// A组件监听 count计算属性的变化
watch: {
// watch 可以监听 data 数据 也可以监听 全局 vuex数据
count () {
// 用本身的数据进行一下计数
this.changeCount++
}
}


第二种方案 vuex中store对象本身提供了watch函数 ,可以利用该函数进行监听
watch(fn: Function, callback: Function, options?: Object): Function
响应式地侦听 fn 的返回值,当值改变时调用回调函数。fn 接收 store 的 state 作为第一个参数,其 getter 作为第二个参数。最后接收一个可选的对象参数表示 Vue 的 vm.$watch 方法的参数
created () {
this.$store.watch((state, getters) => {
return state.count
}, () => {
this.changeCount++
})
}

在v-model上怎么用Vuex中state的值?

1
2
3
4
5
6
7
8
9
10
11
12
13
//需要通过computed计算属性来转换
<input v-model="message">
// ...
computed: {
message: {
get () {
return this.$store.state.message
},
set (value) {
this.$store.commit('updateMessage', value)
}
}
}

Vue3

Vue3 比 vue2 优势

1
2
3
4
5
6
7
1. 性能更好
2. 体积更小
3. 更好的ts支持
4. 更好的代码组织
5. 更好的逻辑抽离
6. 更多新的功能

Vue3 升级了哪些重要的功能?

1
2
3
4
5
6
7
8
9
10
11
12
1. createApp:创建vue实例的方式
2. emits属性,组件中的事件要先使用emits进行声明,然后在setup的形参引入
3. 生命周期
4. 多事件
5. fragment:不再限制唯一根节点
6. 移除.sync:
7. 异步组件的写法:Vue2 直接import进来,Vue3需要使用defineAsyncComponent包裹一层
8. 移除filter
9. teleport:把组件直接to到某个dom
10. suspense:fallback,就是一个具名插槽
11. composition API (组合API)

Vue3 生命周期

1
2
3
4
1. Options API生命周期
beforeCreate => created => beforeMount => mounted => beforeUpdate => updated => beforeUnmount => unmounted
2. Composition API生命周期
setup() => setup() => onBeforeMount => onMounted => onBeforeUpdate => onUpdated => onBeforeUnmount => onUnmounted

如何看待composition API(组合API) 和options API(选项API)

1
2
3
4
5
6
1. Composition API 更好的代码组织,更好的逻辑复用,更好的类型推到
2. 小型项目,业务逻辑简单,用Options API
3. 中大型项目,业务逻辑复杂的,用Composition API
4. Composition API 是为了解决复杂业务逻辑而设计的
5. 类似React Hooks

Options API 存在的问题是什么?Composition API 的优势有哪些?

1
2
3
4
5
1.Options API 存在的问题
使用传统OptionsAPI中,新增或者修改一个需求,就需要分别在data,methods,computed里修改 。

2.Composition API 的优势
我们可以更加优雅的组织我们的代码,函数。让相关功能的代码更加有序的组织在一起。

如何理解ref toRef和toRefs?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
1. ref
1. 生成值类型的响应式数据
2. 可用于模板和reactive
3. 通过.value修改值
2. toRef
1. 针对一个响应式对象(reactive封装)的prop
2. 创建一个ref,具有响应式
3. 两者保持引用关系
3. toRefs,避免模板中导出都是state
1. 将响应式对象(reactive封装)转换成普通对象
2. 对象的每个prop都是对应的ref
3. 两者保持引用关系
4. 最佳使用方式
1. 用reactive做对象的响应式,用ref做值类型的响应式
2. setup中返回toRefs(state),或者toRef(state, 'prop')
3. ref的变量命名都用xxxRef
4. 合成函数返回响应式对象时,用toRefs

ref toRef toRefs进阶,深入理解

1
2
3
4
5
6
7
8
9
10
11
12
13
1. 为何需要ref?
1. 返回值类型,会丢失响应式
2. setup、computed、合成函数,都有可能返回值类型
3. Vue如果不定义ref,用户将自定义ref,反而混乱
2. 为何需要.value?
1. ref是一个对象(不丢失响应式),value存储值
2. 通过.value属性的get和set实现响应式
3. 用于模板、reactive时,不需要.value,其他情况都需要
3. 为何需要toRef toRefs
1. 初衷:不丢失响应式的情况下,把对象数据进行分解和扩散
2. 前提:针对的事响应式对象,不是普通对象
3. 注意:不创造响应式,而是延续响应式

Composition API如何实现代码逻辑复用?

1
2
3
4
1. 抽离逻辑代码到一个函数
2. 函数命名约定为useXXX格式(React Hooks也是)
3. 在setup中引用useXXX函数

watch和watchEffect的区别是什么?

1
2
3
4
1. 二者都可以监听属性变化
2. watch需要明确监听哪个属性
3. watchEffect会根据其中的属性,自动监听其变化

setup中如何获取组件实例?

1
2
3
4
1. setup和其他Composition API中都没有this
2. 在Options API中仍然可以使用this
3. Composition API中可以使用getCurrentInstance方法获取

Vite是什么?

1
2
3
4
1. 前端打包工具
2. 在开发环境下,使用ES6 Module,不打包,启动快
3. 生产环境打包使用rollup

Composition API和React Hooks的对比

1
2
3
4
5
1. 前者setup(生命周期create)只会被调用一次,后者函数会被多次调用
2. 前者无需useMemo,useCallback,因为setup只调用一次
3. 前者无需考虑调用顺序,后者需要保证hooks的顺序一致
4. 前者reactive + ref 比后者的useState,要难理解

Vue3为何比Vue2快?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
1. Proxy实现响应式
2. patchFlag https://vue-next-template-explorer.netlify.app/
1. 编译模板时,动态节点做标记
2. 标记,分为不同的类型,如TEXT,PROPS
3. diff时,区分静态节点和不同类型的动态节点
3. hoistStatic
1. 将静态节点的定义,提升到父作用域,缓存起来,空间换时间
2. 多个相邻的静态节点,会被合并起来,编译优化
4. cacheHandler
缓存事件
5. SSR优化
静态节点直接输出为dom,绕过vdom
6. tree-shaking
编译时,按需引入API

vue3为什么要用proxy实现双向绑定?

1
2
3
4
5
6
7
8
9
10
11
12
object.defineProperty的缺点:

1.因为es5的object.defineProperty无法监听对象属性的删除和添加
2.不能监听数组的变化,除了push/pop/shift/unshift/splice/spObject.definert/reverse,其他都不行
3.Object.defineProperty只能遍历对象属性直接修改(需要深拷贝进行修改)

proxy的优点:

1.直接监听对象而非属性
2.直接监听数组的变化
3.拦截的方式有很多种(有13种,set,get,has)
4.Proxy返回一个新对象,可以操作新对象达到目的

vue2与vue3的区别

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
1、 vue2和vue3响应式原理发生了改变
vue2 的响应式原理是利⽤es5 的⼀个 API ,Object.defineProperty()对数据进⾏劫持结合发布订阅模式的⽅式来实现的。

vue3 中使⽤了 es6 的 proxy API 对数据代理,通过 reactive() 函数给每⼀个对象都包⼀层 Proxy,通过 Proxy 监听属性的变化,从⽽ 实现对数据的监控

2、Vue2使⽤的是选项API(Options API),Vue3使⽤的是组合API(Composition API)

3、实例化不同
vue2中new出的实例对象,所有的东西都在这个vue对象上,这样其实⽆论你⽤到还是没⽤到,都会跑⼀遍,这样不仅提⾼了性能消耗,也⽆疑增加了⽤户加载时间。

vue3中可以⽤ES module imports按需引⼊,如:keep-alive内置组件、v-model指令,等等,不仅我们开发起来更加的便捷,减少 了内存消耗,也同时减少了⽤户加载时间,优化⽤户体验

4、生命周期对比
前者为vue2 后者为vue3
beforeCreate -> 使用 setup()
created -> 使用 setup()
beforeMount -> onBeforeMount
mounted -> onMounted
beforeUpdate -> onBeforeUpdate
updated -> onUpdated
beforeDestroy -> onBeforeUnmount
destroyed -> onUnmounted
errorCaptured -> onErrorCaptured

Object.defineProperty 和 Proxy 的区别

1
2
3
4
5
6
7
1.Proxy 可以直接监听对象而非属性;
2.Proxy 可以直接监听数组的变化;
3.Proxy 有多达 13 种拦截方法,不限于 apply、ownKeys、deleteProperty、has 等等是 Object.defineProperty 不具备的
4.Proxy 返回的是一个新对象,我们可以只操作新的对象达到目的,而Object.defineProperty 只能遍历对象属性直接修改
5.Proxy 作为新标准将受到浏览器厂商重点持续的性能优化,也就是传说中的新标准的性能红利
6.Object.defineProperty 兼容性好,支持 IE9,而 Proxy 的存在浏览器兼容性问题,而且无法用 polyfill 磨平,因此 Vue 的作者才声明需要等到下个大版本( 3.0 )才能用 Proxy 重写

怎么在 watch 监听开始之后立即被调用?

1
在选项参数中指定 immediate: true 将立即以表达式的当前值触发回调

为什么vue3需要对引入的组件使用markRaw?

vue2中 is是通过组件名称切换的,vue3中setup是通过组件实例切换的。直接把组件实例放到reactive中代理,vue会发出警告。告知我们可以通过shallowRef 或者 markRaw 跳过proxy 代理。对组件实例进行响应式代理毫无意义,且浪费性能

markRaw:标记一个对象,使其不能成为一个响应式对象。
toRaw:将响应式对象(由 reactive定义的响应式)转换为普通对象。
shallowRef:只处理基本数据类型的响应式, 不进行对象的响应式处理。
shallowReactive:只处理对象最外层属性的响应式(浅响应式)。

1
2
3
4
5
6
7
8
9
10
11
12
<template>
<component :is="currentComponent"></component>
</template>

<script setup>
import A from '../components/A.vue'
import B from '../components/B.vue'
let tabList = reactive([
{name:'组件A',com:markRaw(A)},
{name:'组件B',com:markRaw(B)},
]);
<script>

前端项目中环境变量怎么处理,怎么配置?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
1)为什么要配置环境变量

在公司,一个项目一般会有开发版本、测试版本、灰度版本和线上版本,每个版本会对应相同或不同的数据库、API地址。为了方便管理,我们通常做成配置文件的形式,根据不同的环境,加载不同的文件。如果手动修改代码中加载配置文件的路径也可以,但是太麻烦,最重要的是很low

2)实现原理

采用nodejs顶层对象中的process.env(进程环境,返回一个包含用户环境信息的对象。)属性,根据各个环境的配置文件区分和切换环境

3)具体操作(以vue项目为例)
1、安装依赖:npm install process
2、在根目录新增五个文件(根据自身情况增减),
.env(默认配置)
VUE_APP_TITLE='dev'

.env.development(本地开发环境配置)
ENV = 'development'

/*ase api*/
VUE_APP_BASE_API="https://xxx"

//在要使用的位置获取:
process.env.VUE_APP_BASE_API

.env.pre(灰度配置)

.env.production(生产配置)
ENV = production

/*ase api*/
VUE_APP_BASE_API="https://xxx"

//在要使用的位置获取:
process.env.VUE_APP_BASE_API

.env.sit(测试配置1)

.env.uat(测试配置2)

(ps: VUE_APP是统一标志,后面的拓展名可以任取)

3、设置项目启动时默认的环境

只需要在项目启动命令后面修改需要的环境就行,例如我这是npm run dev,把–mode dev改成–mode uat就行了
//package.json
"scripts": {
"dev": "vue-cli-service serve --mode dev",
"build": "vue-cli-service build --mode dev",
"lint": "vue-cli-service lint",
"build-sit": "vue-cli-service build --mode sit",
"build-uat": "vue-cli-service build --mode uat",
"build-pre": "vue-cli-service build --mode pre",
"build-prod": "vue-cli-service build --mode prod"
},

4、查看环境是否配置成功
在main.js里打印当前环境,输出就成功了

console.log(process.env.NODE_NEV)
1
2

...补充中
------ The End ------
您的认可是我不断进步的动力!