当前位置: 首页 > article >正文

Vue3、vue学习笔记

<!-- `Vue3` -->

1、Vue项目搭建

npm init vue@latest
cd 文件目录
npm i
npm run dev    // npm run _ 这个在package.json中查看scripts
/* vue_study\.vscode可删  // vue_study\src\components也可删除(基本语法,不使用组件) */
// vue_study\.vscode\launch.json
{
    // Use IntelliSense to learn about possible attributes.
    // Hover to view descriptions of existing attributes.
    // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
    "version": "0.2.0",
    "configurations": [
        {
            "type": "pwa-msedge",
            "request": "launch",
            "name": "Launch Edge against localhost",
            "url": "http:\\localhost:5173",
            "webRoot": "C:\\Program Files (x86)\\Microsoft\\Edge\\Application\\msedge"
        }
    ]
}

2、代码风格

<!-- 还是上面的命令 npm run dev -->
<template>
    <div>
        msg: {{ msg }}
    </div>
    <button @click="add"> +++ </button>
</template>
​
<script>
    export default {
        data() {
            return {
                msg: 'Hello Vue!'
            }
        },
        methods: {
            add() {
                this.msg += '-杨小槿-'
            }
        },
    }
</script>

vue3阻隔式

<template>
    <div>msg: {{ msg }}</div>
    <button @click="add"> add </button>
</template>
<script>
    import {ref} from "vue";
    export default {
        setup() {
            const msg = ref("杨小槿")
            function add(){
                msg.value = "xxx"
            }
            return {
                /* msg: '杨小槿' //无法操作  */
                msg,
                add
            }
        }
    }
</script>

简写

<template>
    <div>msg: {{ msg }}</div>
    <button @click="add"> add </button>
</template>
<script setup>
    import {ref} from "vue";
    const msg = ref("杨小槿")
    function add(){
        msg.value = "xxx"
    }
</script>

3、模版语法

插值表达式

<template>
  <div>msg: {{ msg }}</div>
  <div v-text="msg"></div>
  <div v-html="html"></div>
</template>
<script setup>
    import {ref} from "vue";
    const msg = ref("杨小槿")
    const html = ref("<a href='https://www.baidu.com'>百度</a>")
</script>

属性 v-bind

<template>
    <div :data-msg="attr" :class="_class" :style="style">msg: {{ msg }}</div>
    <div v-bind:data-msg="attr" :class="_class2">msg: {{ msg }}</div>
</template>
​
<script setup>
    import {ref} from "vue";
    const msg = ref("杨小槿")
    const attr = ref("吃了吗")
    const _class = ref("active")
    const _class2 = {
        /* class对象形式,为true赋值,flase不赋值 */
        active: true,
        isBanner: true,
        active2: false,
    }
    const style = {"color": "blue"}
</script>

动态属性,用于解构(一般不用)

<template>
 <div :data-msg="attr" v-bind="bind">msg: {{ msg }}</div>
 <div v-bind:data-msg="attr" >msg: {{ msg }}</div>
</template>
​
<script setup>
 import {ref, reactive} from "vue";
 const msg = ref("杨小槿")
 const attr = ref("吃了吗")
 const bind = reactive({
     /* 也可以不要reactive() */
     m1: "杨小槿",
     m2: "任小粟",
     m3: "庆尘",
 })
</script>

在插值表达式和动态属性中,都可以写一段js表达式

<div v-bind:data-msg="msg.startsWith('杨小槿') ? '成立' : '不成立'">
    msg1: {{msg === "杨小槿" ? "成立" : "不成立"}}
</div>

4、条件渲染

  • v-if

  • v-show 控制css display: none

<template>
  <div class="box1" v-if="isShow" style="width: 20px; height: 20px; background-color: cornflowerblue;"></div>
  <div v-else-if="isShow"> 你好 </div>
  <div v-else> 你好呀 </div>
  <div class="box2" v-show="isShow" style="width: 20px; height: 20px; background-color: cornflowerblue;"></div>
  <div> <button @click="isShow=!isShow">显示</button> </div>
</template>

<script setup>
    import {ref, reactive} from "vue";
    const isShow = ref(true)
</script>

5、循环

遍历列表

<template>
	<ul>
    	<li v-for="item in list" :key="index">{{item}}</li>
    </ul>
	<ul>
    	<li v-for="(item, index) in list" :key="index">{{index}}-{{item}}</li>
    </ul>
</template>

<script setup>
    import {ref, reactive} from "vue";
    const list = ref(["杨小槿", "任小粟", "庆尘", "李叔同"])
</script>

解构,列表

<template>
	<ul>
    	<li v-for="(item, index) in list1" :key="item.id">{{item.name}}</li>
    </ul>
	<ul>
    	<li v-for="({id, name}, index) in list1" :key="id">{{name}}</li>
    </ul>
</template>
<script setup>
    import {ref, reactive} from "vue";
	const list1 = ref([
        {id: 1, name: "杨小槿"},
        {id: 2, name: "任小粟"},
        {id: 3, name: "庆尘"},
        {id: 4, name: "李书同"}
    ])
</script>

字典

<template>
	<ul>
    	<li v-for="val in dict" :key="val">{{val}}</li>
    </ul>
	<ul>
    	<li v-for="(val, key) in dict" :key="key">{{key}}-{{val}}</li>
    </ul>
	<ul>
    	<li v-for="(val, key, index) in dict" :key="key">{{index}}-{{key}}-{{val}}</li>
    </ul>
</template>

<script setup>
    import {ref, reactive} from "vue";
    const dict = reactive({
        name: "杨小槿",
        age: 18,
    });
</script>

6、事件

可以用v-on(简写@) 来监听DOM事件,事件对象是$event

<template>
	<div>
      <div>count: {{count}}</div>
      <button @click="count ++">count++</button>
      <button @click="add">add</button>
      <button @click="addN(10, $event)">add 10</button>
  </div>
</template>

<script setup>
    import {ref} from "vue";
    const count = ref(0);
    function add() {
      count.value++
    }
    function addN(n, e){
      count.value+=n
      console.log(e)
    }
</script>

事件修饰符

<!-- 单击事件将停止传递 -->
<a @click.stop="doThis"></a>
<!-- 提交事件将不再重新加载页面 -->
<from @submit.prevent="onSubmit"></from>
<!-- 修饰语可以使用链式书写 -->
<a @click.stop.prevent="doThat"></a>
<!-- 也可以只有修饰符 -->
<from @submit.prevent></from>
<!-- 仅当 event.target 是元素本身时才会触发事件处理器 -->
<!-- 如:事件处理器不来自子元素 -->
<div @click.self="doThis">...</div>
<!-- 点击事件最多被触发一次 -->
<a @click.once="doThis"></a>
<!-- 滚动事件的默认行为(scrolling)将立即发生而非等待`onScroll`完成 -->
<!-- 以防其中包含`event.preventDefault()` -->
<div @scroll.passive="onScroll">...</div>

按键修饰符

<!-- 仅在`key`为`Enter`时调用`submit` -->
<input placeholder="请输入内容" @keydown.enter="submit" />
<div style="background-color: #2c3e50; width: 20px; height: 20px" @mousedown.left="函数名"></div>
<!-- 常用的键盘按键 -->
.enter
.tab
.delete (捕获“删除”和“退格”键)
.esc
.space
.up
.down
.left
.right
.ctrl
.alt
.shift
<!-- 鼠标按键 -->
.left
.right
.middle

7、计算属性

<template>
    <div>
        <div>count: {{count}}</div>
        <div>count1: {{count1}}</div>
        <div>count2: {{count2(20)}}</div>
    </div>
</template>

<script setup>
    import {ref, computed} from "vue";
    const count = ref(0);
    const count1 = computed(()=>{
        return count.value + 10
    })
    const count2 = computed(()=>{
        return function(num){
            return count.value + num
        }
    })
</script>

8、watch监听属性

ref监听

<template>
	<div>
      <div>msg: {{msg}}</div>
      <input v-model="msg">
  </div>
</template>

<script setup>
    import {ref, watch} from "vue";
    const msg = ref("");
    // 可以直接监听一个 ref
    watch(msg, async(n, o) => {
      // 新值 n,旧值 o
      console.log(n, o);
    })
