现代框架开发官网
一、项目背景
维护过 灵犀官网、企业邮官网、免费邮官网 均使用 jquery + webpack多页面打包的方式
开发起来较为繁琐
新的官网项目,想使用现代前端框架,但SPA应用不利于SEO
使用SSR方案又依赖运维,增加维护和沟通成本
二、SSG vs 预渲染
SSG(静态站点生成):
特点:SSG 是一种构建时生成静态 HTML 页面的技术。在构建过程中,服务器会根据应用的路由和数据,生成一系列静态 HTML 文件,这些文件可以直接提供给用户,不需要实时的服务器请求。每次页面访问时,都会返回预先生成的静态页面。
优点:SSG 可以提供非常快速的页面加载速度,因为它们是预先生成的静态文件,不需要服务器动态生成页面内容。另外,SSG 也有利于搜索引擎优化(SEO),因为搜索引擎可以直接访问静态 HTML 页面。
适用场景:适用于内容相对固定、不经常更新的网站,如博客、公司官网等。Jekyll、hexo、Gatsby、vite-ssg
预渲染:
特点:预渲染是在构建时生成静态 HTML 页面的过程,但它只会为特定的页面进行预渲染,而不是整个网站。在构建过程中,预渲染工具会解析应用的特定页面,并生成相应的静态 HTML 文件,这些文件会在用户访问时直接返回。
优点:预渲染可以针对特定页面进行优化,可以选择性地预渲染那些对性能和用户体验影响较大的页面。它可以在构建时处理异步数据获取,确保页面在用户访问时已经包含了相关数据,提供更快的初始加载速度。
适用场景:适用于有部分页面需要实时数据的网站,如商品详情页、新闻文章等。
三、SEO标签
<title>:指定网页的标题,是搜索引擎结果页面(SERP)中显示的主要标题。
<meta name="description">:提供网页的描述,用于 SERP 中显示网页的简短描述。
<meta name="description" content="网页描述">
<meta name="keywords">:指定网页的关键词,用逗号分隔多个关键词
<meta name="keywords" content="关键词1, 关键词2, 关键词3">
<meta name="robots">:指定搜索引擎爬虫的行为。
<meta name="robots" content="index,follow"> <!-- 爬取并索引该页面的链接和内容 -->
<meta name="robots" content="noindex,nofollow"> <!-- 不爬取和索引该页面的链接和内容 -->
<meta name="viewport">:用于优化移动设备上的显示效果
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="author">:指定网页的作者
<meta name="author" content="作者名">
<meta name="og:title">:在社交媒体上分享时显示的标题。
<meta name="og:title" content="分享标题">
<meta name="og:description">:在社交媒体上分享时显示的描述
<meta name="og:description" content="分享描述">
<meta name="og:image">:在社交媒体上分享时显示的缩略图。
<meta name="og:image" content="缩略图链接">
<meta name="applicable-device"content="pc,mobile">:告诉浏览器页面属于什么类型设备
<meta name="applicable-device"content="pc">
企业邮官网是两套代码,不设置这个标签有可能会导致在pc搜索时,移动端网站排名靠前
react 使用插件react-helmet
vue 使用插件 vue-meta vue-head
四、最终方案
react + craco + prerender-spa-plugin + tailwindcss
五、脚手架
使用create-react-app 创建的项目默认是无法修改其内部的webpack配置的,不像vue-cli那样可以通过一个配置文件修改。虽然有一个eject 命令可以是将配置完全暴露出来,但这是一个不可逆的操作,同时也会失去CRA带来的便利和后续升级。
如果想要无 eject 重写 CRA 配置,目前成熟的是下面这几种方式
通过 CRA 官方支持的 --scripts-version 参数,创建项目时使用自己重写过的 react-scripts 包
使用 react-app-rewired + customize-cra 组合覆盖配置
使用 craco 覆盖配置
六、预渲染
prerender-spa-plugin webpack 插件
prerender-spa-plugin 是一个通用的预渲染插件,适用于各种前端框架,包括 React。
它使用 Puppeteer(一个无头浏览器)在构建过程中模拟浏览器环境,并将预渲染的 HTML 文件保存到指定目录。
prerender-spa-plugin 需要在 webpack 的配置文件中进行配置,并与其他 webpack 插件和加载器一起使用。
它支持路由配置,可以指定哪些路由需要预渲染。
prerender-spa-plugin 提供了更多的灵活性和定制选项,可以通过配置文件来设置渲染的行为和页面参数
React Snap命令行工具
React Snap 是专门为 React 应用设计的预渲染工具。
它通过在构建过程中运行 React 应用,并在客户端和服务器端渲染之间进行切换,将 React 组件转换为静态 HTML 文件。
React Snap 的配置相对简单,只需添加一个配置文件并运行构建命令即可。
它支持路由配置,你可以指定哪些路由需要预渲染。
React Snap 还提供了一些其他的配置选项,如添加 HTTP 头、设置超时时间等。
React Snap 更适合与 React 应用紧密集成,并且配置简单,适用于快速实现预渲染。prerender-spa-plugin 则更通用,适用于各种前端框架,并且具有更多的配置选项和定制能力。
打包遇到的问题
webpack5插件兼容问题 报错:
报错:Unable to prerender all routes
可以使用修改后的库
yarn add @dreysolano/prerender-spa-plugin
const PrerenderSPAPlugin = require('@dreysolano/prerender-spa-plugin')
服务器环境
报错:Can’t Use Puppeteer – Error: Failed to launch chrome
linux打包需要安装
https://stackoverflow.com/questions/59112956/cant-use-puppeteer-error-failed-to-launch-chrome
七、原子化css
大部分情况写css应该都是写一个类,然后整一堆样式进去。这种方式写多了以后,会感受到一些痛点,比如说:
取名困难
样式污染
复用难
原子化 CSS 是一种 CSS 的架构方式,它倾向于小巧且用途单一的 class,并且会以视觉效果进行命名
Tailwind CSS:
特点:Tailwind CSS是一个功能强大的工具包,提供了一系列的可复用的原子类,用于构建用户界面。它具有广泛的样式和布局类,可快速搭建页面,并支持自定义配置。
优点:Tailwind CSS具有非常灵活的设计,允许开发者根据需要选择和组合类来创建自定义样式。它的配置文件可以轻松地进行自定义和扩展,而且有大量的文档和社区支持。
缺点:Tailwind CSS生成的样式表文件相对较大,因为它提供了大量的类选择器。对于一些项目而言,文件大小可能会成为一个问题。
WindiCSS:
特点:WindiCSS是一个面向现代框架的类似Tailwind CSS的工具,它具有更好的性能和开发体验。它通过使用 Just-In-Time (JIT) 编译方式,仅生成所需的样式,从而减小了生成的样式表文件的大小。
优点:WindiCSS相比于Tailwind CSS在性能方面有所提升,同时保持了类似的开发体验和灵活性。它的配置文件也支持自定义和扩展。
缺点:相对于Tailwind CSS,WindiCSS的社区和文档资源相对较少,可能会对一些开发者造成一定的学习和使用难度。
UnoCSS:
特点:UnoCSS是另一个类似于Tailwind CSS的CSS框架,提供了一组可复用的原子类。它专注于可访问性和可定制性,并支持通过自定义配置文件进行样式的调整。
优点:UnoCSS具有良好的可访问性和可定制性,可以根据项目需求进行灵活的样式定制。它还提供了一些附加的功能,如主题切换和自动修复样式规则。
缺点:UnoCSS的用户群体相对较小,相比于Tailwind CSS和WindiCSS的知名度和社区支持较少。
className=“h-11 bg-white flex justify-between items-center px-6 shadow”
并且写响应式比较方便
className=“md:text-xl md:mt-4”
维护老项目可以使用
https://github.com/changgeee/bit-css
八:项目优化
图片压缩
在线网站:https://kt.fkw.com/yasuo.html?_ta=8780
开源工具:
ffmpeg号称多媒体的瑞士军刀,可以很完美的处理视频、音频、图片
ffmpeg -i input.jpg -vf scale=640:-1 output.jpg
python库: Pillow
使用图片懒加载
react-lazyload
和react-lazy-load-image-component
是两个常用的 React 图片懒加载插件
react-lazyload:
react-lazyload 是一个轻量级的图片懒加载库,可以延迟加载图片,当图片进入可视区域时再进行加载。
它提供了 组件,你可以将需要懒加载的图片包裹在其中。
react-lazyload 提供了一些配置选项,如占位符、阈值等,可以根据需要进行自定义。
这个库相对较小,易于使用,适用于简单的图片懒加载需求。
react-lazy-load-image-component:
react-lazy-load-image-component 是一个功能丰富的图片懒加载库,提供了更多的功能和定制选项。
它提供了 组件,可以代替 元素进行图片懒加载。
react-lazy-load-image-component 支持设置占位符、加载效果、错误处理、渐进式加载等。
这个库的体积相对较大,但提供了更多的灵活性和可定制性,适用于复杂的图片懒加载需求。
css打包优化
mini-css-extract-plugin:将 CSS 代码从 JavaScript 中分离出来,生成单独的 CSS 文件
purgecss-webpack-plugin:清除多余css,必须结合上面的插件使用
前后对比
原因是tailwindcss有太多未使用的样式,这里使用 prugecss 去除
yarn add purgecss-webpack-plugin@next -D
yarn add mini-css-extract-plugin@1.6.0 -D
webpack配置
new MiniCssExtractPlugin({ filename: "[name].css",}),new PurgecssPlugin({paths: glob.sync(`${PATHS.src}/**/*`, { nodir: true }),}),
查看&&优化 js包体积
webpack4+无需手动添加 UglifyJsPlugin
craco webpackConfig.optimization.minimize 默认设置为true
使用webpack-bundle-analyzer
进行分析
const { BundleAnalyzerPlugin } = require('webpack-bundle-analyzer'); new BundleAnalyzerPlugin({analyzerMode: 'server',analyzerHost: '127.0.0.1',analyzerPort: 8888,openAnalyzer: true, // 构建完打开浏览器reportFilename: path.resolve(__dirname, `analyzer/index.html`), }),
代码拆分的情况
使用splitChunks拆分代码
plugins:[],
configure: (webpackConfig, { env: webpackEnv, paths }) => {webpackConfig.optimization.splitChunks = {...webpackConfig.optimization.splitChunks,cacheGroups: {base: {// 基本框架chunks: 'all',test: /(react|react-dom|react-dom-router|axios)/,name: 'base',priority: 100,},moment: {test: /[\\/]node_modules[\\/]moment[\\/]/,name: 'moment',priority: 90,},commons: {chunks: 'all',// 将两个以上的chunk所共享的模块打包至commons组。minChunks: 2,name: 'commons',priority: 80,},},};return webpackConfig;}
使用babel-plugin-import按需引入
babel: {plugins: [// 配置 babel-plugin-import ant按需加载['import',{libraryName: 'antd',libraryDirectory: 'es',style: 'true',},'antd',],['import',{libraryName: 'moment',libraryDirectory: 'es',style: 'true',},'moment',],],
css异步加载
css文件的加载是会block网站渲染的 因此我们可以把非关键(非首屏)css异步加载
<link rel="preload" href="style.css" as="style" onload="this.onload=null;this.rel='stylesheet'"><link href="73131f5.css" rel="stylesheet" media="xxx" onload="this.media='all'" />
静态资源CDN
考虑访问网站的国外用户较多,需要将打包后的js,css资源放在http://storage.cowork.netease.com/upload.html
/*将静态资源css,js上传至cdn并替换*/
const fs = require('fs');
const rp = require('request-promise');
const glob = require('glob');//上传文件并获取url
async function uploadFile(filePath) {var options = {method: 'POST',// url: 'http://10.242.0.216:8080/api/pub/intranet/upload',// url: 'http://storage.cowork.netease.com/api/pub/intranet/upload',url: 'http://storage.cowork.netease.com/api/pub/file/upload',formData: {bizCode: 'common-40b61c',contentType: 'text/css',needHttps: 'true',file: fs.createReadStream(filePath),},};// console.log(rp(options))return await rp(options).then(res => JSON.parse(res).data);
}//读取文件,并且替换文件中指定的字符串
function replaceFile(filePath, sourceRegx, targetStr) {fs.readFile(filePath, function(err, data) {if (err) {return err;}let str = data.toString();str = str.replace(sourceRegx, targetStr);fs.writeFile(filePath, str, function(err) {if (err) {console.log(err);return err;}});});
}
//遍历statics文件夹,找到main_*.js
function findFile(dir, filenameReg, cb) {fs.readdir(dir, function(err, files) {if (err) {return err;}if (files.length != 0) {files.forEach(item => {let path = dir + '/' + item;//判断文件的状态,用于区分文件名/文件夹fs.stat(path, function(err, status) {if (err) {return err;}let isFile = status.isFile(); //是文件let isDir = status.isDirectory(); //是文件夹if (isFile) {if (item.match(new RegExp(filenameReg))) {// console.log('找到了', path);// arr.push(path);cb(path);}}if (isDir) {findFile(path, filenameReg, cb);}});});}});
}function test() {findFile('./build', '^main.*css$', function(css) {uploadFile(css).then(url => {console.log('----', url);});});
}// 替换静态资源
function replaceCss(htmls, dynamicValue) {findFile('./build', '^' + dynamicValue + '.*css$', function(path) {console.log(path);uploadFile(path).then(url => {console.log(url);htmls.forEach(html => {replaceFile(html, new RegExp('\/static\/css\/' + dynamicValue + '.[a-z0-9]{8}.css'), url);});});});}
function replaceJs(htmls, dynamicValue) {findFile('./build', '^' + dynamicValue + '.*js$', function(path) {console.log(path);uploadFile(path).then(url => {// /\/static\/js\/main.[a-z0-9]{8}.js/htmls.forEach(html => {replaceFile(html, new RegExp('\/static\/js\/' + dynamicValue + '.[a-z0-9]{8}.js'), url);});});});}
let htmls = [];
glob.sync('./build/**/index.html').forEach(html => {htmls.push(html);
});replaceCss(htmls, 'main');
replaceJs(htmls, 'main');
replaceJs(htmls, 'base');
replaceJs(htmls, 'moment');
replaceJs(htmls, 'lodash');// test()
相关文章:

现代框架开发官网
一、项目背景 维护过 灵犀官网、企业邮官网、免费邮官网 均使用 jquery webpack多页面打包的方式 开发起来较为繁琐 新的官网项目,想使用现代前端框架,但SPA应用不利于SEO 使用SSR方案又依赖运维,增加维护和沟通成本 二、SSG vs 预渲染 S…...

一篇文章快速认识YOLO11 | 关键改进点 | 安装使用 | 模型训练和推理
前言 本文分享YOLO11的关键改进点、性能对比、安装使用、模型训练和推理等内容。 YOLO11 是 Ultralytics 最新的实时目标检测器,凭借更高的精度、速度和效率重新定义了可能性。 除了传统的目标检测外,YOLO11 还支持目标跟踪、实例分割、姿态估计、OBB…...

AtCoder Beginner Contest 375(A,B,C,D,E,F)(大模拟,前缀和,dp,离线处理,Floyd)
比赛链接 AtCoder Beginner Contest 375 A题 代码 #pragma GCC optimize("O2") #pragma GCC optimize("O3") #include <bits/stdc.h> using namespace std; #define int long long const int N 2e5 5, M 1e6 5; const int inf 0x3f3f3f3f3f…...

认识maven
什么是 Maven? Maven 是一个开源的项目管理工具,主要用于 Java 项目的构建、依赖管理和项目生命周期管理。它提供了一种标准的项目结构和管理流程,使得开发人员能够更轻松地管理项目的构建过程,提高代码的可重用性和可维护性。 …...

OSINT技术情报精选·2024年10月第2周
OSINT技术情报精选2024年10月第2周 2024.10.16版权声明:本文为博主chszs的原创文章,未经博主允许不得转载。 1、亿欧智库:《2024中国高精定位服务产业白皮书》 报告的主要内容如下: 产业背景:在“北斗”发展态势的…...

中企通信赋能中信戴卡入选工信部颁发的2023年工业互联网试点示范名单
2024年10月17日,北京-随着工业互联网的迅猛发展,网络安全已成为国家关注的重点议题之一。日前,工业和信息化部(工信部)公布了2023年工业互联网试点示范名单,中企网络通信技术有限公司(简称“中企…...

【C语言】函数的声明与定义
函数的声明 用户自定义函数需要在main函数之前进行声明,用分号结尾。 函数的定义 用户自定义函数在main函数之后进行定义,需要写出具体形参的变量名。注意函数的返回值和返回值类型要一一对应。 函数的调用 调用时,直接使用函数名进行调用&am…...

游戏如何应对薅羊毛问题
在大众眼里,“薅羊毛”是指在电商领域,“羊毛党”利用平台、商家的促销规则,低价获取商品和服务的行为。如前不久“小天鹅被一夜薅走7000万”的案例震惊全网。 然而实际上,“薅羊毛”现象不仅存在于电商场景,在游戏中…...

Chromium html<script>对应c++接口定义
<script>:脚本元素 <script> 元素用于嵌入可执行代码或数据,这通常用作嵌入或者引用 JavaScript 代码。<script> 元素也能在其他语言中使用,比如 WebGL 的 GLSL 着色器语言和 JSON。 更多参考:<script>&…...

ollama + fastgpt+m3e本地部署
ollama fastgptm3e本地部署 开启WSL更新wsl安装ubuntu docker下载修改docker镜像源开启WSL integration 安装fastgpt先创建一个文件夹来放置一些配置文件用命令下载fastgpt配置文件用命令下载docker的部署文件 启动容器M3E下载ollama下载oneapi配置登录oneapi配置ollama渠道配…...

Linux执行source /etc/profile命令报错:权限不够问(已解决)
1.问题 明明以root账号登录Linux系统,在终端执行命令source /etc/profile时 显示权限不够 如下图: 2.问题原因 可能在编辑 /etc/profile 这个文件时不小心把开头的 井号 ‘#’ 给删除了 如图: 这里一定要有# 3.解决办法 进入/etc/pro…...

Windows 11开发全解析
Windows 11开发全解析 一、搭建开发环境 在开始Windows 11开发之前,搭建一个高效的开发环境是至关重要的。Windows 11提供了多种工具和框架,可以帮助开发者快速搭建起一个强大的开发环境。 1. Visual Studio 2024 Visual Studio 2024是微软为Windows…...
如何进行数学家式的学习思考?
如何进行数学家式的学习思考? 学生阶段的数学学习是非常重要的,对这一点很少有人质疑。一提起数学学习,一些学生、家长甚至一些教师认为,学生的数学学习往往侧重于掌握基本概念、公式和解题技巧,通过做题来巩固知识和提…...

自定义类型--结构体
目录 1. 结构体类型的声明 1.1结构的声明 1.2 结构体变量的创建和初始化 1.3不完全结构体 1.4结构的⾃引⽤ 2 结构体的内存对齐 2.1offsetof 2.2 对⻬规则 2.3 为什么存在内存对⻬? 2.4修改默认对⻬数 3. 结构体传参 4 结构体实现位段 4.1什么是位段 4.2 位段的内…...

笔试练习day7
目录 OR59 字符串中找出连续最长的数字串题目解析解法(双指针遍历)代码 NC109 岛屿数量题目解析解法代码(dfs)dfs的实现 拼三角题目解析解法(枚举)代码 感谢各位大佬对我的支持,如果我的文章对你有用,欢迎点击以下链接 🐒🐒🐒 个人主页 &…...

python 爬虫 入门 一、基础工具
目录 一,网页开发者工具的使用 二、通过python发送请求 (一)、get (二)、带参数的get (三)、post 后续:数据解析 一,网页开发者工具的使用 我们可以用 requests 库…...

金融衍生品中的风险对冲策略分析
金融衍生品是现代金融市场中不可或缺的一部分,它们通过标的资产的价格波动为投资者提供了多样的风险管理工具。随着市场的不确定性和复杂性增加,风险对冲成为企业和个人投资者的首要任务。本文将深入探讨金融衍生品中的常见风险对冲策略,分析…...

linux下建立软链接
深度学习训练中经常会遇到数据量庞大或者工程中模型报错太多导致磁盘空间不够,但是又不想修改原来在代码中写的路径,这个时候制作软连接很有作用,把占用量大的目录移到别的空闲磁盘,然后在原来的目录做一个软连接指向那个移到的空…...

MySql数据库left join中添加子查询
user表查询出数据列表(多条,如id)左连接到order表中的order_agent_id字段,并通过 order_agent_id分组,求和user_order_partner,使用COALESCE()聚合函数对未获取到和值的进行默认赋值,防止查询不…...

redis--过期策略和内存淘汰策略
redis过期策略 1、惰性删除 当客户端尝试访问某个键时,Redis会先检查该键是否设置了过期时间,并判断是否过期。 如果键已过期,则Redis会立即将其删除。这就是惰性删除。 总结:该策略可以最大化的节省CPU资源,却对内存非…...

qt QTableview 左侧 序号 倒序
本文主要在QTableview插入数据的基础上,使左边序号实现倒序,实现如下图所示。 解决办法: QTableview左侧是QHeaderView类构成的,重写QHeaderView的paintSection, 重写序号的文字内容,进而 实现QTableview …...

隧道代理IP如何帮助企业采集数据?
在数字化时代,数据已成为企业决策的重要基石。无论是市场调研、竞品分析,还是用户行为研究,高质量的数据采集都是企业成功的关键。然而,面对复杂的网络环境和日益严格的反爬虫机制,如何高效、稳定地采集数据成为了一个…...

Spring Boot知识管理系统:技术与方法论
2相关技术 2.1 MYSQL数据库 MySQL是一个真正的多用户、多线程SQL数据库服务器。 是基于SQL的客户/服务器模式的关系数据库管理系统,它的有点有有功能强大、使用简单、管理方便、安全可靠性高、运行速度快、多线程、跨平台性、完全网络化、稳定性等,非常适…...

SpringBoot1~~~
目录 快速入门 依赖管理和自动配置 修改自动仲裁/默认版本号 starter场景启动器 自动配置 修改默认扫描包结构 修改默认配置 读取application.properties文件 按需加载原则 容器功能 Configuration Import 编辑 Conditional ImportResource 配置绑定Configur…...

兼容多家品牌手机的多协议取电快充芯片
随着智能手机的普及和功能不断的增强,电池续航能力成为了用户关注的焦点,为了解决这各问题各大手机厂商推出了手机快充技术,快充协议是快充技术的核心,每家品牌手机都有自己的独家快充协议,如FCP/SCP协议是华为手机的独…...

Java和Python的不同
1. 语法差异 Java: - Java是一种强类型语言,要求在编译时明确变量的数据类型。 - Java代码块由大括号 {} 包围,如方法体、循环和条件语句。 - Java使用分号 ; 作为语句的结束符。 public class HelloWorld {public static void main(String[] args) {S…...

Moshang摩熵医药数据库
摩熵医药数据库是摩熵数科信息公司旗下的一个核心产品,专注于为医药行业提供全面的数据支持和决策服务。该医药数据库整合了中、美、欧、日等全球七十多个主流国家的数10万数据信息源,其中收载的50亿数据体系的覆盖了生物医药全生命周期数据和精细化工全…...

基于web的酒店客房管理系统【附源码】
基于web的酒店客房管理系统(源码L文说明文档) 目录 4 系统设计 4.1 系统概述 4.2系统结构 4.3.数据库设计 4.3.1数据库实体 4.3.2数据库设计表 5系统详细实现 5.1 用户信息管理 5.2 会员信息管理 5.3 客房信息管理 5.…...

潜水定位通信系统的功能和使用方法_鼎跃安全
潜水定位通信系统是保障潜水安全与作业高效的关键设备。它利用先进的声呐、无线电等技术,可精准定位潜水员位置。在水下能实现潜水员之间以及与水面的双向通信,确保信息及时传递。具备高可靠性和稳定性,即使在复杂水环境中也能正常运行。 一、…...

Golang | Leetcode Golang题解之第477题汉明距离总和
题目: 题解: func totalHammingDistance(nums []int) (ans int) {n : len(nums)for i : 0; i < 30; i {c : 0for _, val : range nums {c val >> i & 1}ans c * (n - c)}return }...