Slide 1

Slide 1 text

©2022 RAKUS Co., Ltd. Svelteとは? ~React、Vueとの違いから学ぶ~ UI開発課 田口 真輝

Slide 2

Slide 2 text

目次 ● Svelteとは? ● React、Vueとの実装比較 ● 実際に記述した感想 ● まとめ 2

Slide 3

Slide 3 text

Svelteとは? 3

Slide 4

Slide 4 text

Svelteとは? 下記のような特徴を持つWebフレームワーク ● コードの記述量が少ない ● 仮想DOMを使わない ● Reactiveに書ける ● バンドルサイズが小さい 4

Slide 5

Slide 5 text

Svelteとは?~仮想DOMを使わない~ 従来のDOM構築…ページ変更→DOM全体を再描画 仮想DOMとは… 1. インメモリ内で仮想のDOMを構築し、 2. DOMの操作が行われたらまず仮想DOM内で変更箇所を 検知し、(差分検出) 3. 変更箇所のみを再描画する →余分なレンダリングが抑えられる 5

Slide 6

Slide 6 text

Svelteとは?~仮想DOMを使わない~

foo

bar

変更したい→仮想DOMはここだけ変更 6

Slide 7

Slide 7 text

Svelteとは?~仮想DOMを使わない~ 仮想DOM有能! なのに… 何故Svelteは使わないの??? 7

Slide 8

Slide 8 text

Svelteとは?~仮想DOMを使わない~ 問題点 1. 差分検出のコスト 2. props受け渡しによる余分な再描画問題 親から子に渡しているprops内の状態に変化があると、 子コンポーネントで再描画が発生してしまう 8

Slide 9

Slide 9 text

Svelteとは?~仮想DOMを使わない~ 解決策 ● コンパイル時に、変更がありそうな箇所のDOMを書き換える JSファイルを生成 →「Svelteはコンパイラ」 9

Slide 10

Slide 10 text

Svelteとは?~Reactiveに書ける~ let count = 0; $: doubled = count * 2; data: { count: 0 }, computed: { doubled: function() { return this.count * 2; } } Svelte Vue 10 countが変化するとdoubledも即変化する

Slide 11

Slide 11 text

Svelteとは?~バンドルサイズが小さい~ 11

Slide 12

Slide 12 text

Svelteとは?~バンドルサイズが小さい~ 12

Slide 13

Slide 13 text

React、Vueとの比較 13

Slide 14

Slide 14 text

React、Vueとの比較 下記の観点で比較(Svelte、Vueは3系) ● Hello World! ● スタイリング ● Container、Presentationコンポーネント分割 ● API処理 ● 状態管理 14

Slide 15

Slide 15 text

React、Vueとの比較~Hello World~ 15

Slide 16

Slide 16 text

Svelte、Reactの比較~Hello World~ 1. 2. const hello = "Hello World!"; 3. 4. 5. 6.

{hello}

7. 8. 9. 1. import './App.css'; 2. 3. function App() { 4. const hello = "Hello World!" 5. return ( 6.

{hello}

7. ); 8. } 9. 10. export default App; App.svelte App.js 16 JSX形式 SFC形式

Slide 17

Slide 17 text

Svelte、Vueの比較~Hello World~ 1. 2. const hello = "Hello World!"; 3. 4. 5. 6.

{hello}

7. 8. 9. 1. 2.

{{ hello }}

3. 4. 5. 6. export default { 7. name: "App", 8. setup() { 9. return { 10. hello: "Hello World!", 11. }; 12. }, 13. }; 14. 15. 16. App.svelte App.vue 17 SFC形式 SFC形式

Slide 18

Slide 18 text

React、Vueとの比較~スタイリング~ 18

Slide 19

Slide 19 text

Svelte、Reactの比較~スタイリング~ 1. 2. let count = 0; 3. const countUp = () => count++; 4. const countDown = () => count--; 5. 6. 7. 8. - 9. {count} 10. + 11. 12. 13. 14. span.minus { 15. color: red; 16. } 17. 1. import './App.css'; 2. import { useState } from 'react'; 3. 4. function App() { 5. const [count, setCount] = useState(0); 6. const countUp = () => setCount(count + 1); 7. const countDown = () => setCount(count - 1); 8. 9. const minus = { 10. color: count < 0 && "red", 11. } 12. 13. return ( 14. <> 15. - 16. {count} 17. + 18. > 19. ); 20. } 21. 22. export default App; App.svelte App.js 19 css-modules的書き方 css-modules的書き方

