一、Vite css处理(模块module、sass less预处理、获取CSS代码字符串)

文章详细介绍了Vite框架如何处理CSS,包括自动导入、@import内联、PostCSS应用、CSS模块化的使用和优势,以及CSS预处理器如Sass、Less和Stylus的支持。此外,还讨论了禁用CSS注入和配置CSS源地图的方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、参考

  1. vite 官网 CSS处理介绍

二、CSS 自动导入

  1. 导入 .css 文件将会把内容插入到 <style> 标签中
  2. 同时也带有 HMR 支持
  3. 也能够以字符串的形式检索处理后的、作为其模块默认导出的 CSS。

三、@import 内联和变基

Vite 通过 postcss-import 预配置支持了 CSS @import 内联,Vite 的路径别名也遵从 CSS @import。换句话说,所有 CSS url() 引用,即使导入的文件在不同的目录中,也总是自动变基,以确保正确性

Sass 和 Less 文件也支持 @import 别名和 URL 变基(具体请参阅 CSS Pre-processors)。

四、PostCSS

如果项目包含有效的 PostCSS 配置 (任何受 postcss-load-config 支持的格式,例如 postcss.config.js),它将会自动应用于所有已导入的 CSS。

请注意,CSS 最小化压缩将在 PostCSS 之后运行,并会使用 build.cssTarget 选项

五、CSS 模块化(.module.css)

任何以 .module.css 为后缀名的 CSS 文件都被认为是一个 CSS modules 文件

  1. 定义 example.module.css
/* example.module.css */
.red {
  color: red;
}
  1. 引入module css
import classes from './example.module.css'
document.getElementById('foo').className = classes.red

5.1 自定义 CSS module 导出命名规则

如果 css.modules.localsConvention 设置开启了 camelCase 格式变量名转换(例如 localsConvention: ‘camelCaseOnly’),你还可以使用按名导入。

// .apply-color -> applyColor
import { applyColor } from './example.module.css'
document.getElementById('foo').className = applyColor

5.2 为什么要有 CSS module

一句话概括:防止命名冲突

场景说明:

  • 一个组件最外层的元素类名一般取名 : wrapper
  • 一个组件最底层的元素类名我们一般取名: .footer

你取了footer这个名字, 别人因为没有看过你这个组件的源代码, 也可能去取名footer这个类名

最终可能会导致样式被覆盖(因为类名重复), 这就是我们在协同开发的时候很容易出现的问题

cssmodule就是来解决这个问题的

大概说一下原理:

  1. module.css (module是一种约定, 表示需要开启css模块化)
  2. 他会将你的所有类名进行一定规则的替换(将footer 替换成 _footer_i22st_1)
  3. 同时创建一个映射对象{ footer: “_footer_i22st_1” }
  4. 将替换过后的内容塞进style标签里然后放入到head标签中 (能够读到index.html的文件内容)
  5. 将componentA.module.css内容进行全部抹除, 替换成JS脚本
  6. 将创建的映射对象在脚本中进行默认导出

5.3 CSS、SCSS module 案例

<template>
  <pre class>
    # 在vite中处理css

    vite天生就支持对css文件的直接处理

    1. vite在读取到main.js中引用到了Index.css
    2. 直接去使用fs模块去读取index.css中文件内容
    3. 直接创建一个style标签, 将index.css中文件内容直接copy进style标签里
    4. 将style标签插入到index.html的head中
    5. 将该css文件中的内容直接替换为js脚本(方便热更新或者css模块化), 同时设置Content-Type为js 从而让浏览器以JS脚本的形式来执行该css后缀的文件
  </pre>
</template>

<script>
import cssModule from  "./css.module.css";
import scssModule from "./sass.module.scss";

console.log("cssModule", cssModule);
debugger
console.log("scssModule", scssModule);

export  default  {
  mounted() {
    const div = document.createElement("div");
    div.innerHTML = `
      cssModule.footerContent
    `
    document.body.appendChild(div);
    div.className = cssModule['footer-content'];
  }
}
</script>

css.module.css

/*:root {*/
/*  --globalColor: red;*/
/*}*/
.footer {
  width: 200px;
  height: 200px;
  background-color: beige;
}

.footer-content {
  width: 200px;
  height: 200px;
  /*
  --globalColor 变量在 vite.config.js 中已经注入了全局文件
  additionalData: `@import "./src/assets/css/global.scss";`,
  */
  background-color: var(--globalColor);
}