</script>

reactive监听

<template>
	<div>
      <div>msg: {{data.msg}}</div>
      <input v-model="data.msg">
  </div>
</template>

<script setup>
    import {ref, watch, reactive} from "vue";
    const data = reactive({
      msg: "",
    })
    // 可以直接监听一个 ref
    watch(data, async(n, o) => {
      // 新值 n,旧值 o
      console.log(n, o);
    })
</script>
<template>
	<div>
      <div>msg: {{data.msg}}</div>
      <input v-model="data.msg">
  </div>
</template>

<script setup>
    import {ref, watch, reactive} from "vue";
    const data = reactive({
      msg: "",
    })
    // 可以直接监听一个 ref
    watch(()=>data.msg, async(n, o) => {
      // 新值 n,旧值 o
      console.log(n, o);
    })
</script>

9、生命周期

setup()
在 beforeCreate 和 created 之前执行,创建的是 data 和 method
onMounted()
在组件挂载完成后执行,可以使用dom
onUpdated()
在组件因为响应式状态变更而更新其 DOM 树之后调用
onUnmounted()
在组件实例被卸载之后调用
onBeforeMount()
在组件被挂载之前被调用
onBeforeUpdate()
在组件即将因为响应式状态变更而更新其 DOM 树之前调用
onBeforeUnmount()
在组件实例被卸载之前调用
onErrorCaptured()
在捕获了后代组件传递的错误时调用
<template>
</template>

<script setup>
    export default {
        setup() {
            console.log('setup')
        },
        beforeCreate() {
            console.log('beforeCreate')
        },
        created() {
            console.log('created')
        },
        beforeMount() {
            console.log('beforeMount')
        },
        mounted() {
            console.log('mounted')
        },

    }
</script>
<template>
</template>

<script setup>
    import { onBeforeMount, onMounted } from 'vue';
    function getData(){
        console.log('调用后端接口')
    }
    console.log('setup1')
    onBeforeMount(() => {
        console.log('onBeforeMount')
    })
    onMounted(() => {
        console.log('onMounted')
    })
    console.log('setup2')
    getData()
</script>

10、模版引用

需求:用户进入界面,要求自动选择文本输入框

<template>
    <div>
        <input id="ipt" placeholder="自动选择" />
    </div>
</template>

<script setup>
    import { onMounted } from 'vue';
    onMounted(() => {
        const ipt = document.getElementById('ipt');
        ipt.focus()
    })
</script>
<template>
    <div>
        <input id="ipt" type="text" ref="defaultIpt" placeholder="自动选择" />
    </div>
</template>

<script setup>
    import { onMounted, ref } from 'vue';
    const defaultIpt = ref()
    onMounted(() => {
        defaultIpt.value.focus()
    })
</script>

11、vue组件

  • src\components新建文件夹

    • 文件夹下新建numberCilck.vue

<!-- numberCilck.vue -->
<script setup>
    import { ref } from 'vue';
    const number = ref(1)
    function click(){
        number.value ++
    }
</script>

<template>
    <div>
        <div>{{ number}}</div>
        <button @click="click">+</button>
    </div>
</template>

<style scoped>

</style>
<!-- src\App.vue -->
<template>
  <div>
    <numberCilck></numberCilck>
  </div>
</template>

<script setup>
  import numberCilck from './components/numberCilck.vue';
</script>

全局注册

  • src\main.js

import './assets/main.css'

import { createApp } from 'vue'
import App from './App.vue'
import numberCilck from './components/numberCilck.vue';  // 加这个


const app = createApp(App)
app.component('numberCilck', numberCilck)   //注册组件, 组件名为numberCilck
app.mount('#app')

12、父传子

<!-- src\App.vue -->
<template>
    <div>
        <numberCilck :num="10"></numberCilck>
		<numberCilck ></numberCilck>
    </div>
</template>

<script setup>
</script>
<!-- numberCilck.vue -->
<script lang="ts" setup>
    import { ref } from 'vue';
    interface Props {
        num?: number   // num是number类型
    }
    const props = defineProps<Props>()
    const {num=1}=props  // num默认值为1
    const number = ref(1)

    function click(){
        number.value += num
    }
</script>

<template>
    <div>
        <div>{{ number}}</div>
        <button @click="click">+</button>
    </div>
</template>

<style scoped>
</style>
// src\main.js
import './assets/main.css'

import { createApp } from 'vue'
import App from './App.vue'
import numberCilck from './components/numberCilck.vue';


const app = createApp(App)
app.component('numberCilck', numberCilck)   //注册组件, 组件名为numberCilck
app.mount('#app')

<!-- numberCilck.vue -->
<!-- 第二种方法 -->
<script lang="ts" setup>
    import { ref } from 'vue';
    interface Props {
        num?: number   // num是number类型
    }
        const props = withDefaults(defineProps<Props>(), {
            num: 1,
        })// 定义props类型
        const number = ref(1)

        function click(){
            number.value += props.num
        }
</script>

<template>
    <div>
        <div>{{ number}}</div>
        <button @click="click">+</button>
    </div>
</template>

<style scoped>
</style>

在子组件里面,不要尝试修改父组件传递来的props

13、子通知父

用户有没有点击按钮,作为父组件其实是不知道的

如果我想让父组件知道,谁点击了按钮,那么我们可以给子组件编写一个事件

点击按钮之后,就通知父组件

如下,子组件编写

<!-- numberCilck.vue -->
<script lang="ts" setup>
    import { c } from 'vite/dist/node/types.d-aGj9QkWt';
import { ref } from 'vue';
    interface Props {
        num?: number   // num是number类型
    }
    const props = withDefaults(defineProps<Props>(), {
        num: 1,
    })// 定义props类型
    const number = ref(1)

    const emits = defineEmits(['plus'])  // 定义事件类型

    function click(){
        number.value += props.num
        emits('plus', 1, "你点到了")   // 事件名称,
    }
</script>

<template>
    <div>
        <div>{{ number}}</div>
        <button @click="click">+</button>
    </div>
</template>

<style scoped>
</style>
<!-- src\App.vue -->
<script setup lang="ts">
import numberCilck from './components/numberCilck.vue';
function plus(num: number, msg: string) {
  console.log(num, msg);
}
</script>

<template>
  <div>
    <numberCilck :num="100" @plus="plus"></numberCilck>
    <numberCilck ></numberCilck>
  </div>
</template>

如果是ts子组件也可以这么写

<!-- numberCilck.vue -->
<script lang="ts" setup>
    import { c } from 'vite/dist/node/types.d-aGj9QkWt';
    import { ref } from 'vue';
    interface Props {
        num?: number   // num是number类型
    }
        const props = withDefaults(defineProps<Props>(), {
            num: 1,
        })// 定义props类型
        const number = ref(1)


        interface Emits {
            (e:"plus", num: number, msg: string): void
        }
            const emits = defineEmits<Emits>()  // 定义事件类型


            function click(){
                number.value += props.num
                emits('plus', 1, "你点到了")   // 事件名称,
            }
</script>

<template>
<div>
    <div>{{ number}}</div>
    <button @click="click">+</button>
    </div>
</template>

<style scoped>
</style>

另一种写法

<!-- numberCilck.vue -->
<script lang="ts" setup>
    import { c } from 'vite/dist/node/types.d-aGj9QkWt';
    import { ref } from 'vue';
    interface Props {
        num?: number   // num是number类型
    }
        const props = withDefaults(defineProps<Props>(), {
            num: 1,
        })// 定义props类型
        const number = ref(1)

        // interface Emits {
        //     (e:"plus", num: number, msg: string): void
        // }
        const emits = defineEmits<{
            plus: [number, string]
        }>()  // 定义事件类型
        // const emits = defineEmits<Emits>()  // 定义事件类型

        function click(){ 
            number.value += props.num
            emits('plus', 1, "你点到了")   // 事件名称,
        }
</script>

<template>
<div>
    <div>{{ number}}</div>
    <button @click="click">+</button>
    </div>
</template>

<style scoped>
</style>

14、父传孙

props透传

  • 新建src\components\child.vue

<!-- src\components\child.vue -->
<script lang="ts" setup>
</script>

<template>
	<div>孙组件</div>