Slide 20

Slide 20 text

Svelte、Vueの比較~スタイリング~ 1. 2. let count = 0; 3. const countUp = () => count++; 4. const countDown = () => count--; 5. 6. 7. 8. - 9. {count} 10. + 11. 12. 13. 14. span.minus { 15. color: red; 16. } 17. 1. 2.
3. - 4. {{ count }} 5. + 6.
7. 8. 9. 10. import { ref } from "vue"; 11. export default { 12. name: "App", 13. setup() { 14. const count = ref(0); 15. return { 16. count, 17. countDown : () => count.value--, 18. countUp: () => count.value++, 19. }; 20. }, 21. }; 22. 23. 24. 25. span.minus { 26. color: red; 27. } 28. App.svelte App.vue 20 css-modules的書き方

Slide 21

Slide 21 text

React、Vueとの比較~コンポーネント分割~ 21

Slide 22

Slide 22 text

Svelte、Reactの比較~コンポーネント分割~ 1. 2. import TodoContainer from "./TodoContainer.svelte"; 3. 4. 5. 6. 7. 1. import './App.css'; 2. import TodoContainer from './TodoContainer'; 3. 4. function App() { 5. 6. return ( 7. <> 8. 9. > 10. ); 11. } 12. 13. export default App; App.svelte App.js Todo TodoContainer Todo TodoContainer 22 script省略

Slide 23

Slide 23 text

1. 2. import Todo from "./Todo.svelte" 3. 4. let todoList = []; 5. let value = ""; 6. 7. const addTodo = () => { 8. todoList = [...todoList, value]; 9. value = ""; 10. } 11. 12. const deleteTodo = (index) => { 13. todoList.splice(index, 1); 14. todoList = todoList 15. } 16. 17. 18.
19. 20.
1. import { useState } from 'react'; 2. import Todo from './Todo'; 3. 4. function TodoContainer () { 5. const [todoList, setTodoList] = useState([]); 6. const [val, setVal] = useState(""); 7. 8. const addTodo = () => { 9. setTodoList([...todoList, val]); 10. setVal(""); 11. } 12. 13. const deleteTodo = (index) => { 14. const newTodoList = [...todoList]; 15. newTodoList.splice(index, 1); 16. setTodoList(newTodoList) 17. } 18. 19. return ( 20. <> 21. 22. > 23. ); 24. } 25. 26. export default TodoContainer ; App.js Todo TodoContainer App.svelte Todo TodoContainer 23 Svelte、Reactの比較~コンポーネント分割~ 再生成 再生成

Slide 24

Slide 24 text

1. 2. export let todoList; 3. export let value; 4. export let addTodo; 5. export let deleteTodo; 6. 7. 8.
9.
10. 11. 追加 12.
13.
14. {#each todoList as todo, i} 15.
{todo}×
16. {/each} 17.
18.
1. function Todo({val, setVal, todoList, addTodo, deleteTodo }) { 2. return ( 3. <> 4.
5. setVal(e.target.value)}/> 6. addTodo()}>追加 7.
8.
9. {todoList.map((todo, i) => ( 10.
{todo} deleteTodo (i)}>×
11. )) } 12.
13. > 14. ); 15. } 16. 17. export default Todo; App.js Todo TodoContainer App.svelte Todo TodoContainer 24 Svelte、Reactの比較~コンポーネント分割~

Slide 25

Slide 25 text

Svelte、Vueの比較~コンポーネント分割~ 1. 2. import TodoContainer from "./TodoContainer.svelte"; 3. 4. 5. 6. 7. 1. 2. 3. 4. 5. 6. import TodoContainer from "./components/TodoContainer.vue" ; 7. export default { 8. name: "App", 9. components: { 10. TodoContainer , 11. }, 12. }; 13. App.vue Todo TodoContainer App.svelte Todo TodoContainer 25

Slide 26

Slide 26 text

