개설 개요
Gmail에 Ajax 기술이 성공적으로 적용되고 고성능 V8 엔진이 출시되면서 웹 애플리케이션 작성이 대중화되었습니다. 복잡한 상호 작용이 포함된 애플리케이션도 프런트엔드 기술을 사용하여 작성할 수 있습니다. 기본 애플리케이션에 비해 웹 애플리케이션은 다음과 같은 장점이 있습니다.
크로스 플랫폼, 낮은 개발 및 유지 관리 비용
업그레이드 및 게시가 편리하고 버전 개념이 없습니다. 사용자 인식 없이 언제 어디서나 게시할 수 있습니다.
반응형 디자인을 사용하면 웹 애플리케이션이 크로스 플랫폼이 될 수 있으며 동일한 코드가 다양한 화면 크기에 적응할 수 있습니다.
웹 애플리케이션이더라도 솔루션은 결국 사용되지 않지만 여전히 적합합니다. 프로토타입 개발
물론 웹 애플리케이션에도 단점이 없는 것은 아닙니다. 다양한 플랫폼과 제조업체의 브라우저가 완전히 동일하지 않기 때문에 플랫폼 간 호환성 비용도 일부 발생합니다. 또한, 웹 애플리케이션의 성능은 네이티브 애플리케이션만큼 좋지 않고, HTML5 API의 한계로 인해 상호 작용이 원활하지 않을 때도 있습니다. 이러한 이유로 두 가지 장점을 결합한 하이브리드 솔루션이 인기를 얻고 있습니다(예: WeChat, 모바일 QQ 및 모바일 QQ 브라우저에는 일부 웹 페이지가 포함됩니다).
저자의 개발 경험을 바탕으로 웹 애플리케이션 개발 시 직면해야 할 몇 가지 문제를 요약해 보겠습니다.
모듈형 프로그래밍
모듈형 프로그래밍은 대규모 애플리케이션을 작성하는 데 필수적인 기능입니다. 다른 주류 프로그래밍 언어에 비해 Javascript는 모듈에 대한 직접적인 지원을 제공하지 않습니다. Javascript 코드를 유지 관리하는 것은 매우 어렵고 3f1c4e4b6b16bbbd69b2ee476dc4f83a 태그에 포함된 코드 순서는 수동 유지 관리가 필요합니다.
모듈형 프로그래밍을 지원하려면 두 가지 문제를 해결해야 합니다.
는 이름 충돌을 방지하기 위해 모듈 작성 및 이름 지정을 지원하고
는 지정된 모듈 종속성 표시를 지원합니다. , 프로그램이 실행될 때 자동으로 종속 모듈을 로드합니다.
Douglas Crockford가 책 "Javascript: The Good Parts"에서 제안한 모듈 패턴은 Javascript의 클로저 기술을 사용하여 모듈의 개념을 시뮬레이션하고 이름 충돌과 전역 변수 사용을 방지합니다. 이로써 첫 번째 문제가 해결됩니다.
var moduleName = function () { // Define private variables and functions var private = ... // Return public interface. return { foo: ... }; }();
두 번째 문제를 해결하기 위해 CommonJS 조직에서는 개발자가 필요할 때 지정된 모듈 간의 종속성을 표시하고 종속 모듈을 로드할 수 있도록 AMD 사양을 정의했습니다. RequireJS는 널리 사용되는 AMD 사양 구현입니다.
먼저 a.js에서 모듈 A를 정의합니다.
define(function () { return { color: "black", size: 10 }; });
그런 다음 모듈 A에 종속되도록 모듈 B를 정의합니다.
define(["a"], function (A) { // ... });
RequireJS는 모듈 B가 실행된 모듈 A가 로드되었습니다. 구체적인 내용은 RequireJS 공식 문서를 참고하세요.
스크립트 로딩
스크립트를 로드하는 가장 간단한 방법은 93f0f5c25f18dab9d176bd4f6de5d30e에 로드하는 것입니다.
<head> <script src="base.js" type="text/javascript"></script> <script src="app.js" type="text/javascript"></script> </head>
단점은
Base.js가 먼저 다운로드된 다음 파싱 및 실행된 다음 app.js가 다운로드된다는 점입니다.
스크립트를 로드할 때 3f1c4e4b6b16bbbd69b2ee476dc4f83a 이후 DOM 요소의 렌더링도 차단합니다.
이러한 문제를 완화하기 위해 이제 3f1c4e4b6b16bbbd69b2ee476dc4f83a를 6c04bd5ca3fcae76e30b72ad730ca86d 하단에 배치하는 것이 일반적입니다.
<script src="base.js" type="text/javascript"></script> <script src="app.js" type="text/javascript"></script> </body>
그러나 모든 스크립트를 6c04bd5ca3fcae76e30b72ad730ca86d 하단에 배치할 수 있는 것은 아닙니다. 예를 들어 페이지가 렌더링될 때 일부 로직을 실행해야 하지만 대부분의 스크립트에는 그러한 요구 사항이 없습니다.
6c04bd5ca3fcae76e30b72ad730ca86d 하단에 스크립트를 배치해도 여전히 순차 다운로드 문제가 해결되지 않습니다. 일부 브라우저 제조업체에서도 이 문제를 인지하고 비동기 다운로드를 지원하기 시작했습니다. HTML5는 표준 솔루션도 제공합니다.
<script src="base.js" type="text/javascript" async></script> <script src="app.js" type="text/javascript" async></script>
async 속성이 표시된 스크립트는 document.write와 같은 코드를 사용하지 않음을 나타냅니다. 브라우저는 이러한 스크립트를 비동기적으로 다운로드하고 실행하며 DOM 트리 렌더링을 방해하지 않습니다. 그러나 이로 인해 또 다른 문제가 발생합니다. 비동기 실행으로 인해 app.js가 base.js보다 먼저 실행될 수 있으며, 이로 인해 둘 사이에 종속성이 있는 경우 오류가 발생합니다.
말하자면 개발자의 관점에서 우리에게 실제로 필요한 기능은 다음과 같습니다.
비동기 다운로드, DOM 렌더링을 차단하지 않음
다음에 따라 해결; 모듈 종속성을 확인하고 스크립트를 실행합니다.
따라서 스크립트 로딩은 실제로 모듈식 프로그래밍 문제와 함께 해결되어야 합니다. RequireJS는 모듈 간의 종속성을 기록할 뿐만 아니라 종속성을 기반으로 온디맨드 로딩 및 실행도 제공합니다(자세한 내용은 RequireJS 공식 문서 참조).
스크립트 로딩에 대한 추가 솔루션은 여기를 참조하세요.
정적 리소스 파일 배포
여기의 정적 리소스 파일은 CSS, Javascript 및 CSS에 필요한 일부 이미지를 참조합니다. 문서. 배포에서는 두 가지 문제를 고려해야 합니다.
다운로드 속도
버전 관리
정적 리소스 파일의 한 가지 특징은 자주 변경되지 않고 사용자 ID와 관련이 없으므로(즉, 쿠키와 관련 없음) 캐싱에 매우 적합하다는 것입니다. 반면에 정적 리소스 파일이 변경되면 브라우저는 웹 서버에서 최신 버전을 다운로드해야 합니다. 웹 애플리케이션의 새 버전이 출시되면 모든 사용자가 새 버전을 즉시 사용하지는 않으며, 이는 버전 일치 문제와 관련됩니다. 이전 버전의 애플리케이션은 이전 버전의 CSS 및 Javascript를 다운로드해야 하며, 최신 버전의 애플리케이션은 최신 버전의 정적 리소스를 다운로드해야 합니다.
버전 불일치를 방지하기 위해 새 버전의 애플리케이션이 출시될 때마다 정적 리소스 파일의 이름을 변경해야 하므로 이전 HTML은 이전 정적 파일을 참조하고 새 HTML은 새로운 정적 파일. 일반적인 접근 방식은 파일 이름에 타임스탬프를 추가하는 것입니다.
매달린 참조를 방지하려면 리소스 파일을 HTML보다 먼저 게시해야 합니다.
위 솔루션을 사용하면 버전 문제를 해결할 수 있으므로 각 정적 파일의 캐시 시간을 임의로 크게 설정하여 반복 다운로드를 방지하는 동시에 브라우저가 업데이트될 수 있습니다. 새 버전이 출시되었습니다.
다운로드 속도 문제를 해결하려면 다음 해결 방법을 고려할 수 있습니다.
정적 파일을 병합하여 파일이 너무 많지 않도록 하려면 일반적으로 브라우저에서 다운로드하는 데 더 많은 연결이 필요합니다. 동일한 도메인 이름에 대한 연결 수;
가독성을 위해 정적 파일을 압축합니다. CSS와 Javascript에는 일반적으로 게시할 때 제거할 수 있는 빈 줄, 들여쓰기 및 주석이 많이 있습니다. >정적 파일은 일반적으로 쿠키와 관련이 없으므로 전송 크기를 줄이고 캐시 적중률을 높이려면(캐시된 키는 쿠키를 고려해야 함) 정적 파일은 쿠키가 없는 도메인 이름에서 호스팅되는 것이 가장 좋습니다.
마지막으로 가장 중요한 것은 위의 프로세스를 자동화하는 것입니다.
MVC 프로그래밍 모델
웹 애플리케이션은 이벤트 기반 프로그래밍 모델을 사용하는데, 이는 인프라에서 제공하는 API가 다르다는 점뿐입니다. UI 프로그래밍은 일반적으로 MVC 디자인 패턴을 채택합니다. 예를 들어 Backbone.js에는 다음과 같은 부분이 포함됩니다.
모델
유일한 데이터 소스
책임 데이터 획득 및 저장
캐싱 메커니즘 제공
데이터 변경 시 이벤트를 통해 다른 객체에 알림
보기
렌더링을 담당
UI 이벤트와 모델 이벤트 Drawing UI를 모두 수신합니다
렌더링 결과는 모델 및 UI 상호 작용 상태라는 두 가지 유형의 데이터에 따라 달라집니다.
UI의 상호 작용 상태는 일반적으로 View 객체에 저장됩니다. , 때로는 편의상 DOM 트리 노드에도 저장되기도 합니다
렌더링 비용을 줄이기 위해 렌더링해야 하는 영역을 줄이고, 데이터가 변경될 때마다 영향을 받는 영역만 렌더링하도록 하세요
라우터
는 URL 변경을 모니터링하고 해당 View 개체에 페이지 렌더링을 알리는 역할을 담당합니다.
MVC를 효과적으로 사용하려면 주의해야 할 몇 가지 문제가 있습니다. 에게.
Model은 View와 완전히 분리되어야 합니다
Model은 데이터에 대한 액세스만 제공하고 View에 의존해서는 안 되므로 Model은 View의 존재를 알 수 없습니다. 따라서 모델은 View 객체에 대한 참조를 보유할 수 없습니다. 모델의 데이터가 변경되면 이벤트를 통해서만 View에 알릴 수 있습니다.
View는 초기화 중에 UI 이벤트를 수신하기 위해 위임을 사용합니다.
여기에는 두 가지 핵심 사항이 있습니다.
초기화 중 이벤트 수신 중 var View = Backbone.View.extend({ 초기화: function () { this.$el.on('click', '#id', function () { // … }); } } );
일부 특별한 경우(아래 참조)를 제외하고 모든 UI 이벤트는 동일한 이벤트가 여러 번 바인딩되는 것을 방지하기 위해 뷰가 초기화될 때 초기화되어야 합니다. 일부 이벤트가 동적으로 모니터링되더라도(모니터링이 필요한 경우도 있고 모니터링이 필요하지 않은 경우도 있습니다. 예를 들어 일부 버튼이 유효할 때도 있고 유효하지 않은 경우도 있음) 초기화 중에 계속 모니터링한 후 이벤트 콜백 함수에서 판단해야 합니다. 처리가 필요합니다. 이는 논리를 더 간단하고 유지 관리하기 쉽게 만듭니다.
UI 이벤트를 모니터링하려면 위임 방식을 사용하세요
위임 방식은 jQuery 문서를 참고하세요.
위에서는 초기화 시 이벤트를 모니터링해야 한다고 강조했지만, 초기화 시 모니터링해야 하는 DOM 노드가 아직 존재하지 않을 수 있으므로 이벤트를 직접 바인딩할 수 없으며 위임만 사용할 수 있습니다. 그러나 위임 방법을 사용하려면 이벤트가 버블링될 수 있어야 합니다.
버블링할 수 없는 이벤트(예: a1f02c36ba31691bcfe87b2722de723b의 로드 이벤트)의 경우 존재가 보장되는 경우에만 직접 바인딩할 수 있으며 초기화 중에는 반드시 바인딩할 수 없습니다.
Complex View가 트리 계층 구조로 구성되어 있습니다.
기능이 너무 커서 여러 하위 기능으로 분할해야 합니다. 마찬가지로, 뷰의 로직이 너무 복잡한 경우 페이지 구조에 따라 여러 하위 뷰로 분할해야 합니다.
상위 뷰는 참조로 하위 뷰에 액세스하지만 하위 뷰는 상위 뷰에 대한 참조를 보유하지 않습니다.
하위 뷰는 자신의 영역 렌더링만 담당하고 상위 뷰는 다른 영역 렌더링을 담당합니다.
부모 뷰는 액세스합니다. 하위 뷰의 기능은 함수 호출을 통해 이루어지며, 하위 뷰는 이벤트를 통해 상위 뷰와 통신합니다.
하위 뷰는 서로 직접 통신할 수 없습니다.
다른 팁은 백본 기술 및 패턴을 확인하세요.
오프라인 애플리케이션 캐시
웹 애플리케이션 경험을 보다 원활하게 만들기 위해 HTML5 오프라인 애플리케이션 캐시 사용을 고려할 수 있습니다. 그러나 다음과 같은 주의 사항이 있습니다.
오프라인 애플리케이션 캐싱과 HTTP 캐싱 메커니즘을 혼동하지 마세요. 전자는 HTML5에서 도입된 새로운 기능이며 HTTP 캐싱 메커니즘과 독립적으로 공존합니다.
Cache manifest文件不应被HTTP缓存太久(通过HTTP头Cache-Control控制缓存 时间),否则发布新版后浏览器不会及时监测到变化并下载新文件;
在Cache manifest文件的NETWORK节放一个*,否则没有列在这个文件的资源不 会被请求;
不适合缓存的请求最好都放在NETWORK节;
如果之前使用过离线应用缓存现在不想再使用了,从100db36a723c770d327fc0aef2ce13b1删除manifest属性, 并发送404响应给manifest文件请求。仅仅删除manifest属性是没有效的。
线上错误报告
Javascript是一个动态语言,许多检查都是在运行时执行的,所以大多数错误只有执行到的时候才能检查到,只能在发布前通过大量测试来发现。即使这样仍可能有少数 没有执行到的路径有错误,这只能通过线上错误报告来发现了。
window.onerror = function (errorMsg, fileLoc, linenumber) { var s = 'url: ' + document.URL + '\nfile: ' + fileLoc + '\nline number: ' + linenumber + '\nmessage: ' + errorMsg; Log.error(s); // 发给服务器统计监控 console.log(s); };
通常线上的Javascript都是经过了合并和压缩的,上报的文件名和行号基本上没法对 应到源代码,对查错帮助不是很大。不过最新版的Chrome支持在onerror的回调函数 中获取出错时的栈轨迹:window.event.error.stack.