Vue3.0基础语法
Vue3优点介绍:
- 最火框架,它是国内最火的前端框架之一,官方文档 (opens new window)中文文档(opens new window)
- 性能提升,运行速度事vue2.x的1.5倍左右
- 体积更小,按需编译体积比vue2.x要更小
- 类型推断,更好的支持Ts(typescript)这个也是趋势
- 高级给予,暴露了更底层的API和提供更先进的内置组件
- ★组合API (composition api) ,能够更好的组织逻辑,封装逻辑,复用逻辑
1. vite基本使用vite是什么:官方文档(opens new window)
- 它是一个更加轻量(热更新速度快,打包构建速度快)的vue项目脚手架工具。
- 相对于vue-cli它默认安装的插件非常少,随着开发过程依赖增多,需要自己额外配置。
使用vite创建一个vue应用:
// (1) 创建项目
npm init vite-app 项目名称 or yarn create vite-app 项目名称
// (2) 安装依赖
npm install or yarn
// (3) 启动项目
npm run dev or yarn dev
2. setup函数
使用细节:
setup是一个新的组件选项,作为组件中使用组合API的起点。- 从组件生命周期来看,它的执行在组件实例创建之前
vue2.x的beforeCreate执行。 - 这就意味着在
setup函数中this还不是组件实例,this此时是undefined - 在模版中需要使用的数据和函数,需要在
setup返回。
<template>
<div class="container">
<span>{{msg}}</span>
<button @click="handle">按钮</button>
</div>
</template>
<script>
export default {
setup () {
console.log(this) // 输出:undefined
// 数据
const msg = 'hi vue3.0'
// 函数
const handle = () => {
console.log('hi lazh-zheng')
}
return {
msg,
say
}
}
}
</script>
3. 生命周期
(1)vue2.x生命周期钩子
- beforeCreate
- created
- beforeMount
- mounted
- beforeUpdate
- updated
- beforeDestroy
- destroyed
(2)vue3.0生命周期钩子
setup创建实例前onBeforeMount挂载DOM前onMounted挂载DOM后onBeforeUpdate更新组件前onUpdated更新组件后onBeforeUnmount卸载销毁前onUnmounted卸载销毁后 使用:
<script>
import { onBeforeMount, onMounted, onBeforeUpdate, onUpdated, onBeforeUnmount, onUnmounted } from 'vue'
export default {
setup () {
// 1.dom渲染前钩子
onBeforeMount(() => {
console.log('1.dom渲染前的钩子,获取dom返回undefined')
})
// 2.dom渲染后的钩子
onMounted(() => {
console.log('2.dom渲染后的钩子,可以回去到模板中的dom元素')
})
// Vue3.0中可以定义多个相同的生命周期,执行顺序按照出现的前后执行,可以定义多个生命周期,实现不同的逻辑
onMounted(() => {
console.log('mounted222')
})
// 3.组件跟新前的钩子
onBeforeUpdate(() => {
console.log('3.组件跟新前的钩子')
})
// 4. 组件跟新后的钩子
onUpdated(() => {
console.log('4.组件跟新后的钩子')
})
// 5. 组件销毁前的钩子
onBeforeUnmount(() => {
console.log('5.组件销毁前的钩子')
})
// 6. 组件销毁后的钩子
onUnmounted(() => {
console.log('6.组件销毁后的钩子')
})
}
}
</script>
4. reactive函数
reactive函数用来定义响应式数据
reactive是一个函数,它可以定义一个复杂数据类型,成为响应式数据。 使用细节:
<template>
<div class="container">
<ul>
<li v-for="(item,key,index) in obj" :key="index">
{{item}}
</li>
</ul>
<button @click="updateName">点击</button>
</div>
</template>
<script>
import { reactive } from 'vue'
export default {
setup () {
// 普通数据 这样定义的数据 修改数据 视图不会更新
// const obj = {
// name: 'lazh-zheng',
// age: 19
// }
// 响应式数据
// reactive用来定义响应式对象数据,通过 reactive 定义的对象数据,修改数据视图也会发生变化
const obj = reactive({
name: 'lazh-zheng',
age: 19
})
// 修改名字
const updateName = () => {
if (obj.age === 19) {
obj.name = '小花'
obj.age = 18
} else {
obj.name = '小明'
obj.age = 19
}
}
return {
obj,
updateName
}
}
}
</script>
// 总结: 通常是用来定义响应式**对象数据**
5. toRef函数
定义响应式数据:
- toRef是函数,转换
响应式对象中某个属性为单独响应式数据,并且值是关联的。 使用细节:
<template>
<div class="container">
<span> {{name}}</span>
<button @click="updateName">修改数据</button>
</div>
</template>
<script>
// 场景:有一个响应式对象数据,但是模版中只需要使用其中一项数据。
import { reactive, toRef } from 'vue'
export default {
setup () {
// 1. 响应式数据对象
const obj = reactive({
name: 'ls',
age: 10
})
console.log(obj)
// 2. 模板中只需要使用name数据
// 注意:从响应式数据对象中解构出的属性数据,不再是响应式数据
// let { name } = obj 不能直接解构,出来的是一个普通数据
const name = toRef(obj, 'name')
// console.log(name)
const updateName = () => {
// toRef转换响应式数据包装成对象,value存放值的位置
name.value = 'zs'
}
return {name, updateName}
}
}
</script>
6. toRefs函数
定义响应式数据:
- toRefs是函数,转换
响应式对象中所有属性为单独响应式数据,对象成为普通对象,并且值是关联的使用细节:
<template>
<div class="container">
<div>{{name}}</div>
<div>{{age}}</div>
<button @click="updateName">修改数据</button>
</div>
</template>
<script>
import { reactive, toRef, toRefs } from 'vue'
export default {
setup () {
// 1. 响应式数据对象
const obj = reactive({
name: '张三',
age: 10
})
console.log(obj)
// 2. 解构或者展开响应式数据对象
// const {name,age} = obj
// console.log(name,age)
// const obj2 = {...obj}
// console.log(obj2)
// 以上方式导致数据就不是响应式数据了
const obj3 = toRefs(obj)
const updateName = () => {
// obj3.name.value = '李四'
obj.name = '李四'
}
return {...obj3, updateName}
或
return {...toRefs(obj), updateName}
}
}
</script>
7.ref函数
定义响应式数据:
ref函数,常用于简单数据类型定义为响应式数据- 再修改值,获取值的时候,需要
.value - 在模板中使用ref申明的响应式数据,可以省略.value,因为模板编译的时候会通过
__v_isRef这个值判断是否是响应式数据
使用细节:
<template>
<div class="container">
<div>{{name}}</div>
<div>{{age}}</div>
<button @click="updateName">修改数据</button>
</div>
</template>
<script>
import { ref } from 'vue'
export default {
setup () {
// 1. name数据
const name = ref('张三')
console.log(name)
const updateName = () => {
name.value = '李四'
}
// 2. age数据
const age = ref(10)
// ref常用定义简单数据类型的响应式数据
// 也可以定义复杂数据类型的响应式数据
// 对于数据未之的情况下 ref 是最适用的
// 模拟请求接口
// const data = ref(null)
// setTimeout(()=>{
// data.value = res.data
// },1000)
return {name, age, updateName}
}
}
</script>
- 当你明确知道需要的是一个响应式数据
对象那么就使用reactive即可 - 其他情况直接使用
ref通过ref获取模板中的dom元素:
<template>
<div class="container">
<!-- 单个元素 -->
<div ref="dom">我是box</div>
<!-- 被遍历的元素 -->
<ul>
<li v-for="i in 6" :key="i" :ref="setDom">第{{i}}LI</li>
</ul>
</div>
</template>
<script>
import { onMounted, onBeforeUpdate, ref } from 'vue'
export default {
name: 'App',
setup () {
// 1. 获取单个元素
// 1.1 先定义一个空的响应式数据ref定义的
// 1.2 setup中返回该数据,你想获取那个dom元素,在该元素上使用ref属性绑定该数据即可。
const dom = ref(null)
onMounted(()=>{
console.log(dom.value) // 输出获取到的div元素
})
// 2. 获取v-for遍历的元素
// 2.1 定义一个空数组,接收所有的LI
// 2.2 定义一个函数,往空数组push DOM
const domList = []
const setDom = (el) => {
domList.push(el)
}
onMounted(()=>{
console.log(domList)
})
// 组件更新的时候会重复的设置dom元素给数组
// ref获取v-for遍历的DOM元素,需要在组件更新的时候重置接受dom元素的数组。
onBeforeUpdate(()=>{
domList = []
})
return {dom, setDom}
}
}
</script>
8. computed函数
一般情况下默认为计算属性不能修改
- computed传入函数,返回值就是计算属性的值
- computed传入对象,get获取计算属性的值,set监听计算属性改变。
<template>
<div class="container">
<div>今年:{{age}}岁</div>
<div>后年:{{newAge}}岁</div>
<!-- 使用v-model绑定计算属性 -->
<input type="text" v-model="newAge">
</div>
</template>
<script>
import { computed, ref } from 'vue'
export default {
setup () {
// 1. 计算属性:当你需要依赖现有的响应式数据,根据一定逻辑得到一个新的数据。
const age = ref(16)
// 得到后年的年龄
// const newAge = computed(()=>{
// // 该函数的返回值就是计算属性的值
// return age.value + 2
// })
// 计算属性高级用法,传人对象
// 目的:计算属性支持v-model
const newAge = computed({
// get函数,获取计算属性的值
get(){
return age.value + 2
},
// set函数,当你给计算属性设置值的时候触发
set (value) {
age.value = value - 2
}
})
return {age, newAge}
}
}
</script>
9. watch函数
<template>
<div class="container">
<div>
<p>count的值:{{count}}</p>
<button @click="add">改数据</button>
</div>
<hr>
<div>
<p>{{obj.name}}</p>
<p>{{obj.age}}</p>
<p>{{obj.brand.name}}</p>
<button @click="updateName">改名字</button>
<button @click="updateBrandName">改品牌名字</button>
</div>
</div>
</template>
<script>
import { reactive, ref, watch } from 'vue'
export default {
setup () {
const count = ref(0)
const add = () => {
count.value++
}
// 当你需要监听数据的变化就可以使用watch
// 1. 监听一个ref数据
// 1.1 第一个参数 需要监听的目标
// 1.2 第二个参数 改变后触发的函数
// watch(count, (newVal,oldVal)=>{
// console.log(newVal,oldVal)
// })
const obj = reactive({
name: 'ls',
age: 10,
brand: {
id: 1,
name: '宝马'
}
})
const updateName = () => {
obj.name = 'zs'
}
const updateBrandName = () => {
obj.brand.name = '奔驰'
}
// 2. 监听一个reactive数据
watch(obj, ()=>{
console.log('数据改变了')
})
watch(()=>obj.brand, ()=>{
console.log('brand数据改变了')
},{
// 5. 需要深度监听
deep: true,
// 6. 想默认触发
immediate: true
})
// 3. 监听多个数据的变化
// watch([count, obj], ()=>{
// console.log('监听多个数据改变了')
// })
// 4. 此时监听对象中某一个属性的变化 例如:obj.name
// 需要写成函数返回该属性的方式才能监听到
// watch(()=>obj.name,()=>{
// console.log('监听obj.name改变了')
// })
return {count, add, obj, updateName, updateBrandName}
}
}
</script>
10. 父子组件通讯
<template>
<div class="container">
<h1>父组件</h1>
<p>{{money}}</p>
<hr>
<Son :money="money" @changeMoney="updateMoney" />
// 下面两行代码相等
<Son :money="money" @update:money="updateMoney" />
<Son v-model:money="money" />
</div>
</template>
<script>
import { ref } from 'vue'
import Son from './Son.vue'
export default {
components: {
Son
},
// 父组件的数据传递给子组件
setup () {
const money = ref(100)
// 接受子组件传递的数据
const updateMoney = (newMoney) => {
money.value = money.value - newMoney
}
return { money ,changeMoney }
}
}
</script>
<template>
<div class="container">
<h1>子组件</h1>
<p>{{money}}</p>
</div>
</template>
<script>
import { onMounted } from 'vue'
export default {
name: 'Son',
// 子组件接收父组件数据使用props即可
props: {
money: {
type: Number,
default: 0
}
},
setup (props,context) {
// 获取父组件数据money
console.log(props.money)
// 子组件向父组件传递数据
const changeMoney => {
context.$emit('changeMoney',50)
// 也可以使用 vue2.0中 .sync 修饰符
context.$emit('update:money',50)
}
return { changeMoney }
}
}
</script>
总结:
- 父传子:在setup种使用props数据
setup(props){ // props就是父组件数据 } - 子传父:触发自定义事件的时候emit来自
setup(props,{emit}){ // emit 就是触发事件函数 } - 在vue3.0中
v-model和.sync已经合并成v-model指令
11. 依赖注入
使用场景:有一个父组件,有子组件,有孙组件,有很多后代组件,共享父组件数据。
使用细节:
<template>
<div class="container">
<h1>父组件 {{money}} </h1>
<button @click="money=1000">发钱</button>
<hr>
<Son />
</div>
</template>
<script>
import { provide, ref } from 'vue'
import Son from './Son.vue'
export default {
components: {
Son
},
setup () {
const money = ref(100)
const changeMoney = (saleMoney) => {
money.value = money.value - saleMoney
}
// 将数据提供给后代组件 provide
provide('money', money)
// 将函数提供给后代组件 provide
provide('changeMoney', changeMoney)
return { money }
}
}
</script>
接受祖先组件传递的值
<template>
<div class="container">
<h2>子组件 {{money}}</h2>
<button @click="fn">消费20</button>
</div>
</template>
<script>
import { inject } from 'vue'
export default {
setup () {
// 接收祖先组件提供的数据
const money = inject('money')
// 子孙组件,消费20,通知父组件,进行修改
// 虽然可以修改,但是不建议修改祖先组件的数据,遵循单选数据流原则
const changeMoney = inject('changeMoney')
const fn = () => {
changeMoney(20)
}
return {money, fn}
}
}
</script>
总结:
- provide函数提供数据和函数给后代组件使用
- inject函数给当前组件注入provide提供的数据和函数
12. v-model语法糖
在vue2.0中v-mode语法糖简写的代码 <Son :value="msg" @input="msg=$event" />
在vue3.0中v-model语法糖有所调整:<Son :modelValue="msg" @update:modelValue="msg=$event" />
<template>
<div class="container">
<!-- 自定义事件 -->
<!-- <Son :modelValue="count" @update:modelValue="count=$event" /> -->
<Son v-model="count" />
</div>
</template>
<script>
import { ref } from 'vue'
import Son from './Son.vue'
export default {
name: 'App',
components: {
Son
},
setup () {
const count = ref(10)
return { count }
}
}
</script>
<template>
<div class="container">
<h2>子组件 {{modelValue}} <button @click="fn">改变数据</button></h2>
</div>
</template>
<script>
export default {
name: 'Son',
props: {
modelValue: {
type: Number,
default: 0
}
},
setup (props, {emit}) {
const fn = () => {
// 改变数据
emit('update:modelValue', 100)
}
return { fn }
}
}
</script>
总结: (1) vue3.0封装组件支持v-model的时候,父传子:modelValue 子传父 @update:modelValue
(2)vue2.0的 xxx.sync 语法糖解析 父传子 :xxx 子传父 @update:xxx 在vue3.0 使用 v-model:xxx 代替。
13. mixins混入
// mixins 配置对象
export const followMixin = {
data () {
return {
loading: false
}
},
methods: {
followFn () {
this.loading = true
// 模拟请求
setTimeout(()=>{
this.loading = false
},2000)
}
}
}
<template>
<div class="container1">
<h1> 作者:小小正 <a href="javascript:;" @click="followFn">{{loading?'请求中...':'关注'}}</a> </h1>
<hr>
<Son />
</div>
</template>
<script>
import Son from './Son.vue'
import {followMixin} from './mixins'
export default {
components: {
Son
},
// 混入
mixins: [followMixin]
}
</script>
总结: 在vue2.0中一些可复用的逻辑可以使用mixins来封装,当是需要考虑逻辑代码冲突问题。vue3.0的组合API很好的解决了这个问题,就不在推荐使用mixins了。