一篇文章 学会 Vue3 极速入门 (附带增删改查 案例 + Springboot)

news2024/11/24 11:44:32

vue3前置

00-导学

在这里插入图片描述

这将是你能看到的最快速Vue3 入门文章, 我们将快速的 去学习Vue3相关的知识,并结合后端做一个增删改查的项目,能够帮助你快速的上手Vue3, 包含了Vue 所含的所有特性, 你会知道 Vue3和Vue2 的区别,以及企业为何偏偏从技术桩型方面从 Vue2切换到 Vue3

img

01-为什么学vue3

目标:了解vue3现状,以及它的优点,展望它的未来

Vue3现状:

  • vue-next 2020年09月18日,正式发布vue3.0版本。但是由于刚发布周边生态不支持,大多数开发者处于观望。
  • 现在主流组件库都已经发布了支持vue3.0的版本,其他生态也在不断地完善中,这是趋势。
    • element-plus 基于 Vue 3.0 的桌面端组件库
    • vant vant3.0版本,有赞前端团队开源移动端组件库
    • ant-design-vue Ant Design Vue 2.0版本,社区根据蚂蚁 ant design 开发

Vue3优点:

  • 最火框架,它是国内最火的前端框架之一,官方文档 中文文档
  • 性能提升,运行速度事vue2.x的1.5倍左右
  • 体积更小,按需编译体积比vue2.x要更小
  • 类型推断,更好的支持Ts(typescript)这个也是趋势
  • 高级给予,暴露了更底层的API和提供更先进的内置组件
  • ★组合API (composition api) ,能够更好的组织逻辑,封装逻辑,复用逻辑

Vue3展望:

  • 这是趋势,越来越多的企业将来肯定会升级到Vue3.0
  • 大型项目,由于对Ts的友好越来越多大型项目可以用Vue3.0

总结: 为什么要学 vue3 ?

  • 适应市场学习流行的技术提高提升自己竞争力,给自己加薪。

02-vite基本使用

目标:了解vite是什么,使用vite创建vue项目,用来学习vue3知识

vite是什么:官方文档

  • 它是一个更加轻量(热更新速度快,打包构建速度快)的vue项目脚手架工具。
  • 相对于vue-cli它默认安装的插件非常少,随着开发过程依赖增多,需要自己额外配置。
  • 所以: 在单纯学习vue3语法会使用它,后面做项目的时候我们还是使用vue-cli

vite基本使用:

  • 创建项目 npm init vite-app 项目名称 或者 yarn create vite-app 项目名称
  • 安装依赖 npm i 或者 yarn
  • 启动项目 npm run dev 或者 yarn dev

总结: vite是什么?

  • 使用vite创建项目学习vue3语法,使用vue-cli创建项目正式开发。

03-创建vue应用

目标:掌握如何创建vue3应用实例

基本步骤:

  • 在main.js中导入createApp函数
  • 定义App.vue组件,导入main.js
  • 使用createApp函数基于App.vue组件创建应用实例
  • 挂载至index.html的#app容器

落地代码:

App.vue

<template>
  <div class="container">
    我是根组件
  </div>
</template>
<script>
export default {
  name: 'App'
}
</script>

main.js

// 创建一个vue应用
// 1. 导入createApp函数
// 2. 编写一个根组件App.vue,导入进来
// 3. 基于根组件创建应用实例
// 4. 挂载到index.html的#app容器

import {createApp} from 'vue'
import App from './App.vue'
const app = createApp(App)
app.mount('#app')

总结: 如何创建vue应用实例?

  • 通过createApp创建应用实例—>扩展功能将来都是在app上进行。

04-选项API和组合API

目标:理解什么是选项API写法,什么是组合API写法。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-1xsUQfIw-1681687891609)(./docs/media/1619277945556.png)]

什么是选项API写法:Options ApI

  • 咱们在vue2.x项目中使用的就是 选项API 写法
    • 代码风格:data选项写数据,methods选项写函数…,一个功能逻辑的代码分散。
  • 优点:易于学习和使用,写代码的位置已经约定好
  • 缺点:代码组织性差,相似的逻辑代码不便于复用,逻辑复杂代码多了不好阅读。
  • 补充:虽然提供mixins用来封装逻辑,但是出现数据函数覆盖的概率很大,不好维护。
<template>
  <div class="container">
    <div>鼠标位置:</div>
    <div>X轴:{{x}}</div>
    <div>Y轴:{{y}}</div>
    <hr>
    <div>{{count}} <button @click="add()">自增</button></div>  
  </div>
</template>
<script>
export default {
  name: 'App',
  data () {
    return {
      x: 0,
      y: 0,
      count: 0
    }
  },
  mounted() {
    document.addEventListener('mousemove', this.move)
  },
  methods: {
    move(e) {
      this.x = e.pageX
      this.y = e.pageY
    },
    add () {
        this.count++
    }    
  },
  destroyed() {
    document.removeEventListener('mousemove', this.move)
  }
}
</script>

