AngularJS2+調用原有的js腳本(AngularJS腳本跟本地原有腳本之間的關係)

来源:https://www.cnblogs.com/andrewwang/archive/2018/03/09/8536292.html
-Advertisement-
Play Games

昨天一個話題說關於AngularJS2以後版本的兩個小技巧,不料引出了另外一個話題,話題起始很簡單: “很多的前端框架並不複雜,比如JQuery,引入即用,實時看到效果,多好。到了Angular2一直到現在的版本5,一點改進沒有,還要編譯,還要部署,原有的JS腳本也不能用了。” 細想起來,這個話題的 ...



昨天一個話題說關於AngularJS2以後版本的兩個小技巧,不料引出了另外一個話題,話題起始很簡單:
“很多的前端框架並不複雜,比如JQuery,引入即用,實時看到效果,多好。到了Angular2一直到現在的版本5,一點改進沒有,還要編譯,還要部署,原有的JS腳本也不能用了。”
細想起來,這個話題的帽子並不小,至少牽扯出來一個關鍵,AngularJS2及以後的版本,其框架之下的JS代碼,跟HTML中<script>塊之中的JS代碼,到底是什麼關係?
我試著來回答一下:

  • 首先,在AngularJS2框架之中實際使用的是ES6,全稱ECMAScript6,是Javascript的下一個版本。官方的例子則是基本採用TS,全稱TypeScript,是JS的一個超集。之所以用起來沒有明顯區別的感覺,因為的確從常用語法上,跟當前使用的JS,或者叫ES5 JS,差別很小,但即便再小,那也算的上不同的語言了。
  • 為什麼採用新的語言,而不是沿用當前的ES5,官網和社區已經有了很多解釋了,新語言當然有新語言的優勢,比如定義變數,可以指定類型,而在程式中用錯類型,則會在編譯過程中就給出警告,不至於等到上線了才發現BUG。這些優勢非常多,這裡就不畫蛇添足了。反正你肯定能理解,新當然有新的好處。
  • 既然採用了新的語言,為了跟當前的瀏覽器系統相容,當然就有一個翻譯過程,準確的說,甭管是TS還是ES6,甚至將來可能的ES7,在當下,都要翻譯成ES5,才能在當前流行的瀏覽器之中運行。這個翻譯,行話上講,也就是“編譯”。
  • 事實上,編譯不僅僅乾這麼一點事,很多的優化工作、查錯工作,也是在這個階段完成的,比如你使用了沒有定義的變數、函數;比如你用錯了函數類型;比如你使用了某個函數庫但只是用了其中一小部分,那麼多沒用的部分應當排除掉避免占用寶貴的下載帶寬,這些都是在編譯過程做到的。
  • 好了,既然經過了這麼複雜的動作,這個編譯也必不可少,那麼實際上答案已經出來了:那就是,很多原有理所應當存在的東西,就比如你在HTML中定義的JS對象、變數、函數,那些都是在執行環節,瀏覽器中才存在的。而在編譯階段,那些東西還只是停留在字元狀態,AngularJS當然並不知道他們存在,也就無法直接的、像原來我們使用HTML-JS一樣來使用它們了,這就如同上面那張圖,看上去海天一色,互相映襯,但在根本上,它們是在兩個世界。

上面是從技術實現上的限制原因,實際上還有一個設計哲學邏輯上的原因:

  • AngularJS設計之初就不是為了單純的在桌面瀏覽器中運行,還希望能夠在手機、移動設備甚至其它設備上執行。你可能會說,現在的手機瀏覽器也很發達啊,至少比很多IE6/IE7之流要強多了,稍等,這裡說的移動設備、其它設備,可不一定是指僅僅瀏覽器,從這種設計邏輯出發,AngularJS成為一種跨平臺的開發框架,直接編譯成各種系統原生的代碼,完全是有可能實現的。試想,在那種情況下,你原來的JS代碼很可能是連存在的空間都沒有,又如何讓AngularJS訪問到呢?

————————————————————————————————————————————

