学习视频:

java开发所需的前端技术全教程

推荐资料:

JavaScript 教程 | 菜鸟教程 (runoob.com)

文章目录

HTML元素文本1、标题 Heading2、段落 Paragraph3、列表 List4、超链接 Anchor

多媒体1、Image2、Video3、Audio

表单常见的表单项数据格式小结

CSS1、选择器2、属性和值3、布局

JavaScript变量与数据类型声明变量letconstvar

基本类型undefined 和 nullstringnumber 和 bigintboolean

对象类型Function 函数定义函数默认参数匿名函数箭头函数函数是对象函数作用域闭包

Object语法JSON

动态类型

运算符与表达式1) ===2) ||3) ?? 与 ?.???.

4) ...5) [] {}[]{}

控制语句for infor of

API 前端案例搭建前端服务器一、查找元素

Vue 3TypeScript动态类型的问题使用入门类型标注位置标注变量标注参数标注返回值

复杂类型typeinterface可选属性鸭子类型

方法类型字面量类型nullish 类型泛型

类基本语法方法 & 只读属性get,set类与接口继承与接口方法重写

Vue3 基础环境准备创建项目安装 devtools修改端口配置代理项目结构

Vue 组件main.tsref 与 reactive属性绑定事件绑定表单绑定计算属性xhraxios基本用法环境变量baseURL拦截器

条件与列表监听器vueuseuseRequestusePagination子组件

Vue3 进阶Ant-design-vue表格分页搜索、删除、新增、修改全局消息表单校验

vue-router安装创建 router动态导入嵌套路由重定向

pinia安装定义Store

HTML

使用 ! + Tab 键可生成 HTML 模板

Document

