vue3的相关知识点归纳

Topic:Vue3的设计与改变

Vue3发展历程

  1. 2018/09/30:尤雨溪在medium个人博客上发布了 Vue 3.0 的开发路线
  2. 2019年初:采用了RFC(征求意见)流程
  3. 2019/10/05:Vue 3 源码开放(pre-alpha状态)
  4. 2020/04/17:Vue 3.0 beta
  5. 2020/05/28:发表文章「Vue 3 设计过程」
  6. 2020/07/18:Vue 3.0 RC
  7. 2020/09/18:Vue 3.0 “One Piece"

Vue3的核心设计与提升

响应式核心原理:Object.defineProperty 切换到 Proxy

  • 可检测到新的属性添加
  • 支持 Map、Set、WeakMap 和 WeakSet
  • 提供更好的性能
    • Proxy 作为浏览器的新标准,性能上是一定会得到厂商的大力优化的
    • 不需要像Vue2那样递归对所有的子数据进行响应式定义,而是再获取到深层数据的时候再去利用reactive进一步定义响应式,这对于大量数据的初始化场景来说收益会非常大

Vue Composition API

  • 旨在解决大规模应用场景中的痛点
  • 更灵活的逻辑组织模式(如逻辑提取与复用)
  • 更为可靠的类型推断能力

Vue3代码库完全使用typescript编写

  • 已支持对模板表达式和 props 的类型检查
  • 已全面支持 TSX

内部模块解耦(采用monorepo)
模块之间的依赖关系更加明确, 降低项目贡献壁垒并提高其长期可维护性

内部包有自己的单独API,类型定义和测试
解锁高级用法
编译器支持自定义AST转换,用于在构建时自定义(如,在构建时进行i18n操作)
核心运行时提供了一系列 API,用于针对不同渲染目标(如native moile、WebGL或终端)的自定义容器。默认的 DOM 渲染器也使用这系列 API 构建。
@vue/reactivity 模块——Vue响应式系统;可作为独立包进行使用。也可以与其他模块解决方案配对使用(如 lit-html),甚至是在非 UI 场景中使用。

性能提升

  • bundle包大小方面(tree-shaking 减少了 41% 的体积)
  • 初始渲染速度方面(快了 55%)
  • 更新速度方面(快了 133%)
  • 内存占用方面(减少了 54%)

实验特性

  • <script setup>在 SFC 内使用 Composition API 的语法糖
<template>
  <button @click="inc">{{ count }}</button>
</template>

<script setup>
  import { ref } from 'vue'
  export const count = ref(0)
  export const inc = () => count.value++
</script>

  • <style vars>:在 SFC 中支持将状态作为 CSS 变量注入到样式中
  • <Suspense>组件
  • 异步组件或带有 async setup() 组件

SFC: Singe-File Components(单文件组件,.vue文件)

Vue3的性能优化

减小包的尺寸:

  • 将大多数全局API和内部帮助程序移至ES模块导出(如@vue/reactivity@vue/runtime-core等)
  • 利用Tree Shaking,只打包需要使用的代码

渲染策略优化

vue渲染策略:

  1. HTML的模板 => (编译) => 虚拟DOM树的渲染函数
  2. 通过递归遍历两个虚拟DOM树,并比较每个节点上的每个属性来确定实际DOM的哪些部分需要更新

瓶颈:

任何一点改变却仍然需要递归整个虚拟DOM树,以了解发生了什么变化,尤其在查看包含大量静态内容且只有少量动态绑定(整个虚拟DOM)的模板时,效率低下。

最终目的:

克服虚拟DOM的瓶颈,最好的方法是消除不必要的虚拟DOM树遍历和属性比较

优化工作:

编译器分析模板并生成带有优化提示的代码,而运行时将拾取提示,并在可能的情况下采用快速路径:

  1. 将模版分为动态的和静态的“块”,对动态块进行了扁平化处理,减少运行时的遍历开销:在树的层面上,我们注意到,节点结构在没有模板指令的时候是完全静态的(例如,v-if和v-for)。如果我们将模板分为动态的和静态的“块”,每个块内的节点结构再次变得完全静态。当我们更新一个块内的节点时,我们不再需要递归遍历树,因为我们可以在平面数组中跟踪该块内的动态绑定。通过将我们需要执行的树遍历量减少一个数量级,从而节约了虚拟DOM的大部分开销。

  2. 静态树提升与静态prop提升:编译器会主动检测模板中的静态节点,子树甚至数据对象,并将其提升到生成代码中的render函数之外。这样可以避免在每个渲染上重新创建这些对象,从而大大提高了内存使用率并减少了垃圾回收的频率。

  3. 生成编译器的优化提示:在元素级别,编译器还会根据需要执行的更新类型为具有动态绑定的每个元素生成一个优化标志。例如,具有动态类绑定和许多静态属性的元素将收到一个标志,指示仅需要进行类检查。运行时将获取这些提示并采用专用的快速路径。

