Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Speaker Deck
PRO
Sign in
Sign up for free
Vue.jsで作ったサイトをバニラJSで書き直す悲しいお話
ybrliiu
December 02, 2019
Technology
1
410
Vue.jsで作ったサイトをバニラJSで書き直す悲しいお話
ybrliiu
December 02, 2019
Tweet
Share
More Decks by ybrliiu
See All by ybrliiu
Perlでも関数の型をチェックしたい
ybrliiu
0
800
Perl5.32の新機能
ybrliiu
0
75
Perlにおける動的なモジュールロードのメリットとデメリット
ybrliiu
0
470
黒魔術で独自定義のenum型制約を満たす値のリ ストを取得する話
ybrliiu
0
160
Perlにおけるクラスの実装パターン.pdf
ybrliiu
0
940
Presentation.pdf
ybrliiu
0
210
ぼくがPerlで開発を行う時に工夫していること
ybrliiu
0
300
Other Decks in Technology
See All in Technology
220428event_ogura_part
caddi_eng
0
180
srenext2022-skaru
mixi_engineers
0
350
エンタープライズにおけるSRE立ち上げとNew Relic選定に至った背景とは / SRE Startup and New Relic in the Enterprise
tomoyakitaura
2
130
jaws-ug-asa-datasync-20220510
hiashisan
0
450
次期LTSに備えよ!AOS 6.1 HCI Core 編
smzksts
0
170
暗号資産ウォレット入門(MetaMaskの入門~NFTの購入~詐欺の注意事項など)
kayato
2
150
モダンデータスタックとかの話(データエンジニアのお仕事とは)
foursue
0
150
プロダクトグロースと技術のベースアップを両立させるRettyのアプリ開発スタイル / Achieve Product Growth and Tech Update
imaizume
1
260
Microsoft Power Automate で 始めるRPAと自動化
taikiyoshida
0
1.9k
Power BIのモバイルと都 +1 / Tokyo
ishiayaya
0
130
tfcon-2022-cpp
cpp
5
4.6k
長年運用されてきたモノリシックアプリケーションをコンテナ化しようとするとどんな問題に遭遇するか? / SRE NEXT 2022
nulabinc
PRO
15
7k
Featured
See All Featured
Infographics Made Easy
chrislema
233
17k
Ruby is Unlike a Banana
tanoku
91
9.2k
Why Our Code Smells
bkeepers
PRO
324
54k
Done Done
chrislema
174
14k
The Illustrated Children's Guide to Kubernetes
chrisshort
14
34k
Typedesign – Prime Four
hannesfritz
33
1.3k
The MySQL Ecosystem @ GitHub 2015
samlambert
238
11k
Building Better People: How to give real-time feedback that sticks.
wjessup
343
17k
Code Review Best Practice
trishagee
41
6.7k
A better future with KSS
kneath
225
15k
Unsuck your backbone
ammeep
659
55k
CSS Pre-Processors: Stylus, Less & Sass
bermonpainter
349
27k
Transcript
Vue.jsで作ったサイトをバニラJS で書き直す悲しいお話 1 / 19
自己紹介 名前: liiu (@_ybrliiu) ソシャゲを作っている会社のソフトウェアエンジニアです Perlが好きです 普段はバックエンド書きつつときどきフロントも書きます 最近はVTuberにはまっています 2 /
19
経緯 6月ごろに知人からとあるサイト制作の依頼を受けました 技術的な制約はありますか?と聞いたところないとのことだっ たのでノリノリで TypeScript + Vue.js で作ります 8月ごろ無事完成させた後、「htmlしか触れない人でも触れ るような形にしてくれませんか?」と言われます
見返りは発生するのでVue.jsを抜いてバニラJSに書き換える ように頑張ることに。。 3 / 19
書き換え方針 フロントエンドで使うツールの設定あたりはVue.js前提の構 成なので1から組立て直す ビジネスロジックみたいなところは分離してたのでそのまま使 える コンポーネントはバニラJSに書き直す テンプレート部分はHTML化して切り出し 4 / 19
ESLint この記事を参考にしつつ設定をがんばります https://teppeis.hatenablog.com/entry/2019/02/typescript- eslint もともと extends に plugin:vue/essential, @vue/airbnb, @vue/typescript
を指定していましたが、 全て移行するのは 時間がかかりそうだったので eslint:recommended しか使 っていません 5 / 19
module.exports = { root: true, env: { node: true, dom:
true, }, extends: [ "eslint:recommended", ], rules: { "linebreak-style": [2, "unix"], "semi": [2, "always"], "no-unused-vars": "off", "@typescript-eslint/no-unused-vars": "error", }, plugins: [ "@typescript-eslint" ], parser: "@typescript-eslint/parser", parserOptions: { "sourceType": "module", "project": "./tsconfig.json" }, }; 6 / 19
webpack 試されるWebpack力 https://gist.github.com/ybrliiu/206e4f1286bac4e5590a8e6b7f34 webpack-config-js 7 / 19
module.exports = { mode: 'development', entry: path.join(__dirname, 'src', 'main.ts'), output:
{ path: path.join(__dirname, 'dist'), filename: 'bundle.js', }, module: { rules: [ { test: /\.ts$/, use: "ts-loader" }, { test: /\.css$/, use: [ 'style-loader', { loader: 'css-loader', options: { url: false, modules: true, // defaultで css module とする }, }, ], }, 8 / 19
{ test: /\.(ico|png)$/, use: [ { loader: 'file-loader', options: {
name: '[name].[ext]', // デフォルトだとファイルの outputPath が // dist ディレクトリの中にフラットに展開されてしまうので、 // ディレクトリ構造を維持するために outputPath の関数を変更する outputPath: (filename, absolute, context) => { const splitedRelativePath = path.relative(context, absolute).split(path.sep); splitedRelativePath.shift(); return splitedRelativePath.join(path.sep); }, } } ] } ] }, resolve: { modules: ["node_modules"], extensions: ['.ts', '.js'] }, 9 / 19
plugins: [ // ただファイルを dist にコピーするだけのプラグイン new CopyWebpackPlugin([ { from:
path.join(__dirname, 'public'), to: path.join(__dirname, 'dist'), ignore: ['*.html'], }, ]), // webpackでバンドルしたものを読み込むHTMLファイルを生成するプラグイン new HtmlWebpackPlugin({ filename: 'index.html', template: path.join(__dirname, 'public', 'index.html'), }), // 複数のHTMLファイルを生成することもできる new HtmlWebpackPlugin({ filename: 'privacy-policy.html', template: path.join(__dirname, 'public', 'privacy-policy.html'), }), ], }; 10 / 19
コンポーネント書き換え方針 classスタイルで書いていたコンポーネントを置き換えていく Scoped CSS CSS Module にして外部ファイルとして切り出し、css- loader で読み込んで利用 テンプレート部分
htmlの部分はhtmlファイル(index.html等)に移動 データバインディングなど、DOMを操作する処理も MVVMで言うViewに該当する部分として捉え、処理を切 り出してクラスを作る 11 / 19
コンポーネント書き換え方針 Vueインスタンス Viewで描画するための状態を保持する役割を引き受け るViewModelのクラスとなっているので、Vueを抜いても 雰囲気は大きく変わらない Vue独自の記法を消して、データバインディングはViewク ラスからコールバック関数を仕込んで実現する 12 / 19
CSS Scoped CSS をバニラJSで書いた場合どうやって使うかが調 べてもわかりませんでした, ちゃんと調べればできるかも 代わりに CSS Modules でやりました
CSS と CSS の型定義を書くと型が付いた状態でCSSをロード できるようになります ロードしたスタイルはViewクラスで動的に追加します 13 / 19
dropdown-menu.css .selectbox { height: 310px; overflow: scroll; z-index: 1; }
.item { margin: 0 10px; background-color: rgba(38, 69, 92, .9); } dropdown-menu.css.d.ts export declare type DropDownMenuStyle = { selectbox: string; item: string; }; declare const style: DropDownMenuStyle; export default style; 14 / 19
dropdown-menu.ts import style, { DropDownMenuStyle } from './dropdown-menu.css'; ... initialize():
void { this.selectBoxNode.classList.add(this.style.selectbox); } 15 / 19
ViewとViewModelのデータバインディング View export class ItemNameComponent { private model: ItemName; private
itemNameTextNode: HTMLElement; constructor(private element: HTMLElement) { this.itemNameTextNode = this.element.getElementsByClassName('item-name-text')[0] as HTMLElement // ViewModelにコールバックを仕込む this.model = new ItemName((itemName) => { this.itemNameTextNode.innerHTML = itemName; }); this.model; } } 16 / 19
ViewModel export class ItemName { private itemName: string = '---';
private engineMapModel: EngineMapModel = Store.ENGINE_MAP_MODEL; private onChangeItemName: (itemName: string) => void; constructor(onChangeItemName: (itemName: string) => void) { this.onChangeItemName = onChangeItemName; this .engineMapModel .addGetEngineMapItemNameNotifier((itemName?: string) => { this.itemName = itemName !== undefined ? itemName : '---'; // ViewModelの状態に変更が起きたらViewから仕込んだコールバックを呼び出す this.onChangeItemName(this.itemName); }); } } 17 / 19
カスタムイベント コンポーネントの親子間のやりとりにはイベントを使っています が、Vueの機能を使っている部分があるので書き換える必要があ ります const event = new CustomEvent( 'select-dropdown-menu-item',
// カスタムイベントは渡すオブジェクトのdetailプロパティに // 独自に渡したいパラメータを渡すことができる { detail: selectedItem } ); this.element.dispatchEvent(event); this.element .getElementsByClassName('dropdown-menu')[0] .addEventListener('select-dropdown-menu-item', (event: Event) => { const selectedItemText = (event as CustomEvent).detail as SelectedItemText<Technology>; this.select(selectedItemText); }); 18 / 19
感想 フレームワークのありがたさが身にしみてわかった WebPack力がついた フロントエンドで使われているツールへの理解が少しふかまっ た いろいろ頑張ったけど多分 webpack 捨てたほうが早く作業 終えられた気がする 19
/ 19