Linaria 也许是如今 React 最佳的 JSS 方案

React 社区一直在探索各类 JSS 方案,好比如今比较出名的 styled-components ,但他们或多或少都有些问题存在,可是社区对 JSS 方案的探索一直没有停下,而如今看上去最像最佳方案的是 Linaria 库。翻了下这个库相关的中文资料几乎没有,因而写了这篇与你们分享介绍下相关的内容css

介绍

Linaria 是一个 零运行时 的JSS 框架,其特色有:html

  • 将 CSS 归入到 JS 体系中,而且这种支持是零成本的! CSS 相关代码会在编译期被抽出到 CSS 文件中
  • 类 Sass 的 CSS 的语法
  • 经过使用 CSS 变量,Linaria 支持快速建立动态属性的 React 样式组件
  • 使用 CSS sourcemaps 易于定位样式位置
  • 支持 stylint
  • 再也不须要预处理器,能够使用 JavaScript 控制 CSS 的逻辑
  • 可是支持使用预处理器,好比 Sass 或 PostCSS

相对于传统 CSS 方案的优势

1. 样式隔离

const title = css` font-size: 18px; `;
复制代码

相似代码最终会被会被编译成node

.k4yi6fg {
  font-size: 18px;
}
复制代码

其 class 命名是经过计算文件路径的哈希值肯定的react

2. 样式和组件归属到同一文件中

不会再有编写组件时须要在 JS 文件和 CSS 文件跳转的上下文切换的状况。不过若是你想要分离的话,一样也是支持的webpack

3. 可靠安全的重构支持

由于 JSS 的样式其实就是 JS 变量而已,因此你能够很容易经过代码逻辑找到组件相关的样式逻辑,而不用惧怕重构会形成预料以外影响web

4. 再也不须要预处理器

JSS 最迷人的一点就是,当你将 CSS 归属到 JS 下时,你就自动得到来使用 JS 编写 CSS 逻辑的能力,最基本的条件计算,被包装成简单函数调用的的复杂逻辑。这意味着 CSS 的表达能力的上限再也不局限于自身,而是由 JS 决定的npm

例如你能够编写这样的代码编程

const DEFAULT_COLOR = '#fffff'
const PRIMARY_COLOR = '#de2d68';

const getColor = Math.random() > 0.5 ? PRIMARY_COLOR : DEFAULT_COLOR;

const button = css` background-color: ${getColor()}; &:hover { background-color: ${Math.random() > 0.5 ? PRIMARY_COLOR : DEFAULT_COLOR}; } `;
复制代码

5. Tree shaking

就像咱们刚才说的同样, JSS 其实只是 JS 变量,那么天然而然 JS 能作到的 Tree shaking ,Linaria 同样能作到。 这点对于 UI 库的开发者其实颇有吸引力,再也不须要引入额外的 babel 插件,而是自动经过 Tree shaking 来作到样式的按需引入json

6. 自动添加浏览器前缀

Linaria 会自动经过添加浏览器前缀,帮你对一些特殊属性作兼容性支持,同时你依然可使用 PostCSS 作进一步的优化浏览器

7. 声明式且动态化的控制 React 组件样式的能力

经过 styled API ,很容易去声明 React 动态样式组件。原理是经过 CSS 变量来实现组件样式自动更新的能力,常规的 CSS 方案则须要你手动的去维护相关的逻辑

const Box = styled.div` background-color: orange; height: ${props => props.size}px; width: ${props => props.size}px; `;

<Box size={48}> 复制代码

相比于 CSS 预处理器的优点

1. 没有新的学习成本

Linaria 的语法能够看做只是支持嵌套的 CSS 语法而已。没有变量,mixins 或 函数什么的,这些均可以用 JS 的逻辑来代替

2. 相对于传统 CSS 方案的优势对于CSS 预处理器也同样

相比于直接写 行内样式 的优点

1. 彻底的 CSS 能力支持

行内样式存在局限性,而 Linaria 则支持 CSS 的全部特性:

  • 媒体查询
  • CSS3 动画
  • 伪类,伪元素

2. 性能优点

经过 class 命名来应用样式要比行内样式快

相比其余的 JSS 方案的优点

1. CSS 的下载和解析是和 JS 分开的

由于 CSS 在编译器被抽出到 CSS 文件中了,所以浏览器能够并行的下载 CSS 和 JS 文件,加速首屏时间

2. 没有额外的解析成本

不少 JSS 框架是经过某个第三方 JS 库来解析 CSS 字符串的,因为须要包含解析器,会使得库的体积增大。 而且 CSS 的解析执行被延迟到了 JS 运行时,在一些低端设备上,很容易带来能够感知到的延迟

Linaria 特殊就特殊在它 没有运行时 这一说,它的样式会在编译期解析抽出来,生成 CSS 文件,不须要在运行时额外解析一次。

3. SSR 时没有重复渲染的性能损耗

