Featured image of post Vue3基础

Vue3基础

vue3基础

Vue3 (vue3js.cn)

官网:[简介 | Vue.js (vuejs.org)](https://cn. vuejs.org/guide/introduction.html)

可以使用CDN

可以用源码

使用html界面,vscode导入vue插件,简单学习vue语法

hello word



## v-html及v-text



## v-if及v-else、v-show

​```HTML
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>

    <!-- CDN -->
    <script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>

</head>
<body>

    <div id="app">
<!-- v-if 是 Vue.js 中用于条件性地渲染一块内容,内容只会在指令的表达返回true值的时候被渲染。
v-else 可用于提供条件为flase时的替代内容。
 
功能:当 age 的值大于 18 时,页面会显示“成年人”;
否则,显示“未成年人”。初始值设定为 18,因此会显示“未成年人” -->
        <!-- v-if -->
        <h1 v-if="age > 18">成年人</h1>
        <h1 v-else>未成年人</h1>
        <h1 v-show="ok">hello!</h1>


    </div>

    <script>
        const app= {
            data() {
                return{
                    age:19,
                }
            }
        }
        Vue.createApp(app).mount('#app');
    </script>


    
</body>
</html>

v-for

基于数组来渲染一个列表,需要 使用 it em in items 形式的特殊语法,其中 items 是源数据数组,而 item 则是被迭代的数组元素的别名。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>

    <!-- CDN -->
    <script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>

</head>

<body>

    <div id="app">
        <ul>
            <li v-for="(item,index) in list"
            :key="index">{{item}}</li>
        </ul>
    </div>
    
    <script>
        const app = {
            data(){
                return{
                    list:['张三','关于','刘备']
                }
            }
        }
        Vue.createApp(app).mount('#app');
    </script>

<!-- vue应用app1 -->
<div id="app1">
        <ul>
            <!-- 不加index,直接使用数组的索引作为key,但是如果元素顺序改变,那么会丢失渲染效果或者状态
             key可以为每个渲染的元素提供一个唯一表示,[动态更新列表]的时候可以使用-->
            <li v-for="item in items"
            :key="index">{{item}}</li>
        </ul>
    </div>
    
    <script>
        const app1 = {
            data(){
                return{
                    // 数组多个元素
                    items:['张三','关于','刘备']
                }
            }
        }
        Vue.createApp(app1).mount('#app1');
        // 挂载app1
    </script>


<!-- key的功能:为每一个渲染元素提供唯一标识,动态更新列表 
    购物车案例-->
    <!-- 当商品被删除或添加时,Vue 可以通过 id 快速识别哪些商品发生了变化,
     [从而只更新这些特定的元素,而不是重新渲染整个列表。]
     这提高了性能并减少了潜在的错误(例如,状态丢失)。 -->
<ul id="app2">
    <li v-for="item in cart" 
    :key="item.id">{{ item.name }}</li>
    <!-- 可以.属性获取对象的不同属性 -->
</ul>

<script>
    const app2 ={
        data() {
            return {
                // 数组包含对象[{"name": "Alice"}, {"name": "Bob"}]【js语法称对象】
                cart: [
                    { id: 1, name: '苹果' },
                    { id: 2, name: '香蕉' },
                    { id: 3, name: '橙子' }
                ]
            }
        }
    }
    Vue.createApp(app2).mount('#app2');
    // vue的应用app2,用mount挂载app2
</script>

</body>

</html>

维护状态

  • 当 Vue 正在更新使用 v-for 渲染的元素列表时,它默认使用“就地更 新”的策略。
  • 如果数据项的顺序被改变,Vue 将不会移动 DOM 元素 来匹配数据项的顺序,而是就地更新每个元素,并且确保它们在每 个索引位置正确渲染。
  • 为了给 Vue 一个提示,以便它能跟踪每个节点的身份,从而重用和 重新排序现有元素,你需要为每项提供一个唯一的 key attribute:

也就是购物车的例子。

事件处理

v-on、内联处理器(事件传递参数)

v-on监听事件

v-on 指令 (通常缩写为 @ 符号) 来监听 DOM 事件,并在触发事件时执行一些 JavaScript。用法为 v-on:click="methodName" 或 使用快捷方式 @click="methodName

事件处理方法

  • 然而许多事件处理逻辑会更为复杂,所以直接把 JavaScript 代码写 在 v-on 指令中是不可行的。因此 v-on 还可以接收一个需要调用的方法。

内联处理器中的方法(事件传递参数)

  • 这是官方的翻译称呼,其实我们可以直接叫他 “事件传递参数”
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <style>
        .status {
            width: 100px;
            height: 100px;
            line-height: 100px;
            text-align: center;
            background-color: orange;
            color: #fff;
        }
    </style>
</head>
<body>

    <!-- v-on监听dom事件,并在触发事件的时候执行一些js -->
    <div id="app">
        <div class="status" v-on:click="methodName">点我</div>
        <p></p>
        <div class="status" @click="methodName1">点我缩写的1</div>

        <button @click="counter += 1">加1</button>
        <p>当前计数: {{ counter }}</p>
        <p>---内联处理器(事件传递参数)</p>
        <button @click="say('hi')">say hiokok</button>


    </div>

    <script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
    <script>
        const app = {
            methods: {  // 记住为methods
                methodName() {
                    console.log('我被点了一下');
                },
                methodName1() {
                    console.log('我被点了一下');
                },
                say(message){
                    alert(message)
                }

            },
            data() {
                return {
                    counter: 0
                }
            }
            
        }
        Vue.createApp(app).mount('#app');
    </script>

</body>
</html>

表单输入绑定

v-model

  • 用 v-model 指令在表单 元素上创 建双向数据绑定。它会根据控件类型自动选取正确的方法来更新元素。
  • 用起来非常神奇
  • 但 v-model 本质上不过是语法糖。它负责监听用 户的输入事件来更新数据,并在某种极端场景下进行一些特殊处理。

语法糖

  • v-model 被称为“语法糖”,这意味着它为你简化了代码的写法。实际上,v-model 处理了事件监听和数据更新,背后使用了相应的方法(如 input 事件)来同步数据。
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>

    <!-- CDN -->
    <script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
</head>

<body>
<!-- v-model 在input textarea select元素上进行数据绑定,-->
    <div id = 'app'>
        <input type="text" v-model = "account" @keyup = "change">
        <p>当前账户: {{ account }}</p>
        <!-- 每次在输入框中输入内容时,account 的值会更新 -->
    </div>

    <script>
        const app = {
            data(){
                return {
                    account:'zs'
                }
            },
            methods:{
                change(){
                    console.log(this.account)
                }
            }
        }
        Vue.createApp(app).mount('#app');
    </script>
    
</body>

</html>

v-bind

v-bind能动态绑定html标签中的属性,在这个例子中,当vue实例中的值变化的时候,html标签中的id值也会随着动态变化?

ChatGPT

是的,正如你所描述的,当 Vue 实例中的 dynamicId 值发生变化时,使用 v-bind 绑定的 HTML 标签中的 id 属性值也会自动更新。这是 Vue 的响应式特性之一。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>

    <!-- CDN -->
    <script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
</head>

<body>
    <div id="app">
      
        <!-- 插值表达式 -->
        <p>  {{msg}} - {{title}} - {{price}} 123</p>
        <!-- 指令v-html,v-text -->
         <!-- 指令v-html和v-text是将标签中的内容进行覆盖 
        功能:为了输出原始的html,需要使用v-html指令
         -->
        <p v-html="msg">我被覆盖了</p>
        <p v-text="title">我也被覆盖了</p>
<!-- 
v-bind 
v-bind 将 Vue 实例中的数据动态绑定到 HTML 元素的属性,而不是使用 Mustache 语法。
-->
        <!-- 此处将id属性绑定为“动态id”与vue实例中的值绑定 -->
        <p v-bind:id = "dynamicId">我是v-bind</p>

        <!-- v-html输出html标签渲染的内容,显示在页面上 -->
        <p v-html="rawhtml">我被覆盖了</p>
        <!-- v-text输出原始的html标签代码,显示在页面上
            也就是输出文本 -->
        <p v-text="rawhtml">我也被覆盖了</p>

    </div>

    <script>
        Vue.createApp({
            data() {
                return {
                    msg: 'hello vue3',
                    title: 'one piece',
                    price: '$163',

                    rawhtml:"<a href='https://www.baidu.com'>百度</a>",

                    dynamicId:1001
                }
            }
        }).mount('#app')
    </script>
    
</body>
</html>

修饰符.lazy

  1. 默认行为
    • 默认情况下,v-model 会在每次 input 事件触发后将输入框的值与数据同步。这意味着每当用户在输入框中输入内容时,Vue 实例中的数据会实时更新。
  2. .lazy 修饰符
    • 使用 .lazy 修饰符后,数据将会在 change 事件触发时进行同步,而不是在每次 input 事件时。这适用于希望在用户完成输入并离开输入框时才更新数据的场景。

change事件

  • 只有在输入框失去焦点(例如,点击其他地方
  • 或者用户按下“Enter”键等触发 change 事件时,message 的值才会更新。

可以更控制何时更新数据,避免频繁的更新操作。

第二个vueapp1

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>

    <!-- CDN -->
    <script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
</head>

<body>
<!-- v-model 在input textarea select元素上进行数据绑定
 
此处account有初始值zs,输入框输入内容会改变account变量的值,进行绑定了-->
    <div id = 'app'>
        <input type="text" v-model = "account" @keyup = "change">
        <p>当前账户: {{ account }}</p>
        <!-- 每次在输入框中输入内容时,account 的值会更新 -->
    </div>

    <script>
        const app = {
            data(){
                return {
                    account:'zs'
                }
            },
            methods:{
                change(){
                    console.log(this.account)
                }
            }
        }
        Vue.createApp(app).mount('#app');
    </script>

<!-- 第二个vueapp 对输入框的内容与 message 变量绑定。
 输入框内容和Vues实例的data()函数的message变量进行绑定。
在输入框中输入内容,message的值会自动更新,p标签也显示message的值

。-->
    <div id="app1">
        <!-- <input v-model="message" placeholder="edit me" /> -->
        <input v-model.lazy="message" placeholder="edit me" />

        <p>Message is: {{ message }}</p>
        

    </div>

    <script>
        const app1 = {
            data() {
                return {
                    message:""
                }
            }
        }
    Vue.createApp(app1).mount('#app1');
    </script>
    
    
</body>

</html>

修饰符.trim

过滤用户输入的首尾空白字符

报错

  • vue.global.js:2260 [Vue warn]: Property “methodName1” was accessed during render but is not defined on instance. at
  • 属性"methodName1"在渲染期间内被访问了,但是没有自傲实例中定义。

image-20241016110010727

image-20241016110015796

调用了methodNme1但是没有写该方法导致的。

hello

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <style>
        .status {
            width: 100px;
            height: 100px;
            line-height: 100px;
            text-align: center;
            background-color: orange;
            color: #fff;
        }
    </style>
</head>
<body>

    <!-- v-on监听dom事件,并在触发事件的时候执行一些js -->
    <div id="app">
        <div class="status" v-on:click="methodName">点我</div>
        <p></p>
        <div class="status" @click="methodName1">点我缩写的1</div>

        <button @click="counter += 1">加1</button>
        <p>当前计数: {{ counter }}</p>
        <p>---内联处理器(事件传递参数)</p>
        <button @click="say('hi')">say hiokok</button>


    </div>

    <script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
    <script>
        const app = {
            methods: {  // 记住为methods
                methodName() {
                    console.log('我被点了一下');
                },
                methodName1() {
                    console.log('我被点了一下');
                },
                say(message){
                    alert(message)
                }

            },
            data() {
                return {
                    counter: 0
                }
            }
            
        }
        Vue.createApp(app).mount('#app');
    </script>

</body>
</html>

vue中js表达式

  • Vue 允许在模板中使用单个 JavaScript 表达式进行数据绑定。

    但不能使用多条语句或复杂的控制结构。

    这些表达式在当前 Vue 实例的数据作用域下被解析。

    例如:

  1. JavaScript 表达式
    • {{ number + 1 }}:将 number 增加 1。
    • {{ ok ? 'YES' : 'NO' }}:根据 ok 的布尔值返回 'YES''NO'
    • {{ message.split('').reverse().join('') }}:将 message 字符串反转。
  2. 限制
    • 但每个绑定只能包含单个表达式,不能是语句
    • 例如,以下示例无效:
      • {{ vara = 1 }}:这是一个赋值语句,不是表达式。
      • if (ok) return message:这是一个控制结构,也不是表达式。
  3. 条件逻辑使用三元表达式
    • 对于条件逻辑,你需要使用三元运算符(? :),而不能使用 if 语句,因为 if 是语句,不会返回值。

单文件组件

Vue单文件组件Single File Component,*.Vue文件,缩写为SFC

通常以 .vue 为扩展名。它将 Vue 组件的模板、逻辑和样式封装在一个文件中,使得组件的结构更加清晰和易于管理。

一个 Vue 单文件组件通常包含三个部分:<template><script><style>

<template>

  • 这一部分定义了组件的 HTML 模板。在这里,你可以使用 Vue 的指令和语法来绑定数据和事件。





主组件

```JS

<template>
<!-- 如何在主组件中加载组件? -->
    <div>
        <h1>我的主应用</h1>
        <!-- 3.显示组件 -->
        <Zujian-vue></Zujian-vue>
    </div>
</template>

<script>

// 1.引入
import Zujian from './zujian.vue';

export default {
    // 2.挂载组件
    components: {
        Zujian
    }
};

</script>
<style scoped>
    h3{
        color:rgb(5, 0, 99);
    }
</style>

创建Vue项目,运行单文件组件

npm install -g @vue/cli
vue --version
vue create vue-demo

按键盘数字选择Babel和 Progressive Web App (PWA) Support 两个选项即可

npm run serve

运行成功后,删除App.vue

在/src目录下创建MainZujian.vue

在src/components目录下创建zujian.vue

如下:

<template>
    <!-- zujian.vue模板 -->
    <h3>单文件组件</h3>
</template>

<script>
// 逻辑
export default {
    name:"Zujian"
}
</script>

<style scoped>
/* 样式 */
    h3{
        color:red;
    }
</style>
<template>
<!-- MainZujian.vue-->
<!-- 如何在主组件中加载组件 -->
    <div>
        <h1>我的主应用</h1>
        <!-- 3.显示组件 -->
        <!-- <Zujian-vue></Zujian-vue> -->
        <zujian></zujian>
    </div>
</template>

<script>

// 1.引入子组件
import Zujian from './components/zujian.vue';

export default {
    // 2.挂载组件
    components: {
        Zujian
    }
};

</script>

<style scoped>
    h3{
        color:rgb(5, 0, 99);
    }
</style>
npm run serve

看到实现了

image-20241016165923901

Props组件交互(自定义的属性

  • 组件与组件之间是需要存在交互的,否则完全没关系,组件的意义就很小了

Prop 是什么:

  • Prop 是你可以在组件上注册的一些自定义 attribute(属性)

    • 这句话写的就不像人话
  • 我来说:它就是 Vue 组件中自定义的属性,用于父组件向子组件传递数据

在父组件使用子组件时,可以通过类似 :title="标题" 这样的方式传递一个值到子组件的 title 属性。

在父组件中:

<zujian title="我是父组件中使用子组件,定义的标题属性,即将传递给子组件"></zujian>

子组件中:

Vue模板的逻辑中,添加:

props:{
        title:{
            type:String,
            default:""
            //二、 default就是如果没设置子组件的title属性那么默认为空字符串
    
        }

代码:

子组件:


<template>
    <!-- 模板 -->
    <div>
        <h3>单文件组件</h3>
    <p>{{ title }} </p>
    <!-- 插值语法展示从父组件传过来的title属性的值 -->
    <slot></slot>
    </div>
    
</template>

<script>
// 逻辑
export default {
    name:"Zujian",
    // 二、这里是子组件的 自定义属性props
    props:{
        title:{
            type:String,
            default:""
            //二、 default就是如果没设置子组件的title属性那么默认为空字符串
        }
    }
}
</script>

<style scoped>
/* 样式 */
    h3{
        color:red;
    }
</style>

主组件:


<template>
<!-- 如何在主组件中加载组件 -->
    <div>
        <h1>我的主应用</h1>
        <!-- 3.显示组件 -->
        <!-- <Zujian-vue></Zujian-vue> -->
        <zujian title="我是父组件使用子组件,定义的标题属性 1+1">123</zujian>
            <!-- prop的传递title的值传递给子组件 -->
    </div>
</template>


<script>

// 1.引入子组件
import Zujian from './components/zujian.vue';

export default {
    // 2.挂载组件
    components: {
        Zujian
    }
};

</script>

<style scoped>
    h3{
        color:rgb(5, 0, 99);
    }
</style>
  • 一定记得进入,有package.json在的文件目录中运行,否则报错缺少配置文件。
    • 同样如果删除了package.json需要手动导入依赖,或者重新创建vue项目。

报错:The template root requires exactly one element.eslint-plugin-vue

  • Vue模板需要有且仅有一个根元素
  • 有该报错网页仍然可正常运行

image-20241017212936539

显然这里,在template模板中,有两个元素了。

解决:加上div包裹起来就行。

问题:Vue使用组件时,标签内的内容默认不会自动显示在子组件中

在这里表示为,父组件中的子组件标签中的123不会显示出来。

<zujian title="我是父组件使用子组件,定义的标题属性 1+1">123</zujian>

解决:显式地使用:插槽slot

子组件的模板中使用 <slot>即可

<template>
    <!-- 模板 -->
    <div>
        <h3>单文件组件</h3>
    <p>{{ title }} </p>
    <!-- 插值语法展示从父组件传过来的title属性的值 -->
    <slot></slot>
    </div>
    
</template>

Prop类型

Prop传递参数其实是没有类型限制的

props: {
  title: String,
  likes: Number,
  isPublished: Boolean,
  commentIds: Array,//返回工厂模式
  author: Object,//返回工厂模式
  callback: Function
}

数据类型为数组或者对象的时候,默认值是需要返回工厂模式

  • 因为如果直接使用数组对象作为默认值,所有组件实例将共享同一个引用,这可能导致意外的副作用

可以看到在prps中自定义的attribute属性有:

  • titile标签是String
  • likes是Number类型
    • Number 是 JavaScript 中的一种基本数据类型,用于表示数值。表示整数正负数和浮点数
  • isPublished: Boolean,布尔类型
  • 评论id是数组类型
  • 作者是Object对象类型
  • callback是Function函数,用于父子组件之间的交互。

callback:

  • 类型: Function
  • 用途: 这是一个函数 Prop,通常用于处理事件或与父组件进行交互。例如,在子组件中可能会有一些操作(如删除评论、点赞等),当这些操作发生时,子组件可以调用这个函数来通知父组件执行相应的操作。

工厂模式

工厂模式要求你返回一个函数,该函数每次调用时都会返回一个新的数组或对象实例

这样,每个组件实例都有自己的独立数据,避免了共享引用带来的潜在问题。

示例:

props: {
  title: String,
  likes: Number,
  isPublished: Boolean,
  commentIds: {
    type: Array,
    default: () => [] // 返回一个新的空数组
  },
  author: {
    type: Object,
    default: () => ({}) // 返回一个新的空对象
  },
  callback: Function
}

解释:

  • default: () => []:这里使用了一个箭头函数,每次组件实例创建时都会调用这个函数,返回一个新的空数组
  • default: () => ({}):同样,返回一个新的空对象

确保了每个组件实例都拥有自己独立的 commentIdsauthor 数据,避免了共享引用带来的潜在问题。

$emit自定义事件组件交互

自定义事件可以在组件中反向传递数据

我们知道: prop 可以将数据从父组件 传递到子组件

自定义事件$emit

  • 作用:发出自定义事件(同时传递参数),让父组件能够响应子组件中的特定操作。

    • 而响应需要父组件中用@onCustom=“getData"进行监听
      • 然后再调用函数执行特定操作。
  • 主要作用:

    1. 事件触发: 子组件可以通过 $emit 方法触发事件,向父组件发送通知。例如,用户在子组件中点击按钮后,子组件可以通过 $emit 触发一个事件。
    2. 数据传递: $emit 还可以携带参数,使得子组件能够将数据传递给父组件。这对于需要在子组件中处理一些逻辑,然后将结果反馈给父组件的场景非常有用。
    3. 增强组件复用性: 使用 $emit 使得子组件更具灵活性和可复用性。父组件可以根据自己的需求来处理子组件发出的事件,而子组件本身不需要知道具体的实现细节。

子组件:

<template>
    <div>
        <h3>单文件组件</h3>
        <button @click="sendHandle">发送数据</button>
    </div>
</template>

<script>
export default {
    name: "Zujian",
    props: {
        title: {
            type: String,
            default: ""
        }
    },
    methods: {
        sendHandle() {
            this.$emit("onCustom", "数据"); // 触发自定义事件,并传递数据
        }
    }
}
</script>

<style scoped>
h3 {
    color: red;
}
</style>

主组件:

<template>
    <div>
        <h1>我的主应用</h1>
        <zujian @onCustom="getData"></zujian> <!-- 监听 onCustom 事件 -->
    </div>
</template>

<script>
import Zujian from './components/zujian.vue';

export default {
    components: {
        Zujian
    },
    methods: {
        getData(data) {
            console.log(data); // 打印接收到的数据
        }
    }
}
</script>

<style scoped>
h3 {
    color: rgb(5, 0, 99);
}
</style>

可以看到,子组件中设置点击按钮@click触发sendHandle方法, 然后$emit该方法,发出onCustom的事件,传递数据作为参数。

在主组件中@onCustom=“getData"监听子组件发出的onCustom事件,被触发后getData调用打印传递的数据。

组件的生命周期

每个组件在被创建时都要经过一系列的初始化过程——例如,需要 设置数据监听、编译模板、将实例挂载到 DOM 并在数据变化时更 新 DOM 等。

同时在这个过程中也会运行一些叫做生命周期钩子的函数,这给了用户在不同阶段添加自己的代码的机会

image-20241017221333267

为了方便记忆,我们可以将他们分类

创建时:beforeCreate、created

渲染时:beforeMount、mounted

更新时:beforeUpdate、updated

卸载时:beforeUnmount、unmounted


在漫长的 小时 分钟中
· 写下 17 篇文章、总计 7.16 k 字
· 迎接次不期而遇。