Vue3
1.创建Vue3工程
1.使用vue-cli创建
# 1.查看版本,保证版本大于4.5.0
vue --version
# 安装或者升级
npm install -g @vue/cli
# 创建
vue create vue_test
# 启动
cd vue_test
npm run serve
2.使用vite创建
1.什么是vite
是一种新型的前端构建工具
优势:
- 开发环境中,无需打包操作,可快速冷启动。
- 轻量快速的热重载。
- 真的的按需编译,不再等待整个应用编译完成。
2.创建工程
# 创建工程
npm init vite-app <project-name>
# 进入
cd <project-name>
# 安装依赖
npm install
# 运行
npm run dev
3.工程结构
1.main.js
new Vue() 不再使用
//引入的不再是Vue构造函数了,引入的是一个名为createApp的工厂函数
import { createApp } from 'vue'
import App from "./App.vue'
// 创建应用实例对象 - createApp(App) 比 new Vue() 更轻
createApp(App).mount('#app')
2.常用组合API
1.setup函数
vue3中的新的配置项,值为一个函数。组件中所用到的数据、方法等,均配置在setup中。
返回值:
- 返回一个对象,则对象的属性,方法,在模板中均可直接使用。
- 返回一个渲染函数,则可以自定义渲染的内容。(了解)
注意:setup不能是一个async函数,因为async函数返回值不再是return的对象,而是promise,模板看不到return对象中的属性。
<template>
<h1>我是APP组件</h1>
<h2>姓名:{{ name }}</h2>
<h2>年龄:{{ age }}</h2>
<button @click="sayHello">说话</button>
</template>
<script>
// h 为渲染函数
import { h } from 'vue'
export default {
name: 'App',
setup(){
let name = '张三'
let age = 18
function sayHello(){
alert(`我叫${name},我${age}岁了,你好啊!`)
}
// return {
// name,
// age,
// sayHello
// }
//返回渲染函数,不管前面的内容,只渲染该函数的内容
return ()=>h('h1', 'AnAn')
}
}
</script>
注意点:
-
setup执行的时机:在beforeCreate之前执行一次,this是undefined。
-
setup的参数
-
props:值为对象,包含组件外部传递过来,且内部声明接收的属性。
-
context:上下文对象
- attrs:值为对象,包含组件外部传递过略,但没有声明接受的属性。相当于this.$attrs
- slots:收到的插槽内容。相当于this.$slots
- emit:分发自定义事件的函数。相当于this.$emit
-
<template>
<Demo @hello="showMsg" name="zc" age="18">demo 组件</Demo>
</template>
<script>
import { reactive } from 'vue'
import Demo from './components/Demo.vue';
export default {
name: 'App',
components: {Demo},
setup(){
function showMsg(value){
alert(`触发了hello事件,收到的参数是${value}`)
}
return {
showMsg
}
}
}
</script>
<template>
<h1>姓名:{{ person.name }}</h1>
<h1>年龄:{{ person.age }}</h1>
<button @click="test">测试hello事件</button>
</template>
<script>
import { reactive } from 'vue';
export default {
name: 'Demo',
props: ['name', 'age'],
emits: ['hello'],
setup(props, context){
console.log(props)
let person = reactive({
name: props.name,
age: props.age
})
function test(){
context.emit('hello', 666)
}
return {
person, test
}
}
}
</script>
2.ref函数
定义一个响应式的数据。
let name = '张三'定义的变量并不是响应式的变量,应使用let name = ref('张三'),值存储在ref对象的属性value中,使用get set进行获取设置,修改变量时,需要修改变量的属性value。
- 创建的是包含响应式数据的引用对象。ref对象。
- JS中操作数据,xxx.value
- 模板中读取数据不需要.value
注意:
- ref()接受的数据可以是:基本类型,也可以是对象类型/
- 基本类型的数据:响应式依然是靠Object.defineProperty()的get 与 set 完成的
- 对象类型的数据:内部是Vue3中的新函数,reactive函数,返回一个proxy对象。
<template>
<h1>我是APP组件</h1>
<h2>姓名:{{ name }}</h2>
<h2>年龄:{{ age }}</h2>
<h2>工作类型:{{ job.type }}</h2>
<h2>工作薪水:{{ job.salary }}</h2>
<button @click="changeInfo">修改信息</button>
</template>
<script>
import { ref } from 'vue'
export default {
name: 'App',
setup(){
let name = ref('张三')
let age = ref(18)
let job = ref({
type: '前端工程师',
salary: '30k'
})
function changeInfo(){
name.value = '李四'
age.value = 48
job.value.type = '后端工程师'
job.value.salary = '50k'
}
return {
name, age, job, changeInfo,
}
}
}
</script>
3.reactive函数
定义一个对象类型的响应式数据,不能使用基本类型。
const 代理对象 = reactive(源对象) 接收一个对象或数组,返回一个proxy对象。
reactive 定义的响应式数据是深层次的,基于ES6的Proxy实现,通过代理对象操作元对象内部数据,不需要.value。
<template>
<h1>我是APP组件</h1>
<h2>姓名:{{ name }}</h2>
<h2>年龄:{{ age }}</h2>
<h2>工作类型:{{ job.type }}</h2>
<h2>工作薪水:{{ job.salary }}</h2>
<h2>爱好:{{ hobby }}</h2>
<h2>b: {{ job.a.b }}</h2>
<button @click="changeInfo">修改信息</button>
</template>
<script>
import { ref,reactive } from 'vue'
export default {
name: 'App',
setup(){
let name = ref('张三')
let age = ref(18)
let job = reactive({
type: '前端工程师',
salary: '30k',
a:{
b: 666
}
})
let hobby = reactive(['抽烟','喝酒','烫头'])
function changeInfo(){
name.value = '李四'
age.value = 48
job.type = '后端工程师'
job.salary = '50k'
hobby[0] = '打游戏'
}
return {
name, age, job, changeInfo, hobby,
}
}
}
</script>
<template>
<h1>我是APP组件</h1>
<h2>姓名:{{ person.name }}</h2>
<h2>年龄:{{ person.age }}</h2>
<h2>工作类型:{{ person.job.type }}</h2>
<h2>工作薪水:{{ person.job.salary }}</h2>
<h2>爱好:{{ person.hobby }}</h2>
<button @click="changeInfo">修改信息</button>
</template>
<script>
import { reactive } from 'vue'
export default {
name: 'App',
setup(){
let person = reactive({
name: 'zc',
age: 18,
job: {
type: 'front-end',
salary: '50k'
},
hobby: ['','','']
})
function changeInfo(){
}
return {
person, changeInfo
}
}
}
</script>
4.Vue3响应式原理
vue2实现原理:
-
对象类型:通过
Object.defineProperty()对属性的读取、修改进行拦截(数据劫持) -
数组类型:通过重写更新数组的一系列方法来实现拦截。(对数组变更方法进行了包裹)
vue2中需要使用Vue.set进行修改.
-
存在的问题
- 新增属性、删除属性、界面不更新
- 直接通过下标修改数组,界面不更新。
Vue3实现原理:
- 通过Proxy代理,拦截对象中任意属性的变化。
- 通过Reflect反射,对被代理对象的属性进行操作。
5.reactive对比ref
- 定义数据角度:ref定义基本类型,reactive定义对象或数组
- 原理角度:ref通过
Object.defineProperty()实现响应式(数据劫持),reactive 通过Proxy实现响应式,通过Reflect操作源对象内部数据。 - 使用角度:ref操作数据需要.value,读取数据时模板中不需要。reactive都不需要.value。
6.计算属性与监视
1.computed函数
使用已有属性,计算新属性。
<template>
姓:<input type="text" v-model="person.firstName">
名:<input type="text" v-model="person.lastName">
<h2>全名:{{ person.fullName }}</h2>
全名:<input type="text" v-model="person.fullName">
</template>
<script>
import { reactive,computed } from 'vue';
export default {
name: 'Demo',
setup(props, context){
let person = reactive({
firstName: 'z',
lastName: 's'
})
//计算属性
person.fullName = computed({
get(){
return person.firstName+'-'+person.lastName
},
set(value){
const nameArr = value.split('-')
person.firstName = nameArr[0]
person.lastName = nameArr[1]
}
})
return {
person,
}
}
}
</script>
2.watch函数
<template>
<h2>求和为: {{ sum }}</h2>
<button @click="sum++">点我加一</button>
<h2>信息为 {{ msg }}</h2>
<button @click="msg+='!'">修改信息</button>
<h2>name: {{ person.name }}</h2>
<h2>age: {{ person.age }}</h2>
<h2>salary {{ person.job.salary }}k</h2>
<button @click="person.name+='!'">change name</button>
<button @click="person.age++">change age</button>
<button @click="person.job.salary++">add salary</button>
</template>
<script>
import { ref, reactive, watch } from 'vue';
export default {
name: 'Demo',
setup(props, context){
let sum = ref(0)
let msg = ref('nnihao')
//监视 -- 情况一
// watch(sum, (newValue, oldValue)=>{
// console.log('sum变了'+newValue+','+oldValue)
// })
//监视 -- 情况二
// watch([sum, msg], (newValue, oldValue)=>{
// console.log('sum变了'+newValue[0]+','+oldValue[0])
// console.log('msg变了'+newValue[1]+','+oldValue[1])
// }, {immediate: true})
let person = reactive({
name: 'zs',
age: 18,
job: {
salary: 10
}
})
//监视 -- 情况三
// reactive 定义的数据,无法正确获取 oldValue
// 默认开启深度监视,可以监视对象内部的对象数据变化,无法关闭
// watch(person, (newValue, oldValue)=>{
// console.log('person changed!', newValue, oldValue)
// })
// 监视 -- 情况四
// 监视reactive变量的某个属性
// watch(()=>person.age, (newValue, oldValue)=>{
// console.log('person Age changed!', newValue, oldValue)
// })
// 监视 -- 情况五
// 监视reactive变量的多个属性
// watch([()=>person.age, ()=>person.name], (newValue, oldValue)=>{
// console.log('person Age or Name changed!', newValue, oldValue)
// })
// 监视 -- 特殊情况
// person.job 是对象,引用地址不变,无法监视,应直接使person.job.salary
watch(()=>person.job, (newValue, oldValue)=>{
console.log('person job changed!', newValue, oldValue)
})
watch(()=>person.job.salary, (newValue, oldValue)=>{
console.log('person job salary changed!', newValue, oldValue)
})
return {
sum, msg, person
}
}
}
</script>
3.watchEffect函数
不用指明监视那个属性,监视的回调中用到哪个属性,就监视哪个属性。
watchEffect(()=>{
const x1 = sum.value
const x2 = person.age
})
7.Vue3声明周期
- beforeCreate
- created
- beforeMount
- mounted
- beforeUpdate
- updated
- beforeUnmount
- unmounted
8.自定义hook
什么是hook?本质是一个函数,把setup函数中组合式API进行了封装。
优势:复用代码,让setup中的逻辑更清楚易懂。
<template>
<h2>当前鼠标坐标为: x:{{ point.x }} y:{{ point.y }}</h2>
</template>
<script>
import { ref, reactive, onMounted, onBeforeUnmount } from 'vue';
import usePoint from '../hooks/usePoint'
export default {
name: 'Demo',
setup(props, context){
/* 正常写法
let point = reactive({
x: 0,
y: 0
})
function savePoint(event) {
point.x = event.pageX
point.y = event.pageY
}
onMounted(()=>{
window.addEventListener('click', savePoint)
})
onBeforeUnmount(()=>{
window.removeEventListener('click',savePoint)
})
*/
/* 定义hook:usePoint.js */
let point = usePoint()
return {
point
}
}
}
</script>
// usePoint.js
import { reactive, onMounted, onBeforeUnmount } from 'vue';
export default function (){
let point = reactive({
x: 0,
y: 0
})
function savePoint(event) {
point.x = event.pageX
point.y = event.pageY
}
onMounted(()=>{
window.addEventListener('click', savePoint)
})
onBeforeUnmount(()=>{
window.removeEventListener('click',savePoint)
})
return point
}
9.toRef
创建一个ref对象,其value值指向另一个对象中的某个属性。
const name = toRef(person, 'name')
应用:将响应式对象中的某个属性单独提供给外部使用。具有引用关系,同步修改。
扩展:toRefs与toRef功能一致,可以批量创建多个ref对象。toRefs(person)
<template>
<h2>{{ person }}</h2>
<h2>name: {{ name }}</h2>
<h2>age: {{ age }}</h2>
<h2>salary {{ salary }}k</h2>
<button @click="name+='!'">change name</button>
<button @click="age++">change age</button>
<button @click="salary++">add salary</button>
</template>
<script>
import { reactive,toRef,toRefs } from 'vue';
export default {
name: 'Demo',
setup(){
let person = reactive({
name: 'zs',
age: 18,
job: {
salary: 10
}
})
const name = toRef(person, 'name')
const age = toRef(person, 'age')
const salary = toRef(person.job, 'salary')
return {
person, name, age, salary
}
}
}
</script>
<template>
<h2>{{ person }}</h2>
<h2>name: {{ name }}</h2>
<h2>age: {{ age }}</h2>
<h2>salary {{ job.salary }}k</h2>
<button @click="name+='!'">change name</button>
<button @click="age++">change age</button>
<button @click="job.salary++">add salary</button>
</template>
<script>
import { reactive,toRef,toRefs } from 'vue';
export default {
name: 'Demo',
setup(){
let person = reactive({
name: 'zs',
age: 18,
job: {
salary: 10
}
})
return {
person, ...toRefs(person)
}
}
}
</script>
3.其他组合API
1.shallowReactive 与 shallowRef
shallowReactive : 浅层响应式,只处理外层属性数据。不处理job.salary。
shallowRef:浅层响应式,只处理基本类型的响应数据,不处理对象类型的响应数据。
2.readonly 与 shallowReadonly
readonly : 让一个响应式数据变成只读的(深只读)。person = readonly(person)
shallowReadonly:浅层只读,深层仍可改(浅只读)person = shallowReadonly(person)
应用场景:不希望数据被修改时。
3.toRaw 与 markRaw
toRaw :将一个reactive响应式对象转为普通对象
使用场景:用于读取响应式对象对应的普通对象,对这个普通对象的所有操作,不会引起页面更新。
markRaw:标记一个对象,使其永远不再成为响应式对象。
应用场景:
- 有些值不应被设置为响应式的。例如复杂的第三方类库等。
- 当渲染具有不可变数据源的大列表时,跳过响应式转换可以提高性能。
4.customRef
创建一个自定义的ref, 并对其依赖项跟踪和更新触发进行显示的控制。
- 实现防抖效果
<template>
<input type="text" v-model="keyword">
<h3>{{ keyword }}</h3>
</template>
<script>
import { ref, customRef } from 'vue';
export default {
name: 'Demo',
setup(){
// let keyword = ref('hello') //vue 提供的ref
// 自定义一个ref -- myRef
function myRef(value){
let timer
return customRef((track, trigger)=>{
return {
get(){
// 模板中 {{ keyword }} 读取时
console.log('从 myRef 中读取数据',value)
track() //通知vue追踪value的修改(告知value的修改是有用的)
return value;
},
set(newValue){
console.log('修改了 myRef 中的数据',newValue)
clearTimeout(timer) // 删除定时器
timer = setTimeout(()=>{ // 设置定时器
value = newValue
trigger() //通知vue重新解析模板
},500) //500ms后更新
}
}
})
}
let keyword = myRef('hello')
return {
keyword
}
}
}
</script>
5.provide 与 inject
- 实现祖先与后代组件间通信
- 套路,祖先组件有一个provide选项来提供数据 ,所有后代组件有一个inject选项来开始使用这些数据。
- 具体写法
// 祖组件中
setup(){
let car = reactive({
name: '',
price: ''
})
provide('car', car)
}
//后代组件中
setup(){
const car = inject('car')
return {car}
}
6.响应式数据的判断
isRef检查一个值是否为一个 ref 对象isReactive检查一个对象是否有 reactive 的响应式代理isReadonlyisProxy
4.组合式API的优势
1.Options API 存在的问题
配置式的API,新增或者修改一个需求,需要分别在data、methods、computed里修改。
2. Composition API
更加优雅的组织代码、函数。让相关功能的代码更加有序的组织在一起。
5.新的组件
1.Fragment
- 在Vue2中: 组件必须有一个根标签
- 在Vue3中: 组件可以没有根标签,内部会将多个标签包含在一个Fragment虚拟元素中
- 好处: 减少标签层级,减小内存占用
2.Teleport
Teleport 是一种能够将我们的组件html结构移动到指定位置的技术。
<template>
<div>
<button @click="isShow=true">点我弹窗</button>
<Teleport to="body">
<div v-if="isShow" class="mask">
<div class="dialog">
<h3>我是一个弹窗</h3>
<h4>一些内容</h4>
<button @click="isShow=false">关闭按钮</button>
</div>
</div>
</Teleport>
</div>
</template>
<script>
import { ref } from 'vue'
export default {
name: 'Dialog',
setup(){
let isShow = ref(false)
return {
isShow
}
}
}
</script>
<style scoped>
.mask{
position: absolute;
top: 0;bottom: 0;left: 0;right: 0;
background-color: rgba(0,0,0,0.5);
}
.dialog {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
text-align: center;
width: 300px;
height: 300px;
background-color: green;
}
</style>
3.Suspense
等待异步组件时渲染一些额外内容,让应用有更好的用户体验。
使用步骤:
- 异步引入组件
// import Fa from './components/Fa.vue'; //静态引入
import { defineAsyncComponent } from 'vue';
const Fa = defineAsyncComponent(()=>import('./components/Fa.vue')) //异步引入
- 使用
Suspense包裹组件,并配置好default与fallback
<template>
<h1>我是APP组件</h1>
<Suspense>
<!-- 需要展示的内容 -->
<template v-slot:default>
<Fa/>
</template>
<!-- 未加载出时显示的内容 -->
<template v-slot:fallback>
<h3>加载中......</h3>
</template>
</Suspense>
</template>
6.其它
1.全局API的转移
Vue3将全局API,即:Vue.xxx调整到应用实例(app)上
2.其他改变
- data选项应始终被声明为一个函数
- 移除keyCode作为v-on的修饰符,同时不再支持config.keyCodes
- 移除v-on.native修饰符
- 移除过滤器filter
常用设置和工具
1.关闭语法检查
在最外层目录新建文件vue.config,js
module.exports = {
lintOnSave: false, //关闭语法检查
}
VueX
1.介绍
专门在Vue中实现集中式状态(数据)管理的一个Vue插件,对应用中多个组件的共享状态进行集中式的管理(读/写),也是一种组件间通信的方式,且适用于任意组件间通信。
1.使用场景
同意数据的共享和修改
- 多个组件依赖于同一状态
- 来自不同组件的行为需要变更同一状态
2.搭建环境
1.安装
npm i vuex
2.使用
app.use(Vuex)
VueRouter
npm install vue-router@4 -s
//main.js
import { createApp } from 'vue'
import App from './App.vue'
import Demo from './components/Demo'
import Fa from './components/Fa'
import { createRouter, createWebHashHistory } from 'vue-router'
const routes = [
{path: '/', component: Demo},
{path: '/about', component: Fa},
]
const router = createRouter({
history: createWebHashHistory(),
routes
})
const app = createApp(App)
app.use(router)
app.mount('#app')
<template>
<div>
<RouterLink to="/" >首页</RouterLink>
<RouterLink to="/about">关于</RouterLink>
</div>
<RouterView></RouterView>
</template>
<script>
export default {
name: 'App',
components: {},
}
</script>