vue3 教程(上)

简介: vue3 教程(上)

学 vue3 通过官方文档更详细,不过阅读本博客,可以更容易理解,且帮你速成!

  • 官方文档(记得将API风格偏好切换为 组合式 否则你学的是vue2)
    https://cn.vuejs.org/guide/introduction.html

学习前的准备

  1. 创建一个 vue3 项目,详见链接
    https://blog.csdn.net/weixin_41192489/article/details/128231728

开启 vue3 的实验性特征

在项目的 vite.config.ts 中添加 reactivityTransform: true,

添加 ts 对 vue3 实验性特征的支持

在项目的 tsconfig.json 中添加 "types": ["vue/ref-macros"]

改用 script setup

vue2 不同,vue3 的 js 代码写在 <script setup lang="ts"> 中,如

<script setup lang="ts">
let count = $ref(0);

function increment() {
  count++;
}
</script>

当然,也可以延用 vue2 的选项式风格,则代码如下:

<script>
export default {
  // `setup` 是一个专门用于组合式 API 的特殊钩子函数
  setup() {
  let count = $ref(0);
  
  function increment() {
    count++;
  }
    // 暴露 count 和 increment 到模板
    return {
      count,
      increment 
    }
  }
}
</script>

很显然, <script setup lang="ts"> 的组合式风格更简洁易用,下文都将使用组合式风格。

以下代码都在项目的 src\views\AboutView.vue 中测试,启动项目后,浏览器访问

http://127.0.0.1:5173/about 查看效果。

先将 AboutView.vue 初始化为

<template>
  <div></div>
</template>

<script setup lang="ts">
</script>

<style scoped>
</style>

定义响应式变量 $ref()

vue2中写在 data(){} 函数中的变量,vue3中写法如下

// 定义响应式变量 count,初始值为 0
let count = $ref(0);

友情提示:此处的 $ref() 用法目前处于实验性阶段,未来可能无法使用。(不过为了编码方便,下文仍会使用这个!)

目前官方的定义响应式变量的方法为 ref() 和 reactive()

ref()

ref() 能创造一种对任意值的 “引用”,并能够在不丢失响应性的前提下传递这些引用。

  • 参数可以是任意数据类型,会返回 ref 对象
    (之所以这样设计,是因为对象才是引用类型,更方便实现响应式)
import { ref } from 'vue'
// 定义响应式变量 count ,初始值为 0
const count = ref(0)
// 此时 count 是一个 ref 对象,实际内容为 { value: 0 } ,所以要想取到 0 ,需使用 count.value

使用范例

解析:当 ref 函数创建的变量名和ref属性值相同时,该变量就能获取到该节点。(ref属性是vue特有的,模板引擎解析代码时看到ref属性,就会把这个标签的vdom缓存在定义的el变量中,以后就能通过el访问该vdom了)

toRef()

基于响应式对象上的一个属性,创建对应的 ref

  • 即使源属性当前不存在,toRef() 也会返回一个可用的 ref。
  • 常用于处理可选 props
const state = reactive({
  foo: 1,
  bar: 2
})

const fooRef = toRef(state, 'foo')

// 更改该 ref 会更新源属性
fooRef.value++
console.log(state.foo) // 2

// 更改源属性也会更新该 ref
state.foo++
console.log(fooRef.value) // 3
<script setup>
import { toRef } from 'vue'

const props = defineProps(/* ... */)

// 将 `props.foo` 转换为 ref,然后传入一个组合式函数
useSomeFeature(toRef(props, 'foo'))
</script>

toRefs()

将响应式对象的属性,都变成响应式的

function useFeatureX() {
  const state = reactive({
    foo: 1,
    bar: 2
  })
  // 在返回时都转为 ref
  return toRefs(state)
}

// 可以解构而不会失去响应性
const { foo, bar } = useFeatureX()

reactive()

reactive() 会创造一个响应式代理(Proxy),传递引用时会丢失响应。

  • 参数必须是对象类型(对象、数组和 Map、Set 这样的集合类型)
import { reactive } from 'vue'

const state = reactive({ count: 0 })

function increment() {
  state.count++
}

计算属性 computed

vue2中写在 computed 中的计算属性,vue3中写法如下

import { computed } from "vue";
let firstName = $ref("朝");
let lastName = $ref("阳");
// 当 firstName 或 lastName 变化时,会触发重新计算 fullName 
let fullName = computed(() => {
  return firstName + lastName;
});
  • 计算属性中不能直接使用 reverse() 和 sort() ,因为这两个方法将变更原始数组,正确的用法是在调用这些方法之前创建一个原数组的副本[...numbers]
return [...numbers].reverse()

实用技巧:动态绑定样式

