0%

Next版本为7.8.0,找到 /themes/next/_config.yml 文件,搜索 font 关键词,这里注意一定要将 font 里面的 enable 设置为true, 因为默认为false,然后设置 family 为自己想要的,如果family在其他平台找的,就需要修改host。(只是修改了family, 没修改enable就会不生效,所以一定一定要记得)

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
font:
enable: true

# Uri of fonts host, e.g. https://fonts.googleapis.com (Default).
host:

# Font options:
# `external: true` will load this font family from `host` above.
# `family: Times New Roman`. Without any quotes.
# `size: x.x`. Use `em` as unit. Default: 1 (16px)

# Global font settings used for all elements inside <body>.
global:
external: true
family: Noto Serif SC
size:

# Font settings for site title (.site-title).
title:
external: true
family: Noto Serif SC
size:

# Font settings for headlines (<h1> to <h6>).
headings:
external: true
family: Noto Serif SC
size:

# Font settings for posts (.post-body).
posts:
external: true
family: Noto Serif SC

# Font settings for <code> and code blocks.
codes:
external: true
family: Source Code Pro

代码可复制,还是上面同一个文件,找到以下代码段,将copy_button的enable设置为true,启用就可以啦。show_result设置true后,复制完会显示一个勾。style对应的copy_button不同的样式,看个人喜好来~

1
2
3
4
5
6
7
8
9
10
11
12
codeblock:
# Code Highlight theme
# Available values: normal | night | night eighties | night blue | night bright | solarized | solarized dark | galactic
# See: https://github.com/chriskempson/tomorrow-theme
highlight_theme: night eighties
# Add copy button on codeblock
copy_button:
enable: true
# Show text copy result.
show_result: true
# Available values: default | flat | mac
style: mac

前端脱离框架去开发,就是不使用任何前端框架(Vue、React等)的情况下,直接用js、html和css去开发web。这意味更能深入了解前端底层原理和机制,避免依赖框架。

在面对静态且不做过多功能的低项目下,其实脱离框架就已经足够了。框架编写的代码必须要进行编译才能在浏览器中渲染,所以便有了webpack。像Vue的出现就是为了解决一些痛点问题,比如双向数据流、自动响应式更新等,但我们又不得不遵守他们框架内的一些语法,随着vue的升级,我们也要不停学习更新的框架内容。框架本身是有利于开发的,但面对一些简单的问题时,不用框架反而是明智的。根据需求而决定去不去用框架进行下一步的开发。

当开发多个类似的网站,没有打包过的代码放上服务器,能直接在服务器改源代码。而使用框架后打包的源码放在服务器中,要修改却只能本地修改,反而更浪费时间。当熟悉纯js开发后,还能开发一些工具以及框架出来。

Vue 中如何正确插入 <script> 脚本呢

在实际开发中,我们经常会接入第三方广告平台、统计工具或客服系统,而这些服务通常会提供一整段包含 <script> 标签的代码。
但如果你在 Vue 的模板中(<template>)直接写 <script>,不仅不会生效,还可能直接报错。

那么 为什么不能在 Vue 模板里直接插入 <script>,以及 如何正确动态插入脚本 呢,接下来笔者详细讲一下。

一、为什么 Vue 中不能直接写 <script>

Vue 单文件组件需要结构清晰,它本身不支持template中加入script,结构主要是包含以下几个:

  • <template>:写结构
  • <script>:写逻辑
  • <style>:写样式

当你在 <template> 中插入 <script> 时,会发生一些问题,比如:

  1. Vue 模板会自动移除或忽略 <script> 标签
  2. 会破坏 Vue 的编译流程,可能直接报错
  3. 导致第三方广告脚本、统计脚本完全无法执行

所以当我们想要让脚本正常工作,我们只能在组件的生命周期中 通过 DOM API 动态插入 <script> 标签

二、错误示例:直接粘贴广告代码(不可行)

例如你拿到的广告商提供的代码是这样的:

1
2
3
4
5
6
7
8
9
10
<script type="text/javascript">
atOptions = {
'key' : '1523b7ddb936e560aadef64a851d500c',
'format' : 'iframe',
'height' : 250,
'width' : 300,
'params' : {}
};
</script>
<script type="text/javascript" src="//www.highperformanceformat.com/1523b7ddb936e560aadef64a851d500c/invoke.js"></script>

如果你把这些代码放进 Vue 的 <template> 区域,广告绝对不会显示

三、正确做法:在 Vue 中动态插入脚本

