React.js 结合 Next.js 的入门与 Snapaper 完全重构

Posted ·1393 Views·5433 Words

背景

申请季开始进入尾声...

前段时间有幸和「哈陆 Halu」一位去年刚入学加拿大滑铁卢大学的 dalao 通过交换友情链接认识了,他的博客链接是 → https://halu.lu ,保存在「伙伴」栏目名为「無位小站」。借着讨论 Nuxt.js 的机会和他聊了一聊,了解了一些他参加的 Co-op 项目和滑大数学系的情况... 他在博文中有提到他收到的另一所学校录取——华盛顿大学(也是 UW 🙂 ) 其世界综合排名会考前很多 (在  2020 US News 世界大学排名中最靠前,第 10 名,实属 dalao),但是最后没有选择去的原因应该还是因为滑铁卢大学独有的 Co-op 项目... 大概意思就是一共 5 年大学本科,每年有 3 个学期,从第一年开始 Co-op 项目即在其中包括一个学期的 Work Term。在 Work Term 中学生可以申请在 7000+ 个合作企业中选择做带薪实习工作,根据官网所述最低总收入为 42,000 加币还是非常可观的。工作目的地包括谷歌等大厂,可选择在加拿大或其他国家(比如美国硅谷等)。本科毕业后就会拥有 2 年工作经验,学习与工作相交的大学体验听起来非常啊梅子英啊 🙂

