前端实习准备 — Vue基础相关

108 阅读11分钟

单文件组件

<script setup>
</script>

<template>
</template>

<style scoped>
</style>

API

1. 选项式API(this指向当前组件的实例)
<script>
export default {
  // data() 响应式状态属性
  data() {
    return {
      count: 0
    }
  },
  // methods 函数
  methods: {
    increment() {
      this.count++
    }
  },
  // mounted()生命周期钩子,在组件生命周期的各个不同阶段被调用
  mounted() {
    console.log(``)
  }
}
</script>

2. 组合式API
<script setup> //setup不要漏,其作用就是暴露响应式状态和函数
import {ref,onMounted} from 'vue'

const count = ref(0)
function increment(){
    count.value++
}
onMounted(()=>{
    console.log("")
})
</script>

构建Vue

1. 安装脚手架
npm create vue@latest

2. 创建vue项目
npm install

3. 运行vue项目
npm run dev

4. 发布vue项目
npm run bulid

创建vue应用

<script>
// 创建应用(根组件)
import {createApp} from 'vue'
const app = createApp({}) // 可以创建多个,每个独享全局资源作用域

// app.mount(挂载应用-->渲染)
app.mount('#app')
    
// app.config(配置)
app.config.errorHandler = (err) = >{}

// app.component(注册组件)
app.component('name',component)
</script>

<template>
<div id="app"></div>
</template>

模板语法 {{}} v-xxx

1. 文本插值
<span>{{ msg }}</span>

2. 响应式绑定(将属性的值变为响应式的)
<div v-bind:id="name" ></div>
<div :id="name"></div>
<div v-bind="obj" ></div> // 绑定多个属性

const name = ref('muggle')
const obj =  {
  id:'muggle',
  class:'student',
  style:''
}

3. 模板内支持的语句
支持JS、单一表达式、函数调用、有限全局对象、
{{}}
v-xxx:xxx

4. 动态参数
// arrtributeName同样支持上述所有,但必须是字符串
v-bind:[attributeName]="value"

5. 修饰符
为.开头的特殊后缀,表明指定需要以特殊的方式绑定
.prevent会告知v-on触发时调用event.preventDefault
<form @submit.prevent="onSubmit"></form>

响应式 ref reactive

(一) ref
1. 基本语法
<script setup>
    import {ref} from 'vue'
    // ref返回一个ref对象,对象中有value属性,就是其值
    const count = ref(0)
    function add(){
        count.value++
    }
</script>

<tamplate>
    <button @click="add()">
         {{count}} // 模板中使用ref不需加.value       
    </button>
</tamplate>

2. ref伪代码
<script>
    // 其内部的get set实际执行了JS的getter setter方法
    const myRef = {
        _value:0,
        get value(){
           track()
           return this._value
        },
        set value(newValue){
            this.value=newValue
            trigger()
        }
    }
</script>

3. ref支持JS中任何类型的值
且具有深层响应性,即嵌套数组里的值改变了也能检测到
<script>
    const obj = ref({
        name:"muggle",
        hobby:['paino','camera']
        firend:{a:'A',b:'B'}
    })
    function person(){
        obj.value.hobby.push('piture')
    }
</script>

