Render渲染函数和JSX
1.Render函数:render是用来替换temlate的,需要更灵活的模板的写法的时候,用render。
官网API地址:https://cn.vuejs.org/v2/guide/render-function.html
通常写的h为createElement的缩写,createElement
会返回虚拟节点 (virtual node)”,也常简写它为“VNode,因为它所包含的信息会告诉 Vue 页面上需要渲染什么样的节点,包括及其子节点的描述信息。
- 第一个参数为{String | Object | Function}是一个 HTML 标签名、组件选项对象,为必选项。
- 第二个参数为 {Object},是一个与模板中属性对应的数据对象。可选。
- 第三个参数为{String | Array},是子级虚拟节点 (VNodes),由 `createElement()` 构建而成,也可以使用字符串来生成“文本虚拟节点”。可选。
1.1一个最简单的render函数例子:
- import Vue from "vue";
- import App from "./App.vue";
- import router from "./router";
- import store from "./store";
- import Bus from "./lib/bus";
- import $ from "jquery";
- Vue.config.productionTip = false;
- Vue.prototype.$bus = Bus;
- new Vue({
- router,
- store,
- //render: h => h(App)
- render(h) {
- return h('div', {
- attrs: {
- id:"box"
- },
- style: {
- color:"blue"
- }
- },"Caoqi");
- },
- }).$mount("#app");
1.2当第一个参数为组件时的写法如下,引入的组件为之前博客中讲过的Count-To组件(https://www.cnblogs.com/qicao/p/10805715.html)
- import Vue from "vue";
- import App from "./App.vue";
- import router from "./router";
- import store from "./store";
- import Bus from "./lib/bus";
- import $ from "jquery";
- import CountTo from "_c/count-to";
- Vue.config.productionTip = false;
- Vue.prototype.$bus = Bus;
- new Vue({
- router,
- store,
- render: function(h) {
- return h(CountTo, {
- /**
- * class作为一个保留字必须用引号包裹
- * 接受一个字符串、对象或字符串和对象组成的数组
- */
- // 'class':'count-up wrapper',
- //class: ["count-to", true ? "classA" : "classB"],
- class: { "count-to": 1 === 1 },
- // 组件 prop
- props: { endVal: 200 },
- // DOM 属性
- domProps: {
- //innerHTML: "baz"
- },
- /**
- * 事件监听器在 `on` 属性内,
- * 但不再支持如 `v-on:keyup.enter` 这样的修饰器。需要在处理函数中手动检查 keyCode。
- */
- on: {
- "on-animation-end": function(val) {
- console.log("animation end");
- }
- },
- /**
- * 仅用于组件,用于监听原生事件,而不是组件内部使用`vm.$emit` 触发的事件。
- */
- nativeOn: {
- click: () => console.log("I am clicked!")
- },
- /*自定义指令*/
- directives: [],
- // 如果组件是其它组件的子组件,需为插槽指定名称
- slot: "name-of-slot"
- });
- }
- }).$mount("#app");
显示效果:
1.3 创建子级虚拟节点:
- import Vue from "vue";
- import App from "./App.vue";
- import router from "./router";
- import store from "./store";
- import Bus from "./lib/bus";
- import $ from "jquery";
- import CountTo from "_c/count-to";
- Vue.config.productionTip = false;
- Vue.prototype.$bus = Bus;
- new Vue({
- router,
- store,
- /**
- * @param {String | Array} h
- * 子级虚拟节点 (VNodes),由 `createElement()` 构建而成,也可以使用字符串来生成“文本虚拟节点”。可选。
- */
- //render:h=>h('div','123')
- render:function(h) {
- return h('div', [
- h('span','span1'),
- h('span','span2'),
- ])
- }
- }).$mount("#app");
1.4使用 JavaScript 代替模板功能
模板代码:
- <template>
- <div>
- <ul @click="handlerClick">
- <li
- @click.stop="handlerClick"
- v-for="(item,index) in list"
- :key="`list_item_${index}`"
- >{{item.name}}</li>
- </ul>
- </div>
- </template>
- <script>
- export default {
- data() {
- return {
- list: [
- {
- name: "张三"
- },
- {
- name: "李四"
- }
- ]
- };
- },
- methods: {
- handlerClick: function(event) {
- console.log(event);
- }
- }
- };
- </script>
main.js:
- import Vue from "vue";
- import App from "./App.vue";
- import router from "./router";
- import store from "./store";
- import Bus from "./lib/bus";
- import $ from "jquery";
- import CountTo from "_c/count-to";
- Vue.config.productionTip = false;
- Vue.prototype.$bus = Bus;
- new Vue({
- router,
- store,
- render: h => h(App)
- }).$mount("#app");
若使用render函数中的js代替模板:只要在原生的 JavaScript 中可以轻松完成的操作,Vue 的渲染函数就不会提供专有的替代方法。比如,在模板中使用的 v-if
和 v-for需要用JavaScript 的
if
/else
和 map
来重写:
- import Vue from "vue";
- import App from "./App.vue";
- import router from "./router";
- import store from "./store";
- import Bus from "./lib/bus";
- import $ from "jquery";
- import CountTo from "_c/count-to";
- Vue.config.productionTip = false;
- Vue.prototype.$bus = Bus;
- const handleClick = event => {
- console.log(event);
- event.stopPropagation();
- };
- let list = [{ name: "张三" }, { name: "李四" }];
- /**
- * Array map用法:
- * 功能:将原数组映射成新数组
- * https://www.zhangxinxu.com/wordpress/2013/04/es5%E6%96%B0%E5%A2%9E%E6%95%B0%E7%BB%84%E6%96%B9%E6%B3%95/#map
- */
- const getLiEleArr = h => {
- return list.map((item,index) =>
- h(
- "li",
- {
- on: {
- click: handleClick
- },
- key:`list_item_${index}`
- },
- item.name
- )
- );
- };
- /**等效于 */
- /*function getLiEleArr(h) {
- return list.map(function(item, index) {
- return h(
- "li",
- {
- on: {
- click: handleClick
- },
- key: `list_item_${index}`
- },
- item.name
- );
- });
- }*/
- new Vue({
- router,
- store,
- render: function(h) {
- return h(
- "ul",
- {
- on: {
- click: handleClick
- }
- },
- getLiEleArr(h)
- );
- }
- }).$mount("#app");
2.函数式组件:函数组件可以用render方式,可以用模板方式。函数组件主要用来做组件的外壳,也就是写模板之前,可以先对传进来的上下文做一些处理。这个“壳”的作用有点类似模板语法里的<template>标签,本身不会渲染,只是做包裹。
官方API地址:https://cn.vuejs.org/v2/guide/render-function.html#%E5%87%BD%E6%95%B0%E5%BC%8F%E7%BB%84%E4%BB%B6
我们可以把函数式组件想像成组件里的一个函数,输入参数是渲染上下文(render context),返回值是渲染好的HTML
对于函数式组件,可以这样定义:
- Stateless(无状态):组件自身是没有状态的
- Instanceless(无实例):组件自身没有实例,也就是没有this,事件只能由父组件传递
例子:
- <div id="app">
- <smart-list :items=items></smart-list>
- </div>
app.vue:
- //当父组件传过来的是空items时
- var EmptyList = {template: '<p>Empty list</p>'};
- //当父组件传来的items元素为对象类型时
- var TableList = 'ul'
- // 当父组件定义了isOrdered变量且为true
- var UnorderedList = 'ul'
- //定义组件
- Vue.component('smart-list', {
- //标记为函数式组件
- functional: true,
- //render函数
- render: function (createElement, context) {
- // console.log(context)//若不理解可以打印出来context来看看里面都有些什么东西
- //规定组件的渲染规则
- function appropriateListComp() {
- //获取父组件传来的数据
- var items = context.props.items;
- //若空,则返回前面定义的emptylist
- if (items.length === 0) return EmptyList;
- //若为对象
- if (typeof items[0] === 'object') return TableList;
- //其他
- return UnorderedList
- }
- //生成模板
- return createElement(
- //模板标记为渲染规则函数返回值
- appropriateListComp(),
- //模板子元素,返回一个数组
- Array.apply(null, {length: context.props.items.length}).map(function (value, index) {
- return createElement('li',context.props.items[index].name)
- })
- )
- },
- props: {
- items: {
- type: Array,
- required: true
- },
- isOrdered: Boolean
- }
- });
- new Vue({
- el: '#app',
- data:{
- items:[
- {
- name:'a',
- id:0
- },
- {
- name:'b',
- id:1
- },
- {
- name:'c',
- id:2
- }
- ]
- }
- })
最终浏览器渲染结果:
- <div id="app">
- <ul>
- <li>a</li>
- <li>b</li>
- <li>c</li>
- </ul>
- </div>
再举一个例子,代码目录结构如下:
render-page:
- <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: function(h, name) {
- return h("i", {
- style: {
- color: "pink"
- }
- },name);
- }
- }
- };
- </script>
router.js:
- import Home from "@/views/Home.vue";
- export default [
- {
- path: "/",
- alias: "/home_page",
- name: "home", //加上name属性 命名路由
- component: Home,
- props: route => ({
- food: route.query.food
- }),
- beforeEnter: (to, from, next) => {
- // if (from.name === "about") alert("这是从about来的");
- // else alert("这不是从about来的");
- next();
- }
- },
- {
- path: '/render_page',
- name: 'render_page',
- component: () => import('@/views/render-page.vue')
- }
- {
- path: "*",
- component: () => import("@/views/error_404.vue")
- }
- ];
list/index.js:
- import List from "./list.vue";
- export default List;
list/list.vue:
- <template>
- <ul>
- <li v-for="(item,index) in list" :key="`item_${index}`">
- <span v-if="!render">{{item.name}}</span>
- <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: {
- type: Function,
- default: () => {}
- }
- }
- };
- </script>
render-dom.js:
- export default {
- functional: true,
- // Props 是可选的
- props: {
- name: String,
- renderFunc: Function
- },
- // 为了弥补缺少的实例,提供第二个参数作为上下文
- render: (h, ctx) => {
- return ctx.props.renderFunc(h, ctx.props.name);
- }
- };
运行效果:
JSX(可参考https://juejin.im/post/5affa64df265da0b93488fdd):
什么是JSX:
JSX就是Javascript和XML结合的一种格式。React发明了JSX,利用HTML语法来创建虚拟DOM。当遇到<,JSX就当HTML解析,遇到{就当JavaScript解析。
将刚才的例子中的render-page.vue中的render函数改为jsx语法(它可以让我们回到更接近于模板的语法上):
- <template>
- <div>
- <list :list="list" :render="render"></list>
- </div>
- </template>
- <script>
- import List from "_c/list";
- export default {
- data() {
- return {
- list: [
- {
- name: "张三"
- },
- {
- name: "李四"
- }
- ]
- };
- },
- components: {
- List
- },
- methods: {
- render: function(h, name) {
- // return h("i", {
- // style: {
- // color: "pink"
- // }
- // },name);
- return (
- ////绑定事件需要用on前缀
- <i on-click={this.handlerClick} style={{color:'pink'}}>{name}</i>
- );
- },
- handlerClick(event){
- console.log(event);
- }
- }
- };
- </script>
在JSX中使用组件(使用和上一个例子同样的目录结构):
注意在render或者JSX中写的组件是不需要在components中注册的。
render-page.vue:
- <template>
- <div>
- <list :list="list" :render="render"></list>
- </div>
- </template>
- <script>
- import List from "_c/list";
- import CountTo from "_c/count-to";
- export default {
- data() {
- return {
- list: [
- {
- number: 100
- },
- {
- number: 200
- }
- ]
- };
- },
- components: {
- List
- },
- methods: {
- render: function(h, number) {
- return (
- //绑定事件需要用on-开头 绑定原生事件用nativeOn-开头
- <CountTo nativeOn-click={this.nativeHandlerOn} on-on-animation-end={this.handlerEnd} endVal={number} />
- );
- },
- handlerClick(event) {
- console.log(event);
- },
- handlerEnd() {
- console.log("End!!!");
- },
- nativeHandlerOn(){
- console.log("这是原生事件");
- }
- }
- };
- </script>
list.vue:
- <template>
- <ul>
- <li v-for="(item,index) in list" :key="`item_${index}`">
- <span v-if="!render">{{item.number}}</span>
- <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: {
- type: Function,
- default: () => {}
- }
- }
- };
- </script>
render-dom.js:
- export default {
- functional: true,
- // Props 是可选的
- props: {
- number: Number,
- renderFunc: Function
- },
- // 为了弥补缺少的实例,提供第二个参数作为上下文
- render: (h, ctx) => {
- return ctx.props.renderFunc(h, ctx.props.number);
- }
- };
效果图:
作用域插槽
作用域插槽就是父组件在调用子组件的时候给子组件传了一个插槽,这个插槽为作用域插槽,该插槽必须放在template标签里面,同时声明从子组件接收的数据放在一个自定义属性内,并定义该数据的渲染方式。适合的场景是至少包含三级以上的组件层级,是一种优秀的组件化方案!
render-page.vue:
- <template>
- <div>
- <list :list="list">
- <count-to slot-scope="count" :endVal="count.number"></count-to>
- </list>
- </div>
- </template>
- <script>
- import List from "_c/list";
- import CountTo from "_c/count-to";
- export default {
- data() {
- return {
- list: [
- {
- number: 100
- },
- {
- number: 200
- }
- ]
- };
- },
- components: {
- List,
- CountTo
- },
- methods: {
- }
- };
- </script>
list.vue:
- <template>
- <ul>
- <li v-for="(item,index) in list" :key="`item_${index}`">
- <!-- <span v-if="!render">{{item.number}}</span>
- <render-dom v-else :render-func="render" :number="item.number"></render-dom> -->
- <slot :number="item.number"></slot>
- </li>
- </ul>
- </template>
- <script>
- import RenderDom from "_c/render-dom";
- export default {
- name: "List",
- components: {
- RenderDom
- },
- props: {
- list: {
- type: Array,
- default: () => []
- },
- render: {
- type: Function,
- default: () => {}
- }
- }
- };
- </script>
Render渲染函数和JSX的更多相关文章
- Vue之render渲染函数和JSX的应用
一.模板缺陷 模板的最大特点是扩展难度大,不易扩展.可能会造成逻辑冗余 <Level :type="1">哈哈</Level> <Level :typ ...
- Vue.js 2.x render 渲染函数 & JSX
Vue.js 2.x render 渲染函数 & JSX Vue绝大多数情况下使用template创建 HTML.但是比如一些重复性比较高的场景,需要运用 JavaScript 的完全编程能力 ...
- Vue.js 渲染函数, JSX(未掌握,未学完)
渲染函数 , JSX(没完成学习) 基础: 实例属性:vm.$slots default 属性包括了所有没有被包含在具名插槽中的节点. 渲染函数: render: function(createEle ...
- Vue躬行记(7)——渲染函数和JSX
除了可通过模板创建HTML之外,Vue还提供了渲染函数和JSX,前者的编码自由度很高,后者对于开发过React的人来说会很熟悉.注意,Vue的模板最终都会被编译成渲染函数. 一.渲染函数 虽然在大部分 ...
- 理解Vue中的Render渲染函数
理解Vue中的Render渲染函数 VUE一般使用template来创建HTML,然后在有的时候,我们需要使用javascript来创建html,这时候我们需要使用render函数.比如如下我想要实现 ...
- 基于VueJS的render渲染函数结合自定义组件打造一款非常强大的IView 的Table
基于VueJS的render渲染函数结合自定义组件打造一款非常强大的IView 的Table https://segmentfault.com/a/1190000015970367
- vue render 渲染函数
vue render 渲染函数 经常看到使用render渲染函数的示例,而且在一些特殊情况下,确实更好使用,可以更加有效地细分组件,因而借助vue-element-admin来学习一波 render函 ...
- 必备技能三、render渲染函数
Vue 推荐使用在绝大多数情况下使用 template 来创建你的 HTML.然而在一些场景中,你真的需要 JavaScript 的完全编程的能力,这就是 render 函数,它比 template ...
- 【js】vue 2.5.1 源码学习 (十一) 模板编译compileToFunctions渲染函数
大体思路(九) 本节内容: 1. compileToFunctions定位 1. compileToFunctions定位 ==> createCompiler = createCompiler ...
随机推荐
- 《深入理解Java虚拟机》笔记02 -- 垃圾收集算法
1. 标记 - 清除算法 先标记出所有需要回收的对象,在标记完成后统一回收所有被标记的对象.它是最基础的收集算法.其他收集算法都是根据其思路,改进其不足之处. 缺点:1) 标记和清除两个阶段的效率都不 ...
- uoj#348/洛谷P4221 [WC2018]州区划分(FWT)
传送门(uoj) 传送门(洛谷) 全世界都会子集卷积就咱不会--全世界都在写\(FMT\)就咱只会\(FWT\)-- 前置芝士 或运算\(FWT\)或者\(FMT\) 左转洛谷模板区,包教包会 子集卷 ...
- IT兄弟连 JavaWeb教程 AJAX定义以及解决的问题
Ajax是"Asynchronous JavaScript And XML"的缩写(即:异步的JavaScript和XML),是一种实现无页面刷新获取服务器数据的混合技术,Ajax ...
- Python学习笔记(字典)
今天学习一个python中的基本类型--字典(dictionary) 字典这种数据结构有点像我们平常用的通讯录,有一个名字和这个名字对应的信息.在字典中,名字叫做“键”,对应的内容信息叫做“值”.字典 ...
- 剑指Offer的学习笔记(C#篇)-- 用两个栈实现队列
题目描述 用两个栈来实现一个队列,完成队列的Push和Pop操作. 队列中的元素为int类型. 一 . 概念! 首先要理解栈和队列的概念. 1. 栈:咱可以简单的把栈理解成装羽毛球的球桶.或者我们吃的 ...
- PHP闭包和匿名函数
概念 闭包和匿名函数在PHP5.3.0中被引入. 闭包 闭包是指创建时封装周围环境的函数.即使闭包所在的环境不存在了,闭包中封装的状态依然存在.这个概念很难理解,不过没关系,继续看下去就会明白了. 匿 ...
- Jeasyui的datagrid前端分页要点
Jeasyui的分页有两种方式: 1. 服务器端分页,是真正的分页,datagridview的pager会自动把pageSize和pageNum传到后台,后台根据根据pageSize和pageNum构 ...
- Linux操作学习笔记1
Linux只有一个根目录/,所有的文件和设备都当成是文件进行管理: pwd 打印当前工作目录 (print working directory) whoami ls 列出当前目录面的文件 ls -l ...
- CSS——三种页面引入方法
目的:为了把样式和内容分开,并且使网页元素更加丰富,引入了CSS CSS页面引入有三种方式: 1)内联式:比较不常用,因为内容和样式仍然在一起,不方便.示例: <!DOCTYPE html> ...
- reaver 破解wifi
1. 无线网卡设置为监控模式 airmon-ng start wlan0 2. 获取附近路由信息 airodump-ng wlan0mon 3. 使用reaver破解wifi reaver -i wl ...