杨国超1010

杨国超1010 查看完整档案

北京编辑  |  填写毕业院校  |  填写所在公司/组织填写个人主网站
编辑

12121

个人动态

杨国超1010 赞了文章 · 2018-12-06

vue.js之props传递参数浅析

vue.js之props传递参数浅析

前段时间用vue做一个后台管理系统,其中每一页都需要一个表格来展示信息。自然就想到了将表格提取出来做成公共组件,将不同页面的数据传入进行渲染,达到复用的目的。

demo地址

1. 问题发现

在父组件中,需要向表格组件传递的数据有表格的内容数据tableData,表格的页面数据page。

<div class="content">
    <my-table :table-data="tableData" :page-info="pageInfo" id="myTable"></my-table>
</div>

其中tableData是个Array对象,是所有需要在表格中展示的数据对象组成的一个数组。而pageInfo是个Object对象,包含了表格页面信息。在父组件对两个数据对如下初始化,形式如下

tableData:[],
pageInfo: {
    current: 1, // 当前是第几页
    total: 100, // 数据对象的总数
    size: 20 // 每页显示的数量
}

按照官方文档上的说明,prop是单向绑定的,不应该在子组件内部改变prop。之所以有想修改prop中数据的冲动,主要是prop作为初始值传入后,子组件想把它当作局部数据来用。对于这种情况,官方的说法是定义一个局部变量,并用 prop 的值初始化它:

props: ['tableData', 'pageInfo'],
data() {
    return {
        tData: this.tableData,
        page: this.pageInfo
    }
}

然后根据官方文档的说法,当每次父组件更新时,子组件的所有prop都会更新为最新值。而tableData和pageInfo的信息是异步通过api从server端获取的:

{
    error: 0,
    msg: "调用成功.",
    data: {
        restrictioninfo: [...],
        total: 42
    }
}

因此当获取到数据时父组件需要改变传入子组件中的值:

me.tableData = Json.data.restrictioninfo;
me.pageInfo.total = Json.data.total;

按理说这时候子组件中的值应该更新成server返回的值,但是子组件页面的总数更新了,但table数据依然是初始化时的空数组。
image
image.png

2.赋值与绑定

首先需要定位数据是在哪个地方出了问题,于是我做了一个demo来定位问题。
首先看父组件与子组件中各元素的初始值:
image
然后当只改变父组件中数组的引用时可以看到子组件的props数组随之改变,而子组件中绑定的数组确并没有随之改变
image
因此,可以发现,问题是出在了这一步

props: ['tableData', 'pageInfo'],
data() {
    return {
        tData: this.tableData,
        page: this.pageInfo
    }
}

而要弄清楚问题的根源,就得弄清楚vue文档中深入响应式原理
image
"在Vue实例的data选项中,Vue将遍历此对象所有的属性,并使用Object.defineProperty把这些属性全部转为 getter/setter","每个组件实例都有相应的 watcher 实例对象,它会在组件渲染的过程中把属性记录为依赖,之后当依赖项的 setter 被调用时,会通知 watcher 重新计算,从而致使它关联的组件得以更新。"文档中说了这么一大堆,简单理解就是Vue将data选项中的vm.$data.a与DOM中的vm.a进行了双向绑定,即其中一个变化,另一个也会跟着变化。在Vue源码中是由defineReactive$$1函数实现的:
image

但其中主要是利用了Object.defineProperty的get和set方法实现了双向绑定。而在子组件中,pros数据和子组件的$data是通过如下方式联系在一起的:

    tData: this.tableData

查询Vue源码可知this.tableData与tData之间仅仅是赋值,即"="关系
image
而上述的initData函数是在组件构建时候执行的,因此只会在create时执行一次。这也是为什么官方文档中"作为初始值传入"这一说法,因为他本就只会执行一次。当组件构建完成后,this.tableData与tData就没有半毛钱关系了,其中一个的变化并不会引起另一个变化。当然,这种说法并不准确,因为在上文中,我们动态改变父组件传入的total,子组件也"随之"改变,感觉就像是绑定在一起了啊,这又是怎么回事呢?

