const monthToInt = val => {
val = val.toLowerCase();
switch (true) {
case !!val.match(/^янв/): return 0;
// TODO добавить больше кейсов
}
}
const dateFromLocaleString = date =>
new Date(
...date
.split(" ")
.reduce(
(acc, val) =>
(acc = [val.match(/\D/) ? monthToInt(val) : parseInt(val), ...acc]),
[]
)
)
dateFromLocaleString('07 янв 2019')
example.com
dev.example.com // как я понимаю слушает только 192.168...
или
example.com/
example.com/dev // imho не удобно
<body>
<div id="app">
<p> {{ calculationResult | rub }}</p>
<!-- Плагин range slider инициализируется через директиву
В кавычка объект из binding.value из сигнатуры хука -->
<div v-range-slider="{ min: 0, max: 10 }"></div>
<!-- Подписка на события осуществляется прямо в html -->
<button @click="sendToChat">Отправить в чат</button>
<button @click="toggleChat">Открыть чат</button>
<button @click="toggleChat">Закрыть чат</button>
<!-- Чат изолирован от остальной разметки в отдельный компонент -->
<chat-app v-show="isChatOpen" :result="calculationResult" />
</div>
</body>
Vue.component('chat-app', {
// Шаблон это HTML + суперспособности vue, например ref
template: '<div ref="chat"></div>',
// Аргумненты компонента, позволяет делать
// - аннотации типов
// - валидация
// - дефолтные значения
props: {
result: { type: Number }
},
mounted() {
// Здесь нет смысла использовать директиву, инициализируем чат когда компонент окажется в ДОМе
// Вместо селекта элемента по например id, мы напрямую получаем из ранее созданной рефки
this.chatInstance = new SomeChatPlugin(this.$refs.chat)
},
watch: {
// При изменении пропа result, в текстовое поле чата будет добавлен текст
result(newValue) {
this.chatInstance.fillText(newValue)
}
}
})
new Vue({
el: '#app',
filters: {
// Фильтры это особый способ вызова функции
// rub(value) === value | rub
rub(v) {
const value = Math.round(v)
switch(value) {
case 1: return `${value} рубль`
case 2:
case 3:
case 4: return `${value} рубля`
default: return `${value} рублей`
}
}
},
directives: {
rangeSlider: {
// Директивы имеют свой жизненный цикл
// Хук inserted это ближайшее к $(document).ready() событие
/**
* @param {HTMLElement} el
* @param {Object} binding https://ru.vuejs.org/v2/guide/custom-directive.html#Аргументы-хуков
* @param {Vue} context vue instance
*/
inserted(el, binding, context) {
$(el).someFancyRangeSlide(binding.value)
.on('move', newValue => context.updateValue(newValue))
// События выпускаемые jquery будут обработаны vue
}
}
},
data() {
return {
value: 0,
calculationResult: 0,
isChatOpen: false,
}
},
methods: {
updateValue(newValue) {
this.value = newValue
},
thoggleChat() {
this.isChatOpen = !this.isChatOpen
},
sendToChat() {
this.calculationResult = this.value
}
}
})
class TodoList extends React.Component {
state = {
todos: [
'Commit',
'Push'
]
}
render() {
return <ul>
{this.state.todos.map(item => {
return <li>{ todo }</li>
}
</ul>
}
}
const todos = [
'Init',
'Commit',
'Push'
]
// Начальный стейт
<ul>
<li>Commit</li>
<li>Push</li>
</ul>
// Добавлен элемент
<ul>
<li>Init</li> // <- разница начинается здесь и до конца древа
<li>Commit</li>
<li>Push</li>
</ul>
const todos = [
'Commit',
'Push',
'Merge'
]
// Начальный стейт
<ul>
<li>Commit</li>
<li>Push</li>
</ul>
// Добавлен элемент
<ul>
<li>Commit</li>
<li>Push</li>
<li>Merge</li> <- разница начинается здесь, от начала и до сих по ничего не менялось
</ul>
<li>Commit</li>
и <li>Push</li>
не менялись, однако реакт недостаточно умён чтобы это понять. Чтобы помочь ему следует воспользоваться специальным пропом key={}
. Он может быть значением любого типа, единственно требование — значение должно стабильно идентифицировать соответствующие данные.class TodoList extends React.Component {
state = {
todos: [
{ id: 0, text: 'Commit' },
{ id: 1, text: 'Push' }
]
}
render() {
return <ul>
{this.state.todos.map(item => {
return <li key={todo.id}>{ todo.text }</li>
}
</ul>
}
}
const todos = [
{ id: 2, text: 'Init' },
{ id: 0, text: 'Commit' },
{ id: 1, text: 'Push' }
]
// Начальный стейт
<ul>
<li>Commit</li> // id 0
<li>Push</li> // id 1
</ul>
// Добавлен элемент
<ul>
<li>Init</li> // id 2 новый элемент отобразится в начале
<li>Commit</li> // id 0
<li>Push</li> // id 1
</ul>
Math.random()
в качестве ключа, так вы почти гарантировано будете всегда получать нестабильные идентификаторы.<template>
<form>
<input v-model="text" />
<button :disabled="!isValid">Submit</button>
</form>
</template>
<script>
export default {
data() {
return {
text: ''
}
},
computed: {
isValid() {
return validator(this.text)
}
}
}
</script>
v-if
не отрендерит разметку никогда, если её биндинг не вернёт true
.v-show
отрендерит разметку, но в зависимости от своего биндинга установит display: hidden
на элемент.const { name, email } = obj
class Buttons extends Component {
state = {
foo: 'bar'
}
changeText = () => {
if (this.state.foo === 'bar') {
this.setState({ foo: 'foo' })
} else {
this.setState({ foo: 'bar' })
}
}
render() {
return (
<Button text={this.foo} />
<Button text={this.foo} />
<Button text={this.foo} />
<Button text={'Изменить текст'} onClick={this.changeText} />
)
}
}
CategoiesFilter
расширит класс React.Component
, вам будет нужно реализовать метод render
. Тогда вы сможете избавиться от селекторов по заранее написанному html.killall vim
find ./ -name *.swp -exec rm {} +
mkdir -p ~/.vim/backup
echo 'set dir=$HOME/.vim/backup' >> ~/.vimrc
vim
[выйти из вима]
find ./ -name *.swp -exec rm {} +
eth0
из контейнера без использования network: host
, будет тем же самым что и использование network: host
.docker run --network host ...
some-service:
network_mode: 'host'
libmagic
.