</template>
<style scoped>
</style>
<!-- numberCilck.vue -->
<script lang="ts" setup>
    import { c } from 'vite/dist/node/types.d-aGj9QkWt';
    import { ref } from 'vue';
    import Child from './child.vue';
    interface Props {
        num?: number   // num是number类型
    }
        const props = withDefaults(defineProps<Props>(), {
            num: 1,
        })// 定义props类型
        const number = ref(1)

        // interface Emits {
        //     (e:"plus", num: number, msg: string): void
        // }
        const emits = defineEmits<{
            plus: [number, string]
        }>()  // 定义事件类型
        // const emits = defineEmits<Emits>()  // 定义事件类型

        function click(){ 
            number.value += props.num
            emits('plus', 1, "你点到了")   // 事件名称,
        }
</script>

<template>
    <div>
        <Child></Child>
        <div>{{ number}}</div>
        <button @click="click">+</button>
    </div>
</template>

<style scoped>
</style>
<!-- src\components\child.vue -->
<script lang="ts" setup>
const props = defineProps(['msg'])
</script>

<template>
    <div>孙组件{{ props.msg }}</div>
</template>
<style scoped>
</style>
<!-- numberCilck.vue -->
<script lang="ts" setup>
    import { c } from 'vite/dist/node/types.d-aGj9QkWt';
import { ref } from 'vue';
import Child from './child.vue';
    interface Props {
        num?: number   // num是number类型
        msg: string    // msg是string类型
    }
    const props = withDefaults(defineProps<Props>(), {
        num: 1,
    })// 定义props类型
    const number = ref(1)

    // interface Emits {
    //     (e:"plus", num: number, msg: string): void
    // }
    const emits = defineEmits<{
        plus: [number, string]
    }>()  // 定义事件类型
    // const emits = defineEmits<Emits>()  // 定义事件类型

    function click(){ 
        number.value += props.num
        emits('plus', 1, "你点到了")   // 事件名称,
    }
</script>

<template>
    <div>
        <Child :msg="props.msg"></Child>
        <div>{{ number}}</div>
        <button @click="click">+</button>
    </div>
</template>

<style scoped>
</style>
<!-- src\App.vue -->
<script setup lang="ts">
import numberCilck from './components/numberCilck.vue';
function plus(num: number, msg: string) {
  console.log(num, msg);
}
</script>

<template>
  <div>
    <numberCilck :num="100"  :msg="'hello'" @plus="plus"></numberCilck>
    <numberCilck :msg="'world'" ></numberCilck>
  </div>
</template>

为了简化,可以使用provideinject进行依赖注入

<!-- 父组件 -->
<!-- src\App.vue -->
<script setup lang="ts">
    import numberCilck from './components/numberCilck.vue';
    import {provide} from 'vue'
    provide('msg','hello!')  // 注入名    值
</script>

<template>
    <div>
        <numberCilck :num="100"></numberCilck>
        <numberCilck></numberCilck>
    </div>
</template>
<!-- 中间组件 -->
<template>
    <div>
        我是中间层
        <Child></Child>
    </div>
</template>

<script setup lang="ts">
    import Child from './child.vue';
</script>
<!-- 孙组件 -->
<!-- src\components\child.vue -->
<script lang="ts" setup>
    import {inject} from 'vue'
    const msg = inject('msg', "")
    // const msg = inject('msg', "默认值”)  // 设置默认值
</script>

<template>
	<div>孙组件{{ msg }}</div>
</template>
<style scoped>
</style>

15、兄弟组件通信

  1. 路由参数

  2. 本地存储

  3. vuexpinia

  4. 全局事件总线(需要用到mitt第三方包)

标签云组件中,点击某个标签,给当前路由加上查询参数,带上选择的标签

然后在文章列表组件中,监听路由查询参数的变化,有变化就从里面取值

然后再查一遍文章列表

16、v-model

<!-- src\App.vue -->
<script setup lang="ts">
  import {ref} from 'vue'
  const ipt = ref('hello')
</script>

<template>
  <div>
      <span>{{ipt}}</span>
      <input v-model="ipt">
  </div>
</template>

其实vue背后做了很多事

<!-- src\App.vue -->
<script setup lang="ts">
    import {ref} from 'vue'
    const ipt = ref('hello')
    function iptInput(e : InputEvent){
        let val = (e.target as HTMLInputElement).value
        ipt.value = val
    }
</script>

<template>
    <div>
        <span>{{ipt}}</span>
        <input :value="ipt" @input="iptInput">
    </div>
</template>

若自己的组件也想实现v-model怎么办

单个v-model

<!-- src\components\myIpt.vue -->
<script setup lang="ts">
    const props = defineProps(["modelValue"])
    const emits = defineEmits(["update:modelValue"])
    function input(e: Event): void {
        const val = (e.target as HTMLInputElement).value
        emits("update:modelValue", val)
    }
</script>

<template>
    <div>
        <span>{{ props.modelValue }}</span>
        <input type="text" :value="props.modelValue" @input="input" />
    </div>
</template>

<style scoped>
</style>
<!-- src\App.vue -->
<script setup lang="ts">
    import {ref} from 'vue'
    import MyIpt from './components/myIpt.vue';
    const ipt = ref('hello')

</script>

<template>
    <div>
        <MyIpt v-model="ipt"></MyIpt>
    </div>
</template>

多个v-model

<!-- App.vue --> 
<script setup Tang="ts">
    import {ref} from "vue";
    import MyIpt from "@/components/myIpt.vue";
    import AModel from "@/components/aModel.vue";
    const ipt = ref("你好")
    const visible = ref(false)
</script>
<template>
    <div>
        <my-ipt v-model="ipt"></my-ipt>
        <AModal v-model:visible="isible"></AModal>
    </div>
</template>
<!-- components/aModel.vue -->
<script setup lang="ts">
    const props = defineProps(["visible"])
    const emits = defineEmits(["update:visible"])
</script>
<template>
    <div>
        {{ props.visible }}
        <button @click="emits('update:visible', !props.visible)">点我</button>
    </div>
</template>
<style scoped>
</style>

17、组件的插槽

默认插槽

<!-- components/mySlot.vue -->
<script setup lang="ts">    
</script>

<template>
    <div>
        <div>模态框头部</div>
        <div>
            <slot>这里写默认内容</slot>
        </div>
        <div>模态框尾部</div>
    </div>
</template>

<style scoped>
</style>
<!-- App.vue --> 
<script setup lang="ts">
    import Myslot from "@/components/myslot.vue";
</script>

<template>
    <div>
        <!-- <my-slot><a>这是我的身体 你快出去</a></my-slot> -->
        <my-slot></my-slot>
    </div>
</template>

具名插槽

<!-- components/mySlot.vue -->
<script setup lang="ts">    
</script>

<template>
    <div>
        <div>
            <slot name="header">模态框头部</slot>
    	</div>
        <div>
            <slot name="body">这里写默认内容</slot>
        </div>
        <div>
            模态框尾部
    	</div>
    </div>
</template>

<style scoped>
</style>
<!-- App.vue --> 
<script setup lang="ts">
    import Myslot from "@/components/myslot.vue";
</script>

<template>
    <div>
        <my-slot> 我的身体 
    		<template #body>身体</template>
			<template v-slot:header>我的头部</template>
    	</my-slot>
    </div>
</template>

动态插槽

<template>
    <div>
        这里有很多插槽
        <div>
            <slot name="mo_1">1</slot>
            <slot name="mo_2">2</slot>
            <slot name="mo_3">3</slot>
            <slot name="mo_4">4</slot>
            <slot name="mo_5">5</slot>
            <slot name="mo_6">6</slot>
            <slot name="mo_7">7</slot>
            <slot name="mo_8">8</slot>
            <slot name="mo_9">9</slot>
        </div>
    </div>
</template>
<script setup lang="ts">
    import syncModal from "@/components/syncModal.vue";
    import {computed,ref} from "vue";
    const name = ref(1)
    function click() {
        name.value++
        if (name.value === 10) {
            name.value = 1
        }
    }
    const slotName = computed(() => {
        return "mo_" + name.value
    })
</script>
<template>
    <div>
        <button @click="click">点我</button>
        <syncModal>
            <template #[slotName]>a</template>
    	</syncModal>
	</div>
