Vue3学习

刚入门写的有的乱,请见谅。

默认有html,css,js基础。

1. 准备

vscode下完配好c和python环境。

npm换淘宝源,下载vue

vscode上下载volar插件

npm init vue@latest 创建vue项目

#项目目录解释
public 资源文件(浏览器图标)
src 源码文件夹
src/assets 存放公共资源,比如图片
.gitignore git忽略文件
package.json 信息描述文件
vite.config.js vue配置文件

在index.html中写入以下代码,将vue应用HelloVueApp挂载到对应id的div标签中。

{{}}用于输出对象的属性和函数返回值。

在一个vue项目中,有且只有一个vue的实例对象。

<head>
<meta charset="utf-8">
<title>Vue 测试实例 - 菜鸟教程(runoob.com)</title>
<script src="https://cdn.staticfile.org/vue/3.2.36/vue.global.min.js"></script>
</head>
<body>
<div id="hello-vue" class="demo">
  {{ message }}
</div>
<script>
const HelloVueApp = {
  data() {
    return {
      message: 'Hello Vue!!'
    }
  }
}
Vue.createApp(HelloVueApp).mount('#hello-vue')
//使用createApp()创建新的应用实例
//传入createApp()的对象实际是一个根组件
//应用挂载(mount)到id为hello-vue的html标签上
</script>
</body>

2. 基础

1. 响应式基础

略。

2. 指令

指令简写说明
v-bind:属性绑定
v-if…v-else…v-else-if条件渲染,有较高的切换渲染开销
v-show条件性地显示或隐藏元素,有较高的初始渲染开销
v-for列表渲染
v-on@事件监听
v-model双向绑定
1. 属性绑定与条件渲染

修改index.html对应部分

<div id="hello-vue" >
    <p v-bind:class="msg">p标签class属性与msg的值绑定</p>
    <p v-bind="obj">绑定多个值</p>
    <p v-if="flag">false隐藏</p>
    <p v-if="type === 'A'">A</p>
    <p v-else-if="type === 'B'">B</p>
    <p v-else="type === 'C'">C</p>
    <p v-show="flag">使用css的display</p>
</div>

const HelloVueApp = {
  data() {
    return {
      msg: 'jkloli',
		obj:{class:"123",id:"456"},
		flag:false,
		type:"B"
    }}}
2. 列表渲染 v-for

v-for遍历列表有两个参数,第二个参数表示下标。

v-for遍历对象有三个参数,三个参数依次为value,key,index。

v-for可以设置key来管理状态,减少重复渲染的次数。

in也可以写成of

  <div id="hello-vue" >
    <div v-for="i in result">
      <p>{{i.id}}</p>
      <p>{{i.name}}</p>
    </div>
    <p v-for="(j,index) in list">内容{{j}},下标{{index}}</p>
    <p v-for="(v,k,i) in obj" :key="i">{{v}},{{k}},{{i}}</p>
  </div>

const HelloVueApp = {
  data() {
    return {
      result:[{"id":"1","name":"雪"},{"id":"2","name":"梅"}],
      list:['a','b','c'],
      obj:{"name":"zs","age":99}
    }}}
3. 事件处理 v-on

事件处理分为内联事件处理和方法事件处理。

在index.html里

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <title>Vue 测试实例</title>
  </head>
  <body>
    <div id="app"></div>
    <script type="module" src="/src/main.js"></script>
  </body>
</html>

在App.vue里,导入Demo1,因为使用了

<script setup>//单文件
import Demo1 from "./components/Demo1.vue"
</script>
<template>
    <Demo1/>
</template>

在main.js里

import { createApp } from 'vue'
import App from './App.vue'
createApp(App).mount('#app')

在Demo1.vue里

<template>
<h3>内联事件处理器</h3>
<button v-on:click="count++">Add</button><br>
<h3>方法事件处理器</h3>
<button @click="mimusCount">minus</button><br>
<p>{{ count }}</p>
</template>

<script>
export default{
    data(){
        return{
            count:0
        }
    },
    methods:{
        mimusCount(){
            this.count-=1
        }
    }
}
</script>
4. 事件参数

