react 渲染机制

前言 文本分为两大部分讲解,一部分是首次挂载渲染原理,另一部分是更新和卸载原理,很多地方非常抽象,希望大家仔细阅读,不然容易脱节。废话不多话,开车!! 正文 在开始之前,需要一些前置知识才能帮助我们更好的理解整个渲染过程。首先就是生命周期(16版本之后),为什么要讲一下生命周期?跟渲染原理有关系吗?当然有,如果你不理解渲染原理的话,更新一个嵌套很深的组件你甚至连父与子生命周期执行的先后顺序都不知道。本文直接对照16版本之后的新生命周期进行讲解,就不讲解老版本了。 初探-生命周期 顾名思义,跟人生一样,生命周期就是一个组件从诞生到销毁的过程。React在组件的生命周期中注册了一系列的钩子函数,支持开发者在其中注入代码,并在适当的时机运行。这里指的生命周期仅针对于类组件中的钩子函数。因为生命周期不是本文的重点,所以Hooks中的新增的钩子函数在本文中均不涉及,可以以后出个Hooks原理篇。 从图中可以看到,我把生命周期分为了挂载阶段、更新阶段、卸载阶段三个阶段。同时,在挂载阶段和更新阶段都会运行getDerivedStateFromProps和render,卸载阶段很好理解,只有一个componentWillUnMount,在卸载组件之前做一些事情,通常用来清除定时器等副作用操作。那么挂载阶段和更新阶段中的生命周期我们来逐一看下每个运行点及作用。 1. constructor 在同一个类组件对象只会运行一次。所以经常来做一些初始化的操作。同一个组件对象被多次创建,它们的construcotr互不干扰。 注意:在construcotr中要尽量避免(最好禁止)使用setState。 我们都知道使用setState会造成页面的重新渲染,但是在初始化阶段,页面都还没有将真实DOM挂载到页面上,那么重新渲染的又有什么意义呢。除异步的情况,比如setInterval中使用setState是没问题的,因为在执行的时候页面早已渲染完成。但也最好不要,容易一些引起奇怪的问题。 constructor(props) { super(props); this.state = { num: 1 }; //不可以,直接Warning this.setState({ num: this.state.num + 1 }); //可以使用,但不建议 setInterval(()=>{ this.setState({ num: this.state.num + 1 }); }, 1000); } 2. 静态属性 static getDerivedStateFromProps 该方法是一个静态属性,在16版本之前不存在,在新版生命周期中主要用来取代componentWillMount和componentWillReceiveProps,因为这两个老生命周期方法在一些开发者不规范的使用下极容易产生一些反模式的bug。因为是静态方法,所以你在其中根本拿不到this,更不可能调用setState。 该方法在挂载阶段和更新阶段都会运行。它有两个参数props和state当前的属性值和状态。它的返回值会合并掉当前的状态(state)。 如果返回了非Object的值,那么它啥都不会做,如果返回的是Object,那么它将会跟当前的状态合并,可以理解为Object.assign。通常情况下,几乎不怎么使用该方法。 /** * 静态方法,首次挂载和更新渲染都会运行该方法 * @param {*} props 当前属性 * @param {*} state 当前状态 */ static getDerivedStateFromProps(props, state){ // return 1; //没用 return { num: 999, //合并到当前state对象 }; } 3. render 最重要的生命周期,没有之一。用来生成虚拟节点(vDom)树。该方法只要遇到需要重新渲染都会运行。同样的,在render中也严禁使用setState,因为会导致无限递归重新渲染导致爆栈。 ...

December 17, 2024

全屏API

在 HTML 中,可以通过调用浏览器的全屏 API 实现页面或页面中的某个元素全屏显示。全屏 API 是 HTML5 提供的一项功能,允许将元素切换为全屏模式。 使用全屏 API 实现全屏 全屏 API 提供了以下常用方法和属性: requestFullscreen():请求将元素设置为全屏模式。 exitFullscreen():退出全屏模式。 fullscreenElement:返回当前正在全屏显示的 DOM 元素,如果没有元素全屏,则返回 null。 fullscreenchange 事件:当全屏状态发生变化时触发,用来监听进入或退出全屏。 实现全屏的基本步骤 选择你希望全屏的元素。 使用 element.requestFullscreen() 方法来进入全屏。 使用 document.exitFullscreen() 来退出全屏模式。 示例代码 <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>全屏示例</title> <style> #fullscreenContent { width: 100px; height: 100px; background-color: lightblue; margin: 50px auto; text-align: center; line-height: 100px; } </style> </head> <body> <div id="fullscreenContent">点击全屏</div> <button id="exitFullscreenBtn" style="display:none;">退出全屏</button> <script> const fullscreenContent = document.getElementById('fullscreenContent'); const exitFullscreenBtn = document.getElementById('exitFullscreenBtn'); // 进入全屏模式 fullscreenContent.addEventListener('click', function() { if (fullscreenContent.requestFullscreen) { fullscreenContent.requestFullscreen(); } else if (fullscreenContent.mozRequestFullScreen) { // Firefox fullscreenContent.mozRequestFullScreen(); } else if (fullscreenContent.webkitRequestFullscreen) { // Chrome, Safari and Opera fullscreenContent.webkitRequestFullscreen(); } else if (fullscreenContent.msRequestFullscreen) { // IE/Edge fullscreenContent.msRequestFullscreen(); } exitFullscreenBtn.style.display = 'inline-block'; }); // 退出全屏模式 exitFullscreenBtn.addEventListener('click', function() { if (document.exitFullscreen) { document.exitFullscreen(); } else if (document.mozCancelFullScreen) { // Firefox document.mozCancelFullScreen(); } else if (document.webkitExitFullscreen) { // Chrome, Safari and Opera document.webkitExitFullscreen(); } else if (document.msExitFullscreen) { // IE/Edge document.msExitFullscreen(); } exitFullscreenBtn.style.display = 'none'; }); // 监听全屏状态变化 document.addEventListener('fullscreenchange', () => { console.log('全屏状态发生变化'); }); </script> </body> </html> 代码解释 进入全屏:点击 #fullscreenContent 的 div 时,调用 requestFullscreen() 方法使该元素进入全屏模式。 退出全屏:点击按钮 #exitFullscreenBtn,调用 document.exitFullscreen() 退出全屏模式。 跨浏览器兼容性:由于部分浏览器的全屏 API 有不同的实现方法,代码中兼容了各个主流浏览器的实现(例如 Chrome, Firefox, Safari, Edge)。 监听全屏状态变化:使用 fullscreenchange 事件来监听当前全屏模式的变化,可以在进入或退出全屏时执行额外操作。 全屏 API 的一些注意事项 全屏 API 只能在用户交互(如点击或按键事件)后触发,不能通过脚本自动进入全屏模式,这是为了防止用户体验被打断。 不同浏览器的全屏 API 实现可能稍有不同,因此通常需要做一些兼容性处理。 在移动端设备上,全屏模式的行为可能与桌面端有所不同。 总结 HTML5 的全屏 API 提供了一种简单而直观的方法来让页面或元素进入全屏模式。通过 requestFullscreen() 方法可以将任意元素全屏显示,exitFullscreen() 则用来退出全屏模式。结合 fullscreenchange 事件可以对全屏状态进行监控,实现更加丰富的交互体验。 ...

October 17, 2024

变量

一、变量的声明 声明变量的时候,变量名前面要加两根连词线(--)。 body { --foo: #7F583F; --bar: #F7EFD2; } 上面代码中,body选择器里面声明了两个变量:--foo和--bar。 它们与color、font-size等正式属性没有什么不同,只是没有默认含义。所以 CSS 变量(CSS variable)又叫做**“CSS 自定义属性”**(CSS custom properties)。因为变量与自定义的 CSS 属性其实是一回事。 你可能会问,为什么选择两根连词线(--)表示变量?因为$foo被 Sass 用掉了,@foo被 Less 用掉了。为了不产生冲突,官方的 CSS 变量就改用两根连词线了。 各种值都可以放入 CSS 变量。 :root{ --main-color: #4d4e53; --main-bg: rgb(255, 255, 255); --logo-border-color: rebeccapurple; --header-height: 68px; --content-padding: 10px 20px; --base-line-height: 1.428571429; --transition-duration: .35s; --external-link: "external link"; --margin-top: calc(2vh + 20px); } 变量名大小写敏感,--header-color和--Header-Color是两个不同变量。 二、var() 函数 var()函数用于读取变量。 a { color: var(--foo); text-decoration-color: var(--bar); } var()函数还可以使用第二个参数,表示变量的默认值。如果该变量不存在,就会使用这个默认值。 color: var(--foo, #7F583F); 第二个参数不处理内部的逗号或空格,都视作参数的一部分。 ...

October 10, 2024

状态管理

本知识点将以 Pinia 为例,介绍 Vue 中的状态管理。 前言 Pinia简称小菠萝🍍,是一个专为Vue 3设计的现代化状态管理库,为Vue 3开发的,它提供了一种简单、可扩展和类型安全的方式来管理应用程序的状态。 与Vue 2中的Vuex相比,Pinia提供了更好的TypeScript支持,具有更好的类型定义和类型推断,可在编译时捕获错误,提供更高的代码可靠性和开发体验。它是专为Vue 3设计的,充分利用了Vue 3的新特性,如Composition API,以提供更直接、自然和灵活的状态管理体验。Pinia的核心概念是Store,它类似于Vuex中的模块,用于管理应用程序的状,可以将相关的状态和逻辑组合到单个Store中,使代码更清晰、结构更有组织性。除此之外海提供了许多有用的特性和功能,例如模块化组织、状态持久化、插件扩展等。 总的来说,Pinia是一个功能强大而灵活的状态管理解决方案,适用于各种规模的Vue 3应用程序。它提供了现代化的特性和工具,帮助我们更好地组织、管理和扩展应用程序的状态,同时提供了更好的类型安全和开发体验。 安装 运行安装命令 npm install pinia 在main.ts中引入 // main.ts import { createApp } from 'vue' import { createPinia } from 'pinia' import App from './App.vue' const pinia = createPinia() const app = createApp(App) app.use(pinia) app.mount('#app') 初始化Store 新建stores文件,用于存放所有的store,然后创建index.ts。 同过 defineStore() 定义一个store,它接受一个参数作为仓库名称,也就是Id。它返回一个函数,默认我们使用user开头的风格来接收。第二个参数为一个Setup函数或者Option对象。 import { defineStore } from 'pinia' export const useUsersStore = defineStore('users', { // 其他配置... }) Option Store 这种方式熟悉Vuex的很了解,传入一个带有 state、actions 与 getters 属性的 Option 对象 export const userUsersStore = defineStore('users', { state: () => { return { name: 'inkun', current: 100 } }, getters: { getName: (state) => state.name + '🐔你好帅' }, actions:{ getUserInfo { ... } } }) 在 Option Store 中: ...

October 5, 2024

call、apply和bind

apply 和 call简介 在 javascript 中,call 和 apply 都是为了改变某个函数运行时的上下文(context)而存在的,换句话说,就是为了改变函数体内部 this 的指向。 JavaScript 的一大特点是,函数存在「定义时上下文」和「运行时上下文」以及「上下文是可以改变的」这样的概念。 比如 A 对象有一个方法,而 B 对象因为某种原因,也需要用到同样的方法,那么这时候不用单独为 B 对象扩展一个方法,可以直接借用 A 对象的方法。这样既完成了需求,又减少了内存的占用。 直接来看个例子: function fruits() {} fruits.prototype = { color: "red", say: function() { console.log("My color is " + this.color); }} var apple = new fruits();apple.say(); //My color is red 但是如果我们有一个对象 banana= {color : "yellow"} ,我们不想对它重新定义 say 方法,那么我们可以通过 call 或 apply 用 apple 的 say 方法: banana = { color: "yellow"}apple.say.call(banana); //My color is yellowapple.say.apply(banana); //My color is yellow apply、call 的区别 对于 apply、call 二者而言,作用完全一样,只是接受参数的方式不太一样。例如,有一个函数定义如下: ...

October 1, 2024

React函数组件更新

React的函数组件(Function Components)自Hooks推出以来已成为React开发的主流方式。理解函数组件的更新原理对于编写高效、可维护的React应用至关重要。本文将深入探讨React函数组件的更新机制。 一、函数组件与类组件的本质区别 在深入更新原理前,我们需要明确函数组件与类组件的根本区别: 类组件:是类的实例,拥有实例属性和生命周期方法 函数组件:是纯函数,接收props作为参数,返回JSX 这种本质区别决定了它们的更新行为完全不同。 二、函数组件的执行机制 当React需要渲染一个函数组件时,它只是简单地调用这个函数: function MyComponent(props) { return <div>{props.message}</div>; } // React内部大致这样处理 const element = MyComponent({ message: 'Hello' }); 每次渲染都是全新的函数调用,所有局部变量和函数都会重新创建。这与类组件形成鲜明对比,类组件在更新时会复用同一个实例。 三、触发更新的场景 函数组件的更新主要由以下情况触发: 父组件重新渲染:即使props未变化,父组件渲染也会导致子组件重新渲染 props发生变化:当传入组件的props值改变时 状态更新:通过useState、useReducer等Hook更新状态 context变化:组件订阅的context值发生变化 Hooks依赖变化:useEffect、useMemo等Hook的依赖项发生变化 四、React的渲染流程 函数组件的更新遵循React的渲染流程: 触发更新:通过setState、父组件渲染等途径触发更新 协调阶段(Reconciliation): React调用函数组件获取新的JSX 与上一次渲染的JSX进行对比(Virtual DOM Diff) 提交阶段(Commit): 将差异应用到真实DOM 执行useLayoutEffect的回调 浏览器绘制:浏览器重绘屏幕 副作用执行:执行useEffect的回调 五、Hooks与组件更新 Hooks是函数组件能够拥有状态和生命周期的关键。理解Hooks的工作机制对理解组件更新至关重要。 useState的工作原理 const [count, setCount] = useState(0); React会在组件首次渲染时为每个useState调用分配一个"状态单元" 后续更新时,React会按照Hooks的调用顺序来提供对应的状态值 setCount调用会触发组件的重新渲染 useEffect与更新 useEffect(() => { // 副作用代码 return () => { // 清理函数 }; }, [dependencies]); 组件每次渲染后,React会比较依赖项数组 如果依赖项变化或没有提供依赖项数组,副作用会重新执行 清理函数会在副作用重新执行前或组件卸载时执行 六、优化更新性能 由于函数组件每次更新都会完整执行函数体,我们需要一些优化手段: ...

December 17, 2024

深拷贝/浅拷贝

浅拷贝和深拷贝都是对于JS中的引用类型而言的,浅拷贝就只是复制对象的引用,如果拷贝后的对象发生变化,原对象也会发生变化。只有深拷贝才是真正地对对象的拷贝。 前言 说到深浅拷贝,必须先提到的是JavaScript的数据类型,大家可以看前面的知识点,这里不多说了。 初学前端的同学,需要知道的一点就是:JavaScript的数据类型分为基本数据类型和引用数据类型。 对于基本数据类型的拷贝,并没有深浅拷贝的区别,我们所说的深浅拷贝都是对于引用数据类型而言的。 浅拷贝 浅拷贝的意思就是只复制引用,而未复制真正的值。 const originArray = [1,2,3,4,5]; const originObj = {a:'a',b:'b',c:[1,2,3],d:{dd:'dd'}}; const cloneArray = originArray; const cloneObj = originObj; console.log(cloneArray); // [1,2,3,4,5] console.log(originObj); // {a:'a',b:'b',c:Array[3],d:{dd:'dd'}} cloneArray.push(6); cloneObj.a = {aa:'aa'}; console.log(cloneArray); // [1,2,3,4,5,6] console.log(originArray); // [1,2,3,4,5,6] console.log(cloneObj); // {a:{aa:'aa'},b:'b',c:Array[3],d:{dd:'dd'}} console.log(originArray); // {a:{aa:'aa'},b:'b',c:Array[3],d:{dd:'dd'}} 上面的代码是最简单的利用 = 赋值操作符实现了一个浅拷贝,可以很清楚的看到,随着 cloneArray 和 cloneObj 改变,originArray 和 originObj 也随着发生了变化。 深拷贝 深拷贝就是对目标的完全拷贝,不像浅拷贝那样只是复制了一层引用,就连值也都复制了。 只要进行了深拷贝,它们老死不相往来,谁也不会影响谁。 目前实现深拷贝的方法不多,主要是两种: 利用 JSON 对象中的 parse 和 stringify 利用递归来实现每一层都重新创建对象并赋值 JSON.stringify/parse的方法 先看看这两个方法吧: The JSON.stringify() method converts a JavaScript value to a JSON string. JSON.stringify 是将一个 JavaScript 值转成一个 JSON 字符串。 ...

November 7, 2024

与 CSS/JS 的交互

HTML、CSS 和 JavaScript 是前端开发的三大基础技术,它们通过不同的方式相互作用,创建交互性良好的网页。 1. HTML 与 CSS 的交互 HTML 负责定义网页的结构,而 CSS 则负责美化这些结构元素。两者的交互主要通过以下方式: <style> 标签:直接在 HTML 中嵌入 CSS 样式。 这种方式将 CSS 写入到 HTML 文件中,但不推荐用于大型项目,因为这会导致样式与结构混杂在一起。 <style> h1 { color: red; } </style> <link> 标签:通过 <link> 标签引入外部 CSS 文件。 这种方式通常用于大型项目中,将样式与结构分离,维护更加清晰。 <link rel="stylesheet" href="styles.css"> 内联样式:直接将样式属性写在 HTML 标签内。 这种方式适合临时的小改动,但不推荐频繁使用,尤其在大型项目中不利于维护。 <h1 style="color: blue;">这是一个标题</h1> 2. HTML 与 JavaScript 的交互 JavaScript 负责操作和控制网页内容,主要与 HTML 进行交互以实现动态效果。以下是交互方式: <script> 标签:通过 <script> 标签直接在 HTML 中嵌入 JavaScript 代码。 此方法会在 HTML 中直接执行 JavaScript,适合小规模的脚本。 <script> alert('这是一个弹窗'); </script> 外部 JavaScript 文件:通过 <script> 标签引入外部 JavaScript 文件。 这种方式将 JavaScript 逻辑与 HTML 分离,便于维护和重用。 <script src="script.js"></script> DOM 操作:通过 JavaScript 操作 DOM(文档对象模型),动态更改 HTML 元素的内容和属性。例如: <p id="text">这是原始文本</p> <script> document.getElementById('text').innerText = '这是通过 JS 改变的文本'; </script> 事件监听:JavaScript 可以通过事件监听器响应用户的交互。例如: <button id="btn">点击我</button> <script> document.getElementById('btn').addEventListener('click', function() { alert('按钮被点击了'); }); </script> 3. CSS 与 JavaScript 的交互 JavaScript 也可以与 CSS 进行交互,动态地控制页面的样式。常见的方法有: ...

October 17, 2024

display

一、display属性介绍 display 属性规定元素应该生成的框的类型。 以下是一些关于display比较常用的属性值: 值 描述 none 元素不会显示 block 此元素将显示为块级元素,此元素前后会带有换行符。 inline 默认。此元素会被显示为内联元素,元素前后没有换行符。 line-block 行内块元素。(CSS2.1 新增的值)[IE6/7不支持] list-item 此元素会作为列表显示。 inline-table 此元素会作为内联表格来显示(类似 table),表格前后没有换行符。 table 此元素会作为块级表格来显示(类似 table),表格前后带有换行符。 table-row 此元素会作为一个表格行显示(类似 tr)。 table-cell 此元素会作为一个表格单元格显示(类似 td 和 th). inherit 规定应该从父元素继承 display 属性的值。 其中我们在前端开发中比较常用的属性值一般是none、block、inline、inline-block。我将按顺序为这些属性值一一讲解。 二、display:none 将元素与其子元素从普通文档流中移除。这时文档的渲染就像元素从来没有存在过一样,也就是说它所占据的空间消失了。元素的内容也会消失。 三、display:block block元素会独占一行,多个block元素会各自新起一行。默认情况下,block元素宽度自动填满其父元素宽度; block元素可以设置margin和padding属性; block元素可以设置width、height属性。 块级元素即使设置了宽度,仍然是独占一行。块级元素在设置宽度的情况下,是通过自定义margin-right来自动填满一行,这个时候你设置margin-right是无效的;块级元素在没有设置宽度的时候,margin-right会生效,块级元素的width通过自定义在自动填满一行。 块级元素在设置宽度的情况下,是通过自定义margin-right来自动填满一行,这个时候你设置margin-right是无效的,如下图所示: 块级元素在没有设置宽度的时候,margin-right会生效,块级元素的width通过自定义在自动填满一行,如下图所示: 四、display:inline inline元素不会独占一行,多个相邻的行内元素会排列在同一行里,直到一行排列不下,才会新换一行,其宽度随元素的内容而变化; inline元素设置width、height属性无效; inline元素的margin和padding属性,水平方向的padding-left、padding-right、margin-left、margin-right都产生边距效果;但竖直方向的padding-top、padding-bottom、margin-top、margin-bottom不会产生边距效果。 如下图所示: 五、display:inline-block 将对象呈现为inline对象,但是对象的内容作为block对象呈现,之后的内联对象会被排列在同一行内。就是集合了block和inline的全部优点。width、height、margin、padding设置都会生效。 如下图所示: 原文:https://juejin.cn/post/6844903781566513160 常见考点 1. 基本概念 请简述 CSS 中的 display 属性及其作用。 display 属性的常见值有哪些?请列举并解释其含义。 例如:block、inline、inline-block、flex、grid、none 等。 2. display: block 什么是 display: block?它会给元素带来哪些表现? 一个块级元素通常具有哪些特点?例如:它的宽度会如何变化?它是否会换行? 3. display: inline 什么是 display: inline?它与 display: block 有什么不同? 内联元素如何处理宽度和高度?它们是否会占据整个可用宽度? 如何让一个内联元素变得像块级元素一样换行? 4. display: inline-block 什么是 display: inline-block?它结合了 inline 和 block 的哪些特点? 使用 inline-block 时,元素的宽高如何影响布局? inline-block 元素如何与其他元素对齐? 5. display: none 请解释 display: none 的作用与 visibility: hidden 的区别。 使用 display: none 会有什么样的表现?例如,它对页面布局有何影响? 当你将一个元素的 display 设置为 none 时,它会消失,但仍然存在于 DOM 中。如何通过 JavaScript 使其重新显示? 6. display: flex display: flex 是什么?它通常用来实现什么样的布局效果? 在使用 flex 布局时,如何控制子元素的排列方向、对齐方式等? justify-content、align-items、flex-direction 等 Flexbox 属性的作用是什么? 7. display: grid 什么是 display: grid 布局?它与 flex 布局有何区别? 在 grid 布局中,如何设置行和列的大小?如何控制子元素的位置? 请举例说明 grid-template-columns、grid-template-rows、grid-gap 等属性的使用方法。 8. display: table 什么是 display: table,它如何模仿传统的 HTML <table> 元素? 使用 display: table 时,如何控制表格的行和列布局? display: table-row、display: table-cell 和 display: table-caption 的作用是什么? 9. 布局模型对比:block vs inline vs flex vs grid 你如何选择使用 block、inline、flex 或 grid 布局? 在实际项目中,什么时候使用 flex 布局,什么时候使用 grid 布局? 请比较 flex 和 grid 布局的优缺点及应用场景。 10. display 与其他布局属性 在使用 display: flex 或 display: grid 布局时,如何与其他布局属性(如 position、float 等)配合使用? 如何解决使用 display: flex 或 display: grid 布局时遇到的常见布局问题,例如元素溢出、对齐等? 11. display: none 与性能 display: none 对页面性能有何影响?如何通过 JavaScript 动态控制显示/隐藏元素以提高性能? 12. display: contents 什么是 display: contents?它是如何工作的?它与其他 display 值有什么区别? 请举例说明 display: contents 的应用场景及可能的问题。 13. display 的继承性 display 属性是否继承?如果没有,如何通过样式继承的方式控制多个元素的 display 属性? 你如何在多个嵌套元素中保持一致的布局设置? 14. display 的可见性与布局 display 属性会影响元素的布局、渲染和可见性。请简要解释 display: none 和 visibility: hidden 的区别,它们对页面的影响不同吗? 15. 实际应用 请描述一个你在实际开发中使用 display 属性解决布局问题的案例。 如果需要在一个响应式设计中调整元素的布局,如何根据屏幕尺寸调整 display 属性?

October 10, 2024

服务器渲染

前言 什么是SSR(Server-Side Rendering)?类似于用Java、ASP.NET、php开发前端页面,服务端准备数据并执行页面渲染,然后把完整的HTML发送给客户端。现代化的SSR通常和Vue、React等前端框架强关联,通过Node.js在服务端提前将page、component使用同构方式渲染为html代码,其目的是在实现SSR时尽量复用一套渲染机制。例如Vue框架就同时支持了CSR(Client-Side Rendering)和SSR。 SSR能解决什么问题? SEO: Google、百度、Yandex、Bing或者Yahoo等搜索引擎会通过网络爬取你的页面并建立索引,如果的信息越完整,那么你的网站就更容易被检索到。使用SSR技术,让页面在服务端提前渲染好再返回给客户端,这样各类搜索引擎就能拿到比较完整的页面信息,提升检索质量。 首屏渲染:SPA(Single Page Application)将页面渲染放到客户端执行,并且在渲染之前要加载大量的Javascript代码,所以首屏渲染需要花费较长时间。而SSR直接在服务端渲染完再返回客户端,使用户能够快速看到页面内容。 有哪些框架支持SSR? 开源库 支持语言 star数 描述 Nuxt Vue 54.9k 快速构架、类型安全、高性能、易扩展 Next React 127k 老牌框架、社区完善、Data Fetch、Styling quasar Vue 26k 丰富的UI、支持桌面端和移动端 Remix React 29.9k 极致的用户体验、开箱即用 本知识点将以 Nuxt 为例,介绍如何实现 SSR。 什么是Nuxt 基于Vue、React、Angular实现的full-stack框架数不胜数,但就Vue框架领域内,应该数Nuxt最为Top 1。Nuxt保持了Vue生态的“开箱即用”特性,类似于Vite,几条指令就可直接run起来。如果你熟悉Vue,那么基于Nuxt写应用,体验上和写Vue客户端应用极其相似。 Nuxt提前预制了一套目录结构,并自动处理路由、导入、数据获取,开发人员仅需按约定的规则实现页面、组件、数据API即可,极大降低开发人员学习成本。Nuxt的特性可总结为: 基于文件的路由: 页面统一添加到pages/目录下,例如添加index.vue、about.vue、contact.vue,就可以通过/访问首页,通过/about返回关于页面; 代码分割:得益于Vue的Virtual Node,Nuxt根据目录规则提前生成完整的依赖树,这样能够将所有code拆分为最小单元的chunks, 从而减少应用初始化加载时间; 开箱即用的SSR:Nuxt内部使用Vue的SSR,除了Vue实现SSR、CSR本身的差异,可以像开发CSR的Vue应用一样来开发SSR,降低研发心智负担; 自动导入:在实现页面或组件时,不需要像CSR手动导入(import Card from './Card.Vue')依赖,当Nuxt识别到有使用外部组件,bundle过程会自动识别; 数据获取工具:提供一套组合式FetchAPI,实现client、server端的同构; 配置好的构建工具:默认使用Vite构建,同时支持Webpack、Rspack; Nuxt的架构设计和vue/core很相似,将整体功能拆分为独立的package: 核心引擎:nuxt; bundlers: 打包器包含@nuxt/vite-builde、nuxt/webpack-builder、@nuxt/rspack-builder; 命令行工具:nuxi; 服务端引擎:nitro; 开发套件:@nuxt/kit; 上手体验 初始化项目 使用nuxi提供的指令初始化项目: npx nuxi@latest init nuxt-learn-examples 如果安装过程下载失败,可能需要配置hosts: 185.199.108.133 raw.githubusercontent.com 最新nuxt版本3.14初始化的项目目录比较简单,少了pages、plugins等目录,入口为app.vue: ...

October 5, 2024