响应式原理:Proxy
Vue会使用带有getter和setter的处理程序遍历其所有property并将其转换为Proxy;
这个Proxy使Vue能够在property被访问或修改时执行依赖项跟踪和更改通知。

const handler = {
  get(target, prop, receiver) {
    // 追踪函数:依赖追踪
    track(target, prop)
    const value = Reflect.get(...arguments)
    if (isObject(value)) {
      return reactive(value)
    } else {
      return value
    }
  },
  set(target, key, value, receiver) {
    // 触发函数:更改通知
    trigger(target, key)
    return Reflect.set(...arguments)
  }
}
// 简化版
function track(target: object, type: TrackOpTypes, key: unknown) {
  const depsMap = targetMap.get(target);
  // 收集依赖时 通过 key 建立一个 set
  let dep = new Set()
  targetMap.set(ITERATE_KEY, dep)
  // effect可以先理解为更新函数,存放在 dep 里
  dep.add(effect)    
}
// 简化版
function trigger(target: object, type: TriggerOpTypes, key?: unknown,) {
  // 是通过key找到所有更新函数 依次执行
  const dep = targetMap.get(target)
  dep.get(key).forEach(effect => effect())
}

Vue Composition API(简称VCA)

什么是Composition API

Composition API是对于现有Option API的补充,构建于响应式API基础之上

setup函数
新的组件选项,VCA的入口点

export default {
  props: {
    title: String
  },
  setup(props, context) {
    console.log(props.title)
    // Attribute (非响应式对象)
    console.log(context.attrs)
    // 插槽 (非响应式对象)
    console.log(context.slots)
    // 触发事件 (方法)
    console.log(context.emit)
    return {}
  }
}
  • 执行时机:在生命周期钩子beforeCreate钩子之前被调用,且执行一次
  • 参数:第一个参数:props,第二个参数context(属性有attrs、slots与emit等)
  • 返回一个对象:返回的对象属性将会被合并到组件模板的渲染上下文(this)

注意点:

  1. 参数props不能解构,会失去响应性
  2. this在 setup() 中不可用
  3. 在setup函数中,不能访问组件选项data、computed与methods

响应式API

ref
接受一个参数值并返回一个响应式且可改变的 ref 对象,通过.value获取属性值

const count = ref(0)
console.log(count.value) // 0

count.value++
console.log(count.value) // 1

reactive
接收一个普通对象然后返回该普通对象的响应式代理。等同于 2.x 的 Vue.observable()

const state = reactive({ count: 0 })

computed
传入一个 getter 函数,返回一个默认不可手动修改的 ref 对象。

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

console.log(plusOne.value) // 2

plusOne.value++ // 错误!

或者传入一个拥有 get 和 set 函数的对象,创建一个可手动修改的计算状态。

const plusOne = computed({
  get: () => count.value + 1,
  set: (val) => {
    count.value = val - 1
  },
})

plusOne.value = 1
console.log(count.value) // 0

readonly
传入一个对象(响应式或普通)或 ref,返回一个原始对象的只读代理

const original = reactive({ count: 0 })
const copy = readonly(original)
watchEffect(() => {
  // 依赖追踪
  console.log(copy.count)
})
// original 上的修改会触发 copy 上的侦听
original.count++
// 无法修改 copy 并会被警告
copy.count++ // warning!

watchEffect
立即执行传入的一个函数,并响应式追踪其依赖,并在其依赖变更时重新运行该函数

const count = ref(0)

watchEffect(() => console.log(count.value))
// -> 打印出 0

setTimeout(() => {
  count.value++
  // -> 打印出 1
}, 100)

停止侦听

const stop = watchEffect(() => {
  /* ... */
})

// 之后
stop()

清除副作用

watchEffect((onInvalidate) => {
  const token = performAsyncOperation(id.value)
  onInvalidate(() => {
    // id 改变时 或 停止侦听时
    // 取消之前的异步操作
    token.cancel()
  })
})
watch

watch API 完全等效于 2.x this.$watch

侦听单个数据