事件参数可以获取event(事件)对象和通过事件传递数据。

在Demo1.vue里

<template>
    <p @click="getN(n,$event)" v-for="(n,i) in l" :key="i">{{ n }}</p>
    </template>
    
    <script>
    export default{
        data(){
            return{
                l:["zs","ls","ww"]
            }
        },
        methods:{
            getN(name,e){
                console.log(name);
                console.log(e);
            }
        }
    }
    </script>

在这里插入图片描述

点击左侧的zs即可在控制台打印event对象

5. 事件修饰符

事件捕获:事件从文档根节点(Document 对象)流向目标节点,途中会经过目标节点的各个父级节点,并在这些节点上触发捕获事件,直至到达事件的目标节点;

事件冒泡:与事件捕获相反,事件会从目标节点流向文档根节点,途中会经过目标节点的各个父级节点,并在这些节点上触发捕获事件,直至到达文档的根节点。整个过程就像水中的气泡一样,从水底向上运动。

v-on提供事件修饰符。

事件修饰符说明
.stop阻止事件冒泡
.prevent阻止默认事件
.self只有自己触发自己才执行,忽略内部冒泡传递的触发信号
.capture从最外层向里捕获
.once最多触发一次
.passive不阻止默认事件
按键修饰符按键修饰符
.enter.tab
.delete 捕获Delete和Backspace.esc
.space.up
.down.left
.right.middle
.ctrl
.alt.shift

.exact修饰符,语序控制触发一个事件所需的确定组合的系统按键修饰符。

<!-- 当按下 Ctrl 时,即使同时按下 Alt 或 Shift 也会触发-->
<button @click.ctrl="onClick">A</button>
<!-- 仅当按下 Ctrl 且未按任何其他键时才会触发 -->
<button @click.ctrl.exact="onCtrlClick">A</button>
<template>
    <a @click.prevent="HK" href="https://www.baidu.com">百度</a>
    <div @click="c1">
        <p @click.stop="c2">若冒泡,先打印c2,再打印c1。这里加了.stop不冒泡</p>
    </div>
    <div @click.self="c1">点我只触发c1
        <p @click="c2">会冒泡,点我只触发c2,因为外部.self忽略了内部冒泡信号</p>
    </div>
    </template>
    
    <script>
    export default{
        data(){
            return{
                l:["zs","ls","ww"]
            }
        },
        methods:{
            HK(){
                console.log("HK");
            },
            c1(){
                console.log("c1");
            },
            c2(){
                console.log("c2");
            }
        }
    }
    </script>

​ 以下代码点击c4,依次打印c2,c3,c4,c1(先由外向内捕获,再由内向外冒泡)。

在这里插入图片描述

<template>
    <div @click="c1">c1
        <div @click.capture="c2">c2
            <div @click.capture="c3">c3
                <div @click="c4">c4
                </div>
            </div>
        </div>
    </div>
    </template>
    
    <script>
    export default{
        data(){
            return{
            }
        },
        methods:{
            c1(){
                console.log("c1");
            },
            c2(){
                console.log("c2");
            },
            c3(){
                console.log("c3");
            },
            c4(){
                console.log("c4");
            }
        }
    }
    </script>
6. 数组变化侦测

变更方法:变更方法会改变原数组。

不可变方法:不改变原数组,返回新数组。

变更方法说明
push()向数组末尾添加元素,并返回新的长度。
pop()删除并返回数组的最后一个元素
shift()删除并返回第一个元素
unshift()向数组的开头添加元素,并返回新的长度
splice(index,len,[list])从index向后删除len个元素,在index前插入[list]
this.list.sort((a,b)=>a.id-b.id)升序排序数组
reverse()数组翻转
不可变方法说明
filter()返回符合条件的数组
concat()连接数组
slice()从已有数组返回选定元素
<template>
    <p @click="c1">修改原数组</p>
    <p @click="c2">替换数组</p>
    <li v-for="i in numbers" :key="i">{{ i }}</li>
    </template>
    
    <script>
    export default{
        
        data(){
            return{
                numbers:[1,2,3,4,5,6]
            }
        },
        methods:{
            c1(){
                this.numbers.push(10);
            },
            c2(){
                this.numbers= this.numbers.concat([7,8,9]);
            }
        }
    }
    </script>

