vue3中定义组件的4种姿势

前言

由于vue3支持创建组件的方式多样化,实际使用的时候容易混淆。这里梳理了常用的4个创建组件方式。包括 option API,composition API,defineComponent, app.component()

1.option API

option API(template)

面向option api配置,使用配置+template方式,跟vue2类似

js 复制代码
<script>
export default {
  name: 'MyComponent',
  props: ['msg'],
  data() {
    return {
      count: 0
    }
  },
  methods: {
    increment() {
      this.count++
    }
  }
}
</script>

<template> 
  <h1>option + template</h1>
  <div>传入参数: {{ msg }}   <button @click="increment">+</button>Count: {{ count }}
  </div>
</template>

效果图

option API(render+h)

最终上面的template会被转化成render方法,所以我们可以不写template,直接执行渲染函数render,通过h函数创建不同的节点

js 复制代码
<script lang="ts">
import { h } from 'vue'

export default {
  name: 'HelloRender',
  props: {
    msg: {
      type: String,
      required: true
    }
  },
  data() {
    return {
      count: 0
    }
  },
  methods: {
    increment() {
      this.count++
    }
  },
  render() {
    return h('div', null, [
        h('h1', null, `option API + render + h`),
      h('span', null, `传入参数: ${this.msg}`),
      h(
        'button',
        {
          onClick: this.increment
        },
        '+1'
      ),
      h('span', null, `count ${this.count} `),
    ])
  }
}
</script>

option API(render+jsx)

jsx最终也是生成vnode ,跟上面的h方法类似

js 复制代码
<script lang="jsx">

export default {
  name: 'HelloRender',
  props: {
    msg: {
      type: String,
      required: true
    }
  },
  data() {
    return {
      count: 0
    }
  },
  methods: {
    increment() {
      this.count++
    }
  },
  render() {
   return  (<div>
      <h1>option API  + return + jsx</h1>
      传入参数: { this.msg }  
      <button onClick={this.increment}>+1</button> Count:  {this.count}
  </div >)
   
  }
}
</script>

注意:jsx需要另外加入插件支持 @vitejs/plugin-vue-jsx

vite配置

js 复制代码
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue' 
import vueJsx from '@vitejs/plugin-vue-jsx'
export default defineConfig({
  plugins: [vue(),vueJsx()],
})

2. composition API

vue3 composition API 是推荐使用的api

composition API (script + template)

script + template 是常用写法

缺点,template 要用的变量都要通过setup的return返回包裹的响应式变量与方法。

js 复制代码
<template>
  <div>
    <h1>composition API (template)</h1>
    <span>传入参数:{{ msg }}</span>
    <button @click="increment">+1</button><span>Count: {{ count }}</span>
  </div>
</template>

<script>
import { ref } from 'vue'

export default {
  name: 'App',
  props: {
    // 标题
    msg: {
      type: String,
    },
  },
  setup() {
    const count = ref(0)

    const increment = () => {
      count.value++
    }

    // 返回模板中需要使用的数据和方法
    return {
      increment, count
    }
  }
}
</script>

效果图

composition API(script setup + template)

vue3 Composition API 语法糖,省去了上面的导出

js 复制代码
<template>
  <div>
    <h1>composition API (script setup + template)</h1>
    <span>传入参数:{{ msg }}</span> 
    <button @click="increment">+1</button><span>Count: {{ count }}</span>
  </div>
</template>

<script setup>
import { ref } from 'vue' 
const props = defineProps(['msg'])
const count = ref(0)

const increment = () => {
  count.value++
}
</script>

composition API(return h )

同理,如果composition API希望不使用template,可以在setup方法里面返回一个vnode节点。下面使用h函数返回vnode, 等价于上面option API的render方法

js 复制代码
<script>
import { ref, h } from 'vue'

export default {
  name: 'App',
  props: {
    // 标题
    msg: {
      type: String,
    },
  },
  setup(props) {
    const count = ref(0)
    const increment = () => count.value++

    return () =>
      h('div', [
        h('h1', `composition API (return h )`), 
         h('span', `传入参数: ${props.msg}`),
        h('button', { onClick: increment }, '+1'),
        h('span', `Count: ${count.value}`)
      ])
  }
}
</script>

