第一節(jié):全面細(xì)致的理解創(chuàng)建vue3應(yīng)用的 createApp函數(shù)

1. createApp 創(chuàng)建應(yīng)用

1.1. createApp 函數(shù)理解

每個(gè) Vue 應(yīng)用實(shí)例都是通過(guò) createApp 函數(shù)創(chuàng)建;每次調(diào)用函數(shù)都會(huì)返回一個(gè)新的應(yīng)用對(duì)象;

使用方式

import {createApp} from 'vue'

const app = createApp({
  // ...options
})

通過(guò)源碼或vue官網(wǎng), 可以知道createApp函數(shù)的類(lèi)型如下

function createApp(rootComponent: Component, rootProps?: object): App

通過(guò)類(lèi)型可以看出 createApp 接收兩個(gè)參數(shù):

  1. 第一個(gè)參數(shù)為根組件對(duì)象(可以是.vue單文件組件, 可以是組件對(duì)象), 是必傳參數(shù),
  2. 第二個(gè)參數(shù)為傳遞給根組件的props, 第二個(gè)參數(shù)時(shí)可選參數(shù)

createApp 返回一個(gè)App 類(lèi)型的應(yīng)用對(duì)象.如果要詳細(xì)了解App類(lèi)型, 可以查看源碼. 我們可以通過(guò)在控制臺(tái)輸出的方式, 查看返回的應(yīng)用對(duì)象都具有哪些屬性.


1.2. 應(yīng)用對(duì)象

createApp函數(shù)調(diào)用完后返回一個(gè)應(yīng)用對(duì)象.

示例:

import {createApp} from 'vue'
const vm =createApp({
})
console.log('vm', vm)

控制臺(tái)輸出結(jié)果:

11.png

通過(guò)控制臺(tái)輸出可以看到很多熟悉的單詞, 比如component表示組件, directive表示指令, 具體如何使用, 我們娓娓道來(lái)

分析完createApp函數(shù)的返回值, 接下來(lái)我們?cè)敿?xì)研究一下參數(shù)

2. createApp API 參數(shù)

createApp函數(shù)接受兩個(gè)參數(shù), 第二個(gè)參數(shù)是可選地,

第一個(gè)參數(shù)有兩種形式

  1. 直接傳入單文件組件, 作為根組件
  2. 使用vue選項(xiàng)對(duì)象

2.1. createApp 參數(shù)單文件組件

通過(guò)createApp函數(shù)的類(lèi)型可知, createApp至少傳一個(gè)參數(shù), 參數(shù)可以為單文件組件

示例:

根組件代碼:

<template>
  <div>這是一個(gè)新的根組件</div>
</template>

<script lang="ts">
import { defineComponent } from "vue";

export default defineComponent({});
</script>

入口函數(shù)main.ts

import { createApp } from 'vue'

// 導(dǎo)入單文件組件
import App from './App.vue'

// 將單文件組件App作為參數(shù)傳遞給createAPP
const app = createApp(App)

運(yùn)行結(jié)果:


9518ec5aa4ee939f5f0ef9f7d71c9bd0.png


2.2. createApp 參數(shù)組件選項(xiàng)對(duì)象

createApp的第一個(gè)參數(shù)除了可以是單文件組件, 也可以是選項(xiàng)對(duì)象

vue2中我們通過(guò)template選項(xiàng)添加模板渲染內(nèi)容

vue3中, 我們也可以使用使用tempate選項(xiàng)定義渲染模板

示例:

// 引入完整vue代碼, 包括運(yùn)行時(shí) + 編譯器
import { createApp} from 'vue/dist/vue.esm-bundler.js'


const app = createApp({
  name: 'App',
  // 使用template 模板
  template: `
    <h2>你好,中國(guó)</h2>
  `,
})

運(yùn)行結(jié)果:


4af2a5e71aa68d7cef7bb0ce667acc80.png

這里有個(gè)需要注意的點(diǎn), 就是導(dǎo)入createApp的模塊不是vue, 而是vue/dist/vue.esm-bundler.js

原因在于從vue中導(dǎo)入的模塊,是運(yùn)行時(shí)(runtime)代碼, 運(yùn)行時(shí)代碼不包括編譯器(compile), 沒(méi)有編譯器,就不能將模板編譯成渲染函數(shù)

所以, 如果你需要在組件對(duì)象中使用template模板, 一定注意使用完整版vue

因?yàn)?code>template模板是需要編譯成渲染函數(shù), 通過(guò)調(diào)用渲染函數(shù), 獲取返回的vnode進(jìn)行頁(yè)面渲染.

所以我們也可以不使用template選項(xiàng), 直接編寫(xiě)渲染函數(shù), 渲染函數(shù)需要返回vnode.,

vue3提供了一個(gè)h函數(shù)用于創(chuàng)建vnode, 需要配合setup選項(xiàng)一起使用

