-
Notifications
You must be signed in to change notification settings - Fork 0
/
vue2.txt
1286 lines (1000 loc) · 58.4 KB
/
vue2.txt
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
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
npm install @vue/cli -g
vue-cli中的命令:
vue create 项目名
npm run serve
npm run build
npm i [email protected]
npm i [email protected]
-------------------------------------------------
参考:https://www.cnblogs.com/caijinghong/p/16907790.html
https://segmentfault.com/a/1190000039829560#item-2-4
【在created之后至beforeMount过程中,即模板编译和虚拟dom生成阶段具体做了什么?】
一、模板编译阶段:
1.解析:Vue 的模板编译器会解析模板字符串,识别其中的指令、插值表达式和普通文本等内容。
2.抽象语法树 (AST):解析后,模板会被转换为抽象语法树 (AST),它是一种以 JavaScript 对象表示模板结构的树形数据结构。
3.优化:在进行编译前,会对 AST 进行一些优化处理,例如静态节点的标记、静态属性的提取等,以提高后续渲染的性能。
4.生成渲染函数:最后,根据 AST 生成可执行的渲染函数,这个函数可以接受数据(h函数、渲染上下文对象context 等参数)作为参数,并返回一个虚拟 DOM。
第 1 步:模板字符串,
<div class="container">
<p v-if="showDescription">{{ description }}</p>
</div>
第 2 步:被vue编译器转换为 抽象语法树 (AST),
{
type: 'Element',
tag: 'div',
attrs: [
{ name: 'class', value: 'container' }
],
children: [
{
type: 'Element',
tag: 'p',
directives: [
{
name: 'if',
value: 'showDescription'
}
],
children: [
{
type: 'Text',
value: '{{ description }}'
}
]
}
]
}
第 3 步:根据 AST 生成可执行的渲染函数,
//在这个示例中,render 函数接收一个 h 参数,它是 createElement 函数的别名。通过调用 h 函数,我们可以创建虚拟 DOM 节点。在 h 函数的调用中,第一个参数是标签名,第二个参数是属性对象,第三个参数是子节点数组。
function render(h) {
return h('div', { class: 'container' }, [
h('h1', { class: 'title' }, 'Hello, Vue!'),
h('p', { class: 'description' }, 'This is a Vue component.'),
// ...
]);
}
二、虚拟 DOM 生成阶段:
1.创建虚拟节点:在渲染函数执行时,会根据模板中的每个节点生成对应的虚拟 DOM 节点。虚拟 DOM 是以 JavaScript 对象的方式表示真实 DOM 的节点,它包含节点类型、属性、子节点等信息。
2.构建虚拟 DOM 树:将所有的虚拟 DOM 节点按照层级关系连接起来,构成一棵虚拟 DOM 树。树的根节点对应模板的根节点。
3.生成真实 DOM:根据虚拟 DOM 树,Vue 会通过遍历虚拟 DOM 树的过程,创建相应的真实 DOM 节点并进行挂载。
【总结:】
1.vue模板引擎的 渲染机制:
在 Vue 中使用虚拟 DOM 的好处是可以将对真实 DOM 的操作优化为对虚拟 DOM 的操作,从而减少直接操作真实 DOM 带来的性能消耗。在数据发生变化时,Vue 会通过比较新旧虚拟 DOM 树的差异,最小化真实 DOM 的操作,只更新需要更新的部分,提高渲染的效率。
需要注意的是,模板编译和虚拟 DOM 生成阶段是在 Vue 实例初始化时进行的,它们只会执行一次,并生成与模板对应的渲染函数和虚拟 DOM。之后,当数据变化时,只需要执行渲染函数并更新虚拟 DOM,而不需要重新进行模板编译和虚拟 DOM 生成的过程。这也是 Vue 在性能方面的优化之一。
2.“AST抽象语法树”与“(vnode)虚拟dom”差异:
AST 是一个以 JavaScript 对象为基础的、描述源代码结构的树状数据结构,它反映了源代码的语法和结构;AST 的节点通常包含类型信息、属性信息和子节点信息,但它并不包含样式和布局相关的信息。
虚拟 DOM 是一个以 JavaScript 对象为基础的、用于描述组件的视图结构和渲染逻辑的树状结构,包含了节点的样式和布局相关信息,会被 Vue 的渲染引擎解析并转换为真实 DOM,并进行页面渲染。
补充:为什么 AST 不可以直接当 virtual DOM 呢?为什么我们要写 render 函数,而不是直接写虚拟 DOM??
“模板”不变,“AST”结构也不会变。当“根据AST生成的可执行渲染函数”所依赖的数据发生改变,vue模板引擎会重新执行该render函数生成vnode,之后比较新旧虚拟 DOM 树,针对性更新。
----------------------------------------------
MVVM代表模型-视图-视图模型(Model-View-ViewModel)。MVVM是一种软件架构模式,用于将用户界面(视图)与应用程序逻辑(模型)进行解耦,以便更好地管理和维护代码。
MVVM的主要特点如下:
模型(Model):模型代表应用程序的数据和业务逻辑。它是应用程序的数据源,通常与服务器端进行数据交互。在Vue.js中,模型可以是组件的数据属性、状态管理工具(如Vuex)中的数据等。
视图(View):视图是用户界面的可见部分,它是模型的可视化表示。在Vue.js中,视图由Vue组件模板表示,可以是HTML模板、Vue单文件组件等。
视图模型(ViewModel):视图模型是连接模型和视图的桥梁。它负责处理视图的渲染、用户输入的响应以及与模型之间的数据交互。在Vue.js中,视图模型由Vue实例表示,它与视图进行绑定,通过数据绑定和指令等技术实现了视图和模型之间的双向数据绑定。
MVVM的核心思想是数据驱动视图。在Vue.js中,当模型发生变化时,视图会自动更新;当用户与视图交互时,数据模型也会相应地更新。这种双向绑定的机制使开发人员能够更方便地处理视图和数据之间的同步,提高了开发效率和代码可维护性。
--------------------------------------------------------------------------------------------------------------
在 Vue.js 中,创建 Vue 实例时,可以传递一个配置对象作为参数,用于指定 Vue 实例的各种选项和行为。这个配置对象可以包含以下常见的参数:
el:指定 Vue 实例要挂载的元素。可以是一个 CSS 选择器字符串,或者是一个实际的 DOM 元素。类型可以是字符串或 DOM 元素。
data:指定 Vue 实例的初始数据。它应该是一个对象或返回一个对象的函数。类型可以是对象或函数。
computed:定义计算属性,这是一个包含计算属性的对象。计算属性可以是一个函数或具有 get 和 set 方法的对象。类型是对象。
methods:定义方法,这是一个包含方法的对象。方法是实例上的函数。类型是对象。
watch:定义侦听器,这是一个包含侦听器的对象。侦听器可以是一个函数、字符串方法名或包含 handler 方法的对象。类型是对象。
mounted:Vue 实例被挂载到 DOM 后要执行的钩子函数。它可以是一个函数或一个函数数组。类型可以是函数或数组。
template:指定 Vue 实例的模板字符串。该字符串将用于渲染实例的 DOM。可以是一个字符串模板或一个已编译的渲染函数。类型是字符串或渲染函数。
components:定义组件,这是一个包含组件的对象。类型是对象。
此外,还有其他参数可以用于配置 Vue 实例,如 props、filters、directives、mixins 等。每个参数都具有不同的作用和数据类型。需要根据实际需求来选择和使用这些参数。
总结一下,new Vue({}) 中的各个参数的数据类型可以是:
el: 字符串或 DOM 元素
data: 对象或函数
computed: 对象
methods: 对象
watch: 对象
mounted: 函数或函数数组
template: 字符串或渲染函数
components: 对象
这些参数可以根据需要组合使用,以创建适合您应用程序需求的 Vue 实例。
请注意,这里列出的是常见的选项,Vue 实例还有其他可用的选项和钩子函数。
--------------------------------------------------------------------------------------------------------------
【数据的 单向绑定、双向绑定】
》单向绑定:数据的变化会驱动视图的更新,但视图的变化不会反过来影响数据。当数据发生变化时,视图会自动更新以反映最新的数据值,但对视图的更改不会更新数据。单向绑定是一种从数据到视图的单向数据流。
在单向绑定中,常见的例子是“将数据属性绑定到视图元素的属性或内容”,如将一个数据绑定到一个文本输入框的 value 属性或将一个数据绑定到一个段落元素的 textContent 内容。
示例:
<!-- 数据的单向绑定 -->
<input type="text" :value="name">
<p>{{ message }}</p>
<!-- 注意:vue.js中的“{{}}插值表达式”只能用于“标签”中,不能用于“标签属性值” -->
》双向绑定:数据的变化会同时驱动视图的更新,并且视图的变化也会反过来更新数据。当数据发生变化时,视图会自动更新,同时当用户在视图上进行操作时,数据也会相应更新。这样,数据和视图之间形成了一个双向的同步关系。
在双向绑定中,常见的例子是“将数据属性绑定到表单元素的属性和事件上”,以实现在输入框中输入内容时数据的更新,同时当数据变化时,输入框的值也会自动更新。
示例:
<!-- 数据的双向绑定 -->
<input type="text" v-model="name">
在上述示例中,使用了 Vue.js 框架的 v-model 指令实现了数据的双向绑定。输入框的值与“数据属性 name ”相关联,当用户在输入框中输入内容时,name 的值会自动更新,同时当 name 的值发生变化时,输入框的值也会自动更新。
总结:数据的单向绑定是指数据的变化驱动视图的更新,而视图的变化不会反过来影响数据。数据的双向绑定是指数据的变化同时驱动视图的更新,并且视图的变化也会反过来更新数据。双向绑定实现了数据和视图之间的双向同步。
--------------------------------------------------------------------------------------------------------------
【函数形式的“data”选项,与 传统的对象字面量形式的“data”选项,比较。前者可拓展性更强!!!】
》区别1:当创建 Vue 实例时,需要对数据进行复杂的初始化处理,例如从外部获取数据、进行异步操作,又或计算默认值等操作时,
函数形式的 data 选项非常有用!!!
代码一:
data() {
const initialData = fetchInitialData(); // 从外部获取数据的异步操作
return {
data: initialData,
isLoading: true
};
}
依赖其他数据:当您需要在数据初始化过程中依赖其他数据进行计算或处理时,代码二:
data() {
const initialCount = 10;
return {
count: initialCount,
doubledCount: initialCount * 2
};
}
》区别2:关于 对组件复用的影响
注意:
在 根实例 中,data不区分是方法还是对象。
若作为普通组件,在复用时,将data写为一个函数可有效避免同一组件间 因数据共享 而引发问题。
当你将data写为一个对象时,它将成为组件实例的一个共享属性。这意味着,如果你在多个地方使用同一个组件,它们将共享(对象地址)相同的data对象。当一个组件的data被修改时,其他使用相同组件的地方也会受到影响,因为它们引用的是同一个对象。
而当你将data写为一个函数时,每个组件实例都会调用该函数来生成一个独立的数据对象。这样每个组件实例都拥有它们自己的数据对象,彼此之间互不影响。这种方式确保了组件的数据独立性,避免了数据在不同实例之间共享而导致的潜在问题。
比如:
Vue.component('my-component', {
data: {
counter: 0
},
template: '<div>{{ counter }}</div>',
mounted() {
setInterval(() => {
this.counter++;
}, 1000);
}
});
如果你在多个地方使用<my-component></my-component>,它们将共享同一个counter属性,因此计数器将在所有实例之间共享,并且每秒钟所有实例的计数器都会增加。
但是,如果你将data改为一个函数,每个实例将拥有自己独立的计数器:
Vue.component('my-component', {
data() {
return {
counter: 0
};
},
template: '<div>{{ counter }}</div>',
mounted() {
setInterval(() => {
this.counter++;
}, 1000);
}
});
现在,每个<my-component></my-component>都有自己独立的计数器,它们的计数器将独立地增加。
通过将data写为函数,你可以更好地控制组件的数据,使其具有独立性和复用性。每个实例都有自己的数据对象,它们之间不会相互影响,这符合Vue组件的设计原则。
---------------------------------------------------------------------------
【常见指令】
//事件 绑定到 当前元素
v-on:事件名 = "函数名"。可简写为,@事件名 = "函数名"
//数据属性url值, 单向绑定到 当前元素的某属性src
v-bind:src = "数据属性url值",可简写为,:src = "数据属性url值"
//数据属性值 与 当前元素的 value值(当单选绑定checkbox时,则为checked属性值boolean) 双向绑定(可收集input、textarea、select、checkbox等表单数据)
v-model = "数据属性url"
》重点1:有一文本框,
<input type="text" v-model = "uname"/>
该语法等同于,<input type="text" v-bind:value = "uname" v-on:input = "uname = $event.target.value"/>,
可简写为,
<input type="text" :value = "uname" @input = "uname = $event.target.value"/>
》重点2:v-model修饰符(trim、number、lazy),
(v-model.trim,去掉数据的 前后空格、v-model.number,截取数据中的“头部”数字):
示例1(v-model.lazy,当前元素为“文本框”,且当其 失焦后 才会改变 view,这是与v-model唯一的区别):
<input type="text" v-model.lazy = "uname"/>等同于,
<input type="text" :value = "uname" @change = "uname = $event.target.value"/>
------------------------------------------------------
【补充,常用的 事件 修饰符】
.stop修饰符,(不希望点击事件继续冒泡到父元素或更上层元素时,可以使用 .stop 修饰符阻止事件继续冒泡。@click.stop)
.prevent或.preventDefault修饰符,(不希望表单提交时刷新、或点击a链接跳转到 新页面 ,而是执行自定义的提交逻辑时,可以使用 .prevent 修饰符阻止默认的提交行为。表单提交@submit.prevent、a链接跳转@click.preventDefault)
.stopPropagation 修饰符,(不希望事件继续冒泡到父元素或更上层元素时,可以使用 .stopPropagation 修饰符阻止事件继续冒泡。@click.stopPropagation)
.keyCode修饰符,(希望在特定按键按下时触发事件,比如在文本输入框中按下回车键时提交表单,可以使用 ”事件名+按键的键码“如@keyup.enter来处理事件。)
.exact 修饰符,(当你希望在只有某个按键单独被按下时触发事件,而不是配合其他按键同时按下时触发。@keydown.exact)
.native 修饰符,(需要监听自定义组件的原生 DOM 事件时。<custom-component @click.native="handleNativeClick">监听自定义组件的原生点击事件</custom-component>
)
.once 修饰符,(希望事件只触发一次时,可以使用 .once 修饰符,该事件监听器在触发一次后将被移除。@click.once)
.left 和 .right 修饰符,(需要根据不同鼠标按键的点击事件来触发不同的操作时。@click.left或@click.right)
.capture 修饰符,(希望事件在捕获阶段(而不是冒泡阶段)被处理时,可以使用 .capture 修饰符。@click.capture)
.self 修饰符,(希望事件仅在点击元素本身时才被触发,而不是在其子元素上触发时,可以使用 .self 修饰符。)
.passive 修饰符,(当你处理触摸滑动等事件时,使用 .passive 修饰符可以优化滑动的性能。如<div @touchmove.passive="handleTouchMove">优化触摸滑动性能</div>)
------------------------------------------------------
》重点3:
3.1 v-model与checkbox间的绑定
3.1.1:只有一个checkbox选项,收集的是 checked属性值,为“布尔值”【比较特别。如 确认协议】
<input type="checkbox" v-model="isAgree"> 等同于,
<input type="checkbox" :checked="isAgree" @change="isAgree = $event.target.checked">
3.1.2:有多个checkbox选项,收集的是value属性,为 所选value值 构成的一个 数组
3.2 v-model与select间的绑定(单选时,一个string形式的value属性值;multiple size时,为 所选value值 构成的一个 数组)
3.3 v-model与radio间的绑定(单选时,一个string形式的value属性值)
3.4 v-model与textarea间的绑定(收集的是 textarea文本域 中的值)
//数据 一次性(首次生效)绑定
v-once = "数据属性url"
//数据(文本型)绑定
v-text= "数据属性url"
//数据(可渲染数据属性中 文本里的 html标签)绑定
v-html= "数据属性url"
//根据 条件,控制 元素的 创建与销毁(引发重排,较费性能!支持template标签。if...else if...else中间不能有其它元素)
v-if= "判断条件1"
v-else-if= "判断条件2"
v-else
补充:【幽灵标签template(本身非实体节点)】,多与v-if结合使用,包裹其它元素,按需渲染。
//根据 条件,控制 元素的 出现或隐藏(只引发重绘!不支持template标签。)
v-show= "数据属性isShow"
// v-for=""... :key=""... 。 根据(array、object、string、number)循环遍历 多个当前元素
【注意,当渲染出的列表项存在“排序”或“过滤”操作时,如果只依赖默认的键生成机制,Vue 可能无法正确追踪和处理变化的列表项。为了避免潜在问题,在使用 v-for 遍历时,不论采用哪种语法形式,遍历哪种数据类型,都需要为每个迭代项提供一个唯一的标识符(可以显式指定 :key 属性,通常绑定object的键、或array的索引)以确保每个迭代项的正确识别和更新。】
》v-for遍历number(渲染页码、有标号的列表项等场景)
<ul>
<li v-for="index in todoCount" :key="index">Todo Item {{ index }}</li>
</ul>
<!--1 2 3 4 5-->
<span v-for=" item in 5">{{item}}</span>
》v-for遍历object
<div v-for="(value, key) in obj" :key="key">
Key: {{ key }} | Value: {{ value }}
</div>
或者(若需要在循环中获取 “循环索引(迭代次数)”),
<div v-for="(value, key, index) in obj" :key="key">
Index: {{ index }} | Key: {{ key }} | Value: {{ value }}
</div>
》v-for遍历array
<div v-for="(item, index) in array" :key="index">
{{ index }}: {{ item }}
</div>
【补充:
1.v-for="(value, key, index) in obj" 语法可以用于遍历数组(在 js中,数组是一种特殊类型的对象)。
在这种情况下,value 会被绑定到数组的“元素”,key 会被绑定到元素的“索引”,index 则会被绑定到循环中的“迭代次数”。
但这样做意义不大,通常情况下,人们更常用传统的 v-for="(item, index) in array" 语法来遍历数组。
2.在vue3中,为了更好地与 js的遍历语法保持一致,可使用 of 作为遍历元素的分隔符(与使用in没有差别):
<div v-for="(item, index) of array" :key="index">
{{ index }}: {{ item }}
</div>
】
》v-for遍历string
<div v-for="(char,index) in `zhangsan`" :key="index">
{{ char }}: {{index }}
</div>
--------------------------------------------------------------------------------------------------------------
【data属性】
数据属性是指在Vue的data属性中定义的基本数据类型(如字符串、整数等)、数组、对象和对象数组。这些属性都是响应式的,也就是说,当修改data中的属性时,页面一般都能实时更新。
【computed属性】
1.计算属性,是指在Vue实例的computed中定义的函数,与methods类似,都是定义函数。计算属性会自动追踪响应式依赖,当依赖的数据发生变化时,计算属性会重新计算,并且缓存结果。获取数据时直接从缓存中获取,效率更高。
2.计算属性中不能写异步代码,即便有也不起作用。
3.同一vm实例中的data数据属性,与计算属性 不能同名!!
4.关于不同的书写方式:
computed: {
// 计算属性 常用写法 第1种写法 使用较多(函数形式)
// allName(){
// return this.firstName + this.lastName
// }
// 计算属性的第2种写法 (对象形式), 里面有两个方法 get,set
// 使用计算属性时,走get ;设置计算属性时,走set
// allName: {
// get(){
// console.log('get....')
// // return 111
// return this.firstName + this.lastName
// }
// }
// 以下为计算属性的“完整写法”
allName: {
get(){
console.log('get....');
return this.firstName + this.lastName
},
// 当去修改计算属性值时 ,项目中很少通过set修改,一般会直接修改return的值。
set(val){
console.log("set...",val)
// 当allName值发生改变的时候 才会走set
let arr = val.split('-')
this.firstName = arr[0]
this.lastName = arr[1]
}
// vm 也会代理计算属性中的数据,即可使用vm.allName="newName"修改计算属性!
}
}
--------------------------------------------------------------------------------------------------------------
【watch侦听器】
每当所侦听的数据发生变化,便会自动调用对应的回调函数,进行下一步操作。
比如:
watch:{
//侦听器第一种写法 (函数方式定义侦听器)
// keyword 数据发生了改变,自动执行
/* keyword(){
// 数据改变了,做一些事
console.log("kw发生了改变");
} */
//侦听器的第二种写法(对象方式定义侦听器 -可以做一些其他的配置,如深度监听、立即执行)
keyword: {
deep: true,// 深度监听(当数据属性为复杂数据类型时,并不能直接监听到数据的改变)
immediate: true,// 立即执行 (首次即监听)
// handler 每当监听到数据改变,便会自动执行handler 方法
handler(){ //这里必须使用 handler 作为函数属性名
console.log("kw发生了改变");
}
}
}
--------------------------------------------------------------------------------------------------------------
【computed和watch的比较】
相同点:
都以Vue的依赖追踪机制为基础,当所依赖的数据发生变化,均会自动调用相关的函数进行下一步操作。
不同点:
computed是及时更新变化值,watch是监听一个值的变化而执行对应的回调。
computed函数所依赖的属性不变的时候会调用缓存;watch每次监听的值发生变化时候都会调用回调。
异步函数在computed中无效,在watch中有效。
computed必须有return;watch可以没有。
--------------------------------------------------------------------------------------------------------------
【filter属性】
已知data:{message:'1'},数据属性
自定义过滤器,被用作一些常见的文本格式化。由"管道符"指示, 格式如下:
(注意:capitalize、formatId均为 过滤器函数。message与 rawId均为 数据属性中的数据,作为传入 过滤器函数 的 首个参数)
<!-- 在两个大括号中 -->
{{ message | capitalize }}
<!-- 在 v-bind 指令中 -->
<div v-bind:id="rawId | formatId"></div>
过滤器可以串联使用:
(解释:message 首先会作为 filter1的第一个参数,“2a”则为第二个参数。filter1返回的结果会作为filter2的第一个参数。)
// 12A
{{ message | filter1("2a") | filter2 }}
filters: {
filter1(value, arg) {
return value.toString() + arg
},
filter2(value) {
return value.toUpperCase();
}
}
--------------------------------------------------------------------------------------------------------------
【ref属性】
相同的 ref 可以在一个组件内重复使用,对应多个元素。
例如:
<div ref="foo"></div>
<button ref="foo"></button>
访问的时候:
this.$refs.foo // 是一个数组 [div, button]
--------------------------------------------------------------------------------------------------------------
【补充:通过对象属性访问运算符(即打点的方式)重置样式的话, 只能改变数据,但不能实时更新视图层!实现响应式属性添加或修改的方法有两个:】
方法一(推荐,能利用vue的数据响应):vue实例中调用this.$set(this.user, 'gender', 'female'); 或直接全局调用Vue.set(this.user, 'gender', 'female');
方法二(不推荐):vue实例中调用this.$forceUpdate()
【:class值的 类型(即动态类名的不同写法)、class的动态绑定】
1.动态类名的不同写法(除非类名只有一个单词,且作为对象形式中的“键”时可不加 引号。其它必须加 引号)
写法1
:class="pink",pink为data:{pink:true}中的pink,本质为一个 数据属性。【注意!没有进行v-bind数据绑定的类class="pink",中的pink为类名,与此不同】
写法2【要使用 实现响应式属性添加或修改的方法】
对象形式(pink为类名)
:class="{'check-bgd': true}"
当只有一个单词 作为对象的 键 时,可不加 引号''
:class="{'check': true}",或者:class="{check: true}"
写法3
数组形式(base-class、active-class、highlighted-class均为类名)
<template>
<div :class="getDynamicClassNames"></div>
</template>
<script>
export default {
data() {
return {
isActive: true,
isHighlighted: false
};
},
methods: {
getDynamicClassNames() {
return ['base-class', { 'active-class': this.isActive, 'highlighted-class': this.isHighlighted }];
}
}
};
</script>
2.class的动态绑定
目标class名为pink,数据属性selected的初始值为 -1。
handle(index){
this.selected = index
}
<li v-for="(item,index) in array" @click="handle(index)" :class="{ 'pink': selected == index }">
{{ item.name }}
</li>
解释:点击当前元素,将 当前元素的 index值 赋值给 数据属性selected,此时selected == index为true!即pink类生效。
--------------------------------------------------------------------------------------------------------------
【:style值(动态 行内样式)的 不同写法】
let vm = new Vue({
el: "#app",
data() {
return {
name: "zhangsan",
obj: {
width: '200px',
height: '200px',
backgroundColor: 'pink'
}
}
},
methods: {
hander() {
this.obj.fontSize = '30px';
// 强制更新视图层
this.$forceUpdate()
// this.$set(targer,key,value)
// this.$set(this.obj,'fontSize','30px')
}
}
});
写法1【要使用 实现响应式属性添加或修改的方法】
对象形式(写对象时 键名 可以不用引号,值 必须使用引号 如果 键名中 有“连接符 - ”需要转为小驼峰命名法)
<div :style="{ width: '200px',height:'200px',backgroundColor: 'red' }">yigekuaiyaunsu</div>
写法2
数组形式
<!-- 动态行内样式数组写法 -->
<div :style="[{width: '200px',height:'200px'},{ background: 'pink' }]">yuansu </div>
--------------------------------------------------------------------------------------------------------------
【非单文件 组件】
组件使用分为三步骤:
1) 定义大VC,即VueComonse组件构造器!Vue.extend() 是 Vue.js 提供的一个方法,用于创建可复用的组件构造器。它接收一个选项对象,并返回一个组件构造器,可以通过这个构造器创建组件实例。
VueComponent(options) {
this._init(options);
}
2) 注册组件 ,全局注册,局部注册
3)使用组件, 在哪里注册,就在对应的模版上使用, 像使用标签一样使用一个组件
<!-- 定义的容器 -->
<div id="app">
<hello-world></hello-world>
<box1></box1>
<box2 />
</div>
》》注意:
1.命名规范:
(变量)组件名:大驼峰命名法;
模板中的 组件标签:不区分 大小写(最好都小写),单词间使用连字符 "-" 连接。【在.vue文件中的自定义组件使用时可以大驼峰,但在.html中必须(每个单词都小写,单词间使用连字符 "-" 连接)】
2.全局注册组件,(无需 在组件构造器中的 components选项 单独注册,可直接在任意一个组件的template中使用)
let HelloWorld = { template: `<div>Malu组件</div>`}
Vue.component('HelloWorld',HelloWorld)
3.let Box1 = Vue.extend({ template: '<h1> 这是一个标题 </h1>' });
可简写为:let Box1 = { template: '<h1> 这是一个标题 </h1>' };
示例:
let Box2 = {//在当前组件的components选项中 局部注册 子组件后,可在当前template: 中以标签形式直接使用。
template: `<div>
这是box2组件
<hr />
<box1></box1>
<hello-world /> <!--全局注册组件HelloWorld,使用前 不再需局部注册!!!-->
</div>`,
components:{//局部注册组件,为 键值对 形式,可简写为一个值(全局注册时不可简写!)
Box1
}
}
//根组件
let vm = new Vue({
el: "#app",
components: {//注册 子组件
Box2,
Box1
}
});
// 结论:
// Vue.extend() 会返回一个构造器VC ,是一个类(组件构造函数VueComponent())
// Vue内部 改变了大VC的原型对象 ,让大VC 的 prototype指向一个实例对象 vm2 【其构造函数为Vue()】
// new 这个类,得到一个 组件对象 叫小vc
// 每一个小vc __proto__ 都指向 vm2实例对象
// 在Vue 的原型对象上有很多API ,不管小vm __proto__能访问API,
// 我们的小vc 也可以访问这些API 通过两次__proto__能访问API
// 我们的 构造器VC.prototype.__proto__也能访问API
--------------------------------------------------------------------------------------------------------------
【vue父子 组件通信】
注意:事件监听最好放在原生dom上,只通过props为子组件传递已定过义的方法或数据属性值
》1.“父组件” 通过在 子组件标签上, 通过 v-bind单项绑定自定义属性或方法名。子组件通过自身的props选项接收到父组件传递过来的 属性名或方法名后,便可以直接使用从父组件传过来的对应 属性值、或调用其方法。
》2.“子传父” 通过在 子组件标签上, 使用@“订阅”自定义事件。子组件通过this.$emit("父组件的自定义事件名称",payload)触发自定义事件。payload为传递给父组件的参数,不限内部数据类型(若有多个数值,便放到一个对象里)
3.$attrs/$listeners:(多级组件嵌套只需要传递数据时,不需对数据做处理)
$attrs:包含了父作用域中不被 prop 所识别 (且获取) 的特性绑定 (class 和 style 除外)。当一个组件没有声明任何 prop 时,这里会包含所有父作用域的绑定 (class 和 style 除外),并且可以通过 v-bind="$attrs" 传入内部组件。通常配合 interitAttrs 选项一起使用。
$listeners:包含了父作用域中的 (不含 .native 修饰器的) v-on 事件监听器。它可以通过 v-on="$listeners" 传入内部组件。
4.$parent / $children与 ref。(无法跨级或兄弟间通信。)
ref:如果在普通的 DOM 元素上使用,引用指向的就是 DOM 元素;如果用在子组件上,引用就指向组件实例。
子组件:
data () {
return {
title: 'Vue.js'
}
},
methods: {
sayHello () {
window.alert('Hello');
}
}
父组件:直接通过引用子组件,访问子组件上的方法和数据属性
mounted () {
const comA = this.$refs.comA;
console.log(comA.title); // Vue.js
comA.sayHello(); // 弹窗Hello
}
5.provide 与 inject。绑定并不是可响应的。这是刻意为之的。然而,如果你传入了一个“可监听的对象”,那么其对象的属性还是“可响应的”。
【补充:创建响应式数据的方法】
使用 ref 或 reactive 创建的响应式对象都可以实现数据的响应式更新。但需要注意的是,在 Vue 3 中,推荐使用 ref 来创建基本类型(如数字、字符串等)的响应式对象,而使用 reactive 来创建复杂对象或嵌套对象的响应式对象。而在 Vue 2 中,只能使用 reactive 创建响应式对象。
//先引入, 同理,使用ref也是先,import { ref } from 'vue';
import { reactive } from 'vue';
const state = reactive({ count: 0, name: 'John' }); // 将普通对象转换为响应式对象
console.log(state.count); // 输出:0
state.count++; // 修改响应式对象的值
console.log(state.count); // 输出:1
我们通常使用 ref 或 reactive 来创建响应式数据,但有时候我们需要在“组件外部”定义一些数据,并将其传递给组件及其子孙组件。Vue.observable 是用于在组件外部创建可响应的数据的方法,可以与 provide 配合使用,将响应式数据传递给组件及其子孙组件,实现优化的响应式数据共享。
组件外部:建了一个可响应的对象
import { Vue } from 'vue';
const sharedState = Vue.observable({
count: 0,
});
export default sharedState;
父组件:在父组件中使用 provide 提供这个可响应的对象给子孙组件
import sharedState from './sharedState.js';
export default {
provide() {
return {
sharedState: sharedState,
};
},
// ...
};
子组件:子孙组件可以通过 inject 来访问这个响应式对象,并在组件中使用:
export default {
inject: ['sharedState'],
// ...
};
【vue兄弟 组件通信】
》1.Bus事件总线:充当中介者,用于在兄弟组件之间传递消息。
// EventBus.js
import Vue from 'vue';
export const EventBus = new Vue();
组件一,引入 事件总线:
import { EventBus } from '@/EventBus.js';
methods: {
updateData() {
// 使用事件总线 触发事件,并传递数据给兄弟组件
EventBus.$emit('dataUpdated', 'Updated data from Child A');
},
},
组件二, 引入 事件总线:
import { EventBus } from '@/EventBus.js';
created() {
// 监听 事件总线上的事件,并更新数据
EventBus.$on('dataUpdated', (data) => {
this.sharedData = data;
});
},
----------------------------------------------------------------
1.vue中模板中 不需要this. 访问数据属性或方法;method、computed中需要this
2.// DOM 更新是异步的!当 Vue 数据发生变化、或者 动态增删改dom时,Vue 会等待队列中所有的 DOM 更新操作完成,然后才会执行 $nextTick() 方法中的代码。
this.$nextTick(() => {
// 通过子组件上的ref获取点击的, 子组件实例this.$refs.item[index],在通过组件实例上的$el方法得到对应的input,即dom节点,使其focus()聚焦。
targetEle.focus();
});
----------------------------------------------------------------------
【vuex】只在vue2中使用
【 this.$store.replaceState(obj)】
created() {
//从数据库中获取数据,放入state【getData为挂载Vue原型对象上的方法,用于从 本地存储 获取数据】
const todoData = this.$getData();
if (todoData) {
注意:replaceState()会将todoData的"引用"赋值给this.$store.state!!!
this.$store.replaceState(todoData);
}
window.addEventListener('beforeunload', () => {
//由于this.$store.state的地址通过replaceState()指向了todoData,所以,
this.$saveData(todoData);//与 this.$saveData(this.$store.state);等效!!!
});
},
【this.$store.dispatch()】
actions中的函数定义时:有两个参数【context必选、payload可选】,
//context【是一个包含了 Vuex store 的上下文对象,它包含了一些核心的属性和方法,如 state、getters、commit、dispatch 等,你可以通过解构赋值或直接使用 context 对象来访问这些属性和方法。】
//payload【为接收的参数,可以是个对象。通过 store.dispatch('actionName', payload) 来传递】
触发异步actions中函数的方式:
// vue实例methods中:this.$store.dispatch('changeOption');
// vue实例templete中:$store.dispatch('changeOption');
【dispatch()。参数一,必选!为要触发的 Action 的名称,为字符串。参数二payload,可选,异步操作时传递给 Action函数的数据】
---------------------------------------------------------------------------
【路由】
------------------------------------------------
// 总结
// 1. 安装路由 [email protected]
// 2. 在src 文件夹下创建文件 router文件,有index.js
// 3. new 路由器对象,路由器中配置很多路由规则,并导出
// 4. 在main.js 中,引入路由器对象,挂载到根组件上
// 5. vue-router 四个核心 两个对象 两个组件 (路由器对象router、路由对象route、路由出口 router-view 类似于a标签router-link )
----------------------------------------------------
// npm i [email protected]
【router/index.js】
import VueRouter from 'vue-router'
import Vue from 'vue'
//注册路由
Vue.use(VueRouter)
一进来,就导入所有组件,组件太多时性能比较低
import Home from '../components/Home.vue'
import About from '../components/About.vue'
import Malu from '../components/Malu.vue'
// 在一个路由器中,可以配置n个路由 ,一个路由对应一个组件
let router = new VueRouter({
// routes 配置路由对象
routes: [
// 重定向 redirect
{ path: "/", redirect: "/malu" },
{ path: "/home", component: Home }, // 一个路由就是一个对象
{ path: "/about", component: About },
{ path: "/malu", component: Malu },
// 当url匹配到对应的组件后,需要把这个组件放到某个地方, 叫路由出口 router-view
// 以上的组件都没有匹配到,就到404页面 就会匹配这个*
{ path: "*", component: NotFount },
],
})
// 把路由器导出
export default router
【路由懒加载配置、路由传参】
import Home from '../components/Home.vue' // 首页不需要懒加载
let router = new VueRouter({
// mode 指定路由模式 默认是hash模式 #
//mode: history 路由 ,没有 # url 看起来好看
// history 模式更改的是url ,url改变了会导致浏览器向服务器发起请求, 这里需要再 服务端做处理
// hash 模式下 ,前端路由修改的是#后面的信息 hash值的切换,不会导致浏览器重新发请求
routes: [
// 重定向 redirect
// 使用场景: 常见的场景 ,如果点击进某个商品页面,商品页面需要登录,如果没有登录,重定向到登录页面
{ path: "/", redirect: "/home" },
{ path: "/home", component: Home }, // 一个路由就是一个对象
// 当匹配到对应的path ,再加载对应的组件 ;如果没有匹配到,就不加载
{ path: "/malu", component: ()=> import('../components/Malu.vue') },
{ path: "*", component: ()=> import('../components/NotFount.vue')},
{ name: 'about', path: '/about/:id?/:name?',component: About }, // 动态路由,在路由对象中的params 中能看参数id,name值
//?查询字符串传参 ,?后面的参数 在 在路由对象中 query 能查到
// http://localhost:8080/#/about?name=zhangsan&sex=%E7%94%B7
],
});
【编程式路由 写法:】
一,用于向 history 栈添加一个新的记录,实现正常的页面导航。
1.字符串
this.$router.push('/about?age=22');// push 后面直接跟,字符串
this.$router.push({ path: '/about/001/zhangsan'}) //跳转路由,params形式传参
2.对象
this.$router.push({ path: '/about'}) // 对象跳转,不传参
this.$router.push({ name: 'about' ,params: { age: 22 }})//对象传参时,name 路由的“唯一标识符”
this.$router.push({path: '/about',query: {address: '河南'}})// 跳转路由,query形式传参
this.$router.push({name: 'about',query: {address: '河南'}})
二,this.$router.replace('/about')// 替换当前的页面 ,不会生成history 历史记录
三,this.$router.go(-1)。用于在 history 记录中向前或向后移动 n 步,n 可以是正数(向前)或负数(向后)。只能在用户的浏览历史记录中有可导航的前一个页面时有效,否则不会触发任何导航操作。
》》补充:
// 宏任务保证获取当前完整路径
// setTimeout(() => {
// console.log(router.currentRoute.fullPath)
// }, 0)
【app 组件中】
<template>
<div id="app">
<h1>app组件</h1>
<!-- router-link 声明式导航 to="跳转的页面" -->
<router-link to="/home">home页面</router-link>
<router-link to="/about">about页面</router-link>
<router-link to="/malu">码路</router-link>
<!-- 路由出口 把匹配到的组件放到路由出口中 -->
<router-view></router-view>
</div>
</template>
【子组件Home】
<template>
<div>
<h1>Home组件</h1>
<router-link to="/home/persion">个人中心</router-link>
<router-link to="/home/setting">设置中心</router-link>
<!-- 设置路由出口 表示 设置“二级路由”出口 -->
<router-view></router-view>
</div>
</template>
【main.js】
import Vue from 'vue'
import App from './App.vue'
import router from './router/index.js'
Vue.config.productionTip = false
// 和vuex 一样,将导入的路由器挂载到根组件上,这样所有的页面都可以使用路由器
new Vue({
router,
render: h => h(App),
}).$mount('#app')
【router导航守卫】
// 全局的导航守卫
router.beforeEach((to, from, next) => {
// to 去哪里 即将进入的route对象
// from 从哪来 即将离开的route对象
// next vue2 通过next 决定如何跳转 next 放行
// next()
// 以上的页面 home about mine 都需要登录 ,如果用户没有登录 ,不让用户访问这些页面
// 直接跳转到登录页面
let isLogin = localStorage.getItem('login');//登录
console.log(isLogin);
if (to.path != '/login1') {
console.log("去的页面不是登录页面");
// 去的页面不是login 判断账号是否登录
if (isLogin) {
// 已登录
next()
} else {
next('/login1')
}
} else {
// 去登录页面 放行
// next()
// 添加判断 判断账号是否登录,如果登录了 ,直接进入主页面
if (isLogin) {
next('/home')
}
}
})
--------------------------------------------------------------------------------
【vue钩子函数 生命周期,四个阶段:】
》创建阶段
beforeCreate(该阶段 初始化了一个空vue实例对象,该实例拥有部分 默认生命周期函数和事件)、
created(该阶段data中数据和methods中的方法已初始化完成)
》挂载阶段
【关于 Vue将模板编译成 渲染函数,再根据数据生成 虚拟dom,并挂载到指定节点 的过程】
import Vue from 'vue'
import App from './App.vue'
new Vue({//main.js中没有template,须显示配置render渲染函数
router,
render: h => h(App)
}).$mount('#app')
1.先指定将要被虚拟dom 替换 掉的真实dom节点【vm实例中配置"el选项",或vm实例最外层使用 .$mount('#app')指定】。
2.如果 Vue 实例中同时存在 el 和 template 选项("选项优先级高"!或外部有<templete>标签),那么 template 选项中(或外部<templete>标签中)的内容将被编译成渲染函数,并用于生成虚拟 DOM。然后这个虚拟 DOM 将会被挂载到 el 元素上,替换掉 el 元素(包括其自身)。
3.解释render: h => h(App)
h(App) 返回一个虚拟 DOM(VNode):h 函数(createElement 函数的别名,用于创建虚拟dom)会根据组件选项 App 创建一个虚拟 DOM,该虚拟 DOM 将包含了组件的结构和数据等信息。其实是,使用组件选项 App 创建一个虚拟 DOM,并将其作为根组件进行渲染。
beforeMount(已将模板编译转换成 渲染函数,渲染函数再根据数据生成的 虚拟dom。还未挂载到真实dom上)
mounted(内存中生成的虚拟dom,已经被挂载到了真实dom上)
【vm实例创建、挂载阶段,具体流程:】
>初始化 Vue 实例:当你创建一个 Vue 实例时,Vue 会将选项对象中的各种属性(比如 data、methods、computed 等)合并到 内部选项对象。
>如果组件有定义 template 选项,Vue 将把模板编译成渲染函数。如果没有 template 选项但有 render 选项,Vue 将直接使用 render 函数,它将直接返回组件界面的虚拟 DOM(是一个 JavaScript 对象,它描述了组件的结构和状态),而不需要模板编译过程,这就是所谓的 "渲染函数"。
>将虚拟 DOM 渲染到真实 DOM 上:Vue 实例在初始化完成后,会调用 mount 方法,将虚拟 DOM 渲染到真实 DOM 上。这时,Vue 会在挂载点(el 属性指定的元素)上创建一个真实的 DOM 树,并将虚拟 DOM 中的内容映射到这个真实 DOM 树上。
》更新阶段
beforeUpdate(data中的数据已经改变,但还未渲染到页面)
updated(data中的数据已经改变,已渲染到页面)
》销毁阶段
beforeDestroy(准备进入销毁阶段)
destroyed(已经解除事件绑定、事件监听,已销毁子组件。数据,指令等都无法使用)
--------------------------------------------------------------------------------
【自定义指令】
》App.vue中
<template>
<div id="app">
<div v-lower="'NAme'"></div><!--name -->
<input type="text" v-focus>
</div>
</template>
1.》main.js中,全局 自定义指令
Vue.directive('lower', (element, options) => { // 转小写
element.innerHTML = options.value.toLowerCase()
})
Vue.directive('focus', { // 获取输入框焦点的 指令