Update 2017-08-10: after post this article on /r/vue, one person commented that we don’t need to pass the context since the function will refer to the context where it is called so now in the end of the article we’ll have an even cleaner way to organize our files!

Before we see an alternative way to organize our vue files/components, I must say although I believe it’s a better approach, in fact, there aren’t right or wrong way, we should follow the way we feel more comfortable to work with.

The following piece of code was extracted from the rss-reader project.

First, we’ll see the original file, and after, the proposed way to organize it.

The mental process

Put all functions in the bottom of the file. When a function needs to access this (the current context), we should pass it as the last parameter to our functions. The ctx parameter you will see is an abbreviation to context .

Reasons to change

Better way to visualize all methods we have in the file. Easier way to extract a function to another file if/when necessary. More “functional” style (!?).

Let’s see and compare the traditional approach with the suggested/alternative one.

Traditional approach

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

113

114

115

116

117

118

119

120

121

122

123

124

125

126

127

128

129

130

131

132

133

134

135

136

137

138

<script>

import Feed from '../../helpers/feeds'

import Favicon from '../../helpers/favicon'

import queue from '../../helpers/queue'

import service from '../../helpers/services'

import { addArticles, addFeed } from '../../vuex/actions'

import _ from 'lodash'

import async from 'async'



export default {

vuex: {

getters: {

offline: state => state.offline,

feeds: state => state.feeds

},

actions: {

addArticles,

addFeed

}

},

computed: {

feedData () {

return this .feeds.map( item => {

if (item.title.length >= 20 ) {

item.origtitle = item.title

}

item.title = _.truncate(item.title, { length : 20 })

return item

})

}

},

data () {

return {

feedurl: '' ,

alertmessage: '' ,

showModal: false ,

processed: false

}

},

methods: {

allArticles () {

return this .$route.router.go({ path : '/' , replace : true })

},

tags () {

return this .$route.router.go({ path : '/tags' , replace : true })

},

favourites () {

return this .$route.router.go({ path : '/article/favourites' })

},

goFeed (title) {

return this .$route.router.go({ path : '/feed/' + title})

},

readArticles () {

return this .$route.router.go({ path : '/article/read' })

},

unreadArticles () {

return this .$route.router.go({ path : '/article/unread' })

},

fetchFeed (callback) {

let feed = new Feed( this .feedurl)

feed.init().then( result => {

if (result === null ) {

let error = 'Sorry. I couldn\'t figure out any RSS feed on this address. Try to find link to RSS feed on that site by yourself and paste it here.'

callback(error)

} else {

callback( null , result)

}

}, err => {

if (err) {}

let error = 'Sorry. Unfortunately this website is not supported.'

callback(error)

})

},

checkFeed (data, callback) {

service.checkFeed(data.meta.title, count => {

if (count === 0 ) {

callback( null , data)

} else {

callback( 'Feed exists' )

}

})

},

fetchIcon (data, callback) {

let favicon = new Favicon(data.meta.link)

favicon.init().then( result => {

let path

if (result !== null ) {

path = queue.queueTask( 'favicon' , result)

} else {

path = null

}

data.meta.favicon = path

data.meta.count = data.articles.length

callback( null , data)

})

},

addFeedItem (data, callback) {

this .addFeed(data.meta, result => {

data.meta = result

callback( null , data)

})

},

addArticleItems (data, callback) {

data.articles.map( item => {

let htmlFilename = queue.queueTask( 'html' , item.link)

item.feed = data.meta.title

item.feed_id = data.meta._id

item.file = htmlFilename

item.favicon = data.meta.favicon

return item

})

this .addArticles(data.articles)

callback( null , 'done' )

},

addFeedData () {

let self = this

this .processed = true

async .waterfall([

this .fetchFeed,

this .checkFeed,

this .fetchIcon,

this .addFeedItem,

this .addArticleItems

], (err, result) => {

if (!err) {

self.processed = false

self.showModal = false

this .feedurl = ''

} else {

self.alert = true

self.alertmessage = err

self.processed = false

}

})

}

}

}

