redux 및 react-redux 간단 한 실현
redux 프로필
자 바스 크 립 트 단일 페이지 응용 개발 이 갈수 록 복잡 해 지면 서 자 바스 크 립 트 는 그 어느 때 보다 많은 state(상태)를 관리 해 야 합 니 다.이 state 는 서버 응답,캐 시 데이터,로 컬 생 성 이 서버 에 오래 지속 되 지 않 은 데 이 터 를 포함 할 수 있 으 며,UI 상태 도 포함 할 수 있 습 니 다.예 를 들 어 활성 화 된 경로,선 택 된 탭,로 딩 효과 나 페 이 퍼 를 표시 할 지 여부 등 입 니 다.
끊임없이 변화 하 는 state 를 관리 하 는 것 은 매우 어렵다.만약 에 하나의 model 의 변화 가 다른 model 변 화 를 일 으 킬 수 있다 면 view 가 변화 할 때 대응 하 는 model 과 다른 model 의 변 화 를 일 으 킬 수 있 고 순서대로 다른 view 의 변 화 를 일 으 킬 수 있다.무슨 일이 일 어 났 는 지 알 수 없 을 때 까지state 가 언제,어떤 이유 로,어떻게 변화 하 는 지 는 이미 통제 되 지 않 습 니 다.시스템 이 복잡 해 지면 문 제 를 재현 하거나 새로운 기능 을 추가 하기 가 어려워 진다.
만약 에 이것 이 나 쁘 지 않다 면 전단 개발 분야 에서 온 새로운 수 요 를 고려 해 야 한다.예 를 들 어 업데이트,서버 렌 더 링,경로 전환 전 요청 데이터 등 이다.전단 개발 자 들 이 전례 없 는 복잡성 을 겪 고 있 는데 이대로 포기 하 는 것 일 까?아니 지.
이곳 의 복잡성 은 어느 정도 에 우 리 는 항상 정리 하기 어 려 운 두 개념 을 한데 혼동한다.변화 와 비동기 이다.둘 을 나 누 면 잘 할 수 있 지만 섞 이면 엉망 이 된다.일부 라 이브 러 리 는 React 와 같은 보기 층 에서 비동기 와 DOM 을 직접 조작 하여 이 문 제 를 해결 하려 고 합 니 다.옥 에 티 는 React 가 여전히 state 의 데 이 터 를 처리 하 는 문 제 를 우리 자신 에 게 남 겨 두 었 다 는 점 이다.redux 는 이 상 태 를 관리 해 줄 수 있 습 니 다.
데모 데모 데모
demo 구조 트 리
├── config-overrides.js
├── .gitignore
├── package.json
├── package-lock.json
├── public
│ ├── favicon.ico
│ ├── index.html
│ └── manifest.json
├── README.md
└── src
├── App.js
├── Demo
│ ├── actionCreate.js
│ ├── Demo.jsx
│ ├── react-redux.js
│ ├── reducer.js
│ ├── redux.js
│ ├── style.css
│ └── thunk.js
└── index.js
1.redux API createStore 의 실현먼저 우 리 는 reducer 와 action 의 지식 을 결합 하여 처음에 보 여 준 demo 를 간단하게 실현 하고 createStore 의 신비 한 베일 을 점차적으로 밝 혔 다.
1.1 준비 작업:
reducer 를 만 들 고 reducer 내 보 내기
// reducer.js
const initState = { user: 'qianyin', age: 18, sex: ' ' };
export const reducer = (state=initState, action) => {
switch(action.type){
case 'USER_UPDATE':
return {...state, ...action.payload};
case 'AGE_GROW':
return {...state, age: state.age + 1};
case 'SEX_UPDATE':
return {...state, ...action.payload};
default:
return state;
}
}
액 션 생 성 함수 생 성
// actionCreate.js
export const changeUser = (user) => {
return {
payload:{user},
type: 'USER_UPDATE',
};
}
export const changeAge = () => {
return { type: 'AGE_GROW' };
}
react 를 통 해 페이지 에 기본 요 소 를 미리 그립 니 다.
/* style.css */
.btn{
height: 31px;
}
.input{
height: 25px;
}
// Demo.jsx
import React from 'react';
import './style.css';
export default class Demo extends React.Component{
onChange = () => {}
onClick = () => {}
render(){
return (
<div>
<p>user: xxx, age: xxx</p>
user:
<input type="text" className="input" onChange={this.onChange}/>
<button className="btn" onClick={this.onClick}> </button>
</div>
);
}
}
최종 페이지 는 다음 과 같이 보 여 줍 니 다:1.2 데모 의 첫 구현 코드
// Demo.jsx
import React from 'react';
import { changeAge, changeUser } from './actionCreate';
import { reducer } from './reducer';
import './style.css';
let state;
const listeners = [];
const subscribe = (listener) => {
listeners.push(listener);
}
const dispatch = (action) => {
state = reducer(state, action);
console.log(state);
listeners.forEach(v => v());
}
dispatch({type: '%$&HJKAJJHDJHJ'});
export default class Demo extends React.Component{
state = {user: 'xxx', age: 'xxx'};
componentDidMount(){
subscribe(this.update);
this.update();
}
update = () => {
this.setState(state);
}
onChange = (e) => {
dispatch(changeUser(e.target.value));
}
onClick = () => {
dispatch(changeAge());
}
render(){
return (
<div>
<p>user: {this.state.user}, age: {this.state.age}</p>
user:
<input type="text" className="input" onChange={this.onChange}/>
<button className="btn" onClick={this.onClick}> </button>
</div>
);
}
}
1.3 API createStore 의 실현사실 위의 코드 에서 createStore 의 실현 원리 에 대해 기본적으로 설명 하고 제 거 했 습 니 다.아래 에 우 리 는 단순히 코드 에 대해 간단 한 패 키 징 을 했 을 뿐 입 니 다.물론 state 를 얻 기 위해 함수 getState 를 추가 하여 이 를 실현 합 니 다.
createStore 함수 구현
// redux.js
export const createStore = (reducer) => {
//
let state;
const listeners = [];
//
const getState = () => {
return state;
}
//
const subscribe = (listener) => {
listeners.push(listener);
}
// [1] reducer [2]
const dispatch = (action) => {
state = reducer(state, action);
listeners.forEach(v => v());
}
// state
dispatch({type: '%$&HJKAJJHDJHJ'});
//
return {getState, subscribe, dispatch};
}
createStore 를 호출 하고 demo 를 수정 합 니 다.
// Demo.jsx
import React from 'react';
import './style.css';
import { changeAge, changeUser } from './actionCreate';
import { reducer } from './reducer';
import { createStore } from './redux';
const store = createStore(reducer);
export default class Demo extends React.Component{
state = {user: 'xxx', age: 'xxx'};
componentDidMount(){
store.subscribe(this.update);
this.update();
}
update = () => {
this.setState(store.getState());
}
onChange = (e) => {
store.dispatch(changeUser(e.target.value));
}
onClick = () => {
store.dispatch(changeAge());
}
render(){
return (
<div>
<p>user: {this.state.user}, age: {this.state.age}</p>
user:
<input type="text" className="input" onChange={this.onChange}/>
<button className="btn" onClick={this.onClick}> </button>
</div>
);
}
}
2.react-redux API Provider 의 실현react 에서 대부분 상황 에서 우 리 는 상 태 를 후대 구성 요소 에 전달 하여 사용 해 야 합 니 다.물론 props 를 통 해 상 태 를 아버지 급 에서 서브 급 으로 전달 할 수 있 지만 상태 가 전달 해 야 할 등급 이 비교적 깊 은 상황 에서 props 를 사용 하면 무력 해 집 니 다.그러면 react-redux 에서 store 에 대한 전달 을 어떻게 실현 합 니까?
2.1 react context 의 도입
App.js 에 store 를 만 들 고 context 를 통 해 store 를 전달 합 니 다.
// App.js
import React, { Component } from 'react';
import propTypes from 'prop-types';
import { createStore } from './Demo/redux';
import { reducer } from './Demo/reducer';
import Demo from './Demo/Demo';
// store
const store = createStore(reducer);
class App extends Component {
// childContextTypes
static childContextTypes = {
store: propTypes.object
};
// childContext
getChildContext(){
return {store}
}
render() {
return <Demo />;
}
}
export default App;
하위 구성 요소 데모 에서 context 를 통 해 store 를 가 져 오고 코드 를 간단하게 수정 합 니 다.
// Demo.jsx
import React from 'react';
import propTypes from 'prop-types';
import './style.css';
import { changeAge, changeUser } from './actionCreate';
export default class Demo extends React.Component{
// context
static contextTypes = {
store: propTypes.object
};
constructor(props, context){
super(props, context);
// store
this.store = context.store;
this.state = {user: 'xxx', age: 'xxx'};
}
componentDidMount(){
this.store.subscribe(this.update);
this.update();
}
update = () => {
this.setState(this.store.getState());
}
onChange = (e) => {
this.store.dispatch(changeUser(e.target.value));
}
onClick = () => {
this.store.dispatch(changeAge());
}
render(){
return (
<div>
<p>user: {this.state.user}, age: {this.state.age}</p>
user:
<input type="text" className="input" onChange={this.onChange}/>
<button className="btn" onClick={this.onClick}> </button>
</div>
);
}
}
2.2 패 키 징 코드 구현 Providerreact context 를 통 해 저 희 는 store 에 대한 전달 을 실 현 했 습 니 다.여기 서 Provider 의 기능 과 실현 원 리 는 대체적으로 뚜렷 한 편 입 니 다.구성 요 소 를 소포 하 는 동시에 react context 를 통 해 공유 store 를 전달 하 는 것 이 아 닙 니 다.그러면 다음 에 우 리 는 코드 에 대한 패 키 징 을 통 해 Provider 구성 요 소 를 실현 합 니 다.
Provider 구성 요소:store 에 대한 전달 실현
// react-redux.js
import React from 'react';
import propTypes from 'prop-types';
export class Provider extends React.Component{
// childContext
static childContextTypes = {
store: propTypes.object
};
// childContext
getChildContext(){
return {store: this.props.store}
}
render(){
return this.props.children;
}
}
App.js 다시 쓰기:Provider 구성 요소 호출
// App.js
import React, { Component } from 'react';
import { createStore } from './Demo/redux';
import { Provider } from './Demo/react-redux';
import { reducer } from './Demo/reducer';
import Demo from './Demo/Demo';
// store
const store = createStore(reducer);
class App extends Component {
render() {
// Provider
return <Provider store={store}><Demo /></Provider>;
}
}
export default App;
3.react-redux API connect 고급 구성 요소 의 실현위의 글 에서 후대 구성 요소 가 store 를 가 져 오 려 면 react context 를 수 동 으로 가 져 와 store 를 호출 하고 외 현적 으로 store 내 부 를 호출 하 는 방법 이 필요 합 니 다.다음 에 우 리 는 이러한 고급 구성 요소 connect 를 실현 합 니 다.우 리 는 필요 한 redux state 와 action 생 성 함수 만 제공 하면 props 를 통 해 해당 하 는 redux state 를 얻 을 수 있 고 props 를 통 해 action 생 성 함 수 를 직접 호출 하여 redux state 를 수정 할 수 있 습 니 다.
고급 구성 요소 connect 만 들 기
// react-redux.js
export const connect = (mapStateToProps, mapDispatchToProps) => (Component) => {
return class NewComponent extends React.Component{
render(){
return <Component />
}
}
}
저장 소 가 져 오기
// react-redux.js
export const connect = (mapStateToProps, mapDispatchToProps) => (Component) => {
return class NewComponent extends React.Component{
// context
static contextType = {
store: propTypes.object
};
// [1] store [2] react state
constructor(props, context){
super(props, context);
this.store = context.store;
this.state = {};
}
render(){
return <Component />
}
}
}
감청 대상 을 추가 하고 props 를 통 해 하위 구성 요소 에 상 태 를 전달 하려 고 시도 합 니 다.
export const connect = (mapStateToProps, mapDispatchToProps) => (Component) => {
return class NewComponent extends React.Component{
static contextType = {
store: propTypes.object
};
constructor(props, context){
super(props, context);
this.store = context.store;
this.state = {};
}
// [1] [2] , react state
componentDidMount(){
this.store.subscribe(this.update);
this.update();
}
update = () => {
// redux state react state
const state = this.store.getState();
this.setState(state);
}
render(){
// props react state
return <Component {...this.state} />
}
}
}
mapStateToProps 를 통 해 지정 한 redux state 가 져 오기
// react-redux.js
export const connect = (mapStateToProps, mapDispatchToProps) => (Component) => {
return class NewComponent extends React.Component{
static contextType = {
store: propTypes.object
};
constructor(props, context){
super(props, context);
this.store = context.store;
this.state = {};
}
componentDidMount(){
this.store.subscribe(this.update);
this.update();
}
update = () => {
// mapStateToProps state
const state = this.store.getState();
const filterState = mapStateToProps(state);
this.setState(filterState);
}
render(){
return <Component {...this.state} />
}
}
}
mapDispatchToProps 를 통 해 action 생 성 함수 가 져 오기:dispatch 소 포 를 사용 한 후 되 돌려 줍 니 다.
// react-redux.js
// react-redux.js
export const connect = (mapStateToProps, mapDispatchToProps) => (Component) => {
return class NewComponent extends React.Component{
static contextTypes = {
store: propTypes.object
};
constructor(props, context){
super(props, context);
this.store = context.store;
this.state = {};
}
componentDidMount(){
this.store.subscribe(this.update);
this.update();
}
update = () => {
// state ===> state
const state = this.store.getState();
const filterState = mapStateToProps(state);
// dispatch mapDispatchToProps action
const actionFun = {};
for(let key in mapDispatchToProps){
actionFun[key] = (...args) => {
this.store.dispatch(mapDispatchToProps[key](...args));
}
}
// :
// const actionFun = Object.keys(mapDispatchToProps)
// .reduce((total, item) => {
// return { ...total, [item]: (...args) => {dispatch(mapDispatchToProps[item](...args));}
// } } ,{});
this.setState({...filterState, ...actionFun});
}
render(){
return <Component {...this.state} />
}
}
}
고급 구성 요소 호출:데모.jsx 수정
// Demo.jsx
import React from 'react';
import { changeAge, changeUser } from './actionCreate';
import { connect } from './react-redux';
import './style.css';
// mapStateToProps redux state redux state
const mapStateToProps = (state) => {
return {user: state.user, age: state.age};
}
//
@connect(mapStateToProps, {changeAge, changeUser})
export default class Demo extends React.Component{
onChange = (e) => {
this.props.changeUser(e.target.value);
}
onClick = () => {
this.props.changeAge();
}
render(){
return (
<div>
<p>user: {this.props.user}, age: {this.props.age}</p>
user:
<input type="text" className="input" onChange={this.onChange}/>
<button className="btn" onClick={this.onClick}> </button>
</div>
);
}
}
4.redux API bidactioncreators 의 실현위 에서 우리 가 mapDispatchToProps 에 대한 처리 과정 은 API bindactioncreators 의 기능 입 니 다.주어진 action 생 성 함 수 를 dispatch 로 패키지 한 후 되 돌려 줍 니 다.
패키지 bindactioncreators
// redux.js
export const bindactioncreators = (mapDispatchToProps, dispatch) => {
const actionFun = {};
// mapDispatchToProps action dispatch
for(let key in mapDispatchToProps){
actionFun[key] = (...args) => {
dispatch(mapDispatchToProps[key](...args));
}
}
return actionFun;
// :
// return actionFun = Object.keys(mapDispatchToProps)
// .reduce((total, item) => {
// return { ...total, [item]: (...args) => {dispatch(mapDispatchToProps[item](...args));}
// } } ,{});
}
수정 connect:
// react-redux.js
import { bindactioncreators } from './redux';
....
export const connect = (mapStateToProps, mapDispatchToProps) => (Component) => {
return class NewComponent extends React.Component{
static contextTypes = {
store: propTypes.object
};
constructor(props, context){
super(props, context);
this.store = context.store;
this.state = {};
}
componentDidMount(){
this.store.subscribe(this.update);
this.update();
}
update = () => {
const state = this.store.getState();
const filterState = mapStateToProps(state);
// API bindactioncreators
// mapDispatchToProps action dispatch
const actionFun = bindactioncreators(mapDispatchToProps, this.store.dispatch);
this.setState({...filterState, ...actionFun});
}
render(){
return <Component {...this.state} />
}
}
}
5.redux API apply Middleware 의 실현이 간략화 판 의 react-redux 는 이미 초보 적 으로 완 성 된 것 이 라 고 할 수 있 습 니 다.그러나 만약 에 우리 가 우리 의 age 값 의 증 가 를 원 하 는 것 은 비동기 작업 입 니 다.예 를 들 어 단 추 를 누 른 후에 2 초 후에 age 를 수정 하 는 것 이지 단 추 를 누 르 면 바로 값 을 수정 하 는 것 이 아 닙 니 다.이렇게 되면 우 리 는 어떻게 실현 해 야 합 니까?
물론 우 리 는 setTimeout 을 통 해 2 초 후에 action 생 성 함 수 를 실행 할 수 있 습 니 다.예 를 들 어 다음 과 같 습 니 다.
onClick = () => {
setTimeout(()=>{
// action
this.props.changeAge();
}, 2000);
}
그러나 사실은 우 리 는 위 와 같이 그렇게 정돈 하 는 것 을 원 하지 않 는 다.우 리 는 이러한 효 과 를 원한 다.우 리 는 action 생 성 함수 만 간단하게 호출 하면 비동기 작업 을 실현 할 수 있 고 추가 적 인 조작 이 필요 한 것 이 아니다.이 때 우 리 는 우리 의 react-redux 를 위해 중간 부품 을 만들어 서 이 효 과 를 실현 해 야 한다.5.1 준비 작업
액 션 생 성 함수 추가
그 전에 우리 의 모든 acton 생 성 함 수 는 하나의 action 대상 에 게 직접 돌아 갑 니 다.다음은 서로 다른 action 생 성 함 수 를 쓰 겠 습 니 다.이것 은 하나의 action 대상 이 아니 라 하나의 함수 로 돌아 갑 니 다.그리고 이 함 수 는 두 개의 매개 변수 dispatch 와 getState 를 받 습 니 다.이 함수 내부 에서 우 리 는 해당 하 는 비동기 작업 을 합 니 다.예 를 들 어 age 값 을 수정 합 니 다.
// actionCreate.js
export const asyncChangeAge = () => {
//
return (dispatch, getState) => {
setTimeout(v=>{
console.log('==>', getState());
dispatch({type: 'AGE_GROW'});
}, 1000);
}
}
페이지 수정:버튼 을 추가 하고 이벤트 에 연결 하 며 asyncChangeAge 함 수 를 호출 합 니 다.
// Demo.jsx
// Demo.jsx
import React from 'react';
import './style.css';
// asyncChangeAge
import { changeAge, changeUser, asyncChangeAge } from './actionCreate';
import { connect } from './react-redux';
const mapStateToProps = (state) => {
return {user: state.user, age: state.age};
}
// asyncChangeAge
@connect(mapStateToProps, {changeAge, changeUser, asyncChangeAge})
export default class Demo extends React.Component{
onChange = (e) => {
this.props.changeUser(e.target.value);
}
onClick = () => {
this.props.changeAge();
}
//
onClickAsync = () => {
this.props.asyncChangeAge();
}
render(){
return (
<div>
<p>user: {this.props.user}, age: {this.props.age}</p>
user:
<input type="text" className="input" onChange={this.onChange}/>
<button className="btn" onClick={this.onClick}> </button>
{/* */}
<button className="btn" onClick={this.onClickAsync}>
</button>
</div>
);
}
}
페이지 의 변 화 는 사실 매우 간단 하 다.바로 버튼 을 추가 한 것 이다.5.2 수요 실현
그 다음 에 우 리 는 아무것도 고려 하지 않 고 먼저 우리 의 요 구 를 실현 합 니 다.현재 action 생 성 함수 asyncChangeAge 는 대상 으로 돌아 갑 니 다.type 값 은 undefined 이기 때문에 단 추 를 눌 렀 을 때 reducer 는 기본 상황 까지 일치 합 니 다.돌아 오 는 것 은 현재 상태 입 니 다.그 다음 에 우리 의 action 생 성 함수 asyncChangeAge 가 유효 합 니 다.비동기 수정 상태의 역할 달성 하기;
확장 createStore
asyncChangeAge 가 돌아 오 는 것 은 더 이상 action 대상 이 아니 라 함수 입 니 다.그러면 사실 우리 가 해 야 할 일 은 매우 간단 하 다.우 리 는 createStore 의 반환 값 dispatch 에 대해 간단 한 확장 만 하면 된다.dispatch 의 action 매개 변수 가 함수 인지 판단 하여 다른 작업 을 진행 합 니 다.
// redux.js
export const createStore = (reducer) => {
......
// createStore dispatch
const dispatchExtend = (action) => {
if(typeof action === 'function'){
// action ,
action(dispatch, getState);
} else {
// action ( ) dispatch
dispatch(action);
}
}
return {getState, dispatch: dispatchExtend, subscribe};
}
5.3 분리 패키지위 에서 저 희 는 createStore 의 반환 값 dispatch 를 확장 하여 redux-react 의 비동기 작업 을 실 현 했 습 니 다.문 제 는 우리 가 코드 를 createStore 에 썼 다 는 것 입 니 다.redux-react 의 비동기 작업 은 필수 옵션 이 아니 라 옵션 이 어야 합 니 다.
createStore 다시 확장:
매개 변수 middleware(함수)를 추가 하여 함수 createStore 시작 위치 에서 middleware 가 존재 하 는 지 판단 하고 존재 하면 실행 합 니 다.
// redux.js
export const createStore = (reducer, middleware) => {
// middleware ,
if(middleware){
return middleware(createStore)(reducer);
}
let state;
const listeners = [];
const getState = () => {
return state;
}
const dispatch = (action) => {
state = reducer(state, action);
listeners.forEach(v => v());
}
const subscribe = (listener) => {
listeners.push(listener);
}
dispatch({type: '%$&HJKAJJHDJHJ'});
return {getState, dispatch, subscribe};
}
함수 apply Middleware 를 작성 하여 store 를 만 들 때 createStore 두 번 째 인자 로 합 니 다.
// App.js
const applyMiddleware = (createStore) => (redux)=> {
// store
const store = createStore(redux);
// store
return {...store}
}
const store = createStore(reducer, applyMiddleware);
apply Middleware 함수 내 dispatch 확장위의 애플 리 케 이 션 Middleware 함 수 는 아무것도 하지 않 았 습 니 다.createStore 함수 밖 에 함 수 를 끼 웠 습 니 다.그러면 다음 에 우 리 는 올 바른 일 을 해서 dispatch 를 확장 하 겠 습 니 다.
// App.js
const applyMiddleware = (createStore) => (redux)=> {
const store = createStore(redux);
const midApi = {
getState: store.getState,
dispatch: (...args) => {dispatch(...args);}
};
const dispatch = (action) => {
if( typeof action === 'function' ){
action(midApi.dispatch, midApi.getState);
} else {
store.dispatch(action);
}
}
return {
...store,
dispatch
};
}
5.4 확장 분리위의 글 은 우리 가 원 하 는 효 과 를 실현 하 였 습 니 다.우 리 는 apply Middleware 에서 dispatch 를 확장 하 였 습 니 다.하지만 우 리 는 그렇게 쉽게 만족 할 수 있 잖 아,물론 아니 야!!apply Middleware 에서 dispatch 에 대한 확장 은 따로 꺼 내 서 함수 로 포장 할 수 있 습 니 다.
애플 리 케 이 션 미 들 웨 어 를 다시 쓰 고 애플 리 케 이 션 미 들 웨 어 에 함 수 를 감 싸 줍 니 다.dispatch 의 확장 을 분리 하고 패키지 하 는 방법 입 니 다.
// App.js
const applyMiddleware = (middleware) => (createStore) => (redux)=> {
const store = createStore(redux);
const midApi = {
getState: store.getState,
dispatch: (...args) => {dispatch(...args);}
};
const dispatch = middleware(midApi)(store.dispatch);
return {
...store,
dispatch
};
}
thunk 중간물:사실 thunk 이 야 말로 진정한 중간물 이다.apply Middleware 는 미들웨어 를 연결 하 는 데 만 사 용 됩 니 다.
// App.js
const thunk = ({dispatch, getState}) => next => (action) => {
if(typeof action === 'function'){
action(dispatch, getState);
} else {
next(action);
}
};
createStore 를 호출 할 때 중간 부품 thunk 바 인 딩
// App.jsx
const store = createStore(reducer, applyMiddleware(thunk));
5.5 코드 정리이 단 계 는 apply Middleware 함 수 를 redux.js 파일 로 옮 기 는 것 입 니 다.동시에 thunk 함 수 를 파일 thunk.jsx 에 쓴 다음 App.js 에서 apply Middleware 와 thunk 를 참조 합 니 다.
최종 효 과 를 보 세 요.
이상 이 바로 본 고의 모든 내용 입 니 다.여러분 의 학습 에 도움 이 되 고 저 희 를 많이 응원 해 주 셨 으 면 좋 겠 습 니 다.
이 내용에 흥미가 있습니까?
현재 기사가 여러분의 문제를 해결하지 못하는 경우 AI 엔진은 머신러닝 분석(스마트 모델이 방금 만들어져 부정확한 경우가 있을 수 있음)을 통해 가장 유사한 기사를 추천합니다:
nginx 에서 사이트 아이콘 표시 설정전단 개발 환경: react:^16.4.1 webpack:^4.16.0 웹 팩 에 favicon. ico 설정 추가: nginx 는 ico 에 대한 지원 을 추가 합 니 다:...
텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
CC BY-SA 2.5, CC BY-SA 3.0 및 CC BY-SA 4.0에 따라 라이센스가 부여됩니다.