示例: 在setup函數(shù)中直接返回render渲染函數(shù)

import { createApp,h } from 'vue'

// createApp, 組件對(duì)象中直接返回渲染函數(shù)
const app = createApp({
  name:'App',
  setup(){

    // 返回render渲染函數(shù)
    return () => {
      // 渲染中返回vnode, 通過(guò)vue3 提供h api 創(chuàng)建vnode
      return h('h1', null, 'hello world')
    }
  }
})

運(yùn)行結(jié)果:

7d3053932e77c51b8b55bde49ee76fd6.png

這里使用的setup選項(xiàng), 以及h函數(shù)都是vue3開(kāi)始提供API, 之后我們會(huì)詳細(xì)分析

這里主要記住兩點(diǎn):

  1. setup選項(xiàng), 可以理解時(shí)一個(gè)鉤子函數(shù), 跟created相似, 會(huì)自動(dòng)執(zhí)行, 可以直接返回一個(gè)渲染函數(shù)
  2. h函數(shù)用于創(chuàng)建vnode, 渲染函數(shù)需要返回vnode

至此, 我們已經(jīng)分析完createApp函數(shù)的第一個(gè)參數(shù), 接下來(lái)我們分析第二個(gè)參數(shù)

2.3. createApp 第二個(gè)參數(shù)為props

createApp函數(shù)第二個(gè)參數(shù)的作用是給根組件傳入props數(shù)據(jù)

props相信大家已經(jīng)比較熟了, 就是父組件給子組件傳參的方式. 問(wèn)題在于根組件已經(jīng)是頂級(jí)組件了, 沒(méi)有父組件傳入props參數(shù).

此時(shí)我們就可以使用createApp第二個(gè)參數(shù)實(shí)現(xiàn)向根組件傳參

示例:

import { createApp, h } from 'vue'

const app = createApp(
  // 第一個(gè)參數(shù): 根組件對(duì)象
  {
    name: 'App',
    // 獲取第二個(gè)參數(shù)傳入的props數(shù)據(jù)
    props: {
      msg: {
        type: String,
        default: ''
      }
    },
    setup(props) {
      console.log('props', props)
  
      return () => h('h1', null, props.msg)
    }
  },

  // 第二個(gè)參數(shù): props 對(duì)象
  {
    msg: 'hello world'
  }
)

控制臺(tái)輸出的props值:

8393606d8dfaeda4168037f6dddb4031.png

一般情況下使用不到第二個(gè)參數(shù), 因?yàn)檎J褂?code>createApp 時(shí),會(huì)使用.vue單文件組件作為參數(shù)

而根組件通常是頁(yè)面的布局結(jié)構(gòu), 不太能使用到數(shù)據(jù)

到此為止,createAppAPI 的入?yún)⒕头治鐾炅? 我們前面講到過(guò), createApp會(huì)返回應(yīng)用對(duì)象.

接下來(lái)我們?cè)敿?xì)分析一下, createApp返回的應(yīng)用對(duì)象

3. 應(yīng)用對(duì)象的屬性與方法

前面我們也在控制臺(tái)輸出了createApp函數(shù)返回的應(yīng)用對(duì)象.應(yīng)用對(duì)象中具有很多屬性和方法.

我們看幾個(gè)常用的方法

示例: 查看app應(yīng)用屬性與方法

import { createApp } from 'vue'
import App from './App.vue'  // 根組件

// createApp創(chuàng)建app應(yīng)用實(shí)例對(duì)象
const app = createApp(App)

console.log(app)
/*

{
    // 1. 注冊(cè)全局組件
    component:  ? component(name, component)
  config: {...}
  directive: ? directive(name, directive)
  mixin: ? mixin(mixin)

  // 2. 掛載方法(參數(shù)時(shí)dom對(duì)象或選擇器)
  mount: (containerOrSelector) => {…}
  provide: ? provide(key, value)
  runWithContext: ? runWithContext(fn)


  // 3. 卸載應(yīng)用
  unmount: ? ()  
  use: ? use(plugin, ...options)
  version: "3.3.4"
  _component: {name: 'JX-APP', __hmrId: 'e16649ff', __scopeId: 'data-v-e16649ff', __file: 'D:/studey/vue-上課/vue3/vue代碼/vue3/vue3-studey/src/App2.vue', setup: ?, …}
  _container: div#app
  _context: {app: {…}, config: {…}, mixins: Array(0), components: {…}, directives: {…}, …}
  _instance: {uid: 0, vnode: {…}, type: {…}, parent: null, appContext: {…}, …}
  _props: null
  _uid: 0
}

*/


3.1. app.mount 掛載方法

vue3通過(guò)調(diào)用應(yīng)用對(duì)象的mount()方法掛載應(yīng)用, 該方法接受一個(gè)"容器"參數(shù), 可以是一個(gè)真實(shí)DOM元素, 也可以是一個(gè)選擇器字符串


