shadow-cljs hooks解决css延迟加载导致页面邹形问题


在shadow-cljs项目中在最后编译的时候将所有的css写入到同一个css文件,在index.html里引用一次
简书地址

现状

我司使用shadow-cljs编译clojure script代码,考虑到大家都编辑一个css文件的话,冲突比较严重,并且代码也没有模块化,所以每个模块有自己的样式文件,目前都是在自己业务模块或者component里引入自己的css文件,结构基本是这样的:

(defn login-main []
  [:div
   [:link {:rel "stylesheet" :href "/css/login.css" }]
   [:div "登录组件样式"]])

分析

好处:

  • 代码模块化,业务组件与样式文件一一对应,便于开发团队维护代码。
  • 不用在入口文件里引入一堆css文件,减少commit冲突。
  • 减少资源浪费,只有页面加载处才加载css,系统不加载无用的资源文件。

痛点

  • 一条足以致命,那就是产品体验不好,css加载延迟,几乎每个页面都出现如下一个瞬间。
    定制

订单

是的,你看的没有错,浏览确实没有加载完,网络好的话这个页面瞬间就过了,但是万一碰上不好的,那就尴尬了,并且有时候还出现过最后样式就没有出来的情况。

思路

不能让组件渲染时才去加载css,所以link这个方案pass掉。
那么只能将css在index.html里引入,不管用不用得到,都先加载出来,让浏览器先缓存下来备用。

方案

  1. 代码层面,还是分模块,业务模块和css模块一一对应。
  2. 在系统运行时将所有自定义的css合并到一个样式文件,在index.html中引用。

其中第二点,在部署时可以使用脚本处理,但是在本地开发时就比较不便,不过好在shadow-cljs提供了build-hooks神器。加载配置中,让我们代码编译时做了这个合并css文件的工作。具体的配置是在根目录的shadow-cljs.edn中加入如下配置:

{...
 :builds
 {:app {:target ...
           :build-hooks [(utils.hooks/watch-resource)]
        ...}}}}

这个hooks的全部代码如下:

(ns utils.hooks
  (:require [clojure.java.shell :as shell]))

(defn watch-resource
  {:shadow.build/stage :flush}
  [build-state & args]
  (future
    (prn "configure completed")
    (let [dir (System/getProperty "user.dir")]
      (clojure.java.shell/with-sh-dir (str dir "/src/cljs/utils")
        (clojure.java.shell/sh "mergecss.sh" (str dir "/resources/public/css") (str dir "/resources/public/css/style.css")))))
  build-state)

如果参考,shadow-cljs官方对build-hooks的说明,不难理解。解释两个东西

  1. (System/getProperty "user.dir"),这个是获取当前项目的工作目录,也就是项目跟目录,注意不是git根目录。有了这个,想获取什么目录或者文件,手到擒来。
  2. hooks里还调用了一个脚本文件mergecss.sh,这个文件的作用是把开发团队自己写的所有css的内容copy到同一个style.css文件中去,第一个参数是要处理的目录(会向下遍历目录),第二个参数是要写入的目标文件,附上脚本代码:
#!/bin/bash
#获取第一个参数,目标目录
srcpath=$1
#第二个参数,输出文件
target_file=$2
#先删除原目标文件
rm -rf ${target_file};

function writeByDir(){
    for file in $1/*
      do
        if test -f $file; then
      #_echo $(basename $file)
          if [[ $(basename $file) = "antd.min.css" ]] || [[ $(basename $file) = "slick.min.css" ]] || [[ $(basename $file) = "slick-theme.min.css" ]]; then
            echo "uncopy css :"$(basename $file)
          else
            cat "$file" >> "${target_file}"
              echo "" >> "${target_file}"
          fi
        else
          writeByDir $file
       fi
    done
}
writeByDir ${srcpath}

此处由衷感慨下,repl真好用,clojure.java.shell这个库的使用,及最后钩子内容好不好使全靠这个工具调试。否则的话,修改hooks就要频繁启动shadow-cljs项目,并且如果hooks函数有错误的没有任何报错信息,只是钩子函数不执行。

这样本地开发的css文件也能及时写入到style.css里,在index.html引用这个文件,妥了。

 <link rel="stylesheet" href="/css/style.css" type="text/css" >

一顿操作猛如虎,定睛一看原地杵

以上操作还是不够完美,

  1. 加上hook后,在本地修改自己的css后,钩子不执行,改的代码不会自己写入目标文件。
    方案一:在9630端口上手动触发compile,强制flush。
    方案二:开发过程中可以在cljs文件里link自己的样式文件,达到高效开发,link段落不用提交。

  2. 为了开发时不冲突,更新gitignore,生成的css不用commit到项目里,release环境在持续集成时执行该hook的操作,调用服务器的脚本文件。

  3. 样式文件没有hash,纯copy,只为解决css加载延迟导致的页面丑陋。

参考

  1. https://blog.csdn.net/sirfenygu/article/details/47025083

评论
  目录