_posts 2016/10/25

vue 入门学习

阅读时长 24 分钟

看完 @尤雨溪 Vue 2.0 的建议学习顺序,决定好好学习一下vue。

HTML/CSS/Javascript 基础知识

扎实的基础知识。

vue example

Vue 是封装好的一个类,实例化操作传入的数据对象。

  • 声明式渲染
    • 数据绑定
    • 属性绑定(v-bind)
        <!-- hello world -->
        <div id="hello" v-bind:title="message">
            {{ message }}
        </div>
    
        <script>
            var vm = new Vue({
                el: '#hello',
                data: {
                    message: 'hello world'
                }
            })
            // 数据与DOM的绑定,定时 5s 更新 message
            setTimeout(function() {
                vm.message = '阿弥陀佛';
            }, 5000)
        </script>
    

v-bind 属性被称为指令,指令都带有前缀v-。

  • 条件与循环

    • 条件(v-if)
        <div v-if="isShow" id="hello" v-bind:title="message">
            {{ message }}
        </div>
    
        <script>
            var vm = new Vue({
                el: '#hello',
                data: {
                    isShow: false,
                    message: 'hello world'
                }
            })
            // 数据与DOM的绑定,定时 5s 更新 message
            setTimeout(function() {
                vm.message = '阿弥陀佛';
                vm.isShow = true;
            }, 5000)
        </script>
    
    • 循环(v-for)
        <ol id="fordemo">
            <li v-for="message in messages"> {{ message.title }} </li>
        </ol>
    
        <script>
            var vm = new Vue({
                el: '#fordemo',
                data: {
                    messages: [{
                        title: '标题一'
                    }, {
                        title: '标题二'
                    }, {
                        title: '标题三'
                    }, {
                        title: '标题四'
                    }]
                }
            })
    
            // 追加内容
            setTimeout(function() {
                vm.messages.push({title: '新增数据'});
            }, 3000)
        </script>
    

    对于这里的 for 的用法我是感到疑惑的。

        // for in
        for(var message in messages) {
            if(messages.hasOwnProperty(message)) {
                console.log(message); // 这个应该是index
            }
        }
    
        // for of
        for(var message of messages) {
            console.log(message); // Object
        }
    
  • 处理用户输入

    • v-on 绑定一个监听事件,调用实例中定义的方法。
        // 点击删除刚刚添加的数据
        <button v-on:click="rmLastData">移除最后一个数据</button>
        <button v-on:click="addData">追加一个数据</button>
    
        <script>
            var vm = new Vue({
                el: '#fordemo',
                data: {
                    messages: [{
                        title: '标题一'
                    }, {
                        title: '标题二'
                    }, {
                        title: '标题三'
                    }, {
                        title: '标题四'
                    }]
                },
                methods: {
                    rmLastData: function() {
                        this.messages.pop();
                    },
                    addData: function() {
                        this.messages.push({title: '新增的数据'});
                    }
                }
            });
        </script>
    
    • v-model 处理用户输入数据
    <div id="modeldemo">
        <p>{{ message }}</p>
        <input type="text" v-bind:value="message" v-model="message" >
    </div>
    
    <script>
        var vm = new Vue({
            el: '#modeldemo',
            data: {
                message: '请输入一些内容'
            }
        });
    </script>
    
  • 用组件构建应用

    <div id="component">
        <header-item v-bind:name="name"></header-item>
    </div>
    
    <script>
        // 注册组件
        Vue.component('header-item', {
            data: function() {
                return {
                    message: '头部'
                }
            },
            props: ['name'],
            template: '<header> {{ message }} {{ name }} </header>'
        })
        Vue.component('footer-item', {
            template: '<footer>脚部</footer>'
        })
        var vm = new Vue({
            el: '#component',
            data: {
                name: '名字'
            }
        });
    </script>
    
    • 局部注册
    new Vue({
        el: '#component',
        data: {
            name: '名字'
        },
        components: {
            'header-item': {
                data: function() {
                    return {
                        message: '头部'
                    }
                },
                props: ['name'],
                template: '<header> {{ message }} {{ name }} </header>'
            }
        }
    })
    
    • 模板解析说明 因为Vue是在浏览器解析完HTML后才检索模板标签解析。参考文档试了一下 table
        <div id="component">
            <header-item v-bind:name="name"></header-item>
            <table>
                <tr-item></tr-item>
            </table>
        </div>
        <script>
            new Vue({
                el: '#component',
                data: {
                    name: '名字'
                },
                components: {
                    'header-item': {
                        data: function() {
                            return {
                                message: '头部'
                            }
                        },
                        props: ['name'],
                        template: '<header> {{ message }} {{ name }} </header>'
                    },
                    'tr-item': {
                        template: '<tr><td> TD </td></tr>'
                    }
                }
            })
        </script>
    
        /**
         * 解析出来的结果就变成了
         * <div id="component"><header> 头部 名字 </header> <tr><td> TD  </td></tr><table></table></div>
         * 
         * 如果需要解析正确,html修改成这样
         * <table>
         *   <tr is="tr-item"></tr>
         * </table>
         */
    
    • data必须为function
        <div id="component">
            <header-item></header-item>
            <header-item></header-item>
            <header-item></header-item>
        </div>
        <script>
            new Vue({
                el: '#component',
                components: {
                    'header-item': {
                        data: function() {
                            return {
                                counter: 0
                            }
                        },
                        methods: {
                            add: function() {
                                this.counter++;
                            }
                        },
                        template: '<button v-on:click="add">{{counter}}</button>'
                    }
                }
            })
        </script>
    

    data function

    这里的data为function应该是涉及到一个闭包的问题,如果返回的是一个全局变量,那么结果就不一样了。

        <div id="component">
            <header-item></header-item>
            <header-item></header-item>
            <header-item></header-item>
        </div>
        <script>
            var counterInit = 0;
            new Vue({
                el: '#component',
                components: {
                    'header-item': {
                        data: function() {
                            return counterInit;
                        },
                        methods: {
                            add: function() {
                                this.counter++;
                            }
                        },
                        template: '<button v-on:click="add">{{counter}}</button>'
                    }
                }
            })
        </script>
    

    data function

    • 构成组件 父子组件之间的关系是,父组件通过props给子组件传递参数,子组件通过events给父组件发送消息。

