vite学习笔记

一、搭建Vite工程

搭建vite工程

vite官网搭建工程指南: https://vitejs.dev/guide/#scaffolding-your-first-vite-project

  • 使用自选配置去构建vite项目:

    yarn create vite
    
  • 构建一些具备框架功能的预设模板(不需要你自选配置)

    yarn create vite my-react-ts-app --template react-ts # 还有更多配置可以参阅官网
    
  • vuex, vue-router,

二、vite的依赖预构建

一、vite的预加载

做了什么(开发环境)
  1. 路径补全

    before:
    import _ from "lodash"; // lodash可能也import了其他的东西
    after:
    import __vite__cjsImport0_lodash from "/node_modules/.vite/deps/lodash.js?v=ebe57916";
    
    1.在处理的过程中如果说看到了有非绝对路径或者相对路径的引用, 他则会尝试开启路径补全
    2.找寻依赖的过程是自当前目录依次向上查找的过程, 直到搜寻到电脑根目录如:C盘,搜寻到对应依赖为止 /user/node_modules/lodash, ../
    
做了什么(生产环境)
  1. 依赖预构建

    // 做了什么
    1.vite会找到对应的依赖
    2.调用esbuild(对js语法进行处理的一个库), 将其他规范的代码转换成esmodule规范,
    3.放到当前目录下的node_modules/.vite/deps, 同时对esmodule规范的各个模块进行统一集成 
    
    // 解决问题
    1.不同的第三方包会有不同的导出格式(这个是vite没法约束人家的事情)
    2.对路径的处理上可以直接使用.vite/deps, 方便路径重写
    3.叫做网络多包传输的性能问题(也是原生esmodule规范不敢支持node_modules的原因之一), 有了依赖预构建以后无论他有多少的额外exportimport, vite都会尽可能的将他们进行集成最后只生成一个或者几个模块
    

三、vite的环境区分和环境变量

一、区分开发环境和生产环境

import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'

//策略模式
const envResolver = {
    "build": ()=>Object.assign({}, baseConfig, 生产环境的vite配置),
    "serve": ()=>Object.assign({}, baseConfig,开发环境的vite配置)
}

// https://vitejs.dev/config/
export default defineConfig(({command:"build" | "serve"})=>{
    return envResolver[command]();
})

二、环境变量配置

1.什么是环境变量

环境变量: 会根据当前的代码环境产生值的变化的变量就叫做环境变量

2.怎么去读取.env文件的 (配置完为什么会生效)

1.vite内置了dotenv这个第三方库

2.dotenv会自动读取.env文件

3.解析这个文件中的对应环境变量 并将其注入到process.env对象下(但是vite考虑到和其他配置的

一些冲突问题, 它不会直接注入到process对象下)

3.如何配置环境变量
创建环境变量
//1.创建文件
.env: 所有环境都需要用到的环境变量
.env.development: 开发环境需要用到的环境变量(默认情况下vite将我们的开发环境取名为development)
.env.production: 生产环境需要用到的环境变量(默认情况下vite将我们的生产环境取名为production)

//2.文件定义内容格式(注意:默认必须VITE_开头)
VITE_JIRONGLIANG = 你好

//如果想要自定义环境变量开头需要在配置项中添加envPrefix属性
export default defineConfig({
    envPrefix: "ENV_"
})
ENV_JIRONGLIANG = 你好
使用环境变量
//客户端
1.在客户端使用(也就是vue文件中)vite会将对应的环境变量注入到import.meta.env里去
console.log(import.meta.env)

//服务端
1.在服务端使用(具体一点就是配置文件中)loadEnv方法
// 第一个参数是.env.xxxx名字相匹配 确认是那个文件
// 如:yarn dev --mode devlop
// 就是匹配.env.devlop文件

// 第二个参数不是必须要使用process.cwd(), 是当前env所在的目录路径

