Vue3.0入门

134 阅读8分钟

01-vue3基本介绍

知道:vue3的现状以及它特点 大致内容:

  • Vue3的现状
  • 相关文档
  • 了解框架优点特点 具体落地:

1. Vue3的现状

2020 年 9 月 18 日发布,许多开发者还在观望。2022 年 2 月 7 日称为默认版本,意味着vue3是现在也是未来。

组件(插件)名称官方地址简介
ant-design-vueantdv.com/docs/vue/in…ant-design-vue 是 Ant Design 的 Vue 实现,组件的风格与 Ant Design 保持同步
element-pluselement-plus.gitee.io/#/zh-CNElement Plus,一套为开发者、设计师和产品经理准备的基于 Vue 3.0 的桌面端组件库
vantvant-contrib.gitee.io/vant/v3/#/z…有赞前端团队开源的移动端组件库,于 2016 年开源,已持续维护 4 年时间
Naive UIwww.naiveui.com/zh-CN/一个 Vue 3 组件库比较完整,主题可调,使用 TypeScript,不算太慢,有点意思
VueUsevueuse.org/基于composition组合api的常用集合,小兔仙项目会部分使用

2. 相关文档

  1. Vue3 中文文档 vue3js.cn/docs/zh/
  2. Vue3 设计理念 vue3js.cn/vue-composi…

3. 了解框架优点特点

  1. 首次渲染更快
  2. diff算法更快
  3. 内存占用更少
  4. 打包体积更小
  5. 更好的Typescript支持
  6. Composition API  组合API

总结:

  • 前端学习vue3是必须的,学习vue3主要学习组合API的使用。

02-vue3开发环境

掌握:使用vue-cli完成项目创建知道初始化代码含义

大致步骤:

  • 创建项目
  • 默认代码

1. 创建项目

image.png

2. 默认代码

// 1. 导入一个创建App的函数
import { createApp } from 'vue'
// 2. 导入根组件
import App from './App.vue'
// 3. 根据根组件创建vue应用实例,挂载到html的app元素上
createApp(App).mount('#app')

其他发现:

  • vue3的组件组件根标签不再是强制,支持代码片段。

总结:

  • createApp 创建vue应用实例,组件支持代码片段代码

03-vue3组合API介绍

知道:什么是选项API什么是组合API,对比它们之间的代码组织特点

大致内容:

  • 选项API与组合API
  • 它们组织代码特点

具体落地:

  1. 选项API,data 选项写数据,methods 选项写逻辑,...

    • 代码分散,一个功能会拆分在各个选项中,不利于维护不利于复用
  2. 组合API,数据、逻辑和其他都在setup内写

    • 代码集中,一个功能可以组织在一起,利于维护,可抽离可复用

总结:

  • data methods 是选项API,写在 setup 里的就是组合API
  • 如果你使用了vue3那么使用组合API一定是不错的选择。

04-vue3组合API体验

体验:组合API实现couter和显示隐藏

大致步骤:

  • 使用选项API实现
  • 使用组合API实现
  • 抽离复用逻辑

1. 选项API实现

<template>
  <img alt="Vue logo" src="./assets/logo.png" v-show="showImg" />
  <hr />
  计数器:{{ count }}
  <button @click="increment">累加</button>
  <hr />
  <button @click="toggle">显示隐藏图片</button>
</template>

<script>
// 1.选项API实现
export default {
  name: 'App',
  data() {
    return {
      count: 0,
      showImg: true
    }
  },
  methods: {
    toggle() {
      this.showImg = !this.showImg
    },
    increment() {
      this.count++
    }
  }
}
</script>

<style>
#app {
  font-family: Avenir, Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-align: center;
  color: #2c3e50;
  margin-top: 60px;
}
</style>

2. 组合API实现

<template>
  <img alt="Vue logo" src="./assets/logo.png" v-show="show" />
  <hr />
  计数器:{{ count }}
  <button @click="increment">累加</button>
  <hr />
  <button @click="toggle">显示隐藏图片</button>
</template>

