# Vue3

# api风格

# 选项式

<template></template>
<script>
export default {
	data(){
		return{
		}
    },
    created(){
    
    },
    methods:{
    
    }
}
</script>
<style></style>

# 组合式

<template></template>
<script setup>
import{ ref,onMounted } from 'vue'
const count = ref(0)
function addCount(){
    count.value++
}
    onMounted(()=>{
    
    })
</script>

# 模板语法(new)

# 动态参数

<a @[eventName]='dosomething'></a>
// 等价于
<a @click="dosomething" />
// script
eventName = 'click'
eventName = null // 显式移除绑定

// 参数语法限制
<!-- 这会触发一个编译器警告 -->
<a :['foo' + bar]="value"> ... </a>

// 阻止默认事件转而执行onSubmit()
<form @submit.prevent="onSubmit">...</form>

# 响应式基础

# ref()声明响应式状态

<script>
    import { ref } from 'vue'
const count = ref(0)
count.value = 100
console.log(count.value) //100
</script>
<template>
    <div>
        {{count}} // 不用count.value
    <div/>
</template>

使用带有.value的 ref 可以使vue检测到ref什么时候被访问或修改

# 深层响应性

const obj = ref({
	name:'songth1ef',
	body:{
		height:178,
		weight:62
	}
})
obj.value.body.height = 176
// 这里也能被检测到

# DOM更新时机

import { nextTick } from 'vue'

async function changeName(na: string) {
  name.value = na
  console.log('值已更新' + name.value)
  name.value = na + ' 先生' //多次修改
  await nextTick(() => {
    console.log('dom已更新')
      // nextTick 更新周期 中 缓冲所有状态的修改
      // 不管多少次状态更改 每个组件只被更新一次
  })
  console.log('dom已更新')
}

# ref()类型标注


import { ref } from 'vue'
import type { Ref } from 'vue'

const name: Ref<string | number> = ref('songth1ef')

const height = ref<string | number>(178)
console.log(height.value) // 178

const weight = ref<string | number>()
console.log(weight.value) // undefined

# reactive()

// 主要用于创建对象类
import { reactive } from 'vue'

const state = reactive({count:0})

# 标注类型


import { reactive } from 'vue'
interface Book {
  title: string
  year: number
}
const book: Book = reactive({ title: '<song of th1ef>', year: 2024 })
const raw = {}
const proxy = reactive(raw)

// 代理对象和原始对象不是全等的
console.log(proxy === raw) // false
// 在同一个对象上调用 reactive() 会返回相同的代理
console.log(reactive(raw) === proxy) // true

// 在一个代理上调用 reactive() 会返回它自己
console.log(reactive(proxy) === proxy) // true

# 局限性

只能用于对象,数组(Map、Set)

不能用于 string、number、boolean 这样的原始类型

不能替换整个对象

let state = reactive({ count: 0 })

// 上面的 ({ count: 0 }) 引用将不再被追踪
// (响应性连接已丢失!)
state = reactive({ count: 1 })

对解构操作不友好

const state = reactive({count:0})
// 当解构时,count已经与state.count断开连接
let { count } = state
// 不会影响原始的 state
count ++

# 计算属性


import { reactive, computed } from 'vue'
const human = reactive({
  name: 'songth1ef',
  body: {
    height: 178,
    weight: 62
  }
})

const fatOfSlim = computed(() => {
  return human.body.weight > 100 ? true : false
})

计算属性的值不能直接修改

可写计算属性


import { ref, computed } from 'vue'
import type { Ref } from 'vue'
const name = ref<string>('songth1ef')
const age: Ref<number> = ref(18)
const info = computed({
  get() {
    return name.value + ' is ' + age.value + ' years old ***'
  },
  set(newValue: string) {
    const [newName, newAge] = newValue.split(' ')
    name.value = newName
    age.value = parseInt(newAge)
  }
})
// info.value = ''
console.log(info.value)
info.value = 'soullucklyman 23'
console.log(info.value)

getter 应该专注于根据其他响应式状态的值计算出一个新的值,并将其返回。不应该在 getter 中进行诸如改变其他状态、发起异步请求或者修改 DOM 的操作

# 类与样式

# 绑定对象


import { reactive } from 'vue'
const fontSetting = reactive({
  'font-light': true
})
    <div :class="fontSetting">description *** 666</div>

