精选文章 Vue render函数

Vue render函数

作者:黄杏波 时间: 2020-07-28 08:48:59
黄杏波 2020-07-28 08:48:59

前几天想学学Vue中怎么编写可复用的组件,提到要对Vue的render函数有所了解。可仔细一想,对于Vue的render函数自己只是看了官方的一些介绍,并未深入一点去了解这方面的知识。为了更好的学习后续的知识,又折回来了解Vue中的render函数,这一切主要都是为了后续能更好的学习Vue的知识。

回忆Vue的一些基本概念

今天我们学习的目的是了解和学习Vue的render函数。如果想要更好的学习Vue的render函数相关的知识,我们有必要重温一下Vue中的一些基本概念。那么先上一张图,这张图从宏观上展现了Vue整体流程:

Vue render函数1

image

从上图中,不难发现一个Vue的应用程序是如何运行起来的,模板通过编译生成AST,再由AST生成Vue的render函数(渲染函数),渲染函数结合数据生成Virtual DOM树,Diff和Patch后生成新的UI。从这张图中,可以接触到Vue的一些主要概念:

  • 模板:Vue的模板基于纯HTML,基于Vue的模板语法,我们可以比较方便地声明数据和UI的关系。
  • AST:AST是Abstract Syntax Tree的简称,Vue使用HTML的Parser将HTML模板解析为AST,并且对AST进行一些优化的标记处理,提取最大的静态树,方便Virtual DOM时直接跳过Diff。
  • 渲染函数:渲染函数是用来生成Virtual DOM的。Vue推荐使用模板来构建我们的应用界面,在底层实现中Vue会将模板编译成渲染函数,当然我们也可以不写模板,直接写渲染函数,以获得更好的控制 (这部分是我们今天主要要了解和学习的部分)。
  • Virtual DOM:虚拟DOM树,Vue的Virtual DOM Patching算法是基于Snabbdom的实现,并在些基础上作了很多的调整和改进。
  • Watcher:每个Vue组件都有一个对应的watcher,这个watcher将会在组件render的时候收集组件所依赖的数据,并在依赖有更新的时候,触发组件重新渲染。你根本不需要写shouldComponentUpdate,Vue会自动优化并更新要更新的UI。

上图中,render函数可以作为一道分割线,render函数的左边可以称之为编译期,将Vue的模板转换为渲染函数render函数的右边是Vue的运行时,主要是基于渲染函数生成Virtual DOM树,Diff和Patch。

渲染函数的基础

Vue推荐在绝大多数情况下使用template来创建你的HTML。然而在一些场景中,需要使用JavaScript的编程能力和创建HTML,这就是render函数,它比template更接近编译器。

 

Hello world!

在HTML层,我们决定这样定义组件接口:

 

Hello world!

当我们开始写一个通过levelprop动态生成heading标签的组件,你可能很快想到这样实现:

 





Vue.component('anchored-heading', {
    template: '#anchored-heading-template',
    props: {
        level: {
            type: Number,
            required: true
        }
    }
})

在这种场景中使用 template 并不是最好的选择:首先代码冗长,为了在不同级别的标题中插入锚点元素,我们需要重复地使用

虽然模板在大多数组件中都非常好用,但是在这里它就不是很简洁的了。那么,我们来尝试使用 render 函数重写上面的例子:

 

Vue.component('anchored-heading', {
    render: function (createElement) {
        return createElement(
            'h' + this.level,   // tag name 标签名称
            this.$slots.default // 子组件中的阵列
        )
    },
    props: {
        level: {
            type: Number,
            required: true
        }
    }
})

简单清晰很多!简单来说,这样代码精简很多,但是需要非常熟悉 Vue 的实例属性。在这个例子中,你需要知道当你不使用 slot 属性向组件中传递内容时,比如 anchored-heading 中的 Hello world!,这些子元素被存储在组件实例中的 $slots.default中。

节点、树以及虚拟DOM

对Vue的一些概念和渲染函数的基础有一定的了解之后,我们需要对一些浏览器的工作原理有一些了解,这样对我们学习render函数是很重要的。比如下面的这段HTML代码:

 

My title

Some text content

当浏览器读到这些代码时,它会建立一个DOM节点树来保持追踪,如果你会画一张家谱树来追踪家庭成员的发展一样。

HTML的DOM节点树如下图所示:

Vue render函数2

image

