基于 Nuxt3 的 SSR 项目开发准备 -- Nuxt3 生态
-
(一个项目开始前的准备工作)
-
这些准备的必要性:
-
- 提前进行较丰富的调研,选择符合需求的生态,这有利于项目开发过程中的避坑,也避免因选错或不知而导致某些需求误认为无法实现的问题发生,也可避免开发时没必要的重复造轮子,同时也可借助对这些生态的提前了解来调整和优化某些不太合理的设计方案(毕竟设计很少会了解开发的困难,一份好的网站需要开发与设计共同优化)
开发原则 -- 设计模式七大原则
-
开发时,脑袋里尽可能带上这些原则去思考问题,提高代码技术和代码质量
-
一、单一职责原则
单一职责原则(SRP:Single responsibility principle)又称为单一功能原则: 它规定一个类应该只负责一项职责
- 二、接口隔离原则
接口隔离原则(Interface Segregation Principle,ISP)的定义:客户端不应该依赖它不需要的接口类,类之间的依赖关系应该建立在最小的接口上。一句话,就是实现接口的类中,有多余的方法时,需要将接口进行拆分。
- 三、依赖倒置原则
依赖倒转原则(Dependency Inversion Principle,DIP)的定义: 程序要依赖于抽象接口,不要依赖于具体实现。简单的说就是要求对抽象进行编程,不要对实现进行编程,这样就降低了客户与实现模块间的耦合。
- 四、里氏替换原则
里氏代换原则(Liskov Substitution Principle,LSP)的定义: 所有引用基类的地方必须能透明地使用其子类的对象,子类可以扩展父类的功能,但不能改变父类原有的功能。
- 五、开闭原则
开闭原则(Open Closed Principle,OCP)的定义是: 一个软件实体如类、模块和函数应该对扩展开放,对修改关闭。模块应尽量在不修改原代码的情况下进行扩展。
- 六、迪米特法则
迪米特法则(Law of Demeter,LOD),有时候也叫做最少知识原则(Least Knowledge Principle,LKP)定义是: 一个软件实体应尽可能少地与其他实体发生相互作用。迪米特法则的初衷在于降低类之间的耦合。由于每个类尽量减少对其他类的依赖,因此,很容易使得系统的功能模块独立,相互之间不存在(或很少有)依赖关系。迪米特法则则不希望类之间建立直接的关系。如果真的有需要建立联系,也希望能通过它的友元类(中间类或者跳转类)来转达。
- 七、合成复用原则
合成复用原则的定义是: 原则是尽量使用合成/聚合的方法,而不是使用继承。聚合用来表示“拥有”关系或者整体与部分的关系: 代表部分的对象有可能会被多个代表整体的对象所共享,而且不一定会随着某个代表整体的对象被销毁或破坏而被销毁或破坏,部分的生命周期可以超越整体。例如,班级和学生,当班级删除后,学生还能存在,学生可以被培训机构引用。
- 函数式编程核心 -- 柯里化
柯里化 (Currying),利用纯函数、闭包这类手段提高函数的可复用性和封装性,避免把过多的变量直接暴露在全局造成全局污染
工具及框架选型
包管理器的选择 -- pnpm
- 选择原因:
-
- pnpm 可解决 npm 和 yarn 带来的深层依赖或包之间不隔离的 bug -- npm2 存在过于深层依赖在window出现找不到包的问题,npm3和yarn则存在依赖包被强行扁平化之后,可以相互访问的问题(项目中即使package.json中没有安装依赖,而某个依赖包安装了其他依赖,在项目中也可以正常import和build,这存在安全问题)
-
-
- npm 和 yarn 都会出现若依赖包之间
-
-
- pnpm 安装依赖耗时少,node_modules体积小
-
- pnpm 可以确保项目中的所有可用依赖包都是在package.json中存在的,依赖包更明确更清晰
-
- 小技巧:若项目中存在这样的情况:需要使用同一个依赖包的多个不同版本,那么可以通过包管理工具的别名功能
pnpm add lodash1@npm:lodash@1
pnpm add lodash2@npm:lodash@2
-
- pnpm 的两个关键设置 .npmrc
shamefully-hoist=true // 默认值:false; 默认情况下,pnpm 创建一个半严格的 node_modules,这意味着依赖项可以访问未声明的依赖项,但 node_modules 之外的模块不行。为了确保严格遵循依赖包必须明确存在于package.json中,开启此功能
strict-peer-dependencies=false // 默认值:false; 启用了此选项,那么在依赖树中存在缺失或无效的 peer 依赖关系时,命令将执行失败。为了确保依赖包自身问题直接影响项目,确保这个关闭的状态
- 引用链接:pnpm.io/zh/aliases
框架选择 -- Nuxt3
- 选择原因:
-
- 近期项目中使用过Nuxt3,避免耗时的摸索
-
- Nuxt3 是近年流行 SSR 框架之一,适合有 SEO 需求的网站
-
- SSR 框架拥有自己的服务端,便于前端开发者封装一些中间层做数据转换,相比以往把逻辑直接暴露在前端更安全,将某些逻辑解偶在中间层也更利于维护和服务端渲染
-
- SSR 框架都拥有 SSG 生成功能,对于某些 SEO 需求更高的页面,可通过生成 SSG 页面来提高 SEO 评分,SSR+SSG 也是提高 SEO 友好度策略之一
CSS框架选择 -- SASS + UnoCSS
- 选择原因:
-
- sass + UnoCSS 可同时提高项目中的 class 复用性、可维护性
-
- sass 带嵌套语法及其丰富的内置功能,更利于高效开发
-
- UnoCSS 号称比Windi CSS, Tailwind CSS, and Twind这类CSS解决方案更优的样式框架,因为这类CSS框架简洁易上手,所以这里选择一个新的尝试感受未尝不可
-
-
- 这类 CSS 框架为为了解决以往项目中无意义的 class 名称过多导致难维护的问题;项目中很多样式没有必要写一个独立的class,但如果写过多的style又会导致阻塞渲染,于是这类为我们预置的切片化 CSS class 就很好的解决了这个问题;
-
代码管理
- git
- husky: 编写 commitlint.config 确保 commit 符合自己项目的规范
- eslint + prettier: 结合 vscode 插件高亮提示代码错误,降低开发错误率;开发者之间统一代码格式,避免不同的代码格式化导致commit记录混乱不清
Git commit 规范
- 每一条 commit 尽量遵循只属于一个功能点的规则,易于问题追溯和回滚
- 每一条 commit message 中必须带上对应的[任务ID],便于后期追溯相应的需求
单测框架选择 -- vitest
- vitest 相对于 jest 更加高效,vitest 拥有多线程并行跑测试脚本的特性
- 一旦项目中存在单测,则需要加入到 git precommit 钩子中,确保每一次 commit 于单测保持一致
状态管理选择 -- pinia
- Pinia 相对于以往的 vuex 属于轻量级的状态管理工具,开发更高效
- Pinia 的切片化特点相比 vuex 更利于组件开发时的封装隔离,我们在封装组件时可以创建组件自己内部的状态共享和控制向外界暴露指定状态,这更利于组件隔离和组件迁移
- Pinia 支持 Nuxt 这类 SSR 框架
- 【注:项目中不可滥用状态贡献,我们应该只把需要共享的状态放入状态管理器中,避免共享大量冗余数据造成内存浪费,特别是在 SSR 框架中,若在 pinia 的某个状态器中开启了 sessionStorage或localStorage功能,容易造成数据撑满存储器的问题】
动画引擎选择 -- animejs
- 一个 SSR 项目一定是为了向外作宣传,那必不可少的一定是首页的动画效果,为了高效且性能更优,就自然需要一个动画库作辅助
- animejs 是一款稳定且使用很广动画引擎,其中带有丰富的动画效果,他的 @types/animejs 仍在维护中,可以放心使用
移动端适配方案 -- UI 框架选择 -- Vant 4
-
Vant 4 是市场上应用广泛的移动端适配方案之一
-
Vant 4 提供了丰富的接近原生设计的移动端组件,无需开发重复造轮子,若样式风格不满足,Vant 可开放了自定义样式的接口,可以满足各种风格需求
-
Vant 4 已经为开发提供了常用且可靠的 CSS 移动端适配方案,可兼容市场上大量常用机型,大大减少了开发需要解决的适配和兼容问题
-
Vant 4 基于 vue 3 开发,且提供了部分好用的组合函数
-
支持 Nuxt 3
-
Vant 4 官方 API:vant-ui.github.io/vant/#/en-U…
-
参考文档 1:juejin.cn/post/721105…
-
参考文档 2(解决闪屏问题):juejin.cn/post/712794…
其他技术栈选择
- Vue 3: 在项目中会结合情况采用 template + jsx(tsx) 的语法, template 虽然相比 jsx 编译要快些(少一层转换),但是 jsx 语法更接近于 js,在 Typescript 环境下更合适,也更利于 vscode 校验语法问题
- Typescript 4
- eslint
- prettier
- VueUse:一个提供了大量辅助工具的 vue3 组合函数
- mitt: 事件广播工具;广播在项目中应确保少用,仅在需求必须依靠广播才能实现的情况下使用,滥用广播会造成代码问题追溯困难
- node-fetch-native: 服务端(node端)发起 fetch 请求的工具包
授权管理方案 -- OAuth 2.0
- OAuth 2.0 是近年来很常用的授权方案
- 相比以往的授权方案,更加安全,用户体验更好,避免密钥暴露在cookie之类问题的同时,还满足了各个服务之间共享登录状态及相关信息的需求
- 特别是现在微服务的时代,项目的各个模块都相互隔离,以往的共享token显然是非常危险的方案,OAuth 2.0 就提供了良好的共享登录和授权的解决方案
- 当然 OAuth 2.0 是需要基于一台独立的服务的,一定是需要付出额外的资源负担的,但是对于中/大型项目,这也是最优选择
一些重要概念记录
- Tree Shaking
-
- Tree Shaking 是近年来基于ESM语法诞生的新打包算法,相比以往的一股脑打包更优
-
- 对于 js 模块,代码必须是基于 ESM 的 export 和 import 语法来引入模块的,只有这样 Tree Shanking 才能在打包编译时通过识别 import 来确定哪些模块未被引用,从而被过滤去除,减小打包后的体积
-
- Vue3 的实现中本身带有 Tree Shaking,Vue3 会通过 import 及其相关指令(如 ref 这些)去识别哪些组件和代码未被使用,且会打上标记便于打包工具去识别,vue3编译器会严格遵守ESM规范,从而让打包工具更好的实现 Tree Shaking
-
- 对于 CSS 的 Tree Shaking,则在使用 Vue3+Vite的环境下,在严格使用内置的 @apply 引入公用的 CSS 模块的情况下,打包工具也能做到对 CSS 模块的 Tree Shaking
-
- 且 CSS 的 Tree Shaking,还可通过开启打包工具的 css: { modules: true } 这样的配置,把组件内的样式会自动本地作用域化(组件内的类名会被哈希化,转换为独一无二的随机字符串类名,例如 .title 类名可能会变成 .module__title___3jRepository 这样),这时未使用的会被安全删除
-
-
- 上述功能开启方式有:
-
-
-
- (1)在Vite中,设置vite.css.modules为true
-
-
-
- (2)nuxt.config.ts
-
export default defineNuxtConfig({
css: {
modules: true
}
})