カテゴリーアーカイブ JavaScript

asaba 著者:asaba

【webpack】webpack3でUglifyjsWebpackPluginを使う方法

UglifyjsWebpackPluginとは、webpackに自動でjavascriptを圧縮させるプラグインです。

公式によるとbundleでまとめたjavascriptを、更に改行や不要なコード削除を行うことで

読み込み速度を改善させることができるみたいですね。

webpack4ではwebpack.config.jsで以下のようにして使うことが出来ます。

 

c

//webpack.config.js
<pre>const UglifyJsPlugin = require('uglifyjs-webpack-plugin'); 
  module.exports = {
   optimization: { minimizer: [new UglifyJsPlugin()],
  },
}</pre>

 

webpack3の場合はoptimizationで書くことが出来ないので

npmで必要なプラグインをダウンロードした後に以下のように

定義をします。

 


//webpack.config.js

const webpack = require("webpack");

plugins: [new webpack.optimize.UglifyJsPlugin(),],

  • この記事いいね! (0)
asaba 著者:asaba

【react】Loading chunk failedでdynamic importができなくなったときの対処法

コードを分割して初回読み込みをチューニングする手段の中の一つに

dynamic importというものがあります。

こちらは、あらかじめ読み込みたいファイルのみを読み込み、ボタンを押したときや

render時などの特定のタイミングで別のファイルを読み込ませるテクニックの一つです。

先に主要部分のファイルだけ読み込んでおけばユーザーを待たせることなく

、後でボタンをタップなどのアクションに合わせてコンポーネント

を出せばいいだけなのでかなりお手軽だなと思い実装。

ところが関数を作ってビルドをするとLoading chunk 0 failedで読み込みエラー発生。

原因は、app.jsのRouteで作成漏れでした。

Dynamic Importをするときは、必ず

</pre>
import Test from './test';

<HashRouter>
<Switch>

<Route exact path="/test" component={Test} />

</Switch>
</HashRouter>
<pre>

と定義する必要がありました。

 

後は主要ファイルの中で切り離したいコンポーネントを

以下のように組み立ててコンポーネントとして差し込めば

完成です。

 

</pre>
main.js

render() {

const SubConponent = Loadable({
loader: () => import('./subcomponent'),
loading: Loading,
});

return(

<SubConponent/>

);

}
<pre>
  • この記事いいね! (0)
著者:杉浦

【Chrome】開発者ツールNetworkタブInitiator列の紹介

 Chromeで開発を行う際、通信の内容を見るためにNetworkタブをよく見ます。そういった時、Initiator列は全く使いませんが、この列は意外に高性能です。具体的に何ができるかというと、カーソルを合わせた時に通信の起点を元にスタックトレースを出力します。

 これによってどのコードのどの部分で通信、特にaxiosなどの非同期通信が走っているか分かります。これだけでも便利なのですが、強力なのはスタックトレースであるという点です。仮にサーバからブラウザに暗号化したリソースを投げたとしてもスタックトレースをさかのぼり、そこからステップ実行をすると復号処理(ページ上でリソースを使うために復号するものと仮定)の場所、引いては復号した状態を得られます。これと前回のJavaScriptの圧縮を展開する方法によって謎リソースを元にした謎処理でも案外、解き明かせます。昨今のブラウザは強力ですね。

  • この記事いいね! (0)
著者:杉浦

【Chrome】圧縮されたJavaScriptを圧縮されてない様に扱う

 Chromeの開発者ツールのちょっとした機能の紹介になります。
 長い行に圧縮されたJavaScriptを展開する機能です。ちょっとしたLint、prettier的な感じで改行とインデントが入ります。使うには下一枚目の赤丸で囲った{}ボタンを押すだけです。

 特筆すべきはこの状態でブレークポイントを指定できる点です。これのおかげで図の様に圧縮済みのコードでも現在、どこのどの変数に何が入っているのか、ここまでのフローはどうなっているのか、今起きているEventは何なのかと様々な情報を得られます。

  • この記事いいね! (0)
村上 著者:村上

【JavaScript】配列から検索条件に一致する全ての要素のインデックスを取得する方法

重複している要素を含む配列を検索した時、その全ての要素のインデックスを取得する方法です。
あまり綺麗な方法ではないかもしれませんが…とりあえず求めている結果は取得できました。

filter メソッドの使い方について参考にさせていただいたサイトはこちら。

