Vue3进阶指南

1,135 阅读3分钟

前言

  • 2019年10月5日 Vue3公布源码

image.png

  • 2020年09月18日,Vue3正式发布,代号 One Piece

image.png

  • 2022年02月07日,Vue3正式作为默认版本

image.png

由Vue2过渡至Vue3记录,Vue3了解过程中,最大感受就是没有了统一的 data,methods,watch,computed...等属性,而是以对象方式灵活调用,最终可以实现代码模块化,可以将实现同一个功能的参数、属性、方法集中到一起,便于迁移

构建

推荐使用Vite+Vue3方式构建项目Vite构建速度相比webpack大幅度提升

Vite

Vite官方文档中查看构建方式

数据

ref 响应式变量

Vue3中变量不再默认支持双向绑定,需创建响应式对象

响应式变量声明 ref()

声明使用ref() 赋值使用变量.value模版中修改不需要使用.value

<template>
  <h1>{{msg}}</h1>
  <h1>{{reve(msg)}}</h1>
  <h1>{{num}}</h1>
  <button @click="setmsg">点击</button>
  <button @click="num++">点击</button>
</template>

<script setup>
import { ref } from 'vue'
let msg = ref('你好世界')
let num = ref(0)
function reve(info){
  return info.split('').reverse().join('')
}
function setmsg(info){
  console.log(msg)
  msg.value = 'Hello world!'
  console.log('msg', msg.value)
}
</script>

reactive 响应式对象

使用ref()可声明响应式对象,但赋值过于繁琐,Vue3提供reactive()用于声明响应式对象

<template>
<!--  ref 声明对象-->
  <h1>{{user.name}}</h1>
  <h1>{{user.age}}</h1>
  <button @click="updateUser">点击</button>
  <!--  ref 声明对象-->

<!--  reactive声明对象-->
  <h1>{{user1.name}}</h1>
  <h1>{{user1.age}}</h1>
  <button @click="updateUser1">点击</button>
<!--  reactive声明对象-->


<!-- ref转换reactive -->
  <h1>{{user2.name}}</h1>
  <h1>{{user2.age}}</h1>
  <button @click="updateUser2">点击</button>
  <!-- ref转换reactive -->

</template>

<script setup>
import { ref,reactive } from 'vue'
//ref 声明对象
let user = ref({
  name:'user',
  age:18
})
function updateUser(){
  user.value.name = '李大锤'
  user.value.age = user.value.age +1
}
//ref 声明对象

// reactive声明对象
let user1 = reactive({
  name:'user1',
  age:19
})
function updateUser1(){
  user1.name = '王小明'
  user1.age = user1.age+1
}
// reactive声明对象

// ref转换reactive
//user2为引用类型 指向 user 跟随发生改变
let user2 = reactive(user.value)
function updateUser2() {
  user2.name = 'user2'
  user2.age = 0
}
// ref转换reactive
</script>

computed 计算属性

调用方式与Vue2不同,基本功能一致 缓存数据优化性能

基本使用

computed 传入 function return 数据

<template>
  <h1>{{msg}}</h1>
  <h1>{{reve(msg)}}</h1>
  <h1>{{reve(msg)}}</h1>
  <h1>{{reve(msg)}}</h1>
  <h1>{{resMsg}}</h1>
  <h1>{{resMsg}}</h1>
  <h1>{{resMsg}}</h1>
</template>

<script setup>
import { ref,computed } from 'vue'
let msg = ref('你好世界')

function reve(info){
  console.log('reve调用次数')
  return info.split('').reverse().join('')
}
//计算属性
const resMsg = computed(()=>{
  console.log('computed调用次数')
  return msg.value.split('').reverse().join('')
})
</script>

更新变量

computed 传入 Object { function get ,function set } function get return 数据 , function set 修改数据

<template>
  <h1>{{ info }}</h1>
  <h1>{{ resInfo }}</h1>
  <button @click="UpdateResInfo">修改</button>
</template>

<script setup>
import {ref, computed} from 'vue'
let info = ref('Hello world')