const count = ref(0)
// 侦听单个数据
watch(count, (count, prevCount) => {
  /* ... */
})

// 侦听多个数据
watch([fooRef, barRef], ([foo, bar], [prevFoo, prevBar]) => {
  /* ... */
})
<template>
    <button @click="increase">increase</button><span> count is: {{count}}</span>
</template>

<script lang="ts">
import { computed, defineComponent, reactive, readonly, ref, toRefs, watch, watchEffect } from 'vue'
export default defineComponent({
    setup() {
        // 响应式对象
        const count = ref(0)
        const state = reactive({ 
            count, 
            // 响应式计算对象
            double: computed(() => count.value)
        })
        // 与vue2.x的watch选项等效:监听响应式对象count
        watch(count, (count, prevCount) => {
            /** do something */
        })
        // 副作用函数:监听响应式对象count
        const stop = watchEffect(() => console.log(count.value))
        if (count.value > 5) {
            // 停止侦听
            stop()
        }
        // 只读响应式对象
        const copy = readonly(count)
        // 无法修改 copy 并会被警告
        copy.value++ // warning!
                const increase = () => count.value++
                // ES6解构,注意;需通过toRefs转换为响应式对象,否则property的响应式都会丢失
                const { double } = toRefs(state) 
        return {
                        count,
                        double,
            increase
        }
    },
})
</script>

生命周期钩子
beforeCreate -> 使用 setup()
created -> 使用 setup()
beforeMount -> onBeforeMount
mounted -> onMounted
beforeUpdate -> onBeforeUpdate
updated -> onUpdated
beforeDestroy -> onBeforeUnmount
destroyed -> onUnmounted
errorCaptured -> onErrorCaptured

新增钩子(用于调试)
onRenderTracked
onRenderTriggered

import { 
    defineComponent, ref, onBeforeMount, onMounted, onBeforeUpdate, onUpdated,
    onBeforeUnmount, onUnmounted, onErrorCaptured, onRenderTracked, onRenderTriggered
} from 'vue'
export default defineComponent({
    setup() {
        const count = ref(0)
        // 在挂载开始之前被调用调用
        onBeforeMount(() => {}) 
        // 在挂载后被调用调用
        onMounted(() => {})
        // 组件更新前调用
        onBeforeUpdate(() => {})
        // 组件更新后调用
        onUpdated(() => {})
        // 组件卸载前调用
        onBeforeUnmount(() => {})
        // 组件卸载后调用
        onUnmounted(() => {})
        // 当捕获一个来自子孙组件的错误时被调用
        onErrorCaptured(() => {})
        
        onRenderTracked((DebuggerEvent) => {
            debugger
            // 检查哪个依赖性被组件追踪
        })
        onRenderTriggered((DebuggerEvent) => {
            debugger
            // 检查哪个依赖性导致组件重新渲染
        })
        return { count }
    },
})

设计动机

更友好的类型推断(defineComponent结合typescript)

vue2.x对typescript的类型推断不友好:

  • vue2.x通过this来暴露property,所有选项(如methords)的this都是指向组件实例,而不是指向mehordes对象。
  • vue2.x使用vue-class-component将组件编写为TypeScript class (借助 decorator,不稳定的 stage 2 提,存在不确定性)

2. 实现逻辑提取与复用

// 追踪鼠标位置的例子
import { ref, onMounted, onUnmounted } from 'vue'

export default function useMousePosition() {
  const x = ref(0)
  const y = ref(0)
  function update(e) {
    x.value = e.pageX
    y.value = e.pageY
  }
  onMounted(() => window.addEventListener('mousemove', update))
  onUnmounted(() => window.removeEventListener('mousemove', update)})

  return { x, y }
}
import { useMousePosition } from './useMousePosition'

export default {
  setup() {
    const { x, y } = useMousePosition()
    // 其他逻辑...
    return { x, y }
  },
}
  1. 更加灵活的代码组织模式,不需要总是通过选项来组织代码:
    Option API:通过选项(如methords、computed、watch等)来组织代码,逻辑关注点分散,这种碎片化使得复杂的组件难以维护
    Composition API:通过逻辑来组织代码,逻辑关注点内聚,便于复用与维护


    image.png

对比Mixins、高阶组件和无渲染组件

弊端:

  • 渲染上下文中暴露的 property 来源不清晰。例如在阅读一个运用了多个 mixin 的模板时,很难看出某个 property 是从哪一个 mixin 中注入的。
  • 命名空间冲突。Mixin 之间的 property 和方法可能有冲突,同时高阶组件也可能和预期的 prop 有命名冲突。
  • 性能方面,高阶组件和无渲染组件需要额外的有状态的组件实例,从而使得性能有所损耗。