props

  • 使用props传递数据 组件实例之间的作用域是孤立的,子组件中需要使用父组件中数据的时候,需要通过在自定义属性来传递参数,子组件中通过props来接受自定义属性名传递的数据。props可以是数组也可以是对象。
    // 局部代码 []
    props: ['msg']

    // 局部代码 {} 检测类型 + 默认值 + 必须 + 验证
    props: {
        msg: {
            type: String,
            default: 'message',
            required: true,
            validator: function (value) {
                return value;
            }
            
        }
    }
  • 驼峰命名与中杠命名法 HTML 特性不区分大小写。在非字符串模板中使用驼峰命名,模板中使用 - 命名。
<div id="component">
    <my-msg></my-msg>
</div>
<script>
new Vue({
    el: '#component',
    components: {
        'myMsg': {
            data: function() {
                return {
                    msg: 'message'
                }
            },
            template: '<div>{{ msg }}</div>'
        }
    }
})
</script>
  • 动态props 绑定动态输入或者动态数据到html属性上。
<div id="propsDemo">
    <input v-model="myMsg" />
    <the-msg :my-msg="myMsg" :init-my-msg="initMyMsg"></the-msg>
</div>
<script>
    new Vue({
        el: '#propsDemo',
        data: {
            myMsg: null,
            initMyMsg: '初始化信息'
        },
        components: {
            'theMsg': {
                props: {
                    myMsg: String,
                    initMyMsg: String
                },
                template: '<div>{{initMyMsg}}{{myMsg}}</div>'
            }
        }
    })
</script>
  • 字面量 vs 动态绑定
    <div id="propsDemo1">
        <msg msg-info="'1'"></msg>
    </div>

    <script>
        new Vue({
            el: '#propsDemo1',
            components: {
                'msg': {
                    props: ['msgInfo'],
                    template: '<div>{{ msgInfo }}</div>'
                }
            }
        })
    </script>

解析后的HTML是

<div id="propsDemo1">
    <div>'1'</div>
</div>

如果动态绑定后呢? 把 msg-info=" '1' " 修改为 :msg-info=" '1' "

解析后的HTML是

<div id="propsDemo1">
    <div>1</div>
</div>
  • 单向数据绑定 父组件数据变动后会影响子组件数据的变动,相反,如果子组件变动后也会影响父组件的变动,但是在vue中提示报错。父组件传递的参数是何子组件接收的参数是指向同一个内存地址。传递一个对象过去更改对象的属性值。
<div id="propsDemo2">
    <p v-for="msg in msgs">{{msg.text}}</p>
    <div2 v-for="msg in msgs" :msg="msg"></div2>
</div>

<script>
    new Vue({
        el: '#propsDemo2',
        data: {
            msgs: [{
                text: 'text1'
            }, {
                text: 'text2'
            }]
        },
        components: {
            'div2': {
                props: ['msg'],
                template: '<div @click="changeVal(msg)"> {{msg.text}} </div>',
                methods: {
                    changeVal: function(msg) {
                        msg.text = '新内容';
                    }
                }
            }
        }
    })
</script>
  • prop数据验证 props可以为对象,内容的prop数据作为对象使用的时候,可以通过设置属性约定prop的 typedefaultrequiredvalidator
<div id="propsDemo3">
    <div2 msg="message"></div2>
</div>

<script>
    new Vue({
        el: '#propsDemo3',
        components: {
            'div2': {
                props: {
                    'msg': {
                        type: String,
                        default: 'abc'
                    }
                },
                template: {
                    '<div>{{msg}}</div>'
                }
            }
        }
    })
