这并非本身第一次发npm包, 因此这里没有多少入门的知识。在此以前已经有一篇前端脚手架,听起来玄乎,实际呢?,但这一次的npm包和上一次的不是一个概念,前者只是一个脚本工具,而这个npm包是平常开发中方法和组件的集合, 是一个库。
在读本文前,假定你已经对npm包有必定概念,熟悉Babel编译和webpack打包的常规用法,知道一些前端工程化的知识。假如你也想本身发布一个npm仓库,但对这一块了解的不是不少,推荐webpack官方的建立一个 librarycss
在前端平常开发中,引入npm库,执行webpack构建已是一件不能再日常的事情。但大多数时候,咱们不关心这个npm库是怎样构成的,咱们只须要知道怎么使用,像antd;在工程化成熟的公司,也不关心webpack的配置究竟是怎样的,只须要npm run start或npm run build去启动一次热加载或打包。可是若是你是编写一个npm仓库,这些东西你都须要知道。从那时的无知提及,起初,我用公司的构建工具(相似于roadhog)去打包个人库,没有坎坷,构建出一个2M多的包并成功发布。前端
在测试项目中引入,构建成功。node
import { EnhanceTable, WithSearch } from 'antd-doddle'; // 引入仓库
在浏览器中打开,打击开始到来:
提示要引入的对象没有正确导出,就是没有作module.export,因此这是一个打包模式的问题,output.libraryTarget须要了解一下。react
webpack的output.libraryTarget决定了打包时对外暴露出来的对象是那种模式,默认是var,用于script标签引入,该模式也是咱们平常开发构建最经常使用的模式,除了这一种,还支持的经常使用选项有:webpack
因此在这里咱们须要设置两个属性来明确打包模式git
library: 'antd-doddle', libraryTarget: 'umd',
上图是用roadhog打包出来的结果,其显示的是开启gzip后能够压缩到的大小,第一次打包的实际大小大概在2M(antd+moment+react+css),后面仔细一想,公司的组件库也才300kb啊,本身是否是哪里搞错了,因此接着就有了下面的探寻之路。es6
["import", { "libraryName": "antd", "libraryDirectory": "lib", "style": "css", }]
externals: { react: { commonjs: 'react', commonjs2: 'react', amd: 'react', }, antd: { commonjs: 'antd', commonjs2: 'antd', amd: 'antd', }, moment: { commonjs: 'moment', commonjs2: 'moment', amd: 'moment', }, }
打包大小优化至此就搞定了,但后面发现用roadhog打包库有一些很难解决的难题,为了解决还得去了解他源码逻辑,因此后面仍是本身写了一个webpack,很是简单的配置。github
在写这个库以前,我曾想到在咱们平常构建时有下面这样一段配置:web
rules: [{ test: /\.js$/, exclude: /(node_modules|bower_components)/, loader: 'babel-loader', query: { presets: ['@babel/preset-env', '@babel/preset-react'] } }
这段配置是告诉webpack,node_modules中引用的代码不须要再由babel编译一次,但这些代码仍是会被打包进dist文件的。在如今前端的主流开发套路中,被引用的库更但愿是一个只编译而没有被打包过的,支持按需加载的库。因此dist中被编译打包过的代码再次被打包, 这样就会有没必要要的代码出现。因此这样来看,咱们只须要将代码编译。babel,没错,就是它,可是多文件编译,仍是找个第三方构建工具比较好,我选择了gulp,直接上代码:npm
// 发布打包 gulp.task('lib', gulp.series('clean', () => { return gulp.src('./src/**/*.js') .pipe(babel()) .pipe(gulp.dest('./lib')); }, 'lessToLib')); // lessToLib用于将less文件拷贝贷lib文件夹
编译事后大概是下面这样的,确实只编译没打包,函数基本原样:
本觉得到这就结束了,可是这才开始。只编译不打包消除没必要要的代码只是很小的缘由,重要的东西我以为换一行说比较好。
不顾语文老师的责骂换行,那什么才是是最重要的:按需打包(tree shaking),对于这种组件和方法库,做为使用者,咱们但愿他能支持按需打包,像lodash和antd这样。因此怀着好奇的心理我去看了他们的package.json,而后发现了这样的配置:
"main": "lib/index.js", "module": "es/index.js", "name": "antd",
除了认知中的main入口定义,还多了一个module入口.为何须要这样呢,和我同样无知的,能够先读webpack官方的tree shaking,若是不够直观,能够再看一位大佬写的一篇相关文章聊聊 package.json 文件中的 module 字段。看下面代码:
// es6 模块写法 fun.js export function square(x) { return x * x; } export function cube(x) { return x * x * x; } // commonJs写法 fun.js exports.square = function(x) { return x * x; } exports.cube = function(x) { return x * x * x; } // index.js引入 import { square } from './fun.js'; const res = square(10); console.log('res:', res);
简单来讲,就是index.js打包编译时,引入commonJs写法的fun.js,打包会将square与cube两个函数同时打进来。而引入es6写法的fun.js,只会将square打包。这样的操做,对于如今的主流趋势,就是必须的优化,特别对于lodash和antd这种庞大的库。而要使咱们的库支持这样的操做,咱们须要编译时,禁止babel将es6的module引入方式编译,其实只须要在前面的基础上多配置一个参数:
"@babel/preset-react" // lib的打包方式 ["@babel/preset-env", { "modules": false }] // 保留es6模块引入的方式
获得的是下面这样的结果:
和上面的lib对比,感受更接近原始代码。至此,编译已结束,可是咱们还须要在package.json中加上相应的配置:
"description": "antd后台项目前端组件封装和方法库", "main": "lib/index.js", "module": "es/index.js", "scripts": { "build": "webpack --config webpack.config.js", "lib": "gulp lib", "es": "gulp es", "prepublish": "gulp && webpack --config webpack.config.js" }, "files": [ "es", "dist", "lib", "utils" ],
由于个人库主要包括组件和方法,我把方法放到一块儿,经过utils做为默认输出。而后项目中引入是这样的:
import { EnhanceTable, WithSearch }, utils from 'antd-doddle'; // 要用里面的方法须要再分解一次或经过utils.xxx const { DATE_FORMAT, idCodeValid } = utils;
虽然感受上不复杂,可是总感受别扭,若是你用过dva,就见过下面这样的引入:
import { routerRedux } from 'dva/router'; import dva from 'dva';
因此我去学习了一下,发现要这样实现也不难
分三步,分目录打包,增长一个输出,并增长内部私有映射,package.json增长一个这个映射目录的输出。具体可查看项目源码。实现后,项目引入是这样的:
import { EnhanceTable, WithSearch }, utils from 'antd-doddle'; import { DATE_FORMAT, idCodeValid } from ‘antd-doddle/utils’; // 一步到位
之前咱们不少命令如webpack,gulp命令只有在全局安装(npm install xxx -g)才能够在命令行中直接运行或在项目中安装,经过script定义执行,但在npm5.2之后,咱们能够只项目中安装,而后经过新增的npx执行。好比上面scripts中定义的lib打包("lib": "gulp"),咱们能够直接在命令行中用:
npx gulp
有可能你和我同样,在处处都是墙的世界,须要在npm,cnpm,公司的npm registry三者之间来回切换,每次都须要这样:
npm set registry 'https://registry.npm.taobao.org/'
麻烦有没有? 幸亏,这世界有不少牛逼的人,nrm registry是个很好用的工具,下面这样:
// 安装 npm install -g nrm // 设置入口npm,cnpm,company nrm add npm 'http://registry.npmjs.org' nrm add cnpm 'https://registry.npm.taobao.org' nrm add vnpm 'http://npm.company.com' // 切换入口到淘宝入口 nrm use cnpm
一个春节本身断断续续就在倒腾这个,收获仍是挺大的。后面本身会慢慢去学习怎么加入demo‘,加入单元测试,去建造一个完整的npm库。
源码库:github
npm仓库地址:npm