相比而言,组合式 API:

  • 暴露给模板的 property 来源十分清晰,因为它们都是被组合逻辑函数返回的值。
  • 不存在命名空间冲突,可以通过解构任意命名
  • 不再需要仅为逻辑复用而创建的组件实例。

缺点与槽点

缺点

  • 新概念ref的心智负担:
    1. 不断地区分响应式引用、基础类型值和对象;
    2. 读写ref的操作更冗余,需要访问.value;
    3. 需要理解ref和reactive的区别和使用场景。
  • setup()返回语句可能会冗长

槽点

  • 所有逻辑都堆在setup函数实现,代码可能会过于臃肿
  • 担心VCA会让没有经验的人编写出意大利面条式代码(维护性差)——需约束
  • 抄袭 React Hook

// 尤雨溪 原话
其实真的用过并且懂 React hooks 的人看到这个都会意识到 Vue Composition API (VCA)跟 hooks 本质上的区别。VCA 在实现上也其实只是把 Vue 本身就有的响应式系统更显式地暴露出来而已。真要说像的话,VCA 跟 MobX 还更像一点。

但对于不懂 React hooks 的人来说,长的像就是一样了,懒得解释。

关于Class API的提议 (opens new window)[Abandoned]

首先,使用Class API(装饰器方案)来支持更好的类型推断;但Class API的合理性存疑
不确定性:装饰器对第二阶段规范的依赖,存在很多不确定性,尤其是当TypeScript的当前实现与TC39提案完全不同步
props的类型推断

  1. 尴尬的双重声明:
interface Props {
  message: string
}
class App extends Component<Props> {
  @prop message: string
}
  1. 无法将装饰器声明的props类型暴露给this.$props,这会破坏TSX的支持
  • 关于"reserved" methods的命名空间:解决方案不完美
  • 实现复杂:
    1. 涉及许多边缘情况,需要引入其他内部代码;
    2. 为了使用Proxy this做响应式追踪,this在构造器中的实现会与在其他的地方不同;
    3. 更多代码:包含类组件与对象组件的申明的转换代码
  • 收益不大:除了提供更好的TypeScript集成之外没有提供任何其他功能。

与React Hook对比

React Hook设计动机:

  1. 在组件之间复用状态逻辑很难:复用不同组件之间的状态逻辑
  2. 复杂组件变得难以理解(每个生命周期常常包含一些不相关的逻辑,且逻辑分散)
  3. 难以理解的 class
    • 理解this的工作方式;
    • 不能忘记绑定事件处理器;
    • class 组件会无意中鼓励开发者使用一些让优化措施无效的方案:不好压缩、热重载不稳定。

Vue Composition API与React Hook的用法对比

// Vue Composition API
import { ref, onMounted, onUnmounted } from 'vue'

export default function useMousePosition() {
  const x = ref(0)
  const y = ref(0)
  function update(e) {
    x.value = e.pageX
    y.value = e.pageY
  }
  onMounted(() => window.addEventListener('mousemove', update))
  onUnmounted(() => window.removeEventListener('mousemove', update)})

  return { x, y }
}
// React Hook

import { useState, useEffect } from 'react';

export default function usePosition() {
    const [ position, setPosition ] = useState({ x: 0, y: 0 }) 
    function update(e) {
        setPosition({
            x: e.pageX,
            y: e.pageY
        })
    }
    useEffect(() => {
        window.addEventListener('mousemove', update)
        return () => {
            window.removeEventListener('mousemove', update)
        }
    }, [])

    return position
}

React Hook的规则(限制,增加心智负担)

  1. 不要在循环,条件或嵌套函数中调用Hook;
  2. 只能React函数的最顶层调用(遵守这条规则,你就能确保Hook在每一次渲染中都按照同样的顺序被调用。这让React能够在多次的useState和useEffect调用之间保持hook状态的正确);
  3. 只能在React的函数组件中调用Hook。

Vue Composition API的优势

  1. 与 React Hook不同,setup 函数仅被调用一次,这在性能上比较占优。
  2. 对调用顺序没什么要求,每次渲染中不会反复调用Hook函数,产生的的 GC 压力较小。
  3. 不必考虑总是需要useCallback的问题,以防止传递函数prop给子组件的引用变化,导致无必要的重新渲染。
  4. React Hook 里的「依赖」是需要你去手动声明的;Vue会自动追踪依赖。
  5. React Hook有臭名昭著的闭包陷阱问题,如果用户忘记传递正确的依赖项数组,useEffect 和 useMemo 可能会捕获过时的变量,这不受此问题的影响。Vue 的自动依赖关系跟踪确保观察者和计算值始终正确无误。
