• 周四. 4月 25th, 2024

5G编程聚合网

5G时代下一个聚合的编程学习网

热门标签

vue3入门

admin

11月 28, 2021

vue3

一、环境

* 需要nodejs环境
* 安装:npm i -g @vue/cli
* 查看版本(需要在4.5.0以上):vue -V
* 脚手架初始化项目:vue create my-project
    Please pick a preset:Manually select features

二、setup

1、ref
<template>
  <div>
    <div>{{count}}</div>
    <button @click="update">按钮</button>
  </div>
</template>

<script lang="ts">
  import {defineComponent, ref} from 'vue';

  export default defineComponent({
    name: 'App',
    // 初始化时执行,返回对象的属性和方法,模板中可以直接使用
    setup() {
      // ref(通常用于基本数据类型):定义一个响应式数据对象
      // js中需使用响应式数据对象的value属性操作数据
      // 如果ref()传入的是一个对象,则返回值.value是一个Proxy对象
      // 模板中直接使用响应式数据对象渲染数据
      const count = ref(0)
      const update = function () {
        count.value++
      }

      return {
        count,
        update
      }
    }
  });
</script>

<style>
</style>
2、reactive
<template>
  <div>
    <div>{{person}}</div>
    <button @click="update">按钮</button>
  </div>
</template>

<script lang="ts">
  import {defineComponent, reactive} from 'vue';

  export default defineComponent({
    name: 'App',
    setup() {
      // reactive:返回一个(深层的)响应式数据代理对象(ES6的Proxy对象)
      const person = reactive({
        name: 'linlong',
        age: 28,
        wife: {
          name: 'tongliya',
          age: 18
        }
      })

      const update = function () {
        person.name = 'wuxi'
        person.age = 5
        person.wife = {
          name: 'jiangsu',
          age: 28
        }
      }

      return {
        person,
        update
      }
    }
  });
</script>

<style>
</style>
3、一些细节知识点
* setup在beforeCreate之前执行
* setup中不能使用this来访问data/computed/methods/props
* setup不能是一个async函数(async函数返回的是一个promise)
* setup(props,{attrs,slots,emit})
    props:父组件传入的,并且props中声明的,包含的所有属性的对象
    attrs:父组件传入的,并且未在props中声明的,包含的所有属性的对象
    slots:插槽
    emit:分发事件

三、响应式数据原理

1、vue2
// vue2需要给每个属性都添加getter和setter(Object.defineProperty())
// vue3只需给属性是对象的添加代理操作(Proxy和Reflect)
// 数组元素无法直接修改,须通过$set()
Object.defineProperty(data, 'count', {
  get() {},
  set() {}
})
2、vue3
// 通过Proxy(代理): 拦截对对象属性的(13种)操作,包括属性的读写,添加, 删除(深度的)等...
// 通过 Reflect(反射): 动态对被代理对象的相应属性进行特定的操作
// 数组的元素可以直接修改(arr[0]=28)
new Proxy(data, {
  // 拦截读取属性值
  get(target, prop) {
    return Reflect.get(target, prop)
  },
  // 拦截设置属性值或添加新属性
  set(target, prop, value) {
    return Reflect.set(target, prop, value)
  },
  // 拦截删除属性
  deleteProperty(target, prop) {
    return Reflect.deleteProperty(target, prop)
  }
})

四、计算属性

<template>
  <div>
    {{person}}<br/>
    {{personCRI}}<br/>
  </div>
</template>

<script lang="ts">
  import {defineComponent, reactive, computed} from 'vue';

  export default defineComponent({
    name: 'App',
    setup() {
      const person = reactive({
        name: 'linlong',
        age: 28
      })

      // get函数中无法直接修改响应式数据的值
      /*const personCRI = computed(() => {
        const p = {...person}
        p.age++
        return p
      })*/

      const personCRI = computed({
        get() {
          const p = {...person}
          p.age++
          return p
        }, set(val: any) {
          person.name = val.name
          person.age = val.age
        }
      })
      // 必须personCRI.value={}方式修改值才会走set(不是深度的)
      personCRI.value = {
        name: 'wuxi',
        age: 5
      }

      return {
        person,
        personCRI
      }
    }
  });
