react 基础工程(react-redux & react-router)

vue 已经很熟悉。react 虽然也用过,但是对于整体的搭建还不是很熟。这次就使用 react-redux(^8.0.5)react-router(^6.11.2)
结合上次的《从零搭建前端工程》,搭建一个基础的 react 项目。加上一点注释,后面有用到也可以代码直接拷。

代码仓库:github

一、Router

当前最新版本为 v6,据说有些大改动,旧版本请参考《升级指南》。

路由文件声明:

import { createBrowserRouter, Navigate } from 'react-router-dom';
import Home from '@/views/Home';
import NotFound from '@/views/NotFound';

const router = createBrowserRouter([
  {
    path: '/home',
    element: <Home />,
  },
  {
    path: '/game',
    // element: <CardGame />,
    async lazy() { // 路由页面懒加载,也就是真的访问到再下载,打包也是独立的 js
      const comp = await import('@/views/CardGame');
      // 这里的 default 是 webpack 的处理,会把 export default 挂在 default 属性上
      return { Component: comp.default };
    },
  },
  {
    path: '/demo/:id',
    // element: <Demo />,
    async lazy() {
      // 注意:这里要 loader 结果返回,才会渲染组件。也就是会阻塞页面。
      const comp = await import('@/views/Demo');
      return { Component: comp.default, loader: comp.loader };
    },
  },
  {
    path: '/', // v6 的重定向改为 Navigate 组件
    element: <Navigate to={'/home'}/>,
  },
  {
    path: '*', // 404页面兜底,我觉得改成和上面的一样的重定向处理应该也可以
    element: <NotFound />,
  },
]);

export default router;

然后路由声明对象的使用:

import { RouterProvider } from 'react-router-dom';
import router from '@/router';

function App() {
  return (
    <RouterProvider router={router} />
  );
}
export default App;

Read More

一次前端性能优化的过程(多图)

性能优化听多了,又是各个注意点,又是监控什么的,如《JS性能优化探讨》《前端性能监控》,真正遇到了还是得具体场景具体分析。

一直觉得我的个人网站首页(现在不会了)很慢,进去后背景和熊猫头渲染得特别慢。当然这个慢是相对的,就我做过的 TO B 系统单单是前置接口的获取都要几秒了,只是有 loading 效果看起来不难受。但是我这个网页啥都没有,就那么几个图片和动画,着实不该。那就实际一步步看看吧。

一. 服务器连接速度

直接百度搜网站访问速度测试就有很多工具网站。一看都没啥问题,除了极个别的比较远的省份(我这是买的阿里云深圳的服务器)其他都在一秒以内。再 ping 一下,也可以看到并无延迟。也就是 DNS 解析和访问并没有问题。

二. 减小图片体积

那首先考虑资源图片过大,因为确实用了比较高清的图片,那就在不糊的情况下尽量缩小体积。兴冲冲地更新一看,没啥变化。还是有那个等待过程。那好吧,一不做而不休,我尝试把其他图片都去掉,只留下一个熊猫头,还是很慢,就证明不该是图片背锅。当然缩小体积是没有问题的。省流量嘛。

三. 使用 Devtool Performance

那就直接用工具看吧,打开开发者工具,切到 Performance,启动录制看看效果。找一下,在 Timings 那里看到 FP FCP 等事件,这里就是影响视觉观看的主要节点了。当然前面还有 Parse HTML 或者 Function call 等等事件。主要看 Parse HTML,因为要解析了 html,才会加载一系列资源,可以看出这一步并没有什么问题,也是很快就响应了。但是到 FCP 却隔了好几秒。也就是中间有东西耽搁了。
看了一下,是必要的 js 文件加载。有两个,一个是 app.***.js,基础的业务 js, 一个是 chunk-vendor.***.js,vue 框架的打包产物。毫无疑问,这两者都是不可或缺的。appjs 比较小还好,很快就获取完了。chunk-vendorjs有 300+kb,耗时几秒。可以看到它加载完后,很快就 FCP 了。所以它的加载就是问题关键了。

四. js 文件太大?

