メソッドを外部のjsファイルに移動する方法について。
変数を外部ファイル(.js)に切り出してexport defaultでインポートする方法はこちら
今回は応用編として、.vue内のメソッドを外部ファイルに切り出す方法。
##流れと考え方
操作の流れは、vueのデータをjsに渡して、処理後に戻す作業になる。
(1).vue -> (2).js -> (3).vue
**(1) .vueの処理** ・vueのメソッドを実行 ・メソッド内で変数を定義し、jsファイルにプロパティ名でデータを渡す。
(2) .jsの処理
・vueのデータを引数としてプロパティで受け取り処理を実行後、戻り値で返す。
(3) .vueの処理
.jsから戻ってきた値を、vueの値(this.xxx)に代入する。
**▼考え方** Inputする(渡す)データとOutputする(戻す)データの受け渡しを意識することが大切。
methods:{
vueのメソッド名(){
const {受け取るデータ} = jsファイルのコマンド名({
渡すデータ
})
vueのデータ = 受け取るデータ
}
}
const メソッド名 = ({受け取るデータ}) => {
処理
return {
渡すデータ
}
}
export default 渡すメソッド
jsファイルに引数としてデータを渡し、処理後にreturnでvue側の変数名と一致するデータを返す。
最後に、受け取ったデータをvueのデータに入れて受け取り完了となる。
##実例
実例で見るとよりわかりやすい。
//外部ファイル(transformationJs.js)からメソッドをjsMultiとしてインポート
import jsMulti from "./transformationJs"
export default {
data(){
return{
A: 1,
B: 2,
x: 100,
y: 200,
}
},
//ここが重要!jsファイルのメソッドとデータをやりとりする記述
methods:{
btnClicked(){
//処理結果をいれるプロパティを用意
const {A, B} = jsMulti({
//引数内でプロパティ名を指定してデータを渡す
A: this.A,
B: this.B,
x: this.x,
y: this.y
})
//vueのデータにjsファイルの処理結果を代入
this.A = A
this.B = B
}
}
}
読み込んだのはテンプレートではなくメソッドなので、components:{}
は不要
//メソッドを定義
const jsMulti = ({A, B, x, y}) => {
//処理結果を代入する変数を定義
const _A = A * x
const _B = B * y
return { //ここが重要!
//vue側で設定したプロパティにデータを入れる
A: _A,
B: _B
}
}
//外部ファイルに渡すメソッド
export default jsMulti
・.js側で処理済みの値はわかりやすいように_
を付けたが、これは別に何でもいい。(Aのままでもいいし、他の新しい変数名でも可)
・.jsのメソッドは最後にreturnをプロパティとして返す。
・A = _A
はエラーになる。(※プロパティの形になっていない)
・A
のみで値を入れない場合は、空のデータが返る。 (※AはA:Aの省略表記となるため、変数Aを定義していない場合は空のプロパティを返したことになる。)
・A:Aの省略表記Aを[short property](https://qiita.com/yuta
38/items/560f8f80ffa5e3b231c0)と呼ぶ
##処理結果事例とフルコード
上記の実例は、ボタンをクリックする毎に、A × 100、B × 200をする処理となる。
↓ クリック
↓ クリック
###▼フルコード
<template>
<div>
<!-- クリックイベントでbtnClickedを発火 -->
<button
@click="btnClicked"
>【Click Me】</button>
<p>・A: {{A}}</p>
<p>・B: {{B}}</p>
</div>
</template>
<script>
import jsMulti from "./transformationJs"
export default {
data(){
return{
A: 1,
B: 2,
x: 100,
y: 200,
}
},
methods:{
btnClicked(){
const {A, B} = jsMulti({
A: this.A,
B: this.B,
x: this.x,
y: this.y
})
this.A = A
this.B = B
}
}
}
</script>
const jsMulti = ({A, B, x, y}) => {
const _A = A * x
const _B = B * y
return {
A: _A,
B: _B
}
}
export default jsMulti
##配列の中のコマンドを実行する例 v-forで表示した配列として格納されているボタンに対し、外部ファイルの処理を実行する場合の例。(*流れのみ)
(1)ボタンタグをクリックするとメソッドが発火する。
このとき、引数でクリックされたボタンの要素を渡す。
@click="onClickButton(command)
(2)ボタンクリックで発火させる処理
command.onClick
でJSファイル内のプロパティonClickを実行している。
<script>
import cellCommands from "./commands"
export default {
methods:{
onClickButton(command){
const {
//js内で処理後の値を格納する変数を用意(output用)
rows,
selectedCells,
} = command.onClick({ //jsファイルに渡すデータ(input用)。プロパティ名: 値 。returnで返す。
selectedCells: this.selectedCells,
rows: this.innerValue,
maxColNum: this.maxColNum,
thPosition: this.thPosition,
})
//jsの出力をvueに渡す(output)
this.innerValue = rows
this.selectedCells = selectedCells
},
}
}
</script>
各要素のプロパティonClickの中に記述された処理が実行される。
const commands = [
{
cmd: 'clearSelects',
text: "選択解除",
icon: 'mdi-cancel',
isActive: anySelectedCells,
onClick: ({
rows,
}) => {
return {
selectedCells:[],
rows,
}
}
},
{
cmd: 'addColumnBefore',
text:"左に列追加",
icon: 'mdi-table-column-plus-before',
isActive: (params) => notThColandSelectedFirstCol(params) && singleSelectedCell(params),
onClick: ({
selectedCells,
rows,
thPosition
}) => {
if(selectedCells.length != 1) {
return {
selectedCells,
rows,
}
}
//変換後の出力の値は別の変数に代入。一時的として、_変数名とする。
const _rows = rows.map((tr, index) => {
let newCell
if(thPosition == 'row' && index === 0){
newCell = initTh()
}else{
newCell = initTd()
}
tr.article_items = [
...tr.article_items.slice(0, selectedCells[0].cellIndex),
...[newCell],
...tr.article_items.slice(selectedCells[0].cellIndex),
]
return tr
})
const _selectedCells = selectedCells.map((cell)=>{
const {cellIndex: cCellIndex} = cell
if(cCellIndex >= selectedCells[0].cellIndex){
return {
...cell,
cellIndex: cCellIndex + 1
}
} else {
return cell
}
})
//vueに戻すoutput用
return {
selectedCells: _selectedCells,
rows: _rows,
}
}
},
{
cmd: 'addColumnAfter',
text:"右に列追加",
icon: 'mdi-table-column-plus-after',
isActive: singleSelectedCell,
onClick: ({
selectedCells,
rows,
thPosition,
}) => {
if(selectedCells.length != 1) {
return {
selectedCells,
rows,
}
}
const _rows = rows.map((tr, index) => {
let newCell
if (thPosition == 'row' && index === 0) {
newCell = initTh()
} else {
newCell = initTd()
}
return {
...tr,
article_items: [
...tr.article_items.slice(0, selectedCells[0].cellIndex + 1),
...[newCell],
...tr.article_items.slice(selectedCells[0].cellIndex + 1),
]
}
})
const _selectedCells = selectedCells.map((cell)=>{
const {cellIndex: cCellIndex} = cell
if(cCellIndex > selectedCells[0].cellIndex){
return {
...cell,
cellIndex: cCellIndex + 1
}
}else {
return cell
}
})
return {
rows: _rows,
selectedCells: _selectedCells
}
}
},
{
cmd: 'removeColumn',
text:"列削除",
icon: 'mdi-table-column-remove',
color: 'error',
isActive: (params) => anySelectedCells(params) && notAllColumnsSelected(params),
onClick: ({
selectedCells,
rows,
maxColNum,
}) => {
const cellIndicesToRemove = new Set(
selectedCells.map((cell) => cell.cellIndex)
)
if(cellIndicesToRemove.size == maxColNum) {
return
}
const _rows = rows.map((row) => {
return {
...row,
article_items: row.article_items.filter((cells, cellIndex)=>
!cellIndicesToRemove.has(cellIndex)
)
}
})
return {
rows: _rows,
selectedCells: []
}
},
},
{
cmd: 'addRowAfter',
text:"下に行追加",
icon: 'mdi-table-row-plus-after',
isActive: singleSelectedCell,
onClick: ({
selectedCells,
rows,
maxColNum,
thPosition,
}) => {
if(selectedCells.length != 1) {
return {
selectedCells,
rows,
}
}
const _rows = [
...rows.slice(0, selectedCells[0].rowIndex + 1),
...[getTr( {maxColNum, thPosition} )],
...rows.slice(selectedCells[0].rowIndex + 1),
]
const _selectedCells = selectedCells.map((cell) => {
const { rowIndex: cRowIndex } = cell
if (cRowIndex > selectedCells[0].rowIndex){
return {
...cell,
rowIndex: cRowIndex + 1,
}
} else {
return cell
}
})
return {
rows: _rows,
selectedCells: _selectedCells
}
},
},
{
cmd: 'addRowBefore',
text:"上に行追加",
icon: 'mdi-table-row-plus-before',
isActive: (params) => singleSelectedCell(params) && notThColandSelectedFirstRow(params),
onClick: ({
selectedCells,
rows,
maxColNum,
thPosition,
}) => {
if(selectedCells.length != 1) {
return {
selectedCells,
rows,
}
}
const _rows = [
...rows.slice(0, selectedCells[0].rowIndex),
...[getTr( {maxColNum, thPosition} )],
...rows.slice(selectedCells[0].rowIndex),
]
const _selectedCells = selectedCells.map((cell) => {
const { rowIndex: cRowIndex } = cell
if (cRowIndex >= selectedCells[0].rowIndex){
return {
...cell,
rowIndex: cRowIndex + 1,
}
} else {
return cell
}
})
return {
rows: _rows,
selectedCells: _selectedCells
}
}
},
{
cmd: 'removeRow',
text:"行削除",
icon: 'mdi-table-row-remove',
color: 'error',
isActive: (params) => anySelectedCells(params) && notAllRowsSelected(params),
onClick: ({
selectedCells,
rows,
}) => {
const rowIndicesToRemove = new Set(
selectedCells.map((cell)=> cell.rowIndex)
)
if(rowIndicesToRemove.size == rows.length) return
const _rows = rows.filter((row, rowIndex)=>
!rowIndicesToRemove.has(rowIndex)
)
return {
rows: _rows,
selectedCells: []
}
},
}
]
export default commands
commands.jsの中身はかなり長いので、vueファイルから切り出すことでコードを見やすくすることができる。
##参考リンク >[export defaultとimport fromの使い方](#https://qiita.com/yuta-38/items/54df030f02afa5de7347) >[複数の変数やメソッドを渡す方法](#https://qiita.com/yuta-38/items/54df030f02afa5de7347#%E8%A4%87%E6%95%B0%E3%81%AE%E5%A4%89%E6%95%B0%E3%82%92%E5%A4%96%E9%83%A8%E3%81%AB%E6%B8%A1%E3%81%99%E5%A0%B4%E5%90%88) >[MDN公式 export default](#https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Statements/export)