åè¨
大æ¦å¨ 2019 å¹´ï¼èªå·±æå»º React å¼åç¯å¢çæ³æ³èè½ï¼å°ç®å为æ¢ï¼å
¬å¸çå¾å¤é¡¹ç®ä¸ï¼ä¹å¨ä½¿ç¨ä¸ï¼æ¯è¾ç¨³å®ã为ä»ä¹è¦èªå·±é è½®åï¼èµ·åæ¯å 为èªå·±å¹¶ä¸æ»¡æå¸é¢ä¸çèææ¶ãå¦å¤ï¼é è½®å对äºèªå·±ä¹æä¸äºææ¯ä¸ç帮å©ï¼å¦å«äººäºæ¬¡å°è£
çä¸è¥¿ï¼ä¸å¦ç´æ¥ä½¿ç¨åºå±çåºï¼è¿æ ·ä¹æå©äºèªå·±ç³»ç»çå¦ä¹ ä¸éç¥è¯ï¼åºè¯ä¸å¤è¯´ï¼ç´æ¥è¿å
¥æ£æï¼å¦ä½æå»ºèªå·±çå¼åç¯å¢ã
åå§å
å建æä»¶å¤¹å¹¶è¿å ¥ï¼
$ mkdir tristana && cd tristana
åå§å package.json
$ npm init
å®è£
Webpack
$ npm install webpack webpack-cli --save-dev
å建以ä¸ç®å½ç»æãæä»¶åå 容ï¼
project
tristana|- package.json|- /dist |- index.html|- /script |- webpack.config.js|- index.html|- /src |- index.js
src/index.js
document.getElementById("root").append("React");
index.html && dist/index.html
tristana
script/webpack.config.js
module.exports = {
mode: "development",
entry: "./src/index.js",
};
package.json
{ // ...
"scripts": { "build": "webpack --mode=development --config script/webpack.config.js"
},
}
ç¶åæ ¹ç®å½ç»ç«¯è¾å
¥ï¼npm run build
卿µè§å¨ä¸æå¼ dist ç®å½ä¸ç index.htmlï¼å¦æä¸åæ£å¸¸ï¼ä½ åºè¯¥è½çå°ä»¥ä¸ææ¬ï¼'React'
index.html ç®åæ¾å¨ dist ç®å½ä¸ï¼ä½å®æ¯æå¨å建çï¼ä¸é¢ä¼æä½ å¦ä½çæ index.html èéæå¨ç¼è¾å®ã
Webpack æ ¸å¿åè½
Babel
$ npm install @babel/cli @babel/core babel-loader @babel/preset-env --save-dev
script/webpack.config.js
module.exports = { // ...
module: {
rules: [
{
test: /\.(js|jsx)$/,
loader: "babel-loader",
exclude: /node_modules/,
},
],
},
};
.babelrc
卿 ¹ç®å½ä¸æ·»å .babelrc æä»¶ï¼
{ "presets": ["@babel/preset-env", "@babel/preset-react"]
}
æ ·å¼
$ npm install style-loader css-loader less less-loader --save-dev
script/webpack.config.js
module.exports = { // ... module: { rules: [
{ test: /\.(css|less)$/, use: [
{ loader: "style-loader",
},
{ loader: "css-loader", options: { importLoaders: 1,
},
},
{ loader: "less-loader", lessOptions: { javascriptEnabled: true,
},
},
],
},
],
},
};
å¾çåä½
$ npm install file-loader --save-dev
script/webpack.config.js
module.exports = { // ...
module: {
rules: [
{
test: /\.(png|svg|jpg|gif|jpeg)$/,
loader: 'file-loader'
},
{
test: /\.(woff|woff2|eot|ttf|otf)$/,
loader: 'file-loader'
}
],
},
};
HTML
$ npm install html-webpack-plugin --save-dev
script/webpack.config.js
const HtmlWebpackPlugin = require('html-webpack-plugin');module.exports = { // ...
plugins: {
html: new HtmlWebpackPlugin({
title: 'tristana',
template: 'public/index.html'
}),
}
};
index.html
tristana
å¼åæå¡
$ npm install webpack-dev-server --save-dev
script/webpack.config.js
const path = require("path");const HtmlWebpackPlugin = require('html-webpack-plugin');module.exports = { // ...
devServer: { contentBase: path.resolve(__dirname, "dist"), hot: true, historyApiFallback: true, compress: true,
},
};
package.json
{ // ...
"scripts": { "start": "webpack serve --mode=development --config script/webpack.config.js"
}, // ...}
æ¸ ç dist
$ npm install clean-webpack-plugin --save-dev
script/webpack.config.js
const { CleanWebpackPlugin } = require('clean-webpack-plugin');module.exports = { // ...
plugins: { new CleanWebpackPlugin()
}
};
Tips
ç±äº webpack 使ç¨çæ¯^5.21.2 çæ¬ï¼å¨ä½¿ç¨è¯¥æä»¶æ¶ï¼ä¼æç¤ºclean-webpack-plugin: options.output.path not defined. Plugin disabled...ï¼ææ¶è¿æªè§£å³ã
ç¯å¢åé
$ npm install cross-env --save-dev
package.json
{ // ...
"scripts": { "start": "cross-env ENV_LWD=development webpack serve --mode=development --config script/webpack.config.js", "build": "cross-env ENV_LWD=production webpack --mode=production --config script/webpack.config.js"
}, // ...}
.jsx æä»¶
å®è£ ä¾èµ
$ npm install @babel/preset-react react react-dom --save-dev
.babelrc
{ "presets": ["@babel/preset-env", "@babel/preset-react"]
}
src/App.jsx
å¨ src ç®å½ä¸ï¼æ°å¢ App.jsx æä»¶ï¼
import React, { Component } from "react";class App extends Component {
render() { return (
Hello, World!
);
}
}
export default App;
src/index.js
import React from "react";import ReactDOM from "react-dom";import App from "./App.jsx"; ReactDOM.render(, document.getElementById("root"));
React Router
å®è£ ä¾èµ
$ npm install react-router history --save
src/index.js
import React from "react";import ReactDOM from "react-dom";import { Router, Route, Link } from "react-router";import { createBrowserHistory } from "history";import App from "./App.jsx";const About = () => { return <>About>;
};
ReactDOM.render(
, document.getElementById("root")
);
MobX
å®è£ ä¾èµ
$ npm install mobx mobx-react babel-preset-mobx --save
.babelrc
{ "presets": ["@babel/preset-env", "@babel/preset-react", "mobx"]
}
src/store.js
å¨ src ç®å½ä¸æ°å»º store.js
import { observable, action, makeObservable } from "mobx";class Store {
constructor() {
makeObservable(this);
}
@observable
count = 0;
@action("add") add = () => { this.count = this.count + 1;
};
@action("reduce") reduce = () => { this.count = this.count - 1;
};
}export default new Store();
index.js
import { Provider } from "mobx-react";import Store from "./store";// ...ReactDOM.render(
, document.getElementById("root")
);
src/App.jsx
import React, { Component } from "react";import { observer, inject } from "mobx-react";@inject("store")@observerclass App extends Component { render() { return (
{this.props.store.count}
);
}
}export default App;
Ant Design
å®è£ ä¾èµ
$ npm install antd babel-plugin-import --save
.babelrc
{ // ...
"plugins": [
[ "import",
{ "libraryName": "antd", "libraryDirectory": "es", "style": true
}
]
]
}
src/App.jsx
// ...import { DatePicker } from "antd";import "antd/dist/antd.css";@inject("store")@observerclass App extends Component {
render() { return (
);
}
}
export default App;
TypeScript
å®è£ ä¾èµ
$ npm install typescript @babel/preset-typescript --save-dev
.babelrc
{ "presets": [ // ...
"@babel/preset-typescript"
]
}
tsconfig.json
卿 ¹ç®å½ä¸ï¼æ°å¢ tsconfig.json æä»¶ï¼
{ "compilerOptions": { "emitDecoratorMetadata": true, "experimentalDecorators": true, "target": "ES5", "allowSyntheticDefaultImports": true, "strict": true, "forceConsistentCasingInFileNames": true, "allowJs": true, "outDir": "./dist/", "esModuleInterop": true, "noImplicitAny": false, "sourceMap": true, "module": "esnext", "moduleResolution": "node", "isolatedModules": true, "importHelpers": true, "lib": ["esnext", "dom", "dom.iterable"], "skipLibCheck": true, "jsx": "react", "typeRoots": ["node", "node_modules/@types"], "rootDirs": ["./src"], "baseUrl": "./src"
}, "include": ["./src/**/*"], "exclude": ["node_modules"]
}
src/App.jsx
æ´æ¢æä»¶åç¼ App.jsx -> App.tsx
import React, { Component } from "react";import { observer, inject } from "mobx-react";import { DatePicker } from "antd";import "antd/dist/antd.css";@inject("store")@observerclass App extends Component { props: any; render() { return (
{this.props.store.count}
);
}
}export default App;
代ç è§è
ä»£ç æ ¡éªãä»£ç æ ¼å¼åãGit æäº¤åæ ¡éªãVscodeé
ç½®ãç¼è¯æ ¡éª
ESLint
å®è£ ä¾èµ
$ npm install @typescript-eslint/parser eslint eslint-plugin-standard @typescript-eslint/parser @typescript-eslint/eslint-plugin eslint-plugin-promise --save-dev
.eslintrc.js
卿 ¹ç®å½ä¸ï¼æ°å¢ .eslintrc.js æä»¶ï¼
module.exports = { extends: ["eslint:recommended", "plugin:react/recommended"], env: { browser: true, commonjs: true, es6: true,
}, globals: {
$: true, process: true, __dirname: true,
}, parser: "@typescript-eslint/parser", parserOptions: { ecmaFeatures: { jsx: true, modules: true,
}, sourceType: "module", ecmaVersion: 6,
}, plugins: ["react", "standard", "promise", "@typescript-eslint"], settings: { "import/ignore": ["node_modules"], react: { version: "latest",
},
}, rules: { quotes: [2, "single"], "no-console": 0, "no-debugger": 1, "no-var": 1, semi: ["error", "always"], "no-irregular-whitespace": 0, "no-trailing-spaces": 1, "eol-last": 0, "no-unused-vars": [ 1,
{ vars: "all", args: "after-used",
},
], "no-case-declarations": 0, "no-underscore-dangle": 0, "no-alert": 2, "no-lone-blocks": 0, "no-class-assign": 2, "no-cond-assign": 2, "no-const-assign": 2, "no-delete-var": 2, "no-dupe-keys": 2, "use-isnan": 2, "no-duplicate-case": 2, "no-dupe-args": 2, "no-empty": 2, "no-func-assign": 2, "no-invalid-this": 0, "no-redeclare": 2, "no-spaced-func": 2, "no-this-before-super": 0, "no-undef": 2, "no-return-assign": 0, "no-script-url": 2, "no-use-before-define": 2, "no-extra-boolean-cast": 0, "no-unreachable": 1, "comma-dangle": 2, "no-mixed-spaces-and-tabs": 2, "prefer-arrow-callback": 0, "arrow-parens": 0, "arrow-spacing": 0, camelcase: 0, "jsx-quotes": [1, "prefer-double"], "react/display-name": 0, "react/forbid-prop-types": [ 2,
{ forbid: ["any"],
},
], "react/jsx-boolean-value": 0, "react/jsx-closing-bracket-location": 1, "react/jsx-curly-spacing": [ 2,
{ when: "never", children: true,
},
], "react/jsx-indent": ["error", 4], "react/jsx-key": 2, "react/jsx-no-bind": 0, "react/jsx-no-duplicate-props": 2, "react/jsx-no-literals": 0, "react/jsx-no-undef": 1, "react/jsx-pascal-case": 0, "react/jsx-sort-props": 0, "react/jsx-uses-react": 1, "react/jsx-uses-vars": 2, "react/no-danger": 0, "react/no-did-mount-set-state": 0, "react/no-did-update-set-state": 0, "react/no-direct-mutation-state": 2, "react/no-multi-comp": 0, "react/no-set-state": 0, "react/no-unknown-property": 2, "react/prefer-es6-class": 2, "react/prop-types": 0, "react/react-in-jsx-scope": 2, "react/self-closing-comp": 0, "react/sort-comp": 0, "react/no-array-index-key": 0, "react/no-deprecated": 1, "react/jsx-equals-spacing": 2,
},
};
.eslintignore
卿 ¹ç®å½ä¸ï¼æ°å¢ .eslintignore æä»¶ï¼
src/assets
.vscode
卿 ¹ç®å½ä¸æ°å¢ .vscode æä»¶å¤¹ï¼ç¶åæ°å¢ .vscode/settings.json
{ "eslint.validate": [ "javascript", "javascriptreact", "typescript", "typescriptreact"
]
}
Perttier
å®è£ ä¾èµ
$ npm install prettier --save-dev
prettier.config.js
卿 ¹ç®å½ä¸ï¼æ°å¢ prettier.config.js æä»¶ï¼
module.exports = { // ä¸è¡æå¤ 100 å符
printWidth: 100, // ä½¿ç¨ 4 ä¸ªç©ºæ ¼ç¼©è¿
tabWidth: 4, // ä¸ä½¿ç¨ç¼©è¿ç¬¦ï¼è使ç¨ç©ºæ ¼
useTabs: false, // è¡å°¾éè¦æåå·
semi: true, // 使ç¨åå¼å·
singleQuote: true, // 对象ç key ä»
å¨å¿
è¦æ¶ç¨å¼å·
quoteProps: 'as-needed', // jsx ä¸ä½¿ç¨åå¼å·ï¼è使ç¨åå¼å·
jsxSingleQuote: false, // æ«å°¾ä¸éè¦éå·
trailingComma: 'none', // 大æ¬å·å
çé¦å°¾éè¦ç©ºæ ¼
bracketSpacing: true, // jsx æ ç¾çåå°æ¬å·éè¦æ¢è¡
jsxBracketSameLine: false, // ç®å¤´å½æ°ï¼åªæä¸ä¸ªåæ°çæ¶åï¼ä¹éè¦æ¬å·
arrowParens: 'avoid', // æ¯ä¸ªæä»¶æ ¼å¼åçèå´æ¯æä»¶çå
¨é¨å
容
rangeStart: 0,
rangeEnd: Infinity, // ä¸éè¦åæä»¶å¼å¤´ç @prettier
requirePragma: false, // ä¸éè¦èªå¨å¨æä»¶å¼å¤´æå
¥ @prettier
insertPragma: false, // 使ç¨é»è®¤çæè¡æ å
proseWrap: 'preserve', // æ ¹æ®æ¾ç¤ºæ ·å¼å³å® html è¦ä¸è¦æè¡
htmlWhitespaceSensitivity: 'css', // æ¢è¡ç¬¦ä½¿ç¨ lf
endOfLine: 'lf'};
stylelint
å®è£ ä¾èµ
$ npm install stylelint stylelint-config-standard stylelint-config-prettier --save-dev
stylelint.config.js
卿 ¹ç®å½ä¸ï¼æ°å¢ stylelint.config.js æä»¶ï¼
module.exports = {
extends: ['stylelint-config-standard', 'stylelint-config-prettier'],
ignoreFiles: [ '**/*.ts', '**/*.tsx', '**/*.png', '**/*.jpg', '**/*.jpeg', '**/*.gif', '**/*.mp3', '**/*.json'
],
rules: { 'at-rule-no-unknown': [
true,
{
ignoreAtRules: ['extends', 'ignores']
}
],
indentation: 4, 'number-leading-zero': null, 'unit-allowed-list': ['em', 'rem', 's', 'px', 'deg', 'all', 'vh', '%'], 'no-eol-whitespace': [
true,
{
ignore: 'empty-lines'
}
], 'declaration-block-trailing-semicolon': 'always', 'selector-pseudo-class-no-unknown': [
true,
{
ignorePseudoClasses: ['global']
}
], 'block-closing-brace-newline-after': 'always', 'declaration-block-semicolon-newline-after': 'always', 'no-descending-specificity': null, 'selector-list-comma-newline-after': 'always', 'selector-pseudo-element-colon-notation': 'single'
}
};
lint-stagedãpre-commit
å®è£ ä¾èµ
$ npm install lint-staged prettier eslint pre-commit --save-dev
package.json
{ // ...
"scripts": { "lint:tsx": "eslint --ext .tsx src && eslint --ext .ts src", "lint:css": "stylelint --aei .less .css src", "precommit": "lint-staged", "precommit-msg": "echo 'Pre-commit checks...' && exit 0"
}, "pre-commit": [ "precommit", "precommit-msg"
], "lint-staged": { "*.{js,jsx,ts,tsx}": [ "eslint --fix", "prettier --write", "git add"
], "*.{css,less}": [ "stylelint --fix", "prettier --write", "git add"
]
}
}
eslint-webpack-plugin
å®è£ ä¾èµ
$ npm install eslint-webpack-plugin --save-dev
script/webpack.config.js
const ESLintPlugin = require('eslint-webpack-plugin');module.exports = { // ...
plugins: [new ESLintPlugin()],
};
æ»ç»
æå»ºè¿ä¸ªçè¿ç¨ï¼ä¹æ¯éå°äºä¸å°åï¼æ¶è·ä¹æ¯è®å¤çï¼å¸æè¿ä¸ªæç¨è½å¤å¸®å©æ´å¤çåå¦ï¼å°éç¹åï¼å®æ´ç React å¼åç¯å¢å¯ä»¥çè¿ä¸ªtristanaï¼æ±ç¹èµï¼æ±å
³æ³¨ï¼

