Компоненты Vue в действии

внешний интерфейс JavaScript Vue.js внешний фреймворк

Недавно я прочитал соответствующие знания о компонентах Vue, в дополнение к официальному сайту также рекомендую этот блог.блог woo woo woo.cn на.com/keep Fool/fear/…, но в этом блоге используется версия 1.0.25, я буду использовать последнюю версию версии 2.4.4, чтобы имитировать приведенный ниже пример, а также расскажу о следующих аспектах:

  1. область компиляции компонента
  2. компоненты родитель-потомок передают данные
  3. Коммуникация между компонентами, не являющимися родительскими и дочерними
  4. фильтр

Давайте посмотрим на окончательный эффект реализации:

vue_component.gif

1. Общий код

Давайте сначала посмотрим на общий код, первая часть — это html-часть, которая разделена на три части.

1. Контейнерная часть id="app" 2. Табличная часть id="grid-template" 3. Диалоговая часть id="dialog-template" Всего задействовано два пользовательских компонента, один родительский компонент<grid-template></grid-template>; дочерний компонент<modal-dialog></<modal-dialog>.

<body>
    <!-- container -->
    <div id="app">
        <div class="container">
            <div class="form-group">
                <label>
                    Search
                </label>
                <input type="text" v-model="searchQuery" />
            </div>
        </div>
        <div class="container">
            <grid-template :column="columns" :search-key="searchQuery" :data-list="people"></grid-template>
        </div>
    </div>

    <!-- 表格 -->
    <template id="grid-template">
        <div>
            <table>
                <thead>
                    <th v-for="headName in column">
                        {{headName.name | capitalize}}
                    </th>
                    <th>
                        Delete
                    </th>
                </thead>
                <tbody class="text-center">
                    <tr v-for="(entry, index) in filt(dataList, searchKey)">
                        <td v-for="col in column">
                            <span v-if="col.isKey">
                                <a href="javascript:void(0)" @click="openEditItemDialog(index, 'Edit item ' + entry[col.name])">{{entry[col.name]}}</a>
                            </span>
                            <span v-else>{{entry[col.name]}}</span>
                        </td>
                        <td>
                            <button @click="deleteItem(index)">Delete</button>
                        </td>
                    </tr>
                </tbody>
            </table>

            <div class="container">
                <button class="button" v-on:click="openNewItemDialog('Create new item')">Create</button>
            </div>
            <modal-dialog :show="show" :mode="mode" :title="title" :fields="column" :item="item" @on-show-change="handleShow" @add-item="addData"
                @update-item="updateData"></modal-dialog>
        </div>
    </template>

    <!-- 对话框 -->
    <template id="dialog-template">
        <div class="dialogs">
            <div class="dialog" v-bind:class="{'dialog-active': myShow}">
                <div class="dialog-content">
                    <header class="dialog-header">
                        <h1 class="dialog-title">{{title}}</h1>
                    </header>

                    <div v-for="field in fields" class="form-group">
                        <label>{{field.name}}</label>
                        <select v-if="field.dataSource" v-model="item[field.name]" :disabled="mode === 2 && field.isKey">
                            <option v-for="opt in field.dataSource" :value="opt">
                                {{opt}}
                            </option>
                        </select>
                        <input v-else type="text" v-model="item[field.name]" :disabled="mode === 2 && field.isKey" />
                    </div>

                    <footer class="dialog-footer">
                        <div class="form-group">
                            <label></label>
                            <button v-on:click="save">Save</button>
                            <button v-on:click="close">Close</button>
                        </div>
                    </footer>
                </div>
            </div>
            <div class="dialog-overlay"></div>
        </div>
    </template>
</body>

Давайте посмотрим на первую часть кода,

    <div id="app">
        <div class="container">
            <div class="form-group">
                <label>
                    Search
                </label>
                <input type="text" v-model="searchQuery" />
            </div>
        </div>
        <div class="container">
            <grid-template :column="columns" :search-key="searchQuery" :data-list="people"></grid-template>
        </div>
    </div>

в приведенном выше коде[column, search-key, data-list]являются свойствами компонента grid-template,[columns,searchQuery,people ]это использоватьcontainerперейти кgrid-templateданные компонентов. Определить метод глобального компонентаVue.component(name, {}), в родительском компонентеgrid-templateпередать атрибутcomponentsопределить подкомпонентыmodal-dialog, который также является наиболее часто используемым способом определения глобальных и локальных компонентов.

