前端快速工程化 前端 vs 后端
ES6
ECMAScript(ES) 是规范、 JavaScript 是 ES 的实现
ES6 的第一个版本 在 2015 年 6 月发布,正式名称是《ECMAScript 2015 标准》(简称 ES2015)
ES6 指是 5.1 版以后的 JavaScript 的下一代标准,涵盖了 ES2015、ES2016、ES2017 等等
let 推荐使用let
关键字替代 var
关键字声明变量,因为 var
存在诸多问题,比如:
跨域 1 2 3 4 5 6 { var a = 1 ; let b = 2 ; } console .log (a); console .log (b);
只会打印a,而b无法引用,所以a可以跨域,不安全
重复申明 1 2 3 4 5 6 7 8 var m = 1 var m = 2 let n = 3 let n = 4 console .log (m) console .log (n)
变量提升 1 2 3 4 5 6 console .log (x); var x = 10 ;console .log (y); let y = 20 ;
const
解构 数组解构 1 2 3 4 5 6 let arr = [1 , 2 , 3 ];let [x, y, z] = arr;console .log (x, y, z);
对象解构 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 const person = { name : "jack" , age : 21 , language : ['java' , 'js' , 'css' ] } const {name, age, language} = person;console .log (name);console .log (age);console .log (language);const {name : nn, age, language} = person;console .log (nn);console .log (age);console .log (language);
链判断 如果读取对象内部的某个属性,往往需要判断一下,属性的上层对象是否存在。
比如,读取message.body.user.firstName这个属性,安全的写法是写成下面这样。
1 2 3 4 5 6 7 8 9 10 let message = null ;const firstName = message.body .user .firstName || 'default' ;const firstName = (message && message.body && message.body .user && message.body .user .firstName ) || 'default' ; console .log (firstName)
这样的层层判断非常麻烦,因此 ES2020 引入了“链判断运算符”(optional chaining operator)?. ,简化上面的写法。
1 const firstName = message?.body ?.user ?.firstName || 'default' ;
参数默认值 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 function add (a, b ) { b = b || 1 ; return a + b; } console .log (add (10 ));function add2 (a, b = 1 ) { return a + b; } console .log (add2 (10 ));
箭头函数 类似于Java中的lambda表达式
1 2 3 4 5 6 7 8 let print = obj => console .log (obj);print (100 );
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 let sum = function (a, b ) { return a + b; } let sum2 = (a, b ) => a + b;console .log (sum2 (10 , 10 ));let sum3 = (a, b ) => { c = a + b; return c; }; console .log (sum3 (10 , 20 ));
模板字符串 1 2 3 4 5 6 let info = "你好,我的名字是:【" +name+"】,年龄是:【" +age+"】,邮箱是:【】" console .log (info);# 模板字符串的写法 let info = `你好,我的名字是:${name} ,年龄是:${person.age} ,邮箱是:${person.email} ` console .log (info);
Promise 代表 异步对象
,类似Java中的 CompletableFuture
promise 是现代 JavaScript 中异步编程的基础,是一个由异步函数返回的可以向我们指示当前操作所处的状态的对象。在 Promise 返回给调用者的时候,操作往往还没有完成,但 Promise 对象可以让我们操作最终完成时对其进行处理(无论成功还是失败)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 const fetchPromise = fetch ( "https://mdn.github.io/learning-area/javascript/apis/fetching-data/can-store/products.json" ); console .log ("开始发送请求..." )fetchPromise.then ((response ) => { console .log (`已收到响应:${response.status} ` ); response.json ().then (data => console .log (data)) }); fetchPromise.catch (error => console .log (error)); console .log ("已发送请求……" );
fetch api
fetch 是浏览器支持从远程获取数据的一个函数,这个函数返回的就是 Promise 对象
通过 fetch() API 得到一个 Response 对象
response.status : 读取响应状态码
response.json() :读取响应体json数据;(这也是个异步对象 )
Promise状态 Promise 有三种状态:
待定(pending) :初始状态,既没有被兑现,也没有被拒绝。这是调用 fetch() 返回 Promise 时的状态,此时请求还在进行中。
已兑现(fulfilled) :意味着操作成功完成。当 Promise 完成时,它的 then() 处理函数被调用。
已拒绝(rejected) :意味着操作失败。当一个 Promise 失败时,它的 catch() 处理函数被调用。
Promise对象 如何自定义Promise对象?
1 2 3 4 5 6 7 8 const promise = new Promise ((resolve, reject ) => {if () { resolve (value); } else { reject (error); } });
自定义Promise对象示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 let get = function (url, data ) { return new Promise ((resolve, reject ) => { $.ajax ({ url : url, type : "GET" , data : data, success (result ) { resolve (result); }, error (error ) { reject (error); } }); }) }
Async 函数 async function 声明创建一个绑定到给定名称的新异步函数。函数体内允许使用 await 关键字,这使得我们可以更简洁地编写基于 promise 的异步代码 ,并且避免了显式地配置 promise 链 的需要。
async 函数
是使用async关键字声明的函数
。async 函数是 AsyncFunction 构造函数的实例,并且其中允许使用 await 关键字。
async 和 await
关键字让我们可以用一种更简洁的方式写出基于 Promise 的异步行为,而无需刻意地链式调用 promise。
async 函数
返回的还是 Promise对象
。
1 2 3 4 async function myFunction ( ) { }
在异步函数中,你可以在调用一个返回 Promise 的函数之前使用 await 关键字。这使得代码在该点上等待,直到 Promise 被完成,这时 Promise 的响应被当作返回值,或者被拒绝的响应被作为错误抛出。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 async function fetchProducts ( ) { try { const response = await fetch ( "https://mdn.github.io/learning-area/javascript/apis/fetching-data/can-store/products.json" , ); if (!response.ok ) { throw new Error (`HTTP 请求错误:${response.status} ` ); } const json = await response.json (); console .log (json[0 ].name ); } catch (error) { console .error (`无法获取产品列表:${error} ` ); } } fetchProducts ();
模块化 实际项目中,不可能将所有的js代码写在某一个单独的文件中,在实际开发中需要模块化,
将 JavaScript 程序拆分为可按需导入的单独模块 的机制。Node.js 已经提供这个能力很长时间了,还有很多的 JavaScript 库和框架已经开始了模块的使用
下面演示一个模块化示例,主要包含三个文件:
index.html:主页面
main.js:主js文件,里面从其他js文件中引入需要的内容
user.js:申明一些变量或函数供第三方使用
index.html 1 2 3 4 5 6 7 8 9 10 11 12 <!DOCTYPE html > <html lang ="en" > <head > <meta charset ="UTF-8" > <title > ES6规范</title > //必须申明type为模块化 module <script src ="main.js" type ="module" > </script > </head > <body > <h1 > 这是ES6模块化测试使用</h1 > </body > </html >
user.js 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 const user = { username : "张三" , age : 18 } const isAdult = (age )=>{ if (age > 18 ){ console .log ("成年人" ) }else { console .log ("未成年" ) } } export {user,isAdult}
main.js 1 2 3 4 5 import {user,isAdult} from "./lib/user.js" ;console .log (`姓名:${user.username} ,年龄:${user.age} ` );isAdult (user.age );
npm npm 是 nodejs 中进行 包管理 的工具
以前引入第三方js,需要手动下载导入,现在只需用npm进行依赖的安装和管理
环境准备
安装nodejs nodejs下载地址
配置npm
```shell npm config set registry https://registry.npmmirror.com #设置国内阿里云镜像源 npm config get registry #查看镜像源1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 ### 常用命令 - npm init: 项目初始化 - npm init -y:默认一路yes,不用挨个输入信息 - npm install 包名:安装js包到项目中(仅当前项目有效)。指定 **包名**,或者 **包名@版本号** - npm install -g: 全局安装,所有项目都能用 - 可以去 [npm仓库](https://www.npmjs.com/) 搜索第三方库 - npm update 包名:升级包到最新版本 - npm uninstall 包名:卸载包 - npm run:项目运行 ### 使用示例 创建一个文件目录`02-npm`,在其下执行`npm init -y`会初始化一个项目  目录中会多出一个`package.json`文件 - **package.json** 文件一般都在每个项目的根目录下面,定义了这个项目所需要的各种模块,以及项目的配置信息,包括名称、版本、许可证、依赖模块等元数据。格式是严格的JSON格式 - 当你执行 npm install 的时候,node 会先从 package.json 文件中读取所有 dependencies 信息,然后根据 dependencies 中的信息与 node_modules 中的模块进行对比,没有的直接下载,已有的检查更新。另外,package.json 文件只记录你通过 npm install 方式安装的模块信息,而这些模块所依赖的其他子模块的信息不会记录  安装所需依赖,假定我们需要使用jquery,则执行`npm install jquery`   运行,`package.json`文件中的`scripts`中的命令我们可以通过`npm run xxx`来运行  我们分享或者从第三下载项目时,由于项目第三方依赖包很大,所以一般会把`node_modules`排除在外,那么我们在本地运行这些项目又需要第三方依赖怎么办?直接在项目中运行`npm install`其会自动根据`package.json`中依赖的第三方库及版本进行下载 ### 总结:npm使用流程 <img src="%E5%89%8D%E7%AB%AF%E5%BF%AB%E9%80%9F%E5%B7%A5%E7%A8%8B%E5%8C%96/1712110492836-0f787e3a-4b9c-4236-8250-3912df233a20.webp" alt="image.png" style="zoom:50%;" /> ## Vite ### 概述 [官网](https://cn.vitejs.dev) <img src="%E5%89%8D%E7%AB%AF%E5%BF%AB%E9%80%9F%E5%B7%A5%E7%A8%8B%E5%8C%96/image-20240606144119659.png" alt="image-20240606144119659" style="zoom:50%;" /> - 快速创建前端项目脚手架 - 统一的工程化规范:目录结构、代码规范、git提交规范 等 - 自动化构建和部署:前端脚手架可以自动进行代码打包、压缩、合并、编译等常见的构建工作,可以通过集成自动化部署脚本,自动将代码部署到测试、生产环境等; ### 常用命令 #### 创建项目 ```shell npm create vite #根据向导选择技术栈
安装依赖 1 2 3 4 npm install #安装项目所有依赖 npm install axios #安装指定依赖到当前项目 npm install -g xxx # 全局安装
启动项目
项目打包 1 npm run build #构建后 生成 dist 文件夹
项目部署
前后分离方式:需要把 dist 文件夹内容部署到如 nginx 之类的服务器上
前后不分离方式:把 dist 文件夹内容复制到 SpringBoot 项目 resources
下面
使用示例 创建项目
安装依赖、启动项目
访问项目
项目打包
Vue3 Vue官方文档
我们可以利用Vite工具链快速创建一个Vue项目
组件化 组件系统是一个抽象的概念:
组件:小型、独立、可复用的单元
组合:通过组件之间的组合、包含关系构建出一个完整应用
几乎任意类型的应用界面都可以抽象为一个组件树
SFC Vue 的单文件组件 (即 .vue 文件,英文 Single-File Component,简称 *SFC ) 是一种特殊的文件格式,使我们能够将一个 Vue 组件的模板、逻辑与样式封装在单个文件中
1 2 3 4 5 6 7 8 9 10 11 <script setup> //编写脚本 </script> <template> //编写页面模板 </template> <style scoped> //编写样式 </style>
Vue运行原理
Vue基础语法 插值 页面展示的某个内容来自于某个变量,如何将变量的值显示在页面的正确位置,需要用到插值,直接在html中需要的位置{{变量名}}
即可引用变量值
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 <script setup> //定义基本数据变量 let name="张三"; let age=27; let scores={ "数学":129, "英语":136, "政治":89 } </script> <template> <table> <thead> <tr> <th>姓名</th> <th>年龄</th> <th>数学</th> <th>英语</th> <th>政治</th> </tr> </thead> <tbody> <tr> <td>{{name}}</td> <td>{{age}}</td> <td>{{scores.数学}}</td> <td>{{scores.英语}}</td> <td>{{scores.政治}}</td> </tr> </tbody> </table> </template> <style scoped> </style>
指令 vue指令官方文档
v-on指令 可以为元素绑定特定的事件,比如点击按钮出发某个函数等操作
1 2 3 4 5 6 7 8 9 10 11 12 13 <script setup> //顶一个对应的函数 function buy(){ alert("欢迎选购!!!"); } </script> <template> <button v-on:click="buy">点击购买</button> </template> <style scoped> </style>
条件判断v-if 比如根据某个条件,决定页面显示的内容
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 <script setup> //定义变量 let person={ name:"章三", age:27 } </script> <template> <div> <!--根据age大小显示不同内容--> <span style="color: red" v-if="person.age>18">成年人</span> <span style="color: blue" v-else>未成年人</span> </div> </template> <style scoped> </style>
循环指令v-for 比如循环显示某个列表的内容
1 2 3 4 5 6 7 8 9 10 11 12 <script setup> //定义变量 let persons=["章三","里斯","王五","赵六"] </script> <template> <div> <li v-for="person in persons">{{person}}</li> </div> </template> <style scoped> </style>
属性绑定v-bind 动态地绑定一个或者多个属性
1 2 3 4 5 6 7 8 9 10 11 12 13 <script setup> //定义变量 let url = "https://www.baidu.com" </script> <template> <div> <!-- 将a标签href属性与变量url动态绑定 --> <a v-bind:href="url">跳转</a> </div> </template> <style scoped> </style>
注意:此时如果我们想修改变量的值,属性值并不会跟着修改
响应式 前面个通过指令绑定,数据地动态变化无法反馈到页面元素中
Vue通过ref()
和reactive()
包装数据,将会生成一个数据的代理对象。vue内部的 基于依赖追踪的响应式系统 就会追踪 感知数据变化 ,并触发页面 的重新渲染
响应式-ref() 使用方式
使用 ref() 包装原始类型、对象类型数据 ,生成 代理对象
任何方法、js代码中 ,使用 代理对象.value
的形式读取和修改值
页面组件中 ,直接使用 代理对象
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 <script setup> //定义变量 import {ref} from "vue"; //ref生成代理对象 let num = ref(0); let incr = function (){ num.value++; } </script> <template> <div> <span><button v-on:click="incr">增加</button></span> <!-- 动态显示num的值 --> <h3>{{num}}</h3> </div> </template> <style scoped> </style>
响应式-reactive() 使用方式
使用 reactive() 包装对象类型数据 ,生成 代理对象
任何方法、js代码中 ,使用 代理对象.属性
的形式读取和修改值
页面组件中 ,直接使用 代理对象.属性
ref() vs reactive() 两者差异比较:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 <script setup> //定义变量 import {reactive, ref} from "vue"; //ref生成代理对象 let car1 = ref({ brand:"比亚迪", price:10000 }) let car2 = reactive({ brand:"小米", price:10000 }) //ref包装对象,要使用到值,需要: 对象.value.属性 let incr1 = function (){ car1.value.price+=10000; } //reactive包装对象,要使用到值只需: 对象.属性 let incr2 = function (){ car2.price+=1000; } </script> <template> <div> <h2>汽车:{{car1.brand}},价格:{{car1.price}}</h2> <h2>汽车:{{car2.brand}},价格:{{car2.price}}</h2> <button @click="incr1">比亚迪涨价</button> <button @click="incr2">小米涨价</button> </div> </template> <style scoped> </style>
表单绑定 前面我们使用的v-bind
只能实现从数据到页面的单向绑定,数据变化可以在页面中体现出来,但是页面变化无法体现在数据变量中
我们可以使用v-model
在表单输入元素或者组件上创建双向绑定,即数据改了可以传递到页面,页面改了可以传递到数据变量
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 <script setup> import {reactive} from "vue"; //将data的各个属性与表单进行绑定 const data=reactive({ name:"章三", agree:true, hobby:[], gender:"男", degree:"", course:[] }); </script> <template> <div style="display: flex;"> <div style="border: 1px solid black;width: 300px"> <form> <h1>表单绑定</h1> <p style="background-color: azure"> <label>姓名(文本框):</label> <input v-model="data.name"/> </p> <p style="background-color: azure"><label>同意协议(checkbox):</label> <input type="checkbox" v-model="data.agree"/> </p> <p style="background-color: azure"> <label>兴趣(多选框):</label><br/> <label><input type="checkbox" value="足球" v-model="data.hobby"/>足球</label> <label><input type="checkbox" value="篮球" v-model="data.hobby"/>篮球</label> <label><input type="checkbox" value="羽毛球" v-model="data.hobby"/>羽毛球</label> <label><input type="checkbox" value="乒乓球" v-model="data.hobby"/>乒乓球</label> </p> <p style="background-color: azure"> <label>性别(单选框):</label> <label><input type="radio" name="sex" value="男" v-model="data.gender">男</label> <label><input type="radio" name="sex" value="女" v-model="data.gender">女</label> </p> <p style="background-color: azure"> <label>学历(单选下拉列表):</label> <select v-model="data.degree"> <option disabled value="">选择学历</option> <option>小学</option> <option>初中</option> <option>高中</option> <option>大学</option> </select> </p> <p style="background-color: azure"> <label>课程(多选下拉列表):</label> <br/> <select multiple v-model="data.course"> <option disabled value="">选择课程</option> <option>语文</option> <option>数学</option> <option>英语</option> <option>道法</option> </select> </p> </form> </div> <div style="border: 1px solid blue;width: 200px"> <h1>结果预览</h1> <p style="background-color: azure"><label>姓名:{{data.name}}</label></p> <p style="background-color: azure"><label>同意协议:{{data.agree}}</label> </p> <p style="background-color: azure"> <label>兴趣: <li v-for="h in data.hobby">{{h}}</li> </label> </p> <p style="background-color: azure"> <label>性别:{{data.gender}}</label> </p> <p style="background-color: azure"> <label>学历:{{data.degree}}</label> </p> <p style="background-color: azure"> <label>课程: <li v-for="c in data.course">{{c}}</li> </label> </p> </div> </div> </template> <style scoped> </style>
计算属性 根据已有的数据计算出新数据
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 <script setup> import {computed, reactive} from "vue"; const car = reactive({ brand:"小米", price:1000, number:10 }); //总价由单价×数目得到 const totalPrice = computed(()=>car.price*car.number); //涨价函数 let addPrice = function(){ car.price+=100; }; </script> <template> <p>汽车品牌:{{car.brand}}</p> <p>汽车单价:{{car.price}}</p> <p>汽车数目:{{car.number}}</p> <p>总价:{{totalPrice}}</p> <button @click="addPrice">加价</button> <button @click="car.number++">加数目</button> </template> <style scoped> </style>
vue进阶使用 监听 watch vue3使用watch的注意事项
监听某个变量或者属性值的变化,然后执行对应的业务逻辑处理
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 <script setup> import {computed, reactive, watch} from "vue"; const car = reactive({ brand:"小米", price:1000, number:1 }); //总价由单价×数目得到 const totalPrice = computed(()=>car.price*car.number); //涨价函数 let addPrice = function(){ car.price+=100; }; //监听数目的变化,如果汽车数目超过了5,则弹出限购提示 //对象的属性必须使用()=>对象.属性这种形式 watch(()=>car.number,()=>{ if(car.number>5){ alert("当前购买数量超出5"); } }) </script> <template> <p>汽车品牌:{{car.brand}}</p> <p>汽车单价:{{car.price}}</p> <p>汽车数目:{{car.number}}</p> <p>总价:{{totalPrice}}</p> <button @click="addPrice">加价</button> <button @click="car.number++">加数目</button> </template> <style scoped> </style>
监听 watchEffect watch只能监听单个ref或者reactive对象,我们可以使用watchEffective同时监听所有的ref或者reactive对象
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 <script setup> import {computed, reactive, watch, watchEffect} from "vue"; const car = reactive({ brand:"小米", price:1000, number:1 }); //总价由单价×数目得到 const totalPrice = computed(()=>car.price*car.number); //涨价函数 let addPrice = function(){ car.price+=100; }; //监听数目的变化,如果汽车数目超过了5,则弹出限购提示 //监听价格变化,价格如果超出1500,则弹出太贵了的提示 //watchEffect会自动监听所有的ref或者reactive对象 watchEffect(()=>{ if(car.number>5){ alert("超出限购"); car.number=5; } if(car.price>1500){ alert("太贵了"); car.price=1500; } }) </script> <template> <p>汽车品牌:{{car.brand}}</p> <p>汽车单价:{{car.price}}</p> <p>汽车数目:{{car.number}}</p> <p>总价:{{totalPrice}}</p> <button @click="addPrice">加价</button> <button @click="car.number++">加数目</button> </template> <style scoped> </style>
Vue生命周期 官网关于生命周期的介绍
每个 Vue 组件实例在创建时都需要经历一系列的初始化步骤,比如设置好数据侦听,编译模板,挂载实例到 DOM,以及在数据改变时更新 DOM。在此过程中,它也会运行被称为生命周期钩子的函数,让开发者有机会在特定阶段运行自己的代码。
生命周期整体分为四个阶段,分别是:创建、挂载、更新、销毁
,每个阶段都有两个钩子,一前一后。
常用的钩子:
onBeforeMount:挂载之前
onMounte:挂载完成
onBeforeUpdate:更新之前
onUpdated:更新完成
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 <script setup> import {onBeforeMount, onBeforeUpdate, onMounted, onUpdated, ref} from "vue"; const count = ref(0); // 生命周期钩子 //常见场景:在挂载之前,即onBeforeMount之前向后台发送请求数据 //挂载之前有值,无页面元素 onBeforeMount(()=>{ console.log('挂载之前',count.value,document.getElementById("btn01")) }) //挂载之后才有页面元素 onMounted(()=>{ console.log('挂载完毕',count.value,document.getElementById("btn01")) }) //更新之前数据变了,但是页面内容未变动 onBeforeUpdate(()=>{ console.log('更新之前',count.value,document.getElementById("btn01").innerHTML) }) onUpdated(()=>{ console.log('更新完毕',count.value,document.getElementById("btn01").innerHTML) }) </script> <template> <button id="btn01" @click="count++">点{{count}}</button> </template> <style scoped> </style>
组件传值 项目中每一个vue文件都相当于一个组件, 一个大型项目由很多组件组成,这些组件之间如何进行值的传递?
父传子-Prop 父组件给子组件传递值,单向数据流效果
父组件修改值,子组件发生变化
子组件修改值,父组件发生变化
1 2 3 4 5 6 7 8 9 10 11 12 //父组件给子组件传递数据:使用属性绑定 <Son :books="data.books" :money="data.money"/> //子组件定义接受父组件的属性 let props = defineProps({ money: { type: Number, required: true, default: 200 }, books: Array });
使用示例
1 2 3 4 5 6 7 8 9 10 11 12 13 //在父组件里,给子组件传递数据,使用属性绑定 <script setup> import Son from "./Son.vue"; let money=1000 </script> <template> <h2>Father</h2> <son :money="money"/> </template> <style scoped> </style>
1 2 3 4 5 6 7 8 9 10 11 12 13 //在子组件里,定义接收父组件数据的属性 <script setup> let props = defineProps(["money"]) </script> <template> <h2 style="color: red">son</h2> <h3>money:{{money}}</h3> </template> <style scoped> </style>
子传父-Emit 1 2 3 4 5 6 7 8 9 10 //子组件定义发生的事件 let emits = defineEmits(['buy']); function buy(){ // props.money -= 5; emits('buy',-5); } //父组件感知事件和接受事件值 <Son :books="data.books" :money="data.money" @buy="moneyMinis"/>
插槽-slots 插槽也可以进行组件之间的传值(html元素)的传递
基本使用 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 //父组件 <script setup> import Son from "./Son.vue"; import {reactive} from "vue"; const car=reactive({ brand:"小米", price:1000 }) </script> <template> <h2>Father</h2> <h3>汽车品牌:{{car.brand}}</h3> <h3>汽车价格:{{car.price}}</h3> <button @click="car.price+=100">涨价</button><br/> <son :car="car"> 哈哈,我是谁?? </son> </template> <style scoped> </style>
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 //子组件 <script setup> let props = defineProps(["car"]) </script> <template> <h2 style="color: red">son</h2> <h3>汽车品牌:{{props.car.brand}}</h3> <h3>汽车价格:{{props.car.price}}</h3> <button @click="props.car.price-=100">降价</button> <h4>来自于父组件使用slot传递的内容:<slot/></h4> </template> <style scoped> </style>
插槽可以带默认值 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 <script setup> let props = defineProps(["car"]) </script> <template> <h2 style="color: red">son</h2> <h3>汽车品牌:{{props.car.brand}}</h3> <h3>汽车价格:{{props.car.price}}</h3> <button @click="props.car.price-=100">降价</button> <h4>来自于父组件使用slot传递的内容:<slot>我是slot默认值</slot></h4> </template> <style scoped> </style>
具名插槽 当子组件中多个地方需要插槽,应当如何进行区分
对于子组件,给插槽取名称<slot name="名称"></slot>
对于父组件,使用template定义插槽’内容 ‘
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 //父组件 <script setup> import Son from "./Son.vue"; import {reactive} from "vue"; const car=reactive({ brand:"小米", price:1000 }) </script> <template> <h2>Father</h2> <h3>汽车品牌:{{car.brand}}</h3> <h3>汽车价格:{{car.price}}</h3> <button @click="car.price+=100">涨价</button><br/> <son :car="car"> <template v-slot:slot1> 插槽1 </template> //v-slot可以用 #代替 <template #slot2> <span style="color: red">插槽2</span> </template> </son> </template> <style scoped> </style>
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 //子组件 <script setup> let props = defineProps(["car"]) </script> <template> <h2 style="color: red">son</h2> <h3>汽车品牌:{{props.car.brand}}</h3> <h3>汽车价格:{{props.car.price}}</h3> <button @click="props.car.price-=100">降价</button> <h4>来自于父组件使用slot传递的内容:<slot>我是slot默认值</slot></h4> <slot name="slot1"></slot> <slot name="slot2"></slot> </template> <style scoped> </style>
vu3总结
几个简写:
v-on
=@
v-bind
= :
v-slot
= #
Vue-Router 路由概述 vue router官方文档
前端系统根据页面路径。跳转到指定的组件,展示对应的页面效果
路由入门示例 创建项目 1 2 npm create vite # 选择对应的项目和语言
整合vue-router 参照官方文档
1 2 # 安装依赖 npm install vue-router@4
官方入门案例
路由配置的一般步骤
编写router/index.js
文件
在index.js
中配置路由信息
在index.js
中创建路由器并导出
在main.js
中使用路由
在相应的页面使用router-link
和router-view
完成路由功能
实例 router/index.js
内容
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 import home from "../views/home.vue" ;import dog from "../views/dog.vue" ;import {createMemoryHistory, createRouter} from "vue-router" ;const routes = [ {path : '/' ,component : home}, {path : '/dog' ,component : dog}, {path : "/cat" ,component :()=> import ('../views/cat.vue' )} ] const router = createRouter ({ history : createMemoryHistory (), routes }) export default router
main.js
内容
1 2 3 4 5 6 import { createApp } from 'vue' import './style.css' import App from './App.vue' import router from "./router/index.js" ;createApp (App ).use (router).mount ('#app' )
App.vue
内容
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 <script setup> </script> <template> <div> <!-- 使用组件 RouterLink 来创建链接 --> <router-link to="/">首页Home</router-link> <router-link to="/dog">dog页面</router-link> <router-link to="/cat">cat页面</router-link> <!-- RouterView 组件可以使 Vue Router 知道你想要在哪里渲染当前 URL 路径对应的路由组件 --> <router-view/> </div> </template> <style scoped> </style>
路径参数 使用 :变量名
接受动态参数;这个成为 路径参数 ,类似与Spring MVC
中的path variable
index.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 import home from "../views/home.vue" ;import dog from "../views/dog.vue" ;import {createMemoryHistory, createRouter} from "vue-router" ;const routes = [ {path : '/' ,component : home}, {path : '/dog' ,component : dog}, {path : "/cat:id" ,component :()=> import ('../views/cat.vue' )} ] const router = createRouter ({ history : createMemoryHistory (), routes }) export default router
嵌套路由 多级路由嵌套,比如从父页面跳转到子页面,再从子页面可以到孙页面
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 import home from "../views/home.vue" ;import dog from "../views/dog.vue" ;import {createMemoryHistory, createRouter} from "vue-router" ;import user from "../views/user.vue" ;import userEmail from "../views/userEmail.vue" ;import userDetail from "../views/userDetail.vue" ;const routes = [ {path : '/' ,component : home}, { path : '/user/:id' , component : user, children : [ { path : 'email' , component : userEmail, }, { path : 'detail' , component : userDetail, }, ], }, ] const router = createRouter ({ history : createMemoryHistory (), routes }) export default router
编程式 useRoute:路由数据 路由传参跳转到指定页面后,页面需要取到传递过来的值,可以使用 useRoute
方法,拿到当前页路由数据;可以做
获取到当前路径
获取到组件名
获取到参数
获取到查询字符串
1 2 3 4 5 6 import {useRoute} from 'vue-router' const route = useRoute ()console .log (route.query )console .log (route.params )
useRouter:路由器 拿到路由器;可以控制跳转、回退等
1 2 3 4 5 6 7 8 import {useRoute, useRouter} from "vue-router" ;const router = useRouter ()router.push ("/" ) router.go (-1 );
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 import {useRoute, useRouter} from "vue-router" ;const router = useRouter ();router.push ('/users/eduardo' ) router.push ({ path : '/users/eduardo' }) router.push ({ name : 'user' , params : { username : 'eduardo' } }) router.push ({ path : '/register' , query : { plan : 'private' } }) router.push ({ path : '/about' , hash : '#team' }) router.push ({ path : '/user' , params : { username } })
路由传参 params 参数 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 <!-- 跳转并携带params参数(to的字符串写法) --> <RouterLink :to="`/news/detail/001/新闻001/内容001`">{{news.title}}</RouterLink> <!-- 跳转并携带params参数(to的对象写法) --> <RouterLink :to="{ name:'xiang', //用name跳转,params情况下,不可用path params:{ id:news.id, title:news.title, content:news.title } }" > {{news.title}} </RouterLink>
query 参数 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 <!-- 跳转并携带query参数(to的字符串写法) --> <router-link to="/news/detail?a=1&b=2&content=欢迎你"> 跳转 </router-link> <!-- 跳转并携带query参数(to的对象写法) --> <RouterLink :to="{ //name:'xiang', //用name也可以跳转 path:'/news/detail', query:{ id:news.id, title:news.title, content:news.content } }" > {{news.title}} </RouterLink>
导航守卫 具体使用细节参照导航守卫官方文档
类似于拦截器,再跳转到指定页面之前进行相应的逻辑处理
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 import {createRouter, createWebHistory} from 'vue-router' import HomeView from '../views/HomeView.vue' const router = createRouter({ history: createWebHistory(import.meta.env.BASE_URL), routes: [ { path: '/', name: 'home', component: HomeView }, { path: '/about', name: 'about', // route level code-splitting // this generates a separate chunk (About.[hash].js) for this route // which is lazy-loaded when the route is visited. component: () => import('../views/AboutView.vue') }, { path: '/user/:name', name: 'User', component: () => import('@/views/user/UserInfo.vue'), children: [ { path: 'profile', component: () => import('@/views/user/Profile.vue') }, { path: 'posts', component: () => import('@/views/user/Posts.vue') } ] } ] }) router.beforeEach(async (to, from) => { console.log("守卫:to:", to) console.log("守卫:from:", from) if (to.fullPath === '/about') { return "/" } }) export default router
vue-router总结
Axios 概述 Axios官方文档
Axios 是一个基于 promise 的网络请求库,可以用于浏览器和 node.js,主要用于前端向后端请求数据
1 2 3 4 //基本用法 import axios from "axios" axios.get('/user') .then(res => console.log(resp.data))
请求 get请求 1 2 3 4 5 6 7 8 9 10 11 12 13 axios.get ('/user?ID=12345' ) .then (function (response ) { console .log (response); }) .catch (function (error ) { console .log (error); }) .finally (function ( ) { });
携带请求参数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 axios.get ('/user' , { params : { ID : 12345 } }) .then (function (response ) { console .log (response); }) .catch (function (error ) { console .log (error); }) .finally (function ( ) { });
post请求 默认post请求体
中的数据将会以json
方式提交
1 2 3 4 5 6 7 8 9 10 axios.post ('/user' , { firstName : 'Fred' , lastName : 'Flintstone' }) .then (function (response ) { console .log (response); }) .catch (function (error ) { console .log (error); });
响应 响应的数据结构如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 { data : {}, status : 200 , statusText : 'OK' , headers : {}, config : {}, request : {} }
配置 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 const instance = axios.create ({ baseURL : 'https://some-domain.com/api/' , timeout : 1000 , headers : {'X-Custom-Header' : 'foobar' } }); { url : '/user' , method : 'get' , baseURL : 'https://some-domain.com/api/' , transformRequest : [function (data, headers ) { return data; }], transformResponse : [function (data ) { return data; }], headers : {'X-Requested-With' : 'XMLHttpRequest' }, params : { ID : 12345 }, paramsSerializer : function (params ) { return Qs .stringify (params, {arrayFormat : 'brackets' }) }, data : { firstName : 'Fred' }, data : 'Country=Brasil&City=Belo Horizonte' , timeout : 1000 , withCredentials : false , adapter : function (config ) { }, auth : { username : 'janedoe' , password : 's00pers3cret' }, responseType : 'json' , responseEncoding : 'utf8' , xsrfCookieName : 'XSRF-TOKEN' , xsrfHeaderName : 'X-XSRF-TOKEN' , onUploadProgress : function (progressEvent ) { }, onDownloadProgress : function (progressEvent ) { }, maxContentLength : 2000 , maxBodyLength : 2000 , validateStatus : function (status ) { return status >= 200 && status < 300 ; }, maxRedirects : 5 , socketPath : null , httpAgent : new http.Agent ({ keepAlive : true }), httpsAgent : new https.Agent ({ keepAlive : true }), proxy : { protocol : 'https' , host : '127.0.0.1' , port : 9000 , auth : { username : 'mikeymike' , password : 'rapunz3l' } }, cancelToken : new CancelToken (function (cancel ) { }), decompress : true }
拦截器 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 axios.interceptors .request .use (function (config ) { return config; }, function (error ) { return Promise .reject (error); }); axios.interceptors .response .use (function (response ) { return response; }, function (error ) { return Promise .reject (error); });
pina 概述 官方文档
Pinia 是 Vue 的存储库 ,它允许您跨组件/页面共享状态 。 背景:
对于一个web页面,用户登录之后,其他各个组件需要使用用户的信息,这个时候我们可以使用前面讲过的vue-router
路由传参或者vue
组件传值的方式来进行数据传输,但是这两个方式比较繁琐,所以需要引入pinia,来进行库组件数据共享。
Pinia 三个核心概念:
State:表示 Pinia Store 内部保存的数据(data)
Getter:可以认为是 Store 里面数据的计算属性(computed)
Actions:是暴露修改数据的几种方式。
虽然外部也可以直接读写Pinia Store 中保存的data,但是我们建议使用Actions暴露的方法操作数据更加安全 。
实例 安装pinia
main.js 1 2 3 4 5 6 7 import { createApp } from 'vue' import './style.css' import App from './App.vue' import {createPinia} from "pinia" ;const pina = createPinia ()createApp (App ).use (pina).mount ('#app' )
定义存储单元 在store/money.js
中编写如下内容,定义存储单元
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 import {defineStore} from "pinia" ;export const moneyStore = defineStore ("money" ,{ state :()=> ({"money" :100 }), getters :{ rmb :(state )=> state["money" ], usb :(state )=> state["money" ]*6 , er :(state )=> state["money" ]*11 }, actions :{ win (args ){ this ["money" ]+=args }, pay (args ){ this ["money" ]-=args } } })
组件中使用存储单元 showMoney.vue
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 <template> <div> <h2>账户余额</h2> <h3>人名币:{{money.rmb}}</h3> <h3>美元:{{money.usb}}</h3> <h3>欧元:{{money.er}}</h3> </div> </template> <script setup> import {moneyStore} from "../store/money.js"; let money = moneyStore() </script> <style scoped> div{ color: #888888; } </style>
actionMoney.vue
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 <template> <button @click ="guaguale" > 刮刮乐</button > <button @click ="shop" > 购物</button > </template> <script setup > import {moneyStore} from "../store/money.js" ; let money = moneyStore () function guaguale ( ){ money.win (100 ); } function shop ( ){ money.pay (10 ); } </script > <style scoped > </style >
App.vue
1 2 3 4 5 6 7 8 9 10 11 12 <script setup> import ActionMoney from "./components/actionMoney.vue" ;import ShoMoney from "./components/shoMoney.vue" ;</script> <template > <sho-money /> <action-money /> </template > <style scoped > </style >
Setup Store 也存在另一种定义 store 的可用语法。与 Vue 组合式 API 的 setup 函数 相似,我们可以传入一个函数,该函数定义了一些响应式属性和方法,并且返回一个带有我们想暴露出去的属性和方法的对象。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 import {defineStore} from "pinia" ;import {computed, ref} from "vue" ;export const moneyStore = defineStore ("money" ,()=> { const money=ref (100 ); const rmb = computed (()=> money.value ); const usb = computed (()=> money.value *6 ); const er = computed (()=> money.value *11 ); const win = (arg )=>{ money.value +=arg; } function pay (arg ){ money.value -=arg; } return {money,rmb,usb,er,win,pay} })
脚手架 1 2 3 npm create vite # 选择 使用 create-vue 自定义项目 npm create vue@latest # 直接使用create-vue 创建项目,会自动整合好vue-router和pinia、Axios等工具链,无需额外下载相关雨来 vue-cli #已经过时
Ant Design Vue 官方文档
一个第三方UI组件库,提供了许多漂亮的UI组件,可以直接引用,快速进行相关页面的开发
使用 npm create vue@latest
创建出项目脚手架,然后整合ant design vue
整合ant design vue 安装依赖
1 npm i --save ant-design-vue@4.x
全局注册: 编写main.js
1 2 3 4 5 6 7 8 import { createApp } from 'vue' ;import Antd from 'ant-design-vue' ;import App from './App' ;import 'ant-design-vue/dist/reset.css' ;const app = createApp (App );app.use (Antd ).mount ('#app' );
组件的使用 各个组件的详细用法,参考官方文档示例
参考资料 参考资料1
参考资料2