×
  • Web前端首页
  • Vue3
  • 小白学习 Vue3技术要点:创建项目、ref、组件、插槽、数据传递

小白学习 Vue3技术要点:创建项目、ref、组件、插槽、数据传递

作者:Terry2024.10.29来源:Web前端之家浏览:68评论:0
关键词:Vue3

小白学习 Vue3技术要点:创建项目、ref、组件、插槽、数据传递

今天来分享下小白如何入学vue3,我们简单来介绍一些常见的应用,供大家参考学习。废话不多说,走起。

一、创建项目

1、安装node

Node.js的下载安装:https://nodejs.org/en

2、安装淘宝镜像源

#查看源地址npm config get registry#设置源地址npm config set registry https://registry.npmmirror.com

3、创建一个vue应用

参考文档
https://cn.vuejs.org/guide/quick-start.html

npm create vue@latest

这里我们去到自己的工作目录下面,然后执行,上面的命令,这里我全部选择否,后面要用到再说,启动发现要vite,那么我这里是直接在该项目下面安装

npm install vite --save-dev

当然全局安装更好

npm install -g vite

image.png

4、用npm init vite@latest来创建项目

npm init vite@latest

image.png

我这里选的是Vue和JavaScript
然后执行如下命令即可启动

  cd vue02
  npm install
  npm run dev

image.png

可以看到这两个命令创建的页面还是有点区别的,本人比较喜欢第二种

npm init vite@latest

5、总之,执行下面的命令即可

npm init vite@latest
  cd 项目  npm install  npm run dev

参考:https://cn.vuejs.org/guide/scaling-up/tooling.html
然后用开发工具打开即可,vscode或者IDEA

二、基础知识点

我们从官网就可以学到所有的内容,这里只是自我学习列出一些关键知识点,我们这里的学习都用的是组合式API,具体有什么区别可以见官网
https://cn.vuejs.org/

1、通过ref()声明响应式状态

<script setup>import  { ref } from 'vue'const count = ref(0)</script><template>
  <div>
        <div @click="count++">点我增加{{count}}</div>
  </div></template><style scoped></style>

2、模版语法

文本插值、原始HTML、Attribute 绑定、JavaScript 表达式等

<script setup>import  { ref } from 'vue'const count = ref(0)const msg = ref('文本插值')const rawHtml = ref('<div style="color:red">原始HTML</div>')const xiaolin = ref('xiaolin')const name = ref('pidan')</script><template>
  <div>
        <div @click="count++">点我增加{{count}}</div>
        <div>{{msg}}</div>
        <p>Using text interpolation: {{ rawHtml }}</p>
        <p>Using v-html directive: <span v-html="rawHtml"></span></p>
        <!-- v-bind可以简写为:id-->
        <div v-bind:id="xiaolin" :name="name">Attribute 绑定</div>
        使用 JavaScript 表达式        <div>{{count+100}}</div>
  </div></template><style scoped></style>

image.png

更多模版语法教程见
https://cn.vuejs.org/guide/essentials/template-syntax.html

3、计算属性

模板中的表达式虽然方便,但也只能用来做简单的操作。如果在模板中写太多逻辑,会让模板变得臃肿,难以维护。

import  { ref ,computed } from 'vue'const count = ref(0)
const publishedBooksMessage = computed(() => {    console.log("计算过程执行了")  return count.value+1;
})
...
<div @click="count++">点我增加{{count}}</div>
<div>计算属性的值1={{publishedBooksMessage}}</div>
<div>计算属性的值2={{publishedBooksMessage}}</div>

可以看到,count的变化会导致publishedBooksMessage的变化,并且页面初始化的时候,计算属性也只计算了一次,这个也是跟方法的区别,方法的话如果你div里面用了两次,那么肯定会执行两次,具体可以看
https://cn.vuejs.org/guide/essentials/computed.html

4、侦听器

计算属性允许我们声明性地计算衍生值。然而在有些情况下,我们需要在状态变化时执行一些“副作用”:例如更改 DOM,或是根据异步操作的结果去修改另一处的状态。