# 绑定computed


const lightStatus = ref<boolean>(true)
const fontSetting = computed(() => ({
  'font-light': lightStatus.value
}))


    <div :class="fontSetting">description *** 666</div>

# 绑定 [ ] 数组


const classLight = ref('font-light')
    <div :class="[classLight]">666666</div>


// errorClass 会一直存在
<div :class="[isActive ? activeClass : '', errorClass]"></div>
<div :class="[{ active: isActive }, errorClass]"></div>

# 组件上使用

<!-- 子组件模板 -->
<p class="foo bar">Hi!</p>

<!-- 在使用组件时 -->
<MyComponent class="baz boo" />
    渲染出的 HTML 为:
    <p class="foo bar baz boo">Hi!</p>

如果你的组件有多个根元素,你将需要指定哪个根元素来接收这个 class。你可以通过组件的 $attrs 属性来指定接收的元素:

template
<!-- MyComponent 模板使用 $attrs 时 -->
<p :class="$attrs.class">Hi!</p>
<span>This is a child component</span>
template
<MyComponent class="baz" />

# 条件渲染

不建议同时使用 v-if 和 v-for

<template v-for="todo in todos">
  <li v-if="!todo.isComplete">
    {{ todo.name }}
  </li>
</template>

# 事件处理

<!-- 使用特殊的 $event 变量 -->
<button @click="warn('Form cannot be submitted yet.', $event)">
  Submit
</button>

<!-- 使用内联箭头函数 -->
<button @click="(event) => warn('Form cannot be submitted yet.', event)">
  Submit