每个元素都是一个节点。每片文字也是一个节点。甚至注释也都是节点。一个节点就是页面的一个部分。就像家谱树一样,每个节点都可以有孩子节点 (也就是说每个部分可以包含其它的一些部分)。

高效的更新所有这些节点会是比较困难的,不过所幸你不必再手动完成这个工作了。你只需要告诉 Vue 你希望页面上的 HTML 是什么,这可以是在一个模板里:

 

{{ blogTitle }}

或者一个渲染函数里:

 

render: function (createElement) {
    return createElement('h1', this.blogTitle)
}

在这两种情况下,Vue 都会自动保持页面的更新,即便 blogTitle 发生了改变。

虚拟DOM

在Vue 2.0中,渲染层的实现做了根本性改动,那就是引入了虚拟DOM。

Vue render函数3

image

Vue的编译器在编译模板之后,会把这些模板编译成一个渲染函数。而函数被调用的时候就会渲染并且返回一个虚拟DOM的树

当我们有了这个虚拟的树之后,再交给一个Patch函数,负责把这些虚拟DOM真正施加到真实的DOM上。在这个过程中,Vue有自身的响应式系统来侦测在渲染过程中所依赖到的数据来源。在渲染过程中,侦测到数据来源之后就可以精确感知数据源的变动。到时候就可以根据需要重新进行渲染。当重新进行渲染之后,会生成一个新的树,将新的树与旧的树进行对比,就可以最终得出应施加到真实DOM上的改动。最后再通过Patch函数施加改动。

简单点讲,在Vue的底层实现上,Vue将模板编译成虚拟DOM渲染函数。结合Vue自带的响应系统,在应该状态改变时,Vue能够智能地计算出重新渲染组件的最小代价并应到DOM操作上。

Vue render函数4

image

Vue支持我们通过data参数传递一个JavaScript对象做为组件数据,然后Vue将遍历此对象属性,使用Object.defineProperty方法设置描述对象,通过存取器函数可以追踪该属性的变更,Vue创建了一层Watcher层,在组件渲染的过程中把属性记录为依赖,之后当依赖项的setter被调用时,会通知Watcher重新计算,从而使它关联的组件得以更新,如下图:

Vue render函数5

image

有关于Vue的响应式相关的内容,可以阅读下列文章:

对于Vue自带的响应式系统,并不是咱们今天要聊的东西。我们还是回到Vue的虚拟DOM中来。对于虚拟DOM,咱们来看一个简单的实例,就是下图所示的这个,详细的阐述了模板 → 渲染函数 → 虚拟DOM树 → 真实DOM的一个过程

Vue render函数6

image

其实Vue中的虚拟DOM还是很复杂的,我也是一知半解,如果你想深入的了解,可以阅读@JoeRay61的《Vue原理解析之Virtual DOM》一文。

通过前面的学习,我们初步了解到Vue通过建立一个虚拟DOM对真实DOM发生的变化保持追踪。比如下面这行代码:

 

return createElement('h1', this.blogTitle)

createElement 到底会返回什么呢?其实不是一个实际的 DOM 元素。它更准确的名字可能是 createNodeDescription,因为它所包含的信息会告诉 Vue 页面上需要渲染什么样的节点,及其子节点。我们把这样的节点描述为“虚拟节点 (Virtual Node)”,也常简写它为“VNode”。“虚拟 DOM”是我们对由 Vue 组件树建立起来的整个 VNode 树的称呼。

Vue组件树建立起来的整个VNode树是唯一的。这意味着,下面的render函数是无效的:

 

render: function (createElement) {
    var myParagraphVNode = createElement('p', 'hi')
    return createElement('div', [
        // 错误-重复的 VNodes
        myParagraphVNode, myParagraphVNode
    ])
}

如果你真的需要重复很多次的元素/组件,你可以使用工厂函数来实现。例如,下面这个例子 render 函数完美有效地渲染了 20 个重复的段落:

 

render: function (createElement) {
    return createElement('div',
        Array.apply(null, { length: 20 }).map(function () {
            return createElement('p', 'hi')
        })
    )
}

Vue的渲染机制

Vue render函数7

image

上图展示的是独立构建时的一个渲染流程图。

继续使用上面用到的模板到真实DOM过程的一个图:

Vue render函数8

image

这里会涉及到Vue的另外两个概念:

  • 独立构建:包含模板编译器,渲染过程HTML字符串 → render函数 → VNode → 真实DOM节点
  • 运行时构建:不包含模板编译器,渲染过程render函数 → VNode → 真实DOM节点