什么是组合API写法:Compositon API

  • 咱们在vue3.0项目中将会使用 组合API 写法
    • 代码风格:一个功能逻辑的代码组织在一起(包含数据,函数…)
  • 优点:功能逻辑复杂繁多情况下,各个功能逻辑代码组织再一起,便于阅读和维护
  • 缺点:需要有良好的代码组织能力和拆分逻辑能力,PS:大家没问题。
  • 补充:为了能让大家较好的过渡到vue3.0的版本来,也支持vue2.x选项API写法
<template>
  <div class="container">
    <div>鼠标位置:</div>
    <div>X轴:{{x}}</div>
    <div>Y轴:{{y}}</div>
    <hr>
    <div>{{count}} <button @click="add()">自增</button></div>  
  </div>
</template>
<script>
import { onMounted, onUnmounted, reactive, ref, toRefs } from 'vue'
export default {
  name: 'App',
  setup () {
    // 鼠标移动逻辑
    const mouse = reactive({
      x: 0,
      y: 0
    })
    const move = e => {
      mouse.x = e.pageX
      mouse.y = e.pageY
    }
    onMounted(()=>{
      document.addEventListener('mousemove',move)
    })
    onUnmounted(()=>{
      document.removeEventListener('mousemove',move)
    })

    // 累加逻辑
    const count = ref(0)
    const add = () => {
      count.value ++ 
    }

    // 返回数据
    return {
      ...toRefs(mouse),
      count,
      add
    }
  }
}
</script>

总结:

  • 知道选项API和组合API的写法区别,建议大家使用组合API在vue3.0项目中。

05-组合API-setup函数

目标:掌握setup函数的基本使用

使用细节:

  • setup 是一个新的组件选项,作为组件中使用组合API的起点。
  • 从组件生命周期来看,它的执行在组件实例创建之前vue2.x的beforeCreate执行。
  • 这就意味着在setup函数中 this 还不是组件实例,this 此时是 undefined
  • 在模版中需要使用的数据和函数,需要在 setup 返回。

演示代码:

<template>
  <div class="container">
    <h1 @click="say()">{{msg}}</h1>
  </div>
</template>
<script>
export default {
  setup () {
    console.log('setup执行了')
    console.log(this)
    // 定义数据和函数
    const msg = 'hi vue3'
    const say = () => {
      console.log(msg)
    }

    return { msg , say}
  },
  beforeCreate() {
    console.log('beforeCreate执行了')
    console.log(this)
  }
}
</script>

总结: setup 组件初始化之前执行,它返回的数据和函数可在模版使用。

06-组合API-生命周期

目标:掌握使用组合API写法的生命周期钩子函数

回顾vue2.x生命周期钩子函数:

  • beforeCreate
  • created
  • beforeMount
  • mounted
  • beforeUpdate
  • updated
  • beforeDestroy
  • destroyed

认识vue3.0生命周期钩子函数

  • setup 创建实例前
  • onBeforeMount 挂载DOM前
  • onMounted 挂载DOM后
  • onBeforeUpdate 更新组件前
  • onUpdated 更新组件后
  • onBeforeUnmount 卸载销毁前
  • onUnmounted 卸载销毁后

演示代码:

<template>
  <div class="container">
    container
  </div>
</template>
<script>
import { onBeforeMount, onMounted } from 'vue'
export default {
  setup () {
    onBeforeMount(()=>{
      console.log('DOM渲染前',document.querySelector('.container'))
    })
    onMounted(()=>{
      console.log('DOM渲染后1',document.querySelector('.container'))
    })
    onMounted(()=>{
      console.log('DOM渲染后2',document.querySelector('.container'))
    })
  },
}
</script>

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-PfmBbW2L-1681687891610)(./docs/media/1619283131573.png)]

总结: 组合API的生命周期钩子有7个,可以多次使用同一个钩子,执行顺序和书写顺序相同。

07-组合API-reactive函数

目标:掌握使用reactive函数定义响应式数据

定义响应式数据:

  • reactive是一个函数,它可以定义一个复杂数据类型,成为响应式数据。

演示代码:

<template>
  <div class="container">
    <div>{{obj.name}}</div>
    <div>{{obj.age}}</div>
    <button @click="updateName">修改数据</button>
  </div>
</template>
<script>
import { reactive } from 'vue'
export default {
  name: 'App',
  setup () {
    // 普通数据
    // const obj = {
    //   name: 'ls',
    //   age: 18
    // }
    const obj = reactive({
      name: 'ls',
      age: 18
    })

    // 修改名字
    const updateName = () => {
      console.log('updateName')
      obj.name = 'zs'
    }

    return { obj ,updateName}
  }
}
</script>

总结: 通常是用来定义响应式对象数据

08-组合API-toRef函数

目标:掌握使用toRef函数转换响应式对象中某个属性为单独响应式数据,并且值是关联的。

定义响应式数据:

  • toRef是函数,转换响应式对象某个属性为单独响应式数据,并且值是关联的

演示代码:

<template>
  <div class="container">
    {{name}} <button @click="updateName">修改数据</button>
  </div>
</template>
<script>
import { reactive, toRef } from 'vue'
export default {
  name: 'App',
  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 = () => {
      console.log('updateName')
      // toRef转换响应式数据包装成对象,value存放值的位置
      name.value = 'zs'
    }

    return {name, updateName}
  }
}
</script>
<style scoped lang="less"></style>

使用场景:有一个响应式对象数据,但是模版中只需要使用其中一项数据。

