前陣子在本身學習React,最開始上手使用的creat-react-app
來建立本身的項目,2版本以後的create-react-app已經支持了不少功能,好比sass、數據mock、typescript支持等等,也升級了相關依賴babel、webpack到一個最新的版本,具體能夠參照Create React App 中文文檔,可是它將項目的webpack配置等內容給藏起來了,想要本身配置的話還要npm run eject
纔可見,不過對於我這種初學者已經足夠了,可是本着折騰的精神,在掘金看了好多大佬的配置文章,終於折騰出一個本身的項目模板,若是有什麼問題或者不對的地方,但願大佬們能及時指出,最後有項目地址~javascript
第二篇生產開發環境配置已經寫完:搭建本身的React+Typescript環境(二)css
主要的依賴以及版本html
mkdir react-ts-template
cd react-ts-template
複製代碼
yarn init -y 或者 npm init -y
複製代碼
yarn add webpack -D 或者 npm i webpack -D
yarn add webpack-cli -D 或者 npm i webpack-cli -D
複製代碼
安裝完畢後在根目錄新建build文件夾,並新建一個webpack.common.js文件,用來存放webpack的公共配置vue
mkdir build
cd build
touch webapck.common.js
複製代碼
而後在webpack.common.js中簡單的配置入口(entry)跟輸出(output)。java
const path = require('path'); module.exports={ entry: path.join(__dirname, '../src/index.js'), output: { filename: 'bundle.js', path: path.join(__dirname, '../dist') } } 複製代碼
接着在根目錄下再新建src文件夾,用來存放主要代碼,並新建index.js,隨便寫點東西。node
console.log('Hello World'); 複製代碼
在package.json中加入一個腳本,並在控制檯中運行它npm run build
react
"scripts": {
"build": "webpack --config build/webpack.common.js"
}
複製代碼
以後會發現生成了一個dist文件夾,而且還有一個bundle.js,同時控制檯還會有報錯webpack
WARNING in configuration The 'mode' option has not been set, webpack will fallback to 'production' for this value. Set 'mode' option to 'development' or 'production' to enable defaults for each environment. You can also set it to 'none' to disable any default behavior. Learn more: https://webpack.js.org/configuration/mode/ 複製代碼
webpack4中提供了 mode
配置選項,告知 webpack 使用相應模式的內置優化,上面這個警告寫着若是不提供mode,webpack將會使用production
模式。咱們把scripts修改一下。git
"scripts": {
"build": "webpack --config build/webpack.common.js --mode production"
}
複製代碼
這樣的webpack簡單的打包功能就有了。es6
Bable這裏使用的是7版本,與以前版本不一樣的是安裝依賴時的包名,像babel-core、babel-preset-env、babel-polyfill等,名字已經更換成了@babel/core、@babel/preset-env、@babel/polyfill,這裏先安裝主要的包。
yarn add @babel/core @babel/preset-env @babel/preset-react babel-loader -D
複製代碼
而後在根目錄下新建一個babel.config.js,這個配置文件跟.babelrc.js是有區別的,根據官網來看babel.config.js是項目級別的一個配置,詳細信息能夠參照官網 Config Files,在其中添加以下內容:
module.exports = { presets: [ '@babel/preset-env', '@babel/preset-react' ], plugins: [] } 複製代碼
修改webpack.common.js,增長 js 文件的 loader 配置,以後還會改。
module: { rules: [{ test: /\.js$/, use: ['babel-loader'], include: path.join(__dirname, '../src') }] } 複製代碼
接下來加入React,也是最重要的部分。
yarn add react react-dom
複製代碼
修改 src/index.js 中的內容
import React from 'react'; import ReactDom from 'react-dom'; ReactDom.render(<div>Hello React!</div>, document.getElementById('root')); 複製代碼
而後在根目錄下新建一個public文件夾,並在裏面新建一個index.html
<!DOCTYPE html>
<html>
<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-TS-Tempalte</title> </head> <body> <div id="root"></div> </body> </html> 複製代碼
想要 webpack 能以這個html爲模板,還須要一個html-webpack-plugin
插件,安裝它yarn add html-webpack-plugin -D
並在webpack.common.js中增長以下配置,這也是用到的第一個插件:
const HtmlWebpackPlugin = require('html-webpack-plugin'); ... plugins: [ new HtmlWebpackPlugin({ filename: 'index.html', template: 'public/index.html', inject: true }) ] 複製代碼
讓咱們打包後打開dist下的index.html看看效果,成功地展現了Hello React。
接下來安裝 webpack-dev-server
來啓動一個簡單的服務器。
yarn add webpack-dev-server -D
複製代碼
修改webpack.common.config.js,增長webpack-dev-server的配置。
devServer: {
host: 'localhost', port: 3000, historyApiFallback: true, overlay: { //當出現編譯器錯誤或警告時,就在網頁上顯示一層黑色的背景層和錯誤信息 errors: true }, inline: true, hot: true } 複製代碼
接下來須要在package.json中增長一個script
"scripts": { "dev": "webpack-dev-server --config build/webpack.common.js --mode development --open" } 複製代碼
在控制檯中輸入npm run dev
,即可以在http://localhost:3000
中看到啓動的項目。
下面加入Typescript依賴,關鍵的依賴包就是 typescript,不過還須要安裝對應的types:@types/react、@types/react-dom。
yarn add typescript @types/react @types/react-dom -D
複製代碼
接下來須要把以前的 js、jsx 文件替換成對應的 ts、tsx,同時還須要對應的loader,可使用 ts-loader
以及以前安裝過的 babel-loader
,這裏使用以前安裝的 babel-loader
,在webpack.common.js中添加配置:
rules: [
{
test: /\.(j|t)sx?$/, include: [resolve('../src')], use: [ { loader: 'babel-loader' } ], // 排除node_modules底下的 exclude: /node_modules/ } ] 複製代碼
修改 webpack 的入口配置
module.exports={ entry: path.join(__dirname, '../src/index.tsx') } 複製代碼
不要忘記把src下的index.js改爲index.tsx
配置tsconfig.json,這個文件也是使用ts時很關鍵的一個文件,下面是官網的推薦配置。
{
"compilerOptions": {
/* Basic Options */
"target": "es5", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019' or 'ESNEXT'. */
"module": "esnext", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */
"lib": [
"dom",
"dom.iterable",
"esnext"
], /* Specify library files to be included in the compilation. */
"allowJs": true, /* Allow javascript files to be compiled. */
"jsx": "react", /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */
"sourceMap": true, /* Generates corresponding '.map' file. */
"outDir": "./dist", /* Redirect output structure to the directory. */
"isolatedModules": true, /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */
"resolveJsonModule": true,
"forceConsistentCasingInFileNames": true,
"skipLibCheck": true,
"strict": true, /* Enable all strict type-checking options. */
"noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */
"noImplicitReturns": true, /* Report error when not all code paths in function return a value. */
"moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */
"baseUrl": ".", /* Base directory to resolve non-absolute module names. */
"paths": {}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */
"allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */
"esModuleInterop": true, /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */
"experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */
},
"include": [
"src"
],
"exclude": [
"node_modules"
]
}
複製代碼
這裏咱們須要用到 style-loader
、css-loader
,先安裝它們
yarn add style-loader css-loader -D
複製代碼
而後在webpack.common.js中添加相應的規則
{
test: /\.css$/, // 正則匹配文件路徑 exclude: /node_modules/, use: [ // 注意loader生效是從下往上的 'style-loader', 'css-loader' ] } 複製代碼
接着在webpack.common.js中配置resolve.extensions,來自動解析肯定的擴展。
resolve: {
extensions: ['.ts', '.tsx', '.js', 'jsx'] } 複製代碼
在根目錄下新建一個index.css,以及App.tsx,並在index.tsx中引入它們
// index.css
.app {
background-color: red;
}
// App.tsx
import * as React from 'react' class App extends React.Component { render() { return ( <div className="app"> Hello React </div> ) } } export default App // index.tsx import React from 'react' import ReactDOM from 'react-dom' import App from './App' import './index.css' ReactDOM.render(<App />, document.getElementById('root')) 複製代碼
啓動後即可以看到設置的紅色背景
若是想要在編寫樣式時使用sass的語法,就須要安裝相應的loader。
yarn add sass-loader node-sass -D
複製代碼
注意:node-sass安裝時可能會遇到網絡問題,須要使用淘寶鏡像源。
安裝完成後在webpack.common.js中加入 .scss 文件的規則
{
test: /\.scss$/, include: path.join(__dirname, '../src'), use: [ 'style-loader', 'css-loader', 'sass-loader' ] } 複製代碼
接下來咱們把根目錄下的index.css改爲index.scss,不要忘記index.tsx中引入的文件後綴也要修改,項目啓動後發現能夠成功解析scss文件。
既然已經可使用sass進行更加簡便的css代碼編寫,那麼咱們也能夠將經常使用的一些樣式代碼和sass變量寫入公共文件中,當使用的時候就能夠直接引入使用。
在src目錄下新建styles文件夾,而後新建一個var.scss文件用於存放樣式變量。 以後在var.scss文件裏寫入一個顏色變量和一個樣式:
$red: red; @mixin ellipsis { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } 複製代碼
而後在剛纔的index.scss文件中引入它。
@import './styles/var.scss'; .app{ background: $red; .aps { width: 50px; @include ellipsis; } } 複製代碼
這樣配置以後還存在一個優化上的問題,若是須要在不一樣的層級引入var.scss就要根據每一個文件夾的路徑相對來引入很是麻煩,那麼咱們可否作到只須要@import var.scss就行呢?答案是能夠的,咱們能夠經過配置loader的屬性includePaths進行路徑優化,修改webpack.common.js。
{
test: /\.scss$/, include: path.join(__dirname, '../src'), use: [ 'style-loader', 'css-loader', { loader: 'sass-loader', options: { includePaths: [path.join(__dirname, '../src/styles')] } } ] } 複製代碼
這樣以後咱們在引入的時候只須要寫文件名稱便可。
@import 'var.scss'; 複製代碼
什麼是PostCSS呢?借用官方的話:
PostCSS 是一個容許使用 JS 插件轉換樣式的工具。 這些插件能夠檢查(lint)你的 CSS,支持 CSS Variables 和 Mixins, 編譯還沒有被瀏覽器普遍支持的先進的 CSS 語法,內聯圖片,以及其它不少優秀的功能。
它提供了不少經常使用的插件
...還有不少,具體能夠查看PostCSS中文Readme
這裏主要用autoprefixer,首先安裝postcss-loader
yarn add postcss-loader autoprefixer -D
複製代碼
以後在根目錄下新建一個postcss.config.js文件,並寫入:
module.exports = { plugins: [ require('autoprefixer') ] } 複製代碼
最後須要在webpack.common.js的樣式相關插件的 css-loader 以後加上配置,以scss爲例
{
test: /\.scss$/, include: path.join(__dirname, '../src'), use: [ 'style-loader', 'css-loader', 'postcss-loader', // 加了這一行 { loader: 'sass-loader', options: { includePaths: [path.join(__dirname, '../src/styles')] } } ] } 複製代碼
隨便寫點樣式,而後在谷歌控制檯能夠發現,會自動幫你添加 -webkit-
的前綴。
注意若是使用了 postcss-preset-env
這個的話,它會自動安裝 autoprefixer
,而且配置了它,就再也不須要配置 autoprefixer
。
const postcssPresetEnv = require('postcss-preset-env'); module.exports = { plugins: [ postcssPresetEnv(/* pluginOptions */) ] } 複製代碼
CSS Modules 是爲了加入局部做用域和模塊依賴,這裏我沒加入它,能夠代替它的方案也有,好比scoped,以及bem命名方式等,這裏我選擇了bem命名方法,掘金也有關於它的介紹,能夠去看看。
首先安裝處理這類資源的加載器
yarn add url-loader file-loader -D
複製代碼
而後在webpack.common.js中加入相關的規則配置
{
test: /\.(png|jpe?g|gif|svg)(\?.*)?$/, use: [ { loader: 'url-loader', options: { //1024 == 1kb //小於10kb時打包成base64編碼的圖片不然單獨打包成圖片 limit: 10240, name: path.join('img/[name].[hash:7].[ext]') } }] }, { test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/, use: [{ loader: 'url-loader', options: { limit: 10240, name: path.join('font/[name].[hash:7].[ext]') } }] } 複製代碼
關於typescript代碼的規範校驗,可選的有tslint和eslint,不過最近官方也推薦往eslint上轉了,因此我這裏使用eslint,安裝相關依賴。
yarn add eslint eslint-plugin-react eslint-plugin-react-hooks @typescript-eslint/eslint-plugin @typescript-eslint/parser
複製代碼
若是不使用 hook
的話,就不用裝 eslint-plugin-react-hooks
這個插件了
在根目錄下新建.eslintrc.js,並寫入配置:
module.exports = { extends: [ "eslint:recommended", "plugin:react/recommended" ], parserOptions: { "ecmaVersion": 2019, "sourceType": "module" }, env: { node: true, browser: true, commonjs: true, es6: true }, parser: '@typescript-eslint/parser', plugins: [ "@typescript-eslint", "react-hooks" ], globals: { // 這裏填入你的項目須要的全局變量 // 這裏值爲 false 表示這個全局變量不容許被從新賦值,好比: // React: false, // ReactDOM: false }, settings: { react: { pragma: "React", version: "detect" } }, rules: { // 這裏填入你的項目須要的個性化配置,好比: // // // @fixable 一個縮進必須用兩個空格替代 semi: ['error', 'never'], 'no-console': 'off', 'no-unused-vars': [ 'warn', { vars: 'all', args: 'none', caughtErrors: 'none' } ], 'max-nested-callbacks': 'off', 'react/no-children-prop': 'off', 'typescript/member-ordering': 'off', 'typescript/member-delimiter-style': 'off', 'react/jsx-indent-props': 'off', 'react/no-did-update-set-state': 'off', "react-hooks/rules-of-hooks": "error", "react-hooks/exhaustive-deps": "warn", indent: [ 'off', 2, { SwitchCase: 1, flatTernaryExpressions: true } ] } } 複製代碼
用 VS Code 開發時,應該還須要配置settings.json
"eslint.autoFixOnSave": true, "eslint.validate": [ "javascript", "javascriptreact", { "language": "html", "autoFix": true }, { "language": "vue", "autoFix": true }, { "language": "typescript", "autoFix": true }, { "language": "typescriptreact", "autoFix": true }, ] 複製代碼
antd是阿里家的一款UI組件庫,官方文檔中關於如何引入使用講的很清楚,咱們來配置一下,先安裝須要的依賴,babel-plugin-import
用於按需引入:
yarn add antd
yarn add babel-plugin-import less less-loader -D
複製代碼
在babel.config.js中增長配置
plugins: [
...
['import', { libraryName: 'antd', libraryDirectory: 'lib', style: true }] ] 複製代碼
還須要在webpack.common.js中配置less規則
{
// for ant design test: /\.less$/, include: resolve('../node_modules'), use: [ 'style-loader', 'css-loader', 'postcss-loader', { loader: 'less-loader', options: { javascriptEnabled: true, modifyVars: theme } } ] } 複製代碼
注意 modifyVars: theme
這個是根據官網來的 自定義主題,須要新建一個 theme.js,這個文件的名字本身定義,這裏修改了一下主要顏色,以及 border-radius-base
的值,還有不少配置具體能夠查看官網。
module.exports = { 'primary-color': 'black', 'border-radius-base': '10px' } 複製代碼
以後即可以按照官網實例愉快的使用了。不過打包以後存在一個問題,icons.js佔了很大的一部分,這裏使用github上的大佬給出的解決方案。
在src下新建一個icons.ts,裏面只寫用到的icon
export { default as DownOutline } from '@ant-design/icons/lib/outline/DownOutline' 複製代碼
而後配置別名,這裏就將在ts中使用別名的方式一塊介紹了。在 webpack.common.js 中的 resolve
項中增長下面配置,跟icon有關的是第二行。
alias: { '@': resolve('../src'), "@ant-design/icons/lib/dist$": resolve('../src/icons.ts'), '@components': resolve('../src/components'), '@img': resolve('../src/assets/img') } 複製代碼
還須要在tsconfig.json中增長配置:
"paths": { "@/*": ["src/*"], "@ant-design/icons/lib/dist$": ["src/icons.js"], "@components/*": ["src/components/*"], "@img/*": ["src/assets/img/*"] } 複製代碼
以前加了 typescript 等依賴,如今來更新一下 babel 的配置,來支持咱們後面可能會用到的功能,好比裝飾器以及路由的動態引入。
yarn add @babel/preset-typescript @babel/plugin-proposal-class-properties @babel/plugin-proposal-decorators @babel/plugin-syntax-dynamic-import -D
複製代碼
修改 babel.config.js
module.exports = { presets: [ '@babel/preset-env', '@babel/preset-typescript', '@babel/preset-react' ], plugins: [ ['import', { libraryName: 'antd', libraryDirectory: 'lib', style: true }], ['@babel/plugin-proposal-decorators', { legacy: true }], ['@babel/plugin-proposal-class-properties', { loose: true }], '@babel/plugin-syntax-dynamic-import' ] } 複製代碼
router使用最新的5版本,而後路由懶加載使用官方例子中的loadable,首先仍是安裝依賴
yarn add react-router-dom
yarn add @loadable/component @types/loadable__component @types/react-router-dom -D
複製代碼
讓咱們在 App.tsx 中使用它們
import * as React from 'react' import { HashRouter as Router, Route, Link } from "react-router-dom" import loadable from '@loadable/component' const HomeComponent = loadable(() => import(/* webpackChunkName: "home" */ './views/Home')) const AboutComponent = loadable(() => import(/* webpackChunkName: "about" */ './views/About')) class App extends React.Component { render() { return ( <div className="app"> <Router> <ul> <li> <Link to="/">To Home</Link> </li> <li> <Link to="/about">To About</Link> </li> </ul> <Route exact path='/' component={HomeComponent}></Route> <Route path='/about' component={AboutComponent}></Route> </Router> <p className="aps">hahahaahhahhahahaha</p> </div> ) } } export default App 複製代碼
到這裏基本的功能已經具有了,接下來還須要一些優化以及拆分開發和生產環境的配置,篇幅有點長了,放到下一篇文章裏寫吧。
最後附上地址 項目地址,若是有不對的地方但願各位指出,感謝。