< /script>



Alternative approach

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

113

114

115

116

117

118

119

120

121

122

123

124

125

126

127

128

129

130

131

132

133

134

135

136

137

138

139

140

141

142

143

144

145

146

147

148

149

150

151

152

153

154

155

156

157

158

159

160

161

162

163

164

165

166

167

168

169

170

171

<script>

import Feed from '../../helpers/feeds'

import Favicon from '../../helpers/favicon'

import queue from '../../helpers/queue'

import service from '../../helpers/services'

import { addArticles, addFeed } from '../../vuex/actions'

import _ from 'lodash'

import async from 'async'



export default {

vuex: {

getters: {

offline: state => state.offline,

feeds: state => state.feeds

},



actions: {

addArticles,

addFeed

}

},



computed: {

feedData() { feedData( this ) }

},



data () {

return {

feedurl: '' ,

alertmessage: '' ,

showModal: false ,

processed: false

}

},



methods: {

allArticles() { allArticles( this ) },

tags() { tags( this ) },

favourites() { favourites( this ) },

goFeed() { goFeed(title, this ) },

readArticles() { readArticles( this ) },

unreadArticles() { unreadArticles( this ) },

fetchFeed() { fetchFeed(callback, this ) },

checkFeed() { checkFeed(data, callback) },

fetchIcon() { fetchIcon (data, callback) },

addFeedItem() { addFeedItem(data, callback, this ) },

addArticleItems() { addArticleItems(data, callback, this ) },

addFeedData() { addFeedData( this ) }

}

}





function feedData ( ctx ) {

return ctx.feeds.map( item => {

if (item.title.length >= 20 ) {

item.origtitle = item.title

}

item.title = _.truncate(item.title, { length : 20 })

return item

})

}





function allArticles ( ctx ) {

return ctx.$route.router.go({ path : '/' , replace : true })

}



function tags ( ctx ) {

return ctx.$route.router.go({ path : '/tags' , replace : true })

}



function favourites ( ctx ) {

return ctx.$route.router.go({ path : '/article/favourites' })

}



function goFeed ( title, ctx ) {

return ctx.$route.router.go({ path : '/feed/' + title})

}



function readArticles ( ctx ) {

return ctx.$route.router.go({ path : '/article/read' })

}



function unreadArticles ( ctx ) {

return ctx.$route.router.go({ path : '/article/unread' })

}



function fetchFeed ( callback, ctx ) {

let feed = new Feed(ctx.feedurl)



feed.init().then( result => {

if (result === null ) {

let error = 'Sorry. I couldn\'t figure out any RSS feed on this address. Try to find link to RSS feed on that site by yourself and paste it here.'

callback(error)

} else {

callback( null , result)

}

}, err => {

if (err) {}

let error = 'Sorry. Unfortunately this website is not supported.'

callback(error)

})

}



function checkFeed ( data, callback ) {

service.checkFeed(data.meta.title, count => {

if (count === 0 ) {

callback( null , data)

} else {

callback( 'Feed exists' )

}

})

}



function fetchIcon ( data, callback ) {

let favicon = new Favicon(data.meta.link)

favicon.init().then( result => {

let path

if (result !== null ) {

path = queue.queueTask( 'favicon' , result)

} else {

path = null

}

data.meta.favicon = path

data.meta.count = data.articles.length

callback( null , data)

})

}



function addFeedItem ( data, callback, ctx ) {

ctx.addFeed(data.meta, result => {

data.meta = result

callback( null , data)

})

}



function addArticleItems ( data, callback, ctx ) {

data.articles.map( item => {

let htmlFilename = queue.queueTask( 'html' , item.link)

item.feed = data.meta.title

item.feed_id = data.meta._id

item.file = htmlFilename

item.favicon = data.meta.favicon

return item

})

ctx.addArticles(data.articles)

callback( null , 'done' )

}



