프런트 엔드 페이지 생성 도구 페이지 메이커에 대한 자세한 설명
Pagemaker는 제품, 운영, 시각 학생들이 간단한 프런트엔드 페이지를 신속하게 개발할 수 있도록 하여 프런트엔드 학생들의 업무 부담을 덜어주는 프런트엔드 페이지 생성 도구입니다. 이 프로젝트의 아이디어는 NetEase 내부 프로젝트 nfop의 페이지 메이커 프로젝트에서 나왔습니다. 프로젝트의 프런트 엔드는 jquery와 템플릿 ejs를 사용하여 만들어지는 것으로 나타났습니다. 컴포넌트가 업데이트될 때마다 전체 DOM이 다시 그려지고 성능이 그다지 좋지 않습니다. 당시에는 React가 매우 인기가 있었고, 프로젝트 자체가 적합했기 때문에 마침내 우리는 React를 사용하여 물을 테스트하기로 결정했습니다. 전체 프로젝트에는 원래 많은 하위 프로젝트가 포함되어 있었기 때문에 백엔드 구현에는 참조가 없었고 완전히 다시 작성되었습니다.
이 프로젝트는 덜 사용되고 복잡한 구성 요소를 제거하여 원래 프로젝트를 간단하게 구현한 것입니다. 그러나 Sparrow는 작고 모든 내부 장기를 갖추고 있지만 이 프로젝트는 완전한 반응 기술 스택 세트를 사용합니다. 이는 이전에 반응을 공부했고 데모를 통해 이해와 실습을 심화시키고 싶은 학생들에게 적합합니다. 이 데모를 공부하기 전에 먼저 관련 지식 포인트를 연구/검토하는 것이 좋습니다: React 기술 스택 튜토리얼 시리즈, 불변의 자세한 설명 및 React 실습.
1. 기능적 특징
풍부한 구성 요소. 제목, 그림, 버튼, 텍스트, 오디오, 비디오, 통계 및 jscss 입력이 있습니다.
실시간 미리보기. 수정할 때마다 최신 미리보기를 즉시 확인할 수 있습니다.
세 가지 가져오기 방법을 지원하고 구성 파일 내보내기를 지원합니다.
실행 취소/다시 실행 작업을 지원합니다. (구성 요소 수의 변화가 트리거 포인트입니다)
게시된 페이지를 언제든지 게시, 수정, 삭제할 수 있습니다.
각 페이지에는 다른 사람이 수정하지 못하도록 게시 비밀번호가 있습니다.
페이지의 프런트엔드 아키텍처는 React+Redux를 채택하고 불변 데이터 구조를 채택합니다. 페이지 성능을 최적화하기 위해 각 구성 요소 업데이트를 최소화할 수 있습니다.
업로드된 이미지는 배경이 자동으로 압축되어 파일 용량이 너무 커지지 않습니다
모바일 단말기에 맞게 적용
2. 사용 기술
1. 프론트엔드
React
Redux
React-Redux
Immutable
React-Router
fetch
es6
-
es7
2. 백엔드
-
Node
Express
3. 도구
Webpack
Sass
Pug
3. 프로젝트에서는 더 많은 기술을 사용하므로 비계 도구를 사용하면 비용을 절약할 수 있습니다. 프로젝트 시간을 구축해야 합니다. 검색 결과 일반적으로 사용되는 세 가지가 있음을 발견했습니다.
- create-react-app
-
4. 핵심 코드 분석
1. Store
Store는 데이터가 저장되는 곳이라고 생각하시면 됩니다. 전체 애플리케이션에 대해 하나의 스토어만 있을 수 있습니다.
import { createStore } from 'redux'; import { combineReducers } from 'redux-immutable'; import unit from './reducer/unit'; // import content from './reducer/content'; let devToolsEnhancer = null; if (process.env.NODE_ENV === 'development') { devToolsEnhancer = require('remote-redux-devtools'); } const reducers = combineReducers({ unit }); let store = null; if (devToolsEnhancer) { store = createStore(reducers, devToolsEnhancer.default({ realtime: true, port: config.reduxDevPort })); } else { store = createStore(reducers); } export default store;
Redux는 Store를 생성하는 createStore 기능을 제공합니다. 전체 애플리케이션에는 모든 데이터를 포함하는 하나의 State 개체만 있으므로 대규모 애플리케이션의 경우 이 State는 매우 커야 하므로 Reducer 기능이 매우 커집니다. Redux는 Reducer를 분할하기 위한 CombineReducers 메서드를 제공합니다. 각 하위 감속기 함수를 정의한 다음 이 방법을 사용하여 이를 큰 감속기로 결합하면 됩니다. 물론 여기에는 분할할 수 있거나 분할할 수 없는 하나의 감속기 유닛만 있습니다.
devToolsEnhancer는 미들웨어입니다. 개발 환경에서 Redux를 디버그하기 위해 Redux DevTools를 사용하는 데 사용됩니다.
2. Action
Action은 현재 일어나고 있는 일을 설명합니다. 상태를 변경하는 유일한 방법은 Action을 사용하는 것입니다. 데이터를 Store로 전달합니다.
import Store from '../store'; const dispatch = Store.dispatch; const actions = { addUnit: (name) => dispatch({ type: 'AddUnit', name }), copyUnit: (id) => dispatch({ type: 'CopyUnit', id }), editUnit: (id, prop, value) => dispatch({ type: 'EditUnit', id, prop, value }), removeUnit: (id) => dispatch({ type: 'RemoveUnit', id }), clear: () => dispatch({ type: 'Clear'}), insert: (data, index) => dispatch({ type: 'Insert', data, index}), moveUnit: (fid, tid) => dispatch({ type: 'MoveUnit', fid, tid }), }; export default actions;
상태 변경은 보기 변경으로 이어집니다. 그러나 사용자는 상태에 액세스할 수 없고 보기에만 액세스할 수 있습니다. 따라서 State의 변경은 View에 의해 발생해야 합니다. Action은 View가 보내는 알림으로 State가 변경되어야 함을 나타냅니다. 코드에서 우리는 각각이 함수인 많은 속성을 가진 actions 개체를 정의합니다. 함수의 출력은 Store.dispatch를 통해 전송되는 작업 개체를 전달하는 것입니다. Action은 필수 유형 속성과 기타 부수적 정보를 포함하는 유형 속성입니다.
3. Immutable
Immutable Data 就是一旦创建,就不能再被更改的数据。对 Immutable 对象的任何修改或添加删除操作都会返回一个新的 Immutable 对象。详细介绍,推荐知乎上的Immutable 详解及 React 中实践。我们项目里用的是Facebook 工程师 Lee Byron 花费 3 年时间打造的immutable.js库。具体的API大家可以去官网学习。
熟悉 React 的都知道,React 做性能优化时有一个避免重复渲染的大招,就是使用 shouldComponentUpdate()
,但它默认返回 true
,即始终会执行 render()
方法,然后做 Virtual DOM 比较,并得出是否需要做真实 DOM 更新,这里往往会带来很多无必要的渲染并成为性能瓶颈。当然我们也可以在 shouldComponentUpdate()
中使用使用 deepCopy 和 deepCompare 来避免无必要的 render()
,但 deepCopy 和 deepCompare 一般都是非常耗性能的。
Immutable 则提供了简洁高效的判断数据是否变化的方法,只需 ===
(地址比较) 和 is
( 值比较) 比较就能知道是否需要执行 render()
,而这个操作几乎 0 成本,所以可以极大提高性能。修改后的 shouldComponentUpdate
是这样的:
import { is } from 'immutable'; shouldComponentUpdate: (nextProps = {}, nextState = {}) => { const thisProps = this.props || {}, thisState = this.state || {}; if (Object.keys(thisProps).length !== Object.keys(nextProps).length || Object.keys(thisState).length !== Object.keys(nextState).length) { return true; } for (const key in nextProps) { if (thisProps[key] !== nextProps[key] || !is(thisProps[key], nextProps[key])) { return true; } } for (const key in nextState) { if (thisState[key] !== nextState[key] || !is(thisState[key], nextState[key])) { return true; } } return false; }
使用 Immutable 后,如下图,当红色节点的 state 变化后,不会再渲染树中的所有节点,而是只渲染图中绿色的部分:
本项目中,我们采用支持 class 语法的 pure-render-decorator 来实现。我们希望达到的效果是:当我们编辑组件的属性时,其他组件并不被渲染,而且preview里,只有被修改的preview组件update,而其他preview组件不渲染。为了方便观察组件是否被渲染,我们人为的给组件增加了data-id的属性,其值为Math.random()
的随机值。效果如下图所示:
immutable实际效果图
4. Reducer
Store 收到 Action 以后,必须给出一个新的 State,这样 View 才会发生变化。这种 State 的计算过程就叫做 Reducer。
import immutable from 'immutable'; const unitsConfig = immutable.fromJS({ META: { type: 'META', name: 'META信息配置', title: '', keywords: '', desc: '' }, TITLE: { type: 'TITLE', name: '标题', text: '', url: '', color: '#000', fontSize: "middle", textAlign: "center", padding: [0, 0, 0, 0], margin: [10, 0, 20, 0] }, IMAGE: { type: 'IMAGE', name: '图片', address: '', url: '', bgColor: '#fff', padding: [0, 0, 0, 0], margin: [10, 0, 20, 0] }, BUTTON: { type: 'BUTTON', name: '按钮', address: '', url: '', txt: '', margin: [ 0, 30, 20, 30 ], buttonStyle: "yellowStyle", bigRadius: true, style: 'default' }, TEXTBODY: { type: 'TEXTBODY', name: '正文', text: '', textColor: '#333', bgColor: '#fff', fontSize: "small", textAlign: "center", padding: [0, 0, 0, 0], margin: [0, 30, 20, 30], changeLine: true, retract: true, bigLH: true, bigPD: true, noUL: true, borderRadius: true }, AUDIO: { type: 'AUDIO', name: '音频', address: '', size: 'middle', position: 'topRight', bgColor: '#9160c3', loop: true, auto: true }, VIDEO: { type: 'VIDEO', name: '视频', address: '', loop: true, auto: true, padding: [0, 0, 20, 0] }, CODE: { type: 'CODE', name: 'JSCSS', js: '', css: '' }, STATISTIC: { type: 'STATISTIC', name: '统计', id: '' } }) const initialState = immutable.fromJS([ { type: 'META', name: 'META信息配置', title: '', keywords: '', desc: '', // 非常重要的属性,表明这次state变化来自哪个组件! fromType: '' } ]); function reducer(state = initialState, action) { let newState, localData, tmp // 初始化从localstorage取数据 if (state === initialState) { localData = localStorage.getItem('config'); !!localData && (state = immutable.fromJS(JSON.parse(localData))); // sessionStorage的初始化 sessionStorage.setItem('configs', JSON.stringify([])); sessionStorage.setItem('index', 0); } switch (action.type) { case 'AddUnit': { tmp = state.push(unitsConfig.get(action.name)); newState = tmp.setIn([0, 'fromType'], action.name); break } case 'CopyUnit': { tmp = state.push(state.get(action.id)); newState = tmp.setIn([0, 'fromType'], state.getIn([action.id, 'type'])); break } case 'EditUnit': { tmp = state.setIn([action.id, action.prop], action.value); newState = tmp.setIn([0, 'fromType'], state.getIn([action.id, 'type'])); break } case 'RemoveUnit': { const type = state.getIn([action.id, 'type']); tmp = state.splice(action.id, 1); newState = tmp.setIn([0, 'fromType'], type); break } case 'Clear': { tmp = initialState; newState = tmp.setIn([0, 'fromType'], 'ALL'); break } case 'Insert': { tmp = immutable.fromJS(action.data); newState = tmp.setIn([0, 'fromType'], 'ALL'); break } case 'MoveUnit':{ const {fid, tid} = action; const fitem = state.get(fid); if (fitem && fid != tid) { tmp = state.splice(fid, 1).splice(tid, 0, fitem); } else { tmp = state; } newState = tmp.setIn([0, 'fromType'], ''); break; } default: newState = state; } // 更新localstorage,便于恢复现场 localStorage.setItem('config', JSON.stringify(newState.toJS())); // 撤销,恢复操作(仅以组件数量变化为触发点,否则存储数据巨大,也没必要) let index = parseInt(sessionStorage.getItem('index')); let configs = JSON.parse(sessionStorage.getItem('configs')); if(action.type == 'Insert' && action.index){ sessionStorage.setItem('index', index + action.index); }else{ if(newState.toJS().length != state.toJS().length){ // 组件的数量有变化,删除历史记录index指针状态之后的所有configs,将这次变化的config作为最新的记录 configs.splice(index + 1, configs.length - index - 1, JSON.stringify(newState.toJS())); sessionStorage.setItem('configs', JSON.stringify(configs)); sessionStorage.setItem('index', configs.length - 1); }else{ // 组件数量没有变化,index不变。但是要更新存储的config配置 configs.splice(index, 1, JSON.stringify(newState.toJS())); sessionStorage.setItem('configs', JSON.stringify(configs)); } } // console.log(JSON.parse(sessionStorage.getItem('configs'))); return newState } export default reducer;
Reducer是一个函数,它接受Action和当前State作为参数,返回一个新的State。unitsConfig是存储着各个组件初始配置的对象集合,所有新添加的组件都从里边取初始值。State有一个初始值:initialState,包含META组件,因为每个web页面必定有一个META信息,而且只有一个,所以页面左侧组件列表里不包含它。
reducer会根据action的type不同,去执行相应的操作。但是一定要注意,immutable数据操作后要记得赋值。每次结束后我们都会去修改fromType值,是因为有的组件,比如AUDIO、CODE等修改后,预览的js代码需要重新执行一次才可以生效,而其他组件我们可以不用去执行,提高性能。
当然,我们页面也做了现场恢复功能(localStorage),也得益于immutable数据结构,我们实现了Redo/Undo的功能。Redo/Undo的功能仅会在组件个数有变化的时候计作一次版本,否则录取的的信息太多,会对性能造成影响。当然,组件信息发生变化我们是会去更新数组的。
5. 工作流程
用户能接触到的只有view层,就是组件里的各种输入框,单选多选等。用户与之发生交互,会发出action。React-Redux提供connect方法,用于从UI组件生成容器组件。connect方法接受两个参数:mapStateToProps和mapDispatchToProps,按照React-Redux的API,我们需要将Store.dispatch(action)写在mapDispatchToProps函数里边,但是为了书写方便和直观看出这个action是哪里发出的,我们没有遵循这个API,而是直接写在在代码中。
然后,Store 自动调用 Reducer,并且传入两个参数:当前 State 和收到的 Action。 Reducer 会返回新的 State 。State 一旦有变化,Store 就会调用监听函数。在React-Redux规则里,我们需要提供mapStateToProps函数,建立一个从(外部的)state对象到(UI组件的)props对象的映射关系。mapStateToProps会订阅 Store,每当state更新的时候,就会自动执行,重新计算 UI 组件的参数,从而触发UI组件的重新渲染。大家可以看我们content.js组件的最后代码:
export default connect( state => ({ unit: state.get('unit'), }) )(Content);
connect方法可以省略mapStateToProps参数,那样的话,UI组件就不会订阅Store,就是说 Store 的更新不会引起 UI 组件的更新。像header和footer组件,就是纯UI组件。
为什么我们的各个子组件都可以拿到state状态,那是因为我们在最顶层组件外面又包了一层
import "babel-polyfill"; import React from 'react'; import ReactDom from 'react-dom'; import { Provider } from 'react-redux'; import { Router, Route, IndexRoute, browserHistory } from 'react-router'; import './index.scss'; import Store from './store'; import App from './components/app'; ReactDom.render( <Provider store={Store}> <Router history={browserHistory}> <Route path="/" component={App}> </Route> </Router> </Provider>, document.querySelector('#app') );
我们的react-router采用的是browserHistory,使用的是HTML5的History API,路由切换交给后台。
五、兼容性和打包优化
1. 兼容性
为了让页面更好的兼容IE9+和android浏览器,因为项目使用了babel,所以采用babel-polyfill和babel-plugin-transform-runtime插件。
2. Antd按需加载
Antd完整包特别大,有10M多。而我们项目里主要是采用了弹窗组件,所以我们应该采用按需加载。只需在.babelrc文件里配置一下即可,详见官方说明。
3. webpack配置externals属性
项目最后打包的main.js非常大,有接近10M多。在网上搜了很多方法,最后发现webpack配置externals属性的方法非常好。可以利用pc的多文件并行下载,降低自己服务器的压力和流量,同时可以利用cdn的缓存资源。配置如下所示:
externals: { "jquery": "jQuery", "react": "React", "react-dom": "ReactDOM", 'CodeMirror': 'CodeMirror', 'immutable': 'Immutable', 'react-router': 'ReactRouter' }
externals属性告诉webpack,如下的这些资源不进行打包,从外部引入。一般都是一些公共文件,比如jquery、react等。注意,因为这些文件从外部引入,所以在npm install
的时候,有些依赖这些公共文件的包安装会报warning,所以看到这些大家不要紧张。经过处理,main.js文件大小降到3.7M,然后nginx配置下gzip编码压缩,最终将文件大小降到872KB。因为在移动端,文件加载还是比较慢的,我又给页面加了loading效果。
위 내용은 프런트 엔드 페이지 생성 도구 페이지 메이커에 대한 자세한 설명의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

핫 AI 도구

Undresser.AI Undress
사실적인 누드 사진을 만들기 위한 AI 기반 앱

AI Clothes Remover
사진에서 옷을 제거하는 온라인 AI 도구입니다.

Undress AI Tool
무료로 이미지를 벗다

Clothoff.io
AI 옷 제거제

Video Face Swap
완전히 무료인 AI 얼굴 교환 도구를 사용하여 모든 비디오의 얼굴을 쉽게 바꾸세요!

인기 기사

뜨거운 도구

메모장++7.3.1
사용하기 쉬운 무료 코드 편집기

SublimeText3 중국어 버전
중국어 버전, 사용하기 매우 쉽습니다.

스튜디오 13.0.1 보내기
강력한 PHP 통합 개발 환경

드림위버 CS6
시각적 웹 개발 도구

SublimeText3 Mac 버전
신 수준의 코드 편집 소프트웨어(SublimeText3)

뜨거운 주제











Ouyi Okx는 세계 최고의 디지털 자산 거래 플랫폼입니다. 1) 개발 기록은 다음과 같습니다. 2017 년에 출시 될 예정, 중국 이름 "Ouyi"는 2021 년에 출시 될 예정이며 2022 년에 Ouyi Okx로 이름이 바뀔 것입니다. 2) 핵심 서비스에는 거래 서비스 (코인, 레버리지, 계약, 덱스, 화폐 거래) 및 금융 서비스 (Yubibao, Defi Mining, Lending)가 포함됩니다. 3) 플랫폼의 특수 기능에는 시장 데이터 서비스 및 위험 제어 시스템이 포함됩니다. 4) 핵심 장점에는 기술 강도, 보안 시스템, 서비스 지원 및 시장 범위가 포함됩니다.