3.引用类型带来的假象

当然,我们还是要从官方文档出发来解决这个问题。文档中有这样一个提示:image
这里就需要理解引用类型的概念,引用数据类型值指保存在堆内存中的对象。也就是,变量中保存的实际上的只是一个指针,这个指针指向内存中的另一个位置,该位置保存着对象。访问方式是按引用访问。例如一个js对象a,他在内存中的存储形式如下图所示:

    var a = new Object();

image
当操作时,需要先从栈中读取内存地址,然后再延指针找到保存在堆内存中的值再操作。

    a.name = 'xz';

image

引用类型变量赋值,本质上赋值的是存储在栈中的指针,将指针复制到栈中未新变量分配的空间中,而这个指针副本和原指针指向存储在堆中的同一个对象;赋值操作结束后,两个变量实际上将引用同一个对象。因此,在使用时,改变其中的一个变量的值,将影响另一个变量。

    var b = a;

image

在了解了引用类型之后,我们在来看看上文提到的动态改变传入子组件前后内存中的情况:

me.tableData = Json.data.restrictioninfo;
me.pageInfo.total = Json.data.total;
========================================
props: ['tableData', 'pageInfo'],
data() {
    return {
        tData: this.tableData,
        page: this.pageInfo
    }
}

首先对tableData的改变是改变了其引用的指针,而对pageInfo则改变了其中一个属性的值,因此动态改变前:
image
动态改变后:
image
这样就解释了为什么子组件页面的总数更新了,但table数据依然是初始化时的空数组。因为引用类型的存在,我们动态改变父组件传入的total,子组件也"随之"改变了。

查看原文

赞 26 收藏 34 评论 8

杨国超1010 赞了文章 · 2018-09-28

2018年5月前端面试题

在上家公司裸辞之后,经过一段时间休整,5月中下旬面试了一些公司,由于本人框架使用的是vue,所以面试题涉及到框架的都是vue,现将面试题整理一下:


es6

列举常用的es6特性。
let,const,var的区别。
箭头函数this的指向。
手写es6 class继承。
promise的状态,链式调用,同步异步流程,唯一性。
set去重。
设置属性默认值。


js基础

闭包,作用域,向上提升,隐式转换(这几个几乎是必考题,基础中的基础)
列举常用的5个字符串操作方法。
列举常用的5个数组操作方法。
浅拷贝和深拷贝区别,描述或者手写深拷贝。
call和apply的用法和区别。
继承的几种方法。
事件冒泡以及事件捕获。
json,string,array的互相转换。


css

清除浮动的方式。
过渡动画。
左边固定,右边自适应布局。
左右固定,中间 自适应布局。
flex布局以及常用属性。
盒模型(box-sizing)。
选择器和伪类。
position的几种属性。
less的一些优势。
重绘和回流的描述及优化方案。


Jquery

jquery的优点。
jquery的常用选择器。
juqery事件绑定(on)
链式调用的原理。
常用的几种方法。
常用插件。
$冲突如何解决。
jsonp的原理。


vue

钩子函数及生命周期。
双向绑定的原理。
父子组件以及兄弟组件的传值。
vue数组添加属性,渲染失败原因及解决方案。
computed和methods的区别。
nextTick使用场景。
vue-router传参。
vuex的几种属性。
Mutation和Action的区别。


其他

前端分页和后端分页优缺点。
http常用状态码。
301和302区别。
输入url到页面出现的流程。(包括浏览器渲染流程)
cookie,sessionStorage,localStorage区别及使用场景。
懒加载实现。
cdn原理。
常用的跨域解决方案(以及原理)。
webpack加载器顺序。
webpack本地代理。
webpack打包压缩实现。
移动端适配解决方案。
说出几种性能优化方案。
一些兼容性、BUG问题。


程序设计

