Компонентное решение WeChat Mini Program

Апплет WeChat
Компонентное решение WeChat Mini Program

Из версии базовой библиотеки апплета1.6.3Изначально апплет поддерживает простое компонентное программирование. Чтобы просмотреть версию базовой библиотеки Mini Program, которую вы используете, вы можете щелкнуть сведения в правой части инструментов разработчика, чтобы просмотреть:

самые основные компоненты

Компонент апплета на самом деле представляет собой каталог, который должен содержать 4 файла:

  1. xxx.json
  2. xxx.wxml
  3. xxx.wxss
  4. xxx.js

объявить компонент

Сначала вам нужноjsonСделайте объявления пользовательских компонентов в файле (будетcomponentполе установлено наtrueЭтот набор файлов можно установить как пользовательские компоненты)

{
  "component": true
}

Во-вторых, в json-файле страницы, которую нужно ввести в компонент, сделайте объявление ссылки

{
  "usingComponents": {
    //自定义的组件名称     : 组件路径,注意是相对路径,不能是绝对路径  
    "component-tag-name": "path/to/the/custom/component"
  }
}

 

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

По сравнению с введением компонентов Vue решение апплета более лаконичное. Внедрение компонентов vue необходимо одновременно импортировать и регистрировать в компонентах, в то время как компоненты апплета необходимо зарегистрировать только в .json и можно использовать в wxml.

использовать слот

Как и vue, апплет также имеет концепцию слота.

один слот

Шаблон компонента может предоставить<slot>Узел, который используется для переноса дочерних узлов, предоставляемых при ссылке на компонент.

// 主页面内,<addlike>是组件
<addlike item="item" my_properties="sssss">
    <text>我是被slot插入的文本</text>
</addlike>
 
// addlike 组件
<view class="container">
    <view>hello, 这里是组件</view>
    <view>hello, {{my_properties}}</view>
    <slot></slot>
</view>
 
// 渲染后
<view class="container">
    <view>hello, 这里是组件</view>
    <view>hello, {{my_properties}}</view>
    <text>我是被slot插入的文本</text>
</view>

несколько слотов

Если вам нужно использовать несколько слотов в компоненте, вам нужно объявить и включить его в компоненте js:

Component({
  options: {
    multipleSlots: true // 在组件定义时的选项中启用多slot支持
  },
  properties: { /* ... */ },
  methods: { /* ... */ }
})

использовать:

// 主页面
<addlike item="item" my_properties="sssss">
    // 在普通的元素上加入 slot 属性,指定slotname, 就可以变成子元素的slot了
    <text slot="slot1">我是被slot1插入的文本</text>
    <text slot="slot2">我是被slot2插入的文本</text>
</addlike>
 
// 子页面
<view class="container">
    <view>hello, 这里是组件</view>
    <view>hello, {{my_properties}}</view>
    <slot name="slot1"></slot>
    <slot name="slot2"></slot>
</view>


Конструктор компонентов

Как мы только что сказали, компонент должен включать четыре файла: js, wxml, wxss и json. wxml эквивалентен HTML, а wxss эквивалентен css, так что же нужно писать на js?

В случае, предоставленном официальным лицом WeChat:

Component({
 
  behaviors: [],
 
  properties: {
   
  },
  data: {}, // 私有数据,可用于模版渲染
 
  // 生命周期函数,可以为函数,或一个在methods段中定义的方法名
  attached: function(){},
  moved: function(){},
  detached: function(){},
 
  methods: {
    onMyButtonTap: function(){
     
    },
    _myPrivateMethod: function(){
     
    },
    _propertyChange: function(newVal, oldVal) {
 
    }
  }
})

Он вызывает конструктор компонента. Конструктор компонента можно использовать для определения компонентов.При вызове конструктора компонента можно указатьсвойства, данные, методыЖдать. Что можно поместить в конкретный компонент, как показано ниже:

properties Object Map нет Эквивалентно реквизиту Vue, через это свойство внешний мир передает данные в компонент. Внешним свойством компонента является таблица сопоставления имени свойства с настройкой свойства.Настройка свойства может содержать три поля.typeУказывает тип атрибута,valueУказывает начальное значение свойства,observerПредставляет функцию ответа при изменении значения свойства.
data Object нет внутренние данные компонента иpropertiesИспользуются вместе для шаблонного рендеринга компонентов. То есть данные и свойства могут быть получены одновременно через this.data.
methods Object нет Методы компонентов, включая функции ответа на событие и любые пользовательские методы, для использования функций ответа на событие см.события компонентов
behaviors String Array нет Механизм повторного использования кода между компонентами, аналогичный миксинам и трейтам, см.behaviors
created Function нет Функция жизненного цикла компонента, выполняемая, когда экземпляр компонента входит в дерево узлов страницы, обратите внимание, что ее нельзя вызвать в это время.setData
attached Function нет Функция жизненного цикла компонента, выполняемая, когда экземпляр компонента входит в дерево узлов страницы.
ready Function нет Функция жизненного цикла компонента, которая выполняется после завершения компоновки компонента, и в это время может быть получена информация об узле (используяSelectorQuery)
moved Function нет Функции жизненного цикла компонента, выполняемые при перемещении экземпляра компонента в другое место в дереве узлов.
detached Function нет Функции жизненного цикла компонента, выполняемые при удалении экземпляра компонента из дерева узлов страницы.
relations Object нет Определенные отношения между компонентами, см.отношения между компонентами
options Object Map нет Некоторые параметры компонента, см. описания в других разделах документации.

 

Компоненты и передача данных

Компонентизация неизбежно включает в себя передачу данных.Чтобы решить проблему обслуживания данных между компонентами, у vue, react и angular есть разные решения. Небольшое программное решение намного проще.

Главная страница передает данные компоненту

Свойства эквивалентны свойствам Vue, которые являются точками входа для входящих внешних данных.

// 主页面使用组件
<a add_like="{{add_like}}">
</a>
 
// 组件a.js 内
Component({
    properties:{
        add_like:{
            type:Array,
            value:[],
            observer:function(){
                
            }
        }
    }
})

Примечание. Входящие данные, независимо от того, являются ли они простым типом данных или ссылочным типом,копия значенияТакой же(Отличие от описания в красной книге сокровищ состоит в том, что параметры функции js передаются как копирование значений, смысл в красной книге сокровищ таков: простые типы данных копируют значения напрямую, а ссылочные типы копируют ссылки, то есть изменяют свойства объекта параметра внутри функции, влияет на свойства объектов вне функции).

Если это реквизит Vue, его можно синхронизировать через .sync, а в подкомпоненте апплета вызов this.setData() для изменения данных в родительском компоненте не повлияет на данные в родительском компоненте, то есть дочернем. изменение свойства компонента, похоже, не имеет ничего общего с родительским компонентом. Итак, если вы измените данные родительского компонента в дочернем компоненте или даже измените данные в родственном компоненте, есть ли простой способ? Он будет упомянут ниже

 

Компонент исходящих данных на главную страницу

Как и в Vue, основной формой взаимодействия между компонентами являются пользовательские события.

компоненты проходят this.triggerEvent()Запустите пользовательское событие, главная страница находится на компонентеbind:component_method="main_page_mehod"для получения пользовательских событий.

в,this.triggerEvent()Помимо получения имени пользовательского события, метод также получает два объекта:eventDetailа такжеeventOptions.

// 子组件触发自定义事件
ontap () {
    // 所有要带到主页面的数据,都装在eventDetail里面
	var eventDetail = {
		name:'sssssssss',
		test:[1,2,3]
	}
	// 触发事件的选项 bubbles是否冒泡,composed是否可穿越组件边界,capturePhase 是否有捕获阶段
	var eventOption = {
		composed: true
	}
	this.triggerEvent('click_btn', eventDetail, eventOption)
}
 
// 主页面里面
main_page_ontap (eventDetail) {
    console.log(eventDetail)
    // eventDetail
    // changedTouches
    // currentTarget
    // target
    // type
    // ……
    // detail   哈哈,所有的子组件的数据,都通过该参数的detail属性暴露出来
}

обмен данными между компонентами

В отличие от решения vuex, предложенного vue, связь между компонентами апплета проста и компактна. Вы можете использовать пользовательские события для связи так же, как главная страница взаимодействует с компонентами.Конечно, более простой и удобный способ — использовать отношения, предоставляемые апплетом.

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

