Skip to content
成为赞助商

相关知识

框架面

SPA单页应用

  • 浏览器一开始就加载必须的HTML、CSS、JS,所有的操作在单个页面上完成,由JS控制交互和局部刷新
  • 优点:
    • 前后端分离、良好的用户体验,不刷新界面,显示更流畅
    • 减轻服务器压力,不需要频繁请求界面
  • 缺点:初次加载比较耗时、且不利于SEO优化

SSR与CSR

  • SSR(Server-Side Rendering) :服务端直接把渲染后的页面发送给客户端,提升页面响应速度。
    • 框架:
    • 特点:
      • 不需要SPA中的等待页面加载 > 执行JS > 加载数据 > 渲染到网页
      • 优点:提升性能、SEO优化友好
      • 需要部署并运行在服务器,无法运行在静态空间
    • 一般构成:
      • 服务器端应用程序:运行服务器上的应用程序,不能直接运行在静态空间,需要使用Node.js运行;负责接收请求、执行渲染过程并返回页面
      • 路由:需要根据客户端请求的不同路径,调用对应的渲染逻辑和数据获取方法
      • 模板引擎:用于将页面模板和数据结合,最终生成HTML内容,基于某种MVVM框架
      • 数据获取:服务端获取初次渲染需要的数据,和用户信息等需要前端获取的数据,可能涉及数据库、API或其他来源的数据
      • 状态管理:需要考虑如何管理客户端和服务器的状态同步,避免出现不一致的情况
      • 客户端交互:在页面初次渲染之后,客户端仍要进行交互,如何使用JS添加动态内容或处理用户操作
  • CSR(Client Side Rendering):依赖运行在客户端的JS,用户首次发送请求只能得到小部分的指引性HTML代码。第二次请求将会请求更多包含HTML字符串的JS文件
  • https://juejin.cn/post/7039151040188383268

image-20230330093208475

MVC与MVVM

  • MVC:

    在传统的非前后端分离项目中,后端需要处理大量的内容,如果不按照一定的模式就是"大乱炖"

    模型 M-视图(用户界面) V - 控制器 C

    • C即controller控制器:接受用户的输入并调用模型和视图去完成用户的需求,控制器本身不输出任何东西和做任何处理。它只是接收请求并决定调用哪个模型构件去处理请求,然后再确定用哪个视图来显示返回的数据。
    • V即View视图:指用户看到并与之交互的界面。比如由html元素组成的网页界面,或者软件的客户端界面。MVC的好处之一在于它能为应用程序处理很多不同的视图。在视图中其实没有真正的处理发生,它只是作为一种输出数据并允许用户操作的方式。
    • M即model模型:指业务规则。在MVC的三个部件中,模型拥有最多的处理任务。被模型返回的数据是中立的,模型与数据格式无关,这样一个模型能为多个视图提供数据,由于应用于模型的代码只需写一次就可以被多个视图重用,所以减少了代码的重复性。

    image-20220419204329588

  • MVVM

    由后端的MVC架构演化而来,传统的MVC并不符合前端的实际需求

    划分代码职责:原本需要发数据、存数据、拼模板、渲染DOM...(大乱炖)

    • Model 模型:对应Vue data中的数据
    • View 视图:模板
    • ViewModel 视图模型:vue实例对象, 是View与Model的结合
    • 优点:
      • 将视图 UI 和业务逻辑分开
      • 低耦合。视图(View)可以独立于Model变化和修改,一个ViewModel可以绑定到不同的"View"上,当View变化的时候Model可以不变,当Model变化的时候View也可以不变。
      • 可重用性。你可以把一些视图逻辑放在一个ViewModel里面,让很多view重用这段视图逻辑。
      • 实现数据的双向绑定:
        • 修改数据 M => 视图自动改变
        • 修改视图 V => 数据自动改变

image-20220419204639547

技巧方法

代码混淆

变量命名管理

便于项目管理的同时避免编码时写错单词

  • 创建 constant.js文件
  • const定义变量并导出 export const INCREMENT = 'increment'
    • 当需要更换变量名时,只需修改一处
  • 使用时导入需要的变量即可 import {INCREMENT} from './constant'

主题色设置

一文搞懂前端多主题适配方案 - 掘金 (juejin.cn)

前端常见方案:

  • 使用浏览器默认主题作为默认主题色,监听浏览器的主题使用。
  • 后续根据用户在界面更换主题按钮,结合本地存储进行独立管理。