// React Demo
import React, { useState, useEffect, useCallback } from 'react';

//  state变化时,重新执行
function Example() {
  const [ count, setCount ] = useState(0)
  const [ val, setVal ] = useState('')
  // count 或 val变化时都会执行
  useEffect(() => console.log('count -', count, 'val -', val))
  // 申明依赖count:只有count变化时执行,val变化时不执行
  useEffect(() => console.log('count -', count), [ count ])
  // 执行一次
  useEffect(() => {
    console.log('mounted!')
    return () => console.log('unmounted!')
  }, [])
  // count变化时,updateVal的引用不变,子组件Child不会重新渲染
  const updateVal = useCallback((val) => setVal(val), [ val ])
  // count变化时,updateVal2的引用改变,子组件Child重新渲染
  const updateVal2 = (val) => setVal(val)
  
  return (
    <div>
      <button onClick={() => setCount(count + 1)}>Increase</button>
      count is {count}
      <Child val={val} updateVal={updateVal} />
      <Child val={val} updateVal={updateVal2} />
    </div>
  );
}
<template>
    <button @click="increase">increase</button><span> count is: {{count}}</span>
    <input type="text" v-model="val">
    <!-- 只有val变化时, HelloWorld组建更新,但不会重载 -->
    <HelloWorld :msg="val" />
</template>

<script lang="ts">
import { defineComponent, onMounted, onUnmounted, ref, watchEffect } from 'vue'
import HelloWorld from './HelloWorld.vue'
export default defineComponent({
    components: { HelloWorld },
    // 只执行一次
    setup() {
        const count = ref(0)
        const val = ref('')
        // 只有count变化才执行
        watchEffect(() => console.log('count -', count.value))
        // 只有val变化才执行
        watchEffect(() => console.log('val -', val.value))
        // 组建挂载时执行
        onMounted(() => console.log('<mounted>'))
        // 组件卸载时执行
        onUnmounted(() => console.log('<unmount>'))
        const increase = () => count.value += 1

        return {
            val, count, 
            increase
        }
    },
})
</script>

使用 react hooks 带来的收益抵得过使用它的成本吗?(opens new window)

如何在Vue2中使用Composition API

# 安装插件
npm install @vue/composition-api
// 入口文件中
import VueCompositionAPI from '@vue/composition-api'
// 挂载Composition API
Vue.use(VueCompositionAPI)
// vue组件
import { computed, ref, reactive, watchEffect, onMounted } from '@vue/composition-api'
export default {
  setup(props, ctx) {
    // 创建响应式数据对象
    const count = ref(0)
    const state = reactive({ 
        count 
        double: computed(() => count * 2)
    })
    watchEffect(() => console.log(state.count))
    onMounted(() => console.log('mounted'))
    return {
        state,
        increment() { state.count += 1 }
    }
  }
}

注意点:

  • Vue2与Vue3响应式对象的核心原理不同;vue2数据观测仍存在缺陷:
    1. 无法检测属性的添加和删除;
    2. 无法检测数组索引和长度的变更
  • 随着提案的更新,它也可能会做一些不兼容的变更,所以我们不建议这个阶段在生产环境中使用它。

相关链接

兼容性

与Vue2兼容,但有重大更改与小改变:

全局API

全局API已更改为使用应用程序实例

2.x 全局 API 3.x 实例 API (app)
Vue.config app.config
Vue.config.productionTip removed
Vue.config.ignoredElements app.config.isCustomElement
Vue.component app.component
Vue.directive app.directive
Vue.mixin app.mixin
Vue.use app.use
// vue2
import Vue from 'vue'
import App from './App.vue'
import VueCompositionAPI from '@vue/composition-api'

Vue.use(VueCompositionAPI)
Vue.config.productionTip = false

new Vue({
  render: h => h(App),
}).$mount('#app')
// vue3
import { createApp } from 'vue'
import App from './App.vue'
import HelloWorld from './components/HelloWorld.vue';
import Counter from './components/Counter.vue';
import { createRouter, createWebHistory } from 'vue-router'

const router = createRouter({
    history: createWebHistory(),
    routes: [ ... ]
})
const app = createApp(App)
app.use(router)
app.mount('#app')

