From 0b46891bd9067483165fe5d04546dee868bdb7e5 Mon Sep 17 00:00:00 2001 From: enzoaicardi Date: Sun, 3 Mar 2024 18:46:06 +0100 Subject: [PATCH] [doc] rework i18n, make return statement optionnal, add production advices --- wiki/concepts/production.md | 68 ++++++++++++++++++++++++ wiki/methods/define.md | 4 +- wiki/methods/i18n.md | 102 ++++++++++++++++++++++++++++-------- 3 files changed, 149 insertions(+), 25 deletions(-) create mode 100644 wiki/concepts/production.md diff --git a/wiki/concepts/production.md b/wiki/concepts/production.md new file mode 100644 index 0000000..b1416ca --- /dev/null +++ b/wiki/concepts/production.md @@ -0,0 +1,68 @@ +# Build or not to build ? + +Vif allows you to publish your site or application without any build step, this is very useful during your developments and will also work perfectly in production. + +However, in order to preserve bandwidth and thus help reduce the ecological impact of the web while making your application more accessible and quick to load, we advise you to go through a process of building your application. + +There are many tools to do this like [SWC](https://swc.rs/), [Rollup](https://rollupjs.org/), or [Vite](https://vitejs.dev /). + +The different steps to carry out are as follows: + +- minify your javascript files + - mangle some property names + - minify template literals of type `html...` +- minify your css style sheets +- minify your html documents +- compress your images and svg + +## Rollup config + +If you use Rollup you can use this configuration as a basis: + +```js +// Import rollup plugins +import resolve from "@rollup/plugin-node-resolve"; +import minifyHTML from "rollup-plugin-minify-html-literals"; +import { terser } from "@rollup/plugin-terser"; +import summary from "rollup-plugin-summary"; + +export default { + // Setup input files + input: { + "first-component": "src/first-component.js", + "second-component": "src/second-component.js", + } + // Setup output directory + output: { + dir: "dist", + format: "esm", + }, + plugins: [ + // Resolve bare module specifiers to relative paths + resolve(), + // Minify HTML template literals + minifyHTML(), + // Minify JS and mangle properties starting by "_" + terser({ + mangle: { + properties: { + regex: /^_/, + }, + }, + }), + // Print bundle summary + summary(), + ], + // Mark vif and web-imported modules as external + // so they are not included into the bundle + external: ["vifjs", /^https:/], + preserveEntrySignatures: "strict", +}; +``` + +--- + +# Next + +Done for the moment... +[back to home](../README.md) diff --git a/wiki/methods/define.md b/wiki/methods/define.md index 382b357..fe56b5f 100644 --- a/wiki/methods/define.md +++ b/wiki/methods/define.md @@ -30,7 +30,7 @@ The render function has one argument with three properties: The render function should return one of theses types: - `string` representing an HTML template -- `this || undefined` representing the current component +- `this | undefined` representing the current component (you can simply omit the return statement) - `this.children` reprensenting the current component's children - `this.childNodes` reprensenting the current component's childNodes @@ -64,7 +64,7 @@ function Counter({ props }) { const count = (props.count = useSignal(0)); props.increment = () => count(count.value + 1); - return this; + return this; // optionnal } useDefine("counter", Counter); diff --git a/wiki/methods/i18n.md b/wiki/methods/i18n.md index eea0620..c9a8d98 100644 --- a/wiki/methods/i18n.md +++ b/wiki/methods/i18n.md @@ -21,10 +21,17 @@ export default { In a second time you will need to reference your files with the `useI18n` method, all the files will be lazy load only when necessary. +By doing this, the `useI18n` method will return a [signal](./signal.md) which contains the default `export` of the corresponding translation file. + ```js -const localeImport = (url) => import(url); +const localeImport = (src, locale) => { + // update html lang attribute (optionnal) + document.documentElement.setAttribute("lang", locale); + return import(src); +}; -useI18n({ +// translations can be considered as a global signal +const translations = useI18n({ fr: { FR: () => localeImport("./fr.js"), default: "FR", @@ -37,7 +44,7 @@ useI18n({ }); ``` -The object argument must have 2 levels and the default property is always mandatory, for example this is not allowed : +The object argument (called `definition`) must have two levels and the **default property** is **mandatory**, for example this is not allowed : ```js useI18n({ @@ -46,39 +53,89 @@ useI18n({ }); ``` -## Wait locale before hydrate +## Hydration with locales + +There are two ways to hydrate a component with translations: -Before using translations you must be sure that the file is loaded otherwise you will get errors in your javascript expressions. +- Define the component once the queries to the translations are completed. +- Immediately define component but add `&&` clause in translated directives. -To achieve this use the `onload` method of `useI18n` : +If you immediately define a component and use translations in your directives you may get errors in your javascript expressions, for example this will throw an error : ```js -useI18n.onload(() => { - // all your component definitions should be here - useDefine("app", function ({ props }) { - props.i18n = useI18n; - return this; - }); - // ... -}); +const myTranslationsSignal = useI18n(/* your definition here */); + +// App render function +function App({ props }) { + props.translations = myTranslationsSignal; + return this; +} + +// Component definition +useDefine("app", App); ``` -## Use translations in components +```html + +

...

+
+``` + +At this point, javascript execution will throw an error because `translations()` returns `undefined` and the `.app` property does not exist on type `undefined`. This error occurs because your `App` component is defined before translations are loaded. + +We will see below how to solve this problem. + +### Wait before hydration -To use translations in your components simply use the i18n method without parameters. +The first solution is to use the native `onload` method of each translation signal. This will wait for your translations to be loaded before defining your component. -In that case, i18n refers to the current translation object. +```js +const myTranslationsSignal = useI18n(/* your definition here */); + +// App render function +function App({ props }) { + props.translations = myTranslationsSignal; + return this; +} + +// Component definition using `onload` +myTranslationsSignal.onload(() => useDefine("app", App)); +``` + +Pros : + +- Easy to setup, maintain and debug. +- Apply translated directives just one time. + +Cons : + +- Interactivity of the component is only available once the translations are loaded + +## Use translations in components + +The second solution applies directly to directives and simply consists of performing a `&&` type check on the translation signal. ```html - -

Default SEO title

+

...

``` +Pros : + +- Immediate interactivity of the component. + +Cons : + +- Apply translated directives two times instead on one. + - One time with `undefined` as value. + - One time with the translation as value. +- More verbose than `onload`. +- Harder to setup, maintain and debug. + ## Change the locale manually -Vif set the locale by default from `localStorage.getItem("locale")` or `navigator.language`. After that you can redefine the locale with the `locale` method of `i18n`. +Vif set the locale by default from `localStorage.getItem("locale")` or `navigator.language`. After that you can redefine the locale with the `locale` method of `useI18n`. ```js function LanguageSwitcher({ props, html }) { @@ -86,7 +143,7 @@ function LanguageSwitcher({ props, html }) { return html``; } -useDefine("lang", LanguageSwitcher); +useDefine("language-switcher", LanguageSwitcher); ``` If you want to store the new locale by default for the user, you can simply use `localStorage.setItem("locale", "en-US")`. @@ -95,5 +152,4 @@ If you want to store the new locale by default for the user, you can simply use # Next -Done for the moment... -[back to home](../README.md) +[Make your app ready for production !](../concepts/production.md)