<script>
// 2.组合API实现
import { ref } from 'vue'
export default {
  name: 'App',
  setup() {
    // 显示隐藏
    const show = ref(true)
    const toggle = () => {
      show.value = !show.value
    }
    // 计数器
    const count = ref(0)
    const increment = () => {
      count.value++
    }
    return { show, toggle, count, increment }
  }
}
</script>

<style>
#app {
  font-family: Avenir, Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-align: center;
  color: #2c3e50;
  margin-top: 60px;
}
</style>

3. 复用逻辑

<template>
  <button @click="toggle">显示隐藏图片</button>
  <hr />
  <img v-show="show" alt="Vue logo" src="./assets/logo.png" />
  <hr />
  计数器:{{ count }} <button @click="increment">累加</button>
  <hr />
  计数器:{{ count1 }} <button @click="increment1">累加</button>
</template>

<script>
// 2.组合API实现
import { ref } from 'vue'
// 逻辑抽离
const counters = () => {
  const count = ref(0)
  const increment = () => {
    count.value++
  }
  return { count, increment }
}
export default {
  name: 'App',
  setup() {
    // 显示隐藏
    const show = ref(true)
    const toggle = () => {
      show.value = !show.value
    }
    // 计数器
    const { count, increment } = counters()
    const { count: count1, increment: increment1 } = counters()
    return { show, toggle, count, increment, count1, increment1 }
  }
}
</script>

<style>
#app {
  font-family: Avenir, Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-align: center;
  color: #2c3e50;
  margin-top: 60px;
}
</style>

总结:

  • 体会组合API对逻辑复用和代码维护支撑,将来在项目中我们会有实践。

05-组合API-setup函数

掌握:setup使用和它的执行时机。

大致内容:

  • setup的基本用法
  • 确定它的执行时机

具体落地:

  • setup是一个新的配置选项,它是一个函数,该函数是组合API的入口函数。
  • setup函数只会在组件初始化执行一次,且在beforeCreate生命周期函数之前执行。
<template>
  <div>根组件</div>
</template>

<script>
export default {
  name: "App",
  setup() {
    // 组合API入口函数
    console.log("setup执行了");
    console.log(this);
  },
  beforeCreate() {
    console.log("beforeCreate执行了");
    console.log(this);
  },
};
</script>

总结:

  • steup在beforeCreate之前执行,函数内this是undefined不是组件实例

06-组合API-reactive函数

掌握:使用reactive函数把普通对象转换成响应式数据

使用步骤:

  • vue 中导出 reactive 函数
  • setup 函数中,使用 reactive 函数,传入一个普通对象,返回一个响应式数据对象
  • 最后 setup 函数返回一个对象,包含该响应式对象即可,模板中可使用
<template>
  <div>
    <p>姓名:{{ state.name }}</p>
    <p>年龄:{{ state.age }}</p> <button @click="state.age++">一年又一年</button>
  </div>
</template>

<script>
// 1. 导入函数
import { reactive } from 'vue'
export default {
  name: 'App',
  setup() {
    // 2. 创建响应式数据对象
    const state = reactive({ name: '张三疯', age: 18 })
    // 3. 返回数据
    return { state }
  }
}
</script>

07-组合API-ref函数

掌握:使用ref函数创建响应式数据

使用步骤:

  • vue 中导出 ref 函数
  • setup 函数中,使用 ref 函数,传入一个普通数据(简单or复杂),返回一个响应式数据
  • 最后 setup 函数返回一个对象,包含该响应式数据即可
  • 注意:使用 ref 创建的数据,js 中需要 .valuetemplate 中可省略
<template>
  <div>
    <p>
      计数器:{{ count }}
      <button @click="count++">累加1</button>
      <!-- template中使用可省略.value -->
      <button @click="increment">累加10</button>
    </p>
  </div>
</template>

<script>
// 1. 导入函数
import { ref } from 'vue'
export default {
  name: 'App',
  setup() {
    // 2. 创建响应式数据对象
    const count = ref(1)
    // js中使用需要.value
    const increment = () => {
      count.value += 10
    }
    // 3. 返回数据
    return { count, increment }
  }
}
</script>