3. 计算属性 computed

​ 一个计算属性仅会在其响应式依赖更新时才重新计算。也就是说只要this.list的值不变,调用多次even也只会计算一次。

在这里插入图片描述

<template>
    <li v-for="i in list" :key="i">{{ i }}</li>
    <p>偶数: {{even}}</p>
    </template>
    
    <script>
    export default{   
        data(){
            return{
                list:[1,2,3,4,5,6]
            }
        },
        computed:{
            even(){
                return this.list.filter((i)=>i%2==0)
            }
        }
    }
    </script>

4. 类与样式绑定

classstylev-bind 用法提供了特殊的功能增强。除了字符串外,表达式的值也可以是对象,数组或数组嵌套对象

<template>
    <p :class="{'a':isA,'b':isB}">class="a b"</p>
    <p :class="[jk,gg]">class="jkloli giegie"</p>
<p :style="obj">Red Taylor Swift</p>
    </template>
    
    <script>
    export default{
        data(){
            return{
                isA:true,
                isB:true,
                jk:"jkloli",
                gg:"giegie",
                obj:{color:"red",
                fontSize:30+'px'}
            }
        }
    }
    </script>

5. 侦听器 watch

​ 在组合式 API 中,我们可以使用watch函数在每次响应式状态发生变化时触发回调函数。

<template>
<p>{{msg}}</p>
<button @click="uD">修改</button>
    </template>
    
    <script>
    export default{
        data(){
            return{
                msg:"hello"
            }
        },
        methods:{
            uD(){
                this.msg="world";
            }
        },
        watch:{//watch选项
            //newValue改变之后新数据
            //oldValue改变之前旧数据
            msg(newValue,oldValue){
                //函数名必须与侦听对象同名
                console.log(newValue,oldValue);
            }
        }
    }
    </script>

6. 表单输入绑定 v-model

双向绑定,改变输入框的值会改变msg的值

在这里插入图片描述

<template>
    <input type="text" v-model.lazy="msg">
    <p>{{msg}}</p>
    <input type="checkbox" id="checkbox" v-model="checked"/>
    <label for="checkbox">{{checked}}</label>
        </template>
        
        <script>
        export default{
            data(){
                return{
                    msg:"hello",
                    checked:false
                }
            }
        }
        </script>
v-model修饰符说明
.lazy在每次change事件后更新数据(失去焦点更新)
.number转为数字类型
.trim过滤首尾空白

7. 模板引用 ref

我们可以使用ref属性访问底层DOM元素。

挂载结束后引用都会暴露在this.$refs上。

<template>
    <input type="text" ref="abc">
    <button @click="getValue">获取属性值</button>
        </template>
        
        <script>
        export default{
            data(){
                return{
                }
            },
            methods:{
                getValue(){
                    console.log(this.$refs.abc.value);
                }
            }
        }
        </script>

8. 组件基础

组件允许我们将 UI 划分为独立的、可重用的部分,并且可以对每个部分进行单独的思考。在实际应用中,组件常常被组织成层层嵌套的树状结构。

在这里插入图片描述

当使用构建步骤时,我们一般会将 Vue 组件定义在一个单独的 .vue 文件中,这被叫做单文件组件(简称 SFC)。此外,我们还可以在Demo1.vue里引入其它组件形成组件嵌套。

3. 深入组件

1. 组件注册(全局和局部)

​ 一个vue组件在使用前需要先被”注册“,vue在渲染模板时才能找到对应的实现。组件注册有全局注册和局部注册两种。

​ 全局注册没法移除未使用的组件。

1. 全局注册

全局注册我们可以使用 Vue 应用实例的 .component() 方法,让组件在vue当前 Vue 应用中全局可用。

./components/Demo1.vue里

<template>
<p class="Redc">{{msg}}</p>
</template>

<script>
        export default{
            data(){
                return{
                    msg:18
                }
            }
        }
</script>
<!--scoped让当前样式只在当前组件中生效-->
<style scoped>
    .Redc{
        color:red;
    }