</template>

作用域插槽

具名插槽的作用域

<template>
    <div>
        <slot name="body" msg="这是消息" :age="12"></slot>
    </div>
</template>
<msgSlot>
    <template #body="data">
		这是插槽内部传递的数据:{{ data.msg }} age:{{ data.age }}
    </template>
</msgSlot>

默认插槽的作用域(和具名插槽写法不太一样)

<template>
    <div>
        <slot msg="这是消息" :age="12"></sLot>
    </div>
</template>
<msgSlot v-slot="data">
	这是插槽内部传递的数据: {{ data.msg }} age:{{ data.age }}
</msgSlot>
<!-- 在有具名插槽情况下 -->
<template>
    <div>
        <my-slot>
            <template #default="d">
                身体 {{ d }}
            </template>
            <template #header="data">
                头部 {{ data }}
            </template>
        </my-slot>
    </div>
</template>

Vue

Vue概述

Vue 是渐进式JavaScript框架

  • Vue.js 自底向上增量开发的设计

  • Vue的核心库关注于视图

  • 渐进式:一步一步,不是将所有的东西都学完才能使用

  • 自底向上设计

  • Vue.js的核心是允许采用简洁式模板语法来声明的将数据渲染进DOM的系统

VueJQuery区别

数据驱动试图

  • jquery直接操作dom 到转变为操作数据

  • jQuery是使用选择器选取DOM对象,对其进行赋值、取值、事件绑定等操作,其实和原生的HTML的区别只在于可以更方便的选取和操作DOM对象

  • Vue则是通过Vue对象将数据和View完全分离开来了。对数据进行操作不再需要引用相应的DOM对象,可以说数据和View是分离的,他们通过Vue对象这个vm实现相互的绑定。这就是传说中的MVVM

1、Vue基本语法

官方文档

<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
<!-- 在编辑器上下载vue插件即可有提示 -->
<!-- npm create vue@latest -->

插值表达式

使用{{ }}进行渲染data:{}中的变量

<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
<div id="app">{{ message }}</div>
<script>
    const { createApp, ref } = Vue
    createApp({
        setup() {
            const message = ref('Hello vue!')
            return {
                message
            }
        }
    }).mount('#app')
</script>
<script src="./vue.js"></scripts>
<div id="app">
    {{a}}{{dic.name}}{{List[0].name}}
    <div>{{ getInfo() }}</div>
</div>
<script>
    var vue = new Vue({
        el: '#app',  // vue挂载
        data:{
            a: 'huajiang',
            dic:{
                name:'杨小槿',
                age: 18,
            },
            List:[
                {name:'任小粟'},
                {name:'杨安京'},                
            ]
        },
        methods: {
            getInfo() { 
                return 'Hello vue!';
            }
        }
    })
</script>
<!-- 控制台vue.a="sll" -->

2、绑定属性

v-text

渲染文本,和插值表达式类似,也是支持运算符的

<script src="./vue.js"></scripts>
<div id="app">
    <div v-text="msg"></div>
    <div>{{ msg }}</div>
    <div v-text="2 > 4 ? '成立': '不成立'"></div>
</div>
<script>
    var vue = new Vue({
        el: '#app',  // vue挂载
        data:{
            a: '天空一声巨响,老奴闪亮登场',
            msg: "嗨嗨嗨,来喽"
        },
    })
</script>

v-html

注意:v-html一般是用来渲染信任的文本,例如文章的详情内容等,最好不要用在用户提交的地方,容易造成XSS攻击

<script src="./vue.js"></scripts>
<div id="app">
    <div v-html="aDemo">{{aDemo}}</div>  <!-- v-html编译成html -->
    <div v-text="aDemo">{{aDemo}}</div>  <!-- v-text直接输出文本 -->
</div>
<script>
    var vue = new Vue({
        el: '#app',  // vue挂载
        data:{
            a: 'huajiang',
            aDemo: '<a href="http://www.baidu.com" target="_blank">百度</a>'
        },
    })
</script>

v-bind (控制属性, 简写:)