核心思路:

  1. 在模板中放一个容器(如 <div id="de_v"></div>
  2. onMounted(或 mounted)生命周期中创建 <script> 标签
  3. 把参数脚本 + 外链脚本动态插进去

四、完整代码示例(Vue 3 + <script setup>

假设你的广告要插入到 id 为 "de_v" 的容器里:

template 部分

1
2
3
<template>
<div id="de_v"></div>
</template>

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
<script setup>
import { onMounted } from "vue";

onMounted(() => {
const targetDiv = document.getElementById("de_v");
if (!targetDiv) return;

// 创建第一个 script(广告参数)
const script1 = document.createElement("script");
script1.type = "text/javascript";
script1.innerHTML = `
atOptions = {
'key' : '1523b7ddb936e560aadef64a851d500c',
'format' : 'iframe',
'height' : 250,
'width' : 300,
'params' : {}
};
`;

// 创建第二个 script(外部广告脚本)
const script2 = document.createElement("script");
script2.type = "text/javascript";
script2.src = "//www.highperformanceformat.com/1523b7ddb936e560aadef64a851d500c/invoke.js";

// 插入脚本到容器内
targetDiv.appendChild(script1);
targetDiv.appendChild(script2);
});
</script>

五、常见问题与注意事项

1. 组件多次挂载会重复插入脚本?

如果组件会反复进入/离开页面,建议:

  • 在插入前检查是否已添加脚本
  • 或在 onUnmounted 中移除它们

2. 多个广告位怎么办?

每个广告位使用 不同的容器 ID 即可,例如:

1
2
<div id="ad1"></div>
<div id="ad2"></div>

脚本注入时改对应的 id 即可。

3. SSR 框架(Nuxt)如何处理?

服务端无法访问 document,必须放到客户端生命周期中执行,例如:

1
onMounted(() => { ... })

否则会报 document is not defined

六、总结

  • Vue 模板中不能直接写 <script>,否则脚本不会执行
  • 正确方式是在生命周期里 动态创建 script 标签
  • 通过 appendChild() 可以正常加载广告脚本、统计脚本、第三方插件等
  • 本文提供的示例可直接用于实际项目

只要按照本文方法插入脚本,不论是广告代码、统计代码还是第三方 SDK,都能正常在 Vue 环境中运行。

最近接到一个需求,这个第三方接口获取到的数据,它的response为csv文档时,该如何处理?
其实可以先把csv文档保存到本地某路径,再通过fs的createReadStream读取数据,保存到本地数据库。

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
const processConversations = async (dateObj) => {
const username = '你的帐户名';
const password = '密码';

const year = dateObj.format('YYYY');
const month = dateObj.format('MM');
const day = dateObj.format('DD');
const hour = dateObj.format('HH');
const dateStr = `${year}-${month}-${day}-${hour}`;
const fileName = `路径后半部份`;
const url = `https://api/${fileName}`;
const outputPath = `/tmp/conversations`;
const tableName = 'test';


try {
// 下载 CSV
const response = await axios.get(url, {
responseType: 'stream',
headers: {
Authorization: `Basic ${Buffer.from(`${username}:${password}`).toString('base64')}`
}
});

const writer = fs.createWriteStream(outputPath);
response.data.pipe(writer);
await new Promise((resolve, reject) => {
writer.on('finish', resolve);
writer.on('error', reject);
});

console.log(' CSV 文件下载成功:', fileName);



const rows = [];

await new Promise((resolve, reject) => {
fs.createReadStream(outputPath)
.pipe(csv())
.on('data', (row) => {
rows.push([
row['DATE_TIME'] ? dayjs(row['DATE_TIME']).format('YYYY-MM-DD HH:mm:ss') : null,
row['CLID'] || null,
row['CLID_SOURCE'] || null
]);
})
.on('end', resolve)
.on('error', reject);
});

if (rows.length === 0) {
console.warn(`CSV 中没有数据: ${fileName}`);
return;
}

const insertSQL = `
INSERT INTO \`${tableName}\` (
date_time, clid, clid_source
) VALUES ?
`;




await query(insertSQL, [rows]);
console.log(`成功插入 ${rows.length} 条记录: ${fileName}`);
} catch (error) {
console.error(`报错 (${fileName}):`, error.message);
}
};

索引的作用

当想要快速查找到自己想要的数据时,可以用索引。通过索引,可以在检索数据时直接定位到,省去时间,就好比一本书前面的目录,能让你快速找到自己想看的内容。如果索引包含多列,那么按照从左到右的顺序,即最左前缀列来进行。

索引的类型

  1. 唯一索引:
    对某些列(比如身份证)添加唯一索引,不能出现两条身份证相同的记录,通过 UNIQUE 关键字我们就添加了一个唯一索引。
1
ADD UNIQUE INDEX uni_sf (sf);

也可以给列添加唯一约束,而不创建唯一索引,列具有唯一性。

1
ADD CONSTRAINT uni_name UNIQUE (name);
  1. 主键索引:
    主键一旦创建,就会自动带上索引。这也是为什么主键搜索数据会更快的原因。一个表只能有一个主键,一个主键可以包含多列,即联合主键。
1
ALTER TABLE table_name ADD PRIMARY KEY (column);
  1. 普通索引:
    任何一列都可以创建索引,建议只在自己经常想要检索的列上加索引,不要全部加,会耗性能。
1
ALTER TABLE table_name ADD INDEX index_name (column);

测试、UI要求的会比较高,比如滑动图标改成自定义图片等,所以传统的@react-native-community/slider已经不能满足所需,所以采取自定义形式,在网上找了很久才发现的一个宝藏依赖:https://github.com/miblanchard/react-native-slider

组件代码如下:

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
import React, { PureComponent } from 'react';
import {
Animated,
Easing,
I18nManager,
Image,
ImageSourcePropType,
LayoutChangeEvent,
PanResponder,
PanResponderInstance,
View,
ViewStyle,
} from 'react-native';
// styles
import { defaultStyles as styles } from './styles';
import type { Dimensions, SliderProps, SliderState } from './types';

type RectReturn = {
containsPoint: (nativeX: number, nativeY: number) => boolean;
height: number;
trackDistanceToPoint: (nativeX: number) => number;
width: number;
x: number;
y: number;
};

const Rect = ({
height,
width,
x,
y,
}: {
height: number;
width: number;
x: number;
y: number;
}) => ({
containsPoint: (nativeX: number, nativeY: number) =>
nativeX >= x &&
nativeY >= y &&
nativeX <= x + width &&
nativeY <= y + height,
height,
trackDistanceToPoint: (nativeX: number) => {
if (nativeX < x) {
return x - nativeX;
}

if (nativeX > x + width) {
return nativeX - (x + width);
}

return 0;
},
width,
x,
y,
});

const DEFAULT_ANIMATION_CONFIGS = {
spring: {
friction: 7,
tension: 100,
},
timing: {
duration: 150,
easing: Easing.inOut(Easing.ease),
delay: 0,
},
};

const normalizeValue = (
props: SliderProps,
value?: number | Array<number>,
): Array<number> => {
if (!value || (Array.isArray(value) && value.length === 0)) {
return [0];
}

const {maximumValue, minimumValue} = props;

const getBetweenValue = (inputValue: number) =>
Math.max(Math.min(inputValue, maximumValue), minimumValue);

if (!Array.isArray(value)) {
return [getBetweenValue(value)];
}

return value.map(getBetweenValue).sort((a, b) => a - b);
};

const updateValues = ({
values,
newValues = values,
}: {
values: number | Array<number> | Animated.Value | Array<Animated.Value>;
newValues?: number | Array<number> | Animated.Value | Array<Animated.Value>;
}): Animated.Value[] => {
if (
Array.isArray(newValues) &&
Array.isArray(values) &&
newValues.length !== values.length
) {
return updateValues({values: newValues});
}

if (Array.isArray(values) && Array.isArray(newValues)) {
return values?.map((value: number | Animated.Value, index: number) => {
let valueToSet = newValues[index];
if (value instanceof Animated.Value) {
if (valueToSet instanceof Animated.Value) {
valueToSet = valueToSet.__getValue();
}
value.setValue(valueToSet);
return value;
}

if (valueToSet instanceof Animated.Value) {
return valueToSet;
}

return new Animated.Value(valueToSet);
});
}

return [new Animated.Value(0)];
};

const indexOfLowest = (values: Array<number>): number => {
let lowestIndex = 0;
values.forEach((value, index, array) => {
if (value < array[lowestIndex]) {
lowestIndex = index;
}
});
return lowestIndex;
};

export class Slider extends PureComponent<SliderProps, SliderState> {
constructor(props: SliderProps) {
super(props);
this._panResponder = PanResponder.create({
onStartShouldSetPanResponder:
this._handleStartShouldSetPanResponder,
onMoveShouldSetPanResponder: this._handleMoveShouldSetPanResponder,
onPanResponderGrant: this._handlePanResponderGrant,
onPanResponderMove: this._handlePanResponderMove,
onPanResponderRelease: this._handlePanResponderEnd,
onPanResponderTerminationRequest:
this._handlePanResponderRequestEnd,
onPanResponderTerminate: this._handlePanResponderEnd,
});
this.state = {
allMeasured: false,
containerSize: {
width: 0,
height: 0,
},
thumbSize: {
width: 0,
height: 0,
},
trackMarksValues: updateValues({
values: normalizeValue(this.props, this.props.trackMarks),
}),
values: updateValues({
values: normalizeValue(
this.props,
this.props.value instanceof Animated.Value
? this.props.value.__getValue()
: this.props.value,
),
}),
};
}

static defaultProps = {
animationType: 'timing',
debugTouchArea: false,
trackMarks: [],
maximumTrackTintColor: '#b3b3b3',
maximumValue: 1,
minimumTrackTintColor: '#3f3f3f',
minimumValue: 0,
step: 0,
thumbTintColor: '#343434',
trackClickable: true,
value: 0,
vertical: false,
};

static getDerivedStateFromProps(props: SliderProps, state: SliderState) {
if (
props.trackMarks &&
!!state.trackMarksValues &&
state.trackMarksValues.length > 0
) {
const newTrackMarkValues = normalizeValue(props, props.trackMarks);
const statePatch = {} as SliderState;

if (
state.trackMarksValues
) {
statePatch.trackMarksValues = updateValues({
values: state.trackMarksValues,
newValues: newTrackMarkValues,
});
}

return statePatch;
}
}

componentDidUpdate() {
const newValues = normalizeValue(
this.props,
this.props.value instanceof Animated.Value
? this.props.value.__getValue()
: this.props.value,
);
newValues.forEach((value, i) => {
if (!this.state.values[i]) {
this._setCurrentValue(value, i);
} else if (value !== this.state.values[i].__getValue()) {
if (this.props.animateTransitions) {
this._setCurrentValueAnimated(value, i);
} else {
this._setCurrentValue(value, i);
}
}
});
}

_getRawValues(
values: Array<Animated.Value> | Array<Animated.AnimatedInterpolation>,
) {
return values.map((value) => value.__getValue());
}

_handleStartShouldSetPanResponder = (
e: any,
): /* gestureState: GestureState */
boolean => this._thumbHitTest(e); // Should we become active when the user presses down on the thumb?

_handleMoveShouldSetPanResponder(): /* e, gestureState: GestureState */
boolean {
// Should we become active when the user moves a touch over the thumb?
return false;
}

_handlePanResponderGrant = (e: {nativeEvent: any}) => {
const {thumbSize} = this.state;
const {nativeEvent} = e;
this._previousLeft = this.props.trackClickable
? nativeEvent.locationX - thumbSize.width
: this._getThumbLeft(this._getCurrentValue(this._activeThumbIndex));

this.props?.onSlidingStart?.(this._getRawValues(this.state.values));
};

_handlePanResponderMove = (_e: any, gestureState: any) => {
if (this.props.disabled) {
return;
}

this._setCurrentValue(
this._getValue(gestureState),
this._activeThumbIndex,
() => {
this.props?.onValueChange?.(
this._getRawValues(this.state.values),
);
},
);
};

_handlePanResponderRequestEnd = () =>
/* e, gestureState: GestureState */
{
// Should we allow another component to take over this pan?
return false;
};

_handlePanResponderEnd = (_e: any, gestureState: any) => {
if (this.props.disabled) {
return;
}

this._setCurrentValue(
this._getValue(gestureState),
this._activeThumbIndex,
() => {
if (this.props.trackClickable) {
this.props?.onValueChange?.(
this._getRawValues(this.state.values),
);
}

this.props?.onSlidingComplete?.(
this._getRawValues(this.state.values),
);
},
);

this._activeThumbIndex = 0;
};

_measureContainer = (e: LayoutChangeEvent) => {
this._handleMeasure('_containerSize', e);
};

_measureTrack = (e: LayoutChangeEvent) => {
this._handleMeasure('_trackSize', e);
};

_measureThumb = (e: LayoutChangeEvent) => {
this._handleMeasure('_thumbSize', e);
};

_handleMeasure = (
name: '_containerSize' | '_trackSize' | '_thumbSize',
e: LayoutChangeEvent,
) => {
const {width, height} = e.nativeEvent.layout;
const size = {
width,
height,
};

const currentSize = this[name];

if (
currentSize &&
width === currentSize.width &&
height === currentSize.height
) {
return;
}

this[name] = size;

if (this._containerSize && this._thumbSize) {
this.setState({
containerSize: this._containerSize,
thumbSize: this._thumbSize,
allMeasured: true,
});
}
};
_getRatio = (value: number) => {
const {maximumValue, minimumValue} = this.props;
return (value - minimumValue) / (maximumValue - minimumValue);
};
_getThumbLeft = (value: number) => {
const {containerSize, thumbSize} = this.state;
const {vertical} = this.props;

const standardRatio = this._getRatio(value);

const ratio = I18nManager.isRTL ? 1 - standardRatio : standardRatio;
return ratio * ((vertical ? containerSize.height : containerSize.width) - thumbSize.width);
};
_getValue = (gestureState: {dx: number, dy: number}) => {
const {containerSize, thumbSize, values} = this.state;
const {maximumValue, minimumValue, step, vertical} = this.props;
const length = containerSize.width - thumbSize.width;
const thumbLeft = vertical ? this._previousLeft + (gestureState.dy * -1) : this._previousLeft + gestureState.dx;
const nonRtlRatio = thumbLeft / length;
const ratio = I18nManager.isRTL ? 1 - nonRtlRatio : nonRtlRatio;
let minValue = minimumValue;
let maxValue = maximumValue;

const rawValues = this._getRawValues(values);

const buffer = step ? step : 0.1;

if (values.length === 2) {
if (this._activeThumbIndex === 1) {
minValue = rawValues[0] + buffer;
} else {
maxValue = rawValues[1] - buffer;
}
}

if (step) {
return Math.max(
minValue,
Math.min(
maxValue,
minimumValue +
Math.round(
(ratio * (maximumValue - minimumValue)) / step,
) *
step,
),
);
}

return Math.max(
minValue,
Math.min(
maxValue,
ratio * (maximumValue - minimumValue) + minimumValue,
),
);
};
_getCurrentValue = (thumbIndex: number = 0) =>
this.state.values[thumbIndex].__getValue();

_setCurrentValue = (
value: number,
thumbIndex: number | null | undefined,
callback?: () => void,
) => {
const safeIndex = thumbIndex ?? 0;
const animatedValue = this.state.values[safeIndex];

if (animatedValue) {
animatedValue.setValue(value);

if (callback) {
callback();
}
} else {
this.setState((prevState: SliderState) => {
const newValues = [...prevState.values];
newValues[safeIndex] = new Animated.Value(value);
return {
values: newValues,
};
}, callback);
}
};

_setCurrentValueAnimated = (value: number, thumbIndex: number = 0) => {
const {animationType} = this.props;
const animationConfig = {
...DEFAULT_ANIMATION_CONFIGS[animationType],
...this.props.animationConfig,
toValue: value,
useNativeDriver: false,
};
Animated[animationType](
this.state.values[thumbIndex],
animationConfig,
).start();
};

_getTouchOverflowSize = (): {
width: number;
height: number;
} => {
const {allMeasured, containerSize, thumbSize} = this.state;
const {thumbTouchSize} = this.props;
const size = {
width: 40,
height: 40,
};

if (allMeasured) {
size.width = Math.max(
0,
thumbTouchSize?.width || 0 - thumbSize.width,
);
size.height = Math.max(
0,
thumbTouchSize?.height || 0 - containerSize.height,
);
}

return size;
};

_getTouchOverflowStyle = () => {
const {width, height} = this._getTouchOverflowSize();

const touchOverflowStyle = {} as ViewStyle;

if (width !== undefined && height !== undefined) {
const verticalMargin = -height / 2;
touchOverflowStyle.marginTop = verticalMargin;
touchOverflowStyle.marginBottom = verticalMargin;
const horizontalMargin = -width / 2;
touchOverflowStyle.marginLeft = horizontalMargin;
touchOverflowStyle.marginRight = horizontalMargin;
}

if (this.props.debugTouchArea === true) {
touchOverflowStyle.backgroundColor = 'orange';
touchOverflowStyle.opacity = 0.5;
}

return touchOverflowStyle;
};
_thumbHitTest = (e: {nativeEvent: any}) => {
const {nativeEvent} = e;
const {trackClickable} = this.props;
const {values} = this.state;
const hitThumb = values.find((_, i) => {
const thumbTouchRect = this._getThumbTouchRect(i);

const containsPoint = thumbTouchRect.containsPoint(
nativeEvent.locationX,
nativeEvent.locationY,
);

if (containsPoint) {
this._activeThumbIndex = i;
}

return containsPoint;
});

if (hitThumb) {
return true;
}

if (trackClickable) {
// set the active thumb index
if (values.length === 1) {
this._activeThumbIndex = 0;
} else {
// we will find the closest thumb and that will be the active thumb
const thumbDistances = values.map((_value, index) => {
const thumbTouchRect = this._getThumbTouchRect(index);

return thumbTouchRect.trackDistanceToPoint(
nativeEvent.locationX,
);
});
this._activeThumbIndex = indexOfLowest(thumbDistances);
}

return true;
}

return false;
};

_getThumbTouchRect = (thumbIndex: number = 0): RectReturn => {
const {containerSize, thumbSize} = this.state;
const {thumbTouchSize} = this.props;
const {height, width} = thumbTouchSize || {height: 40, width: 40};

const touchOverflowSize = this._getTouchOverflowSize();

return Rect({
height,
width,
x:
touchOverflowSize.width / 2 +
this._getThumbLeft(this._getCurrentValue(thumbIndex)) +
(thumbSize.width - width) / 2,
y:
touchOverflowSize.height / 2 +
(containerSize.height - height) / 2,
});
};

_activeThumbIndex: number = 0;
_containerSize: Dimensions | null | undefined;
_panResponder: PanResponderInstance;
_previousLeft: number = 0;
_thumbSize: Dimensions | null | undefined;
_trackSize: Dimensions | null | undefined;

_renderDebugThumbTouchRect = (
thumbLeft: Animated.AnimatedInterpolation,
index: number,
) => {
const {height, y, width} = this._getThumbTouchRect() || {};
const positionStyle = {
height,
left: thumbLeft,
top: y,
width,
};
return (
<Animated.View
key={`debug-thumb-${index}`}
pointerEvents="none"
style={[styles.debugThumbTouchArea, positionStyle]}
/>
);
};

_renderThumbImage = (thumbIndex: number = 0) => {
const {thumbImage} = this.props;

if (!thumbImage) {
return null;
}

return (
<Image
source={
(Array.isArray(thumbImage)
? thumbImage[thumbIndex]
: thumbImage) as ImageSourcePropType
}
/>
);
};

render() {
const {
containerStyle,
debugTouchArea,
maximumTrackTintColor,
maximumValue,
minimumTrackTintColor,
minimumValue,
renderAboveThumbComponent,
renderTrackMarkComponent,
renderThumbComponent,
thumbStyle,
thumbTintColor,
trackStyle,
vertical,
...other
} = this.props;
const {
allMeasured,
containerSize,
thumbSize,
trackMarksValues,
values,
} = this.state;
const interpolatedThumbValues = values.map((value) =>
value.interpolate({
inputRange: [minimumValue, maximumValue],
outputRange: I18nManager.isRTL
? [0, -(containerSize.width - thumbSize.width)]
: [0, containerSize.width - thumbSize.width],
}),
);
const interpolatedTrackValues = values.map((value) =>
value.interpolate({
inputRange: [minimumValue, maximumValue],
outputRange: [0, containerSize.width - thumbSize.width],
}),
);
const interpolatedTrackMarksValues =
trackMarksValues &&
trackMarksValues.map((v) =>
v.interpolate({
inputRange: [minimumValue, maximumValue],
outputRange: I18nManager.isRTL
? [0, -(containerSize.width - thumbSize.width)]
: [0, containerSize.width - thumbSize.width],
}),
);
const valueVisibleStyle = {} as ViewStyle;

if (!allMeasured) {
valueVisibleStyle.opacity = 0;
}

const interpolatedRawValues = this._getRawValues(
interpolatedTrackValues,
);

const minThumbValue = new Animated.Value(
Math.min(...interpolatedRawValues),
);
const maxThumbValue = new Animated.Value(
Math.max(...interpolatedRawValues),
);
const minimumTrackStyle = {
position: 'absolute',
left:
interpolatedTrackValues.length === 1
? new Animated.Value(0)
: Animated.add(minThumbValue, thumbSize.width / 2),
width:
interpolatedTrackValues.length === 1
? Animated.add(
interpolatedTrackValues[0],
thumbSize.width / 2,
)
: Animated.add(
Animated.multiply(minThumbValue, -1),
maxThumbValue,
),
backgroundColor: minimumTrackTintColor,
...valueVisibleStyle,
} as ViewStyle;

const touchOverflowStyle = this._getTouchOverflowStyle();

return (
<>
{renderAboveThumbComponent && (
<View style={styles.aboveThumbComponentsContainer}>
{interpolatedThumbValues.map((value, i) => (
<Animated.View
key={`slider-above-thumb-${i}`}
style={[
styles.renderThumbComponent, // eslint-disable-next-line react-native/no-inline-styles
{
bottom: 0,
transform: [
{
translateX: value,
},
{
translateY: 0,
},
],
...valueVisibleStyle,
},
]}>
{renderAboveThumbComponent(i)}
</Animated.View>
))}
</View>
)}
<View
{...other}
style={[styles.container, vertical ? {transform: [{rotate: '-90deg' }]} : {}, containerStyle]}
onLayout={this._measureContainer}>
<View
renderToHardwareTextureAndroid
style={[
styles.track,
{
backgroundColor: maximumTrackTintColor,
},
trackStyle,
]}
onLayout={this._measureTrack}
/>
<Animated.View
renderToHardwareTextureAndroid
style={[styles.track, trackStyle, minimumTrackStyle]}
/>
{renderTrackMarkComponent &&
interpolatedTrackMarksValues &&
interpolatedTrackMarksValues.map((value, i) => (
<Animated.View
key={`track-mark-${i}`}
style={[
styles.renderThumbComponent,
{
transform: [
{
translateX: value,
},
{
translateY: 0,
},
],
...valueVisibleStyle,
},
]}>
{renderTrackMarkComponent(i)}
</Animated.View>
))}
{interpolatedThumbValues.map((value, i) => (
<Animated.View
key={`slider-thumb-${i}`}
style={[
renderThumbComponent
? styles.renderThumbComponent
: styles.thumb,
renderThumbComponent
? {}
: {
backgroundColor: thumbTintColor,
...thumbStyle,
},
{
transform: [
{
translateX: value,
},
{
translateY: 0,
},
],
...valueVisibleStyle,
},
]}
onLayout={this._measureThumb}>
{renderThumbComponent
? renderThumbComponent()
: this._renderThumbImage(i)}
</Animated.View>
))}
<View
style={[styles.touchArea, touchOverflowStyle]}
{...this._panResponder.panHandlers}>
{!!debugTouchArea &&
interpolatedThumbValues.map((value, i) =>
this._renderDebugThumbTouchRect(value, i),
)}
</View>
</View>
</>
);
}
}

导入部分:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import { Slider } from '所放的上述组件路径'

<Slider
minimumValue={1}
maximumValue={7} //可滑动范围为1到7
minimumTrackTintColor="#0A79C3"
maximumTrackTintColor="#F3F3F3"
step={1} //步长
value={targetDay} //默认值,例如4
onValueChange={targetDays => dispatch({
type: 'SetupWeekTarget/updateState',
payload: { targetDay: targetDays }
})} //滑动触发的事件
trackStyle={{ height: 10, borderRadius: 12 }}
containerStyle={{ width: SCREEN_WIDTH - 90 }}
thumbTintColor="transparent"
thumbImage={require('图片路径')}
/>

详细参数可参考链接:

https://github.com/miblanchard/react-native-slider

最开始我想的是有多少个输入框,就放多少个input,做完会发现有各种bug。比如输入时跳转下个输入框不顺畅,键盘突然消失、操作不丝滑等。
正确思路是一个输入框就够了,然后通过改变样式(间隔、高亮)来实现,就是表面看起来是六个输入框,本质上只有一个input

正确解法:

1
2
3
<view class="w-80 h-100 rad-10 b-a-9 tc f-50" v-for="(item,index)  in 6">
<input class="w-80 h-100 " maxlength="1" type="text" @input="inputListener(index)" :focus="focus && (focusIndex == index)"/>
</view>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
data(){
return{
focus: true,
code:['','','','','',''],// 需要获取焦点的序号
focusIndex: 0
}
}
method:{
// 输入时事件
inputListener(e) {
if (this.focusIndex != e)this.focusIndex=e
if (e < 6) {
this.focus=true,
this.focusIndex=e + 1
}else {
this.focus=false
}
},
}

完整版可参考链接:

https://blog.csdn.net/u011423258/article/details/106683068?spm=1001.2014.3001.5506

什么是序列化

在 TCP 通道里传输的数据只能是二进制形式的,所以将数据结构或对象转化成二进制的形式就叫序列化。反过来就叫反序列化,他们之间的规则就叫协议。

RPC是什么

全称是Remote Procedure Call, 中文名字叫远程过程调用。大部份的 RPC 是基于TCP的。具体实现流程如下:

1、前端通过 Proxy 代理调用相应的接口, Proxy 将 RPC 的服务名,方法名,参数等信息转换成 RPC Request 对象,交给 RPC 框架
2、RPC 框架通过 RPC 协议将 RPC Request 对象序列化成二进制形式
3、RPC 通过 TCP 通道把二进制数据传递给后端
4、后端将二进制数据反序列化成 RPC Request 对象
5、后端将反序列化后的数据对应到自己写的方法内,传入参数,获取数据,将获取后的数据进行封装,封装成 RPC Response 交给 RPC 框架
6、RPC 框架通过 RPC 协议将 RPC Response 对象序列化成二进制形式, 通过 TCP 通道传递给前端
6、前端将收到的二进制数据反序列化成 RPC response 对象,将结果通过 Proxy 返回给业务代码

The intonation.(语调)
Express the different feelings of the speaker.
We often use the falling tone when we read a statement, a special question, an exclamatory or imperative sentence(祈使句、感叹句). We often use rising tone when we ask a Yes/No question. We often use rising-falling tone to read an alternative question and a coordinate clause(选择疑问句、并列句). There are two parts in a tag question(反义疑问句). The first part is often in a falling tone and for second part, there are two cases. If we are not sure about the question, we often use a rising tone. If we are sure about that, we offen use a falling tone.
We choose different intonations to express different feelings and moods.

The word stress(单词重音)
Read words more exactly
When we say a word with more than one syllable, some of the syllables need to be stressed.
When we read two-syllable words, normally we stress the first syllable for nouns, and stress the second syllable for verbs. As for the multi-syllable words, we stress the third from the bottom.

The sentence stress(句子重读)
Draw attention to certain information.
We usually stress the lexical words such as nouns, verbs, adjectives, adverbs and numbers. We don’t stress the function words such as a, the, is, of and so on. Besides, you can stress some information you want to emphasis.
We should change it according to the situation and feelings of the speaker.

The liaison/ˈlēəˌzän,lēˈāˌzän//Word linking.(连读)
Speak English more fluently and naturally.
In one sense group, we can read the two words together if the former word ends with a consonant/ˈkänsənənt/, a vowel or “r” sound, and the following word begins with a vowel. If the two words finish and start with the same consonant, we only read one of the consonants.
The two words must be in one sense group.

incomplete plosion/‘pləʊʒ(ə)n/(不完全爆破)
Speak English more fluently and naturally.
There are six plosive sounds /t/ /d/ /p/ /b/ /k/ /g/ in English.When two consonants of them are next to each other, we read the former sound silently.
The two words must be in one sense group.

Weak form(弱读)
speak English more fluently and naturally.
Some words often appear in a weak form such as function words, articles, auxiliary verbs and modal verbs.
there are mainly three ways to change function words to weak forms. They are shortening, elision[ i’liʒən ] and vowel changed to schwa  [ʃwɑ:] sound.

Assimilation(同化)
We use it to speak English more fluently and naturally.
As we can see, Two different sounds affect each other and become more alike.
/s+ j/ -> /F/this year,/d+j/ -> /d3/ would you

Sense group(意群)
We use it to make our expression more easily to understand.
As we can see, a sentence may consists of several groups according to meaning or structure. Each group is called a sense group. It can be a word, a phrase, a clause. After a sense group, there should be a short pause.
We should remind that we should change it according to the situation and feelings of the speaker.

The pause(停顿)
We use to speak English more fluently and naturally.
As we can see, in a long sentence, we should pause several times according to the sense group. Normally, we pause based on meaning and grammatical structure.
We should remind that there should be no pause in a sense group. Where to pause also depends on the situation and the feelings of the speaker.

开场问候

Dear Judges, Now I’ll start the lesson. Good morning, Boys and girls. How are you today? Good? Fine? I’m fine, too. Before the class, Let’s sing a song together. If you can sing, sing along with me . “You are my sunshine, my only sunshine, you make me happy.” Do you feel better? Great, let’s start the lesson.
At the beginning, I’ll show you a video, and you should try to answer the question: What can you see in the video? Who would like to try? Blair, please. You saw the girl had much fun. She had dinner and went shopping with friends. Thank you, Blair, you set a good example for us. You know so much about this topic. Do you want to learn more? Today, let’s learn a new lesson — How was your day off.

导入

OK, everyone, here is a passage. I’ll read it for you. You should listen carefully and tell me the main idea, OK? Here we go! …….OK, Please share your ideas. Jenny, you please. It talks about the Bermuda Triangle, where a number of planes and ships disappeared.Do you agree? I totally agree with her.

Pre

Boys and girls, since we already know the content of the letter, Let’s look at the structure of the letter.There are 4 parts of it, right?
The first one is to whom. We call it salutation/ˌsalyəˈtāSH(ə)n/, such as Hi Henry, Dear Lucy and so on. The second part is body part. You can write whatever you want. The third part, we often add complementary close/wishes, such as ‘yours’, ‘sincerely’. The last part is from whom, we call it signature. Am I clear? So when we write a letter, we can imitate it. Brilliant!

the first part is introduction. It’s a fun day last Saturday. The second part is main body, it’s about what they did in the morning, afternoon and so on. The third part is conclusion. So if we want to write out our everyday life, we can imitate this structure, OK?

写前

Before writing, let’s have a brainstorm. As Tom, what information can you write to reply to Nick’s letter? You can have a discussion in groups. 3 minutes for you. Stop! Who can try, this boy? You will write some activities during camping. Your ideas are wonderful. Bravo! Thank you.
OK,I can see you all have many ideas. How about writing down your ideas with the structure we just learned. You have 2 minutes to design your outline.OK, time is up. Finished? I see all of you nod your heads. Very good!
It’s time to finish the whole letter. While writing, you should pay attention to the grammar.We should use simple past tense to talk about the experience in the past.And for the signature, you should write”Tom”. If you have any problems, just raise your hands, and I will help you. Are you ready? Let’s do it.

写后

Have you finished your writing? Show me your hands. Well done. Next, go over your writing one more time and then exchange it with your group members.Try to check and correct the mistakes, got it? You have 2 minutes.
Stop here.Who wants to share your writing? Cindy, please. Wow, good pronunciation. You state the story vividly. The logic is clear! OK, anyone else? Tony. Please. Oh, you want to present Tina’s article.Can you read it? Very good. Thank you, Tony. Guys, Let’s look at Tina’s work together. “Tom” went to a mountain for camping. I think her article is perfect. We can learn a lot from it. Boys and girls, you all did a good job!

总结

How time files! The class is almost over. Before ending our class, let’s have a summary. Who can summarize what we have learned this class? Fiona please. Very good. I hope you can apply what we have learned in your daily life so that we can improve our reading and speaking ability, right?
Our homework is as follows: You should finish the exercise book and search for more information about ask your parents about their day off. Next class, we will share them together.

Class is over, see you next time.