0%

第十二天

第十二天

过滤器

概念:Vue.js允许自定义过滤器,可以被用作一些常见的文本格式化,过滤器可以用在两个地方:mustache插值和v-bind表达式。过滤器应该被添加在JavaScript表达式的尾部,由“管道”符表示

全局过滤器

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
<!DOCTYPE html>
<html lang="en">

<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>过滤器</title>
<script src="./js/vue.js"></script>
</head>

<body>
<div id="app">
{{msg | msgFormat("疯狂") | test}}
</div>
<script>
// 定义一个Vue全局过滤器
// 全局过滤器,就是所有的VM共享的
Vue.filter('msgFormat', function (msg, arg) {
// return msg.replace(/单纯/g, '邪恶');
return msg.replace(/单纯/g, arg);
});
Vue.filter("test", function (msg) {
return msg + "==========";
})

var vm = new Vue({
el: "#app",
data: {
msg: '曾经,我也是一个单纯的少年,单纯的我,傻傻的问,谁是世界上最单纯的人'
},
});

// 过滤器的定义语法
// 过滤器中的function,第一个参数,规定为过滤器管道符前面传递过来的数据

vue.filter("过滤器的名称", function (data) {
return data + '123';
});
// |即管道符
// 过滤器调用时的格式 {{name | 过滤器的名称}}
</script>
</body>

</html>

私有过滤器

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
var vm = new Vue({
el: "#app",
data: {
id: '',
name: '',
keywords: '',
list: [{
id: 1,
name: "奔驰",
ctime: new Date()
},
{
id: 2,
name: "宝马",
ctime: new Date()
}
]
},
methods: {
add() {
// 分析
// 1. 获取到id和name, 直接从data上获取
// 2. 组织出一个对象
// 3. 把这个对象,调用数组相关方法,添加到当前data中的list中
// 注意:在vue中已经实现了数据的双向绑定,每当我们修改了data中的数据,vue会默认监听到数据的改动,自动把最新的数据应用到页面上
// 程序员更多的是在进行VM中Model数据的操作, 同时, 在操作Model数据的时候, 指定的业务逻辑操作
var car = {
id: this.id,
name: this.name,
ctime: new Date()
};
this.list.push(car);
this.id = this.name = "";
},
del(id) { //根据id删除数据
// 分析
// 1. 如何根据id找到要删除项的索引
// 2. 如果找到索引了,直接调用数组splice方法
// this.list.some((item, i) => {
// if (item.id == id) {
// this.list.splice(i, 1);
// //在数组的some方法中,如果return true,就会立即终止这个数组的后续循环
// return true;
// }
// })
var index = this.list.findIndex(item => {
if (item.id == id) {
return true;
}
})
this.list.splice(index, 1);
},
search(keywords) { //根据关键字进行数据搜索
// var newList = [];
// this.list.forEach(item => {
// if (item.name.indexOf(keywords) != -1) {
// newList.push(item);
// }
// });
// return newList;

// forEach some filter findIndex 都属于数组新方法,都会被数组中的每一项进行遍历,执行相关操作
return this.list.filter(item => {
// if(item.name.indexOf(keywords)!=-1)
// es6中为字符串提供一个新方法-String.prototype.includes(要包含的字符串),若包含,则返回true,否则返回 false
if (item.name.includes(keywords)) {
return item;
}
})
}
},

// 定义私有过滤器 过滤器有两个条件-过滤器名称和处理函数
filters: {
//过滤器调用的时候,采用的是就近原则,如果私有过滤器和全局过滤器名称一致,优先调用私有过滤器
dateFormat: function (dateStr, pattern = "") {
//根据给定的时间字符串,得到特定的时间
var dt = new Date(dateStr);

var y = dt.getFullYear();
// 使用es6新方法 padStart()实现补零格式化
var m = (dt.getMonth() + 1).toString().padStart(2, '0');
var d = (dt.getDate()).toString().padStart(2, '0');

if (pattern.toLowerCase() === 'yyyy-mm-dd') {
return `${y}-${m}-${d}`;
} else {
var hh = (dt.getHours()).toString().padStart(2, '0');
var mm = (dt.getMinutes()).toString().padStart(2, '0');
var ss = (dt.getSeconds()).toString().padStart(2, '0');
return `${y}-${m}-${d} ${hh}:${mm}:${ss}!!!`;
}
}

}
});

自定义键盘修饰符

