Adding Vue.js to Sage 9: Dependencies and Approach

I am using this at the moment:

{
    test: /\.css$/,
    exclude: config.paths.assets,
    use: [
      { loader: 'vue-style-loader' },
      { loader: 'css-loader' },
    ],
  },

placed right before sage’s CSS rules. You will need to add vue-style-loader to your list of packages too.

This works fine just running yarn run build but running the watcher I get this error in the console:

Uncaught SyntaxError: Unexpected token *

Which points to this line:

var content = __webpack_require__(/*! !../../../../node_modules/cache-loader/dist/cjs.js!../../../../node_modules/css-loader?{"sourceMap":true}!../../../../node_modules/vue-loader/lib/loaders/stylePostLoader.js!../../../../node_modules/postcss-loader/lib?{"config":{"path":"/.../web/app/themes/.../resources/assets/build","ctx":{"open":false,"copy":"images/**/*","proxyUrl":"https://localhost:3000","cacheBusting":"[name]_[hash:8]","paths":{"root":"/.../web/app/themes/...","assets":"/.../web/app/themes/..../resources/assets","dist":"/.../web/app/themes/.../dist"},"enabled":{"sourceMaps":true,"optimize":false,"cacheBusting":false,"watcher":true},"watch":["app/**/*.php","config/**/*.php","resources/views/**/*.php"],"entry":{"main":["./scripts/main.js","./styles/main.css"],"customizer":["./scripts/customizer.js"]},"publicPath":"/app/themes/.../dist/","devUrl":"https://....test","env":{"production":false,"development":true},"manifest":{}}},"sourceMap":true}!../../../../node_modules/vue-loader/lib?{}!../../../../node_modules/import-glob!../../../../node_modules/import-glob!./ContactForm.vue?vue&type=style&index=0&lang=css */ 35);

So it’s a bit strange!

1 Like

Yeah, I think that’s how far I got too and reverted course. It looks like it’s because of babel’s own loader sequence, mixed in with sage is configured to pull all scss back into style sheets, and because of how vue-style-loader is setup. Some digging to do.

I’ve got around this now by setting output.pathinfo to false in the resources/assets/build/webpack.config.watch.js file.

My knowledge of webpack isn’t great, but it just seems like it adds in those comments for debugging help during development. I haven’t faced any side effects whilst using sage though, yet!

2 Likes

Is Sage not considering at any point adding an option at install for Vue / Angular / React such as it is with style frameworks (bootstrap etc.)? Would that be possible? Or is it not viable at all and why not?

1 Like

Yeah, maybe like an extra question in the sage-installer script, or as an installer script or preset in sage directly. For that we’d need to split out the webpack configs into different presets (default, vue, react, etc).

Either that or maybe there’s a library that exists to upgrade webpack configs post-install to inject the Vue configs in the right spots (reliably?).

Updated instructions for buble instead of babel, with vue-style-loader working

The previous solution above was missing vue-style-loader integration, and this one fixes that problem, permitting <style> sections in .vue single-file components.

Thanks to @kellymears for having published https://github.com/pixelcollective/SageJS, which includes these fixes.

Known issue:

In the final main.css, the styles from .vue components are included before the normal scss styles. Now sure how to fix that yet.

1. Add dependencies

yarn add vue
yarn add --dev vue-loader vue-template-compiler eslint-plugin-vue vue-style-loader

2. Add stuff to webpack.config.js

To your webpack.config.js file (found in resources/assets/build/ ), include vue-loader :

const { VueLoaderPlugin } = require('vue-loader')

Add this to module.rules :

      {
        test: /\.vue$/,
        loader: 'vue-loader',
      },

And also insert this in module.rules, right before the other test: /\.css$/ entry, not replacing it:

      {
        test: /\.css$/,
        exclude: config.paths.assets,
        use: [
          { loader: 'vue-style-loader' },
          { loader: 'css-loader' },
        ],
      },

And to force the runtime to include the full compiler (only needed if you want your root Vue element to be parsed as a Vue template), add this under resolve :

    alias: {
      'vue$': 'vue/dist/vue.esm.js'

Now register VueLoaderPlugin under plugins .

    new VueLoaderPlugin(),

I placed mine after the debug watcher plugin (does it even matter?):

    new webpack.LoaderOptionsPlugin({
      minimize: config.enabled.optimize,
      debug: config.enabled.watcher,
      stats: { colors: true },
    }),
    new VueLoaderPlugin(), //here

3. Modify webpack.config.watch.js

Set pathinfo to false to fix “Unexpected token ‘*’” errors while running yarn start

  output: {
    pathinfo: false, // set to false
    publicPath: config.proxyUrl + config.publicPath,
  },

4. Lint .vue files properly

In .eslintrc.js , replace the extends property with:

  "extends": [
    "eslint:recommended",
    "plugin:vue/essential",
  ],

5. Setup a directory for your .vue components

resources/assets/scripts/vue/ seems like a good spot for those .vue components.

See example reference above in the original post.

6. Add the new directory to your watch list in resources/assets/config.json:

Under watch:, add:

    "resources/assets/scripts/vue/**/*.vue"
10 Likes

@pascallaliberte

Your solution works great except that the linter hangs forever with no indication of where the lint error is.

With yarn build I get:
12% building modules 22/29 modules 7 active ...es.vue?vue&type=template&id=6b81681d&...

With yarn start I get:
Webpack is watching the files…

I’ve followed your instructions very carefully. Any ideas?

Nevermind, I think previous customisations to my webpack config might have broken things.

Seems to be working for now. Thanks again.

This latest iteration is working for me as well. Thanks for the hard work.
For those who haven’t learned yet. Sitepoint’s “Jump Start Vue.js” book is a great place to start. Wasn’t overwhelming.

I am still having occasional trouble compiling .vue files after following these instructions.

For the most part it works but occasionally my code will not compile with either yarn build or yarn start, and there’s no indication of why.

For example, here is some .vue file code that does not compile or produce any error messages for me:

<div v-if="retrieving">
	<p>Retrieving...</p>
</div>

<div v-else>
	<p>Retrieved!.</p>
</div>

The usual error message I would receive from chrome developer tools would be something like:
Component template should contain exactly one root element
which I understand.

However with Sage and the Vue integration method outlined in this post I receive no indication or message explaining the error. The yarn build simply hangs forever.

I’m sure there are other occasions where similar errors are not indicated as I have had similar trouble before.

Any ideas?

Wish I could help Stuart, but I’m just getting started with the Roots stack, myself.

I’m also having a small issue. Until this point, everything has been compiling fine, until trying to get mixins working properly. When creating one and using it all within a single file component – no problem. Works great. But when importing a mixin from an external file, it breaks the component. The external JS is even in the same directory.

So the external file (slideBase.js) looks something like this:

export const slideBase = {
    // mixin here
};

I also tried:

export default {
  // mixin here
}

In the single file component:

import { slideBase } from './slideBase.js'
export default {
  name: 'slide-half-2',
  mixins: [slideBase],
  data() {
    ...

This should be correct, yes?

Anyone else running into mixin problems? Couldn’t find any info remotely related to this. On the discourse or otherwise.

Cheers,

@db12

I haven’t had much experience with vue mixins but is it possible to use require instead of import? Something similar to the way I am defining components below:

components: {
	'test-component-1': require('./TestComponent1.vue').default,
	'test-component-2': require('./TestComponent2.vue'').default,
},

So in your case it would be:

mixins: [require("./slideBase.js").default]

I don’t understand why the ‘.default’ part is needed for my components. Perhaps the webpack script can be updated to avoid this?

Didn’t end up solving the problem. However, I’m getting the same console error that one of my variables is undefined. If my logic is correct, that could mean that it is indeed importing when trying both import and require, and something else is going on. I’m on Vue forums to try to understand the big picture of loading order for a component – since I’m using a vuex store and such. Will bring up more here if I find webpack/vue-loader to be the issue.

@pascallaliberte

A different issue – I’m having trouble getting global sass variables with sass-loader to work properly. I combined the instructions here with your method of adding vue-style-loader for CSS. So the rule is placed above the other test: /\.scss$/ and looks like this:

      {
        test: /\.scss$/,
        exclude: config.paths.assets,
        use: [
          { loader: 'vue-style-loader' },
          { loader: 'css-loader' },
          { loader: 'sass-loader',
            options: {
              data: `@import "scripts/vue/Project/SCSS/test.scss";`
            },
          },
        ],
      },

When I load a variable into test.scss, I’m still getting a build error ‘undefined variable’ when trying to use it in the single file component:

<style lang="scss">
</style>

The test: /\.css$/ rule from the above instructions is still present.

Any ideas?
Thanks.

Yeah, the one problem that I think is remaining from this setup is that the scss extracted from .vue files ends up being parsed before the rest of the site’s scss. Because of that, your global sass variables aren’t available at the time the .vue styles are parsed.

I haven’t looked deeper into why this is happening this way. Maybe someone will fall on the solution.

Am getting an error when i try to do any scss styles in the vue component. If left blank, it works. But if i add for eg body {}, i get a Module build failed: TypeError: Cannot read property 'postcss' of null at Processor.normalize

I wonder if Sage 10’s use of Laravel Mix will make Vue integration a little easier…

These configuration changes work well for the most part but I think they may be causing conflicts and browsersync issues…

2 Likes

I’ve followed all of the instructions here to get Vue setup. I’m using the latest Sage 9 version with Tailwind.

My console produces the following error:

[Vue warn]: You are using the runtime-only build of Vue where the template compiler is not available. Either pre-compile the templates into render functions, or use the compiler-included build.

In place of the component Vue renders:
<!--function (a, b, c, d) { return createElement(vm, a, b, c, d, true); }-->

This guide worked almost perfect for me.

However, I had error Parsing error: Unexpected token .. every second time when I ran yarn build (wierd).
Here is my code which caused the error:

  computed: {
    ...mapGetters({// <-- yarn build breaks here sometimes
      allDogPlaceCoordinates: 'allDogPlaceCoordinates',
    }),
  },

The good news Looks like this solution resolves this build error:
update value 'ecmaVersion': 2017
to value 'ecmaVersion': 2018
site/web/app/themes/theme-name/.eslintrc.js

source:

This was very helpful, I managed to get it working with vuex,
Thank you for posting this. I was wondering if you were using the chrome vue extension with this and if you were is there something I need to enable to use it?