搭建本身的React+Typescript环境(一)

前言

前阵子在本身学习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

  • webpack4+
  • babel7+
  • typescript3+
  • react16.8+
  • antd3+
  • react-router5+
  • eslint5+

初始化项目

  1. 建立一个目录,名字按本身喜爱来
    mkdir react-ts-template
    cd react-ts-template
    复制代码
  2. 初始化项目,填写项目信息
    yarn init -y 或者 npm init -y
    复制代码

安装webpack

yarn add webpack -D 或者 npm i webpack -D
yarn add webpack-cli -D 或者 npm i webpack-cli -D
复制代码
  • webpack也能够全局安装,不过要注意配置PATH
  • webpack4将命令行相关的操做抽离到了webpack-cli中,好比init、migrate、serve等等,不过都没用过

安装完毕后在根目录新建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 buildreact

"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

Babel

Babel这里使用的是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

接下来加入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依赖,关键的依赖包就是 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"
  ]
}
复制代码

CSS配置

这里咱们须要用到 style-loadercss-loader,先安装它们

  • css-loader使你可以使用相似@import 和 url(...)的方法实现 require()的功能;
  • style-loader将全部的计算后的样式加入页面中; 两者组合在一块儿使你可以把样式表嵌入webpack打包后的JS文件中。
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

若是想要在编写样式时使用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属性

既然已经可使用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呢?借用官方的话:

PostCSS 是一个容许使用 JS 插件转换样式的工具。 这些插件能够检查(lint)你的 CSS,支持 CSS Variables 和 Mixins, 编译还没有被浏览器普遍支持的先进的 CSS 语法,内联图片,以及其它不少优秀的功能。

它提供了不少经常使用的插件

  • 提早使用先进的 CSS 特性
    • autoprefixer 添加了 vendor 浏览器前缀,它使用 Can I Use 上面的数据。
    • postcss-preset-env 容许你使用将来的 CSS 特性。
  • 更佳的 CSS 可读性
    • precss 囊括了许多插件来支持相似 Sass 的特性,好比 CSS 变量,套嵌,mixins 等。
  • 图片和字体
    • postcss-assets 能够插入图片尺寸和内联文件。
    • postcss-sprites 能生成雪碧图。

...还有不少,具体能够查看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优化

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]')
      }
    }]
  }
复制代码

ESlint

关于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
        },
    ]
复制代码

AntDesign

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/*"]
}
复制代码

更新babel配置

以前加了 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'
  ]
}
复制代码

React-router

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
复制代码

更多

到这里基本的功能已经具有了,接下来还须要一些优化以及拆分开发和生产环境的配置,篇幅有点长了,放到下一篇文章里写吧。

最后附上地址 项目地址,若是有不对的地方但愿各位指出,感谢。

相关文章
相关标签/搜索