属性上不能使用插值表达式({{}}

v-bind: 可以简写为 :

<script src="./vue.js"></scripts>
<div id="app">
    <div class="name" v-bind:title="a.name">
        姓名: {{a.name}}
    </div>
    <img v-bind:src="src" alt="">
    <img :src="src" alt="">
</div>
<script>
    var vue = new Vue({
        el: '#app',  // vue挂载
        data: {
            a: {
                name: "杨小槿",
                age: 18,
            },
            src: 'https://profile-avatar.csdnimg.cn/392dc034291241239877635a77987f39_m0_65534674.jpg!1',
        }
    })
</script>

v-bind不仅可用于HTML存在的属性,还可以应用在自定义属性上

<p :id="'p' + '_01'" :data="msg" :还可以是中文="msg"></p>
<p id="p_01" data="haha" 还可以是中文="haha"></p>

vue所有的指令,都支持表达式

<div class="name" :title="'www'+a.split('.')[1]">
    <!-- var a = 1; 语句 -->
    {{ a.length === 0 ? '没有数据' : a }}    <!-- 表达式 -->
    -{{ a.split('.')[1] }}
</div>

3、条件渲染

v-if和v-else的普通使用

v-if中的布尔值为true,则渲染这个div

如果if不成立,则渲染else中的代码块

<script src="./vue.js"></script>
<div id="app">
    <div class="name" v-if="flag">{{ num }}</div>
    <div id="if">
        <div v-if="num > 0.9">大于0.9的概率</div>
        <div v-else>v-if不满足显示我</div>
    </div>
</div>
<script>
    new Vue({
        el: '#app',
        data: {
            num: Math.random(), // 一个0-1之间的随机数字
            flag: false,
        }
    })
</script>

v-else-if多条件语句

<div id="v-else-if">
    <div v-if="num > 0.9">大于0.9的概率</div>
    <div v-else-if="num > 0.6">大于0.6的概率</div>
    <div v-else-if="num > 0.4">大于0.4的概率</div>
    <div v-else-if="num > 0.2">大于0.2的概率</div>
    <div v-else>所有条件都不成立</div>
</div>

v-show与v-if的区别

  • v-if如果是false,不会渲染

  • v-show如果是false,不会显示 style="display: none;"

<script src="./vue.js"></script>
<div id="app">
    <div class="name" v-show="flag">{{ num }}</div>
    <!-- style="display: none;渲染之后隐藏了 -->
</div>
<script>
    new Vue({
        el: '#app',
        data: {
            flag: false,
        }
    })
</script>

4、列表渲染

主要分为遍历列表和遍历对象

遍历列表

key要唯一

<div id="app">
    <ul>
        <li v-for="item in lis" :key="item">  <!-- key要唯一!!! -->
            {{ item }}
        </li>
        <li v-for="(item, index) in lis" :key="item">  <!-- 带索引 -->
            {{ index }} -- {{ item }}
        </li>
    </ul>
</div>
<script>
    var vue = new Vue({
        el: "#app",
        data:{
            lis: [
                '张三',
                '王伟',
                '张伟',
                '王五',
            ],
        }
    })
</script>

遍历对象

  • 一个参数,就是遍历对象的值

  • 二个参数,值和键

  • 三个参数,值,键,索引

<div id="app">
    <ul>
        <li v-for="item in obj" :key="item">
            {{ item }}
        </li>
    </ul>
    <ul>
        <li v-for="(item, key) in obj" :key="item">
            {{ key }}{{ item }}
        </li>
    </ul>
    <ul>
        <li v-for="(item, key, index) in obj" :key="item">
            {{ index }}{{ key }}{{ item }}
        </li>
    </ul>
</div>
<script>
    var vue = new Vue({
        el: "#app",
        data:{
            obj:{
                name: '张三',
                age: 21,
                addr: '北京市',
            },
        }
    })
</script>

5、Vue事件

v-on:或者@

事件

<div id="app">
    <div>
        {{ num }}
    </div>
    <div>
        <button v-on:click="add">点我 +</button>
        <button @dblclick="num--">点我 -1</button>  <!-- dblclick双击鼠标 -->
    </div>
</div>
<script>
    var vue = new Vue({
        el: "#app",
        data:{
            num: 1,
        },
        methods:{
            add(){
                this.num++;
            }
        }
    })
</script>

参数问题,可以通过传参进行参数传递

<div id="app">
    <div>
        {{ num }}
        <button v-on:click="add(10)">点我 +</button>
    </div>
</div>
<script>
    var vue = new Vue({
        el: "#app",
        data:{
            num: 1,
        },
        methods:{
            add(number) {
                this.num += number
            }
        }
    })
</script>

默认参数,默认参数就是触发当前事件的事件对象

一定是v-on:click="add",只写一个函数名,而不是v-on:click="add()",这样获取不到事件对象

<div id="app">
    <div>
        {{ num }}
        <button id="add" class="add_cls" v-on:click="add">点我 +</button>
    </div>
</div>
<script>
    var vue = new Vue({
        el: "#app",
        data:{
            num: 1,
        },
        methods:{
            add(event) {
                this.num++;
                // 触发当前事件的事件对象
                console.log(event)
                // 获取标签
                console.log(event.target)
                // 获取id,class
                console.log(event.target.id)
                console.log(event.target.className)
            }
        }
    })
</script>

如果有参数,想接收事件对象,使用$event进行传递

<script src="./vue.js"></script>
<div id="app">
    <div>
        {{ num }}
        <button id="add" class="add_cls" v-on:click="add(10, $event)">点我 +</button>
    </div>
</div>
<script>
    var vue = new Vue({
        el: "#app",
        data:{
            num: 1,
        },
        methods:{
            add(number, event) {
                this.num += number
                // 触发当前事件的事件对象
                console.log(event)
                // 获取标签
                console.log(event.target)
                // 获取id,class
                console.log(event.target.id)
                console.log(event.target.className)
            }
        }
    })
</script>

6、图片轮播案例

<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>图片轮播</title>
        <script src="./vue.js"></script>
    </head>
    <body>
        <div id="app">
            <div>
                <img :src="img_list[index]" alt="">
            </div>
            <div>
                <button @click="prevImag(index, img_list.length)">上一张</button>
                <button @click="nextImag">下一张</button>
            </div>
        </div>
        <script>
            var vue = new Vue({
                el: '#app', // vue挂载的位置,不能是body
                data: {
                    index: 0,
                    img_list: [
                        'https://c-ssl.dtstatic.com/uploads/blog/202210/05/20221005212142_1ac33.thumb.400_0.jpg',
                        'https://c-ssl.dtstatic.com/uploads/blog/202210/05/20221005212142_48820.thumb.400_0.jpg',
                        'https://c-ssl.dtstatic.com/uploads/blog/202210/05/20221005212142_e0b09.thumb.400_0.jpg',
                        'https://c-ssl.dtstatic.com/uploads/blog/202210/05/20221005212142_efdaf.thumb.400_0.jpg',
                        'https://c-ssl.dtstatic.com/uploads/blog/202210/05/20221005212142_6e9d5.thumb.400_0.jpg',
                        'https://c-ssl.dtstatic.com/uploads/blog/202210/05/20221005212142_c33fb.thumb.400_0.jpg',
                    ]
                },
                methods: {
                    // 下一张
                    nextImag() {
                        // 让索引加1
                        // 判断是不是最后一张,如果是就回到第一张
                        if (this.index === this.img_list.length - 1) {
                            this.index = 0
                            return
                        }
                        this.index++
                    },
                    // 上一张
                    prevImag(index, len){
                        // 我希望把索引和列表长度传递进来
                        if (index === 0) {
                            this.index = len - 1
                            return
                        }
                        this.index --
                    }
                }
            })
        </script>
    </body>
</html>

事件修饰符

在事件处理程序中调用 event.preventDefault()event.stopPropagation() 是非常常见的需求。尽管我们可以在方法中轻松实现这点,但更好的方式是:方法只有纯粹的数据逻辑,而不是去处理 DOM 事件细节

为了解决这个问题,Vue.jsv-on 提供了事件修饰符。之前提过,修饰符是由点开头的指令后缀来表示的

<!-- 阻止单击事件继续传播 -->
<a v-on:click.stop="doThis"></a>

<!-- 提交事件不再重载页面 -->
<form v-on:submit.prevent="onSubmit"></form>

<!-- 修饰符可以串联 -->
<a v-on:click.stop.prevent="doThat"></a>

<!-- 只有修饰符 -->
<form v-on:submit.prevent></form>

<!-- 添加事件监听器时使用事件捕获模式 -->
<!-- 即内部元素触发的事件先在此处理,然后才交由内部元素进行处理 -->
<div v-on:click.capture="doThis">...</div>

<!-- 只当在 event.target 是当前元素自身时触发处理函数 -->
<!-- 即事件不是从内部元素触发的 -->
<div v-on:click.self="doThat">...</div>
<!--阻止默认事件-->
<a href="http://www.baidu.com" @click.prevent="my_alert">杨小槿</a>
<script src=".vue.js"></script>
<div id="app">
    <a href="http://www.baidu.com" @click="my_alert">百度</a>
</div>
<script>
	new Vue({
        el: '#app',
        methods:{
            my_alert(e){
                console.log(e.target)
                console.log(e.preventDefault())  // event.preventDefault() vent.stopPropagation()
                alert("是否跳转")
            }
        }
    })
</script>
事件冒泡
<div class="parent" @click="log('外元素')">
    <div class="center" @click="log('中元素')">
        <div class="child" @click="log('里元素')">
        </div>
    </div>
</div>
<!-- 点击里元素,输出里中外(这就是冒泡);点击中,输出中外 -->
<!-- 被父元素包裹的元素,点击事件发送后会逐级将事件向上传递 -->

img

<p>阻止事件冒泡</p>
<div class="parent" @click="log('外元素')">
    <div class="center" @click="log('中元素')">
        <div class="child" @click.stop="log('里元素')">
        </div>
    </div>
</div>
<!-- 添加stop事件修饰符之后,点击里元素,会阻止事件继续向上传播 -->

键盘事件

@keydown   键盘按下
@keyup     键盘回弹

按下回车触发,支持组合键

9、v-model双向数据绑定

<input type="text" v-model="msg">
<input type="text" :value="msg">
...
date:{
	msg:"abcd",
}
<div id="app">
    <div>
        <input type="text" v-model.lazy="msg">  <!-- .lazy事件修饰符,失焦后再改变 -->
        <button @click="getMsg">获取</button>
        <select v-model="selected">
            <!-- 变成多选<select v-model="selected" multiple>,然后把selected改成列表 -->
            <option :value="item.value" :key="item.value" v-for="item in select_list">
                {{item.label}}
            </option>
        </select>
    </div>
</div>
<script>
    var vue = new Vue({
        el:'#app',
        data:{
            msg:"杨小槿",
            selected: 2,
            select_list:[
                {value: 1, label:'麻辣香锅'},
                {value: 2, label:'铁锅炖'},
                {value: 3, label:'烤肉'},
                {value: 4, label:'火锅'},
            ]
        },
        methods:{
            getMsg(){
                console.log(this)
            }
        }
    })
</script>
<input type="text" v-model="msg" @keyup.enter.ctrl="get_msg('上')">
<input type="text" v-model="msg" @keyup.enter="get_msg('上')">

使用修饰符时,顺序很重要;相应的代码会以同样的顺序产生。因此,用 v-on:click.prevent.self 会阻止所有的点击,而 v-on:click.self.prevent 只会阻止对元素自身的点击。

2.1.4新增
<!-- 点击事件将只会触发一次 -->
<a v-on:click.once="doThis"></a>

按键修饰符

<!-- 最常见的可能就是在一个输入框中,判断用户是否按下了回车键 -->
<!-- 只有在 `key` 是 `Enter` 时调用 `vm.submit()` -->
<input v-on:keyup.enter="submit">
<!-- 一些必要的按键名称 -->
.enter
.tab
.delete (捕获“删除”和“退格”键)
.esc
.space
.up
.down
.left
.right
<!-- 组合按键 -->
<!-- Alt + C -->
<input v-on:keyup.alt.67="clear">
<!-- Ctrl + Click -->
<div v-on:click.ctrl="doSomething">Do something</div>

7、计算属性

  • 调用的时候不用加括号(只是一个属性)

  • 可以监听属性变化,属性变化,计算属性重新执行

  • 有缓存(多个计算属性,只执行一次)

和methods的区别

属性变化,methods方法全部重新获取

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>计算属性</title>
    <script src="../vue.js"></script>
</head>
<body>
<div id="app">
    <div>{{ name }}</div>
    <div>{{ name.split('').reverse().join('') }}</div>
    <div>{{ name.length === 5 ? '五言绝句' : '七言绝句' }}</div>
    <div v-if="name.length === 5">五言绝句</div>
    <div v-else-if="name.length === 7">七言绝句</div>
    <div v-else>错了</div>

    <!--  计算属性  -->
    <div>{{ getName }}</div>
    <div>{{ getName }}</div>
    <div>{{ get_data_name() }}</div>
    <div>{{ get_data_name() }}</div>
    <button @click="getsub">点我</button>
    <span>{{ num }}</span>
    <button @click="set_name">计算属性</button>
</div>
<script>
    var vue = new Vue({
        el: '#app', // vue挂载的位置,不能是body
        data: {
            name: '床前明月光',
            num: 0
        },
        methods: {
            get_data_name() {
                console.log('属性方法')
                return this.name.split('').reverse().join('')
            },
            getsub(e) {
                this.num ++
            },
            set_name(){
                this.name += 'a'
            }
        },

        computed: {
            getName() {
                console.log('计算属性')
                return this.name.split('').reverse().join('')
            }
        }
    })
</script>
</body>
</html>

computed与watch,methods的区别

computed:有缓存(多个计算属性,只执行一次),节省了性能开销,计算属性不能传值

methods:可以放入函数,并且没有缓存

watch:监听,当数据发送变化时,才会触发,可以得到现在的值和过去的值,还可以监听路由变化,和属性同名

8、自定义过滤器

计算属性不能传值不好搞

<script src="./vue.js"></script>
<div id="app">
    <li v-for="item in student_list" :key="item.id">
        {{item.name|getLastChar }} -- {{ item.addr }}
    </li>
</div>
<script>
    var vue = new Vue({
        el: '#app',
        data: {
            student_list: [
                {name: '张三', addr: '北京市海淀区', id: 1 },
                {name: '李四', addr: '上海市浦东区', id: 2 },
                {name: '王五', addr: '广州市天河区', id: 3 }]
        },
        // 自定义过滤器
        filters:{
            // 截取最后一个字符
            getLastChar(item)
            {
                return item.substr(item.length - 1, 1) // .substr是截取字符串的函数
            }
        }
    })
</script>

时间过滤器

img

<div id="app">
    <div>
        当前时间:{{ now|get_date }}
    </div>
    <ul>
        <li v-for="item in date_list" :key="item.id">
            id:{{ item.id }} 时间:{{ item.date|get_date }}
        </li>
    </ul>
    <div>
        时间过滤
    </div>
    <ul>
        <li v-for="item in date_list" :key="item.id">
            id:{{ item.id }} 时间:{{ item.date|time_to_filter }}
        </li>
    </ul>
</div>
<script src="../vue.js"></script>
<script>
    function getDateDiff(datestamp) {
        var publishTime = datestamp / 1000,
            d_seconds,
            d_minutes,
            d_hours,
            d_days,
            timeNow = parseInt(new Date().getTime() / 1000),
            d,
            date = new Date(publishTime * 1000),
            Y = date.getFullYear(),
            M = date.getMonth() + 1,
            D = date.getDate(),
            H = date.getHours(),
            m = date.getMinutes(),
            s = date.getSeconds();
        //小于10的在前面补0
        if (M < 10) {
            M = '0' + M;
        }
        if (D < 10) {
            D = '0' + D;
        }
        if (H < 10) {
            H = '0' + H;
        }
        if (m < 10) {
            m = '0' + m;
        }
        if (s < 10) {
            s = '0' + s;
        }
        d = timeNow - publishTime;
        d_days = parseInt(d / 86400);
        d_hours = parseInt(d / 3600);
        d_minutes = parseInt(d / 60);
        d_seconds = parseInt(d);
        if (d_days > 0 && d_days < 30) {
            return d_days + '天前';
        } else if (d_days <= 0 && d_hours > 0) {
            return d_hours + '小时前';
        } else if (d_hours <= 0 && d_minutes > 0) {
            return d_minutes + '分钟前';
        } else if (d_seconds < 60) {
            if (d_seconds <= 0) {
                return '刚刚发表';
            } else {
                return d_seconds + '秒前';
            }
        } else if (d_days >= 30) {
            return Y + '-' + M + '-' + D + ' ' + H + ':' + m;
        }
    }

    var vue = new Vue({
        el: '#app',
        data: {
            now: '2021-12-8 22:01:11',
            date_list: [
                {id: 1, date: '2021-12-8 22:02:11'},
                {id: 2, date: '2021-12-6 22:02:21'},
                {id: 3, date: '2021-12-8 21:01:14'},
                {id: 4, date: '2021-12-5 20:12:11'},
            ]
        },
        filters: {
            // 年-月-日
            get_date(date_str) {
                // date,传递的参数

                // 字符串转时间对象
                let date = new Date(date_str)
                return `${date.getFullYear()}-${date.getMonth() + 1}-${date.getDate()}`
            },
            time_to_filter(date_str) {
                // 字符串->对象->时间戳
                // 和当前时间做差,算出相差的时间(多少秒,多少分)
                // 字符串转时间对象->时间戳
                let datestamp = new Date(date_str).getTime()
                return getDateDiff(datestamp)
            }
        }
    })
</script>

参数问题

自定义过滤器也是可以接受参数,它的书写语法是

<script src="./vue.js"></script>
<div id="app">
    <li v-for="item in date_list" :key = "item.id" >
        id: {{ item.id }}
        时间: {{ item.date | time_to_filter('a', 'b') }} 
    </li>
</div>
<script>
    var vue = new Vue({
        el: '#app',
        data: {
            date_list: [
                {id: 1, date: '2021-01-01 12:00:00'},
                {id: 2, date: '2021-01-02 13:00:00'},
            ]
        },
        // 自定义过滤器
        filters: {
            time_to_filter(date_str, a, b) 
            {
                // date_str  ->  '自身的值'
                // a  -> 'a'
                // b  -> 'b'
                return date_str
            }
        }
    })
</script>

过滤器方法的第一个参数就是调用者本身的值,第二个参数之后就是调用的实参,并且支持链式过滤器

<div>{{now|get_now|get_now1}}</div>

全局方法

需要将属性和方法挂载到Vue.prototype

Vue.prototype.$com = "全局变量"
let global_method = () => {
    return "全局方法"
}
function add() {
    return "全局 add 方法"
}
Vue.prototype.$global_method = global_method
Vue.prototype.$add = add

使用

<div>
    {{ $global_method() }}
</div>
<div>
    {{ $add() }}
</div>

全局过滤器

// 全局过滤器
let global_filter = (item) => {
    return item + '--global'
}
// 注册全局过滤器   (过滤器名称,方法)
Vue.filter('global_filter', global_filter)

使用全局过滤器

<div>
    {{ $com|global_filter }}
</div>

10、vue组件

内容共同使用,但是数据是相互隔离的 components

局部组件

<script src="./vue.js"></script>
<div id="app">
    <App></App>  <!--使用子组件-->
</div>
<script>
    // app 组件 html css js
    const App = {
        template: `
                  <div>
                      <h3>我是app组件</h3>
                      <p>{{ msg }}</p>
                      <button @click="C">按一下改变msg</button>
    			</div>`,
        data() {
            return {msg: 'hello'}
        },
        methods: {
            C() {
                this.msg = 'world'
            }
        },
        created(){  // 只要有组件调用就会执行
            console.log(1)
        },
    }
    new Vue({
        el: '#app',
        data: {},
        components: {
            App  // 挂载子组件
        }
    })
</script>

全局组件

<div id="app">
    <App></App>
</div>
<script>
    Vue.component('Vheader', {  // component注册
        template: `
                    <div>
                        <span>我是导航组件</span>
    </div>`})
    Vue.component('Vsider', {
        data() {
            return {msg: '侧边栏'};
        },
        template: `
                    <div>
                      <span>我是侧边栏的msg: {{ msg }}</span>
    </div>`,})
    const Vbtn = {
        template: `<button>按钮</button>`
    }
    const App = {
        template: `
                    <div>
                        <Vheader></Vheader>
                        <Vbtn></Vbtn>
                        <Vsider></Vsider>
                        <h3>我是app组件</h3>
                        <p>{{ msg }}</p>
                        <button @click="C">按钮</button>
    </div>`,
        data() {
            return {msg: 'hello'}
        },
        components: {
            Vbtn
        },
        methods: {
            C() {
                this.msg = 'world'
            }
        }
    }
    new Vue({
        el: '#app',
        data: {},
        components: {
            App
        }
    })
</script>

11、组件通信

父传子 props

父传子:通过props来进行通信

1. 在自组件中声明props接收在父组件挂载的属性
2. 可以在自组件的template在任意使用
3, 在父组件绑定自定义的属性

props传入一个对象

// 例: props的值
props: ['title', 'likes', 'isPublished', 'commentIds', 'author']
props: {
title: String,
likes: Number,
isPublished: Boolean,
commentIds: Array,
author: Object,
callback: Function,
contactsPromise: Promise // or any other constructor
}
props: {
 data: {
     type: String,  // 类型
     required: true,  // 必填项
     default: '张三'  // 如果没传值,那么这个默认值就是它
 }
}
<div id="app">
    <App></App>  <!--使用子组件-->
</div>
<script>
    Vue.component('Child', {   // 定义子组件
        template: `
                  <div>
                    <h3>这是子组件</h3>
                    <h3> 222 {{ childData }} </h3>
    </div>
                `,
        props: ['childData']  // 接收父组件传递的属性
    })
    const App = {   // 定义父组件
        data() {  // 父组件的数据
            return {msg: '111'}
        },
        template: `
                  <div>
                    <Child :childData="msg"></Child>
                    <p>{{ msg }}</p>
    </div>
                `,
    }
    new Vue({
        el: '#app',
        data: {},
        components: {
            App  // 注册父组件
        }
    })
</script>

created与mounted

created(){
 console.log(this.data, 1)
},
mounted(){
 console.log(this.data, 2)
}

子传父

1. 在父组件中,自组件上绑定自定义事件
2. 在自组件中,触发原生的事件,在事件函数通过this.$emit触发自定义的事件
<div id="app">
    <div> 父组件 {{num}} </div>
    <div>
        <my_header v-on:add2="parent_add"/>
    </div>
</div>
<script>
    let my_header = {
        template:`
                        <div>
                            <button @click="add1">父组件值+1</button>
    </div>`,
        data(){
            return{}
        },
        methods:{
            add1(){
                this.$emit('add2', {msg: '来自子组件的数据'})
            }
        },
    }
    new Vue({
        el: '#app',
        data:{
            num:10
        },
        components:{
            my_header
        },
        methods:{
            parent_add(data){
                console.log(data)
                this.num += 1
            }
        }
    })
</script>

平行组件

<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>平行组件</title>
        <script src="./vue.js"></script>
        <style>
            body {
                margin: 0;
            }
            #app {
                display: flex;
            }
            #app > div {
                width: 50%;
                height: 200px;
                display: flex;
                justify-content: center;
                align-items: center;
            }
            #gouwuc {
                background-color: #3a8ee6;
            }
            #get {
                background-color: #b3d9d9;
            }
        </style>
    </head>
    <body>
        <div id="app">
            <App1 id="gouwuc"></App1>  <!--使用子组件-->
            <App2 id="get"></App2>  <!--使用子组件-->
        </div>
        <script>
            let Bus = new Vue()
            let App1 = {
                data() {
                    return {
                        num: 100
                    }
                },
                template:`
                    <div>
                        购物车 商品总数{{ num }}
            </div>`,
                mounted(){
                    Bus.$on('add2', (data)=>{
                        this.num ++
                    })
                }
            }
            let App2 = {
                template: `
                    <div>
                        <button @click="add1">加入购物车</button>
            </div>`,
                data() {
                    return {
                        num: 100
                    }
                },
                methods: {
                    add1() {
                        Bus.$emit('add2', {msg: '你的好兄弟又下单啦'})
                    }
                },
            }
            new Vue({
                el: '#app',
                data: {},
                components: {
                    App1,
                    App2,
                }
            })
        </script>
    </body>
