feng xiaohan
vite 的 css 预处理

css 的预处理器主要是用于配置 css 的预处理语言的语法,比如 less,sass,stylus 等。

preprocessorOptions

通过该项来配置预处理器,该项其实会调用预处理语言(less,sass 等)的内置编译器(lessc、sassc 等)。

补充:
当没有使用任何构建工具的时候如何将预处理语言文件转换为 css 文件?(以 less 为例子)

  1. 安装 less
npm install less -g

注意:全局安装 less 就可以获得 lessc 编译工具,可直接通过 lessc 命令来编译 less 文件。如果本地安装则可以通过npx lessc来编译。

  1. 编译 less 文件
lessc src/index.less src/index.css

指定参数:

lessc src/index.less src/index.css -m=always

这样就会在指定位置生成一个编译后的 css 文件。

vite 中配置预处理器

  • 安装好对应的预处理语言依赖后,在 vite 中配置:
export default defineConfig({
  css: {
    preprocessorOptions: {
      less: {
        math: "always",
      },
    },
  },
});
  • .vue文件中直接使用 less:
<style scoped lang="less">
.test {
  height: 10 * 2vh;
  width: 1920 / 108vh;
  border: 1px solid rgb(77, 73, 73);
  color: yellowgreen;
}
</style>

设置全局变量

在 less 中定义了某个全局变量后,可以直接在想使用的地方引入这个文件然后使用:

// global.less
@textColor: rgb(43, 153, 226);
<style scoped lang="less">
@import "./assets/global.less"; // 注意要加上 ;

.test {
  height: 10 * 2vh;
  width: 1920 / 108vh;
  border: 1px solid rgb(77, 73, 73);
  color: @textColor;
}
</style>

如果不想直接引入这个文件,可以在 vite.config.js 的 preprocessOptions 定义全局变量:

export default defineConfig({
  css: {
    preprocessorOptions: {
      less: {
        math: "always",
        globalVars: {
          textColor: "rgb(43, 153, 226)",
        },
      },
    },
  },
});
<style scoped lang="less">
.test {
  height: 10 * 2vh;
  width: 1920 / 108vh;
  border: 1px solid rgb(77, 73, 73);
  color: @textColor;
}
</style>
vite 中使用 css

vite 直接就支持 css 文件,不需要别的操作。

原理

当 vite 在读取到 main.js 中引用的 index.css 时:

  1. 首先使用 node 的 fs 模块获取 css 文件的内容。
    浏览器行为:
    在浏览器获取 css 文件时,vite 会将其原本的 css 内容替换为 js 脚本,并让浏览器以执行 js 文件的形式来执行 css 后缀的文件。该脚本的内容执行内容如下:

    细节:获取到内容后,会见文件在本地的绝对路径作为 id 和内容一起传给 vite 处理 css 的函数 ——— updateStyle(id, content),该函数会创建一个 style 标签,并将 type 设置为 ‘text/css’,将其插入到 head 中(后面的内容)。

  2. 创建一个 style 标签,将 css 文件中的内容直接复制到里面。

  3. style 标签插入到 head 中。

CSS Module

问题:如果两个 css 文件定义了同一个类名,样式会被覆盖。
解决:使用 CSS Module(postcss),将每一个 class 名变为独一无二的,不会与其他选择器重名。

将 css 文件的后缀改为 .module.css 即可。vite 会利用 postcss 自动将后缀名为 .module.css 的文件转换为 CSS Module,在类名前添加上独一无二的 hash 值。

使用:

import index from './css/test2.module.css'
...
div.className = index.test

引入的值可以是认为原始 css 类名 和修改后独一无二的 css 类名 的映射表。

配置 CSS Module

可以通过 vite.config.js 配置 CSS Module,其实也是将参数传递给 postcss。

export default defineConfig({
  // css module
  css: {
    // (postcss)
    modules: {
      scopeBehaviour: "local", // 是否开启模块化 local(*) | global
      localsConvention: "camelCaseOnly", // 返回键值对格式 camelCaseOnly(*) | camelCase | dashes | dashesOnly | null
      // 修改生成的 hash 名称
      // generateScopedName: '[name]-[local]-[hash:base64:5]', // string

      // hashPrefix: 'prefix-', // 生成的 hash 名前缀 ?
      globalModulePaths: ["src/module/cssNoUseModule/*.*.css"], // 不想使用css module的路径 RegExp[]
    },
  },
});

CSS sourcemap

默认情况下,我们在开发环境调试定位元素时,指向的是 head 里的 style 标签。在开发环境下,vite 可以配置 css 的 sourcemap 属性devSourcemap,可以让我们找到处理后 css 类所对应的源文件。