參數(shù)為真實(shí)dom元素

示例:

import { createApp } from 'vue'
import App from './App.vue'  // 根組件

// createApp創(chuàng)建app應(yīng)用實(shí)例對(duì)象
const app = createApp(App)

// 獲取真實(shí)的dom節(jié)點(diǎn)
const dom = document.getElementById('app')

// 通過(guò)真實(shí)dom節(jié)點(diǎn)進(jìn)行掛載
app.mount(dom)


參數(shù)為選擇器字符串

示例:

// 導(dǎo)入createApp
import { createApp } from "vue";
import App from "./App.vue";

// 初始化vue應(yīng)用
const app = createApp(App);

// 調(diào)用mount方法掛著vue
app.mount("#app");

這個(gè)時(shí)候不知道大家是否會(huì)有一問(wèn), 選擇器只能用id嗎? 如#app

答案是否定的, 也可以使用其他選擇器, 比如class 選擇 .app, 只不過(guò)id具有唯一性

示例:

<div  class="app"></div>
<script type="module" src="/src/main.ts"></script>s
// 導(dǎo)入createApp
import { createApp } from "vue";
import App from "./App.vue";

// 初始化vue應(yīng)用
const app = createApp(App);

// 調(diào)用mount方法掛著vue
app.mount(".app");

我們會(huì)發(fā)現(xiàn)也是可以運(yùn)行的, 原因在于mount參數(shù)如果是選擇器, vue3會(huì)自己獲取dom節(jié)點(diǎn)

我們不用擔(dān)心class類(lèi)名,或者標(biāo)簽名有多個(gè)的問(wèn)題, 因?yàn)?code>vue使用querySelector 方法通過(guò)選擇器獲取第一個(gè)dom節(jié)點(diǎn)

我們簡(jiǎn)單看一下vue3源碼中mount方法的實(shí)現(xiàn)

源碼:

 app.mount = (containerOrSelector: Element | ShadowRoot | string): any => {
   // 獲取掛載點(diǎn)的真實(shí)dom
    const container = normalizeContainer(containerOrSelector)

   // ....
}


function normalizeContainer(
  container: Element | ShadowRoot | string
): Element | null {
  // 判斷mount 掛載方法參數(shù)是不是字符串
  if (isString(container)) {
    // 獲取dom, 因?yàn)槭褂胵uerySelector 方法獲取, 因此返回第一個(gè)dom對(duì)象
    const res = document.querySelector(container)

    // 如果通過(guò)mount參數(shù)字符串沒(méi)有獲取到掛載點(diǎn)dom對(duì)象, 則報(bào)錯(cuò)
    if (__DEV__ && !res) {
      warn(
        `Failed to mount app: mount target selector "${container}" returned null.`
      )
    }
    return res
  }

  
  // 如果參數(shù)時(shí)真實(shí)dom節(jié)點(diǎn)直接返回
  return container as any
}


3.2. app.unmount 卸載應(yīng)用

應(yīng)用對(duì)象通過(guò)mount方法掛載元素, 相對(duì)應(yīng)的,是通過(guò)unmount方法卸載應(yīng)用

示例

// 導(dǎo)入createApp
import { createApp } from "vue";
import App from "./App.vue";

// 初始化vue應(yīng)用
const app = createApp(App);

// 調(diào)用mount方法掛著vue
app.mount("#app");


// 卸載應(yīng)用
app.unmount()


3.3. app.component 注冊(cè)全局組件

app.component 方法是vue3提供的用于注冊(cè)全局組件

  1. 如果component方法傳入兩個(gè)參數(shù), 第一個(gè)參數(shù)為字符串,用于聲明組件名稱(chēng), 第二個(gè)參數(shù)為組件對(duì)象,
  2. 如果component接受一個(gè)參數(shù), 參數(shù)為一個(gè)名字,則會(huì)返回用該名字對(duì)應(yīng)的注冊(cè)組件 (如果存在的話)。


示例:

注冊(cè)全局組件

// 導(dǎo)入createApp
import { createApp } from "vue";
import App from "./App.vue";

// 初始化vue應(yīng)用
const app = createApp(App);

// 注冊(cè)全局組件
app.component(
  // 第一個(gè)參數(shù)為組件名稱(chēng)
  'jx-hello',
  // 第二參數(shù)為組件對(duì)象
  {
    name: 'JxHello',
    setup() {
      return () => {
        return h('div', null, '這是一個(gè)全局組件')
      }
    }
  }
)


// 調(diào)用mount方法掛著vue
app.mount("#app");

使用全局組件

<template>
  <div>
        <!--   使用全局組件     -->
    <jx-hello></jx-hello>
  </div>
</template>


<script  lang="ts">
import { defineComponent,  } from 'vue';


export default defineComponent({
  name: "JX-APP",
  setup() {
    return {}
  }
})
</script>

到這里, createAppAPI的使用就已經(jīng)分析完了

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容