这篇只讲环境配置
万事开头难,写bug容易,环境难配。记录一下,给自己日后参考。
React
- 因为是npm 6+, 用了init来初始化项目
npm init react-app my-app
装的 React 17:
"react": "^17.0.2",
"react-dom": "^17.0.2",
Typescript, Enzyme, Jest
- 装 ts, enzyme, jest 依赖
npm i --save awesome-typescript-loader source-map-loader typescript webpack @types/react @types/react-dom
"awesome-typescript-loader": "^5.2.1",
"source-map-loader": "^3.0.0",
"typescript": "^4.4.3",
"webpack": "4.44.2",
"@types/react": "^17.0.24",
"@types/react-dom": "^17.0.9",
npm i --save-dev enzyme enzyme-adapter-react-16 jest react-test-renderer @types/enzyme @types/jest
"enzyme": "^3.11.0",
"enzyme-adapter-react-16": "^1.15.6",
"jest": "^27.2.1",
"react-test-renderer": "^17.0.2",
"@types/enzyme": "^3.10.9",
"@types/jest": "^27.0.1",
- 暴露配置
npm run eject
Project 结构
- 前3步之后,会得到这样的一个项目。 标注“(新建)”的是手工新建的文件
.\
├── src\
│ ├── components\
│ │ ├── Hello.tsx (新建)
│ │ ├── Hello.module.less (加styles的时候再新建)
│ │ └── Hello.spec.tsx (新建)
│ ├── App.tsx (把.js改成.tsx)
│ └── index.tsx
├── config\
│ ├── jest\
│ ├── ...
│ ├── webpack.config.js
│ └── webpackDevServer.config.js
├── public\
│ ├── index.html
├── test-preprocessor.js (新建)
├── test-setup.js (新建)
├── test-shim.js (新建)
├── package.json
├── tsconfig.json (新建)
├── externals.d.ts (新建)
└── ...
- Hello.tsx 和 App.tsx
// Hello.tsx
import React from 'react';
interface Props {}
const Hello: React.FC = (props: Props) => {
return <h1>Hello</h1>
};
export default Hello;
// App.tsx
import React from 'react';
import Hello from 'src/components/Hello';
const App = () => {
return <Hello />
};
export default App;
- config/path.js
// ...
const moduleFileExtensions = [
'web.mjs',
'mjs',
'web.js',
'js',
'web.ts',
'ts',
'web.tsx',
'tsx', //must contain tsx
'json',
'web.jsx',
'jsx',
];
- tsconfig.json
{
"compilerOptions": {
"allowJs": true,
"baseUrl": ".",
"esModuleInterop": true,
"isolatedModules": true,
"jsx": "react",
"lib": [
"esnext",
"dom"
],
"moduleResolution": "node",
"noUnusedLocals": true,
"outDir": "./target/",
"paths": {
"@src/*": [
"./src/*"
]
},
"sourceMap": true,
"skipLibCheck": true,
"removeComments": true,
"noImplicitAny": true,
// "module": "commonjs",
"resolveJsonModule": true,
"strict": true,
"target": "es5",
"plugins": [
{
"name": "typescript-plugin-css-modules"
}
]
},
"include": [
"./src/**/*",
"src/externals.d.ts"
],
"exclude": [
"node_modules"
]
}
- 这个时候,
npm run start应该能跑起来了
-- 开始搞测试 ----
测试
- test-preprocessor.js
/**
* Transpiles TypeScript to JavaScript code.
*
* @link https://github.com/facebook/jest/blob/master/examples/typescript/preprocessor.js
* @copyright 2004-present Facebook. All Rights Reserved.
*/
const tsc = require('typescript');
const tsConfig = require('./tsconfig.json');
module.exports = {
process(src, path) {
if (path.endsWith('.ts') || path.endsWith('.tsx') || path.endsWith('.js')) {
return tsc.transpile(src, tsConfig.compilerOptions, path, []);
}
return src;
},
};
- test-setup.js
/**
* Defines the React 16 Adapter for Enzyme.
*
* @link http://airbnb.io/enzyme/docs/installation/#working-with-react-16
* @copyright 2017 Airbnb, Inc.
*/
const enzyme = require("enzyme");
const Adapter = require("enzyme-adapter-react-16");
enzyme.configure({ adapter: new Adapter() });
- test-shim.js
/**
* Get rids of the missing requestAnimationFrame polyfill warning.
*
* @link https://reactjs.org/docs/javascript-environment-requirements.html
* @copyright 2004-present Facebook. All Rights Reserved.
*/
global.requestAnimationFrame = function(callback) {
setTimeout(callback, 0);
};
- package.json
{
"name": "my-app",
...
"jest": {
"setupFiles": [
"<rootDir>/test-shim.js",
"<rootDir>/test-setup.js"
],
"moduleFileExtensions": [
"ts",
"tsx",
"js"
],
"transform": {
"^.+\\.(ts|tsx)$": "<rootDir>/test-preprocessor.js"
},
"testMatch": [
"**/__tests__/*.(ts|tsx|js)"
]
},
"scripts": {
"jest": "./node_modules/.bin/jest"
},
...
}
- Hello.spec.tsx
import React from 'react';
import {shallow} from 'enzyme';
import Hello from './Hello';
it('renders the heading', ()=> {
const result = shallow(<Hello />).contains(<h1>Hello</h1>);
expect(result).toBeTruthy();
})
- 跑
npm run jest
> my-app@0.1.0 jest <myPath>\my-app
> jest
PASS src/components/Hello.spec.tsx
√ renders the heading (16 ms)
Test Suites: 1 passed, 1 total
Tests: 1 passed, 1 total
Snapshots: 0 total
Time: 5.934 s
Ran all test suites.
-- 开始加styles ----
Less
- 用的Less,因为bootstrap 天然支持less,就不费劲搞scss了。
最好先查下webpack版本,webpack 4 貌似最多只支持到less-loader@7。
npm install --save-dev less less-loader@7
"webpack": "4.44.2",
"less": "^4.1.1",
"less-loader": "^7.3.0",
改 config\webpack.config.js
const cssRegex = /\.(css|less)$/;
const cssModuleRegex = /\.module\.(css|less)$/;
//...
module.exports = function (webpackEnv) {
// ...
resolve: {
extensions: paths.moduleFileExtensions
.map(ext => `.${ext}`)
.filter(ext => useTypeScript || !ext.includes('ts')),
},
module: {
rules: [
{
test: cssRegex,
exclude: cssModuleRegex,
use: getStyleLoaders({
importLoaders: 1,
sourceMap: isEnvProduction
? shouldUseSourceMap
: isEnvDevelopment,
},
'less-loader'), // 加上 less-loader
// Don't consider CSS imports dead code even if the
// containing package claims to have no side effects.
// Remove this when webpack adds a warning or an error for this.
// See https://github.com/webpack/webpack/issues/6571
sideEffects: true,
},
// Adds support for CSS Modules (https://github.com/css-modules/css-modules)
// using the extension .module.css
{
test: cssModuleRegex,
use: getStyleLoaders({
importLoaders: 1,
sourceMap: isEnvProduction
? shouldUseSourceMap
: isEnvDevelopment,
modules: {
getLocalIdent: getCSSModuleLocalIdent,
},
},
'less-loader'), //加上 less-loader
},
]
}
}
- Hello.module.less
// src\components\Hello.module.less
.hello {
color: red;
}
改Hello.tsx
import React from 'react';
import * as styles from './Hello.module.less'; // 引入less
interface Props {}
const Hello: React.FC = (props: Props) => {
return <h1 className={styles.hello}>Hello</h1>
};
export default Hello;
- 如果报错 cannot find module Hello.module.less 新建 src\externals.d.ts
declare module '*.less' {
const resource: {[key: string]: string};
export = resource;
}
改 tsconfig.json,确保"src/externals.d.ts"在"include"里面,注意路径别错。
"include": [
"./src/**/*",
"src/externals.d.ts"
],
这时候,"cannot find module" error 应该消失了。
- 如果报错 TypeError: this.getOptions is not a function, 又可能是当前 webpack版本不支持当前less-loader 版本。我用的webpack@4, 支持less-load@7
./src/components/Hello.module.less (./node_modules/css-loader/dist/cjs.js??ref--5-oneOf-7-1!./node_modules/postcss-loader/src??postcss!./node_modules/resolve-url-loader??ref--5-oneOf-7-3!./node_modules/less-loader/dist/cjs.js??ref--5-oneOf-7-4!./src/components/Hello.module.less)
TypeError: this.getOptions is not a function
现在 npm run start, 应该能看到Hello变成红色了.
Bootstrap, Reactstrap
- Bootstrap
npm i --save-dev bootstrap @types/bootstrap reactstrap @types/reactstrap因为咱用了ts,所以那两@types/xx是必须的哈
改 src\components\Hello.tsx
import React from 'react';
import * as styles from './Hello.module.less';
import { Button } from 'reactstrap';
interface Props {}
const Hello: React.FC = (props: Props) => {
// return <h1 className="hello">Hello</h1>
return <>
<h1 className={styles.hello}>Hello</h1>
<div>
<Button color="primary">primary</Button>{' '}
<Button color="secondary">secondary</Button>{' '}
<Button color="success">success</Button>{' '}
<Button color="info">info</Button>{' '}
<Button color="warning">warning</Button>{' '}
<Button color="danger">danger</Button>{' '}
<Button color="link">link</Button>
</div>
</>
};
export default Hello;
改 src\components\Hello.tsx,我们来试试效果
import React from 'react';
import * as styles from './Hello.module.less';
import { Button } from 'reactstrap';
const Hello = () => {
// return <h1 className="hello">Hello</h1>
return <>
<h1 className={styles.hello}>Hello</h1>
<div>
<Button color="primary">primary</Button>{' '}
<Button color="secondary">secondary</Button>{' '}
<Button color="success">success</Button>{' '}
<Button color="info">info</Button>{' '}
<Button color="warning">warning</Button>{' '}
<Button color="danger">danger</Button>{' '}
<Button color="link">link</Button>
</div>
</>
};
export default Hello;
npm start, 如果报这个error:
<my project path>/node_modules/reactstrap/dist/reactstrap.cjs.js' implicitly has an 'any'
type.
Try `npm i --save-dev @types/reactstrap` if it exists or add a new declaration (.d.ts) file containing `declare module 'reactstrap';` TS7016
1 | import React from 'react';
2 | import * as styles from './Hello.module.less';
> 3 | import { Button } from 'reactstrap';
去 src\externals.d.ts 声明 reactstrap
declare module '*.less' {
const resource: { [key: string]: string };
export = resource;
};
declare module 'reactstrap';
npm start, 这回倒是不报错了,可是buttons 没颜色,我了个去,原来bootstrap不会自动引入css文件,所以要手工引入。在 src\index.js 加import
import 'bootstrap/dist/css/bootstrap.min.css';
npm start, 那几个buttons 终于有颜色了!!!
bootstrap 算是搞定了,但是我中途遇到过好几次 “TypeError: this.getOptions is not a function”,所以试了好几个版本,心好累,不多说了,放在这里以防万一。
"@types/bootstrap": "^5.1.6",
"@types/reactstrap": "^8.7.2",
"bootstrap": "^4.6.0",
"reactstrap": "^6.5.0"
- React-router
npm i --save-dev react-router-dom @types/react-router-dom @types/query-string
环境真难,搞的差点崩溃,您都看到这儿了,点个赞再走?
---- References ----------------------
# Configuring React 16 + Jest + Enzyme + Typescript
typescript can't find module less
How to use @types/bootstrap in my Typescript and React application
package.json