</style>

main.js

import { createApp } from 'vue'
import App from './App.vue'
import Demo1 from './components/Demo1.vue'//引入

const app = createApp(App)
//下面写组件注册
app.component("Demo1",Demo1)
app.mount("#app")

App.vue

<template>
    <Demo1 /><!--在这显示-->
    </template>
    <script>
    </script>
    <!--scoped让当前样式只在当前组件中生效-->
    <style scoped>
    </style>
2. 局部注册

局部注册可以使用components显式注册。

在使用<script setup>的单文件组件中,导入的组件可以直接在模板中使用,无需注册。

main.js

import { createApp } from 'vue'
import App from './App.vue'
const app = createApp(App)
app.mount("#app")

App.vue里

<template>
<!-- 3.显示组件-->
<Demo1/>
</template>

<script>
//1.引入组件
import Demo1 from "./components/Demo1.vue"

//2.注入组件(局部注册)
export default{
    components:{
        Demo1
    }
}
</script>
<style>
</style>
<script setup>
import Demo1 from "./components/Demo1.vue"
</script>

3. 父传子 props

我们可以用props在组件间传递数据,并且只能由父组件传到子组件,且不能直接在子组件修改父组件传来的数据。但是我们可以传递函数,让函数在子组件调用时修改父组件数据。

在传递过程中我们还可以设置props校验,判断传入数据类型,设置数据默认值,设置必选项(必须传入该数据)。

如果是数组和对象,必须通过工厂函数返回默认值。

在这里插入图片描述

App.vue

<template>
<!-- 3.显示组件-->
<Father />
</template>

<script setup>
import Father from "./components/Father.vue"
</script>

Father.vue 父组件

<template>
    <h3>Father</h3>
    <input style="text" v-model:="msg" />
    <Child :jkloli="cD" :title="msg" />
</template>

<script>
import Child from './Child.vue'
export default {
    data(){
        return{
            msg:"Father data"
        }
    },
    methods:{
        cD(data,e){
            this.msg=data;
        }
    },
    components:{
        Child //局部注册
    }
}
</script>

Child.vue 子组件

<template>
    <h3>Child</h3>
    <p>{{title}}</p>
    <li v-for="(j,i) in arr" :key="i">{{j}}</li>
    <input type="text" v-model="vv">
    <button @click="jkloli(vv,$event)">间接修改父组件msg</button>
  </template>
  <script>
  export default {
      data(){
          return{
                vv:""
          }
      },
      //props:["jkloli","title"]
      props:{
          jkloli:{
              type:Function,
              //数据需要满足的类型,不满足报警告
              required:true //必选项
          },
          title:{
              type:[String,Number,Array],
              default:"Child" //默认值
          },
          arr:{
              type:Array,
              default(){
                  return [1,2,3,4];
              }
          }
          
      }
  }
  </script>

4. 组件事件

在组件的模板表达式中,可以直接使用 $emit 方法触发自定义事件,实现子传父。

在这里插入图片描述

Father.vue

<template>
    <h3>Father</h3>
子组件传递的数据: <p>{{msg}}</p>
    <Child @someEvent="getHandle"/>
</template>

<script>
import Child from './Child.vue'
export default {
    data(){
        return{
            msg:""
        }
    },
    methods:{
        getHandle(data){//事件处理
            console.log("自定义事件someEvent触发了");
            this.msg=data;
        }
    },
    components:{
        Child //局部注册
    }
    
}
</script>

Child.vue

<template>
    <h3>Child</h3>
    <input type="text" v-model="msg">
    <button @click="adr">传递数据</button>
  </template>
  <script>
  export default {
      data(){
          return{
              msg:""
          }
      },
      methods:{//或者用侦听器实时更新数据
          adr(){
              //触发自定义事件someEvent
              this.$emit("someEvent",this.msg);
          }
      }
  }
  </script>

5. 透传attributes

“透传 attribute”指的是传递给一个组件,却没有被该组件声明为 props 或 emits的 attribute 或者 v-on 事件监听器。最常见的例子就是 classstyleid

当一个组件以单个元素为根作渲染时,透传的 attribute 会自动被添加到根元素上。

