vue3学习笔记---小黑是小白

1 插值语法和绑定语法

<script >
export default {
  data() {
    return {
      msg: '你好,vue',
      url: "http://www.baidu.com",
      input:{
        value:1234
      }
    }
  }

}
script>

<template>
  <div>
    
    {{ msg }}
  div>

  <div>
    
    
    <a v-bind:href="url">点击跳转百度a>
    
    <a :href="url">点击跳转百度a>
  div>

  <div>
    
   单项数据绑定: <input type="text" name="" id="" :value="input.value"><br>
      
      
   
   
   双向数据绑定: <input type="text" name="" id="" v-model="input.value">
  div>
template>

<script setup lang="ts">
import { ref } from 'vue'
const a: number = 1;
const b: number = 0;
const c: number[] = [1, 2, 3];
const d: string = '我是ts版vue'
const e: string = '

我是ts版vue

'
const f: boolean = true
script> <template> {{ a }} <br><br> {{ b ? 'B为1' : 'B为0' }} <br><br> {{ c.map(item => ({ 数组索引: item - 1 })) }} <div v-for='item in c'>{{item}}div> <br><br> <div v-text="d">div> <br><br> <div v-html="e">div> <br><br> <div v-if="f">如果为true就会展示这段内容,v-if 会在节点上控制DOM的显现,还有v-else-if,v-elsediv> <div v-show="f">如果为true就会展示这段内容,v-show只是在样式上控制DOM的显现div> template> <style scoped>style>

2 事件处理

methods

vue2中,有一个属性 methods 专门用来存储事件方法,在setup直接写

<div id="root">
    <button v-on:click = "showInfo">点击我,弹出提示信息button>
    
div>

<script setup>
     const showInfo=()=> {
            alert('同学您好')
        }
script>
当我们一点击按钮,就会弹出对话框,当然这里的 showInfo 方法中,我们也可以加 event 参数,和 JS 语法中一样
带参数的方法

<div id="root">
    <button v-on:click = "showInfo($event, 666)">点击我,弹出提示信息button>
div>

<script>
      const showInfo=(event, number)=> {
           alert('同学您好' + number);
           console.log(event);
       }
script>

如果我们想同时传入 event 事件和自定义参数,那么需要在模板中的事件上添加 $event 来帮助 event 事件参数进行占位
事件的回调需要配置在 methods 对象中,最终会绑定在 vm 上(虽然也可以直接配置在 data 对象中,但是不推荐)

@click = “demo” 和 @click = “demo($event)” 效果一致,后者只是多传入了一个 event 的对象

事件修饰符

Vue 中的事件修饰符(常见):

prevent:阻止默认事件
stop:阻止事件冒泡(放在子级元素身上)
once:事件只触发一次
capture:使用事件的捕获模式(放在父级元素身上)
self:只有 event.target 是当前操作的元素才触发事件
passive:事件的默认行为立即执行,无需等待事件回调执行完毕
sroll 是滚动条的事件,每次触发好多次
wheel 是滚轮的事件,每次触发一次

DOM事件的执行是从根元素随着嵌套到最底层,再对上冒泡

<div id="root">
    
    <div>
        <a href="http://www.baidu.com" @click.prevent ="showInfo" class="box1">百度a>
    div>
    
    <div @click = "showInfo">
        <button @click.stop = "showInfo">点击button>
    div>
    
    <div>
        <button @click.once = "showInfo">点击button>
    div>
    
    <div @click.capture = "showInfo">
        <button @click = "showMsg">点击button>
    div>
    
    
    <div @click.self = "showInfo">
        <button @click.stop = "showMsg">点击button>
    div>
div>
<script>
    const vm = new Vue({
        el: '#root',
        data: {
            name: '张三',

        },
        methods: {
            showInfo(e) {
                // e.preventDefault();
                alert('Vue 很有趣!!')
            },
            showMsg(e) {
                alert('所以,同学快来学 Vue')
            }
        }
    })
script>

键盘事件

Vue 中给常用的按钮起了别名:

回车 => enter
删除 => delete(捕获 “删除” 和 “退格”)
退出 => esc
换行 => tab
上 => up
下 => down
左 => left
右 => right
这里的 tab 键比较特殊,因为 tab 的主要作用就是用来切换焦点的,所以当我们搭配 keyup 时,按钮会失效,只能搭配 keydown 进行触发

<div id="root">
    <input type="text" placeholder="按下回车触发事件" @keyup.enter = "showInfo">
div>
<script>
    // 定义别名按键
    // Vue.config.keyCodes.huiche = 13;
    const vm = new Vue({
        el:'#root',
        methods: {
            showInfo(e) {
                // console.log(e.key,e.keyCode)
                // if(e.keyCode == 13) {
                    console.log(e.target.value);
                // }
            }
        }
    })