冒泡排序,快速排序。
数组去重。
将url参数转成json(键值对)。
获取字符串中出现次数最多的字符。
手写正则:身份证,电话,邮箱。
tree包含多个子节点及孙节点,遍历tree。
去除json中一些指定字符。
json中name属性按照英文字母排序。(sort)
json中age属性按照数字大小排序。(sort)
去掉字符串中的所有空格。

查看原文

赞 122 收藏 173 评论 5

杨国超1010 赞了文章 · 2018-09-21

nth-last-child()和nth-last-of-type()的用法和区别

终于搞明白nth-last-child()和nth-last-of-type()的区别了:

<!DOCTYPE html>
<html>
<head>
    <title>css3</title>
    <style type="text/css">
        /*p的父元素下的p元素 且 p元素是倒数第二个子元素*/
        p:nth-last-child(2){
            background-color: red;
        }
        /*p的父元素下的倒数第二个p元素*/
        p:nth-last-of-type(2){
            background-color: blue;
        }
    </style>
</head>
<body>
    <div>
        <p>1</p>
        <p>2</p>
        <p>3</p>
        <p>4</p>
        <span>5</span>
    </div>
</body>
</html>

clipboard.png

细细品味,nth-last-child()和nth-last-of-type()还是有child和type的区别的。
若是改变了span和最后一个p的位置,就会发现p:nth-last-child()不起作用了,因为div的倒数第二个元素不是p。

clipboard.png

查看原文

赞 1 收藏 0 评论 0

杨国超1010 赞了回答 · 2018-09-20

解决css font-size=0有什么妙用?

问题的根源是 inlinea标签默认是display:inline) 和 inline-block (.list-info 设置的是 display:inline-block) 是内联布局,既然是内联那么就会受空白区域的影响。

解决办法:

1、去掉空白

图片描述

2、font-size:0 可以让空白区域消失

关注 6 回答 4

杨国超1010 赞了回答 · 2018-09-05

解决Vue packages version mismatch

npm 5 的 bug,删除 package-lock.json 文件及 node_modules 目录重新安装依赖即可

关注 2 回答 2

杨国超1010 赞了文章 · 2018-04-20

如何理解Vue的render函数

第一个参数(必须) - {String | Object | Function}

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>render</title>
    <script data-original="https://cdn.bootcss.com/vue/2.3.4/vue.js"></script>
</head>
<body>
    <div id="app">
        <elem></elem>
    </div>
    <script>
        Vue.component('elem', {
            render: function(createElement) {
                return createElement('div');//一个HTML标签字符
                /*return createElement({
                    template: '<div></div>'//组件选项对象
                });*/
                /*var func = function() {
                    return {template: '<div></div>'}
                };
                return createElement(func());//一个返回HTML标签字符或组件选项对象的函数*/
            }
        });
        new Vue({
            el: '#app'
        });
    </script>
</body>
</html>

第二个参数(可选) - {Object}

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>render</title>
    <script data-original="https://cdn.bootcss.com/vue/2.3.4/vue.js"></script>
</head>
<body>
    <div id="app">
        <elem></elem>
    </div>
    <script>
        Vue.component('elem', {
            render: function(createElement) {
                var self = this;
                return createElement('div', {//一个包含模板相关属性的数据对象
                    'class': {
                        foo: true,
                        bar: false
                    },
                    style: {
                        color: 'red',
                        fontSize: '14px'
                    },
                    attrs: {
                        id: 'foo'
                    },
                    domProps: {
                        innerHTML: 'baz'
                    }
                });
            }
        });
        new Vue({
            el: '#app'
        });
    </script>
</body>
</html>

第三个参数(可选) - {String | Array}

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>render</title>
    <script data-original="https://cdn.bootcss.com/vue/2.3.4/vue.js"></script>
</head>
<body>
    <div id="app">
        <elem></elem>
    </div>
    <script>
        Vue.component('elem', {
            render: function(createElement) {
                var self = this;
                // return createElement('div', '文本');//使用字符串生成文本节点
                return createElement('div', [//由createElement函数构建而成的数组
                    createElement('h1', '主标'),//createElement函数返回VNode对象
                    createElement('h2', '副标')
                ]);
            }
        });
        new Vue({
            el: '#app'
        });
    </script>