import  { ref ,computed,watch  } from 'vue'// 可以直接侦听一个 ref
watch(count, async (newQuestion, oldQuestion) => {    console.log("newQuestion="+newQuestion)    console.log("oldQuestion="+oldQuestion)
})
...
<div @click="count++">点我增加{{count}}</div>
...

具体看
https://cn.vuejs.org/guide/essentials/watchers.html

5、模版引用

虽然 Vue 的声明性渲染模型为你抽象了大部分对 DOM 的直接操作,但在某些情况下,我们仍然需要直接访问底层 DOM 元素。要实现这一点,我们可以使用特殊的 ref attribute:

<script setup>import { useTemplateRef, onMounted } from 'vue'// 第一个参数必须与模板中的 ref 值匹配const input = useTemplateRef('my-input')

onMounted(() => {
  input.value.focus()
})</script><template>
  <input ref="my-input" /></template>

更多参考:https://cn.vuejs.org/guide/essentials/template-refs.html

组件上的 ref

模板引用也可以被用在一个子组件上。这种情况下引用中获得的值是组件实例:
使用了<script setup>的组件是默认私有的:一个父组件无法访问到一个使用了 <script setup>的子组件中的任何东西,除非子组件在其中通过 defineExpose 宏显式暴露:

<script setup>import { ref } from 'vue'const count = ref(0)function haha(){
    count.value++;
}
defineExpose({
  count,
  haha
})</script><template>{{count}}</template>

然后父组件

<script setup>import { ref,useTemplateRef } from 'vue'import HelloWorld from './components/HelloWorld.vue'const hello = useTemplateRef("helloWorld");function add(){    console.log("hello.count",hello.value.count)   // hello.value.haha();
    hello.value.count++;
}</script><template>
 <HelloWorld ref="helloWorld"></HelloWorld>
 <button @click="add()">点击</button></template>

上面可以通过子组件的ref调用方法以及修改值。这里不需要再解包,这个点有点奇怪!

6、组件的定义和使用

定义组件

组件允许我们将 UI 划分为独立的、可重用的部分,并且可以对每个部分进行单独的思考。

<script setup>import { ref } from 'vue'const count = ref(0)</script><template>
  <button @click="count++">You clicked me {{ count }} times.</button></template>

使用组件

<script setup>import HelloWorld from './components/HelloWorld.vue'</script><template>
 <HelloWorld></HelloWorld></template>

参考:https://cn.vuejs.org/guide/essentials/component-basics.html

三、组件进阶知识点

我们在上面已经学过来组件的基本定义和使用,下面来学一些组件的高级用法,具体可见官网
https://cn.vuejs.org/guide/components/registration.html

1、父组件向子组件传递参数

这里用的是props,一个组件需要显式声明它所接受的 props,这样 Vue 才能知道外部传入的哪些是 props
在子组件中定义

<script setup>const props = defineProps(['foo'])</script><template>
  <div>{{props.foo}}</div></template>

此时,父组件只需要

<script setup>import HelloWorld from './components/HelloWorld.vue'</script><template>
 <HelloWorld foo="123"></HelloWorld></template>

即可把123传过去,当然可以用:foo=”foo”动态变化。

2、子组件向父组件传递参数

这个要用组件事件
在组件的模板表达式中,可以直接使用 $emit 方法触发自定义事件 (例如:在 v-on 的处理函数中):

<script setup></script><template><button @click="$emit('someEvent','123')">Click Me</button></template>

然后在父组件中

<script setup>import HelloWorld from './components/HelloWorld.vue'function callback(a){    console.log(a);
}</script><template>
 <HelloWorld @some-event="callback"></HelloWorld></template>

这样就可以把123从子组件传递到父组件了。

声明触发的事件

#我们在 <template> 中使用的 $emit 方法不能在组件的 <script setup> 部分中使用,但 defineEmits() 会返回一个相同作用的函数供我们使用:<script setup>const emit = defineEmits(['some-event'])function send(){
    emit("some-event","123")
}</script><template><button @click="send()">Click Me</button></template>

