Skip to content
On this page

ref

定义基础数据类型,如 string, boolean, number

html
<template>
  <!-- 模板读取不需要.value -->
  <p>{{name}}</p>
  <el-button @click="chengeName">改变name</el-button>
</template>
<template>
  <!-- 模板读取不需要.value -->
  <p>{{name}}</p>
  <el-button @click="chengeName">改变name</el-button>
</template>
js
import { defineComponent, ref, Ref } from 'vue';

export default defineComponent({
  setup(){
    //定义 响应式变量
    let name = ref<string>('梁朝伟');

    //这种写法也可以  但是这种写法没有上面的简洁
    let name2:Ref<string> = ref('110');

    //改变 name 
    let chengeName = () =>{
      //修改 ref响应式数据 需要.value
      name.value = '120';
    }

    return {
      name,
      chengeName
    }
  }
})
import { defineComponent, ref, Ref } from 'vue';

export default defineComponent({
  setup(){
    //定义 响应式变量
    let name = ref<string>('梁朝伟');

    //这种写法也可以  但是这种写法没有上面的简洁
    let name2:Ref<string> = ref('110');

    //改变 name 
    let chengeName = () =>{
      //修改 ref响应式数据 需要.value
      name.value = '120';
    }

    return {
      name,
      chengeName
    }
  }
})

reactive

定义复杂引用类型数据,如 object, array

html
<template>
  <p>{{data.name}}</p>
  <el-button @click="chengeData">改变data</el-button>
</template>
<template>
  <p>{{data.name}}</p>
  <el-button @click="chengeData">改变data</el-button>
</template>
js
import { defineComponent, reactive, toRef, toRefs } from 'vue';
enum SEX{
  MAN = '',
  WOMAN = ''
}

interface IData {
  name: string,
  age: number,
  sex: SEX
}
export default defineComponent({
  setup(){
    //定义响应式对象数据, let data = reactive<IData> 也可以
    let data: IData = reactive({
      name: '梁朝伟',
      age: 20,
      sex: SEX.MAN
    });
    
    let chengeData = () =>{
      data = {
        name: '吴彦祖',
        age: 40,
        sex: SEX.MAN
      }
    }
    return {
      data,
      chengeData
    }
  }
})
import { defineComponent, reactive, toRef, toRefs } from 'vue';
enum SEX{
  MAN = '',
  WOMAN = ''
}

interface IData {
  name: string,
  age: number,
  sex: SEX
}
export default defineComponent({
  setup(){
    //定义响应式对象数据, let data = reactive<IData> 也可以
    let data: IData = reactive({
      name: '梁朝伟',
      age: 20,
      sex: SEX.MAN
    });
    
    let chengeData = () =>{
      data = {
        name: '吴彦祖',
        age: 40,
        sex: SEX.MAN
      }
    }
    return {
      data,
      chengeData
    }
  }
})

computed

js
import { defineComponent, computed, ref } from 'vue';

export default defineComponent({
    setup(){
      let count = ref<number>(0);

      let getCount = computed(() =>{
        return count.value * 10;
      });
    }
})
import { defineComponent, computed, ref } from 'vue';

export default defineComponent({
    setup(){
      let count = ref<number>(0);

      let getCount = computed(() =>{
        return count.value * 10;
      });
    }
})

watch

js
import { defineComponent, reactive, ref, watch, watchEffect } from 'vue';

export default defineComponent({
  name: 'watch',
  setup(){
    //监听普通类型
    let count = ref(1);
    const changeCount = () => {
      count.value += 1
    };
    watch(count, (newValue, oldValue) => { //直接监听
      console.log('count改变了:' + newValue + ',' + oldValue);
    });

    //监听响应式对象
    let book = reactive({
      name: 'js编程',
      price: 50,
      data: {
        d2: {
          age: 10
        }
      }
    });

    const changeBookName = () => {
      book.name = 'JAVA编程'
    };
    //指定监听 book.name 属性
    watch(()=> book.name,() =>{
      console.log('书名改变了')
    })

    const changeBookAge = () => {
      book.data.d2.age = 40 
    };
    //深度监听
    watch(()=> book,() =>{
      console.log('age改变了')
    },{
      deep: true,
    })

    //监听多个值
    watch([() => book.name, count], ([name, count], [preName, preCount]) => {
      console.log("count或book.name改变了");
    });

    //监听setup 里所有响应式对象
    //1.不需要手动传入依赖
    //2.每次初始化时会执行一次回调函数来自动获取依赖
    //3.无法获取到原值,只能得到变化后的值
    //4.watchEffect 会返回一个用于停止这个监听的函数 
    const stop = watchEffect(() => {
      
    })

    //执行stop 方法可停止 监听
    stop()
  }
})
import { defineComponent, reactive, ref, watch, watchEffect } from 'vue';

export default defineComponent({
  name: 'watch',
  setup(){
    //监听普通类型
    let count = ref(1);
    const changeCount = () => {
      count.value += 1
    };
    watch(count, (newValue, oldValue) => { //直接监听
      console.log('count改变了:' + newValue + ',' + oldValue);
    });

    //监听响应式对象
    let book = reactive({
      name: 'js编程',
      price: 50,
      data: {
        d2: {
          age: 10
        }
      }
    });

    const changeBookName = () => {
      book.name = 'JAVA编程'
    };
    //指定监听 book.name 属性
    watch(()=> book.name,() =>{
      console.log('书名改变了')
    })

    const changeBookAge = () => {
      book.data.d2.age = 40 
    };
    //深度监听
    watch(()=> book,() =>{
      console.log('age改变了')
    },{
      deep: true,
    })

    //监听多个值
    watch([() => book.name, count], ([name, count], [preName, preCount]) => {
      console.log("count或book.name改变了");
    });

    //监听setup 里所有响应式对象
    //1.不需要手动传入依赖
    //2.每次初始化时会执行一次回调函数来自动获取依赖
    //3.无法获取到原值,只能得到变化后的值
    //4.watchEffect 会返回一个用于停止这个监听的函数 
    const stop = watchEffect(() => {
      
    })

    //执行stop 方法可停止 监听
    stop()
  }
})

async setup

  • 子组件 children.vue
js
import { defineComponent, getCurrentInstance } from 'vue'

export default defineComponent({
  name: 'v-children',
  async setup() {
    //vue this
    const { proxy } : any = getCurrentInstance(); 

    //比如获取 某某列表
    const d = await proxy.API.xxxList();
    if(!d.code){
      //获取成功
    }
  },
})
import { defineComponent, getCurrentInstance } from 'vue'

export default defineComponent({
  name: 'v-children',
  async setup() {
    //vue this
    const { proxy } : any = getCurrentInstance(); 

    //比如获取 某某列表
    const d = await proxy.API.xxxList();
    if(!d.code){
      //获取成功
    }
  },
})
  • 父组件 parent.vue
html
<template>
  <div class="v-parent">
    <!-- 需要使用 suspense 组件包裹, suspense为vue3 内置组件, 不然vChildren组件会显示不出来 -->
    <suspense>
      <vChildren />
    </suspense>
  </div>
</template>
<template>
  <div class="v-parent">
    <!-- 需要使用 suspense 组件包裹, suspense为vue3 内置组件, 不然vChildren组件会显示不出来 -->
    <suspense>
      <vChildren />
    </suspense>
  </div>
</template>
js
import { defineComponent } from 'vue';

//引入子组件
import vChildren from './vChildren';

export default defineComponent({
  name: 'v-parent',
  components: {
    vComponent
  },
  setup() {}
})
import { defineComponent } from 'vue';

//引入子组件
import vChildren from './vChildren';

export default defineComponent({
  name: 'v-parent',
  components: {
    vComponent
  },
  setup() {}
})

父子组件通信

  • 父组件 parent.vue
html
<template>
  <div class="parent-children">
    <el-button size="medium" type="primary" @click="isShowChildren = true">显示子组件</el-button>
    <el-button size="medium" type="primary" @click="handleChildrenShow">调用子组件show方法</el-button>
    <vChildren ref="vChildren" v-model:value="isShow" @checkValue="handleCheckValue" />
  </div>
</template>
<template>
  <div class="parent-children">
    <el-button size="medium" type="primary" @click="isShowChildren = true">显示子组件</el-button>
    <el-button size="medium" type="primary" @click="handleChildrenShow">调用子组件show方法</el-button>
    <vChildren ref="vChildren" v-model:value="isShow" @checkValue="handleCheckValue" />
  </div>
</template>
js
import{ defineComponent, computed, reactive, ref } from 'vue';
import vChildren from './children.vue';

export default defineComponent({
  name: 'v-children',
  components: {
    vChildren
  },
  setup(){
    //是否显示子组件
    let isShowChildren = ref<boolean>(false);

    //子组件 $emit 回调方法
    let handleCheckValue = (value: boolean) =>{
      isShowChildren.value = value;
    }

    //vue3 获取dom 节点上的ref 只需定义一个与ref 名称相同的 响应式变量即可
    let vChildren = ref(null);
    let handleChildrenShow = () =>{
      vChildren.value.show()
    }

    return{
      isShowChildren,
      handleCheckValue,

      vChildren,
      show
    }
  }
})
import{ defineComponent, computed, reactive, ref } from 'vue';
import vChildren from './children.vue';

export default defineComponent({
  name: 'v-children',
  components: {
    vChildren
  },
  setup(){
    //是否显示子组件
    let isShowChildren = ref<boolean>(false);

    //子组件 $emit 回调方法
    let handleCheckValue = (value: boolean) =>{
      isShowChildren.value = value;
    }

    //vue3 获取dom 节点上的ref 只需定义一个与ref 名称相同的 响应式变量即可
    let vChildren = ref(null);
    let handleChildrenShow = () =>{
      vChildren.value.show()
    }

    return{
      isShowChildren,
      handleCheckValue,

      vChildren,
      show
    }
  }
})
  • 子组件 children.vue
html
<template>
  <div class="v-children" v-show="value">
    子组件
    <el-button size="medium" type="primary" @click="handleCheck">修改父组件值(emit)</el-button>
    <el-button size="medium" type="primary" @click="handleCheck2">修改父组件值(直接修改)</el-button>
  </div>
</template>
<template>
  <div class="v-children" v-show="value">
    子组件
    <el-button size="medium" type="primary" @click="handleCheck">修改父组件值(emit)</el-button>
    <el-button size="medium" type="primary" @click="handleCheck2">修改父组件值(直接修改)</el-button>
  </div>
</template>
js
import{ defineComponent } from 'vue';

export default defineComponent({
  name: 'v-children',
  props: {
    value: {
      type: Boolean,
      required: true
    }
  },
  setup(props, context){
    //通过emit回调
    let handleCheck = () =>{
      context.emit('checkValue', false);
    }
    //直接通过vue 提供的 update 特性修改
    let handleCheck2 = () =>{
      context.emit('update:value', false)
    }
    //子组件方法
    let show = () =>{
      console.log('子组件show方法');
    }
    return{
      show,
      handleCheck,
      handleCheck2,
    }
  }
})
import{ defineComponent } from 'vue';

export default defineComponent({
  name: 'v-children',
  props: {
    value: {
      type: Boolean,
      required: true
    }
  },
  setup(props, context){
    //通过emit回调
    let handleCheck = () =>{
      context.emit('checkValue', false);
    }
    //直接通过vue 提供的 update 特性修改
    let handleCheck2 = () =>{
      context.emit('update:value', false)
    }
    //子组件方法
    let show = () =>{
      console.log('子组件show方法');
    }
    return{
      show,
      handleCheck,
      handleCheck2,
    }
  }
})

slot

  • 子组件 children.vue
html
<template>
  <div class="v-children">
    <header>
      <slot name="header" />
    </header>
    <div class="v-body">
      <slot name="body">
        hello body
      </slot>
    </div>
    <footer>
      <slot name="footer" />
    </footer>
  </div>
</template>
<template>
  <div class="v-children">
    <header>
      <slot name="header" />
    </header>
    <div class="v-body">
      <slot name="body">
        hello body
      </slot>
    </div>
    <footer>
      <slot name="footer" />
    </footer>
  </div>
</template>
  • 父组件 parent.vue
html
<vChildren>
  <!-- v-solt:header 缩写为 #header -->
  <template #header>
    头部
  </template>  
  <template #footer>
    底部
  </template>  
</vChildren>
<vChildren>
  <!-- v-solt:header 缩写为 #header -->
  <template #header>
    头部
  </template>  
  <template #footer>
    底部
  </template>  
</vChildren>

vue hook

  • vue3 hook相当于vue2 的mixin, 不同于hook是函数
  • vue3 hook能让我提高代码复用性
  • 让函数拥有组件的特性

useMousePosition.ts

js
import { onMounted, onUnmounted, ref } from 'vue';

//点击窗口获取 鼠标 x y 坐标
function useMousePosition(){
  const x = ref(0);
  const y = ref(0);

  const clickHandler = (e: MouseEvent) =>{
    x.value = e.pageX;
    y.value = e.pageY;
  }

  onMounted(() =>{
    window.addEventListener('click', clickHandler);
  })

  onUnmounted(() =>{
    window.removeEventListener('click', clickHandler);
  })

  return{
    x,
    y
  }
}

export default useMousePosition;
import { onMounted, onUnmounted, ref } from 'vue';

//点击窗口获取 鼠标 x y 坐标
function useMousePosition(){
  const x = ref(0);
  const y = ref(0);

  const clickHandler = (e: MouseEvent) =>{
    x.value = e.pageX;
    y.value = e.pageY;
  }

  onMounted(() =>{
    window.addEventListener('click', clickHandler);
  })

  onUnmounted(() =>{
    window.removeEventListener('click', clickHandler);
  })

  return{
    x,
    y
  }
}

export default useMousePosition;

useWindowResize.ts

js
import { onMounted, onUnmounted, ref } from 'vue';

//窗口尺寸改变  实时获取窗口宽高
function useWindowResize(){
  const width = ref(0);
  const height = ref(0);

  const onResize = () =>{
    width.value = window.innerWidth;
    height.value = window.innerHeight;
  }

  onMounted(() =>{
    onResize();
    window.addEventListener('resize', onResize);
  })

  onUnmounted(() =>{
    window.removeEventListener('resize', onResize);
  })

  return{
    width,
    height
  }
}

export default useWindowResize;
import { onMounted, onUnmounted, ref } from 'vue';

//窗口尺寸改变  实时获取窗口宽高
function useWindowResize(){
  const width = ref(0);
  const height = ref(0);

  const onResize = () =>{
    width.value = window.innerWidth;
    height.value = window.innerHeight;
  }

  onMounted(() =>{
    onResize();
    window.addEventListener('resize', onResize);
  })

  onUnmounted(() =>{
    window.removeEventListener('resize', onResize);
  })

  return{
    width,
    height
  }
}

export default useWindowResize;

组件中使用

html
<template>
  <div class="hook">
    窗口尺寸
    <p>宽: {{width}}</p>
    <p>高: {{height}}</p>

    点击窗口获取 鼠标 x y 值
    <p>x: {{x}}</p>
    <p>y: {{y}}</p>
  </div>
</template>
<template>
  <div class="hook">
    窗口尺寸
    <p>宽: {{width}}</p>
    <p>高: {{height}}</p>

    点击窗口获取 鼠标 x y 值
    <p>x: {{x}}</p>
    <p>y: {{y}}</p>
  </div>
</template>
js
import { defineComponent } from 'vue';
import useWindowResize from '@/hook/useWindowResize';
import useMousePosition from '@/hook/useMousePosition';

export default defineComponent({
  name: 'hook',
  setup(){
    const { width, height }  = useWindowResize();
    const { x, y }  = useMousePosition();

    return{
      width, 
      height,
      x,
      y
    }
  }
})
import { defineComponent } from 'vue';
import useWindowResize from '@/hook/useWindowResize';
import useMousePosition from '@/hook/useMousePosition';

export default defineComponent({
  name: 'hook',
  setup(){
    const { width, height }  = useWindowResize();
    const { x, y }  = useMousePosition();

    return{
      width, 
      height,
      x,
      y
    }
  }
})

provide/inject 依赖注入

当一个负责页面业务逻辑负责, 需要根据业务拆分成很多子组件编写, 当顶层父组件有个数据需要逐层传递到 子组件,孙组件, ... 的时候. 当然使用 prop 也是可以的, 但是需要在 子组件,孙组件, ... 都要定义prop 缺点: 会存在命名冲突问题, 命名上谨慎处理好

父组件

html
<template>
  <div class="provide-inject">
    provide-inject

    <vChildren />
  </div>
</template>
<template>
  <div class="provide-inject">
    provide-inject

    <vChildren />
  </div>
</template>
js
import { defineComponent, provide } from 'vue';
import vChildren from './children.vue'; 

export default defineComponent({
  name: 'provide-inject',
  components: {
    vChildren
  },
  setup(){
    //注入 一个字符串
    provide('role', '超级管理员');
    //注入 一个对象
    provide('userInfo', {
      name: '梁朝伟',
      age: 20,
      sex: ''
    });

    return{
    }
  }

})
import { defineComponent, provide } from 'vue';
import vChildren from './children.vue'; 

export default defineComponent({
  name: 'provide-inject',
  components: {
    vChildren
  },
  setup(){
    //注入 一个字符串
    provide('role', '超级管理员');
    //注入 一个对象
    provide('userInfo', {
      name: '梁朝伟',
      age: 20,
      sex: ''
    });

    return{
    }
  }

})

子组件

html
<template>
  <div class="v-children">
    子组件
    <p>角色: {{role}}</p> 
    <p>姓名: {{userInfo.name}}</p>
    <p>年龄: {{userInfo.age}}</p>
    <p>性别: {{userInfo.sex}}</p>
  </div>
</template>
<template>
  <div class="v-children">
    子组件
    <p>角色: {{role}}</p> 
    <p>姓名: {{userInfo.name}}</p>
    <p>年龄: {{userInfo.age}}</p>
    <p>性别: {{userInfo.sex}}</p>
  </div>
</template>
js
import{ defineComponent, inject } from 'vue';

export default defineComponent({
  name: 'v-children',
  setup(){
    //获取顶层组件注入的 role
    const role = inject('role');
    //获取顶层组件注入的 role
    const userInfo = inject('userInfo');

    return{
      role,
      userInfo
    }
  }
})
import{ defineComponent, inject } from 'vue';

export default defineComponent({
  name: 'v-children',
  setup(){
    //获取顶层组件注入的 role
    const role = inject('role');
    //获取顶层组件注入的 role
    const userInfo = inject('userInfo');

    return{
      role,
      userInfo
    }
  }
})

Teleport

html
//teleport 组件包裹 可以通过to参数设置 teleport内的内容渲染到指定位置
//body 只指渲染到 body 节点下, 参数值还可以是 id 和 class
//disabled 参数可以设置 是否开启指定渲染操作 值为 true false
<teleport to="body" :disabled="displayVideoInline">
  <div>
    内容
  </div> 
</teleport>
//teleport 组件包裹 可以通过to参数设置 teleport内的内容渲染到指定位置
//body 只指渲染到 body 节点下, 参数值还可以是 id 和 class
//disabled 参数可以设置 是否开启指定渲染操作 值为 true false
<teleport to="body" :disabled="displayVideoInline">
  <div>
    内容
  </div> 
</teleport>

vue2到vue3迁移

迁移文档