(二)reactive
返回响应式对象proxy
局限性:仅支持对象类型,不能替换对象,不能解构
<script>
    import {reactive} from 'vue'
    const person = {
        name:"muggle",
        hobby:['paino','camera']
        firend:{a:'A',b:'B'
    }
    // proxy != person 
    const proxy = reactive(person)
    // 对同一个对象调用reactive(),返回相同的代理
    reactive(person) === proxy  ture
    // 对同一个代理调用reactive(),返回其本身
    reactive(proxy) === proxy  ture
</script>
<template>
   <button>
       {{proxy.name}}
   </button>
</template>

(三)ref和reactive的区别
1. ref可用于所有数据类型,reactive只能用于对象类(Object,Array,Map,Set)
2. ref返回一个包含.value的对象,访问时需要使用.value,模板使用时不需要;reactive直接返回一个响应式对象,访问和模板使用时为proxy.name模式
3. ref对于复杂对象需要手动解包,reactive深度响应不需要

(四)DOM更新时机
当修改了响应式状态,DOM会自动更新,但DOM更新不是同步的,Vue会在next tick更新周期中缓冲所有状态,确保组件只更新一次。如果需要等待DOM更新完成后才能执行代码,可以使用nextTick()全局API
<script>
    import {nextTick} from 'vue'
    async function add(){
        count.value++
        await nextTick()
    }
</script>

计算属性 computed()

1. 基本语法
<script setup>
    import {ref,computed} from 'vue'
    const person = reactive({
        name:'muggle',
        hobby:['piano','camera','piture']
    })
    // 接收getter函数,返回ref对象,因此script中记得value,模板不用
    const hobbyCount = computed(()=>{
        return hobby.length //hobbyCount.value = hobby.length
    })
</script>

<template>
   <span>{{hobbyCount}}</span>
</template>

2. computed()和普通函数的区别
computed()基于其响应式依赖被缓存,只有响应式依赖更新时才会重新计算重新调用getter();而函数会重复执行很多次

3. 可写计算属性(避免修改)
计算属性默认只读,如果想要修改,可以通过创建getter和setter实现
<script setup>
    import {ref,computed} from 'vue'
    const fistName = ref('muggle')
    const lastName = ref('wizard')
    const fullName = computed({
        get(){
            return firstNmae.value + ' ' + lastName.value
        },
        set(newValue){
            [firstName.value,lastName.value] = newValue.split(' ')
        }
    })
</script>

样式绑定 :class :style

(一) :class="{}"
1. 绑定对象
:class=""传递一个对象(键:值 = 类名 : true/flase)动态绑定class,键为CSS类名,值为true/false,决定该元素是否绑定这个类
<script setup>
    import{ref,reactive,computed} from 'vue'
    const isActive = ref(true)
    const hasError = ref(false)
    // 也可以直接设为一个对象,:class传入该对象
    const classObject = reactive({
        active:true,
        'text-danger':false // 键中有-类字符需用''
    })
    // 也可以计算属性返回一个对象,关键点就是:class传入的是对象(键+值=类名+true/false),此处computed返回的是ref对象
    const classObject = computed(()=>{
          active: isActive.value && !error.value,
          'text-danger': error.value && error.value.type === 'fatal'
    })
</script>

<template>
   // :class可以与原始属性class共存
   <div :class = "{active:isActive,'text-danger':hasError}"></div>
   <span :class = "classObject"></span>
</template>

2. 绑定数组
简单的为元素添加一组class
<script>
    // activeClass.value = 'active'
    const activeClass = ref('active')
    const errorClass = ref('text-danger')
</script>

// ['active','text-danger']
<div :class = "[activeClass,errorClass]"></div>

3. 组件上使用
自定义组件只有一个根元素,自动继承组件的class
<p class="foo bar"></p> // 子组件模板
<myComponent :Class="{active:isActive}"></myComponent>
则p实际为<p class="foo bar active"></p>

自定义组件有多个根元素,通过$attrs.class指定哪个根元素继承组件的class
<p :class="{$attrs.class}"></p>


(二) :style="{}" 内联样式
1. 绑定对象
<script>
    const color = ref('red')
    const fontSize = ref(30)
    const styleObject = reactive({
        color:'red',
        fontSize:'30px'
    })
</script>
<div :style="{color:color,fontSize:fontSize + 'px'}"></div>
<p :style="styleObject"></p>

2. 绑定数组
<div :style = "[baseStyle, unqieStyle]"></div>

条件渲染 v-if v-show

1. v-if v-else-if  v-else  返回真值时渲染
<h1 v-if="type === 'A'">A</h1>
<h1 v-else-if="type === 'B'">B</h1>
<h1 v-else>Not A B</h1>
// 如果想要切换不止一个元素,可用tempalte包裹,该元素渲染不可见
<template v-if="ok">
  <h1>A</h1>
  <h1>B</h1>
</template>

2. v-show
<h1 v-if="ok">A</h1>

3. v-if与v-show的区别
v-if是真正的按条件渲染,是惰性的,返回真值时才渲染,适用切换少
v-show始终渲染,只是改变CSS中的display属性,适用切换多
v-if可以用template,v-show不可

4. v-if不建议与v-for一块使用

列表渲染 v-for

1. in 操作符
(1) key in Object  
key字符串或Symbol,表示检查的键(obj的键为属性,arr的键为索引)
Object是对象或数组
in检查对象自身和原型链,Object.hasOwmProperty只检查自身
返回true/false

(2) key in obj
const obj = {name:'muggle', age:23}
console.log("name" in obj) // true

(3) key in arr
const arr = [3,4,5]
console.log(0 in arr) //true
console.log(5 in arr) //false


2. for
(1) forEach((value,key)=>{} 遍历arr,map,set的键和值,不能遍历对象
arr.forEach
map.forEach
set.forEach // 无键

(2)for(let key in obj){} 遍历对象的键(obj的属性,arr的索引)
for(let key in obj){
  console.log(key,obj.key)
}
for(let key in arr){
  console.log(key) // "0"  "1"  "3" // 返回的是索引的字符串
}

(3)for(let value of obj){} 遍历可迭代器的值(obj,arr,map,set,string)
for(let value of arr){
  console.log(value) // 3  4  5
}


3. v-for="item in items" 与forEach类似,of可以替代in
(1)遍历数组
const arr = ref([{message:'A'},{message:'B'}])
<li v-for="(value,key) in arr">{{value.message}} - {{key}}</li>
<li v-for="{message} in arr">{{message}}</li> //解构赋值

(2)遍历对象
const obj = reactive({name:'muggle',age:23})
<li v-for="(value,key,index) in obj">{{index}}. {{key}}:{{value}}</li>

(3)遍历整数值(1...n)
const count = 100
<span v-for="n in count">{{n}}</span> // 1-100

(4)template中使用,同时渲染多个模块
<template v-for='(value,key,index) in obj'>
  <li>{{index}}.{key}:{{value}}</li>
  <li>空一行</li>
</template>

(5)不能在用一个标签上用v-if和v-for,v-if的优先级比v-for高,类比一般先写for再写if
const obj=[{name:'muggle',age:23},{name:'wizar',age:25}]
<template v-for="value in obj">
   <li v-if="value.age===23">{{value.name}}</li>
</template>

(6)v-for中内置的特殊属性key
应为每一个v-for标签绑定一个key,key的值只能是数字或字符串,不能是对象,绑定key的原因是,v-for渲染策略是就地更新,如果数据项的顺序变化,vue不会随之移动Dom元素的顺序,而添加key会重新排序
<template v-for="value in obj" :key='value.name'>
   <li>{{value.name}}</li>
</template>

(7)自定义组件上使用v-for
// 和普通标签一样,但是不会传递任何数据给组件,因为组件有自己独立的作用域,所以我们需要传递props!
<myComponent v-for="value in obj" :key="value.name"></myComponent>

(8)v-for中数组方法的注意事项
改变自身:push pop shift unshift splice sort reverse
不改变自身,返回新数组:filter slice concat

改变自身的会响应式更新,不改变自身的可以利用computed存储新数组
const arr=ref([1,2,3,4,5])
const filterArr = computed(()=>{
   return arr.value.filter((n)=>n%2)
})
<li v-for="value in filterArr">{{value}}</li>

若,有多层嵌套数组,可以通过嵌套v-for来拆解出数组的值
const set = ref([[1,2,3,4,5],[6,7,8,9,10]])
<ul v-for="arr in set">
    <li v-for="value in arr">{{value}}</li>
</ul>

事件处理 v-on(@)

v-on:click = "handler"  @click = "handler"
1. 内联事件处理器 count++  add()
const count = ref(0)
<button @click="count++"></button>

//在内联事件处理器中调用方法,可以传参替换原生event
function say(msg){
  alert(msg)
}
<button @click="say('hello')"></button>

//在内联事件处理器中使用event
function say(msg,event){
  alert(msg)
  console.log(event.target.name)
}
<button @click="say('hello',$event)"></button>


2. 方法事件处理器 add
// 自动接收原生DOM事件event
const count = ref(0)
function add(event){
   count.value++
   event.preventDefault()
}
<button @click="add"></button>


3. 事件修饰符
常用事件:click  keypress  change  mouseover  submit
.stop  --  event.stopPropagation  --  阻止事件传播(停留在子元素)
.prevent  --  event.preventDefault  --  阻止默认行为(阻止浏览器跳转/页面刷新)
.capture  --  addEventListener-option-capture  --  true捕获触发,false冒泡触发+默认
.once  --  addEventListener-option-once  --  true触发一次后移除
.passive  --  addEventListener-option-passive  --  true禁止调用preventDefault()
.self  --  非原生JS  --  仅事件元素本身触发
<a @click.stop.prevent='doThar'></a> // 注意顺序
@click.prevent.self 阻止元素及其子元素的默认行为
@click.self.prevent 阻止对元素本身的默认行为


4. 按键修饰符
常用键盘事件:keydown keyup keypress
.enter  .tap  .delete(捕获Delete和Backspace)  .esc  .space  .up  .down  .left  .right
.ctrl  .alt  .shift  .meta
.exact(未额外按下其他按键才触发)


5. 鼠标修饰符
.left .right .middle

表单输入绑定 v-model

1. 表单元素
<input type = "text,password,email,number,checkbox,radio,file"/>
<textarea></textarea>
<button></button>
<lable></lable>
<select>
   <option></option>
</select>

2. 基本用法 v-model绑定input标签的value并传入event事件处理
// 文本
const msg = ref('')
<input v-model="msg">
<span>{{msg}}</span>

// 复选框
const checked = ref(false)
<input type="checkbox" id="check" v-model="checked">
<label for="check">{{checked}}</label>

// 多个复选框,将其值绑定到同一个数组/集合
const names = ref([])
<input type="checkbox" id="A" value="abby" v-model="names">
<label for="A">abby</label>
<input type="checkbox" id="B" value="bubby" v-model="names">
<label for="B">bubby</label>

// 单选框
const pick = ref('One')
<input type="radio" id="A" value="One" v-model="pick">
<label for="A">One</label>
<input type="radio" id="B" value="Two" v-model="pick">
<label for="B">Two</label>

// 下拉列表
const selected = ref('')
<select v-model="selected">
    <option disable value="">设置空值禁用选项</option> //IOS问题
    <option>A</option> // 没有指定value时,option标签内的文本就是value
    <option>B</option>
    <option>C</option>
</select>
<span>{{selected}}</span>

// 下拉列表的option使用v-for
const selected = ref('muggle')
const options = ref([{name:'muggle',age:23},{name:'wizar',age:25}])

<select v-model="selected">
    <option v-for="option in options" :value="option.age">Name: {{option.name}}</option>
</select>
<span>Age: {{selected}}</span>

// v-model="" 和:value="" 都支持非字符串类型的值绑定,比如对象数组

3. 修饰符
.lazy  v-model会在每次input事件后更新数据,.lazy改为在change事件后更新
.number  输入自动转化为数字
.trim  自动去除输入两端的空格
<input v-model.lazy="msg" />

模板引用 ref

ref属性用于标记DOM元素和组件实例,可以通过this.$refs访问所有被标记的DOM元素和组件实例,this.$refs是一个对象。
1. 选项式api
<button ref="myButton"></button>
<myComponent ref="myComponent"></myComponent>
this.$ref.myButton.focus() // 获取DOM元素并执行操作
this.$ref.myComponent.someMethod()  // 获取组件并调用其方法

2. 组合式api
<script>
    import {useTemplateRed,onMounted} from 'vue'
    const btn = useTemplateRed('myButton')
    // 必须在组件挂载之后才能使用
    onMounted(()=>{
        btn.focus()
    })
</script>
<button ref="myButton"></button>

3. v-for中使用ref
<script>
    import {ref,useTemplateRef,onMounted} from 'vue'
    const list = [1,2,3]
    const lis = useTemplateRef('lis')
    onMounted(()=>{
        cosole.log(lis.textContent) // [1,2,3]
    })
</script>
<ul>
    <li v-for="value in list" ref="lis">{{value}}</li>
</ul>

生命周期钩子 onMounted() onUpdated() onUnmounted()

每个vue组件实例在创建时,都经历一系列的初始化步骤:设置数据侦听、编译模板、挂在实例到DOM、数据改变时更新DOM。在执行这些步骤的时候会运行生命周期钩子函数。所有钩子都不能异步注册,应与组件实例同步注册,例如放在setup下。调用钩子时,其回调函数会自动注册到当前正在初始化的组件实例上。

1. onMounted() 
组件挂载完成后执行:所有同步子组件已被挂载(不含异步),自身DOM树构建完成并插入父容器,不会在服务端渲染期间执行。
<script>
    import {ref,onMounted} from 'vue'
    const myButton = ref()
    onMounted(()=>{
        myButton.value // <button></button> 证明DOM树已构建
    })
</script>
<button ref="myButton"></button>

2. onUpdated()
响应式状态变更而更新DOM树后调用:组件内任意DOM更新后调用,不会在服务端渲染期间执行。如果想要特定状态导致的DOM更新,需要使用nextTick(),Vue在nextTick更新周期中缓冲所有状态,确保组件只更新一次。不能在 updated 钩子中更改组件的状态,这可能会导致无限的更新循环。
<script>
    import {reft,onUpdated} from 'vue'
    const count=ref(0)
    onUpdated(()=>{
        console.log(document.getElementById("myButton").textContent)  // count.value DOM树更新完毕
    })
</script>
<button id="myButton" @click="count++">{{count}}</button>

3. onUnmounted()
组件实例被卸载之后调用:所有子组件已卸载,所有响应式作用(渲染、计算属性、侦听器)都停止,通常用于清理一些副作用,例如定时器,事件监听器,与服务器的连接等。不会在服务端渲染期间执行。
<script>
    import {onMounted,onUnmounted} from 'vue'
    const intervalId
    onMounted(()=>{
        intervalId = setInterval(()=>{})
    })
    onUnmounted(()=>{
        clearInterval(intervalId)
    })
</script>

4. onBeforeMount()
组件挂载之前调用:完成响应式状态设置,但没有创建DOM节点,即将进行DOM的首次渲染。

5. onBeforeUpdate()
组件响应式状态更新,但还没更新DOM树。在这个钩子更改状态是安全的。

6. onBeforeUnmout()
组件卸载前调用,组件实例依然保有全部功能

7. onErrorCatured()
捕获组件传递错误时调用

8. onRenderTracked()
调试钩子,组件渲染时追踪到响应式依赖时调用,仅开发模式下可用

9. onRendTriggerd()
调试钩子,响应式依赖变更触发了组件渲染时调用,仅开发模式下可用

10. onActive()
组件实例是<KeepAlive></KeepAlive>缓存树的一部分,当组件插入DOM树时调用

11. onDeactivated()
组件实例是<KeepAlive></KeepAlive>缓存树的一部分,当组件移除DOM树时调用

12. onServerPrefetch()
注册一个异步函数,服务器渲染之前调用,仅会在服务端渲染中执行,用于执行一些仅存在于服务端的数据抓取过程。若此钩子返回一个promise,服务端渲染会等待该promise完成

组件生命周期图示

侦听器 watch()

用于每次响应式状态发生变化时触发回调函数
1. watch()
watch (数据源,回调函数,{deep:true},{imediate:true},{once:true})
数据源:ref  reactive  getter函数(return某个变量的函数)  多个数据源的数组
回调函数:(newValue,oldValue)=>{},接收两个参数,在父组件更新之后,所属组件DOM更新之前调用
{deep:true}:自定义深层响应
{imediate:true}:watch默认懒执行,仅当数据源变化时才执行回调,若想初始化时执行一次,可以使用这个
{once:true}: 仅执行一次回调

<script>
    const obj = reactive({name:'muggle'})
    // 错误,因为obj.name = 'muggle',传入的是字符串,不是变量
    watch(obj.name,(name)=>{ 
        console.log(name)
    })
    // 正确,应使用一个箭头函数来return其变量
    watch(()=>obj.name,(name)=>{  // name = obj.name
        console.log(name)
    }
</script>

2. 深层侦听器
如果数据源是响应式对象,则对象里的嵌套变更都会触发侦听器
<script>
    const obj = reactive({name:'muggle'})
    watch(obj,()=>{})
    watch(()=>obj) // 这样返回的没有深层响应
</script>

3. watchEffect
不需要数据源,自动侦听回调函数内使用到的所有响应式数据,只要其中一个发生变化,就会触发回调函数。并且watchEffet在执行时会立即触发一次回调函数。
<script>
    const name = ref("muggle")
    watchEffect(()=>{
        console.log(name)
    })
</script>

4. 副作用清理 vue3.5+有效
使用onWatcherCleanup()清理过时请求,即请求还没完成id已发生变化
<script>
    watch(id,(newId)=>{
        const controller = new AbortController()
        fetch(`/api/${newId}`).then(()=>{})
        onWatchCleanup(()=>{
            controller.abort()
        })
    })
</script>

5. 停止侦听器
watch()要用同步语句创建,即setup下,可以自动停止。如果在异步函数内创建了watch(),需要手动停止,否则会内存泄漏
<script>
    setTimeout(()=>{
        watch(()=>{})
    },2000)
    const unwatch = watchEfferct(()=>{})
    unwatch() // 手动停止
</script>

6. watch和watchEffect的区别
watch需要指定数据源,数据源只能是ref对象、reactive对象、getter返回的响应式数据,这些才能启动响应式数据的依赖追踪。若传入的是单纯的响应式数据,不会触发响应式依赖。响应式数据变更时才触发回调。
watchEffect不需要指定数据源,回调函数自动追踪所有响应式数据的依赖。立即触发一次回调

组合式函数

利用vue组合式API封装和复用有状态逻辑的函数
1. 鼠标位置例子
exprot将组合式函数传递出去
// mouse.js
<script setup>
    import {ref,onMounted,onUnmounted} from 'vue'
    // 封装函数
    export default 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一个值
        return {x,y}
    }
</script>
​
组件内使用函数
<script setup>
    import {useMouse} from 'vue'
    const {x,y} = useMouse() // x,y本来就是ref,所有函数返回的也是ref
</script>
<span>{{x}}{{y}}</span>
​
2. 异步状态例子
// fetch.js
<script setup>
    import {ref} from 'vue'
    export default function useFetch(url){
        const data = ref()
        const error = ref()
        // 记得使用toValuet提取url的值,因为url是一个ref对象
        fetch(toValue(url))
        .then((res)=>res.json())
        .then((json))=>(data.value=json))
        .cathc((error)=>(error.value=error))
        return {data,error}
    }
</script>
// 组件使用
<script setup>
    import {useFetch} from './fetch.js'
    const url = ref('./initial-url')
    // 将url设为响应式,每次更改url都会触发一次useFetch。或设为getter函数。
    const {data,error} = useFetch(url)
</script>
​
3. 组合式函数约定规则
命名:驼峰命名+use开头
输入参数:使用toValue,对传入函数的参数化为值,对ref和getter函数均有效
返回值:返回一个包含多个ref的对象return {x,y} ,这样解构的时候仍保持响应式
​

自定义指令

1. 自定义指令在script setup下构建,任何以v开头的驼峰式命名变量都可以作为自定义指令
<script setup>
    const vFocus = {
        // 当DOM树初次构建完成时自动聚焦,el为绑定的元素
        mounted:(el)=>el.focus()
    }
</script>
// 与自定义属性(props) 自定义事件(emits)一样,自定义指令也用-这种方式命名,vue会自动解析成vFocus
<input v-focus/>
​
2. 指令钩子
// 具体可看文档
​
3. 不建议在组件上使用自定义指令

插件

1. 定义插件
插件是一个包含install(app,option)方法的对象,或者是install(app,option)函数本身
// myPlugin.js
const myPlugin = {
    install:(app,option)=>{
        app.component()
        app.provide()
    } // 意味着在插件里可以使用app所有的全局方法,同时外部还可以传参到插件里面
}
export {myPlugin}
​
2. 使用插件
import {myPlugin} from './myPlugin.js'
import {createApp} from 'vue'
const app = createApp(App)
app.use(myPlugin,option) // 传入app和option参数对象给到插件

vue项目结构

├── node_modules/            # 依赖模块
├── public/                  # 公共文件(如 index.html)
│   └── index.html           # 项目的入口 HTML 文件
├── src/                     # 源代码
│   ├── assets/              # 静态资源(图片、字体等)
│   ├── components/          # Vue 组件
│   ├── views/               # 页面视图组件
│   ├── App.vue              # 根组件
│   ├── main.js              # Vue 应用的入口文件
├── package.json             # 项目配置文件
├── .gitignore               # Git 忽略文件
├── vue.config.js            # Vue CLI 配置(如果有的话)
└── README.md                # 项目说明文件
​
1. index.html
作为容器放置根组件,通过script标签引入vue应用的入口文件main.ts,同时main.ts使用app.mount('#app')将vue应用挂载到div DOM元素上
<body>
    <div id="app"></div>
</body>
<script type="module" src="./main.ts"></script>
​
2. main.js
vue应用的入口文件,用于创建vue应用
import {crateApp} from 'vue'
import App from './App.vue' // 根组件
const app = create(App)
app.mount('#app') // 将根组件挂载到id='app'的dom元素上
​
3. App.vue
Vue应用的根组件,不需要export defualt name,script setup自动将组件暴露到全局
​

defineOtions

直接在script setup下定义属性选项,不再需要单独的script
<script setup>
  defineOptions({
      name: 'TButton', // 组件名
      inheritAttrs: false,  // s
      customOptions: {
          // 自定义选项
      }
  })
</script>

vue的渲染机制

Vue组件挂载时
1. 编译:模板被编译为渲染函数,即用于返回虚拟dom的函数(render)
2. 挂载:调用渲染函数,遍历返回虚拟DOM树,基于虚拟DOM树创建实际节点,在这个阶段会追踪所有响应式依赖
3. 更新:当一个依赖发生变化,会诚信创建一个更新后的虚拟DOM树,遍历这颗新的虚拟DOM树,与旧树对比,将必要的更新到真实的DOM
​
其中render函数和创建虚拟DOM节点的h函数都可以由用户直接调用
​
模板vs渲染函数
模板会预编译为虚拟DOM渲染函数,可以之直接手写渲染函数render,创建想要的虚拟节点vnode
​
渲染函数基本用法
1. 创建vnode
import {h} from 'vue'
const vnode = h(
    'div', // type
    {id:'foo',class:'bar'}, // props
    [
        // children vnode
    ]
)
​
2. 使用renderAPI将虚拟vnode渲染到指定容器中
render(VNode, container)