(轉(zhuǎn)載) Marvin's Blog【程式人生】
Ability will never catch up with the demand for it
23Dec 2018
用Rollup打包Typescript和React
前端項(xiàng)目最麻煩的問題是如何解決代碼間的依賴,以及如果打包代碼。目前比較主流的代碼打包工具有webpack和rollup。我比較喜歡后者,因?yàn)榕渲帽容^簡(jiǎn)單,而且一上來就支持es6.
我的需求是要把react和typescript打包到一起。react提供了一個(gè)工具Create React App,可以幫助你快速搭建一個(gè)react app項(xiàng)目。但是Create React App默認(rèn)支持的是webpack,而不是我希望的rollup。所以本文研究如何用rollup來打包react和typescript。
實(shí)驗(yàn)過程
創(chuàng)建項(xiàng)目
首先創(chuàng)建一個(gè)項(xiàng)目demo,并添加react相關(guān)的依賴(react,react-dom):
mkdir demo
cd demo
yarn add react react-dom
上面的命令會(huì)在當(dāng)前目錄生成node_modules package.json yarn.lock
。
添加typescript
然后添加typescript到dev依賴中
yarn add --dev typescript
創(chuàng)建里面tsconfig.json文件,用來配置typescript,內(nèi)容如下:
{
"compilerOptions": {
"target": "es6",
"jsx": "react",
"sourceMap": true,
"module": "commonjs"
},
"exclude": [
"node_modules"
]
}
上面的配置讓typescript能夠轉(zhuǎn)譯tsx文件(也就是ts版的jsx文件),并生成es6代碼。
demo.tsx
現(xiàn)在來編寫一個(gè)小例子demo.tsx,內(nèi)容如下:
import * as React from "react";
import * as ReactDOM from "react-dom";
ReactDOM.render(
<div>
<h1>Hello, Welcome to the first page</h1>
</div>,
document.getElementById("root")
);
可以使用./node_modules/.bin/tsc -b
命令來編譯上面的例子,會(huì)生成demo.js,其內(nèi)容如下:
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const React = require("react");
const ReactDOM = require("react-dom");
ReactDOM.render(React.createElement("div", null,
React.createElement("h1", null, "Hello, Welcome to the first page")), document.getElementById("root"));
//# sourceMappingURL=demo.js.map
添加rollup
下面的命令在dev依賴中添加rollup以及rollup的兩個(gè)plugin:rollup-plugin-commonjs和rollup-plugin-node-resolve
yarn add --dev rollup rollup rollup-plugin-commonjs rollup-plugin-node-resolve
我們還需要添加rollup的typescript相關(guān)的plugin
yarn add --dev rollup-plugin-typescript tslib
添加tslib,是因?yàn)樗莚ollup-plugin-typescript的peer依賴
創(chuàng)建rollup的配置文件rollup.config.js,添加以下內(nèi)容
import typescript from 'rollup-plugin-typescript'
import commonjs from 'rollup-plugin-commonjs'
import resolve from 'rollup-plugin-node-resolve'
export default {
input: './demo.tsx',
output: {
file: 'dist/index.js',
format: 'iife',
exports: 'named',
sourcemap: true
},
plugins: [
typescript(),
resolve(),
commonjs()
]
}
Missing exports錯(cuò)誤
使用./node_modules/.bin/rollup -c
來打包當(dāng)前項(xiàng)目,不料卻出現(xiàn)若干問題:
./demo.tsx → dist/index.js...
(!) Missing exports
https://rollupjs.org/guide/en#error-name-is-not-exported-by-module-
demo.tsx
render is not exported by node_modules/react-dom/index.js
...
createElement is not exported by node_modules/react/index.js
...
createElement is not exported by node_modules/react/index.js
...
解決辦法一
一個(gè)解決辦法是使用rollup-plugin-commonjs的custom-named-exports功能,這需要修改rollup.config.js的plugin配置中的commonjs部分:
commonjs({
namedExports: {
'node_modules/react/index.js': ['createElement'],
'node_modules/react-dom/index.js': ['render']
}
})
做完上述的配置后執(zhí)行./node_modules/.bin/rollup -c
,錯(cuò)誤消除。
這個(gè)解題思路和StackOverflow的這個(gè)帖子一致
解決辦法二
另一個(gè)解決辦法,在Making of a component library for React一文中提到的,是在rollup.confg.js中把react和react-dom作為外部依賴。
修改rollup.config.js:
export default {
input: './demo.tsx',
output: {
file: 'dist/index.js',
format: 'iife',
exports: 'named',
sourcemap: true,
// 添加globals
globals: {
react: 'React',
'react-dom': 'ReactDOM'
}
},
// 添加externs
external: ['react', 'react-dom'],
plugins: [
typescript(),
resolve(),
// 取消之前對(duì)commonjs的配置
commonjs()
]
}
這樣做的好處是./node_modules/.bin/rollup -c
執(zhí)行非???,因?yàn)椴恍枰虬黵eact和react-dom;壞處是需要自己在html中添加react和react-dom,例如:
<!-- react和react-dom 需要在你的腳本之前添加 -->
<script crossorigin src="https://unpkg.com/react@16/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>
上面的例子參考了react-rectangle-popup-menu
解決辦法三
解決辦法三和解決辦法二相似,但是使用到了peer依賴。這個(gè)解決辦法來自于create-react-library。它可以用來創(chuàng)建基于rollup和typescript的react庫(kù)。
create-react-library把react和react-dom在package.json中列為了peerDependencies(在yarn add的時(shí)候指定–peer即可)。然后使用rollup-plugin-peer-deps-external插件,這個(gè)插件可以把讓rollup把所有的peerDependencies當(dāng)成external依賴處理。最后的效果跟解決辦法二類似
解決辦法四
如果我們?cè)赿emo.tsx里面把下面的代碼:
import * as React from "react";
import * as ReactDOM from "react-dom";
改成
import React from "react";
import ReactDOM from "react-dom";
那么錯(cuò)誤有可能變成:
[!] Error: 'default' is not exported by node_modules/react-dom/index.js
有一個(gè)小戲法能夠幫助解決這個(gè)問題,那就是在rollup.config.js中使用rollup-plugin-replace插件:
import replace from 'rollup-plugin-replace'
...
export default {
...
plugins: [
replace({ 'process.env.NODE_ENV': JSON.stringify('devolpment') }), // 或者production
...
在nodejs中把環(huán)境變量process.env.NODE_ENV改成development或者production,這個(gè)錯(cuò)誤就會(huì)消失,實(shí)在不知道是為什么呢? buble-react-rollup-starter也是這樣??做的。
其他參考
typescript和reactjs相關(guān)
- [How & why: A guide to using Typescript with React](How & why: A guide to using Typescript with Reacthttps://blog.logrocket.com/how-why-a-guide-to-using-typescript-with-react-fffb76c61614)
- ES6:
import * as React
vsimport React
- import fails with ‘no default export’ #8
- Migrating from create-react-app-typescript to Create React App
- What exactly is the ‘react-scripts start’ command?
rollup相關(guān)
- Rollup - react.js does not export Component #643
- NamedModules: cloneElement is not exported by react #345
- Rollup Error: ‘isValidElementType’ is not exported by node_modules/react-is/index.js
- Is “named exports” feature or bug? #211
nodejs依賴
- https://yarnpkg.com/lang/en/docs/dependency-types/
- http://npm.github.io/using-pkgs-docs/package-json/types/peerdependencies.html
- https://nodejs.org/en/blog/npm/peer-dependencies/
- https://stackoverflow.com/questions/26737819/why-use-peer-dependencies-in-npm-for-plugins
JS模塊
- how to use node.js module system on the clientside
- Learn the basics of the JavaScript module system and build your own library
打包器
其他starter
- Creating a React App… From Scratch.
- https://github.com/Microsoft/TypeScript-React-Starter
- Guide to building a React components library with Rollup and styled-jsx.
- How I set-up a React component library with Rollup
Categories
Tags