对于基于组件的 JSS 框架来讲,使用不一样的 props 渲染同一个组件会使得同一份样式被复制屡次,这使得 SSR 时产物的体积会增大。尽管大部分状况下,这种问题带来的性能损耗不值一提,可是对于渲染多个仅有细微差别的大型列表时,很容易使得体积迅速增加

除此以外,在作 SSR 你须要将写在 JS 文件中的 CSS 样式抽取出来,而后传输给浏览器,这一样增长产物体积

Linaria 会生成惟一的样式规则,使用 CSS 变量来应用不一样的差别,因此不会有重复样式的问题,也就减小产物的体积

4. 编译期检查语法错误

非法 JS 值和错误的 CSS 语法,都会在编译期检查出来,而不用等到运行时才暴露出来。这意味着你不会在生产模式遇到这些低级错误,一样的, Linaria 支持 stylelint ,你依旧能够得到原来同样的 lint 体验。

5. 熟悉的 CSS 语法

不一样于其余的一些 JSS 框架, Linaria 语法只是支持嵌套的 CSS 原生语法而已,没有什么上手成本。完美支持面向 Copy-Paste 编程

6. 支持无 JavaScript 运行

若是你的网站须要在禁止 JavaScript 的状况运行,或者想要在编译生成静态网页的运行, Linaria 一样能够在这些状况下正常运行,由于它的 没有运行时 特性

安装配置

Linaria 同时支持 Webpack , Rollup 和 Sevlte ,本文只会讲述配合 Webpack 使用的方式,若有其余需求,能够去官网看下配合其余构建工具的文档

首先

若是你在项目里使用里 Babel 作转译或这 polyfill 什么的,那么必定要在根项目下创建一个 Babel 配置文件,把你须要的 presets 和 plugin 写在里面,否则 Linaria 会没法解析你的代码

和 Webpack 配合

和 Webpack 配合很容易,只须要将 babel-loader 加上 linaria/loader 便可,必定确保 linaria/loader 紧挨着且在 babel-loader 以后

{
  test: /\.js$/,
  use: [
    { loader: 'babel-loader' },
    {
      loader: 'linaria/loader',
      options: {
        sourceMap: process.env.NODE_ENV !== 'production',
      },
    }
  ],
}
复制代码

此外,为了将收集到的样式抽取出来,你须要另一个 Webpack 插件 mini-css-extract-plugin , 执行 npm i -D css-loader mini-css-extract-plugin 来安装

而后导入 mini-css-extract-plugin

const MiniCssExtractPlugin = require('mini-css-extract-plugin');
复制代码

而后设置相应的解析规则和插件

{
  test: /\.css$/,
  use: [
    {
      loader: MiniCssExtractPlugin.loader,
      options: {
        hmr: process.env.NODE_ENV !== 'production',
      },
    },
    {
      loader: 'css-loader',
      options: {
        sourceMap: process.env.NODE_ENV !== 'production',
      },
    },
  ],
},
复制代码

mini-css-extract-plugin 加入 Webpack 配置的 plugins 属性中

new MiniCssExtractPlugin({
  filename: 'styles.css',
});
复制代码

你能够经过 HTMLWebpackPlugin 插件来将抽离出来的 CSS 文件与构建产生的 html 文件链接起来,对于生产模式,你也许须要将哈希值设置的 CSS 文件名上:

new MiniCssExtractPlugin({
  filename: 'styles-[contenthash].css',
});
复制代码

由于 Linaria 会抽离出来的样式文件过一遍 Webpack .css 规则的 loader 流水线,因此你能够很容易去使用 postcssclean-css 来作一些定制化操做

一个完整的 Webpack 配置例子

const webpack = require('webpack');
const path = require('path');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');

const dev = process.env.NODE_ENV !== 'production';

module.exports = {
  mode: dev ? 'development' : 'production',
  devtool: 'source-map',
  entry: {
    app: './src/index',
  },
  output: {
    path: path.resolve(__dirname, 'dist'),
    publicPath: '/dist/',
    filename: '[name].bundle.js',
  },
  optimization: {
    noEmitOnErrors: true,
  },
  plugins: [
    new webpack.DefinePlugin({
      'process.env': { NODE_ENV: JSON.stringify(process.env.NODE_ENV) },
    }),
    new MiniCssExtractPlugin({ filename: 'styles.css' }),
  ],
  module: {
    rules: [
      {
        test: /\.js$/,
        exclude: /node_modules/,
        use: [
          { loader: 'babel-loader' },
          {
            loader: 'linaria/loader',
            options: { sourceMap: dev },
          },
        ],
      },
      {
        test: /\.css$/,
        use: [
          {
            loader: MiniCssExtractPlugin.loader,
            options: {
              hmr: process.env.NODE_ENV !== 'production',
            },
          },
          {
            loader: 'css-loader',
            options: { sourceMap: dev },
          },
        ],
      },
      {
        test: /\.(jpg|png|gif|woff|woff2|eot|ttf|svg)$/,
        use: [{ loader: 'file-loader' }],
      },
    ],
  },
  devServer: {
    contentBase: [path.join(__dirname, 'public')],
    historyApiFallback: true,
  },
};
复制代码