总结:

  • ref可以把简单数据或者复杂数据转换成响应式数据,注意使用加上 .value,不过模板可省略。
  • 疑问:定义响应式数据使用 ref 还是 reactive 呢?

08-组合API-reactive和ref的选择

知道:在定义响应式数据的时候如何选择reactive和ref

开始分析:

  • reactive 可以转换对象成为响应式数据对象,但是不支持简单数据类型。

  • ref 可以转换简单数据类型位响应式数据对象,也支持复杂数据类型,但是操作的时候需要 .value

  • 它们各有特点,现在也没有最佳实践,没有明显的界限,所有大家可以自由选择。 推荐用法:

  • 如果能确定数据是对象且字段名称也确定,可使用 reactive 转成响应式数据,其他一概使用 ref 。这样就没有 心智负担 。

 // 1. 明确表单对象有两个字段
    const form = reactive({
      username: '',
      password: ''
    })

    // 2. 后台返回的数据对象
    const data = ref(null)
    const res = await axios.get('/user/100')
    data.value = res.data

总结:

  • 在定义响应式数据的函数选择上,遵循:确定字段的对象使用 reactive 其他都使用 ref 函数

09-组合API-toRefs函数

掌握:在使用reactive创建的响应式数据被展开或解构的时候使用toRefs保持响应式

大致步骤:

  • 使用 reactive 创建响应式数据,踩坑
  • 使用 toRefs 处理响应式数据,爬坑
  • toRefs 函数的作用,与使用场景
<template>
  <div>
    <p>姓名:{{ name }}</p>
    <p>年龄:{{ age }}</p>
    <button @click="age++">一年又一年</button>
  </div>
</template>

<script>
// 1. 导入函数
import { reactive, toRefs } from 'vue'
export default {
  name: 'App',
  setup() {
    // 2. 创建响应式数据对象
    const state = reactive({ name: '张三疯', age: 18 })
    // 3. 返回数据
    return { ...toRefs(state) }
  }
}
</script>

  • toRefs 函数的作用,与使用场景

    • 作用:把对象中的每一个属性做一次包装成为响应式数据
    • 响应式数据展开的时候使用,解构响应式数据的时候使用

总结:

  • 当去解构和展开 reactive 的响应式数据对象使用 toRefs 保持响应式

10-组合API-computed函数

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

大致步骤:

  • vue 中导出 computed 函数
  • setup 函数中,使用 computed 函数,传入一个函数,函数返回计算好的数据
  • 最后 setup 函数返回一个对象,包含该计算属性数据即可,然后模板内使用

11-组合API-watch函数

掌握:使用watch函数监听数据的变化

大致内容:

  • 使用 watch 监听一个响应式数据

  • 使用 watch 监听多个响应式数据

  • 使用 watch 监听响应式对象数据中的一个属性(简单)

  • 使用 watch 监听响应式对象数据中的一个属性(复杂),配置深度监听

  • 使用 watch 监听,配置默认执行

  • 使用 watch 监听一个响应式数据

<template>
  <p>计数器:{{ count }}</p>
</template>

<script>
import { ref, watch } from "vue";
export default {
  name: "App",
  setup() {
    const count = ref(0);
    // 1. 监听一个响应式数据
    // watch(数据, 改变后回调函数)
    watch(count, () => {
      console.log("count改变了");
    });
    // 2s改变数据
    setTimeout(() => {
      count.value++;
    }, 2000);
    return { count };
  },
};
</script>
  • 使用 watch 监听多个响应式数据
<template>
  <p>计数器:{{ count }}</p>
  <p>
    姓名:{{ user.name }} 性别:{{ user.info.gender }} 年龄:{{ user.info.age }}
  </p>
</template>

<script>
import { reactive, ref, watch } from "vue";
export default {
  name: "App",
  setup() {
    const count = ref(0);
    const user = reactive({
      name: "tom",
      info: {
        gender: "男",
        age: 18,
      },
    });
    // 2. 监听多个响应式数据
    // watch([数据1, 数据2, ...], 改变后回调函数)
    watch([count, user], () => {
      console.log("数据改变了");
    });
    // 2s改变数据
    setTimeout(() => {
      count.value++;
    }, 2000);
    // 4s改变数据
    setTimeout(() => {
      user.info.age++;
    }, 4000);
    return { count, user };
  },
};
</script>
  • 使用 watch 监听响应式对象数据中的一个属性(简单)