<template>
  <div>
    <div :class="classObject">你好</div>
    <button @click="bolder">加粗</button>
    <button @click="markError">标红</button>
  </div>
</template>

<script setup lang="ts">
import { computed } from "vue";

let isBold = $ref(false);
let error = $ref(false);

let classObject = computed(() => ({
  bold: isBold,
  "text-danger": error,
}));

function bolder() {
  isBold = true;
}

function markError() {
  error = true;
}
</script>

<style scoped>
.text-danger {
  color: red;
}
.bold {
  font-weight: bold;
}
</style>

定义方法 function

vue2中写在 methods 中的方法,vue3中写法如下

// 定义方法 increment,每执行一次,count 会自增 1
function increment() {
  count++;
}

内联事件

逻辑极其简单的方法也可以直接写在元素上

<button @click="count++">增加1</button>

在方法后加 () 也会被视为内联事件

<button @click="increment()">增加1</button>

此时可以给方法添加参数

<button @click="increment('参数1',‘参数2’)">按指定参数增加</button>

内联事件访问原生 DOM 事件

需使用 $event

<button @click="submit('参数1', $event)">提交</button>

或者使用内联箭头函数

<button @click="(event) => submit('参数1', event)">提交</button>

方法事件

逻辑比较复杂时,则需给事件绑定方法

<button @click="show">展示</button>

此时方法的默认参数为原生 DOM 事件 event

// event 为原生 DOM 事件
function show(event: any) {
  // 通过 event 可以获取事件相关的各种信息
  console.log(event);
  if (event) {
    // 比如触发事件的标签名称
    console.log(event.target.tagName);
  }
}

事件修饰符

  • .stop 禁止事件冒泡
<a @click.stop="doThis"></a>

效果同 event.stopPropagation()

  • .prevent 禁止事件默认行为
    效果同 event.preventDefault()

使用范例:提交表单时不再重新加载页面

<form @submit.prevent="onSubmit"></form>
  • .capture 事件采用捕获模式
    指向内部元素的事件,在被内部元素处理前,先被外部处理
<div @click.capture="doThis">...</div>
  • .passive 用于改善移动端设备的滚屏性能
    一般用于触摸事件的监听器

范例:滚动事件的默认行为 (scrolling) 将立即发生而非等待 onScroll 完成,以防其中包含 event.preventDefault()

<div @scroll.passive="onScroll">...</div>

.passive 和 .prevent 不能同时使用,因为 .passive 已经向浏览器表明了你不想阻止事件的默认行为。如果同时使用,则 .prevent 会被忽略,并且浏览器会抛出警告。

还可以只使用修饰符,不绑定方法

<form @submit.prevent></form>

链式调用事件修饰符

<a @click.stop.prevent="show"></a>

但要注意调用顺序:

@click.prevent.self 阻止元素及其子元素的所有点击事件的默认行为

@click.self.prevent 只会阻止对元素本身的点击事件的默认行为。

常规按键修饰符

.按键名 即可实现指定按键触发事件

  • 按键名的格式为 kebab-case 形式,如 PageDown 键需使用 .page-down
<!-- 按回车键时调用  submit  -->
<input @keyup.enter="submit" />
<!-- 按 a 键时调用  submit  -->
<input @keyup.a="submit" />
<!-- 按 PageDown 键时调用 submit  -->
<input @keyup.page-down="submit" />

常用的按键有以下别名

  • .enter
  • .tab
  • .delete (捕获“Delete”和“Backspace”两个按键)
  • .esc
  • .space
  • .up
  • .down
  • .left
  • .right

系统按键修饰符

  • .ctrl
  • .alt
  • .shift
  • .meta
    在 Mac 键盘上,meta 是 Command 键 (⌘)。
    在 Windows 键盘上,meta 键是 Windows 键 (⊞)。

系统按键修饰符与 keyup 事件一起使用时,该按键必须在事件发出时处于按下状态。如 keyup.ctrl 只会在你按住 ctrl 但松开另一个键时被触发,单独松开 ctrl 键将不会触发。

组合键的实现

<!-- Alt + Enter -->
<input @keyup.alt.enter="clear" />

<!-- Win + Enter  
 -->
<input @keyup.meta.enter="clear" />

<!-- Ctrl + 点击 -->
<div @click.ctrl="doSomething">Do something</div>

<!-- Ctrl + shift + a -->
<input @keyup.ctrl.shift.a="submit" />

.exact 修饰符

控制触发一个事件所需的确定组合

<!-- 当按下 Ctrl 时,即使同时按下 Alt 或 Shift 也会触发 -->
<button @click.ctrl="onClick">A</button>

<!-- 仅当按下 Ctrl 且未按任何其他键时才会触发 -->
<button @click.ctrl.exact="onCtrlClick">A</button>