</body>
</html>

两种组件写法对比

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>render</title>
    <script data-original="https://cdn.bootcss.com/vue/2.3.4/vue.js"></script>
</head>
<body>
    <div id="app">
        <ele></ele>
    </div>
    <script>
        /*Vue.component('ele', {
            template: '<div id="elem" :class="{show: show}" @click="handleClick">文本</div>',
            data: function() {
                return {
                    show: true
                }
            },
            methods: {
                handleClick: function() {
                    console.log('clicked!');
                }
            }
        });*/
        Vue.component('ele', {
            render: function(createElement) {
                return createElement('div', {
                    'class': {
                        show: this.show
                    },
                    attrs: {
                        id: 'elem'
                    },
                    on: {
                        click: this.handleClick
                    }
                }, '文本');
            },
            data: function() {
                return {
                    show: true
                }
            },
            methods: {
                handleClick: function() {
                    console.log('clicked!');
                }
            }
        });
        new Vue({
            el: '#app'
        });
    </script>
</body>
</html>

this.$slots用法

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>render</title>
    <script data-original="https://cdn.bootcss.com/vue/2.3.4/vue.js"></script>
</head>
<body>
    <div id="app">
        <blog-post>
            <h1 slot="header"><span>About Me</span></h1>
            <p>Here's some page content</p>
            <p slot="footer">Copyright 2016 Evan You</p>
            <p>If I have some content down here</p>
        </blog-post>
    </div>
    <script>
        Vue.component('blog-post', {
            render: function(createElement) {
                var header = this.$slots.header,//返回由VNode组成的数组
                    body = this.$slots.default,
                    footer = this.$slots.footer;
                return createElement('div', [
                    createElement('header', header),
                    createElement('main', body),
                    createElement('footer', footer)
                ])
            }
        });
        new Vue({
            el: '#app'
        });
    </script>
</body>
</html>

使用props传递数据

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>render</title>
    <script data-original="https://cdn.bootcss.com/vue/2.3.4/vue.js"></script>
</head>
<body>
    <div id="app">
        <ele :show="show"></ele>
        <ele :show="!show"></ele>
    </div>
    <script>
        Vue.component('ele', {
            render: function(createElement) {
                if (this.show) {
                    return createElement('p', 'true');
                } else {
                    return createElement('p', 'false');
                }
            },
            props: {
                show: {
                    type: Boolean,
                    default: false
                }
            }
        });
        new Vue({
            el: '#app',
            data: {
                show: false
            }
        });
    </script>
</body>
</html>

VNodes必须唯一

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>render</title>
    <script data-original="https://cdn.bootcss.com/vue/2.3.4/vue.js"></script>
</head>
<body>
    <!-- VNode必须唯一 -->
    <div id="app">
        <ele></ele>
    </div>
    <script>
        var child = {
            render: function(createElement) {
                return createElement('p', 'text');
            }
        };
        /*Vue.component('ele', {
            render: function(createElement) {
                var childNode = createElement(child);
                return createElement('div', [
                    childNode, childNode//VNodes必须唯一,渲染失败
                ]);
            }
        });*/
        Vue.component('ele', {
            render: function(createElement) {
                return createElement('div', 
                    Array.apply(null, {
                        length: 2
                    }).map(function() {
                        return createElement(child)//正确写法
                    })
                );
            }
        });
        new Vue({
            el: '#app'
        })
    </script>
</body>
</html>

v-model指令

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>render</title>
    <script data-original="https://cdn.bootcss.com/vue/2.3.4/vue.js"></script>
</head>
<body>
    <div id="app">
        <el-input :name="name" @input="val=>name=val"></el-input>
        <div>你的名字是{{name}}</div>
    </div>
    <script>
        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
            }
        });
        new Vue({
            el: '#app',
            data: {
                name: 'hdl'
            }
        });
    </script>