//计算属性
// computed 传入 Object { function get ,function set }  function get return 数据 function set 修改数据
const resInfo = computed({
  get() {
    return info.value.split('').reverse().join('')
  },
  set(value) {
    info.value = value
  }
})
let UpdateResInfo = () => {
  resInfo.value = '李大锤'
}
</script>

watch 监听

watch 可监听 data Object function return Object.data [data,Object,function]

<template>
  <h1>{{ info }}</h1>
  <button @click="updateInfo">修改</button>

  <h1>{{ user.name }}</h1>
  <h1>{{ user.sex }}</h1>
  <ul>
    <li :key="index" v-for="(i,index) in user.log">{{i.title}} {{i.time}}</li>
  </ul>

  <button @click="updateUser">修改</button>
  <button @click="updateUser2">深度修改</button>
</template>

<script setup>
import {ref,reactive, watch} from 'vue'

//watch监听变量
let info = ref('Hello world')
watch(info,(oldData,newData)=>{
  console.log('oldData:',oldData,'newData:',newData)
})
function updateInfo(){
  info.value = '你好 世界'
}

//watch监听对象
let user = reactive({
  name:'李大锤',
  sex:'男',
  log:[
    {
      time:'2020.01.01',
      title:'修改密码',
    },
    {
      time:'2020.01.01',
      title:'删除数据', 
    }
  ]
})
//监听整个对象
watch(user,(oldData,newData)=>{
  console.log('user oldData:',oldData,'user newData:',newData)
})
//监听对象中的属性 user.name
watch(()=>user.name ,(oldData,newData)=>{
  console.log('user.name oldData:',oldData,'user.name newData:',newData)
})
//同时监听多个 user.name,info
watch([()=>user.name,info] ,(oldData,newData)=>{
  console.log('user.name,info oldData:',oldData,'user.name,info newData:',newData)
})
function updateUser(){
  user.name = '李二锤'
  user.sex = '女'
}
function updateUser2(){
  user.log[0].title = '更新密码'
}
//watch监听对象
</script>

组件

defineProps 传参

父组件向子组件传参 子组件改用defineProps对象接收数据

  • App.vue
<template>
  <userItem :title="i.name" :address="i.address" v-for="i in userList"></userItem>
</template>

<script setup>
import {ref, reactive} from 'vue'
import userItem from './components/userItem.vue'

let userList = reactive([
  {
    name: '江敏',
    address: '澳门特别行政区 离岛 '
  },
  {
    name: '方勇',
    address: '海南省 三亚市 '
  },
  {
    name: '方勇',
    address: '海南省 三亚市 '
  }
])
</script>
  • userItem.vue
<template>
  <div style="display: flex;padding: 30px;border: 1px solid #ddd;margin-bottom: 20px;justify-content: space-between">
    <div>{{props.title}}</div>
    <div>{{props.address}}</div>
  </div>
</template>

<script setup>
import {defineProps} from 'vue'
const props = defineProps({
  title:{
    default:'',
    type:String
  },
  address:{
    default:'',
    type:String
  }
})
</script>

defineEmits 注册事件

子组件通过defineProps注册事件,触发传递到父组件

  • App.vue
<template>
<!--  绑定事件-->
  <userItem :title="i.name" :address="i.address" v-for="i in userList" @tap="toPage"></userItem>
</template>

<script setup>
import {ref, reactive} from 'vue'
import userItem from './components/userItem.vue'
let userList = reactive([
  {
    name: '江敏',
    address: '澳门特别行政区 离岛 '
  },
  {
    name: '方勇',
    address: '海南省 三亚市 '
  },
  {
    name: '方勇',
    address: '海南省 三亚市 '
  }
])
// 触发事件执行 function
function toPage(itemInfo){
  console.log('itemInfo',itemInfo)
  console.log('itemInfo',itemInfo.title)
}
</script>
  • userItem.vue
<template>
  <div style="display: flex;padding: 30px;border: 1px solid #ddd;margin-bottom: 20px;justify-content: space-between" @click="tapItem(props)">
    <div>{{props.title}}</div>
    <div>{{props.address}}</div>
  </div>
</template>