<template>
  <p>
    姓名:{{ user.name }} 性别:{{ user.info.gender }} 年龄:{{ user.info.age }}
  </p>
</template>

<script>
import { reactive, watch } from "vue";
export default {
  name: "App",
  setup() {
    const user = reactive({
      name: "tom",
      info: {
        gender: "男",
        age: 18,
      },
    });
    // 3. 监听响应式对象数据的一个数据,简单类型
    // watch(()=>数据, 改变后回调函数)
    watch(()=>user.name, () => {
      console.log("数据改变了");
    });
    // 2s改变数据
    setTimeout(() => {
      user.name = 'jack';
    }, 2000);
    // 4s改变数据
    setTimeout(() => {
      user.info.age = 60;
    }, 4000);
    return { user };
  },
};
</script>
  • 使用 watch 监听响应式对象数据中的一个属性(复杂),配置深度监听
<template>
  <p>
    姓名:{{ user.name }} 性别:{{ user.info.gender }} 年龄:{{ user.info.age }}
  </p>
</template>

<script>
import { reactive, watch } from "vue";
export default {
  name: "App",
  setup() {
    const user = reactive({
      name: "tom",
      info: {
        gender: "男",
        age: 18,
      },
    });
    // 4. 监听响应式对象数据的一个数据,复杂类型
    // watch(()=>数据, 改变后回调函数, {deep: true})
    watch(
      () => user.info,
      () => {
        console.log("数据改变了");
      },
      {
        // 开启深度监听
        deep: true,
      }
    );
    // 2s改变数据
    setTimeout(() => {
      user.info.age = 60;
    }, 2000);
    return { user };
  },
};
</script>
  • 使用 watch 监听,配置默认执行
      {
        // 开启深度监听
        deep: true,
        // 默认执行一次
        immediate: true
      }

总结:

  • watch('需要监听的数据',数据改变执行函数,配置对象) 来进行数据的侦听
  • 数据:单个数据,多个数据,函数返回对象属性,属性复杂需要开启深度监听
  • 配置对象:deep 深度监听 immediate 默认执行

12-组合API-生命周期函数

掌握:vue3的常用生命周期函数

使用步骤:

  1. 先从vue中导入以on打头的生命周期钩子函数
  2. 在setup函数中调用生命周期函数并传入回调函数
  3. 生命周期钩子函数可以调用多次

具体内容:

  • Vue3和vue2的生命周期对比
选项式API下的生命周期函数使用组合式API下的生命周期函数使用
beforeCreate不需要(直接写到setup函数中)
created不需要(直接写到setup函数中)
beforeMountonBeforeMount
mountedonMounted
beforeUpdateonBeforeUpdate
updatedonUpdated
beforeDestroyedonBeforeUnmount
destroyedonUnmounted
activatedonActivated
deactivatedonDeactivated-
<template>
  <div>生命周期函数</div>
</template>
<script>
import { onMounted } from "vue";
export default {
  name: "App",
  setup() {
    // 生命周期函数:组件渲染完毕
  
    onMounted(()=>{
      console.log('onMounted触发了')
    })

    onMounted(()=>{
      console.log('onMounted也触发了')
    })
  },
};
</script>

总结:

  • 常用的 onMounted 组件渲染完毕:发请求,操作dom,初始化图表...

13-组合API-父子通讯

掌握:在组合API中父子通讯的过程

知识梳理:

  • 组合API中的父传子数据通讯
  • 组合API中的子传父数据通讯

image.png

1. 父传子

父组件

<template>
  <div class="app">
    <p>父组件</p>
    <p>姓名:{{ students.name }}</p>
    <p>年龄:{{ students.age }}</p>
    <hr />
    <sonCom :students="students" />
  </div>
</template>