</html>

多次嵌套取值

1. provide  提供变量  函数(){return{}},
2. inject  接收变量 inject: [],

12、vue插槽

匿名插槽

修改已经写好的组件

在模板中把想要替换的东西放在<slot>标签中,这就是一个占位符

<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>杨小槿</title>
        <script src="./vue.js"></script>
        <style> 
            body{
                margin: 0;
            }
            #header{
                display: flex;
                height: 60px;
            }
            #header>div{
                width: 50%;
                display: flex;
                align-items: center;
                height: 100%;
            }
        </style>
    </head>
    <body>
        <div id="app"> 
            <my_header>首页</my_header>
            <!-- <my_header>111</my_header> -->
            <!-- 这样就会将标签中的内容去代替slot中的内容 -->
        </div>
        <script>
            let my_header = {
                data() {
                    return {}
                },
                template: `
                <div id="header">
                    <div class="left">
                        <slot>左部分</slot>
                    </div>
                    <div class="right">
                        <slot>右部分</slot>
                        <p>如果slot都没有名字,那么如果有多个slot,就会全部进行替换</p>
                    </div>
                </div>`
            }   
            new Vue({
                el: '#app',
                components: {
                    my_header
                }
            })
        </script>
    </body>
</html>

具名插槽

如果你的模板中需要有多个地方被替换,那么匿名插槽就不适用

