尼采般地抒情

尼采般地抒情

尼采般地抒情

音乐盒

站点信息

文章总数目: 311
已运行时间: 1518

前言:对Vue3的初次接触是使用Vue3(Vue.js - 渐进式 JavaScript 框架 | Vue.js)的 组合式API 编程风格来进行Vue2旧项目的重构。


基础变动:

  • setup:Vue3.1版本是作为选项式的方法,Vue3.2之后才是作为属性放到script上的
  • data methods 都不再需要,也不再需要return出变量或是函数
  • 路由相关知识,只注意useRoute() useRouter()其余的,和之前自己写的lyrics前端路由就够用了
  • Vue3 异步编程
  • provide 和 inject
  • ······

响应式数据

ref

<template>
  <a-modal
    v-model:visible="visible"
    />
</template>
<script setup lang="ts">
  import {ref} from 'vue'
  const visible = ref<boolean>(false)
</script>

reactive

  • reactive这个更多使用在对象类型的响应式定义数据
  • toRefstoRefreactive响应式转为ref响应式
<template>
  <div class="setup-component">
    <h3>setup component:</h3>
    <button @click="changeCount">change</button>
    <p>state.msg: {{ state.msg }}</p>
    <p>reactiveToRefsMsg: {{ reactiveToRefsMsg }}</p>
    <p>reactiveToRefMsg: {{ reactiveToRefMsg }}</p>
  </div>
</template>

<script setup>
import { reactive, toRefs, toRef } from "vue";

const state = reactive({ msg: "[reactive init value]" });

const { msg: reactiveToRefsMsg } = toRefs(state);
const reactiveToRefMsg = toRef(state, "msg");

const changeCount = () => {
state.msg = "[reactive change value]";
};
</script>

<style>
.setup-component {
border: 1px solid red;
}
</style>

计算属性和方法

<template>
  <div class="setup-component">
    <h3>setup component:</h3>
    <button @click="changeCount">change</button>
    <p>count: {{ count }}</p>
    <p>doubleCount: {{ doubleCount }}</p>
  </div>
</template>

<script setup>
import { ref, computed } from "vue";

const count = ref(0);
const doubleCount = computed(() => count.value * 2);
const changeCount = () => {
count.value++;
};
</script>

<style>
.setup-component {
border: 1px solid red;
}
</style>

生命周期

选项式API

初始化实例时触发

  1. beforeCreate:在实例初始化之后,数据观测和事件配置之前被调用
    1. 响应式数据没有,dom未渲染
  1. created:在实例创建完成后被立即调用
    1. 响应式数据有了,dom未渲染
    2. 一般在这里异步请求初始化数据
  1. beforeMount:在挂载开始之前被调用
    1. 响应式数据有了,dom未渲染
  1. mounted:el被新创建的vm.$el替换,并挂载到实例上去之后调用该钩子
    1. 响应式数据有了,dom已渲染
  1. activated
  2. deactivated

更新数据时触发

  1. beforeUpdate:数据更新时调用,发生在虚拟DOM打补丁之前
    1. 响应式数据变了,但是dom还没有更新
  1. updated:由于数据更改导致的虚拟DOM重新渲染和打补丁,在这之后会调用该钩子
    1. 响应式数据变了,dom也更新了

卸载实例时触发

  1. beforeDestroy:实例销毁之前调用
    1. 响应式数据还有,dom也有
  1. destroyed:实例销毁后调用
    1. 响应式数据还有,dom没了
  1. errorCaptured
  2. renderTracked
  3. renderTriggered

组合式API

  1. api:选项式生命周期API前面加on(eg:onBeforeMount,onMounted···)
  2. 没有onBeforeCreate,onCreated
  3. 可以多次调用,不用将逻辑写在一个里面

在Vue3组合式API的生命周期里面,没有created阶段了,直接在setup里面了,也就是setup阶段是没有挂载真实DOM的,如果需要操作真实dom需要在onMounetd里面进行相应逻辑