<script>
import sonCom from '@/components/sonCom.vue'
import { reactive } from 'vue'
export default {
  name: 'App',
  components: { sonCom },
  setup() {
    const students = reactive({ name: '张三疯', age: 18 })
    return { students }
  }
}
</script>

子组件

<template>
  <div class="son">
    <p>子组件</p>
    <p>姓名:{{ students.name }}</p>
    <p>年龄:{{ students.age }}</p>
  </div>
</template>
<script>
export default {
  name: 'sonCom',
  props: {
    students: {
      type: Object
    }
  }
}
</script>
<style></style>

2. 子传父

子组件

<template>
  <div class="son">
    子组件:{{ money }} <button @click="changeMoney">买手机</button>
  </div>
</template>
<script>
export default {
  name: "SonCom",
  props: {
    money: {
      type: Number,
    },
  },
  // vue3规范,自定义事件需要在这声明
  emits: ["change-money"],
  setup(props, { emit }) {
    console.log("数据money:", props.money);
    // 通过emit触发自定义事件
    const changeMoney = () => {
      emit("change-money", props.money - 3999);
    };
    return { changeMoney };
  },
};
</script>

父组件

<template>
  <div class="app">
    父组件:{{ money }}
    <hr />
    <SonCom :money="money" @change-money="money = $event" />
  </div>
</template>
<script>
import SonCom from "./SonCom.vue";
export default {
  name: "App",
  components: { SonCom },
  setup () {
    const money = ref(10000)
    return { money }
  }
};
</script>

总结:

  • 父传子,通过props传递,setup函数 第一个参数就是props
  • 子传父,通过emit函数,setup函数 第二个参数解构出emit函数,emits选项需要声明自定义事件名称

14-组合API-provide和inject函数

掌握:通过 provide 和 inject 函数实现跨级组件通讯

大致内容:

  • 后代组件 使用 祖先组件的数据,祖传后
  • 后代组件 修改 祖先组件的数据,后传祖 image.png

具体落地:

  • 祖先传递后代

    • provideinjectvue 中导入
    • provide('数据名称', 响应式数据) 提供数据的祖先组件使用
    • const data = inject('数据名称') 注入数据的后代组件使用

祖先组件

<template>
  <div class="app">
    <p>祖先组件</p>
    <p>姓名:{{ students.name }}</p>
    <p>年龄:{{ students.age }}</p>
    <hr />
    <Parent />
  </div>
</template>

<script>
import Parent from '@/components/parent.vue'
import { reactive, provide } from 'vue'
export default {
  name: 'App',
  components: { Parent },
  setup() {
    const students = reactive({ name: '张三疯', age: 199 })
    provide('students.age', students.age)
    return { students }
  }
}
</script>

父级组件

<template>
  <div>
    <p>父级组件</p>
    <hr />
    <son></son>
  </div>
</template>
<script>
import son from '@/components/son.vue'
export default {
  name: 'parentA',
  components: { son }
}
</script>
<style lang=""></style>

子级组件

<template>
  <div>
    <p>子级组件</p>
    <p>注入数据的后代组件使用:{{ data }}</p>
  </div>
</template>
<script>
import { inject } from 'vue'
export default {
  name: 'sonB',
  // 注入祖先组件提供的数据
  setup() {
    const data = inject('students.age')
    return { data }
  }
}
</script>
<style lang=""></style>

后代传递祖先

  • provide('数据名称', 修改函数) 提供函数的祖先组件使用
  • const changeMoney = inject('数据名称') 注入函数的后代组件使用
  • 遵循:数据由谁定义,由谁修改。

祖先组件

<template>
  <div class="app">
    <p>祖先组件</p>
    <p>姓名:{{ students.name }}</p>
    <p>年龄:{{ students.age }}</p>
    <hr />
    <Parent />
  </div>
</template>

<script>
import Parent from '@/components/parent.vue'
import { reactive, provide } from 'vue'
export default {
  name: 'App',
  components: { Parent },
  setup() {
    const students = reactive({ name: '张三疯', age: 199 })
    // 提供数据给后代
    provide('students.age', students.age)
    // 提供函数给后代
    provide('change', () => {
      students.age -= 99
    })
    return { students }
  }
}
</script>