</body>
</html>

作用域插槽

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>render</title>
    <script data-original="https://cdn.bootcss.com/vue/2.3.4/vue.js"></script>
</head>
<body>
    <div id="app">
        <ele>
            <template scope="props">
                <span>{{props.text}}</span>
            </template>
        </ele>
    </div>
    <script>
        Vue.component('ele', {
            render: function(createElement) {
                // 相当于<div><slot :text="msg"></slot></div>
                return createElement('div', [
                    this.$scopedSlots.default({
                        text: this.msg
                    })
                ]);
            },
            data: function() {
                return {
                    msg: '来自子组件'
                }
            }
        });
        new Vue({
            el: '#app'
        });
    </script>
</body>
</html>

向子组件中传递作用域插槽

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>render</title>
    <script data-original="https://cdn.bootcss.com/vue/2.3.4/vue.js"></script>
</head>
<body>
    <div id="app">
        <ele></ele>
    </div>
    <script>
        Vue.component('ele', {
            render: function(createElement) {
                return createElement('div', [
                    createElement('child', {
                        scopedSlots: {
                            default: function(props) {
                                return [
                                    createElement('span', '来自父组件'),
                                    createElement('span', props.text)
                                ];
                            }
                        }
                    })
                ]);
            }
        });
        Vue.component('child', {
            render: function(createElement) {
                return createElement('b', this.$scopedSlots.default({text: '我是组件'}));
            }
        });
        new Vue({
            el: '#app'
        });
    </script>
</body>
</html>

函数化组件

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>render</title>
    <script data-original="https://cdn.bootcss.com/vue/2.3.4/vue.js"></script>
</head>
<body>
    <div id="app">
        <smart-item :data="data"></smart-item>
        <button @click="change('img')">切换为图片为组件</button>
        <button @click="change('video')">切换为视频为组件</button>
        <button @click="change('text')">切换为文本组件</button>
    </div>
    <script>
        // 图片组件选项
        var ImgItem = {
            props: ['data'],
            render: function(createElement) {
                return createElement('div', [
                    createElement('p', '图片组件'),
                    createElement('img', {
                        attrs: {
                            src: this.data.url
                        }
                    })
                ]);
            }
        }
        // 视频组件
        var VideoItem = {
            props: ['data'],
            render: function(createElement) {
                return createElement('div', [
                    createElement('p', '视频组件'),
                    createElement('video', {
                        attrs: {
                            src: this.data.url,
                            controls: 'controls',
                            autoplay: 'autoplay'
                        }
                    })
                ]);
            }
        };
        /*纯文本组件*/
        var TextItem = {
            props: ['data'],
            render: function(createElement) {
                return createElement('div', [
                    createElement('p', '纯文本组件'),
                    createElement('p', this.data.text)
                ]);
            }
        };

        Vue.component('smart-item', {
            functional: true,
            render: function(createElement, context) {
                function getComponent() {
                    var data = context.props.data;
                    if (data.type === 'img') return ImgItem;
                    if (data.type === 'video') return VideoItem;
                    return TextItem;
                }
                return createElement(
                    getComponent(),
                    {
                        props: {
                            data: context.props.data
                        }
                    },
                    context.children
                )
            },
            props: {
                data: {
                    type: Object,
                    required: true
                }
            }
        });
        new Vue({
            el: '#app',
            data() {
                return {
                    data: {}
                }
            },
            methods: {
                change: function(type) {
                     if (type === 'img') {
                        this.data = {
                            type: 'img',
                            url: 'https://raw.githubusercontent.com/iview/iview/master/assets/logo.png'
                        }
                    } else if (type === 'video') {
                        this.data = {
                            type: 'video',
                            url: 'http://vjs.zencdn.net/v/oceans.mp4'
                        }
                    } else if (type === 'text') {
                        this.data = {
                            type: 'text',
                            content: '这是一段纯文本'
                        }
                    }
                }
            },
            created: function() {
                this.change('img');
            }
        });
    </script>