<!-- 仅当没有按下任何系统按键时触发 -->
<button @click.exact="onClick">A</button>

鼠标按键修饰符

  • .left
  • .right
  • .middle
<!-- 在该div上点击鼠标左键时触发 -->
<div @mousedown.left="submit">

条件渲染 v-if v-show

多个元素需要使用 v-if 控制渲染时,可以添加 <template> 包裹

<template v-if="showDetail">
  <h1>标题</h1>
  <p>段落一</p>
  <p>段落二</p>
</template>

v-show 无法在 <template> 上使用

列表渲染 v-for

  • 既可以用 in ,也可以用 of
<div v-for="item of items"></div>
  • 可使用解构
const items = $ref([{ message: "1" }, { message: "2" }]);
 
<li v-for="({ message }, index) in items">{{ message }} {{ index }}</li>
  • 渲染对象
<li v-for="(value, key, index) in myObject">
  {{ index }}. {{ key }}: {{ value }}
</li>
  • 渲染整数
    效果为 1、2、3、…… n
<!-- n 的初值是从 1 开始而非 0 -->
<span v-for="n in 10">{{ n }}</span>
  • 多个元素需要使用 v-for 渲染时,可以添加 <template> 包裹
<ul>
  <template v-for="item in items">
    <li>{{ item.msg }}</li>
    <li class="divider" role="presentation"></li>
  </template>
</ul>

此时 key 也要写在 <template>

<template v-for="todo in todos" :key="todo.name">
  <li>{{ todo.name }}</li>
</template>
  • key 需为字符串或 number 类型,不要用对象作为 key

v-if 和 v-for 同时存在于一个元素上时

v-if 会首先被执行,但不推荐这种写法

<ul>
  <li
    v-for="user in users"
    v-if="user.isActive"
    :key="user.id"
  >
    {{ user.name }}
  </li>
</ul>

应改用 <template> 包裹

<ul>
  <template v-for="user in users" :key="user.id">
    <li v-if="user.isActive">
      {{ user.name }}
    </li>
  </template>
</ul>

双向绑定 v-model

v-model 绑定的值通常是静态的字符串 (复选框中是布尔值),使用 v-bind 可以将选项值绑定为非字符串的数据类型。

<select v-model="selected">
  <!-- 内联对象字面量 -->
  <option :value="{ number: 123 }">123</option>
</select>

单选按钮 radio

<div>Picked: {{ picked }}</div>

<input type="radio" id="one" value="One" v-model="picked" />
<label for="one">One</label>

<input type="radio" id="two" value="Two" v-model="picked" />
<label for="two">Two</label>

复选框 checkbox

单个复选框

<input type="checkbox" id="checkbox" v-model="checked" />
<label for="checkbox">{{ checked }}</label>

默认值为 true 和 false

通过以下方法可以自定义值

<input
  type="checkbox"
  v-model="toggle"
  true-value="yes"
  false-value="no" />

true-value 和 false-value 是 Vue 特有的属性,仅支持和 v-model 配套使用

多个复选框——值为数组

const checkedNames = $ref([])
 
<div>Checked names: {{ checkedNames }}</div>

<input type="checkbox" id="jack" value="Jack" v-model="checkedNames">
<label for="jack">Jack</label>

<input type="checkbox" id="john" value="John" v-model="checkedNames">
<label for="john">John</label>

<input type="checkbox" id="mike" value="Mike" v-model="checkedNames">
<label for="mike">Mike</label>

自定义复选框

下拉单选 select

<div>Selected: {{ selected }}</div>

<select v-model="selected">
  <option disabled value="">Please select one</option>
  <option>A</option>
  <option>B</option>
  <option>C</option>
</select>

v-model 的初始值不匹配任何一个选择项时,会渲染成一个“未选择”的状态。在 iOS 上,这将导致用户无法选择第一项,因为 iOS 在这种情况下不会触发 change 事件。因此,建议提供一个空值的禁用选项,如上例所示。

.lazy 修饰符

默认情况下,v-model 会在每次 input 事件后更新数据

.lazy 修饰符可以改为在每次 change 事件后更新数据

<!-- 在 "change" 事件后同步更新而不是 "input" -->
<input v-model.lazy="msg" />

.number 修饰符

让用户输入自动转换为数字

<input v-model.number="age" />

若该值无法被 parseFloat() 处理,则返回原始值。

number 修饰符会在输入框有 type=“number” 时自动启用。

.trim 修饰符

自动去除用户输入内容中两端的空格

<input v-model.trim="msg" />

生命周期–钩子函数

因 setup 执行的时间在 beforeCreate 之后,在created之前,所以需在 beforeCreate 和 created 中执行的代码,直接写在 setup 中即可,无需使用钩子函数。

