ゼロからreact+typescript足場を作ります.
29394 ワード
参考文献:私はこのようにTypecript+Reactプロジェクト環境を構築しました。
基本設定
webpack 4+に基づく構築
インストール .
プロジェクトを起動するには、いくつかの設定依存パッケージが必要です. .
基本設定
webpack 4+に基づく構築
インストール
webpack
:npm install webpack@4 webpack-cli@3 -D
設定を保存するための新しいフォルダbuild
.mkdir build
次に、これらのファイルをbuild
フォルダの下に新規作成します.config.js
環境変数proxy.js
プロキシ構成webpack.common.js
汎用構成webpack.dev.js
開発環境構成webpack.prod.js
生産環境構成$ cd build
$ touch config.js proxy.js webpack.common.js webpack.dev.js webpack.prod.js
次に二つの依存パッケージをインストールします.webpack-merge
は、汎用構成webpack.com mmon.jsと開発環境devまたは生産環境prodの構成を統合することができる.cross-env
は、プラットフォームにまたがって環境変数を設定し、使用してもよい.mac
とwindow
の構成の異なる問題を解決するnpm install webpack-merge cross-env -D
変更package.json
ファイル:"scripts": {
"start": "cross-env NODE_ENV=development webpack-dev-server --config ./build/webpack.dev.js",
"build": "cross-env NODE_ENV=production webpack --config ./build/webpack.prod.js"
}
構築に必要な環境変数を準備し、config.js
を修正する.const SERVER_PORT = 9527
const SERVER_HOST = '127.0.0.1'
const PROJECT_NAME = "cli"
const isDev = process.env.NODE_ENV !== 'production'
module.exports = {
isDev,
PROJECT_NAME,
SERVER_PORT,
SERVER_HOST
}
次に、webpack
プロファイルを準備します.// webpack.common.js
const { resolve } = require('path')
module.exports = {
entry: resolve(__dirname,"../src/index.js"),
output: {
filename: 'js/bundle.[hash:8].js',
path: resolve(__dirname, '../dist'),
},
}
//webpack.dev.js
const { merge } = require('webpack-merge')
const common = require('./webpack.common.js')
module.exports = merge(common, {
mode: 'development',
})
//webpack.prod.js
const { merge } = require('webpack-merge')
const common = require('./webpack.common.js')
module.exports = merge(common, {
mode: 'production',
})
新規プロジェクト入口ファイル: src/
- index.js
プロジェクトを開始プロジェクトを起動するには、いくつかの設定依存パッケージが必要です.
html-webpack-plugin
テンプレートファイルは、私たちが包装したリソースをhtmlに導入します.webpack-dev-server
は、ローカルhttpサービスを開始し、熱更新、エージェントなどを構成することができる.clean-webpack-plugin
はフォルダを整理して、毎回包装した後に先に自動的に古いファイルを整理します.copy-webpack-plugin
は、リソースファイルをパッケージディレクトリにコピーするnpm install html-webpack-plugin webpack-dev-server clean-webpack-plugin copy-webpack-plugin -D
テンプレートの設定public
フォルダを新設して、中に私達のhtmlテンプレートファイルを入れます.mkdir public
touch index.html
//index.html
<div id="root"/>
</code></pre>
<p> <code>htmlWebpackPlugin</code> , <code>webpack.common.js</code>:</p>
<pre><code>const { resolve } = require('path')
const config = require("./config")
const CopyPlugin = require('copy-webpack-plugin')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const { CleanWebpackPlugin } = require('clean-webpack-plugin')
module.exports = {
entry: "../src/index.tsx",
output: {
filename: 'js/bundle.[hash:8].js',
path: resolve(__dirname, '../dist'),
},
plugins:[
new HtmlWebpackPlugin({
template: resolve(__dirname, '../public/index.html'),
filename: 'index.html',
title: config.PROJECT_NAME,
cache: false,
}),
new CopyPlugin({
patterns: [
{ from: resolve(__dirname, "../public"), to: resolve(__dirname, "../dist") }
],
}),
new CleanWebpackPlugin()
]
}</code></pre>
<h3>devServer </h3>
<h4> </h4>
<p> <code>proxy.js</code>, :</p>
<pre><code>const proxySetting = {
'/api/': {
target: 'http://localhost:3001',
changeOrigin: true,
},
// 2
'/api-2/': {
target: 'http://localhost:3002',
changeOrigin: true,
pathRewrite: {
'^/api-2': '',
},
},
}
module.exports = proxySetting</code></pre>
<h3>devServer</h3>
<p> <code>webpack.dev.js</code>:</p>
<pre><code>const { merge } = require('webpack-merge');
const webpack = require('webpack');
const {resolve} = require("path");
const common = require('./webpack.common.js');
const proxySetting = require('./proxy');
const config = require('./config');
module.exports = merge(common, {
mode: 'development',
devServer: {
host: config.SERVER_HOST,
port: config.SERVER_PORT,
stats: 'errors-only',
clientLogLevel: 'silent',
compress: true,
open: false,
hot: true, //
proxy: { ...proxySetting }, //
contentBase: resolve(__dirname, '../public')
},
plugins: [new webpack.HotModuleReplacementPlugin()],
});
</code></pre>
<h3>devtool</h3>
<p><code>devtool</code> , , <code>eval-source-map</code> , , :</p>
<pre><code>//webpack.dev.js
module.exports = merge(common, {
mode: 'development',
+ devtool: 'eval-source-map',
})</code></pre>
<pre><code>//webpack.prod.js
module.exports = merge(common, {
mode: 'production',
+ devtool: 'none',
})</code></pre>
<h2> </h2>
<p><code>style-loader</code> <code>css-loader</code> , <code>less</code> , <code>less</code> <code>less-loader</code>。 <code>sass</code> <code>node-sass</code> <code>sass-loader</code>, <code>less</code>, :</p>
<pre><code>npm install css-loader style-loader less less-loader -D</code></pre>
<p> <code>rule</code>, <code>css</code> <code>less</code> :</p>
<pre><code>// webpack.common.js
module.exports = {
// other...
module: {
rules: [
{test: /\.css$/,use: ['style-loader','css-loader']},
{test: /\.less$/,use: [
'style-loader',
{
loader:'css-loader',
options:{importLoaders:1}
},
'less-loader'
]
},
]
},
}</code></pre>
<p> <em>sourceMap</em></p>
<h3>postcss </h3>
<p><code>postcss</code> <em>babel</em> , <em>preset</em> :</p>
<ul>
<li><a href="https://github.com/luisrudge/postcss-flexbugs-fixes" rel="nofollow noreferrer">postcss-flexbugs-fixes</a>: flex bug。</li>
<li><a href="https://github.com/csstools/postcss-preset-env" rel="nofollow noreferrer">postcss-preset-env</a>: CSS CSS , 。 autoprefixer 。</li>
<li><a href="https://github.com/csstools/postcss-normalize" rel="nofollow noreferrer">postcss-normalize</a>: browserslist normalize.css 。</li>
</ul>
<pre><code>npm install postcss-loader postcss-flexbugs-fixes postcss-preset-env autoprefixer postcss-normalize -D</code></pre>
<p><code>postcss</code> :</p>
<pre><code>{
loader: 'postcss-loader',
options: {
sourceMap: config.isDev, // sourceMap
postcssOptions: {
plugins: [
// flex bug
require('postcss-flexbugs-fixes'),
require('postcss-preset-env')({
autoprefixer: {grid: true,flexbox: 'no-2009'},
stage: 3,
}),
require('postcss-normalize')]}
}
}</code></pre>
<p> <code>css</code> <code>less</code> , ,<br/><em>build</em> <code>utils.js</code> , :</p>
<pre><code>//utils.js
const {isDev} = require('./config')
exports.getCssLoaders = (importLoaders) => [
'style-loader',
{
loader: 'css-loader',
options: {
modules: false,
sourceMap: isDev,
importLoaders,
},
},
{
loader: 'postcss-loader',
options: {
sourceMap: isDev,
postcssOptions: {
plugins: [
// flex bug
require('postcss-flexbugs-fixes'),
require('postcss-preset-env')({
autoprefixer: {
grid: true,
flexbox: 'no-2009',
},
stage: 3,
}),
require('postcss-normalize'),
],
},
},
},
]</code></pre>
<p> <code>webpack.common.js</code> :</p>
<pre><code>const {getCssLoaders} = require("./utils");
...
module:{
rules:[
{ test: /.(css)$/, use: getCssLoaders(1) },
{
test: /\.less$/,
use: [
...getCssLoaders(2),
{
loader: 'less-loader',
options: {sourceMap: config.isDev},
}
]
}
]
}
...</code></pre>
<p> , <code>package.json</code> <code>browserslist</code>:</p>
<pre><code>{
"browserslist": [
">0.2%",
"not dead",
"ie >= 9",
"not op_mini all"
],
}</code></pre>
<h3> </h3>
<p> , <code>url-loader</code> , base64, , <code>file-loader</code> , :</p>
<pre><code>npm install file-loader url-loader -D</code></pre>
<pre><code>// webpack.common.js
module.exports = {
// other...
module: {
rules: [
// other...
{
test: [/\.bmp$/, /\.gif$/, /\.jpe?g$/, /\.png$/],
use: [
{
loader: 'url-loader',
options: {
limit: 10 * 1024,
name: '[name].[contenthash:8].[ext]',
outputPath: 'assets/images',
},
},
],
},
{
test: /\.(ttf|woff|woff2|eot|otf|svg)$/,
use: [
{
loader: 'file-loader',
options: {
name: '[name].[contenthash:8].[ext]',
outputPath: 'assets/fonts',
},
},
],
},
]
},
plugins: [//...],
}</code></pre>
<p><code>typescript</code> , <code>src/typings</code> <code>file.d.ts</code> , :</p>
<pre><code>declare module '*.svg' {
const path: string
export default path
}
declare module '*.bmp' {
const path: string
export default path
}
declare module '*.gif' {
const path: string
export default path
}
declare module '*.jpg' {
const path: string
export default path
}
declare module '*.jpeg' {
const path: string
export default path
}
declare module '*.png' {
const path: string
export default path
}</code></pre>
<h3>react typescript</h3>
<p> <code>react</code>:</p>
<pre><code>npm install react react-dom -S</code></pre>
<p> <code>babel</code> :</p>
<pre><code>npm install babel-loader @babel/core @babel/preset-env @babel/plugin-transform-runtime @babel/preset-react -D</code></pre>
<pre><code>npm install @babel/runtime-corejs3 -S</code></pre>
<blockquote>
: @babel/runtime-corejs3 。
</blockquote>
<ul>
<li><strong>babel-loader</strong> <code>babel</code> </li>
<li><strong>@babel/core</strong> <code>babel</code> </li>
<li><strong>@babel/preset-env</strong> javascript </li>
<li><strong>@babel/preset-react</strong> jsx </li>
<li><strong>@babel/plugin-transform-runtime</strong> / 、 (helper function)</li>
<li><strong>@babel/runtime-corejs3</strong> </li>
</ul>
<p> <code>.babelrc</code>, :</p>
<pre><code>{
"presets": [
[
"@babel/preset-env",
{
// babel CommonJS , tree-shaking
"modules": false
}
],
"@babel/preset-react"
],
"plugins": [
[
"@babel/plugin-transform-runtime",
{
"corejs": {
"version": 3,
"proposals": true
},
"useESModules": true
}
]
]
}</code></pre>
<p> <code>webpack.common.js</code> , :</p>
<pre><code>module.exports = {
// other...
module: {
rules: [
{
test: /\.(tsx?|js)$/,
loader: 'babel-loader',
options: { cacheDirectory: true },
exclude: /node_modules/,
},
// other...
]
},
plugins: [ //... ],
}</code></pre>
<blockquote>
babel-loader , , , , cacheDirectory , 。
</blockquote>
<h4>resolve.extensions resolve.alias</h4>
<ul>
<li><code>extensions</code> </li>
<li><code>alias</code> </li>
</ul>
<p> <code>webpack.common.js</code> <code>resolve</code>:</p>
<pre><code>resolve: {
alias:{"@":resolve(__dirname, '../src')},
extensions: ['.tsx', '.ts', '.js', '.json'],
},</code></pre>
<h4> typescript</h4>
<p> <code>src/index.js</code> <code>src/index.tsx</code>:</p>
<pre><code>entry: {
app: resolve(__dirname, '../src/index.tsx'),
},</code></pre>
<p> Typescript <code>tsconfig.json</code> , :</p>
<ul>
<li> </li>
<li> </li>
</ul>
<p> <code>tsconfig.json</code> :</p>
<pre><code>npx tsc --init</code></pre>
<p> <code>tsconfig.json</code> , <a href="https://jkchao.github.io/typescript-book-chinese/project/compilationContext.html#tsconfig-json" rel="nofollow noreferrer"> TypeScript-tsconfig-json</a> , <code>tsconfig.json</code>, :</p>
<pre><code>{
"compilerOptions": {
//
"target": "ES5", // es
"module": "ESNext", //
"lib": ["dom", "dom.iterable", "esnext"], //
"allowJs": true, // js
"jsx": "react", // .tsx JSX
"isolatedModules": true,
"strict": true, //
"noImplicitAny": false, // any
//
"moduleResolution": "node", //
"esModuleInterop": true, // CommonJS ES
"resolveJsonModule": true, // json
"baseUrl": "./", //
"paths": { // , baseUrl
"@/*": ["src/*"],
},
//
"experimentalDecorators": true, // ES
"emitDecoratorMetadata": true, //
//
"forceConsistentCasingInFileNames": true, //
"skipLibCheck": true, // ( *.d.ts)
"allowSyntheticDefaultImports": true, //
"noEmit": true // tsc ( ( Babel ) )
},
"exclude": ["node_modules"]
}</code></pre>
<p> <code>eslint</code> , <code>baseUrl</code> <code>paths</code> , :</p>
<pre><code>npm install eslint-import-resolver-typescript -D</code></pre>
<p> eslintrc.js setting :</p>
<pre><code>settings: {
'import/resolver': {
node: {
extensions: ['.tsx', '.ts', '.js', '.json'],
},
typescript: {},
},
},</code></pre>
<p> <code>typescript</code> <code>@babel/preset-typescript</code> <code>fork-ts-checker-webpack-plugin</code></p>
<ul>
<li><code>@babel/preset-typescript </code> ts , ts , babel </li>
<li><code>fork-ts-checker-webpack-plugin</code> <code>preset-typescript</code> , </li>
</ul>
<p> :</p>
<pre><code>npm install @babel/preset-typescript fork-ts-checker-webpack-plugin -D</code></pre>
<p> <code>webpack.common.js</code> :</p>
<pre><code>const ForkTsCheckerWebpackPlugin = require('fork-ts-checker-webpack-plugin')
module.exports = {
plugins: [
// plugin...
new ForkTsCheckerWebpackPlugin({
typescript: {
configFile: resolve(__dirname, '../tsconfig.json'),
},
}),
]
}</code></pre>
<p> <code>.babelrc</code> <code>preset-typescript</code> :</p>
<pre><code>"presets": [
[
//...
"@babel/preset-typescript"
]</code></pre>
<p> <code>React</code> :</p>
<pre><code>npm install @types/react @types/react-dom -D</code></pre>
<h4> </h4>
<p><code>src</code> <code>index.tsx</code> <code>App.tsx</code> , :</p>
<ul>
<li>index.tsx</li>
</ul>
<pre><code>import React from "react";
import ReactDOM from "react-dom";
import App from "./App";
ReactDOM.render(<app age="{12}" name="test"/>, document.querySelector("#root"));</code></pre>
<ul>
<li>App.tsx</li>
</ul>
<pre><code>import React from "react";
interface IProps {
name: string;
age: number;
}
function App(props: IProps) {
const { name, age } = props;
return (
<div classname="app">
<span>{`Hello! I'm ${name}, ${age} years old.`}</span>
</div>
);
}
export default App;</code></pre>
<h2> </h2>
<h3> </h3>
<blockquote>
<code>webpackbar</code>
</blockquote>
<pre><code>npm install webpackbar -D</code></pre>
<p> webpack.common.js :</p>
<pre><code>const WebpackBar = require("webpackbar");
class Reporter {
done(context) {
if (config.isDev) {
console.clear();
console.log(` :${config.SERVER_HOST}:${config.SERVER_PORT}`);
}
}
}
module.exports = {
plugins: [
// plugin...
new WebpackBar({
name: config.isDev ? " " : " ",
color: "#fa8c16",
reporter: new Reporter()
})
]
}</code></pre>
<h3> </h3>
<blockquote>
<code>hard-source-webpack-plugin</code> ( lodash) , node_modules/.cache/hard-source , , ,
</blockquote>
<pre><code>npm install hard-source-webpack-plugin -D</code></pre>
<p> <code>webpack.common.js</code> :</p>
<pre><code>const HardSourceWebpackPlugin = require('hard-source-webpack-plugin')
module.exports = {
plugins: [
// plugin...
new HardSourceWebpackPlugin(),
]
}</code></pre>
<h3>external </h3>
<blockquote>
<code>react</code>、
<code>react-dom</code> , CDN
</blockquote>
<p> webpack.common.js , :</p>
<pre><code>module.exports = {
externals: {
react: 'React',
'react-dom': 'ReactDOM',
},
}</code></pre>
<p> </p>
<ul>
<li>CDN :</li>
</ul>
<pre><code>
<div id="root"/>
+ <script crossorigin="" src="https://unpkg.com/react@16.13.1/umd/react.production.min.js"/>
+ <script crossorigin="" src="https://unpkg.com/react-dom@16.13.1/umd/react-dom.production.min.js"/>
</code></pre>
<ul>
<li> :</li>
</ul>
<p> <code>public</code> <code>lib</code> , :</p>
<pre><code>public/
index.html
lib/
react.production.min.js
react-dom.production.min.js</code></pre>
<h3>DllPlugin</h3>
<p> dll , , <a href="https://github.com/asfktz/autodll-webpack-plugin" rel="nofollow noreferrer">autodll-webpack-plugin</a>;</p>
<h3>splitChunks</h3>
<p>React <code>React.lazy</code> <code>React.Suspense</code> , :</p>
<pre><code>import React, { Suspense, useState } from 'react'
const ComputedOne = React.lazy(() => import('Components/ComputedOne'))
const ComputedTwo = React.lazy(() => import('Components/ComputedTwo'))
function App() {
const [showTwo, setShowTwo] = useState<boolean>(false)
return (
<div classname="app">
<suspense fallback="{<div">Loading...</suspense></div>}>
<computedone a="{1}" b="{2}"/>
{showTwo && <computedtwo a="{3}" b="{4}"/>}
<button type="button" onclick="{()"> setShowTwo(true)}>
Two
</button>
</boolean></code></pre></article></div>
)
}
export default App
<p> <code>chunk</code> , chunk, webpack.common.js :</p>
<pre><code>module.exports = {
// other...
externals: {//...},
optimization: {
splitChunks: {
chunks: 'all',
name: true,
},
},
}</code></pre>
<h3> </h3>
<p> <strong>devServer</strong> , js , <code>index.jsx</code> :</p>
<pre><code>if (module && module.hot) {
module.hot.accept()
}</code></pre>
<p> ts , , <code>@types/webpack-env</code>:</p>
<pre><code>npm install @types/webpack-env -D</code></pre>
<h2> </h2>
<h3> </h3>
<h4> </h4>
<p> <code>mini-css-extract-plugin</code>:</p>
<pre><code>npm install mini-css-extract-plugin -D</code></pre>
<p><code>build/utils.js</code> :</p>
<pre><code>const MiniCssExtractPlugin = require('mini-css-extract-plugin')
const getCssLoaders = (importLoaders) => [
isDev ? 'style-loader' : MiniCssExtractPlugin.loader,
// ....
]</code></pre>
<p><code>webpack.prop.js</code> :</p>
<pre><code>const MiniCssExtractPlugin = require('mini-css-extract-plugin')
module.exports = {
plugins: [
// plugin...
new MiniCssExtractPlugin({
filename: 'css/[name].[contenthash:8].css',
chunkFilename: 'css/[name].[contenthash:8].css',
ignoreOrder: false,
}),
]
}</code></pre>
<h4> </h4>
<pre><code>npm install purgecss-webpack-plugin glob -D</code></pre>
<p><code>webpack.prop.js</code> :</p>
<pre><code>const glob = require("glob");
const PurgeCSSPlugin = require('purgecss-webpack-plugin')
const { resolve } = require("path");
module.exports = merge(common, {
plugins: [
// ...
new PurgeCSSPlugin({
paths: glob.sync(`${resolve(__dirname, "../src")}/**/*.{tsx,scss,less,css}`, {
nodir: true
}),
whitelist: ["html", "body"]
})
],
})</code></pre>
<h3> </h3>
<pre><code>npm install optimize-css-assets-webpack-plugin terser-webpack-plugin@4 -D</code></pre>
<p><code>webpack.prop.js</code> :</p>
<pre><code>const TerserPlugin = require("terser-webpack-plugin");
const OptimizeCssAssetsPlugin = require('optimize-css-assets-webpack-plugin')
module.exports = merge(common, {
//...
optimization: {
minimize: true,
minimizer: [
new TerserPlugin({
extractComments: false,
terserOptions: {
compress: { pure_funcs: ["console.log"] }
}
}),
new OptimizeCssAssetsPlugin()
]
},
plugins:[...]
})</code></pre>
<h3> </h3>
<p><code>webpack.prop.js</code> :</p>
<pre><code>const webpack = require('webpack')
module.exports = merge(common, {
plugins: [
// ...
new webpack.BannerPlugin({
raw: true,
banner: '/** @preserve Powered by chenwl */',
}),
]
})</code></pre>
<h3> </h3>
<pre><code>npm install webpack-bundle-analyzer -D</code></pre>
<p><code>webpack.prop.js</code> :</p>
<pre><code>const { BundleAnalyzerPlugin } = require('webpack-bundle-analyzer')
module.exports = merge(common, {
plugins: [
// ...
new BundleAnalyzerPlugin({
analyzerMode: 'server', //
analyzerHost: '127.0.0.1', // host
analyzerPort: 8081, //
}),
],
})</code></pre>
</div>