</script>

自定义事件

  • 自定义事件 父组件通过props给子组件传递参数,子组件通过event把参数回传给父组件。
<div id="propsDemo3">
    <div>{{counter}}</div>
    <div2 @total="total"></div2>
    <div2 @total="total"></div2>
    <div2 @click.native="doTheThing"></div2>
</div>

<script>
    new Vue({
        el: '#propsDemo3',
        data: {
            counter: 0
        },
        methods: {
            total: function() {
                this.counter++;
            },
            doTheThing: function() {
                this.counter--;
            }
        },
        components: {
            'div2': {
                data: function() {
                    return {
                        counter: 0
                    }
                },
                methods: {
                    some: function() {
                        this.counter++;
                        this.$emit('total');
                    }
                },
                template: '<div @click="some">{{counter}}</div>'
            }
        }
    })
</script>

在子组件上注册一个原生事情.native,子组件内部执行点击事件后会触发doTheThing

  • 使用自定义事件的表单输入事件 对于v-model的理解
    <p>{{message}}</p>
    <input type="text" v-model="message">
    // 这个只是语法糖写法 原版写法应该是
    <input type="text" :input="value = $event.target.value" :value="value">
    // 缩写成
    <input type="text" :input="value = arguments[0]" :value="value">

完善一个例子:

    <div id="propsDemo4">
        <p>{{ message }}</p>
        <my-input label="Message" v-model="message"></my-input>
    </div>

    <script>
        new Vue({
            el: '#propsDemo4',
            data: {
                message: 'hello'
            },
            components: {
                'myInput': {
                    props: ['value', 'label'],
                    template: '\
                        <label :for="label">{{label}}</label>\
                        <input :id="label" type="text" :input="onInput">\
                    ',
                    methods: {
                        'onInput': function(event) {
                            this.$emit('input', event.target.value);
                        }
                    }
                }
            }
        })
    </script>

Component template should contain exactly one root element: 组件模板需要用一个根元素包裹起来。

  • 非父子组件通信 具体实际应用场景还没用到,仅仅学习一下方法。

var say = new Vue();
say.$emit('welcome');
say.$on('webcome', function() {
    console.log('webcome you');
})

使用slots分发内容

  • 单个slot 其实一开始看到slots是完全懵逼的状态的。 但看到实际的demo后这个好像是见过的。
// video
<video src="movie.ogg" controls="controls">
您的浏览器不支持 video 标签。
</video>

// vue
<div id="app">
    <welcome>
        <p>父组件中的“欢迎方式”</p>
    </welcome>
</div>
<script>
    Vue.component('welcome', {
        data: function() {
            return {
                isShow: false,
                you: 'unofficial'
            }
        },
        template: '<div><div v-if="isShow">{{ you }}</div><slot>欢迎欢迎热烈欢迎</slot></div>'
    })

    new Vue({
        el: '#app'
    })
</script>

如果父组件作用域welcome内无内容,子组件无输出内容,那么显示的就是子组件内部的slot内容;如果welcome内有内容,那么输出的就是welcome内的内容;如果子组件有输出内容,那么输出的就是子组件中的内容+slot内容。

  • 具名slot 父组件内标签上添加slot属性,属性值对应子组件中slot标签的name属性值,标签中的内容作为子组件slot的替换内容。
// vue
<div id="app">
    <welcome>
        <p slot="header">header</p>
        <p slot="welc">默认欢迎语</p>
        <p slot="footer">footer</p>
    </welcome>
</div>
<script>
    Vue.component('welcome', {
        data: function() {
            return {
                isShow: false,
                you: 'unofficial'
            }
        },
        template: '<div><div v-if="isShow">{{ you }}</div><slot name="footer">footer</slot><slot name="header">header</slot><slot name="welc">welc</slot></div>'
    })

    new Vue({
        el: '#app'
    })
</script>

动态组件

多个组件绑定到同一个挂载点上,然后动态的在他们之间切换,使用保留的 <component> 标签,绑定到动态的 is 上。

<div id="app">
    <component :is="currentView"></component>
</div>
<script>
    new Vue({
        el: '#app',
        data: {
            currentView: 'home'
        },
        components: {
            home: {
                template: '<div>home</div>'
            },
            list: {
                template: '<div>list</div>'
            }
        }
    })

    // 或者这么写

    new Vue({
        el: '#app',
        data: {
            currentView: {
                template: '<div>home</div>'
            }
        }
    })
</script>
  • keep-alive 把切换出去的动态组件保存在内存中,防止再次被渲染,保存他的状态。
// eg
<keep-alive>
    <component :is="currentView"></component>
</keep-alive>
// 如果currentView = home, 就不能再更改currentView的值了

总结

只是按照文档学习了一部分知识点,还有很多细节部分。陆续学习完善。