likes
comments
collection
share

前端构建工具vite进阶系列(三) -- 静态资源与css模块化的处理

作者站长头像
站长
· 阅读数 8

前言

上一章前端构建工具vite进阶系列(二) -- vite的依赖预构建与配置文件相关处理讲解了vite的依赖预构建与配置文件相关处理,能够让你使用vite去开发项目的时候,更加得心应手,这一章我们将一起来探索一下vite对静态资源以及css模块化相关的处理吧。

vite是如何去识别文件的

我们来回忆一下在webpack中,是怎么去读取文件的呢?是不是通过loader,通过各种loader去读取各种文件,比如css-loaderstyle-loader处理css文件。因为webpack他所遵循的是CommonJS规范,并且他只能够识别js文件,所以需要各种loader来做代码转换,loader的本质就是一个函数,最后返回的是一段可执行的JavaScript代码就可以了,但是vite呢?我们来研究一下。

  • 新建index.css文件,在index.js中引入import 'index.css'
html, body {
  width: 100%;
  height: 100%;
  background-color: red;
}

效果: 前端构建工具vite进阶系列(三) -- 静态资源与css模块化的处理 我们发现了,在head标签里面新建了一个style标签,.css文件里里面的内容,都被写到了标签里面。 大概我们能猜到,vitecss文件的处理,也是通过创建一个style标签,在页面引入样式文件这样子处理的。

但是vite不同的是会把css文件转化为js脚本文件,设置Content-Type:"text/javascript"来告知,浏览器应该以JavaScript的方式来解析css脚本。这样做的好处有三点:

  • 用于热更新
  • 用于css模块化。
  • 去除了第三方工具对css的处理,提高了编译性能。

所以vite应该是在本地开发服务器中,把对资源文件的处理,转化为浏览器对资源文件的解析,比如:

  • 构建本地开发服务器
  • 设置相应头:Content-type:"text/javascript"「.vue文件」
  • 让浏览器以JavaScript的方式来解析.vue文件。

vite对css模块化的处理

我们知道webpackcss文件的热更新依赖于loader自身的热更新,但是vite里面没有实现loader机制,这一点我将会在后期热更新那一章讲到。那么这里主要来讲一下vitecss模块化处理吧。

  • 为什么会出现css模块化?
    • 其根本原因就是为了解决协同开发,类名id等冲突引起样式覆盖的问题。
  • 如何解决?
    • 比如css模块化,模块化策略表示,每一个css文件为单独的一个模块,具有单独模块的作用域,这样能够保证我使用的类名的唯一性,独立性。
    • 我们来依次建立两个文件来证明一下,kk.module.cssbb.mocule.css
.l{ // 两个文件依次写上这样的类名
  font-size: 100px;
  color: red
}

// kk.js
import kkModuleCss from './kk.module.css'
console.log(kkModuleCss);
const ele = document.createElement('div');
ele.className = kkModuleCss.l;
ele.innerHTML = '我是kk';
document.body.append(ele)

// bb.js
import bbModuleCss from './bb.module.css'
console.log(bbModuleCss);
const ele = document.createElement('div');
ele.className = bbModuleCss.l;
ele.innerHTML = '我是bb';
document.body.append(ele)

效果:

前端构建工具vite进阶系列(三) -- 静态资源与css模块化的处理

  • 我们发现了我们写的样式会被处理成key:value的形式,以类名作为键名,处理后的类名作为键值。

前端构建工具vite进阶系列(三) -- 静态资源与css模块化的处理

  • 真实的类名会被替换成处理后的键值。

前端构建工具vite进阶系列(三) -- 静态资源与css模块化的处理

  • 我们发现在style中也会出现替换后的内容。

所以cssModule的原理也就是很简单了。

  • module.css是一种约定,表示要开启css模块化。
  • 类名替换,比如'.l'会被替换成'_l_1jq3b_1'
  • 创建映射对象,比如{l: "_l_1jq3b_1"}
  • 将替换后的样式内容创建style标签,插入到head中。
  • vitemodule.css文件内容删除,把新内容替换成js脚本,用于热更新。

less或者scss的模块化处理

lessscss模块化处理,跟css一样,都会创建一个映射对象,创建style,插入内容,插入head,这里不再复述。

lessscss去编译文件,就是把less或者scss写法,变成css写法。

用户配置大于默认,vite中应该怎么去做

import { defineConfig } from 'vite';
import postcssPresetEnv from 'postcss-preset-env';
export default defineConfig({
  ...
  envPrefix:'ENV_', // 配置环境变量前缀
  css:{
    modules:{
      localsConvention:"camelCase", // 展示处理之后的key,
      scopeBehaviour:'local', // 配置的模块化还是全局还是局部
      generateScopedName:'[name]_[local]_[hash:5]' // 类名生成规则
      hashPrefix:'xxxxx', // 根据哈希加老类名生成文件新类名
      globalModulePaths:[] // 不想参与css格式化的文件路径
    },
    preprocessorOptions:{
      less:{
        // 在这里写的参数为key:value形式,会作为命令行参数,传递到预处理器编译的时候执行
        // 比如 npx less --math="strict" index.module.css
        // 就需要在这里配置
        math:"strict",
        globalVars:{
          mainColor:"red" //全局变量
        } 
        // 更多请查看 https://lesscss.org/usage/#less-options-include-paths
      },
      scss:{  
        // 命令行追加参数
        ...
      }
    },
    devSourcemap:true, // 开启css文件索引,以便于报错的时候能够找到源码位置
    postcss:{
      //  postcssPresetEnv插件可以帮我们实现自动补全,语法降级等功能
      plugins:[postcssPresetEnv()]
      // 关于postcss文章请查看https://github.com/postcss/postcss/blob/main/docs/README-cn.md
    } 
  }
})