09-组合API-toRefs函数

目标:掌握使用toRefs函数定义转换响应式中所有属性为响应式数据,通常用于解构|展开reactive定义对象。

定义响应式数据:

  • 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 {
  name: 'App',
  setup () {
    // 1. 响应式数据对象
    const obj = reactive({
      name: 'ls',
      age: 10
    })
    console.log(obj)
    // 2. 解构或者展开响应式数据对象
    // const {name,age} = obj
    // console.log(name,age)
    // const obj2 = {...obj}
    // console.log(obj2)
    // 以上方式导致数据就不是响应式数据了
    const obj3 = toRefs(obj)
    console.log(obj3)

    const updateName = () => {
      // obj3.name.value = 'zs'
      obj.name = 'zs'
    }

    return {...obj3, updateName}
  }
}
</script>
<style scoped lang="less"></style>

使用场景:剥离响应式对象(解构|展开),想使用响应式对象中的多个或者所有属性做为响应式数据。

10-组合API-ref函数

目标:掌握使用ref函数定义响应式数据,一般用于简单类型数据

定义响应式数据:

  • ref函数,常用于简单数据类型定义为响应式数据
    • 再修改值,获取值的时候,需要.value
    • 在模板中使用ref申明的响应式数据,可以省略.value

演示代码:

<template>
  <div class="container">
    <div>{{name}}</div>
    <div>{{age}}</div>
    <button @click="updateName">修改数据</button>
  </div>
</template>
<script>
import { ref } from 'vue'
export default {
  name: 'App',
  setup () {
    // 1. name数据
    const name = ref('ls')
    console.log(name)
    const updateName = () => {
      name.value = 'zs'
    }
    // 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

11-知识运用案例

目标:利用所学知识完成组合API实例

基本步骤:

  • 记录鼠标坐标
    • 定义一个响应式数据对象,包含x和y属性。
    • 在组件渲染完毕后,监听document的鼠标移动事件
    • 指定move函数为事件对应方法,在函数中修改坐标
    • 在setup返回数据,模版中使用
  • 累加1功能
    • 定义一个简单数据类型的响应式数据
    • 定义一个修改数字的方法
    • 在setup返回数据和函数,模板中使用

落的代码:

<template>
  <div class="container">
    <div>坐标</div>
    <div>x: {{x}}</div>
    <div>y: {{y}}</div>
    <hr>
    <div>{{count}} <button @click="add">累加1</button></div>
  </div>
</template>
<script>
import { onMounted, onUnmounted, reactive , ref, toRefs} from 'vue'
const useMouse = () => {
    // 1. 记录鼠标坐标
    // 1.1 申明一个响应式数据,他是一个对象,包含x y
    const mouse = reactive({
      x: 0,
      y: 0
    })
    // 1.3 修改响应式数据
    const move = (e) => {
      mouse.x = e.pageX
      mouse.y = e.pageY
    }
    // 1.2 等dom渲染完毕。去监听事件
    onMounted(()=>{
      document.addEventListener('mousemove', move)
    })
    // 1.4 组件消耗,删除事件
    onUnmounted(()=>{
      document.removeEventListener('mousemove', move)
    })

    return mouse
}
export default {
  name: 'App',
  setup () {

    const mouse = useMouse()

    // 2. 数字累加
    const count = ref(0) 
    const add = () => {
      count.value ++
    }



    return { ...toRefs(mouse), count, add }
  }
}
</script>
<style scoped lang="less"></style>

总结: 体会组合API的写法,尝试组织可读性高的代码。

12-组合API-computed函数

目标:掌握使用computed函数定义计算属性

定义计算属性:

  • computed函数,是用来定义计算属性的,计算属性不能修改。

基本使用:

<template>
  <div class="container">
    <div>今年:{{age}}岁</div>
    <div>后年:{{newAge}}岁</div>
  </div>
</template>
<script>
import { computed, ref } from 'vue'
export default {
  name: 'App',
  setup () {
    // 1. 计算属性:当你需要依赖现有的响应式数据,根据一定逻辑得到一个新的数据。
    const age = ref(16)
    // 得到后年的年龄
    const newAge = computed(()=>{
      // 该函数的返回值就是计算属性的值
      return age.value + 2
    })

    return {age, newAge}
  }
}
</script>

高级用法:

<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 {
  name: 'App',
  setup () {
    // 1. 计算属性:当你需要依赖现有的响应式数据,根据一定逻辑得到一个新的数据。
    const age = ref(16)
    // 得到后年的年龄
    // const newAge = computed(()=>{
    //   // 该函数的返回值就是计算属性的值
    //   return age.value + 2
    // })

    // 计算属性高级用法,传人对象
    const newAge = computed({
      // get函数,获取计算属性的值
      get(){
        return age.value + 2
      },
      // set函数,当你给计算属性设置值的时候触发
      set (value) {
        age.value = value - 2
      }
    })


    return {age, newAge}
  }
}
</script>

目的:让计算属性支持双向数据绑定。

总结:计算属性两种用法

  • 给computed传入函数,返回值就是计算属性的值
  • 给computed传入对象,get获取计算属性的值,set监听计算属性改变。

13-组合API-watch函数

目标:掌握使用watch函数定义侦听器

定义计算属性:

  • watch函数,是用来定义侦听器的

监听ref定义的响应式数据

监听多个响应式数据数据

监听reactive定义的响应式数据

监听reactive定义的响应式数据,某一个属性

深度监听

默认执行

<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 {
  name: 'App',
  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>

总结: 掌握watch的各种用法。

14-组合API-ref属性

目标:掌握使用ref属性绑定DOM或组件

获取DOM或者组件实例可以使用ref属性,写法和vue2.0需要区分开

获取单个DOM或者组件

<template>
  <div class="container">
    <!-- vue2.0 获取单个元素 -->
    <!-- 1. 通过ref属性绑定该元素 -->
    <!-- 2. 通过this.$refs.box获取元素 -->
    <!-- <div ref="box">我是box</div> -->
    <!-- vue2.0 获取v-for遍历的多个元素 -->
    <!-- 1. 通过ref属性绑定被遍历元素 -->
    <!-- 2. 通过this.$refs.li 获取所有遍历元素  -->
    <!-- <ul>
      <li v-for="i in 4" :key="i" ref="li">{{i}}</li>
    </ul> -->

    <!-- 单个元素 -->
    <div ref="dom">我是box</div>
    <!-- 被遍历的元素 -->
    <ul>
      <li v-for="i in 4" :key="i" :ref="setDom">第{{i}}LI</li>
    </ul>
  </div>
</template>
<script>
import { onMounted, 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)
    })
  }
}
</script>
<style scoped lang="less"></style>