onMounted(() => {
  if (document.getElementById('test')) {
    console.log('exist #test DOM...');
  }
});
<script>
  setup() {
    console.log('----setup第一个执行----')
    // vue3.x生命周期写在setup中
    onBeforeMount(() => {
      console.log('------vue3中onBeforeMount-----')
    })
    onMounted(() => {
      console.log('------vue3中onMounted-----')
    })
    onRenderTriggered((event) => {
      console.log('------vue3中onRenderTriggered-----', event)
    })
  },
  // vue2当中的
  beforeCreate() {
    console.log('----vue2中beforeCreate第二个执行----')
  },
  // vue2当中的
  created() {
    console.log('----vue2中created第三个执行----')
  },
</script>

执行结果如下:

watch

watch(count, (newVal, oldVal) => {
  console.log("watch count: ", newVal, oldVal);
});

watchEffect

  1. 所依赖的变量发生改变,就会执行,再次写相关逻辑代码
  2. 执行时机:数据更新后,DOM更新前;通过第二个参数flush: post可以改变至数据更新后,DOM更新后触发
  3. 返回的是一个函数,执行该函数,可以停止watchEffect监听
  4. 传入一个形参,该形参函数所执行时机为更新前和组件卸载前,在此可以写一些清除逻辑

watch和watchEffect

  • warchEffect初始化会执行一遍
  • 想要明确使用变化前后值


hook

  • use函数(通常以use开头命名)封装优于mixins,可以自定义传参等
import { ref, computed } from "vue";
const useXY = () => {
  const x = ref(12);
  const y = computed(() => x.value * 2);
  return { x, y };
};
export { useXY };

<template>
  <p>useXY x: {{ x }}; y: {{ y }}</p>
</template>
<script setup>
  import { useXY } from "./usseXY";
  const { x, y } = useXY();
</script>


生态链

Router

  1. SPA单页面应用,以及路径模式和哈希模式下的路由
    1. hash模式
    2. history模式,和前端较为古老的ajax局部刷新页面类似,都是利用pushState相关技术
<!-- hash模式 -->
<body>
  <ul>
    <!-- 定义路由 -->
    <li><a href="#/home">home</a></li>
    <li><a href="#/about">about</a></li>
    <!-- 渲染路由对应的 UI -->
    <div id="routerView"></div>
  </ul>
</body>
<script>
  window.addEventListener('hashchange', onHashChange)
  onHashChange()
  function onHashChange () {
    switch (location.hash) {
      case '#/home':
        routerView.innerHTML = 'Home'
        break;
      case '#/about':
        routerView.innerHTML = 'About'
        break;
    }
  }
</script>
<!-- history模式 -->
<body>
  <ul>
    <!-- 定义路由 -->
    <li><a href="/home">home</a></li>
    <li><a href="/about">about</a></li>
    <!-- 渲染路由对应的 UI -->
    <div id="routerView"></div>
  </ul>
</body>
<script>
  let linkList = document.querySelectorAll('a[href]')
  for(let i=0;i<linkList.length;i++){
    linkList[i].addEventListener('click', function(e){
      e.preventDefault()
      history.pushState(null, '', this.getAttribute('href'))
      onPopState()
    })
  }
  window.addEventListener('popstate', onPopState)
  onPopState()
  function onPopState () {
    switch (location.pathname) {
      case '/home':
        routerView.innerHTML = 'Home'
        break;
      case '/about':
        routerView.innerHTML = 'About'
        break;
    }
  }
