.. _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 the build system and for translations |-- scripts/ |-- my-vue-app/ |-- po/ # Localization strings |-- 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 |-- 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.config.js # Webpack configuration to build the App Build your Vue application -------------------------- The build system will read ``build-manifest.json`` to understand where it needs to run ``npm install`` and ``npm run build``. .. code-block:: JavaScript // tuleap/plugins/my-plugin/build-manifest.json { "name": "my-plugin", "components": [ "scripts/" ], "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.config.js`` file. This file should be located in ``my-plugin/scripts/``. .. code-block:: JavaScript // tuleap/plugins/my-plugin/scripts/webpack.config.js const path = require("path"); const webpack_configurator = require("../../../tools/utils/scripts/webpack-configurator.js"); const assets_dir_path = path.resolve(__dirname, "./assets"); const webpack_config_for_my_awesome_vue_app = { entry: { "my-vue-app": "./my-vue-app/src/index.ts" }, context: path.resolve(__dirname), output: webpack_configurator.configureOutput(assets_dir_path), 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()], resolveLoader: { alias: webpack_configurator.easygettext_loader_alias } }; module.exports = webpack_config_for_my_awesome_vue_app; .. _npm_scripts: Once you have a webpack config, you will need a ``package.json`` in ``my-plugin/scripts/``. .. code-block:: JavaScript // tuleap/plugins/my-plugin/scripts/package.json { "author": "you", "name": "my-vue-app", "version": "0.0.1", "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": "NODE_ENV=production $npm_package_config_bin/webpack --mode=production", "watch": "NODE_ENV=watch $npm_package_config_bin/concurrently --raw --kill-others '$npm_package_config_bin/webpack --watch --mode=development' 'CI=true npm test -- --watch'", "test": "$npm_package_config_bin/jest" } } .. NOTE:: All the development dependencies are available at the tuleap root, 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 # Run the :ref:`Jest ` unit tests and build the app in watch mode at the same time. npm run 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/scripts/tsconfig.json { "extends": "../../../tsconfig.json", "compilerOptions": { "module": "es2015" //redefine otherwise vue-jest crashes: https://github.com/vuejs/vue-jest/issues/144 }, "include": [ "my-vue-app/**/*" ], "exclude": [ "node_modules", "my-vue-app/**/*.test.ts" ] } 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//src/index.ts import Vue from 'vue'; import MyVueApp from './components/MyVueApp.vue'; document.addEventListener('DOMContentLoaded', () => { // 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}`); } const MyVueAppComponent = Vue.extend(MyVueApp); new MyVueAppComponent({ // Create a new component propsData: { user: JSON.parse(vue_mount_point.dataset.user) // Pass the data bound to the mount point to the 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. 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