App.vue

<template>
    <!-- 传递class给子组件Demo1-->
    <Demo1 class="a-c" />
    </template>   
    <script setup>
    import Demo1 from "./components/Demo1.vue"
    </script>

Demo1.vue

<template>
    <!--1. 必须是唯一根元素,比如有且只有一个h3-->
    <h3>透传属性</h3>
    </template>
    <script>
            export default{
                //inheritAttrs:false //禁用 Attributes 继承 
            }
    </script>
    <!--scoped让当前样式只在当前组件中生效-->
    <style scoped>
    .a-c{
        color:red;
    }
    </style>

6. 插槽 slot

1. 简介

我们可以使用插槽在子组件中渲染父组件传递的模板片段。

在这里插入图片描述

<slot> 元素是一个插槽出口 (slot outlet),标示了父元素提供的插槽内容 (slot content) 将在哪里被渲染。

在这里插入图片描述

Father.vue

<template>
    <h3>Father</h3>
    <Child>
        <h3>插槽内容</h3>
    </Child>
</template>

<script setup>
import Child from './Child.vue'
</script>

Child.vue

<template>
    <h3>Child</h3>
    <slot></slot>
  </template>
  <script>
  </script>
2. 渲染作用域

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

Father.vue

<template>
    <h3>Father</h3>
    <Child>
        <h3>{{msg}}</h3>
    </Child>
</template>

<script>
import Child from './Child.vue'
export default{
    data(){
        return{
            msg:"666"
        }
    },
    components:{
        Child
    }
}
</script>
3. 插槽默认值
<slot>在slot里写插槽默认值</slot>
4. 具名插槽

<slot> 元素可以有一个特殊的 attribute name,用来给各个插槽分配唯一的 ID,以确定每一处要渲染的内容。

v-slot,简写#,意思是将这部分的模板片段传入子组件的header插槽中。

Father.vue

<template>
    <h3>Father</h3>
    <Child>
        <template v-slot:A>
            <p>hello world</p>
		</template>
		<template #B>
            <p>xss</p>
		</template>
    </Child>
</template>

<script setup>
import Child from './Child.vue'
</script>

Child.vue

<template>
    <h3>Child</h3>
    <slot name="A">插槽A</slot>
    <hr>
    <slot name="B">插槽B</slot>
  </template>
  <script>
  </script>
5. 作用域插槽

可以像对组件传递 props 那样,向一个插槽的出口上传递 attributes。

子组件count传给父组件,父组件将count又传给子组件显示。

Father.vue

<template>
    <h3>Father</h3>
    <Child v-slot="slotProps">
        {{slotProps.count}}
    </Child>
</template>

<script setup>
import Child from './Child.vue'
</script>

Child.vue

<template>
    <h3>Child</h3>
    <slot :count="65">插槽A</slot>
  </template>

如果是具名插槽,Father.vue可以这样写

<template>
    <h3>Father</h3>
    <Child>
        <template #A="slotProps">
			{{slotProps.count}}
		</template>   
    </Child>
</template>
<script setup>
import Child from './Child.vue'
</script>

7. 组件生命周期

每个 Vue 组件实例在创建时都需要经历一系列的初始化步骤,比如设置好数据侦听,编译模板,挂载实例到 DOM,以及在数据改变时更新 DOM。在此过程中,它也会运行被称为生命周期钩子的函数,让开发者有机会在特定阶段运行自己的代码。

在这里插入图片描述

通过ref获取DOM元素

APP.vue

<template>
<h3>App</h3>
<p ref="name">you get it!</p>
    </template>
    
    <script>
export default{
    beforeMount(){
        console.log(this.$refs.name);//undefined
    },
    mounted(){
        console.log(this.$refs.name);//<p>you get it!</p>
    }
}
    </script>

8. 动态组件

通过 Vue 的 <component> 元素和特殊的 is attribute 实现在两个组件间来回切换。

Demo1.vue和Demo2.vue随便写点内容。

App.vue

<template>
<h3>App</h3>
<component :is="CT"></component>
<button @click="changeHandle">切换组件</button>
</template>
    