</script>
  1. 嵌套路由:路由表中嵌套写法
  2. 动态路由:$route.params.xxx
  3. 声明式路由:<RouterLink />
  4. 编程式路由:this.$Router.push('/xxx)
  5. 路由元信息:meta
  6. 路由传递参数
    1. query方式:相当于路径参数$route.query
    2. params方式(显式):$route.params
    3. params方式(隐式):$route.params
  1. $route$router
    1. 前者获取当前路由信息
    2. 后者调用路由相关方法
import { createRouter, createWebHistory } from 'vue-router'
import HomeView from '../views/HomeView.vue'

const router = createRouter({
history: createWebHistory(import.meta.env.BASE_URL),
routes: [
{
path: ‘/‘,
name: ‘home’,
component: HomeView
},
{
path: ‘/about’,
name: ‘about’,
// route level code-splitting
// this generates a separate chunk (About.[hash].js) for this route
// which is lazy-loaded when the route is visited.
component: () => import(‘../views/AboutView.vue’)
}
]
})

export default router

Pinia

Pinia其实就是Vuex 5。与 Vuex相比,Pinia 提供了一个更简单的 API,具有更少的规范,提供了 Composition-API 风格的 API,最重要的是,在与TypeScript 一起使用时具有可靠的类型推断支持。

Pinia API 与 Vuex4有很大不同,即:

  • mutations 不再存在。他们经常被认为是 非常冗长。他们最初带来了 devtools 集成,但这不再是问题。
  • 无需创建自定义复杂包装器来支持 TypeScript,所有内容都是类型化的,并且 API 的设计方式尽可能利用 TS 类型推断。不再需要注入、导入函数、调用函数、享受自动完成功能!
  • 无需动态添加 Store,默认情况下它们都是动态的,您甚至都不会注意到。请注意,您仍然可以随时手动使用 Store 进行注册,但因为它是自动的,您无需担心。
  • 不再有 modules 的嵌套结构。您仍然可以通过在另一个 Store 中导入和 使用 来隐式嵌套 Store,但 Pinia 通过设计提供平面结构,同时仍然支持 Store 之间的交叉组合方式。 您甚至可以拥有 Store 的循环依赖关系。
  • 没有 命名空间模块。鉴于 Store 的扁平架构,“命名空间” Store 是其定义方式所固有的,您可以说所有 Store 都是命名空间的。

总之Pinia简化了Vuex的操作,这也是未来Vuex5的趋势,下面就来尝试使用一下Pinia吧。

用你最喜欢的包管理器安装 pinia:

npm install pinia

vue引入pinia:

import { createPinia } from 'pinia'
const pinia = createPinia()
createApp(App).use(pinia).mount('#app')

组合式写法:

import { ref, computed } from 'vue'
import { defineStore } from 'pinia'

export const useCounterStore = defineStore(‘counter’, () => {
const count = ref(0)
const doubleCount = computed(() => count.value * 2)
function increment() {
count.value++
}

return { count, doubleCount, increment }
})

选项式写法:

import { defineStore } from 'pinia'

export const useCounterStore = defineStore(‘counterStore’, {
state: () => ({
counter: 0
}),
actions: {
add(){
this.counter++
}
}
})

以上两种风格的写法是等价的,接下来就是如何去调用这个模块了。在App.vue中引入counter.js并使用:

<template>
  <button @click="changePiniaValue">change pinia value</button>
  {{ count }}, {{ doubleCount }}
</template>
<script setup>
  import { storeToRefs } from 'pinia';
  import { useCounterStore } from './stores/counter';

const counterStore = useCounterStore()
// 下面操作可使共享状态具备响应式
const { counter, doubleCount } = storeToRefs(counterStore);
//下面四种操作行为均可修改counter值,并具备响应式变化
const changePiniaValue = () => {
counterStore.increment()
// counter.value++;
// counterStore.counter++;
// counterStore.$patch({
// counter: counter.value + 1
// })
}
</script>

increment()方法可以直接编写异步程序和传参处理:

actions: {
  increment(n){
    setTimeout(()=>{
      this.counter += n;
    }, 1000)
  }
}

Pinia去掉了繁琐的mutations,异步同步都采用actions来完成,并且简化了modules的使用方式等等。

其他

slot使用

vue2:

<template>
  <template slot="content">
    ···
  </template>
</template>

vue3:

<template>
  <slot name="content">
    ···
  </slot>
</template>

全局变量的使用

vue3注册全局变量失去响应性

  • 点击登录按钮,相应一个loading的状态,这个在之前项目中是利用全局注册的一个变量,vue3的全局注册变量方法
  • 接口 - loading 的全局变量迁移

不能使用v3全局变量api getCurrentInstance 不能响应式,同时打包情况下

直接导入ts文件,在ts文件中做变量导出,

具名插槽

  • vue3插槽使用方式和vue2不一样,不能使用template,而是slot标签,name属性
  • 具名插槽在vue2和vue3两者使用有差别

根据dom属性来动态改变css

当给dom添加自定义属性,vue2和vue3实际渲染出来的不一样(如果是标签自身属性,vue3和vue2是一样的)

<div :selected="true">selected</div>
<div :selected="false">UnSlected</div>
  • vue3实际渲染

//  vue3
div[selected=true] {
  color: red;
}
  • vue2实际渲染

// vue2
div[selected] {
  color: red;
}

评论区