</body>
</html>
查看原文

赞 135 收藏 134 评论 9

杨国超1010 收藏了文章 · 2018-04-20

如何理解Vue的render函数

第一个参数(必须) - {String | Object | Function}

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>render</title>
    <script data-original="https://cdn.bootcss.com/vue/2.3.4/vue.js"></script>
</head>
<body>
    <div id="app">
        <elem></elem>
    </div>
    <script>
        Vue.component('elem', {
            render: function(createElement) {
                return createElement('div');//一个HTML标签字符
                /*return createElement({
                    template: '<div></div>'//组件选项对象
                });*/
                /*var func = function() {
                    return {template: '<div></div>'}
                };
                return createElement(func());//一个返回HTML标签字符或组件选项对象的函数*/
            }
        });
        new Vue({
            el: '#app'
        });
    </script>
</body>
</html>

第二个参数(可选) - {Object}

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>render</title>
    <script data-original="https://cdn.bootcss.com/vue/2.3.4/vue.js"></script>
</head>
<body>
    <div id="app">
        <elem></elem>
    </div>
    <script>
        Vue.component('elem', {
            render: function(createElement) {
                var self = this;
                return createElement('div', {//一个包含模板相关属性的数据对象
                    'class': {
                        foo: true,
                        bar: false
                    },
                    style: {
                        color: 'red',
                        fontSize: '14px'
                    },
                    attrs: {
                        id: 'foo'
                    },
                    domProps: {
                        innerHTML: 'baz'
                    }
                });
            }
        });
        new Vue({
            el: '#app'
        });
    </script>
</body>
</html>

第三个参数(可选) - {String | Array}

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>render</title>
    <script data-original="https://cdn.bootcss.com/vue/2.3.4/vue.js"></script>
</head>
<body>
    <div id="app">
        <elem></elem>
    </div>
    <script>
        Vue.component('elem', {
            render: function(createElement) {
                var self = this;
                // return createElement('div', '文本');//使用字符串生成文本节点
                return createElement('div', [//由createElement函数构建而成的数组
                    createElement('h1', '主标'),//createElement函数返回VNode对象
                    createElement('h2', '副标')
                ]);
            }
        });
        new Vue({
            el: '#app'
        });
    </script>
</body>
</html>

两种组件写法对比

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>render</title>
    <script data-original="https://cdn.bootcss.com/vue/2.3.4/vue.js"></script>
</head>
<body>
    <div id="app">
        <ele></ele>
    </div>
    <script>
        /*Vue.component('ele', {
            template: '<div id="elem" :class="{show: show}" @click="handleClick">文本</div>',
            data: function() {
                return {
                    show: true
                }
            },
            methods: {
                handleClick: function() {
                    console.log('clicked!');
                }
            }
        });*/
        Vue.component('ele', {
            render: function(createElement) {
                return createElement('div', {
                    'class': {
                        show: this.show
                    },
                    attrs: {
                        id: 'elem'
                    },
                    on: {
                        click: this.handleClick
                    }
                }, '文本');
            },
            data: function() {
                return {
                    show: true
                }
            },
            methods: {
                handleClick: function() {
                    console.log('clicked!');
                }
            }
        });
        new Vue({
            el: '#app'
        });
    </script>
</body>
</html>

this.$slots用法

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>render</title>
    <script data-original="https://cdn.bootcss.com/vue/2.3.4/vue.js"></script>
</head>
<body>
    <div id="app">
        <blog-post>
            <h1 slot="header"><span>About Me</span></h1>
            <p>Here's some page content</p>
            <p slot="footer">Copyright 2016 Evan You</p>
            <p>If I have some content down here</p>
        </blog-post>
    </div>
    <script>
        Vue.component('blog-post', {
            render: function(createElement) {
                var header = this.$slots.header,//返回由VNode组成的数组
                    body = this.$slots.default,
                    footer = this.$slots.footer;
                return createElement('div', [
                    createElement('header', header),
                    createElement('main', body),
                    createElement('footer', footer)
                ])
            }
        });
        new Vue({
            el: '#app'
        });
    </script>
