[TOC]
>[success] # 渲染函數(render函數)和JSX快速掌握
在接下來講學習到如何在 **Vue** 中使用 **渲染函數** 來 **創建視圖模板** ,并且會講解 **JSX** 的 **語法** ,同時會補充2個內容,第一是 **函數式組件** ,第二是 **作用域插槽** 。
>[success] ## render函數
**render函數** :可以用 **函數的方式** 渲染 **dom元素** 到頁面中。
下面會講解2種使用場景:
1. 在 **main.js** 中如何使用
2. 在 **.vue** 文件中使用
>[success] ### render函數在main.js中使用
**src/main.js**
~~~
import Vue from 'vue'
import App from './App.vue' // app組件
import router from './router'
import store from './store'
import './plugins/element.js'
import Bus from './lib/bus' // 引入Bus
Vue.config.productionTip = false
Vue.prototype.$bus = Bus // 掛載Bus到Vue原型鏈(全局掛載Bus)
new Vue({
router,
store,
render: h => h(App) // 渲染app組件
}).$mount('#app')
~~~
我們首先在 **main.js** 中學習 **render函數** , **render** 的 **屬性值** 是一個 **回調函數** ,它的參數可以用括號包裹起來,例如這樣: **render: (h) => h(App)** ,如果 **只有一個參數就可以不用括號包裹** , **h這個參數是一個方法** ,這個方法能 **創建一個虛擬節點** ,**這個函數 return 返回一個結果** 。
| 方法名 | 參數 | 是否必填 |
| --- | --- | --- |
| **render函數:render: h => h(App)** | **h函數的第1個參數**:**要渲染的組件,或者一個標簽字符串,或者也可以是一個函數** | **是** |
| | **h函數的第2個參數**: 該參數是一個 **配置對象**,可以 **通過該對象給元素設置屬性** ,例如 **div** 標簽的 **id、class 等等** | **否** |
| | **h函數的第3個參數**:該 **參數可以是字符串或者數組**,主要作用是 **給元素添加內容** | **否** |
1. **h函數的參數1**
**說明** : **第1個參數** 是用來傳入 **元素標簽** 或者 **組件** 。
1.1. **傳入組件** :**main.js** 中 **h函數** 默認傳入的是 **app.vue頁面組件**,所以就會 **渲染該組件** ,下面引入之前封裝的 **CountTo 組件**
~~~
import Vue from 'vue'
import App from './App.vue'
import router from './router'
import store from './store'
import './plugins/element.js'
import Bus from './lib/bus'
import CountTo from '_c/count-to' // 引入CountTo 組件
Vue.config.productionTip = false
Vue.prototype.$bus = Bus
new Vue({
router,
store,
render: h => h(CountTo, {
// 'class': 'count-to', // 給組件最外層盒子添加class類名
// 或者這樣寫
// 'class': ['count-to', true ? 'count-to2' : ''],
// 或者這樣寫
'class': {
'count-to': true,
'count-to2': 1 === 1,
},
attrs: {}, // 定義屬性id等等
style: {}, // 定義樣式
props: { // 添加屬性 這里可以理解為就是<count-to :endValue="100"/>
endValue: 100
},
// domProps: { // dom的一些屬性
// innerHTML: '11' // 可以設置標簽的一些內容
// },
on: { // 添加事件
'on-animation-end': (val) => { // 事件名
console.log(val)
}
},
nativeOn: { // 組件內沒有定義click事件時,給組件最外層元素綁定一個click事件
'click': () => {
console.log('click')
}
},
directives: [], // 可以定義自定義指令
scopedSlots: {},
slot: '', // 插槽
key: '', // 設置一個值讓每個組件的key不相等
ref: '' // ref
})
}).$mount('#app')
~~~
1.2. **傳入字符串** :也可以像下面寫,這樣頁面中就 **渲染** 出一個 **div標簽** 。
~~~
new Vue({
router,
store,
render: h => h('div')
}).$mount('#app')
~~~
1.3. **傳入函數** :
~~~
let func = ()=> 'h3'
new Vue({
router,
store,
render: h => h(func())
}).$mount('#app')
~~~
2. **h函數的參數2**
**說明** : **h函數** 的 **第2個參數是個對象** ,用來 **定義元素的一些屬性** 。
2.1. **給元素設置屬性** :
~~~
new Vue({
router,
store,
render: h => h('div', {
attrs: { // 添加屬性
id: 'box'
},
style: { // 添加樣式
color: 'red'
}
})
}).$mount('#app')
~~~
3. **h函數的參數3**
**說明** : **h函數的第3個參數可以是字符串或者數組** ,用來 **定義元素的內容** 。
3.1. **傳入字符串**
~~~
new Vue({
router,
store,
render: h => h('div', {
attrs: { // 添加屬性
id: 'box'
},
style: { // 添加樣式
color: 'red'
}
}, '我是div的內容')
}).$mount('#app')
~~~
**如果不設置樣式可以直接忽略h函數的第2個參數** , 直接寫內容
~~~
new Vue({
router,
store,
render: h => h('div', '123')
}).$mount('#app')
~~~
3.2. **傳入數組**
如果想讓內容是 **多個標簽** ,就需要 **傳入數組**
~~~
new Vue({
router,
store,
render: h => h('div', [
h('span', '111'),
h('span', '222')
])
}).$mount('#app')
~~~
3.3. **循環傳入標簽**
如果想實現一個類似 **v-for** 的效果,首先先看 **正常循環列表寫法**
~~~
<template>
<ul @click="handleClick">
<li @click.stop="handleClick" v-for="(item, index) in list" :key="`list_item_${index}`">{{ item.name }} </li>
</ul>
</template>
<script>
export default{
data(){
return{
list: [
{name: '小明'},
{name: '小黑'}
]
}
},
methods: {
handleClick(event){
console.log(event)
}
}
}
</script>
~~~
接下來使用 **render函數** 實現上面的 **循環列表跟點擊事件**
~~~
// 點擊事件
const handleClick = event => {
console.log(event)
event.stopPropagation()
}
// 數據
let list = [
{name: '小明'},
{name: '小黑'}
]
// 循環生成li
const getLiEleArr = (h) => {
return list.map((item, index) => h('li', {
on: {
'click': handleClick
},
key: `list_item_${index}`
}, item.name))
}
// 最終渲染
new Vue({
router,
store,
render: h => h('div',[
h('ul', {
on: {
'click': handleClick
}
}, getLiEleArr(h))
])
}).$mount('#app')
~~~
>[success] ### render函數在.vue中使用
有時 **即使你封裝好了組件,但是想根據自己的方式去定制組件內的元素以及內容**,這時候就需要 **給組件傳入一個render函數**
1. **父組件**
首先在 **路由列表** 的 **路由對象** 中添加新創建的 **render-page** 頁面配置路由
**src/router/router.js**
~~~
export default [
{
path: '/render-page',
name: '/render_page',
component: () => import('@/views/render-page')
}
]
~~~
然后在 **src/views/render-page.vue** 頁面 **引入list組件**
**src/views/render-page.vue**
~~~
<template>
<div>
<list :list="list" :render="renderFunc"></list>
</div>
</template>
<script>
import List from '_c/list'
export default{
data(){
return{
list: [ // 數據
{name: '小明'},
{name: '小黑'}
]
}
},
components: {
List
},
methods: {
renderFunc(h, name){ // 自定義render函數渲染自己想要的dom節點內容
return h('i', {
style: {
color: 'pink'
}
}, name)
}
}
}
</script>
~~~
1. **子組件**
**src/components/list/list.vue**
~~~
<template>
<ul>
<li v-for="(item, index) in list" :key="`item_${index}`">
<!-- 如果未傳入render函數就span標簽內容 -->
<span v-if="!render">{{ item.name }}</span>
<!-- 如果傳入了render函數,就使用render函數自定義dom的節點來渲染 -->
<render-dom v-else :render-func="render" :name="item.name"></render-dom>
</li>
</ul>
</template>
<script>
// 引入函數式組件
import RenderDom from '_c/render-dom'
export default {
name: 'List',
components: {
RenderDom // 注冊函數式組件
},
props: {
list: { // 列表內容
type: Array,
default: () => []
},
render: { // render 函數
type: Function,
default: () => {}
}
}
}
</script>
~~~
定義一個 **index.js 方便父組件引用**
**src/components/list/index.js**
~~~
import List from './list.vue'
export default List
~~~
在上面的組件中使用到了 **函數式組件** ,這里寫的 **函數式組件** 就是使用 **reder函數 自定義一些想要的標簽** 然后 **return** 返回一個 **虛擬節點** , **最終渲染在使用函數式組件的地方** ,**函數式組件詳解看下方的文檔介紹**。
**src/components/render-dom.js**
~~~
export default {
functional: true,
props: {
name: String, // 組件渲染的文字內容
renderFunc: Function // 傳入的render函數
},
/**
* render渲染函數
* @param {Function} h - render函數的回調方法,用于生成dom節點
* @param {Object} ctx - 指代當前的這個對象
*/
render: (h, ctx) => {
return ctx.props.renderFunc(h, ctx.props.name)
}
}
~~~
>[success] ## 函數式組件
**函數式組件** : **只給它傳入數據,它不做任何響應式的操作, 不監聽傳遞給它的狀態** ,這個組件 **沒有生命周期和鉤子函數**,它 **只是作為一個接收參數的函數** , 當 **functional 設置為 true** 時候,證明 **它是一個沒有狀態的組件,也沒有實例,就是一個對象** , 當 **把這個對象引入到其他頁面,當做一個組件去使用的時候,vue會把它做一個處理,會把 【render函數】里面邏輯返回的【虛擬節點】做一個渲染** 。
**src/components/render-dom.js**
~~~
export default {
functional: true,
props: {
name: String, // 組件渲染的文字內容
renderFunc: Function // 傳入的render函數
},
/**
* render渲染函數
* @param {Function} h - render函數的回調方法,用于生成dom節點
* @param {Object} ctx - 指代當前的這個對象
*/
render: (h, ctx) => {
return ctx.props.renderFunc(h, ctx.props.name)
}
}
~~~
>[success] ## JSX
**JSX** :**JSX** 最先是 **react** 提出的, **通過一種形式,在 js 里面寫 html 標簽,還有一些特定的語法** ,最后會把這個 **字符串** 轉譯成 **js** ,去用 **render函數** 來做渲染。
>[success] ### JSX渲染標簽字符串
1. **父組件**
**src/views/render-page.vue**
~~~
<template>
<div>
<list :list="list" :render="renderFunc"></list>
</div>
</template>
<script>
import List from '_c/list'
export default{
data(){
return{
list: [ // 數據
{name: '小明'},
{name: '小黑'}
]
}
},
components: {
List
},
methods: {
renderFunc(h, name){ // 自定義render函數渲染自己想要的dom節點內容
/**
* render函數方式
*/
// return h('i', {
// style: {
// color: 'pink'
// }
// }, name)
/**
* JSX方式
* JSX與template模板對比
* style:
* template的標簽中寫法:style="{ color: 'pink' }"
* JSX寫法:style={{color: 'pink'}}
* JSX中style不需要添加雙引號,屬性都需要用{}包裹
*
* 事件:
* template的標簽中寫法v-click="handleClick" 或者 @click="handleClick"
* JSX寫法on-click={ this.handleClick }
*/
return (
<i on-click={ this.handleClick } style={{color: 'pink'}}>{ name }</i>
)
},
handleClick(event){
console.log(event)
}
}
}
</script>
~~~
使用 **JSX** 時 **render函數** 的 **形參必須是 h** ,不可以改成其他的(例如 **createElement**)。以上代碼中引入的 **List組件** 在上面的 **render函數** 文檔中有寫過,在此處就不再過多陳述。
>[success] ### JSX渲染組件
1. **父組件**
**src/views/render-page.vue**
~~~
<template>
<div>
<list :list="list" :render="renderFunc"></list>
</div>
</template>
<script>
import List from '_c/list'
import CountTo from '_c/count-to' // 引入組件
export default{
data(){
return{
list: [ // 數據
{ number: 100 },
{ number: 45 }
]
}
},
components: {
List
},
methods: {
renderFunc(h, number){ // 自定義render函數渲染自己想要的dom節點內容
return (
/**
* 這里可以引入組件進行渲染,而且不用在components對象中注冊
* 事件分為2種:原生事件、自定義事件
* html標簽:支持原生事件
* 組件:支持原生事件(給下面的CountTo組件綁定一個原生click事件,就相當于給組件內的最外層元素綁定了一個click事件,
* 寫法:nativeOn-事件名稱={方法})、
* 自定義事件(寫法:on-自定義事件名稱={方法})
* template模板中的事件修飾符在JSX跟render函數中用到需要看文檔
* */
<CountTo nativeOn-click={this.handleClick} on-on-animation-end={this.handleEnd} endValue={number} style={{color: 'pink'}}></CountTo>
)
},
handleClick(event){
console.log(event)
},
handleEnd(){
console.log('end!')
}
}
}
</script>
~~~
父組件中引入的 **[CountTo組件](http://www.hmoore.net/wangjiachong/vue_notes/1971966)**
2. **子組件**
**src/components/list.vue**
~~~
<template>
<ul>
<li @mousemove="handleMove" v-for="(item, index) in list" :key="`item_${index}`">
<!-- 如果未傳入render函數就span標簽內容 -->
<span v-if="!render">{{ item.number }}</span>
<!-- 如果傳入了render函數,就使用render函數自定義dom的節點來渲染 -->
<render-dom v-else :render-func="render" :number="item.number"></render-dom>
</li>
</ul>
</template>
<script>
// 引入函數式組件
import RenderDom from '_c/render-dom'
export default {
name: 'List',
components: {
RenderDom // 注冊函數式組件
},
props: {
list: { // 列表內容
type: Array,
default: () => []
},
render: { // render 函數
type: Function,
default: () => {}
}
},
methods: {
handleMove(event){
// 阻止默認行為(文字不可以選中復制)
event.preventDefault()
}
}
}
</script>
~~~
**list組件** 對應的 **index.js**,用于 **父組件方便引用**
**src/components/list/index.js**
~~~
import List from './list.vue'
export default List
~~~
**子組件** 中用到的 **函數式組件 render-dom.js**
**src/components/render-dom.js**
~~~
export default {
functional: true,
props: {
number: Number, // 組件渲染的文字內容
renderFunc: Function // 傳入的render函數
},
/**
* render渲染函數
* @param {Function} h - render函數的回調方法,用于生成dom節點
* @param {Object} ctx - 指代當前的這個對象
*/
render: (h, ctx) => {
return ctx.props.renderFunc(h, ctx.props.number)
}
}
~~~
>[success] ## 作用域插槽
**定制組件內的元素以及內容** ,用 **render函數** 以及 **JSX** 都比較繁瑣,接下來用 **作用域插槽** 來實現這個需求。
1. **父組件**
**src/views/render-page.vue**
~~~
<template>
<div>
<list :list="list">
<h3 slot="aa">我是小明</h3>
<coun-to slot-scope="count" :end-value="count.number"></coun-to>
</list>
</div>
</template>
<script>
import List from '_c/list'
import CounTo from '_c/count-to' // 引入組件
export default{
data(){
return{
list: [ // 數據
{ number: 100 },
{ number: 45 }
]
}
},
components: {
List,
CounTo
}
}
</script>
~~~
2. **子組件**
**src/components/list.vue**
~~~
<template>
<ul>
<li v-for="(item, index) in list" :key="`item_${index}`">
<!-- 默認插槽 -->
<!-- <slot></slot> -->
<!-- 具名插槽 -->
<slot name="aa"></slot>
<!-- 作用域插槽 -->
<slot :number="item.number"></slot>
</li>
</ul>
</template>
<script>
export default {
name: 'List',
props: {
list: { // 列表內容
type: Array,
default: () => []
}
}
}
</script>
~~~
3. **插槽總結**
3.1 **匿名插槽** 應用場景:**組件內** 只需要一個 **插槽** 的情況,可以使用 **匿名插槽** 。
3.2 **具名插槽** 應用場景:**組件內** 需要多個 **插槽** 的情況,可以使用 **具名插槽** 。
3.3 **作用域插槽**應用場景:**父組件插槽插入的內容中** 使用到了 **組件內的數據** 。
- vue 26課
- Vue-cli3.0項目搭建
- Vue-ui 創建cli3.0項目
- Vue-ui 界面詳解
- 項目目錄詳解
- public文件夾
- favicon.ico
- index.html
- src文件夾
- api文件夾
- assets文件夾
- components文件夾
- config文件夾
- directive文件夾
- lib文件夾
- mock文件夾
- mock簡明文檔
- router文件夾
- store文件夾
- views文件夾
- App.vue
- main.js
- .browserslistrc
- .editorconfig
- .eslintrc.js
- .gitignore
- babel.config.js
- package-lock.json
- package.json
- postcss.config.js
- README.en.md
- README.md
- vue.config.js
- Vue Router
- 路由詳解(一)----基礎篇
- 路由詳解(二)----進階篇
- Vuex
- Bus
- Vuex-基礎-state&getter
- Vuex-基礎-mutation&action/module
- Vuex-進階
- Ajax請求
- 解決跨域問題
- 封裝axios
- Mock.js模擬Ajax響應
- 組件封裝
- 從數字漸變組件談第三方JS庫使用
- 從SplitPane組件談Vue中如何【操作】DOM
- 渲染函數和JSX快速掌握
- 遞歸組件的使用
- 登陸/登出以及JWT認證
- 響應式布局
- 可收縮多級菜單的實現
- vue雜項
- vue遞歸組件
- vue-cli3.0多環境打包配置
- Vue+Canvas實現圖片剪切
- vue3系統入門與項目實戰
- Vue語法初探
- 初學編寫 HelloWorld 和 Counter
- 編寫字符串反轉和內容隱藏功能
- 編寫TodoList功能了解循環與雙向綁定
- 組件概念初探,對 TodoList 進行組件代碼拆分
- Vue基礎語法
- Vue 中應用和組件的基礎概念
- 理解 Vue 中的生命周期函數
- 常用模版語法講解
- 數據,方法,計算屬性和偵聽器
- 樣式綁定語法
- 條件渲染
- 列表循環渲染
- 事件綁定
- 表單中雙向綁定指令的使用
- 探索組件的理念
- 組件的定義及復用性,局部組件和全局組件
- 組件間傳值及傳值校驗
- 單向數據流的理解
- Non-Props 屬性是什么
- 父子組件間如何通過事件進行通信
- 組件間雙向綁定高級內容
- 使用匿名插槽和具名插槽解決組件內容傳遞問題
- 作用域插槽
- 動態組件和異步組件
- 基礎語法知識點查缺補漏
- Vue 中的動畫
- 使用 Vue 實現基礎的 CSS 過渡與動畫效果
- 使用 transition 標簽實現單元素組件的過渡和動畫效果
- 組件和元素切換動畫的實現
- 列表動畫
- 狀態動畫
- Vue 中的高級語法
- Mixin 混入的基礎語法
- 開發實現 Vue 中的自定義指令
- Teleport 傳送門功能
- 更加底層的 render 函數
- 插件的定義和使用
- 數據校驗插件開發實例
- Composition API
- Setup 函數的使用
- ref,reactive 響應式引用的用法和原理
- toRef 以及 context 參數
- 使用 Composition API 開發TodoList
- computed方法生成計算屬性
- watch 和 watchEffect 的使用和差異性
- 生命周期函數的新寫法
- Provide,Inject,模版 Ref 的用法
- Vue 項目開發配套工具講解
- VueCLI 的使用和單文件組件
- 使用單文件組件編寫 TodoList
- Vue-Router 路由的理解和使用
- VueX 的語法詳解
- CompositionAPI 中如何使用 VueX
- 使用 axios 發送ajax 請求
- Vue3.0(正式版) + TS
- 你好 Typescript: 進入類型的世界
- 什么是 Typescript
- 為什么要學習 Typescript
- 安裝 Typescript
- 原始數據類型和 Any 類型
- 數組和元組
- Interface- 接口初探
- 函數
- 類型推論 聯合類型和 類型斷言
- class - 類 初次見面
- 類和接口 - 完美搭檔
- 枚舉(Enum)
- 泛型(Generics) 第一部分
- 泛型(Generics) 第二部分 - 約束泛型
- 泛型第三部分 - 泛型在類和接口中的使用
- 類型別名,字面量 和 交叉類型
- 聲明文件
- 內置類型
- 總結