0
点赞
收藏
分享

微信扫一扫

vue3 教程(上)


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

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


学习前的准备

  1. 创建一个 vue3 项目,详见链接
    javascript:void(0)
  2. 开启 vue3 的实验性特征
    在项目的 vite.config.ts 中添加 ​​reactivityTransform: true,​
  3. 添加 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

使用范例

vue3 教程(上)_前端_02


解析:当 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>


举报

相关推荐

0 条评论