一、创建Vue3.0工程


1. 使用 vue-cli 创建
// 查看 @vue/cli 版本,确保在 4.5.0 以上
vue -V
// 创建
vue create vue3_test
2. 使用 vite 创建

官方文档

vite官网

npm init vite-app <project-name>

二、分析工程结构


1. main.js

2. App.vue
  • vue3 组件中的模板结构可以没有根标签

三、常用的 Composition Api


1. 拉开序幕的 setup
  1. 理解:Vue3.0 中的一个新的配置项,值为一个函数
  2. setup是所有 Composition Api(组合Api) "表演的舞台"
  3. 组件中用到的:数据、方法等,都要配置在 setup
  4. setup函数的两种返回值:

  5. 若返回一个对象,则对象中的属性、方法,在模板中均可直接使用!!!

  • 若返回一个渲染函数:则可以自定义渲染内容(了解)

  1. 注意点:

    1. 尽量不要与 vue2 配置混用

      • vue2 配置(data、methosd、computed...)中 可以访问到setup中的属性、方法

      • 但是在 setup中不能访问到vue2 配置(data、methos、computed...)

      • 如果有重名,setup优先

​ 2. setup不能是一个 async 函数,因为返回值不在是 return 的对象,而是 promise,模板看不到 return 对象中的属性

2. ref函数
  • 作用:定义一个响应式的数据

  • 语法:const xxx ref(initValue)

    1. 创建一个包含响应式数据的引用对象(reference对象)
    2. JS 中操作数据:xxx.value
    3. 模板中读取数据:不需要.value,直接: <div></div>
  • 备注:

    1. 接收的数据可以是:基本类型、对象类型
    2. 基本类型的数据:响应式依然是靠Object.defineProperty()getset完成的
    3. 对象类型的数据:内部“求助”了 Vue3 中的一个新函数——reactive函数

3. reactive 函数
  • 作用:定义一个对象类型的响应式数据(基本类型不要用,使用 ref 函数)
  • 语法:const 代理对象 = reactive(源对象)接收一个对象或数组,返回一个代理对象(proxy的实例对象,简称proxy对象)

  • reactive 定义的响应式数据是深层次的
let job = reactive({
    type: '前端',
    a: {
        b: {
            c: {
                d: '111'
            }
        }
    }
})
function changeInfo () {
    job.a.b.c.d = '222'
}
// job.a.b.c.d 222
  • 内部是基于 ES6 的 Proxy 实现,通过代理对象操作源对象内部数据进行操作
4.Vue3.0 中的响应式原理
vue2的响应式
  • 实现原理:

    1. 对象类型:通过Object.defineProperty()对属性的读取、修改进行拦截(数据劫持)
    2. 数组类型:通过重写更新数组的一系列方法来实现拦截。(对数组的变更方法进行了包裹)
    Object.defineProperty(data, 'count', {
        get(){},
        set(){}
    })
    
  • 存在问题:

    1. 新增属性、删除属性,界面不会更新
    2. 直接通过下标修改数组,界面不会自动更新
    this.$set(this.p, 'name', '123')
    this.$delete(this.p, 'name')
    
vue3的响应式
  • 实现原理:

    1. 通过Proxy(代理):拦截对象中任意属性的变化,包括:属性值的读写、属性的添加、属性的删除等。
    2. 通过Reflect(反射):对被代理对象的属性进行操作
    3. MDN文档中描述的 Proxy 与 Reflect:

      new Proxy(data, {
          // 拦截读取属性值
          get(target, propName) {
              return Reflect.get(target, propName)
          },
          // 拦截设置属性值或添加新属性
          set(target, propName, value) {
              return Reflect.set(target, propName, value)
          },
          // 拦截删除属性
          deleteProperty(target, propName) {
              return Reflect.deleteProperty(target, propName)
          }
      })
      
5. reactive 对比 ref
  • 从定义数据角度对比:
    1. ref 用来定义:基本数据类型code>
    2. reactive用来定义:对象(或数组)类型数据
    3. 备注:ref 也可以用来定义对象(或数组)类型数据,它内部会自动通过 reactive 转为 代理对象
  • 从原理角度对比:
    1. ref 通过 Object.defineProperty()getset来实现响应式(数据劫持)。
    2. reactive 通过使用 Proxy来实现响应式(数据劫持),并通过 Reflect操作源对象内部的数据。
  • 从使用角度对比:
    1. ref 定义的数据:操作数据需要 .value,读取数据时,替中直接读取,不需要 .value
    2. reactive 定义的数据:操作数据与读取数据,均不需要 .value
6. setup 的两个注意点
  • setup执行的时机

    1. 在 beforeCreate 之前执行一次,this 是 undefined
  • setup的参数

    1. props:值为对象,包含:组件外部传递过来,且组件内部声明接收了的属性
    2. context:上下文对象
      1. attrs:值为对象,包含组件外部传递过来,但没有在 props 配置中声明的属性,相当于 this.$attrs
      2. slots:收到的插槽内容,相当于this.$slots
      3. emit:分发自定义事件的函数,相当于this.$emit
    // 子组件
    <template>
      <h1>个人信息</h1>
      <h3>姓名:</h3>
      <h3>年龄:</h3>
      <button @click="sayHello">说话</button>
      <slot name="hello"></slot>
    </template>
    
    <script>
    import { reactive } from 'vue';
    export default {
      name: 'Demo-app',
      props: {
        msg: {
          type: String,
          defaule: ''
        }
      },
      emits: ['hello'],
      setup (props, context) {
        console.log(props);
        console.log(context.slots);
        let person = reactive({
          name: '张三',
          age: 20
        })
    
        function sayHello () {
          context.emit('hello', `我叫${person.name},你好啊`)
        }
        return {
          person,
          sayHello
        }
      }
    }
    </script>
    
    // 父组件
    <template>
      <Demo msg="黑科技" @hello="test">
        <template v-slot:hello>
          <span>hello</span>
        </template>
      </Demo>
    </template>
    
    <script>
    import Demo from './components/Demo.vue'
    export default {
      name: 'App',
      components: { Demo },
      setup () {
        function test (val) {
          alert(val)
        }
    
        return {
          test
        }
      }
    }
    </script>
    
7. 计算属性与监视
  1. computed 函数
import { computed } from 'vue';
// 计算属性,没有考虑修改计算属性的值(简写)
    person.name = computed(() => {
      return `${person.firstName}-${person.lastName}`
    })
// 计算属性,完整写法
    person.name = computed({
      get () {
        return `${person.firstName}-${person.lastName}`
      },
      set (val) {
        const nameArr = val.split('-');
        person.firstName = nameArr[0];
        person.lastName = nameArr[1];
      }
    })
2. watch 函数
  • 与 vue2 中的配置功能一直
  • 两个小坑:
    1. 监视 reactive 定义的响应式数据时:oldValue 无法正确获取、强制开启了深度监视(deep配置无效)
    2. 监视 reactive 定义的响应式数据中某个属性时:deep配置有效
import { ref, watch } from 'vue';
export default {
  name: 'Demo1-app',
  setup () {
    let num = ref(0);

    // 情况一:监视 ref 所定义的一个响应式数据
    watch(num, (newVal, oldVal) => {
      console.log(`num的值改变了--${newVal}-${oldVal}`);
    })
    return {
      num
    }
  }
}

watch 接收三个参数,第三个为配置项 {immediate: true, deep: true}

let num = ref(0);
let msg = ref('你好');
// 情况二:监视 ref 所定义的多个响应式数据
watch([num, msg], (newVal, oldVal) => {
  console.log(newVal, oldVal);
}, { immediate: true })
// newVal [1, '你好'] oldVal [0, '你好']
// 情况三:监视 reactive 所定义的一个响应式数据,注意:此处无法正确的获取 oldVal
// 强制开启了深度监听(deep配置无效)
watch(person, (newVal, oldVal) => {
   console.log(newVal, oldVal);
}, {deep: false}) // 无效
// newVal: Proxy {name: '张三~~', age: 21} 
// oldVal: Proxy {name: '张三~~', age: 21}
// 此处 newVal 与 oldVal相同,如果实际情况需要使用 oldVal,则可以把 对象属性提出来用 ref 定义
// 情况四:监听 reactive 所定义的一个响应式数据中的某个属性
watch(() => person.age, (newVal, oldVal) => {
   console.log(newVal, oldVal);
})
// 此时 oldVal 是正确的
// 情况五: 监听 reactive 所定义的一个响应式数据中的某些属性
watch([() => person.name, () => person.age], (newVal,oldVal) => {
   console.log(newVal, oldVal);
})
// newVal ['张三~', 20]
// oldVal ['张三', 20]
// 特殊情况
// 此处由于是监视的 reactive所定义的数据中对象的某个属性, deep 生效
watch(() => person.job, (newVal, oldVal) => {
   console.log(newVal.j1.salary, oldVal.j1.salary);
}, {deep: true})
// newVal 22 oldVal 22

results matching ""

    No results matching ""