全部的按键别名:

  • .enter
  • .tab
  • .delete(捕获“删除”或“退格”键)
  • .esc
  • .space
  • .up
  • .down
  • .left
  • .right

不在以上列表中的按键,可以用键盘码

1
2
3
<!-- 此处113也是对的,113是F2的键盘码 -->
<label for="">Name: <input type="text" v-model="name" @keyup.113="add"></label>
<input type="button" value="添加" @click="add">

也可以自定义按键修饰符

1
2
// 自定义全局按键按键修饰符
Vue.config.keyCodes.f2 = 113;

自定义指令

自定义全局指令

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
 // 自定义全局指令,,实现自动获取焦点
// 参数1:指令的名称,注意:在定义的时候,指令名称前不需要加v-前缀,调用时必须添加
// 参数2:是一个对象,对象上有一些指令相关的函数,这些函数可以在特定的阶段执行相关的操作
Vue.directive('focus', {
bind: function (el) {
//每当指令绑定到元素上的时候,会立即执行这个bind函数,只执行一次
// 注意:在每个函数中,第一个参数永远是el,表示被绑定了指令的那个元素,这个el参数,是一个原生JS对象
// 在元素刚绑定了指令时,还没有插入到DOM中,这时,调用focus()没用,因为一个元素只有插入DOM后,才能获取焦点
// el.focus();
},
inserted: function (el) {
// 表示元素插入到DOM中的时候会执行这个inserted函数,触发一次
// 和JS行为有关的操作,最好在inserted中去执行,防止JS行为不生效
el.focus();
},
updated: function (el) {
// 当VNode更新的时候,会执行这个updated,可能会触发多次
}
})

使用钩子函数的第二个参数拿到传递的参数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// 自定义一个设置字体颜色的指令
Vue.directive('color', {
// 样式,只要通过指令绑定给了元素,不管这个元素有没有插入到页面中去,这个元素肯定有了一个内联的样式,将来元素肯定会显示到页面中,这时浏览器的渲染引擎必然会解析样式,应用给这个元素
bind: function (el, binding) {
// 和样式相关的操作,一般都可以在bind执行
// el.style.color = "red";
// binding: 一个对象,包含以下属性
// name: 指令名,不包括v-
// value:指令绑定值,可计算
// oldValue: 指令绑定前一个值
// expression: 字符串形式的指令表达
// arg: 传给指令的参数,可选
// modifies:一个包含修饰符的对象
el.style.color = binding.value;
}
})

自定义局部指令

1
2
3
4
5
6
7
8
9
10
var vm = new Vue({
el: "#app",
directives: {
'fontweight': {
bind: function (el, binding) {
el.style.fontWeight = binding.value;
}
}
}
});

函数简写

1
2
3
4
5
6
7
var vm = new Vue({
el: "#app",
directives: {
'fontweight': function(el,binding){ // 注意:这个function等同于把代码写到了bind和update中去
el.style.fontSize = parseInt(binding.value)+"px";
}
}

案例-品牌列表

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
<!DOCTYPE html>
<html lang="en">

<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>品牌列表案例</title>
<script src="./js/vue.js"></script>
<style>
#app th,
td {
border: 1px solid #000;
}

#app thead th {
width: 200px;
}

#app tbody td {
width: 200px;
text-align: center;
}
</style>
</head>

<body>
<div id="app">
<div>
<label for="">ID: <input type="text" v-model="id"></label>
<!-- 自定义按键修饰符 -->
<label for="">Name: <input type="text" v-model="name" @keyup.f2="add"></label>
<input type="button" value="添加" @click="add">
<label for="">搜索名称关键字:
<!-- 注意:Vue中所有的指令在调用时都以v-开头 -->
<input type="text" v-model="keywords" v-focus v-color="'blue'">
<!-- 注意v-color中是""中套'',如果没有'',则会在data中去找-->
</label>

