1 | <a href="#">
|
2 | <img src="https://cdn-images-1.medium.com/max/1000/1*Cmy6UutD5-ogL8dr1DySMQ.png" alt="Marko logo" width="100%" />
|
3 | </a><br />
|
4 |
|
5 | You can find the original ["10 Awesome Marko Features" article here](https://medium.com/@austinkelleher/10-awesome-marko-features-afba9d094d42)!
|
6 |
|
7 | # 10 Awesome Marko Features
|
8 |
|
9 | [Marko](http://markojs.com/) is a friendly and super fast UI library that makes
|
10 | building web apps<br> fun! In celebration of rapidly approaching [5,000 stars on
|
11 | GitHub](https://github.com/marko-js/marko) (the ultimate open source vanity
|
12 | metric), here are 10 features that will make you more productive in no
|
13 | particular order...
|
14 |
|
15 | #### 1. Shorthand Attributes
|
16 |
|
17 | Tired of constantly typing out `class` and `id` attributes? No need with Marko.
|
18 | Simply utilize the shorthand based on CSS selectors:
|
19 |
|
20 | ```marko
|
21 | style {
|
22 | .count {
|
23 | color:#09c;
|
24 | }
|
25 | }
|
26 |
|
27 | // Equivalent to <div class="count"/>
|
28 | <div.count/>
|
29 |
|
30 | // Equivalent to <span id="my-id"/>
|
31 | <span#my-id/>
|
32 |
|
33 | // Combined
|
34 | <button#submit.primary/>
|
35 | ```
|
36 |
|
37 | #### 2. All attribute values are Just JavaScript™
|
38 |
|
39 | Unlike with HTML, you are not limited to string attribute values when using
|
40 | Marko. Attributes can have types, which makes it really easy to pass data to
|
41 | custom tags and it works for standard HTML tags too:
|
42 |
|
43 | ```marko
|
44 | <div class=input.myClassName/>
|
45 | <input type="checkbox" checked=input.isChecked/>
|
46 | <awesome-component myString="Hello"/>
|
47 | <awesome-component myNumber=1/>
|
48 | <awesome-component myTemplateString=`Hello ${name}`/>
|
49 | <awesome-component myBoolean=true/>
|
50 | <awesome-component myArray=[1, 2, 3]/>
|
51 | <awesome-component myObject={hello: 'world'}/>
|
52 | <awesome-component myVariable=name/>
|
53 | <awesome-component myFunctionCall=input.foo()/>
|
54 | ```
|
55 |
|
56 | #### 3. Isomorphic UI components made easy
|
57 |
|
58 | Tired of boilerplate code and trouble managing component input and state? Marko
|
59 | makes it a breeze to develop self-contained and individually testable
|
60 | components. Changing state is completely synchronous, so there won’t be any
|
61 | headaches. You can also use inline styles making it very easy to develop small
|
62 | components quickly.
|
63 |
|
64 | ```marko
|
65 | class {
|
66 | onInput(input) {
|
67 | this.state = {
|
68 | count: input.count || 0
|
69 | };
|
70 | }
|
71 | increment() {
|
72 | this.state.count++;
|
73 | }
|
74 | }
|
75 |
|
76 | style {
|
77 | .count {
|
78 | color:#09c;
|
79 | }
|
80 | }
|
81 |
|
82 | <div.count>${state.count}</div>
|
83 | <button on-click('increment')>
|
84 | Click me!
|
85 | </button>
|
86 | ```
|
87 |
|
88 | Do you see references to “Marko” in the snippet above? Yeah, me neither.
|
89 |
|
90 | Is your component becoming too large? Do you prefer separating your CSS,
|
91 | JavaScript, and markup code? No problem. You can easily [rip out your code into
|
92 | multiple files](http://markojs.com/docs/components/#multi-file-components):
|
93 |
|
94 | ```
|
95 | components/
|
96 | click-counter/
|
97 | component.js
|
98 | index.marko
|
99 | style.css
|
100 | ```
|
101 |
|
102 | #### 4. Concise syntax
|
103 |
|
104 | The DOM is just a tree structure. Indentation is a great way to describe a DOM
|
105 | tree without having to worry about matching up beginning and ending tags. Marko
|
106 | lets you choose between a concise, indentation-based syntax, and a familiar HTML
|
107 | syntax:
|
108 |
|
109 | ```marko
|
110 | <!-- Count our clicks! -->
|
111 | <div.count>
|
112 | <p>Count: ${state.count}</p>
|
113 | </div>
|
114 | <button.example-button on-click('increment')>
|
115 | Click me!
|
116 | </button>
|
117 | ```
|
118 |
|
119 | Here’s the same thing with the concise syntax:
|
120 |
|
121 | ```marko
|
122 | // Count our clicks!
|
123 | div.count
|
124 | p -- Count: ${state.count}
|
125 | button.example-button on-click('increment') — Click me!
|
126 | ```
|
127 |
|
128 | Can’t make up your mind or just want to paste in that code snippet from
|
129 | StackOverflow? HTML syntax can be used within in the concise syntax. You’ll come
|
130 | back and make it consistent…_one day_.
|
131 |
|
132 | #### 5. Import JavaScript modules
|
133 |
|
134 | Do you have some helper JavaScript functions that you need to use in your views?
|
135 | Marko let’s you import any JavaScript module into your template using the same
|
136 | syntax as the JavaScript `import` statement without using Babel or any other
|
137 | build tool. No need for problematic globals (you could do that too, but please
|
138 | don’t or your coworkers will hate you).
|
139 |
|
140 | ```marko
|
141 | import sum from './utils/sum';
|
142 |
|
143 | <div>The sum of 2 + 3 is ${sum(2, 3)}</div>
|
144 | ```
|
145 |
|
146 | #### 6. No need to import custom tags (it’s a good thing, trust me)
|
147 |
|
148 | Marko uses your directory structure as a method for automatically registering
|
149 | custom tags. This means that Marko can implicitly import tags based on where the
|
150 | template is located on disk. Marko will search up the directory looking for
|
151 | custom tags in `components/`directories similar to how Node.js discovers modules
|
152 | in `node_modules/` directories.
|
153 |
|
154 | Given the following directory structure:
|
155 |
|
156 | ```
|
157 | components/
|
158 | fancy-button/
|
159 | index.marko
|
160 | fancy-container/
|
161 | index.marko
|
162 | ```
|
163 |
|
164 | If `fancy-button` is used inside of `fancy-container`, it will be implicitly<br>
|
165 | imported:
|
166 |
|
167 | ```marko
|
168 | <!-- No need to use `require` or `import` because it will implicitly import custom tags -->
|
169 | <div>
|
170 | <fancy-button color=input.buttonColor/>
|
171 | </div>
|
172 | ```
|
173 |
|
174 | #### 7. Use JavaScript to set CSS classes and styles
|
175 |
|
176 | Setting CSS classes and styles is made easy using JavaScript! Marko will happily
|
177 | accept simple strings, JavaScript objects and arrays (_falsy values will be
|
178 | ignored)._
|
179 |
|
180 | ```marko
|
181 | $ const fontColor = input.color || 'blue';
|
182 | $ const isActive = input.active === true;
|
183 |
|
184 | <div class=['person', isActive && 'active']
|
185 | style={color: fontColor} />
|
186 | ```
|
187 |
|
188 | #### 8. Inline JavaScript Statements
|
189 |
|
190 | Marko takes HTML and makes it more like JavaScript. You can exit out of HTML
|
191 | mode to embed a JavaScript statement by starting the line with a `$`. You can
|
192 | use this feature to embed JavaScript variables, functions, etc. where they are
|
193 | needed (take that, “separation of concerns”).
|
194 |
|
195 | ```marko
|
196 | $ const randomNumber = Math.random();
|
197 | $ const person = {
|
198 | name: 'Frank',
|
199 | age: 32
|
200 | };
|
201 |
|
202 | <div>Random number: ${randomNumber}</div>
|
203 | <div>${person.name} is ${person.age} years old</div>
|
204 | ```
|
205 |
|
206 | If you want to combine multiple JavaScript statements you can do that too:
|
207 |
|
208 | ```marko
|
209 | $ {
|
210 | const randomNumber = Math.random();
|
211 | const person = {
|
212 | name: 'Frank',
|
213 | age: 32
|
214 | };
|
215 | }
|
216 |
|
217 | <div>Random number: ${randomNumber}</div>
|
218 | <div>${person.name} is ${person.age} years old</div>
|
219 | ```
|
220 |
|
221 | #### 9. Async rendering with the `<await>` tag
|
222 |
|
223 | Node.js is asynchronous. Browsers are asynchronous. Why should rendering be
|
224 | synchronous? Pass your promise along to your template and Marko will
|
225 | asynchronously render parts of your view. Turns out, [this is good for
|
226 | performance](http://www.ebaytechblog.com/2014/12/08/async-fragments-rediscovering-progressive-html-rendering-with-marko/).
|
227 |
|
228 | ```marko
|
229 | $ const searchResultsPromise = searchService.performSearch(keywords);
|
230 |
|
231 | <await(searchResultsPromise)>
|
232 | <@then|person|>
|
233 | Hello ${person.name}!
|
234 | </@then>
|
235 | <@catch|err|>
|
236 | The error was: ${err.message}.
|
237 | </@catch>
|
238 | </await>
|
239 | ```
|
240 |
|
241 | #### 10. Server side rendering is easy
|
242 |
|
243 | Can’t decide if you want to do server-side rendering or client-side rendering?
|
244 | Why are we even talking about this in 2017? It doesn’t matter. Seriously, just
|
245 | do both. Marko makes this a no-brainer since you can render a Marko template
|
246 | directly to a stream (oh, and Marko will [automatically mount UI
|
247 | components](http://markojs.com/docs/server-side-rendering/) rendered on the
|
248 | server when the page loads in the browser):
|
249 |
|
250 | ```js
|
251 | require("marko/node-require").install(); // require .marko files!
|
252 |
|
253 | const http = require("http");
|
254 | const template = require("./template");
|
255 |
|
256 | http
|
257 | .createServer()
|
258 | .on("request", (req, res) => {
|
259 | template.render(
|
260 | {
|
261 | name: "Frank",
|
262 | count: 30,
|
263 | colors: ["red", "green", "blue"]
|
264 | },
|
265 | res
|
266 | );
|
267 | })
|
268 | .listen(8080);
|
269 | ```
|
270 |
|
271 | #### Bonus: Friendly compile-time errors
|
272 |
|
273 | We all make mistakes _every now and then_. Typo in your custom tag? Forgot an
|
274 | ending tag? No worries! Marko will give you a friendly error message and point
|
275 | you right to the problematic code.
|
276 |
|
277 | ```marko
|
278 | <!-- Ahhhh typo! This should be <fancy-button/> -->
|
279 | <fancy-buttn/>
|
280 | ```
|
281 |
|
282 | You may have missed it, but it was obvious to Marko:
|
283 |
|
284 | ```
|
285 | Unrecognized tag: fancy-buttn — More details: https://github.com/marko-js/marko/wiki/Error:-Unrecognized-Tag at line 2 col 1
|
286 | ```
|
287 |
|
288 | Coming soon: auto correction and autonomous coding
|
289 |
|
290 | ---
|
291 |
|
292 | _Cover image credit:
|
293 | _[Wikipedia](https://commons.wikimedia.org/wiki/File:Amanhecer_no_Hercules_--.jpg)
|