讲道理,300+kb 的 js 文件不算小,但也不能叫很大啊。怎么要好几秒才能加载完?当然我的服务器没有加速功能,如果放到 CDN 服务,我想应该效果会好点。但是没有这条件,那只能另外想想办法。我代码里用了 Element-UI,测试了一下确实是按需引入,去掉几个组件后,体积变小了。
但,这些就是要用到的,减不了。路由页面用的也是 import() 动态引入,也就是到了真正访问那个页面组件,才会去加载相应的 js。再说了业务代码也不会打包到 vendor 去。那体积没法减了。然后在自己本地测试了一下,以及线上的缓存状态下,页面出现都很快,更加印证了我的猜测,就是这个 “大 js 文件” 导致了页面渲染慢。(当然最好有个 CDN 什么的加速可以验证…)

事情本到这里就结束了(啥都没干,555…),结果又看到一个神奇的网站,谷歌的性能分析网站,地址:https://pagespeed.web.dev (可能需要科学上网)

Read More

文件切片上传(断点续传)

上传文件体积一大,要等很久。如果只是水管小,等一会也就算了。但是如果网络差,上传到 99% 突然断网了又重新开始,就比较难受了。所以又有了文件切片进行断点上传的方法。

上传的文件对象是一个 File 对象,然后它是 Blob 对象的一个子类。再然后它有一个 slice 方法,用这个方法就可以对文件进行切片,就和数组的这个方法一样。用法如示例。

话不多说直接上代码(里面代码是问了 chatgpt 然后改的,哈哈)。把下面两份代码分别保存为 index.htmlserver.js,安装好依赖,然后启动执行 node server.js,访问 http://localhost:3000

前端也大概美化了一下上传组件,支持点击选择文件或者拖拽文件上传。原生的 <input type="file" ></input> 组件的样式应该没人用吧,那么丑…。其中的 loading 效果出自网上大佬的《纯css实现117个Loading效果》,里面挺多效果不错的。

Read More

nginx 的基础用法 & linux(centos)下支持 https 和 http2

其实之前写过一篇相关的《记录下 nginx 使用配置》,关于 nginx 的一些稍微复杂的场景。然后发现日常的基础用法,反而记不住。这里就记录一下。

安装,mac 可以用 brew 下载。windows 的也很简单,去官网下载个压缩包就行了。至于 linux 的,就网上搜搜啦,我记得也很简单。

# 安装
brew install nginx

#查看
brew info nginx

查看信息,可以看到配置文件在 /opt/homebrew/etc/nginx/nginx.conf

命令

# 启动
nginx

# 刷新,改了 nginx.conf 文件,要重新生效
nginx -s reload

# 关闭
nginx -s stop

默认端口是 8080。所以直接访问本地地址 http://localhost:8080/,看到有显示 Welcome to nginx! 字样就是启动成功了。

其实 nginx.conf 已经写了例子和注释,这里只是稍微补充点。

root:资源文件夹
index: 默认的 index 文件

location / {
  root   /电脑地址/nginx;
  index  index.html index.htm;
}

如果要配置不同路由访问不同的文件,root 要改成 alias,比如访问 http://localhost:8080/page

location /page {
  alias /电脑地址/nginx;
  index page.html;
}

当然最好是写个兜底返回。在 vue 或者 react 使用 router 时,兜底返回默认 index.html。不然每增加一个路由就要写多一个配置很麻烦。

