⇧ Oh, my gosh...
⇧ 研究が進んだことで「アミロイドβ」の問題が発覚したのかしら?
Vue.jsのリアクティブが動作する基準が分からない
ドキュメントによると、
Vue は、配列における次の変更は検知できません:
- インデックスと一緒にアイテムを直接セットする場合、例えば
vm.items[indexOfItem] = newValue
- 配列の長さを変更する場合、例えば
vm.items.length = newLength
⇧ とあって、
注意事項 1 を克服するに、次のいずれも vm.items[indexOfItem] = newValue
と同様に機能しますが、リアクティビティシステムで状態の更新をトリガします:
// Vue.set
Vue.set(vm.items, indexOfItem, newValue)
// Array.prototype.splice
vm.items.splice(indexOfItem, 1, newValue)
また、vm.$set
インスタンスメソッドを使うこともできます。これはグローバルな Vue.set
のエイリアスです:
vm.$set(vm.items, indexOfItem, newValue)
⇧ と言う対応で、配列をVue.jsでリアクティブに使えると言っているのだけど、
Vue は画面の更新もトリガするために、監視された配列の変更メソッドを抱合 (wrap) します。抱合されたメソッドは次のとおりです:
push()
pop()
shift()
unshift()
splice()
sort()
reverse()
⇧ まさかのリアクティブの説明じゃないところで、リアクティブの対応方法を記載してくれちゃってるというね...
ドキュメントが整理されていない辛さ...
直接代入できるやんけ...
Vue.jsかなりカオスなんですが、
⇧ リアクティブが反映されない問題があるのですが、この問題が起きる時と起きない時の基準がよく分からんのですよ...
自分の環境では、普通に配列の変数に代入は、リアクティブが有効になっちゃったんですよ...
あくまで、配列の要素については、リアクティブを有効にするためのメソッドとかを使う必要があるのだけど、配列の変数に、配列を丸ごとそのまま代入する分には、気にしなくて良いんかね?
というか、mounted()の中だからなのか分からんけど、普通に要素の更新についてもHTML側に反映されてしまうんだが...
ちなみに、試してるソースコードは、
⇧ 前回の記事と同じものです。
■C:\Users\Toshinobu\Desktop\soft_work\vue_work\vue-typescript-admin-template\src\views\login\index.vue
<template> <div class="login-container"> <el-form ref="loginForm" :model="loginForm" :rules="loginRules" class="login-form" autocomplete="on" label-position="left" > <div class="title-container"> <h3 class="title"> {{ $t('login.title') }} | テス | テス </h3> <lang-select class="set-language" /> </div> <div class="reactive"> <ul v-for="(item, index) in dayOfWeekArr" v-bind:key="index"> <li>{{item}}</li> </ul> </div> <el-form-item prop="username"> <span class="svg-container"> <svg-icon name="user" /> </span> <el-input ref="username" v-model="loginForm.username" :placeholder="$t('login.username')" name="username" type="text" tabindex="1" autocomplete="on" /> </el-form-item> <el-tooltip v-model="capsTooltip" content="Caps lock is On" placement="right" manual > <el-form-item prop="password"> <span class="svg-container"> <svg-icon name="password" /> </span> <el-input :key="passwordType" ref="password" v-model="loginForm.password" :type="passwordType" :placeholder="$t('login.password')" name="password" tabindex="2" autocomplete="on" @keyup.native="checkCapslock" @blur="capsTooltip = false" @keyup.enter.native="handleLogin" /> <span class="show-pwd" @click="showPwd" > <svg-icon :name="passwordType === 'password' ? 'eye-off' : 'eye-on'" /> </span> </el-form-item> </el-tooltip> <el-button :loading="loading" type="primary" style="width:100%; margin-bottom:30px;" @click.native.prevent="handleLogin" > {{ $t('login.logIn') }} </el-button> <div style="position:relative"> <div class="tips"> <span>{{ $t('login.username') }} : admin </span> <span>{{ $t('login.password') }} : {{ $t('login.any') }} </span> </div> <div class="tips"> <span>{{ $t('login.username') }} : editor </span> <span>{{ $t('login.password') }} : {{ $t('login.any') }} </span> </div> <el-button class="thirdparty-button" type="primary" @click="showDialog=true" > {{ $t('login.thirdparty') }} </el-button> </div> </el-form> <el-dialog :title="$t('login.thirdparty')" :visible.sync="showDialog" > {{ $t('login.thirdpartyTips') }} <br> <br> <br> <social-sign /> </el-dialog> <LoadingScreen :isLoading="isLoading" /> </div> </template> <script lang="ts"> import { Component, Vue, Watch } from 'vue-property-decorator' import { Route } from 'vue-router' import { Dictionary } from 'vue-router/types/router' import { Form as ElForm, Input } from 'element-ui' import { UserModule } from '@/store/modules/user' import { isValidUsername } from '@/utils/validate' import LangSelect from '@/components/LangSelect/index.vue' import SocialSign from './components/SocialSignin.vue' import LoadingScreen from './components/LoadingScreen.vue' @Component({ name: 'Login', components: { LangSelect, SocialSign, LoadingScreen } }) export default class extends Vue { private validateUsername = (rule: any, value: string, callback: Function) => { if (!isValidUsername(value)) { callback(new Error('Please enter the correct user name')) } else { callback() } } private validatePassword = (rule: any, value: string, callback: Function) => { if (value.length < 6) { callback(new Error('The password can not be less than 6 digits')) } else { callback() } } private loginForm = { username: 'admin', password: '111111' } private loginRules = { username: [{ validator: this.validateUsername, trigger: 'blur' }], password: [{ validator: this.validatePassword, trigger: 'blur' }] } private passwordType = 'password' private loading = false private showDialog = false private capsTooltip = false private redirect?: string private otherQuery: Dictionary<string> = {} private isLoading = true private dayOfWeekArr: Array<string> = [] @Watch('$route', { immediate: true }) private onRouteChange(route: Route) { // TODO: remove the "as Dictionary<string>" hack after v4 release for vue-router // See https://github.com/vuejs/vue-router/pull/2050 for details const query = route.query as Dictionary<string> if (query) { this.redirect = query.redirect this.otherQuery = this.getOtherQuery(query) } } mounted() { this.dayOfWeekArr = ['月', '火', '水', '木', '金'] this.dayOfWeekArr[0] = 'Monday' this.dayOfWeekArr[5] = '土' // this.dayOfWeekArr.length = 0 if (isComplete) { this.isLoading = false } if (this.loginForm.username === '') { (this.$refs.username as Input).focus() } else if (this.loginForm.password === '') { (this.$refs.password as Input).focus() } } private checkCapslock(e: KeyboardEvent) { const { key } = e this.capsTooltip = key !== null && key.length === 1 && (key >= 'A' && key <= 'Z') } private showPwd() { if (this.passwordType === 'password') { this.passwordType = '' } else { this.passwordType = 'password' } this.$nextTick(() => { (this.$refs.password as Input).focus() }) } private handleLogin() { (this.$refs.loginForm as ElForm).validate(async(valid: boolean) => { if (valid) { this.loading = true await UserModule.Login(this.loginForm) this.$router.push({ path: this.redirect || '/', query: this.otherQuery }).catch(err => { console.warn(err) }) // Just to simulate the time of the request setTimeout(() => { this.loading = false }, 0.5 * 1000) } else { return false } }) } private getOtherQuery(query: Dictionary<string>) { return Object.keys(query).reduce((acc, cur) => { if (cur !== 'redirect') { acc[cur] = query[cur] } return acc }, {} as Dictionary<string>) } } let isComplete = false // Service Workerの確認 navigator.serviceWorker.getRegistrations() .then(function(registrations) { console.log(isComplete) console.log('■LoadingScreen') console.dir(registrations) let count = 0 for (const registration of registrations) { console.dir(registration) if (registration.active && registration.active.state === 'activated') { console.log('■Service Workerの読み込み完了■') count++ } } if (count === registrations.length) { console.log('■□■すべてのService Workerの読み込み完了■□■') isComplete = true } }).finally(() => { console.log(isComplete) const element = document.getElementsByClassName('page-container')[0].classList element.add('fadeout') }) </script> <style lang="scss"> @supports (-webkit-mask: none) and (not (cater-color: $loginCursorColor)) { .login-container .el-input { input { color: $loginCursorColor; } input::first-line { color: $lightGray; } } } .login-container { .el-input { display: inline-block; height: 47px; width: 85%; input { height: 47px; background: transparent; border: 0px; border-radius: 0px; padding: 12px 5px 12px 15px; color: $lightGray; caret-color: $loginCursorColor; -webkit-appearance: none; &:-webkit-autofill { box-shadow: 0 0 0px 1000px $loginBg inset !important; -webkit-text-fill-color: #fff !important; } } } .el-form-item { border: 1px solid rgba(255, 255, 255, 0.1); background: rgba(0, 0, 0, 0.1); border-radius: 5px; color: #454545; } } </style> <style lang="scss" scoped> .login-container { height: 100%; width: 100%; overflow: hidden; background-color: $loginBg; .login-form { position: relative; width: 520px; max-width: 100%; padding: 160px 35px 0; margin: 0 auto; overflow: hidden; } .tips { font-size: 14px; color: #fff; margin-bottom: 10px; span { &:first-of-type { margin-right: 16px; } } } .svg-container { padding: 6px 5px 6px 15px; color: $darkGray; vertical-align: middle; width: 30px; display: inline-block; } .title-container { position: relative; .title { font-size: 26px; color: $lightGray; margin: 0px auto 40px auto; text-align: center; font-weight: bold; } .set-language { color: #fff; position: absolute; top: 3px; font-size: 18px; right: 0px; cursor: pointer; } } .show-pwd { position: absolute; right: 10px; top: 7px; font-size: 16px; color: $darkGray; cursor: pointer; user-select: none; } .thirdparty-button { position: absolute; right: 0; bottom: 6px; } @media only screen and (max-width: 470px) { .thirdparty-button { display: none; } } .reactive { color: #fff; } } </style>
⇧ で保存して、npm run serveでVue.jsのアプリケーションを起動してアクセスしてみたところ、
⇧ 普通に配列の要素の更新がHTML側に反映されてしまっているんだが...
今回は、string型の配列を試してるけど、何か、object型の配列とかだと反映されないとかなんかな?
Vue.jsのリアクティブってものがより一層分からなくなったというね...
何て言うかVue.jsカオス過ぎるでしょ...Vue.js何も分からん...
毎度モヤモヤ感が半端ない...
今回はこのへんで。