前端框架选型是企业提升开发效率与用户体验的关键因素
852
2022-11-04
利用vanilla ES6和Custom Elements构建基于组件的UI,不需要任何框架
HTMLComponent
Build component-based UIs without frameworks, just vanilla ES6 and Custom Elements.
What?
You know how in frontend frameworks like React/Angular/Vue you can split your entire UI in small, self-contained components? It turns out you can get a similar kind of organization without any framework.
Why?
Splitting a complex page into small, self contained components is really helpful for ease of maintenance. If that's all you're interested, and are ok with manipulating the DOM yourself, you shouldn't need to pull a framework for that. Frontend frameworks can be really helpful, but they have their own share of problems: the payload size, lack of control, leaky abstractions, vendor lock-in, steep learning curve, integration with browser dev tools, etc. In the meantime, browsers are implementing Web Components natively and some parts of it, like Custom Elements, have polyfills stable enough that can be used in production today.
Web components technologies
"Web components" is an umbrella term for 4 different technologies:
Custom Elements: allow creation of new element tags, with custom behaviorHTML Imports: allow importing an .html file like we can already do with .css and .js filesTemplate tags: standard approach for holding DOM based templatesShadow DOM: allow creating an internal DOM for each element, out of the document's main scope.
While all these technologies are great, it's not very easy to use them on all browsers today. Shadow DOM is very hard to polyfill, and attempts to do it are either slow and/or very complex. HTML Imports (polyfilled) can have performance issues when there are too many dependencies (each dependency being a new call to the server), and is still subject to spec changes.
The goal of this project is to enable the usage of web components today, by replacing these tricky technologies with safer alternatives, while keeping a simple and modern workflow for development.
Single file components
One of the best patterns for organizing self-contained components is to have each one in a single file, complete with template, style and script. Using native html imports + html-component, you can describe a component like this:
Hello {{props.to}}!
And then, import and use it like this:
The component will automatically register itself on the page it was attached to. Usually, this would mean simply calling customElements.define('hello-world', HelloWorld). Besides that, the HelloWorld.register() method called above does a few things more:
compile the template as a mustache template (used by the this.render() method)scope the style to the component (the example above will render as hello-world p { font-weight: bold }), as a super simple alternative to Shadow DOM
This provides a nice workflow, since no build step is necessary in development for browsers supporting native html imports (currently, only Google Chrome). No file watchers, no just in time compilation, just run the app natively.
Build process
For other browsers, use the build-html-component custom script for converting all these HTML imports into old-school combined js and css assets:
Steps performed in the build script:
precompiles mustache templates with hogan.js,transpiles ES6 scripts to ES5 with babel,scopes styles to the component (as described above),process the css with postcss-cssnext (enable modern CSS features).
To run the build, first npm install all dependencies, then npm run build.
HTMLComponent class
HTMLComponent is a small base class wrapping the native HTMLElement. It provides a few helpers to make it easier to adopt this style of development:
this.render(): invokes the component's template and replace the component's content as the result. It uses the component's mustache template by default. Also fixes boolean attributes in mustache (see Caveats below). this.props: gets all attributes of the element as a hash, with some basic "guess" type conversion for numbers and booleans. this.update({props}): updates all attributes of the hash and automatically triggers a re-render. Being able to re-render components means there's a lot less need of manual DOM manipulation. this.on(event, selector, handler): attaches events using event delegation, so you can re-render the content of the component without losing the events. this.emit(event, data): notify a parent component with a native Custom Event, optionally passing custom data. The parents can listen to this event with the same this.on() method. this.beforeEach(handler) and this.afterEach(handler): a handler to run before / after all events attached with this.on(). this.show(), this.hide() and this.toggle(showOrHide): shortcuts for adding/removing the hidden attribute Component.create(props) (static): shortcut for instantiating a component with its props. Component.register() (static): defines custom element and optionally register template/style (see build example above)
For more details, please check the source HTMLComponent.
Advanced example
From the canonical Todo application:
Best practices
The guidelines for building maintainable components with this approach are more or less similar to what we see in other frameworks:
keep components small and focusedsingle file components: template + behavior + styles together (group by feature, not by technology)parent components can communicate with child components, but without accessing their inner DOMchild components only emit events to notify parents, don't modify parents directlysibling components don't talk to each other, notify parents instead
An example of these principles all in practice is the sample Todo App implementation: todo-list, todo-item and todo-summary.
Dependencies
document-register-element.js: standalone polyfill for Custom Elements v1, in about 10kb min, supporting most browsers and IE9+.dom4.js (DOM4 methods polyfills): optional, makes it easier to work with the DOM so you'll never miss jquery again.
Getting Started
Clone this projectCreate your own .html components in /srcReference them in index.html
Caveats
Mustache templates require a small change in their syntax when handling boolean attributes: won't work, so use instead. The reason is, the first example isn't valid html, and whatever the content of the template tag is, it must be html. In the render method, there's a fix for adding/removing these boolean attributes according to their value. "scoped" styles don't actually scope, they just add the component's name as a namespace to your selectors. So, in the
FAQ
Oh great, another MVC framework...
This is not a framework, much less MVC. This is more like a boilerplate web project with a very opinionated build process.
How is it different from React/Angular/Vue...?
These frameworks all have built their own non-standard model of components, parallel to the native browser functionalities. They also build on the approach of abstracting the DOM and having a state/model object being the single source of truth, leaving the DOM manipulation entirely to the framework. While there are benefits to this approach, it also comes with a cost. Using vanilla Custom Elements and ES6 means the DOM is the source of truth, and you'll manipulate it directly, but don't panic! Not only the DOM api is much better to work with these days, but having it split over components that can be easily re-rendered from a template also makes it much simpler to build powerful UIs.
Why not Polymer?
I like very much of the idea behind Polymer, having a small framework on top of all the 4 Web Components technologies (Templates, HTML Imports, Shadow DOM and Custom Elements) and overall push the adoption of Web Standards. However, the framework is entirely dependent on the stability of the polyfills, and some technologies are harder to polyfill than others (like Shadow DOM, for example). It also doesn't inspire a lot of confidence the fact that until today, not all browsers have agreed to implement all of these technologies. They did agree to implement Custom Elements v1, though, so depending only on that seems like a safe ground to proceed.
What about performance?
You'll have complete control over what happens on the DOM, so how fast it is depends on how well you know vanilla javascript. The main polyfill, document-register-element, is based on native MutationObservers (which is pretty fast), so there's no inherent technical reason for this approach to have performance issues.
版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。
发表评论
暂时没有评论,来抢沙发吧~