从jQuery到Vue组件化,一步步入门Vue

442 阅读5分钟

利用jQuery实现todolist

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <script src="https://code.jquery.com/jquery-3.4.1.js"></script>
    <title>Document</title>
</head>
<body>
    <div>
        <input id="input" type="text">
        <button id="btn">提交</button>
        <ul id="list"></ul>
    </div>

    <script>
        // MVP模式
        function Page() {

        }

        $.extend(Page.prototype,{
            init: function () {
                this.bindEvents()
                
            },
            bindEvents:function () {
                var btn = $('#btn')
                btn.on('click',$.proxy(this.handleBtnClick,this))

            },
            handleBtnClick: function () {
                var inputElement = $("#input");
                var inputValue = $("#input").val();
                var ulElem = $("#list");
                ulElem.append('<li>'+ inputValue+'</li>');
                inputElement.val('');
            }
        })

        var page=new Page();
        page.init();
    </script>
    
</body>
</html>

直接引入vue实现的简单todolist

<!DOCTYPE html>
<html lang="en">
<head>
</head>
<body>
    <div id="app">
        <!-- v-model数据的双向绑定 ,input框内容和inputValue
        其中之一变了,另一个也会跟着变-->
        <input type="text" v-model="inputValue" @keyup.enter="handleBtnClick">
        <button @click="handleBtnClick">提交</button>
        <ul>
            <li v-for="item in list">{{item}}</li>
        </ul>
    </div>
    <script>
       
        var app =new Vue({
            el: '#app',
            data: {
                list: [],
                inputValue: ''
            },
            methods: {
                handleBtnClick: function (){
                    this.list.push(this.inputValue)
                    this.inputValue=''
                }
            }
        })
        
    </script>
</body>
</html>

使用组件化思想修改todolist

之前todo的列表是用<li></li>标签实现的。现在我们将这一部分改成用组件显示。

全局组件

首先使用Vue.component("")方法创建一个全局组件

// vue
Vue.component("TodoItem")

然后在组件中使用template创建组件模板

// vue
Vue.component("TodoItem",{
    template:"<li>todo item</li>"
})
``
在html中使用创建好的标签,比如下面的`todo-item`.