</div>
<table>
<thead>
<tr>
<th>ID</th>
<th>Name</th>
<th>Ctime</th>
<th>Operation</th>
</tr>
</thead>
<tbody>
<!-- 之前v-for中的数据,都是直接从data上的list中直接渲染过来的 -->
<!-- <tr v-for=" item in list" :key="item.id"> -->
<!-- 现在,我们自定义了一个search方法,同时把所有的关键字通过传参的形式,传递给search方法 -->
<!-- 在search方法内部,通过执行for循环,把所有符合搜索关键字的数据,保存到一个新数组中,返回 -->
<tr v-for="item in search(keywords)" :key="item.id">
<td>{{item.id}}</td>
<td>{{item.name}}</td>
<td>{{item.ctime | dateFormat}}</td>
<td><a href="" @click.prevent="del(item.id)">删除</a></td>
</tr>
</tbody>
</table>
</div>
<script>
Vue.filter('dateFormat', function (dateStr, pattern = "") {
//根据给定的时间字符串,得到特定的时间
var dt = new Date(dateStr);

var y = dt.getFullYear();
var m = dt.getMonth() + 1;
var d = dt.getDate();
// return y + '-' + m + '-' + d '
if (pattern.toLowerCase() === 'yyyy-mm-dd') {
return `${y}-${m}-${d}`;
} else {
var hh = dt.getHours();
var mm = dt.getMinutes();
var ss = dt.getSeconds();
return `${y}-${m}-${d} ${hh}:${mm}:${ss}`;
}
})

// 自定义全局按键按键修饰符
Vue.config.keyCodes.f2 = 113;

// 自定义全局指令,实现自动获取焦点
// 参数1:指令的名称,注意:在定义的时候,指令名称前不需要加v-前缀,调用时必须添加
// 参数2:是一个对象,对象上有一些指令相关的函数,这些函数可以在特定的阶段执行相关的操作
Vue.directive('focus', {
bind: function (el) {
//每当指令绑定到元素上的时候,会立即执行这个bind函数,只执行一次
// 注意:在每个函数中,第一个参数永远是el,表示被绑定了指令的那个元素,这个el参数,是一个原生JS对象
// 在元素刚绑定了指令时,还没有插入到DOM中,这时,调用focus()没用,因为一个元素只有插入DOM后,才能获取焦点
// el.focus();
},
inserted: function (el) {
// 表示元素插入到DOM中的时候会执行这个inserted函数,触发一次
// 和JS行为有关的操作,最好在inserted中去执行,防止JS行为不生效
el.focus();
},
updated: function (el) {
// 当VNode更新的时候,会执行这个updated,可能会触发多次
}
})

// 自定义一个设置字体颜色的指令
Vue.directive('color', {
// 样式,只要通过指令绑定给了元素,不管这个元素有没有插入到页面中去,这个元素肯定有了一个内联的样式,将来元素肯定会显示到页面中,这时浏览器的渲染引擎必然会解析样式,应用给这个元素
bind: function (el, binding) {
// 和样式相关的操作,一般都可以在bind执行
// el.style.color = "red";
// binding: 一个对象,包含以下属性
// name: 指令名,不包括v-
// value:指令绑定值,可计算
// oldValue: 指令绑定前一个值
// expression: 字符串形式的指令表达
// arg: 传给指令的参数,可选
// modifies:一个包含修饰符的对象
el.style.color = binding.value;
}
})

var vm = new Vue({
el: "#app",
data: {
id: '',
name: '',
keywords: '',
list: [{
id: 1,
name: "奔驰",
ctime: new Date()
},
{
id: 2,
name: "宝马",
ctime: new Date()
}
]
},
methods: {
add() {
// 分析
// 1. 获取到id和name, 直接从data上获取
// 2. 组织出一个对象
// 3. 把这个对象,调用数组相关方法,添加到当前data中的list中
// 注意:在vue中已经实现了数据的双向绑定,每当我们修改了data中的数据,vue会默认监听到数据的改动,自动把最新的数据应用到页面上
// 程序员更多的是在进行VM中Model数据的操作, 同时, 在操作Model数据的时候, 指定的业务逻辑操作
var car = {
id: this.id,
name: this.name,
ctime: new Date()
};
this.list.push(car);
this.id = this.name = "";
},
del(id) { //根据id删除数据
// 分析
// 1. 如何根据id找到要删除项的索引
// 2. 如果找到索引了,直接调用数组splice方法
// this.list.some((item, i) => {
// if (item.id == id) {
// this.list.splice(i, 1);
// //在数组的some方法中,如果return true,就会立即终止这个数组的后续循环
// return true;
// }
// })
var index = this.list.findIndex(item => {
if (item.id == id) {
return true;
}
})
this.list.splice(index, 1);
},
search(keywords) { //根据关键字进行数据搜索
// var newList = [];
// this.list.forEach(item => {
// if (item.name.indexOf(keywords) != -1) {
// newList.push(item);
// }
// });
// return newList;

// forEach some filter findIndex 都属于数组新方法,都会被数组中的每一项进行遍历,执行相关操作
return this.list.filter(item => {
// if(item.name.indexOf(keywords)!=-1)
// es6中为字符串提供一个新方法-String.prototype.includes(要包含的字符串),若包含,则返回true,否则返回 false
if (item.name.includes(keywords)) {
return item;
}
})
}
},

// 定义私有过滤器 过滤器有两个条件-过滤器名称和处理函数
filters: {
//过滤器调用的时候,采用的是就近原则,如果私有过滤器和全局过滤器名称一致,优先调用私有过滤器
dateFormat: function (dateStr, pattern = "") {
//根据给定的时间字符串,得到特定的时间
var dt = new Date(dateStr);

var y = dt.getFullYear();
// 使用es6新方法 padStart()实现补零格式化
var m = (dt.getMonth() + 1).toString().padStart(2, '0');
var d = (dt.getDate()).toString().padStart(2, '0');

if (pattern.toLowerCase() === 'yyyy-mm-dd') {
return `${y}-${m}-${d}`;
} else {
var hh = (dt.getHours()).toString().padStart(2, '0');
var mm = (dt.getMinutes()).toString().padStart(2, '0');
var ss = (dt.getSeconds()).toString().padStart(2, '0');
return `${y}-${m}-${d} ${hh}:${mm}:${ss}!!!`;
}
}

},
directives: {
'fontweight': {
bind: function (el, binding) {
el.style.fontWeight = binding.value;
}
}
}
});
</script>
</body>