</button>
js
function warn(message, event) {
  // 这里可以访问原生事件
  if (event) {
    event.preventDefault()
  }
  alert(message)
}
// 有时我们需要在内联事件处理器中访问原生 DOM 事件。你可以向该处理器方法传入一个特殊的 $event 变量,或者使用内联箭头函数:
function handOne(message: any, event: any) {
  console.log(message, event)
}
function handTwo(message: any, event: any) {
  console.log(message, event)
}
function handThree(event: any) {
  console.log(event)
}
      <button @click="handOne('something', $event)">don`t click !</button>
      <button @click="(event) => handTwo('message', event)">don`t click two!</button>
      <button @click="(event) => handThree(event)">don`t click three!</button>

# 事件修饰符

<!-- 单击事件将停止传递 -->
<a @click.stop="doThis"></a>

<!-- 提交事件将不再重新加载页面 -->
<form @submit.prevent="onSubmit"></form>

<!-- 修饰语可以使用链式书写 -->
<a @click.stop.prevent="doThat"></a>

<!-- 也可以只有修饰符 -->
<form @submit.prevent></form>

<!-- 仅当 event.target 是元素本身时才会触发事件处理器 -->
<!-- 例如:事件处理器不来自子元素 -->
<div @click.self="doThat">...</div>

# 修饰符

# .lazy

默认情况下,v-model 会在每次 input 事件后更新数据 (IME 拼字阶段的状态 (opens new window)例外)。你可以添加 lazy 修饰符来改为在每次 change 事件后更新数据:

# .number

<input v-model.lazy="msg" />

如果你想让用户输入自动转换为数字,你可以在 v-model 后添加 .number 修饰符来管理输入:

如果该值无法被 parseFloat() 处理,那么将返回原始值。

<input v-model.number="age" />

number 修饰符会在输入框有 type="number" 时自动启用。

# .trim

如果你想要默认自动去除用户输入内容中两端的空格,你可以在 v-model 后添加 .trim 修饰符:

<input v-model.trim="msg" />

# 生命周期

# 注册周期钩子


import { onMounted } from 'vue'
onMounted(() => {
  console.log('prosess is onMouted')
})

setTimeout(() => {
  onMounted(() => {
    // 异步注册时当前组件实例已丢失
    // 这将不会正常工作
  })
}, 100)

# onBeforeMount

onBeforeMount(()=>{
	// 还没创建DOM节点,即将执行首次DOM渲染
})
// 初始化数据、设置定时器或监听器

# onMounted

onMounted(()=>{
	// 在组件挂载完成之后执行
})
// 其所有同步子组件都已经被挂载(不包含异步组件和 suspense树内的组件)
适合的操作:数据初始化、DOM操作(事件监听,修改样式)、异步请求、执行动画、订阅事件

# onBeforeUpdate

onBeforeUpdate(()=>{
	// 数据更新但是重新渲染之前
})
// 适合访问props和state,进行一些预处理操作,根据新的props值更新组件内部状态

# onUpdated

onUpdated(()=>{
	// 组件更新完毕之后被调用
})
// 父组件的更新钩子将在其子组件的更新钩子之后调用

# onBeforeUnmount

onBeforeUnmount(()=>{
	// 组件卸载之前执行 清理操作
})
// 清除定时器、取消事件监听器、清理临时数据

# onUnmounted

onUnmounted(()=>{
	// 组件卸载之后、清理操作,取消订阅
})
// 清除相关的引用、资源释放、取消异步

# 监听器watch


watch(lightIndex, async (newV, oldV) => {
  if (newV) {
    console.log(newV, oldV)
  }
})
// 多个来源组成的数组
watch([x, () => y.value], ([newX, newY], [oldX, oldY]) => {
  console.log(`x is ${newX} (was ${oldX}) and y is ${newY} (was ${oldY})`);
});

对象属性监听


const human = ref({
  name: 'songth1ef',
  age: 18,
  body: {
    height: 178,
    weight: 62
  }
})
watch(
  () => human.value.body.height,
  (newHeight, oldHeight) => {
    console.log(newHeight, oldHeight)
  }
)

# 深层监听


const human = reactive({
  name: 'songth1ef',
  age: 18,
  body: {
    height: 178,
    weight: 62
  }
})


watch(human, (newV, oldV) => {
  console.log(newV.body.height, oldV.body.height)
  // newV和oldV式相等的 因为他们是同一个对象
})

# 监听对象


watch(human, (newV, oldV) => {
  console.log(newV.body.height, oldV.body.height)
  // newV和oldV式相等的 因为他们是同一个对象
})

# deep选项


watch(
   // human.body.height++ 不会触发
  () => human.body,  // 只有当human.body被替换才会触发
  (newV, oldV) => {
    console.log(newV, oldV)
  }
)

watch(
  // human.body.height++ 会触发
  () => human.body, 
  (newV, oldV) => {
    console.log(newV, oldV)
  },
  { deep: true }
)

# immediate

watch( 
  () => human.body,  
  (newV, oldV) => {
    console.log(newV, oldV)
  },
  { deep: true,
   immediate: true // 	立即执行
  }
)

# once

watch(
  () => human.body,
  (newV, oldV) => {
    console.log(newV, oldV)
  },
  { deep: true, once: true } // 只触发一次
)

# watchEffect


import { watchEffect } from 'vue'
const todoId = ref(99)
const data = ref(null)
watchEffect(async () => {
  const response = await fetch(`https://jsonplaceholder.typicode.com/todos/${todoId.value}`)
  data.value = await response.json()
  console.log(data.value)
})
import { watchEffect, watch, ref } from 'vue';

const data = ref(0);

// watchEffect 等同于 watch(() => {}, { immediate: true })
watchEffect(() => {
  console.log('data changed:', data.value);
});

// 相当于 Vue 2.x 中的 watch,immediate: true 表示立即执行回调函数
watch(data, (newValue, oldValue) => {
  console.log('data changed:', newValue);
}, { immediate: true });

// 修改 data 的值,触发 watchEffect 和 watch 回调函数
data.value = 1;

# flush: 'post'

// 使用 flush: 'post' 选项确保在 DOM 更新后执行回调函数

watch(source, callback, {
  flush: 'post'
})

watchEffect(callback, {
  flush: 'post'
})

// 后置刷新的 watchEffect() 有个更方便的别名 watchPostEffect():
import { watchPostEffect } from 'vue'

watchPostEffect(() => {
  /* 在 Vue 更新后执行 */
})

# flush: 'sync'

// 可以将侦听器设置为同步触发,即在数据发生变化时立即执行侦听器,而不是等待下一次 DOM 更新。
watch(source, callback, {
  flush: 'sync'
})

watchEffect(callback, {
  flush: 'sync'
})
// 同步触发的 watchEffect() 有个更方便的别名 watchSyncEffect()
import { watchSyncEffect } from 'vue'

watchSyncEffect(() => {
  /* 在响应式数据变化时同步执行 */
})

# 停止监听

<script setup>
import { watchEffect } from 'vue'

// 它会自动停止
watchEffect(() => {})