</script>

<style>
</style>

五、监听属性

<template>
  <div>
    {{person}}<br/>
    {{count}}<br/>
    <button @click="update">按钮</button>
    <br/>
  </div>
</template>

<script lang="ts">
  import {defineComponent, ref, reactive, watch, watchEffect} from 'vue';

  export default defineComponent({
    name: 'App',
    setup() {
      const person = reactive({
        name: 'linlong',
        age: 28,
        wife: {
          name: 'tongliya',
          age: 18
        }
      })

      const count = ref(0)

      // ********
      /*function update() {
        person.wife.age++
      }
      watch(person, () => {
        console.log(person)
      }, {
        immediate: true,  // 是否初始化立即执行一次, 默认是false
        deep: true, // 是否是深度监视, 默认是false
      })*/

      // ********
      /*function update() {
        person.wife.age = 28
      }
      // 除等号之前,所有使用到的响应式数据都会监听
      // 默认 immediate: true,deep: false
      watchEffect(() => {
        person.wife.age = 1
      })*/

      // ********
      function update() {
        // count.value = 1
        person.wife.age = 16
      }
      // 监听ref对象可直接指定。监听reactive对象,需要通过函数指定
      watch([() => person.wife.age, count], () => {
        console.log(person)
      })

      return {
        person,
        count,
        update
      }
    }
  });
</script>

<style>
</style>

六、生命周期

<template>
<div class="about">
  <h2>msg: {{msg}}</h2>
  <hr>
  <button @click="update">更新</button>
</div>
</template>

<script lang="ts">
import {
  ref,
  onMounted,
  onUpdated,
  onUnmounted, 
  onBeforeMount, 
  onBeforeUpdate,
  onBeforeUnmount
} from "vue"

export default {
  beforeCreate () {
    console.log('beforeCreate()')
  },

  created () {
    console.log('created')
  },

  beforeMount () {
    console.log('beforeMount')
  },

  mounted () {
    console.log('mounted')
  },

  beforeUpdate () {
    console.log('beforeUpdate')
  },

  updated () {
    console.log('updated')
  },

  beforeUnmount () {
    console.log('beforeUnmount')
  },

  unmounted () {
     console.log('unmounted')
  },
  
  setup() {
    const msg = ref('abc')

    const update = () => {
      msg.value += '--'
    }

    onBeforeMount(() => {
      console.log('--onBeforeMount')
    })

    onMounted(() => {
      console.log('--onMounted')
    })

    onBeforeUpdate(() => {
      console.log('--onBeforeUpdate')
    })

    onUpdated(() => {
      console.log('--onUpdated')
    })

    onBeforeUnmount(() => {
      console.log('--onBeforeUnmount')
    })

    onUnmounted(() => {
      console.log('--onUnmounted')
    })
    
    return {
      msg,
      update
    }
  }
}
</script>

七、hooks函数

import {reactive, onMounted} from "vue"

export default function (name: string, age: number) {
  const person = reactive({
    name,
    age
  })

  onMounted(() => {
    console.log(person)
  })

  return {person}
}

八、toRefs

<script lang="ts">
  import {defineComponent, toRefs} from 'vue';

  export default defineComponent({
    name: 'App',
    setup() {
      const obj = {
        name: 'huangtingting',
        age: 18
      }
      // 会将对象的每一个属性都变为响应式数据(ref对象)
      const person = toRefs(obj)

      return {
        ...person
      }
    }
  });
</script>

九、ref的另一个作用

<template>
  <div ref="div"></div>
</template>