<script setup>
import {defineProps,defineEmits} from 'vue'
const props = defineProps({
  title:{
    default:'',
    type:String
  },
  address:{
    default:'',
    type:String
  }
})
//注册事件 数组形式注册多个
const emit = defineEmits(['tap','longTap'])
//抛出事件
function tapItem(itemInfo){
  emit('tap',itemInfo)
}
</script>

路由

安装vue-router

npm install vue-router

基本使用

  • main.js
import { createApp } from 'vue'
import App from './App.vue'
import router from './router/index'
let app = createApp(App)
app.use(router)
app.mount('#app')
  • App.vue
<template>
<!--  路由视图位置-->
  <router-view></router-view>
</template>
  • /src/router/index.js
import {
    createRouter,
    createWebHashHistory,
    createWebHistory,
} from 'vue-router'

//引入组件
import Home from '../views/Home/index.vue'
import User from '../views/User/index.vue'

//配置路由
const routes = [
    {

        path:'/',
        component:Home,
        name:'home'
    },
    {
        path:'/user',
        component:User,
        name:'user'
    },
    {
        path:'/goods/:id',
        component:()=>import('../views/goods/index.vue'),
        name:'goods'
    }
]

//创建路由 传入配置 设置history方式
const router = createRouter({
    history:createWebHashHistory(),
    routes
})

// 导出router
export  default router
  • /src/views/User/index.vue
<template>
  <div>
    Home
    <router-link to="/user">去user页</router-link>
  </div>
</template>
  • /src/views/Home/index.vue
<template>
  <div>
    Home
    <router-link to="/user">去user页</router-link>
    <button @click="toPage">去user页</button>
  </div>
  <div>
    goods
    <input v-model="id">
    <button @click="toGoods">去goods页</button>
  </div>
</template>
<script setup>
import {ref} from 'vue'
import { useRoute,useRouter} from 'vue-router'
let route = useRoute()
let router = useRouter()

//user页
function toPage(){
  console.log('route',route)
  router.push('/user')
}
//user页


//goods页
let id = ref(0)
function toGoods(){
  router.push(`/goods/${id.value}`)
}
</script>
  • /src/views/goods/index.vue
<template>
  goods
  <div>{{route.params.id}}</div>
</template>

<script setup>
import {useRoute}  from 'vue-router'
console.log('33333')
const route = useRoute()
console.log(route)
</script>

状态管理

组件中的数据 组件销毁时释放 需声明响应式数据

Vuex中的数据 应用销毁时释放 全局响应式数据

缓存中的数据 长久保存,清除缓存后释放 静态数据

Vuex

  • main.js
import { createApp } from 'vue'
import App from './App.vue'
import router from './router/index'
import store from "./store/index";

let app = createApp(App)
app.use(router)
app.use(store)
app.mount('#app')

  • /src/store/index.js
import { createStore } from 'vuex'
// 创建一个新的 store 实例
const store = createStore({
    state () {
        return {
            count: 0
        }
    },
    //修改数据
    mutations: {
        increment (state,data) {
            state.count  += data
        }
    },
    //计算属性
    getters:{
        countx5(state){
            return state.count * 5
        }
    },
    //异步请求数据
    actions:{
        // 仓库对象store, 请求参数payload
        asyncAdd(store,payload){
            // commit 用于触发mutations 中方法
            setTimeout(()=>{
                console.log(payload)
                store.commit('increment',payload)
            },1000)

        }
    }

})
export default store
  • /src/views/Home/index.vue
<template>
  <div>数量:{{store.state.count}}</div>
  <div>数量X5:{{store.getters.countx5}}</div>
  <button @click="addGoods">添加</button>
  <button @click="asyncAddGoods">异步添加 11</button>
  <router-link to="/goods/11">toGoods</router-link>
</template>
<script setup>
import {useStore} from 'vuex'
import {ref} from 'vue'
let store = useStore()
function addGoods(){
  //同步操作
  store.commit('increment',3)
}
function asyncAddGoods(){
  //异步操作
  store.dispatch('asyncAdd',11)
}
</script>
  • /src/views/goods/index.vue
<template>
  <div>数量:{{store.state.count}}</div>
  <div>数量X5:{{store.getters.countx5}}</div>
</template>

<script setup>
import {useStore} from "vuex";
let store = useStore()
</script>

后续学习整理中···