jquery抓取网页内容(我的个人博客技术栈变更与我感受中的前端技术发展)
优采云 发布时间: 2022-04-14 13:31jquery抓取网页内容(我的个人博客技术栈变更与我感受中的前端技术发展)
概述
这次的标题非常抢眼。这个文章其实是想谈谈我个人博客的技术栈的变化以及我的感受中的前端技术的发展。
事实上,对我来说,博客这个词似乎是上个世纪的事情。小时候微博很流行。我是在微博之后才知道写博客的。2017年左右,突然想找个地方输出文字,做一些记录。当时我注册了一个微信公众号,直到2019年才开始搭建这个个人博客。其实建博客可以说是酒鬼不喝酒的本意。成熟的博客生成应用相当多,不用自己折腾。但是当时我在宣传的时候买了一台阿里云服务器,只是想学习一些前端技术。这个博客是关于它应运而生的。
有时是为了实现一些功能,学习一些新技术,有时恰恰相反,把一些新东西应用到博客上,使博客成为一个完整的“冗余项目”。我博客的技术栈发生了变化,也就是刚好成为我个人技术栈的成长过程,就在这里记录一下,顺便记录一下使用NextJS的心得。
模板渲染、Session 和 jQuery
一开始,作为一个主要使用Python的后端程序员,我尝试使用Django开发博客。Django 是一个遵循 MVC 模型的 Web 框架。Python相关的应用一般性能不强,所以Django的主要卖点是快速交付、内置用户系统、管理后台、模型迁移等。博客的初始版本没多久。完成。
数据库使用 MySQL,在 Django 中使用内置的模板引擎来处理前端页面:
{% if latest_question_list %}
{% for question in latest_question_list %}
{{ question.question_text }}
{% endfor %}
{% else %}
<p>No polls are available.
{% endif %}
</p>
将静态文件放在约定的文件夹中,使用 nginx 代理。UI风格主要是通过BootStrap。毕竟我对CSS真的很陌生,有些页面*敏*感*词*也是通过框架或者搜索一些jQuery代码来解决的。这里有很多 Doucet 的博客。
Django内置了用户和组模型,管理后台也内置。登录认证也是通过默认的Session中间件实现的,OAuth登录是通过第三方库实现的。基本上,没有时间花在这些方面。最开始博客开通了登录注册和评论功能可以通过Github登录,但是后来被认为没有必要的时候就删除了。这里 UI 和业务代码是完全紧密联系在一起的。
RESTful、Docker 和 TLS
由于工作原因,我开始向全栈方向发展,开始学习JavaScript。在此之前,我经常听到三个前端框架的名字。因为一不小心加了一个 React 组,就开始学习 React。这段时间,我先把博客的后端部分提取出来,并借助Django REST框架库快速完成了这部分工作,力图使接口符合RESTful规范。毕竟,应用程序很简单,没有什么可以妥协或违反规范的。注释 去掉了这个功能,但是仍然保留了登录界面,所以做了一个JWT登录认证界面,后来删掉了。Django 部分的其余部分不再关心如何呈现用户界面,
我的服务器操作系统是 Ubuntu 18.04。在最初的部署过程中,我们需要考虑 Python 版本,以及 nginx 和 MySQL 的安装。如果以后需要迁移服务器(毕竟阿里云学生机活动才两年),也比较麻烦,所以我用了Docker,写好镜像文件后,运行容器。
在安全问题和钱包的取舍中,我选择了 Let's Encypt 的免费证书来实现 HTTPS,安装简单,每三个月自动更新一次。
SPA和SSR
在项目中成功使用React作为管理后台后,开始考虑在博客中使用。但是通常都是开发SPA单页应用,怎么解决SEO问题呢?
之前,我使用 Django 的模板引擎进行服务器端渲染,即在返回响应之前,我已经将 文章 内容和其他数据插入到 HTML 文件中,最终用户在浏览器中获取静态文件。如果用户请求到另一个页面,那么他会得到一个完全不同的静态文件。
用 React 编写的 SPA 在客户端渲染,使用虚拟 DOM,整个应用程序往往只有一个 HTML 文件,切换页面不会重新请求新页面,而是替换需要更改的组件。
那么两者的优劣势就很明显了。传统的服务器端渲染(SSR),首页打开速度更快,因为一次不会加载太多内容,对搜索引擎也很友好。毕竟爬虫可以直接获取到一个收录数据的静态 HTML 文件,但是切换页面需要替换整个页面并重新渲染,而且前后端、表现层和业务层是紧耦合的;单页应用在客户端渲染,页面切换快,数据异步获取,前后端分离,但由于首次访问获取整个应用资源,首屏加载慢,在客户端由JS渲染,爬虫很难获取到需要的数据,
NextJS:页面、路由和自定义应用
通过 React 和 SEO 这两个关键词,我发现了 NextJS,即 React 脚手架。在它的官网上,我看到了关键词SSR服务端渲染。前端经过多年的发展会倒退吗?当然不是,历史的发展总是螺旋式上升的,这里的SSR不同于传统的方式。准确的说,这个技术应该叫SSG静态站点生成或者Jamstack。
NextJS 采用常规路由。pages目录下的文件名,如about.js,对应URL /about,默认导出的组件为页面组件。比较特别的是index.js对应的是/。除此之外,还有动态路由,它要求文件名用方括号括起来,例如 [tag].js。可以匹配/a、/someThing、/?tag=crumb等。 pages目录默认为页面级组件,共享组件放在src/component。
框架提供了useRoute Hook,方便我们使用路由api。这里主要是希望在页面切换时*敏*感*词*切换事件,改变加载状态,改善弱网环境下的用户体验(其实很难看到这个切换过程,后面的原因又来了)。*敏*感*词*路由切换很方便,但是如果每个页面都需要注册一次*敏*感*词*,并且在组件卸载的时候取消*敏*感*词*,会出现太多重复的代码。
NextJS 提供了修改容器组件的功能,在 pages 文件夹中新建一个 _app.js:
// _app.tsx节选
function MyApp({ Component, pageProps }: AppProps) {
const [loading, setLoading] = useState(false)
const router = useRouter()
const startLoading = () => {
console.log('route change start')
setLoading(true)
}
const stopLoading = () => {
console.log('route change complete')
setLoading(false)
}
useEffect(() => {
router.events.on('routeChangeStart', startLoading)
router.events.on('routeChangeComplete', stopLoading)
router.events.on('routeChangeError', stopLoading)
return () => {
router.events.off('routeChangeStart', startLoading)
router.events.off('routeChangeComplete', stopLoading)
router.events.on('routeChangeError', stopLoading)
}
}, [])
return (
{`
body {
background-color: #f6f6f6
};
`}
)
}
上面代码中使用了内置的css-in-component,一种内联样式的写法。我当时使用的 React 版本已经有了 Hooks。我在整个博客代码中使用了功能组件。其实这里也可以使用类组件。
数据获取
前面提到过,NextJS 允许搜索引擎获取预渲染的、带有数据的静态页面,那么如何在 Next 中获取数据呢?
CI/CD、无服务器和 GraphQL
一开始打算用Github Actions进行自动部署,但碰巧遇到了Next更新,而且Next所属的Vercel云服务比较好用,所以选择了官方推荐的方式,只要你提供一个链接到 git 仓库,Vercel 会在每次推送后自动以 serverless 的形式部署,并提供域名和 HTTPS 证书(其实就是 Let's Encypt 的证书)。
此时,我想将后端转换为无服务器应用程序。顺便发现了hasura,它可以提供免费的GraphQL服务。只要有 postgresql 数据库,就可以生成基于 GraphQL 的后端。简单的CRUD完全没问题,所以没有继续折腾。但是对于前端,我只是想在构建过程中获取数据。Apollo 对我来说太重了,所以我找到了 graphql-request 库,它基本上只是对 fetch 的简单封装,足够使用 .
// 示例
const response = await request(GraphQLEndpoint, query, variables)
Link、Shallow Routing、Screening Paging 前面提到,服务器端渲染在切换页面的速度上存在缺陷,因为请求新页面需要返回新页面的完整静态文件,即使大部分页面布局没有改变了。Next 提供的 Link 组件,默认会在空闲时自动请求 JSON 数据,这样当用户点击链接时,可以快速改变内容,渲染新的页面。这也是因为我在非弱网络环境中看到了这一点。没有页面加载效果。
在我的博客中,我为 文章 模型设置了很多外键,例如 文章 列、标签和分页。如果我想为这些设置页面,Next 提供了一种避免重新抓取的方法。浅路由,获取数据更新页面的方式:
// 代码节选
route.push(`/posts?column=${item.column.name}`, undefined, { shallow: true })}>{item.column.name}
const route = useRoute()
useEffect(() => {
if (column) {
setArticles(articles => sourceArticles.filter(article => article.column.name === route.query.column))
}
console.log(articles)
}, [route.query.column])
在useEffect的Hook中,根据route.query.column的变化判断是否更新文章数据源,过滤即可,无需重新取数据,并且页面仅部分更新。
但是我很贪心,想用静态模式,每次只获取过滤需要的数据,而不是一次性获取所有数据,在客户端进行过滤,这个可以借助帮助动态路由的(这里我使用 TypeScript,Next 完全支持 TS):
pages
├── about.tsx
├── _app.tsx
├── posts
│ └── [id].tsx
├── series.tsx
└── [column]
│ └── [page].tsx
├── [tag]
│ └── [page].tsx
└── [page].tsx
但是这种形式,对栏目、标签、页面的过滤其实就是首页列表页,导致只有getStaticPaths函数不同,其余都是重复代码,不能接受,只有这样才可以接受 /[column]/[page 的路由,但不接受 /column=Python&page=2 的查询形式。
那么有没有办法一次接受所有的动态路由呢?其实有,一个叫[...slug]的页面组件就可以了,但是参数只能是数组,比如['a','b']对应/a/b,所以可以' t 区分Column和tag,并且如上所述,不能以查询参数的形式访问。
在这方面,有一个问题。在某个版本中可以实现getStaticParams等API。对于博客等小数据的应用,使用 Shallow Routing 完全没问题,但对于 知乎 这样的大平台,过滤、搜索、分页都是必不可少的。
下一步
现在我博客原有的Django部分已经完全废弃了,自然不能使用Django提供的管理后台。Blazor在上一篇文章文章中介绍过,预计还要几个周末才能到。构建 SPA 后端。毕竟后端应用不需要SEO,SPA会更合适。CSharp 用于编写前端。过去,JS 渗透到桌面和移动端。反过来,静态语言开始蚕食 web 前端。
总的来说,这个博客的不断重建过程,也是我学习一些前端技巧的一个实验过程。在这个过程中,我感觉自己经历了前端技术的发展变化。工程化、系统化,随着Node和一些框架的发展,前端开发体验也在不断提升。博客本身就变成了一个实验室,各种事情轮番经历。这个过程暂时不会停止。毕竟,业余时间写代码是很有趣的。博客已开源。