script>

注意:键盘上的每一个按键其实都有自己的名称和编码,例如 回车键{ 名称:Enter; 编码: 13}

键盘事件的其他定义方式:

Vue 未提供别名的按键,可以使用按钮原始的 key 值去绑定,但是注意要转化为 kebab-case(短横线命名),例如回车键,我们可以使用 Enter,大小写切换键 CapsLock 可以使用 caps-lock

系统修饰符(用法特殊):ctrl、alt、shift、meta(即 windows 系统中的徽标键,mac 系统中的 command 键)

配合 keyup 使用:按下修饰符的同时,再按下其他键,随后释放其他键,事件才能触发
配合 keydown 使用:正常触发事件

也可以使用e. keycode 来指定具体的按钮(不推荐)

也可以使用e. code 来指定具体的按钮,能区分左右shift之类的

也可以使用e. key 来指定具体的按钮

Vue.config.keyCodes.自定义键名 = 键码(同样因为使用到 keycode ,不推荐)


3 ref

ref的基础使用

<script setup lang="ts">
// 非响应式的,值发生改变不会引起界面上的变化 
const man = { name: 'zs' }
const change = () => {
   man.name = 'ls'
   console.log(man);

}
script>

<template>
   {{ man }}
   <button @click="change">点击button>
template>
<script setup lang="ts">
// 非响应式的,值发生改变不会引起界面上的变化 
import { ref, isRef, shallowRef } from 'vue'

type m = {
   name: string
}

const man = ref<m>({ name: 'zs' })
const man1 = shallowRef<m>({ name: 'zs' })

const change = () => {
   man.value.name = 'ls'
   console.log(man);

   // isRef判断一个是不是ref对象
   // ref和shallowRef都是ref对象
   console.log(isRef(man));


}

const change1 = () => {
   man1.value.name = 'ls'
   console.log(man1);
}
const change2 = () => {
   man1.value = { name: 'ls' }
   console.log(man1);
}


// ref和shallowRef不能共用一个函数调用,不让会受到影响
// ref=shallowRef+triggerRef,只要triggerRef被调用,就会响应界面,所以两个一起用会受到影响
script>

<template>
   <h2>响应式数据,只有从最底层的属性name改变才能触发响应,在value中修改不会响应到界面h2>
   {{ man }}
   <button @click="change">点击button>
   <br><br>


   <h2>浅响应式数据,只有从value改变才能触发响应,在name中修改不会相应到界面h2>
   {{ man1 }}
   <button @click="change1">从name赋值button>
   <button @click="change2">从value赋值button>
template>

<style scoped>style>

自定义ref

<script setup lang="ts">
// 非响应式的,值发生改变不会引起界面上的变化 
import { customRef,isRef} from 'vue'


 const obj=myRef<string>('自定义ref')

 type t = {
   value:string
}

function myRef<T>(value:T) {
   return customRef((track,trigger) => {
         return{
            get(){
                //获取依赖
               track()
               return  value
            },
            set(newValue){
               console.log('触发了');
               value=newValue
                //进行依赖的更新
               trigger()
            }
         }
      }
   )
}

const change = () => {
  obj.value = '被修改了'
   console.log(obj);

   // isRef判断一个是不是ref对象
   // ref和shallowRef都是ref对象
   console.log(isRef(obj));


}


script>

<template>
  
   {{ obj }}
   <button @click="change">点击button>
template>

<style scoped>style>

自定义ref的防抖用例

<script setup lang="ts">
// 非响应式的,值发生改变不会引起界面上的变化 
import { ref, customRef, isRef } from 'vue'

    //ref还可以读取dom值,用法和react中差不多
 const dom=ref<HTMLDivElement>()

const obj = myRef<string>('自定义ref')

type t = {
   value: string
}

function myRef<T>(value: T) {
   let timer: any
   return customRef((track, trigger) => {
      return {
         get() {
            track()
            return value
         },
         set(newValue) {
            clearTimeout(timer)
            timer = setTimeout(() => {
               console.log('触发了');
               value = newValue
               trigger()
            }, 2000);


            console.log(dom.value?.innerText);
            
         }
      }
   }
   )
}

const change = () => {
   obj.value = '被修改了'
   console.log(obj);

   // isRef判断一个是不是ref对象
   // ref和shallowRef都是ref对象
   console.log(isRef(obj));


}


script>

<template>
   {{ obj }}
   <button @click="change">点击button>

   <div ref="dom">ref也可以读取domdiv>
template>

<style scoped>style>

4 reactive

<script setup lang="ts">

