Ders İçeriği
Build Process ve Production Hazırlığı
React uygulamanızı production ortamına dağıtmadan önce, uygulamanızı optimize etmeniz ve production için hazırlamanız gerekir. Bu süreç, uygulamanızın performansını artırmak, dosya boyutlarını küçültmek ve güvenlik açıklarını kapatmak için kritiktir.
Create React App ile oluşturulan projeler, varsayılan olarak güçlü bir build sistemi ile gelir. Bu sistem, Webpack, Babel ve diğer modern araçları kullanarak uygulamanızı production için optimize eder. Build süreci sırasında gerçekleşen optimizasyonlar şunlardır: kod minification (küçültme), dead code elimination (kullanılmayan kodların kaldırılması), bundle splitting (kod parçalama), asset optimization (görsel ve diğer dosyaların optimizasyonu) ve source map oluşturma.
Production Build Oluşturma
Production build oluşturmak için aşağıdaki komutu kullanın:
bash npm run build
Bu komut çalıştırıldığında, Create React App şu işlemleri gerçekleştirir:
# Build süreci detayları
Creating an optimized production build...
Compiled successfully.
File sizes after gzip:
46.61 KB build/static/js/2.8e8b8999.chunk.js
1.78 KB build/static/js/main.5ecd60bf.chunk.js
1.17 KB build/static/js/runtime-main.665d9e0e.js
1.69 KB build/static/css/main.5f361e03.chunk.css
The project was built assuming it is hosted at /.
You can control this with the homepage field in your package.json.
The build folder is ready to be deployed.
You may serve it with a static server:
npm install -g serve
serve -s build
Find out more about deployment here:
https://cra.link/deployment
Build süreci tamamlandığında, build
klasörü oluşturulur ve bu klasör production için optimize edilmiş dosyaları içerir.
Build Optimizasyonları
Production build sırasında gerçekleşen optimizasyonları anlamak önemlidir:
// package.json - Build scripts ve konfigürasyon
{
"name": "my-react-app",
"version": "0.1.0",
"private": true,
"homepage": "https://myusername.github.io/my-react-app",
"dependencies": {
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-scripts": "5.0.1"
},
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test",
"eject": "react-scripts eject",
"analyze": "npm run build && npx webpack-bundle-analyzer build/static/js/*.js"
},
"eslintConfig": {
"extends": [
"react-app",
"react-app/jest"
]
},
"browserslist": {
"production": [
">0.2%",
"not dead",
"not op_mini all"
],
"development": [
"last 1 chrome version",
"last 1 firefox version",
"last 1 safari version"
]
}
}
Environment Variables
Production ortamında farklı konfigürasyonlar kullanmak için environment variables kullanabilirsiniz:
# .env.production
REACT_APP_API_URL=https://api.myapp.com
REACT_APP_ANALYTICS_ID=GA-XXXXXXXXX
REACT_APP_VERSION=$npm_package_version
GENERATE_SOURCEMAP=false
// src/config/environment.js
const config = {
development: {
apiUrl: 'http://localhost:3001/api',
analyticsId: null,
debug: true
},
production: {
apiUrl: process.env.REACT_APP_API_URL || 'https://api.myapp.com',
analyticsId: process.env.REACT_APP_ANALYTICS_ID,
debug: false
}
};
const environment = process.env.NODE_ENV || 'development';
export default config[environment];
// Kullanım
import config from './config/environment';
// API çağrıları
const apiClient = axios.create({
baseURL: config.apiUrl,
timeout: 10000
});
// Analytics
if (config.analyticsId) {
// Google Analytics initialization
gtag('config', config.analyticsId);
}
Performance Optimizasyonları
Production build öncesi uygulamanızı optimize etmek için çeşitli teknikler kullanabilirsiniz:
// Code splitting with React.lazy
import React, { Suspense, lazy } from 'react';
import { BrowserRouter as Router, Routes, Route } from 'react-router-dom';
import LoadingSpinner from './components/LoadingSpinner';
// Lazy load components
const Home = lazy(() => import('./pages/Home'));
const About = lazy(() => import('./pages/About'));
const Dashboard = lazy(() => import('./pages/Dashboard'));
const Profile = lazy(() => import('./pages/Profile'));
function App() {
return (
<Router>
<Suspense fallback={<LoadingSpinner />}>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/about" element={<About />} />
<Route path="/dashboard" element={<Dashboard />} />
<Route path="/profile" element={<Profile />} />
</Routes>
</Suspense>
</Router>
);
}
// Image optimization
import { useState, useCallback } from 'react';
function OptimizedImage({ src, alt, className, ...props }) {
const [loaded, setLoaded] = useState(false);
const [error, setError] = useState(false);
const handleLoad = useCallback(() => {
setLoaded(true);
}, []);
const handleError = useCallback(() => {
setError(true);
}, []);
if (error) {
return (
<div className={`image-placeholder ${className}`}>
<span>Image failed to load</span>
</div>
);
}
return (
<div className={`image-container ${className}`}>
{!loaded && <div className="image-skeleton" />}
<img
src={src}
alt={alt}
onLoad={handleLoad}
onError={handleError}
style={{ display: loaded ? 'block' : 'none' }}
loading="lazy"
{...props}
/>
</div>
);
}
// Memoization for expensive calculations
import { useMemo, memo } from 'react';
const ExpensiveComponent = memo(({ data, filter }) => {
const processedData = useMemo(() => {
return data
.filter(item => item.category === filter)
.sort((a, b) => b.score - a.score)
.slice(0, 100);
}, [data, filter]);
return (
<div>
{processedData.map(item => (
<div key={item.id}>{item.name}</div>
))}
</div>
);
});
// Service Worker for caching
// public/sw.js
const CACHE_NAME = 'my-app-v1';
const urlsToCache = [
'/',
'/static/js/bundle.js',
'/static/css/main.css',
'/manifest.json'
];
self.addEventListener('install', (event) => {
event.waitUntil(
caches.open(CACHE_NAME)
.then((cache) => {
return cache.addAll(urlsToCache);
})
);
});
self.addEventListener('fetch', (event) => {
event.respondWith(
caches.match(event.request)
.then((response) => {
if (response) {
return response;
}
return fetch(event.request);
})
);
});
// Service Worker registration
// src/index.js
import React from 'react';
import ReactDOM from 'react-dom/client';
import App from './App';
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(<App />);
// Register service worker
if ('serviceWorker' in navigator) {
window.addEventListener('load', () => {
navigator.serviceWorker.register('/sw.js')
.then((registration) => {
console.log('SW registered: ', registration);
})
.catch((registrationError) => {
console.log('SW registration failed: ', registrationError);
});
});
}
Hosting Seçenekleri
React uygulamanızı dağıtmak için birçok hosting seçeneği bulunur. Her seçeneğin kendine özgü avantajları ve kullanım senaryoları vardır.
Netlify
Netlify, static site hosting için popüler bir platformdur. Git repository'nizi bağlayarak otomatik deployment sağlar.
# Netlify CLI kurulumu
npm install -g netlify-cli
# Login
netlify login
# Build ve deploy
netlify build
netlify deploy --prod
# Netlify konfigürasyonu
# netlify.toml
[build]
publish = "build"
command = "npm run build"
[build.environment]
REACT_APP_API_URL = "https://api.myapp.com"
[[redirects]]
from = "/*"
to = "/index.html"
status = 200
[[headers]]
for = "/static/*"
[headers.values]
Cache-Control = "public, max-age=31536000, immutable"
[[headers]]
for = "/*.js"
[headers.values]
Cache-Control = "public, max-age=31536000, immutable"
[[headers]]
for = "/*.css"
[headers.values]
Cache-Control = "public, max-age=31536000, immutable"
Vercel
Vercel, özellikle React ve Next.js uygulamaları için optimize edilmiş bir platformdur.
# Vercel CLI kurulumu
npm install -g vercel
# Deploy
vercel
# Production deploy
vercel --prod
# Vercel konfigürasyonu
# vercel.json
{
"version": 2,
"builds": [
{
"src": "package.json",
"use": "@vercel/static-build",
"config": {
"distDir": "build"
}
}
],
"routes": [
{
"src": "/static/(.*)",
"headers": {
"cache-control": "public, max-age=31536000, immutable"
}
},
{
"src": "/(.*)",
"dest": "/index.html"
}
],
"env": {
"REACT_APP_API_URL": "https://api.myapp.com"
}
}
GitHub Pages
GitHub Pages, GitHub repository'nizden doğrudan static site hosting sağlar.
# gh-pages paketi kurulumu
npm install --save-dev gh-pages
# package.json'a script ekleme
{
"scripts": {
"predeploy": "npm run build",
"deploy": "gh-pages -d build"
},
"homepage": "https://yourusername.github.io/your-repo-name"
}
# Deploy
npm run deploy
# GitHub Actions ile otomatik deployment
# .github/workflows/deploy.yml
name: Deploy to GitHub Pages
on:
push:
branches: [ main ]
jobs:
build-and-deploy:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v3
- name: Setup Node.js
uses: actions/setup-node@v3
with:
node-version: '18'
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: Run tests
run: npm test -- --coverage --watchAll=false
- name: Build
run: npm run build
env:
REACT_APP_API_URL: ${{ secrets.REACT_APP_API_URL }}
- name: Deploy
uses: peaceiris/actions-gh-pages@v3
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
publish_dir: ./build# AWS CLI kurulumu ve konfigürasyonu
aws configure
# S3 bucket oluşturma
aws s3 mb s3://my-react-app-bucket
# Build dosyalarını S3'e yükleme
aws s3 sync build/ s3://my-react-app-bucket --delete
# Bucket policy
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "PublicReadGetObject",
"Effect": "Allow",
"Principal": "*",
"Action": "s3:GetObject",
"Resource": "arn:aws:s3:::my-react-app-bucket/*"
}
]
}
# CloudFormation template
# infrastructure.yml
AWSTemplateFormatVersion: '2010-09-09'
Description: 'React App Infrastructure'
Resources:
S3Bucket:
Type: AWS::S3::Bucket
Properties:
BucketName: my-react-app-bucket
WebsiteConfiguration:
IndexDocument: index.html
ErrorDocument: index.html
PublicAccessBlockConfiguration:
BlockPublicAcls: false
BlockPublicPolicy: false
IgnorePublicAcls: false
RestrictPublicBuckets: false
CloudFrontDistribution:
Type: AWS::CloudFront::Distribution
Properties:
DistributionConfig:
Origins:
- DomainName: !GetAtt S3Bucket.RegionalDomainName
Id: S3Origin
S3OriginConfig:
OriginAccessIdentity: ''
Enabled: true
DefaultRootObject: index.html
CustomErrorResponses:
- ErrorCode: 404
ResponseCode: 200
ResponsePagePath: /index.html
DefaultCacheBehavior:
TargetOriginId: S3Origin
ViewerProtocolPolicy: redirect-to-https
CachePolicyId: 4135ea2d-6df8-44a3-9df3-4b5a84be39ad
PriceClass: PriceClass_100
Outputs:
CloudFrontURL:
Description: 'CloudFront Distribution URL'
Value: !GetAtt CloudFrontDistribution.DomainName
Docker ile Containerization
Docker kullanarak uygulamanızı containerize edebilir ve herhangi bir ortamda çalıştırabilirsiniz.
# Dockerfile
# Multi-stage build
FROM node:18-alpine as build
WORKDIR /app
# Copy package files
COPY package*.json ./
# Install dependencies
RUN npm ci --only=production
# Copy source code
COPY . .
# Build the app
RUN npm run build
# Production stage
FROM nginx:alpine
# Copy build files
COPY --from=build /app/build /usr/share/nginx/html
# Copy nginx configuration
COPY nginx.conf /etc/nginx/nginx.conf
# Expose port
EXPOSE 80
# Start nginx
CMD ["nginx", "-g", "daemon off;"]
# nginx.conf
events {
worker_connections 1024;
}
http {
include /etc/nginx/mime.types;
default_type application/octet-stream;
server {
listen 80;
server_name localhost;
root /usr/share/nginx/html;
index index.html;
# Gzip compression
gzip on;
gzip_vary on;
gzip_min_length 1024;
gzip_types
text/plain
text/css
text/xml
text/javascript
application/javascript
application/xml+rss
application/json;
# Cache static assets
location /static/ {
expires 1y;
add_header Cache-Control "public, immutable";
}
# Handle client-side routing
location / {
try_files $uri $uri/ /index.html;
}
# Security headers
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-XSS-Protection "1; mode=block" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
}
}
# Docker build ve run
docker build -t my-react-app .
docker run -p 80:80 my-react-app
# Docker Compose
# docker-compose.yml
version: '3.8'
services:
web:
build: .
ports:
- "80:80"
environment:
- NODE_ENV=production
restart: unless-stopped
# Optional: Add a reverse proxy
nginx:
image: nginx:alpine
ports:
- "443:443"
volumes:
- ./ssl:/etc/nginx/ssl
- ./nginx-proxy.conf:/etc/nginx/nginx.conf
depends_on:
- web
restart: unless-stopped
CI/CD Pipeline
Continuous Integration ve Continuous Deployment (CI/CD) pipeline'ları, kodunuzun otomatik olarak test edilmesi, build edilmesi ve deploy edilmesi için kritiktir.
GitHub Actions
GitHub Actions ile kapsamlı bir CI/CD pipeline oluşturabilirsiniz:
# .github/workflows/ci-cd.yml
name: CI/CD Pipeline
on:
push:
branches: [ main, develop ]
pull_request:
branches: [ main ]
env:
NODE_VERSION: '18'
REGISTRY: ghcr.io
IMAGE_NAME: ${{ github.repository }}
jobs:
test:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v3
- name: Setup Node.js
uses: actions/setup-node@v3
with:
node-version: ${{ env.NODE_VERSION }}
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: Run linting
run: npm run lint
- name: Run tests
run: npm test -- --coverage --watchAll=false
- name: Upload coverage to Codecov
uses: codecov/codecov-action@v3
with:
file: ./coverage/lcov.info
build:
needs: test
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v3
- name: Setup Node.js
uses: actions/setup-node@v3
with:
node-version: ${{ env.NODE_VERSION }}
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: Build application
run: npm run build
env:
REACT_APP_API_URL: ${{ secrets.REACT_APP_API_URL }}
REACT_APP_ANALYTICS_ID: ${{ secrets.REACT_APP_ANALYTICS_ID }}
- name: Upload build artifacts
uses: actions/upload-artifact@v3
with:
name: build-files
path: build/
deploy-staging:
needs: build
runs-on: ubuntu-latest
if: github.ref == 'refs/heads/develop'
environment: staging
steps:
- name: Download build artifacts
uses: actions/download-artifact@v3
with:
name: build-files
path: build/
- name: Deploy to staging
run: |
# Deploy to staging environment
echo "Deploying to staging..."
# Your staging deployment commands here
deploy-production:
needs: build
runs-on: ubuntu-latest
if: github.ref == 'refs/heads/main'
environment: production
steps:
- name: Download build artifacts
uses: actions/download-artifact@v3
with:
name: build-files
path: build/
- name: Deploy to production
run: |
# Deploy to production environment
echo "Deploying to production..."
# Your production deployment commands here
docker:
needs: test
runs-on: ubuntu-latest
if: github.ref == 'refs/heads/main'
steps:
- name: Checkout code
uses: actions/checkout@v3
- name: Log in to Container Registry
uses: docker/login-action@v2
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Extract metadata
id: meta
uses: docker/metadata-action@v4
with:
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
tags: |
type=ref,event=branch
type=ref,event=pr
type=sha
- name: Build and push Docker image
uses: docker/build-push-action@v4
with:
context: .
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
GitLab CI/CD
GitLab CI/CD ile pipeline oluşturma:
# .gitlab-ci.yml
stages:
- test
- build
- deploy
variables:
NODE_VERSION: "18"
DOCKER_DRIVER: overlay2
cache:
paths:
- node_modules/
test:
stage: test
image: node:$NODE_VERSION
script:
- npm ci
- npm run lint
- npm test -- --coverage --watchAll=false
artifacts:
reports:
coverage_report:
coverage_format: cobertura
path: coverage/cobertura-coverage.xml
coverage: '/Lines\s*:\s*(\d+\.\d+)%/'
build:
stage: build
image: node:$NODE_VERSION
script:
- npm ci
- npm run build
artifacts:
paths:
- build/
expire_in: 1 hour
only:
- main
- develop
deploy_staging:
stage: deploy
image: alpine:latest
script:
- echo "Deploying to staging..."
# Your staging deployment commands
environment:
name: staging
url: https://staging.myapp.com
only:
- develop
deploy_production:
stage: deploy
image: alpine:latest
script:
- echo "Deploying to production..."
# Your production deployment commands
environment:
name: production
url: https://myapp.com
when: manual
only:
- main
docker_build:
stage: build
image: docker:latest
services:
- docker:dind
script:
- docker build -t $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA .
- docker push $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA
only:
- main
React uygulamanızı production ortamına dağıtmak, dikkatli planlama ve doğru araçların seçilmesini gerektirir. Build optimizasyonları, uygun hosting platformu seçimi ve otomatik deployment pipeline'ları, uygulamanızın başarılı bir şekilde kullanıcılara ulaşmasını sağlar. Her hosting seçeneğinin kendine özgü avantajları vardır ve projenizin gereksinimlerine göre en uygun olanını seçmelisiniz.