</body>
</html>

使用props传递数据

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>render</title>
    <script data-original="https://cdn.bootcss.com/vue/2.3.4/vue.js"></script>
</head>
<body>
    <div id="app">
        <ele :show="show"></ele>
        <ele :show="!show"></ele>
    </div>
    <script>
        Vue.component('ele', {
            render: function(createElement) {
                if (this.show) {
                    return createElement('p', 'true');
                } else {
                    return createElement('p', 'false');
                }
            },
            props: {
                show: {
                    type: Boolean,
                    default: false
                }
            }
        });
        new Vue({
            el: '#app',
            data: {
                show: false
            }
        });
    </script>
</body>
</html>

VNodes必须唯一

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>render</title>
    <script data-original="https://cdn.bootcss.com/vue/2.3.4/vue.js"></script>
</head>
<body>
    <!-- VNode必须唯一 -->
    <div id="app">
        <ele></ele>
    </div>
    <script>
        var child = {
            render: function(createElement) {
                return createElement('p', 'text');
            }
        };
        /*Vue.component('ele', {
            render: function(createElement) {
                var childNode = createElement(child);
                return createElement('div', [
                    childNode, childNode//VNodes必须唯一,渲染失败
                ]);
            }
        });*/
        Vue.component('ele', {
            render: function(createElement) {
                return createElement('div', 
                    Array.apply(null, {
                        length: 2
                    }).map(function() {
                        return createElement(child)//正确写法
                    })
                );
            }
        });
        new Vue({
            el: '#app'
        })
    </script>
</body>
</html>

v-model指令

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>render</title>
    <script data-original="https://cdn.bootcss.com/vue/2.3.4/vue.js"></script>
</head>
<body>
    <div id="app">
        <el-input :name="name" @input="val=>name=val"></el-input>
        <div>你的名字是{{name}}</div>
    </div>
    <script>
        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
            }
        });
        new Vue({
            el: '#app',
            data: {
                name: 'hdl'
            }
        });
    </script>
</body>
</html>

作用域插槽

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>render</title>
    <script data-original="https://cdn.bootcss.com/vue/2.3.4/vue.js"></script>
</head>
<body>
    <div id="app">
        <ele>
            <template scope="props">
                <span>{{props.text}}</span>
            </template>
        </ele>
    </div>
    <script>
        Vue.component('ele', {
            render: function(createElement) {
                // 相当于<div><slot :text="msg"></slot></div>
                return createElement('div', [
                    this.$scopedSlots.default({
                        text: this.msg
                    })
                ]);
            },
            data: function() {
                return {
                    msg: '来自子组件'
                }
            }
        });
        new Vue({
            el: '#app'
        });
    </script>
</body>
</html>

向子组件中传递作用域插槽

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>render</title>
    <script data-original="https://cdn.bootcss.com/vue/2.3.4/vue.js"></script>
</head>
<body>
    <div id="app">
        <ele></ele>
    </div>
    <script>
        Vue.component('ele', {
            render: function(createElement) {
                return createElement('div', [
                    createElement('child', {
                        scopedSlots: {
                            default: function(props) {
                                return [
                                    createElement('span', '来自父组件'),
                                    createElement('span', props.text)
                                ];
                            }
                        }
                    })
                ]);
            }
        });
        Vue.component('child', {
            render: function(createElement) {
                return createElement('b', this.$scopedSlots.default({text: '我是组件'}));
            }
        });
        new Vue({
            el: '#app'
        });
    </script>
</body>
</html>

函数化组件

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>render</title>
    <script data-original="https://cdn.bootcss.com/vue/2.3.4/vue.js"></script>