获取v-for遍历的DOM或者组件

    // 2. 获取v-for遍历的元素
    // 2.1 定义一个空数组,接收所有的LI
    // 2.2 定义一个函数,往空数组push DOM
    const domList = []
    const setDom = (el) => {
      domList.push(el)
    }
    onMounted(()=>{
      console.log(domList)
    })
    return {dom, setDom}

总结:

  • 单个元素:先申明ref响应式数据,返回给模版使用,通过ref绑定数据

  • 遍历的元素:先定义一个空数组,定一个函数获取元素,返回给模版使用,通过ref绑定这个函数

    • 有一个边界问题:组件更新的时候会重复的设置dom元素给数组:
        // ref获取v-for遍历的DOM元素,需要在组件更新的时候重置接受dom元素的数组。
        onBeforeUpdate(()=>{
          list = []
        })
    

15-组合API-父子通讯

目标:掌握使用props选项和emits选项完成父子组件通讯

父传子:

<template>
  <div class="container">
    <h1>父组件</h1>
    <p>{{money}}</p>
    <hr>
    <Son :money="money" />
  </div>
</template>
<script>
import { ref } from 'vue'
import Son from './Son.vue'
export default {
  name: 'App',
  components: {
    Son
  },
  // 父组件的数据传递给子组件
  setup () {
    const money = ref(100)
    return { money }
  }
}
</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) {
    // 获取父组件数据money
    console.log(props.money)
  }
}
</script>

子传父:

<template>
  <div class="container">
    <h1>父组件</h1>
    <p>{{money}}</p>
    <hr>
+    <Son :money="money" @change-money="updateMoney" />
  </div>
</template>
<script>
import { ref } from 'vue'
import Son from './Son.vue'
export default {
  name: 'App',
  components: {
    Son
  },
  // 父组件的数据传递给子组件
  setup () {
    const money = ref(100)
+    const updateMoney = (newMoney) => {
+      money.value = newMoney
+    }
+    return { money , updateMoney}
  }
}
</script>

<template>
  <div class="container">
    <h1>子组件</h1>
    <p>{{money}}</p>
+    <button @click="changeMoney">花50元</button>
  </div>
</template>
<script>
import { onMounted } from 'vue'
export default {
  name: 'Son',
  // 子组件接收父组件数据使用props即可
  props: {
    money: {
      type: Number,
      default: 0
    }
  },
  // props 父组件数据
  // emit 触发自定义事件的函数
+  setup (props, {emit}) {
    // 获取父组件数据money
    console.log(props.money)
    // 向父组件传值
+    const changeMoney = () => {
      // 消费50元
      // 通知父组件,money需要变成50
+      emit('change-money', 50)
+    }
+    return {changeMoney}
  }
}
</script>

扩展:

  • 在vue2.x的时候 .sync 除去v-model实现双向数据绑定的另一种方式
<!-- <Son :money='money' @update:money="fn"  /> -->
<Son :money.sync='money'  />
  • 在vue3.0的时候,使用 v-model:money="money" 即可
    <!-- <Son :money="money" @update:money="updateMoney" /> -->
    <Son v-model:money="money" />

