-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathatom.xml
More file actions
956 lines (923 loc) · 60 KB
/
Copy pathatom.xml
File metadata and controls
956 lines (923 loc) · 60 KB
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
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
<id>https://lcb522.github.io</id>
<title>温良</title>
<updated>2022-12-31T10:22:39.496Z</updated>
<generator>https://github.com/jpmonette/feed</generator>
<link rel="alternate" href="https://lcb522.github.io"/>
<link rel="self" href="https://lcb522.github.io/atom.xml"/>
<subtitle>长路漫漫,你要一直可爱下去</subtitle>
<logo>https://lcb522.github.io/images/avatar.png</logo>
<icon>https://lcb522.github.io/favicon.ico</icon>
<rights>All rights reserved 2022, 温良</rights>
<entry>
<title type="html"><![CDATA[八股文之vue]]></title>
<id>https://lcb522.github.io/post/biao-ti/</id>
<link href="https://lcb522.github.io/post/biao-ti/">
</link>
<updated>2022-12-29T12:40:04.000Z</updated>
<content type="html"><![CDATA[<h3 id="1-你怎么理解指令工作中你封装过自定义指令吗举一些例子">1. 你怎么理解指令?工作中你封装过自定义指令吗?举一些例子</h3>
<ul>
<li>封装好的方法,更加方便直接的操作<code>dom</code>的指令</li>
<li><code>V-debounce</code>使用防抖函数限制规定时间内只能点击一次</li>
</ul>
<pre><code class="language-js">const debounce = {
inserted: function (el, binding) {
let timer
el.addEventListener('click', () => {
if (timer) {
clearTimeout(timer)
}
timer = setTimeout(() => {
binding.value()
}, 1000)
})
},
}
export default debounce
</code></pre>
<h3 id="2-计算属性能不能绑定在v-model上">2. 计算属性能不能绑定在v-model上?</h3>
<ul>
<li>可以</li>
</ul>
<h3 id="3-侦听器能监听哪些变量变化">3. 侦听器能监听哪些变量变化?</h3>
<ul>
<li>data 计算属性 <code>vuex</code>数据 <code>$route</code>(只要这个数据有__ob__都可以被侦听器)</li>
</ul>
<h3 id="4-说一下vue的响应式原理你是如何理解mvvm的">4. 说一下Vue的响应式原理?(你是如何理解MVVM的)</h3>
<ul>
<li>
<p>回答版本一:<br>
当vue组件被创建时,在生命周期的第一阶段,Vue使用Object.defineProperty()对data选项进行遍历劫持并添加get/set钩子;在生命周期第二阶段,指令第一次与声明式变量touch时,发生依赖收集,再调用当前组件的watcher第一次更新DOM,DOM视图就显示出来了。当声明式变量发生变化时,vue再次通知Watcher更新视图,这就是响应式(原理)</p>
</li>
<li>
<p>回答版本二:<br>
当new Vue时,Vue源码中有一个Observer,会对data中所有的数据进行劫持,把data中的数据处理成响应式,每处理一个响应式数据,都有一个Dep与之对应。同时编译模板,编译模板时会找到小胡子语法和指令,处理小胡子语法和指令时,会创建出一个个的Watcher,这些Watcher会存储到对应的Dep中,这个过程叫依赖收集,同时调用对应的Updater,完成对应的页面的初始化。当数据发生变化时,在Observer的setter中会监听到,数据变化,就通知Dep中的Watcher更新视图,每个Watcher都绑定了更新视图的回调函数。</p>
</li>
</ul>
<h3 id="5-你工作中有没有封装比较好的组件">5. 你工作中有没有封装比较好的组件?</h3>
<ul>
<li>分页组件</li>
</ul>
<pre><code class="language-javascript"><template>
<div class="xtx-pagination">
<a href="javascript:;" v-if="myCurrentPage===1" class="disabled">上一页</a>
<a href="javascript:;" v-else @click="go(-1)">上一页</a>
<!-- 如果起点按钮大于2就显示 -->
<span v-if="pageInfo.start>2">...</span>
<a href="javascript:;" @click="changePage(item)" :class="{active: myCurrentPage === item}" v-for="(item,idx) in pageInfo.pager" :key="idx"> {{item}} </a>
<!-- 如果当前终点按钮小于总页数就显示 当终点按钮是20总按钮也是20 就不显示 -->
<span v-if="pageInfo.end < pageInfo.pageCount">...</span>
<!-- 如果当前按钮等于最后一页就禁止点击 disabled -->
<a href="javascript:;" v-if="myCurrentPage==pageInfo.end" class="disabled">下一页</a>
<a href="javascript:;" v-else @click="go(1)">下一页</a>
</div>
</template>
<script>
import { computed, ref, watch } from 'vue'
export default {
name: 'XtxPagination',
props: {
total: { type: Number, default: 100 },
pageSize: { type: Number, default: 10 },
currentPage: { type: Number, default: 1 },
btnCount: { type: Number, default: 5 }
},
setup (props, { emit }) {
const myTotal = ref(100) // 总条数
const myPageSize = ref(5) // 每页共几条
const myCurrentPage = ref(3) // 用户实时点击,修改
const myBtnCount = ref(5) // 分页按钮的个数5个
// 让当前的页码处于正中间
// const pager = ref([1, 2, 3, 4, 5])
// 根据上边信息,实时计算 pager,起始页码,结束页码
const pageInfo = computed(() => {
// 总页数 = 总条数/每页几条
const pageCount = Math.ceil(myTotal.value / myPageSize.value)
// 起点 = 当前页数-总页数/2 举例 3 - Math.floor(5/2) = 1
let start = myCurrentPage.value - Math.floor((myBtnCount.value / 2))
// 终点 = 起点页数 + 总页数 - 1 距离 1 + 5 -1 || 3 + 5 -1
let end = start + myBtnCount.value - 1
// 意外1 当起点小于1
if (start < 1) {
start = 1
// 终点= 当前页数>总页数?总页数 否则 当前页数
end = myBtnCount.value > pageCount ? pageCount : myBtnCount.value
}
// 意外2 当终点大于最大页码
if (end > pageCount) {
end = pageCount
// 起点= 终点+(-所有页数+1)>1?1:= 终点+(-所有页数+1)
start = (end - myBtnCount.value + 1) < 1 ? 1 : (end - myBtnCount.value + 1)
}
const pager = []
for (let i = start; i <= end; i++) {
pager.push(i)
}
return { start, end, pageCount, pager }
})
// 上一页下一页
const go = (step) => {
myCurrentPage.value += step
}
const changePage = (page) => {
// 如果等于现在页页码 保持不动
if (page === myCurrentPage.value) return
myCurrentPage.value = page
emit('currentCahnge', page)
}
// 监听传入的值改变
watch(props, () => {
myTotal.value = props.total
myPageSize.value = props.pageSize
myCurrentPage.value = props.currentPage
myBtnCount.value = props.btnCount
}, { immediate: true })
return { myTotal, myPageSize, myCurrentPage, myBtnCount, pageInfo, go, changePage }
}
}
</script>
<style scoped lang="less">
.xtx-pagination {
display: flex;
justify-content: center;
padding: 30px;
> a {
display: inline-block;
padding: 5px 10px;
border: 1px solid #e4e4e4;
border-radius: 4px;
margin-right: 10px;
&:hover {
color: @xtxColor;
}
&.active {
background: @xtxColor;
color: #fff;
border-color: @xtxColor;
}
&.disabled {
cursor: not-allowed;
opacity: 0.4;
&:hover {
color: #333
}
}
}
> span {
margin-right: 10px;
}
}
</style>
</code></pre>
<h3 id="6-说一下vue的生命周期展开说不仅仅是那几个钩子">6. 说一下Vue的生命周期(展开说,不仅仅是那几个钩子)?</h3>
<ul>
<li>
<p>创建期间的生命周期函数:</p>
<ul>
<li>beforeCreate:实例刚在内存中被创建出来,此时,还没有初始化好data 和 methods 属性</li>
<li>created:实例已经完成了模板的编译,但是还没有挂载到页面中</li>
<li>beforeMount:此时已经完成了模板的翻译,但是还有完全挂载到页面中</li>
<li>mounted:此时,已经将编译好的模板,挂载到了页面指定的容器中显示</li>
</ul>
</li>
<li>
<p>运行期间的生命周期函数:</p>
<ul>
<li>
<p>beforeUpdate:状态更新之前执行此函数,此时 data 中的状态值是最新的,但是界面上显示的数据还是旧的,因为此时还没有开始重新渲染DOM节点</p>
</li>
<li>
<p>updated:实例更新完毕之后调用次函数,此时 data 中的状态值 和 界面上显示的数据,都已经完成了更新,界面已经被重新渲染好了</p>
</li>
</ul>
</li>
<li>
<p>销毁期间的生命周期函数:</p>
<ul>
<li>
<p>beforeDestroy:实例销毁之前调用,在这一步,实例仍然完全可用</p>
<ul>
<li>当执行 beforeDestroy 钩子函数的时候,Vue实例就已经从运行阶段进入到了销毁阶段;当执行 beforeDestroy 的时候,实例身上所有的 data 和所有的 methods, 以及 过滤器、指令、、 都处于可用状态,此时,还没有真正执行销毁的过程</li>
</ul>
</li>
<li>
<p>destroyed:Vue 实例销毁后调用。调用后,vue 实例 指示的所有东西都会解绑,所有的事件监听器会被移除,所有的子实例也会被销毁</p>
</li>
</ul>
</li>
</ul>
<h3 id="7-虚拟dom存在的价值点在哪里">7. 虚拟DOM存在的价值点在哪里?</h3>
<ul>
<li>避免直接操作DOM,提高开发效率</li>
<li>虚拟DOM作为一个中间层可以跨平台,除了在Web平台使用以外,还支持服务端渲染,以及<code>weex</code>框架可以跨移动端平台</li>
<li>提高性能
<ul>
<li>复杂视图情况下提升渲染性能,如果有频繁DOM操作的话,虚拟DOM在更新真实DOM之前,首先会通过<code>Diff</code>算法对比新旧两个DOM树的差异,最终把差异更新到真实DOM上,不会每次操作真实DOM,另外通过给节点设置Key属性,可以让节点重用,避免大量的重绘 (提高性能)</li>
</ul>
</li>
</ul>
<h3 id="8-vue有哪些内置组件vue中实现条件渲染渲染有哪些办法">8. Vue有哪些内置组件?Vue中实现条件渲染渲染有哪些办法?</h3>
<ul>
<li>
<p>五个内置组件(slot,transition,component,keep-alive)</p>
</li>
<li>
<p>条件渲染: v-if v-show 动态组件</p>
</li>
</ul>
<h3 id="9-你有封装过vue插件怎么封装的封装过什么">9. 你有封装过Vue插件?怎么封装的?封装过什么?</h3>
<ul>
<li>在Vue生态中,除了Vue本身,其它所有与Vue相关的第三方包,都是插件,都得以插件的方式进行集成。</li>
<li>作用:是一种更加高级的代码复用技术,可以以插件的方式为我们提供可复用的组件、混入、指令、过滤器、原型链API。</li>
</ul>
<p>两种写法:<br>
第一种写法: const Plugin = { install (Vue) { } }<br>
第二种写法: const Plugin = function (Vue) {}</p>
<p>在项目中我之前二次封装axios,把它封装成一个插件,参考:<br>
https://juejin.cn/post/6844903599764406280</p>
<p>封装右键菜单插件:<br>
https://juejin.cn/post/6906788973981466637</p>
<h3 id="10-谈一谈你对-vuenexttick-的理解有什么用">10. 谈一谈你对 Vue.nextTick() 的理解?有什么用?</h3>
<ul>
<li>
<p>数据变化,进行set操作,代码确实是同步的,但是set行为是异步的;set操作修改声明变量,触发re-render生成新的虚拟DOM,进一步执行diff运算,找到脏节点集合,交给Vue背后的更新队列去执行循环更新。在更新队列中每一个更新任务都是一个更新单元,nextTick表示下一个更新单元(更新周期)。</p>
</li>
<li>
<p>我们set操作data(更新DOM),你希望访问这个DOM的最新状态时,使用this.$nextTick(handler)。</p>
</li>
</ul>
<h3 id="11-谈一谈vue中组件通信">11. 谈一谈Vue中组件通信?</h3>
<pre><code>1)父子组件通信:父传子使用自定义属性(props),子传父使用自定义事件($emit())。
2)状态提升:当兄弟组件之间需要共享数据时,我们通常的做法是把这个数据定义它们的共同的父组件中,再通过自定义属性实现数据共享。
3)provide/inject:这是在组件树中,自上而下的一种数据通信方案,也就是说只能父级组件中向后代组件传递。需要注意的是,当provide提供动态数据(声明式变量)时,动态数据发生变化,后代组件们不会自动更新。这是为什么呢?你自己从生命周期流程的角度去思考。
4)ref通信:ref是Vue内置的一个属性,每一个HTML元素或组件都有这个属性;ref作用在HTML元素上得到DOM实例,ref作用在组件上得到组件实例。使用ref访问组件实例,进一步可以访问组件中的数据和方法。(说明:ref是一种快速的DOM的访问方式,当然ref也可作用在组件上得到组件实例。这些ref得到的DOM实例或组件实例,使用this.$refs来访问它们。ref尽量少用,除非某些难搞的需求。)
5)插槽通信:借助&lt;slot>组件实现从子组件向父组件传递数据,借助this.$slots访问父组件中的插槽实例。(在自定义组件中使用this.$slots访问父组件给的插槽实例;在父组件插槽中使用#default='scoped'访问子组件&lt;slot>回传的数据。这种通信在组件库中、工作中,非常常见!)
6)$parent/$children:借助$parent/$children可以实现,在任一组件中访问组件树中的其它任意组件实例,可以做到在组件中随意穿梭。($parent表示的是当前组件的父组件实例,$children表示的是当前组件的子组件们。)
7)$attrs/$listeners:借助$attrs可访问父组件传递过来的自定义属性(除了class和style外),借助$listenrs可以访问父组件给的自定义事件。在某些场景下,$attrs/$listeners可以替代props/$emit()这种通用的通信方案。
8)事件总线:借助于Vue内置的事件系统($on/$emit/$off/$once)实现“订阅-发布”式的通信,这种通信方式是一种与组件层级无关的“一对多”的通信。(工作中很少用,一些特殊的Vue项目才用得到事件总线。)
9)Vuex通信:这是Vue架构中终极的通信方案,也是Vue架构中用的最多的一种通信方案。
</code></pre>
<h3 id="12-vue中逻辑复用技巧有哪些">12. Vue中逻辑复用技巧有哪些?</h3>
<p>组件:复用UI+逻辑<br>
混入<br>
自定义指令<br>
渲染函数&JSX<br>
插件<br>
过滤器<br>
V3中的自定义hook</p>
<h3 id="13-你项目的鉴权怎么做的你这个管理系统的权限怎么设计的">13. 你项目的鉴权怎么做的?你这个管理系统的权限怎么设计的?</h3>
<ul>
<li>我们之前的公司中小公司,做的项目,大概有30个左右的模块,所以权限这一块,就前端去处理的。我们处理的流程大致是这样的:前端登录换取token,在导航守卫中,实现权限设计,首先判断有没有token,没有token,直接跳到登录页面。有token会进一步判断vuex中有没有用户信息。如果没有用户信息,拿着token,调用接口获取用户信息,用户信息中保存了最重要的字段,就是角色,有了角色后,通过算法生成当前用户可访问的动态路由规则(算法大至是使用后端返回的角色和路由元信息中的角色进行对比,得到可以访问的动态路由规则),有了动态访问的路由规则,再通过addRoutes方法,把得到的动态访问的路由规则添加到路由系统。</li>
</ul>
<h3 id="14-简述vuex的工作流程">14. 简述Vuex的工作流程?</h3>
<ul>
<li>
<p>actions -> mutations -> state -> 组件</p>
<p>1. 在vue组件里面,通过dispatch来触发actions提交修改数据的操作。</p>
<p>2. 然后再通过actions的commit来触发mutations来修改数据。</p>
<p>3. mutations接收到commit的请求,就会自动通过Mutate来修改state(数据中心里面的数据状态)里面的数据。</p>
<p>4. 最后由store触发每一个调用它的组件的更新</p>
</li>
</ul>
<h3 id="15-说说你对-spa-单面的理解它的优缺点分别是什么">15. 说说你对 SPA 单⻚⾯的理解,它的优缺点分别是什么?</h3>
<ul>
<li>
<ul>
<li>SPA(single-page application),翻译过来就是单页应用<code>SPA</code>是一种网络应用程序或网站的模型,它通过动态重写当前页面来与用户交互,这种方法避免了页面之间切换打断用户体验在单页应用中,所有必要的代码(<code>HTML</code>、<code>JavaScript</code>和<code>CSS</code>)都通过单个页面的加载而检索,或者根据需要(通常是为响应用户操作)动态装载适当的资源并添加到页面页面在任何时间点都不会重新加载</li>
</ul>
</li>
</ul>
<h3 id="16-v-show-与-v-if-有什么区别">16. v-show 与 v-if 有什么区别?</h3>
<pre><code>1. 手段:v-if是通过控制dom节点的存在与否来控制元素的显隐;v-show是通过设置DOM元素的display样式,block为显示,none为隐藏;
2. 编译过程:v-if切换有一个局部编译/卸载的过程,切换过程中合适地销毁和重建内部的事件监听和子组件;v-show只是简单的基于css切换;
3. 编译条件:v-if是惰性的,如果初始条件为假,则什么也不做;只有在条件第一次变为真时才开始局部编译(编译被缓存?编译被缓存后,然后再切换的时候进行局部卸载); v-show是在任何条件下(首次条件是否为真)都被编译,然后被缓存,而且DOM元素保留;
4. 性能消耗:v-if有更高的切换消耗;v-show有更高的初始渲染消耗;
</code></pre>
<h3 id="17-数组中的哪些方法会触发视图的更新">17. 数组中的哪些⽅法会触发视图的更新?</h3>
<ul>
<li>
<p>push()、pop()、shift()、unshift()、splice()、sort()、reverse()以上这些方法会改变被操作的数组;</p>
</li>
<li>
<p>filter()、concat()、 slice() 这些方法不会改变被操作的数组,返回一个新的数组;</p>
</li>
</ul>
<h3 id="18-vue中v-for的key-有什么作用">18. Vue中v-for的key 有什么作⽤?</h3>
<pre><code>1.vue中列表循环需加:key="唯一标识" 唯一标识尽量是item里面id等,因为vue组件高度复用增加Key可以标识组件的唯一性,为了更好地区别各个组件 key的作用主要是为了高效的更新虚拟DOM。
2.key主要用来做dom diff算法用的,diff算法是同级比较,比较当前标签上的key还有它当前的标签名,如果key和标签名都一样时只是做了一个移动的操作,不会重新创建元素和删除元素。
3.没有key的时候默认使用的是“就地复用”策略。如果数据项的顺序被改变,Vue不是移动Dom元素来匹配数据项的改变,而是简单复用原来位置的每个元素。如果删除第一个元素,在进行比较时发现标签一样值不一样时,就会复用之前的位置,将新值直接放到该位置,以此类推,最后多出一个就会把最后一个删除掉。
4.尽量不要使用索引值index作key值,一定要用唯一标识的值,如id等。因为若用数组索引index为key,当向数组中指定位置插入一个新元素后,因为这时候会重新更新index索引,对应着后面的虚拟DOM的key值全部更新了,这个时候还是会做不必要的更新,就像没有加key一样,因此index虽然能够解决key不冲突的问题,但是并不能解决复用的情况。如果是静态数据,用索引号index做key值是没有问题的。
5.标签名一样,key一样这时候就会就地复用,如果标签名不一样,key一样不会复用。
</code></pre>
<h3 id="19-computed和method有什么区别">19. computed和method有什么区别?</h3>
<ul>
<li>调用方式不同。computed直接以对象属性方式调用,不需要加括号,而methods必须要函数执行才可以得到结果。</li>
<li>绑定方式不同。methods与compute纯get方式都是单向绑定,不可以更改输入框中的值。compute的get与set方式是真正的双向绑定。</li>
<li>是否存在缓存。methods没有缓存,调用相同的值计算还是会重新计算。competed有缓存,在值不变的情况下不会再次计算,而是直接使用缓存中的值。</li>
</ul>
<h3 id="20-什么是双向绑定v-model的本质是什么">20. 什么是双向绑定?v-model的本质是什么?</h3>
<ul>
<li>双向的含义是:状态的变化能更新到视图上;用户在表单中的修改也能更新到状态中。</li>
<li>等价于<组件 :value='状态' @input='新值=>状态=新值' /></li>
</ul>
<h3 id="21-data选项为什么是一个函数而不是对象">21. data选项为什么是⼀个函数⽽不是对象?</h3>
<ul>
<li>根实例对象data可以是对象也可以是函数(根实例是单例),不会产生数据污染情况</li>
<li>组件实例对象data必须为函数,目的是为了防止多个组件实例对象之间共用一个data,产生数据污染。采用函数的形式,initData时会将其作为工厂函数都会返回全新data对象</li>
</ul>
<h3 id="22-vue-data-中某一个属性的值发生改变后视图会立即同步执行-重新渲染吗">22. Vue data 中某⼀个属性的值发⽣改变后,视图会⽴即同步执⾏、重新渲染吗?(?)</h3>
<ul>
<li>不会,</li>
</ul>
<h3 id="23-在-vue-js开发环境下调用api接口如何避免跨域">23. 在 Vue. js开发环境下调⽤API接⼝,如何避免跨域</h3>
<ul>
<li>vue.config.js配置文件中 :</li>
</ul>
<pre><code class="language-javascript">module.exports = {
devServer: {
// 代理配置
proxy: {
// 到底代理哪些接口 ?
'/api': { // 凡是接口中后面域名中带有api的 , 框架都会进行一个代理转发
target: '真实接口地址写到这里', // 后端提供的接口地址
changeOrigin: true, // 开启跨域代理
}
}
}
}
</code></pre>
<h3 id="24-v-if和v-for一起使用的弊端及解决办法">24. v-if和v-for⼀起使⽤的弊端及解决办法</h3>
<ul>
<li>
<p>由于v-for的优先级比v-if高,所以导致每循环一次就会去v-if一次,而v-if是通过创建和销毁dom元素来控制元素的显示与隐藏,所以就会不停的去创建和销毁元素,造成页面卡顿,性能下降。</p>
</li>
<li>
<p>解决办法:<br>
在v-for的外层或内层包裹一个元素来使用v-if<br>
用computed处理</p>
</li>
</ul>
<h3 id="25-谈谈你对-keep-alive-的了解">25. 谈谈你对 keep-alive 的了解?</h3>
<ul>
<li>keep-alive用法
<ul>
<li><keep-alive>包裹动态组件时,会缓存不活动的组件实例,而不是销毁它们</li>
<li><keep-alive>是一个抽象组件:它自身不会渲染一个DOM元素,也不会出现在父组件链中</li>
<li>当组件在<keep-alive>内被切换,它的activated和deactivated这两个生命周期钩子函数将会被对应执行</li>
</ul>
</li>
</ul>
<h3 id="26-什么是侦听器有什么用">26. 什么是侦听器?有什么用?</h3>
<ul>
<li>响应数据的变化</li>
<li>watch</li>
</ul>
<h3 id="27-侦听器能不能监听数组对象的变化可以但默认不监听深层deeptrue">27. 侦听器能不能监听数组/对象的变化?(可以,但默认不监听深层)deep:true</h3>
<h3 id="28-如何监听一个对象中的某个属性变化deeptrue-推荐-objkey这种监听写法">28. 如何监听一个对象中的某个属性变化?(deep:true, 推荐 'obj.key'这种监听写法)</h3>
<h3 id="29-侦听器为什么默认不支持深度监听性能优化">29. 侦听器为什么默认不支持深度监听?(性能优化)</h3>
<h3 id="30-什么是组件化你怎么理解组件化">30. 什么是组件化?你怎么理解组件化?</h3>
<ul>
<li>把页面拆分成多个组件</li>
<li>组件是可以复用性的</li>
<li>易于维护</li>
<li>有封装性,易于使用</li>
<li>大型项目中降低组件之间重复性</li>
</ul>
<h3 id="31-你工作中有没有封装比较好的组件">31. 你工作中有没有封装比较好的组件?</h3>
<p>分页组件</p>
<pre><code class="language-javascript"><template>
<div class="xtx-pagination">
<a href="javascript:;" v-if="myCurrentPage===1" class="disabled">上一页</a>
<a href="javascript:;" v-else @click="go(-1)">上一页</a>
<!-- 如果起点按钮大于2就显示 -->
<span v-if="pageInfo.start>2">...</span>
<a href="javascript:;" @click="changePage(item)" :class="{active: myCurrentPage === item}" v-for="(item,idx) in pageInfo.pager" :key="idx"> {{item}} </a>
<!-- 如果当前终点按钮小于总页数就显示 当终点按钮是20总按钮也是20 就不显示 -->
<span v-if="pageInfo.end < pageInfo.pageCount">...</span>
<!-- 如果当前按钮等于最后一页就禁止点击 disabled -->
<a href="javascript:;" v-if="myCurrentPage==pageInfo.end" class="disabled">下一页</a>
<a href="javascript:;" v-else @click="go(1)">下一页</a>
</div>
</template>
<script>
import { computed, ref, watch } from 'vue'
export default {
name: 'XtxPagination',
props: {
total: { type: Number, default: 100 },
pageSize: { type: Number, default: 10 },
currentPage: { type: Number, default: 1 },
btnCount: { type: Number, default: 5 }
},
setup (props, { emit }) {
const myTotal = ref(100) // 总条数
const myPageSize = ref(5) // 每页共几条
const myCurrentPage = ref(3) // 用户实时点击,修改
const myBtnCount = ref(5) // 分页按钮的个数5个
// 让当前的页码处于正中间
// const pager = ref([1, 2, 3, 4, 5])
// 根据上边信息,实时计算 pager,起始页码,结束页码
const pageInfo = computed(() => {
// 总页数 = 总条数/每页几条
const pageCount = Math.ceil(myTotal.value / myPageSize.value)
// 起点 = 当前页数-总页数/2 举例 3 - Math.floor(5/2) = 1
let start = myCurrentPage.value - Math.floor((myBtnCount.value / 2))
// 终点 = 起点页数 + 总页数 - 1 距离 1 + 5 -1 || 3 + 5 -1
let end = start + myBtnCount.value - 1
// 意外1 当起点小于1
if (start < 1) {
start = 1
// 终点= 当前页数>总页数?总页数 否则 当前页数
end = myBtnCount.value > pageCount ? pageCount : myBtnCount.value
}
// 意外2 当终点大于最大页码
if (end > pageCount) {
end = pageCount
// 起点= 终点+(-所有页数+1)>1?1:= 终点+(-所有页数+1)
start = (end - myBtnCount.value + 1) < 1 ? 1 : (end - myBtnCount.value + 1)
}
const pager = []
for (let i = start; i <= end; i++) {
pager.push(i)
}
return { start, end, pageCount, pager }
})
// 上一页下一页
const go = (step) => {
myCurrentPage.value += step
}
const changePage = (page) => {
// 如果等于现在页页码 保持不动
if (page === myCurrentPage.value) return
myCurrentPage.value = page
emit('currentCahnge', page)
}
// 监听传入的值改变
watch(props, () => {
myTotal.value = props.total
myPageSize.value = props.pageSize
myCurrentPage.value = props.currentPage
myBtnCount.value = props.btnCount
}, { immediate: true })
return { myTotal, myPageSize, myCurrentPage, myBtnCount, pageInfo, go, changePage }
}
}
</script>
<style scoped lang="less">
.xtx-pagination {
display: flex;
justify-content: center;
padding: 30px;
> a {
display: inline-block;
padding: 5px 10px;
border: 1px solid #e4e4e4;
border-radius: 4px;
margin-right: 10px;
&:hover {
color: @xtxColor;
}
&.active {
background: @xtxColor;
color: #fff;
border-color: @xtxColor;
}
&.disabled {
cursor: not-allowed;
opacity: 0.4;
&:hover {
color: #333
}
}
}
> span {
margin-right: 10px;
}
}
</style>
</code></pre>
<h3 id="32-说说vue插槽的作用和平时开发中的应用">32. 说说Vue插槽的作⽤和平时开发中的应⽤?</h3>
<ul>
<li>
<p>vue.js插槽的作用:</p>
<ul>
<li>1、显示标签,在组件内部通过【<slot></slot>】进行接收;</li>
<li>2、命名插槽,增加插槽的灵活性;</li>
<li>3、带参数的插槽,将插槽中的数据使用组件内部的数据。</li>
</ul>
</li>
<li>
<p>插槽平时开发中的应⽤:</p>
<ul>
<li>1、在封装组件时,如果组件中的某个内容是动态的或不确定的,就可以使⽤插槽来代替了。</li>
<li>2、在使⽤第三⽅库时,往往会通过使⽤插槽类⾃定义第三⽅组件中的某些内容。</li>
</ul>
</li>
</ul>
<h3 id="33父子组件的生命周期顺序">33.⽗⼦组件的⽣命周期顺序</h3>
<ul>
<li>
<p>加载渲染过程: ⽗beforeCreate -> ⽗created -> ⽗beforeMount -> ⼦beforeCreate -> ⼦created -> ⼦beforeMount ->⼦mounted -> ⽗mounted</p>
</li>
<li>
<p>⼦组件更新过程:⽗beforeUpdate -> ⼦beforeUpdate -> ⼦updated -> ⽗updated</p>
</li>
<li>
<p>⽗组件更新过程:⽗beforeUpdate -> ⽗updated</p>
</li>
<li>
<p>销毁过程:⽗beforeDestroy -> ⼦beforeDestroy -> ⼦destroyed -> ⽗destroyed</p>
</li>
</ul>
<h3 id="34什么是composition-api-和-options-api">34.什么是Composition API 和 Options API?</h3>
<ul>
<li>Options API是指在对应的属性中编写对应的功能模块, ⽐如data定义数据、methods中定义⽅法、computed中定<br>
义计算属性、watch中监听属性改变,也包括⽣命周期钩⼦
<ul>
<li>弊端: 当我们实现某⼀个功能时,这个功能对应的代码逻辑会被拆分到各个属性中,当组件变得复杂,导致对应属性的列表也会增⻓,这可能会导致组件难以阅读和理解</li>
</ul>
</li>
<li>Composition API 是⼀组 API,允许我们使⽤导⼊的函数⽽不是声明选项来编写 Vue 组件。它是⼀个涵盖以下 API 的总称:Reactivity API、Lifecycle Hooks、Dependency Injection等等<br>
使⽤Composition API编写组件时可以根据逻辑功能来组织代码。⽐如可以把⼀个功能所⽤到的API 放在⼀起,这样可以让代码⾼内聚和低耦合,进⽽提⾼了代码的逻辑的复⽤性。<br>
在 Vue 3 中,它也主要与script setup语法⼀起使⽤。</li>
</ul>
<h3 id="35composition-api和之options-api有什么区别">35.Composition API和之Options API有什么区别?</h3>
<ul>
<li>1)在逻辑组织和逻辑复⽤⽅⾯,Composition API是优于Options API。</li>
<li>2)Composition API⼏乎是函数,会有更好的类型推断,对于TS的⽀持更友好。</li>
<li>3)Composition API对 tree-shaking 友好,代码也更容易压缩。</li>
<li>4)Composition API中⻅不到this的使⽤,减少了this指向不明的情况。</li>
<li>5)Composition API⽤起来稍微复杂⼀点,⽽Options API就⾮常简单、易于使⽤。</li>
</ul>
<h3 id="36-说说vue3中setup函数的作用">36. 说说Vue3中setup函数的作⽤?</h3>
<ul>
<li>
<p>在Vue3中, setup() 函数充当了组件编写Composition API 的⼊⼝点。setup函数参数主要有两个参数:<br>
1)第⼀个参数:props , ⽗组件传递过来的属性会被放到props对象中<br>
2)第⼆个参数:context, 它⾥⾯包含三个属性:attrs:所有的⾮prop的attribute;slots:⽗组件传递过来的插槽;emit:当我们组件内部需要发出事件时会⽤到emit(因为我们不能访问this,所以不可以通过 this.$emit发出事件)</p>
</li>
<li>
<p>可以在setup中可以定义响应式数据、⽅法、计算属性、侦听器等等。<br>
可以通过setup的返回值来替代data选项,让数据可以直接在template中使⽤。</p>
</li>
</ul>
<h3 id="37ref和reactive有什么区别开发中如何选择">37.ref和reactive有什么区别?开发中如何选择?</h3>
<ul>
<li>区别
<ul>
<li>
<p>ref和reactive都是响应式的API,都可以⽤来定义响应式的数据。<br>
ref可以包裹任意数据类型,reactive只能包裹复杂数据类型,⽐如对象、数组。</p>
</li>
<li>
<p>ref返回⼀个ref对象,在script中取值需要通过value属性,但是在模板中使⽤会进⾏解包不需要调⽤value。</p>
</li>
<li>
<p>reactive包裹的是复杂数据类型,直接取⾥⾯的属性即可。</p>
</li>
</ul>
</li>
<li>选择
<ul>
<li>ref⼏乎可以应⽤在任何场景,⽽且包含reactive适合的场景</li>
<li>reactive的应⽤场景⽐较受限,第⼀:值⽐较固定,第⼆:值与值之间是有联系的。</li>
<li>开发中尽量选择ref</li>
</ul>
</li>
</ul>
<h3 id="38-composition-api常的几个函数与用法">38. Composition API常⻅的⼏个函数与⽤法?</h3>
<ul>
<li>
<p>ref:包裹任意类型的值,将包裹的值加⼊响应式</p>
</li>
<li>
<p>reactive:包裹复杂类型的值,将包裹的值加⼊响应式</p>
</li>
<li>
<p>computed:把⼀些复杂逻辑⽤computed进⾏包裹,如同Options API中的计算属性⼀样,computed会⾃动收集相关依赖,当依赖发⽣变化时,会⾃动进⾏更新</p>
</li>
<li>
<p>⽣命周期:Vue3中想要在beforeCreate和created中做的事,直接在setup中做即可,Vue3的其他的⽣命周期函数都要在前⾯加⼀个on,然后需要在vue中主动引⼊</p>
</li>
<li>
<p>watch:<br>
1)watch可以监听单个数据源,也可以监听多个数据源<br>
2)watch是懒执⾏,第⼀次是不会执⾏的,除⾮你为其提供第三个参数中的immediate属性为true<br>
3)watch只有等到监听的数据源发⽣了变化后,才会执⾏第⼆个参数(回调)<br>
4)watch可以获取监听数据源的前后变化的值<br>
5)侦听多个数据源的时候,第⼀个参数是数组类型</p>
</li>
<li>
<p>watchEffect:<br>
1)watchEffect会⾃动收集依赖,收集的依赖是第⼀个参数,也就是回调函数中有哪些东⻄是加⼊响应式的<br>
2)如果这个值加⼊了响应式就会被收集起来,当被收集的值发⽣了变化,就会重新执⾏这个回调函数<br>
3)watchEffect第⼀次执⾏是在DOM挂载前执⾏的,所以如果你想在第⼀次执⾏时拿到DOM元素<br>
4)需要传⼊第⼆个参数,第⼆个参数是⼀个对象,让其flush属性的值为post即可</p>
</li>
<li>
<p>toRefs:<br>
1)对reactive进⾏解构后就失去了响应式的效果,因为reactive返回的是⼀个Proxy对象<br>
2)对Proxy对象进⾏解构,拿到的是纯净的值,所以没有了响应式的效果<br>
3)如果想要对reactive进⾏解构,需要对其包裹⼀个toRefs<br>
4)这么做相当于为reactive中的每⼀个值包裹了⼀个ref</p>
</li>
</ul>
<h3 id="39-vue3中的watch和watcheffect有什么区别">39. Vue3中的watch和watchEffect有什么区别?</h3>
<ul>
<li>watch
<ul>
<li>具有一定的惰性lazy 第一次页面展示的时候不会执行,只有数据变化的时候才会执行</li>
<li>参数可以拿到当前值和原始值</li>
<li>需要传递要侦听的内容</li>
<li>可以侦听多个数据的变化,用一个侦听起承载</li>
</ul>
</li>
<li>watchEffect
<ul>
<li>立即执行,没有惰性,页面的首次加载就会执行。</li>
<li>没有过多的参数,只有一个回调函数</li>
<li>自动检测内部代码,代码中有依赖就会执行</li>
<li>不需要传递要侦听的内容,会自动感知代码依赖</li>
<li>无法获取到原值,只能得到变化后的值</li>
<li>异步的操作放在这里会更加合适</li>
</ul>
</li>
</ul>
<h3 id="40-说说vue3中script-setup语法糖常用法">40. 说说Vue3中script setup语法糖常⻅⽤法?</h3>
<p>script setup 是在单⽂件组件中使⽤ Composition API 的编译时语法糖,相⽐与之前的setup函数写法,它具有更多的优势:<br>
1)更少的样板内容,更简洁的代码。<br>
2)能够使⽤纯 TypeScript 声明 props 和抛出事件。<br>
3)更好的运⾏时性能 (其模板会被编译成与其同⼀作⽤域的渲染函数,没有任何的中间代理)。<br>
4)更好的 IDE 类型推断性能 (减少语⾔服务器从代码中抽离类型的⼯作)。</p>
<p>script setup:<br>
1)当使⽤ script setup 的时候,任何在 script setup 声明的顶层绑定都能在模板中直接使⽤<br>
2)声明的顶层绑定:包括变量,函数声明,以及 import 引⼊的内容<br>
3)响应式数据需要通过ref、reactive来创建<br>
4)在script setup中导⼊的组件可以直接使⽤</p>
<p>defineProps:<br>
1)在script setup语法糖中必须使⽤ defineProps API来声明props,它具备完整的类型推断并且在<code><script setup></code> 中是直接可⽤的(不需要额外导⼊)。</p>
<p>defineEmits:<br>
1)在script setup语法糖中必须使⽤ defineEmits API来声明 emits,它具备完整的类型推断并且在<code><script setup></code> 中是直接可⽤的(不需要额外导⼊)。</p>
<p>defineExpose:<br>
1)获取组件的实例可以通过ref来获取,接着组件挂载完成后可通过value拿到组件实例。<br>
2)当拿到组件实例后,默认是不可以访问这个实例中的⽅法和属性,因为默认没暴露任何⽅法和属性。<br>
3)因此在Vue3组件中可以⽤defineExpose API来暴露⽅法和属性给外部访问。<br>
4)defineExpose 也是不需要导⼊,直接使⽤即可</p>
<h3 id="41vue-router路由的两种模式">41.vue-router路由的两种模式</h3>
<ul>
<li>
<p>vue-router中默认使⽤的是hash模式:<br>
1)hash模式,带#。如:http://localhost:8080/#/page。改变hash,浏览器本身不会有任何请求服务器动作。<br>
2)history模式,不带#, 如:<http://localhost:8080/page ,路径没有#。基于HTML5的pushState、replaceState实现。</p>
</li>
<li>
<p>hash:<br>
1)有 # 号<br>
2)能够兼容到IE8<br>
3)实际的url之前使⽤哈希字符,这部分url不会发送到服务器,不需要在服务器层⾯上进⾏任何处理<br>
4)刷新不会存在 404 问题<br>
5)不需要服务器任何配置</p>
</li>
<li>
<p>history:<br>
1)没有 # 号<br>
2)只能兼容到IE10<br>
3)每访问⼀个⻚⾯都需要服务器进⾏路由匹配⽣成html ⽂件再发送响应给浏览器,消耗服务器⼤量资源<br>
4)浏览器直接访问嵌套路由时,会报 404 问题。<br>
5)需要在服务器配置⼀个回调路由</p>
</li>
</ul>
<h3 id="42-在创建挂载更新销毁阶段vue在背后分别做了些什么事儿">42. 在创建/挂载/更新/销毁阶段,Vue在背后分别做了些什么事儿?</h3>
<p>Vue的生命周期指的是组件从“生”到“死”的全过程,一共分四个阶段,不同阶段有不同的钩子函数:</p>
<ul>
<li>创建阶段:beforeCreate、created</li>
<li>挂载阶段:beforeMount、mounted</li>
<li>更新阶段:beforeUpdate、updated</li>
<li>销毁阶段:beforeDestroy、destroyed</li>
<li>与动态组件有关的两个特殊的钩:activated(激活)、deactivated(休眠)</li>
<li>与组件异常捕获有关的一个钩子:errorCaptured</li>
</ul>
<p>在beforeCreate之前会声明methods中的方法和声明生命周期钩子函数,这个钩子函数我们用的并不多</p>
<p>在created之前会注入一些数据,初始化响应式系统,我们通常在这个钩子函数中调接口,获取路由参数等</p>
<p>在beforeMount之前会通过el $meount template找模板,会把模块变成render函数 调用render函数创建虚拟DOM,虚拟DOM转化成真实DOM,进行挂载,通常这个钩子函数我们也用不到</p>
<p>在mounted时,已表示真实DOM已挂载完毕,我们在这个钩子中通常调接口,开定时器,DOM操作,建立websocket连接 实例化echarts实例等。</p>
<p>当数据变化时,会触发beforeUpdate钩子,这个钩子通常也用不到。</p>
<p>在updated之前,要生成新的虚拟DOM,新的虚拟DOM和老的虚拟DOM进行对比,会执行patch运算,diff算法,找到两个虚拟DOM的最小差异,找到后,进行异步更新,key的目的就是最快找到最小差异,这个钩子也不常用,但是有点类似于watch侦听器或类似于$nextTick(),我们不能在这个钩子中更新数据,会导致死循环。</p>
<p>当我们手动调用$destory()或路由切换时,会调用beforeDestroy这个钩子函数,我们可以在这个钩子函数中清空定时器,解除事件绑定,清除缓存...</p>
<h3 id="43-响应式原理发生在vue哪些生命周期阶段">43. 响应式原理,发生在Vue哪些生命周期阶段?</h3>
<ul>
<li><code>init</code>阶段,创建阶段。</li>
<li>更新阶段</li>
</ul>
<h3 id="44-虚拟dom在哪些阶段生成的">44. 虚拟DOM,在哪些阶段生成的?</h3>
<ul>
<li>挂载阶段
<ul>
<li>在beforeMount之前会通过el $meount template找模板,会把模块变成render函数 调用render函数创建虚拟DOM,虚拟DOM转化成真实DOM,进行挂载,通常这个钩子函数我们也用不到</li>
</ul>
</li>
<li>更新阶段
<ul>
<li>在updated之前,要生成新的虚拟DOM,新的虚拟DOM和老的虚拟DOM进行对比,会执行patch运算,diff算法,找到两个虚拟DOM的最小差异,找到后,进行异步更新,key的目的就是最快找到最小差异,这个钩子也不常用,但是有点类似于watch侦听器或类似于$nextTick(),我们不能在这个钩子中更新数据,会导致死循环。</li>
</ul>
</li>
</ul>
<h3 id="45-哪些生命周期钩子可以执行多次哪些执行一次">45. 哪些生命周期钩子可以执行多次?哪些执行一次?</h3>
<ul>
<li>更新阶段:beforeUpdate、updated<br>
与动态组件有关的两个特殊的钩:activated(激活)、deactivated(休眠)</li>
</ul>
<p>上面四个钩子函数执行多次。</p>
<h3 id="46什么虚拟dom是一个很大的json数据用于描述视图模板的保存在内存中">46.什么虚拟DOM?(是一个很大的<code>JSON</code>数据,用于描述视图模板的,保存在内存中)</h3>
<ul>
<li>用 <code>js</code> 对象来表示真实的 DOM 树结构,真实 <code>dom</code> 的一个 <code>js 对象</code>映射</li>
<li>频繁的操作 DOM,会产⽣⼀定的性能问题</li>
<li>在组件渲染的时候会调用 <code>render</code> 函数,这个函数会生成一个虚拟 <code>dom</code>,再根据这个虚拟 <code>dom</code> 生成真实的 <code>dom</code>,然后这个真实的<code>dom</code> 会挂载到我们的页面中。</li>
<li>数据发生改变的时候 render 函数会生成一个新的虚拟 <code>dom</code> 新的虚拟 <code>dom</code> 树和旧的虚拟 <code>dom</code> 树进行对比,找到要要修改的虚拟 <code>dom</code>的部分,去修改相对应部分的真实 <code>dom</code></li>
</ul>
<h3 id="47-谈一谈你对-mvvm-mvc-mvp的理解">47. 谈一谈你对 MVVM、MVC、MVP的理解?</h3>
<ul>
<li>MVVM流程 : M数据层 -> VM虚拟DOM层 -> V视图层</li>
<li>网页本质 = M数据层 + V视图结构</li>
<li>M+V是怎么组装的?( MVC MVP MVVM)</li>
<li>M+V在哪儿组装?(前后端分离、前后端不分离、SSR服务端渲染)</li>
<li>进一步理解(阮一峰博客):https://www.ruanyifeng.com/blog/2015/02/mvcmvp_mvvm.html</li>
</ul>
<h3 id="48mounted和activated有什么区别deactivated和beforedestroy有什么区别执行次数-使用场景">48.mounted和activated有什么区别?deactivated和beforeDestroy有什么区别?(执行次数、使用场景)</h3>
<ul>
<li>这两组钩子的区别和使用场景:activated(执行多次) - mounted(执行一次) 、 deactivated(执行多次) - beforeDestroy(执行一次)</li>
<li>mounted和activated区别
<ul>
<li>mounted:是挂载vue实例后的钩子函数,钩子在主页挂载时执行一次,如果没有缓存的话,<br>
再次回到主页时,此函数还会执行</li>
<li>activated:是组件被激活后的钩子函数,每次回到页面都会执行</li>
</ul>
</li>
<li>deactivated和beforeDestroy区别
<ul>
<li>beforeDestroy生命周期函数表示即将销毁,此时仍然可以使用子组件的实例、methods、watch</li>
<li>到了destroyed生命周期函数,此时已经被销毁,无法再使用子组件的实例,methods、watch</li>
</ul>
</li>
</ul>
<h3 id="49-vue有哪些内置组件">49. Vue有哪些内置组件?</h3>
<p>1)component组件:有两个属性---is inline-template</p>
<p>渲染一个‘元组件’为动态组件,按照'is'特性的值来渲染成那个组件</p>
<p>2)transition组件:为组件的载入和切换提供动画效果,具有非常强的可定制性,支持16个属性和12个事件</p>
<p>3)transition-group:作为多个元素/组件的过渡效果</p>
<p>4)keep-alive:包裹动态组件时,会缓存不活动的组件实例,而不是销毁它们</p>
<p>5)slot:作为组件模板之中的内容分发插槽,slot元素自身将被替换</p>
<h3 id="50-哪些场景下你会用到动态组件">50. 哪些场景下你会用到动态组件?</h3>
<p>动态组件也是为了实现条件渲染,在需要使用条件渲染地方,根据不同的组件,渲染不到组件,都可以使用动态组件。</p>
<h3 id="51-在vue中实现条件渲染渲染有哪些办法">51. 在Vue中实现条件渲染渲染有哪些办法?</h3>
<p>v-if / v-show / component is</p>
<h3 id="52-路由跳转时如何传递数据">52. 路由跳转时如何传递数据?</h3>
<p>动态路由:<br>
1)path: /user/:id<br>
2)获取动态路由的值的⽅式如下:<br>
3)在template中,直接通过 <span class="katex"><span class="katex-mathml"><math><semantics><mrow><mi>r</mi><mi>o</mi><mi>u</mi><mi>t</mi><mi>e</mi><mi mathvariant="normal">.</mi><mi>p</mi><mi>a</mi><mi>r</mi><mi>a</mi><mi>m</mi><mi>s</mi><mi mathvariant="normal">获</mi><mi mathvariant="normal">取</mi><mi mathvariant="normal">值</mi><mn>4</mn><mi mathvariant="normal">)</mi><mi mathvariant="normal">在</mi><mi>c</mi><mi>r</mi><mi>e</mi><mi>a</mi><mi>t</mi><mi>e</mi><mi>d</mi><mi mathvariant="normal">中</mi><mi mathvariant="normal">,</mi><mi mathvariant="normal">通</mi><mi mathvariant="normal">过</mi><mi>t</mi><mi>h</mi><mi>i</mi><mi>s</mi><mi mathvariant="normal">.</mi></mrow><annotation encoding="application/x-tex">route.params获取值
4)在created中,通过 this.</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.8888799999999999em;vertical-align:-0.19444em;"></span><span class="mord mathdefault" style="margin-right:0.02778em;">r</span><span class="mord mathdefault">o</span><span class="mord mathdefault">u</span><span class="mord mathdefault">t</span><span class="mord mathdefault">e</span><span class="mord">.</span><span class="mord mathdefault">p</span><span class="mord mathdefault">a</span><span class="mord mathdefault" style="margin-right:0.02778em;">r</span><span class="mord mathdefault">a</span><span class="mord mathdefault">m</span><span class="mord mathdefault">s</span><span class="mord cjk_fallback">获</span><span class="mord cjk_fallback">取</span><span class="mord cjk_fallback">值</span><span class="mord">4</span><span class="mord cjk_fallback">)</span><span class="mord cjk_fallback">在</span><span class="mord mathdefault">c</span><span class="mord mathdefault" style="margin-right:0.02778em;">r</span><span class="mord mathdefault">e</span><span class="mord mathdefault">a</span><span class="mord mathdefault">t</span><span class="mord mathdefault">e</span><span class="mord mathdefault">d</span><span class="mord cjk_fallback">中</span><span class="mord cjk_fallback">,</span><span class="mord cjk_fallback">通</span><span class="mord cjk_fallback">过</span><span class="mord mathdefault">t</span><span class="mord mathdefault">h</span><span class="mord mathdefault">i</span><span class="mord mathdefault">s</span><span class="mord">.</span></span></span></span>route.params获取值<br>
5)在setup中,使⽤ vue-router库提供的⼀个hook useRoute(该Hook会返回⼀个Route对象,对象中保存着当前路由相关的值)</p>
<p>query参数:<br>
1)通过query的⽅式来传递参数<br>
2)在界⾯中通过 <span class="katex"><span class="katex-mathml"><math><semantics><mrow><mi>r</mi><mi>o</mi><mi>u</mi><mi>t</mi><mi>e</mi><mi mathvariant="normal">.</mi><mi>q</mi><mi>u</mi><mi>e</mi><mi>r</mi><mi>y</mi><mi mathvariant="normal">来</mi><mi mathvariant="normal">获</mi><mi mathvariant="normal">取</mi><mi mathvariant="normal">参</mi><mi mathvariant="normal">数</mi><mn>3</mn><mi mathvariant="normal">)</mi><mi mathvariant="normal">在</mi><mi>c</mi><mi>r</mi><mi>e</mi><mi>a</mi><mi>t</mi><mi>e</mi><mi>d</mi><mi mathvariant="normal">中</mi><mi mathvariant="normal">,</mi><mi mathvariant="normal">通</mi><mi mathvariant="normal">过</mi><mi>t</mi><mi>h</mi><mi>i</mi><mi>s</mi><mi mathvariant="normal">.</mi></mrow><annotation encoding="application/x-tex">route.query 来获取参数
3)在created中,通过 this.</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.8888799999999999em;vertical-align:-0.19444em;"></span><span class="mord mathdefault" style="margin-right:0.02778em;">r</span><span class="mord mathdefault">o</span><span class="mord mathdefault">u</span><span class="mord mathdefault">t</span><span class="mord mathdefault">e</span><span class="mord">.</span><span class="mord mathdefault" style="margin-right:0.03588em;">q</span><span class="mord mathdefault">u</span><span class="mord mathdefault">e</span><span class="mord mathdefault" style="margin-right:0.02778em;">r</span><span class="mord mathdefault" style="margin-right:0.03588em;">y</span><span class="mord cjk_fallback">来</span><span class="mord cjk_fallback">获</span><span class="mord cjk_fallback">取</span><span class="mord cjk_fallback">参</span><span class="mord cjk_fallback">数</span><span class="mord">3</span><span class="mord cjk_fallback">)</span><span class="mord cjk_fallback">在</span><span class="mord mathdefault">c</span><span class="mord mathdefault" style="margin-right:0.02778em;">r</span><span class="mord mathdefault">e</span><span class="mord mathdefault">a</span><span class="mord mathdefault">t</span><span class="mord mathdefault">e</span><span class="mord mathdefault">d</span><span class="mord cjk_fallback">中</span><span class="mord cjk_fallback">,</span><span class="mord cjk_fallback">通</span><span class="mord cjk_fallback">过</span><span class="mord mathdefault">t</span><span class="mord mathdefault">h</span><span class="mord mathdefault">i</span><span class="mord mathdefault">s</span><span class="mord">.</span></span></span></span>route.query获取值<br>
4)在setup,使⽤ vue-router库提供的⼀个hook useRoute 来获取</p>
<h3 id="53-计算属性有什么作用两大作用">53. 计算属性有什么作用?(两大作用)</h3>
<ul>
<li>作用1:当指令的表达式比较复杂时,我们建议使用计算属性来优化,提升视图模板中代码的可阅读性、可维护性。</li>
</ul>
<ul>
<li>作用2:用于缓存一个复杂的运算,避免组件更新时产生没有必要的性能损耗。计算属性本质上是一个函数,Vue会分析函数体中使用到了哪些声明式变量,有且仅有这些声明式变量发生变化时,计算属性才会重新执行。</li>
</ul>
<h3 id="54-什么是路由守卫路由守卫有什么作用">54. 什么是路由守卫?路由守卫有什么作⽤?</h3>
<p>vue-router 提供的路由(导航)守卫主要⽤来通过跳转或取消的⽅式守卫导航。有很多种⽅式植⼊路由导航中:全局的,单个路由独享的,或者组件级的。</p>
<p>全局导航钩⼦:<br>
1)router.beforeEach(to,from,next)<br>
2)router.afterEach(to,from)<br>
3)...</p>
<p>组件内的钩⼦:<br>
1)beforeRouteEnter (to, from, next)<br>
2)beforeRouteUpdate (to, from, next)<br>
3)beforeRouteLeave (to, from, next)<br>
4)...</p>
<p>单独路由独享组件:<br>
1)beforeEnter: (to, from, next)<br>
2)afterEach(to,from)<br>
3)...</p>
<ul>
<li>路由守卫有什么作⽤:<br>
1)可以在进⼊路由之前进⾏某些判断,⽐如,检查token是否存在来判断⽤户是否已经登录。<br>
2)可以在路由守卫中进⾏⻚⾯的权限判断,⽐如,判断某个⽤户是否拥有该⻚⾯的权限。<br>
3)也可以⽤来记录⻚⾯的某些信息,⽐如,记录⻚⾯的滚动信息等等。</li>
</ul>
<h3 id="55route和router的区别">55.route和router的区别</h3>
<ul>
<li><code>$router</code>是VueRouter的一个对象,通过Vue.use(VueRouter)和Vue构造函数得到一个router的实例对象,这个对象中是一个全局的对象,他包含了所有的路由,包含了许多关键的对象和属性。
<ul>
<li>以history对象来举例:<br>
<code>$router</code>.push({path:'home'}),本质是向history栈中添加一个路由,在我们看来是切换路由,但本质是在添加一个history记录<br>
<code>$router.replace({path:'home'}),//替换路由,没有历史记录 $router</code>.push('/login') ,跳转到指定路由</li>
</ul>
</li>
<li><code>$route</code>是一个跳转的路由对象,每一个路由都会有一个<code>$route</code>对象,是一个局部的对象,可以获取对应的name,path,params,query等
<ul>
<li>这两者不同的结构可以看出两者的区别,他们的一些属性是不同的。<br>
<code>$route.path</code> 字符串,等于当前路由对象的路径,会被解析为绝对路径,如/home/ews<br>
<code>$route.params</code> 对象,含路有种的动态片段和全匹配片段的键值对,不会拼接到路由的url后面<br>
<code>$route.query</code> 对象,包含路由中查询参数的键值对。会拼接到路由url后面<br>
<code>$route.router</code> 路由规则所属的路由器<br>
<code>$route.matchd</code> 数组,包含当前匹配的路径中所包含的所有片段所对象的配置参数对象<br>
<code>$route.name</code> 当前路由的名字,如果没有使用具体路径,则名字为空</li>
</ul>
</li>
</ul>
<h3 id="56vue-中怎么阻止冒泡怎么阻止默认事件怎么监听键盘enter键">56.vue 中怎么阻止冒泡?怎么阻止默认事件?怎么监听键盘enter键?</h3>
<ul>
<li>
<p>阻止冒泡和默认事件:<br>
1)通过事件对象<br>
2)事件事件修饰符</p>
</li>
<li>
<p>监听键盘enter键:<br>
1)通过事件对象<br>
2)通过按键修改符</p>
</li>
</ul>
<pre><code class="language-javascript">.stop:阻止事件冒泡
.prevent:阻止默认行为
@keyup.enter:监听键盘回车事件
</code></pre>
<h3 id="57-什么是状态管理什么是单项数据流">57. 什么是状态管理?什么是单项数据流?</h3>
<ul>
<li>
<p>在开发中,应⽤程序是需要处理各种各样的数据,这些数据需要保存在应⽤程序中的某⼀个位置,对于这些数据的管理就称之为是 状态管理。以前我们是如何管理应⽤程序的状态?<br>
1)在Vue开发中,我们使⽤组件化的开发⽅式。⽽在组件中我们定义的data或在setup中返回的数据,这些数据我们称之为状态(State)。<br>
2)在模块template中我们可以使⽤这些数据,模块最终会被渲染成DOM,我们称之为View。<br>
3)在模块中我们会产⽣⼀些⾏为事件,处理这些⾏为事件时,有可能会修改State,这些⾏为事件我们称之为Actions。</p>
</li>
<li>
<p>其实Vue组件内部的数据是以单向数据流的形式来管理数据的。例如,组件的数据定义在State中,接着在View层使⽤State中的数据,然后View层会产⽣⼀些事件Actions,⽽这些Actions可能会修改State的数据,这就是⼀个单项数据流的概念。</p>
</li>
</ul>
<h3 id="58-什么是vuex你使用过-vuex-吗">58. 什么是Vuex?你使⽤过 Vuex 吗?</h3>
<ul>
<li>
<p>vuex 是⼀个专为 Vue.js 应⽤程序开发的状态管理模式。每⼀个 Vuex 应⽤的核⼼就是 store(仓库)。“store” 基本上就是⼀个容器,它包含着你的应⽤中⼤部分的状态 ( state )。<br>
1)Vuex 的状态存储是响应式的。当 Vue 组件从 store 中读取状态的时候,若 store 中的状态发⽣变化,那么相应的组件也会相应地得到⾼效更新。<br>
2)改变 store 中的状态的唯⼀途径就是显式地提交 (commit) mutation。这样使得我们可以⽅便地跟踪每⼀个状态的变化。</p>
</li>
<li>
<p>Vuex包括⼀下⼏个核⼼模块:<br>
1)State:定义了应⽤状态的数据结构,可以在这⾥设置默认的初始状态。<br>
2)Getter:允许组件从 Store 中获取数据,mapGetters 辅助函数仅仅是将 store 中的 getter 映射到计算属性。<br>
3)Mutation:是唯⼀更改 store 中状态的⽅法,且必须是同步函数。<br>
4)Action:⽤于提交 mutation,⽽不是直接变更状态,可以包含任意异步操作。<br>
5)Module:允许将单⼀的 Store 拆分为多个 store 且同时保存在单⼀的状态树中。</p>
</li>
</ul>
<h3 id="59v-model-有哪些修饰符">59.v-model 有哪些修饰符?</h3>
<ul>
<li><a href="https://cn.vuejs.org/guide/essentials/forms.html#lazy"><code>.lazy</code></a> ——监听 <code>change</code> 事件而不是 <code>input</code></li>
<li><a href="https://cn.vuejs.org/guide/essentials/forms.html#number"><code>.number</code></a> ——将输入的合法符串转为数字</li>
<li><a href="https://cn.vuejs.org/guide/essentials/forms.html#trim"><code>.trim</code></a> ——移除输入内容两端空格</li>
</ul>
<h3 id="60-vue中怎么做动画">60. Vue中怎么做动画?</h3>
<p>内置组件transition,使用那6个类名编写自定义动画、使用animate.css第三方动画</p>
]]></content>
</entry>
<entry>
<title type="html"><![CDATA[Hello Gridea]]></title>
<id>https://lcb522.github.io/post/hello-gridea/</id>
<link href="https://lcb522.github.io/post/hello-gridea/">
</link>
<updated>2018-12-11T16:00:00.000Z</updated>
<summary type="html"><![CDATA[<p>👏 欢迎使用 <strong>Gridea</strong> !<br>
✍️ <strong>Gridea</strong> 一个静态博客写作客户端。你可以用它来记录你的生活、心情、知识、笔记、创意... ...</p>
]]></summary>
<content type="html"><![CDATA[<p>👏 欢迎使用 <strong>Gridea</strong> !<br>
✍️ <strong>Gridea</strong> 一个静态博客写作客户端。你可以用它来记录你的生活、心情、知识、笔记、创意... ...</p>
<!-- more -->
<p><a href="https://github.com/getgridea/gridea">Github</a><br>
<a href="https://gridea.dev/">Gridea 主页</a><br>
<a href="https://fehey.com/">示例网站</a></p>
<h2 id="特性">特性👇</h2>
<p>📝 你可以使用最酷的 <strong>Markdown</strong> 语法,进行快速创作</p>
<p>🌉 你可以给文章配上精美的封面图和在文章任意位置插入图片</p>
<p>🏷️ 你可以对文章进行标签分组</p>
<p>📋 你可以自定义菜单,甚至可以创建外部链接菜单</p>
<p>💻 你可以在 <strong>Windows</strong>,<strong>MacOS</strong> 或 <strong>Linux</strong> 设备上使用此客户端</p>
<p>🌎 你可以使用 <strong>𝖦𝗂𝗍𝗁𝗎𝖻 𝖯𝖺𝗀𝖾𝗌</strong> 或 <strong>Coding Pages</strong> 向世界展示,未来将支持更多平台</p>
<p>💬 你可以进行简单的配置,接入 <a href="https://github.com/gitalk/gitalk">Gitalk</a> 或 <a href="https://github.com/SukkaW/DisqusJS">DisqusJS</a> 评论系统</p>
<p>🇬🇧 你可以使用<strong>中文简体</strong>或<strong>英语</strong></p>
<p>🌁 你可以任意使用应用内默认主题或任意第三方主题,强大的主题自定义能力</p>
<p>🖥 你可以自定义源文件夹,利用 OneDrive、百度网盘、iCloud、Dropbox 等进行多设备同步</p>
<p>🌱 当然 <strong>Gridea</strong> 还很年轻,有很多不足,但请相信,它会不停向前 🏃</p>
<p>未来,它一定会成为你离不开的伙伴</p>
<p>尽情发挥你的才华吧!</p>
<p>😘 Enjoy~</p>
]]></content>
</entry>
</feed>