Svelte、Vueの比較~コンポーネント分割~ 1. 2. import Todo from "./Todo.svelte" 3. 4. let todoList = []; 5. let value = ""; 6. 7. const addTodo = () => { 8. todoList = [...todoList, value]; 9. value = ""; 10. } 11. 12. const deleteTodo = (index) => { 13. todoList.splice(index, 1); 14. todoList = todoList 15. } 16. 17. 18.
19. 20.
1. 2. 8. 9. 10. 11. import Todo from "./Todo.vue"; 12. import { ref } from "vue"; 13. export default { 14. name: "TodoContainer", 15. components: { 16. Todo, 17. }, 18. setup() { 19. const todoList = ref([]); 20. const val = ref(""); 21. 22. const addTodo = () => { 23. todoList.value.push(val.value); 24. val.value = ""; 25. }; 26. const deleteTodo = (index) => { 27. todoList.value.splice(index, 1); 28. }; 29. return { 30. todoList, 31. val, 32. addTodo, 33. deleteTodo, 34. }; 35. }, 36. }; 37. App.vue Todo TodoContainer App.svelte Todo TodoContainer 26 破壊的代入で更新

Slide 27

Slide 27 text

Svelte、Vueの比較~コンポーネント分割~ 1. 2. export let todoList; 3. export let value; 4. export let addTodo; 5. export let deleteTodo; 6. 7. 8.
9.
10. 11. 追加 12.
13.
14. {#each todoList as todo, i} 15.
{todo}×
16. {/each} 17.
18.
1. 2.
3.
4. 5. 追加 6.
7.
8.
9. {{ todo }}× 10.
11.
12.
13. 14. 15. 16. export default { 17. name: "TodoPresentation", 18. props: { 19. todoList: Array, 20. val: String, 21. addTodo: Function, 22. deleteTodo: Function, 23. }, 24. setup() { 25. return; 26. }, 27. }; 28. App.vue Todo TodoContainer App.svelte Todo TodoContainer 27 バインド バインド

Slide 28

Slide 28 text

React、Vueとの比較~API処理~ 28

Slide 29

Slide 29 text

Svelte、Reactの比較~APIモック~ 1. // APIモック 2. const fetchTodoList = async() => { 3. const data = await new Promise(resolve => { 4. setTimeout(() => { 5. resolve(["テスト1", "テスト2", "テス ト3"]) 6. }, 2000) 7. }) 8. todoList = [...todoList, ...data]; 9. }; 1. // APIモック 2. const fetchTodoList = async() => { 3. const data = await new Promise(resolve => { 4. setTimeout(() => { 5. resolve(["テスト1", "テスト2", "テスト3"]) 6. }, 2000) 7. }) 8. setTodoList([...todoList, ...data]); 9. }; React Svelte 29 コード長いのでAPIモックだけ切り出し

Slide 30

Slide 30 text

Svelte、Reactの比較~API処理~ 1. 2. import TodoContainer from "./TodoContainer.svelte" ; 3. 4. 5. 6. 7. 1. import './App.css'; 2. import TodoContainer from './TodoContainer'; 3. 4. function App() { 5. 6. return ( 7. <> 8. 9. > 10. ); 11. } 12. 13. export default App; App.js Todo TodoContainer App.svelte Todo TodoContainer 30

Slide 31

Slide 31 text

Svelte、Reactの比較~API処理~ 1. 2. import Todo from "./Todo.svelte" 3. 4. let todoList = []; 5. let value = ""; 6. 7. // APIモック 8. 9. const addTodo = () => { 10. todoList = [...todoList, value]; 11. value = ""; 12. } 13. 14. const deleteTodo = (index) => { 15. todoList.splice(index, 1); 16. todoList = todoList 17. } 18. 19. 20.
21. {#await fetchTodoList ()} 22.

Loading...

23. {:then _} 24. 25. {/await} 26.
1. import { useEffect , useState } from 'react' ; 2. import Todo from './Todo' ; 3. 4. function TodoContainer () { 5. const [todoList , setTodoList ] = useState ([]); 6. const [val, setVal ] = useState (""); 7. 8. // API モック 9. 10. useEffect (() => { 11. fetchTodoList (); 12. },[]); 13. 14. const addTodo = () => { 15. setTodoList ([... todoList , val]); 16. setVal (""); 17. } 18. 19. const deleteTodo = (index ) => { 20. const newTodoList = [... todoList ]; 21. newTodoList .splice (index , 1); 22. setTodoList (newTodoList ) 23. } 24. 25. if(!todoList .length ) return

loading...

26. 27. return ( 28. <> 29. 30. > 31. ); 32. } 33. 34. export default TodoContainer ; App.js Todo TodoContainer App.svelte Todo TodoContainer 31 PromiseのPending中 useEffectの処理前

Slide 32

Slide 32 text

Svelte、Reactの比較~API処理~ 1. 2. export let todoList ; 3. export let value; 4. export let addTodo; 5. export let deleteTodo ; 6. 7. 8.
9.
10. 11. 追加 12.
13.
14. {#each todoList as todo, i} 15.
{todo}×
16. {/each} 17.
18.
1. function Todo({val, setVal, todoList, addTodo, deleteTodo}) { 2. return ( 3. <> 4.
5. setVal(e.target.value)}/> 6. addTodo()}>追加 7.
8.
9. {todoList.map((todo, i) => ( 10.
{todo} deleteTodo(i)}>×
11. ))} 12.
13. > 14. ); 15. } 16. 17. export default Todo; App.js Todo TodoContainer App.svelte Todo TodoContainer 32

