.. _vue-js:
Vue.js
======
`Vue.js `_ is an open-source JavaScript component-based framework that makes it easy to develop reactive applications.
Here you will find some guidelines explaining how you have to proceed to build up you application.
Before beginning
----------------
Vue.js can be used along with some additional libraries like `VueX `_ or `Vue-router `_.
To know when you have to use these libraries, here is a table that will help you to decide:
+-------------------------------------------------+----------------------------+
| Your app | Which lib should I use? |
+=================================================+============================+
| Has a component with few responsibilities | Vue.js |
+-------------------------------------------------+----------------------------+
| Is medium-sized and has complex workflows | Vue.js + Vuex |
+-------------------------------------------------+----------------------------+
| Has several pages to display | Vue.js + Vuex + Vue-router |
+-------------------------------------------------+----------------------------+
.. NOTE:: We strongly suggest you to install the `vue-devtools browser extension `_.
It provides nice features that ease the development of your applications.
Folder structure of a Vue application
-------------------------------------
A Vue app has to be split out in distinct parts.
Here is the folder structure you have to follow:
.. code-block:: bash
my-plugin/
|-- build-manifest.json # Edit it to declare your app for translations
|-- jest.config.js # Unit tests bootstrapping
|-- package.json # Declares the App, its dependencies and its build script.
|-- package-lock.json # Generated by npm. Never edit manually.
|-- tsconfig.json # Typescript configuration
|-- webpack.common.js # Webpack configuration to build the App
|-- webpack.dev.js # Inherits webpack configuration for production
|-- webpack.prod.js # Inherits webpack configuration for development
|-- scripts/
|-- my-vue-app/
|-- po/ # Localization strings
|-- fr_FR.po # Localized strings for French
|-- src/ # The app source-code
|-- api/ # REST API consumers
|-- components/ # Vue components
|-- MyFeature/
|-- OtherFeature/
|-- store/ # Vuex store modules (actions/mutations/getters)
|-- router/ # Vue-router modules
|-- index.ts # App bootstrapping
|-- themes/
|-- _my-vue-app.scss # All SCSS rules should go here
|-- my-vue-app-blue.scss # All the following files provide color themes
|-- my-vue-app-blue-condensed.scss
|-- my-vue-app-green.scss
|-- my-vue-app-green-condensed.scss
|-- my-vue-app-grey.scss
|-- my-vue-app-grey-condensed.scss
|-- my-vue-app-orange.scss
|-- my-vue-app-orange-condensed.scss
|-- my-vue-app-purple.scss
|-- my-vue-app-purple-condensed.scss
|-- my-vue-app-red.scss
|-- my-vue-app-red-condensed.scss
Build your Vue application
--------------------------
The build system will read ``build-manifest.json`` to understand how and where it needs to extract translated strings.
.. code-block:: JavaScript
// tuleap/plugins/my-plugin/build-manifest.json
{
"name": "my-plugin",
"gettext-vue": {
"my-vue-app": {
"src": "scripts/my-vue-app/src",
"po": "scripts/my-vue-app/po"
}
}
}
To build up your application, you will have to update or create a ``webpack.common.js`` file.
This file should be located in ``my-plugin/``.
.. code-block:: JavaScript
// tuleap/plugins/my-plugin/webpack.config.js
const path = require("path");
const webpack_configurator = require("../../tools/utils/scripts/webpack-configurator.js");
const context = __dirname;
const output = webpack_configurator.configureOutput(
path.resolve(__dirname, "../../src/www/assets/my-plugin"),
"/assets/my-plugin/"
);
const entry_points = {
"my-vue-app": "./scripts/my-vue-app/src/index.ts"
};
const colors = ["blue", "green", "grey", "orange", "purple", "red"];
for (const color of colors) {
entry_points[`my-vue-app-${color}`] = `./themes/my-vue-app-${color}.scss`;
entry_points[
`my-vue-app-${color}-condensed`
] = `./themes/my-vue-app-${color}-condensed.scss`;
}
module.exports = [
{
entry: entry_points,
context,
output,
externals: {
tlp: "tlp"
},
module: {
rules: [
...webpack_configurator.configureTypescriptRules(
webpack_configurator.babel_options_ie11
),
webpack_configurator.rule_easygettext_loader,
webpack_configurator.rule_vue_loader
]
},
plugins: [
webpack_configurator.getManifestPlugin(),
webpack_configurator.getVueLoaderPlugin(),
webpack_configurator.getTypescriptCheckerPlugin(true)
],
resolveLoader: {
alias: webpack_configurator.easygettext_loader_alias
}
}
];
.. _npm_scripts:
Once you have a webpack config, you will need a ``package.json`` in ``my-plugin/``.
.. code-block:: JavaScript
// tuleap/plugins/my-plugin/package.json
{
"author": "Enalean Team", // or yourself
"name": "@tuleap/plugin-my-plugin",
"homepage": "https://tuleap.org", // or your plugin's homepage
"license": "GPL-2.0-or-later", // or your license
"private": true,
"dependencies": {
"vue": "^2.6.10",
"vue-gettext": "^2.1.0",
"vuex": "^3.1.1"
},
"devDependencies": {},
"config": {
"bin": "../../node_modules/.bin" // This should point to the node_modules/.bin folder in tuleap/ root folder
},
"scripts": {
"build": "$npm_package_config_bin/webpack --config webpack.prod.js",
"watch": "$npm_package_config_bin/webpack --config webpack.dev.js --watch",
"test": "$npm_package_config_bin/jest"
}
}
.. NOTE:: All the webpack/jest dependencies are available at the tuleap root folder, hence the ``config.bin``.
Use the npm scripts to build up the application or to launch the unit tests.
.. code-block:: bash
npm run build # For a production build, outputs minified code.
npm run watch # Build the app in watch mode.
npm test # Run the :ref:`Jest ` unit tests only once.
Once you have a ``package.json`` file, you will also need a ``tsconfig.json`` file to configure Typescript.
.. code-block:: JavaScript
// tuleap/plugins/my-plugin/tsconfig.json
{
"extends": "../../tsconfig.json",
"compilerOptions": {
"module": "esNext"
},
"include": [
"scripts/my-vue-app/**/*"
]
}
Bootstrap your application
--------------------------
This section will explain you how to properly integrate your application in Tuleap.
Create a mount point
^^^^^^^^^^^^^^^^^^^^
To allow your app to run in Tuleap, you may need to create a mount point in a mustache template.
Your mount point needs to have a unique identifier in order to be easily retrieved from the DOM.
This is also the place where you can pass some data from PHP to JavaScript via ``data-*`` attributes:
.. code-block:: html
Once your mount point is ready, head to your ``index.ts`` file.
.. code-block:: TypeScript
// tuleap/plugins/my-plugin/scripts/my-vue-app/src/index.ts
import Vue from "vue";
import { initVueGettext } from "../../../../src/www/scripts/tuleap/gettext/vue-gettext-init";
import MyVueApp from "./components/MyVueApp.vue";
document.addEventListener("DOMContentLoaded", async () => {
// Retrieve the mount point from the DOM
const vue_mount_point_id = "my-vue-app-mount-point";
const vue_mount_point = document.getElementById(vue_mount_point_id);
if (!vue_mount_point) {
throw new Error(`Could not find Vue mount point ${vue_mount_point_id}`);
}
// Dynamically import the translations relevant to the current user's language.
await initVueGettext(Vue, (locale: string) =>
import(/* webpackChunkName: "my-vue-app-po-" */ `../po/${locale}.po`)
);
// Retrieve the JSON data from the mount point
const user_data = vue_mount_point.dataset.user;
if (!user_data) {
throw new Error("Missing data-user attribute on vue mount point");
}
const MyVueAppComponent = Vue.extend(MyVueApp);
new MyVueAppComponent({ // Create a new component
propsData: {
user: JSON.parse(user_data) // Pass the mount-point data to the vue app
}
}).$mount(vue_mount_point); // Mount the app on the moint point
});
Vue and Typescript
------------------
The reference language to use with Vue.js is now `Typescript `_.
Best-practices for Tuleap
-------------------------
When you submit a patch for review, we may request changes to better match the following best practices. Please try to follow them.
Many rules are already enforced by the pre-commit hook that runs eslint_ with `eslint-plugin-vue`_.
* Please avoid the usage of `vue directives shorthands `_. Shorthands are nice to use but it is not obvious for the others to figure out which directive you are actually using.
* Always use ``PascalCase`` for component names.
* Always use multi-word names for components, for example: "DocumentSearch". In templates, this translates as ````. See `the dedicated Vue Style Guide rule `_.
* Always use ``snake_case`` for computed properties. I know, there are parentheses when we define them, but they really are *properties*, not methods. See :ref:`Tuleap coding standards `.
* Always use ``snake_case`` for props. They follow the same rule as variables.
* Always use ``camelCase`` for methods.
* Always use ``snake_case`` for Vuex State properties and Getters. They are properties too.
* Always use ``camelCase`` for Vuex Mutations and Actions. They are methods.
* Always name files and folders inside ``components/`` with ``PascalCase`` (just like component names).
* Always name javascript files (in all other folders) with ``dash-case``.
* Avoid having too many components that depend on ``this.$route``. Inject what you need via props instead.
* Always use named exports in Vuex Getters, Mutations and Actions. Default export may be used for State definition. Named exports make it easier to import only what we want.
* Always use the inline export syntax ``export function myAction()`` or ``export const myMutation() => {}``. It makes it easy to add "private" (non-exported) functions that will be reused.
* Be carfull with translations, when ``translate`` is used in a ```` extraction won't work, that means you must extract your translations into a dedicated component
Resources
^^^^^^^^^
- Vue.js doc: https://vuejs.org/v2/guide/
- Vuex doc: https://vuex.vuejs.org/
- Vue-router doc: https://router.vuejs.org/
- Vue.js Official Style Guide: https://vuejs.org/v2/style-guide/
- eslint-plugin-vue's rules: https://vuejs.github.io/eslint-plugin-vue/rules/
- TypeScript reference: https://www.typescriptlang.org
- vue-gettext: https://github.com/Polyconseil/vue-gettext
.. _eslint: https://eslint.org/
.. _eslint-plugin-vue: https://github.com/vuejs/eslint-plugin-vue
.. _Vue Style Guide: https://vuejs.org/v2/style-guide/
.. _vue-gettext: https://github.com/Polyconseil/vue-gettext