background

The main work of this week is to build the shelf of the new project, which is based on VUe-CLI construction. The basic functionality is already available in scaffolding, but some customization is needed for specific business scenarios.

mock

A normal development process would look something like this with the current development model of front and back end separation:

In this way, the front-end can be developed without waiting for the back-end interface to be completely written, and the front-end and back-end can be developed in parallel to improve team efficiency.

Scaffolding provides only one proxy function, which cannot meet our needs. So we need to write our own mock-Middleware. This is probably one of the things I won’t post to mock- Middleware:

module.exports = () => {
    const argv = Array.prototype.slice.call(process.argv, 2);
    const proxyAddress = argv[0];
    if(! argv.length || proxyAddress ==='mock') {
        return mockMiddleware;
    } else {
        returnproxyMiddleware(proxyAddress); }}Copy the code

The net effect is to run NPM run Dev mock, which forwards API requests (note: only proxy API requests, not static resources) to mock data. After the front and back end development is complete, you can run NPM run dev {proxy_ip} to broker API requests to a development server for tuning.

Of course, this article is not focused on that, but to summarize how to write CSS in a SPA application.

CSS modular

The story starts with the selection of the component library. The project chooses the open source Element as the component library. This component library does not meet the visual specifications of the Koala front-end group, and there is no plan to fork a branch for maintenance at present, so a simple and crude approach is adopted, style overwriting. Let’s take a look at the styles in Element:

@component-namespace el { @b progress { position: relative; line-height: 1; @e text { font-size:14px; display: inline-block; vertical-align: middle; margin-left: 10px; line-height: 1; } @m circle { display: inline-block; }}}Copy the code

Some of you may be a little confused when you first see this pattern, but look closely at @B, @E, @M, it is the BEM specification.

BEM specification

There are many articles on the web about the BEM specification. There is also an article on Zhihu to discuss the pros and cons of BEM specification. I do not discuss the pros and cons of BEM specification here. I think it is always right to agree on a specification in a project and strictly implement it.

In order to be consistent with the CSS specification of the component library, on the other hand, I personally feel that the advantages of BEM outweigh the disadvantages, so I plan to write this specification in my project.

The problem is that browsers don’t understand @b, @e, @m syntax, and postCSS is needed to help.

postcss

Postcss, like tools such as gulp and Webpack, is not a preprocessor or post-processor per se, but is transformed by plug-ins (such as the popular Autoprefixer).

Postcss-salad is a collection of postCSS plug-ins that support the latest CSS syntax, some SASS nested syntax, and BEM transformations. Postcss.config.js: postcss.config.js

module.exports = {
  plugins: [
    require('postcss-salad')({
      browsers: ['ie > 9'.'last 2 versions'],
      features: {
        bem: {
          shortcuts: {
            component: 'b',
            descendent: 'e',
            modifier: 'm'
          },
          separators: {
            descendent: '__',
            modifier: The '-'}}}})]}Copy the code

The concatenators between BEMs can be customized to be consistent with Element’s components. In this case, we can use the postCSs-CLI tool to see the effect. Postcss + postCSs-salad = postCSs-salad = postCSs-salad = postCSs-salad = postCSs-salad

.el-progress {
    position: relative;
    line-height: 1
}

.el-progress__text {
    font-size: 14px;
    display: inline-block;
    vertical-align: middle;
    margin-left: 10px;
    line-height: 1
}

.el-progress--circle {
    display: inline-block
}Copy the code

Postcss-loader: PostCSS-Loader: PostCSS-Loader: PostCSS-Loader: PostCSS-Loader: PostCSS-Loader: PostCSS-Loader

Then import the postcss.config.js file as the postCSs-loader configuration file.

{
  loader: 'postcss-loader',
  options: {
    config: {
      path: 'path/to/postcss.config.js'}}}Copy the code

At this point, you can use this nested BEM syntax for CSS writing in a WebPack project.

CSS Modules

That’s not the end of the story. One common requirement when writing single-page applications is that you want CSS scopes between components to be isolated from each other. The first thing that comes to mind is Scoped CSS. Vue-loader also supports Scoped CSS (it’s actually postCSS). So how does Scoped CSS handle this problem:

<style scoped>
.example {
  color: red;
}
</style>

=>

<style>
.example[_v-f3f3eg9] {
  color: red;
}
</style>Copy the code

As you can see, Scoped CSS implements CSS scoping isolation with a hash after the class, but in practice there are several problems:

  1. Scoped CSS works on both parent and child components, so there is no separation of parent and child styles.
  2. Because Scoped CSS changes the class in the DOM, there is no way to override the global style of an Element component within the component.

At this time you need CSS Modules, CSS Modules is the current CSS modularization scheme is a highly accepted scheme, there are a lot of articles on the Internet to introduce it. Again, we need to look at what CSS Modules can do first.

Postcss has a lot of plugins, so there must be a plugin for CSS Modules as well. We will also use the previous postCSs-CLI to test, first configure this plugin:

require('postcss-modules')({
  generateScopedName: '[local]--[hash:base64:5]'
})Copy the code

[local] is the class name, [hash:base64:5] is the hash value generated according to the given rule, you can also use variables like [name] is the tag name, [path] is the path, etc. CSS Modules generate class can customize rules, so you can also use custom rules. Instead of using the BEM specification, it depends on the specific situation of the project

With the same CSS code, look at the result:

.el-progress--3tuDF {
    position: relative;
    line-height: 1
}

.el-progress__text--1W8n3 {
    font-size: 14px;
    display: inline-block;
    vertical-align: middle;
    margin-left: 10px;
    line-height: 1
}

.el-progress--circle--3OD0E {
    display: inline-block
}Copy the code

Consistent with our preset style rules, this solves the first problem above, making the style unique within each component.

Global {.class} : Global {.class} :

:global(.el-progress) {
  position: relative;
  line-height: 1;
}

=>

.el-progress {
  position: relative;
  line-height: 1;
}Copy the code

This eliminates the hash suffix on the class and allows you to override global styles.

Practice of CSS Modules in Vue+ Webpack project

First, configure the generation class rule in the vue-loader configuration file:

cssModules: {
    localIdentName: '[local]--[hash:base64:5]',
    camelCase: true
}Copy the code

Then turn on CSS Modules within the component by adding Modules on the style.

<style module>
</style>Copy the code

Css-loader will inject a $style object into the current component. So in practice it would look something like this:

<header :class="$style['titan-header']">
</header>Copy the code

Sometimes we use it like this:

<div :class="{ 'active': selectedIndex == index} ">
</div>Copy the code

In this case, the style is the attribute name of the object, and we know that with CSS Modules, we have to replace ‘active’ with $style.active, thankfully we have ES6! ES6 has a feature that uses double parentheses to support the use of computed attributes as attribute names, i.e. :


<div :class="{[$style.active]: selectedIndex == index} ">
</div>Copy the code

conclusion

Bem +CSS Modules has been in practice for a week in the new project, and I didn’t find any problems, so I just wrote this article to summarize. The above is just some summary of my construction process in this project, and I do not guarantee that my views are completely correct, for your reference.