2017 was the year vue.js exploded, and React got a serious competitor to challenge it for the crown (Vue and React star numbers are closing in on Github as of this writing). Our team adopted the Vue technology stack in more than ten large projects this year, and achieved good results in development efficiency, page performance, maintainability and other aspects. We hope to extract reusable functional components from these projects and use them for subsequent projects to reduce repeated development and improve efficiency. At the same time, in order to pay tribute to the industry’s “create a framework, build a wheel”, a mobile UI component library based on Vue 2 is proposed. Component library development process is generally relatively smooth, here to share some problems and thinking with you.

Scaffolding selection

Although most of our team’s scaffolding for these Vue tech stack projects is WebPack, we hesitated between Webpack and Rollup when choosing scaffolding for our component library.

Rollup seems to be a better fit for component library development, it builds all the modules in a single function for more efficient execution, it supports Tree Shaking, only packages the required code, and smaller output files (webPack later supported this). But all things considered, we chose WebPack as the packaging tool. First, demo presentations and documentation pages are planned in this scaffolding, so there is a need for features like code splitting and hot loading, which Rollup is nowhere near as good as WebPack. In addition, the component library is developed and maintained by multiple people, making it cheaper and more efficient to build on existing WebPack scaffolding. Choosing Webpack allows us to focus more on building wheels.

packaging

Even if webPack is chosen as the packaging tool, we do not want to limit the use of the library to webPack projects, and should support scenarios such as AMD/CMD or even direct references via script tags. To do this, we need to set the output format in the WebPack configuration file. The option to configure is output.libraryTarget, which has the following optional values:

  • “Var” (the default) is printed as a variable
var MyLibrary = _entry_return_ ;Copy the code

  • “This” is output as a property of this
this["MyLibrary"] = _entry_return_ ;Copy the code

  • The “window” output is a property of the window object
window["MyLibrary"] = _entry_return_ ;Copy the code

  • The “global” output is a property of the global object
global["MyLibrary"] = _entry_return_ ;Copy the code

  • The ‘commonJS’ output is an attribute of exports
exports["MyLibrary"] = _entry_return_ ;Copy the code

  • Commonjs2 is exported as module.exports
module.exports = _entry_return_ ;Copy the code

  • Amd indicates an AMD module
  • “Umd” is exposed to all module definitions, allowing it to work with CommonJS/AMD/ global variables

Obviously, we need to set output.libraryTarget to “umd” to make our library work in a variety of scenarios.

LibraryTarget is set to umd and output.library is set. Setting this to true will name AMD modules.