<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>杨小槿</title>
        <script src="./vue.js"></script>
        <style> 
            body{
                margin: 0;
            }
            #header{
                display: flex;
                height: 60px;
            }
            #header>div{
                width: 50%;
                display: flex;
                align-items: center;
                height: 100%;
            }
        </style>
    </head>
    <body>
        <div id="app"> 
            <my_header>
            	<template v-slot:left>左左左左左</template>
                <!-- v-slot:  可以简写为  # -->
                <template v-slot:right>右右右右右</template>
            </my_heade>
        </div>
        <script>
            let my_header = {
                data() {
                    return {}
                },
                template: `
                <div id="header">
                    <div class="left">
                        <slot name="left">左部分</slot>
                    </div>
                    <div class="right">
                        <slot name="right">右部分</slot>
                        <!--若有多个slot,并且都没名字,那么就会全部进行替换-->
                    </div>
                </div>`
            }   
            new Vue({
                el: '#app',
                components: {
                    my_header
                }
            })
        </script>
    </body>
</html>
<div id="app"> 
    <my_header>
        <template v-slot:left>左左左左左</template>
        <!-- v-slot:  可以简写为  # -->
        <template #right>右右右右右</template>
        </my_heade>
</div>

我们在替换的时候,是可以写任意标签的,也就是说,在替换的时候,你也可以将组件写入插槽中

<div id="app"> 
    <my_header>
        <template v-slot:left>左左左左左</template>
        <!-- v-slot:  可以简写为  # -->
        <template v-slot:right>右右右右右</template>
        <child></child>
        </my_heade>
</div>
<script>
    let child = {
        data(){
            return {}
        },
        template: `<li>子组件</li>`
    }
    Vue.component('child', child)

    let my_header = {
        data() {
            return {
                user: {
                    name: "杨小槿",
                    age: 18,
                }
            }
        },
        template: `
                <div id="header">
                    <div class="left">
                        <slot name="left">左部分</slot>
    </div>
                    <div class="right">
                        <slot name="right">{{ user.name }}</slot>
                        <!--若有多个slot,并且都没名字,那么就会全部进行替换-->
    </div>
    </div>`
    }   
    new Vue({
        el: '#app',
        components: {
            my_header
        }
    })
</script>

作用域插槽

