以前一直使用Vue框架,此次打算學習一下React順便學習一下TypeScript,因而就有了這個項目。在開發過程當中遇到了一些問題,感受這些坑或者說剛開始學不知道的要注意的點,是有必要記錄下來的。javascript
首先咱們用$ yarn init
初始化一個項目,這樣咱們就在項目根目錄獲得一個package.json
文件,這個文件是項目配置文件,咱們須要對他進行一些改動, 向其json中加入以下代碼:css
"scripts": {
"dev": "npx webpack --config webpack.config.js"
}
複製代碼
上面的配置做用是:指定項目運行命令html
其次,咱們把須要的包都下載下來:java
$ yarn add react react-dom core-js regenerator-runtime
$ yarn add -D @babel/core @bable/preset-env @babel/preset-react @babel/preset-typescript
$ yarn add -D typescript
$ yarn add -D @types/react @types/react-dom
$ yarn add -D node-sass
$ yarn add -D webpack webpack-cli
$ yarn add -D babel-loader css-loader sass-loader source-map-loader style-loader ts-loader
複製代碼
這些包都是幹什麼的呢?node
爲何要下載
@types/xxx
包?
你可能會遇到Could not find a declaration file for module 'xxx'
的問題,這個問題是由於TypeScript還不認識相關包,要想讓typescript認識他們,就要下載相應的@types/xxx
包。react
爲何要下載loader?
由於Webpack是由Node.js編寫的項目打包工具,這就意味着它只能認識JavaScript文件,要想讓他認識其餘類型的文件,就要使用到這些loader包去加載、編譯、解析。webpack
在項目根目錄中新建一個webpack.config.js
文件,內容以下:git
module.exports = {
mode: "development",
watch: true, // 讓webpack監聽項目,項目更新後當即進行從新編譯,實現開發過程當中的即時更新
entry: "./src/index.tsx", // 項目入口
output: { // 編譯打包後的項目輸出
filename: "bundle.js",
path: __dirname + "/dist"
},
resolve: {
extensions: [".ts", ".tsx", ".js", ".json"]
},
devtool: "source-map",
module: {
rules: [ // 配置解析規則,爲被正則匹配到的文件指定不一樣的loader
{ test: /\.scss$/, use: [ "style-loader", "css-loader", "sass-loader" ] }, // loader鏈,從右至左解析輸出文件
{ test: /\.tsx?$/, loader: "babel-loader" },
{ test: /\.tsx?$/, loader: "ts-loader" },
{ enforce: "pre", test: /\.js$/, loader: "source-map-loader" }
]
}
};
複製代碼
Webpack相關知識
Webpack從項目入口開始檢索依賴,將全部依賴性通過loader、編譯以後,打包輸出至出口文件web
在項目根目錄中新建一個.babelrc
文件,內容以下:chrome
{
"presets": [
"@babel/react",
"@babel/typescript",
[
"@babel/env",
{
"modules": false,
"targets": {
"chrome": "58",
"ie": "11"
}
}
]
],
}
複製代碼
爲何要用Babel?
Babel能夠幫咱們把typescript文件編譯成javascript文件,而且可以實現對某些瀏覽器的兼容編譯,即編譯成置頂環境支持的語法,能夠理解爲一個polyfill機。
在項目根目錄中新建一個tsconfig.json
文件,內容以下:
{
"compilerOptions": {
"outDir": "dist/",
"noImplicitAny": true,
"module": "commonjs",
"target": "es2015",
"jsx": "react"
},
"include": [
"./src/**/*"
]
}
複製代碼
在配置完項目以後,咱們的項目結構應該是這樣的
.
├── .babelrc
├── node_modules
| └─ ...
├── package.json
├── tsconfig.json
├── webpack.config.js
└── yarn.lock
複製代碼
什麼是yarn.lock文件?
yarn.lock文件中儲存着你這個項目所須要用到的包的信息。這樣別人想運行你的項目時,就不須要一個一個下載你項目中所用到的依賴,只須要運行一下yarn install
就能夠下載下來所有依賴,你就也不須要將巨大的node_modules文件上傳至git倉庫了。同理package-lock.json
也是相同的做用,不過只是它是npm的依賴文件。
首先咱們須要一個public/index.html
的入口文件,其內容以下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>React Project</title>
</head>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
<div id="root"></div>
<!-- bundle.js即在webpack中配置的編譯出口文件 -->
<script src="../dist/bundle.js"></script>
</body>
</html>
複製代碼
其次是src/index.tsx
文件,即webpack中配置的項目入口文件,內容以下:
import * as React from 'react';
import * as ReactDom from 'react-dom';
ReactDom.render(
<div>Hello World!</div>,
document.getElementById('root')
);
複製代碼
而開發過程當中的scss代碼均保存爲.scss文件,webpack就能夠自動將其編譯輸出爲css文件。
還記得以前在package.json
中配置的運行命令,如今咱們在項目根目錄下運行:
$ yarn run dev
複製代碼
就可讓webpack監聽項目目錄,這樣當文件發生變更時,webpack就能夠自動將文件編譯、打包、輸出到./dist
文件夾下。
這時咱們打開public/index.html
便可成功運行看到Hello World!
字樣,項目就運行成功了。
下載依賴:
yarn add react-router react-router-dom
yarn add -D @types/react-router @types/react-router-dom
複製代碼
index.tsx
代碼:
import * as React from 'react';
import * as ReactDom from 'react-dom';
import { BrowserRouter, Switch, Route } from 'react-router-dom';
// import Components
import MenuBar from "./components/MenuBar";
// import Pages
import Article from "./pages/Article";
import About from "./pages/About";
import ArticleList from "./pages/ArticleList";
ReactDom.render(
<BrowserRouter>
<Switch>
<Route exact path="/" component={MenuBar}/>
<Route path="article" component={ArticleList}>
<Route path=":id" component={Article} />
</Route>
<Route path="about" component={About} />
</Switch>
</BrowserRouter>,
document.getElementById('root')
);
複製代碼
這是當你使用了async/await
以後Babel產生的錯誤,解決這個錯誤只須要安裝一個包,而且增長一些.babelrc
配置就能夠了。
$ yarn add -D @babel/plugin-transform-runtime
複製代碼
在.babelrc
文件中添加以下配置:
"plugins": [
"@babel/plugin-transform-runtime"
]
複製代碼
沒有添加css-loader/url-loader。
下載loader:
$ yarn add -D url-loader file-loader
複製代碼
在webpack.config.js
中添加配置:
{ test: /\.css$/, use: [ "style-loader", "css-loader"] },
{ test: /\.(png|woff|woff2|eot|ttf|svg)$/, loader: 'url-loader?limit=100000' }
複製代碼
另外,若是你在使用Next.js,注意要下載@zeit/next-css
$ yarn add @zeit/next-css
複製代碼
並在next.config.js
中添加配置:
const withCss = require('@zeit/next-css');
module.exports = withCss({
webpack: function (config) {
config.module.rules.push({
test: /\.(eot|woff|woff2|ttf|svg|png|jpg|gif)$/,
use: {
loader: 'url-loader',
options: {
limit: 100000,
name: '[name].[ext]'
}
}
});
return config;
}
});
複製代碼
若是有多個withXxx
記得嵌套而不是並列:
const withCss = require('@zeit/next-css');
const withSass = require('@zeit/next-sass');
module.exports = withSass(withCss({
webpack: function (config) {
config.module.rules.push({
test: /\.(eot|woff|woff2|ttf|svg|png|jpg|gif)$/,
use: {
loader: 'url-loader',
options: {
limit: 100000,
name: '[name].[ext]'
}
}
});
return config;
}
}));
複製代碼
問題描述: 在Uncontrolled Component下的onInput/onChange事件沒法鍵入中文
參考文章: Controlled and uncontrolled component design pattern in React
解決方案:
使用CompositionEvent。在onCompositionStart/onCompositionUpdate的時候開啓鎖,onCompositionEnd的時候關閉鎖,並在onChange的handler中判斷:只有在鎖關閉的時候更改Value。參考文章: 中文輸入法與React文本輸入框的問題與解決方案
Chrome 53之後的版本中,onChange事件被調整到了onCompositionEnd以後執行,那麼就須要在onCompositionEnd中對Chrome瀏覽器進行特判,並執行onChangeHandler中的邏輯.
將Uncontrolled Component替換爲Controlled Component。這個方法最簡單粗暴,也是最有效的。