Closed32

vuex-router-syncをvuex4.x,vue-router4.xに対応させる作業

vuex4.0 docsはこれ
vue-router4.0 docsはこれ.migration guide

まあ、両方とも3.0のちゃんとした書き方を理解してないので、それを見る必要もあるかもしれない。

vuex-router-syncは一つのファイルだけ

https://github.com/vuejs/vuex-router-sync/blob/master/src/index.ts

初期化時に、vuexに対してregistermoduleで登録してるんだねー
clonRouteはいい感じにstateに保持すべき値を計算してくれる感じ。

  store.registerModule(moduleName, {
    namespaced: true,
    state: cloneRoute(router.currentRoute),
    mutations: {
      ROUTE_CHANGED(_state: State, transition: Transition): void {
        store.state[moduleName] = cloneRoute(transition.to, transition.from)
      }
    }
  })

https://vuex.vuejs.org/ja/api/#registermodule

で、breaking changeなさそうに見える

https://next.vuex.vuejs.org/guide/modules.html#dynamic-module-registration

vuexのstoreにwatchなんてあるのか

https://vuex.vuejs.org/ja/api/#watch

watch(fn: Function, callback: Function, options?: Object): Function

fnが返す値をリアクティブに監視し、値が変わった時にコールバックを呼びます。fnは最初の引数としてストアのステートを、2番目の引数としてゲッターを受け取ります。 Vue のvm.$watchメソッドと同じオプションをオプションのオブジェクトとして受け付けます。

これもbreaking changeなさそうだけど新しい方のドキュメントは見つからないなあ

そして、routerのafterEachでcommitをhookしている

  // sync store on router navigation
  const afterEachUnHook = router.afterEach((to, from) => {
    if (isTimeTraveling) {
      isTimeTraveling = false
      return
    }
    currentPath = to.fullPath
    store.commit(moduleName + '/ROUTE_CHANGED', { to, from })
  })

これも特にbreaking changeなさそうだ

https://next.router.vuejs.org/api/#addroute-2

あんまりbreaking change関わるところなさそうなので、一旦バージョン上げて見て様子見する

型変えたらビルド通っちゃった

ああ、先にテストから変えとくのが良かったかな。
テストの方は結構変更が必要そう。
vuexもvue-routerも初期化が変わってるから

いやぁ、テストがむずい。
テストのためのmountとかいるのかな

わからないところ

  • testのためにcreatAppしたものをmountする必要があるのか(なさそう?)
  • どこで出ているかわからないpromise error
  • expect(app.$el.textContent).toBe('/a/b a b')とかいてあるようなtextContentのvue3でのアクセス方法
  • 既存で存在する_hoge参照しているstoreのテストの意味。また、4.0ではそれらのpropsが生えてないので違うテストコードを書く必要があるのか。

app.mountはこれでいけた

  const rootEl = document.createElement('div')
  document.body.appendChild(rootEl)
  app.mount(rootEl)

jsのdocument objectについてあまり理解できてない。
ブラウザにいないときでもつかえるのか。

これなんですよ

Unexpected error when starting the router: TypeError: Cannot read property '_history' of null
        at Window.get history

windowオブジェクトはブラウザじゃないとないはずだから、pupeteerいるのかなって

vue routerについては、
はじめのpush直後に await router.isReady()
それ以降の遷移は await router.push()
と、適切にawaitすることで解決できた。

次にvue appのtextContentを参照したい。
これについてはvue-test-utilsを使うか、そのwrapperの実装を見れば解決しそうである。

vtuの参照はこんな感じ

  html() {
    // cover cases like <Suspense>, multiple root nodes.
    if (this.parentElement['__vue_app__']) {
      return this.parentElement.innerHTML
    }

    return this.element.outerHTML
  }

  text() {
    return this.element.textContent?.trim()
  }
  const rootEl = document.createElement('div')
  document.body.appendChild(rootEl)

  const app = createApp({
    render: () => h('router-view')
  })
  app.use(store)
  app.use(router)
  app.mount(rootEl)
  console.log(rootEl.innerHTML)

は現状だと<router-view></router-view>になっちゃいますわ。

render: () => h('router-view')
これが原因。
RouterViewコンポーネントをちゃんとimportして使おう。
import { RouterView } from 'vue-router'
render: () => h(RouterView)

  const app = createApp({
    render: () => h(RouterView)
  })
  app.use(store)
  app.use(router)
  app.mount(rootEl)
  console.log(rootEl.innerHTML)
  await router.push('/')
  console.log(rootEl.innerHTML)

で、'/a/b'のほうがうまく行かないので、登録したcomponentの記述が間違っている。

  const Home = defineComponent({
    computed: mapState(moduleName, {
      path: (state: any) => state.fullPath,
      foo: (state: any) => state.params.foo,
      bar: (state: any) => state.params.bar
    }),
    render() {
      h('div', [this.path, ' ', this.foo, ' ', this.bar])
    }
  })

これが間違ってる。
まあ調べずに適当に書いたからな・・・。
defineComponentの正しい書き方調べるか、他の定義方法かどちらかだろう。
(VueのSFC以外のcomponent定義についてまとめるのおもしろそう。何種類あるのだろう。)

good

  const Home = defineComponent({
    setup() {
      const moduleState: any = useStore().state[moduleName]
      const path = moduleState.fullPath
      const foo = moduleState.params.foo
      const bar = moduleState.params.bar
      return () => h('div', [path, ' ', foo, ' ', bar])
    }
  })

not goodだった。
正しくは

  const Home = defineComponent({
    setup() {
      const store = useStore()
      const path = computed(() => store.state[moduleName].fullPath)
      const foo = computed(() => store.state[moduleName].params.foo)
      const bar = computed(() => store.state[moduleName].params.bar)
      return () => h('div', [path.value, ' ', foo.value, ' ', bar.value])
    }
  })

unsyncがうまく動いてないからそこを直したら終わり。
古いやつだと
store.watch
router.afterEach
これらの返り値としてunsubscribeするための関数が返ってきてたのだけど、
新しい奴らはvoid.
違う関数を使うか、unsubscribeの方法を探して適切にunsubscribeする必要がある。

うそです。勘違いでした。

vue-router

Add a navigation hook that is executed after every navigation. Returns a function that removes the registered hook.

vuexはdocs見つからない

unsyncは正しくできてるみたいだけど、
unsyncできたかどうかを確認するためのテストが今までの書き方だとうまく行かないようだ。
参照していた配列とかは使用が変わっている。
subscribeしたときに追加される場所を突き止めなければいけない。

vue-router-next

afterEachはafterGuards.addを参照していて、
addするとafterGuardsのhandlersに追加されている。
router.tsの中自体ではafterGuards.list()で参照ができるけど、
そいつは外にexportされてないので外から見ることができない。
だからafterEachに何が登録されているのかを見る手段は現状なさそう。

このスクラップは2020/12/06にクローズされました
ログインするとコメントできます