运行时构建的包,会比独立构建少一个模板编译器。在$mount函数上也不同。而$mount方法又是整个渲染过程的起始点。用一张流程图来说明:

Vue render函数9

image

由此图可以看到,在渲染过程中,提供了三种渲染模式,自定义render函数、templateel均可以渲染页面,也就是对应我们使用Vue时,三种写法:

自定义render函数

 

Vue.component('anchored-heading', {
    render: function (createElement) {
        return createElement (
            'h' + this.level,   // tag name标签名称
            this.$slots.default // 子组件中的阵列
        )
    },
    props: {
        level: {
            type: Number,
            required: true
        }
    }
})

template写法

 

let app = new Vue({
    template: `
{{ msg }}
`, data () { return { msg: '' } } })

el写法

 

let app = new Vue({
    el: '#app',
    data () {
        return {
            msg: 'Hello Vue!'
        }
    }
})

这三种渲染模式最终都是要得到render函数。只不过用户自定义的render函数省去了程序分析的过程,等同于处理过的render函数,而普通的template或者el只是字符串,需要解析成AST,再将AST转化为render函数。

记住一点,无论哪种方法,都要得到render函数。

我们在使用过程中具体要使用哪种调用方式,要根据具体的需求来。

如果是比较简单的逻辑,使用templateel比较好,因为这两种都属于声明式渲染,对用户理解比较容易,但灵活性比较差,因为最终生成的render函数是由程序通过AST解析优化得到的;而使用自定义render函数相当于人已经将逻辑翻译给程序,能够胜任复杂的逻辑,灵活性高,但对于用户的理解相对差点。

理解createElement

在使用render函数,其中还有另一个需要掌握的部分,那就是createElement。接下来我们需要熟悉的是如何在createElement函数中生成模板。那么我们分两个部分来对createElement进行理解。

createElement参数

createElement可以是接受多个参数:

第一个参数:{String | Object | Function}

第一个参数对于createElement而言是一个必须的参数,这个参数可以是字符串string、是一个对象object,也可以是一个函数function

 

Vue.component('custom-element', { render: function (createElement) { return createElement('div') } }) let app = new Vue({ el: '#app' })

上面的示例,给createElement传了一个String参数'div',即传了一个HTML标签字符。最后会有一个div元素渲染出来:

Vue render函数10

image

接着把上例中的String换成一个Object,比如:

 

Vue.component('custom-element', {
    render: function (createElement) {
        return createElement({
            template: `
Hello Vue!
` }) } })