</head>
<body>
    <div id="app">
        <smart-item :data="data"></smart-item>
        <button @click="change('img')">切换为图片为组件</button>
        <button @click="change('video')">切换为视频为组件</button>
        <button @click="change('text')">切换为文本组件</button>
    </div>
    <script>
        // 图片组件选项
        var ImgItem = {
            props: ['data'],
            render: function(createElement) {
                return createElement('div', [
                    createElement('p', '图片组件'),
                    createElement('img', {
                        attrs: {
                            src: this.data.url
                        }
                    })
                ]);
            }
        }
        // 视频组件
        var VideoItem = {
            props: ['data'],
            render: function(createElement) {
                return createElement('div', [
                    createElement('p', '视频组件'),
                    createElement('video', {
                        attrs: {
                            src: this.data.url,
                            controls: 'controls',
                            autoplay: 'autoplay'
                        }
                    })
                ]);
            }
        };
        /*纯文本组件*/
        var TextItem = {
            props: ['data'],
            render: function(createElement) {
                return createElement('div', [
                    createElement('p', '纯文本组件'),
                    createElement('p', this.data.text)
                ]);
            }
        };

        Vue.component('smart-item', {
            functional: true,
            render: function(createElement, context) {
                function getComponent() {
                    var data = context.props.data;
                    if (data.type === 'img') return ImgItem;
                    if (data.type === 'video') return VideoItem;
                    return TextItem;
                }
                return createElement(
                    getComponent(),
                    {
                        props: {
                            data: context.props.data
                        }
                    },
                    context.children
                )
            },
            props: {
                data: {
                    type: Object,
                    required: true
                }
            }
        });
        new Vue({
            el: '#app',
            data() {
                return {
                    data: {}
                }
            },
            methods: {
                change: function(type) {
                     if (type === 'img') {
                        this.data = {
                            type: 'img',
                            url: 'https://raw.githubusercontent.com/iview/iview/master/assets/logo.png'
                        }
                    } else if (type === 'video') {
                        this.data = {
                            type: 'video',
                            url: 'http://vjs.zencdn.net/v/oceans.mp4'
                        }
                    } else if (type === 'text') {
                        this.data = {
                            type: 'text',
                            content: '这是一段纯文本'
                        }
                    }
                }
            },
            created: function() {
                this.change('img');
            }
        });
    </script>
</body>
</html>
查看原文

杨国超1010 发布了文章 · 2018-04-18

vue2.0写组件使用jsx语法

webpack 配置方面:

  npm install\
  babel-plugin-syntax-jsx\
  babel-plugin-transform-vue-jsx\
  babel-helper-vue-jsx-merge-props\
  --save-dev

新建.babelrc文件:

{
  "presets": ["es2015"],
  "plugins": ["transform-vue-jsx"]
}

现在运行webpack, 代码里面的JSX就会被正确的编译为标准的JavaScript代码。

查看原文

赞 0 收藏 0 评论 0

杨国超1010 赞了问题 · 2018-03-28

解决VUX的transferDom是什么?

<div v-transfer-dom>
    <loading v-model="isLoading"></loading>
</div>
<div v-transfer-dom>
    <actionsheet :menus="menus" v-model="showMenu" @on-click-menu="changeLocale"></actionsheet>
</div>

<script>
import { TransferDom } from 'vux'
</script>

这个transferDom是干嘛的呢?

关注 3 回答 1

杨国超1010 赞了回答 · 2018-03-28

解决VUX的transferDom是什么?

如果当前组件所在位置某一父级使用了 ViewBox 组件或者直接使用了 Safari 的 overflowScrolling:touch,请引入指令 transfer-dom 以解决其带来的 z-index 失效问题。

如果你没有使用,那么不需要参照 demo 加上v-transfer-dom

https://vux.li/#/zh-CN/compon... 来自VUX文档组件xdialog说明

关注 3 回答 1

认证与成就

  • 获得 3 次点赞
  • 获得 16 枚徽章 获得 1 枚金徽章, 获得 3 枚银徽章, 获得 12 枚铜徽章

擅长技能
编辑

开源项目 & 著作
编辑

(゚∀゚ )
暂时没有

注册于 2016-09-09
个人主页被 479 人浏览