</html>

Vue实例的生命周期

  • 什么是生命周期:从Vue实例创建、运行、到销毁期间,总是伴随着各种各样的事件,这些事件,统称为生命周期。
  • 生命周期钩子,就是生命周期事件的别名
  • 主要的生命周期函数分类:
    • 创建期间的生命周期函数:
      • beforeCreate:实例刚在内存中被创建出来,此时,还没有初始化好data和methods属性
      • created:实例已经在内存中创建OK,此时data和methods已经创建OK,此时还没有开始编译模板
      • breforeMount:此时已经完成了模板的编译,但是还没有挂载到页面中
      • mounted:此时,已经将编译好的模板挂在到了页面指定的窗口中显示
    • 运行期间的生命周期函数:
      • beforeUpdate:状态更新之前执行此函数,此时data中的状态值是最新的,但是界面上显示的数据还是旧的,因为此时还没有开始重新渲染DOM节点
      • updated:实例更新完毕后调用此函数,此时data中的状态值和界面上显示的数据都已经完成了更新,界面已经被重新渲染好了!
    • 销毁期间的生命周期函数
      • beforeDestroy:实例销毁之前调用。在这一步,实例仍然完全可用
      • destroyed:vue实例销毁后调用。调用后,vue实例指示的所有东西都会解绑定,所有的事件监听器会被移除,所有的子实例也会被销毁。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
<!DOCTYPE html>
<html lang="en">

<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>生命周期函数</title>
<script src="./js/vue.js"></script>
</head>

<body>
<div id="app">
<input type="button" value="修改msg" @click="msg='no'">
<h3 id="h3">{{msg}}</h3>
</div>
<script>
var vm = new Vue({
el: "#app",
data: {
msg: "ok"
},
methods: {
show() {
console.log("执行了show方法")
}
},
beforeCreate() {
//这是遇到的第一个生命周期函数,表示实例完全被创建出来之前,会执行它
// console.log(this.msg);
// this.show()
//注意:在beforeCreate生命周期函数执行的时候,data和methods中的数据都还没有被初始化,所以this.msg/this.show()都是undefined
},
created() {
// 这是遇到的第二个生命周期函数
// console.log(this.msg);
// this.show();
// 在created中,data和methods都已经被初始化好了
// 如果要调用methods中的方法,或者操作data中的数据,最早只能在created中操作
},
breforeMount() {
// 这是遇到的第三个生命周期函数,表示模板已经在内存中编辑完成,但学未把模板渲染到页面中
// console.log(document.getElementById("h3").innerText);
// 在beforeMount 执行的时候,页面中的元素还没有被真正替换过来,只是之前写的一些模板字符串
},
mounted() {
// 这是遇到的第四个生命周期函数,表示内存中的模板已经真实挂载到页面中,用户已经可以看到渲染好的页面了
console.log(document.getElementById("h3").innerText);
// 注意: mounted是实例创建期间的最后一个生命周期函数, 当执行完mounted就表示, 实例已经被完全创建好了, 此时, 如果没有其它操作的话, 这个实例, 就静静待在内存中
},
beforeUpdate() {
//这时,界面还没有被更新,数据已经被更新了
console.log('界面上元素的内容:' + document.getElementById("h3").innerText);
console.log('data中的msg数据是:' + this.msg);
// 结论:当执行beforeUpdate时,页面中显示的数据还是旧的,此时data数据是最新的,页面还没有和最新的数据保持同步
},
updated() {
// 这时,页面和data数据已经保持同步了,都是最新的
console.log('界面上元素的内容:' + document.getElementById("h3").innerText);
console.log('data中的msg数据是:' + this.msg);
}

})
</script>
</body>