Component({
   relations:{
    './path_to_b': {                 // './path_to_b'是对方组件的相对路径
        type: 'child',               //  type可选择两组:parent和child、ancestor和descendant
        linked:function(target){  }  // 钩子函数,在组件linked时候被调用 target是组件的实例,
        linkChanged: function(target){}
        unlinked: function(target){}
        }
    },
})

Например, есть два компонента, как показано в коде:

// 组件a slot 包含了组件b
<a>
    <b></b>
</a>

Связь между ними показана на следующем рисунке:

 

Два компонента захватывают экземпляр другого компонента с помощью метода this.getRelationNodes('./path_to_a'). Теперь, когда экземпляр противоположного компонента получен, вы можете получить доступ к данным противоположного компонента, а также можете установить данные противоположного компонента, но вы не можете вызвать метод противоположного компонента.

 

// 在a 组件中
Component({
    relations:{
        './path_to_b': {
            type: 'child',
            linked:function(target){  }  // target是组件b的实例,
            linkChanged: function(target){}
            unlinked: function(target){}
        }
    },
    methods:{
        test () {
            var nodes = this.getRelationNodes('./path_to_b')
            var component_b = nodes[0];
            
            // 获取到b组件的数据
            console.log(component_b.data.name)
            
            // 设置父组件的数据
            // 这样的设置是无效的
            this.setData({
                component_b.data.name:'ss'
            })
            // 需要调用对方组件的setData()方法来设置
            component_b.setData({
                name:'ss'
            })
        }
    }
})
 
// 在b 组件里面
Component({
    relations:{
        './path_to_a': {                      //注意!必须双方组件都声明relations属性
            type:'parent'
        }
    },
    data: {
        name: 'dudu'
    }
})

 

Примечание: 1. При использовании компонентов на главной странице у вас не может быть номеров, таких как или , вы можете задать новое имя в JSON на главной странице.

{
    "usingComponents":{
        "test_component_subb": "../../../components/test_component_sub2/test_component_sub2"
    }
}

2. Путь в отношениях, например здесь:

Быть

Это реальный относительный путь противоположного компонента, а не логический путь между компонентами.

3. Если отношения не связаны, то this.getRelationNodes не может получить другой компонент

4. Этот компонент не может получить экземпляр этого компонента, использование this.getRelatonsNodes('./path_to_self ') вернет null

4. тип можно выбратьparent,child,ancestor,descendant 

 


Теперь, когда мы можем передавать данные между двумя компонентами, как передать данные между несколькими компонентами?

 

Как показано на рисунке выше, компонент b одного уровня и компонент c одного уровня не могут быть получены напрямую между b и c. b может получить a, c также может получить a, а a может напрямую получить b и c. . Следовательно, если вы хотите получить родственный элемент, вам нужно сначала получить узел-предок, а затем получить узел-брат через узел-предок.

я здесь组件bВнутри мне нужно сначала найти祖先组件aэкземпляр, затем используйте祖先组件aслучайgetRelationNodesспособ получить组件cпример. Быть

ты это видел? Боюсь, нам снова придется писать много повторяющегося кода.

 

К счастью, апплет WeChat также предоставляет атрибут поведения, эквивалентный примеси, который прост для понимания и позволяет улучшить повторное использование кода.

Идеи:

Предположим, что в настоящее время имеется три компонента: компонент a, компонент b и компонент c, где компонент b и компонент c являются одноуровневыми компонентами, а компонент a является одноуровневым компонентом b и c. Чтобы уменьшить дублирование кода, мы инкапсулируем метод получения родительского компонента и метод получения родственного компонента, а также инкапсулируем их в методы поведения. Пока это компонент, который вводит поведение, метод может быть вызван удобно.

выполнить:

Создайте новый файл поведения, имя не имеет значения, например, ratio_behavior.js.

// 在 get_relation.js 文件里面
module.exports = Behavior({
	methods:{
	    // 获取父组件实例的快捷方法
		_parent () {
			// 如果根据该路径获取到acestor组件为null,则说明this为ancesor
			var parentNode =  this.getRelationNodes('../record_item/record_item')
			if (parentNode&&parentNode.length !== 0) {
				return parentNode[0]
			} else {
				return this
			}
		},
		// 获取兄弟组件实例的快捷方法
		_sibling(name) {
			var node = this._parent().getRelationNodes(`../${name}/${name}`)
			if (node &&node.length > 0) {
				return node[0]
			}
		}
	}
})