关于css模块化,就说这么多,那么vite是怎么去处理静态资源的呢?来一起探索下。

静态资源处理

webpack中,比如图片视频,需要通过image-loaderfile-loader等来加载处理,但是vite对静态资源是开箱即用的,除了svg资源,那么我们来研究一下他是怎么开箱即用的呢。

  • 关于图片,我们只需要像往常一样写就可以了。比如我们生成一个img标签:
import indexImg from './assets/index.jpg'
const loadImg = ()=>{
  const img = document.createElement('img');
  img.src = indexImg
  img.setAttribute('width','80%')
  document.body.append(img)
}

export default loadImg // 在index.js中引入执行

效果:

前端构建工具vite进阶系列(三) -- 静态资源与css模块化的处理

  • 关于json文件的处理
// index.json
{
  "name":"YiXIZHISHI",
  "age":18
}

// index.js
import indexJson from './index.json';
console.log(indexJson)
for(let i in indexJson){
  const e = document.createElement('p');
  e.innerHTML = `${i}:${indexJson[i]}`;
  document.body.append(e)
}

前端构建工具vite进阶系列(三) -- 静态资源与css模块化的处理

效果:

前端构建工具vite进阶系列(三) -- 静态资源与css模块化的处理

这样跟一般的导入的话区别是这样导入是一个对象,不用vite的话,他将是一个字符串。但是vite还支持按需导入属性,有利于tree-shaking,比如:

import {name,age} from './index.json';
console.log(`name:${name}, age:${age}`);

前端构建工具vite进阶系列(三) -- 静态资源与css模块化的处理

如果不懂tree-shaking的话,请移步 >>> 重学webpack系列(九) -- webpack的高级特性与前端性能优化

  • 关于svg文件的处理

vitesvg也是开箱即用的,但是svgimg不同,这得追溯一下我们为什么要使用svg?为了方便修改样式对不对?首先svg可以按照普通图片那样加载没问题,也可以按照svg方式加载。

  • 按照图片加载:import indexSvg from '@assets/index.svg?url',拿到的是相对路径。

前端构建工具vite进阶系列(三) -- 静态资源与css模块化的处理

  • 按照svg加载:import indexSvgRaw from '@assets/index.svg?raw',拿到的是svg字符串。

前端构建工具vite进阶系列(三) -- 静态资源与css模块化的处理 变成了svg,我们就可以去改变颜色等属性了。

路径别名配置

  • 场景:在解决相对路径造成的代码不雅观的同时,能够防止路径出错,让你不再../../。vite提供了路径别名来解决这样的问题。
  • 实现:在vite.base.config.js里面配置resolve属性。
export default defineConfig({
  ...
  resolve:{
    alias:{
      "@":path.resolve(__dirname, './src'),
      "@assets":path.resolve(__dirname, './src/assets'),
      // 指定@符的路径,比如 import xx from '@component/index.vue'
      "@component":path.resolve(__dirname, './src/component')
    }
  }
})

vite在生产环境对静态资源的处理

首先我们来执行一下npm run build,这样在项目根目录生成了一个dist文件,长这样:

前端构建工具vite进阶系列(三) -- 静态资源与css模块化的处理

webpack对比一下,文件名,文件内容都给我们进行了处理,比如文件名的替换(hash:8),内容的压缩,tree-shaking,我们看一下index.html文件。

前端构建工具vite进阶系列(三) -- 静态资源与css模块化的处理

关于资源的路径处理成了绝对路径,以上种种vite在生产环境是不会去处理的而是交给rollup去全权处理,所以我们需要告诉rollup的一些配置规则。

build的配置

export default defineConfig({
  ...
  build:{ // 生产环境的配置
    rollupOptions:{ // 给rollup的配置
      output:{ // 输出
        assetFileNames:"[name]-[hash].[ext]"
      }
    },
    assetsInlineLimit:"1024", // 大于1024kb资源,打包成base64,
    outDir:"build", // 输出目录
    assetsDir:"static" // 静态目录
    // 更多请查看https://rollupjs.org/configuration-options/#output-assetfilenames
    // vite配置文档https://cn.vitejs.dev/config/build-options.html#build-rollupoptions
}
})

总结

本章节讲述了vitecss模块化静态资源的一些处理,基本上都是开箱即用的,在开发环境只是这样,在生产环境vite并没有做更多的,只是全权交给rollup去做,这到底是提升还是提升呢?rollup处理与webpack的处理又有什么区别呢?下一章 >>> 前端构建工具vite进阶系列(四) -- 插件系统让vite变得更强大