全局和内部 API 已经被重构为可 tree-shakable:
Vue 2.x 中的这些全局 API 受此更改的影响:

Vue.nextTick
Vue.observable (用 Vue.reactive 替换)
Vue.version
Vue.compile (仅全构建)
Vue.set (仅兼容构建)
Vue.delete (仅兼容构建)

// vue2
import Vue from 'vue'

Vue.nextTick(() => {
  // 一些和DOM有关的东西
})
// vue3
import { nextTick } from 'vue'

nextTick(() => {
  // 一些和DOM有关的东西
})

模版指令

  • 组件上 v-model 用法已更改
  • <template v-for> 和非 - v-for 节点上 key 用法已更改
  • 在同一元素上使用的 v-if 和 v-for优先级已更改(v-if优先)
  • v-bind="object" 现在排序敏感
  • v-for 中的 ref 不再注册 ref 数组

组件

  • 只能使用普通函数创建功能组件
  • functional 属性在单文件组件 (SFC) <template> 和 functional 组件选项被抛弃
  • 异步组件现在需要 defineAsyncComponent 方法来创建

渲染函数

  • 渲染函数 API 改变
  • scopedSlots property 已删除,所有插槽都通过slots 作为函数暴露
  • 自定义指令 API 已更改为与组件生命周期一致
  • 一些转换 class 被重命名了:
    • v-enter -> v-enter-from
    • v-leave -> v-leave-from
  • 组件 watch 选项和实例方法 $watch 不再支持点分隔字符串路径,请改用计算函数作为参数
  • 在 Vue 2.x 中,应用根容器的 outerHTML 将替换为根组件模板 (如果根组件没有模板/渲染选项,则最终编译为模板)。VUE3.x 现在使用应用程序容器的 innerHTML。

移除API

  • keyCode 支持作为 v-on 的修饰符
  • on,off 和 $once 实例方法
  • 过滤
  • 内联模板 attribute
  • $destroy 实例方法。用户不应再手动管理单个 Vue 组件的生命周期。

脚手架

vite

  1. 一个基于浏览器原生 ES imports 的开发服务器。利用浏览器去解析<script module>,在服务器端按需编译返回,完全跳过了打包这个概念,服务器随起随用(冷启动快)
  2. 不仅有 Vue 文件支持,还搞定了热更新,而且热更新的速度不会随着模块增多而变慢(即时的模块热更新)。
  3. 针对生产环境则可以把同一份代码用 rollup 打包(按需编译)。

缺点: 支持转译ts文件,但不执行类型检查

<!--Vite App入口文件-->
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <link rel="icon" href="/favicon.ico" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Vite App</title>
</head>
<body>
  <div id="app"></div>
  <script type="module" src="/src/main.js"></script>
</body>
</html>

// main.js
import { createApp } from 'vue'
import App from './App.vue'
import './index.css'
createApp(App).mount('#app')
# 创建Vite App项目
npm init vite-app <project-name>
cd <project-name>
npm install
npm run dev

vue-cli
一个基于 Vue.js 进行快速开发的完整系统

# 安装
npm install -g @vue/cli

# 创建vue3项目
vue create <project-name>

# 选择`vue3 preview`
? Please pick a preset: (Use arrow keys)
❯ Default ([Vue 2] babel, eslint) 
  Default (Vue 3 Preview) ([Vue 3] babel, eslint) 
  Manually select features 

# 添加typescript插件
vue add typescript

Vue插件

Vue-router

安装

npm install vue-router@next --save

Vue3中的使用方式

// 1. Define route components.
const Home = { template: '<div>Home</div>' }
const About = { template: '<div>About</div>' }

// 2. Define some routes
const routes = [
  { path: '/', component: Home },
  { path: '/about', component: About },
]

// 3. Create the router instance and pass the `routes` option
const router = VueRouter.createRouter({
  // 4. Provide the history implementation to use. 
  history: VueRouter.createWebHashHistory(),
  routes,
})

// 5. Create and mount the root instance.
const app = Vue.createApp({})
app.use(router)
app.mount('#app')

Vuex

安装

npm install vuex@next --save

vue3中的使用方式

import { createApp } from 'vue'
import { createStore } from 'vuex'

const app = createApp({ ... })
const store = createStore({ ... })

app.use(store)
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,185评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,445评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,684评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,564评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,681评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,874评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,025评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,761评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,217评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,545评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,694评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,351评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,988评论 3 315
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,778评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,007评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,427评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,580评论 2 349

推荐阅读更多精彩内容