composition API(return jsx )

同理,我们也可以使用jsx 生成vnode节点

js 复制代码
<script lang="jsx">
import { ref } from 'vue'

export default {
  name: 'App',
  props: {
    // 标题
    msg: {
      type: String,
    },
  },
  setup(props) {
    const count = ref(0)
    const increment = () => count.value++

    return () =>
      (<div>
      <h1>composition API (return jsx )</h1>
      传入参数: { props.msg }  
      <button onClick={increment}>+1</button> Count:  {count.value}
  </div >)
  }
}
</script>

3. defineComponent

上面我们可以看到使用的都是弱类型,无法对ts类型做支持,于是vue3新增了defineComponent对象用于支持option API和composition API的声明。

下面是option API的使用defineComponent进行声明,其实就是在export default {} 多包装了 一层 export default defineComponent({....})

defineComponent + option API (template)

js 复制代码
<script>
import { defineComponent, ref } from 'vue'

export default defineComponent({
  name: 'MyComponent',
  props: {
    msg: {
      type: String,
      required: true
    }
  },
  data() {
    return {
      count: 0
    }
  },
  methods: {
    increment() {
      this.count++
    }
  }
})
</script>

<template>
  <div>
    <h1>defineComponent + option API (template)</h1>
    传入参数: {{ msg }}
    <button @click="increment">+1</button>Count:  {{ count }}
  </div>
</template>

效果图

defineComponent + option API (render + h)

js 复制代码
<script lang="ts">
import { h,defineComponent } from 'vue'

export default defineComponent({
  name: 'HelloRender',
  props: {
    msg: {
      type: String,
      required: true
    }
  },
  data() {
    return {
      count: 0
    }
  },
  methods: {
    increment() {
      this.count++
    }
  },
  render() {
    return h('div', null, [
        h('h1', null, `defineComponent + option API (render + h)`),
      h('span', null, `传入参数: ${this.msg}`),
      h(
        'button',
        {
          onClick: this.increment
        },
        '+1'
      ),
      h('span', null, `count ${this.count} `),
    ])
  }
})
</script>

defineComponent + option API (render + jsx)

js 复制代码
<script lang="jsx">
import { h,defineComponent } from 'vue'

export default defineComponent({
  name: 'HelloRender',
  props: {
    msg: {
      type: String,
      required: true
    }
  },
  data() {
    return {
      count: 0
    }
  },
  methods: {
    increment() {
      this.count++
    }
  },
   render() {
   return  (<div>
      <h1>defineComponent + option API (render + jsx)</h1>
      传入参数: { this.msg }  
      <button onClick={this.increment}>+1</button> Count:  {this.count}
  </div >)
   
  }
})
</script>

defineComponent + composition API (template)

js 复制代码
<template>
  <div>
    <h1>defineComponent + composition API (template)</h1>
    <span>传入参数:{{ msg }}</span>
    <button @click="increment">+1</button><span>Count: {{ count }}</span>
  </div>
</template>

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

export default defineComponent({
  name: 'App',
  props: {
    // 标题
    msg: {
      type: String,
    },
  },
  setup() {
    const count = ref(0)

    const increment = () => {
      count.value++
    }

    // 返回模板中需要使用的数据和方法
    return {
      increment, count
    }
  }
})
</script>

defineComponent + composition API ( return + h)

js 复制代码
<script>
import { defineComponent, h, ref } from 'vue'

export default defineComponent({

   props: {
    msg: {
      type: String,
      required: true
    } 
  },
  setup(props) {
    const count = ref(0)
    const increment = () => count.value++

    return () =>
      h('div', [
        h('h1', `defineComponent + composition API ( return + h)`), 
         h('span', `传入参数: ${props.msg}`),
        h('button', { onClick: increment }, '+1'),
        h('span', `Count: ${count.value}`)
      ])
  }
})
</script>

defineComponent + composition API ( return + h)

js 复制代码
<script lang="jsx">
import { defineComponent, h, ref } from 'vue'