2025 년의 상위 10 개 암호 화폐 계약 교환 플랫폼은 다음과 같습니다. 1. Binance Futures, 2. Okx Futures, 3. Gate.io, 4. Huobi Futures, 5. Bitmex, 6. Bybit, 7. Bitfinex, 9. Coinflex, 10. Phemex,이 플랫폼은 그들의 유동적 인 무역 안보자로 인정 받고 있습니다.

2025 년에 레버리지 거래, 보안 및 사용자 경험에서 뛰어난 성능을 보이는 플랫폼은 다음과 같습니다. 1. OKX, 고주파 거래자에게 적합하여 최대 100 배의 레버리지를 제공합니다. 2. Binance, 전 세계의 다중 통화 거래자에게 적합하며 125 배 높은 레버리지를 제공합니다. 3. Gate.io, 전문 파생 상품 플레이어에게 적합하며 100 배의 레버리지를 제공합니다. 4. 초보자 및 소셜 트레이더에게 적합한 Bitget, 최대 100 배의 레버리지를 제공합니다. 5. 크라켄은 꾸준한 투자자에게 적합하며 5 배의 레버리지를 제공합니다. 6. Bybit, Altcoin Explorers에 적합하며 20 배의 레버리지를 제공합니다. 7. 저비용 거래자에게 적합한 Kucoin, 10 배의 레버리지를 제공합니다. 8. 비트 피 넥스, 시니어 플레이에 적합합니다