sass.module.scss

.content {
  width: 800px;
  .main {
      background: green;
      padding: (100px / 2);
      margin: 100px / 2;
      background-color: red;
      filter: blur(100px);
      // 响应式布局, 左侧一个菜单栏 宽度自适应根据屏幕 30%
      // 400px ---> 父容器
      // 120px  200 * 0.3 -> 60  100px 240px 200px; preset-env会帮助我们做语法降级  vite内部会有一个主流浏览器的支持表
      width: clamp(100px, 30%, 200px);
      user-select: none; // 他在其他浏览器上不支持
  }
}
.main{
  color: blue;
}

六、CSS 预处理器 (sass less stylus)

Vite 也同时提供了对 .scss, .sass, .less, .styl 和 .stylus 文件的内置支持。没有必要为它们安装特定的 Vite 插件,但必须安装相应的预处理器依赖:(开箱即用)

# .scss and .sass
npm add -D sass

# .less
npm add -D less

# .styl and .stylus
npm add -D stylus
  1. 如果使用的是单文件组件,可以通过 <style lang=“sass”>(或其他预处理器)自动开启

  2. Vite 为 Sass 和 Less 改进了 @import 解析,以保证 Vite 别名也能被使用。另外,url() 中的相对路径引用的,与根文件不同目录中的 Sass/Less 文件会自动变基以保证正确性。

七、禁用 CSS 注入页面

自动注入 CSS 内容的行为可以通过 ?inline 参数来关闭。在关闭时,被处理过的 CSS 字符串将会作为该模块的默认导出,但样式并没有被注入到页面中。

import './foo.css' // 样式将会注入页面
import otherStyles from './bar.css?inline' // 样式不会注入页面

八、css sourcemap 配置

开发中,为了能快速定位到 CSS的源码位置,可以开启 CSS 的 sourcemap, 修改vite.config.js配置

import { fileURLToPath, URL } from 'node:url';

import { defineConfig, loadEnv } from 'vite';
import vue from '@vitejs/plugin-vue';
import basicSsl from '@vitejs/plugin-basic-ssl';
// https://github.com/antfu/unplugin-auto-import
import AutoImport from 'unplugin-auto-import/vite';
/* 
  https://github.com/antfu/unplugin-vue-components
  On-demand components auto importing for Vue.(自动将组件引入到Vue中,不用明确代码引入了)
*/
import Components from 'unplugin-vue-components/vite';
// https://www.npmjs.com/package/vite-plugin-pages
import Pages from 'vite-plugin-pages';
import Unocss from 'unocss/vite';
/* 
  https://www.npmjs.com/package/vite-plugin-vue-devtools
  提高Vue 的调试工具
*/
import VueDevTools from 'vite-plugin-vue-devtools';
/* 
  https://github.com/xxholly32/vite-plugin-custom-attr#readme
  Set default values for tags.(设置标签的默认值)
*/
import CustomProps from 'vite-plugin-custom-attr';

import { ElementPlusResolver } from 'unplugin-vue-components/resolvers';

// https://vitejs.dev/config/
// export default defineConfig({
//   plugins: [vue()],
// })