<script lang="ts">
  import {defineComponent, ref, onMounted} from 'vue';

  export default defineComponent({
    name: 'App',
    setup() {
      // 通过ref函数获取页面元素
      const div = ref<HTMLElement | null>(null)

      onMounted(() => {
        console.log(div.value)
      })

      return {
        div
      }
    }
  });
</script>

<style>
</style>

十、shallowReactive 与 shallowRef

浅度的reactive:对象的属性修改可以监听到,嵌套对象的属性修改不会监听到
浅度的ref:对象修改可以监听到,对象的属性修改不会监听到

十一、readonly 与 shallowReadonly

深只读:嵌套对象的属性也是只读
浅只读:嵌套对象的属性不是只读

十二、toRaw 与 markRaw

toRaw:将响应式数据对象转化为普通对象
markRaw:转化的对象不能成为响应式数据

十三、toRef

toRef(响应式数据对象,'属性'):返回值是一个响应式数据,与响应式数据对象的属性双向绑定

十四、customRef

<template>
  <div>
    {{personRef}}<br/>
    <button @click="update">按钮</button>
  </div>
</template>

<script lang="ts">
  import {defineComponent, customRef} from 'vue';

  export default defineComponent({
    name: 'App',
    setup() {
      let person = {
        name: '',
        age: 0
      }

      // 自定义响应式数据
      const personRef = customRef(((track, trigger) => {
        return {
          get() {
            // 开启数据追踪
            track()
            return person
          },
          set(val: any) {
            person = val
            // 更新视图
            trigger()
          }
        }
      }))


      return {
        personRef,
        update() {
          personRef.value = {
            name: 'linlong',
            age: 28
          }
        }
      }
    }
  });
</script>

<style>
</style>

十五、provide 与 inject

provide('名称',响应式数据):祖组件提供数据
inject('名称'):孙组件使用数据

十六、响应式数据的判断

isRef: 检查一个值是否为一个 ref 对象
isReactive: 检查一个对象是否是由 reactive 创建的响应式代理
isReadonly: 检查一个对象是否是由 readonly 创建的只读代理
isProxy: 检查一个对象是否是由 reactive 或者 readonly 方法创建的代理

十七、手写API

1、shallowReactive 与 reactive
const reactiveHandler = {
  get (target, key) {

    if (key==='_is_reactive') return true

    return Reflect.get(target, key)
  },

  set (target, key, value) {
    const result = Reflect.set(target, key, value)
    console.log('数据已更新, 去更新界面')
    return result
  },

  deleteProperty (target, key) {
    const result = Reflect.deleteProperty(target, key)
    console.log('数据已删除, 去更新界面')
    return result
  },
}

/* 
自定义shallowReactive
*/
function shallowReactive(obj) {
  return new Proxy(obj, reactiveHandler)
}

/* 
自定义reactive
*/
function reactive (target) {
  if (target && typeof target==='object') {
    if (target instanceof Array) { // 数组
      target.forEach((item, index) => {
        target[index] = reactive(item)
      })
    } else { // 对象
      Object.keys(target).forEach(key => {
        target[key] = reactive(target[key])
      })
    }

    const proxy = new Proxy(target, reactiveHandler)
    return proxy
  }

  return target
}


/* 测试自定义shallowReactive */
const proxy = shallowReactive({
  a: {
    b: 3
  }
})

proxy.a = {b: 4} // 劫持到了
proxy.a.b = 5 // 没有劫持到


/* 测试自定义reactive */
const obj = {
  a: 'abc',
  b: [{x: 1}],
  c: {x: [11]},
}

const proxy = reactive(obj)
console.log(proxy)
proxy.b[0].x += 1
proxy.c.x[0] += 1
2、shallowRef 与 ref
/*
自定义shallowRef
*/
function shallowRef(target) {
  const result = {
    _value: target, // 用来保存数据的内部属性
    _is_ref: true, // 用来标识是ref对象
    get value () {
      return this._value
    },
    set value (val) {
      this._value = val
      console.log('set value 数据已更新, 去更新界面')
    }
  }

  return result
}

/* 
自定义ref
*/
function ref(target) {
  if (target && typeof target==='object') {
    target = reactive(target)
  }

  const result = {
    _value: target, // 用来保存数据的内部属性
    _is_ref: true, // 用来标识是ref对象
    get value () {
      return this._value
    },
    set value (val) {
      this._value = val
      console.log('set value 数据已更新, 去更新界面')
    }
  }

  return result
}

/* 测试自定义shallowRef */
const ref3 = shallowRef({
  a: 'abc',
})
ref3.value = 'xxx'
ref3.value.a = 'yyy'


/* 测试自定义ref */
const ref1 = ref(0)
const ref2 = ref({
  a: 'abc',
  b: [{x: 1}],
  c: {x: [11]},
})
ref1.value++
ref2.value.b[0].x++
console.log(ref1, ref2)
3、shallowReadonly 与 readonly
const readonlyHandler = {
  get (target, key) {
    if (key==='_is_readonly') return true

    return Reflect.get(target, key)
  },

  set () {
    console.warn('只读的, 不能修改')
    return true
  },

  deleteProperty () {
    console.warn('只读的, 不能删除')
    return true
  },
}

/* 
自定义shallowReadonly
*/
function shallowReadonly(obj) {
  return new Proxy(obj, readonlyHandler)
}

/* 
自定义readonly
*/
function readonly(target) {
  if (target && typeof target==='object') {
    if (target instanceof Array) { // 数组
      target.forEach((item, index) => {
        target[index] = readonly(item)
      })
    } else { // 对象
      Object.keys(target).forEach(key => {
        target[key] = readonly(target[key])
      })
    }
    const proxy = new Proxy(target, readonlyHandler)

    return proxy 
  }

  return target
}

/* 测试自定义readonly */
/* 测试自定义shallowReadonly */
const objReadOnly = readonly({
  a: {
    b: 1
  }
})
const objReadOnly2 = shallowReadonly({
  a: {
    b: 1
  }
})

objReadOnly.a = 1
objReadOnly.a.b = 2
objReadOnly2.a = 1
objReadOnly2.a.b = 2
4、isRef, isReactive 与 isReadonly
/* 
判断是否是ref对象
*/
function isRef(obj) {
  return obj && obj._is_ref
}

/* 
判断是否是reactive对象
*/
function isReactive(obj) {
  return obj && obj._is_reactive
}

/* 
判断是否是readonly对象
*/
function isReadonly(obj) {
  return obj && obj._is_readonly
}

/* 
是否是reactive或readonly产生的代理对象
*/
function isProxy (obj) {
  return isReactive(obj) || isReadonly(obj)
}


/* 测试判断函数 */
console.log(isReactive(reactive({})))
console.log(isRef(ref({})))
console.log(isReadonly(readonly({})))
console.log(isProxy(reactive({})))
console.log(isProxy(readonly({})))

十八、Teleport

<teleport to="body">
  <div>该标签会添加到body标签下</div>
</teleport>

十九、Suspense

<template>
  <Suspense>
    <template v-slot:default>
      <llComponent></llComponent>
    </template>
    <template v-slot:fallback>
      <div>加载中...</div>
    </template>
  </Suspense>
</template>

<script lang="ts">
  import {defineComponent, defineAsyncComponent} from 'vue';

  export default defineComponent({
    name: 'App',
    components: {
      // 加载异步组件
      llComponent: defineAsyncComponent(() => import('./views/llComponent.vue'))
    },
    setup() {
      return {}
    }
  });
</script>

<style scoped>

</style>
<template>
  <div>{{msg}}</div>
</template>

<script lang="ts">
  export default {
    name: 'llComponent',
    setup() {
      return new Promise((resolve, reject) => {
        setTimeout(() => {
          resolve({
            msg: '你好,世界!'
          })
        }, 3000)
      })
    }
  }
</script>

<style scoped>

</style>

发表回复

您的电子邮箱地址不会被公开。 必填项已用*标注