cryptocurrency 교환 선택에 대한 제안 : 1. 유동성 요구 사항의 경우 우선 순위는 순서 깊이와 강한 변동성 저항으로 인해 Binance, Gate.io 또는 Okx입니다. 2. 규정 준수 및 보안, 코인베이스, 크라켄 및 쌍둥이 자리는 엄격한 규제 승인을 받았습니다. 3. Kucoin의 소프트 스테이 킹 및 Bybit의 파생 설계 혁신적인 기능은 고급 사용자에게 적합합니다.

주류 코인을 구매할 수있는 앱 소프트웨어는 다음과 같습니다. 1. Binance, 세계 최고의 거래량 및 빠른 속도; 2. OKX, 혁신적인 제품, 낮은 수수료, 높은 보안; 3. Gate.io, 보안에 중점을 둔 다양한 자산 및 거래 옵션; 4. Huobi (HTX), 낮은 수수료, 좋은 사용자 경험; 5. 초보자, 높은 보안에 적합한 코인베이스; 6. 크라켄, 안전하고 준수하는 다양한 서비스를 제공합니다. 7. Kucoin, 낮은 수수료, 전문 거래자에게 적합합니다. 8. Gemini는 준수를 강조하며 관리 서비스를 제공합니다. 9. 다양한 제안 및 서비스를 제공하는 crypto.com; 10. 비트 스탬프, 오래된 교환, 강한 유동성,