export default defineConfig(function (options) {
  // console.log(arguments) // 只有一个参数值
  // console.log(options) //{ mode: 'development', command: 'serve', ssrBuild: false }
  const { mode, command, ssrBuild } = options;
  const env = loadEnv(mode, process.cwd(), '');

  // process.env = { ...process.env, ...loadEnv(mode, process.cwd()) };
  // const publicPath = process.env.VITE_BASE_URL;

  return {
    /**
     * 在生产中服务时的基本公共路径。
     * @default '/'
     */
    // base: '/huangbiao/', // 默认是 '/'
    // publicDir: 'public',
    // cacheDir: 'node_modules/.vite',
    // envDir: 'root', // 用于加载 .env 文件的目录。可以是一个绝对路径,也可以是相对于项目根的路径。
    // envPrefix: 'VITE_', // 以 envPrefix 开头的环境变量会通过 import.meta.env 暴露在你的客户端源码中
    resolve: {
      /* 路径使用别名 */
      alias: {
        '@': fileURLToPath(new URL('./src', import.meta.url)),
      },
      /* 
      引入文件的后缀名称,可以省略
      如果出现同名,按照数组加载的优先顺序
      */
      extensions: ['.mjs', '.js', '.ts', '.jsx', '.tsx', '.json', '.vue'],
    },
    // 服务配置
    server: {
      host: '0.0.0.0',
      port: 3333, // 端口号
      open: true, // 自动在浏览器打开
      // https: true,// 是否开启 https
      // http2: true,// 建议使用 http2, 是否开启 https
      fs: {
        // 可以为项目根目录的上一级提供服务
        allow: ['..'],
      },
      proxy: {
        '^/mockapi': {
          target: 'http://10.32.38.100:3000/',
          changeOrigin: true,
        },
        '^/ctm05zjaefs/': {
          target: 'http://localhost:39696',
          changeOrigin: true,
        },
        // 带选项写法:http://localhost:5173/api/bar -> http://jsonplaceholder.typicode.com/bar
        '/api': {
          target: 'http://jsonplaceholder.typicode.com',
          changeOrigin: true,
          rewrite: (path) => path.replace(/^\/api/, ''),
        },
      },
    },
    // css 处理
    css: {
      preprocessorOptions: {
        scss: {
          /* .scss全局预定义变量,引入多个文件 以;(分号分割)*/
          additionalData: `@import "./src/assets/css/global.scss";`,
        },
      },
      // 可以查看 CSS 的源码
      devSourcemap: true,
    },
    //  生产环境
    build: {
      //指定输出路径
      assetsDir: './static',
      // 指定输出文件路径
      outDir: 'dist',
      sourcemap: true, // 构建后是否生成 source map 文件。如果为 true,将会创建一个独立的 source map 文件。
      // 代码压缩配置
      terserOptions: {
        // 生产环境移除console
        compress: {
          drop_console: true,
          drop_debugger: true,
        },
      },
    },
    plugins: [
      VueDevTools(),
      vue(),
      basicSsl(),
      // Pages({
      //   extensions: ['vue', 'md'],
      //   dirs: [
      //     // basic
      //     { dir: 'src/pages/**', baseRoute: '.' },
      //     // // features dir for pages
      //     // { dir: 'src/features/**/pages', baseRoute: 'features' },
      //     // // with custom file pattern
      //     // { dir: 'src/admin/pages', baseRoute: 'admin', filePattern: '**/*.page.*' },
      //   ],
      // }),
      // Pages({
      //   extensions: ['vue', 'md'],
      // }),

      // https://github.com/JohnCampionJr/vite-plugin-vue-layouts
      Pages({
        dirs: 'src/pages',
        exclude: ['**/comp/*.vue'],
        extensions: ['vue'], // ['vue', 'ts', 'js'],
        extendRoute(route, parent) {
          console.log('route, parent', route, parent);
          /* 
          打印的内容如下
          route, parent {
            name: 'Welcome',
            path: '/welcome',
            component: '/src/pages/Welcome.vue',
            customBlock: undefined,
            props: true
          }
          */
          if (route.path === '/') {
            //给默认路由加一个redirect,默认为index.vue
            return {
              ...route,
              redirect: 'page1',
              meta: {
                auth: true,
              },
            };
          } else {
            return route;
          }
        },
      }),
      Unocss({
        // mode: 'shadow-dom',
        shortcuts: [ // 自定义组合样式,即 样式 cool-blue 代表`bg-blue-500 text-white` 的组合
          { 'cool-blue': 'bg-blue-500 text-white' }, 
          { 'cool-green': 'bg-green-500 text-black' }
        ],
      }),
      AutoImport({
        imports: [
          // 需要自动导入的插件,自定义导入的API
          'vue',
          'vue-router',
          'pinia',
        ],
        dts: 'src/types/auto-import.d.ts', // 指明 .d.ts 文件的位置和文件名
        resolvers: [ElementPlusResolver()],
      }),
      Components({
        resolvers: [ElementPlusResolver()],
      }),
      /* 
      // Set default values for tags.
      CustomProps({
        tags: {
          "h1" : {
            style: "font-size: 2rem;"
          },
          "el-button" : {
            type: "success",
            plain: true,
          }, 
        }
      })
      
      <template>
        <h1>change style</h1>
        <el-button>no type</el-button>
        <el-button type="primary">type primay</el-button>
      </template>
      转为
      <template>
        <h1 style="font-size: 2rem;">change style</h1>
        <el-button type="success" plain>no type</el-button>
        <el-button type="primary" plain>type primay</el-button>
      </template>
      */
    ],
  };
});

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值