【JavaScript入門】filterで配列のデータを抽出する方法 | 侍エンジニア塾ブログ(Samurai Blog) – プログラミング入門者向けサイト
https://www.sejuku.net/blog/21887

 

サンプルコードはこちら。

let data = [検索したい配列];
var indexs = [];
data.filter(function(value, index, array) {
    if (検索条件) {
        indexs.push(index);
    }
}

indexOf メソッドだと、一番最初に一致した要素のインデックスのみしか取得できないので、filter メソッドを使っています。
そして、検索条件に一致した要素のインデックスは、indexs という配列に追加されるので、後は任意の処理でお使いください。

…が、これ filter メソッドではなく、 map メソッドを使うべきな気が…。
参考サイトによると、「filter メソッドは条件に一致した要素だけを抽出して新しい配列として格納する」とあるので、正しい使い方ではないかも?

ということで、map メソッドを使って書くなら下記の通りです。

let data = [検索したい配列];
var indexs = [];
data.map(function(value, index, array) {
    if (検索条件) {
        indexs.push(index);
    }
}

どちらでも正しい結果は得られるので、後は好みの問題な気もしますね…。
ただ、使い方からすると、正しいのはおそらく map メソッドを使った方法だと思います。

 

以上、配列から検索条件に一致する全ての要素のインデックスを取得する方法でした。
ご参考になれば幸いです。

  • この記事いいね! (0)
村上 著者:村上

【Cordova】「imgcache.js」でキャッシュした画像のパスを取得する方法

以前に紹介した、画像をキャッシュするライブラリ imgcache.js で、キャッシュした画像のURLを取得する方法についてです。
前回の記事を投稿した時点では「WARN: No source given to getCachedFileName INFO: File undefined not in cache」というエラーが発生して画像が取得できなかったのですが、ようやく取得方法がわかりました。

なお、以前の記事はこちら。

【Cordova】アプリで表示する画像を「imgcache.js」を使ってキャッシュする
https://cpoint-lab.co.jp/article/201910/12435/

ライブラリのインストールから、画像をキャッシュに保存するまでの手順を紹介しています。

 

で、方法ですが、.getCachedFileURL() メソッドを使いました。
こちらは、キャッシュした画像の URL を取得できます。
サンプルコードはこちら。

ImgCache.isCached([キャッシュしたい画像のURL], function(path, success) {
  if (success) {
    ImgCache.getCachedFileURL(path, function(originalUrl, cacheUrl) {
      // 任意の処理を実行
      // cacheUrl にキャッシュした画像のURLが代入されている
    }, function(error) {
      console.log(error);
    });
  } else {
    ImgCache.cacheFile(path, function () {
      ImgCache.getCachedFileURL(path, function(originalUrl, cacheUrl) {
        // 任意の処理を実行
        // cacheUrl にキャッシュした画像のURLが代入されている
      }, function(error) {
        console.log(error);
      });
    });
  }
});

まず 1行目で、画像がキャッシュ済みかを調べます。
結果は success に格納され、キャッシュ済みだったら、3行目で画像のパスを取得しています。
コメントにも記載している通り、cacheUrl にキャッシュした画像のパスが保存されているので、そちらを使って任意の処理を実行してください。
なお、pathoriginalUrl には、キャッシュされる前のオリジナルのURLが格納されています。
指定した画像がまだキャッシュされていなかった場合は、10行目で画像をキャッシュし、11行目でキャッシュした画像の URL を取得しています。
処理としては以上です!

私の環境では .useCachedFile() メソッドが動作しなかったのと、キャッシュされた画像の URL を取得できた方が処理的にも便利だったので、 .getCachedFileURL() を使いました。

 

また、前回の記事で「node_modules > imgcache ディレクトリ内にある imgcache.js を任意の場所にコピーし、それをインポートします。」と書きましたが、よくよく確認したところ、node_modules 内の imgcache ライブラリのディレクトリの名前が imgcache.js になっていました。
こちらを imgcache に変更し、import ImgCache from 'imgcache'; と指定したところ、問題なくインポートできましたので、ご参考いただければと思います。
ディレクトリ名に拡張子が入ってしまっていたせいで、正常に読み込めなかったんですね…。

 

以上、imgcache.js を使ってキャッシュした画像の URL を取得する方法でした。
ご参考になれば幸いです。

  • この記事いいね! (0)
asaba 著者:asaba

【cordova】scriptタグを高速で読み込む方法

アプリの高速化を計るため、devToolでどこが遅いか調べていたところ、

ログインページ辺りでjavascriptの読み込みがやや遅くなっていたので

performancesタブで詳細を取ってみました。するとやはり最初にscriptタグを

読んでいた辺りが怪しかったのでindex.htmlの中のscriptタグの

読み込みを少し修正。。。

 

修正箇所は、”text/javascript” src=”cordova.js”async>

の赤文字の部分。

text/javascriptの部分を抜き取ります

 

</pre>
<script src="cordova.js"async></script>
<pre>

 

HTML5では、scriptタグのtypeのデフォルト値が 最初から”text/javascript” に

設定されており、これが付いている状態で読み込むと、同じ属性”text/javascript” を

二回読むことになってしまい読み込み時間にラグが生まれるという訳です。

 

ここは省略して–script src=”cordova.js”async</script>–のようにすることで

速度の改善を見込むことができます。

正しくはこんな感じ↓

 

</pre>
<script src="cordova.js"async></script>
<pre>

scriptタグが多く設定している時ほど威力を体感できるのではないでしょうか。

後は古い機種(android5.0とか)でも感度良さげですね。

  • この記事いいね! (0)
著者:杉浦

【JavaScript】配列関数を使った読みやすいオブジェクト生成の書き方

 次の様な入出力を実現する関数dictMergeをなるべく読みやすく短い関数で実現するのが目標です。dictMergeは未定義や余分な定義を含む複数の辞書から必要なカテゴリーの語のみをまとめるための関数です。たとえなので辞書ですが今回紹介する配列、オブジェクト操作のようなやり方は色々な場面で使えます。

// 入力
const categoryKeys = ['hoge', 'fuga', 'hogefuga'];
const dictA = {
	hoge : ['hoge1', 'hoge3', 'hoge5'],
	fuga : ['fuga3', 'fuga7', 'fuga13'],
}
const dictB = {
	hoge : ['hoge7', 'hoge9', 'hoge11'],
	bar : ['bar2', 'bar4', 'bar6'],
}
const dictC = {
	hoge : [],
	fuga : ['fuga19', 'fuga29', 'fuga37'],
	hogefuga : ['hogefuga'],
}

// 出力
const mergedDict = dictMerge(categoryKeys, dictA, dictB, dictC);
console.log(mergedDict);
/*
{
	hoge : ['hoge1', 'hoge3', 'hoge5', 'hoge7', 'hoge9', 'hoge11'],
	fuga : ['fuga3', 'fuga7', 'fuga13', 'fuga19', 'fuga29', 'fuga37']
	hogefuga : ['hogefuga']
}
*/

 実装したdictMerge()が次になります。コードの細部はコメントの通りです。

/**
 * @param {Array} categoryKeys
 * @param {Array} dicts
 * @return {Object}
 */
function dictMerge(categoryKeys, ...dicts) { // 第二引数以下を変数dictに配列として格納する. 今回ならdicts = [dictA, dictB, dictC]
    return categoryKeys.reduce((mergedDict, category) => { // reduce関数で配列を一つのオブジェクトに変換する。詳しくは下記
        return {
            ...mergedDict, // スプレッド構文でreduceの前のループ結果を引き継がせる
            [category]: dicts.map(dict => dict[category]) // [category]で変数をキー名にする。map関数で扱う配列を[dictA[category], dictB[category], dictC[category]]にする
                .flat() // 配列の中身を一段flatにする。例えば[Array(3),Array(3),Array(0)]の二次元配列がArray(6)の一次元配列になる
                .filter(v => v !== undefined)// 未定義でない値のみを配列の中身に残す
        }
    }, {});
}

 上記の様にJavascriptに用意されている関数をチェーンするだけで少々複雑な問題も簡易な記述で解けます。この様なやり方を用いる際に注意する必要があるのはreduce関数です。reduce関数はJavaScriptの配列操作においてとても汎用的であり、書こうとさえ思えばほぼ全ての配列操作関数をreduce関数で表現できます。これを使うと次のように黒魔術的もかくやというコードを書けます。

// dictMergeと等価
function dictMergeByReduce(categoryKeys, ...dicts) {
    return categoryKeys.reduce((mergedDict, category) => {
        return {
            ...mergedDict,
            [category]: dicts.reduce((acc, value, index) => {
                acc[index] = value[category];
                return acc;
            }, [])
                .reduce((acc, value) => acc.concat(value), [])
                .reduce((acc, value) => {
                    if (value !== undefined) {
                        acc.push(value);
                    }
                    return acc;
                }, [])
        }
    }, {});
}

 なるべくreduceを使わず特化的な名前の少ない引数の配列関数を使うとわかりやすいコードになります。最初の例も正直reduceを使うまでもありません。reduce((o, k)=>{return {…o, [k]:kに関する何か}},object型引数)という構文は便利なのですが慣れない人に渡すとけっこう混乱します。注意するべきでしょう。

// 多くの人がわかりやすく読みやすいであろう版
function dictMergeSimple(categoryKeys, ...dicts) {
    const mergedDict = {};
    categoryKeys.forEach(category => {
        mergedDict[category] = dicts.map(dict => dict[category])
                .flat()
                .filter(v => v !== undefined)   
    });
    
    return mergedDict;
}
  • この記事いいね! (1)
takahashi 著者:takahashi

jQuery MobileのAjaxLoadを完全に無効化する方法

スマホ向けデザインフレームワークのjQueryMobileには、すべてのページ遷移の操作をオーバーライドして、実際にページ遷移を行うことなくコンテンツの中身を遷移先のものに入れ替える”AjaxLoad”がデフォルトで有効になっています。

うまくやればページ遷移のエフェクトを含めたリッチなサイトも構築できるのですが、全ページで使用するアセットをランディングページ(サイトで一番最初に表示されるページ)になりうるすべてのページに読み込んでおかないといけないなどのデメリットがあり、こういったAjaxLoadの仕様に設計上対応できない(したくない)場合などに無効化したい場合があります。

ところが、jQueryMobileのバージョンで指定方法が異なったりなど、 確実に無効化する方法がわかりづらかったりするのが難点です。

今回はAjaxLoadを確実に無効化する方法をご紹介します。

結論から言ってしまうと、HTMLのheadタグ内に、下記のコードを埋め込むだけで無効化できました。

<script type="text/javascript">
    $(document).bind("mobileinit", function(){
        $.mobile.ajaxEnabled = false; 
        $.mobile.ajaxLinksEnabled = false; 
        $.mobile.ajaxFormsEnabled = false;
        $.mobile.loadingMessage = false;
        $.mobile.hashListeningEnabled = false;
    });
</script>

なお、

$.mobile.ajaxEnabled = false; 

は新バージョンの指定で、

$.mobile.ajaxLinksEnabled = false; 
$.mobile.ajaxFormsEnabled = false;

は旧バージョンの指定となっています。

両方入れておくことで、どのjQueryMobileバージョンであっても対応できそうです。

これらの指定を入れておくことで、この指定が入っているページについてはAjaxLoadが働かず、通常通りのページ遷移をさせることが可能です。

お困りの方は試してみてください。

  • この記事いいね! (0)
著者:杉浦

【Vue.js】コンポーネント外で定義した値をテンプレート内で使う

 Vue.jsの単一ファイルコンポーネントはコンポーネントを構成するHTMLテンプレート、JavaScript、CSSを一ファイルにまとめる仕組みです。コンポーネントの構成要素全体を把握しやすい点で優秀なのですが、テンプレート部で扱うことのできる変数が若干不透明で時として直感的でない振る舞いをします。
 次の例ではコンポーネント内全体がスコープっぽいところで変数hogeStrを宣言してテンプレート内で使用しようとしますが、これはhogeStrが未定義扱いされます。

<template>
  <div>{{ hogeStr }}</div>
</template>

<script lang="ts">
const hogeStr = 'hoge';

import Vue from 'vue';
export default {};
</script>

 実際に扱うときにはVueインスタンスの内部に含まれなければいけません。変化のない定数でも必須です。そのため実際は次のようにします。

<template>
  <div>{{ hogeStr }}</div>
</template>

<script lang="ts">
const hogeStr = 'hoge';
import fuga from 'fuga';

import Vue from 'vue';
export default {
  data() {
    return {
      hogeStr // 変化する値はdata
    };
  },
  computed: {
    nearMsgEnum, // 変化せず、getterのみでいいならcomputed
  },
};
</script>

 単一ファイルコンポーネントの作りではテンプレート、JavaScript、CSSがタグごとに分かれますが、結局のところtemplateタグもVueインスタンスの一部でありVueインスタンス外部を見るスコープにないようです。
 余談ですがdata, computedで同じ名前の値を読み込んでもVue.jsは動き続けます。外部から値を読み込むように作り替えた時に想定外の挙動が起きたならば、これが起きているのではないかと疑えます。

  • この記事いいね! (1)