location ~* ^/* {
  root   /电脑地址/nginx;
  try_files $uri $uri/ /index.html;
}

注意
root 与 alias
两者区别在于 nginx 如何解释 location 后面的 url

root:
语法:root path
默认值:root html
配置段:http、server、location、if
处理结果:root 路径+ location 路径

alias:
语法:alias path
配置段:location
处理结果:使用 alias 路径替换 location 路径

所以用正则匹配写路径的要注意写好 root 或 alias,不然找不到资源就会出现 403!

可以写多个server,启动多个服务

server {
  listen       3000;
  server_name  0.0.0.0;

  location / {
    root   /电脑地址/nginx;
    index  3000.html 3000.htm;
  }
}

代理 ~ 为区分大小写,~*为不区分大小写,其他符号则请查询官网啦。

location ~ /api/* {
  proxy_pass   http://localhost:3000;
}

https服务,声明好证书即可。这里只是把其中证书配置列出来,实际上 nginx.conf 的例子还有挺多配置,一般我们不是运维,应该默认就够了。作为调试用,可能都不需要启动到 https server。

server {
  ssl_certificate      /电脑地址/nginx/cert/cert.pem;
  ssl_certificate_key  /电脑地址/nginx/cert/server.key;
}

开启文本压缩

http {
  gzip on;
  # 压缩比例,比例越大,压缩时间越长。默认是1
  gzip_comp_level 6;
  # 哪些文件可以被压缩
  gzip_types text/xml text/plain text/css application/javascript application/x-javascript application/rss+xml;
}

linux 下支持 https 和 http2,也是网上搜集的,实操了可以,这里记录一下。

# 安装依赖
yum -y install gcc zlib zlib-devel pcre-devel openssl openssl-devel
# 解压缩
tar -zxvf nginx-1.23.4.tar.gz
cd nginx-1.23.4
# 执行配置
./configure --with-http_ssl_module --with-http_v2_module
# 编译安装
make
make install
# 默认安装在
/usr/local/nginx
# 启动
/usr/local/nginx/sbin/nginx
# 刷新配置启动
/usr/local/nginx/sbin/nginx -s reload
# 关闭
/usr/local/nginx/sbin/nginx -s stop
# 修改配置
vim  /usr/local/nginx/conf/nginx.conf

从零搭建前端工程(下)

文章内容太多,分为上下两部分,这里是下半部分。上半部分在《从零搭建前端工程(上)》,此篇的内容有:
4. 使用 eslint + prettier 让代码健壮和优雅
5. 使用 husky + lint-staged 强制增量各类检查
6. 使用 @commitlint/cli 规范提交信息
7. 使用 埋点(性能 + 错误) 让项目运行更好



四、使用 eslint + prettier 让代码健壮和优雅

到 eslint 了,是不是让人又爱又恨。刚接的时候应该很不爽吧,动辄就来个错,这也错那也错。满屏尽是红 error。其实都是没有配好,也有处理好。eslint 是非常必要的,可以尽早发现一些错误及不合理,也可以统一一些写法,减少冲突等。比如,vue template 的属性顺序,import 的顺序等等。

循例先上代码示例,在项目根目录创建一份 .eslintrc.js,记得前面有个点的,上书代码:

module.exports = {
  env: {
    // 关键字指定你想启用的环境
    browser: true,
    es2021: true,
  },
  extends: [
    // 一个配置文件可以被基础配置中的已启用的规则继承
    'plugin:vue/essential',
    'plugin:vue/recommended',
    'standard',
    'plugin:prettier/recommended',
  ],
  parserOptions: {
    // 允许你指定你想要支持的 JavaScript 语言选项
    ecmaVersion: 12,
    sourceType: 'module',
  },
  plugins: [
    // 支持使用第三方插件,检查自定义的语法
    'vue', // 省略了 eslint-plugin- 前缀,插件全称为 eslint-plugin-vue
    'prettier',
  ],
  rules: {
    // 直接声明的 eslint 规则
    semi: ['error', 'always'], // 规则为:需要结束分号,优先级为 error,即抛错
    // 当最后一个元素或属性与闭括号 ] 或 } 在 不同的行时,要求使用拖尾逗号
    // 当在 同一行时,禁止使用拖尾逗号。https://eslint.bootcss.com/docs/rules/comma-dangle
    'comma-dangle': ['error', 'always-multiline'],
    'no-console': ['error', { allow: ['info', 'warn', 'error'] }],
    'no-unused-vars': ['error', { args: 'after-used' }],
    // "plugin1/rule1": "error" 配置定义在插件中的一个规则的时候,必须使用 插件名/规则ID 的形式
    'prettier/prettier': 'error',
  },
  globals: {
    var1: 'writable', // 对 var1 这个全局变量允许重写
    var2: 'readonly', // 对 var2 这个全局变量只允许读取
    var3: 'off', // 不支持 var3 这个全局变量
  },
};

说一下可能配置较多的。

Read More