<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>杨小槿</title>
        <script src="./vue.js"></script>
        <style> 
            body{
                margin: 0;
            }
            #header{
                display: flex;
                height: 60px;
            }
            #header>div{
                width: 50%;
                display: flex;
                align-items: center;
                height: 100%;
            }
        </style>
    </head>
    <body>
        <div id="app"> 
            <my_header>
                <template v-slot:left>左左左左左</template>
                <!-- v-slot:  可以简写为  # -->
                <template v-slot:right="slotProps">{{ slotProps.user.age }}</template>
            </my_heade>
        </div>
        <script>
            let my_header = {
                data() {
                    return {
                        user: {
                            name: "杨小槿",
                            age: 18,
                        }
                    }
                },
                template: `
                        <div id="header">
                            <div class="left">
                                <slot name="left">左部分</slot>
            </div>
                            <div class="right">
                                <slot :user="user" name="right">{{ user.name }}</slot>
                                <!--若有多个slot,并且都没名字,那么就会全部进行替换-->
            </div>
            </div>`
            }   
            new Vue({
                el: '#app',
                data: {},
                components: {
                    my_header
                }
            })
        </script>
    </body>
</html>

在子组件中,我在插槽里面显示的是user的xin,如果我想让它显示user的name应该怎么做呢

如果我们直接在父组件调用的时候这样写

<base_layout>
 {{ user.name }}
</base_layout>

这是错误的写法,因为我们的user是被定义再自组件中的,在父组件中就无法使用这个user,所以就会报错啦

那么我们就应该将这个user对象传递给父组件

在子组件中

<slot :user="user">{{ user.xin }}</slot>

通过动态属性的方式将user传递给父组件

在父组件中

<base_layout>
 <template v-slot:default="slotProps">
     {{ slotProps.user.name }}
 </template>
</base_layout>

接收传递来的user即可

slotProps可以是你自己定义的名字,它里面存储的是这样的

{ "user": { "name": "枫枫", "xin": "知道" } }

所以我们将slotProps中的user中的name传递给子组件,就可以做到数据动态替换

如果你的组件中,有且只有一个默认插槽,那么在替换的时候,是可以这么做的

<base_layout v-slot:default="slotProps">
 {{ slotProps }}
</base_layout>

13、自定义指令

动态组件

<component>元素是vue里面的一个内置组件。 在里面使用 v-bind: is,可以实现动态组件的效果

自定义指令

前面使用的v-if, v-show,以及v-for这些都是Vue为我们提供的内置指令

当然,我们也可以自己自定义一个指令

局部自定义指令

directives: {
    focus: {
        // 指令的定义
        // 使用这个指令就会聚焦在输入框中
        inserted: function (el) {
            el.focus()
        }
    }
}

使用

<input type="text" v-focus>

在focus中有几个钩子函数,需要了解

  • bind:只调用一次,指令第一次绑定到元素时调用。在这里可以进行一次性的初始化设置。

  • inserted:被绑定元素插入父节点时调用 (仅保证父节点存在,但不一定已被插入文档中)。

  • update:所在组件的 VNode 更新时调用,但是可能发生在其子 VNode 更新之前。指令的值可能发生了改变,也可能没有。但是你可以通过比较更新前后的值来忽略不必要的模板更新 (详细的钩子函数参数见下)。

绑定方法到this

Vue.prototype.meta_ = () => {
    console.log(1)
}

在created生命周期中不能使用

可以在mounted方法中使用

14、vue国际化

安装

npm install vue-i18n@9

基本使用

在src目录下创建lang目录

在此目录下创建对应语言的ts文件,如:

zh.ts

en.ts

kor.ts 韩文

jp.ts日文

zh.ts示例

export default {
  "首页": "首页",
  "新闻": "新闻",
  "产品": "产品",
  index: "首页"
};

en.ts

// en.js
export default {
    "首页": "Index",
    "新闻": "News",
    "产品": "Product",
    index: "Index"
}

然后新建一个index.tslang目录下

import {createI18n } from 'vue-i18n'
import zh from "@/lang/zh";
import en from "@/lang/en";

const i18n = createI18n({
    locale: "zh", // 默认是中文
    legacy: false, // 解决报错的
    messages: {
        "zh": zh,
        "en": en,
    }
})

export default i18n

然后在main.ts中进行注册

import {createApp} from 'vue'
import App from './App.vue'
import i18n from "@/lang/index"

const app = createApp(App)
app.use(i18n)
app.mount('#app')

在vue的模板中使用

<template>
    <main>
        <div>
    		<span>{{ $t("首页") }}</span>
            <span>{{ $t("新闻") }}</span>
            <span>{{ $t("产品") }}</span>
    	</div>
    </main>
	<header>
    		<button @click="setLang('zh')">中文</button>
        	<button @click="setLang('en')">英文</button>
    </header>
</template>
<script setup lang="ts">
    import {useI18n} from 'vue-i18n'
    const {t} = useI18n();
    console.log(t('index'))
    
    function setLang(lang:string){
        locale.value = lang
    }
</script>

如果$t警告的话,就在env.d.ts里面加上

/// <reference types="vite/client" />
declare function $t()

翻译数据从后端来

后端

npm i axios
npm i mockjs
npm i @types/mockjs

准备两个接口

一个是展示有哪些语言的

一个是有对应语言的字典文件

export function langList(): Promise<string[]> {
    return useAxios.get("/api/langs")
}
export function langDetail(lang: string): Promise<object> {
    return useAxios.get("/api/langs/detail", {params: {lang}})
}

mock

import {mock} from "mockjs";
mock(/api\/langs\/detail/, function (options) {
    if (options.url.includes("zh")) {
        return {
            "首页": "首页",
            "新闻": "新闻",
            "产品": "产品",
            index: "首页"
        }
    }
    if (options.url.includes("en")) {
        return {
            "首页": "Index",
            "新闻": "News",
            "产品": "Product",
            index: "Index"
        }
    }
})
mock(/api\/langs/, ["zh", "en"])

页面语言切换

<template>
<header>
    <button @click="setLang(item)" v-for="item in langs">{{ item }}</button>
    </header>
<main>
    <span>{{ $t("首页") }}</span>
    <span>{{ $t("新闻") }}</span>
    <span>{{ $t("产品") }}</span>
    </main>
</template>

<script setup lang="ts">
    import {useI18n} from 'vue-i18n'
    import {langList, langDetail} from "@/api";
    import {ref} from "vue";
    const {locale, t, messages} = useI18n();
    const langs = ref([])
    async function getData() {
        langs.value = await langList()
    }
    getData()
    async function setLang(lang: string) {
        messages.value[lang] = await langDetail(lang)
        console.log(messages.value)
        locale.value = lang; // 切换语言
    }
</script>

参考文档

官网

Not available in legacy mode

在ts中使用

配置i18n国际化


http://www.kler.cn/a/579311.html

相关文章:

  • Pytorch 第九回:卷积神经网络——ResNet模型
  • Ae 效果详解:VR 平面到球面
  • CmBacktrace的学习跟移植思路
  • 基于国产芯片的AI引擎技术,打造更安全的算力生态 | 京东零售技术实践
  • 关于OceanBase与CDH适配的经验分享
  • VMware笔记(断续更新)
  • 引领变革!北京蜂巢世纪科技有限公司荣获“GAS消费电子科创奖-产品创新奖”!
  • 【面试】Java 集合
  • MWC 2025 | 紫光展锐与中国联通联合发布5G eSIM 平板
  • 【对话推荐系统综述】Broadening the View: Demonstration-augmented Prompt Learning for CR
  • http协议的三次握手机制
  • Browser Use+DeepSeek的使用教程
  • Android中AIDL和HIDL的区别
  • 【每日学点HarmonyOS Next知识】对话框去掉圆角、数组拼接、自定义对话框依附某个控件、平移动画、页面栈管理
  • Vue项目通过内嵌iframe访问另一个vue页面,获取token适配后端鉴权(以内嵌若依项目举例)
  • 硬件学习笔记--48 磁保持继电器相关基础知识介绍
  • 2025最新群智能优化算法:云漂移优化(Cloud Drift Optimization,CDO)算法求解23个经典函数测试集,MATLAB
  • P8685 [蓝桥杯 2019 省 A] 外卖店优先级--优先队列“数组”!!!!!
  • DeepSeek-R1入门指南:架构、训练、本地部署和硬件要求
  • 图像形成与计算机视觉基础