Затем введите поведение для компонента b и компонента c и вызовите метод, чтобы получить экземпляры родительских компонентов и одноуровневых компонентов.

// 组件b中
var relation_behavior = require('./path_to_relation_behavior')
Component({
    behaviors:[relation_behavior],
    methods:{
        test () {
            // 获得父组件的实例
            let parent = this._parent()
            
            // 访问父组件的数据d
            console.log(parent.data.name)
            
            // 修改父组件的数据
            parent.setData({
                name: 'test1'
            })
            
            // 获得兄弟组件的实例
            let sibling = this._sibling('c')
            
            // 访问兄弟组件的数据
            console.log(sibling.data.name)
            
            // 修改兄弟组件的数据
            sibling.setData({
                name:"test"
            })
        }
    }
})
 
// 组件c中
var relation_behavior = require('./path_to_relation_behavior')
Component({
    behaviors:[relation_behavior],
    methods:{
        test () {
            // 获得父组件的实例
            let parent = this._parent()
            
            // 访问父组件的数据d
            console.log(parent.data.name)
            
            // 修改父组件的数据
            parent.setData({
                name: 'test1'
            })
            
            // 获得兄弟组件的实例
            let sibling = this._sibling('b')
            
            // 访问兄弟组件的数据
            console.log(sibling.data.name)
            
            // 修改兄弟组件的数据
            sibling.setData({
                name:"test"
            })
        }
    }
})

 

 

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

Однако областью действия компонента b и компонента c является область действия главной страницы, а входящие свойства — свойства главной страницы, что обеспечивает гибкость данных компонента. Отношения связывают множество компонентов наподобие невидимой цепочки.Связанные компоненты могут обращаться друг к другу и изменять данные друг друга, но каждый компонент может независимо получать данные из внешнего мира.

 

Прочитав так много теоретических вещей, мне все еще нужен конкретный сценарий для применения.

Например, у нас есть страница для обмена и записи настроения картинки, когда пользователь нажмет кнопку [Нравится], кнопка лайк записи настроения станет красной, а имя того, кто поставил лайк, добавить в следующую колонку.

 

Если это не компонентизация, то, скорее всего, будет изменена кнопка «Нравится», затем будет выполнен обход данных для обновления данных, и, наконец, будет повторно отображено состояние всех списков записей.

Если он разделен на компоненты: инкапсулируйте кнопку «Мне нравится» как组件b, поле подобного человека ниже инкапсулировано как组件c,Каждая запись настроения — это组件a

Ниже приведена реализация кода

// 在主页面内
<view wx:for='{{feed_item}}'>
    <a item='{{item}}'>
        <b></b>
        <c></c>
    </a>
<view>
 
 
// 在组件a内
var behavior_relation = require('../../relation_behavior.js)  //这里引入上文说的Behavior
Component({
    behaviors:[behavior_relation],
    relations:{
        '../b/b':{
            type: 'descendant'
        }
    }
})
 
// 在组件b内
var behavior_relation = require('../../relation_behavior.js)  //这里引入上文说的Behavior
Component({
    behaviors:[behavior_relation]
    relations:{
        '../a/a':{
            type: 'ancestor'
        }
    },
    data: {
        is_like: false  //控制点赞图标的状态
    },
    methods:{
        // 当用户点赞的时候
        onClick () {
            //  修改本组件的状态
            this.setData({
                is_like: true
            })
            // 修改 c 组件的数据
            this._sibling('c').setData({
                likeStr: this._sibling('c').data.likeStr + '我' 
            })
        }
    }
})
 
// 在组件c内
var behavior_relation = require('../../relation_behavior.js)  //这里引入上文说的Behavior
Component({
    behaviors:[behavior_relation],
    relations:{
        '../a/a':{
            type: 'ancestor'
        }
    },
    data:{
        likeStr:'晓红,小明'
    }
})

Таким образом, компонент b может изменять данные в компоненте c. В то же время компонент b и компонент c могут поддерживать независимый обмен данными с главной страницей через систему свойств и событий.