<script>
import Demo1 from './components/Demo1.vue'
import Demo2 from './components/Demo2.vue'
export default{
    data(){
        return{
            CT:"Demo1"
        }
    },
    components:{
        Demo1,
        Demo2
    },
    methods:{
        changeHandle(){
            this.CT=this.CT=="Demo1"?"Demo2":"Demo1";
        }
    }
}
</script>

9. 组件保持存活

当使用<component :is="...">在多个组件间来回切换时,被切换掉的组件会被卸载,我们可以用<keep-alive>来使被切换掉的组件保持存活状态。比如下面的Demo1更新成新数据后,切Demo2再切回来依旧是新数据。

App.vue

<template>
<h3>App</h3>
    <keep-alive>
        <component :is="CT"></component>
    </keep-alive>
<button @click="changeHandle">切换组件</button>
</template>
    
<script>
import Demo1 from './components/Demo1.vue'
import Demo2 from './components/Demo2.vue'
export default{
    data(){
        return{
            CT:"Demo1"
        }
    },
    components:{
        Demo1,
        Demo2
    },
    methods:{
        changeHandle(){            
            this.CT=this.CT=="Demo1"?"Demo2":"Demo1";
        }
    }
}
</script>

Demo1.vue

<template>
    <h3>Demo1</h3>
    <p>msg: {{msg}}</p>
    <button @click="changeHandle">更新数据</button>
    </template>      
    <script>
    export default{
        data(){
            return{
                msg:"旧数据"
            }
        },
        methods:{
            changeHandle(){            
                this.msg="新数据";
            }
        }
    }
    </script>

10. 异步组件

​ 在大型项目中,我们可能需要拆分应用为更小的块,并仅在需要时再从服务器加载相关组件。Vue 提供了 defineAsyncComponent方法来实现此功能。

App.vue

<template>
    <h3>App</h3>
        <keep-alive>
            <component :is="CT"></component>
        </keep-alive>
    <button @click="changeHandle">切换组件</button>
    </template>
        
    <script>
    import {defineAsyncComponent} from 'vue'
    import Demo1 from './components/Demo1.vue'
    //异步加载组件
    const Demo2=defineAsyncComponent(()=>import('./components/Demo2.vue'))
    export default{
        data(){
            return{
                CT:"Demo1"
            }
        },
        components:{
            Demo1,
            Demo2
        },
        methods:{
            changeHandle(){            
                this.CT=this.CT=="Demo1"?"Demo2":"Demo1";
            }
        }
    }
    </script>

11. 依赖注入

​ 有一些多层级嵌套的组件,形成了一颗巨大的组件树,而某个深层的子组件需要一个较远的祖先组件中的部分数据。此时使用props会非常麻烦(prop逐级透传),因此我们可以使用provideinject解决这一问题。

​ inject注入会在组件自身的状态之前被解析,因此你可以在 data() 中访问到注入的属性。

在这里插入图片描述

App.vue

<template>
    <Father />
    </template>
    <script>
    import Father from "./components/Father.vue"
    export default{
        components:{
            Father
        },
        provide:{
            msg:"App的数据"
        }
    }
    </script>

App.vue中provide也可以读取data选项中的数据

<template>
    <Father />
    </template>
    <script>
    import Father from "./components/Father.vue"
    export default{
        data(){
            return{
                msg:"App的数据"
            }
        },
        components:{
            Father
        },
        provide(){
            return{
                msg:this.msg
            }
        }
    }
    </script>

Father.vue

<template>
    <h3>Father</h3>
    <Child />
</template>
<script setup>
import Child from './Child.vue'
</script>

Child.vue

<template>
    <h3>Child</h3>
	<p>{{msg}}</p>
</template>
<script>
export default{
      inject:["msg"]
}
</script>

此外,我们还可以在整个应用层面提供依赖

main.js

import { createApp } from 'vue'
import App from './App.vue'
const app = createApp(App)
app.provide("msg","hello world")
//msg为注入名,hello world为值
app.mount("#app")

参考

Vue3从入门到精通 蔡蔡小趴菜
vue.js 文档
走进Vue【二】Vue3语法糖 忆凡_

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值