子级组件

<template>
  <div>
    <p>子级组件</p>
    <p>注入数据的后代组件使用:{{ data }}</p>
    <button @click="change">子级组件age</button>
  </div>
</template>
<script>
import { inject } from 'vue'
export default {
  name: 'sonB',
  // 注入祖先组件提供的数据
  setup() {
    const data = inject('students.age')

    // 注入祖先组件提供的函数
    const change = inject('change')

    return { data, change }
  }
}
</script>
<style lang=""></style>

总结:

  • 使用 provide 提供数据,使用 inject 注入数据。

    • 可以传递数据(使用数据),可以传递函数(修改数据)

15-组合API-ref属性

掌握:使用 ref 属性获取DOM元素或者组件实例

大致内容:

  • 通过 ref 属性获取DOM元素
  • 通过 ref 属性获取组件实例

1. 通过 ref 属性获取DOM元素

<template>
  <div class="app">
    <!-- 2. 使用ref属性绑定响应式数据 -->
    <h1 ref="refH">App组件</h1>
  </div>
</template>

<script>
import { ref, onMounted } from 'vue'
export default {
  name: 'App',
  setup() {
    // 1. 声明响应式数据并返回给模板使用
    const refH = ref(null)

    // 3. 组件渲染后查看dom
    onMounted(() => {
      console.log(refH.value)
    })

    return { refH }
  }
}
</script>

2. 通过 ref 属性获取组件实例

<template>
  <div class="app">
    <!-- 2. 使用ref属性绑定响应式数据 -->
    <h1 ref="refH">App组件</h1>
    <son ref="refCom"/>
  </div>
</template>

<script>
import { ref, onMounted } from 'vue'
import son from '@/components/son.vue'
export default {
  name: 'App',
   components: { son },
  setup() {
    // 1. 声明响应式数据并返回给模板使用
    const refCom = ref(null)

    // 3. 组件渲染后查看dom
    onMounted(() => {
      console.log(refCom.value)
       refCom.value.show()
    })

    return { refCom }
  }
}
</script>

总结:

  • 通过ref函数创建一个数据,给标签或者组件使用ref属性绑定这个数据,组件渲染后可获取dom或者组件实例。

16-vue3的一些破坏性改变

了解:一些不兼容vue2的一些改变

参考文档:非兼容的变更

不再支持:

  • 过滤器 filter

  • 同步修饰符 :visible.sync="dialogFormVisible"

    怎么替代:

    • 函数(过滤器)
    • v-model (支持多个数据双向绑定)

    总结:

    • vue3移除了过滤器,sync修饰符,项目中有实践。

17-vue3综合案例

> 掌握:axios 和 组合API 配合开发
-   需求说明
    -   列表渲染
    -   删除数据
    -   使用组合API实现

image.png

  • 克隆代码 git@gitee.com:zhoushugang/vue3-case.git
<template>
  <div class="app">
    <el-table :data="list" border>
      <el-table-column label="ID" prop="id"></el-table-column>
      <el-table-column label="姓名" prop="name" width="150"></el-table-column>
      <el-table-column label="籍贯" prop="place"></el-table-column>
      <el-table-column label="操作" width="100">
        <template v-slot="{ row }">
          <el-button type="text" @click="del(row.id)" align="center">删除</el-button>
        </template>
      </el-table-column>
    </el-table>
  </div>
</template>
<script>
import { onMounted, ref } from 'vue'
import axios from 'axios'
export default {
  name: 'App',
  setup() {
    const list = ref([])
    const getList = async () => {
      const result = await axios.get('/list')
      console.log(result)
      list.value = result.data
    }
    onMounted(() => {
      getList()
    })
    const del = async id => {
      // console.log('点击删除了' + id)
      
      await axios.delete(`/del?id=${id}`)
      getList()
    }
    return { list, del }
  }
}
</script>
<style>
.app {
  width: 980px;
  margin: 100px auto 0;
}
</style>