父组件一样可以拿到数据。

3、通过组件 v-model来实现双向绑定传值

v-model 可以在组件上使用以实现双向绑定。
子组件

<script setup>const model = defineModel()</script><template>
  <div>子组件中的值: {{ model }}</div>
  <button @click="model++">子组件中的按钮</button></template>

父组件

<script setup>import { ref } from 'vue'import HelloWorld from './components/HelloWorld.vue'const countModel =ref(0)</script><template>
 <HelloWorld v-model="countModel"></HelloWorld>
 <div>父组件中的值{{countModel}}</div>
  <button @click="countModel++">父组件中的按钮</button></template>

效果是,我们不管点击父组件中的按钮还是子组件中的按钮修改了值,父子都是同时变化。
感叹:这方法好啊
https://cn.vuejs.org/guide/components/v-model.html

4、插槽

在某些场景中,我们可能想要为子组件传递一些模板片段,让子组件在它们的组件中渲染这些片段。此时就需要用到插槽slot.
官方文档:https://cn.vuejs.org/guide/components/slots.html

子组件

<script setup></script><template><button class="fancy-btn">
  <slot></slot> <!-- 插槽出口 --></button></template>

父组件

<script setup>import { ref } from 'vue'import HelloWorld from './components/HelloWorld.vue'const count =ref(0)</script><template>
 <HelloWorld>
    我是插槽内容{{count}} </HelloWorld>
  <button @click="count++">add</button></template>

点击父组件的add,插槽内容是会变化的应为,插槽内容可以访问到父组件的数据作用域,因为插槽内容本身是在父组件模板中定义的。

父组件模板中的表达式只能访问父组件的作用域;子组件模板中的表达式只能访问子组件的作用域。

匿名插槽

上面的例子就是你们插槽,也是默认插槽

具名插槽

插槽可以带有名字
子组件

<script setup></script><template><div class="container">
  <header>
    <slot name="header"></slot>
  </header>
  <main>
    <slot></slot>
  </main>
  <footer>
    <slot name="footer"></slot>
  </footer></div></template>

父组件

<script setup>import HelloWorld from './components/HelloWorld.vue'</script><template>
 <HelloWorld>
    <template  v-slot:header>
       <h1>具名插槽显示内容header</h1>
     </template>

     <template #default>
       默认插槽显示内容     </template>

     <template #footer>
       <p>具名插槽显示内容footer</p>
     </template>
 </HelloWorld></template>

v-slot 有对应的简写 #,因此<template v-slot:header>可以简写为<template #header>。其意思就是“将这部分模板片段传入子组件的 header 插槽中”。

当一个组件同时接收默认插槽和具名插槽时,所有位于顶级的非<template> 节点都被隐式地视为默认插槽的内容。所以上面也可以写成:

<script setup>import HelloWorld from './components/HelloWorld.vue'</script><template>
 <HelloWorld>123    <template  v-slot:header>
       <h1>具名插槽显示内容header</h1>
     </template>

       默认插槽显示内容     <template #footer>
       <p>具名插槽显示内容footer</p>
     </template>
 </HelloWorld></template>

插槽获取子组件的内容

插槽内容可以访问到父组件的数据作用域,因为插槽内容本身是在父组件模板中定义的,但是我们假设想用子组件的内容来渲染插槽怎么办呢,此时就需要用作用域插槽。

我们也确实有办法这么做!可以像对组件传递 props 那样,向一个插槽的出口上传递 attributes:

<script setup>import { ref } from 'vue'const greetingMessage = ref("子组件的数据")</script><template><slot :text="greetingMessage" :count="1"></slot></template>

当需要接收插槽 props 时,默认插槽和具名插槽的使用方式有一些小区别。下面我们将先展示默认插槽如何接受 props,通过子组件标签上的 v-slot 指令,直接接收到了一个插槽 props 对象:

<script setup>import HelloWorld from './components/HelloWorld.vue'</script><template>
 <HelloWorld v-slot="slotProps">
    {{slotProps.text}},{{slotProps.count}} </HelloWorld></template>

具名插槽其实也差不多

<script setup>import { ref } from 'vue'const greetingMessage = ref("子组件的数据")</script><template><slot name="abc" :text="greetingMessage" :count="1"></slot></template>

父组件

<script setup>import HelloWorld from './components/HelloWorld.vue'</script><template>
 <HelloWorld #abc="slotProps">
    {{slotProps.text}},{{slotProps.count}} </HelloWorld></template>

5、依赖注入

通常情况下,当我们需要从父组件向子组件传递数据时,会使用 props。想象一下这样的结构:有一些多层级嵌套的组件,形成了一棵巨大的组件树,而某个深层的子组件需要一个较远的祖先组件中的部分数据。在这种情况下,如果仅使用 props 则必须将其沿着组件链逐级传递下去,这会非常麻烦。
参考文档:https://cn.vuejs.org/guide/components/provide-inject.html

provide 和 inject 可以帮助我们解决这一问题 。一个父组件相对于其所有的后代组件,会作为依赖提供者。任何后代的组件树,无论层级有多深,都可以注入由父组件提供给整条链路的依赖。

Provide (提供)

<script setup>import { inject } from 'vue'const message = inject('message')</script><template>{{message}}</template>

Inject (注入)

<script setup>import { provide } from 'vue'import HelloWorld from './components/HelloWorld.vue'provide('message','hello!')</script><template>
 <HelloWorld></HelloWorld></template>

如果注入的是个ref

<script setup>import { ref,provide } from 'vue'import HelloWorld from './components/HelloWorld.vue'const count = ref(0)

provide('message',count)</script><template>
 <HelloWorld></HelloWorld>
 <button @click="count++">点击</button></template>

父组件值变化后,子组件中也会同时变化。

子组件修改值,父组件也会变化

<script setup>import { ref,provide } from 'vue'import HelloWorld from './components/HelloWorld.vue'const count = ref(0)

provide('message',count)</script><template>
 {{count}} <HelloWorld></HelloWorld>
 <button @click="count++">点击</button></template>

思考:这岂不是也是一种父子之间传递值的好方法?

四、有用的API

1、生命周期钩子

参考:https://cn.vuejs.org/api/composition-api-lifecycle.html

2、nextTick()

等待下一次 DOM 更新刷新的工具方法。

当你在 Vue 中更改响应式状态时,最终的 DOM 更新并不是同步生效的,而是由 Vue 将它们缓存在一个队列中,直到下一个“tick”才一起执行。这样是为了确保每个组件无论发生多少状态改变,都仅执行一次更新。

nextTick() 可以在状态改变后立即使用,以等待 DOM 更新完成。你可以传递一个回调函数作为参数,或者 await 返回的 Promise。

<script setup>import { ref, nextTick } from 'vue'const count = ref(0)async function increment() {
  count.value++  // DOM 还未更新
  console.log(document.getElementById('counter').textContent) // 0

  await nextTick()  // DOM 此时已经更新
  console.log(document.getElementById('counter').textContent) // 1}</script><template>
  <button id="counter" @click="increment">{{ count }}</button></template>

参考:https://cn.vuejs.org/api/general.html#nexttick

所有api见:https://cn.vuejs.org/api/

五、相关总结

父子组件数据传递的方法

  • 1、props(父->子)

  • 2、emit(子->父)

  • 3、依赖注入(父<->子)

  • 4、组件引用(父<->子)

  • 5、v-model(父<->子)

大家可以参考下吧,希望能够帮助到大家,有问题可以留言或者加QQ群讨论。

您的支持是我们创作的动力!
温馨提示:本文作者系Terry ,经Web前端之家编辑修改或补充,转载请注明出处和本文链接:
https://jiangweishan.com/article/jsdfjxuf23423fnv.html

网友评论文明上网理性发言 已有0人参与

发表评论: