vue3最新文档学习

  • Post author:
  • Post category:vue



@update 2022/4/22



@author helson



生命周期



vue2.0和vue3.0之间生命周期对比

vue2 vue3
beforeCreate setup
created setup
mounted onMounted
beforeUpdate onBeforeUpdate
updated onUpdated
beforeUnmount beforeUnmount
unmounted onUnmounted
errorCaptured onErrorCaptured
renderTracked onRenderTracked
renderTriggered onRenderTriggered



新特性



支持多根节点

子组件

// Card.vue
<template>
  <div class="header">{{header}}</div>
  <div class="footer">{{footer}}</div>
</template>
<script setup lang="ts">
  const props = defineProps({
    header: {
      type: String,
      required: true,
    },
    footer: {
       type: String,
       required: true,
    },
  })
</script>

父组件

<template>
    <div class="blink">
      <Card :header="header" :footer="footer" />
    </div>
</template>
<script setup lang="ts">
import {ref} from 'vue'
const header = ref('headerInfo');
const footer = ref('footerinfo');
</script>

支持多根节点可以让我们写一些组件的时候,不用在最外层多包括一层

div

,Dom结构更加直观。




setUp

函数

在创建组件实例时,在初始 props 解析之后立即调用

setup

。在生命周期方面,它是在


beforeCreate


钩子之前调用的。


setup

函数接受两个参数,第一个参数

props

第二个参数

context



props


props

是响应式的,在传入新的 props 时会对其进行更新,并且可以通过使用

watchEffect



watch

进行观测和响应,不要对

props

进行解构赋值,解构之后将不具有响应性。

export default {
  props: {
    name: String
  },
  setup(props) {
    watchEffect(() => {
      console.log(`name is: ` + props.name)
    })
  }
}



context:执行上下文

常用的

emit


slots


attrs

在context可以获取到。

export default defineComponent({
  name: "CardList",
  components: { Blank },
  props: {
    list: {
      type: Array,
      default: () => {
        return [];
      },
    },
  },
  setup(props, context) {
    // 实例化 router
    const router = useRouter();
    const toView = (item) => {
      router.push({ name: "Article", query: { id: item.id } });
    };
    const like = (index) => {
      context.emit("like", index);
    };
    return {
      toView,
      like,
    };
  },
});




setup

语法糖(



<script>

内添加

setup

之后,可以不需要再写复杂的结构了,精简代码。



组件

组件的使用不需要引入组件之后,再在

components

内注册。直接引入就可以使用。



变量

变量和函数的声明之后可以直接使用,不需要再进行

return

<script setup lang="ts">
import {ref} from 'vue';
// 导入之后,就可以直接使用
import Header from '@/components/Header.vue';
</script> 



Props和Emit


props



emit

需要使用

defineProps defineEmit

来定义

<script setup>
  import { defineProps, defineEmit, useContext } from 'vue'
 
  const props = defineProps({
    foo: String,
  })
  const emit = defineEmit(['change', 'delete'])
</script>

props增强类型可以使用ts来定义或者直接采用2.0一样方法使用对象方式

2.0方式:

<script setup>
  import { defineProps} from 'vue'
  const props = defineProps({
    foo: {
      type: String,
      require: true,
    },
    name: {
      type: String,
    }
  })
</script>

ts方式:

<script setup>
  import { defineProps} from 'vue'
  interface Props {
    foo: string;
    name?: string;
  }
  const props = defineProps<Props>();
</script>

以上两种方式是等价的.

如果想要设置默认参数值使用

withDefaults

import {withDefaults} from "vue"
withDefaults(defineProps<Props>(), {
  foo: "demo",
  name: "helson"
});



Ref获取组件实例

使用setup语法糖,默认是不会保留属性和参数的。想要暴露组件的一些属性的方法时,需要使用

defineExpose

来定义暴露参数

TS中想要指定

ref

参数的类型可以使用泛型exp:

const myDom = ref<HTMLElement>(null)

// 父组件
<template>
  <div>
  	<header ref="headerRef"></header>
    <div class="myDom"></div>
  </div>
</template>
<script setup>
  import {ref} from 'vue'
  const headerRef= ref(null);
  // headerRef.value.clear();![请添加图片描述](https://img-blog.csdnimg.cn/ac68c468f5174a3487f21a84bb01795f.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBASGVsc29uQA==,size_20,color_FFFFFF,t_70,g_se,x_16)

  // const headerRef= ref<HTMLElement>(null)
</script>

// 子组件
<script setup lang="ts">
const clear = () => {};
const name = ref("demo")
defineExpose({name,clear})
</script>



await语法支持



script

可以直接

await

语法

<script setup>
  const post = await fetch('https://api.fastmock.site/dhskajs12/card/list').then((r) => r.json())
</script>



对象监听


watch

:

watch是惰性执行,也就是只有监听的值发生变化的时候才会执行

watch只能监听响应式数据:ref定义的属性和reactive定义的对象,如果直接监听reactive定义对象中的属性是不允许的,除非使用函数转换一下

在这里插入图片描述


watchEffect

:监听

副作用

(当某个属性值改变时做一些操作)

对监听函数内的响应式属性都会追踪依赖,当响应式的属性发生改变时,会重新执行(拿到的值是更新后的值)。默认会执行一次。

如果监听reactive定义的对象是不起作用的,只能监听对象中的属性

场景案例:网站主题色

themeVal

有多个参数值,当主题色

themeVal

修改时,需要修改主题色。

const themeVal = ref("light");

watchEffect(() => {
  theme.setTheme(themeVal.value);
  document.body.setAttribute(
    "arco-theme",
    themeVal.value === "dark" ? "dark" : ""
  );
});



计算属性

计算属性具有缓存机制,所以在对数据量较大的属性进行处理时具有优势,相对于method可以节省开销。方法在重渲染时会重新计算。

注意:

1.计算数据不应有副作用,在计算属性内,不建议去对Dom进行操作;

2.避免直接修改计算属性的值:从计算属性返回的值是派生状态。可以把它看作是一个“临时快照”,每当源状态发生变化时,就会创建一个新的快照。因此更改快照是没有意义的,因此**,**

计算属性的返回值应该被视为只读的

,并且永远不会发生突变。应该更新它所依赖的源状态,以触发新一次计算。



跨组件传值provide和inject


provide

绑定的值如果是响应式的数据,

inject

注入的数据也是具有响应式的。


inject

的第二个参数为默认值参数,如果

provide

没有注入对应的参数值,则采用默认值。

在一些场景中,默认值可能需要通过调用一个函数或初始化一个类来取得。为了避免在不使用可选值的情况下进行不必要的计算或产生副作用,我们可以使用工厂函数来创建默认值:


const value = inject('key', () => new ExpensiveClass())

// 根组件
<script setup lang="ts">
import { provide, ref } from "vue";
import Header from "@/components/Header.vue";
const message = ref("message");
setTimeout(() => {
  message.value += "1";
}, 1000);
provide("message", message);
</script>
<script setup lang="ts">
// 子组件:message 为响应式的数据
const message = inject("message", "默认值");
</script>

可以观察到上面案例内,子组件内的

message

的值在1秒之后改变为了

message1




异步组件 |

defineAsyncComponent

import { defineAsyncComponent } from 'vue'

const AsyncComp = defineAsyncComponent(() =>
  import('./components/MyComponent.vue')
)

关于组件加载和错误状态

const AsyncComp = defineAsyncComponent({
  // 加载函数
  loader: () => import('./Foo.vue'),

  // 加载异步组件时使用的组件
  loadingComponent: LoadingComponent,
  // 展示加载完成的组件前的延迟,默认为 200ms
  delay: 200,

  // 加载失败后展示的组件
  errorComponent: ErrorComponent,
  // 如果提供了一个 timeout 时间限制,并超过了该时间
  // 也会显示报错组件
  timeout: 3000
})



自定义指令

在setup内以v开头的驼峰命名的自定义指令函数,会自动注入。

const vFocus = {
  mounted: (el) => el.focus(),
};



$attr: 支持class 和style继承

vue3自动会继承父级的class 和 style

多个根节点时需要指定接受的节点

<template>
  <div v-bind="$attrs"></div>
  <div></div>
</template>



extends弃用,使用createApp创建组件


createApp

接受两个参数:第一个参数

vue

组件,第二个参数

props

export function decoratorsRender(
  id: string,
  name: string,
  option: { [key: string]: string }
) {
  //异步加载组件
  const componentWillMount = defineAsyncComponent(
    () => import(`./${name}.vue`)
  );
  const fvComponent = createApp(componentWillMount, option);
  const mountDom = document.getElementById(id);
  if (!mountDom) throw new Error("will mount id dom can't be find");
  fvComponent.mount(mountDom);
}



组件注册(批量)

vue3 使用

Vue.compoent(componentName, coponent)

注册组件

批量注册组件(组件库)


/components/index.ts

import { App } from "vue";
// eslint-disable-next-line @typescript-eslint/no-var-requires
const path = require("path");
export default {
  install(Vue: App<Element>) {
    const files = require.context("./", true, /.vue$/);
    files.keys().forEach(async (key: string) => {
      const name = path.dirname(key).slice(2); //返回文件名 不含后缀名
      const componet = files(key).default;
      Vue.component(name, componet);
    });
  },
};



main.ts

内注册使用

import FvDecorator from "@/components/index";
const app = createApp(App);
app.use(Antd).use(FvDecorator).use(Vue3ColorPicker).use(router).use(store);



Hooks


hooks

提高了代码的复用性,代码逻辑清晰。


hooks

可以在组件外部调用Vue的所有能力,包括onMounted,ref, reactive等等。


useMousePosition.ts

// hooks
import {onBeforeUnmount, onMounted, ref} from 'vue
export default function () {
  const x = ref(-1) ; // x 绑定为响应式数据
  const y = ref(-1);
  const clickHandler=(event:MouseEvent)=>{
    x.value = event.pageX
    y.value = event.pageY
  } 
  onMounted(()=>{
    window.addEventListener('click', clickHandlker)
  })
  onBeforeUnmount(()=>{
    window.removeEventListner('click', clickHandler)
  })
  return {
    x,
    y
  }
}
<template>
<div>
  <h2>x: {{x}}, y: {{y}}</h2>
</div>
</template>

<script>

import {
  ref
} from "vue"
/* 
在组件中引入并使用自定义hook
自定义hook的作用类似于vue2中的mixin技术
自定义Hook的优势: 很清楚复用功能代码的来源, 更清楚易懂
*/
import useMousePosition from './hooks/useMousePosition'

export default {
  setup() {

    const {x, y} = useMousePosition() // 这里就用了 hooks 函数, 从而提高了复用性

    return {
      x,
      y,
    }
  }
}
</script>



Teleport组件


teleport



vue3

新增的组件。他可以让我们将组件插入到指定的

Dom

结构下。

使用案例:

在项目中,有一个场景:在页面滚动区域过长的情况下,需要能够一键回到顶部的这样一样操作按钮。

在Vue3中,就可以利用

Teleport

来实现这个功能。只需要操作按钮组件编写完成,然后将

Teleport



to

属性绑定到置顶的Dom上即可。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Zer2b1M3-1650615049721)(https://pic.kblue.site/20210625134437.png)]


toUper.vue

<template>
  <!-- #app 代表这个 Dom将要挂载到id为App的dom下方 -->
  <teleport to="#app">
      <div class="toUpper animated flash" v-if="show">
        <div class="ic" @click="toHome()">
          <i class="iconfont icon-shouye1"></i>
        </div>
        <div class="ic" @click="toUp()">
          <i class="iconfont icon-up"></i>
        </div>
      </div>
  </teleport>
</template>
<script>
import { defineComponent } from "vue";
import { useRouter } from 'vue-router';
import useScrollToTop from "@/hooks/useScrollToTop";
export default defineComponent({
  name: "ToUpper",
  props: {
    show: {
      type: Boolean,
      default: false,
    },
  },
  setup() {
      const router = useRouter();
    const toUp = () => {
      useScrollToTop();
    };
    const toHome = () => {
        router.push({path: '/'});
    }
    return {
      toUp,
      toHome,
    };
  },
});
</script>

<style lang="scss" scoped>
.toUpper {
  position: sticky;
  top: 90%;
  left: 90%;
}
.ic {
  float: right;
  display: block;
  margin-right: 40px;
  width: 40px;
  height: 40px;
  border-radius: 50%;
  background: #528bff;
  display: flex;
  align-items: center;
  justify-content: center;
  &:hover {
      cursor: pointer;
  }
  .iconfont {
    color: #fff;
  }
}
</style>


Home.vue

<template>
  <div class="home" if="home">
    <Header @changStatus="changStatus" :status="status" />
    <div class="main">
      <router-view></router-view>
      <div id="upper"></div>
    </div>
    <!-- 当页面滚动区域操作20之后才显示  -->
    <to-upper :show="scrollTop > 20" />
  </div>**
**</template>



状态管理



利用响应式API来实现状态管理

如果你有一部分状态需要在多个组件实例间共享,你可以使用


reactive()


来创建一个响应式对象,并在不同组件中导入它:

// store.js
import { reactive } from 'vue'

export const store = reactive({
  count: 0
})
<!-- ComponentA.vue -->
<script setup>
import { store } from './store.js'
</script>

<template>来自 A{{ store.count }}</template>
<!-- ComponentB.vue -->
<script setup>
import { store } from './store.js'
</script>

<template>来自 B{{ store.count }}</template>

现在每当

store

对象被更改时,

<ComponentA>



<ComponentB>

都会自动更新它们的视图。现在我们有了单一的数据源。

然而,这也意味着任意一个导入了

store

的组件都可以随意修改它的状态。



版权声明:本文为qq_37651169原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。