webpackConfig.output = { path: path.resolve(__dirname, 'dist'), publicPath:"/", filename: '[name].js', library: 'XXX ', // module name libraryTarget: 'umd', // output format umdNamedDefine: true // whether the module name as AMD output namespace};Copy the code

The Vue component library only provides components. The Vue file itself needs to be introduced by the user of the component library in the project. There is no need for packaging in the library. So we can add Vue to externals.

externals: {
    vue: 'vue'
}Copy the code

This way Vue will not be packaged. There is a problem, however, with reference to Vue as a script tag, the variable name hanging on the window is “Vue”, not the desired “Vue”, so the use of Vue undefined error.

Fortunately, webPack’s externals configuration item supports passing in an object that can be named differently for different export forms. So this is the way to solve the problem.

Component type

The planned Vue Component library contains three types: Component, Directive and Filter.

Special are Modal components such as Dialog, Toast, and so on. There may be many modals in a page, and in many scenarios only part of them will be triggered by the user’s behavior. Is it appropriate to write all possible Modal (especially asynchronous and complex Modal) on a page? For multi-page applications, is it cumbersome to write each page or encapsulate another layer of components? This issue has been discussed on Zhihu. You (Vue.js author You Yuxi) himself suggested in the discussion that when components are nested in multiple layers, Modal should be placed in the root component, and then triggered by events in the sub-component. In practical applications, this should be used, in line with Vue’s “state-driven” approach. However, in the component library, we wanted to provide a more convenient and generic way to use Modal-type components.

Taking a page from good component libraries like Element UI, we hooked modal-type components to Vue.prototype to make them instance methods of Vue, once installed, globally called.

this.$dialog(options);Copy the code

Therefore, our component library component types also include “instance methods.”

Component CSS scope

For a component, we want its CSS to apply only to elements within the current component, so we add the scoped attribute to the style tag of each component’s Vue single-page file. When compiled, the HTML tag is automatically given a randomly generated unique attribute (such as data-v-f3f3eg9), and the CSS selector is given a property selector of the same name (such as. Example [data-v-f3f3eg9]), so that the CSS within the component is scoped.

The compiled:

The scoped attribute does help to set the scope for component styles, basically preventing the styles inside the component from affecting the outside, but it also introduces the inconvenience of overwriting the styles inside the component. No matter how generic the functionality of a component is or how flexible the interface is, when it comes to the UI, it’s hard to avoid meeting all project style requirements, so you should be able to override some or all of the component styles as needed in a specific project. Scoped makes it more difficult to override styles by randomly generating attribute names.

After a tradeoff, we removed the scoped attribute from the component and used the class policy instead to prevent the styling inside the component from affecting the outside. Of course, the scoped property is also useful. It is more suitable for specific applications and not the best choice for components with high reuse.

On-demand use and custom build

As the project progressed, there were more and more components in the library, more than 40 at present, and the files after construction became bigger and bigger. If an application uses only a few components in the library, there is no need to use a complete build pack, so we need to provide a way to use it on demand. In the early days, we let users install component libraries through private NPM and load them on demand by directly referencing component sources in the SRC directory as required by the application itself. This approach has great limitations, because the source code referenced is not compiled, requiring users to deal with component dependencies, ES6/SCSS/Vue template compilation work also requires users to complete in their own projects, tedious, error-prone, and difficult to support other scenarios outside webPack. We envision providing a custom build for on-demand packaging. You first let the user choose which components you want, then you generate a personalized configuration file based on that information, then you build from that file, and finally you package and compile only the components you specify.

So, how do you interact with users and collect user instructions? A more user-friendly approach is through the Web, such as providing a page on the project home page that allows users to select components online and then download the built files. According to the current positioning of our component library, the recommended way to use is through private NPM installation, so we first introduced a command-line interface (CLI) to complete the custom build.

The user only needs to execute the command “NPM Run Custom” in the terminal to get the list of all components, select the required components through the keyboard, and then press Enter, and the scaffolding will automatically complete the rest of the personalized build work.

A few moments later, a build package containing only user-selected components appears in the dist directory, with a much smaller file size than the full version.

In this way, the selected components will go through the complete construction process of component library scaffolding, automatic processing of component dependencies, the ES6/SCSS/Vue syntax is also compiled, the constructed files also support AMD/CMD/ Script tag direct reference and other scenarios, can better meet the needs of on-demand use.

icon

Component library UI components inevitably contain small ICONS, and you need to find an appropriate way to handle these ICONS.

In application development, some images are sometimes Base64 encoded in the code, which can increase the amount of data by about 30%, so this method is not suitable for larger images. For small ICONS, the increase in absolute data is not large, but can reduce one HTTP request, which is also an optimization solution. However, component libraries are more sensitive to data volumes than normal applications, and this approach is not the best option.

CSS Sprite is another classic way to deal with small ICONS, but this icon reference method based on precise location information is not so popular in mobile REM based layout, because REM layout itself is difficult to be accurate, and if used in component libraries will cause some inconvenience for on-demand reference.

For small ICONS, a vector scheme is more needed on the mobile end, which naturally ADAPTS to screens of various pixel densities.

In component libraries, a popular option is the ICON font scheme based on the CSS3 font (@font-face), which places ICONS in a custom font file. There are many advantages, such as:

  • ICON is a vector in font, which is popular on mobile terminals
  • Good browser compatibility. Web fonts were not invented by CSS3, and older browsers (including IE6) actually support them, although there are some differences, but there are ways to make them compatible
  • You can use CSS to control styles such as ICON color and transparency, and even achieve color gradient effects

We did not choose the ICON FONT scheme, we think the SVG scheme is more suitable for the mobile component library:

  • SVG is not compatible with some antique browsers on the PC side, but it is compatible with mobile
  • ICON fonts are considered text, so some browsers will anti-aliasing them, which can cause ICONS to be less sharp and less clear
  • SVG style control is more flexible than ICON FONT and can even control the color of various parts of an ICON to achieve a colorful ICON. That’s not possible with ICON fonts
  • ICON fonts are typically inserted into pages with pseudo-objects or pseudo-classes, and their presentation is affected by “line-height”, “vertical-align”, “letter-spacing”, “word-spacing” and FONT related CSS attributes, as well as by the FONT character design itself. SVG, on the other hand, is a tag on a page, which is easier to control and more semantic
  • Combining the Symbol element makes it possible to achieve what is called an “SVG Sprite,” which combines many SVG ICONS together and specifies them by ID reference for reuse. This is more convenient than CSS Sprite because you don’t need to care about the location of the icon

SVG Sprite doesn’t have to be assembled manually, it’s easy to dynamically generate SVG Sprite with WebPack’s SVG-Sprite-loader, and loading ICONS on demand is not a dream.

Well, limited by space, I will talk so much this time. For more content, please pay attention to our team’s public account “Full stack Exploration”.

Further reading

  • NutUI Component library — nutui.jd.com
  • Webpack output. LibraryTarget – webpack.js.org/configurati…
  • Umd – github.com/umdjs/umd