Vue3.0基础语法

192 阅读4分钟

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了。