Vue3.0-01 API学习

Vue 专栏收录该内容
36 篇文章 1 订阅

升级到Vue3

升级VueCLI

VueCLI需要在4.3.1以上才可以支持Vue3

npm update -g @vue/cli

vue -V
@vue/cli 4.4.6

创建项目

vue create vue3-learning
vue add vue-next # 添加 vue3 插件升级为 vue3

在创建项目时选择手动添加配置,选择vue-router和Vuex,这样创建完的项目各个插件也都会升级为支持Vue3的版本

{
  "dependencies": {
    "core-js": "^3.6.5",
    "vue": "^3.0.0-beta.1",
    "vue-router": "^4.0.0-alpha.6",
    "vuex": "^4.0.0-alpha.1"
  }
}

创建Vue实例

import {createApp} from 'vue';
import App from './App.vue';
import router from './router';
import store from './store';

createApp(App)
  .use(router)
  .use(store)
  .mount('#app');

创建Router

import {createRouter, createWebHistory} from 'vue-router';

const router = createRouter({
  history: createWebHistory(process.env.BASE_URL),
  routes
});

export default router;

创建路由的方式与以前有所变化,路由模式除了Hash模式(createWebHashHistory)和History模式(createWebHistory),还多了带缓存的History路由(createMemoryHistory

使用路由跳转的API也有所变化:

import {useRouter} from 'vue-router';

export default {
  setup() {
    const router = useRouter();
    const goHome = () => router.push('/');
    return {goHome}
  }
}

关于router的具体变化,后面再单独学习

创建Store

import {createStore} from 'vuex';

export default createStore({
  state: {
    count: 0
  },
  mutations: {
    changeCount(state, isAdd) {
      state.count = isAdd ? state.count + 1 : state.count - 1;
    }
  },
  actions: {},
  modules: {}
});

使用:

import {useStore} from 'vuex';

export default {
  setup() {
    const store = useStore();
    const storeState = store.state;
    return {storeState}
  }
}

可以看出来,Vuex和vue-router,API都有了一些变化,与React Hooks的API很类似,但是基本原理没有太大变化

setup

setup函数是新的Composition API的入口点

调用时机

Props初始化后就会调用setup函数,在beforeCreate钩子前被调用

返回值

setup返回的对象的属性将被合并到组件模板的渲染上下文,也可以返回一个渲染函数

参数

接受props作为第一个参数,使用的时候,需要首先声明props

export default {
  props: {
    name: String,
  },
  setup(props) {
    console.log(props.name)
  },
}

props是响应式的,前提是不对props进行解构,解构后会失去响应性

setup第二个参数是上下文对象,从2.x中的this选择性地暴露出一些属性,例如attrs/slots/emit等,可以解构取值不会失去响应性

this的用法

thissetup中不可用

响应式系统API

reactive

const obj = reactive({ count: 0 })

返回一个普通对象的响应式代理,响应式转换是深层的,会影响对象内部嵌套的属性,基于ES2015的Proxy实现,返回的代理对象不等于原始对象,避免使用原始对象

经过试验,Vue3中可以通过修改数组下标来响应式的更改数组成员的值了

ref

ref的引入是为了以变量形式传递响应式的值而不再依赖访问this:

const count = ref(0)

接受一个参数,返回一个响应式可改变的ref对象,ref对象拥有一个指向内部值的单一属性.value

ref主要目的是保证基本类型值的响应式,如果传入的参数不是基本类型,会调用reative方法进行深层响应式转换

ref使用时:

  • ref的返回值setup中返回应用在模板中时,会自动解构,不需要书写.value
  • ref作为reactive对象的属性被修改或访问时,也会自动解构,不需要书写.value
  • 通过ArrayMap等原声集合类中范围ref时,不会自动解构,需要使用.value获取值

reactive VS ref

使用refreactive的区别可以通过如何撰写编撰的JavaScript逻辑比较

// 风格 1: 将变量分离
let x = 0
let y = 0

function updatePosition(e) {
  x = e.pageX
  y = e.pageY
}

// --- 与下面的相比较 ---

// 风格 2: 单个对象
const pos = {
  x: 0,
  y: 0,
}

function updatePosition(e) {
  pos.x = e.pageX
  pos.y = e.pageY
}

使用ref就是将将风格(1)转换为使用ref,让基础类型值也具有响应性,使用reactive和风格(2)一致

只使用reactive的问题是,使用组合函数的时候必须始终保持对这个组合函数返回对象的引用以保持响应性,这个对象不能够被解构或者展开

// 组合函数:
function useMousePosition() {
  const pos = reactive({
    x: 0,
    y: 0,
  })

  // ...
  return pos
}

// 消费者组件
export default {
  setup() {
    // 这里会丢失响应性!
    const { x, y } = useMousePosition()
    return {
      x,
      y,
    }

    // 这里会丢失响应性!
    return {
      ...useMousePosition(),
    }

    // 这是保持响应性的唯一办法!
    // 你必须返回 `pos` 本身,并按 `pos.x` 和 `pos.y` 的方式在模板中引用 x 和 y。
    return {
      pos: useMousePosition(),
    }
  },
}

解决方法时使用toRefs将响应式对象的每个对象都转换为响应的ref

function useMousePosition() {
  const pos = reactive({
    x: 0,
    y: 0,
  })

  // ...
  return toRefs(pos)
}

// x & y 现在是 ref 形式了!
const { x, y } = useMousePosition()

目前阶段可以从下面两种风格二选其一:

(1)如果在普通的JavaScript中声明基础变量类型与对象变量时一样区别使用refreacitve,也就是说如果声明响应式的基础类型使用ref,如果声明响应式对象变量使用reactive

(2)全部使用reactive,然后在组合函数返回对象时使用toRefs

目前(2020.08.01)官方对refreactive的最佳实践还没有建议,自己选择更适合自己的风格使用,我会选择风格1使用。

computed

const count = ref(1)
const plusOne = computed(() => count.value + 1)

传入一个getter函数,也可以传入一个拥有getset函数的对象

readonly

const original = reactive({ count: 0 })

const copy = readonly(original)

传入一个对象(普通或者响应式对象)或ref,返回原始对象的深层的制度代理

watchEffect

与React的useEffect非常类似

const count = ref(0)

watchEffect(() => console.log(count.value))

立即执行传入的函数,并响应式追踪依赖

停止监听

setup中或生命周期钩子中被调用是,会被链接到组件的生命周期,在组件写在时自动停止

返回值是一个函数,可以手动来停止真挺

清除副作用

传入的函数中,可以接受onInvalidate函数作为入参,用来注册清理失效时的回调,在下面的情况中:

  • 副作用即将重新执行
  • 侦听器被停止
watchEffect((onInvalidate) => {
  const token = performAsyncOperation(id.value)
  onInvalidate(() => {
    // id 改变时 或 停止侦听时
    // 取消之前的异步操作
    token.cancel()
  })
})

执行时机

watchEffect会在组件初始运行时同步打印出来,在监听状态变化后,会在组件更新后执行副作用

watchEffect中访问DOM,需要在onMounted钩子中进行

可以通过传递第二个参数的flush属性来改变副作用函数的执行时机:

  • post,默认,在组件更新后执行
  • sync,同步运行
  • pre,在组件更新前执行

调试

在第二个参数中传入onTrackonTrigger来调试

watchEffect(
  () => {
    /* 副作用的内容 */
  },
  {
    onTrigger(e) {
      debugger
    },
  }
)

onTrack在依赖被追踪时被调用,onTrigger在依赖变更导致副作用被触发时调用

仅在开发模式下生效

watch

// 侦听一个 getter
const state = reactive({ count: 0 })
watch(
  () => state.count,
  (count, prevCount) => {
    /* ... */
  }
)

// 直接侦听一个 ref
const count = ref(0)
watch(count, (count, prevCount) => {
  /* ... */
})

// 侦听多个数据源
watch([fooRef, barRef], ([foo, bar], [prevFoo, prevBar]) => {
  /* ... */
})

与2.X的watch完全相同,与watchEffect相比的不同点:

  • 懒执行
  • 将依赖提取为第一个参数,更明确哪些状态的改变会重新运行副作用
  • 可以访问侦听状态变化前后的值

生命周期

生命周期钩子函数只能在setup期间同步使用,在组件卸载时,生命周期内部创建的侦听器和计算状态也会被自动删除

与Vue2.x相比,beforeCreatedcreated被删除了,对应的逻辑在setup内部完成,其他的生命周期钩子都改为了onXxx的形式(beforeDestoryed改为了onBeforeUnmountdestroyed改为了onUnmounted

两个新增的调试钩子函数onRenderTrackedonRenderTriggered

export default {
  onRenderTriggered(e) {
    debugger
    // 检查哪个依赖性导致组件重新渲染
  },
}

目前(2020.07.29)在Demo尝试调用这两个钩子函数,没有生效,不知道是Bug还是我调用的姿势不对

依赖注入

使用provideinject实现依赖注入,与2.x版本中基本一致,只能在setup中使用

import { provide, inject } from 'vue'

const ThemeSymbol = Symbol()

const Ancestor = {
  setup() {
    provide(ThemeSymbol, ref('dark'))
  },
}

const Descendent = {
  setup() {
    const theme = inject(ThemeSymbol, ref('light') /* optional default value */)
    return {
      theme,
    }
  },
}

使用ref传值可以保证providedinjected之间值的响应性

模板Refs

Vue2.x中的ref原本是用于获取DOM的, Vue3中ref不仅可以响应化数据,也可以实现获取DOM的功能

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

<script>
  import { ref, onMounted } from 'vue'

  export default {
    setup() {
      const root = ref(null)

      onMounted(() => {
        // 在渲染完成后, 这个 div DOM 会被赋值给 root ref 对象
        console.log(root.value) // <div/>
      })

      return {
        root,
      }
    },
  }
</script>

setup中声明一个ref并返回,在模板中声明ref并且值与返回的ref相同,这时在渲染初始化后(onMounted)就可以获取分配的DOM或组件实例

v-for中使用时,需要使用3.0新增的函数形的ref,为ref赋值:

<template>
  <div v-for="(item, i) in list" :ref="el => { divs[i] = el }">
    {{ item }}
  </div>
</template>

<script>
  import { ref, reactive, onBeforeUpdate } from 'vue'

  export default {
    setup() {
      const list = reactive([1, 2, 3])
      const divs = ref([])

      // 确保在每次变更之前重置引用
      onBeforeUpdate(() => {
        divs.value = []
      })

      return {
        list,
        divs,
      }
    },
  }
</script>

响应式系统工具集

isRef

判断值是否是ref对象

isProxy

判断一个对象是否是由reactive或者readonly创建的代理

isReactive

判断一个对象是否是由reactive创建的代理。

如果这个代理是由readonly创建的,但是又被reactive创建的另一个代理包裹了一层,那么同样也会返回true

isReadonly

判断一个对象是否是由readonly创建的代理。

unref

用来快速返回ref的值,如果参数是ref,返回它的value,否则返回参数本身。它是val = isRef(val) ? ref.value : ref的语法糖

toRef

reactive对象的属性创建一个ref,这个ref可以被传递并且保持响应性

const state = reactive({
  foo: 1,
  bar: 2,
})

const fooRef = toRef(state, 'foo')

fooRef.value++
console.log(state.foo) // 2

state.foo++
console.log(fooRef.value) // 3

当需要将一个Prop中的属性作为ref传给组合逻辑函数时,可以使用toRef

export default {
  setup(props) {
    useSomeFeature(toRef(props, 'foo'))
  },
}

目前还没有发现这种情况的实际场景

toRefs

把一个响应式对象转换为普通对象,该普通对象的每个属性都是一个ref,与原来的响应式对象一一对应

const state = reactive({
  foo: 1,
  bar: 2,
})

const stateAsRefs = toRefs(state)
/*
stateAsRefs 的类型如下:

{
  foo: Ref<number>,
  bar: Ref<number>
}
*/

// ref 对象 与 原属性的引用是 "链接" 上的
state.foo++
console.log(stateAsRefs.foo) // 2

stateAsRefs.foo.value++
console.log(state.foo) // 3

当从一个组合逻辑中返回响应式对象时,用toRefs是很有效的,它可以让消费组件可以解构或者扩展返回的对象,而不是去响应性

function useFeatureX() {
  const state = reactive({
    foo: 1,
    bar: 2,
  })

  // 对 state 的逻辑操作

  // 返回时将属性都转为 ref
  return toRefs(state)
}

export default {
  setup() {
    // 可以解构,不会丢失响应性
    const { foo, bar } = useFeatureX()

    return {
      foo,
      bar,
    }
  },
}

高级响应式系统API

customRef

用来自定义ref,可以显示依赖追踪和触发响应,接受一个函数,函数的两个参数是用于追踪的track和触发响应式的trigger,返回一个带有getset属性的对象

可以使用自定义ref来实现带防抖功能的v-model

function useDebouncedRef(value, delay = 200) {
  let timeout
  return customRef((track, trigger) => {
    return {
      get() {
        track()
        return value
      },
      set(newValue) {
        clearTimeout(timeout)
        timeout = setTimeout(() => {
          value = newValue
          trigger()
        }, delay)
      },
    }
  })
}

export default {
  setup() {
    return {
      text: useDebouncedRef('hello'),
    }
  },
}

个人还不知道有什么好的应用场景,实际上上面的例子不通过custromRef来实现,可能灵活度还更大

markRaw

显示标记一个对象永远不会转换为响应式dialing,返回这个对象本身

const foo = markRaw({})
console.log(isReactive(reactive(foo))) // false

// 如果被 markRaw 标记了,即使在响应式对象中作属性,也依然不是响应式的
const bar = reactive({ foo })
console.log(isReactive(bar.foo)) // false

它的作用是用来覆盖reactive为默认深层的特性,主要用来提升性能,比如一些值复杂的第三方类库的实例或者Vue组件对象,或者一个元素数量庞大,但是数据不可变,跳过Proxy也可以提升性能

这种标识只停留在根级别,markRaw对象的属性如果被reactive处理,仍然会返回一个响应式对象,并且导致原始值与Proxy值不同

const foo = markRaw({
  nested: {},
})

const bar = reactive({
  // 尽管 `foo` 己经被标记为 raw 了, 但 foo.nested 并没有
  nested: foo.nested,
})

console.log(foo.nested === bar.nested) // false

shallowReactive

只为某个对象的私有(第一层)属性创建浅层次的响应式代理,不会对深层属性做深层次、递归地响应式代理

onst state = shallowReactive({
  foo: 1,
  nested: {
    bar: 2,
  },
})

// 变更 state 的自有属性是响应式的
state.foo++

// ...但不会深层代理
isReactive(state.nested) // false
state.nested.bar++ // 非响应式

shallowReadonly

shallowReactive类似,只为对象的私有(第一层)属性创建浅层的只读响应代理

shallowRef

创建一个ref,将会追踪它的.value更改操作,但是不会对变更后的.value做响应式代理转换

const foo = shallowRef({})

// 更改对操作会触发响应
foo.value = {}

// 但上面新赋的这个对象并不会变为响应式对象
isReactive(foo.value) // false

注意,如果每次都为foo.value重新赋值,那么仍然会触发响应式改动。上面说的“不会变为响应式对象”指的是更改value的某个属性不会触发响应式改动

toRaw

返回reactive或者readonly方法转换为响应式代理的普通对象。用于临时读取,访问不会被代理、跟踪,写入时不会触发更改。不建议一致持有原始对象的引用。

组合式API

更多的灵活性来自更多的自我克制

组合式API的初衷就是为了实现更有组织的代码,实现更灵活的逻辑提取与复用,在代码中会出现更多的、零碎的函数模块,在不同的位置、不同的组件间进行重复调用

它可以避免Vue2.x时代逻辑复用的几种主要形式(Mixin/HOS/SLOT)的弊端,带来了比较明显的好处:

但是它在提到了代码质量的上限的同时,降低了下线,setup中会出现大量面条式的代码,避免这种糟糕情况的关键就是,将逻辑更合理的划分为单独的函数,将setup作为一个入口,在其中进行不同组合函数的调用。

与React Hooks比较

Vue3的基于函数的组合式API受到了React Hooks的启发,在很多思维模型方面与React Hooks很类似,提供了同等级别的逻辑组合能力,但是也有着比较明显的不同,组合式API的setup函数只会被调用一次,也就意味着使用组合式API时:

  1. 不需要考虑调用顺序,可以用在条件语句中(React Hooks不可以)
  2. 不会再每次渲染时重复执行,降低垃圾回收的压力(React Hooks每次渲染都会重复执行)
  3. 不存在内联处理函数导致子组件永远更新的问题,也不需要useCallback(React Hooks需要用useCallback进行性能优化)
  4. 不存在忘记记录依赖的问题,也不需要useEffecruseMemo并传入依赖数组以捕获过时的变量,Vue的自动以来可以确保侦听器和计算值总是准确无误的(React Hooks需要手动记录依赖)

参考

  • 0
    点赞
  • 0
    评论
  • 1
    收藏
  • 一键三连
    一键三连
  • 扫一扫,分享海报

打赏
文章很值,打赏犒劳作者一下
相关推荐
DirectX修复工具(DirectX Repair)是一款系统级工具软件,简便易用。本程序为绿色版,无需安装,可直接运行。 本程序的主要功能是检测当前系统的DirectX状态,如果发现异常则进行修复。程序主要针对0xc000007b问题设计,可以完美修复该问题。本程序中包含了最新版的DirectX redist(Jun2010),并且全部DX文件都有Microsoft的数字签名,安全放心。 本程序为了应对一般电脑用户的使用,采用了易用的一键式设计,只要点击主界面上的“检测并修复”按钮,程序就会自动完成校验、检测、下载、修复以及注册的全部功能,无需用户的介入,大大降低了使用难度。在常规修复过程中,程序还会自动检测DirectX加速状态,在异常时给予用户相应提示。 本程序适用于多个操作系统,如Windows XP(需先安装.NET 2.0,详情请参阅“致Windows XP用户.txt”文件)、Windows Vista、Windows 7、Windows 8、Windows 8.1、Windows 8.1 Update、Windows 10,同时兼容32位操作系统和64位操作系统。本程序会根据系统的不同,自动调整任务模式,无需用户进行设置。 本程序的V4.0版分为标准版、增强版以及在线修复版。所有版本都支持修复DirectX的功能,而增强版则额外支持修复c++的功能。在线修复版功能与标准版相同,但其所需的数据包需要在修复时自动下载。各个版本之间,主程序完全相同,只是其配套使用的数据包不同。因此,标准版和在线修复版可以通过补全扩展包的形式成为增强版。本程序自V3.5版起,自带扩展功能。只要在主界面的“工具”菜单下打开“选项”对话框,找到“扩展”标签,点击其中的“开始扩展”按钮即可。扩展过程需要Internet连接,扩展成功后新的数据包可自动生效。扩展用时根据网络速度不同而不同,最快仅需数秒,最慢需要数分钟,烦请耐心等待。如扩展失败,可点击“扩展”界面左上角小锁图标切换为加密连接,即可很大程度上避免因防火墙或其他原因导致的连接失败。 本程序自V2.0版起采用全新的底层程序架构,使用了异步多线程编程技术,使得检测、下载、修复单独进行,互不干扰,快速如飞。新程序更改了自我校验方式,因此使用新版本的程序时不会再出现自我校验失败的错误;但并非取消自我校验,因此程序安全性与之前版本相同,并未降低。 程序有更新系统c++功能。由于绝大多数软件运行时需要c++的支持,并且c++的异常也会导致0xc000007b错误,因此程序在检测修复的同时,也会根据需要更新系统中的c++组件。自V3.2版本开始使用了全新的c++扩展包,可以大幅提高工业软件修复成功的概率。修复c++的功能仅限于增强版,标准版及在线修复版在系统c++异常时(非丢失时)会提示用户使用增强版进行修复。除常规修复外,新版程序还支持C++强力修复功能。当常规修复无效时,可以到本程序的选项界面内开启强力修复功能,可大幅提高修复成功率。请注意,请仅在常规修复无效时再使用此功能。 程序有两种窗口样式。正常模式即默认样式,适合绝大多数用户使用。另有一种简约模式,此时窗口将只显示最基本的内容,修复会自动进行,修复完成10秒钟后会自动退出。该窗口样式可以使修复工作变得更加简单快速,同时方便其他软件、游戏将本程序内嵌,即可进行无需人工参与的快速修复。开启简约模式的方法是:打开程序所在目录下的“Settings.ini”文件(如果没有可以自己创建),将其中的“FormStyle”一项的值改为“Simple”并保存即可。 新版程序支持命令行运行模式。在命令行中调用本程序,可以在路径后直接添加命令进行相应的设置。常见的命令有7类,分别是设置语言的命令、设置窗口模式的命令,设置安全级别的命令、开启强力修复的命令、设置c++修复模式的命令、控制Direct加速的命令、显示版权信息的命令。具体命令名称可以通过“/help”或“/?”进行查询。 程序有高级筛选功能,开启该功能后用户可以自主选择要修复的文件,避免了其他不必要的修复工作。同时,也支持通过文件进行辅助筛选,只要在程序目录下建立“Filter.dat”文件,其中的每一行写一个需要修复文件的序号即可。该功能仅针对高级用户使用,并且必须在正常窗口模式下才有效(简约模式时无效)。 本程序有自动记录日志功能,可以记录每一次检测修复结果,方便在出现问题时,及时分析和查找原因,以便找到解决办法。 程序的“选项”对话框中包含了7项高级功能。点击"常规”选项卡可以调整程序的基本运行情况,包括日志记录、安全级别控制、调试模式开启等。只有开启调试模式后才能在C
©️2020 CSDN 皮肤主题: 编程工作室 设计师:CSDN官方博客 返回首页

打赏

多拉斯基

你的鼓励将是我创作的最大动力

¥2 ¥4 ¥6 ¥10 ¥20
输入1-500的整数
余额支付 (余额:-- )
扫码支付
扫码支付:¥2
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值