你可使用下面命令来安装全部必须的 npm 库

npm i -D webpack webpack-cli webpack-dev-server mini-css-extract-plugin css-loader file-loader babel-loader
复制代码

定制 linaria/loader 的 options 属性

你能够这样来传递 options 属性

{
  loader: 'linaria/loader',
  options: {
    sourceMap: false, // 是否产生 CSS source map,默认 false
    cacheDirectory: '.linaria-cache', // 缓存所在文件见,默认 .linaria-cache
		extension: '.linaria.css', // CSS 文件处于中间态时的命名,默认 .linaria.css
		preprocessor: 'stylis', // 定义 css 的预处理器,默认为 stylis

  },
}
复制代码

使用方式

Linaria 使用起来很是简单,只有一个核心方法 `css``` ,基本上能够覆盖全部场景了,同时为了方便开发也提供了一些语法糖性质的辅助函数

浏览器端 API

css```

css 是一个 标签函数 ,这意味着你能够经过模版字符串 ```` 而非 () 来调用这个函数,标签函数求值结果会被 Babel 插件转换成一个独一无二的 class 命名

import { css } from 'linaria';

const flower = css` display: inline; color: violet; `;

// flower === flower__9o5awv –> with babel plugin
复制代码

任何在模版字符串写的 CSS 样式都会局限在相应的 class 命名下,包括媒体查询和动画。咱们能够这样声明动画:

import { css } from 'linaria';

const box = css` animation: rotate 1s linear infinite; @keyframes rotate { { from: 0deg; } { to: 360deg; } } `;
复制代码

cx(...classNames: Array<string | false | void | null | 0>) => string

cx() 会对传入的字符串进行拼接,但会忽略掉 Falsy 值,好比 ''nullundefined

import { css, cx } from 'linaria';

const cat = css` font-weight: bold; `;

const yarn = css` color: violet; `;

const fun = css` display: flex; `;

function App({ isPlaying }) {
  return <Playground className={cx(cat, yarn, isPlaying && fun)} />;
}
复制代码

cx() 这个函数看着很像一个流行库 classnames ,但仍是有点区别的, cx() 不处理对象

styled

一个用于快速建立 React 组件的辅助对象,它的使用形式很像 styled-components

styled 的使用方式和 css 很类似,除此以外,你能够在模版字符串中插入函数来获取组件的 props ,并动态的设置样式

import { styled } from 'linaria/react';
import colors from './colors.json';

const Container = styled.div` background-color: ${colors.background}; color: ${props => props.color}; width: ${100 / 3}%; border: 1px solid red; &:hover { border-color: blue; } `; 
复制代码

一样的,全部的样式规则也是局部化的。为了不重复的 CSS 样式代码,咱们能够这样去引用别的样式

const Title = styled.h1` font-size: 36px; `;

const Article = styled.article` font-size: 16px; /* this will evaluate to the selector that refers to `Title` */ ${Title} { margin-bottom: 24px; } `;
复制代码

而且,咱们能够经过 as 属性来指定实际渲染时的 html 标签是什么

// Here `Button` is defined as a `button` tag
const Button = styled.button` background-color: rebeccapurple; `;

// You can switch it to use an `a` tag with the `as` prop
<Button as="a" href="/get-started"> Click me </Button>;
复制代码

styled 也支持相似高阶组件形式的样式嵌套

const Button = styled.button` background-color: rebeccapurple; `;

// The background-color in FancyButton will take precedence
const FancyButton = styled(Button)` background-color: black; `;
复制代码

服务端 API (linaria/server)

collect(html: string, css: string) => string

在作 SSR 时咱们不只须要将相应的 HTML 代码进行返回,也须要将 须要的 样式代码返回,这就那些 关键的 的 CSS 代码,咱们能够经过利用 collect() 函数来抽离出关键的 CSS 代码

import { collect } from 'linaria/server';

const css = fs.readFileSync('./dist/styles.css', 'utf8');
const html = ReactDOMServer.renderToString(<App />);
const { critical, other } = collect(html, css);

// critical – returns critical CSS for given html
// other – returns the rest of styles
复制代码

collect() 会根据元素的 class 属性,将用到的 CSS 代码抽离出来,这样就能够跟随 html 一块儿返回

须要注意的被抽离出来的 css 代码选择器的顺序会变乱掉,这使得若是你的样式依赖选择器顺序的权重,可能就会出现意料的以外的错误,不过因为 Linaria 生成的 class 命名都是惟一的,因此通常不会出现这个问题,但与其余的库协做时须要注意到这点

警告

Linaria 是基于 CSS 变量的,大部分现代浏览器支持这个特性,可是对于 IE 11 以及如下,是不支持的,因此若是你须要支持 IE 11 ,也许 Linaria 不是你最好的选择

后续

linaria/lader 已经更名为 @linaria/webpack4-loader

相关文章
相关标签/搜索