扯太多了....但是他也谈到他的第一年 Co-op 申请中大多数的职位都有对于 React.js 的技能要求,他也是属于要 React 会 Vue 又没有时间现学的状态吧。不过 React.js 的生态确实非常成熟和多元,各个大厂也是大多使用其作为前端框架。比如知乎、阿里云、腾讯云等,当然 Vue 也是在被 Bilibili 使用的... 各种大型项目中的广泛应用更是增加了学习它的必要性,所以赶紧来入门下 React... 不过有了 Vue.js 的一些基础后入门应该算是蛮快的,两天就重构完了 Snapaper (https://www.snapaper.com) 呢 🙂

 

React 入门

React.js 当然是有中文文档的 → https://react.docschina.org,只是之前选择热门框架学习的时候被 React 的入门教程惊了,上来就是井字棋、状态、生命周期、类/函数组件...不像 Vue.js 官网给出的简介视频,直观明了(才不是因为自己太菜了) 从 Vue.js 开始学习一方面是因为作者是国人(停止偏见!),也是因为 React.js 纯纯上手会赶紧比较麻烦,JSX 语法也是需要学学的新东西。总之 Vue.js 的设计哲学感觉就有一些符合小白逻辑,模板 + 配置快速上手自然也容易一些。那说回 React 入门,在入门 Nuxt.js 时就注意到其文档中提到 Next.js 灵感起源的引用,Next.js 即是辅助 React 进行快速服务端渲染、路由免配置的工具吧...不过还是先从官方提供的默认项目构建模板 create-react-app (https://www.npmjs.com/package/create-react-app) 开始探索,大概和 Vue-Cli 是一个存在的道理吧。通过 npx (一个 NPM 内置 Package 运行工具,虽然我也不知道是啥用) 创建模板项目,具体见 → https://react.docschina.org/docs/create-a-new-react-app.html#create-react-app  目录结构会和 Vue-Cli 有一丝类似,通过 react-scripts start 来启动开发服务器。

不同于 Vue.js 中聚合模板和 JavaScript 的 .vue 单文件组件,React 中使用 JSX 语法来书写页面,文件类型为 .jsx (或者 .js) 具体可见 https://react.docschina.org/docs/introducing-jsx.html ,它将标记与逻辑耦合,也就是在 JavaScript 中加入标记语言 (如 HTML) 支持,典型的例子是:

return (
    <div>
        <h1>Hello World</h1>
    </div>
);

↑ JSX 语法

React 类拥有众多子类组件,创建一个组件的方式有两种,函数组件与类组件。也就是 Function 和 Class。函数组件会在性能上比类组件好但是是不支持 this、state 状态等特征的,需要通过 React Hooks (https://react.docschina.org/docs/hooks-intro.html) 来实现 (具体之后再继续研究吧),例子大概如下:

// 函数组件
function FComponent(props){
    return(
        <div>
            Hello World
        </div>
    )
}

// 类组件
class CComponent extends React.Component {
    constructor(props){
        super(props)
        this.state = {
            props: this.props.xxx
        }
    }
    render() {
        return(
            <div>
                Hello World
            </div>
        )
    }
}

↑ 两中创建组件方式

需要注意的是在类组件中是通过 Constructor 构造函数接受组件传递的参数的,并且必须使用 super(props) 来使用 this.props 获取参数。只有在构造函数中可以直接通过 this.state 来定义状态数据,在类内必须通过 this.setState({key:value}) 来更新或设定状态数据,对于已存在的状态数据同样通过 setState 将进行浅合并更新。React 中 state 状态的大概理解就是 Vue.js 相对的 data 函数(可能是吧,用起来像),通过更改状态即可动态地更新 UI 界面。

函数组件中的返回值与类组件 render 方法的返回即为该组件需要渲染的模板,在渲染时调用其他已定义模板只需通过 <组件名></组件名> 标签来调用渲染其他模板即可,大概例子如下:

// 模板规定必须以大写字母开头
import Footer from "/components/footer"

export default class Index extends React.Component {
    ...
    render() {
        return (
            <div>
                <Footer></Footer>
            </div>
    }
}

↑ 调用模板样例

通过 create-react-app 大概将基础知识熟悉到这儿就准备开始探究 Router 路由配置了,但是有了之前对于 Nuxt.js 酥爽无感的路由使用经验后当然就毅然决然地选择应用 Next.js (https://www.nextjs.cn) 了。正好之前浏览器引入 Vue.js 的粗糙项目 Snapaper 刷题网站年久失修,就拿它开刀啊不是动土啊不是开盘啊不是折腾了 🙂

 

Next.js 使用

路由配置

Next.js 中同样不需要手动配置路由,通过 src/pages 目录结构来自动生成路由配置,动态路由格式是 [props].jsx。当然 src 目录是不必要的,根目录 pages 也会被自动遍历生成路由。具体可以参考之前关于 Nuxt.js 的文章:

博客 Nuxt.js 移植重构与服务端渲染入门实现

ID: 659  发布于: 2020-03-13 20:09:20

 

CSS 预渲染

Next.js 中内置的是对 Sass/Scss 的 CSS 预渲染支持,可以直接 import 引入,使用 Less 和 Stylus 则需要安装对应插件。不过需要注意的是貌似在 JavaScript 中直接通过 <style>{``}</style> 书写 CSS 时不支持 CSS 预渲染,这点不同于 .vue 单文件中对于 <style lang="sass"></style> 的支持。在 Netx.js 中引入全局样式可以通过在 pages/_app.jsx 中引入来实现,_app.jsx 即为 一个默认套壳所有页面的渲染都要经过它,修改其便可以定制所有页面初始化时的操作,样例可见下一节。

 

路由与进度条

不同于 Nuxt.js 的是 Next.js 没有内置加载进度条 (虽然上次 Nuxt.js 也没用原生的),这次加载进度条也同样是在路由改变时的拦截函数中实现的,同样使用 NProgress,同样也是通过修改 pages/_app.jsx 来定制,样例如下:

// 全局样式引入
import "../styles/global.scss";

// Nprogress 进度条引入与配置
import NProgress from "nprogress";
import "nprogress/nprogress.css";
NProgress.configure({
  easing: "ease-in-out",
  speed: 500,
  showSpinner: false,
  trickleSpeed: 200,
  minimum: 0.2,
});

// React Router 引入以配置
import Router from "next/router";

// React Router 配置加载进度条
Router.onRouteChangeStart = (url) => {
  NProgress.inc();
};
Router.onRouteChangeComplete = () => NProgress.done();
Router.onRouteChangeError = () => NProgress.done();

function MyApp({ Component, pageProps }) {
  return <Component {...pageProps} />;
}

export default (Router)(MyApp);

↑ pages/_app.jsx 样例

不同于 Nuxt 中内置的 <nuxt-link></nuxt-link> 路由跳转标签,Next.js 中路由跳转需要引入和使用 next/link 库来实现,使用样例如下:

import Link from "next/link";

...
<Link href="/">Back to Home</Link>
...

↑ next/link 使用样例

在组件 (类组件为例) 中获取 React Router 的参数,如当前路径等时需要使用 withRouter 或者 useRouter 钩子 (https://www.nextjs.cn/docs/api-reference/next/router),引入自 next/router 将 router 作为私有变量传递进组件类。样例如下:

import { withRouter } from "next/router";

class thisComponent extends React.Component {
    constructor(){
        this.state = {
            route: this.router.route
        }
    }
}

export default withRouter(thisComponent);

↑ withRouter 使用样例

 

双向绑定

不同于 Vue.js 中内置的数据双向绑定 (https://cn.vuejs.org/v2/guide/forms.html),React 中需要通过数据改变传参回调函数来手动配置数据绑定,在内容值改变时触发 onChange 并通过一个回调函数来修改状态数据,例子如下:

...
handleChange = (e) => {
    this.setState({ subject: e.target.value });
}
...
<Input onChange={this.handleChange} />
...

↑ 手动双向绑定样例

 

HTTP 请求

同样的还是使用惯用的 axios.js 完成 HTTP 请求,不过 axios 也提供了 react-axios 的库来更优雅的数据获取方法,可见文档 → http://axios-js.com/zh-cn/docs/react-axios.html ,通过 Helper 组件来完成请求,GET 请求获取后端数据组件样例如下:

<Get
  url="xxx"
  onSuccess={(response) =>
    this.setState({
      display: true
    })
  }
  onLoading={() =>
    this.setState({
      display: false
    })
  }
>
  {(error, response, isLoading) => {
    if (error) {
      ...
      return (
        <div>
          <p>{error.message}</p>
        </div>
      );
    } else if (isLoading) {
      return (
        <div>
        ...
        </div>
      );
    } else if (response !== null) {
        return (
        <div>
          {
              // 通过 map() 变量数据
              response.data.cates.map((item, index) => {
              ...
              })
          }
        </div>
        )
    }
    return (
      <div>
      ...
      </div>
    );
  }}
</Get>

↑ react-axios 使用样例

需要注意的是不同于 Vue.js 中提供的 v-for 指令,React 直接使用 JavaScript 遍历的函数方法来实现列表数据渲染。

 

后记

Ant Design (https://ant.design) 最初就是基于 React.js 的 UI 框架,当然也是比 Ant Design Vue 更早地更新到了 4.0 的版本,也就在 Snapaper 中应用了。于是这次的重构又是不同的设计风格了... 上截图吧

React.js 由 FaceBook 团队维护,生态非常健全, 比如 React Native 可以用 React 来写原生应用听起来真香、Redux 类似于 Vuex 但是 Vuex 还没搞懂等。有 React Hooks 等最近更新的特征还有经过很多大型项目历练总结提取的最佳实践等还有很多很多可以和值得深入的内容,慢慢继续探究吧 🙂

最后,5 月这个期末考试月希望好运,当然也希望能和 Halu 滑铁卢大学见。

Comments

Leave a comment to join the discussion