// ...这个则不会!
setTimeout(() => {
  watchEffect(() => {})
}, 100)
</script>
要手动停止一个侦听器,请调用 watch 或 watchEffect 返回的函数:
 
const unwatch = watchEffect(() => {})

// ...当该侦听器不再需要时
unwatch()

# 模板引用

import { ref,onMounted } from 'vue'
const inputRef = ref(null)
onMounted(()=>{
    inputRef.value.focus()
})

<input ref="inputRef" />
// 只有在 组件挂载之后才可以访问
  
onBeforeMount(() => {
  console.log(inputHTML.value) //  null
})
onMounted(() => {
  console.log(inputHTML.value) // <html/>
}
// 监听一个模板要考虑null的情况
watchEffact(()=>{
	if(inputHTML){
  
	}
})  
// ref绑定一个函数
// 每次组件更新会被调用 绑定的元素被卸载时 函数也会被调用一次
      <input
        :ref="
          (el) => {
            console.log(el)
          }
        "
      />
// 模板引用也可以用在子组件上
import Child from './Child.vue'  
onMounted(() => {
  // child.value 是 <Child /> 组件的实例
})
  <Child ref="child" />

# defineExpose

有一个例外的情况,使用了 的组件是**默认私有**的:一个父组件无法访问到一个使用了 的子组件中的任何东西,除非子组件在其中通过 defineExpose 宏显式暴露

// 子组件
import { ref, defineExpose } from 'vue'
const age = ref(18)
// 像 defineExpose 这样的编译器宏不需要导入
defineExpose({
  age
})
// 父组件
const son = ref(null)
onMounted(() => {
  console.log(son.value.age)
})
    <Info ref="son"></Info>

# 组件基础

# 传递props (父传子)

# defineProps

//父组件
    <Info ref="son" :name="name" :userInfo="userInfo"></Info>
  
// 子组件
const propsData = defineProps({
  name: String,
  userInfo: Object
})
// 或者 默认接收所有类型的值
const propsData = defineProps(['name', 'userInfo']) 
console.log(propsData.name)

# $emit (子调用父方法)

# defineEmits

// 父组件

    <Info @addCount="addCount"></Info>
// 子组件
用defineEmits 注册事件
const emits = defineEmits(['addCount'])
function countPlus() {
  emits('addCount'12123)
}

# 动态组件

 // <component>标签和 is属性 实现
components: {
	top,
	center,
	bottom
},

lightComponentName='top'

<component :is='lightComponentName' ></component >

# 组件注册

# 全局注册

import { createApp } from 'vue'
const app = createApp
app.component(
    'Mycomponent', // 组件的名字
    {} // 组件的实现
)
// 使用
import Card from './components/Card/index.vue'
createApp(App).component('Card',Card).mount('#app') 

# 局部注册

<div>
	<Info></Info>
</div>

<script setup>
import Info from './Info.vue'
</script>

# 深入组件

# props

# 单向数据流

子组件的 props 是只读的 在子组件不能更改props数据

可以通过 $emit 修改

所有props是可选的 除非声明了required:true

# defineModel

// 父组件
    <Info v-model="name" >
// 子组件
    
const fatherName = defineModel()
console.log(fatherName.value)// 等于父组件的name
// defineModle 返回的值是一个ref 所以要.value

      <input type="text" v-model="fatherName.name" />
// 这个值 在子组件被改变 父组件会同步
// 使 v-model 必填
const model = defineModel({ required: true })

// 提供一个默认值
const model = defineModel({ default: 0 })

# defineModle的参数

// 父组件
<MyComponent v-model:title="bookTitle" />

//子组件
<script setup>
const title = defineModel('title')
</script>

<template>
  <input type="text" v-model="title" />
</template>

const title = defineModel('title', { required: true })

# 多个v-modle的绑定

<UserName
  v-model:first-name="first"
  v-model:last-name="last"
/>
<script setup>
const firstName = defineModel('firstName')
const lastName = defineModel('lastName')
</script>

<template>
  <input type="text" v-model="firstName" />
  <input type="text" v-model="lastName" />
</template>

# 处理v-model修饰符

//例如.trim, .number 和 .lazy
//创建一个自定义的修饰符
<MyComponent v-model.capitalize="myText" />
    //子组件
    <script setup>
const [model, modifiers] = defineModel()

console.log(modifiers) // { capitalize: true }
</script>

<template>
  <input type="text" v-model="model" />
</template>

# 透传Attributes

"透传 attribute" 指的是将传递给一个组件的属性(attribute),但这些属性在组件中并没有被显式声明为 props 或 emits,也没有使用 v-on 事件监听器进行监听。这些属性会直接传递给组件的根元素或子元素,而不需要在组件中显式声明。

# class,style继承

//父组件的 属性 在渲染出DOM结果会渲染到子组件的最外层
// 父
<component class="flexC"></component>
// 子
<template>
	<button class="button-submit"></button>
</template>
//DOM渲染之后
	<button class="button-submit flexC"></button>

# v-on监听器继承

<MyButton @click="onClick" /> //也适用继承

# 深层组件透传

如果一个组件在其根节点上渲染另一个组件,那么父组件传递给子组件的属性(透传 attribute)会继续传递给根节点上的子组件。

透传的属性不包括在 组件中已经声明为 props 的属性,以及针对 emits 声明的事件所绑定的 v-on 侦听函数。换句话说,如果在 组件中声明了一个属性或者绑定了一个事件监听器,那么这些属性和事件监听器不会被透传给 `` 组件。

# 禁用透传

// 子组件
<script setup>
defineOptions({
  inheritAttrs: false
})
// ...setup 逻辑
</script>

// 这些透传进来的 attribute 可以在模板的表达式中直接用 $attrs 访问到。
<span>Fallthrough attribute: {{ $attrs }}</span>
//可以不用在根元素上
<div class="btn-wrapper">
  <button class="btn" v-bind="$attrs">click me</button>
</div>

# 多根节点透传

如果 $attrs 被显式绑定,则不会有警告:

<header>...</header>
<main v-bind="$attrs">...</main>
<footer>...</footer>

Js中访问Attributes

<script setup>
import { useAttrs } from 'vue'

const attrs = useAttrs()
</script>

# 插槽slot

# 渲染作用域

// 插槽可以访问父组件作用域
<span>{{ message }}</span>
<FancyButton>{{ message }}</FancyButton>

# 默认内容

//外部没提供内容的时候 可以默认定义内容
<button type="submit">
  <slot>
    Submit <!-- 默认内容 -->
  </slot>
</button>
// 如果我提供了slot内容, 默认内容会被 提供的替代
<SubmitButton>Save</SubmitButton>
    =>
<button type="submit">Save</button>

# 具名插槽

// 子组件
<div class="container">
  <header>
    <slot name="header"></slot>
  </header>
  <main>
    <slot></slot>
  </main>
  <footer>
    <slot name="footer"></slot>
  </footer>
</div>
// 父组件
<BaseLayout>
  <template v-slot:header>
    <!-- header 插槽的内容放这里 -->
  </template>
</BaseLayout>

// 也可以采用缩写 
  <template #header>
    <h1>Here might be a page title</h1>
  </template>

# 条件插槽

你可以结合使用 $slots (opens new window) 属性与 v-if (opens new window) 来实现。

<template>
  <div class="card">
    <div v-if="$slots.header" class="card-header">
      <slot name="header" />
    </div>
  
    <div class="card-content">
      <slot />
    </div>
  
    <div v-if="$slots.footer" class="card-footer">
      <slot name="footer" />
    </div>
  </div>
</template>

# 动态插槽名

<base-layout>
  <template v-slot:[dynamicSlotName]>
    ...
  </template>

  <!-- 缩写为 -->
  <template #[dynamicSlotName]>
    ...
  </template>
</base-layout>

# 作用域插槽

// 子组件 向 父组件 传递props
//	子
<div>
	<slot :status='InnerStatus'>
	</slot>
</div>
//	父
<son v-slot='slotProps'>
	{{slotProps.status}}
</son>

# 依赖注入(孙子组件传值)

props逐级传递太麻烦

provide 和 inject 可以帮助解决这一问题

# provide (提供)

import { provide } from 'vue'
 
    provide(/* 注入名 */ 'message', /* 值 */ 'hello!') 

# inject (注入)

 
import { inject } from 'vue'

const message = inject('message') 

# 异步组件

# defineAsyncComponent

import { defineAsyncComponent } from 'vue'
const AsyncComp = defineAsyncComponent(()=>{
    return new Promise((resolve(),reject)=>{
   	 	// ...从服务器获取组件
    	resolve(/* 获取到的组件 */)
	})
})
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 时间限制,并超时了
  // 也会显示这里配置的报错组件,默认值是:Infinity
  timeout: 3000
})