function addFeedData ( ctx ) {

let self = ctx

ctx.processed = true

async .waterfall([

ctx.fetchFeed,

ctx.checkFeed,

ctx.fetchIcon,

ctx.addFeedItem,

ctx.addArticleItems

], (err, result) => {

if (!err) {

self.processed = false

self.showModal = false

ctx.feedurl = ''

} else {

self.alert = true

self.alertmessage = err

self.processed = false

}

})

}

< /script>



Better and cleaner approach

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

113

114

115

116

117

118

119

120

121

122

123

124

125

126

127

128

129

130

131

132

133

134

135

136

137

138

139

140

141

142

143

144

145

146

147

148

149

150

151

152

153

154

155

156

157

158

159

160

161

162

163

164

165

166

167

168

169

170

171

<script>

import Feed from '../../helpers/feeds'

import Favicon from '../../helpers/favicon'

import queue from '../../helpers/queue'

import service from '../../helpers/services'

import { addArticles, addFeed } from '../../vuex/actions'

import _ from 'lodash'

import async from 'async'



export default {

vuex: {

getters: {

offline: state => state.offline,

feeds: state => state.feeds

},



actions: {

addArticles,

addFeed

}

},



computed: {

feedData

},



data () {

return {

feedurl: '' ,

alertmessage: '' ,

showModal: false ,

processed: false

}

},



methods: {

allArticles,

tags,

favourites,

goFeed,

readArticles,

unreadArticles,

fetchFeed,

checkFeed,

fetchIcon,

addFeedItem,

addArticleItems,

addFeedData

}

}





function feedData ( ) {

return this .feeds.map( item => {

if (item.title.length >= 20 ) {

item.origtitle = item.title

}

item.title = _.truncate(item.title, { length : 20 })

return item

})

}





function allArticles ( ) {

return this .$route.router.go({ path : '/' , replace : true })

}



function tags ( ) {

return this .$route.router.go({ path : '/tags' , replace : true })

}



function favourites ( ) {

return this .$route.router.go({ path : '/article/favourites' })

}



function goFeed ( title ) {

return this .$route.router.go({ path : '/feed/' + title})

}



function readArticles ( ) {

return this .$route.router.go({ path : '/article/read' })

}



function unreadArticles ( ) {

return this .$route.router.go({ path : '/article/unread' })

}



function fetchFeed ( callback ) {

let feed = new Feed( this .feedurl)



feed.init().then( result => {

if (result === null ) {

let error = 'Sorry. I couldn\'t figure out any RSS feed on this address. Try to find link to RSS feed on that site by yourself and paste it here.'

callback(error)

} else {

callback( null , result)

}

}, err => {

if (err) {}

let error = 'Sorry. Unfortunately this website is not supported.'

callback(error)

})

}



function checkFeed ( data, callback ) {

service.checkFeed(data.meta.title, count => {

if (count === 0 ) {

callback( null , data)

} else {

callback( 'Feed exists' )

}

})

}



function fetchIcon ( data, callback ) {

let favicon = new Favicon(data.meta.link)

favicon.init().then( result => {

let path

if (result !== null ) {

path = queue.queueTask( 'favicon' , result)

} else {

path = null

}

data.meta.favicon = path

data.meta.count = data.articles.length

callback( null , data)

})

}



function addFeedItem ( data, callback ) {

this .addFeed(data.meta, result => {

data.meta = result

callback( null , data)

})

}



function addArticleItems ( data, callback ) {

data.articles.map( item => {

let htmlFilename = queue.queueTask( 'html' , item.link)

item.feed = data.meta.title

item.feed_id = data.meta._id

item.file = htmlFilename

item.favicon = data.meta.favicon

return item

})

this .addArticles(data.articles)

callback( null , 'done' )

}



function addFeedData ( ) {

let self = this

this .processed = true

async .waterfall([

this .fetchFeed,

this .checkFeed,

this .fetchIcon,

this .addFeedItem,

this .addArticleItems

], (err, result) => {

if (!err) {

self.processed = false

self.showModal = false

this .feedurl = ''

} else {

self.alert = true

self.alertmessage = err

self.processed = false

}

})

}

< /script>



Which one do you prefer? Why? Share your thoughts with us :)