Vue.component('grid-template', {
        template: "#grid-template",
        props: [
            'dataList', 'column', 'searchKey'
        ],        
        components: {
            'modal-dialog': {
                template: '#dialog-template',
                data: function () {
                    return {
                        myShow: this.show
                    }
                },
                props: [
                    'mode',
                    'title',
                    'fields',
                    'item',
                    'show'
                ]
            }
        }
}

Используйте kebab-case (название, разделенное тире) в html и используйте camelCase (название в верблюжьем регистре) в JS. Этот Vue автоматически распознает его, и мы можем кодировать его в соответствии с нашими собственными привычками.

2. Объем компиляции компонента

Точно так же, как у функций в языках программирования есть область видимости, у компонентов в Vue тоже есть область видимости, и область видимости есть только в шаблоне компонента. См. официальный пример:

<child-component>
    {{ message }}
</child-component>

Здесь сообщение использует данные родительского компонента (родительский компонент — это компонент, который использует этот компонент) или данные дочернего компонента? Ответ - родительский компонент. Официальное определение области действия:

Содержимое шаблона родительского компонента компилируется в области действия родительского компонента, содержимое шаблона дочернего компонента компилируется в области действия дочернего компонента.

Давайте посмотрим на другой пример:

<!-- 无效 -->
<child-component v-show="someChildProperty"></child-component>

предполагаемыйsomeChildPropertyЯвляется ли свойство дочернего компонента, в приведенном выше коде будут проблемы, потому что это область действия родительского компонента, то естьsomeChildPropertyЭто должны быть данные, определенные в родительском компоненте. Если вы хотите привязать директиву области действия подкомпонента к корневому узлу компонента, вам нужно сделать это в шаблоне подкомпонента:

Vue.component('child-component', {
  // 有效,因为是在正确的作用域内
  template: '<div v-show="someChildProperty">Child</div>',
  data: function () {
    return {
      someChildProperty: true
    }
  }
})

Давайте подробнее рассмотрим вторую часть приведенного выше кода — табличную часть, чтобы увидеть, как родительский компонент использует дочерний компонент:

    <!-- 表格 -->
    <template id="grid-template">
        <div>
            <modal-dialog :show="show" :mode="mode" :title="title" :fields="column" :item="item" @on-show-change="handleShow" @add-item="addData"
                @update-item="updateData"></modal-dialog>
        </div>
    </template>

в,[show, mode, title, fields, item]даmodal-dialogСвойства, определяемые самим компонентом, @v-on:сокращение для ,method [handleShow, addData]это функция в родительском компоненте id="grid-template".

    Vue.component('grid-template', {
        template: "#grid-template",
        methods: {
            handleShow: function (val) {
                this.show = val
            },
            addData: function (item) {
                if (this.itemExists(item)) {
                    alert('Item ' + item[this.keyColumn] + " is already exists");
                    this.show = true;
                    return;
                }
                this.dataList.push(item)
                this.item = {}
                this.show = false
            },
            updateData: function (item) {
                var keyColumn = this.keyColumn

                for (var i = 0; i < this.dataList.length; i++) {
                    if (this.dataList[i][keyColumn] === item[keyColumn]) {
                        for (var j in item) {
                            this.dataList[i][j] = item[j]
                        }
                        break;
                    }
                }

                item = {}
            }
        },
        components: {
            'modal-dialog': {
                template: '#dialog-template',
                ......
            }
        }
    })

3. Родительско-дочерние компоненты передают данные

Экземпляры компонентов ограничены изолированно. Это означает, что на данные родительского компонента нельзя (и не следует) напрямую ссылаться в шаблоне дочернего компонента. Данные родительского компонента необходимо отправить дочернему через реквизиты. Подкомпоненты должны явно использовать [props] объявляет ожидаемые данные:

Vue.component('child', {
  // 声明 props
  props: ['message'],
  // 就像 data 一样,prop 也可以在模板中使用
  // 同样也可以在 vm 实例中通过 this.message 来使用
  template: '<span>{{ message }}</span>'
})

Затем мы можем передать ему обычную строку следующим образом: Результатhello!

Вы также можете использовать динамические реквизиты, то есть директиву v-bind. v-bind динамически привязывает свойства к данным родительского компонента. Всякий раз, когда данные родительского компонента изменяются, это изменение также распространяется на дочерний компонент:

    <div id="app">
        <div class="container">
            <div class="form-group">
                <label>
                    Search
                </label>
                <input type="text" v-model="searchQuery" />
            </div>
        </div>
        <div class="container">
            <grid-template :column="columns" :search-key="searchQuery" :data-list="people"></grid-template>
        </div>
    </div>

Как дочерний компонент взаимодействует с родительским компонентом? Здесь пригодится система пользовательских событий Vue.

Используйте $on(eventName) для прослушивания событий Запустить событие с помощью $emit(eventName)

Посмотрите на наш пример, добавленные данные в диалоге должны уведомить родительский компонент<grid-template></grid-template>, ты можешь это сделать: в диалоговом окне дочернего компонента<modal-dialog></<modal-dialog>

<footer class="dialog-footer">
        <div class="form-group">
               <label></label>
               <button v-on:click="save">Save</button>
         </div>
</footer>

 methods: {
     save: function () {
               this.$emit('add-item', this.item)
     }
},

Vue слушает «добавить элемент»事件,Vue就会调用父组件метод addData в `:

<modal-dialog @add-item="addData" ></modal-dialog>

methods: {
            addData: function (item) {
                if (this.itemExists(item)) {
                    alert('Item ' + item[this.keyColumn] + " is already exists");
                    this.show = true;
                    return;
                }
                this.dataList.push(item)
                this.item = {}
                this.show = false
}

4. Взаимодействие между компонентами, не являющимися родительскими и дочерними.

Иногда также требуется связь между двумя компонентами, которые не находятся в отношениях родитель-потомок. Пустой экземпляр Vue можно использовать как шину событий:

var bus = new Vue()

// 触发组件 A 中的事件
bus.$emit('id-selected', 1)

// 在组件 B 创建的钩子中监听事件
bus.$on('id-selected', function (id) {
  // ...
})

В нашем примере для отображения диалога требуется связь между родительским и дочерним компонентами, щелкнитеCreateКнопка должна контролировать истинное или ложное отображение, чтобы контролировать, отображается ли диалоговое окно или нет. Некоторые друзья могут сразу подумать о методе реквизита, но будет проблема:

Передача значения show диалоговому окну дочернего компонента в качестве атрибута через родительский компонент может управлять отображением или скрытием диалогового окна, но когда вы нажимаете кнопку сохранения или закрытия в диалоговом окне, вам нужно изменить значение шоу, что эквивалентно Дочернему компоненту изменили реквизиты свойства, переданные от родительского компонента, что не является гармоничным.Vue рекомендует, чтобы реквизиты были доступны только для чтения при передаче дочернему компоненту, то есть дочерний компонент должен не изменять порты, переданные от родительского компонента.

    <template id="dialog-template">
        <div class="dialogs">
            <div class="dialog" v-bind:class="{'dialog-active': show}">
                
        </div>
    </template>

В настоящее время вы можете достичь этой цели с помощью событий. Состояние показа используется только как данные самого диалогового окна дочернего компонента. Родительский компонент не знает этого свойства. Чтобы управлять состоянием диалогового окна, вы нужно отправить событие дочернему компоненту:

<div class="container">
     <button class="button" v-on:click="openNewItemDialog('Create new item')">Create</button>
</div>

Vue.component('grid-template', {
        template: "#grid-template",
        methods: {
            openNewItemDialog: function (title) {
                this.title = title
                this.mode = 1
                this.item = {}
                bus.$emit('dialog-show', true)    //通过总线发送dialog-show事件
            }
            addData: function (item) {
                this.dataList.push(item)
            },
        },
        components: {
            'modal-dialog': {
                template: '#dialog-template',
                mounted: function () {
                    bus.$on('dialog-show', function (show) {
                        this.show = show
                    }.bind(this))
                }
            }
        }
})

1. Нажмите кнопку «Создать» и отправьте событие dialog-show через шину. $emit('dialog-show', true) //через шину 2. Дочерний компонент прослушивает события в смонтированном хуке жизненного цикла. смонтирован: функция () { bus.$on('диалог-шоу', function (show) { это.показать = показать }.связать(это)) }

Что делать, если вам нужно передать метод свойства props? Ты можешь это сделать,

    components: {
            'modal-dialog': {
                template: '#dialog-template',
                data: function () {
                    return {
                        myShow: this.show
                    }
                },
                methods: {
                    close: function () {
                        this.myShow= false
                    }
                },
                watch: {
                    show(val) {
                        this.myShow = val;
                    },
                    myShow(val) {
                        this.$emit("on-show-change", val);
                    }
                },
            }
        }    

1. Не изменяйте напрямую реквизиты, переданные из родительского компонента, в дочерний компонент, будет сообщено об ошибке: Измените props-show непосредственно в дочерних компонентах закрыть: функция () { это.шоу = ложь } Ошибка: избегайте изменения реквизита напрямую, так как значение будет перезаписано при каждом повторном рендеринге родительского компонента. Вместо этого используйте данные или вычисляемое свойство на основе значения реквизита. 2. Вычисляя свойство, измените свойство myShow дочернего компонента, когда родительский компонент показывает изменения в часах; аналогичным образом, когда изменяется свойство myShow дочернего компонента, уведомите родительский компонент об изменении свойства show через событие emit. Определите метод в родительском компоненте handleShow: функция (значение) { this.show = значение }

5. Фильтры

Vue.js позволяет настраивать фильтры, которые можно использовать для некоторых распространенных форматов текста. Фильтры можно использовать в двух местах: интерполяция двойной фигурной скобки и выражения v-bind (последнее поддерживается начиная с версии 2.1.0+). Фильтры следует добавлять в конце выражений JavaScript, обозначенных символом «труба»:

<!-- 在双花括号中 -->
{{ message | capitalize }}

<!-- 在 `v-bind` 中 -->
<div v-bind:id="rawId | formatId"></div>

Есть два способа определить: определить локальные фильтры через фильтры в параметрах компонента, определить глобальные фильтры через Vue.filter.

// 首字母大写的过滤器
Vue.filter('capitalize', function (value) {
        if (!value) return ''
        value = value.toString()
        return value.charAt(0).toUpperCase() + value.slice(1)
})

Используйте, как показано ниже, с заглавными буквами в заголовке таблицы.

 <th v-for="headName in column">
    {{headName.name | capitalize}}
</th>

6. Резюме

Примеры в этой статье взяты изблог woo woo woo.cn на.com/keep Fool/fear/…, с одной стороны самообучающийся, а с другой переписанный с V2.4.4, и некоторые детали расширены и объяснены.Надеюсь немного вам поможет, спасибо!