声明为 HTML5 文档 元素是 HTML 页面的根元素 元素包含了文档的元(meta)数据,如 定义网页编码格式为 utf-8。 元素描述了文档的标题<body> 元素包含了可见的页面内容<h1> 元素定义一个大标题<p> 元素定义一个段落 </p><p>元素 </p><p>文本 </p><p>1、标题 Heading </p><p><h1>1号标题</h1></p><p><h2>2号标题</h2></p><p><h3>3号标题</h3></p><p><h4>4号标题</h4></p><p><h5>5号标题</h5></p><p><h6>6号标题</h6></p><p>2、段落 Paragraph </p><p><p>段落</p></p><p>3、列表 List </p><p>无序列表 unordered list </p><p><ul></p><p> <li>列表项1</li></p><p> <li>列表项2</li></p><p> <li>列表项3</li></p><p></ul></p><p>有序列表 </p><p><ol></p><p> <li>列表项1</li></p><p> <li>列表项2</li></p><p> <li>列表项3</li></p><p></ol></p><p>多级列表 </p><p><ul></p><p> <li></p><p> 深圳市</p><p> <ul></p><p> <li>南山区</li></p><p> <li>福田区</li></p><p> <li>龙岗区</li></p><p> </ul></p><p> </li></p><p> <li></p><p> 广州市</p><p> <ul></p><p> <li>番禺区</li></p><p> </ul></p><p> </li></p><p></ul></p><p>4、超链接 Anchor </p><p><a href="resource/page.html">本地网页</a></p><p>多媒体 </p><p>1、Image </p><p><img onclick="xtip.photoApp('jzpic',{index:'1'})" data-xphoto="jzpic" src="文件路径" title="前端学习笔记(Vue、TypeScript、JavaScript) 第1张" alt="前端学习笔记(Vue、TypeScript、JavaScript) 第1张"></p><p>2、Video </p><p><video src="文件路径"></video></p><p>3、Audio </p><p><audio src="文件路径"></audio></p><p>表单 </p><p>表单的作用:收集用户填入的数据,并将这些数据提交给服务器 </p><p><form action="服务器地址" method="请求方式" enctype="数据格式"></p><p> <!-- 表单项 --></p><p> <input type="text" name="uesrname"></p><p> <input type="submit" value="提交按钮"></p><p></form></p><p>method 请求方式有: </p><p> get (默认)提交时,数据跟在 URL 地址之后post 提交时,数据在请求体内 enctype 在 post 请求时,指定请求体的数据格式 </p><p> application/x-www-form-urlencoded(默认)multipart/form-data 其中表单项提供多种收集数据的方式 </p><p> 有 name 属性的表单项数据,才会被发送给服务器 </p><p>常见的表单项 </p><p>文本框 </p><p><input type="text" name="uesrname"></p><p>密码框 </p><p><input type="password" name="password"></p><p>隐藏框 </p><p><input type="hidden" name="id"></p><p>日期框 </p><p><input type="date" name="birthday"></p><p>单选(name 要一致) </p><p><input type="radio" name="sex" value="男" checked></p><p><input type="radio" name="sex" value="女"></p><p>多选(name 要一致,后端用 List 接收) </p><p><input type="checkbox" name="fav" value="唱歌"></p><p><input type="checkbox" name="fav" value="逛街"></p><p><input type="checkbox" name="fav" value="游戏"></p><p>文件上传 </p><p><input type="file" name="avatar"></p><p>数据格式小结 </p><p>客户端发送: </p><p>编码 </p><p> application/x-www-form-urlencoded :url 编码application/json:utf-8 编码multipart/form-data:每部分编码可以不同 表单只支持以 application/x-www-form-urlencoded 和 multipart/form-data 格式发送数据文件上传需要用 multipart/form-data 格式js 代码可以支持任意格式发送数据 </p><p>服务端接收: </p><p>对 application/x-www-form-urlencoded 和 multipart/form-data 格式的数据,Spring 接收方式是统一的,只需要用 java bean 的属性名对应请求参数名即可对于 applicaiton/json 格式的数据,Spring 接收需要使用 @RequestBody 注解 + java bean 的方式 </p><p>CSS </p><p>CSS 即 Cascading Style Sheets 层叠样式表,它描述了网页的表现与展示效果。 </p><p>1、选择器 </p><p> type 选择器:根据标签名进行匹配(元素选择器) class 选择器:根据元素的 class 属性进行匹配 id 选择器:根据元素的 id 属性进行匹配 </p><p>2、属性和值 </p><p>background-color : red;…display </p><p>3、布局 </p><p>与布局相关的 html 元素 </p><p>divtemplate </p><p>/* 元素type选择器 */</p><p>p {</p><p> background-color:aqua;</p><p>}</p><p>/* 元素class选择器 */</p><p>.c1 {</p><p> background-color:cornflowerblue;</p><p>}</p><p>/* id选择器 */</p><p>#p3 {</p><p> background-color:red;</p><p>}</p><p>JavaScript </p><p>它是一种脚本语言,可以用来更改页面内容,控制多媒体,制作图像、动画等等。 </p><p>js 代码位置 </p><p><script></p><p>// js 代码</p><p></script></p><p>引入 js 脚本 </p><p><script src="js脚本路径"></script></p><p>注意,到了框架之后,引入方式会有不同 </p><p>变量与数据类型 </p><p>声明变量 </p><p>let </p><p>let 变量名 = 值;</p><p>let 声明的变量可以被多次赋值,例如 </p><p>let a = 100; // 初始值是 100</p><p>a = 200; // ok, 被重新赋值为 200</p><p>const </p><p>const 修饰的叫常量,只能赋值一次 </p><p>const b = 300; // 初始值是 300</p><p>b = 400; // error, 不能再次赋值</p><p>const 并不意味着它引用的内容不可修改,例如 </p><p>const c = [1,2,3];</p><p>c[2] = 4; // ok, 数组内容被修改成 [1,2,4]</p><p>c = [5,6]; // error, 不能再次赋值</p><p>var </p><p>var 声明的变量可以被多次赋值,例如 </p><p>var f = 100;</p><p>f = 200;</p><p> let 与 var 的区别体现在作用域不同。 </p><p>如果函数外层引用的是 let 变量,那么外层普通的 {} 也会作为作用域边界,最外层的 let 也占一个 script 作用域 </p><p>let x = 10; </p><p>if(true) {</p><p> let y = 20;</p><p> function b() {</p><p> console.log(x,y);</p><p> }</p><p> console.dir(b);</p><p>}</p><p>如果函数外层引用的是 var 变量,外层普通的 {} 不会视为边界 </p><p>var x = 10; </p><p>if(true) {</p><p> var y = 20;</p><p> function b() {</p><p> console.log(x,y);</p><p> }</p><p> console.dir(b);</p><p>}</p><p>如果 var 变量出现了重名,则他俩会被视为同一作用域中的同一个变量 </p><p>var e = 10; </p><p>if(true) {</p><p> var e = 20; // 相当于 e = 20</p><p> console.log(e); // 打印 20</p><p>}</p><p>console.log(e); // 因为是同一个变量,还是打印 20</p><p>如果是 let,则视为两个作用域中的两个变量 </p><p>let e = 10; </p><p>if(true) {</p><p> let e = 20; </p><p> console.log(e); // 打印 20</p><p>}</p><p>console.log(e); // 打印 10</p><p>要想里面的 e 和外面的 e 能区分开来,最简单的办法是改成 let,或者用函数来界定作用域范围 </p><p>var e = 10; </p><p>if(true) {</p><p> function b() {</p><p> var e = 20;</p><p> console.log(e);</p><p> }</p><p> b();</p><p>}</p><p>console.log(e); </p><p>基本类型 </p><p>undefined 和 null </p><p>执行表达式或函数,没有返回结果,出现 undefined访问数组不存在的元素,访问对象不存在的属性,出现 undefined定义变量,没有初始化,出现 undefined </p><p>console.log(1); // 函数没有返回值, 结果是 undefined</p><p>let a = 10; // 表达式没有返回值, 结果是 undefined</p><p>let b = [1,2,3];</p><p>console.log(b[10]); // 数组未定义元素是 undefined</p><p>let c = {"name":"张三"};</p><p>console.log(c.age); // 对象未定义属性是 undefined</p><p>let d;</p><p>console.log(d); // 变量未初始化是 undefined</p><p>二者共同点 </p><p>都没有属性、方法二者合称 Nullish </p><p>二者区别 </p><p>undefined 由 js 产生null 由程序员提供 </p><p>string </p><p>js 字符串三种写法 </p><p>let a = "hello"; // 双引号</p><p>let b = "world"; // 单引号</p><p>let c = `hello`; // 反引号</p><p>html 代码如下,用 java 和 js 中的字符串如何表示? </p><p><a href="1.html">超链接</a></p><p>java 显得比较繁琐 </p><p>String s1 = "<a href=\"1.html\">超链接</a>";</p><p>String s2 = """</p><p> <a href="1.html">超链接</a>""";</p><p>js 就比较灵活 </p><p>let s1 = '<a href="1.html">超链接</a>';</p><p>let s2 = `<a href="1.html">超链接</a>`;</p><p>模板字符串(Template strings) </p><p>需求:拼接 URI 的请求参数,如 </p><p>/test?name=zhang&age=18</p><p>/test?name=li&age=20</p><p>传统方法拼接 </p><p>let name = ; // zhang li ...</p><p>let age = ; // 18 20 ...</p><p>let uri = "/test?name=" + name + "&age=" + age;</p><p>模板字符串方式 </p><p>let name = ; // zhang li ...</p><p>let age = ; // 18 20 ...</p><p>let uri = `/test?name=${name}&age=${age}`;</p><p>number 和 bigint </p><p>number 类型标识的是双精度浮动小数,例如 </p><p>10 / 3; // 结果 3.3333333333333335</p><p>既然是浮点小数,那么可以除零 </p><p>10 / 0; // 结果 Infinity 正无穷大</p><p>-10 / 0; // 结果 -Infinity 负无穷大</p><p>浮点小数都有运算精度问题,例如 </p><p>2.0 - 1.1; // 结果 0.8999999999999999</p><p>字符串转数字 </p><p>parseInt("10"); // 结果是数字 10 </p><p>parseInt("10.5"); // 结果是数字 10, 去除了小数部分</p><p>parseInt("10") / 3; // 结果仍视为 number 浮点数, 因此结果为 3.3333333333333335</p><p>parseInt("abc"); // 转换失败,结果是特殊值 NaN (Not a Number)</p><p>要表示真正的整数,需要用 bigint,数字的结尾用 n 表示它是一个 bigint 类型 </p><p>10n / 3n; // 结果 3n, 按整数除法处理</p><p>boolean </p><p>TruthyFalsy </p><p>在 js 中,并不是 boolean 才能用于条件判断,你可以在 if 语句中使用【数字】、【字符串】… 作为判断条件 </p><p>let b = 1;</p><p>if(b) { // true</p><p> console.log("进入了");</p><p>}</p><p>这时就有一个规则,当需要条件判断时,这个值被当作 true 还是 false,当作 true 的值归类为 truthy,当作 false 的值归类为 falsy </p><p>下面值都是 falsy </p><p>falseNullish (null, undefined)0, 0n, NaN"" '' `` 即长度为零的字符串 </p><p>剩余的值绝大部分都是 truthy </p><p>有几个容易被当作 falsy 实际是 truthy 的 </p><p>"false", "0" 即字符串的 false 和 字符串的零[] 空数组{} 空对象 </p><p>对象类型 </p><p>Function 函数 </p><p>定义函数 </p><p>function 函数名(参数) {</p><p> // 函数体</p><p> return 结果;</p><p>}</p><p>js 中的函数调用特点:对参数的类型和个数都没有限制,例如 </p><p>add('a', 'b'); // 返回 ab</p><p>add(4, 5, 6); // 返回 9, 第三个参数没有被用到, 不会报错</p><p>add(1); // 返回 NaN, 这时 b 没有定义是 undefined, undefined 做数学运算结果就是 NaN</p><p>默认参数 </p><p>java 中(spring)要实现默认参数的效果得这么做: </p><p>@RestController </p><p>public class MyController {</p><p> @RequestMapping("/page")</p><p> @ResponseBody</p><p> public void page(</p><p> @RequestParam(defaultValue="1") int page, </p><p> @RequestParam(defaultValue="10") int size){</p><p> // ...</p><p> }</p><p>}</p><p>js </p><p>function pagination(page = 1, size = 10) {</p><p> console.log(page, size);</p><p>}</p><p>匿名函数 </p><p>(function (参数) {</p><p> // 函数体</p><p> return 结果;</p><p>})</p><p>第一种场景:定义完毕后立刻调用 </p><p>(function(a,b){</p><p> return a + b;</p><p>})(1,2)</p><p>第二种场景:作为其它对象的方法,例如 </p><p>页面有元素 </p><p><p id="p1">点我啊</p></p><p>此元素有一个 onclick 方法,会在鼠标单击这个元素后被执行,onclick 方法刚开始是 null,需要赋值后才能使用 </p><p>document.getElementById("p1").onclick = (function(){</p><p> console.log("鼠标单击了...");</p><p>});</p><p>箭头函数 </p><p>(参数) => {</p><p> // 函数体</p><p> return 结果;</p><p>}</p><p>如果没有参数,() 还是要保留如果只有一个参数,() 可以省略如果函数体内只有一行代码,{} 可以省略如果这一行代码就是结果,return 可以省略 </p><p>document.getElementById("p1").onclick = () => console.log("aa");</p><p>函数是对象 </p><p>可以参与赋值,例,具名函数也能参与赋值 </p><p>function abc() {</p><p> console.log("bb");</p><p>}</p><p>document.getElementById("p1").onclick = abc;</p><p>可以作为方法参数 </p><p>function aa() {</p><p> console.log('aa')</p><p>}</p><p>function bb(fn) { // fn 将来可以是一个函数对象</p><p> console.log('bb')</p><p> fn(); // 调用函数对象</p><p>}</p><p>bb(aa);</p><p>可以作为方法返回值 </p><p>function c() {</p><p> console.log("c");</p><p> function d() {</p><p> console.log("d");</p><p> }</p><p> return d;</p><p>}</p><p>c()()</p><p>函数作用域 </p><p>函数可以嵌套(js 代码中很常见,只是嵌套的形式更多是匿名函数,箭头函数) </p><p>function a() {</p><p> function b() { </p><p> }</p><p>}</p><p>以函数为分界线划定作用域,所有函数之外是全局作用域查找变量时,由内向外查找 </p><p> 在内层作用域找到变量,就会停止查找,不会再找外层所有作用域都找不到变量,报错 作用域本质上是函数对象的属性,可以通过 console.dir 来查看调试 </p><p>var x = 10;</p><p>function a() {</p><p> var y = 20;</p><p> function b() {</p><p> console.log(x, y);</p><p> }</p><p> b();</p><p>}</p><p>a();</p><p>闭包 </p><p>var x = 10;</p><p>function a() {</p><p> var y = 20;</p><p> function b() {</p><p> console.log(x,y);</p><p> }</p><p> return b;</p><p>}</p><p>a()(); // 在外面执行了 b</p><p>函数定义时,它的作用域已经确定好了,因此无论函数将来去了哪,都能从它的作用域中找到当时那些变量闭包就是指函数能够访问自己的作用域中变量 </p><p>Object </p><p>语法 </p><p>let obj = {</p><p> 属性名: 值,</p><p> 方法名: 函数,</p><p> get 属性名() {},</p><p> set 属性名(新值) {}</p><p>}</p><p>例1 </p><p>let stu1 = {</p><p> name: "小明",</p><p> age: 18,</p><p> study: function() {</p><p> console.log(this.name + "爱学习");</p><p> } </p><p>}</p><p>例2 </p><p>let name = "小黑";</p><p>let age = 20;</p><p>let study = function() {</p><p> console.log(this.name + "爱学习");</p><p>}</p><p>let stu2 = { name, age, study }</p><p>例3(重点) </p><p>let stu3 = {</p><p> name: "小白",</p><p> age: 18,</p><p> study() {</p><p> console.log(this.name + "爱学习");</p><p> } </p><p>}</p><p>注意:对象方法这么写,仅限于对象内部 </p><p>例4 </p><p>let stu4 = {</p><p> _name: null, /*类似于java中私有成员变量*/</p><p> get name() {</p><p> console.log("进入了get");</p><p> return this._name;</p><p> },</p><p> set name(name) {</p><p> console.log("进入了set");</p><p> this._name = name;</p><p> }</p><p>}</p><p>调用 get,set </p><p>stu4.name = "小白"; // set</p><p>console.log(stu4.name); // get</p><p>JSON </p><p>一个 json 对象可以长这样: </p><p>{</p><p> "name":"张三",</p><p> "age":18</p><p>}</p><p>一个 js 对象长这样: </p><p>{</p><p> name:"张三",</p><p> age:18</p><p>}</p><p>本质不同 </p><p> json 对象本质上是个字符串,它的职责是作为客户端和服务器之间传递数据的一种格式js 对象是切切实实的对象,可以有属性方法 语法细节不同 </p><p> json 中只能有 null、true|false、数字、字符串(只有双引号)、对象、数组json 中不能有除以上的其它 js 对象的特性,如方法等json 中的属性必须用双引号引起来 </p><p>json 字符串与 js 对象的转换 </p><p>JSON.parse(json字符串); // 返回js对象</p><p>JSON.stringify(js对象); // 返回json字符串</p><p>动态类型 </p><p>静态类型语言,如 Java,值有类型,变量也有类型、赋值给变量时,类型要相符 </p><p>int a = 10;</p><p>String b = "abc";</p><p>int c = "abc"; // 错误</p><p>而 js 属于动态类型语言,值有类型,但变量没有类型,赋值给变量时,没要求 </p><p>let a = 200;</p><p>let b = 100;</p><p>b = 'abc';</p><p>b = true;</p><p>动态类型看起来比较灵活,但变量没有类型,会给后期维护带来困难,例如 </p><p>function test(obj) {</p><p> // obj 的类型未知,必须根据不同类型做出相应的容错处理</p><p>}</p><p>于是后续出现了 typescript。 </p><p>运算符与表达式 </p><p>+ - * / % ** += -= *= /= %= **=++ --位运算、移位运算== != > >= < <==== !==&& || !?? ?....解构赋值 </p><p>1) === </p><p>严格相等运算符,用作逻辑判等 </p><p>1 == 1 // 返回 true </p><p>1 == '1' // 返回 true,会先将右侧的字符串转为数字,再做比较</p><p>1 === '1' // 返回 false,类型不等,直接返回 false</p><p>typeof 查看某个值的类型 </p><p>typeof 1 // 返回 'number'</p><p>typeof '1' // 返回 'string'</p><p>2) || </p><p>需求,如果参数 n 没有传递,给它一个【男】 </p><p>推荐做法 </p><p>function test(n = '男') {</p><p> console.log(n);</p><p>}</p><p>你可能的做法 </p><p>function test(n) {</p><p> if(n === undefined) {</p><p> n = '男';</p><p> }</p><p> console.log(n);</p><p>}</p><p>还可能是这样 </p><p>function test(n) {</p><p> n = (n === undefined) ? '男' : n;</p><p> console.log(n);</p><p>}</p><p>一些老旧代码中可能的做法(不推荐) </p><p>function test(n) {</p><p> n = n || '男';</p><p> console.log(n);</p><p>}</p><p>它的语法是 </p><p>值1 || 值2</p><p>如果值1 是 Truthy,返回值1,如果值1 是 Falsy 返回值 2 </p><p>3) ?? 与 ?. </p><p>?? </p><p>需求,如果参数 n 没有传递或是 null,给它一个【男】 </p><p>如果用传统办法 </p><p>function test(n) {</p><p> if(n === undefined || n === null) {</p><p> n = '男';</p><p> }</p><p> console.log(n);</p><p>}</p><p>用 ?? </p><p>function test(n) {</p><p> n = n ?? '男';</p><p> console.log(n);</p><p>}</p><p>语法 </p><p>值1 ?? 值2</p><p>值1 是 nullish,返回值2值1 不是 nullish,返回值1 </p><p>?. </p><p>需求,函数参数是一个对象,可能包含有子属性 </p><p>例如,参数可能是 </p><p>let stu1 = {</p><p> name:"张三",</p><p> address: {</p><p> city: '北京'</p><p> }</p><p>};</p><p>let stu2 = {</p><p> name:"李四"</p><p>}</p><p>let stu3 = {</p><p> name:"李四",</p><p> address: null</p><p>}</p><p>现在要访问子属性(有问题) </p><p>function test(stu) {</p><p> console.log(stu.address.city)</p><p>}</p><p>现在希望当某个属性是 nullish 时,短路并返回 undefined,可以用 ?. </p><p>function test(stu) {</p><p> console.log(stu.address?.city)</p><p>}</p><p>用传统办法 </p><p>function test(stu) {</p><p> if(stu.address === undefined || stu.address === null) {</p><p> console.log(undefined);</p><p> return;</p><p> }</p><p> console.log(stu.address.city)</p><p>}</p><p>4) … </p><p>展开运算符 </p><p>作用1:打散数组,把元素传递给多个参数 </p><p>let arr = [1,2,3];</p><p>function test(a,b,c) {</p><p> console.log(a,b,c);</p><p>}</p><p>需求,把数组元素依次传递给函数参数 </p><p>传统写法 </p><p>test(arr[0],arr[1],arr[2]); // 输出 1,2,3</p><p>展开运算符写法 </p><p>test(...arr); // 输出 1,2,3</p><p>打散可以理解为【去掉了】数组外侧的中括号,只剩下数组元素 </p><p>作用2:复制数组或对象 </p><p>数组 </p><p>let arr1 = [1,2,3];</p><p>let arr2 = [...arr1]; // 复制数组</p><p>对象 </p><p>let obj1 = {name:'张三', age: 18};</p><p>let obj2 = {...obj1}; // 复制对象</p><p>注意:展开运算符复制属于浅拷贝,例如 </p><p>let o1 = {name:'张三', address: {city: '北京'} }</p><p>let o2 = {...o1};</p><p>作用3:合并数组或对象 </p><p>合并数组 </p><p>let a1 = [1,2];</p><p>let a2 = [3,4];</p><p>let b1 = [...a1,...a2]; // 结果 [1,2,3,4]</p><p>let b2 = [...a2,5,...a1] // 结果 [3,4,5,1,2]</p><p>合并对象 </p><p>let o1 = {name:'张三'};</p><p>let o2 = {age:18};</p><p>let o3 = {name:'李四'};</p><p>let n1 = {...o1, ...o2}; // 结果 {name:'张三',age:18}</p><p>let n2 = {...o3, ...o2, ...o1}; // 结果{name:'李四',age:18}</p><p>复制对象时出现同名属性,后面的会覆盖前面的 </p><p>5) [] {} </p><p>解构赋值 </p><p>[] </p><p>用在声明变量时 </p><p>let arr = [1,2,3];</p><p>let [a, b, c] = arr; // 结果 a=1, b=2, c=3</p><p>用在声明参数时 </p><p>let arr = [1,2,3];</p><p>function test([a,b,c]) {</p><p> console.log(a,b,c) // 结果 a=1, b=2, c=3</p><p>}</p><p>test(arr); </p><p>{} </p><p>用在声明变量时 </p><p>let obj = {name:"张三", age:18};</p><p>let {name,age} = obj; // 结果 name=张三, age=18</p><p>用在声明参数时 </p><p>let obj = {name:"张三", age:18};</p><p>function test({name, age}) {</p><p> console.log(name, age); // 结果 name=张三, age=18</p><p>}</p><p>test(obj)</p><p>控制语句 </p><p>for in </p><p>主要用来遍历对象 </p><p>let father = {name:'张三', age:18, study:function(){}};</p><p>for(const n in father) {</p><p> console.log(n);</p><p>}</p><p>其中 const n 代表遍历出来的属性名注意1:方法名也能被遍历出来(它其实也算一种特殊属性)注意2:遍历子对象时,父对象的属性会跟着遍历出来 </p><p>let son = Object.create(father);</p><p>son.sex = "男";</p><p>for(const n in son) {</p><p> console.log(n);</p><p>}</p><p>注意3:在 for in 内获取属性值,要使用 [] 语法,而不能用 . 语法 </p><p>for(const n in son) {</p><p> console.log(n, son[n]);</p><p>}</p><p>for of </p><p>主要用来遍历数组,也可以是其它可迭代对象,如 Map,Set 等 </p><p>let a1 = [1,2,3];</p><p>for(const i of a1) {</p><p> console.log(i);</p><p>}</p><p>let a2 = [</p><p> {name:'张三', age:18},</p><p> {name:'李四', age:20},</p><p> {name:'王五', age:22}</p><p>];</p><p>for(const obj of a2) {</p><p> console.log(obj.name, obj.age);</p><p>}</p><p>for(const {name,age} of a2) {</p><p> console.log(name, age);</p><p>}</p><p>API 前端案例 </p><p>搭建前端服务器 </p><p>新建一个保存项目的 client 文件夹,进入文件夹执行 </p><p>npm install express --save-dev</p><p>修改 package.json 文件 </p><p>{</p><p> "type": "module",</p><p> "devDependencies": {</p><p> "express": "^4.18.1"</p><p> }</p><p>}</p><p>其中 devDependencies 是 npm install --save-dev 添加的 </p><p>编写 main.js 代码 </p><p>import express from 'express'</p><p>const app = express()</p><p>app.use(express.static('./')) //设置静态资源路径</p><p>app.listen(7070)</p><p>执行 js 代码(运行前端服务器) </p><p>node main.js</p><p>一、查找元素 </p><p>document.getElementById:根据 id 值查找一个元素;[document|元素].querySelector:根据选择器查找第一个匹配元素;[document|元素].querySelectorAll:根据选择器查找所有匹配元素; </p><p><div></p><p> <div class="title">学生列表</div></p><p> <div class="thead"></p><p> <div class="row bold"></p><p> <div class="col">编号</div></p><p> <div class="col">姓名</div></p><p> <div class="col">性别</div></p><p> <div class="col">年龄</div></p><p> </div></p><p> </div></p><p> <div class="tbody"></p><p> <div class="row"></p><p> <div class="col">1</div></p><p> <div class="col">张三</div></p><p> <div class="col">男</div></p><p> <div class="col">18</div></p><p> </div></p><p> </div></p><p></div></p><p>执行 </p><p>document.querySelector('.title'); // 找到 <div class="title">学生列表</div></p><p>执行 </p><p>document.querySelector('.col'); // 找到 <div class="col">编号</div></p><p>执行 </p><p>document.querySelectorAll('.col');</p><p>/*</p><p> 找到的是一个集合</p><p> <div class="col">编号</div></p><p> <div class="col">姓名</div></p><p> <div class="col">性别</div></p><p> <div class="col">年龄</div></p><p> <div class="col">1</div></p><p> <div class="col">张三</div></p><p> <div class="col">男</div></p><p> <div class="col">18</div></p><p>*/</p><p>执行 </p><p>const thead = document.querySelector('.thead');</p><p>// 只在 thead 元素范围内找</p><p>thead.querySelectorAll('.col');</p><p>/*</p><p> 找到的是一个集合</p><p> <div class="col">编号</div></p><p> <div class="col">姓名</div></p><p> <div class="col">性别</div></p><p> <div class="col">年龄</div></p><p>*/</p><p>根据 id 属性查找既可以用 </p><p>document.getElementById("id值")</p><p>也可以用 </p><p>document.querySelector("#id值")</p><p>Vue 3 </p><p>TypeScript </p><p>动态类型的问题 </p><p>前面我们讲过 js 属于动态类型语言,例如 </p><p>function test(obj) { </p><p>}</p><p>obj 可能只是个字符串 </p><p>test('hello, world')</p><p>obj 也有可能是个函数 </p><p>test(()=>console.log('hello, world'))</p><p>obj 类型不确定,就给后期使用者带来了麻烦,一旦参数传不对,代码就崩溃了 </p><p>动态类型意味着:运行代码时才知道发生什么静态类型意味着:在代码运行前,就对它的行为做出预测 </p><p>下面的 typescript 代码,就在代码运行前对参数加入了约束限制 </p><p>//限制了参数只能做string那些事</p><p>function test(msg : string) {</p><p> console.log(msg.toUpperCase());</p><p>}</p><p>//限制了参数只能做函数那些事</p><p>function test(msg : Function) {</p><p> msg()</p><p>}</p><p>使用入门 </p><p>安装 typescript 编译器 </p><p>npm install -g typescript</p><p>编写 ts 代码 </p><p>function hello(msg: string) {</p><p> console.log(msg)</p><p>}</p><p>hello('hello,world')</p><p>执行 tsc 编译命令之前,先创建一个 tsconfig.json 文件,避免 “函数实现重复” 报错。 </p><p>tsc --init</p><p>执行 tsc 编译命令 </p><p>tsc xxx.ts</p><p>编译生成 js 代码,编译后进行了类型擦除 </p><p>function hello(msg) {</p><p> console.log(msg);</p><p>}</p><p>hello('hello,world');</p><p>再来一个例子,用 interface 定义用户类型 </p><p>interface User {</p><p> name: string,</p><p> age: number</p><p>}</p><p>function test(u: User): void {</p><p> console.log(u.name)</p><p> console.log(u.age)</p><p>}</p><p>test({ name: 'zhangs', age: 18 })</p><p>编译后 </p><p>function test(u) {</p><p> console.log(u.name);</p><p> console.log(u.age);</p><p>}</p><p>test({ name: 'zhangs', age: 18 });</p><p>可见,typescript 属于编译时实施类型检查(静态类型)的技术 </p><p>类型 </p><p>类型例备注字符串类型string数字类型number布尔类型boolean数组类型number[],string[], boolean[] 依此类推任意类型any相当于又回到了没有类型的时代复杂类型type 与 interface函数类型() => void对函数的参数和返回值进行说明字面量类型“a”|“b”|“c”限制变量或参数的取值nullish类型null 与 undefined泛型<T>,<T extends 父类型> </p><p>标注位置 </p><p>标注变量 </p><p>let message: string = 'hello,world'</p><p>一般可以省略,因为可以根据后面的字面量推断出前面变量类型 </p><p>let message = 'hello,world'</p><p>标注参数 </p><p>function greet(name: string) {</p><p>}</p><p>标注返回值 </p><p>一般也可以省略,因为可以根据返回值做类型推断 </p><p>function add(a: number, b: number) : number {</p><p> return a + b</p><p>}</p><p>复杂类型 </p><p>type </p><p>type Cat = {</p><p> name: string,</p><p> age: number</p><p>}</p><p>const c1: Cat = { name: '小白', age: 1 }</p><p>const c2: Cat = { name: '小花' } // 错误: 缺少 age 属性</p><p>const c3: Cat = { name: '小黑', age: 1, sex: '公' } // 错误: 多出 sex 属性</p><p>interface </p><p>interface Cat {</p><p> name: string,</p><p> age: number</p><p>}</p><p>const c1: Cat = { name: '小白', age: 1 }</p><p>const c2: Cat = { name: '小花' } // 错误: 缺少 age 属性</p><p>const c3: Cat = { name: '小黑', age: 1, sex: '公' } // 错误: 多出 sex 属性</p><p>可选属性 </p><p>如果需要某个属性可选,可以用下面的语法 </p><p>interface Cat {</p><p> name: string,</p><p> age?: number</p><p>}</p><p>const c1: Cat = { name: '小白', age: 1 }</p><p>const c2: Cat = { name: '小花' } // 正确: age 属性可选</p><p>可选属性要注意处理 undefined 值 </p><p>鸭子类型 </p><p>interface Cat {</p><p> name: string</p><p>}</p><p>function test(cat: Cat) {</p><p> console.log(cat.name)</p><p>}</p><p>const c1 = { name: '小白', age: 1 } </p><p>test(c1)</p><p>const c1 并没有声明类型为 Cat,但它与 Cat 类型有一样的属性,也可以被当作是 Cat 类型 </p><p>方法类型 </p><p>interface Api {</p><p> foo(): void,</p><p> bar(str: string): string</p><p>}</p><p>function test(api: Api) {</p><p> api.foo()</p><p> console.log(api.bar('hello'))</p><p>}</p><p>test({</p><p> foo() { console.log('ok') },</p><p> bar(str: string) { return str.toUpperCase() }</p><p>})</p><p>字面量类型 </p><p>function printText(s: string, alignment: "left" | "right" | "center") {</p><p> console.log(s, alignment)</p><p>}</p><p>printText('hello', 'left')</p><p>printText('hello', 'aaa') // 错误: 取值只能是 left | right | center</p><p>nullish 类型 </p><p>function test(x?: string | null) {</p><p> console.log(x?.toUpperCase())</p><p>}</p><p>test('aaa')</p><p>test(null)</p><p>test()</p><p>x?: string | null 表示可能是 undefined 或者是 string 或者是 null </p><p>泛型 </p><p>interface Ref<T> {</p><p> value: T</p><p>}</p><p>const r1: Ref<string> = { value: 'hello' }</p><p>const r2: Ref<number> = { value: 123 }</p><p>const r3: Ref<boolean> = { value: true }</p><p>泛型的要点就是 <类型参数>,把【类型】也当作一个变化的要素,像参数一样传递过来,这样就可以派生出结构相似的新类型 </p><p>函数定义也支持泛型 </p><p>function ref<T>(n: T): Ref<T> {</p><p> return { value: n }</p><p>}</p><p>const v1 = ref("hello"); // Ref<string></p><p>const v2 = ref(123.3333); // Ref<number></p><p>v1.value.toLocaleLowerCase()</p><p>v2.value.toFixed(2)</p><p>类 </p><p> 关于 TypeScript 与 JavaScript 中的类语法不是重点,class 相关语法只是起到辅助作用,更重要的是前面讲的 interface </p><p>基本语法 </p><p>class User {</p><p> name: string;</p><p> constructor(name: string) {</p><p> this.name = name</p><p> }</p><p>}</p><p>const u = new User('张三')</p><p>其实会被编译成这个样子(默认 --target=es3) </p><p>var User = /** @class */ (function () {</p><p> function User(name) {</p><p> this.name = name;</p><p> }</p><p> return User;</p><p>}());</p><p>var u = new User('张三');</p><p>所以 js 中的 class,并不等价于 java 中的 class,它还是基于原型实现的,原理参考第二章(036、037) </p><p>方法 & 只读属性 </p><p>readonly 是 typescript 特有的,表示该属性只读 </p><p>class User {</p><p> readonly name: string;</p><p> constructor(name: string) {</p><p> this.name = name</p><p> }</p><p> study() {</p><p> console.log(`[${this.name}]正在学习`)</p><p> }</p><p>}</p><p>const u = new User('张三')</p><p>u.study()</p><p>get,set </p><p>class User {</p><p> _name: string;</p><p> constructor(name: string) {</p><p> this._name = name</p><p> }</p><p> get name() {</p><p> return this._name</p><p> }</p><p> set name(name: string) {</p><p> this._name = name</p><p> }</p><p>}</p><p>const u = new User('张三')</p><p>console.log(u.name)</p><p>u.name = '李四'</p><p>console.log(u.name)</p><p>注意,需要在编译时加上 tsc --target es6 .\xxx.ts 选项es6 等价于 es2015,再此之上还有 es2016 … es2022 </p><p>类与接口 </p><p>interface User {</p><p> name: string</p><p> study(course: string): void</p><p>}</p><p>class UserImpl implements User {</p><p> name: string;</p><p> constructor(name: string) {</p><p> this.name = name</p><p> }</p><p> study(course: string) {</p><p> console.log(`[${this.name}]正在学习[${course}]`)</p><p> }</p><p> foo() { }</p><p>}</p><p>const user: User = new UserImpl('张三')</p><p>user.study('Typescript')</p><p>user.foo() // 错误,必须是接口中定义的方法</p><p>继承与接口 </p><p>interface Flyable {</p><p> fly(): void</p><p>}</p><p>class Animal {</p><p> name: string;</p><p> constructor(name: string) {</p><p> this.name = name</p><p> }</p><p>}</p><p>class Bird extends Animal implements Flyable {</p><p> fly() {</p><p> console.log(`${this.name}在飞翔`)</p><p> }</p><p>}</p><p>const b: Flyable & Animal = new Bird("小花")</p><p>b.fly()</p><p>Flyable & Animal 表示变量是 flyable 类型,同时也是 Animal 类型 </p><p>方法重写 </p><p>class Father {</p><p> study(): void {</p><p> console.log(`father study`)</p><p> }</p><p>}</p><p>class Son extends Father { </p><p> study(): void {</p><p> super.study()</p><p> console.log(`son study`)</p><p> }</p><p>}</p><p>const f: Father = new Son()</p><p>f.study()</p><p>Vue3 基础 </p><p>技术选型 </p><p>Vue </p><p> 选项式 API 还是 组合式 API✔️HTML 还是 单文件组件✔️ 语法 </p><p> javascript 还是 typescript✔️ 构建工具 </p><p> @vue/cli 还是 vite✔️ 路由 </p><p> vue-router✔️ 共享存储 </p><p> vuex 还是 pinia✔️ 视图组件 </p><p> ElementUI 还是 Antdv✔️ </p><p>环境准备 </p><p>创建项目 </p><p>采用 vite 作为前端项目的打包,构建工具 </p><p>npm init vite@latest</p><p>按提示操作 </p><p>cd 项目目录</p><p>npm install</p><p>npm run dev</p><p>访问 localhost:5173 </p><p>安装 devtools </p><p>devtools 插件网址:https://devtools.vuejs.org/guide/installation.html </p><p>修改端口 </p><p>打开项目根目录下 vite.config.ts </p><p>import { defineConfig } from 'vite'</p><p>import vue from '@vitejs/plugin-vue'</p><p>// https://vitejs.dev/config/</p><p>export default defineConfig({</p><p> plugins: [vue()],</p><p> server: {</p><p> port: 7070</p><p> }</p><p>})</p><p> 文档地址:配置 Vite {#configuring-vite} | Vite中文网 (vitejs.cn) </p><p>配置代理 </p><p>为了避免前后端服务器联调时, fetch、xhr 请求产生跨域问题,需要配置代理,同样是修改项目根目录下 vite.config.ts </p><p>import { defineConfig } from 'vite'</p><p>import vue from '@vitejs/plugin-vue'</p><p>// https://vitejs.dev/config/</p><p>export default defineConfig({</p><p> plugins: [vue()],</p><p> server: {</p><p> port: 7070,</p><p> proxy: {</p><p> '/api': {</p><p> target: 'http://localhost:8080',</p><p> changeOrigin: true</p><p> }</p><p> }</p><p> }</p><p>})</p><p>项目结构 </p><p>index.html</p><p>package.json</p><p>tsconfig.json</p><p>vite.config.ts</p><p>├─public</p><p>└─src</p><p> ├─assets</p><p> ├─components</p><p> ├─model</p><p> ├─router</p><p> ├─store</p><p> └─views</p><p>index.html 为主页面package.json npm 配置文件tsconfig.json typescript 配置文件vite.config.ts vite 配置文件public 静态资源src/components 可重用组件src/model 模型定义src/router 路由src/store 共享存储src/views 视图组件 </p><p>Vue 组件 </p><p>Vue 的组件文件以 .vue 结尾,每个组件由三部分组成 </p><p><script setup lang="ts"></script></p><p><template></template></p><p><style scoped></style></p><p>script 代码部分,控制模板的数据来源和行为template 模板部分,由它生成 html 代码style 样式部分,一般不咋关心 </p><p>根组件是 src/App.vue,先来个 Hello,world 例子 </p><p><script setup lang="ts"></p><p>import { ref } from "vue";</p><p>let msg = ref("hello"); // 把数据变成响应式的</p><p>function change() {</p><p> msg.value = "world";</p><p> console.log(msg);</p><p>}</p><p></script></p><p><template></p><p> <h1>{{ msg }}</h1></p><p> <input type="button" value="修改msg" @click="change" /></p><p></template></p><p>{{msg}} 用来把一个变量绑定到页面上某个位置绑定的变量必须用 ref 函数来封装 </p><p> ref 返回的是【响应式】数据,即数据一旦变化,页面展示也跟着变化 </p><p>main.ts </p><p>import { createApp } from 'vue'</p><p>import './style.css'</p><p>import App from './App.vue'</p><p>createApp(App).mount('#app')</p><p>createApp 是创建一个 Vue 应用程序,它接收的参数 App 即之前我们看到的根组件mount 就是把根组件生成的 html 代码片段【挂载】到 index.html 中 id 为 app 的 html 元素上 </p><p>ref 与 reactive </p><p>vue 提供了两个函数,都可以将数据变为【响应式】的 </p><p><script setup lang="ts"></p><p>import { ref, reactive } from 'vue'</p><p>const msg = ref('Hello, World')</p><p>const user = reactive({ name: '张三' })</p><p></script></p><p><template></p><p> <h2>{{msg}}</h2></p><p> <h2>{{user.name}}</h2></p><p></template></p><p>ref 能将任意类型的数据变为【响应式】的reactive 只能将对象类型变为【响应式】,对基本类型无效(例如 string,number,boolean) </p><p>还有一点不同: </p><p><script setup lang="ts"></p><p>import { ref, reactive } from 'vue'</p><p>const u1 = ref({ name: '张三' })</p><p>const u2 = reactive({ name: '张三' })</p><p>function test() {</p><p> console.log(u1.value)</p><p> console.log(u2)</p><p>}</p><p>test()</p><p></script></p><p><template></p><p> <h2>{{u1.name}}</h2></p><p> <h2>{{u2.name}}</h2></p><p></template></p><p>在 template 模板中使用 ref 包装的数据,直接写【变量名】就可以了但在代码中要使用 ref 包装的数据,必须用【变量名.value】才能访问到 </p><p>属性绑定 </p><p><script setup lang="ts"></p><p>import { ref } from 'vue'</p><p>const path = ref('/src/assets/vue.svg')</p><p></script></p><p><template></p><p> <img onclick="xtip.photoApp('jzpic',{index:'2'})" data-xphoto="jzpic" src="path" title="前端学习笔记(Vue、TypeScript、JavaScript) 第2张" alt="前端学习笔记(Vue、TypeScript、JavaScript) 第2张" :src="path"><!-- 加个冒号: --></p><p></template></p><p>【:属性名】用来将标签属性与【响应式】变量绑定 </p><p>事件绑定 </p><p><!-- 事件绑定 --></p><p><script setup lang="ts"></p><p>import {ref} from 'vue'</p><p>const count = ref(0)</p><p>function inc() {</p><p> count.value++;</p><p>}</p><p>function dec() {</p><p> count.value--;</p><p>}</p><p></script></p><p><template></p><p> <input type="button" value="+" @click="inc"></p><p> <h2>{{ count }}</h2></p><p> <input type="button" value="-" @click="dec"></p><p></template></p><p>表单绑定 </p><p><script setup lang="ts"></p><p>import { ref } from "vue";</p><p>const user = ref({</p><p> name:'张三',</p><p> age:18,</p><p> sex:'男',</p><p> fav:['游泳','打球']</p><p>})</p><p>function saveUser() {</p><p> console.log(user.value)</p><p>}</p><p></script></p><p><template></p><p> <div class="outer"></p><p> <div></p><p> <label for="">请输入姓名</label></p><p> <input type="text" v-model="user.name"/></p><p> </div></p><p> <div></p><p> <label for="">请输入年龄</label></p><p> <input type="text" v-model="user.age"/></p><p> </div></p><p> <div></p><p> <label for="">请选择性别</label></p><p> 男 <input type="radio" value="男" v-model="user.sex"/> </p><p> 女 <input type="radio" value="女" v-model="user.sex"/></p><p> </div></p><p> <div></p><p> <label for="">请选择爱好</label></p><p> 游泳 <input type="checkbox" value="游泳" v-model="user.fav"/> </p><p> 打球 <input type="checkbox" value="打球" v-model="user.fav"/> </p><p> 健身 <input type="checkbox" value="健身" v-model="user.fav"/></p><p> </div></p><p> <div></p><p> <input type="button" value="保存" @click="saveUser"></p><p> </div></p><p> </div></p><p></template></p><p><style scoped></p><p> div {</p><p> margin-bottom: 8px;</p><p> }</p><p> .outer {</p><p> width: 100%;</p><p> position: relative;</p><p> padding-left: 80px;</p><p> }</p><p> label {</p><p> text-align: left;</p><p> width: 100px;</p><p> display: inline-block;</p><p> position: absolute;</p><p> left :0;</p><p> }</p><p></style></p><p>用 v-model 实现双向绑定: </p><p> javascript 数据可以同步到表单标签反过来用户在表单标签输入的新值也会同步到 javascript 这边 双向绑定只适用于表单这种带【输入】功能的标签,其它标签的数据绑定,单向就足够了复选框这种标签,双向绑定的 javascript 数据类型一般用数组 </p><p>计算属性 </p><p>有时在数据展示时要做简单的计算 </p><p><script setup lang="ts"></p><p>import { ref } from 'vue'</p><p>const firstName = ref('三')</p><p>const lastName = ref('张')</p><p></script></p><p><template></p><p> <h2>{{lastName + firstName}}</h2></p><p> <h3>{{lastName + firstName}}</h3></p><p> <h4>{{lastName + firstName}}</h4></p><p></template></p><p>看起来较为繁琐,可以用计算属性改进 </p><p><script setup lang="ts"></p><p>import { ref, computed } from 'vue'</p><p>const firstName = ref('三')</p><p>const lastName = ref('张')</p><p>const fullName = computed(() => {</p><p> console.log('enter')</p><p> return lastName.value + firstName.value</p><p>})</p><p></script></p><p><template></p><p> <h2>{{fullName}}</h2></p><p> <h3>{{fullName}}</h3></p><p> <h4>{{fullName}}</h4></p><p></template></p><p>fullName 即为计算属性,它具备缓存功能,即 firstName 和 lastName 的值发生了变化,才会重新计算如果用函数实现相同功能,则没有缓存功能 </p><p><script setup lang="ts"></p><p>import { ref } from 'vue'</p><p>const firstName = ref('三')</p><p>const lastName = ref('张')</p><p>function fullName() {</p><p> console.log('enter')</p><p> return lastName.value + firstName.value</p><p>}</p><p></script></p><p><template></p><p> <h2>{{fullName()}}</h2></p><p> <h3>{{fullName()}}</h3></p><p> <h4>{{fullName()}}</h4></p><p></template></p><p>xhr </p><p>浏览器中有两套 API 可以和后端交互,发送请求、接收响应,fetch api 前面我们已经介绍过了,另一套 api 是 xhr,基本用法如下 </p><p>const xhr = new XMLHttpRequest();</p><p>//当响应返回时,会触发 onload 事件</p><p>xhr.onload = function() {</p><p> //2.接收响应</p><p> console.log(xhr.response)</p><p>}</p><p>//1.发请求</p><p>xhr.open('GET', "http://localhost:8080/api/students")</p><p>xhr.responseType = 'json'</p><p>xhr.send()</p><p>但这套 api 虽然功能强大,但比较老,不直接支持 Promise,因此有必要对其进行改造 </p><p>function get(url: string) {</p><p> return new Promise((resolve, reject)=>{</p><p> const xhr = new XMLHttpRequest()</p><p> xhr.onload = function() {</p><p> if(xhr.status === 200){</p><p> resolve(xhr.response)</p><p> } else if(xhr.status === 404) {</p><p> reject(xhr.response)</p><p> } // 其它情况也需考虑,这里简化处理</p><p> }</p><p> xhr.open('GET', url)</p><p> xhr.responseType = 'json'</p><p> xhr.send()</p><p> })</p><p>}</p><p>Promise 对象适合用来封装异步操作,并可以配合 await 一齐使用Promise 在构造时,需要一个箭头函数,箭头函数有两个参数 resolve 和 reject </p><p> resolve 是异步操作成功时被调用,把成功的结果传递给它,最后会作为 await 的结果返回reject 在异步操作失败时被调用,把失败的结果传递给它,最后在 catch 块被捉住 await 会一直等到 Promise 内调用了 resolve 或 reject 才会继续向下运行 </p><p>axios </p><p>基本用法 </p><p>axios 就是对 xhr api 的封装,手法与前面例子类似 </p><p>安装依赖 </p><p>npm install axios</p><p>一个简单的例子 </p><p><script setup lang="ts"></p><p>import axios from 'axios'</p><p>import {ref, onMounted} from 'vue'</p><p>const count = ref(0)</p><p>async function getData() {</p><p> const resp = await axios.get('http://localhost:8080/source/getIndexInfo/2')</p><p> count.value = resp.data.data.moduleList.length</p><p>}</p><p>onMounted(() => { //页面组件加载完成后执行</p><p> getData()</p><p>})</p><p></script></p><p><template></p><p> <h2>数量为 {{ count }}</h2></p><p></template></p><p>onMounted 指 vue 组件生成的 html 代码片段,挂载完毕后被执行 </p><p>再来看一个 post 例子 </p><p><script setup lang="ts"></p><p>import { ref } from "vue";</p><p>import axios from "axios";</p><p>const student = ref({</p><p> name: '',</p><p> sex: '男',</p><p> age: 18</p><p>})</p><p>async function addStudent() {</p><p> console.log(student.value)</p><p> //第二个参数是请求体数据,数据格式默认是json,后端使用@RequestBody接收</p><p> const resp = await axios.post('/api/students', student.value)</p><p> console.log(resp.data.data)</p><p>}</p><p></script></p><p><template></p><p> <div></p><p> <div></p><p> <input type="text" placeholder="请输入姓名" v-model="student.name"/></p><p> </div></p><p> <div></p><p> <label for="">请选择性别</label></p><p> 男 <input type="radio" value="男" v-model="student.sex"/> </p><p> 女 <input type="radio" value="女" v-model="student.sex"/></p><p> </div></p><p> <div></p><p> <input type="number" placeholder="请输入年龄" v-model="student.age"/></p><p> </div></p><p> <div></p><p> <input type="button" value="添加" @click="addStudent"/></p><p> </div></p><p> </div></p><p></template></p><p><style scoped></p><p>div {</p><p> font-size: 14px;</p><p>}</p><p></style></p><p>环境变量 </p><p>开发环境下,联调的后端服务器地址是 http://localhost:8080,上线改为生产环境后,后端服务器地址为 http://itheima.com </p><p>这就要求我们区分开发环境和生产环境,这件事交给构建工具 vite 来做 </p><p>默认情况下,vite 支持上面两种环境,分别对应根目录下两个配置文件 </p><p>.env.development - 开发环境.env.production - 生产环境 </p><p>针对以上需求,分别在两个文件中加入 </p><p>VITE_BACKEND_API_BASE_URL = 'http://localhost:8080' //.env.development</p><p>VITE_BACKEND_API_BASE_URL = 'http://itheima.com' //.env.production</p><p>然后在代码中使用 vite 给我们提供的特殊对象 import.meta.env,就可以获取到 VITE_BACKEND_API_BASE_URL 在不同环境下的值 </p><p>import.meta.env.VITE_BACKEND_API_BASE_URL</p><p>默认情况下,不能智能提示自定义的环境变量,做如下配置:新增文件 src/env.d.ts 并添加如下内容 </p><p>/// <reference types="vite/client" /></p><p>interface ImportMetaEnv {</p><p> readonly VITE_BACKEND_API_BASE_URL: string</p><p> // 更多环境变量...</p><p>}</p><p>interface ImportMeta {</p><p> readonly env: ImportMetaEnv</p><p>}</p><p>参考文档地址 环境变量和模式 | Vite 官方中文文档 (vitejs.dev) </p><p>baseURL </p><p>可以自己创建一个 axios 对象,方便添加默认设置,新建文件 /src/api/request.ts </p><p>// 创建新的 axios 对象</p><p>import axios from 'axios'</p><p>const _axios = axios.create({</p><p> baseURL: import.meta.env.VITE_BACKEND_API_BASE_URL</p><p>})</p><p>export default _axios</p><p>然后在其它组件中引用这个 ts 文件,例如 /src/views/E8.vue,就不用自己拼接路径前缀了 </p><p><script setup lang="ts"></p><p>import axios from '../api/request' //导入自己写的axios</p><p>// ...</p><p>await axios.post('/api/students', ...) //无需写地址前缀 </p><p></script></p><p>拦截器 </p><p>// 创建新的 axios 对象</p><p>import axios from 'axios'</p><p>const _axios = axios.create({</p><p> baseURL: import.meta.env.VITE_BACKEND_API_BASE_URL</p><p>})</p><p>// 请求拦截器</p><p>_axios.interceptors.request.use(</p><p> (config)=>{ // 请求成功时的处理,统一添加请求头(如JWT)</p><p> config.headers = {</p><p> Authorization: 'aaa.bbb.ccc'</p><p> }</p><p> return config</p><p> },</p><p> (error)=>{ // 请求出错时的处理</p><p> return Promise.reject(error)</p><p> }</p><p>)</p><p>// 响应拦截器</p><p>_axios.interceptors.response.use(</p><p> (response)=>{ // 状态码 2xx</p><p> // 这里的code是自定义的错误码</p><p> if(response.data.code === 200) {</p><p> return response</p><p> } </p><p> else if(response.data.code === 401) { </p><p> // 情况1</p><p> return Promise.resolve({})</p><p> }</p><p> // ... </p><p> },</p><p> (error)=>{ // 状态码 4xx,5xx</p><p> console.error(error) //统一异常处理</p><p> if(error.response.status === 400) {</p><p> // 情况1</p><p> } else if(error.response.status === 401) {</p><p> // 情况2</p><p> } </p><p> // ...</p><p> // return Promise.reject(error)</p><p> return Promise.resolve({})</p><p> }</p><p>)</p><p>export default _axios</p><p>处理响应时,又分成两种情况 </p><p>后端返回的是标准响应状态码,这时会走响应拦截器第二个箭头函数,用 error.response.status 做分支判断后端返回的响应状态码总是200,用自定义错误码表示出错,这时会走响应拦截器第一个箭头函数,用 response.data.code 做分支判断 </p><p>另外 </p><p>Promise.reject(error) 类似于将异常继续向上抛出,异常由调用者(Vue组件)来配合 try … catch 来处理Promise.resolve({}) 表示错误已解决,返回一个空对象,调用者中接到这个空对象时,需要配合 ?. 来避免访问不存在的属性 </p><p>条件与列表 </p><p>首先,新增模型数据 '/src/model/Model8080.ts' </p><p>export interface Student {</p><p> id: number;</p><p> name: string;</p><p> sex: string;</p><p> age: number;</p><p>}</p><p>// 如果 spring 错误,返回的对象格式</p><p>export interface SpringError {</p><p> timestamp: string,</p><p> status: number,</p><p> error: string,</p><p> message: string,</p><p> path: string</p><p>}</p><p>// 如果 spring 成功,返回 list 情况</p><p>export interface SpringList<T> {</p><p> data: T[],</p><p> message?: string,</p><p> code: number</p><p>}</p><p>// 如果 spring 成功,返回 page 情况</p><p>export interface SpringPage<T> {</p><p> data: { list: T[], total: number },</p><p> message?: string,</p><p> code: number</p><p>}</p><p>// 如果 spring 成功,返回 string 情况</p><p>export interface SpringString {</p><p> data: string,</p><p> message?: string,</p><p> code: number</p><p>}</p><p>import { AxiosResponse } from 'axios'</p><p>export interface AxiosRespError extends AxiosResponse<SpringError> { }</p><p>export interface AxiosRespList<T> extends AxiosResponse<SpringList<T>> { }</p><p>export interface AxiosRespPage<T> extends AxiosResponse<SpringPage<T>> { }</p><p>export interface AxiosRespString extends AxiosResponse<SpringString> { }</p><p>AxiosRespPage 代表分页时的响应类型AxiosRespList 代表返回集合时的响应类型AxiosRespString 代表返回字符串时的响应类型AxiosRespError 代表 Spring 出错时时的响应类型 </p><p><script lang="ts" setup></p><p>import { ref, onMounted } from "vue";</p><p>import axios from "../api/request";</p><p>import { Student, SpringList } from "../model/Model8080";</p><p>// 说明 students 数组类型为 Student[]</p><p>const students = ref<Student[]>([]);</p><p>async function getStudents() {</p><p> // 说明 resp.data 类型是 SpringList<Student></p><p> const resp = await axios.get<SpringList<Student>>("/api/students"); </p><p> console.log(resp.data.data);</p><p> students.value = resp.data.data;</p><p>}</p><p>onMounted(() => getStudents());</p><p></script></p><p><template></p><p> <div class="outer"></p><p> <div class="title">学生列表</div></p><p> <div class="thead"></p><p> <div class="row bold"></p><p> <div class="col">编号</div></p><p> <div class="col">姓名</div></p><p> <div class="col">性别</div></p><p> <div class="col">年龄</div></p><p> </div></p><p> </div></p><p> <div class="tbody"></p><p> <div v-if="students.length === 0">暂无数据</div></p><p> <template v-else></p><p> <div class="row" v-for="s of students" :key="s.id"></p><p> <div class="col">{{ s.id }}</div></p><p> <div class="col">{{ s.name }}</div></p><p> <div class="col">{{ s.sex }}</div></p><p> <div class="col">{{ s.age }}</div></p><p> </div></p><p> </template></p><p> </div></p><p> </div></p><p></template></p><p><style scoped></p><p>......</p><p></style></p><p>加入泛型是为了更好的提示v-if 与 v-else 不能和 v-for 处于同一标签template 标签还有一个用途,就是用它少生成一层真正 html 代码可以看到将结果封装为响应式数据还是比较繁琐的,后面会使用 useRequest 改进 </p><p>监听器 </p><p>利用监听器,可以在【响应式】的基础上添加一些副作用,把更多的东西变成【响应式的】 </p><p> 原本只是数据变化 => 页面更新 watch 可以在数据变化时 => 其它更新 </p><p><template></p><p> <input type="text" v-model="name" /></p><p></template></p><p><script setup lang="ts"></p><p>import { ref, watch } from "vue";</p><p>function useStorage(name: string) {</p><p> const data = ref(sessionStorage.getItem(name) ?? "");</p><p> // 参数1:要监听的数据</p><p> // 参数2:数据变化时要执行的箭头函数</p><p> watch(data, (newValue) => {</p><p> sessionStorage.setItem(name, newValue);</p><p> });</p><p> return data;</p><p>}</p><p>const name = useStorage("name");</p><p></script></p><p>名称为 useXXXX 的函数,作用是返回带扩展功能的【响应式】数据localStorage 即使浏览器关闭,数据还在sessionStorage 数据工作在浏览器活动期间 </p><p>vueuse </p><p>安装 </p><p>npm install @vueuse/core</p><p>一些函数的用法 </p><p><template></p><p> <h3>X: {{x}}</h3></p><p> <h3>Y: {{y}}</h3></p><p> <h3>{{count}}</h3></p><p> <input type="button" @click="inc()" value="+"></p><p> <input type="button" @click="dec()" value="-"></p><p> <input type="text" v-model="name"></p><p></template></p><p><script setup lang="ts"></p><p>import { useMouse, useCounter, useStorage } from '@vueuse/core'</p><p>const {x, y} = useMouse() //获取鼠标坐标</p><p>const {count, inc, dec} = useCounter() //使用计数器</p><p>const name = useStorage("name", "") //使用存储</p><p></script></p><p>useRequest </p><p>响应式的 axios 封装,官网地址 一个 Vue 请求库 | VueRequest (attojs.org) </p><p>首先安装 vue-request </p><p>npm install vue-request@next</p><p>组件使用 </p><p><template></p><p> <h3 v-if="students.length === 0">暂无数据</h3></p><p> <ul v-else></p><p> <li v-for="s of students" :key="s.id"></p><p> <span>{{s.name}}</span></p><p> <span>{{s.sex}}</span></p><p> <span>{{s.age}}</span></p><p> </li></p><p> </ul></p><p></template></p><p><script setup lang="ts"></p><p>import axios from "../api/request"</p><p>import { useRequest } from 'vue-request'</p><p>import { computed } from 'vue'</p><p>import { AxiosRespList, Student } from '../model/Model8080'</p><p>// data 代表就是 axios 的响应对象</p><p>const { data } = useRequest<AxiosRespList<Student>>(() => axios.get('/api/students'))</p><p>const students = computed(()=>{</p><p> return data?.value?.data.data || []</p><p>})</p><p></script></p><p><style scoped></p><p>......</p><p></style></p><p>data.value 的取值一开始是 undefined,随着响应返回变成 axios 的响应对象用 computed 进行适配 </p><p>usePagination </p><p>在 src/model/Model8080.ts 中补充类型说明 </p><p>export interface StudentQueryDto {</p><p> name?: string,</p><p> sex?: string,</p><p> age?: string, // 18,20</p><p> page: number,</p><p> size: number</p><p>}</p><p>js 中类似于 18,20 这样以逗号分隔字符串,会在 get 传参时转换为 java 中的整数数组 </p><p>编写组件 </p><p><template></p><p> <input type="text" placeholder="请输入姓名" v-model="dto.name"></p><p> <select v-model="dto.sex"></p><p> <option value="" selected>请选择性别</option></p><p> <option value="男">男</option></p><p> <option value="女">女</option></p><p> </select></p><p> <input type="text" placeholder="请输入年龄范围" v-model="dto.age"></p><p> <br></p><p> <input type="text" placeholder="请输入页码" v-model="dto.page"></p><p> <input type="text" placeholder="请输入页大小" v-model="dto.size"></p><p> <input type="button" value="搜索" @click="search"></p><p> <hr></p><p> <h3 v-if="students.length === 0">暂无数据</h3></p><p> <ul v-else></p><p> <li v-for="s of students" :key="s.id"></p><p> <span>{{s.name}}</span></p><p> <span>{{s.sex}}</span></p><p> <span>{{s.age}}</span></p><p> </li></p><p> </ul></p><p> <hr></p><p> 总记录数{{total}} 总页数{{totalPage}}</p><p></template></p><p><script setup lang="ts"></p><p>import axios from "../api/request"</p><p>import { usePagination } from 'vue-request'</p><p>import { computed, ref } from 'vue'</p><p>import { AxiosRespPage, Student, StudentQueryDto } from '../model/Model8080'</p><p>const dto = ref<StudentQueryDto>({name:'', sex:'', age:'', page:1, size:5})</p><p>// data 代表就是 axios 的响应对象</p><p>// 泛型参数1: 响应类型</p><p>// 泛型参数2: 请求类型</p><p>const { data, total, totalPage, run } = usePagination<AxiosRespPage<Student>, StudentQueryDto[]>(</p><p> (d) => axios.get('/api/students/q', {params: d}), // 箭头函数</p><p> {</p><p> defaultParams: [ dto.value ], // 默认参数, 会作为参数传递给上面的箭头函数</p><p> pagination: {</p><p> currentKey: 'page', // 指明当前页属性</p><p> pageSizeKey: 'size', // 指明页大小属性</p><p> totalKey: 'data.data.total' // 指明总记录数属性</p><p> } </p><p> } // 选项</p><p>)</p><p>const students = computed(()=>{</p><p> return data?.value?.data.data.list || []</p><p>})</p><p>function search() {</p><p> run(dto.value) // 会作为参数传递给usePagination的箭头函数</p><p>}</p><p></script></p><p><style scoped></p><p>......</p><p></style></p><p>usePagination 只需要定义一次,后续还想用它内部的 axios 发请求,只需调用 run 函数 </p><p>子组件 </p><p>子组件可以用于抽取公共组件。 </p><p>定义子组件 Child1 </p><p><template></p><p> <div class="container"></p><p> <div class="card"></p><p> <div></p><p> <p class="name">{{name}}</p></p><p> <p class="location">{{country}}</p></p><p> </div></p><p> <img onclick="xtip.photoApp('jzpic',{index:'3'})" data-xphoto="jzpic" src="avatar || " title="前端学习笔记(Vue、TypeScript、JavaScript) 第3张" alt="前端学习笔记(Vue、TypeScript、JavaScript) 第3张" :src="avatar || "></p><p> </div></p><p> </div></p><p></template></p><p><script setup lang="ts"></p><p>// 定义属性, 编译宏</p><p>defineProps<{name:string,country:string,avatar?:string}>()</p><p></script></p><p><style scoped></p><p>......</p><p></style></p><p>父组件引用 </p><p><template></p><p> <Child1 name="张三" country="中国" avatar="/src/assets/vue.svg"></Child1></p><p> <Child1 name="李四" country="印度" avatar="/vite.svg"></Child1></p><p> <Child1 name="王五" country="韩国" ></Child1></p><p></template></p><p><script lang="ts" setup></p><p>import Child1 from '../components/Child1.vue';</p><p></script></p><p>通用型代码 </p><p><!-- 父组件 --></p><p><template></p><p> <Child1 v-for="u of users" </p><p> :name="u.name.first" </p><p> :country="u.location.country" </p><p> :avatar="u.picture.medium"</p><p> :key="u.login.username"></Child1></p><p></template></p><p><script setup lang="ts"></p><p>import axios from "axios";</p><p>import { useRequest } from "vue-request";</p><p>import { computed } from "vue";</p><p>import { AxiosRespResults } from '../model/ModelRandomUser'</p><p>import Child1 from "../components/Child1.vue";</p><p>const { data } = useRequest<AxiosRespResults>(</p><p> ()=>axios.get('https://randomuser.me/api/?results=3')</p><p>)</p><p>const users = computed(()=>{</p><p> return data.value?.data.results || []</p><p>})</p><p></script></p><p>Vue3 进阶 </p><p>组件总览 - Ant Design Vue (antdv.com) </p><p>Ant-design-vue </p><p>添加必要插件 </p><p>npm install ant-design-vue</p><p>ant-design-vue 组件库插件 </p><p>引入 antdv 功能,修改 main.ts(全局导入) </p><p>import { createApp } from 'vue'</p><p>import './style.css'</p><p>import App from './App.vue'</p><p>import antd from 'ant-design-vue'</p><p>import 'ant-design-vue/dist/antd.css'</p><p>createApp(App).use(antd).mount('#app')</p><p>表格 </p><p><template></p><p> <a-table :columns="colums" :dataSource="studens" rowKey="id"></a-table></p><p></template></p><p><script setup lang="ts"></p><p>import axios from "../api/request";</p><p>import { ref, computed } from "vue";</p><p>import { useRequest } from "vue-request";</p><p>import { AxiosRespList, Student } from "../model/Model8080";</p><p>const {data} = useRequest<AxiosRespList<Student>>(</p><p> () => {</p><p> return axios.get('/api/students')</p><p> }</p><p>)</p><p>const studens = computed(()=>{</p><p> return data.value?.data.data || []</p><p>})</p><p>const colums = ref([</p><p> {</p><p> title: "编号",</p><p> dataIndex: "id"</p><p> },</p><p> {</p><p> title: "姓名",</p><p> dataIndex: "name"</p><p> },</p><p> {</p><p> title: "性别",</p><p> dataIndex: "sex"</p><p> },</p><p> {</p><p> title: "年龄",</p><p> dataIndex: "age"</p><p> }</p><p>])</p><p></script></p><p>分页 </p><p><template></p><p> <a-table :columns="colums" :dataSource="studens" rowKey="id" </p><p> :pagination="pagination" @change="tableChange"></a-table></p><p></template></p><p><script setup lang="ts"></p><p>import axios from "../api/request";</p><p>import { ref, computed } from "vue";</p><p>import { usePagination } from "vue-request";</p><p>import { AxiosRespPage, Student, StudentQueryDto } from "../model/Model8080";</p><p>import { PaginationProps } from "ant-design-vue";</p><p>const dto = ref({page: 1, size: 5})</p><p>const {data, total, run} = usePagination<AxiosRespPage<Student>, StudentQueryDto[]>(</p><p> (d) => axios.get('/api/students/query', {params:d}),</p><p> {</p><p> defaultParams: [dto.value],</p><p> pagination: {</p><p> currentKey: "page",</p><p> pageSizeKey: "size",</p><p> totalKey: "data.data.total"</p><p> }</p><p> }</p><p>)</p><p>//在页号或页大小改变时调用</p><p>function tableChange(paginationProps: PaginationProps) {</p><p> console.log(paginationProps)</p><p> dto.value.page = paginationProps.current ?? 1</p><p> dto.value.size = paginationProps.pageSize ?? 5</p><p> run(dto.value)</p><p>}</p><p>//分页选项</p><p>const pagination = computed<PaginationProps>(()=>{</p><p> return {</p><p> current: dto.value.page, //当前页</p><p> pageSize: dto.value.size, //页大小</p><p> total: total.value, //总记录数</p><p> showSizeChanger: true, //展示页大小设置框</p><p> pageSizeOptions: ["3", "5", "10", "20", "50", "100"] //自定义页大小</p><p> }</p><p>})</p><p>const studens = computed(()=>{</p><p> return data.value?.data.data.list || []</p><p>})</p><p>const colums = ref([</p><p> {</p><p> title: "编号",</p><p> dataIndex: "id"</p><p> },</p><p> {</p><p> title: "姓名",</p><p> dataIndex: "name"</p><p> },</p><p> {</p><p> title: "性别",</p><p> dataIndex: "sex"</p><p> },</p><p> {</p><p> title: "年龄",</p><p> dataIndex: "age"</p><p> }</p><p>])</p><p></script></p><p>搜索、删除、新增、修改 </p><p><template></p><p> <a-row></p><p> <a-col :span="2"></p><p> <a-button type="primary" size="small" @click="onInsert">新增</a-button></p><p> </a-col></p><p> <a-col :span="2"></p><p> <a-popconfirm title="确认要删除选中学生吗?"</p><p> ok-text="确定" cancel-text="取消" @confirm="onDeleteIds"</p><p> @visibleChange="onVisibleChange" :visible="visible"></p><p> <a-button type="primary" size="small">删除选中</a-button></p><p> </a-popconfirm></p><p> </a-col></p><p> <a-col :span="6"></a-col></p><p> <a-col :span="4"></p><p> <!-- 内容跟普通的HTML标签绑定,直接用v-model --></p><p> <!-- 内容跟vue组件绑定,要使用v-model:value --></p><p> <a-input placeholder="输姓名" size="small" v-model:value="dto.name"></a-input></p><p> </a-col></p><p> <a-col :span="4"></p><p> <a-select placeholder="选性别" :allowClear="true" size="small" v-model:value="dto.sex"></p><p> <a-select-option value="男">男</a-select-option></p><p> <a-select-option value="女">女</a-select-option></p><p> </a-select></p><p> </a-col></p><p> <a-col :span="4"></p><p> <a-select placeholder="选年龄" :allowClear="true" size="small" v-model:value="dto.age"></p><p> <a-select-option value="0,20">20以下</a-select-option></p><p> <a-select-option value="21,30">21~30</a-select-option></p><p> <a-select-option value="31,40">31~40</a-select-option></p><p> <a-select-option value="40,120">40以上</a-select-option></p><p> </a-select></p><p> </a-col></p><p> <a-col :span="2"></p><p> <a-button @click="tableChange" type="primary" size="small">搜索</a-button></p><p> </a-col></p><p> </a-row></p><p> <hr/></p><p> <a-table :columns="colums" :dataSource="studens" rowKey="id" </p><p> :pagination="pagination" @change="tableChange"</p><p> :row-selection="{selectedRowKeys:ids, onChange:onSelectChange}"></p><p> <template #bodyCell="{column, record}"></p><p> <template v-if="column.dataIndex === 'name'"></p><p> {{ record.name + (record.sex === '男' ? '(大侠)' : '(女侠)') }}</p><p> </template></p><p> <template v-else-if="column.dataIndex === 'operation'"></p><p> <a @click="onUpdate(record)">修改</a></p><p> <a-divider type="vertical"></a-divider></p><p> <a-popconfirm title="确认要删除该学生吗?" ok-text="确定" cancel-text="取消" </p><p> @confirm="onDelete(record.id)"></p><p> <a>删除</a></p><p> </a-popconfirm></p><p> </template></p><p> </template></p><p> </a-table></p><p> <!-- <A4Save :id="id" :dto="saveDto" :visible="saveVisible"></A4Save> --></p><p> <a-modal :visible="saveVisible" :title="title" @ok="onOk" @cancel="onCancel"></p><p> <a-form></p><p> <a-form-item label="编号" v-if="id"></p><p> <a-input readonly v-bind:value="id"></a-input></p><p> </a-form-item></p><p> <a-form-item label="姓名"></p><p> <a-input v-model:value="saveDto.name"></a-input></p><p> </a-form-item></p><p> <a-form-item label="性别"></p><p> <a-radio-group v-model:value="saveDto.sex"></p><p> <a-radio-button value="男">男</a-radio-button></p><p> <a-radio-button value="女">女</a-radio-button></p><p> </a-radio-group></p><p> </a-form-item></p><p> <a-form-item label="年龄"></p><p> <a-input-number v-model:value="saveDto.age"></a-input-number></p><p> </a-form-item></p><p> </a-form></p><p> </a-modal></p><p></template></p><p><script setup lang="ts"></p><p>import axios from "../api/request";</p><p>import { ref, computed } from "vue";</p><p>import { usePagination, useRequest } from "vue-request";</p><p>import { AxiosRespPage, Student, StudentQueryDto, AxiosRespString, StudentSaveDto } from "../model/Model8080";</p><p>import { PaginationProps } from "ant-design-vue";</p><p>import A4Save from "./A4Save.vue";</p><p>const dto = ref({page: 1, size: 5, name: "", sex: null, age: null})</p><p>//====================新增修改功能======================</p><p>const id = ref(0)</p><p>const saveDto = ref({name:'', sex:'男', age:20})</p><p>const saveVisible = ref(false)</p><p>const title = ref('新增学生')</p><p>const {runAsync:insert} = useRequest<AxiosRespString, StudentSaveDto[]>(</p><p> (insertDto) => axios.post('/api/students', insertDto), </p><p> {</p><p> manual: true</p><p> }</p><p>)</p><p>const {runAsync:update} = useRequest<AxiosRespString, StudentSaveDto[]>(</p><p> (updateDto) => axios.put(`/api/students/${id.value}`, updateDto), </p><p> {</p><p> manual: true</p><p> }</p><p>)</p><p>function onInsert() {</p><p> //恢复初始值</p><p> id.value = 0</p><p> saveDto.value = {name:'', sex:'男', age:20}</p><p> title.value = '新增学生'</p><p> saveVisible.value = true</p><p>}</p><p>function onUpdate(record: Student) {</p><p> saveVisible.value = true</p><p> id.value = record.id</p><p> // saveDto.value.name = record.name</p><p> // saveDto.value.sex = record.sex</p><p> // saveDto.value.age = record.age</p><p> Object.assign(saveDto.value, record)</p><p> title.value = '修改学生'</p><p>}</p><p>async function onOk() {</p><p> saveVisible.value = false</p><p> if (id.value === 0) {</p><p> await insert(saveDto.value)</p><p> } else {</p><p> await update(saveDto.value)</p><p> }</p><p> search(dto.value)</p><p>}</p><p>function onCancel() {</p><p> saveVisible.value = false</p><p>}</p><p>//==================搜索功能============================</p><p>//默认组件加载时就会执行一次</p><p>const {data, total, run:search} = usePagination<AxiosRespPage<Student>, StudentQueryDto[]>(</p><p> (d) => axios.get('/api/students/query', {params:d}),</p><p> {</p><p> defaultParams: [dto.value],</p><p> pagination: {</p><p> currentKey: "page",</p><p> pageSizeKey: "size",</p><p> totalKey: "data.data.total"</p><p> }</p><p> }</p><p>)</p><p>//在页号或页大小改变时调用</p><p>function tableChange(paginationProps: PaginationProps) {</p><p> console.log(paginationProps)</p><p> dto.value.page = paginationProps.current ?? 1</p><p> dto.value.size = paginationProps.pageSize ?? 5</p><p> search(dto.value)</p><p>}</p><p>//分页选项</p><p>const pagination = computed<PaginationProps>(()=>{</p><p> return {</p><p> current: dto.value.page, //当前页</p><p> pageSize: dto.value.size, //页大小</p><p> total: total.value, //总记录数</p><p> showSizeChanger: true, //展示页大小设置框</p><p> pageSizeOptions: ["3", "5", "10", "20", "50", "100"] //自定义页大小</p><p> }</p><p>})</p><p>const studens = computed(()=>{</p><p> return data.value?.data.data.list || []</p><p>})</p><p>//==========================删除功能=========================</p><p>async function onDelete(id:number) {</p><p> await deleteById(id) //使用await使得deleteById方法响应之后再执行search方法</p><p> search(dto.value)</p><p>}</p><p>const {runAsync:deleteById} = useRequest<AxiosRespString, number[]>(</p><p> (id) => axios.delete(`/api/students/${id}`),</p><p> {</p><p> manual: true, //表示手动调用请求,而不是组件加载就执行一次</p><p> }</p><p>)</p><p>//======================删除选中功能===========================</p><p>const ids = ref<number[]>([])</p><p>function onSelectChange(keys:number[]) {</p><p> console.log(keys)</p><p> ids.value = keys</p><p>}</p><p>async function onDeleteIds() {</p><p> await deleteByIds(ids.value)</p><p> ids.value = []</p><p> search(dto.value)</p><p>}</p><p>const {runAsync:deleteByIds} = useRequest<AxiosRespString, number[][]>(</p><p> (ids) => axios.delete('/api/students', {data:ids}), //data对应着请求体中的数据</p><p> {</p><p> manual: true</p><p> }</p><p>)</p><p>const visible = ref(false)</p><p>function onVisibleChange(v:boolean) {</p><p> if(!v) { // 希望隐藏</p><p> visible.value = false</p><p> } else { // 希望显示</p><p> visible.value = ids.value.length > 0</p><p> }</p><p>}</p><p>//==============================================================</p><p>const colums = ref([</p><p> {</p><p> title: "编号",</p><p> dataIndex: "id"</p><p> },</p><p> {</p><p> title: "姓名",</p><p> dataIndex: "name"</p><p> },</p><p> {</p><p> title: "性别",</p><p> dataIndex: "sex"</p><p> },</p><p> {</p><p> title: "年龄",</p><p> dataIndex: "age"</p><p> },</p><p> {</p><p> title: "操作",</p><p> dataIndex: "operation"</p><p> }</p><p>])</p><p></script></p><p>全局消息 </p><p>在 request.ts 中对响应消息统一处理 </p><p>import { message } from 'ant-design-vue'</p><p>// ...</p><p>// 响应拦截器</p><p>_axios.interceptors.response.use(</p><p> (response)=>{ // 状态码 2xx</p><p> if(response.data.message) {</p><p> message.success(response.data.message, 3)</p><p> }</p><p> // ... </p><p> },</p><p> (error)=>{ // 状态码 > 2xx, 400,401,403,404,500</p><p> // ...</p><p> }</p><p>)</p><p>表单校验 </p><p><template></p><p> <a-modal :visible="visible" :title="title" @ok="onOk" @cancel="onCancel"></p><p> <a-form></p><p> <a-form-item label="编号" v-if="id"></p><p> <a-input readonly v-model:value="id"></a-input></p><p> </a-form-item></p><p> <a-form-item label="姓名" v-bind="validateInfos.name"><!-- 绑定错误校验信息 --></p><p> <a-input v-model:value="dto.name"></a-input></p><p> </a-form-item></p><p> <a-form-item label="性别" v-bind="validateInfos.sex"></p><p> <a-radio-group v-model:value="dto.sex"></p><p> <a-radio-button value="男">男</a-radio-button></p><p> <a-radio-button value="女">女</a-radio-button></p><p> </a-radio-group></p><p> </a-form-item></p><p> <a-form-item label="年龄" v-bind="validateInfos.age"></p><p> <a-input-number v-model:value="dto.age"></a-input-number></p><p> </a-form-item></p><p> </a-form></p><p> </a-modal></p><p></template></p><p><script setup lang="ts"></p><p>import axios from "../api/request";</p><p>import { ref, computed } from "vue";</p><p>import { useRequest } from "vue-request";</p><p>import { StudentSaveDto, AxiosRespString } from "../model/Model8080";</p><p>import { Form } from 'ant-design-vue'</p><p>//......</p><p>async function onOk() {</p><p> try {</p><p> //先进行数据校验</p><p> await validate()</p><p> saveVisible.value = false</p><p> if (id.value === 0) {</p><p> await insert(saveDto.value)</p><p> } else {</p><p> await update(saveDto.value)</p><p> }</p><p> search(dto.value)</p><p> } catch(e) {</p><p> console.error(e)</p><p> } </p><p>}</p><p>//======================数据校验============================</p><p>const rules = ref({</p><p> name: [</p><p> {required: true, message: "姓名不能为空"},</p><p> {min: 2, message: "字符数至少为2"}</p><p> ],</p><p> sex: [</p><p> {required: true, message: "性别不能为空"}</p><p> ],</p><p> age: [</p><p> {required: true, message: "年龄不能为空"},</p><p> {min: 10, message: "年龄最小为10岁", type: "number"},</p><p> {max: 120, message: "年龄最大为120岁", type: "number"}</p><p> ]</p><p>})</p><p>// 参数1: 待校验的数据</p><p>// 参数2: 校验规则</p><p>const { validateInfos, validate } = Form.useForm(saveDto, rules)</p><p></script></p><p>vue-router </p><p>安装 </p><p>npm install vue-router@4</p><p>创建 router </p><p>首先创建一个 /src/router/a5router.ts 文件,在其中定义路由 </p><p>import {createRouter, createWebHashHistory} from 'vue-router'</p><p>import A51 from '../views/A51.vue'</p><p>import A52 from '../views/A52.vue'</p><p>// 路由 => 路径和组件之间的对应关系</p><p>const routes = [</p><p> {</p><p> path: '/a1',</p><p> component: A51</p><p> },</p><p> {</p><p> path: '/a2', </p><p> component: A52</p><p> }</p><p>]</p><p>const router = createRouter({ </p><p> history: createWebHashHistory(), // 路径格式</p><p> routes: routes // 路由数组</p><p>})</p><p>export default router</p><p> createWebHashHistory 是用 # 符号作为【单页面】跳转技术,上面两个路由访问时路径格式为 </p><p> http://localhost:7070/#/a1http://localhost:7070/#/a2 每个路由都有两个必须属性 </p><p> path:路径 component:组件 createRouter 用来创建 router 对象,作为默认导出 </p><p>需要在 main.ts 中导入 router 对象: </p><p>// ...</p><p>import A5 from './views/A5.vue' // vue-router</p><p>import router from './router/a5router'</p><p>createApp(A5).use(antdv).use(router).mount('#app')</p><p>A5 是根组件,不必在 router 中定义,但需要在其中定义 router-view,用来控制路由跳转后,A51、A52 这些组件的显示位置,内容如下 </p><p><template></p><p> <div class="a5"></p><p> <router-view></router-view></p><p> </div></p><p></template></p><p>动态导入 </p><p>import {createRouter, createWebHashHistory} from 'vue-router'</p><p>import A51 from '../views/A51.vue'</p><p>import A52 from '../views/A52.vue' //静态导入</p><p>const routes = [</p><p> // ...</p><p> {</p><p> path: '/a3',</p><p> component: () => import('../views/A53.vue') //动态导入</p><p> }</p><p>]</p><p>用 import 关键字导入,效果是打包时会将组件的 js 代码都打包成一个大的 js 文件,如果组件非常多,会影响页面加载速度而 import 函数导入(动态导入),则是按需加载,即 </p><p> 当路由跳转到 /a3 路径时,才会去加载 A53 组件对应的 js 代码vue-router 官方推荐采用动态导入 </p><p>嵌套路由 </p><p>如果希望再嵌套更深层次的路由跳转,例如:希望在 A53 组件内再进行路由跳转 </p><p>首先,修改 A53.vue </p><p><template></p><p> <div class="a53"></p><p> <router-view></router-view></p><p> </div></p><p></template></p><p>其次,再修改 /src/router/a5router.ts 文件 内容 </p><p>import {createRouter, createWebHashHistory} from 'vue-router'</p><p>import A51 from '../views/A51.vue'</p><p>import A52 from '../views/A52.vue'</p><p>const routes = [</p><p> // ...</p><p> {</p><p> path: '/a3',</p><p> component: () => import('../views/A53.vue'),</p><p> children: [ //子组件</p><p> {</p><p> path: 'student',</p><p> component: () => import('../views/A531.vue')</p><p> },</p><p> {</p><p> path: 'teacher',</p><p> component: () => import('../views/A532.vue')</p><p> }</p><p> ]</p><p> }</p><p>]</p><p>重定向 </p><p>用法1 </p><p>import {createRouter, createWebHashHistory} from 'vue-router'</p><p>import A51 from '../views/A51.vue'</p><p>import A52 from '../views/A52.vue'</p><p>const routes = [</p><p> // ...</p><p> {</p><p> path: '/a3',</p><p> component: () => import('../views/A53.vue'),</p><p> redirect: '/a3/student', // 重定向到另外路径</p><p> children: [</p><p> {</p><p> path: 'student',</p><p> component: () => import('../views/A531.vue')</p><p> },</p><p> {</p><p> path: 'teacher',</p><p> component: () => import('../views/A532.vue')</p><p> }</p><p> ]</p><p> }</p><p>]</p><p>// ...</p><p>效果是,页面输入 /a3,紧接着会重定向跳转到 /a3/student </p><p>用法2 </p><p>import {createRouter, createWebHashHistory} from 'vue-router'</p><p>import A51 from '../views/A51.vue'</p><p>import A52 from '../views/A52.vue'</p><p>const routes = [</p><p> {</p><p> path: '/a1',</p><p> component: A51</p><p> },</p><p> {</p><p> path: '/a2', </p><p> component: A52</p><p> },</p><p> // ...</p><p> {</p><p> path: '/:pathMatcher(.*)*', // 可以匹配剩余的路径</p><p> redirect: '/a2'</p><p> }</p><p>]</p><p>// ...</p><p>效果是,当页面输入一个不存在路径 /aaa 时,会被 path: '/:pathMatcher(.*)*' 匹配到,然后重定向跳转到 A52 组件(404页面)去。 </p><p>pinia </p><p>作用:组件间共享数据。 </p><p>需求:在组件 p1 里更新了数据,主页组件也同步更新显示 </p><p>storage 虽然可以实现多个组件的数据共享,但是需要【主动访问】才能获取更新后的数据本例中由于没有涉及主页组件的 mounted 操作,因此并不会【主动】获取 storage 的数据 </p><p>安装 </p><p>npm install pinia</p><p>在 main.ts 中引入 </p><p>import { createPinia } from 'pinia'</p><p>// ...</p><p>createApp(A6).use(antdv).use(router).use(createPinia()).mount('#app')</p><p>定义Store </p><p>再新建 store 目录来管理共享数据,下面是 /src/store/UserInfo.ts </p><p>import axios from '../api/request'</p><p>import { defineStore } from "pinia"</p><p>import { UserInfoDto } from '../model/Model8080'</p><p>export const useUserInfo = defineStore('userInfo', {</p><p> state: () => {</p><p> return { username: '', name: '', sex: '' }</p><p> },</p><p> actions: {</p><p> async get(username: string) {</p><p> const resp = await axios.get(`/api/info/${username}`)</p><p> Object.assign(this, resp.data.data)</p><p> },</p><p> async update(dto: UserInfoDto) {</p><p> await axios.post('/api/info', dto)</p><p> Object.assign(this, dto)</p><p> }</p><p> }</p><p>})</p><p> 定义了 useUserInfo 函数,用来获取共享数据,它可能用于多个组件 </p><p> 命名习惯上,函数变量以 use 打头 state 定义数据格式 actions 定义操作数据的方法 </p><p> get 方法用来获取用户信息 update 方法用来修改用户信息 由于 useRequest 必须放在 setup 函数内,这里简化起见,直接使用了 axios </p><p>获取用户信息 </p><p><template></p><p> <div class="a6main"></p><p> <a-layout></p><p> <a-layout-header></p><p> <span>{{serverUsername}} 【{{userInfo.name}} - {{userInfo.sex}}】</span></p><p> </a-layout-header></p><p> <a-layout></p><p> <!-- ... --></p><p> </a-layout></p><p> </a-layout></p><p> </div></p><p></template></p><p><script setup lang="ts"></p><p>import { onMounted } from 'vue';</p><p>import AIcon from '../components/AIcon3' // jsx icon 组件</p><p>import { serverMenus, serverUsername } from '../router/a6router'</p><p>import { useUserInfo } from '../store/UserInfo'</p><p>const userInfo = useUserInfo()</p><p>onMounted(()=>{</p><p> userInfo.get(serverUsername.value)</p><p>})</p><p></script></p><p>修改用户信息 </p><p><template></p><p> <div class="a6p1"></p><p> <h3>修改用户信息</h3></p><p> <hr></p><p> <a-form></p><p> <a-form-item label="用户名"></p><p> <a-input readonly v-model:value="dto.username"></a-input></p><p> </a-form-item></p><p> <a-form-item label="姓名" v-bind="validateInfos.name"></p><p> <a-input v-model:value="dto.name"></a-input></p><p> </a-form-item></p><p> <a-form-item label="性别"></p><p> <a-radio-group v-model:value="dto.sex"></p><p> <a-radio-button value="男">男</a-radio-button></p><p> <a-radio-button value="女">女</a-radio-button></p><p> </a-radio-group></p><p> </a-form-item></p><p> </a-form></p><p> <a-button type="primary" @click="onClick">确定</a-button></p><p> </div></p><p></template></p><p><script setup lang="ts"></p><p>import { Form } from 'ant-design-vue'</p><p>import { onMounted, ref } from 'vue'</p><p>import { UserInfoDto } from '../model/Model8080'</p><p>import { useUserInfo } from '../store/UserInfo';</p><p>const dto = ref<UserInfoDto>({ username: '', name: '', sex: '' })</p><p>const userInfo = useUserInfo()</p><p>onMounted(()=>{</p><p> Object.assign(dto.value, userInfo)</p><p>})</p><p>const rules = ref({</p><p> name: [</p><p> {required: true, message:'姓名必填'}</p><p> ]</p><p>})</p><p>const { validateInfos, validate } = Form.useForm(dto, rules)</p><p>async function onClick() {</p><p> try {</p><p> await validate()</p><p> await userInfo.update(dto.value)</p><p> } catch (e) {</p><p> console.error(e)</p><p> }</p><p>}</p><p></script></p><p>不能直接把 userInfo 绑定到表单,需要 dto 中转一下userInfo.update 和 useInfo.get 返回的都是 Promise 对象,可以配合 await 一起用</p><p>文章链接</p><div class="ly_isview_code_1"><div class="ly_isview_codea" data-id="713695165"><span><a href="https://www.kuazhi.com/zb_system/login.php" target="_blank">评论可见</a>,请评论后查看内容,谢谢!!!评论后请刷新页面。</span></div></div> </div> <div class="copyright"><blockquote>本文由 用户 于 2024-04-29 发布在 夸智网,如有疑问,请联系我们。<br>本文链接:<a href="https://www.kuazhi.com/post/713695165.html">https://www.kuazhi.com/post/713695165.html</a></blockquote></div> <div class="single-share"> <div class="post-share"> <a title="分享"><i class="jzicon-jzfenxiang"></i></a> <div class="share-icons share-sns" data-title="前端学习笔记(Vue、TypeScript、JavaScript)" data-url="https://www.kuazhi.com/post/713695165.html"> <span class="share-icon share-wechat cl" data-type="wechat" title="分享到微信"><i class="jzicon-weixin"></i><span id="wechat-qrcode"></span></span> <span class="share-icon share-sina-weibo cl" data-type="weibo" title="分享到微博"><i class="jzicon-weibo"></i></span><span class="share-icon share-qq cl" data-type="qq" title="分享到QQ好友"><i class="jzicon-qq"></i></span> </div> </div> <div class="post-like"> <a href="javascript:;" onclick="Jz52_tsqa_prise('713695165')" class="Jz52_tsqa_prise_id-713695165 dotGood Jz52_tsqa_prise badge" title="好文!一定要点赞!" data-badge="0"><i class="jzicon-jzzan-h"></i><em class="emz">0</em><em>赞</em></a> </div></div> </div> <div class="nextpro www_kuazhi_com"> <div class="prev"> <article class="post-overlay"> <div class="background-img" style="background-image:url(https://www.kuazhi.com/zb_users/theme/Jz52_tsqa/style/images/prevnoimg.jpg)"> </div> <div class="post_text"> <span><i class="jzicon-angle-left"></i>上一篇</span> <h3 class="post__title typescale-1 nav-prev">测试工具 python 2024爬虫 用selenium 爬取 51job(前程无忧)代码</h3> </div> <a href="https://www.kuazhi.com/post/713695158.html" class="link-overlay"></a> </article> </div> <div class="next"> <article class="post-overlay"> <div class="background-img" style="background-image:url(https://www.kuazhi.com/zb_users/theme/Jz52_tsqa/style/images/nextnoimg.jpg)"> </div> <div class="post_text"> <span>下一篇<i class="jzicon-angle-right"></i></span> <h3 class="post__title typescale-1 nav-next">javascript 前端 开发语言 typescript Eslint:Parsing error: The keyword ‘interface‘ is reserved</h3> </div> <a href="https://www.kuazhi.com/post/713695172.html" class="link-overlay"></a> </article> </div> </div> <div class="related-list www_kuazhi_com"> <h3><i class="jzicon-jztuwen"></i> 相关文章</h3> <ul> <li><a href="https://www.kuazhi.com/post/714476148.html" title="数据库 web安全 安全 网络安全 漏洞复现之Redis未授权访问"><p>数据库 web安全 安全 网络安全 漏洞复现之Redis未授权访问</p></a> </li> <li><a href="https://www.kuazhi.com/post/714476141.html" title="有了这个神器,快速告别垃圾短信邮件"><p>有了这个神器,快速告别垃圾短信邮件</p></a> </li> <li><a href="https://www.kuazhi.com/post/714231568.html" title="git redis.clients.jedis.exceptions.JedisDataException: ERR Client sent AUTH, but no password is set已解决"><p>git redis.clients.jedis.exceptions.JedisDataException: ERR Client sent AUTH, but no password is set已解决</p></a> </li> <li><a href="https://www.kuazhi.com/post/714476134.html" title="Python基础变量类型——List浅析"><p>Python基础变量类型——List浅析</p></a> </li> <li><a href="https://www.kuazhi.com/post/714181623.html" title="数据库 微服务 【深入学习Redis丨第一篇】Redis服务器部署详解"><p>数据库 微服务 【深入学习Redis丨第一篇】Redis服务器部署详解</p></a> </li> <li><a href="https://www.kuazhi.com/post/714476127.html" title="Python基础数据类型——tuple浅析"><p>Python基础数据类型——tuple浅析</p></a> </li> <li><a href="https://www.kuazhi.com/post/714227557.html" title="前沿技术 高效数据处理的前沿:【C++】、【Redis】、【人工智能】与【大数据】的深度整合"><p>前沿技术 高效数据处理的前沿:【C++】、【Redis】、【人工智能】与【大数据】的深度整合</p></a> </li> <li><a href="https://www.kuazhi.com/post/714476120.html" title="一篇文章教会你使用HTML5 SVG 标签"><p>一篇文章教会你使用HTML5 SVG 标签</p></a> </li> </ul> </div> <div class="comments www_kuazhi_com"> <div id="comments" class="comments-area clearfix"> <div class="comment-list"><!--评论框--><div class="jz-comment" id="divCommentPost"><h4 class="comments-title"> <span><i class="jzicon-jzqipaoc"></i>发表评论</span><a rel="nofollow" id="cancel-reply" href="#divCommentPost" style="display:none;float:right;"><small>取消回复</small></a></h4> <form id="frmSumbit" target="_self" method="post" action="https://www.kuazhi.com/zb_system/cmd.php?act=cmt&postid=713695165&key=4aa5fbaa50827afa4d1a29aae8f3a30a" ><input type="hidden" name="inpId" id="inpId" value="713695165"><input type="hidden" name="inpRevID" id="inpRevID" value="0"><div class="jz-comment-box jz-comment-ul3"> <input type="text" name="inpName" id="inpName" class="text" value="" size="28" tabindex="1" placeholder="名称(*)"> </div> <div class="jz-comment-box jz-comment-ul3 jz-comment-ul3-2"> <input type="text" name="inpEmail" id="inpEmail" class="text" value="" size="28" tabindex="2" placeholder="邮箱"> </div> <div class="jz-comment-box jz-comment-ul3"> <input type="text" name="inpHomePage" id="inpHomePage" class="text" value="" size="28" tabindex="3" placeholder="网站"> </div><div class="jz-comment-box jz-comment-textarea"> <textarea name="txaArticle" id="txaArticle" class="text" cols="50" rows="4" tabindex="5" placeholder="欢迎在这里交流评论,但是垃圾评论不受欢迎!"></textarea> </div><input name="sumbit" type="submit" tabindex="6" value="发表评论" onclick="return zbp.comment.post()" class="jz-commbut"> </form></div><label id="AjaxCommentBegin"></label><!--评论输出--><!--评论翻页条输出--><div class="pagelist page-comment"> </div><label id="AjaxCommentEnd"></label></div> </div></div> </div> </div> <aside id="sticky-wrapper"><div> <span class="ifread"><a href="javascript:;" onclick="Jz52_tsqa_prise('713695165')" class="Jz52_tsqa_prise_id-713695165 dotGood Jz52_tsqa_prise badge" title="好文!一定要点赞!" data-badge="0"><i class="jzicon-jzzan-h"></i><em class="emz">0</em><em>赞</em></a></span><span class="ifread"><a title="回复" href="#divCommentPost" class="badge" data-badge="0"><i class="jzicon-message-3-fill"></i></a></span><span class="ifread"><a title="热度" href="#" class="badge" data-badge="1"><i class="jzicon-fire-fill"></i></a></span><span class="ifread Cshare"><a title="分享" href="javascript:;" class=""><i class="jzicon-share-forward-fill"></i></a> <em class="share-sns" data-title="前端学习笔记(Vue、TypeScript、JavaScript)" data-url="https://www.kuazhi.com/post/713695165.html"> <span class="cl" data-type="wechatl" title="分享到微信"><a title="分享到微信" href="#" class="bds_weixin" ><i class="jzicon-weixin"></i>微信<span id="wechat-qrcodel"></span></a></span><span class="cl" data-type="weibo" title="分享到微博"><a title="分享到新浪微博" href="#" class="bds_tsina" ><i class="jzicon-weibo"></i>新浪微博</a></span><span class="cl" data-type="qzone" title="分享到QQ空间"><a title="分享到QQ空间" href="#" class="bds_qzone" ><i class="jzicon-qzone"></i>QQ空间</a></span><span class="cl" data-type="qq" title="分享到QQ好友"><a title="分享到QQ好友" href="#" class="bds_qq" ><i class="jzicon-qq"></i>QQ</a></span> </em></span><span style="margin-top: 30px;"><a href="javascript:;" title="沉浸阅读" class="goread"><i class="jzicon-book-read-fill"></i></a></span><span class="ifread"><a title="智能问答" href="http://ai.kuazhi.com/ai_chat" class=""><i class=" jzicon-jzqipaoa "></i></a></span></div></aside> <aside id="sidebar-right"><div class="widget ifread" id="side-new-userarticle"><h3 class="function_t">TA的新帖</h3><ul><li class="widget-list-item"><div class="widget-post-list-item"><div class="list-body"><a class="list-title" href="https://www.kuazhi.com/post/714335392.html" target="_blank" title="100DaysOfAI Challenge">100DaysOfAI Challenge</a></div><div class="list-footer"><span>2024-06-13</span></div></div><div class="clear"></div></li><li class="widget-list-item"><div class="widget-post-list-item"><div class="list-body"><a class="list-title" href="https://www.kuazhi.com/post/713827549.html" target="_blank" title="行程大数据不准(行程大数据不准确)">行程大数据不准(行程大数据不准确)</a></div><div class="list-footer"><span>2024-06-13</span></div></div><div class="clear"></div></li><li class="widget-list-item"><div class="widget-post-list-item"><div class="list-body"><a class="list-title" href="https://www.kuazhi.com/post/712812457.html" target="_blank" title="人工智能展示方式">人工智能展示方式</a></div><div class="list-footer"><span>2024-06-13</span></div></div><div class="clear"></div></li><li class="widget-list-item"><div class="widget-post-list-item"><div class="list-body"><a class="list-title" href="https://www.kuazhi.com/post/713821704.html" target="_blank" title="游戏引擎 使用Unity按钮控制动画播放">游戏引擎 使用Unity按钮控制动画播放</a></div><div class="list-footer"><span>2024-06-13</span></div></div><div class="clear"></div></li><li class="widget-list-item"><div class="widget-post-list-item"><div class="list-body"><a class="list-title" href="https://www.kuazhi.com/post/345953.html" target="_blank" title="解剖SQLSERVER 第十一篇 对SQLSERVER的多个版本进行自动化测试(译)">解剖SQLSERVER 第十一篇 对SQLSERVER的多个版本进行自动化测试(译)</a></div><div class="list-footer"><span>2024-06-13</span></div></div><div class="clear"></div></li><li class="widget-list-item"><div class="widget-post-list-item"><div class="list-body"><a class="list-title" href="https://www.kuazhi.com/post/714144677.html" target="_blank" title="运维 elasticsearch Kibana启动报错:Kibana server is not ready yet">运维 elasticsearch Kibana启动报错:Kibana server is not ready yet</a></div><div class="list-footer"><span>2024-06-13</span></div></div><div class="clear"></div></li></ul></div><div id="directory"></div><div class="widget ifread www_kuazhi_com" id="side-hot-view-item"><h3 class="function_t">热门文章</h3><ul><li class="widget-list-item"><div class="widget-post-list-item"><div class="list-body"> <a class="list-title" href="https://www.kuazhi.com/post/714196288.html" target="_blank" title="EducUp Studio">EducUp Studio</a> </div><div class="list-footer"> <span>2024-06-13</span> </div></div></li><li class="widget-list-item"><div class="widget-post-list-item"><div class="list-body"> <a class="list-title" href="https://www.kuazhi.com/post/714334468.html" target="_blank" title="Watermark Remover by Magic Studio">Watermark Remover by Magic Studio</a> </div><div class="list-footer"> <span>2024-06-13</span> </div></div></li><li class="widget-list-item"><div class="widget-post-list-item"><div class="list-body"> <a class="list-title" href="https://www.kuazhi.com/post/712807523.html" target="_blank" title="Noty.ai">Noty.ai</a> </div><div class="list-footer"> <span>2024-06-13</span> </div></div></li><li class="widget-list-item"><div class="widget-post-list-item"><div class="list-body"> <a class="list-title" href="https://www.kuazhi.com/post/714335203.html" target="_blank" title="Spiritme AI ScriptWriter">Spiritme AI ScriptWriter</a> </div><div class="list-footer"> <span>2024-06-13</span> </div></div></li><li class="widget-list-item"><div class="widget-post-list-item"><div class="list-body"> <a class="list-title" href="https://www.kuazhi.com/post/714335014.html" target="_blank" title="EarnBetter">EarnBetter</a> </div><div class="list-footer"> <span>2024-06-13</span> </div></div></li><li class="widget-list-item"><div class="widget-post-list-item"><div class="list-body"> <a class="list-title" href="https://www.kuazhi.com/post/712807921.html" target="_blank" title="Axiom">Axiom</a> </div><div class="list-footer"> <span>2024-06-13</span> </div></div></li><li class="widget-list-item"><div class="widget-post-list-item"><div class="list-body"> <a class="list-title" href="https://www.kuazhi.com/post/712807299.html" target="_blank" title="Fy! Studio">Fy! Studio</a> </div><div class="list-footer"> <span>2024-06-13</span> </div></div></li><li class="widget-list-item"><div class="widget-post-list-item"><div class="list-body"> <a class="list-title" href="https://www.kuazhi.com/post/712808738.html" target="_blank" title="OpExams">OpExams</a> </div><div class="list-footer"> <span>2024-06-13</span> </div></div></li><li class="widget-list-item"><div class="widget-post-list-item"><div class="list-body"> <a class="list-title" href="https://www.kuazhi.com/post/712807868.html" target="_blank" title="Xembly">Xembly</a> </div><div class="list-footer"> <span>2024-06-13</span> </div></div></li><li class="widget-list-item"><div class="widget-post-list-item"><div class="list-body"> <a class="list-title" href="https://www.kuazhi.com/post/714334139.html" target="_blank" title="Walles.ai">Walles.ai</a> </div><div class="list-footer"> <span>2024-06-13</span> </div></div></li><li class="widget-list-item"><div class="widget-post-list-item"><div class="list-body"> <a class="list-title" href="https://www.kuazhi.com/post/712808310.html" target="_blank" title="AI PICASSO">AI PICASSO</a> </div><div class="list-footer"> <span>2024-06-13</span> </div></div></li><li class="widget-list-item"><div class="widget-post-list-item"><div class="list-body"> <a class="list-title" href="https://www.kuazhi.com/post/712808236.html" target="_blank" title="arXiv Xplorer">arXiv Xplorer</a> </div><div class="list-footer"> <span>2024-06-13</span> </div></div></li><li class="widget-list-item"><div class="widget-post-list-item"><div class="list-body"> <a class="list-title" href="https://www.kuazhi.com/post/712808054.html" target="_blank" title="Broadn">Broadn</a> </div><div class="list-footer"> <span>2024-06-13</span> </div></div></li><li class="widget-list-item"><div class="widget-post-list-item"><div class="list-body"> <a class="list-title" href="https://www.kuazhi.com/post/712808048.html" target="_blank" title="VWO">VWO</a> </div><div class="list-footer"> <span>2024-06-13</span> </div></div></li><li class="widget-list-item"><div class="widget-post-list-item"><div class="list-body"> <a class="list-title" href="https://www.kuazhi.com/post/712807944.html" target="_blank" title="Writer">Writer</a> </div><div class="list-footer"> <span>2024-06-13</span> </div></div></li><li class="widget-list-item"><div class="widget-post-list-item"><div class="list-body"> <a class="list-title" href="https://www.kuazhi.com/post/714335336.html" target="_blank" title="Design Buddy">Design Buddy</a> </div><div class="list-footer"> <span>2024-06-13</span> </div></div></li><li class="widget-list-item"><div class="widget-post-list-item"><div class="list-body"> <a class="list-title" href="https://www.kuazhi.com/post/712807261.html" target="_blank" title="VidIq">VidIq</a> </div><div class="list-footer"> <span>2024-06-13</span> </div></div></li><li class="widget-list-item"><div class="widget-post-list-item"><div class="list-body"> <a class="list-title" href="https://www.kuazhi.com/post/712807575.html" target="_blank" title="CrowdView">CrowdView</a> </div><div class="list-footer"> <span>2024-06-13</span> </div></div></li><li class="widget-list-item"><div class="widget-post-list-item"><div class="list-body"> <a class="list-title" href="https://www.kuazhi.com/post/712808918.html" target="_blank" title="Help.AI">Help.AI</a> </div><div class="list-footer"> <span>2024-06-13</span> </div></div></li><li class="widget-list-item"><div class="widget-post-list-item"><div class="list-body"> <a class="list-title" href="https://www.kuazhi.com/post/712808913.html" target="_blank" title="Rezi Hiring Tools">Rezi Hiring Tools</a> </div><div class="list-footer"> <span>2024-06-13</span> </div></div></li><li class="widget-list-item"><div class="widget-post-list-item"><div class="list-body"> <a class="list-title" href="https://www.kuazhi.com/post/712807675.html" target="_blank" title="Nuclia">Nuclia</a> </div><div class="list-footer"> <span>2024-06-13</span> </div></div></li><li class="widget-list-item"><div class="widget-post-list-item"><div class="list-body"> <a class="list-title" href="https://www.kuazhi.com/post/712808643.html" target="_blank" title="getscore">getscore</a> </div><div class="list-footer"> <span>2024-06-13</span> </div></div></li><li class="widget-list-item"><div class="widget-post-list-item"><div class="list-body"> <a class="list-title" href="https://www.kuazhi.com/post/714196316.html" target="_blank" title="Fliki">Fliki</a> </div><div class="list-footer"> <span>2024-06-13</span> </div></div></li><li class="widget-list-item"><div class="widget-post-list-item"><div class="list-body"> <a class="list-title" href="https://www.kuazhi.com/post/714334391.html" target="_blank" title="Aispect">Aispect</a> </div><div class="list-footer"> <span>2024-06-13</span> </div></div></li><li class="widget-list-item"><div class="widget-post-list-item"><div class="list-body"> <a class="list-title" href="https://www.kuazhi.com/post/712807590.html" target="_blank" title="Mental Models AI">Mental Models AI</a> </div><div class="list-footer"> <span>2024-06-13</span> </div></div></li><li class="widget-list-item"><div class="widget-post-list-item"><div class="list-body"> <a class="list-title" href="https://www.kuazhi.com/post/714194391.html" target="_blank" title="ExtendMusic.AI">ExtendMusic.AI</a> </div><div class="list-footer"> <span>2024-06-13</span> </div></div></li><li class="widget-list-item"><div class="widget-post-list-item"><div class="list-body"> <a class="list-title" href="https://www.kuazhi.com/post/714334769.html" target="_blank" title="80days.me">80days.me</a> </div><div class="list-footer"> <span>2024-06-13</span> </div></div></li><li class="widget-list-item"><div class="widget-post-list-item"><div class="list-body"> <a class="list-title" href="https://www.kuazhi.com/post/712808213.html" target="_blank" title="Vega AI">Vega AI</a> </div><div class="list-footer"> <span>2024-06-13</span> </div></div></li><li class="widget-list-item"><div class="widget-post-list-item"><div class="list-body"> <a class="list-title" href="https://www.kuazhi.com/post/712808432.html" target="_blank" title="ExplainThis AI">ExplainThis AI</a> </div><div class="list-footer"> <span>2024-06-13</span> </div></div></li><li class="widget-list-item"><div class="widget-post-list-item"><div class="list-body"> <a class="list-title" href="https://www.kuazhi.com/post/712808013.html" target="_blank" title="Open Voice OS">Open Voice OS</a> </div><div class="list-footer"> <span>2024-06-13</span> </div></div></li></ul></div><div class="widget ifread www_kuazhi_com" id="side-hot-cmt-item"><h3 class="function_t">热评文章</h3><ul><li class="widget-list-item"><div class="widget-post-list-item"><div class="list-body"> <a class="list-title" href="https://www.kuazhi.com/post/712831467.html" target="_blank" title="柚子快报赚钱软件下载778899分享:UE4逆向篇-1">柚子快报赚钱软件下载778899分享:UE4逆向篇-1</a> </div><div class="list-footer"> <span>2024-06-13</span> </div></div></li><li class="widget-list-item"><div class="widget-post-list-item"><div class="list-body"> <a class="list-title" href="https://www.kuazhi.com/post/108.html" target="_blank" title="Mybatis-Plus的QueryWrapper获取自定义SQL 简化自定义XML复杂情况${ew.customSqlSegment} ">Mybatis-Plus的QueryWrapper获取自定义SQL 简化自定义XML复杂情况${ew.customSqlSegment} </a> </div><div class="list-footer"> <span>2024-06-13</span> </div></div></li><li class="widget-list-item"><div class="widget-post-list-item"><div class="list-body"> <a class="list-title" href="https://www.kuazhi.com/post/305594.html" target="_blank" title="npm 前端 Node.js16.15.1的一个报错及解决方案">npm 前端 Node.js16.15.1的一个报错及解决方案</a> </div><div class="list-footer"> <span>2024-06-13</span> </div></div></li><li class="widget-list-item"><div class="widget-post-list-item"><div class="list-body"> <a class="list-title" href="https://www.kuazhi.com/post/418121.html" target="_blank" title="spring java spring boot swagger在项目中的使用,快速上手。">spring java spring boot swagger在项目中的使用,快速上手。</a> </div><div class="list-footer"> <span>2024-06-13</span> </div></div></li><li class="widget-list-item"><div class="widget-post-list-item"><div class="list-body"> <a class="list-title" href="https://www.kuazhi.com/post/714003606.html" target="_blank" title="android 开发语言 visual studio code 前端 【PHP【实战项目】系统性教学】——使用最精简的代码完成用户的登录与退出">android 开发语言 visual studio code 前端 【PHP【实战项目】系统性教学】——使用最精简的代码完成用户的登录与退出</a> </div><div class="list-footer"> <span>2024-06-13</span> </div></div></li><li class="widget-list-item"><div class="widget-post-list-item"><div class="list-body"> <a class="list-title" href="https://www.kuazhi.com/post/712801881.html" target="_blank" title="容器 【安全】使用docker安装Nessus">容器 【安全】使用docker安装Nessus</a> </div><div class="list-footer"> <span>2024-06-13</span> </div></div></li><li class="widget-list-item"><div class="widget-post-list-item"><div class="list-body"> <a class="list-title" href="https://www.kuazhi.com/post/470420.html" target="_blank" title="用ChatGPT的同学">用ChatGPT的同学</a> </div><div class="list-footer"> <span>2024-06-13</span> </div></div></li><li class="widget-list-item"><div class="widget-post-list-item"><div class="list-body"> <a class="list-title" href="https://www.kuazhi.com/post/409694.html" target="_blank" title="windows 华硕主板通过奥创与海盗船内存条神光同步">windows 华硕主板通过奥创与海盗船内存条神光同步</a> </div><div class="list-footer"> <span>2024-06-13</span> </div></div></li><li class="widget-list-item"><div class="widget-post-list-item"><div class="list-body"> <a class="list-title" href="https://www.kuazhi.com/post/394271.html" target="_blank" title="前端 html5 css3 侧边导航栏(抽屉式设计)界面 (html + css)">前端 html5 css3 侧边导航栏(抽屉式设计)界面 (html + css)</a> </div><div class="list-footer"> <span>2024-06-13</span> </div></div></li><li class="widget-list-item"><div class="widget-post-list-item"><div class="list-body"> <a class="list-title" href="https://www.kuazhi.com/post/306839.html" target="_blank" title="java tomcat eclipse 【系统分析与设计】高校学生社团管理系统">java tomcat eclipse 【系统分析与设计】高校学生社团管理系统</a> </div><div class="list-footer"> <span>2024-06-13</span> </div></div></li><li class="widget-list-item"><div class="widget-post-list-item"><div class="list-body"> <a class="list-title" href="https://www.kuazhi.com/post/330537.html" target="_blank" title="[LeetCode] 847. Shortest Path Visiting All Nodes 访问所有结点的最短路径">[LeetCode] 847. Shortest Path Visiting All Nodes 访问所有结点的最短路径</a> </div><div class="list-footer"> <span>2024-06-13</span> </div></div></li><li class="widget-list-item"><div class="widget-post-list-item"><div class="list-body"> <a class="list-title" href="https://www.kuazhi.com/post/66030.html" target="_blank" title="visualstudio c语言 c++ Visual Studio 2022最新版安装教程(数千字图文详解),一步步教会你如何安装并运行VS2022(+背景图设置)">visualstudio c语言 c++ Visual Studio 2022最新版安装教程(数千字图文详解),一步步教会你如何安装并运行VS2022(+背景图设置)</a> </div><div class="list-footer"> <span>2024-06-13</span> </div></div></li><li class="widget-list-item"><div class="widget-post-list-item"><div class="list-body"> <a class="list-title" href="https://www.kuazhi.com/post/304546.html" target="_blank" title="asp.net office-interop 只是在Microsoft.Office.Interop.Word中可访问的第一页">asp.net office-interop 只是在Microsoft.Office.Interop.Word中可访问的第一页</a> </div><div class="list-footer"> <span>2024-06-13</span> </div></div></li><li class="widget-list-item"><div class="widget-post-list-item"><div class="list-body"> <a class="list-title" href="https://www.kuazhi.com/post/13575.html" target="_blank" title="Mybatis源码分析之SqlSessionFactory,SqlSession和连接池">Mybatis源码分析之SqlSessionFactory,SqlSession和连接池</a> </div><div class="list-footer"> <span>2024-06-13</span> </div></div></li><li class="widget-list-item"><div class="widget-post-list-item"><div class="list-body"> <a class="list-title" href="https://www.kuazhi.com/post/356824.html" target="_blank" title="chrome selenium入门超详细教程——网页自动化操作">chrome selenium入门超详细教程——网页自动化操作</a> </div><div class="list-footer"> <span>2024-06-13</span> </div></div></li><li class="widget-list-item"><div class="widget-post-list-item"><div class="list-body"> <a class="list-title" href="https://www.kuazhi.com/post/714476106.html" target="_blank" title="数据库 缓存 【Redis篇】简述Redis | 详解Redis命令">数据库 缓存 【Redis篇】简述Redis | 详解Redis命令</a> </div><div class="list-footer"> <span>2024-06-13</span> </div></div></li><li class="widget-list-item"><div class="widget-post-list-item"><div class="list-body"> <a class="list-title" href="https://www.kuazhi.com/post/714476099.html" target="_blank" title="区块链 为什么市场称SoBit 是铭文跨链赛道真正的龙头?">区块链 为什么市场称SoBit 是铭文跨链赛道真正的龙头?</a> </div><div class="list-footer"> <span>2024-06-13</span> </div></div></li><li class="widget-list-item"><div class="widget-post-list-item"><div class="list-body"> <a class="list-title" href="https://www.kuazhi.com/post/714476092.html" target="_blank" title="区块链 ERC-6551 带来 NFT 与 DID 新范式">区块链 ERC-6551 带来 NFT 与 DID 新范式</a> </div><div class="list-footer"> <span>2024-06-13</span> </div></div></li><li class="widget-list-item"><div class="widget-post-list-item"><div class="list-body"> <a class="list-title" href="https://www.kuazhi.com/post/714476085.html" target="_blank" title="区块链 etf期权交易的关键要点和步骤策略分享">区块链 etf期权交易的关键要点和步骤策略分享</a> </div><div class="list-footer"> <span>2024-06-13</span> </div></div></li><li class="widget-list-item"><div class="widget-post-list-item"><div class="list-body"> <a class="list-title" href="https://www.kuazhi.com/post/714476078.html" target="_blank" title="基于区块链的物流管理系统设计与实现">基于区块链的物流管理系统设计与实现</a> </div><div class="list-footer"> <span>2024-06-13</span> </div></div></li><li class="widget-list-item"><div class="widget-post-list-item"><div class="list-body"> <a class="list-title" href="https://www.kuazhi.com/post/714476071.html" target="_blank" title="Hotcoin Research|玩赚WEB3:探索Apeiron:颠覆传统的区块链游戏,融合神话与现代玩法">Hotcoin Research|玩赚WEB3:探索Apeiron:颠覆传统的区块链游戏,融合神话与现代玩法</a> </div><div class="list-footer"> <span>2024-06-13</span> </div></div></li><li class="widget-list-item"><div class="widget-post-list-item"><div class="list-body"> <a class="list-title" href="https://www.kuazhi.com/post/714476064.html" target="_blank" title="对审计、风险和合规专业人士的区块链介绍">对审计、风险和合规专业人士的区块链介绍</a> </div><div class="list-footer"> <span>2024-06-13</span> </div></div></li><li class="widget-list-item"><div class="widget-post-list-item"><div class="list-body"> <a class="list-title" href="https://www.kuazhi.com/post/714476057.html" target="_blank" title="php 服务器 linux 搭建第一个区块链网络(Fisco-Bcos),启动并使用控制台">php 服务器 linux 搭建第一个区块链网络(Fisco-Bcos),启动并使用控制台</a> </div><div class="list-footer"> <span>2024-06-13</span> </div></div></li><li class="widget-list-item"><div class="widget-post-list-item"><div class="list-body"> <a class="list-title" href="https://www.kuazhi.com/post/714476050.html" target="_blank" title="探秘WeBASE-Front:区块链前端服务框架,简化开发流程">探秘WeBASE-Front:区块链前端服务框架,简化开发流程</a> </div><div class="list-footer"> <span>2024-06-13</span> </div></div></li><li class="widget-list-item"><div class="widget-post-list-item"><div class="list-body"> <a class="list-title" href="https://www.kuazhi.com/post/714476043.html" target="_blank" title="人工智能 web3 Let‘s Move Sui:解锁区块链高性能潜力,探索创新开发体验">人工智能 web3 Let‘s Move Sui:解锁区块链高性能潜力,探索创新开发体验</a> </div><div class="list-footer"> <span>2024-06-13</span> </div></div></li><li class="widget-list-item"><div class="widget-post-list-item"><div class="list-body"> <a class="list-title" href="https://www.kuazhi.com/post/714476036.html" target="_blank" title="开发语言 Java实现一个简单的区块链">开发语言 Java实现一个简单的区块链</a> </div><div class="list-footer"> <span>2024-06-13</span> </div></div></li><li class="widget-list-item"><div class="widget-post-list-item"><div class="list-body"> <a class="list-title" href="https://www.kuazhi.com/post/714476029.html" target="_blank" title="智能合约 信任链 分布式账本 去中心化 共识算法 同态加密 区块链技术简介 区块链具体是什么">智能合约 信任链 分布式账本 去中心化 共识算法 同态加密 区块链技术简介 区块链具体是什么</a> </div><div class="list-footer"> <span>2024-06-13</span> </div></div></li><li class="widget-list-item"><div class="widget-post-list-item"><div class="list-body"> <a class="list-title" href="https://www.kuazhi.com/post/714476022.html" target="_blank" title="双花攻击及其技术细节——哈尔滨工程大学计算机学院2024年区块链技术课程">双花攻击及其技术细节——哈尔滨工程大学计算机学院2024年区块链技术课程</a> </div><div class="list-footer"> <span>2024-06-13</span> </div></div></li><li class="widget-list-item"><div class="widget-post-list-item"><div class="list-body"> <a class="list-title" href="https://www.kuazhi.com/post/714476015.html" target="_blank" title="【区块链】深入解析Proof of Work (PoW): 区块链技术的核心驱动力">【区块链】深入解析Proof of Work (PoW): 区块链技术的核心驱动力</a> </div><div class="list-footer"> <span>2024-06-13</span> </div></div></li><li class="widget-list-item"><div class="widget-post-list-item"><div class="list-body"> <a class="list-title" href="https://www.kuazhi.com/post/714476008.html" target="_blank" title="Python自动化办公--Pandas玩转Excel【一】">Python自动化办公--Pandas玩转Excel【一】</a> </div><div class="list-footer"> <span>2024-06-13</span> </div></div></li></ul></div></aside> </div><script src="https://www.kuazhi.com/zb_users/theme/Jz52_tsqa/script/asid.js"></script> <div id="footer"> <p><p>夸智网——用心陪伴AI人工智能技术共同成长</p><p><a href="https://www.kuazhi.com/category-16.html" target="_blank">AI工具</a> <a href="https://www.kuazhi.com/category-17.html" target="_blank">AI教程</a> <a href="https://www.kuazhi.com/category-15.html" target="_blank">ChatGPT教程</a></p><a href="https://beian.miit.gov.cn/" target="_blank">浙ICP备15009899号-3</a><div style="display:none"><script charset="UTF-8" id="LA_COLLECT" src="//sdk.51.la/js-sdk-pro.min.js"></script><script>LA.init({id: "JsGU3vXOubP3rMz2",ck: "JsGU3vXOubP3rMz2"})</script></div><script charset="UTF-8" id="kuazhi_ai_click" src="https://kuazhi.com/kuazhi_ai_click.js"></script><div>本站部分信息来自互联网收集,仅供学习和交流,如有侵权、后门、不妥之处,请联系我们进行删除处理。</div></p> </div></div></div><a href="javascript:void(0);" class="to-top" id="to-top"><i class="jzicon-jzzhiding"></i><em>返回顶部</em></a><a class="jznight" href="javascript:switchNightMode()" target="_self"><i class="jzicon-yejian-b"></i><em>暗黑模式</em></a><script src="https://www.kuazhi.com/zb_users/theme/Jz52_tsqa/script/custom.js?v1.1.0"></script><script src="https://www.kuazhi.com/zb_users/theme/Jz52_tsqa/script/qrcode.min.js"></script><script src="https://www.kuazhi.com/zb_users/plugin/gbll_rollname/names.js"></script><script src="https://www.kuazhi.com/zb_users/plugin/gbll_rollname/roll.js"></script><script src="https://www.kuazhi.com/zb_users/theme/Jz52_tsqa/script/sticky-left.js"></script><script src="https://www.kuazhi.com/zb_users/theme/Jz52_tsqa/script/sidebar-right.js"></script><div id="ly_cache" data-id="713695165"></div></body></html><!--ly_cache 2024-06-13 22:58:21-->