상위 5 개 비트 코인 선물 교환 순위는 다음과 같습니다. 1. CME 그룹 : 세계에서 가장 신뢰할 수 있고 기관 투자자를 유치하며 강력한 규정 준수를합니다. 2. Coinbase : 특히 13 개의 선물 계약을 제공하는 소매 투자자에게 특히 사용이 쉽습니다. 3. BINANCE : 높은 레버리지, 유동성이 우수하며 큰 사용자 볼륨; 4. OKX : 큰 누적 거래량, 전문 인터페이스 및 완전한 위험 관리; 5. 크라켄 : 유럽 시장 및 기관 고객에게 적합한 높은 보안.

2025 년 세계 최고의 암호 화폐 교환은 다음과 같습니다. 1. Binance, 2. Coinbase, 3. Okx, 4. Kraken, 5. Kucoin, 6. Bitget, 7. Bybit, 8. gemini, 9. crypto.com, MEXC Matcha Exchange, 10. MEXC Matcha Exchange, 10. MEXC Matcha Exchange, 10. 글로벌 적용 범위는 다른 요구가있는 투자자에게 적합합니다.

양자 체인은 다음 거래소에서 거래 될 수 있습니다. 1. Binance : 전 세계 최대의 거래소 중 하나, 많은 거래량, 풍부한 통화 및 높은 보안이 있습니다. 2. 참깨 오픈 도어 (GATE.IO) : 대규모 교환으로 다양한 디지털 통화 거래를 제공하며 거래 깊이가 우수합니다. 3. OUYI (OKX) : OK Group이 강한 포괄적 인 강도, 대량의 거래량 및 완전한 안전 조치로 운영합니다. 4. BITGET : 빠른 개발, 양자 체인 트랜잭션을 제공하며 보안을 향상시킵니다. 5. Bithumb : 일본에서 운영되며 여러 주류 가상 통화의 거래를 지원하며 안전하고 신뢰할 수 있습니다. 6. Matcha Exchange : 친절한 인터페이스와 잘 알려진 교환과 양자 체인 거래를 지원합니다. 7. Huobi : 양자 체인 거래를 제공하는 대규모 교환,