export default defineConfig({
  css: {
    // modules: {
    //   // ...
    // },
    // preprocessorOptions: {
    //   // ...
    // }
    devSourcemap: true,
  },
});
vite 中使用 ts

vite 中默认支持 ts,不需要额外配置。

vite-plugin-checker

它是一个可以在工作线程中运行 TypeScript、 VLS、 vue-tsc、 ESLint 的插件,可以根据配置,阻断编译,在控制台及浏览器展示报错信息。

基本使用

  • 安装:
npm i vite-plugin-checker -D
  • vite config 中配置:
// vite.config.js
import checker from "vite-plugin-checker";
import { defineConfig } from "vite";
export default defineConfig({
  plugins: [
    checker({
      typescript: true,
    }),
  ],
});
  • 生成 tsconfig.json 配置文件:
npm tsc --init
  • 指定需要进行 ts check 的文件:
// tsconfig.json
{
  "include": ["src/*.ts"]
}

注意:以上配置不影响正常打包,只是在运行的时候出报错。

打包前进行 ts 检查

vite 本身仅执行 .ts 文件的转译工作,并不执行任何类型检查。所以我们可以借助 ts 本身的类型检查功能,在打包前对代码进行类型检查:

"scripts": {
  "build": "tsc --noEmit && vite build"
}

对于 .vue 文件,可以安装 vue-tsc 然后运行 vue-tsc –noEmit 来对 *.vue 文件做类型检查。

原理:
tsc是 TypeScript 的编译器,它负责将 TypeScript 代码转换为 JavaScript 代码。tsc 会读取配置文件获取参数值,--noEmit 的作用是只进行检查,不进行编译输出。如果我们的代码无错,会直接退出,执行下一条语句,否则报错。

ts 智能提示

默认情况下,vite 在 vite/client.d.ts 中为import.meta.env提供了类型定义。但是如果我们设置了一些自定义的环境变量(.env[mode])是没有提示的,如果也想在浏览器环境代码中有智能提示,可以在env.d.ts中定义:

/// <reference types="vite/client" />

interface ImportMetaEnv {
  readonly VITE_APP_TITLE: string;
}
interface ImportMeta {
  readonly env: ImportMetaEnv;
}

注意:定义的文件名和文件位置其实不重要,重要的是需要在 tsconfig.json 配置将其文件进行 ts 检查:

{
  "include": ["src/*.ts", "env.d.ts"]
}
二进制的一些小技巧

^

  • 找到汉明距离(两个数对应二进制位不同的数目);

    汉明距离是使用在数据传输差错控制编码里。

(num - 1) & num

  • 用于清除 num 最右边的 1;

Brian Kernighan(布莱恩 柯林汉) 算法是一种用于统计一个二进制数中 1 的个数的算法,通过不断将数字中的最低位的 1 变成 0 的方式统计二进制数中的 1 的个数。

num & 1

  • 用于判断 num 的二进制表示中最右边的位是否为 1;

num >> 1

  • 用于将 num 的二进制表示向右移动一位;

(1 << n) - 1

  • 生成一个长度为 n 并且全为 1 的二进制数;

    用于使用异或快速将一个数进行取反, 如果直接取反一个数的话得到的是负数。

(1 << k) & n

k 从 30 开始递减。

  • 用于比较一个二进制数的高位是否为 1。

快速构造小于 num 的 2 的幂次方的数组

const num = 15; // 最多为 2^3,也就是 8

let x = num;
const ans = [];
while (x > 0) {
  ans.push(lowbit(x));
  x -= lowbit(x);
}

function lowbit(x) {
  return x & -x;
}

异或前缀和

一个子数组的异或和(i, j)= 异或前缀和(j + 1)^ 异或前缀和(i)

异或前缀和数组比原数组长度多 1,多了一个 0.

CSS Modules

CSS 模块化,主要是包括两个方面:局部作用域和模块依赖。

局部作用域

保证某个组件的样式不会影响到其他组件,其原理就是使用一个独一无二的 class 名,不会与其他选择器重名。

为什么不使用 id 来确定:样式复用,样式覆盖问题,语义化。

CSS Modules 提供各种插件,支持不同的构建工具。

全局作用域

CSS Modules 允许使用:global(.className)的语法,声明一个全局规则。凡是这样声明的 class,都不会被编译成哈希字符串。

局部作用域

  • 显示局部作用域::local(.className)
  • (隐式)局部作用域:.className(直接写)

定制哈希类名

对于插件 css-loader 来说,默认的哈希算法是[hash:base64],这会将.title 编译成.\_3zyde4l1yATCOkgn-DBWEL这样的字符串。

CSS 组合

在 CSS Module 中可以使用composes继承别的 class 的样式:

.className {
  background-color: blue;
}

.title {
  composes: className;
  color: red;
}

引入其他 CSS 模块样式

选择器也可以继承其他 CSS 文件里面的规则。

.className {
  background-color: blue;
}
.title {
  composes: className from "./another.css";
  color: red;
}

导出变量

CSS Modules 支持使用变量,不过需要安装 PostCSS 和 postcss-modules-values 插件。

npm install --save postcss-loader postcss-modules-values
  • 例如在 webpack 中引入 postcss-loader
var path = require("path");
var webpack = require("webpack");
var ExtractTextPlugin = require("extract-text-webpack-plugin");
var values = require("postcss-modules-values");

module.exports = {
  entry: ["./src/index"],
  output: {
    filename: "bundle.js",
    path: path.join(__dirname, "public"),
    publicPath: "/public/",
  },
  module: {
    loaders: [
      { test: /\.js$/, loader: "babel-loader", exclude: /node_modules/ },
      {
        test: /\.css$/,
        loader: ExtractTextPlugin.extract(
          "style-loader",
          "css-loader?modules&importLoaders=1&localIdentName=[name]__[local]___[hash:base64:5]!postcss-loader"
        ),
      },
    ],
  },
  postcss: [values],
  plugins: [new ExtractTextPlugin("style.css", { allChunks: true })],
};
  • 设置变量

colors.css

@value blue: #0c77f8;
@value red: #ff0000;
@value green: #aaf200;
  • 引入 css 文件中
/* import your colors... */
@value colors: "./colors.css";
@value blue, red, green from colors;

.button {
  color: blue;
  display: inline-block;
}
ES6 特性

let 和 const

箭头函数

模板字符串

解构赋值

const obj = { a: 1, b: 2 };
const { a, b } = obj;
console.log(a, b); // 1, 2

const arr = [1, 2, 3];
const [x, y, z] = arr;
console.log(x, y, z); // 1, 2, 3

默认参数

function foo(a, b = 2, c = 3) {}

剩余(rest)参数

function sum(...nums) {
  return nums.reduce((a, b) => a + b);
}
sum(1, 2, 3, 4, 5);

扩展运算符

const arr1 = [1, 2, 3];
const arr2 = [...arr, 4, 5];

const obj1 = { a: 1, b: 2 };
const obj2 = { ...obj, c: 3 };

扩展对象字面量

对象属性简写

const name = "zhangsan";
const age = 18;
const preson = { name, age };

对象方法简写

const person = {
  name: "zhangsan",
  greet() {
    console.log("Hello!");
  },
};

for…of 循环

模块化(import/export)

// foo.js
export const foo = () => {};

// main.js
import { foo } from "./foo";

默认导出

// foo.js
export default () => {};

// main.js
import foo from "./foo";
foo();

类(class)

class Person {
  constructor(name) {
    this.name = name;
  }
  say() {}
}

class Student extends Person {
  say() {
    console.log(`I'm a student, ${this.name}`);
  }
}
const s = new Student("zhangsan");
s.say();

Promise

Symbol

生成器和迭代器

Set 和 Map

静态方法

class MathUtil {
  static add(a, b) {
    return a + b;
  }
}
console.log(MathUtil.add(1, 2));

字符串新方法

  • startsWith
  • endsWith
  • includes

数组新方法

  • find
  • findIndex

对象合并

const obj1 = { a: 1, b: 2 };
const obj2 = { b: 3, c: 5 };
const obj3 = { ...obj1, ...obj2 };

空值合并运算符

const foo = null ?? "default";
const bar = 0 ?? 42;
console.log(bar); // 0

块级作用域

Proxy

Proxy 对象用于创建一个对象的代理,从而实现基本操作的拦截和自定义(如属性查找、赋值、枚举、函数调用等)。

const target = {
  message: "hello world",
};
const handler = {
  get: (obj, prop) => {
    return prop in obj ? obj[prop] : "not fount prop";
  },
  set: (obj, prop, value) => {
    return value;
  },
};
const proxy = new Proxy(target, handler);
Node 和 Element

Node

Node 是一个接口,各种类型的 DOM API 对象会从这个接口继承。它允许我们使用相似的方式对待这些不同类型的对象。

Element

Element 是元素,是网页组成的一部分。一个典型的元素包括一个开始标签,中间文本和结束标签。

dataset

html 中使用data-来定义自定义属性,这是因为:

  • 通过添加data-前缀,可以避免与未来版本中的 HTML 引入的新属性名称发生冲突;
  • 提高代码的可读性和维护性,让开发者清晰的知道哪些是自定义的属性;
  • 可以使用 dataset 将所有的自定义属性获取到;