import { reactive, readonly,shallowReactive, toRaw } from 'vue'

// reactive proxy 不能直接赋值,不然会破坏响应式对象
//能通过数组方法来调用
// 通过数组的属性值来直接赋值也是可以的
const obj = reactive({
   name:'zs',
   age:13
})
const list = reactive([
   {name:'zs',age:13},
   {name:'ls',age:14},
   {name:'ww',age:11},
   {name:'sq',age:33},
]
)


//toRef只能用于响应式,用于将reactive中的属性单独提出来,适用场景为解构赋值
 const name1=toRaw(obj.name)
 const {name,age}=toRaw(obj)

console.log(name1);


// toRaw的作用就是将响应式对象变成一般对象,没啥应用场景

 const read=readonly(list)
//  readonly()只读的,不能修改


// const obj1 = shallowReactive({
//    name:'zs',
//    age:13
// })
//shallowReactive浅响应,只响应最顶层
// 不能和reactive混用


 const submit=() => {
   list.push(obj)
   
 }

script>

<template>
   <form action="">
      <input type="text" v-model="obj.name"><br>
      <input type="text" v-model="obj.age"><br>
      <button @click.prevent="submit">提交button>
      <br>
     <ol>
      <li v-for="item in list"> 姓名:{{ item.name }}, 年龄:{{ item.age }}li>
     ol>
   form>
template>

<style scoped>style>

5 响应式原理

看不懂就算了,没啥用,会用就行

let activeEffect;
export const effect=(fn:Function) => {
  const _effect=function () {
    activeEffect=_effect
    fn()
  } 
  _effect() 
}
 const targetMap=new WeakMap()
export  const track=(target,key) => {
    let depsMap=targetMap.get(target)
    if (!depsMap) {
        depsMap=new Map()
        targetMap.set(target,depsMap)
    }
    let deps=depsMap.get(key)

    if (!deps) {
        depsMap=new Set()
        depsMap.set(key,deps)
    }
    deps.add(activeEffect)
}

export  const trigger=(target,key) => {
     const depsMap=targetMap.get(target)
     const deps=depsMap.get(key)

     deps.forEach(effect => effect());
}
import { track, trigger } from "./effect";

export const reactive = <T extends object>(target: T) => {
  return new Proxy(target, {
    get(target, key, reactive) {
      let res = Reflect.get(target, key, reactive);
      track(target,key)
      return res;
    },
    set(target, key, reactive, value) {
      let res = Reflect.set(target, key, reactive, value);
      trigger(target,key)
      return res;
    },
  });
};
                                                                  

6计算属性

定义

动态的、要通过已有的属性进行计算而得来的属性

get 函数执行的时机:

1.初次读取的时候会执行一次
2.当依赖的数据发生改变时会被再次调用

优势:与 methods 实现相比较,内部存在缓存机制(复用),效率更高,调用更加方便

注意

1.计算属性最终会落到 vm 上,直接读取使用即可
2.如果计算属性要修改,必须写到 set 函数去响应修改,且 set 中要引起计算时依赖的数据改变

计算属性写法

正常写法

let fullName=computed({
      get(){
        return this.first_name.slice(0,3)+'-'+this.last_name.slice(0,3)
      },
       set(value){
           const arr=value.split('-'),
           this.first_name=arr[0],
           this.last_name=arr[1]
       }
     })

我们如果省略了 set 方法,那么可以将原来的 get 方法简写为:

 let fullName=computed( ()=>{
        return this.first_name.slice(0,3)+'-'+this.last_name.slice(0,3)
     }
  )

这个计算属性直接写成"方法",并且表示的就是 get 方法

当然,这样写是vue的语法糖,表示将 get 方法绑定到 fullName 属性上,而不是 fullName 就是一个方法,其本质上还只是一个计算属性

7 监听属性

基本示例

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

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

<script setup>
import { ref, watch } from 'vue'

const question = ref('')
const answer = ref('Questions usually contain a question mark. ;-)')

// 可以直接侦听一个 ref
watch(question, async (newQuestion, oldQuestion) => {
  if (newQuestion.indexOf('?') > -1) {
    answer.value = 'Thinking...'
    try {
      const res = await fetch('https://yesno.wtf/api')
      answer.value = (await res.json()).answer
    } catch (error) {
      answer.value = 'Error! Could not reach the API. ' + error
    }
  }
})
script>

<template>
  <p>
    Ask a yes/no question:
    <input v-model="question" />
  p>
  <p>{{ answer }}p>
template>

侦听数据源类型

watch 的第一个参数可以是不同形式的“数据源”:它可以是一个 ref (包括计算属性)、一个响应式对象、一个 getter 函数、或多个数据源组成的数组:

const x = ref(0)
const y = ref(0)

// 单个 ref
watch(x, (newX) => {
  console.log(`x is ${newX}`)
})

// getter 函数
watch(
  () => x.value + y.value,
  (sum) => {
    console.log(`sum of x + y is: ${sum}`)
  }
)

// 多个来源组成的数组
watch([x, () => y.value], ([newX, newY]) => {
  console.log(`x is ${newX} and y is ${newY}`)
})

注意,你不能直接侦听响应式对象的属性值,例如:

const obj = reactive({ count: 0 })

// 错误,因为 watch() 得到的参数是一个 number
watch(obj.count, (count) => {
  console.log(`count is: ${count}`)
})

这里需要用一个返回该属性的 getter 函数:

// 提供一个 getter 函数
watch(
  () => obj.count,
  (count) => {
    console.log(`count is: ${count}`)
  }
)

与 VUE2 中的 watch 不同,VUE3 可以多次使用 watch 方法,通过多个watch 方法来监听多个对象。而 VUE2 则是把所有的要监控的对象放在 watch 里面。

VUE2 代码:

watch: {
    nums () {},
    'demo.name' () {}
}

VUE3 代码:

watch(nums, () => {})
watch(() => demo.name, () => {})

关于 watch 的第三个参数,除了布尔类型的 deep,还有一个布尔类型的 immediate。源码中的接口声明如下:

export declare interface WatchOptions<Immediate = boolean> extends WatchOptionsBase
{
    immediate?: Immediate;
    deep?: boolean;
}

immediate 的作用就是设置是否立即执行监控,当我们将其值设置为 true 时,那么被监控的对象在初始化时就会触发一次 watch 方法,相当于页面一刷新就会触发。

深度侦听

如果侦听的属性内部还有其他属性,那么我们如果想要继续侦听内部的属性就需要用到参数 deep,将其设置为 true

export default {
 setup() {
 	 const route = useRouter();
 	 //获取当前路由地址
	 watch(()=>route, (newVal, oldVal) => {
           console.log(newVal)
           console.log( oldVal)
         },
         //深度监听
         {deep: true}
     )
 }

Vue 中的 watch 默认不监测对象内部值的改变
如果想要让 Vue 中的 watch 可以监测到对象内部值的改变,需要设置deep: true
注意:Vue 自身可以监测到对象内部值的改变,但是 Vue 提供的 watch 默认是监测不到的

侦听属性和计算属性的对比

computed 能完成的功能,watch 都能完成,反过来,watch 能完成的事情,computed 不一定就能完成
computed 所包含的计算属性的值通过 return 返回,就无法执行异步任务,例如设置一个定时器 setTimeout,而 watch 就可以进行异步任务
重要提醒:
被 Vue 管理的函数,最好写成普通函数,这样 this 的指向才会是 vm 或组件实例对象
不被 Vue 管理的函数(定时器的回调函数,ajax 的回调函数等),最好写成箭头函数,这样 this 的指向才会是 vm 或组件实例对象

高级监听属性

<template>
   <div>
<input type="text" v-model="message" name="" id=""><br>
<input type="text" v-model="message2" name="" id="">

   div>
template>

<script setup lang="ts">
import { ref,watchEffect } from 'vue';
let message=ref<string>('飞机')
let message2=ref<string>('飞机厂')
// watch只会在监听的值发生改变时调用,watchEffect在一开始就进行调用,和react的useEffect差不多
watchEffect((onInvalidate) => {
   console.log('message为'+message.value);


   onInvalidate(() => {
      console.log('重新更新时立即执行');
   })
   
})
script>

Vue3watchEffect侦听副作用传入的函数可以接收一个 onInvalidate 函数作为入参,用来注册清理失效时的回调。当以下情况发生时,这个失效回调会被触发:

  • 副作用即将重新执行时(即依赖的值改变)
  • 侦听器被停止 (通过显示调用返回值停止侦听,或组件被卸载时隐式调用了停止侦听)

watch vs watchEffect

watchwatchEffect 都能响应式地执行有副作用的回调。它们之间的主要区别是追踪响应式依赖的方式:

  • watch 只追踪明确侦听的数据源。它不会追踪任何在回调中访问到的东西。另外,仅在数据源确实改变时才会触发回调。watch 会避免在发生副作用时追踪依赖,因此,我们能更加精确地控制回调函数的触发时机。
  • watchEffect,则会在副作用发生期间追踪依赖。它会在同步执行过程中,自动追踪所有能访问到的响应式属性。这更方便,而且代码往往更简洁,但有时其响应性依赖关系会不那么明确。

停止侦听器

setup()

你可能感兴趣的:(学习,笔记,javascript,vue.js,vue)