// 第三个参数是.env文件名 默认就是.env
export default defineConfig(({command,mode})=>{
	//console.log(command,"区分环境运行的命令")
	//console.log(mode,"细分的环境可自定义")
	const env = loadEnv(mode, process.cwd(), "");
	return {
		plugins:[vue()]
	}
})

三、vite的css模块化

一、Vite中处理CSS

1.Vite和webpack的对比

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

webpack需要安装并配置 css-loaderstyle-loader | mini-css-extract-plugin ,处理 css 文件

2.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后缀的文件
3.Vite如何处理模块化CSS(重点)为了解决类名重复
1.在vite中处理模块化css步骤
  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. 将创建的映射对象在脚本中进行默认导出
2.创建和使用
/**创建*/
1.创建一个compoment.module.css文件写入样式
.footer {
    width: 200px;
    height: 200px;
    background-color: beige;
}
.footer-content {
    width: 200px;
    height: 200px;
    background-color: var(--globalColor);
}
//使用
import componentCss from  "./componentA.module.css";
const div = document.createElement("div");

document.body.appendChild(div);
div.className = componentCss.footerContent;

二、配置模块化CSS

1.vite.config.js中css配置(modules篇)

在vite.config.js中我们通过css属性去控制正个vite对于css的处理行

  1. localConvention: "camelCaseOnly"修改生成的配置对象的key的展示形式(驼峰还是中划线形式)

    export default defineConfig({
        css: {
            modules: {
                localsConvention: "camelCaseOnly",
            }
        }
    })
    
  2. scopeBehaviour:“local” 配置当前的模块化行为是模块化还是全局化global | local (模块化加hash 全局化就是不改变类名)

    export default defineConfig({
        css: {
            modules: {
                scopeBehaviour: "local",
            }
        }
    })
    
  3. generateScopedName:"[name][local][hash:5]"生成的类名的规则 (配置成字符串规则: https://github.com/webpack/loader-utils#interpolatename)

    export default defineConfig({
        css: {
            modules: {
               generateScopedName: (name, filename, css) => {
                     // name -> 代表的是你此刻css文件中的类名
                     // filename -> 是你当前css文件的绝对路径
                     // css -> 给的就是你当前样式
                    console.log("name", name, "filename", filename, "css", css); // 这一行会输出在哪??? 输出在node
                     // 配置成函数以后, 返回值就决定了他最终显示的类型
                     return `${name}_${Math.random().toString(36).substr(3, 8) }`;
                 }
            }
        }
    })
    
  4. hashPrefix:“Hello”, 你配置的这个字符串会参与到最终的hash生成, (hash: 只要你的字符串有一个字不一样, 那么生成的hash就完全不一样, 但是只要你的字符串完全一样, 生成的hash就会一样)

    export default defineConfig({
        css: {
            modules: {
                hashPrefix: "hello"
            }
        }
    })
    
  5. globalModulePaths:[“./componentB.module.css”]代表你不想参与到css模块化的路径

    export default defineConfig({
        css: {
            modules: {
                globalModulePaths: ["./componentB.module.css"]
            }
        }
    })
    

三、Vite配置less预处理

1.vite配置文件中css配置流程(preprocessorOptions篇)

相关less的配置Less 教程_Less 中文网 (lesscss.com.cn)

export default defineConfig({
    css: {
        preprocessorOptions: { // key + config key代表预处理器的名
            less: { // 整个的配置对象都会最终给到less的执行参数(全局参数)中去
                // 在webpack里就给less-loader去配置就好了
                math: "always",
                globalVars: { // 全局变量
                    mainColor: "red",
                }
            },
        },
        devSourcemap: true,
    }
})

四、后处理器PostCss

vite天生对postcss有非常良好的支持

1.PostCss是什么?

PostCss是后处理器就像babel对css代码进行降级和兼容

需要下载不同的Plugins支持不同的功能

2.通用使用PostCss(webpack和vite都可以)
  1. 安装依赖

    postcss-preset-env 包含了 必要的其他依赖

    yarn add postcss-preset-env -D
    
  2. 创建配置文件文件 ( postcss.config.js )

    所需的Plugins

const postcssPresetEnv = require('postcss-preset-env');
// postcssPresetEnv一次吧必要的插件都装上
module.exports = {
 plugins: [
   	postcssPresetEnv(/* pluginOptions */)
   ],
}
3.Vite中使用PostCss

1.对高语法进行降级如

/** before */
.content {
    width: 800px;
    .main {
        width: clamp(100px, 30%, 200px);
        user-select: none; // 他在其他浏览器上不支持
    }
}
/** after */
.content {
    width: 800px;
    .main {
        width: max(100px, min(30%, 200px));
       -webkit-user-select: none;
    	-moz-user-select: none;
    	user-select: non
    }
}
import postcssPresetEnv from 'postcss-preset-env';
export default defineConfig({
    css: {
         postcss: {
           plugins: [
             postcssPresetEnv({})
           ],
         }
    }
})
4. 注意事项
  1. 如果是vite+vue3 则 用postcss.config.js文件会报错
// 解决方案
postcss.config.cjs
  1. 全局变量问题
import postcssPresetEnv from 'postcss-preset-env';
// const path = require("path"); // 做路径处理的   node用的其实是commonjs标准
import path from "path"; // 做路径处理的           vite只能用 esmodule 标准

export default defineConfig({
    css: {
         postcss: {
           plugins: [
            postcssPresetEnv({
            importFrom: path.resolve(__dirname, "./variable.css"), // 就好比你现在让postcss去知道 有一些全局变量他需要记下来
        })
           ],
         }
    }
})

variable.css文件

/* 我们使用的一些未来的css特性是不需要经过less sass的预处理器进行编译, 我们只用交给postcss去处理 */
:root {
    --globalColor: lightblue;
}

四、vite处理静态资源

一、Vite处理静态资源

核心内容:
  1. Vite是开箱即用可直接使用静态资源(包括图片文字和json文件之类)
  2. 服务端读取图片的参数
  3. 设置路径别名
具体步骤:
示例1:在js中import图片资源
// 加载静态图片资源
import sylasPicUrl from "@assets/images/sylas.png"; 
console.log("sylasPicUrl", sylasPicUrl);

const img = document.createElement("img");
img.src = sylasPicUrl;
document.body.append(img);
示例2:导入JSON文件资源

如果你用的不是vite, 在其他的一些构建工具里 json文件的导入会作为一个JSON字符串形式存在

尽量使用解构方便摇树优化

import { name } from "./src/assets/json/index.json";

console.log("jsonFile", name);
服务端读取图片的参数

1.raw

服务端 他会去读取这个图片文件的内容 —> Buffer 二进制的字符串

2.url

服务端返回图片路径 默认就是url

示例1:读取图片的参数
// 加载静态图片资源
import sylasPicUrl from "@assets/images/sylas.png?raw"; 
const img = document.createElement("img");
img.src = sylasPicUrl;
document.body.append(img);
设置路径别名
示例1:vite配置路径
import { defineConfig } from "vite";
import path from "path";

export default defineConfig({
     resolve: {
        alias: {
            "@": path.resolve(__dirname, "./src"), // 设置别名, 以后我们在其他组件中可以使用@来代替src这个目录
        }
    }
})

#二、webpack处理图片和字体

处理代码中引入的图片文件、字体文件

核心内容:
  1. 通过 css-loader 处理:css 代码中 url() 引入的图片或字体
  2. 通过 资源模块 处理:js 代码中 import 引入的图片或字体
具体步骤:
示例1:在样式中引用图片
  1. 基于之前配置过 css-loader 的项目,找一些图片放入 src/assets/images/目录
  2. 为页面添加一个元素,并通过样式设置背景图
 <div class="my-img"></div>

<style>
 .my-img {
    width: 200px;
    height: 200px;
    background: url('./images/pic1.png') center center no-repeat;
    background-size: contain;
  }
</style>
  1. 执行打包,能看到引用的图片已被放入 dist ,并自动生成了随机文件名

以上功能都是由 css-loader 完成的,它可以处理通过 url()引用的资源

示例2:在 js 中 import 图片资源
  1. index.html 中添加元素
<img id="img2" src="" width="200" height="200" />

<script>
// 导入一张图片
import pic2 from './assets/images/pic2.png'

// 将导入的图片设置到 img 标签上
const img2 = document.getElementById('img2')
img2.src = pic2
</script>
  1. 处理 import 导入的资源文件,需使用 webpack 内置的特殊 loader:Asset Modules

webpack.config.js 中添加规则:

// 通过 Asset Modules 处理以下后缀名的图片或字体文件
{
  test: /\.(png|jpe?g|gif|svg|eot|ttf|woff|woff2)$/i,
  type: "asset"
}

Asset Moduls 的说明】

它其实是 webpack 内置的一种专用于处理静态资源的 loader。type 属性可设置为下列值:

  • asset/resource 将资源处理成单独文件,并得到它的 URL 路径
  • asset/inline 将资源处理成 data URI 字符串
  • asset/source 将资源处理成它的原始内容
  • asset 根据文件大小,在处理成 data URI 字符串和单独文件之间自动选择

五、vite基本插件

一、vite-aliases官方网址

1. 作用

vite-aliases可以帮助我们自动生成别名

2.使用方法

Install

npm i vite-aliases -D

Add it to vite.config.js

// vite.config.js
import { ViteAliases } from 'vite-aliases'

export default {
	plugins: [
		ViteAliases()
	]
};
3.原理实现Plugin API | Vite (vitejs.dev)config

插件就是在不同生命周期做不同的事

在 config()生命周期中 添加alias别名

// vite的插件必须返回给vite一个配置对象
const fs = require("fs");
const path = require("path");

function diffDirAndFile(dirFilesArr = [], basePath = "") {
    const result = {
        dirs: [],
        files: []
    }
    dirFilesArr.forEach(name => {
        // 我直接用异步的方式去写的
        const currentFileStat = fs.statSync(path.resolve(__dirname, basePath + "/" + name));
        console.log("current file stat", name, currentFileStat.isDirectory());
        const isDirectory = currentFileStat.isDirectory();

        if (isDirectory) {
            result.dirs.push(name);
        } else {
            result.files.push(name);
        }

    })

    return result;
}

function getTotalSrcDir(keyName) {
    const result = fs.readdirSync(path.resolve(__dirname, "../src"));
    const diffResult = diffDirAndFile(result, "../src");
    console.log("diffResult", diffResult);
    const resolveAliasesObj = {}; // 放的就是一个一个的别名配置 @assets: xxx
    diffResult.dirs.forEach(dirName => {
        const key = `${keyName}${dirName}`;
        const absPath = path.resolve(__dirname, "../src" + "/" + dirName);
        resolveAliasesObj[key] = absPath;
    })

    return resolveAliasesObj;
}

module.exports = ({
    keyName = "@"
} = {}) => {
    return {
        config(config, env) {
            // 只是传给你 有没有执行配置文件: 没有
            console.log("config", config, env);
            // config: 目前的一个配置对象
            // production  development  serve build yarn dev yarn build 
            // env: mode: string, command: string
            // config函数可以返回一个对象, 这个对象是部分的viteconfig配置【其实就是你想改的那一部分】
            const resolveAliasesObj = getTotalSrcDir(keyName);
            console.log("resolve", resolveAliasesObj);
            return {
                // 在这我们要返回一个resolve出去, 将src目录下的所有文件夹进行别名控制
                // 读目录
                resolve: {
                    alias: resolveAliasesObj
                }
            };
        }
    }
}

二、vite-plugin-html官网网址

1. 作用

帮我们动态的去控制整个html文件中内容

2.使用方法
  1. 使用的是ejs语法 <%= title %>在index.html中

ejs在服务端会用的比较频繁 因为服务端可能经常会动态的去修改index.html的内容

  1. 下载依赖并且vite.config.js配置
npm i vite-plugin-html -D
import { createHtmlPlugin } from 'vite-plugin-html'

export default defineConfig({
    plugins: [
          createHtmlPlugin({
              inject: {
                  data: {
                      title:"首页"
                  }
              }
          })
    ]
})
3.实现原理Plugin API | Vite (vitejs.dev)transformIndexHtml
module.exports = (options) => {
  return {
    // 转换html的 
    // 将我们插件的一个执行时机提前 
    transformIndexHtml: {
      enforce: "pre",
      transform: (html, ctx) => {
        // ctx 表示当前整个请求的一个执行期上下文: api /index.html  /user/userlist json  get post headers
        console.log("html", html);
  
        return html.replace(/<%= title %>/g, options.inject.data.title);
      }
    }
    }
}

三、vite-plugin-mock官网地址

模拟后端数据方便开发

2. 使用方法
npm i  mockjs -S  npm i vite-plugin-mock -D
import { viteMockServe } from 'vite-plugin-mock'

export default ({ command }: ConfigEnv): UserConfigExport => {
  return {
    plugins: [
      viteMockServe({
        mockPath: 'mock',
        localEnabled: command === 'serve',
      }),
    ],
  }
}

创建模拟后端数据 mock.js

const mockJS = require("mockjs");

const userList = mockJS.mock({
  "data|100": [{
    name: "@cname", // 表示生成不同的中文名
    // ename: mockJS.Random.name(), // 生成不同的英文名
    "id|+1": 1, // 
    time: "@time",
    date: "@date"
  }]
})

module.exports = [
  {
    method: "post",
    url: "/api/users",
    response: ({ body }) => {
      // body -> 请求体 
      // page pageSize body
      return {
        code: 200,
        msg: "success",
        data: userList
      };
    }
  },
]

四、vite-plugin-federation 联邦webpack5提出

1. 作用

模块联邦主要用于微前端的架构体系中, 他允许你在两个不同的项目中采用公用模块以及进行模块复用

项目A

// project a webpack config
const { ModuleFederationPlugin } = require("webpack").container;
module.exports = {
    plugins: [
        new ModuleFederationPlugin({
            name: "module", // 你要暴露出去的公用模块的名称
            filename: "module-entry.js", // 你暴露出去的公用模块最终会打包到一个js文件里, 你可以配置这个js文件名
            exposes: {
                // 具体暴露的模块
                "./cloneDeep": "./src/cloneDeep.js"
            }
        })
    ]
}

项目B

// project b webpack config
module.exports = {
    plugins: [
        new ModuleFederationPlugin({
            remotes: {
                moduleA: "module@http:localhost:8080/cloneDeep.js", // 就引入了, 然后导入的时候直接module/cloneDeep就好了
            }
        })
    ]
}

六、Vite中配置TS

一、vite-aliases官方网址

1. 作用

vite-aliases可以帮助我们自动生成别名

2.使用方法

Install

npm i vite-aliases -D

Add it to vite.config.js

// vite.config.js
import { ViteAliases } from 'vite-aliases'

export default {
	plugins: [
		ViteAliases()
	]
};
3.原理实现Plugin API | Vite (vitejs.dev)config

插件就是在不同生命周期做不同的事

在 config()生命周期中 添加alias别名

// vite的插件必须返回给vite一个配置对象
const fs = require("fs");
const path = require("path");

function diffDirAndFile(dirFilesArr = [], basePath = "") {
    const result = {
        dirs: [],
        files: []
    }
    dirFilesArr.forEach(name => {
        // 我直接用异步的方式去写的
        const currentFileStat = fs.statSync(path.resolve(__dirname, basePath + "/" + name));
        console.log("current file stat", name, currentFileStat.isDirectory());
        const isDirectory = currentFileStat.isDirectory();

        if (isDirectory) {
            result.dirs.push(name);
        } else {
            result.files.push(name);
        }

    })

    return result;
}

function getTotalSrcDir(keyName) {
    const result = fs.readdirSync(path.resolve(__dirname, "../src"));
    const diffResult = diffDirAndFile(result, "../src");
    console.log("diffResult", diffResult);
    const resolveAliasesObj = {}; // 放的就是一个一个的别名配置 @assets: xxx
    diffResult.dirs.forEach(dirName => {
        const key = `${keyName}${dirName}`;
        const absPath = path.resolve(__dirname, "../src" + "/" + dirName);
        resolveAliasesObj[key] = absPath;
    })

    return resolveAliasesObj;
}

module.exports = ({
    keyName = "@"
} = {}) => {
    return {
        config(config, env) {
            // 只是传给你 有没有执行配置文件: 没有
            console.log("config", config, env);
            // config: 目前的一个配置对象
            // production  development  serve build yarn dev yarn build 
            // env: mode: string, command: string
            // config函数可以返回一个对象, 这个对象是部分的viteconfig配置【其实就是你想改的那一部分】
            const resolveAliasesObj = getTotalSrcDir(keyName);
            console.log("resolve", resolveAliasesObj);
            return {
                // 在这我们要返回一个resolve出去, 将src目录下的所有文件夹进行别名控制
                // 读目录
                resolve: {
                    alias: resolveAliasesObj
                }
            };
        }
    }
}

二、vite-plugin-html官网网址

1. 作用

帮我们动态的去控制整个html文件中内容

2.使用方法
  1. 使用的是ejs语法 <%= title %>在index.html中

ejs在服务端会用的比较频繁 因为服务端可能经常会动态的去修改index.html的内容

  1. 下载依赖并且vite.config.js配置
npm i vite-plugin-html -D
import { createHtmlPlugin } from 'vite-plugin-html'

export default defineConfig({
    plugins: [
          createHtmlPlugin({
              inject: {
                  data: {
                      title:"首页"
                  }
              }
          })
    ]
})
3.实现原理Plugin API | Vite (vitejs.dev)transformIndexHtml
module.exports = (options) => {
  return {
    // 转换html的 
    // 将我们插件的一个执行时机提前 
    transformIndexHtml: {
      enforce: "pre",
      transform: (html, ctx) => {
        // ctx 表示当前整个请求的一个执行期上下文: api /index.html  /user/userlist json  get post headers
        console.log("html", html);
  
        return html.replace(/<%= title %>/g, options.inject.data.title);
      }
    }
    }
}

三、vite-plugin-mock官网地址

模拟后端数据方便开发

2. 使用方法
npm i  mockjs -S  npm i vite-plugin-mock -D
import { viteMockServe } from 'vite-plugin-mock'

export default ({ command }: ConfigEnv): UserConfigExport => {
  return {
    plugins: [
      viteMockServe({
        mockPath: 'mock',
        localEnabled: command === 'serve',
      }),
    ],
  }
}

创建模拟后端数据 mock.js

const mockJS = require("mockjs");

const userList = mockJS.mock({
  "data|100": [{
    name: "@cname", // 表示生成不同的中文名
    // ename: mockJS.Random.name(), // 生成不同的英文名
    "id|+1": 1, // 
    time: "@time",
    date: "@date"
  }]
})

module.exports = [
  {
    method: "post",
    url: "/api/users",
    response: ({ body }) => {
      // body -> 请求体 
      // page pageSize body
      return {
        code: 200,
        msg: "success",
        data: userList
      };
    }
  },
]

四、vite-plugin-federation 联邦webpack5提出

1. 作用

模块联邦主要用于微前端的架构体系中, 他允许你在两个不同的项目中采用公用模块以及进行模块复用

项目A

// project a webpack config
const { ModuleFederationPlugin } = require("webpack").container;
module.exports = {
    plugins: [
        new ModuleFederationPlugin({
            name: "module", // 你要暴露出去的公用模块的名称
            filename: "module-entry.js", // 你暴露出去的公用模块最终会打包到一个js文件里, 你可以配置这个js文件名
            exposes: {
                // 具体暴露的模块
                "./cloneDeep": "./src/cloneDeep.js"
            }
        })
    ]
}

项目B

// project b webpack config
module.exports = {
    plugins: [
        new ModuleFederationPlugin({
            remotes: {
                moduleA: "module@http:localhost:8080/cloneDeep.js", // 就引入了, 然后导入的时候直接module/cloneDeep就好了
            }
        })
    ]
}

七、vite中性能优化

一、页面的性能指标

1. 首屏渲染时: fcp(first content paint)
1. 懒加载
2.http优化: 协商缓存 强缓存

强缓存:服务端会给响应头中添加expires设置过期时间 在这个时间内是不会重新请求而是从缓存中取

协商缓存:是否使用缓存要和后端商量一下,当服务端打上协商缓存的标记后,下次发请求时会发协商请求给服务端如果不需要就返回304拿缓存

2. JavaScript逻辑
1. 副作用清除

组件是会频繁的挂载和卸载:如果我们在某个组件中有定时器,不清除下次挂载等于开启两个线程

// react hook
const [timer,setTimer] = useState(null);

useEffect(()=>{
    setTimer(setTimeout(()=>{
        return () => cleatTimeout(timer)
    }))
})

// 对作用域的一个控制
const arr = [1 ,2]
// 由近到远
for(let i= 0 , len=arr.length;i<=len;i ++){
    
}
2. 浏览器渲染
故事背景

传统的javascript 动画是通过定时器 setTimeout 或者 setInterval 实现的。但是定时器动画一直存在两个问题

  1. 动画的循时间环间隔不好确定,设置长了动画显得不够平滑流畅,设置短了浏览器的重绘频率会达到瓶颈,推荐的最佳循环间隔是16.7ms(大多数电脑的显示器刷新频率是60Hz,1000ms/60)
  2. 定时器第二个时间参数只是指定了多久后将动画任务添加到浏览器的UI线程队列中,如果UI线程处于忙碌状态,那么动画不会立刻执行。为了解决这些问题,H5 中加入了 requestAnimationFrame以及requestIdleCallback
requestAnimationFrame
  1. 特点
  1. 会把每一帧中的所有 DOM 操作集中起来,在一次重绘或回流中就完成,并且重绘或回流的时间间隔紧紧跟随浏览器的刷新频率
  2. 在隐藏或不可见的元素中,requestAnimationFrame 将不会进行重绘或回流,这当然就意味着更少的 CPU、GPU 和内存使用量
  3. requestAnimationFrame 是由浏览器专门为动画提供的 API,在运行时浏览器会自动优化方法的调用,并且如果页面不是激活状态下的话,动画会自动暂停,有效节省了 CPU 开销requestAnimationFrame的回调会在每一帧确定执行,属于高优先级任务
  1. 使用
let myReq;
let i = 0;
function step(timestamp) {
    console.log(i++);
    myReq = window.requestAnimationFrame(step);
}
window.requestAnimationFrame(step);

document.onclick = function(){
    window.cancelAnimationFrame(myReq);    // 专属清除方式
}
requestIdleCallback
  1. 特点

为了在渲染空闲时间执行优先级不高的操作,以避免阻塞渲染。

当前帧的运行时间小于刷新频率(16.6ms),函数fn才会执行。否则,就推迟到下一帧,以此类推。如果timeout时间内都没有时间,fn则会强制执行。

  1. 使用
// 函数fn接受deadline对象作为参数,deadline对象有两个属性:timeRemaining和didTimeout。

// timeRemaining() 返回当前帧还剩余的毫秒数。
// didTimeout 指定的时间是否过期。
function lowWork(deadline) {
  while (
    (deadline.timeRemaining() > 0 || deadline.didTimeout) &&
    taskList.length > 0
  ) {
    doWork();
  }
  if (taskList.length > 0) {
    requestIdleCallback(lowWork);
  }
}
requestIdleCallback(lowWork, 2000);
参考
3. css方面的优化

尽量避免太过于深的css嵌套

关注继承属性: 能继承就不要重复写

二、构建优化:vite(rollup) webpack

  1. 优化体积

    压缩 treeshaking 图片资源压缩 cdn加载 分包 …

1. 分包策略
浏览器的缓存策略

名字没有变化,那么他就不会重新去拿

hash: 只要有内容变化hash就会变 文件名不一样就会重新请求

故事背景

分包就是吧不容易改变的文件,进行单独的打包处理

步骤大纲(简化版)
  1. 创建新目录,编写一个公用工具模块
  2. 编写两个入口 ts 文件,它们都引用了上面的工具模块
  3. 安装 loadsh 并在上面其中一个模块中调用
  4. 编写 vite 配置,处理多入口,
  5. 打包并观察结果
具体步骤(简化版)
  1. 创建新目录,编写以下文件

    公共模块:src/common/index.ts

    import { cloneDeep } from 'lodash'
    cloneDeep([])
    

    页面入口1:src/index.ts

    import { forEach } from 'lodash'
    import './common/index'
    
    const mainArr = []
    
    forEach(mainArr, el => {
    	window.console.log(el)
    })
    

    页面入口2:src/product.ts

    import { forEach } from 'lodash'
    import './common/index'
    
    const mainArr = []
    
    forEach(mainArr, () => {
    	window.console.log('12')
    })
    
    

    配置 vite

import { defineConfig } from 'vite'
import path from 'path'

export default defineConfig(() => {
	return {
		build: {
			minify: false,
			rollupOptions: {
				input: {
					main: path.resolve(__dirname, './index.html'),
					product: path.resolve(__dirname, './product.html')
				},
				output: {
					manualChunks: (id: string) => {
						if (id.includes('node_modules')) {
							return 'vendor'
						}
					}
				}
			}
		}
	}
})

打包并观察结果 自己试试

2. gzip压缩
具体步骤
  1. 下载依赖

npm i vite-plugin-compression -D

  1. 在文件vite.config.ts中配置
// 静态资源压缩
import viteCompression from 'vite-plugin-compression';
export default defineConfig({
  plugins: [
    viteCompression({
      verbose: true,
      disable: false, // 不禁用压缩
      deleteOriginFile: false, // 压缩后是否删除原文件
      threshold: 10240, // 压缩前最小文件大小
      algorithm: 'gzip', // 压缩算法
      ext: '.gz' // 文件类型
    })
  ],
});
后端配合
  1. 服务端读取gzip文件 返回时响应头加上content-encoding == > gzip
3. 动态导入
1. webpack和vite 构建原理的差别
> webpack: 是将所有的文件打包之后才会构建开发服务器
>
> vite:  是直接将入口文件 index.html 抛出去 如果引用了main.js  通过浏览器 esmodule的规范 去请求开发服务器 所以 vite是按需加载的

按需加载和动态导入有异曲同工之妙

动态导入是es6的新特性在此之前webpack也有自己的import()实现 使用动态导入按需加载

2. 具体步骤
  1. 在路由文件中

    import("@/component/index.vue") //返回的是一个promise
    
3. 实现原理
function import (path){
    return new Promise((resolv)=>{
        // vite 用的是es原生的动态导入 是浏览器自己执行的
      //e函数创建了promise.all 创建了一个script标签 src只想home这个文件
      webpack__require.e().then(() =>{
			const result = await webpack__require(path)
		})
    })
}

当没有进入过某个页面中把这个组件放入script标签里面去 但是布塞茹body里面
当进入这个页面才添加到body
4. cdn加速vite-plugin-cdn-import - npm (npmjs.com)

cdn:content delivery network 内容分发网络

将包用连接的形式加入到项目里

具体步骤

yarn add vite-plugin-cdn-import -D

import importToCDN from 'vite-plugin-cdn-import'
export default defineConfig({
  plugins: [
    importToCDN({
       name: 'lodash',
       var: '_',
       path: `umd/react-dom.production.min.js`,
    })
  ],
});

对应

import _ from "lodash";  name 和 var
  • 2
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值