耦合指模块间关联的程度. 模块之间的关联越多, 其耦合性越强, 那么独立性也就越差了. 高耦合的代码, 会出现一处改, 处处改的情况. 高耦合的代码, 模块之间的联系, 就像一团乱码.
工作中, 大家可能会碰到这样的情况:
接口的返回值变了, 要改一堆代码.
升级了一个第三方库, 要改一堆代码.
某个组件的内部实现变了, 要改一堆代码.
如何在遇到这种情况的时候, 减少前端代码的改动呢? 我分享给大家 2 个技巧:
降低代码间的耦合.
业务代码和依赖代码之间加适配层.
下面我们具体来看~
降低代码间的耦合
耦合指模块间关联的程度. 模块之间的关联越多, 其耦合性越强, 那么独立性也就越差了.
高耦合的代码, 会出现一处改, 处处改的情况. 高耦合的代码, 模块之间的联系, 就像一团乱码.
解藕, 就是避免对模块内部具体实现的依赖.
下面我们来看一些具体的耦合场景, 以及对应的解藕方案.
耦合 DOM
直接操作 DOM, 是耦合的 DOM 的. 当 DOM 结构发生变化时, 操作代码也要跟着变. 下面是显示用户名的代码:
html:
用户名:
用户名:
- JavaScript:
- const userName = ...
- document.querySelector('#user-name').textContent = userName
当显示用户名的 id 变成其他时, JavaScript 也要变化.
解藕方法
vue, React 之列的框架, 声明了 数据 和 视图 的关系, 不会耦合 DOM.
用 Vue:
用户名:
- {{userName}}
- new Vue({
- ...
- data: {
- userName: ...
- }
- })
用 React:
- function App () {
- const [userName, setUserName] = useState(...)
- return (
用户名:
{userName}
)
}
耦合其他模块的方法或属性
以组件为例, 当父组件主动调用子组件方法, 会造成耦合. 例如, 父组件要让子组件获得焦点. 用 React 实现的代码如下:
inputRef.focus()}> 点我获得焦点
// 子组件
如果子组件获得焦点的方法签名发生了变化, 上面的代码就失效了. 同样的, 父组件获取子组件的内部属性, 也会造成耦合的问题.
解藕方法
耦合方法的解决方案 1
子组件监听属性的变化, 来响应父组件的主动调用. 改写代码如下:
- const [controlFocus, setControlFocus] = useState(0);
- return (
- <>
setControlFocus(Date.now())}> 点我获得焦点
)
耦合方法的解决方案 2
用发布订阅模式. 父组件需要主动调用子组件方法时, 触发个自定义事件, 子组建监听该自定义事件.
耦合属性的解决方案 1
将耦合的属性放到父组件来维护, 子组件改属性时, 通知父组件. 如:
- const [value, setValue] = useState(0);
- return (
- )
耦合属性的解决方案 2
父组件要获得子组件的内部属性时, 改变某个属性. 子组件监听到该属性的变化后, 执行父组件获取值的回调函数.
- const [value, setValue] = useState(0);
- const [controlGetValue, setControlGetValue] = useState(0);
- return (
- )
上面代码中, 父组件要获取子组件内部的 value 值, 只需改 controlGetValue 的值即可.
业务代码和依赖代码之间加适配层
如果业务代码中有多处代码直接调用了外部依赖, 如果依赖项发生了变化, 调用的地方也要改. 比如: 接口的请求和响应改了; 升级的第三方库的 API 发生了变化.
解决这个问题, 可以在业务代码和依赖代码之间加适配层. 当依赖项改后, 只需要改适配层代码, 不需要改业务代码.
注意: 增加适配层本身会增加代码的复杂度. 因此, 不要过度的加. 一般在满足以下 2 个条件的情况下加:
代码中有多处代码直接调用了外部依赖.
外部依赖变动的可能性比较大.
常见的外部依赖有: 配置项, 接口, 第三方库, 全局 API. 我们一个个来看.
分类配置项
将配置从逻辑中分离出来, 写成配置文件. 如
- export const API_HOST = '/api' // 接口前缀
- export const PAGE_LIMIT = 10 // 分页时, 一页的条数
适配接口的请求参数和响应结果
一般会加个适配层来统一对接口的请求参数和响应. 适配层在请求里加 token 之类的, 在响应里处理通用报错. 如:
- const request = (options) => {
- // 添加请求拦截器
- axios.interceptors.request.use(...)
- // 添加响应拦截器
- axios.interceptors.response.use(...)
- return axios
- }
对具体接口做格式化请求参数和接口的返回.
- export const formatFromServer = res => ...
- export const formatToServer = data => ...
适配第三方库
如果是第三方组件, 可以用个组件代理第三库的组件. 如:
- import { Sortable } from "react-sortablejs"
- function Sort(props) {
- return (
- {...props}
- ...
- >
- ...
- )
- }
如果是一个函数, 可以用一个函数来代理. 如:
- import xxx from 'xxx'
- function myXxx(...args) {
- return xxx(...args)
- }
适配全局 API
如果我们大量代码用了浏览器的可能会被废弃的实验性的 API. 可以做这样的配置:
- const someAPI = () => {
- if(Windows.someAPI) {
- return someAPI
- }
- // 不支持的情况.
- return () => ...
- }
总结
外部变化后, 要减少前端代码的改动, 主要靠 降低代码的耦合 和 增加适配代码. 但也不要过度使用哦~
来源: http://developer.51cto.com/art/202109/680533.htm