最近,新Relic核心UI团队移到了反应v16.0。为了支持这一举措,我们不得不升级了公司各团队拥有的一些共享UI库。正如我们大多数人所知道的那样,升级跨公司依赖关系是一项相当大的挑战,它需要大量的时间。

通常,在这样的工作中,我们发现UI库并不总是处于共享的最佳状态。为此,我列出了构建可共享UI库的一些最佳实践和建议。为了清晰起见,我将这些建议分为三个关键类别:CSS、JavaScript和打包和分发。

CSS最佳实践

虽然下面的许多实践已经被一些人解决了CSS-in-JS图书馆,在使用普通的CSS或预处理器时,请记住他们仍然有助于萨斯

#1:使用命名空间选择器
很容易为已经被一个或多个使用者使用的类选择一个名称。所以不要使用类名,比如.MyComponent,最好使用名称空间;例如:.mylib-MyComponent

一个冗长的类名可能不那么吸引人,但确保这个名称不会与用户的CSS冲突是值得的。

#2:不要使用全局选择器
全局选择器选择所有可用的元素并相应样式化它们。但是对于全局选择器,一些简单的东西

李{填充:20px;}

在您的图书馆的CSS中,将在视觉上打破任何消费者申请。

相反,使用您拥有的CSS类选择器来确定库中的所有样式的范围。例如,下面的选择器会更安全:

.mylib-MyComponent li{填充:20px;}

#3:不应用全局重置/归一化
启动新项目时,包含reset.css或normalize.css文件非常常见以重置浏览器的默认样式。但对于共享图书馆,这可能是非常危险的。

一旦消费者导入了库的CSS,除非您的重置文件及其完全相同,否则它将在视觉上打破其申请,这是非常不可能的。

相反,您可以在组件级别进行重置。例如:

.mylib-MyComponent {margin: 0;填充:0;字体:继承;…}

请记住,您的共享UI库的消费者可能会有全局CSS和CSS重置。因此,在组件CSS中添加强大的重置文件是个好主意。在这种情况下,复位越强大,您遇到问题的可能性就越小。

#4:提供一个主要入口
使用您的共享库的消费者将很高兴,如果您提供一个用于使用库所需的所有CSS的搜索点CSS文件,则很高兴。

所以对于以下CSS文件:

@import'./components/icon/styles';@import'./components/button/styles';@import'./components/link/styles';@import'./components/logo/styles';......

你的消费者可以做一个单独的导入来使用你的CSS库:

进口的@my-scope /图书馆/ dist / styles的css

JavaScript最佳实践

以下最佳实践可以帮助简化库中的JavaScript。

#1:添加新依赖前要三思
我们都知道依赖关系占用空间,大部分时间也取决于其他依赖。在您知道之前,依赖项可以完全嵌入您的生产捆绑包。

添加新依赖项时,请始终查看其软件包.json文件以查看项目使用的依赖项。您可以使用工具bundlephobia或者包大小检查他们的大小。

#2:尽可能使用选择性导入
如果你的库只使用了一个大型库中的几个项目,你应该考虑只导入你将要使用的项目。这样,只有库真正需要的东西才会出现在消费者的产品包中。

例如,而不是:

导入{deounce}从'lodash';从'react-虚拟化'导入{Grid};

尝试:

从'lodash/debounce'进口反弹;从'react-虚拟化/commonjs/Grid'导入网格;/代码>

这种做法将1)帮助保持您的图书馆规模小,2)让您免于依赖您的消费者树摇晃在其构建中启用以摆脱未使用的代码。

#3:提供一个主要的入口点
类似于为库的CSS提供一个主入口点,提供一个主入口点是一个很好的做法index.jsentrypoint,从这里导出库中的所有公共模块。

例如,对于以下模块:

export {default as Select} from ` ./components/Select `;从'./components/Input'中导出{default as Input};export {default as searchput} from ` ./components/ searchput `;export {default as Checkbox} from ` ./components/Checkbox `;

你的消费者可以从你的库中使用他们需要的任何东西:

import {Select, Input} from ` @my-scope/my-library `;

#4:考虑使用对等依赖
如果你的库依赖于React或者你的消费者经常使用的其他大型库,那么考虑将依赖添加为对等依赖。有了对等依赖,你可以指定版本的范围,给你的消费者更多的灵活性,而不是强迫他们使用你的库中固定的版本。

例如:

“peerDependencies”:{“d3”:“3”。x", "react": "^15.5.0 || ^16.0.0"}

#5:从生产中删除仅用于开发的代码
有时,库中包含的代码只能在开发中运行,不能在生产环境中运行。此模式经常用于警告(React.propTypes)和devtools (Redux devtools)。

包装在生产中可以消除的代码块,使用process.env.NODE_ENV

例如,你在你的代码中添加以下内容:

if(process.env.node_env!==“生产”){//此代码只会在开发中运行}

当库的消费者使用WebPack的DefinePlugin构建项目并将Node_Env设置为生产时,将转换以前的代码块:

if ("production" !== "production"){//此代码只在开发中运行}

如果消费者使用UglifyJS,它将被转换为:

if (false){//此代码只在开发中运行}

并将在稍后被UglifyJS的死代码消除特性完全剥离。

包装和分发最佳实践

一旦您的图书馆工作并准备好在世界其他地区,您需要打包并分发它。这是很多库失败的地方。以下最佳实践旨在帮助避免这种命运。

#1:不要在npm包中发布包
虽然用webpack创建库包很容易,但有一些后果需要考虑。

当你把你的库和webpack绑定在一起时,所有的依赖都不是webpack外表也会在包中结束。您的所有依赖项都将是一个巨大捆绑包中的静态代码。如果所有依赖项都是静态的,消费者就不会自动获得次要更新或补丁更新。同样,静态依赖项不能与消费应用程序的依赖项相互抵消。

相反,使用一对一的翻译,其中每个文件都是单独翻译的,而不是编译所有文件并将它们捆绑到一个文件中。

在JavaScript中,最简单的方法是编译巴别塔:

——out-dir build/

或者你可以用Sass编译:

node-sass src/ -o build/

#2:在库中提供CommonJS和ES模块
CommonJS模块可以与任何构建工具一起使用,不需要额外的配置。它不像提供库那样常见ECMAScript (ES)模块,但是需要它们来支持摇树。

使用Babel包装库时,可以使用此配置Babel_env.变量。一个基本的.babelrcCommonJS和ES模块的配置如下:

{“env”:{" commonjs ":{“插件”:[“transform-runtime”(“transform-react-remove-prop-types”{“模式”:“包装”}]],“预设”:[“env”、“反应”]},“esmodules”:{“插件”:[“transform-runtime”(“transform-react-remove-prop-types”{“模式”:“包装”}]],“预设”:[[“env”{模块:假}],”反应 ", ] } } }

注意,示例设置["env", {modules: false}]为了esmodules环境。这将禁止将ES模块(导入/导出)转换为CommonJS模块(require/exports),这是摇树工作的主要要求。

鉴于该例子.babelrcConfig,您可以通过运行这两个命令来生成两个版本:

$ BABEL_ENV=esmodules babel src/——out-dir build/es

#3:将npm包的主属性指向编译后的CommonJs版本的库
package.json #主要属性指示库的主JavaScript入口点的路径。是很重要的package.json #主要指向编译后的CommonJs版本的库,这样用户无需额外的工具或配置就可以使用它。

鉴于以下内容package.json:

{name: " @my-scope/my-library ", main: " build/commonjs/index. "js "}

当你的库的使用者像这样导入它:

从'@my-scope/my-library'导入myLibrary;
@my-scope /我的图书馆

属性中指定的路径package.json #主要属性。消费者不需要执行额外的配置。

#4:使用npm包模块属性来启用摇树
package.json#module.field不是npm的官方属性,但它是webpack在解析模块的主入口点时检查的第一个字段。如果您没有设置模块属性,webpack将默认为main属性

指向模块属性添加到你的ES模块版本的库中,可以让webpack的使用者打开摇树功能:

{name: " @my-scope/my-library ", main: " build/commonjs/index. "js”模块:“构建/ es /索引。js "}

#5:在发布的包中使用.npmignore和npm包文件属性
发布的包应该只包含使用库所需的文件。Jenkins脚本、测试、文档和其他项目不应该包含在包中。

使用.npmignore让文件远离你的包。或使用package.json文件属性要在包中显式包含文件。

#6:去除反应proptype
反应proptype仅用于开发,不用于生产。要删除生产包中的proptype,请使用babel-plugin-transform-react-remove-prop-types

这个插件让你包裹proptype转换成条件语句,这样就可以在生产中删除它们。

考虑以下示例组件:

class mycomponent扩展了React.Component {静态proptypes = {classname:proptypes.string,项目:proptypes.array,onclick:proptypes.func,} render(){return 
... ;}}

当您启用删除时proptype插件,这proptype将被这样的条件三元封装:

MyComponent。proptype= process.env.NODE_ENV !== "production" ? { className: PropTypes.string, items: PropTypes.array, onClick: PropTypes.string } : {};

该代码块在被UglifyJS处理后将被转换为:

mycomponent.proptypes = {};

#7:使用Babel运行时转换
变形将帮助您避免库的输出中的Babel Helper复制。您还可以使用此转换来创建沙盒环境,以便尝试使用您的代码。

为成功建立你的库

这只是组合可共享UI库的最佳实践的一个小示例。这些技巧提供了一个很好的方法,可以主动减少在共享代码的大型跨团队项目中可能出现的摩擦。

javiersánchez-marín是西班牙巴塞罗那的新遗物领先软件工程师。查看帖子

对新遗物博客的写作有兴趣吗?亚搏体育登入网给我们发一份建议书!