那是不是原有的JS代碼和技術都要作廢掉,無法再使用了呢?
當然不是,你肯定早看到了,大量的第三方模塊和代碼庫,通過NPM的管理,共存於這個架構中,彼此友好的相處。你原有的工作,完全可以用同樣的方式來工作。
你也可能會說,可我有很多代碼沒有做到那麼好的面向對象化包裝,也不想做那麼複雜,該怎麼辦呢?AngularJS也提供了至少3個方法,來完成兩個世界的打通工作。
第一個方法,使用declare來預聲明:
我們來先看一個例子,使用ng new testExtJS來新建一個工程,接著cd testJS進入項目目錄,使用cnpm install來初始化依賴包。用cnpm的原因是如果在中國,速度會快很多,這個在上一篇文章也說了。
接著修改index.html,這裡只貼出最後的結果:

<head>
    <meta charset="utf-8">
    <title>TestExtJs</title>
    <base href="/">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <link rel="icon" type="image/x-icon" href="favicon.ico">
</head>
<body>
<script>
    var webGlObject = (function() {
        return {
            init: function() {
                alert('webGlObject initialized');
            }
        }
    })(webGlObject || {})   
</script>
    <app-root>Loading...</app-root>
</body>

</html>

註意中間的<script>塊是我們增加的部分,來模擬我們在html本地已經有了一段js代碼。
然後在app.component.ts中增加聲明和調用的部分:

import { Component } from '@angular/core';

declare var webGlObject: any;

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent {
  title = 'app works!';
  constructor() {
    this.title="constructor works!"
    webGlObject.init();
  }

}

註意上面代碼中的declare聲明,和下麵添加的constructor構造函數和其中對js對象的調用。
declare的意思就是告訴AngularJS,相信我,雖然現在你看不到對象webGlObject,但相信我,或早或晚,反正你一定會看到它的存在的,你正常編譯、正常執行就好啦。當然這裡的潛臺詞和副作用就是:諾,AngularJS,這部分代碼我負責啦,你不用管它的對錯,反正錯了我也不會怪你。
使用這種方法,類似上一篇文章的問題,你也完全可以聲明一個window對象,然後直接訪問其中的userAgent:

    ...
declare var window:any;
    ...
    console.log(window.navigator.userAgent);

問題又來了,既然直接能訪問到window對象,那還用什麼ng4-device-detector組件,直接從userAgent中判斷設備類型不好嗎?
這就牽涉到我上面解釋的最後一條,將來這段AngularJS代碼,很可能不是運行在一個瀏覽器,其中可能根本沒有window/document對象,那時候,這段代碼就出錯了。當然你可能會說,不不不,我就是在瀏覽器運行,不考慮別的。OK,我也不較勁,你當我沒說,你完全可以就這麼用。
但是比較規範的辦法,應當是把window對象以及你需要的其它類似對象,寫成一個服務,然後註入到app.component之中,這樣,即便將來運行環境有變化,只修改服務部分代碼,你的主程式完全可以不用修改。
落實到代碼,大致是這樣,首先把window對象包裝成一個服務:

import { Injectable } from '@angular/core';

function _window() : any {
   // return the global native browser window object
   return window;
}

@Injectable()
export class WindowRef {
   get nativeWindow() : any {
      return _window();
   }
}

註冊到provider:

import { WindowRef } from './WindowRef';

...

@NgModule({
    ...
    providers: [ WindowRef ]
})
export class AppModule{}

在需要的組件中,引用這個服務,然後就可以使用了:

...
import { WindowRef } from './WindowRef';
...
@Component({...})
class MyComponent {
...
    constructor(private winRef: WindowRef) {
        // 得到window對象
        console.log('Native window obj', winRef.nativeWindow);
    }
...
}

我得承認,這樣是麻煩了不少,不過規範、可復用的代碼,本身的確就多了很多限制。
參考資料:https://juristr.com/blog/2016/09/ng2-get-window-ref/

————————————————————————————————————————————

AngularJS也一直在努力,儘力彌合這種鴻溝,其中HostListener和HostBinding就是具體的兩個實現,也是我們開始所說的3個方法中的後兩個。
HostListener 是屬性裝飾器,用來為宿主元素添加事件監聽,這個行為表示html端某個元素的事件,產生到達TS腳本的調用動作。比如:

import { Directive, HostListener } from '@angular/core';

@Directive({
    selector: 'button[counting]'
})
class CountClicks {
    numberOfClicks = 0;

    @HostListener('click', ['$event.target'])
    onClick(btn: HTMLElement) {
        console.log('button', btn, 'number of clicks:', this.numberOfClicks++);
    }
}

