Vue
概述
- Vue.js是一个专注于构建Web用户界面的JavaScript库
- 作者:尤雨溪,中国人,早前就职于Google公司
- 思想:MVVM(Model-View-ViewModel)
- M:Model,模型(数据和业务逻辑)
- View:视图
- ViewModel:视图模型,是一个纽带,它连接模型和视图
- 一个渐进式的框架
使用
安装
//使用CDN <script src="https://unpkg.com/vue@3/dist/vue.global.js"></script> //先安装node.js,在当前终端中:npm install vue <script src="./node_modules/vue/dist/vue.global.js"></script>
创建第一个案例
<div id="app"> {{msg}} </div>
//app表示一个Vue的应用实例 const app = Vue.createApp({ data(){ return{ msg:'Hello!!' } } }); //root表示Vue应用的根组件 const root = app.mount("#app");
- Vue:是一个包含各种工具和方法的对象,用来创建并配置一个新的Vue应用实例
- Vue.createApp(Vue组件配置对象):创建了一个新的Vue应用实例
- mount(“#app”):把Vue实例挂载到id为app的div上
- root:表示一个根组件实例,可以通过这个实例来访问组件的属性,方法和数据
- 组件的data()方法返回的对象中的数据属性可以直接在组件的模板中访问,也可以通过组件的实例来访问。但注意。data()方法必须返回一个对象
- ():插值表达式,指的是使用双大括号
{{}}
在模板中嵌入动态的值
语法
插值表达式
- 在插值表达式中可以写简单的js表达式
- 不是语句,不能写控制语句
指令
Vue提供了一系列的内置指令,用于在模板中执行一些常见的任务,如条件渲染,列表渲染,事件监听等,这些指定都是以v-前缀开头的
v-text:用于更新元素的textContent,不会对数据中的html进行解析
v-html:用于更新元素的innerHTML,会对数据中的html标签进行解析,显示解析后的结果
v-bind:用于绑定属性到表达式,简写形式:
:
v-model:绑定表单数据,双向绑定
<input v-model="title"> data(){ return{ title:'tom' } }
v-on:绑定事件处理程序,简写:@
<div id="app"> <input v-on:click="test" type="button" value="测试"> <input :value="abc"> </div> <script> const root = Vue.createApp({ data() { return { abc:'', } }, methods:{ test(){ this.abc="tom"//this:当前vue组件 } } }).mount("#app"); </script>
v-show:是否显示元素,元素的显示和隐藏是通过css的display属性来控制的
<p v-show="b">abc</p> <script> const root = Vue.createApp({ data() { return { b:false } },methods:{ ifshow(){ this.b=!this.b } } }).mount("#app"); </script>
v-if,v-else-if,v-else:用于条件渲染
<div id="app"> <input v-model="score"> <p v-if="score>=90">优秀</p> <p v-else-if="score>=80">良好</p> <p v-else-if="score>=70">中等</p> <p v-else-if="score>=60">及格</p> <p v-else>不及格</p> </div> <script> const root = Vue.createApp({ data() { return { score:90 } } }).mount("#app"); </script>
v-if和v-show的区别
- v-if:如果为false,会删除DOM元素,为true,会创建DOM元素
- v-show:通过样式显示和隐藏元素,隐藏时并不会删除DOM元素
v-for:用于渲染列表
<table border="1"> <tr> <th>学号</th> <th>姓名</th> <th>年龄</th> </tr> <tr v-for="(stu,index) in stus":key="stu.index"> <td>{{index+1}}</td> <td>{{stu.name}}</td> <td>{{stu.age}}岁</td> </tr> </table> </div> <script> const root = Vue.createApp({ data() { return { stus:[ {name:'tom',age:20}, {name:'marry',age:18}, {name:'scott',age:25} ] } } }).mount("#app"); </script>
vue使用”就地复用”的思想复用已经生成的DOM,使用绑定key属性来确定DOM属性是否已经创建,要求key的值是唯一的
表单样例
<!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="UTF-8"> <title>Title</title> <script src="./node_modules/vue/dist/vue.global.js"></script> </head> <body> <div id="app"> <table border="1"> <tr> <th>学号</th> <th>姓名</th> <th>年龄</th> <th colspan="2">操作</th> </tr> <tr v-for="(stu,index) in stus":key="stu.index"> <td>{{index+1}}</td> <template v-if="stu.edit" > <td><input type="text" size="5" v-model="stu.name"></td> <td><input type="text" size="5" v-model="stu.age"></td> </template> <template v-else> <td>{{stu.name}}</td> <td>{{stu.age}}岁</td> </template> <td><input @click="edit(stu)" :value="statement" type="button"></td> <td><input type="button" @click="deleteItem(index)" value="删除"></td> </tr> </table> 姓名:<input type="text" v-model="stu.name"> 年龄:<input type="text" v-model="stu.age"> <input type="button" value="添加" @click="add(stu.name,stu.age)"> </div> <script> const root = Vue.createApp({ data() { return { stus:[ {name:'tom',age:20}, {name:'marry',age:18}, {name:'scott',age:25} ], statement:"编辑", stu:{ name:'', age:'' } } },methods:{ edit(stu){ if(this.statement === "编辑"){ stu.edit = true; this.statement = "保存"; }else { stu.edit = false; this.statement = "编辑"; } }, deleteItem(index){ if (confirm("是否删除")){ this.stus.splice(index,1); } }, add(){ this.stus.push(this.stu); this.stu={ name:"", age:"" } } } }).mount("#app"); </script> </body> </html>
绑定复选框
<template v-for="hobby in hobbies">
<input type="checkbox" v-model="selectedItem" :value="hobby">{{hobby}}
</template>
单个复选框:使用v-model绑定一个布尔值
<input type="checkbox" v-model="isAll"> <script> Vue.createApp({ data(){ return{ isAll:false//复选框被选中,isAll为true } } }) </script>
复选框组:使用v-model绑定到一个数组,如果复选框被选中,当前复选框的值会被放到数组中,取消选择,则从数组中移除
<input type="checkbox" v-model="selectedItem" :value="hobby">{{hobby}} const root = Vue.createApp({ data() { return { hobbies:[ "学java", "玩游戏", "踢足球", "打篮球" ], selectedItem:[ ], selectTime:false, isAll:false } },methods:{ checkAll(){ // this.selectTime = !this.selectTime; if (this.isAll){ this.selectedItem =[...this.hobbies]; }else{ this.selectedItem.length = 0; } }, reverseAll(){ this.selectedItem = this.hobbies.filter(h=>{ //includes:查找参数元素是否存在,如果存在返回true,否则返回false return !this.selectedItem.includes(h); /*if (this.selectedItem.indexOf(h) >= 0){ return false; }else { return true; }*/ }) } } }).mount("#app"); </script>
绑定单选按钮
<div id="app">
<template v-for="hobby in hobbies">
<input type="radio" v-model="selectItem" :value="hobby">{{hobby}}
</template>
</div>
<script>
const root = Vue.createApp({
data() {
return {
hobbies:["阅读","旅游","音乐","运动"],
selectItem:''//如果值为"阅读",则"阅读"被默认选中
}
}
}).mount("#app");
注意:没有给单选按钮的value赋值,默认值是on
绑定下拉列表框
<select v-model="selectProvince" @change="cityChange">
<option v-for="province in provinces" >{{province}}</option>
</select>
省市级联综合案例
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="./node_modules/vue/dist/vue.global.js"></script>
</head>
<body>
<div id="app">
省份<select v-model="selectProvince" @change="cityChange">
<option v-for="province in provinces" >{{province}}</option>
</select>
城市<select>
<option v-for="city in selectCities">{{city}}</option>
</select>
</div>
<script>
const root = Vue.createApp({
data() {
return {
provinces:["辽宁",'吉林','黑龙江'],
selectProvince:'--请选择--',
cities:new Map([
['辽宁',['沈阳','大连','盘锦']],
['吉林',['长春','吉林']],
['黑龙江',['哈尔滨','齐齐哈尔']]
]),
selectCities:[]
}
},methods:{
cityChange(){
this.selectCities=this.cities.get(this.selectProvince)
}
}
}).mount("#app");
</script>
</body>
</html>
Map集合
创建map集合
let map = new Map();
创建并初始化map集合
let map = new Map([[key.value],[key.value],[key.value],[key.value],[key.value]...]);
添加键值对
map.set(key,value)
根据键取值
map.get(key)
移除键值对
map.delete(key)
得到键的迭代器对象
let keys = map.keys() let keys = this.map.keys(); for (let i = 0; i < this.map.size; i++) { this.items.push(keys.next().value) }
得到值的迭代器对象
let values = map.values() let items = this.map.values() for (let i = 0; i < this.map.size; i++) { this.items.push(items.next().value) }
得到键值对的个数
map.size
样式绑定
通过v-bind指令,绑定内联样式(行内样式)和类样式
绑定内联样式
- 绑定单个样式
<div id="app"> <p :style="{color:colorValue,fontSize:fontSizeValue}">tom</p> <input type="button" value="修改" @click="changeStyle"> </div> <script> const root = Vue.createApp({ data() { return { colorValue:'red', fontSizeValue:'15px' } },methods:{ changeStyle(){ this.colorValue = 'blue' this.fontSizeValue = "50px" } } }).mount("#app"); </script>
绑定样式对象
<div id="app"> <p :style="styleObj">tom</p> <input type="button" value="修改" @click="changeStyle"> </div> <script> const root = Vue.createApp({ data() { return { styleObj:{ color:'red', fontSize:'15px' } } },methods:{ changeStyle(){ } } }).mount("#app"); </script>
类样式绑定
绑定单个类名
<style> .class1{ color: red; } </style> <p :class="{class1:isShow}">普通文本</p> data() { return { isShow:true } }
绑定类对象
<style> .class1{ color: red; } .class2{ font-size: 20px; } </style> <div id="app"> <p :class="classObj">普通文本</p> </div> data() { return { classObj:{ class1:true, class2:true } } }
绑定类数组
<style> .class1{ color: red; } .class2{ font-size: 50px; } </style> <div id="app"> <p :class="[classObj1,classObj2]">普通文本</p> </div> data() { return { classObj1:{ class1:true }, classObj2:{ class2:true } } }
计算属性
定义:计算属性是基于组件的响应式依赖进行缓存的属性,只有当它的依赖发生变化时,才会重新求值
使用原因:
- 性能优化
- 逻辑清晰
基本使用
<div id="app"> 商品价格:<input type="text" v-model="price"> 商品数量:<input type="number" v-model="sum"><br> 商品总价:{{total}}<br> 姓名:{{showName}} </div> <script> const root = Vue.createApp({ data() { return { price:10, sum:0, name:"tom" } },computed:{//计算属性 total(){ return this.price*this.sum }, showName(){ return this.name.toUpperCase(); } } }).mount("#app"); </script>
计算属性和方法
- 计算属性依赖缓存,只有当依赖发生变化的时候才会重新求值
监听属性
定义:监听属性允许你响应某些特定的数据变化,执行自定义的逻辑,与计算属性相似,但专为侦听数据的变化而设计
基本使用
<div id="app"> <input type="text" v-model="km">千米<br> <input type="text" v-model="m">米<br> </div> <script> const root = Vue.createApp({ data() { return { km:0, m:0 } },methods:{ },computed:{ },watch:{ km(newValue,oldValue){ this.m = newValue*1000; }, m(newValue,oldValue){ this.km = newValue/1000; } } }).mount("#app");
为什么使用监听属性
- 执行异步操作或昂贵操作(计算属性无法执行异步操作,它是同步的)
- 可以对多个数据源进行侦听
深度监听和立即执行
<div id="app"> 商品价格:<input type="text" v-model="good.price"><br> 商品数量:<input type="text" v-model="good.num"><br> {{total}} </div> <script> const root = Vue.createApp({ data() { return { good:{ price:1, num:1 }, total:'' } },watch:{ good:{ handler(newValue){ this.total = this.good.num * this.good.price }, deep:true//深度监听,监听对象的属性 ,immediate:true//立即执行 } } }).mount("#app"); </script>
组件
概述
定义:组件是Vue最强大的功能之一,组件可以扩展html元素,封装可重用的代码
目的:
- 代码重用
- 模块化
- 简洁性:清晰地定义界面的每个部分
基础使用:
定义组件
const myComponent = { template:`<div >{{msg}}--{{count}}--<input @click="add" type="button" value="+"></div>`, data(){ return{ msg:'Hello!', count:1 } },methods:{ add(){ this.count++ } } }
注册组件
局部注册
const root = Vue.createApp({ data() { return { } }, components:{//注册组件 组件名:组件对象 "my-component":myComponent } }).mount("#app");
全局注册
const app = Vue.createApp({ data() { return { } }, //在Vue应用对象中注册,称为全局注册 app.component("my-component",myComponent)
在父组件的模板中使用
<div id="app"> <my-component></my-component> </div>
组件名称注意事项:
- 驼峰命名:在JavaScript中使用驼峰命名法,例如:myComponent
- 短横线命名:在html的模板中,组件名应使用短横线命名,例如:my-component
- 避免使用Vue保留的前缀,如:v-
Vue3父子组件通信
基本概念:
- 在vue中,父子组件的关系可以总结为prop down,events up(属性下传,事件上抛)
- 父组件通过props将数据传递给子组件
- 子组件通过事件向父组件发送信息
props
定义:是props是父组件用来传递数据给子组件的自定义属性
使用:在子组件中声明
props
属性来接收数据//子组件 { name:'子组件的名字', props:["num"] }
注意:子组件内部不应该修改prop的值
自定义事件
子组件
//ChildCom.vue this.$emit('事件名',向父组件传递的数据)
父组件
//ParentCom.vue <template> <ChildCom @事件名="父组件中的方法"></ChildCom> <ChildCom @事件名="num=$"></ChildCom> </template>
methods:{ //value表示子组件传递出来的值 父组件中的方法(value){ } },data(){ return{ num:0 } }
$event:表示子组件传递出来的值
provide和inject
概述:主要用于避免为深层次的组件通过 props 逐层传递数据。
工作原理:
- provide:通过该属性暴露一些属性或方法,使其在子组件中可用
- inject:任何子组件或更深层的组件可以通过 inject 选项来获取其祖先组件暴露出来的属性或方法
基本使用
父组件
provide:{ themeColor:'blue' }
子组件
inject:['themeColor']
插槽
实现了一种内容分发的机制。可以使得父组件插入内容到子组件的视图结构中。
基础插槽
默认插槽:允许父组件为子组件插入内容
- 如果子组件没有预留出插槽,父组件的内容被忽略
- 子组件内容使用
<slot></slot>
来指定内容的分发位置
子组件
//ChildSlot <template> <div><slot></slot></div> </template>
父组件
<ChildSlot> <h1>父组件插入的内容</h1> </ChildSlot>
具名插槽
允许多个插槽的存在,需要使用name属性区分
子组件
//ChildSlot <template> <div><slot name="header"></slot></div> <div><slot></slot></div> </template>
父组件
<ChildSlot> <template #header> 插入到名称为header的子组件插槽 </template> <h1>父组件插入的内容</h1> </ChildSlot>
作用域插槽
允许子组件为插槽传递数据
子组件
//ChildSlotScope data(){ return { num:100, name:'tom' } } <template> <slot :n="num" :username="name"></slot> <slot name="header" :n="num"></slot> </template>
父组件
<ChildSlotScope> <template #default="slotScope"> 数量:{{slotScope.n}},用户名:{{slotScope.username}} </template> <template #header="slotScope"> 数量:{{slotScope.n}} </template> </ChildSlotScope>
Vite
概述
Vite是什么?
- Vite(法语中的”快”),是一个由Vue.js的创建者尤雨溪开发的Web开发构建工具,旨在提供更快的冷启动时间
- 它不同于传统的工具如Webpack,Vite利用了ES6的原生模块系统(ESM)进行快速的源码到源码的转换
基础使用
npm create vite@latest npm create vite@latest 项目名 -- --template vue
Vite中vue项目目录结构
- index.html:这是应用的主html文件,它是Vite开发服务器的入口点,并且也是构建后的应用的入口点
- package.json:这个文件包含了项目的元数据,例如:项目的名称,版本,依赖
- package-lock.json:是自动生成的,用于描述项目的依赖树结构的文件,当在项目中安装或更新依赖时,这个文件会自动更新
- src/:这个目录包含了应用的所有源文件,包括Vue组件、JavaScript文件和其他资源
- assets:这个目录是用于存放静态资源,如图片,字体等
- components:这里通常存放vue组件文件
- App.vue:主Vue组件文件,通常会包括应用的布局和样式
- vite.config.js:自定义的vite配置放到这里
路由
概述
介绍
- Vue Router是Vue.js官方的路由管理器,它与Vue.js的核心深度集成,简化单页面应用的开发
安装
npm install vue-router@4
基本使用
创建路由对象
//src/router/index.js import {createRouter,createWebHistory} from "vue-router"; //路由表 const routes=[ { path:"/login", name:"login", component:()=> import("../views/Login.vue") },{ path:"/main", name:"main", component:()=>import("../views/main.vue") } ] //创建一个路由器对象 const router=createRouter({ history:createWebHistory(import.meta.env.BASE_URL), routes }) export default router;
在主应用中使用
//main.js import { createApp } from 'vue' // import './style.css' import router from "./router/index.js";//导入路由器对象 import App from './App.vue' createApp(App).use(router).mount('#app')//在vue应用中注册路由插件
在App.vue组建中添加
<router-view></router-view>
路由传参
使用路由路径参数
{ path:'editDept/:deptno' } //使用params传参,不能使用path属性 <router-link :to="{name:'editdept',params:{deptno:dept.deptno}}"></router-link> //得到数据 {{$route.params.deptno}} //地址栏 http://localhost:5173/main/editdept/10
实用查询参数
不需要在路由配置中预先定义查询参数
{ path:'editdept' } <router-link :to="{name:'editdept',query:{deptno:dept.deptno}}"></router-link> //地址栏 http://localhost:5173/main/deptModify?deptno=10&dname=人力资源部
编程式路由
导航的两种方式
- 声明式导航:通过
<router-link>
标签 - 编程式导航:通过Vue-router提供的api来实现
- 声明式导航:通过
router.push()
会向路由的历史记录中添加一个新的记录
$router.push("/main") $router.push({name:'dept',query:{deptno:Deptno}})
router.replace()
跟push()类似,但它不会向历史记录中添加新纪录,而是替换掉当前记录
router.go(n)
在历史记录中来回跳转
this.$router.go(-1)后退一页 this.$router.go(1)前进一页
路由守卫
主要用于监视路由的变化,可以执行某些操作,例如:权限检查,页面跳转
router.beforeEach((to,from,next)=>{
if (to.path != '/' && to.path != '/login'){
const user = sessionStorage.getItem("user");
if (!user){
next("/login")//跳转回登录页
}else {
next();
}
}
next();//放行
})
- beforeEach():允许在路由变化之前设置一个钩子函数
- {to,from,next}=>{}
- to:路由对象,表示即将导航到的目的路由对象
- from:表示从哪个路由导航而来
- next:是一个函数
- next(‘/login’):拦截,跳转到指定路径
- next(false):将中断导航
路由监听
使用watch选项来监听某个路由的变化
//APP.vue
watch:{
$route(to,from){
if (to.name === "deptModify" && from.name === "dept"){
alert("即将跳转")
}
}
}
组件的生命周期
描述了组件从被创建到被销毁的整个过程,我们可以使用”钩子”在关键时刻插入自定义的代码
- beforeCreate和created
- beforeCreate:组件实例创建之前执行的方法
- created:组件实例创建完毕
- beforeMount和mounted
- beforeMount:当组件挂载之前执行
- mount:组件被挂载到DOM上
- beforeUpdate和updated
- beforeUpdate:当响应式数据发生变化,组件需要更新之前
- update:组件模板已经更新,数据变化后
- beforeDestory和destroyed
- beforeDestory:组件销毁前执行
- destroyed:组件被销毁后执行
Vuex
介绍
- 是vue.js的状态管理库
安装
npm install vuex@next --save
配置
//src/store/index.js import {createStore} from "vuex"; const store = createStore({ state(){ return{ count:0 } } }) export default store
在Vue应用对象中注册插件
//main.js import store from "./store/index.js"; createApp(App).use(router).use(store).mount('#app')
在vue组件中
{{$store.state.count}} this.$store.state.count
改变状态
不能直接改变store中的状态,修改store中的状态的唯一途径就是显示的提交(commit) mutation
const store = createStore({ state(){ return{ count:0 } }, mutations:{ increment(state){ state.count++ } } }) //代码 this.$store.commit('increment')
getters
从store中的state派生出一些状态
import {createStore} from "vuex"; const store = createStore({ state(){ return{ count:0 } }, mutations:{ increment(state,num){ state.count += num } }, getters:{ count2: state => state.count+"个" } }) //模板中 {{$store.getters.count2}}
Actions
Action可以包含任意异步操作
Action提交时是mutation而不是直接修改状态
import {createStore} from "vuex"; const store = createStore({ state(){ return{ count:0 } }, mutations:{ increment(state,num){ state.count += num } }, getters:{ count2: state => state.count+"个" }, actions:{ add(context){ setTimeout(function (){ context.commit('increment',2) },3000) } } }) export default store
组合式API
概述
是Vue3新引入的一种编写组件逻辑的方式,与Vue2的选项式API相比,提供了更加灵活的代码组织方式
set()函数
<script>
import {ref} from "vue";
export default {
setup(){
let num = ref(10);
let name = ref("tom")
function test(){
num.value++;
name.value = "marry"
}
return {
num,
test,
name
}
}
}
</script>
<template>
{{num}}
{{name}}
<input type="button" @click="test" value="test">
</template>
<style scoped>
</style>
<script setup>
<script setup>
import {ref} from "vue";
let num = ref(10);
let name = ref("tom")
function test(){
num.value++
name.value = "marry"
}
</script>
<template>
{{num}}
{{name}}
<input type="button" @click="test" value="test">
</template>
<style scoped>
</style>
ref和reactive
ref:用于将原始数据类型(如:string、number)转化为响应式对象
- 返回的对象有一个value属性,通过这个属性可以获取或设置其值
reactive:用于创建一个响应式对象
- 对于对象和数组,可以直接使用它们,无需.value
- 不能直接包装原始数据类型
import {reactive, ref} from "vue"; const obj = reactive( { age:10, arr:[1,2,3], scores:[ {name:'数学',score:100}, {name:'语文',score:90}, {name:'英语',score:80}, ] } ) const arr=reactive([ {name:'数学',score:100}, {name:'语文',score:90}, {name:'英语',score:80}, ]) const arr2 =ref( { count:10, name:'tom' } ) function change(){ // obj.age++; // obj.arr[0]++; // obj.scores[0].score--; // arr[0].score-- arr2.value.count++ }
计算属性
let total = computed(()=>price.value*num.value)
props
//子组件
let props = defineProps(['name','num'])
{{props.name}}--{{props.num}}
//父组件
<ChildTest name='tom' num='18'></ChildTest>
defineEmits
//子组件
let emits = defineEmits([
'event1','event2'
])
function toParent(){
emits('event1',100)
emits("event2",200)
}
//父组件
<script setup>
import ChildTest from "./ChildTest.vue"
import {ref} from "vue";
let num = ref(0)
</script>
<template>
<ChildTest name="tom" num="18" @event2="num = $event"></ChildTest>
{{num}}
</template>
<style scoped>
</style>
生命周期钩子函数
没有对应的created函数,原来在created事件发生事后执行的代码直接写到script标签中
function showHi(){ alter("Hi") } showHi();
onMounted
onMounted(() =>{ alter("Hi") })
大多数生命周期的钩子函数都是在原来函数前加
on
,特殊的如:destroyed和beforeDestroyed用onUnmounted
和onBeforeUnmount
替代
监听器
let km = ref(0)
let m = ref(0)
watch(km,(newValue,oldValue)=>{
m.value = km.value * 1000
})
watch(m,()=>{
km.value = m.value / 1000
})
补充
在Vite中动态使用图片
import imgSrc from '../assets/vue.svg'
<img :src="imgSrc">
1.