只要一提到组件化,我相信大多数同学,必然会想到 react, vue, angular… 这样的前端框架。
然而我心中始终有这样一些疑问,是这些框架成就了组件化?还是组件化成就了这些框架?
目录
- 为啥要组件化
- 为啥不用框架
- 无框架 组件化 理论基础
- 无框架 组件化 实战经验
为啥要组件化
我们都知道 HTML,CSS,JS 分别对应网页中的结构,样式,行为。
组件化能减少关注点
假设现在有个功能是:一个红色按钮,点击这个按钮输出alert-text
属性上的文案。
按照最原始的方式实现,使用时必须了解:
- css 文件路径
./component/button.css
- js 文件路径
./component/button.js
- 两个 css 选择器
btn
,j_btn_alert
(以及它们的命名规则) - 标签名称
button
- 按钮类型
type="button"
- 文案属性名
data-alert-text="你点击了我"
- 按钮文案
click
按照组件化逻辑(这里以React为例)大概会是这样,使用时必须了解:
- 组件文件路径
./Button/index.js
- 组件名称
Button
- 文案属性名
alertText="你点击了我"
- 按钮文案
click
明显看出站在使用方的角度,组件化的关注点比原始方案少很多。
原文链接
HTML JS CSS 相互制衡
在我看来,写网页和建房子是很像的。每个页面对应着每个房间,每个房间里的元素对应不同的模块。网站传统的架构和这个逻辑也是类似的。
HTML, CSS 和 JS 之间彼此通过文件引用以及选择器关联,如果我们想把桌子从 A 房间搬到 B 房间。不仅要搬桌子的 HTML,JS, CSS。还得知道桌子上放了什么。
比如桌子上面有一个台灯,那同时还得把台灯的 HTML,JS,CSS 也都搬到 B 房间。
并且桌子上有可能还放了其它的物件,所有这些逻辑关系,都得人工单独找出来,对于习惯了组件化的开发的同学来说,简直就是噩梦。
从组件化的角度来看组件之间是通过接口关联的。
在复用一个组件的时候,完全用关心其内部逻辑实现。甚至都不用感知到 HTML,JS 和 CSS 本身。眼里一切都是组件,使用方只需要做组件的搬运工,以及对接接口就好。
组件化省钱
要知道,搬运工这件事,是不需要太多技术含量的。之前项目,可能需要招 4 个高级工程师(必须熟知整站的架构,规范)。有了组件化,可能只需要 2 个高级工程师写组件,2 个初级工程师做搬运工。后期,组件库完善之后,高级工程师只需要兼职做做这个项目的技术顾问即可。
如果多个项目技术架构相同,组件复用还可以进一步节省人力。
为啥不用框架
在解释这个原因之前,我们先来听一个,你要过河找你女友的鬼故事。
一天,你和你女友约好周末去河边看星星,结果到了才发现,河上并没有桥。
然而你们并不是一起去的,只是约在了河边碰头,而此时恰好这个河将你和你的女友分割两方。
你女友要求你过河去找她,你要是做不到她就和你分手。
在这焦急万分的时刻,你告诉自己不能慌,整个场面你要 hold 住。于是你仔细的观察河周围的情况。
- 河旁边有一颗树,树上有一根绳,可以用绳子荡过去;
- 河旁边有几块木板,可以用这些拼一个大的板子,搭在桥上过河;
- 可能你本人就是木匠,你直接用这些零件造了简单可以称为桥的东西;
- 河旁边有几个造桥的工厂,拿到零件,按照说明书可以直接拼成一座完整的桥;
这是个鬼故事?
如果把这个女友看做产品经理,过河这件事看作项目需求。这一切听起来是不是像每天都会发生的鬼故事?
可能产品经理要求不只是你,而是你整个团队都要过河,并且可能就给你们半天,过不来那就是你们团队技术不行。
回到项目问题上,上面的四种状态其实就分别对应着我们解决问题的四种方法:
- 原始:管它怎样,先完成需求过去再说,但团队里不是每个小伙伴都能凭绳子过河;
- 模块化:临时搭建了一些简陋的模块,能解决问题。但是作为使用方需要完全了解,模块拼装规则和使用方式;
- 组件化:封装了一个完整的组件,对于使用方,只需要了解,桥从哪里来,桥怎么用即可;
- 框架: 能让没有什么专业能力的小伙伴,也能按照说明书,造出过河的桥;
框架需要代价
需要选择: 旁边两家工厂一个叫 React,一个叫 Vue 怎么选?
学习成本: 团队里面的人之前都没有接触过 React 和 Vue,就得从 0 开始学习;
不确定因素: 工厂随时可能被替代,今天可能这个工厂流行,明天就有可能是另外一个工厂(Nokia);
排他性: 很难在同一个项目中,同时使用两个工厂的设计理念(苹果和安卓)。
全盘接受:接受一个工厂好处的同时,也全盘接受了工厂的缺点;
无框架 组件化
在实际项目开发过程中,我们之所以不能用框架,往往不是框架不够好,而是项目为大的无奈之举。不能因为我想要用 React 框架,就给让项目完全停下来去升级框架。
我又想组件化,又不能用框架,怎么办?
此时就需要分析一下,框架在组件化这一层都做了哪些事情。
构建
传统的基于文件的构建,通常的做法是,同类型的文件放到一个文件夹下,以便于统一处理,但是这个需要人工建立引用关系。
组件化做法是以 JS 文件作为组件的入口文件。并利用这个入口文件来组织内部依赖关系。
写组件的时候内聚,但是用户在看到页面的时候,组件实际是被拆开,平铺在 HTML 中,通过文件引用和各种“钩子”联系。这个对于构建的依赖分析要求会非常的高。
HTML First ?JSFirst
不管是 React,VUE,都是以 JS 为切入点,去描述一个组件以及其内部关系。这个其实很好理解,HTML 擅长描述结构,CSS 擅长描述样式,依赖关系以及逻辑关系,明显 JS 更胜一筹。
以 JS 优先,也更加的利于构建工具做依赖分析。随着框架的流行,你会听到诸如 CSS in JS, JSX(HTML in JS)… 这样JS 优先的方案杀出来了。
而传统的方案则是以HTML 优先的方式处理的。通常我们会用 HTML 模版引擎,去描述组件的结构。当有数据的时候,再借用模版引擎的 Render 方法去拿到渲染的 HTML。
<!-- home.ejs -->
<html>
<body>
<h1><%= data.title %></h1>
<p><%= data.description %></p>
</body>
</html>
/** home.js **/
import ejsHome from "./home.ejs";
import EJS from "ejs";
const data={
title:'首页',
description:'这是首页'
};
const html = EJS.render(ejsHome, {data});
return html;
<!-- home.html -->
<html>
<body>
<h1>首页</h1>
<p>这是首页</p>
</body>
</html>
这时HTML 优先这个方案,蹩脚的地方就出来了。因为 HTML 并不能做完所有的事情,即使 HTML 优先,仍然需要模版引擎的加持。并且拼接 HTML模版和数据这件事情,仍然需要 JS 参与。
所以想要做组件化,目前比较推荐的是使用 JS First 的方案。
同构实战
前面几节分别讲了,组件化的好处,为了推进组件化而选择框架的一些代价,以及想要实现,无框架,组件化的两个理论基础(构建加持,JS First)。
这一节讲讲实战,以我们 webnovel(需要科学上网) 为例,给大家介绍
- PC 站(传统 jQuery 项目):www.webnovel.com
- M 站(React 框架):m.wenovel.com
这两个站点分别代表着,前面提到的传统派和框架派。
比较有趣的是,我们团队的小伙伴会觉得 PC 站的开发体验相比 M 站糟糕。但是从页面加载速度,SEO 和页面性能等技术角度来说,PC 站又明显胜过 M 站。
为了不捡芝麻丢西瓜。我们在传统和框架之间选择了尝试无框架组件化这个思路。在我们看来,只要能在 PC 站实现组件化,就能非常大的提升开发体验。
升级构建
当我们将构建从 Gulp 升级到 webpack,对于开发角度最直观的体感是说,我们的文件结构变自由了。
当然这不仅是说我们可以组件化的去划分我们的文件夹,更可喜的是组件化的文件夹可以和原始的文件结构兼容。因为构建本身并不关心文件夹结构是怎样,只要路径依赖正确就不会有影响。
也就是说,我们可以渐进增强的做网站的组件化。旧的模块和页面,就等它们在那里,岁月静好。新页面,新功能,组件化开发开心。
JS First
当构建升级之后,就可以直接用index.js
这个入口文件去引用index.css
和index.ejs
文件。使用方只需要引用index.js
然后初始化一下即可。
这是不是离前面提到的组件化已经非常接近了?
前面已经提到的JS First的概念,简单的说就是让 HTML in JS,可以让组件更加的内聚。比如上图这个例子,甚至可以用 JS 的字符串模版替换掉原始的 EJS HTML模版引擎。
然而当我们正式在项目中用这种非结构化(Function 的方式)的去描述结构本身,看起来非常吃力。
此时,不得不佩服 React,为了让 HTML in JS 发明的 JSX 这个语法糖(右图),可以在 JS 里以结构化方式(类似 HTML)去描述组件之间的关系,能完美的解决了这个痛点。
然而,说好的无框架,组件化,难道我们又要用 React?不!我们不想!
JSX without React
介绍全新的 JSX 转换,跳转链接
这时 React 官网发来喜讯。JSX 不再依赖 React ,而内置在了新版本的 Babel 中 。
这时只需要在 Babel 的配置中添加@babel/plugin-transfrom-react-jsx
插件,即可将左边这样的 JSX 转成 Function 语法。等等!Function 语法?不对啊,我们要的不是Function 而是 HTML 啊!
此时 JSX 转 HTML 成为了我们新的难题。
然而,我们能搜到的方案,都是运行时的,他们会通过document.createElement(tagName)
这个浏览器端的 API 去创建元素。而我们要的转换是在构件时。没法此时只能自己造轮子。
jsx2string:点我查看npm包
简单梳理一下流程,其实很简单。就是你在 JS 中写了 JSX 语法,Babel 在构建的时候会将其转换成 Function 的形式,最后利用 jsx2string 执行就可以拿到你想要的 HTML 字符串。
服务端渲染
到这里非服务端渲染的逻辑,已经可以组件化开发。
但是我们目前 PC 站的 服务端渲染是 koa + EJS 的逻辑。又是熟悉的 HTML First 的味道。
以前是用 EJS 拼凑,HTML 模版和数据,得到渲染的 HTML。现在我们实现了 JS First,所以只需要将数据作为参数传递给 Render 方法执行就能拿到 HTML。
因为服务端渲染的 JS 文件,只是需要拿到服务端数据之后,执行吐出 HTML 字符串。它和浏览器端的 JS 要处理的事情不完全一样。
在本地开发,实际是起了一个两个构建,一个是构建浏览器端需要的文件index.js
, 一个是服务端渲染的构建index.node.js
。
.
└── Home
├── index.js // 浏览器端 js 入口文件
└── index.node.js // 服务端渲染的 js 文件
此时有一个比较难的点,就是在于index.node.js
中需要拿到index.js
中所有的依赖关系放到 link 和 script 标签(webpack build 出来一个页面可能有多个 js 和 css 文件的依赖关系)中在浏览器中运行。
并且index.node.js
文件自身也是有依赖关系的。为了减少复杂度,我们让index.node.js
所有的依赖都打包到一个文件中。
这样我们只需要处理index.js
依赖注入的问题即可。 这里我们是用了一个比较 Hack 的方式。
我们都知道 webpack 插件html-webpack-plugin
是可以根据模版生成 HTML 文件的,并且自动将 chunks 里的所有依赖都自动放进去。
new HtmlWebpackPlugin({
filename: "./home/index.config.js",
chunks: [ "Home/index" ],
// 这里包含了 home/index.js 文件中所有的依赖关系
htmlPluginOption:({ htmlWebpackPlugin }) =>`module.exports = ${JSON.stringify(htmlWebpackPlugin)};`
});
因为我们并不需要 HTML, 所以我们让html-webpack-plugin
返回的是一个包含当前所有依赖关系的index.config.js
文件。
.
└── Home
├── index.config.js // index.js 之后所有的依赖文件(webpack build 自动生成)
├── index.js // 浏览器端,js 入口文件
└── index.node.js // 服务端渲染的 js 文件
所以我们是先启动index.js
文件的构建,然后会分析出所有的依赖关系,并同时输出包含依赖关系的配置文件index.config.js
。index.nodex.js
的构建会打包成最后服务端渲染的一个可执行的单 JS 文件。
在服务端渲染时,将这个index.config.js
配置文件返回的对象,连同服务端请求的数据,一起注入到index.nodex.js
渲染方法中,然后执行输出 HTML 即可。
结语
到这里我们算是将我们 PC 整站的组件化推进了一大步。值得一提的是,这个组件化是没有附加任何框架的代价的。
因为我落点是在最后的项目组件化推进上。所以我刻意回避了,组件化本身的问题。比如之前提到的组件化封装本身也是一个复杂的学问。太内聚往往不够灵活,太灵活使用方需要关注的点就会变多。这还是需要基于团队能力找到一个平衡点的。
没有绝对好的方案,绝对坏的方案,一切都看是否适合。
常见问题FAQ
- 免费下载或者VIP会员专享资源能否直接商用?
- 本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
- 提示下载完但解压或打开不了?
- 找不到素材资源介绍文章里的示例图片?
- 模板不会安装或需要功能定制以及二次开发?
发表评论
还没有评论,快来抢沙发吧!