上例传了一个{template: '

Hello Vue!
'}对象。此时custom-element组件渲染出来的结果如下:

Vue render函数11

image

除此之外,还可以传一个Function,比如:

 

Vue.component('custom-element', {
    render: function (createElement) {
        var eleFun = function () {
            return {
                template: `
Hello Vue!
` } } return createElement(eleFun()) } })

最终得到的结果和上图是一样的。这里传了一个eleFun()函数给createElement,而这个函数返回的是一个对象。

第二个参数:{Object}

createElement是一个可选参数,这个参数是一个Object。来看一个小示例:

 

Vue.component('custom-element', { render: function (createElement) { var self = this // 第一个参数是一个简单的HTML标签字符 “必选” // 第二个参数是一个包含模板相关属性的数据对象 “可选” return createElement('div', { 'class': { foo: true, bar: false }, style: { color: 'red', fontSize: '14px' }, attrs: { id: 'boo' }, domProps: { innerHTML: 'Hello Vue!' } }) } }) let app = new Vue({ el: '#app' })

最终生成的DOM,将会带一些属性和内容的div元素,如下图所示:

Vue render函数12

image

第三个参数:{String | Array}

createElement还有第三个参数,这个参数是可选的,可以给其传一个StringArray。比如下面这个小示例:

 

Vue.component('custom-element', { render: function (createElement) { var self = this return createElement( 'div', // 第一个参数是一个简单的HTML标签字符 “必选” { class: { title: true }, style: { border: '1px solid', padding: '10px' } }, // 第二个参数是一个包含模板相关属性的数据对象 “可选” [ createElement('h1', 'Hello Vue!'), createElement('p', '开始学习Vue!') ] // 第三个参数是传了多个子元素的一个数组 “可选” ) } }) let app = new Vue({ el: '#app' })

最终的效果如下:

Vue render函数13

image

其实从上面这几个小例来看,不难发现,以往我们使用Vue.component()创建组件的方式,都可以用render函数配合createElement来完成。你也会发现,使用Vue.component()render各有所长,正如文章开头的一个示例代码,就不适合Vue.component()template,而使用render更方便。

接下来看一个小示例,看看templaterender方式怎么创建相同效果的一个组件:

 

Vue.component('custom-element', { template: `
Hello Vue!
`, data () { return { show: true } }, methods: { handleClick: function () { console.log('Clicked!') } } })

上面Vue.component()中的代码换成render函数之后,可以这样写:

 

Vue.component('custom-element', {
    render: function (createElement) {
        return createElement('div', {
            class: {
                show: this.show
            },
            attrs: {
                id: 'box'
            },
            on: {
                click: this.handleClick
            }
        }, 'Hello Vue!')
    },
    data () {
        return {
            show: true
        }
    },
    methods: {
        handleClick: function () {
            console.log('Clicked!')
        }
    }
})

最后声明一个Vue实例,并挂载到id#app的一个元素上:

 

let app = new Vue({
    el: '#app'
})

createElement解析过程

简单的来看一下createElement解析的过程,这部分需要对JS有一些功底。不然看起来有点蛋疼:

 

const SIMPLE_NORMALIZE = 1
const ALWAYS_NORMALIZE = 2

function createElement (context, tag, data, children, normalizationType, alwaysNormalize) {

    // 兼容不传data的情况
    if (Array.isArray(data) || isPrimitive(data)) {
        normalizationType = children
        children = data
        data = undefined
    }

    // 如果alwaysNormalize是true
    // 那么normalizationType应该设置为常量ALWAYS_NORMALIZE的值
    if (alwaysNormalize) normalizationType = ALWAYS_NORMALIZE
        // 调用_createElement创建虚拟节点
        return _createElement(context, tag, data, children, normalizationType)
    }

    function _createElement (context, tag, data, children, normalizationType) {
        /**
        * 如果存在data.__ob__,说明data是被Observer观察的数据
        * 不能用作虚拟节点的data
        * 需要抛出警告,并返回一个空节点
        * 
        * 被监控的data不能被用作vnode渲染的数据的原因是:
        * data在vnode渲染过程中可能会被改变,这样会触发监控,导致不符合预期的操作
        */
        if (data && data.__ob__) {
            process.env.NODE_ENV !== 'production' && warn(
            `Avoid using observed data object as vnode data: ${JSON.stringify(data)}\n` +
            'Always create fresh vnode data objects in each render!',
            context
            )
            return createEmptyVNode()
        }

        // 当组件的is属性被设置为一个falsy的值
        // Vue将不会知道要把这个组件渲染成什么
        // 所以渲染一个空节点
        if (!tag) {
            return createEmptyVNode()
        }

        // 作用域插槽
        if (Array.isArray(children) && typeof children[0] === 'function') {
            data = data || {}
            data.scopedSlots = { default: children[0] }
            children.length = 0
        }

        // 根据normalizationType的值,选择不同的处理方法
        if (normalizationType === ALWAYS_NORMALIZE) {
            children = normalizeChildren(children)
        } else if (normalizationType === SIMPLE_NORMALIZE) {
            children = simpleNormalizeChildren(children)
        }
        let vnode, ns

        // 如果标签名是字符串类型
        if (typeof tag === 'string') {
            let Ctor
            // 获取标签名的命名空间
            ns = config.getTagNamespace(tag)

            // 判断是否为保留标签
            if (config.isReservedTag(tag)) {
                // 如果是保留标签,就创建一个这样的vnode
                vnode = new VNode(
                    config.parsePlatformTagName(tag), data, children,
                    undefined, undefined, context
                )

                // 如果不是保留标签,那么我们将尝试从vm的components上查找是否有这个标签的定义
            } else if ((Ctor = resolveAsset(context.$options, 'components', tag))) {
                // 如果找到了这个标签的定义,就以此创建虚拟组件节点
                vnode = createComponent(Ctor, data, context, children, tag)
            } else {
                // 兜底方案,正常创建一个vnode
                vnode = new VNode(
                    tag, data, children,
                    undefined, undefined, context
                )
            }

        // 当tag不是字符串的时候,我们认为tag是组件的构造类
        // 所以直接创建
        } else {
            vnode = createComponent(tag, data, context, children)
        }

        // 如果有vnode
        if (vnode) {
            // 如果有namespace,就应用下namespace,然后返回vnode
            if (ns) applyNS(vnode, ns)
            return vnode
        // 否则,返回一个空节点
        } else {
            return createEmptyVNode()
        }
    }
}

简单的梳理了一个流程图,可以参考下

Vue render函数14

image

这部分代码和流程图来自于@JoeRay61的《Vue原理解析之Virtual DOM》一文。

使用JavaScript代替模板功能

在使用Vue模板的时候,我们可以在模板中灵活的使用v-ifv-forv-model之类的。但在render函数中是没有提供专用的API。如果在render使用这些,需要使用原生的JavaScript来实现。

v-ifv-for

render函数中可以使用if/elsemap来实现template中的v-ifv-for

 

  • {{ item }}

No items found.

换成render函数,可以这样写:

 

Vue.component('item-list',{
    props: ['items'],
    render: function (createElement) {
        if (this.items.length) {
            return createElement('ul', this.items.map((item) => {
                return createElement('item')
            }))
        } else {
            return createElement('p', 'No items found.')
        }
    }
})

let app = new Vue({ el: '#app', data () { return { items: ['大漠', 'W3cplus', 'blog'] } } })

得到的效果如下:

Vue render函数15

image

v-model

render函数中也没有与v-model相应的API,如果要实现v-model类似的功能,同样需要使用原生JavaScript来实现。

 

Vue.component('el-input', { render: function (createElement) { var self = this return createElement('input', { domProps: { value: self.name }, on: { input: function (event) { self.$emit('input', event.target.value) } } }) }, props: { name: String } }) let app = new Vue({ el: '#app', data () { return { name: '大漠' } } })

刷新你的浏览器,可以看到效果如下:

Vue render函数16

image

这就是深入底层要付出的,尽管麻烦了一些,但相对于 v-model 来说,你可以更灵活地控制。

插槽

你可以从this.$slots获取VNodes列表中的静态内容:

 

render: function (createElement) {
    // 相当于 `
` return createElement('div', this.$slots.default) }

还可以从this.$scopedSlots中获得能用作函数的作用域插槽,这个函数返回VNodes:

 

props: ['message'],
render: function (createElement) {
    // `
` return createElement('div', [ this.$scopedSlots.default({ text: this.message }) ]) }

如果要用渲染函数向子组件中传递作用域插槽,可以利用VNode数据中的scopedSlots域:

 

Vue.component('custom-ele', { render: function (createElement) { return createElement('div', [ createElement('child', { scopedSlots: { default: function (props) { return [ createElement('span', 'From Parent Component'), createElement('span', props.text) ] } } }) ]) } }) Vue.component('child', { render: function (createElement) { return createElement('strong', this.$scopedSlots.default({ text: 'This is Child Component' })) } }) let app = new Vue({ el: '#app' })

JSX

如果写习惯了template,然后要用render函数来写,一定会感觉好痛苦,特别是面对复杂的组件的时候。不过我们在Vue中使用JSX可以让我们回到更接近于模板的语法上。

 

import AnchoredHeading from './AnchoredHeading.vue'

new Vue({
    el: '#demo',
    render: function (h) {
        return (
            
                Hello world!
            
        )
    }
})

h 作为 createElement 的别名是 Vue 生态系统中的一个通用惯例,实际上也是 JSX 所要求的,如果在作用域中 h 失去作用,在应用中会触发报错。

总结

回过头来看,Vue中的渲染核心关键的几步流程还是非常清晰的:

  • new Vue,执行初始化
  • 挂载$mount方法,通过自定义render方法、templateel等生成render函数
  • 通过Watcher监听数据的变化
  • 当数据发生变化时,render函数执行生成VNode对象
  • 通过patch方法,对比新旧VNode对象,通过DOM Diff算法,添加、修改、删除真正的DOM元素

至此,整个new Vue的渲染过程完毕。

而这篇文章,主要把精力集中在render函数这一部分。学习了怎么用render函数来创建组件,以及了解了其中createElement

最后要说的是,上文虽然以学习render函数,但文中涉及了Vue不少的知识点,也有点零乱。初学者自己根据自己获取所要的知识点。由于本人也是初涉Vue相关的知识点,如果文章中有不对之处,烦请路过的大神拍正。

此文转载于:[https://www.w3cplus.com/vue/vue-render-function.html]



作者:kangaroo_v
链接:https://www.jianshu.com/p/7508d2a114d3
来源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

勿删,copyright占位
分享文章到微博
分享文章到朋友圈

上一篇:MQ消息队列详解7:ActiveMQ连接协议说明

下一篇:VIM 库存周转率报表(开发笔记)

您可能感兴趣

  • Spring Security 中的四种权限控制方式

    江南一点雨 公众号:江南一点雨,书《Spring boot+Vue实战》 9 人赞同了该文章 Spring Security 中对于权限控制默认已经提供了很多了,但是,一个优秀的框架必须具备良好的扩展性,恰好,Spring Security 的扩展性就非常棒,我们既可以使用 Spring Security 提供的方式做授权,也可以自定义授权逻辑。一句话,你想怎么玩都可以! 今天松哥来和大家介...

  • Vue.js简单开发使用

    步骤一:安装node.js 在搭建vue的开发环境之前,需要先下载node.js,vue的运行是要依赖于node的npm的管理工具来实现,node可以在官网或者中文网里面下载,根据自己的电脑选择是32还是64 , 网址:https://nodejs.org/en/ 建议使用LTS版本 步骤二:nodejs 配置全局 globle 缓存 catch: 找到自己的安装目录,在里面新建两个文件夹 ...

  • 好文推荐 |手把手教你如何实现大量图片的自适应图片页面的排列

    作者:开课吧前端团队 前 言 最近在开发一个批量展示图片的页面,图片的自适应排列是一个无法避免的问题。 在付出了许多头发的代价之后,终于完成了图片排列,并封装成组件,最终效果如下: 好文推荐 |手把手教你如何实现大量图片的自适应图片页面的排列 一、设 计 思 路 为了使结构清晰,我将图片列表处理成了二维数组,第一维为行,第二维为列: render() { const { classN...

  • 关于前端学习路线的一些建议

    前言 对于刚开始学习前端的伙伴俩说,问得最多的问题就是 ——前端技术现在如此繁杂,我到底应该如何学习。这个话题太大了,几句话回答不好;也由于这个问题确实困扰了很多前端开发人员,所以我也就着手系统的输出这篇文章。 虽然这篇文章花了很长时间,肯定也有其局限性;希望各位不吝指出。 入题 我们俨然能感受到前端岗位现在已经发展成了最重要的研发岗位之一,所以对我们提出的要求也就越来越高。所以我们需要学的...

  • es6 import和export 用法及实战

    欢迎大家访问我的博客 www.kevink.club 看到vue 不断的进行模块的引用, 并且每个模块都会有export, 不禁想去尝试一下。 说实话,之前用js写功能类也没这么用过, 今天就来实现以下 ###尝试一: index.html ``` Title ``` 已运行就报错了, ```Uncaught SyntaxError: Cannot use import statement ...

  • vue知识点总结

    # 1.ES6新增? *1、变量的改变* *let:代码块内有效;不能重复声明;不存在变量提升* *const:只读变量,声明之后不允许改变。意味着,一旦声明必须初始化,否则会报错。;* *2、模版字符串(``)* *3、函数* **1、箭头函数 (sender) => { } 箭头函数最直观的三个特点。 不需要function关键字 可以省略return关键字 继承当前上下文的this关键...

  • 关于 webpack 的几个知识点

    最近几天备战秋招复习 webpack 的时候,发现一篇总结的比较好的博文,摘取几个相关的问题转载过来,同时根据自己的观点进行了部分调整,希望能够帮助有需要的人。原文链接:关于webpack的面试题总结 随着现代前端开发的复杂度和规模越来越庞大,已经不能抛开工程化来独立开发了,比如 sass 和 less 的代码浏览器是不支持的, 但如果摒弃了这些开发框架,那么开发的效率将大幅下降。在众多前端...

  • React Hooks 原理

    前言 目前,Hooks 应该是 React 中最火的概念了,在阅读这篇文章之前,希望你已经了解了基本的 Hooks 用法。 在使用 Hooks 的时候,我们可能会有很多疑惑 为什么只能在函数最外层调用 Hook,不要在循环、条件判断或者子函数中调用? 为什么 useEffect 第二个参数是空数组,就相当于 ComponentDidMount ,只会执行一次? 自定义的 Hook 是如何影响...

华为云40多款云服务产品0元试用活动

免费套餐,马上领取!
CSDN

CSDN

中国开发者社区CSDN (Chinese Software Developer Network) 创立于1999年,致力为中国开发者提供知识传播、在线学习、职业发展等全生命周期服务。