export default defineComponent({
   props: {
    msg: {
      type: String,
      required: true
    } 
  },
  setup(props) {
    const count = ref(0)
    const increment = () => count.value++

    return () =>
    (<div>
      <h1>defineComponent + composition API ( return + jsx)</h1>
      传入参数: { props.msg }  
      <button onClick={increment}>+1</button> Count:  {count.value}
  </div >)
  }
})
</script>

3种总效果图

4. app.component()

在非sfc单页面.vue 项目中,通过app.component 定义注册全局的组件

template 字符串

由于是运行时解析,所以我们可以把template定义成字符串, main.js

js 复制代码
import { createApp } from 'vue'
import './style.css'
import App from './App.vue' 

import appComponent_template from './components/appComponent_template.js'  

const app = createApp(App)

app.component('appComponent_template', appComponent_template);  //这里使用的是一个json对象,而不是.vue文件,其实最终也是解析成这个json对象
 
app.mount('#app')

/appComponent_template.js

js 复制代码
export default  { 
  template: '<div><h1>app.component </h1>传入参数:{{msg}}  <button @click="increment()">+1</button>Count:{{count}}</div>',
  props: {
    msg: {
      type: String,
      required: true
    }
  },
  data() { return {count:0 } }
  , methods: {increment() { this.count += 1 } }
}

App.vue 无需导入,直接使用

js 复制代码
<template>
  <appComponent_template msg="xx"/>  
</template>

注意这种方式工程化项目需要开启运行时解析template

vite配置

js 复制代码
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue' 
import vueJsx from '@vitejs/plugin-vue-jsx'
import { resolve } from 'path'
export default defineConfig({
  plugins: [vue(),vueJsx()],
  resolve: {
    alias: {
      // 关键:让 vue 指向支持模板编译的版本
      'vue': 'vue/dist/vue.esm-bundler.js'
    }
  }
})

效果

其他 template API/composition API/ defineComponent

js 复制代码
import { createApp } from 'vue'
import './style.css'
import App from './App.vue' 

import appComponent_template from './components/appComponent_template.js' 
import option_template from './components/option_template.vue' 
import defineComponent_option_template from './components/defineComponent_option_template.vue' 
import composition_template from './components/composition_template.vue' 

const app = createApp(App)

app.component('appComponent_template', appComponent_template); 
app.component('option_template', option_template); 
app.component('defineComponent_option_template', defineComponent_option_template); 
app.component('composition_template', composition_template); 
 
app.mount('#app')

App.vue 无需导入,直接使用

js 复制代码
<template> 
  <appComponent_template msg="xx"/>
  <option_template msg="xx"/>
  <defineComponent_option_template msg="xx"/>
  <composition_template msg="xx"/>  

</template>

效果图

5.总结

在实际开发中,包括维护别人的代码会看到很多不同的定义方式,但是百变不离其中,最终还是得吧template的内容转化成vnode虚拟dom,上面的所有代码也是基于这个核心出发。 掌握渲染实现,有利我们定义高阶组件的时候可以借助如jsx或h函数等,更灵活动态化配置我们的组件。

6.参考地址

github.com/mjsong07/vu...

相关推荐
袋鱼不重4 小时前
Gitee 与 GitHub 仓库同步:从手动操作到自动化部署
前端·github
哒哒哒就是我4 小时前
React中,函数组件里执行setState后到UI上看到最新内容的呈现,react内部会经历哪些过程?
前端·react.js·前端框架
AGG_Chan4 小时前
flutter专栏--深入剖析你的第一个flutter应用
前端·flutter
再学一点就睡4 小时前
多端单点登录(SSO)实战:从架构设计到代码实现
前端·架构
繁依Fanyi4 小时前
思维脑图转时间轴线
前端
发愤图强的羔羊4 小时前
Chartdb 解析数据库 DDL:从 SQL 脚本到可视化数据模型的实现之道
前端
摸着石头过河的石头4 小时前
控制反转 (IoC) 是什么?用代码例子轻松理解
前端·javascript·设计模式
携欢5 小时前
PortSwigger靶场之Stored XSS into HTML context with nothing encoded通关秘籍
前端·xss
小桥风满袖5 小时前
极简三分钟ES6 - const声明
前端·javascript