```html
 <ul>
    <!-- <li v-for="item in list">{{item}}</li> -->
    <todo-item v-for="item in list"></todo-item>
</ul>

这样就把一个todo组件化了。但是现在无论输入什么,结果都是todo item。想要子组件展示相应的内容,就需要对组件传值。 这里就需要v-bindv-bind向子组件传递一个绑定值。所以需要将上面的html代码修改为:

 <ul>
    <todo-item v-bind:content="item" v-for="item in list"></todo-item>
</ul>

意思在循环list时,把list中的每一项赋值为item,再把输入的值通过v-bind的content传给item。

接下来就需要把值传递给组件了。这里要用到props,在TodoItem中添加props属性,props接收一个content参数:

// vue
Vue.component("TodoItem",{
    props: ['content']
    template:"<li>todo item</li>"
})

这样子组件就能接收到父组件输入的值。现在只需要再修改一下template里的todo item,就可以显示出来了:

// vue
Vue.component("TodoItem",{
    props: ['content']
    template:"<li>{{content}}</li>"
})

下面是完整的代码:

<!DOCTYPE html>
<html lang="en">
<head>
</head>
<body>
    <div id="app">
        <input type="text" v-model="inputValue" @keyup.enter="handleBtnClick">
        <button @click="handleBtnClick">提交</button>
        <ul>
            <!-- v-bind向子组件传入绑定值,通过content把外部获取的值传递给todo-item-->
            <todo-item v-bind:content="item" v-for="item in list"></todo-item>
        </ul>
    </div>
    <script>
        // 创建了全局组件
        Vue.component("TodoItem",{
            // 子组件通过props获取父组件传递的值
            props: ['content'],
            template: "<li>{{content}}</li>"
        })
        var app =new Vue({
            el: '#app',
            data: {
                list: [],
                inputValue: ''
            },
            methods: {
                handleBtnClick: function (){
                    this.list.push(this.inputValue)
                    this.inputValue=''
                }
            }
        })
    </script>
</body>
</html>

局部组件

首先创建一个局部组件。

var TodoItem ={
            props: ['content'],
            template: "<li>{{content}}</li>" 
        }

局部组件使用时要先在vue实例中注册注册,才能被使用:

var app =new Vue({
           
    // 局部组件需要注册才能使用
    components: {
        TodoItem
            }

下面是完整的代码:

<!DOCTYPE html>
<html lang="en">
<head>
</head>
<body>
    <div id="app">
        <input type="text" v-model="inputValue" @keyup.enter="handleBtnClick">
        <button @click="handleBtnClick">提交</button>
        <ul>
            <todo-item v-bind:content="item" v-for="item in list"></todo-item>
        </ul>
    </div>
    <script>
      
        var TodoItem ={
            props: ['content'],
            template: "<li>{{content}}</li>" 
        }
       
        var app =new Vue({
            el: '#app',
            data: {
                list: [],
                inputValue: ''
            },
            // 局部组件需要注册才能使用
            components: {
                TodoItem
            },
            methods: {
                handleBtnClick: function (){
                    this.list.push(this.inputValue)
                    this.inputValue=''
                }
            }
        })
        
    </script>
</body>
</html>

vue组件之间的传值

上面已经实现了一个todo的添加。但现在删除功能还没有实现。现在需要实现点击一个todo就将它删除。 因为上面我们把todo-item设为了子组件。现在就涉及到子组件向父组件传值。

首先需要在子组件中做事件绑定,然后编写事件函数。当点击todo-item时,子组件将数据传递给父组件,让父组件进行删除操作。

当子组件被点击时,子组件需要将删除操作传递给父组件,这里用到了$emit,向上一层触发事件

// vue局部组件
var TodoItem ={
            props: ['content'],
            template: "<li @click='handleItemClick'>{{content}}</li>" ,
            methods: {
                handleItemClick: function () {
                    // 子组件被点击时,将事件发射出去
                   this.$emit("delete") 
                }
            }
        }

父组件需要对事件进行监听,一旦删除事件被触发,就调用hangdleItemClick函数

<ul>
        
    <todo-item @delete="handleItemDelete" v-bind:content="item" v-for="item in list"></todo-item>
</ul>

接下来就需要在父组件中定义handleItemClick函数。此函数需要删除点击对应的item,但是目前项目中没有对应的数据。所以需要在todo-item中利用v-bind再传一个值:

<ul>
          
    <todo-item @delete="handleItemDelete" v-bind:index="item" v-bind:content="item" v-for="(item,index) in list"></todo-item>
</ul>

然后还需要在子组件的props中加入index,这样子组件才能接受父组件的值:

  var TodoItem ={
    props: ['content','index']
  }

然后还需要把子组件中的handleItemClick函数添加一个this.index。这样父组件监听时就会拿到delete与index

var TodoItem ={
    props: ['content','index'],
           
    methods: {
        handleItemClick: function () {
            this.$emit("delete",this.index) 
            }
        }
}

最后就是添加父组件中的handleItemDele:

handleItemDelete: function (index) {
    this.list.splice(index,1)
    }

完整代码如下:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <script src="https://cdn.jsdelivr.net/npm/vue"></script>
    <style>
        #id{
            width: 400px;
            height: 600px;
        }
        ul{
            background-color:bisque;
        }
    </style>
    <title>Document</title>
</head>
<body>
    <div id="app">
        <!-- v-model数据的双向绑定 ,input框内容和inputValue
        其中之一变了,另一个也会跟着变-->
        <input type="text" v-model="inputValue" @keyup.enter="handleBtnClick">
        <button @click="handleBtnClick">提交</button>
        <ul>
           
            <!-- v-bind向子组件传入绑定值,通过content把外部获取的值传递给todo-item-->
            <!-- 父组件监听delete事件,一旦删除事件被触发,就调用hangdleItemClick函数 -->
            <todo-item @delete="handleItemDelete" v-bind:index="index" v-bind:content="item" v-for="(item,index) in list"></todo-item>
        </ul>
    </div>
    <script>
        // 局部组件
        var TodoItem ={
            props: ['content','index'],
            template: "<li @click='handleItemClick'>{{content}}</li>" ,
            methods: {
                handleItemClick: function () {
                    // 子组件被点击时,将事件发射出去
                   this.$emit("delete",this.index) 
                }
            }
        }
       
        var app =new Vue({
            el: '#app',
            data: {
                list: [],
                inputValue: ''
            },
            // 局部组件需要注册才能使用
            components: {
                TodoItem
            },
            methods: {
                handleBtnClick: function (){
                    this.list.push(this.inputValue)
                    this.inputValue=''
                },
                handleItemDelete: function (index) {
                    this.list.splice(index,1)
                }
            }
        })
        
    </script>
</body>
</html>