</html>

Vue-resource实现get、post、jsonp请求

除了vue-resource外,还可以使用"axios"的第三包实现数据的请求

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
<!DOCTYPE html>
<html lang="en">

<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>vue-resource基本使用</title>
<script src="./js/vue.js"></script>
<!-- 注意:vue-resource依赖于vue,所以先后顺序不能反 -->
<!-- this.$http.jsonp -->
<script src="./js/vue-resource.js"></script>
</head>

<body>
<div id="app">
<input type="button" value="get请求" @click="getInfo">
<input type="button" value="post请求" @click="postInfo">
<input type="button" value="jsonp请求" @click="jsonpInfo">
</div>
<script>
var vm = new Vue({
el: "#app",
data: {},
methods: {
getInfo() {
// 发起get请求
// 当发起get请求后, 通过.then来设置成功的回调函数
this.$http.get('http://vue.studyit.io/api/getlunbo').then(function (result) {
// 通过result.body拿到服务器返回成功的数据
// console.log(result.body);
})
},
postInfo() {
// 发起post请求
// 手动发起的post请求,默认没有表单格式,所有有的服务器处理不了
// 通过post方法的第三个参数emulateJSON:true, 设置提交的内容类型为普通表单数据格式
this.$http.post('http://vue.studyit.io/api/post', {}, {
emulateJSON: true
}).then((result) => {
console.log(result.body);
});
},
jsonpInfo() {
//发起jsonp请求
this.$http.jsonp('http://vue.studyit.io/api/jsonp').then(result => {
console.log(result.body);
})
}
}
})
</script>
</body>

</html>

JSONP的实现原理

  • 由于浏览器的安全性限制,不允许AJAX访问协议不同、域名不同、端口号不同的数据接口,浏览器认为这种访问不安全
  • 可以通过动态创建script标签的形式,把script标签的src属性,指向数据接口的地址,因为script标签不存在跨域限制,这种数据获取方式,称作JSONP(注意:根据JSONP的实现原理,知晓JSONP只支持get请求)
  • 具体实现过程:
    • 先在客户端定义一个回调方法,预定义对数据的操作
    • 再把这个回调方法的名称,通过URL传参的形式,提交到服务器的数据接口
    • 服务器数据接口组织好要发送给客户端的数据,再拿着客户端传递过来的回调方法名称,拼接出一个调用这个方法的字符串,发送给客户端去解析执行
    • 客户端拿到服务器返回的字符串后,当作script脚本去解析执行,这样就能拿到JSONP数据了

通过Node.js,手动实现一个JSONP

后端:

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
const http = require('http')
// 这个核心模块可以帮我们解析URL地址,从而拿到pathname 和 query
const urlModule = require('url')

const server = http.createServer()

server.on('request', function (req, res) {
// const url = req.url
const {
pathname: url,
query
} = urlModule.parse(req.url, true)

if (url === '/getscript') {
// 拼接一个合法的JS脚本,这里拼接的是一个方法的调用
// var scriptStr = 'show()'

var data = {
name: 'zs',
age: 18,
gender: '男'
}

var scriptStr = `${query.callback}(${JSON.stringify(data)})`
// res.end 发送给客户端,客户端将这个字符串当作JS代码去解析执行
res.end(scriptStr)
} else {
res.end('404')
}
})

server.listen(3000, function () {
console.log('server listen at http://127.0.0.1:3000')
})

前端:

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
<!DOCTYPE html>
<html lang="en">

<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>客户端JSONP页面</title>
</head>

<body>
<!-- Host Current Workspace and Open in Browser -->
<script>
function showInfo123(data) {
console.log(data);
}
</script>

<script src="http://127.0.0.1:3000/getscript?callback=showInfo123"></script>
<!-- 相当于
<script>
show()
</script> -->

</body>

</html>