# 逻辑复用

// mouse.js
import { ref, onMounted, onUnmounted } from 'vue'

// 按照惯例,组合式函数名以“use”开头
export function useMouse() {
  // 被组合式函数封装和管理的状态
  const x = ref(0)
  const y = ref(0)

  // 组合式函数可以随时更改其状态。
  function update(event) {
    x.value = event.pageX
    y.value = event.pageY
  }

  // 一个组合式函数也可以挂靠在所属组件的生命周期上
  // 来启动和卸载副作用
  onMounted(() => window.addEventListener('mousemove', update))
  onUnmounted(() => window.removeEventListener('mousemove', update))

  // 通过返回值暴露所管理的状态
  return { x, y }
}
<script setup>
import { useMouse } from './mouse.js'

const { x, y } = useMouse()
</script>

<template>Mouse position is at: {{ x }}, {{ y }}</template>

# 自定义指令

<script setup>
// 在模板中启用 v-focus
const vFocus = {
  mounted: (el) => el.focus()
}
</script>

<template>
  <input v-focus />
</template>

在 `` 中,任何以 v 开头的驼峰式命名的变量都可以被用作一个自定义指令。在上面的例子中,vFocus 即可以在模板中以 v-focus 的形式使用。

在没有使用 `` 的情况下,自定义指令需要通过 directives 选项注册:

export default {
  setup() {
    /*...*/
  },
  directives: {
    // 在模板中启用 v-focus
    focus: {
      /* ... */
    }
  }
}

将一个自定义指令全局注册到应用层级也是一种常见的做法:

const app = createApp({})

// 使 v-focus 在所有组件中都可用
app.directive('focus', {
  /* ... */
})

一个指令的定义对象可以提供几种钩子函数 (都是可选的):

const myDirective = {
  // 在绑定元素的 attribute 前
  // 或事件监听器应用前调用
  created(el, binding, vnode, prevVnode) {
    // 下面会介绍各个参数的细节
  },
  // 在元素被插入到 DOM 前调用
  beforeMount(el, binding, vnode, prevVnode) {},
  // 在绑定元素的父组件
  // 及他自己的所有子节点都挂载完成后调用
  mounted(el, binding, vnode, prevVnode) {},
  // 绑定元素的父组件更新前调用
  beforeUpdate(el, binding, vnode, prevVnode) {},
  // 在绑定元素的父组件
  // 及他自己的所有子节点都更新后调用
  updated(el, binding, vnode, prevVnode) {},
  // 绑定元素的父组件卸载前调用
  beforeUnmount(el, binding, vnode, prevVnode) {},
  // 绑定元素的父组件卸载后调用
  unmounted(el, binding, vnode, prevVnode) {}
}

# jest

typescript

yarn add --dev @vue/test-utils @types/jest jest ts-jest vue-jest

// tsconfig.json
{
  "compilerOptions": {
    "types": ["jest", "node"],
    // 其他配置...
  }
}
npm install --save-dev @babel/core @babel/preset-env

# Vue3.4源码解析 (opens new window)

# 1vue设计理念 设计思想 开发环境

# 声明式框架

(v-if v-for)内部原理不用管 ,也可以编写命令式代码实现功能

命令式框架for(var i ) 直接编写源代码

渐进式框架

# 采用虚拟DOM

# 区分编译时 和 运行时

# 设计理念

拆分模块

按需引入 组合式api

允许自定义渲染器 (默认DOM渲染)扩展更方便

使用RFC确保方案

# Monorepo管理项目

# Vue3组成

compiler-dom 编译时

runtime-dom 运行时

reactivity 响应式系统

# 2

npm i pnpm -g	
pnpm init
// package.json
  "private": true,
// pnpm-workspace.yaml
packages:
  - "packages/*"
pnpm install vue
pnpm install typescript esbuild minimist -D -w
npx tsc --init

# 4

vue3响应式数据核心

# CompositionAPI

# 编写复杂业务逻辑不会出现反复横跳问题
# 不存在this指向不明确问题
# 对tree-shaking友好 代码更容易压缩
# 提取公共逻辑方便