🐈 Евгений

Илья Таратухин

Вы кто такие?

1) Мы идём в ногу со временем

2) В зале есть профессионалы Vue?

3) Мы видели некоторое дерьмо

Почему Vue?

~30Kb
~35Kb
~42Kb

Какой план?

Какой план?

  • Будем сравнивать с react, иногда с angular
  • Разберем сильные стороны
  • Обратим внимание на слабые стороны
  • Дадим рекомендации

http://bit.ly/vue-react-ng

1. КМБ

Установка

  • через CDN включив <script> тег в HTML
  • через npm
  • через Bower
  • используя Vue-cli

Vue-cli

0kestrel ~/reps0kestrel ~/repsvueinitwebpackvue-testdownloadingtemplatedownloadingtemplatedownloadingtemplatedownloadingtemplatedownloadingtemplatedownloadingtemplatedownloadingtemplatedownloadingtemplatedownloadingtemplatedownloadingtemplate?Projectname(vue-test)?Projectnamevue-test?Projectdescription(AVue.jsproject)?ProjectdescriptionAVue.jsproject?Author(EvgenyGusev<bunopus@gmail.com>)?AuthorEvgenyGusev<bunopus@gmail.com>?Vuebuild(Usearrowkeys)Runtime+Compiler:recommendedformostusersRuntime-only:about6KBlightermin+gzip,buttemplates(oranyVue-specificHTML)areONLYallowedin.vuefiles-renderfunctionsarerequiredelsewhere?Vuebuildstandalone?Installvue-router?(Y/n)?Installvue-router?Yes?UseESLinttolintyourcode?(Y/n)?UseESLinttolintyourcode?Yes?PickanESLintpreset(Usearrowkeys)Standard(https://github.com/standard/standard)Airbnb(https://github.com/airbnb/javascript)none(configureityourself)?PickanESLintpresetStandard?Setupunittests(Y/n)?SetupunittestsYes?Pickatestrunner(Usearrowkeys)JestKarmaandMocha?Pickatestrunnerjest?Setupe2etestswithNightwatch?(Y/n)?Setupe2etestswithNightwatch?Yes?Shouldwerun`npminstall`foryouaftertheprojecthasbeencreated?(recommended)(Usearrowkeys)Yes,useNPMYes,useYarnNo,Iwillhandlethatmyself?Shouldwerun`npminstall`foryouaftertheprojecthasbeencreated?(recommended)npmvue-cli·Generated"vue-test".#Installingprojectdependencies...#========================0kestrel ~/repsvueinitwebpackvue-test0kestrel ~/repsvueinitwebpackvue-test
  • спрашивает при установке, чего ты хочешь
  • предлагает большое количество шаблонов
  • позволяет делать шаблоны самому

¯\_(ツ)_/¯

"Философия" create-react-app

One Dependency:There is just one build dependency.

No Configuration Required: You don't need to configure anything.

No Lock-In: You can “eject” to a custom setup at any time.

Итого:

$$$ Соц. сеть $$$

2. DOM

Virtual DOM. Как он работает?

SFC -> JS

<template> <div id="hello"> <h1>{{ msg }}</h1> </div> </template> <script> export default { name: 'hello', data () { return { msg: 'Welcome to Your Vue.js App' } } } </script> <style scoped> #hello { font-family: 'Avenir', Helvetica, Arial, sans-serif; } </style>

Vue template compiler


<div id="hello">
  <h1>{{ msg }}</h1>
</div>
						

render


function render() {
  with(this) {
    return _c('div',{attrs:{"id":"hello"}},[_c('h1',[_v(_s(msg))])])
  }
}
// staticRenderFns:[]
								

render


render: function (createElement) { // or h
  return createElement('div',
    {
      attrs: {"id": "hello"}
    },
    [
      createElement('h1',
        [
          Vue.createTextVNode(_toString(msg)) // _v(_s(msg))
        ]
      )
    ]
  )
}
							

Vue template compiler


<div>
  <p>Hello!</p>
</div>
						

staticRenderFns


_m(0): function anonymous() {
  with(this){return _c('div',[_c('p',[_v("Hello!")])])}
}
								
Virtual DOM во Vue
vue/src/core/vdom/vnode.js

export default class VNode {
  tag: string | void;
  data: VNodeData | void;
  children: ?Array<VNode>
  text: string | void;
  elm: Node | void;
  ns: string | void;
  context: Component | void; // rendered in this component's scope
  key: string | number | void;
  componentOptions: VNodeComponentOptions | void;
  componentInstance: Component | void; // component instance
  parent: VNode | void; // component placeholder node
  //...
                        
vue/src/core/vdom/patch.js


/**
 * Virtual DOM patching algorithm based on Snabbdom by
 * Simon Friis Vindum (@paldepind)
 * Licensed under the MIT License
 * https://github.com/paldepind/snabbdom/blob/master/LICENSE
 *
 * modified by Evan You (@yyx990803)
 *
 * Not type-checking this because this file is perf-critical and the cost
 * of making flow understand it is not worth it.
 */
                        

// vue/src/core/vdom/patch.js
function createElm (
  // ...
  vnode.elm = vnode.ns
    ? nodeOps.createElementNS(vnode.ns, tag)
    : nodeOps.createElement(tag, vnode)

export function createPatchFunction (backend) {
  const { modules, nodeOps } = backend
  // ...



// vue/src/platforms/web/runtime/node-ops.js export function createElement (tagName: string, vnode: VNode): Element { const elm = document.createElement(tagName)
Переиспользование DOM
Reuse Dom - Vue (key), React (key), Angular (trackBy)

                            <div v-for="item in items" :key="item.id">
                              <!-- content -->
                            </div>
                        
Инкапсуляция стилей

<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
  div {
    color: red;
  }
</style>
                        

<div data-v-469af010="">
    <ul data-v-469af010="">
        <li data-v-469af010="">
            <div data-v-469af010="" >
                        
Классические шаблоны/JSX
JSX
Классические шаблоны
Классический VDOM
React Fiber
¯\_(ツ)_/¯
SSR на функциях
SSR на функциях
SSR на функциях

3. Binding

Как это работает?

<template>
  <div>
    {{ message }}
    <button v-on:click="message = 'Yay!'"></button>
  </div>
</template>

<script>
export default {
  data () {
    return {
      message: "Hello, World!"
    }
  }
</script>
                        
NgZone + Monkey Patching

¯\_(ツ)_/¯

АТОМЫ


//...
data () {
  return {
    message: 'Hello, World!'
  }
},
//...
                        

{__ob__: Observer}
  message:(...)
  ▼ __ob__:Observer
    ▶ dep: Dep {id: 8, subs: Array(0)}
    ▶ value: {__ob__: Observer}
    vmCount:1
    __proto__: Object
  ▶ get message: ƒ reactiveGetter()
  ▶ set message:ƒ reactiveSetter(newVal)
  ▶ __proto__: Object
                        
vue/src/core/observer/index.js

 /**
   * Walk through each property and convert them into
   * getter/setters. This method should only be called when
   * value type is Object.
   */
  walk (obj: Object) {
    const keys = Object.keys(obj)
    for (let i = 0; i < keys.length; i++) {
      defineReactive(obj, keys[i])
    }
  }

                        

Проблемы?

Memory Overhead (20x)

40b -> 760b

Классические шаблоны/JSX
JSX
Классические шаблоны
Классический VDOM
React Fiber
¯\_(ツ)_/¯
Атомы
Ничего :-(
Зоны

4. Data flow

Vuex

State management pattern + library

									Vue.use(Vuex)
									//...
									let app = new Vue({
									  el: '#app',
									  store,
									  components: { App },
									  template: '<App/>'
									})
								

const store = createStore(rootReducer)
//...
render(
  <Provider store={store}>
    <App />
  </Provider>,
  document.getElementById('root')
)
								

const Shawermeter = {
  template: `<div>{{ score }}</div>`,
  computed: {
    count () {
      return this.$store.state.score
    }
  }
}
								

function Shawermeter({score}) {
  return <div>{ score }</div>
}

export default connect(
    state => {score: state.score}
)(Shawermeter)
                                
Геттеры

const store = new Vuex.Store({
  state: {
    reviews: [{ score: 4.3, text: '...'}, { score: 3.2, text: '...'}]
  },
  getters: {
    topReviews: state => {
      return state.reviews.filter(review => reviews.score > 4)
    }
  }
})
								
  1. Перестраиваем state
  2. Вычисляем в функции render
Мутации

const store = new Vuex.Store({
  state: { count: 1 },
  mutations: {
    increment (state) {
      state.count++
    }
  }
})
//...
store.commit('increment')
								
Action -> reducer -> store update
Мутации должны:
  • Следовать правилам реактивности Vue
  • (Хорошо бы) Использовать константы для типов
  • Быть синхронными
Action -> reducer -> store update
Действия (Actions)

actions: {
  incrementAsync ({ commit }, amount) {
    setTimeout(() => {
	  commit('increment', amount)
    }, 1000)
  }
}
//...
store.dispatch('incrementAsync', {  amount: 10  })
								

function incrementAsync(amount) {
  return function(dispatch) {
    setTimeout(() => {
      dispatch({type: 'increment', amount}), 1000)
    }
  }
}
								

actions: {
  actionA ({ commit }) {
    return new Promise((resolve, reject) => {
      // resolve or reject
    })
  }
}
//...
store.dispatch('actionA').then(() => { //...
								

function actionA(data) {
  return function(dispatch) {
    dispatch({type: 'startActionA'})
    asyncFunction.then(dispatch({type: 'endActionA', data})
  }
}
//...
dispatch(actionA).then(() => { //...

Vuex vs Redux

  • Чуть больше сахара
  • Проще

5. Выводы

Классические шаблоны/JSX
JSX
Классические шаблоны
Классический VDOM
React Fiber
¯\_(ツ)_/¯
Атомы
Ничего :-(
Зоны
Универсальность
Кастомизация
Фичи

Конец

Angular vs React