nativescript-vue / nativescript-vue-loader

Webpack loader for Vue.js components

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Next Planning

rigor789 opened this issue · comments

We would like to pre compile templates to improve nativescript app performance, and eliminate the need to parse and compile templates on the device.

The best case scenario for this loader is to be able to chain it with the official vue-loader, so we don't have to keep updating it from the official repo.

nativescript-vue-loader -> vue-loader -> other loaders -> build

This loader has to support (all optional) multiple templates (web/native/android/ios), multiple scripts (script - shared, script native, script web, script android, script ios) and multiple styles.

Based on the target platform we have to parse a .vue file (using nativescript-vue-template-compiler), and then build out another valid .vue file that we pass on to vue-loader to handle.

Case 1: only targetting nativescript

The .vue file is as follows:

<template></template> 
<template android/ios></template> 

<script></script>
<script android></script>
<script ios></script>

<style></style>
<style android></style>
<style ios></style>

First step (for all targets) is to parse the .vue file into block descriptors.

build target: android
First, we pick the template

  • if <template android /> exists we use it
  • else we use <template> (if exists)

Second we pick the <script>,

Third, if <script android> exists, we create a custom block <mixin>

Fourth, we pick the <style> block, and if <style android> exists we merge it's contents into the <style> block (taking care of cases where only <style android> exists too!)

Finally we assemble a new .vue file (in memory) as follows:

<template /> <!-- either from template:android or regular template -->
<script />
<mixin /> <!-- if script:android existed -->
<style /> <!-- contains base style + android only style -->

Once the assemble phase is done, we are done with the .vue loader.

build target: ios
Same as android.

We also need to create a loader for the mixin block as a runtime block.

This is documented here: https://vue-loader.vuejs.org/en/configurations/custom-blocks.html#runtime-available-docs

pseudo code example:

module.exports = function (source, map) {
  this.callback(null, `

var mixin = require( webpack string that runs the source throu babel etc. )

module.exports = function(Component) {
  Component.options.mixins.push(mixin)
}
`, map)
}

Things to solve

We have to compile the templates using the nativescript-template-compiler, which I'm not sure we can do easily, at least not using the <template> block. A custom block (like <nativescript-vue-template/>) could take care of it, in a similar way as the <mixin> block above.

@yyx990803 posted a gist of an upcoming vue-loader version that simplifies how loaders are resolved, which inspired this write up. We can most likely use the features provided by the next version.
https://gist.github.com/yyx990803/e0f4f1275841f4ce756b8c1ac1db76e9

Cool!

This is pretty cool. IIRC, it was not possible in the previous vue-loader. Btw, vue-loader changed a LOT, which makes the current ns-vue-loader even more outdated.

<template>, <scripts> and <style> targets

This will get more complicated when we add code-sharing. I know you are focusing on NS only, but let me add our past ideas about .vue files in a code-sharing project. The .vue file would be as follows:

<template></template>
<template web/native></template> 
<template android/ios></template> 

<script></script>
<script web></script>
<script native></script>
<script android></script>
<script ios></script>

<style></style>
<style web></style>
<style native></style>
<style android></style>
<style ios></style>

This would add an extra step in merging/selecting the blocks.

Can we get away without any of this?

In my recent experiments with a code-sharing project I am trying to figure out if what we can get away with by just using different files with different extensions.

For example, a component could have the files:

  • my-component.vue: The shared file with shared script. Could also be a .js file.
  • my-component.web.vue: Web template/style. Script is mixed in with the shared code.
    -my-component.native.vue: native template/style. Script is mixed in with the shared code. Applies to both android and iOS builds.
  • my-component.android.vue: If present, is used instead of the the .native.vue file in android builds.
  • my-component.ios.vue: If present, is used instead of the the .native.vue file in ios builds.

This is basically what @yyx990803 suggested previously. Although the multi-build .vue file looks nice, it does add a little extra cognitive overhead, because the user will have to keep in mind how NS-Vue deals with all the blocks. Show me the code™ is the best approach in these cases. Lets try all alternatives, write "real" code and see how it feels.

Is this still in the works? Could be achieved with vue-loader's custom blocks so vue-loader itself could still be used as-is and this library wouldn't have to reimplement the functionality?

<!-- possible by using resourceQuery: /blockType=style-ios/ -->
<style-ios lang="sass"></style-ios>
<!-- or if resourceQuery can match on the query string (untested): -->
<style lang="sass?ios></style>

The only problem that I foresee with this approach is that a native block should override a block that doesn't specify a platform. The loader should somehow keep track of what platform-specific blocks exist and omit any non-specific blocks they correspond to.

Another option to allow for code sharing is to break out of the .vue file by using src imports.

<!-- shared code (same file) -->
<template-ios src="./template.html"></template-ios>
<template-android src="./template.html"></template-android>

<!-- platform-specific code -->
<style-ios src="./style.ios.css"></style-ios>
<style-android src="./style.android.css"></style-android>

However this is implemented if custom blocks are to be used, this library could expose an array of loader rules that could be merged into the consumer's webpack config.