css获取主题色
css
/* prefers-color-scheme: dark 当浏览器模式为深色时生效 */
@media (prefers-color-scheme: dark) {
  .day.dark-scheme   { background:  #333; color: white; }
  .night.dark-scheme { background: black; color:  #ddd; }
}

/* prefers-color-scheme: light 当浏览器模式为浅色时生效 */
@media (prefers-color-scheme: light) {
  .day.light-scheme   { background: white; color:  #555; }
  .night.light-scheme { background:  #eee; color: black; }
}

/* .day.dark-scheme 表示选择同时拥有 day、dark-scheme 类名的标签 */
js获取主题色
js
const isDarkTheme = window.matchMedia("(prefers-color-scheme: dark)");
if (isDarkTheme.matches) { // 是深色
  // 主题设置为深色。
} else { // 不是深色
  // 主题设置为浅色。
}
指定局部主题

color-scheme CSS 属性允许元素指示它可以舒适地呈现哪些颜色方案。

操作系统颜色方案的常见选择为“亮色”和“暗色”,或“日间模式”和“夜间模式”。当用户选择其中一种颜色方案时,操作系统会对用户界面进行调整,包括表单控件、滚动条和 CSS 系统颜色的使用值。

该css属性,可以覆盖浏览器的默认主题配置

css
/* 
禁止用户代理覆盖元素的颜色方案。
可以使用 color-scheme: only light; 应用于特定的元素或 :root,以关闭由 Chrome 的自动深色主题引起的颜色覆盖。
*/
color-scheme: only light; /* 指定必须为 亮色 */

color-scheme: normal;	/* 使用浏览器的默认配色方案 */
color-scheme: light;    /* 亮色 */
color-scheme: dark;		/* 暗色 */
监听当前环境颜色模式的变化
js
var mqList = window.matchMedia('(prefers-color-scheme: dark)');

mqList.addEventListener('change', (event) => {
  if (event.matches) {
    // 暗色模式
  } else {
    // 亮色模式
  }
});


// 兼容性问题:
/*  
Safari < 14时 
没有addEventListener和 .removeEventListener,需要使用
MediaQueryList.addListener, 和  MediaQueryList.removeListener, 
*/
/* IE不支持 */

获取ip地址

前端js
js
 // 获取用户ip地址 借助搜狐接口
let loginIp=''
const script = document.createElement("script")
script.src = "https://pv.sohu.com/cityjson?ie=utf-8"
script.async = true
document.body.appendChild(script)
script.onload = () => loginIp = window.returnCitySN["cip"];
// {"cip": "211.142.190.47", "cid": "410000", "cname": "河南省"};
// loginIp 为ip地址
后端node
js
// 获取客户端ip地址
app.get('/ip', function (req, res) {
  var clientIp = getIp(req)
  console.log('客户端ip',clientIp)
  res.json({'youIp':clientIp});
})
//通过req的hearers来获取客户端ip
var getIp = function(req) {
  var ip = req.headers['x-real-ip'] || req.headers['x-forwarded-for'] || req.connection.remoteAddres || req.socket.remoteAddress || '';
  if(ip.split(',').length>0){
    ip = ip.split(',')[0];
  }
  return ip;
};

文件下载携带token

使用 msSaveBlob或 msSaveOrOpenBlob 可能存在兼容性问题

js
// 因为存在兼容问题,使用前建议判断一下
 if (window.navigator.msSaveOrOpenBlob) {
    navigator.msSaveBlob(blob, filename);
  } else {
 	// 使用a标签的下载功能。。。
  }
// 创建内容
var blobObject = new Blob(["I scream. You scream. We all scream for ice cream."]);
// 1.msSaveBlob:只提供一个保存按钮
window.navigator.msSaveBlob(blobObject, 'msSaveBlob_testFile.txt');
// 2.msSaveOrOpenBlob:提供保存和打开按钮
window.navigator.msSaveOrOpenBlob(blobObject,'msSaveBlobOrOpenBlob_testFile.txt');

下载文件使用a标签的 download 属性,但a标签无法设置请求头、请求参数

  • 使用a标签下载文件时要设置请求头如带上用于鉴权的token - 简书 (jianshu.com)

  • <a href="文件路径" download="filename">

    • 注:如果遇到下载 txt、jpg 等文件时出现直接打开文件而不是下载文件的情况时,可以在下载地址即 url 后拼接 ?response-content-type=application/octet-stream 即可
  • objectURL = URL.createObjectURL(fileObject) 获取当前文件的一个内存URL,同步执行

    • 参数:用于创建 URL 的 File 对象、Blob 对象或者 MediaSource 对象。
  • URL.revokeObjectURL(objectURL) 释放他们

    • 浏览器在 document 卸载的时候,会自动释放它们,但是为了获得最佳性能和内存使用状况,你应该在安全的时机主动释放
  • image-20220914152932167

  • js
    // 解决思路:
    // 1.使用xhr 发起请求,得到文件数据
    // 2.利用URL.createObjectURL() 将xhr获取的临时文件生成 本地临时地址
    // 3.使用a标签下载本地临时地址的文件 √
    this.getBlob(fileUrl || file.thumbUrl).then(blob => {
       this.saveAs(blob, file.name);
     });
    
    /** 获取 blob    url 目标文件地址  */
    getBlob(url) {
      return new Promise(resolve => {
        const xhr = new XMLHttpRequest();
        xhr.open("GET", url, true);
        xhr.setRequestHeader("token",sessionStorage.getItem('token'));
        xhr.responseType = "blob";
        xhr.onload = () => {
          if (xhr.status === 200) {
            resolve(xhr.response);
          }
        };
        xhr.send();
      });
    },
        
    /** 保存 blob
     * filename 想要保存的文件名称   */
    saveAs(blob, filename) {
      if (window.navigator.msSaveOrOpenBlob) {
        navigator.msSaveBlob(blob, filename);
      } else {
        const link = document.createElement("a");
        const body = document.querySelector("body");
        link.href = window.URL.createObjectURL(blob);
        link.download = filename;
        // fix Firefox
        link.style.display = "none";
        body.appendChild(link);
        link.click();
        body.removeChild(link);
        window.URL.revokeObjectURL(link.href);
      }
    },
        
    // 用fetch发送请求 对请求回来的二进制文件流进行处理
    fetch('/upload/user.png').then((res) => {
      res.blob().then((blob) => {
        const blobUrl = window.URL.createObjectURL(blob);
        // 这里的文件名根据实际情况从响应头或者url里获取
        const filename = 'user.txt';
        const a = document.createElement('a');
        a.href = blobUrl;
        a.download = filename;
        a.click();
        window.URL.revokeObjectURL(blobUrl);
      });
    });

token刷新并发处理解决方案

  1. 延时刷新:在token即将过期时,前端通过异步请求获取新的token,此时旧token还可以使用。等到新token获取后,再将旧token替换成新token。

    限制请求频率:前端可以限制刷新token的请求频率,避免多次重复请求导致并发问题。可以设置一个时间窗口,在该时间窗口内只允许刷新一次token。

  2. 轮询刷新:前端可以通过轮询的方式定时获取新的token,以保证token的实时性。可以设置轮询时间间隔,避免并发请求问题。而非调用接口时进行判断

  3. 使用锁机制:前端可以使用锁机制来保证同时只有一个请求可以刷新token,其他请求需要等待锁释放后才能进行刷新操作。可以使用JavaScript的锁机制或者在后端设置锁机制。🧪

  4. 使用队列:前端可以使用队列来控制token刷新请求的顺序,避免并发问题。将刷新token请求放入队列中,每次只处理队列中的一个请求,等待处理完后再处理下一个请求。

国际化语言

编辑器注释规范

js
/**
*
* @param [eye] 中括号表示为可选值
* @param [name='xiaowang'] 等号表示,该参数有默认值
*/

image-20230608095740537

clsx.js条件用类

与classnames用法一致,but classnames针对所有浏览器,clsx主要针对react中使用

  • 同名包中的 clsx() 函数是一个 JavaScript 实用程序,用于设置设置 className 属性值的条件。它接受无限数量的参数,不限于一种特定类型。

  • 最终,clsx() 函数返回一个 string 插值,检查 JavaScript 变量的 boolean 值并相应地应用类

  • 会被丢弃的值

    • js
      clsx(true, false, '', null, undefined, 0, NaN);
jsx
// classNameTwo的存在与否,根据number的值进行动态展示
import "./styles.css";
import clsx from "clsx";
const classNameOne = "redButton";
const classNameTwo = "blueBorder";
export default function App() {
    const number = 3;
    return (
        <div className="App">
        <button className={clsx(classNameOne, { [classNameTwo]: number > 5 })}>
            A sample button
        </button>
        </div>
    );
}



// => className='foo bar baz quux'
<div className={clsx('foo', { bar: true, duck: false }, 'baz', { quux: true })} />

const menuStyle = clsx({
    [classes.root] : true, //always applies
    [classes.menuOpen] : open //only when open === true
})

多栏布局拖拽

vue实现多栏布局拖拽(改变盒子的宽度)_vue拖拽改变盒子大小-CSDN博客

主题切换工程化方案

css变量方案
  • 按梯度层析,定义一系列css颜色变量

    • tailwindcss 预设颜色值
  • 将梯度css变量作为颜色值使用

    • background-color: theme('colors.gray.500');
  • 在业务代码中利用 css 变量来设置颜色

    css
    .app {
      background-color: var(--bg-color);
      color: var(--text-color);
    }
  • 添加切换主题的功能:点击切换时更换根元素类名 或 css变量的值

Vercel-网站托管

将网站托管到vercel,而不用购买服务器

  • 官网,注册账号

  • 导入代码仓库 或 本地操作

  • 本地

    • 全局安装 vercel npm i -g vercel

    • 在本地项目的根目录 新建vercel.json

      image-20230412113726723

      json
      {
        "rewrites":[{"source":"/api/(.*)"}]
      }
    • 根目录新建api文件夹/index.js

      image-20230412113822967

    • 命令行登陆 vercel vercel login 自动跳转到网页进行登陆,成功示例:

      image-20230412114024789

    • 本地命令行继续输入 vercel 进行推送,进行配置,自动上传成功

    • 后续修改后,只需输入 vercel 会自动推送

    • 更多配置...

前端安全

  • [如何安全传输存储用户密码?](https://www.cnblogs.com/jay-huaxiao/p/14224777.html#:~:text=总结 1 因此,一般使用https 协议 %2B 非对称加密算法(如RSA)来传输用户密码,为了更加安全,可以在前端构造一下随机因子哦。 2,使用BCrypt %2B 盐存储用户密码。 3 在感知到暴力破解危害的时候, 「开启短信验证、图形验证码、账号暂时锁定」 等防御机制来抵御暴力破解。)
  • JavaScript库**「jsencrypt」**

前端可观测性

前端可观测性是一套科学完整的性能检测、分析、优化体系。包含:页面指标、网络传输、服务可靠性、用户体验。

  • 构建前端可观测性的基础条件

    • 在浏览器有一套 收集发送指标的逻辑

    • 服务器对指标进行存储和处理ß

    • 对指标进行可视化展示

  • 借助第三方产品

    • 观测云
    • 听云
    • 友盟+

image-20230327173427967

img

img

异常捕获

  • 监听全局未捕获的错误监听 window的 error事件

    js
    window.addEventListener("error",function(event){
      // event错误事件对象
      event.message // 报错信息
      event.filename // 报错的文件
      event.lineno   event.columnNo // 报错的行列
      event.error.stack // 报错的堆栈信息
      
      let log = {}
    })

组件库开发

  • BEM(block element modifier)规范

    typescript
    // block-代码块    element-元素     modifier-装饰    state-状态
    // z-button  z-button_element z-button_element--disable  is-disable is-enabeld 诸如此类的命名
    
    // 但是为了便书写,可封装函数操作   实现  :clss=[bem.b()]   =>  z-button
    
    function _bem(prefixName:string,blockSuffix:string,element:string,modifier:string){
        if(blockSuffix) prefixName+=`-${blockSuffix}`
        if(element) prefixName+=`__${element}`
        if(modifier) prefixName+=`--${modifier}`
        return prefixName
    }
    
    function createBEM (prefixName){
        const b = (blockSuffix:string = '') => _bem(prefixName,blockSuffix,'','')
        // element 无值时取空,而不应返回一个block,由于与后续控制样式
        const e = (element:string = '') => element ? _bem(prefixName,'',element,'') : ''
        const m = (modifier:string = '') => modifier ? _bem(prefixName,'','',modifier) : ''
       	const be = (blockSuffix:string,element:string) => blockSuffix && element ? _bem(prefixName,blockSuffix,element,'') : ''
        const bm = (blockSuffix:string,modifier:string) => blockSuffix && modifier ? _bem(prefixName,blockSuffix,'',modifier) : ''
        const em = (element:string,modifier:string) => element && modifier ? _bem(prefixName,'',element,modifier) : ''
        const bem = (blockSuffix:string,element:string,modifier:string) =>  blockSuffix && element && modifier ? _bem(prefixName,blockSuffix,element,modifier) : ''
        
        const is = (name:string,state:any) => state ? `is-${name}` : ''
        
        return { b,e,m,be,bm,em,bem,is }
    }
    
    export function cerateNamespace(name:string)=>{
        const prefixName = `z-${name}`;
        return createBM(prefixName);
    }
    
    // 测试 , js生成类名
    const bem = cerateNamespace('icon')
    bem.b()   // 输出: z-icon
    bem.b('box')   // 输出: z-icon-box
    bem.e('ele')   // 输出: z-icon__ele
    bem.is('checked',true)   // 输出: is-checked

前端性能优化

性能指标

打包视图分析

vue3中 rollup-plugin-visualizer

vite3+vue3 项目打包优化实战之-视图分析(rollup-plugin-visualizer)、CDN引入、依赖分包、gzip压缩、history404问题-CSDN博客

Lighthouse前端性能分析

项目优化

使用三方CDN、依赖分包、

vite3+vue3 项目打包优化实战之-视图分析(rollup-plugin-visualizer)、CDN引入、依赖分包、gzip压缩、history404问题-CSDN博客

js
// vite-plugin-cdn-import
pnpm install vite-plugin-cdn-import --save-dev
// vite.config.js 基本用法
import reactRefresh from '@vitejs/plugin-react-refresh'
import importToCDN from 'vite-plugin-cdn-import'

import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import { autoComplete, Plugin as importToCDN } from 'vite-plugin-cdn-import';

export default defineConfig({
  plugins: [vue(),
   importToCDN({
    prodUrl: 'https://unpkg.com/{name}@{version}/{path}',
    modules: [
      autoComplete('vue'),
      {
        name: 'element-plus',
        var: 'ElementPlus', //根据main.js中定义的来
        version: '2.2.17',
        path: 'dist/index.full.js',
        css: 'dist/index.css'
      },
      {
        name: 'vue-demi',
        var: 'VueDemi', //根据main.js中定义的来
        version: '0.13.11',
        path: 'lib/index.iife.js'
      },
      {
        name: '@element-plus/icons-vue',
        var: 'ElementPlusIconsVue', //根据main.js中定义的来
        version: '2.0.9',
        path: 'dist/index.iife.min.js'
      },
    ],
  })
],
)}




// vite中  使用 vite-plugin-cdn-import   或  vite-plugin-cdn-import-async(性能更高?,可设置 defer/async )
pnpm install vite-plugin-cdn-import-async --save-dev

// vite.config.js 基本用法
import cdnImport from 'vite-plugin-cdn-import-async'	// 引入cdnImport

export default {
    plugins: [
        cdnImport({
            modules: [
                {
                    name: 'react',
                    var: 'React',
                    mode: 'async', // 'async' atrribute will be added to its <script> tag.
                    path: `https://cdn.jsdelivr.net/npm/react@18.2.0/umd/react.production.min.js`,
                },
                {
                    name: 'lottie-web',
                    var: 'lottie',
                    mode: 'defer', // 'defer' atrribute will be added to its <script> tag.
                    path: `https://cdn.jsdelivr.net/npm/lottie-web@5.10.0/build/player/lottie.min.js`,
                },
                {
                    name: 'axios',  // Module without 'mode' param will be loaded synchronously.
                    var: 'axios',
                    path: 'https://cdn.jsdelivr.net/npm/axios@1.2.1/dist/axios.min.js',
                }
            ],
        }),
    ],
}
开启gzip

Nginx开启Gzip apach开启gzip 前端脚手架开启gzip cdn开启gzip

nginx中如何设置gzip(总结) - 范仁义 - 博客园 (cnblogs.com)

网页加载过程

  • web前端的开发与部署过程

    • 用户使用浏览器,借助网址向服务器获取【动态的、增量式】的静态资源
    • image-20211130230533113.png
  • 用户输入url ,浏览器解析后发送到DNS服务器,查询ip地址【DNS缓存,降低dns查询的时间?】

  • 借助基础网络,传输到对应的服务器【使用CDN请求静态资源,解决网络缓存、线路选择等问题,但默认携带的cookie是一种浪费,不同的cdn域名可以解决;但cdn无法作为接口】

  • 服务器接收到请求后,进行请求的分发处理Contorller

    • Model层,进行数据交互,读取数据库,获取数据
    • 最后将渲染好的页面通过View,返会给网络--浏览器
  • 浏览器接受数据【 减少http请求的次数和大小?】

    • rander过程,浏览器将服务器返回的数据(html/css/js...)进行渲染【服务器端渲染?】

    • 浏览器生成DOM和BOM树,在进行css渲染,然后再去执行js相关的代码【渲染过程的优化?】image-20211130230829988

资源的合并与压缩

作用点:借助文件的合并减少http请求次数;借助文件压缩,减少请求文件的大小

html压缩
  • 压缩在文本文件中有意义,但在HTML中不显示的字符,包括空格、制表符、换行符等,还有一些其他意义的字符,如:HTML注释也可以被压缩
  • HTML压缩的效果不是很明显,但对于大量用户访问的网页来说,每kb的流量都不容忽视
html压缩方式
  • 使用网站在线压缩,适合初学者(但一般不用,使用webpack...)
  • node.js 提供了 html-minifier工具(可以进行很多的配置,具有可扩展性)
  • 后端模板引擎渲染压缩(会增加服务端的计算量)
css压缩
  • 使用在线网站进行压缩
  • 使用html-minifier库对html中的css进行压缩
  • 使用clean-css库对css进行压缩
js压缩与混乱
  • 作用:无效字符的删除、剔除注释、代码语义的缩减和优化、代码保护(降低可读性)
  • 方法:
    • 使用网站在线压缩
    • 使用html-minifier配置,对js进行压缩
    • 使用uglifyjs2对js进行压缩【】
文件合并
  • 不合并存在的问题

    • 文件与文件之间有插入的上行请求,增加了N-1个网络延迟

    • 受丢包影响更为严重

    • 经过代理服务器时可能会被断开

    • image-20211201103045973

  • 合并后的问题

    • 首屏渲染问题,合并后文件变大,首屏渲染可能减慢,需要适当分开
    • 缓存失效问题,任一文件的改动,会导致文件缓存大面积失效的问题
  • 文件合并的建议

    • 公共库合并,对长期不经常改动的内合并
    • 不同页面的合并,对于单页应用,只加载当前页面,分别打包。
    • 见机行事,随机应变。
  • 文件合并方法

    • 使用在线网站进行
    • 使用node.js实现文件合并,多种多样的库,自行挑选

操作流程

在线压缩工具
  • 有一定的限制,可能不支持部分文件格式的压缩
  • 分别手动对原生代码进行压缩
  • 在js压缩过程中,需要手动根据依赖关系压缩后再合并
  • image-20211201105304024
构建工具的优点
  • 对于大量的文件可以进行高效快速的构建
  • 具有良好的可扩展性
  • 可根据配置进行定制化的设置
webpack构建工具(使用再学习)
fis3构建工具(使用再学习)
  • 流程:
    • 单文件编译过程,形成完整的结构
    • 打包,依据源文件的内容,进行压缩和混乱

image-20211201105540261

图片相关优化

使用参考:文件大小、色彩的丰富度 根据不同场景进行选择

类型分类
JPG图片- 有损压缩
  • 情景:大部分不需要透明图片的业务场景
  • 原始的数据与实际jpg压缩后的数据是不同的
  • 但大多数jpg压缩并不影响肉眼观察
  • 可能不支持透明,但实际使用大都可以透明
png8/png24/png32
  • 情景:大部分需要透明图片的业务场景
  • png8:256色+支持透明,
    • 内部其实是一个颜色的索引表,每个颜色就是2^8的索引值,因此会小很多
    • 适用于颜色变化不特别丰富的图片
    • 缺点:支持的颜色比较少,不适合颜色丰富且相近的图片
  • png24:2^24色+不支持透明
    • 每一个索引值是png8的三倍
  • png32:2^24色+支持透明
    • 相比于png24,增加了8位,用于支持透明
webp压缩程度更好,再ios和webview有兼容性问题
  • 压缩程度更好,建议安卓全部使用
svg矢量图,代码内嵌,相对较小,图片样式相对简单的场景
  • 情景:图片样式相对简单的业务场景
  • 例如 icon库,或者其他库....
gif 支持动画
图片压缩

本质:针对图片真实情况,舍弃一些相对无关紧要的色彩信息

Image inline
  • 将图片的内容内嵌到html中,减少网站的http请求数量
  • 适合大小较小的图片,建议8kb以下做inline image使用
  • 特点:导致页面文件变大,但减少了一次http请求
  • image-20211213162118143
svg矢量图
  • 使用svg标签进行矢量图的绘制
  • 使用iconfont解决icon问题
  • 优点:速度和大小都会有很好的优化
  • 学习:w3c
webp
  • 优势:具有更优的图像数据压缩算法,能带来更小的体积,拥有无差别的图像质量
  • 具备无损和有损的压缩模式、Alpha透明、动画特性
  • 再PNG和JPEG上的转哈效果都非常优秀、稳定和统一
转换方法
格式转换

image-20211213204333298

css和js的装载与执行

  • 学习目标:
    1. 理解浏览器端 html、css、js的加载过程
    2. 结合 chrome 的能力学习掌握加载过程中的优化点
  • 渲染的过程
    1. 拿到html文件后渲染为DOM树
      • HTML渲染特点
      • 词法分析,由上至下顺序执行生成DOM,
      • 多种资源的加载是并发进行加载的(受浏览器对单个域名并发请求限制),因此一个网站常使用多个cdn服务
      • 是否阻塞:css加载阻塞js加载?js加载阻塞js执行?
        • css 在head中阻塞页面的渲染,当css加载完再渲染html(推荐)
        • css阻塞js的执行,因为js的执行可能操作css的内容
        • css不阻塞外部脚本的加载,并发加载
        • 直接引入的js阻塞页面的渲染,存在js操作dom时,影响dom加载
        • js不阻塞资源的加载(有一个预先扫描器,不执行先加载)
        • js顺序执行,阻塞后续js逻辑的执行
      • 依赖关系:css资源加载过慢时,出现屏闪问题(等完全渲染完后再显示)
      • 引入方式:
        • css:link 和 @import
        • js:script 和 路由动态引入 和 和
    2. 再根据css样式生成CSSOM树
    3. 将两者合并渲染为页面

image-20211213211431367

CSS

JS

  • 尽可能避免污染全局变量,全局声明的变量不会被垃圾回收机制回收
  • 局部变量在代码调用结束之后,就会释放对应的内存空间,提高代码性能

总结-优化点

  1. 减少HTTP请求数量

    • 多域名 不超过6个,有的会根据域名进行限制,同一域名同一时刻会限制请求数量
    • 合并代码,使用雪碧图、精灵图或canvas、svg等方式将图片转为代码
    • 使用更合理的代码逻辑,防抖节流等减少请求
  2. 压缩请求数据的大小,减少每次请求的代码量

    • 一般借助打包工具,对代码、图片进行压缩
    • 代码逻辑中去除重复的 脚本/代码 引用
  3. 较少cookie体积,会在每次请求中携带,影响请求的速度

  4. 延迟加载非必要的引用脚本 - 当前太慢,砍当前

    • 均摊第一次加载时的任务量,提升体验
  5. 预加载可能需要的内容 - 当前太慢,牺牲上次

    • 在上次加载中 提前加载本次需要的内容
  6. 减少DOM元素的层级

  7. 减少css代码中,标签嵌套的层级

  8. 减少DOM的访问次数,较少DOM的重构(回流),和不必要的重绘

  9. 使用特别的属性,opacity、transform、

    这些属性能够在不同的层,进行单独绘制,不会导致页面重绘

    间接使用设备GPU硬件渲染,提升效率

  10. 合理利用HTTP缓存,浏览器缓存,利用静态资源

    • 本地缓存、避免重复加载
网页加载阶段分析
  1. 网页资源请求与加载阶段 ( DevTools查看请求各阶段耗时)

    • 浏览器对每个源允许的TCP连接数量限制

      现状:Chrome基于 http只允许6个TCP同一时刻连接同一源

      方案:

      • 将多个资源分布在不同的子域名上,减少请求队列的等待时间;
      • 但不同的域,需要不同的DNS域名解析过程(通常划分为 3-5 个域名比较合适)
      • DNS解析记录会在浏览器中进行缓存,因此推荐使用 公共CDN托管
  2. 网页渲染阶段( DevTools查看网页渲染各部分耗时Paining\Rendering)

    • 获取HTML生成DOM树,同时下载css、js文件

    • 将CSS解析为CSSOM 层叠样式表模型

      过于复杂的css嵌套规则会影响CSSOM的生成,导致渲染时间延长

    • JS引擎处理JS

      如果在页面渲染时,使用JS操作DOM,则会阻塞渲染

      推荐方案:将Js代码放在页面代码底部 </body>

    • 将 DOM 和 CSSOM 构建成为渲染树,生成用户界面

字体压缩

font−spider

智能 WebFont 压缩工具,它能自动分析出页面使用的 WebFont 并进行按需压缩

js
// 1.安装
npm install font-spider -g

// 2.新建用于转换文本的 html 文件,把需要用的文案粘贴进去
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
  <style>
    @font-face {
      font-family: 'convert-font';
      /* src: url('./Alibaba-PuHuiTi-Bold.ttf') format('truetype'); */
      src: url('./Alibaba-PuHuiTi-Medium.ttf') format('truetype');
      font-weight: normal;
      font-style: normal;
    }

    .convert-font {
      font-family: 'convert-font';
    }
  </style>
</head>
<body>
  <div>
    Lorem ipsum dolor sit amet consectetur, adipisicing elit. Quam quos minima autem animi quae itaque assumenda doloribus, similique dicta sequi, molestias tenetur quaerat qui suscipit ducimus labore, saepe debitis adipisci!
  </div>
  </br>
  <div class="convert-font">
    Lorem ipsum dolor sit amet consectetur adipisicing elit. Blanditiis amet error quae perferendis eum repellendus
    necessitatibus quod, minus commodi? Quis eaque iusto distinctio libero eos architecto deserunt mollitia, aliquid
    tempora.
  </div>
</body>
</html>

// 3.在该项目文件夹内执行命令
font-spider index.html
    
// 4.查看并使用文件夹内新增的 .font-spider 中文件

PWA

渐进式web应用,给用户原生引用体验;运用现代的Web Api及传统的渐进式增强策略创建跨平台web应用程序。

  • 优势
    • 渐进式,适用于所有浏览器
    • 流畅,能够借助Service Worker在离线或者网络较差的情况下正常访问
    • 可安装、具备原生体验:首屏加载动画、隐藏地址栏等
    • 粘性:通过推送离线通知,让用户回流
  • PWA应用 三要素
    • HTTPS 服务支持
    • manifest.json 清单
    • service worker

manifest清单

  • web app manifest 应用程序清单
    • 作用:提供有关应用的配置信息
    • 注意:
      • 需要在https协议下才能访问!!!
js
// 1.在项目更目录 创建manifest.json文件
// 2.在index.html中引入 manifest.json
<link ref='manifest' href='manifest.json' />
    
    
// 3.manifest配置信息;
name:'应用名称';	// 用户安装横幅提示的名称,和启动画面的文字
short_name:'应用的短名称'; // 用于主屏幕展示
description:''; // 描述
start_url:'/'; // 指定用户设备启动时加载的url,绝对路径/相对路径
icons:[]; // 在各种环境中应用程序图标的对象数组
background_color:''; // 指定用户启动动画的背景颜色
theme_color:''; // 指定应用程序的主题颜色
display:'' // 指定应用程序的显示模式
// - fullscreen 全屏显示,不显示状态栏
// - standalone 更像原生App
// - minimal-ui 保留浏览器栏

ServiceWorker

  • 14年提出的 HTML5 API,主要用来做持久的离线缓存
  • 特点
    • 允许web应用在网络较差或离线环境下依旧可以使用,静态资源优先读缓存或不常变更的资源优先读取缓存
    • 是一个独立的worker线程,相当于当前网页进程,是一种特殊的webworker
    • 一旦被install就永远存在,除非被手动 unregister
    • 独立于主线程之外,使用时被唤醒,不使用时自动休眠
    • 可编程拦截代理请求和返回,缓存的文件可以被网页进程取到(包括离线状态)
    • 离线内容开发者可控
    • 必须在HTTPS环境下工作
    • 异步实现,内部大都是通过Promise实现
  • 生命周期
    • install 在serviceWorker注册成功时触发,主要用于缓存资源
    • activate 在serviceWorker激活时触发,用于删除旧的资源
    • fetch 发送请求时触发,用于操作缓存或读取网络资源
  • 注意
    • sw.js 文件发生变化时,install 会重新触发
    • activate 事件会在install事件后触发,但由于sw.js文件改变引起的install事件中由于serviceWorker已经存在,就会处于等待状态,直到当前serviceWorker终止
      • 可通过self.skipWaiting()方法跳过等待,返回Promise对象
      • 通过self.waitUntil()方法结束一个Pomise对象,会在Promsie结束后结束当前生命周期函数,防止浏览器在异步操作之前就停止了生命周期。
    • 注册serviceWorker默认在下次刷新页面时才生效;claim()方法会立即控制这些页面
js
// 在window.onload 中注册 service worker,防止与其他资源竞争
// navigator对象中内置了 serviceWorker属性
// 兼容性判断: if('serviceWorker' in navigator){}
// register注册serviceWorker,返回Promise对象

// index.html
window.onload = ()=>{
    if('serviceWorker' in navigator){
        navigator.serviceWorker.register('./sw.js')
            .then(resitration=>{console.log(resitration)})
        	.catch(err=>{console.log(err)})
    }
}

// sw.js 
self.addEventListener('insatll',event=>{
    // 等待skipWaiting跳过等待结束,再触发activate事件
    event.waitUntil(self.skipWaiting())
})

self.addEventListener('activate',event=>{
    // 表示serviceWorke激活后,立即获取控制权
    event.waitUntil(self.clients.claim())
})

self.addEventListener('fetch',event=>{
    //
})

CacheStorage缓存

image-20241120173054332

notification通知

更现代的PWA做法

WebAssembly

了解AssemblyScript:基于TS的语言,可编译为WebAssembly ?

  • 特点:
    • WebAssembly一种新的编码方式,属于低级的类汇编语言,具备紧凑的二进制格式,接近原生性能运行,编译其他语言并在 Web运行。可与 JavaScript 协同工作。
    • 编译和优化:wasm是体积小且加载快的二进制格式,用于充分发挥硬件能力以达到原生执行效率
    • 可使用非 JavaScript 编程语言编写代码在浏览器运行的技术方案,是一种新的字节码格式。最终编译为.wasm文件
    • 快速、高效、可移植、限制在安全沙箱中运行,遵循浏览器的同源策略和授权策略
    • wasm文件体积更小,下载速度更快,解析比JS更快
    • WebAssembly模块 在很多方面和 ES模块等价
    • JavaScript API 为开发者提供了创建模块、内存、表格和实例的能力
    • 任意的 JavaScript 函数都能被 WebAssembly 代码导入并同步调用
  • 可能的由来
    • 3D 游戏、虚拟现实、增强现实、计算机视觉、图像/视频编辑等大量要求原生性能的领域中JS性能受限
  • 相关概念
    • 模块:被浏览器编译为可执行机器码的WebAssembly二进制代码;无状态的;支持像ES模块的声明导入导出
    • 内存:可变长的ArrayBuffer。本质上是连续的字节数组。WebAssembly 的低级内存存取指令可进行读写操作。
    • 表格:可变长的类型化数组,存储不能作为原始字节存储在内存里的对象的引用。
    • 实例:模块及其在运行时使用的所有状态,包括内存、表格和一系列导入值。
  • 入门
    • 使用 Emscripten 移植一个 C/C++ 应用程序。
    • 直接在汇编层,编写或生成 WebAssembly 代码。【略】
    • 编写 Rust 程序,将 WebAssembly 作为它的输出。
    • 使用 AssemblyScript,它类似于 TypeScript 并且可编译成二进制 WebAssembly 代码。

JS接口

C/C++移植

  • Emscripten 工具可以将任何 C/C++ 源代码编译成 Wasm 模块

Emscripten

Emscripten 工具可以将任何 C/C++ 源代码编译成 Wasm 模块,再加上必要的 JavaScript 粘合代码,用于加载和运行模块,以及显示代码结果的 HTML 文档。

img

  • 工作流
    1. Emscripten 首先把 C/C++ 提供给 clang+LLVM —— 成熟的开源 C/C++ 编译器工具链
    2. Emscripten 将 clang+LLVM 编译的结果转换为 Wasm 二进制文件
    3. WebAssembly 本身无法直接访问 DOM;它只能调用 JavaScript,并且只能传入整型和浮点型的原始数据类型作为参数 【 计划将来允许 WebAssembly 直接调用 Web API

Rust移植

AssemblyScript

  • 将 TypeScript 的严格变体编译为 WebAssembly,让网络开发者可以继续使用他们熟悉的 TypeScript 兼容工具,如 Prettier、ESLint、VS Code IntelliSense 等
  • https://www.assemblyscript.org/
  • 针对WebAssembly 的功能集、与 TypeScript 的相似性、与现有的Web 生态系统集成
访客总数 总访问量统计始于2024.10.29