Slide 33

Slide 33 text

Svelte、Vueの比較~APIモック~ 1. // APIモック 2. const fetchTodoList = async() => { 3. const data = await new Promise(resolve => { 4. setTimeout(() => { 5. resolve(["テスト1", "テスト2", "テス ト3"]) 6. }, 2000) 7. }) 8. todoList = [...todoList, ...data]; 9. }; 1. // APIモック 2. const fetchTodoList = async () => { 3. const data = await new Promise((resolve) => { 4. setTimeout(() => { 5. resolve(["テスト1", "テスト2", "テスト 3"]); 6. }, 2000); 7. }); 8. todoList.value.push(...data); 9. }; Vue Svelte 33

Slide 34

Slide 34 text

Svelte、Vueの比較~API処理~ 1. 2. import TodoContainer from "./TodoContainer.svelte" ; 3. 4. 5. 6. 7. 1. 2. 3. 4. 5. 6.

Loading...

7. 8. 9. 10. 11. import TodoContainer from "./components/TodoContainer.vue"; 12. export default { 13. name: "App", 14. components: { 15. TodoContainer, 16. }, 17. }; 18. App.vue Todo TodoContainer App.svelte Todo TodoContainer 34 PromiseのPending中

Slide 35

Slide 35 text

1. 2. import Todo from "./Todo.svelte" 3. 4. let todoList = []; 5. let value = ""; 6. 7. // APIモック 8. 9. const addTodo = () => { 10. todoList = [...todoList, value]; 11. value = ""; 12. } 13. 14. const deleteTodo = (index) => { 15. todoList.splice(index, 1); 16. todoList = todoList 17. } 18. 19. 20.
21. {#await fetchTodoList ()} 22.

Loading...

23. {:then _} 24. 25. {/await} 26.
1. 2. 8. 9. 10. 11. import Todo from "./Todo.vue" ; 12. import { ref } from "vue" ; 13. export default { 14. name: "TodoContainer" , 15. components: { 16. Todo , 17. }, 18. async setup () { 19. const todoList = ref([]); 20. const val = ref(""); 21. 22. // API モック 23. 24. await fetchTodoList (); 25. 26. const addTodo = () => { 27. todoList .value .push (val.value ); 28. val.value = ""; 29. }; 30. const deleteTodo = ( index ) => { 31. todoList .value .splice (index , 1); 32. }; 33. return { 34. todoList , 35. val, 36. addTodo , 37. deleteTodo , 38. }; 39. }, 40. }; 41. App.vue Todo TodoContainer App.svelte Todo TodoContainer 35 Svelte、Vueの比較~API処理~

Slide 36

Slide 36 text

1. 2. export let todoList ; 3. export let value; 4. export let addTodo; 5. export let deleteTodo ; 6. 7. 8.
9.
10. 11. 追加 12.
13.
14. {#each todoList as todo, i} 15.
{todo}×
16. {/each} 17.
18.
1. 2.
3.
4. 5. 追加 6.
7.
8.
9. {{ todo }}× 10.
11.
12.
13. 14. 15. 16. export default { 17. name: "TodoPresentation" , 18. props: { 19. todoList: Array, 20. val: String, 21. addTodo: Function, 22. deleteTodo : Function, 23. }, 24. setup() { 25. return; 26. }, 27. }; 28. App.vue Todo TodoContainer App.svelte Todo TodoContainer 36 Svelte、Vueの比較~API処理~

Slide 37

Slide 37 text

React、Vueとの比較~状態管理~ 37

Slide 38

Slide 38 text

Svelte、Reactの比較~状態管理~ 1. 2. import TodoContainer from "./TodoContainer.svelte" ; 3. 4. 5. 6. 7. 1. import { useState, createContext } from 'react'; 2. import './App.css'; 3. import TodoContainer from './TodoContainer'; 4. 5. export const TodoListContext = createContext(); 6. 7. function App() { 8. const [todoList, setTodoList] = useState(["テスト1", "テスト2", "テスト3"]) 9. 10. return ( 11. <> 12. 13. 14. 15. > 16. ); 17. } 18. 19. export default App; Store App.js Todo TodoContainer App.svelte Todo TodoContainer 38 Contextを使用

Slide 39

Slide 39 text

1. 2. import Todo from "./Todo.svelte" 3. import { todoList } from './store.js' ; 4. 5. let value = ""; 6. 7. const addTodo = () => { 8. $todoList = [...$todoList , value]; 9. value = ""; 10. } 11. 12. const deleteTodo = (index) => { 13. $todoList .splice(index, 1); 14. $todoList = $todoList 15. } 16. 17. 18.
19. 20.
1. import { useState, useContext } from 'react'; 2. import Todo from './Todo'; 3. import {TodoListContext } from './App' 4. 5. function TodoContainer () { 6. const [val, setVal] = useState(""); 7. const {todoList, setTodoList } = useContext (TodoListContext ); 8. 9. const addTodo = () => { 10. setTodoList ([...todoList, val]); 11. setVal(""); 12. } 13. 14. const deleteTodo = (index) => { 15. const newTodoList = [...todoList]; 16. newTodoList .splice(index, 1); 17. setTodoList (newTodoList ) 18. } 19. 20. return ( 21. <> 22. 23. > 24. ); 25. } 26. 27. export default TodoContainer ; Store App.js Todo TodoContainer App.svelte Todo TodoContainer 39 Svelte、Reactの比較~状態管理~ $変数名でStore参照 useContextでContext参照

Slide 40

Slide 40 text

1. 2. import { todoList } from './store.js' ; 3. export let value; 4. export let addTodo; 5. export let deleteTodo ; 6. 7. 8.
9.
10. 11. 追加 12.
13.
14. {#each $todoList as todo, i} 15.
{todo}×
16. {/each} 17.
18.
1. import { useContext } from 'react'; 2. import {TodoListContext} from './App' 3. 4. function Todo({val, setVal, addTodo, deleteTodo}) { 5. const { todoList } = useContext(TodoListContext); 6. return ( 7. <> 8.
9. setVal(e.target.value)}/> 10. addTodo()}>追加 11.
12.
13. {todoList.map((todo, i) => ( 14.
{todo} deleteTodo(i)}>×
15. ))} 16.
17. > 18. ); 19. } 20. 21. export default Todo; Store App.js Todo TodoContainer App.svelte Todo TodoContainer 40 Svelte、Reactの比較~状態管理~

Slide 41

Slide 41 text

1. import { writable } from 'svelte/store' ; 2. 3. export const todoList = writable(["テスト 1", "テスト2", "テスト3"]); Store App.js Todo TodoContainer App.svelte Todo TodoContainer 41 Svelte、Reactの比較~状態管理~ readableやderived等も宣言可能

Slide 42

Slide 42 text

Svelte、Vueの比較~状態管理~ 1. 2. import TodoContainer from "./TodoContainer.svelte" ; 3. 4. 5. 6. 7. 1. 2. 3. 4. 5. 6. import TodoContainer from "./components/TodoContainer.vue"; 7. export default { 8. name: "App", 9. components: { 10. TodoContainer, 11. }, 12. }; 13. Store Store main App.vue Todo TodoContainer App.svelte Todo TodoContainer 42

Slide 43

Slide 43 text

1. 2. import Todo from "./Todo.svelte" 3. import { todoList } from './store.js' ; 4. 5. let value = ""; 6. 7. const addTodo = () => { 8. $todoList = [...$todoList , value]; 9. value = ""; 10. } 11. 12. const deleteTodo = (index) => { 13. $todoList .splice(index, 1); 14. $todoList = $todoList 15. } 16. 17. 18.
19. 20.
1. 2. 3. 4. 5. 6. import Todo from "./Todo.vue" ; 7. import { ref } from "vue"; 8. import store from "../store" ; 9. export default { 10. name: "TodoContainer" , 11. components: { 12. Todo, 13. }, 14. setup() { 15. const val = ref(""); 16. 17. const addTodo = function () { 18. store.commit("addTodo" , val.value); 19. val.value = ""; 20. }; 21. const deleteTodo = function (index) { 22. store.commit("deleteTodo" , index); 23. }; 24. return { 25. val, 26. addTodo , 27. deleteTodo , 28. }; 29. }, 30. }; 31. Store Store main App.vue Todo TodoContainer App.svelte Todo TodoContainer 43 Svelte、Vueの比較~状態管理~ 直接Stateを操作しない

Slide 44

Slide 44 text

1. 2. import { todoList } from './store.js' ; 3. export let value; 4. export let addTodo; 5. export let deleteTodo ; 6. 7. 8.
9.
10. 11. 追加 12.
13.
14. {#each $todoList as todo, i} 15.
{todo}×
16. {/each} 17.
18.
1. 2.
3.
4. 5. 追加 6.
7.
8.
9. {{ todo }}× 10.
11.
12.
13. 14. 15. 16. export default { 17. name: "TodoPresentation" , 18. props: { 19. val: String, 20. addTodo: Function, 21. deleteTodo : Function, 22. }, 23. setup() { 24. return; 25. }, 26. }; 27. Store Store main App.vue Todo TodoContainer App.svelte Todo TodoContainer 44 Svelte、Vueの比較~状態管理~

Slide 45

Slide 45 text

1. import { writable } from 'svelte/store' ; 2. 3. export const todoList = writable(["テスト 1", "テスト2", "テスト3"]); 1. import { createStore } from 'vuex' 2. 3. export default createStore({ 4. state () { 5. return { 6. todoList: ["テスト1", "テスト2", "テスト3"] 7. } 8. }, 9. mutations: { 10. addTodo(state, value){ 11. state.todoList.push(value); 12. }, 13. deleteTodo(state, index){ 14. state.todoList.splice(index, 1); 15. } 16. }, 17. actions: { 18. }, 19. modules: { 20. } 21. }) Store Store main App.vue Todo TodoContainer App.svelte Todo TodoContainer 45 Svelte、Vueの比較~状態管理~ 本当はここからmutations呼び出し

Slide 46

Slide 46 text

1. import { createApp } from 'vue' 2. import App from './App.vue' 3. import store from './store' 4. 5. createApp(App).use(store).mount('#app') Store Store App.vue Todo TodoContainer App.svelte Todo TodoContainer 46 Svelte、Vueの比較~状態管理~ main

Slide 47

Slide 47 text

実際に記述した感想 47

Slide 48

Slide 48 text

実際に記述した感想 + 書きやすい + 公式ページのチュートリアルが充実している + Vue経験者は取っ付きやすそう + データバインディング簡単 + store簡単 48

Slide 49

Slide 49 text

実際に記述した感想 − propsの受け渡しがexportなのに違和感 − テストの設定記述が面倒 − 規模が大きくなった時の管理が大変そう − マテリアルデザインの選択肢が少ない − TypeScriptないと大分厳しい ← Svelte関係ない − 実行コマンド統一してほしい ← package.json書きましょう 49

Slide 50

Slide 50 text

まとめ 50

Slide 51

Slide 51 text

まとめ Svelteは… 課題はたくさんある! けど 記述しやすく、今後伸びそう! なWebフレームワーク(個人的意見) 51

Slide 52

Slide 52 text

扱いたかった題材 ● Next.js、Nuxt.js、Svelte Kitの比較 ● マテリアルデザインの適用 ● ライブラリ関係 ● TypeScriptとの親和性 ● パフォーマンス測定 52

Slide 53

Slide 53 text

参考 ● Svelte https://svelte.jp/ ● Vue.js https://v3.ja.vuejs.org/ ● React https://ja.reactjs.org/ ● Bundlephobia https://bundlephobia.com/ 53