onBeforeMount()

组件被挂载之前被调用

  • 已完成了其响应式状态的设置(响应式变量可用),但还没有创建 DOM 节点($refs不可用)。
  • 服务器端渲染期间不会被调用

onMounted()

在组件挂载完成后执行

  • 服务器端渲染期间不会被调用
// 通过模板引用访问一个元素
<script setup>
import { ref, onMounted } from 'vue'

const el = ref()

onMounted(() => {
  el.value // <div>
})
</script>

<template>
  <div ref="el"></div>
</template>

onBeforeUpdate()

组件即将因为响应式状态变更而更新其 DOM 树之前调用

  • 用于在 Vue 更新 DOM 之前访问 DOM 状态
  • 可以在这个钩子中更改组件的状态
  • 服务器端渲染期间不会被调用

onUpdated()

在组件因为响应式状态变更而更新其 DOM 树之后调用

  • 父组件的更新钩子将在其子组件的更新钩子之后调用
  • 服务器端渲染期间不会被调用
  • 不要在 updated 钩子中更改组件的状态,这可能会导致无限的更新循环!
  • 若需要在某个特定的状态更改后访问更新后的 DOM,请使用 nextTick() 作为替代
// 访问更新后的 DOM
<script setup>
import { ref, onUpdated } from 'vue'

const count = ref(0)

onUpdated(() => {
  // 文本内容应该与当前的 `count.value` 一致
  console.log(document.getElementById('count').textContent)
})
</script>

<template>
  <button id="count" @click="count++">{{ count }}</button>
</template>

onBeforeUnmount()

组件实例被卸载之前调用

  • 组件实例依然还保有全部的功能
  • 服务器端渲染期间不会被调用

onUnmounted()

组件实例被卸载之后调用

  • 常用于手动清理一些副作用,如计时器、DOM 事件监听器、与服务器的连接等。
  • 服务器端渲染期间不会被调用
// 页面卸载时清除计时器
<script setup>
import { onMounted, onUnmounted } from 'vue'

let intervalId
onMounted(() => {
  intervalId = setInterval(() => {
    // ...
  })
})

onUnmounted(() => clearInterval(intervalId))
</script>

目录
相关文章
|
1天前
vue3【实战】语义化首页布局
vue3【实战】语义化首页布局
7 2
|
1天前
|
存储 容器
vue3【实战】来回拖拽放置图片
vue3【实战】来回拖拽放置图片
7 2
|
1天前
|
JavaScript 开发工具 开发者
vue3【提效】使用 VueUse 高效开发(工具库 @vueuse/core + 新增的组件库 @vueuse/components)
vue3【提效】使用 VueUse 高效开发(工具库 @vueuse/core + 新增的组件库 @vueuse/components)
6 1
|
1天前
|
API
Pinia 实用教程【Vue3 状态管理】状态持久化 pinia-plugin-persistedstate,异步Action,storeToRefs(),修改State的 $patch,$reset
Pinia 实用教程【Vue3 状态管理】状态持久化 pinia-plugin-persistedstate,异步Action,storeToRefs(),修改State的 $patch,$reset
9 1
|
1天前
|
JavaScript
vue3 【提效】自动注册组件 unplugin-vue-components 实用教程
vue3 【提效】自动注册组件 unplugin-vue-components 实用教程
5 1
|
1天前
|
JavaScript 网络架构
vue3 【提效】自动路由(含自定义路由) unplugin-vue-router 实用教程
vue3 【提效】自动路由(含自定义路由) unplugin-vue-router 实用教程
8 0
vue3 【提效】自动路由(含自定义路由) unplugin-vue-router 实用教程
|
1天前
vue3 【提效】自动导入框架方法 unplugin-auto-import 实用教程
vue3 【提效】自动导入框架方法 unplugin-auto-import 实用教程
4 0
|
1天前
vue3 【提效】全局布局 vite-plugin-vue-layouts 实用教程
vue3 【提效】全局布局 vite-plugin-vue-layouts 实用教程
5 0
|
1天前
|
数据采集 JavaScript 前端开发
Vue框架的优缺点是什么
【7月更文挑战第5天】 Vue框架:组件化开发利于重用与扩展,响应式数据绑定简化状态管理;学习曲线平缓,生态系统丰富,集成便捷,且具性能优化手段。缺点包括社区规模相对小,类型支持不足(Vue 3.x改善),路由和状态管理需额外配置,SEO支持有限。随着发展,部分缺点正被克服。
7 1
|
1天前
|
JavaScript
Vue卸载eslint的写法,单独安装eslint,单独卸载eslint
Vue卸载eslint的写法,单独安装eslint,单独卸载eslint