使用counting裝飾的button按鈕,每次點擊,都會產生一次計數行為,並且列印到控制的日誌中去。
HostBinding 是屬性裝飾器,用來動態設置宿主元素的屬性值,這個跟上面的動作相反,表示首先標記在html某元素的某屬性,然後在TS腳本端,對這個屬性進行設置、賦值。比如:

import { Directive, HostBinding, HostListener } from '@angular/core';

@Directive({
    selector: '[exeButtonPress]'
})
export class ExeButtonPress {
    @HostBinding('attr.role') role = 'button';
    @HostBinding('class.pressed') isPressed: boolean;

    @HostListener('mousedown') hasPressed() {
        this.isPressed = true;
    }
    @HostListener('mouseup') hasReleased() {
        this.isPressed = false;
    }
}

上面的代碼表示,如果某個html元素用exeButtonPress屬性修飾之後,會有一個.pressed屬性,可以監控到滑鼠按下、抬起的事件,這表現了html元素到ts端雙向的互動。
HostListener和HostBinding有一個簡寫的形式host,如下所示:

import { Directive, HostListener } from '@angular/core';

@Directive({
    selector: '[exeButtonPress]',
    host: {
      'role': 'button',
      '[class.pressed]': 'isPressed'
    }
})
export class ExeButtonPress {
    isPressed: boolean;

    @HostListener('mousedown') hasPressed() {
        this.isPressed = true;
    }
    @HostListener('mouseup') hasReleased() {
        this.isPressed = false;
    }
}

看看,跟上一篇中快捷鍵綁定的方法很相似了?
這一部分的代碼使用了https://segmentfault.com/a/1190000008878888的資料,這篇文章寫的很細緻,想詳細瞭解的建議及早閱讀。


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 一、簡介 瀏覽器全屏有兩種方式,一種是HTML5新增的requestFullscreen全屏,另一種是摁F11實現的全屏,本文將詳解兩種全屏的特點以及其實現。 二、requestFullscreen全屏 1、判斷是否支持全屏 通過判斷元素的requestFullscreen函數是否存在與docume ...
  • [1]基本用法 [2]組件樣式 [3]擴展樣式 [4]更換標簽 [5]傳遞屬性 [6]attrs函數 [7]動畫 [8]添加類名 ...
  • 一、jQuery實現輪播圖 效果如下: 首先是HTML部分,代碼如下: JS代碼分析: 二、vue實現輪播圖 效果如下: vue輪播圖的基本原理相對簡單,即通過v-show="index currentIndex"來實現圖片的顯示,通過改變currentIndex的值來切換圖片 ...
  • ## 0. 前言 比較基礎的圖標載入:<img src="x.png">和塊元素的背景background: url(./x.png). 頁面多圖標時,使用雪碧圖(多個png壓縮成一個png,定位來使用),減少圖片所占空間。 大屏顯示,為保證圖標清晰度,可以使用svg格式的圖片。svg是矢量圖,直接 ...
  • 前面的話 css modules是一種流行的模塊化和組合CSS的系統。 vue-loader提供了與css modules的集成,作為scope CSS的替代方案。本文將詳細介紹css modules 引入 最開始使用Vue的時候,提倡並大量使用的是scoped這種技術 這個可選 scoped 屬性 ...
  • 今天勉強看完視頻了,雖然白天都好像是在看視頻,可是看的都不快,學習的方法錯了果然是要出問題的,應該先看視頻,然後看代碼,再然後自己實現一遍的。 講到了路由和引用註入、還包括代碼拆分的思想什麼的。 換了個老師講課,現在的這個老師講課比較抽象,總是用些奇奇怪怪的例子,聽起來還不太好理解,有些遺憾當時沒跟 ...
  • 什麼是閉包?? 有權訪問另一個函數作用域內變數的函數就是閉包。 看一個例子: 在這裡,控制台兩次輸出不一樣???為什麼??變數n不是重新賦值了嗎?? 並沒有,在這裡,var c = a(); 這段代碼意思是將 a 方法的返回值賦值給變數 c ,那麼 a() 的返回值就是 inc ,也就 functi ...
  • 本案例github:https://github.com/axel10/Vuex_demo-Counter-and-list 本篇教程將以計數器及列表展示兩個例子來講解Vuex的簡單用法。 從安裝到啟動初始頁面的過程都直接跳過。註意安裝時選擇需要路由。 首先,src目錄下新建store目錄及相應文件 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...