总结:

  • 父传子:在setup种使用props数据 setup(props){ // props就是父组件数据 }
  • 子传父:触发自定义事件的时候emit来自 setup(props,{emit}){ // emit 就是触发事件函数 }
  • 在vue3.0中 v-model.sync 已经合并成 v-model 指令

16-组合API-依赖注入

目标:掌握使用provide函数和inject函数完成后代组件数据通讯

使用场景:有一个父组件,里头有子组件,有孙组件,有很多后代组件,共享父组件数据。

演示代码:

<template>
  <div class="container">
    <h1>父组件 {{money}} <button @click="money=1000">发钱</button></h1>
    <hr>
    <Son />
  </div>
</template>
<script>
import { provide, ref } from 'vue'
import Son from './Son.vue'
export default {
  name: 'App',
  components: {
    Son
  },
  setup () {
    const money = ref(100)
    const changeMoney = (saleMoney) => {
      console.log('changeMoney',saleMoney)
      money.value = money.value - saleMoney
    }
    // 将数据提供给后代组件 provide
    provide('money', money)
    // 将函数提供给后代组件 provide
    provide('changeMoney', changeMoney)

    return { money }
  }
}
</script>
<style scoped lang="less"></style>

<template>
  <div class="container">
    <h2>子组件 {{money}}</h2>
    <hr>
    <GrandSon />
  </div>
</template>
<script>
import { inject } from 'vue'
import GrandSon from './GrandSon.vue'
export default {
  name: 'Son',
  components: {
    GrandSon
  },
  setup () {
    // 接收祖先组件提供的数据
    const money = inject('money')
    return { money }
  }
}
</script>
<style scoped lang="less"></style>

<template>
  <div class="container">
    <h3>孙组件 {{money}} <button @click="fn">消费20</button></h3>
  </div>
</template>
<script>
import { inject } from 'vue'
export default {
  name: 'GrandSon',
  setup () {
    const money = inject('money')
    // 孙组件,消费50,通知父组件App.vue组件,进行修改
    // 不能自己修改数据,遵循单选数据流原则,大白话:数据谁定义谁修改
    const changeMoney = inject('changeMoney')
    const fn = () => {
      changeMoney(20)
    }
    return {money, fn}
  }
}
</script>
<style scoped lang="less"></style>

总结:

  • provide函数提供数据和函数给后代组件使用
  • inject函数给当前组件注入provide提供的数据和函数

17-补充-v-model语法糖

目标:掌握vue3.0的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">
    <!-- 如果你想获取原生事件事件对象 -->
    <!-- 如果绑定事函数 fn fn(e){ // e 就是事件对象 } -->
    <!-- 如果绑定的是js表达式  此时提供一个默认的变量 $event -->
    <h1 @click="$event.target.style.color='red'">父组件 {{count}}</h1>
    <hr>
    <!-- 如果你想获取自定义事件  -->
    <!-- 如果绑定事函数 fn fn(data){ // data 触发自定义事件的传参 } -->
    <!-- 如果绑定的是js表达式  此时 $event代表触发自定义事件的传参 -->
    <!-- <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>

总结: vue3.0封装组件支持v-model的时候,父传子:modelValue 子传父 @update:modelValue

补充: vue2.0的 xxx.sync 语法糖解析 父传子 :xxx 子传父 @update:xxx 在vue3.0 使用 v-model:xxx 代替。

18-补充-mixins语法

目标:掌握mixins语法的基本使用,vue2.x封装逻辑的方式,vue3.0建议使用组合API

官方话术:

  • 混入 (mixin) 提供了一种非常灵活的方式,来分发 Vue 组件中的可复用功能。一个混入对象可以包含任意组件选项。当组件使用混入对象时,所有混入对象的选项将被“混合”进入该组件本身的选项。

理解全局混入:所有组件混入了这些逻辑代码

// 全局混入 全局mixin
// vue2.0 写法  Vue.mixin({})
app.mixin({
  methods: {
    say () {
      console.log(this.$el,'在mounted中调用say函数')
    }
  },
  mounted () {
    this.say()
  }
})
<template>
  <div class="container1">
    <h1> 作者:周杰伦  <a href="javascript:;">关注</a> </h1>
    <hr>
    <Son />
  </div>
</template>
<script>
import Son from './Son.vue'
export default {
  name: 'App',
  components: {
    Son
  }
}
</script>


<template>
  <div class="container2">
    <h2> 作者:周杰伦  <button>关注</button> </h2>
  </div>
</template>
<script>
export default {
  name: 'Son'
}
</script>
<style scoped lang="less"></style>

理解局部混入:通过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 {
  name: 'App',
  components: {
    Son
  },
  mixins: [followMixin]
}
</script>


<template>
  <div class="container2">
    <h2> 作者:周杰伦  <button @click="followFn">{{loading?'loading...':'关注'}}</button> </h2>
  </div>
</template>
<script>
import {followMixin} from './mixins'
export default {
  name: 'Son',
  mixins: [followMixin]
}
</script>
<style scoped lang="less"></style>

总结: 在vue2.0中一些可复用的逻辑可以使用mixins来封装,当是需要考虑逻辑代码冲突问题。vue3.0的组合API很好的解决了这个问题,就不在推荐使用mixins了。

19- Vue3 增删改查 案例

经过上面的学习,相信我们对 Vue3有了一个更新更新层次的理解,接下来我们来完成一个小案例,加固印象,也算是走出了Vue3项目的第一步

  1. 创建一个新的Vue3项目
vue create my-project

其中,my-project是您要创建的项目名称。

在安装过程中,您可以选择您要安装的特性,或者使用默认设置。注意,在Vue CLI 4.x中,默认情况下会使用Vue3版本,因此您无需执行任何其他操作即可使用Vue3。

  1. 安装Vue Router

在终端中进入您的项目目录,并输入以下命令来安装Vue Router:

cd my-project
npm install vue-router@4
  1. 编写路由

在您的项目根目录中创建一个名为router.js的文件,并将以下代码添加到文件中:

import { createRouter, createWebHistory } from 'vue-router'

import Home from './views/Home.vue'
import Add from './views/Add.vue'
import Edit from './views/Edit.vue'
import View from './views/View.vue'

const routes = [
  {
    path: '/',
    name: 'Home',
    component: Home
  },
  {
    path: '/add',
    name: 'Add',
    component: Add
  },
  {
    path: '/edit/:id',
    name: 'Edit',
    component: Edit,
    props: true
  },
  {
    path: '/view/:id',
    name: 'View',
    component: View,
    props: true
  }
]

const router = createRouter({
  history: createWebHistory(),
  routes
})

export default router

在这里,我们创建了四个路由,一个是Home,一个是Add,一个是Edit,一个是View,并将它们导出作为路由器的配置。

  1. 创建组件

在您的项目根目录中创建一个名为views的文件夹,用于存放组件。然后,创建四个组件文件:Home.vue、Add.vue、Edit.vue和View.vue。

在Home.vue中,添加以下代码:

<template>
  <div>
    <h1>Users</h1>
    <table>
      <thead>
        <tr>
          <th>ID</th>
          <th>Name</th>
          <th>Email</th>
          <th>Actions</th>
        </tr>
      </thead>
      <tbody>
        <tr v-for="user in users" :key="user.id">
          <td>{{ user.id }}</td>
          <td>{{ user.name }}</td>
          <td>{{ user.email }}</td>
          <td>
            <button @click="editUser(user)">Edit</button>
            <button @click="deleteUser(user.id)">Delete</button>
          </td>
        </tr>
      </tbody>
    </table>
    <div v-if="showEditDialog">
      <h2>Edit User</h2>
      <form @submit.prevent="saveUser">
        <label>Name:</label>
        <input type="text" v-model="editUserForm.name" />
        <label>Email:</label>
        <input type="text" v-model="editUserForm.email" />
        <button type="submit">Save</button>
        <button type="button" @click="cancelEditUser">Cancel</button>
      </form>
    </div>
    <div v-else>
      <button @click="showCreateDialog = true">Create User</button>
    </div>
    <div v-if="showCreateDialog">
      <h2>Create User</h2>
      <form @submit.prevent="createUser">
        <label>Name:</label>
        <input type="text" v-model="newUserForm.name" />
        <label>Email:</label>
        <input type="text" v-model="newUserForm.email" />
        <button type="submit">Create</button>
        <button type="button" @click="cancelCreateUser">Cancel</button>
      </form>
    </div>
  </div>
</template>

<script>

import axios from 'axios'
import { ref } from 'vue'

export default {
  setup() {
    const users = ref([])
    const showEditDialog = ref(false)
    const showCreateDialog = ref(false)
    const editUserForm = ref({})
    const newUserForm = ref({})

    const loadUsers = async () => {
      const response = await axios.get('http://localhost:3000/users')
      users.value = response.data
    }

    const saveUser = async () => {
      const response = await axios.put(
        `http://localhost:3000/users/${editUserForm.value.id}`,
        editUserForm.value
      )
      users.value = response.data
      showEditDialog.value = false
    }

    const createUser = async () => {
      const response = await axios.post('http://localhost:3000/users', newUserForm.value)
      users.value = response.data
      showCreateDialog.value = false
    }

    const deleteUser = async (id) => {
      await axios.delete(`http://localhost:3000/users/${id}`)
      users.value = users.value.filter((user) => user.id !== id)
    }

    const editUser = (user) => {
      editUserForm.value = { ...user }
      showEditDialog.value = true
    }

    const cancelEditUser = () => {
      showEditDialog.value = false
    }

    const cancelCreateUser = () => {
      showCreateDialog.value = false
    }

    return {
      users,
      showEditDialog,
      showCreateDialog,
      editUserForm,
      newUserForm,
      loadUsers,
      saveUser,
      createUser,
      deleteUser,
      editUser,
      cancelEditUser,
      cancelCreateUser,
    }
  },
  mounted() {
    this.loadUsers()
  },
}
</script>

在这个示例中,我们使用了axios库来发送HTTP请求,并且使用了Vue3的ref来绑定数据。这里的loadUsers函数会从API中加载用户数据,saveUser和createUser函数会保存和创建新的用户数据,deleteUser函数会删除指定的用户数据。editUser函数用于在编辑时填充表单,而cancelEditUser和cancelCreateUser函数则用于取消编辑和创建。

19-1 搭建后端(全栈看,前端可略)

专业的前端可以跳过这部分, 如果你是偏向全栈的岗位,也可尝试去搭建一下

  1. 创建一个Spring Boot项目

使用您喜欢的IDE或者命令行,创建一个新的Spring Boot项目。可以选择使用Maven或Gradle构建项目,具体取决于您的偏好。

  1. 添加依赖项

在您的Spring Boot项目中,添加以下依赖项:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>

<dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-web</artifactId>
</dependency>

<dependency>
   <groupId>com.h2database</groupId>
   <artifactId>h2</artifactId>
   <scope>runtime</scope>
</dependency>

这些依赖项将使您能够使用JPA处理数据、创建Web应用程序以及使用H2数据库。

  1. 创建实体类和数据访问对象(DAO)

在您的Spring Boot项目中,创建一个实体类和一个数据访问对象(DAO),用于处理您的数据。例如,如果您要创建一个用户实体类,可以像这样编写:

@Entity
@Table(name = "users")
public class User {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
 
    @Column(nullable = false)
    private String name;
 
    @Column(nullable = false)
    private String email;
 
    // getters and setters
}

@Repository
public interface UserRepository extends JpaRepository<User, Long> {
}

在这里,我们创建了一个名为User的实体类,它有一个ID、名称和电子邮件属性。我们还创建了一个名为UserRepository的数据访问对象,它继承自JpaRepository并处理User实体类。

  1. 创建控制器

在您的Spring Boot项目中,创建一个控制器,用于处理您的API请求。例如,如果您要创建一个用户控制器,可以像这样编写:

@RestController
@RequestMapping("/api/users")
public class UserController {
    @Autowired
    private UserRepository userRepository;
 
    @GetMapping
    public List<User> getAllUsers() {
        return userRepository.findAll();
    }
 
    @GetMapping("/{id}")
    public User getUserById(@PathVariable Long id) {
        return userRepository.findById(id)
            .orElseThrow(() -> new ResourceNotFoundException("User not found with id " + id));
    }
 
    @PostMapping
    public User createUser(@Valid @RequestBody User user) {
        return userRepository.save(user);
    }
 
    @PutMapping("/{id}")
    public User updateUser(@PathVariable Long id, @Valid @RequestBody User userDetails) {
        User user = userRepository.findById(id)
            .orElseThrow(() -> new ResourceNotFoundException("User not found with id " + id));
 
        user.setName(userDetails.getName());
        user.setEmail(userDetails.getEmail());
 
        User updatedUser = userRepository.save(user);
        return updatedUser;
    }
 
    @DeleteMapping("/{id}")
    public ResponseEntity<?> deleteUser(@PathVariable Long id) {
        User user = userRepository.findById(id)
            .orElseThrow(() -> new ResourceNotFoundException("User not found with id " + id));
 
        userRepository.delete(user);
 
        return ResponseEntity.ok().build();
    }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/422125.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Day944.度量指标 -系统重构实战

度量指标 Hi&#xff0c;我是阿昌&#xff0c;今天学习记录的是关于度量指标的内容。 很多时候在研发过程中&#xff0c;都习惯性地用“拍脑袋”的方式来看待一个事情。例如这个代码写得不好、这个自动化测试覆盖不充分、版本的发布频率太差了等等。往往只知道哪里有问题&…

后台服务异常?测试右移告警监控早知道。。。。

目录 引言 “测试右移”思想下实践步骤 什么是“测试右移” 一、收到问题反馈 二、沟通定位问题 1.服务架构 三、讨论并选定解决方案 1.讨论分析解决方案 2.选定解决方案 四、解决方案实现 1.总体方案设计 2.编写监控脚本 3.配置服务器定时任务 五、测试环境验证…

从零开始学Java之Integer底层原理探究

前言 在之前的两篇文章中&#xff0c;壹哥给大家介绍了Java中的包装类及其特点、用法&#xff0c;但是这些内容主要是停留在”怎么用“的层面&#xff0c;没有太多涉及”为什么“&#xff0c;所以接下来壹哥会给大家讲一讲Integer这个包装类的底层原理。在现在的就业环境下&am…

Apache Tomcat CVE-2020-1938 漏洞

Apache Tomcat CVE-2020-1938 漏洞简单复现 文章目录 Apache Tomcat CVE-2020-1938 漏洞简单复现实验准备实验步骤搭建环境nmap扫描漏洞端口POC代码验证漏洞修复建议 参考链接 实验准备 所选漏洞&#xff1a;Apache Tomcat远程代码执行漏洞 漏洞编号&#xff1a;CVE-2020-193…

AppArmor零知识学习三、源码介绍与下载

本文内容参考&#xff1a; AppArmor配置&#xff08;二&#xff09;_domybest_nsg的博客-CSDN博客&#xff0c; Apparmor简单学习_trap0D的博客-CSDN博客&#xff0c; 学习LSM(Linux security module)之三:Apparmor的前世今生和基本使用_wx5b7658e51ef04的技术博客_51CTO博客…

JavaEE初阶学习:文件操作

1.文件 1.认识文件 平时说的文件一般都是指存储再硬盘上的普通文件&#xff0c;形如txt&#xff0c;jpg&#xff0c;MP4&#xff0c;rar等这些文件都可以认为是普通文件&#xff0c;它们都是再硬盘上存储的。 在计算机中&#xff0c;文件可能是一个广义的概念&#xff0c;就…

【云原生|Docker】12-Docker Harbor企业级镜像管理

【云原生Docker】12-Docker Harbor企业级镜像管理 文章目录【云原生Docker】12-Docker Harbor企业级镜像管理前言Harbor简介简介架构介绍组件间说明工作原理docker logindocker pushHarbor安装版本介绍安装Http模式部署https模式部署前言 ​ 上一章节我们介绍了Docker的官方的镜…

【redis】集成到SpringBoot

集成到SpringBoot 类似于java连接mysql需要JDBC 而Java连接redis则需要 对应的 工具类 提示&#xff1a;写完文章后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录集成到SpringBoot前言一、Jedis加依赖写业务二、lettuce加依赖写业务Jedis和…

转换字符串的最少操作次数是否所有1都至少相隔k个元素重复至少k次且长度为M的模式移除指定数字得到的最大结果 得到k个黑块的最少涂色次数

转换字符串的最少操作次数&#xff08;回顾等级&#xff1a;不值得&#xff1b;已达最优解&#xff09; 来源&#xff1a;自己LeetCode刷题 usa int minimumMoves(char * s) {int szstrlen(s);int res0;for (int i0;i<sz;i){if (s[i]X){i2;res;}}return res; }是否所有1都…

Makefile实验

一、准备C程序 1、input.h #ifndef __INPUT_H #define __INPUT_Hvoid input_int(int *a, int *b);#endif 2、input.c #include <stdio.h> #include "input.h"void input_int(int *a, int *b) {printf("input two nums: ");scanf("%d %d&q…

8年经验的面试官分享Android工程师的面试秘诀

本人目前在一家知名企业担任Android高级工程师&#xff0c;工作八年来面试不过不少Android工程师求职者&#xff0c;前后累积有两三百人。在本文内容中&#xff0c;小编将结合本人的面试经验&#xff0c;给各位正在找Android开发工作的小伙伴提出一些面试前的准备建议。 一&am…

vue3通用后台管理项目

一、创建项目 1、使用vite创建名为my-vue的项目&#xff1a;npm init vite-app my-vue 2、进入到my-vue文件夹下&#xff1a;cd my-vue 3、安装依赖包&#xff1a;npm install 4、运行该项目&#xff1a;npm run dev 二、引入element-plus 1、element-plus地址&#xff1a;http…

Camunda整体架构

REST API REST API 允许您从远程应用程序或 JavaScript 应用程序使用流程引擎。&#xff08;注意&#xff1a;REST API 的文档被分解为自己的文档。&#xff09;REST API Reference | docs.camunda.org Camunda Tasklist用于人工工作流管理和用户任务的 Web 应用程序&#xff0…

UniverSeg:通用医学图像分割模型来了!

自从今年以来ChatGPT爆火和GPT-4的发布&#xff0c;一时间在大模型的潮流下&#xff0c;通用人工智能&#xff08;AGI&#xff09;也呼之欲出。随着本月初SAM和SegGPT等通用的CV大模型的提出&#xff0c;大模型和通用模型这把火也逐渐烧到的CV领域&#xff0c;特别是图像分割领…

Linux应用编程(文件IO进阶)

一、Linux 系统如何管理文件 1.1、静态文件与 inode 文件存放在磁盘文件系统中&#xff0c;并且以一种固定的形式进行存放&#xff0c;我们把他们称为静态文件。 每一个文件都必须对应一个 inode&#xff0c;inode 实质上是一个结构体&#xff0c;这个结构体中有很多的元素&a…

【C语言】初阶指针(指针运算、二级指针及指针数组)

简单不先于复杂&#xff0c;而是在复杂之后。 目录 1. 指针运算 4.1 指针-整数 1.2 指针 - 指针 1.3 指针的关系运算 2. 指针和数组 3. 二级指针 4. 指针数组 1. 指针运算 指针-整数指针-指针指针的关系运算 4.1 指针-整数 上面这个程序的作用是将数组中每个元…

【攻城狮计划】Renesas RA2E1 开发板

&#x1f6a9;WRITE IN FRONT&#x1f6a9; &#x1f50e;介绍&#xff1a;"謓泽"正在路上朝着"攻城狮"方向"前进四"&#x1f50e;&#x1f3c5;荣誉&#xff1a;2021|2022年度博客之星物联网与嵌入式开发TOP5|TOP4、2021|2022博客之星TOP10…

Scalable Recognition with a Vocabulary Tree(词汇树)

视觉单词 参考 视觉词袋&#xff08;BoVW&#xff0c;Bag of Visual Words&#xff09;模型&#xff0c;是“词袋”&#xff08;BoW&#xff0c;Bag of Words&#xff09;模型从自然语言处理与分析领域向图像处理与分析领域的一次自然推广。对于任意一幅图像&#xff0c;BoVW模…

jwt生成和解密-jose4j

jwt生成和解密-jose4j jwt的概念和生成意义在这里就不描述了&#xff0c;百度能搜到很多&#xff0c;直接上代码 官网地址 https://bitbucket.org/b_c/jose4j/wiki/Home maven <dependency><groupId>org.bitbucket.b_c</groupId><artifactId>jose4j…

【微信小程序】-- 配置uni-app的开发环境(四十八)

&#x1f48c; 所属专栏&#xff1a;【微信小程序开发教程】 &#x1f600; 作  者&#xff1a;我是夜阑的狗&#x1f436; &#x1f680; 个人简介&#xff1a;一个正在努力学技术的CV工程师&#xff0c;专注基础和实战分享 &#xff0c;欢迎咨询&#xff01; &…