javaWeb vue的简单语法
一、简介
两大核心优势:
声明式渲染:Vue 基于标准 HTML 拓展了一套模板语法,使得我们可以声明式地描述最终输出的 HTML 和 JavaScript 状态之间的关系。
响应性:Vue 会自动跟踪 JavaScript 状态并在其发生变化时响应式地更新 DOM
二、单文件组件SFC
Single File Component。
(1)VUE的组件
一个页面作为整体,是由多个部分组成的,每个部分在这里就可以理解为一个组件。也就是说,组件是页面的一个部分。
每个.vue文件就可以理解为一个组件,多个.vue文件可以构成一个整体页面
组件化给我们带来的另一个好处就是组件的复用和维护非常的方便
(2).vue文件
传统的页面由.html文件.css文件和.js文件三个文件组成(多文件组件)
vue将这文件合并成一个.vue文件(Single-File Component,简称 SFC,单文件组件)
.vue文件对js/css/html统一封装,这是VUE中的概念 该文件由三个部分组成 `<script> <template> <style>`
template标签 代表组件的html部分代码 代替传统的.html文件
script标签 代表组件的js代码 代替传统的.js文件
style标签 代表组件的css样式代码 代替传统的.css文件
(3)工程化vue项目
index.html是项目的入口,其中 `<div id ='app'></div>` 用于挂载所有组建的元素
index.html中的script标签引入了一个main.js文件,具体的挂载过程在main.js中执行
main.js是vue工程中非常重要的文件,他决定这项目使用哪些依赖、导入的第一个组件是什么
App.vue是vue中的核心组件,所有的其他组件都要通过该组件进行导入,该组件通过路由可以控制页面的切换
三、响应式数据和setup语法糖
(1)响应式数据
非响应式数据:修改后VUE不会更新DOM
响应式数据:修改后VUE会更新DOM
VUE2中数据默认是响应式的
VUE3中数据要经过ref或者reactive处理后才是响应式的
ref是VUE3框架提供的一个函数,需要导入(目前在VSCode中可以实现使用ref时自动在文件开头script标签中实现导入)
import {ref} from 'vue'
(ref相当于将counter转换为一个对象,其值代表此对象value属性的值{value:1})
ref处理的响应式数据在js编码修改的时候需要通过.value操作,但在绑定到html上时不需要。
数据变化时,vue框架会将变量更新后的值更新到dom树之中
<script type="module">
import {ref} from 'vue'
export default{
setup(){
//ref处理的响应式数据在js编码修改的时候需要通过.value操作
//ref响应式数据在绑定到html上时不需要.value
let counter = ref(1)
function increase(){
// 通过.value修改响应式数据
counter.value++
}
function decrease(){
counter.value--
}
return {
counter,
increase,
decrease
}
}
}
</script>
<template>
<div>
<button @click="decrease()">-</button>
{{ counter }}
<button @click="increase()">+</button>
</div>
响应式数据的处理方式:
①ref:适用于单个变量
②reactive式:更加适用于对象
<script setup>
import { reactive, ref } from 'vue';
let counter=ref(10)
function fun1(){
counter.value++
}
// 使用ref的方式 script标签中需要使用value调用,但是template中不需要。
let person=reactive({
name:"Qum",
age:18
})
// 使用reactive的方式 响应式数据,调用时与java调用属性基本相同,可以直接调用。
function agePlus(){
person.age++
}
</script>
<template>
<div>
<button @click="fun1">+</button>
<br>
<button @click="counter++">++</button>
{{ counter }}
<br>
<button @click="agePlus()">+</button>
<br>
<button @click="person.age++">++</button>
{{ person.age }}
</div>
</template>
<style scoped>
</style>
toRef函数
tiRefs
(2)setup语法糖
<script type="module" setup> 通过setup关键字
可以省略 export default {setup(){ return{}}}这些冗余的语法结构
四、Vue 的视图渲染
1、插值表达式
写法:
{{写入具体代码}}
插值表达式不依赖标签,没有标签也可以单独使用
插值表达式中可以调用函数,将函数的返回值渲染到指定的位置
插值表达式支持一些常见的运算符
<script setup type="module">
let msg ="hello vue3"
let getMsg= ()=>{
return 'hello vue3 message'
}
let age = 19
let bee = '蜜 蜂'
// 购物车
const carts = [{name:'beer',price:15,number:2},{name:'cake',price:16,number:1}];
//计算购物车总金额
function compute(){
let count = 0;
for(let index in carts){
count += carts[index].price*carts[index].number;
}
return count;
}
</script>
<template>
<div>
<h1>{{ msg }}</h1>
msg的值为: {{ msg }} <br>
getMsg返回的值为:{{ getMsg() }} <br>
是否成年: {{ age>=18?'true':'false' }} <br>
反转: {{ bee.split(' ').reverse().join('-') }} <br>
购物车总金额: {{ compute() }} <br/>
购物车总金额: {{carts[0].price*carts[0].number + carts[1].price*carts[1].number}} <br>
</div>
</template>
2、文本渲染命令
为了渲染标签中的文本,我们也可以选择使用`v-text`和`v-html`命令
(1) v-*** 这种写法的方式使用的是vue的命令
(2) v-***的命令必须依赖元素,并且要写在元素的开始标签中
(3) v-***指令支持ES6中的字符串模板
(4) 文本渲染中支持javascript的运算表达式
(5) 文本渲染中也支持函数的调用
(6) v-text可以将数据渲染成双标签中间的文本,但是不识别html元素结构的文本
(7) v-html可以将数据渲染成双标签中间的文本,识别html元素结构的文本
<script setup type="module">
let msg ='hello vue3'
let getMsg= ()=>{
return msg
}
let age = 19
let bee = '蜜 蜂'
let redMsg ='<font color=\'red\'>msg</font>'
let greenMsg =`<font color=\'green\'>${msg}</font>`
</script>
<template>
<div>
<span v-text='msg'></span> <br>
<span v-text='redMsg'></span> <br>
<span v-text='getMsg()'></span> <br>
<span v-text='age>18?"成年":"未成年"'></span> <br>
<span v-text='bee.split(" ").reverse().join("-")'></span> <br>
<span v-html='msg'></span> <br>
<span v-html='redMsg'></span> <br>
<span v-html='greenMsg'></span> <br>
<span v-html="`<font color='green'>${msg}</font>`"></span> <br>
</div>
</template>
3、属性渲染命令
由于插值表达式不能直接放在标签的属性中,所有要渲染元素的属性就应该使用v-bind
v-bind可以用于渲染任何元素的属性,语法为 v-bind:属性名='数据名', 可以简写为 :属性名='数据名'
<script setup>
const data = {
name:'尚硅谷',
url:"http://www.atguigu.com",
logo:"http://www.atguigu.com/images/index_new/logo.png"
}
</script>
<template>
<div>
<a
v-bind:href='data.url'
target="_self">
<img
:src="data.logo"
:title="data.name">
<!-- 此处省略了bind标签,只留下冒号。书写上存在方便,但阅读时可能会导致一些困难。 -->
<br>
<input type="button"
:value="`点击访问${data.name}`">
</a>
</div>
</template>
4、事件渲染命令
v-on:事件名称=“函数名()”
v-on:事件名可以简写为@事件名
事件的修饰符:once 时间只绑定一次
Prevent 修饰符阻止组件的默认值
我们可以使用 `v-on` 来监听 DOM 事件,并在事件触发时执行对应的 Vue的JavaScript代码。
用法:v-on:click="handler" 或简写为 @click="handler"
vue中的事件名=原生事件名去掉`on` 前缀 如:onClick --> click
handler的值可以是方法事件处理器,也可以是内联事件处理器
绑定事件时,可以通过一些绑定的修饰符,常见的事件修饰符如下
+ `.once:只触发一次事件。[重点]`
+ `.prevent:阻止默认事件。[重点]`
+ .stop:阻止事件冒泡。
+ .capture:使用事件捕获模式而不是冒泡模式。
+ .self:只在事件发送者自身触发时才触发事件。
<script setup>
import { ref } from 'vue';
function fun1(){
alert("hi")
}
let counter=ref(1)
function fun2(){
counter.value++
}
function fun3(event){
let flag=confirm("Are you sure to continue?")
if(!flag){
event.preventDefault()
}
}
function fun4(){
alert("be clicked!")
}
</script>
<template>
<div>
<!-- 事件的绑定函数 -->
<button v-on:click="fun1()">hello</button>
<!-- 内联事件处理器 可以在事件名中直接写入想要执行的函数的代码,依然生效。-->
<br>
<button v-on:click="counter++">+</button>
<!-- 例如,此行代码与下一行等效。 -->
<br>
<button @click="fun2()" >click here</button>
<br>
<button @click.once="fun2()" >click three</button>
<!-- 如果在事件名加上.once后缀,则只执行一次。 -->
{{ counter }}
<br>
<a href="http://www.atguigu.com" @click="fun3()"></a>
<!-- 原生js的阻止访问的方式 可以自主选择是否继续访问资源-->
<a href="http://www.atguigu.com" @click.prevent="fun4()"></a>
<!-- 直接进行对跳转链接的阻止 -->
</div>
</template>
<style scoped>
</style>
5、条件渲染
(1)v-if和v-else
v-if='表达式' 只有在指令的表达式返回true时才成功渲染,可以在页面中进行展示。
可以同时使用 v-else 为 `v-if` 添加一个“else 区块”。当v-if不显示的时候,显示v-else中的内容。
一个 v-else 元素必须跟在一个 v-if元素后面,否则将不会被识别,也就是说,v-else依赖于v-if 必须和v-if 成对出现。
<script setup>
import { reactive, ref } from 'vue';
let flag=ref(true)
</script>
<template>
<div>
<h1 v-if="flag"> miss miss</h1>
<h1 v-else>oh yeah</h1>
<!-- 当v-if绑定的内容不展示的时候,才展示v-else中的内容。 -->
<h1 v-show="flag">芜湖</h1>
<!-- v-show基本和v-if一样,表达式的值为true时进行展示。 -->
<button @click="flag=!flag">toggle</button>
<!-- 通过这种方法可以实现改变表达式的值。 -->
</div>
</template>
<style scoped>
</style>
(2)v-show
v-show基本和v-if一样,表达式的值为true时进行展示。
v-if和v-show的辨析:
v-if v-show
v-if 是“真实的”按条件渲染,因为它确保了在切换时,条件区块内的事件监听器和子组件都会被销毁与重建。
v-if 也是惰性的:如果在初次渲染时条件值为 false,则不会做任何事。条件区块只有当条件首次变为 true 时才被渲染。
相比之下,`v-show` 简单许多,元素无论初始条件如何,始终会被渲染,只有 CSS `display` 属性会被切换。
+ 总的来说,`v-if` 有更高的切换开销,而 `v-show` 有更高的初始渲染开销。因此,如果需要频繁切换,则使用 `v-show` 较好;如果在运行时绑定条件很少改变,则 `v-if` 会更合适。
6、列表渲染
我们可以使用 `v-for` 指令基于一个数组来渲染一个列表。
`v-for` 指令的值需要使用 `item in items` 形式的特殊语法,其中 `items` 是源数据的数组,而 `item` 是迭代项的别名。
在 `v-for` 块中可以完整地访问父作用域内的属性和变量。`v-for` 也支持使用可选的第二个参数表示当前项的位置索引。
<script type="module" setup>
import {ref,reactive} from 'vue'
let parentMessage= ref('产品')
let items =reactive([
{
id:'item1',
message:"薯片"
},
{
id:'item2',
message:"可乐"
}
])
</script>
<template>
<div>
<ul>
<!-- :key不写也可以,此处省略的是v-bind的属性渲染 -->
<li v-for='item in items' :key='item.id'>
{{ item.message }}
</li>
</ul>
<!-- 此处代码呈现无序排列的具体数组内容。 -->
<ul>
<!-- index表示索引,取名上不做强制要求-->
<li v-for="(item, index) in items" :key="index">
{{ parentMessage }} - {{ index }} - {{ item.message }}
</li>
</ul>
</div>
</template>
<style scoped>
</style>
7、双向绑定
单向绑定和双向绑定:
(1)单向绑定:响应式数据的变化会更新dom树,但是dom树上用户的操作造成的数据改变不会同步更新到响应式数据
(2)双向绑定: 响应式数据的变化会更新dom树,dom树上用户的操作造成的数据改变也会同步更新到响应式数据
+ 用户通过表单标签才能够输入数据,所以双向绑定都是应用到表单标签上的,其他标签不行
+ v-model专门用于双向绑定表单标签的value属性,语法为 `v-model:value=''`,可以简写为 v-model=""
+ v-model还可以用于各种不同类型的输入,`<textarea>`、`<select>` 元素。
v-model=""
<script type="module" setup>
import { reactive, ref, toValue } from 'vue';
let message=ref("zhangsan")
let user=reactive({name:"Qum",pwd:"123456",intro:""})
//创建一个对象user
let hbs=ref([])//需要绑定的value属性
//一个清空数据的方法:
function clearx(){
//user = {};// 这种写法会将数据变成非响应的,应该是user.name=""
user.name=''
user.pwd=''
user.intro=''
hbs.value.splice(0,hbs.value.length);;
}
</script>
<template>
<div>
<input type="text" placeholder="please input your username!" v-model="user.name"><br>
hobby:
sleep <input type="checkbox" v-model="hbs" value="break">
stay with him <input type="checkbox" v-model="hbs" value="love">
<br>
<textarea v-model="user.intro"></textarea>
{{ user }}
{{ hbs }}
<button @click="clearx()">重置</button>
</div>
</template>
<style scoped>
</style>
8、计算属性
<script type="module" setup>
import { reactive, ref, toValue } from 'vue';
const author=reactive({name:"Qum",books:["不逢君","与君伤别离"]})
function hasBooks(){
console.log("function")
return author.books.length>0?'yes':'no'
}
// 调用一次,执行一次
let bookMessage=computed(()=>{
console.log("variable")
return author.books.length>0?'yes':'no'
})//每次执行时,如果和上一次的数据没有差别,若无改变,直接沿用上一次的值
</script>
<template>
<div>
<p>
author:{{ author.name }}
</p>
Did publicize books:{{ hasBooks() }}
<br>
Did publicize books:{{ bookMessage }}
</div>
</template>
<style scoped>
</style>
9、数据监听器
watch主要用于以下场景:
当数据发生变化时需要执行相应的操作
监听数据变化,当满足一定条件时触发相应操作
在异步操作前或操作后需要执行相应的操作
> 监控响应式数据(watch):
<script type="module" setup>
//引入模块
import { ref,reactive,watch} from 'vue'
let firstname=ref('')
let lastname=reactive({name:''})
let fullname=ref('')
//监听一个ref响应式数据
格式:watch(需要监听的ref数据,根据监听作出响应的处理函数)
watch(firstname,(newValue,oldValue)=>{
console.log(`${oldValue}变为${newValue}`)
fullname.value=firstname.value+lastname.name
})
//监听reactive响应式数据的指定属性
格式:watch(函数返回要监听的响应式数据,处理函数)
watch(()=>lastname.name,(newValue,oldValue)=>{
console.log(`${oldValue}变为${newValue}`)
fullname.value=firstname.value+lastname.name
})
//监听reactive响应式数据的所有属性(深度监视,一般不推荐)
//deep:true 深度监视
//immediate:true 深度监视在进入页面时立即执行一次
watch(()=>lastname,(newValue,oldValue)=>{
// 此时的newValue和oldValue一样,都是lastname
console.log(newValue)
console.log(oldValue)
fullname.value=firstname.value+lastname.name
},{deep:true,immediate:false})
</script>
<template>
<div>
全名:{{fullname}} <br>
姓氏:<input type="text" v-model="firstname"> <br>
名字:<input type="text" v-model="lastname.name" > <br>
</div>
</template>
<style scoped>
</style>
```
监控响应式数据(watchEffect):
watchEffect默认监听所有的响应式数据
<script type="module" setup>
//引入模块
import { ref,reactive,watch, watchEffect} from 'vue'
let firstname=ref('')
let lastname=reactive({name:''})
let fullname=ref('')
//监听所有响应式数据
watchEffect(()=>{
//直接在内部使用监听属性即可,不用外部声明
//也不需要即时回调设置,默认初始化就加载
console.log(firstname.value)
console.log(lastname.name)
fullname.value=`${firstname.value}${lastname.name}`
})
</script>
<template>
<div>
全名:{{fullname}} <br>
姓氏:<input type="text" v-model="firstname"> <br>
名字:<input type="text" v-model="lastname.name" > <br>
</div>
</template>
<style scoped>
</style>
watch和watchEffect的对比:
> `watch` vs. `watchEffect`
+ `watch` 和 `watchEffect` 都能响应式地执行有副作用的回调。它们之间的主要区别是追踪响应式依赖的方式:
watch 只追踪明确侦听的数据源。它不会追踪任何在回调中访问到的东西。另外,仅在数据源确实改变时才会触发回调。`watch` 会避免在发生副作用时追踪依赖,因此,我们能更加精确地控制回调函数的触发时机。
watchEffect,则会在副作用发生期间追踪依赖。它会在同步执行过程中,自动追踪所有能访问到的响应式属性。这更方便,而且代码往往更简洁,但有时其响应性依赖关系会不那么明确。
watchEffect函数
在watchEffect函数中,如果想监听数据,直接调用即可,无需将要监听的响应式数据传入参数。
在watchEffect函数中调用后的响应式数据才会被监视,否则不会被监视。
在watchEffect函数中,所有被调用的响应式数据指的都是newValue,不能使用oldValue。
10、生命周期
> 每个 Vue 组件实例在创建时都需要经历一系列的初始化步骤,比如设置好数据侦听,编译模板,挂载实例到 DOM,以及在数据改变时更新 DOM。在此过程中,它也会运行被称为`生命周期钩子的函数`,让开发者有机会在特定阶段运行自己的代码!
常见钩子函数
+ onBeforeMount() 注册一个钩子,在组件被挂载之前被调用。
+ onMounted() 注册一个回调函数,在组件挂载完成后执行。
+ onBeforeUpdate() 注册一个钩子,在组件即将因为响应式状态变更而更新其 DOM 树之前调用。
+ onUpdated() 注册一个回调函数,在组件因为响应式状态变更而更新其 DOM 树之后调用。
+ onBeforeUnmount() 注册一个钩子,在组件实例被卸载之前调用。
+ onUnmounted() 注册一个回调函数,在组件实例被卸载之后调用。
先执行 onBeforeMount() 和onMounted() 实现挂载。
11、组件
(1)组件概述
组件允许我们将 UI 划分为独立的、可重用的部分,并且可以对每个部分进行单独的思考。组件就是实现应用中局部功能代码和资源的集合!在实际应用中,组件常常被组织成层层嵌套的树状结构:
这和我们嵌套 HTML 元素的方式类似,Vue 实现了自己的组件模型,使我们可以在每个组件内封装自定义内容与逻辑。
组件化:对js/css/html统一封装,这是VUE中的概念
模块化:对js的统一封装,这是ES6中的概念
组件化中,对js部分代码的处理使用ES6中的模块化
(2)使用组件的代码案例
三个组件文件:
Header.vue
<script setup type="module">
</script>
<template>
<div>
欢迎: xx <a href="#">退出登录</a>
</div>
</template>
<style>
</style>
Navigator.vue
<script setup type="module">
</script>
<template>
<!-- 推荐写一个根标签-->
<div>
<ul>
<li>学员管理</li>
<li>图书管理</li>
<li>请假管理</li>
<li>考试管理</li>
<li>讲师管理</li>
</ul>
</div>
</template>
<style>
</style>
Content.vue
<script setup type="module">
</script>
<template>
<div>
show principal conment
</div>
</template>
<style>
</style>
使用方法:在App.vue中引入已经写好的组件。
App.vue
<script setup>
import Header from './components/Header.vue'
import Navigator from './components/Navigator.vue'
import Content from './components/Content.vue'
</script>
<template>
<div>
<Header class="header"></Header>
<Navigator class="navigator"></Navigator>
<Content class="content"></Content>
</div>
</template>
<style scoped>
.header{
height: 80px;
border: 1px solid red;
}
.navigator{
width: 15%;
height: 800px;
display: inline-block;
border: 1px blue solid;
float: left;
}
.content{
width: 83%;
height: 800px;
display: inline-block;
border: 1px goldenrod solid;
float: right;
}
</style>
(3)组件的传参
①父传子
Vue3 中父组件向子组件传值可以通过 props 进行,具体操作如下:
1. 首先,在父组件中定义需要传递给子组件的值。
接着,在父组件的模板中引入子组件,同时在引入子组件的标签中添加 props 属性并为其设置需要传递的值。
2. 在 Vue3 中,父组件通过 props 传递给子组件的值是响应式的。也就是说,如果在父组件中的传递的值发生了改变,子组件中的值也会相应地更新。
父组件代码:App.vue
导入子组件。在template标签中,使用子组件标签,并且使用属性渲染传递数据。
在script标签中,可以实现对传递数据的修改。
<script setup>
import Son from './components/Son.vue'
import {ref,reactive,toRefs} from 'vue'
let message = ref('parent data!')
let title = ref(42)
function changeMessage(){
message.value = '修改数据!'
title.value++
}
</script>
<template>
<div>
<h2>{{ message }}</h2>
<hr>
<!-- 使用子组件,并且传递数据! -->
<Son :message="message" :title="title"></Son>
<hr>
<button @click="changeMessage">点击更新</button>
</div>
</template>
<style scoped>
</style>
子组件代码:Son.vue
从vue中导入defineProps,并且在defineProps中声明父组件传递参数的属性类型。
<script setup type="module">
import {ref,isRef,defineProps} from 'vue'
//声明父组件传递属性值
defineProps({
message:String ,
title:Number
})
</script>
<template>
<div>
<div>{{ message }}</div>
<div>{{ title }}</div>
</div>
</template>
<style>
</style>
②子传父
父组件中导入子组件,自定义函数接收子组件中传输的数据。使用事件渲染绑定自定义函数,实现参数的接收。
父组件: App.vue
<script setup>
import Son from './components/Son.vue'
import {ref} from 'vue'
let pdata = ref('')
const padd = (data) => {
console.log('2222');
pdata.value =data;
}
//自定义接收,子组件传递数据方法! 参数为数据!
const psub = (data) => {
console.log('11111');
pdata.value = data;
}
</script>
<template>
<div>
<!-- 声明@事件名应该等于子模块对应事件名!调用方法可以是当前自定义!-->
<Son @add="padd" @sub="psub"></Son>
<hr>
{{ pdata }}
</div>
</template>
<style>
</style>
子组件:Son.vue
子组件从vue包中导入defineEmits,并且通过defineEmits的方法,创建emites实例,函数内调用对应属性,并将函数绑定在temp标签中的的button标签内。
<script setup>
import {ref,defineEmits} from 'vue'
//1.定义要发送给父组件的方法,可以1或者多个
let emites = defineEmits(['add','sub']);
let data = ref(1);
function sendMsgToParent(){
console.log('-------son--------');
//2.触发父组件对应的方法,调用defineEmites对应的属性
emites('add','add data!'+data.value)
emites('sub','sub data!'+data.value)
data.value ++;
}
</script>
<template>
<div>
<button @click="sendMsgToParent">发送消息给父组件</button>
</div>
</template>