🦔
Vue コンポーネントの状態と URL クエリパラメータを同期させる
概要
以下の gif のように、 URL のクエリパラメータと、Vue コンポーネントの状態(data) を同期させる mixin
を作ったので備忘録。
前提
- vue 2.6.12
- vue-router 3.0.2
- TypeScript は不使用
コンポーネント側
component.vue
export default {
mixins: [useURLQuery],
data: () => ({
hoge: '',
fuga: 0
}),
urlSyncedData: {
hoge: {
type: Number
validator: (v) => v > 0 && v < 100
},
fuga: {
type: String
}
}
}
使い方
- 後述の
useURLQuery
をミックスインに含めます -
data
で定義しているパラメータのうち、URLクエリパラメータと同期させるパラメータを、urlSyncedData
に定義します-
type
: 期待する型 (Number/String/Boolean のみ) -
validator
URLから受け取ったデータを元にしたバリデータ関数
-
特徴
- URLとコンポーネントで双方向に同期するため、初期読み込み時はURLを元にコンポーネントが初期描画され、UI上で状態が切り替わると、URLも即座に変更されます
- バリデータによって、URLに含まれているデータが不正と判定された場合は、すみやかにコンポーネント側の初期値を利用します
-
useURLQuery
で定義していないパラメータについては、URLに含まれていても干渉しません
mixin 側
useURLQuery.js
export default {
beforeMount() {
const urlParams = this.$route.query
Object.keys(this.urlSyncedData).forEach(key => {
// 同期対象パラメータを監視対象にする
this.$watch(key, _ => this.updateUrlParams())
// URLパラメータの指定がない場合は、コンポーネント側の初期値をそのまま使う
if (urlParams[key] === undefined) return
// コンポーネント側で設定された、同期対象パラメータ設定を取得
const typeConstructor = this.urlSyncedData[key].type
const validator = this.urlSyncedData[key].validator
// 有効なURLパラメータが設定されている場合のみ、コンポーネントの状態を初期化
const parsedValue = this.parseRawValue(urlParams[key], typeConstructor)
if (validator === undefined || validator(parsedValue)) {
this.$set(this, key, parsedValue)
}
})
},
computed: {
urlSyncedData() {
return this.$options.urlSyncedData
}
},
methods: {
/**
* 指定した型コンストラクタに応じて、URLパラメータ内の生データを変換する
* @param {Number|String|Boolean} rawValue
* @param {Function} typeConstructor
* @param {Function=} validator
*/
parseRawValue(rawValue, typeConstructor) {
return typeConstructor === Boolean ? rawValue === 'true' : typeConstructor(rawValue)
},
/**
* 最新のコンポーネントの状態を元に、URLパラメータを更新する
*/
updateUrlParams() {
const newUrlParams = {}
Object.keys(this.urlSyncedData).forEach(key => {
newUrlParams[key] = `${this.$data[key]}`
})
this.$router.replace({
...this.$route,
query: {
...this.$route.query,
...newUrlParams
}
})
}
}
}
解説
- Vue2 の Options API は純粋なオブジェクトなので、
this.urlSyncedData
の形でアクセスできます -
this.$watch
this.$set
によって、動的に Vue コンポーネントを拡張できます -
this.$router.replace
によって、画面遷移をせずに、シームレスに URLクエリパラメータを書き換えることができます
備考
-
Array
やObject
は必要でなかったため、対応していません- 対応する場合は
parseRawValue
関数の拡張が必要そう
- 対応する場合は
- Options API の仕組みに依存しているので、 CompositionAPI や Vue3 での使用は難しそう
- というかよくあるユースケースなので、他にベストプラクティスがありそう
Discussion